Merge master.kernel.org:/pub/scm/linux/kernel/git/davej/cpufreq

* master.kernel.org:/pub/scm/linux/kernel/git/davej/cpufreq:
  [CPUFREQ] Report the number of processors in PowerNow-k8 correctly
  [CPUFREQ] do not declare undefined functions
  [CPUFREQ] cleanup kconfig options
  [CPUFREQ] Longhaul - Revert Longhaul ver. 2
  [CPUFREQ] Remove deprecated /proc/acpi/processor/performance write support
  [CPUFREQ] Fix limited cpufreq when booted on battery
  Fix preemption warnings in speedstep-centrino.c
  [CPUFREQ] Longhaul - Correct PCI code
  [CPUFREQ] p4-clockmod: switch to rdmsr_on_cpu/wrmsr_on_cpu
diff --git a/CREDITS b/CREDITS
index 6bd8ab8..d714030 100644
--- a/CREDITS
+++ b/CREDITS
@@ -317,6 +317,12 @@
 S: Seattle, Washington 98126-2010
 S: USA
 
+N: Johannes Berg
+E: johannes@sipsolutions.net
+W: http://johannes.sipsolutions.net/
+P: 1024D/9AB78CA5 AD02 0176 4E29 C137 1DF6 08D2 FC44 CF86 9AB7 8CA5
+D: powerpc & 802.11 hacker
+
 N: Stephen R. van den Berg (AKA BuGless)
 E: berg@pool.informatik.rwth-aachen.de
 D: General kernel, gcc, and libc hacker
@@ -2286,14 +2292,14 @@
 S: Germany
 
 N: Arnaldo Carvalho de Melo
-E: acme@mandriva.com
 E: acme@ghostprotocols.net
+E: arnaldo.melo@gmail.com
+E: acme@redhat.com
 W: http://oops.ghostprotocols.net:81/blog/
 P: 1024D/9224DF01 D5DF E3BB E3C8 BCBB F8AD  841A B6AB 4681 9224 DF01
 D: IPX, LLC, DCCP, cyc2x, wl3501_cs, net/ hacks
-S: Mandriva
-S: R. Tocantins, 89 - Cristo Rei
-S: 80050-430 - Curitiba - Paraná
+S: R. Brasílio Itiberê, 4270/1010 - Água Verde
+S: 80240-060 - Curitiba - Paraná
 S: Brazil
 
 N: Karsten Merker
@@ -3295,6 +3301,14 @@
 S: Beaverton, Oregon 97005
 S: USA
 
+N: Li Yang
+E: leoli@freescale.com
+D: Freescale Highspeed USB device driver
+D: Freescale QE SoC support and Ethernet driver
+S: B-1206 Jingmao Guojigongyu
+S: 16 Baliqiao Nanjie, Beijing 101100
+S: People's Repulic of China
+
 N: Marcelo Tosatti
 E: marcelo@kvack.org
 D: v2.4 kernel maintainer
diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb
new file mode 100644
index 0000000..f9937ad
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -0,0 +1,41 @@
+What:		/sys/bus/usb/devices/.../power/autosuspend
+Date:		March 2007
+KernelVersion:	2.6.21
+Contact:	Alan Stern <stern@rowland.harvard.edu>
+Description:
+		Each USB device directory will contain a file named
+		power/autosuspend.  This file holds the time (in seconds)
+		the device must be idle before it will be autosuspended.
+		0 means the device will be autosuspended as soon as
+		possible.  Negative values will prevent the device from
+		being autosuspended at all, and writing a negative value
+		will resume the device if it is already suspended.
+
+		The autosuspend delay for newly-created devices is set to
+		the value of the usbcore.autosuspend module parameter.
+
+What:		/sys/bus/usb/devices/.../power/level
+Date:		March 2007
+KernelVersion:	2.6.21
+Contact:	Alan Stern <stern@rowland.harvard.edu>
+Description:
+		Each USB device directory will contain a file named
+		power/level.  This file holds a power-level setting for
+		the device, one of "on", "auto", or "suspend".
+
+		"on" means that the device is not allowed to autosuspend,
+		although normal suspends for system sleep will still
+		be honored.  "auto" means the device will autosuspend
+		and autoresume in the usual manner, according to the
+		capabilities of its driver.  "suspend" means the device
+		is forced into a suspended state and it will not autoresume
+		in response to I/O requests.  However remote-wakeup requests
+		from the device may still be enabled (the remote-wakeup
+		setting is controlled separately by the power/wakeup
+		attribute).
+
+		During normal use, devices should be left in the "auto"
+		level.  The other levels are meant for administrative uses.
+		If you want to suspend a device immediately but leave it
+		free to wake up in response to I/O requests, you should
+		write "0" to power/autosuspend.
diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl
index 0bb9023..b61dfc7 100644
--- a/Documentation/DocBook/kernel-api.tmpl
+++ b/Documentation/DocBook/kernel-api.tmpl
@@ -236,6 +236,12 @@
 !Enet/core/dev.c
 !Enet/ethernet/eth.c
 !Iinclude/linux/etherdevice.h
+!Edrivers/net/phy/phy.c
+!Idrivers/net/phy/phy.c
+!Edrivers/net/phy/phy_device.c
+!Idrivers/net/phy/phy_device.c
+!Edrivers/net/phy/mdio_bus.c
+!Idrivers/net/phy/mdio_bus.c
 <!-- FIXME: Removed for now since no structured comments in source
 X!Enet/core/wireless.c
 -->
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt
index 19b4c96..1a9e600 100644
--- a/Documentation/feature-removal-schedule.txt
+++ b/Documentation/feature-removal-schedule.txt
@@ -6,6 +6,18 @@
 
 ---------------------------
 
+What:	V4L2 VIDIOC_G_MPEGCOMP and VIDIOC_S_MPEGCOMP
+When:	October 2007
+Why:	Broken attempt to set MPEG compression parameters. These ioctls are
+	not able to implement the wide variety of parameters that can be set
+	by hardware MPEG encoders. A new MPEG control mechanism was created
+	in kernel 2.6.18 that replaces these ioctls. See the V4L2 specification
+	(section 1.9: Extended controls) for more information on this topic.
+Who:	Hans Verkuil <hverkuil@xs4all.nl> and
+	Mauro Carvalho Chehab <mchehab@infradead.org>
+
+---------------------------
+
 What:	/sys/devices/.../power/state
 	dev->power.power_state
 	dpm_runtime_{suspend,resume)()
@@ -134,15 +146,6 @@
 
 ---------------------------
 
-What:	mount/umount uevents
-When:	February 2007
-Why:	These events are not correct, and do not properly let userspace know
-	when a file system has been mounted or unmounted.  Userspace should
-	poll the /proc/mounts file instead to detect this properly.
-Who:	Greg Kroah-Hartman <gregkh@suse.de>
-
----------------------------
-
 What:	USB driver API moves to EXPORT_SYMBOL_GPL
 When:	February 2008
 Files:	include/linux/usb.h, drivers/usb/core/driver.c
@@ -211,15 +214,6 @@
 
 ---------------------------
 
-What:	IPv4 only connection tracking/NAT/helpers
-When:	2.6.22
-Why:	The new layer 3 independant connection tracking replaces the old
-	IPv4 only version. After some stabilization of the new code the
-	old one will be removed.
-Who:	Patrick McHardy <kaber@trash.net>
-
----------------------------
-
 What:	ACPI hooks (X86_SPEEDSTEP_CENTRINO_ACPI) in speedstep-centrino driver
 When:	December 2006
 Why:	Speedstep-centrino driver with ACPI hooks and acpi-cpufreq driver are
@@ -294,18 +288,6 @@
 
 ---------------------------
 
-What:	Wireless extensions over netlink (CONFIG_NET_WIRELESS_RTNETLINK)
-When:	with the merge of wireless-dev, 2.6.22 or later
-Why:	The option/code is
-	 * not enabled on most kernels
-	 * not required by any userspace tools (except an experimental one,
-	   and even there only for some parts, others use ioctl)
-	 * pointless since wext is no longer evolving and the ioctl
-	   interface needs to be kept
-Who:	Johannes Berg <johannes@sipsolutions.net>
-
----------------------------
-
 What:	i8xx_tco watchdog driver
 When:	in 2.6.22
 Why:	the i8xx_tco watchdog driver has been replaced by the iTCO_wdt
@@ -313,3 +295,39 @@
 Who:	Wim Van Sebroeck <wim@iguana.be>
 
 ---------------------------
+
+What:	Multipath cached routing support in ipv4
+When:	in 2.6.23
+Why:	Code was merged, then submitter immediately disappeared leaving
+	us with no maintainer and lots of bugs.  The code should not have
+	been merged in the first place, and many aspects of it's
+	implementation are blocking more critical core networking
+	development.  It's marked EXPERIMENTAL and no distribution
+	enables it because it cause obscure crashes due to unfixable bugs
+	(interfaces don't return errors so memory allocation can't be
+	handled, calling contexts of these interfaces make handling
+	errors impossible too because they get called after we've
+	totally commited to creating a route object, for example).
+	This problem has existed for years and no forward progress
+	has ever been made, and nobody steps up to try and salvage
+	this code, so we're going to finally just get rid of it.
+Who:	David S. Miller <davem@davemloft.net>
+
+---------------------------
+
+What:	read_dev_chars(), read_conf_data{,_lpm}() (s390 common I/O layer)
+When:	December 2007
+Why:	These functions are a leftover from 2.4 times. They have several
+	problems:
+	- Duplication of checks that are done in the device driver's
+	  interrupt handler
+	- common I/O layer can't do device specific error recovery
+	- device driver can't be notified for conditions happening during
+	  execution of the function
+	Device drivers should issue the read device characteristics and read
+	configuration data ccws and do the appropriate error handling
+	themselves.
+Who:	Cornelia Huck <cornelia.huck@de.ibm.com>
+
+---------------------------
+
diff --git a/Documentation/filesystems/afs.txt b/Documentation/filesystems/afs.txt
index 2f4237d..12ad6c7 100644
--- a/Documentation/filesystems/afs.txt
+++ b/Documentation/filesystems/afs.txt
@@ -1,31 +1,82 @@
+			     ====================
 			     kAFS: AFS FILESYSTEM
 			     ====================
 
-ABOUT
+Contents:
+
+ - Overview.
+ - Usage.
+ - Mountpoints.
+ - Proc filesystem.
+ - The cell database.
+ - Security.
+ - Examples.
+
+
+========
+OVERVIEW
+========
+
+This filesystem provides a fairly simple secure AFS filesystem driver. It is
+under development and does not yet provide the full feature set.  The features
+it does support include:
+
+ (*) Security (currently only AFS kaserver and KerberosIV tickets).
+
+ (*) File reading.
+
+ (*) Automounting.
+
+It does not yet support the following AFS features:
+
+ (*) Write support.
+
+ (*) Local caching.
+
+ (*) pioctl() system call.
+
+
+===========
+COMPILATION
+===========
+
+The filesystem should be enabled by turning on the kernel configuration
+options:
+
+	CONFIG_AF_RXRPC		- The RxRPC protocol transport
+	CONFIG_RXKAD		- The RxRPC Kerberos security handler
+	CONFIG_AFS		- The AFS filesystem
+
+Additionally, the following can be turned on to aid debugging:
+
+	CONFIG_AF_RXRPC_DEBUG	- Permit AF_RXRPC debugging to be enabled
+	CONFIG_AFS_DEBUG	- Permit AFS debugging to be enabled
+
+They permit the debugging messages to be turned on dynamically by manipulating
+the masks in the following files:
+
+	/sys/module/af_rxrpc/parameters/debug
+	/sys/module/afs/parameters/debug
+
+
 =====
-
-This filesystem provides a fairly simple AFS filesystem driver. It is under
-development and only provides very basic facilities. It does not yet support
-the following AFS features:
-
-	(*) Write support.
-	(*) Communications security.
-	(*) Local caching.
-	(*) pioctl() system call.
-	(*) Automatic mounting of embedded mountpoints.
-
-
 USAGE
 =====
 
 When inserting the driver modules the root cell must be specified along with a
 list of volume location server IP addresses:
 
-	insmod rxrpc.o
+	insmod af_rxrpc.o
+	insmod rxkad.o
 	insmod kafs.o rootcell=cambridge.redhat.com:172.16.18.73:172.16.18.91
 
-The first module is a driver for the RxRPC remote operation protocol, and the
-second is the actual filesystem driver for the AFS filesystem.
+The first module is the AF_RXRPC network protocol driver.  This provides the
+RxRPC remote operation protocol and may also be accessed from userspace.  See:
+
+	Documentation/networking/rxrpc.txt
+
+The second module is the kerberos RxRPC security driver, and the third module
+is the actual filesystem driver for the AFS filesystem.
 
 Once the module has been loaded, more modules can be added by the following
 procedure:
@@ -33,7 +84,7 @@
 	echo add grand.central.org 18.7.14.88:128.2.191.224 >/proc/fs/afs/cells
 
 Where the parameters to the "add" command are the name of a cell and a list of
-volume location servers within that cell.
+volume location servers within that cell, with the latter separated by colons.
 
 Filesystems can be mounted anywhere by commands similar to the following:
 
@@ -42,11 +93,6 @@
 	mount -t afs "#root.afs." /afs
 	mount -t afs "#root.cell." /afs/cambridge
 
-  NB: When using this on Linux 2.4, the mount command has to be different,
-      since the filesystem doesn't have access to the device name argument:
-
-	mount -t afs none /afs -ovol="#root.afs."
-
 Where the initial character is either a hash or a percent symbol depending on
 whether you definitely want a R/W volume (hash) or whether you'd prefer a R/O
 volume, but are willing to use a R/W volume instead (percent).
@@ -60,55 +106,66 @@
 Additional cells can be added through /proc (see later section).
 
 
+===========
 MOUNTPOINTS
 ===========
 
-AFS has a concept of mountpoints. These are specially formatted symbolic links
-(of the same form as the "device name" passed to mount). kAFS presents these
-to the user as directories that have special properties:
+AFS has a concept of mountpoints. In AFS terms, these are specially formatted
+symbolic links (of the same form as the "device name" passed to mount).  kAFS
+presents these to the user as directories that have a follow-link capability
+(ie: symbolic link semantics).  If anyone attempts to access them, they will
+automatically cause the target volume to be mounted (if possible) on that site.
 
-  (*) They cannot be listed. Running a program like "ls" on them will incur an
-      EREMOTE error (Object is remote).
+Automatically mounted filesystems will be automatically unmounted approximately
+twenty minutes after they were last used.  Alternatively they can be unmounted
+directly with the umount() system call.
 
-  (*) Other objects can't be looked up inside of them. This also incurs an
-      EREMOTE error.
+Manually unmounting an AFS volume will cause any idle submounts upon it to be
+culled first.  If all are culled, then the requested volume will also be
+unmounted, otherwise error EBUSY will be returned.
 
-  (*) They can be queried with the readlink() system call, which will return
-      the name of the mountpoint to which they point. The "readlink" program
-      will also work.
+This can be used by the administrator to attempt to unmount the whole AFS tree
+mounted on /afs in one go by doing:
 
-  (*) They can be mounted on (which symbolic links can't).
+	umount /afs
 
 
+===============
 PROC FILESYSTEM
 ===============
 
-The rxrpc module creates a number of files in various places in the /proc
-filesystem:
-
-  (*) Firstly, some information files are made available in a directory called
-      "/proc/net/rxrpc/". These list the extant transport endpoint, peer,
-      connection and call records.
-
-  (*) Secondly, some control files are made available in a directory called
-      "/proc/sys/rxrpc/". Currently, all these files can be used for is to
-      turn on various levels of tracing.
-
 The AFS modules creates a "/proc/fs/afs/" directory and populates it:
 
-  (*) A "cells" file that lists cells currently known to the afs module.
+  (*) A "cells" file that lists cells currently known to the afs module and
+      their usage counts:
+
+	[root@andromeda ~]# cat /proc/fs/afs/cells
+	USE NAME
+	  3 cambridge.redhat.com
 
   (*) A directory per cell that contains files that list volume location
       servers, volumes, and active servers known within that cell.
 
+	[root@andromeda ~]# cat /proc/fs/afs/cambridge.redhat.com/servers
+	USE ADDR            STATE
+	  4 172.16.18.91        0
+	[root@andromeda ~]# cat /proc/fs/afs/cambridge.redhat.com/vlservers
+	ADDRESS
+	172.16.18.91
+	[root@andromeda ~]# cat /proc/fs/afs/cambridge.redhat.com/volumes
+	USE STT VLID[0]  VLID[1]  VLID[2]  NAME
+	  1 Val 20000000 20000001 20000002 root.afs
 
+
+=================
 THE CELL DATABASE
 =================
 
-The filesystem maintains an internal database of all the cells it knows and
-the IP addresses of the volume location servers for those cells. The cell to
-which the computer belongs is added to the database when insmod is performed
-by the "rootcell=" argument.
+The filesystem maintains an internal database of all the cells it knows and the
+IP addresses of the volume location servers for those cells.  The cell to which
+the system belongs is added to the database when insmod is performed by the
+"rootcell=" argument or, if compiled in, using a "kafs.rootcell=" argument on
+the kernel command line.
 
 Further cells can be added by commands similar to the following:
 
@@ -118,20 +175,65 @@
 No other cell database operations are available at this time.
 
 
+========
+SECURITY
+========
+
+Secure operations are initiated by acquiring a key using the klog program.  A
+very primitive klog program is available at:
+
+	http://people.redhat.com/~dhowells/rxrpc/klog.c
+
+This should be compiled by:
+
+	make klog LDLIBS="-lcrypto -lcrypt -lkrb4 -lkeyutils"
+
+And then run as:
+
+	./klog
+
+Assuming it's successful, this adds a key of type RxRPC, named for the service
+and cell, eg: "afs@<cellname>".  This can be viewed with the keyctl program or
+by cat'ing /proc/keys:
+
+	[root@andromeda ~]# keyctl show
+	Session Keyring
+	       -3 --alswrv      0     0  keyring: _ses.3268
+		2 --alswrv      0     0   \_ keyring: _uid.0
+	111416553 --als--v      0     0   \_ rxrpc: afs@CAMBRIDGE.REDHAT.COM
+
+Currently the username, realm, password and proposed ticket lifetime are
+compiled in to the program.
+
+It is not required to acquire a key before using AFS facilities, but if one is
+not acquired then all operations will be governed by the anonymous user parts
+of the ACLs.
+
+If a key is acquired, then all AFS operations, including mounts and automounts,
+made by a possessor of that key will be secured with that key.
+
+If a file is opened with a particular key and then the file descriptor is
+passed to a process that doesn't have that key (perhaps over an AF_UNIX
+socket), then the operations on the file will be made with key that was used to
+open the file.
+
+
+========
 EXAMPLES
 ========
 
-Here's what I use to test this. Some of the names and IP addresses are local
-to my internal DNS. My "root.afs" partition has a mount point within it for
+Here's what I use to test this.  Some of the names and IP addresses are local
+to my internal DNS.  My "root.afs" partition has a mount point within it for
 some public volumes volumes.
 
-insmod -S /tmp/rxrpc.o 
-insmod -S /tmp/kafs.o rootcell=cambridge.redhat.com:172.16.18.73:172.16.18.91
+insmod /tmp/rxrpc.o
+insmod /tmp/rxkad.o
+insmod /tmp/kafs.o rootcell=cambridge.redhat.com:172.16.18.91
 
 mount -t afs \%root.afs. /afs
 mount -t afs \%cambridge.redhat.com:root.cell. /afs/cambridge.redhat.com/
 
-echo add grand.central.org 18.7.14.88:128.2.191.224 > /proc/fs/afs/cells 
+echo add grand.central.org 18.7.14.88:128.2.191.224 > /proc/fs/afs/cells
 mount -t afs "#grand.central.org:root.cell." /afs/grand.central.org/
 mount -t afs "#grand.central.org:root.archive." /afs/grand.central.org/archive
 mount -t afs "#grand.central.org:root.contrib." /afs/grand.central.org/contrib
@@ -141,15 +243,7 @@
 mount -t afs "#grand.central.org:root.software." /afs/grand.central.org/software
 mount -t afs "#grand.central.org:root.user." /afs/grand.central.org/user
 
-umount /afs/grand.central.org/user
-umount /afs/grand.central.org/software
-umount /afs/grand.central.org/service
-umount /afs/grand.central.org/project
-umount /afs/grand.central.org/doc
-umount /afs/grand.central.org/contrib
-umount /afs/grand.central.org/archive
-umount /afs/grand.central.org
-umount /afs/cambridge.redhat.com
 umount /afs
 rmmod kafs
+rmmod rxkad
 rmmod rxrpc
diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt
index 5484ab5..7aaf09b 100644
--- a/Documentation/filesystems/proc.txt
+++ b/Documentation/filesystems/proc.txt
@@ -1421,6 +1421,15 @@
 be dropped.  The  default  settings  limit  warning messages to one every five
 seconds.
 
+warnings
+--------
+
+This controls console messages from the networking stack that can occur because
+of problems on the network like duplicate address or bad checksums. Normally,
+this should be enabled, but if the problem persists the messages can be
+disabled.
+
+
 netdev_max_backlog
 ------------------
 
diff --git a/Documentation/infiniband/user_mad.txt b/Documentation/infiniband/user_mad.txt
index 750fe5e..8ec54b9 100644
--- a/Documentation/infiniband/user_mad.txt
+++ b/Documentation/infiniband/user_mad.txt
@@ -91,6 +91,14 @@
 	if (ret != sizeof *mad + mad_length)
 		perror("write");
 
+Transaction IDs
+
+  Users of the umad devices can use the lower 32 bits of the
+  transaction ID field (that is, the least significant half of the
+  field in network byte order) in MADs being sent to match
+  request/response pairs.  The upper 32 bits are reserved for use by
+  the kernel and will be overwritten before a MAD is sent.
+
 Setting IsSM Capability Bit
 
   To set the IsSM capability bit for a port, simply open the
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 12533a9..84c3bd0 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -181,19 +181,41 @@
 			that require a timer override, but don't have
 			HPET
 
-	acpi_dbg_layer=	[HW,ACPI]
+	acpi.debug_layer=	[HW,ACPI]
 			Format: <int>
 			Each bit of the <int> indicates an ACPI debug layer,
 			1: enable, 0: disable. It is useful for boot time
 			debugging. After system has booted up, it can be set
-			via /proc/acpi/debug_layer.
+			via /sys/module/acpi/parameters/debug_layer.
+			CONFIG_ACPI_DEBUG must be enabled for this to produce any output.
+			Available bits (add the numbers together) to enable debug output
+			for specific parts of the ACPI subsystem:
+			0x01 utilities 0x02 hardware 0x04 events 0x08 tables
+			0x10 namespace 0x20 parser 0x40 dispatcher
+			0x80 executer 0x100 resources 0x200 acpica debugger
+			0x400 os services 0x800 acpica disassembler.
+			The number can be in decimal or prefixed with 0x in hex.
+			Warning: Many of these options can produce a lot of
+			output and make your system unusable. Be very careful.
 
-	acpi_dbg_level=	[HW,ACPI]
+	acpi.debug_level=	[HW,ACPI]
 			Format: <int>
 			Each bit of the <int> indicates an ACPI debug level,
 			1: enable, 0: disable. It is useful for boot time
 			debugging. After system has booted up, it can be set
-			via /proc/acpi/debug_level.
+			via /sys/module/acpi/parameters/debug_level.
+			CONFIG_ACPI_DEBUG must be enabled for this to produce any output.
+			Available bits (add the numbers together) to enable different
+			debug output levels of the ACPI subsystem:
+			0x01 error 0x02 warn 0x04 init 0x08 debug object
+			0x10 info 0x20 init names 0x40 parse 0x80 load
+			0x100 dispatch 0x200 execute 0x400 names 0x800 operation region
+			0x1000 bfield 0x2000 tables 0x4000 values 0x8000 objects
+			0x10000 resources 0x20000 user requests 0x40000 package.
+			The number can be in decimal or prefixed with 0x in hex.
+			Warning: Many of these options can produce a lot of
+			output and make your system unusable. Be very careful.
+
 
 	acpi_fake_ecdt	[HW,ACPI] Workaround failure due to BIOS lacking ECDT
 
@@ -1792,7 +1814,7 @@
 			for newly-detected USB devices (default 2).  This
 			is the time required before an idle device will be
 			autosuspended.  Devices for which the delay is set
-			to 0 won't be autosuspended at all.
+			to a negative value won't be autosuspended at all.
 
 	usbhid.mousepoll=
 			[USBHID] The interval which mice are to be polled at.
diff --git a/Documentation/keys.txt b/Documentation/keys.txt
index 60c665d9..81d9aa0 100644
--- a/Documentation/keys.txt
+++ b/Documentation/keys.txt
@@ -859,6 +859,18 @@
 	void unregister_key_type(struct key_type *type);
 
 
+Under some circumstances, it may be desirable to desirable to deal with a
+bundle of keys.  The facility provides access to the keyring type for managing
+such a bundle:
+
+	struct key_type key_type_keyring;
+
+This can be used with a function such as request_key() to find a specific
+keyring in a process's keyrings.  A keyring thus found can then be searched
+with keyring_search().  Note that it is not possible to use request_key() to
+search a specific keyring, so using keyrings in this way is of limited utility.
+
+
 ===================================
 NOTES ON ACCESSING PAYLOAD CONTENTS
 ===================================
diff --git a/Documentation/networking/bcm43xx.txt b/Documentation/networking/bcm43xx.txt
index 28541d2..a136721 100644
--- a/Documentation/networking/bcm43xx.txt
+++ b/Documentation/networking/bcm43xx.txt
@@ -2,35 +2,88 @@
 			BCM43xx Linux Driver Project
 			============================
 
-About this software
--------------------
-
-The goal of this project is to develop a linux driver for Broadcom
-BCM43xx chips, based on the specification at 
-http://bcm-specs.sipsolutions.net/
-
-The project page is http://bcm43xx.berlios.de/
-
-
-Requirements
+Introduction
 ------------
 
-1)	Linux Kernel 2.6.16 or later
-	http://www.kernel.org/
+Many of the wireless devices found in modern notebook computers are
+based on the wireless chips produced by Broadcom. These devices have
+been a problem for Linux users as there is no open-source driver
+available. In addition, Broadcom has not released specifications
+for the device, and driver availability has been limited to the
+binary-only form used in the GPL versions of AP hardware such as the
+Linksys WRT54G, and the Windows and OS X drivers.  Before this project
+began, the only way to use these devices were to use the Windows or
+OS X drivers with either the Linuxant or ndiswrapper modules. There
+is a strong penalty if this method is used as loading the binary-only
+module "taints" the kernel, and no kernel developer will help diagnose
+any kernel problems.
 
-	You may want to configure your kernel with:
+Development
+-----------
 
-	CONFIG_DEBUG_FS (optional):
-		-> Kernel hacking
-		  -> Debug Filesystem
+This driver has been developed using
+a clean-room technique that is described at
+http://bcm-specs.sipsolutions.net/ReverseEngineeringProcess. For legal
+reasons, none of the clean-room crew works on the on the Linux driver,
+and none of the Linux developers sees anything but the specifications,
+which are the ultimate product of the reverse-engineering group.
 
-2)	SoftMAC IEEE 802.11 Networking Stack extension and patched ieee80211
-	modules:
-	http://softmac.sipsolutions.net/
+Software
+--------
 
-3)	Firmware Files
+Since the release of the 2.6.17 kernel, the bcm43xx driver has been
+distributed with the kernel source, and is prebuilt in most, if not
+all, distributions.  There is, however, additional software that is
+required. The firmware used by the chip is the intellectual property
+of Broadcom and they have not given the bcm43xx team redistribution
+rights to this firmware.  Since we cannot legally redistribute
+the firwmare we cannot include it with the driver. Furthermore, it
+cannot be placed in the downloadable archives of any distributing
+organization; therefore, the user is responsible for obtaining the
+firmware and placing it in the appropriate location so that the driver
+can find it when initializing.
 
-	Please try fwcutter. Fwcutter can extract the firmware from various 
-	binary driver files. It supports driver files from Windows, MacOS and 
-	Linux. You can get fwcutter from http://bcm43xx.berlios.de/.
-	Also, fwcutter comes with a README file for further instructions.
+To help with this process, the bcm43xx developers provide a separate
+program named bcm43xx-fwcutter to "cut" the firmware out of a
+Windows or OS X driver and write the extracted files to the proper
+location. This program is usually provided with the distribution;
+however, it may be downloaded from
+
+http://developer.berlios.de/project/showfiles.php?group_id=4547
+
+The firmware is available in two versions. V3 firmware is used with
+the in-kernel bcm43xx driver that uses a software MAC layer called
+SoftMAC, and will have a microcode revision of 0x127 or smaller. The
+V4 firmware is used by an out-of-kernel driver employing a variation of
+the Devicescape MAC layer known as d80211. Once bcm43xx-d80211 reaches
+a satisfactory level of development, it will replace bcm43xx-softmac
+in the kernel as it is much more flexible and powerful.
+
+A source for the latest V3 firmware is
+
+http://downloads.openwrt.org/sources/wl_apsta-3.130.20.0.o
+
+Once this file is downloaded, the command
+'bcm43xx-fwcutter -w <dir> <filename>'
+will extract the microcode and write it to directory
+<dir>. The correct directory will depend on your distribution;
+however, most use '/lib/firmware'. Once this step is completed,
+the bcm3xx driver should load when the system is booted. To see
+any messages relating to the driver, issue the command 'dmesg |
+grep bcm43xx' from a terminal window. If there are any problems,
+please send that output to Bcm43xx-dev@lists.berlios.de.
+
+Although the driver has been in-kernel since 2.6.17, the earliest
+version is quite limited in its capability. Patches that include
+all features of later versions are available for the stable kernel
+versions from 2.6.18. These will be needed if you use a BCM4318,
+or a PCI Express version (BCM4311 and BCM4312). In addition, if you
+have an early BCM4306 and more than 1 GB RAM, your kernel will need
+to be patched.	These patches, which are being updated regularly,
+are available at ftp://lwfinger.dynalias.org/patches. Look for
+combined_2.6.YY.patch. Of course you will need kernel source downloaded
+from kernel.org, or the source from your distribution.
+
+If you build your own kernel, please enable CONFIG_BCM43XX_DEBUG
+and CONFIG_IEEE80211_SOFTMAC_DEBUG. The log information provided is
+essential for solving any problems.
diff --git a/Documentation/networking/bonding.txt b/Documentation/networking/bonding.txt
index de809e5..1da5666 100644
--- a/Documentation/networking/bonding.txt
+++ b/Documentation/networking/bonding.txt
@@ -920,40 +920,9 @@
 documented above.
 
 	To create multiple bonding devices with differing options, it
-is necessary to load the bonding driver multiple times.  Note that
-current versions of the sysconfig network initialization scripts
-handle this automatically; if your distro uses these scripts, no
-special action is needed.  See the section Configuring Bonding
-Devices, above, if you're not sure about your network initialization
-scripts.
+is necessary to use bonding parameters exported by sysfs, documented
+in the section below.
 
-	To load multiple instances of the module, it is necessary to
-specify a different name for each instance (the module loading system
-requires that every loaded module, even multiple instances of the same
-module, have a unique name).  This is accomplished by supplying
-multiple sets of bonding options in /etc/modprobe.conf, for example:
-	
-alias bond0 bonding
-options bond0 -o bond0 mode=balance-rr miimon=100
-
-alias bond1 bonding
-options bond1 -o bond1 mode=balance-alb miimon=50
-
-	will load the bonding module two times.  The first instance is
-named "bond0" and creates the bond0 device in balance-rr mode with an
-miimon of 100.  The second instance is named "bond1" and creates the
-bond1 device in balance-alb mode with an miimon of 50.
-
-	In some circumstances (typically with older distributions),
-the above does not work, and the second bonding instance never sees
-its options.  In that case, the second options line can be substituted
-as follows:
-
-install bond1 /sbin/modprobe --ignore-install bonding -o bond1 \
-	mode=balance-alb miimon=50
-
-	This may be repeated any number of times, specifying a new and
-unique name in place of bond1 for each subsequent instance.
 
 3.4 Configuring Bonding Manually via Sysfs
 ------------------------------------------
diff --git a/Documentation/networking/dccp.txt b/Documentation/networking/dccp.txt
index 387482e..4504cc5 100644
--- a/Documentation/networking/dccp.txt
+++ b/Documentation/networking/dccp.txt
@@ -57,6 +57,16 @@
 	coverage value are also acceptable. The higher the number, the more
 	restrictive this setting (see [RFC 4340, sec. 9.2.1]).
 
+The following two options apply to CCID 3 exclusively and are getsockopt()-only.
+In either case, a TFRC info struct (defined in <linux/tfrc.h>) is returned.
+DCCP_SOCKOPT_CCID_RX_INFO
+	Returns a `struct tfrc_rx_info' in optval; the buffer for optval and
+	optlen must be set to at least sizeof(struct tfrc_rx_info).
+DCCP_SOCKOPT_CCID_TX_INFO
+	Returns a `struct tfrc_tx_info' in optval; the buffer for optval and
+	optlen must be set to at least sizeof(struct tfrc_tx_info).
+
+
 Sysctl variables
 ================
 Several DCCP default parameters can be managed by the following sysctls
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index 702d1d8..af6a63a 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -179,11 +179,31 @@
 	because they eat maximum 1.5K of memory, but they tend
 	to live longer.	Cf. tcp_max_orphans.
 
-tcp_frto - BOOLEAN
+tcp_frto - INTEGER
 	Enables F-RTO, an enhanced recovery algorithm for TCP retransmission
 	timeouts.  It is particularly beneficial in wireless environments
 	where packet loss is typically due to random radio interference
-	rather than intermediate router congestion.
+	rather than intermediate router congestion. If set to 1, basic
+	version is enabled. 2 enables SACK enhanced F-RTO, which is
+	EXPERIMENTAL. The basic version can be used also when SACK is
+	enabled for a flow through tcp_sack sysctl.
+
+tcp_frto_response - INTEGER
+	When F-RTO has detected that a TCP retransmission timeout was
+	spurious (i.e, the timeout would have been avoided had TCP set a
+	longer retransmission timeout), TCP has several options what to do
+	next. Possible values are:
+		0 Rate halving based; a smooth and conservative response,
+		  results in halved cwnd and ssthresh after one RTT
+		1 Very conservative response; not recommended because even
+		  though being valid, it interacts poorly with the rest of
+		  Linux TCP, halves cwnd and ssthresh immediately
+		2 Aggressive response; undoes congestion control measures
+		  that are now known to be unnecessary (ignoring the
+		  possibility of a lost retransmission that would require
+		  TCP to be more cautious), cwnd and ssthresh are restored
+		  to the values prior timeout
+	Default: 0 (rate halving based)
 
 tcp_keepalive_time - INTEGER
 	How often TCP sends out keepalive messages when keepalive is enabled.
@@ -995,7 +1015,12 @@
 	Default: 1
 
 bridge-nf-filter-vlan-tagged - BOOLEAN
-	1 : pass bridged vlan-tagged ARP/IP traffic to arptables/iptables.
+	1 : pass bridged vlan-tagged ARP/IP/IPv6 traffic to {arp,ip,ip6}tables.
+	0 : disable this.
+	Default: 1
+
+bridge-nf-filter-pppoe-tagged - BOOLEAN
+	1 : pass bridged pppoe-tagged IP/IPv6 traffic to {ip,ip6}tables.
 	0 : disable this.
 	Default: 1
 
diff --git a/Documentation/networking/rxrpc.txt b/Documentation/networking/rxrpc.txt
new file mode 100644
index 0000000..cae231b
--- /dev/null
+++ b/Documentation/networking/rxrpc.txt
@@ -0,0 +1,859 @@
+			    ======================
+			    RxRPC NETWORK PROTOCOL
+			    ======================
+
+The RxRPC protocol driver provides a reliable two-phase transport on top of UDP
+that can be used to perform RxRPC remote operations.  This is done over sockets
+of AF_RXRPC family, using sendmsg() and recvmsg() with control data to send and
+receive data, aborts and errors.
+
+Contents of this document:
+
+ (*) Overview.
+
+ (*) RxRPC protocol summary.
+
+ (*) AF_RXRPC driver model.
+
+ (*) Control messages.
+
+ (*) Socket options.
+
+ (*) Security.
+
+ (*) Example client usage.
+
+ (*) Example server usage.
+
+ (*) AF_RXRPC kernel interface.
+
+
+========
+OVERVIEW
+========
+
+RxRPC is a two-layer protocol.  There is a session layer which provides
+reliable virtual connections using UDP over IPv4 (or IPv6) as the transport
+layer, but implements a real network protocol; and there's the presentation
+layer which renders structured data to binary blobs and back again using XDR
+(as does SunRPC):
+
+		+-------------+
+		| Application |
+		+-------------+
+		|     XDR     |		Presentation
+		+-------------+
+		|    RxRPC    |		Session
+		+-------------+
+		|     UDP     |		Transport
+		+-------------+
+
+
+AF_RXRPC provides:
+
+ (1) Part of an RxRPC facility for both kernel and userspace applications by
+     making the session part of it a Linux network protocol (AF_RXRPC).
+
+ (2) A two-phase protocol.  The client transmits a blob (the request) and then
+     receives a blob (the reply), and the server receives the request and then
+     transmits the reply.
+
+ (3) Retention of the reusable bits of the transport system set up for one call
+     to speed up subsequent calls.
+
+ (4) A secure protocol, using the Linux kernel's key retention facility to
+     manage security on the client end.  The server end must of necessity be
+     more active in security negotiations.
+
+AF_RXRPC does not provide XDR marshalling/presentation facilities.  That is
+left to the application.  AF_RXRPC only deals in blobs.  Even the operation ID
+is just the first four bytes of the request blob, and as such is beyond the
+kernel's interest.
+
+
+Sockets of AF_RXRPC family are:
+
+ (1) created as type SOCK_DGRAM;
+
+ (2) provided with a protocol of the type of underlying transport they're going
+     to use - currently only PF_INET is supported.
+
+
+The Andrew File System (AFS) is an example of an application that uses this and
+that has both kernel (filesystem) and userspace (utility) components.
+
+
+======================
+RXRPC PROTOCOL SUMMARY
+======================
+
+An overview of the RxRPC protocol:
+
+ (*) RxRPC sits on top of another networking protocol (UDP is the only option
+     currently), and uses this to provide network transport.  UDP ports, for
+     example, provide transport endpoints.
+
+ (*) RxRPC supports multiple virtual "connections" from any given transport
+     endpoint, thus allowing the endpoints to be shared, even to the same
+     remote endpoint.
+
+ (*) Each connection goes to a particular "service".  A connection may not go
+     to multiple services.  A service may be considered the RxRPC equivalent of
+     a port number.  AF_RXRPC permits multiple services to share an endpoint.
+
+ (*) Client-originating packets are marked, thus a transport endpoint can be
+     shared between client and server connections (connections have a
+     direction).
+
+ (*) Up to a billion connections may be supported concurrently between one
+     local transport endpoint and one service on one remote endpoint.  An RxRPC
+     connection is described by seven numbers:
+
+	Local address	}
+	Local port	} Transport (UDP) address
+	Remote address	}
+	Remote port	}
+	Direction
+	Connection ID
+	Service ID
+
+ (*) Each RxRPC operation is a "call".  A connection may make up to four
+     billion calls, but only up to four calls may be in progress on a
+     connection at any one time.
+
+ (*) Calls are two-phase and asymmetric: the client sends its request data,
+     which the service receives; then the service transmits the reply data
+     which the client receives.
+
+ (*) The data blobs are of indefinite size, the end of a phase is marked with a
+     flag in the packet.  The number of packets of data making up one blob may
+     not exceed 4 billion, however, as this would cause the sequence number to
+     wrap.
+
+ (*) The first four bytes of the request data are the service operation ID.
+
+ (*) Security is negotiated on a per-connection basis.  The connection is
+     initiated by the first data packet on it arriving.  If security is
+     requested, the server then issues a "challenge" and then the client
+     replies with a "response".  If the response is successful, the security is
+     set for the lifetime of that connection, and all subsequent calls made
+     upon it use that same security.  In the event that the server lets a
+     connection lapse before the client, the security will be renegotiated if
+     the client uses the connection again.
+
+ (*) Calls use ACK packets to handle reliability.  Data packets are also
+     explicitly sequenced per call.
+
+ (*) There are two types of positive acknowledgement: hard-ACKs and soft-ACKs.
+     A hard-ACK indicates to the far side that all the data received to a point
+     has been received and processed; a soft-ACK indicates that the data has
+     been received but may yet be discarded and re-requested.  The sender may
+     not discard any transmittable packets until they've been hard-ACK'd.
+
+ (*) Reception of a reply data packet implicitly hard-ACK's all the data
+     packets that make up the request.
+
+ (*) An call is complete when the request has been sent, the reply has been
+     received and the final hard-ACK on the last packet of the reply has
+     reached the server.
+
+ (*) An call may be aborted by either end at any time up to its completion.
+
+
+=====================
+AF_RXRPC DRIVER MODEL
+=====================
+
+About the AF_RXRPC driver:
+
+ (*) The AF_RXRPC protocol transparently uses internal sockets of the transport
+     protocol to represent transport endpoints.
+
+ (*) AF_RXRPC sockets map onto RxRPC connection bundles.  Actual RxRPC
+     connections are handled transparently.  One client socket may be used to
+     make multiple simultaneous calls to the same service.  One server socket
+     may handle calls from many clients.
+
+ (*) Additional parallel client connections will be initiated to support extra
+     concurrent calls, up to a tunable limit.
+
+ (*) Each connection is retained for a certain amount of time [tunable] after
+     the last call currently using it has completed in case a new call is made
+     that could reuse it.
+
+ (*) Each internal UDP socket is retained [tunable] for a certain amount of
+     time [tunable] after the last connection using it discarded, in case a new
+     connection is made that could use it.
+
+ (*) A client-side connection is only shared between calls if they have have
+     the same key struct describing their security (and assuming the calls
+     would otherwise share the connection).  Non-secured calls would also be
+     able to share connections with each other.
+
+ (*) A server-side connection is shared if the client says it is.
+
+ (*) ACK'ing is handled by the protocol driver automatically, including ping
+     replying.
+
+ (*) SO_KEEPALIVE automatically pings the other side to keep the connection
+     alive [TODO].
+
+ (*) If an ICMP error is received, all calls affected by that error will be
+     aborted with an appropriate network error passed through recvmsg().
+
+
+Interaction with the user of the RxRPC socket:
+
+ (*) A socket is made into a server socket by binding an address with a
+     non-zero service ID.
+
+ (*) In the client, sending a request is achieved with one or more sendmsgs,
+     followed by the reply being received with one or more recvmsgs.
+
+ (*) The first sendmsg for a request to be sent from a client contains a tag to
+     be used in all other sendmsgs or recvmsgs associated with that call.  The
+     tag is carried in the control data.
+
+ (*) connect() is used to supply a default destination address for a client
+     socket.  This may be overridden by supplying an alternate address to the
+     first sendmsg() of a call (struct msghdr::msg_name).
+
+ (*) If connect() is called on an unbound client, a random local port will
+     bound before the operation takes place.
+
+ (*) A server socket may also be used to make client calls.  To do this, the
+     first sendmsg() of the call must specify the target address.  The server's
+     transport endpoint is used to send the packets.
+
+ (*) Once the application has received the last message associated with a call,
+     the tag is guaranteed not to be seen again, and so it can be used to pin
+     client resources.  A new call can then be initiated with the same tag
+     without fear of interference.
+
+ (*) In the server, a request is received with one or more recvmsgs, then the
+     the reply is transmitted with one or more sendmsgs, and then the final ACK
+     is received with a last recvmsg.
+
+ (*) When sending data for a call, sendmsg is given MSG_MORE if there's more
+     data to come on that call.
+
+ (*) When receiving data for a call, recvmsg flags MSG_MORE if there's more
+     data to come for that call.
+
+ (*) When receiving data or messages for a call, MSG_EOR is flagged by recvmsg
+     to indicate the terminal message for that call.
+
+ (*) A call may be aborted by adding an abort control message to the control
+     data.  Issuing an abort terminates the kernel's use of that call's tag.
+     Any messages waiting in the receive queue for that call will be discarded.
+
+ (*) Aborts, busy notifications and challenge packets are delivered by recvmsg,
+     and control data messages will be set to indicate the context.  Receiving
+     an abort or a busy message terminates the kernel's use of that call's tag.
+
+ (*) The control data part of the msghdr struct is used for a number of things:
+
+     (*) The tag of the intended or affected call.
+
+     (*) Sending or receiving errors, aborts and busy notifications.
+
+     (*) Notifications of incoming calls.
+
+     (*) Sending debug requests and receiving debug replies [TODO].
+
+ (*) When the kernel has received and set up an incoming call, it sends a
+     message to server application to let it know there's a new call awaiting
+     its acceptance [recvmsg reports a special control message].  The server
+     application then uses sendmsg to assign a tag to the new call.  Once that
+     is done, the first part of the request data will be delivered by recvmsg.
+
+ (*) The server application has to provide the server socket with a keyring of
+     secret keys corresponding to the security types it permits.  When a secure
+     connection is being set up, the kernel looks up the appropriate secret key
+     in the keyring and then sends a challenge packet to the client and
+     receives a response packet.  The kernel then checks the authorisation of
+     the packet and either aborts the connection or sets up the security.
+
+ (*) The name of the key a client will use to secure its communications is
+     nominated by a socket option.
+
+
+Notes on recvmsg:
+
+ (*) If there's a sequence of data messages belonging to a particular call on
+     the receive queue, then recvmsg will keep working through them until:
+
+     (a) it meets the end of that call's received data,
+
+     (b) it meets a non-data message,
+
+     (c) it meets a message belonging to a different call, or
+
+     (d) it fills the user buffer.
+
+     If recvmsg is called in blocking mode, it will keep sleeping, awaiting the
+     reception of further data, until one of the above four conditions is met.
+
+ (2) MSG_PEEK operates similarly, but will return immediately if it has put any
+     data in the buffer rather than sleeping until it can fill the buffer.
+
+ (3) If a data message is only partially consumed in filling a user buffer,
+     then the remainder of that message will be left on the front of the queue
+     for the next taker.  MSG_TRUNC will never be flagged.
+
+ (4) If there is more data to be had on a call (it hasn't copied the last byte
+     of the last data message in that phase yet), then MSG_MORE will be
+     flagged.
+
+
+================
+CONTROL MESSAGES
+================
+
+AF_RXRPC makes use of control messages in sendmsg() and recvmsg() to multiplex
+calls, to invoke certain actions and to report certain conditions.  These are:
+
+	MESSAGE ID		SRT DATA	MEANING
+	=======================	=== ===========	===============================
+	RXRPC_USER_CALL_ID	sr- User ID	App's call specifier
+	RXRPC_ABORT		srt Abort code	Abort code to issue/received
+	RXRPC_ACK		-rt n/a		Final ACK received
+	RXRPC_NET_ERROR		-rt error num	Network error on call
+	RXRPC_BUSY		-rt n/a		Call rejected (server busy)
+	RXRPC_LOCAL_ERROR	-rt error num	Local error encountered
+	RXRPC_NEW_CALL		-r- n/a		New call received
+	RXRPC_ACCEPT		s-- n/a		Accept new call
+
+	(SRT = usable in Sendmsg / delivered by Recvmsg / Terminal message)
+
+ (*) RXRPC_USER_CALL_ID
+
+     This is used to indicate the application's call ID.  It's an unsigned long
+     that the app specifies in the client by attaching it to the first data
+     message or in the server by passing it in association with an RXRPC_ACCEPT
+     message.  recvmsg() passes it in conjunction with all messages except
+     those of the RXRPC_NEW_CALL message.
+
+ (*) RXRPC_ABORT
+
+     This is can be used by an application to abort a call by passing it to
+     sendmsg, or it can be delivered by recvmsg to indicate a remote abort was
+     received.  Either way, it must be associated with an RXRPC_USER_CALL_ID to
+     specify the call affected.  If an abort is being sent, then error EBADSLT
+     will be returned if there is no call with that user ID.
+
+ (*) RXRPC_ACK
+
+     This is delivered to a server application to indicate that the final ACK
+     of a call was received from the client.  It will be associated with an
+     RXRPC_USER_CALL_ID to indicate the call that's now complete.
+
+ (*) RXRPC_NET_ERROR
+
+     This is delivered to an application to indicate that an ICMP error message
+     was encountered in the process of trying to talk to the peer.  An
+     errno-class integer value will be included in the control message data
+     indicating the problem, and an RXRPC_USER_CALL_ID will indicate the call
+     affected.
+
+ (*) RXRPC_BUSY
+
+     This is delivered to a client application to indicate that a call was
+     rejected by the server due to the server being busy.  It will be
+     associated with an RXRPC_USER_CALL_ID to indicate the rejected call.
+
+ (*) RXRPC_LOCAL_ERROR
+
+     This is delivered to an application to indicate that a local error was
+     encountered and that a call has been aborted because of it.  An
+     errno-class integer value will be included in the control message data
+     indicating the problem, and an RXRPC_USER_CALL_ID will indicate the call
+     affected.
+
+ (*) RXRPC_NEW_CALL
+
+     This is delivered to indicate to a server application that a new call has
+     arrived and is awaiting acceptance.  No user ID is associated with this,
+     as a user ID must subsequently be assigned by doing an RXRPC_ACCEPT.
+
+ (*) RXRPC_ACCEPT
+
+     This is used by a server application to attempt to accept a call and
+     assign it a user ID.  It should be associated with an RXRPC_USER_CALL_ID
+     to indicate the user ID to be assigned.  If there is no call to be
+     accepted (it may have timed out, been aborted, etc.), then sendmsg will
+     return error ENODATA.  If the user ID is already in use by another call,
+     then error EBADSLT will be returned.
+
+
+==============
+SOCKET OPTIONS
+==============
+
+AF_RXRPC sockets support a few socket options at the SOL_RXRPC level:
+
+ (*) RXRPC_SECURITY_KEY
+
+     This is used to specify the description of the key to be used.  The key is
+     extracted from the calling process's keyrings with request_key() and
+     should be of "rxrpc" type.
+
+     The optval pointer points to the description string, and optlen indicates
+     how long the string is, without the NUL terminator.
+
+ (*) RXRPC_SECURITY_KEYRING
+
+     Similar to above but specifies a keyring of server secret keys to use (key
+     type "keyring").  See the "Security" section.
+
+ (*) RXRPC_EXCLUSIVE_CONNECTION
+
+     This is used to request that new connections should be used for each call
+     made subsequently on this socket.  optval should be NULL and optlen 0.
+
+ (*) RXRPC_MIN_SECURITY_LEVEL
+
+     This is used to specify the minimum security level required for calls on
+     this socket.  optval must point to an int containing one of the following
+     values:
+
+     (a) RXRPC_SECURITY_PLAIN
+
+	 Encrypted checksum only.
+
+     (b) RXRPC_SECURITY_AUTH
+
+	 Encrypted checksum plus packet padded and first eight bytes of packet
+	 encrypted - which includes the actual packet length.
+
+     (c) RXRPC_SECURITY_ENCRYPTED
+
+	 Encrypted checksum plus entire packet padded and encrypted, including
+	 actual packet length.
+
+
+========
+SECURITY
+========
+
+Currently, only the kerberos 4 equivalent protocol has been implemented
+(security index 2 - rxkad).  This requires the rxkad module to be loaded and,
+on the client, tickets of the appropriate type to be obtained from the AFS
+kaserver or the kerberos server and installed as "rxrpc" type keys.  This is
+normally done using the klog program.  An example simple klog program can be
+found at:
+
+	http://people.redhat.com/~dhowells/rxrpc/klog.c
+
+The payload provided to add_key() on the client should be of the following
+form:
+
+	struct rxrpc_key_sec2_v1 {
+		uint16_t	security_index;	/* 2 */
+		uint16_t	ticket_length;	/* length of ticket[] */
+		uint32_t	expiry;		/* time at which expires */
+		uint8_t		kvno;		/* key version number */
+		uint8_t		__pad[3];
+		uint8_t		session_key[8];	/* DES session key */
+		uint8_t		ticket[0];	/* the encrypted ticket */
+	};
+
+Where the ticket blob is just appended to the above structure.
+
+
+For the server, keys of type "rxrpc_s" must be made available to the server.
+They have a description of "<serviceID>:<securityIndex>" (eg: "52:2" for an
+rxkad key for the AFS VL service).  When such a key is created, it should be
+given the server's secret key as the instantiation data (see the example
+below).
+
+	add_key("rxrpc_s", "52:2", secret_key, 8, keyring);
+
+A keyring is passed to the server socket by naming it in a sockopt.  The server
+socket then looks the server secret keys up in this keyring when secure
+incoming connections are made.  This can be seen in an example program that can
+be found at:
+
+	http://people.redhat.com/~dhowells/rxrpc/listen.c
+
+
+====================
+EXAMPLE CLIENT USAGE
+====================
+
+A client would issue an operation by:
+
+ (1) An RxRPC socket is set up by:
+
+	client = socket(AF_RXRPC, SOCK_DGRAM, PF_INET);
+
+     Where the third parameter indicates the protocol family of the transport
+     socket used - usually IPv4 but it can also be IPv6 [TODO].
+
+ (2) A local address can optionally be bound:
+
+	struct sockaddr_rxrpc srx = {
+		.srx_family	= AF_RXRPC,
+		.srx_service	= 0,  /* we're a client */
+		.transport_type	= SOCK_DGRAM,	/* type of transport socket */
+		.transport.sin_family	= AF_INET,
+		.transport.sin_port	= htons(7000), /* AFS callback */
+		.transport.sin_address	= 0,  /* all local interfaces */
+	};
+	bind(client, &srx, sizeof(srx));
+
+     This specifies the local UDP port to be used.  If not given, a random
+     non-privileged port will be used.  A UDP port may be shared between
+     several unrelated RxRPC sockets.  Security is handled on a basis of
+     per-RxRPC virtual connection.
+
+ (3) The security is set:
+
+	const char *key = "AFS:cambridge.redhat.com";
+	setsockopt(client, SOL_RXRPC, RXRPC_SECURITY_KEY, key, strlen(key));
+
+     This issues a request_key() to get the key representing the security
+     context.  The minimum security level can be set:
+
+	unsigned int sec = RXRPC_SECURITY_ENCRYPTED;
+	setsockopt(client, SOL_RXRPC, RXRPC_MIN_SECURITY_LEVEL,
+		   &sec, sizeof(sec));
+
+ (4) The server to be contacted can then be specified (alternatively this can
+     be done through sendmsg):
+
+	struct sockaddr_rxrpc srx = {
+		.srx_family	= AF_RXRPC,
+		.srx_service	= VL_SERVICE_ID,
+		.transport_type	= SOCK_DGRAM,	/* type of transport socket */
+		.transport.sin_family	= AF_INET,
+		.transport.sin_port	= htons(7005), /* AFS volume manager */
+		.transport.sin_address	= ...,
+	};
+	connect(client, &srx, sizeof(srx));
+
+ (5) The request data should then be posted to the server socket using a series
+     of sendmsg() calls, each with the following control message attached:
+
+	RXRPC_USER_CALL_ID	- specifies the user ID for this call
+
+     MSG_MORE should be set in msghdr::msg_flags on all but the last part of
+     the request.  Multiple requests may be made simultaneously.
+
+     If a call is intended to go to a destination other then the default
+     specified through connect(), then msghdr::msg_name should be set on the
+     first request message of that call.
+
+ (6) The reply data will then be posted to the server socket for recvmsg() to
+     pick up.  MSG_MORE will be flagged by recvmsg() if there's more reply data
+     for a particular call to be read.  MSG_EOR will be set on the terminal
+     read for a call.
+
+     All data will be delivered with the following control message attached:
+
+	RXRPC_USER_CALL_ID	- specifies the user ID for this call
+
+     If an abort or error occurred, this will be returned in the control data
+     buffer instead, and MSG_EOR will be flagged to indicate the end of that
+     call.
+
+
+====================
+EXAMPLE SERVER USAGE
+====================
+
+A server would be set up to accept operations in the following manner:
+
+ (1) An RxRPC socket is created by:
+
+	server = socket(AF_RXRPC, SOCK_DGRAM, PF_INET);
+
+     Where the third parameter indicates the address type of the transport
+     socket used - usually IPv4.
+
+ (2) Security is set up if desired by giving the socket a keyring with server
+     secret keys in it:
+
+	keyring = add_key("keyring", "AFSkeys", NULL, 0,
+			  KEY_SPEC_PROCESS_KEYRING);
+
+	const char secret_key[8] = {
+		0xa7, 0x83, 0x8a, 0xcb, 0xc7, 0x83, 0xec, 0x94 };
+	add_key("rxrpc_s", "52:2", secret_key, 8, keyring);
+
+	setsockopt(server, SOL_RXRPC, RXRPC_SECURITY_KEYRING, "AFSkeys", 7);
+
+     The keyring can be manipulated after it has been given to the socket. This
+     permits the server to add more keys, replace keys, etc. whilst it is live.
+
+ (2) A local address must then be bound:
+
+	struct sockaddr_rxrpc srx = {
+		.srx_family	= AF_RXRPC,
+		.srx_service	= VL_SERVICE_ID, /* RxRPC service ID */
+		.transport_type	= SOCK_DGRAM,	/* type of transport socket */
+		.transport.sin_family	= AF_INET,
+		.transport.sin_port	= htons(7000), /* AFS callback */
+		.transport.sin_address	= 0,  /* all local interfaces */
+	};
+	bind(server, &srx, sizeof(srx));
+
+ (3) The server is then set to listen out for incoming calls:
+
+	listen(server, 100);
+
+ (4) The kernel notifies the server of pending incoming connections by sending
+     it a message for each.  This is received with recvmsg() on the server
+     socket.  It has no data, and has a single dataless control message
+     attached:
+
+	RXRPC_NEW_CALL
+
+     The address that can be passed back by recvmsg() at this point should be
+     ignored since the call for which the message was posted may have gone by
+     the time it is accepted - in which case the first call still on the queue
+     will be accepted.
+
+ (5) The server then accepts the new call by issuing a sendmsg() with two
+     pieces of control data and no actual data:
+
+	RXRPC_ACCEPT		- indicate connection acceptance
+	RXRPC_USER_CALL_ID	- specify user ID for this call
+
+ (6) The first request data packet will then be posted to the server socket for
+     recvmsg() to pick up.  At that point, the RxRPC address for the call can
+     be read from the address fields in the msghdr struct.
+
+     Subsequent request data will be posted to the server socket for recvmsg()
+     to collect as it arrives.  All but the last piece of the request data will
+     be delivered with MSG_MORE flagged.
+
+     All data will be delivered with the following control message attached:
+
+	RXRPC_USER_CALL_ID	- specifies the user ID for this call
+
+ (8) The reply data should then be posted to the server socket using a series
+     of sendmsg() calls, each with the following control messages attached:
+
+	RXRPC_USER_CALL_ID	- specifies the user ID for this call
+
+     MSG_MORE should be set in msghdr::msg_flags on all but the last message
+     for a particular call.
+
+ (9) The final ACK from the client will be posted for retrieval by recvmsg()
+     when it is received.  It will take the form of a dataless message with two
+     control messages attached:
+
+	RXRPC_USER_CALL_ID	- specifies the user ID for this call
+	RXRPC_ACK		- indicates final ACK (no data)
+
+     MSG_EOR will be flagged to indicate that this is the final message for
+     this call.
+
+(10) Up to the point the final packet of reply data is sent, the call can be
+     aborted by calling sendmsg() with a dataless message with the following
+     control messages attached:
+
+	RXRPC_USER_CALL_ID	- specifies the user ID for this call
+	RXRPC_ABORT		- indicates abort code (4 byte data)
+
+     Any packets waiting in the socket's receive queue will be discarded if
+     this is issued.
+
+Note that all the communications for a particular service take place through
+the one server socket, using control messages on sendmsg() and recvmsg() to
+determine the call affected.
+
+
+=========================
+AF_RXRPC KERNEL INTERFACE
+=========================
+
+The AF_RXRPC module also provides an interface for use by in-kernel utilities
+such as the AFS filesystem.  This permits such a utility to:
+
+ (1) Use different keys directly on individual client calls on one socket
+     rather than having to open a whole slew of sockets, one for each key it
+     might want to use.
+
+ (2) Avoid having RxRPC call request_key() at the point of issue of a call or
+     opening of a socket.  Instead the utility is responsible for requesting a
+     key at the appropriate point.  AFS, for instance, would do this during VFS
+     operations such as open() or unlink().  The key is then handed through
+     when the call is initiated.
+
+ (3) Request the use of something other than GFP_KERNEL to allocate memory.
+
+ (4) Avoid the overhead of using the recvmsg() call.  RxRPC messages can be
+     intercepted before they get put into the socket Rx queue and the socket
+     buffers manipulated directly.
+
+To use the RxRPC facility, a kernel utility must still open an AF_RXRPC socket,
+bind an addess as appropriate and listen if it's to be a server socket, but
+then it passes this to the kernel interface functions.
+
+The kernel interface functions are as follows:
+
+ (*) Begin a new client call.
+
+	struct rxrpc_call *
+	rxrpc_kernel_begin_call(struct socket *sock,
+				struct sockaddr_rxrpc *srx,
+				struct key *key,
+				unsigned long user_call_ID,
+				gfp_t gfp);
+
+     This allocates the infrastructure to make a new RxRPC call and assigns
+     call and connection numbers.  The call will be made on the UDP port that
+     the socket is bound to.  The call will go to the destination address of a
+     connected client socket unless an alternative is supplied (srx is
+     non-NULL).
+
+     If a key is supplied then this will be used to secure the call instead of
+     the key bound to the socket with the RXRPC_SECURITY_KEY sockopt.  Calls
+     secured in this way will still share connections if at all possible.
+
+     The user_call_ID is equivalent to that supplied to sendmsg() in the
+     control data buffer.  It is entirely feasible to use this to point to a
+     kernel data structure.
+
+     If this function is successful, an opaque reference to the RxRPC call is
+     returned.  The caller now holds a reference on this and it must be
+     properly ended.
+
+ (*) End a client call.
+
+	void rxrpc_kernel_end_call(struct rxrpc_call *call);
+
+     This is used to end a previously begun call.  The user_call_ID is expunged
+     from AF_RXRPC's knowledge and will not be seen again in association with
+     the specified call.
+
+ (*) Send data through a call.
+
+	int rxrpc_kernel_send_data(struct rxrpc_call *call, struct msghdr *msg,
+				   size_t len);
+
+     This is used to supply either the request part of a client call or the
+     reply part of a server call.  msg.msg_iovlen and msg.msg_iov specify the
+     data buffers to be used.  msg_iov may not be NULL and must point
+     exclusively to in-kernel virtual addresses.  msg.msg_flags may be given
+     MSG_MORE if there will be subsequent data sends for this call.
+
+     The msg must not specify a destination address, control data or any flags
+     other than MSG_MORE.  len is the total amount of data to transmit.
+
+ (*) Abort a call.
+
+	void rxrpc_kernel_abort_call(struct rxrpc_call *call, u32 abort_code);
+
+     This is used to abort a call if it's still in an abortable state.  The
+     abort code specified will be placed in the ABORT message sent.
+
+ (*) Intercept received RxRPC messages.
+
+	typedef void (*rxrpc_interceptor_t)(struct sock *sk,
+					    unsigned long user_call_ID,
+					    struct sk_buff *skb);
+
+	void
+	rxrpc_kernel_intercept_rx_messages(struct socket *sock,
+					   rxrpc_interceptor_t interceptor);
+
+     This installs an interceptor function on the specified AF_RXRPC socket.
+     All messages that would otherwise wind up in the socket's Rx queue are
+     then diverted to this function.  Note that care must be taken to process
+     the messages in the right order to maintain DATA message sequentiality.
+
+     The interceptor function itself is provided with the address of the socket
+     and handling the incoming message, the ID assigned by the kernel utility
+     to the call and the socket buffer containing the message.
+
+     The skb->mark field indicates the type of message:
+
+	MARK				MEANING
+	===============================	=======================================
+	RXRPC_SKB_MARK_DATA		Data message
+	RXRPC_SKB_MARK_FINAL_ACK	Final ACK received for an incoming call
+	RXRPC_SKB_MARK_BUSY		Client call rejected as server busy
+	RXRPC_SKB_MARK_REMOTE_ABORT	Call aborted by peer
+	RXRPC_SKB_MARK_NET_ERROR	Network error detected
+	RXRPC_SKB_MARK_LOCAL_ERROR	Local error encountered
+	RXRPC_SKB_MARK_NEW_CALL		New incoming call awaiting acceptance
+
+     The remote abort message can be probed with rxrpc_kernel_get_abort_code().
+     The two error messages can be probed with rxrpc_kernel_get_error_number().
+     A new call can be accepted with rxrpc_kernel_accept_call().
+
+     Data messages can have their contents extracted with the usual bunch of
+     socket buffer manipulation functions.  A data message can be determined to
+     be the last one in a sequence with rxrpc_kernel_is_data_last().  When a
+     data message has been used up, rxrpc_kernel_data_delivered() should be
+     called on it..
+
+     Non-data messages should be handled to rxrpc_kernel_free_skb() to dispose
+     of.  It is possible to get extra refs on all types of message for later
+     freeing, but this may pin the state of a call until the message is finally
+     freed.
+
+ (*) Accept an incoming call.
+
+	struct rxrpc_call *
+	rxrpc_kernel_accept_call(struct socket *sock,
+				 unsigned long user_call_ID);
+
+     This is used to accept an incoming call and to assign it a call ID.  This
+     function is similar to rxrpc_kernel_begin_call() and calls accepted must
+     be ended in the same way.
+
+     If this function is successful, an opaque reference to the RxRPC call is
+     returned.  The caller now holds a reference on this and it must be
+     properly ended.
+
+ (*) Reject an incoming call.
+
+	int rxrpc_kernel_reject_call(struct socket *sock);
+
+     This is used to reject the first incoming call on the socket's queue with
+     a BUSY message.  -ENODATA is returned if there were no incoming calls.
+     Other errors may be returned if the call had been aborted (-ECONNABORTED)
+     or had timed out (-ETIME).
+
+ (*) Record the delivery of a data message and free it.
+
+	void rxrpc_kernel_data_delivered(struct sk_buff *skb);
+
+     This is used to record a data message as having been delivered and to
+     update the ACK state for the call.  The socket buffer will be freed.
+
+ (*) Free a message.
+
+	void rxrpc_kernel_free_skb(struct sk_buff *skb);
+
+     This is used to free a non-DATA socket buffer intercepted from an AF_RXRPC
+     socket.
+
+ (*) Determine if a data message is the last one on a call.
+
+	bool rxrpc_kernel_is_data_last(struct sk_buff *skb);
+
+     This is used to determine if a socket buffer holds the last data message
+     to be received for a call (true will be returned if it does, false
+     if not).
+
+     The data message will be part of the reply on a client call and the
+     request on an incoming call.  In the latter case there will be more
+     messages, but in the former case there will not.
+
+ (*) Get the abort code from an abort message.
+
+	u32 rxrpc_kernel_get_abort_code(struct sk_buff *skb);
+
+     This is used to extract the abort code from a remote abort message.
+
+ (*) Get the error number from a local or network error message.
+
+	int rxrpc_kernel_get_error_number(struct sk_buff *skb);
+
+     This is used to extract the error number from a message indicating either
+     a local error occurred or a network error occurred.
diff --git a/Documentation/networking/wan-router.txt b/Documentation/networking/wan-router.txt
index 653978d..07dd6d9 100644
--- a/Documentation/networking/wan-router.txt
+++ b/Documentation/networking/wan-router.txt
@@ -250,7 +250,6 @@
 	sdladrv.h	SDLA support module API definitions
 	sdlasfm.h	SDLA firmware module definitions
 	if_wanpipe.h	WANPIPE Socket definitions
-	if_wanpipe_common.h	WANPIPE Socket/Driver common definitions.
 	sdlapci.h	WANPIPE PCI definitions
 	
 
diff --git a/Documentation/power/interface.txt b/Documentation/power/interface.txt
index 74311d7..8c5b41b 100644
--- a/Documentation/power/interface.txt
+++ b/Documentation/power/interface.txt
@@ -18,17 +18,10 @@
 
 
 /sys/power/disk controls the operating mode of the suspend-to-disk
-mechanism. Suspend-to-disk can be handled in several ways. The
-greatest distinction is who writes memory to disk - the firmware or
-the kernel. If the firmware does it, we assume that it also handles
-suspending the system. 
-
-If the kernel does it, then we have three options for putting the system
-to sleep - using the platform driver (e.g. ACPI or other PM
-registers), powering off the system or rebooting the system (for
-testing). The system will support either 'firmware' or 'platform', and
-that is known a priori. But, the user may choose 'shutdown' or
-'reboot' as alternatives. 
+mechanism. Suspend-to-disk can be handled in several ways. We have a
+few options for putting the system to sleep - using the platform driver
+(e.g. ACPI or other pm_ops), powering off the system or rebooting the
+system (for testing).
 
 Additionally, /sys/power/disk can be used to turn on one of the two testing
 modes of the suspend-to-disk mechanism: 'testproc' or 'test'.  If the
@@ -44,16 +37,12 @@
 Reading from this file will display what the mode is currently set
 to. Writing to this file will accept one of
 
-       'firmware'
-       'platform'
+       'platform' (only if the platform supports it)
        'shutdown'
        'reboot'
        'testproc'
        'test'
 
-It will only change to 'firmware' or 'platform' if the system supports
-it. 
-
 /sys/power/image_size controls the size of the image created by
 the suspend-to-disk mechanism.  It can be written a string
 representing a non-negative integer that will be used as an upper
diff --git a/Documentation/power/states.txt b/Documentation/power/states.txt
index 0931a33..34800cc 100644
--- a/Documentation/power/states.txt
+++ b/Documentation/power/states.txt
@@ -62,17 +62,18 @@
 inconvenience, this method requires minimal work by the kernel, since
 the firmware will also handle restoring memory contents on resume. 
 
-If the kernel is responsible for persistently saving state, a mechanism
-called 'swsusp' (Swap Suspend) is used to write memory contents to
-free swap space. swsusp has some restrictive requirements, but should
-work in most cases. Some, albeit outdated, documentation can be found
-in Documentation/power/swsusp.txt. 
+For suspend-to-disk, a mechanism called swsusp called 'swsusp' (Swap
+Suspend) is used to write memory contents to free swap space.
+swsusp has some restrictive requirements, but should work in most
+cases. Some, albeit outdated, documentation can be found in
+Documentation/power/swsusp.txt. Alternatively, userspace can do most
+of the actual suspend to disk work, see userland-swsusp.txt.
 
 Once memory state is written to disk, the system may either enter a
 low-power state (like ACPI S4), or it may simply power down. Powering
 down offers greater savings, and allows this mechanism to work on any
 system. However, entering a real low-power state allows the user to
-trigger wake up events (e.g. pressing a key or opening a laptop lid). 
+trigger wake up events (e.g. pressing a key or opening a laptop lid).
 
 A transition from Suspend-to-Disk to the On state should take about 30
 seconds, though it's typically a bit more with the current
diff --git a/Documentation/power/swsusp.txt b/Documentation/power/swsusp.txt
index 0761ff6..c55bd50 100644
--- a/Documentation/power/swsusp.txt
+++ b/Documentation/power/swsusp.txt
@@ -156,8 +156,7 @@
 be very careful).
 
 
-Q: What is the difference between "platform", "shutdown" and
-"firmware" in /sys/power/disk?
+Q: What is the difference between "platform" and "shutdown"?
 
 A:
 
@@ -166,11 +165,8 @@
 platform: save state in linux, then tell bios to powerdown and blink
           "suspended led"
 
-firmware: tell bios to save state itself [needs BIOS-specific suspend
-	  partition, and has very little to do with swsusp]
-
-"platform" is actually right thing to do, but "shutdown" is most
-reliable.
+"platform" is actually right thing to do where supported, but
+"shutdown" is most reliable (except on ACPI systems).
 
 Q: I do not understand why you have such strong objections to idea of
 selective suspend.
@@ -388,8 +384,8 @@
 modes like "suspend-to-RAM" or "standby".  (Don't write "disk" to the
 /sys/power/state file; write "standby" or "mem".)  We've not seen any
 hardware that can use these modes through software suspend, although in
-theory some systems might support "platform" or "firmware" modes that
-won't break the USB connections.
+theory some systems might support "platform" modes that won't break the
+USB connections.
 
 Remember that it's always a bad idea to unplug a disk drive containing a
 mounted filesystem.  That's true even when your system is asleep!  The
diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt
index b41397d..033a3f3 100644
--- a/Documentation/powerpc/booting-without-of.txt
+++ b/Documentation/powerpc/booting-without-of.txt
@@ -39,7 +39,7 @@
                            and property data. The old style variable
                            alignment would make it impossible to do
                            "simple" insertion of properties using
-                           memove (thanks Milton for
+                           memmove (thanks Milton for
                            noticing). Updated kernel patch as well
 			 - Correct a few more alignment constraints
 			 - Add a chapter about the device-tree
@@ -55,7 +55,7 @@
 
  ToDo:
 	- Add some definitions of interrupt tree (simple/complex)
-	- Add some definitions for pci host bridges
+	- Add some definitions for PCI host bridges
 	- Add some common address format examples
 	- Add definitions for standard properties and "compatible"
 	  names for cells that are not already defined by the existing
@@ -114,7 +114,7 @@
         forth words isn't required), you can enter the kernel with:
 
               r5 : OF callback pointer as defined by IEEE 1275
-              bindings to powerpc. Only the 32 bit client interface
+              bindings to powerpc. Only the 32-bit client interface
               is currently supported
 
               r3, r4 : address & length of an initrd if any or 0
@@ -194,7 +194,7 @@
   for this is to keep kernels on embedded systems small and efficient;
   part of this is due to the fact the code is already that way. In the
   future, a kernel may support multiple platforms, but only if the
-  platforms feature the same core architectire.  A single kernel build
+  platforms feature the same core architecture.  A single kernel build
   cannot support both configurations with Book E and configurations
   with classic Powerpc architectures.
 
@@ -215,7 +215,7 @@
   enable another config option to select the specific board
   supported.
 
-NOTE: If ben doesn't merge the setup files, may need to change this to
+NOTE: If Ben doesn't merge the setup files, may need to change this to
 point to setup_32.c
 
 
@@ -256,7 +256,7 @@
         u32     off_dt_struct;          /* offset to structure */
         u32     off_dt_strings;         /* offset to strings */
         u32     off_mem_rsvmap;         /* offset to memory reserve map
-*/
+                                           */
         u32     version;                /* format version */
         u32     last_comp_version;      /* last compatible version */
 
@@ -265,6 +265,9 @@
                                            booting on */
         /* version 3 fields below */
         u32     size_dt_strings;        /* size of the strings block */
+
+        /* version 17 fields below */
+        u32	size_dt_struct;		/* size of the DT structure block */
 };
 
    Along with the constants:
@@ -273,7 +276,7 @@
 #define OF_DT_HEADER            0xd00dfeed      /* 4: version,
 						   4: total size */
 #define OF_DT_BEGIN_NODE        0x1             /* Start node: full name
-*/
+						   */
 #define OF_DT_END_NODE          0x2             /* End node */
 #define OF_DT_PROP              0x3             /* Property: name off,
                                                    size, content */
@@ -310,9 +313,8 @@
    - off_mem_rsvmap
 
      This is an offset from the beginning of the header to the start
-     of the reserved memory map. This map is a list of pairs of 64
+     of the reserved memory map. This map is a list of pairs of 64-
      bit integers. Each pair is a physical address and a size. The
-
      list is terminated by an entry of size 0. This map provides the
      kernel with a list of physical memory areas that are "reserved"
      and thus not to be used for memory allocations, especially during
@@ -325,7 +327,7 @@
      contain _at least_ this DT block itself (header,total_size). If
      you are passing an initrd to the kernel, you should reserve it as
      well. You do not need to reserve the kernel image itself. The map
-     should be 64 bit aligned.
+     should be 64-bit aligned.
 
    - version
 
@@ -335,10 +337,13 @@
      to reallocate it easily at boot and free up the unused flattened
      structure after expansion. Version 16 introduces a new more
      "compact" format for the tree itself that is however not backward
-     compatible. You should always generate a structure of the highest
-     version defined at the time of your implementation. Currently
-     that is version 16, unless you explicitly aim at being backward
-     compatible.
+     compatible. Version 17 adds an additional field, size_dt_struct,
+     allowing it to be reallocated or moved more easily (this is
+     particularly useful for bootloaders which need to make
+     adjustments to a device tree based on probed information). You
+     should always generate a structure of the highest version defined
+     at the time of your implementation. Currently that is version 17,
+     unless you explicitly aim at being backward compatible.
 
    - last_comp_version
 
@@ -347,7 +352,7 @@
      is backward compatible with version 1 (that is, a kernel build
      for version 1 will be able to boot with a version 2 format). You
      should put a 1 in this field if you generate a device tree of
-     version 1 to 3, or 0x10 if you generate a tree of version 0x10
+     version 1 to 3, or 16 if you generate a tree of version 16 or 17
      using the new unit name format.
 
    - boot_cpuid_phys
@@ -360,6 +365,17 @@
      point (see further chapters for more informations on the required
      device-tree contents)
 
+   - size_dt_strings
+
+     This field only exists on version 3 and later headers.  It
+     gives the size of the "strings" section of the device tree (which
+     starts at the offset given by off_dt_strings).
+
+   - size_dt_struct
+
+     This field only exists on version 17 and later headers.  It gives
+     the size of the "structure" section of the device tree (which
+     starts at the offset given by off_dt_struct).
 
    So the typical layout of a DT block (though the various parts don't
    need to be in that order) looks like this (addresses go from top to
@@ -417,7 +433,7 @@
 A node has 2 names. The actual node name is generally contained in a
 property of type "name" in the node property list whose value is a
 zero terminated string and is mandatory for version 1 to 3 of the
-format definition (as it is in Open Firmware). Version 0x10 makes it
+format definition (as it is in Open Firmware). Version 16 makes it
 optional as it can generate it from the unit name defined below.
 
 There is also a "unit name" that is used to differentiate nodes with
@@ -461,7 +477,7 @@
 interrupt tree which will be described in a further version of this
 document.
 
-This "linux, phandle" property is a 32 bit value that uniquely
+This "linux, phandle" property is a 32-bit value that uniquely
 identifies a node. You are free to use whatever values or system of
 values, internal pointers, or whatever to generate these, the only
 requirement is that every node for which you provide that property has
@@ -471,7 +487,7 @@
 designates a node followed by the node unit name. Properties are
 presented with their name followed by their content. "content"
 represents an ASCII string (zero terminated) value, while <content>
-represents a 32 bit hexadecimal value. The various nodes in this
+represents a 32-bit hexadecimal value. The various nodes in this
 example will be discussed in a later chapter. At this point, it is
 only meant to give you a idea of what a device-tree looks like. I have
 purposefully kept the "name" and "linux,phandle" properties which
@@ -543,15 +559,15 @@
      * [align gap to next 4 bytes boundary]
      * for each property:
         * token OF_DT_PROP (that is 0x00000003)
-        * 32 bit value of property value size in bytes (or 0 of no
-     * value)
-        * 32 bit value of offset in string block of property name
+        * 32-bit value of property value size in bytes (or 0 if no
+          value)
+        * 32-bit value of offset in string block of property name
         * property value data if any
         * [align gap to next 4 bytes boundary]
      * [child nodes if any]
      * token OF_DT_END_NODE (that is 0x00000002)
 
-So the node content can be summarised as a start token, a full path,
+So the node content can be summarized as a start token, a full path,
 a list of properties, a list of child nodes, and an end token. Every
 child node is a full node structure itself as defined above.
 
@@ -583,7 +599,7 @@
 ----------------------------------------------
 
 The general rule is documented in the various Open Firmware
-documentations. If you chose to describe a bus with the device-tree
+documentations. If you choose to describe a bus with the device-tree
 and there exist an OF bus binding, then you should follow the
 specification. However, the kernel does not require every single
 device or bus to be described by the device tree.
@@ -596,9 +612,9 @@
 on the processor bus.
 
 Those 2 properties define 'cells' for representing an address and a
-size. A "cell" is a 32 bit number. For example, if both contain 2
+size. A "cell" is a 32-bit number. For example, if both contain 2
 like the example tree given above, then an address and a size are both
-composed of 2 cells, and each is a 64 bit number (cells are
+composed of 2 cells, and each is a 64-bit number (cells are
 concatenated and expected to be in big endian format). Another example
 is the way Apple firmware defines them, with 2 cells for an address
 and one cell for a size.  Most 32-bit implementations should define
@@ -632,7 +648,7 @@
 
 The "reg" property only defines addresses and sizes (if #size-cells
 is non-0) within a given bus. In order to translate addresses upward
-(that is into parent bus addresses, and possibly into cpu physical
+(that is into parent bus addresses, and possibly into CPU physical
 addresses), all busses must contain a "ranges" property. If the
 "ranges" property is missing at a given level, it's assumed that
 translation isn't possible. The format of the "ranges" property for a
@@ -648,9 +664,9 @@
 PCI<->ISA bridge, that would be a PCI address. It defines the base
 address in the parent bus where the beginning of that range is mapped.
 
-For a new 64 bit powerpc board, I recommend either the 2/2 format or
+For a new 64-bit powerpc board, I recommend either the 2/2 format or
 Apple's 2/1 format which is slightly more compact since sizes usually
-fit in a single 32 bit word.   New 32 bit powerpc boards should use a
+fit in a single 32-bit word.   New 32-bit powerpc boards should use a
 1/1 format, unless the processor supports physical addresses greater
 than 32-bits, in which case a 2/1 format is recommended.
 
@@ -764,7 +780,7 @@
   Required properties:
 
     - device_type : has to be "cpu"
-    - reg : This is the physical cpu number, it's a single 32 bit cell
+    - reg : This is the physical CPU number, it's a single 32-bit cell
       and is also used as-is as the unit number for constructing the
       unit name in the full path. For example, with 2 CPUs, you would
       have the full path:
@@ -785,7 +801,7 @@
       the kernel timebase/decrementer calibration based on this
       value.
     - clock-frequency : a cell indicating the CPU core clock frequency
-      in Hz. A new property will be defined for 64 bit values, but if
+      in Hz. A new property will be defined for 64-bit values, but if
       your frequency is < 4Ghz, one cell is enough. Here as well as
       for the above, the common code doesn't use that property, but
       you are welcome to re-use the pSeries or Maple one. A future
@@ -832,8 +848,7 @@
 
   This node is a bit "special". Normally, that's where open firmware
   puts some variable environment information, like the arguments, or
-  phandle pointers to nodes like the main interrupt controller, or the
-  default input/output devices.
+  the default input/output devices.
 
   This specification makes a few of these mandatory, but also defines
   some linux-specific properties that would be normally constructed by
@@ -853,14 +868,14 @@
       that the kernel tries to find out the default console and has
       knowledge of various types like 8250 serial ports. You may want
       to extend this function to add your own.
-    - interrupt-controller : This is one cell containing a phandle
-      value that matches the "linux,phandle" property of your main
-      interrupt controller node. May be used for interrupt routing.
-
 
   Note that u-boot creates and fills in the chosen node for platforms
   that use it.
 
+  (Note: a practice that is now obsolete was to include a property
+  under /chosen called interrupt-controller which had a phandle value
+  that pointed to the main interrupt controller)
+
   f) the /soc<SOCname> node
 
   This node is used to represent a system-on-a-chip (SOC) and must be
@@ -908,8 +923,7 @@
   The SOC node may contain child nodes for each SOC device that the
   platform uses.  Nodes should not be created for devices which exist
   on the SOC but are not used by a particular platform. See chapter VI
-  for more information on how to specify devices that are part of an
-SOC.
+  for more information on how to specify devices that are part of a SOC.
 
   Example SOC node for the MPC8540:
 
@@ -972,7 +986,7 @@
         [-o output-filename] [-V output_version] input_filename
 
 
-The "output_version" defines what versio of the "blob" format will be
+The "output_version" defines what version of the "blob" format will be
 generated. Supported versions are 1,2,3 and 16. The default is
 currently version 3 but that may change in the future to version 16.
 
@@ -994,12 +1008,12 @@
 				 */
 
   property2 = <1234abcd>;	/* define a property containing a
-                                 * numerical 32 bits value (hexadecimal)
+                                 * numerical 32-bit value (hexadecimal)
 				 */
 
   property3 = <12345678 12345678 deadbeef>;
                                 /* define a property containing 3
-                                 * numerical 32 bits values (cells) in
+                                 * numerical 32-bit values (cells) in
                                  * hexadecimal
 				 */
   property4 = [0a 0b 0c 0d de ea ad be ef];
@@ -1068,7 +1082,7 @@
     its usage in early_init_devtree(), and the corresponding various
     early_init_dt_scan_*() callbacks. That code can be re-used in a
     GPL bootloader, and as the author of that code, I would be happy
-    to discuss possible free licencing to any vendor who wishes to
+    to discuss possible free licensing to any vendor who wishes to
     integrate all or part of this code into a non-GPL bootloader.
 
 
@@ -1077,7 +1091,7 @@
 =======================================
 
 Many companies are now starting to develop system-on-a-chip
-processors, where the processor core (cpu) and many peripheral devices
+processors, where the processor core (CPU) and many peripheral devices
 exist on a single piece of silicon.  For these SOCs, an SOC node
 should be used that defines child nodes for the devices that make
 up the SOC. While platforms are not required to use this model in
@@ -1109,42 +1123,7 @@
 MPC8540.
 
 
-2) Specifying interrupt information for SOC devices
----------------------------------------------------
-
-Each device that is part of an SOC and which generates interrupts
-should have the following properties:
-
-	- interrupt-parent : contains the phandle of the interrupt
-          controller which handles interrupts for this device
-	- interrupts : a list of tuples representing the interrupt
-          number and the interrupt sense and level for each interrupt
-          for this device.
-
-This information is used by the kernel to build the interrupt table
-for the interrupt controllers in the system.
-
-Sense and level information should be encoded as follows:
-
-   Devices connected to openPIC-compatible controllers should encode
-   sense and polarity as follows:
-
-	0 = low to high edge sensitive type enabled
-	1 = active low level sensitive type enabled
-	2 = active high level sensitive type enabled
-	3 = high to low edge sensitive type enabled
-
-   ISA PIC interrupt controllers should adhere to the ISA PIC
-   encodings listed below:
-
-	0 =  active low level sensitive type enabled
-	1 =  active high level sensitive type enabled
-	2 =  high to low edge sensitive type enabled
-	3 =  low to high edge sensitive type enabled
-
-
-
-3) Representing devices without a current OF specification
+2) Representing devices without a current OF specification
 ----------------------------------------------------------
 
 Currently, there are many devices on SOCs that do not have a standard
@@ -1201,6 +1180,13 @@
     - phy-handle : The phandle for the PHY connected to this ethernet
       controller.
 
+  Recommended properties:
+
+    - linux,network-index : This is the intended "index" of this
+      network device.  This is used by the bootwrapper to interpret
+      MAC addresses passed by the firmware when no information other
+      than indices is available to associate an address with a device.
+
   Example:
 
 	ethernet@24000 {
@@ -1312,10 +1298,10 @@
    and additions :  
 
    Required properties :
-    - compatible : Should be "fsl-usb2-mph" for multi port host usb
-      controllers, or "fsl-usb2-dr" for dual role usb controllers
-    - phy_type : For multi port host usb controllers, should be one of
-      "ulpi", or "serial". For dual role usb controllers, should be
+    - compatible : Should be "fsl-usb2-mph" for multi port host USB
+      controllers, or "fsl-usb2-dr" for dual role USB controllers
+    - phy_type : For multi port host USB controllers, should be one of
+      "ulpi", or "serial". For dual role USB controllers, should be
       one of "ulpi", "utmi", "utmi_wide", or "serial".
     - reg : Offset and length of the register set for the device
     - port0 : boolean; if defined, indicates port0 is connected for
@@ -1339,7 +1325,7 @@
     - interrupt-parent : the phandle for the interrupt controller that
       services interrupts for this device.
 
-   Example multi port host usb controller device node : 
+   Example multi port host USB controller device node :
 	usb@22000 {
 	        device_type = "usb";
 		compatible = "fsl-usb2-mph";
@@ -1353,7 +1339,7 @@
 		port1;
 	};
 
-   Example dual role usb controller device node : 
+   Example dual role USB controller device node :
 	usb@23000 {
 		device_type = "usb";
 		compatible = "fsl-usb2-dr";
@@ -1387,7 +1373,7 @@
     - channel-fifo-len : An integer representing the number of
       descriptor pointers each channel fetch fifo can hold.
     - exec-units-mask : The bitmask representing what execution units
-      (EUs) are available. It's a single 32 bit cell. EU information
+      (EUs) are available. It's a single 32-bit cell. EU information
       should be encoded following the SEC's Descriptor Header Dword
       EU_SEL0 field documentation, i.e. as follows:
 
@@ -1403,7 +1389,7 @@
       bits 8 through 31 are reserved for future SEC EUs.
 
     - descriptor-types-mask : The bitmask representing what descriptors
-      are available. It's a single 32 bit cell. Descriptor type
+      are available. It's a single 32-bit cell. Descriptor type
       information should be encoded following the SEC's Descriptor
       Header Dword DESC_TYPE field documentation, i.e. as follows:
 
@@ -1492,7 +1478,7 @@
    Required properties:
    - device_type : should be "spi".
    - compatible : should be "fsl_spi".
-   - mode : the spi operation mode, it can be "cpu" or "qe".
+   - mode : the SPI operation mode, it can be "cpu" or "qe".
    - reg : Offset and length of the register set for the device
    - interrupts : <a b> where a is the interrupt number and b is a
      field that represents an encoding of the sense and level
@@ -1569,6 +1555,12 @@
    - mac-address : list of bytes representing the ethernet address.
    - phy-handle : The phandle for the PHY connected to this controller.
 
+   Recommended properties:
+   - linux,network-index : This is the intended "index" of this
+     network device.  This is used by the bootwrapper to interpret
+     MAC addresses passed by the firmware when no information other
+     than indices is available to associate an address with a device.
+
    Example:
 	ucc@2000 {
 		device_type = "network";
@@ -1712,7 +1704,7 @@
      - partitions : Several pairs of 32-bit values where the first value is
        partition's offset from the start of the device and the second one is
        partition size in bytes with LSB used to signify a read only
-       partition (so, the parition size should always be an even number).
+       partition (so, the partition size should always be an even number).
      - partition-names : The list of concatenated zero terminated strings
        representing the partition names.
      - probe-type : The type of probe which should be done for the chip
@@ -1733,6 +1725,92 @@
 
    More devices will be defined as this spec matures.
 
+VII - Specifying interrupt information for devices
+===================================================
+
+The device tree represents the busses and devices of a hardware
+system in a form similar to the physical bus topology of the
+hardware.
+
+In addition, a logical 'interrupt tree' exists which represents the
+hierarchy and routing of interrupts in the hardware.
+
+The interrupt tree model is fully described in the
+document "Open Firmware Recommended Practice: Interrupt
+Mapping Version 0.9".  The document is available at:
+<http://playground.sun.com/1275/practice>.
+
+1) interrupts property
+----------------------
+
+Devices that generate interrupts to a single interrupt controller
+should use the conventional OF representation described in the
+OF interrupt mapping documentation.
+
+Each device which generates interrupts must have an 'interrupt'
+property.  The interrupt property value is an arbitrary number of
+of 'interrupt specifier' values which describe the interrupt or
+interrupts for the device.
+
+The encoding of an interrupt specifier is determined by the
+interrupt domain in which the device is located in the
+interrupt tree.  The root of an interrupt domain specifies in
+its #interrupt-cells property the number of 32-bit cells
+required to encode an interrupt specifier.  See the OF interrupt
+mapping documentation for a detailed description of domains.
+
+For example, the binding for the OpenPIC interrupt controller
+specifies  an #interrupt-cells value of 2 to encode the interrupt
+number and level/sense information. All interrupt children in an
+OpenPIC interrupt domain use 2 cells per interrupt in their interrupts
+property.
+
+The PCI bus binding specifies a #interrupt-cell value of 1 to encode
+which interrupt pin (INTA,INTB,INTC,INTD) is used.
+
+2) interrupt-parent property
+----------------------------
+
+The interrupt-parent property is specified to define an explicit
+link between a device node and its interrupt parent in
+the interrupt tree.  The value of interrupt-parent is the
+phandle of the parent node.
+
+If the interrupt-parent property is not defined for a node, it's
+interrupt parent is assumed to be an ancestor in the node's
+_device tree_ hierarchy.
+
+3) OpenPIC Interrupt Controllers
+--------------------------------
+
+OpenPIC interrupt controllers require 2 cells to encode
+interrupt information.  The first cell defines the interrupt
+number.  The second cell defines the sense and level
+information.
+
+Sense and level information should be encoded as follows:
+
+	0 = low to high edge sensitive type enabled
+	1 = active low level sensitive type enabled
+	2 = active high level sensitive type enabled
+	3 = high to low edge sensitive type enabled
+
+4) ISA Interrupt Controllers
+----------------------------
+
+ISA PIC interrupt controllers require 2 cells to encode
+interrupt information.  The first cell defines the interrupt
+number.  The second cell defines the sense and level
+information.
+
+ISA PIC interrupt controllers should adhere to the ISA PIC
+encodings listed below:
+
+	0 =  active low level sensitive type enabled
+	1 =  active high level sensitive type enabled
+	2 =  high to low edge sensitive type enabled
+	3 =  low to high edge sensitive type enabled
+
 
 Appendix A - Sample SOC node for MPC8540
 ========================================
diff --git a/Documentation/s390/crypto/crypto-API.txt b/Documentation/s390/crypto/crypto-API.txt
deleted file mode 100644
index 71ae6ca..0000000
--- a/Documentation/s390/crypto/crypto-API.txt
+++ /dev/null
@@ -1,83 +0,0 @@
-crypto-API support for z990 Message Security Assist (MSA) instructions
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-AUTHOR:	Thomas Spatzier (tspat@de.ibm.com)
-
-
-1. Introduction crypto-API
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-See Documentation/crypto/api-intro.txt for an introduction/description of the
-kernel crypto API.
-According to api-intro.txt support for z990 crypto instructions has been added
-in the algorithm api layer of the crypto API. Several files containing z990
-optimized implementations of crypto algorithms are placed in the
-arch/s390/crypto directory.
-
-
-2. Probing for availability of MSA
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-It should be possible to use Kernels with the z990 crypto implementations both
-on machines with MSA available and on those without MSA (pre z990 or z990
-without MSA). Therefore a simple probing mechanism has been implemented:
-In the init function of each crypto module the availability of MSA and of the
-respective crypto algorithm in particular will be tested. If the algorithm is
-available the module will load and register its algorithm with the crypto API.
-
-If the respective crypto algorithm is not available, the init function will
-return -ENOSYS. In that case a fallback to the standard software implementation
-of the crypto algorithm must be taken ( -> the standard crypto modules are
-also built when compiling the kernel).
-
-
-3. Ensuring z990 crypto module preference
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-If z990 crypto instructions are available the optimized modules should be
-preferred instead of standard modules.
-
-3.1. compiled-in modules
-~~~~~~~~~~~~~~~~~~~~~~~~
-For compiled-in modules it has to be ensured that the z990 modules are linked
-before the standard crypto modules. Then, on system startup the init functions
-of z990 crypto modules will be called first and query for availability of z990
-crypto instructions. If instruction is available, the z990 module will register
-its crypto algorithm implementation -> the load of the standard module will fail
-since the algorithm is already registered.
-If z990 crypto instruction is not available the load of the z990 module will
-fail -> the standard module will load and register its algorithm.
-
-3.2. dynamic modules
-~~~~~~~~~~~~~~~~~~~~
-A system administrator has to take care of giving preference to z990 crypto
-modules. If MSA is available appropriate lines have to be added to
-/etc/modprobe.conf.
-
-Example:	z990 crypto instruction for SHA1 algorithm is available
-
-		add the following line to /etc/modprobe.conf (assuming the
-		z990 crypto modules for SHA1 is called sha1_z990):
-
-		alias sha1 sha1_z990
-
-		-> when the sha1 algorithm is requested through the crypto API
-		(which has a module autoloader) the z990 module will be loaded.
-
-TBD:	a userspace module probing mechanism
-	something like 'probe sha1 sha1_z990 sha1' in modprobe.conf
-	-> try module sha1_z990, if it fails to load standard module sha1
-	the 'probe' statement is currently not supported in modprobe.conf
-
-
-4. Currently implemented z990 crypto algorithms
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The following crypto algorithms with z990 MSA support are currently implemented.
-The name of each algorithm under which it is registered in crypto API and the
-name of the respective module is given in square brackets.
-
-- SHA1 Digest Algorithm [sha1 -> sha1_z990]
-- DES Encrypt/Decrypt Algorithm (64bit key) [des -> des_z990]
-- Triple DES Encrypt/Decrypt Algorithm (128bit key) [des3_ede128 -> des_z990]
-- Triple DES Encrypt/Decrypt Algorithm (192bit key) [des3_ede -> des_z990]
-
-In order to load, for example, the sha1_z990 module when the sha1 algorithm is
-requested (see 3.2.) add 'alias sha1 sha1_z990' to /etc/modprobe.conf.
-
diff --git a/Documentation/s390/zfcpdump.txt b/Documentation/s390/zfcpdump.txt
new file mode 100644
index 0000000..cf45d27
--- /dev/null
+++ b/Documentation/s390/zfcpdump.txt
@@ -0,0 +1,87 @@
+s390 SCSI dump tool (zfcpdump)
+
+System z machines (z900 or higher) provide hardware support for creating system
+dumps on SCSI disks. The dump process is initiated by booting a dump tool, which
+has to create a dump of the current (probably crashed) Linux image. In order to
+not overwrite memory of the crashed Linux with data of the dump tool, the
+hardware saves some memory plus the register sets of the boot cpu before the
+dump tool is loaded. There exists an SCLP hardware interface to obtain the saved
+memory afterwards. Currently 32 MB are saved.
+
+This zfcpdump implementation consists of a Linux dump kernel together with
+a userspace dump tool, which are loaded together into the saved memory region
+below 32 MB. zfcpdump is installed on a SCSI disk using zipl (as contained in
+the s390-tools package) to make the device bootable. The operator of a Linux
+system can then trigger a SCSI dump by booting the SCSI disk, where zfcpdump
+resides on.
+
+The kernel part of zfcpdump is implemented as a debugfs file under "zcore/mem",
+which exports memory and registers of the crashed Linux in an s390
+standalone dump format. It can be used in the same way as e.g. /dev/mem. The
+dump format defines a 4K header followed by plain uncompressed memory. The
+register sets are stored in the prefix pages of the respective cpus. To build a
+dump enabled kernel with the zcore driver, the kernel config option
+CONFIG_ZFCPDUMP has to be set. When reading from "zcore/mem", the part of
+memory, which has been saved by hardware is read by the driver via the SCLP
+hardware interface. The second part is just copied from the non overwritten real
+memory.
+
+The userspace application of zfcpdump can reside e.g. in an intitramfs or an
+initrd. It reads from zcore/mem and writes the system dump to a file on a
+SCSI disk.
+
+To build a zfcpdump kernel use the following settings in your kernel
+configuration:
+ * CONFIG_ZFCPDUMP=y
+ * Enable ZFCP driver
+ * Enable SCSI driver
+ * Enable ext2 and ext3 filesystems
+ * Disable as many features as possible to keep the kernel small.
+   E.g. network support is not needed at all.
+
+To use the zfcpdump userspace application in an initramfs you have to do the
+following:
+
+ * Copy the zfcpdump executable somewhere into your Linux tree.
+   E.g. to "arch/s390/boot/zfcpdump. If you do not want to include
+   shared libraries, compile the tool with the "-static" gcc option.
+ * If you want to include e2fsck, add it to your source tree, too. The zfcpdump
+   application attempts to start /sbin/e2fsck from the ramdisk.
+ * Use an initramfs config file like the following:
+
+   dir /dev 755 0 0
+   nod /dev/console 644 0 0 c 5 1
+   nod /dev/null 644 0 0 c 1 3
+   nod /dev/sda1 644 0 0 b 8 1
+   nod /dev/sda2 644 0 0 b 8 2
+   nod /dev/sda3 644 0 0 b 8 3
+   nod /dev/sda4 644 0 0 b 8 4
+   nod /dev/sda5 644 0 0 b 8 5
+   nod /dev/sda6 644 0 0 b 8 6
+   nod /dev/sda7 644 0 0 b 8 7
+   nod /dev/sda8 644 0 0 b 8 8
+   nod /dev/sda9 644 0 0 b 8 9
+   nod /dev/sda10 644 0 0 b 8 10
+   nod /dev/sda11 644 0 0 b 8 11
+   nod /dev/sda12 644 0 0 b 8 12
+   nod /dev/sda13 644 0 0 b 8 13
+   nod /dev/sda14 644 0 0 b 8 14
+   nod /dev/sda15 644 0 0 b 8 15
+   file /init arch/s390/boot/zfcpdump 755 0 0
+   file /sbin/e2fsck arch/s390/boot/e2fsck 755 0 0
+   dir /proc 755 0 0
+   dir /sys 755 0 0
+   dir /mnt 755 0 0
+   dir /sbin 755 0 0
+
+ * Issue "make image" to build the zfcpdump image with initramfs.
+
+In a Linux distribution the zfcpdump enabled kernel image must be copied to
+/usr/share/zfcpdump/zfcpdump.image, where the s390 zipl tool is looking for the
+dump kernel when preparing a SCSI dump disk.
+
+If you use a ramdisk copy it to "/usr/share/zfcpdump/zfcpdump.rd".
+
+For more information on how to use zfcpdump refer to the s390 'Using the Dump
+Tools book', which is available from
+http://www.ibm.com/developerworks/linux/linux390.
diff --git a/Documentation/sony-laptop.txt b/Documentation/sony-laptop.txt
index dfd26df..7a5c1a8 100644
--- a/Documentation/sony-laptop.txt
+++ b/Documentation/sony-laptop.txt
@@ -3,12 +3,18 @@
 	Copyright (C) 2004- 2005 Stelian Pop <stelian@popies.net>
 	Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
 
-This mini-driver drives the SNC device present in the ACPI BIOS of
-the Sony Vaio laptops.
+This mini-driver drives the SNC and SPIC device present in the ACPI BIOS of the
+Sony Vaio laptops. This driver mixes both devices functions under the same
+(hopefully consistent) interface. This also means that the sonypi driver is
+obsoleted by sony-laptop now.
 
-It gives access to some extra laptop functionalities. In its current
-form, this driver let the user set or query the screen brightness
-through the backlight subsystem and remove/apply power to some devices.
+Fn keys (hotkeys):
+------------------
+Some models report hotkeys through the SNC or SPIC devices, such events are
+reported both through the ACPI subsystem as acpi events and through the INPUT
+subsystem. See the logs of acpid or /proc/acpi/event and
+/proc/bus/input/devices to find out what those events are and which input
+devices are created by the driver.
 
 Backlight control:
 ------------------
@@ -39,6 +45,8 @@
 	audiopower		power on/off the internal sound card
 	lanpower		power on/off the internal ethernet card
 				(only in debug mode)
+	bluetoothpower		power on/off the internal bluetooth device
+	fanspeed		get/set the fan speed
 
 Note that some files may be missing if they are not supported
 by your particular laptop model.
@@ -76,9 +84,9 @@
 current ones found on several Vaio models), an entry under
 /sys/devices/platform/sony-laptop, just like the 'cdpower' one.
 You can create other entries corresponding to your own laptop methods by
-further editing the source (see the 'sony_acpi_values' table, and add a new
+further editing the source (see the 'sony_nc_values' table, and add a new
 entry to this table with your get/set method names using the
-HANDLE_NAMES macro).
+SNC_HANDLE_NAMES macro).
 
 Your mission, should you accept it, is to try finding out what
 those entries are for, by reading/writing random values from/to those
@@ -87,6 +95,9 @@
 Should you find anything interesting, please report it back to me,
 I will not disavow all knowledge of your actions :)
 
+See also http://www.linux.it/~malattia/wiki/index.php/Sony_drivers for other
+useful info.
+
 Bugs/Limitations:
 -----------------
 
diff --git a/Documentation/ibm-acpi.txt b/Documentation/thinkpad-acpi.txt
similarity index 60%
rename from Documentation/ibm-acpi.txt
rename to Documentation/thinkpad-acpi.txt
index 0132d36..2d48033 100644
--- a/Documentation/ibm-acpi.txt
+++ b/Documentation/thinkpad-acpi.txt
@@ -1,16 +1,22 @@
-		    IBM ThinkPad ACPI Extras Driver
+		     ThinkPad ACPI Extras Driver
 
-                            Version 0.12
-                           17 August 2005
+                            Version 0.14
+                          April 21st, 2007
 
                Borislav Deianov <borislav@users.sf.net>
+	     Henrique de Moraes Holschuh <hmh@hmh.eng.br>
 		      http://ibm-acpi.sf.net/
 
 
-This is a Linux ACPI driver for the IBM ThinkPad laptops. It supports
-various features of these laptops which are accessible through the
-ACPI framework but not otherwise supported by the generic Linux ACPI
-drivers.
+This is a Linux driver for the IBM and Lenovo ThinkPad laptops. It
+supports various features of these laptops which are accessible
+through the ACPI and ACPI EC framework, but not otherwise fully
+supported by the generic Linux ACPI drivers.
+
+This driver used to be named ibm-acpi until kernel 2.6.21 and release
+0.13-20070314.  It used to be in the drivers/acpi tree, but it was
+moved to the drivers/misc tree and renamed to thinkpad-acpi for kernel
+2.6.22, and release 0.14.
 
 
 Status
@@ -21,7 +27,7 @@
 
 	- Fn key combinations
 	- Bluetooth enable and disable
-	- video output switching, expansion control	
+	- video output switching, expansion control
 	- ThinkLight on and off
 	- limited docking and undocking
 	- UltraBay eject
@@ -32,7 +38,7 @@
 	- Experimental: embedded controller register dump
 	- LCD brightness control
 	- Volume control
-	- Experimental: fan speed, fan enable/disable
+	- Fan control and monitoring: fan speed, fan enable/disable
 	- Experimental: WAN enable and disable
 
 A compatibility table by model and feature is maintained on the web
@@ -42,6 +48,8 @@
 
 	- ThinkPad model name
 	- a copy of your DSDT, from /proc/acpi/dsdt
+	- a copy of the output of dmidecode, with serial numbers
+	  and UUIDs masked off
 	- which driver features work and which don't
 	- the observed behavior of non-working features
 
@@ -52,25 +60,85 @@
 ------------
 
 If you are compiling this driver as included in the Linux kernel
-sources, simply enable the CONFIG_ACPI_IBM option (Power Management /
-ACPI / IBM ThinkPad Laptop Extras).
+sources, simply enable the CONFIG_THINKPAD_ACPI option, and optionally
+enable the CONFIG_THINKPAD_ACPI_BAY option if you want the
+thinkpad-specific bay functionality.
 
 Features
 --------
 
-The driver creates the /proc/acpi/ibm directory. There is a file under
-that directory for each feature described below. Note that while the
-driver is still in the alpha stage, the exact proc file format and
-commands supported by the various features is guaranteed to change
-frequently.
+The driver exports two different interfaces to userspace, which can be
+used to access the features it provides.  One is a legacy procfs-based
+interface, which will be removed at some time in the distant future.
+The other is a new sysfs-based interface which is not complete yet.
 
-Driver version -- /proc/acpi/ibm/driver
----------------------------------------
+The procfs interface creates the /proc/acpi/ibm directory.  There is a
+file under that directory for each feature it supports.  The procfs
+interface is mostly frozen, and will change very little if at all: it
+will not be extended to add any new functionality in the driver, instead
+all new functionality will be implemented on the sysfs interface.
+
+The sysfs interface tries to blend in the generic Linux sysfs subsystems
+and classes as much as possible.  Since some of these subsystems are not
+yet ready or stabilized, it is expected that this interface will change,
+and any and all userspace programs must deal with it.
+
+
+Notes about the sysfs interface:
+
+Unlike what was done with the procfs interface, correctness when talking
+to the sysfs interfaces will be enforced, as will correctness in the
+thinkpad-acpi's implementation of sysfs interfaces.
+
+Also, any bugs in the thinkpad-acpi sysfs driver code or in the
+thinkpad-acpi's implementation of the sysfs interfaces will be fixed for
+maximum correctness, even if that means changing an interface in
+non-compatible ways.  As these interfaces mature both in the kernel and
+in thinkpad-acpi, such changes should become quite rare.
+
+Applications interfacing to the thinkpad-acpi sysfs interfaces must
+follow all sysfs guidelines and correctly process all errors (the sysfs
+interface makes extensive use of errors).  File descriptors and open /
+close operations to the sysfs inodes must also be properly implemented.
+
+The version of thinkpad-acpi's sysfs interface is exported by the driver
+as a driver attribute (see below).
+
+Sysfs driver attributes are on the driver's sysfs attribute space,
+for 2.6.20 this is /sys/bus/platform/drivers/thinkpad-acpi/.
+
+Sysfs device attributes are on the driver's sysfs attribute space,
+for 2.6.20 this is /sys/devices/platform/thinkpad-acpi/.
+
+Driver version
+--------------
+
+procfs: /proc/acpi/ibm/driver
+sysfs driver attribute: version
 
 The driver name and version. No commands can be written to this file.
 
-Hot keys -- /proc/acpi/ibm/hotkey
----------------------------------
+Sysfs interface version
+-----------------------
+
+sysfs driver attribute: interface_version
+
+Version of the thinkpad-acpi sysfs interface, as an unsigned long
+(output in hex format: 0xAAAABBCC), where:
+	AAAA - major revision
+	BB - minor revision
+	CC - bugfix revision
+
+The sysfs interface version changelog for the driver can be found at the
+end of this document.  Changes to the sysfs interface done by the kernel
+subsystems are not documented here, nor are they tracked by this
+attribute.
+
+Hot keys
+--------
+
+procfs: /proc/acpi/ibm/hotkey
+sysfs device attribute: hotkey/*
 
 Without this driver, only the Fn-F4 key (sleep button) generates an
 ACPI event. With the driver loaded, the hotkey feature enabled and the
@@ -84,15 +152,6 @@
 addition, the lid microswitch and some docking station buttons may
 also generate such events.
 
-The following commands can be written to this file:
-
-	echo enable > /proc/acpi/ibm/hotkey -- enable the hot keys feature
-	echo disable > /proc/acpi/ibm/hotkey -- disable the hot keys feature
-	echo 0xffff > /proc/acpi/ibm/hotkey -- enable all possible hot keys
-	echo 0x0000 > /proc/acpi/ibm/hotkey -- disable all possible hot keys
-	... any other 4-hex-digit mask ...
-	echo reset > /proc/acpi/ibm/hotkey -- restore the original mask
-
 The bit mask allows some control over which hot keys generate ACPI
 events. Not all bits in the mask can be modified. Not all bits that
 can be modified do anything. Not all hot keys can be individually
@@ -124,15 +183,77 @@
 be used through the "ThinkPad Buttons" utility, see
 http://www.nongnu.org/tpb/
 
-Bluetooth -- /proc/acpi/ibm/bluetooth
--------------------------------------
+procfs notes:
 
-This feature shows the presence and current state of a Bluetooth
-device. If Bluetooth is installed, the following commands can be used:
+The following commands can be written to the /proc/acpi/ibm/hotkey file:
+
+	echo enable > /proc/acpi/ibm/hotkey -- enable the hot keys feature
+	echo disable > /proc/acpi/ibm/hotkey -- disable the hot keys feature
+	echo 0xffff > /proc/acpi/ibm/hotkey -- enable all possible hot keys
+	echo 0x0000 > /proc/acpi/ibm/hotkey -- disable all possible hot keys
+	... any other 4-hex-digit mask ...
+	echo reset > /proc/acpi/ibm/hotkey -- restore the original mask
+
+sysfs notes:
+
+	The hot keys attributes are in a hotkey/ subdirectory off the
+	thinkpad device.
+
+	bios_enabled:
+		Returns the status of the hot keys feature when
+		thinkpad-acpi was loaded.  Upon module unload, the hot
+		key feature status will be restored to this value.
+
+		0: hot keys were disabled
+		1: hot keys were enabled
+
+	bios_mask:
+		Returns the hot keys mask when thinkpad-acpi was loaded.
+		Upon module unload, the hot keys mask will be restored
+		to this value.
+
+	enable:
+		Enables/disables the hot keys feature, and reports
+		current status of the hot keys feature.
+
+		0: disables the hot keys feature / feature disabled
+		1: enables the hot keys feature / feature enabled
+
+	mask:
+		bit mask to enable ACPI event generation for each hot
+		key (see above).  Returns the current status of the hot
+		keys mask, and allows one to modify it.
+
+
+Bluetooth
+---------
+
+procfs: /proc/acpi/ibm/bluetooth
+sysfs device attribute: bluetooth/enable
+
+This feature shows the presence and current state of a ThinkPad
+Bluetooth device in the internal ThinkPad CDC slot.
+
+Procfs notes:
+
+If Bluetooth is installed, the following commands can be used:
 
 	echo enable > /proc/acpi/ibm/bluetooth
 	echo disable > /proc/acpi/ibm/bluetooth
 
+Sysfs notes:
+
+	If the Bluetooth CDC card is installed, it can be enabled /
+	disabled through the "bluetooth/enable" thinkpad-acpi device
+	attribute, and its current status can also be queried.
+
+	enable:
+		0: disables Bluetooth / Bluetooth is disabled
+		1: enables Bluetooth / Bluetooth is enabled.
+
+	Note: this interface will be probably be superseeded by the
+	generic rfkill class.
+
 Video output control -- /proc/acpi/ibm/video
 --------------------------------------------
 
@@ -209,7 +330,7 @@
 booted while not in the dock, the following message is shown in the
 logs:
 
-	Mar 17 01:42:34 aero kernel: ibm_acpi: dock device not present
+	Mar 17 01:42:34 aero kernel: thinkpad_acpi: dock device not present
 
 In this case, no dock-related events are generated but the dock and
 undock commands described below still work. They can be executed
@@ -269,7 +390,7 @@
 in the Linux ACPI framework. If the laptop was booted without the
 UltraBay, the following message is shown in the logs:
 
-	Mar 17 01:42:34 aero kernel: ibm_acpi: bay device not present
+	Mar 17 01:42:34 aero kernel: thinkpad_acpi: bay device not present
 
 In this case, no bay-related events are generated but the eject
 command described below still works. It can be executed manually or
@@ -313,23 +434,19 @@
 Note: the UltraBay eject support on the 600e/x, A22p and A3x is
 EXPERIMENTAL and may not work as expected. USE WITH CAUTION!
 
-CMOS control -- /proc/acpi/ibm/cmos
------------------------------------
+CMOS control
+------------
+
+procfs: /proc/acpi/ibm/cmos
+sysfs device attribute: cmos_command
 
 This feature is used internally by the ACPI firmware to control the
 ThinkLight on most newer ThinkPad models. It may also control LCD
 brightness, sounds volume and more, but only on some models.
 
-The commands are non-negative integer numbers:
-
-	echo 0 >/proc/acpi/ibm/cmos
-	echo 1 >/proc/acpi/ibm/cmos
-	echo 2 >/proc/acpi/ibm/cmos
-	...
-
-The range of valid numbers is 0 to 21, but not all have an effect and
-the behavior varies from model to model. Here is the behavior on the
-X40 (tpb is the ThinkPad Buttons utility):
+The range of valid cmos command numbers is 0 to 21, but not all have an
+effect and the behavior varies from model to model.  Here is the behavior
+on the X40 (tpb is the ThinkPad Buttons utility):
 
 	0 - no effect but tpb reports "Volume down"
 	1 - no effect but tpb reports "Volume up"
@@ -342,6 +459,9 @@
 	13 - ThinkLight off
 	14 - no effect but tpb reports ThinkLight status change
 
+The cmos command interface is prone to firmware split-brain problems, as
+in newer ThinkPads it is just a compatibility layer.
+
 LED control -- /proc/acpi/ibm/led
 ---------------------------------
 
@@ -393,17 +513,17 @@
 	16 - one medium-pitched beep repeating constantly, stop with 17
 	17 - stop 16
 
-Temperature sensors -- /proc/acpi/ibm/thermal
----------------------------------------------
+Temperature sensors
+-------------------
+
+procfs: /proc/acpi/ibm/thermal
+sysfs device attributes: (hwmon) temp*_input
 
 Most ThinkPads include six or more separate temperature sensors but
 only expose the CPU temperature through the standard ACPI methods.
 This feature shows readings from up to eight different sensors on older
 ThinkPads, and it has experimental support for up to sixteen different
-sensors on newer ThinkPads.  Readings from sensors that are not available
-return -128.
-
-No commands can be written to this file.
+sensors on newer ThinkPads.
 
 EXPERIMENTAL: The 16-sensors feature is marked EXPERIMENTAL because the
 implementation directly accesses hardware registers and may not work as
@@ -460,6 +580,20 @@
 8:  Bay Battery: secondary sensor
 
 
+Procfs notes:
+	Readings from sensors that are not available return -128.
+	No commands can be written to this file.
+
+Sysfs notes:
+	Sensors that are not available return the ENXIO error.  This
+	status may change at runtime, as there are hotplug thermal
+	sensors, like those inside the batteries and docks.
+
+	thinkpad-acpi thermal sensors are reported through the hwmon
+	subsystem, and follow all of the hwmon guidelines at
+	Documentation/hwmon.
+
+
 EXPERIMENTAL: Embedded controller register dump -- /proc/acpi/ibm/ecdump
 ------------------------------------------------------------------------
 
@@ -472,7 +606,7 @@
 registers. Values which have changed since the last time the registers
 were dumped are marked with a star:
 
-[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump 
+[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump
 EC       +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f
 EC 0x00:  a7  47  87  01  fe  96  00  08  01  00  cb  00  00  00  40  00
 EC 0x10:  00  00  ff  ff  f4  3c  87  09  01  ff  42  01  ff  ff  0d  00
@@ -503,7 +637,7 @@
 the fan speed fluctuates a bit. The third will (hopefully) mark the
 fan register with a star:
 
-[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump 
+[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump
 EC       +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f
 EC 0x00:  a7  47  87  01  fe  96  00  08  01  00  cb  00  00  00  40  00
 EC 0x10:  00  00  ff  ff  f4  3c  87  09  01  ff  42  01  ff  ff  0d  00
@@ -533,19 +667,59 @@
 with this, do send me your results (including some complete dumps with
 a description of the conditions when they were taken.)
 
-LCD brightness control -- /proc/acpi/ibm/brightness
----------------------------------------------------
+LCD brightness control
+----------------------
+
+procfs: /proc/acpi/ibm/brightness
+sysfs backlight device "thinkpad_screen"
 
 This feature allows software control of the LCD brightness on ThinkPad
-models which don't have a hardware brightness slider. The available
-commands are:
+models which don't have a hardware brightness slider.
+
+It has some limitations: the LCD backlight cannot be actually turned on or off
+by this interface, and in many ThinkPad models, the "dim while on battery"
+functionality will be enabled by the BIOS when this interface is used, and
+cannot be controlled.
+
+The backlight control has eight levels, ranging from 0 to 7.  Some of the
+levels may not be distinct.
+
+Procfs notes:
+
+	The available commands are:
 
 	echo up   >/proc/acpi/ibm/brightness
 	echo down >/proc/acpi/ibm/brightness
 	echo 'level <level>' >/proc/acpi/ibm/brightness
 
-The <level> number range is 0 to 7, although not all of them may be
-distinct. The current brightness level is shown in the file.
+Sysfs notes:
+
+The interface is implemented through the backlight sysfs class, which is poorly
+documented at this time.
+
+Locate the thinkpad_screen device under /sys/class/backlight, and inside it
+there will be the following attributes:
+
+	max_brightness:
+		Reads the maximum brightness the hardware can be set to.
+		The minimum is always zero.
+
+	actual_brightness:
+		Reads what brightness the screen is set to at this instant.
+
+	brightness:
+		Writes request the driver to change brightness to the given
+		value.  Reads will tell you what brightness the driver is trying
+		to set the display to when "power" is set to zero and the display
+		has not been dimmed by a kernel power management event.
+
+	power:
+		power management mode, where 0 is "display on", and 1 to 3 will
+		dim the display backlight to brightness level 0 because
+		thinkpad-acpi cannot really turn the backlight off.  Kernel
+		power management events can temporarily increase the current
+		power management level, i.e. they can dim the display.
+
 
 Volume control -- /proc/acpi/ibm/volume
 ---------------------------------------
@@ -563,41 +737,42 @@
 up or down command (the level command will not unmute the volume).
 The current volume level and mute state is shown in the file.
 
-EXPERIMENTAL: fan speed, fan enable/disable -- /proc/acpi/ibm/fan
------------------------------------------------------------------
+Fan control and monitoring: fan speed, fan enable/disable
+---------------------------------------------------------
 
-This feature is marked EXPERIMENTAL because the implementation
-directly accesses hardware registers and may not work as expected. USE
-WITH CAUTION! To use this feature, you need to supply the
-experimental=1 parameter when loading the module.
+procfs: /proc/acpi/ibm/fan
+sysfs device attributes: (hwmon) fan_input, pwm1, pwm1_enable
+
+NOTE NOTE NOTE: fan control operations are disabled by default for
+safety reasons.  To enable them, the module parameter "fan_control=1"
+must be given to thinkpad-acpi.
 
 This feature attempts to show the current fan speed, control mode and
 other fan data that might be available.  The speed is read directly
 from the hardware registers of the embedded controller.  This is known
-to work on later R, T and X series ThinkPads but may show a bogus
+to work on later R, T, X and Z series ThinkPads but may show a bogus
 value on other models.
 
-Most ThinkPad fans work in "levels".  Level 0 stops the fan.  The higher
-the level, the higher the fan speed, although adjacent levels often map
-to the same fan speed.  7 is the highest level, where the fan reaches
-the maximum recommended speed.  Level "auto" means the EC changes the
-fan level according to some internal algorithm, usually based on
-readings from the thermal sensors.  Level "disengaged" means the EC
-disables the speed-locked closed-loop fan control, and drives the fan as
-fast as it can go, which might exceed hardware limits, so use this level
-with caution.
+Fan levels:
 
-The fan usually ramps up or down slowly from one speed to another,
-and it is normal for the EC to take several seconds to react to fan
-commands.
+Most ThinkPad fans work in "levels" at the firmware interface.  Level 0
+stops the fan.  The higher the level, the higher the fan speed, although
+adjacent levels often map to the same fan speed.  7 is the highest
+level, where the fan reaches the maximum recommended speed.
 
-The fan may be enabled or disabled with the following commands:
+Level "auto" means the EC changes the fan level according to some
+internal algorithm, usually based on readings from the thermal sensors.
 
-	echo enable  >/proc/acpi/ibm/fan
-	echo disable >/proc/acpi/ibm/fan
+There is also a "full-speed" level, also known as "disengaged" level.
+In this level, the EC disables the speed-locked closed-loop fan control,
+and drives the fan as fast as it can go, which might exceed hardware
+limits, so use this level with caution.
 
-Placing a fan on level 0 is the same as disabling it.  Enabling a fan
-will try to place it in a safe level if it is too slow or disabled.
+The fan usually ramps up or down slowly from one speed to another, and
+it is normal for the EC to take several seconds to react to fan
+commands.  The full-speed level may take up to two minutes to ramp up to
+maximum speed, and in some ThinkPads, the tachometer readings go stale
+while the EC is transitioning to the full-speed level.
 
 WARNING WARNING WARNING: do not leave the fan disabled unless you are
 monitoring all of the temperature sensor readings and you are ready to
@@ -615,46 +790,146 @@
 HDD temperature drops to 41 degrees.  These thresholds cannot
 currently be controlled.
 
-The fan level can be controlled with the command:
-
-	echo 'level <level>' > /proc/acpi/ibm/thermal
-
-Where <level> is an integer from 0 to 7, or one of the words "auto"
-or "disengaged" (without the quotes).  Not all ThinkPads support the
-"auto" and "disengaged" levels.
-
-On the X31 and X40 (and ONLY on those models), the fan speed can be
-controlled to a certain degree. Once the fan is running, it can be
-forced to run faster or slower with the following command:
-
-	echo 'speed <speed>' > /proc/acpi/ibm/thermal
-
-The sustainable range of fan speeds on the X40 appears to be from
-about 3700 to about 7350. Values outside this range either do not have
-any effect or the fan speed eventually settles somewhere in that
-range. The fan cannot be stopped or started with this command.
-
 The ThinkPad's ACPI DSDT code will reprogram the fan on its own when
 certain conditions are met.  It will override any fan programming done
-through ibm-acpi.
+through thinkpad-acpi.
 
-EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan
----------------------------------------
+The thinkpad-acpi kernel driver can be programmed to revert the fan
+level to a safe setting if userspace does not issue one of the procfs
+fan commands: "enable", "disable", "level" or "watchdog", or if there
+are no writes to pwm1_enable (or to pwm1 *if and only if* pwm1_enable is
+set to 1, manual mode) within a configurable amount of time of up to
+120 seconds.  This functionality is called fan safety watchdog.
+
+Note that the watchdog timer stops after it enables the fan.  It will be
+rearmed again automatically (using the same interval) when one of the
+above mentioned fan commands is received.  The fan watchdog is,
+therefore, not suitable to protect against fan mode changes made through
+means other than the "enable", "disable", and "level" procfs fan
+commands, or the hwmon fan control sysfs interface.
+
+Procfs notes:
+
+The fan may be enabled or disabled with the following commands:
+
+	echo enable  >/proc/acpi/ibm/fan
+	echo disable >/proc/acpi/ibm/fan
+
+Placing a fan on level 0 is the same as disabling it.  Enabling a fan
+will try to place it in a safe level if it is too slow or disabled.
+
+The fan level can be controlled with the command:
+
+	echo 'level <level>' > /proc/acpi/ibm/fan
+
+Where <level> is an integer from 0 to 7, or one of the words "auto" or
+"full-speed" (without the quotes).  Not all ThinkPads support the "auto"
+and "full-speed" levels.  The driver accepts "disengaged" as an alias for
+"full-speed", and reports it as "disengaged" for backwards
+compatibility.
+
+On the X31 and X40 (and ONLY on those models), the fan speed can be
+controlled to a certain degree.  Once the fan is running, it can be
+forced to run faster or slower with the following command:
+
+	echo 'speed <speed>' > /proc/acpi/ibm/fan
+
+The sustainable range of fan speeds on the X40 appears to be from about
+3700 to about 7350. Values outside this range either do not have any
+effect or the fan speed eventually settles somewhere in that range.  The
+fan cannot be stopped or started with this command.  This functionality
+is incomplete, and not available through the sysfs interface.
+
+To program the safety watchdog, use the "watchdog" command.
+
+	echo 'watchdog <interval in seconds>' > /proc/acpi/ibm/fan
+
+If you want to disable the watchdog, use 0 as the interval.
+
+Sysfs notes:
+
+The sysfs interface follows the hwmon subsystem guidelines for the most
+part, and the exception is the fan safety watchdog.
+
+Writes to any of the sysfs attributes may return the EINVAL error if
+that operation is not supported in a given ThinkPad or if the parameter
+is out-of-bounds, and EPERM if it is forbidden.  They may also return
+EINTR (interrupted system call), and EIO (I/O error while trying to talk
+to the firmware).
+
+Features not yet implemented by the driver return ENOSYS.
+
+hwmon device attribute pwm1_enable:
+	0: PWM offline (fan is set to full-speed mode)
+	1: Manual PWM control (use pwm1 to set fan level)
+	2: Hardware PWM control (EC "auto" mode)
+	3: reserved (Software PWM control, not implemented yet)
+
+	Modes 0 and 2 are not supported by all ThinkPads, and the
+	driver is not always able to detect this.  If it does know a
+	mode is unsupported, it will return -EINVAL.
+
+hwmon device attribute pwm1:
+	Fan level, scaled from the firmware values of 0-7 to the hwmon
+	scale of 0-255.  0 means fan stopped, 255 means highest normal
+	speed (level 7).
+
+	This attribute only commands the fan if pmw1_enable is set to 1
+	(manual PWM control).
+
+hwmon device attribute fan1_input:
+	Fan tachometer reading, in RPM.  May go stale on certain
+	ThinkPads while the EC transitions the PWM to offline mode,
+	which can take up to two minutes.  May return rubbish on older
+	ThinkPads.
+
+driver attribute fan_watchdog:
+	Fan safety watchdog timer interval, in seconds.  Minimum is
+	1 second, maximum is 120 seconds.  0 disables the watchdog.
+
+To stop the fan: set pwm1 to zero, and pwm1_enable to 1.
+
+To start the fan in a safe mode: set pwm1_enable to 2.  If that fails
+with EINVAL, try to set pwm1_enable to 1 and pwm1 to at least 128 (255
+would be the safest choice, though).
+
+
+EXPERIMENTAL: WAN
+-----------------
+
+procfs: /proc/acpi/ibm/wan
+sysfs device attribute: wwan/enable
 
 This feature is marked EXPERIMENTAL because the implementation
 directly accesses hardware registers and may not work as expected. USE
 WITH CAUTION! To use this feature, you need to supply the
 experimental=1 parameter when loading the module.
 
-This feature shows the presence and current state of a WAN (Sierra
-Wireless EV-DO) device. If WAN is installed, the following commands can
-be used:
+This feature shows the presence and current state of a W-WAN (Sierra
+Wireless EV-DO) device.
+
+It was tested on a Lenovo Thinkpad X60. It should probably work on other
+Thinkpad models which come with this module installed.
+
+Procfs notes:
+
+If the W-WAN card is installed, the following commands can be used:
 
 	echo enable > /proc/acpi/ibm/wan
 	echo disable > /proc/acpi/ibm/wan
 
-It was tested on a Lenovo Thinkpad X60. It should probably work on other
-Thinkpad models which come with this module installed.
+Sysfs notes:
+
+	If the W-WAN card is installed, it can be enabled /
+	disabled through the "wwan/enable" thinkpad-acpi device
+	attribute, and its current status can also be queried.
+
+	enable:
+		0: disables WWAN card / WWAN card is disabled
+		1: enables WWAN card / WWAN card is enabled.
+
+	Note: this interface will be probably be superseeded by the
+	generic rfkill class.
 
 Multiple Commands, Module Parameters
 ------------------------------------
@@ -665,64 +940,42 @@
 	echo enable,0xffff > /proc/acpi/ibm/hotkey
 	echo lcd_disable,crt_enable > /proc/acpi/ibm/video
 
-Commands can also be specified when loading the ibm_acpi module, for
-example:
+Commands can also be specified when loading the thinkpad-acpi module,
+for example:
 
-	modprobe ibm_acpi hotkey=enable,0xffff video=auto_disable
+	modprobe thinkpad_acpi hotkey=enable,0xffff video=auto_disable
 
-The ibm-acpi kernel driver can be programmed to revert the fan level
-to a safe setting if userspace does not issue one of the fan commands:
-"enable", "disable", "level" or "watchdog" within a configurable
-ammount of time.  To do this, use the "watchdog" command.
+Enabling debugging output
+-------------------------
 
-	echo 'watchdog <interval>' > /proc/acpi/ibm/fan
+The module takes a debug paramater which can be used to selectively
+enable various classes of debugging output, for example:
 
-Interval is the ammount of time in seconds to wait for one of the
-above mentioned fan commands before reseting the fan level to a safe
-one.  If set to zero, the watchdog is disabled (default).  When the
-watchdog timer runs out, it does the exact equivalent of the "enable"
-fan command.
+	 modprobe ibm_acpi debug=0xffff
 
-Note that the watchdog timer stops after it enables the fan.  It will
-be rearmed again automatically (using the same interval) when one of
-the above mentioned fan commands is received.  The fan watchdog is,
-therefore, not suitable to protect against fan mode changes made
-through means other than the "enable", "disable", and "level" fan
-commands.
+will enable all debugging output classes.  It takes a bitmask, so
+to enable more than one output class, just add their values.
+
+	Debug bitmask		Description
+	0x0001			Initialization and probing
+	0x0002			Removal
+
+There is also a kernel build option to enable more debugging
+information, which may be necessary to debug driver problems.
+
+The level of debugging information output by the driver can be changed
+at runtime through sysfs, using the driver attribute debug_level.  The
+attribute takes the same bitmask as the debug module parameter above.
+
+Force loading of module
+-----------------------
+
+If thinkpad-acpi refuses to detect your ThinkPad, you can try to specify
+the module parameter force_load=1.  Regardless of whether this works or
+not, please contact ibm-acpi-devel@lists.sourceforge.net with a report.
 
 
-Example Configuration
----------------------
+Sysfs interface changelog:
 
-The ACPI support in the kernel is intended to be used in conjunction
-with a user-space daemon, acpid. The configuration files for this
-daemon control what actions are taken in response to various ACPI
-events. An example set of configuration files are included in the
-config/ directory of the tarball package available on the web
-site. Note that these are provided for illustration purposes only and
-may need to be adapted to your particular setup.
-
-The following utility scripts are used by the example action
-scripts (included with ibm-acpi for completeness):
-
-	/usr/local/sbin/idectl -- from the hdparm source distribution,
-		see http://www.ibiblio.org/pub/Linux/system/hardware
-	/usr/local/sbin/laptop_mode -- from the Linux kernel source
-		distribution, see Documentation/laptop-mode.txt
-	/sbin/service -- comes with Redhat/Fedora distributions
-	/usr/sbin/hibernate -- from the Software Suspend 2 distribution,
-		see http://softwaresuspend.berlios.de/
-
-Toan T Nguyen <ntt@physics.ucla.edu> notes that Suse uses the
-powersave program to suspend ('powersave --suspend-to-ram') or
-hibernate ('powersave --suspend-to-disk'). This means that the
-hibernate script is not needed on that distribution.
-
-Henrik Brix Andersen <brix@gentoo.org> has written a Gentoo ACPI event
-handler script for the X31. You can get the latest version from
-http://dev.gentoo.org/~brix/files/x31.sh
-
-David Schweikert <dws@ee.eth.ch> has written an alternative blank.sh
-script which works on Debian systems. This scripts has now been
-extended to also work on Fedora systems and included as the default
-blank.sh in the distribution.
+0x000100:	Initial sysfs support, as a single platform driver and
+		device.
diff --git a/Documentation/usb/usbmon.txt b/Documentation/usb/usbmon.txt
index 0f6808a..53ae866a 100644
--- a/Documentation/usb/usbmon.txt
+++ b/Documentation/usb/usbmon.txt
@@ -16,7 +16,7 @@
 
 Unlike the packet socket, usbmon has an interface which provides traces
 in a text format. This is used for two purposes. First, it serves as a
-common trace exchange format for tools while most sophisticated formats
+common trace exchange format for tools while more sophisticated formats
 are finalized. Second, humans can read it in case tools are not available.
 
 To collect a raw text trace, execute following steps.
@@ -34,7 +34,7 @@
 Verify that bus sockets are present.
 
 # ls /sys/kernel/debug/usbmon
-1s  1t  2s  2t  3s  3t  4s  4t
+1s  1t  1u  2s  2t  2u  3s  3t  3u  4s  4t  4u
 #
 
 2. Find which bus connects to the desired device
@@ -54,7 +54,7 @@
 
 3. Start 'cat'
 
-# cat /sys/kernel/debug/usbmon/3t > /tmp/1.mon.out
+# cat /sys/kernel/debug/usbmon/3u > /tmp/1.mon.out
 
 This process will be reading until killed. Naturally, the output can be
 redirected to a desirable location. This is preferred, because it is going
@@ -75,46 +75,80 @@
 
 * Raw text data format
 
-The '1t' type data consists of a stream of events, such as URB submission,
+Two formats are supported currently: the original, or '1t' format, and
+the '1u' format. The '1t' format is deprecated in kernel 2.6.21. The '1u'
+format adds a few fields, such as ISO frame descriptors, interval, etc.
+It produces slightly longer lines, but otherwise is a perfect superset
+of '1t' format.
+
+If it is desired to recognize one from the other in a program, look at the
+"address" word (see below), where '1u' format adds a bus number. If 2 colons
+are present, it's the '1t' format, otherwise '1u'.
+
+Any text format data consists of a stream of events, such as URB submission,
 URB callback, submission error. Every event is a text line, which consists
 of whitespace separated words. The number or position of words may depend
 on the event type, but there is a set of words, common for all types.
 
 Here is the list of words, from left to right:
+
 - URB Tag. This is used to identify URBs is normally a kernel mode address
  of the URB structure in hexadecimal.
+
 - Timestamp in microseconds, a decimal number. The timestamp's resolution
   depends on available clock, and so it can be much worse than a microsecond
   (if the implementation uses jiffies, for example).
+
 - Event Type. This type refers to the format of the event, not URB type.
   Available types are: S - submission, C - callback, E - submission error.
-- "Pipe". The pipe concept is deprecated. This is a composite word, used to
-  be derived from information in pipes. It consists of three fields, separated
-  by colons: URB type and direction, Device address, Endpoint number.
+
+- "Address" word (formerly a "pipe"). It consists of four fields, separated by
+  colons: URB type and direction, Bus number, Device address, Endpoint number.
   Type and direction are encoded with two bytes in the following manner:
     Ci Co   Control input and output
     Zi Zo   Isochronous input and output
     Ii Io   Interrupt input and output
     Bi Bo   Bulk input and output
-  Device address and Endpoint number are 3-digit and 2-digit (respectively)
-  decimal numbers, with leading zeroes.
-- URB Status. In most cases, this field contains a number, sometimes negative,
-  which represents a "status" field of the URB. This field makes no sense for
-  submissions, but is present anyway to help scripts with parsing. When an
-  error occurs, the field contains the error code. In case of a submission of
-  a Control packet, this field contains a Setup Tag instead of an error code.
-  It is easy to tell whether the Setup Tag is present because it is never a
-  number. Thus if scripts find a number in this field, they proceed to read
-  Data Length. If they find something else, like a letter, they read the setup
-  packet before reading the Data Length.
+  Bus number, Device address, and Endpoint are decimal numbers, but they may
+  have leading zeros, for the sake of human readers.
+
+- URB Status word. This is either a letter, or several numbers separated
+  by colons: URB status, interval, start frame, and error count. Unlike the
+  "address" word, all fields save the status are optional. Interval is printed
+  only for interrupt and isochronous URBs. Start frame is printed only for
+  isochronous URBs. Error count is printed only for isochronous callback
+  events.
+
+  The status field is a decimal number, sometimes negative, which represents
+  a "status" field of the URB. This field makes no sense for submissions, but
+  is present anyway to help scripts with parsing. When an error occurs, the
+  field contains the error code.
+
+  In case of a submission of a Control packet, this field contains a Setup Tag
+  instead of an group of numbers. It is easy to tell whether the Setup Tag is
+  present because it is never a number. Thus if scripts find a set of numbers
+  in this word, they proceed to read Data Length (except for isochronous URBs).
+  If they find something else, like a letter, they read the setup packet before
+  reading the Data Length or isochronous descriptors.
+
 - Setup packet, if present, consists of 5 words: one of each for bmRequestType,
   bRequest, wValue, wIndex, wLength, as specified by the USB Specification 2.0.
   These words are safe to decode if Setup Tag was 's'. Otherwise, the setup
   packet was present, but not captured, and the fields contain filler.
+
+- Number of isochronous frame descriptors and descriptors themselves.
+  If an Isochronous transfer event has a set of descriptors, a total number
+  of them in an URB is printed first, then a word per descriptor, up to a
+  total of 5. The word consists of 3 colon-separated decimal numbers for
+  status, offset, and length respectively. For submissions, initial length
+  is reported. For callbacks, actual length is reported.
+
 - Data Length. For submissions, this is the requested length. For callbacks,
   this is the actual length.
+
 - Data tag. The usbmon may not always capture data, even if length is nonzero.
   The data words are present only if this tag is '='.
+
 - Data words follow, in big endian hexadecimal format. Notice that they are
   not machine words, but really just a byte stream split into words to make
   it easier to read. Thus, the last word may contain from one to four bytes.
@@ -153,20 +187,18 @@
 	}
 }
 
-This format may be changed in the future.
-
 Examples:
 
 An input control transfer to get a port status.
 
-d5ea89a0 3575914555 S Ci:001:00 s a3 00 0000 0003 0004 4 <
-d5ea89a0 3575914560 C Ci:001:00 0 4 = 01050000
+d5ea89a0 3575914555 S Ci:1:001:0 s a3 00 0000 0003 0004 4 <
+d5ea89a0 3575914560 C Ci:1:001:0 0 4 = 01050000
 
 An output bulk transfer to send a SCSI command 0x5E in a 31-byte Bulk wrapper
 to a storage device at address 5:
 
-dd65f0e8 4128379752 S Bo:005:02 -115 31 = 55534243 5e000000 00000000 00000600 00000000 00000000 00000000 000000
-dd65f0e8 4128379808 C Bo:005:02 0 31 >
+dd65f0e8 4128379752 S Bo:1:005:2 -115 31 = 55534243 5e000000 00000000 00000600 00000000 00000000 00000000 000000
+dd65f0e8 4128379808 C Bo:1:005:2 0 31 >
 
 * Raw binary format and API
 
diff --git a/Documentation/video4linux/CARDLIST.bttv b/Documentation/video4linux/CARDLIST.bttv
index fc2fe9b..b606391 100644
--- a/Documentation/video4linux/CARDLIST.bttv
+++ b/Documentation/video4linux/CARDLIST.bttv
@@ -143,3 +143,5 @@
 142 -> Sabrent TV-FM (bttv version)
 143 -> Hauppauge ImpactVCB (bt878)                         [0070:13eb]
 144 -> MagicTV
+145 -> SSAI Security Video Interface                       [4149:5353]
+146 -> SSAI Ultrasound Video Interface                     [414a:5353]
diff --git a/Documentation/video4linux/CARDLIST.cx88 b/Documentation/video4linux/CARDLIST.cx88
index 62e32b4..60f838b 100644
--- a/Documentation/video4linux/CARDLIST.cx88
+++ b/Documentation/video4linux/CARDLIST.cx88
@@ -37,7 +37,7 @@
  36 -> AVerTV 303 (M126)                                   [1461:000a]
  37 -> Hauppauge Nova-S-Plus DVB-S                         [0070:9201,0070:9202]
  38 -> Hauppauge Nova-SE2 DVB-S                            [0070:9200]
- 39 -> KWorld DVB-S 100                                    [17de:08b2]
+ 39 -> KWorld DVB-S 100                                    [17de:08b2,1421:0341]
  40 -> Hauppauge WinTV-HVR1100 DVB-T/Hybrid                [0070:9400,0070:9402]
  41 -> Hauppauge WinTV-HVR1100 DVB-T/Hybrid (Low Profile)  [0070:9800,0070:9802]
  42 -> digitalnow DNTV Live! DVB-T Pro                     [1822:0025,1822:0019]
diff --git a/Documentation/video4linux/CARDLIST.ivtv b/Documentation/video4linux/CARDLIST.ivtv
new file mode 100644
index 0000000..ddd76a0
--- /dev/null
+++ b/Documentation/video4linux/CARDLIST.ivtv
@@ -0,0 +1,18 @@
+ 1 -> Hauppauge WinTV PVR-250
+ 2 -> Hauppauge WinTV PVR-350
+ 3 -> Hauppauge WinTV PVR-150 or PVR-500
+ 4 -> AVerMedia M179 				[1461:a3ce,1461:a3cf]
+ 5 -> Yuan MPG600/Kuroutoshikou iTVC16-STVLP 	[12ab:fff3,12ab:ffff]
+ 6 -> Yuan MPG160/Kuroutoshikou iTVC15-STVLP 	[12ab:0000,10fc:40a0]
+ 7 -> Yuan PG600/DiamondMM PVR-550 		[ff92:0070,ffab:0600]
+ 8 -> Adaptec AVC-2410 				[9005:0093]
+ 9 -> Adaptec AVC-2010 				[9005:0092]
+10 -> NAGASE TRANSGEAR 5000TV 			[1461:bfff]
+11 -> AOpen VA2000MAX-STN6 			[0000:ff5f]
+12 -> YUAN MPG600GR/Kuroutoshikou CX23416GYC-STVLP [12ab:0600,fbab:0600,1154:0523]
+13 -> I/O Data GV-MVP/RX 			[10fc:d01e,10fc:d038,10fc:d039]
+14 -> I/O Data GV-MVP/RX2E 			[10fc:d025]
+15 -> GOTVIEW PCI DVD (partial support only) 	[12ab:0600]
+16 -> GOTVIEW PCI DVD2 Deluxe 			[ffac:0600]
+17 -> Yuan MPC622 				[ff01:d998]
+18 -> Digital Cowboy DCT-MTVP1 			[1461:bfff]
diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134
index a12246a..d7bb2e2 100644
--- a/Documentation/video4linux/CARDLIST.saa7134
+++ b/Documentation/video4linux/CARDLIST.saa7134
@@ -53,7 +53,7 @@
  52 -> AverMedia AverTV/305                     [1461:2108]
  53 -> ASUS TV-FM 7135                          [1043:4845]
  54 -> LifeView FlyTV Platinum FM / Gold        [5168:0214,1489:0214,5168:0304]
- 55 -> LifeView FlyDVB-T DUO                    [5168:0306]
+ 55 -> LifeView FlyDVB-T DUO / MSI TV@nywhere Duo [5168:0306,4E42:0306]
  56 -> Avermedia AVerTV 307                     [1461:a70a]
  57 -> Avermedia AVerTV GO 007 FM               [1461:f31f]
  58 -> ADS Tech Instant TV (saa7135)            [1421:0350,1421:0351,1421:0370,1421:1370]
@@ -76,7 +76,7 @@
  75 -> AVerMedia AVerTVHD MCE A180              [1461:1044]
  76 -> SKNet MonsterTV Mobile                   [1131:4ee9]
  77 -> Pinnacle PCTV 40i/50i/110i (saa7133)     [11bd:002e]
- 78 -> ASUSTeK P7131 Dual                       [1043:4862,1043:4876]
+ 78 -> ASUSTeK P7131 Dual                       [1043:4862,1043:4857]
  79 -> Sedna/MuchTV PC TV Cardbus TV/Radio (ITO25 Rev:2B)
  80 -> ASUS Digimatrix TV                       [1043:0210]
  81 -> Philips Tiger reference design           [1131:2018]
@@ -107,3 +107,7 @@
 106 -> Encore ENLTV                             [1131:2342,1131:2341,3016:2344]
 107 -> Encore ENLTV-FM                          [1131:230f]
 108 -> Terratec Cinergy HT PCI                  [153b:1175]
+109 -> Philips Tiger - S Reference design
+110 -> Avermedia M102                           [1461:f31e]
+111 -> ASUS P7131 4871                          [1043:4871]
+112 -> ASUSTeK P7131 Hybrid                     [1043:4876]
diff --git a/Documentation/video4linux/CARDLIST.usbvision b/Documentation/video4linux/CARDLIST.usbvision
new file mode 100644
index 0000000..3d6850e
--- /dev/null
+++ b/Documentation/video4linux/CARDLIST.usbvision
@@ -0,0 +1,64 @@
+  0 -> Xanboo                                                   [0a6f:0400]
+  1 -> Belkin USB VideoBus II Adapter                           [050d:0106]
+  2 -> Belkin Components USB VideoBus                           [050d:0207]
+  3 -> Belkin USB VideoBus II                                   [050d:0208]
+  4 -> echoFX InterView Lite                                    [0571:0002]
+  5 -> USBGear USBG-V1 resp. HAMA USB                           [0573:0003]
+  6 -> D-Link V100                                              [0573:0400]
+  7 -> X10 USB Camera                                           [0573:2000]
+  8 -> Hauppauge WinTV USB Live (PAL B/G)                       [0573:2d00]
+  9 -> Hauppauge WinTV USB Live Pro (NTSC M/N)                  [0573:2d01]
+ 10 -> Zoran Co. PMD (Nogatech) AV-grabber Manhattan            [0573:2101]
+ 11 -> Nogatech USB-TV (NTSC) FM                                [0573:4100]
+ 12 -> PNY USB-TV (NTSC) FM                                     [0573:4110]
+ 13 -> PixelView PlayTv-USB PRO (PAL) FM                        [0573:4450]
+ 14 -> ZTV ZT-721 2.4GHz USB A/V Receiver                       [0573:4550]
+ 15 -> Hauppauge WinTV USB (NTSC M/N)                           [0573:4d00]
+ 16 -> Hauppauge WinTV USB (PAL B/G)                            [0573:4d01]
+ 17 -> Hauppauge WinTV USB (PAL I)                              [0573:4d02]
+ 18 -> Hauppauge WinTV USB (PAL/SECAM L)                        [0573:4d03]
+ 19 -> Hauppauge WinTV USB (PAL D/K)                            [0573:4d04]
+ 20 -> Hauppauge WinTV USB (NTSC FM)                            [0573:4d10]
+ 21 -> Hauppauge WinTV USB (PAL B/G FM)                         [0573:4d11]
+ 22 -> Hauppauge WinTV USB (PAL I FM)                           [0573:4d12]
+ 23 -> Hauppauge WinTV USB (PAL D/K FM)                         [0573:4d14]
+ 24 -> Hauppauge WinTV USB Pro (NTSC M/N)                       [0573:4d2a]
+ 25 -> Hauppauge WinTV USB Pro (NTSC M/N) V2                    [0573:4d2b]
+ 26 -> Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L)          [0573:4d2c]
+ 27 -> Hauppauge WinTV USB Pro (NTSC M/N) V3                    [0573:4d20]
+ 28 -> Hauppauge WinTV USB Pro (PAL B/G)                        [0573:4d21]
+ 29 -> Hauppauge WinTV USB Pro (PAL I)                          [0573:4d22]
+ 30 -> Hauppauge WinTV USB Pro (PAL/SECAM L)                    [0573:4d23]
+ 31 -> Hauppauge WinTV USB Pro (PAL D/K)                        [0573:4d24]
+ 32 -> Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L)             [0573:4d25]
+ 33 -> Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) V2          [0573:4d26]
+ 34 -> Hauppauge WinTV USB Pro (PAL B/G) V2                     [0573:4d27]
+ 35 -> Hauppauge WinTV USB Pro (PAL B/G,D/K)                    [0573:4d28]
+ 36 -> Hauppauge WinTV USB Pro (PAL I,D/K)                      [0573:4d29]
+ 37 -> Hauppauge WinTV USB Pro (NTSC M/N FM)                    [0573:4d30]
+ 38 -> Hauppauge WinTV USB Pro (PAL B/G FM)                     [0573:4d31]
+ 39 -> Hauppauge WinTV USB Pro (PAL I FM)                       [0573:4d32]
+ 40 -> Hauppauge WinTV USB Pro (PAL D/K FM)                     [0573:4d34]
+ 41 -> Hauppauge WinTV USB Pro (Temic PAL/SECAM B/G/I/D/K/L FM) [0573:4d35]
+ 42 -> Hauppauge WinTV USB Pro (Temic PAL B/G FM)               [0573:4d36]
+ 43 -> Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L FM)       [0573:4d37]
+ 44 -> Hauppauge WinTV USB Pro (NTSC M/N FM) V2                 [0573:4d38]
+ 45 -> Camtel Technology USB TV Genie Pro FM Model TVB330       [0768:0006]
+ 46 -> Digital Video Creator I                                  [07d0:0001]
+ 47 -> Global Village GV-007 (NTSC)                             [07d0:0002]
+ 48 -> Dazzle Fusion Model DVC-50 Rev 1 (NTSC)                  [07d0:0003]
+ 49 -> Dazzle Fusion Model DVC-80 Rev 1 (PAL)                   [07d0:0004]
+ 50 -> Dazzle Fusion Model DVC-90 Rev 1 (SECAM)                 [07d0:0005]
+ 51 -> Eskape Labs MyTV2Go                                      [07f8:9104]
+ 52 -> Pinnacle Studio PCTV USB (PAL)                           [2304:010d]
+ 53 -> Pinnacle Studio PCTV USB (SECAM)                         [2304:0109]
+ 54 -> Pinnacle Studio PCTV USB (PAL) FM                        [2304:0110]
+ 55 -> Miro PCTV USB                                            [2304:0111]
+ 56 -> Pinnacle Studio PCTV USB (NTSC) FM                       [2304:0112]
+ 57 -> Pinnacle Studio PCTV USB (PAL) FM V2                     [2304:0210]
+ 58 -> Pinnacle Studio PCTV USB (NTSC) FM V2                    [2304:0212]
+ 59 -> Pinnacle Studio PCTV USB (PAL) FM V3                     [2304:0214]
+ 60 -> Pinnacle Studio Linx Video input cable (NTSC)            [2304:0300]
+ 61 -> Pinnacle Studio Linx Video input cable (PAL)             [2304:0301]
+ 62 -> Pinnacle PCTV Bungee USB (PAL) FM                        [2304:0419]
+ 63 -> Hauppauge WinTv-USB                                      [2400:4200]
diff --git a/Documentation/video4linux/README.ivtv b/Documentation/video4linux/README.ivtv
new file mode 100644
index 0000000..73df22c
--- /dev/null
+++ b/Documentation/video4linux/README.ivtv
@@ -0,0 +1,187 @@
+
+ivtv release notes
+==================
+
+This is a v4l2 device driver for the Conexant cx23415/6 MPEG encoder/decoder.
+The cx23415 can do both encoding and decoding, the cx23416 can only do MPEG
+encoding. Currently the only card featuring full decoding support is the
+Hauppauge PVR-350.
+
+NOTE: this driver requires the latest encoder firmware (version 2.06.039, size
+376836 bytes). Get the firmware from here:
+
+http://dl.ivtvdriver.org/ivtv/firmware/firmware.tar.gz
+
+NOTE: 'normal' TV applications do not work with this driver, you need
+an application that can handle MPEG input such as mplayer, xine, MythTV,
+etc.
+
+The primary goal of the IVTV project is to provide a "clean room" Linux
+Open Source driver implementation for video capture cards based on the
+iCompression iTVC15 or Conexant CX23415/CX23416 MPEG Codec.
+
+Features:
+ * Hardware mpeg2 capture of broadcast video (and sound) via the tuner or
+   S-Video/Composite and audio line-in.
+ * Hardware mpeg2 capture of FM radio where hardware support exists
+ * Supports NTSC, PAL, SECAM with stereo sound
+ * Supports SAP and bilingual transmissions.
+ * Supports raw VBI (closed captions and teletext).
+ * Supports sliced VBI (closed captions and teletext) and is able to insert
+   this into the captured MPEG stream.
+ * Supports raw YUV and PCM input.
+
+Additional features for the PVR-350 (CX23415 based):
+ * Provides hardware mpeg2 playback
+ * Provides comprehensive OSD (On Screen Display: ie. graphics overlaying the
+   video signal)
+ * Provides a framebuffer (allowing X applications to appear on the video
+   device) (this framebuffer is not yet part of the kernel. In the meantime it
+   is available from www.ivtvdriver.org).
+ * Supports raw YUV output.
+
+IMPORTANT: In case of problems first read this page:
+	   http://www.ivtvdriver.org/index.php/Troubleshooting
+
+See also:
+
+Homepage + Wiki
+http://www.ivtvdriver.org
+
+IRC
+irc://irc.freenode.net/ivtv-dev
+
+----------------------------------------------------------
+
+Devices
+=======
+
+A maximum of 12 ivtv boards are allowed at the moment.
+
+Cards that don't have a video output capability (i.e. non PVR350 cards)
+lack the vbi8, vbi16, video16 and video48 devices. They also do not
+support the framebuffer device /dev/fbx for OSD.
+
+The radio0 device may or may not be present, depending on whether the
+card has a radio tuner or not.
+
+Here is a list of the base v4l devices:
+crw-rw----    1 root     video     81,   0 Jun 19 22:22 /dev/video0
+crw-rw----    1 root     video     81,  16 Jun 19 22:22 /dev/video16
+crw-rw----    1 root     video     81,  24 Jun 19 22:22 /dev/video24
+crw-rw----    1 root     video     81,  32 Jun 19 22:22 /dev/video32
+crw-rw----    1 root     video     81,  48 Jun 19 22:22 /dev/video48
+crw-rw----    1 root     video     81,  64 Jun 19 22:22 /dev/radio0
+crw-rw----    1 root     video     81, 224 Jun 19 22:22 /dev/vbi0
+crw-rw----    1 root     video     81, 228 Jun 19 22:22 /dev/vbi8
+crw-rw----    1 root     video     81, 232 Jun 19 22:22 /dev/vbi16
+
+Base devices
+============
+
+For every extra card you have the numbers increased by one. For example,
+/dev/video0 is listed as the 'base' encoding capture device so we have:
+
+ /dev/video0  is the encoding capture device for the first card (card 0)
+ /dev/video1  is the encoding capture device for the second card (card 1)
+ /dev/video2  is the encoding capture device for the third card (card 2)
+
+Note that if the first card doesn't have a feature (eg no decoder, so no
+video16, the second card will still use video17. The simple rule is 'add
+the card number to the base device number'. If you have other capture
+cards (e.g. WinTV PCI) that are detected first, then you have to tell
+the ivtv module about it so that it will start counting at 1 (or 2, or
+whatever). Otherwise the device numbers can get confusing. The ivtv
+'ivtv_first_minor' module option can be used for that.
+
+
+/dev/video0
+The encoding capture device(s).
+Read-only.
+
+Reading from this device gets you the MPEG1/2 program stream.
+Example:
+
+cat /dev/video0 > my.mpg (you need to hit ctrl-c to exit)
+
+
+/dev/video16
+The decoder output device(s)
+Write-only. Only present if the MPEG decoder (i.e. CX23415) exists.
+
+An mpeg2 stream sent to this device will appear on the selected video
+display, audio will appear on the line-out/audio out.  It is only
+available for cards that support video out. Example:
+
+cat my.mpg >/dev/video16
+
+
+/dev/video24
+The raw audio capture device(s).
+Read-only
+
+The raw audio PCM stereo stream from the currently selected
+tuner or audio line-in.  Reading from this device results in a raw
+(signed 16 bit Little Endian, 48000 Hz, stereo pcm) capture.
+This device only captures audio. This should be replaced by an ALSA
+device in the future.
+Note that there is no corresponding raw audio output device, this is
+not supported in the decoder firmware.
+
+
+/dev/video32
+The raw video capture device(s)
+Read-only
+
+The raw YUV video output from the current video input. The YUV format
+is non-standard (V4L2_PIX_FMT_HM12).
+
+Note that the YUV and PCM streams are not synchronized, so they are of
+limited use.
+
+
+/dev/video48
+The raw video display device(s)
+Write-only. Only present if the MPEG decoder (i.e. CX23415) exists.
+
+Writes a YUV stream to the decoder of the card.
+
+
+/dev/radio0
+The radio tuner device(s)
+Cannot be read or written.
+
+Used to enable the radio tuner and tune to a frequency. You cannot
+read or write audio streams with this device.  Once you use this
+device to tune the radio, use /dev/video24 to read the raw pcm stream
+or /dev/video0 to get an mpeg2 stream with black video.
+
+
+/dev/vbi0
+The 'vertical blank interval' (Teletext, CC, WSS etc) capture device(s)
+Read-only
+
+Captures the raw (or sliced) video data sent during the Vertical Blank
+Interval. This data is used to encode teletext, closed captions, VPS,
+widescreen signalling, electronic program guide information, and other
+services.
+
+
+/dev/vbi8
+Processed vbi feedback device(s)
+Read-only. Only present if the MPEG decoder (i.e. CX23415) exists.
+
+The sliced VBI data embedded in an MPEG stream is reproduced on this
+device. So while playing back a recording on /dev/video16, you can
+read the embedded VBI data from /dev/vbi8.
+
+
+/dev/vbi16
+The vbi 'display' device(s)
+Write-only. Only present if the MPEG decoder (i.e. CX23415) exists.
+
+Can be used to send sliced VBI data to the video-out connector.
+
+---------------------------------
+
+Hans Verkuil <hverkuil@xs4all.nl>
diff --git a/Documentation/video4linux/cx2341x/fw-decoder-regs.txt b/Documentation/video4linux/cx2341x/fw-decoder-regs.txt
index db2366c..cf52c8f 100644
--- a/Documentation/video4linux/cx2341x/fw-decoder-regs.txt
+++ b/Documentation/video4linux/cx2341x/fw-decoder-regs.txt
@@ -624,11 +624,11 @@
 2A00
       bits 0:2
 	osd colour mode
+	  000 = 8 bit indexed
 	  001 = 16 bit (565)
 	  010 = 15 bit (555)
 	  011 = 12 bit (444)
 	  100 = 32 bit (8888)
-	  101 = 8 bit indexed
 
       bits 4:5
 	osd display bpp
@@ -676,9 +676,11 @@
      completely transparent. When using 565, 555 or 444 colour modes, the
      colour key is always 16 bits wide. The colour to key on is set in Reg 2A18.
 
-     Local alpha is a per-pixel 256 step transparency, with 0 being transparent
-     and 255 being solid. This is only available in 32 bit & 8 bit indexed
-     colour modes.
+     Local alpha works differently depending on the colour mode. For 32bpp & 8
+     bit indexed, local alpha is a per-pixel 256 step transparency, with 0 being
+     transparent and 255 being solid. For the 16bpp modes 555 & 444, the unused
+     bit(s) act as a simple transparency switch, with 0 being solid & 1 being
+     fully transparent. There is no local alpha support for 16bit 565.
 
      Global alpha is a 256 step transparency that applies to the entire osd,
      with 0 being transparent & 255 being solid.
@@ -811,5 +813,5 @@
 
 --------------------------------------------------------------------------------
 
-v0.3 - 2 February 2007 - Ian Armstrong (ian@iarmst.demon.co.uk)
+v0.4 - 12 March 2007 - Ian Armstrong (ian@iarmst.demon.co.uk)
 
diff --git a/Documentation/video4linux/cx2341x/fw-encoder-api.txt b/Documentation/video4linux/cx2341x/fw-encoder-api.txt
index 242104c..5dd3109 100644
--- a/Documentation/video4linux/cx2341x/fw-encoder-api.txt
+++ b/Documentation/video4linux/cx2341x/fw-encoder-api.txt
@@ -663,12 +663,13 @@
 
 -------------------------------------------------------------------------------
 
-Name 	CX2341X_ENC_UNKNOWN
+Name 	CX2341X_ENC_SET_VERT_CROP_LINE
 Enum 	219/0xDB
 Description
-	Unknown API, it's used by Hauppauge though.
+	Something to do with 'Vertical Crop Line'
 Param[0]
-	0 This is the value Hauppauge uses, Unknown what it means.
+	If saa7114 and raw VBI capture and 60 Hz, then set to 10001.
+	Else 0.
 
 -------------------------------------------------------------------------------
 
@@ -682,11 +683,9 @@
 	Command number:
 	 1=set initial SCR value when starting encoding (works).
 	 2=set quality mode (apparently some test setting).
-	 3=setup advanced VIM protection handling (supposedly only for the cx23416
-	   for raw YUV).
-	   Actually it looks like this should be 0 for saa7114/5 based card and 1
-	   for cx25840 based cards.
-	 4=generate artificial PTS timestamps
+	 3=setup advanced VIM protection handling.
+	   Always 1 for the cx23416 and 0 for cx23415.
+	 4=generate DVD compatible PTS timestamps
 	 5=USB flush mode
 	 6=something to do with the quantization matrix
 	 7=set navigation pack insertion for DVD: adds 0xbf (private stream 2)
@@ -698,7 +697,9 @@
 	 9=set history parameters of the video input module
 	10=set input field order of VIM
 	11=set quantization matrix
-	12=reset audio interface
+	12=reset audio interface after channel change or input switch (has no argument).
+	   Needed for the cx2584x, not needed for the mspx4xx, but it doesn't seem to
+	   do any harm calling it regardless.
 	13=set audio volume delay
 	14=set audio delay
 
diff --git a/Documentation/video4linux/cx2341x/fw-osd-api.txt b/Documentation/video4linux/cx2341x/fw-osd-api.txt
index 0a602f3..89c4601 100644
--- a/Documentation/video4linux/cx2341x/fw-osd-api.txt
+++ b/Documentation/video4linux/cx2341x/fw-osd-api.txt
@@ -21,7 +21,11 @@
 Description
 	Query OSD format
 Result[0]
-	0=8bit index, 4=AlphaRGB 8:8:8:8
+	0=8bit index
+	1=16bit RGB 5:6:5
+	2=16bit ARGB 1:5:5:5
+	3=16bit ARGB 1:4:4:4
+	4=32bit ARGB 8:8:8:8
 
 -------------------------------------------------------------------------------
 
@@ -30,7 +34,11 @@
 Description
 	Assign pixel format
 Param[0]
-	0=8bit index, 4=AlphaRGB 8:8:8:8
+	0=8bit index
+	1=16bit RGB 5:6:5
+	2=16bit ARGB 1:5:5:5
+	3=16bit ARGB 1:4:4:4
+	4=32bit ARGB 8:8:8:8
 
 -------------------------------------------------------------------------------
 
diff --git a/Documentation/video4linux/meye.txt b/Documentation/video4linux/meye.txt
index ecb3416..5e51c59 100644
--- a/Documentation/video4linux/meye.txt
+++ b/Documentation/video4linux/meye.txt
@@ -5,10 +5,9 @@
 	Copyright (C) 2000 Andrew Tridgell <tridge@samba.org>
 
 This driver enable the use of video4linux compatible applications with the
-Motion Eye camera. This driver requires the "Sony Vaio Programmable I/O
-Control Device" driver (which can be found in the "Character drivers"
-section of the kernel configuration utility) to be compiled and installed
-(using its "camera=1" parameter).
+Motion Eye camera. This driver requires the "Sony Laptop Extras" driver (which
+can be found in the "Misc devices" section of the kernel configuration utility)
+to be compiled and installed (using its "camera=1" parameter).
 
 It can do at maximum 30 fps @ 320x240 or 15 fps @ 640x480.
 
diff --git a/Documentation/video4linux/sn9c102.txt b/Documentation/video4linux/sn9c102.txt
index 2913da3..5fe0ad7 100644
--- a/Documentation/video4linux/sn9c102.txt
+++ b/Documentation/video4linux/sn9c102.txt
@@ -25,7 +25,7 @@
 
 1. Copyright
 ============
-Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it>
+Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it>
 
 
 2. Disclaimer
@@ -216,10 +216,10 @@
 		1 = critical errors
 		2 = significant informations
 		3 = more verbose messages
-		Level 3 is useful for testing only, when only one device
-		is used. It also shows some more informations about the
-		hardware being detected. This parameter can be changed at
-		runtime thanks to the /sys filesystem interface.
+		Level 3 is useful for testing only. It also shows some more
+		informations about the hardware being detected.
+		This parameter can be changed at runtime thanks to the /sys
+		filesystem interface.
 Default:        2
 -------------------------------------------------------------------------------
 
@@ -235,7 +235,7 @@
 channel's gain by writing the desired value to it. The value may range from 0
 to 15 for the SN9C101 or SN9C102 bridges, from 0 to 127 for the SN9C103,
 SN9C105 and SN9C120 bridges.
-Similarly, only for the SN9C103, SN9C105 and SN9120 controllers, blue and red
+Similarly, only for the SN9C103, SN9C105 and SN9C120 controllers, blue and red
 gain control files are available in the same directory, for which accepted
 values may range from 0 to 127.
 
@@ -402,38 +402,49 @@
 0x0c45     0x60bc
 0x0c45     0x60be
 0x0c45     0x60c0
+0x0c45     0x60c2
 0x0c45     0x60c8
 0x0c45     0x60cc
 0x0c45     0x60ea
 0x0c45     0x60ec
+0x0c45     0x60ef
 0x0c45     0x60fa
 0x0c45     0x60fb
 0x0c45     0x60fc
 0x0c45     0x60fe
+0x0c45     0x6102
+0x0c45     0x6108
+0x0c45     0x610f
 0x0c45     0x6130
+0x0c45     0x6138
 0x0c45     0x613a
 0x0c45     0x613b
 0x0c45     0x613c
 0x0c45     0x613e
 
 The list above does not imply that all those devices work with this driver: up
-until now only the ones that assemble the following image sensors are
-supported; kernel messages will always tell you whether this is the case (see
-"Module loading" paragraph):
+until now only the ones that assemble the following pairs of SN9C1xx bridges
+and image sensors are supported; kernel messages will always tell you whether
+this is the case (see "Module loading" paragraph):
 
-Model       Manufacturer
------       ------------
-HV7131D     Hynix Semiconductor, Inc.
-MI-0343     Micron Technology, Inc.
-OV7630      OmniVision Technologies, Inc.
-OV7660      OmniVision Technologies, Inc.
-PAS106B     PixArt Imaging, Inc.
-PAS202BCA   PixArt Imaging, Inc.
-PAS202BCB   PixArt Imaging, Inc.
-TAS5110C1B  Taiwan Advanced Sensor Corporation
-TAS5130D1B  Taiwan Advanced Sensor Corporation
+Image sensor / SN9C1xx bridge      | SN9C10[12]  SN9C103  SN9C105  SN9C120
+-------------------------------------------------------------------------------
+HV7131D    Hynix Semiconductor     | Yes         No       No       No
+HV7131R    Hynix Semiconductor     | No          Yes      Yes      Yes
+MI-0343    Micron Technology       | Yes         No       No       No
+MI-0360    Micron Technology       | No          Yes      No       No
+OV7630     OmniVision Technologies | Yes         Yes      No       No
+OV7660     OmniVision Technologies | No          No       Yes      Yes
+PAS106B    PixArt Imaging          | Yes         No       No       No
+PAS202B    PixArt Imaging          | Yes         Yes      No       No
+TAS5110C1B Taiwan Advanced Sensor  | Yes         No       No       No
+TAS5110D   Taiwan Advanced Sensor  | Yes         No       No       No
+TAS5130D1B Taiwan Advanced Sensor  | Yes         No       No       No
 
-Some of the available control settings of each image sensor are supported
+"Yes" means that the pair is supported by the driver, while "No" means that the
+pair does not exist or is not supported by the driver.
+
+Only some of the available control settings of each image sensor are supported
 through the V4L2 interface.
 
 Donations of new models for further testing and support would be much
@@ -482,8 +493,8 @@
 formats over the USB: either native "Sequential RGB Bayer" or compressed.
 The compression is used to achieve high frame rates. With regard to the
 SN9C101, SN9C102 and SN9C103, the compression is based on the Huffman encoding
-algorithm described below, while the SN9C105 and SN9C120 the compression is
-based on the JPEG standard.
+algorithm described below, while with regard to the SN9C105 and SN9C120 the
+compression is based on the JPEG standard.
 The current video format may be selected or queried from the user application
 by calling the VIDIOC_S_FMT or VIDIOC_G_FMT ioctl's, as described in the V4L2
 API specifications.
@@ -573,4 +584,5 @@
 - Mizuno Takafumi for the donation of a webcam;
 - an "anonymous" donator (who didn't want his name to be revealed) for the
   donation of a webcam.
-- an anonymous donator for the donation of four webcams.
+- an anonymous donator for the donation of four webcams and two boards with ten
+  image sensors.
diff --git a/Documentation/video4linux/zr364xx.txt b/Documentation/video4linux/zr364xx.txt
new file mode 100644
index 0000000..c76992d
--- /dev/null
+++ b/Documentation/video4linux/zr364xx.txt
@@ -0,0 +1,65 @@
+Zoran 364xx based USB webcam module version 0.72
+site: http://royale.zerezo.com/zr364xx/
+mail: royale@zerezo.com
+
+introduction:
+This brings support under Linux for the Aiptek PocketDV 3300 in webcam mode.
+If you just want to get on your PC the pictures and movies on the camera, you should use the usb-storage module instead.
+The driver works with several other cameras in webcam mode (see the list below).
+Maybe this code can work for other JPEG/USB cams based on the Coach chips from Zoran?
+Possible chipsets are : ZR36430 (ZR36430BGC) and maybe ZR36431, ZR36440, ZR36442...
+You can try the experience changing the vendor/product ID values (look at the source code).
+You can get these values by looking at /var/log/messages when you plug your camera, or by typing : cat /proc/bus/usb/devices.
+If you manage to use your cam with this code, you can send me a mail (royale@zerezo.com) with the name of your cam and a patch if needed.
+This is a beta release of the driver.
+Since version 0.70, this driver is only compatible with V4L2 API and 2.6.x kernels.
+If you need V4L1 or 2.4x kernels support, please use an older version, but the code is not maintained anymore.
+Good luck!
+
+install:
+In order to use this driver, you must compile it with your kernel.
+Location: Device Drivers -> Multimedia devices -> Video For Linux -> Video Capture Adapters -> V4L USB devices
+
+usage:
+modprobe zr364xx debug=X mode=Y
+ - debug      : set to 1 to enable verbose debug messages
+ - mode       : 0 = 320x240, 1 = 160x120, 2 = 640x480
+You can then use the camera with V4L2 compatible applications, for example Ekiga.
+To capture a single image, try this: dd if=/dev/video0 of=test.jpg bs=1 count=1
+
+links :
+http://mxhaard.free.fr/ (support for many others cams including some Aiptek PocketDV)
+http://www.harmwal.nl/pccam880/ (this project also supports cameras based on this chipset)
+
+supported devices:
+------  -------  -----------     -----
+Vendor  Product  Distributor     Model
+------  -------  -----------     -----
+0x08ca  0x0109   Aiptek          PocketDV 3300
+0x08ca  0x0109   Maxell          Maxcam PRO DV3
+0x041e  0x4024   Creative        PC-CAM 880
+0x0d64  0x0108   Aiptek          Fidelity 3200
+0x0d64  0x0108   Praktica        DCZ 1.3 S
+0x0d64  0x0108   Genius          Digital Camera (?)
+0x0d64  0x0108   DXG Technology  Fashion Cam
+0x0546  0x3187   Polaroid        iON 230
+0x0d64  0x3108   Praktica        Exakta DC 2200
+0x0d64  0x3108   Genius          G-Shot D211
+0x0595  0x4343   Concord         Eye-Q Duo 1300
+0x0595  0x4343   Concord         Eye-Q Duo 2000
+0x0595  0x4343   Fujifilm        EX-10
+0x0595  0x4343   Ricoh           RDC-6000
+0x0595  0x4343   Digitrex        DSC 1300
+0x0595  0x4343   Firstline       FDC 2000
+0x0bb0  0x500d   Concord         EyeQ Go Wireless
+0x0feb  0x2004   CRS Electronic  3.3 Digital Camera
+0x0feb  0x2004   Packard Bell    DSC-300
+0x055f  0xb500   Mustek          MDC 3000
+0x08ca  0x2062   Aiptek          PocketDV 5700
+0x052b  0x1a18   Chiphead        Megapix V12
+0x04c8  0x0729   Konica          Revio 2
+0x04f2  0xa208   Creative        PC-CAM 850
+0x0784  0x0040   Traveler        Slimline X5
+0x06d6  0x0034   Trust           Powerc@m 750
+0x0a17  0x0062   Pentax          Optio 50L
+
diff --git a/MAINTAINERS b/MAINTAINERS
index 277877a..af1c792 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -55,7 +55,7 @@
 
 8.	Happy hacking.
 
- 		-----------------------------------
+		-----------------------------------
 
 Maintainers List (try to look for most precise areas first)
 
@@ -384,7 +384,7 @@
 
 APPLETALK NETWORK LAYER
 P:	Arnaldo Carvalho de Melo
-M:	acme@conectiva.com.br
+M:	acme@ghostprotocols.net
 S:	Maintained
 
 ARC FRAMEBUFFER DRIVER
@@ -656,6 +656,7 @@
 ATMEL WIRELESS DRIVER
 P:	Simon Kelley
 M:	simon@thekelleys.org.uk
+L:	linux-wireless@vger.kernel.org
 W:	http://www.thekelleys.org.uk/atmel
 W:	http://atmelwlandriver.sourceforge.net/
 S:	Maintained
@@ -711,6 +712,7 @@
 M:	Larry.Finger@lwfinger.net
 P:	Stefano Brivio
 M:	st3@riseup.net
+L:	linux-wireless@vger.kernel.org
 W:	http://bcm43xx.berlios.de/
 S:	Maintained
 
@@ -871,6 +873,12 @@
 T:	git kernel.org:/pub/scm/linux/kernel/git/mchehab/v4l-dvb.git
 S:	Maintained
 
+CAFE CMOS INTEGRATED CAMERA CONTROLLER DRIVER
+P:   	Jonathan Corbet
+M:	corbet@lwn.net
+L:	video4linux-list@redhat.com
+S:	Maintained
+
 CALGARY x86-64 IOMMU
 P:	Muli Ben-Yehuda
 M:	muli@il.ibm.com
@@ -892,6 +900,12 @@
 L:	linux-kernel@vger.kernel.org
 S:	Maintained
 
+CFG80211 and NL80211
+P:	Johannes Berg
+M:	johannes@sipsolutions.net
+L:	linux-wireless@vger.kernel.org
+S:	Maintained
+
 COMMON INTERNET FILE SYSTEM (CIFS)
 P:	Steve French
 M:	sfrench@samba.org
@@ -899,7 +913,7 @@
 L:	samba-technical@lists.samba.org
 W:	http://us1.samba.org/samba/Linux_CIFS_client.html
 T:	git kernel.org:/pub/scm/linux/kernel/git/sfrench/cifs-2.6.git
-S:	Supported	
+S:	Supported
 
 CONFIGFS
 P:	Joel Becker
@@ -967,6 +981,11 @@
 W:	http://www.wittsend.com/computone.html
 S:	Maintained
 
+CONEXANT ACCESSRUNNER USB DRIVER
+P:	Simon Arlott
+M:	cxacru@fire.lp0.eu
+S:	Maintained
+
 COSA/SRP SYNC SERIAL DRIVER
 P:	Jan "Yenya" Kasprzak
 M:	kas@fi.muni.cz
@@ -1034,9 +1053,8 @@
 
 CYCLADES 2X SYNC CARD DRIVER
 P:	Arnaldo Carvalho de Melo
-M:	acme@conectiva.com.br
-W:	http://advogato.org/person/acme
-L:	cycsyn-devel@bazar.conectiva.com.br
+M:	acme@ghostprotocols.net
+W:	http://oops.ghostprotocols.net:81/blog
 S:	Maintained
 
 CYCLADES ASYNC MUX DRIVER
@@ -1077,7 +1095,7 @@
 
 DCCP PROTOCOL
 P:	Arnaldo Carvalho de Melo
-M:	acme@mandriva.com
+M:	acme@ghostprotocols.net
 L:	dccp@vger.kernel.org
 W:	http://linux-net.osdl.org/index.php/DCCP
 S:	Maintained
@@ -1376,6 +1394,13 @@
 L:	netdev@vger.kernel.org
 S:	Maintained
 
+FREESCALE HIGHSPEED USB DEVICE DRIVER
+P:	Li Yang
+M:	leoli@freescale.com
+L:	linux-usb-devel@lists.sourceforge.net
+L:	linuxppc-embedded@ozlabs.org
+S:	Maintained
+
 FILE LOCKING (flock() and fcntl()/lockf())
 P:	Matthew Wilcox
 M:	matthew@wil.cx
@@ -1542,23 +1567,24 @@
 M:	chirag.kantharia@hp.com
 L:	iss_storagedev@hp.com
 S:	Maintained
- 
+
 HEWLETT-PACKARD SMART2 RAID DRIVER
 P:	Chirag Kantharia
 M:	chirag.kantharia@hp.com
 L:	iss_storagedev@hp.com
 S:	Maintained
- 
+
 HEWLETT-PACKARD SMART CISS RAID DRIVER (cciss)
 P:	Mike Miller
 M:	mike.miller@hp.com
 L:	iss_storagedev@hp.com
 S:	Supported
- 
+
 HOST AP DRIVER
 P:	Jouni Malinen
-M:	jkmaline@cc.hut.fi
-L:	hostap@shmoo.com
+M:	j@w1.fi
+L:	hostap@shmoo.com (subscribers-only)
+L:	linux-wireless@vger.kernel.org
 W:	http://hostap.epitest.fi/
 S:	Maintained
 
@@ -1632,15 +1658,6 @@
 T:	git kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git
 S:	Maintained
 
-IBM ACPI EXTRAS DRIVER
-P:	Henrique de Moraes Holschuh
-M:	ibm-acpi@hmh.eng.br
-L:	ibm-acpi-devel@lists.sourceforge.net
-W:	http://ibm-acpi.sourceforge.net
-W:	http://thinkwiki.org/wiki/Ibm-acpi
-T:	git repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git
-S:	Maintained
-
 SN-IA64 (Itanium) SUB-PLATFORM
 P:	Jes Sorensen
 M:	jes@sgi.com
@@ -1665,7 +1682,7 @@
 P:	Dave Jeffery
 M:	ipslinux@adaptec.com
 W:	http://www.developer.ibm.com/welcome/netfinity/serveraid.html
-S:	Supported 
+S:	Supported
 
 IDE SUBSYSTEM
 P:	Bartlomiej Zolnierkiewicz
@@ -1794,6 +1811,7 @@
 M:	jeffrey.t.kirsher@intel.com
 P:	Auke Kok
 M:	auke-jan.h.kok@intel.com
+L:	e1000-devel@lists.sourceforge.net
 W:	http://sourceforge.net/projects/e1000/
 S:	Supported
 
@@ -1808,6 +1826,7 @@
 M:	jeffrey.t.kirsher@intel.com
 P:	Auke Kok
 M:	auke-jan.h.kok@intel.com
+L:	e1000-devel@lists.sourceforge.net
 W:	http://sourceforge.net/projects/e1000/
 S:	Supported
 
@@ -1822,6 +1841,7 @@
 M:	jesse.brandeburg@intel.com
 P:	Auke Kok
 M:	auke-jan.h.kok@intel.com
+L:	e1000-devel@lists.sourceforge.net
 W:	http://sourceforge.net/projects/e1000/
 S:	Supported
 
@@ -1830,6 +1850,7 @@
 M:	yi.zhu@intel.com
 P:	James Ketrenos
 M:	jketreno@linux.intel.com
+L:	linux-wireless@vger.kernel.org
 L:	ipw2100-devel@lists.sourceforge.net
 L:	http://lists.sourceforge.net/mailman/listinfo/ipw2100-devel
 W:	http://ipw2100.sourceforge.net
@@ -1840,6 +1861,7 @@
 M:	yi.zhu@intel.com
 P:	James Ketrenos
 M:	jketreno@linux.intel.com
+L:	linux-wireless@vger.kernel.org
 L:	ipw2100-devel@lists.sourceforge.net
 L:	http://lists.sourceforge.net/mailman/listinfo/ipw2100-devel
 W:	http://ipw2200.sourceforge.net
@@ -1871,7 +1893,7 @@
 
 IPX NETWORK LAYER
 P:	Arnaldo Carvalho de Melo
-M:	acme@conectiva.com.br
+M:	acme@ghostprotocols.net
 L:	netdev@vger.kernel.org
 S:	Maintained
 
@@ -1965,7 +1987,7 @@
 P:	Sam Ravnborg
 M:	sam@ravnborg.org
 T:	git kernel.org:/pub/scm/linux/kernel/git/sam/kbuild.git
-S:	Maintained 
+S:	Maintained
 
 KERNEL JANITORS
 P:	Several
@@ -2108,7 +2130,7 @@
 
 LLC (802.2)
 P:	Arnaldo Carvalho de Melo
-M:	acme@conectiva.com.br
+M:	acme@ghostprotocols.net
 S:	Maintained
 
 LINUX FOR 64BIT POWERPC
@@ -2145,7 +2167,7 @@
 LOGICAL DISK MANAGER SUPPORT (LDM, Windows 2000/XP Dynamic Disks)
 P:	Richard Russon (FlatCap)
 M:	ldm@flatcap.org
-L:	ldm-devel@lists.sourceforge.net	
+L:	ldm-devel@lists.sourceforge.net
 W:	http://ldm.sourceforge.net
 S:	Maintained
 
@@ -2236,6 +2258,14 @@
 T:	git git://git.infradead.org/mtd-2.6.git
 S:	Maintained
 
+UNSORTED BLOCK IMAGES (UBI)
+P:	Artem Bityutskiy
+M:	dedekind@infradead.org
+W:	http://www.linux-mtd.infradead.org/
+L:	linux-mtd@lists.infradead.org
+T:	git git://git.infradead.org/ubi-2.6.git
+S:	Maintained
+
 MICROTEK X6 SCANNER
 P:	Oliver Neukum
 M:	oliver@neukum.name
@@ -2473,6 +2503,19 @@
 L:	linux-fbdev-devel@lists.sourceforge.net (subscribers-only)
 S:	Maintained
 
+NETERION (S2IO) Xframe 10GbE DRIVER
+P:	Ramkrishna Vepa
+M:	ram.vepa@neterion.com
+P:	Rastapur Santosh
+M:	santosh.rastapur@neterion.com
+P:	Sivakumar Subramani
+M:	sivakumar.subramani@neterion.com
+P:	Sreenivasa Honnur
+M:	sreenivasa.honnur@neterion.com
+L:	netdev@vger.kernel.org
+W:	http://trac.neterion.com/cgi-bin/trac.cgi/wiki/TitleIndex?anonymous
+S:	Supported
+
 OPENCORES I2C BUS DRIVER
 P:	Peter Korsgaard
 M:	jacmet@sunsite.dk
@@ -2486,13 +2529,13 @@
 M:	kurt.hackel@oracle.com
 L:	ocfs2-devel@oss.oracle.com
 W:	http://oss.oracle.com/projects/ocfs2/
-S:	Supported	
+S:	Supported
 
 OLYMPIC NETWORK DRIVER
 P:	Peter De Shrijver
 M:	p2@ace.ulyssis.student.kuleuven.ac.be
 P:	Mike Phillips
-M:	mikep@linuxtr.net 
+M:	mikep@linuxtr.net
 L:	netdev@vger.kernel.org
 L:	linux-tr@linuxtr.net
 W:	http://www.linuxtr.net
@@ -2508,6 +2551,12 @@
 M:	laforge@gnumonks.org
 S:	Maintained
 
+OMNIVISION OV7670 SENSOR DRIVER
+P:   	Jonathan Corbet
+M:	corbet@lwn.net
+L:	video4linux-list@redhat.com
+S:	Maintained
+
 ONSTREAM SCSI TAPE DRIVER
 P:	Willem Riede
 M:	osst@riede.org
@@ -2532,6 +2581,7 @@
 M:	proski@gnu.org
 P:	David Gibson
 M:	hermes@gibson.dropbear.id.au
+L:	linux-wireless@vger.kernel.org
 L:	orinoco-users@lists.sourceforge.net
 L:	orinoco-devel@lists.sourceforge.net
 W:	http://www.nongnu.org/orinoco/
@@ -2711,7 +2761,7 @@
 PRISM54 WIRELESS DRIVER
 P:	Prism54 Development Team
 M:	developers@islsm.org
-L:	netdev@vger.kernel.org
+L:	linux-wireless@vger.kernel.org
 W:	http://prism54.org
 S:	Maintained
 
@@ -2782,7 +2832,7 @@
 RAYLINK/WEBGEAR 802.11 WIRELESS LAN DRIVER
 P:	Corey Thomas
 M:	corey@world.std.com
-L:	linux-kernel@vger.kernel.org
+L:	linux-wireless@vger.kernel.org
 S:	Maintained
 
 RANDOM NUMBER DRIVER
@@ -2961,8 +3011,10 @@
 M:	sds@tycho.nsa.gov
 P:	James Morris
 M:	jmorris@namei.org
+P:	Eric Paris
+M:	eparis@parisplace.org
 L:	linux-kernel@vger.kernel.org (kernel issues)
-L: 	selinux@tycho.nsa.gov (general discussion)
+L: 	selinux@tycho.nsa.gov (subscribers-only, general discussion)
 W:	http://www.nsa.gov/selinux
 S:	Supported
 
@@ -3024,7 +3076,7 @@
 P:	Thomas Winischhofer
 M:	thomas@winischhofer.net
 W:	http://www.winischhofer.net/linuxsisvga.shtml
-S:	Maintained	
+S:	Maintained
 
 SIS USB2VGA DRIVER
 P:	Thomas Winischhofer
@@ -3045,7 +3097,7 @@
 P:	Daniel Drake
 M:	dsd@gentoo.org
 W:	http://softmac.sipsolutions.net/
-L:	netdev@vger.kernel.org
+L:	linux-wireless@vger.kernel.org
 S:	Maintained
 
 SOFTWARE RAID (Multiple Disks) SUPPORT
@@ -3121,6 +3173,15 @@
 M:	chris@zankel.net
 S:	Maintained
 
+THINKPAD ACPI EXTRAS DRIVER
+P:	Henrique de Moraes Holschuh
+M:	ibm-acpi@hmh.eng.br
+L:	ibm-acpi-devel@lists.sourceforge.net
+W:	http://ibm-acpi.sourceforge.net
+W:	http://thinkwiki.org/wiki/Ibm-acpi
+T:	git repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git
+S:	Maintained
+
 UltraSPARC (sparc64):
 P:	David S. Miller
 M:	davem@davemloft.net
@@ -3573,7 +3634,7 @@
 W:	http://www.connecttech.com
 S:	Supported
 
-USB SN9C10x DRIVER
+USB SN9C1xx DRIVER
 P:	Luca Risolia
 M:	luca.risolia@studio.unibo.it
 L:	linux-usb-devel@lists.sourceforge.net
@@ -3628,6 +3689,14 @@
 W:	http://linux-lc100020.sourceforge.net
 S:	Maintained
 
+USB ZR364XX DRIVER
+P:	Antoine Jacquet
+M:	royale@zerezo.com
+L:	linux-usb-devel@lists.sourceforge.net
+L:	video4linux-list@redhat.com
+W:	http://royale.zerezo.com/zr364xx/
+S:	Maintained
+
 USER-MODE LINUX
 P:	Jeff Dike
 M:	jdike@karaya.com
@@ -3635,7 +3704,7 @@
 L:	user-mode-linux-user@lists.sourceforge.net
 W:	http://user-mode-linux.sourceforge.net
 S:	Maintained
-	
+
 FAT/VFAT/MSDOS FILESYSTEM:
 P:	OGAWA Hirofumi
 M:	hirofumi@mail.parknet.co.jp
@@ -3750,6 +3819,7 @@
 WAVELAN NETWORK DRIVER & WIRELESS EXTENSIONS
 P:	Jean Tourrilhes
 M:	jt@hpl.hp.com
+L:	linux-wireless@vger.kernel.org
 W:	http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/
 S:	Maintained
 
@@ -3766,8 +3836,9 @@
 
 WL3501 WIRELESS PCMCIA CARD DRIVER
 P:	Arnaldo Carvalho de Melo
-M:	acme@conectiva.com.br
-W:	http://advogato.org/person/acme
+M:	acme@ghostprotocols.net
+L:	linux-wireless@vger.kernel.org
+W:	http://oops.ghostprotocols.net:81/blog
 S:	Maintained
 
 X.25 NETWORK LAYER
@@ -3830,6 +3901,7 @@
 P:	Ulrich Kunitz
 M:	kune@deine-taler.de
 W:	http://zd1211.ath.cx/wiki/DriverRewrite
+L:	linux-wireless@vger.kernel.org
 L:	zd1211-devs@lists.sourceforge.net (subscribers-only)
 S:	Maintained
 
diff --git a/arch/alpha/lib/Makefile b/arch/alpha/lib/Makefile
index 21cf624..ea098f3 100644
--- a/arch/alpha/lib/Makefile
+++ b/arch/alpha/lib/Makefile
@@ -36,7 +36,6 @@
 	$(ev6-y)csum_ipv6_magic.o \
 	$(ev6-y)clear_page.o \
 	$(ev6-y)copy_page.o \
-	strcasecmp.o \
 	fpreg.o \
 	callback_srm.o srm_puts.o srm_printk.o
 
diff --git a/arch/alpha/lib/strcasecmp.c b/arch/alpha/lib/strcasecmp.c
deleted file mode 100644
index 4e57a21..0000000
--- a/arch/alpha/lib/strcasecmp.c
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- *  linux/arch/alpha/lib/strcasecmp.c
- */
-
-#include <linux/string.h>
-
-
-/* We handle nothing here except the C locale.  Since this is used in
-   only one place, on strings known to contain only 7 bit ASCII, this
-   is ok.  */
-
-int strcasecmp(const char *a, const char *b)
-{
-	int ca, cb;
-
-	do {
-		ca = *a++ & 0xff;
-		cb = *b++ & 0xff;
-		if (ca >= 'A' && ca <= 'Z')
-			ca += 'a' - 'A';
-		if (cb >= 'A' && cb <= 'Z')
-			cb += 'a' - 'A';
-	} while (ca == cb && ca != '\0');
-
-	return ca - cb;
-}
diff --git a/arch/arm/common/sharpsl_pm.c b/arch/arm/common/sharpsl_pm.c
index a9bc5b5..5972df2 100644
--- a/arch/arm/common/sharpsl_pm.c
+++ b/arch/arm/common/sharpsl_pm.c
@@ -766,10 +766,10 @@
 }
 
 static struct pm_ops sharpsl_pm_ops = {
-	.pm_disk_mode	= PM_DISK_FIRMWARE,
 	.prepare	= pxa_pm_prepare,
 	.enter		= corgi_pxa_pm_enter,
 	.finish		= pxa_pm_finish,
+	.valid		= pm_valid_only_mem,
 };
 
 static int __init sharpsl_pm_probe(struct platform_device *pdev)
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index b49bfda..ff8db29 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -201,7 +201,6 @@
 
 
 static struct pm_ops at91_pm_ops ={
-	.pm_disk_mode	= 0,
 	.valid		= at91_pm_valid_state,
 	.prepare	= at91_pm_prepare,
 	.enter		= at91_pm_enter,
diff --git a/arch/arm/mach-omap1/pm.c b/arch/arm/mach-omap1/pm.c
index 49efe90..0383ab3 100644
--- a/arch/arm/mach-omap1/pm.c
+++ b/arch/arm/mach-omap1/pm.c
@@ -698,10 +698,10 @@
 
 
 static struct pm_ops omap_pm_ops ={
-	.pm_disk_mode	= 0,
 	.prepare	= omap_pm_prepare,
 	.enter		= omap_pm_enter,
 	.finish		= omap_pm_finish,
+	.valid		= pm_valid_only_mem,
 };
 
 static int __init omap_pm_init(void)
diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c
index d7eee99..6f4a543 100644
--- a/arch/arm/mach-omap2/pm.c
+++ b/arch/arm/mach-omap2/pm.c
@@ -370,10 +370,10 @@
 }
 
 static struct pm_ops omap_pm_ops = {
-	.pm_disk_mode	= 0,
 	.prepare	= omap2_pm_prepare,
 	.enter		= omap2_pm_enter,
 	.finish		= omap2_pm_finish,
+	.valid		= pm_valid_only_mem,
 };
 
 int __init omap2_pm_init(void)
diff --git a/arch/arm/mach-pnx4008/pm.c b/arch/arm/mach-pnx4008/pm.c
index 3649cd3..2a137f3 100644
--- a/arch/arm/mach-pnx4008/pm.c
+++ b/arch/arm/mach-pnx4008/pm.c
@@ -107,50 +107,19 @@
 	case PM_SUSPEND_MEM:
 		pnx4008_suspend();
 		break;
-	case PM_SUSPEND_DISK:
-		return -ENOTSUPP;
-	default:
-		return -EINVAL;
 	}
 	return 0;
 }
 
-/*
- * Called after processes are frozen, but before we shut down devices.
- */
-static int pnx4008_pm_prepare(suspend_state_t state)
+static int pnx4008_pm_valid(suspend_state_t state)
 {
-	switch (state) {
-	case PM_SUSPEND_STANDBY:
-	case PM_SUSPEND_MEM:
-		break;
-
-	case PM_SUSPEND_DISK:
-		return -ENOTSUPP;
-		break;
-
-	default:
-		return -EINVAL;
-		break;
-	}
-	return 0;
+	return (state == PM_SUSPEND_STANDBY) ||
+	       (state == PM_SUSPEND_MEM);
 }
 
-/*
- * Called after devices are re-setup, but before processes are thawed.
- */
-static int pnx4008_pm_finish(suspend_state_t state)
-{
-	return 0;
-}
-
-/*
- * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk.
- */
 static struct pm_ops pnx4008_pm_ops = {
-	.prepare = pnx4008_pm_prepare,
 	.enter = pnx4008_pm_enter,
-	.finish = pnx4008_pm_finish,
+	.valid = pnx4008_pm_valid,
 };
 
 static int __init pnx4008_pm_init(void)
diff --git a/arch/arm/mach-pxa/pm.c b/arch/arm/mach-pxa/pm.c
index b4d8276..6bf15ae 100644
--- a/arch/arm/mach-pxa/pm.c
+++ b/arch/arm/mach-pxa/pm.c
@@ -223,14 +223,11 @@
 
 EXPORT_SYMBOL_GPL(pxa_pm_finish);
 
-/*
- * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk.
- */
 static struct pm_ops pxa_pm_ops = {
-	.pm_disk_mode	= PM_DISK_FIRMWARE,
 	.prepare	= pxa_pm_prepare,
 	.enter		= pxa_pm_enter,
 	.finish		= pxa_pm_finish,
+	.valid		= pm_valid_only_mem,
 };
 
 static int __init pxa_pm_init(void)
diff --git a/arch/arm/mach-sa1100/pm.c b/arch/arm/mach-sa1100/pm.c
index 786c853..d674cf3 100644
--- a/arch/arm/mach-sa1100/pm.c
+++ b/arch/arm/mach-sa1100/pm.c
@@ -59,9 +59,6 @@
 	unsigned long gpio, sleep_save[SLEEP_SAVE_SIZE];
 	struct timespec delta, rtc;
 
-	if (state != PM_SUSPEND_MEM)
-		return -EINVAL;
-
 	/* preserve current time */
 	rtc.tv_sec = RCNR;
 	rtc.tv_nsec = 0;
@@ -134,12 +131,9 @@
 	return virt_to_phys(sp);
 }
 
-/*
- * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk.
- */
 static struct pm_ops sa11x0_pm_ops = {
-	.pm_disk_mode	= PM_DISK_FIRMWARE,
 	.enter		= sa11x0_pm_enter,
+	.valid		= pm_valid_only_mem,
 };
 
 static int __init sa11x0_pm_init(void)
diff --git a/arch/arm/plat-s3c24xx/pm.c b/arch/arm/plat-s3c24xx/pm.c
index ecf68d6..c6b03f8 100644
--- a/arch/arm/plat-s3c24xx/pm.c
+++ b/arch/arm/plat-s3c24xx/pm.c
@@ -511,11 +511,6 @@
 		return -EINVAL;
 	}
 
-	if (state != PM_SUSPEND_MEM) {
-		printk(KERN_ERR PFX "error: only PM_SUSPEND_MEM supported\n");
-		return -EINVAL;
-	}
-
 	/* check if we have anything to wake-up with... bad things seem
 	 * to happen if you suspend with no wakeup (system will often
 	 * require a full power-cycle)
@@ -617,30 +612,9 @@
 	return 0;
 }
 
-/*
- * Called after processes are frozen, but before we shut down devices.
- */
-static int s3c2410_pm_prepare(suspend_state_t state)
-{
-	return 0;
-}
-
-/*
- * Called after devices are re-setup, but before processes are thawed.
- */
-static int s3c2410_pm_finish(suspend_state_t state)
-{
-	return 0;
-}
-
-/*
- * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk.
- */
 static struct pm_ops s3c2410_pm_ops = {
-	.pm_disk_mode	= PM_DISK_FIRMWARE,
-	.prepare	= s3c2410_pm_prepare,
 	.enter		= s3c2410_pm_enter,
-	.finish		= s3c2410_pm_finish,
+	.valid		= pm_valid_only_mem,
 };
 
 /* s3c2410_pm_init
diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig
index ce4013a..3ec7658 100644
--- a/arch/avr32/Kconfig
+++ b/arch/avr32/Kconfig
@@ -57,9 +57,6 @@
 	bool
 	default n
 
-config GENERIC_BUST_SPINLOCK
-	bool
-
 config GENERIC_HWEIGHT
 	bool
 	default y
@@ -68,6 +65,11 @@
 	bool
 	default y
 
+config GENERIC_BUG
+	bool
+	default y
+	depends on BUG
+
 source "init/Kconfig"
 
 menu "System Type and features"
@@ -106,6 +108,9 @@
 config BOARD_ATSTK1000
 	bool "ATSTK1000 evaluation board"
 	select BOARD_ATSTK1002 if CPU_AT32AP7000
+
+config BOARD_ATNGW100
+	bool "ATNGW100 Network Gateway"
 endchoice
 
 choice
@@ -116,6 +121,8 @@
 	bool "U-Boot (or similar) bootloader"
 endchoice
 
+source "arch/avr32/mach-at32ap/Kconfig"
+
 config LOAD_ADDRESS
 	hex
 	default 0x10000000 if LOADER_U_BOOT=y && CPU_AT32AP7000=y
diff --git a/arch/avr32/Makefile b/arch/avr32/Makefile
index 7b842e9..6115fc1 100644
--- a/arch/avr32/Makefile
+++ b/arch/avr32/Makefile
@@ -27,6 +27,7 @@
 head-y					+= arch/avr32/kernel/head.o
 core-$(CONFIG_PLATFORM_AT32AP)		+= arch/avr32/mach-at32ap/
 core-$(CONFIG_BOARD_ATSTK1000)		+= arch/avr32/boards/atstk1000/
+core-$(CONFIG_BOARD_ATNGW100)		+= arch/avr32/boards/atngw100/
 core-$(CONFIG_LOADER_U_BOOT)		+= arch/avr32/boot/u-boot/
 core-y					+= arch/avr32/kernel/
 core-y					+= arch/avr32/mm/
diff --git a/arch/avr32/boards/atngw100/Makefile b/arch/avr32/boards/atngw100/Makefile
new file mode 100644
index 0000000..c740aa1
--- /dev/null
+++ b/arch/avr32/boards/atngw100/Makefile
@@ -0,0 +1 @@
+obj-y				+= setup.o flash.o
diff --git a/arch/avr32/boards/atngw100/flash.c b/arch/avr32/boards/atngw100/flash.c
new file mode 100644
index 0000000..f9b32a8
--- /dev/null
+++ b/arch/avr32/boards/atngw100/flash.c
@@ -0,0 +1,95 @@
+/*
+ * ATNGW100 board-specific flash initialization
+ *
+ * Copyright (C) 2005-2006 Atmel Corporation
+ *
+ * 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.
+ */
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/physmap.h>
+
+#include <asm/arch/smc.h>
+
+static struct smc_config flash_config __initdata = {
+	.ncs_read_setup		= 0,
+	.nrd_setup		= 40,
+	.ncs_write_setup	= 0,
+	.nwe_setup		= 10,
+
+	.ncs_read_pulse		= 80,
+	.nrd_pulse		= 40,
+	.ncs_write_pulse	= 65,
+	.nwe_pulse		= 55,
+
+	.read_cycle		= 120,
+	.write_cycle		= 120,
+
+	.bus_width		= 2,
+	.nrd_controlled		= 1,
+	.nwe_controlled		= 1,
+	.byte_write		= 1,
+};
+
+static struct mtd_partition flash_parts[] = {
+	{
+		.name           = "u-boot",
+		.offset         = 0x00000000,
+		.size           = 0x00020000,           /* 128 KiB */
+		.mask_flags     = MTD_WRITEABLE,
+	},
+	{
+		.name           = "root",
+		.offset         = 0x00020000,
+		.size           = 0x007d0000,
+	},
+	{
+		.name           = "env",
+		.offset         = 0x007f0000,
+		.size           = 0x00010000,
+		.mask_flags     = MTD_WRITEABLE,
+	},
+};
+
+static struct physmap_flash_data flash_data = {
+	.width		= 2,
+	.nr_parts	= ARRAY_SIZE(flash_parts),
+	.parts		= flash_parts,
+};
+
+static struct resource flash_resource = {
+	.start		= 0x00000000,
+	.end		= 0x007fffff,
+	.flags		= IORESOURCE_MEM,
+};
+
+static struct platform_device flash_device = {
+	.name		= "physmap-flash",
+	.id		= 0,
+	.resource	= &flash_resource,
+	.num_resources	= 1,
+	.dev		= {
+		.platform_data = &flash_data,
+	},
+};
+
+/* This needs to be called after the SMC has been initialized */
+static int __init atngw100_flash_init(void)
+{
+	int ret;
+
+	ret = smc_set_configuration(0, &flash_config);
+	if (ret < 0) {
+		printk(KERN_ERR "atngw100: failed to set NOR flash timing\n");
+		return ret;
+	}
+
+	platform_device_register(&flash_device);
+
+	return 0;
+}
+device_initcall(atngw100_flash_init);
diff --git a/arch/avr32/boards/atngw100/setup.c b/arch/avr32/boards/atngw100/setup.c
new file mode 100644
index 0000000..9bc37d4
--- /dev/null
+++ b/arch/avr32/boards/atngw100/setup.c
@@ -0,0 +1,124 @@
+/*
+ * Board-specific setup code for the ATNGW100 Network Gateway
+ *
+ * Copyright (C) 2005-2006 Atmel Corporation
+ *
+ * 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.
+ */
+#include <linux/clk.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/linkage.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/spi/spi.h>
+
+#include <asm/io.h>
+#include <asm/setup.h>
+
+#include <asm/arch/at32ap7000.h>
+#include <asm/arch/board.h>
+#include <asm/arch/init.h>
+
+/* Initialized by bootloader-specific startup code. */
+struct tag *bootloader_tags __initdata;
+
+struct eth_addr {
+	u8 addr[6];
+};
+static struct eth_addr __initdata hw_addr[2];
+static struct eth_platform_data __initdata eth_data[2];
+
+static struct spi_board_info spi0_board_info[] __initdata = {
+	{
+		.modalias	= "mtd_dataflash",
+		.max_speed_hz	= 10000000,
+		.chip_select	= 0,
+	},
+};
+
+/*
+ * The next two functions should go away as the boot loader is
+ * supposed to initialize the macb address registers with a valid
+ * ethernet address. But we need to keep it around for a while until
+ * we can be reasonably sure the boot loader does this.
+ *
+ * The phy_id is ignored as the driver will probe for it.
+ */
+static int __init parse_tag_ethernet(struct tag *tag)
+{
+	int i;
+
+	i = tag->u.ethernet.mac_index;
+	if (i < ARRAY_SIZE(hw_addr))
+		memcpy(hw_addr[i].addr, tag->u.ethernet.hw_address,
+		       sizeof(hw_addr[i].addr));
+
+	return 0;
+}
+__tagtable(ATAG_ETHERNET, parse_tag_ethernet);
+
+static void __init set_hw_addr(struct platform_device *pdev)
+{
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	const u8 *addr;
+	void __iomem *regs;
+	struct clk *pclk;
+
+	if (!res)
+		return;
+	if (pdev->id >= ARRAY_SIZE(hw_addr))
+		return;
+
+	addr = hw_addr[pdev->id].addr;
+	if (!is_valid_ether_addr(addr))
+		return;
+
+	/*
+	 * Since this is board-specific code, we'll cheat and use the
+	 * physical address directly as we happen to know that it's
+	 * the same as the virtual address.
+	 */
+	regs = (void __iomem __force *)res->start;
+	pclk = clk_get(&pdev->dev, "pclk");
+	if (!pclk)
+		return;
+
+	clk_enable(pclk);
+	__raw_writel((addr[3] << 24) | (addr[2] << 16)
+		     | (addr[1] << 8) | addr[0], regs + 0x98);
+	__raw_writel((addr[5] << 8) | addr[4], regs + 0x9c);
+	clk_disable(pclk);
+	clk_put(pclk);
+}
+
+struct platform_device *at32_usart_map[1];
+unsigned int at32_nr_usarts = 1;
+
+void __init setup_board(void)
+{
+	at32_map_usart(1, 0);	/* USART 1: /dev/ttyS0, DB9 */
+	at32_setup_serial_console(0);
+}
+
+static int __init atngw100_init(void)
+{
+	/*
+	 * ATNGW100 uses 16-bit SDRAM interface, so we don't need to
+	 * reserve any pins for it.
+	 */
+
+	at32_add_system_devices();
+
+	at32_add_device_usart(0);
+
+	set_hw_addr(at32_add_device_eth(0, &eth_data[0]));
+	set_hw_addr(at32_add_device_eth(1, &eth_data[1]));
+
+	at32_add_device_spi(0, spi0_board_info, ARRAY_SIZE(spi0_board_info));
+
+	return 0;
+}
+postcore_initcall(atngw100_init);
diff --git a/arch/avr32/boards/atstk1000/atstk1002.c b/arch/avr32/boards/atstk1000/atstk1002.c
index 5974768..abe6ca2 100644
--- a/arch/avr32/boards/atstk1000/atstk1002.c
+++ b/arch/avr32/boards/atstk1000/atstk1002.c
@@ -33,7 +33,7 @@
 static struct eth_addr __initdata hw_addr[2];
 
 static struct eth_platform_data __initdata eth_data[2];
-extern struct lcdc_platform_data atstk1000_fb0_data;
+static struct lcdc_platform_data atstk1000_fb0_data;
 
 static struct spi_board_info spi0_board_info[] __initdata = {
 	{
@@ -148,6 +148,8 @@
 	set_hw_addr(at32_add_device_eth(0, &eth_data[0]));
 
 	at32_add_device_spi(0, spi0_board_info, ARRAY_SIZE(spi0_board_info));
+	atstk1000_fb0_data.fbmem_start = fbmem_start;
+	atstk1000_fb0_data.fbmem_size = fbmem_size;
 	at32_add_device_lcdc(0, &atstk1000_fb0_data);
 
 	return 0;
diff --git a/arch/avr32/boards/atstk1000/setup.c b/arch/avr32/boards/atstk1000/setup.c
index 272c011..2bc4b88 100644
--- a/arch/avr32/boards/atstk1000/setup.c
+++ b/arch/avr32/boards/atstk1000/setup.c
@@ -18,33 +18,3 @@
 
 /* Initialized by bootloader-specific startup code. */
 struct tag *bootloader_tags __initdata;
-
-struct lcdc_platform_data __initdata atstk1000_fb0_data;
-
-void __init board_setup_fbmem(unsigned long fbmem_start,
-			      unsigned long fbmem_size)
-{
-	if (!fbmem_size)
-		return;
-
-	if (!fbmem_start) {
-		void *fbmem;
-
-		fbmem = alloc_bootmem_low_pages(fbmem_size);
-		fbmem_start = __pa(fbmem);
-	} else {
-		pg_data_t *pgdat;
-
-		for_each_online_pgdat(pgdat) {
-			if (fbmem_start >= pgdat->bdata->node_boot_start
-			    && fbmem_start <= pgdat->bdata->node_low_pfn)
-				reserve_bootmem_node(pgdat, fbmem_start,
-						     fbmem_size);
-		}
-	}
-
-	printk("%luKiB framebuffer memory at address 0x%08lx\n",
-	       fbmem_size >> 10, fbmem_start);
-	atstk1000_fb0_data.fbmem_start = fbmem_start;
-	atstk1000_fb0_data.fbmem_size = fbmem_size;
-}
diff --git a/arch/avr32/configs/atngw100_defconfig b/arch/avr32/configs/atngw100_defconfig
new file mode 100644
index 0000000..c254ffc
--- /dev/null
+++ b/arch/avr32/configs/atngw100_defconfig
@@ -0,0 +1,1085 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.21-rc6
+# Thu Apr 12 16:35:07 2007
+#
+CONFIG_AVR32=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_TIME=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_GENERIC_BUG=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+# CONFIG_IPC_NS is not set
+CONFIG_SYSVIPC_SYSCTL=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+# CONFIG_TASKSTATS is not set
+# CONFIG_UTS_NS is not set
+# CONFIG_AUDIT is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_SYSFS_DEPRECATED=y
+# CONFIG_RELAY is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+# CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+# CONFIG_BASE_FULL is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SHMEM=y
+CONFIG_SLAB=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=1
+# CONFIG_SLOB is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Block layer
+#
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_AS is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+
+#
+# System Type and features
+#
+CONFIG_SUBARCH_AVR32B=y
+CONFIG_MMU=y
+CONFIG_PERFORMANCE_COUNTERS=y
+CONFIG_PLATFORM_AT32AP=y
+CONFIG_CPU_AT32AP7000=y
+# CONFIG_BOARD_ATSTK1000 is not set
+CONFIG_BOARD_ATNGW100=y
+CONFIG_LOADER_U_BOOT=y
+
+#
+# Atmel AVR32 AP options
+#
+# CONFIG_AP7000_32_BIT_SMC is not set
+CONFIG_AP7000_16_BIT_SMC=y
+# CONFIG_AP7000_8_BIT_SMC is not set
+CONFIG_LOAD_ADDRESS=0x10000000
+CONFIG_ENTRY_ADDRESS=0x90000000
+CONFIG_PHYS_OFFSET=0x10000000
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+# CONFIG_HAVE_ARCH_BOOTMEM_NODE is not set
+# CONFIG_ARCH_HAVE_MEMORY_PRESENT is not set
+# CONFIG_NEED_NODE_MEMMAP_SIZE is not set
+CONFIG_ARCH_FLATMEM_ENABLE=y
+# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
+# CONFIG_ARCH_SPARSEMEM_ENABLE is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+# CONFIG_OWNERSHIP_TRACE is not set
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=250
+CONFIG_CMDLINE=""
+
+#
+# Bus options
+#
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+# CONFIG_PCCARD is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+# CONFIG_NETDEBUG is not set
+CONFIG_PACKET=y
+CONFIG_PACKET_MMAP=y
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=y
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+CONFIG_NET_KEY=y
+# CONFIG_NET_KEY_MIGRATE is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_ASK_IP_FIB_HASH=y
+# CONFIG_IP_FIB_TRIE is not set
+CONFIG_IP_FIB_HASH=y
+# CONFIG_IP_MULTIPLE_TABLES is not set
+# CONFIG_IP_ROUTE_MULTIPATH is not set
+# CONFIG_IP_ROUTE_VERBOSE is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+CONFIG_IP_MROUTE=y
+CONFIG_IP_PIMSM_V1=y
+# CONFIG_IP_PIMSM_V2 is not set
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=y
+CONFIG_INET_ESP=y
+CONFIG_INET_IPCOMP=y
+CONFIG_INET_XFRM_TUNNEL=y
+CONFIG_INET_TUNNEL=y
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+
+#
+# IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=y
+# CONFIG_IPV6_PRIVACY is not set
+# CONFIG_IPV6_ROUTER_PREF is not set
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+# CONFIG_IPV6_MIP6 is not set
+CONFIG_INET6_XFRM_TUNNEL=y
+CONFIG_INET6_TUNNEL=y
+CONFIG_INET6_XFRM_MODE_TRANSPORT=y
+CONFIG_INET6_XFRM_MODE_TUNNEL=y
+CONFIG_INET6_XFRM_MODE_BEET=y
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+CONFIG_IPV6_SIT=y
+# CONFIG_IPV6_TUNNEL is not set
+# CONFIG_IPV6_MULTIPLE_TABLES is not set
+# CONFIG_NETWORK_SECMARK is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# Core Netfilter Configuration
+#
+# CONFIG_NETFILTER_NETLINK is not set
+CONFIG_NF_CONNTRACK_ENABLED=m
+CONFIG_NF_CONNTRACK_SUPPORT=y
+# CONFIG_IP_NF_CONNTRACK_SUPPORT is not set
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_CT_ACCT=y
+CONFIG_NF_CONNTRACK_MARK=y
+# CONFIG_NF_CONNTRACK_EVENTS is not set
+CONFIG_NF_CT_PROTO_GRE=m
+# CONFIG_NF_CT_PROTO_SCTP is not set
+CONFIG_NF_CONNTRACK_AMANDA=m
+CONFIG_NF_CONNTRACK_FTP=m
+CONFIG_NF_CONNTRACK_H323=m
+CONFIG_NF_CONNTRACK_IRC=m
+CONFIG_NF_CONNTRACK_NETBIOS_NS=m
+CONFIG_NF_CONNTRACK_PPTP=m
+CONFIG_NF_CONNTRACK_SANE=m
+CONFIG_NF_CONNTRACK_SIP=m
+CONFIG_NF_CONNTRACK_TFTP=m
+CONFIG_NETFILTER_XTABLES=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
+# CONFIG_NETFILTER_XT_TARGET_CONNMARK is not set
+# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
+CONFIG_NETFILTER_XT_TARGET_MARK=m
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
+CONFIG_NETFILTER_XT_TARGET_NFLOG=m
+# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_COMMENT=m
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
+# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
+# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
+CONFIG_NETFILTER_XT_MATCH_ESP=m
+CONFIG_NETFILTER_XT_MATCH_HELPER=m
+CONFIG_NETFILTER_XT_MATCH_LENGTH=m
+CONFIG_NETFILTER_XT_MATCH_LIMIT=m
+CONFIG_NETFILTER_XT_MATCH_MAC=m
+CONFIG_NETFILTER_XT_MATCH_MARK=m
+CONFIG_NETFILTER_XT_MATCH_POLICY=m
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
+CONFIG_NETFILTER_XT_MATCH_QUOTA=m
+CONFIG_NETFILTER_XT_MATCH_REALM=m
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
+CONFIG_NETFILTER_XT_MATCH_STATE=m
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
+CONFIG_NETFILTER_XT_MATCH_STRING=m
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_NF_CONNTRACK_PROC_COMPAT=y
+# CONFIG_IP_NF_QUEUE is not set
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+# CONFIG_IP_NF_TARGET_ULOG is not set
+CONFIG_NF_NAT=m
+CONFIG_NF_NAT_NEEDED=y
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_TARGET_NETMAP=m
+CONFIG_IP_NF_TARGET_SAME=m
+CONFIG_NF_NAT_SNMP_BASIC=m
+CONFIG_NF_NAT_PROTO_GRE=m
+CONFIG_NF_NAT_FTP=m
+CONFIG_NF_NAT_IRC=m
+CONFIG_NF_NAT_TFTP=m
+CONFIG_NF_NAT_AMANDA=m
+CONFIG_NF_NAT_PPTP=m
+CONFIG_NF_NAT_H323=m
+CONFIG_NF_NAT_SIP=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_TARGET_CLUSTERIP=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+
+#
+# IPv6: Netfilter Configuration (EXPERIMENTAL)
+#
+CONFIG_NF_CONNTRACK_IPV6=m
+CONFIG_IP6_NF_QUEUE=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_OWNER=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_AH=m
+CONFIG_IP6_NF_MATCH_MH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_LOG=m
+CONFIG_IP6_NF_TARGET_REJECT=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_TARGET_HL=m
+CONFIG_IP6_NF_RAW=m
+
+#
+# DCCP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_DCCP is not set
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_SCTP is not set
+
+#
+# TIPC Configuration (EXPERIMENTAL)
+#
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+CONFIG_VLAN_8021Q=m
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+CONFIG_NET_CLS_ROUTE=y
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_IEEE80211 is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+# CONFIG_FW_LOADER is not set
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+
+#
+# Connector - unified userspace <-> kernelspace linker
+#
+# CONFIG_CONNECTOR is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_CFI_INTELEXT is not set
+CONFIG_MTD_CFI_AMDSTD=y
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+# CONFIG_MTD_OBSOLETE_CHIPS is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_START=0x80000000
+CONFIG_MTD_PHYSMAP_LEN=0x0
+CONFIG_MTD_PHYSMAP_BANKWIDTH=2
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+CONFIG_MTD_DATAFLASH=y
+# CONFIG_MTD_M25P80 is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+
+#
+# NAND Flash Device Drivers
+#
+# CONFIG_MTD_NAND is not set
+
+#
+# OneNAND Flash Device Drivers
+#
+# CONFIG_MTD_ONENAND is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Plug and Play support
+#
+# CONFIG_PNPACPI is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=m
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=m
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+
+#
+# Misc devices
+#
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_NETLINK is not set
+
+#
+# Serial ATA (prod) and Parallel ATA (experimental) drivers
+#
+# CONFIG_ATA is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+
+#
+# Fusion MPT device support
+#
+# CONFIG_FUSION is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+
+#
+# I2O device support
+#
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_TUN=m
+
+#
+# PHY device support
+#
+# CONFIG_PHYLIB is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+CONFIG_MACB=y
+
+#
+# Ethernet (1000 Mbit)
+#
+
+#
+# Ethernet (10000 Mbit)
+#
+
+#
+# Token Ring devices
+#
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=m
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPP_MPPE=m
+CONFIG_PPPOE=m
+# CONFIG_SLIP is not set
+CONFIG_SLHC=m
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+# CONFIG_INPUT is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_ATMEL=y
+CONFIG_SERIAL_ATMEL_CONSOLE=y
+# CONFIG_SERIAL_ATMEL_TTYAT is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_RTC is not set
+# CONFIG_GEN_RTC is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# TPM devices
+#
+# CONFIG_TCG_TPM is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# SPI support
+#
+CONFIG_SPI=y
+# CONFIG_SPI_DEBUG is not set
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+CONFIG_SPI_ATMEL=y
+# CONFIG_SPI_BITBANG is not set
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_AT25 is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 is not set
+
+#
+# Hardware Monitoring support
+#
+# CONFIG_HWMON is not set
+# CONFIG_HWMON_VID is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Digital Video Broadcasting Devices
+#
+# CONFIG_DVB is not set
+
+#
+# Graphics support
+#
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+# CONFIG_FB is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB_ARCH_HAS_HCD is not set
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+
+#
+# LED devices
+#
+# CONFIG_NEW_LEDS is not set
+
+#
+# LED drivers
+#
+
+#
+# LED Triggers
+#
+
+#
+# InfiniBand support
+#
+
+#
+# EDAC - error detection and reporting (RAS) (EXPERIMENTAL)
+#
+
+#
+# Real Time Clock
+#
+# CONFIG_RTC_CLASS is not set
+
+#
+# DMA Engine support
+#
+# CONFIG_DMA_ENGINE is not set
+
+#
+# DMA Clients
+#
+
+#
+# DMA Devices
+#
+
+#
+# Auxiliary Display support
+#
+
+#
+# Virtualization
+#
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_FS_XATTR is not set
+# CONFIG_EXT4DEV_FS is not set
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_INOTIFY is not set
+# CONFIG_QUOTA is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+CONFIG_FUSE_FS=m
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=850
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+# CONFIG_PROC_KCORE is not set
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+CONFIG_CONFIGFS_FS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_SUMMARY is not set
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+# CONFIG_NFS_V4 is not set
+# CONFIG_NFS_DIRECTIO is not set
+# CONFIG_NFSD is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+# CONFIG_SMB_NLS_DEFAULT is not set
+CONFIG_CIFS=m
+# CONFIG_CIFS_STATS is not set
+# CONFIG_CIFS_WEAK_PW_HASH is not set
+# CONFIG_CIFS_XATTR is not set
+# CONFIG_CIFS_DEBUG2 is not set
+# CONFIG_CIFS_EXPERIMENTAL is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+# CONFIG_9P_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+CONFIG_NLS_CODEPAGE_850=y
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+CONFIG_NLS_UTF8=y
+
+#
+# Distributed Lock Manager
+#
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_TIMER_STATS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_LIST is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_FORCED_INLINING is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_KPROBES is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_HMAC=y
+# CONFIG_CRYPTO_XCBC is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=y
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_WP512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+CONFIG_CRYPTO_ECB=m
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_PCBC=m
+# CONFIG_CRYPTO_LRW is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_TEA is not set
+CONFIG_CRYPTO_ARC4=m
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+CONFIG_CRYPTO_DEFLATE=y
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Hardware crypto devices
+#
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_CRC_CCITT=m
+# CONFIG_CRC16 is not set
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_TEXTSEARCH=y
+CONFIG_TEXTSEARCH_KMP=m
+CONFIG_TEXTSEARCH_BM=m
+CONFIG_TEXTSEARCH_FSM=m
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
diff --git a/arch/avr32/kernel/cpu.c b/arch/avr32/kernel/cpu.c
index 2e72fd2..2714cf6 100644
--- a/arch/avr32/kernel/cpu.c
+++ b/arch/avr32/kernel/cpu.c
@@ -209,16 +209,17 @@
 void __init setup_processor(void)
 {
 	unsigned long config0, config1;
+	unsigned long features;
 	unsigned cpu_id, cpu_rev, arch_id, arch_rev, mmu_type;
 	unsigned tmp;
 
-	config0 = sysreg_read(CONFIG0); /* 0x0000013e; */
-	config1 = sysreg_read(CONFIG1); /* 0x01f689a2; */
-	cpu_id = config0 >> 24;
-	cpu_rev = (config0 >> 16) & 0xff;
-	arch_id = (config0 >> 13) & 0x07;
-	arch_rev = (config0 >> 10) & 0x07;
-	mmu_type = (config0 >> 7) & 0x03;
+	config0 = sysreg_read(CONFIG0);
+	config1 = sysreg_read(CONFIG1);
+	cpu_id = SYSREG_BFEXT(PROCESSORID, config0);
+	cpu_rev = SYSREG_BFEXT(PROCESSORREVISION, config0);
+	arch_id = SYSREG_BFEXT(AT, config0);
+	arch_rev = SYSREG_BFEXT(AR, config0);
+	mmu_type = SYSREG_BFEXT(MMUT, config0);
 
 	boot_cpu_data.arch_type = arch_id;
 	boot_cpu_data.cpu_type = cpu_id;
@@ -226,16 +227,16 @@
 	boot_cpu_data.cpu_revision = cpu_rev;
 	boot_cpu_data.tlb_config = mmu_type;
 
-	tmp = (config1 >> 13) & 0x07;
+	tmp = SYSREG_BFEXT(ILSZ, config1);
 	if (tmp) {
-		boot_cpu_data.icache.ways = 1 << ((config1 >> 10) & 0x07);
-		boot_cpu_data.icache.sets = 1 << ((config1 >> 16) & 0x0f);
+		boot_cpu_data.icache.ways = 1 << SYSREG_BFEXT(IASS, config1);
+		boot_cpu_data.icache.sets = 1 << SYSREG_BFEXT(ISET, config1);
 		boot_cpu_data.icache.linesz = 1 << (tmp + 1);
 	}
-	tmp = (config1 >> 3) & 0x07;
+	tmp = SYSREG_BFEXT(DLSZ, config1);
 	if (tmp) {
-		boot_cpu_data.dcache.ways = 1 << (config1 & 0x07);
-		boot_cpu_data.dcache.sets = 1 << ((config1 >> 6) & 0x0f);
+		boot_cpu_data.dcache.ways = 1 << SYSREG_BFEXT(DASS, config1);
+		boot_cpu_data.dcache.sets = 1 << SYSREG_BFEXT(DSET, config1);
 		boot_cpu_data.dcache.linesz = 1 << (tmp + 1);
 	}
 
@@ -250,16 +251,39 @@
 		cpu_names[cpu_id], cpu_id, cpu_rev,
 		arch_names[arch_id], arch_rev);
 	printk ("CPU: MMU configuration: %s\n", mmu_types[mmu_type]);
+
 	printk ("CPU: features:");
-	if (config0 & (1 << 6))
-		printk(" fpu");
-	if (config0 & (1 << 5))
-		printk(" java");
-	if (config0 & (1 << 4))
-		printk(" perfctr");
-	if (config0 & (1 << 3))
+	features = 0;
+	if (config0 & SYSREG_BIT(CONFIG0_R)) {
+		features |= AVR32_FEATURE_RMW;
+		printk(" rmw");
+	}
+	if (config0 & SYSREG_BIT(CONFIG0_D)) {
+		features |= AVR32_FEATURE_DSP;
+		printk(" dsp");
+	}
+	if (config0 & SYSREG_BIT(CONFIG0_S)) {
+		features |= AVR32_FEATURE_SIMD;
+		printk(" simd");
+	}
+	if (config0 & SYSREG_BIT(CONFIG0_O)) {
+		features |= AVR32_FEATURE_OCD;
 		printk(" ocd");
+	}
+	if (config0 & SYSREG_BIT(CONFIG0_P)) {
+		features |= AVR32_FEATURE_PCTR;
+		printk(" perfctr");
+	}
+	if (config0 & SYSREG_BIT(CONFIG0_J)) {
+		features |= AVR32_FEATURE_JAVA;
+		printk(" java");
+	}
+	if (config0 & SYSREG_BIT(CONFIG0_F)) {
+		features |= AVR32_FEATURE_FPU;
+		printk(" fpu");
+	}
 	printk("\n");
+	boot_cpu_data.features = features;
 }
 
 #ifdef CONFIG_PROC_FS
diff --git a/arch/avr32/kernel/entry-avr32b.S b/arch/avr32/kernel/entry-avr32b.S
index eeb6679..42657f1 100644
--- a/arch/avr32/kernel/entry-avr32b.S
+++ b/arch/avr32/kernel/entry-avr32b.S
@@ -100,55 +100,49 @@
 
 	.global	tlb_miss_common
 tlb_miss_common:
-	mfsr	r0, SYSREG_PTBR
-	mfsr	r1, SYSREG_TLBEAR
+	mfsr	r0, SYSREG_TLBEAR
+	mfsr	r1, SYSREG_PTBR
 
 	/* Is it the vmalloc space? */
-	bld	r1, 31
+	bld	r0, 31
 	brcs	handle_vmalloc_miss
 
 	/* First level lookup */
 pgtbl_lookup:
-	lsr	r2, r1, PGDIR_SHIFT
-	ld.w	r0, r0[r2 << 2]
-	bld	r0, _PAGE_BIT_PRESENT
+	lsr	r2, r0, PGDIR_SHIFT
+	ld.w	r3, r1[r2 << 2]
+	bfextu	r1, r0, PAGE_SHIFT, PGDIR_SHIFT - PAGE_SHIFT
+	bld	r3, _PAGE_BIT_PRESENT
 	brcc	page_table_not_present
 
-	/* TODO: Check access rights on page table if necessary */
-
 	/* Translate to virtual address in P1. */
-	andl	r0, 0xf000
-	sbr	r0, 31
+	andl	r3, 0xf000
+	sbr	r3, 31
 
 	/* Second level lookup */
-	lsl	r1, (32 - PGDIR_SHIFT)
-	lsr	r1, (32 - PGDIR_SHIFT) + PAGE_SHIFT
-	add	r2, r0, r1 << 2
-	ld.w	r1, r2[0]
-	bld	r1, _PAGE_BIT_PRESENT
+	ld.w	r2, r3[r1 << 2]
+	mfsr	r0, SYSREG_TLBARLO
+	bld	r2, _PAGE_BIT_PRESENT
 	brcc	page_not_present
 
 	/* Mark the page as accessed */
-	sbr	r1, _PAGE_BIT_ACCESSED
-	st.w	r2[0], r1
+	sbr	r2, _PAGE_BIT_ACCESSED
+	st.w	r3[r1 << 2], r2
 
 	/* Drop software flags */
-	andl	r1, _PAGE_FLAGS_HARDWARE_MASK & 0xffff
-	mtsr	SYSREG_TLBELO, r1
+	andl	r2, _PAGE_FLAGS_HARDWARE_MASK & 0xffff
+	mtsr	SYSREG_TLBELO, r2
 
 	/* Figure out which entry we want to replace */
-	mfsr	r0, SYSREG_TLBARLO
+	mfsr	r1, SYSREG_MMUCR
 	clz	r2, r0
 	brcc	1f
-	mov	r1, -1			/* All entries have been accessed, */
-	mtsr	SYSREG_TLBARLO, r1	/* so reset TLBAR */
-	mov	r2, 0			/* and start at 0 */
-1:	mfsr	r1, SYSREG_MMUCR
-	lsl	r2, 14
-	andl	r1, 0x3fff, COH
-	or	r1, r2
-	mtsr	SYSREG_MMUCR, r1
+	mov	r3, -1			/* All entries have been accessed, */
+	mov	r2, 0			/* so start at 0 */
+	mtsr	SYSREG_TLBARLO, r3	/* and reset TLBAR */
 
+1:	bfins	r1, r2, SYSREG_DRP_OFFSET, SYSREG_DRP_SIZE
+	mtsr	SYSREG_MMUCR, r1
 	tlbw
 
 	tlbmiss_restore
@@ -156,8 +150,8 @@
 
 handle_vmalloc_miss:
 	/* Simply do the lookup in init's page table */
-	mov	r0, lo(swapper_pg_dir)
-	orh	r0, hi(swapper_pg_dir)
+	mov	r1, lo(swapper_pg_dir)
+	orh	r1, hi(swapper_pg_dir)
 	rjmp	pgtbl_lookup
 
 
@@ -340,12 +334,34 @@
 do_nmi_ll:
 	sub	sp, 4
 	stmts	--sp, r0-lr
-	/* FIXME: Make sure RAR_NMI and RSR_NMI are pushed instead of *_EX */
-	rcall	save_full_context_ex
+	mfsr	r9, SYSREG_RSR_NMI
+	mfsr	r8, SYSREG_RAR_NMI
+	bfextu	r0, r9, MODE_SHIFT, 3
+	brne	2f
+
+1:	pushm	r8, r9	/* PC and SR */
 	mfsr	r12, SYSREG_ECR
 	mov	r11, sp
 	rcall	do_nmi
-	rjmp	bad_return
+	popm	r8-r9
+	mtsr	SYSREG_RAR_NMI, r8
+	tst	r0, r0
+	mtsr	SYSREG_RSR_NMI, r9
+	brne	3f
+
+	ldmts	sp++, r0-lr
+	sub	sp, -4		/* skip r12_orig */
+	rete
+
+2:	sub	r10, sp, -(FRAME_SIZE_FULL - REG_LR)
+	stdsp	sp[4], r10	/* replace saved SP */
+	rjmp	1b
+
+3:	popm	lr
+	sub	sp, -4		/* skip sp */
+	popm	r0-r12
+	sub	sp, -4		/* skip r12_orig */
+	rete
 
 handle_address_fault:
 	sub	sp, 4
@@ -630,9 +646,12 @@
 	rcall	do_IRQ
 
 	lddsp	r4, sp[REG_SR]
-	andh	r4, (MODE_MASK >> 16), COH
+	bfextu	r4, r4, SYSREG_M0_OFFSET, 3
+	cp.w	r4, MODE_SUPERVISOR >> SYSREG_M0_OFFSET
+	breq	2f
+	cp.w	r4, MODE_USER >> SYSREG_M0_OFFSET
 #ifdef CONFIG_PREEMPT
-	brne	2f
+	brne	3f
 #else
 	brne	1f
 #endif
@@ -649,9 +668,18 @@
 	sub	sp, -4		/* ignore r12_orig */
 	rete
 
+2:	get_thread_info	r0
+	ld.w	r1, r0[TI_flags]
+	bld	r1, TIF_CPU_GOING_TO_SLEEP
 #ifdef CONFIG_PREEMPT
-2:
-	get_thread_info	r0
+	brcc	3f
+#else
+	brcc	1b
+#endif
+	sub	r1, pc, . - cpu_idle_skip_sleep
+	stdsp	sp[REG_PC], r1
+#ifdef CONFIG_PREEMPT
+3:	get_thread_info r0
 	ld.w	r2, r0[TI_preempt_count]
 	cp.w	r2, 0
 	brne	1b
@@ -662,12 +690,32 @@
 	bld	r4, SYSREG_GM_OFFSET
 	brcs	1b
 	rcall	preempt_schedule_irq
-	rjmp	1b
 #endif
+	rjmp	1b
 	.endm
 
 	.section .irq.text,"ax",@progbits
 
+.global cpu_idle_sleep
+cpu_idle_sleep:
+	mask_interrupts
+	get_thread_info r8
+	ld.w	r9, r8[TI_flags]
+	bld	r9, TIF_NEED_RESCHED
+	brcs	cpu_idle_enable_int_and_exit
+	sbr	r9, TIF_CPU_GOING_TO_SLEEP
+	st.w	r8[TI_flags], r9
+	unmask_interrupts
+	sleep 0
+cpu_idle_skip_sleep:
+	mask_interrupts
+	ld.w	r9, r8[TI_flags]
+	cbr	r9, TIF_CPU_GOING_TO_SLEEP
+	st.w	r8[TI_flags], r9
+cpu_idle_enable_int_and_exit:
+	unmask_interrupts
+	retal	r12
+
 	.global	irq_level0
 	.global	irq_level1
 	.global	irq_level2
diff --git a/arch/avr32/kernel/module.c b/arch/avr32/kernel/module.c
index b599eae..1167fe9c 100644
--- a/arch/avr32/kernel/module.c
+++ b/arch/avr32/kernel/module.c
@@ -12,10 +12,11 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/moduleloader.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
+#include <linux/bug.h>
 #include <linux/elf.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleloader.h>
 #include <linux/vmalloc.h>
 
 void *module_alloc(unsigned long size)
@@ -315,10 +316,10 @@
 	vfree(module->arch.syminfo);
 	module->arch.syminfo = NULL;
 
-	return 0;
+	return module_bug_finalize(hdr, sechdrs, module);
 }
 
 void module_arch_cleanup(struct module *module)
 {
-
+	module_bug_cleanup(module);
 }
diff --git a/arch/avr32/kernel/process.c b/arch/avr32/kernel/process.c
index 0b43259..4e4181e 100644
--- a/arch/avr32/kernel/process.c
+++ b/arch/avr32/kernel/process.c
@@ -11,6 +11,7 @@
 #include <linux/fs.h>
 #include <linux/ptrace.h>
 #include <linux/reboot.h>
+#include <linux/uaccess.h>
 #include <linux/unistd.h>
 
 #include <asm/sysreg.h>
@@ -19,6 +20,8 @@
 void (*pm_power_off)(void) = NULL;
 EXPORT_SYMBOL(pm_power_off);
 
+extern void cpu_idle_sleep(void);
+
 /*
  * This file handles the architecture-dependent parts of process handling..
  */
@@ -27,9 +30,8 @@
 {
 	/* endless idle loop with no priority at all */
 	while (1) {
-		/* TODO: Enter sleep mode */
 		while (!need_resched())
-			cpu_relax();
+			cpu_idle_sleep();
 		preempt_enable_no_resched();
 		schedule();
 		preempt_disable();
@@ -114,39 +116,178 @@
 	/* do nothing */
 }
 
+static void dump_mem(const char *str, const char *log_lvl,
+		     unsigned long bottom, unsigned long top)
+{
+	unsigned long p;
+	int i;
+
+	printk("%s%s(0x%08lx to 0x%08lx)\n", log_lvl, str, bottom, top);
+
+	for (p = bottom & ~31; p < top; ) {
+		printk("%s%04lx: ", log_lvl, p & 0xffff);
+
+		for (i = 0; i < 8; i++, p += 4) {
+			unsigned int val;
+
+			if (p < bottom || p >= top)
+				printk("         ");
+			else {
+				if (__get_user(val, (unsigned int __user *)p)) {
+					printk("\n");
+					goto out;
+				}
+				printk("%08x ", val);
+			}
+		}
+		printk("\n");
+	}
+
+out:
+	return;
+}
+
+static inline int valid_stack_ptr(struct thread_info *tinfo, unsigned long p)
+{
+	return (p > (unsigned long)tinfo)
+		&& (p < (unsigned long)tinfo + THREAD_SIZE - 3);
+}
+
+#ifdef CONFIG_FRAME_POINTER
+static void show_trace_log_lvl(struct task_struct *tsk, unsigned long *sp,
+			       struct pt_regs *regs, const char *log_lvl)
+{
+	unsigned long lr, fp;
+	struct thread_info *tinfo;
+
+	if (regs)
+		fp = regs->r7;
+	else if (tsk == current)
+		asm("mov %0, r7" : "=r"(fp));
+	else
+		fp = tsk->thread.cpu_context.r7;
+
+	/*
+	 * Walk the stack as long as the frame pointer (a) is within
+	 * the kernel stack of the task, and (b) it doesn't move
+	 * downwards.
+	 */
+	tinfo = task_thread_info(tsk);
+	printk("%sCall trace:\n", log_lvl);
+	while (valid_stack_ptr(tinfo, fp)) {
+		unsigned long new_fp;
+
+		lr = *(unsigned long *)fp;
+#ifdef CONFIG_KALLSYMS
+		printk("%s [<%08lx>] ", log_lvl, lr);
+#else
+		printk(" [<%08lx>] ", lr);
+#endif
+		print_symbol("%s\n", lr);
+
+		new_fp = *(unsigned long *)(fp + 4);
+		if (new_fp <= fp)
+			break;
+		fp = new_fp;
+	}
+	printk("\n");
+}
+#else
+static void show_trace_log_lvl(struct task_struct *tsk, unsigned long *sp,
+			       struct pt_regs *regs, const char *log_lvl)
+{
+	unsigned long addr;
+
+	printk("%sCall trace:\n", log_lvl);
+
+	while (!kstack_end(sp)) {
+		addr = *sp++;
+		if (kernel_text_address(addr)) {
+#ifdef CONFIG_KALLSYMS
+			printk("%s [<%08lx>] ", log_lvl, addr);
+#else
+			printk(" [<%08lx>] ", addr);
+#endif
+			print_symbol("%s\n", addr);
+		}
+	}
+	printk("\n");
+}
+#endif
+
+void show_stack_log_lvl(struct task_struct *tsk, unsigned long sp,
+			struct pt_regs *regs, const char *log_lvl)
+{
+	struct thread_info *tinfo;
+
+	if (sp == 0) {
+		if (tsk)
+			sp = tsk->thread.cpu_context.ksp;
+		else
+			sp = (unsigned long)&tinfo;
+	}
+	if (!tsk)
+		tsk = current;
+
+	tinfo = task_thread_info(tsk);
+
+	if (valid_stack_ptr(tinfo, sp)) {
+		dump_mem("Stack: ", log_lvl, sp,
+			 THREAD_SIZE + (unsigned long)tinfo);
+		show_trace_log_lvl(tsk, (unsigned long *)sp, regs, log_lvl);
+	}
+}
+
+void show_stack(struct task_struct *tsk, unsigned long *stack)
+{
+	show_stack_log_lvl(tsk, (unsigned long)stack, NULL, "");
+}
+
+void dump_stack(void)
+{
+	unsigned long stack;
+
+	show_trace_log_lvl(current, &stack, NULL, "");
+}
+EXPORT_SYMBOL(dump_stack);
+
 static const char *cpu_modes[] = {
 	"Application", "Supervisor", "Interrupt level 0", "Interrupt level 1",
 	"Interrupt level 2", "Interrupt level 3", "Exception", "NMI"
 };
 
-void show_regs(struct pt_regs *regs)
+void show_regs_log_lvl(struct pt_regs *regs, const char *log_lvl)
 {
 	unsigned long sp = regs->sp;
 	unsigned long lr = regs->lr;
 	unsigned long mode = (regs->sr & MODE_MASK) >> MODE_SHIFT;
 
-	if (!user_mode(regs))
+	if (!user_mode(regs)) {
 		sp = (unsigned long)regs + FRAME_SIZE_FULL;
 
-	print_symbol("PC is at %s\n", instruction_pointer(regs));
-	print_symbol("LR is at %s\n", lr);
-	printk("pc : [<%08lx>]    lr : [<%08lx>]    %s\n"
-	       "sp : %08lx  r12: %08lx  r11: %08lx\n",
-	       instruction_pointer(regs),
-	       lr, print_tainted(), sp, regs->r12, regs->r11);
-	printk("r10: %08lx  r9 : %08lx  r8 : %08lx\n",
-	       regs->r10, regs->r9, regs->r8);
-	printk("r7 : %08lx  r6 : %08lx  r5 : %08lx  r4 : %08lx\n",
-	       regs->r7, regs->r6, regs->r5, regs->r4);
-	printk("r3 : %08lx  r2 : %08lx  r1 : %08lx  r0 : %08lx\n",
-	       regs->r3, regs->r2, regs->r1, regs->r0);
-	printk("Flags: %c%c%c%c%c\n",
+		printk("%s", log_lvl);
+		print_symbol("PC is at %s\n", instruction_pointer(regs));
+		printk("%s", log_lvl);
+		print_symbol("LR is at %s\n", lr);
+	}
+
+	printk("%spc : [<%08lx>]    lr : [<%08lx>]    %s\n"
+	       "%ssp : %08lx  r12: %08lx  r11: %08lx\n",
+	       log_lvl, instruction_pointer(regs), lr, print_tainted(),
+	       log_lvl, sp, regs->r12, regs->r11);
+	printk("%sr10: %08lx  r9 : %08lx  r8 : %08lx\n",
+	       log_lvl, regs->r10, regs->r9, regs->r8);
+	printk("%sr7 : %08lx  r6 : %08lx  r5 : %08lx  r4 : %08lx\n",
+	       log_lvl, regs->r7, regs->r6, regs->r5, regs->r4);
+	printk("%sr3 : %08lx  r2 : %08lx  r1 : %08lx  r0 : %08lx\n",
+	       log_lvl, regs->r3, regs->r2, regs->r1, regs->r0);
+	printk("%sFlags: %c%c%c%c%c\n", log_lvl,
 	       regs->sr & SR_Q ? 'Q' : 'q',
 	       regs->sr & SR_V ? 'V' : 'v',
 	       regs->sr & SR_N ? 'N' : 'n',
 	       regs->sr & SR_Z ? 'Z' : 'z',
 	       regs->sr & SR_C ? 'C' : 'c');
-	printk("Mode bits: %c%c%c%c%c%c%c%c%c\n",
+	printk("%sMode bits: %c%c%c%c%c%c%c%c%c\n", log_lvl,
 	       regs->sr & SR_H ? 'H' : 'h',
 	       regs->sr & SR_R ? 'R' : 'r',
 	       regs->sr & SR_J ? 'J' : 'j',
@@ -156,9 +297,21 @@
 	       regs->sr & SR_I1M ? '1' : '.',
 	       regs->sr & SR_I0M ? '0' : '.',
 	       regs->sr & SR_GM ? 'G' : 'g');
-	printk("CPU Mode: %s\n", cpu_modes[mode]);
+	printk("%sCPU Mode: %s\n", log_lvl, cpu_modes[mode]);
+	printk("%sProcess: %s [%d] (task: %p thread: %p)\n",
+	       log_lvl, current->comm, current->pid, current,
+	       task_thread_info(current));
+}
 
-	show_trace(NULL, (unsigned long *)sp, regs);
+void show_regs(struct pt_regs *regs)
+{
+	unsigned long sp = regs->sp;
+
+	if (!user_mode(regs))
+		sp = (unsigned long)regs + FRAME_SIZE_FULL;
+
+	show_regs_log_lvl(regs, "");
+	show_trace_log_lvl(current, (unsigned long *)sp, regs, "");
 }
 EXPORT_SYMBOL(show_regs);
 
diff --git a/arch/avr32/kernel/setup.c b/arch/avr32/kernel/setup.c
index a1a7c3c..b279d66 100644
--- a/arch/avr32/kernel/setup.c
+++ b/arch/avr32/kernel/setup.c
@@ -8,12 +8,14 @@
 
 #include <linux/clk.h>
 #include <linux/init.h>
+#include <linux/initrd.h>
 #include <linux/sched.h>
 #include <linux/console.h>
 #include <linux/ioport.h>
 #include <linux/bootmem.h>
 #include <linux/fs.h>
 #include <linux/module.h>
+#include <linux/pfn.h>
 #include <linux/root_dev.h>
 #include <linux/cpu.h>
 #include <linux/kernel.h>
@@ -30,13 +32,6 @@
 extern int root_mountflags;
 
 /*
- * Bootloader-provided information about physical memory
- */
-struct tag_mem_range *mem_phys;
-struct tag_mem_range *mem_reserved;
-struct tag_mem_range *mem_ramdisk;
-
-/*
  * Initialize loops_per_jiffy as 5000000 (500MIPS).
  * Better make it too large than too small...
  */
@@ -48,48 +43,193 @@
 static char __initdata command_line[COMMAND_LINE_SIZE];
 
 /*
- * Should be more than enough, but if you have a _really_ complex
- * setup, you might need to increase the size of this...
- */
-static struct tag_mem_range __initdata mem_range_cache[32];
-static unsigned mem_range_next_free;
-
-/*
  * Standard memory resources
  */
-static struct resource mem_res[] = {
-	{
-		.name	= "Kernel code",
-		.start	= 0,
-		.end	= 0,
-		.flags	= IORESOURCE_MEM
-	},
-	{
-		.name	= "Kernel data",
-		.start	= 0,
-		.end	= 0,
-		.flags	= IORESOURCE_MEM,
-	},
+static struct resource __initdata kernel_data = {
+	.name	= "Kernel data",
+	.start	= 0,
+	.end	= 0,
+	.flags	= IORESOURCE_MEM,
+};
+static struct resource __initdata kernel_code = {
+	.name	= "Kernel code",
+	.start	= 0,
+	.end	= 0,
+	.flags	= IORESOURCE_MEM,
+	.sibling = &kernel_data,
 };
 
-#define kernel_code	mem_res[0]
-#define kernel_data	mem_res[1]
+/*
+ * Available system RAM and reserved regions as singly linked
+ * lists. These lists are traversed using the sibling pointer in
+ * struct resource and are kept sorted at all times.
+ */
+static struct resource *__initdata system_ram;
+static struct resource *__initdata reserved = &kernel_code;
+
+/*
+ * We need to allocate these before the bootmem allocator is up and
+ * running, so we need this "cache". 32 entries are probably enough
+ * for all but the most insanely complex systems.
+ */
+static struct resource __initdata res_cache[32];
+static unsigned int __initdata res_cache_next_free;
+
+static void __init resource_init(void)
+{
+	struct resource *mem, *res;
+	struct resource *new;
+
+	kernel_code.start = __pa(init_mm.start_code);
+
+	for (mem = system_ram; mem; mem = mem->sibling) {
+		new = alloc_bootmem_low(sizeof(struct resource));
+		memcpy(new, mem, sizeof(struct resource));
+
+		new->sibling = NULL;
+		if (request_resource(&iomem_resource, new))
+			printk(KERN_WARNING "Bad RAM resource %08x-%08x\n",
+			       mem->start, mem->end);
+	}
+
+	for (res = reserved; res; res = res->sibling) {
+		new = alloc_bootmem_low(sizeof(struct resource));
+		memcpy(new, res, sizeof(struct resource));
+
+		new->sibling = NULL;
+		if (insert_resource(&iomem_resource, new))
+			printk(KERN_WARNING
+			       "Bad reserved resource %s (%08x-%08x)\n",
+			       res->name, res->start, res->end);
+	}
+}
+
+static void __init
+add_physical_memory(resource_size_t start, resource_size_t end)
+{
+	struct resource *new, *next, **pprev;
+
+	for (pprev = &system_ram, next = system_ram; next;
+	     pprev = &next->sibling, next = next->sibling) {
+		if (end < next->start)
+			break;
+		if (start <= next->end) {
+			printk(KERN_WARNING
+			       "Warning: Physical memory map is broken\n");
+			printk(KERN_WARNING
+			       "Warning: %08x-%08x overlaps %08x-%08x\n",
+			       start, end, next->start, next->end);
+			return;
+		}
+	}
+
+	if (res_cache_next_free >= ARRAY_SIZE(res_cache)) {
+		printk(KERN_WARNING
+		       "Warning: Failed to add physical memory %08x-%08x\n",
+		       start, end);
+		return;
+	}
+
+	new = &res_cache[res_cache_next_free++];
+	new->start = start;
+	new->end = end;
+	new->name = "System RAM";
+	new->flags = IORESOURCE_MEM;
+
+	*pprev = new;
+}
+
+static int __init
+add_reserved_region(resource_size_t start, resource_size_t end,
+		    const char *name)
+{
+	struct resource *new, *next, **pprev;
+
+	if (end < start)
+		return -EINVAL;
+
+	if (res_cache_next_free >= ARRAY_SIZE(res_cache))
+		return -ENOMEM;
+
+	for (pprev = &reserved, next = reserved; next;
+	     pprev = &next->sibling, next = next->sibling) {
+		if (end < next->start)
+			break;
+		if (start <= next->end)
+			return -EBUSY;
+	}
+
+	new = &res_cache[res_cache_next_free++];
+	new->start = start;
+	new->end = end;
+	new->name = name;
+	new->flags = IORESOURCE_MEM;
+
+	*pprev = new;
+
+	return 0;
+}
+
+static unsigned long __init
+find_free_region(const struct resource *mem, resource_size_t size,
+		 resource_size_t align)
+{
+	struct resource *res;
+	unsigned long target;
+
+	target = ALIGN(mem->start, align);
+	for (res = reserved; res; res = res->sibling) {
+		if ((target + size) <= res->start)
+			break;
+		if (target <= res->end)
+			target = ALIGN(res->end + 1, align);
+	}
+
+	if ((target + size) > (mem->end + 1))
+		return mem->end + 1;
+
+	return target;
+}
+
+static int __init
+alloc_reserved_region(resource_size_t *start, resource_size_t size,
+		      resource_size_t align, const char *name)
+{
+	struct resource *mem;
+	resource_size_t target;
+	int ret;
+
+	for (mem = system_ram; mem; mem = mem->sibling) {
+		target = find_free_region(mem, size, align);
+		if (target <= mem->end) {
+			ret = add_reserved_region(target, target + size - 1,
+						  name);
+			if (!ret)
+				*start = target;
+			return ret;
+		}
+	}
+
+	return -ENOMEM;
+}
 
 /*
  * Early framebuffer allocation. Works as follows:
  *   - If fbmem_size is zero, nothing will be allocated or reserved.
  *   - If fbmem_start is zero when setup_bootmem() is called,
- *     fbmem_size bytes will be allocated from the bootmem allocator.
+ *     a block of fbmem_size bytes will be reserved before bootmem
+ *     initialization. It will be aligned to the largest page size
+ *     that fbmem_size is a multiple of.
  *   - If fbmem_start is nonzero, an area of size fbmem_size will be
- *     reserved at the physical address fbmem_start if necessary. If
- *     the area isn't in a memory region known to the kernel, it will
- *     be left alone.
+ *     reserved at the physical address fbmem_start if possible. If
+ *     it collides with other reserved memory, a different block of
+ *     same size will be allocated, just as if fbmem_start was zero.
  *
  * Board-specific code may use these variables to set up platform data
  * for the framebuffer driver if fbmem_size is nonzero.
  */
-static unsigned long __initdata fbmem_start;
-static unsigned long __initdata fbmem_size;
+resource_size_t __initdata fbmem_start;
+resource_size_t __initdata fbmem_size;
 
 /*
  * "fbmem=xxx[kKmM]" allocates the specified amount of boot memory for
@@ -103,49 +243,43 @@
  */
 static int __init early_parse_fbmem(char *p)
 {
+	int ret;
+	unsigned long align;
+
 	fbmem_size = memparse(p, &p);
-	if (*p == '@')
+	if (*p == '@') {
 		fbmem_start = memparse(p, &p);
+		ret = add_reserved_region(fbmem_start,
+					  fbmem_start + fbmem_size - 1,
+					  "Framebuffer");
+		if (ret) {
+			printk(KERN_WARNING
+			       "Failed to reserve framebuffer memory\n");
+			fbmem_start = 0;
+		}
+	}
+
+	if (!fbmem_start) {
+		if ((fbmem_size & 0x000fffffUL) == 0)
+			align = 0x100000;	/* 1 MiB */
+		else if ((fbmem_size & 0x0000ffffUL) == 0)
+			align = 0x10000;	/* 64 KiB */
+		else
+			align = 0x1000;		/* 4 KiB */
+
+		ret = alloc_reserved_region(&fbmem_start, fbmem_size,
+					    align, "Framebuffer");
+		if (ret) {
+			printk(KERN_WARNING
+			       "Failed to allocate framebuffer memory\n");
+			fbmem_size = 0;
+		}
+	}
+
 	return 0;
 }
 early_param("fbmem", early_parse_fbmem);
 
-static inline void __init resource_init(void)
-{
-	struct tag_mem_range *region;
-
-	kernel_code.start = __pa(init_mm.start_code);
-	kernel_code.end = __pa(init_mm.end_code - 1);
-	kernel_data.start = __pa(init_mm.end_code);
-	kernel_data.end = __pa(init_mm.brk - 1);
-
-	for (region = mem_phys; region; region = region->next) {
-		struct resource *res;
-		unsigned long phys_start, phys_end;
-
-		if (region->size == 0)
-			continue;
-
-		phys_start = region->addr;
-		phys_end = phys_start + region->size - 1;
-
-		res = alloc_bootmem_low(sizeof(*res));
-		res->name = "System RAM";
-		res->start = phys_start;
-		res->end = phys_end;
-		res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
-
-		request_resource (&iomem_resource, res);
-
-		if (kernel_code.start >= res->start &&
-		    kernel_code.end <= res->end)
-			request_resource (res, &kernel_code);
-		if (kernel_data.start >= res->start &&
-		    kernel_data.end <= res->end)
-			request_resource (res, &kernel_data);
-	}
-}
-
 static int __init parse_tag_core(struct tag *tag)
 {
 	if (tag->hdr.size > 2) {
@@ -157,11 +291,9 @@
 }
 __tagtable(ATAG_CORE, parse_tag_core);
 
-static int __init parse_tag_mem_range(struct tag *tag,
-				      struct tag_mem_range **root)
+static int __init parse_tag_mem(struct tag *tag)
 {
-	struct tag_mem_range *cur, **pprev;
-	struct tag_mem_range *new;
+	unsigned long start, end;
 
 	/*
 	 * Ignore zero-sized entries. If we're running standalone, the
@@ -171,34 +303,53 @@
 	if (tag->u.mem_range.size == 0)
 		return 0;
 
-	/*
-	 * Copy the data so the bootmem init code doesn't need to care
-	 * about it.
-	 */
-	if (mem_range_next_free >= ARRAY_SIZE(mem_range_cache))
-		panic("Physical memory map too complex!\n");
+	start = tag->u.mem_range.addr;
+	end = tag->u.mem_range.addr + tag->u.mem_range.size - 1;
 
-	new = &mem_range_cache[mem_range_next_free++];
-	*new = tag->u.mem_range;
+	add_physical_memory(start, end);
+	return 0;
+}
+__tagtable(ATAG_MEM, parse_tag_mem);
 
-	pprev = root;
-	cur = *root;
-	while (cur) {
-		pprev = &cur->next;
-		cur = cur->next;
+static int __init parse_tag_rdimg(struct tag *tag)
+{
+#ifdef CONFIG_INITRD
+	struct tag_mem_range *mem = &tag->u.mem_range;
+	int ret;
+
+	if (initrd_start) {
+		printk(KERN_WARNING
+		       "Warning: Only the first initrd image will be used\n");
+		return 0;
 	}
 
-	*pprev = new;
-	new->next = NULL;
+	ret = add_reserved_region(mem->start, mem->start + mem->size - 1,
+				  "initrd");
+	if (ret) {
+		printk(KERN_WARNING
+		       "Warning: Failed to reserve initrd memory\n");
+		return ret;
+	}
+
+	initrd_start = (unsigned long)__va(mem->addr);
+	initrd_end = initrd_start + mem->size;
+#else
+	printk(KERN_WARNING "RAM disk image present, but "
+	       "no initrd support in kernel, ignoring\n");
+#endif
 
 	return 0;
 }
+__tagtable(ATAG_RDIMG, parse_tag_rdimg);
 
-static int __init parse_tag_mem(struct tag *tag)
+static int __init parse_tag_rsvd_mem(struct tag *tag)
 {
-	return parse_tag_mem_range(tag, &mem_phys);
+	struct tag_mem_range *mem = &tag->u.mem_range;
+
+	return add_reserved_region(mem->addr, mem->addr + mem->size - 1,
+				   "Reserved");
 }
-__tagtable(ATAG_MEM, parse_tag_mem);
+__tagtable(ATAG_RSVD_MEM, parse_tag_rsvd_mem);
 
 static int __init parse_tag_cmdline(struct tag *tag)
 {
@@ -207,12 +358,6 @@
 }
 __tagtable(ATAG_CMDLINE, parse_tag_cmdline);
 
-static int __init parse_tag_rdimg(struct tag *tag)
-{
-	return parse_tag_mem_range(tag, &mem_ramdisk);
-}
-__tagtable(ATAG_RDIMG, parse_tag_rdimg);
-
 static int __init parse_tag_clock(struct tag *tag)
 {
 	/*
@@ -223,12 +368,6 @@
 }
 __tagtable(ATAG_CLOCK, parse_tag_clock);
 
-static int __init parse_tag_rsvd_mem(struct tag *tag)
-{
-	return parse_tag_mem_range(tag, &mem_reserved);
-}
-__tagtable(ATAG_RSVD_MEM, parse_tag_rsvd_mem);
-
 /*
  * Scan the tag table for this tag, and call its parse function. The
  * tag table is built by the linker from all the __tagtable
@@ -260,10 +399,137 @@
 			       t->hdr.tag);
 }
 
+/*
+ * Find a free memory region large enough for storing the
+ * bootmem bitmap.
+ */
+static unsigned long __init
+find_bootmap_pfn(const struct resource *mem)
+{
+	unsigned long bootmap_pages, bootmap_len;
+	unsigned long node_pages = PFN_UP(mem->end - mem->start + 1);
+	unsigned long bootmap_start;
+
+	bootmap_pages = bootmem_bootmap_pages(node_pages);
+	bootmap_len = bootmap_pages << PAGE_SHIFT;
+
+	/*
+	 * Find a large enough region without reserved pages for
+	 * storing the bootmem bitmap. We can take advantage of the
+	 * fact that all lists have been sorted.
+	 *
+	 * We have to check that we don't collide with any reserved
+	 * regions, which includes the kernel image and any RAMDISK
+	 * images.
+	 */
+	bootmap_start = find_free_region(mem, bootmap_len, PAGE_SIZE);
+
+	return bootmap_start >> PAGE_SHIFT;
+}
+
+#define MAX_LOWMEM	HIGHMEM_START
+#define MAX_LOWMEM_PFN	PFN_DOWN(MAX_LOWMEM)
+
+static void __init setup_bootmem(void)
+{
+	unsigned bootmap_size;
+	unsigned long first_pfn, bootmap_pfn, pages;
+	unsigned long max_pfn, max_low_pfn;
+	unsigned node = 0;
+	struct resource *res;
+
+	printk(KERN_INFO "Physical memory:\n");
+	for (res = system_ram; res; res = res->sibling)
+		printk("  %08x-%08x\n", res->start, res->end);
+	printk(KERN_INFO "Reserved memory:\n");
+	for (res = reserved; res; res = res->sibling)
+		printk("  %08x-%08x: %s\n",
+		       res->start, res->end, res->name);
+
+	nodes_clear(node_online_map);
+
+	if (system_ram->sibling)
+		printk(KERN_WARNING "Only using first memory bank\n");
+
+	for (res = system_ram; res; res = NULL) {
+		first_pfn = PFN_UP(res->start);
+		max_low_pfn = max_pfn = PFN_DOWN(res->end + 1);
+		bootmap_pfn = find_bootmap_pfn(res);
+		if (bootmap_pfn > max_pfn)
+			panic("No space for bootmem bitmap!\n");
+
+		if (max_low_pfn > MAX_LOWMEM_PFN) {
+			max_low_pfn = MAX_LOWMEM_PFN;
+#ifndef CONFIG_HIGHMEM
+			/*
+			 * Lowmem is memory that can be addressed
+			 * directly through P1/P2
+			 */
+			printk(KERN_WARNING
+			       "Node %u: Only %ld MiB of memory will be used.\n",
+			       node, MAX_LOWMEM >> 20);
+			printk(KERN_WARNING "Use a HIGHMEM enabled kernel.\n");
+#else
+#error HIGHMEM is not supported by AVR32 yet
+#endif
+		}
+
+		/* Initialize the boot-time allocator with low memory only. */
+		bootmap_size = init_bootmem_node(NODE_DATA(node), bootmap_pfn,
+						 first_pfn, max_low_pfn);
+
+		/*
+		 * Register fully available RAM pages with the bootmem
+		 * allocator.
+		 */
+		pages = max_low_pfn - first_pfn;
+		free_bootmem_node (NODE_DATA(node), PFN_PHYS(first_pfn),
+				   PFN_PHYS(pages));
+
+		/* Reserve space for the bootmem bitmap... */
+		reserve_bootmem_node(NODE_DATA(node),
+				     PFN_PHYS(bootmap_pfn),
+				     bootmap_size);
+
+		/* ...and any other reserved regions. */
+		for (res = reserved; res; res = res->sibling) {
+			if (res->start > PFN_PHYS(max_pfn))
+				break;
+
+			/*
+			 * resource_init will complain about partial
+			 * overlaps, so we'll just ignore such
+			 * resources for now.
+			 */
+			if (res->start >= PFN_PHYS(first_pfn)
+			    && res->end < PFN_PHYS(max_pfn))
+				reserve_bootmem_node(
+					NODE_DATA(node), res->start,
+					res->end - res->start + 1);
+		}
+
+		node_set_online(node);
+	}
+}
+
 void __init setup_arch (char **cmdline_p)
 {
 	struct clk *cpu_clk;
 
+	init_mm.start_code = (unsigned long)_text;
+	init_mm.end_code = (unsigned long)_etext;
+	init_mm.end_data = (unsigned long)_edata;
+	init_mm.brk = (unsigned long)_end;
+
+	/*
+	 * Include .init section to make allocations easier. It will
+	 * be removed before the resource is actually requested.
+	 */
+	kernel_code.start = __pa(__init_begin);
+	kernel_code.end = __pa(init_mm.end_code - 1);
+	kernel_data.start = __pa(init_mm.end_code);
+	kernel_data.end = __pa(init_mm.brk - 1);
+
 	parse_tags(bootloader_tags);
 
 	setup_processor();
@@ -289,24 +555,16 @@
 		       ((cpu_hz + 500) / 1000) % 1000);
 	}
 
-	init_mm.start_code = (unsigned long) &_text;
-	init_mm.end_code = (unsigned long) &_etext;
-	init_mm.end_data = (unsigned long) &_edata;
-	init_mm.brk = (unsigned long) &_end;
-
 	strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE);
 	*cmdline_p = command_line;
 	parse_early_param();
 
 	setup_bootmem();
 
-	board_setup_fbmem(fbmem_start, fbmem_size);
-
 #ifdef CONFIG_VT
 	conswitchp = &dummy_con;
 #endif
 
 	paging_init();
-
 	resource_init();
 }
diff --git a/arch/avr32/kernel/time.c b/arch/avr32/kernel/time.c
index c10833f..7014a35 100644
--- a/arch/avr32/kernel/time.c
+++ b/arch/avr32/kernel/time.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2004-2006 Atmel Corporation
+ * Copyright (C) 2004-2007 Atmel Corporation
  *
  * Based on MIPS implementation arch/mips/kernel/time.c
  *   Copyright 2001 MontaVista Software Inc.
@@ -20,18 +20,25 @@
 #include <linux/init.h>
 #include <linux/profile.h>
 #include <linux/sysdev.h>
+#include <linux/err.h>
 
 #include <asm/div64.h>
 #include <asm/sysreg.h>
 #include <asm/io.h>
 #include <asm/sections.h>
 
-static cycle_t read_cycle_count(void)
+/* how many counter cycles in a jiffy? */
+static u32 cycles_per_jiffy;
+
+/* the count value for the next timer interrupt */
+static u32 expirelo;
+
+cycle_t __weak read_cycle_count(void)
 {
 	return (cycle_t)sysreg_read(COUNT);
 }
 
-static struct clocksource clocksource_avr32 = {
+struct clocksource __weak clocksource_avr32 = {
 	.name		= "avr32",
 	.rating		= 350,
 	.read		= read_cycle_count,
@@ -40,12 +47,20 @@
 	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
 };
 
+irqreturn_t __weak timer_interrupt(int irq, void *dev_id);
+
+struct irqaction timer_irqaction = {
+	.handler	= timer_interrupt,
+	.flags		= IRQF_DISABLED,
+	.name		= "timer",
+};
+
 /*
  * By default we provide the null RTC ops
  */
 static unsigned long null_rtc_get_time(void)
 {
-	return mktime(2004, 1, 1, 0, 0, 0);
+	return mktime(2007, 1, 1, 0, 0, 0);
 }
 
 static int null_rtc_set_time(unsigned long sec)
@@ -56,23 +71,14 @@
 static unsigned long (*rtc_get_time)(void) = null_rtc_get_time;
 static int (*rtc_set_time)(unsigned long) = null_rtc_set_time;
 
-/* how many counter cycles in a jiffy? */
-static unsigned long cycles_per_jiffy;
-
-/* cycle counter value at the previous timer interrupt */
-static unsigned int timerhi, timerlo;
-
-/* the count value for the next timer interrupt */
-static unsigned int expirelo;
-
 static void avr32_timer_ack(void)
 {
-	unsigned int count;
+	u32 count;
 
 	/* Ack this timer interrupt and set the next one */
 	expirelo += cycles_per_jiffy;
+	/* setting COMPARE to 0 stops the COUNT-COMPARE */
 	if (expirelo == 0) {
-		printk(KERN_DEBUG "expirelo == 0\n");
 		sysreg_write(COMPARE, expirelo + 1);
 	} else {
 		sysreg_write(COMPARE, expirelo);
@@ -86,27 +92,56 @@
 	}
 }
 
-static unsigned int avr32_hpt_read(void)
+int __weak avr32_hpt_init(void)
 {
-	return sysreg_read(COUNT);
+	int ret;
+	unsigned long mult, shift, count_hz;
+
+	count_hz = clk_get_rate(boot_cpu_data.clk);
+	shift = clocksource_avr32.shift;
+	mult = clocksource_hz2mult(count_hz, shift);
+	clocksource_avr32.mult = mult;
+
+	{
+		u64 tmp;
+
+		tmp = TICK_NSEC;
+		tmp <<= shift;
+		tmp += mult / 2;
+		do_div(tmp, mult);
+
+		cycles_per_jiffy = tmp;
+	}
+
+	ret = setup_irq(0, &timer_irqaction);
+	if (ret) {
+		pr_debug("timer: could not request IRQ 0: %d\n", ret);
+		return -ENODEV;
+	}
+
+	printk(KERN_INFO "timer: AT32AP COUNT-COMPARE at irq 0, "
+			"%lu.%03lu MHz\n",
+			((count_hz + 500) / 1000) / 1000,
+			((count_hz + 500) / 1000) % 1000);
+
+	return 0;
 }
 
 /*
  * Taken from MIPS c0_hpt_timer_init().
  *
- * Why is it so complicated, and what is "count"?  My assumption is
- * that `count' specifies the "reference cycle", i.e. the cycle since
- * reset that should mean "zero". The reason COUNT is written twice is
- * probably to make sure we don't get any timer interrupts while we
- * are messing with the counter.
+ * The reason COUNT is written twice is probably to make sure we don't get any
+ * timer interrupts while we are messing with the counter.
  */
-static void avr32_hpt_init(unsigned int count)
+int __weak avr32_hpt_start(void)
 {
-	count = sysreg_read(COUNT) - count;
+	u32 count = sysreg_read(COUNT);
 	expirelo = (count / cycles_per_jiffy + 1) * cycles_per_jiffy;
 	sysreg_write(COUNT, expirelo - cycles_per_jiffy);
 	sysreg_write(COMPARE, expirelo);
 	sysreg_write(COUNT, count);
+
+	return 0;
 }
 
 /*
@@ -115,26 +150,18 @@
  *
  * In UP mode, it is invoked from the (global) timer_interrupt.
  */
-static void local_timer_interrupt(int irq, void *dev_id)
+void local_timer_interrupt(int irq, void *dev_id)
 {
 	if (current->pid)
 		profile_tick(CPU_PROFILING);
 	update_process_times(user_mode(get_irq_regs()));
 }
 
-static irqreturn_t
-timer_interrupt(int irq, void *dev_id)
+irqreturn_t __weak timer_interrupt(int irq, void *dev_id)
 {
-	unsigned int count;
-
 	/* ack timer interrupt and try to set next interrupt */
-	count = avr32_hpt_read();
 	avr32_timer_ack();
 
-	/* Update timerhi/timerlo for intra-jiffy calibration */
-	timerhi += count < timerlo;	/* Wrap around */
-	timerlo = count;
-
 	/*
 	 * Call the generic timer interrupt handler
 	 */
@@ -153,60 +180,37 @@
 	return IRQ_HANDLED;
 }
 
-static struct irqaction timer_irqaction = {
-	.handler	= timer_interrupt,
-	.flags		= IRQF_DISABLED,
-	.name		= "timer",
-};
-
 void __init time_init(void)
 {
-	unsigned long mult, shift, count_hz;
 	int ret;
 
+	/*
+	 * Make sure we don't get any COMPARE interrupts before we can
+	 * handle them.
+	 */
+	sysreg_write(COMPARE, 0);
+
 	xtime.tv_sec = rtc_get_time();
 	xtime.tv_nsec = 0;
 
 	set_normalized_timespec(&wall_to_monotonic,
 				-xtime.tv_sec, -xtime.tv_nsec);
 
-	printk("Before time_init: count=%08lx, compare=%08lx\n",
-	       (unsigned long)sysreg_read(COUNT),
-	       (unsigned long)sysreg_read(COMPARE));
-
-	count_hz = clk_get_rate(boot_cpu_data.clk);
-	shift = clocksource_avr32.shift;
-	mult = clocksource_hz2mult(count_hz, shift);
-	clocksource_avr32.mult = mult;
-
-	printk("Cycle counter: mult=%lu, shift=%lu\n", mult, shift);
-
-	{
-		u64 tmp;
-
-		tmp = TICK_NSEC;
-		tmp <<= shift;
-		tmp += mult / 2;
-		do_div(tmp, mult);
-
-		cycles_per_jiffy = tmp;
+	ret = avr32_hpt_init();
+	if (ret) {
+		pr_debug("timer: failed setup: %d\n", ret);
+		return;
 	}
 
-	/* This sets up the high precision timer for the first interrupt. */
-	avr32_hpt_init(avr32_hpt_read());
-
-	printk("After time_init: count=%08lx, compare=%08lx\n",
-	       (unsigned long)sysreg_read(COUNT),
-	       (unsigned long)sysreg_read(COMPARE));
-
 	ret = clocksource_register(&clocksource_avr32);
 	if (ret)
-		printk(KERN_ERR
-		       "timer: could not register clocksource: %d\n", ret);
+		pr_debug("timer: could not register clocksource: %d\n", ret);
 
-	ret = setup_irq(0, &timer_irqaction);
-	if (ret)
-		printk("timer: could not request IRQ 0: %d\n", ret);
+	ret = avr32_hpt_start();
+	if (ret) {
+		pr_debug("timer: failed starting: %d\n", ret);
+		return;
+	}
 }
 
 static struct sysdev_class timer_class = {
diff --git a/arch/avr32/kernel/traps.c b/arch/avr32/kernel/traps.c
index adc01a1..4f0382d 100644
--- a/arch/avr32/kernel/traps.c
+++ b/arch/avr32/kernel/traps.c
@@ -5,158 +5,25 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-#undef DEBUG
-#include <linux/sched.h>
+
+#include <linux/bug.h>
 #include <linux/init.h>
-#include <linux/module.h>
 #include <linux/kallsyms.h>
+#include <linux/module.h>
 #include <linux/notifier.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
 
-#include <asm/traps.h>
-#include <asm/sysreg.h>
 #include <asm/addrspace.h>
-#include <asm/ocd.h>
 #include <asm/mmu_context.h>
-#include <asm/uaccess.h>
-
-static void dump_mem(const char *str, unsigned long bottom, unsigned long top)
-{
-	unsigned long p;
-	int i;
-
-	printk("%s(0x%08lx to 0x%08lx)\n", str, bottom, top);
-
-	for (p = bottom & ~31; p < top; ) {
-		printk("%04lx: ", p & 0xffff);
-
-		for (i = 0; i < 8; i++, p += 4) {
-			unsigned int val;
-
-			if (p < bottom || p >= top)
-				printk("         ");
-			else {
-				if (__get_user(val, (unsigned int __user *)p)) {
-					printk("\n");
-					goto out;
-				}
-				printk("%08x ", val);
-			}
-		}
-		printk("\n");
-	}
-
-out:
-	return;
-}
-
-static inline int valid_stack_ptr(struct thread_info *tinfo, unsigned long p)
-{
-	return (p > (unsigned long)tinfo)
-		&& (p < (unsigned long)tinfo + THREAD_SIZE - 3);
-}
-
-#ifdef CONFIG_FRAME_POINTER
-static inline void __show_trace(struct task_struct *tsk, unsigned long *sp,
-				struct pt_regs *regs)
-{
-	unsigned long lr, fp;
-	struct thread_info *tinfo;
-
-	tinfo = (struct thread_info *)
-		((unsigned long)sp & ~(THREAD_SIZE - 1));
-
-	if (regs)
-		fp = regs->r7;
-	else if (tsk == current)
-		asm("mov %0, r7" : "=r"(fp));
-	else
-		fp = tsk->thread.cpu_context.r7;
-
-	/*
-	 * Walk the stack as long as the frame pointer (a) is within
-	 * the kernel stack of the task, and (b) it doesn't move
-	 * downwards.
-	 */
-	while (valid_stack_ptr(tinfo, fp)) {
-		unsigned long new_fp;
-
-		lr = *(unsigned long *)fp;
-		printk(" [<%08lx>] ", lr);
-		print_symbol("%s\n", lr);
-
-		new_fp = *(unsigned long *)(fp + 4);
-		if (new_fp <= fp)
-			break;
-		fp = new_fp;
-	}
-	printk("\n");
-}
-#else
-static inline void __show_trace(struct task_struct *tsk, unsigned long *sp,
-				struct pt_regs *regs)
-{
-	unsigned long addr;
-
-	while (!kstack_end(sp)) {
-		addr = *sp++;
-		if (kernel_text_address(addr)) {
-			printk(" [<%08lx>] ", addr);
-			print_symbol("%s\n", addr);
-		}
-	}
-}
-#endif
-
-void show_trace(struct task_struct *tsk, unsigned long *sp,
-		       struct pt_regs *regs)
-{
-	if (regs &&
-	    (((regs->sr & MODE_MASK) == MODE_EXCEPTION) ||
-	     ((regs->sr & MODE_MASK) == MODE_USER)))
-		return;
-
-	printk ("Call trace:");
-#ifdef CONFIG_KALLSYMS
-	printk("\n");
-#endif
-
-	__show_trace(tsk, sp, regs);
-	printk("\n");
-}
-
-void show_stack(struct task_struct *tsk, unsigned long *sp)
-{
-	unsigned long stack;
-
-	if (!tsk)
-		tsk = current;
-	if (sp == 0) {
-		if (tsk == current) {
-			register unsigned long *real_sp __asm__("sp");
-			sp = real_sp;
-		} else {
-			sp = (unsigned long *)tsk->thread.cpu_context.ksp;
-		}
-	}
-
-	stack = (unsigned long)sp;
-	dump_mem("Stack: ", stack,
-		 THREAD_SIZE + (unsigned long)tsk->thread_info);
-	show_trace(tsk, sp, NULL);
-}
-
-void dump_stack(void)
-{
-	show_stack(NULL, NULL);
-}
-EXPORT_SYMBOL(dump_stack);
+#include <asm/ocd.h>
+#include <asm/sysreg.h>
+#include <asm/traps.h>
 
 ATOMIC_NOTIFIER_HEAD(avr32_die_chain);
 
 int register_die_notifier(struct notifier_block *nb)
 {
-	pr_debug("register_die_notifier: %p\n", nb);
-
 	return atomic_notifier_chain_register(&avr32_die_chain, nb);
 }
 EXPORT_SYMBOL(register_die_notifier);
@@ -169,93 +36,103 @@
 
 static DEFINE_SPINLOCK(die_lock);
 
-void __die(const char *str, struct pt_regs *regs, unsigned long err,
-	   const char *file, const char *func, unsigned long line)
+void NORET_TYPE die(const char *str, struct pt_regs *regs, long err)
 {
-	struct task_struct *tsk = current;
 	static int die_counter;
 
 	console_verbose();
 	spin_lock_irq(&die_lock);
 	bust_spinlocks(1);
 
-	printk(KERN_ALERT "%s", str);
-	if (file && func)
-		printk(" in %s:%s, line %ld", file, func, line);
-	printk("[#%d]:\n", ++die_counter);
-	print_modules();
-	show_regs(regs);
-	printk("Process %s (pid: %d, stack limit = 0x%p)\n",
-	       tsk->comm, tsk->pid, tsk->thread_info + 1);
-
-	if (!user_mode(regs) || in_interrupt()) {
-		dump_mem("Stack: ", regs->sp,
-			 THREAD_SIZE + (unsigned long)tsk->thread_info);
+	printk(KERN_ALERT "Oops: %s, sig: %ld [#%d]\n" KERN_EMERG,
+	       str, err, ++die_counter);
+#ifdef CONFIG_PREEMPT
+	printk("PREEMPT ");
+#endif
+#ifdef CONFIG_FRAME_POINTER
+	printk("FRAME_POINTER ");
+#endif
+	if (current_cpu_data.features & AVR32_FEATURE_OCD) {
+		unsigned long did = __mfdr(DBGREG_DID);
+		printk("chip: 0x%03lx:0x%04lx rev %lu\n",
+		       (did >> 1) & 0x7ff,
+		       (did >> 12) & 0x7fff,
+		       (did >> 28) & 0xf);
+	} else {
+		printk("cpu: arch %u r%u / core %u r%u\n",
+		       current_cpu_data.arch_type,
+		       current_cpu_data.arch_revision,
+		       current_cpu_data.cpu_type,
+		       current_cpu_data.cpu_revision);
 	}
 
+	print_modules();
+	show_regs_log_lvl(regs, KERN_EMERG);
+	show_stack_log_lvl(current, regs->sp, regs, KERN_EMERG);
 	bust_spinlocks(0);
 	spin_unlock_irq(&die_lock);
-	do_exit(SIGSEGV);
+
+	if (in_interrupt())
+		panic("Fatal exception in interrupt");
+
+	if (panic_on_oops)
+		panic("Fatal exception");
+
+	do_exit(err);
 }
 
-void __die_if_kernel(const char *str, struct pt_regs *regs, unsigned long err,
-		     const char *file, const char *func, unsigned long line)
+void _exception(long signr, struct pt_regs *regs, int code,
+		unsigned long addr)
 {
+	siginfo_t info;
+
 	if (!user_mode(regs))
-		__die(str, regs, err, file, func, line);
+		die("Unhandled exception in kernel mode", regs, signr);
+
+	memset(&info, 0, sizeof(info));
+	info.si_signo = signr;
+	info.si_code = code;
+	info.si_addr = (void __user *)addr;
+	force_sig_info(signr, &info, current);
+
+	/*
+	 * Init gets no signals that it doesn't have a handler for.
+	 * That's all very well, but if it has caused a synchronous
+	 * exception and we ignore the resulting signal, it will just
+	 * generate the same exception over and over again and we get
+	 * nowhere.  Better to kill it and let the kernel panic.
+	 */
+	if (is_init(current)) {
+		__sighandler_t handler;
+
+		spin_lock_irq(&current->sighand->siglock);
+		handler = current->sighand->action[signr-1].sa.sa_handler;
+		spin_unlock_irq(&current->sighand->siglock);
+		if (handler == SIG_DFL) {
+			/* init has generated a synchronous exception
+			   and it doesn't have a handler for the signal */
+			printk(KERN_CRIT "init has generated signal %ld "
+			       "but has no handler for it\n", signr);
+			do_exit(signr);
+		}
+	}
 }
 
 asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs)
 {
-#ifdef CONFIG_SUBARCH_AVR32B
-	/*
-	 * The exception entry always saves RSR_EX. For NMI, this is
-	 * wrong; it should be RSR_NMI
-	 */
-	regs->sr = sysreg_read(RSR_NMI);
-#endif
-
-	printk("NMI taken!!!!\n");
-	die("NMI", regs, ecr);
-	BUG();
+	printk(KERN_ALERT "Got Non-Maskable Interrupt, dumping regs\n");
+	show_regs_log_lvl(regs, KERN_ALERT);
+	show_stack_log_lvl(current, regs->sp, regs, KERN_ALERT);
 }
 
 asmlinkage void do_critical_exception(unsigned long ecr, struct pt_regs *regs)
 {
-	printk("Unable to handle critical exception %lu at pc = %08lx!\n",
-	       ecr, regs->pc);
-	die("Oops", regs, ecr);
-	BUG();
+	die("Critical exception", regs, SIGKILL);
 }
 
 asmlinkage void do_address_exception(unsigned long ecr, struct pt_regs *regs)
 {
-	siginfo_t info;
-
-	die_if_kernel("Oops: Address exception in kernel mode", regs, ecr);
-
-#ifdef DEBUG
-	if (ecr == ECR_ADDR_ALIGN_X)
-		pr_debug("Instruction Address Exception at pc = %08lx\n",
-			 regs->pc);
-	else if (ecr == ECR_ADDR_ALIGN_R)
-		pr_debug("Data Address Exception (Read) at pc = %08lx\n",
-			 regs->pc);
-	else if (ecr == ECR_ADDR_ALIGN_W)
-		pr_debug("Data Address Exception (Write) at pc = %08lx\n",
-			 regs->pc);
-	else
-		BUG();
-
-	show_regs(regs);
-#endif
-
-	info.si_signo = SIGBUS;
-	info.si_errno = 0;
-	info.si_code = BUS_ADRALN;
-	info.si_addr = (void __user *)regs->pc;
-
-	force_sig_info(SIGBUS, &info, current);
+	_exception(SIGBUS, regs, BUS_ADRALN, regs->pc);
 }
 
 /* This way of handling undefined instructions is stolen from ARM */
@@ -280,7 +157,8 @@
 {
 	int cop_nr;
 	u32 cpucr;
-	if ( (insn & 0xfdf00000) == 0xf1900000 )
+
+	if ((insn & 0xfdf00000) == 0xf1900000)
 		/* LDC0 */
 		cop_nr = 0;
 	else
@@ -292,136 +170,91 @@
 	sysreg_write(CPUCR, cpucr);
 
 	cpucr = sysreg_read(CPUCR);
-	if ( !(cpucr & (1 << (24 + cop_nr))) ){
-		printk("Coprocessor #%i not found!\n", cop_nr);
-		return -1;
-	}
+	if (!(cpucr & (1 << (24 + cop_nr))))
+		return -ENODEV;
 
 	return 0;
 }
 
-#ifdef CONFIG_BUG
-#ifdef CONFIG_DEBUG_BUGVERBOSE
-static inline void do_bug_verbose(struct pt_regs *regs, u32 insn)
+int is_valid_bugaddr(unsigned long pc)
 {
-	char *file;
-	u16 line;
-	char c;
+	unsigned short opcode;
 
-	if (__get_user(line, (u16 __user *)(regs->pc + 2)))
-		return;
-	if (__get_user(file, (char * __user *)(regs->pc + 4))
-	    || (unsigned long)file < PAGE_OFFSET
-	    || __get_user(c, file))
-		file = "<bad filename>";
+	if (pc < PAGE_OFFSET)
+		return 0;
+	if (probe_kernel_address((u16 *)pc, opcode))
+		return 0;
 
-	printk(KERN_ALERT "kernel BUG at %s:%d!\n", file, line);
+	return opcode == AVR32_BUG_OPCODE;
 }
-#else
-static inline void do_bug_verbose(struct pt_regs *regs, u32 insn)
-{
-
-}
-#endif
-#endif
 
 asmlinkage void do_illegal_opcode(unsigned long ecr, struct pt_regs *regs)
 {
 	u32 insn;
 	struct undef_hook *hook;
-	siginfo_t info;
 	void __user *pc;
+	long code;
 
-	if (!user_mode(regs))
-		goto kernel_trap;
+	if (!user_mode(regs) && (ecr == ECR_ILLEGAL_OPCODE)) {
+		enum bug_trap_type type;
+
+		type = report_bug(regs->pc);
+		switch (type) {
+		case BUG_TRAP_TYPE_NONE:
+			break;
+		case BUG_TRAP_TYPE_WARN:
+			regs->pc += 2;
+			return;
+		case BUG_TRAP_TYPE_BUG:
+			die("Kernel BUG", regs, SIGKILL);
+		}
+	}
 
 	local_irq_enable();
 
-	pc = (void __user *)instruction_pointer(regs);
-	if (__get_user(insn, (u32 __user *)pc))
-		goto invalid_area;
+	if (user_mode(regs)) {
+		pc = (void __user *)instruction_pointer(regs);
+		if (get_user(insn, (u32 __user *)pc))
+			goto invalid_area;
 
-        if (ecr == ECR_COPROC_ABSENT) {
-		if (do_cop_absent(insn) == 0)
+		if (ecr == ECR_COPROC_ABSENT && !do_cop_absent(insn))
 			return;
-        }
 
-	spin_lock_irq(&undef_lock);
-	list_for_each_entry(hook, &undef_hook, node) {
-		if ((insn & hook->insn_mask) == hook->insn_val) {
-			if (hook->fn(regs, insn) == 0) {
-				spin_unlock_irq(&undef_lock);
-				return;
+		spin_lock_irq(&undef_lock);
+		list_for_each_entry(hook, &undef_hook, node) {
+			if ((insn & hook->insn_mask) == hook->insn_val) {
+				if (hook->fn(regs, insn) == 0) {
+					spin_unlock_irq(&undef_lock);
+					return;
+				}
 			}
 		}
+		spin_unlock_irq(&undef_lock);
 	}
-	spin_unlock_irq(&undef_lock);
 
-invalid_area:
-
-#ifdef DEBUG
-	printk("Illegal instruction at pc = %08lx\n", regs->pc);
-	if (regs->pc < TASK_SIZE) {
-		unsigned long ptbr, pgd, pte, *p;
-
-		ptbr = sysreg_read(PTBR);
-		p = (unsigned long *)ptbr;
-		pgd = p[regs->pc >> 22];
-		p = (unsigned long *)((pgd & 0x1ffff000) | 0x80000000);
-		pte = p[(regs->pc >> 12) & 0x3ff];
-		printk("page table: 0x%08lx -> 0x%08lx -> 0x%08lx\n", ptbr, pgd, pte);
-	}
-#endif
-
-	info.si_signo = SIGILL;
-	info.si_errno = 0;
-	info.si_addr = (void __user *)regs->pc;
 	switch (ecr) {
-	case ECR_ILLEGAL_OPCODE:
-	case ECR_UNIMPL_INSTRUCTION:
-		info.si_code = ILL_ILLOPC;
-		break;
 	case ECR_PRIVILEGE_VIOLATION:
-		info.si_code = ILL_PRVOPC;
+		code = ILL_PRVOPC;
 		break;
 	case ECR_COPROC_ABSENT:
-		info.si_code = ILL_COPROC;
+		code = ILL_COPROC;
 		break;
 	default:
-		BUG();
+		code = ILL_ILLOPC;
+		break;
 	}
 
-	force_sig_info(SIGILL, &info, current);
+	_exception(SIGILL, regs, code, regs->pc);
 	return;
 
-kernel_trap:
-#ifdef CONFIG_BUG
-	if (__kernel_text_address(instruction_pointer(regs))) {
-		insn = *(u16 *)instruction_pointer(regs);
-		if (insn == AVR32_BUG_OPCODE) {
-			do_bug_verbose(regs, insn);
-			die("Kernel BUG", regs, 0);
-			return;
-		}
-	}
-#endif
-
-	die("Oops: Illegal instruction in kernel code", regs, ecr);
+invalid_area:
+	_exception(SIGSEGV, regs, SEGV_MAPERR, regs->pc);
 }
 
 asmlinkage void do_fpe(unsigned long ecr, struct pt_regs *regs)
 {
-	siginfo_t info;
-
-	printk("Floating-point exception at pc = %08lx\n", regs->pc);
-
-	/* We have no FPU... */
-	info.si_signo = SIGILL;
-	info.si_errno = 0;
-	info.si_addr = (void __user *)regs->pc;
-	info.si_code = ILL_COPROC;
-
-	force_sig_info(SIGILL, &info, current);
+	/* We have no FPU yet */
+	_exception(SIGILL, regs, ILL_COPROC, regs->pc);
 }
 
 
diff --git a/arch/avr32/kernel/vmlinux.lds.c b/arch/avr32/kernel/vmlinux.lds.c
index ef13b7c7..7ad20cf 100644
--- a/arch/avr32/kernel/vmlinux.lds.c
+++ b/arch/avr32/kernel/vmlinux.lds.c
@@ -26,6 +26,12 @@
 			_sinittext = .;
 			*(.text.reset)
 			*(.init.text)
+			/*
+			 * .exit.text is discarded at runtime, not
+			 * link time, to deal with references from
+			 * __bug_table
+			 */
+			*(.exit.text)
 			_einittext = .;
 		. = ALIGN(4);
 		__tagtable_begin = .;
@@ -86,6 +92,8 @@
 		__stop___ex_table = .;
 	}
 
+	BUG_TABLE
+
 	RODATA
 
 	. = ALIGN(8192);
@@ -126,7 +134,6 @@
 	 * thrown away, as cleanup code is never called unless it's a module.
 	 */
 	/DISCARD/       	: {
-		*(.exit.text)
 		*(.exit.data)
 		*(.exitcall.exit)
 	}
diff --git a/arch/avr32/mach-at32ap/Kconfig b/arch/avr32/mach-at32ap/Kconfig
new file mode 100644
index 0000000..eb30783
--- /dev/null
+++ b/arch/avr32/mach-at32ap/Kconfig
@@ -0,0 +1,31 @@
+if PLATFORM_AT32AP
+
+menu "Atmel AVR32 AP options"
+
+choice
+	prompt "AT32AP7000 static memory bus width"
+	depends on CPU_AT32AP7000
+	default AP7000_16_BIT_SMC
+	help
+	  Define the width of the AP7000 external static memory interface.
+	  This is used to determine how to mangle the address and/or data
+	  when doing little-endian port access.
+
+	  The current code can only support a single external memory bus
+	  width for all chip selects, excluding the flash (which is using
+	  raw access and is thus not affected by any of this.)
+
+config AP7000_32_BIT_SMC
+	bool "32 bit"
+
+config AP7000_16_BIT_SMC
+	bool "16 bit"
+
+config AP7000_8_BIT_SMC
+	bool "8 bit"
+
+endchoice
+
+endmenu
+
+endif # PLATFORM_AT32AP
diff --git a/arch/avr32/mach-at32ap/Makefile b/arch/avr32/mach-at32ap/Makefile
index b21bea9..f1d3957 100644
--- a/arch/avr32/mach-at32ap/Makefile
+++ b/arch/avr32/mach-at32ap/Makefile
@@ -1,2 +1,3 @@
 obj-y				+= at32ap.o clock.o intc.o extint.o pio.o hsmc.o
 obj-$(CONFIG_CPU_AT32AP7000)	+= at32ap7000.o
+obj-$(CONFIG_CPU_AT32AP7000)	+= time-tc.o
diff --git a/arch/avr32/mach-at32ap/at32ap7000.c b/arch/avr32/mach-at32ap/at32ap7000.c
index 472703f..56db45b 100644
--- a/arch/avr32/mach-at32ap/at32ap7000.c
+++ b/arch/avr32/mach-at32ap/at32ap7000.c
@@ -18,6 +18,7 @@
 #include <asm/arch/sm.h>
 
 #include "clock.h"
+#include "hmatrix.h"
 #include "pio.h"
 #include "sm.h"
 
@@ -416,7 +417,15 @@
 	.resource	= sm_resource,
 	.num_resources	= ARRAY_SIZE(sm_resource),
 };
-DEV_CLK(pclk, at32_sm, pbb, 0);
+static struct clk at32_sm_pclk = {
+	.name		= "pclk",
+	.dev		= &at32_sm_device.dev,
+	.parent		= &pbb_clk,
+	.mode		= pbb_clk_mode,
+	.get_rate	= pbb_clk_get_rate,
+	.users		= 1,
+	.index		= 0,
+};
 
 static struct resource intc0_resource[] = {
 	PBMEM(0xfff00400),
@@ -442,6 +451,7 @@
 	.mode		= hsb_clk_mode,
 	.get_rate	= hsb_clk_get_rate,
 	.users		= 1,
+	.index		= 3,
 };
 
 static struct resource smc0_resource[] = {
@@ -467,6 +477,57 @@
 };
 
 /* --------------------------------------------------------------------
+ * HMATRIX
+ * -------------------------------------------------------------------- */
+
+static struct clk hmatrix_clk = {
+	.name		= "hmatrix_clk",
+	.parent		= &pbb_clk,
+	.mode		= pbb_clk_mode,
+	.get_rate	= pbb_clk_get_rate,
+	.index		= 2,
+	.users		= 1,
+};
+#define HMATRIX_BASE	((void __iomem *)0xfff00800)
+
+#define hmatrix_readl(reg)					\
+	__raw_readl((HMATRIX_BASE) + HMATRIX_##reg)
+#define hmatrix_writel(reg,value)				\
+	__raw_writel((value), (HMATRIX_BASE) + HMATRIX_##reg)
+
+/*
+ * Set bits in the HMATRIX Special Function Register (SFR) used by the
+ * External Bus Interface (EBI). This can be used to enable special
+ * features like CompactFlash support, NAND Flash support, etc. on
+ * certain chipselects.
+ */
+static inline void set_ebi_sfr_bits(u32 mask)
+{
+	u32 sfr;
+
+	clk_enable(&hmatrix_clk);
+	sfr = hmatrix_readl(SFR4);
+	sfr |= mask;
+	hmatrix_writel(SFR4, sfr);
+	clk_disable(&hmatrix_clk);
+}
+
+/* --------------------------------------------------------------------
+ *  System Timer/Counter (TC)
+ * -------------------------------------------------------------------- */
+static struct resource at32_systc0_resource[] = {
+	PBMEM(0xfff00c00),
+	IRQ(22),
+};
+struct platform_device at32_systc0_device = {
+	.name		= "systc",
+	.id		= 0,
+	.resource	= at32_systc0_resource,
+	.num_resources	= ARRAY_SIZE(at32_systc0_resource),
+};
+DEV_CLK(pclk, at32_systc0, pbb, 3);
+
+/* --------------------------------------------------------------------
  *  PIO
  * -------------------------------------------------------------------- */
 
@@ -514,6 +575,8 @@
 	platform_device_register(&smc0_device);
 	platform_device_register(&pdc_device);
 
+	platform_device_register(&at32_systc0_device);
+
 	platform_device_register(&pio0_device);
 	platform_device_register(&pio1_device);
 	platform_device_register(&pio2_device);
@@ -950,6 +1013,7 @@
 	&pbb_clk,
 	&at32_sm_pclk,
 	&at32_intc0_pclk,
+	&hmatrix_clk,
 	&ebi_clk,
 	&hramc_clk,
 	&smc0_pclk,
@@ -962,6 +1026,7 @@
 	&pio2_mck,
 	&pio3_mck,
 	&pio4_mck,
+	&at32_systc0_pclk,
 	&atmel_usart0_usart,
 	&atmel_usart1_usart,
 	&atmel_usart2_usart,
@@ -1024,6 +1089,9 @@
 	for (i = 0; i < ARRAY_SIZE(at32_clock_list); i++) {
 		struct clk *clk = at32_clock_list[i];
 
+		if (clk->users == 0)
+			continue;
+
 		if (clk->mode == &cpu_clk_mode)
 			cpu_mask |= 1 << clk->index;
 		else if (clk->mode == &hsb_clk_mode)
diff --git a/arch/avr32/mach-at32ap/hmatrix.h b/arch/avr32/mach-at32ap/hmatrix.h
new file mode 100644
index 0000000..d10bfb6
--- /dev/null
+++ b/arch/avr32/mach-at32ap/hmatrix.h
@@ -0,0 +1,182 @@
+/*
+ * Register definitions for High-Speed Bus Matrix
+ */
+#ifndef __HMATRIX_H
+#define __HMATRIX_H
+
+/* HMATRIX register offsets */
+#define HMATRIX_MCFG0				0x0000
+#define HMATRIX_MCFG1				0x0004
+#define HMATRIX_MCFG2				0x0008
+#define HMATRIX_MCFG3				0x000c
+#define HMATRIX_MCFG4				0x0010
+#define HMATRIX_MCFG5				0x0014
+#define HMATRIX_MCFG6				0x0018
+#define HMATRIX_MCFG7				0x001c
+#define HMATRIX_MCFG8				0x0020
+#define HMATRIX_MCFG9				0x0024
+#define HMATRIX_MCFG10				0x0028
+#define HMATRIX_MCFG11				0x002c
+#define HMATRIX_MCFG12				0x0030
+#define HMATRIX_MCFG13				0x0034
+#define HMATRIX_MCFG14				0x0038
+#define HMATRIX_MCFG15				0x003c
+#define HMATRIX_SCFG0				0x0040
+#define HMATRIX_SCFG1				0x0044
+#define HMATRIX_SCFG2				0x0048
+#define HMATRIX_SCFG3				0x004c
+#define HMATRIX_SCFG4				0x0050
+#define HMATRIX_SCFG5				0x0054
+#define HMATRIX_SCFG6				0x0058
+#define HMATRIX_SCFG7				0x005c
+#define HMATRIX_SCFG8				0x0060
+#define HMATRIX_SCFG9				0x0064
+#define HMATRIX_SCFG10				0x0068
+#define HMATRIX_SCFG11				0x006c
+#define HMATRIX_SCFG12				0x0070
+#define HMATRIX_SCFG13				0x0074
+#define HMATRIX_SCFG14				0x0078
+#define HMATRIX_SCFG15				0x007c
+#define HMATRIX_PRAS0				0x0080
+#define HMATRIX_PRBS0				0x0084
+#define HMATRIX_PRAS1				0x0088
+#define HMATRIX_PRBS1				0x008c
+#define HMATRIX_PRAS2				0x0090
+#define HMATRIX_PRBS2				0x0094
+#define HMATRIX_PRAS3				0x0098
+#define HMATRIX_PRBS3				0x009c
+#define HMATRIX_PRAS4				0x00a0
+#define HMATRIX_PRBS4				0x00a4
+#define HMATRIX_PRAS5				0x00a8
+#define HMATRIX_PRBS5				0x00ac
+#define HMATRIX_PRAS6				0x00b0
+#define HMATRIX_PRBS6				0x00b4
+#define HMATRIX_PRAS7				0x00b8
+#define HMATRIX_PRBS7				0x00bc
+#define HMATRIX_PRAS8				0x00c0
+#define HMATRIX_PRBS8				0x00c4
+#define HMATRIX_PRAS9				0x00c8
+#define HMATRIX_PRBS9				0x00cc
+#define HMATRIX_PRAS10				0x00d0
+#define HMATRIX_PRBS10				0x00d4
+#define HMATRIX_PRAS11				0x00d8
+#define HMATRIX_PRBS11				0x00dc
+#define HMATRIX_PRAS12				0x00e0
+#define HMATRIX_PRBS12				0x00e4
+#define HMATRIX_PRAS13				0x00e8
+#define HMATRIX_PRBS13				0x00ec
+#define HMATRIX_PRAS14				0x00f0
+#define HMATRIX_PRBS14				0x00f4
+#define HMATRIX_PRAS15				0x00f8
+#define HMATRIX_PRBS15				0x00fc
+#define HMATRIX_MRCR				0x0100
+#define HMATRIX_SFR0				0x0110
+#define HMATRIX_SFR1				0x0114
+#define HMATRIX_SFR2				0x0118
+#define HMATRIX_SFR3				0x011c
+#define HMATRIX_SFR4				0x0120
+#define HMATRIX_SFR5				0x0124
+#define HMATRIX_SFR6				0x0128
+#define HMATRIX_SFR7				0x012c
+#define HMATRIX_SFR8				0x0130
+#define HMATRIX_SFR9				0x0134
+#define HMATRIX_SFR10				0x0138
+#define HMATRIX_SFR11				0x013c
+#define HMATRIX_SFR12				0x0140
+#define HMATRIX_SFR13				0x0144
+#define HMATRIX_SFR14				0x0148
+#define HMATRIX_SFR15				0x014c
+
+/* Bitfields in MCFGx */
+#define HMATRIX_ULBT_OFFSET			0
+#define HMATRIX_ULBT_SIZE			3
+
+/* Bitfields in SCFGx */
+#define HMATRIX_SLOT_CYCLE_OFFSET		0
+#define HMATRIX_SLOT_CYCLE_SIZE			8
+#define HMATRIX_DEFMSTR_TYPE_OFFSET		16
+#define HMATRIX_DEFMSTR_TYPE_SIZE		2
+#define HMATRIX_FIXED_DEFMSTR_OFFSET		18
+#define HMATRIX_FIXED_DEFMSTR_SIZE		4
+#define HMATRIX_ARBT_OFFSET			24
+#define HMATRIX_ARBT_SIZE			2
+
+/* Bitfields in PRASx */
+#define HMATRIX_M0PR_OFFSET			0
+#define HMATRIX_M0PR_SIZE			4
+#define HMATRIX_M1PR_OFFSET			4
+#define HMATRIX_M1PR_SIZE			4
+#define HMATRIX_M2PR_OFFSET			8
+#define HMATRIX_M2PR_SIZE			4
+#define HMATRIX_M3PR_OFFSET			12
+#define HMATRIX_M3PR_SIZE			4
+#define HMATRIX_M4PR_OFFSET			16
+#define HMATRIX_M4PR_SIZE			4
+#define HMATRIX_M5PR_OFFSET			20
+#define HMATRIX_M5PR_SIZE			4
+#define HMATRIX_M6PR_OFFSET			24
+#define HMATRIX_M6PR_SIZE			4
+#define HMATRIX_M7PR_OFFSET			28
+#define HMATRIX_M7PR_SIZE			4
+
+/* Bitfields in PRBSx */
+#define HMATRIX_M8PR_OFFSET			0
+#define HMATRIX_M8PR_SIZE			4
+#define HMATRIX_M9PR_OFFSET			4
+#define HMATRIX_M9PR_SIZE			4
+#define HMATRIX_M10PR_OFFSET			8
+#define HMATRIX_M10PR_SIZE			4
+#define HMATRIX_M11PR_OFFSET			12
+#define HMATRIX_M11PR_SIZE			4
+#define HMATRIX_M12PR_OFFSET			16
+#define HMATRIX_M12PR_SIZE			4
+#define HMATRIX_M13PR_OFFSET			20
+#define HMATRIX_M13PR_SIZE			4
+#define HMATRIX_M14PR_OFFSET			24
+#define HMATRIX_M14PR_SIZE			4
+#define HMATRIX_M15PR_OFFSET			28
+#define HMATRIX_M15PR_SIZE			4
+
+/* Bitfields in SFR4 */
+#define HMATRIX_CS1A_OFFSET			1
+#define HMATRIX_CS1A_SIZE			1
+#define HMATRIX_CS3A_OFFSET			3
+#define HMATRIX_CS3A_SIZE			1
+#define HMATRIX_CS4A_OFFSET			4
+#define HMATRIX_CS4A_SIZE			1
+#define HMATRIX_CS5A_OFFSET			5
+#define HMATRIX_CS5A_SIZE			1
+#define HMATRIX_DBPUC_OFFSET			8
+#define HMATRIX_DBPUC_SIZE			1
+
+/* Constants for ULBT */
+#define HMATRIX_ULBT_INFINITE			0
+#define HMATRIX_ULBT_SINGLE			1
+#define HMATRIX_ULBT_FOUR_BEAT			2
+#define HMATRIX_ULBT_EIGHT_BEAT			3
+#define HMATRIX_ULBT_SIXTEEN_BEAT		4
+
+/* Constants for DEFMSTR_TYPE */
+#define HMATRIX_DEFMSTR_TYPE_NO_DEFAULT		0
+#define HMATRIX_DEFMSTR_TYPE_LAST_DEFAULT	1
+#define HMATRIX_DEFMSTR_TYPE_FIXED_DEFAULT	2
+
+/* Constants for ARBT */
+#define HMATRIX_ARBT_ROUND_ROBIN		0
+#define HMATRIX_ARBT_FIXED_PRIORITY		1
+
+/* Bit manipulation macros */
+#define HMATRIX_BIT(name)					\
+	(1 << HMATRIX_##name##_OFFSET)
+#define HMATRIX_BF(name,value)					\
+	(((value) & ((1 << HMATRIX_##name##_SIZE) - 1))		\
+	 << HMATRIX_##name##_OFFSET)
+#define HMATRIX_BFEXT(name,value)				\
+	(((value) >> HMATRIX_##name##_OFFSET)			\
+	 & ((1 << HMATRIX_##name##_SIZE) - 1))
+#define HMATRIX_BFINS(name,value,old)				\
+	(((old) & ~(((1 << HMATRIX_##name##_SIZE) - 1)		\
+		    << HMATRIX_##name##_OFFSET))		\
+	 | HMATRIX_BF(name,value))
+
+#endif /* __HMATRIX_H */
diff --git a/arch/avr32/mach-at32ap/hsmc.c b/arch/avr32/mach-at32ap/hsmc.c
index 7691721..5e22a75 100644
--- a/arch/avr32/mach-at32ap/hsmc.c
+++ b/arch/avr32/mach-at32ap/hsmc.c
@@ -75,12 +75,35 @@
 		return -EINVAL;
 	}
 
+	switch (config->nwait_mode) {
+	case 0:
+		mode |= HSMC_BF(EXNW_MODE, HSMC_EXNW_MODE_DISABLED);
+		break;
+	case 1:
+		mode |= HSMC_BF(EXNW_MODE, HSMC_EXNW_MODE_RESERVED);
+		break;
+	case 2:
+		mode |= HSMC_BF(EXNW_MODE, HSMC_EXNW_MODE_FROZEN);
+		break;
+	case 3:
+		mode |= HSMC_BF(EXNW_MODE, HSMC_EXNW_MODE_READY);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (config->tdf_cycles) {
+		mode |= HSMC_BF(TDF_CYCLES, config->tdf_cycles);
+	}
+
 	if (config->nrd_controlled)
 		mode |= HSMC_BIT(READ_MODE);
 	if (config->nwe_controlled)
 		mode |= HSMC_BIT(WRITE_MODE);
 	if (config->byte_write)
 		mode |= HSMC_BIT(BAT);
+	if (config->tdf_mode)
+		mode |= HSMC_BIT(TDF_MODE);
 
 	pr_debug("smc cs%d: setup/%08x pulse/%08x cycle/%08x mode/%08x\n",
 		 cs, setup, pulse, cycle, mode);
diff --git a/arch/avr32/mach-at32ap/time-tc.c b/arch/avr32/mach-at32ap/time-tc.c
new file mode 100644
index 0000000..e3070bd
--- /dev/null
+++ b/arch/avr32/mach-at32ap/time-tc.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2004-2007 Atmel Corporation
+ *
+ * Based on MIPS implementation arch/mips/kernel/time.c
+ *   Copyright 2001 MontaVista Software Inc.
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/clocksource.h>
+#include <linux/time.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel_stat.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/profile.h>
+#include <linux/sysdev.h>
+#include <linux/err.h>
+
+#include <asm/div64.h>
+#include <asm/sysreg.h>
+#include <asm/io.h>
+#include <asm/sections.h>
+
+#include <asm/arch/time.h>
+
+/* how many counter cycles in a jiffy? */
+static u32 cycles_per_jiffy;
+
+/* the count value for the next timer interrupt */
+static u32 expirelo;
+
+/* the I/O registers of the TC module */
+static void __iomem *ioregs;
+
+cycle_t read_cycle_count(void)
+{
+	return (cycle_t)timer_read(ioregs, 0, CV);
+}
+
+struct clocksource clocksource_avr32 = {
+	.name		= "avr32",
+	.rating		= 342,
+	.read		= read_cycle_count,
+	.mask		= CLOCKSOURCE_MASK(16),
+	.shift		= 16,
+	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static void avr32_timer_ack(void)
+{
+	u16 count = expirelo;
+
+	/* Ack this timer interrupt and set the next one, use a u16
+	 * variable so it will wrap around correctly */
+	count += cycles_per_jiffy;
+	expirelo = count;
+	timer_write(ioregs, 0, RC, expirelo);
+
+	/* Check to see if we have missed any timer interrupts */
+	count = timer_read(ioregs, 0, CV);
+	if ((count - expirelo) < 0x7fff) {
+		expirelo = count + cycles_per_jiffy;
+		timer_write(ioregs, 0, RC, expirelo);
+	}
+}
+
+u32 avr32_hpt_read(void)
+{
+	return timer_read(ioregs, 0, CV);
+}
+
+static int avr32_timer_calc_div_and_set_jiffies(struct clk *pclk)
+{
+	unsigned int cycles_max = (clocksource_avr32.mask + 1) / 2;
+	unsigned int divs[] = { 4, 8, 16, 32 };
+	int divs_size = sizeof(divs) / sizeof(*divs);
+	int i = 0;
+	unsigned long count_hz;
+	unsigned long shift;
+	unsigned long mult;
+	int clock_div = -1;
+	u64 tmp;
+
+	shift = clocksource_avr32.shift;
+
+	do {
+		count_hz = clk_get_rate(pclk) / divs[i];
+		mult = clocksource_hz2mult(count_hz, shift);
+		clocksource_avr32.mult = mult;
+
+		tmp = TICK_NSEC;
+		tmp <<= shift;
+		tmp += mult / 2;
+		do_div(tmp, mult);
+
+		cycles_per_jiffy = tmp;
+	} while (cycles_per_jiffy > cycles_max && ++i < divs_size);
+
+	clock_div = i + 1;
+
+	if (clock_div > divs_size) {
+		pr_debug("timer: could not calculate clock divider\n");
+		return -EFAULT;
+	}
+
+	/* Set the clock divider */
+	timer_write(ioregs, 0, CMR, TIMER_BF(CMR_TCCLKS, clock_div));
+
+	return 0;
+}
+
+int avr32_hpt_init(unsigned int count)
+{
+	struct resource *regs;
+	struct clk *pclk;
+	int irq = -1;
+	int ret = 0;
+
+	ret = -ENXIO;
+
+	irq = platform_get_irq(&at32_systc0_device, 0);
+	if (irq < 0) {
+		pr_debug("timer: could not get irq\n");
+		goto out_error;
+	}
+
+	pclk = clk_get(&at32_systc0_device.dev, "pclk");
+	if (IS_ERR(pclk)) {
+		pr_debug("timer: could not get clk: %ld\n", PTR_ERR(pclk));
+		goto out_error;
+	}
+	clk_enable(pclk);
+
+	regs = platform_get_resource(&at32_systc0_device, IORESOURCE_MEM, 0);
+	if (!regs) {
+		pr_debug("timer: could not get resource\n");
+		goto out_error_clk;
+	}
+
+	ioregs = ioremap(regs->start, regs->end - regs->start + 1);
+	if (!ioregs) {
+		pr_debug("timer: could not get ioregs\n");
+		goto out_error_clk;
+	}
+
+	ret = avr32_timer_calc_div_and_set_jiffies(pclk);
+	if (ret)
+		goto out_error_io;
+
+	ret = setup_irq(irq, &timer_irqaction);
+	if (ret) {
+		pr_debug("timer: could not request irq %d: %d\n",
+				irq, ret);
+		goto out_error_io;
+	}
+
+	expirelo = (timer_read(ioregs, 0, CV) / cycles_per_jiffy + 1)
+		* cycles_per_jiffy;
+
+	/* Enable clock and interrupts on RC compare */
+	timer_write(ioregs, 0, CCR, TIMER_BIT(CCR_CLKEN));
+	timer_write(ioregs, 0, IER, TIMER_BIT(IER_CPCS));
+	/* Set cycles to first interrupt */
+	timer_write(ioregs, 0,  RC, expirelo);
+
+	printk(KERN_INFO "timer: AT32AP system timer/counter at 0x%p irq %d\n",
+			ioregs, irq);
+
+	return 0;
+
+out_error_io:
+	iounmap(ioregs);
+out_error_clk:
+	clk_put(pclk);
+out_error:
+	return ret;
+}
+
+int avr32_hpt_start(void)
+{
+	timer_write(ioregs, 0, CCR, TIMER_BIT(CCR_SWTRG));
+	return 0;
+}
+
+irqreturn_t timer_interrupt(int irq, void *dev_id)
+{
+	unsigned int sr = timer_read(ioregs, 0, SR);
+
+	if (sr & TIMER_BIT(SR_CPCS)) {
+		/* ack timer interrupt and try to set next interrupt */
+		avr32_timer_ack();
+
+		/*
+		 * Call the generic timer interrupt handler
+		 */
+		write_seqlock(&xtime_lock);
+		do_timer(1);
+		write_sequnlock(&xtime_lock);
+
+		/*
+		 * In UP mode, we call local_timer_interrupt() to do profiling
+		 * and process accounting.
+		 *
+		 * SMP is not supported yet.
+		 */
+		local_timer_interrupt(irq, dev_id);
+
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
diff --git a/arch/avr32/mm/fault.c b/arch/avr32/mm/fault.c
index 6785572..146ebdb 100644
--- a/arch/avr32/mm/fault.c
+++ b/arch/avr32/mm/fault.c
@@ -16,26 +16,8 @@
 #include <asm/kdebug.h>
 #include <asm/mmu_context.h>
 #include <asm/sysreg.h>
-#include <asm/uaccess.h>
 #include <asm/tlb.h>
-
-#ifdef DEBUG
-static void dump_code(unsigned long pc)
-{
-	char *p = (char *)pc;
-	char val;
-	int i;
-
-
-	printk(KERN_DEBUG "Code:");
-	for (i = 0; i < 16; i++) {
-		if (__get_user(val, p + i))
-			break;
-		printk(" %02x", val);
-	}
-	printk("\n");
-}
-#endif
+#include <asm/uaccess.h>
 
 #ifdef CONFIG_KPROBES
 ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain);
@@ -68,17 +50,19 @@
 }
 #endif
 
+int exception_trace = 1;
+
 /*
  * This routine handles page faults. It determines the address and the
  * problem, and then passes it off to one of the appropriate routines.
  *
  * ecr is the Exception Cause Register. Possible values are:
- *   5:  Page not found (instruction access)
  *   6:  Protection fault (instruction access)
- *   12: Page not found (read access)
- *   13: Page not found (write access)
- *   14: Protection fault (read access)
- *   15: Protection fault (write access)
+ *   15: Protection fault (read access)
+ *   16: Protection fault (write access)
+ *   20: Page not found (instruction access)
+ *   24: Page not found (read access)
+ *   28: Page not found (write access)
  */
 asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
 {
@@ -88,7 +72,9 @@
 	const struct exception_table_entry *fixup;
 	unsigned long address;
 	unsigned long page;
-	int writeaccess = 0;
+	int writeaccess;
+	long signr;
+	int code;
 
 	if (notify_page_fault(DIE_PAGE_FAULT, regs,
 			      ecr, SIGSEGV) == NOTIFY_STOP)
@@ -99,6 +85,9 @@
 	tsk = current;
 	mm = tsk->mm;
 
+	signr = SIGSEGV;
+	code = SEGV_MAPERR;
+
 	/*
 	 * If we're in an interrupt or have no user context, we must
 	 * not take the fault...
@@ -125,7 +114,9 @@
 	 * can handle it...
 	 */
 good_area:
-	//pr_debug("good area: vm_flags = 0x%lx\n", vma->vm_flags);
+	code = SEGV_ACCERR;
+	writeaccess = 0;
+
 	switch (ecr) {
 	case ECR_PROTECTION_X:
 	case ECR_TLB_MISS_X:
@@ -176,46 +167,24 @@
 	 * map. Fix it, but check if it's kernel or user first...
 	 */
 bad_area:
-	pr_debug("Bad area [%s:%u]: addr %08lx, ecr %lu\n",
-		 tsk->comm, tsk->pid, address, ecr);
-
 	up_read(&mm->mmap_sem);
 
 	if (user_mode(regs)) {
-		/* Hmm...we have to pass address and ecr somehow... */
-		/* tsk->thread.address = address;
-		   tsk->thread.error_code = ecr; */
-#ifdef DEBUG
-		show_regs(regs);
-		dump_code(regs->pc);
-
-		page = sysreg_read(PTBR);
-		printk("ptbr = %08lx", page);
-		if (page) {
-			page = ((unsigned long *)page)[address >> 22];
-			printk(" pgd = %08lx", page);
-			if (page & _PAGE_PRESENT) {
-				page &= PAGE_MASK;
-				address &= 0x003ff000;
-				page = ((unsigned long *)__va(page))[address >> PAGE_SHIFT];
-				printk(" pte = %08lx\n", page);
-			}
-		}
-#endif
-		pr_debug("Sending SIGSEGV to PID %d...\n",
-			tsk->pid);
-		force_sig(SIGSEGV, tsk);
+		if (exception_trace)
+			printk("%s%s[%d]: segfault at %08lx pc %08lx "
+			       "sp %08lx ecr %lu\n",
+			       is_init(tsk) ? KERN_EMERG : KERN_INFO,
+			       tsk->comm, tsk->pid, address, regs->pc,
+			       regs->sp, ecr);
+		_exception(SIGSEGV, regs, code, address);
 		return;
 	}
 
 no_context:
-	pr_debug("No context\n");
-
 	/* Are we prepared to handle this kernel fault? */
 	fixup = search_exception_tables(regs->pc);
 	if (fixup) {
 		regs->pc = fixup->fixup;
-		pr_debug("Found fixup at %08lx\n", fixup->fixup);
 		return;
 	}
 
@@ -230,7 +199,6 @@
 		printk(KERN_ALERT
 		       "Unable to handle kernel paging request");
 	printk(" at virtual address %08lx\n", address);
-	printk(KERN_ALERT "pc = %08lx\n", regs->pc);
 
 	page = sysreg_read(PTBR);
 	printk(KERN_ALERT "ptbr = %08lx", page);
@@ -241,20 +209,20 @@
 			page &= PAGE_MASK;
 			address &= 0x003ff000;
 			page = ((unsigned long *)__va(page))[address >> PAGE_SHIFT];
-			printk(" pte = %08lx\n", page);
+			printk(" pte = %08lx", page);
 		}
 	}
-	die("\nOops", regs, ecr);
-	do_exit(SIGKILL);
+	printk("\n");
+	die("Kernel access of bad area", regs, signr);
+	return;
 
 	/*
 	 * We ran out of memory, or some other thing happened to us
 	 * that made us unable to handle the page fault gracefully.
 	 */
 out_of_memory:
-	printk("Out of memory\n");
 	up_read(&mm->mmap_sem);
-	if (current->pid == 1) {
+	if (is_init(current)) {
 		yield();
 		down_read(&mm->mmap_sem);
 		goto survive;
@@ -267,21 +235,20 @@
 do_sigbus:
 	up_read(&mm->mmap_sem);
 
-	/*
-	 * Send a sigbus, regardless of whether we were in kernel or
-	 * user mode.
-	 */
-	/* address, error_code, trap_no, ... */
-#ifdef DEBUG
-	show_regs(regs);
-	dump_code(regs->pc);
-#endif
-	pr_debug("Sending SIGBUS to PID %d...\n", tsk->pid);
-	force_sig(SIGBUS, tsk);
-
 	/* Kernel mode? Handle exceptions or die */
+	signr = SIGBUS;
+	code = BUS_ADRERR;
 	if (!user_mode(regs))
 		goto no_context;
+
+	if (exception_trace)
+		printk("%s%s[%d]: bus error at %08lx pc %08lx "
+		       "sp %08lx ecr %lu\n",
+		       is_init(tsk) ? KERN_EMERG : KERN_INFO,
+		       tsk->comm, tsk->pid, address, regs->pc,
+		       regs->sp, ecr);
+
+	_exception(SIGBUS, regs, BUS_ADRERR, address);
 }
 
 asmlinkage void do_bus_error(unsigned long addr, int write_access,
@@ -292,8 +259,7 @@
 	       addr, write_access ? "write" : "read");
 	printk(KERN_INFO "DTLB dump:\n");
 	dump_dtlb();
-	die("Bus Error", regs, write_access);
-	do_exit(SIGKILL);
+	die("Bus Error", regs, SIGKILL);
 }
 
 /*
diff --git a/arch/avr32/mm/init.c b/arch/avr32/mm/init.c
index 70da689..82cf708 100644
--- a/arch/avr32/mm/init.c
+++ b/arch/avr32/mm/init.c
@@ -10,11 +10,9 @@
 #include <linux/mm.h>
 #include <linux/swap.h>
 #include <linux/init.h>
-#include <linux/initrd.h>
 #include <linux/mmzone.h>
 #include <linux/bootmem.h>
 #include <linux/pagemap.h>
-#include <linux/pfn.h>
 #include <linux/nodemask.h>
 
 #include <asm/page.h>
@@ -78,242 +76,6 @@
 	printk ("%d pages swap cached\n", cached);
 }
 
-static void __init print_memory_map(const char *what,
-				    struct tag_mem_range *mem)
-{
-	printk ("%s:\n", what);
-	for (; mem; mem = mem->next) {
-		printk ("  %08lx - %08lx\n",
-			(unsigned long)mem->addr,
-			(unsigned long)(mem->addr + mem->size));
-	}
-}
-
-#define MAX_LOWMEM	HIGHMEM_START
-#define MAX_LOWMEM_PFN	PFN_DOWN(MAX_LOWMEM)
-
-/*
- * Sort a list of memory regions in-place by ascending address.
- *
- * We're using bubble sort because we only have singly linked lists
- * with few elements.
- */
-static void __init sort_mem_list(struct tag_mem_range **pmem)
-{
-	int done;
-	struct tag_mem_range **a, **b;
-
-	if (!*pmem)
-		return;
-
-	do {
-		done = 1;
-		a = pmem, b = &(*pmem)->next;
-		while (*b) {
-			if ((*a)->addr > (*b)->addr) {
-				struct tag_mem_range *tmp;
-				tmp = (*b)->next;
-				(*b)->next = *a;
-				*a = *b;
-				*b = tmp;
-				done = 0;
-			}
-			a = &(*a)->next;
-			b = &(*a)->next;
-		}
-	} while (!done);
-}
-
-/*
- * Find a free memory region large enough for storing the
- * bootmem bitmap.
- */
-static unsigned long __init
-find_bootmap_pfn(const struct tag_mem_range *mem)
-{
-	unsigned long bootmap_pages, bootmap_len;
-	unsigned long node_pages = PFN_UP(mem->size);
-	unsigned long bootmap_addr = mem->addr;
-	struct tag_mem_range *reserved = mem_reserved;
-	struct tag_mem_range *ramdisk = mem_ramdisk;
-	unsigned long kern_start = virt_to_phys(_stext);
-	unsigned long kern_end = virt_to_phys(_end);
-
-	bootmap_pages = bootmem_bootmap_pages(node_pages);
-	bootmap_len = bootmap_pages << PAGE_SHIFT;
-
-	/*
-	 * Find a large enough region without reserved pages for
-	 * storing the bootmem bitmap. We can take advantage of the
-	 * fact that all lists have been sorted.
-	 *
-	 * We have to check explicitly reserved regions as well as the
-	 * kernel image and any RAMDISK images...
-	 *
-	 * Oh, and we have to make sure we don't overwrite the taglist
-	 * since we're going to use it until the bootmem allocator is
-	 * fully up and running.
-	 */
-	while (1) {
-		if ((bootmap_addr < kern_end) &&
-		    ((bootmap_addr + bootmap_len) > kern_start))
-			bootmap_addr = kern_end;
-
-		while (reserved &&
-		       (bootmap_addr >= (reserved->addr + reserved->size)))
-			reserved = reserved->next;
-
-		if (reserved &&
-		    ((bootmap_addr + bootmap_len) >= reserved->addr)) {
-			bootmap_addr = reserved->addr + reserved->size;
-			continue;
-		}
-
-		while (ramdisk &&
-		       (bootmap_addr >= (ramdisk->addr + ramdisk->size)))
-			ramdisk = ramdisk->next;
-
-		if (!ramdisk ||
-		    ((bootmap_addr + bootmap_len) < ramdisk->addr))
-			break;
-
-		bootmap_addr = ramdisk->addr + ramdisk->size;
-	}
-
-	if ((PFN_UP(bootmap_addr) + bootmap_len) >= (mem->addr + mem->size))
-		return ~0UL;
-
-	return PFN_UP(bootmap_addr);
-}
-
-void __init setup_bootmem(void)
-{
-	unsigned bootmap_size;
-	unsigned long first_pfn, bootmap_pfn, pages;
-	unsigned long max_pfn, max_low_pfn;
-	unsigned long kern_start = virt_to_phys(_stext);
-	unsigned long kern_end = virt_to_phys(_end);
-	unsigned node = 0;
-	struct tag_mem_range *bank, *res;
-
-	sort_mem_list(&mem_phys);
-	sort_mem_list(&mem_reserved);
-
-	print_memory_map("Physical memory", mem_phys);
-	print_memory_map("Reserved memory", mem_reserved);
-
-	nodes_clear(node_online_map);
-
-	if (mem_ramdisk) {
-#ifdef CONFIG_BLK_DEV_INITRD
-		initrd_start = (unsigned long)__va(mem_ramdisk->addr);
-		initrd_end = initrd_start + mem_ramdisk->size;
-
-		print_memory_map("RAMDISK images", mem_ramdisk);
-		if (mem_ramdisk->next)
-			printk(KERN_WARNING
-			       "Warning: Only the first RAMDISK image "
-			       "will be used\n");
-		sort_mem_list(&mem_ramdisk);
-#else
-		printk(KERN_WARNING "RAM disk image present, but "
-		       "no initrd support in kernel!\n");
-#endif
-	}
-
-	if (mem_phys->next)
-		printk(KERN_WARNING "Only using first memory bank\n");
-
-	for (bank = mem_phys; bank; bank = NULL) {
-		first_pfn = PFN_UP(bank->addr);
-		max_low_pfn = max_pfn = PFN_DOWN(bank->addr + bank->size);
-		bootmap_pfn = find_bootmap_pfn(bank);
-		if (bootmap_pfn > max_pfn)
-			panic("No space for bootmem bitmap!\n");
-
-		if (max_low_pfn > MAX_LOWMEM_PFN) {
-			max_low_pfn = MAX_LOWMEM_PFN;
-#ifndef CONFIG_HIGHMEM
-			/*
-			 * Lowmem is memory that can be addressed
-			 * directly through P1/P2
-			 */
-			printk(KERN_WARNING
-			       "Node %u: Only %ld MiB of memory will be used.\n",
-			       node, MAX_LOWMEM >> 20);
-			printk(KERN_WARNING "Use a HIGHMEM enabled kernel.\n");
-#else
-#error HIGHMEM is not supported by AVR32 yet
-#endif
-		}
-
-		/* Initialize the boot-time allocator with low memory only. */
-		bootmap_size = init_bootmem_node(NODE_DATA(node), bootmap_pfn,
-						 first_pfn, max_low_pfn);
-
-		printk("Node %u: bdata = %p, bdata->node_bootmem_map = %p\n",
-		       node, NODE_DATA(node)->bdata,
-		       NODE_DATA(node)->bdata->node_bootmem_map);
-
-		/*
-		 * Register fully available RAM pages with the bootmem
-		 * allocator.
-		 */
-		pages = max_low_pfn - first_pfn;
-		free_bootmem_node (NODE_DATA(node), PFN_PHYS(first_pfn),
-				   PFN_PHYS(pages));
-
-		/*
-		 * Reserve space for the kernel image (if present in
-		 * this node)...
-		 */
-		if ((kern_start >= PFN_PHYS(first_pfn)) &&
-		    (kern_start < PFN_PHYS(max_pfn))) {
-			printk("Node %u: Kernel image %08lx - %08lx\n",
-			       node, kern_start, kern_end);
-			reserve_bootmem_node(NODE_DATA(node), kern_start,
-					     kern_end - kern_start);
-		}
-
-		/* ...the bootmem bitmap... */
-		reserve_bootmem_node(NODE_DATA(node),
-				     PFN_PHYS(bootmap_pfn),
-				     bootmap_size);
-
-		/* ...any RAMDISK images... */
-		for (res = mem_ramdisk; res; res = res->next) {
-			if (res->addr > PFN_PHYS(max_pfn))
-				break;
-
-			if (res->addr >= PFN_PHYS(first_pfn)) {
-				printk("Node %u: RAMDISK %08lx - %08lx\n",
-				       node,
-				       (unsigned long)res->addr,
-				       (unsigned long)(res->addr + res->size));
-				reserve_bootmem_node(NODE_DATA(node),
-						     res->addr, res->size);
-			}
-		}
-
-		/* ...and any other reserved regions. */
-		for (res = mem_reserved; res; res = res->next) {
-			if (res->addr > PFN_PHYS(max_pfn))
-				break;
-
-			if (res->addr >= PFN_PHYS(first_pfn)) {
-				printk("Node %u: Reserved %08lx - %08lx\n",
-				       node,
-				       (unsigned long)res->addr,
-				       (unsigned long)(res->addr + res->size));
-				reserve_bootmem_node(NODE_DATA(node),
-						     res->addr, res->size);
-			}
-		}
-
-		node_set_online(node);
-	}
-}
-
 /*
  * paging_init() sets up the page tables
  *
diff --git a/arch/i386/defconfig b/arch/i386/defconfig
index f4efd66..c96911c 100644
--- a/arch/i386/defconfig
+++ b/arch/i386/defconfig
@@ -692,7 +692,6 @@
 CONFIG_SATA_VIA=y
 # CONFIG_SATA_VITESSE is not set
 # CONFIG_SATA_INIC162X is not set
-CONFIG_SATA_INTEL_COMBINED=y
 CONFIG_SATA_ACPI=y
 # CONFIG_PATA_ALI is not set
 # CONFIG_PATA_AMD is not set
diff --git a/arch/i386/kernel/acpi/earlyquirk.c b/arch/i386/kernel/acpi/earlyquirk.c
index a7d22d9..8f7efd3 100644
--- a/arch/i386/kernel/acpi/earlyquirk.c
+++ b/arch/i386/kernel/acpi/earlyquirk.c
@@ -23,10 +23,13 @@
 static int __init check_bridge(int vendor, int device)
 {
 #ifdef CONFIG_ACPI
+	static int warned;
 	/* According to Nvidia all timer overrides are bogus unless HPET
 	   is enabled. */
 	if (!acpi_use_timer_override && vendor == PCI_VENDOR_ID_NVIDIA) {
-		if (acpi_table_parse(ACPI_SIG_HPET, nvidia_hpet_check)) {
+		if (!warned && acpi_table_parse(ACPI_SIG_HPET,
+						nvidia_hpet_check)) {
+			warned = 1;
 			acpi_skip_timer_override = 1;
 			  printk(KERN_INFO "Nvidia board "
                        "detected. Ignoring ACPI "
diff --git a/arch/ia64/hp/sim/simeth.c b/arch/ia64/hp/sim/simeth.c
index 424e925..f26077a 100644
--- a/arch/ia64/hp/sim/simeth.c
+++ b/arch/ia64/hp/sim/simeth.c
@@ -427,7 +427,6 @@
 		printk(KERN_NOTICE "%s: memory squeeze. dropping packet.\n", dev->name);
 		return NULL;
 	}
-	nskb->dev = dev;
 
 	skb_reserve(nskb, 2);	/* Align IP on 16 byte boundaries */
 
@@ -474,7 +473,7 @@
 		 * XXX Fix me
 		 * Should really do a csum+copy here
 		 */
-		memcpy(skb->data, frame, len);
+		skb_copy_to_linear_data(skb, frame, len);
 #endif
 		skb->protocol = eth_type_trans(skb, dev);
 
diff --git a/arch/ia64/lib/csum_partial_copy.c b/arch/ia64/lib/csum_partial_copy.c
index 503dfe6..118daf5 100644
--- a/arch/ia64/lib/csum_partial_copy.c
+++ b/arch/ia64/lib/csum_partial_copy.c
@@ -128,6 +128,8 @@
 	return (__force __wsum)result;
 }
 
+EXPORT_SYMBOL(csum_partial_copy_from_user);
+
 __wsum
 csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum)
 {
diff --git a/arch/ia64/sn/kernel/xpnet.c b/arch/ia64/sn/kernel/xpnet.c
index c8173db..5419acb 100644
--- a/arch/ia64/sn/kernel/xpnet.c
+++ b/arch/ia64/sn/kernel/xpnet.c
@@ -233,7 +233,7 @@
 			"%lu)\n", skb->data, &msg->data,
 			(size_t) msg->embedded_bytes);
 
-		memcpy(skb->data, &msg->data, (size_t) msg->embedded_bytes);
+		skb_copy_to_linear_data(skb, &msg->data, (size_t)msg->embedded_bytes);
 	} else {
 		dev_dbg(xpnet, "transferring buffer to the skb->data area;\n\t"
 			"bte_copy(0x%p, 0x%p, %hu)\n", (void *)msg->buf_pa,
@@ -264,17 +264,16 @@
 
 	dev_dbg(xpnet, "<skb->head=0x%p skb->data=0x%p skb->tail=0x%p "
 		"skb->end=0x%p skb->len=%d\n", (void *) skb->head,
-		(void *) skb->data, (void *) skb->tail, (void *) skb->end,
+		(void *)skb->data, skb_tail_pointer(skb), skb_end_pointer(skb),
 		skb->len);
 
-	skb->dev = xpnet_device;
 	skb->protocol = eth_type_trans(skb, xpnet_device);
 	skb->ip_summed = CHECKSUM_UNNECESSARY;
 
 	dev_dbg(xpnet, "passing skb to network layer; \n\tskb->head=0x%p "
 		"skb->data=0x%p skb->tail=0x%p skb->end=0x%p skb->len=%d\n",
-		(void *) skb->head, (void *) skb->data, (void *) skb->tail,
-		(void *) skb->end, skb->len);
+		(void *)skb->head, (void *)skb->data, skb_tail_pointer(skb),
+		skb_end_pointer(skb), skb->len);
 
 
 	xpnet_device->last_rx = jiffies;
@@ -476,7 +475,7 @@
 
 	dev_dbg(xpnet, ">skb->head=0x%p skb->data=0x%p skb->tail=0x%p "
 		"skb->end=0x%p skb->len=%d\n", (void *) skb->head,
-		(void *) skb->data, (void *) skb->tail, (void *) skb->end,
+		(void *)skb->data, skb_tail_pointer(skb), skb_end_pointer(skb),
 		skb->len);
 
 
@@ -498,7 +497,7 @@
 
 	/* get the beginning of the first cacheline and end of last */
 	start_addr = ((u64) skb->data & ~(L1_CACHE_BYTES - 1));
-	end_addr = L1_CACHE_ALIGN((u64) skb->tail);
+	end_addr = L1_CACHE_ALIGN((u64)skb_tail_pointer(skb));
 
 	/* calculate how many bytes to embed in the XPC message */
 	embedded_bytes = 0;
@@ -567,14 +566,15 @@
 			msg->version = XPNET_VERSION_EMBED;
 			dev_dbg(xpnet, "calling memcpy(0x%p, 0x%p, 0x%lx)\n",
 				&msg->data, skb->data, (size_t) embedded_bytes);
-			memcpy(&msg->data, skb->data, (size_t) embedded_bytes);
+			skb_copy_from_linear_data(skb, &msg->data,
+						  (size_t)embedded_bytes);
 		} else {
 			msg->version = XPNET_VERSION;
 		}
 		msg->magic = XPNET_MAGIC;
 		msg->size = end_addr - start_addr;
 		msg->leadin_ignore = (u64) skb->data - start_addr;
-		msg->tailout_ignore = end_addr - (u64) skb->tail;
+		msg->tailout_ignore = end_addr - (u64)skb_tail_pointer(skb);
 		msg->buf_pa = __pa(start_addr);
 
 		dev_dbg(xpnet, "sending XPC message to %d:%d\nmsg->buf_pa="
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index c78b143..130d825 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -10,7 +10,6 @@
 
 config ZONE_DMA
 	bool
-	default y
 
 choice
 	prompt "System type"
@@ -165,7 +164,7 @@
 	select HW_HAS_PCI
 	select I8259
 	select IRQ_CPU
-	select MIPS_GT64111
+	select PCI_GT64XXX_PCI0
 	select SYS_HAS_CPU_NEVADA
 	select SYS_HAS_EARLY_PRINTK
 	select SYS_SUPPORTS_32BIT_KERNEL
@@ -207,7 +206,7 @@
 	depends on EXPERIMENTAL
 	select DMA_NONCOHERENT
 	select HW_HAS_PCI
-	select MIPS_GT64120
+	select PCI_GT64XXX_PCI0
 	select SYS_HAS_CPU_R5000
 	select SYS_SUPPORTS_32BIT_KERNEL
 	select SYS_SUPPORTS_64BIT_KERNEL
@@ -245,7 +244,7 @@
 	select DMA_NONCOHERENT
 	select SYS_HAS_EARLY_PRINTK
 	select HW_HAS_PCI
-	select MIPS_GT64120
+	select PCI_GT64XXX_PCI0
 	select MIPS_NILE4
 	select R5000_CPU_SCACHE
 	select SYS_HAS_CPU_R5000
@@ -263,7 +262,7 @@
 	select HW_HAS_PCI
 	select MIPS_BOARDS_GEN
 	select MIPS_BONITO64
-	select MIPS_GT64120
+	select PCI_GT64XXX_PCI0
 	select MIPS_MSC
 	select RM7000_CPU_SCACHE
 	select SWAP_IO_SPACE
@@ -296,7 +295,7 @@
 	select MIPS_BOARDS_GEN
 	select MIPS_BONITO64
 	select MIPS_CPU_SCACHE
-	select MIPS_GT64120
+	select PCI_GT64XXX_PCI0
 	select MIPS_MSC
 	select SWAP_IO_SPACE
 	select SYS_HAS_CPU_MIPS32_R1
@@ -340,7 +339,7 @@
 	select BOOT_ELF32
 	select DMA_NONCOHERENT
 	select HW_HAS_PCI
-	select MIPS_GT64120
+	select PCI_GT64XXX_PCI0
 	select SWAP_IO_SPACE
 	select SYS_HAS_CPU_MIPS32_R1
 	select SYS_HAS_CPU_MIPS32_R2
@@ -398,7 +397,7 @@
 	select HW_HAS_PCI
 	select IRQ_CPU
 	select IRQ_CPU_RM7K
-	select MIPS_GT64120
+	select PCI_GT64XXX_PCI0
 	select RM7000_CPU_SCACHE
 	select SWAP_IO_SPACE
 	select SYS_HAS_CPU_RM7000
@@ -501,10 +500,8 @@
 	  ether port USB, AC97, PCI, etc.
 
 config MACH_VR41XX
-	bool "NEC VR41XX-based machines"
+	bool "NEC VR4100 series based machines"
 	select SYS_HAS_CPU_VR41XX
-	select SYS_SUPPORTS_32BIT_KERNEL
-	select SYS_SUPPORTS_64BIT_KERNEL if EXPERIMENTAL
 	select GENERIC_HARDIRQS_NO__DO_IRQ
 
 config PMC_YOSEMITE
@@ -779,6 +776,7 @@
 	select SYS_SUPPORTS_LITTLE_ENDIAN
 	select SYS_SUPPORTS_BIG_ENDIAN
 	select TOSHIBA_BOARDS
+	select GENERIC_HARDIRQS_NO__DO_IRQ
 
 config TOSHIBA_RBTX4927
 	bool "Toshiba TBTX49[23]7 board"
@@ -922,6 +920,7 @@
 
 config GENERIC_ISA_DMA
 	bool
+	select ZONE_DMA
 
 config I8259
 	bool
@@ -945,6 +944,7 @@
 
 config GENERIC_ISA_DMA_SUPPORT_BROKEN
 	bool
+	select ZONE_DMA
 
 #
 # Endianess selection.  Sufficiently obscure so many users don't know what to
@@ -999,10 +999,7 @@
 config MIPS_BOARDS_GEN
 	bool
 
-config MIPS_GT64111
-	bool
-
-config MIPS_GT64120
+config PCI_GT64XXX_PCI0
 	bool
 
 config MIPS_TX3927
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index 92bca6a..f2f742d 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -530,25 +530,29 @@
 load-$(CONFIG_SGI_IP32)		+= 0xffffffff80004000
 
 #
-# Sibyte SB1250 SOC
+# Sibyte SB1250/BCM1480 SOC
 #
 # This is a LIB so that it links at the end, and initcalls are later
 # the sequence; but it is built as an object so that modules don't get
 # removed (as happens, even if they have __initcall/module_init)
 #
 core-$(CONFIG_SIBYTE_BCM112X)	+= arch/mips/sibyte/sb1250/
+core-$(CONFIG_SIBYTE_BCM112X)	+= arch/mips/sibyte/common/
 cflags-$(CONFIG_SIBYTE_BCM112X)	+= -Iinclude/asm-mips/mach-sibyte \
 			-DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1250_112x_ALL
 
 core-$(CONFIG_SIBYTE_SB1250)	+= arch/mips/sibyte/sb1250/
+core-$(CONFIG_SIBYTE_SB1250)	+= arch/mips/sibyte/common/
 cflags-$(CONFIG_SIBYTE_SB1250)	+= -Iinclude/asm-mips/mach-sibyte \
 			-DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1250_112x_ALL
 
 core-$(CONFIG_SIBYTE_BCM1x55)	+= arch/mips/sibyte/bcm1480/
+core-$(CONFIG_SIBYTE_BCM1x55)	+= arch/mips/sibyte/common/
 cflags-$(CONFIG_SIBYTE_BCM1x55)	+= -Iinclude/asm-mips/mach-sibyte \
 			-DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1480_ALL
 
 core-$(CONFIG_SIBYTE_BCM1x80)	+= arch/mips/sibyte/bcm1480/
+core-$(CONFIG_SIBYTE_BCM1x80)	+= arch/mips/sibyte/common/
 cflags-$(CONFIG_SIBYTE_BCM1x80)	+= -Iinclude/asm-mips/mach-sibyte \
 			-DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1480_ALL
 
diff --git a/arch/mips/basler/excite/excite_setup.c b/arch/mips/basler/excite/excite_setup.c
index 42f0eda..2f0e4c0 100644
--- a/arch/mips/basler/excite/excite_setup.c
+++ b/arch/mips/basler/excite/excite_setup.c
@@ -63,7 +63,7 @@
 volatile void __iomem * const titan_base = (void *) (EXCITE_ADDR_TITAN);
 
 /* Protect access to shared GPI registers */
-spinlock_t titan_lock = SPIN_LOCK_UNLOCKED;
+DEFINE_SPINLOCK(titan_lock);
 int titan_irqflags;
 
 
diff --git a/arch/mips/cobalt/Makefile b/arch/mips/cobalt/Makefile
index b36dd8f..de017c1 100644
--- a/arch/mips/cobalt/Makefile
+++ b/arch/mips/cobalt/Makefile
@@ -4,5 +4,6 @@
 
 obj-y	 := irq.o reset.o setup.o
 
+obj-$(CONFIG_PCI)		+= pci.o
 obj-$(CONFIG_EARLY_PRINTK)	+= console.o
 obj-$(CONFIG_MTD_PHYSMAP)	+= mtd.o
diff --git a/arch/mips/cobalt/console.c b/arch/mips/cobalt/console.c
index ca56b41..0485d51 100644
--- a/arch/mips/cobalt/console.c
+++ b/arch/mips/cobalt/console.c
@@ -1,13 +1,11 @@
 /*
  * (C) P. Horton 2006
  */
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/console.h>
 #include <linux/serial_reg.h>
+
 #include <asm/addrspace.h>
-#include <asm/mach-cobalt/cobalt.h>
+
+#include <cobalt.h>
 
 void prom_putchar(char c)
 {
diff --git a/arch/mips/cobalt/irq.c b/arch/mips/cobalt/irq.c
index fe93b84..950ad1e 100644
--- a/arch/mips/cobalt/irq.c
+++ b/arch/mips/cobalt/irq.c
@@ -17,7 +17,7 @@
 #include <asm/irq_cpu.h>
 #include <asm/gt64120.h>
 
-#include <asm/mach-cobalt/cobalt.h>
+#include <cobalt.h>
 
 /*
  * We have two types of interrupts that we handle, ones that come in through
diff --git a/arch/mips/cobalt/pci.c b/arch/mips/cobalt/pci.c
new file mode 100644
index 0000000..d91027f
--- /dev/null
+++ b/arch/mips/cobalt/pci.c
@@ -0,0 +1,47 @@
+/*
+ * Register PCI controller.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1996, 1997, 2004, 05 by Ralf Baechle (ralf@linux-mips.org)
+ * Copyright (C) 2001, 2002, 2003 by Liam Davies (ldavies@agile.tv)
+ *
+ */
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include <asm/gt64120.h>
+
+extern struct pci_ops gt64xxx_pci0_ops;
+
+static struct resource cobalt_mem_resource = {
+	.start	= GT_DEF_PCI0_MEM0_BASE,
+	.end	= GT_DEF_PCI0_MEM0_BASE + GT_DEF_PCI0_MEM0_SIZE - 1,
+	.name	= "PCI memory",
+	.flags	= IORESOURCE_MEM,
+};
+
+static struct resource cobalt_io_resource = {
+	.start	= 0x1000,
+	.end	= GT_DEF_PCI0_IO_SIZE - 1,
+	.name	= "PCI I/O",
+	.flags	= IORESOURCE_IO,
+};
+
+static struct pci_controller cobalt_pci_controller = {
+	.pci_ops	= &gt64xxx_pci0_ops,
+	.mem_resource	= &cobalt_mem_resource,
+	.io_resource	= &cobalt_io_resource,
+	.io_offset	= 0 - GT_DEF_PCI0_IO_BASE,
+};
+
+static int __init cobalt_pci_init(void)
+{
+	register_pci_controller(&cobalt_pci_controller);
+
+	return 0;
+}
+
+arch_initcall(cobalt_pci_init);
diff --git a/arch/mips/cobalt/reset.c b/arch/mips/cobalt/reset.c
index 753dfcc..43cca21 100644
--- a/arch/mips/cobalt/reset.c
+++ b/arch/mips/cobalt/reset.c
@@ -8,15 +8,12 @@
  * Copyright (C) 1995, 1996, 1997 by Ralf Baechle
  * Copyright (C) 2001 by Liam Davies (ldavies@agile.tv)
  */
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <asm/cacheflush.h>
+#include <linux/jiffies.h>
+
 #include <asm/io.h>
-#include <asm/processor.h>
 #include <asm/reboot.h>
-#include <asm/system.h>
-#include <asm/mipsregs.h>
-#include <asm/mach-cobalt/cobalt.h>
+
+#include <cobalt.h>
 
 void cobalt_machine_halt(void)
 {
diff --git a/arch/mips/cobalt/setup.c b/arch/mips/cobalt/setup.c
index 88d34f1..d0dd817 100644
--- a/arch/mips/cobalt/setup.c
+++ b/arch/mips/cobalt/setup.c
@@ -19,12 +19,10 @@
 #include <asm/bootinfo.h>
 #include <asm/time.h>
 #include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/processor.h>
 #include <asm/reboot.h>
 #include <asm/gt64120.h>
 
-#include <asm/mach-cobalt/cobalt.h>
+#include <cobalt.h>
 
 extern void cobalt_machine_restart(char *command);
 extern void cobalt_machine_halt(void);
@@ -63,22 +61,6 @@
 	GT_WRITE(GT_INTRMASK_OFS, GT_INTR_T0EXP_MSK | GT_READ(GT_INTRMASK_OFS));
 }
 
-extern struct pci_ops gt64111_pci_ops;
-
-static struct resource cobalt_mem_resource = {
-	.start	= GT_DEF_PCI0_MEM0_BASE,
-	.end	= GT_DEF_PCI0_MEM0_BASE + GT_DEF_PCI0_MEM0_SIZE - 1,
-	.name	= "PCI memory",
-	.flags	= IORESOURCE_MEM
-};
-
-static struct resource cobalt_io_resource = {
-	.start	= 0x1000,
-	.end	= 0xffff,
-	.name	= "PCI I/O",
-	.flags	= IORESOURCE_IO
-};
-
 /*
  * Cobalt doesn't have PS/2 keyboard/mouse interfaces,
  * keyboard conntroller is never used.
@@ -111,14 +93,6 @@
 	},
 };
 
-static struct pci_controller cobalt_pci_controller = {
-	.pci_ops	= &gt64111_pci_ops,
-	.mem_resource	= &cobalt_mem_resource,
-	.mem_offset	= 0,
-	.io_resource	= &cobalt_io_resource,
-	.io_offset	= 0 - GT_DEF_PCI0_IO_BASE,
-};
-
 void __init plat_mem_setup(void)
 {
 	static struct uart_port uart;
@@ -146,10 +120,6 @@
 
 	printk("Cobalt board ID: %d\n", cobalt_board_id);
 
-#ifdef CONFIG_PCI
-	register_pci_controller(&cobalt_pci_controller);
-#endif
-
 	if (cobalt_board_id > COBALT_BRD_ID_RAQ1) {
 #ifdef CONFIG_SERIAL_8250
 		uart.line	= 0;
diff --git a/arch/mips/configs/jmr3927_defconfig b/arch/mips/configs/jmr3927_defconfig
index 21a0947..068e48e 100644
--- a/arch/mips/configs/jmr3927_defconfig
+++ b/arch/mips/configs/jmr3927_defconfig
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.20
-# Tue Feb 20 21:47:34 2007
+# Linux kernel version: 2.6.21-rc3
+# Thu Mar 15 00:40:40 2007
 #
 CONFIG_MIPS=y
 
@@ -70,7 +70,7 @@
 CONFIG_GENERIC_CALIBRATE_DELAY=y
 CONFIG_GENERIC_TIME=y
 CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y
-# CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ is not set
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
 CONFIG_DMA_NONCOHERENT=y
 CONFIG_DMA_NEED_PCI_MAP_STATE=y
 CONFIG_CPU_BIG_ENDIAN=y
@@ -138,12 +138,12 @@
 # CONFIG_HZ_48 is not set
 # CONFIG_HZ_100 is not set
 # CONFIG_HZ_128 is not set
-# CONFIG_HZ_250 is not set
+CONFIG_HZ_250=y
 # CONFIG_HZ_256 is not set
-CONFIG_HZ_1000=y
+# CONFIG_HZ_1000 is not set
 # CONFIG_HZ_1024 is not set
 CONFIG_SYS_SUPPORTS_ARBIT_HZ=y
-CONFIG_HZ=1000
+CONFIG_HZ=250
 CONFIG_PREEMPT_NONE=y
 # CONFIG_PREEMPT_VOLUNTARY is not set
 # CONFIG_PREEMPT is not set
@@ -175,14 +175,15 @@
 # CONFIG_AUDIT is not set
 # CONFIG_IKCONFIG is not set
 CONFIG_SYSFS_DEPRECATED=y
-CONFIG_RELAY=y
+# CONFIG_RELAY is not set
+# CONFIG_BLK_DEV_INITRD is not set
 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
 CONFIG_SYSCTL=y
 CONFIG_EMBEDDED=y
 CONFIG_SYSCTL_SYSCALL=y
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
-CONFIG_HOTPLUG=y
+# CONFIG_HOTPLUG is not set
 CONFIG_PRINTK=y
 CONFIG_BUG=y
 CONFIG_ELF_CORE=y
@@ -217,11 +218,11 @@
 CONFIG_IOSCHED_AS=y
 CONFIG_IOSCHED_DEADLINE=y
 CONFIG_IOSCHED_CFQ=y
-CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_AS is not set
 # CONFIG_DEFAULT_DEADLINE is not set
-# CONFIG_DEFAULT_CFQ is not set
+CONFIG_DEFAULT_CFQ=y
 # CONFIG_DEFAULT_NOOP is not set
-CONFIG_DEFAULT_IOSCHED="anticipatory"
+CONFIG_DEFAULT_IOSCHED="cfq"
 
 #
 # Bus options (PCI, PCMCIA, EISA, ISA, TC)
@@ -233,12 +234,10 @@
 #
 # PCCARD (PCMCIA/CardBus) support
 #
-# CONFIG_PCCARD is not set
 
 #
 # PCI Hotplug Support
 #
-# CONFIG_HOTPLUG_PCI is not set
 
 #
 # Executable file formats
@@ -250,10 +249,7 @@
 #
 # Power management options
 #
-CONFIG_PM=y
-# CONFIG_PM_LEGACY is not set
-# CONFIG_PM_DEBUG is not set
-# CONFIG_PM_SYSFS_DEPRECATED is not set
+# CONFIG_PM is not set
 
 #
 # Networking
@@ -267,12 +263,7 @@
 CONFIG_PACKET=y
 # CONFIG_PACKET_MMAP is not set
 CONFIG_UNIX=y
-CONFIG_XFRM=y
-CONFIG_XFRM_USER=y
-# CONFIG_XFRM_SUB_POLICY is not set
-CONFIG_XFRM_MIGRATE=y
-CONFIG_NET_KEY=y
-CONFIG_NET_KEY_MIGRATE=y
+# CONFIG_NET_KEY is not set
 CONFIG_INET=y
 # CONFIG_IP_MULTICAST is not set
 # CONFIG_IP_ADVANCED_ROUTER is not set
@@ -290,19 +281,18 @@
 # CONFIG_INET_IPCOMP is not set
 # CONFIG_INET_XFRM_TUNNEL is not set
 # CONFIG_INET_TUNNEL is not set
-CONFIG_INET_XFRM_MODE_TRANSPORT=y
-CONFIG_INET_XFRM_MODE_TUNNEL=y
-CONFIG_INET_XFRM_MODE_BEET=y
-CONFIG_INET_DIAG=y
-CONFIG_INET_TCP_DIAG=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_DIAG is not set
 # CONFIG_TCP_CONG_ADVANCED is not set
 CONFIG_TCP_CONG_CUBIC=y
 CONFIG_DEFAULT_TCP_CONG="cubic"
-CONFIG_TCP_MD5SIG=y
+# CONFIG_TCP_MD5SIG is not set
 # CONFIG_IPV6 is not set
 # CONFIG_INET6_XFRM_TUNNEL is not set
 # CONFIG_INET6_TUNNEL is not set
-CONFIG_NETWORK_SECMARK=y
+# CONFIG_NETWORK_SECMARK is not set
 # CONFIG_NETFILTER is not set
 
 #
@@ -343,13 +333,7 @@
 # CONFIG_HAMRADIO is not set
 # CONFIG_IRDA is not set
 # CONFIG_BT is not set
-CONFIG_IEEE80211=y
-# CONFIG_IEEE80211_DEBUG is not set
-CONFIG_IEEE80211_CRYPT_WEP=y
-CONFIG_IEEE80211_CRYPT_CCMP=y
-CONFIG_IEEE80211_SOFTMAC=y
-# CONFIG_IEEE80211_SOFTMAC_DEBUG is not set
-CONFIG_WIRELESS_EXT=y
+# CONFIG_IEEE80211 is not set
 
 #
 # Device Drivers
@@ -360,14 +344,12 @@
 #
 CONFIG_STANDALONE=y
 CONFIG_PREVENT_FIRMWARE_BUILD=y
-CONFIG_FW_LOADER=y
 # CONFIG_SYS_HYPERVISOR is not set
 
 #
 # Connector - unified userspace <-> kernelspace linker
 #
-CONFIG_CONNECTOR=y
-CONFIG_PROC_EVENTS=y
+# CONFIG_CONNECTOR is not set
 
 #
 # Memory Technology Devices (MTD)
@@ -396,16 +378,13 @@
 # CONFIG_BLK_DEV_NBD is not set
 # CONFIG_BLK_DEV_SX8 is not set
 # CONFIG_BLK_DEV_RAM is not set
-# CONFIG_BLK_DEV_INITRD is not set
-CONFIG_CDROM_PKTCDVD=y
-CONFIG_CDROM_PKTCDVD_BUFFERS=8
-# CONFIG_CDROM_PKTCDVD_WCACHE is not set
-CONFIG_ATA_OVER_ETH=y
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
 
 #
 # Misc devices
 #
-CONFIG_SGI_IOC4=y
+# CONFIG_SGI_IOC4 is not set
 # CONFIG_TIFM_CORE is not set
 
 #
@@ -416,7 +395,7 @@
 #
 # SCSI device support
 #
-CONFIG_RAID_ATTRS=y
+# CONFIG_RAID_ATTRS is not set
 # CONFIG_SCSI is not set
 # CONFIG_SCSI_NETLINK is not set
 
@@ -462,26 +441,13 @@
 #
 # PHY device support
 #
-CONFIG_PHYLIB=y
-
-#
-# MII PHY device drivers
-#
-CONFIG_MARVELL_PHY=y
-CONFIG_DAVICOM_PHY=y
-CONFIG_QSEMI_PHY=y
-CONFIG_LXT_PHY=y
-CONFIG_CICADA_PHY=y
-CONFIG_VITESSE_PHY=y
-CONFIG_SMSC_PHY=y
-# CONFIG_BROADCOM_PHY is not set
-# CONFIG_FIXED_PHY is not set
+# CONFIG_PHYLIB is not set
 
 #
 # Ethernet (10 or 100Mbit)
 #
 CONFIG_NET_ETHERNET=y
-# CONFIG_MII is not set
+CONFIG_MII=y
 # CONFIG_HAPPYMEAL is not set
 # CONFIG_SUNGEM is not set
 # CONFIG_CASSINI is not set
@@ -493,7 +459,27 @@
 #
 # CONFIG_NET_TULIP is not set
 # CONFIG_HP100 is not set
-# CONFIG_NET_PCI is not set
+CONFIG_NET_PCI=y
+# CONFIG_PCNET32 is not set
+# CONFIG_AMD8111_ETH is not set
+# CONFIG_ADAPTEC_STARFIRE is not set
+# CONFIG_B44 is not set
+# CONFIG_FORCEDETH is not set
+CONFIG_TC35815=y
+# CONFIG_DGRS is not set
+# CONFIG_EEPRO100 is not set
+# CONFIG_E100 is not set
+# CONFIG_FEALNX is not set
+# CONFIG_NATSEMI is not set
+# CONFIG_NE2K_PCI is not set
+# CONFIG_8139CP is not set
+# CONFIG_8139TOO is not set
+# CONFIG_SIS900 is not set
+# CONFIG_EPIC100 is not set
+# CONFIG_SUNDANCE is not set
+# CONFIG_TLAN is not set
+# CONFIG_VIA_RHINE is not set
+# CONFIG_SC92031 is not set
 
 #
 # Ethernet (1000 Mbit)
@@ -509,20 +495,21 @@
 # CONFIG_SKGE is not set
 # CONFIG_SKY2 is not set
 # CONFIG_SK98LIN is not set
+# CONFIG_VIA_VELOCITY is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_BNX2 is not set
-CONFIG_QLA3XXX=y
+# CONFIG_QLA3XXX is not set
 # CONFIG_ATL1 is not set
 
 #
 # Ethernet (10000 Mbit)
 #
 # CONFIG_CHELSIO_T1 is not set
-CONFIG_CHELSIO_T3=y
+# CONFIG_CHELSIO_T3 is not set
 # CONFIG_IXGB is not set
 # CONFIG_S2IO is not set
 # CONFIG_MYRI10GE is not set
-CONFIG_NETXEN_NIC=y
+# CONFIG_NETXEN_NIC is not set
 
 #
 # Token Ring devices
@@ -566,10 +553,7 @@
 #
 # Userland interfaces
 #
-CONFIG_INPUT_MOUSEDEV=y
-CONFIG_INPUT_MOUSEDEV_PSAUX=y
-CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
-CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_MOUSEDEV is not set
 # CONFIG_INPUT_JOYDEV is not set
 # CONFIG_INPUT_TSDEV is not set
 # CONFIG_INPUT_EVDEV is not set
@@ -587,21 +571,13 @@
 #
 # Hardware I/O ports
 #
-CONFIG_SERIO=y
-# CONFIG_SERIO_I8042 is not set
-CONFIG_SERIO_SERPORT=y
-# CONFIG_SERIO_PCIPS2 is not set
-# CONFIG_SERIO_LIBPS2 is not set
-CONFIG_SERIO_RAW=y
+# CONFIG_SERIO is not set
 # CONFIG_GAMEPORT is not set
 
 #
 # Character devices
 #
-CONFIG_VT=y
-CONFIG_VT_CONSOLE=y
-CONFIG_HW_CONSOLE=y
-CONFIG_VT_HW_CONSOLE_BINDING=y
+# CONFIG_VT is not set
 CONFIG_SERIAL_NONSTANDARD=y
 # CONFIG_COMPUTONE is not set
 # CONFIG_ROCKETPORT is not set
@@ -609,7 +585,7 @@
 # CONFIG_DIGIEPCA is not set
 # CONFIG_MOXA_INTELLIO is not set
 # CONFIG_MOXA_SMARTIO is not set
-CONFIG_MOXA_SMARTIO_NEW=y
+# CONFIG_MOXA_SMARTIO_NEW is not set
 # CONFIG_ISI is not set
 # CONFIG_SYNCLINKMP is not set
 # CONFIG_SYNCLINK_GT is not set
@@ -629,11 +605,12 @@
 # Non-8250 serial port support
 #
 CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
 CONFIG_SERIAL_TXX9=y
 CONFIG_HAS_TXX9_SERIAL=y
 CONFIG_SERIAL_TXX9_NR_UARTS=6
-# CONFIG_SERIAL_TXX9_CONSOLE is not set
-# CONFIG_SERIAL_TXX9_STDSERIAL is not set
+CONFIG_SERIAL_TXX9_CONSOLE=y
+CONFIG_SERIAL_TXX9_STDSERIAL=y
 # CONFIG_SERIAL_JSM is not set
 # CONFIG_UNIX98_PTYS is not set
 CONFIG_LEGACY_PTYS=y
@@ -685,6 +662,11 @@
 # CONFIG_HWMON_VID is not set
 
 #
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+
+#
 # Multimedia devices
 #
 # CONFIG_VIDEO_DEV is not set
@@ -697,51 +679,8 @@
 #
 # Graphics support
 #
-# CONFIG_FIRMWARE_EDID is not set
-CONFIG_FB=y
-# CONFIG_FB_CFB_FILLRECT is not set
-# CONFIG_FB_CFB_COPYAREA is not set
-# CONFIG_FB_CFB_IMAGEBLIT is not set
-# CONFIG_FB_SVGALIB is not set
-# CONFIG_FB_MACMODES is not set
-# CONFIG_FB_BACKLIGHT is not set
-# CONFIG_FB_MODE_HELPERS is not set
-# CONFIG_FB_TILEBLITTING is not set
-# CONFIG_FB_CIRRUS is not set
-# CONFIG_FB_PM2 is not set
-# CONFIG_FB_CYBER2000 is not set
-# CONFIG_FB_ASILIANT is not set
-# CONFIG_FB_IMSTT is not set
-# CONFIG_FB_S1D13XXX is not set
-# CONFIG_FB_NVIDIA is not set
-# CONFIG_FB_RIVA is not set
-# CONFIG_FB_MATROX is not set
-# CONFIG_FB_RADEON is not set
-# CONFIG_FB_ATY128 is not set
-# CONFIG_FB_ATY is not set
-# CONFIG_FB_S3 is not set
-# CONFIG_FB_SAVAGE is not set
-# CONFIG_FB_SIS is not set
-# CONFIG_FB_NEOMAGIC is not set
-# CONFIG_FB_KYRO is not set
-# CONFIG_FB_3DFX is not set
-# CONFIG_FB_VOODOO1 is not set
-# CONFIG_FB_SMIVGX is not set
-# CONFIG_FB_TRIDENT is not set
-# CONFIG_FB_VIRTUAL is not set
-
-#
-# Console display driver support
-#
-# CONFIG_VGA_CONSOLE is not set
-CONFIG_DUMMY_CONSOLE=y
-# CONFIG_FRAMEBUFFER_CONSOLE is not set
-
-#
-# Logo configuration
-#
-# CONFIG_LOGO is not set
 # CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+# CONFIG_FB is not set
 
 #
 # Sound
@@ -864,7 +803,7 @@
 CONFIG_DNOTIFY=y
 # CONFIG_AUTOFS_FS is not set
 # CONFIG_AUTOFS4_FS is not set
-CONFIG_FUSE_FS=y
+# CONFIG_FUSE_FS is not set
 
 #
 # CD-ROM/DVD Filesystems
@@ -889,14 +828,13 @@
 # CONFIG_TMPFS is not set
 # CONFIG_HUGETLB_PAGE is not set
 CONFIG_RAMFS=y
-CONFIG_CONFIGFS_FS=y
+# CONFIG_CONFIGFS_FS is not set
 
 #
 # Miscellaneous filesystems
 #
 # CONFIG_ADFS_FS is not set
 # CONFIG_AFFS_FS is not set
-# CONFIG_ECRYPT_FS is not set
 # CONFIG_HFS_FS is not set
 # CONFIG_HFSPLUS_FS is not set
 # CONFIG_BEFS_FS is not set
@@ -944,10 +882,7 @@
 #
 # Distributed Lock Manager
 #
-CONFIG_DLM=y
-CONFIG_DLM_TCP=y
-# CONFIG_DLM_SCTP is not set
-# CONFIG_DLM_DEBUG is not set
+# CONFIG_DLM is not set
 
 #
 # Profiling support
@@ -972,65 +907,22 @@
 #
 # Security options
 #
-CONFIG_KEYS=y
-CONFIG_KEYS_DEBUG_PROC_KEYS=y
+# CONFIG_KEYS is not set
 # CONFIG_SECURITY is not set
 
 #
 # Cryptographic options
 #
-CONFIG_CRYPTO=y
-CONFIG_CRYPTO_ALGAPI=y
-CONFIG_CRYPTO_BLKCIPHER=y
-CONFIG_CRYPTO_HASH=y
-CONFIG_CRYPTO_MANAGER=y
-CONFIG_CRYPTO_HMAC=y
-CONFIG_CRYPTO_XCBC=y
-CONFIG_CRYPTO_NULL=y
-CONFIG_CRYPTO_MD4=y
-CONFIG_CRYPTO_MD5=y
-CONFIG_CRYPTO_SHA1=y
-CONFIG_CRYPTO_SHA256=y
-CONFIG_CRYPTO_SHA512=y
-CONFIG_CRYPTO_WP512=y
-CONFIG_CRYPTO_TGR192=y
-CONFIG_CRYPTO_GF128MUL=y
-CONFIG_CRYPTO_ECB=y
-CONFIG_CRYPTO_CBC=y
-CONFIG_CRYPTO_PCBC=y
-CONFIG_CRYPTO_LRW=y
-CONFIG_CRYPTO_DES=y
-CONFIG_CRYPTO_FCRYPT=y
-CONFIG_CRYPTO_BLOWFISH=y
-CONFIG_CRYPTO_TWOFISH=y
-CONFIG_CRYPTO_TWOFISH_COMMON=y
-CONFIG_CRYPTO_SERPENT=y
-CONFIG_CRYPTO_AES=y
-CONFIG_CRYPTO_CAST5=y
-CONFIG_CRYPTO_CAST6=y
-CONFIG_CRYPTO_TEA=y
-CONFIG_CRYPTO_ARC4=y
-CONFIG_CRYPTO_KHAZAD=y
-CONFIG_CRYPTO_ANUBIS=y
-CONFIG_CRYPTO_DEFLATE=y
-CONFIG_CRYPTO_MICHAEL_MIC=y
-CONFIG_CRYPTO_CRC32C=y
-CONFIG_CRYPTO_CAMELLIA=y
-
-#
-# Hardware crypto devices
-#
+# CONFIG_CRYPTO is not set
 
 #
 # Library routines
 #
 CONFIG_BITREVERSE=y
 # CONFIG_CRC_CCITT is not set
-CONFIG_CRC16=y
+# CONFIG_CRC16 is not set
 CONFIG_CRC32=y
-CONFIG_LIBCRC32C=y
-CONFIG_ZLIB_INFLATE=y
-CONFIG_ZLIB_DEFLATE=y
+# CONFIG_LIBCRC32C is not set
 CONFIG_PLIST=y
 CONFIG_HAS_IOMEM=y
 CONFIG_HAS_IOPORT=y
diff --git a/arch/mips/configs/pnx8550-v2pci_defconfig b/arch/mips/configs/pnx8550-v2pci_defconfig
deleted file mode 100644
index 3d6c2d7..0000000
--- a/arch/mips/configs/pnx8550-v2pci_defconfig
+++ /dev/null
@@ -1,1540 +0,0 @@
-#
-# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.20
-# Tue Feb 20 21:47:39 2007
-#
-CONFIG_MIPS=y
-
-#
-# Machine selection
-#
-CONFIG_ZONE_DMA=y
-# CONFIG_MIPS_MTX1 is not set
-# CONFIG_MIPS_BOSPORUS is not set
-# CONFIG_MIPS_PB1000 is not set
-# CONFIG_MIPS_PB1100 is not set
-# CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_PB1550 is not set
-# CONFIG_MIPS_PB1200 is not set
-# CONFIG_MIPS_DB1000 is not set
-# CONFIG_MIPS_DB1100 is not set
-# CONFIG_MIPS_DB1500 is not set
-# CONFIG_MIPS_DB1550 is not set
-# CONFIG_MIPS_DB1200 is not set
-# CONFIG_MIPS_MIRAGE is not set
-# CONFIG_BASLER_EXCITE is not set
-# CONFIG_MIPS_COBALT is not set
-# CONFIG_MACH_DECSTATION is not set
-# CONFIG_MIPS_EV64120 is not set
-# CONFIG_MACH_JAZZ is not set
-# CONFIG_LASAT is not set
-# CONFIG_MIPS_ATLAS is not set
-# CONFIG_MIPS_MALTA is not set
-# CONFIG_MIPS_SEAD is not set
-# CONFIG_WR_PPMC is not set
-# CONFIG_MIPS_SIM is not set
-# CONFIG_MOMENCO_JAGUAR_ATX is not set
-# CONFIG_MOMENCO_OCELOT is not set
-# CONFIG_MOMENCO_OCELOT_3 is not set
-# CONFIG_MOMENCO_OCELOT_C is not set
-# CONFIG_MOMENCO_OCELOT_G is not set
-# CONFIG_MIPS_XXS1500 is not set
-# CONFIG_PNX8550_JBS is not set
-# CONFIG_PNX8550_STB810 is not set
-# CONFIG_DDB5477 is not set
-# CONFIG_MACH_VR41XX is not set
-# CONFIG_PMC_YOSEMITE is not set
-# CONFIG_QEMU is not set
-# CONFIG_MARKEINS is not set
-# CONFIG_SGI_IP22 is not set
-# CONFIG_SGI_IP27 is not set
-# CONFIG_SGI_IP32 is not set
-# CONFIG_SIBYTE_BIGSUR is not set
-# CONFIG_SIBYTE_SWARM is not set
-# CONFIG_SIBYTE_SENTOSA is not set
-# CONFIG_SIBYTE_RHONE is not set
-# CONFIG_SIBYTE_CARMEL is not set
-# CONFIG_SIBYTE_PTSWARM is not set
-# CONFIG_SIBYTE_LITTLESUR is not set
-# CONFIG_SIBYTE_CRHINE is not set
-# CONFIG_SIBYTE_CRHONE is not set
-# CONFIG_SNI_RM is not set
-# CONFIG_TOSHIBA_JMR3927 is not set
-# CONFIG_TOSHIBA_RBTX4927 is not set
-# CONFIG_TOSHIBA_RBTX4938 is not set
-CONFIG_RWSEM_GENERIC_SPINLOCK=y
-# CONFIG_ARCH_HAS_ILOG2_U32 is not set
-# CONFIG_ARCH_HAS_ILOG2_U64 is not set
-CONFIG_GENERIC_FIND_NEXT_BIT=y
-CONFIG_GENERIC_HWEIGHT=y
-CONFIG_GENERIC_CALIBRATE_DELAY=y
-CONFIG_GENERIC_TIME=y
-CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y
-CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
-CONFIG_DMA_NONCOHERENT=y
-CONFIG_DMA_NEED_PCI_MAP_STATE=y
-# CONFIG_CPU_BIG_ENDIAN is not set
-CONFIG_CPU_LITTLE_ENDIAN=y
-CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y
-CONFIG_PNX8550=y
-CONFIG_SOC_PNX8550=y
-CONFIG_MIPS_L1_CACHE_SHIFT=5
-
-#
-# CPU selection
-#
-CONFIG_CPU_MIPS32_R1=y
-# CONFIG_CPU_MIPS32_R2 is not set
-# CONFIG_CPU_MIPS64_R1 is not set
-# CONFIG_CPU_MIPS64_R2 is not set
-# CONFIG_CPU_R3000 is not set
-# CONFIG_CPU_TX39XX is not set
-# CONFIG_CPU_VR41XX is not set
-# CONFIG_CPU_R4300 is not set
-# CONFIG_CPU_R4X00 is not set
-# CONFIG_CPU_TX49XX is not set
-# CONFIG_CPU_R5000 is not set
-# CONFIG_CPU_R5432 is not set
-# CONFIG_CPU_R6000 is not set
-# CONFIG_CPU_NEVADA is not set
-# CONFIG_CPU_R8000 is not set
-# CONFIG_CPU_R10000 is not set
-# CONFIG_CPU_RM7000 is not set
-# CONFIG_CPU_RM9000 is not set
-# CONFIG_CPU_SB1 is not set
-CONFIG_SYS_HAS_CPU_MIPS32_R1=y
-CONFIG_CPU_MIPS32=y
-CONFIG_CPU_MIPSR1=y
-CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y
-CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y
-
-#
-# Kernel type
-#
-CONFIG_32BIT=y
-# CONFIG_64BIT is not set
-CONFIG_PAGE_SIZE_4KB=y
-# CONFIG_PAGE_SIZE_8KB is not set
-# CONFIG_PAGE_SIZE_16KB is not set
-# CONFIG_PAGE_SIZE_64KB is not set
-CONFIG_CPU_HAS_PREFETCH=y
-CONFIG_MIPS_MT_DISABLED=y
-# CONFIG_MIPS_MT_SMP is not set
-# CONFIG_MIPS_MT_SMTC is not set
-# CONFIG_MIPS_VPE_LOADER is not set
-# CONFIG_64BIT_PHYS_ADDR is not set
-CONFIG_CPU_HAS_LLSC=y
-CONFIG_CPU_HAS_SYNC=y
-CONFIG_GENERIC_HARDIRQS=y
-CONFIG_GENERIC_IRQ_PROBE=y
-CONFIG_CPU_SUPPORTS_HIGHMEM=y
-CONFIG_ARCH_FLATMEM_ENABLE=y
-CONFIG_SELECT_MEMORY_MODEL=y
-CONFIG_FLATMEM_MANUAL=y
-# CONFIG_DISCONTIGMEM_MANUAL is not set
-# CONFIG_SPARSEMEM_MANUAL is not set
-CONFIG_FLATMEM=y
-CONFIG_FLAT_NODE_MEM_MAP=y
-# CONFIG_SPARSEMEM_STATIC is not set
-CONFIG_SPLIT_PTLOCK_CPUS=4
-# CONFIG_RESOURCES_64BIT is not set
-CONFIG_ZONE_DMA_FLAG=1
-# CONFIG_HZ_48 is not set
-# CONFIG_HZ_100 is not set
-# CONFIG_HZ_128 is not set
-CONFIG_HZ_250=y
-# CONFIG_HZ_256 is not set
-# CONFIG_HZ_1000 is not set
-# CONFIG_HZ_1024 is not set
-CONFIG_SYS_SUPPORTS_ARBIT_HZ=y
-CONFIG_HZ=250
-CONFIG_PREEMPT_NONE=y
-# CONFIG_PREEMPT_VOLUNTARY is not set
-# CONFIG_PREEMPT is not set
-# CONFIG_KEXEC is not set
-CONFIG_LOCKDEP_SUPPORT=y
-CONFIG_STACKTRACE_SUPPORT=y
-CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
-
-#
-# Code maturity level options
-#
-CONFIG_EXPERIMENTAL=y
-CONFIG_BROKEN_ON_SMP=y
-CONFIG_INIT_ENV_ARG_LIMIT=32
-
-#
-# General setup
-#
-CONFIG_LOCALVERSION=""
-CONFIG_LOCALVERSION_AUTO=y
-CONFIG_SWAP=y
-CONFIG_SYSVIPC=y
-# CONFIG_IPC_NS is not set
-CONFIG_SYSVIPC_SYSCTL=y
-# CONFIG_POSIX_MQUEUE is not set
-# CONFIG_BSD_PROCESS_ACCT is not set
-# CONFIG_TASKSTATS is not set
-# CONFIG_UTS_NS is not set
-# CONFIG_AUDIT is not set
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_SYSFS_DEPRECATED=y
-# CONFIG_RELAY is not set
-CONFIG_INITRAMFS_SOURCE=""
-# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
-CONFIG_SYSCTL=y
-CONFIG_EMBEDDED=y
-# CONFIG_SYSCTL_SYSCALL is not set
-CONFIG_KALLSYMS=y
-# CONFIG_KALLSYMS_EXTRA_PASS is not set
-CONFIG_HOTPLUG=y
-CONFIG_PRINTK=y
-CONFIG_BUG=y
-CONFIG_ELF_CORE=y
-CONFIG_BASE_FULL=y
-CONFIG_FUTEX=y
-CONFIG_EPOLL=y
-CONFIG_SHMEM=y
-CONFIG_SLAB=y
-CONFIG_VM_EVENT_COUNTERS=y
-CONFIG_RT_MUTEXES=y
-# CONFIG_TINY_SHMEM is not set
-CONFIG_BASE_SMALL=0
-# CONFIG_SLOB is not set
-
-#
-# Loadable module support
-#
-CONFIG_MODULES=y
-# CONFIG_MODULE_UNLOAD is not set
-# CONFIG_MODVERSIONS is not set
-# CONFIG_MODULE_SRCVERSION_ALL is not set
-CONFIG_KMOD=y
-
-#
-# Block layer
-#
-CONFIG_BLOCK=y
-# CONFIG_LBD is not set
-# CONFIG_BLK_DEV_IO_TRACE is not set
-# CONFIG_LSF is not set
-
-#
-# IO Schedulers
-#
-CONFIG_IOSCHED_NOOP=y
-CONFIG_IOSCHED_AS=y
-CONFIG_IOSCHED_DEADLINE=y
-CONFIG_IOSCHED_CFQ=y
-CONFIG_DEFAULT_AS=y
-# CONFIG_DEFAULT_DEADLINE is not set
-# CONFIG_DEFAULT_CFQ is not set
-# CONFIG_DEFAULT_NOOP is not set
-CONFIG_DEFAULT_IOSCHED="anticipatory"
-
-#
-# Bus options (PCI, PCMCIA, EISA, ISA, TC)
-#
-CONFIG_HW_HAS_PCI=y
-CONFIG_PCI=y
-CONFIG_MMU=y
-
-#
-# PCCARD (PCMCIA/CardBus) support
-#
-# CONFIG_PCCARD is not set
-
-#
-# PCI Hotplug Support
-#
-# CONFIG_HOTPLUG_PCI is not set
-
-#
-# Executable file formats
-#
-CONFIG_BINFMT_ELF=y
-# CONFIG_BINFMT_MISC is not set
-CONFIG_TRAD_SIGNALS=y
-
-#
-# Power management options
-#
-CONFIG_PM=y
-# CONFIG_PM_LEGACY is not set
-# CONFIG_PM_DEBUG is not set
-# CONFIG_PM_SYSFS_DEPRECATED is not set
-
-#
-# Networking
-#
-CONFIG_NET=y
-
-#
-# Networking options
-#
-# CONFIG_NETDEBUG is not set
-CONFIG_PACKET=y
-# CONFIG_PACKET_MMAP is not set
-CONFIG_UNIX=y
-CONFIG_XFRM=y
-# CONFIG_XFRM_USER is not set
-# CONFIG_XFRM_SUB_POLICY is not set
-CONFIG_XFRM_MIGRATE=y
-# CONFIG_NET_KEY is not set
-CONFIG_INET=y
-# CONFIG_IP_MULTICAST is not set
-# CONFIG_IP_ADVANCED_ROUTER is not set
-CONFIG_IP_FIB_HASH=y
-CONFIG_IP_PNP=y
-# CONFIG_IP_PNP_DHCP is not set
-# CONFIG_IP_PNP_BOOTP is not set
-# CONFIG_IP_PNP_RARP is not set
-# CONFIG_NET_IPIP is not set
-# CONFIG_NET_IPGRE is not set
-# CONFIG_ARPD is not set
-# CONFIG_SYN_COOKIES is not set
-# CONFIG_INET_AH is not set
-# CONFIG_INET_ESP is not set
-# CONFIG_INET_IPCOMP is not set
-# CONFIG_INET_XFRM_TUNNEL is not set
-CONFIG_INET_TUNNEL=m
-CONFIG_INET_XFRM_MODE_TRANSPORT=y
-CONFIG_INET_XFRM_MODE_TUNNEL=y
-CONFIG_INET_XFRM_MODE_BEET=y
-CONFIG_INET_DIAG=y
-CONFIG_INET_TCP_DIAG=y
-# CONFIG_TCP_CONG_ADVANCED is not set
-CONFIG_TCP_CONG_CUBIC=y
-CONFIG_DEFAULT_TCP_CONG="cubic"
-CONFIG_TCP_MD5SIG=y
-
-#
-# IP: Virtual Server Configuration
-#
-# CONFIG_IP_VS is not set
-CONFIG_IPV6=m
-# CONFIG_IPV6_PRIVACY is not set
-CONFIG_IPV6_ROUTER_PREF=y
-CONFIG_IPV6_ROUTE_INFO=y
-# CONFIG_INET6_AH is not set
-# CONFIG_INET6_ESP is not set
-# CONFIG_INET6_IPCOMP is not set
-# CONFIG_IPV6_MIP6 is not set
-# CONFIG_INET6_XFRM_TUNNEL is not set
-# CONFIG_INET6_TUNNEL is not set
-CONFIG_INET6_XFRM_MODE_TRANSPORT=m
-CONFIG_INET6_XFRM_MODE_TUNNEL=m
-CONFIG_INET6_XFRM_MODE_BEET=m
-# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
-CONFIG_IPV6_SIT=m
-# CONFIG_IPV6_TUNNEL is not set
-# CONFIG_IPV6_MULTIPLE_TABLES is not set
-# CONFIG_NETWORK_SECMARK is not set
-CONFIG_NETFILTER=y
-# CONFIG_NETFILTER_DEBUG is not set
-
-#
-# Core Netfilter Configuration
-#
-# CONFIG_NETFILTER_NETLINK is not set
-CONFIG_NF_CONNTRACK_ENABLED=m
-CONFIG_NF_CONNTRACK_SUPPORT=y
-# CONFIG_IP_NF_CONNTRACK_SUPPORT is not set
-CONFIG_NF_CONNTRACK=m
-CONFIG_NF_CT_ACCT=y
-CONFIG_NF_CONNTRACK_MARK=y
-CONFIG_NF_CONNTRACK_EVENTS=y
-CONFIG_NF_CT_PROTO_GRE=m
-CONFIG_NF_CT_PROTO_SCTP=m
-CONFIG_NF_CONNTRACK_AMANDA=m
-CONFIG_NF_CONNTRACK_FTP=m
-CONFIG_NF_CONNTRACK_H323=m
-CONFIG_NF_CONNTRACK_IRC=m
-# CONFIG_NF_CONNTRACK_NETBIOS_NS is not set
-CONFIG_NF_CONNTRACK_PPTP=m
-CONFIG_NF_CONNTRACK_SANE=m
-CONFIG_NF_CONNTRACK_SIP=m
-CONFIG_NF_CONNTRACK_TFTP=m
-CONFIG_NETFILTER_XTABLES=m
-CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
-CONFIG_NETFILTER_XT_TARGET_MARK=m
-CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
-CONFIG_NETFILTER_XT_TARGET_NFLOG=m
-CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
-CONFIG_NETFILTER_XT_MATCH_COMMENT=m
-CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
-CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
-CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
-CONFIG_NETFILTER_XT_MATCH_DCCP=m
-# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
-CONFIG_NETFILTER_XT_MATCH_ESP=m
-CONFIG_NETFILTER_XT_MATCH_HELPER=m
-CONFIG_NETFILTER_XT_MATCH_LENGTH=m
-CONFIG_NETFILTER_XT_MATCH_LIMIT=m
-CONFIG_NETFILTER_XT_MATCH_MAC=m
-CONFIG_NETFILTER_XT_MATCH_MARK=m
-# CONFIG_NETFILTER_XT_MATCH_POLICY is not set
-CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
-CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
-# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
-CONFIG_NETFILTER_XT_MATCH_REALM=m
-CONFIG_NETFILTER_XT_MATCH_SCTP=m
-CONFIG_NETFILTER_XT_MATCH_STATE=m
-# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
-CONFIG_NETFILTER_XT_MATCH_STRING=m
-CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
-CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m
-
-#
-# IP: Netfilter Configuration
-#
-CONFIG_NF_CONNTRACK_IPV4=m
-CONFIG_NF_CONNTRACK_PROC_COMPAT=y
-# CONFIG_IP_NF_QUEUE is not set
-# CONFIG_IP_NF_IPTABLES is not set
-# CONFIG_IP_NF_ARPTABLES is not set
-
-#
-# IPv6: Netfilter Configuration (EXPERIMENTAL)
-#
-CONFIG_NF_CONNTRACK_IPV6=m
-# CONFIG_IP6_NF_QUEUE is not set
-# CONFIG_IP6_NF_IPTABLES is not set
-
-#
-# DCCP Configuration (EXPERIMENTAL)
-#
-# CONFIG_IP_DCCP is not set
-
-#
-# SCTP Configuration (EXPERIMENTAL)
-#
-# CONFIG_IP_SCTP is not set
-
-#
-# TIPC Configuration (EXPERIMENTAL)
-#
-# CONFIG_TIPC is not set
-# CONFIG_ATM is not set
-# CONFIG_BRIDGE is not set
-# CONFIG_VLAN_8021Q is not set
-# CONFIG_DECNET is not set
-# CONFIG_LLC2 is not set
-# CONFIG_IPX is not set
-# CONFIG_ATALK is not set
-# CONFIG_X25 is not set
-# CONFIG_LAPB is not set
-# CONFIG_ECONET is not set
-# CONFIG_WAN_ROUTER is not set
-
-#
-# QoS and/or fair queueing
-#
-# CONFIG_NET_SCHED is not set
-CONFIG_NET_CLS_ROUTE=y
-
-#
-# Network testing
-#
-# CONFIG_NET_PKTGEN is not set
-# CONFIG_HAMRADIO is not set
-# CONFIG_IRDA is not set
-# CONFIG_BT is not set
-# CONFIG_IEEE80211 is not set
-
-#
-# Device Drivers
-#
-
-#
-# Generic Driver Options
-#
-CONFIG_STANDALONE=y
-CONFIG_PREVENT_FIRMWARE_BUILD=y
-CONFIG_FW_LOADER=y
-# CONFIG_SYS_HYPERVISOR is not set
-
-#
-# Connector - unified userspace <-> kernelspace linker
-#
-# CONFIG_CONNECTOR is not set
-
-#
-# Memory Technology Devices (MTD)
-#
-# CONFIG_MTD is not set
-
-#
-# Parallel port support
-#
-# CONFIG_PARPORT is not set
-
-#
-# Plug and Play support
-#
-# CONFIG_PNPACPI is not set
-
-#
-# Block devices
-#
-# CONFIG_BLK_CPQ_DA is not set
-# CONFIG_BLK_CPQ_CISS_DA is not set
-# CONFIG_BLK_DEV_DAC960 is not set
-# CONFIG_BLK_DEV_UMEM is not set
-# CONFIG_BLK_DEV_COW_COMMON is not set
-CONFIG_BLK_DEV_LOOP=y
-# CONFIG_BLK_DEV_CRYPTOLOOP is not set
-# CONFIG_BLK_DEV_NBD is not set
-# CONFIG_BLK_DEV_SX8 is not set
-# CONFIG_BLK_DEV_UB is not set
-CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_RAM_COUNT=16
-CONFIG_BLK_DEV_RAM_SIZE=8192
-CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
-CONFIG_BLK_DEV_INITRD=y
-# CONFIG_CDROM_PKTCDVD is not set
-# CONFIG_ATA_OVER_ETH is not set
-
-#
-# Misc devices
-#
-CONFIG_SGI_IOC4=m
-# CONFIG_TIFM_CORE is not set
-
-#
-# ATA/ATAPI/MFM/RLL support
-#
-CONFIG_IDE=y
-CONFIG_IDE_MAX_HWIFS=4
-CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
-# CONFIG_BLK_DEV_IDE_SATA is not set
-CONFIG_BLK_DEV_IDEDISK=y
-CONFIG_IDEDISK_MULTI_MODE=y
-# CONFIG_BLK_DEV_IDECD is not set
-# CONFIG_BLK_DEV_IDETAPE is not set
-# CONFIG_BLK_DEV_IDEFLOPPY is not set
-# CONFIG_BLK_DEV_IDESCSI is not set
-# CONFIG_IDE_TASK_IOCTL is not set
-
-#
-# IDE chipset support/bugfixes
-#
-CONFIG_IDE_GENERIC=y
-CONFIG_BLK_DEV_IDEPCI=y
-CONFIG_IDEPCI_SHARE_IRQ=y
-# CONFIG_BLK_DEV_OFFBOARD is not set
-# CONFIG_BLK_DEV_GENERIC is not set
-# CONFIG_BLK_DEV_OPTI621 is not set
-CONFIG_BLK_DEV_IDEDMA_PCI=y
-# CONFIG_BLK_DEV_IDEDMA_FORCED is not set
-CONFIG_IDEDMA_PCI_AUTO=y
-# CONFIG_IDEDMA_ONLYDISK is not set
-# CONFIG_BLK_DEV_AEC62XX is not set
-# CONFIG_BLK_DEV_ALI15X3 is not set
-# CONFIG_BLK_DEV_AMD74XX is not set
-CONFIG_BLK_DEV_CMD64X=y
-# CONFIG_BLK_DEV_TRIFLEX is not set
-# CONFIG_BLK_DEV_CY82C693 is not set
-# CONFIG_BLK_DEV_CS5520 is not set
-# CONFIG_BLK_DEV_CS5530 is not set
-# CONFIG_BLK_DEV_HPT34X is not set
-# CONFIG_BLK_DEV_HPT366 is not set
-# CONFIG_BLK_DEV_JMICRON is not set
-# CONFIG_BLK_DEV_SC1200 is not set
-# CONFIG_BLK_DEV_PIIX is not set
-CONFIG_BLK_DEV_IT8213=m
-# CONFIG_BLK_DEV_IT821X is not set
-# CONFIG_BLK_DEV_NS87415 is not set
-# CONFIG_BLK_DEV_PDC202XX_OLD is not set
-# CONFIG_BLK_DEV_PDC202XX_NEW is not set
-# CONFIG_BLK_DEV_SVWKS is not set
-# CONFIG_BLK_DEV_SIIMAGE is not set
-# CONFIG_BLK_DEV_SLC90E66 is not set
-# CONFIG_BLK_DEV_TRM290 is not set
-# CONFIG_BLK_DEV_VIA82CXXX is not set
-CONFIG_BLK_DEV_TC86C001=m
-# CONFIG_IDE_ARM is not set
-CONFIG_BLK_DEV_IDEDMA=y
-# CONFIG_IDEDMA_IVB is not set
-CONFIG_IDEDMA_AUTO=y
-# CONFIG_BLK_DEV_HD is not set
-
-#
-# SCSI device support
-#
-# CONFIG_RAID_ATTRS is not set
-CONFIG_SCSI=y
-CONFIG_SCSI_TGT=m
-CONFIG_SCSI_NETLINK=y
-CONFIG_SCSI_PROC_FS=y
-
-#
-# SCSI support type (disk, tape, CD-ROM)
-#
-CONFIG_BLK_DEV_SD=y
-# CONFIG_CHR_DEV_ST is not set
-# CONFIG_CHR_DEV_OSST is not set
-# CONFIG_BLK_DEV_SR is not set
-# CONFIG_CHR_DEV_SG is not set
-# CONFIG_CHR_DEV_SCH is not set
-
-#
-# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
-#
-# CONFIG_SCSI_MULTI_LUN is not set
-# CONFIG_SCSI_CONSTANTS is not set
-# CONFIG_SCSI_LOGGING is not set
-CONFIG_SCSI_SCAN_ASYNC=y
-
-#
-# SCSI Transports
-#
-CONFIG_SCSI_SPI_ATTRS=m
-CONFIG_SCSI_FC_ATTRS=y
-CONFIG_SCSI_ISCSI_ATTRS=m
-# CONFIG_SCSI_SAS_ATTRS is not set
-# CONFIG_SCSI_SAS_LIBSAS is not set
-
-#
-# SCSI low-level drivers
-#
-CONFIG_ISCSI_TCP=m
-# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
-# CONFIG_SCSI_3W_9XXX is not set
-# CONFIG_SCSI_ACARD is not set
-# CONFIG_SCSI_AACRAID is not set
-CONFIG_SCSI_AIC7XXX=m
-CONFIG_AIC7XXX_CMDS_PER_DEVICE=32
-CONFIG_AIC7XXX_RESET_DELAY_MS=15000
-# CONFIG_AIC7XXX_DEBUG_ENABLE is not set
-CONFIG_AIC7XXX_DEBUG_MASK=0
-# CONFIG_AIC7XXX_REG_PRETTY_PRINT is not set
-# CONFIG_SCSI_AIC7XXX_OLD is not set
-# CONFIG_SCSI_AIC79XX is not set
-# CONFIG_SCSI_AIC94XX is not set
-# CONFIG_SCSI_DPT_I2O is not set
-# CONFIG_SCSI_ARCMSR is not set
-# CONFIG_MEGARAID_NEWGEN is not set
-# CONFIG_MEGARAID_LEGACY is not set
-# CONFIG_MEGARAID_SAS is not set
-# CONFIG_SCSI_HPTIOP is not set
-# CONFIG_SCSI_DMX3191D is not set
-# CONFIG_SCSI_FUTURE_DOMAIN is not set
-# CONFIG_SCSI_IPS is not set
-# CONFIG_SCSI_INITIO is not set
-# CONFIG_SCSI_INIA100 is not set
-# CONFIG_SCSI_STEX is not set
-# CONFIG_SCSI_SYM53C8XX_2 is not set
-# CONFIG_SCSI_QLOGIC_1280 is not set
-# CONFIG_SCSI_QLA_FC is not set
-# CONFIG_SCSI_QLA_ISCSI is not set
-# CONFIG_SCSI_LPFC is not set
-# CONFIG_SCSI_DC395x is not set
-# CONFIG_SCSI_DC390T is not set
-# CONFIG_SCSI_NSP32 is not set
-# CONFIG_SCSI_DEBUG is not set
-# CONFIG_SCSI_SRP is not set
-
-#
-# Serial ATA (prod) and Parallel ATA (experimental) drivers
-#
-# CONFIG_ATA is not set
-
-#
-# Multi-device support (RAID and LVM)
-#
-# CONFIG_MD is not set
-
-#
-# Fusion MPT device support
-#
-# CONFIG_FUSION is not set
-# CONFIG_FUSION_SPI is not set
-# CONFIG_FUSION_FC is not set
-# CONFIG_FUSION_SAS is not set
-
-#
-# IEEE 1394 (FireWire) support
-#
-# CONFIG_IEEE1394 is not set
-
-#
-# I2O device support
-#
-# CONFIG_I2O is not set
-
-#
-# Network device support
-#
-CONFIG_NETDEVICES=y
-# CONFIG_DUMMY is not set
-# CONFIG_BONDING is not set
-# CONFIG_EQUALIZER is not set
-CONFIG_TUN=m
-
-#
-# ARCnet devices
-#
-# CONFIG_ARCNET is not set
-
-#
-# PHY device support
-#
-# CONFIG_PHYLIB is not set
-
-#
-# Ethernet (10 or 100Mbit)
-#
-CONFIG_NET_ETHERNET=y
-CONFIG_MII=y
-# CONFIG_HAPPYMEAL is not set
-# CONFIG_SUNGEM is not set
-# CONFIG_CASSINI is not set
-# CONFIG_NET_VENDOR_3COM is not set
-# CONFIG_DM9000 is not set
-
-#
-# Tulip family network device support
-#
-# CONFIG_NET_TULIP is not set
-# CONFIG_HP100 is not set
-CONFIG_NET_PCI=y
-# CONFIG_PCNET32 is not set
-# CONFIG_AMD8111_ETH is not set
-# CONFIG_ADAPTEC_STARFIRE is not set
-# CONFIG_B44 is not set
-# CONFIG_FORCEDETH is not set
-# CONFIG_DGRS is not set
-# CONFIG_EEPRO100 is not set
-# CONFIG_E100 is not set
-# CONFIG_FEALNX is not set
-CONFIG_NATSEMI=y
-# CONFIG_NE2K_PCI is not set
-# CONFIG_8139CP is not set
-CONFIG_8139TOO=y
-# CONFIG_8139TOO_PIO is not set
-# CONFIG_8139TOO_TUNE_TWISTER is not set
-# CONFIG_8139TOO_8129 is not set
-# CONFIG_8139_OLD_RX_RESET is not set
-# CONFIG_SIS900 is not set
-# CONFIG_EPIC100 is not set
-# CONFIG_SUNDANCE is not set
-# CONFIG_TLAN is not set
-# CONFIG_VIA_RHINE is not set
-# CONFIG_SC92031 is not set
-
-#
-# Ethernet (1000 Mbit)
-#
-# CONFIG_ACENIC is not set
-# CONFIG_DL2K is not set
-# CONFIG_E1000 is not set
-# CONFIG_NS83820 is not set
-# CONFIG_HAMACHI is not set
-# CONFIG_YELLOWFIN is not set
-# CONFIG_R8169 is not set
-# CONFIG_SIS190 is not set
-# CONFIG_SKGE is not set
-# CONFIG_SKY2 is not set
-# CONFIG_SK98LIN is not set
-# CONFIG_VIA_VELOCITY is not set
-# CONFIG_TIGON3 is not set
-# CONFIG_BNX2 is not set
-# CONFIG_QLA3XXX is not set
-# CONFIG_ATL1 is not set
-
-#
-# Ethernet (10000 Mbit)
-#
-# CONFIG_CHELSIO_T1 is not set
-CONFIG_CHELSIO_T3=m
-# CONFIG_IXGB is not set
-# CONFIG_S2IO is not set
-# CONFIG_MYRI10GE is not set
-CONFIG_NETXEN_NIC=m
-
-#
-# Token Ring devices
-#
-# CONFIG_TR is not set
-
-#
-# Wireless LAN (non-hamradio)
-#
-# CONFIG_NET_RADIO is not set
-
-#
-# Wan interfaces
-#
-# CONFIG_WAN is not set
-# CONFIG_FDDI is not set
-# CONFIG_HIPPI is not set
-CONFIG_PPP=m
-# CONFIG_PPP_MULTILINK is not set
-# CONFIG_PPP_FILTER is not set
-CONFIG_PPP_ASYNC=m
-CONFIG_PPP_SYNC_TTY=m
-CONFIG_PPP_DEFLATE=m
-# CONFIG_PPP_BSDCOMP is not set
-CONFIG_PPP_MPPE=m
-# CONFIG_PPPOE is not set
-# CONFIG_SLIP is not set
-CONFIG_SLHC=m
-# CONFIG_NET_FC is not set
-# CONFIG_SHAPER is not set
-# CONFIG_NETCONSOLE is not set
-# CONFIG_NETPOLL is not set
-# CONFIG_NET_POLL_CONTROLLER is not set
-
-#
-# ISDN subsystem
-#
-# CONFIG_ISDN is not set
-
-#
-# Telephony Support
-#
-# CONFIG_PHONE is not set
-
-#
-# Input device support
-#
-CONFIG_INPUT=y
-# CONFIG_INPUT_FF_MEMLESS is not set
-
-#
-# Userland interfaces
-#
-CONFIG_INPUT_MOUSEDEV=y
-CONFIG_INPUT_MOUSEDEV_PSAUX=y
-CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
-CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
-# CONFIG_INPUT_JOYDEV is not set
-# CONFIG_INPUT_TSDEV is not set
-CONFIG_INPUT_EVDEV=m
-# CONFIG_INPUT_EVBUG is not set
-
-#
-# Input Device Drivers
-#
-CONFIG_INPUT_KEYBOARD=y
-CONFIG_KEYBOARD_ATKBD=y
-# CONFIG_KEYBOARD_SUNKBD is not set
-# CONFIG_KEYBOARD_LKKBD is not set
-# CONFIG_KEYBOARD_XTKBD is not set
-# CONFIG_KEYBOARD_NEWTON is not set
-# CONFIG_KEYBOARD_STOWAWAY is not set
-CONFIG_INPUT_MOUSE=y
-CONFIG_MOUSE_PS2=y
-# CONFIG_MOUSE_SERIAL is not set
-# CONFIG_MOUSE_VSXXXAA is not set
-# CONFIG_INPUT_JOYSTICK is not set
-# CONFIG_INPUT_TOUCHSCREEN is not set
-# CONFIG_INPUT_MISC is not set
-
-#
-# Hardware I/O ports
-#
-CONFIG_SERIO=y
-CONFIG_SERIO_I8042=y
-CONFIG_SERIO_SERPORT=y
-# CONFIG_SERIO_PCIPS2 is not set
-CONFIG_SERIO_LIBPS2=y
-# CONFIG_SERIO_RAW is not set
-# CONFIG_GAMEPORT is not set
-
-#
-# Character devices
-#
-CONFIG_VT=y
-# CONFIG_VT_CONSOLE is not set
-CONFIG_HW_CONSOLE=y
-# CONFIG_VT_HW_CONSOLE_BINDING is not set
-CONFIG_SERIAL_NONSTANDARD=y
-# CONFIG_COMPUTONE is not set
-# CONFIG_ROCKETPORT is not set
-# CONFIG_CYCLADES is not set
-# CONFIG_DIGIEPCA is not set
-# CONFIG_MOXA_INTELLIO is not set
-# CONFIG_MOXA_SMARTIO is not set
-CONFIG_MOXA_SMARTIO_NEW=m
-# CONFIG_ISI is not set
-# CONFIG_SYNCLINKMP is not set
-# CONFIG_SYNCLINK_GT is not set
-# CONFIG_N_HDLC is not set
-# CONFIG_RISCOM8 is not set
-# CONFIG_SPECIALIX is not set
-# CONFIG_SX is not set
-# CONFIG_RIO is not set
-# CONFIG_STALDRV is not set
-
-#
-# Serial drivers
-#
-# CONFIG_SERIAL_8250 is not set
-
-#
-# Non-8250 serial port support
-#
-CONFIG_SERIAL_PNX8XXX=y
-CONFIG_SERIAL_PNX8XXX_CONSOLE=y
-CONFIG_SERIAL_CORE=y
-CONFIG_SERIAL_CORE_CONSOLE=y
-# CONFIG_SERIAL_JSM is not set
-CONFIG_UNIX98_PTYS=y
-CONFIG_LEGACY_PTYS=y
-CONFIG_LEGACY_PTY_COUNT=256
-
-#
-# IPMI
-#
-# CONFIG_IPMI_HANDLER is not set
-
-#
-# Watchdog Cards
-#
-# CONFIG_WATCHDOG is not set
-CONFIG_HW_RANDOM=y
-# CONFIG_RTC is not set
-# CONFIG_GEN_RTC is not set
-# CONFIG_DTLK is not set
-# CONFIG_R3964 is not set
-# CONFIG_APPLICOM is not set
-# CONFIG_DRM is not set
-# CONFIG_RAW_DRIVER is not set
-
-#
-# TPM devices
-#
-# CONFIG_TCG_TPM is not set
-
-#
-# I2C support
-#
-CONFIG_I2C=m
-CONFIG_I2C_CHARDEV=m
-
-#
-# I2C Algorithms
-#
-CONFIG_I2C_ALGOBIT=m
-# CONFIG_I2C_ALGOPCF is not set
-# CONFIG_I2C_ALGOPCA is not set
-
-#
-# I2C Hardware Bus support
-#
-# CONFIG_I2C_ALI1535 is not set
-# CONFIG_I2C_ALI1563 is not set
-# CONFIG_I2C_ALI15X3 is not set
-# CONFIG_I2C_AMD756 is not set
-# CONFIG_I2C_AMD8111 is not set
-# CONFIG_I2C_I801 is not set
-# CONFIG_I2C_I810 is not set
-# CONFIG_I2C_PIIX4 is not set
-# CONFIG_I2C_NFORCE2 is not set
-# CONFIG_I2C_OCORES is not set
-# CONFIG_I2C_PARPORT_LIGHT is not set
-# CONFIG_I2C_PASEMI is not set
-# CONFIG_I2C_PROSAVAGE is not set
-# CONFIG_I2C_SAVAGE4 is not set
-# CONFIG_I2C_SIS5595 is not set
-# CONFIG_I2C_SIS630 is not set
-# CONFIG_I2C_SIS96X is not set
-# CONFIG_I2C_STUB is not set
-# CONFIG_I2C_VIA is not set
-# CONFIG_I2C_VIAPRO is not set
-# CONFIG_I2C_VOODOO3 is not set
-# CONFIG_I2C_PCA_ISA is not set
-
-#
-# Miscellaneous I2C Chip support
-#
-# CONFIG_SENSORS_DS1337 is not set
-# CONFIG_SENSORS_DS1374 is not set
-# CONFIG_SENSORS_EEPROM is not set
-# CONFIG_SENSORS_PCF8574 is not set
-# CONFIG_SENSORS_PCA9539 is not set
-# CONFIG_SENSORS_PCF8591 is not set
-# CONFIG_SENSORS_MAX6875 is not set
-# CONFIG_I2C_DEBUG_CORE is not set
-# CONFIG_I2C_DEBUG_ALGO is not set
-# CONFIG_I2C_DEBUG_BUS is not set
-# CONFIG_I2C_DEBUG_CHIP is not set
-
-#
-# SPI support
-#
-# CONFIG_SPI is not set
-# CONFIG_SPI_MASTER is not set
-
-#
-# Dallas's 1-wire bus
-#
-# CONFIG_W1 is not set
-
-#
-# Hardware Monitoring support
-#
-CONFIG_HWMON=y
-# CONFIG_HWMON_VID is not set
-# CONFIG_SENSORS_ABITUGURU is not set
-# CONFIG_SENSORS_ADM1021 is not set
-# CONFIG_SENSORS_ADM1025 is not set
-# CONFIG_SENSORS_ADM1026 is not set
-# CONFIG_SENSORS_ADM1029 is not set
-# CONFIG_SENSORS_ADM1031 is not set
-# CONFIG_SENSORS_ADM9240 is not set
-# CONFIG_SENSORS_ASB100 is not set
-# CONFIG_SENSORS_ATXP1 is not set
-# CONFIG_SENSORS_DS1621 is not set
-# CONFIG_SENSORS_F71805F is not set
-# CONFIG_SENSORS_FSCHER is not set
-# CONFIG_SENSORS_FSCPOS is not set
-# CONFIG_SENSORS_GL518SM is not set
-# CONFIG_SENSORS_GL520SM is not set
-# CONFIG_SENSORS_IT87 is not set
-# CONFIG_SENSORS_LM63 is not set
-# CONFIG_SENSORS_LM75 is not set
-# CONFIG_SENSORS_LM77 is not set
-# CONFIG_SENSORS_LM78 is not set
-# CONFIG_SENSORS_LM80 is not set
-# CONFIG_SENSORS_LM83 is not set
-# CONFIG_SENSORS_LM85 is not set
-# CONFIG_SENSORS_LM87 is not set
-# CONFIG_SENSORS_LM90 is not set
-# CONFIG_SENSORS_LM92 is not set
-# CONFIG_SENSORS_MAX1619 is not set
-# CONFIG_SENSORS_PC87360 is not set
-# CONFIG_SENSORS_PC87427 is not set
-# CONFIG_SENSORS_SIS5595 is not set
-# CONFIG_SENSORS_SMSC47M1 is not set
-# CONFIG_SENSORS_SMSC47M192 is not set
-# CONFIG_SENSORS_SMSC47B397 is not set
-# CONFIG_SENSORS_VIA686A is not set
-# CONFIG_SENSORS_VT1211 is not set
-# CONFIG_SENSORS_VT8231 is not set
-# CONFIG_SENSORS_W83781D is not set
-# CONFIG_SENSORS_W83791D is not set
-# CONFIG_SENSORS_W83792D is not set
-# CONFIG_SENSORS_W83793 is not set
-# CONFIG_SENSORS_W83L785TS is not set
-# CONFIG_SENSORS_W83627HF is not set
-# CONFIG_SENSORS_W83627EHF is not set
-# CONFIG_HWMON_DEBUG_CHIP is not set
-
-#
-# Multimedia devices
-#
-# CONFIG_VIDEO_DEV is not set
-
-#
-# Digital Video Broadcasting Devices
-#
-# CONFIG_DVB is not set
-# CONFIG_USB_DABUSB is not set
-
-#
-# Graphics support
-#
-CONFIG_FIRMWARE_EDID=y
-CONFIG_FB=y
-# CONFIG_FB_DDC is not set
-# CONFIG_FB_CFB_FILLRECT is not set
-# CONFIG_FB_CFB_COPYAREA is not set
-# CONFIG_FB_CFB_IMAGEBLIT is not set
-# CONFIG_FB_SVGALIB is not set
-# CONFIG_FB_MACMODES is not set
-# CONFIG_FB_BACKLIGHT is not set
-# CONFIG_FB_MODE_HELPERS is not set
-# CONFIG_FB_TILEBLITTING is not set
-# CONFIG_FB_CIRRUS is not set
-# CONFIG_FB_PM2 is not set
-# CONFIG_FB_CYBER2000 is not set
-# CONFIG_FB_ASILIANT is not set
-# CONFIG_FB_IMSTT is not set
-# CONFIG_FB_S1D13XXX is not set
-# CONFIG_FB_NVIDIA is not set
-# CONFIG_FB_RIVA is not set
-# CONFIG_FB_MATROX is not set
-# CONFIG_FB_RADEON is not set
-# CONFIG_FB_ATY128 is not set
-# CONFIG_FB_ATY is not set
-# CONFIG_FB_S3 is not set
-# CONFIG_FB_SAVAGE is not set
-# CONFIG_FB_SIS is not set
-# CONFIG_FB_NEOMAGIC is not set
-# CONFIG_FB_KYRO is not set
-# CONFIG_FB_3DFX is not set
-# CONFIG_FB_VOODOO1 is not set
-# CONFIG_FB_SMIVGX is not set
-# CONFIG_FB_TRIDENT is not set
-# CONFIG_FB_VIRTUAL is not set
-
-#
-# Console display driver support
-#
-# CONFIG_VGA_CONSOLE is not set
-CONFIG_DUMMY_CONSOLE=y
-# CONFIG_FRAMEBUFFER_CONSOLE is not set
-
-#
-# Logo configuration
-#
-# CONFIG_LOGO is not set
-# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
-
-#
-# Sound
-#
-# CONFIG_SOUND is not set
-
-#
-# HID Devices
-#
-CONFIG_HID=y
-# CONFIG_HID_DEBUG is not set
-
-#
-# USB support
-#
-CONFIG_USB_ARCH_HAS_HCD=y
-CONFIG_USB_ARCH_HAS_OHCI=y
-CONFIG_USB_ARCH_HAS_EHCI=y
-CONFIG_USB=y
-# CONFIG_USB_DEBUG is not set
-
-#
-# Miscellaneous USB options
-#
-CONFIG_USB_DEVICEFS=y
-# CONFIG_USB_DYNAMIC_MINORS is not set
-# CONFIG_USB_SUSPEND is not set
-# CONFIG_USB_OTG is not set
-
-#
-# USB Host Controller Drivers
-#
-# CONFIG_USB_EHCI_HCD is not set
-# CONFIG_USB_ISP116X_HCD is not set
-# CONFIG_USB_OHCI_HCD is not set
-# CONFIG_USB_UHCI_HCD is not set
-# CONFIG_USB_SL811_HCD is not set
-
-#
-# USB Device Class drivers
-#
-# CONFIG_USB_ACM is not set
-# CONFIG_USB_PRINTER is not set
-
-#
-# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
-#
-
-#
-# may also be needed; see USB_STORAGE Help for more information
-#
-CONFIG_USB_STORAGE=y
-# CONFIG_USB_STORAGE_DEBUG is not set
-# CONFIG_USB_STORAGE_DATAFAB is not set
-# CONFIG_USB_STORAGE_FREECOM is not set
-# CONFIG_USB_STORAGE_ISD200 is not set
-# CONFIG_USB_STORAGE_DPCM is not set
-# CONFIG_USB_STORAGE_USBAT is not set
-# CONFIG_USB_STORAGE_SDDR09 is not set
-# CONFIG_USB_STORAGE_SDDR55 is not set
-# CONFIG_USB_STORAGE_JUMPSHOT is not set
-# CONFIG_USB_STORAGE_ALAUDA is not set
-# CONFIG_USB_STORAGE_KARMA is not set
-# CONFIG_USB_LIBUSUAL is not set
-
-#
-# USB Input Devices
-#
-CONFIG_USB_HID=y
-# CONFIG_USB_HIDINPUT_POWERBOOK is not set
-# CONFIG_HID_FF is not set
-CONFIG_USB_HIDDEV=y
-# CONFIG_USB_AIPTEK is not set
-# CONFIG_USB_WACOM is not set
-# CONFIG_USB_ACECAD is not set
-# CONFIG_USB_KBTAB is not set
-# CONFIG_USB_POWERMATE is not set
-# CONFIG_USB_TOUCHSCREEN is not set
-# CONFIG_USB_YEALINK is not set
-# CONFIG_USB_XPAD is not set
-# CONFIG_USB_ATI_REMOTE is not set
-# CONFIG_USB_ATI_REMOTE2 is not set
-# CONFIG_USB_KEYSPAN_REMOTE is not set
-# CONFIG_USB_APPLETOUCH is not set
-# CONFIG_USB_GTCO is not set
-
-#
-# USB Imaging devices
-#
-# CONFIG_USB_MDC800 is not set
-# CONFIG_USB_MICROTEK is not set
-
-#
-# USB Network Adapters
-#
-# CONFIG_USB_CATC is not set
-# CONFIG_USB_KAWETH is not set
-# CONFIG_USB_PEGASUS is not set
-# CONFIG_USB_RTL8150 is not set
-# CONFIG_USB_USBNET_MII is not set
-# CONFIG_USB_USBNET is not set
-CONFIG_USB_MON=y
-
-#
-# USB port drivers
-#
-
-#
-# USB Serial Converter support
-#
-# CONFIG_USB_SERIAL is not set
-
-#
-# USB Miscellaneous drivers
-#
-# CONFIG_USB_EMI62 is not set
-# CONFIG_USB_EMI26 is not set
-# CONFIG_USB_ADUTUX is not set
-# CONFIG_USB_AUERSWALD is not set
-# CONFIG_USB_RIO500 is not set
-# CONFIG_USB_LEGOTOWER is not set
-# CONFIG_USB_LCD is not set
-# CONFIG_USB_BERRY_CHARGE is not set
-# CONFIG_USB_LED is not set
-# CONFIG_USB_CYPRESS_CY7C63 is not set
-# CONFIG_USB_CYTHERM is not set
-# CONFIG_USB_PHIDGET is not set
-# CONFIG_USB_IDMOUSE is not set
-# CONFIG_USB_FTDI_ELAN is not set
-# CONFIG_USB_APPLEDISPLAY is not set
-# CONFIG_USB_LD is not set
-# CONFIG_USB_TRANCEVIBRATOR is not set
-# CONFIG_USB_TEST is not set
-
-#
-# USB DSL modem support
-#
-
-#
-# USB Gadget Support
-#
-# CONFIG_USB_GADGET is not set
-
-#
-# MMC/SD Card support
-#
-# CONFIG_MMC is not set
-
-#
-# LED devices
-#
-# CONFIG_NEW_LEDS is not set
-
-#
-# LED drivers
-#
-
-#
-# LED Triggers
-#
-
-#
-# InfiniBand support
-#
-# CONFIG_INFINIBAND is not set
-
-#
-# EDAC - error detection and reporting (RAS) (EXPERIMENTAL)
-#
-
-#
-# Real Time Clock
-#
-# CONFIG_RTC_CLASS is not set
-
-#
-# DMA Engine support
-#
-# CONFIG_DMA_ENGINE is not set
-
-#
-# DMA Clients
-#
-
-#
-# DMA Devices
-#
-
-#
-# Auxiliary Display support
-#
-
-#
-# Virtualization
-#
-
-#
-# File systems
-#
-CONFIG_EXT2_FS=y
-# CONFIG_EXT2_FS_XATTR is not set
-# CONFIG_EXT2_FS_XIP is not set
-CONFIG_EXT3_FS=y
-CONFIG_EXT3_FS_XATTR=y
-# CONFIG_EXT3_FS_POSIX_ACL is not set
-# CONFIG_EXT3_FS_SECURITY is not set
-# CONFIG_EXT4DEV_FS is not set
-CONFIG_JBD=y
-# CONFIG_JBD_DEBUG is not set
-CONFIG_FS_MBCACHE=y
-# CONFIG_REISERFS_FS is not set
-# CONFIG_JFS_FS is not set
-# CONFIG_FS_POSIX_ACL is not set
-CONFIG_XFS_FS=m
-# CONFIG_XFS_QUOTA is not set
-# CONFIG_XFS_SECURITY is not set
-# CONFIG_XFS_POSIX_ACL is not set
-# CONFIG_XFS_RT is not set
-# CONFIG_GFS2_FS is not set
-# CONFIG_OCFS2_FS is not set
-# CONFIG_MINIX_FS is not set
-# CONFIG_ROMFS_FS is not set
-CONFIG_INOTIFY=y
-CONFIG_INOTIFY_USER=y
-# CONFIG_QUOTA is not set
-CONFIG_DNOTIFY=y
-CONFIG_AUTOFS_FS=y
-CONFIG_AUTOFS4_FS=y
-# CONFIG_FUSE_FS is not set
-
-#
-# CD-ROM/DVD Filesystems
-#
-# CONFIG_ISO9660_FS is not set
-# CONFIG_UDF_FS is not set
-
-#
-# DOS/FAT/NT Filesystems
-#
-CONFIG_FAT_FS=y
-CONFIG_MSDOS_FS=y
-CONFIG_VFAT_FS=y
-CONFIG_FAT_DEFAULT_CODEPAGE=437
-CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
-# CONFIG_NTFS_FS is not set
-
-#
-# Pseudo filesystems
-#
-CONFIG_PROC_FS=y
-# CONFIG_PROC_KCORE is not set
-CONFIG_PROC_SYSCTL=y
-CONFIG_SYSFS=y
-CONFIG_TMPFS=y
-# CONFIG_TMPFS_POSIX_ACL is not set
-# CONFIG_HUGETLB_PAGE is not set
-CONFIG_RAMFS=y
-CONFIG_CONFIGFS_FS=m
-
-#
-# Miscellaneous filesystems
-#
-# CONFIG_ADFS_FS is not set
-# CONFIG_AFFS_FS is not set
-# CONFIG_HFS_FS is not set
-# CONFIG_HFSPLUS_FS is not set
-# CONFIG_BEFS_FS is not set
-# CONFIG_BFS_FS is not set
-# CONFIG_EFS_FS is not set
-CONFIG_CRAMFS=y
-# CONFIG_VXFS_FS is not set
-# CONFIG_HPFS_FS is not set
-# CONFIG_QNX4FS_FS is not set
-# CONFIG_SYSV_FS is not set
-# CONFIG_UFS_FS is not set
-
-#
-# Network File Systems
-#
-CONFIG_NFS_FS=y
-CONFIG_NFS_V3=y
-# CONFIG_NFS_V3_ACL is not set
-# CONFIG_NFS_V4 is not set
-# CONFIG_NFS_DIRECTIO is not set
-CONFIG_NFSD=m
-# CONFIG_NFSD_V3 is not set
-# CONFIG_NFSD_TCP is not set
-CONFIG_ROOT_NFS=y
-CONFIG_LOCKD=y
-CONFIG_LOCKD_V4=y
-CONFIG_EXPORTFS=m
-CONFIG_NFS_COMMON=y
-CONFIG_SUNRPC=y
-# CONFIG_RPCSEC_GSS_KRB5 is not set
-# CONFIG_RPCSEC_GSS_SPKM3 is not set
-CONFIG_SMB_FS=m
-# CONFIG_SMB_NLS_DEFAULT is not set
-# CONFIG_CIFS is not set
-# CONFIG_NCP_FS is not set
-# CONFIG_CODA_FS is not set
-# CONFIG_AFS_FS is not set
-# CONFIG_9P_FS is not set
-
-#
-# Partition Types
-#
-# CONFIG_PARTITION_ADVANCED is not set
-CONFIG_MSDOS_PARTITION=y
-
-#
-# Native Language Support
-#
-CONFIG_NLS=y
-CONFIG_NLS_DEFAULT="iso8859-1"
-# CONFIG_NLS_CODEPAGE_437 is not set
-# CONFIG_NLS_CODEPAGE_737 is not set
-# CONFIG_NLS_CODEPAGE_775 is not set
-# CONFIG_NLS_CODEPAGE_850 is not set
-# CONFIG_NLS_CODEPAGE_852 is not set
-# CONFIG_NLS_CODEPAGE_855 is not set
-# CONFIG_NLS_CODEPAGE_857 is not set
-# CONFIG_NLS_CODEPAGE_860 is not set
-# CONFIG_NLS_CODEPAGE_861 is not set
-# CONFIG_NLS_CODEPAGE_862 is not set
-# CONFIG_NLS_CODEPAGE_863 is not set
-# CONFIG_NLS_CODEPAGE_864 is not set
-# CONFIG_NLS_CODEPAGE_865 is not set
-# CONFIG_NLS_CODEPAGE_866 is not set
-# CONFIG_NLS_CODEPAGE_869 is not set
-# CONFIG_NLS_CODEPAGE_936 is not set
-# CONFIG_NLS_CODEPAGE_950 is not set
-# CONFIG_NLS_CODEPAGE_932 is not set
-# CONFIG_NLS_CODEPAGE_949 is not set
-# CONFIG_NLS_CODEPAGE_874 is not set
-# CONFIG_NLS_ISO8859_8 is not set
-# CONFIG_NLS_CODEPAGE_1250 is not set
-# CONFIG_NLS_CODEPAGE_1251 is not set
-# CONFIG_NLS_ASCII is not set
-# CONFIG_NLS_ISO8859_1 is not set
-# CONFIG_NLS_ISO8859_2 is not set
-# CONFIG_NLS_ISO8859_3 is not set
-# CONFIG_NLS_ISO8859_4 is not set
-# CONFIG_NLS_ISO8859_5 is not set
-# CONFIG_NLS_ISO8859_6 is not set
-# CONFIG_NLS_ISO8859_7 is not set
-# CONFIG_NLS_ISO8859_9 is not set
-# CONFIG_NLS_ISO8859_13 is not set
-# CONFIG_NLS_ISO8859_14 is not set
-# CONFIG_NLS_ISO8859_15 is not set
-# CONFIG_NLS_KOI8_R is not set
-# CONFIG_NLS_KOI8_U is not set
-# CONFIG_NLS_UTF8 is not set
-
-#
-# Distributed Lock Manager
-#
-CONFIG_DLM=m
-CONFIG_DLM_TCP=y
-# CONFIG_DLM_SCTP is not set
-# CONFIG_DLM_DEBUG is not set
-
-#
-# Profiling support
-#
-# CONFIG_PROFILING is not set
-
-#
-# Kernel hacking
-#
-CONFIG_TRACE_IRQFLAGS_SUPPORT=y
-# CONFIG_PRINTK_TIME is not set
-CONFIG_ENABLE_MUST_CHECK=y
-# CONFIG_MAGIC_SYSRQ is not set
-# CONFIG_UNUSED_SYMBOLS is not set
-# CONFIG_DEBUG_FS is not set
-# CONFIG_HEADERS_CHECK is not set
-# CONFIG_DEBUG_KERNEL is not set
-CONFIG_LOG_BUF_SHIFT=14
-CONFIG_CROSSCOMPILE=y
-CONFIG_CMDLINE=""
-CONFIG_SYS_SUPPORTS_KGDB=y
-
-#
-# Security options
-#
-# CONFIG_KEYS is not set
-# CONFIG_SECURITY is not set
-
-#
-# Cryptographic options
-#
-CONFIG_CRYPTO=y
-CONFIG_CRYPTO_ALGAPI=y
-CONFIG_CRYPTO_BLKCIPHER=m
-CONFIG_CRYPTO_HASH=m
-CONFIG_CRYPTO_MANAGER=m
-# CONFIG_CRYPTO_HMAC is not set
-CONFIG_CRYPTO_XCBC=m
-# CONFIG_CRYPTO_NULL is not set
-# CONFIG_CRYPTO_MD4 is not set
-CONFIG_CRYPTO_MD5=y
-CONFIG_CRYPTO_SHA1=m
-# CONFIG_CRYPTO_SHA256 is not set
-# CONFIG_CRYPTO_SHA512 is not set
-# CONFIG_CRYPTO_WP512 is not set
-# CONFIG_CRYPTO_TGR192 is not set
-CONFIG_CRYPTO_GF128MUL=m
-CONFIG_CRYPTO_ECB=m
-CONFIG_CRYPTO_CBC=m
-CONFIG_CRYPTO_PCBC=m
-CONFIG_CRYPTO_LRW=m
-# CONFIG_CRYPTO_DES is not set
-CONFIG_CRYPTO_FCRYPT=m
-# CONFIG_CRYPTO_BLOWFISH is not set
-# CONFIG_CRYPTO_TWOFISH is not set
-# CONFIG_CRYPTO_SERPENT is not set
-# CONFIG_CRYPTO_AES is not set
-# CONFIG_CRYPTO_CAST5 is not set
-# CONFIG_CRYPTO_CAST6 is not set
-# CONFIG_CRYPTO_TEA is not set
-CONFIG_CRYPTO_ARC4=m
-# CONFIG_CRYPTO_KHAZAD is not set
-# CONFIG_CRYPTO_ANUBIS is not set
-# CONFIG_CRYPTO_DEFLATE is not set
-# CONFIG_CRYPTO_MICHAEL_MIC is not set
-CONFIG_CRYPTO_CRC32C=m
-CONFIG_CRYPTO_CAMELLIA=m
-# CONFIG_CRYPTO_TEST is not set
-
-#
-# Hardware crypto devices
-#
-
-#
-# Library routines
-#
-CONFIG_BITREVERSE=y
-CONFIG_CRC_CCITT=m
-# CONFIG_CRC16 is not set
-CONFIG_CRC32=y
-CONFIG_LIBCRC32C=m
-CONFIG_ZLIB_INFLATE=y
-CONFIG_ZLIB_DEFLATE=m
-CONFIG_TEXTSEARCH=y
-CONFIG_TEXTSEARCH_KMP=m
-CONFIG_TEXTSEARCH_BM=m
-CONFIG_TEXTSEARCH_FSM=m
-CONFIG_PLIST=y
-CONFIG_HAS_IOMEM=y
-CONFIG_HAS_IOPORT=y
diff --git a/arch/mips/gt64120/wrppmc/pci.c b/arch/mips/gt64120/wrppmc/pci.c
index 2fbe934..0d5289b 100644
--- a/arch/mips/gt64120/wrppmc/pci.c
+++ b/arch/mips/gt64120/wrppmc/pci.c
@@ -13,7 +13,7 @@
 #include <linux/kernel.h>
 #include <asm/gt64120.h>
 
-extern struct pci_ops gt64120_pci_ops;
+extern struct pci_ops gt64xxx_pci0_ops;
 
 static struct resource pci0_io_resource = {
 	.name  = "pci_0 io",
@@ -30,7 +30,7 @@
 };
 
 static struct pci_controller hose_0 = {
-	.pci_ops	= &gt64120_pci_ops,
+	.pci_ops	= &gt64xxx_pci0_ops,
 	.io_resource	= &pci0_io_resource,
 	.mem_resource	= &pci0_mem_resource,
 };
diff --git a/arch/mips/jmr3927/common/prom.c b/arch/mips/jmr3927/common/prom.c
index aa481b7..5398813 100644
--- a/arch/mips/jmr3927/common/prom.c
+++ b/arch/mips/jmr3927/common/prom.c
@@ -41,16 +41,6 @@
 
 #include <asm/bootinfo.h>
 
-extern int prom_argc;
-extern char **prom_argv, **prom_envp;
-
-typedef struct
-{
-    char *name;
-/*    char *val; */
-}t_env_var;
-
-
 char * __init prom_getcmdline(void)
 {
 	return &(arcs_cmdline[0]);
@@ -60,6 +50,8 @@
 {
 	char *cp;
 	int actr;
+	int prom_argc = fw_arg0;
+	char **prom_argv = (char **) fw_arg1;
 
 	actr = 1; /* Always ignore argv[0] */
 
diff --git a/arch/mips/jmr3927/common/puts.c b/arch/mips/jmr3927/common/puts.c
index 1c1cad9..c611ab4 100644
--- a/arch/mips/jmr3927/common/puts.c
+++ b/arch/mips/jmr3927/common/puts.c
@@ -32,137 +32,29 @@
  *  675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#include <linux/types.h>
-#include <asm/jmr3927/txx927.h>
 #include <asm/jmr3927/tx3927.h>
-#include <asm/jmr3927/jmr3927.h>
 
 #define TIMEOUT       0xffffff
-#define SLOW_DOWN
-
-static const char digits[16] = "0123456789abcdef";
-
-#ifdef SLOW_DOWN
-#define slow_down() { int k; for (k=0; k<10000; k++); }
-#else
-#define slow_down()
-#endif
 
 void
-putch(const unsigned char c)
+prom_putchar(char c)
 {
         int i = 0;
 
         do {
-            slow_down();
             i++;
-            if (i>TIMEOUT) {
+            if (i>TIMEOUT)
                 break;
-            }
         } while (!(tx3927_sioptr(1)->cisr & TXx927_SICISR_TXALS));
 	tx3927_sioptr(1)->tfifo = c;
 	return;
 }
 
-unsigned char getch(void)
-{
-        int i = 0;
-	int dicr;
-	char c;
-
-	/* diable RX int. */
-	dicr = tx3927_sioptr(1)->dicr;
-	tx3927_sioptr(1)->dicr = 0;
-
-        do {
-            slow_down();
-            i++;
-            if (i>TIMEOUT) {
-                break;
-            }
-        } while (tx3927_sioptr(1)->disr & TXx927_SIDISR_UVALID)
-		;
-	c = tx3927_sioptr(1)->rfifo;
-
-	/* clear RX int. status */
-	tx3927_sioptr(1)->disr &= ~TXx927_SIDISR_RDIS;
-	/* enable RX int. */
-	tx3927_sioptr(1)->dicr = dicr;
-
-	return c;
-}
 void
-do_jmr3927_led_set(char n)
+puts(const char *cp)
 {
-    /* and with current leds */
-    jmr3927_led_and_set(n);
-}
-
-void
-puts(unsigned char *cp)
-{
-    int i = 0;
-
-    while (*cp) {
-        do {
-            slow_down();
-            i++;
-            if (i>TIMEOUT) {
-                break;
-            }
-        } while (!(tx3927_sioptr(1)->cisr & TXx927_SICISR_TXALS));
-	tx3927_sioptr(1)->tfifo = *cp++;
-    }
-    putch('\r');
-    putch('\n');
-}
-
-void
-fputs(unsigned char *cp)
-{
-    int i = 0;
-
-    while (*cp) {
-        do {
-             slow_down();
-            i++;
-            if (i>TIMEOUT) {
-                break;
-            }
-        } while (!(tx3927_sioptr(1)->cisr & TXx927_SICISR_TXALS));
-	tx3927_sioptr(1)->tfifo = *cp++;
-    }
-}
-
-
-void
-put64(uint64_t ul)
-{
-    int cnt;
-    unsigned ch;
-
-    cnt = 16;            /* 16 nibbles in a 64 bit long */
-    putch('0');
-    putch('x');
-    do {
-        cnt--;
-        ch = (unsigned char)(ul >> cnt * 4) & 0x0F;
-                putch(digits[ch]);
-    } while (cnt > 0);
-}
-
-void
-put32(unsigned u)
-{
-    int cnt;
-    unsigned ch;
-
-    cnt = 8;            /* 8 nibbles in a 32 bit long */
-    putch('0');
-    putch('x');
-    do {
-        cnt--;
-        ch = (unsigned char)(u >> cnt * 4) & 0x0F;
-                putch(digits[ch]);
-    } while (cnt > 0);
+    while (*cp)
+	prom_putchar(*cp++);
+    prom_putchar('\r');
+    prom_putchar('\n');
 }
diff --git a/arch/mips/jmr3927/rbhma3100/Makefile b/arch/mips/jmr3927/rbhma3100/Makefile
index 18fe9a8..8d00ba4 100644
--- a/arch/mips/jmr3927/rbhma3100/Makefile
+++ b/arch/mips/jmr3927/rbhma3100/Makefile
@@ -3,5 +3,4 @@
 #
 
 obj-y	 			+= init.o irq.o setup.o
-obj-$(CONFIG_RUNTIME_DEBUG) 	+= debug.o
 obj-$(CONFIG_KGDB)		+= kgdb_io.o
diff --git a/arch/mips/jmr3927/rbhma3100/init.c b/arch/mips/jmr3927/rbhma3100/init.c
index a55cb45..9169fab 100644
--- a/arch/mips/jmr3927/rbhma3100/init.c
+++ b/arch/mips/jmr3927/rbhma3100/init.c
@@ -28,20 +28,10 @@
  *  675 Mass Ave, Cambridge, MA 02139, USA.
  */
 #include <linux/init.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <linux/bootmem.h>
-
-#include <asm/addrspace.h>
 #include <asm/bootinfo.h>
-#include <asm/mipsregs.h>
 #include <asm/jmr3927/jmr3927.h>
 
-int prom_argc;
-char **prom_argv, **prom_envp;
 extern void  __init prom_init_cmdline(void);
-extern char *prom_getenv(char *envname);
-unsigned long mips_nofpu = 0;
 
 const char *get_system_type(void)
 {
@@ -52,7 +42,7 @@
 	;
 }
 
-extern void puts(unsigned char *cp);
+extern void puts(const char *cp);
 
 void __init prom_init(void)
 {
@@ -61,10 +51,6 @@
 	if ((tx3927_ccfgptr->ccfg & TX3927_CCFG_TLBOFF) == 0)
 		puts("Warning: TX3927 TLB off\n");
 #endif
-	prom_argc = fw_arg0;
-	prom_argv = (char **) fw_arg1;
-	prom_envp = (char **) fw_arg2;
-
 	mips_machgroup = MACH_GROUP_TOSHIBA;
 
 #ifdef CONFIG_TOSHIBA_JMR3927
diff --git a/arch/mips/jmr3927/rbhma3100/irq.c b/arch/mips/jmr3927/rbhma3100/irq.c
index 7d2c203..1187b44 100644
--- a/arch/mips/jmr3927/rbhma3100/irq.c
+++ b/arch/mips/jmr3927/rbhma3100/irq.c
@@ -30,53 +30,21 @@
  *  675 Mass Ave, Cambridge, MA 02139, USA.
  */
 #include <linux/init.h>
-
-#include <linux/errno.h>
-#include <linux/irq.h>
-#include <linux/kernel_stat.h>
-#include <linux/signal.h>
 #include <linux/sched.h>
 #include <linux/types.h>
 #include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/timex.h>
-#include <linux/slab.h>
-#include <linux/random.h>
-#include <linux/smp.h>
-#include <linux/smp_lock.h>
-#include <linux/bitops.h>
 
-#include <asm/irq_regs.h>
 #include <asm/io.h>
 #include <asm/mipsregs.h>
 #include <asm/system.h>
 
-#include <asm/ptrace.h>
 #include <asm/processor.h>
-#include <asm/jmr3927/irq.h>
-#include <asm/debug.h>
 #include <asm/jmr3927/jmr3927.h>
 
 #if JMR3927_IRQ_END > NR_IRQS
 #error JMR3927_IRQ_END > NR_IRQS
 #endif
 
-struct tb_irq_space* tb_irq_spaces;
-
-static int jmr3927_irq_base = -1;
-
-#ifdef CONFIG_PCI
-static int jmr3927_gen_iack(void)
-{
-	/* generate ACK cycle */
-#ifdef __BIG_ENDIAN
-	return (tx3927_pcicptr->iiadp >> 24) & 0xff;
-#else
-	return tx3927_pcicptr->iiadp & 0xff;
-#endif
-}
-#endif
-
 #define irc_dlevel	0
 #define irc_elevel	1
 
@@ -87,89 +55,24 @@
 	6, 6, 6			/* TMR */
 };
 
-static void jmr3927_irq_disable(unsigned int irq_nr);
-static void jmr3927_irq_enable(unsigned int irq_nr);
-
-static void jmr3927_irq_ack(unsigned int irq)
-{
-	if (irq == JMR3927_IRQ_IRC_TMR0)
-		jmr3927_tmrptr->tisr = 0;       /* ack interrupt */
-
-	jmr3927_irq_disable(irq);
-}
-
-static void jmr3927_irq_end(unsigned int irq)
-{
-	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
-		jmr3927_irq_enable(irq);
-}
-
-static void jmr3927_irq_disable(unsigned int irq_nr)
-{
-	struct tb_irq_space* sp;
-
-	for (sp = tb_irq_spaces; sp; sp = sp->next) {
-		if (sp->start_irqno <= irq_nr &&
-		    irq_nr < sp->start_irqno + sp->nr_irqs) {
-			if (sp->mask_func)
-				sp->mask_func(irq_nr - sp->start_irqno,
-					      sp->space_id);
-			break;
-		}
-	}
-}
-
-static void jmr3927_irq_enable(unsigned int irq_nr)
-{
-	struct tb_irq_space* sp;
-
-	for (sp = tb_irq_spaces; sp; sp = sp->next) {
-		if (sp->start_irqno <= irq_nr &&
-		    irq_nr < sp->start_irqno + sp->nr_irqs) {
-			if (sp->unmask_func)
-				sp->unmask_func(irq_nr - sp->start_irqno,
-						sp->space_id);
-			break;
-		}
-	}
-}
-
 /*
  * CP0_STATUS is a thread's resource (saved/restored on context switch).
- * So disable_irq/enable_irq MUST handle IOC/ISAC/IRC registers.
+ * So disable_irq/enable_irq MUST handle IOC/IRC registers.
  */
-static void mask_irq_isac(int irq_nr, int space_id)
+static void mask_irq_ioc(unsigned int irq)
 {
 	/* 0: mask */
-	unsigned char imask =
-		jmr3927_isac_reg_in(JMR3927_ISAC_INTM_ADDR);
-	unsigned int bit  = 1 << irq_nr;
-	jmr3927_isac_reg_out(imask & ~bit, JMR3927_ISAC_INTM_ADDR);
-	/* flush write buffer */
-	(void)jmr3927_ioc_reg_in(JMR3927_IOC_REV_ADDR);
-}
-static void unmask_irq_isac(int irq_nr, int space_id)
-{
-	/* 0: mask */
-	unsigned char imask = jmr3927_isac_reg_in(JMR3927_ISAC_INTM_ADDR);
-	unsigned int bit  = 1 << irq_nr;
-	jmr3927_isac_reg_out(imask | bit, JMR3927_ISAC_INTM_ADDR);
-	/* flush write buffer */
-	(void)jmr3927_ioc_reg_in(JMR3927_IOC_REV_ADDR);
-}
-
-static void mask_irq_ioc(int irq_nr, int space_id)
-{
-	/* 0: mask */
+	unsigned int irq_nr = irq - JMR3927_IRQ_IOC;
 	unsigned char imask = jmr3927_ioc_reg_in(JMR3927_IOC_INTM_ADDR);
 	unsigned int bit = 1 << irq_nr;
 	jmr3927_ioc_reg_out(imask & ~bit, JMR3927_IOC_INTM_ADDR);
 	/* flush write buffer */
 	(void)jmr3927_ioc_reg_in(JMR3927_IOC_REV_ADDR);
 }
-static void unmask_irq_ioc(int irq_nr, int space_id)
+static void unmask_irq_ioc(unsigned int irq)
 {
 	/* 0: mask */
+	unsigned int irq_nr = irq - JMR3927_IRQ_IOC;
 	unsigned char imask = jmr3927_ioc_reg_in(JMR3927_IOC_INTM_ADDR);
 	unsigned int bit = 1 << irq_nr;
 	jmr3927_ioc_reg_out(imask | bit, JMR3927_IOC_INTM_ADDR);
@@ -177,8 +80,9 @@
 	(void)jmr3927_ioc_reg_in(JMR3927_IOC_REV_ADDR);
 }
 
-static void mask_irq_irc(int irq_nr, int space_id)
+static void mask_irq_irc(unsigned int irq)
 {
+	unsigned int irq_nr = irq - JMR3927_IRQ_IRC;
 	volatile unsigned long *ilrp = &tx3927_ircptr->ilr[irq_nr / 2];
 	if (irq_nr & 1)
 		*ilrp = (*ilrp & 0x00ff) | (irc_dlevel << 8);
@@ -191,8 +95,9 @@
 	(void)tx3927_ircptr->ssr;
 }
 
-static void unmask_irq_irc(int irq_nr, int space_id)
+static void unmask_irq_irc(unsigned int irq)
 {
+	unsigned int irq_nr = irq - JMR3927_IRQ_IRC;
 	volatile unsigned long *ilrp = &tx3927_ircptr->ilr[irq_nr / 2];
 	if (irq_nr & 1)
 		*ilrp = (*ilrp & 0x00ff) | (irc_level[irq_nr] << 8);
@@ -203,98 +108,14 @@
 	tx3927_ircptr->imr = irc_elevel;
 }
 
-struct tb_irq_space jmr3927_isac_irqspace = {
-	.next = NULL,
-	.start_irqno = JMR3927_IRQ_ISAC,
-	nr_irqs : JMR3927_NR_IRQ_ISAC,
-	.mask_func = mask_irq_isac,
-	.unmask_func = unmask_irq_isac,
-	.name = "ISAC",
-	.space_id = 0,
-	can_share : 0
-};
-struct tb_irq_space jmr3927_ioc_irqspace = {
-	.next = NULL,
-	.start_irqno = JMR3927_IRQ_IOC,
-	nr_irqs : JMR3927_NR_IRQ_IOC,
-	.mask_func = mask_irq_ioc,
-	.unmask_func = unmask_irq_ioc,
-	.name = "IOC",
-	.space_id = 0,
-	can_share : 1
-};
-
-struct tb_irq_space jmr3927_irc_irqspace = {
-	.next		= NULL,
-	.start_irqno	= JMR3927_IRQ_IRC,
-	.nr_irqs	= JMR3927_NR_IRQ_IRC,
-	.mask_func	= mask_irq_irc,
-	.unmask_func	= unmask_irq_irc,
-	.name		= "on-chip",
-	.space_id	= 0,
-	.can_share	= 0
-};
-
-
-#ifdef CONFIG_TX_BRANCH_LIKELY_BUG_WORKAROUND
-static int tx_branch_likely_bug_count = 0;
-static int have_tx_branch_likely_bug = 0;
-
-static void tx_branch_likely_bug_fixup(void)
-{
-	struct pt_regs *regs = get_irq_regs();
-
-	/* TX39/49-BUG: Under this condition, the insn in delay slot
-           of the branch likely insn is executed (not nullified) even
-           the branch condition is false. */
-	if (!have_tx_branch_likely_bug)
-		return;
-	if ((regs->cp0_epc & 0xfff) == 0xffc &&
-	    KSEGX(regs->cp0_epc) != KSEG0 &&
-	    KSEGX(regs->cp0_epc) != KSEG1) {
-		unsigned int insn = *(unsigned int*)(regs->cp0_epc - 4);
-		/* beql,bnel,blezl,bgtzl */
-		/* bltzl,bgezl,blezall,bgezall */
-		/* bczfl, bcztl */
-		if ((insn & 0xf0000000) == 0x50000000 ||
-		    (insn & 0xfc0e0000) == 0x04020000 ||
-		    (insn & 0xf3fe0000) == 0x41020000) {
-			regs->cp0_epc -= 4;
-			tx_branch_likely_bug_count++;
-			printk(KERN_INFO
-			       "fix branch-likery bug in %s (insn %08x)\n",
-			       current->comm, insn);
-		}
-	}
-}
-#endif
-
-static void jmr3927_spurious(void)
-{
-	struct pt_regs * regs = get_irq_regs();
-
-#ifdef CONFIG_TX_BRANCH_LIKELY_BUG_WORKAROUND
-	tx_branch_likely_bug_fixup();
-#endif
-	printk(KERN_WARNING "spurious interrupt (cause 0x%lx, pc 0x%lx, ra 0x%lx).\n",
-	       regs->cp0_cause, regs->cp0_epc, regs->regs[31]);
-}
-
 asmlinkage void plat_irq_dispatch(void)
 {
-	struct pt_regs * regs = get_irq_regs();
+	unsigned long cp0_cause = read_c0_cause();
 	int irq;
 
-#ifdef CONFIG_TX_BRANCH_LIKELY_BUG_WORKAROUND
-	tx_branch_likely_bug_fixup();
-#endif
-	if ((regs->cp0_cause & CAUSEF_IP7) == 0) {
-#if 0
-		jmr3927_spurious();
-#endif
+	if ((cp0_cause & CAUSEF_IP7) == 0)
 		return;
-	}
-	irq = (regs->cp0_cause >> CAUSEB_IP2) & 0x0f;
+	irq = (cp0_cause >> CAUSEB_IP2) & 0x0f;
 
 	do_IRQ(irq + JMR3927_IRQ_IRC);
 }
@@ -317,35 +138,6 @@
 	jmr3927_ioc_interrupt, 0, CPU_MASK_NONE, "IOC", NULL, NULL,
 };
 
-static irqreturn_t jmr3927_isac_interrupt(int irq, void *dev_id)
-{
-	unsigned char istat = jmr3927_isac_reg_in(JMR3927_ISAC_INTS2_ADDR);
-	int i;
-
-	for (i = 0; i < JMR3927_NR_IRQ_ISAC; i++) {
-		if (istat & (1 << i)) {
-			irq = JMR3927_IRQ_ISAC + i;
-			do_IRQ(irq);
-		}
-	}
-	return IRQ_HANDLED;
-}
-
-static struct irqaction isac_action = {
-	jmr3927_isac_interrupt, 0, CPU_MASK_NONE, "ISAC", NULL, NULL,
-};
-
-
-static irqreturn_t jmr3927_isaerr_interrupt(int irq, void *dev_id)
-{
-	printk(KERN_WARNING "ISA error interrupt (irq 0x%x).\n", irq);
-
-	return IRQ_HANDLED;
-}
-static struct irqaction isaerr_action = {
-	jmr3927_isaerr_interrupt, 0, CPU_MASK_NONE, "ISA error", NULL, NULL,
-};
-
 static irqreturn_t jmr3927_pcierr_interrupt(int irq, void *dev_id)
 {
 	printk(KERN_WARNING "PCI error interrupt (irq 0x%x).\n", irq);
@@ -358,54 +150,19 @@
 	jmr3927_pcierr_interrupt, 0, CPU_MASK_NONE, "PCI error", NULL, NULL,
 };
 
-int jmr3927_ether1_irq = 0;
-
-void jmr3927_irq_init(u32 irq_base);
+static void __init jmr3927_irq_init(void);
 
 void __init arch_init_irq(void)
 {
-	/* look for io board's presence */
-	int have_isac = jmr3927_have_isac();
-
 	/* Now, interrupt control disabled, */
 	/* all IRC interrupts are masked, */
 	/* all IRC interrupt mode are Low Active. */
 
-	if (have_isac) {
-
-		/* ETHER1 (NE2000 compatible 10M-Ether) parameter setup */
-		/* temporary enable interrupt control */
-		tx3927_ircptr->cer = 1;
-		/* ETHER1 Int. Is High-Active. */
-		if (tx3927_ircptr->ssr & (1 << 0))
-			jmr3927_ether1_irq = JMR3927_IRQ_IRC_INT0;
-#if 0	/* INT3 may be asserted by ether0 (even after reboot...) */
-		else if (tx3927_ircptr->ssr & (1 << 3))
-			jmr3927_ether1_irq = JMR3927_IRQ_IRC_INT3;
-#endif
-		/* disable interrupt control */
-		tx3927_ircptr->cer = 0;
-
-		/* Ether1: High Active */
-		if (jmr3927_ether1_irq) {
-			int ether1_irc = jmr3927_ether1_irq - JMR3927_IRQ_IRC;
-			tx3927_ircptr->cr[ether1_irc / 8] |=
-				TX3927_IRCR_HIGH << ((ether1_irc % 8) * 2);
-		}
-	}
-
 	/* mask all IOC interrupts */
 	jmr3927_ioc_reg_out(0, JMR3927_IOC_INTM_ADDR);
 	/* setup IOC interrupt mode (SOFT:High Active, Others:Low Active) */
 	jmr3927_ioc_reg_out(JMR3927_IOC_INTF_SOFT, JMR3927_IOC_INTP_ADDR);
 
-	if (have_isac) {
-		/* mask all ISAC interrupts */
-		jmr3927_isac_reg_out(0, JMR3927_ISAC_INTM_ADDR);
-		/* setup ISAC interrupt mode (ISAIRQ3,ISAIRQ5:Low Active ???) */
-		jmr3927_isac_reg_out(JMR3927_ISAC_INTF_IRQ3|JMR3927_ISAC_INTF_IRQ5, JMR3927_ISAC_INTP_ADDR);
-	}
-
 	/* clear PCI Soft interrupts */
 	jmr3927_ioc_reg_out(0, JMR3927_IOC_INTS1_ADDR);
 	/* clear PCI Reset interrupts */
@@ -415,21 +172,11 @@
 	tx3927_ircptr->cer = TX3927_IRCER_ICE;
 	tx3927_ircptr->imr = irc_elevel;
 
-	jmr3927_irq_init(NR_ISA_IRQS);
-
-	/* setup irq space */
-	add_tb_irq_space(&jmr3927_isac_irqspace);
-	add_tb_irq_space(&jmr3927_ioc_irqspace);
-	add_tb_irq_space(&jmr3927_irc_irqspace);
+	jmr3927_irq_init();
 
 	/* setup IOC interrupt 1 (PCI, MODEM) */
 	setup_irq(JMR3927_IRQ_IOCINT, &ioc_action);
 
-	if (have_isac) {
-		setup_irq(JMR3927_IRQ_ISACINT, &isac_action);
-		setup_irq(JMR3927_IRQ_ISAC_ISAER, &isaerr_action);
-	}
-
 #ifdef CONFIG_PCI
 	setup_irq(JMR3927_IRQ_IRC_PCI, &pcierr_action);
 #endif
@@ -438,21 +185,28 @@
 	set_c0_status(ST0_IM);	/* IE bit is still 0. */
 }
 
-static struct irq_chip jmr3927_irq_controller = {
-	.name = "jmr3927_irq",
-	.ack = jmr3927_irq_ack,
-	.mask = jmr3927_irq_disable,
-	.mask_ack = jmr3927_irq_ack,
-	.unmask = jmr3927_irq_enable,
-	.end = jmr3927_irq_end,
+static struct irq_chip jmr3927_irq_ioc = {
+	.name = "jmr3927_ioc",
+	.ack = mask_irq_ioc,
+	.mask = mask_irq_ioc,
+	.mask_ack = mask_irq_ioc,
+	.unmask = unmask_irq_ioc,
 };
 
-void jmr3927_irq_init(u32 irq_base)
+static struct irq_chip jmr3927_irq_irc = {
+	.name = "jmr3927_irc",
+	.ack = mask_irq_irc,
+	.mask = mask_irq_irc,
+	.mask_ack = mask_irq_irc,
+	.unmask = unmask_irq_irc,
+};
+
+static void __init jmr3927_irq_init(void)
 {
 	u32 i;
 
-	for (i= irq_base; i< irq_base + JMR3927_NR_IRQ_IRC + JMR3927_NR_IRQ_IOC; i++)
-		set_irq_chip(i, &jmr3927_irq_controller);
-
-	jmr3927_irq_base = irq_base;
+	for (i = JMR3927_IRQ_IRC; i < JMR3927_IRQ_IRC + JMR3927_NR_IRQ_IRC; i++)
+		set_irq_chip_and_handler(i, &jmr3927_irq_irc, handle_level_irq);
+	for (i = JMR3927_IRQ_IOC; i < JMR3927_IRQ_IOC + JMR3927_NR_IRQ_IOC; i++)
+		set_irq_chip_and_handler(i, &jmr3927_irq_ioc, handle_level_irq);
 }
diff --git a/arch/mips/jmr3927/rbhma3100/kgdb_io.c b/arch/mips/jmr3927/rbhma3100/kgdb_io.c
index 269a42d..2604f2c 100644
--- a/arch/mips/jmr3927/rbhma3100/kgdb_io.c
+++ b/arch/mips/jmr3927/rbhma3100/kgdb_io.c
@@ -31,23 +31,12 @@
  *  675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#include <linux/types.h>
-#include <asm/jmr3927/txx927.h>
-#include <asm/jmr3927/tx3927.h>
 #include <asm/jmr3927/jmr3927.h>
 
 #define TIMEOUT       0xffffff
-#define SLOW_DOWN
-
-static const char digits[16] = "0123456789abcdef";
-
-#ifdef SLOW_DOWN
-#define slow_down() { int k; for (k=0; k<10000; k++); }
-#else
-#define slow_down()
-#endif
 
 static int remoteDebugInitialized = 0;
+static void debugInit(int baud)
 
 int putDebugChar(unsigned char c)
 {
@@ -103,20 +92,8 @@
 	return c;
 }
 
-void debugInit(int baud)
+static void debugInit(int baud)
 {
-	/*
-	volatile unsigned long lcr;
-	volatile unsigned long dicr;
-	volatile unsigned long disr;
-	volatile unsigned long cisr;
-	volatile unsigned long fcr;
-	volatile unsigned long flcr;
-	volatile unsigned long bgr;
-	volatile unsigned long tfifo;
-	volatile unsigned long rfifo;
-	*/
-
 	tx3927_sioptr(0)->lcr = 0x020;
 	tx3927_sioptr(0)->dicr = 0;
 	tx3927_sioptr(0)->disr = 0x4100;
@@ -125,31 +102,4 @@
 	tx3927_sioptr(0)->flcr = 0x02;
 	tx3927_sioptr(0)->bgr = ((JMR3927_BASE_BAUD + baud / 2) / baud) |
 		TXx927_SIBGR_BCLK_T0;
-#if 0
-	/*
-	 * Reset the UART.
-	 */
-	tx3927_sioptr(0)->fcr = TXx927_SIFCR_SWRST;
-	while (tx3927_sioptr(0)->fcr & TXx927_SIFCR_SWRST)
-		;
-
-	/*
-	 * and set the speed of the serial port
-	 * (currently hardwired to 9600 8N1
-	 */
-
-	tx3927_sioptr(0)->lcr = TXx927_SILCR_UMODE_8BIT |
-		TXx927_SILCR_USBL_1BIT |
-		TXx927_SILCR_SCS_IMCLK_BG;
-	tx3927_sioptr(0)->bgr =
-		((JMR3927_BASE_BAUD + baud / 2) / baud) |
-		TXx927_SIBGR_BCLK_T0;
-
-	/* HW RTS/CTS control */
-	if (ser->flags & ASYNC_HAVE_CTS_LINE)
-		tx3927_sioptr(0)->flcr = TXx927_SIFLCR_RCS | TXx927_SIFLCR_TES |
-			TXx927_SIFLCR_RTSTL_MAX /* 15 */;
-	/* Enable RX/TX */
-	tx3927_sioptr(0)->flcr &= ~(TXx927_SIFLCR_RSDE | TXx927_SIFLCR_TSDE);
-#endif
 }
diff --git a/arch/mips/jmr3927/rbhma3100/setup.c b/arch/mips/jmr3927/rbhma3100/setup.c
index fc523bd..d1ef2895 100644
--- a/arch/mips/jmr3927/rbhma3100/setup.c
+++ b/arch/mips/jmr3927/rbhma3100/setup.c
@@ -54,87 +54,18 @@
 
 #include <asm/addrspace.h>
 #include <asm/time.h>
-#include <asm/bcache.h>
-#include <asm/irq.h>
 #include <asm/reboot.h>
-#include <asm/gdb-stub.h>
 #include <asm/jmr3927/jmr3927.h>
 #include <asm/mipsregs.h>
-#include <asm/traps.h>
 
-extern void puts(unsigned char *cp);
+extern void puts(const char *cp);
 
 /* Tick Timer divider */
 #define JMR3927_TIMER_CCD	0	/* 1/2 */
 #define JMR3927_TIMER_CLK	(JMR3927_IMCLK / (2 << JMR3927_TIMER_CCD))
 
-unsigned char led_state = 0xf;
-
-struct {
-    struct resource ram0;
-    struct resource ram1;
-    struct resource pcimem;
-    struct resource iob;
-    struct resource ioc;
-    struct resource pciio;
-    struct resource jmy1394;
-    struct resource rom1;
-    struct resource rom0;
-    struct resource sio0;
-    struct resource sio1;
-} jmr3927_resources = {
-	{
-		.start	= 0,
-		.end	= 0x01FFFFFF,
-		.name	= "RAM0",
-		.flags = IORESOURCE_MEM
-	}, {
-		.start	= 0x02000000,
-		.end	= 0x03FFFFFF,
-		.name	= "RAM1",
-		.flags = IORESOURCE_MEM
-	}, {
-		.start	= 0x08000000,
-		.end	= 0x07FFFFFF,
-		.name	= "PCIMEM",
-		.flags = IORESOURCE_MEM
-	}, {
-		.start	= 0x10000000,
-		.end	= 0x13FFFFFF,
-		.name	= "IOB"
-	}, {
-		.start	= 0x14000000,
-		.end	= 0x14FFFFFF,
-		.name	= "IOC"
-	}, {
-		.start	= 0x15000000,
-		.end	= 0x15FFFFFF,
-		.name	= "PCIIO"
-	}, {
-		.start	= 0x1D000000,
-		.end	= 0x1D3FFFFF,
-		.name	= "JMY1394"
-	}, {
-		.start	= 0x1E000000,
-		.end	= 0x1E3FFFFF,
-		.name	= "ROM1"
-	}, {
-		.start	= 0x1FC00000,
-		.end	= 0x1FFFFFFF,
-		.name	= "ROM0"
-	}, {
-		.start	= 0xFFFEF300,
-		.end	= 0xFFFEF3FF,
-		.name	= "SIO0"
-	}, {
-		.start	= 0xFFFEF400,
-		.end	= 0xFFFEF4FF,
-		.name	= "SIO1"
-	},
-};
-
 /* don't enable - see errata */
-int jmr3927_ccfg_toeon = 0;
+static int jmr3927_ccfg_toeon;
 
 static inline void do_reset(void)
 {
@@ -173,9 +104,15 @@
 	return jiffies * (JMR3927_TIMER_CLK / HZ) + jmr3927_tmrptr->trr;
 }
 
+static void jmr3927_timer_ack(void)
+{
+	jmr3927_tmrptr->tisr = 0;       /* ack interrupt */
+}
+
 static void __init jmr3927_time_init(void)
 {
 	clocksource_mips.read = jmr3927_hpt_read;
+	mips_timer_ack = jmr3927_timer_ack;
 	mips_hpt_frequency = JMR3927_TIMER_CLK;
 }
 
@@ -190,9 +127,6 @@
 	setup_irq(JMR3927_IRQ_TICK, irq);
 }
 
-#define USECS_PER_JIFFY (1000000/HZ)
-
-//#undef DO_WRITE_THROUGH
 #define DO_WRITE_THROUGH
 #define DO_ENABLE_CACHE
 
@@ -224,12 +158,6 @@
 	/* Reboot on panic */
 	panic_timeout = 180;
 
-	{
-		unsigned int conf;
-		conf = read_c0_conf();
-	}
-
-#if 1
 	/* cache setup */
 	{
 		unsigned int conf;
@@ -256,16 +184,14 @@
 		write_c0_conf(conf);
 		write_c0_cache(0);
 	}
-#endif
 
 	/* initialize board */
 	jmr3927_board_init();
 
 	argptr = prom_getcmdline();
 
-	if ((argptr = strstr(argptr, "toeon")) != NULL) {
-			jmr3927_ccfg_toeon = 1;
-	}
+	if ((argptr = strstr(argptr, "toeon")) != NULL)
+		jmr3927_ccfg_toeon = 1;
 	argptr = prom_getcmdline();
 	if ((argptr = strstr(argptr, "ip=")) == NULL) {
 		argptr = prom_getcmdline();
@@ -281,7 +207,7 @@
 			memset(&req, 0, sizeof(req));
 			req.line = i;
 			req.iotype = UPIO_MEM;
-			req.membase = (char *)TX3927_SIO_REG(i);
+			req.membase = (unsigned char __iomem *)TX3927_SIO_REG(i);
 			req.mapbase = TX3927_SIO_REG(i);
 			req.irq = i == 0 ?
 				JMR3927_IRQ_IRC_SIO0 : JMR3927_IRQ_IRC_SIO1;
@@ -303,65 +229,33 @@
 
 static void tx3927_setup(void);
 
-#ifdef CONFIG_PCI
-unsigned long mips_pci_io_base;
-unsigned long mips_pci_io_size;
-unsigned long mips_pci_mem_base;
-unsigned long mips_pci_mem_size;
-/* for legacy I/O, PCI I/O PCI Bus address must be 0 */
-unsigned long mips_pci_io_pciaddr = 0;
-#endif
-
 static void __init jmr3927_board_init(void)
 {
-	char *argptr;
-
-#ifdef CONFIG_PCI
-	mips_pci_io_base = JMR3927_PCIIO;
-	mips_pci_io_size = JMR3927_PCIIO_SIZE;
-	mips_pci_mem_base = JMR3927_PCIMEM;
-	mips_pci_mem_size = JMR3927_PCIMEM_SIZE;
-#endif
-
 	tx3927_setup();
 
-	if (jmr3927_have_isac()) {
-
-#ifdef CONFIG_FB_E1355
-		argptr = prom_getcmdline();
-		if ((argptr = strstr(argptr, "video=")) == NULL) {
-			argptr = prom_getcmdline();
-			strcat(argptr, " video=e1355fb:crt16h");
-		}
-#endif
-
-#ifdef CONFIG_BLK_DEV_IDE
-		/* overrides PCI-IDE */
-#endif
-	}
-
 	/* SIO0 DTR on */
 	jmr3927_ioc_reg_out(0, JMR3927_IOC_DTR_ADDR);
 
 	jmr3927_led_set(0);
 
-
-	if (jmr3927_have_isac())
-		jmr3927_io_led_set(0);
 	printk("JMR-TX3927 (Rev %d) --- IOC(Rev %d) DIPSW:%d,%d,%d,%d\n",
 	       jmr3927_ioc_reg_in(JMR3927_IOC_BREV_ADDR) & JMR3927_REV_MASK,
 	       jmr3927_ioc_reg_in(JMR3927_IOC_REV_ADDR) & JMR3927_REV_MASK,
 	       jmr3927_dipsw1(), jmr3927_dipsw2(),
 	       jmr3927_dipsw3(), jmr3927_dipsw4());
-	if (jmr3927_have_isac())
-		printk("JMI-3927IO2 --- ISAC(Rev %d) DIPSW:%01x\n",
-		       jmr3927_isac_reg_in(JMR3927_ISAC_REV_ADDR) & JMR3927_REV_MASK,
-		       jmr3927_io_dipsw());
 }
 
-void __init tx3927_setup(void)
+static void __init tx3927_setup(void)
 {
 	int i;
+#ifdef CONFIG_PCI
+	unsigned long mips_pci_io_base = JMR3927_PCIIO;
+	unsigned long mips_pci_io_size = JMR3927_PCIIO_SIZE;
+	unsigned long mips_pci_mem_base = JMR3927_PCIMEM;
+	unsigned long mips_pci_mem_size = JMR3927_PCIMEM_SIZE;
+	/* for legacy I/O, PCI I/O PCI Bus address must be 0 */
+	unsigned long mips_pci_io_pciaddr = 0;
+#endif
 
 	/* SDRAMC are configured by PROM */
 
@@ -475,10 +369,8 @@
 		tx3927_pcicptr->mbas = ~(mips_pci_mem_size - 1);
 		tx3927_pcicptr->mba = 0;
 		tx3927_pcicptr->tlbmma = 0;
-#ifndef JMR3927_INIT_INDIRECT_PCI
 		/* Enable Direct mapping Address Space Decoder */
 		tx3927_pcicptr->lbc |= TX3927_PCIC_LBC_ILMDE | TX3927_PCIC_LBC_ILIDE;
-#endif
 
 		/* Clear All Local Bus Status */
 		tx3927_pcicptr->lbstat = TX3927_PCIC_LBIM_ALL;
@@ -491,22 +383,15 @@
 
 		/* PCIC Int => IRC IRQ10 */
 		tx3927_pcicptr->il = TX3927_IR_PCI;
-#if 1
 		/* Target Control (per errata) */
 		tx3927_pcicptr->tc = TX3927_PCIC_TC_OF8E | TX3927_PCIC_TC_IF8E;
-#endif
 
 		/* Enable Bus Arbiter */
-#if 0
-		tx3927_pcicptr->req_trace = 0x73737373;
-#endif
 		tx3927_pcicptr->pbapmc = TX3927_PCIC_PBAPMC_PBAEN;
 
 		tx3927_pcicptr->pcicmd = PCI_COMMAND_MASTER |
 			PCI_COMMAND_MEMORY |
-#if 1
 			PCI_COMMAND_IO |
-#endif
 			PCI_COMMAND_PARITY | PCI_COMMAND_SERR;
 	}
 #endif /* CONFIG_PCI */
@@ -555,8 +440,6 @@
 		.flags	= IORESOURCE_MEM,
 	};
 	struct platform_device *dev;
-	if (!jmr3927_have_nvram())
-		return -ENODEV;
 	dev = platform_device_register_simple("ds1742", -1, &res, 1);
 	return IS_ERR(dev) ? PTR_ERR(dev) : 0;
 }
diff --git a/arch/mips/kernel/asm-offsets.c b/arch/mips/kernel/asm-offsets.c
index 222de46..761a779 100644
--- a/arch/mips/kernel/asm-offsets.c
+++ b/arch/mips/kernel/asm-offsets.c
@@ -102,7 +102,6 @@
 	offset("#define TI_ADDR_LIMIT      ", struct thread_info, addr_limit);
 	offset("#define TI_RESTART_BLOCK   ", struct thread_info, restart_block);
 	offset("#define TI_REGS            ", struct thread_info, regs);
-	constant("#define _THREAD_SIZE_ORDER ", THREAD_SIZE_ORDER);
 	constant("#define _THREAD_SIZE       ", THREAD_SIZE);
 	constant("#define _THREAD_MASK       ", THREAD_MASK);
 	linefeed;
diff --git a/arch/mips/kernel/i8259.c b/arch/mips/kernel/i8259.c
index 9c79703..2345160 100644
--- a/arch/mips/kernel/i8259.c
+++ b/arch/mips/kernel/i8259.c
@@ -328,8 +328,8 @@
 {
 	int i;
 
-	request_resource(&ioport_resource, &pic1_io_resource);
-	request_resource(&ioport_resource, &pic2_io_resource);
+	insert_resource(&ioport_resource, &pic1_io_resource);
+	insert_resource(&ioport_resource, &pic2_io_resource);
 
 	init_8259A(0);
 
diff --git a/arch/mips/kernel/kspd.c b/arch/mips/kernel/kspd.c
index 29eadd4..c658001 100644
--- a/arch/mips/kernel/kspd.c
+++ b/arch/mips/kernel/kspd.c
@@ -17,6 +17,7 @@
  */
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/sched.h>
 #include <linux/unistd.h>
 #include <linux/file.h>
 #include <linux/fs.h>
@@ -198,7 +199,6 @@
 	int cmd;
 
 	char *vcwd;
-	mm_segment_t old_fs;
 	int size;
 
 	ret.retval = -1;
@@ -241,8 +241,6 @@
  		if ((ret.retval = sp_syscall(__NR_gettimeofday, (int)&tv,
  		                             (int)&tz, 0,0)) == 0)
 		ret.retval = tv.tv_sec;
-
-		ret.errno = errno;
 		break;
 
  	case MTSP_SYSCALL_EXIT:
@@ -279,7 +277,6 @@
 		if (cmd >= 0) {
 			ret.retval = sp_syscall(cmd, generic.arg0, generic.arg1,
 			                        generic.arg2, generic.arg3);
-			ret.errno = errno;
 		} else
  			printk(KERN_WARNING
 			       "KSPD: Unknown SP syscall number %d\n", sc.cmd);
diff --git a/arch/mips/kernel/rtlx.c b/arch/mips/kernel/rtlx.c
index e6e3047..bfc8ca1 100644
--- a/arch/mips/kernel/rtlx.c
+++ b/arch/mips/kernel/rtlx.c
@@ -289,7 +289,7 @@
 	return write_spacefree(chan->rt_read, chan->rt_write, chan->buffer_size);
 }
 
-ssize_t rtlx_read(int index, void __user *buff, size_t count, int user)
+ssize_t rtlx_read(int index, void __user *buff, size_t count)
 {
 	size_t lx_write, fl = 0L;
 	struct rtlx_channel *lx;
@@ -331,9 +331,10 @@
 	return count;
 }
 
-ssize_t rtlx_write(int index, const void __user *buffer, size_t count, int user)
+ssize_t rtlx_write(int index, const void __user *buffer, size_t count)
 {
 	struct rtlx_channel *rt;
+	unsigned long failed;
 	size_t rt_read;
 	size_t fl;
 
@@ -363,7 +364,7 @@
 	}
 
 out:
-	count -= cailed;
+	count -= failed;
 
 	smp_wmb();
 	rt->rt_write = (rt->rt_write + count) % rt->buffer_size;
diff --git a/arch/mips/mips-boards/generic/display.c b/arch/mips/mips-boards/generic/display.c
index f653946..548dbe5 100644
--- a/arch/mips/mips-boards/generic/display.c
+++ b/arch/mips/mips-boards/generic/display.c
@@ -24,16 +24,16 @@
 
 void mips_display_message(const char *str)
 {
-	static volatile unsigned int *display = NULL;
+	static unsigned int __iomem *display = NULL;
 	int i;
 
 	if (unlikely(display == NULL))
-		display = (volatile unsigned int *)ioremap(ASCII_DISPLAY_POS_BASE, 16*sizeof(int));
+		display = ioremap(ASCII_DISPLAY_POS_BASE, 16*sizeof(int));
 
 	for (i = 0; i <= 14; i=i+2) {
 	         if (*str)
-		         display[i] = *str++;
+		         writel(*str++, display + i);
 		 else
-		         display[i] = ' ';
+		         writel(' ', display + i);
 	}
 }
diff --git a/arch/mips/mips-boards/generic/pci.c b/arch/mips/mips-boards/generic/pci.c
index 3192a14..f98d60f 100644
--- a/arch/mips/mips-boards/generic/pci.c
+++ b/arch/mips/mips-boards/generic/pci.c
@@ -65,7 +65,7 @@
 };
 
 extern struct pci_ops bonito64_pci_ops;
-extern struct pci_ops gt64120_pci_ops;
+extern struct pci_ops gt64xxx_pci0_ops;
 extern struct pci_ops msc_pci_ops;
 
 static struct pci_controller bonito64_controller = {
@@ -76,7 +76,7 @@
 };
 
 static struct pci_controller gt64120_controller = {
-	.pci_ops	= &gt64120_pci_ops,
+	.pci_ops	= &gt64xxx_pci0_ops,
 	.io_resource	= &gt64120_io_resource,
 	.mem_resource	= &gt64120_mem_resource,
 };
diff --git a/arch/mips/mips-boards/generic/reset.c b/arch/mips/mips-boards/generic/reset.c
index 0996ba3..7a1bb51 100644
--- a/arch/mips/mips-boards/generic/reset.c
+++ b/arch/mips/mips-boards/generic/reset.c
@@ -39,24 +39,24 @@
 
 static void mips_machine_restart(char *command)
 {
-        volatile unsigned int *softres_reg = (unsigned int *)ioremap (SOFTRES_REG, sizeof(unsigned int));
+	unsigned int __iomem *softres_reg = ioremap(SOFTRES_REG, sizeof(unsigned int));
 
-	*softres_reg = GORESET;
+	writew(GORESET, softres_reg);
 }
 
 static void mips_machine_halt(void)
 {
-        volatile unsigned int *softres_reg = (unsigned int *)ioremap (SOFTRES_REG, sizeof(unsigned int));
+        unsigned int __iomem *softres_reg = ioremap(SOFTRES_REG, sizeof(unsigned int));
 
-	*softres_reg = GORESET;
+	writew(GORESET, softres_reg);
 }
 
 #if defined(CONFIG_MIPS_ATLAS)
 static void atlas_machine_power_off(void)
 {
-        volatile unsigned int *psustby_reg = (unsigned int *)ioremap(ATLAS_PSUSTBY_REG, sizeof(unsigned int));
+	unsigned int __iomem *psustby_reg = ioremap(ATLAS_PSUSTBY_REG, sizeof(unsigned int));
 
-	*psustby_reg = ATLAS_GOSTBY;
+	writew(ATLAS_GOSTBY, psustby_reg);
 }
 #endif
 
diff --git a/arch/mips/mips-boards/malta/malta_int.c b/arch/mips/mips-boards/malta/malta_int.c
index 3c206bb..83d7602 100644
--- a/arch/mips/mips-boards/malta/malta_int.c
+++ b/arch/mips/mips-boards/malta/malta_int.c
@@ -42,8 +42,6 @@
 #include <asm/mips-boards/msc01_pci.h>
 #include <asm/msc01_ic.h>
 
-extern void mips_timer_interrupt(void);
-
 static DEFINE_SPINLOCK(mips_irq_lock);
 
 static inline int mips_pcibios_iack(void)
@@ -85,7 +83,7 @@
 		dummy = BONITO_PCIMAP_CFG;
 		iob();    /* sync */
 
-		irq = *(volatile u32 *)(_pcictrl_bonito_pcicfg);
+		irq = readl((u32 *)_pcictrl_bonito_pcicfg);
 		iob();    /* sync */
 		irq &= 0xff;
 		BONITO_PCIMAP_CFG = 0;
diff --git a/arch/mips/mips-boards/malta/malta_setup.c b/arch/mips/mips-boards/malta/malta_setup.c
index 56ea766..7873932 100644
--- a/arch/mips/mips-boards/malta/malta_setup.c
+++ b/arch/mips/mips-boards/malta/malta_setup.c
@@ -145,7 +145,8 @@
 #ifdef CONFIG_BLK_DEV_IDE
 	/* Check PCI clock */
 	{
-		int jmpr = (*((volatile unsigned int *)ioremap(MALTA_JMPRS_REG, sizeof(unsigned int))) >> 2) & 0x07;
+		unsigned int __iomem *jmpr_p = (unsigned int *) ioremap(MALTA_JMPRS_REG, sizeof(unsigned int));
+		int jmpr = (readw(jmpr_p) >> 2) & 0x07;
 		static const int pciclocks[] __initdata = {
 			33, 20, 25, 30, 12, 16, 37, 10
 		};
@@ -179,7 +180,6 @@
 	};
 #endif
 #endif
-
 	mips_reboot_setup();
 
 	board_time_init = mips_time_init;
diff --git a/arch/mips/mips-boards/sim/Makefile b/arch/mips/mips-boards/sim/Makefile
index 6aeebc9..dc0bfda 100644
--- a/arch/mips/mips-boards/sim/Makefile
+++ b/arch/mips/mips-boards/sim/Makefile
@@ -17,7 +17,8 @@
 # 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
 #
 
-obj-y := sim_setup.o sim_mem.o sim_time.o sim_int.o sim_cmdline.o
+obj-y := sim_platform.o sim_setup.o sim_mem.o sim_time.o sim_int.o \
+	 sim_cmdline.o
 
 obj-$(CONFIG_EARLY_PRINTK) += sim_console.o
 obj-$(CONFIG_SMP) += sim_smp.o
diff --git a/arch/mips/mips-boards/sim/sim_platform.c b/arch/mips/mips-boards/sim/sim_platform.c
new file mode 100644
index 0000000..53210a8
--- /dev/null
+++ b/arch/mips/mips-boards/sim/sim_platform.c
@@ -0,0 +1,35 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2007 by Ralf Baechle (ralf@linux-mips.org)
+ */
+#include <linux/init.h>
+#include <linux/if_ether.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+static char mipsnet_string[] = "mipsnet";
+
+static struct platform_device eth1_device = {
+	.name		= mipsnet_string,
+	.id		= 0,
+};
+
+/*
+ * Create a platform device for the GPI port that receives the
+ * image data from the embedded camera.
+ */
+static int __init mipsnet_devinit(void)
+{
+	int err;
+
+	err = platform_device_register(&eth1_device);
+	if (err)
+		printk(KERN_ERR "%s: registration failed\n", mipsnet_string);
+
+	return err;
+}
+
+device_initcall(mipsnet_devinit);
diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c
index 4e8f1b6..abf99b1 100644
--- a/arch/mips/mm/cache.c
+++ b/arch/mips/mm/cache.c
@@ -96,7 +96,7 @@
 
 		kaddr = kmap_coherent(page, vmaddr);
 		flush_data_cache_page((unsigned long)kaddr);
-		kunmap_coherent(kaddr);
+		kunmap_coherent();
 	}
 }
 
diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c
index e9951c0e..2d1c2c0 100644
--- a/arch/mips/mm/init.c
+++ b/arch/mips/mm/init.c
@@ -177,7 +177,7 @@
 
 #define UNIQUE_ENTRYHI(idx) (CKSEG0 + ((idx) << (PAGE_SHIFT + 1)))
 
-void kunmap_coherent(struct page *page)
+void kunmap_coherent(void)
 {
 #ifndef CONFIG_MIPS_MT_SMTC
 	unsigned int wired;
@@ -210,7 +210,7 @@
 	if (cpu_has_dc_aliases) {
 		vfrom = kmap_coherent(from, vaddr);
 		copy_page(vto, vfrom);
-		kunmap_coherent(from);
+		kunmap_coherent();
 	} else {
 		vfrom = kmap_atomic(from, KM_USER0);
 		copy_page(vto, vfrom);
@@ -233,7 +233,7 @@
 	if (cpu_has_dc_aliases) {
 		void *vto = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK);
 		memcpy(vto, src, len);
-		kunmap_coherent(page);
+		kunmap_coherent();
 	} else
 		memcpy(dst, src, len);
 	if ((vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc)
@@ -250,7 +250,7 @@
 		void *vfrom =
 			kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK);
 		memcpy(dst, vfrom, len);
-		kunmap_coherent(page);
+		kunmap_coherent();
 	} else
 		memcpy(dst, src, len);
 }
@@ -351,18 +351,15 @@
 #endif
 	kmap_coherent_init();
 
-#ifdef CONFIG_ISA
-	if (max_low_pfn >= MAX_DMA_PFN)
-		if (min_low_pfn >= MAX_DMA_PFN) {
-			zones_size[ZONE_DMA] = 0;
-			zones_size[ZONE_NORMAL] = max_low_pfn - min_low_pfn;
-		} else {
-			zones_size[ZONE_DMA] = MAX_DMA_PFN - min_low_pfn;
-			zones_size[ZONE_NORMAL] = max_low_pfn - MAX_DMA_PFN;
-		}
+#ifdef CONFIG_ZONE_DMA
+	if (min_low_pfn < MAX_DMA_PFN && MAX_DMA_PFN <= max_low_pfn) {
+		zones_size[ZONE_DMA] = MAX_DMA_PFN - min_low_pfn;
+		zones_size[ZONE_NORMAL] = max_low_pfn - MAX_DMA_PFN;
+	} else if (max_low_pfn < MAX_DMA_PFN)
+		zones_size[ZONE_DMA] = max_low_pfn - min_low_pfn;
 	else
 #endif
-	zones_size[ZONE_DMA] = max_low_pfn - min_low_pfn;
+	zones_size[ZONE_NORMAL] = max_low_pfn - min_low_pfn;
 
 #ifdef CONFIG_HIGHMEM
 	zones_size[ZONE_HIGHMEM] = highend_pfn - highstart_pfn;
diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile
index bf85995..df487c0 100644
--- a/arch/mips/pci/Makefile
+++ b/arch/mips/pci/Makefile
@@ -8,8 +8,7 @@
 # PCI bus host bridge specific code
 #
 obj-$(CONFIG_MIPS_BONITO64)	+= ops-bonito64.o
-obj-$(CONFIG_MIPS_GT64111)	+= ops-gt64111.o
-obj-$(CONFIG_MIPS_GT64120)	+= ops-gt64120.o
+obj-$(CONFIG_PCI_GT64XXX_PCI0)	+= ops-gt64xxx_pci0.o
 obj-$(CONFIG_PCI_MARVELL)	+= ops-marvell.o
 obj-$(CONFIG_MIPS_MSC)		+= ops-msc.o
 obj-$(CONFIG_MIPS_NILE4)	+= ops-nile4.o
diff --git a/arch/mips/pci/fixup-jmr3927.c b/arch/mips/pci/fixup-jmr3927.c
index 6e72d21..73d1850 100644
--- a/arch/mips/pci/fixup-jmr3927.c
+++ b/arch/mips/pci/fixup-jmr3927.c
@@ -29,7 +29,6 @@
  */
 #include <linux/types.h>
 #include <linux/pci.h>
-#include <linux/kernel.h>
 #include <linux/init.h>
 
 #include <asm/jmr3927/jmr3927.h>
@@ -81,14 +80,8 @@
 
 	/* Check OnBoard Ethernet (IDSEL=A24, DevNu=13) */
 	if (dev->bus->parent == NULL &&
-	    slot == TX3927_PCIC_IDSEL_AD_TO_SLOT(24)) {
-		extern int jmr3927_ether1_irq;
-		/* check this irq line was reserved for ether1 */
-		if (jmr3927_ether1_irq != JMR3927_IRQ_ETHER0)
-			irq = JMR3927_IRQ_ETHER0;
-		else
-			irq = 0;	/* disable */
-	}
+	    slot == TX3927_PCIC_IDSEL_AD_TO_SLOT(24))
+		irq = JMR3927_IRQ_ETHER0;
 	return irq;
 }
 
diff --git a/arch/mips/pci/ops-gt64111.c b/arch/mips/pci/ops-gt64111.c
deleted file mode 100644
index ecd3991..0000000
--- a/arch/mips/pci/ops-gt64111.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 1995, 1996, 1997, 2002 by Ralf Baechle
- * Copyright (C) 2001, 2002, 2003 by Liam Davies (ldavies@agile.tv)
- */
-#include <linux/types.h>
-#include <linux/pci.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-
-#include <asm/pci.h>
-#include <asm/io.h>
-#include <asm/gt64120.h>
-
-#include <asm/mach-cobalt/cobalt.h>
-
-/*
- * Device 31 on the GT64111 is used to generate PCI special
- * cycles, so we shouldn't expected to find a device there ...
- */
-static inline int pci_range_ck(struct pci_bus *bus, unsigned int devfn)
-{
-	if (bus->number == 0 && PCI_SLOT(devfn) < 31)
-		return 0;
-
-	return -1;
-}
-
-static int gt64111_pci_read_config(struct pci_bus *bus, unsigned int devfn,
-	int where, int size, u32 * val)
-{
-	if (pci_range_ck(bus, devfn))
-		return PCIBIOS_DEVICE_NOT_FOUND;
-
-	switch (size) {
-	case 4:
-		PCI_CFG_SET(devfn, where);
-		*val = GT_READ(GT_PCI0_CFGDATA_OFS);
-		return PCIBIOS_SUCCESSFUL;
-
-	case 2:
-		PCI_CFG_SET(devfn, (where & ~0x3));
-		*val = GT_READ(GT_PCI0_CFGDATA_OFS)
-		    >> ((where & 3) * 8);
-		return PCIBIOS_SUCCESSFUL;
-
-	case 1:
-		PCI_CFG_SET(devfn, (where & ~0x3));
-		*val = GT_READ(GT_PCI0_CFGDATA_OFS)
-		    >> ((where & 3) * 8);
-		return PCIBIOS_SUCCESSFUL;
-	}
-
-	return PCIBIOS_BAD_REGISTER_NUMBER;
-}
-
-static int gt64111_pci_write_config(struct pci_bus *bus, unsigned int devfn,
-	int where, int size, u32 val)
-{
-	u32 tmp;
-
-	if (pci_range_ck(bus, devfn))
-		return PCIBIOS_DEVICE_NOT_FOUND;
-
-	switch (size) {
-	case 4:
-		PCI_CFG_SET(devfn, where);
-		GT_WRITE(GT_PCI0_CFGDATA_OFS, val);
-
-		return PCIBIOS_SUCCESSFUL;
-
-	case 2:
-		PCI_CFG_SET(devfn, (where & ~0x3));
-		tmp = GT_READ(GT_PCI0_CFGDATA_OFS);
-		tmp &= ~(0xffff << ((where & 0x3) * 8));
-		tmp |= (val << ((where & 0x3) * 8));
-		GT_WRITE(GT_PCI0_CFGDATA_OFS, tmp);
-
-		return PCIBIOS_SUCCESSFUL;
-
-	case 1:
-		PCI_CFG_SET(devfn, (where & ~0x3));
-		tmp = GT_READ(GT_PCI0_CFGDATA_OFS);
-		tmp &= ~(0xff << ((where & 0x3) * 8));
-		tmp |= (val << ((where & 0x3) * 8));
-		GT_WRITE(GT_PCI0_CFGDATA_OFS, tmp);
-
-		return PCIBIOS_SUCCESSFUL;
-	}
-
-	return PCIBIOS_BAD_REGISTER_NUMBER;
-}
-
-struct pci_ops gt64111_pci_ops = {
-	.read = gt64111_pci_read_config,
-	.write = gt64111_pci_write_config,
-};
diff --git a/arch/mips/pci/ops-gt64120.c b/arch/mips/pci/ops-gt64xxx_pci0.c
similarity index 80%
rename from arch/mips/pci/ops-gt64120.c
rename to arch/mips/pci/ops-gt64xxx_pci0.c
index 6335844..3d896c5 100644
--- a/arch/mips/pci/ops-gt64120.c
+++ b/arch/mips/pci/ops-gt64xxx_pci0.c
@@ -39,8 +39,8 @@
 #define PCI_CFG_TYPE1_DEV_SHF           11
 #define PCI_CFG_TYPE1_BUS_SHF           16
 
-static int gt64120_pcibios_config_access(unsigned char access_type,
-	struct pci_bus *bus, unsigned int devfn, int where, u32 * data)
+static int gt64xxx_pci0_pcibios_config_access(unsigned char access_type,
+		struct pci_bus *bus, unsigned int devfn, int where, u32 * data)
 {
 	unsigned char busnum = bus->number;
 	u32 intr;
@@ -100,13 +100,13 @@
  * We can't address 8 and 16 bit words directly.  Instead we have to
  * read/write a 32bit word and mask/modify the data we actually want.
  */
-static int gt64120_pcibios_read(struct pci_bus *bus, unsigned int devfn,
-                                int where, int size, u32 * val)
+static int gt64xxx_pci0_pcibios_read(struct pci_bus *bus, unsigned int devfn,
+		int where, int size, u32 * val)
 {
 	u32 data = 0;
 
-	if (gt64120_pcibios_config_access(PCI_ACCESS_READ, bus, devfn, where,
-				          &data))
+	if (gt64xxx_pci0_pcibios_config_access(PCI_ACCESS_READ, bus, devfn,
+	                                       where, &data))
 		return PCIBIOS_DEVICE_NOT_FOUND;
 
 	if (size == 1)
@@ -119,16 +119,16 @@
 	return PCIBIOS_SUCCESSFUL;
 }
 
-static int gt64120_pcibios_write(struct pci_bus *bus, unsigned int devfn,
-			      int where, int size, u32 val)
+static int gt64xxx_pci0_pcibios_write(struct pci_bus *bus, unsigned int devfn,
+		int where, int size, u32 val)
 {
 	u32 data = 0;
 
 	if (size == 4)
 		data = val;
 	else {
-		if (gt64120_pcibios_config_access(PCI_ACCESS_READ, bus, devfn,
-		                                  where, &data))
+		if (gt64xxx_pci0_pcibios_config_access(PCI_ACCESS_READ, bus,
+		                                       devfn, where, &data))
 			return PCIBIOS_DEVICE_NOT_FOUND;
 
 		if (size == 1)
@@ -139,14 +139,14 @@
 				(val << ((where & 3) << 3));
 	}
 
-	if (gt64120_pcibios_config_access(PCI_ACCESS_WRITE, bus, devfn, where,
-				       &data))
+	if (gt64xxx_pci0_pcibios_config_access(PCI_ACCESS_WRITE, bus, devfn,
+	                                       where, &data))
 		return PCIBIOS_DEVICE_NOT_FOUND;
 
 	return PCIBIOS_SUCCESSFUL;
 }
 
-struct pci_ops gt64120_pci_ops = {
-	.read = gt64120_pcibios_read,
-	.write = gt64120_pcibios_write
+struct pci_ops gt64xxx_pci0_ops = {
+	.read	= gt64xxx_pci0_pcibios_read,
+	.write	= gt64xxx_pci0_pcibios_write
 };
diff --git a/arch/mips/pci/ops-tx3927.c b/arch/mips/pci/ops-tx3927.c
index 42530a0..aa698bd 100644
--- a/arch/mips/pci/ops-tx3927.c
+++ b/arch/mips/pci/ops-tx3927.c
@@ -40,7 +40,6 @@
 
 #include <asm/addrspace.h>
 #include <asm/jmr3927/jmr3927.h>
-#include <asm/debug.h>
 
 static inline int mkaddr(unsigned char bus, unsigned char dev_fn,
 	unsigned char where)
@@ -130,234 +129,3 @@
 	jmr3927_pci_read_config,
 	jmr3927_pci_write_config,
 };
-
-
-#ifndef JMR3927_INIT_INDIRECT_PCI
-
-inline unsigned long tc_readl(volatile __u32 * addr)
-{
-	return readl(addr);
-}
-
-inline void tc_writel(unsigned long data, volatile __u32 * addr)
-{
-	writel(data, addr);
-}
-#else
-
-unsigned long tc_readl(volatile __u32 * addr)
-{
-	unsigned long val;
-
-	*(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipciaddr =
-	    (unsigned long) CPHYSADDR(addr);
-	*(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcibe =
-	    (PCI_IPCIBE_ICMD_MEMREAD << PCI_IPCIBE_ICMD_SHIFT) |
-	    PCI_IPCIBE_IBE_LONG;
-	while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC));
-	val =
-	    le32_to_cpu(*(volatile u32 *) (unsigned long) & tx3927_pcicptr->
-			ipcidata);
-	/* clear by setting */
-	tx3927_pcicptr->istat |= PCI_ISTAT_IDICC;
-	return val;
-}
-
-void tc_writel(unsigned long data, volatile __u32 * addr)
-{
-	*(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcidata =
-	    cpu_to_le32(data);
-	*(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipciaddr =
-	    (unsigned long) CPHYSADDR(addr);
-	*(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcibe =
-	    (PCI_IPCIBE_ICMD_MEMWRITE << PCI_IPCIBE_ICMD_SHIFT) |
-	    PCI_IPCIBE_IBE_LONG;
-	while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC));
-	/* clear by setting */
-	tx3927_pcicptr->istat |= PCI_ISTAT_IDICC;
-}
-
-unsigned char tx_ioinb(unsigned char *addr)
-{
-	unsigned long val;
-	__u32 ioaddr;
-	int offset;
-	int byte;
-
-	ioaddr = (unsigned long) addr;
-	offset = ioaddr & 0x3;
-	byte = 0xf & ~(8 >> offset);
-
-	*(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipciaddr =
-	    (unsigned long) ioaddr;
-	*(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcibe =
-	    (PCI_IPCIBE_ICMD_IOREAD << PCI_IPCIBE_ICMD_SHIFT) | byte;
-	while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC));
-	val =
-	    le32_to_cpu(*(volatile u32 *) (unsigned long) & tx3927_pcicptr->
-			ipcidata);
-	val = val & 0xff;
-	/* clear by setting */
-	tx3927_pcicptr->istat |= PCI_ISTAT_IDICC;
-	return val;
-}
-
-void tx_iooutb(unsigned long data, unsigned char *addr)
-{
-	__u32 ioaddr;
-	int offset;
-	int byte;
-
-	data = data | (data << 8) | (data << 16) | (data << 24);
-	ioaddr = (unsigned long) addr;
-	offset = ioaddr & 0x3;
-	byte = 0xf & ~(8 >> offset);
-
-	*(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcidata = data;
-	*(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipciaddr =
-	    (unsigned long) ioaddr;
-	*(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcibe =
-	    (PCI_IPCIBE_ICMD_IOWRITE << PCI_IPCIBE_ICMD_SHIFT) | byte;
-	while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC));
-	/* clear by setting */
-	tx3927_pcicptr->istat |= PCI_ISTAT_IDICC;
-}
-
-unsigned short tx_ioinw(unsigned short *addr)
-{
-	unsigned long val;
-	__u32 ioaddr;
-	int offset;
-	int byte;
-
-	ioaddr = (unsigned long) addr;
-	offset = ioaddr & 0x2;
-	byte = 3 << offset;
-
-	*(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipciaddr =
-	    (unsigned long) ioaddr;
-	*(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcibe =
-	    (PCI_IPCIBE_ICMD_IOREAD << PCI_IPCIBE_ICMD_SHIFT) | byte;
-	while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC));
-	val =
-	    le32_to_cpu(*(volatile u32 *) (unsigned long) & tx3927_pcicptr->
-			ipcidata);
-	val = val & 0xffff;
-	/* clear by setting */
-	tx3927_pcicptr->istat |= PCI_ISTAT_IDICC;
-	return val;
-
-}
-
-void tx_iooutw(unsigned long data, unsigned short *addr)
-{
-	__u32 ioaddr;
-	int offset;
-	int byte;
-
-	data = data | (data << 16);
-	ioaddr = (unsigned long) addr;
-	offset = ioaddr & 0x2;
-	byte = 3 << offset;
-
-	*(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcidata = data;
-	*(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipciaddr =
-	    (unsigned long) ioaddr;
-	*(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcibe =
-	    (PCI_IPCIBE_ICMD_IOWRITE << PCI_IPCIBE_ICMD_SHIFT) | byte;
-	while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC));
-	/* clear by setting */
-	tx3927_pcicptr->istat |= PCI_ISTAT_IDICC;
-}
-
-unsigned long tx_ioinl(unsigned int *addr)
-{
-	unsigned long val;
-	__u32 ioaddr;
-
-	ioaddr = (unsigned long) addr;
-	*(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipciaddr =
-	    (unsigned long) ioaddr;
-	*(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcibe =
-	    (PCI_IPCIBE_ICMD_IOREAD << PCI_IPCIBE_ICMD_SHIFT) |
-	    PCI_IPCIBE_IBE_LONG;
-	while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC));
-	val =
-	    le32_to_cpu(*(volatile u32 *) (unsigned long) & tx3927_pcicptr->
-			ipcidata);
-	/* clear by setting */
-	tx3927_pcicptr->istat |= PCI_ISTAT_IDICC;
-	return val;
-}
-
-void tx_iooutl(unsigned long data, unsigned int *addr)
-{
-	__u32 ioaddr;
-
-	ioaddr = (unsigned long) addr;
-	*(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcidata =
-	    cpu_to_le32(data);
-	*(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipciaddr =
-	    (unsigned long) ioaddr;
-	*(volatile u32 *) (unsigned long) & tx3927_pcicptr->ipcibe =
-	    (PCI_IPCIBE_ICMD_IOWRITE << PCI_IPCIBE_ICMD_SHIFT) |
-	    PCI_IPCIBE_IBE_LONG;
-	while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC));
-	/* clear by setting */
-	tx3927_pcicptr->istat |= PCI_ISTAT_IDICC;
-}
-
-void tx_insbyte(unsigned char *addr, void *buffer, unsigned int count)
-{
-	unsigned char *ptr = (unsigned char *) buffer;
-
-	while (count--) {
-		*ptr++ = tx_ioinb(addr);
-	}
-}
-
-void tx_insword(unsigned short *addr, void *buffer, unsigned int count)
-{
-	unsigned short *ptr = (unsigned short *) buffer;
-
-	while (count--) {
-		*ptr++ = tx_ioinw(addr);
-	}
-}
-
-void tx_inslong(unsigned int *addr, void *buffer, unsigned int count)
-{
-	unsigned long *ptr = (unsigned long *) buffer;
-
-	while (count--) {
-		*ptr++ = tx_ioinl(addr);
-	}
-}
-
-void tx_outsbyte(unsigned char *addr, void *buffer, unsigned int count)
-{
-	unsigned char *ptr = (unsigned char *) buffer;
-
-	while (count--) {
-		tx_iooutb(*ptr++, addr);
-	}
-}
-
-void tx_outsword(unsigned short *addr, void *buffer, unsigned int count)
-{
-	unsigned short *ptr = (unsigned short *) buffer;
-
-	while (count--) {
-		tx_iooutw(*ptr++, addr);
-	}
-}
-
-void tx_outslong(unsigned int *addr, void *buffer, unsigned int count)
-{
-	unsigned long *ptr = (unsigned long *) buffer;
-
-	while (count--) {
-		tx_iooutl(*ptr++, addr);
-	}
-}
-#endif
diff --git a/arch/mips/pci/pci-lasat.c b/arch/mips/pci/pci-lasat.c
index 88fb191..985784a 100644
--- a/arch/mips/pci/pci-lasat.c
+++ b/arch/mips/pci/pci-lasat.c
@@ -12,7 +12,7 @@
 #include <asm/bootinfo.h>
 
 extern struct pci_ops nile4_pci_ops;
-extern struct pci_ops gt64120_pci_ops;
+extern struct pci_ops gt64xxx_pci0_ops;
 static struct resource lasat_pci_mem_resource = {
 	.name	= "LASAT PCI MEM",
 	.start	= 0x18000000,
@@ -38,7 +38,7 @@
 
 	switch (mips_machtype) {
 	case MACH_LASAT_100:
-                lasat_pci_controller.pci_ops = &gt64120_pci_ops;
+                lasat_pci_controller.pci_ops = &gt64xxx_pci0_ops;
                 break;
 	case MACH_LASAT_200:
                 lasat_pci_controller.pci_ops = &nile4_pci_ops;
diff --git a/arch/mips/pci/pci-ocelot.c b/arch/mips/pci/pci-ocelot.c
index 2b9495d..7f94f26 100644
--- a/arch/mips/pci/pci-ocelot.c
+++ b/arch/mips/pci/pci-ocelot.c
@@ -81,7 +81,7 @@
 };
 
 static struct pci_controller ocelot_pci_controller = {
-	.pci_ops	= gt64120_pci_ops;
+	.pci_ops	= gt64xxx_pci0_ops;
 	.mem_resource	= &ocelot_mem_resource;
 	.io_resource	= &ocelot_io_resource;
 };
diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c
index de7cfc5..8108231 100644
--- a/arch/mips/pci/pci.c
+++ b/arch/mips/pci/pci.c
@@ -77,6 +77,13 @@
 
 void __init register_pci_controller(struct pci_controller *hose)
 {
+	if (request_resource(&iomem_resource, hose->mem_resource) < 0)
+		goto out;
+	if (request_resource(&ioport_resource, hose->io_resource) < 0) {
+		release_resource(hose->mem_resource);
+		goto out;
+	}
+
 	*hose_tail = hose;
 	hose_tail = &hose->next;
 
@@ -87,6 +94,11 @@
 		printk(KERN_WARNING
 		       "registering PCI controller with io_map_base unset\n");
 	}
+	return;
+
+out:
+	printk(KERN_WARNING
+	       "Skipping PCI bus scan due to resource conflict\n");
 }
 
 /* Most MIPS systems have straight-forward swizzling needs.  */
@@ -121,11 +133,6 @@
 	/* Scan all of the recorded PCI controllers.  */
 	for (next_busno = 0, hose = hose_head; hose; hose = hose->next) {
 
-		if (request_resource(&iomem_resource, hose->mem_resource) < 0)
-			goto out;
-		if (request_resource(&ioport_resource, hose->io_resource) < 0)
-			goto out_free_mem_resource;
-
 		if (!hose->iommu)
 			PCI_DMA_BUS_IS_PHYS = 1;
 
@@ -144,14 +151,6 @@
 				need_domain_info = 1;
 			}
 		}
-		continue;
-
-out_free_mem_resource:
-		release_resource(hose->mem_resource);
-
-out:
-		printk(KERN_WARNING
-		       "Skipping PCI bus scan due to resource conflict\n");
 	}
 
 	if (!pci_probe_only)
diff --git a/arch/mips/sgi-ip22/ip22-nvram.c b/arch/mips/sgi-ip22/ip22-nvram.c
index fd29fd4..e19d60d 100644
--- a/arch/mips/sgi-ip22/ip22-nvram.c
+++ b/arch/mips/sgi-ip22/ip22-nvram.c
@@ -52,8 +52,7 @@
  * national semiconductor nv ram chip the op code is 3 bits and
  * the address is 6/8 bits.
  */
-static inline void eeprom_cmd(volatile unsigned int *ctrl, unsigned cmd,
-			      unsigned reg)
+static inline void eeprom_cmd(unsigned int *ctrl, unsigned cmd, unsigned reg)
 {
 	unsigned short ser_cmd;
 	int i;
@@ -61,33 +60,34 @@
 	ser_cmd = cmd | (reg << (16 - BITS_IN_COMMAND));
 	for (i = 0; i < BITS_IN_COMMAND; i++) {
 		if (ser_cmd & (1<<15))	/* if high order bit set */
-			*ctrl |= EEPROM_DATO;
+			writel(readl(ctrl) | EEPROM_DATO, ctrl);
 		else
-			*ctrl &= ~EEPROM_DATO;
-		*ctrl &= ~EEPROM_ECLK;
-		*ctrl |= EEPROM_ECLK;
+			writel(readl(ctrl) & ~EEPROM_DATO, ctrl);
+		writel(readl(ctrl) & ~EEPROM_ECLK, ctrl);
+		writel(readl(ctrl) | EEPROM_ECLK, ctrl);
 		ser_cmd <<= 1;
 	}
-	*ctrl &= ~EEPROM_DATO;	/* see data sheet timing diagram */
+	/* see data sheet timing diagram */
+	writel(readl(ctrl) & ~EEPROM_DATO, ctrl);
 }
 
-unsigned short ip22_eeprom_read(volatile unsigned int *ctrl, int reg)
+unsigned short ip22_eeprom_read(unsigned int *ctrl, int reg)
 {
 	unsigned short res = 0;
 	int i;
 
-	*ctrl &= ~EEPROM_EPROT;
+	writel(readl(ctrl) & ~EEPROM_EPROT, ctrl);
 	eeprom_cs_on(ctrl);
 	eeprom_cmd(ctrl, EEPROM_READ, reg);
 
 	/* clock the data ouf of serial mem */
 	for (i = 0; i < 16; i++) {
-		*ctrl &= ~EEPROM_ECLK;
+		writel(readl(ctrl) & ~EEPROM_ECLK, ctrl);
 		delay();
-		*ctrl |= EEPROM_ECLK;
+		writel(readl(ctrl) | EEPROM_ECLK, ctrl);
 		delay();
 		res <<= 1;
-		if (*ctrl & EEPROM_DATI)
+		if (readl(ctrl) & EEPROM_DATI)
 			res |= 1;
 	}
 
diff --git a/arch/mips/sgi-ip22/ip22-time.c b/arch/mips/sgi-ip22/ip22-time.c
index 2055547..8e88a44 100644
--- a/arch/mips/sgi-ip22/ip22-time.c
+++ b/arch/mips/sgi-ip22/ip22-time.c
@@ -94,7 +94,7 @@
 static unsigned long dosample(void)
 {
 	u32 ct0, ct1;
-	volatile u8 msb, lsb;
+	u8 msb, lsb;
 
 	/* Start the counter. */
 	sgint->tcword = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL |
@@ -107,21 +107,21 @@
 
 	/* Latch and spin until top byte of counter2 is zero */
 	do {
-		sgint->tcword = SGINT_TCWORD_CNT2 | SGINT_TCWORD_CLAT;
-		lsb = sgint->tcnt2;
-		msb = sgint->tcnt2;
+		writeb(SGINT_TCWORD_CNT2 | SGINT_TCWORD_CLAT, &sgint->tcword);
+		lsb = readb(&sgint->tcnt2);
+		msb = readb(&sgint->tcnt2);
 		ct1 = read_c0_count();
 	} while (msb);
 
 	/* Stop the counter. */
-	sgint->tcword = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL |
-			 SGINT_TCWORD_MSWST);
+	writeb(sgint->tcword, (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL |
+			       SGINT_TCWORD_MSWST));
 	/*
 	 * Return the difference, this is how far the r4k counter increments
 	 * for every 1/HZ seconds. We round off the nearest 1 MHz of master
 	 * clock (= 1000000 / HZ / 2).
 	 */
-	/*return (ct1 - ct0 + (500000/HZ/2)) / (500000/HZ) * (500000/HZ);*/
+
 	return (ct1 - ct0) / (500000/HZ) * (500000/HZ);
 }
 
diff --git a/arch/mips/sibyte/Kconfig b/arch/mips/sibyte/Kconfig
index bdf24a7..e6b003e 100644
--- a/arch/mips/sibyte/Kconfig
+++ b/arch/mips/sibyte/Kconfig
@@ -2,6 +2,7 @@
 	bool
 	select HW_HAS_PCI
 	select SIBYTE_ENABLE_LDT_IF_PCI
+	select SIBYTE_HAS_ZBUS_PROFILING
 	select SIBYTE_SB1xxx_SOC
 	select SYS_SUPPORTS_SMP
 
@@ -34,6 +35,7 @@
 config SIBYTE_BCM1x80
 	bool
 	select HW_HAS_PCI
+	select SIBYTE_HAS_ZBUS_PROFILING
 	select SIBYTE_SB1xxx_SOC
 	select SYS_SUPPORTS_SMP
 
diff --git a/arch/mips/sibyte/common/Makefile b/arch/mips/sibyte/common/Makefile
new file mode 100644
index 0000000..8a06a4f
--- /dev/null
+++ b/arch/mips/sibyte/common/Makefile
@@ -0,0 +1,5 @@
+obj-y :=
+
+obj-$(CONFIG_SIBYTE_TBPROF)		+= sb_tbprof.o
+
+EXTRA_AFLAGS := $(CFLAGS)
diff --git a/arch/mips/sibyte/sb1250/bcm1250_tbprof.c b/arch/mips/sibyte/common/sb_tbprof.c
similarity index 80%
rename from arch/mips/sibyte/sb1250/bcm1250_tbprof.c
rename to arch/mips/sibyte/common/sb_tbprof.c
index ea0ca13..4fcdaa8 100644
--- a/arch/mips/sibyte/sb1250/bcm1250_tbprof.c
+++ b/arch/mips/sibyte/common/sb_tbprof.c
@@ -31,14 +31,29 @@
 #include <linux/vmalloc.h>
 #include <linux/fs.h>
 #include <linux/errno.h>
-#include <linux/types.h>
 #include <linux/wait.h>
-
 #include <asm/io.h>
 #include <asm/sibyte/sb1250.h>
+
+#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
+#include <asm/sibyte/bcm1480_regs.h>
+#include <asm/sibyte/bcm1480_scd.h>
+#include <asm/sibyte/bcm1480_int.h>
+#elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X)
 #include <asm/sibyte/sb1250_regs.h>
 #include <asm/sibyte/sb1250_scd.h>
 #include <asm/sibyte/sb1250_int.h>
+#else
+#error invalid SiByte UART configuation
+#endif
+
+#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
+#undef K_INT_TRACE_FREEZE
+#define K_INT_TRACE_FREEZE K_BCM1480_INT_TRACE_FREEZE
+#undef K_INT_PERF_CNT
+#define K_INT_PERF_CNT K_BCM1480_INT_PERF_CNT
+#endif
+
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
@@ -118,7 +133,7 @@
 			: /* inputs */ \
 			: /* modifies */ "$8" )
 
-#define DEVNAME "bcm1250_tbprof"
+#define DEVNAME "sb_tbprof"
 
 #define TB_FULL (sbp.next_tb_sample == MAX_TB_SAMPLES)
 
@@ -132,6 +147,7 @@
  * overflow.
  *
  * We map the interrupt for trace_buffer_freeze to handle it on CPU 0.
+ *
  */
 
 static u64 tb_period;
@@ -143,25 +159,36 @@
 	u64 tb_options = M_SCD_TRACE_CFG_FREEZE_FULL;
 
 	/*
-	 * Generate an SCD_PERFCNT interrupt in TB_PERIOD Zclks to trigger
-	 *start of trace.  XXX vary sampling period
+	 * Generate an SCD_PERFCNT interrupt in TB_PERIOD Zclks to
+	 * trigger start of trace.  XXX vary sampling period
 	 */
 	__raw_writeq(0, IOADDR(A_SCD_PERF_CNT_1));
 	scdperfcnt = __raw_readq(IOADDR(A_SCD_PERF_CNT_CFG));
 
 	/*
-	 * Unfortunately, in Pass 2 we must clear all counters to knock down a
-	 * previous interrupt request.  This means that bus profiling requires
-	 * ALL of the SCD perf counters.
+	 * Unfortunately, in Pass 2 we must clear all counters to knock down
+	 * a previous interrupt request.  This means that bus profiling
+	 * requires ALL of the SCD perf counters.
 	 */
+#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
+	__raw_writeq((scdperfcnt & ~M_SPC_CFG_SRC1) |
+						/* keep counters 0,2,3,4,5,6,7 as is */
+		     V_SPC_CFG_SRC1(1),		/* counter 1 counts cycles */
+		     IOADDR(A_BCM1480_SCD_PERF_CNT_CFG0));
+	__raw_writeq(
+		     M_SPC_CFG_ENABLE |		/* enable counting */
+		     M_SPC_CFG_CLEAR |		/* clear all counters */
+		     V_SPC_CFG_SRC1(1),		/* counter 1 counts cycles */
+		     IOADDR(A_BCM1480_SCD_PERF_CNT_CFG1));
+#else
 	__raw_writeq((scdperfcnt & ~M_SPC_CFG_SRC1) |
 						/* keep counters 0,2,3 as is */
 		     M_SPC_CFG_ENABLE |		/* enable counting */
 		     M_SPC_CFG_CLEAR |		/* clear all counters */
 		     V_SPC_CFG_SRC1(1),		/* counter 1 counts cycles */
 		     IOADDR(A_SCD_PERF_CNT_CFG));
+#endif
 	__raw_writeq(next, IOADDR(A_SCD_PERF_CNT_1));
-
 	/* Reset the trace buffer */
 	__raw_writeq(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));
 #if 0 && defined(M_SCD_TRACE_CFG_FORCECNT)
@@ -190,38 +217,37 @@
 			/* Subscripts decrease to put bundle in the order */
 			/*   t0 lo, t0 hi, t1 lo, t1 hi, t2 lo, t2 hi */
 			p[i - 1] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
-								/* read t2 hi */
+			/* read t2 hi */
 			p[i - 2] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
-								/* read t2 lo */
+			/* read t2 lo */
 			p[i - 3] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
-								/* read t1 hi */
+			/* read t1 hi */
 			p[i - 4] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
-								/* read t1 lo */
+			/* read t1 lo */
 			p[i - 5] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
-								/* read t0 hi */
+			/* read t0 hi */
 			p[i - 6] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
-								/* read t0 lo */
+			/* read t0 lo */
 		}
 		if (!sbp.tb_enable) {
 			pr_debug(DEVNAME ": tb_intr shutdown\n");
 			__raw_writeq(M_SCD_TRACE_CFG_RESET,
 				     IOADDR(A_SCD_TRACE_CFG));
 			sbp.tb_armed = 0;
-			wake_up(&sbp.tb_sync);
+			wake_up_interruptible(&sbp.tb_sync);
 		} else {
-			arm_tb();	/* knock down current interrupt and get another one later */
+			/* knock down current interrupt and get another one later */
+			arm_tb();
 		}
 	} else {
 		/* No more trace buffer samples */
 		pr_debug(DEVNAME ": tb_intr full\n");
 		__raw_writeq(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));
 		sbp.tb_armed = 0;
-		if (!sbp.tb_enable) {
-			wake_up(&sbp.tb_sync);
-		}
-		wake_up(&sbp.tb_read);
+		if (!sbp.tb_enable)
+			wake_up_interruptible(&sbp.tb_sync);
+		wake_up_interruptible(&sbp.tb_read);
 	}
-
 	return IRQ_HANDLED;
 }
 
@@ -250,8 +276,8 @@
 	sbp.next_tb_sample = 0;
 	filp->f_pos = 0;
 
-	err = request_irq(K_INT_TRACE_FREEZE, sbprof_tb_intr, 0,
-	                DEVNAME " trace freeze", &sbp);
+	err = request_irq (K_INT_TRACE_FREEZE, sbprof_tb_intr, 0,
+			   DEVNAME " trace freeze", &sbp);
 	if (err)
 		return -EBUSY;
 
@@ -263,23 +289,29 @@
 		     IOADDR(A_SCD_PERF_CNT_CFG));
 
 	/*
-	 * We grab this interrupt to prevent others from trying to use it, even
-	 * though we don't want to service the interrupts (they only feed into
-	 * the trace-on-interrupt mechanism)
+	 * We grab this interrupt to prevent others from trying to use
+         * it, even though we don't want to service the interrupts
+         * (they only feed into the trace-on-interrupt mechanism)
 	 */
-	err = request_irq(K_INT_PERF_CNT, sbprof_pc_intr, 0,
-	                DEVNAME " scd perfcnt", &sbp);
-	if (err)
-		goto out_free_irq;
+	if (request_irq(K_INT_PERF_CNT, sbprof_pc_intr, 0, DEVNAME " scd perfcnt", &sbp)) {
+		free_irq(K_INT_TRACE_FREEZE, &sbp);
+		return -EBUSY;
+	}
 
 	/*
-	 * I need the core to mask these, but the interrupt mapper to pass them
-	 * through.  I am exploiting my knowledge that cp0_status masks out
-	 * IP[5]. krw
+	 * I need the core to mask these, but the interrupt mapper to
+	 *  pass them through.  I am exploiting my knowledge that
+	 *  cp0_status masks out IP[5]. krw
 	 */
+#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
+	__raw_writeq(K_BCM1480_INT_MAP_I3,
+		     IOADDR(A_BCM1480_IMR_REGISTER(0, R_BCM1480_IMR_INTERRUPT_MAP_BASE_L) +
+			    ((K_BCM1480_INT_PERF_CNT & 0x3f) << 3)));
+#else
 	__raw_writeq(K_INT_MAP_I3,
 		     IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) +
 			    (K_INT_PERF_CNT << 3)));
+#endif
 
 	/* Initialize address traps */
 	__raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_0));
@@ -298,7 +330,7 @@
 	__raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_3));
 
 	/* Initialize Trace Event 0-7 */
-	/*				when interrupt */
+	/*				when interrupt  */
 	__raw_writeq(M_SCD_TREVT_INTERRUPT, IOADDR(A_SCD_TRACE_EVENT_0));
 	__raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_1));
 	__raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_2));
@@ -324,24 +356,23 @@
 	__raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_7));
 
 	/* Now indicate the PERF_CNT interrupt as a trace-relevant interrupt */
+#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
+	__raw_writeq(1ULL << (K_BCM1480_INT_PERF_CNT & 0x3f),
+		     IOADDR(A_BCM1480_IMR_REGISTER(0, R_BCM1480_IMR_INTERRUPT_TRACE_L)));
+#else
 	__raw_writeq(1ULL << K_INT_PERF_CNT,
 		     IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_TRACE)));
-
+#endif
 	arm_tb();
 
 	pr_debug(DEVNAME ": done starting\n");
 
 	return 0;
-
-out_free_irq:
-	free_irq(K_INT_TRACE_FREEZE, &sbp);
-
-	return err;
 }
 
 static int sbprof_zbprof_stop(void)
 {
-	int err;
+	int err = 0;
 
 	pr_debug(DEVNAME ": stopping\n");
 
@@ -365,7 +396,7 @@
 
 	pr_debug(DEVNAME ": done stopping\n");
 
-	return 0;
+	return err;
 }
 
 static int sbprof_tb_open(struct inode *inode, struct file *filp)
@@ -380,11 +411,9 @@
 		return -EBUSY;
 
 	memset(&sbp, 0, sizeof(struct sbprof_tb));
-
 	sbp.sbprof_tbbuf = vmalloc(MAX_TBSAMPLE_BYTES);
 	if (!sbp.sbprof_tbbuf)
 		return -ENOMEM;
-
 	memset(sbp.sbprof_tbbuf, 0, MAX_TBSAMPLE_BYTES);
 	init_waitqueue_head(&sbp.tb_sync);
 	init_waitqueue_head(&sbp.tb_read);
@@ -397,8 +426,9 @@
 
 static int sbprof_tb_release(struct inode *inode, struct file *filp)
 {
-	int minor = iminor(inode);
+	int minor;
 
+	minor = iminor(inode);
 	if (minor != 0 || !sbp.open)
 		return -ENODEV;
 
@@ -419,10 +449,10 @@
 			      size_t size, loff_t *offp)
 {
 	int cur_sample, sample_off, cur_count, sample_left;
-	long  cur_off = *offp;
-	char *dest    =	 buf;
-	int   count   =	 0;
 	char *src;
+	int   count   =	 0;
+	char *dest    =	 buf;
+	long  cur_off = *offp;
 
 	if (!access_ok(VERIFY_WRITE, buf, size))
 		return -EFAULT;
@@ -445,7 +475,6 @@
 			mutex_unlock(&sbp.lock);
 			return err;
 		}
-
 		pr_debug(DEVNAME ": read from sample %d, %d bytes\n",
 		         cur_sample, cur_count);
 		size -= cur_count;
@@ -461,45 +490,46 @@
 		dest += cur_count;
 		count += cur_count;
 	}
-
 	*offp = cur_off;
 	mutex_unlock(&sbp.lock);
 
 	return count;
 }
 
-static long sbprof_tb_ioctl(struct file *filp, unsigned int command,
-	unsigned long arg)
+static long sbprof_tb_ioctl(struct file *filp,
+			    unsigned int command,
+			    unsigned long arg)
 {
-	int error = 0;
+	int err = 0;
 
 	switch (command) {
 	case SBPROF_ZBSTART:
 		mutex_lock(&sbp.lock);
-		error = sbprof_zbprof_start(filp);
+		err = sbprof_zbprof_start(filp);
 		mutex_unlock(&sbp.lock);
 		break;
 
 	case SBPROF_ZBSTOP:
 		mutex_lock(&sbp.lock);
-		error = sbprof_zbprof_stop();
+		err = sbprof_zbprof_stop();
 		mutex_unlock(&sbp.lock);
 		break;
 
-	case SBPROF_ZBWAITFULL:
-		error = wait_event_interruptible(sbp.tb_read, TB_FULL);
-		if (error)
+	case SBPROF_ZBWAITFULL: {
+		err = wait_event_interruptible(sbp.tb_read, TB_FULL);
+		if (err)
 			break;
 
-		error = put_user(TB_FULL, (int *) arg);
-		break;
-
-	default:
-		error = -EINVAL;
+		err = put_user(TB_FULL, (int *) arg);
 		break;
 	}
 
-	return error;
+	default:
+		err = -EINVAL;
+		break;
+	}
+
+	return err;
 }
 
 static const struct file_operations sbprof_tb_fops = {
@@ -544,8 +574,8 @@
 
 	sbp.open = 0;
 	tb_period = zbbus_mhz * 10000LL;
-	pr_info(DEVNAME ": initialized - tb_period = %lld\n", tb_period);
-
+	pr_info(DEVNAME ": initialized - tb_period = %lld\n",
+		(long long) tb_period);
 	return 0;
 
 out_class:
diff --git a/arch/mips/sibyte/sb1250/Makefile b/arch/mips/sibyte/sb1250/Makefile
index 04c0f1a..df662c6 100644
--- a/arch/mips/sibyte/sb1250/Makefile
+++ b/arch/mips/sibyte/sb1250/Makefile
@@ -1,6 +1,5 @@
 obj-y := setup.o irq.o time.o
 
 obj-$(CONFIG_SMP)			+= smp.o
-obj-$(CONFIG_SIBYTE_TBPROF)		+= bcm1250_tbprof.o
 obj-$(CONFIG_SIBYTE_STANDALONE)		+= prom.o
 obj-$(CONFIG_SIBYTE_BUS_WATCHER)	+= bus_watcher.o
diff --git a/arch/mips/sni/pcimt.c b/arch/mips/sni/pcimt.c
index 8e8593b..9ee208d 100644
--- a/arch/mips/sni/pcimt.c
+++ b/arch/mips/sni/pcimt.c
@@ -91,7 +91,7 @@
 };
 
 static struct resource sni_io_resource = {
-	.start	= 0x00001000UL,
+	.start	= 0x00000000UL,
 	.end	= 0x03bfffffUL,
 	.name	= "PCIMT IO MEM",
 	.flags	= IORESOURCE_IO,
@@ -132,107 +132,19 @@
 };
 
 static struct resource sni_mem_resource = {
-	.start	= 0x10000000UL,
-	.end	= 0xffffffffUL,
+	.start	= 0x18000000UL,
+	.end	= 0x1fbfffffUL,
 	.name	= "PCIMT PCI MEM",
 	.flags	= IORESOURCE_MEM
 };
 
-/*
- * The RM200/RM300 has a few holes in it's PCI/EISA memory address space used
- * for other purposes.  Be paranoid and allocate all of the before the PCI
- * code gets a chance to to map anything else there ...
- *
- * This leaves the following areas available:
- *
- * 0x10000000 - 0x1009ffff (640kB) PCI/EISA/ISA Bus Memory
- * 0x10100000 - 0x13ffffff ( 15MB) PCI/EISA/ISA Bus Memory
- * 0x18000000 - 0x1fbfffff (124MB) PCI/EISA Bus Memory
- * 0x1ff08000 - 0x1ffeffff (816kB) PCI/EISA Bus Memory
- * 0xa0000000 - 0xffffffff (1.5GB) PCI/EISA Bus Memory
- */
-static struct resource pcimt_mem_resources[] = {
-	{
-		.start	= 0x100a0000,
-		.end	= 0x100bffff,
-		.name	= "Video RAM area",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x100c0000,
-		.end	= 0x100fffff,
-		.name	= "ISA Reserved",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x14000000,
-		.end	= 0x17bfffff,
-		.name	= "PCI IO",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x17c00000,
-		.end	= 0x17ffffff,
-		.name	= "Cache Replacement Area",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x1a000000,
-		.end	= 0x1a000003,
-		.name	= "PCI INT Acknowledge",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x1fc00000,
-		.end	= 0x1fc7ffff,
-		.name	= "Boot PROM",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x1fc80000,
-		.end	= 0x1fcfffff,
-		.name	= "Diag PROM",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x1fd00000,
-		.end	= 0x1fdfffff,
-		.name	= "X-Bus",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x1fe00000,
-		.end	= 0x1fefffff,
-		.name	= "BIOS map",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x1ff00000,
-		.end	= 0x1ff7ffff,
-		.name	= "NVRAM / EEPROM",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x1fff0000,
-		.end	= 0x1fffefff,
-		.name	= "ASIC PCI",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x1ffff000,
-		.end	= 0x1fffffff,
-		.name	= "MP Agent",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x20000000,
-		.end	= 0x9fffffff,
-		.name	= "Main Memory",
-		.flags	= IORESOURCE_BUSY
-	}
-};
-
 static void __init sni_pcimt_resource_init(void)
 {
 	int i;
 
 	/* request I/O space for devices used on all i[345]86 PCs */
 	for (i = 0; i < ARRAY_SIZE(pcimt_io_resources); i++)
-		request_resource(&ioport_resource, pcimt_io_resources + i);
-
-	/* request mem space for pcimt-specific devices */
-	for (i = 0; i < ARRAY_SIZE(pcimt_mem_resources); i++)
-		request_resource(&sni_mem_resource, pcimt_mem_resources + i);
-
-	ioport_resource.end = sni_io_resource.end;
+		request_resource(&sni_io_resource, pcimt_io_resources + i);
 }
 
 extern struct pci_ops sni_pcimt_ops;
@@ -240,9 +152,10 @@
 static struct pci_controller sni_controller = {
 	.pci_ops	= &sni_pcimt_ops,
 	.mem_resource	= &sni_mem_resource,
-	.mem_offset	= 0x10000000UL,
+	.mem_offset	= 0x00000000UL,
 	.io_resource	= &sni_io_resource,
-	.io_offset	= 0x00000000UL
+	.io_offset	= 0x00000000UL,
+	.io_map_base    = SNI_PORT_BASE
 };
 
 static void enable_pcimt_irq(unsigned int irq)
@@ -363,15 +276,17 @@
 
 void sni_pcimt_init(void)
 {
-	sni_pcimt_resource_init();
 	sni_pcimt_detect();
 	sni_pcimt_sc_init();
 	rtc_mips_get_time = mc146818_get_cmos_time;
 	rtc_mips_set_time = mc146818_set_rtc_mmss;
 	board_time_init = sni_cpu_time_init;
+	ioport_resource.end = sni_io_resource.end;
 #ifdef CONFIG_PCI
+	PCIBIOS_MIN_IO = 0x9000;
 	register_pci_controller(&sni_controller);
 #endif
+	sni_pcimt_resource_init();
 }
 
 static int __init snirm_pcimt_setup_devinit(void)
diff --git a/arch/mips/sni/pcit.c b/arch/mips/sni/pcit.c
index 1dfc3f0..00d151f 100644
--- a/arch/mips/sni/pcit.c
+++ b/arch/mips/sni/pcit.c
@@ -43,7 +43,7 @@
 };
 
 static struct plat_serial8250_port pcit_cplus_data[] = {
-	PORT(0x3f8, 4),
+	PORT(0x3f8, 0),
 	PORT(0x2f8, 3),
 	PORT(0x3e8, 4),
 	PORT(0x2e8, 3),
@@ -59,9 +59,9 @@
 };
 
 static struct resource sni_io_resource = {
-	.start	= 0x00001000UL,
+	.start	= 0x00000000UL,
 	.end	= 0x03bfffffUL,
-	.name	= "PCIT IO MEM",
+	.name	= "PCIT IO",
 	.flags	= IORESOURCE_IO,
 };
 
@@ -92,6 +92,11 @@
 		.name	= "dma2",
 		.flags	= IORESOURCE_BUSY
 	}, {
+		.start	=  0xcf8,
+		.end	= 0xcfb,
+		.name	= "PCI config addr",
+		.flags	= IORESOURCE_BUSY
+	}, {
 		.start	=  0xcfc,
 		.end	= 0xcff,
 		.name	= "PCI config data",
@@ -100,107 +105,19 @@
 };
 
 static struct resource sni_mem_resource = {
-	.start	= 0x10000000UL,
-	.end	= 0xffffffffUL,
+	.start	= 0x18000000UL,
+	.end	= 0x1fbfffffUL,
 	.name	= "PCIT PCI MEM",
 	.flags	= IORESOURCE_MEM
 };
 
-/*
- * The RM200/RM300 has a few holes in it's PCI/EISA memory address space used
- * for other purposes.  Be paranoid and allocate all of the before the PCI
- * code gets a chance to to map anything else there ...
- *
- * This leaves the following areas available:
- *
- * 0x10000000 - 0x1009ffff (640kB) PCI/EISA/ISA Bus Memory
- * 0x10100000 - 0x13ffffff ( 15MB) PCI/EISA/ISA Bus Memory
- * 0x18000000 - 0x1fbfffff (124MB) PCI/EISA Bus Memory
- * 0x1ff08000 - 0x1ffeffff (816kB) PCI/EISA Bus Memory
- * 0xa0000000 - 0xffffffff (1.5GB) PCI/EISA Bus Memory
- */
-static struct resource pcit_mem_resources[] = {
-	{
-		.start	= 0x14000000,
-		.end	= 0x17bfffff,
-		.name	= "PCI IO",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x17c00000,
-		.end	= 0x17ffffff,
-		.name	= "Cache Replacement Area",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x180a0000,
-		.end	= 0x180bffff,
-		.name	= "Video RAM area",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x180c0000,
-		.end	= 0x180fffff,
-		.name	= "ISA Reserved",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x19000000,
-		.end	= 0x1fbfffff,
-		.name	= "PCI MEM",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x1fc00000,
-		.end	= 0x1fc7ffff,
-		.name	= "Boot PROM",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x1fc80000,
-		.end	= 0x1fcfffff,
-		.name	= "Diag PROM",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x1fd00000,
-		.end	= 0x1fdfffff,
-		.name	= "X-Bus",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x1fe00000,
-		.end	= 0x1fefffff,
-		.name	= "BIOS map",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x1ff00000,
-		.end	= 0x1ff7ffff,
-		.name	= "NVRAM / EEPROM",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x1fff0000,
-		.end	= 0x1fffefff,
-		.name	= "MAUI ASIC",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x1ffff000,
-		.end	= 0x1fffffff,
-		.name	= "MP Agent",
-		.flags	= IORESOURCE_BUSY
-	}, {
-		.start	= 0x20000000,
-		.end	= 0x9fffffff,
-		.name	= "Main Memory",
-		.flags	= IORESOURCE_BUSY
-	}
-};
-
 static void __init sni_pcit_resource_init(void)
 {
 	int i;
 
 	/* request I/O space for devices used on all i[345]86 PCs */
 	for (i = 0; i < ARRAY_SIZE(pcit_io_resources); i++)
-		request_resource(&ioport_resource, pcit_io_resources + i);
-
-	/* request mem space for pcimt-specific devices */
-	for (i = 0; i < ARRAY_SIZE(pcit_mem_resources); i++)
-		request_resource(&sni_mem_resource, pcit_mem_resources + i);
-
-	ioport_resource.end = sni_io_resource.end;
+		request_resource(&sni_io_resource, pcit_io_resources + i);
 }
 
 
@@ -209,9 +126,10 @@
 static struct pci_controller sni_pcit_controller = {
 	.pci_ops	= &sni_pcit_ops,
 	.mem_resource	= &sni_mem_resource,
-	.mem_offset	= 0x10000000UL,
+	.mem_offset	= 0x00000000UL,
 	.io_resource	= &sni_io_resource,
-	.io_offset	= 0x00000000UL
+	.io_offset	= 0x00000000UL,
+	.io_map_base    = SNI_PORT_BASE
 };
 
 static void enable_pcit_irq(unsigned int irq)
@@ -262,7 +180,7 @@
 	int irq;
 
 	clear_c0_status(IE_IRQ0);
-	irq = ffs((pending >> 16) & 0x7f);
+	irq = ffs((pending >> 16) & 0x3f);
 
 	if (likely(irq > 0))
 		do_IRQ (irq + SNI_PCIT_INT_START - 1);
@@ -289,6 +207,8 @@
 
 	if (pending & C_IRQ0)
 		pcit_hwint0();
+	else if (pending & C_IRQ1)
+		do_IRQ (MIPS_CPU_IRQ_BASE + 3);
 	else if (pending & C_IRQ2)
 		do_IRQ (MIPS_CPU_IRQ_BASE + 4);
 	else if (pending & C_IRQ3)
@@ -317,21 +237,23 @@
 	mips_cpu_irq_init();
 	for (i = SNI_PCIT_INT_START; i <= SNI_PCIT_INT_END; i++)
 		set_irq_chip(i, &pcit_irq_type);
-	*(volatile u32 *)SNI_PCIT_INT_REG = 0;
+	*(volatile u32 *)SNI_PCIT_INT_REG = 0x40000000;
 	sni_hwint = sni_pcit_hwint_cplus;
 	change_c0_status(ST0_IM, IE_IRQ0);
-	setup_irq (SNI_PCIT_INT_START + 6, &sni_isa_irq);
+	setup_irq (MIPS_CPU_IRQ_BASE + 3, &sni_isa_irq);
 }
 
 void sni_pcit_init(void)
 {
-	sni_pcit_resource_init();
 	rtc_mips_get_time = mc146818_get_cmos_time;
 	rtc_mips_set_time = mc146818_set_rtc_mmss;
 	board_time_init = sni_cpu_time_init;
+	ioport_resource.end = sni_io_resource.end;
 #ifdef CONFIG_PCI
+	PCIBIOS_MIN_IO = 0x9000;
 	register_pci_controller(&sni_pcit_controller);
 #endif
+	sni_pcit_resource_init();
 }
 
 static int __init snirm_pcit_setup_devinit(void)
diff --git a/arch/mips/vr41xx/Kconfig b/arch/mips/vr41xx/Kconfig
index 92f41f6..8f4d3e7 100644
--- a/arch/mips/vr41xx/Kconfig
+++ b/arch/mips/vr41xx/Kconfig
@@ -1,6 +1,10 @@
-config CASIO_E55
-	bool "Support for CASIO CASSIOPEIA E-10/15/55/65"
+choice
+	prompt "Machine type"
 	depends on MACH_VR41XX
+	default TANBAC_TB022X
+
+config CASIO_E55
+	bool "CASIO CASSIOPEIA E-10/15/55/65"
 	select DMA_NONCOHERENT
 	select IRQ_CPU
 	select ISA
@@ -8,8 +12,7 @@
 	select SYS_SUPPORTS_LITTLE_ENDIAN
 
 config IBM_WORKPAD
-	bool "Support for IBM WorkPad z50"
-	depends on MACH_VR41XX
+	bool "IBM WorkPad z50"
 	select DMA_NONCOHERENT
 	select IRQ_CPU
 	select ISA
@@ -17,26 +20,18 @@
 	select SYS_SUPPORTS_LITTLE_ENDIAN
 
 config NEC_CMBVR4133
-	bool "Support for NEC CMB-VR4133"
-	depends on MACH_VR41XX
+	bool "NEC CMB-VR4133"
 	select DMA_NONCOHERENT
 	select IRQ_CPU
 	select HW_HAS_PCI
 	select SYS_SUPPORTS_32BIT_KERNEL
 	select SYS_SUPPORTS_LITTLE_ENDIAN
 
-config ROCKHOPPER
-	bool "Support for Rockhopper baseboard"
-	depends on NEC_CMBVR4133
-	select I8259
-	select HAVE_STD_PC_SERIAL_PORT
-
 config TANBAC_TB022X
-	bool "Support for TANBAC VR4131 multichip module and TANBAC VR4131DIMM"
-	depends on MACH_VR41XX
+	bool "TANBAC VR4131 multichip module and TANBAC VR4131DIMM"
 	select DMA_NONCOHERENT
-	select HW_HAS_PCI
 	select IRQ_CPU
+	select HW_HAS_PCI
 	select SYS_SUPPORTS_32BIT_KERNEL
 	select SYS_SUPPORTS_LITTLE_ENDIAN
 	help
@@ -46,40 +41,65 @@
 	  Please refer to <http://www.tanbac.co.jp/>
 	  about VR4131 multichip module and VR4131DIMM.
 
-config TANBAC_TB0226
-	bool "Support for TANBAC Mbase(TB0226)"
+config VICTOR_MPC30X
+	bool "Victor MP-C303/304"
+	select DMA_NONCOHERENT
+	select IRQ_CPU
+	select HW_HAS_PCI
+	select PCI_VR41XX
+	select SYS_SUPPORTS_32BIT_KERNEL
+	select SYS_SUPPORTS_LITTLE_ENDIAN
+
+config ZAO_CAPCELLA
+	bool "ZAO Networks Capcella"
+	select DMA_NONCOHERENT
+	select IRQ_CPU
+	select HW_HAS_PCI
+	select PCI_VR41XX
+	select SYS_SUPPORTS_32BIT_KERNEL
+	select SYS_SUPPORTS_LITTLE_ENDIAN
+
+endchoice
+
+config ROCKHOPPER
+	bool "Support for Rockhopper base board"
+	depends on NEC_CMBVR4133
+	select PCI_VR41XX
+	select I8259
+	select HAVE_STD_PC_SERIAL_PORT
+
+choice
+	prompt "Base board type"
 	depends on TANBAC_TB022X
+	default TANBAC_TB0287
+
+config TANBAC_TB0219
+	bool "TANBAC DIMM Evaluation Kit(TB0219)"
 	select GPIO_VR41XX
+	select PCI_VR41XX
+	help
+	  The TANBAC DIMM Evaluation Kit(TB0219) is a MIPS-based platform
+	  manufactured by TANBAC.
+	  Please refer to <http://www.tanbac.co.jp/> about DIMM Evaluation Kit.
+
+config TANBAC_TB0226
+	bool "TANBAC Mbase(TB0226)"
+	select GPIO_VR41XX
+	select PCI_VR41XX
 	help
 	  The TANBAC Mbase(TB0226) is a MIPS-based platform
 	  manufactured by TANBAC.
 	  Please refer to <http://www.tanbac.co.jp/> about Mbase.
 
 config TANBAC_TB0287
-	bool "Support for TANBAC Mini-ITX DIMM base(TB0287)"
-	depends on TANBAC_TB022X
+	bool "TANBAC Mini-ITX DIMM base(TB0287)"
+	select PCI_VR41XX
 	help
 	  The TANBAC Mini-ITX DIMM base(TB0287) is a MIPS-based platform
 	  manufactured by TANBAC.
 	  Please refer to <http://www.tanbac.co.jp/> about Mini-ITX DIMM base.
 
-config VICTOR_MPC30X
-	bool "Support for Victor MP-C303/304"
-	depends on MACH_VR41XX
-	select DMA_NONCOHERENT
-	select HW_HAS_PCI
-	select IRQ_CPU
-	select SYS_SUPPORTS_32BIT_KERNEL
-	select SYS_SUPPORTS_LITTLE_ENDIAN
-
-config ZAO_CAPCELLA
-	bool "Support for ZAO Networks Capcella"
-	depends on MACH_VR41XX
-	select DMA_NONCOHERENT
-	select HW_HAS_PCI
-	select IRQ_CPU
-	select SYS_SUPPORTS_32BIT_KERNEL
-	select SYS_SUPPORTS_LITTLE_ENDIAN
+endchoice
 
 config PCI_VR41XX
 	bool "Add PCI control unit support of NEC VR4100 series"
diff --git a/arch/parisc/configs/c3000_defconfig b/arch/parisc/configs/c3000_defconfig
index 782906b6..eb2f9a3 100644
--- a/arch/parisc/configs/c3000_defconfig
+++ b/arch/parisc/configs/c3000_defconfig
@@ -435,7 +435,6 @@
 # CONFIG_SCSI_SATA_ULI is not set
 CONFIG_SCSI_SATA_VIA=m
 # CONFIG_SCSI_SATA_VITESSE is not set
-CONFIG_SCSI_SATA_INTEL_COMBINED=y
 # CONFIG_SCSI_DMX3191D is not set
 # CONFIG_SCSI_FUTURE_DOMAIN is not set
 # CONFIG_SCSI_IPS is not set
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 6dfbd52..a54a9a2e 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -11,6 +11,11 @@
 	  This option selects whether a 32-bit or a 64-bit kernel
 	  will be built.
 
+config PPC_PM_NEEDS_RTC_LIB
+	bool
+	select RTC_LIB
+	default y if PM
+
 config PPC32
 	bool
 	default y if !PPC64
@@ -89,7 +94,7 @@
 
 config ARCH_MAY_HAVE_PC_FDC
 	bool
-	default y
+	default !PPC_PSERIES || PCI
 
 config PPC_OF
 	def_bool y
@@ -157,17 +162,20 @@
 	select FSL_SOC
 	select 83xx
 	select PPC_FPU
+	select WANT_DEVICE_TREE
 
 config PPC_85xx
 	bool "Freescale 85xx"
 	select E500
 	select FSL_SOC
 	select 85xx
+	select WANT_DEVICE_TREE
 
 config PPC_86xx
 	bool "Freescale 86xx"
 	select 6xx
 	select FSL_SOC
+	select FSL_PCIE
 	select PPC_FPU
 	select ALTIVEC
 	help
@@ -186,7 +194,6 @@
 	bool "AMCC 44x"
 	select PPC_DCR_NATIVE
 
-
 config E200
 	bool "Freescale e200"
 
@@ -367,394 +374,7 @@
 
 source "init/Kconfig"
 
-menu "Platform support"
-	depends on PPC64 || CLASSIC32
-
-choice
-	prompt "Machine type"
-	default PPC_MULTIPLATFORM
-
-config PPC_MULTIPLATFORM
-	bool "Generic desktop/server/laptop"
-	help
-	  Select this option if configuring for an IBM pSeries or
-	  RS/6000 machine, an Apple machine, or a PReP, CHRP,
-	  Maple or Cell-based machine.
-
-config EMBEDDED6xx
-	bool "Embedded 6xx/7xx/7xxx-based board"
-	depends on PPC32 && (BROKEN||BROKEN_ON_SMP)
-
-config APUS
-	bool "Amiga-APUS"
-	depends on PPC32 && BROKEN
-	help
-	  Select APUS if configuring for a PowerUP Amiga.
-	  More information is available at:
-	  <http://linux-apus.sourceforge.net/>.
-endchoice
-
-config QUICC_ENGINE
-	bool
-	depends on PPC_MPC836x || PPC_MPC832x
-	default y
-	help
-	  The QUICC Engine (QE) is a new generation of communications
-	  coprocessors on Freescale embedded CPUs (akin to CPM in older chips).
-	  Selecting this option means that you wish to build a kernel
-	  for a machine with a QE coprocessor.
-
-config PPC_PSERIES
-	depends on PPC_MULTIPLATFORM && PPC64
-	bool "IBM pSeries & new (POWER5-based) iSeries"
-	select MPIC
-	select PPC_I8259
-	select PPC_RTAS
-	select RTAS_ERROR_LOGGING
-	select PPC_UDBG_16550
-	select PPC_NATIVE
-	default y
-
-config PPC_ISERIES
-	bool "IBM Legacy iSeries"
-	depends on PPC_MULTIPLATFORM && PPC64
-	select PPC_INDIRECT_IO
-
-config PPC_CHRP
-	bool "Common Hardware Reference Platform (CHRP) based machines"
-	depends on PPC_MULTIPLATFORM && PPC32
-	select MPIC
-	select PPC_I8259
-	select PPC_INDIRECT_PCI
-	select PPC_RTAS
-	select PPC_MPC106
-	select PPC_UDBG_16550
-	select PPC_NATIVE
-	default y
-
-config PPC_MPC52xx
-	bool
-	default n
-
-config PPC_MPC5200
-	bool
-	select PPC_MPC52xx
-	default n
-
-config PPC_MPC5200_BUGFIX
-	bool "MPC5200 (L25R) bugfix support"
-	depends on PPC_MPC5200
-	default n
-	help
-	  Enable workarounds for original MPC5200 errata.  This is not required
-	  for MPC5200B based boards.
-
-	  It is safe to say 'Y' here
-
-config PPC_EFIKA
-	bool "bPlan Efika 5k2. MPC5200B based computer"
-	depends on PPC_MULTIPLATFORM && PPC32
-	select PPC_RTAS
-	select RTAS_PROC
-	select PPC_MPC52xx
-	select PPC_NATIVE
-	default n
-
-config PPC_LITE5200
-	bool "Freescale Lite5200 Eval Board"
-	depends on PPC_MULTIPLATFORM && PPC32
-	select PPC_MPC5200
-	default n
-
-config PPC_PMAC
-	bool "Apple PowerMac based machines"
-	depends on PPC_MULTIPLATFORM
-	select MPIC
-	select PPC_INDIRECT_PCI if PPC32
-	select PPC_MPC106 if PPC32
-	select PPC_NATIVE
-	default y
-
-config PPC_PMAC64
-	bool
-	depends on PPC_PMAC && POWER4
-	select MPIC
-	select U3_DART
-	select MPIC_BROKEN_U3
-	select GENERIC_TBSYNC
-	select PPC_970_NAP
-	default y
-
-config PPC_PREP
-	bool "PowerPC Reference Platform (PReP) based machines"
-	depends on PPC_MULTIPLATFORM && PPC32 && BROKEN
-	select MPIC
-	select PPC_I8259
-	select PPC_INDIRECT_PCI
-	select PPC_UDBG_16550
-	select PPC_NATIVE
-	default n
-
-config PPC_MAPLE
-	depends on PPC_MULTIPLATFORM && PPC64
-	bool "Maple 970FX Evaluation Board"
-	select MPIC
-	select U3_DART
-	select MPIC_BROKEN_U3
-	select GENERIC_TBSYNC
-	select PPC_UDBG_16550
-	select PPC_970_NAP
-	select PPC_NATIVE
-	select PPC_RTAS
-	select MMIO_NVRAM
-	select ATA_NONSTANDARD if ATA
-	default n
-	help
-          This option enables support for the Maple 970FX Evaluation Board.
-	  For more information, refer to <http://www.970eval.com>
-
-config PPC_PASEMI
-	depends on PPC_MULTIPLATFORM && PPC64
-	bool "PA Semi SoC-based platforms"
-	default n
-	select MPIC
-	select PPC_UDBG_16550
-	select GENERIC_TBSYNC
-	select PPC_NATIVE
-	help
-	  This option enables support for PA Semi's PWRficient line
-	  of SoC processors, including PA6T-1682M
-
-config PPC_CELL
-	bool
-	default n
-
-config PPC_CELL_NATIVE
-	bool
-	select PPC_CELL
-	select PPC_DCR_MMIO
-	select PPC_OF_PLATFORM_PCI
-	select PPC_INDIRECT_IO
-	select PPC_NATIVE
-	select MPIC
-	default n
-
-config PPC_IBM_CELL_BLADE
-	bool "IBM Cell Blade"
-	depends on PPC_MULTIPLATFORM && PPC64
-	select PPC_CELL_NATIVE
-	select PPC_RTAS
-	select MMIO_NVRAM
-	select PPC_UDBG_16550
-	select UDBG_RTAS_CONSOLE
-
-config PPC_PS3
-	bool "Sony PS3 (incomplete)"
-	depends on PPC_MULTIPLATFORM && PPC64
-	select PPC_CELL
-	select USB_ARCH_HAS_OHCI
-	select USB_OHCI_LITTLE_ENDIAN
-	select USB_OHCI_BIG_ENDIAN_MMIO
-	select USB_ARCH_HAS_EHCI
-	select USB_EHCI_BIG_ENDIAN_MMIO
-	help
-	  This option enables support for the Sony PS3 game console
-	  and other platforms using the PS3 hypervisor.
-	  Support for this platform is not yet complete, so
-	  enabling this will not result in a bootable kernel on a
-	  PS3 system.
-
-config PPC_CELLEB
-	bool "Toshiba's Cell Reference Set 'Celleb' Architecture"
-	depends on PPC_MULTIPLATFORM && PPC64
-	select PPC_CELL
-	select PPC_OF_PLATFORM_PCI
-	select HAS_TXX9_SERIAL
-	select PPC_UDBG_BEAT
-	select USB_OHCI_BIG_ENDIAN_MMIO
-	select USB_EHCI_BIG_ENDIAN_MMIO
-
-config PPC_NATIVE
-	bool
-	depends on PPC_MULTIPLATFORM
-	help
-	  Support for running natively on the hardware, i.e. without
-	  a hypervisor. This option is not user-selectable but should
-	  be selected by all platforms that need it.
-
-config UDBG_RTAS_CONSOLE
-	bool "RTAS based debug console"
-	depends on PPC_RTAS
-	default n
-
-config PPC_UDBG_BEAT
-	bool "BEAT based debug console"
-	depends on PPC_CELLEB
-	default n
-
-config XICS
-	depends on PPC_PSERIES
-	bool
-	default y
-
-config U3_DART
-	bool 
-	depends on PPC_MULTIPLATFORM && PPC64
-	default n
-
-config PPC_RTAS
-	bool
-	default n
-
-config RTAS_ERROR_LOGGING
-	bool
-	depends on PPC_RTAS
-	default n
-
-config RTAS_PROC
-	bool "Proc interface to RTAS"
-	depends on PPC_RTAS
-	default y
-
-config RTAS_FLASH
-	tristate "Firmware flash interface"
-	depends on PPC64 && RTAS_PROC
-
-config PPC_PMI
-	tristate "Support for PMI"
-	depends PPC_IBM_CELL_BLADE
-	help
-	  PMI (Platform Management Interrupt) is a way to
-	  communicate with the BMC (Baseboard Mangement Controller).
-	  It is used in some IBM Cell blades.
-	default m
-
-config MMIO_NVRAM
-	bool
-	default n
-
-config MPIC_BROKEN_U3
-	bool
-	depends on PPC_MAPLE
-	default y
-
-config IBMVIO
-	depends on PPC_PSERIES || PPC_ISERIES
-	bool
-	default y
-
-config IBMEBUS
-	depends on PPC_PSERIES
-	bool "Support for GX bus based adapters"
-	help
-	  Bus device driver for GX bus based adapters.
-
-config PPC_MPC106
-	bool
-	default n
-
-config PPC_970_NAP
-	bool
-	default n
-
-config PPC_INDIRECT_IO
-	bool
-	select GENERIC_IOMAP
-	default n
-
-config GENERIC_IOMAP
-	bool
-	default n
-
-source "drivers/cpufreq/Kconfig"
-
-config CPU_FREQ_PMAC
-	bool "Support for Apple PowerBooks"
-	depends on CPU_FREQ && ADB_PMU && PPC32
-	select CPU_FREQ_TABLE
-	help
-	  This adds support for frequency switching on Apple PowerBooks,
-	  this currently includes some models of iBook & Titanium
-	  PowerBook.
-
-config CPU_FREQ_PMAC64
-	bool "Support for some Apple G5s"
-	depends on CPU_FREQ && PPC64
-	select CPU_FREQ_TABLE
-	help
-	  This adds support for frequency switching on Apple iMac G5,
-	  and some of the more recent desktop G5 machines as well.
-
-config PPC601_SYNC_FIX
-	bool "Workarounds for PPC601 bugs"
-	depends on 6xx && (PPC_PREP || PPC_PMAC)
-	help
-	  Some versions of the PPC601 (the first PowerPC chip) have bugs which
-	  mean that extra synchronization instructions are required near
-	  certain instructions, typically those that make major changes to the
-	  CPU state.  These extra instructions reduce performance slightly.
-	  If you say N here, these extra instructions will not be included,
-	  resulting in a kernel which will run faster but may not run at all
-	  on some systems with the PPC601 chip.
-
-	  If in doubt, say Y here.
-
-config TAU
-	bool "On-chip CPU temperature sensor support"
-	depends on 6xx
-	help
-	  G3 and G4 processors have an on-chip temperature sensor called the
-	  'Thermal Assist Unit (TAU)', which, in theory, can measure the on-die
-	  temperature within 2-4 degrees Celsius. This option shows the current
-	  on-die temperature in /proc/cpuinfo if the cpu supports it.
-
-	  Unfortunately, on some chip revisions, this sensor is very inaccurate
-	  and in many cases, does not work at all, so don't assume the cpu
-	  temp is actually what /proc/cpuinfo says it is.
-
-config TAU_INT
-	bool "Interrupt driven TAU driver (DANGEROUS)"
-	depends on TAU
-	---help---
-	  The TAU supports an interrupt driven mode which causes an interrupt
-	  whenever the temperature goes out of range. This is the fastest way
-	  to get notified the temp has exceeded a range. With this option off,
-	  a timer is used to re-check the temperature periodically.
-
-	  However, on some cpus it appears that the TAU interrupt hardware
-	  is buggy and can cause a situation which would lead unexplained hard
-	  lockups.
-
-	  Unless you are extending the TAU driver, or enjoy kernel/hardware
-	  debugging, leave this option off.
-
-config TAU_AVERAGE
-	bool "Average high and low temp"
-	depends on TAU
-	---help---
-	  The TAU hardware can compare the temperature to an upper and lower
-	  bound.  The default behavior is to show both the upper and lower
-	  bound in /proc/cpuinfo. If the range is large, the temperature is
-	  either changing a lot, or the TAU hardware is broken (likely on some
-	  G4's). If the range is small (around 4 degrees), the temperature is
-	  relatively stable.  If you say Y here, a single temperature value,
-	  halfway between the upper and lower bounds, will be reported in
-	  /proc/cpuinfo.
-
-	  If in doubt, say N here.
-
-endmenu
-
-source arch/powerpc/platforms/embedded6xx/Kconfig
-source arch/powerpc/platforms/4xx/Kconfig
-source arch/powerpc/platforms/82xx/Kconfig
-source arch/powerpc/platforms/83xx/Kconfig
-source arch/powerpc/platforms/85xx/Kconfig
-source arch/powerpc/platforms/86xx/Kconfig
-source arch/powerpc/platforms/8xx/Kconfig
-source arch/powerpc/platforms/cell/Kconfig
-source arch/powerpc/platforms/ps3/Kconfig
-source arch/powerpc/platforms/pasemi/Kconfig
+source "arch/powerpc/platforms/Kconfig"
 
 menu "Kernel options"
 
@@ -837,15 +457,6 @@
 
 	  Don't change this unless you know what you are doing.
 
-config EMBEDDEDBOOT
-	bool
-	depends on 8xx || 8260
-	default y
-
-config PC_KEYBOARD
-	bool "PC PS/2 style Keyboard"
-	depends on 4xx || CPM2
-
 config PPCBUG_NVRAM
 	bool "Enable reading PPCBUG NVRAM during boot" if PPLUS || LOPEC
 	default y if PPC_PREP
@@ -859,8 +470,6 @@
 	  CPU.  Generally saying Y is safe, although some problems have been
 	  reported with SMP Power Macintoshes with this option enabled.
 
-source "arch/powerpc/platforms/pseries/Kconfig"
-
 config NUMA
 	bool "NUMA support"
 	depends on PPC64
@@ -910,10 +519,10 @@
 	depends on PPC64
 	help
 	  This option changes the kernel logical page size to 64k. On machines
-          without processor support for 64k pages, the kernel will simulate
-          them by loading each individual 4k page on demand transparently,
-          while on hardware with such support, it will be used to map
-          normal application pages.
+	  without processor support for 64k pages, the kernel will simulate
+	  them by loading each individual 4k page on demand transparently,
+	  while on hardware with such support, it will be used to map
+	  normal application pages.
 
 config SCHED_SMT
 	bool "SMT (Hyperthreading) scheduler support"
@@ -931,8 +540,6 @@
 	  an image of the device tree that the kernel copies from Open
 	  Firmware or other boot firmware. If unsure, say Y here.
 
-source "arch/powerpc/platforms/prep/Kconfig"
-
 config CMDLINE_BOOL
 	bool "Default bootloader kernel arguments"
 
@@ -967,6 +574,29 @@
 
 	  If unsure, say Y. Only embedded should say N here.
 
+config WANT_DEVICE_TREE
+	bool
+	default n
+
+config DEVICE_TREE
+	string "Static device tree source file"
+	depends on WANT_DEVICE_TREE
+	help
+	  This specifies the device tree source (.dts) file to be
+	  compiled and included when building the bootwrapper.  If a
+	  relative filename is given, then it will be relative to
+	  arch/powerpc/boot/dts.  If you are not using the bootwrapper,
+	  or do not need to build a dts into the bootwrapper, this
+	  field is ignored.
+
+	  For example, this is required when building a cuImage target
+	  for an older U-Boot, which cannot pass a device tree itself.
+	  Such a kernel will not work with a newer U-Boot that tries to
+	  pass a device tree (unless you tell it not to).  If your U-Boot
+	  does not mention a device tree in "help bootm", then use the
+	  cuImage target and specify a device tree here.  Otherwise, use
+	  the uImage target and leave this field blank.
+
 endmenu
 
 config ISA_DMA_API
@@ -995,24 +625,17 @@
 	depends on PPC64 || POWER4 || 6xx && !CPM2
 	default y
 
-config MPIC
-	bool
-	default n
-
-config MPIC_WEIRD
-	bool
-	default n
-
-config PPC_I8259
-	bool
-	default n
-
 config PPC_INDIRECT_PCI
 	bool
 	depends on PCI
 	default y if 40x || 44x
 	default n
 
+config PPC_INDIRECT_PCI_BE
+	bool
+	depends PPC_INDIRECT_PCI
+	default n
+
 config EISA
 	bool
 
@@ -1022,13 +645,18 @@
 config FSL_SOC
 	bool
 
+config FSL_PCIE
+	bool
+	depends on PPC_86xx
+
 # Yes MCA RS/6000s exist but Linux-PPC does not currently support any
 config MCA
 	bool
 
 config PCI
 	bool "PCI support" if 40x || CPM2 || PPC_83xx || PPC_85xx || PPC_86xx \
-		|| PPC_MPC52xx || (EMBEDDED && PPC_ISERIES) || MPC7448HPC2 || PPC_PS3
+		|| PPC_MPC52xx || (EMBEDDED && (PPC_PSERIES || PPC_ISERIES)) \
+		|| MPC7448HPC2 || PPC_PS3
 	default y if !40x && !CPM2 && !8xx && !APUS && !PPC_83xx \
 		&& !PPC_85xx && !PPC_86xx
 	default PCI_PERMEDIA if !4xx && !CPM2 && !8xx && APUS
@@ -1228,12 +856,10 @@
 
 source "arch/powerpc/sysdev/qe_lib/Kconfig"
 
-source "arch/powerpc/platforms/iseries/Kconfig"
-
 source "lib/Kconfig"
 
 menu "Instrumentation Support"
-        depends on EXPERIMENTAL
+	depends on EXPERIMENTAL
 
 source "arch/powerpc/oprofile/Kconfig"
 
diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug
index d39d133..86aa374 100644
--- a/arch/powerpc/Kconfig.debug
+++ b/arch/powerpc/Kconfig.debug
@@ -18,6 +18,15 @@
 
 	  This option will slow down process creation somewhat.
 
+config DEBUG_PAGEALLOC
+        bool "Debug page memory allocations"
+        depends on DEBUG_KERNEL && !SOFTWARE_SUSPEND
+        help
+          Unmap pages from the kernel linear mapping after free_pages().
+          This results in a large slowdown, but helps to find certain types
+          of memory corruptions.
+
+
 config HCALL_STATS
 	bool "Hypervisor call instrumentation"
 	depends on PPC_PSERIES && DEBUG_FS
@@ -132,8 +141,7 @@
 
 config SERIAL_TEXT_DEBUG
 	bool "Support for early boot texts over serial port"
-	depends on 4xx || LOPEC || MV64X60 || PPLUS || PRPMC800 || \
-		PPC_GEN550 || PPC_MPC52xx
+	depends on 4xx
 
 config PPC_EARLY_DEBUG
 	bool "Early debugging (dangerous)"
diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile
index a00fe72..7949920 100644
--- a/arch/powerpc/Makefile
+++ b/arch/powerpc/Makefile
@@ -102,9 +102,9 @@
 # kernel considerably.
 CFLAGS += $(call cc-option,-funit-at-a-time)
 
-ifndef CONFIG_FSL_BOOKE
-CFLAGS		+= -mstring
-endif
+# Never use string load/store instructions as they are
+# often slow when they are implemented at all
+CFLAGS		+= -mno-string
 
 ifeq ($(CONFIG_6xx),y)
 CFLAGS		+= -mcpu=powerpc
@@ -148,7 +148,7 @@
 
 CPPFLAGS_vmlinux.lds	:= -Upowerpc
 
-BOOT_TARGETS = zImage zImage.initrd uImage
+BOOT_TARGETS = zImage zImage.initrd uImage cuImage
 
 PHONY += $(BOOT_TARGETS)
 
@@ -166,6 +166,9 @@
   @echo '  *_defconfig     - Select default config from arch/$(ARCH)/configs'
 endef
 
+install:
+	$(Q)$(MAKE) $(build)=$(boot) BOOTIMAGE=$(KBUILD_IMAGE) install
+
 archclean:
 	$(Q)$(MAKE) $(clean)=$(boot)
 
diff --git a/arch/powerpc/boot/.gitignore b/arch/powerpc/boot/.gitignore
index 0734b2f..eec7af7 100644
--- a/arch/powerpc/boot/.gitignore
+++ b/arch/powerpc/boot/.gitignore
@@ -18,6 +18,9 @@
 kernel-vmlinux.strip.gz
 mktree
 uImage
+cuImage
+cuImage.bin.gz
+cuImage.elf
 zImage
 zImage.chrp
 zImage.coff
diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile
index dc77940..3716594 100644
--- a/arch/powerpc/boot/Makefile
+++ b/arch/powerpc/boot/Makefile
@@ -40,10 +40,11 @@
 $(addprefix $(obj)/,$(zlib) main.o): $(addprefix $(obj)/,$(zliblinuxheader)) \
 		$(addprefix $(obj)/,$(zlibheader))
 
-src-wlib := string.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \
-		ns16550.c serial.c simple_alloc.c div64.S util.S $(zlib)
-src-plat := of.c
-src-boot := crt0.S $(src-wlib) $(src-plat) empty.c
+src-wlib := string.S crt0.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \
+		ns16550.c serial.c simple_alloc.c div64.S util.S \
+		gunzip_util.c elf_util.c $(zlib) devtree.c
+src-plat := of.c cuboot-83xx.c cuboot-85xx.c
+src-boot := $(src-wlib) $(src-plat) empty.c
 
 src-boot := $(addprefix $(obj)/, $(src-boot))
 obj-boot := $(addsuffix .o, $(basename $(src-boot)))
@@ -75,7 +76,7 @@
 	@cp $< $@
 
 clean-files := $(zlib) $(zlibheader) $(zliblinuxheader) \
-		empty.c zImage zImage.coff.lds zImage.lds zImage.sandpoint
+		empty.c zImage.coff.lds zImage.lds
 
 quiet_cmd_bootcc = BOOTCC  $@
       cmd_bootcc = $(CROSS32CC) -Wp,-MD,$(depfile) $(BOOTCFLAGS) -c -o $@ $<
@@ -84,23 +85,25 @@
       cmd_bootas = $(CROSS32CC) -Wp,-MD,$(depfile) $(BOOTAFLAGS) -c -o $@ $<
 
 quiet_cmd_bootar = BOOTAR  $@
-      cmd_bootar = $(CROSS32AR) -cr $@.$$$$ $^; mv $@.$$$$ $@
+      cmd_bootar = $(CROSS32AR) -cr $@.$$$$ $(filter-out FORCE,$^); mv $@.$$$$ $@
 
-$(patsubst %.c,%.o, $(filter %.c, $(src-boot))): %.o: %.c
+$(patsubst %.c,%.o, $(filter %.c, $(src-boot))): %.o: %.c FORCE
 	$(call if_changed_dep,bootcc)
-$(patsubst %.S,%.o, $(filter %.S, $(src-boot))): %.o: %.S
+$(patsubst %.S,%.o, $(filter %.S, $(src-boot))): %.o: %.S FORCE
 	$(call if_changed_dep,bootas)
 
-$(obj)/wrapper.a: $(obj-wlib)
-	$(call cmd,bootar)
+$(obj)/wrapper.a: $(obj-wlib) FORCE
+	$(call if_changed,bootar)
 
 hostprogs-y	:= addnote addRamDisk hack-coff mktree
 
-extra-y		:= $(obj)/crt0.o $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o \
+targets		+= $(patsubst $(obj)/%,%,$(obj-boot) wrapper.a)
+extra-y		:= $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o \
 		   $(obj)/zImage.lds $(obj)/zImage.coff.lds
 
 wrapper		:=$(srctree)/$(src)/wrapper
-wrapperbits	:= $(extra-y) $(addprefix $(obj)/,addnote hack-coff mktree)
+wrapperbits	:= $(extra-y) $(addprefix $(obj)/,addnote hack-coff mktree) \
+			$(wrapper) FORCE
 
 #############
 # Bits for building various flavours of zImage
@@ -113,50 +116,10 @@
 endif
 endif
 
+# args (to if_changed): 1 = (this rule), 2 = platform, 3 = dts 4=dtb 5=initrd
 quiet_cmd_wrap	= WRAP    $@
-      cmd_wrap	=$(CONFIG_SHELL) $(wrapper) -c -o $@ -p $2 $(CROSSWRAP) vmlinux
-quiet_cmd_wrap_initrd = WRAP    $@
-      cmd_wrap_initrd =$(CONFIG_SHELL) $(wrapper) -c -o $@ -p $2 $(CROSSWRAP) \
-				-i $(obj)/ramdisk.image.gz vmlinux
-
-$(obj)/zImage.chrp: vmlinux $(wrapperbits)
-	$(call cmd,wrap,chrp)
-
-$(obj)/zImage.initrd.chrp: vmlinux $(wrapperbits)
-	$(call cmd,wrap_initrd,chrp)
-
-$(obj)/zImage.pseries:	vmlinux $(wrapperbits)
-	$(call cmd,wrap,pseries)
-
-$(obj)/zImage.initrd.pseries: vmlinux $(wrapperbits)
-	$(call cmd,wrap_initrd,pseries)
-
-$(obj)/zImage.pmac: vmlinux $(wrapperbits)
-	$(call cmd,wrap,pmac)
-
-$(obj)/zImage.initrd.pmac: vmlinux $(wrapperbits)
-	$(call cmd,wrap_initrd,pmac)
-
-$(obj)/zImage.coff: vmlinux $(wrapperbits)
-	$(call cmd,wrap,pmaccoff)
-
-$(obj)/zImage.initrd.coff: vmlinux $(wrapperbits)
-	$(call cmd,wrap_initrd,pmaccoff)
-
-$(obj)/zImage.miboot: vmlinux $(wrapperbits)
-	$(call cmd,wrap,miboot)
-
-$(obj)/zImage.initrd.miboot: vmlinux $(wrapperbits)
-	$(call cmd,wrap_initrd,miboot)
-
-$(obj)/zImage.ps3: vmlinux
-	$(STRIP) -s -R .comment $< -o $@
-
-$(obj)/zImage.initrd.ps3: vmlinux
-	@echo "  WARNING zImage.initrd.ps3 not supported (yet)"
-
-$(obj)/uImage: vmlinux $(wrapperbits)
-	$(call cmd,wrap,uboot)
+      cmd_wrap	=$(CONFIG_SHELL) $(wrapper) -c -o $@ -p $2 $(CROSSWRAP) \
+		$(if $3, -s $3)$(if $4, -d $4)$(if $5, -i $5) vmlinux
 
 image-$(CONFIG_PPC_PSERIES)		+= zImage.pseries
 image-$(CONFIG_PPC_MAPLE)		+= zImage.pseries
@@ -166,7 +129,7 @@
 image-$(CONFIG_PPC_CHRP)		+= zImage.chrp
 image-$(CONFIG_PPC_EFIKA)		+= zImage.chrp
 image-$(CONFIG_PPC_PMAC)		+= zImage.pmac
-image-$(CONFIG_DEFAULT_UIMAGE)		+= uImage
+image-$(CONFIG_DEFAULT_UIMAGE)		+= uImage cuImage
 
 # For 32-bit powermacs, build the COFF and miboot images
 # as well as the ELF images.
@@ -174,16 +137,55 @@
 image-$(CONFIG_PPC_PMAC)	+= zImage.coff zImage.miboot
 endif
 
+initrd-  := $(patsubst zImage%, zImage.initrd%, $(image-n) $(image-))
 initrd-y := $(patsubst zImage%, zImage.initrd%, $(image-y))
+initrd-y := $(filter-out $(image-y), $(initrd-y))
+targets	+= $(image-y) $(initrd-y)
+
+$(addprefix $(obj)/, $(initrd-y)): $(obj)/ramdisk.image.gz
+
+# Don't put the ramdisk on the pattern rule; when its missing make will try
+# the pattern rule with less dependencies that also matches (even with the
+# hard dependency listed).
+$(obj)/zImage.initrd.%: vmlinux $(wrapperbits)
+	$(call if_changed,wrap,$*,,,$(obj)/ramdisk.image.gz)
+
+$(obj)/zImage.%: vmlinux $(wrapperbits)
+	$(call if_changed,wrap,$*)
+
+$(obj)/zImage.ps3: vmlinux
+	$(STRIP) -s -R .comment $< -o $@
+
+$(obj)/zImage.initrd.ps3: vmlinux
+	@echo "  WARNING zImage.initrd.ps3 not supported (yet)"
+
+$(obj)/uImage: vmlinux $(wrapperbits)
+	$(call if_changed,wrap,uboot)
+
+cuboot-plat-$(CONFIG_83xx) += 83xx
+cuboot-plat-$(CONFIG_85xx) += 85xx
+cuboot-plat-y += unknown-platform
+
+dts = $(if $(shell echo $(CONFIG_DEVICE_TREE) | grep '^/'),\
+       ,$(srctree)/$(src)/dts/)$(CONFIG_DEVICE_TREE)
+
+$(obj)/cuImage: vmlinux $(wrapperbits)
+	$(call if_changed,wrap,cuboot-$(word 1,$(cuboot-plat-y)),$(dts))
 
 $(obj)/zImage:		$(addprefix $(obj)/, $(image-y))
 	@rm -f $@; ln $< $@
 $(obj)/zImage.initrd:	$(addprefix $(obj)/, $(initrd-y))
 	@rm -f $@; ln $< $@
 
-install: $(CONFIGURE) $(image-y)
+install: $(CONFIGURE) $(addprefix $(obj)/, $(image-y))
 	sh -x $(srctree)/$(src)/install.sh "$(KERNELRELEASE)" vmlinux System.map "$(INSTALL_PATH)" $<
 
-clean-files += $(addprefix $(objtree)/, $(obj-boot) vmlinux.strip.gz)
-clean-files += $(addprefix $(objtree)/, $(obj-boot) vmlinux.bin.gz)
-clean-files += $(image-)
+# anything not in $(targets)
+clean-files += $(image-) $(initrd-) zImage zImage.initrd \
+               cuImage.elf cuImage.bin.gz
+
+# clean up files cached by wrapper
+clean-kernel := vmlinux.strip vmlinux.bin
+clean-kernel += $(addsuffix .gz,$(clean-kernel))
+# If not absolute clean-files are relative to $(obj).
+clean-files += $(addprefix $(objtree)/, $(clean-kernel))
diff --git a/arch/powerpc/boot/crt0.S b/arch/powerpc/boot/crt0.S
index 70e65b1..5a4215c 100644
--- a/arch/powerpc/boot/crt0.S
+++ b/arch/powerpc/boot/crt0.S
@@ -16,8 +16,11 @@
 _zimage_start_opd:
 	.long	_zimage_start, 0, 0, 0
 
+	.weak	_zimage_start
 	.globl	_zimage_start
 _zimage_start:
+	.globl	_zimage_start_lib
+_zimage_start_lib:
 	/* Work out the offset between the address we were linked at
 	   and the address where we're running. */
 	bl	1f
@@ -44,7 +47,7 @@
 	addi	r9,r9,4
 	bdnz	2b
 
-	/* Do a cache flush for our text, in case OF didn't */
+	/* Do a cache flush for our text, in case the loader didn't */
 3:	lis	r9,_start@ha
 	addi	r9,r9,_start@l
 	add	r9,r0,r9
@@ -59,6 +62,34 @@
 	sync
 	isync
 
-	mr	r6,r1
-	b	start
+	/* Clear the BSS */
+	lis	r9,__bss_start@ha
+	addi	r9,r9,__bss_start@l
+	add	r9,r0,r9
+	lis	r8,_end@ha
+	addi	r8,r8,_end@l
+	add	r8,r0,r8
+	li	r10,0
+5:	stw	r10,0(r9)
+	addi	r9,r9,4
+	cmplw	cr0,r9,r8
+	blt	5b
 
+	/* Possibly set up a custom stack */
+.weak	_platform_stack_top
+	lis	r8,_platform_stack_top@ha
+	addi	r8,r8,_platform_stack_top@l
+	cmpwi	r8,0
+	beq	6f
+	add	r8,r0,r8
+	lwz	r1,0(r8)
+	add	r1,r0,r1
+	li	r0,0
+	stwu	r0,-16(r1)	/* establish a stack frame */
+6:
+
+	/* Call platform_init() */
+	bl	platform_init
+
+	/* Call start */
+	b	start
diff --git a/arch/powerpc/boot/cuboot-83xx.c b/arch/powerpc/boot/cuboot-83xx.c
new file mode 100644
index 0000000..6cbc20a
--- /dev/null
+++ b/arch/powerpc/boot/cuboot-83xx.c
@@ -0,0 +1,68 @@
+/*
+ * Old U-boot compatibility for 83xx
+ *
+ * Author: Scott Wood <scottwood@freescale.com>
+ *
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ */
+
+#include "ops.h"
+#include "stdio.h"
+
+#define TARGET_83xx
+#include "ppcboot.h"
+
+static bd_t bd;
+extern char _end[];
+extern char _dtb_start[], _dtb_end[];
+
+static void platform_fixups(void)
+{
+	void *soc;
+
+	dt_fixup_memory(bd.bi_memstart, bd.bi_memsize);
+	dt_fixup_mac_addresses(bd.bi_enetaddr, bd.bi_enet1addr);
+	dt_fixup_cpu_clocks(bd.bi_intfreq, bd.bi_busfreq / 4, bd.bi_busfreq);
+
+	/* Unfortunately, the specific model number is encoded in the
+	 * soc node name in existing dts files -- once that is fixed,
+	 * this can do a simple path lookup.
+	 */
+	soc = find_node_by_devtype(NULL, "soc");
+	if (soc) {
+		void *serial = NULL;
+
+		setprop(soc, "bus-frequency", &bd.bi_busfreq,
+		        sizeof(bd.bi_busfreq));
+
+		while ((serial = find_node_by_devtype(serial, "serial"))) {
+			if (get_parent(serial) != soc)
+				continue;
+
+			setprop(serial, "clock-frequency", &bd.bi_busfreq,
+			        sizeof(bd.bi_busfreq));
+		}
+	}
+}
+
+void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+                   unsigned long r6, unsigned long r7)
+{
+	unsigned long end_of_ram = bd.bi_memstart + bd.bi_memsize;
+	unsigned long avail_ram = end_of_ram - (unsigned long)_end;
+
+	memcpy(&bd, (bd_t *)r3, sizeof(bd));
+	loader_info.initrd_addr = r4;
+	loader_info.initrd_size = r4 ? r5 : 0;
+	loader_info.cmdline = (char *)r6;
+	loader_info.cmdline_len = r7 - r6;
+
+	simple_alloc_init(_end, avail_ram - 1024*1024, 32, 64);
+	ft_init(_dtb_start, _dtb_end - _dtb_start, 32);
+	serial_console_init();
+	platform_ops.fixups = platform_fixups;
+}
diff --git a/arch/powerpc/boot/cuboot-85xx.c b/arch/powerpc/boot/cuboot-85xx.c
new file mode 100644
index 0000000..f88ba00
--- /dev/null
+++ b/arch/powerpc/boot/cuboot-85xx.c
@@ -0,0 +1,69 @@
+/*
+ * Old U-boot compatibility for 85xx
+ *
+ * Author: Scott Wood <scottwood@freescale.com>
+ *
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ */
+
+#include "ops.h"
+#include "stdio.h"
+
+#define TARGET_85xx
+#include "ppcboot.h"
+
+static bd_t bd;
+extern char _end[];
+extern char _dtb_start[], _dtb_end[];
+
+static void platform_fixups(void)
+{
+	void *soc;
+
+	dt_fixup_memory(bd.bi_memstart, bd.bi_memsize);
+	dt_fixup_mac_addresses(bd.bi_enetaddr, bd.bi_enet1addr,
+	                       bd.bi_enet2addr);
+	dt_fixup_cpu_clocks(bd.bi_intfreq, bd.bi_busfreq / 8, bd.bi_busfreq);
+
+	/* Unfortunately, the specific model number is encoded in the
+	 * soc node name in existing dts files -- once that is fixed,
+	 * this can do a simple path lookup.
+	 */
+	soc = find_node_by_devtype(NULL, "soc");
+	if (soc) {
+		void *serial = NULL;
+
+		setprop(soc, "bus-frequency", &bd.bi_busfreq,
+		        sizeof(bd.bi_busfreq));
+
+		while ((serial = find_node_by_devtype(serial, "serial"))) {
+			if (get_parent(serial) != soc)
+				continue;
+
+			setprop(serial, "clock-frequency", &bd.bi_busfreq,
+			        sizeof(bd.bi_busfreq));
+		}
+	}
+}
+
+void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+                   unsigned long r6, unsigned long r7)
+{
+	unsigned long end_of_ram = bd.bi_memstart + bd.bi_memsize;
+	unsigned long avail_ram = end_of_ram - (unsigned long)_end;
+
+	memcpy(&bd, (bd_t *)r3, sizeof(bd));
+	loader_info.initrd_addr = r4;
+	loader_info.initrd_size = r4 ? r5 : 0;
+	loader_info.cmdline = (char *)r6;
+	loader_info.cmdline_len = r7 - r6;
+
+	simple_alloc_init(_end, avail_ram - 1024*1024, 32, 64);
+	ft_init(_dtb_start, _dtb_end - _dtb_start, 32);
+	serial_console_init();
+	platform_ops.fixups = platform_fixups;
+}
diff --git a/arch/powerpc/boot/devtree.c b/arch/powerpc/boot/devtree.c
new file mode 100644
index 0000000..c995155
--- /dev/null
+++ b/arch/powerpc/boot/devtree.c
@@ -0,0 +1,307 @@
+/*
+ * devtree.c - convenience functions for device tree manipulation
+ * Copyright 2007 David Gibson, IBM Corporation.
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ *
+ * Authors: David Gibson <david@gibson.dropbear.id.au>
+ *	    Scott Wood <scottwood@freescale.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.
+ */
+#include <stdarg.h>
+#include <stddef.h>
+#include "types.h"
+#include "string.h"
+#include "stdio.h"
+#include "ops.h"
+
+void dt_fixup_memory(u64 start, u64 size)
+{
+	void *root, *memory;
+	int naddr, nsize, i;
+	u32 memreg[4];
+
+	root = finddevice("/");
+	if (getprop(root, "#address-cells", &naddr, sizeof(naddr)) < 0)
+		naddr = 2;
+	if (naddr < 1 || naddr > 2)
+		fatal("Can't cope with #address-cells == %d in /\n\r", naddr);
+
+	if (getprop(root, "#size-cells", &nsize, sizeof(nsize)) < 0)
+		nsize = 1;
+	if (nsize < 1 || nsize > 2)
+		fatal("Can't cope with #size-cells == %d in /\n\r", nsize);
+
+	i = 0;
+	if (naddr == 2)
+		memreg[i++] = start >> 32;
+	memreg[i++] = start & 0xffffffff;
+	if (nsize == 2)
+		memreg[i++] = size >> 32;
+	memreg[i++] = size & 0xffffffff;
+
+	memory = finddevice("/memory");
+	if (! memory) {
+		memory = create_node(NULL, "memory");
+		setprop_str(memory, "device_type", "memory");
+	}
+
+	printf("Memory <- <0x%x", memreg[0]);
+	for (i = 1; i < (naddr + nsize); i++)
+		printf(" 0x%x", memreg[i]);
+	printf("> (%ldMB)\n\r", (unsigned long)(size >> 20));
+
+	setprop(memory, "reg", memreg, (naddr + nsize)*sizeof(u32));
+}
+
+#define MHZ(x)	((x + 500000) / 1000000)
+
+void dt_fixup_cpu_clocks(u32 cpu, u32 tb, u32 bus)
+{
+	void *devp = NULL;
+
+	printf("CPU clock-frequency <- 0x%x (%dMHz)\n\r", cpu, MHZ(cpu));
+	printf("CPU timebase-frequency <- 0x%x (%dMHz)\n\r", tb, MHZ(tb));
+	if (bus > 0)
+		printf("CPU bus-frequency <- 0x%x (%dMHz)\n\r", bus, MHZ(bus));
+
+	while ((devp = find_node_by_devtype(devp, "cpu"))) {
+		setprop_val(devp, "clock-frequency", cpu);
+		setprop_val(devp, "timebase-frequency", tb);
+		if (bus > 0)
+			setprop_val(devp, "bus-frequency", bus);
+	}
+}
+
+void dt_fixup_clock(const char *path, u32 freq)
+{
+	void *devp = finddevice(path);
+
+	if (devp) {
+		printf("%s: clock-frequency <- %x (%dMHz)\n\r", path, freq, MHZ(freq));
+		setprop_val(devp, "clock-frequency", freq);
+	}
+}
+
+void __dt_fixup_mac_addresses(u32 startindex, ...)
+{
+	va_list ap;
+	u32 index = startindex;
+	void *devp;
+	const u8 *addr;
+
+	va_start(ap, startindex);
+	while ((addr = va_arg(ap, const u8 *))) {
+		devp = find_node_by_prop_value(NULL, "linux,network-index",
+					       (void*)&index, sizeof(index));
+
+		printf("ENET%d: local-mac-address <-"
+		       " %02x:%02x:%02x:%02x:%02x:%02x\n\r", index,
+		       addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+
+		if (devp)
+			setprop(devp, "local-mac-address", addr, 6);
+
+		index++;
+	}
+	va_end(ap);
+}
+
+#define MAX_ADDR_CELLS 4
+#define MAX_RANGES 8
+
+static void get_reg_format(void *node, u32 *naddr, u32 *nsize)
+{
+	if (getprop(node, "#address-cells", naddr, 4) != 4)
+		*naddr = 2;
+	if (getprop(node, "#size-cells", nsize, 4) != 4)
+		*nsize = 1;
+}
+
+static void copy_val(u32 *dest, u32 *src, int naddr)
+{
+	int pad = MAX_ADDR_CELLS - naddr;
+
+	memset(dest, 0, pad * 4);
+	memcpy(dest + pad, src, naddr * 4);
+}
+
+static int sub_reg(u32 *reg, u32 *sub)
+{
+	int i, borrow = 0;
+
+	for (i = MAX_ADDR_CELLS - 1; i >= 0; i--) {
+		int prev_borrow = borrow;
+		borrow = reg[i] < sub[i] + prev_borrow;
+		reg[i] -= sub[i] + prev_borrow;
+	}
+
+	return !borrow;
+}
+
+static int add_reg(u32 *reg, u32 *add, int naddr)
+{
+	int i, carry = 0;
+
+	for (i = MAX_ADDR_CELLS - 1; i >= MAX_ADDR_CELLS - naddr; i--) {
+		u64 tmp = (u64)reg[i] + add[i] + carry;
+		carry = tmp >> 32;
+		reg[i] = (u32)tmp;
+	}
+
+	return !carry;
+}
+
+/* It is assumed that if the first byte of reg fits in a
+ * range, then the whole reg block fits.
+ */
+static int compare_reg(u32 *reg, u32 *range, u32 *rangesize)
+{
+	int i;
+	u32 end;
+
+	for (i = 0; i < MAX_ADDR_CELLS; i++) {
+		if (reg[i] < range[i])
+			return 0;
+		if (reg[i] > range[i])
+			break;
+	}
+
+	for (i = 0; i < MAX_ADDR_CELLS; i++) {
+		end = range[i] + rangesize[i];
+
+		if (reg[i] < end)
+			break;
+		if (reg[i] > end)
+			return 0;
+	}
+
+	return reg[i] != end;
+}
+
+/* reg must be MAX_ADDR_CELLS */
+static int find_range(u32 *reg, u32 *ranges, int nregaddr,
+                      int naddr, int nsize, int buflen)
+{
+	int nrange = nregaddr + naddr + nsize;
+	int i;
+
+	for (i = 0; i + nrange <= buflen; i += nrange) {
+		u32 range_addr[MAX_ADDR_CELLS];
+		u32 range_size[MAX_ADDR_CELLS];
+
+		copy_val(range_addr, ranges + i, naddr);
+		copy_val(range_size, ranges + i + nregaddr + naddr, nsize);
+
+		if (compare_reg(reg, range_addr, range_size))
+			return i;
+	}
+
+	return -1;
+}
+
+/* Currently only generic buses without special encodings are supported.
+ * In particular, PCI is not supported.  Also, only the beginning of the
+ * reg block is tracked; size is ignored except in ranges.
+ */
+static u32 dt_xlate_buf[MAX_ADDR_CELLS * MAX_RANGES * 3];
+
+static int dt_xlate(void *node, int res, int reglen, unsigned long *addr,
+		unsigned long *size)
+{
+	u32 last_addr[MAX_ADDR_CELLS];
+	u32 this_addr[MAX_ADDR_CELLS];
+	void *parent;
+	u64 ret_addr, ret_size;
+	u32 naddr, nsize, prev_naddr;
+	int buflen, offset;
+
+	parent = get_parent(node);
+	if (!parent)
+		return 0;
+
+	get_reg_format(parent, &naddr, &nsize);
+
+	if (nsize > 2)
+		return 0;
+
+	offset = (naddr + nsize) * res;
+
+	if (reglen < offset + naddr + nsize ||
+	    sizeof(dt_xlate_buf) < offset + naddr + nsize)
+		return 0;
+
+	copy_val(last_addr, dt_xlate_buf + offset, naddr);
+
+	ret_size = dt_xlate_buf[offset + naddr];
+	if (nsize == 2) {
+		ret_size <<= 32;
+		ret_size |= dt_xlate_buf[offset + naddr + 1];
+	}
+
+	while ((node = get_parent(node))) {
+		prev_naddr = naddr;
+
+		get_reg_format(node, &naddr, &nsize);
+
+		buflen = getprop(node, "ranges", dt_xlate_buf,
+				sizeof(dt_xlate_buf));
+		if (buflen < 0)
+			continue;
+		if (buflen > sizeof(dt_xlate_buf))
+			return 0;
+
+		offset = find_range(last_addr, dt_xlate_buf, prev_naddr,
+		                    naddr, nsize, buflen / 4);
+
+		if (offset < 0)
+			return 0;
+
+		copy_val(this_addr, dt_xlate_buf + offset, prev_naddr);
+
+		if (!sub_reg(last_addr, this_addr))
+			return 0;
+
+		copy_val(this_addr, dt_xlate_buf + offset + prev_naddr, naddr);
+
+		if (!add_reg(last_addr, this_addr, naddr))
+			return 0;
+	}
+
+	if (naddr > 2)
+		return 0;
+
+	ret_addr = ((u64)last_addr[2] << 32) | last_addr[3];
+
+	if (sizeof(void *) == 4 &&
+	    (ret_addr >= 0x100000000ULL || ret_size > 0x100000000ULL ||
+	     ret_addr + ret_size > 0x100000000ULL))
+		return 0;
+
+	*addr = ret_addr;
+	if (size)
+		*size = ret_size;
+
+	return 1;
+}
+
+int dt_xlate_reg(void *node, int res, unsigned long *addr, unsigned long *size)
+{
+	int reglen;
+
+	reglen = getprop(node, "reg", dt_xlate_buf, sizeof(dt_xlate_buf)) / 4;
+	return dt_xlate(node, res, reglen, addr, size);
+}
+
+int dt_xlate_addr(void *node, u32 *buf, int buflen, unsigned long *xlated_addr)
+{
+
+	if (buflen > sizeof(dt_xlate_buf))
+		return 0;
+
+	memcpy(dt_xlate_buf, buf, buflen);
+	return dt_xlate(node, 0, buflen / 4, xlated_addr, NULL);
+}
diff --git a/arch/powerpc/boot/dts/kuroboxHD.dts b/arch/powerpc/boot/dts/kuroboxHD.dts
index b897918..157dc98 100644
--- a/arch/powerpc/boot/dts/kuroboxHD.dts
+++ b/arch/powerpc/boot/dts/kuroboxHD.dts
@@ -29,7 +29,6 @@
 
 	cpus {
 		linux,phandle = <2000>;
-		#cpus = <1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 
@@ -126,17 +125,17 @@
 			interrupt-parent = <4400>;
 			interrupt-map-mask = <f800 0 0 7>;
 			interrupt-map = <
-				/* IDSEL 0x11 - IRQ0 ETH */
+				/* IDSEL 11 - IRQ0 ETH */
 				5800 0 0 1 4400 0 1
 				5800 0 0 2 4400 1 1
 				5800 0 0 3 4400 2 1
 				5800 0 0 4 4400 3 1
-				/* IDSEL 0x12 - IRQ1 IDE0 */
+				/* IDSEL 12 - IRQ1 IDE0 */
 				6000 0 0 1 4400 1 1
 				6000 0 0 2 4400 2 1
 				6000 0 0 3 4400 3 1
 				6000 0 0 4 4400 0 1
-				/* IDSEL 0x14 - IRQ3 USB2.0 */
+				/* IDSEL 14 - IRQ3 USB2.0 */
 				7000 0 0 1 4400 3 1
 				7000 0 0 2 4400 3 1
 				7000 0 0 3 4400 3 1
diff --git a/arch/powerpc/boot/dts/kuroboxHG.dts b/arch/powerpc/boot/dts/kuroboxHG.dts
index 7531027..919eb29 100644
--- a/arch/powerpc/boot/dts/kuroboxHG.dts
+++ b/arch/powerpc/boot/dts/kuroboxHG.dts
@@ -29,7 +29,6 @@
 
 	cpus {
 		linux,phandle = <2000>;
-		#cpus = <1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 
@@ -126,17 +125,17 @@
 			interrupt-parent = <4400>;
 			interrupt-map-mask = <f800 0 0 7>;
 			interrupt-map = <
-				/* IDSEL 0x11 - IRQ0 ETH */
+				/* IDSEL 11 - IRQ0 ETH */
 				5800 0 0 1 4400 0 1
 				5800 0 0 2 4400 1 1
 				5800 0 0 3 4400 2 1
 				5800 0 0 4 4400 3 1
-				/* IDSEL 0x12 - IRQ1 IDE0 */
+				/* IDSEL 12 - IRQ1 IDE0 */
 				6000 0 0 1 4400 1 1
 				6000 0 0 2 4400 2 1
 				6000 0 0 3 4400 3 1
 				6000 0 0 4 4400 0 1
-				/* IDSEL 0x14 - IRQ3 USB2.0 */
+				/* IDSEL 14 - IRQ3 USB2.0 */
 				7000 0 0 1 4400 3 1
 				7000 0 0 2 4400 3 1
 				7000 0 0 3 4400 3 1
diff --git a/arch/powerpc/boot/dts/lite5200.dts b/arch/powerpc/boot/dts/lite5200.dts
index c03103c..ba54c6b 100644
--- a/arch/powerpc/boot/dts/lite5200.dts
+++ b/arch/powerpc/boot/dts/lite5200.dts
@@ -24,7 +24,6 @@
 	#size-cells = <1>;
 
 	cpus {
-		#cpus = <1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 
diff --git a/arch/powerpc/boot/dts/lite5200b.dts b/arch/powerpc/boot/dts/lite5200b.dts
index 3875ca9..2e00308 100644
--- a/arch/powerpc/boot/dts/lite5200b.dts
+++ b/arch/powerpc/boot/dts/lite5200b.dts
@@ -24,7 +24,6 @@
 	#size-cells = <1>;
 
 	cpus {
-		#cpus = <1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 
diff --git a/arch/powerpc/boot/dts/mpc7448hpc2.dts b/arch/powerpc/boot/dts/mpc7448hpc2.dts
index 41d0720..6fa3754 100644
--- a/arch/powerpc/boot/dts/mpc7448hpc2.dts
+++ b/arch/powerpc/boot/dts/mpc7448hpc2.dts
@@ -19,7 +19,6 @@
 	linux,phandle = <100>;
 
 	cpus {
-		#cpus = <1>;
 		#address-cells = <1>;
 		#size-cells =<0>;
 		linux,phandle = <200>;
diff --git a/arch/powerpc/boot/dts/mpc8272ads.dts b/arch/powerpc/boot/dts/mpc8272ads.dts
index 260b2e4..423eedc 100644
--- a/arch/powerpc/boot/dts/mpc8272ads.dts
+++ b/arch/powerpc/boot/dts/mpc8272ads.dts
@@ -17,7 +17,6 @@
        linux,phandle = <100>;
 
        cpus {
-               #cpus = <1>;
                #address-cells = <1>;
                #size-cells = <0>;
                linux,phandle = <200>;
diff --git a/arch/powerpc/boot/dts/mpc8313erdb.dts b/arch/powerpc/boot/dts/mpc8313erdb.dts
index 6d72190..a1533cc 100644
--- a/arch/powerpc/boot/dts/mpc8313erdb.dts
+++ b/arch/powerpc/boot/dts/mpc8313erdb.dts
@@ -16,7 +16,6 @@
 	#size-cells = <1>;
 
 	cpus {
-		#cpus = <1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 
diff --git a/arch/powerpc/boot/dts/mpc832x_mds.dts b/arch/powerpc/boot/dts/mpc832x_mds.dts
index 06b3106..c798491 100644
--- a/arch/powerpc/boot/dts/mpc832x_mds.dts
+++ b/arch/powerpc/boot/dts/mpc832x_mds.dts
@@ -16,7 +16,6 @@
 	#size-cells = <1>;
 
 	cpus {
-		#cpus = <1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 
diff --git a/arch/powerpc/boot/dts/mpc832x_rdb.dts b/arch/powerpc/boot/dts/mpc832x_rdb.dts
new file mode 100644
index 0000000..b55bced
--- /dev/null
+++ b/arch/powerpc/boot/dts/mpc832x_rdb.dts
@@ -0,0 +1,291 @@
+/*
+ * MPC832x RDB Device Tree Source
+ *
+ * Copyright 2007 Freescale Semiconductor Inc.
+ *
+ * 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.
+ */
+
+/ {
+	model = "MPC8323ERDB";
+	compatible = "MPC8323ERDB", "MPC832xRDB", "MPC83xxRDB";
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		PowerPC,8323@0 {
+			device_type = "cpu";
+			reg = <0>;
+			d-cache-line-size = <20>;	// 32 bytes
+			i-cache-line-size = <20>;	// 32 bytes
+			d-cache-size = <4000>;		// L1, 16K
+			i-cache-size = <4000>;		// L1, 16K
+			timebase-frequency = <0>;
+			bus-frequency = <0>;
+			clock-frequency = <0>;
+			32-bit;
+		};
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <00000000 04000000>;
+	};
+
+	soc8323@e0000000 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		#interrupt-cells = <2>;
+		device_type = "soc";
+		ranges = <0 e0000000 00100000>;
+		reg = <e0000000 00000200>;
+		bus-frequency = <0>;
+
+		wdt@200 {
+			device_type = "watchdog";
+			compatible = "mpc83xx_wdt";
+			reg = <200 100>;
+		};
+
+		i2c@3000 {
+			device_type = "i2c";
+			compatible = "fsl-i2c";
+			reg = <3000 100>;
+			interrupts = <e 8>;
+			interrupt-parent = <&pic>;
+			dfsrr;
+		};
+
+		serial@4500 {
+			device_type = "serial";
+			compatible = "ns16550";
+			reg = <4500 100>;
+			clock-frequency = <0>;
+			interrupts = <9 8>;
+			interrupt-parent = <&pic>;
+		};
+
+		serial@4600 {
+			device_type = "serial";
+			compatible = "ns16550";
+			reg = <4600 100>;
+			clock-frequency = <0>;
+			interrupts = <a 8>;
+			interrupt-parent = <&pic>;
+		};
+
+		crypto@30000 {
+			device_type = "crypto";
+			model = "SEC2";
+			compatible = "talitos";
+			reg = <30000 7000>;
+			interrupts = <b 8>;
+			interrupt-parent = <&pic>;
+			/* Rev. 2.2 */
+			num-channels = <1>;
+			channel-fifo-len = <18>;
+			exec-units-mask = <0000004c>;
+			descriptor-types-mask = <0122003f>;
+		};
+
+		pci@8500 {
+			interrupt-map-mask = <f800 0 0 7>;
+			interrupt-map = <
+					/* IDSEL 0x10 AD16 (USB) */
+					 8000 0 0 1 &pic 11 8
+
+					/* IDSEL 0x11 AD17 (Mini1)*/
+					 8800 0 0 1 &pic 12 8
+					 8800 0 0 2 &pic 13 8
+					 8800 0 0 3 &pic 14 8
+					 8800 0 0 4 &pic 30 8
+
+					/* IDSEL 0x12 AD18 (PCI/Mini2) */
+					 9000 0 0 1 &pic 13 8
+					 9000 0 0 2 &pic 14 8
+					 9000 0 0 3 &pic 30 8
+					 9000 0 0 4 &pic 11 8>;
+
+			interrupt-parent = <&pic>;
+			interrupts = <42 8>;
+			bus-range = <0 0>;
+			ranges = <42000000 0 80000000 80000000 0 10000000
+			          02000000 0 90000000 90000000 0 10000000
+			          01000000 0 d0000000 d0000000 0 04000000>;
+			clock-frequency = <0>;
+			#interrupt-cells = <1>;
+			#size-cells = <2>;
+			#address-cells = <3>;
+			reg = <8500 100>;
+			compatible = "83xx";
+			device_type = "pci";
+		};
+
+		pic:pic@700 {
+			interrupt-controller;
+			#address-cells = <0>;
+			#interrupt-cells = <2>;
+			reg = <700 100>;
+			built-in;
+			device_type = "ipic";
+		};
+
+		par_io@1400 {
+			reg = <1400 100>;
+			device_type = "par_io";
+			num-ports = <7>;
+
+			ucc2pio:ucc_pin@02 {
+				pio-map = <
+			/* port  pin  dir  open_drain  assignment  has_irq */
+					3  4  3  0  2  0 	/* MDIO */
+					3  5  1  0  2  0 	/* MDC */
+					3 15  2  0  1  0 	/* RX_CLK (CLK16) */
+					3 17  2  0  1  0 	/* TX_CLK (CLK3) */
+					0 12  1  0  1  0 	/* TxD0 */
+					0 13  1  0  1  0 	/* TxD1 */
+					0 14  1  0  1  0 	/* TxD2 */
+					0 15  1  0  1  0 	/* TxD3 */
+					0 16  2  0  1  0 	/* RxD0 */
+					0 17  2  0  1  0 	/* RxD1 */
+					0 18  2  0  1  0 	/* RxD2 */
+					0 19  2  0  1  0 	/* RxD3 */
+					0 1a  2  0  1  0 	/* RX_ER */
+					0 1b  1  0  1  0 	/* TX_ER */
+					0 1c  2  0  1  0 	/* RX_DV */
+					0 1d  2  0  1  0 	/* COL */
+					0 1e  1  0  1  0 	/* TX_EN */
+					0 1f  2  0  1  0>;      /* CRS */
+			};
+			ucc3pio:ucc_pin@03 {
+				pio-map = <
+			/* port  pin  dir  open_drain  assignment  has_irq */
+					0  d  2  0  1  0 	/* RX_CLK (CLK9) */
+					3 18  2  0  1  0 	/* TX_CLK (CLK10) */
+					1  0  1  0  1  0 	/* TxD0 */
+					1  1  1  0  1  0 	/* TxD1 */
+					1  2  1  0  1  0 	/* TxD2 */
+					1  3  1  0  1  0 	/* TxD3 */
+					1  4  2  0  1  0 	/* RxD0 */
+					1  5  2  0  1  0 	/* RxD1 */
+					1  6  2  0  1  0 	/* RxD2 */
+					1  7  2  0  1  0 	/* RxD3 */
+					1  8  2  0  1  0 	/* RX_ER */
+					1  9  1  0  1  0 	/* TX_ER */
+					1  a  2  0  1  0 	/* RX_DV */
+					1  b  2  0  1  0 	/* COL */
+					1  c  1  0  1  0 	/* TX_EN */
+					1  d  2  0  1  0>;      /* CRS */
+			};
+		};
+	};
+
+	qe@e0100000 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		device_type = "qe";
+		model = "QE";
+		ranges = <0 e0100000 00100000>;
+		reg = <e0100000 480>;
+		brg-frequency = <0>;
+		bus-frequency = <BCD3D80>;
+
+		muram@10000 {
+			device_type = "muram";
+			ranges = <0 00010000 00004000>;
+
+			data-only@0 {
+				reg = <0 4000>;
+			};
+		};
+
+		spi@4c0 {
+			device_type = "spi";
+			compatible = "fsl_spi";
+			reg = <4c0 40>;
+			interrupts = <2>;
+			interrupt-parent = <&qeic>;
+			mode = "cpu";
+		};
+
+		spi@500 {
+			device_type = "spi";
+			compatible = "fsl_spi";
+			reg = <500 40>;
+			interrupts = <1>;
+			interrupt-parent = <&qeic>;
+			mode = "cpu";
+		};
+
+		ucc@3000 {
+			device_type = "network";
+			compatible = "ucc_geth";
+			model = "UCC";
+			device-id = <2>;
+			reg = <3000 200>;
+			interrupts = <21>;
+			interrupt-parent = <&qeic>;
+			mac-address = [ 00 04 9f ef 03 02 ];
+			rx-clock = <20>;
+			tx-clock = <13>;
+			phy-handle = <&phy00>;
+			pio-handle = <&ucc2pio>;
+		};
+
+		ucc@2200 {
+			device_type = "network";
+			compatible = "ucc_geth";
+			model = "UCC";
+			device-id = <3>;
+			reg = <2200 200>;
+			interrupts = <22>;
+			interrupt-parent = <&qeic>;
+			mac-address = [ 00 04 9f ef 03 01 ];
+			rx-clock = <19>;
+			tx-clock = <1a>;
+			phy-handle = <&phy04>;
+			pio-handle = <&ucc3pio>;
+		};
+
+		mdio@3120 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <3120 18>;
+			device_type = "mdio";
+			compatible = "ucc_geth_phy";
+
+			phy00:ethernet-phy@00 {
+				interrupt-parent = <&pic>;
+				interrupts = <0>;
+				reg = <0>;
+				device_type = "ethernet-phy";
+				interface = <3>; //ENET_100_MII
+			};
+			phy04:ethernet-phy@04 {
+				interrupt-parent = <&pic>;
+				interrupts = <0>;
+				reg = <4>;
+				device_type = "ethernet-phy";
+				interface = <3>;
+			};
+		};
+
+		qeic:qeic@80 {
+			interrupt-controller;
+			device_type = "qeic";
+			#address-cells = <0>;
+			#interrupt-cells = <1>;
+			reg = <80 80>;
+			built-in;
+			big-endian;
+			interrupts = <20 8 21 8>; //high:32 low:33
+			interrupt-parent = <&pic>;
+		};
+	};
+};
diff --git a/arch/powerpc/boot/dts/mpc8349emitx.dts b/arch/powerpc/boot/dts/mpc8349emitx.dts
index 61b550b..db0d003 100644
--- a/arch/powerpc/boot/dts/mpc8349emitx.dts
+++ b/arch/powerpc/boot/dts/mpc8349emitx.dts
@@ -15,7 +15,6 @@
 	#size-cells = <1>;
 
 	cpus {
-		#cpus = <1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 
diff --git a/arch/powerpc/boot/dts/mpc8349emitxgp.dts b/arch/powerpc/boot/dts/mpc8349emitxgp.dts
index b2e1a5e..f636528 100644
--- a/arch/powerpc/boot/dts/mpc8349emitxgp.dts
+++ b/arch/powerpc/boot/dts/mpc8349emitxgp.dts
@@ -15,7 +15,6 @@
 	#size-cells = <1>;
 
 	cpus {
-		#cpus = <1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 
diff --git a/arch/powerpc/boot/dts/mpc834x_mds.dts b/arch/powerpc/boot/dts/mpc834x_mds.dts
index e4b43c2..07bcc51 100644
--- a/arch/powerpc/boot/dts/mpc834x_mds.dts
+++ b/arch/powerpc/boot/dts/mpc834x_mds.dts
@@ -16,7 +16,6 @@
 	#size-cells = <1>;
 
 	cpus {
-		#cpus = <1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 
diff --git a/arch/powerpc/boot/dts/mpc836x_mds.dts b/arch/powerpc/boot/dts/mpc836x_mds.dts
index 4fe45c0..7f578eb 100644
--- a/arch/powerpc/boot/dts/mpc836x_mds.dts
+++ b/arch/powerpc/boot/dts/mpc836x_mds.dts
@@ -21,7 +21,6 @@
 	#size-cells = <1>;
 
 	cpus {
-		#cpus = <1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 
diff --git a/arch/powerpc/boot/dts/mpc8540ads.dts b/arch/powerpc/boot/dts/mpc8540ads.dts
index 3c0917f..f261d64 100644
--- a/arch/powerpc/boot/dts/mpc8540ads.dts
+++ b/arch/powerpc/boot/dts/mpc8540ads.dts
@@ -17,7 +17,6 @@
 	#size-cells = <1>;
 
 	cpus {
-		#cpus = <1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 
diff --git a/arch/powerpc/boot/dts/mpc8541cds.dts b/arch/powerpc/boot/dts/mpc8541cds.dts
index 2a1ae76..5fdcb69 100644
--- a/arch/powerpc/boot/dts/mpc8541cds.dts
+++ b/arch/powerpc/boot/dts/mpc8541cds.dts
@@ -17,7 +17,6 @@
 	#size-cells = <1>;
 
 	cpus {
-		#cpus = <1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 
diff --git a/arch/powerpc/boot/dts/mpc8544ds.dts b/arch/powerpc/boot/dts/mpc8544ds.dts
new file mode 100644
index 0000000..6b08460
--- /dev/null
+++ b/arch/powerpc/boot/dts/mpc8544ds.dts
@@ -0,0 +1,136 @@
+/*
+ * MPC8544 DS Device Tree Source
+ *
+ * Copyright 2007 Freescale Semiconductor Inc.
+ *
+ * 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.
+ */
+
+/ {
+	model = "MPC8544DS";
+	compatible = "MPC8544DS", "MPC85xxDS";
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	cpus {
+		#cpus = <1>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		PowerPC,8544@0 {
+			device_type = "cpu";
+			reg = <0>;
+			d-cache-line-size = <20>;	// 32 bytes
+			i-cache-line-size = <20>;	// 32 bytes
+			d-cache-size = <8000>;		// L1, 32K
+			i-cache-size = <8000>;		// L1, 32K
+			timebase-frequency = <0>;
+			bus-frequency = <0>;
+			clock-frequency = <0>;
+			32-bit;
+		};
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <00000000 00000000>;	// Filled by U-Boot
+	};
+
+	soc8544@e0000000 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		#interrupt-cells = <2>;
+		device_type = "soc";
+		ranges = <0 e0000000 00100000>;
+		reg = <e0000000 00100000>;	// CCSRBAR 1M
+		bus-frequency = <0>;		// Filled out by uboot.
+
+		i2c@3000 {
+			device_type = "i2c";
+			compatible = "fsl-i2c";
+			reg = <3000 100>;
+			interrupts = <1b 2>;
+			interrupt-parent = <&mpic>;
+			dfsrr;
+		};
+
+		mdio@24520 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			device_type = "mdio";
+			compatible = "gianfar";
+			reg = <24520 20>;
+			phy0: ethernet-phy@0 {
+				interrupt-parent = <&mpic>;
+				interrupts = <3a 1>;
+				reg = <0>;
+				device_type = "ethernet-phy";
+			};
+			phy1: ethernet-phy@1 {
+				interrupt-parent = <&mpic>;
+				interrupts = <3a 1>;
+				reg = <1>;
+				device_type = "ethernet-phy";
+			};
+		};
+
+		ethernet@24000 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			device_type = "network";
+			model = "TSEC";
+			compatible = "gianfar";
+			reg = <24000 1000>;
+			local-mac-address = [ 00 00 00 00 00 00 ];
+			interrupts = <d 2 e 2 12 2>;
+			interrupt-parent = <&mpic>;
+			phy-handle = <&phy0>;
+		};
+
+		ethernet@26000 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			device_type = "network";
+			model = "TSEC";
+			compatible = "gianfar";
+			reg = <26000 1000>;
+			local-mac-address = [ 00 00 00 00 00 00 ];
+			interrupts = <f 2 10 2 11 2>;
+			interrupt-parent = <&mpic>;
+			phy-handle = <&phy1>;
+		};
+
+		serial@4500 {
+			device_type = "serial";
+			compatible = "ns16550";
+			reg = <4500 100>;
+			clock-frequency = <0>;
+			interrupts = <1a 2>;
+			interrupt-parent = <&mpic>;
+		};
+
+		serial@4600 {
+			device_type = "serial";
+			compatible = "ns16550";
+			reg = <4600 100>;
+			clock-frequency = <0>;
+			interrupts = <1a 2>;
+			interrupt-parent = <&mpic>;
+		};
+
+		mpic: pic@40000 {
+			clock-frequency = <0>;
+			interrupt-controller;
+			#address-cells = <0>;
+			#interrupt-cells = <2>;
+			reg = <40000 40000>;
+			built-in;
+			compatible = "chrp,open-pic";
+			device_type = "open-pic";
+			big-endian;
+		};
+	};
+};
diff --git a/arch/powerpc/boot/dts/mpc8548cds.dts b/arch/powerpc/boot/dts/mpc8548cds.dts
index 7eb5d81..b2b2200 100644
--- a/arch/powerpc/boot/dts/mpc8548cds.dts
+++ b/arch/powerpc/boot/dts/mpc8548cds.dts
@@ -17,7 +17,6 @@
 	#size-cells = <1>;
 
 	cpus {
-		#cpus = <1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 
diff --git a/arch/powerpc/boot/dts/mpc8555cds.dts b/arch/powerpc/boot/dts/mpc8555cds.dts
index 5f9c102..68a4795 100644
--- a/arch/powerpc/boot/dts/mpc8555cds.dts
+++ b/arch/powerpc/boot/dts/mpc8555cds.dts
@@ -17,7 +17,6 @@
 	#size-cells = <1>;
 
 	cpus {
-		#cpus = <1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 
diff --git a/arch/powerpc/boot/dts/mpc8560ads.dts b/arch/powerpc/boot/dts/mpc8560ads.dts
index 1050263..1f2afe9 100644
--- a/arch/powerpc/boot/dts/mpc8560ads.dts
+++ b/arch/powerpc/boot/dts/mpc8560ads.dts
@@ -17,7 +17,6 @@
 	#size-cells = <1>;
 
 	cpus {
-		#cpus = <1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 
diff --git a/arch/powerpc/boot/dts/mpc8568mds.dts b/arch/powerpc/boot/dts/mpc8568mds.dts
index bf49d8c..7361b36 100644
--- a/arch/powerpc/boot/dts/mpc8568mds.dts
+++ b/arch/powerpc/boot/dts/mpc8568mds.dts
@@ -21,7 +21,6 @@
 	#size-cells = <1>;
 
 	cpus {
-		#cpus = <1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 
diff --git a/arch/powerpc/boot/dts/mpc8641_hpcn.dts b/arch/powerpc/boot/dts/mpc8641_hpcn.dts
index 8a4995a..260b264 100644
--- a/arch/powerpc/boot/dts/mpc8641_hpcn.dts
+++ b/arch/powerpc/boot/dts/mpc8641_hpcn.dts
@@ -17,7 +17,6 @@
 	#size-cells = <1>;
 
 	cpus {
-		#cpus = <2>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 
@@ -300,6 +299,30 @@
 			};
 
 		};
+
+		pci@9000 {
+			compatible = "86xx";
+			device_type = "pci";
+			#interrupt-cells = <1>;
+			#size-cells = <2>;
+			#address-cells = <3>;
+			reg = <9000 1000>;
+			bus-range = <0 ff>;
+			ranges = <02000000 0 a0000000 a0000000 0 20000000
+				  01000000 0 00000000 e3000000 0 00100000>;
+			clock-frequency = <1fca055>;
+			interrupt-parent = <&mpic>;
+			interrupts = <19 2>;
+			interrupt-map-mask = <f800 0 0 7>;
+			interrupt-map = <
+				/* IDSEL 0x0 */
+				0000 0 0 1 &mpic 44 1
+				0000 0 0 2 &mpic 45 1
+				0000 0 0 3 &mpic 46 1
+				0000 0 0 4 &mpic 47 1
+				>;
+		};
+
 		mpic: pic@40000 {
 			clock-frequency = <0>;
 			interrupt-controller;
diff --git a/arch/powerpc/boot/dts/mpc866ads.dts b/arch/powerpc/boot/dts/mpc866ads.dts
index 2b56b5d..c0d06fd 100644
--- a/arch/powerpc/boot/dts/mpc866ads.dts
+++ b/arch/powerpc/boot/dts/mpc866ads.dts
@@ -18,7 +18,6 @@
 	linux,phandle = <100>;
 
 	cpus {
-		#cpus = <1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 		linux,phandle = <200>;
diff --git a/arch/powerpc/boot/dts/mpc885ads.dts b/arch/powerpc/boot/dts/mpc885ads.dts
index faecd08..110bf61 100644
--- a/arch/powerpc/boot/dts/mpc885ads.dts
+++ b/arch/powerpc/boot/dts/mpc885ads.dts
@@ -18,7 +18,6 @@
 	linux,phandle = <100>;
 
 	cpus {
-		#cpus = <1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 		linux,phandle = <200>;
diff --git a/arch/powerpc/boot/elf.h b/arch/powerpc/boot/elf.h
index d4828fc..1941bc5 100644
--- a/arch/powerpc/boot/elf.h
+++ b/arch/powerpc/boot/elf.h
@@ -146,4 +146,12 @@
 #define ELFOSABI_NONE	0
 #define ELFOSABI_LINUX	3
 
+struct elf_info {
+	unsigned long loadsize;
+	unsigned long memsize;
+	unsigned long elfoffset;
+};
+int parse_elf64(void *hdr, struct elf_info *info);
+int parse_elf32(void *hdr, struct elf_info *info);
+
 #endif				/* _PPC_BOOT_ELF_H_ */
diff --git a/arch/powerpc/boot/elf_util.c b/arch/powerpc/boot/elf_util.c
new file mode 100644
index 0000000..7454aa4
--- /dev/null
+++ b/arch/powerpc/boot/elf_util.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) Paul Mackerras 1997.
+ *
+ * Updates for PPC64 by Todd Inglett, Dave Engebretsen & Peter Bergner.
+ *
+ * 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.
+ */
+#include <stdarg.h>
+#include <stddef.h>
+#include "elf.h"
+#include "page.h"
+#include "string.h"
+#include "stdio.h"
+
+int parse_elf64(void *hdr, struct elf_info *info)
+{
+	Elf64_Ehdr *elf64 = hdr;
+	Elf64_Phdr *elf64ph;
+	unsigned int i;
+
+	if (!(elf64->e_ident[EI_MAG0]  == ELFMAG0	&&
+	      elf64->e_ident[EI_MAG1]  == ELFMAG1	&&
+	      elf64->e_ident[EI_MAG2]  == ELFMAG2	&&
+	      elf64->e_ident[EI_MAG3]  == ELFMAG3	&&
+	      elf64->e_ident[EI_CLASS] == ELFCLASS64	&&
+	      elf64->e_ident[EI_DATA]  == ELFDATA2MSB	&&
+	      elf64->e_type            == ET_EXEC	&&
+	      elf64->e_machine         == EM_PPC64))
+		return 0;
+
+	elf64ph = (Elf64_Phdr *)((unsigned long)elf64 +
+				 (unsigned long)elf64->e_phoff);
+	for (i = 0; i < (unsigned int)elf64->e_phnum; i++, elf64ph++)
+		if (elf64ph->p_type == PT_LOAD)
+			break;
+	if (i >= (unsigned int)elf64->e_phnum)
+		return 0;
+
+	info->loadsize = (unsigned long)elf64ph->p_filesz;
+	info->memsize = (unsigned long)elf64ph->p_memsz;
+	info->elfoffset = (unsigned long)elf64ph->p_offset;
+
+	return 1;
+}
+
+int parse_elf32(void *hdr, struct elf_info *info)
+{
+	Elf32_Ehdr *elf32 = hdr;
+	Elf32_Phdr *elf32ph;
+	unsigned int i;
+
+	if (!(elf32->e_ident[EI_MAG0]  == ELFMAG0	&&
+	      elf32->e_ident[EI_MAG1]  == ELFMAG1	&&
+	      elf32->e_ident[EI_MAG2]  == ELFMAG2	&&
+	      elf32->e_ident[EI_MAG3]  == ELFMAG3	&&
+	      elf32->e_ident[EI_CLASS] == ELFCLASS32	&&
+	      elf32->e_ident[EI_DATA]  == ELFDATA2MSB	&&
+	      elf32->e_type            == ET_EXEC	&&
+	      elf32->e_machine         == EM_PPC))
+		return 0;
+
+	elf32ph = (Elf32_Phdr *) ((unsigned long)elf32 + elf32->e_phoff);
+	for (i = 0; i < elf32->e_phnum; i++, elf32ph++)
+		if (elf32ph->p_type == PT_LOAD)
+			break;
+	if (i >= elf32->e_phnum)
+		return 0;
+
+	info->loadsize = elf32ph->p_filesz;
+	info->memsize = elf32ph->p_memsz;
+	info->elfoffset = elf32ph->p_offset;
+	return 1;
+}
diff --git a/arch/powerpc/boot/flatdevtree.c b/arch/powerpc/boot/flatdevtree.c
index c76c194..d00fbd9 100644
--- a/arch/powerpc/boot/flatdevtree.c
+++ b/arch/powerpc/boot/flatdevtree.c
@@ -29,12 +29,20 @@
 
 #define _ALIGN(x, al)	(((x) + (al) - 1) & ~((al) - 1))
 
+static char *ft_root_node(struct ft_cxt *cxt)
+{
+	return cxt->rgn[FT_STRUCT].start;
+}
+
 /* Routines for keeping node ptrs returned by ft_find_device current */
 /* First entry not used b/c it would return 0 and be taken as NULL/error */
-static void *ft_node_add(struct ft_cxt *cxt, char *node)
+static void *ft_get_phandle(struct ft_cxt *cxt, char *node)
 {
 	unsigned int i;
 
+	if (!node)
+		return NULL;
+
 	for (i = 1; i < cxt->nodes_used; i++)	/* already there? */
 		if (cxt->node_tbl[i] == node)
 			return (void *)i;
@@ -238,7 +246,7 @@
 			if (rgn == FT_STRUCT)
 				ft_node_update_before(cxt, p, -nextra);
 		}
-		*p -= nextra;
+		*pp -= nextra;
 		cxt->rgn[rgn].start -= nextra;
 		cxt->rgn[rgn].size += nextra;
 		return 1;
@@ -253,8 +261,14 @@
 	char *str, *next;
 	enum ft_rgn_id r;
 
-	if (!cxt->isordered && !ft_reorder(cxt, nextra))
-		return 0;
+	if (!cxt->isordered) {
+		unsigned long rgn_off = *pp - cxt->rgn[rgn].start;
+
+		if (!ft_reorder(cxt, nextra))
+			return 0;
+
+		*pp = cxt->rgn[rgn].start + rgn_off;
+	}
 	if (ft_shuffle(cxt, pp, rgn, nextra))
 		return 1;
 
@@ -415,7 +429,7 @@
 {
 	int off, len;
 
-	off = lookup_string(cxt, name);
+	off = map_string(cxt, name);
 	if (off == NO_STRING)
 		return -1;
 
@@ -590,7 +604,7 @@
 
 void ft_begin_tree(struct ft_cxt *cxt)
 {
-	cxt->p = cxt->rgn[FT_STRUCT].start;
+	cxt->p = ft_root_node(cxt);
 }
 
 void ft_end_tree(struct ft_cxt *cxt)
@@ -636,8 +650,21 @@
 	/* require absolute path */
 	if (srch_path[0] != '/')
 		return NULL;
-	node = ft_find_descendent(cxt, cxt->rgn[FT_STRUCT].start, srch_path);
-	return ft_node_add(cxt, node);
+	node = ft_find_descendent(cxt, ft_root_node(cxt), srch_path);
+	return ft_get_phandle(cxt, node);
+}
+
+void *ft_find_device_rel(struct ft_cxt *cxt, const void *top,
+                         const char *srch_path)
+{
+	char *node;
+
+	node = ft_node_ph2node(cxt, top);
+	if (node == NULL)
+		return NULL;
+
+	node = ft_find_descendent(cxt, node, srch_path);
+	return ft_get_phandle(cxt, node);
 }
 
 void *ft_find_descendent(struct ft_cxt *cxt, void *top, const char *srch_path)
@@ -701,23 +728,18 @@
 	return NULL;
 }
 
-void *ft_get_parent(struct ft_cxt *cxt, const void *phandle)
+void *__ft_get_parent(struct ft_cxt *cxt, void *node)
 {
-	void *node;
 	int d;
 	struct ft_atom atom;
 	char *p;
 
-	node = ft_node_ph2node(cxt, phandle);
-	if (node == NULL)
-		return NULL;
-
 	for (d = 0; cxt->genealogy[d] != NULL; ++d)
 		if (cxt->genealogy[d] == node)
-			return cxt->genealogy[d > 0 ? d - 1 : 0];
+			return d > 0 ? cxt->genealogy[d - 1] : NULL;
 
 	/* have to do it the hard way... */
-	p = cxt->rgn[FT_STRUCT].start;
+	p = ft_root_node(cxt);
 	d = 0;
 	while ((p = ft_next(cxt, p, &atom)) != NULL) {
 		switch (atom.tag) {
@@ -726,7 +748,7 @@
 			if (node == atom.data) {
 				/* found it */
 				cxt->genealogy[d + 1] = NULL;
-				return d > 0 ? cxt->genealogy[d - 1] : node;
+				return d > 0 ? cxt->genealogy[d - 1] : NULL;
 			}
 			++d;
 			break;
@@ -738,41 +760,131 @@
 	return NULL;
 }
 
-int ft_get_prop(struct ft_cxt *cxt, const void *phandle, const char *propname,
-		void *buf, const unsigned int buflen)
+void *ft_get_parent(struct ft_cxt *cxt, const void *phandle)
+{
+	void *node = ft_node_ph2node(cxt, phandle);
+	if (node == NULL)
+		return NULL;
+
+	node = __ft_get_parent(cxt, node);
+	return ft_get_phandle(cxt, node);
+}
+
+static const void *__ft_get_prop(struct ft_cxt *cxt, void *node,
+                                 const char *propname, unsigned int *len)
 {
 	struct ft_atom atom;
-	void *node;
-	char *p;
-	int depth;
-	unsigned int size;
+	int depth = 0;
 
-	node = ft_node_ph2node(cxt, phandle);
-	if (node == NULL)
-		return -1;
-
-	depth = 0;
-	p = (char *)node;
-
-	while ((p = ft_next(cxt, p, &atom)) != NULL) {
+	while ((node = ft_next(cxt, node, &atom)) != NULL) {
 		switch (atom.tag) {
 		case OF_DT_BEGIN_NODE:
 			++depth;
 			break;
+
 		case OF_DT_PROP:
-			if ((depth != 1) || strcmp(atom.name, propname))
+			if (depth != 1 || strcmp(atom.name, propname))
 				break;
-			size = min(atom.size, buflen);
-			memcpy(buf, atom.data, size);
-			return atom.size;
+
+			if (len)
+				*len = atom.size;
+
+			return atom.data;
+
 		case OF_DT_END_NODE:
 			if (--depth <= 0)
-				return -1;
+				return NULL;
 		}
 	}
+
+	return NULL;
+}
+
+int ft_get_prop(struct ft_cxt *cxt, const void *phandle, const char *propname,
+		void *buf, const unsigned int buflen)
+{
+	const void *data;
+	unsigned int size;
+
+	void *node = ft_node_ph2node(cxt, phandle);
+	if (!node)
+		return -1;
+
+	data = __ft_get_prop(cxt, node, propname, &size);
+	if (data) {
+		unsigned int clipped_size = min(size, buflen);
+		memcpy(buf, data, clipped_size);
+		return size;
+	}
+
 	return -1;
 }
 
+void *__ft_find_node_by_prop_value(struct ft_cxt *cxt, void *prev,
+                                   const char *propname, const char *propval,
+                                   unsigned int proplen)
+{
+	struct ft_atom atom;
+	char *p = ft_root_node(cxt);
+	char *next;
+	int past_prev = prev ? 0 : 1;
+	int depth = -1;
+
+	while ((next = ft_next(cxt, p, &atom)) != NULL) {
+		const void *data;
+		unsigned int size;
+
+		switch (atom.tag) {
+		case OF_DT_BEGIN_NODE:
+			depth++;
+
+			if (prev == p) {
+				past_prev = 1;
+				break;
+			}
+
+			if (!past_prev || depth < 1)
+				break;
+
+			data = __ft_get_prop(cxt, p, propname, &size);
+			if (!data || size != proplen)
+				break;
+			if (memcmp(data, propval, size))
+				break;
+
+			return p;
+
+		case OF_DT_END_NODE:
+			if (depth-- == 0)
+				return NULL;
+
+			break;
+		}
+
+		p = next;
+	}
+
+	return NULL;
+}
+
+void *ft_find_node_by_prop_value(struct ft_cxt *cxt, const void *prev,
+                                 const char *propname, const char *propval,
+                                 int proplen)
+{
+	void *node = NULL;
+
+	if (prev) {
+		node = ft_node_ph2node(cxt, prev);
+
+		if (!node)
+			return NULL;
+	}
+
+	node = __ft_find_node_by_prop_value(cxt, node, propname,
+	                                    propval, proplen);
+	return ft_get_phandle(cxt, node);
+}
+
 int ft_set_prop(struct ft_cxt *cxt, const void *phandle, const char *propname,
 		const void *buf, const unsigned int buflen)
 {
@@ -849,19 +961,26 @@
 	return -1;
 }
 
-void *ft_create_node(struct ft_cxt *cxt, const void *parent, const char *path)
+void *ft_create_node(struct ft_cxt *cxt, const void *parent, const char *name)
 {
 	struct ft_atom atom;
 	char *p, *next;
 	int depth = 0;
 
-	p = cxt->rgn[FT_STRUCT].start;
+	if (parent) {
+		p = ft_node_ph2node(cxt, parent);
+		if (!p)
+			return NULL;
+	} else {
+		p = ft_root_node(cxt);
+	}
+
 	while ((next = ft_next(cxt, p, &atom)) != NULL) {
 		switch (atom.tag) {
 		case OF_DT_BEGIN_NODE:
 			++depth;
-			if (depth == 1 && strcmp(atom.name, path) == 0)
-				/* duplicate node path, return error */
+			if (depth == 1 && strcmp(atom.name, name) == 0)
+				/* duplicate node name, return error */
 				return NULL;
 			break;
 		case OF_DT_END_NODE:
@@ -870,7 +989,7 @@
 				break;
 			/* end of node, insert here */
 			cxt->p = p;
-			ft_begin_node(cxt, path);
+			ft_begin_node(cxt, name);
 			ft_end_node(cxt);
 			return p;
 		}
diff --git a/arch/powerpc/boot/flatdevtree.h b/arch/powerpc/boot/flatdevtree.h
index b9cd9f6..cb26325 100644
--- a/arch/powerpc/boot/flatdevtree.h
+++ b/arch/powerpc/boot/flatdevtree.h
@@ -97,10 +97,17 @@
 void ft_dump_blob(const void *bphp);
 void ft_merge_blob(struct ft_cxt *cxt, void *blob);
 void *ft_find_device(struct ft_cxt *cxt, const char *srch_path);
+void *ft_find_device_rel(struct ft_cxt *cxt, const void *top,
+                         const char *srch_path);
 void *ft_find_descendent(struct ft_cxt *cxt, void *top, const char *srch_path);
 int ft_get_prop(struct ft_cxt *cxt, const void *phandle, const char *propname,
 		void *buf, const unsigned int buflen);
 int ft_set_prop(struct ft_cxt *cxt, const void *phandle, const char *propname,
 		const void *buf, const unsigned int buflen);
+void *ft_get_parent(struct ft_cxt *cxt, const void *phandle);
+void *ft_find_node_by_prop_value(struct ft_cxt *cxt, const void *prev,
+                                 const char *propname, const char *propval,
+                                 int proplen);
+void *ft_create_node(struct ft_cxt *cxt, const void *parent, const char *name);
 
 #endif /* FLATDEVTREE_H */
diff --git a/arch/powerpc/boot/flatdevtree_misc.c b/arch/powerpc/boot/flatdevtree_misc.c
index 04da38f..4341e65 100644
--- a/arch/powerpc/boot/flatdevtree_misc.c
+++ b/arch/powerpc/boot/flatdevtree_misc.c
@@ -16,24 +16,43 @@
 
 static struct ft_cxt cxt;
 
-static void *ft_finddevice(const char *name)
+static void *fdtm_finddevice(const char *name)
 {
 	return ft_find_device(&cxt, name);
 }
 
-static int ft_getprop(const void *phandle, const char *propname, void *buf,
-		const int buflen)
+static int fdtm_getprop(const void *phandle, const char *propname,
+                        void *buf, const int buflen)
 {
 	return ft_get_prop(&cxt, phandle, propname, buf, buflen);
 }
 
-static int ft_setprop(const void *phandle, const char *propname,
-		const void *buf, const int buflen)
+static int fdtm_setprop(const void *phandle, const char *propname,
+                        const void *buf, const int buflen)
 {
 	return ft_set_prop(&cxt, phandle, propname, buf, buflen);
 }
 
-static unsigned long ft_finalize(void)
+static void *fdtm_get_parent(const void *phandle)
+{
+	return ft_get_parent(&cxt, phandle);
+}
+
+static void *fdtm_create_node(const void *phandle, const char *name)
+{
+	return ft_create_node(&cxt, phandle, name);
+}
+
+static void *fdtm_find_node_by_prop_value(const void *prev,
+                                          const char *propname,
+                                          const char *propval,
+                                          int proplen)
+{
+	return ft_find_node_by_prop_value(&cxt, prev, propname,
+	                                  propval, proplen);
+}
+
+static unsigned long fdtm_finalize(void)
 {
 	ft_end_tree(&cxt);
 	return (unsigned long)cxt.bph;
@@ -41,10 +60,13 @@
 
 int ft_init(void *dt_blob, unsigned int max_size, unsigned int max_find_device)
 {
-	dt_ops.finddevice = ft_finddevice;
-	dt_ops.getprop = ft_getprop;
-	dt_ops.setprop = ft_setprop;
-	dt_ops.finalize = ft_finalize;
+	dt_ops.finddevice = fdtm_finddevice;
+	dt_ops.getprop = fdtm_getprop;
+	dt_ops.setprop = fdtm_setprop;
+	dt_ops.get_parent = fdtm_get_parent;
+	dt_ops.create_node = fdtm_create_node;
+	dt_ops.find_node_by_prop_value = fdtm_find_node_by_prop_value;
+	dt_ops.finalize = fdtm_finalize;
 
 	return ft_open(&cxt, dt_blob, max_size, max_find_device,
 			platform_ops.realloc);
diff --git a/arch/powerpc/boot/gunzip_util.c b/arch/powerpc/boot/gunzip_util.c
new file mode 100644
index 0000000..df8ab07
--- /dev/null
+++ b/arch/powerpc/boot/gunzip_util.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2007 David Gibson, IBM Corporation.
+ * Based on earlier work, Copyright (C) Paul Mackerras 1997.
+ *
+ * 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.
+ */
+
+#include <stddef.h>
+#include "string.h"
+#include "stdio.h"
+#include "ops.h"
+#include "gunzip_util.h"
+
+#define HEAD_CRC	2
+#define EXTRA_FIELD	4
+#define ORIG_NAME	8
+#define COMMENT		0x10
+#define RESERVED	0xe0
+
+/**
+ * gunzip_start - prepare to decompress gzip data
+ * @state:     decompressor state structure to be initialized
+ * @src:       buffer containing gzip compressed or uncompressed data
+ * @srclen:    size in bytes of the buffer at src
+ *
+ * If the buffer at @src contains a gzip header, this function
+ * initializes zlib to decompress the data, storing the decompression
+ * state in @state.  The other functions in this file can then be used
+ * to decompress data from the gzipped stream.
+ *
+ * If the buffer at @src does not contain a gzip header, it is assumed
+ * to contain uncompressed data.  The buffer information is recorded
+ * in @state and the other functions in this file will simply copy
+ * data from the uncompressed data stream at @src.
+ *
+ * Any errors, such as bad compressed data, cause an error to be
+ * printed an the platform's exit() function to be called.
+ */
+void gunzip_start(struct gunzip_state *state, void *src, int srclen)
+{
+	char *hdr = src;
+	int hdrlen = 0;
+
+	memset(state, 0, sizeof(*state));
+
+	/* Check for gzip magic number */
+	if ((hdr[0] == 0x1f) && (hdr[1] == 0x8b)) {
+		/* gzip data, initialize zlib parameters */
+		int r, flags;
+
+		state->s.workspace = state->scratch;
+		if (zlib_inflate_workspacesize() > sizeof(state->scratch))
+			fatal("insufficient scratch space for gunzip\n\r");
+
+		/* skip header */
+		hdrlen = 10;
+		flags = hdr[3];
+		if (hdr[2] != Z_DEFLATED || (flags & RESERVED) != 0)
+			fatal("bad gzipped data\n\r");
+		if ((flags & EXTRA_FIELD) != 0)
+			hdrlen = 12 + hdr[10] + (hdr[11] << 8);
+		if ((flags & ORIG_NAME) != 0)
+			while (hdr[hdrlen++] != 0)
+				;
+		if ((flags & COMMENT) != 0)
+			while (hdr[hdrlen++] != 0)
+				;
+		if ((flags & HEAD_CRC) != 0)
+			hdrlen += 2;
+		if (hdrlen >= srclen)
+			fatal("gunzip_start: ran out of data in header\n\r");
+
+		r = zlib_inflateInit2(&state->s, -MAX_WBITS);
+		if (r != Z_OK)
+			fatal("inflateInit2 returned %d\n\r", r);
+	}
+
+	state->s.next_in = src + hdrlen;
+	state->s.avail_in = srclen - hdrlen;
+}
+
+/**
+ * gunzip_partial - extract bytes from a gzip data stream
+ * @state:     gzip state structure previously initialized by gunzip_start()
+ * @dst:       buffer to store extracted data
+ * @dstlen:    maximum number of bytes to extract
+ *
+ * This function extracts at most @dstlen bytes from the data stream
+ * previously associated with @state by gunzip_start(), decompressing
+ * if necessary.  Exactly @dstlen bytes are extracted unless the data
+ * stream doesn't contain enough bytes, in which case the entire
+ * remainder of the stream is decompressed.
+ *
+ * Returns the actual number of bytes extracted.  If any errors occur,
+ * such as a corrupted compressed stream, an error is printed an the
+ * platform's exit() function is called.
+ */
+int gunzip_partial(struct gunzip_state *state, void *dst, int dstlen)
+{
+	int len;
+
+	if (state->s.workspace) {
+		/* gunzipping */
+		int r;
+
+		state->s.next_out = dst;
+		state->s.avail_out = dstlen;
+		r = zlib_inflate(&state->s, Z_FULL_FLUSH);
+		if (r != Z_OK && r != Z_STREAM_END)
+			fatal("inflate returned %d msg: %s\n\r", r, state->s.msg);
+		len = state->s.next_out - (unsigned char *)dst;
+	} else {
+		/* uncompressed image */
+		len = min(state->s.avail_in, (unsigned)dstlen);
+		memcpy(dst, state->s.next_in, len);
+		state->s.next_in += len;
+		state->s.avail_in -= len;
+	}
+	return len;
+}
+
+/**
+ * gunzip_exactly - extract a fixed number of bytes from a gzip data stream
+ * @state:     gzip state structure previously initialized by gunzip_start()
+ * @dst:       buffer to store extracted data
+ * @dstlen:    number of bytes to extract
+ *
+ * This function extracts exactly @dstlen bytes from the data stream
+ * previously associated with @state by gunzip_start(), decompressing
+ * if necessary.
+ *
+ * If there are less @dstlen bytes available in the data stream, or if
+ * any other errors occur, such as a corrupted compressed stream, an
+ * error is printed an the platform's exit() function is called.
+ */
+void gunzip_exactly(struct gunzip_state *state, void *dst, int dstlen)
+{
+	int len;
+
+	len  = gunzip_partial(state, dst, dstlen);
+	if (len < dstlen)
+		fatal("\n\rgunzip_exactly: ran out of data!"
+				" Wanted %d, got %d.\n\r", dstlen, len);
+}
+
+/**
+ * gunzip_discard - discard bytes from a gzip data stream
+ * @state:     gzip state structure previously initialized by gunzip_start()
+ * @len:       number of bytes to discard
+ *
+ * This function extracts, then discards exactly @len bytes from the
+ * data stream previously associated with @state by gunzip_start().
+ * Subsequent gunzip_partial(), gunzip_exactly() or gunzip_finish()
+ * calls will extract the data following the discarded bytes in the
+ * data stream.
+ *
+ * If there are less @len bytes available in the data stream, or if
+ * any other errors occur, such as a corrupted compressed stream, an
+ * error is printed an the platform's exit() function is called.
+ */
+void gunzip_discard(struct gunzip_state *state, int len)
+{
+	static char discard_buf[128];
+
+	while (len > sizeof(discard_buf)) {
+		gunzip_exactly(state, discard_buf, sizeof(discard_buf));
+		len -= sizeof(discard_buf);
+	}
+
+	if (len > 0)
+		gunzip_exactly(state, discard_buf, len);
+}
+
+/**
+ * gunzip_finish - extract all remaining bytes from a gzip data stream
+ * @state:     gzip state structure previously initialized by gunzip_start()
+ * @dst:       buffer to store extracted data
+ * @dstlen:    maximum number of bytes to extract
+ *
+ * This function extracts all remaining data, or at most @dstlen
+ * bytes, from the stream previously associated with @state by
+ * gunzip_start().  zlib is then shut down, so it is an error to use
+ * any of the functions in this file on @state until it is
+ * re-initialized with another call to gunzip_start().
+ *
+ * If any errors occur, such as a corrupted compressed stream, an
+ * error is printed an the platform's exit() function is called.
+ */
+int gunzip_finish(struct gunzip_state *state, void *dst, int dstlen)
+{
+	int len;
+
+	if (state->s.workspace) {
+		len = gunzip_partial(state, dst, dstlen);
+		zlib_inflateEnd(&state->s);
+	} else {
+		/* uncompressed image */
+		len = min(state->s.avail_in, (unsigned)dstlen);
+		memcpy(dst, state->s.next_in, len);
+	}
+
+	return len;
+}
diff --git a/arch/powerpc/boot/gunzip_util.h b/arch/powerpc/boot/gunzip_util.h
new file mode 100644
index 0000000..b3dfa6e
--- /dev/null
+++ b/arch/powerpc/boot/gunzip_util.h
@@ -0,0 +1,45 @@
+/*
+ * Decompression convenience functions
+ *
+ * Copyright 2007 David Gibson, IBM Corporation.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#ifndef _PPC_BOOT_GUNZIP_UTIL_H_
+#define _PPC_BOOT_GUNZIP_UTIL_H_
+
+#include "zlib.h"
+
+/*
+ * These functions are designed to make life easy for decompressing
+ * kernel images, initrd images or any other gzip compressed image,
+ * particularly if its useful to decompress part of the image (e.g. to
+ * examine headers) before decompressing the remainder.
+ *
+ * To use:
+ *     - declare a gunzip_state structure
+ *     - use gunzip_start() to initialize the state, associating it
+ *       with a stream of compressed data
+ *     - use gunzip_partial(), gunzip_exactly() and gunzip_discard()
+ *       in any combination to extract pieces of data from the stream
+ *     - Finally use gunzip_finish() to extract the tail of the
+ *       compressed stream and wind up zlib
+ */
+
+/* scratch space for gunzip; 46912 is from zlib_inflate_workspacesize() */
+#define GUNZIP_SCRATCH_SIZE	46912
+
+struct gunzip_state {
+	z_stream s;
+	char scratch[46912];
+};
+
+void gunzip_start(struct gunzip_state *state, void *src, int srclen);
+int gunzip_partial(struct gunzip_state *state, void *dst, int dstlen);
+void gunzip_exactly(struct gunzip_state *state, void *dst, int len);
+void gunzip_discard(struct gunzip_state *state, int len);
+int gunzip_finish(struct gunzip_state *state, void *dst, int len);
+
+#endif /* _PPC_BOOT_GUNZIP_UTIL_H_ */
diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c
index 6f6b50d..56b56a8 100644
--- a/arch/powerpc/boot/main.c
+++ b/arch/powerpc/boot/main.c
@@ -14,11 +14,10 @@
 #include "page.h"
 #include "string.h"
 #include "stdio.h"
-#include "zlib.h"
 #include "ops.h"
+#include "gunzip_util.h"
 #include "flatdevtree.h"
-
-extern void flush_cache(void *, unsigned long);
+#include "reg.h"
 
 extern char _start[];
 extern char __bss_start[];
@@ -30,304 +29,173 @@
 extern char _dtb_start[];
 extern char _dtb_end[];
 
+static struct gunzip_state gzstate;
+
 struct addr_range {
-	unsigned long addr;
+	void *addr;
 	unsigned long size;
-	unsigned long memsize;
 };
-static struct addr_range vmlinux;
-static struct addr_range vmlinuz;
-static struct addr_range initrd;
-
-static unsigned long elfoffset;
-static int is_64bit;
-
-/* scratch space for gunzip; 46912 is from zlib_inflate_workspacesize() */
-static char scratch[46912];
-static char elfheader[256];
 
 typedef void (*kernel_entry_t)(unsigned long, unsigned long, void *);
 
 #undef DEBUG
 
-#define HEAD_CRC	2
-#define EXTRA_FIELD	4
-#define ORIG_NAME	8
-#define COMMENT		0x10
-#define RESERVED	0xe0
-
-static void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp)
+static struct addr_range prep_kernel(void)
 {
-	z_stream s;
-	int r, i, flags;
-
-	/* skip header */
-	i = 10;
-	flags = src[3];
-	if (src[2] != Z_DEFLATED || (flags & RESERVED) != 0) {
-		printf("bad gzipped data\n\r");
-		exit();
-	}
-	if ((flags & EXTRA_FIELD) != 0)
-		i = 12 + src[10] + (src[11] << 8);
-	if ((flags & ORIG_NAME) != 0)
-		while (src[i++] != 0)
-			;
-	if ((flags & COMMENT) != 0)
-		while (src[i++] != 0)
-			;
-	if ((flags & HEAD_CRC) != 0)
-		i += 2;
-	if (i >= *lenp) {
-		printf("gunzip: ran out of data in header\n\r");
-		exit();
-	}
-
-	if (zlib_inflate_workspacesize() > sizeof(scratch)) {
-		printf("gunzip needs more mem\n");
-		exit();
-	}
-	memset(&s, 0, sizeof(s));
-	s.workspace = scratch;
-	r = zlib_inflateInit2(&s, -MAX_WBITS);
-	if (r != Z_OK) {
-		printf("inflateInit2 returned %d\n\r", r);
-		exit();
-	}
-	s.next_in = src + i;
-	s.avail_in = *lenp - i;
-	s.next_out = dst;
-	s.avail_out = dstlen;
-	r = zlib_inflate(&s, Z_FULL_FLUSH);
-	if (r != Z_OK && r != Z_STREAM_END) {
-		printf("inflate returned %d msg: %s\n\r", r, s.msg);
-		exit();
-	}
-	*lenp = s.next_out - (unsigned char *) dst;
-	zlib_inflateEnd(&s);
-}
-
-static int is_elf64(void *hdr)
-{
-	Elf64_Ehdr *elf64 = hdr;
-	Elf64_Phdr *elf64ph;
-	unsigned int i;
-
-	if (!(elf64->e_ident[EI_MAG0]  == ELFMAG0	&&
-	      elf64->e_ident[EI_MAG1]  == ELFMAG1	&&
-	      elf64->e_ident[EI_MAG2]  == ELFMAG2	&&
-	      elf64->e_ident[EI_MAG3]  == ELFMAG3	&&
-	      elf64->e_ident[EI_CLASS] == ELFCLASS64	&&
-	      elf64->e_ident[EI_DATA]  == ELFDATA2MSB	&&
-	      elf64->e_type            == ET_EXEC	&&
-	      elf64->e_machine         == EM_PPC64))
-		return 0;
-
-	elf64ph = (Elf64_Phdr *)((unsigned long)elf64 +
-				 (unsigned long)elf64->e_phoff);
-	for (i = 0; i < (unsigned int)elf64->e_phnum; i++, elf64ph++)
-		if (elf64ph->p_type == PT_LOAD)
-			break;
-	if (i >= (unsigned int)elf64->e_phnum)
-		return 0;
-
-	elfoffset = (unsigned long)elf64ph->p_offset;
-	vmlinux.size = (unsigned long)elf64ph->p_filesz + elfoffset;
-	vmlinux.memsize = (unsigned long)elf64ph->p_memsz + elfoffset;
-
-	is_64bit = 1;
-	return 1;
-}
-
-static int is_elf32(void *hdr)
-{
-	Elf32_Ehdr *elf32 = hdr;
-	Elf32_Phdr *elf32ph;
-	unsigned int i;
-
-	if (!(elf32->e_ident[EI_MAG0]  == ELFMAG0	&&
-	      elf32->e_ident[EI_MAG1]  == ELFMAG1	&&
-	      elf32->e_ident[EI_MAG2]  == ELFMAG2	&&
-	      elf32->e_ident[EI_MAG3]  == ELFMAG3	&&
-	      elf32->e_ident[EI_CLASS] == ELFCLASS32	&&
-	      elf32->e_ident[EI_DATA]  == ELFDATA2MSB	&&
-	      elf32->e_type            == ET_EXEC	&&
-	      elf32->e_machine         == EM_PPC))
-		return 0;
-
-	elf32 = (Elf32_Ehdr *)elfheader;
-	elf32ph = (Elf32_Phdr *) ((unsigned long)elf32 + elf32->e_phoff);
-	for (i = 0; i < elf32->e_phnum; i++, elf32ph++)
-		if (elf32ph->p_type == PT_LOAD)
-			break;
-	if (i >= elf32->e_phnum)
-		return 0;
-
-	elfoffset = elf32ph->p_offset;
-	vmlinux.size = elf32ph->p_filesz + elf32ph->p_offset;
-	vmlinux.memsize = elf32ph->p_memsz + elf32ph->p_offset;
-	return 1;
-}
-
-static void prep_kernel(unsigned long a1, unsigned long a2)
-{
+	char elfheader[256];
+	void *vmlinuz_addr = _vmlinux_start;
+	unsigned long vmlinuz_size = _vmlinux_end - _vmlinux_start;
+	void *addr = 0;
+	struct elf_info ei;
 	int len;
 
-	vmlinuz.addr = (unsigned long)_vmlinux_start;
-	vmlinuz.size = (unsigned long)(_vmlinux_end - _vmlinux_start);
-
 	/* gunzip the ELF header of the kernel */
-	if (*(unsigned short *)vmlinuz.addr == 0x1f8b) {
-		len = vmlinuz.size;
-		gunzip(elfheader, sizeof(elfheader),
-				(unsigned char *)vmlinuz.addr, &len);
-	} else
-		memcpy(elfheader, (const void *)vmlinuz.addr,
-		       sizeof(elfheader));
+	gunzip_start(&gzstate, vmlinuz_addr, vmlinuz_size);
+	gunzip_exactly(&gzstate, elfheader, sizeof(elfheader));
 
-	if (!is_elf64(elfheader) && !is_elf32(elfheader)) {
-		printf("Error: not a valid PPC32 or PPC64 ELF file!\n\r");
-		exit();
-	}
+	if (!parse_elf64(elfheader, &ei) && !parse_elf32(elfheader, &ei))
+		fatal("Error: not a valid PPC32 or PPC64 ELF file!\n\r");
+
 	if (platform_ops.image_hdr)
 		platform_ops.image_hdr(elfheader);
 
-	/* We need to alloc the memsize plus the file offset since gzip
-	 * will expand the header (file offset), then the kernel, then
-	 * possible rubbish we don't care about. But the kernel bss must
-	 * be claimed (it will be zero'd by the kernel itself)
+	/* We need to alloc the memsize: gzip will expand the kernel
+	 * text/data, then possible rubbish we don't care about. But
+	 * the kernel bss must be claimed (it will be zero'd by the
+	 * kernel itself)
 	 */
-	printf("Allocating 0x%lx bytes for kernel ...\n\r", vmlinux.memsize);
-	vmlinux.addr = (unsigned long)malloc(vmlinux.memsize);
-	if (vmlinux.addr == 0) {
-		printf("Can't allocate memory for kernel image !\n\r");
-		exit();
+	printf("Allocating 0x%lx bytes for kernel ...\n\r", ei.memsize);
+
+	if (platform_ops.vmlinux_alloc) {
+		addr = platform_ops.vmlinux_alloc(ei.memsize);
+	} else {
+		if ((unsigned long)_start < ei.memsize)
+			fatal("Insufficient memory for kernel at address 0!"
+			       " (_start=%p)\n\r", _start);
 	}
 
+	/* Finally, gunzip the kernel */
+	printf("gunzipping (0x%p <- 0x%p:0x%p)...", addr,
+	       vmlinuz_addr, vmlinuz_addr+vmlinuz_size);
+	/* discard up to the actual load data */
+	gunzip_discard(&gzstate, ei.elfoffset - sizeof(elfheader));
+	len = gunzip_finish(&gzstate, addr, ei.loadsize);
+	if (len != ei.loadsize)
+		fatal("ran out of data!  only got 0x%x of 0x%lx bytes.\n\r",
+				len, ei.loadsize);
+	printf("done 0x%x bytes\n\r", len);
+
+	flush_cache(addr, ei.loadsize);
+
+	return (struct addr_range){addr, ei.memsize};
+}
+
+static struct addr_range prep_initrd(struct addr_range vmlinux, void *chosen,
+				     unsigned long initrd_addr,
+				     unsigned long initrd_size)
+{
+	/* If we have an image attached to us, it overrides anything
+	 * supplied by the loader. */
+	if (_initrd_end > _initrd_start) {
+		printf("Attached initrd image at 0x%p-0x%p\n\r",
+		       _initrd_start, _initrd_end);
+		initrd_addr = (unsigned long)_initrd_start;
+		initrd_size = _initrd_end - _initrd_start;
+	} else if (initrd_size > 0) {
+		printf("Using loader supplied ramdisk at 0x%lx-0x%lx\n\r",
+		       initrd_addr, initrd_addr + initrd_size);
+	}
+
+	/* If there's no initrd at all, we're done */
+	if (! initrd_size)
+		return (struct addr_range){0, 0};
+
 	/*
-	 * Now find the initrd
-	 *
-	 * First see if we have an image attached to us.  If so
-	 * allocate memory for it and copy it there.
+	 * If the initrd is too low it will be clobbered when the
+	 * kernel relocates to its final location.  In this case,
+	 * allocate a safer place and move it.
 	 */
-	initrd.size = (unsigned long)(_initrd_end - _initrd_start);
-	initrd.memsize = initrd.size;
-	if (initrd.size > 0) {
+	if (initrd_addr < vmlinux.size) {
+		void *old_addr = (void *)initrd_addr;
+
 		printf("Allocating 0x%lx bytes for initrd ...\n\r",
-		       initrd.size);
-		initrd.addr = (unsigned long)malloc((u32)initrd.size);
-		if (initrd.addr == 0) {
-			printf("Can't allocate memory for initial "
-					"ramdisk !\n\r");
-			exit();
-		}
-		printf("initial ramdisk moving 0x%lx <- 0x%lx "
-			"(0x%lx bytes)\n\r", initrd.addr,
-			(unsigned long)_initrd_start, initrd.size);
-		memmove((void *)initrd.addr, (void *)_initrd_start,
-			initrd.size);
-		printf("initrd head: 0x%lx\n\r",
-				*((unsigned long *)initrd.addr));
-	} else if (a2 != 0) {
-		/* Otherwise, see if yaboot or another loader gave us an initrd */
-		initrd.addr = a1;
-		initrd.memsize = initrd.size = a2;
-		printf("Using loader supplied initrd at 0x%lx (0x%lx bytes)\n\r",
-		       initrd.addr, initrd.size);
+		       initrd_size);
+		initrd_addr = (unsigned long)malloc(initrd_size);
+		if (! initrd_addr)
+			fatal("Can't allocate memory for initial "
+			       "ramdisk !\n\r");
+		printf("Relocating initrd 0x%lx <- 0x%p (0x%lx bytes)\n\r",
+		       initrd_addr, old_addr, initrd_size);
+		memmove((void *)initrd_addr, old_addr, initrd_size);
 	}
 
-	/* Eventually gunzip the kernel */
-	if (*(unsigned short *)vmlinuz.addr == 0x1f8b) {
-		printf("gunzipping (0x%lx <- 0x%lx:0x%0lx)...",
-		       vmlinux.addr, vmlinuz.addr, vmlinuz.addr+vmlinuz.size);
-		len = vmlinuz.size;
-		gunzip((void *)vmlinux.addr, vmlinux.memsize,
-			(unsigned char *)vmlinuz.addr, &len);
-		printf("done 0x%lx bytes\n\r", len);
-	} else {
-		memmove((void *)vmlinux.addr,(void *)vmlinuz.addr,
-			vmlinuz.size);
-	}
+	printf("initrd head: 0x%lx\n\r", *((unsigned long *)initrd_addr));
 
-	/* Skip over the ELF header */
-#ifdef DEBUG
-	printf("... skipping 0x%lx bytes of ELF header\n\r",
-			elfoffset);
-#endif
-	vmlinux.addr += elfoffset;
+	/* Tell the kernel initrd address via device tree */
+	setprop_val(chosen, "linux,initrd-start", (u32)(initrd_addr));
+	setprop_val(chosen, "linux,initrd-end", (u32)(initrd_addr+initrd_size));
 
-	flush_cache((void *)vmlinux.addr, vmlinux.size);
+	return (struct addr_range){(void *)initrd_addr, initrd_size};
 }
 
 /* A buffer that may be edited by tools operating on a zImage binary so as to
  * edit the command line passed to vmlinux (by setting /chosen/bootargs).
  * The buffer is put in it's own section so that tools may locate it easier.
  */
-static char builtin_cmdline[COMMAND_LINE_SIZE]
+static char cmdline[COMMAND_LINE_SIZE]
 	__attribute__((__section__("__builtin_cmdline")));
 
-static void get_cmdline(char *buf, int size)
+static void prep_cmdline(void *chosen)
 {
-	void *devp;
-	int len = strlen(builtin_cmdline);
+	if (cmdline[0] == '\0')
+		getprop(chosen, "bootargs", cmdline, COMMAND_LINE_SIZE-1);
 
-	buf[0] = '\0';
+	printf("\n\rLinux/PowerPC load: %s", cmdline);
+	/* If possible, edit the command line */
+	if (console_ops.edit_cmdline)
+		console_ops.edit_cmdline(cmdline, COMMAND_LINE_SIZE);
+	printf("\n\r");
 
-	if (len > 0) { /* builtin_cmdline overrides dt's /chosen/bootargs */
-		len = min(len, size-1);
-		strncpy(buf, builtin_cmdline, len);
-		buf[len] = '\0';
-	}
-	else if ((devp = finddevice("/chosen")))
-		getprop(devp, "bootargs", buf, size);
-}
-
-static void set_cmdline(char *buf)
-{
-	void *devp;
-
-	if ((devp = finddevice("/chosen")))
-		setprop(devp, "bootargs", buf, strlen(buf) + 1);
+	/* Put the command line back into the devtree for the kernel */
+	setprop_str(chosen, "bootargs", cmdline);
 }
 
 struct platform_ops platform_ops;
 struct dt_ops dt_ops;
 struct console_ops console_ops;
+struct loader_info loader_info;
 
-void start(unsigned long a1, unsigned long a2, void *promptr, void *sp)
+void start(void)
 {
+	struct addr_range vmlinux, initrd;
 	kernel_entry_t kentry;
-	char cmdline[COMMAND_LINE_SIZE];
 	unsigned long ft_addr = 0;
+	void *chosen;
 
-	memset(__bss_start, 0, _end - __bss_start);
-	memset(&platform_ops, 0, sizeof(platform_ops));
-	memset(&dt_ops, 0, sizeof(dt_ops));
-	memset(&console_ops, 0, sizeof(console_ops));
+	/* Do this first, because malloc() could clobber the loader's
+	 * command line.  Only use the loader command line if a
+	 * built-in command line wasn't set by an external tool */
+	if ((loader_info.cmdline_len > 0) && (cmdline[0] == '\0'))
+		memmove(cmdline, loader_info.cmdline,
+			min(loader_info.cmdline_len, COMMAND_LINE_SIZE-1));
 
-	if (platform_init(promptr, _dtb_start, _dtb_end))
-		exit();
 	if (console_ops.open && (console_ops.open() < 0))
 		exit();
 	if (platform_ops.fixups)
 		platform_ops.fixups();
 
 	printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r",
-	       _start, sp);
+	       _start, get_sp());
 
-	prep_kernel(a1, a2);
+	/* Ensure that the device tree has a /chosen node */
+	chosen = finddevice("/chosen");
+	if (!chosen)
+		chosen = create_node(NULL, "chosen");
 
-	/* If cmdline came from zimage wrapper or if we can edit the one
-	 * in the dt, print it out and edit it, if possible.
-	 */
-	if ((strlen(builtin_cmdline) > 0) || console_ops.edit_cmdline) {
-		get_cmdline(cmdline, COMMAND_LINE_SIZE);
-		printf("\n\rLinux/PowerPC load: %s", cmdline);
-		if (console_ops.edit_cmdline)
-			console_ops.edit_cmdline(cmdline, COMMAND_LINE_SIZE);
-		printf("\n\r");
-		set_cmdline(cmdline);
-	}
+	vmlinux = prep_kernel();
+	initrd = prep_initrd(vmlinux, chosen,
+			     loader_info.initrd_addr, loader_info.initrd_size);
+	prep_cmdline(chosen);
 
 	printf("Finalizing device tree...");
 	if (dt_ops.finalize)
@@ -335,7 +203,7 @@
 	if (ft_addr)
 		printf(" flat tree at 0x%lx\n\r", ft_addr);
 	else
-		printf(" using OF tree (promptr=%p)\n\r", promptr);
+		printf(" using OF tree (promptr=%p)\n\r", loader_info.promptr);
 
 	if (console_ops.close)
 		console_ops.close();
@@ -344,10 +212,9 @@
 	if (ft_addr)
 		kentry(ft_addr, 0, NULL);
 	else
-		/* XXX initrd addr/size should be passed in properties */
-		kentry(initrd.addr, initrd.size, promptr);
+		kentry((unsigned long)initrd.addr, initrd.size,
+		       loader_info.promptr);
 
-	/* console closed so printf below may not work */
-	printf("Error: Linux kernel returned to zImage boot wrapper!\n\r");
-	exit();
+	/* console closed so printf in fatal below may not work */
+	fatal("Error: Linux kernel returned to zImage boot wrapper!\n\r");
 }
diff --git a/arch/powerpc/boot/ns16550.c b/arch/powerpc/boot/ns16550.c
index 1ffe72e..f8f1b2f 100644
--- a/arch/powerpc/boot/ns16550.c
+++ b/arch/powerpc/boot/ns16550.c
@@ -55,10 +55,15 @@
 int ns16550_console_init(void *devp, struct serial_console_data *scdp)
 {
 	int n;
+	unsigned long reg_phys;
 
 	n = getprop(devp, "virtual-reg", &reg_base, sizeof(reg_base));
-	if (n != sizeof(reg_base))
-		return -1;
+	if (n != sizeof(reg_base)) {
+		if (!dt_xlate_reg(devp, 0, &reg_phys, NULL))
+			return -1;
+
+		reg_base = (void *)reg_phys;
+	}
 
 	n = getprop(devp, "reg-shift", &reg_shift, sizeof(reg_shift));
 	if (n != sizeof(reg_shift))
diff --git a/arch/powerpc/boot/of.c b/arch/powerpc/boot/of.c
index 0182f38..d16ee3e 100644
--- a/arch/powerpc/boot/of.c
+++ b/arch/powerpc/boot/of.c
@@ -173,7 +173,7 @@
 	return (void *) virt;
 }
 
-static void *of_try_claim(u32 size)
+static void *of_try_claim(unsigned long size)
 {
 	unsigned long addr = 0;
 
@@ -208,6 +208,16 @@
 	}
 }
 
+static void *of_vmlinux_alloc(unsigned long size)
+{
+	void *p = malloc(size);
+
+	if (!p)
+		fatal("Can't allocate memory for kernel image!\n\r");
+
+	return p;
+}
+
 static void of_exit(void)
 {
 	call_prom("exit", 0, 0);
@@ -256,11 +266,12 @@
 	call_prom("write", 3, 1, of_stdout_handle, buf, len);
 }
 
-int platform_init(void *promptr, char *dt_blob_start, char *dt_blob_end)
+void platform_init(unsigned long a1, unsigned long a2, void *promptr)
 {
 	platform_ops.image_hdr = of_image_hdr;
 	platform_ops.malloc = of_try_claim;
 	platform_ops.exit = of_exit;
+	platform_ops.vmlinux_alloc = of_vmlinux_alloc;
 
 	dt_ops.finddevice = of_finddevice;
 	dt_ops.getprop = of_getprop;
@@ -270,5 +281,9 @@
 	console_ops.write = of_console_write;
 
 	prom = (int (*)(void *))promptr;
-	return 0;
+	loader_info.promptr = promptr;
+	if (a1 && a2 && a2 != 0xdeadbeef) {
+		loader_info.initrd_addr = a1;
+		loader_info.initrd_size = a2;
+	}
 }
diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h
index 8abb651..73bd47a 100644
--- a/arch/powerpc/boot/ops.h
+++ b/arch/powerpc/boot/ops.h
@@ -11,7 +11,9 @@
 #ifndef _PPC_BOOT_OPS_H_
 #define _PPC_BOOT_OPS_H_
 
+#include <stddef.h>
 #include "types.h"
+#include "string.h"
 
 #define	COMMAND_LINE_SIZE	512
 #define	MAX_PATH_LEN		256
@@ -21,10 +23,11 @@
 struct platform_ops {
 	void	(*fixups)(void);
 	void	(*image_hdr)(const void *);
-	void *	(*malloc)(u32 size);
+	void *	(*malloc)(unsigned long size);
 	void	(*free)(void *ptr);
 	void *	(*realloc)(void *ptr, unsigned long size);
 	void	(*exit)(void);
+	void *	(*vmlinux_alloc)(unsigned long size);
 };
 extern struct platform_ops platform_ops;
 
@@ -35,6 +38,12 @@
 			const int buflen);
 	int	(*setprop)(const void *phandle, const char *name,
 			const void *buf, const int buflen);
+	void *(*get_parent)(const void *phandle);
+	/* The node must not already exist. */
+	void *(*create_node)(const void *parent, const char *name);
+	void *(*find_node_by_prop_value)(const void *prev,
+	                                 const char *propname,
+	                                 const char *propval, int proplen);
 	unsigned long (*finalize)(void);
 };
 extern struct dt_ops dt_ops;
@@ -58,13 +67,23 @@
 	void		(*close)(void);
 };
 
-int platform_init(void *promptr, char *dt_blob_start, char *dt_blob_end);
+struct loader_info {
+	void *promptr;
+	unsigned long initrd_addr, initrd_size;
+	char *cmdline;
+	int cmdline_len;
+};
+extern struct loader_info loader_info;
+
+void start(void);
 int ft_init(void *dt_blob, unsigned int max_size, unsigned int max_find_device);
 int serial_console_init(void);
 int ns16550_console_init(void *devp, struct serial_console_data *scdp);
-void *simple_alloc_init(char *base, u32 heap_size, u32 granularity,
-		u32 max_allocs);
-
+void *simple_alloc_init(char *base, unsigned long heap_size,
+			unsigned long granularity, unsigned long max_allocs);
+extern void flush_cache(void *, unsigned long);
+int dt_xlate_reg(void *node, int res, unsigned long *addr, unsigned long *size);
+int dt_xlate_addr(void *node, u32 *buf, int buflen, unsigned long *xlated_addr);
 
 static inline void *finddevice(const char *name)
 {
@@ -76,12 +95,76 @@
 	return (dt_ops.getprop) ? dt_ops.getprop(devp, name, buf, buflen) : -1;
 }
 
-static inline int setprop(void *devp, const char *name, void *buf, int buflen)
+static inline int setprop(void *devp, const char *name,
+                          const void *buf, int buflen)
 {
 	return (dt_ops.setprop) ? dt_ops.setprop(devp, name, buf, buflen) : -1;
 }
+#define setprop_val(devp, name, val) \
+	do { \
+		typeof(val) x = (val); \
+		setprop((devp), (name), &x, sizeof(x)); \
+	} while (0)
 
-static inline void *malloc(u32 size)
+static inline int setprop_str(void *devp, const char *name, const char *buf)
+{
+	if (dt_ops.setprop)
+		return dt_ops.setprop(devp, name, buf, strlen(buf) + 1);
+
+	return -1;
+}
+
+static inline void *get_parent(const char *devp)
+{
+	return dt_ops.get_parent ? dt_ops.get_parent(devp) : NULL;
+}
+
+static inline void *create_node(const void *parent, const char *name)
+{
+	return dt_ops.create_node ? dt_ops.create_node(parent, name) : NULL;
+}
+
+
+static inline void *find_node_by_prop_value(const void *prev,
+                                            const char *propname,
+                                            const char *propval, int proplen)
+{
+	if (dt_ops.find_node_by_prop_value)
+		return dt_ops.find_node_by_prop_value(prev, propname,
+		                                      propval, proplen);
+
+	return NULL;
+}
+
+static inline void *find_node_by_prop_value_str(const void *prev,
+                                                const char *propname,
+                                                const char *propval)
+{
+	return find_node_by_prop_value(prev, propname, propval,
+	                               strlen(propval) + 1);
+}
+
+static inline void *find_node_by_devtype(const void *prev,
+                                         const char *type)
+{
+	return find_node_by_prop_value_str(prev, "device_type", type);
+}
+
+void dt_fixup_memory(u64 start, u64 size);
+void dt_fixup_cpu_clocks(u32 cpufreq, u32 tbfreq, u32 busfreq);
+void dt_fixup_clock(const char *path, u32 freq);
+void __dt_fixup_mac_addresses(u32 startindex, ...);
+#define dt_fixup_mac_addresses(...) \
+	__dt_fixup_mac_addresses(0, __VA_ARGS__, NULL)
+
+
+static inline void *find_node_by_linuxphandle(const u32 linuxphandle)
+{
+	return find_node_by_prop_value(NULL, "linux,phandle",
+			(char *)&linuxphandle, sizeof(u32));
+}
+
+static inline void *malloc(unsigned long size)
 {
 	return (platform_ops.malloc) ? platform_ops.malloc(size) : NULL;
 }
@@ -98,5 +181,11 @@
 		platform_ops.exit();
 	for(;;);
 }
+#define fatal(args...) { printf(args); exit(); }
+
+
+#define BSS_STACK(size) \
+	static char _bss_stack[size]; \
+	void *_platform_stack_top = _bss_stack + sizeof(_bss_stack);
 
 #endif /* _PPC_BOOT_OPS_H_ */
diff --git a/arch/powerpc/boot/ppcboot.h b/arch/powerpc/boot/ppcboot.h
new file mode 100644
index 0000000..5290ff2
--- /dev/null
+++ b/arch/powerpc/boot/ppcboot.h
@@ -0,0 +1,108 @@
+/*
+ * This interface is used for compatibility with old U-boots *ONLY*.
+ * Please do not imitate or extend this.
+ */
+
+/*
+ * (C) Copyright 2000, 2001
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.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
+ */
+
+#ifndef __PPCBOOT_H__
+#define __PPCBOOT_H__
+
+/*
+ * Board information passed to kernel from PPCBoot
+ *
+ * include/asm-ppc/ppcboot.h
+ */
+
+#include "types.h"
+
+typedef struct bd_info {
+	unsigned long	bi_memstart;	/* start of DRAM memory */
+	unsigned long	bi_memsize;	/* size	 of DRAM memory in bytes */
+	unsigned long	bi_flashstart;	/* start of FLASH memory */
+	unsigned long	bi_flashsize;	/* size	 of FLASH memory */
+	unsigned long	bi_flashoffset; /* reserved area for startup monitor */
+	unsigned long	bi_sramstart;	/* start of SRAM memory */
+	unsigned long	bi_sramsize;	/* size	 of SRAM memory */
+#if defined(TARGET_8xx) || defined(TARGET_CPM2) || defined(TARGET_85xx) ||\
+	defined(TARGET_83xx)
+	unsigned long	bi_immr_base;	/* base of IMMR register */
+#endif
+#if defined(TARGET_PPC_MPC52xx)
+	unsigned long   bi_mbar_base;   /* base of internal registers */
+#endif
+	unsigned long	bi_bootflags;	/* boot / reboot flag (for LynxOS) */
+	unsigned long	bi_ip_addr;	/* IP Address */
+	unsigned char	bi_enetaddr[6];	/* Ethernet address */
+	unsigned short	bi_ethspeed;	/* Ethernet speed in Mbps */
+	unsigned long	bi_intfreq;	/* Internal Freq, in MHz */
+	unsigned long	bi_busfreq;	/* Bus Freq, in MHz */
+#if defined(TARGET_CPM2)
+	unsigned long	bi_cpmfreq;	/* CPM_CLK Freq, in MHz */
+	unsigned long	bi_brgfreq;	/* BRG_CLK Freq, in MHz */
+	unsigned long	bi_sccfreq;	/* SCC_CLK Freq, in MHz */
+	unsigned long	bi_vco;		/* VCO Out from PLL, in MHz */
+#endif
+#if defined(TARGET_PPC_MPC52xx)
+	unsigned long   bi_ipbfreq;     /* IPB Bus Freq, in MHz */
+	unsigned long   bi_pcifreq;     /* PCI Bus Freq, in MHz */
+#endif
+	unsigned long	bi_baudrate;	/* Console Baudrate */
+#if defined(TARGET_4xx)
+	unsigned char	bi_s_version[4];	/* Version of this structure */
+	unsigned char	bi_r_version[32];	/* Version of the ROM (IBM) */
+	unsigned int	bi_procfreq;	/* CPU (Internal) Freq, in Hz */
+	unsigned int	bi_plb_busfreq;	/* PLB Bus speed, in Hz */
+	unsigned int	bi_pci_busfreq;	/* PCI Bus speed, in Hz */
+	unsigned char	bi_pci_enetaddr[6];	/* PCI Ethernet MAC address */
+#endif
+#if defined(TARGET_HYMOD)
+	hymod_conf_t	bi_hymod_conf;	/* hymod configuration information */
+#endif
+#if defined(TARGET_EVB64260) || defined(TARGET_405EP) || defined(TARGET_44x) || \
+	defined(TARGET_85xx) ||	defined(TARGET_83xx)
+	/* second onboard ethernet port */
+	unsigned char	bi_enet1addr[6];
+#define HAVE_ENET1ADDR
+#endif
+#if defined(TARGET_EVB64260) || defined(TARGET_440GX) || defined(TARGET_85xx)
+	/* third onboard ethernet ports */
+	unsigned char	bi_enet2addr[6];
+#define HAVE_ENET2ADDR
+#endif
+#if defined(TARGET_440GX)
+	/* fourth onboard ethernet ports */
+	unsigned char	bi_enet3addr[6];
+#define HAVE_ENET3ADDR
+#endif
+#if defined(TARGET_4xx)
+	unsigned int	bi_opbfreq;		/* OB clock in Hz */
+	int		bi_iic_fast[2];		/* Use fast i2c mode */
+#endif
+#if defined(TARGET_440GX)
+	int		bi_phynum[4];		/* phy mapping */
+	int		bi_phymode[4];		/* phy mode */
+#endif
+} bd_t;
+
+#define bi_tbfreq	bi_intfreq
+
+#endif	/* __PPCBOOT_H__ */
diff --git a/arch/powerpc/boot/reg.h b/arch/powerpc/boot/reg.h
new file mode 100644
index 0000000..d3cd9ee
--- /dev/null
+++ b/arch/powerpc/boot/reg.h
@@ -0,0 +1,22 @@
+#ifndef _PPC_BOOT_REG_H
+#define _PPC_BOOT_REG_H
+/*
+ * Copyright 2007 Davud Gibson, IBM Corporation.
+ *
+ * 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.
+ */
+
+static inline u32 mfpvr(void)
+{
+	u32 pvr;
+	asm volatile ("mfpvr	%0" : "=r"(pvr));
+	return pvr;
+}
+
+register void *__stack_pointer asm("r1");
+#define get_sp()	(__stack_pointer)
+
+#endif	/* _PPC_BOOT_REG_H */
diff --git a/arch/powerpc/boot/simple_alloc.c b/arch/powerpc/boot/simple_alloc.c
index cfe3a75..65ec135 100644
--- a/arch/powerpc/boot/simple_alloc.c
+++ b/arch/powerpc/boot/simple_alloc.c
@@ -19,24 +19,24 @@
 #define	ENTRY_IN_USE	0x02
 
 static struct alloc_info {
-	u32	flags;
-	u32	base;
-	u32	size;
+	unsigned long	flags;
+	unsigned long	base;
+	unsigned long	size;
 } *alloc_tbl;
 
-static u32 tbl_entries;
-static u32 alloc_min;
-static u32 next_base;
-static u32 space_left;
+static unsigned long tbl_entries;
+static unsigned long alloc_min;
+static unsigned long next_base;
+static unsigned long space_left;
 
 /*
  * First time an entry is used, its base and size are set.
  * An entry can be freed and re-malloc'd but its base & size don't change.
  * Should be smart enough for needs of bootwrapper.
  */
-static void *simple_malloc(u32 size)
+static void *simple_malloc(unsigned long size)
 {
-	u32 i;
+	unsigned long i;
 	struct alloc_info *p = alloc_tbl;
 
 	if (size == 0)
@@ -67,13 +67,14 @@
 
 static struct alloc_info *simple_find_entry(void *ptr)
 {
-	u32 i;
+	unsigned long i;
 	struct alloc_info *p = alloc_tbl;
 
 	for (i=0; i<tbl_entries; i++,p++) {
 		if (!(p->flags & ENTRY_BEEN_USED))
 			break;
-		if ((p->flags & ENTRY_IN_USE) && (p->base == (u32)ptr))
+		if ((p->flags & ENTRY_IN_USE) &&
+		    (p->base == (unsigned long)ptr))
 			return p;
 	}
 	return NULL;
@@ -122,10 +123,10 @@
  * Returns addr of first byte after heap so caller can see if it took
  * too much space.  If so, change args & try again.
  */
-void *simple_alloc_init(char *base, u32 heap_size, u32 granularity,
-		u32 max_allocs)
+void *simple_alloc_init(char *base, unsigned long heap_size,
+			unsigned long granularity, unsigned long max_allocs)
 {
-	u32 heap_base, tbl_size;
+	unsigned long heap_base, tbl_size;
 
 	heap_size = _ALIGN_UP(heap_size, granularity);
 	alloc_min = granularity;
@@ -136,7 +137,7 @@
 	alloc_tbl = (struct alloc_info *)_ALIGN_UP((unsigned long)base, 8);
 	memset(alloc_tbl, 0, tbl_size);
 
-	heap_base = _ALIGN_UP((u32)alloc_tbl + tbl_size, alloc_min);
+	heap_base = _ALIGN_UP((unsigned long)alloc_tbl + tbl_size, alloc_min);
 
 	next_base = heap_base;
 	space_left = heap_size;
diff --git a/arch/powerpc/boot/stdio.h b/arch/powerpc/boot/stdio.h
index 73b8a91..adffc58 100644
--- a/arch/powerpc/boot/stdio.h
+++ b/arch/powerpc/boot/stdio.h
@@ -7,11 +7,12 @@
 #define	EINVAL		22	/* Invalid argument */
 #define ENOSPC		28	/* No space left on device */
 
-extern int printf(const char *fmt, ...);
+extern int printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
 
 #define fprintf(fmt, args...)	printf(args)
 
-extern int sprintf(char *buf, const char *fmt, ...);
+extern int sprintf(char *buf, const char *fmt, ...)
+	__attribute__((format(printf, 2, 3)));
 
 extern int vsprintf(char *buf, const char *fmt, va_list args);
 
diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper
index 024e4d4..5cedd90 100755
--- a/arch/powerpc/boot/wrapper
+++ b/arch/powerpc/boot/wrapper
@@ -29,6 +29,7 @@
 dtb=
 dts=
 cacheit=
+gzip=.gz
 
 # cross-compilation prefix
 CROSS=
@@ -42,7 +43,7 @@
 usage() {
     echo 'Usage: wrapper [-o output] [-p platform] [-i initrd]' >&2
     echo '       [-d devtree] [-s tree.dts] [-c] [-C cross-prefix]' >&2
-    echo '       [-D datadir] [-W workingdir] [vmlinux]' >&2
+    echo '       [-D datadir] [-W workingdir] [--no-gzip] [vmlinux]' >&2
     exit 1
 }
 
@@ -91,6 +92,9 @@
 	[ "$#" -gt 0 ] || usage
 	tmpdir="$1"
 	;;
+    --no-gzip)
+        gzip=
+        ;;
     -?)
 	usage
 	;;
@@ -137,31 +141,44 @@
     ksection=image
     isection=initrd
     ;;
+cuboot*)
+    gzip=
+    ;;
 esac
 
 vmz="$tmpdir/`basename \"$kernel\"`.$ext"
-if [ -z "$cacheit" -o ! -f "$vmz.gz" -o "$vmz.gz" -ot "$kernel" ]; then
+if [ -z "$cacheit" -o ! -f "$vmz$gzip" -o "$vmz$gzip" -ot "$kernel" ]; then
     ${CROSS}objcopy $objflags "$kernel" "$vmz.$$"
-    gzip -f -9 "$vmz.$$"
+
+    if [ -n "$gzip" ]; then
+        gzip -f -9 "$vmz.$$"
+    fi
+
     if [ -n "$cacheit" ]; then
-	mv -f "$vmz.$$.gz" "$vmz.gz"
+	mv -f "$vmz.$$$gzip" "$vmz$gzip"
     else
 	vmz="$vmz.$$"
     fi
 fi
 
+vmz="$vmz$gzip"
+
 case "$platform" in
-uboot)
-    rm -f "$ofile"
+uboot|cuboot*)
     version=`${CROSS}strings "$kernel" | grep '^Linux version [-0-9.]' | \
 	cut -d' ' -f3`
     if [ -n "$version" ]; then
 	version="-n Linux-$version"
     fi
+esac
+
+case "$platform" in
+uboot)
+    rm -f "$ofile"
     mkimage -A ppc -O linux -T kernel -C gzip -a 00000000 -e 00000000 \
-	$version -d "$vmz.gz" "$ofile"
+	$version -d "$vmz" "$ofile"
     if [ -z "$cacheit" ]; then
-	rm -f $vmz.gz
+	rm -f "$vmz"
     fi
     exit 0
     ;;
@@ -173,9 +190,9 @@
 	--set-section-flags=$3=contents,alloc,load,readonly,data
 }
 
-addsec $tmp "$vmz.gz" $ksection $object/empty.o
+addsec $tmp "$vmz" $ksection $object/empty.o
 if [ -z "$cacheit" ]; then
-    rm -f "$vmz.gz"
+    rm -f "$vmz"
 fi
 
 if [ -n "$initrd" ]; then
@@ -191,7 +208,7 @@
 
 if [ "$platform" != "miboot" ]; then
     ${CROSS}ld -m elf32ppc -T $lds -o "$ofile" \
-	$object/crt0.o $platformo $tmp $object/wrapper.a
+	$platformo $tmp $object/wrapper.a
     rm $tmp
 fi
 
@@ -201,7 +218,19 @@
     $object/addnote "$ofile"
     ;;
 pmaccoff)
-    ${CROSS}objcopy -O aixcoff-rs6000 --set-start 0x500000 "$ofile"
+    entry=`objdump -f "$ofile" | grep '^start address ' | \
+	cut -d' ' -f3`
+    ${CROSS}objcopy -O aixcoff-rs6000 --set-start "$entry" "$ofile"
     $object/hack-coff "$ofile"
     ;;
+cuboot*)
+    base=`${CROSS}nm "$ofile" | grep ' _start$' | cut -d' ' -f1`
+    entry=`${CROSS}objdump -f "$ofile" | grep '^start address ' | \
+           cut -d' ' -f3`
+    mv "$ofile" "$ofile".elf
+    ${CROSS}objcopy -O binary "$ofile".elf "$ofile".bin
+    gzip -f -9 "$ofile".bin
+    mkimage -A ppc -O linux -T kernel -C gzip -a "$base" -e "$entry" \
+            $version -d "$ofile".bin.gz "$ofile"
+    ;;
 esac
diff --git a/arch/powerpc/boot/zImage.coff.lds.S b/arch/powerpc/boot/zImage.coff.lds.S
index a360905..fe87a90 100644
--- a/arch/powerpc/boot/zImage.coff.lds.S
+++ b/arch/powerpc/boot/zImage.coff.lds.S
@@ -1,5 +1,6 @@
 OUTPUT_ARCH(powerpc:common)
-ENTRY(_start)
+ENTRY(_zimage_start_opd)
+EXTERN(_zimage_start_opd)
 SECTIONS
 {
   . = (5*1024*1024);
diff --git a/arch/powerpc/boot/zImage.lds.S b/arch/powerpc/boot/zImage.lds.S
index 4be3c64..f6e380f 100644
--- a/arch/powerpc/boot/zImage.lds.S
+++ b/arch/powerpc/boot/zImage.lds.S
@@ -1,5 +1,6 @@
 OUTPUT_ARCH(powerpc:common)
 ENTRY(_zimage_start)
+EXTERN(_zimage_start)
 SECTIONS
 {
   . = (4*1024*1024);
diff --git a/arch/powerpc/configs/cell_defconfig b/arch/powerpc/configs/cell_defconfig
index cf7e316..6061e5f 100644
--- a/arch/powerpc/configs/cell_defconfig
+++ b/arch/powerpc/configs/cell_defconfig
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.21-rc3
-# Fri Mar  9 23:34:53 2007
+# Linux kernel version: 2.6.21-rc6
+# Mon Apr 23 20:46:48 2007
 #
 CONFIG_PPC64=y
 CONFIG_64BIT=y
@@ -139,11 +139,31 @@
 # CONFIG_PPC_PMAC is not set
 # CONFIG_PPC_MAPLE is not set
 # CONFIG_PPC_PASEMI is not set
+CONFIG_PPC_CELLEB=y
+CONFIG_PPC_PS3=y
+
+#
+# PS3 Platform Options
+#
+# CONFIG_PS3_ADVANCED is not set
+CONFIG_PS3_HTAB_SIZE=20
+# CONFIG_PS3_DYNAMIC_DMA is not set
+CONFIG_PS3_USE_LPAR_ADDR=y
+CONFIG_PS3_VUART=y
+CONFIG_PS3_PS3AV=y
+CONFIG_PS3_SYS_MANAGER=y
 CONFIG_PPC_CELL=y
 CONFIG_PPC_CELL_NATIVE=y
 CONFIG_PPC_IBM_CELL_BLADE=y
-CONFIG_PPC_PS3=y
-CONFIG_PPC_CELLEB=y
+
+#
+# Cell Broadband Engine options
+#
+CONFIG_SPU_FS=m
+CONFIG_SPU_BASE=y
+CONFIG_CBE_RAS=y
+CONFIG_CBE_THERM=m
+CONFIG_CBE_CPUFREQ=m
 CONFIG_PPC_NATIVE=y
 CONFIG_UDBG_RTAS_CONSOLE=y
 CONFIG_PPC_UDBG_BEAT=y
@@ -175,26 +195,6 @@
 CONFIG_MPIC=y
 
 #
-# Cell Broadband Engine options
-#
-CONFIG_SPU_FS=m
-CONFIG_SPU_BASE=y
-CONFIG_CBE_RAS=y
-CONFIG_CBE_THERM=m
-CONFIG_CBE_CPUFREQ=m
-
-#
-# PS3 Platform Options
-#
-# CONFIG_PS3_ADVANCED is not set
-CONFIG_PS3_HTAB_SIZE=20
-# CONFIG_PS3_DYNAMIC_DMA is not set
-CONFIG_PS3_USE_LPAR_ADDR=y
-CONFIG_PS3_VUART=y
-CONFIG_PS3_PS3AV=y
-CONFIG_PS3_SYS_MANAGER=y
-
-#
 # Kernel options
 #
 # CONFIG_HZ_100 is not set
@@ -534,7 +534,6 @@
 # CONFIG_BLK_DEV_OPTI621 is not set
 CONFIG_BLK_DEV_IDEDMA_PCI=y
 # CONFIG_BLK_DEV_IDEDMA_FORCED is not set
-CONFIG_IDEDMA_PCI_AUTO=y
 # CONFIG_IDEDMA_ONLYDISK is not set
 CONFIG_BLK_DEV_AEC62XX=y
 # CONFIG_BLK_DEV_ALI15X3 is not set
@@ -561,11 +560,10 @@
 # CONFIG_BLK_DEV_TRM290 is not set
 # CONFIG_BLK_DEV_VIA82CXXX is not set
 # CONFIG_BLK_DEV_TC86C001 is not set
-CONFIG_BLK_DEV_IDE_CELLEB=y
+CONFIG_BLK_DEV_CELLEB=y
 # CONFIG_IDE_ARM is not set
 CONFIG_BLK_DEV_IDEDMA=y
 # CONFIG_IDEDMA_IVB is not set
-CONFIG_IDEDMA_AUTO=y
 # CONFIG_BLK_DEV_HD is not set
 
 #
@@ -937,7 +935,7 @@
 # CONFIG_LEGACY_PTYS is not set
 CONFIG_HVC_DRIVER=y
 CONFIG_HVC_RTAS=y
-# CONFIG_HVC_BEAT is not set
+CONFIG_HVC_BEAT=y
 
 #
 # IPMI
@@ -1482,6 +1480,8 @@
 # Distributed Lock Manager
 #
 # CONFIG_DLM is not set
+# CONFIG_UCC_SLOW is not set
+# CONFIG_UCC_FAST is not set
 
 #
 # Library routines
@@ -1540,6 +1540,7 @@
 # CONFIG_FAULT_INJECTION is not set
 # CONFIG_DEBUG_STACKOVERFLOW is not set
 # CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUG_PAGEALLOC is not set
 CONFIG_DEBUGGER=y
 CONFIG_XMON=y
 CONFIG_XMON_DEFAULT=y
diff --git a/arch/powerpc/configs/g5_defconfig b/arch/powerpc/configs/g5_defconfig
index 7724847..3ccf19d 100644
--- a/arch/powerpc/configs/g5_defconfig
+++ b/arch/powerpc/configs/g5_defconfig
@@ -143,7 +143,7 @@
 CONFIG_U3_DART=y
 # CONFIG_PPC_RTAS is not set
 # CONFIG_MMIO_NVRAM is not set
-CONFIG_MPIC_BROKEN_U3=y
+CONFIG_MPIC_U3_HT_IRQS=y
 # CONFIG_PPC_MPC106 is not set
 CONFIG_PPC_970_NAP=y
 # CONFIG_PPC_INDIRECT_IO is not set
diff --git a/arch/powerpc/configs/maple_defconfig b/arch/powerpc/configs/maple_defconfig
index de97f2f..15366f0 100644
--- a/arch/powerpc/configs/maple_defconfig
+++ b/arch/powerpc/configs/maple_defconfig
@@ -146,7 +146,7 @@
 CONFIG_RTAS_PROC=y
 # CONFIG_RTAS_FLASH is not set
 # CONFIG_MMIO_NVRAM is not set
-CONFIG_MPIC_BROKEN_U3=y
+CONFIG_MPIC_U3_HT_IRQS=y
 # CONFIG_PPC_MPC106 is not set
 CONFIG_PPC_970_NAP=y
 # CONFIG_PPC_INDIRECT_IO is not set
diff --git a/arch/powerpc/configs/mpc832x_rdb_defconfig b/arch/powerpc/configs/mpc832x_rdb_defconfig
new file mode 100644
index 0000000..56fc0a8
--- /dev/null
+++ b/arch/powerpc/configs/mpc832x_rdb_defconfig
@@ -0,0 +1,1292 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.21-rc3
+# Mon Mar 12 17:32:19 2007
+#
+# CONFIG_PPC64 is not set
+CONFIG_PPC32=y
+CONFIG_PPC_MERGE=y
+CONFIG_MMU=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_IRQ_PER_CPU=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_ARCH_HAS_ILOG2_U32=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_GENERIC_FIND_NEXT_BIT=y
+CONFIG_PPC=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_GENERIC_NVRAM=y
+CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y
+CONFIG_ARCH_MAY_HAVE_PC_FDC=y
+CONFIG_PPC_OF=y
+CONFIG_PPC_UDBG_16550=y
+# CONFIG_GENERIC_TBSYNC is not set
+CONFIG_AUDIT_ARCH=y
+CONFIG_GENERIC_BUG=y
+CONFIG_DEFAULT_UIMAGE=y
+
+#
+# Processor support
+#
+# CONFIG_CLASSIC32 is not set
+# CONFIG_PPC_82xx is not set
+CONFIG_PPC_83xx=y
+# CONFIG_PPC_85xx is not set
+# CONFIG_PPC_86xx is not set
+# CONFIG_PPC_8xx is not set
+# CONFIG_40x is not set
+# CONFIG_44x is not set
+# CONFIG_E200 is not set
+CONFIG_6xx=y
+CONFIG_83xx=y
+CONFIG_PPC_FPU=y
+# CONFIG_PPC_DCR_NATIVE is not set
+# CONFIG_PPC_DCR_MMIO is not set
+CONFIG_PPC_STD_MMU=y
+CONFIG_PPC_STD_MMU_32=y
+# CONFIG_SMP is not set
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+# CONFIG_IPC_NS is not set
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_UTS_NS is not set
+# CONFIG_AUDIT is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_SYSFS_DEPRECATED=y
+# CONFIG_RELAY is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_KALLSYMS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+# CONFIG_EPOLL is not set
+CONFIG_SHMEM=y
+CONFIG_SLAB=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+# CONFIG_SLOB is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+# CONFIG_KMOD is not set
+
+#
+# Block layer
+#
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="anticipatory"
+CONFIG_QUICC_ENGINE=y
+CONFIG_PPC_GEN550=y
+# CONFIG_WANT_EARLY_SERIAL is not set
+
+#
+# Platform support
+#
+# CONFIG_MPC8313_RDB is not set
+# CONFIG_MPC832x_MDS is not set
+CONFIG_MPC832x_RDB=y
+# CONFIG_MPC834x_MDS is not set
+# CONFIG_MPC834x_ITX is not set
+# CONFIG_MPC836x_MDS is not set
+CONFIG_PPC_MPC832x=y
+# CONFIG_MPIC is not set
+
+#
+# Kernel options
+#
+# CONFIG_HIGHMEM is not set
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=250
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+CONFIG_MATH_EMULATION=y
+CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
+CONFIG_ARCH_FLATMEM_ENABLE=y
+CONFIG_ARCH_POPULATES_NODE_MAP=y
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_PROC_DEVICETREE=y
+# CONFIG_CMDLINE_BOOL is not set
+# CONFIG_PM is not set
+CONFIG_SECCOMP=y
+CONFIG_ISA_DMA_API=y
+
+#
+# Bus options
+#
+CONFIG_ZONE_DMA=y
+CONFIG_GENERIC_ISA_DMA=y
+# CONFIG_MPIC_WEIRD is not set
+# CONFIG_PPC_I8259 is not set
+CONFIG_PPC_INDIRECT_PCI=y
+CONFIG_FSL_SOC=y
+CONFIG_PCI=y
+CONFIG_PCI_DOMAINS=y
+# CONFIG_PCIEPORTBUS is not set
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+# CONFIG_PCCARD is not set
+
+#
+# PCI Hotplug Support
+#
+# CONFIG_HOTPLUG_PCI is not set
+
+#
+# Advanced setup
+#
+# CONFIG_ADVANCED_OPTIONS is not set
+
+#
+# Default settings for advanced configuration options are used
+#
+CONFIG_HIGHMEM_START=0xfe000000
+CONFIG_LOWMEM_SIZE=0x30000000
+CONFIG_KERNEL_START=0xc0000000
+CONFIG_TASK_SIZE=0x80000000
+CONFIG_BOOT_LOAD=0x00800000
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+# CONFIG_NETDEBUG is not set
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_INET6_XFRM_TUNNEL is not set
+# CONFIG_INET6_TUNNEL is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+
+#
+# DCCP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_DCCP is not set
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_SCTP is not set
+
+#
+# TIPC Configuration (EXPERIMENTAL)
+#
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_IEEE80211 is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_FW_LOADER is not set
+# CONFIG_SYS_HYPERVISOR is not set
+
+#
+# Connector - unified userspace <-> kernelspace linker
+#
+# CONFIG_CONNECTOR is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Plug and Play support
+#
+# CONFIG_PNPACPI is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_UMEM is not set
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_SX8 is not set
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=32768
+CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+
+#
+# Misc devices
+#
+# CONFIG_SGI_IOC4 is not set
+# CONFIG_TIFM_CORE is not set
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_BLK_DEV_SR is not set
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_3W_9XXX is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_AIC94XX is not set
+# CONFIG_SCSI_DPT_I2O is not set
+# CONFIG_SCSI_ARCMSR is not set
+# CONFIG_MEGARAID_NEWGEN is not set
+# CONFIG_MEGARAID_LEGACY is not set
+# CONFIG_MEGARAID_SAS is not set
+# CONFIG_SCSI_HPTIOP is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_STEX is not set
+# CONFIG_SCSI_SYM53C8XX_2 is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+# CONFIG_SCSI_QLA_FC is not set
+# CONFIG_SCSI_QLA_ISCSI is not set
+# CONFIG_SCSI_LPFC is not set
+# CONFIG_SCSI_DC395x is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_NSP32 is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_SRP is not set
+
+#
+# Serial ATA (prod) and Parallel ATA (experimental) drivers
+#
+# CONFIG_ATA is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+
+#
+# Fusion MPT device support
+#
+# CONFIG_FUSION is not set
+# CONFIG_FUSION_SPI is not set
+# CONFIG_FUSION_FC is not set
+# CONFIG_FUSION_SAS is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_IEEE1394 is not set
+
+#
+# I2O device support
+#
+# CONFIG_I2O is not set
+
+#
+# Macintosh device drivers
+#
+# CONFIG_MAC_EMUMOUSEBTN is not set
+# CONFIG_WINDFARM is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+
+#
+# PHY device support
+#
+# CONFIG_PHYLIB is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_HAPPYMEAL is not set
+# CONFIG_SUNGEM is not set
+# CONFIG_CASSINI is not set
+# CONFIG_NET_VENDOR_3COM is not set
+
+#
+# Tulip family network device support
+#
+# CONFIG_NET_TULIP is not set
+# CONFIG_HP100 is not set
+# CONFIG_NET_PCI is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_ACENIC is not set
+# CONFIG_DL2K is not set
+CONFIG_E1000=y
+# CONFIG_E1000_NAPI is not set
+# CONFIG_E1000_DISABLE_PACKET_SPLIT is not set
+# CONFIG_NS83820 is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
+# CONFIG_SIS190 is not set
+# CONFIG_SKGE is not set
+# CONFIG_SKY2 is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_TIGON3 is not set
+# CONFIG_BNX2 is not set
+# CONFIG_GIANFAR is not set
+CONFIG_UCC_GETH=y
+CONFIG_UGETH_NAPI=y
+# CONFIG_UGETH_MAGIC_PACKET is not set
+# CONFIG_UGETH_FILTERING is not set
+# CONFIG_UGETH_TX_ON_DEMOND is not set
+# CONFIG_QLA3XXX is not set
+# CONFIG_ATL1 is not set
+
+#
+# Ethernet (10000 Mbit)
+#
+# CONFIG_CHELSIO_T1 is not set
+# CONFIG_CHELSIO_T3 is not set
+# CONFIG_IXGB is not set
+# CONFIG_S2IO is not set
+# CONFIG_MYRI10GE is not set
+# CONFIG_NETXEN_NIC is not set
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NET_FC is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_TSDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_PCI=y
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+
+#
+# Non-8250 serial port support
+#
+# CONFIG_SERIAL_UARTLITE is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_JSM is not set
+# CONFIG_SERIAL_OF_PLATFORM is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+CONFIG_WATCHDOG=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+CONFIG_83xx_WDT=y
+
+#
+# PCI-based Watchdog Cards
+#
+# CONFIG_PCIPCWATCHDOG is not set
+# CONFIG_WDTPCI is not set
+
+#
+# USB-based Watchdog Cards
+#
+# CONFIG_USBPCWATCHDOG is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_NVRAM is not set
+CONFIG_GEN_RTC=y
+# CONFIG_GEN_RTC_X is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+# CONFIG_AGP is not set
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# TPM devices
+#
+# CONFIG_TCG_TPM is not set
+
+#
+# I2C support
+#
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+
+#
+# I2C Algorithms
+#
+# CONFIG_I2C_ALGOBIT is not set
+# CONFIG_I2C_ALGOPCF is not set
+# CONFIG_I2C_ALGOPCA is not set
+
+#
+# I2C Hardware Bus support
+#
+# CONFIG_I2C_ALI1535 is not set
+# CONFIG_I2C_ALI1563 is not set
+# CONFIG_I2C_ALI15X3 is not set
+# CONFIG_I2C_AMD756 is not set
+# CONFIG_I2C_AMD8111 is not set
+# CONFIG_I2C_I801 is not set
+# CONFIG_I2C_I810 is not set
+# CONFIG_I2C_PIIX4 is not set
+CONFIG_I2C_MPC=y
+# CONFIG_I2C_NFORCE2 is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_PASEMI is not set
+# CONFIG_I2C_PROSAVAGE is not set
+# CONFIG_I2C_SAVAGE4 is not set
+# CONFIG_I2C_SIS5595 is not set
+# CONFIG_I2C_SIS630 is not set
+# CONFIG_I2C_SIS96X is not set
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_VIA is not set
+# CONFIG_I2C_VIAPRO is not set
+# CONFIG_I2C_VOODOO3 is not set
+# CONFIG_I2C_PCA_ISA is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_SENSORS_DS1337 is not set
+# CONFIG_SENSORS_DS1374 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_M41T00 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+
+#
+# SPI support
+#
+# CONFIG_SPI is not set
+# CONFIG_SPI_MASTER is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 is not set
+
+#
+# Hardware Monitoring support
+#
+CONFIG_HWMON=y
+# CONFIG_HWMON_VID is not set
+# CONFIG_SENSORS_ABITUGURU is not set
+# CONFIG_SENSORS_ADM1021 is not set
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1026 is not set
+# CONFIG_SENSORS_ADM1029 is not set
+# CONFIG_SENSORS_ADM1031 is not set
+# CONFIG_SENSORS_ADM9240 is not set
+# CONFIG_SENSORS_ASB100 is not set
+# CONFIG_SENSORS_ATXP1 is not set
+# CONFIG_SENSORS_DS1621 is not set
+# CONFIG_SENSORS_F71805F is not set
+# CONFIG_SENSORS_FSCHER is not set
+# CONFIG_SENSORS_FSCPOS is not set
+# CONFIG_SENSORS_GL518SM is not set
+# CONFIG_SENSORS_GL520SM is not set
+# CONFIG_SENSORS_IT87 is not set
+# CONFIG_SENSORS_LM63 is not set
+# CONFIG_SENSORS_LM75 is not set
+# CONFIG_SENSORS_LM77 is not set
+# CONFIG_SENSORS_LM78 is not set
+# CONFIG_SENSORS_LM80 is not set
+# CONFIG_SENSORS_LM83 is not set
+# CONFIG_SENSORS_LM85 is not set
+# CONFIG_SENSORS_LM87 is not set
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_LM92 is not set
+# CONFIG_SENSORS_MAX1619 is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_PC87427 is not set
+# CONFIG_SENSORS_SIS5595 is not set
+# CONFIG_SENSORS_SMSC47M1 is not set
+# CONFIG_SENSORS_SMSC47M192 is not set
+# CONFIG_SENSORS_SMSC47B397 is not set
+# CONFIG_SENSORS_VIA686A is not set
+# CONFIG_SENSORS_VT1211 is not set
+# CONFIG_SENSORS_VT8231 is not set
+# CONFIG_SENSORS_W83781D is not set
+# CONFIG_SENSORS_W83791D is not set
+# CONFIG_SENSORS_W83792D is not set
+# CONFIG_SENSORS_W83793 is not set
+# CONFIG_SENSORS_W83L785TS is not set
+# CONFIG_SENSORS_W83627HF is not set
+# CONFIG_SENSORS_W83627EHF is not set
+# CONFIG_HWMON_DEBUG_CHIP is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Digital Video Broadcasting Devices
+#
+# CONFIG_DVB is not set
+# CONFIG_USB_DABUSB is not set
+
+#
+# Graphics support
+#
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+# CONFIG_FB is not set
+# CONFIG_FB_IBM_GXT4500 is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# HID Devices
+#
+CONFIG_HID=y
+# CONFIG_HID_DEBUG is not set
+
+#
+# USB support
+#
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+CONFIG_USB_ARCH_HAS_EHCI=y
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_OTG is not set
+
+#
+# USB Host Controller Drivers
+#
+CONFIG_USB_EHCI_HCD=y
+# CONFIG_USB_EHCI_SPLIT_ISO is not set
+# CONFIG_USB_EHCI_ROOT_HUB_TT is not set
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+# CONFIG_USB_EHCI_BIG_ENDIAN_MMIO is not set
+# CONFIG_USB_ISP116X_HCD is not set
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_OHCI_HCD_PPC_OF=y
+CONFIG_USB_OHCI_HCD_PPC_OF_BE=y
+# CONFIG_USB_OHCI_HCD_PPC_OF_LE is not set
+CONFIG_USB_OHCI_HCD_PCI=y
+CONFIG_USB_OHCI_BIG_ENDIAN_DESC=y
+CONFIG_USB_OHCI_BIG_ENDIAN_MMIO=y
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+# CONFIG_USB_UHCI_HCD is not set
+# CONFIG_USB_SL811_HCD is not set
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# may also be needed; see USB_STORAGE Help for more information
+#
+CONFIG_USB_STORAGE=y
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_DPCM is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Input Devices
+#
+# CONFIG_USB_HID is not set
+
+#
+# USB HID Boot Protocol drivers
+#
+# CONFIG_USB_KBD is not set
+# CONFIG_USB_MOUSE is not set
+# CONFIG_USB_AIPTEK is not set
+# CONFIG_USB_WACOM is not set
+# CONFIG_USB_ACECAD is not set
+# CONFIG_USB_KBTAB is not set
+# CONFIG_USB_POWERMATE is not set
+# CONFIG_USB_TOUCHSCREEN is not set
+# CONFIG_USB_YEALINK is not set
+# CONFIG_USB_XPAD is not set
+# CONFIG_USB_ATI_REMOTE is not set
+# CONFIG_USB_ATI_REMOTE2 is not set
+# CONFIG_USB_KEYSPAN_REMOTE is not set
+# CONFIG_USB_APPLETOUCH is not set
+# CONFIG_USB_GTCO is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_MICROTEK is not set
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_USBNET_MII is not set
+# CONFIG_USB_USBNET is not set
+CONFIG_USB_MON=y
+
+#
+# USB port drivers
+#
+
+#
+# USB Serial Converter support
+#
+# CONFIG_USB_SERIAL is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_AUERSWALD is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_BERRY_CHARGE is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_PHIDGET is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_SISUSBVGA is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+
+#
+# USB DSL modem support
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+
+#
+# LED devices
+#
+# CONFIG_NEW_LEDS is not set
+
+#
+# LED drivers
+#
+
+#
+# LED Triggers
+#
+
+#
+# InfiniBand support
+#
+# CONFIG_INFINIBAND is not set
+
+#
+# EDAC - error detection and reporting (RAS) (EXPERIMENTAL)
+#
+
+#
+# Real Time Clock
+#
+# CONFIG_RTC_CLASS is not set
+
+#
+# DMA Engine support
+#
+# CONFIG_DMA_ENGINE is not set
+
+#
+# DMA Clients
+#
+
+#
+# DMA Devices
+#
+
+#
+# Auxiliary Display support
+#
+
+#
+# Virtualization
+#
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_XATTR=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# CONFIG_EXT3_FS_SECURITY is not set
+# CONFIG_EXT4DEV_FS is not set
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
+CONFIG_FS_MBCACHE=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+CONFIG_DNOTIFY=y
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+CONFIG_NFS_V4=y
+# CONFIG_NFS_DIRECTIO is not set
+# CONFIG_NFSD is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+# CONFIG_9P_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+CONFIG_LDM_PARTITION=y
+# CONFIG_LDM_DEBUG is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+CONFIG_NLS_CODEPAGE_932=y
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+CONFIG_NLS_ISO8859_8=y
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+
+#
+# Distributed Lock Manager
+#
+# CONFIG_DLM is not set
+
+#
+# QE Options
+#
+CONFIG_UCC_SLOW=y
+CONFIG_UCC_FAST=y
+CONFIG_UCC=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+
+#
+# Instrumentation Support
+#
+# CONFIG_PROFILING is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_MUST_CHECK=y
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_BOOTX_TEXT is not set
+# CONFIG_SERIAL_TEXT_DEBUG is not set
+# CONFIG_PPC_EARLY_DEBUG is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_MANAGER=y
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_XCBC is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_WP512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+CONFIG_CRYPTO_ECB=m
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_PCBC=m
+# CONFIG_CRYPTO_LRW is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Hardware crypto devices
+#
diff --git a/arch/powerpc/configs/mpc8544_ds_defconfig b/arch/powerpc/configs/mpc8544_ds_defconfig
new file mode 100644
index 0000000..b563513
--- /dev/null
+++ b/arch/powerpc/configs/mpc8544_ds_defconfig
@@ -0,0 +1,1077 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.21-rc3
+# Mon Mar 19 17:18:49 2007
+#
+# CONFIG_PPC64 is not set
+CONFIG_PPC32=y
+CONFIG_PPC_MERGE=y
+CONFIG_MMU=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_IRQ_PER_CPU=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_ARCH_HAS_ILOG2_U32=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_GENERIC_FIND_NEXT_BIT=y
+CONFIG_PPC=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_GENERIC_NVRAM=y
+CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y
+CONFIG_ARCH_MAY_HAVE_PC_FDC=y
+CONFIG_PPC_OF=y
+CONFIG_PPC_UDBG_16550=y
+# CONFIG_GENERIC_TBSYNC is not set
+CONFIG_AUDIT_ARCH=y
+CONFIG_GENERIC_BUG=y
+CONFIG_DEFAULT_UIMAGE=y
+
+#
+# Processor support
+#
+# CONFIG_CLASSIC32 is not set
+# CONFIG_PPC_82xx is not set
+# CONFIG_PPC_83xx is not set
+CONFIG_PPC_85xx=y
+# CONFIG_PPC_86xx is not set
+# CONFIG_PPC_8xx is not set
+# CONFIG_40x is not set
+# CONFIG_44x is not set
+# CONFIG_E200 is not set
+CONFIG_85xx=y
+CONFIG_E500=y
+# CONFIG_PPC_DCR_NATIVE is not set
+# CONFIG_PPC_DCR_MMIO is not set
+CONFIG_BOOKE=y
+CONFIG_FSL_BOOKE=y
+# CONFIG_PHYS_64BIT is not set
+# CONFIG_SPE is not set
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_IPC_NS=y
+CONFIG_SYSVIPC_SYSCTL=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_UTS_NS is not set
+CONFIG_AUDIT=y
+# CONFIG_AUDITSYSCALL is not set
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_SYSFS_DEPRECATED=y
+# CONFIG_RELAY is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+CONFIG_KALLSYMS_ALL=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+# CONFIG_HOTPLUG is not set
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SHMEM=y
+CONFIG_SLAB=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+# CONFIG_SLOB is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Block layer
+#
+CONFIG_BLOCK=y
+CONFIG_LBD=y
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+# CONFIG_WANT_EARLY_SERIAL is not set
+
+#
+# Platform support
+#
+# CONFIG_MPC8540_ADS is not set
+# CONFIG_MPC8560_ADS is not set
+# CONFIG_MPC85xx_CDS is not set
+# CONFIG_MPC85xx_MDS is not set
+CONFIG_MPC8544_DS=y
+CONFIG_MPC85xx=y
+CONFIG_PPC_INDIRECT_PCI_BE=y
+CONFIG_MPIC=y
+
+#
+# Kernel options
+#
+CONFIG_HIGHMEM=y
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=250
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_MISC=m
+CONFIG_MATH_EMULATION=y
+CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
+CONFIG_ARCH_FLATMEM_ENABLE=y
+CONFIG_ARCH_POPULATES_NODE_MAP=y
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_PROC_DEVICETREE=y
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="root=/dev/sda3 rw console=ttyS0,115200"
+# CONFIG_PM is not set
+CONFIG_SECCOMP=y
+CONFIG_ISA_DMA_API=y
+
+#
+# Bus options
+#
+CONFIG_ZONE_DMA=y
+# CONFIG_MPIC_WEIRD is not set
+# CONFIG_PPC_I8259 is not set
+CONFIG_PPC_INDIRECT_PCI=y
+CONFIG_FSL_SOC=y
+# CONFIG_PCI is not set
+# CONFIG_PCI_DOMAINS is not set
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+
+#
+# PCI Hotplug Support
+#
+
+#
+# Advanced setup
+#
+# CONFIG_ADVANCED_OPTIONS is not set
+
+#
+# Default settings for advanced configuration options are used
+#
+CONFIG_HIGHMEM_START=0xfe000000
+CONFIG_LOWMEM_SIZE=0x30000000
+CONFIG_KERNEL_START=0xc0000000
+CONFIG_TASK_SIZE=0x80000000
+CONFIG_BOOT_LOAD=0x00800000
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+# CONFIG_NETDEBUG is not set
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=m
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+CONFIG_NET_KEY=m
+# CONFIG_NET_KEY_MIGRATE is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_ASK_IP_FIB_HASH=y
+# CONFIG_IP_FIB_TRIE is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+# CONFIG_IP_ROUTE_MULTIPATH_CACHED is not set
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=y
+CONFIG_NET_IPGRE=y
+CONFIG_NET_IPGRE_BROADCAST=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_ARPD=y
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_INET6_XFRM_TUNNEL is not set
+# CONFIG_INET6_TUNNEL is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+
+#
+# DCCP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_DCCP is not set
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+CONFIG_IP_SCTP=m
+# CONFIG_SCTP_DBG_MSG is not set
+# CONFIG_SCTP_DBG_OBJCNT is not set
+# CONFIG_SCTP_HMAC_NONE is not set
+# CONFIG_SCTP_HMAC_SHA1 is not set
+CONFIG_SCTP_HMAC_MD5=y
+
+#
+# TIPC Configuration (EXPERIMENTAL)
+#
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_IEEE80211 is not set
+CONFIG_FIB_RULES=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+
+#
+# Connector - unified userspace <-> kernelspace linker
+#
+# CONFIG_CONNECTOR is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Plug and Play support
+#
+# CONFIG_PNPACPI is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+CONFIG_BLK_DEV_NBD=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=2
+CONFIG_BLK_DEV_RAM_SIZE=16384
+CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+
+#
+# Misc devices
+#
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=y
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_BLK_DEV_SR is not set
+CONFIG_CHR_DEV_SG=y
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_CONSTANTS is not set
+CONFIG_SCSI_LOGGING=y
+# CONFIG_SCSI_SCAN_ASYNC is not set
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_DEBUG is not set
+
+#
+# Serial ATA (prod) and Parallel ATA (experimental) drivers
+#
+CONFIG_ATA=y
+# CONFIG_ATA_NONSTANDARD is not set
+# CONFIG_PATA_PLATFORM is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+
+#
+# Fusion MPT device support
+#
+# CONFIG_FUSION is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+
+#
+# I2O device support
+#
+
+#
+# Macintosh device drivers
+#
+# CONFIG_MAC_EMUMOUSEBTN is not set
+# CONFIG_WINDFARM is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+
+#
+# PHY device support
+#
+CONFIG_PHYLIB=y
+
+#
+# MII PHY device drivers
+#
+# CONFIG_MARVELL_PHY is not set
+# CONFIG_DAVICOM_PHY is not set
+# CONFIG_QSEMI_PHY is not set
+# CONFIG_LXT_PHY is not set
+# CONFIG_CICADA_PHY is not set
+CONFIG_VITESSE_PHY=y
+# CONFIG_SMSC_PHY is not set
+# CONFIG_BROADCOM_PHY is not set
+# CONFIG_FIXED_PHY is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+
+#
+# Ethernet (1000 Mbit)
+#
+CONFIG_GIANFAR=y
+CONFIG_GFAR_NAPI=y
+
+#
+# Ethernet (10000 Mbit)
+#
+
+#
+# Token Ring devices
+#
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_TSDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+CONFIG_SERIO_I8042=y
+CONFIG_SERIO_SERPORT=y
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_SERIO_RAW is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+
+#
+# Non-8250 serial port support
+#
+# CONFIG_SERIAL_UARTLITE is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_OF_PLATFORM is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_HW_RANDOM is not set
+CONFIG_NVRAM=y
+CONFIG_GEN_RTC=y
+CONFIG_GEN_RTC_X=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# TPM devices
+#
+# CONFIG_TCG_TPM is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# SPI support
+#
+# CONFIG_SPI is not set
+# CONFIG_SPI_MASTER is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 is not set
+
+#
+# Hardware Monitoring support
+#
+# CONFIG_HWMON is not set
+# CONFIG_HWMON_VID is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Digital Video Broadcasting Devices
+#
+CONFIG_DVB=y
+CONFIG_DVB_CORE=m
+# CONFIG_DVB_CORE_ATTACH is not set
+
+#
+# Supported DVB Frontends
+#
+
+#
+# Customise DVB Frontends
+#
+# CONFIG_DVB_FE_CUSTOMISE is not set
+
+#
+# DVB-S (satellite) frontends
+#
+
+#
+# DVB-T (terrestrial) frontends
+#
+
+#
+# DVB-C (cable) frontends
+#
+
+#
+# ATSC (North American/Korean Terrestrial/Cable DTV) frontends
+#
+
+#
+# Tuners/PLL support
+#
+
+#
+# Miscellaneous devices
+#
+
+#
+# Graphics support
+#
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+# CONFIG_FB is not set
+# CONFIG_FB_IBM_GXT4500 is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# HID Devices
+#
+CONFIG_HID=y
+# CONFIG_HID_DEBUG is not set
+
+#
+# USB support
+#
+# CONFIG_USB_ARCH_HAS_HCD is not set
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+
+#
+# LED devices
+#
+# CONFIG_NEW_LEDS is not set
+
+#
+# LED drivers
+#
+
+#
+# LED Triggers
+#
+
+#
+# InfiniBand support
+#
+
+#
+# EDAC - error detection and reporting (RAS) (EXPERIMENTAL)
+#
+
+#
+# Real Time Clock
+#
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+
+#
+# RTC drivers
+#
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_TEST is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# DMA Engine support
+#
+# CONFIG_DMA_ENGINE is not set
+
+#
+# DMA Clients
+#
+
+#
+# DMA Devices
+#
+
+#
+# Auxiliary Display support
+#
+
+#
+# Virtualization
+#
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_XATTR=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# CONFIG_EXT3_FS_SECURITY is not set
+# CONFIG_EXT4DEV_FS is not set
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
+CONFIG_FS_MBCACHE=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+CONFIG_DNOTIFY=y
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+CONFIG_NTFS_FS=y
+# CONFIG_NTFS_DEBUG is not set
+# CONFIG_NTFS_RW is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+CONFIG_ADFS_FS=m
+# CONFIG_ADFS_FS_RW is not set
+CONFIG_AFFS_FS=m
+CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
+CONFIG_BEFS_FS=m
+# CONFIG_BEFS_DEBUG is not set
+CONFIG_BFS_FS=m
+CONFIG_EFS_FS=m
+CONFIG_CRAMFS=y
+CONFIG_VXFS_FS=m
+CONFIG_HPFS_FS=m
+CONFIG_QNX4FS_FS=m
+CONFIG_SYSV_FS=m
+CONFIG_UFS_FS=m
+# CONFIG_UFS_FS_WRITE is not set
+# CONFIG_UFS_DEBUG is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+CONFIG_NFS_V4=y
+# CONFIG_NFS_DIRECTIO is not set
+# CONFIG_NFSD is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+# CONFIG_9P_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+CONFIG_NLS_UTF8=m
+
+#
+# Distributed Lock Manager
+#
+# CONFIG_DLM is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+
+#
+# Instrumentation Support
+#
+# CONFIG_PROFILING is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_MUST_CHECK=y
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_TIMER_STATS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_HIGHMEM is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+CONFIG_DEBUG_INFO=y
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_LIST is not set
+CONFIG_FORCED_INLINING=y
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_DEBUG_STACKOVERFLOW is not set
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUGGER is not set
+# CONFIG_BDI_SWITCH is not set
+# CONFIG_BOOTX_TEXT is not set
+# CONFIG_PPC_EARLY_DEBUG is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_HMAC=y
+# CONFIG_CRYPTO_XCBC is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_WP512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_ECB is not set
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_PCBC=m
+# CONFIG_CRYPTO_LRW is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Hardware crypto devices
+#
diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig
index a8da0ae..126b9f8 100644
--- a/arch/powerpc/configs/ppc64_defconfig
+++ b/arch/powerpc/configs/ppc64_defconfig
@@ -152,7 +152,7 @@
 CONFIG_RTAS_PROC=y
 CONFIG_RTAS_FLASH=m
 CONFIG_MMIO_NVRAM=y
-CONFIG_MPIC_BROKEN_U3=y
+CONFIG_MPIC_U3_HT_IRQS=y
 CONFIG_IBMVIO=y
 # CONFIG_IBMEBUS is not set
 # CONFIG_PPC_MPC106 is not set
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 8120d42..e0fa80ec 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -25,8 +25,8 @@
 obj-$(CONFIG_PPC_OF)		+= of_device.o of_platform.o prom_parse.o
 procfs-$(CONFIG_PPC64)		:= proc_ppc64.o
 obj-$(CONFIG_PROC_FS)		+= $(procfs-y)
-rtaspci-$(CONFIG_PPC64)		:= rtas_pci.o
-obj-$(CONFIG_PPC_RTAS)		+= rtas.o rtas-rtc.o $(rtaspci-y)
+rtaspci-$(CONFIG_PPC64)-$(CONFIG_PCI)	:= rtas_pci.o
+obj-$(CONFIG_PPC_RTAS)		+= rtas.o rtas-rtc.o $(rtaspci-y-y)
 obj-$(CONFIG_RTAS_FLASH)	+= rtas_flash.o
 obj-$(CONFIG_RTAS_PROC)		+= rtas-proc.o
 obj-$(CONFIG_LPARCFG)		+= lparcfg.o
diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c
index 4734b5d..5c9ff7f 100644
--- a/arch/powerpc/kernel/align.c
+++ b/arch/powerpc/kernel/align.c
@@ -241,7 +241,7 @@
 	if (user_mode(regs) && !access_ok(VERIFY_WRITE, p, size))
 		return -EFAULT;
 	for (i = 0; i < size / sizeof(long); ++i)
-		if (__put_user(0, p+i))
+		if (__put_user_inatomic(0, p+i))
 			return -EFAULT;
 	return 1;
 }
@@ -288,7 +288,8 @@
 		} else {
 			unsigned long pc = regs->nip ^ (swiz & 4);
 
-			if (__get_user(instr, (unsigned int __user *)pc))
+			if (__get_user_inatomic(instr,
+						(unsigned int __user *)pc))
 				return -EFAULT;
 			if (swiz == 0 && (flags & SW))
 				instr = cpu_to_le32(instr);
@@ -324,27 +325,31 @@
 			       ((nb0 + 3) / 4) * sizeof(unsigned long));
 
 		for (i = 0; i < nb; ++i, ++p)
-			if (__get_user(REG_BYTE(rptr, i ^ bswiz), SWIZ_PTR(p)))
+			if (__get_user_inatomic(REG_BYTE(rptr, i ^ bswiz),
+						SWIZ_PTR(p)))
 				return -EFAULT;
 		if (nb0 > 0) {
 			rptr = &regs->gpr[0];
 			addr += nb;
 			for (i = 0; i < nb0; ++i, ++p)
-				if (__get_user(REG_BYTE(rptr, i ^ bswiz),
-					       SWIZ_PTR(p)))
+				if (__get_user_inatomic(REG_BYTE(rptr,
+								 i ^ bswiz),
+							SWIZ_PTR(p)))
 					return -EFAULT;
 		}
 
 	} else {
 		for (i = 0; i < nb; ++i, ++p)
-			if (__put_user(REG_BYTE(rptr, i ^ bswiz), SWIZ_PTR(p)))
+			if (__put_user_inatomic(REG_BYTE(rptr, i ^ bswiz),
+						SWIZ_PTR(p)))
 				return -EFAULT;
 		if (nb0 > 0) {
 			rptr = &regs->gpr[0];
 			addr += nb;
 			for (i = 0; i < nb0; ++i, ++p)
-				if (__put_user(REG_BYTE(rptr, i ^ bswiz),
-					       SWIZ_PTR(p)))
+				if (__put_user_inatomic(REG_BYTE(rptr,
+								 i ^ bswiz),
+							SWIZ_PTR(p)))
 					return -EFAULT;
 		}
 	}
@@ -398,7 +403,8 @@
 
 		if (cpu_has_feature(CPU_FTR_PPC_LE) && (regs->msr & MSR_LE))
 			pc ^= 4;
-		if (unlikely(__get_user(instr, (unsigned int __user *)pc)))
+		if (unlikely(__get_user_inatomic(instr,
+						 (unsigned int __user *)pc)))
 			return -EFAULT;
 		if (cpu_has_feature(CPU_FTR_REAL_LE) && (regs->msr & MSR_LE))
 			instr = cpu_to_le32(instr);
@@ -474,16 +480,16 @@
 		p = (unsigned long) addr;
 		switch (nb) {
 		case 8:
-			ret |= __get_user(data.v[0], SWIZ_PTR(p++));
-			ret |= __get_user(data.v[1], SWIZ_PTR(p++));
-			ret |= __get_user(data.v[2], SWIZ_PTR(p++));
-			ret |= __get_user(data.v[3], SWIZ_PTR(p++));
+			ret |= __get_user_inatomic(data.v[0], SWIZ_PTR(p++));
+			ret |= __get_user_inatomic(data.v[1], SWIZ_PTR(p++));
+			ret |= __get_user_inatomic(data.v[2], SWIZ_PTR(p++));
+			ret |= __get_user_inatomic(data.v[3], SWIZ_PTR(p++));
 		case 4:
-			ret |= __get_user(data.v[4], SWIZ_PTR(p++));
-			ret |= __get_user(data.v[5], SWIZ_PTR(p++));
+			ret |= __get_user_inatomic(data.v[4], SWIZ_PTR(p++));
+			ret |= __get_user_inatomic(data.v[5], SWIZ_PTR(p++));
 		case 2:
-			ret |= __get_user(data.v[6], SWIZ_PTR(p++));
-			ret |= __get_user(data.v[7], SWIZ_PTR(p++));
+			ret |= __get_user_inatomic(data.v[6], SWIZ_PTR(p++));
+			ret |= __get_user_inatomic(data.v[7], SWIZ_PTR(p++));
 			if (unlikely(ret))
 				return -EFAULT;
 		}
@@ -551,16 +557,16 @@
 		p = (unsigned long) addr;
 		switch (nb) {
 		case 8:
-			ret |= __put_user(data.v[0], SWIZ_PTR(p++));
-			ret |= __put_user(data.v[1], SWIZ_PTR(p++));
-			ret |= __put_user(data.v[2], SWIZ_PTR(p++));
-			ret |= __put_user(data.v[3], SWIZ_PTR(p++));
+			ret |= __put_user_inatomic(data.v[0], SWIZ_PTR(p++));
+			ret |= __put_user_inatomic(data.v[1], SWIZ_PTR(p++));
+			ret |= __put_user_inatomic(data.v[2], SWIZ_PTR(p++));
+			ret |= __put_user_inatomic(data.v[3], SWIZ_PTR(p++));
 		case 4:
-			ret |= __put_user(data.v[4], SWIZ_PTR(p++));
-			ret |= __put_user(data.v[5], SWIZ_PTR(p++));
+			ret |= __put_user_inatomic(data.v[4], SWIZ_PTR(p++));
+			ret |= __put_user_inatomic(data.v[5], SWIZ_PTR(p++));
 		case 2:
-			ret |= __put_user(data.v[6], SWIZ_PTR(p++));
-			ret |= __put_user(data.v[7], SWIZ_PTR(p++));
+			ret |= __put_user_inatomic(data.v[6], SWIZ_PTR(p++));
+			ret |= __put_user_inatomic(data.v[7], SWIZ_PTR(p++));
 		}
 		if (unlikely(ret))
 			return -EFAULT;
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 030d300..0c5150c 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -77,7 +77,6 @@
 	DEFINE(KSP_VSID, offsetof(struct thread_struct, ksp_vsid));
 #else /* CONFIG_PPC64 */
 	DEFINE(PGDIR, offsetof(struct thread_struct, pgdir));
-	DEFINE(LAST_SYSCALL, offsetof(struct thread_struct, last_syscall));
 #if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
 	DEFINE(THREAD_DBCR0, offsetof(struct thread_struct, dbcr0));
 	DEFINE(PT_PTRACED, PT_PTRACED);
@@ -140,6 +139,7 @@
 	DEFINE(PACA_SYSTEM_TIME, offsetof(struct paca_struct, system_time));
 	DEFINE(PACA_SLBSHADOWPTR, offsetof(struct paca_struct, slb_shadow_ptr));
 	DEFINE(PACA_DATA_OFFSET, offsetof(struct paca_struct, data_offset));
+	DEFINE(PACA_TRAP_SAVE, offsetof(struct paca_struct, trap_save));
 
 	DEFINE(SLBSHADOW_STACKVSID,
 	       offsetof(struct slb_shadow, save_area[SLB_NUM_BOLTED - 1].vsid));
diff --git a/arch/powerpc/kernel/btext.c b/arch/powerpc/kernel/btext.c
index 3678997..e7b6846 100644
--- a/arch/powerpc/kernel/btext.c
+++ b/arch/powerpc/kernel/btext.c
@@ -161,33 +161,33 @@
 	unsigned long address = 0;
 	const u32 *prop;
 
-	prop = get_property(np, "linux,bootx-width", NULL);
+	prop = of_get_property(np, "linux,bootx-width", NULL);
 	if (prop == NULL)
-		prop = get_property(np, "width", NULL);
+		prop = of_get_property(np, "width", NULL);
 	if (prop == NULL)
 		return -EINVAL;
 	width = *prop;
-	prop = get_property(np, "linux,bootx-height", NULL);
+	prop = of_get_property(np, "linux,bootx-height", NULL);
 	if (prop == NULL)
-		prop = get_property(np, "height", NULL);
+		prop = of_get_property(np, "height", NULL);
 	if (prop == NULL)
 		return -EINVAL;
 	height = *prop;
-	prop = get_property(np, "linux,bootx-depth", NULL);
+	prop = of_get_property(np, "linux,bootx-depth", NULL);
 	if (prop == NULL)
-		prop = get_property(np, "depth", NULL);
+		prop = of_get_property(np, "depth", NULL);
 	if (prop == NULL)
 		return -EINVAL;
 	depth = *prop;
 	pitch = width * ((depth + 7) / 8);
-	prop = get_property(np, "linux,bootx-linebytes", NULL);
+	prop = of_get_property(np, "linux,bootx-linebytes", NULL);
 	if (prop == NULL)
-		prop = get_property(np, "linebytes", NULL);
+		prop = of_get_property(np, "linebytes", NULL);
 	if (prop && *prop != 0xffffffffu)
 		pitch = *prop;
 	if (pitch == 1)
 		pitch = 0x1000;
-	prop = get_property(np, "address", NULL);
+	prop = of_get_property(np, "address", NULL);
 	if (prop)
 		address = *prop;
 
@@ -219,7 +219,7 @@
 	struct device_node *np = NULL; 
 	int rc = -ENODEV;
 
-	name = get_property(of_chosen, "linux,stdout-path", NULL);
+	name = of_get_property(of_chosen, "linux,stdout-path", NULL);
 	if (name != NULL) {
 		np = of_find_node_by_path(name);
 		if (np != NULL) {
@@ -236,7 +236,7 @@
 		return rc;
 
 	for (np = NULL; (np = of_find_node_by_type(np, "display"));) {
-		if (get_property(np, "linux,opened", NULL)) {
+		if (of_get_property(np, "linux,opened", NULL)) {
 			printk("trying %s ...\n", np->full_name);
 			rc = btext_initialize(np);
 			printk("result: %d\n", rc);
diff --git a/arch/powerpc/kernel/cpu_setup_pa6t.S b/arch/powerpc/kernel/cpu_setup_pa6t.S
index 4047be2..d62cb9c 100644
--- a/arch/powerpc/kernel/cpu_setup_pa6t.S
+++ b/arch/powerpc/kernel/cpu_setup_pa6t.S
@@ -34,7 +34,7 @@
 	beqlr
 
 	mfspr	r0,SPRN_HID5
-	ori	r0,r0,0x30
+	ori	r0,r0,0x38
 	mtspr	SPRN_HID5,r0
 
 	mfspr	r0,SPRN_LPCR
diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c
index e4006dc..9cb24d2 100644
--- a/arch/powerpc/kernel/cputable.c
+++ b/arch/powerpc/kernel/cputable.c
@@ -389,6 +389,8 @@
 		.pmc_type		= PPC_PMC_PA6T,
 		.cpu_setup		= __setup_cpu_pa6t,
 		.cpu_restore		= __restore_cpu_pa6t,
+		.oprofile_cpu_type	= "ppc64/pa6t",
+		.oprofile_type		= PPC_OPROFILE_PA6T,
 		.platform		= "pa6t",
 	},
 	{	/* default match */
@@ -558,6 +560,18 @@
 		.cpu_setup		= __setup_cpu_750cx,
 		.platform		= "ppc750",
 	},
+	{	/* 750CL */
+		.pvr_mask		= 0xfffff0f0,
+		.pvr_value		= 0x00087010,
+		.cpu_name		= "750CL",
+		.cpu_features		= CPU_FTRS_750CL,
+		.cpu_user_features	= COMMON_USER | PPC_FEATURE_PPC_LE,
+		.icache_bsize		= 32,
+		.dcache_bsize		= 32,
+		.num_pmcs		= 4,
+		.cpu_setup		= __setup_cpu_750,
+		.platform		= "ppc750",
+	},
 	{	/* 745/755 */
 		.pvr_mask		= 0xfffff000,
 		.pvr_value		= 0x00083000,
diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S
index c03e829..c29d165 100644
--- a/arch/powerpc/kernel/entry_32.S
+++ b/arch/powerpc/kernel/entry_32.S
@@ -191,7 +191,6 @@
 0:
 
 _GLOBAL(DoSyscall)
-	stw	r0,THREAD+LAST_SYSCALL(r2)
 	stw	r3,ORIG_GPR3(r1)
 	li	r12,0
 	stw	r12,RESULT(r1)
diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S
index 97cedcd6..1111fce 100644
--- a/arch/powerpc/kernel/head_64.S
+++ b/arch/powerpc/kernel/head_64.S
@@ -278,8 +278,12 @@
 	beq-	1f;							   \
 	ld	r1,PACAKSAVE(r13);	/* kernel stack to use		*/ \
 1:	cmpdi	cr1,r1,0;		/* check if r1 is in userspace	*/ \
-	bge-	cr1,bad_stack;		/* abort if it is		*/ \
-	std	r9,_CCR(r1);		/* save CR in stackframe	*/ \
+	bge-	cr1,2f;			/* abort if it is		*/ \
+	b	3f;							   \
+2:	li	r1,(n);			/* will be reloaded later	*/ \
+	sth	r1,PACA_TRAP_SAVE(r13);					   \
+	b	bad_stack;						   \
+3:	std	r9,_CCR(r1);		/* save CR in stackframe	*/ \
 	std	r11,_NIP(r1);		/* save SRR0 in stackframe	*/ \
 	std	r12,_MSR(r1);		/* save SRR1 in stackframe	*/ \
 	std	r10,0(r1);		/* make stack chain pointer	*/ \
@@ -940,6 +944,8 @@
 	SAVE_2GPRS(7,r1)
 	SAVE_10GPRS(12,r1)
 	SAVE_10GPRS(22,r1)
+	lhz	r12,PACA_TRAP_SAVE(r13)
+	std	r12,_TRAP(r1)
 	addi	r11,r1,INT_FRAME_SIZE
 	std	r11,0(r1)
 	li	r12,0
@@ -1555,7 +1561,6 @@
 	
 	/* turn on 64-bit mode */
 	bl	.enable_64b_mode
-	isync
 
 	/* Set up a paca value for this processor. Since we have the
 	 * physical cpu id in r24, we need to search the pacas to find
@@ -1735,10 +1740,6 @@
 	/* We never return */
 	trap
 
-/*
- * At this point, r3 contains the physical address we are running at,
- * returned by prom_init()
- */
 _STATIC(__after_prom_start)
 
 /*
@@ -1851,7 +1852,6 @@
 _GLOBAL(pmac_secondary_start)
 	/* turn on 64-bit mode */
 	bl	.enable_64b_mode
-	isync
 
 	/* Copy some CPU settings from CPU 0 */
 	bl	.__restore_cpu_ppc970
diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c
index 82bd2f1..9a8c9af 100644
--- a/arch/powerpc/kernel/ibmebus.c
+++ b/arch/powerpc/kernel/ibmebus.c
@@ -2,36 +2,37 @@
  * IBM PowerPC IBM eBus Infrastructure Support.
  *
  * Copyright (c) 2005 IBM Corporation
+ *  Joachim Fenkes <fenkes@de.ibm.com>
  *  Heiko J Schick <schickhj@de.ibm.com>
- *    
+ *
  * All rights reserved.
  *
- * This source code is distributed under a dual license of GPL v2.0 and OpenIB 
- * BSD. 
+ * This source code is distributed under a dual license of GPL v2.0 and OpenIB
+ * BSD.
  *
  * OpenIB BSD License
  *
- * Redistribution and use in source and binary forms, with or without 
- * modification, are permitted provided that the following conditions are met: 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
  *
- * Redistributions of source code must retain the above copyright notice, this 
- * list of conditions and the following disclaimer. 
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
  *
- * Redistributions in binary form must reproduce the above copyright notice, 
- * this list of conditions and the following disclaimer in the documentation 
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
  * and/or other materials
- * provided with the distribution. 
+ * provided with the distribution.
  *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 
+ * 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.
  */
 
@@ -43,19 +44,19 @@
 #include <asm/ibmebus.h>
 #include <asm/abs_addr.h>
 
-static struct ibmebus_dev ibmebus_bus_device = { /* fake "parent" device */
-	.name = ibmebus_bus_device.ofdev.dev.bus_id,
-	.ofdev.dev.bus_id = "ibmebus",
-	.ofdev.dev.bus    = &ibmebus_bus_type,
+static struct device ibmebus_bus_device = { /* fake "parent" device */
+	.bus_id = "ibmebus",
 };
 
+struct bus_type ibmebus_bus_type;
+
 static void *ibmebus_alloc_coherent(struct device *dev,
 				    size_t size,
 				    dma_addr_t *dma_handle,
 				    gfp_t flag)
 {
 	void *mem;
-	
+
 	mem = kmalloc(size, flag);
 	*dma_handle = (dma_addr_t)mem;
 
@@ -63,7 +64,7 @@
 }
 
 static void ibmebus_free_coherent(struct device *dev,
-				  size_t size, void *vaddr, 
+				  size_t size, void *vaddr,
 				  dma_addr_t dma_handle)
 {
 	kfree(vaddr);
@@ -79,7 +80,7 @@
 
 static void ibmebus_unmap_single(struct device *dev,
 				 dma_addr_t dma_addr,
-				 size_t size, 
+				 size_t size,
 				 enum dma_data_direction direction)
 {
 	return;
@@ -90,13 +91,13 @@
 			  int nents, enum dma_data_direction direction)
 {
 	int i;
-	
+
 	for (i = 0; i < nents; i++) {
-		sg[i].dma_address = (dma_addr_t)page_address(sg[i].page) 
+		sg[i].dma_address = (dma_addr_t)page_address(sg[i].page)
 			+ sg[i].offset;
 		sg[i].dma_length = sg[i].length;
 	}
-	
+
 	return nents;
 }
 
@@ -128,15 +129,15 @@
 	struct ibmebus_driver *ibmebusdrv = to_ibmebus_driver(dev->driver);
 	const struct of_device_id *id;
 	int error = -ENODEV;
-	
+
 	if (!ibmebusdrv->probe)
 		return error;
-	
+
 	id = of_match_device(ibmebusdrv->id_table, &ibmebusdev->ofdev);
 	if (id) {
 		error = ibmebusdrv->probe(ibmebusdev, id);
 	}
-	
+
 	return error;
 }
 
@@ -144,11 +145,11 @@
 {
 	struct ibmebus_dev *ibmebusdev    = to_ibmebus_dev(dev);
 	struct ibmebus_driver *ibmebusdrv = to_ibmebus_driver(dev->driver);
-	
+
 	if (ibmebusdrv->remove) {
 		return ibmebusdrv->remove(ibmebusdev);
 	}
-	
+
 	return 0;
 }
 
@@ -158,21 +159,12 @@
 	kfree(to_ibmebus_dev(dev));
 }
 
-static ssize_t ibmebusdev_show_name(struct device *dev, 
-				    struct device_attribute *attr, char *buf)
-{
-	return sprintf(buf, "%s\n", to_ibmebus_dev(dev)->name);
-}
-static DEVICE_ATTR(name, S_IRUSR | S_IRGRP | S_IROTH, ibmebusdev_show_name, 
-		   NULL);
-
-static struct ibmebus_dev* __devinit ibmebus_register_device_common(
+static int __devinit ibmebus_register_device_common(
 	struct ibmebus_dev *dev, const char *name)
 {
 	int err = 0;
 
-	dev->name = name;
-	dev->ofdev.dev.parent  = &ibmebus_bus_device.ofdev.dev;
+	dev->ofdev.dev.parent  = &ibmebus_bus_device;
 	dev->ofdev.dev.bus     = &ibmebus_bus_type;
 	dev->ofdev.dev.release = ibmebus_dev_release;
 
@@ -181,17 +173,15 @@
 	dev->ofdev.dev.archdata.numa_node = of_node_to_nid(dev->ofdev.node);
 
 	/* An ibmebusdev is based on a of_device. We have to change the
-	 * bus type to use our own DMA mapping operations. 
-	 */       
+	 * bus type to use our own DMA mapping operations.
+	 */
 	if ((err = of_device_register(&dev->ofdev)) != 0) {
 		printk(KERN_ERR "%s: failed to register device (%d).\n",
 		       __FUNCTION__, err);
-		return NULL;
+		return -ENODEV;
 	}
-	
-	device_create_file(&dev->ofdev.dev, &dev_attr_name);
-	
-	return dev;
+
+	return 0;
 }
 
 static struct ibmebus_dev* __devinit ibmebus_register_device_node(
@@ -201,35 +191,35 @@
 	const char *loc_code;
 	int length;
 
-	loc_code = get_property(dn, "ibm,loc-code", NULL);
+	loc_code = of_get_property(dn, "ibm,loc-code", NULL);
 	if (!loc_code) {
                 printk(KERN_WARNING "%s: node %s missing 'ibm,loc-code'\n",
 		       __FUNCTION__, dn->name ? dn->name : "<unknown>");
-		return NULL;
+		return ERR_PTR(-EINVAL);
         }
-	
+
 	if (strlen(loc_code) == 0) {
 	        printk(KERN_WARNING "%s: 'ibm,loc-code' is invalid\n",
 		       __FUNCTION__);
-		return NULL;
+		return ERR_PTR(-EINVAL);
 	}
 
 	dev = kzalloc(sizeof(struct ibmebus_dev), GFP_KERNEL);
 	if (!dev) {
-		return NULL;
+		return ERR_PTR(-ENOMEM);
 	}
 
 	dev->ofdev.node = of_node_get(dn);
-       
+
 	length = strlen(loc_code);
-	memcpy(dev->ofdev.dev.bus_id, loc_code 
-		+ (length - min(length, BUS_ID_SIZE - 1)), 
+	memcpy(dev->ofdev.dev.bus_id, loc_code
+		+ (length - min(length, BUS_ID_SIZE - 1)),
 		min(length, BUS_ID_SIZE - 1));
 
 	/* Register with generic device framework. */
-	if (ibmebus_register_device_common(dev, dn->name) == NULL) {
+	if (ibmebus_register_device_common(dev, dn->name) != 0) {
 		kfree(dev);
-		return NULL;
+		return ERR_PTR(-ENODEV);
 	}
 
 	return dev;
@@ -238,17 +228,16 @@
 static void ibmebus_probe_of_nodes(char* name)
 {
 	struct device_node *dn = NULL;
-	
+
 	while ((dn = of_find_node_by_name(dn, name))) {
-		if (ibmebus_register_device_node(dn) == NULL) {
+		if (IS_ERR(ibmebus_register_device_node(dn))) {
 			of_node_put(dn);
-			
 			return;
 		}
 	}
-	
+
 	of_node_put(dn);
-	
+
 	return;
 }
 
@@ -262,17 +251,21 @@
 	return;
 }
 
-static int ibmebus_match_helper(struct device *dev, void *data)
+static int ibmebus_match_name(struct device *dev, void *data)
 {
-	if (strcmp((char*)data, to_ibmebus_dev(dev)->name) == 0)
+	const struct ibmebus_dev *ebus_dev = to_ibmebus_dev(dev);
+	const char *name;
+
+	name = of_get_property(ebus_dev->ofdev.node, "name", NULL);
+
+	if (name && (strcmp(data, name) == 0))
 		return 1;
-	
+
 	return 0;
 }
 
 static int ibmebus_unregister_device(struct device *dev)
 {
-	device_remove_file(dev, &dev_attr_name);
 	of_device_unregister(to_of_device(dev));
 
 	return 0;
@@ -281,17 +274,16 @@
 static void ibmebus_remove_devices_by_id(struct of_device_id *idt)
 {
 	struct device *dev;
-	
+
 	while (strlen(idt->name) > 0) {
-		while ((dev = bus_find_device(&ibmebus_bus_type, NULL, 
+		while ((dev = bus_find_device(&ibmebus_bus_type, NULL,
 					      (void*)idt->name,
-					      ibmebus_match_helper))) {
+					      ibmebus_match_name))) {
 			ibmebus_unregister_device(dev);
 		}
 		idt++;
-		
 	}
-	
+
 	return;
 }
 
@@ -307,30 +299,33 @@
 	if ((err = driver_register(&drv->driver) != 0))
 		return err;
 
+	/* remove all supported devices first, in case someone
+	 * probed them manually before registering the driver */
+	ibmebus_remove_devices_by_id(drv->id_table);
 	ibmebus_add_devices_by_id(drv->id_table);
-	
+
 	return 0;
 }
 EXPORT_SYMBOL(ibmebus_register_driver);
 
 void ibmebus_unregister_driver(struct ibmebus_driver *drv)
-{	
+{
 	driver_unregister(&drv->driver);
 	ibmebus_remove_devices_by_id(drv->id_table);
 }
 EXPORT_SYMBOL(ibmebus_unregister_driver);
 
 int ibmebus_request_irq(struct ibmebus_dev *dev,
-			u32 ist, 
+			u32 ist,
 			irq_handler_t handler,
 			unsigned long irq_flags, const char * devname,
 			void *dev_id)
 {
 	unsigned int irq = irq_create_mapping(NULL, ist);
-	
+
 	if (irq == NO_IRQ)
 		return -EINVAL;
-	
+
 	return request_irq(irq, handler,
 			   irq_flags, devname, dev_id);
 }
@@ -339,56 +334,163 @@
 void ibmebus_free_irq(struct ibmebus_dev *dev, u32 ist, void *dev_id)
 {
 	unsigned int irq = irq_find_mapping(NULL, ist);
-	
+
 	free_irq(irq, dev_id);
 }
 EXPORT_SYMBOL(ibmebus_free_irq);
 
 static int ibmebus_bus_match(struct device *dev, struct device_driver *drv)
-{	
+{
 	const struct ibmebus_dev *ebus_dev = to_ibmebus_dev(dev);
 	struct ibmebus_driver *ebus_drv    = to_ibmebus_driver(drv);
 	const struct of_device_id *ids     = ebus_drv->id_table;
 	const struct of_device_id *found_id;
-	
+
 	if (!ids)
 		return 0;
-	
+
 	found_id = of_match_device(ids, &ebus_dev->ofdev);
 	if (found_id)
 		return 1;
-	
+
 	return 0;
 }
 
+static ssize_t name_show(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	struct ibmebus_dev *ebus_dev = to_ibmebus_dev(dev);
+	const char *name = of_get_property(ebus_dev->ofdev.node, "name", NULL);
+	return sprintf(buf, "%s\n", name);
+}
+
+static struct device_attribute ibmebus_dev_attrs[] = {
+	__ATTR_RO(name),
+	__ATTR_NULL
+};
+
+static int ibmebus_match_path(struct device *dev, void *data)
+{
+	int rc;
+	struct device_node *dn =
+		of_node_get(to_ibmebus_dev(dev)->ofdev.node);
+
+	rc = (dn->full_name && (strcasecmp((char*)data, dn->full_name) == 0));
+
+	of_node_put(dn);
+	return rc;
+}
+
+static char *ibmebus_chomp(const char *in, size_t count)
+{
+	char *out = (char*)kmalloc(count + 1, GFP_KERNEL);
+	if (!out)
+		return NULL;
+
+	memcpy(out, in, count);
+	out[count] = '\0';
+	if (out[count - 1] == '\n')
+		out[count - 1] = '\0';
+
+	return out;
+}
+
+static ssize_t ibmebus_store_probe(struct bus_type *bus,
+				   const char *buf, size_t count)
+{
+	struct device_node *dn = NULL;
+	struct ibmebus_dev *dev;
+	char *path;
+	ssize_t rc;
+
+	path = ibmebus_chomp(buf, count);
+	if (!path)
+		return -ENOMEM;
+
+	if (bus_find_device(&ibmebus_bus_type, NULL, path,
+			     ibmebus_match_path)) {
+		printk(KERN_WARNING "%s: %s has already been probed\n",
+		       __FUNCTION__, path);
+		rc = -EINVAL;
+		goto out;
+	}
+
+	if ((dn = of_find_node_by_path(path))) {
+		dev = ibmebus_register_device_node(dn);
+		of_node_put(dn);
+		rc = IS_ERR(dev) ? PTR_ERR(dev) : count;
+	} else {
+		printk(KERN_WARNING "%s: no such device node: %s\n",
+		       __FUNCTION__, path);
+		rc = -ENODEV;
+	}
+
+out:
+	kfree(path);
+	return rc;
+}
+
+static ssize_t ibmebus_store_remove(struct bus_type *bus,
+				    const char *buf, size_t count)
+{
+	struct device *dev;
+	char *path;
+
+	path = ibmebus_chomp(buf, count);
+	if (!path)
+		return -ENOMEM;
+
+	if ((dev = bus_find_device(&ibmebus_bus_type, NULL, path,
+				   ibmebus_match_path))) {
+		ibmebus_unregister_device(dev);
+
+		kfree(path);
+		return count;
+	} else {
+		printk(KERN_WARNING "%s: %s not on the bus\n",
+		       __FUNCTION__, path);
+
+		kfree(path);
+		return -ENODEV;
+	}
+}
+
+static struct bus_attribute ibmebus_bus_attrs[] = {
+	__ATTR(probe, S_IWUSR, NULL, ibmebus_store_probe),
+	__ATTR(remove, S_IWUSR, NULL, ibmebus_store_remove),
+	__ATTR_NULL
+};
+
 struct bus_type ibmebus_bus_type = {
-	.name = "ibmebus",
-	.match = ibmebus_bus_match,
+	.name      = "ibmebus",
+	.match     = ibmebus_bus_match,
+	.dev_attrs = ibmebus_dev_attrs,
+	.bus_attrs = ibmebus_bus_attrs
 };
 EXPORT_SYMBOL(ibmebus_bus_type);
 
 static int __init ibmebus_bus_init(void)
 {
 	int err;
-	
+
 	printk(KERN_INFO "IBM eBus Device Driver\n");
-	
+
 	err = bus_register(&ibmebus_bus_type);
 	if (err) {
 		printk(KERN_ERR ":%s: failed to register IBM eBus.\n",
 		       __FUNCTION__);
 		return err;
 	}
-	
-	err = device_register(&ibmebus_bus_device.ofdev.dev);
+
+	err = device_register(&ibmebus_bus_device);
 	if (err) {
-		printk(KERN_WARNING "%s: device_register returned %i\n", 
+		printk(KERN_WARNING "%s: device_register returned %i\n",
 		       __FUNCTION__, err);
 		bus_unregister(&ibmebus_bus_type);
 
 		return err;
 	}
-	
+
 	return 0;
 }
 __initcall(ibmebus_bus_init);
diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
index 95edad4..c08ceca 100644
--- a/arch/powerpc/kernel/iommu.c
+++ b/arch/powerpc/kernel/iommu.c
@@ -47,6 +47,8 @@
 static int novmerge = 1;
 #endif
 
+static int protect4gb = 1;
+
 static inline unsigned long iommu_num_pages(unsigned long vaddr,
 					    unsigned long slen)
 {
@@ -58,6 +60,16 @@
 	return npages;
 }
 
+static int __init setup_protect4gb(char *str)
+{
+	if (strcmp(str, "on") == 0)
+		protect4gb = 1;
+	else if (strcmp(str, "off") == 0)
+		protect4gb = 0;
+
+	return 1;
+}
+
 static int __init setup_iommu(char *str)
 {
 	if (!strcmp(str, "novmerge"))
@@ -67,6 +79,7 @@
 	return 1;
 }
 
+__setup("protect4gb=", setup_protect4gb);
 __setup("iommu=", setup_iommu);
 
 static unsigned long iommu_range_alloc(struct iommu_table *tbl,
@@ -429,6 +442,9 @@
 struct iommu_table *iommu_init_table(struct iommu_table *tbl, int nid)
 {
 	unsigned long sz;
+	unsigned long start_index, end_index;
+	unsigned long entries_per_4g;
+	unsigned long index;
 	static int welcomed = 0;
 	struct page *page;
 
@@ -450,7 +466,7 @@
 
 #ifdef CONFIG_CRASH_DUMP
 	if (ppc_md.tce_get) {
-		unsigned long index, tceval;
+		unsigned long tceval;
 		unsigned long tcecount = 0;
 
 		/*
@@ -480,6 +496,23 @@
 	ppc_md.tce_free(tbl, tbl->it_offset, tbl->it_size);
 #endif
 
+	/*
+	 * DMA cannot cross 4 GB boundary.  Mark last entry of each 4
+	 * GB chunk as reserved.
+	 */
+	if (protect4gb) {
+		entries_per_4g = 0x100000000l >> IOMMU_PAGE_SHIFT;
+
+		/* Mark the last bit before a 4GB boundary as used */
+		start_index = tbl->it_offset | (entries_per_4g - 1);
+		start_index -= tbl->it_offset;
+
+		end_index = tbl->it_size;
+
+		for (index = start_index; index < end_index - 1; index += entries_per_4g)
+			__set_bit(index, tbl->it_map);
+	}
+
 	if (!welcomed) {
 		printk(KERN_INFO "IOMMU table initialized, virtual merging %s\n",
 		       novmerge ? "disabled" : "enabled");
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index 1009308..6c83fe2 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -394,7 +394,7 @@
 #ifdef CONFIG_PPC_MERGE
 
 static LIST_HEAD(irq_hosts);
-static spinlock_t irq_big_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(irq_big_lock);
 static DEFINE_PER_CPU(unsigned int, irq_radix_reader);
 static unsigned int irq_radix_writer;
 struct irq_map_entry irq_map[NR_IRQS];
diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c
index dd2886f..ef647e7 100644
--- a/arch/powerpc/kernel/kprobes.c
+++ b/arch/powerpc/kernel/kprobes.c
@@ -59,12 +59,14 @@
 	}
 
 	if (!ret) {
-		memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
+		memcpy(p->ainsn.insn, p->addr,
+				MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
 		p->opcode = *p->addr;
 		flush_icache_range((unsigned long)p->ainsn.insn,
 			(unsigned long)p->ainsn.insn + sizeof(kprobe_opcode_t));
 	}
 
+	p->ainsn.boostable = 0;
 	return ret;
 }
 
@@ -232,6 +234,38 @@
 		return 1;
 
 ss_probe:
+	if (p->ainsn.boostable >= 0) {
+		unsigned int insn = *p->ainsn.insn;
+
+		/* regs->nip is also adjusted if emulate_step returns 1 */
+		ret = emulate_step(regs, insn);
+		if (ret > 0) {
+			/*
+			 * Once this instruction has been boosted
+			 * successfully, set the boostable flag
+			 */
+			if (unlikely(p->ainsn.boostable == 0))
+				p->ainsn.boostable = 1;
+
+			if (p->post_handler)
+				p->post_handler(p, regs, 0);
+
+			kcb->kprobe_status = KPROBE_HIT_SSDONE;
+			reset_current_kprobe();
+			preempt_enable_no_resched();
+			return 1;
+		} else if (ret < 0) {
+			/*
+			 * We don't allow kprobes on mtmsr(d)/rfi(d), etc.
+			 * So, we should never get here... but, its still
+			 * good to catch them, just in case...
+			 */
+			printk("Can't step on instruction %x\n", insn);
+			BUG();
+		} else if (ret == 0)
+			/* This instruction can't be boosted */
+			p->ainsn.boostable = -1;
+	}
 	prepare_singlestep(p, regs);
 	kcb->kprobe_status = KPROBE_HIT_SS;
 	return 1;
diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c
index 325f490..63dd2c3 100644
--- a/arch/powerpc/kernel/legacy_serial.c
+++ b/arch/powerpc/kernel/legacy_serial.c
@@ -44,12 +44,12 @@
 	int index;
 
 	/* get clock freq. if present */
-	clk = get_property(np, "clock-frequency", NULL);
+	clk = of_get_property(np, "clock-frequency", NULL);
 	if (clk && *clk)
 		clock = *clk;
 
 	/* get default speed if present */
-	spd = get_property(np, "current-speed", NULL);
+	spd = of_get_property(np, "current-speed", NULL);
 
 	/* If we have a location index, then try to use it */
 	if (want_index >= 0 && want_index < MAX_LEGACY_SERIAL_PORTS)
@@ -121,11 +121,11 @@
 	/* We only support ports that have a clock frequency properly
 	 * encoded in the device-tree.
 	 */
-	if (get_property(np, "clock-frequency", NULL) == NULL)
+	if (of_get_property(np, "clock-frequency", NULL) == NULL)
 		return -1;
 
 	/* if rtas uses this device, don't try to use it as well */
-	if (get_property(np, "used-by-rtas", NULL) != NULL)
+	if (of_get_property(np, "used-by-rtas", NULL) != NULL)
 		return -1;
 
 	/* Get the address */
@@ -157,7 +157,7 @@
 	DBG(" -> add_legacy_isa_port(%s)\n", np->full_name);
 
 	/* Get the ISA port number */
-	reg = get_property(np, "reg", NULL);
+	reg = of_get_property(np, "reg", NULL);
 	if (reg == NULL)
 		return -1;
 
@@ -168,7 +168,7 @@
 	/* Now look for an "ibm,aix-loc" property that gives us ordering
 	 * if any...
 	 */
-	typep = get_property(np, "ibm,aix-loc", NULL);
+	typep = of_get_property(np, "ibm,aix-loc", NULL);
 
 	/* If we have a location index, then use it */
 	if (typep && *typep == 'S')
@@ -206,7 +206,7 @@
 	 * compatible UARTs on PCI need all sort of quirks (port offsets
 	 * etc...) that this code doesn't know about
 	 */
-	if (get_property(np, "clock-frequency", NULL) == NULL)
+	if (of_get_property(np, "clock-frequency", NULL) == NULL)
 		return -1;
 
 	/* Get the PCI address. Assume BAR 0 */
@@ -232,7 +232,7 @@
 	 * we get to their "reg" property
 	 */
 	if (np != pci_dev) {
-		const u32 *reg = get_property(np, "reg", NULL);
+		const u32 *reg = of_get_property(np, "reg", NULL);
 		if (reg && (*reg < 4))
 			index = lindex = *reg;
 	}
@@ -296,7 +296,7 @@
 	DBG(" -> find_legacy_serial_port()\n");
 
 	/* Now find out if one of these is out firmware console */
-	path = get_property(of_chosen, "linux,stdout-path", NULL);
+	path = of_get_property(of_chosen, "linux,stdout-path", NULL);
 	if (path != NULL) {
 		stdout = of_find_node_by_path(path);
 		if (stdout)
@@ -529,7 +529,7 @@
 	}
 	/* We are getting a weird phandle from OF ... */
 	/* ... So use the full path instead */
-	name = get_property(of_chosen, "linux,stdout-path", NULL);
+	name = of_get_property(of_chosen, "linux,stdout-path", NULL);
 	if (name == NULL) {
 		DBG(" no linux,stdout-path !\n");
 		return -ENODEV;
@@ -541,12 +541,12 @@
 	}
 	DBG("stdout is %s\n", prom_stdout->full_name);
 
-	name = get_property(prom_stdout, "name", NULL);
+	name = of_get_property(prom_stdout, "name", NULL);
 	if (!name) {
 		DBG(" stdout package has no name !\n");
 		goto not_found;
 	}
-	spd = get_property(prom_stdout, "current-speed", NULL);
+	spd = of_get_property(prom_stdout, "current-speed", NULL);
 	if (spd)
 		speed = *spd;
 
diff --git a/arch/powerpc/kernel/lparcfg.c b/arch/powerpc/kernel/lparcfg.c
index 89486b6..c492cee 100644
--- a/arch/powerpc/kernel/lparcfg.c
+++ b/arch/powerpc/kernel/lparcfg.c
@@ -130,30 +130,31 @@
 /*
  * Methods used to fetch LPAR data when running on a pSeries platform.
  */
-/* find a better place for this function... */
 static void log_plpar_hcall_return(unsigned long rc, char *tag)
 {
-	if (rc == 0)		/* success, return */
+	switch(rc) {
+	case 0:
 		return;
-/* check for null tag ? */
-	if (rc == H_HARDWARE)
-		printk(KERN_INFO
-		       "plpar-hcall (%s) failed with hardware fault\n", tag);
-	else if (rc == H_FUNCTION)
-		printk(KERN_INFO
-		       "plpar-hcall (%s) failed; function not allowed\n", tag);
-	else if (rc == H_AUTHORITY)
-		printk(KERN_INFO
-		       "plpar-hcall (%s) failed; not authorized to this"
-		       " function\n", tag);
-	else if (rc == H_PARAMETER)
-		printk(KERN_INFO "plpar-hcall (%s) failed; Bad parameter(s)\n",
-		       tag);
-	else
-		printk(KERN_INFO
-		       "plpar-hcall (%s) failed with unexpected rc(0x%lx)\n",
-		       tag, rc);
-
+	case H_HARDWARE:
+		printk(KERN_INFO "plpar-hcall (%s) "
+				"Hardware fault\n", tag);
+		return;
+	case H_FUNCTION:
+		printk(KERN_INFO "plpar-hcall (%s) "
+				"Function not allowed\n", tag);
+		return;
+	case H_AUTHORITY:
+		printk(KERN_INFO "plpar-hcall (%s) "
+				"Not authorized to this function\n", tag);
+		return;
+	case H_PARAMETER:
+		printk(KERN_INFO "plpar-hcall (%s) "
+				"Bad parameter(s)\n",tag);
+		return;
+	default:
+		printk(KERN_INFO "plpar-hcall (%s) "
+				"Unexpected rc(0x%lx)\n", tag, rc);
+	}
 }
 
 /*
@@ -321,15 +322,16 @@
 	struct device_node *rtas_node;
 	const int *lrdrp = NULL;
 
-	rtas_node = find_path_device("/rtas");
+	rtas_node = of_find_node_by_path("/rtas");
 	if (rtas_node)
-		lrdrp = get_property(rtas_node, "ibm,lrdr-capacity", NULL);
+		lrdrp = of_get_property(rtas_node, "ibm,lrdr-capacity", NULL);
 
 	if (lrdrp == NULL) {
 		partition_potential_processors = vdso_data->processorCount;
 	} else {
 		partition_potential_processors = *(lrdrp + 4);
 	}
+	of_node_put(rtas_node);
 
 	partition_active_processors = lparcfg_count_active_processors();
 
@@ -537,25 +539,27 @@
 
 	seq_printf(m, "%s %s \n", MODULE_NAME, MODULE_VERS);
 
-	rootdn = find_path_device("/");
+	rootdn = of_find_node_by_path("/");
 	if (rootdn) {
-		tmp = get_property(rootdn, "model", NULL);
+		tmp = of_get_property(rootdn, "model", NULL);
 		if (tmp) {
 			model = tmp;
 			/* Skip "IBM," - see platforms/iseries/dt.c */
 			if (firmware_has_feature(FW_FEATURE_ISERIES))
 				model += 4;
 		}
-		tmp = get_property(rootdn, "system-id", NULL);
+		tmp = of_get_property(rootdn, "system-id", NULL);
 		if (tmp) {
 			system_id = tmp;
 			/* Skip "IBM," - see platforms/iseries/dt.c */
 			if (firmware_has_feature(FW_FEATURE_ISERIES))
 				system_id += 4;
 		}
-		lp_index_ptr = get_property(rootdn, "ibm,partition-no", NULL);
+		lp_index_ptr = of_get_property(rootdn, "ibm,partition-no",
+					NULL);
 		if (lp_index_ptr)
 			lp_index = *lp_index_ptr;
+		of_node_put(rootdn);
 	}
 	seq_printf(m, "serial_number=%s\n", system_id);
 	seq_printf(m, "system_type=%s\n", model);
diff --git a/arch/powerpc/kernel/machine_kexec_64.c b/arch/powerpc/kernel/machine_kexec_64.c
index a24b09c..704375b 100644
--- a/arch/powerpc/kernel/machine_kexec_64.c
+++ b/arch/powerpc/kernel/machine_kexec_64.c
@@ -72,8 +72,8 @@
 	/* We also should not overwrite the tce tables */
 	for (node = of_find_node_by_type(NULL, "pci"); node != NULL;
 			node = of_find_node_by_type(node, "pci")) {
-		basep = get_property(node, "linux,tce-base", NULL);
-		sizep = get_property(node, "linux,tce-size", NULL);
+		basep = of_get_property(node, "linux,tce-base", NULL);
+		sizep = of_get_property(node, "linux,tce-size", NULL);
 		if (basep == NULL || sizep == NULL)
 			continue;
 
@@ -294,19 +294,19 @@
 static struct property htab_base_prop = {
 	.name = "linux,htab-base",
 	.length = sizeof(unsigned long),
-	.value = (unsigned char *)&htab_base,
+	.value = &htab_base,
 };
 
 static struct property htab_size_prop = {
 	.name = "linux,htab-size",
 	.length = sizeof(unsigned long),
-	.value = (unsigned char *)&htab_size_bytes,
+	.value = &htab_size_bytes,
 };
 
 static struct property kernel_end_prop = {
 	.name = "linux,kernel-end",
 	.length = sizeof(unsigned long),
-	.value = (unsigned char *)&kernel_end,
+	.value = &kernel_end,
 };
 
 static void __init export_htab_values(void)
@@ -335,7 +335,7 @@
 static struct property crashk_base_prop = {
 	.name = "linux,crashkernel-base",
 	.length = sizeof(unsigned long),
-	.value = (unsigned char *)&crashk_res.start,
+	.value = &crashk_res.start,
 };
 
 static unsigned long crashk_size;
@@ -343,7 +343,7 @@
 static struct property crashk_size_prop = {
 	.name = "linux,crashkernel-size",
 	.length = sizeof(unsigned long),
-	.value = (unsigned char *)&crashk_size,
+	.value = &crashk_size,
 };
 
 static void __init export_crashk_values(void)
diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S
index 412bea3..98decf8 100644
--- a/arch/powerpc/kernel/misc_32.S
+++ b/arch/powerpc/kernel/misc_32.S
@@ -734,10 +734,6 @@
 	sub	r3,r3,r4
 	blr
 
-_GLOBAL(_get_SP)
-	mr	r3,r1		/* Close enough */
-	blr
-
 /*
  * Create a kernel thread
  *   kernel_thread(fn, arg, flags)
diff --git a/arch/powerpc/kernel/of_device.c b/arch/powerpc/kernel/of_device.c
index e921514..0c8ea76 100644
--- a/arch/powerpc/kernel/of_device.c
+++ b/arch/powerpc/kernel/of_device.c
@@ -120,6 +120,117 @@
 }
 
 
+static ssize_t of_device_get_modalias(struct of_device *ofdev,
+					char *str, ssize_t len)
+{
+	const char *compat;
+	int cplen, i;
+	ssize_t tsize, csize, repend;
+
+	/* Name & Type */
+	csize = snprintf(str, len, "of:N%sT%s",
+				ofdev->node->name, ofdev->node->type);
+
+	/* Get compatible property if any */
+	compat = of_get_property(ofdev->node, "compatible", &cplen);
+	if (!compat)
+		return csize;
+
+	/* Find true end (we tolerate multiple \0 at the end */
+	for (i=(cplen-1); i>=0 && !compat[i]; i--)
+		cplen--;
+	if (!cplen)
+		return csize;
+	cplen++;
+
+	/* Check space (need cplen+1 chars including final \0) */
+	tsize = csize + cplen;
+	repend = tsize;
+
+	if (csize>=len)		/* @ the limit, all is already filled */
+		return tsize;
+
+	if (tsize>=len) {		/* limit compat list */
+		cplen = len-csize-1;
+		repend = len;
+	}
+
+	/* Copy and do char replacement */
+	memcpy(&str[csize+1], compat, cplen);
+	for (i=csize; i<repend; i++) {
+		char c = str[i];
+		if (c=='\0')
+			str[i] = 'C';
+		else if (c==' ')
+			str[i] = '_';
+	}
+
+	return tsize;
+}
+
+int of_device_uevent(struct device *dev,
+		char **envp, int num_envp, char *buffer, int buffer_size)
+{
+	struct of_device *ofdev;
+	const char *compat;
+	int i = 0, length = 0, seen = 0, cplen, sl;
+
+	if (!dev)
+		return -ENODEV;
+
+	ofdev = to_of_device(dev);
+
+	if (add_uevent_var(envp, num_envp, &i,
+			   buffer, buffer_size, &length,
+			   "OF_NAME=%s", ofdev->node->name))
+		return -ENOMEM;
+
+	if (add_uevent_var(envp, num_envp, &i,
+			   buffer, buffer_size, &length,
+			   "OF_TYPE=%s", ofdev->node->type))
+		return -ENOMEM;
+
+        /* Since the compatible field can contain pretty much anything
+         * it's not really legal to split it out with commas. We split it
+         * up using a number of environment variables instead. */
+
+	compat = of_get_property(ofdev->node, "compatible", &cplen);
+	while (compat && *compat && cplen > 0) {
+		if (add_uevent_var(envp, num_envp, &i,
+				   buffer, buffer_size, &length,
+				   "OF_COMPATIBLE_%d=%s", seen, compat))
+			return -ENOMEM;
+
+		sl = strlen (compat) + 1;
+		compat += sl;
+		cplen -= sl;
+		seen++;
+	}
+
+	if (add_uevent_var(envp, num_envp, &i,
+			   buffer, buffer_size, &length,
+			   "OF_COMPATIBLE_N=%d", seen))
+		return -ENOMEM;
+
+	/* modalias is trickier, we add it in 2 steps */
+	if (add_uevent_var(envp, num_envp, &i,
+			   buffer, buffer_size, &length,
+			   "MODALIAS="))
+		return -ENOMEM;
+
+	sl = of_device_get_modalias(ofdev, &buffer[length-1],
+					buffer_size-length);
+	if (sl >= (buffer_size-length))
+		return -ENOMEM;
+
+	length += sl;
+
+	envp[i] = NULL;
+
+	return 0;
+}
+
+
 EXPORT_SYMBOL(of_match_node);
 EXPORT_SYMBOL(of_match_device);
 EXPORT_SYMBOL(of_device_register);
@@ -127,3 +238,4 @@
 EXPORT_SYMBOL(of_dev_get);
 EXPORT_SYMBOL(of_dev_put);
 EXPORT_SYMBOL(of_release_dev);
+EXPORT_SYMBOL(of_device_uevent);
diff --git a/arch/powerpc/kernel/of_platform.c b/arch/powerpc/kernel/of_platform.c
index b734517..908ed79 100644
--- a/arch/powerpc/kernel/of_platform.c
+++ b/arch/powerpc/kernel/of_platform.c
@@ -133,6 +133,7 @@
 struct bus_type of_platform_bus_type = {
        .name	= "of_platform",
        .match	= of_platform_bus_match,
+       .uevent	= of_device_uevent,
        .probe	= of_platform_device_probe,
        .remove	= of_platform_device_remove,
        .suspend	= of_platform_device_suspend,
@@ -177,7 +178,7 @@
 	 * and 'D' for MMIO DCRs.
 	 */
 #ifdef CONFIG_PPC_DCR
-	reg = get_property(node, "dcr-reg", NULL);
+	reg = of_get_property(node, "dcr-reg", NULL);
 	if (reg) {
 #ifdef CONFIG_PPC_DCR_NATIVE
 		snprintf(name, BUS_ID_SIZE, "d%x.%s",
@@ -197,7 +198,7 @@
 	/*
 	 * For MMIO, get the physical address
 	 */
-	reg = get_property(node, "reg", NULL);
+	reg = of_get_property(node, "reg", NULL);
 	if (reg) {
 		addr = of_translate_address(node, reg);
 		if (addr != OF_BAD_ADDR) {
@@ -475,9 +476,6 @@
        .name = "of-pci",
        .match_table = of_pci_phb_ids,
        .probe = of_pci_phb_probe,
-       .driver = {
-	       .multithread_probe = 1,
-       },
 };
 
 static __init int of_pci_phb_init(void)
diff --git a/arch/powerpc/kernel/pci_32.c b/arch/powerpc/kernel/pci_32.c
index d8ef2e1..f022862 100644
--- a/arch/powerpc/kernel/pci_32.c
+++ b/arch/powerpc/kernel/pci_32.c
@@ -637,7 +637,7 @@
 
 	if (pci_bus >= pci_bus_count)
 		return;
-	bus_range = get_property(node, "bus-range", &len);
+	bus_range = of_get_property(node, "bus-range", &len);
 	if (bus_range == NULL || len < 2 * sizeof(int)) {
 		printk(KERN_WARNING "Can't get bus-range for %s, "
 		       "assuming it starts at 0\n", node->full_name);
@@ -649,17 +649,20 @@
 		struct pci_dev* dev;
 		const unsigned int *class_code, *reg;
 	
-		class_code = get_property(node, "class-code", NULL);
+		class_code = of_get_property(node, "class-code", NULL);
 		if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI &&
 			(*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS))
 			continue;
-		reg = get_property(node, "reg", NULL);
+		reg = of_get_property(node, "reg", NULL);
 		if (!reg)
 			continue;
-		dev = pci_find_slot(pci_bus, ((reg[0] >> 8) & 0xff));
-		if (!dev || !dev->subordinate)
+		dev = pci_get_bus_and_slot(pci_bus, ((reg[0] >> 8) & 0xff));
+		if (!dev || !dev->subordinate) {
+			pci_dev_put(dev);
 			continue;
+		}
 		make_one_node_map(node, dev->subordinate->number);
+		pci_dev_put(dev);
 	}
 }
 	
@@ -669,6 +672,7 @@
 	int i;
 	struct pci_controller* hose;
 	struct property *map_prop;
+	struct device_node *dn;
 
 	pci_to_OF_bus_map = kmalloc(pci_bus_count, GFP_KERNEL);
 	if (!pci_to_OF_bus_map) {
@@ -690,12 +694,13 @@
 			continue;
 		make_one_node_map(node, hose->first_busno);
 	}
-	map_prop = of_find_property(find_path_device("/"),
-			"pci-OF-bus-map", NULL);
+	dn = of_find_node_by_path("/");
+	map_prop = of_find_property(dn, "pci-OF-bus-map", NULL);
 	if (map_prop) {
 		BUG_ON(pci_bus_count > map_prop->length);
 		memcpy(map_prop->value, pci_to_OF_bus_map, pci_bus_count);
 	}
+	of_node_put(dn);
 #ifdef DEBUG
 	printk("PCI->OF bus map:\n");
 	for (i=0; i<pci_bus_count; i++) {
@@ -724,7 +729,7 @@
 		 * a fake root for all functions of a multi-function device,
 		 * we go down them as well.
 		 */
-		class_code = get_property(node, "class-code", NULL);
+		class_code = of_get_property(node, "class-code", NULL);
 		if ((!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI &&
 			(*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) &&
 			strcmp(node->name, "multifunc-device"))
@@ -744,7 +749,7 @@
 	unsigned int psize;
 
 	while ((np = of_get_next_child(parent, np)) != NULL) {
-		reg = get_property(np, "reg", &psize);
+		reg = of_get_property(np, "reg", &psize);
 		if (reg == NULL || psize < 4)
 			continue;
 		if (((reg[0] >> 8) & 0xff) == devfn)
@@ -859,7 +864,7 @@
 	if (!scan_OF_pci_childs(((struct device_node*)hose->arch_data)->child,
 			find_OF_pci_device_filter, (void *)node))
 		return -ENODEV;
-	reg = get_property(node, "reg", NULL);
+	reg = of_get_property(node, "reg", NULL);
 	if (!reg)
 		return -ENODEV;
 	*bus = (reg[0] >> 16) & 0xff;
@@ -895,14 +900,14 @@
 	int rlen = 0, orig_rlen;
 	int memno = 0;
 	struct resource *res;
-	int np, na = prom_n_addr_cells(dev);
+	int np, na = of_n_addr_cells(dev);
 	np = na + 5;
 
 	/* First we try to merge ranges to fix a problem with some pmacs
 	 * that can have more than 3 ranges, fortunately using contiguous
 	 * addresses -- BenH
 	 */
-	dt_ranges = get_property(dev, "ranges", &rlen);
+	dt_ranges = of_get_property(dev, "ranges", &rlen);
 	if (!dt_ranges)
 		return;
 	/* Sanity check, though hopefully that never happens */
@@ -1006,14 +1011,19 @@
 pci_create_OF_bus_map(void)
 {
 	struct property* of_prop;
-	
+	struct device_node *dn;
+
 	of_prop = (struct property*) alloc_bootmem(sizeof(struct property) + 256);
-	if (of_prop && find_path_device("/")) {
+	if (!of_prop)
+		return;
+	dn = of_find_node_by_path("/");
+	if (dn) {
 		memset(of_prop, -1, sizeof(struct property) + 256);
 		of_prop->name = "pci-OF-bus-map";
 		of_prop->length = 256;
-		of_prop->value = (unsigned char *)&of_prop[1];
-		prom_add_property(find_path_device("/"), of_prop);
+		of_prop->value = &of_prop[1];
+		prom_add_property(dn, of_prop);
+		of_node_put(dn);
 	}
 }
 
diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c
index 7e97d71..60d7d4b 100644
--- a/arch/powerpc/kernel/pci_64.c
+++ b/arch/powerpc/kernel/pci_64.c
@@ -61,8 +61,7 @@
 
 LIST_HEAD(hose_list);
 
-struct dma_mapping_ops *pci_dma_ops;
-EXPORT_SYMBOL(pci_dma_ops);
+static struct dma_mapping_ops *pci_dma_ops;
 
 int global_phb_number;		/* Global phb counter */
 
@@ -70,6 +69,17 @@
 struct pci_dev *ppc64_isabridge_dev = NULL;
 EXPORT_SYMBOL_GPL(ppc64_isabridge_dev);
 
+void set_pci_dma_ops(struct dma_mapping_ops *dma_ops)
+{
+	pci_dma_ops = dma_ops;
+}
+
+struct dma_mapping_ops *get_pci_dma_ops(void)
+{
+	return pci_dma_ops;
+}
+EXPORT_SYMBOL(get_pci_dma_ops);
+
 static void fixup_broken_pcnet32(struct pci_dev* dev)
 {
 	if ((dev->class>>8 == PCI_CLASS_NETWORK_ETHERNET)) {
@@ -258,7 +268,7 @@
 	const u32 *prop;
 	int len;
 
-	prop = get_property(np, name, &len);
+	prop = of_get_property(np, name, &len);
 	if (prop && len >= 4)
 		return *prop;
 	return def;
@@ -291,7 +301,7 @@
 	u32 i;
 	int proplen;
 
-	addrs = get_property(node, "assigned-addresses", &proplen);
+	addrs = of_get_property(node, "assigned-addresses", &proplen);
 	if (!addrs)
 		return;
 	DBG("    parse addresses (%d bytes) @ %p\n", proplen, addrs);
@@ -333,7 +343,7 @@
 	dev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL);
 	if (!dev)
 		return NULL;
-	type = get_property(node, "device_type", NULL);
+	type = of_get_property(node, "device_type", NULL);
 	if (type == NULL)
 		type = "";
 
@@ -397,7 +407,7 @@
 
 	while ((child = of_get_next_child(node, child)) != NULL) {
 		DBG("  * %s\n", child->full_name);
-		reg = get_property(child, "reg", &reglen);
+		reg = of_get_property(child, "reg", &reglen);
 		if (reg == NULL || reglen < 20)
 			continue;
 		devfn = (reg[0] >> 8) & 0xff;
@@ -430,13 +440,13 @@
 	DBG("of_scan_pci_bridge(%s)\n", node->full_name);
 
 	/* parse bus-range property */
-	busrange = get_property(node, "bus-range", &len);
+	busrange = of_get_property(node, "bus-range", &len);
 	if (busrange == NULL || len != 8) {
 		printk(KERN_DEBUG "Can't get bus-range for PCI-PCI bridge %s\n",
 		       node->full_name);
 		return;
 	}
-	ranges = get_property(node, "ranges", &len);
+	ranges = of_get_property(node, "ranges", &len);
 	if (ranges == NULL) {
 		printk(KERN_DEBUG "Can't get ranges for PCI-PCI bridge %s\n",
 		       node->full_name);
@@ -900,7 +910,7 @@
 	unsigned int size;
 	int rlen = 0;
 
-	range = get_property(isa_node, "ranges", &rlen);
+	range = of_get_property(isa_node, "ranges", &rlen);
 	if (range == NULL || (rlen < sizeof(struct isa_range))) {
 		printk(KERN_ERR "no ISA ranges or unexpected isa range size,"
 		       "mapping 64k\n");
@@ -947,7 +957,7 @@
 	int rlen = 0;
 	int memno = 0;
 	struct resource *res;
-	int np, na = prom_n_addr_cells(dev);
+	int np, na = of_n_addr_cells(dev);
 	unsigned long pci_addr, cpu_phys_addr;
 
 	np = na + 5;
@@ -960,7 +970,7 @@
 	 *			(size depending on dev->n_addr_cells)
 	 *   cells 4+5 or 5+6:	the size of the range
 	 */
-	ranges = get_property(dev, "ranges", &rlen);
+	ranges = of_get_property(dev, "ranges", &rlen);
 	if (ranges == NULL)
 		return;
 	hose->io_base_phys = 0;
diff --git a/arch/powerpc/kernel/pci_dn.c b/arch/powerpc/kernel/pci_dn.c
index 68df018..d7d36df 100644
--- a/arch/powerpc/kernel/pci_dn.c
+++ b/arch/powerpc/kernel/pci_dn.c
@@ -40,7 +40,8 @@
 static void * __devinit update_dn_pci_info(struct device_node *dn, void *data)
 {
 	struct pci_controller *phb = data;
-	const int *type = get_property(dn, "ibm,pci-config-space-type", NULL);
+	const int *type =
+		of_get_property(dn, "ibm,pci-config-space-type", NULL);
 	const u32 *regs;
 	struct pci_dn *pdn;
 
@@ -54,14 +55,14 @@
 	dn->data = pdn;
 	pdn->node = dn;
 	pdn->phb = phb;
-	regs = get_property(dn, "reg", NULL);
+	regs = of_get_property(dn, "reg", NULL);
 	if (regs) {
 		/* First register entry is addr (00BBSS00)  */
 		pdn->busno = (regs[0] >> 16) & 0xff;
 		pdn->devfn = (regs[0] >> 8) & 0xff;
 	}
 	if (firmware_has_feature(FW_FEATURE_ISERIES)) {
-		const u32 *busp = get_property(dn, "linux,subbus", NULL);
+		const u32 *busp = of_get_property(dn, "linux,subbus", NULL);
 		if (busp)
 			pdn->bussubno = *busp;
 	}
@@ -100,7 +101,7 @@
 		u32 class;
 
 		nextdn = NULL;
-		classp = get_property(dn, "class-code", NULL);
+		classp = of_get_property(dn, "class-code", NULL);
 		class = classp ? *classp : 0;
 
 		if (pre && ((ret = pre(dn, data)) != NULL))
diff --git a/arch/powerpc/kernel/ppc_ksyms.c b/arch/powerpc/kernel/ppc_ksyms.c
index ecee596..ff252aa 100644
--- a/arch/powerpc/kernel/ppc_ksyms.c
+++ b/arch/powerpc/kernel/ppc_ksyms.c
@@ -20,7 +20,6 @@
 #include <asm/processor.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
-#include <asm/ide.h>
 #include <asm/atomic.h>
 #include <asm/checksum.h>
 #include <asm/pgtable.h>
@@ -84,8 +83,6 @@
 EXPORT_SYMBOL(strcat);
 EXPORT_SYMBOL(strlen);
 EXPORT_SYMBOL(strcmp);
-EXPORT_SYMBOL(strcasecmp);
-EXPORT_SYMBOL(strncasecmp);
 
 EXPORT_SYMBOL(csum_partial);
 EXPORT_SYMBOL(csum_partial_copy_generic);
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index e53b298..e509aae 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -305,9 +305,7 @@
 		set_dabr(new->thread.dabr);
 		__get_cpu_var(current_dabr) = new->thread.dabr;
 	}
-
-	flush_tlb_pending();
-#endif
+#endif /* CONFIG_PPC64 */
 
 	new_thread = &new->thread;
 	old_thread = &current->thread;
@@ -402,11 +400,11 @@
 }
 
 #ifdef CONFIG_PPC64
-#define REG		"%016lX"
+#define REG		"%016lx"
 #define REGS_PER_LINE	4
 #define LAST_VOLATILE	13
 #else
-#define REG		"%08lX"
+#define REG		"%08lx"
 #define REGS_PER_LINE	8
 #define LAST_VOLATILE	12
 #endif
@@ -421,7 +419,7 @@
 	       regs, regs->trap, print_tainted(), init_utsname()->release);
 	printk("MSR: "REG" ", regs->msr);
 	printbits(regs->msr, msr_bits);
-	printk("  CR: %08lX  XER: %08lX\n", regs->ccr, regs->xer);
+	printk("  CR: %08lx  XER: %08lx\n", regs->ccr, regs->xer);
 	trap = TRAP(regs);
 	if (trap == 0x300 || trap == 0x600)
 		printk("DAR: "REG", DSISR: "REG"\n", regs->dar, regs->dsisr);
@@ -572,7 +570,6 @@
 	kregs->nip = *((unsigned long *)ret_from_fork);
 #else
 	kregs->nip = (unsigned long)ret_from_fork;
-	p->thread.last_syscall = -1;
 #endif
 
 	return 0;
@@ -823,6 +820,35 @@
 	return error;
 }
 
+#ifdef CONFIG_IRQSTACKS
+static inline int valid_irq_stack(unsigned long sp, struct task_struct *p,
+				  unsigned long nbytes)
+{
+	unsigned long stack_page;
+	unsigned long cpu = task_cpu(p);
+
+	/*
+	 * Avoid crashing if the stack has overflowed and corrupted
+	 * task_cpu(p), which is in the thread_info struct.
+	 */
+	if (cpu < NR_CPUS && cpu_possible(cpu)) {
+		stack_page = (unsigned long) hardirq_ctx[cpu];
+		if (sp >= stack_page + sizeof(struct thread_struct)
+		    && sp <= stack_page + THREAD_SIZE - nbytes)
+			return 1;
+
+		stack_page = (unsigned long) softirq_ctx[cpu];
+		if (sp >= stack_page + sizeof(struct thread_struct)
+		    && sp <= stack_page + THREAD_SIZE - nbytes)
+			return 1;
+	}
+	return 0;
+}
+
+#else
+#define valid_irq_stack(sp, p, nb)	0
+#endif /* CONFIG_IRQSTACKS */
+
 int validate_sp(unsigned long sp, struct task_struct *p,
 		       unsigned long nbytes)
 {
@@ -832,19 +858,7 @@
 	    && sp <= stack_page + THREAD_SIZE - nbytes)
 		return 1;
 
-#ifdef CONFIG_IRQSTACKS
-	stack_page = (unsigned long) hardirq_ctx[task_cpu(p)];
-	if (sp >= stack_page + sizeof(struct thread_struct)
-	    && sp <= stack_page + THREAD_SIZE - nbytes)
-		return 1;
-
-	stack_page = (unsigned long) softirq_ctx[task_cpu(p)];
-	if (sp >= stack_page + sizeof(struct thread_struct)
-	    && sp <= stack_page + THREAD_SIZE - nbytes)
-		return 1;
-#endif
-
-	return 0;
+	return valid_irq_stack(sp, p, nbytes);
 }
 
 #ifdef CONFIG_PPC64
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index 8d52b23..caef555 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -390,18 +390,19 @@
 		if (allnextpp) {
 			pp->name = "name";
 			pp->length = sz;
-			pp->value = (unsigned char *)(pp + 1);
+			pp->value = pp + 1;
 			*prev_pp = pp;
 			prev_pp = &pp->next;
 			memcpy(pp->value, ps, sz - 1);
 			((char *)pp->value)[sz - 1] = 0;
-			DBG("fixed up name for %s -> %s\n", pathp, pp->value);
+			DBG("fixed up name for %s -> %s\n", pathp,
+				(char *)pp->value);
 		}
 	}
 	if (allnextpp) {
 		*prev_pp = NULL;
-		np->name = get_property(np, "name", NULL);
-		np->type = get_property(np, "device_type", NULL);
+		np->name = of_get_property(np, "name", NULL);
+		np->type = of_get_property(np, "device_type", NULL);
 
 		if (!np->name)
 			np->name = "<NULL>";
@@ -719,6 +720,7 @@
 					    const char *uname, int depth, void *data)
 {
 	unsigned long *lprop;
+	u32 *prop;
 	unsigned long l;
 	char *p;
 
@@ -760,6 +762,22 @@
                crashk_res.end = crashk_res.start + *lprop - 1;
 #endif
 
+#ifdef CONFIG_BLK_DEV_INITRD
+	DBG("Looking for initrd properties... ");
+	prop = of_get_flat_dt_prop(node, "linux,initrd-start", &l);
+	if (prop) {
+		initrd_start = (unsigned long)__va(of_read_ulong(prop, l/4));
+		prop = of_get_flat_dt_prop(node, "linux,initrd-end", &l);
+		if (prop) {
+			initrd_end = (unsigned long)__va(of_read_ulong(prop, l/4));
+			initrd_below_start_ok = 1;
+		} else {
+			initrd_start = 0;
+		}
+	}
+	DBG("initrd_start=0x%lx  initrd_end=0x%lx\n", initrd_start, initrd_end);
+#endif /* CONFIG_BLK_DEV_INITRD */
+
 	/* Retreive command line */
  	p = of_get_flat_dt_prop(node, "bootargs", &l);
 	if (p != NULL && l > 0)
@@ -926,6 +944,12 @@
 	self_size = initial_boot_params->totalsize;
 	lmb_reserve(self_base, self_size);
 
+#ifdef CONFIG_BLK_DEV_INITRD
+	/* then reserve the initrd, if any */
+	if (initrd_start && (initrd_end > initrd_start))
+		lmb_reserve(__pa(initrd_start), initrd_end - initrd_start);
+#endif /* CONFIG_BLK_DEV_INITRD */
+
 #ifdef CONFIG_PPC32
 	/* 
 	 * Handle the case where we might be booting from an old kexec
@@ -954,9 +978,6 @@
 		size = *(reserve_map++);
 		if (size == 0)
 			break;
-		/* skip if the reservation is for the blob */
-		if (base == self_base && size == self_size)
-			continue;
 		DBG("reserving: %llx -> %llx\n", base, size);
 		lmb_reserve(base, size);
 	}
@@ -1021,102 +1042,46 @@
 
 #undef printk
 
-int
-prom_n_addr_cells(struct device_node* np)
+int of_n_addr_cells(struct device_node* np)
 {
 	const int *ip;
 	do {
 		if (np->parent)
 			np = np->parent;
-		ip = get_property(np, "#address-cells", NULL);
+		ip = of_get_property(np, "#address-cells", NULL);
 		if (ip != NULL)
 			return *ip;
 	} while (np->parent);
 	/* No #address-cells property for the root node, default to 1 */
 	return 1;
 }
-EXPORT_SYMBOL(prom_n_addr_cells);
+EXPORT_SYMBOL(of_n_addr_cells);
 
-int
-prom_n_size_cells(struct device_node* np)
+int of_n_size_cells(struct device_node* np)
 {
 	const int* ip;
 	do {
 		if (np->parent)
 			np = np->parent;
-		ip = get_property(np, "#size-cells", NULL);
+		ip = of_get_property(np, "#size-cells", NULL);
 		if (ip != NULL)
 			return *ip;
 	} while (np->parent);
 	/* No #size-cells property for the root node, default to 1 */
 	return 1;
 }
-EXPORT_SYMBOL(prom_n_size_cells);
-
-/**
- * Construct and return a list of the device_nodes with a given name.
- */
-struct device_node *find_devices(const char *name)
-{
-	struct device_node *head, **prevp, *np;
-
-	prevp = &head;
-	for (np = allnodes; np != 0; np = np->allnext) {
-		if (np->name != 0 && strcasecmp(np->name, name) == 0) {
-			*prevp = np;
-			prevp = &np->next;
-		}
-	}
-	*prevp = NULL;
-	return head;
-}
-EXPORT_SYMBOL(find_devices);
-
-/**
- * Construct and return a list of the device_nodes with a given type.
- */
-struct device_node *find_type_devices(const char *type)
-{
-	struct device_node *head, **prevp, *np;
-
-	prevp = &head;
-	for (np = allnodes; np != 0; np = np->allnext) {
-		if (np->type != 0 && strcasecmp(np->type, type) == 0) {
-			*prevp = np;
-			prevp = &np->next;
-		}
-	}
-	*prevp = NULL;
-	return head;
-}
-EXPORT_SYMBOL(find_type_devices);
-
-/**
- * Returns all nodes linked together
- */
-struct device_node *find_all_nodes(void)
-{
-	struct device_node *head, **prevp, *np;
-
-	prevp = &head;
-	for (np = allnodes; np != 0; np = np->allnext) {
-		*prevp = np;
-		prevp = &np->next;
-	}
-	*prevp = NULL;
-	return head;
-}
-EXPORT_SYMBOL(find_all_nodes);
+EXPORT_SYMBOL(of_n_size_cells);
 
 /** Checks if the given "compat" string matches one of the strings in
  * the device's "compatible" property
  */
-int device_is_compatible(const struct device_node *device, const char *compat)
+int of_device_is_compatible(const struct device_node *device,
+		const char *compat)
 {
 	const char* cp;
 	int cplen, l;
 
-	cp = get_property(device, "compatible", &cplen);
+	cp = of_get_property(device, "compatible", &cplen);
 	if (cp == NULL)
 		return 0;
 	while (cplen > 0) {
@@ -1129,7 +1094,7 @@
 
 	return 0;
 }
-EXPORT_SYMBOL(device_is_compatible);
+EXPORT_SYMBOL(of_device_is_compatible);
 
 
 /**
@@ -1143,51 +1108,13 @@
 
 	root = of_find_node_by_path("/");
 	if (root) {
-		rc = device_is_compatible(root, compat);
+		rc = of_device_is_compatible(root, compat);
 		of_node_put(root);
 	}
 	return rc;
 }
 EXPORT_SYMBOL(machine_is_compatible);
 
-/**
- * Construct and return a list of the device_nodes with a given type
- * and compatible property.
- */
-struct device_node *find_compatible_devices(const char *type,
-					    const char *compat)
-{
-	struct device_node *head, **prevp, *np;
-
-	prevp = &head;
-	for (np = allnodes; np != 0; np = np->allnext) {
-		if (type != NULL
-		    && !(np->type != 0 && strcasecmp(np->type, type) == 0))
-			continue;
-		if (device_is_compatible(np, compat)) {
-			*prevp = np;
-			prevp = &np->next;
-		}
-	}
-	*prevp = NULL;
-	return head;
-}
-EXPORT_SYMBOL(find_compatible_devices);
-
-/**
- * Find the device_node with a given full_name.
- */
-struct device_node *find_path_device(const char *path)
-{
-	struct device_node *np;
-
-	for (np = allnodes; np != 0; np = np->allnext)
-		if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0)
-			return np;
-	return NULL;
-}
-EXPORT_SYMBOL(find_path_device);
-
 /*******
  *
  * New implementation of the OF "find" APIs, return a refcounted
@@ -1280,7 +1207,7 @@
 		if (type != NULL
 		    && !(np->type != 0 && strcasecmp(np->type, type) == 0))
 			continue;
-		if (device_is_compatible(np, compatible) && of_node_get(np))
+		if (of_device_is_compatible(np, compatible) && of_node_get(np))
 			break;
 	}
 	of_node_put(from);
@@ -1527,8 +1454,8 @@
 	int err = 0;
 	const phandle *ibm_phandle;
 
-	node->name = get_property(node, "name", NULL);
-	node->type = get_property(node, "device_type", NULL);
+	node->name = of_get_property(node, "name", NULL);
+	node->type = of_get_property(node, "device_type", NULL);
 
 	if (!parent) {
 		err = -ENODEV;
@@ -1542,7 +1469,7 @@
 		return -ENODEV;
 
 	/* fix up new node's linux_phandle field */
-	if ((ibm_phandle = get_property(node, "ibm,phandle", NULL)))
+	if ((ibm_phandle = of_get_property(node, "ibm,phandle", NULL)))
 		node->linux_phandle = *ibm_phandle;
 
 out:
@@ -1605,13 +1532,13 @@
  * Find a property with a given name for a given node
  * and return the value.
  */
-const void *get_property(const struct device_node *np, const char *name,
+const void *of_get_property(const struct device_node *np, const char *name,
 			 int *lenp)
 {
 	struct property *pp = of_find_property(np,name,lenp);
 	return pp ? pp->value : NULL;
 }
-EXPORT_SYMBOL(get_property);
+EXPORT_SYMBOL(of_get_property);
 
 /*
  * Add a property to a node
@@ -1742,10 +1669,10 @@
 		/* Check for ibm,ppc-interrupt-server#s. If it doesn't exist
 		 * fallback to "reg" property and assume no threads
 		 */
-		intserv = get_property(np, "ibm,ppc-interrupt-server#s",
+		intserv = of_get_property(np, "ibm,ppc-interrupt-server#s",
 				&plen);
 		if (intserv == NULL) {
-			const u32 *reg = get_property(np, "reg", NULL);
+			const u32 *reg = of_get_property(np, "reg", NULL);
 			if (reg == NULL)
 				continue;
 			if (*reg == hardid) {
diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c
index 4fb5938..e27d9d1 100644
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -2035,39 +2035,50 @@
 #endif
 
 #ifdef CONFIG_PPC_CHRP
-/* Pegasos and BriQ lacks the "ranges" property in the isa node */
+/*
+ * Pegasos and BriQ lacks the "ranges" property in the isa node
+ * Pegasos needs decimal IRQ 14/15, not hexadecimal
+ */
 static void __init fixup_device_tree_chrp(void)
 {
-	phandle isa;
-	u32 isa_ranges[6];
+	phandle ph;
+	u32 prop[6];
 	u32 rloc = 0x01006000; /* IO space; PCI device = 12 */
 	char *name;
 	int rc;
 
 	name = "/pci@80000000/isa@c";
-	isa = call_prom("finddevice", 1, 1, ADDR(name));
-	if (!PHANDLE_VALID(isa)) {
+	ph = call_prom("finddevice", 1, 1, ADDR(name));
+	if (!PHANDLE_VALID(ph)) {
 		name = "/pci@ff500000/isa@6";
-		isa = call_prom("finddevice", 1, 1, ADDR(name));
+		ph = call_prom("finddevice", 1, 1, ADDR(name));
 		rloc = 0x01003000; /* IO space; PCI device = 6 */
 	}
-	if (!PHANDLE_VALID(isa))
-		return;
+	if (PHANDLE_VALID(ph)) {
+		rc = prom_getproplen(ph, "ranges");
+		if (rc == 0 || rc == PROM_ERROR) {
+			prom_printf("Fixing up missing ISA range on Pegasos...\n");
 
-	rc = prom_getproplen(isa, "ranges");
-	if (rc != 0 && rc != PROM_ERROR)
-		return;
+			prop[0] = 0x1;
+			prop[1] = 0x0;
+			prop[2] = rloc;
+			prop[3] = 0x0;
+			prop[4] = 0x0;
+			prop[5] = 0x00010000;
+			prom_setprop(ph, name, "ranges", prop, sizeof(prop));
+		}
+	}
 
-	prom_printf("Fixing up missing ISA range on Pegasos...\n");
-
-	isa_ranges[0] = 0x1;
-	isa_ranges[1] = 0x0;
-	isa_ranges[2] = rloc;
-	isa_ranges[3] = 0x0;
-	isa_ranges[4] = 0x0;
-	isa_ranges[5] = 0x00010000;
-	prom_setprop(isa, name, "ranges",
-			isa_ranges, sizeof(isa_ranges));
+	name = "/pci@80000000/ide@C,1";
+	ph = call_prom("finddevice", 1, 1, ADDR(name));
+	if (PHANDLE_VALID(ph)) {
+		prom_printf("Fixing up IDE interrupt on Pegasos...\n");
+		prop[0] = 14;
+		prop[1] = 0x0;
+		prop[2] = 15;
+		prop[3] = 0x0;
+		prom_setprop(ph, name, "interrupts", prop, 4*sizeof(u32));
+	}
 }
 #else
 #define fixup_device_tree_chrp()
diff --git a/arch/powerpc/kernel/prom_parse.c b/arch/powerpc/kernel/prom_parse.c
index 91b443c..aa40a53 100644
--- a/arch/powerpc/kernel/prom_parse.c
+++ b/arch/powerpc/kernel/prom_parse.c
@@ -68,9 +68,9 @@
 				       int *addrc, int *sizec)
 {
 	if (addrc)
-		*addrc = prom_n_addr_cells(dev);
+		*addrc = of_n_addr_cells(dev);
 	if (sizec)
-		*sizec = prom_n_size_cells(dev);
+		*sizec = of_n_size_cells(dev);
 }
 
 static u64 of_bus_default_map(u32 *addr, const u32 *range,
@@ -196,7 +196,7 @@
 		return NULL;
 
 	/* Get "reg" or "assigned-addresses" property */
-	prop = get_property(dev, bus->addresses, &psize);
+	prop = of_get_property(dev, bus->addresses, &psize);
 	if (prop == NULL)
 		return NULL;
 	psize /= 4;
@@ -438,7 +438,7 @@
 	 * to translate addresses that aren't supposed to be translated in
 	 * the first place. --BenH.
 	 */
-	ranges = get_property(parent, "ranges", &rlen);
+	ranges = of_get_property(parent, "ranges", &rlen);
 	if (ranges == NULL || rlen == 0) {
 		offset = of_read_number(addr, na);
 		memset(addr, 0, pna * 4);
@@ -578,7 +578,7 @@
 		return NULL;
 
 	/* Get "reg" or "assigned-addresses" property */
-	prop = get_property(dev, bus->addresses, &psize);
+	prop = of_get_property(dev, bus->addresses, &psize);
 	if (prop == NULL)
 		return NULL;
 	psize /= 4;
@@ -650,17 +650,17 @@
 	/* busno is always one cell */
 	*busno = *(dma_window++);
 
-	prop = get_property(dn, "ibm,#dma-address-cells", NULL);
+	prop = of_get_property(dn, "ibm,#dma-address-cells", NULL);
 	if (!prop)
-		prop = get_property(dn, "#address-cells", NULL);
+		prop = of_get_property(dn, "#address-cells", NULL);
 
-	cells = prop ? *(u32 *)prop : prom_n_addr_cells(dn);
+	cells = prop ? *(u32 *)prop : of_n_addr_cells(dn);
 	*phys = of_read_number(dma_window, cells);
 
 	dma_window += cells;
 
-	prop = get_property(dn, "ibm,#dma-size-cells", NULL);
-	cells = prop ? *(u32 *)prop : prom_n_size_cells(dn);
+	prop = of_get_property(dn, "ibm,#dma-size-cells", NULL);
+	cells = prop ? *(u32 *)prop : of_n_size_cells(dn);
 	*size = of_read_number(dma_window, cells);
 }
 
@@ -680,7 +680,7 @@
 		return NULL;
 
 	do {
-		parp = get_property(child, "interrupt-parent", NULL);
+		parp = of_get_property(child, "interrupt-parent", NULL);
 		if (parp == NULL)
 			p = of_get_parent(child);
 		else {
@@ -691,7 +691,7 @@
 		}
 		of_node_put(child);
 		child = p;
-	} while (p && get_property(p, "#interrupt-cells", NULL) == NULL);
+	} while (p && of_get_property(p, "#interrupt-cells", NULL) == NULL);
 
 	return p;
 }
@@ -716,7 +716,7 @@
 		struct device_node *np;
 
 		for(np = NULL; (np = of_find_all_nodes(np)) != NULL;) {
-			if (get_property(np, "interrupt-controller", NULL)
+			if (of_get_property(np, "interrupt-controller", NULL)
 			    == NULL)
 				continue;
 			/* Skip /chosen/interrupt-controller */
@@ -755,7 +755,7 @@
 	 * is none, we are nice and just walk up the tree
 	 */
 	do {
-		tmp = get_property(ipar, "#interrupt-cells", NULL);
+		tmp = of_get_property(ipar, "#interrupt-cells", NULL);
 		if (tmp != NULL) {
 			intsize = *tmp;
 			break;
@@ -779,7 +779,7 @@
 	 */
 	old = of_node_get(ipar);
 	do {
-		tmp = get_property(old, "#address-cells", NULL);
+		tmp = of_get_property(old, "#address-cells", NULL);
 		tnode = of_get_parent(old);
 		of_node_put(old);
 		old = tnode;
@@ -795,7 +795,8 @@
 		/* Now check if cursor is an interrupt-controller and if it is
 		 * then we are done
 		 */
-		if (get_property(ipar, "interrupt-controller", NULL) != NULL) {
+		if (of_get_property(ipar, "interrupt-controller", NULL) !=
+				NULL) {
 			DBG(" -> got it !\n");
 			memcpy(out_irq->specifier, intspec,
 			       intsize * sizeof(u32));
@@ -806,7 +807,7 @@
 		}
 
 		/* Now look for an interrupt-map */
-		imap = get_property(ipar, "interrupt-map", &imaplen);
+		imap = of_get_property(ipar, "interrupt-map", &imaplen);
 		/* No interrupt map, check for an interrupt parent */
 		if (imap == NULL) {
 			DBG(" -> no map, getting parent\n");
@@ -816,7 +817,7 @@
 		imaplen /= sizeof(u32);
 
 		/* Look for a mask */
-		imask = get_property(ipar, "interrupt-map-mask", NULL);
+		imask = of_get_property(ipar, "interrupt-map-mask", NULL);
 
 		/* If we were passed no "reg" property and we attempt to parse
 		 * an interrupt-map, then #address-cells must be 0.
@@ -863,15 +864,13 @@
 			/* Get #interrupt-cells and #address-cells of new
 			 * parent
 			 */
-			tmp = get_property(newpar, "#interrupt-cells",
-						  NULL);
+			tmp = of_get_property(newpar, "#interrupt-cells", NULL);
 			if (tmp == NULL) {
 				DBG(" -> parent lacks #interrupt-cells !\n");
 				goto fail;
 			}
 			newintsize = *tmp;
-			tmp = get_property(newpar, "#address-cells",
-						  NULL);
+			tmp = of_get_property(newpar, "#address-cells", NULL);
 			newaddrsize = (tmp == NULL) ? 0 : *tmp;
 
 			DBG(" -> newintsize=%d, newaddrsize=%d\n",
@@ -928,7 +927,7 @@
 	 * everything together on these)
 	 */
 	while (device) {
-		ints = get_property(device, "AAPL,interrupts", &intlen);
+		ints = of_get_property(device, "AAPL,interrupts", &intlen);
 		if (ints != NULL)
 			break;
 		device = device->parent;
@@ -970,13 +969,13 @@
 		return of_irq_map_oldworld(device, index, out_irq);
 
 	/* Get the interrupts property */
-	intspec = get_property(device, "interrupts", &intlen);
+	intspec = of_get_property(device, "interrupts", &intlen);
 	if (intspec == NULL)
 		return -EINVAL;
 	intlen /= sizeof(u32);
 
 	/* Get the reg property (if any) */
-	addr = get_property(device, "reg", NULL);
+	addr = of_get_property(device, "reg", NULL);
 
 	/* Look for the interrupt parent. */
 	p = of_irq_find_parent(device);
@@ -984,7 +983,7 @@
 		return -EINVAL;
 
 	/* Get size of interrupt specifier */
-	tmp = get_property(p, "#interrupt-cells", NULL);
+	tmp = of_get_property(p, "#interrupt-cells", NULL);
 	if (tmp == NULL) {
 		of_node_put(p);
 		return -EINVAL;
diff --git a/arch/powerpc/kernel/rtas-proc.c b/arch/powerpc/kernel/rtas-proc.c
index 6cbf2ae..190b7ed 100644
--- a/arch/powerpc/kernel/rtas-proc.c
+++ b/arch/powerpc/kernel/rtas-proc.c
@@ -450,7 +450,7 @@
 		int llen, offs;
 
 		sprintf (rstr, SENSOR_PREFIX"%04d", p->token);
-		loc = get_property(rtas_node, rstr, &llen);
+		loc = of_get_property(rtas_node, rstr, &llen);
 
 		/* A sensor may have multiple instances */
 		for (j = 0, offs = 0; j <= p->quant; j++) {
@@ -477,7 +477,7 @@
 	const unsigned int *utmp;
 	int len, i;
 
-	utmp = get_property(rtas_node, "rtas-sensors", &len);
+	utmp = of_get_property(rtas_node, "rtas-sensors", &len);
 	if (utmp == NULL) {
 		printk (KERN_ERR "error: could not get rtas-sensors\n");
 		return 1;
diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
index 9d0735a..2147807 100644
--- a/arch/powerpc/kernel/rtas.c
+++ b/arch/powerpc/kernel/rtas.c
@@ -192,18 +192,19 @@
 
 	if (display_width == 0) {
 		display_width = 0x10;
-		if ((root = find_path_device("/rtas"))) {
-			if ((p = get_property(root,
+		if ((root = of_find_node_by_path("/rtas"))) {
+			if ((p = of_get_property(root,
 					"ibm,display-line-length", NULL)))
 				display_width = *p;
-			if ((p = get_property(root,
+			if ((p = of_get_property(root,
 					"ibm,form-feed", NULL)))
 				form_feed = *p;
-			if ((p = get_property(root,
+			if ((p = of_get_property(root,
 					"ibm,display-number-of-lines", NULL)))
 				display_lines = *p;
-			row_width = get_property(root,
+			row_width = of_get_property(root,
 					"ibm,display-truncation-length", NULL);
+			of_node_put(root);
 		}
 		display_character = rtas_token("display-character");
 		set_indicator = rtas_token("set-indicator");
@@ -298,7 +299,7 @@
 	const int *tokp;
 	if (rtas.dev == NULL)
 		return RTAS_UNKNOWN_SERVICE;
-	tokp = get_property(rtas.dev, service, NULL);
+	tokp = of_get_property(rtas.dev, service, NULL);
 	return tokp ? *tokp : RTAS_UNKNOWN_SERVICE;
 }
 EXPORT_SYMBOL(rtas_token);
@@ -832,12 +833,12 @@
 	if (rtas.dev) {
 		const u32 *basep, *entryp, *sizep;
 
-		basep = get_property(rtas.dev, "linux,rtas-base", NULL);
-		sizep = get_property(rtas.dev, "rtas-size", NULL);
+		basep = of_get_property(rtas.dev, "linux,rtas-base", NULL);
+		sizep = of_get_property(rtas.dev, "rtas-size", NULL);
 		if (basep != NULL && sizep != NULL) {
 			rtas.base = *basep;
 			rtas.size = *sizep;
-			entryp = get_property(rtas.dev,
+			entryp = of_get_property(rtas.dev,
 					"linux,rtas-entry", NULL);
 			if (entryp == NULL) /* Ugh */
 				rtas.entry = rtas.base;
diff --git a/arch/powerpc/kernel/rtas_pci.c b/arch/powerpc/kernel/rtas_pci.c
index ace9f4c..f228682 100644
--- a/arch/powerpc/kernel/rtas_pci.c
+++ b/arch/powerpc/kernel/rtas_pci.c
@@ -60,7 +60,7 @@
 {
         const char *status;
 
-        status = get_property(dn, "status", NULL);
+        status = of_get_property(dn, "status", NULL);
 
         if (!status)
                 return 1;
@@ -177,7 +177,7 @@
 
 int is_python(struct device_node *dev)
 {
-	const char *model = get_property(dev, "model", NULL);
+	const char *model = of_get_property(dev, "model", NULL);
 
 	if (model && strstr(model, "Python"))
 		return 1;
@@ -247,7 +247,7 @@
 	const int *bus_range;
 	unsigned int len;
 
-	bus_range = get_property(dev, "bus-range", &len);
+	bus_range = of_get_property(dev, "bus-range", &len);
 	if (bus_range == NULL || len < 2 * sizeof(int)) {
 		return 1;
  	}
@@ -274,7 +274,7 @@
 	return 0;
 }
 
-unsigned long __init find_and_init_phbs(void)
+void __init find_and_init_phbs(void)
 {
 	struct device_node *node;
 	struct pci_controller *phb;
@@ -309,18 +309,16 @@
 	if (of_chosen) {
 		const int *prop;
 
-		prop = get_property(of_chosen,
+		prop = of_get_property(of_chosen,
 				"linux,pci-probe-only", NULL);
 		if (prop)
 			pci_probe_only = *prop;
 
-		prop = get_property(of_chosen,
+		prop = of_get_property(of_chosen,
 				"linux,pci-assign-all-buses", NULL);
 		if (prop)
 			pci_assign_all_buses = *prop;
 	}
-
-	return 0;
 }
 
 /* RPA-specific bits for removing PHBs */
diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c
index 89cfaf4..3708037 100644
--- a/arch/powerpc/kernel/setup-common.c
+++ b/arch/powerpc/kernel/setup-common.c
@@ -21,7 +21,6 @@
 #include <linux/delay.h>
 #include <linux/initrd.h>
 #include <linux/platform_device.h>
-#include <linux/ide.h>
 #include <linux/seq_file.h>
 #include <linux/ioport.h>
 #include <linux/console.h>
@@ -304,26 +303,8 @@
 void __init check_for_initrd(void)
 {
 #ifdef CONFIG_BLK_DEV_INITRD
-	const unsigned int *prop;
-	int len;
-
-	DBG(" -> check_for_initrd()\n");
-
-	if (of_chosen) {
-		prop = get_property(of_chosen, "linux,initrd-start", &len);
-		if (prop != NULL) {
-			initrd_start = (unsigned long)
-				__va(of_read_ulong(prop, len / 4));
-			prop = get_property(of_chosen,
-					"linux,initrd-end", &len);
-			if (prop != NULL) {
-				initrd_end = (unsigned long)
-					__va(of_read_ulong(prop, len / 4));
-				initrd_below_start_ok = 1;
-			} else
-				initrd_start = 0;
-		}
-	}
+	DBG(" -> check_for_initrd()  initrd_start=0x%lx  initrd_end=0x%lx\n",
+	    initrd_start, initrd_end);
 
 	/* If we were passed an initrd, set the ROOT_DEV properly if the values
 	 * look sensible. If not, clear initrd reference.
@@ -371,11 +352,12 @@
 		const int *intserv;
 		int j, len = sizeof(u32), nthreads = 1;
 
-		intserv = get_property(dn, "ibm,ppc-interrupt-server#s", &len);
+		intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s",
+				&len);
 		if (intserv)
 			nthreads = len / sizeof(int);
 		else {
-			intserv = get_property(dn, "reg", NULL);
+			intserv = of_get_property(dn, "reg", NULL);
 			if (!intserv)
 				intserv = &cpu;	/* assume logical == phys */
 		}
@@ -398,10 +380,10 @@
 		int num_addr_cell, num_size_cell, maxcpus;
 		const unsigned int *ireg;
 
-		num_addr_cell = prom_n_addr_cells(dn);
-		num_size_cell = prom_n_size_cells(dn);
+		num_addr_cell = of_n_addr_cells(dn);
+		num_size_cell = of_n_size_cells(dn);
 
-		ireg = get_property(dn, "ibm,lrdr-capacity", NULL);
+		ireg = of_get_property(dn, "ibm,lrdr-capacity", NULL);
 
 		if (!ireg)
 			goto out;
@@ -496,11 +478,39 @@
 	printk(KERN_INFO "Using %s machine description\n", ppc_md.name);
 }
 
+/* Match a class of boards, not a specific device configuration. */
 int check_legacy_ioport(unsigned long base_port)
 {
-	if (ppc_md.check_legacy_ioport == NULL)
-		return 0;
-	return ppc_md.check_legacy_ioport(base_port);
+	struct device_node *parent, *np = NULL;
+	int ret = -ENODEV;
+
+	switch(base_port) {
+	case I8042_DATA_REG:
+		np = of_find_node_by_type(NULL, "8042");
+		break;
+	case FDC_BASE: /* FDC1 */
+		np = of_find_node_by_type(NULL, "fdc");
+		break;
+#ifdef CONFIG_PPC_PREP
+	case _PIDXR:
+	case _PNPWRP:
+	case PNPBIOS_BASE:
+		/* implement me */
+#endif
+	default:
+		/* ipmi is supposed to fail here */
+		break;
+	}
+	if (!np)
+		return ret;
+	parent = of_get_parent(np);
+	if (parent) {
+		if (strcmp(parent->type, "isa") == 0)
+			ret = 0;
+		of_node_put(parent);
+	}
+	of_node_put(np);
+	return ret;
 }
 EXPORT_SYMBOL(check_legacy_ioport);
 
diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c
index 44a6a3c..35f8f44 100644
--- a/arch/powerpc/kernel/setup_32.c
+++ b/arch/powerpc/kernel/setup_32.c
@@ -92,7 +92,8 @@
 
 	/* First zero the BSS -- use memset_io, some platforms don't have
 	 * caches on yet */
-	memset_io((void __iomem *)PTRRELOC(&__bss_start), 0, _end - __bss_start);
+	memset_io((void __iomem *)PTRRELOC(&__bss_start), 0,
+			__bss_stop - __bss_start);
 
 	/*
 	 * Identify the CPU type and fix up code sections
@@ -195,18 +196,22 @@
 
 #endif /* CONFIG_NVRAM */
 
-static struct cpu cpu_devices[NR_CPUS];
+static DEFINE_PER_CPU(struct cpu, cpu_devices);
 
 int __init ppc_init(void)
 {
-	int i;
+	int cpu;
 
 	/* clear the progress line */
-	if ( ppc_md.progress ) ppc_md.progress("             ", 0xffff);
+	if (ppc_md.progress)
+		ppc_md.progress("             ", 0xffff);
 
 	/* register CPU devices */
-	for_each_possible_cpu(i)
-		register_cpu(&cpu_devices[i], i);
+	for_each_possible_cpu(cpu) {
+		struct cpu *c = &per_cpu(cpu_devices, cpu);
+		c->hotpluggable = 1;
+		register_cpu(c, cpu);
+	}
 
 	/* call platform init */
 	if (ppc_md.init != NULL) {
diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c
index 3733de3..22083ce 100644
--- a/arch/powerpc/kernel/setup_64.c
+++ b/arch/powerpc/kernel/setup_64.c
@@ -20,7 +20,6 @@
 #include <linux/reboot.h>
 #include <linux/delay.h>
 #include <linux/initrd.h>
-#include <linux/ide.h>
 #include <linux/seq_file.h>
 #include <linux/ioport.h>
 #include <linux/console.h>
@@ -110,7 +109,7 @@
 	dn = of_find_node_by_path("/options");
 
 	if (dn) {
-		smt_option = get_property(dn, "ibm,smt-enabled", NULL);
+		smt_option = of_get_property(dn, "ibm,smt-enabled", NULL);
 
                 if (smt_option) {
 			if (!strcmp(smt_option, "on"))
@@ -305,10 +304,10 @@
 
 			size = 0;
 			lsize = cur_cpu_spec->dcache_bsize;
-			sizep = get_property(np, "d-cache-size", NULL);
+			sizep = of_get_property(np, "d-cache-size", NULL);
 			if (sizep != NULL)
 				size = *sizep;
-			lsizep = get_property(np, dc, NULL);
+			lsizep = of_get_property(np, dc, NULL);
 			if (lsizep != NULL)
 				lsize = *lsizep;
 			if (sizep == 0 || lsizep == 0)
@@ -322,10 +321,10 @@
 
 			size = 0;
 			lsize = cur_cpu_spec->icache_bsize;
-			sizep = get_property(np, "i-cache-size", NULL);
+			sizep = of_get_property(np, "i-cache-size", NULL);
 			if (sizep != NULL)
 				size = *sizep;
-			lsizep = get_property(np, ic, NULL);
+			lsizep = of_get_property(np, ic, NULL);
 			if (lsizep != NULL)
 				lsize = *lsizep;
 			if (sizep == 0 || lsizep == 0)
diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
index 924d692..d8e503b 100644
--- a/arch/powerpc/kernel/smp.c
+++ b/arch/powerpc/kernel/smp.c
@@ -428,10 +428,6 @@
 	smp_wmb();
 	while (__get_cpu_var(cpu_state) != CPU_UP_PREPARE)
 		cpu_relax();
-
-#ifdef CONFIG_PPC64
-	flush_tlb_pending();
-#endif
 	cpu_set(cpu, cpu_online_map);
 	local_irq_enable();
 }
diff --git a/arch/powerpc/kernel/sys_ppc32.c b/arch/powerpc/kernel/sys_ppc32.c
index 673e8d9..047246a 100644
--- a/arch/powerpc/kernel/sys_ppc32.c
+++ b/arch/powerpc/kernel/sys_ppc32.c
@@ -53,10 +53,6 @@
 #include <asm/ppc-pci.h>
 #include <asm/syscalls.h>
 
-/* readdir & getdents */
-#define NAME_OFFSET(de) ((int) ((de)->d_name - (char __user *) (de)))
-#define ROUND_UP(x) (((x)+sizeof(u32)-1) & ~(sizeof(u32)-1))
-
 struct old_linux_dirent32 {
 	u32		d_ino;
 	u32		d_offset;
diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c
index d57818a..933e214 100644
--- a/arch/powerpc/kernel/sysfs.c
+++ b/arch/powerpc/kernel/sysfs.c
@@ -66,16 +66,17 @@
 	if (!cpu_has_feature(CPU_FTR_SMT))
 		return -ENODEV;
 
-	options = find_path_device("/options");
+	options = of_find_node_by_path("/options");
 	if (!options)
 		return -ENODEV;
 
-	val = get_property(options, "ibm,smt-snooze-delay", NULL);
+	val = of_get_property(options, "ibm,smt-snooze-delay", NULL);
 	if (!smt_snooze_cmdline && val) {
 		for_each_possible_cpu(cpu)
 			per_cpu(smt_snooze_delay, cpu) = *val;
 	}
 
+	of_node_put(options);
 	return 0;
 }
 __initcall(smt_setup);
@@ -189,12 +190,12 @@
 SYSFS_PMCSETUP(spurr, SPRN_SPURR);
 SYSFS_PMCSETUP(dscr, SPRN_DSCR);
 
-SYSFS_PMCSETUP(pa6t_pmc0, PA6T_SPRN_PMC0);
-SYSFS_PMCSETUP(pa6t_pmc1, PA6T_SPRN_PMC1);
-SYSFS_PMCSETUP(pa6t_pmc2, PA6T_SPRN_PMC2);
-SYSFS_PMCSETUP(pa6t_pmc3, PA6T_SPRN_PMC3);
-SYSFS_PMCSETUP(pa6t_pmc4, PA6T_SPRN_PMC4);
-SYSFS_PMCSETUP(pa6t_pmc5, PA6T_SPRN_PMC5);
+SYSFS_PMCSETUP(pa6t_pmc0, SPRN_PA6T_PMC0);
+SYSFS_PMCSETUP(pa6t_pmc1, SPRN_PA6T_PMC1);
+SYSFS_PMCSETUP(pa6t_pmc2, SPRN_PA6T_PMC2);
+SYSFS_PMCSETUP(pa6t_pmc3, SPRN_PA6T_PMC3);
+SYSFS_PMCSETUP(pa6t_pmc4, SPRN_PA6T_PMC4);
+SYSFS_PMCSETUP(pa6t_pmc5, SPRN_PA6T_PMC5);
 
 
 static SYSDEV_ATTR(mmcra, 0600, show_mmcra, store_mmcra);
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index f6f0c6b..7cedef8 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -834,7 +834,7 @@
 	cpu = of_find_node_by_type(NULL, "cpu");
 
 	if (cpu) {
-		fp = get_property(cpu, name, NULL);
+		fp = of_get_property(cpu, name, NULL);
 		if (fp) {
 			found = 1;
 			*val = of_read_ulong(fp, cells);
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 17724fb..f786222 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -90,21 +90,11 @@
  * Trap & Exception support
  */
 
-static DEFINE_SPINLOCK(die_lock);
-
-int die(const char *str, struct pt_regs *regs, long err)
-{
-	static int die_counter;
-
-	if (debugger(regs))
-		return 1;
-
-	console_verbose();
-	spin_lock_irq(&die_lock);
-	bust_spinlocks(1);
 #ifdef CONFIG_PMAC_BACKLIGHT
+static void pmac_backlight_unblank(void)
+{
 	mutex_lock(&pmac_backlight_mutex);
-	if (machine_is(powermac) && pmac_backlight) {
+	if (pmac_backlight) {
 		struct backlight_properties *props;
 
 		props = &pmac_backlight->props;
@@ -113,26 +103,67 @@
 		backlight_update_status(pmac_backlight);
 	}
 	mutex_unlock(&pmac_backlight_mutex);
+}
+#else
+static inline void pmac_backlight_unblank(void) { }
 #endif
-	printk("Oops: %s, sig: %ld [#%d]\n", str, err, ++die_counter);
+
+int die(const char *str, struct pt_regs *regs, long err)
+{
+	static struct {
+		spinlock_t lock;
+		u32 lock_owner;
+		int lock_owner_depth;
+	} die = {
+		.lock =			__SPIN_LOCK_UNLOCKED(die.lock),
+		.lock_owner =		-1,
+		.lock_owner_depth =	0
+	};
+	static int die_counter;
+	unsigned long flags;
+
+	if (debugger(regs))
+		return 1;
+
+	oops_enter();
+
+	if (die.lock_owner != raw_smp_processor_id()) {
+		console_verbose();
+		spin_lock_irqsave(&die.lock, flags);
+		die.lock_owner = smp_processor_id();
+		die.lock_owner_depth = 0;
+		bust_spinlocks(1);
+		if (machine_is(powermac))
+			pmac_backlight_unblank();
+	} else {
+		local_save_flags(flags);
+	}
+
+	if (++die.lock_owner_depth < 3) {
+		printk("Oops: %s, sig: %ld [#%d]\n", str, err, ++die_counter);
 #ifdef CONFIG_PREEMPT
-	printk("PREEMPT ");
+		printk("PREEMPT ");
 #endif
 #ifdef CONFIG_SMP
-	printk("SMP NR_CPUS=%d ", NR_CPUS);
+		printk("SMP NR_CPUS=%d ", NR_CPUS);
 #endif
 #ifdef CONFIG_DEBUG_PAGEALLOC
-	printk("DEBUG_PAGEALLOC ");
+		printk("DEBUG_PAGEALLOC ");
 #endif
 #ifdef CONFIG_NUMA
-	printk("NUMA ");
+		printk("NUMA ");
 #endif
-	printk("%s\n", ppc_md.name ? "" : ppc_md.name);
+		printk("%s\n", ppc_md.name ? ppc_md.name : "");
 
-	print_modules();
-	show_regs(regs);
+		print_modules();
+		show_regs(regs);
+	} else {
+		printk("Recursive die() failure, output suppressed\n");
+	}
+
 	bust_spinlocks(0);
-	spin_unlock_irq(&die_lock);
+	die.lock_owner = -1;
+	spin_unlock_irqrestore(&die.lock, flags);
 
 	if (kexec_should_crash(current) ||
 		kexec_sr_activated(smp_processor_id()))
@@ -145,6 +176,7 @@
 	if (panic_on_oops)
 		panic("Fatal exception");
 
+	oops_exit();
 	do_exit(err);
 
 	return 0;
diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c
index 2968ffe..9eaefac 100644
--- a/arch/powerpc/kernel/vio.c
+++ b/arch/powerpc/kernel/vio.c
@@ -81,7 +81,7 @@
 		struct iommu_table *tbl;
 		unsigned long offset, size;
 
-		dma_window = get_property(dev->dev.archdata.of_node,
+		dma_window = of_get_property(dev->dev.archdata.of_node,
 					  "ibm,my-dma-window", NULL);
 		if (!dma_window)
 			return NULL;
@@ -226,7 +226,7 @@
 		return NULL;
 	}
 
-	unit_address = get_property(of_node, "reg", NULL);
+	unit_address = of_get_property(of_node, "reg", NULL);
 	if (unit_address == NULL) {
 		printk(KERN_WARNING "%s: node %s missing 'reg'\n",
 				__FUNCTION__,
@@ -246,7 +246,7 @@
 	viodev->type = of_node->type;
 	viodev->unit_address = *unit_address;
 	if (firmware_has_feature(FW_FEATURE_ISERIES)) {
-		unit_address = get_property(of_node,
+		unit_address = of_get_property(of_node,
 				"linux,unit_address", NULL);
 		if (unit_address != NULL)
 			viodev->unit_address = *unit_address;
@@ -308,7 +308,7 @@
 		return err;
 	}
 
-	node_vroot = find_devices("vdevice");
+	node_vroot = of_find_node_by_name(NULL, "vdevice");
 	if (node_vroot) {
 		struct device_node *of_node;
 
@@ -322,6 +322,7 @@
 					__FUNCTION__, of_node);
 			vio_register_device_node(of_node);
 		}
+		of_node_put(node_vroot);
 	}
 
 	return 0;
@@ -377,7 +378,7 @@
 	dn = dev->archdata.of_node;
 	if (!dn)
 		return -ENODEV;
-	cp = get_property(dn, "compatible", &length);
+	cp = of_get_property(dn, "compatible", &length);
 	if (!cp)
 		return -ENODEV;
 
@@ -406,12 +407,12 @@
  * @which:	The property/attribute to be extracted.
  * @length:	Pointer to length of returned data size (unused if NULL).
  *
- * Calls prom.c's get_property() to return the value of the
+ * Calls prom.c's of_get_property() to return the value of the
  * attribute specified by @which
 */
 const void *vio_get_attribute(struct vio_dev *vdev, char *which, int *length)
 {
-	return get_property(vdev->dev.archdata.of_node, which, length);
+	return of_get_property(vdev->dev.archdata.of_node, which, length);
 }
 EXPORT_SYMBOL(vio_get_attribute);
 
@@ -443,7 +444,7 @@
 	char kobj_name[BUS_ID_SIZE];
 
 	/* construct the kobject name from the device node */
-	unit_address = get_property(vnode, "reg", NULL);
+	unit_address = of_get_property(vnode, "reg", NULL);
 	if (!unit_address)
 		return NULL;
 	snprintf(kobj_name, BUS_ID_SIZE, "%x", *unit_address);
diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile
index 4b1ba49..450258d 100644
--- a/arch/powerpc/lib/Makefile
+++ b/arch/powerpc/lib/Makefile
@@ -7,13 +7,12 @@
 endif
 
 ifeq ($(CONFIG_PPC_MERGE),y)
-obj-y			:= string.o strcase.o
+obj-y			:= string.o
 obj-$(CONFIG_PPC32)	+= div64.o copy_32.o checksum_32.o
 endif
 
 obj-$(CONFIG_PPC64)	+= checksum_64.o copypage_64.o copyuser_64.o \
-			   memcpy_64.o usercopy_64.o mem_64.o string.o \
-			   strcase.o
+			   memcpy_64.o usercopy_64.o mem_64.o string.o
 obj-$(CONFIG_QUICC_ENGINE) += rheap.o
 obj-$(CONFIG_XMON)	+= sstep.o
 obj-$(CONFIG_KPROBES)	+= sstep.o
diff --git a/arch/powerpc/lib/copyuser_64.S b/arch/powerpc/lib/copyuser_64.S
index a6b54cb..25ec537 100644
--- a/arch/powerpc/lib/copyuser_64.S
+++ b/arch/powerpc/lib/copyuser_64.S
@@ -24,7 +24,7 @@
 	dcbt	0,r4
 	beq	.Lcopy_page_4K
 	andi.	r6,r6,7
-	mtcrf	0x01,r5
+	PPC_MTOCRF	0x01,r5
 	blt	cr1,.Lshort_copy
 	bne	.Ldst_unaligned
 .Ldst_aligned:
@@ -135,7 +135,7 @@
 	b	.Ldo_tail
 
 .Ldst_unaligned:
-	mtcrf	0x01,r6		/* put #bytes to 8B bdry into cr7 */
+	PPC_MTOCRF	0x01,r6		/* put #bytes to 8B bdry into cr7 */
 	subf	r5,r6,r5
 	li	r7,0
 	cmpldi	r1,r5,16
@@ -150,7 +150,7 @@
 2:	bf	cr7*4+1,3f
 37:	lwzx	r0,r7,r4
 83:	stwx	r0,r7,r3
-3:	mtcrf	0x01,r5
+3:	PPC_MTOCRF	0x01,r5
 	add	r4,r6,r4
 	add	r3,r6,r3
 	b	.Ldst_aligned
diff --git a/arch/powerpc/lib/locks.c b/arch/powerpc/lib/locks.c
index 80b482c..79d0fa3 100644
--- a/arch/powerpc/lib/locks.c
+++ b/arch/powerpc/lib/locks.c
@@ -43,9 +43,11 @@
 	if (firmware_has_feature(FW_FEATURE_ISERIES))
 		HvCall2(HvCallBaseYieldProcessor, HvCall_YieldToProc,
 			((u64)holder_cpu << 32) | yield_count);
+#ifdef CONFIG_PPC_SPLPAR
 	else
 		plpar_hcall_norets(H_CONFER,
 			get_hard_smp_processor_id(holder_cpu), yield_count);
+#endif
 }
 
 /*
@@ -72,9 +74,11 @@
 	if (firmware_has_feature(FW_FEATURE_ISERIES))
 		HvCall2(HvCallBaseYieldProcessor, HvCall_YieldToProc,
 			((u64)holder_cpu << 32) | yield_count);
+#ifdef CONFIG_PPC_SPLPAR
 	else
 		plpar_hcall_norets(H_CONFER,
 			get_hard_smp_processor_id(holder_cpu), yield_count);
+#endif
 }
 #endif
 
diff --git a/arch/powerpc/lib/mem_64.S b/arch/powerpc/lib/mem_64.S
index 68df202..11ce045 100644
--- a/arch/powerpc/lib/mem_64.S
+++ b/arch/powerpc/lib/mem_64.S
@@ -19,7 +19,7 @@
 	rlwimi	r4,r4,16,0,15
 	cmplw	cr1,r5,r0		/* do we get that far? */
 	rldimi	r4,r4,32,0
-	mtcrf	1,r0
+	PPC_MTOCRF	1,r0
 	mr	r6,r3
 	blt	cr1,8f
 	beq+	3f			/* if already 8-byte aligned */
@@ -49,7 +49,7 @@
 	bdnz	4b
 5:	srwi.	r0,r5,3
 	clrlwi	r5,r5,29
-	mtcrf	1,r0
+	PPC_MTOCRF	1,r0
 	beq	8f
 	bf	29,6f
 	std	r4,0(r6)
@@ -65,7 +65,7 @@
 	std	r4,0(r6)
 	addi	r6,r6,8
 8:	cmpwi	r5,0
-	mtcrf	1,r5
+	PPC_MTOCRF	1,r5
 	beqlr+
 	bf	29,9f
 	stw	r4,0(r6)
diff --git a/arch/powerpc/lib/memcpy_64.S b/arch/powerpc/lib/memcpy_64.S
index 7173ba9..3f13112 100644
--- a/arch/powerpc/lib/memcpy_64.S
+++ b/arch/powerpc/lib/memcpy_64.S
@@ -12,7 +12,7 @@
 	.align	7
 _GLOBAL(memcpy)
 	std	r3,48(r1)	/* save destination pointer for return value */
-	mtcrf	0x01,r5
+	PPC_MTOCRF	0x01,r5
 	cmpldi	cr1,r5,16
 	neg	r6,r3		# LS 3 bits = # bytes to 8-byte dest bdry
 	andi.	r6,r6,7
@@ -128,7 +128,7 @@
 	b	.Ldo_tail
 
 .Ldst_unaligned:
-	mtcrf	0x01,r6		# put #bytes to 8B bdry into cr7
+	PPC_MTOCRF	0x01,r6		# put #bytes to 8B bdry into cr7
 	subf	r5,r6,r5
 	li	r7,0
 	cmpldi	r1,r5,16
@@ -143,7 +143,7 @@
 2:	bf	cr7*4+1,3f
 	lwzx	r0,r7,r4
 	stwx	r0,r7,r3
-3:	mtcrf	0x01,r5
+3:	PPC_MTOCRF	0x01,r5
 	add	r4,r6,r4
 	add	r3,r6,r3
 	b	.Ldst_aligned
diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c
index 7e8ded0..4aae0c3 100644
--- a/arch/powerpc/lib/sstep.c
+++ b/arch/powerpc/lib/sstep.c
@@ -54,7 +54,7 @@
  */
 int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
 {
-	unsigned int opcode, rd;
+	unsigned int opcode, rs, rb, rd, spr;
 	unsigned long int imm;
 
 	opcode = instr >> 26;
@@ -152,6 +152,49 @@
 				regs->nip &= 0xffffffffUL;
 			return 1;
 #endif
+		case 0x26:	/* mfcr */
+			regs->gpr[rd] = regs->ccr;
+			regs->gpr[rd] &= 0xffffffffUL;
+			goto mtspr_out;
+		case 0x2a6:	/* mfspr */
+			spr = (instr >> 11) & 0x3ff;
+			switch (spr) {
+			case 0x20:	/* mfxer */
+				regs->gpr[rd] = regs->xer;
+				regs->gpr[rd] &= 0xffffffffUL;
+				goto mtspr_out;
+			case 0x100:	/* mflr */
+				regs->gpr[rd] = regs->link;
+				goto mtspr_out;
+			case 0x120:	/* mfctr */
+				regs->gpr[rd] = regs->ctr;
+				goto mtspr_out;
+			}
+			break;
+		case 0x378:	/* orx */
+			rs = (instr >> 21) & 0x1f;
+			rb = (instr >> 11) & 0x1f;
+			if (rs == rb) {		/* mr */
+				rd = (instr >> 16) & 0x1f;
+				regs->gpr[rd] = regs->gpr[rs];
+				goto mtspr_out;
+			}
+			break;
+		case 0x3a6:	/* mtspr */
+			spr = (instr >> 11) & 0x3ff;
+			switch (spr) {
+			case 0x20:	/* mtxer */
+				regs->xer = (regs->gpr[rd] & 0xffffffffUL);
+				goto mtspr_out;
+			case 0x100:	/* mtlr */
+				regs->link = regs->gpr[rd];
+				goto mtspr_out;
+			case 0x120:	/* mtctr */
+				regs->ctr = regs->gpr[rd];
+mtspr_out:
+				regs->nip += 4;
+				return 1;
+			}
 		}
 	}
 	return 0;
diff --git a/arch/powerpc/lib/strcase.c b/arch/powerpc/lib/strcase.c
deleted file mode 100644
index f8ec1eb..0000000
--- a/arch/powerpc/lib/strcase.c
+++ /dev/null
@@ -1,25 +0,0 @@
-#include <linux/types.h>
-#include <linux/ctype.h>
-#include <linux/string.h>
-
-int strcasecmp(const char *s1, const char *s2)
-{
-	int c1, c2;
-
-	do {
-		c1 = tolower(*s1++);
-		c2 = tolower(*s2++);
-	} while (c1 == c2 && c1 != 0);
-	return c1 - c2;
-}
-
-int strncasecmp(const char *s1, const char *s2, size_t n)
-{
-	int c1, c2;
-
-	do {
-		c1 = tolower(*s1++);
-		c2 = tolower(*s2++);
-	} while ((--n > 0) && c1 == c2 && c1 != 0);
-	return c1 - c2;
-}
diff --git a/arch/powerpc/mm/hash_low_32.S b/arch/powerpc/mm/hash_low_32.S
index bd68df5..ddceefc 100644
--- a/arch/powerpc/mm/hash_low_32.S
+++ b/arch/powerpc/mm/hash_low_32.S
@@ -283,6 +283,7 @@
 #define PTEG_SIZE	64
 #define LG_PTEG_SIZE	6
 #define LDPTEu		lwzu
+#define LDPTE		lwz
 #define STPTE		stw
 #define CMPPTE		cmpw
 #define PTE_H		0x40
@@ -389,13 +390,30 @@
 	 * and we know there is a definite (although small) speed
 	 * advantage to putting the PTE in the primary PTEG, we always
 	 * put the PTE in the primary PTEG.
+	 *
+	 * In addition, we skip any slot that is mapping kernel text in
+	 * order to avoid a deadlock when not using BAT mappings if
+	 * trying to hash in the kernel hash code itself after it has
+	 * already taken the hash table lock. This works in conjunction
+	 * with pre-faulting of the kernel text.
+	 *
+	 * If the hash table bucket is full of kernel text entries, we'll
+	 * lockup here but that shouldn't happen
 	 */
-	addis	r4,r7,next_slot@ha
+
+1:	addis	r4,r7,next_slot@ha		/* get next evict slot */
 	lwz	r6,next_slot@l(r4)
-	addi	r6,r6,PTE_SIZE
+	addi	r6,r6,PTE_SIZE			/* search for candidate */
 	andi.	r6,r6,7*PTE_SIZE
 	stw	r6,next_slot@l(r4)
 	add	r4,r3,r6
+	LDPTE	r0,PTE_SIZE/2(r4)		/* get PTE second word */
+	clrrwi	r0,r0,12
+	lis	r6,etext@h
+	ori	r6,r6,etext@l			/* get etext */
+	tophys(r6,r6)
+	cmpl	cr0,r0,r6			/* compare and try again */
+	blt	1b
 
 #ifndef CONFIG_SMP
 	/* Store PTE in PTEG */
diff --git a/arch/powerpc/mm/hash_low_64.S b/arch/powerpc/mm/hash_low_64.S
index 9bc0a9c..e64ce3e 100644
--- a/arch/powerpc/mm/hash_low_64.S
+++ b/arch/powerpc/mm/hash_low_64.S
@@ -445,9 +445,12 @@
 
 htab_insert_pte:
 	/* real page number in r5, PTE RPN value + index */
-	rldicl	r5,r31,64-PTE_RPN_SHIFT,PTE_RPN_SHIFT
+	andis.	r0,r31,_PAGE_4K_PFN@h
+	srdi	r5,r31,PTE_RPN_SHIFT
+	bne-	htab_special_pfn
 	sldi	r5,r5,PAGE_SHIFT-HW_PAGE_SHIFT
 	add	r5,r5,r25
+htab_special_pfn:
 	sldi	r5,r5,HW_PAGE_SHIFT
 
 	/* Calculate primary group hash */
diff --git a/arch/powerpc/mm/hash_native_64.c b/arch/powerpc/mm/hash_native_64.c
index 6f1016a..79aedaf 100644
--- a/arch/powerpc/mm/hash_native_64.c
+++ b/arch/powerpc/mm/hash_native_64.c
@@ -505,7 +505,7 @@
 	int enabled = 1;
 
 	if (root) {
-		const char *model = get_property(root, "model", NULL);
+		const char *model = of_get_property(root, "model", NULL);
 		if (model && !strcmp(model, "IBM,9076-N81"))
 			enabled = 0;
 		of_node_put(root);
diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c
index 3c7fe2c..4961846 100644
--- a/arch/powerpc/mm/hash_utils_64.c
+++ b/arch/powerpc/mm/hash_utils_64.c
@@ -100,6 +100,11 @@
 #ifdef CONFIG_PPC_64K_PAGES
 int mmu_ci_restrictions;
 #endif
+#ifdef CONFIG_DEBUG_PAGEALLOC
+static u8 *linear_map_hash_slots;
+static unsigned long linear_map_hash_count;
+static spinlock_t linear_map_hash_lock;
+#endif /* CONFIG_DEBUG_PAGEALLOC */
 
 /* There are definitions of page sizes arrays to be used when none
  * is provided by the firmware.
@@ -152,11 +157,10 @@
 
 	for (vaddr = vstart, paddr = pstart; vaddr < vend;
 	     vaddr += step, paddr += step) {
-		unsigned long vpn, hash, hpteg;
+		unsigned long hash, hpteg;
 		unsigned long vsid = get_kernel_vsid(vaddr);
 		unsigned long va = (vsid << 28) | (vaddr & 0x0fffffff);
 
-		vpn = va >> shift;
 		tmp_mode = mode;
 		
 		/* Make non-kernel text non-executable */
@@ -174,6 +178,10 @@
 
 		if (ret < 0)
 			break;
+#ifdef CONFIG_DEBUG_PAGEALLOC
+		if ((paddr >> PAGE_SHIFT) < linear_map_hash_count)
+			linear_map_hash_slots[paddr >> PAGE_SHIFT] = ret | 0x80;
+#endif /* CONFIG_DEBUG_PAGEALLOC */
 	}
 	return ret < 0 ? ret : 0;
 }
@@ -281,6 +289,7 @@
 		memcpy(mmu_psize_defs, mmu_psize_defaults_gp,
 		       sizeof(mmu_psize_defaults_gp));
  found:
+#ifndef CONFIG_DEBUG_PAGEALLOC
 	/*
 	 * Pick a size for the linear mapping. Currently, we only support
 	 * 16M, 1M and 4K which is the default
@@ -289,6 +298,7 @@
 		mmu_linear_psize = MMU_PAGE_16M;
 	else if (mmu_psize_defs[MMU_PAGE_1M].shift)
 		mmu_linear_psize = MMU_PAGE_1M;
+#endif /* CONFIG_DEBUG_PAGEALLOC */
 
 #ifdef CONFIG_PPC_64K_PAGES
 	/*
@@ -303,12 +313,14 @@
 	if (mmu_psize_defs[MMU_PAGE_64K].shift) {
 		mmu_virtual_psize = MMU_PAGE_64K;
 		mmu_vmalloc_psize = MMU_PAGE_64K;
+		if (mmu_linear_psize == MMU_PAGE_4K)
+			mmu_linear_psize = MMU_PAGE_64K;
 		if (cpu_has_feature(CPU_FTR_CI_LARGE_PAGE))
 			mmu_io_psize = MMU_PAGE_64K;
 		else
 			mmu_ci_restrictions = 1;
 	}
-#endif
+#endif /* CONFIG_PPC_64K_PAGES */
 
 	printk(KERN_DEBUG "Page orders: linear mapping = %d, "
 	       "virtual = %d, io = %d\n",
@@ -476,6 +488,13 @@
 
 	mode_rw = _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_COHERENT | PP_RWXX;
 
+#ifdef CONFIG_DEBUG_PAGEALLOC
+	linear_map_hash_count = lmb_end_of_DRAM() >> PAGE_SHIFT;
+	linear_map_hash_slots = __va(lmb_alloc_base(linear_map_hash_count,
+						    1, lmb.rmo_size));
+	memset(linear_map_hash_slots, 0, linear_map_hash_count);
+#endif /* CONFIG_DEBUG_PAGEALLOC */
+
 	/* On U3 based machines, we need to reserve the DART area and
 	 * _NOT_ map it to avoid cache paradoxes as it's remapped non
 	 * cacheable later on
@@ -573,6 +592,27 @@
 	return pp;
 }
 
+/*
+ * Demote a segment to using 4k pages.
+ * For now this makes the whole process use 4k pages.
+ */
+void demote_segment_4k(struct mm_struct *mm, unsigned long addr)
+{
+#ifdef CONFIG_PPC_64K_PAGES
+	if (mm->context.user_psize == MMU_PAGE_4K)
+		return;
+	mm->context.user_psize = MMU_PAGE_4K;
+	mm->context.sllp = SLB_VSID_USER | mmu_psize_defs[MMU_PAGE_4K].sllp;
+	get_paca()->context = mm->context;
+	slb_flush_and_rebolt();
+#ifdef CONFIG_SPE_BASE
+	spu_flush_all_slbs(mm);
+#endif
+#endif
+}
+
+EXPORT_SYMBOL_GPL(demote_segment_4k);
+
 /* Result code is:
  *  0 - handled
  *  1 - normal page fault
@@ -665,15 +705,19 @@
 #ifndef CONFIG_PPC_64K_PAGES
 	rc = __hash_page_4K(ea, access, vsid, ptep, trap, local);
 #else
+	/* If _PAGE_4K_PFN is set, make sure this is a 4k segment */
+	if (pte_val(*ptep) & _PAGE_4K_PFN) {
+		demote_segment_4k(mm, ea);
+		psize = MMU_PAGE_4K;
+	}
+
 	if (mmu_ci_restrictions) {
 		/* If this PTE is non-cacheable, switch to 4k */
 		if (psize == MMU_PAGE_64K &&
 		    (pte_val(*ptep) & _PAGE_NO_CACHE)) {
 			if (user_region) {
+				demote_segment_4k(mm, ea);
 				psize = MMU_PAGE_4K;
-				mm->context.user_psize = MMU_PAGE_4K;
-				mm->context.sllp = SLB_VSID_USER |
-					mmu_psize_defs[MMU_PAGE_4K].sllp;
 			} else if (ea < VMALLOC_END) {
 				/*
 				 * some driver did a non-cacheable mapping
@@ -756,16 +800,8 @@
 	if (mmu_ci_restrictions) {
 		/* If this PTE is non-cacheable, switch to 4k */
 		if (mm->context.user_psize == MMU_PAGE_64K &&
-		    (pte_val(*ptep) & _PAGE_NO_CACHE)) {
-			mm->context.user_psize = MMU_PAGE_4K;
-			mm->context.sllp = SLB_VSID_USER |
-				mmu_psize_defs[MMU_PAGE_4K].sllp;
-			get_paca()->context = mm->context;
-			slb_flush_and_rebolt();
-#ifdef CONFIG_SPE_BASE
-			spu_flush_all_slbs(mm);
-#endif
-		}
+		    (pte_val(*ptep) & _PAGE_NO_CACHE))
+			demote_segment_4k(mm, ea);
 	}
 	if (mm->context.user_psize == MMU_PAGE_64K)
 		__hash_page_64K(ea, access, vsid, ptep, trap, local);
@@ -825,3 +861,62 @@
 	}
 	bad_page_fault(regs, address, SIGBUS);
 }
+
+#ifdef CONFIG_DEBUG_PAGEALLOC
+static void kernel_map_linear_page(unsigned long vaddr, unsigned long lmi)
+{
+	unsigned long hash, hpteg, vsid = get_kernel_vsid(vaddr);
+	unsigned long va = (vsid << 28) | (vaddr & 0x0fffffff);
+	unsigned long mode = _PAGE_ACCESSED | _PAGE_DIRTY |
+		_PAGE_COHERENT | PP_RWXX | HPTE_R_N;
+	int ret;
+
+	hash = hpt_hash(va, PAGE_SHIFT);
+	hpteg = ((hash & htab_hash_mask) * HPTES_PER_GROUP);
+
+	ret = ppc_md.hpte_insert(hpteg, va, __pa(vaddr),
+				 mode, HPTE_V_BOLTED, mmu_linear_psize);
+	BUG_ON (ret < 0);
+	spin_lock(&linear_map_hash_lock);
+	BUG_ON(linear_map_hash_slots[lmi] & 0x80);
+	linear_map_hash_slots[lmi] = ret | 0x80;
+	spin_unlock(&linear_map_hash_lock);
+}
+
+static void kernel_unmap_linear_page(unsigned long vaddr, unsigned long lmi)
+{
+	unsigned long hash, hidx, slot, vsid = get_kernel_vsid(vaddr);
+	unsigned long va = (vsid << 28) | (vaddr & 0x0fffffff);
+
+	hash = hpt_hash(va, PAGE_SHIFT);
+	spin_lock(&linear_map_hash_lock);
+	BUG_ON(!(linear_map_hash_slots[lmi] & 0x80));
+	hidx = linear_map_hash_slots[lmi] & 0x7f;
+	linear_map_hash_slots[lmi] = 0;
+	spin_unlock(&linear_map_hash_lock);
+	if (hidx & _PTEIDX_SECONDARY)
+		hash = ~hash;
+	slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;
+	slot += hidx & _PTEIDX_GROUP_IX;
+	ppc_md.hpte_invalidate(slot, va, mmu_linear_psize, 0);
+}
+
+void kernel_map_pages(struct page *page, int numpages, int enable)
+{
+	unsigned long flags, vaddr, lmi;
+	int i;
+
+	local_irq_save(flags);
+	for (i = 0; i < numpages; i++, page++) {
+		vaddr = (unsigned long)page_address(page);
+		lmi = __pa(vaddr) >> PAGE_SHIFT;
+		if (lmi >= linear_map_hash_count)
+			continue;
+		if (enable)
+			kernel_map_linear_page(vaddr, lmi);
+		else
+			kernel_unmap_linear_page(vaddr, lmi);
+	}
+	local_irq_restore(flags);
+}
+#endif /* CONFIG_DEBUG_PAGEALLOC */
diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c
index f6ffaaa..8508f97 100644
--- a/arch/powerpc/mm/hugetlbpage.c
+++ b/arch/powerpc/mm/hugetlbpage.c
@@ -316,12 +316,11 @@
 {
 	if (pte_present(*ptep)) {
 		/* We open-code pte_clear because we need to pass the right
-		 * argument to hpte_update (huge / !huge)
+		 * argument to hpte_need_flush (huge / !huge). Might not be
+		 * necessary anymore if we make hpte_need_flush() get the
+		 * page size from the slices
 		 */
-		unsigned long old = pte_update(ptep, ~0UL);
-		if (old & _PAGE_HASHPTE)
-			hpte_update(mm, addr & HPAGE_MASK, ptep, old, 1);
-		flush_tlb_pending();
+		pte_update(mm, addr & HPAGE_MASK, ptep, ~0UL, 1);
 	}
 	*ptep = __pte(pte_val(pte) & ~_PAGE_HPTEFLAGS);
 }
@@ -329,12 +328,7 @@
 pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr,
 			      pte_t *ptep)
 {
-	unsigned long old = pte_update(ptep, ~0UL);
-
-	if (old & _PAGE_HASHPTE)
-		hpte_update(mm, addr & HPAGE_MASK, ptep, old, 1);
-	*ptep = __pte(0);
-
+	unsigned long old = pte_update(mm, addr, ptep, ~0UL, 1);
 	return __pte(old);
 }
 
diff --git a/arch/powerpc/mm/init_32.c b/arch/powerpc/mm/init_32.c
index 0e53ca8..5fce6cc 100644
--- a/arch/powerpc/mm/init_32.c
+++ b/arch/powerpc/mm/init_32.c
@@ -115,6 +115,10 @@
 	if (strstr(cmd_line, "noltlbs")) {
 		__map_without_ltlbs = 1;
 	}
+#ifdef CONFIG_DEBUG_PAGEALLOC
+	__map_without_bats = 1;
+	__map_without_ltlbs = 1;
+#endif
 }
 
 /*
diff --git a/arch/powerpc/mm/lmb.c b/arch/powerpc/mm/lmb.c
index 716a290..e3a1e8d 100644
--- a/arch/powerpc/mm/lmb.c
+++ b/arch/powerpc/mm/lmb.c
@@ -146,6 +146,10 @@
 		unsigned long rgnbase = rgn->region[i].base;
 		unsigned long rgnsize = rgn->region[i].size;
 
+		if ((rgnbase == base) && (rgnsize == size))
+			/* Already have this region, so we're done */
+			return 0;
+
 		adjacent = lmb_addrs_adjacent(base,size,rgnbase,rgnsize);
 		if ( adjacent > 0 ) {
 			rgn->region[i].base -= size;
diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c
index 52f397c1..c4bcd75 100644
--- a/arch/powerpc/mm/mem.c
+++ b/arch/powerpc/mm/mem.c
@@ -58,9 +58,6 @@
 int mem_init_done;
 unsigned long memory_limit;
 
-extern void hash_preload(struct mm_struct *mm, unsigned long ea,
-			 unsigned long access, unsigned long trap);
-
 int page_is_ram(unsigned long pfn)
 {
 	unsigned long paddr = (pfn << PAGE_SHIFT);
diff --git a/arch/powerpc/mm/mmu_decl.h b/arch/powerpc/mm/mmu_decl.h
index bea2d21..9c4538b 100644
--- a/arch/powerpc/mm/mmu_decl.h
+++ b/arch/powerpc/mm/mmu_decl.h
@@ -19,9 +19,14 @@
  *  2 of the License, or (at your option) any later version.
  *
  */
+#include <linux/mm.h>
 #include <asm/tlbflush.h>
 #include <asm/mmu.h>
 
+extern void hash_preload(struct mm_struct *mm, unsigned long ea,
+			 unsigned long access, unsigned long trap);
+
+
 #ifdef CONFIG_PPC32
 extern void mapin_ram(void);
 extern int map_page(unsigned long va, phys_addr_t pa, int flags);
diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c
index e86c37c..b3a592b 100644
--- a/arch/powerpc/mm/numa.c
+++ b/arch/powerpc/mm/numa.c
@@ -74,7 +74,7 @@
 
 	while ((cpu_node = of_find_node_by_type(cpu_node, "cpu")) != NULL) {
 		/* Try interrupt server first */
-		interrupt_server = get_property(cpu_node,
+		interrupt_server = of_get_property(cpu_node,
 					"ibm,ppc-interrupt-server#s", &len);
 
 		len = len / sizeof(u32);
@@ -85,7 +85,7 @@
 					return cpu_node;
 			}
 		} else {
-			reg = get_property(cpu_node, "reg", &len);
+			reg = of_get_property(cpu_node, "reg", &len);
 			if (reg && (len > 0) && (reg[0] == hw_cpuid))
 				return cpu_node;
 		}
@@ -97,7 +97,7 @@
 /* must hold reference to node during call */
 static const int *of_get_associativity(struct device_node *dev)
 {
-	return get_property(dev, "ibm,associativity", NULL);
+	return of_get_property(dev, "ibm,associativity", NULL);
 }
 
 /* Returns nid in the range [0..MAX_NUMNODES-1], or -1 if no useful numa
@@ -179,7 +179,7 @@
 	 * configuration (should be all 0's) and the second is for a normal
 	 * NUMA configuration.
 	 */
-	ref_points = get_property(rtas_root,
+	ref_points = of_get_property(rtas_root,
 			"ibm,associativity-reference-points", &len);
 
 	if ((len >= 1) && ref_points) {
@@ -201,8 +201,8 @@
 	if (!memory)
 		panic("numa.c: No memory nodes found!");
 
-	*n_addr_cells = prom_n_addr_cells(memory);
-	*n_size_cells = prom_n_size_cells(memory);
+	*n_addr_cells = of_n_addr_cells(memory);
+	*n_size_cells = of_n_size_cells(memory);
 	of_node_put(memory);
 }
 
@@ -308,9 +308,9 @@
 	int nid, default_nid = 0;
 	unsigned int start, ai, flags;
 
-	lm = get_property(memory, "ibm,lmb-size", &ls);
-	dm = get_property(memory, "ibm,dynamic-memory", &ld);
-	aa = get_property(memory, "ibm,associativity-lookup-arrays", &la);
+	lm = of_get_property(memory, "ibm,lmb-size", &ls);
+	dm = of_get_property(memory, "ibm,dynamic-memory", &ld);
+	aa = of_get_property(memory, "ibm,associativity-lookup-arrays", &la);
 	if (!lm || !dm || !aa ||
 	    ls < sizeof(unsigned int) || ld < sizeof(unsigned int) ||
 	    la < 2 * sizeof(unsigned int))
@@ -404,10 +404,10 @@
 		const unsigned int *memcell_buf;
 		unsigned int len;
 
-		memcell_buf = get_property(memory,
+		memcell_buf = of_get_property(memory,
 			"linux,usable-memory", &len);
 		if (!memcell_buf || len <= 0)
-			memcell_buf = get_property(memory, "reg", &len);
+			memcell_buf = of_get_property(memory, "reg", &len);
 		if (!memcell_buf || len <= 0)
 			continue;
 
@@ -725,7 +725,7 @@
 		const unsigned int *memcell_buf;
 		unsigned int len;
 
-		memcell_buf = get_property(memory, "reg", &len);
+		memcell_buf = of_get_property(memory, "reg", &len);
 		if (!memcell_buf || len <= 0)
 			continue;
 
diff --git a/arch/powerpc/mm/pgtable_32.c b/arch/powerpc/mm/pgtable_32.c
index c284bda..bca5603 100644
--- a/arch/powerpc/mm/pgtable_32.c
+++ b/arch/powerpc/mm/pgtable_32.c
@@ -183,8 +183,8 @@
 	 * mem_init() sets high_memory so only do the check after that.
 	 */
 	if (mem_init_done && (p < virt_to_phys(high_memory))) {
-		printk("__ioremap(): phys addr "PHYS_FMT" is RAM lr %p\n", p,
-		       __builtin_return_address(0));
+		printk("__ioremap(): phys addr 0x%llx is RAM lr %p\n",
+		       (unsigned long long)p, __builtin_return_address(0));
 		return NULL;
 	}
 
@@ -266,9 +266,12 @@
 	pg = pte_alloc_kernel(pd, va);
 	if (pg != 0) {
 		err = 0;
-		set_pte_at(&init_mm, va, pg, pfn_pte(pa >> PAGE_SHIFT, __pgprot(flags)));
-		if (mem_init_done)
-			flush_HPTE(0, va, pmd_val(*pd));
+		/* The PTE should never be already set nor present in the
+		 * hash table
+		 */
+		BUG_ON(pte_val(*pg) & (_PAGE_PRESENT | _PAGE_HASHPTE));
+		set_pte_at(&init_mm, va, pg, pfn_pte(pa >> PAGE_SHIFT,
+						     __pgprot(flags)));
 	}
 	return err;
 }
@@ -279,16 +282,19 @@
 void __init mapin_ram(void)
 {
 	unsigned long v, p, s, f;
+	int ktext;
 
 	s = mmu_mapin_ram();
 	v = KERNELBASE + s;
 	p = PPC_MEMSTART + s;
 	for (; s < total_lowmem; s += PAGE_SIZE) {
-		if ((char *) v >= _stext && (char *) v < etext)
-			f = _PAGE_RAM_TEXT;
-		else
-			f = _PAGE_RAM;
+		ktext = ((char *) v >= _stext && (char *) v < etext);
+		f = ktext ?_PAGE_RAM_TEXT : _PAGE_RAM;
 		map_page(v, p, f);
+#ifdef CONFIG_PPC_STD_MMU_32
+		if (ktext)
+			hash_preload(&init_mm, v, 0, 0x300);
+#endif
 		v += PAGE_SIZE;
 		p += PAGE_SIZE;
 	}
@@ -445,3 +451,55 @@
 	return ret;
 }
 
+#ifdef CONFIG_DEBUG_PAGEALLOC
+
+static int __change_page_attr(struct page *page, pgprot_t prot)
+{
+	pte_t *kpte;
+	pmd_t *kpmd;
+	unsigned long address;
+
+	BUG_ON(PageHighMem(page));
+	address = (unsigned long)page_address(page);
+
+	if (v_mapped_by_bats(address) || v_mapped_by_tlbcam(address))
+		return 0;
+	if (!get_pteptr(&init_mm, address, &kpte, &kpmd))
+		return -EINVAL;
+	set_pte_at(&init_mm, address, kpte, mk_pte(page, prot));
+	wmb();
+	flush_HPTE(0, address, pmd_val(*kpmd));
+	pte_unmap(kpte);
+
+	return 0;
+}
+
+/*
+ * Change the page attributes of an page in the linear mapping.
+ *
+ * THIS CONFLICTS WITH BAT MAPPINGS, DEBUG USE ONLY
+ */
+static int change_page_attr(struct page *page, int numpages, pgprot_t prot)
+{
+	int i, err = 0;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	for (i = 0; i < numpages; i++, page++) {
+		err = __change_page_attr(page, prot);
+		if (err)
+			break;
+	}
+	local_irq_restore(flags);
+	return err;
+}
+
+
+void kernel_map_pages(struct page *page, int numpages, int enable)
+{
+	if (PageHighMem(page))
+		return;
+
+	change_page_attr(page, numpages, enable ? PAGE_KERNEL : __pgprot(0));
+}
+#endif /* CONFIG_DEBUG_PAGEALLOC */
diff --git a/arch/powerpc/mm/ppc_mmu_32.c b/arch/powerpc/mm/ppc_mmu_32.c
index 7cceb2c..0506667 100644
--- a/arch/powerpc/mm/ppc_mmu_32.c
+++ b/arch/powerpc/mm/ppc_mmu_32.c
@@ -85,8 +85,10 @@
 	unsigned long max_size = (256<<20);
 	unsigned long align;
 
-	if (__map_without_bats)
+	if (__map_without_bats) {
+		printk(KERN_DEBUG "RAM mapped without BATs\n");
 		return 0;
+	}
 
 	/* Set up BAT2 and if necessary BAT3 to cover RAM. */
 
diff --git a/arch/powerpc/mm/tlb_64.c b/arch/powerpc/mm/tlb_64.c
index b58baa6..fd8d08c 100644
--- a/arch/powerpc/mm/tlb_64.c
+++ b/arch/powerpc/mm/tlb_64.c
@@ -120,17 +120,20 @@
 }
 
 /*
- * Update the MMU hash table to correspond with a change to
- * a Linux PTE.  If wrprot is true, it is permissible to
- * change the existing HPTE to read-only rather than removing it
- * (if we remove it we should clear the _PTE_HPTEFLAGS bits).
+ * A linux PTE was changed and the corresponding hash table entry
+ * neesd to be flushed. This function will either perform the flush
+ * immediately or will batch it up if the current CPU has an active
+ * batch on it.
+ *
+ * Must be called from within some kind of spinlock/non-preempt region...
  */
-void hpte_update(struct mm_struct *mm, unsigned long addr,
-		 pte_t *ptep, unsigned long pte, int huge)
+void hpte_need_flush(struct mm_struct *mm, unsigned long addr,
+		     pte_t *ptep, unsigned long pte, int huge)
 {
 	struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch);
-	unsigned long vsid;
+	unsigned long vsid, vaddr;
 	unsigned int psize;
+	real_pte_t rpte;
 	int i;
 
 	i = batch->index;
@@ -151,6 +154,26 @@
 	} else
 		psize = pte_pagesize_index(pte);
 
+	/* Build full vaddr */
+	if (!is_kernel_addr(addr)) {
+		vsid = get_vsid(mm->context.id, addr);
+		WARN_ON(vsid == 0);
+	} else
+		vsid = get_kernel_vsid(addr);
+	vaddr = (vsid << 28 ) | (addr & 0x0fffffff);
+	rpte = __real_pte(__pte(pte), ptep);
+
+	/*
+	 * Check if we have an active batch on this CPU. If not, just
+	 * flush now and return. For now, we don global invalidates
+	 * in that case, might be worth testing the mm cpu mask though
+	 * and decide to use local invalidates instead...
+	 */
+	if (!batch->active) {
+		flush_hash_page(vaddr, rpte, psize, 0);
+		return;
+	}
+
 	/*
 	 * This can happen when we are in the middle of a TLB batch and
 	 * we encounter memory pressure (eg copy_page_range when it tries
@@ -162,47 +185,42 @@
 	 * batch
 	 */
 	if (i != 0 && (mm != batch->mm || batch->psize != psize)) {
-		flush_tlb_pending();
+		__flush_tlb_pending(batch);
 		i = 0;
 	}
 	if (i == 0) {
 		batch->mm = mm;
 		batch->psize = psize;
 	}
-	if (!is_kernel_addr(addr)) {
-		vsid = get_vsid(mm->context.id, addr);
-		WARN_ON(vsid == 0);
-	} else
-		vsid = get_kernel_vsid(addr);
-	batch->vaddr[i] = (vsid << 28 ) | (addr & 0x0fffffff);
-	batch->pte[i] = __real_pte(__pte(pte), ptep);
+	batch->pte[i] = rpte;
+	batch->vaddr[i] = vaddr;
 	batch->index = ++i;
 	if (i >= PPC64_TLB_BATCH_NR)
-		flush_tlb_pending();
+		__flush_tlb_pending(batch);
 }
 
+/*
+ * This function is called when terminating an mmu batch or when a batch
+ * is full. It will perform the flush of all the entries currently stored
+ * in a batch.
+ *
+ * Must be called from within some kind of spinlock/non-preempt region...
+ */
 void __flush_tlb_pending(struct ppc64_tlb_batch *batch)
 {
-	int i;
-	int cpu;
 	cpumask_t tmp;
-	int local = 0;
+	int i, local = 0;
 
-	BUG_ON(in_interrupt());
-
-	cpu = get_cpu();
 	i = batch->index;
-	tmp = cpumask_of_cpu(cpu);
+	tmp = cpumask_of_cpu(smp_processor_id());
 	if (cpus_equal(batch->mm->cpu_vm_mask, tmp))
 		local = 1;
-
 	if (i == 1)
 		flush_hash_page(batch->vaddr[0], batch->pte[0],
 				batch->psize, local);
 	else
 		flush_hash_range(i, local);
 	batch->index = 0;
-	put_cpu();
 }
 
 void pte_free_finish(void)
diff --git a/arch/powerpc/oprofile/Makefile b/arch/powerpc/oprofile/Makefile
index 4ccef2d..4b5f952 100644
--- a/arch/powerpc/oprofile/Makefile
+++ b/arch/powerpc/oprofile/Makefile
@@ -12,6 +12,6 @@
 
 oprofile-y := $(DRIVER_OBJS) common.o backtrace.o
 oprofile-$(CONFIG_PPC_CELL_NATIVE) += op_model_cell.o
-oprofile-$(CONFIG_PPC64) += op_model_rs64.o op_model_power4.o
+oprofile-$(CONFIG_PPC64) += op_model_rs64.o op_model_power4.o op_model_pa6t.o
 oprofile-$(CONFIG_FSL_BOOKE) += op_model_fsl_booke.o
 oprofile-$(CONFIG_6xx) += op_model_7450.o
diff --git a/arch/powerpc/oprofile/common.c b/arch/powerpc/oprofile/common.c
index fbd62ea..1a7ef7e 100644
--- a/arch/powerpc/oprofile/common.c
+++ b/arch/powerpc/oprofile/common.c
@@ -160,6 +160,9 @@
 		case PPC_OPROFILE_POWER4:
 			model = &op_model_power4;
 			break;
+		case PPC_OPROFILE_PA6T:
+			model = &op_model_pa6t;
+			break;
 #endif
 #ifdef CONFIG_6xx
 		case PPC_OPROFILE_G4:
diff --git a/arch/powerpc/oprofile/op_model_cell.c b/arch/powerpc/oprofile/op_model_cell.c
index e08e1d7..626b29f 100644
--- a/arch/powerpc/oprofile/op_model_cell.c
+++ b/arch/powerpc/oprofile/op_model_cell.c
@@ -37,6 +37,7 @@
 #include <asm/system.h>
 
 #include "../platforms/cell/interrupt.h"
+#include "../platforms/cell/cbe_regs.h"
 
 #define PPU_CYCLES_EVENT_NUM 1	/*  event number for CYCLES */
 #define PPU_CYCLES_GRP_NUM   1  /* special group number for identifying
@@ -130,7 +131,7 @@
 static u32 reset_value[NR_PHYS_CTRS];
 static int num_counters;
 static int oprofile_running;
-static spinlock_t virt_cntr_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(virt_cntr_lock);
 
 static u32 ctr_enabled;
 
diff --git a/arch/powerpc/oprofile/op_model_pa6t.c b/arch/powerpc/oprofile/op_model_pa6t.c
new file mode 100644
index 0000000..e8a56b0
--- /dev/null
+++ b/arch/powerpc/oprofile/op_model_pa6t.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2006-2007 PA Semi, Inc
+ *
+ * Author: Shashi Rao, PA Semi
+ *
+ * Maintained by: Olof Johansson <olof@lixom.net>
+ *
+ * Based on arch/powerpc/oprofile/op_model_power4.c
+ *
+ * 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
+ */
+
+#include <linux/oprofile.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/percpu.h>
+#include <asm/processor.h>
+#include <asm/cputable.h>
+#include <asm/oprofile_impl.h>
+#include <asm/reg.h>
+
+static unsigned char oprofile_running;
+
+/* mmcr values are set in pa6t_reg_setup, used in pa6t_cpu_setup */
+static u64 mmcr0_val;
+static u64 mmcr1_val;
+
+/* inited in pa6t_reg_setup */
+static u64 reset_value[OP_MAX_COUNTER];
+
+static inline u64 ctr_read(unsigned int i)
+{
+	switch (i) {
+	case 0:
+		return mfspr(SPRN_PA6T_PMC0);
+	case 1:
+		return mfspr(SPRN_PA6T_PMC1);
+	case 2:
+		return mfspr(SPRN_PA6T_PMC2);
+	case 3:
+		return mfspr(SPRN_PA6T_PMC3);
+	case 4:
+		return mfspr(SPRN_PA6T_PMC4);
+	case 5:
+		return mfspr(SPRN_PA6T_PMC5);
+	default:
+		printk(KERN_ERR "ctr_read called with bad arg %u\n", i);
+		return 0;
+	}
+}
+
+static inline void ctr_write(unsigned int i, u64 val)
+{
+	switch (i) {
+	case 0:
+		mtspr(SPRN_PA6T_PMC0, val);
+		break;
+	case 1:
+		mtspr(SPRN_PA6T_PMC1, val);
+		break;
+	case 2:
+		mtspr(SPRN_PA6T_PMC2, val);
+		break;
+	case 3:
+		mtspr(SPRN_PA6T_PMC3, val);
+		break;
+	case 4:
+		mtspr(SPRN_PA6T_PMC4, val);
+		break;
+	case 5:
+		mtspr(SPRN_PA6T_PMC5, val);
+		break;
+	default:
+		printk(KERN_ERR "ctr_write called with bad arg %u\n", i);
+		break;
+	}
+}
+
+
+/* precompute the values to stuff in the hardware registers */
+static void pa6t_reg_setup(struct op_counter_config *ctr,
+			   struct op_system_config *sys,
+			   int num_ctrs)
+{
+	int pmc;
+
+	/*
+	 * adjust the mmcr0.en[0-5] and mmcr0.inten[0-5] values obtained from the
+	 * event_mappings file by turning off the counters that the user doesn't
+	 * care about
+	 *
+	 * setup user and kernel profiling
+	 */
+	for (pmc = 0; pmc < cur_cpu_spec->num_pmcs; pmc++)
+		if (!ctr[pmc].enabled) {
+			sys->mmcr0 &= ~(0x1UL << pmc);
+			sys->mmcr0 &= ~(0x1UL << (pmc+12));
+			pr_debug("turned off counter %u\n", pmc);
+		}
+
+	if (sys->enable_kernel)
+		sys->mmcr0 |= PA6T_MMCR0_SUPEN | PA6T_MMCR0_HYPEN;
+	else
+		sys->mmcr0 &= ~(PA6T_MMCR0_SUPEN | PA6T_MMCR0_HYPEN);
+
+	if (sys->enable_user)
+		sys->mmcr0 |= PA6T_MMCR0_PREN;
+	else
+		sys->mmcr0 &= ~PA6T_MMCR0_PREN;
+
+	/*
+	 * The performance counter event settings are given in the mmcr0 and
+	 * mmcr1 values passed from the user in the op_system_config
+	 * structure (sys variable).
+	 */
+	mmcr0_val = sys->mmcr0;
+	mmcr1_val = sys->mmcr1;
+	pr_debug("mmcr0_val inited to %016lx\n", sys->mmcr0);
+	pr_debug("mmcr1_val inited to %016lx\n", sys->mmcr1);
+
+	for (pmc = 0; pmc < cur_cpu_spec->num_pmcs; pmc++) {
+		/* counters are 40 bit. Move to cputable at some point? */
+		reset_value[pmc] = (0x1UL << 39) - ctr[pmc].count;
+		pr_debug("reset_value for pmc%u inited to 0x%lx\n",
+				 pmc, reset_value[pmc]);
+	}
+}
+
+/* configure registers on this cpu */
+static void pa6t_cpu_setup(struct op_counter_config *ctr)
+{
+	u64 mmcr0 = mmcr0_val;
+	u64 mmcr1 = mmcr1_val;
+
+	/* Default is all PMCs off */
+	mmcr0 &= ~(0x3FUL);
+	mtspr(SPRN_PA6T_MMCR0, mmcr0);
+
+	/* program selected programmable events in */
+	mtspr(SPRN_PA6T_MMCR1, mmcr1);
+
+	pr_debug("setup on cpu %d, mmcr0 %016lx\n", smp_processor_id(),
+		mfspr(SPRN_PA6T_MMCR0));
+	pr_debug("setup on cpu %d, mmcr1 %016lx\n", smp_processor_id(),
+		mfspr(SPRN_PA6T_MMCR1));
+}
+
+static void pa6t_start(struct op_counter_config *ctr)
+{
+	int i;
+
+	/* Hold off event counting until rfid */
+	u64 mmcr0 = mmcr0_val | PA6T_MMCR0_HANDDIS;
+
+	for (i = 0; i < cur_cpu_spec->num_pmcs; i++)
+		if (ctr[i].enabled)
+			ctr_write(i, reset_value[i]);
+		else
+			ctr_write(i, 0UL);
+
+	mtspr(SPRN_PA6T_MMCR0, mmcr0);
+
+	oprofile_running = 1;
+
+	pr_debug("start on cpu %d, mmcr0 %lx\n", smp_processor_id(), mmcr0);
+}
+
+static void pa6t_stop(void)
+{
+	u64 mmcr0;
+
+	/* freeze counters */
+	mmcr0 = mfspr(SPRN_PA6T_MMCR0);
+	mmcr0 |= PA6T_MMCR0_FCM0;
+	mtspr(SPRN_PA6T_MMCR0, mmcr0);
+
+	oprofile_running = 0;
+
+	pr_debug("stop on cpu %d, mmcr0 %lx\n", smp_processor_id(), mmcr0);
+}
+
+/* handle the perfmon overflow vector */
+static void pa6t_handle_interrupt(struct pt_regs *regs,
+				  struct op_counter_config *ctr)
+{
+	unsigned long pc = mfspr(SPRN_PA6T_SIAR);
+	int is_kernel = is_kernel_addr(pc);
+	u64 val;
+	int i;
+	u64 mmcr0;
+
+	/* disable perfmon counting until rfid */
+	mmcr0 = mfspr(SPRN_PA6T_MMCR0);
+	mtspr(SPRN_PA6T_MMCR0, mmcr0 | PA6T_MMCR0_HANDDIS);
+
+	/* Record samples. We've got one global bit for whether a sample
+	 * was taken, so add it for any counter that triggered overflow.
+	 */
+	for (i = 0; i < cur_cpu_spec->num_pmcs; i++) {
+		val = ctr_read(i);
+		if (val & (0x1UL << 39)) { /* Overflow bit set */
+			if (oprofile_running && ctr[i].enabled) {
+				if (mmcr0 & PA6T_MMCR0_SIARLOG)
+					oprofile_add_ext_sample(pc, regs, i, is_kernel);
+				ctr_write(i, reset_value[i]);
+			} else {
+				ctr_write(i, 0UL);
+			}
+		}
+	}
+
+	/* Restore mmcr0 to a good known value since the PMI changes it */
+	mmcr0 = mmcr0_val | PA6T_MMCR0_HANDDIS;
+	mtspr(SPRN_PA6T_MMCR0, mmcr0);
+}
+
+struct op_powerpc_model op_model_pa6t = {
+	.reg_setup		= pa6t_reg_setup,
+	.cpu_setup		= pa6t_cpu_setup,
+	.start			= pa6t_start,
+	.stop			= pa6t_stop,
+	.handle_interrupt	= pa6t_handle_interrupt,
+};
diff --git a/arch/powerpc/platforms/4xx/Kconfig b/arch/powerpc/platforms/4xx/Kconfig
index 2f2a13e..ded357c 100644
--- a/arch/powerpc/platforms/4xx/Kconfig
+++ b/arch/powerpc/platforms/4xx/Kconfig
@@ -3,278 +3,206 @@
 	depends on 40x || 44x
 	default y
 
-config WANT_EARLY_SERIAL
+config BOOKE
 	bool
-	select SERIAL_8250
-	default n
-
-menu "AMCC 4xx options"
-	depends on 4xx
-
-choice
-	prompt "Machine Type"
-	depends on 40x
-	default WALNUT
-
-config BUBINGA
-	bool "Bubinga"
-	select WANT_EARLY_SERIAL
-	help
-	  This option enables support for the IBM 405EP evaluation board.
-
-config CPCI405
-	bool "CPCI405"
-	help
-	  This option enables support for the CPCI405 board.
-
-config EP405
-	bool "EP405/EP405PC"
-	help
-	  This option enables support for the EP405/EP405PC boards.
-
-config REDWOOD_5
-	bool "Redwood-5"
-	help
-	  This option enables support for the IBM STB04 evaluation board.
-
-config REDWOOD_6
-	bool "Redwood-6"
-	help
-	  This option enables support for the IBM STBx25xx evaluation board.
-
-config SYCAMORE
-	bool "Sycamore"
-	help
-	  This option enables support for the IBM PPC405GPr evaluation board.
-
-config WALNUT
-	bool "Walnut"
-	help
-	  This option enables support for the IBM PPC405GP evaluation board.
-
-config XILINX_ML300
-	bool "Xilinx-ML300"
-	help
-	  This option enables support for the Xilinx ML300 evaluation board.
-
-endchoice
-
-choice
-	prompt "Machine Type"
 	depends on 44x
-	default EBONY
+	default y
 
-config BAMBOO
-	bool "Bamboo"
-	select WANT_EARLY_SERIAL
-	help
-	  This option enables support for the IBM PPC440EP evaluation board.
+menu "AMCC 40x options"
+	depends on 40x
+
+#config BUBINGA
+#	bool "Bubinga"
+#	depends on 40x
+#	default n
+#	select 405EP
+#	help
+#	  This option enables support for the IBM 405EP evaluation board.
+
+#config CPCI405
+#	bool "CPCI405"
+#	depends on 40x
+#	default n
+#	select 405GP
+#	help
+#	  This option enables support for the CPCI405 board.
+
+#config EP405
+#	bool "EP405/EP405PC"
+#	depends on 40x
+#	default n
+#	select 405GP
+#	help
+#	  This option enables support for the EP405/EP405PC boards.
+
+#config EP405PC
+#	bool "EP405PC Support"
+#	depends on EP405
+#	default y
+#	help
+#	  This option enables support for the extra features of the EP405PC board.
+
+#config REDWOOD_5
+#	bool "Redwood-5"
+#	depends on 40x
+#	default n
+#	select STB03xxx
+#	help
+#	  This option enables support for the IBM STB04 evaluation board.
+
+#config REDWOOD_6
+#	bool "Redwood-6"
+#	depends on 40x
+#	default n
+#	select STB03xxx
+#	help
+#	  This option enables support for the IBM STBx25xx evaluation board.
+
+#config SYCAMORE
+#	bool "Sycamore"
+#	depends on 40x
+#	default n
+#	select 405GPR
+#	help
+#	  This option enables support for the IBM PPC405GPr evaluation board.
+
+#config WALNUT
+#	bool "Walnut"
+#	depends on 40x
+#	default y
+#	select 405GP
+#	help
+#	  This option enables support for the IBM PPC405GP evaluation board.
+
+#config XILINX_ML300
+#	bool "Xilinx-ML300"
+#	depends on 40x
+#	default y
+#	select VIRTEX_II_PRO
+#	help
+#	  This option enables support for the Xilinx ML300 evaluation board.
+
+endmenu
+
+# 40x specific CPU modules, selected based on the board above.
+config NP405H
+	bool
+	#depends on ASH
+
+# OAK doesn't exist but wanted to keep this around for any future 403GCX boards
+config 403GCX
+	bool
+	#depends on OAK
+	select IBM405_ERR51
+
+config 405GP
+	bool
+	select IBM405_ERR77
+	select IBM405_ERR51
+
+config 405EP
+	bool
+
+config 405GPR
+	bool
+
+config VIRTEX_II_PRO
+	bool
+	select IBM405_ERR77
+	select IBM405_ERR51
+
+config STB03xxx
+	bool
+	select IBM405_ERR77
+	select IBM405_ERR51
+
+# 40x errata/workaround config symbols, selected by the CPU models above
+
+# All 405-based cores up until the 405GPR and 405EP have this errata.
+config IBM405_ERR77
+	bool
+
+# All 40x-based cores, up until the 405GPR and 405EP have this errata.
+config IBM405_ERR51
+	bool
+
+menu "AMCC 44x options"
+	depends on 44x
+
+#config BAMBOO
+#	bool "Bamboo"
+#	depends on 44x
+#	default n
+#	select 440EP
+#	help
+#	  This option enables support for the IBM PPC440EP evaluation board.
 
 config EBONY
 	bool "Ebony"
-	select WANT_EARLY_SERIAL
+	depends on 44x
+	default y
+	select 440GP
 	help
 	  This option enables support for the IBM PPC440GP evaluation board.
 
-config LUAN
-	bool "Luan"
-	select WANT_EARLY_SERIAL
-	help
-	  This option enables support for the IBM PPC440SP evaluation board.
+#config LUAN
+#	bool "Luan"
+#	depends on 44x
+#	default n
+#	select 440SP
+#	help
+#	  This option enables support for the IBM PPC440SP evaluation board.
 
-config OCOTEA
-	bool "Ocotea"
-	select WANT_EARLY_SERIAL
-	help
-	  This option enables support for the IBM PPC440GX evaluation board.
+#config OCOTEA
+#	bool "Ocotea"
+#	depends on 44x
+#	default n
+#	select 440GX
+#	help
+#	  This option enables support for the IBM PPC440GX evaluation board.
 
-endchoice
+endmenu
 
-config EP405PC
-	bool "EP405PC Support"
-	depends on EP405
-
-
-# It's often necessary to know the specific 4xx processor type.
-# Fortunately, it is impled (so far) from the board type, so we
-# don't need to ask more redundant questions.
-config NP405H
-	bool
-	depends on ASH
-	default y
-
+# 44x specific CPU modules, selected based on the board above.
 config 440EP
 	bool
-	depends on BAMBOO
 	select PPC_FPU
-	default y
+	select IBM440EP_ERR42
 
 config 440GP
 	bool
-	depends on EBONY
-	default y
+	select IBM_NEW_EMAC_ZMII
 
 config 440GX
 	bool
-	depends on OCOTEA
-	default y
 
 config 440SP
 	bool
-	depends on LUAN
-	default y
-
-config 440
-	bool
-	depends on 440GP || 440SP || 440EP
-	default y
 
 config 440A
 	bool
 	depends on 440GX
 	default y
 
+# 44x errata/workaround config symbols, selected by the CPU models above
 config IBM440EP_ERR42
 	bool
-	depends on 440EP
-	default y
 
-# All 405-based cores up until the 405GPR and 405EP have this errata.
-config IBM405_ERR77
-	bool
-	depends on 40x && !403GCX && !405GPR && !405EP
-	default y
+#config XILINX_OCP
+#	bool
+#	depends on XILINX_ML300
+#	default y
 
-# All 40x-based cores, up until the 405GPR and 405EP have this errata.
-config IBM405_ERR51
-	bool
-	depends on 40x && !405GPR && !405EP
-	default y
+#config BIOS_FIXUP
+#	bool
+#	depends on BUBINGA || EP405 || SYCAMORE || WALNUT
+#	default y
 
-config BOOKE
-	bool
-	depends on 44x
-	default y
+#config PPC4xx_DMA
+#	bool "PPC4xx DMA controller support"
+#	depends on 4xx
 
-config IBM_OCP
-	bool
-	depends on ASH || BAMBOO || BUBINGA || CPCI405 || EBONY || EP405 || LUAN || OCOTEA || REDWOOD_5 || REDWOOD_6 || SYCAMORE || WALNUT
-	default y
-
-config XILINX_OCP
-	bool
-	depends on XILINX_ML300
-	default y
-
-config IBM_EMAC4
-	bool
-	depends on 440GX || 440SP
-	default y
-
-config BIOS_FIXUP
-	bool
-	depends on BUBINGA || EP405 || SYCAMORE || WALNUT
-	default y
-
-# OAK doesn't exist but wanted to keep this around for any future 403GCX boards
-config 403GCX
-	bool
-	depends on OAK
-	default y
-
-config 405EP
-	bool
-	depends on BUBINGA
-	default y
-
-config 405GP
-	bool
-	depends on CPCI405 || EP405 || WALNUT
-	default y
-
-config 405GPR
-	bool
-	depends on SYCAMORE
-	default y
-
-config VIRTEX_II_PRO
-	bool
-	depends on XILINX_ML300
-	default y
-
-config STB03xxx
-	bool
-	depends on REDWOOD_5 || REDWOOD_6
-	default y
-
-config EMBEDDEDBOOT
-	bool
-	depends on EP405 || XILINX_ML300
-	default y
-
-config IBM_OPENBIOS
-	bool
-	depends on ASH || BUBINGA || REDWOOD_5 || REDWOOD_6 || SYCAMORE || WALNUT
-	default y
-
-config PPC4xx_DMA
-	bool "PPC4xx DMA controller support"
-	depends on 4xx
-
-config PPC4xx_EDMA
-	bool
-	depends on !STB03xxx && PPC4xx_DMA
-	default y
-
-config PPC_GEN550
-	bool
-	depends on 4xx
-	default y
-
-choice
-	prompt "TTYS0 device and default console"
-	depends on 40x
-	default UART0_TTYS0
-
-config UART0_TTYS0
-	bool "UART0"
-
-config UART0_TTYS1
-	bool "UART1"
-
-endchoice
-
-config SERIAL_SICC
-	bool "SICC Serial port support"
-	depends on STB03xxx
-
-config UART1_DFLT_CONSOLE
-	bool
-	depends on SERIAL_SICC && UART0_TTYS1
-	default y
-
-config SERIAL_SICC_CONSOLE
-	bool
-	depends on SERIAL_SICC && UART0_TTYS1
-	default y
-endmenu
-
-
-menu "IBM 40x options"
-	depends on 40x
-
-config SERIAL_SICC
-	bool "SICC Serial port"
-	depends on STB03xxx
-
-config UART1_DFLT_CONSOLE
-	bool
-	depends on SERIAL_SICC && UART0_TTYS1
-	default y
-
-config SERIAL_SICC_CONSOLE
-	bool
-	depends on SERIAL_SICC && UART0_TTYS1
-	default y
-
-endmenu
+#config PPC4xx_EDMA
+#	bool
+#	depends on !STB03xxx && PPC4xx_DMA
+#	default y
diff --git a/arch/powerpc/platforms/52xx/Kconfig b/arch/powerpc/platforms/52xx/Kconfig
new file mode 100644
index 0000000..bc4aa4a
--- /dev/null
+++ b/arch/powerpc/platforms/52xx/Kconfig
@@ -0,0 +1,35 @@
+config PPC_MPC52xx
+	bool
+	default n
+
+config PPC_MPC5200
+	bool
+	select PPC_MPC52xx
+	default n
+
+config PPC_MPC5200_BUGFIX
+	bool "MPC5200 (L25R) bugfix support"
+	depends on PPC_MPC5200
+	default n
+	help
+	  Enable workarounds for original MPC5200 errata.  This is not required
+	  for MPC5200B based boards.
+
+	  It is safe to say 'Y' here
+
+config PPC_EFIKA
+	bool "bPlan Efika 5k2. MPC5200B based computer"
+	depends on PPC_MULTIPLATFORM && PPC32
+	select PPC_RTAS
+	select RTAS_PROC
+	select PPC_MPC52xx
+	select PPC_NATIVE
+	default n
+
+config PPC_LITE5200
+	bool "Freescale Lite5200 Eval Board"
+	depends on PPC_MULTIPLATFORM && PPC32
+	select PPC_MPC5200
+	default n
+
+
diff --git a/arch/powerpc/platforms/52xx/efika.c b/arch/powerpc/platforms/52xx/efika.c
index 8de0341..a6bba97 100644
--- a/arch/powerpc/platforms/52xx/efika.c
+++ b/arch/powerpc/platforms/52xx/efika.c
@@ -112,7 +112,7 @@
 		return;
 	}
 
-	bus_range = get_property(pcictrl, "bus-range", &len);
+	bus_range = of_get_property(pcictrl, "bus-range", &len);
 	if (bus_range == NULL || len < 2 * sizeof(int)) {
 		printk(KERN_WARNING EFIKA_PLATFORM_NAME
 		       ": Can't get bus-range for %s\n", pcictrl->full_name);
@@ -158,18 +158,17 @@
 static void efika_show_cpuinfo(struct seq_file *m)
 {
 	struct device_node *root;
-	const char *revision = NULL;
-	const char *codegendescription = NULL;
-	const char *codegenvendor = NULL;
+	const char *revision;
+	const char *codegendescription;
+	const char *codegenvendor;
 
 	root = of_find_node_by_path("/");
 	if (!root)
 		return;
 
-	revision = get_property(root, "revision", NULL);
-	codegendescription =
-		    get_property(root, "CODEGEN,description", NULL);
-	codegenvendor = get_property(root, "CODEGEN,vendor", NULL);
+	revision = of_get_property(root, "revision", NULL);
+	codegendescription = of_get_property(root, "CODEGEN,description", NULL);
+	codegenvendor = of_get_property(root, "CODEGEN,vendor", NULL);
 
 	if (codegendescription)
 		seq_printf(m, "machine\t\t: %s\n", codegendescription);
diff --git a/arch/powerpc/platforms/52xx/lite5200.c b/arch/powerpc/platforms/52xx/lite5200.c
index cc3b40d..8e2646a 100644
--- a/arch/powerpc/platforms/52xx/lite5200.c
+++ b/arch/powerpc/platforms/52xx/lite5200.c
@@ -94,8 +94,8 @@
 
 	np = of_find_node_by_type(NULL, "cpu");
 	if (np) {
-		unsigned int *fp =
-		    (int *)get_property(np, "clock-frequency", NULL);
+		const unsigned int *fp =
+			of_get_property(np, "clock-frequency", NULL);
 		if (fp != 0)
 			loops_per_jiffy = *fp / HZ;
 		else
@@ -108,9 +108,11 @@
 	lite5200_setup_cpu();	/* Platorm specific */
 
 #ifdef CONFIG_PCI
-	np = of_find_node_by_type(np, "pci");
-	if (np)
+	np = of_find_node_by_type(NULL, "pci");
+	if (np) {
 		mpc52xx_add_bridge(np);
+		of_node_put(np);
+	}
 #endif
 
 #ifdef CONFIG_BLK_DEV_INITRD
@@ -132,7 +134,7 @@
 	const char *model = NULL;
 
 	if (np)
-		model = get_property(np, "model", NULL);
+		model = of_get_property(np, "model", NULL);
 
 	seq_printf(m, "vendor\t\t:	Freescale Semiconductor\n");
 	seq_printf(m, "machine\t\t:	%s\n", model ? model : "unknown");
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_common.c b/arch/powerpc/platforms/52xx/mpc52xx_common.c
index ed0cb69..2dd415f 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_common.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_common.c
@@ -60,7 +60,7 @@
 
 	of_node_get(node);
 	while (node) {
-		p_ipb_freq = get_property(node, "bus-frequency", NULL);
+		p_ipb_freq = of_get_property(node, "bus-frequency", NULL);
 		if (p_ipb_freq)
 			break;
 
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pci.c b/arch/powerpc/platforms/52xx/mpc52xx_pci.c
index faf161b..34d34a2 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_pci.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_pci.c
@@ -370,7 +370,7 @@
 		return -EINVAL;
 	}
 
-	bus_range = get_property(node, "bus-range", &len);
+	bus_range = of_get_property(node, "bus-range", &len);
 	if (bus_range == NULL || len < 2 * sizeof(int)) {
 		printk(KERN_WARNING "Can't get %s bus-range, assume bus 0\n",
 		       node->full_name);
diff --git a/arch/powerpc/platforms/82xx/Kconfig b/arch/powerpc/platforms/82xx/Kconfig
index 47d841e..de7fce9 100644
--- a/arch/powerpc/platforms/82xx/Kconfig
+++ b/arch/powerpc/platforms/82xx/Kconfig
@@ -1,21 +1,36 @@
-menu "Platform support"
-       depends on PPC_82xx
-
 choice
-       prompt "Machine Type"
-       default MPC82xx_ADS
+	prompt "Machine Type"
+	depends on PPC_82xx
+	default MPC82xx_ADS
 
 config MPC82xx_ADS
-       bool "Freescale MPC82xx ADS"
-       select DEFAULT_UIMAGE
-       select PQ2ADS
-       select 8272
-       select 8260
-       select CPM2
-       select FSL_SOC
-       help
-         This option enables support for the MPC8272 ADS board
+	bool "Freescale MPC82xx ADS"
+	select DEFAULT_UIMAGE
+	select PQ2ADS
+	select 8272
+	select 8260
+	select FSL_SOC
+	help
+	This option enables support for the MPC8272 ADS board
 
 endchoice
 
-endmenu
+config PQ2ADS
+	bool
+	default n
+
+config 8260
+	bool
+	depends on 6xx
+	select CPM2
+	help
+	  The MPC8260 is a typical embedded CPU made by Freescale.  Selecting
+	  this option means that you wish to build a kernel for a machine with
+	  an 8260 class CPU.
+
+config 8272
+	bool
+	select 8260
+	help
+	  The MPC8272 CPM has a different internal dpram setup than other CPM2
+	  devices
diff --git a/arch/powerpc/platforms/82xx/mpc82xx.c b/arch/powerpc/platforms/82xx/mpc82xx.c
index 74e7892..cc9900d 100644
--- a/arch/powerpc/platforms/82xx/mpc82xx.c
+++ b/arch/powerpc/platforms/82xx/mpc82xx.c
@@ -55,17 +55,17 @@
 static int __init get_freq(char *name, unsigned long *val)
 {
 	struct device_node *cpu;
-	unsigned int *fp;
+	const unsigned int *fp;
 	int found = 0;
 
 	/* The cpu node should have timebase and clock frequency properties */
 	cpu = of_find_node_by_type(NULL, "cpu");
 
 	if (cpu) {
-		fp = (unsigned int *)get_property(cpu, name, NULL);
+		fp = of_get_property(cpu, name, NULL);
 		if (fp) {
 			found = 1;
-			*val = *fp++;
+			*val = *fp;
 		}
 
 		of_node_put(cpu);
diff --git a/arch/powerpc/platforms/82xx/mpc82xx_ads.c b/arch/powerpc/platforms/82xx/mpc82xx_ads.c
index 7334c1a..47cb09f 100644
--- a/arch/powerpc/platforms/82xx/mpc82xx_ads.c
+++ b/arch/powerpc/platforms/82xx/mpc82xx_ads.c
@@ -456,7 +456,7 @@
 		iounmap(immap);
 		return;
 	}
-	irq_map = get_property(np, "interrupt-map", &size);
+	irq_map = of_get_property(np, "interrupt-map", &size);
 	if ((!irq_map) || (size <= 7)) {
 		printk(KERN_INFO "No interrupt-map property of pci node\n");
 		iounmap(immap);
@@ -481,7 +481,7 @@
 	}
 	pci_pic_node = of_node_get(np);
 	/* PCI interrupt controller registers: status and mask */
-	regs = get_property(np, "reg", &size);
+	regs = of_get_property(np, "reg", &size);
 	if ((!regs) || (size <= 2)) {
 		printk(KERN_INFO "No reg property in pci pic node\n");
 		iounmap(immap);
@@ -521,20 +521,20 @@
 	struct pci_controller *hose;
 	struct resource r;
 	const int *bus_range;
-	const void *ptr;
+	const uint *ptr;
 
 	memset(&r, 0, sizeof(r));
 	if (of_address_to_resource(np, 0, &r)) {
 		printk(KERN_INFO "No PCI reg property in device tree\n");
 		return;
 	}
-	if (!(ptr = get_property(np, "clock-frequency", NULL))) {
+	if (!(ptr = of_get_property(np, "clock-frequency", NULL))) {
 		printk(KERN_INFO "No clock-frequency property in PCI node");
 		return;
 	}
-	pci_clk_frq = *(uint *) ptr;
+	pci_clk_frq = *ptr;
 	of_node_put(np);
-	bus_range = get_property(np, "bus-range", &len);
+	bus_range = of_get_property(np, "bus-range", &len);
 	if (bus_range == NULL || len < 2 * sizeof(int)) {
 		printk(KERN_WARNING "Can't get bus-range for %s, assume"
 		       " bus 0\n", np->full_name);
diff --git a/arch/powerpc/platforms/83xx/Kconfig b/arch/powerpc/platforms/83xx/Kconfig
index 713b31a..19cafdf 100644
--- a/arch/powerpc/platforms/83xx/Kconfig
+++ b/arch/powerpc/platforms/83xx/Kconfig
@@ -1,8 +1,6 @@
-menu "Platform support"
-	depends on PPC_83xx
-
 choice
 	prompt "Machine Type"
+	depends on PPC_83xx
 	default MPC834x_MDS
 
 config MPC8313_RDB
@@ -18,6 +16,13 @@
 	help
 	  This option enables support for the MPC832x MDS evaluation board.
 
+config MPC832x_RDB
+	bool "Freescale MPC832x RDB"
+	select DEFAULT_UIMAGE
+	select QUICC_ENGINE
+	help
+	  This option enables support for the MPC8323 RDB board.
+
 config MPC834x_MDS
 	bool "Freescale MPC834x MDS"
 	select DEFAULT_UIMAGE
@@ -57,7 +62,7 @@
 	bool
 	select PPC_UDBG_16550
 	select PPC_INDIRECT_PCI
-	default y if MPC832x_MDS
+	default y if MPC832x_MDS || MPC832x_RDB
 
 config MPC834x
 	bool
@@ -70,5 +75,3 @@
 	select PPC_UDBG_16550
 	select PPC_INDIRECT_PCI
 	default y if MPC836x_MDS
-
-endmenu
diff --git a/arch/powerpc/platforms/83xx/Makefile b/arch/powerpc/platforms/83xx/Makefile
index dfc970d..31a91b5 100644
--- a/arch/powerpc/platforms/83xx/Makefile
+++ b/arch/powerpc/platforms/83xx/Makefile
@@ -4,6 +4,7 @@
 obj-y				:= misc.o
 obj-$(CONFIG_PCI)		+= pci.o
 obj-$(CONFIG_MPC8313_RDB)	+= mpc8313_rdb.o
+obj-$(CONFIG_MPC832x_RDB)	+= mpc832x_rdb.o
 obj-$(CONFIG_MPC834x_MDS)	+= mpc834x_mds.o
 obj-$(CONFIG_MPC834x_ITX)	+= mpc834x_itx.o
 obj-$(CONFIG_MPC836x_MDS)	+= mpc836x_mds.o
diff --git a/arch/powerpc/platforms/83xx/mpc832x_mds.c b/arch/powerpc/platforms/83xx/mpc832x_mds.c
index 17e3a3c..fff09f5 100644
--- a/arch/powerpc/platforms/83xx/mpc832x_mds.c
+++ b/arch/powerpc/platforms/83xx/mpc832x_mds.c
@@ -41,7 +41,6 @@
 #include <asm/qe_ic.h>
 
 #include "mpc83xx.h"
-#include "mpc832x_mds.h"
 
 #undef DEBUG
 #ifdef DEBUG
diff --git a/arch/powerpc/platforms/83xx/mpc832x_mds.h b/arch/powerpc/platforms/83xx/mpc832x_mds.h
deleted file mode 100644
index a495889..0000000
--- a/arch/powerpc/platforms/83xx/mpc832x_mds.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved.
- *
- * Description:
- * MPC832x MDS board specific 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 of the  License, or (at your
- * option) any later version.
- *
- */
-
-#ifndef __MACH_MPC832x_MDS_H__
-#define __MACH_MPC832x_MDS_H__
-
-extern u8 *get_bcsr(void);
-
-#endif				/* __MACH_MPC832x_MDS_H__ */
diff --git a/arch/powerpc/platforms/83xx/mpc832x_rdb.c b/arch/powerpc/platforms/83xx/mpc832x_rdb.c
new file mode 100644
index 0000000..6b71e9f
--- /dev/null
+++ b/arch/powerpc/platforms/83xx/mpc832x_rdb.c
@@ -0,0 +1,138 @@
+/*
+ * arch/powerpc/platforms/83xx/mpc832x_rdb.c
+ *
+ * Copyright (C) Freescale Semiconductor, Inc. 2007. All rights reserved.
+ *
+ * Description:
+ * MPC832x RDB board specific routines.
+ * This file is based on mpc832x_mds.c and mpc8313_rdb.c
+ * Author: Michael Barkowski <michael.barkowski@freescale.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.
+ */
+
+#include <linux/pci.h>
+
+#include <asm/of_platform.h>
+#include <asm/time.h>
+#include <asm/ipic.h>
+#include <asm/udbg.h>
+#include <asm/qe.h>
+#include <asm/qe_ic.h>
+
+#include "mpc83xx.h"
+
+#undef DEBUG
+#ifdef DEBUG
+#define DBG(fmt...) udbg_printf(fmt)
+#else
+#define DBG(fmt...)
+#endif
+
+#ifndef CONFIG_PCI
+unsigned long isa_io_base = 0;
+unsigned long isa_mem_base = 0;
+#endif
+
+/* ************************************************************************
+ *
+ * Setup the architecture
+ *
+ */
+static void __init mpc832x_rdb_setup_arch(void)
+{
+	struct device_node *np;
+
+	if (ppc_md.progress)
+		ppc_md.progress("mpc832x_rdb_setup_arch()", 0);
+
+#ifdef CONFIG_PCI
+	for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;)
+		add_bridge(np);
+
+	ppc_md.pci_exclude_device = mpc83xx_exclude_device;
+#endif
+
+#ifdef CONFIG_QUICC_ENGINE
+	qe_reset();
+
+	if ((np = of_find_node_by_name(np, "par_io")) != NULL) {
+		par_io_init(np);
+		of_node_put(np);
+
+		for (np = NULL; (np = of_find_node_by_name(np, "ucc")) != NULL;)
+			par_io_of_config(np);
+	}
+#endif				/* CONFIG_QUICC_ENGINE */
+}
+
+static struct of_device_id mpc832x_ids[] = {
+	{ .type = "soc", },
+	{ .compatible = "soc", },
+	{ .type = "qe", },
+	{},
+};
+
+static int __init mpc832x_declare_of_platform_devices(void)
+{
+	if (!machine_is(mpc832x_rdb))
+		return 0;
+
+	/* Publish the QE devices */
+	of_platform_bus_probe(NULL, mpc832x_ids, NULL);
+
+	return 0;
+}
+device_initcall(mpc832x_declare_of_platform_devices);
+
+void __init mpc832x_rdb_init_IRQ(void)
+{
+
+	struct device_node *np;
+
+	np = of_find_node_by_type(NULL, "ipic");
+	if (!np)
+		return;
+
+	ipic_init(np, 0);
+
+	/* Initialize the default interrupt mapping priorities,
+	 * in case the boot rom changed something on us.
+	 */
+	ipic_set_default_priority();
+	of_node_put(np);
+
+#ifdef CONFIG_QUICC_ENGINE
+	np = of_find_node_by_type(NULL, "qeic");
+	if (!np)
+		return;
+
+	qe_ic_init(np, 0);
+	of_node_put(np);
+#endif				/* CONFIG_QUICC_ENGINE */
+}
+
+/*
+ * Called very early, MMU is off, device-tree isn't unflattened
+ */
+static int __init mpc832x_rdb_probe(void)
+{
+	unsigned long root = of_get_flat_dt_root();
+
+	return of_flat_dt_is_compatible(root, "MPC832xRDB");
+}
+
+define_machine(mpc832x_rdb) {
+	.name		= "MPC832x RDB",
+	.probe		= mpc832x_rdb_probe,
+	.setup_arch	= mpc832x_rdb_setup_arch,
+	.init_IRQ	= mpc832x_rdb_init_IRQ,
+	.get_irq	= ipic_get_irq,
+	.restart	= mpc83xx_restart,
+	.time_init	= mpc83xx_time_init,
+	.calibrate_decr	= generic_calibrate_decr,
+	.progress	= udbg_progress,
+};
diff --git a/arch/powerpc/platforms/83xx/mpc834x_itx.h b/arch/powerpc/platforms/83xx/mpc834x_itx.h
deleted file mode 100644
index 174ca4e..0000000
--- a/arch/powerpc/platforms/83xx/mpc834x_itx.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * arch/powerpc/platforms/83xx/mpc834x_itx.h
- *
- * MPC834X ITX common board definitions
- *
- * Maintainer: Kumar Gala <galak@kernel.crashing.org>
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of  the GNU General  Public License as published by the
- * Free Software Foundation;  either version 2 of the  License, or (at your
- * option) any later version.
- *
- */
-
-#ifndef __MACH_MPC83XX_ITX_H__
-#define __MACH_MPC83XX_ITX_H__
-
-#define PIRQA	MPC83xx_IRQ_EXT4
-#define PIRQB	MPC83xx_IRQ_EXT5
-#define PIRQC	MPC83xx_IRQ_EXT6
-#define PIRQD	MPC83xx_IRQ_EXT7
-
-#endif				/* __MACH_MPC83XX_ITX_H__ */
diff --git a/arch/powerpc/platforms/83xx/pci.c b/arch/powerpc/platforms/83xx/pci.c
index 9c36505..774457d0 100644
--- a/arch/powerpc/platforms/83xx/pci.c
+++ b/arch/powerpc/platforms/83xx/pci.c
@@ -60,7 +60,7 @@
 	has_address = (of_address_to_resource(dev, 0, &rsrc) == 0);
 
 	/* Get bus range if any */
-	bus_range = get_property(dev, "bus-range", &len);
+	bus_range = of_get_property(dev, "bus-range", &len);
 	if (bus_range == NULL || len < 2 * sizeof(int)) {
 		printk(KERN_WARNING "Can't get bus-range for %s, assume"
 		       " bus 0\n", dev->full_name);
diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig
index e764c0a..629926e 100644
--- a/arch/powerpc/platforms/85xx/Kconfig
+++ b/arch/powerpc/platforms/85xx/Kconfig
@@ -1,8 +1,6 @@
-menu "Platform support"
-	depends on PPC_85xx
-
 choice
 	prompt "Machine Type"
+	depends on PPC_85xx
 	default MPC8540_ADS
 
 config MPC8540_ADS
@@ -30,6 +28,12 @@
 	help
 	  This option enables support for the MPC85xx MDS board
 
+config MPC8544_DS
+	bool "Freescale MPC8544 DS"
+	select DEFAULT_UIMAGE
+	help
+	  This option enables support for the MPC8544 DS board
+
 endchoice
 
 config MPC8540
@@ -40,33 +44,15 @@
 
 config MPC8560
 	bool
-	select PPC_INDIRECT_PCI
+	select CPM2
 	default y if MPC8560_ADS
 
 config MPC85xx
 	bool
 	select PPC_UDBG_16550
 	select PPC_INDIRECT_PCI
+	select PPC_INDIRECT_PCI_BE
+	select MPIC
 	select SERIAL_8250_SHARE_IRQ if SERIAL_8250
-	default y if MPC8540_ADS || MPC85xx_CDS || MPC8560_ADS || MPC85xx_MDS
-
-config PPC_INDIRECT_PCI_BE
-	bool
-	depends on PPC_85xx
-	default y
-
-config MPIC
-	bool
-	default y
-
-config CPM2
-	bool
-	depends on MPC8560
-	default y
-	help
-	  The CPM2 (Communications Processor Module) is a coprocessor on
-	  embedded CPUs made by Motorola.  Selecting this option means that
-	  you wish to build a kernel for a machine with a CPM2 coprocessor
-	  on it.
-
-endmenu
+	default y if MPC8540_ADS || MPC85xx_CDS || MPC8560_ADS \
+		|| MPC85xx_MDS || MPC8544_DS
diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile
index 4e63917..4e02cbb 100644
--- a/arch/powerpc/platforms/85xx/Makefile
+++ b/arch/powerpc/platforms/85xx/Makefile
@@ -5,4 +5,5 @@
 obj-$(CONFIG_MPC8540_ADS) += mpc85xx_ads.o
 obj-$(CONFIG_MPC8560_ADS) += mpc85xx_ads.o
 obj-$(CONFIG_MPC85xx_CDS) += mpc85xx_cds.o
+obj-$(CONFIG_MPC8544_DS)  += mpc8544_ds.o
 obj-$(CONFIG_MPC85xx_MDS) += mpc85xx_mds.o
diff --git a/arch/powerpc/platforms/85xx/mpc8544_ds.c b/arch/powerpc/platforms/85xx/mpc8544_ds.c
new file mode 100644
index 0000000..2867f85
--- /dev/null
+++ b/arch/powerpc/platforms/85xx/mpc8544_ds.c
@@ -0,0 +1,144 @@
+/*
+ * MPC8544 DS Board Setup
+ *
+ * Author Xianghua Xiao (x.xiao@freescale.com)
+ * Copyright 2007 Freescale Semiconductor Inc.
+ *
+ * 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.
+ */
+
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/kdev_t.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+
+#include <asm/system.h>
+#include <asm/time.h>
+#include <asm/machdep.h>
+#include <asm/mpc85xx.h>
+#include <mm/mmu_decl.h>
+#include <asm/prom.h>
+#include <asm/udbg.h>
+#include <asm/mpic.h>
+#include <asm/i8259.h>
+
+#include <sysdev/fsl_soc.h>
+#include "mpc85xx.h"
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args)
+#else
+#define DBG(fmt, args...)
+#endif
+
+
+void __init mpc8544_ds_pic_init(void)
+{
+	struct mpic *mpic;
+	struct resource r;
+	struct device_node *np = NULL;
+#ifdef CONFIG_PPC_I8259
+	struct device_node *cascade_node = NULL;
+	int cascade_irq;
+#endif
+
+	np = of_find_node_by_type(np, "open-pic");
+
+	if (np == NULL) {
+		printk(KERN_ERR "Could not find open-pic node\n");
+		return;
+	}
+
+	if (of_address_to_resource(np, 0, &r)) {
+		printk(KERN_ERR "Failed to map mpic register space\n");
+		of_node_put(np);
+		return;
+	}
+
+	/* Alloc mpic structure and per isu has 16 INT entries. */
+	mpic = mpic_alloc(np, r.start,
+			  MPIC_PRIMARY | MPIC_WANTS_RESET | MPIC_BIG_ENDIAN,
+			  16, 64, " OPENPIC     ");
+	BUG_ON(mpic == NULL);
+
+	/*
+	 * 48 Internal Interrupts
+	 */
+	mpic_assign_isu(mpic, 0, r.start + 0x10200);
+	mpic_assign_isu(mpic, 1, r.start + 0x10400);
+	mpic_assign_isu(mpic, 2, r.start + 0x10600);
+
+	/*
+	 * 16 External interrupts
+	 */
+	mpic_assign_isu(mpic, 3, r.start + 0x10000);
+
+	mpic_init(mpic);
+
+#ifdef CONFIG_PPC_I8259
+	/* Initialize the i8259 controller */
+	for_each_node_by_type(np, "interrupt-controller")
+	    if (device_is_compatible(np, "chrp,iic")) {
+		cascade_node = np;
+		break;
+	}
+
+	if (cascade_node == NULL) {
+		printk(KERN_DEBUG "Could not find i8259 PIC\n");
+		return;
+	}
+
+	cascade_irq = irq_of_parse_and_map(cascade_node, 0);
+	if (cascade_irq == NO_IRQ) {
+		printk(KERN_ERR "Failed to map cascade interrupt\n");
+		return;
+	}
+
+	DBG("mpc8544ds: cascade mapped to irq %d\n", cascade_irq);
+
+	i8259_init(cascade_node, 0);
+	of_node_put(cascade_node);
+
+	set_irq_chained_handler(cascade_irq, mpc8544_8259_cascade);
+#endif	/* CONFIG_PPC_I8259 */
+}
+
+
+/*
+ * Setup the architecture
+ */
+static void __init mpc8544_ds_setup_arch(void)
+{
+	if (ppc_md.progress)
+		ppc_md.progress("mpc8544_ds_setup_arch()", 0);
+
+	printk("MPC8544 DS board from Freescale Semiconductor\n");
+}
+
+
+/*
+ * Called very early, device-tree isn't unflattened
+ */
+static int __init mpc8544_ds_probe(void)
+{
+	unsigned long root = of_get_flat_dt_root();
+
+	return of_flat_dt_is_compatible(root, "MPC8544DS");
+}
+
+define_machine(mpc8544_ds) {
+	.name			= "MPC8544 DS",
+	.probe			= mpc8544_ds_probe,
+	.setup_arch		= mpc8544_ds_setup_arch,
+	.init_IRQ		= mpc8544_ds_pic_init,
+	.get_irq		= mpic_get_irq,
+	.restart		= mpc85xx_restart,
+	.calibrate_decr		= generic_calibrate_decr,
+	.progress		= udbg_progress,
+};
diff --git a/arch/powerpc/platforms/85xx/mpc85xx_ads.c b/arch/powerpc/platforms/85xx/mpc85xx_ads.c
index 8ed034a..5d27621 100644
--- a/arch/powerpc/platforms/85xx/mpc85xx_ads.c
+++ b/arch/powerpc/platforms/85xx/mpc85xx_ads.c
@@ -227,7 +227,7 @@
 	if (cpu != 0) {
 		const unsigned int *fp;
 
-		fp = get_property(cpu, "clock-frequency", NULL);
+		fp = of_get_property(cpu, "clock-frequency", NULL);
 		if (fp != 0)
 			loops_per_jiffy = *fp / HZ;
 		else
diff --git a/arch/powerpc/platforms/85xx/mpc85xx_cds.c b/arch/powerpc/platforms/85xx/mpc85xx_cds.c
index 4232686..7e71636 100644
--- a/arch/powerpc/platforms/85xx/mpc85xx_cds.c
+++ b/arch/powerpc/platforms/85xx/mpc85xx_cds.c
@@ -237,7 +237,7 @@
 	if (cpu != 0) {
 		const unsigned int *fp;
 
-		fp = get_property(cpu, "clock-frequency", NULL);
+		fp = of_get_property(cpu, "clock-frequency", NULL);
 		if (fp != 0)
 			loops_per_jiffy = *fp / HZ;
 		else
diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c
index 81144d2..54db416 100644
--- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c
+++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c
@@ -80,7 +80,7 @@
 	np = of_find_node_by_type(NULL, "cpu");
 	if (np != NULL) {
 		const unsigned int *fp =
-		    get_property(np, "clock-frequency", NULL);
+		    of_get_property(np, "clock-frequency", NULL);
 		if (fp != NULL)
 			loops_per_jiffy = *fp / HZ;
 		else
diff --git a/arch/powerpc/platforms/85xx/pci.c b/arch/powerpc/platforms/85xx/pci.c
index 05930ee..48f17e2 100644
--- a/arch/powerpc/platforms/85xx/pci.c
+++ b/arch/powerpc/platforms/85xx/pci.c
@@ -51,7 +51,7 @@
 	has_address = (of_address_to_resource(dev, 0, &rsrc) == 0);
 
 	/* Get bus range if any */
-	bus_range = get_property(dev, "bus-range", &len);
+	bus_range = of_get_property(dev, "bus-range", &len);
 	if (bus_range == NULL || len < 2 * sizeof(int)) {
 		printk(KERN_WARNING "Can't get bus-range for %s, assume"
 		       " bus 0\n", dev->full_name);
diff --git a/arch/powerpc/platforms/86xx/Kconfig b/arch/powerpc/platforms/86xx/Kconfig
index 0c70944..d1bcff5 100644
--- a/arch/powerpc/platforms/86xx/Kconfig
+++ b/arch/powerpc/platforms/86xx/Kconfig
@@ -1,8 +1,6 @@
-menu "Platform Support"
-	depends on PPC_86xx
-
 choice
 	prompt "Machine Type"
+	depends on PPC_86xx
 	default MPC8641_HPCN
 
 config MPC8641_HPCN
@@ -14,20 +12,10 @@
 
 endchoice
 
-
 config MPC8641
 	bool
 	select PPC_INDIRECT_PCI
+	select PPC_INDIRECT_PCI_BE
 	select PPC_UDBG_16550
+	select MPIC
 	default y if MPC8641_HPCN
-
-config MPIC
-	bool
-	default y
-
-config PPC_INDIRECT_PCI_BE
-	bool
-	depends on PPC_86xx
-	default y
-
-endmenu
diff --git a/arch/powerpc/platforms/86xx/Makefile b/arch/powerpc/platforms/86xx/Makefile
index 476a6ee..418fd8f 100644
--- a/arch/powerpc/platforms/86xx/Makefile
+++ b/arch/powerpc/platforms/86xx/Makefile
@@ -4,4 +4,4 @@
 
 obj-$(CONFIG_SMP)		+= mpc86xx_smp.o
 obj-$(CONFIG_MPC8641_HPCN)	+= mpc86xx_hpcn.o
-obj-$(CONFIG_PCI)		+= pci.o mpc86xx_pcie.o
+obj-$(CONFIG_PCI)		+= pci.o
diff --git a/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c b/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c
index f42f801..3d3d98f 100644
--- a/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c
+++ b/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c
@@ -349,7 +349,7 @@
 	if (np != 0) {
 		const unsigned int *fp;
 
-		fp = get_property(np, "clock-frequency", NULL);
+		fp = of_get_property(np, "clock-frequency", NULL);
 		if (fp != 0)
 			loops_per_jiffy = *fp / HZ;
 		else
@@ -384,7 +384,7 @@
 
 	root = of_find_node_by_path("/");
 	if (root)
-		model = get_property(root, "model", NULL);
+		model = of_get_property(root, "model", NULL);
 	seq_printf(m, "Machine\t\t: %s\n", model);
 	of_node_put(root);
 
diff --git a/arch/powerpc/platforms/86xx/pci.c b/arch/powerpc/platforms/86xx/pci.c
index 481e18e..8235c56 100644
--- a/arch/powerpc/platforms/86xx/pci.c
+++ b/arch/powerpc/platforms/86xx/pci.c
@@ -22,9 +22,9 @@
 #include <asm/atomic.h>
 #include <asm/io.h>
 #include <asm/prom.h>
-#include <asm/immap_86xx.h>
 #include <asm/pci-bridge.h>
 #include <sysdev/fsl_soc.h>
+#include <sysdev/fsl_pcie.h>
 
 #include "mpc86xx.h"
 
@@ -163,7 +163,7 @@
 	has_address = (of_address_to_resource(dev, 0, &rsrc) == 0);
 
 	/* Get bus range if any */
-	bus_range = get_property(dev, "bus-range", &len);
+	bus_range = of_get_property(dev, "bus-range", &len);
 	if (bus_range == NULL || len < 2 * sizeof(int))
 		printk(KERN_WARNING "Can't get bus-range for %s, assume"
 		       " bus 0\n", dev->full_name);
diff --git a/arch/powerpc/platforms/8xx/Kconfig b/arch/powerpc/platforms/8xx/Kconfig
index beea683..39bb8c5 100644
--- a/arch/powerpc/platforms/8xx/Kconfig
+++ b/arch/powerpc/platforms/8xx/Kconfig
@@ -1,6 +1,3 @@
-menu "Platform support"
-        depends on PPC_8xx
-
 config FADS
 	bool
 
@@ -9,6 +6,7 @@
 
 choice
 	prompt "8xx Machine Type"
+	depends on PPC_8xx
 	depends on 8xx
 	default MPC885ADS
 
@@ -36,38 +34,36 @@
 endchoice
 
 menu "Freescale Ethernet driver platform-specific options"
-        depends on (FS_ENET && MPC885ADS)
+	depends on (FS_ENET && MPC885ADS)
 
-        config MPC8xx_SECOND_ETH
-        bool "Second Ethernet channel"
-        depends on MPC885ADS
-        default y
-        help
-          This enables support for second Ethernet on MPC885ADS and MPC86xADS boards.
-          The latter will use SCC1, for 885ADS you can select it below.
+	config MPC8xx_SECOND_ETH
+	bool "Second Ethernet channel"
+	depends on MPC885ADS
+	default y
+	help
+	  This enables support for second Ethernet on MPC885ADS and MPC86xADS boards.
+	  The latter will use SCC1, for 885ADS you can select it below.
 
-        choice
-                prompt "Second Ethernet channel"
-                depends on MPC8xx_SECOND_ETH
-                default MPC8xx_SECOND_ETH_FEC2
+	choice
+		prompt "Second Ethernet channel"
+		depends on MPC8xx_SECOND_ETH
+		default MPC8xx_SECOND_ETH_FEC2
 
-                config MPC8xx_SECOND_ETH_FEC2
-                bool "FEC2"
-                depends on MPC885ADS
-                help
-                  Enable FEC2 to serve as 2-nd Ethernet channel. Note that SMC2
-                  (often 2-nd UART) will not work if this is enabled.
+		config MPC8xx_SECOND_ETH_FEC2
+		bool "FEC2"
+		depends on MPC885ADS
+		help
+		  Enable FEC2 to serve as 2-nd Ethernet channel. Note that SMC2
+		  (often 2-nd UART) will not work if this is enabled.
 
-                config MPC8xx_SECOND_ETH_SCC3
-                bool "SCC3"
-                depends on MPC885ADS
-                help
-                  Enable SCC3 to serve as 2-nd Ethernet channel. Note that SMC1
-                  (often 1-nd UART) will not work if this is enabled.
+		config MPC8xx_SECOND_ETH_SCC3
+		bool "SCC3"
+		depends on MPC885ADS
+		help
+		  Enable SCC3 to serve as 2-nd Ethernet channel. Note that SMC1
+		  (often 1-nd UART) will not work if this is enabled.
 
-        endchoice
-
-endmenu
+	endchoice
 
 endmenu
 
@@ -98,7 +94,7 @@
 	  require workarounds for Linux (and most other OSes to work).  If you
 	  get a BUG() very early in boot, this might fix the problem.  For
 	  more details read the document entitled "MPC860 Family Device Errata
-	  Reference" on Motorola's website.  This option also incurs a
+	  Reference" on Freescale's website.  This option also incurs a
 	  performance hit.
 
 	  If in doubt, say N here.
@@ -135,4 +131,3 @@
 	depends on !NO_UCODE_PATCH
 
 endmenu
-
diff --git a/arch/powerpc/platforms/8xx/m8xx_setup.c b/arch/powerpc/platforms/8xx/m8xx_setup.c
index 9ed7125..0901dba 100644
--- a/arch/powerpc/platforms/8xx/m8xx_setup.c
+++ b/arch/powerpc/platforms/8xx/m8xx_setup.c
@@ -85,17 +85,17 @@
 static int __init get_freq(char *name, unsigned long *val)
 {
         struct device_node *cpu;
-        unsigned int *fp;
+        const unsigned int *fp;
         int found = 0;
 
         /* The cpu node should have timebase and clock frequency properties */
         cpu = of_find_node_by_type(NULL, "cpu");
 
         if (cpu) {
-                fp = (unsigned int *)get_property(cpu, name, NULL);
+                fp = of_get_property(cpu, name, NULL);
                 if (fp) {
                         found = 1;
-                        *val = *fp++;
+                        *val = *fp;
                 }
 
                 of_node_put(cpu);
@@ -262,7 +262,7 @@
 
 	root = of_find_node_by_path("/");
 	if (root)
-		model = get_property(root, "model", NULL);
+		model = of_get_property(root, "model", NULL);
 	seq_printf(m, "Machine\t\t: %s\n", model);
 	of_node_put(root);
 
diff --git a/arch/powerpc/platforms/8xx/mpc86xads.h b/arch/powerpc/platforms/8xx/mpc86xads.h
index b5d19dd..59bad2f 100644
--- a/arch/powerpc/platforms/8xx/mpc86xads.h
+++ b/arch/powerpc/platforms/8xx/mpc86xads.h
@@ -37,7 +37,7 @@
 #define CPM_MAP_ADDR		(get_immrbase() + MPC8xx_CPM_OFFSET)
 #define CPM_IRQ_OFFSET		16     // for compability with cpm_uart driver
 
-#define PCMCIA_MEM_ADDR		(uint)0xff020000)
+#define PCMCIA_MEM_ADDR		((uint)0xff020000)
 #define PCMCIA_MEM_SIZE		((uint)(64 * 1024))
 
 /* Bits of interest in the BCSRs.
diff --git a/arch/powerpc/platforms/8xx/mpc86xads_setup.c b/arch/powerpc/platforms/8xx/mpc86xads_setup.c
index ef52ce7..a35315a 100644
--- a/arch/powerpc/platforms/8xx/mpc86xads_setup.c
+++ b/arch/powerpc/platforms/8xx/mpc86xads_setup.c
@@ -247,7 +247,7 @@
 	}
 }
 
-int platform_device_skip(char *model, int id)
+int platform_device_skip(const char *model, int id)
 {
 	return 0;
 }
@@ -260,7 +260,7 @@
 	if (cpu != 0) {
 		const unsigned int *fp;
 
-		fp = get_property(cpu, "clock-frequency", NULL);
+		fp = of_get_property(cpu, "clock-frequency", NULL);
 		if (fp != 0)
 			loops_per_jiffy = *fp / HZ;
 		else
diff --git a/arch/powerpc/platforms/8xx/mpc885ads.h b/arch/powerpc/platforms/8xx/mpc885ads.h
index 30cbebf..7c31aec 100644
--- a/arch/powerpc/platforms/8xx/mpc885ads.h
+++ b/arch/powerpc/platforms/8xx/mpc885ads.h
@@ -37,7 +37,7 @@
 #define CPM_MAP_ADDR		(get_immrbase() + MPC8xx_CPM_OFFSET)
 #define CPM_IRQ_OFFSET		16     // for compability with cpm_uart driver
 
-#define PCMCIA_MEM_ADDR		(uint)0xff020000)
+#define PCMCIA_MEM_ADDR		((uint)0xff020000)
 #define PCMCIA_MEM_SIZE		((uint)(64 * 1024))
 
 /* Bits of interest in the BCSRs.
diff --git a/arch/powerpc/platforms/8xx/mpc885ads_setup.c b/arch/powerpc/platforms/8xx/mpc885ads_setup.c
index c5fefdf6..a57b577 100644
--- a/arch/powerpc/platforms/8xx/mpc885ads_setup.c
+++ b/arch/powerpc/platforms/8xx/mpc885ads_setup.c
@@ -322,7 +322,7 @@
 	}
 }
 
-int platform_device_skip(char *model, int id)
+int platform_device_skip(const char *model, int id)
 {
 #ifdef CONFIG_MPC8xx_SECOND_ETH_SCC3
 	const char *dev = "FEC";
@@ -346,7 +346,7 @@
 	if (cpu != 0) {
 		const unsigned int *fp;
 
-		fp = get_property(cpu, "clock-frequency", NULL);
+		fp = of_get_property(cpu, "clock-frequency", NULL);
 		if (fp != 0)
 			loops_per_jiffy = *fp / HZ;
 		else
diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
new file mode 100644
index 0000000..51e3334
--- /dev/null
+++ b/arch/powerpc/platforms/Kconfig
@@ -0,0 +1,259 @@
+menu "Platform support"
+
+choice
+	prompt "Machine type"
+	depends on PPC64 || CLASSIC32
+	default PPC_MULTIPLATFORM
+
+config PPC_MULTIPLATFORM
+	bool "Generic desktop/server/laptop"
+	help
+	  Select this option if configuring for an IBM pSeries or
+	  RS/6000 machine, an Apple machine, or a PReP, CHRP,
+	  Maple or Cell-based machine.
+
+config EMBEDDED6xx
+	bool "Embedded 6xx/7xx/7xxx-based board"
+	depends on PPC32 && (BROKEN||BROKEN_ON_SMP)
+
+config APUS
+	bool "Amiga-APUS"
+	depends on PPC32 && BROKEN
+	help
+	  Select APUS if configuring for a PowerUP Amiga.
+	  More information is available at:
+	  <http://linux-apus.sourceforge.net/>.
+endchoice
+
+source "arch/powerpc/platforms/pseries/Kconfig"
+source "arch/powerpc/platforms/iseries/Kconfig"
+source "arch/powerpc/platforms/chrp/Kconfig"
+source "arch/powerpc/platforms/52xx/Kconfig"
+source "arch/powerpc/platforms/powermac/Kconfig"
+source "arch/powerpc/platforms/prep/Kconfig"
+source "arch/powerpc/platforms/maple/Kconfig"
+source "arch/powerpc/platforms/pasemi/Kconfig"
+source "arch/powerpc/platforms/celleb/Kconfig"
+source "arch/powerpc/platforms/ps3/Kconfig"
+source "arch/powerpc/platforms/cell/Kconfig"
+source "arch/powerpc/platforms/8xx/Kconfig"
+source "arch/powerpc/platforms/82xx/Kconfig"
+source "arch/powerpc/platforms/83xx/Kconfig"
+source "arch/powerpc/platforms/85xx/Kconfig"
+source "arch/powerpc/platforms/86xx/Kconfig"
+source "arch/powerpc/platforms/embedded6xx/Kconfig"
+#source "arch/powerpc/platforms/4xx/Kconfig
+
+config PPC_NATIVE
+	bool
+	depends on PPC_MULTIPLATFORM
+	help
+	  Support for running natively on the hardware, i.e. without
+	  a hypervisor. This option is not user-selectable but should
+	  be selected by all platforms that need it.
+
+config UDBG_RTAS_CONSOLE
+	bool "RTAS based debug console"
+	depends on PPC_RTAS
+	default n
+
+config PPC_UDBG_BEAT
+	bool "BEAT based debug console"
+	depends on PPC_CELLEB
+	default n
+
+config XICS
+	depends on PPC_PSERIES
+	bool
+	default y
+
+config MPIC
+	bool
+	default n
+
+config MPIC_WEIRD
+	bool
+	default n
+
+config PPC_I8259
+	bool
+	default n
+
+config U3_DART
+	bool
+	depends on PPC_MULTIPLATFORM && PPC64
+	default n
+
+config PPC_RTAS
+	bool
+	default n
+
+config RTAS_ERROR_LOGGING
+	bool
+	depends on PPC_RTAS
+	default n
+
+config RTAS_PROC
+	bool "Proc interface to RTAS"
+	depends on PPC_RTAS
+	default y
+
+config RTAS_FLASH
+	tristate "Firmware flash interface"
+	depends on PPC64 && RTAS_PROC
+
+config PPC_PMI
+	tristate "Support for PMI"
+	depends PPC_IBM_CELL_BLADE
+	help
+	  PMI (Platform Management Interrupt) is a way to
+	  communicate with the BMC (Baseboard Mangement Controller).
+	  It is used in some IBM Cell blades.
+	default m
+
+config MMIO_NVRAM
+	bool
+	default n
+
+config MPIC_U3_HT_IRQS
+	bool
+	depends on PPC_MAPLE
+	default y
+
+config IBMVIO
+	depends on PPC_PSERIES || PPC_ISERIES
+	bool
+	default y
+
+config IBMEBUS
+	depends on PPC_PSERIES
+	bool "Support for GX bus based adapters"
+	help
+	  Bus device driver for GX bus based adapters.
+
+config PPC_MPC106
+	bool
+	default n
+
+config PPC_970_NAP
+	bool
+	default n
+
+config PPC_INDIRECT_IO
+	bool
+	select GENERIC_IOMAP
+	default n
+
+config GENERIC_IOMAP
+	bool
+	default n
+
+source "drivers/cpufreq/Kconfig"
+
+menu "CPU Frequency drivers"
+	depends on CPU_FREQ
+
+config CPU_FREQ_PMAC
+	bool "Support for Apple PowerBooks"
+	depends on ADB_PMU && PPC32
+	select CPU_FREQ_TABLE
+	help
+	  This adds support for frequency switching on Apple PowerBooks,
+	  this currently includes some models of iBook & Titanium
+	  PowerBook.
+
+config CPU_FREQ_PMAC64
+	bool "Support for some Apple G5s"
+	depends on PPC_PMAC && PPC64
+	select CPU_FREQ_TABLE
+	help
+	  This adds support for frequency switching on Apple iMac G5,
+	  and some of the more recent desktop G5 machines as well.
+
+config PPC_PASEMI_CPUFREQ
+	bool "Support for PA Semi PWRficient"
+	depends on PPC_PASEMI
+	default y
+	select CPU_FREQ_TABLE
+	help
+	  This adds the support for frequency switching on PA Semi
+	  PWRficient processors.
+
+endmenu
+
+config PPC601_SYNC_FIX
+	bool "Workarounds for PPC601 bugs"
+	depends on 6xx && (PPC_PREP || PPC_PMAC)
+	help
+	  Some versions of the PPC601 (the first PowerPC chip) have bugs which
+	  mean that extra synchronization instructions are required near
+	  certain instructions, typically those that make major changes to the
+	  CPU state.  These extra instructions reduce performance slightly.
+	  If you say N here, these extra instructions will not be included,
+	  resulting in a kernel which will run faster but may not run at all
+	  on some systems with the PPC601 chip.
+
+	  If in doubt, say Y here.
+
+config TAU
+	bool "On-chip CPU temperature sensor support"
+	depends on CLASSIC32
+	help
+	  G3 and G4 processors have an on-chip temperature sensor called the
+	  'Thermal Assist Unit (TAU)', which, in theory, can measure the on-die
+	  temperature within 2-4 degrees Celsius. This option shows the current
+	  on-die temperature in /proc/cpuinfo if the cpu supports it.
+
+	  Unfortunately, on some chip revisions, this sensor is very inaccurate
+	  and in many cases, does not work at all, so don't assume the cpu
+	  temp is actually what /proc/cpuinfo says it is.
+
+config TAU_INT
+	bool "Interrupt driven TAU driver (DANGEROUS)"
+	depends on TAU
+	---help---
+	  The TAU supports an interrupt driven mode which causes an interrupt
+	  whenever the temperature goes out of range. This is the fastest way
+	  to get notified the temp has exceeded a range. With this option off,
+	  a timer is used to re-check the temperature periodically.
+
+	  However, on some cpus it appears that the TAU interrupt hardware
+	  is buggy and can cause a situation which would lead unexplained hard
+	  lockups.
+
+	  Unless you are extending the TAU driver, or enjoy kernel/hardware
+	  debugging, leave this option off.
+
+config TAU_AVERAGE
+	bool "Average high and low temp"
+	depends on TAU
+	---help---
+	  The TAU hardware can compare the temperature to an upper and lower
+	  bound.  The default behavior is to show both the upper and lower
+	  bound in /proc/cpuinfo. If the range is large, the temperature is
+	  either changing a lot, or the TAU hardware is broken (likely on some
+	  G4's). If the range is small (around 4 degrees), the temperature is
+	  relatively stable.  If you say Y here, a single temperature value,
+	  halfway between the upper and lower bounds, will be reported in
+	  /proc/cpuinfo.
+
+	  If in doubt, say N here.
+
+config QUICC_ENGINE
+	bool
+	help
+	  The QUICC Engine (QE) is a new generation of communications
+	  coprocessors on Freescale embedded CPUs (akin to CPM in older chips).
+	  Selecting this option means that you wish to build a kernel
+	  for a machine with a QE coprocessor.
+
+config CPM2
+	bool
+	default n
+	help
+	  The CPM2 (Communications Processor Module) is a coprocessor on
+	  embedded CPUs made by Freescale.  Selecting this option means that
+	  you wish to build a kernel for a machine with a CPM2 coprocessor
+	  on it (826x, 827x, 8560).
+
+endmenu
diff --git a/arch/powerpc/platforms/cell/Kconfig b/arch/powerpc/platforms/cell/Kconfig
index 06a85b7..8255177 100644
--- a/arch/powerpc/platforms/cell/Kconfig
+++ b/arch/powerpc/platforms/cell/Kconfig
@@ -1,3 +1,26 @@
+config PPC_CELL
+	bool
+	default n
+
+config PPC_CELL_NATIVE
+	bool
+	select PPC_CELL
+	select PPC_DCR_MMIO
+	select PPC_OF_PLATFORM_PCI
+	select PPC_INDIRECT_IO
+	select PPC_NATIVE
+	select MPIC
+	default n
+
+config PPC_IBM_CELL_BLADE
+	bool "IBM Cell Blade"
+	depends on PPC_MULTIPLATFORM && PPC64
+	select PPC_CELL_NATIVE
+	select PPC_RTAS
+	select MMIO_NVRAM
+	select PPC_UDBG_16550
+	select UDBG_RTAS_CONSOLE
+
 menu "Cell Broadband Engine options"
 	depends on PPC_CELL
 
@@ -18,6 +41,7 @@
 
 config CBE_RAS
 	bool "RAS features for bare metal Cell BE"
+	depends on PPC_CELL_NATIVE
 	default y
 
 config CBE_THERM
diff --git a/arch/powerpc/platforms/cell/cbe_cpufreq.c b/arch/powerpc/platforms/cell/cbe_cpufreq.c
index a3850fd..f9ac3fe 100644
--- a/arch/powerpc/platforms/cell/cbe_cpufreq.c
+++ b/arch/powerpc/platforms/cell/cbe_cpufreq.c
@@ -25,9 +25,12 @@
 
 #include <asm/hw_irq.h>
 #include <asm/io.h>
+#include <asm/machdep.h>
 #include <asm/processor.h>
 #include <asm/prom.h>
 #include <asm/time.h>
+#include <asm/pmi.h>
+#include <asm/of_platform.h>
 
 #include "cbe_regs.h"
 
@@ -68,6 +71,38 @@
  * hardware specific functions
  */
 
+static struct of_device *pmi_dev;
+
+static int set_pmode_pmi(int cpu, unsigned int pmode)
+{
+	int ret;
+	pmi_message_t pmi_msg;
+#ifdef DEBUG
+	u64 time;
+#endif
+
+	pmi_msg.type = PMI_TYPE_FREQ_CHANGE;
+	pmi_msg.data1 =	cbe_cpu_to_node(cpu);
+	pmi_msg.data2 = pmode;
+
+#ifdef DEBUG
+	time = (u64) get_cycles();
+#endif
+
+	pmi_send_message(pmi_dev, pmi_msg);
+	ret = pmi_msg.data2;
+
+	pr_debug("PMI returned slow mode %d\n", ret);
+
+#ifdef DEBUG
+	time = (u64) get_cycles() - time; /* actual cycles (not cpu cycles!) */
+	time = 1000000000 * time / CLOCK_TICK_RATE; /* time in ns (10^-9) */
+	pr_debug("had to wait %lu ns for a transition\n", time);
+#endif
+	return ret;
+}
+
+
 static int get_pmode(int cpu)
 {
 	int ret;
@@ -79,7 +114,7 @@
 	return ret;
 }
 
-static int set_pmode(int cpu, unsigned int pmode)
+static int set_pmode_reg(int cpu, unsigned int pmode)
 {
 	struct cbe_pmd_regs __iomem *pmd_regs;
 	struct cbe_mic_tm_regs __iomem *mic_tm_regs;
@@ -120,37 +155,71 @@
 	return 0;
 }
 
+static int set_pmode(int cpu, unsigned int slow_mode) {
+	if (pmi_dev)
+		return set_pmode_pmi(cpu, slow_mode);
+	else
+		return set_pmode_reg(cpu, slow_mode);
+}
+
+static void cbe_cpufreq_handle_pmi(struct of_device *dev, pmi_message_t pmi_msg)
+{
+	struct cpufreq_policy policy;
+	u8 cpu;
+	u8 cbe_pmode_new;
+
+	BUG_ON(pmi_msg.type != PMI_TYPE_FREQ_CHANGE);
+
+	cpu = cbe_node_to_cpu(pmi_msg.data1);
+	cbe_pmode_new = pmi_msg.data2;
+
+	cpufreq_get_policy(&policy, cpu);
+
+	policy.max = min(policy.max, cbe_freqs[cbe_pmode_new].frequency);
+	policy.min = min(policy.min, policy.max);
+
+	pr_debug("cbe_handle_pmi: new policy.min=%d policy.max=%d\n", policy.min, policy.max);
+	cpufreq_set_policy(&policy);
+}
+
+static struct pmi_handler cbe_pmi_handler = {
+	.type			= PMI_TYPE_FREQ_CHANGE,
+	.handle_pmi_message	= cbe_cpufreq_handle_pmi,
+};
+
+
 /*
  * cpufreq functions
  */
 
-static int cbe_cpufreq_cpu_init (struct cpufreq_policy *policy)
+static int cbe_cpufreq_cpu_init(struct cpufreq_policy *policy)
 {
-	u32 *max_freq;
+	const u32 *max_freqp;
+	u32 max_freq;
 	int i, cur_pmode;
 	struct device_node *cpu;
 
 	cpu = of_get_cpu_node(policy->cpu, NULL);
 
-	if(!cpu)
+	if (!cpu)
 		return -ENODEV;
 
 	pr_debug("init cpufreq on CPU %d\n", policy->cpu);
 
-	max_freq = (u32*) get_property(cpu, "clock-frequency", NULL);
+	max_freqp = of_get_property(cpu, "clock-frequency", NULL);
 
-	if(!max_freq)
+	if (!max_freqp)
 		return -EINVAL;
 
-	// we need the freq in kHz
-	*max_freq /= 1000;
+	/* we need the freq in kHz */
+	max_freq = *max_freqp / 1000;
 
-	pr_debug("max clock-frequency is at %u kHz\n", *max_freq);
+	pr_debug("max clock-frequency is at %u kHz\n", max_freq);
 	pr_debug("initializing frequency table\n");
 
-	// initialize frequency table
+	/* initialize frequency table */
 	for (i=0; cbe_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) {
-		cbe_freqs[i].frequency = *max_freq / cbe_freqs[i].index;
+		cbe_freqs[i].frequency = max_freq / cbe_freqs[i].index;
 		pr_debug("%d: %d\n", i, cbe_freqs[i].frequency);
 	}
 
@@ -167,10 +236,10 @@
 	policy->cpus = cpu_sibling_map[policy->cpu];
 #endif
 
-	cpufreq_frequency_table_get_attr (cbe_freqs, policy->cpu);
+	cpufreq_frequency_table_get_attr(cbe_freqs, policy->cpu);
 
 	/* this ensures that policy->cpuinfo_min and policy->cpuinfo_max are set correctly */
-	return cpufreq_frequency_table_cpuinfo (policy, cbe_freqs);
+	return cpufreq_frequency_table_cpuinfo(policy, cbe_freqs);
 }
 
 static int cbe_cpufreq_cpu_exit(struct cpufreq_policy *policy)
@@ -202,7 +271,7 @@
 	freqs.new = cbe_freqs[cbe_pmode_new].frequency;
 	freqs.cpu = policy->cpu;
 
-	mutex_lock (&cbe_switch_mutex);
+	mutex_lock(&cbe_switch_mutex);
 	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 
 	pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n",
@@ -233,11 +302,26 @@
 
 static int __init cbe_cpufreq_init(void)
 {
+	struct device_node *np;
+
+	if (!machine_is(cell))
+		return -ENODEV;
+
+	np = of_find_node_by_type(NULL, "ibm,pmi");
+
+	pmi_dev = of_find_device_by_node(np);
+
+	if (pmi_dev)
+		pmi_register_handler(pmi_dev, &cbe_pmi_handler);
+
 	return cpufreq_register_driver(&cbe_cpufreq_driver);
 }
 
 static void __exit cbe_cpufreq_exit(void)
 {
+	if (pmi_dev)
+		pmi_unregister_handler(pmi_dev, &cbe_pmi_handler);
+
 	cpufreq_unregister_driver(&cbe_cpufreq_driver);
 }
 
diff --git a/arch/powerpc/platforms/cell/cbe_regs.c b/arch/powerpc/platforms/cell/cbe_regs.c
index 9a0ee62..12c9674 100644
--- a/arch/powerpc/platforms/cell/cbe_regs.c
+++ b/arch/powerpc/platforms/cell/cbe_regs.c
@@ -14,6 +14,8 @@
 #include <asm/pgtable.h>
 #include <asm/prom.h>
 #include <asm/ptrace.h>
+#include <asm/of_device.h>
+#include <asm/of_platform.h>
 
 #include "cbe_regs.h"
 
@@ -27,6 +29,7 @@
 static struct cbe_regs_map
 {
 	struct device_node *cpu_node;
+	struct device_node *be_node;
 	struct cbe_pmd_regs __iomem *pmd_regs;
 	struct cbe_iic_regs __iomem *iic_regs;
 	struct cbe_mic_tm_regs __iomem *mic_tm_regs;
@@ -37,30 +40,43 @@
 static struct cbe_thread_map
 {
 	struct device_node *cpu_node;
+	struct device_node *be_node;
 	struct cbe_regs_map *regs;
+	unsigned int thread_id;
+	unsigned int cbe_id;
 } cbe_thread_map[NR_CPUS];
 
+static cpumask_t cbe_local_mask[MAX_CBE] = { [0 ... MAX_CBE-1] = CPU_MASK_NONE };
+static cpumask_t cbe_first_online_cpu = CPU_MASK_NONE;
+
 static struct cbe_regs_map *cbe_find_map(struct device_node *np)
 {
 	int i;
 	struct device_node *tmp_np;
 
-	if (strcasecmp(np->type, "spe") == 0) {
-		if (np->data == NULL) {
-			/* walk up path until cpu node was found */
-			tmp_np = np->parent;
-			while (tmp_np != NULL && strcasecmp(tmp_np->type, "cpu") != 0)
-				tmp_np = tmp_np->parent;
-
-			np->data = cbe_find_map(tmp_np);
-		}
-		return np->data;
+	if (strcasecmp(np->type, "spe")) {
+		for (i = 0; i < cbe_regs_map_count; i++)
+			if (cbe_regs_maps[i].cpu_node == np ||
+			    cbe_regs_maps[i].be_node == np)
+				return &cbe_regs_maps[i];
+		return NULL;
 	}
 
-	for (i = 0; i < cbe_regs_map_count; i++)
-		if (cbe_regs_maps[i].cpu_node == np)
-			return &cbe_regs_maps[i];
-	return NULL;
+	if (np->data)
+		return np->data;
+
+	/* walk up path until cpu or be node was found */
+	tmp_np = np;
+	do {
+		tmp_np = tmp_np->parent;
+		/* on a correct devicetree we wont get up to root */
+		BUG_ON(!tmp_np);
+	} while (strcasecmp(tmp_np->type, "cpu") &&
+		 strcasecmp(tmp_np->type, "be"));
+
+	np->data = cbe_find_map(tmp_np);
+
+	return np->data;
 }
 
 struct cbe_pmd_regs __iomem *cbe_get_pmd_regs(struct device_node *np)
@@ -130,38 +146,105 @@
 }
 EXPORT_SYMBOL_GPL(cbe_get_cpu_mic_tm_regs);
 
-/* FIXME
- * This is little more than a stub at the moment.  It should be
- * fleshed out so that it works for both SMT and non-SMT, no
- * matter if the passed cpu is odd or even.
- * For SMT enabled, returns 0 for even-numbered cpu; otherwise 1.
- * For SMT disabled, returns 0 for all cpus.
- */
 u32 cbe_get_hw_thread_id(int cpu)
 {
-	return (cpu & 1);
+	return cbe_thread_map[cpu].thread_id;
 }
 EXPORT_SYMBOL_GPL(cbe_get_hw_thread_id);
 
-void __init cbe_regs_init(void)
+u32 cbe_cpu_to_node(int cpu)
 {
-	int i;
-	struct device_node *cpu;
+	return cbe_thread_map[cpu].cbe_id;
+}
+EXPORT_SYMBOL_GPL(cbe_cpu_to_node);
 
-	/* Build local fast map of CPUs */
-	for_each_possible_cpu(i)
-		cbe_thread_map[i].cpu_node = of_get_cpu_node(i, NULL);
+u32 cbe_node_to_cpu(int node)
+{
+	return find_first_bit( (unsigned long *) &cbe_local_mask[node], sizeof(cpumask_t));
+}
+EXPORT_SYMBOL_GPL(cbe_node_to_cpu);
 
-	/* Find maps for each device tree CPU */
-	for_each_node_by_type(cpu, "cpu") {
-		struct cbe_regs_map *map = &cbe_regs_maps[cbe_regs_map_count++];
+static struct device_node *cbe_get_be_node(int cpu_id)
+{
+	struct device_node *np;
 
+	for_each_node_by_type (np, "be") {
+		int len,i;
+		const phandle *cpu_handle;
+
+		cpu_handle = of_get_property(np, "cpus", &len);
+
+		for (i=0; i<len; i++)
+			if (of_find_node_by_phandle(cpu_handle[i]) == of_get_cpu_node(cpu_id, NULL))
+				return np;
+	}
+
+	return NULL;
+}
+
+void __init cbe_fill_regs_map(struct cbe_regs_map *map)
+{
+	if(map->be_node) {
+		struct device_node *be, *np;
+
+		be = map->be_node;
+
+		for_each_node_by_type(np, "pervasive")
+			if (of_get_parent(np) == be)
+				map->pmd_regs = of_iomap(np, 0);
+
+		for_each_node_by_type(np, "CBEA-Internal-Interrupt-Controller")
+			if (of_get_parent(np) == be)
+				map->iic_regs = of_iomap(np, 2);
+
+		for_each_node_by_type(np, "mic-tm")
+			if (of_get_parent(np) == be)
+				map->mic_tm_regs = of_iomap(np, 0);
+	} else {
+		struct device_node *cpu;
 		/* That hack must die die die ! */
 		const struct address_prop {
 			unsigned long address;
 			unsigned int len;
 		} __attribute__((packed)) *prop;
 
+		cpu = map->cpu_node;
+
+		prop = of_get_property(cpu, "pervasive", NULL);
+		if (prop != NULL)
+			map->pmd_regs = ioremap(prop->address, prop->len);
+
+		prop = of_get_property(cpu, "iic", NULL);
+		if (prop != NULL)
+			map->iic_regs = ioremap(prop->address, prop->len);
+
+		prop = of_get_property(cpu, "mic-tm", NULL);
+		if (prop != NULL)
+			map->mic_tm_regs = ioremap(prop->address, prop->len);
+	}
+}
+
+
+void __init cbe_regs_init(void)
+{
+	int i;
+	unsigned int thread_id;
+	struct device_node *cpu;
+
+	/* Build local fast map of CPUs */
+	for_each_possible_cpu(i) {
+		cbe_thread_map[i].cpu_node = of_get_cpu_node(i, &thread_id);
+		cbe_thread_map[i].be_node = cbe_get_be_node(i);
+		cbe_thread_map[i].thread_id = thread_id;
+	}
+
+	/* Find maps for each device tree CPU */
+	for_each_node_by_type(cpu, "cpu") {
+		struct cbe_regs_map *map;
+		unsigned int cbe_id;
+
+		cbe_id = cbe_regs_map_count++;
+		map = &cbe_regs_maps[cbe_id];
 
 		if (cbe_regs_map_count > MAX_CBE) {
 			printk(KERN_ERR "cbe_regs: More BE chips than supported"
@@ -170,22 +253,21 @@
 			return;
 		}
 		map->cpu_node = cpu;
-		for_each_possible_cpu(i)
-			if (cbe_thread_map[i].cpu_node == cpu)
-				cbe_thread_map[i].regs = map;
 
-		prop = get_property(cpu, "pervasive", NULL);
-		if (prop != NULL)
-			map->pmd_regs = ioremap(prop->address, prop->len);
+		for_each_possible_cpu(i) {
+			struct cbe_thread_map *thread = &cbe_thread_map[i];
 
-		prop = get_property(cpu, "iic", NULL);
-		if (prop != NULL)
-			map->iic_regs = ioremap(prop->address, prop->len);
+			if (thread->cpu_node == cpu) {
+				thread->regs = map;
+				thread->cbe_id = cbe_id;
+				map->be_node = thread->be_node;
+				cpu_set(i, cbe_local_mask[cbe_id]);
+				if(thread->thread_id == 0)
+					cpu_set(i, cbe_first_online_cpu);
+			}
+		}
 
-		prop = (struct address_prop *)get_property(cpu, "mic-tm",
-							   NULL);
-		if (prop != NULL)
-			map->mic_tm_regs = ioremap(prop->address, prop->len);
+		cbe_fill_regs_map(map);
 	}
 }
 
diff --git a/arch/powerpc/platforms/cell/cbe_regs.h b/arch/powerpc/platforms/cell/cbe_regs.h
index 440a7ec..17d5971 100644
--- a/arch/powerpc/platforms/cell/cbe_regs.h
+++ b/arch/powerpc/platforms/cell/cbe_regs.h
@@ -255,6 +255,11 @@
 extern struct cbe_mic_tm_regs __iomem *cbe_get_mic_tm_regs(struct device_node *np);
 extern struct cbe_mic_tm_regs __iomem *cbe_get_cpu_mic_tm_regs(int cpu);
 
+/* some utility functions to deal with SMT */
+extern u32 cbe_get_hw_thread_id(int cpu);
+extern u32 cbe_cpu_to_node(int cpu);
+extern u32 cbe_node_to_cpu(int node);
+
 /* Init this module early */
 extern void cbe_regs_init(void);
 
diff --git a/arch/powerpc/platforms/cell/cbe_thermal.c b/arch/powerpc/platforms/cell/cbe_thermal.c
index 70e0d96..f370f0f 100644
--- a/arch/powerpc/platforms/cell/cbe_thermal.c
+++ b/arch/powerpc/platforms/cell/cbe_thermal.c
@@ -1,6 +1,31 @@
 /*
  * thermal support for the cell processor
  *
+ * This module adds some sysfs attributes to cpu and spu nodes.
+ * Base for measurements are the digital thermal sensors (DTS)
+ * located on the chip.
+ * The accuracy is 2 degrees, starting from 65 up to 125 degrees celsius
+ * The attributes can be found under
+ * /sys/devices/system/cpu/cpuX/thermal
+ * /sys/devices/system/spu/spuX/thermal
+ *
+ * The following attributes are added for each node:
+ * temperature:
+ *	contains the current temperature measured by the DTS
+ * throttle_begin:
+ *	throttling begins when temperature is greater or equal to
+ *	throttle_begin. Setting this value to 125 prevents throttling.
+ * throttle_end:
+ *	throttling is being ceased, if the temperature is lower than
+ *	throttle_end. Due to a delay between applying throttling and
+ *	a reduced temperature this value should be less than throttle_begin.
+ *	A value equal to throttle_begin provides only a very little hysteresis.
+ * throttle_full_stop:
+ *	If the temperatrue is greater or equal to throttle_full_stop,
+ *	full throttling is applied to the cpu or spu. This value should be
+ *	greater than throttle_begin and throttle_end. Setting this value to
+ *	65 prevents the unit from running code at all.
+ *
  * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
  *
  * Author: Christian Krafft <krafft@de.ibm.com>
@@ -31,6 +56,26 @@
 #include "cbe_regs.h"
 #include "spu_priv1_mmio.h"
 
+#define TEMP_MIN 65
+#define TEMP_MAX 125
+
+#define SYSDEV_PREFIX_ATTR(_prefix,_name,_mode)			\
+struct sysdev_attribute attr_ ## _prefix ## _ ## _name = {	\
+	.attr = { .name = __stringify(_name), .mode = _mode },	\
+	.show	= _prefix ## _show_ ## _name,			\
+	.store	= _prefix ## _store_ ## _name,			\
+};
+
+static inline u8 reg_to_temp(u8 reg_value)
+{
+	return ((reg_value & 0x3f) << 1) + TEMP_MIN;
+}
+
+static inline u8 temp_to_reg(u8 temp)
+{
+	return ((temp - TEMP_MIN) >> 1) & 0x3f;
+}
+
 static struct cbe_pmd_regs __iomem *get_pmd_regs(struct sys_device *sysdev)
 {
 	struct spu *spu;
@@ -43,14 +88,14 @@
 /* returns the value for a given spu in a given register */
 static u8 spu_read_register_value(struct sys_device *sysdev, union spe_reg __iomem *reg)
 {
-	unsigned int *id;
+	const unsigned int *id;
 	union spe_reg value;
 	struct spu *spu;
 
 	/* getting the id from the reg attribute will not work on future device-tree layouts
 	 * in future we should store the id to the spu struct and use it here */
 	spu = container_of(sysdev, struct spu, sysdev);
-	id = (unsigned int *)get_property(spu_devnode(spu), "reg", NULL);
+	id = of_get_property(spu_devnode(spu), "reg", NULL);
 	value.val = in_be64(&reg->val);
 
 	return value.spe[*id];
@@ -58,20 +103,81 @@
 
 static ssize_t spu_show_temp(struct sys_device *sysdev, char *buf)
 {
-	int value;
+	u8 value;
 	struct cbe_pmd_regs __iomem *pmd_regs;
 
 	pmd_regs = get_pmd_regs(sysdev);
 
 	value = spu_read_register_value(sysdev, &pmd_regs->ts_ctsr1);
-	/* clear all other bits */
-	value &= 0x3F;
-	/* temp is stored in steps of 2 degrees */
-	value *= 2;
-	/* base temp is 65 degrees */
-	value += 65;
 
-	return sprintf(buf, "%d\n", (int) value);
+	return sprintf(buf, "%d\n", reg_to_temp(value));
+}
+
+static ssize_t show_throttle(struct cbe_pmd_regs __iomem *pmd_regs, char *buf, int pos)
+{
+	u64 value;
+
+	value = in_be64(&pmd_regs->tm_tpr.val);
+	/* access the corresponding byte */
+	value >>= pos;
+	value &= 0x3F;
+
+	return sprintf(buf, "%d\n", reg_to_temp(value));
+}
+
+static ssize_t store_throttle(struct cbe_pmd_regs __iomem *pmd_regs, const char *buf, size_t size, int pos)
+{
+	u64 reg_value;
+	int temp;
+	u64 new_value;
+	int ret;
+
+	ret = sscanf(buf, "%u", &temp);
+
+	if (ret != 1 || temp < TEMP_MIN || temp > TEMP_MAX)
+		return -EINVAL;
+
+	new_value = temp_to_reg(temp);
+
+	reg_value = in_be64(&pmd_regs->tm_tpr.val);
+
+	/* zero out bits for new value */
+	reg_value &= ~(0xffull << pos);
+	/* set bits to new value */
+	reg_value |= new_value << pos;
+
+	out_be64(&pmd_regs->tm_tpr.val, reg_value);
+	return size;
+}
+
+static ssize_t spu_show_throttle_end(struct sys_device *sysdev, char *buf)
+{
+	return show_throttle(get_pmd_regs(sysdev), buf, 0);
+}
+
+static ssize_t spu_show_throttle_begin(struct sys_device *sysdev, char *buf)
+{
+	return show_throttle(get_pmd_regs(sysdev), buf, 8);
+}
+
+static ssize_t spu_show_throttle_full_stop(struct sys_device *sysdev, char *buf)
+{
+	return show_throttle(get_pmd_regs(sysdev), buf, 16);
+}
+
+static ssize_t spu_store_throttle_end(struct sys_device *sysdev, const char *buf, size_t size)
+{
+	return store_throttle(get_pmd_regs(sysdev), buf, size, 0);
+}
+
+static ssize_t spu_store_throttle_begin(struct sys_device *sysdev, const char *buf, size_t size)
+{
+	return store_throttle(get_pmd_regs(sysdev), buf, size, 8);
+}
+
+static ssize_t spu_store_throttle_full_stop(struct sys_device *sysdev, const char *buf, size_t size)
+{
+	return store_throttle(get_pmd_regs(sysdev), buf, size, 16);
 }
 
 static ssize_t ppe_show_temp(struct sys_device *sysdev, char *buf, int pos)
@@ -82,16 +188,9 @@
 	pmd_regs = cbe_get_cpu_pmd_regs(sysdev->id);
 	value = in_be64(&pmd_regs->ts_ctsr2);
 
-	/* access the corresponding byte */
-	value >>= pos;
-	/* clear all other bits */
-	value &= 0x3F;
-	/* temp is stored in steps of 2 degrees */
-	value *= 2;
-	/* base temp is 65 degrees */
-	value += 65;
+	value = (value >> pos) & 0x3f;
 
-	return sprintf(buf, "%d\n", (int) value);
+	return sprintf(buf, "%d\n", reg_to_temp(value));
 }
 
 
@@ -108,13 +207,52 @@
 	return ppe_show_temp(sysdev, buf, 0);
 }
 
+static ssize_t ppe_show_throttle_end(struct sys_device *sysdev, char *buf)
+{
+	return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 32);
+}
+
+static ssize_t ppe_show_throttle_begin(struct sys_device *sysdev, char *buf)
+{
+	return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 40);
+}
+
+static ssize_t ppe_show_throttle_full_stop(struct sys_device *sysdev, char *buf)
+{
+	return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 48);
+}
+
+static ssize_t ppe_store_throttle_end(struct sys_device *sysdev, const char *buf, size_t size)
+{
+	return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 32);
+}
+
+static ssize_t ppe_store_throttle_begin(struct sys_device *sysdev, const char *buf, size_t size)
+{
+	return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 40);
+}
+
+static ssize_t ppe_store_throttle_full_stop(struct sys_device *sysdev, const char *buf, size_t size)
+{
+	return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 48);
+}
+
+
 static struct sysdev_attribute attr_spu_temperature = {
 	.attr = {.name = "temperature", .mode = 0400 },
 	.show = spu_show_temp,
 };
 
+static SYSDEV_PREFIX_ATTR(spu, throttle_end, 0600);
+static SYSDEV_PREFIX_ATTR(spu, throttle_begin, 0600);
+static SYSDEV_PREFIX_ATTR(spu, throttle_full_stop, 0600);
+
+
 static struct attribute *spu_attributes[] = {
 	&attr_spu_temperature.attr,
+	&attr_spu_throttle_end.attr,
+	&attr_spu_throttle_begin.attr,
+	&attr_spu_throttle_full_stop.attr,
 	NULL,
 };
 
@@ -133,9 +271,16 @@
 	.show = ppe_show_temp1,
 };
 
+static SYSDEV_PREFIX_ATTR(ppe, throttle_end, 0600);
+static SYSDEV_PREFIX_ATTR(ppe, throttle_begin, 0600);
+static SYSDEV_PREFIX_ATTR(ppe, throttle_full_stop, 0600);
+
 static struct attribute *ppe_attributes[] = {
 	&attr_ppe_temperature0.attr,
 	&attr_ppe_temperature1.attr,
+	&attr_ppe_throttle_end.attr,
+	&attr_ppe_throttle_begin.attr,
+	&attr_ppe_throttle_full_stop.attr,
 	NULL,
 };
 
diff --git a/arch/powerpc/platforms/cell/interrupt.c b/arch/powerpc/platforms/cell/interrupt.c
index 6666d03..4fc4e927 100644
--- a/arch/powerpc/platforms/cell/interrupt.c
+++ b/arch/powerpc/platforms/cell/interrupt.c
@@ -261,7 +261,7 @@
 		return -ENODEV;
 	if (intsize != 1)
 		return -ENODEV;
-	val = get_property(ct, "#interrupt-cells", NULL);
+	val = of_get_property(ct, "#interrupt-cells", NULL);
 	if (val == NULL || *val != 1)
 		return -ENODEV;
 
@@ -327,7 +327,7 @@
 		if (!device_is_compatible(dn,
 				     "IBM,CBEA-Internal-Interrupt-Controller"))
 			continue;
-		np = get_property(dn, "ibm,interrupt-server-ranges", NULL);
+		np = of_get_property(dn, "ibm,interrupt-server-ranges", NULL);
 		if (np == NULL) {
 			printk(KERN_WARNING "IIC: CPU association not found\n");
 			of_node_put(dn);
diff --git a/arch/powerpc/platforms/cell/io-workarounds.c b/arch/powerpc/platforms/cell/io-workarounds.c
index 7c73128..d68d920 100644
--- a/arch/powerpc/platforms/cell/io-workarounds.c
+++ b/arch/powerpc/platforms/cell/io-workarounds.c
@@ -318,7 +318,7 @@
 	 */
 	list_for_each_entry(phb, &hose_list, list_node) {
 		struct device_node *np = phb->arch_data;
-		const char *model = get_property(np, "model", NULL);
+		const char *model = of_get_property(np, "model", NULL);
 
 		/* If no model property or name isn't exactly "pci", skip */
 		if (model == NULL || strcmp(np->name, "pci"))
diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c
index 67d617b..760caa7 100644
--- a/arch/powerpc/platforms/cell/iommu.c
+++ b/arch/powerpc/platforms/cell/iommu.c
@@ -291,9 +291,9 @@
 		const unsigned int *nidp;
 		const unsigned long *tmp;
 
-		nidp = get_property(np, "node-id", NULL);
+		nidp = of_get_property(np, "node-id", NULL);
 		if (nidp && *nidp == nid) {
-			tmp = get_property(np, "ioc-translation", NULL);
+			tmp = of_get_property(np, "ioc-translation", NULL);
 			if (tmp) {
 				*base = *tmp;
 				of_node_put(np);
@@ -430,7 +430,7 @@
 	struct iommu_window *window;
 	const unsigned int *ioid;
 
-	ioid = get_property(np, "ioid", NULL);
+	ioid = of_get_property(np, "ioid", NULL);
 	if (ioid == NULL)
 		printk(KERN_WARNING "iommu: missing ioid for %s using 0\n",
 		       np->full_name);
@@ -496,7 +496,7 @@
 	struct dev_archdata *archdata = &dev->archdata;
 
 	/* If we run without iommu, no need to do anything */
-	if (pci_dma_ops == &dma_direct_ops)
+	if (get_pci_dma_ops() == &dma_direct_ops)
 		return;
 
 	/* Current implementation uses the first window available in that
@@ -530,7 +530,7 @@
 		return 0;
 
 	/* We use the PCI DMA ops */
-	dev->archdata.dma_ops = pci_dma_ops;
+	dev->archdata.dma_ops = get_pci_dma_ops();
 
 	cell_dma_dev_setup(dev);
 
@@ -549,7 +549,7 @@
 	unsigned long index;
 
 	/* Use ibm,dma-window if available, else, hard code ! */
-	dma_window = get_property(np, "ibm,dma-window", NULL);
+	dma_window = of_get_property(np, "ibm,dma-window", NULL);
 	if (dma_window == NULL) {
 		*base = 0;
 		*size = 0x80000000u;
@@ -646,7 +646,7 @@
 	unsigned long base = 0, size;
 
 	/* When no iommu is present, we use direct DMA ops */
-	pci_dma_ops = &dma_direct_ops;
+	set_pci_dma_ops(&dma_direct_ops);
 
 	/* First make sure all IOC translation is turned off */
 	cell_disable_iommus();
@@ -734,7 +734,7 @@
 	}
 
 	/* Setup default PCI iommu ops */
-	pci_dma_ops = &dma_iommu_ops;
+	set_pci_dma_ops(&dma_iommu_ops);
 
  bail:
 	/* Register callbacks on OF platform device addition/removal
diff --git a/arch/powerpc/platforms/cell/ras.c b/arch/powerpc/platforms/cell/ras.c
index 0984c70..3961a08 100644
--- a/arch/powerpc/platforms/cell/ras.c
+++ b/arch/powerpc/platforms/cell/ras.c
@@ -3,11 +3,13 @@
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/smp.h>
+#include <linux/reboot.h>
 
 #include <asm/reg.h>
 #include <asm/io.h>
 #include <asm/prom.h>
 #include <asm/machdep.h>
+#include <asm/rtas.h>
 
 #include "ras.h"
 #include "cbe_regs.h"
@@ -82,6 +84,164 @@
 	return 0;
 }
 
+struct ptcal_area {
+	struct list_head list;
+	int nid;
+	int order;
+	struct page *pages;
+};
+
+static LIST_HEAD(ptcal_list);
+
+static int ptcal_start_tok, ptcal_stop_tok;
+
+static int __init cbe_ptcal_enable_on_node(int nid, int order)
+{
+	struct ptcal_area *area;
+	int ret = -ENOMEM;
+	unsigned long addr;
+
+#ifdef CONFIG_CRASH_DUMP
+	rtas_call(ptcal_stop_tok, 1, 1, NULL, nid);
+#endif
+
+	area = kmalloc(sizeof(*area), GFP_KERNEL);
+	if (!area)
+		goto out_err;
+
+	area->nid = nid;
+	area->order = order;
+	area->pages = alloc_pages_node(area->nid, GFP_KERNEL, area->order);
+
+	if (!area->pages)
+		goto out_free_area;
+
+	addr = __pa(page_address(area->pages));
+
+	ret = -EIO;
+	if (rtas_call(ptcal_start_tok, 3, 1, NULL, area->nid,
+				(unsigned int)(addr >> 32),
+				(unsigned int)(addr & 0xffffffff))) {
+		printk(KERN_ERR "%s: error enabling PTCAL on node %d!\n",
+				__FUNCTION__, nid);
+		goto out_free_pages;
+	}
+
+	list_add(&area->list, &ptcal_list);
+
+	return 0;
+
+out_free_pages:
+	__free_pages(area->pages, area->order);
+out_free_area:
+	kfree(area);
+out_err:
+	return ret;
+}
+
+static int __init cbe_ptcal_enable(void)
+{
+	const u32 *size;
+	struct device_node *np;
+	int order, found_mic = 0;
+
+	np = of_find_node_by_path("/rtas");
+	if (!np)
+		return -ENODEV;
+
+	size = of_get_property(np, "ibm,cbe-ptcal-size", NULL);
+	if (!size)
+		return -ENODEV;
+
+	pr_debug("%s: enabling PTCAL, size = 0x%x\n", __FUNCTION__, *size);
+	order = get_order(*size);
+	of_node_put(np);
+
+	/* support for malta device trees, with be@/mic@ nodes */
+	for_each_node_by_type(np, "mic-tm") {
+		cbe_ptcal_enable_on_node(of_node_to_nid(np), order);
+		found_mic = 1;
+	}
+
+	if (found_mic)
+		return 0;
+
+	/* support for older device tree - use cpu nodes */
+	for_each_node_by_type(np, "cpu") {
+		const u32 *nid = of_get_property(np, "node-id", NULL);
+		if (!nid) {
+			printk(KERN_ERR "%s: node %s is missing node-id?\n",
+					__FUNCTION__, np->full_name);
+			continue;
+		}
+		cbe_ptcal_enable_on_node(*nid, order);
+		found_mic = 1;
+	}
+
+	return found_mic ? 0 : -ENODEV;
+}
+
+static int cbe_ptcal_disable(void)
+{
+	struct ptcal_area *area, *tmp;
+	int ret = 0;
+
+	pr_debug("%s: disabling PTCAL\n", __FUNCTION__);
+
+	list_for_each_entry_safe(area, tmp, &ptcal_list, list) {
+		/* disable ptcal on this node */
+		if (rtas_call(ptcal_stop_tok, 1, 1, NULL, area->nid)) {
+			printk(KERN_ERR "%s: error disabling PTCAL "
+					"on node %d!\n", __FUNCTION__,
+					area->nid);
+			ret = -EIO;
+			continue;
+		}
+
+		/* ensure we can access the PTCAL area */
+		memset(page_address(area->pages), 0,
+				1 << (area->order + PAGE_SHIFT));
+
+		/* clean up */
+		list_del(&area->list);
+		__free_pages(area->pages, area->order);
+		kfree(area);
+	}
+
+	return ret;
+}
+
+static int cbe_ptcal_notify_reboot(struct notifier_block *nb,
+		unsigned long code, void *data)
+{
+	return cbe_ptcal_disable();
+}
+
+static struct notifier_block cbe_ptcal_reboot_notifier = {
+	.notifier_call = cbe_ptcal_notify_reboot
+};
+
+int __init cbe_ptcal_init(void)
+{
+	int ret;
+	ptcal_start_tok = rtas_token("ibm,cbe-start-ptcal");
+	ptcal_stop_tok = rtas_token("ibm,cbe-stop-ptcal");
+
+	if (ptcal_start_tok == RTAS_UNKNOWN_SERVICE
+			|| ptcal_stop_tok == RTAS_UNKNOWN_SERVICE)
+		return -ENODEV;
+
+	ret = register_reboot_notifier(&cbe_ptcal_reboot_notifier);
+	if (ret) {
+		printk(KERN_ERR "Can't disable PTCAL, so not enabling\n");
+		return ret;
+	}
+
+	return cbe_ptcal_enable();
+}
+
+arch_initcall(cbe_ptcal_init);
+
 void __init cbe_ras_init(void)
 {
 	unsigned long hid0;
diff --git a/arch/powerpc/platforms/cell/setup.c b/arch/powerpc/platforms/cell/setup.c
index 36989c2..54b9618 100644
--- a/arch/powerpc/platforms/cell/setup.c
+++ b/arch/powerpc/platforms/cell/setup.c
@@ -71,7 +71,7 @@
 
 	root = of_find_node_by_path("/");
 	if (root)
-		model = get_property(root, "model", NULL);
+		model = of_get_property(root, "model", NULL);
 	seq_printf(m, "machine\t\t: CHRP %s\n", model);
 	of_node_put(root);
 }
@@ -190,15 +190,6 @@
 	return 1;
 }
 
-/*
- * Cell has no legacy IO; anything calling this function has to
- * fail or bad things will happen
- */
-static int cell_check_legacy_ioport(unsigned int baseport)
-{
-	return -ENODEV;
-}
-
 define_machine(cell) {
 	.name			= "Cell",
 	.probe			= cell_probe,
@@ -211,7 +202,6 @@
 	.get_rtc_time		= rtas_get_rtc_time,
 	.set_rtc_time		= rtas_set_rtc_time,
 	.calibrate_decr		= generic_calibrate_decr,
-	.check_legacy_ioport	= cell_check_legacy_ioport,
 	.progress		= cell_progress,
 	.init_IRQ       	= cell_init_irq,
 	.pci_setup_phb		= rtas_setup_phb,
diff --git a/arch/powerpc/platforms/cell/spider-pic.c b/arch/powerpc/platforms/cell/spider-pic.c
index 21a9ebd..fb1f157 100644
--- a/arch/powerpc/platforms/cell/spider-pic.c
+++ b/arch/powerpc/platforms/cell/spider-pic.c
@@ -254,25 +254,25 @@
 	}
 
 	/* Now do the horrible hacks */
-	tmp = get_property(pic->of_node, "#interrupt-cells", NULL);
+	tmp = of_get_property(pic->of_node, "#interrupt-cells", NULL);
 	if (tmp == NULL)
 		return NO_IRQ;
 	intsize = *tmp;
-	imap = get_property(pic->of_node, "interrupt-map", &imaplen);
+	imap = of_get_property(pic->of_node, "interrupt-map", &imaplen);
 	if (imap == NULL || imaplen < (intsize + 1))
 		return NO_IRQ;
 	iic = of_find_node_by_phandle(imap[intsize]);
 	if (iic == NULL)
 		return NO_IRQ;
 	imap += intsize + 1;
-	tmp = get_property(iic, "#interrupt-cells", NULL);
+	tmp = of_get_property(iic, "#interrupt-cells", NULL);
 	if (tmp == NULL)
 		return NO_IRQ;
 	intsize = *tmp;
 	/* Assume unit is last entry of interrupt specifier */
 	unit = imap[intsize - 1];
 	/* Ok, we have a unit, now let's try to get the node */
-	tmp = get_property(iic, "ibm,interrupt-server-ranges", NULL);
+	tmp = of_get_property(iic, "ibm,interrupt-server-ranges", NULL);
 	if (tmp == NULL) {
 		of_node_put(iic);
 		return NO_IRQ;
diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c
index eba7a26..fec5152 100644
--- a/arch/powerpc/platforms/cell/spu_base.c
+++ b/arch/powerpc/platforms/cell/spu_base.c
@@ -36,12 +36,14 @@
 #include <asm/xmon.h>
 
 const struct spu_management_ops *spu_management_ops;
+EXPORT_SYMBOL_GPL(spu_management_ops);
+
 const struct spu_priv1_ops *spu_priv1_ops;
 
 static struct list_head spu_list[MAX_NUMNODES];
 static LIST_HEAD(spu_full_list);
 static DEFINE_MUTEX(spu_mutex);
-static spinlock_t spu_list_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(spu_list_lock);
 
 EXPORT_SYMBOL_GPL(spu_priv1_ops);
 
@@ -290,7 +292,6 @@
 
 	return stat ? IRQ_HANDLED : IRQ_NONE;
 }
-EXPORT_SYMBOL_GPL(spu_irq_class_1_bottom);
 
 static irqreturn_t
 spu_irq_class_2(int irq, void *data)
@@ -431,10 +432,11 @@
 		spu = list_entry(spu_list[node].next, struct spu, list);
 		list_del_init(&spu->list);
 		pr_debug("Got SPU %d %d\n", spu->number, spu->node);
-		spu_init_channels(spu);
 	}
 	mutex_unlock(&spu_mutex);
 
+	if (spu)
+		spu_init_channels(spu);
 	return spu;
 }
 EXPORT_SYMBOL_GPL(spu_alloc_node);
@@ -461,108 +463,6 @@
 }
 EXPORT_SYMBOL_GPL(spu_free);
 
-static int spu_handle_mm_fault(struct spu *spu)
-{
-	struct mm_struct *mm = spu->mm;
-	struct vm_area_struct *vma;
-	u64 ea, dsisr, is_write;
-	int ret;
-
-	ea = spu->dar;
-	dsisr = spu->dsisr;
-#if 0
-	if (!IS_VALID_EA(ea)) {
-		return -EFAULT;
-	}
-#endif /* XXX */
-	if (mm == NULL) {
-		return -EFAULT;
-	}
-	if (mm->pgd == NULL) {
-		return -EFAULT;
-	}
-
-	down_read(&mm->mmap_sem);
-	vma = find_vma(mm, ea);
-	if (!vma)
-		goto bad_area;
-	if (vma->vm_start <= ea)
-		goto good_area;
-	if (!(vma->vm_flags & VM_GROWSDOWN))
-		goto bad_area;
-#if 0
-	if (expand_stack(vma, ea))
-		goto bad_area;
-#endif /* XXX */
-good_area:
-	is_write = dsisr & MFC_DSISR_ACCESS_PUT;
-	if (is_write) {
-		if (!(vma->vm_flags & VM_WRITE))
-			goto bad_area;
-	} else {
-		if (dsisr & MFC_DSISR_ACCESS_DENIED)
-			goto bad_area;
-		if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
-			goto bad_area;
-	}
-	ret = 0;
-	switch (handle_mm_fault(mm, vma, ea, is_write)) {
-	case VM_FAULT_MINOR:
-		current->min_flt++;
-		break;
-	case VM_FAULT_MAJOR:
-		current->maj_flt++;
-		break;
-	case VM_FAULT_SIGBUS:
-		ret = -EFAULT;
-		goto bad_area;
-	case VM_FAULT_OOM:
-		ret = -ENOMEM;
-		goto bad_area;
-	default:
-		BUG();
-	}
-	up_read(&mm->mmap_sem);
-	return ret;
-
-bad_area:
-	up_read(&mm->mmap_sem);
-	return -EFAULT;
-}
-
-int spu_irq_class_1_bottom(struct spu *spu)
-{
-	u64 ea, dsisr, access, error = 0UL;
-	int ret = 0;
-
-	ea = spu->dar;
-	dsisr = spu->dsisr;
-	if (dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED)) {
-		u64 flags;
-
-		access = (_PAGE_PRESENT | _PAGE_USER);
-		access |= (dsisr & MFC_DSISR_ACCESS_PUT) ? _PAGE_RW : 0UL;
-		local_irq_save(flags);
-		if (hash_page(ea, access, 0x300) != 0)
-			error |= CLASS1_ENABLE_STORAGE_FAULT_INTR;
-		local_irq_restore(flags);
-	}
-	if (error & CLASS1_ENABLE_STORAGE_FAULT_INTR) {
-		if ((ret = spu_handle_mm_fault(spu)) != 0)
-			error |= CLASS1_ENABLE_STORAGE_FAULT_INTR;
-		else
-			error &= ~CLASS1_ENABLE_STORAGE_FAULT_INTR;
-	}
-	spu->dar = 0UL;
-	spu->dsisr = 0UL;
-	if (!error) {
-		spu_restart_dma(spu);
-	} else {
-		spu->dma_callback(spu, SPE_EVENT_SPE_DATA_STORAGE);
-	}
-	return ret;
-}
-
 struct sysdev_class spu_sysdev_class = {
 	set_kset_name("spu")
 };
@@ -636,12 +536,6 @@
 	return 0;
 }
 
-static void spu_destroy_sysdev(struct spu *spu)
-{
-	sysfs_remove_device_from_node(&spu->sysdev, spu->node);
-	sysdev_unregister(&spu->sysdev);
-}
-
 static int __init create_spu(void *data)
 {
 	struct spu *spu;
@@ -693,58 +587,37 @@
 	return ret;
 }
 
-static void destroy_spu(struct spu *spu)
-{
-	list_del_init(&spu->list);
-	list_del_init(&spu->full_list);
-
-	spu_destroy_sysdev(spu);
-	spu_free_irqs(spu);
-	spu_destroy_spu(spu);
-	kfree(spu);
-}
-
-static void cleanup_spu_base(void)
-{
-	struct spu *spu, *tmp;
-	int node;
-
-	mutex_lock(&spu_mutex);
-	for (node = 0; node < MAX_NUMNODES; node++) {
-		list_for_each_entry_safe(spu, tmp, &spu_list[node], list)
-			destroy_spu(spu);
-	}
-	mutex_unlock(&spu_mutex);
-	sysdev_class_unregister(&spu_sysdev_class);
-}
-module_exit(cleanup_spu_base);
-
 static int __init init_spu_base(void)
 {
-	int i, ret;
+	int i, ret = 0;
+
+	for (i = 0; i < MAX_NUMNODES; i++)
+		INIT_LIST_HEAD(&spu_list[i]);
 
 	if (!spu_management_ops)
-		return 0;
+		goto out;
 
 	/* create sysdev class for spus */
 	ret = sysdev_class_register(&spu_sysdev_class);
 	if (ret)
-		return ret;
-
-	for (i = 0; i < MAX_NUMNODES; i++)
-		INIT_LIST_HEAD(&spu_list[i]);
+		goto out;
 
 	ret = spu_enumerate_spus(create_spu);
 
 	if (ret) {
 		printk(KERN_WARNING "%s: Error initializing spus\n",
 			__FUNCTION__);
-		cleanup_spu_base();
-		return ret;
+		goto out_unregister_sysdev_class;
 	}
 
 	xmon_register_spus(&spu_full_list);
 
+	return 0;
+
+ out_unregister_sysdev_class:
+	sysdev_class_unregister(&spu_sysdev_class);
+ out:
+
 	return ret;
 }
 module_init(init_spu_base);
diff --git a/arch/powerpc/platforms/cell/spu_coredump.c b/arch/powerpc/platforms/cell/spu_coredump.c
index 6915b41..4fd37ff 100644
--- a/arch/powerpc/platforms/cell/spu_coredump.c
+++ b/arch/powerpc/platforms/cell/spu_coredump.c
@@ -26,19 +26,18 @@
 
 #include <asm/spu.h>
 
-static struct spu_coredump_calls spu_coredump_calls;
+static struct spu_coredump_calls *spu_coredump_calls;
 static DEFINE_MUTEX(spu_coredump_mutex);
 
 int arch_notes_size(void)
 {
 	long ret;
-	struct module *owner = spu_coredump_calls.owner;
 
 	ret = -ENOSYS;
 	mutex_lock(&spu_coredump_mutex);
-	if (owner && try_module_get(owner)) {
-		ret = spu_coredump_calls.arch_notes_size();
-		module_put(owner);
+	if (spu_coredump_calls && try_module_get(spu_coredump_calls->owner)) {
+		ret = spu_coredump_calls->arch_notes_size();
+		module_put(spu_coredump_calls->owner);
 	}
 	mutex_unlock(&spu_coredump_mutex);
 	return ret;
@@ -46,36 +45,35 @@
 
 void arch_write_notes(struct file *file)
 {
-	struct module *owner = spu_coredump_calls.owner;
-
 	mutex_lock(&spu_coredump_mutex);
-	if (owner && try_module_get(owner)) {
-		spu_coredump_calls.arch_write_notes(file);
-		module_put(owner);
+	if (spu_coredump_calls && try_module_get(spu_coredump_calls->owner)) {
+		spu_coredump_calls->arch_write_notes(file);
+		module_put(spu_coredump_calls->owner);
 	}
 	mutex_unlock(&spu_coredump_mutex);
 }
 
 int register_arch_coredump_calls(struct spu_coredump_calls *calls)
 {
-	if (spu_coredump_calls.owner)
-		return -EBUSY;
+	int ret = 0;
+
 
 	mutex_lock(&spu_coredump_mutex);
-	spu_coredump_calls.arch_notes_size = calls->arch_notes_size;
-	spu_coredump_calls.arch_write_notes = calls->arch_write_notes;
-	spu_coredump_calls.owner = calls->owner;
+	if (spu_coredump_calls)
+		ret = -EBUSY;
+	else
+		spu_coredump_calls = calls;
 	mutex_unlock(&spu_coredump_mutex);
-	return 0;
+	return ret;
 }
 EXPORT_SYMBOL_GPL(register_arch_coredump_calls);
 
 void unregister_arch_coredump_calls(struct spu_coredump_calls *calls)
 {
-	BUG_ON(spu_coredump_calls.owner != calls->owner);
+	BUG_ON(spu_coredump_calls != calls);
 
 	mutex_lock(&spu_coredump_mutex);
-	spu_coredump_calls.owner = NULL;
+	spu_coredump_calls = NULL;
 	mutex_unlock(&spu_coredump_mutex);
 }
 EXPORT_SYMBOL_GPL(unregister_arch_coredump_calls);
diff --git a/arch/powerpc/platforms/cell/spu_manage.c b/arch/powerpc/platforms/cell/spu_manage.c
index e34599f..1d4562a 100644
--- a/arch/powerpc/platforms/cell/spu_manage.c
+++ b/arch/powerpc/platforms/cell/spu_manage.c
@@ -48,11 +48,11 @@
 {
 	const unsigned int *prop;
 	int proplen;
-	prop = get_property(spe, "unit-id", &proplen);
+	prop = of_get_property(spe, "unit-id", &proplen);
 	if (proplen == 4)
 		return (u64)*prop;
 
-	prop = get_property(spe, "reg", &proplen);
+	prop = of_get_property(spe, "reg", &proplen);
 	if (proplen == 4)
 		return (u64)*prop;
 
@@ -76,12 +76,12 @@
 	int nid;
 
 	/* Get the interrupt source unit from the device-tree */
-	tmp = get_property(np, "isrc", NULL);
+	tmp = of_get_property(np, "isrc", NULL);
 	if (!tmp)
 		return -ENODEV;
 	isrc = tmp[0];
 
-	tmp = get_property(np->parent->parent, "node-id", NULL);
+	tmp = of_get_property(np->parent->parent, "node-id", NULL);
 	if (!tmp) {
 		printk(KERN_WARNING "%s: can't find node-id\n", __FUNCTION__);
 		nid = spu->node;
@@ -110,7 +110,7 @@
 	} __attribute__((packed)) *prop;
 	int proplen;
 
-	prop = get_property(n, name, &proplen);
+	prop = of_get_property(n, name, &proplen);
 	if (prop == NULL || proplen != sizeof (struct address_prop))
 		return NULL;
 
@@ -124,11 +124,11 @@
 	int ret;
 
 	ret = -ENODEV;
-	spu->name = get_property(node, "name", NULL);
+	spu->name = of_get_property(node, "name", NULL);
 	if (!spu->name)
 		goto out;
 
-	prop = get_property(node, "local-store", NULL);
+	prop = of_get_property(node, "local-store", NULL);
 	if (!prop)
 		goto out;
 	spu->local_store_phys = *(unsigned long *)prop;
@@ -139,7 +139,7 @@
 	if (!spu->local_store)
 		goto out;
 
-	prop = get_property(node, "problem", NULL);
+	prop = of_get_property(node, "problem", NULL);
 	if (!prop)
 		goto out_unmap;
 	spu->problem_phys = *(unsigned long *)prop;
@@ -226,7 +226,7 @@
 	struct device_node *np = spu->devnode;
 	int ret = -ENODEV;
 
-	spu->name = get_property(np, "name", NULL);
+	spu->name = of_get_property(np, "name", NULL);
 	if (!spu->name)
 		goto out;
 
diff --git a/arch/powerpc/platforms/cell/spufs/Makefile b/arch/powerpc/platforms/cell/spufs/Makefile
index 472217d..2cd89c1 100644
--- a/arch/powerpc/platforms/cell/spufs/Makefile
+++ b/arch/powerpc/platforms/cell/spufs/Makefile
@@ -1,4 +1,4 @@
-obj-y += switch.o
+obj-y += switch.o fault.o
 
 obj-$(CONFIG_SPU_FS) += spufs.o
 spufs-y += inode.o file.o context.o syscalls.o coredump.o
diff --git a/arch/powerpc/platforms/cell/spufs/backing_ops.c b/arch/powerpc/platforms/cell/spufs/backing_ops.c
index 1898f0d..3322528 100644
--- a/arch/powerpc/platforms/cell/spufs/backing_ops.c
+++ b/arch/powerpc/platforms/cell/spufs/backing_ops.c
@@ -350,6 +350,11 @@
 	return ret;
 }
 
+static void spu_backing_restart_dma(struct spu_context *ctx)
+{
+	/* nothing to do here */
+}
+
 struct spu_context_ops spu_backing_ops = {
 	.mbox_read = spu_backing_mbox_read,
 	.mbox_stat_read = spu_backing_mbox_stat_read,
@@ -376,4 +381,5 @@
 	.read_mfc_tagstatus = spu_backing_read_mfc_tagstatus,
 	.get_mfc_free_elements = spu_backing_get_mfc_free_elements,
 	.send_mfc_command = spu_backing_send_mfc_command,
+	.restart_dma = spu_backing_restart_dma,
 };
diff --git a/arch/powerpc/platforms/cell/spufs/context.c b/arch/powerpc/platforms/cell/spufs/context.c
index 04ad2e3..a87d9ca 100644
--- a/arch/powerpc/platforms/cell/spufs/context.c
+++ b/arch/powerpc/platforms/cell/spufs/context.c
@@ -41,9 +41,10 @@
 		goto out_free;
 	}
 	spin_lock_init(&ctx->mmio_lock);
+	spin_lock_init(&ctx->mapping_lock);
 	kref_init(&ctx->kref);
 	mutex_init(&ctx->state_mutex);
-	init_MUTEX(&ctx->run_sema);
+	mutex_init(&ctx->run_mutex);
 	init_waitqueue_head(&ctx->ibox_wq);
 	init_waitqueue_head(&ctx->wbox_wq);
 	init_waitqueue_head(&ctx->stop_wq);
@@ -51,6 +52,7 @@
 	ctx->state = SPU_STATE_SAVED;
 	ctx->ops = &spu_backing_ops;
 	ctx->owner = get_task_mm(current);
+	INIT_LIST_HEAD(&ctx->rq);
 	if (gang)
 		spu_gang_add_ctx(gang, ctx);
 	ctx->rt_priority = current->rt_priority;
@@ -75,6 +77,7 @@
 	spu_fini_csa(&ctx->csa);
 	if (ctx->gang)
 		spu_gang_remove_ctx(ctx->gang, ctx);
+	BUG_ON(!list_empty(&ctx->rq));
 	kfree(ctx);
 }
 
@@ -119,46 +122,6 @@
 }
 
 /**
- * spu_acquire_exclusive - lock spu contex and protect against userspace access
- * @ctx:	spu contex to lock
- *
- * Note:
- *	Returns 0 and with the context locked on success
- *	Returns negative error and with the context _unlocked_ on failure.
- */
-int spu_acquire_exclusive(struct spu_context *ctx)
-{
-	int ret = -EINVAL;
-
-	spu_acquire(ctx);
-	/*
-	 * Context is about to be freed, so we can't acquire it anymore.
-	 */
-	if (!ctx->owner)
-		goto out_unlock;
-
-	if (ctx->state == SPU_STATE_SAVED) {
-		ret = spu_activate(ctx, 0);
-		if (ret)
-			goto out_unlock;
-	} else {
-		/*
-		 * We need to exclude userspace access to the context.
-		 *
-		 * To protect against memory access we invalidate all ptes
-		 * and make sure the pagefault handlers block on the mutex.
-		 */
-		spu_unmap_mappings(ctx);
-	}
-
-	return 0;
-
- out_unlock:
-	spu_release(ctx);
-	return ret;
-}
-
-/**
  * spu_acquire_runnable - lock spu contex and make sure it is in runnable state
  * @ctx:	spu contex to lock
  *
diff --git a/arch/powerpc/platforms/cell/spufs/coredump.c b/arch/powerpc/platforms/cell/spufs/coredump.c
index 725e195..5d9ad5a 100644
--- a/arch/powerpc/platforms/cell/spufs/coredump.c
+++ b/arch/powerpc/platforms/cell/spufs/coredump.c
@@ -169,12 +169,12 @@
 	struct spu_context *ctx;
 	loff_t pos = 0;
 	int sz, dfd, rc, total = 0;
-	const int bufsz = 4096;
+	const int bufsz = PAGE_SIZE;
 	char *name;
 	char fullname[80], *buf;
 	struct elf_note en;
 
-	buf = kmalloc(bufsz, GFP_KERNEL);
+	buf = (void *)get_zeroed_page(GFP_KERNEL);
 	if (!buf)
 		return;
 
@@ -187,9 +187,8 @@
 		sz = spufs_coredump_read[i].size;
 
 	ctx = ctx_info->ctx;
-	if (!ctx) {
-		return;
-	}
+	if (!ctx)
+		goto out;
 
 	sprintf(fullname, "SPU/%d/%s", dfd, name);
 	en.n_namesz = strlen(fullname) + 1;
@@ -197,23 +196,25 @@
 	en.n_type = NT_SPU;
 
 	if (!spufs_dump_write(file, &en, sizeof(en)))
-		return;
+		goto out;
 	if (!spufs_dump_write(file, fullname, en.n_namesz))
-		return;
+		goto out;
 	if (!spufs_dump_seek(file, roundup((unsigned long)file->f_pos, 4)))
-		return;
+		goto out;
 
 	do {
 		rc = do_coredump_read(i, ctx, buf, bufsz, &pos);
 		if (rc > 0) {
 			if (!spufs_dump_write(file, buf, rc))
-				return;
+				goto out;
 			total += rc;
 		}
 	} while (rc == bufsz && total < sz);
 
 	spufs_dump_seek(file, roundup((unsigned long)file->f_pos
 						- total + sz, 4));
+out:
+	free_page((unsigned long)buf);
 }
 
 static void spufs_arch_write_notes(struct file *file)
diff --git a/arch/powerpc/platforms/cell/spufs/fault.c b/arch/powerpc/platforms/cell/spufs/fault.c
new file mode 100644
index 0000000..0f75c07
--- /dev/null
+++ b/arch/powerpc/platforms/cell/spufs/fault.c
@@ -0,0 +1,211 @@
+/*
+ * Low-level SPU handling
+ *
+ * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
+ *
+ * Author: Arnd Bergmann <arndb@de.ibm.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; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+
+#include <asm/spu.h>
+#include <asm/spu_csa.h>
+
+#include "spufs.h"
+
+/*
+ * This ought to be kept in sync with the powerpc specific do_page_fault
+ * function. Currently, there are a few corner cases that we haven't had
+ * to handle fortunately.
+ */
+static int spu_handle_mm_fault(struct mm_struct *mm, unsigned long ea, unsigned long dsisr)
+{
+	struct vm_area_struct *vma;
+	unsigned long is_write;
+	int ret;
+
+#if 0
+	if (!IS_VALID_EA(ea)) {
+		return -EFAULT;
+	}
+#endif /* XXX */
+	if (mm == NULL) {
+		return -EFAULT;
+	}
+	if (mm->pgd == NULL) {
+		return -EFAULT;
+	}
+
+	down_read(&mm->mmap_sem);
+	vma = find_vma(mm, ea);
+	if (!vma)
+		goto bad_area;
+	if (vma->vm_start <= ea)
+		goto good_area;
+	if (!(vma->vm_flags & VM_GROWSDOWN))
+		goto bad_area;
+	if (expand_stack(vma, ea))
+		goto bad_area;
+good_area:
+	is_write = dsisr & MFC_DSISR_ACCESS_PUT;
+	if (is_write) {
+		if (!(vma->vm_flags & VM_WRITE))
+			goto bad_area;
+	} else {
+		if (dsisr & MFC_DSISR_ACCESS_DENIED)
+			goto bad_area;
+		if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
+			goto bad_area;
+	}
+	ret = 0;
+	switch (handle_mm_fault(mm, vma, ea, is_write)) {
+	case VM_FAULT_MINOR:
+		current->min_flt++;
+		break;
+	case VM_FAULT_MAJOR:
+		current->maj_flt++;
+		break;
+	case VM_FAULT_SIGBUS:
+		ret = -EFAULT;
+		goto bad_area;
+	case VM_FAULT_OOM:
+		ret = -ENOMEM;
+		goto bad_area;
+	default:
+		BUG();
+	}
+	up_read(&mm->mmap_sem);
+	return ret;
+
+bad_area:
+	up_read(&mm->mmap_sem);
+	return -EFAULT;
+}
+
+static void spufs_handle_dma_error(struct spu_context *ctx,
+				unsigned long ea, int type)
+{
+	if (ctx->flags & SPU_CREATE_EVENTS_ENABLED) {
+		ctx->event_return |= type;
+		wake_up_all(&ctx->stop_wq);
+	} else {
+		siginfo_t info;
+		memset(&info, 0, sizeof(info));
+
+		switch (type) {
+		case SPE_EVENT_INVALID_DMA:
+			info.si_signo = SIGBUS;
+			info.si_code = BUS_OBJERR;
+			break;
+		case SPE_EVENT_SPE_DATA_STORAGE:
+			info.si_signo = SIGBUS;
+			info.si_addr = (void __user *)ea;
+			info.si_code = BUS_ADRERR;
+			break;
+		case SPE_EVENT_DMA_ALIGNMENT:
+			info.si_signo = SIGBUS;
+			/* DAR isn't set for an alignment fault :( */
+			info.si_code = BUS_ADRALN;
+			break;
+		case SPE_EVENT_SPE_ERROR:
+			info.si_signo = SIGILL;
+			info.si_addr = (void __user *)(unsigned long)
+				ctx->ops->npc_read(ctx) - 4;
+			info.si_code = ILL_ILLOPC;
+			break;
+		}
+		if (info.si_signo)
+			force_sig_info(info.si_signo, &info, current);
+	}
+}
+
+void spufs_dma_callback(struct spu *spu, int type)
+{
+	spufs_handle_dma_error(spu->ctx, spu->dar, type);
+}
+EXPORT_SYMBOL_GPL(spufs_dma_callback);
+
+/*
+ * bottom half handler for page faults, we can't do this from
+ * interrupt context, since we might need to sleep.
+ * we also need to give up the mutex so we can get scheduled
+ * out while waiting for the backing store.
+ *
+ * TODO: try calling hash_page from the interrupt handler first
+ *       in order to speed up the easy case.
+ */
+int spufs_handle_class1(struct spu_context *ctx)
+{
+	u64 ea, dsisr, access;
+	unsigned long flags;
+	int ret;
+
+	/*
+	 * dar and dsisr get passed from the registers
+	 * to the spu_context, to this function, but not
+	 * back to the spu if it gets scheduled again.
+	 *
+	 * if we don't handle the fault for a saved context
+	 * in time, we can still expect to get the same fault
+	 * the immediately after the context restore.
+	 */
+	if (ctx->state == SPU_STATE_RUNNABLE) {
+		ea = ctx->spu->dar;
+		dsisr = ctx->spu->dsisr;
+		ctx->spu->dar= ctx->spu->dsisr = 0;
+	} else {
+		ea = ctx->csa.priv1.mfc_dar_RW;
+		dsisr = ctx->csa.priv1.mfc_dsisr_RW;
+		ctx->csa.priv1.mfc_dar_RW = 0;
+		ctx->csa.priv1.mfc_dsisr_RW = 0;
+	}
+
+	if (!(dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED)))
+		return 0;
+
+	pr_debug("ctx %p: ea %016lx, dsisr %016lx state %d\n", ctx, ea,
+		dsisr, ctx->state);
+
+	/* we must not hold the lock when entering spu_handle_mm_fault */
+	spu_release(ctx);
+
+	access = (_PAGE_PRESENT | _PAGE_USER);
+	access |= (dsisr & MFC_DSISR_ACCESS_PUT) ? _PAGE_RW : 0UL;
+	local_irq_save(flags);
+	ret = hash_page(ea, access, 0x300);
+	local_irq_restore(flags);
+
+	/* hashing failed, so try the actual fault handler */
+	if (ret)
+		ret = spu_handle_mm_fault(current->mm, ea, dsisr);
+
+	spu_acquire(ctx);
+	/*
+	 * If we handled the fault successfully and are in runnable
+	 * state, restart the DMA.
+	 * In case of unhandled error report the problem to user space.
+	 */
+	if (!ret) {
+		if (ctx->spu)
+			ctx->ops->restart_dma(ctx);
+	} else
+		spufs_handle_dma_error(ctx, ea, SPE_EVENT_SPE_DATA_STORAGE);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(spufs_handle_class1);
diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c
index 505266a..d010b24 100644
--- a/arch/powerpc/platforms/cell/spufs/file.c
+++ b/arch/powerpc/platforms/cell/spufs/file.c
@@ -44,9 +44,25 @@
 {
 	struct spufs_inode_info *i = SPUFS_I(inode);
 	struct spu_context *ctx = i->i_ctx;
+
+	spin_lock(&ctx->mapping_lock);
 	file->private_data = ctx;
-	ctx->local_store = inode->i_mapping;
-	smp_wmb();
+	if (!i->i_openers++)
+		ctx->local_store = inode->i_mapping;
+	spin_unlock(&ctx->mapping_lock);
+	return 0;
+}
+
+static int
+spufs_mem_release(struct inode *inode, struct file *file)
+{
+	struct spufs_inode_info *i = SPUFS_I(inode);
+	struct spu_context *ctx = i->i_ctx;
+
+	spin_lock(&ctx->mapping_lock);
+	if (!--i->i_openers)
+		ctx->local_store = NULL;
+	spin_unlock(&ctx->mapping_lock);
 	return 0;
 }
 
@@ -149,6 +165,7 @@
 
 static const struct file_operations spufs_mem_fops = {
 	.open	 = spufs_mem_open,
+	.release = spufs_mem_release,
 	.read    = spufs_mem_read,
 	.write   = spufs_mem_write,
 	.llseek  = generic_file_llseek,
@@ -238,16 +255,33 @@
 	struct spufs_inode_info *i = SPUFS_I(inode);
 	struct spu_context *ctx = i->i_ctx;
 
+	spin_lock(&ctx->mapping_lock);
 	file->private_data = ctx;
-	ctx->cntl = inode->i_mapping;
-	smp_wmb();
+	if (!i->i_openers++)
+		ctx->cntl = inode->i_mapping;
+	spin_unlock(&ctx->mapping_lock);
 	return simple_attr_open(inode, file, spufs_cntl_get,
 					spufs_cntl_set, "0x%08lx");
 }
 
+static int
+spufs_cntl_release(struct inode *inode, struct file *file)
+{
+	struct spufs_inode_info *i = SPUFS_I(inode);
+	struct spu_context *ctx = i->i_ctx;
+
+	simple_attr_close(inode, file);
+
+	spin_lock(&ctx->mapping_lock);
+	if (!--i->i_openers)
+		ctx->cntl = NULL;
+	spin_unlock(&ctx->mapping_lock);
+	return 0;
+}
+
 static const struct file_operations spufs_cntl_fops = {
 	.open = spufs_cntl_open,
-	.release = simple_attr_close,
+	.release = spufs_cntl_release,
 	.read = simple_attr_read,
 	.write = simple_attr_write,
 	.mmap = spufs_cntl_mmap,
@@ -723,12 +757,28 @@
 {
 	struct spufs_inode_info *i = SPUFS_I(inode);
 	struct spu_context *ctx = i->i_ctx;
+
+	spin_lock(&ctx->mapping_lock);
 	file->private_data = ctx;
-	ctx->signal1 = inode->i_mapping;
-	smp_wmb();
+	if (!i->i_openers++)
+		ctx->signal1 = inode->i_mapping;
+	spin_unlock(&ctx->mapping_lock);
 	return nonseekable_open(inode, file);
 }
 
+static int
+spufs_signal1_release(struct inode *inode, struct file *file)
+{
+	struct spufs_inode_info *i = SPUFS_I(inode);
+	struct spu_context *ctx = i->i_ctx;
+
+	spin_lock(&ctx->mapping_lock);
+	if (!--i->i_openers)
+		ctx->signal1 = NULL;
+	spin_unlock(&ctx->mapping_lock);
+	return 0;
+}
+
 static ssize_t __spufs_signal1_read(struct spu_context *ctx, char __user *buf,
 			size_t len, loff_t *pos)
 {
@@ -821,6 +871,7 @@
 
 static const struct file_operations spufs_signal1_fops = {
 	.open = spufs_signal1_open,
+	.release = spufs_signal1_release,
 	.read = spufs_signal1_read,
 	.write = spufs_signal1_write,
 	.mmap = spufs_signal1_mmap,
@@ -830,12 +881,28 @@
 {
 	struct spufs_inode_info *i = SPUFS_I(inode);
 	struct spu_context *ctx = i->i_ctx;
+
+	spin_lock(&ctx->mapping_lock);
 	file->private_data = ctx;
-	ctx->signal2 = inode->i_mapping;
-	smp_wmb();
+	if (!i->i_openers++)
+		ctx->signal2 = inode->i_mapping;
+	spin_unlock(&ctx->mapping_lock);
 	return nonseekable_open(inode, file);
 }
 
+static int
+spufs_signal2_release(struct inode *inode, struct file *file)
+{
+	struct spufs_inode_info *i = SPUFS_I(inode);
+	struct spu_context *ctx = i->i_ctx;
+
+	spin_lock(&ctx->mapping_lock);
+	if (!--i->i_openers)
+		ctx->signal2 = NULL;
+	spin_unlock(&ctx->mapping_lock);
+	return 0;
+}
+
 static ssize_t __spufs_signal2_read(struct spu_context *ctx, char __user *buf,
 			size_t len, loff_t *pos)
 {
@@ -932,6 +999,7 @@
 
 static const struct file_operations spufs_signal2_fops = {
 	.open = spufs_signal2_open,
+	.release = spufs_signal2_release,
 	.read = spufs_signal2_read,
 	.write = spufs_signal2_write,
 	.mmap = spufs_signal2_mmap,
@@ -1031,13 +1099,30 @@
 	struct spu_context *ctx = i->i_ctx;
 
 	file->private_data = i->i_ctx;
-	ctx->mss = inode->i_mapping;
-	smp_wmb();
+
+	spin_lock(&ctx->mapping_lock);
+	if (!i->i_openers++)
+		ctx->mss = inode->i_mapping;
+	spin_unlock(&ctx->mapping_lock);
 	return nonseekable_open(inode, file);
 }
 
+static int
+spufs_mss_release(struct inode *inode, struct file *file)
+{
+	struct spufs_inode_info *i = SPUFS_I(inode);
+	struct spu_context *ctx = i->i_ctx;
+
+	spin_lock(&ctx->mapping_lock);
+	if (!--i->i_openers)
+		ctx->mss = NULL;
+	spin_unlock(&ctx->mapping_lock);
+	return 0;
+}
+
 static const struct file_operations spufs_mss_fops = {
 	.open	 = spufs_mss_open,
+	.release = spufs_mss_release,
 	.mmap	 = spufs_mss_mmap,
 };
 
@@ -1072,14 +1157,30 @@
 	struct spufs_inode_info *i = SPUFS_I(inode);
 	struct spu_context *ctx = i->i_ctx;
 
+	spin_lock(&ctx->mapping_lock);
 	file->private_data = i->i_ctx;
-	ctx->psmap = inode->i_mapping;
-	smp_wmb();
+	if (!i->i_openers++)
+		ctx->psmap = inode->i_mapping;
+	spin_unlock(&ctx->mapping_lock);
 	return nonseekable_open(inode, file);
 }
 
+static int
+spufs_psmap_release(struct inode *inode, struct file *file)
+{
+	struct spufs_inode_info *i = SPUFS_I(inode);
+	struct spu_context *ctx = i->i_ctx;
+
+	spin_lock(&ctx->mapping_lock);
+	if (!--i->i_openers)
+		ctx->psmap = NULL;
+	spin_unlock(&ctx->mapping_lock);
+	return 0;
+}
+
 static const struct file_operations spufs_psmap_fops = {
 	.open	 = spufs_psmap_open,
+	.release = spufs_psmap_release,
 	.mmap	 = spufs_psmap_mmap,
 };
 
@@ -1126,12 +1227,27 @@
 	if (atomic_read(&inode->i_count) != 1)
 		return -EBUSY;
 
+	spin_lock(&ctx->mapping_lock);
 	file->private_data = ctx;
-	ctx->mfc = inode->i_mapping;
-	smp_wmb();
+	if (!i->i_openers++)
+		ctx->mfc = inode->i_mapping;
+	spin_unlock(&ctx->mapping_lock);
 	return nonseekable_open(inode, file);
 }
 
+static int
+spufs_mfc_release(struct inode *inode, struct file *file)
+{
+	struct spufs_inode_info *i = SPUFS_I(inode);
+	struct spu_context *ctx = i->i_ctx;
+
+	spin_lock(&ctx->mapping_lock);
+	if (!--i->i_openers)
+		ctx->mfc = NULL;
+	spin_unlock(&ctx->mapping_lock);
+	return 0;
+}
+
 /* interrupt-level mfc callback function. */
 void spufs_mfc_callback(struct spu *spu)
 {
@@ -1313,7 +1429,10 @@
 	if (ret)
 		goto out;
 
-	spu_acquire_runnable(ctx, 0);
+	ret = spu_acquire_runnable(ctx, 0);
+	if (ret)
+		goto out;
+
 	if (file->f_flags & O_NONBLOCK) {
 		ret = ctx->ops->send_mfc_command(ctx, &cmd);
 	} else {
@@ -1399,6 +1518,7 @@
 
 static const struct file_operations spufs_mfc_fops = {
 	.open	 = spufs_mfc_open,
+	.release = spufs_mfc_release,
 	.read	 = spufs_mfc_read,
 	.write	 = spufs_mfc_write,
 	.poll	 = spufs_mfc_poll,
diff --git a/arch/powerpc/platforms/cell/spufs/hw_ops.c b/arch/powerpc/platforms/cell/spufs/hw_ops.c
index ae42e03..428875c 100644
--- a/arch/powerpc/platforms/cell/spufs/hw_ops.c
+++ b/arch/powerpc/platforms/cell/spufs/hw_ops.c
@@ -296,6 +296,14 @@
 	}
 }
 
+static void spu_hw_restart_dma(struct spu_context *ctx)
+{
+	struct spu_priv2 __iomem *priv2 = ctx->spu->priv2;
+
+	if (!test_bit(SPU_CONTEXT_SWITCH_PENDING, &ctx->spu->flags))
+		out_be64(&priv2->mfc_control_RW, MFC_CNTL_RESTART_DMA_COMMAND);
+}
+
 struct spu_context_ops spu_hw_ops = {
 	.mbox_read = spu_hw_mbox_read,
 	.mbox_stat_read = spu_hw_mbox_stat_read,
@@ -320,4 +328,5 @@
 	.read_mfc_tagstatus = spu_hw_read_mfc_tagstatus,
 	.get_mfc_free_elements = spu_hw_get_mfc_free_elements,
 	.send_mfc_command = spu_hw_send_mfc_command,
+	.restart_dma = spu_hw_restart_dma,
 };
diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c
index 8079983..13e4f70 100644
--- a/arch/powerpc/platforms/cell/spufs/inode.c
+++ b/arch/powerpc/platforms/cell/spufs/inode.c
@@ -36,6 +36,7 @@
 #include <asm/prom.h>
 #include <asm/semaphore.h>
 #include <asm/spu.h>
+#include <asm/spu_priv1.h>
 #include <asm/uaccess.h>
 
 #include "spufs.h"
@@ -54,6 +55,7 @@
 
 	ei->i_gang = NULL;
 	ei->i_ctx = NULL;
+	ei->i_openers = 0;
 
 	return &ei->vfs_inode;
 }
@@ -520,13 +522,14 @@
 
 /* File system initialization */
 enum {
-	Opt_uid, Opt_gid, Opt_err,
+	Opt_uid, Opt_gid, Opt_mode, Opt_err,
 };
 
 static match_table_t spufs_tokens = {
-	{ Opt_uid, "uid=%d" },
-	{ Opt_gid, "gid=%d" },
-	{ Opt_err, NULL  },
+	{ Opt_uid,  "uid=%d" },
+	{ Opt_gid,  "gid=%d" },
+	{ Opt_mode, "mode=%o" },
+	{ Opt_err,   NULL  },
 };
 
 static int
@@ -553,6 +556,11 @@
 				return 0;
 			root->i_gid = option;
 			break;
+		case Opt_mode:
+			if (match_octal(&args[0], &option))
+				return 0;
+			root->i_mode = option | S_IFDIR;
+			break;
 		default:
 			return 0;
 		}
@@ -560,6 +568,11 @@
 	return 1;
 }
 
+static void spufs_exit_isolated_loader(void)
+{
+	kfree(isolated_loader);
+}
+
 static void
 spufs_init_isolated_loader(void)
 {
@@ -571,7 +584,7 @@
 	if (!dn)
 		return;
 
-	loader = get_property(dn, "loader", &size);
+	loader = of_get_property(dn, "loader", &size);
 	if (!loader)
 		return;
 
@@ -653,6 +666,10 @@
 {
 	int ret;
 
+	ret = -ENODEV;
+	if (!spu_management_ops)
+		goto out;
+
 	ret = -ENOMEM;
 	spufs_inode_cache = kmem_cache_create("spufs_inode_cache",
 			sizeof(struct spufs_inode_info), 0,
@@ -660,25 +677,29 @@
 
 	if (!spufs_inode_cache)
 		goto out;
-	if (spu_sched_init() != 0) {
-		kmem_cache_destroy(spufs_inode_cache);
-		goto out;
-	}
-	ret = register_filesystem(&spufs_type);
+	ret = spu_sched_init();
 	if (ret)
 		goto out_cache;
+	ret = register_filesystem(&spufs_type);
+	if (ret)
+		goto out_sched;
 	ret = register_spu_syscalls(&spufs_calls);
 	if (ret)
 		goto out_fs;
 	ret = register_arch_coredump_calls(&spufs_coredump_calls);
 	if (ret)
-		goto out_fs;
+		goto out_syscalls;
 
 	spufs_init_isolated_loader();
 
 	return 0;
+
+out_syscalls:
+	unregister_spu_syscalls(&spufs_calls);
 out_fs:
 	unregister_filesystem(&spufs_type);
+out_sched:
+	spu_sched_exit();
 out_cache:
 	kmem_cache_destroy(spufs_inode_cache);
 out:
@@ -689,6 +710,7 @@
 static void __exit spufs_exit(void)
 {
 	spu_sched_exit();
+	spufs_exit_isolated_loader();
 	unregister_arch_coredump_calls(&spufs_coredump_calls);
 	unregister_spu_syscalls(&spufs_calls);
 	unregister_filesystem(&spufs_type);
diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c
index f95a611..5762660 100644
--- a/arch/powerpc/platforms/cell/spufs/run.c
+++ b/arch/powerpc/platforms/cell/spufs/run.c
@@ -18,27 +18,6 @@
 	wake_up_all(&ctx->stop_wq);
 }
 
-void spufs_dma_callback(struct spu *spu, int type)
-{
-	struct spu_context *ctx = spu->ctx;
-
-	if (ctx->flags & SPU_CREATE_EVENTS_ENABLED) {
-		ctx->event_return |= type;
-		wake_up_all(&ctx->stop_wq);
-	} else {
-		switch (type) {
-		case SPE_EVENT_DMA_ALIGNMENT:
-		case SPE_EVENT_SPE_DATA_STORAGE:
-		case SPE_EVENT_INVALID_DMA:
-			force_sig(SIGBUS, /* info, */ current);
-			break;
-		case SPE_EVENT_SPE_ERROR:
-			force_sig(SIGILL, /* info */ current);
-			break;
-		}
-	}
-}
-
 static inline int spu_stopped(struct spu_context *ctx, u32 * stat)
 {
 	struct spu *spu;
@@ -63,13 +42,18 @@
 	const u32 status_loading = SPU_STATUS_RUNNING
 		| SPU_STATUS_ISOLATED_STATE | SPU_STATUS_ISOLATED_LOAD_STATUS;
 
+	ret = -ENODEV;
 	if (!isolated_loader)
-		return -ENODEV;
-
-	ret = spu_acquire_exclusive(ctx);
-	if (ret)
 		goto out;
 
+	/*
+	 * We need to exclude userspace access to the context.
+	 *
+	 * To protect against memory access we invalidate all ptes
+	 * and make sure the pagefault handlers block on the mutex.
+	 */
+	spu_unmap_mappings(ctx);
+
 	mfc_cntl = &ctx->spu->priv2->mfc_control_RW;
 
 	/* purge the MFC DMA queue to ensure no spurious accesses before we
@@ -82,7 +66,7 @@
 			printk(KERN_ERR "%s: timeout flushing MFC DMA queue\n",
 					__FUNCTION__);
 			ret = -EIO;
-			goto out_unlock;
+			goto out;
 		}
 		cond_resched();
 	}
@@ -119,12 +103,15 @@
 		pr_debug("%s: isolated LOAD failed\n", __FUNCTION__);
 		ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
 		ret = -EACCES;
+		goto out_drop_priv;
+	}
 
-	} else if (!(status & SPU_STATUS_ISOLATED_STATE)) {
+	if (!(status & SPU_STATUS_ISOLATED_STATE)) {
 		/* This isn't allowed by the CBEA, but check anyway */
 		pr_debug("%s: SPU fell out of isolated mode?\n", __FUNCTION__);
 		ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_STOP);
 		ret = -EINVAL;
+		goto out_drop_priv;
 	}
 
 out_drop_priv:
@@ -132,30 +119,19 @@
 	sr1 |= MFC_STATE1_PROBLEM_STATE_MASK;
 	spu_mfc_sr1_set(ctx->spu, sr1);
 
-out_unlock:
-	spu_release(ctx);
 out:
 	return ret;
 }
 
-static inline int spu_run_init(struct spu_context *ctx, u32 * npc)
+static int spu_run_init(struct spu_context *ctx, u32 * npc)
 {
-	int ret;
-	unsigned long runcntl = SPU_RUNCNTL_RUNNABLE;
-
-	ret = spu_acquire_runnable(ctx, 0);
-	if (ret)
-		return ret;
-
 	if (ctx->flags & SPU_CREATE_ISOLATE) {
+		unsigned long runcntl;
+
 		if (!(ctx->ops->status_read(ctx) & SPU_STATUS_ISOLATED_STATE)) {
-			/* Need to release ctx, because spu_setup_isolated will
-			 * acquire it exclusively.
-			 */
-			spu_release(ctx);
-			ret = spu_setup_isolated(ctx);
-			if (!ret)
-				ret = spu_acquire_runnable(ctx, 0);
+			int ret = spu_setup_isolated(ctx);
+			if (ret)
+				return ret;
 		}
 
 		/* if userspace has set the runcntrl register (eg, to issue an
@@ -164,16 +140,17 @@
 			(SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE);
 		if (runcntl == 0)
 			runcntl = SPU_RUNCNTL_RUNNABLE;
+		ctx->ops->runcntl_write(ctx, runcntl);
 	} else {
 		spu_start_tick(ctx);
 		ctx->ops->npc_write(ctx, *npc);
+		ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
 	}
 
-	ctx->ops->runcntl_write(ctx, runcntl);
-	return ret;
+	return 0;
 }
 
-static inline int spu_run_fini(struct spu_context *ctx, u32 * npc,
+static int spu_run_fini(struct spu_context *ctx, u32 * npc,
 			       u32 * status)
 {
 	int ret = 0;
@@ -189,19 +166,27 @@
 	return ret;
 }
 
-static inline int spu_reacquire_runnable(struct spu_context *ctx, u32 *npc,
+static int spu_reacquire_runnable(struct spu_context *ctx, u32 *npc,
 				         u32 *status)
 {
 	int ret;
 
-	if ((ret = spu_run_fini(ctx, npc, status)) != 0)
+	ret = spu_run_fini(ctx, npc, status);
+	if (ret)
 		return ret;
-	if (*status & (SPU_STATUS_STOPPED_BY_STOP |
-		       SPU_STATUS_STOPPED_BY_HALT)) {
+
+	if (*status & (SPU_STATUS_STOPPED_BY_STOP | SPU_STATUS_STOPPED_BY_HALT))
 		return *status;
-	}
-	if ((ret = spu_run_init(ctx, npc)) != 0)
+
+	ret = spu_acquire_runnable(ctx, 0);
+	if (ret)
 		return ret;
+
+	ret = spu_run_init(ctx, npc);
+	if (ret) {
+		spu_release(ctx);
+		return ret;
+	}
 	return 0;
 }
 
@@ -253,17 +238,17 @@
 {
 	struct spu_syscall_block s;
 	u32 ls_pointer, npc;
-	char *ls;
+	void __iomem *ls;
 	long spu_ret;
 	int ret;
 
 	/* get syscall block from local store */
-	npc = ctx->ops->npc_read(ctx);
-	ls = ctx->ops->get_ls(ctx);
-	ls_pointer = *(u32*)(ls + npc);
+	npc = ctx->ops->npc_read(ctx) & ~3;
+	ls = (void __iomem *)ctx->ops->get_ls(ctx);
+	ls_pointer = in_be32(ls + npc);
 	if (ls_pointer > (LS_SIZE - sizeof(s)))
 		return -EFAULT;
-	memcpy(&s, ls + ls_pointer, sizeof (s));
+	memcpy_fromio(&s, ls + ls_pointer, sizeof(s));
 
 	/* do actual syscall without pinning the spu */
 	ret = 0;
@@ -283,7 +268,7 @@
 	}
 
 	/* write result, jump over indirect pointer */
-	memcpy(ls + ls_pointer, &spu_ret, sizeof (spu_ret));
+	memcpy_toio(ls + ls_pointer, &spu_ret, sizeof(spu_ret));
 	ctx->ops->npc_write(ctx, npc);
 	ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
 	return ret;
@@ -292,11 +277,8 @@
 static inline int spu_process_events(struct spu_context *ctx)
 {
 	struct spu *spu = ctx->spu;
-	u64 pte_fault = MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED;
 	int ret = 0;
 
-	if (spu->dsisr & pte_fault)
-		ret = spu_irq_class_1_bottom(spu);
 	if (spu->class_0_pending)
 		ret = spu_irq_class_0_bottom(spu);
 	if (!ret && signal_pending(current))
@@ -310,14 +292,21 @@
 	int ret;
 	u32 status;
 
-	if (down_interruptible(&ctx->run_sema))
+	if (mutex_lock_interruptible(&ctx->run_mutex))
 		return -ERESTARTSYS;
 
 	ctx->ops->master_start(ctx);
 	ctx->event_return = 0;
-	ret = spu_run_init(ctx, npc);
+
+	ret = spu_acquire_runnable(ctx, 0);
 	if (ret)
+		return ret;
+
+	ret = spu_run_init(ctx, npc);
+	if (ret) {
+		spu_release(ctx);
 		goto out;
+	}
 
 	do {
 		ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status));
@@ -330,6 +319,10 @@
 				break;
 			status &= ~SPU_STATUS_STOPPED_BY_STOP;
 		}
+		ret = spufs_handle_class1(ctx);
+		if (ret)
+			break;
+
 		if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) {
 			ret = spu_reacquire_runnable(ctx, npc, &status);
 			if (ret) {
@@ -363,6 +356,6 @@
 
 out:
 	*event = ctx->event_return;
-	up(&ctx->run_sema);
+	mutex_unlock(&ctx->run_mutex);
 	return ret;
 }
diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c
index 39823ce..91030b8 100644
--- a/arch/powerpc/platforms/cell/spufs/sched.c
+++ b/arch/powerpc/platforms/cell/spufs/sched.c
@@ -71,14 +71,27 @@
 
 void spu_start_tick(struct spu_context *ctx)
 {
-	if (ctx->policy == SCHED_RR)
+	if (ctx->policy == SCHED_RR) {
+		/*
+		 * Make sure the exiting bit is cleared.
+		 */
+		clear_bit(SPU_SCHED_EXITING, &ctx->sched_flags);
+		mb();
 		queue_delayed_work(spu_sched_wq, &ctx->sched_work, SPU_TIMESLICE);
+	}
 }
 
 void spu_stop_tick(struct spu_context *ctx)
 {
-	if (ctx->policy == SCHED_RR)
+	if (ctx->policy == SCHED_RR) {
+		/*
+		 * While the work can be rearming normally setting this flag
+		 * makes sure it does not rearm itself anymore.
+		 */
+		set_bit(SPU_SCHED_EXITING, &ctx->sched_flags);
+		mb();
 		cancel_delayed_work(&ctx->sched_work);
+	}
 }
 
 void spu_sched_tick(struct work_struct *work)
@@ -86,7 +99,15 @@
 	struct spu_context *ctx =
 		container_of(work, struct spu_context, sched_work.work);
 	struct spu *spu;
-	int rearm = 1;
+	int preempted = 0;
+
+	/*
+	 * If this context is being stopped avoid rescheduling from the
+	 * scheduler tick because we would block on the state_mutex.
+	 * The caller will yield the spu later on anyway.
+	 */
+	if (test_bit(SPU_SCHED_EXITING, &ctx->sched_flags))
+		return;
 
 	mutex_lock(&ctx->state_mutex);
 	spu = ctx->spu;
@@ -94,12 +115,19 @@
 		int best = sched_find_first_bit(spu_prio->bitmap);
 		if (best <= ctx->prio) {
 			spu_deactivate(ctx);
-			rearm = 0;
+			preempted = 1;
 		}
 	}
 	mutex_unlock(&ctx->state_mutex);
 
-	if (rearm)
+	if (preempted) {
+		/*
+		 * We need to break out of the wait loop in spu_run manually
+		 * to ensure this context gets put on the runqueue again
+		 * ASAP.
+		 */
+		wake_up(&ctx->stop_wq);
+	} else
 		spu_start_tick(ctx);
 }
 
@@ -208,58 +236,40 @@
  * spu_add_to_rq - add a context to the runqueue
  * @ctx:       context to add
  */
-static void spu_add_to_rq(struct spu_context *ctx)
+static void __spu_add_to_rq(struct spu_context *ctx)
 {
-	spin_lock(&spu_prio->runq_lock);
-	list_add_tail(&ctx->rq, &spu_prio->runq[ctx->prio]);
-	set_bit(ctx->prio, spu_prio->bitmap);
-	spin_unlock(&spu_prio->runq_lock);
+	int prio = ctx->prio;
+
+	list_add_tail(&ctx->rq, &spu_prio->runq[prio]);
+	set_bit(prio, spu_prio->bitmap);
 }
 
-/**
- * spu_del_from_rq - remove a context from the runqueue
- * @ctx:       context to remove
- */
-static void spu_del_from_rq(struct spu_context *ctx)
+static void __spu_del_from_rq(struct spu_context *ctx)
 {
-	spin_lock(&spu_prio->runq_lock);
-	list_del_init(&ctx->rq);
-	if (list_empty(&spu_prio->runq[ctx->prio]))
-		clear_bit(ctx->prio, spu_prio->bitmap);
-	spin_unlock(&spu_prio->runq_lock);
-}
+	int prio = ctx->prio;
 
-/**
- * spu_grab_context - remove one context from the runqueue
- * @prio:      priority of the context to be removed
- *
- * This function removes one context from the runqueue for priority @prio.
- * If there is more than one context with the given priority the first
- * task on the runqueue will be taken.
- *
- * Returns the spu_context it just removed.
- *
- * Must be called with spu_prio->runq_lock held.
- */
-static struct spu_context *spu_grab_context(int prio)
-{
-	struct list_head *rq = &spu_prio->runq[prio];
-
-	if (list_empty(rq))
-		return NULL;
-	return list_entry(rq->next, struct spu_context, rq);
+	if (!list_empty(&ctx->rq))
+		list_del_init(&ctx->rq);
+	if (list_empty(&spu_prio->runq[prio]))
+		clear_bit(prio, spu_prio->bitmap);
 }
 
 static void spu_prio_wait(struct spu_context *ctx)
 {
 	DEFINE_WAIT(wait);
 
+	spin_lock(&spu_prio->runq_lock);
 	prepare_to_wait_exclusive(&ctx->stop_wq, &wait, TASK_INTERRUPTIBLE);
 	if (!signal_pending(current)) {
+		__spu_add_to_rq(ctx);
+		spin_unlock(&spu_prio->runq_lock);
 		mutex_unlock(&ctx->state_mutex);
 		schedule();
 		mutex_lock(&ctx->state_mutex);
+		spin_lock(&spu_prio->runq_lock);
+		__spu_del_from_rq(ctx);
 	}
+	spin_unlock(&spu_prio->runq_lock);
 	__set_current_state(TASK_RUNNING);
 	remove_wait_queue(&ctx->stop_wq, &wait);
 }
@@ -280,9 +290,14 @@
 	spin_lock(&spu_prio->runq_lock);
 	best = sched_find_first_bit(spu_prio->bitmap);
 	if (best < MAX_PRIO) {
-		struct spu_context *ctx = spu_grab_context(best);
-		if (ctx)
-			wake_up(&ctx->stop_wq);
+		struct list_head *rq = &spu_prio->runq[best];
+		struct spu_context *ctx;
+
+		BUG_ON(list_empty(rq));
+
+		ctx = list_entry(rq->next, struct spu_context, rq);
+		__spu_del_from_rq(ctx);
+		wake_up(&ctx->stop_wq);
 	}
 	spin_unlock(&spu_prio->runq_lock);
 }
@@ -365,6 +380,12 @@
 			}
 			spu_unbind_context(spu, victim);
 			mutex_unlock(&victim->state_mutex);
+			/*
+			 * We need to break out of the wait loop in spu_run
+			 * manually to ensure this context gets put on the
+			 * runqueue again ASAP.
+			 */
+			wake_up(&victim->stop_wq);
 			return spu;
 		}
 	}
@@ -377,7 +398,7 @@
  * @ctx:	spu context to schedule
  * @flags:	flags (currently ignored)
  *
- * Tries to find a free spu to run @ctx.  If no free spu is availble
+ * Tries to find a free spu to run @ctx.  If no free spu is available
  * add the context to the runqueue so it gets woken up once an spu
  * is available.
  */
@@ -402,9 +423,7 @@
 			return 0;
 		}
 
-		spu_add_to_rq(ctx);
 		spu_prio_wait(ctx);
-		spu_del_from_rq(ctx);
 	} while (!signal_pending(current));
 
 	return -ERESTARTSYS;
@@ -438,7 +457,6 @@
 void spu_yield(struct spu_context *ctx)
 {
 	struct spu *spu;
-	int need_yield = 0;
 
 	if (mutex_trylock(&ctx->state_mutex)) {
 		if ((spu = ctx->spu) != NULL) {
@@ -447,13 +465,10 @@
 				pr_debug("%s: yielding SPU %d NODE %d\n",
 					 __FUNCTION__, spu->number, spu->node);
 				spu_deactivate(ctx);
-				need_yield = 1;
 			}
 		}
 		mutex_unlock(&ctx->state_mutex);
 	}
-	if (unlikely(need_yield))
-		yield();
 }
 
 int __init spu_sched_init(void)
diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h
index 5c4e47d..0a947fd 100644
--- a/arch/powerpc/platforms/cell/spufs/spufs.h
+++ b/arch/powerpc/platforms/cell/spufs/spufs.h
@@ -41,7 +41,7 @@
 
 /* ctx->sched_flags */
 enum {
-	SPU_SCHED_WAKE = 0, /* currently unused */
+	SPU_SCHED_EXITING = 0,
 };
 
 struct spu_context {
@@ -50,16 +50,17 @@
 	spinlock_t mmio_lock;		  /* protects mmio access */
 	struct address_space *local_store; /* local store mapping.  */
 	struct address_space *mfc;	   /* 'mfc' area mappings. */
-	struct address_space *cntl; 	   /* 'control' area mappings. */
-	struct address_space *signal1; 	   /* 'signal1' area mappings. */
-	struct address_space *signal2; 	   /* 'signal2' area mappings. */
-	struct address_space *mss; 	   /* 'mss' area mappings. */
-	struct address_space *psmap; 	   /* 'psmap' area mappings. */
+	struct address_space *cntl;	   /* 'control' area mappings. */
+	struct address_space *signal1;	   /* 'signal1' area mappings. */
+	struct address_space *signal2;	   /* 'signal2' area mappings. */
+	struct address_space *mss;	   /* 'mss' area mappings. */
+	struct address_space *psmap;	   /* 'psmap' area mappings. */
+	spinlock_t mapping_lock;
 	u64 object_id;		   /* user space pointer for oprofile */
 
 	enum { SPU_STATE_RUNNABLE, SPU_STATE_SAVED } state;
 	struct mutex state_mutex;
-	struct semaphore run_sema;
+	struct mutex run_mutex;
 
 	struct mm_struct *owner;
 
@@ -140,6 +141,7 @@
 			       struct spu_dma_info * info);
 	void (*proxydma_info_read) (struct spu_context * ctx,
 				    struct spu_proxydma_info * info);
+	void (*restart_dma)(struct spu_context *ctx);
 };
 
 extern struct spu_context_ops spu_hw_ops;
@@ -149,6 +151,7 @@
 	struct spu_context *i_ctx;
 	struct spu_gang *i_gang;
 	struct inode vfs_inode;
+	int i_openers;
 };
 #define SPUFS_I(inode) \
 	container_of(inode, struct spufs_inode_info, vfs_inode)
@@ -170,6 +173,9 @@
 void spu_gang_remove_ctx(struct spu_gang *gang, struct spu_context *ctx);
 void spu_gang_add_ctx(struct spu_gang *gang, struct spu_context *ctx);
 
+/* fault handling */
+int spufs_handle_class1(struct spu_context *ctx);
+
 /* context management */
 static inline void spu_acquire(struct spu_context *ctx)
 {
@@ -190,7 +196,6 @@
 void spu_forget(struct spu_context *ctx);
 int spu_acquire_runnable(struct spu_context *ctx, unsigned long flags);
 void spu_acquire_saved(struct spu_context *ctx);
-int spu_acquire_exclusive(struct spu_context *ctx);
 
 int spu_activate(struct spu_context *ctx, unsigned long flags);
 void spu_deactivate(struct spu_context *ctx);
@@ -218,14 +223,13 @@
 		prepare_to_wait(&(wq), &__wait, TASK_INTERRUPTIBLE);	\
 		if (condition)						\
 			break;						\
-		if (!signal_pending(current)) {				\
-			spu_release(ctx);				\
-			schedule();					\
-			spu_acquire(ctx);				\
-			continue;					\
+		if (signal_pending(current)) {				\
+			__ret = -ERESTARTSYS;				\
+			break;						\
 		}							\
-		__ret = -ERESTARTSYS;					\
-		break;							\
+		spu_release(ctx);					\
+		schedule();						\
+		spu_acquire(ctx);					\
 	}								\
 	finish_wait(&(wq), &__wait);					\
 	__ret;								\
diff --git a/arch/powerpc/platforms/cell/spufs/switch.c b/arch/powerpc/platforms/cell/spufs/switch.c
index fd91c73..8347c4a 100644
--- a/arch/powerpc/platforms/cell/spufs/switch.c
+++ b/arch/powerpc/platforms/cell/spufs/switch.c
@@ -2084,6 +2084,10 @@
 	int rc;
 
 	acquire_spu_lock(spu);	        /* Step 1.     */
+	prev->dar = spu->dar;
+	prev->dsisr = spu->dsisr;
+	spu->dar = 0;
+	spu->dsisr = 0;
 	rc = __do_spu_save(prev, spu);	/* Steps 2-53. */
 	release_spu_lock(spu);
 	if (rc != 0 && rc != 2 && rc != 6) {
@@ -2109,9 +2113,9 @@
 
 	acquire_spu_lock(spu);
 	harvest(NULL, spu);
-	spu->dar = 0;
-	spu->dsisr = 0;
 	spu->slb_replace = 0;
+	new->dar = 0;
+	new->dsisr = 0;
 	spu->class_0_pending = 0;
 	rc = __do_spu_restore(new, spu);
 	release_spu_lock(spu);
diff --git a/arch/powerpc/platforms/celleb/Kconfig b/arch/powerpc/platforms/celleb/Kconfig
new file mode 100644
index 0000000..2db1e29
--- /dev/null
+++ b/arch/powerpc/platforms/celleb/Kconfig
@@ -0,0 +1,9 @@
+config PPC_CELLEB
+	bool "Toshiba's Cell Reference Set 'Celleb' Architecture"
+	depends on PPC_MULTIPLATFORM && PPC64
+	select PPC_CELL
+	select PPC_OF_PLATFORM_PCI
+	select HAS_TXX9_SERIAL
+	select PPC_UDBG_BEAT
+	select USB_OHCI_BIG_ENDIAN_MMIO
+	select USB_EHCI_BIG_ENDIAN_MMIO
diff --git a/arch/powerpc/platforms/celleb/iommu.c b/arch/powerpc/platforms/celleb/iommu.c
index f63b94c..755d869 100644
--- a/arch/powerpc/platforms/celleb/iommu.c
+++ b/arch/powerpc/platforms/celleb/iommu.c
@@ -37,7 +37,7 @@
 	const unsigned long *dma_window;
 
 	for_each_node_by_type(dn, "ioif") {
-		dma_window = get_property(dn, "toshiba,dma-window", NULL);
+		dma_window = of_get_property(dn, "toshiba,dma-window", NULL);
 		if (dma_window) {
 			*io_space_id = (dma_window[0] >> 32) & 0xffffffffUL;
 			*ioid = dma_window[0] & 0x7ffUL;
@@ -80,7 +80,7 @@
 	if (action != BUS_NOTIFY_ADD_DEVICE)
 		return 0;
 
-	dev->archdata.dma_ops = pci_dma_ops;
+	dev->archdata.dma_ops = get_pci_dma_ops();
 
 	return 0;
 }
@@ -95,7 +95,7 @@
 		return -ENODEV;
 
 	celleb_init_direct_mapping();
-	pci_dma_ops = &dma_direct_ops;
+	set_pci_dma_ops(&dma_direct_ops);
 	bus_register_notifier(&of_platform_bus_type, &celleb_of_bus_notifier);
 
 	return 0;
diff --git a/arch/powerpc/platforms/celleb/pci.c b/arch/powerpc/platforms/celleb/pci.c
index 98de836..d1adf34 100644
--- a/arch/powerpc/platforms/celleb/pci.c
+++ b/arch/powerpc/platforms/celleb/pci.c
@@ -309,13 +309,13 @@
 		goto error;
 	}
 
-	name = get_property(node, "model", &rlen);
+	name = of_get_property(node, "model", &rlen);
 	if (!name) {
 		printk(KERN_ERR "PCI: model property not found.\n");
 		goto error;
 	}
 
-	wi4 = get_property(node, "reg", &rlen);
+	wi4 = of_get_property(node, "reg", &rlen);
 	if (wi4 == NULL)
 		goto error;
 
@@ -352,10 +352,10 @@
 	}
 	pr_debug("PCI: res assigned 0x%016lx\n", (unsigned long)*res);
 
-	wi0 = get_property(node, "device-id", NULL);
-	wi1 = get_property(node, "vendor-id", NULL);
-	wi2 = get_property(node, "class-code", NULL);
-	wi3 = get_property(node, "revision-id", NULL);
+	wi0 = of_get_property(node, "device-id", NULL);
+	wi1 = of_get_property(node, "vendor-id", NULL);
+	wi2 = of_get_property(node, "class-code", NULL);
+	wi3 = of_get_property(node, "revision-id", NULL);
 
 	celleb_config_write_fake(*config, PCI_DEVICE_ID, 2, wi0[0] & 0xffff);
 	celleb_config_write_fake(*config, PCI_VENDOR_ID, 2, wi1[0] & 0xffff);
@@ -376,7 +376,7 @@
 
 	celleb_setup_pci_base_addrs(hose, devno, fn, num_base_addr);
 
-	li = get_property(node, "interrupts", &rlen);
+	li = of_get_property(node, "interrupts", &rlen);
 	val = li[0];
 	celleb_config_write_fake(*config, PCI_INTERRUPT_PIN, 1, 1);
 	celleb_config_write_fake(*config, PCI_INTERRUPT_LINE, 1, val);
@@ -424,7 +424,7 @@
 	const int *bus_range;
 	unsigned int len;
 
-	bus_range = get_property(dev, "bus-range", &len);
+	bus_range = of_get_property(dev, "bus-range", &len);
 	if (bus_range == NULL || len < 2 * sizeof(int))
 		return 1;
 
@@ -451,7 +451,7 @@
 	struct device_node *node;
 	unsigned int rlen;
 
-	name = get_property(dev, "name", &rlen);
+	name = of_get_property(dev, "name", &rlen);
 	if (!name)
 		return 1;
 
diff --git a/arch/powerpc/platforms/celleb/setup.c b/arch/powerpc/platforms/celleb/setup.c
index 5f4d0d9..596ab2a 100644
--- a/arch/powerpc/platforms/celleb/setup.c
+++ b/arch/powerpc/platforms/celleb/setup.c
@@ -67,7 +67,7 @@
 
 	root = of_find_node_by_path("/");
 	if (root)
-		model = get_property(root, "model", NULL);
+		model = of_get_property(root, "model", NULL);
 	/* using "CHRP" is to trick anaconda into installing FCx into Celleb */
 	seq_printf(m, "machine\t\t: %s %s\n", celleb_machine_type, model);
 	of_node_put(root);
@@ -128,15 +128,6 @@
 	return 1;
 }
 
-/*
- * Cell has no legacy IO; anything calling this function has to
- * fail or bad things will happen
- */
-static int celleb_check_legacy_ioport(unsigned int baseport)
-{
-	return -ENODEV;
-}
-
 #ifdef CONFIG_KEXEC
 static void celleb_kexec_cpu_down(int crash, int secondary)
 {
@@ -173,7 +164,6 @@
 	.get_rtc_time		= beat_get_rtc_time,
 	.set_rtc_time		= beat_set_rtc_time,
 	.calibrate_decr		= generic_calibrate_decr,
-	.check_legacy_ioport	= celleb_check_legacy_ioport,
 	.progress		= celleb_progress,
 	.power_save		= beat_power_save,
 	.nvram_size		= beat_nvram_get_size,
diff --git a/arch/powerpc/platforms/chrp/Kconfig b/arch/powerpc/platforms/chrp/Kconfig
new file mode 100644
index 0000000..d2c6905
--- /dev/null
+++ b/arch/powerpc/platforms/chrp/Kconfig
@@ -0,0 +1,11 @@
+config PPC_CHRP
+	bool "Common Hardware Reference Platform (CHRP) based machines"
+	depends on PPC_MULTIPLATFORM && PPC32
+	select MPIC
+	select PPC_I8259
+	select PPC_INDIRECT_PCI
+	select PPC_RTAS
+	select PPC_MPC106
+	select PPC_UDBG_16550
+	select PPC_NATIVE
+	default y
diff --git a/arch/powerpc/platforms/chrp/nvram.c b/arch/powerpc/platforms/chrp/nvram.c
index 0dd4a64..8efd424 100644
--- a/arch/powerpc/platforms/chrp/nvram.c
+++ b/arch/powerpc/platforms/chrp/nvram.c
@@ -74,7 +74,7 @@
 	if (nvram == NULL)
 		return;
 
-	nbytes_p = get_property(nvram, "#bytes", &proplen);
+	nbytes_p = of_get_property(nvram, "#bytes", &proplen);
 	if (nbytes_p == NULL || proplen != sizeof(unsigned int))
 		return;
 
diff --git a/arch/powerpc/platforms/chrp/pci.c b/arch/powerpc/platforms/chrp/pci.c
index ddb4a11..1469d64 100644
--- a/arch/powerpc/platforms/chrp/pci.c
+++ b/arch/powerpc/platforms/chrp/pci.c
@@ -7,7 +7,6 @@
 #include <linux/delay.h>
 #include <linux/string.h>
 #include <linux/init.h>
-#include <linux/ide.h>
 
 #include <asm/io.h>
 #include <asm/pgtable.h>
@@ -137,9 +136,11 @@
 	struct device_node *np;
 	struct resource r;
 
-	np = find_devices("mac-io");
-	if (np == NULL || of_address_to_resource(np, 0, &r))
+	np = of_find_node_by_name(NULL, "mac-io");
+	if (np == NULL || of_address_to_resource(np, 0, &r)) {
+		of_node_put(np);
 		return 0;
+	}
 	Hydra = ioremap(r.start, r.end-r.start);
 	printk("Hydra Mac I/O at %llx\n", (unsigned long long)r.start);
 	printk("Hydra Feature_Control was %x",
@@ -186,10 +187,9 @@
 /* Marvell Discovery II based Pegasos 2 */
 static void __init setup_peg2(struct pci_controller *hose, struct device_node *dev)
 {
-	struct device_node *root = find_path_device("/");
+	struct device_node *root = of_find_node_by_path("/");
 	struct device_node *rtas;
 
-	of_node_get(root);
 	rtas = of_find_node_by_name (root, "rtas");
 	if (rtas) {
 		hose->ops = &rtas_pci_ops;
@@ -199,6 +199,7 @@
 			" your firmware\n");
 	}
 	pci_assign_all_buses = 1;
+	/* keep the reference to the root node */
 }
 
 void __init
@@ -211,14 +212,14 @@
 	const unsigned int *dma;
 	const char *model, *machine;
 	int is_longtrail = 0, is_mot = 0, is_pegasos = 0;
-	struct device_node *root = find_path_device("/");
+	struct device_node *root = of_find_node_by_path("/");
 	struct resource r;
 	/*
 	 * The PCI host bridge nodes on some machines don't have
 	 * properties to adequately identify them, so we have to
 	 * look at what sort of machine this is as well.
 	 */
-	machine = get_property(root, "model", NULL);
+	machine = of_get_property(root, "model", NULL);
 	if (machine != NULL) {
 		is_longtrail = strncmp(machine, "IBM,LongTrail", 13) == 0;
 		is_mot = strncmp(machine, "MOT", 3) == 0;
@@ -237,7 +238,7 @@
 			       dev->full_name);
 			continue;
 		}
-		bus_range = get_property(dev, "bus-range", &len);
+		bus_range = of_get_property(dev, "bus-range", &len);
 		if (bus_range == NULL || len < 2 * sizeof(int)) {
 			printk(KERN_WARNING "Can't get bus-range for %s\n",
 				dev->full_name);
@@ -263,7 +264,7 @@
 		hose->first_busno = bus_range[0];
 		hose->last_busno = bus_range[1];
 
-		model = get_property(dev, "model", NULL);
+		model = of_get_property(dev, "model", NULL);
 		if (model == NULL)
 			model = "<none>";
 		if (device_is_compatible(dev, "IBM,python")) {
@@ -285,7 +286,8 @@
 					   r.start + 0x000f8000,
 					   r.start + 0x000f8010);
 			if (index == 0) {
-				dma = get_property(dev, "system-dma-base",&len);
+				dma = of_get_property(dev, "system-dma-base",
+							&len);
 				if (dma && len >= sizeof(*dma)) {
 					dma = (unsigned int *)
 						(((unsigned long)dma) +
@@ -303,12 +305,13 @@
 
 		/* check the first bridge for a property that we can
 		   use to set pci_dram_offset */
-		dma = get_property(dev, "ibm,dma-ranges", &len);
+		dma = of_get_property(dev, "ibm,dma-ranges", &len);
 		if (index == 0 && dma != NULL && len >= 6 * sizeof(*dma)) {
 			pci_dram_offset = dma[2] - dma[3];
 			printk("pci_dram_offset = %lx\n", pci_dram_offset);
 		}
 	}
+	of_node_put(root);
 }
 
 /* SL82C105 IDE Control/Status Register */
diff --git a/arch/powerpc/platforms/chrp/setup.c b/arch/powerpc/platforms/chrp/setup.c
index 117c9a0..1870038 100644
--- a/arch/powerpc/platforms/chrp/setup.c
+++ b/arch/powerpc/platforms/chrp/setup.c
@@ -28,7 +28,6 @@
 #include <linux/adb.h>
 #include <linux/module.h>
 #include <linux/delay.h>
-#include <linux/ide.h>
 #include <linux/console.h>
 #include <linux/seq_file.h>
 #include <linux/root_dev.h>
@@ -111,9 +110,9 @@
 	struct device_node *root;
 	const char *model = "";
 
-	root = find_path_device("/");
+	root = of_find_node_by_path("/");
 	if (root)
-		model = get_property(root, "model", NULL);
+		model = of_get_property(root, "model", NULL);
 	seq_printf(m, "machine\t\t: CHRP %s\n", model);
 
 	/* longtrail (goldengate) stuff */
@@ -161,6 +160,7 @@
 			   gg2_cachetypes[(t>>2) & 3],
 			   gg2_cachemodes[t & 3]);
 	}
+	of_node_put(root);
 }
 
 /*
@@ -205,13 +205,15 @@
 {
 	struct device_node *root;
 
-	if ((root = find_path_device("/")) &&
-	    !strncmp(get_property(root, "model", NULL), "IBM,LongTrail", 13)) {
+	if ((root = of_find_node_by_path("/")) &&
+	    !strncmp(of_get_property(root, "model", NULL),
+			"IBM,LongTrail", 13)) {
 		/* logical device 0 (KBC/Keyboard) */
 		sio_fixup_irq("keyboard", 0, 1, 2);
 		/* select logical device 1 (KBC/Mouse) */
 		sio_fixup_irq("mouse", 1, 12, 2);
 	}
+	of_node_put(root);
 }
 
 
@@ -224,12 +226,12 @@
 		return;
 
 	/* Enable L2 cache if needed */
-	np = find_type_devices("cpu");
+	np = of_find_node_by_type(NULL, "cpu");
 	if (np != NULL) {
-		const unsigned int *l2cr = get_property(np, "l2cr", NULL);
+		const unsigned int *l2cr = of_get_property(np, "l2cr", NULL);
 		if (l2cr == NULL) {
 			printk ("Pegasos l2cr : no cpu l2cr property found\n");
-			return;
+			goto out;
 		}
 		if (!((*l2cr) & 0x80000000)) {
 			printk ("Pegasos l2cr : L2 cache was not active, "
@@ -238,6 +240,8 @@
 			_set_L2CR((*l2cr) | 0x80000000);
 		}
 	}
+out:
+	of_node_put(np);
 }
 
 static void briq_restart(char *cmd)
@@ -250,14 +254,14 @@
 
 void __init chrp_setup_arch(void)
 {
-	struct device_node *root = find_path_device ("/");
+	struct device_node *root = of_find_node_by_path("/");
 	const char *machine = NULL;
 
 	/* init to some ~sane value until calibrate_delay() runs */
 	loops_per_jiffy = 50000000/HZ;
 
 	if (root)
-		machine = get_property(root, "model", NULL);
+		machine = of_get_property(root, "model", NULL);
 	if (machine && strncmp(machine, "Pegasos", 7) == 0) {
 		_chrp_type = _CHRP_Pegasos;
 	} else if (machine && strncmp(machine, "IBM", 3) == 0) {
@@ -273,6 +277,7 @@
 		/* Let's assume it is an IBM chrp if all else fails */
 		_chrp_type = _CHRP_IBM;
 	}
+	of_node_put(root);
 	printk("chrp type = %x [%s]\n", _chrp_type, chrp_names[_chrp_type]);
 
 	rtas_initialize();
@@ -361,8 +366,8 @@
 		return;
 	root = of_find_node_by_path("/");
 	if (root) {
-		opprop = get_property(root, "platform-open-pic", &oplen);
-		na = prom_n_addr_cells(root);
+		opprop = of_get_property(root, "platform-open-pic", &oplen);
+		na = of_n_addr_cells(root);
 	}
 	if (opprop && oplen >= na * sizeof(unsigned int)) {
 		opaddr = opprop[na-1];	/* assume 32-bit */
@@ -378,7 +383,7 @@
 
 	printk(KERN_INFO "OpenPIC at %lx\n", opaddr);
 
-	iranges = get_property(np, "interrupt-ranges", &len);
+	iranges = of_get_property(np, "interrupt-ranges", &len);
 	if (iranges == NULL)
 		len = 0;	/* non-distributed mpic */
 	else
@@ -427,7 +432,7 @@
 	of_node_put(np);
 }
 
-#if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(XMON)
+#if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_XMON)
 static struct irqaction xmon_irqaction = {
 	.handler = xmon_irq,
 	.mask = CPU_MASK_NONE,
@@ -463,15 +468,16 @@
 	 * Also, Pegasos-type platforms don't have a proper node to start
 	 * from anyway
 	 */
-	for (np = find_devices("pci"); np != NULL; np = np->next) {
-		const unsigned int *addrp = get_property(np,
+	for_each_node_by_name(np, "pci") {
+		const unsigned int *addrp = of_get_property(np,
 				"8259-interrupt-acknowledge", NULL);
 
 		if (addrp == NULL)
 			continue;
-		chrp_int_ack = addrp[prom_n_addr_cells(np)-1];
+		chrp_int_ack = addrp[of_n_addr_cells(np)-1];
 		break;
 	}
+	of_node_put(np);
 	if (np == NULL)
 		printk(KERN_WARNING "Cannot find PCI interrupt acknowledge"
 		       " address, polling\n");
@@ -493,7 +499,7 @@
 
 void __init chrp_init_IRQ(void)
 {
-#if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(XMON)
+#if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_XMON)
 	struct device_node *kbd;
 #endif
 	chrp_find_openpic();
@@ -510,13 +516,14 @@
 	if (_chrp_type == _CHRP_Pegasos)
 		ppc_md.get_irq        = i8259_irq;
 
-#if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(XMON)
+#if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_XMON)
 	/* see if there is a keyboard in the device tree
 	   with a parent of type "adb" */
-	for (kbd = find_devices("keyboard"); kbd; kbd = kbd->next)
+	for_each_node_by_name(kbd, "keyboard")
 		if (kbd->parent && kbd->parent->type
 		    && strcmp(kbd->parent->type, "adb") == 0)
 			break;
+	of_node_put(kbd);
 	if (kbd)
 		setup_irq(HYDRA_INT_ADB_NMI, &xmon_irqaction);
 #endif
@@ -542,9 +549,9 @@
 	/* Get the event scan rate for the rtas so we know how
 	 * often it expects a heartbeat. -- Cort
 	 */
-	device = find_devices("rtas");
+	device = of_find_node_by_name(NULL, "rtas");
 	if (device)
-		p = get_property(device, "rtas-event-scan-rate", NULL);
+		p = of_get_property(device, "rtas-event-scan-rate", NULL);
 	if (p && *p) {
 		/*
 		 * Arrange to call chrp_event_scan at least *p times
@@ -571,6 +578,7 @@
 		printk("RTAS Event Scan Rate: %u (%lu jiffies)\n",
 		       *p, interval);
 	}
+	of_node_put(device);
 
 	if (ppc_md.progress)
 		ppc_md.progress("  Have fun!    ", 0x7777);
diff --git a/arch/powerpc/platforms/chrp/time.c b/arch/powerpc/platforms/chrp/time.c
index 7d78890..96d1e4b 100644
--- a/arch/powerpc/platforms/chrp/time.c
+++ b/arch/powerpc/platforms/chrp/time.c
@@ -39,12 +39,17 @@
 	struct resource r;
 	int base;
 
-	rtcs = find_compatible_devices("rtc", "pnpPNP,b00");
+	rtcs = of_find_compatible_node(NULL, "rtc", "pnpPNP,b00");
 	if (rtcs == NULL)
-		rtcs = find_compatible_devices("rtc", "ds1385-rtc");
-	if (rtcs == NULL || of_address_to_resource(rtcs, 0, &r))
+		rtcs = of_find_compatible_node(NULL, "rtc", "ds1385-rtc");
+	if (rtcs == NULL)
 		return 0;
-	
+	if (of_address_to_resource(rtcs, 0, &r)) {
+		of_node_put(rtcs);
+		return 0;
+	}
+	of_node_put(rtcs);
+
 	base = r.start;
 	nvram_as1 = 0;
 	nvram_as0 = base;
diff --git a/arch/powerpc/platforms/embedded6xx/Kconfig b/arch/powerpc/platforms/embedded6xx/Kconfig
index 3410bcb..9557908 100644
--- a/arch/powerpc/platforms/embedded6xx/Kconfig
+++ b/arch/powerpc/platforms/embedded6xx/Kconfig
@@ -2,78 +2,6 @@
 	prompt "Machine Type"
 	depends on EMBEDDED6xx
 
-config KATANA
-	bool "Artesyn-Katana"
-	help
-	  Select KATANA if configuring an Artesyn KATANA 750i or 3750
-	  cPCI board.
-
-config WILLOW
-	bool "Cogent-Willow"
-
-config CPCI690
-	bool "Force-CPCI690"
-	help
-	  Select CPCI690 if configuring a Force CPCI690 cPCI board.
-
-config POWERPMC250
-	bool "Force-PowerPMC250"
-
-config CHESTNUT
-	bool "IBM 750FX Eval board or 750GX Eval board"
-	help
-	  Select CHESTNUT if configuring an IBM 750FX Eval Board or a
-	  IBM 750GX Eval board.
-
-config SPRUCE
-	bool "IBM-Spruce"
-	select PPC_INDIRECT_PCI
-
-config HDPU
-	bool "Sky-HDPU"
-	help
-	  Select HDPU if configuring a Sky Computers Compute Blade.
-
-config HDPU_FEATURES
-	depends on HDPU
-	tristate "HDPU-Features"
-	help
-	  Select to enable HDPU enhanced features.
-
-config EV64260
-	bool "Marvell-EV64260BP"
-	help
-	  Select EV64260 if configuring a Marvell (formerly Galileo)
-	  EV64260BP Evaluation platform.
-
-config LOPEC
-	bool "Motorola-LoPEC"
-	select PPC_I8259
-
-config MVME5100
-	bool "Motorola-MVME5100"
-	select PPC_INDIRECT_PCI
-
-config PPLUS
-	bool "Motorola-PowerPlus"
-	select PPC_I8259
-	select PPC_INDIRECT_PCI
-
-config PRPMC750
-	bool "Motorola-PrPMC750"
-	select PPC_INDIRECT_PCI
-
-config PRPMC800
-	bool "Motorola-PrPMC800"
-	select PPC_INDIRECT_PCI
-
-config SANDPOINT
-	bool "Motorola-Sandpoint"
-	select PPC_I8259
-	help
-	  Select SANDPOINT if configuring for a Motorola Sandpoint X3
-	  (any flavor).
-
 config LINKSTATION
 	bool "Linkstation / Kurobox(HG) from Buffalo"
 	select MPIC
@@ -97,212 +25,24 @@
 	help
 	  Select MPC7448HPC2 if configuring for Freescale MPC7448HPC2 (Taiga)
 	  platform
-
-config RADSTONE_PPC7D
-	bool "Radstone Technology PPC7D board"
-	select PPC_I8259
-
-config PAL4
-	bool "SBS-Palomar4"
-
-config EST8260
-	bool "EST8260"
-	---help---
-	  The EST8260 is a single-board computer manufactured by Wind River
-	  Systems, Inc. (formerly Embedded Support Tools Corp.) and based on
-	  the MPC8260.  Wind River Systems has a website at
-	  <http://www.windriver.com/>, but the EST8260 cannot be found on it
-	  and has probably been discontinued or rebadged.
-
-config SBC82xx
-	bool "SBC82xx"
-	---help---
-	  SBC PowerQUICC II, single-board computer with MPC82xx CPU
-	  Manufacturer: Wind River Systems, Inc.
-	  Date of Release: May 2003
-	  End of Life: -
-	  URL: <http://www.windriver.com/>
-
-config SBS8260
-	bool "SBS8260"
-
-config RPX8260
-	bool "RPXSUPER"
-
-config TQM8260
-	bool "TQM8260"
-	---help---
-	  MPC8260 based module, little larger than credit card,
-	  up to 128 MB global + 64 MB local RAM, 32 MB Flash,
-	  32 kB EEPROM, 256 kB L@ Cache, 10baseT + 100baseT Ethernet,
-	  2 x serial ports, ...
-	  Manufacturer: TQ Components, www.tq-group.de
-	  Date of Release: June 2001
-	  End of Life: not yet :-)
-	  URL: <http://www.denx.de/PDF/TQM82xx_SPEC_Rev005.pdf>
-
-config ADS8272
-	bool "ADS8272"
-
-config PQ2FADS
-	bool "Freescale-PQ2FADS"
-	help
-	  Select PQ2FADS if you wish to configure for a Freescale
-	  PQ2FADS board (-VR or -ZU).
-
-config EV64360
-	bool "Marvell-EV64360BP"
-	help
-	  Select EV64360 if configuring a Marvell EV64360BP Evaluation
-	  platform.
 endchoice
 
-config PQ2ADS
-	bool
-	depends on ADS8272
-	default y
-
-config TQM8xxL
-	bool
-	depends on 8xx && (TQM823L || TQM850L || FPS850L || TQM855L || TQM860L)
-	default y
-
-config 8260
-	bool "CPM2 Support" if WILLOW
-	depends on 6xx
-	default y if TQM8260 || RPX8260 || EST8260 || SBS8260 || SBC82xx || PQ2FADS
-	help
-	  The MPC8260 is a typical embedded CPU made by Motorola.  Selecting
-	  this option means that you wish to build a kernel for a machine with
-	  an 8260 class CPU.
-
-config 8272
-	bool
-	depends on 6xx
-	default y if ADS8272
-	select 8260
-	help
-	  The MPC8272 CPM has a different internal dpram setup than other CPM2
-	  devices
-
-config CPM2
-	bool
-	depends on 8260 || MPC8560 || MPC8555
-	default y
-	help
-	  The CPM2 (Communications Processor Module) is a coprocessor on
-	  embedded CPUs made by Motorola.  Selecting this option means that
-	  you wish to build a kernel for a machine with a CPM2 coprocessor
-	  on it (826x, 827x, 8560).
-
-config PPC_GEN550
-	bool
-	depends on SANDPOINT || SPRUCE || PPLUS || \
-		PRPMC750 || PRPMC800 || LOPEC || \
-		(EV64260 && !SERIAL_MPSC) || CHESTNUT || RADSTONE_PPC7D || \
-		83xx || LINKSTATION
-	default y
-
-config FORCE
-	bool
-	depends on 6xx && POWERPMC250
-	default y
-
-config GT64260
-	bool
-	depends on EV64260 || CPCI690
-	default y
-
-config MV64360		# Really MV64360 & MV64460
-	bool
-	depends on CHESTNUT || KATANA || RADSTONE_PPC7D || HDPU || EV64360
-	default y
-
-config MV64X60
-	bool
-	depends on (GT64260 || MV64360)
-	select PPC_INDIRECT_PCI
-	default y
-
 config TSI108_BRIDGE
 	bool
 	depends on MPC7448HPC2
 	default y
 
-menu "Set bridge options"
-	depends on MV64X60
-
-config NOT_COHERENT_CACHE
-	bool "Turn off Cache Coherency"
-	default n
-	help
-	  Some 64x60 bridges lock up when trying to enforce cache coherency.
-	  When this option is selected, cache coherency will be turned off.
-	  Note that this can cause other problems (e.g., stale data being
-	  speculatively loaded via a cached mapping).  Use at your own risk.
-
-config MV64X60_BASE
-	hex "Set bridge base used by firmware"
-	default "0xf1000000"
-	help
-	  A firmware can leave the base address of the bridge's registers at
-	  a non-standard location.  If so, set this value to reflect the
-	  address of that non-standard location.
-
-config MV64X60_NEW_BASE
-	hex "Set bridge base used by kernel"
-	default "0xf1000000"
-	help
-	  If the current base address of the bridge's registers is not where
-	  you want it, set this value to the address that you want it moved to.
-
-endmenu
-
-config NONMONARCH_SUPPORT
-	bool "Enable Non-Monarch Support"
-	depends on PRPMC800
-
-config HARRIER
-	bool
-	depends on PRPMC800
-	default y
-
-config EPIC_SERIAL_MODE
-	bool
-	depends on 6xx && (LOPEC || SANDPOINT)
-	default y
-
 config MPC10X_BRIDGE
 	bool
-	depends on POWERPMC250 || LOPEC || SANDPOINT || LINKSTATION
+	depends on LINKSTATION
 	select PPC_INDIRECT_PCI
 	default y
 
 config MPC10X_OPENPIC
 	bool
-	depends on POWERPMC250 || LOPEC || SANDPOINT || LINKSTATION
+	depends on LINKSTATION
 	default y
 
 config MPC10X_STORE_GATHERING
 	bool "Enable MPC10x store gathering"
 	depends on MPC10X_BRIDGE
-
-config SANDPOINT_ENABLE_UART1
-	bool "Enable DUART mode on Sandpoint"
-	depends on SANDPOINT
-	help
-	  If this option is enabled then the MPC824x processor will run
-	  in DUART mode instead of UART mode.
-
-config HARRIER_STORE_GATHERING
-	bool "Enable Harrier store gathering"
-	depends on HARRIER
-
-config MVME5100_IPMC761_PRESENT
-	bool "MVME5100 configured with an IPMC761"
-	depends on MVME5100
-	select PPC_I8259
-
-config SPRUCE_BAUD_33M
-	bool "Spruce baud clock support"
-	depends on SPRUCE
diff --git a/arch/powerpc/platforms/embedded6xx/linkstation.c b/arch/powerpc/platforms/embedded6xx/linkstation.c
index 3f6c411..b412f00 100644
--- a/arch/powerpc/platforms/embedded6xx/linkstation.c
+++ b/arch/powerpc/platforms/embedded6xx/linkstation.c
@@ -58,11 +58,11 @@
 {
 	int len;
 	struct pci_controller *hose;
-	int *bus_range;
+	const int *bus_range;
 
 	printk("Adding PCI host bridge %s\n", dev->full_name);
 
-	bus_range = (int *) get_property(dev, "bus-range", &len);
+	bus_range = of_get_property(dev, "bus-range", &len);
 	if (bus_range == NULL || len < 2 * sizeof(int))
 		printk(KERN_WARNING "Can't get bus-range for %s, assume"
 				" bus 0\n", dev->full_name);
@@ -106,7 +106,7 @@
 {
 	struct mpic *mpic;
 	struct device_node *dnp;
-	void *prop;
+	const u32 *prop;
 	int size;
 	phys_addr_t paddr;
 
@@ -114,7 +114,7 @@
 	if (dnp == NULL)
 		return;
 
-	prop = (struct device_node *)get_property(dnp, "reg", &size);
+	prop = of_get_property(dnp, "reg", &size);
 	paddr = (phys_addr_t)of_translate_address(dnp, prop);
 
 	mpic = mpic_alloc(dnp, paddr, MPIC_PRIMARY | MPIC_WANTS_RESET, 4, 32, " EPIC     ");
diff --git a/arch/powerpc/platforms/embedded6xx/ls_uart.c b/arch/powerpc/platforms/embedded6xx/ls_uart.c
index 0e83776..d0bee9f 100644
--- a/arch/powerpc/platforms/embedded6xx/ls_uart.c
+++ b/arch/powerpc/platforms/embedded6xx/ls_uart.c
@@ -110,8 +110,8 @@
 	if (!avr)
 		return -EINVAL;
 
-	avr_clock = *(u32*)get_property(avr, "clock-frequency", &len);
-	phys_addr = ((u32*)get_property(avr, "reg", &len))[0];
+	avr_clock = *(u32*)of_get_property(avr, "clock-frequency", &len);
+	phys_addr = ((u32*)of_get_property(avr, "reg", &len))[0];
 
 	if (!avr_clock || !phys_addr)
 		return -EINVAL;
diff --git a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c
index 3fcc85f..c3f64dd 100644
--- a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c
+++ b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c
@@ -25,7 +25,6 @@
 #include <linux/console.h>
 #include <linux/delay.h>
 #include <linux/irq.h>
-#include <linux/ide.h>
 #include <linux/seq_file.h>
 #include <linux/root_dev.h>
 #include <linux/serial.h>
@@ -82,7 +81,7 @@
 	if (cpu != 0) {
 		const unsigned int *fp;
 
-		fp = get_property(cpu, "clock-frequency", NULL);
+		fp = of_get_property(cpu, "clock-frequency", NULL);
 		if (fp != 0)
 			loops_per_jiffy = *fp / HZ;
 		else
@@ -91,16 +90,6 @@
 	}
 	tsi108_csr_vir_base = get_vir_csrbase();
 
-#ifdef	CONFIG_ROOT_NFS
-	ROOT_DEV = Root_NFS;
-#else
-	ROOT_DEV = Root_HDA1;
-#endif
-
-#ifdef CONFIG_BLK_DEV_INITRD
-	ROOT_DEV = Root_RAM0;
-#endif
-
 	/* setup PCI host bridge */
 #ifdef CONFIG_PCI
 	for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;)
@@ -143,7 +132,7 @@
 	tsi_pic = of_find_node_by_type(NULL, "open-pic");
 	if (tsi_pic) {
 		unsigned int size;
-		const void *prop = get_property(tsi_pic, "reg", &size);
+		const void *prop = of_get_property(tsi_pic, "reg", &size);
 		mpic_paddr = of_translate_address(tsi_pic, prop);
 	}
 
diff --git a/arch/powerpc/platforms/iseries/Kconfig b/arch/powerpc/platforms/iseries/Kconfig
index 54e6b3b..46c3a8e 100644
--- a/arch/powerpc/platforms/iseries/Kconfig
+++ b/arch/powerpc/platforms/iseries/Kconfig
@@ -1,3 +1,7 @@
+config PPC_ISERIES
+	bool "IBM Legacy iSeries"
+	depends on PPC_MULTIPLATFORM && PPC64
+	select PPC_INDIRECT_IO
 
 menu "iSeries device drivers"
 	depends on PPC_ISERIES
diff --git a/arch/powerpc/platforms/iseries/iommu.c b/arch/powerpc/platforms/iseries/iommu.c
index d7a756d..3b6a966 100644
--- a/arch/powerpc/platforms/iseries/iommu.c
+++ b/arch/powerpc/platforms/iseries/iommu.c
@@ -171,7 +171,7 @@
 {
 	struct iommu_table *tbl;
 	struct pci_dn *pdn = PCI_DN(dn);
-	const u32 *lsn = get_property(dn, "linux,logical-slot-number", NULL);
+	const u32 *lsn = of_get_property(dn, "linux,logical-slot-number", NULL);
 
 	BUG_ON(lsn == NULL);
 
@@ -194,5 +194,5 @@
 	ppc_md.tce_build = tce_build_iSeries;
 	ppc_md.tce_free  = tce_free_iSeries;
 
-	pci_dma_ops = &dma_iommu_ops;
+	set_pci_dma_ops(&dma_iommu_ops);
 }
diff --git a/arch/powerpc/platforms/iseries/irq.c b/arch/powerpc/platforms/iseries/irq.c
index 5225abf..63b3367 100644
--- a/arch/powerpc/platforms/iseries/irq.c
+++ b/arch/powerpc/platforms/iseries/irq.c
@@ -30,7 +30,6 @@
 #include <linux/param.h>
 #include <linux/string.h>
 #include <linux/bootmem.h>
-#include <linux/ide.h>
 #include <linux/irq.h>
 #include <linux/spinlock.h>
 
@@ -337,6 +336,8 @@
 	return irq;
 }
 
+#ifdef CONFIG_PCI
+
 static int iseries_irq_host_map(struct irq_host *h, unsigned int virq,
 				irq_hw_number_t hw)
 {
@@ -384,3 +385,4 @@
 				"failed with rc 0x%x\n", ret);
 }
 
+#endif	/* CONFIG_PCI */
diff --git a/arch/powerpc/platforms/iseries/pci.c b/arch/powerpc/platforms/iseries/pci.c
index 4a06d9c..9c97422 100644
--- a/arch/powerpc/platforms/iseries/pci.c
+++ b/arch/powerpc/platforms/iseries/pci.c
@@ -24,7 +24,6 @@
 #include <linux/string.h>
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/ide.h>
 #include <linux/pci.h>
 
 #include <asm/io.h>
@@ -177,7 +176,7 @@
 			struct pci_dn *pdn = PCI_DN(node);
 			const u32 *agent;
 
-			agent = get_property(node, "linux,agent-id", NULL);
+			agent = of_get_property(node, "linux,agent-id", NULL);
 			if ((pdn != NULL) && (agent != NULL)) {
 				u8 irq = iSeries_allocate_IRQ(pdn->busno, 0,
 						pdn->bussubno);
@@ -755,7 +754,7 @@
 		if ((node->type == NULL) || (strcmp(node->type, "pci") != 0))
 			continue;
 
-		busp = get_property(node, "bus-range", NULL);
+		busp = of_get_property(node, "bus-range", NULL);
 		if (busp == NULL)
 			continue;
 		bus = *busp;
diff --git a/arch/powerpc/platforms/iseries/setup.c b/arch/powerpc/platforms/iseries/setup.c
index cce7e30..7f5dcee 100644
--- a/arch/powerpc/platforms/iseries/setup.c
+++ b/arch/powerpc/platforms/iseries/setup.c
@@ -628,15 +628,6 @@
 {
 }
 
-/*
- * iSeries has no legacy IO, anything calling this function has to
- * fail or bad things will happen
- */
-static int iseries_check_legacy_ioport(unsigned int baseport)
-{
-	return -ENODEV;
-}
-
 static int __init iseries_probe(void)
 {
 	unsigned long root = of_get_flat_dt_root();
@@ -667,7 +658,6 @@
 	.calibrate_decr	= generic_calibrate_decr,
 	.progress	= iSeries_progress,
 	.probe		= iseries_probe,
-	.check_legacy_ioport	= iseries_check_legacy_ioport,
 	.ioremap	= iseries_ioremap,
 	.iounmap	= iseries_iounmap,
 	/* XXX Implement enable_pmcs for iSeries */
diff --git a/arch/powerpc/platforms/iseries/viopath.c b/arch/powerpc/platforms/iseries/viopath.c
index e2100ec..2ca2d8a 100644
--- a/arch/powerpc/platforms/iseries/viopath.c
+++ b/arch/powerpc/platforms/iseries/viopath.c
@@ -155,7 +155,7 @@
 	node = of_find_node_by_path("/");
 	sysid = NULL;
 	if (node != NULL)
-		sysid = get_property(node, "system-id", NULL);
+		sysid = of_get_property(node, "system-id", NULL);
 
 	if (sysid == NULL)
 		seq_printf(m, "SRLNBR=<UNKNOWN>\n");
diff --git a/arch/powerpc/platforms/maple/Kconfig b/arch/powerpc/platforms/maple/Kconfig
new file mode 100644
index 0000000..f7c95eb
--- /dev/null
+++ b/arch/powerpc/platforms/maple/Kconfig
@@ -0,0 +1,17 @@
+config PPC_MAPLE
+	depends on PPC_MULTIPLATFORM && PPC64
+	bool "Maple 970FX Evaluation Board"
+	select MPIC
+	select U3_DART
+	select MPIC_U3_HT_IRQS
+	select GENERIC_TBSYNC
+	select PPC_UDBG_16550
+	select PPC_970_NAP
+	select PPC_NATIVE
+	select PPC_RTAS
+	select MMIO_NVRAM
+	select ATA_NONSTANDARD if ATA
+	default n
+	help
+          This option enables support for the Maple 970FX Evaluation Board.
+	  For more information, refer to <http://www.970eval.com>
diff --git a/arch/powerpc/platforms/maple/pci.c b/arch/powerpc/platforms/maple/pci.c
index 73c5990..b1d3b99 100644
--- a/arch/powerpc/platforms/maple/pci.c
+++ b/arch/powerpc/platforms/maple/pci.c
@@ -44,11 +44,11 @@
 		int len;
 
 		/* For PCI<->PCI bridges or CardBus bridges, we go down */
-		class_code = get_property(node, "class-code", NULL);
+		class_code = of_get_property(node, "class-code", NULL);
 		if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI &&
 			(*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS))
 			continue;
-		bus_range = get_property(node, "bus-range", &len);
+		bus_range = of_get_property(node, "bus-range", &len);
 		if (bus_range != NULL && len > 2 * sizeof(int)) {
 			if (bus_range[1] > higher)
 				higher = bus_range[1];
@@ -77,7 +77,7 @@
 			       bridge->full_name);
 		return;
 	}
-	bus_range = (int *)prop->value;
+	bus_range = prop->value;
 	bus_range[1] = fixup_one_level_bus_range(bridge->child, bus_range[1]);
 }
 
@@ -454,7 +454,7 @@
 
 	DBG("Adding PCI host bridge %s\n", dev->full_name);
 
-	bus_range = get_property(dev, "bus-range", &len);
+	bus_range = of_get_property(dev, "bus-range", &len);
 	if (bus_range == NULL || len < 2 * sizeof(int)) {
 		printk(KERN_WARNING "Can't get bus-range for %s, assume bus 0\n",
 		dev->full_name);
diff --git a/arch/powerpc/platforms/maple/setup.c b/arch/powerpc/platforms/maple/setup.c
index 82d3f9e..2a30c5b 100644
--- a/arch/powerpc/platforms/maple/setup.c
+++ b/arch/powerpc/platforms/maple/setup.c
@@ -32,7 +32,6 @@
 #include <linux/initrd.h>
 #include <linux/vt_kern.h>
 #include <linux/console.h>
-#include <linux/ide.h>
 #include <linux/pci.h>
 #include <linux/adb.h>
 #include <linux/cuda.h>
@@ -114,8 +113,8 @@
 		printk(KERN_EMERG "Maple: Unable to find Service Processor\n");
 		goto fail;
 	}
-	maple_nvram_offset = get_property(sp, "restart-addr", NULL);
-	maple_nvram_command = get_property(sp, "restart-value", NULL);
+	maple_nvram_offset = of_get_property(sp, "restart-addr", NULL);
+	maple_nvram_command = of_get_property(sp, "restart-value", NULL);
 	of_node_put(sp);
 
 	/* send command */
@@ -141,8 +140,8 @@
 		printk(KERN_EMERG "Maple: Unable to find Service Processor\n");
 		goto fail;
 	}
-	maple_nvram_offset = get_property(sp, "power-off-addr", NULL);
-	maple_nvram_command = get_property(sp, "power-off-value", NULL);
+	maple_nvram_offset = of_get_property(sp, "power-off-addr", NULL);
+	maple_nvram_command = of_get_property(sp, "power-off-value", NULL);
 	of_node_put(sp);
 
 	/* send command */
@@ -249,8 +248,8 @@
 
 	/* Find address list in /platform-open-pic */
 	root = of_find_node_by_path("/");
-	naddr = prom_n_addr_cells(root);
-	opprop = get_property(root, "platform-open-pic", &opplen);
+	naddr = of_n_addr_cells(root);
+	opprop = of_get_property(root, "platform-open-pic", &opplen);
 	if (opprop != 0) {
 		openpic_addr = of_read_number(opprop, naddr);
 		has_isus = (opplen > naddr);
@@ -261,11 +260,11 @@
 	BUG_ON(openpic_addr == 0);
 
 	/* Check for a big endian MPIC */
-	if (get_property(np, "big-endian", NULL) != NULL)
+	if (of_get_property(np, "big-endian", NULL) != NULL)
 		flags |= MPIC_BIG_ENDIAN;
 
 	/* XXX Maple specific bits */
-	flags |= MPIC_BROKEN_U3 | MPIC_WANTS_RESET;
+	flags |= MPIC_U3_HT_IRQS | MPIC_WANTS_RESET;
 	/* All U3/U4 are big-endian, older SLOF firmware doesn't encode this */
 	flags |= MPIC_BIG_ENDIAN;
 
diff --git a/arch/powerpc/platforms/pasemi/Kconfig b/arch/powerpc/platforms/pasemi/Kconfig
index 68dc529..eb4dbc7 100644
--- a/arch/powerpc/platforms/pasemi/Kconfig
+++ b/arch/powerpc/platforms/pasemi/Kconfig
@@ -1,3 +1,15 @@
+config PPC_PASEMI
+	depends on PPC_MULTIPLATFORM && PPC64
+	bool "PA Semi SoC-based platforms"
+	default n
+	select MPIC
+	select PPC_UDBG_16550
+	select GENERIC_TBSYNC
+	select PPC_NATIVE
+	help
+	  This option enables support for PA Semi's PWRficient line
+	  of SoC processors, including PA6T-1682M
+
 menu "PA Semi PWRficient options"
 	depends on PPC_PASEMI
 
@@ -7,4 +19,11 @@
 	help
 	  IOMMU support for PA6T-1682M
 
+config PPC_PASEMI_MDIO
+	depends on PHYLIB
+	tristate "MDIO support via GPIO"
+	default y
+	help
+	  Driver for MDIO via GPIO on PWRficient platforms
+
 endmenu
diff --git a/arch/powerpc/platforms/pasemi/Makefile b/arch/powerpc/platforms/pasemi/Makefile
index e657cca..2cd2a4f 100644
--- a/arch/powerpc/platforms/pasemi/Makefile
+++ b/arch/powerpc/platforms/pasemi/Makefile
@@ -1,2 +1,3 @@
 obj-y	+= setup.o pci.o time.o idle.o powersave.o iommu.o
-
+obj-$(CONFIG_PPC_PASEMI_MDIO)	+= gpio_mdio.o
+obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += cpufreq.o
diff --git a/arch/powerpc/platforms/pasemi/cpufreq.c b/arch/powerpc/platforms/pasemi/cpufreq.c
new file mode 100644
index 0000000..2a57d60
--- /dev/null
+++ b/arch/powerpc/platforms/pasemi/cpufreq.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2007 PA Semi, Inc
+ *
+ * Authors: Egor Martovetsky <egor@pasemi.com>
+ *	    Olof Johansson <olof@lixom.net>
+ *
+ * Maintained by: Olof Johansson <olof@lixom.net>
+ *
+ * Based on arch/powerpc/platforms/cell/cbe_cpufreq.c:
+ * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/cpufreq.h>
+#include <linux/timer.h>
+
+#include <asm/hw_irq.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+
+#define SDCASR_REG		0x0100
+#define SDCASR_REG_STRIDE	0x1000
+#define SDCPWR_CFGA0_REG	0x0100
+#define SDCPWR_PWST0_REG	0x0000
+#define SDCPWR_GIZTIME_REG	0x0440
+
+/* SDCPWR_GIZTIME_REG fields */
+#define SDCPWR_GIZTIME_GR	0x80000000
+#define SDCPWR_GIZTIME_LONGLOCK	0x000000ff
+
+/* Offset of ASR registers from SDC base */
+#define SDCASR_OFFSET		0x120000
+
+static void __iomem *sdcpwr_mapbase;
+static void __iomem *sdcasr_mapbase;
+
+static DEFINE_MUTEX(pas_switch_mutex);
+
+/* Current astate, is used when waking up from power savings on
+ * one core, in case the other core has switched states during
+ * the idle time.
+ */
+static int current_astate;
+
+/* We support 5(A0-A4) power states excluding turbo(A5-A6) modes */
+static struct cpufreq_frequency_table pas_freqs[] = {
+	{0,	0},
+	{1,	0},
+	{2,	0},
+	{3,	0},
+	{4,	0},
+	{0,	CPUFREQ_TABLE_END},
+};
+
+static struct freq_attr *pas_cpu_freqs_attr[] = {
+	&cpufreq_freq_attr_scaling_available_freqs,
+	NULL,
+};
+
+/*
+ * hardware specific functions
+ */
+
+static int get_astate_freq(int astate)
+{
+	u32 ret;
+	ret = in_le32(sdcpwr_mapbase + SDCPWR_CFGA0_REG + (astate * 0x10));
+
+	return ret & 0x3f;
+}
+
+static int get_cur_astate(int cpu)
+{
+	u32 ret;
+
+	ret = in_le32(sdcpwr_mapbase + SDCPWR_PWST0_REG);
+	ret = (ret >> (cpu * 4)) & 0x7;
+
+	return ret;
+}
+
+static int get_gizmo_latency(void)
+{
+	u32 giztime, ret;
+
+	giztime = in_le32(sdcpwr_mapbase + SDCPWR_GIZTIME_REG);
+
+	/* just provide the upper bound */
+	if (giztime & SDCPWR_GIZTIME_GR)
+		ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 128000;
+	else
+		ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 1000;
+
+	return ret;
+}
+
+static void set_astate(int cpu, unsigned int astate)
+{
+	u64 flags;
+
+	/* Return if called before init has run */
+	if (unlikely(!sdcasr_mapbase))
+		return;
+
+	local_irq_save(flags);
+
+	out_le32(sdcasr_mapbase + SDCASR_REG + SDCASR_REG_STRIDE*cpu, astate);
+
+	local_irq_restore(flags);
+}
+
+void restore_astate(int cpu)
+{
+	set_astate(cpu, current_astate);
+}
+
+/*
+ * cpufreq functions
+ */
+
+static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
+{
+	const u32 *max_freqp;
+	u32 max_freq;
+	int i, cur_astate;
+	struct resource res;
+	struct device_node *cpu, *dn;
+	int err = -ENODEV;
+
+	cpu = of_get_cpu_node(policy->cpu, NULL);
+
+	if (!cpu)
+		goto out;
+
+	dn = of_find_compatible_node(NULL, "sdc", "1682m-sdc");
+	if (!dn)
+		goto out;
+	err = of_address_to_resource(dn, 0, &res);
+	of_node_put(dn);
+	if (err)
+		goto out;
+	sdcasr_mapbase = ioremap(res.start + SDCASR_OFFSET, 0x2000);
+	if (!sdcasr_mapbase) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	dn = of_find_compatible_node(NULL, "gizmo", "1682m-gizmo");
+	if (!dn) {
+		err = -ENODEV;
+		goto out_unmap_sdcasr;
+	}
+	err = of_address_to_resource(dn, 0, &res);
+	of_node_put(dn);
+	if (err)
+		goto out_unmap_sdcasr;
+	sdcpwr_mapbase = ioremap(res.start, 0x1000);
+	if (!sdcpwr_mapbase) {
+		err = -EINVAL;
+		goto out_unmap_sdcasr;
+	}
+
+	pr_debug("init cpufreq on CPU %d\n", policy->cpu);
+
+	max_freqp = of_get_property(cpu, "clock-frequency", NULL);
+	if (!max_freqp) {
+		err = -EINVAL;
+		goto out_unmap_sdcpwr;
+	}
+
+	/* we need the freq in kHz */
+	max_freq = *max_freqp / 1000;
+
+	pr_debug("max clock-frequency is at %u kHz\n", max_freq);
+	pr_debug("initializing frequency table\n");
+
+	/* initialize frequency table */
+	for (i=0; pas_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) {
+		pas_freqs[i].frequency = get_astate_freq(pas_freqs[i].index) * 100000;
+		pr_debug("%d: %d\n", i, pas_freqs[i].frequency);
+	}
+
+	policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+
+	policy->cpuinfo.transition_latency = get_gizmo_latency();
+
+	cur_astate = get_cur_astate(policy->cpu);
+	pr_debug("current astate is at %d\n",cur_astate);
+
+	policy->cur = pas_freqs[cur_astate].frequency;
+	policy->cpus = cpu_online_map;
+
+	cpufreq_frequency_table_get_attr(pas_freqs, policy->cpu);
+
+	/* this ensures that policy->cpuinfo_min and policy->cpuinfo_max
+	 * are set correctly
+	 */
+	return cpufreq_frequency_table_cpuinfo(policy, pas_freqs);
+
+out_unmap_sdcpwr:
+	iounmap(sdcpwr_mapbase);
+
+out_unmap_sdcasr:
+	iounmap(sdcasr_mapbase);
+out:
+	return err;
+}
+
+static int pas_cpufreq_cpu_exit(struct cpufreq_policy *policy)
+{
+	if (sdcasr_mapbase)
+		iounmap(sdcasr_mapbase);
+	if (sdcpwr_mapbase)
+		iounmap(sdcpwr_mapbase);
+
+	cpufreq_frequency_table_put_attr(policy->cpu);
+	return 0;
+}
+
+static int pas_cpufreq_verify(struct cpufreq_policy *policy)
+{
+	return cpufreq_frequency_table_verify(policy, pas_freqs);
+}
+
+static int pas_cpufreq_target(struct cpufreq_policy *policy,
+			      unsigned int target_freq,
+			      unsigned int relation)
+{
+	struct cpufreq_freqs freqs;
+	int pas_astate_new;
+	int i;
+
+	cpufreq_frequency_table_target(policy,
+				       pas_freqs,
+				       target_freq,
+				       relation,
+				       &pas_astate_new);
+
+	freqs.old = policy->cur;
+	freqs.new = pas_freqs[pas_astate_new].frequency;
+	freqs.cpu = policy->cpu;
+
+	mutex_lock(&pas_switch_mutex);
+	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+	pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n",
+		 policy->cpu,
+		 pas_freqs[pas_astate_new].frequency,
+		 pas_freqs[pas_astate_new].index);
+
+	current_astate = pas_astate_new;
+
+	for_each_online_cpu(i)
+		set_astate(i, pas_astate_new);
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+	mutex_unlock(&pas_switch_mutex);
+
+	return 0;
+}
+
+static struct cpufreq_driver pas_cpufreq_driver = {
+	.name		= "pas-cpufreq",
+	.owner		= THIS_MODULE,
+	.flags		= CPUFREQ_CONST_LOOPS,
+	.init		= pas_cpufreq_cpu_init,
+	.exit		= pas_cpufreq_cpu_exit,
+	.verify		= pas_cpufreq_verify,
+	.target		= pas_cpufreq_target,
+	.attr		= pas_cpu_freqs_attr,
+};
+
+/*
+ * module init and destoy
+ */
+
+static int __init pas_cpufreq_init(void)
+{
+	if (!machine_is_compatible("PA6T-1682M"))
+		return -ENODEV;
+
+	return cpufreq_register_driver(&pas_cpufreq_driver);
+}
+
+static void __exit pas_cpufreq_exit(void)
+{
+	cpufreq_unregister_driver(&pas_cpufreq_driver);
+}
+
+module_init(pas_cpufreq_init);
+module_exit(pas_cpufreq_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>, Olof Johansson <olof@lixom.net>");
diff --git a/arch/powerpc/platforms/pasemi/gpio_mdio.c b/arch/powerpc/platforms/pasemi/gpio_mdio.c
new file mode 100644
index 0000000..c91a335
--- /dev/null
+++ b/arch/powerpc/platforms/pasemi/gpio_mdio.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2006-2007 PA Semi, Inc
+ *
+ * Author: Olof Johansson, PA Semi
+ *
+ * Maintained by: Olof Johansson <olof@lixom.net>
+ *
+ * Based on drivers/net/fs_enet/mii-bitbang.c.
+ *
+ * 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
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <asm/of_platform.h>
+
+#define DELAY 1
+
+static void __iomem *gpio_regs;
+
+struct gpio_priv {
+	int mdc_pin;
+	int mdio_pin;
+};
+
+#define MDC_PIN(bus)	(((struct gpio_priv *)bus->priv)->mdc_pin)
+#define MDIO_PIN(bus)	(((struct gpio_priv *)bus->priv)->mdio_pin)
+
+static inline void mdio_lo(struct mii_bus *bus)
+{
+	out_le32(gpio_regs+0x10, 1 << MDIO_PIN(bus));
+}
+
+static inline void mdio_hi(struct mii_bus *bus)
+{
+	out_le32(gpio_regs, 1 << MDIO_PIN(bus));
+}
+
+static inline void mdc_lo(struct mii_bus *bus)
+{
+	out_le32(gpio_regs+0x10, 1 << MDC_PIN(bus));
+}
+
+static inline void mdc_hi(struct mii_bus *bus)
+{
+	out_le32(gpio_regs, 1 << MDC_PIN(bus));
+}
+
+static inline void mdio_active(struct mii_bus *bus)
+{
+	out_le32(gpio_regs+0x20, (1 << MDC_PIN(bus)) | (1 << MDIO_PIN(bus)));
+}
+
+static inline void mdio_tristate(struct mii_bus *bus)
+{
+	out_le32(gpio_regs+0x30, (1 << MDIO_PIN(bus)));
+}
+
+static inline int mdio_read(struct mii_bus *bus)
+{
+	return !!(in_le32(gpio_regs+0x40) & (1 << MDIO_PIN(bus)));
+}
+
+static void clock_out(struct mii_bus *bus, int bit)
+{
+	if (bit)
+		mdio_hi(bus);
+	else
+		mdio_lo(bus);
+	udelay(DELAY);
+	mdc_hi(bus);
+	udelay(DELAY);
+	mdc_lo(bus);
+}
+
+/* Utility to send the preamble, address, and register (common to read and write). */
+static void bitbang_pre(struct mii_bus *bus, int read, u8 addr, u8 reg)
+{
+	int i;
+
+	/* CFE uses a really long preamble (40 bits). We'll do the same. */
+	mdio_active(bus);
+	for (i = 0; i < 40; i++) {
+		clock_out(bus, 1);
+	}
+
+	/* send the start bit (01) and the read opcode (10) or write (10) */
+	clock_out(bus, 0);
+	clock_out(bus, 1);
+
+	clock_out(bus, read);
+	clock_out(bus, !read);
+
+	/* send the PHY address */
+	for (i = 0; i < 5; i++) {
+		clock_out(bus, (addr & 0x10) != 0);
+		addr <<= 1;
+	}
+
+	/* send the register address */
+	for (i = 0; i < 5; i++) {
+		clock_out(bus, (reg & 0x10) != 0);
+		reg <<= 1;
+	}
+}
+
+static int gpio_mdio_read(struct mii_bus *bus, int phy_id, int location)
+{
+	u16 rdreg;
+	int ret, i;
+	u8 addr = phy_id & 0xff;
+	u8 reg = location & 0xff;
+
+	bitbang_pre(bus, 1, addr, reg);
+
+	/* tri-state our MDIO I/O pin so we can read */
+	mdio_tristate(bus);
+	udelay(DELAY);
+	mdc_hi(bus);
+	udelay(DELAY);
+	mdc_lo(bus);
+
+	/* read 16 bits of register data, MSB first */
+	rdreg = 0;
+	for (i = 0; i < 16; i++) {
+		mdc_lo(bus);
+		udelay(DELAY);
+		mdc_hi(bus);
+		udelay(DELAY);
+		mdc_lo(bus);
+		udelay(DELAY);
+		rdreg <<= 1;
+		rdreg |= mdio_read(bus);
+	}
+
+	mdc_hi(bus);
+	udelay(DELAY);
+	mdc_lo(bus);
+	udelay(DELAY);
+
+	ret = rdreg;
+
+	return ret;
+}
+
+static int gpio_mdio_write(struct mii_bus *bus, int phy_id, int location, u16 val)
+{
+	int i;
+
+	u8 addr = phy_id & 0xff;
+	u8 reg = location & 0xff;
+	u16 value = val & 0xffff;
+
+	bitbang_pre(bus, 0, addr, reg);
+
+	/* send the turnaround (10) */
+	mdc_lo(bus);
+	mdio_hi(bus);
+	udelay(DELAY);
+	mdc_hi(bus);
+	udelay(DELAY);
+	mdc_lo(bus);
+	mdio_lo(bus);
+	udelay(DELAY);
+	mdc_hi(bus);
+	udelay(DELAY);
+
+	/* write 16 bits of register data, MSB first */
+	for (i = 0; i < 16; i++) {
+		mdc_lo(bus);
+		if (value & 0x8000)
+			mdio_hi(bus);
+		else
+			mdio_lo(bus);
+		udelay(DELAY);
+		mdc_hi(bus);
+		udelay(DELAY);
+		value <<= 1;
+	}
+
+	/*
+	 * Tri-state the MDIO line.
+	 */
+	mdio_tristate(bus);
+	mdc_lo(bus);
+	udelay(DELAY);
+	mdc_hi(bus);
+	udelay(DELAY);
+	return 0;
+}
+
+static int gpio_mdio_reset(struct mii_bus *bus)
+{
+	/*nothing here - dunno how to reset it*/
+	return 0;
+}
+
+
+static int __devinit gpio_mdio_probe(struct of_device *ofdev,
+				     const struct of_device_id *match)
+{
+	struct device *dev = &ofdev->dev;
+	struct device_node *np = ofdev->node;
+	struct device_node *gpio_np;
+	struct mii_bus *new_bus;
+	struct resource res;
+	struct gpio_priv *priv;
+	const unsigned int *prop;
+	int err = 0;
+	int i;
+
+	gpio_np = of_find_compatible_node(NULL, "gpio", "1682m-gpio");
+
+	if (!gpio_np)
+		return -ENODEV;
+
+	err = of_address_to_resource(gpio_np, 0, &res);
+	of_node_put(gpio_np);
+
+	if (err)
+		return -EINVAL;
+
+	if (!gpio_regs)
+		gpio_regs = ioremap(res.start, 0x100);
+
+	if (!gpio_regs)
+		return -EPERM;
+
+	priv = kzalloc(sizeof(struct gpio_priv), GFP_KERNEL);
+	if (priv == NULL)
+		return -ENOMEM;
+
+	new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL);
+
+	if (new_bus == NULL)
+		return -ENOMEM;
+
+	new_bus->name = "pasemi gpio mdio bus",
+	new_bus->read = &gpio_mdio_read,
+	new_bus->write = &gpio_mdio_write,
+	new_bus->reset = &gpio_mdio_reset,
+
+	prop = of_get_property(np, "reg", NULL);
+	new_bus->id = *prop;
+	new_bus->priv = priv;
+
+	new_bus->phy_mask = 0;
+
+	new_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL);
+	for(i = 0; i < PHY_MAX_ADDR; ++i)
+		new_bus->irq[i] = irq_create_mapping(NULL, 10);
+
+
+	prop = of_get_property(np, "mdc-pin", NULL);
+	priv->mdc_pin = *prop;
+
+	prop = of_get_property(np, "mdio-pin", NULL);
+	priv->mdio_pin = *prop;
+
+	new_bus->dev = dev;
+	dev_set_drvdata(dev, new_bus);
+
+	err = mdiobus_register(new_bus);
+
+	if (0 != err) {
+		printk(KERN_ERR "%s: Cannot register as MDIO bus, err %d\n",
+				new_bus->name, err);
+		goto bus_register_fail;
+	}
+
+	return 0;
+
+bus_register_fail:
+	kfree(new_bus);
+
+	return err;
+}
+
+
+static int gpio_mdio_remove(struct of_device *dev)
+{
+	struct mii_bus *bus = dev_get_drvdata(&dev->dev);
+
+	mdiobus_unregister(bus);
+
+	dev_set_drvdata(&dev->dev, NULL);
+
+	kfree(bus->priv);
+	bus->priv = NULL;
+	kfree(bus);
+
+	return 0;
+}
+
+static struct of_device_id gpio_mdio_match[] =
+{
+	{
+		.compatible      = "gpio-mdio",
+	},
+	{},
+};
+
+static struct of_platform_driver gpio_mdio_driver =
+{
+	.name		= "gpio-mdio-bitbang",
+	.match_table	= gpio_mdio_match,
+	.probe		= gpio_mdio_probe,
+	.remove		= gpio_mdio_remove,
+};
+
+int gpio_mdio_init(void)
+{
+	return of_register_platform_driver(&gpio_mdio_driver);
+}
+
+void gpio_mdio_exit(void)
+{
+	of_unregister_platform_driver(&gpio_mdio_driver);
+}
+device_initcall(gpio_mdio_init);
+
diff --git a/arch/powerpc/platforms/pasemi/idle.c b/arch/powerpc/platforms/pasemi/idle.c
index 1ca3ff3..5985ce0 100644
--- a/arch/powerpc/platforms/pasemi/idle.c
+++ b/arch/powerpc/platforms/pasemi/idle.c
@@ -61,6 +61,10 @@
 		/* do system reset */
 		return 0;
 	}
+
+	/* Set higher astate since we come out of power savings at 0 */
+	restore_astate(hard_smp_processor_id());
+
 	/* everything handled */
 	regs->msr |= MSR_RI;
 	return 1;
@@ -68,6 +72,11 @@
 
 void __init pasemi_idle_init(void)
 {
+#ifndef CONFIG_PPC_PASEMI_CPUFREQ
+	printk(KERN_WARNING "No cpufreq driver, powersavings modes disabled\n");
+	current_mode = 0;
+#endif
+
 	ppc_md.system_reset_exception = pasemi_system_reset_exception;
 	ppc_md.power_save = modes[current_mode].entry;
 	printk(KERN_INFO "Using PA6T idle loop (%s)\n", modes[current_mode].name);
diff --git a/arch/powerpc/platforms/pasemi/iommu.c b/arch/powerpc/platforms/pasemi/iommu.c
index 71dbf1a..95fa6a7 100644
--- a/arch/powerpc/platforms/pasemi/iommu.c
+++ b/arch/powerpc/platforms/pasemi/iommu.c
@@ -249,13 +249,13 @@
 	iommu_off = 1;
 #else
 	iommu_off = of_chosen &&
-			get_property(of_chosen, "linux,iommu-off", NULL);
+			of_get_property(of_chosen, "linux,iommu-off", NULL);
 #endif
 	if (iommu_off) {
 		/* Direct I/O, IOMMU off */
 		ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_null;
 		ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_null;
-		pci_dma_ops = &dma_direct_ops;
+		set_pci_dma_ops(&dma_direct_ops);
 
 		return;
 	}
@@ -266,7 +266,7 @@
 	ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_pasemi;
 	ppc_md.tce_build = iobmap_build;
 	ppc_md.tce_free  = iobmap_free;
-	pci_dma_ops = &dma_iommu_ops;
+	set_pci_dma_ops(&dma_iommu_ops);
 }
 
 void __init alloc_iobmap_l2(void)
diff --git a/arch/powerpc/platforms/pasemi/pasemi.h b/arch/powerpc/platforms/pasemi/pasemi.h
index 2d3927e..be84954 100644
--- a/arch/powerpc/platforms/pasemi/pasemi.h
+++ b/arch/powerpc/platforms/pasemi/pasemi.h
@@ -14,6 +14,14 @@
 extern void idle_spin(void);
 extern void idle_doze(void);
 
+/* Restore astate to last set */
+#ifdef CONFIG_PPC_PASEMI_CPUFREQ
+extern void restore_astate(int cpu);
+#else
+static inline void restore_astate(int cpu)
+{
+}
+#endif
 
 
 #endif /* _PASEMI_PASEMI_H */
diff --git a/arch/powerpc/platforms/pasemi/pci.c b/arch/powerpc/platforms/pasemi/pci.c
index 7ecb2ba..056243d 100644
--- a/arch/powerpc/platforms/pasemi/pci.c
+++ b/arch/powerpc/platforms/pasemi/pci.c
@@ -33,7 +33,17 @@
 
 #define PA_PXP_CFA(bus, devfn, off) (((bus) << 20) | ((devfn) << 12) | (off))
 
-#define CONFIG_OFFSET_VALID(off) ((off) < 4096)
+static inline int pa_pxp_offset_valid(u8 bus, u8 devfn, int offset)
+{
+	/* Device 0 Function 0 is special: It's config space spans function 1 as
+	 * well, so allow larger offset. It's really a two-function device but the
+	 * second function does not probe.
+	 */
+	if (bus == 0 && devfn == 0)
+		return offset < 8192;
+	else
+		return offset < 4096;
+}
 
 static void volatile __iomem *pa_pxp_cfg_addr(struct pci_controller *hose,
 				       u8 bus, u8 devfn, int offset)
@@ -51,7 +61,7 @@
 	if (!hose)
 		return PCIBIOS_DEVICE_NOT_FOUND;
 
-	if (!CONFIG_OFFSET_VALID(offset))
+	if (!pa_pxp_offset_valid(bus->number, devfn, offset))
 		return PCIBIOS_BAD_REGISTER_NUMBER;
 
 	addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset);
@@ -85,7 +95,7 @@
 	if (!hose)
 		return PCIBIOS_DEVICE_NOT_FOUND;
 
-	if (!CONFIG_OFFSET_VALID(offset))
+	if (!pa_pxp_offset_valid(bus->number, devfn, offset))
 		return PCIBIOS_BAD_REGISTER_NUMBER;
 
 	addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset);
diff --git a/arch/powerpc/platforms/pasemi/setup.c b/arch/powerpc/platforms/pasemi/setup.c
index 449cf1a..f88f0ec 100644
--- a/arch/powerpc/platforms/pasemi/setup.c
+++ b/arch/powerpc/platforms/pasemi/setup.c
@@ -35,6 +35,7 @@
 #include <asm/mpic.h>
 #include <asm/smp.h>
 #include <asm/time.h>
+#include <asm/of_platform.h>
 
 #include "pasemi.h"
 
@@ -101,12 +102,6 @@
 	pasemi_idle_init();
 }
 
-/* No legacy IO on our parts */
-static int pas_check_legacy_ioport(unsigned int baseport)
-{
-	return -ENODEV;
-}
-
 static __init void pas_init_IRQ(void)
 {
 	struct device_node *np;
@@ -136,8 +131,8 @@
 
 	/* Find address list in /platform-open-pic */
 	root = of_find_node_by_path("/");
-	naddr = prom_n_addr_cells(root);
-	opprop = get_property(root, "platform-open-pic", &opplen);
+	naddr = of_n_addr_cells(root);
+	opprop = of_get_property(root, "platform-open-pic", &opplen);
 	if (!opprop) {
 		printk(KERN_ERR "No platform-open-pic property.\n");
 		of_node_put(root);
@@ -147,7 +142,7 @@
 	printk(KERN_DEBUG "OpenPIC addr: %lx\n", openpic_addr);
 
 	mpic = mpic_alloc(mpic_node, openpic_addr,
-			  MPIC_PRIMARY|MPIC_LARGE_VECTORS,
+			  MPIC_PRIMARY|MPIC_LARGE_VECTORS|MPIC_WANTS_RESET,
 			  0, 0, " PAS-OPIC  ");
 	BUG_ON(!mpic);
 
@@ -209,6 +204,20 @@
 	iommu_init_early_pasemi();
 }
 
+static struct of_device_id pasemi_bus_ids[] = {
+	{ .type = "sdc", },
+	{},
+};
+
+static int __init pasemi_publish_devices(void)
+{
+	/* Publish OF platform devices for southbridge IOs */
+	of_platform_bus_probe(NULL, pasemi_bus_ids, NULL);
+
+	return 0;
+}
+device_initcall(pasemi_publish_devices);
+
 
 /*
  * Called very early, MMU is off, device-tree isn't unflattened
@@ -237,7 +246,6 @@
 	.restart		= pas_restart,
 	.get_boot_time		= pas_get_boot_time,
 	.calibrate_decr		= generic_calibrate_decr,
-	.check_legacy_ioport    = pas_check_legacy_ioport,
 	.progress		= pas_progress,
 	.machine_check_exception = pas_machine_check_handler,
 	.pci_irq_fixup		= pas_pci_irq_fixup,
diff --git a/arch/powerpc/platforms/powermac/Kconfig b/arch/powerpc/platforms/powermac/Kconfig
new file mode 100644
index 0000000..5b7afe5
--- /dev/null
+++ b/arch/powerpc/platforms/powermac/Kconfig
@@ -0,0 +1,20 @@
+config PPC_PMAC
+	bool "Apple PowerMac based machines"
+	depends on PPC_MULTIPLATFORM
+	select MPIC
+	select PPC_INDIRECT_PCI if PPC32
+	select PPC_MPC106 if PPC32
+	select PPC_NATIVE
+	default y
+
+config PPC_PMAC64
+	bool
+	depends on PPC_PMAC && POWER4
+	select MPIC
+	select U3_DART
+	select MPIC_U3_HT_IRQS
+	select GENERIC_TBSYNC
+	select PPC_970_NAP
+	default y
+
+
diff --git a/arch/powerpc/platforms/powermac/backlight.c b/arch/powerpc/platforms/powermac/backlight.c
index de7440e..d679964 100644
--- a/arch/powerpc/platforms/powermac/backlight.c
+++ b/arch/powerpc/platforms/powermac/backlight.c
@@ -56,13 +56,16 @@
 
 int pmac_has_backlight_type(const char *type)
 {
-	struct device_node* bk_node = find_devices("backlight");
+	struct device_node* bk_node = of_find_node_by_name(NULL, "backlight");
 
 	if (bk_node) {
-		const char *prop = get_property(bk_node,
+		const char *prop = of_get_property(bk_node,
 				"backlight-control", NULL);
-		if (prop && strncmp(prop, type, strlen(type)) == 0)
+		if (prop && strncmp(prop, type, strlen(type)) == 0) {
+			of_node_put(bk_node);
 			return 1;
+		}
+		of_node_put(bk_node);
 	}
 
 	return 0;
diff --git a/arch/powerpc/platforms/powermac/cpufreq_32.c b/arch/powerpc/platforms/powermac/cpufreq_32.c
index c2b6b41..8943a94 100644
--- a/arch/powerpc/platforms/powermac/cpufreq_32.c
+++ b/arch/powerpc/platforms/powermac/cpufreq_32.c
@@ -421,7 +421,7 @@
 
 static u32 read_gpio(struct device_node *np)
 {
-	const u32 *reg = get_property(np, "reg", NULL);
+	const u32 *reg = of_get_property(np, "reg", NULL);
 	u32 offset;
 
 	if (reg == NULL)
@@ -521,13 +521,14 @@
 		int lenp, rc;
 		const u32 *freqs, *ratio;
 
-		freqs = get_property(cpunode, "bus-frequencies", &lenp);
+		freqs = of_get_property(cpunode, "bus-frequencies", &lenp);
 		lenp /= sizeof(u32);
 		if (freqs == NULL || lenp != 2) {
 			printk(KERN_ERR "cpufreq: bus-frequencies incorrect or missing\n");
 			return 1;
 		}
-		ratio = get_property(cpunode, "processor-to-bus-ratio*2", NULL);
+		ratio = of_get_property(cpunode, "processor-to-bus-ratio*2",
+						NULL);
 		if (ratio == NULL) {
 			printk(KERN_ERR "cpufreq: processor-to-bus-ratio*2 missing\n");
 			return 1;
@@ -562,7 +563,7 @@
 	/* If we use the PMU, look for the min & max frequencies in the
 	 * device-tree
 	 */
-	value = get_property(cpunode, "min-clock-frequency", NULL);
+	value = of_get_property(cpunode, "min-clock-frequency", NULL);
 	if (!value)
 		return 1;
 	low_freq = (*value) / 1000;
@@ -571,7 +572,7 @@
 	if (low_freq < 100000)
 		low_freq *= 10;
 
-	value = get_property(cpunode, "max-clock-frequency", NULL);
+	value = of_get_property(cpunode, "max-clock-frequency", NULL);
 	if (!value)
 		return 1;
 	hi_freq = (*value) / 1000;
@@ -585,7 +586,7 @@
 {
 	struct device_node *volt_gpio_np;
 
-	if (get_property(cpunode, "dynamic-power-step", NULL) == NULL)
+	if (of_get_property(cpunode, "dynamic-power-step", NULL) == NULL)
 		return 1;
 
 	volt_gpio_np = of_find_node_by_name(NULL, "cpu-vcore-select");
@@ -614,11 +615,11 @@
 	u32 pvr;
 	const u32 *value;
 
-	if (get_property(cpunode, "dynamic-power-step", NULL) == NULL)
+	if (of_get_property(cpunode, "dynamic-power-step", NULL) == NULL)
 		return 1;
 
 	hi_freq = cur_freq;
-	value = get_property(cpunode, "reduced-clock-frequency", NULL);
+	value = of_get_property(cpunode, "reduced-clock-frequency", NULL);
 	if (!value)
 		return 1;
 	low_freq = (*value) / 1000;
@@ -657,19 +658,19 @@
 		return 0;
 
 	/* Assume only one CPU */
-	cpunode = find_type_devices("cpu");
+	cpunode = of_find_node_by_type(NULL, "cpu");
 	if (!cpunode)
 		goto out;
 
 	/* Get current cpu clock freq */
-	value = get_property(cpunode, "clock-frequency", NULL);
+	value = of_get_property(cpunode, "clock-frequency", NULL);
 	if (!value)
 		goto out;
 	cur_freq = (*value) / 1000;
 
 	/*  Check for 7447A based MacRISC3 */
 	if (machine_is_compatible("MacRISC3") &&
-	    get_property(cpunode, "dynamic-power-step", NULL) &&
+	    of_get_property(cpunode, "dynamic-power-step", NULL) &&
 	    PVR_VER(mfspr(SPRN_PVR)) == 0x8003) {
 		pmac_cpufreq_init_7447A(cpunode);
 	/* Check for other MacRISC3 machines */
@@ -707,6 +708,7 @@
 	else if (PVR_VER(mfspr(SPRN_PVR)) == 0x7000)
 		pmac_cpufreq_init_750FX(cpunode);
 out:
+	of_node_put(cpunode);
 	if (set_speed_proc == NULL)
 		return -ENODEV;
 
diff --git a/arch/powerpc/platforms/powermac/cpufreq_64.c b/arch/powerpc/platforms/powermac/cpufreq_64.c
index 9d22361..567d552 100644
--- a/arch/powerpc/platforms/powermac/cpufreq_64.c
+++ b/arch/powerpc/platforms/powermac/cpufreq_64.c
@@ -410,7 +410,7 @@
 	/* Get first CPU node */
 	for (cpunode = NULL;
 	     (cpunode = of_get_next_child(cpus, cpunode)) != NULL;) {
-		const u32 *reg = get_property(cpunode, "reg", NULL);
+		const u32 *reg = of_get_property(cpunode, "reg", NULL);
 		if (reg == NULL || (*reg) != 0)
 			continue;
 		if (!strcmp(cpunode->type, "cpu"))
@@ -422,7 +422,7 @@
 	}
 
 	/* Check 970FX for now */
-	valp = get_property(cpunode, "cpu-version", NULL);
+	valp = of_get_property(cpunode, "cpu-version", NULL);
 	if (!valp) {
 		DBG("No cpu-version property !\n");
 		goto bail_noprops;
@@ -434,7 +434,7 @@
 	}
 
 	/* Look for the powertune data in the device-tree */
-	g5_pmode_data = get_property(cpunode, "power-mode-data",&psize);
+	g5_pmode_data = of_get_property(cpunode, "power-mode-data",&psize);
 	if (!g5_pmode_data) {
 		DBG("No power-mode-data !\n");
 		goto bail_noprops;
@@ -493,7 +493,7 @@
 	 * half freq in this version. So far, I haven't yet seen a machine
 	 * supporting anything else.
 	 */
-	valp = get_property(cpunode, "clock-frequency", NULL);
+	valp = of_get_property(cpunode, "clock-frequency", NULL);
 	if (!valp)
 		return -ENODEV;
 	max_freq = (*valp)/1000;
@@ -563,7 +563,7 @@
 	/* Lookup the cpuid eeprom node */
         cpuid = of_find_node_by_path("/u3@0,f8000000/i2c@f8001000/cpuid@a0");
 	if (cpuid != NULL)
-		eeprom = get_property(cpuid, "cpuid", NULL);
+		eeprom = of_get_property(cpuid, "cpuid", NULL);
 	if (eeprom == NULL) {
 		printk(KERN_ERR "cpufreq: Can't find cpuid EEPROM !\n");
 		rc = -ENODEV;
@@ -573,13 +573,13 @@
 	/* Lookup the i2c hwclock */
 	for (hwclock = NULL;
 	     (hwclock = of_find_node_by_name(hwclock, "i2c-hwclock")) != NULL;){
-		const char *loc = get_property(hwclock,
+		const char *loc = of_get_property(hwclock,
 				"hwctrl-location", NULL);
 		if (loc == NULL)
 			continue;
 		if (strcmp(loc, "CPU CLOCK"))
 			continue;
-		if (!get_property(hwclock, "platform-get-frequency", NULL))
+		if (!of_get_property(hwclock, "platform-get-frequency", NULL))
 			continue;
 		break;
 	}
@@ -638,7 +638,7 @@
 	 */
 
 	/* Get max frequency from device-tree */
-	valp = get_property(cpunode, "clock-frequency", NULL);
+	valp = of_get_property(cpunode, "clock-frequency", NULL);
 	if (!valp) {
 		printk(KERN_ERR "cpufreq: Can't find CPU frequency !\n");
 		rc = -ENODEV;
diff --git a/arch/powerpc/platforms/powermac/feature.c b/arch/powerpc/platforms/powermac/feature.c
index 24cc50c..52cfdd8 100644
--- a/arch/powerpc/platforms/powermac/feature.c
+++ b/arch/powerpc/platforms/powermac/feature.c
@@ -1044,6 +1044,7 @@
 	unsigned long flags;
 	struct macio_chip *macio;
 	struct device_node *np;
+	struct device_node *cpus;
 	const int dflt_reset_lines[] = {	KL_GPIO_RESET_CPU0,
 						KL_GPIO_RESET_CPU1,
 						KL_GPIO_RESET_CPU2,
@@ -1053,12 +1054,12 @@
 	if (macio->type != macio_keylargo)
 		return -ENODEV;
 
-	np = find_path_device("/cpus");
-	if (np == NULL)
+	cpus = of_find_node_by_path("/cpus");
+	if (cpus == NULL)
 		return -ENODEV;
-	for (np = np->child; np != NULL; np = np->sibling) {
-		const u32 *num = get_property(np, "reg", NULL);
-		const u32 *rst = get_property(np, "soft-reset", NULL);
+	for (np = cpus->child; np != NULL; np = np->sibling) {
+		const u32 *num = of_get_property(np, "reg", NULL);
+		const u32 *rst = of_get_property(np, "soft-reset", NULL);
 		if (num == NULL || rst == NULL)
 			continue;
 		if (param == *num) {
@@ -1066,6 +1067,7 @@
 			break;
 		}
 	}
+	of_node_put(cpus);
 	if (np == NULL || reset_io == 0)
 		reset_io = dflt_reset_lines[param];
 
@@ -1095,7 +1097,7 @@
 	    macio->type != macio_intrepid)
 		return -ENODEV;
 
-	prop = get_property(node, "AAPL,clock-id", NULL);
+	prop = of_get_property(node, "AAPL,clock-id", NULL);
 	if (!prop)
 		return -ENODEV;
 	if (strncmp(prop, "usb0u048", 8) == 0)
@@ -1497,17 +1499,18 @@
 	unsigned long flags;
 	struct macio_chip *macio;
 	struct device_node *np;
+	struct device_node *cpus;
 
 	macio = &macio_chips[0];
 	if (macio->type != macio_keylargo2 && macio->type != macio_shasta)
 		return -ENODEV;
 
-	np = find_path_device("/cpus");
-	if (np == NULL)
+	cpus = of_find_node_by_path("/cpus");
+	if (cpus == NULL)
 		return -ENODEV;
-	for (np = np->child; np != NULL; np = np->sibling) {
-		const u32 *num = get_property(np, "reg", NULL);
-		const u32 *rst = get_property(np, "soft-reset", NULL);
+	for (np = cpus->child; np != NULL; np = np->sibling) {
+		const u32 *num = of_get_property(np, "reg", NULL);
+		const u32 *rst = of_get_property(np, "soft-reset", NULL);
 		if (num == NULL || rst == NULL)
 			continue;
 		if (param == *num) {
@@ -1515,6 +1518,7 @@
 			break;
 		}
 	}
+	of_node_put(cpus);
 	if (np == NULL || reset_io == 0)
 		return -ENODEV;
 
@@ -2404,14 +2408,15 @@
 	struct macio_chip *macio = &macio_chips[0];
 	const char *model = NULL;
 	struct device_node *dt;
+	int ret = 0;
 
 	/* Lookup known motherboard type in device-tree. First try an
 	 * exact match on the "model" property, then try a "compatible"
 	 * match is none is found.
 	 */
-	dt = find_devices("device-tree");
+	dt = of_find_node_by_name(NULL, "device-tree");
 	if (dt != NULL)
-		model = get_property(dt, "model", NULL);
+		model = of_get_property(dt, "model", NULL);
 	for(i=0; model && i<(sizeof(pmac_mb_defs)/sizeof(struct pmac_mb_def)); i++) {
 	    if (strcmp(model, pmac_mb_defs[i].model_string) == 0) {
 		pmac_mb = pmac_mb_defs[i];
@@ -2474,15 +2479,18 @@
 		break;
 #endif /* CONFIG_POWER4 */
 	default:
-		return -ENODEV;
+		ret = -ENODEV;
+		goto done;
 	}
 found:
 #ifndef CONFIG_POWER4
 	/* Fixup Hooper vs. Comet */
 	if (pmac_mb.model_id == PMAC_TYPE_HOOPER) {
 		u32 __iomem * mach_id_ptr = ioremap(0xf3000034, 4);
-		if (!mach_id_ptr)
-			return -ENODEV;
+		if (!mach_id_ptr) {
+			ret = -ENODEV;
+			goto done;
+		}
 		/* Here, I used to disable the media-bay on comet. It
 		 * appears this is wrong, the floppy connector is actually
 		 * a kind of media-bay and works with the current driver.
@@ -2499,18 +2507,26 @@
 	 * that all Apple OF revs did it properly, I do it the paranoid way.
 	 */
 	while (uninorth_base && uninorth_rev > 3) {
-		struct device_node *np = find_path_device("/cpus");
-		if (!np || !np->child) {
+		struct device_node *cpus = of_find_node_by_path("/cpus");
+		struct device_node *np;
+
+		if (!cpus || !cpus->child) {
 			printk(KERN_WARNING "Can't find CPU(s) in device tree !\n");
+			of_node_put(cpus);
 			break;
 		}
-		np = np->child;
+		np = cpus->child;
 		/* Nap mode not supported on SMP */
-		if (np->sibling)
+		if (np->sibling) {
+			of_node_put(cpus);
 			break;
+		}
 		/* Nap mode not supported if flush-on-lock property is present */
-		if (get_property(np, "flush-on-lock", NULL))
+		if (of_get_property(np, "flush-on-lock", NULL)) {
+			of_node_put(cpus);
 			break;
+		}
+		of_node_put(cpus);
 		powersave_nap = 1;
 		printk(KERN_DEBUG "Processor NAP mode on idle enabled.\n");
 		break;
@@ -2532,7 +2548,9 @@
 
 
 	printk(KERN_INFO "PowerMac motherboard: %s\n", pmac_mb.model_name);
-	return 0;
+done:
+	of_node_put(dt);
+	return ret;
 }
 
 /* Initialize the Core99 UniNorth host bridge and memory controller
@@ -2558,7 +2576,7 @@
 	if (uninorth_node == NULL)
 		return;
 
-	addrp = get_property(uninorth_node, "reg", NULL);
+	addrp = of_get_property(uninorth_node, "reg", NULL);
 	if (addrp == NULL)
 		return;
 	address = of_translate_address(uninorth_node, addrp);
@@ -2642,7 +2660,7 @@
 		return;
 	}
 	if (type == macio_keylargo || type == macio_keylargo2) {
-		const u32 *did = get_property(node, "device-id", NULL);
+		const u32 *did = of_get_property(node, "device-id", NULL);
 		if (*did == 0x00000025)
 			type = macio_pangea;
 		if (*did == 0x0000003e)
@@ -2655,7 +2673,7 @@
 	macio_chips[i].base	= base;
 	macio_chips[i].flags	= MACIO_FLAG_SCCB_ON | MACIO_FLAG_SCCB_ON;
 	macio_chips[i].name	= macio_names[type];
-	revp = get_property(node, "revision-id", NULL);
+	revp = of_get_property(node, "revision-id", NULL);
 	if (revp)
 		macio_chips[i].rev = *revp;
 	printk(KERN_INFO "Found a %s mac-io controller, rev: %d, mapped at 0x%p\n",
@@ -2706,8 +2724,8 @@
 	int port_type = PMAC_SCC_ASYNC;
 	int modem = 0;
 
-	slots = get_property(np, "slot-names", &len);
-	conn = get_property(np, "AAPL,connector", &len);
+	slots = of_get_property(np, "slot-names", &len);
+	conn = of_get_property(np, "AAPL,connector", &len);
 	if (conn && (strcmp(conn, "infrared") == 0))
 		port_type = PMAC_SCC_IRDA;
 	else if (device_is_compatible(np, "cobalt"))
@@ -2735,12 +2753,14 @@
 	 * differenciate them all and since that hack was there for a long
 	 * time, I'll keep it around
 	 */
-	if (macio_chips[0].type == macio_ohare && !find_devices("via-pmu")) {
+	if (macio_chips[0].type == macio_ohare) {
 		struct macio_chip *macio = &macio_chips[0];
-		MACIO_OUT32(OHARE_FCR, STARMAX_FEATURES);
-	} else if (macio_chips[0].type == macio_ohare) {
-		struct macio_chip *macio = &macio_chips[0];
-		MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE);
+		np = of_find_node_by_name(NULL, "via-pmu");
+		if (np)
+			MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE);
+		else
+			MACIO_OUT32(OHARE_FCR, STARMAX_FEATURES);
+		of_node_put(np);
 	} else if (macio_chips[1].type == macio_ohare) {
 		struct macio_chip *macio = &macio_chips[1];
 		MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE);
@@ -2833,14 +2853,13 @@
 		}
 
 		/* Switch airport off */
-		np = find_devices("radio");
-		while(np) {
+		for_each_node_by_name(np, "radio") {
 			if (np && np->parent == macio_chips[0].of_node) {
 				macio_chips[0].flags |= MACIO_FLAG_AIRPORT_ON;
 				core99_airport_enable(np, 0, 0);
 			}
-			np = np->next;
 		}
+		of_node_put(np);
 	}
 
 	/* On all machines that support sound PM, switch sound off */
@@ -2860,16 +2879,12 @@
 #endif /* CONFIG_POWER4 */
 
 	/* On all machines, switch modem & serial ports off */
-	np = find_devices("ch-a");
-	while(np) {
+	for_each_node_by_name(np, "ch-a")
 		initial_serial_shutdown(np);
-		np = np->next;
-	}
-	np = find_devices("ch-b");
-	while(np) {
+	of_node_put(np);
+	for_each_node_by_name(np, "ch-b")
 		initial_serial_shutdown(np);
-		np = np->next;
-	}
+	of_node_put(np);
 }
 
 void __init
diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c
index bfc4829..5430e14 100644
--- a/arch/powerpc/platforms/powermac/low_i2c.c
+++ b/arch/powerpc/platforms/powermac/low_i2c.c
@@ -491,7 +491,7 @@
 	 * on all i2c keywest nodes so far ... we would have to fallback
 	 * to macio parsing if that wasn't the case
 	 */
-	addrp = get_property(np, "AAPL,address", NULL);
+	addrp = of_get_property(np, "AAPL,address", NULL);
 	if (addrp == NULL) {
 		printk(KERN_ERR "low_i2c: Can't find address for %s\n",
 		       np->full_name);
@@ -505,13 +505,13 @@
 	host->timeout_timer.function = kw_i2c_timeout;
 	host->timeout_timer.data = (unsigned long)host;
 
-	psteps = get_property(np, "AAPL,address-step", NULL);
+	psteps = of_get_property(np, "AAPL,address-step", NULL);
 	steps = psteps ? (*psteps) : 0x10;
 	for (host->bsteps = 0; (steps & 0x01) == 0; host->bsteps++)
 		steps >>= 1;
 	/* Select interface rate */
 	host->speed = KW_I2C_MODE_25KHZ;
-	prate = get_property(np, "AAPL,i2c-rate", NULL);
+	prate = of_get_property(np, "AAPL,i2c-rate", NULL);
 	if (prate) switch(*prate) {
 	case 100:
 		host->speed = KW_I2C_MODE_100KHZ;
@@ -619,7 +619,7 @@
 		} else {
 			for (child = NULL;
 			     (child = of_get_next_child(np, child)) != NULL;) {
-				const u32 *reg = get_property(child,
+				const u32 *reg = of_get_property(child,
 						"reg", NULL);
 				if (reg == NULL)
 					continue;
@@ -905,7 +905,7 @@
 		if (strcmp(busnode->type, "i2c") &&
 		    strcmp(busnode->type, "i2c-bus"))
 			continue;
-		reg = get_property(busnode, "reg", NULL);
+		reg = of_get_property(busnode, "reg", NULL);
 		if (reg == NULL)
 			continue;
 
@@ -950,7 +950,8 @@
 			if (p == bus->busnode) {
 				if (prev && bus->flags & pmac_i2c_multibus) {
 					const u32 *reg;
-					reg = get_property(prev, "reg", NULL);
+					reg = of_get_property(prev, "reg",
+								NULL);
 					if (!reg)
 						continue;
 					if (((*reg) >> 8) != bus->channel)
@@ -971,7 +972,7 @@
 
 u8 pmac_i2c_get_dev_addr(struct device_node *device)
 {
-	const u32 *reg = get_property(device, "reg", NULL);
+	const u32 *reg = of_get_property(device, "reg", NULL);
 
 	if (reg == NULL)
 		return 0;
diff --git a/arch/powerpc/platforms/powermac/pci.c b/arch/powerpc/platforms/powermac/pci.c
index 6fbac30..22c4ae4 100644
--- a/arch/powerpc/platforms/powermac/pci.c
+++ b/arch/powerpc/platforms/powermac/pci.c
@@ -70,11 +70,11 @@
 		int len;
 
 		/* For PCI<->PCI bridges or CardBus bridges, we go down */
-		class_code = get_property(node, "class-code", NULL);
+		class_code = of_get_property(node, "class-code", NULL);
 		if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI &&
 			(*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS))
 			continue;
-		bus_range = get_property(node, "bus-range", &len);
+		bus_range = of_get_property(node, "bus-range", &len);
 		if (bus_range != NULL && len > 2 * sizeof(int)) {
 			if (bus_range[1] > higher)
 				higher = bus_range[1];
@@ -100,7 +100,7 @@
 	if (prop == NULL || prop->length < 2 * sizeof(int))
 		return;
 
-	bus_range = (int *)prop->value;
+	bus_range = prop->value;
 	bus_range[1] = fixup_one_level_bus_range(bridge->child, bus_range[1]);
 }
 
@@ -246,8 +246,8 @@
 	if (np == NULL)
 		return PCIBIOS_DEVICE_NOT_FOUND;
 
-	vendor = get_property(np, "vendor-id", NULL);
-	device = get_property(np, "device-id", NULL);
+	vendor = of_get_property(np, "vendor-id", NULL);
+	device = of_get_property(np, "device-id", NULL);
 	if (vendor == NULL || device == NULL)
 		return PCIBIOS_DEVICE_NOT_FOUND;
 
@@ -622,13 +622,14 @@
 
 	/* XXX it would be better here to identify the specific
 	   PCI-PCI bridge chip we have. */
-	if ((p2pbridge = find_devices("pci-bridge")) == 0
+	p2pbridge = of_find_node_by_name(NULL, "pci-bridge");
+	if (p2pbridge == NULL
 	    || p2pbridge->parent == NULL
 	    || strcmp(p2pbridge->parent->name, "pci") != 0)
-		return;
+		goto done;
 	if (pci_device_from_OF_node(p2pbridge, &bus, &devfn) < 0) {
 		DBG("Can't find PCI infos for PCI<->PCI bridge\n");
-		return;
+		goto done;
 	}
 	/* Warning: At this point, we have not yet renumbered all busses.
 	 * So we must use OF walking to find out hose
@@ -636,16 +637,18 @@
 	hose = pci_find_hose_for_OF_device(p2pbridge);
 	if (!hose) {
 		DBG("Can't find hose for PCI<->PCI bridge\n");
-		return;
+		goto done;
 	}
 	if (early_read_config_word(hose, bus, devfn,
 				   PCI_BRIDGE_CONTROL, &val) < 0) {
 		printk(KERN_ERR "init_p2pbridge: couldn't read bridge"
 		       " control\n");
-		return;
+		goto done;
 	}
 	val &= ~PCI_BRIDGE_CTL_MASTER_ABORT;
 	early_write_config_word(hose, bus, devfn, PCI_BRIDGE_CONTROL, val);
+done:
+	of_node_put(p2pbridge);
 }
 
 static void __init init_second_ohare(void)
@@ -691,17 +694,17 @@
 		const u32 *prop;
 		u8 bus, devfn;
 
-		prop = get_property(nec, "vendor-id", NULL);
+		prop = of_get_property(nec, "vendor-id", NULL);
 		if (prop == NULL)
 			continue;
 		if (0x1033 != *prop)
 			continue;
-		prop = get_property(nec, "device-id", NULL);
+		prop = of_get_property(nec, "device-id", NULL);
 		if (prop == NULL)
 			continue;
 		if (0x0035 != *prop)
 			continue;
-		prop = get_property(nec, "reg", NULL);
+		prop = of_get_property(nec, "reg", NULL);
 		if (prop == NULL)
 			continue;
 		devfn = (prop[0] >> 8) & 0xff;
@@ -909,7 +912,7 @@
 	has_address = (of_address_to_resource(dev, 0, &rsrc) == 0);
 
 	/* Get bus range if any */
-	bus_range = get_property(dev, "bus-range", &len);
+	bus_range = of_get_property(dev, "bus-range", &len);
 	if (bus_range == NULL || len < 2 * sizeof(int)) {
 		printk(KERN_WARNING "Can't get bus-range for %s, assume"
 		       " bus 0\n", dev->full_name);
@@ -1199,8 +1202,7 @@
 	}
 #endif /* CONFIG_BLK_DEV_IDE */
 
-	nd = find_devices("firewire");
-	while (nd) {
+	for_each_node_by_name(nd, "firewire") {
 		if (nd->parent && (device_is_compatible(nd, "pci106b,18") ||
 				   device_is_compatible(nd, "pci106b,30") ||
 				   device_is_compatible(nd, "pci11c1,5811"))
@@ -1208,15 +1210,14 @@
 			pmac_call_feature(PMAC_FTR_1394_ENABLE, nd, 0, 0);
 			pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, nd, 0, 0);
 		}
-		nd = nd->next;
 	}
-	nd = find_devices("ethernet");
-	while (nd) {
+	of_node_put(nd);
+	for_each_node_by_name(nd, "ethernet") {
 		if (nd->parent && device_is_compatible(nd, "gmac")
 		    && device_is_compatible(nd->parent, "uni-north"))
 			pmac_call_feature(PMAC_FTR_GMAC_ENABLE, nd, 0, 0);
-		nd = nd->next;
 	}
+	of_node_put(nd);
 }
 
 #ifdef CONFIG_PPC32
diff --git a/arch/powerpc/platforms/powermac/pfunc_base.c b/arch/powerpc/platforms/powermac/pfunc_base.c
index 5c6c15c..45d54b9 100644
--- a/arch/powerpc/platforms/powermac/pfunc_base.c
+++ b/arch/powerpc/platforms/powermac/pfunc_base.c
@@ -114,7 +114,7 @@
 	 * we just create them all
 	 */
 	for (gp = NULL; (gp = of_get_next_child(gparent, gp)) != NULL;) {
-		const u32 *reg = get_property(gp, "reg", NULL);
+		const u32 *reg = of_get_property(gp, "reg", NULL);
 		unsigned long offset;
 		if (reg == NULL)
 			continue;
diff --git a/arch/powerpc/platforms/powermac/pfunc_core.c b/arch/powerpc/platforms/powermac/pfunc_core.c
index 7651f278..85434231 100644
--- a/arch/powerpc/platforms/powermac/pfunc_core.c
+++ b/arch/powerpc/platforms/powermac/pfunc_core.c
@@ -692,8 +692,7 @@
 		name = pp->name + plen;
 		if (strlen(name) && pp->length >= 12)
 			count += pmf_add_function_prop(dev, driverdata, name,
-						       (u32 *)pp->value,
-						       pp->length);
+						       pp->value, pp->length);
 	}
 	return count;
 }
@@ -821,7 +820,7 @@
 	 * one, then we fallback to a direct call attempt
 	 */
 	snprintf(fname, 63, "platform-%s", name);
-	prop = get_property(target, fname, NULL);
+	prop = of_get_property(target, fname, NULL);
 	if (prop == NULL)
 		goto find_it;
 	ph = *prop;
diff --git a/arch/powerpc/platforms/powermac/pic.c b/arch/powerpc/platforms/powermac/pic.c
index 5e5c0e4..ae5097a 100644
--- a/arch/powerpc/platforms/powermac/pic.c
+++ b/arch/powerpc/platforms/powermac/pic.c
@@ -482,14 +482,14 @@
 	pmac_call_feature(PMAC_FTR_ENABLE_MPIC, np, 0, 0);
 
 	flags |= MPIC_WANTS_RESET;
-	if (get_property(np, "big-endian", NULL))
+	if (of_get_property(np, "big-endian", NULL))
 		flags |= MPIC_BIG_ENDIAN;
 
 	/* Primary Big Endian means HT interrupts. This is quite dodgy
 	 * but works until I find a better way
 	 */
 	if (master && (flags & MPIC_BIG_ENDIAN))
-		flags |= MPIC_BROKEN_U3;
+		flags |= MPIC_U3_HT_IRQS;
 
 	mpic = mpic_alloc(np, r.start, flags, 0, 0, name);
 	if (mpic == NULL)
@@ -510,7 +510,7 @@
 	for (np = NULL; (np = of_find_node_by_type(np, "open-pic"))
 		     != NULL;) {
 		if (master == NULL &&
-		    get_property(np, "interrupts", NULL) == NULL)
+		    of_get_property(np, "interrupts", NULL) == NULL)
 			master = of_node_get(np);
 		else if (slave == NULL)
 			slave = of_node_get(np);
@@ -575,7 +575,7 @@
 #ifdef CONFIG_PPC32
 	if (!pmac_newworld)
 		flags |= OF_IMAP_OLDWORLD_MAC;
-	if (get_property(of_chosen, "linux,bootx", NULL) != NULL)
+	if (of_get_property(of_chosen, "linux,bootx", NULL) != NULL)
 		flags |= OF_IMAP_NO_PHANDLE;
 #endif /* CONFIG_PPC_32 */
 
diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c
index 651fa42..b820cab 100644
--- a/arch/powerpc/platforms/powermac/setup.c
+++ b/arch/powerpc/platforms/powermac/setup.c
@@ -42,7 +42,6 @@
 #include <linux/initrd.h>
 #include <linux/vt_kern.h>
 #include <linux/console.h>
-#include <linux/ide.h>
 #include <linux/pci.h>
 #include <linux/adb.h>
 #include <linux/cuda.h>
@@ -135,12 +134,12 @@
 	seq_printf(m, "machine\t\t: ");
 	np = of_find_node_by_path("/");
 	if (np != NULL) {
-		pp = get_property(np, "model", NULL);
+		pp = of_get_property(np, "model", NULL);
 		if (pp != NULL)
 			seq_printf(m, "%s\n", pp);
 		else
 			seq_printf(m, "PowerMac\n");
-		pp = get_property(np, "compatible", &plen);
+		pp = of_get_property(np, "compatible", &plen);
 		if (pp != NULL) {
 			seq_printf(m, "motherboard\t:");
 			while (plen > 0) {
@@ -164,11 +163,13 @@
 	if (np == NULL)
 		np = of_find_node_by_type(NULL, "cache");
 	if (np != NULL) {
-		const unsigned int *ic = get_property(np, "i-cache-size", NULL);
-		const unsigned int *dc = get_property(np, "d-cache-size", NULL);
+		const unsigned int *ic =
+			of_get_property(np, "i-cache-size", NULL);
+		const unsigned int *dc =
+			of_get_property(np, "d-cache-size", NULL);
 		seq_printf(m, "L2 cache\t:");
 		has_l2cache = 1;
-		if (get_property(np, "cache-unified", NULL) != 0 && dc) {
+		if (of_get_property(np, "cache-unified", NULL) != 0 && dc) {
 			seq_printf(m, " %dK unified", *dc / 1024);
 		} else {
 			if (ic)
@@ -177,7 +178,7 @@
 				seq_printf(m, "%s %dK data",
 					   (ic? " +": ""), *dc / 1024);
 		}
-		pp = get_property(np, "ram-type", NULL);
+		pp = of_get_property(np, "ram-type", NULL);
 		if (pp)
 			seq_printf(m, " %s", pp);
 		seq_printf(m, "\n");
@@ -192,8 +193,11 @@
 #ifndef CONFIG_ADB_CUDA
 int find_via_cuda(void)
 {
-	if (!find_devices("via-cuda"))
+	struct device_node *dn = of_find_node_by_name(NULL, "via-cuda");
+
+	if (!dn)
 		return 0;
+	of_node_put(dn);
 	printk("WARNING ! Your machine is CUDA-based but your kernel\n");
 	printk("          wasn't compiled with CONFIG_ADB_CUDA option !\n");
 	return 0;
@@ -203,8 +207,11 @@
 #ifndef CONFIG_ADB_PMU
 int find_via_pmu(void)
 {
-	if (!find_devices("via-pmu"))
+	struct device_node *dn = of_find_node_by_name(NULL, "via-pmu");
+
+	if (!dn)
 		return 0;
+	of_node_put(dn);
 	printk("WARNING ! Your machine is PMU-based but your kernel\n");
 	printk("          wasn't compiled with CONFIG_ADB_PMU option !\n");
 	return 0;
@@ -224,6 +231,8 @@
 
 static void __init ohare_init(void)
 {
+	struct device_node *dn;
+
 	/* this area has the CPU identification register
 	   and some registers used by smp boards */
 	sysctrl_regs = (volatile u32 *) ioremap(0xf8000000, 0x1000);
@@ -233,7 +242,9 @@
 	 * We assume that we have a PSX memory controller iff
 	 * we have an ohare I/O controller.
 	 */
-	if (find_devices("ohare") != NULL) {
+	dn = of_find_node_by_name(NULL, "ohare");
+	if (dn) {
+		of_node_put(dn);
 		if (((sysctrl_regs[2] >> 24) & 0xf) >= 3) {
 			if (sysctrl_regs[4] & 0x10)
 				sysctrl_regs[4] |= 0x04000020;
@@ -249,18 +260,19 @@
 {
 	/* Checks "l2cr-value" property in the registry */
 	if (cpu_has_feature(CPU_FTR_L2CR)) {
-		struct device_node *np = find_devices("cpus");
+		struct device_node *np = of_find_node_by_name(NULL, "cpus");
 		if (np == 0)
-			np = find_type_devices("cpu");
+			np = of_find_node_by_type(NULL, "cpu");
 		if (np != 0) {
 			const unsigned int *l2cr =
-				get_property(np, "l2cr-value", NULL);
+				of_get_property(np, "l2cr-value", NULL);
 			if (l2cr != 0) {
 				ppc_override_l2cr = 1;
 				ppc_override_l2cr_value = *l2cr;
 				_set_L2CR(0);
 				_set_L2CR(ppc_override_l2cr_value);
 			}
+			of_node_put(np);
 		}
 	}
 
@@ -286,7 +298,7 @@
 	loops_per_jiffy = 50000000 / HZ;
 	cpu = of_find_node_by_type(NULL, "cpu");
 	if (cpu != NULL) {
-		fp = get_property(cpu, "clock-frequency", NULL);
+		fp = of_get_property(cpu, "clock-frequency", NULL);
 		if (fp != NULL) {
 			if (pvr >= 0x30 && pvr < 0x80)
 				/* PPC970 etc. */
@@ -303,7 +315,7 @@
 
 	/* See if newworld or oldworld */
 	for (ic = NULL; (ic = of_find_all_nodes(ic)) != NULL; )
-		if (get_property(ic, "interrupt-controller", NULL))
+		if (of_get_property(ic, "interrupt-controller", NULL))
 			break;
 	if (ic) {
 		pmac_newworld = 1;
@@ -341,8 +353,15 @@
 
 #ifdef CONFIG_SMP
 	/* Check for Core99 */
-	if (find_devices("uni-n") || find_devices("u3") || find_devices("u4"))
+	ic = of_find_node_by_name(NULL, "uni-n");
+	if (!ic)
+		ic = of_find_node_by_name(NULL, "u3");
+	if (!ic)
+		ic = of_find_node_by_name(NULL, "u4");
+	if (ic) {
+		of_node_put(ic);
 		smp_ops = &core99_smp_ops;
+	}
 #ifdef CONFIG_PPC32
 	else
 		smp_ops = &psurge_smp_ops;
@@ -616,15 +635,6 @@
 #endif
 }
 
-/*
- * pmac has no legacy IO, anything calling this function has to
- * fail or bad things will happen
- */
-static int pmac_check_legacy_ioport(unsigned int baseport)
-{
-	return -ENODEV;
-}
-
 static int __init pmac_declare_of_platform_devices(void)
 {
 	struct device_node *np;
@@ -736,7 +746,6 @@
 	.get_rtc_time		= pmac_get_rtc_time,
 	.calibrate_decr		= pmac_calibrate_decr,
 	.feature_call		= pmac_do_feature_call,
-	.check_legacy_ioport	= pmac_check_legacy_ioport,
 	.progress		= udbg_progress,
 #ifdef CONFIG_PPC64
 	.pci_probe_mode		= pmac_pci_probe_mode,
diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c
index d73fb73..6f32c4e 100644
--- a/arch/powerpc/platforms/powermac/smp.c
+++ b/arch/powerpc/platforms/powermac/smp.c
@@ -264,6 +264,7 @@
 static int __init smp_psurge_probe(void)
 {
 	int i, ncpus;
+	struct device_node *dn;
 
 	/* We don't do SMP on the PPC601 -- paulus */
 	if (PVR_VER(mfspr(SPRN_PVR)) == 1)
@@ -279,8 +280,10 @@
 	 * in the hammerhead memory controller in the case of the
 	 * dual-cpu powersurge board.  -- paulus.
 	 */
-	if (find_devices("hammerhead") == NULL)
+	dn = of_find_node_by_name(NULL, "hammerhead");
+	if (dn == NULL)
 		return 1;
+	of_node_put(dn);
 
 	hhead_base = ioremap(HAMMERHEAD_BASE, 0x800);
 	quad_base = ioremap(PSURGE_QUAD_REG_ADDR, 1024);
@@ -567,7 +570,7 @@
 		pmac_tb_clock_chip_host = pmac_i2c_find_bus(cc);
 		if (pmac_tb_clock_chip_host == NULL)
 			continue;
-		reg = get_property(cc, "reg", NULL);
+		reg = of_get_property(cc, "reg", NULL);
 		if (reg == NULL)
 			continue;
 		switch (*reg) {
@@ -695,7 +698,7 @@
 		struct device_node *cpus =
 			of_find_node_by_path("/cpus");
 		if (cpus &&
-		    get_property(cpus, "platform-cpu-timebase", NULL)) {
+		    of_get_property(cpus, "platform-cpu-timebase", NULL)) {
 			pmac_tb_freeze = smp_core99_pfunc_tb_freeze;
 			printk(KERN_INFO "Processor timebase sync using"
 			       " platform function\n");
@@ -712,7 +715,7 @@
 		core99_tb_gpio = KL_GPIO_TB_ENABLE;	/* default value */
 		cpu = of_find_node_by_type(NULL, "cpu");
 		if (cpu != NULL) {
-			tbprop = get_property(cpu, "timebase-enable", NULL);
+			tbprop = of_get_property(cpu, "timebase-enable", NULL);
 			if (tbprop)
 				core99_tb_gpio = *tbprop;
 			of_node_put(cpu);
diff --git a/arch/powerpc/platforms/powermac/time.c b/arch/powerpc/platforms/powermac/time.c
index a417390..bf9da56 100644
--- a/arch/powerpc/platforms/powermac/time.c
+++ b/arch/powerpc/platforms/powermac/time.c
@@ -297,49 +297,11 @@
 }
 #endif
 
-#ifdef CONFIG_PM
-/*
- * Reset the time after a sleep.
- */
-static int
-time_sleep_notify(struct pmu_sleep_notifier *self, int when)
-{
-	static unsigned long time_diff;
-	unsigned long flags;
-	unsigned long seq;
-	struct timespec tv;
-
-	switch (when) {
-	case PBOOK_SLEEP_NOW:
-		do {
-			seq = read_seqbegin_irqsave(&xtime_lock, flags);
-			time_diff = xtime.tv_sec - pmac_get_boot_time();
-		} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
-		break;
-	case PBOOK_WAKE:
-		tv.tv_sec = pmac_get_boot_time() + time_diff;
-		tv.tv_nsec = 0;
-		do_settimeofday(&tv);
-		break;
-	}
-	return PBOOK_SLEEP_OK;
-}
-
-static struct pmu_sleep_notifier time_sleep_notifier = {
-	time_sleep_notify, SLEEP_LEVEL_MISC,
-};
-#endif /* CONFIG_PM */
-
 /*
  * Query the OF and get the decr frequency.
  */
 void __init pmac_calibrate_decr(void)
 {
-#if defined(CONFIG_PM) && defined(CONFIG_ADB_PMU)
-	/* XXX why here? */
-	pmu_register_sleep_notifier(&time_sleep_notifier);
-#endif
-
 	generic_calibrate_decr();
 
 #ifdef CONFIG_PPC32
diff --git a/arch/powerpc/platforms/powermac/udbg_scc.c b/arch/powerpc/platforms/powermac/udbg_scc.c
index 379db05..47de4d3 100644
--- a/arch/powerpc/platforms/powermac/udbg_scc.c
+++ b/arch/powerpc/platforms/powermac/udbg_scc.c
@@ -81,7 +81,7 @@
 	macio = of_get_parent(escc);
 	if (macio == NULL)
 		goto bail;
-	path = get_property(of_chosen, "linux,stdout-path", NULL);
+	path = of_get_property(of_chosen, "linux,stdout-path", NULL);
 	if (path != NULL)
 		stdout = of_find_node_by_path(path);
 	for (ch = NULL; (ch = of_get_next_child(escc, ch)) != NULL;) {
@@ -96,13 +96,13 @@
 	ch = ch_def ? ch_def : ch_a;
 
 	/* Get address within mac-io ASIC */
-	reg = get_property(escc, "reg", NULL);
+	reg = of_get_property(escc, "reg", NULL);
 	if (reg == NULL)
 		goto bail;
 	addr = reg[0];
 
 	/* Get address of mac-io PCI itself */
-	reg = get_property(macio, "assigned-addresses", NULL);
+	reg = of_get_property(macio, "assigned-addresses", NULL);
 	if (reg == NULL)
 		goto bail;
 	addr += reg[2];
diff --git a/arch/powerpc/platforms/prep/Kconfig b/arch/powerpc/platforms/prep/Kconfig
index 673ac47..29d4112 100644
--- a/arch/powerpc/platforms/prep/Kconfig
+++ b/arch/powerpc/platforms/prep/Kconfig
@@ -1,3 +1,12 @@
+config PPC_PREP
+	bool "PowerPC Reference Platform (PReP) based machines"
+	depends on PPC_MULTIPLATFORM && PPC32 && BROKEN
+	select MPIC
+	select PPC_I8259
+	select PPC_INDIRECT_PCI
+	select PPC_UDBG_16550
+	select PPC_NATIVE
+	default n
 
 config PREP_RESIDUAL
 	bool "Support for PReP Residual Data"
diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig
index 1a481a6..40f0008 100644
--- a/arch/powerpc/platforms/ps3/Kconfig
+++ b/arch/powerpc/platforms/ps3/Kconfig
@@ -1,3 +1,19 @@
+config PPC_PS3
+	bool "Sony PS3 (incomplete)"
+	depends on PPC_MULTIPLATFORM && PPC64
+	select PPC_CELL
+	select USB_ARCH_HAS_OHCI
+	select USB_OHCI_LITTLE_ENDIAN
+	select USB_OHCI_BIG_ENDIAN_MMIO
+	select USB_ARCH_HAS_EHCI
+	select USB_EHCI_BIG_ENDIAN_MMIO
+	help
+	  This option enables support for the Sony PS3 game console
+	  and other platforms using the PS3 hypervisor.
+	  Support for this platform is not yet complete, so
+	  enabling this will not result in a bootable kernel on a
+	  PS3 system.
+
 menu "PS3 Platform Options"
 	depends on PPC_PS3
 
diff --git a/arch/powerpc/platforms/ps3/htab.c b/arch/powerpc/platforms/ps3/htab.c
index e12e59f..ea60c45 100644
--- a/arch/powerpc/platforms/ps3/htab.c
+++ b/arch/powerpc/platforms/ps3/htab.c
@@ -39,7 +39,7 @@
 static unsigned char *bolttab;
 static unsigned char *inusetab;
 
-static spinlock_t ps3_bolttab_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(ps3_bolttab_lock);
 
 #define debug_dump_hpte(_a, _b, _c, _d, _e, _f, _g) \
 	_debug_dump_hpte(_a, _b, _c, _d, _e, _f, _g, __func__, __LINE__)
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index a57032c..16e4e40 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -1,3 +1,13 @@
+config PPC_PSERIES
+	depends on PPC_MULTIPLATFORM && PPC64
+	bool "IBM pSeries & new (POWER5-based) iSeries"
+	select MPIC
+	select PPC_I8259
+	select PPC_RTAS
+	select RTAS_ERROR_LOGGING
+	select PPC_UDBG_16550
+	select PPC_NATIVE
+	default y
 
 config PPC_SPLPAR
 	depends on PPC_PSERIES
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
index 2dfd050..90235d5 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -2,14 +2,15 @@
 EXTRA_CFLAGS		+= -mno-minimal-toc
 endif
 
-obj-y			:= pci.o lpar.o hvCall.o nvram.o reconfig.o \
-			   setup.o iommu.o ras.o rtasd.o pci_dlpar.o \
+obj-y			:= lpar.o hvCall.o nvram.o reconfig.o \
+			   setup.o iommu.o ras.o rtasd.o \
 			   firmware.o power.o
 obj-$(CONFIG_SMP)	+= smp.o
 obj-$(CONFIG_XICS)	+= xics.o
 obj-$(CONFIG_SCANLOG)	+= scanlog.o
 obj-$(CONFIG_EEH)	+= eeh.o eeh_cache.o eeh_driver.o eeh_event.o
 obj-$(CONFIG_KEXEC)	+= kexec.o
+obj-$(CONFIG_PCI)	+= pci.o pci_dlpar.o
 
 obj-$(CONFIG_HOTPLUG_CPU)	+= hotplug-cpu.o
 
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
index 6cedbc0..48fbd44 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -74,7 +74,10 @@
  * is broken and panic.  This sets the threshold for how many read
  * attempts we allow before panicking.
  */
-#define EEH_MAX_FAILS	100000
+#define EEH_MAX_FAILS	2100000
+
+/* Time to wait for a PCI slot to retport status, in milliseconds */
+#define PCI_BUS_RESET_WAIT_MSEC (60*1000)
 
 /* RTAS tokens */
 static int ibm_set_eeh_option;
@@ -83,6 +86,7 @@
 static int ibm_read_slot_reset_state2;
 static int ibm_slot_error_detail;
 static int ibm_get_config_addr_info;
+static int ibm_get_config_addr_info2;
 static int ibm_configure_bridge;
 
 int eeh_subsystem_enabled;
@@ -168,6 +172,55 @@
 }
 
 /**
+ * eeh_wait_for_slot_status - returns error status of slot
+ * @pdn pci device node
+ * @max_wait_msecs maximum number to millisecs to wait
+ *
+ * Return negative value if a permanent error, else return
+ * Partition Endpoint (PE) status value.
+ *
+ * If @max_wait_msecs is positive, then this routine will
+ * sleep until a valid status can be obtained, or until
+ * the max allowed wait time is exceeded, in which case
+ * a -2 is returned.
+ */
+int
+eeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs)
+{
+	int rc;
+	int rets[3];
+	int mwait;
+
+	while (1) {
+		rc = read_slot_reset_state(pdn, rets);
+		if (rc) return rc;
+		if (rets[1] == 0) return -1;  /* EEH is not supported */
+
+		if (rets[0] != 5) return rets[0]; /* return actual status */
+
+		if (rets[2] == 0) return -1; /* permanently unavailable */
+
+		if (max_wait_msecs <= 0) return -1;
+
+		mwait = rets[2];
+		if (mwait <= 0) {
+			printk (KERN_WARNING
+			        "EEH: Firmware returned bad wait value=%d\n", mwait);
+			mwait = 1000;
+		} else if (mwait > 300*1000) {
+			printk (KERN_WARNING
+			        "EEH: Firmware is taking too long, time=%d\n", mwait);
+			mwait = 300*1000;
+		}
+		max_wait_msecs -= mwait;
+		msleep (mwait);
+	}
+
+	printk(KERN_WARNING "EEH: Timed out waiting for slot status\n");
+	return -2;
+}
+
+/**
  * eeh_token_to_phys - convert EEH address token to phys address
  * @token i/o token, should be address in the form 0xA....
  */
@@ -229,7 +282,7 @@
 	dn = find_device_pe (dn);
 
 	/* Back up one, since config addrs might be shared */
-	if (PCI_DN(dn) && PCI_DN(dn)->eeh_pe_config_addr)
+	if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent))
 		dn = dn->parent;
 
 	PCI_DN(dn)->eeh_mode |= mode_flag;
@@ -263,7 +316,7 @@
 	dn = find_device_pe (dn);
 	
 	/* Back up one, since config addrs might be shared */
-	if (PCI_DN(dn) && PCI_DN(dn)->eeh_pe_config_addr)
+	if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent))
 		dn = dn->parent;
 
 	PCI_DN(dn)->eeh_mode &= ~mode_flag;
@@ -293,7 +346,6 @@
 	int rets[3];
 	unsigned long flags;
 	struct pci_dn *pdn;
-	enum pci_channel_state state;
 	int rc = 0;
 
 	total_mmio_ffs++;
@@ -367,6 +419,14 @@
 		goto dn_unlock;
 	}
 
+	/* Note that config-io to empty slots may fail;
+	 * they are empty when they don't have children. */
+	if ((rets[0] == 5) && (dn->child == NULL)) {
+		false_positives++;
+		rc = 0;
+		goto dn_unlock;
+	}
+
 	/* If EEH is not supported on this device, punt. */
 	if (rets[1] != 1) {
 		printk(KERN_WARNING "EEH: event on unsupported device, rc=%d dn=%s\n",
@@ -377,15 +437,7 @@
 	}
 
 	/* If not the kind of error we know about, punt. */
-	if (rets[0] != 2 && rets[0] != 4 && rets[0] != 5) {
-		false_positives++;
-		rc = 0;
-		goto dn_unlock;
-	}
-
-	/* Note that config-io to empty slots may fail;
-	 * we recognize empty because they don't have children. */
-	if ((rets[0] == 5) && (dn->child == NULL)) {
+	if (rets[0] != 1 && rets[0] != 2 && rets[0] != 4 && rets[0] != 5) {
 		false_positives++;
 		rc = 0;
 		goto dn_unlock;
@@ -399,17 +451,12 @@
 	eeh_mark_slot (dn, EEH_MODE_ISOLATED);
 	spin_unlock_irqrestore(&confirm_error_lock, flags);
 
-	state = pci_channel_io_normal;
-	if ((rets[0] == 2) || (rets[0] == 4))
-		state = pci_channel_io_frozen;
-	if (rets[0] == 5)
-		state = pci_channel_io_perm_failure;
-	eeh_send_failure_event (dn, dev, state, rets[2]);
+	eeh_send_failure_event (dn, dev);
 
 	/* Most EEH events are due to device driver bugs.  Having
 	 * a stack trace will help the device-driver authors figure
 	 * out what happened.  So print that out. */
-	if (rets[0] != 5) dump_stack();
+	dump_stack();
 	return 1;
 
 dn_unlock:
@@ -458,38 +505,6 @@
 /* The code below deals with error recovery */
 
 /**
- * eeh_slot_availability - returns error status of slot
- * @pdn pci device node
- *
- * Return negative value if a permanent error, else return
- * a number of milliseconds to wait until the PCI slot is
- * ready to be used.
- */
-static int
-eeh_slot_availability(struct pci_dn *pdn)
-{
-	int rc;
-	int rets[3];
-
-	rc = read_slot_reset_state(pdn, rets);
-
-	if (rc) return rc;
-
-	if (rets[1] == 0) return -1;  /* EEH is not supported */
-	if (rets[0] == 0) return 0;   /* Oll Korrect */
-	if (rets[0] == 5) {
-		if (rets[2] == 0) return -1; /* permanently unavailable */
-		return rets[2]; /* number of millisecs to wait */
-	}
-	if (rets[0] == 1)
-		return 250;
-
-	printk (KERN_ERR "EEH: Slot unavailable: rc=%d, rets=%d %d %d\n",
-		rc, rets[0], rets[1], rets[2]);
-	return -2;
-}
-
-/**
  * rtas_pci_enable - enable MMIO or DMA transfers for this slot
  * @pdn pci device node
  */
@@ -512,9 +527,13 @@
 		            function);
 
 	if (rc)
-		printk(KERN_WARNING "EEH: Cannot enable function %d, err=%d dn=%s\n",
+		printk(KERN_WARNING "EEH: Unexpected state change %d, err=%d dn=%s\n",
 		        function, rc, pdn->node->full_name);
 
+	rc = eeh_wait_for_slot_status (pdn, PCI_BUS_RESET_WAIT_MSEC);
+	if ((rc == 4) && (function == EEH_THAW_MMIO))
+		return 0;
+
 	return rc;
 }
 
@@ -595,36 +614,24 @@
 {
 	int i, rc;
 
-	__rtas_set_slot_reset(pdn);
+	/* Take three shots at resetting the bus */
+	for (i=0; i<3; i++) {
+		__rtas_set_slot_reset(pdn);
 
-	/* Now double check with the firmware to make sure the device is
-	 * ready to be used; if not, wait for recovery. */
-	for (i=0; i<10; i++) {
-		rc = eeh_slot_availability (pdn);
+		rc = eeh_wait_for_slot_status(pdn, PCI_BUS_RESET_WAIT_MSEC);
 		if (rc == 0)
 			return 0;
 
-		if (rc == -2) {
-			printk (KERN_ERR "EEH: failed (%d) to reset slot %s\n",
-			        i, pdn->node->full_name);
-			__rtas_set_slot_reset(pdn);
-			continue;
-		}
-
 		if (rc < 0) {
 			printk (KERN_ERR "EEH: unrecoverable slot failure %s\n",
 			        pdn->node->full_name);
 			return -1;
 		}
-
-		msleep (rc+100);
+		printk (KERN_ERR "EEH: bus reset %d failed on slot %s\n",
+		        i+1, pdn->node->full_name);
 	}
 
-	rc = eeh_slot_availability (pdn);
-	if (rc)
-		printk (KERN_ERR "EEH: timeout resetting slot %s\n", pdn->node->full_name);
-
-	return rc;
+	return -1;
 }
 
 /* ------------------------------------------------------- */
@@ -744,16 +751,48 @@
 	unsigned int buid_lo;
 };
 
+static int get_pe_addr (int config_addr,
+                        struct eeh_early_enable_info *info)
+{
+	unsigned int rets[3];
+	int ret;
+
+	/* Use latest config-addr token on power6 */
+	if (ibm_get_config_addr_info2 != RTAS_UNKNOWN_SERVICE) {
+		/* Make sure we have a PE in hand */
+		ret = rtas_call (ibm_get_config_addr_info2, 4, 2, rets,
+			config_addr, info->buid_hi, info->buid_lo, 1);
+		if (ret || (rets[0]==0))
+			return 0;
+
+		ret = rtas_call (ibm_get_config_addr_info2, 4, 2, rets,
+			config_addr, info->buid_hi, info->buid_lo, 0);
+		if (ret)
+			return 0;
+		return rets[0];
+	}
+
+	/* Use older config-addr token on power5 */
+	if (ibm_get_config_addr_info != RTAS_UNKNOWN_SERVICE) {
+		ret = rtas_call (ibm_get_config_addr_info, 4, 2, rets,
+			config_addr, info->buid_hi, info->buid_lo, 0);
+		if (ret)
+			return 0;
+		return rets[0];
+	}
+	return 0;
+}
+
 /* Enable eeh for the given device node. */
 static void *early_enable_eeh(struct device_node *dn, void *data)
 {
 	unsigned int rets[3];
 	struct eeh_early_enable_info *info = data;
 	int ret;
-	const char *status = get_property(dn, "status", NULL);
-	const u32 *class_code = get_property(dn, "class-code", NULL);
-	const u32 *vendor_id = get_property(dn, "vendor-id", NULL);
-	const u32 *device_id = get_property(dn, "device-id", NULL);
+	const char *status = of_get_property(dn, "status", NULL);
+	const u32 *class_code = of_get_property(dn, "class-code", NULL);
+	const u32 *vendor_id = of_get_property(dn, "vendor-id", NULL);
+	const u32 *device_id = of_get_property(dn, "device-id", NULL);
 	const u32 *regs;
 	int enable;
 	struct pci_dn *pdn = PCI_DN(dn);
@@ -796,7 +835,7 @@
 
 	/* Ok... see if this device supports EEH.  Some do, some don't,
 	 * and the only way to find out is to check each and every one. */
-	regs = get_property(dn, "reg", NULL);
+	regs = of_get_property(dn, "reg", NULL);
 	if (regs) {
 		/* First register entry is addr (00BBSS00)  */
 		/* Try to enable eeh */
@@ -810,15 +849,7 @@
 
 			/* If the newer, better, ibm,get-config-addr-info is supported, 
 			 * then use that instead. */
-			pdn->eeh_pe_config_addr = 0;
-			if (ibm_get_config_addr_info != RTAS_UNKNOWN_SERVICE) {
-				ret = rtas_call (ibm_get_config_addr_info, 4, 2, rets, 
-					pdn->eeh_config_addr, 
-					info->buid_hi, info->buid_lo,
-					0);
-				if (ret == 0)
-					pdn->eeh_pe_config_addr = rets[0];
-			}
+			pdn->eeh_pe_config_addr = get_pe_addr(pdn->eeh_config_addr, info);
 
 			/* Some older systems (Power4) allow the
 			 * ibm,set-eeh-option call to succeed even on nodes
@@ -889,6 +920,7 @@
 	ibm_read_slot_reset_state = rtas_token("ibm,read-slot-reset-state");
 	ibm_slot_error_detail = rtas_token("ibm,slot-error-detail");
 	ibm_get_config_addr_info = rtas_token("ibm,get-config-addr-info");
+	ibm_get_config_addr_info2 = rtas_token("ibm,get-config-addr-info2");
 	ibm_configure_bridge = rtas_token ("ibm,configure-bridge");
 
 	if (ibm_set_eeh_option == RTAS_UNKNOWN_SERVICE)
diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c
index a4c0bf8..3170e00 100644
--- a/arch/powerpc/platforms/pseries/eeh_driver.c
+++ b/arch/powerpc/platforms/pseries/eeh_driver.c
@@ -158,7 +158,8 @@
 		return;
 
 	rc = driver->err_handler->slot_reset(dev);
-	if (*res == PCI_ERS_RESULT_NONE) *res = rc;
+	if ((*res == PCI_ERS_RESULT_NONE) ||
+	    (*res == PCI_ERS_RESULT_RECOVERED)) *res = rc;
 	if (*res == PCI_ERS_RESULT_DISCONNECT &&
 	     rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
 }
@@ -248,6 +249,7 @@
 
 static int eeh_reset_device (struct pci_dn *pe_dn, struct pci_bus *bus)
 {
+	struct device_node *dn;
 	int cnt, rc;
 
 	/* pcibios will clear the counter; save the value */
@@ -263,23 +265,20 @@
 	if (rc)
 		return rc;
 
- 	/* New-style config addrs might be shared across multiple devices,
- 	 * Walk over all functions on this device */
- 	if (pe_dn->eeh_pe_config_addr) {
- 		struct device_node *pe = pe_dn->node;
- 		pe = pe->parent->child;
- 		while (pe) {
- 			struct pci_dn *ppe = PCI_DN(pe);
- 			if (pe_dn->eeh_pe_config_addr == ppe->eeh_pe_config_addr) {
- 				rtas_configure_bridge(ppe);
- 				eeh_restore_bars(ppe);
- 			}
- 			pe = pe->sibling;
+	/* Walk over all functions on this device.  */
+	dn = pe_dn->node;
+	if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent))
+		dn = dn->parent->child;
+
+	while (dn) {
+		struct pci_dn *ppe = PCI_DN(dn);
+		/* On Power4, always true because eeh_pe_config_addr=0 */
+		if (pe_dn->eeh_pe_config_addr == ppe->eeh_pe_config_addr) {
+			rtas_configure_bridge(ppe);
+			eeh_restore_bars(ppe);
  		}
- 	} else {
- 		rtas_configure_bridge(pe_dn);
- 		eeh_restore_bars(pe_dn);
- 	}
+		dn = dn->sibling;
+	}
 
 	/* Give the system 5 seconds to finish running the user-space
 	 * hotplug shutdown scripts, e.g. ifdown for ethernet.  Yes, 
@@ -299,7 +298,7 @@
 /* The longest amount of time to wait for a pci device
  * to come back on line, in seconds.
  */
-#define MAX_WAIT_FOR_RECOVERY 15
+#define MAX_WAIT_FOR_RECOVERY 150
 
 struct pci_dn * handle_eeh_events (struct eeh_event *event)
 {
@@ -315,14 +314,14 @@
 
 	if (!frozen_dn) {
 
-		location = get_property(event->dn, "ibm,loc-code", NULL);
+		location = of_get_property(event->dn, "ibm,loc-code", NULL);
 		location = location ? location : "unknown";
 		printk(KERN_ERR "EEH: Error: Cannot find partition endpoint "
 		                "for location=%s pci addr=%s\n",
 		        location, pci_name(event->dev));
 		return NULL;
 	}
-	location = get_property(frozen_dn, "ibm,loc-code", NULL);
+	location = of_get_property(frozen_dn, "ibm,loc-code", NULL);
 	location = location ? location : "unknown";
 
 	/* There are two different styles for coming up with the PE.
@@ -341,13 +340,6 @@
 		return NULL;
 	}
 
-#if 0
-	/* We may get "permanent failure" messages on empty slots.
-	 * These are false alarms. Empty slots have no child dn. */
-	if ((event->state == pci_channel_io_perm_failure) && (frozen_device == NULL))
-		return;
-#endif
-
 	frozen_pdn = PCI_DN(frozen_dn);
 	frozen_pdn->eeh_freeze_count++;
 
@@ -362,13 +354,12 @@
 	if (frozen_pdn->eeh_freeze_count > EEH_MAX_ALLOWED_FREEZES)
 		goto excess_failures;
 
-	/* If the reset state is a '5' and the time to reset is 0 (infinity)
-	 * or is more then 15 seconds, then mark this as a permanent failure.
-	 */
-	if ((event->state == pci_channel_io_perm_failure) &&
-	    ((event->time_unavail <= 0) ||
-	     (event->time_unavail > MAX_WAIT_FOR_RECOVERY*1000)))
+	/* Get the current PCI slot state. */
+	rc = eeh_wait_for_slot_status (frozen_pdn, MAX_WAIT_FOR_RECOVERY*1000);
+	if (rc < 0) {
+		printk(KERN_WARNING "EEH: Permanent failure\n");
 		goto hard_fail;
+	}
 
 	eeh_slot_error_detail(frozen_pdn, 1 /* Temporary Error */);
 	printk(KERN_WARNING
@@ -390,14 +381,18 @@
 	 */
 	if (result == PCI_ERS_RESULT_NONE) {
 		rc = eeh_reset_device(frozen_pdn, frozen_bus);
-		if (rc)
+		if (rc) {
+			printk(KERN_WARNING "EEH: Unable to reset, rc=%d\n", rc);
 			goto hard_fail;
+		}
 	}
 
 	/* If all devices reported they can proceed, then re-enable MMIO */
 	if (result == PCI_ERS_RESULT_CAN_RECOVER) {
 		rc = rtas_pci_enable(frozen_pdn, EEH_THAW_MMIO);
 
+		if (rc < 0)
+			goto hard_fail;
 		if (rc) {
 			result = PCI_ERS_RESULT_NEED_RESET;
 		} else {
@@ -410,6 +405,8 @@
 	if (result == PCI_ERS_RESULT_CAN_RECOVER) {
 		rc = rtas_pci_enable(frozen_pdn, EEH_THAW_DMA);
 
+		if (rc < 0)
+			goto hard_fail;
 		if (rc)
 			result = PCI_ERS_RESULT_NEED_RESET;
 		else
@@ -417,21 +414,28 @@
 	}
 
 	/* If any device has a hard failure, then shut off everything. */
-	if (result == PCI_ERS_RESULT_DISCONNECT)
+	if (result == PCI_ERS_RESULT_DISCONNECT) {
+		printk(KERN_WARNING "EEH: Device driver gave up\n");
 		goto hard_fail;
+	}
 
 	/* If any device called out for a reset, then reset the slot */
 	if (result == PCI_ERS_RESULT_NEED_RESET) {
 		rc = eeh_reset_device(frozen_pdn, NULL);
-		if (rc)
+		if (rc) {
+			printk(KERN_WARNING "EEH: Cannot reset, rc=%d\n", rc);
 			goto hard_fail;
+		}
 		result = PCI_ERS_RESULT_NONE;
 		pci_walk_bus(frozen_bus, eeh_report_reset, &result);
 	}
 
 	/* All devices should claim they have recovered by now. */
-	if (result != PCI_ERS_RESULT_RECOVERED)
+	if ((result != PCI_ERS_RESULT_RECOVERED) &&
+	    (result != PCI_ERS_RESULT_NONE)) {
+		printk(KERN_WARNING "EEH: Not recovered\n");
 		goto hard_fail;
+	}
 
 	/* Tell all device drivers that they can resume operations */
 	pci_walk_bus(frozen_bus, eeh_report_resume, NULL);
diff --git a/arch/powerpc/platforms/pseries/eeh_event.c b/arch/powerpc/platforms/pseries/eeh_event.c
index 49037ed..ddb80f5 100644
--- a/arch/powerpc/platforms/pseries/eeh_event.c
+++ b/arch/powerpc/platforms/pseries/eeh_event.c
@@ -118,9 +118,7 @@
  * (from a workqueue).
  */
 int eeh_send_failure_event (struct device_node *dn,
-                            struct pci_dev *dev,
-                            enum pci_channel_state state,
-                            int time_unavail)
+                            struct pci_dev *dev)
 {
 	unsigned long flags;
 	struct eeh_event *event;
@@ -128,7 +126,7 @@
 
 	if (!mem_init_done) {
 		printk(KERN_ERR "EEH: event during early boot not handled\n");
-		location = get_property(dn, "ibm,loc-code", NULL);
+		location = of_get_property(dn, "ibm,loc-code", NULL);
 		printk(KERN_ERR "EEH: device node = %s\n", dn->full_name);
 		printk(KERN_ERR "EEH: PCI location = %s\n", location);
 		return 1;
@@ -144,8 +142,6 @@
 
 	event->dn = dn;
 	event->dev = dev;
-	event->state = state;
-	event->time_unavail = time_unavail;
 
 	/* We may or may not be called in an interrupt context */
 	spin_lock_irqsave(&eeh_eventlist_lock, flags);
diff --git a/arch/powerpc/platforms/pseries/firmware.c b/arch/powerpc/platforms/pseries/firmware.c
index 90522e3..29bf83b 100644
--- a/arch/powerpc/platforms/pseries/firmware.c
+++ b/arch/powerpc/platforms/pseries/firmware.c
@@ -80,7 +80,7 @@
 		goto out;
 	}
 
-	hypertas = get_property(dn, "ibm,hypertas-functions", &len);
+	hypertas = of_get_property(dn, "ibm,hypertas-functions", &len);
 	if (hypertas == NULL)
 		goto out;
 
diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c
index f460b9c..9711eb0 100644
--- a/arch/powerpc/platforms/pseries/hotplug-cpu.c
+++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c
@@ -143,7 +143,7 @@
 	int err = -ENOSPC, len, nthreads, i;
 	const u32 *intserv;
 
-	intserv = get_property(np, "ibm,ppc-interrupt-server#s", &len);
+	intserv = of_get_property(np, "ibm,ppc-interrupt-server#s", &len);
 	if (!intserv)
 		return 0;
 
@@ -203,7 +203,7 @@
 	int len, nthreads, i;
 	const u32 *intserv;
 
-	intserv = get_property(np, "ibm,ppc-interrupt-server#s", &len);
+	intserv = of_get_property(np, "ibm,ppc-interrupt-server#s", &len);
 	if (!intserv)
 		return;
 
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index e6653a8..66665c8 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -242,6 +242,7 @@
 	return tce_ret;
 }
 
+#ifdef CONFIG_PCI
 static void iommu_table_setparms(struct pci_controller *phb,
 				 struct device_node *dn,
 				 struct iommu_table *tbl)
@@ -252,8 +253,8 @@
 
 	node = (struct device_node *)phb->arch_data;
 
-	basep = get_property(node, "linux,tce-base", NULL);
-	sizep = get_property(node, "linux,tce-size", NULL);
+	basep = of_get_property(node, "linux,tce-base", NULL);
+	sizep = of_get_property(node, "linux,tce-size", NULL);
 	if (basep == NULL || sizep == NULL) {
 		printk(KERN_ERR "PCI_DMA: iommu_table_setparms: %s has "
 				"missing tce entries !\n", dn->full_name);
@@ -403,7 +404,7 @@
 
 	/* Find nearest ibm,dma-window, walking up the device tree */
 	for (pdn = dn; pdn != NULL; pdn = pdn->parent) {
-		dma_window = get_property(pdn, "ibm,dma-window", NULL);
+		dma_window = of_get_property(pdn, "ibm,dma-window", NULL);
 		if (dma_window != NULL)
 			break;
 	}
@@ -478,29 +479,6 @@
 		       pci_name(dev));
 }
 
-static int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *node)
-{
-	int err = NOTIFY_OK;
-	struct device_node *np = node;
-	struct pci_dn *pci = PCI_DN(np);
-
-	switch (action) {
-	case PSERIES_RECONFIG_REMOVE:
-		if (pci && pci->iommu_table &&
-		    get_property(np, "ibm,dma-window", NULL))
-			iommu_free_table(np);
-		break;
-	default:
-		err = NOTIFY_DONE;
-		break;
-	}
-	return err;
-}
-
-static struct notifier_block iommu_reconfig_nb = {
-	.notifier_call = iommu_reconfig_notifier,
-};
-
 static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev)
 {
 	struct device_node *pdn, *dn;
@@ -521,7 +499,7 @@
 
 	for (pdn = dn; pdn && PCI_DN(pdn) && !PCI_DN(pdn)->iommu_table;
 	     pdn = pdn->parent) {
-		dma_window = get_property(pdn, "ibm,dma-window", NULL);
+		dma_window = of_get_property(pdn, "ibm,dma-window", NULL);
 		if (dma_window)
 			break;
 	}
@@ -554,15 +532,44 @@
 
 	dev->dev.archdata.dma_data = pci->iommu_table;
 }
+#else  /* CONFIG_PCI */
+#define pci_dma_bus_setup_pSeries	NULL
+#define pci_dma_dev_setup_pSeries	NULL
+#define pci_dma_bus_setup_pSeriesLP	NULL
+#define pci_dma_dev_setup_pSeriesLP	NULL
+#endif /* !CONFIG_PCI */
+
+static int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *node)
+{
+	int err = NOTIFY_OK;
+	struct device_node *np = node;
+	struct pci_dn *pci = PCI_DN(np);
+
+	switch (action) {
+	case PSERIES_RECONFIG_REMOVE:
+		if (pci && pci->iommu_table &&
+		    of_get_property(np, "ibm,dma-window", NULL))
+			iommu_free_table(np);
+		break;
+	default:
+		err = NOTIFY_DONE;
+		break;
+	}
+	return err;
+}
+
+static struct notifier_block iommu_reconfig_nb = {
+	.notifier_call = iommu_reconfig_notifier,
+};
 
 /* These are called very early. */
 void iommu_init_early_pSeries(void)
 {
-	if (of_chosen && get_property(of_chosen, "linux,iommu-off", NULL)) {
+	if (of_chosen && of_get_property(of_chosen, "linux,iommu-off", NULL)) {
 		/* Direct I/O, IOMMU off */
 		ppc_md.pci_dma_dev_setup = NULL;
 		ppc_md.pci_dma_bus_setup = NULL;
-		pci_dma_ops = &dma_direct_ops;
+		set_pci_dma_ops(&dma_direct_ops);
 		return;
 	}
 
@@ -588,6 +595,6 @@
 
 	pSeries_reconfig_notifier_register(&iommu_reconfig_nb);
 
-	pci_dma_ops = &dma_iommu_ops;
+	set_pci_dma_ops(&dma_iommu_ops);
 }
 
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index 843ee96..3a70e8a 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -209,13 +209,13 @@
 	/* find the boot console from /chosen/stdout */
 	if (!of_chosen)
 		return;
-	name = get_property(of_chosen, "linux,stdout-path", NULL);
+	name = of_get_property(of_chosen, "linux,stdout-path", NULL);
 	if (name == NULL)
 		return;
 	stdout_node = of_find_node_by_path(name);
 	if (!stdout_node)
 		return;
-	name = get_property(stdout_node, "name", NULL);
+	name = of_get_property(stdout_node, "name", NULL);
 	if (!name) {
 		printk(KERN_WARNING "stdout node missing 'name' property!\n");
 		goto out;
@@ -226,7 +226,7 @@
 	/* Check if it's a virtual terminal */
 	if (strncmp(name, "vty", 3) != 0)
 		goto out;
-	termno = get_property(stdout_node, "reg", NULL);
+	termno = of_get_property(stdout_node, "reg", NULL);
 	if (termno == NULL)
 		goto out;
 	vtermno = termno[0];
diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index 64163ce..f68903e 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -130,7 +130,7 @@
 	if (nvram == NULL)
 		return -ENODEV;
 
-	nbytes_p = get_property(nvram, "#bytes", &proplen);
+	nbytes_p = of_get_property(nvram, "#bytes", &proplen);
 	if (nbytes_p == NULL || proplen != sizeof(unsigned int))
 		return -EIO;
 
diff --git a/arch/powerpc/platforms/pseries/pci.c b/arch/powerpc/platforms/pseries/pci.c
index fa59124..2c6ded2 100644
--- a/arch/powerpc/platforms/pseries/pci.c
+++ b/arch/powerpc/platforms/pseries/pci.c
@@ -25,6 +25,7 @@
 #include <linux/pci.h>
 #include <linux/string.h>
 
+#include <asm/eeh.h>
 #include <asm/pci-bridge.h>
 #include <asm/prom.h>
 #include <asm/ppc-pci.h>
@@ -39,7 +40,7 @@
 	 */
 	dn = pci_device_to_OF_node(dev);
 	if (dn) {
-		char *loc_code = get_property(dn, "ibm,loc-code", 0);
+		const char *loc_code = of_get_property(dn, "ibm,loc-code", 0);
 		if (loc_code) {
 			int loc_len = strlen(loc_code);
 			if (loc_len < sizeof(dev->dev.name)) {
diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c
index ac56b86..fdc1a36 100644
--- a/arch/powerpc/platforms/pseries/pci_dlpar.c
+++ b/arch/powerpc/platforms/pseries/pci_dlpar.c
@@ -29,6 +29,7 @@
 #include <asm/pci-bridge.h>
 #include <asm/ppc-pci.h>
 #include <asm/firmware.h>
+#include <asm/eeh.h>
 
 static struct pci_bus *
 find_bus_among_children(struct pci_bus *bus,
diff --git a/arch/powerpc/platforms/pseries/ras.c b/arch/powerpc/platforms/pseries/ras.c
index edc0388..53aa041 100644
--- a/arch/powerpc/platforms/pseries/ras.c
+++ b/arch/powerpc/platforms/pseries/ras.c
@@ -85,7 +85,7 @@
 	 * map those interrupts using the default interrupt host and default
 	 * trigger
 	 */
-	opicprop = get_property(np, "open-pic-interrupt", &opicplen);
+	opicprop = of_get_property(np, "open-pic-interrupt", &opicplen);
 	if (opicprop) {
 		opicplen /= sizeof(u32);
 		for (i = 0; i < opicplen; i++) {
diff --git a/arch/powerpc/platforms/pseries/rtasd.c b/arch/powerpc/platforms/pseries/rtasd.c
index 77d0937..9797b10 100644
--- a/arch/powerpc/platforms/pseries/rtasd.c
+++ b/arch/powerpc/platforms/pseries/rtasd.c
@@ -363,7 +363,7 @@
 
 	node = of_find_node_by_path("/rtas");
 
-	ip = get_property(node, "rtas-event-scan-rate", NULL);
+	ip = of_get_property(node, "rtas-event-scan-rate", NULL);
 	if (ip == NULL) {
 		printk(KERN_ERR "rtasd: no rtas-event-scan-rate\n");
 		of_node_put(node);
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index 34aff47..33eec28 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -65,6 +65,7 @@
 #include <asm/udbg.h>
 #include <asm/smp.h>
 #include <asm/firmware.h>
+#include <asm/eeh.h>
 
 #include "plpar_wrappers.h"
 #include "pseries.h"
@@ -92,7 +93,7 @@
 
 	root = of_find_node_by_path("/");
 	if (root)
-		model = get_property(root, "model", NULL);
+		model = of_get_property(root, "model", NULL);
 	seq_printf(m, "machine\t\t: CHRP %s\n", model);
 	of_node_put(root);
 }
@@ -138,8 +139,8 @@
 	struct mpic *mpic;
 
 	np = of_find_node_by_path("/");
-	naddr = prom_n_addr_cells(np);
-	opprop = get_property(np, "platform-open-pic", &opplen);
+	naddr = of_n_addr_cells(np);
+	opprop = of_get_property(np, "platform-open-pic", &opplen);
 	if (opprop != 0) {
 		openpic_addr = of_read_number(opprop, naddr);
 		printk(KERN_DEBUG "OpenPIC addr: %lx\n", openpic_addr);
@@ -188,11 +189,11 @@
 			break;
 		if (strcmp(np->name, "pci") != 0)
 			continue;
-		addrp = get_property(np, "8259-interrupt-acknowledge",
+		addrp = of_get_property(np, "8259-interrupt-acknowledge",
 					    NULL);
 		if (addrp == NULL)
 			continue;
-		naddr = prom_n_addr_cells(np);
+		naddr = of_n_addr_cells(np);
 		intack = addrp[naddr-1];
 		if (naddr > 1)
 			intack |= ((unsigned long)addrp[naddr-2]) << 32;
@@ -225,7 +226,7 @@
 
 	for (np = NULL; (np = of_find_node_by_name(np,
 						   "interrupt-controller"));) {
-		typep = get_property(np, "compatible", NULL);
+		typep = of_get_property(np, "compatible", NULL);
 		if (strstr(typep, "open-pic")) {
 			pSeries_mpic_node = of_node_get(np);
 			ppc_md.init_IRQ       = pseries_mpic_init_IRQ;
@@ -334,32 +335,6 @@
 	DBG(" <- pSeries_init_early()\n");
 }
 
-
-static int pSeries_check_legacy_ioport(unsigned int baseport)
-{
-	struct device_node *np;
-
-#define I8042_DATA_REG	0x60
-#define FDC_BASE	0x3f0
-
-
-	switch(baseport) {
-	case I8042_DATA_REG:
-		np = of_find_node_by_type(NULL, "8042");
-		if (np == NULL)
-			return -ENODEV;
-		of_node_put(np);
-		break;
-	case FDC_BASE:
-		np = of_find_node_by_type(NULL, "fdc");
-		if (np == NULL)
-			return -ENODEV;
-		of_node_put(np);
-		break;
-	}
-	return 0;
-}
-
 /*
  * Called very early, MMU is off, device-tree isn't unflattened
  */
@@ -514,6 +489,10 @@
 	for (;;);
 }
 
+#ifndef CONFIG_PCI
+void pSeries_final_fixup(void) { }
+#endif
+
 define_machine(pseries) {
 	.name			= "pSeries",
 	.probe			= pSeries_probe,
@@ -532,7 +511,6 @@
 	.set_rtc_time		= rtas_set_rtc_time,
 	.calibrate_decr		= generic_calibrate_decr,
 	.progress		= rtas_progress,
-	.check_legacy_ioport	= pSeries_check_legacy_ioport,
 	.system_reset_exception = pSeries_system_reset_exception,
 	.machine_check_exception = pSeries_machine_check_exception,
 };
diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c
index 81d172d..896cbf3 100644
--- a/arch/powerpc/platforms/pseries/xics.c
+++ b/arch/powerpc/platforms/pseries/xics.c
@@ -576,7 +576,7 @@
 	 * This happens to be the case so far but we are playing with fire...
 	 * should be fixed one of these days. -BenH.
 	 */
-	ireg = get_property(np, "ibm,interrupt-server-ranges", NULL);
+	ireg = of_get_property(np, "ibm,interrupt-server-ranges", NULL);
 
 	/* Do that ever happen ? we'll know soon enough... but even good'old
 	 * f80 does have that property ..
@@ -588,7 +588,7 @@
 		 */
 		*indx = *ireg;
 	}
-	ireg = get_property(np, "reg", &ilen);
+	ireg = of_get_property(np, "reg", &ilen);
 	if (!ireg)
 		panic("xics_init_IRQ: can't find interrupt reg property");
 
@@ -640,10 +640,10 @@
 			break;
 		if (strcmp(np->name, "pci") != 0)
 			continue;
-		addrp = get_property(np, "8259-interrupt-acknowledge", NULL);
+		addrp = of_get_property(np, "8259-interrupt-acknowledge", NULL);
 		if (addrp == NULL)
 			continue;
-		naddr = prom_n_addr_cells(np);
+		naddr = of_n_addr_cells(np);
 		intack = addrp[naddr-1];
 		if (naddr > 1)
 			intack |= ((unsigned long)addrp[naddr-2]) << 32;
@@ -664,10 +664,11 @@
 		int i, len;
 		const u32 *intserv;
 
-		intserv = get_property(np, "ibm,ppc-interrupt-server#s", &len);
+		intserv = of_get_property(np, "ibm,ppc-interrupt-server#s",
+					&len);
 
 		if (!intserv)
-			intserv = get_property(np, "reg", &len);
+			intserv = of_get_property(np, "reg", &len);
 
 		i = len / sizeof(u32);
 
@@ -709,7 +710,7 @@
 	/* Find the server numbers for the boot cpu. */
 	np = cpuid_to_of_node(boot_cpuid);
 	BUG_ON(!np);
-	ireg = get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen);
+	ireg = of_get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen);
 	if (!ireg)
 		goto skip_gserver_check;
 	i = ilen / sizeof(int);
@@ -725,7 +726,7 @@
 			default_server = hcpuid;
 			default_distrib_server = ireg[j+1];
 
-			isize = get_property(np,
+			isize = of_get_property(np,
 					"ibm,interrupt-server#-size", NULL);
 			if (isize)
 				interrupt_server_size = *isize;
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index 26ca3ff..e96ca96 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -11,12 +11,17 @@
 obj-$(CONFIG_U3_DART)		+= dart_iommu.o
 obj-$(CONFIG_MMIO_NVRAM)	+= mmio_nvram.o
 obj-$(CONFIG_FSL_SOC)		+= fsl_soc.o
+obj-$(CONFIG_FSL_PCIE)		+= fsl_pcie.o
 obj-$(CONFIG_TSI108_BRIDGE)	+= tsi108_pci.o tsi108_dev.o
 obj-$(CONFIG_QUICC_ENGINE)	+= qe_lib/
 
+# contains only the suspend handler for time
+obj-$(CONFIG_PM)		+= timer.o
+
 ifeq ($(CONFIG_PPC_MERGE),y)
 obj-$(CONFIG_PPC_I8259)		+= i8259.o
 obj-$(CONFIG_PPC_83xx)		+= ipic.o
+obj-$(CONFIG_4xx)		+= uic.o
 endif
 
 # Temporary hack until we have migrated to asm-powerpc
diff --git a/arch/powerpc/sysdev/dart_iommu.c b/arch/powerpc/sysdev/dart_iommu.c
index 1488535..336186d 100644
--- a/arch/powerpc/sysdev/dart_iommu.c
+++ b/arch/powerpc/sysdev/dart_iommu.c
@@ -333,7 +333,7 @@
 		ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_dart;
 
 		/* Setup pci_dma ops */
-		pci_dma_ops = &dma_iommu_ops;
+		set_pci_dma_ops(&dma_iommu_ops);
 		return;
 	}
 
@@ -343,7 +343,7 @@
 	ppc_md.pci_dma_bus_setup = NULL;
 
 	/* Setup pci_dma ops */
-	pci_dma_ops = &dma_direct_ops;
+	set_pci_dma_ops(&dma_direct_ops);
 }
 
 
diff --git a/arch/powerpc/sysdev/dcr.c b/arch/powerpc/sysdev/dcr.c
index 1fc5819..574b6ef 100644
--- a/arch/powerpc/sysdev/dcr.c
+++ b/arch/powerpc/sysdev/dcr.c
@@ -26,7 +26,7 @@
 unsigned int dcr_resource_start(struct device_node *np, unsigned int index)
 {
 	unsigned int ds;
-	const u32 *dr = get_property(np, "dcr-reg", &ds);
+	const u32 *dr = of_get_property(np, "dcr-reg", &ds);
 
 	if (dr == NULL || ds & 1 || index >= (ds / 8))
 		return 0;
@@ -37,7 +37,7 @@
 unsigned int dcr_resource_len(struct device_node *np, unsigned int index)
 {
 	unsigned int ds;
-	const u32 *dr = get_property(np, "dcr-reg", &ds);
+	const u32 *dr = of_get_property(np, "dcr-reg", &ds);
 
 	if (dr == NULL || ds & 1 || index >= (ds / 8))
 		return 0;
@@ -53,9 +53,9 @@
 	const u32 *p;
 
 	for (par = of_node_get(node); par;) {
-		if (get_property(par, "dcr-controller", NULL))
+		if (of_get_property(par, "dcr-controller", NULL))
 			break;
-		p = get_property(par, "dcr-parent", NULL);
+		p = of_get_property(par, "dcr-parent", NULL);
 		tmp = par;
 		if (p == NULL)
 			par = of_get_parent(par);
@@ -80,13 +80,13 @@
 		return OF_BAD_ADDR;
 
 	/* Stride is not properly defined yet, default to 0x10 for Axon */
-	p = get_property(dp, "dcr-mmio-stride", NULL);
+	p = of_get_property(dp, "dcr-mmio-stride", NULL);
 	stride = (p == NULL) ? 0x10 : *p;
 
 	/* XXX FIXME: Which property name is to use of the 2 following ? */
-	p = get_property(dp, "dcr-mmio-range", NULL);
+	p = of_get_property(dp, "dcr-mmio-range", NULL);
 	if (p == NULL)
-		p = get_property(dp, "dcr-mmio-space", NULL);
+		p = of_get_property(dp, "dcr-mmio-space", NULL);
 	if (p == NULL)
 		return OF_BAD_ADDR;
 
diff --git a/arch/powerpc/platforms/86xx/mpc86xx_pcie.c b/arch/powerpc/sysdev/fsl_pcie.c
similarity index 98%
rename from arch/powerpc/platforms/86xx/mpc86xx_pcie.c
rename to arch/powerpc/sysdev/fsl_pcie.c
index a2f4f73..041c07e 100644
--- a/arch/powerpc/platforms/86xx/mpc86xx_pcie.c
+++ b/arch/powerpc/sysdev/fsl_pcie.c
@@ -24,8 +24,6 @@
 #include <asm/pci-bridge.h>
 #include <asm/machdep.h>
 
-#include "mpc86xx.h"
-
 #define PCI_CFG_OUT out_be32
 
 /* ERRATA PCI-Ex 14 PCIE Controller timeout */
diff --git a/arch/powerpc/sysdev/fsl_pcie.h b/arch/powerpc/sysdev/fsl_pcie.h
new file mode 100644
index 0000000..8d9779c
--- /dev/null
+++ b/arch/powerpc/sysdev/fsl_pcie.h
@@ -0,0 +1,94 @@
+/*
+ * MPC85xx/86xx PCI Express structure define
+ *
+ * Copyright 2007 Freescale Semiconductor, Inc
+ *
+ * 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.
+ *
+ */
+
+#ifdef __KERNEL__
+#ifndef __POWERPC_FSL_PCIE_H
+#define __POWERPC_FSL_PCIE_H
+
+/* PCIE Express IO block registers in 85xx/86xx */
+
+struct ccsr_pex {
+	__be32 __iomem    pex_config_addr;	/* 0x.000 - PCI Express Configuration Address Register */
+	__be32 __iomem    pex_config_data;	/* 0x.004 - PCI Express Configuration Data Register */
+	u8 __iomem    res1[4];
+	__be32 __iomem    pex_otb_cpl_tor;	/* 0x.00c - PCI Express Outbound completion timeout register */
+	__be32 __iomem    pex_conf_tor;		/* 0x.010 - PCI Express configuration timeout register */
+	u8 __iomem    res2[12];
+	__be32 __iomem    pex_pme_mes_dr;	/* 0x.020 - PCI Express PME and message detect register */
+	__be32 __iomem    pex_pme_mes_disr;	/* 0x.024 - PCI Express PME and message disable register */
+	__be32 __iomem    pex_pme_mes_ier;	/* 0x.028 - PCI Express PME and message interrupt enable register */
+	__be32 __iomem    pex_pmcr;		/* 0x.02c - PCI Express power management command register */
+	u8 __iomem    res3[3024];
+	__be32 __iomem    pexotar0;		/* 0x.c00 - PCI Express outbound translation address register 0 */
+	__be32 __iomem    pexotear0;		/* 0x.c04 - PCI Express outbound translation extended address register 0*/
+	u8 __iomem    res4[8];
+	__be32 __iomem    pexowar0;		/* 0x.c10 - PCI Express outbound window attributes register 0*/
+	u8 __iomem    res5[12];
+	__be32 __iomem    pexotar1;		/* 0x.c20 - PCI Express outbound translation address register 1 */
+	__be32 __iomem    pexotear1;		/* 0x.c24 - PCI Express outbound translation extended address register 1*/
+	__be32 __iomem    pexowbar1;		/* 0x.c28 - PCI Express outbound window base address register 1*/
+	u8 __iomem    res6[4];
+	__be32 __iomem    pexowar1;		/* 0x.c30 - PCI Express outbound window attributes register 1*/
+	u8 __iomem    res7[12];
+	__be32 __iomem    pexotar2;		/* 0x.c40 - PCI Express outbound translation address register 2 */
+	__be32 __iomem    pexotear2;		/* 0x.c44 - PCI Express outbound translation extended address register 2*/
+	__be32 __iomem    pexowbar2;		/* 0x.c48 - PCI Express outbound window base address register 2*/
+	u8 __iomem    res8[4];
+	__be32 __iomem    pexowar2;		/* 0x.c50 - PCI Express outbound window attributes register 2*/
+	u8 __iomem    res9[12];
+	__be32 __iomem    pexotar3;		/* 0x.c60 - PCI Express outbound translation address register 3 */
+	__be32 __iomem    pexotear3;		/* 0x.c64 - PCI Express outbound translation extended address register 3*/
+	__be32 __iomem    pexowbar3;		/* 0x.c68 - PCI Express outbound window base address register 3*/
+	u8 __iomem    res10[4];
+	__be32 __iomem    pexowar3;		/* 0x.c70 - PCI Express outbound window attributes register 3*/
+	u8 __iomem    res11[12];
+	__be32 __iomem    pexotar4;		/* 0x.c80 - PCI Express outbound translation address register 4 */
+	__be32 __iomem    pexotear4;		/* 0x.c84 - PCI Express outbound translation extended address register 4*/
+	__be32 __iomem    pexowbar4;		/* 0x.c88 - PCI Express outbound window base address register 4*/
+	u8 __iomem    res12[4];
+	__be32 __iomem    pexowar4;		/* 0x.c90 - PCI Express outbound window attributes register 4*/
+	u8 __iomem    res13[12];
+	u8 __iomem    res14[256];
+	__be32 __iomem    pexitar3;		/* 0x.da0 - PCI Express inbound translation address register 3 */
+	u8 __iomem    res15[4];
+	__be32 __iomem    pexiwbar3;		/* 0x.da8 - PCI Express inbound window base address register 3 */
+	__be32 __iomem    pexiwbear3;		/* 0x.dac - PCI Express inbound window base extended address register 3 */
+	__be32 __iomem    pexiwar3;		/* 0x.db0 - PCI Express inbound window attributes register 3 */
+	u8 __iomem    res16[12];
+	__be32 __iomem    pexitar2;		/* 0x.dc0 - PCI Express inbound translation address register 2 */
+	u8 __iomem    res17[4];
+	__be32 __iomem    pexiwbar2;		/* 0x.dc8 - PCI Express inbound window base address register 2 */
+	__be32 __iomem    pexiwbear2;		/* 0x.dcc - PCI Express inbound window base extended address register 2 */
+	__be32 __iomem    pexiwar2;		/* 0x.dd0 - PCI Express inbound window attributes register 2 */
+	u8 __iomem    res18[12];
+	__be32 __iomem    pexitar1;		/* 0x.de0 - PCI Express inbound translation address register 2 */
+	u8 __iomem    res19[4];
+	__be32 __iomem    pexiwbar1;		/* 0x.de8 - PCI Express inbound window base address register 2 */
+	__be32 __iomem    pexiwbear1;		/* 0x.dec - PCI Express inbound window base extended address register 2 */
+	__be32 __iomem    pexiwar1;		/* 0x.df0 - PCI Express inbound window attributes register 2 */
+	u8 __iomem    res20[12];
+	__be32 __iomem    pex_err_dr;		/* 0x.e00 - PCI Express error detect register */
+	u8 __iomem    res21[4];
+	__be32 __iomem    pex_err_en;		/* 0x.e08 - PCI Express error interrupt enable register */
+	u8 __iomem    res22[4];
+	__be32 __iomem    pex_err_disr;		/* 0x.e10 - PCI Express error disable register */
+	u8 __iomem    res23[12];
+	__be32 __iomem    pex_err_cap_stat;	/* 0x.e20 - PCI Express error capture status register */
+	u8 __iomem    res24[4];
+	__be32 __iomem    pex_err_cap_r0;	/* 0x.e28 - PCI Express error capture register 0 */
+	__be32 __iomem    pex_err_cap_r1;	/* 0x.e2c - PCI Express error capture register 0 */
+	__be32 __iomem    pex_err_cap_r2;	/* 0x.e30 - PCI Express error capture register 0 */
+	__be32 __iomem    pex_err_cap_r3;	/* 0x.e34 - PCI Express error capture register 0 */
+};
+
+#endif /* __POWERPC_FSL_PCIE_H */
+#endif /* __KERNEL__ */
diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c
index d20f0292..8a123c7 100644
--- a/arch/powerpc/sysdev/fsl_soc.c
+++ b/arch/powerpc/sysdev/fsl_soc.c
@@ -52,7 +52,7 @@
 	soc = of_find_node_by_type(NULL, "soc");
 	if (soc) {
 		unsigned int size;
-		const void *prop = get_property(soc, "reg", &size);
+		const void *prop = of_get_property(soc, "reg", &size);
 
 		if (prop)
 			immrbase = of_translate_address(soc, prop);
@@ -78,8 +78,8 @@
 	node = of_find_node_by_type(NULL, "cpm");
 	if (node) {
 		unsigned int size;
-		const unsigned int *prop = get_property(node, "brg-frequency",
-					&size);
+		const unsigned int *prop = of_get_property(node,
+					"brg-frequency", &size);
 
 		if (prop)
 			brgfreq = *prop;
@@ -103,8 +103,8 @@
 	node = of_find_node_by_type(NULL, "serial");
 	if (node) {
 		unsigned int size;
-		const unsigned int *prop = get_property(node, "current-speed",
-				&size);
+		const unsigned int *prop = of_get_property(node,
+				"current-speed", &size);
 
 		if (prop)
 			fs_baudrate = *prop;
@@ -153,7 +153,8 @@
 		while ((child = of_get_next_child(np, child)) != NULL) {
 			int irq = irq_of_parse_and_map(child, 0);
 			if (irq != NO_IRQ) {
-				const u32 *id = get_property(child, "reg", NULL);
+				const u32 *id = of_get_property(child,
+							"reg", NULL);
 				mdio_data.irq[*id] = irq;
 			}
 		}
@@ -209,7 +210,7 @@
 
 		of_irq_to_resource(np, 0, &r[1]);
 
-		model = get_property(np, "model", NULL);
+		model = of_get_property(np, "model", NULL);
 
 		/* If we aren't the FEC we have multiple interrupts */
 		if (model && strcasecmp(model, "FEC")) {
@@ -253,7 +254,7 @@
 			    FSL_GIANFAR_DEV_HAS_VLAN |
 			    FSL_GIANFAR_DEV_HAS_EXTENDED_HASH;
 
-		ph = get_property(np, "phy-handle", NULL);
+		ph = of_get_property(np, "phy-handle", NULL);
 		phy = of_find_node_by_phandle(*ph);
 
 		if (phy == NULL) {
@@ -263,7 +264,7 @@
 
 		mdio = of_get_parent(phy);
 
-		id = get_property(phy, "reg", NULL);
+		id = of_get_property(phy, "reg", NULL);
 		ret = of_address_to_resource(mdio, 0, &res);
 		if (ret) {
 			of_node_put(phy);
@@ -325,11 +326,11 @@
 		}
 
 		i2c_data.device_flags = 0;
-		flags = get_property(np, "dfsrr", NULL);
+		flags = of_get_property(np, "dfsrr", NULL);
 		if (flags)
 			i2c_data.device_flags |= FSL_I2C_DEV_SEPARATE_DFSRR;
 
-		flags = get_property(np, "fsl5200-clocking", NULL);
+		flags = of_get_property(np, "fsl5200-clocking", NULL);
 		if (flags)
 			i2c_data.device_flags |= FSL_I2C_DEV_CLOCK_5200;
 
@@ -374,7 +375,7 @@
 		goto nosoc;
 	}
 
-	freq = get_property(soc, "bus-frequency", NULL);
+	freq = of_get_property(soc, "bus-frequency", NULL);
 	if (!freq) {
 		ret = -ENODEV;
 		goto err;
@@ -466,15 +467,15 @@
 
 		usb_data.operating_mode = FSL_USB2_MPH_HOST;
 
-		prop = get_property(np, "port0", NULL);
+		prop = of_get_property(np, "port0", NULL);
 		if (prop)
 			usb_data.port_enables |= FSL_USB2_PORT0_ENABLED;
 
-		prop = get_property(np, "port1", NULL);
+		prop = of_get_property(np, "port1", NULL);
 		if (prop)
 			usb_data.port_enables |= FSL_USB2_PORT1_ENABLED;
 
-		prop = get_property(np, "phy_type", NULL);
+		prop = of_get_property(np, "phy_type", NULL);
 		usb_data.phy_mode = determine_usb_phy(prop);
 
 		ret =
@@ -501,7 +502,7 @@
 
 		of_irq_to_resource(np, 0, &r[1]);
 
-		prop = get_property(np, "dr_mode", NULL);
+		prop = of_get_property(np, "dr_mode", NULL);
 
 		if (!prop || !strcmp(prop, "host")) {
 			usb_data.operating_mode = FSL_USB2_DR_HOST;
@@ -538,7 +539,7 @@
 			goto err;
 		}
 
-		prop = get_property(np, "phy_type", NULL);
+		prop = of_get_property(np, "phy_type", NULL);
 		usb_data.phy_mode = determine_usb_phy(prop);
 
 		if (usb_dev_dr_host) {
@@ -633,7 +634,7 @@
 			goto err;
 		}
 
-		model = get_property(np, "model", NULL);
+		model = of_get_property(np, "model", NULL);
 		if (model == NULL) {
 			ret = -ENODEV;
 			goto unreg;
@@ -643,7 +644,7 @@
 		if (mac_addr)
 			memcpy(fs_enet_data.macaddr, mac_addr, 6);
 
-		ph = get_property(np, "phy-handle", NULL);
+		ph = of_get_property(np, "phy-handle", NULL);
 		phy = of_find_node_by_phandle(*ph);
 
 		if (phy == NULL) {
@@ -651,12 +652,12 @@
 			goto unreg;
 		}
 
-		phy_addr = get_property(phy, "reg", NULL);
+		phy_addr = of_get_property(phy, "reg", NULL);
 		fs_enet_data.phy_addr = *phy_addr;
 
-		phy_irq = get_property(phy, "interrupts", NULL);
+		phy_irq = of_get_property(phy, "interrupts", NULL);
 
-		id = get_property(np, "device-id", NULL);
+		id = of_get_property(np, "device-id", NULL);
 		fs_enet_data.fs_no = *id;
 		strcpy(fs_enet_data.fs_type, model);
 
@@ -668,8 +669,10 @@
                         goto unreg;
                 }
 
-		fs_enet_data.clk_rx = *((u32 *) get_property(np, "rx-clock", NULL));
-		fs_enet_data.clk_tx = *((u32 *) get_property(np, "tx-clock", NULL));
+		fs_enet_data.clk_rx = *((u32 *)of_get_property(np,
+						"rx-clock", NULL));
+		fs_enet_data.clk_tx = *((u32 *)of_get_property(np,
+						"tx-clock", NULL));
 
 		if (strstr(model, "FCC")) {
 			int fcc_index = *id - 1;
@@ -690,7 +693,7 @@
 			fs_enet_data.bus_id = (char*)&bus_id[(*id)];
 			fs_enet_data.init_ioports = init_fcc_ioports;
 
-			mdio_bb_prop = get_property(phy, "bitbang", NULL);
+			mdio_bb_prop = of_get_property(phy, "bitbang", NULL);
 			if (mdio_bb_prop) {
 				struct platform_device *fs_enet_mdio_bb_dev;
 				struct fs_mii_bb_platform_info fs_enet_mdio_bb_data;
@@ -796,10 +799,10 @@
 			goto err;
 		}
 
-		id = get_property(np, "device-id", NULL);
+		id = of_get_property(np, "device-id", NULL);
 		cpm_uart_data.fs_no = *id;
 
-		model = (char*)get_property(np, "model", NULL);
+		model = of_get_property(np, "model", NULL);
 		strcpy(cpm_uart_data.fs_type, model);
 
 		cpm_uart_data.uart_clk = ppc_proc_freq;
@@ -808,8 +811,10 @@
 		cpm_uart_data.tx_buf_size = 32;
 		cpm_uart_data.rx_num_fifo = 4;
 		cpm_uart_data.rx_buf_size = 32;
-		cpm_uart_data.clk_rx = *((u32 *) get_property(np, "rx-clock", NULL));
-		cpm_uart_data.clk_tx = *((u32 *) get_property(np, "tx-clock", NULL));
+		cpm_uart_data.clk_rx = *((u32 *)of_get_property(np,
+						"rx-clock", NULL));
+		cpm_uart_data.clk_tx = *((u32 *)of_get_property(np,
+						"tx-clock", NULL));
 
 		ret =
 		    platform_device_add_data(cpm_uart_dev, &cpm_uart_data,
@@ -833,7 +838,7 @@
 #ifdef CONFIG_8xx
 
 extern void init_scc_ioports(struct fs_platform_info*);
-extern int platform_device_skip(char *model, int id);
+extern int platform_device_skip(const char *model, int id);
 
 static int __init fs_enet_mdio_of_init(void)
 {
@@ -900,21 +905,22 @@
 		struct resource r[4];
 		struct device_node *phy = NULL, *mdio = NULL;
 		struct fs_platform_info fs_enet_data;
-		unsigned int *id, *phy_addr;
+		const unsigned int *id;
+		const unsigned int *phy_addr;
 		void *mac_addr;
-		phandle *ph;
-		char *model;
+		const phandle *ph;
+		const char *model;
 
 		memset(r, 0, sizeof(r));
 		memset(&fs_enet_data, 0, sizeof(fs_enet_data));
 
-		model = (char *)get_property(np, "model", NULL);
+		model = of_get_property(np, "model", NULL);
 		if (model == NULL) {
 			ret = -ENODEV;
 			goto unreg;
 		}
 
-		id = (u32 *) get_property(np, "device-id", NULL);
+		id = of_get_property(np, "device-id", NULL);
 		fs_enet_data.fs_no = *id;
 
 		if (platform_device_skip(model, *id))
@@ -929,12 +935,12 @@
 		if (mac_addr)
 			memcpy(fs_enet_data.macaddr, mac_addr, 6);
 
-		ph = (phandle *) get_property(np, "phy-handle", NULL);
+		ph = of_get_property(np, "phy-handle", NULL);
 		if (ph != NULL)
 			phy = of_find_node_by_phandle(*ph);
 
 		if (phy != NULL) {
-			phy_addr = (u32 *) get_property(phy, "reg", NULL);
+			phy_addr = of_get_property(phy, "reg", NULL);
 			fs_enet_data.phy_addr = *phy_addr;
 			fs_enet_data.has_phy = 1;
 
@@ -947,7 +953,7 @@
 			}
 		}
 
-		model = (char*)get_property(np, "model", NULL);
+		model = of_get_property(np, "model", NULL);
 		strcpy(fs_enet_data.fs_type, model);
 
 		if (strstr(model, "FEC")) {
@@ -1038,8 +1044,8 @@
 	     i++) {
 		struct resource r[3];
 		struct fs_uart_platform_info cpm_uart_data;
-		int *id;
-		char *model;
+		const int *id;
+		const char *model;
 
 		memset(r, 0, sizeof(r));
 		memset(&cpm_uart_data, 0, sizeof(cpm_uart_data));
@@ -1066,10 +1072,10 @@
 			goto err;
 		}
 
-		model = (char*)get_property(np, "model", NULL);
+		model = of_get_property(np, "model", NULL);
 		strcpy(cpm_uart_data.fs_type, model);
 
-		id = (int*)get_property(np, "device-id", NULL);
+		id = of_get_property(np, "device-id", NULL);
 		cpm_uart_data.fs_no = *id;
 		cpm_uart_data.uart_clk = ppc_proc_freq;
 
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
index bcfb900..0b84b7c 100644
--- a/arch/powerpc/sysdev/mpic.c
+++ b/arch/powerpc/sysdev/mpic.c
@@ -304,7 +304,7 @@
 	}
 }
 
-#ifdef CONFIG_MPIC_BROKEN_U3
+#ifdef CONFIG_MPIC_U3_HT_IRQS
 
 /* Test if an interrupt is sourced from HyperTransport (used on broken U3s)
  * to force the edge setting on the MPIC and do the ack workaround.
@@ -476,7 +476,7 @@
 	}
 }
 
-#else /* CONFIG_MPIC_BROKEN_U3 */
+#else /* CONFIG_MPIC_U3_HT_IRQS */
 
 static inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source)
 {
@@ -487,7 +487,7 @@
 {
 }
 
-#endif /* CONFIG_MPIC_BROKEN_U3 */
+#endif /* CONFIG_MPIC_U3_HT_IRQS */
 
 
 #define mpic_irq_to_hw(virq)	((unsigned int)irq_map[virq].hwirq)
@@ -615,7 +615,7 @@
 	mpic_eoi(mpic);
 }
 
-#ifdef CONFIG_MPIC_BROKEN_U3
+#ifdef CONFIG_MPIC_U3_HT_IRQS
 
 static void mpic_unmask_ht_irq(unsigned int irq)
 {
@@ -665,7 +665,7 @@
 		mpic_ht_end_irq(mpic, src);
 	mpic_eoi(mpic);
 }
-#endif /* !CONFIG_MPIC_BROKEN_U3 */
+#endif /* !CONFIG_MPIC_U3_HT_IRQS */
 
 #ifdef CONFIG_SMP
 
@@ -788,7 +788,7 @@
 };
 #endif /* CONFIG_SMP */
 
-#ifdef CONFIG_MPIC_BROKEN_U3
+#ifdef CONFIG_MPIC_U3_HT_IRQS
 static struct irq_chip mpic_irq_ht_chip = {
 	.startup	= mpic_startup_ht_irq,
 	.shutdown	= mpic_shutdown_ht_irq,
@@ -797,7 +797,7 @@
 	.eoi		= mpic_end_ht_irq,
 	.set_type	= mpic_set_irq_type,
 };
-#endif /* CONFIG_MPIC_BROKEN_U3 */
+#endif /* CONFIG_MPIC_U3_HT_IRQS */
 
 
 static int mpic_host_match(struct irq_host *h, struct device_node *node)
@@ -837,11 +837,11 @@
 	/* Default chip */
 	chip = &mpic->hc_irq;
 
-#ifdef CONFIG_MPIC_BROKEN_U3
+#ifdef CONFIG_MPIC_U3_HT_IRQS
 	/* Check for HT interrupts, override vecpri */
 	if (mpic_is_ht_interrupt(mpic, hw))
 		chip = &mpic->hc_ht_irq;
-#endif /* CONFIG_MPIC_BROKEN_U3 */
+#endif /* CONFIG_MPIC_U3_HT_IRQS */
 
 	DBG("mpic: mapping to irq chip @%p\n", chip);
 
@@ -937,12 +937,12 @@
 	mpic->hc_irq.typename = name;
 	if (flags & MPIC_PRIMARY)
 		mpic->hc_irq.set_affinity = mpic_set_affinity;
-#ifdef CONFIG_MPIC_BROKEN_U3
+#ifdef CONFIG_MPIC_U3_HT_IRQS
 	mpic->hc_ht_irq = mpic_irq_ht_chip;
 	mpic->hc_ht_irq.typename = name;
 	if (flags & MPIC_PRIMARY)
 		mpic->hc_ht_irq.set_affinity = mpic_set_affinity;
-#endif /* CONFIG_MPIC_BROKEN_U3 */
+#endif /* CONFIG_MPIC_U3_HT_IRQS */
 
 #ifdef CONFIG_SMP
 	mpic->hc_ipi = mpic_ipi_chip;
@@ -970,7 +970,7 @@
 	mpic->spurious_vec  = intvec_top;
 
 	/* Check for "big-endian" in device-tree */
-	if (node && get_property(node, "big-endian", NULL) != NULL)
+	if (node && of_get_property(node, "big-endian", NULL) != NULL)
 		mpic->flags |= MPIC_BIG_ENDIAN;
 
 
@@ -986,13 +986,13 @@
 	BUG_ON(paddr == 0 && node == NULL);
 
 	/* If no physical address passed in, check if it's dcr based */
-	if (paddr == 0 && get_property(node, "dcr-reg", NULL) != NULL)
+	if (paddr == 0 && of_get_property(node, "dcr-reg", NULL) != NULL)
 		mpic->flags |= MPIC_USES_DCR;
 
 #ifdef CONFIG_PPC_DCR
 	if (mpic->flags & MPIC_USES_DCR) {
 		const u32 *dbasep;
-		dbasep = get_property(node, "dcr-reg", NULL);
+		dbasep = of_get_property(node, "dcr-reg", NULL);
 		BUG_ON(dbasep == NULL);
 		mpic->dcr_base = *dbasep;
 		mpic->reg_type = mpic_access_dcr;
@@ -1006,7 +1006,7 @@
 	 */
 	if (paddr == 0 && !(mpic->flags & MPIC_USES_DCR)) {
 		const u32 *reg;
-		reg = get_property(node, "reg", NULL);
+		reg = of_get_property(node, "reg", NULL);
 		BUG_ON(reg == NULL);
 		paddr = of_translate_address(node, reg);
 		BUG_ON(paddr == OF_BAD_ADDR);
@@ -1142,7 +1142,7 @@
 
 	/* Do the HT PIC fixups on U3 broken mpic */
 	DBG("MPIC flags: %x\n", mpic->flags);
-	if ((mpic->flags & MPIC_BROKEN_U3) && (mpic->flags & MPIC_PRIMARY))
+	if ((mpic->flags & MPIC_U3_HT_IRQS) && (mpic->flags & MPIC_PRIMARY))
  		mpic_scan_ht_pics(mpic);
 
 	for (i = 0; i < mpic->num_sources; i++) {
diff --git a/arch/powerpc/sysdev/pmi.c b/arch/powerpc/sysdev/pmi.c
index a528201..85a7c99 100644
--- a/arch/powerpc/sysdev/pmi.c
+++ b/arch/powerpc/sysdev/pmi.c
@@ -33,7 +33,7 @@
 #include <asm/of_platform.h>
 #include <asm/io.h>
 #include <asm/pmi.h>
-
+#include <asm/prom.h>
 
 struct pmi_data {
 	struct list_head	handler;
@@ -49,21 +49,6 @@
 };
 
 
-
-static void __iomem *of_iomap(struct device_node *np)
-{
-	struct resource res;
-
-	if (of_address_to_resource(np, 0, &res))
-		return NULL;
-
-	pr_debug("Resource start: 0x%lx\n", res.start);
-	pr_debug("Resource end: 0x%lx\n", res.end);
-
-	return ioremap(res.start, 1 + res.end - res.start);
-}
-
-
 static int pmi_irq_handler(int irq, void *dev_id)
 {
 	struct pmi_data *data;
@@ -118,6 +103,7 @@
 
 static struct of_device_id pmi_match[] = {
 	{ .type = "ibm,pmi", .name = "ibm,pmi" },
+	{ .type = "ibm,pmi" },
 	{},
 };
 
@@ -153,7 +139,7 @@
 		goto out;
 	}
 
-	data->pmi_reg = of_iomap(np);
+	data->pmi_reg = of_iomap(np, 0);
 	if (!data->pmi_reg) {
 		printk(KERN_ERR "pmi: invalid register address.\n");
 		rc = -EFAULT;
@@ -279,6 +265,9 @@
 	struct pmi_data *data;
 	data = device->dev.driver_data;
 
+	if (!data)
+		return;
+
 	spin_lock(&data->handler_spinlock);
 	list_add_tail(&handler->node, &data->handler);
 	spin_unlock(&data->handler_spinlock);
@@ -289,11 +278,13 @@
 			    struct pmi_handler *handler)
 {
 	struct pmi_data *data;
+	data = device->dev.driver_data;
+
+	if (!data)
+		return;
 
 	pr_debug("pmi: unregistering handler %p\n", handler);
 
-	data = device->dev.driver_data;
-
 	spin_lock(&data->handler_spinlock);
 	list_del(&handler->node);
 	spin_unlock(&data->handler_spinlock);
diff --git a/arch/powerpc/sysdev/qe_lib/Kconfig b/arch/powerpc/sysdev/qe_lib/Kconfig
index a725e80..887739f 100644
--- a/arch/powerpc/sysdev/qe_lib/Kconfig
+++ b/arch/powerpc/sysdev/qe_lib/Kconfig
@@ -2,11 +2,8 @@
 # QE Communication options
 #
 
-menu "QE Options"
-	depends on QUICC_ENGINE
-
 config UCC_SLOW
-	bool "UCC Slow Protocols Support"
+	bool
 	default n
 	select UCC
 	help
@@ -14,10 +11,9 @@
 	  protocols: UART, BISYNC, QMC
 
 config UCC_FAST
-	bool "UCC Fast Protocols Support"
+	bool
 	default n
 	select UCC
-	select UCC_SLOW
 	help
 	  This option provides qe_lib support to UCC fast
 	  protocols: HDLC, Ethernet, ATM, transparent
@@ -26,5 +22,3 @@
 	bool
 	default y if UCC_FAST || UCC_SLOW
 
-endmenu
-
diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c
index 43f6cc9..7f4c075 100644
--- a/arch/powerpc/sysdev/qe_lib/qe.c
+++ b/arch/powerpc/sysdev/qe_lib/qe.c
@@ -71,7 +71,7 @@
 	qe = of_find_node_by_type(NULL, "qe");
 	if (qe) {
 		unsigned int size;
-		const void *prop = get_property(qe, "reg", &size);
+		const void *prop = of_get_property(qe, "reg", &size);
 		qebase = of_translate_address(qe, prop);
 		of_node_put(qe);
 	};
@@ -158,7 +158,7 @@
 	qe = of_find_node_by_type(NULL, "qe");
 	if (qe) {
 		unsigned int size;
-		const u32 *prop = get_property(qe, "brg-frequency", &size);
+		const u32 *prop = of_get_property(qe, "brg-frequency", &size);
 		brg_clk = *prop;
 		of_node_put(qe);
 	};
diff --git a/arch/powerpc/sysdev/qe_lib/qe_io.c b/arch/powerpc/sysdev/qe_lib/qe_io.c
index 0afe6bf..e32b45b 100644
--- a/arch/powerpc/sysdev/qe_lib/qe_io.c
+++ b/arch/powerpc/sysdev/qe_lib/qe_io.c
@@ -53,7 +53,7 @@
 		return ret;
 	par_io = ioremap(res.start, res.end - res.start + 1);
 
-	num_ports = get_property(np, "num-ports", NULL);
+	num_ports = of_get_property(np, "num-ports", NULL);
 	if (num_ports)
 		num_par_io_ports = *num_ports;
 
@@ -161,7 +161,7 @@
 		return -1;
 	}
 
-	ph = get_property(np, "pio-handle", NULL);
+	ph = of_get_property(np, "pio-handle", NULL);
 	if (ph == 0) {
 		printk(KERN_ERR "pio-handle not available \n");
 		return -1;
@@ -169,7 +169,7 @@
 
 	pio = of_find_node_by_phandle(*ph);
 
-	pio_map = get_property(pio, "pio-map", &pio_map_len);
+	pio_map = of_get_property(pio, "pio-map", &pio_map_len);
 	if (pio_map == NULL) {
 		printk(KERN_ERR "pio-map is not set! \n");
 		return -1;
diff --git a/arch/powerpc/sysdev/qe_lib/ucc_fast.c b/arch/powerpc/sysdev/qe_lib/ucc_fast.c
index a457ac1..66137bf 100644
--- a/arch/powerpc/sysdev/qe_lib/ucc_fast.c
+++ b/arch/powerpc/sysdev/qe_lib/ucc_fast.c
@@ -210,6 +210,9 @@
 	uf_regs = uccf->uf_regs;
 	uccf->p_ucce = (u32 *) & (uf_regs->ucce);
 	uccf->p_uccm = (u32 *) & (uf_regs->uccm);
+#ifdef CONFIG_UGETH_TX_ON_DEMAND
+	uccf->p_utodr = (u16 *) & (uf_regs->utodr);
+#endif
 #ifdef STATISTICS
 	uccf->tx_frames = 0;
 	uccf->rx_frames = 0;
diff --git a/arch/powerpc/sysdev/qe_lib/ucc_slow.c b/arch/powerpc/sysdev/qe_lib/ucc_slow.c
index 817df73..b930d68 100644
--- a/arch/powerpc/sysdev/qe_lib/ucc_slow.c
+++ b/arch/powerpc/sysdev/qe_lib/ucc_slow.c
@@ -187,7 +187,7 @@
 	uccs->us_pram = qe_muram_addr(uccs->us_pram_offset);
 
 	/* Init Guemr register */
-	if ((ret = ucc_init_guemr((struct ucc_common *) (us_info->regs)))) {
+	if ((ret = ucc_init_guemr((struct ucc_common *) us_regs))) {
 		printk(KERN_ERR "%s: cannot init GUEMR", __FUNCTION__);
 		ucc_slow_free(uccs);
 		return ret;
@@ -195,7 +195,7 @@
 
 	/* Set UCC to slow type */
 	if ((ret = ucc_set_type(us_info->ucc_num,
-				(struct ucc_common *) (us_info->regs),
+				(struct ucc_common *) us_regs,
 				UCC_SPEED_TYPE_SLOW))) {
 		printk(KERN_ERR "%s: cannot set UCC type", __FUNCTION__);
 		ucc_slow_free(uccs);
diff --git a/arch/powerpc/sysdev/timer.c b/arch/powerpc/sysdev/timer.c
new file mode 100644
index 0000000..4a01748
--- /dev/null
+++ b/arch/powerpc/sysdev/timer.c
@@ -0,0 +1,71 @@
+/*
+ * Common code to keep time when machine suspends.
+ *
+ * Copyright 2007	Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPLv2
+ */
+
+#include <linux/time.h>
+#include <linux/sysdev.h>
+#include <asm/rtc.h>
+
+static unsigned long suspend_rtc_time;
+
+/*
+ * Reset the time after a sleep.
+ */
+static int timer_resume(struct sys_device *dev)
+{
+	struct timeval tv;
+	struct timespec ts;
+	struct rtc_time cur_rtc_tm;
+	unsigned long cur_rtc_time, diff;
+
+	/* get current RTC time and convert to seconds */
+	get_rtc_time(&cur_rtc_tm);
+	rtc_tm_to_time(&cur_rtc_tm, &cur_rtc_time);
+
+	diff = cur_rtc_time - suspend_rtc_time;
+
+	/* adjust time of day by seconds that elapsed while
+	 * we were suspended */
+	do_gettimeofday(&tv);
+	ts.tv_sec = tv.tv_sec + diff;
+	ts.tv_nsec = tv.tv_usec * NSEC_PER_USEC;
+	do_settimeofday(&ts);
+
+	return 0;
+}
+
+static int timer_suspend(struct sys_device *dev, pm_message_t state)
+{
+	struct rtc_time suspend_rtc_tm;
+	WARN_ON(!ppc_md.get_rtc_time);
+
+	get_rtc_time(&suspend_rtc_tm);
+	rtc_tm_to_time(&suspend_rtc_tm, &suspend_rtc_time);
+
+	return 0;
+}
+
+static struct sysdev_class timer_sysclass = {
+	.resume = timer_resume,
+	.suspend = timer_suspend,
+	set_kset_name("timer"),
+};
+
+static struct sys_device device_timer = {
+	.id = 0,
+	.cls = &timer_sysclass,
+};
+
+static int time_init_device(void)
+{
+	int error = sysdev_class_register(&timer_sysclass);
+	if (!error)
+		error = sysdev_register(&device_timer);
+	return error;
+}
+
+device_initcall(time_init_device);
diff --git a/arch/powerpc/sysdev/tsi108_dev.c b/arch/powerpc/sysdev/tsi108_dev.c
index 97f37ef..337039e 100644
--- a/arch/powerpc/sysdev/tsi108_dev.c
+++ b/arch/powerpc/sysdev/tsi108_dev.c
@@ -48,7 +48,7 @@
 	tsi = of_find_node_by_type(NULL, "tsi-bridge");
 	if (tsi) {
 		unsigned int size;
-		const void *prop = get_property(tsi, "reg", &size);
+		const void *prop = of_get_property(tsi, "reg", &size);
 		tsi108_csr_base = of_translate_address(tsi, prop);
 		of_node_put(tsi);
 	};
@@ -77,10 +77,10 @@
 		struct resource r[2];
 		struct device_node *phy;
 		hw_info tsi_eth_data;
-		unsigned int *id;
-		unsigned int *phy_id;
+		const unsigned int *id;
+		const unsigned int *phy_id;
 		const void *mac_addr;
-		phandle *ph;
+		const phandle *ph;
 
 		memset(r, 0, sizeof(r));
 		memset(&tsi_eth_data, 0, sizeof(tsi_eth_data));
@@ -107,10 +107,10 @@
 			goto err;
 		}
 
-		mac_addr = get_property(np, "address", NULL);
+		mac_addr = of_get_property(np, "address", NULL);
 		memcpy(tsi_eth_data.mac_addr, mac_addr, 6);
 
-		ph = (phandle *) get_property(np, "phy-handle", NULL);
+		ph = of_get_property(np, "phy-handle", NULL);
 		phy = of_find_node_by_phandle(*ph);
 
 		if (phy == NULL) {
@@ -118,8 +118,8 @@
 			goto unreg;
 		}
 
-		id = (u32 *) get_property(phy, "reg", NULL);
-		phy_id = (u32 *) get_property(phy, "phy-id", NULL);
+		id = of_get_property(phy, "reg", NULL);
+		phy_id = of_get_property(phy, "phy-id", NULL);
 		ret = of_address_to_resource(phy, 0, &res);
 		if (ret) {
 			of_node_put(phy);
diff --git a/arch/powerpc/sysdev/tsi108_pci.c b/arch/powerpc/sysdev/tsi108_pci.c
index ae249c6..58b9e7f 100644
--- a/arch/powerpc/sysdev/tsi108_pci.c
+++ b/arch/powerpc/sysdev/tsi108_pci.c
@@ -211,7 +211,7 @@
 	has_address = (of_address_to_resource(dev, 0, &rsrc) == 0);
 
 	/* Get bus range if any */
-	bus_range = get_property(dev, "bus-range", &len);
+	bus_range = of_get_property(dev, "bus-range", &len);
 	if (bus_range == NULL || len < 2 * sizeof(int)) {
 		printk(KERN_WARNING "Can't get bus-range for %s, assume"
 		       " bus 0\n", dev->full_name);
diff --git a/arch/powerpc/sysdev/uic.c b/arch/powerpc/sysdev/uic.c
new file mode 100644
index 0000000..968fb40
--- /dev/null
+++ b/arch/powerpc/sysdev/uic.c
@@ -0,0 +1,342 @@
+/*
+ * arch/powerpc/sysdev/uic.c
+ *
+ * IBM PowerPC 4xx Universal Interrupt Controller
+ *
+ * Copyright 2007 David Gibson <dwg@au1.ibm.com>, IBM Corporation.
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/reboot.h>
+#include <linux/slab.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/sysdev.h>
+#include <linux/device.h>
+#include <linux/bootmem.h>
+#include <linux/spinlock.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/dcr.h>
+
+#define NR_UIC_INTS	32
+
+#define UIC_SR		0x0
+#define UIC_ER		0x2
+#define UIC_CR		0x3
+#define UIC_PR		0x4
+#define UIC_TR		0x5
+#define UIC_MSR		0x6
+#define UIC_VR		0x7
+#define UIC_VCR		0x8
+
+#define uic_irq_to_hw(virq)	(irq_map[virq].hwirq)
+
+struct uic *primary_uic;
+
+struct uic {
+	int index;
+	int dcrbase;
+
+	spinlock_t lock;
+
+	/* The remapper for this UIC */
+	struct irq_host	*irqhost;
+
+	/* For secondary UICs, the cascade interrupt's irqaction */
+	struct irqaction cascade;
+
+	/* The device node of the interrupt controller */
+	struct device_node *of_node;
+};
+
+static void uic_unmask_irq(unsigned int virq)
+{
+	struct uic *uic = get_irq_chip_data(virq);
+	unsigned int src = uic_irq_to_hw(virq);
+	unsigned long flags;
+	u32 er;
+
+	spin_lock_irqsave(&uic->lock, flags);
+	er = mfdcr(uic->dcrbase + UIC_ER);
+	er |= 1 << (31 - src);
+	mtdcr(uic->dcrbase + UIC_ER, er);
+	spin_unlock_irqrestore(&uic->lock, flags);
+}
+
+static void uic_mask_irq(unsigned int virq)
+{
+	struct uic *uic = get_irq_chip_data(virq);
+	unsigned int src = uic_irq_to_hw(virq);
+	unsigned long flags;
+	u32 er;
+
+	spin_lock_irqsave(&uic->lock, flags);
+	er = mfdcr(uic->dcrbase + UIC_ER);
+	er &= ~(1 << (31 - src));
+	mtdcr(uic->dcrbase + UIC_ER, er);
+	spin_unlock_irqrestore(&uic->lock, flags);
+}
+
+static void uic_ack_irq(unsigned int virq)
+{
+	struct uic *uic = get_irq_chip_data(virq);
+	unsigned int src = uic_irq_to_hw(virq);
+	unsigned long flags;
+
+	spin_lock_irqsave(&uic->lock, flags);
+	mtdcr(uic->dcrbase + UIC_SR, 1 << (31-src));
+	spin_unlock_irqrestore(&uic->lock, flags);
+}
+
+static int uic_set_irq_type(unsigned int virq, unsigned int flow_type)
+{
+	struct uic *uic = get_irq_chip_data(virq);
+	unsigned int src = uic_irq_to_hw(virq);
+	struct irq_desc *desc = get_irq_desc(virq);
+	unsigned long flags;
+	int trigger, polarity;
+	u32 tr, pr, mask;
+
+	switch (flow_type & IRQ_TYPE_SENSE_MASK) {
+	case IRQ_TYPE_NONE:
+		uic_mask_irq(virq);
+		return 0;
+
+	case IRQ_TYPE_EDGE_RISING:
+		trigger = 1; polarity = 1;
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		trigger = 1; polarity = 0;
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		trigger = 0; polarity = 1;
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		trigger = 0; polarity = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	mask = ~(1 << (31 - src));
+
+	spin_lock_irqsave(&uic->lock, flags);
+	tr = mfdcr(uic->dcrbase + UIC_TR);
+	pr = mfdcr(uic->dcrbase + UIC_PR);
+	tr = (tr & mask) | (trigger << (31-src));
+	pr = (pr & mask) | (polarity << (31-src));
+
+	mtdcr(uic->dcrbase + UIC_PR, pr);
+	mtdcr(uic->dcrbase + UIC_TR, tr);
+
+	desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
+	desc->status |= flow_type & IRQ_TYPE_SENSE_MASK;
+	if (trigger)
+		desc->status |= IRQ_LEVEL;
+
+	spin_unlock_irqrestore(&uic->lock, flags);
+
+	return 0;
+}
+
+static struct irq_chip uic_irq_chip = {
+	.typename	= " UIC  ",
+	.unmask		= uic_unmask_irq,
+	.mask		= uic_mask_irq,
+/* 	.mask_ack	= uic_mask_irq_and_ack, */
+	.ack		= uic_ack_irq,
+	.set_type	= uic_set_irq_type,
+};
+
+static int uic_host_match(struct irq_host *h, struct device_node *node)
+{
+	struct uic *uic = h->host_data;
+	return uic->of_node == node;
+}
+
+static int uic_host_map(struct irq_host *h, unsigned int virq,
+			irq_hw_number_t hw)
+{
+	struct uic *uic = h->host_data;
+
+	set_irq_chip_data(virq, uic);
+	/* Despite the name, handle_level_irq() works for both level
+	 * and edge irqs on UIC.  FIXME: check this is correct */
+	set_irq_chip_and_handler(virq, &uic_irq_chip, handle_level_irq);
+
+	/* Set default irq type */
+	set_irq_type(virq, IRQ_TYPE_NONE);
+
+	return 0;
+}
+
+static int uic_host_xlate(struct irq_host *h, struct device_node *ct,
+			  u32 *intspec, unsigned int intsize,
+			  irq_hw_number_t *out_hwirq, unsigned int *out_type)
+
+{
+	/* UIC intspecs must have 2 cells */
+	BUG_ON(intsize != 2);
+	*out_hwirq = intspec[0];
+	*out_type = intspec[1];
+	return 0;
+}
+
+static struct irq_host_ops uic_host_ops = {
+	.match	= uic_host_match,
+	.map	= uic_host_map,
+	.xlate	= uic_host_xlate,
+};
+
+irqreturn_t uic_cascade(int virq, void *data)
+{
+	struct uic *uic = data;
+	u32 msr;
+	int src;
+	int subvirq;
+
+	msr = mfdcr(uic->dcrbase + UIC_MSR);
+	src = 32 - ffs(msr);
+
+	subvirq = irq_linear_revmap(uic->irqhost, src);
+	generic_handle_irq(subvirq);
+
+	return IRQ_HANDLED;
+}
+
+static struct uic * __init uic_init_one(struct device_node *node)
+{
+	struct uic *uic;
+	const u32 *indexp, *dcrreg;
+	int len;
+
+	BUG_ON(! device_is_compatible(node, "ibm,uic"));
+
+	uic = alloc_bootmem(sizeof(*uic));
+	if (! uic)
+		return NULL; /* FIXME: panic? */
+
+	memset(uic, 0, sizeof(*uic));
+	spin_lock_init(&uic->lock);
+	uic->of_node = of_node_get(node);
+	indexp = of_get_property(node, "cell-index", &len);
+	if (!indexp || (len != sizeof(u32))) {
+		printk(KERN_ERR "uic: Device node %s has missing or invalid "
+		       "cell-index property\n", node->full_name);
+		return NULL;
+	}
+	uic->index = *indexp;
+
+	dcrreg = of_get_property(node, "dcr-reg", &len);
+	if (!dcrreg || (len != 2*sizeof(u32))) {
+		printk(KERN_ERR "uic: Device node %s has missing or invalid "
+		       "dcr-reg property\n", node->full_name);
+		return NULL;
+	}
+	uic->dcrbase = *dcrreg;
+
+	uic->irqhost = irq_alloc_host(IRQ_HOST_MAP_LINEAR, NR_UIC_INTS,
+				      &uic_host_ops, -1);
+	if (! uic->irqhost) {
+		of_node_put(node);
+		return NULL; /* FIXME: panic? */
+	}
+
+	uic->irqhost->host_data = uic;
+
+	/* Start with all interrupts disabled, level and non-critical */
+	mtdcr(uic->dcrbase + UIC_ER, 0);
+	mtdcr(uic->dcrbase + UIC_CR, 0);
+	mtdcr(uic->dcrbase + UIC_TR, 0);
+	/* Clear any pending interrupts, in case the firmware left some */
+	mtdcr(uic->dcrbase + UIC_SR, 0xffffffff);
+
+	printk ("UIC%d (%d IRQ sources) at DCR 0x%x\n", uic->index,
+		NR_UIC_INTS, uic->dcrbase);
+
+	return uic;
+}
+
+void __init uic_init_tree(void)
+{
+	struct device_node *np;
+	struct uic *uic;
+	const u32 *interrupts;
+
+	/* First locate and initialize the top-level UIC */
+
+	np = of_find_compatible_node(NULL, NULL, "ibm,uic");
+	while (np) {
+		interrupts = of_get_property(np, "interrupts", NULL);
+		if (! interrupts)
+			break;
+
+		np = of_find_compatible_node(np, NULL, "ibm,uic");
+	}
+
+	BUG_ON(!np); /* uic_init_tree() assumes there's a UIC as the
+		      * top-level interrupt controller */
+	primary_uic = uic_init_one(np);
+	if (! primary_uic)
+		panic("Unable to initialize primary UIC %s\n", np->full_name);
+
+	irq_set_default_host(primary_uic->irqhost);
+	of_node_put(np);
+
+	/* The scan again for cascaded UICs */
+	np = of_find_compatible_node(NULL, NULL, "ibm,uic");
+	while (np) {
+		interrupts = of_get_property(np, "interrupts", NULL);
+		if (interrupts) {
+			/* Secondary UIC */
+			int cascade_virq;
+			int ret;
+
+			uic = uic_init_one(np);
+			if (! uic)
+				panic("Unable to initialize a secondary UIC %s\n",
+				      np->full_name);
+
+			cascade_virq = irq_of_parse_and_map(np, 0);
+
+			uic->cascade.handler = uic_cascade;
+			uic->cascade.name = "UIC cascade";
+			uic->cascade.dev_id = uic;
+
+			ret = setup_irq(cascade_virq, &uic->cascade);
+			if (ret)
+				printk(KERN_ERR "Failed to setup_irq(%d) for "
+				       "UIC%d cascade\n", cascade_virq,
+				       uic->index);
+
+			/* FIXME: setup critical cascade?? */
+		}
+
+		np = of_find_compatible_node(np, NULL, "ibm,uic");
+	}
+}
+
+/* Return an interrupt vector or NO_IRQ if no interrupt is pending. */
+unsigned int uic_get_irq(void)
+{
+	u32 msr;
+	int src;
+
+	BUG_ON(! primary_uic);
+
+	msr = mfdcr(primary_uic->dcrbase + UIC_MSR);
+	src = 32 - ffs(msr);
+
+	return irq_linear_revmap(primary_uic->irqhost, src);
+}
diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c
index bf299b6..b481db1 100644
--- a/arch/powerpc/xmon/xmon.c
+++ b/arch/powerpc/xmon/xmon.c
@@ -330,18 +330,17 @@
 static int xmon_core(struct pt_regs *regs, int fromipi)
 {
 	int cmd = 0;
-	unsigned long msr;
 	struct bpt *bp;
 	long recurse_jmp[JMP_BUF_LEN];
 	unsigned long offset;
+	unsigned long flags;
 #ifdef CONFIG_SMP
 	int cpu;
 	int secondary;
 	unsigned long timeout;
 #endif
 
-	msr = mfmsr();
-	mtmsr(msr & ~MSR_EE);	/* disable interrupts */
+	local_irq_save(flags);
 
 	bp = in_breakpoint_table(regs->nip, &offset);
 	if (bp != NULL) {
@@ -516,7 +515,7 @@
 
 	insert_cpu_bpts();
 
-	mtmsr(msr);		/* restore interrupt enable */
+	local_irq_restore(flags);
 
 	return cmd != 'X' && cmd != EOF;
 }
@@ -1360,8 +1359,12 @@
 	if (is_warning_bug(bug))
 		return;
 
+#ifdef CONFIG_DEBUG_BUGVERBOSE
 	printf("kernel BUG at %s:%u!\n",
 	       bug->file, bug->line);
+#else
+	printf("kernel BUG at %p!\n", (void *)bug->bug_addr);
+#endif
 }
 
 void excprint(struct pt_regs *fp)
diff --git a/arch/ppc/8260_io/enet.c b/arch/ppc/8260_io/enet.c
index a6056c2..48ce84f 100644
--- a/arch/ppc/8260_io/enet.c
+++ b/arch/ppc/8260_io/enet.c
@@ -477,7 +477,6 @@
 			cep->stats.rx_dropped++;
 		}
 		else {
-			skb->dev = dev;
 			skb_put(skb,pkt_len-4);	/* Make room */
 			eth_copy_and_sum(skb,
 				(unsigned char *)__va(bdp->cbd_bufaddr),
diff --git a/arch/ppc/8260_io/fcc_enet.c b/arch/ppc/8260_io/fcc_enet.c
index 06b84c3..9db825f 100644
--- a/arch/ppc/8260_io/fcc_enet.c
+++ b/arch/ppc/8260_io/fcc_enet.c
@@ -734,7 +734,6 @@
 			cep->stats.rx_dropped++;
 		}
 		else {
-			skb->dev = dev;
 			skb_put(skb,pkt_len);	/* Make room */
 			eth_copy_and_sum(skb,
 				(unsigned char *)__va(bdp->cbd_bufaddr),
diff --git a/arch/ppc/8xx_io/Kconfig b/arch/ppc/8xx_io/Kconfig
index 57dacf9..c623e44 100644
--- a/arch/ppc/8xx_io/Kconfig
+++ b/arch/ppc/8xx_io/Kconfig
@@ -74,10 +74,6 @@
 	  Allocate large buffers for MPC8xx Ethernet. Increases throughput
 	  and decreases the likelihood of dropped packets, but costs memory.
 
-config HTDMSOUND
-	bool "Embedded Planet HIOX Audio"
-	depends on SOUND=y
-
 # This doesn't really belong here, but it is convenient to ask
 # 8xx specific questions.
 comment "Generic MPC8xx Options"
diff --git a/arch/ppc/8xx_io/Makefile b/arch/ppc/8xx_io/Makefile
index d876018..1051a06 100644
--- a/arch/ppc/8xx_io/Makefile
+++ b/arch/ppc/8xx_io/Makefile
@@ -7,4 +7,3 @@
 obj-$(CONFIG_FEC_ENET)	+= fec.o
 obj-$(CONFIG_SCC_ENET)	+= enet.o
 obj-$(CONFIG_UCODE_PATCH) += micropatch.o
-obj-$(CONFIG_HTDMSOUND) += cs4218_tdm.o
diff --git a/arch/ppc/8xx_io/cs4218.h b/arch/ppc/8xx_io/cs4218.h
deleted file mode 100644
index e5f9430..0000000
--- a/arch/ppc/8xx_io/cs4218.h
+++ /dev/null
@@ -1,166 +0,0 @@
-#ifndef _cs4218_h_
-/*
- *  Hacked version of linux/drivers/sound/dmasound/dmasound.h
- *
- *
- *  Minor numbers for the sound driver.
- *
- *  Unfortunately Creative called the codec chip of SB as a DSP. For this
- *  reason the /dev/dsp is reserved for digitized audio use. There is a
- *  device for true DSP processors but it will be called something else.
- *  In v3.0 it's /dev/sndproc but this could be a temporary solution.
- */
-#define _cs4218_h_
-
-#include <linux/types.h>
-
-#define SND_NDEVS	256	/* Number of supported devices */
-#define SND_DEV_CTL	0	/* Control port /dev/mixer */
-#define SND_DEV_SEQ	1	/* Sequencer output /dev/sequencer (FM
-				   synthesizer and MIDI output) */
-#define SND_DEV_MIDIN	2	/* Raw midi access */
-#define SND_DEV_DSP	3	/* Digitized voice /dev/dsp */
-#define SND_DEV_AUDIO	4	/* Sparc compatible /dev/audio */
-#define SND_DEV_DSP16	5	/* Like /dev/dsp but 16 bits/sample */
-#define SND_DEV_STATUS	6	/* /dev/sndstat */
-/* #7 not in use now. Was in 2.4. Free for use after v3.0. */
-#define SND_DEV_SEQ2	8	/* /dev/sequencer, level 2 interface */
-#define SND_DEV_SNDPROC 9	/* /dev/sndproc for programmable devices */
-#define SND_DEV_PSS	SND_DEV_SNDPROC
-
-/* switch on various prinks */
-#define DEBUG_DMASOUND 1
-
-#define MAX_AUDIO_DEV	5
-#define MAX_MIXER_DEV	4
-#define MAX_SYNTH_DEV	3
-#define MAX_MIDI_DEV	6
-#define MAX_TIMER_DEV	3
-
-#define MAX_CATCH_RADIUS	10
-
-#define le2be16(x)	(((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff))
-#define le2be16dbl(x)	(((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff))
-
-#define IOCTL_IN(arg, ret) \
-	do { int error = get_user(ret, (int *)(arg)); \
-		if (error) return error; \
-	} while (0)
-#define IOCTL_OUT(arg, ret)	ioctl_return((int *)(arg), ret)
-
-static inline int ioctl_return(int *addr, int value)
-{
-	return value < 0 ? value : put_user(value, addr);
-}
-
-#define HAS_RECORD
-
-    /*
-     *  Initialization
-     */
-
-/* description of the set-up applies to either hard or soft settings */
-
-typedef struct {
-    int format;		/* AFMT_* */
-    int stereo;		/* 0 = mono, 1 = stereo */
-    int size;		/* 8/16 bit*/
-    int speed;		/* speed */
-} SETTINGS;
-
-    /*
-     *  Machine definitions
-     */
-
-typedef struct {
-    const char *name;
-    const char *name2;
-    void (*open)(void);
-    void (*release)(void);
-    void *(*dma_alloc)(unsigned int, gfp_t);
-    void (*dma_free)(void *, unsigned int);
-    int (*irqinit)(void);
-#ifdef MODULE
-    void (*irqcleanup)(void);
-#endif
-    void (*init)(void);
-    void (*silence)(void);
-    int (*setFormat)(int);
-    int (*setVolume)(int);
-    int (*setBass)(int);
-    int (*setTreble)(int);
-    int (*setGain)(int);
-    void (*play)(void);
-    void (*record)(void);		/* optional */
-    void (*mixer_init)(void);		/* optional */
-    int (*mixer_ioctl)(u_int, u_long);	/* optional */
-    int (*write_sq_setup)(void);	/* optional */
-    int (*read_sq_setup)(void);		/* optional */
-    int (*sq_open)(mode_t);		/* optional */
-    int (*state_info)(char *, size_t);	/* optional */
-    void (*abort_read)(void);		/* optional */
-    int min_dsp_speed;
-    int max_dsp_speed;
-    int version ;
-    int hardware_afmts ;		/* OSS says we only return h'ware info */
-					/* when queried via SNDCTL_DSP_GETFMTS */
-    int capabilities ;		/* low-level reply to SNDCTL_DSP_GETCAPS */
-    SETTINGS default_hard ;	/* open() or init() should set something valid */
-    SETTINGS default_soft ;	/* you can make it look like old OSS, if you want to */
-} MACHINE;
-
-    /*
-     *  Low level stuff
-     */
-
-typedef struct {
-    ssize_t (*ct_ulaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
-    ssize_t (*ct_alaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
-    ssize_t (*ct_s8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
-    ssize_t (*ct_u8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
-    ssize_t (*ct_s16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
-    ssize_t (*ct_u16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
-    ssize_t (*ct_s16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
-    ssize_t (*ct_u16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
-} TRANS;
-
-
-    /*
-     * Sound queue stuff, the heart of the driver
-     */
-
-struct sound_queue {
-    /* buffers allocated for this queue */
-    int numBufs;		/* real limits on what the user can have */
-    int bufSize;		/* in bytes */
-    char **buffers;
-
-    /* current parameters */
-    int locked ;		/* params cannot be modified when != 0 */
-    int user_frags ;		/* user requests this many */
-    int user_frag_size ;	/* of this size */
-    int max_count;		/* actual # fragments <= numBufs */
-    int block_size;		/* internal block size in bytes */
-    int max_active;		/* in-use fragments <= max_count */
-
-    /* it shouldn't be necessary to declare any of these volatile */
-    int front, rear, count;
-    int rear_size;
-    /*
-     *	The use of the playing field depends on the hardware
-     *
-     *	Atari, PMac: The number of frames that are loaded/playing
-     *
-     *	Amiga: Bit 0 is set: a frame is loaded
-     *	       Bit 1 is set: a frame is playing
-     */
-    int active;
-    wait_queue_head_t action_queue, open_queue, sync_queue;
-    int open_mode;
-    int busy, syncing, xruns, died;
-};
-
-#define SLEEP(queue)		interruptible_sleep_on_timeout(&queue, HZ)
-#define WAKE_UP(queue)		(wake_up_interruptible(&queue))
-
-#endif /* _cs4218_h_ */
diff --git a/arch/ppc/8xx_io/cs4218_tdm.c b/arch/ppc/8xx_io/cs4218_tdm.c
deleted file mode 100644
index a956f28..0000000
--- a/arch/ppc/8xx_io/cs4218_tdm.c
+++ /dev/null
@@ -1,2833 +0,0 @@
-
-/* This is a modified version of linux/drivers/sound/dmasound.c to
- * support the CS4218 codec on the 8xx TDM port.  Thanks to everyone
- * that contributed to the dmasound software (which includes me :-).
- *
- * The CS4218 is configured in Mode 4, sub-mode 0.  This provides
- * left/right data only on the TDM port, as a 32-bit word, per frame
- * pulse.  The control of the CS4218 is provided by some other means,
- * like the SPI port.
- * Dan Malek (dmalek@jlc.net)
- */
-
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/major.h>
-#include <linux/fcntl.h>
-#include <linux/errno.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/sound.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-
-#include <asm/system.h>
-#include <asm/irq.h>
-#include <asm/pgtable.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-
-/* Should probably do something different with this path name.....
- * Actually, I should just stop using it...
- */
-#include "cs4218.h"
-#include <linux/soundcard.h>
-
-#include <asm/mpc8xx.h>
-#include <asm/8xx_immap.h>
-#include <asm/commproc.h>
-
-#define DMASND_CS4218		5
-
-#define MAX_CATCH_RADIUS	10
-#define MIN_BUFFERS		4
-#define MIN_BUFSIZE 		4
-#define MAX_BUFSIZE		128
-
-#define HAS_8BIT_TABLES
-
-static int sq_unit = -1;
-static int mixer_unit = -1;
-static int state_unit = -1;
-static int irq_installed = 0;
-static char **sound_buffers = NULL;
-static char **sound_read_buffers = NULL;
-
-static DEFINE_SPINLOCK(cs4218_lock);
-
-/* Local copies of things we put in the control register.  Output
- * volume, like most codecs is really attenuation.
- */
-static int cs4218_rate_index;
-
-/*
- * Stuff for outputting a beep.  The values range from -327 to +327
- * so we can multiply by an amplitude in the range 0..100 to get a
- * signed short value to put in the output buffer.
- */
-static short beep_wform[256] = {
-	0,	40,	79,	117,	153,	187,	218,	245,
-	269,	288,	304,	316,	323,	327,	327,	324,
-	318,	310,	299,	288,	275,	262,	249,	236,
-	224,	213,	204,	196,	190,	186,	183,	182,
-	182,	183,	186,	189,	192,	196,	200,	203,
-	206,	208,	209,	209,	209,	207,	204,	201,
-	197,	193,	188,	183,	179,	174,	170,	166,
-	163,	161,	160,	159,	159,	160,	161,	162,
-	164,	166,	168,	169,	171,	171,	171,	170,
-	169,	167,	163,	159,	155,	150,	144,	139,
-	133,	128,	122,	117,	113,	110,	107,	105,
-	103,	103,	103,	103,	104,	104,	105,	105,
-	105,	103,	101,	97,	92,	86,	78,	68,
-	58,	45,	32,	18,	3,	-11,	-26,	-41,
-	-55,	-68,	-79,	-88,	-95,	-100,	-102,	-102,
-	-99,	-93,	-85,	-75,	-62,	-48,	-33,	-16,
-	0,	16,	33,	48,	62,	75,	85,	93,
-	99,	102,	102,	100,	95,	88,	79,	68,
-	55,	41,	26,	11,	-3,	-18,	-32,	-45,
-	-58,	-68,	-78,	-86,	-92,	-97,	-101,	-103,
-	-105,	-105,	-105,	-104,	-104,	-103,	-103,	-103,
-	-103,	-105,	-107,	-110,	-113,	-117,	-122,	-128,
-	-133,	-139,	-144,	-150,	-155,	-159,	-163,	-167,
-	-169,	-170,	-171,	-171,	-171,	-169,	-168,	-166,
-	-164,	-162,	-161,	-160,	-159,	-159,	-160,	-161,
-	-163,	-166,	-170,	-174,	-179,	-183,	-188,	-193,
-	-197,	-201,	-204,	-207,	-209,	-209,	-209,	-208,
-	-206,	-203,	-200,	-196,	-192,	-189,	-186,	-183,
-	-182,	-182,	-183,	-186,	-190,	-196,	-204,	-213,
-	-224,	-236,	-249,	-262,	-275,	-288,	-299,	-310,
-	-318,	-324,	-327,	-327,	-323,	-316,	-304,	-288,
-	-269,	-245,	-218,	-187,	-153,	-117,	-79,	-40,
-};
-
-#define BEEP_SPEED	5	/* 22050 Hz sample rate */
-#define BEEP_BUFLEN	512
-#define BEEP_VOLUME	15	/* 0 - 100 */
-
-static int beep_volume = BEEP_VOLUME;
-static int beep_playing = 0;
-static int beep_state = 0;
-static short *beep_buf;
-static void (*orig_mksound)(unsigned int, unsigned int);
-
-/* This is found someplace else......I guess in the keyboard driver
- * we don't include.
- */
-static void (*kd_mksound)(unsigned int, unsigned int);
-
-static int catchRadius = 0;
-static int numBufs = 4, bufSize = 32;
-static int numReadBufs = 4, readbufSize = 32;
-
-
-/* TDM/Serial transmit and receive buffer descriptors.
-*/
-static volatile cbd_t	*rx_base, *rx_cur, *tx_base, *tx_cur;
-
-module_param(catchRadius, int, 0);
-module_param(numBufs, int, 0);
-module_param(bufSize, int, 0);
-module_param(numreadBufs, int, 0);
-module_param(readbufSize, int, 0);
-
-#define arraysize(x)	(sizeof(x)/sizeof(*(x)))
-#define le2be16(x)	(((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff))
-#define le2be16dbl(x)	(((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff))
-
-#define IOCTL_IN(arg, ret) \
-	do { int error = get_user(ret, (int *)(arg)); \
-		if (error) return error; \
-	} while (0)
-#define IOCTL_OUT(arg, ret)	ioctl_return((int *)(arg), ret)
-
-/* CS4218 serial port control in mode 4.
-*/
-#define CS_INTMASK	((uint)0x40000000)
-#define CS_DO1		((uint)0x20000000)
-#define CS_LATTEN	((uint)0x1f000000)
-#define CS_RATTEN	((uint)0x00f80000)
-#define CS_MUTE		((uint)0x00040000)
-#define CS_ISL		((uint)0x00020000)
-#define CS_ISR		((uint)0x00010000)
-#define CS_LGAIN	((uint)0x0000f000)
-#define CS_RGAIN	((uint)0x00000f00)
-
-#define CS_LATTEN_SET(X)	(((X) & 0x1f) << 24)
-#define CS_RATTEN_SET(X)	(((X) & 0x1f) << 19)
-#define CS_LGAIN_SET(X)		(((X) & 0x0f) << 12)
-#define CS_RGAIN_SET(X)		(((X) & 0x0f) << 8)
-
-#define CS_LATTEN_GET(X)	(((X) >> 24) & 0x1f)
-#define CS_RATTEN_GET(X)	(((X) >> 19) & 0x1f)
-#define CS_LGAIN_GET(X)		(((X) >> 12) & 0x0f)
-#define CS_RGAIN_GET(X)		(((X) >> 8) & 0x0f)
-
-/* The control register is effectively write only.  We have to keep a copy
- * of what we write.
- */
-static	uint	cs4218_control;
-
-/* A place to store expanding information.
-*/
-static int	expand_bal;
-static int	expand_data;
-
-/* Since I can't make the microcode patch work for the SPI, I just
- * clock the bits using software.
- */
-static	void	sw_spi_init(void);
-static	void	sw_spi_io(u_char *obuf, u_char *ibuf, uint bcnt);
-static	uint	cs4218_ctl_write(uint ctlreg);
-
-/*** Some low level helpers **************************************************/
-
-/* 16 bit mu-law */
-
-static short ulaw2dma16[] = {
-	-32124,	-31100,	-30076,	-29052,	-28028,	-27004,	-25980,	-24956,
-	-23932,	-22908,	-21884,	-20860,	-19836,	-18812,	-17788,	-16764,
-	-15996,	-15484,	-14972,	-14460,	-13948,	-13436,	-12924,	-12412,
-	-11900,	-11388,	-10876,	-10364,	-9852,	-9340,	-8828,	-8316,
-	-7932,	-7676,	-7420,	-7164,	-6908,	-6652,	-6396,	-6140,
-	-5884,	-5628,	-5372,	-5116,	-4860,	-4604,	-4348,	-4092,
-	-3900,	-3772,	-3644,	-3516,	-3388,	-3260,	-3132,	-3004,
-	-2876,	-2748,	-2620,	-2492,	-2364,	-2236,	-2108,	-1980,
-	-1884,	-1820,	-1756,	-1692,	-1628,	-1564,	-1500,	-1436,
-	-1372,	-1308,	-1244,	-1180,	-1116,	-1052,	-988,	-924,
-	-876,	-844,	-812,	-780,	-748,	-716,	-684,	-652,
-	-620,	-588,	-556,	-524,	-492,	-460,	-428,	-396,
-	-372,	-356,	-340,	-324,	-308,	-292,	-276,	-260,
-	-244,	-228,	-212,	-196,	-180,	-164,	-148,	-132,
-	-120,	-112,	-104,	-96,	-88,	-80,	-72,	-64,
-	-56,	-48,	-40,	-32,	-24,	-16,	-8,	0,
-	32124,	31100,	30076,	29052,	28028,	27004,	25980,	24956,
-	23932,	22908,	21884,	20860,	19836,	18812,	17788,	16764,
-	15996,	15484,	14972,	14460,	13948,	13436,	12924,	12412,
-	11900,	11388,	10876,	10364,	9852,	9340,	8828,	8316,
-	7932,	7676,	7420,	7164,	6908,	6652,	6396,	6140,
-	5884,	5628,	5372,	5116,	4860,	4604,	4348,	4092,
-	3900,	3772,	3644,	3516,	3388,	3260,	3132,	3004,
-	2876,	2748,	2620,	2492,	2364,	2236,	2108,	1980,
-	1884,	1820,	1756,	1692,	1628,	1564,	1500,	1436,
-	1372,	1308,	1244,	1180,	1116,	1052,	988,	924,
-	876,	844,	812,	780,	748,	716,	684,	652,
-	620,	588,	556,	524,	492,	460,	428,	396,
-	372,	356,	340,	324,	308,	292,	276,	260,
-	244,	228,	212,	196,	180,	164,	148,	132,
-	120,	112,	104,	96,	88,	80,	72,	64,
-	56,	48,	40,	32,	24,	16,	8,	0,
-};
-
-/* 16 bit A-law */
-
-static short alaw2dma16[] = {
-	-5504,	-5248,	-6016,	-5760,	-4480,	-4224,	-4992,	-4736,
-	-7552,	-7296,	-8064,	-7808,	-6528,	-6272,	-7040,	-6784,
-	-2752,	-2624,	-3008,	-2880,	-2240,	-2112,	-2496,	-2368,
-	-3776,	-3648,	-4032,	-3904,	-3264,	-3136,	-3520,	-3392,
-	-22016,	-20992,	-24064,	-23040,	-17920,	-16896,	-19968,	-18944,
-	-30208,	-29184,	-32256,	-31232,	-26112,	-25088,	-28160,	-27136,
-	-11008,	-10496,	-12032,	-11520,	-8960,	-8448,	-9984,	-9472,
-	-15104,	-14592,	-16128,	-15616,	-13056,	-12544,	-14080,	-13568,
-	-344,	-328,	-376,	-360,	-280,	-264,	-312,	-296,
-	-472,	-456,	-504,	-488,	-408,	-392,	-440,	-424,
-	-88,	-72,	-120,	-104,	-24,	-8,	-56,	-40,
-	-216,	-200,	-248,	-232,	-152,	-136,	-184,	-168,
-	-1376,	-1312,	-1504,	-1440,	-1120,	-1056,	-1248,	-1184,
-	-1888,	-1824,	-2016,	-1952,	-1632,	-1568,	-1760,	-1696,
-	-688,	-656,	-752,	-720,	-560,	-528,	-624,	-592,
-	-944,	-912,	-1008,	-976,	-816,	-784,	-880,	-848,
-	5504,	5248,	6016,	5760,	4480,	4224,	4992,	4736,
-	7552,	7296,	8064,	7808,	6528,	6272,	7040,	6784,
-	2752,	2624,	3008,	2880,	2240,	2112,	2496,	2368,
-	3776,	3648,	4032,	3904,	3264,	3136,	3520,	3392,
-	22016,	20992,	24064,	23040,	17920,	16896,	19968,	18944,
-	30208,	29184,	32256,	31232,	26112,	25088,	28160,	27136,
-	11008,	10496,	12032,	11520,	8960,	8448,	9984,	9472,
-	15104,	14592,	16128,	15616,	13056,	12544,	14080,	13568,
-	344,	328,	376,	360,	280,	264,	312,	296,
-	472,	456,	504,	488,	408,	392,	440,	424,
-	88,	72,	120,	104,	24,	8,	56,	40,
-	216,	200,	248,	232,	152,	136,	184,	168,
-	1376,	1312,	1504,	1440,	1120,	1056,	1248,	1184,
-	1888,	1824,	2016,	1952,	1632,	1568,	1760,	1696,
-	688,	656,	752,	720,	560,	528,	624,	592,
-	944,	912,	1008,	976,	816,	784,	880,	848,
-};
-
-
-/*** Translations ************************************************************/
-
-
-static ssize_t cs4218_ct_law(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-static ssize_t cs4218_ct_s8(const u_char *userPtr, size_t userCount,
-			  u_char frame[], ssize_t *frameUsed,
-			  ssize_t frameLeft);
-static ssize_t cs4218_ct_u8(const u_char *userPtr, size_t userCount,
-			  u_char frame[], ssize_t *frameUsed,
-			  ssize_t frameLeft);
-static ssize_t cs4218_ct_s16(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-static ssize_t cs4218_ct_u16(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-static ssize_t cs4218_ctx_law(const u_char *userPtr, size_t userCount,
-			    u_char frame[], ssize_t *frameUsed,
-			    ssize_t frameLeft);
-static ssize_t cs4218_ctx_s8(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-static ssize_t cs4218_ctx_u8(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-static ssize_t cs4218_ctx_s16(const u_char *userPtr, size_t userCount,
-			    u_char frame[], ssize_t *frameUsed,
-			    ssize_t frameLeft);
-static ssize_t cs4218_ctx_u16(const u_char *userPtr, size_t userCount,
-			    u_char frame[], ssize_t *frameUsed,
-			    ssize_t frameLeft);
-static ssize_t cs4218_ct_s16_read(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-static ssize_t cs4218_ct_u16_read(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-
-
-/*** Low level stuff *********************************************************/
-
-struct cs_sound_settings {
-	MACHINE mach;		/* machine dependent things */
-	SETTINGS hard;		/* hardware settings */
-	SETTINGS soft;		/* software settings */
-	SETTINGS dsp;		/* /dev/dsp default settings */
-	TRANS *trans_write;	/* supported translations for playback */
-	TRANS *trans_read;	/* supported translations for record */
-	int volume_left;	/* volume (range is machine dependent) */
-	int volume_right;
-	int bass;		/* tone (range is machine dependent) */
-	int treble;
-	int gain;
-	int minDev;		/* minor device number currently open */
-};
-
-static struct cs_sound_settings sound;
-
-static void *CS_Alloc(unsigned int size, gfp_t flags);
-static void CS_Free(void *ptr, unsigned int size);
-static int CS_IrqInit(void);
-#ifdef MODULE
-static void CS_IrqCleanup(void);
-#endif /* MODULE */
-static void CS_Silence(void);
-static void CS_Init(void);
-static void CS_Play(void);
-static void CS_Record(void);
-static int CS_SetFormat(int format);
-static int CS_SetVolume(int volume);
-static void cs4218_tdm_tx_intr(void *devid);
-static void cs4218_tdm_rx_intr(void *devid);
-static void cs4218_intr(void *devid);
-static int cs_get_volume(uint reg);
-static int cs_volume_setter(int volume, int mute);
-static int cs_get_gain(uint reg);
-static int cs_set_gain(int gain);
-static void cs_mksound(unsigned int hz, unsigned int ticks);
-static void cs_nosound(unsigned long xx);
-
-/*** Mid level stuff *********************************************************/
-
-
-static void sound_silence(void);
-static void sound_init(void);
-static int sound_set_format(int format);
-static int sound_set_speed(int speed);
-static int sound_set_stereo(int stereo);
-static int sound_set_volume(int volume);
-
-static ssize_t sound_copy_translate(const u_char *userPtr,
-				    size_t userCount,
-				    u_char frame[], ssize_t *frameUsed,
-				    ssize_t frameLeft);
-static ssize_t sound_copy_translate_read(const u_char *userPtr,
-				    size_t userCount,
-				    u_char frame[], ssize_t *frameUsed,
-				    ssize_t frameLeft);
-
-
-/*
- * /dev/mixer abstraction
- */
-
-struct sound_mixer {
-    int busy;
-    int modify_counter;
-};
-
-static struct sound_mixer mixer;
-
-static struct sound_queue sq;
-static struct sound_queue read_sq;
-
-#define sq_block_address(i)	(sq.buffers[i])
-#define SIGNAL_RECEIVED	(signal_pending(current))
-#define NON_BLOCKING(open_mode)	(open_mode & O_NONBLOCK)
-#define ONE_SECOND	HZ	/* in jiffies (100ths of a second) */
-#define NO_TIME_LIMIT	0xffffffff
-
-/*
- * /dev/sndstat
- */
-
-struct sound_state {
-	int busy;
-	char buf[512];
-	int len, ptr;
-};
-
-static struct sound_state state;
-
-/*** Common stuff ********************************************************/
-
-static long long sound_lseek(struct file *file, long long offset, int orig);
-
-/*** Config & Setup **********************************************************/
-
-void dmasound_setup(char *str, int *ints);
-
-/*** Translations ************************************************************/
-
-
-/* ++TeSche: radically changed for new expanding purposes...
- *
- * These two routines now deal with copying/expanding/translating the samples
- * from user space into our buffer at the right frequency. They take care about
- * how much data there's actually to read, how much buffer space there is and
- * to convert samples into the right frequency/encoding. They will only work on
- * complete samples so it may happen they leave some bytes in the input stream
- * if the user didn't write a multiple of the current sample size. They both
- * return the number of bytes they've used from both streams so you may detect
- * such a situation. Luckily all programs should be able to cope with that.
- *
- * I think I've optimized anything as far as one can do in plain C, all
- * variables should fit in registers and the loops are really short. There's
- * one loop for every possible situation. Writing a more generalized and thus
- * parameterized loop would only produce slower code. Feel free to optimize
- * this in assembler if you like. :)
- *
- * I think these routines belong here because they're not yet really hardware
- * independent, especially the fact that the Falcon can play 16bit samples
- * only in stereo is hardcoded in both of them!
- *
- * ++geert: split in even more functions (one per format)
- */
-
-static ssize_t cs4218_ct_law(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	short *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma16: alaw2dma16;
-	ssize_t count, used;
-	short *p = (short *) &frame[*frameUsed];
-	int val, stereo = sound.soft.stereo;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	used = count = min(userCount, frameLeft);
-	while (count > 0) {
-		u_char data;
-		if (get_user(data, userPtr++))
-			return -EFAULT;
-		val = table[data];
-		*p++ = val;
-		if (stereo) {
-			if (get_user(data, userPtr++))
-				return -EFAULT;
-			val = table[data];
-		}
-		*p++ = val;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 2: used;
-}
-
-
-static ssize_t cs4218_ct_s8(const u_char *userPtr, size_t userCount,
-			  u_char frame[], ssize_t *frameUsed,
-			  ssize_t frameLeft)
-{
-	ssize_t count, used;
-	short *p = (short *) &frame[*frameUsed];
-	int val, stereo = sound.soft.stereo;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	used = count = min(userCount, frameLeft);
-	while (count > 0) {
-		u_char data;
-		if (get_user(data, userPtr++))
-			return -EFAULT;
-		val = data << 8;
-		*p++ = val;
-		if (stereo) {
-			if (get_user(data, userPtr++))
-				return -EFAULT;
-			val = data << 8;
-		}
-		*p++ = val;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 2: used;
-}
-
-
-static ssize_t cs4218_ct_u8(const u_char *userPtr, size_t userCount,
-			  u_char frame[], ssize_t *frameUsed,
-			  ssize_t frameLeft)
-{
-	ssize_t count, used;
-	short *p = (short *) &frame[*frameUsed];
-	int val, stereo = sound.soft.stereo;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	used = count = min(userCount, frameLeft);
-	while (count > 0) {
-		u_char data;
-		if (get_user(data, userPtr++))
-			return -EFAULT;
-		val = (data ^ 0x80) << 8;
-		*p++ = val;
-		if (stereo) {
-			if (get_user(data, userPtr++))
-				return -EFAULT;
-			val = (data ^ 0x80) << 8;
-		}
-		*p++ = val;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 2: used;
-}
-
-
-/* This is the default format of the codec.  Signed, 16-bit stereo
- * generated by an application shouldn't have to be copied at all.
- * We should just get the phsical address of the buffers and update
- * the TDM BDs directly.
- */
-static ssize_t cs4218_ct_s16(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	ssize_t count, used;
-	int stereo = sound.soft.stereo;
-	short *fp = (short *) &frame[*frameUsed];
-
-	frameLeft >>= 2;
-	userCount >>= (stereo? 2: 1);
-	used = count = min(userCount, frameLeft);
-	if (!stereo) {
-		short *up = (short *) userPtr;
-		while (count > 0) {
-			short data;
-			if (get_user(data, up++))
-				return -EFAULT;
-			*fp++ = data;
-			*fp++ = data;
-			count--;
-		}
-	} else {
-		if (copy_from_user(fp, userPtr, count * 4))
-			return -EFAULT;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 4: used * 2;
-}
-
-static ssize_t cs4218_ct_u16(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	ssize_t count, used;
-	int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
-	int stereo = sound.soft.stereo;
-	short *fp = (short *) &frame[*frameUsed];
-	short *up = (short *) userPtr;
-
-	frameLeft >>= 2;
-	userCount >>= (stereo? 2: 1);
-	used = count = min(userCount, frameLeft);
-	while (count > 0) {
-		int data;
-		if (get_user(data, up++))
-			return -EFAULT;
-		data ^= mask;
-		*fp++ = data;
-		if (stereo) {
-			if (get_user(data, up++))
-				return -EFAULT;
-			data ^= mask;
-		}
-		*fp++ = data;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 4: used * 2;
-}
-
-
-static ssize_t cs4218_ctx_law(const u_char *userPtr, size_t userCount,
-			    u_char frame[], ssize_t *frameUsed,
-			    ssize_t frameLeft)
-{
-	unsigned short *table = (unsigned short *)
-		(sound.soft.format == AFMT_MU_LAW ? ulaw2dma16: alaw2dma16);
-	unsigned int data = expand_data;
-	unsigned int *p = (unsigned int *) &frame[*frameUsed];
-	int bal = expand_bal;
-	int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
-	int utotal, ftotal;
-	int stereo = sound.soft.stereo;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	ftotal = frameLeft;
-	utotal = userCount;
-	while (frameLeft) {
-		u_char c;
-		if (bal < 0) {
-			if (userCount == 0)
-				break;
-			if (get_user(c, userPtr++))
-				return -EFAULT;
-			data = table[c];
-			if (stereo) {
-				if (get_user(c, userPtr++))
-					return -EFAULT;
-				data = (data << 16) + table[c];
-			} else
-				data = (data << 16) + data;
-			userCount--;
-			bal += hSpeed;
-		}
-		*p++ = data;
-		frameLeft--;
-		bal -= sSpeed;
-	}
-	expand_bal = bal;
-	expand_data = data;
-	*frameUsed += (ftotal - frameLeft) * 4;
-	utotal -= userCount;
-	return stereo? utotal * 2: utotal;
-}
-
-
-static ssize_t cs4218_ctx_s8(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	unsigned int *p = (unsigned int *) &frame[*frameUsed];
-	unsigned int data = expand_data;
-	int bal = expand_bal;
-	int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
-	int stereo = sound.soft.stereo;
-	int utotal, ftotal;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	ftotal = frameLeft;
-	utotal = userCount;
-	while (frameLeft) {
-		u_char c;
-		if (bal < 0) {
-			if (userCount == 0)
-				break;
-			if (get_user(c, userPtr++))
-				return -EFAULT;
-			data = c << 8;
-			if (stereo) {
-				if (get_user(c, userPtr++))
-					return -EFAULT;
-				data = (data << 16) + (c << 8);
-			} else
-				data = (data << 16) + data;
-			userCount--;
-			bal += hSpeed;
-		}
-		*p++ = data;
-		frameLeft--;
-		bal -= sSpeed;
-	}
-	expand_bal = bal;
-	expand_data = data;
-	*frameUsed += (ftotal - frameLeft) * 4;
-	utotal -= userCount;
-	return stereo? utotal * 2: utotal;
-}
-
-
-static ssize_t cs4218_ctx_u8(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	unsigned int *p = (unsigned int *) &frame[*frameUsed];
-	unsigned int data = expand_data;
-	int bal = expand_bal;
-	int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
-	int stereo = sound.soft.stereo;
-	int utotal, ftotal;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	ftotal = frameLeft;
-	utotal = userCount;
-	while (frameLeft) {
-		u_char c;
-		if (bal < 0) {
-			if (userCount == 0)
-				break;
-			if (get_user(c, userPtr++))
-				return -EFAULT;
-			data = (c ^ 0x80) << 8;
-			if (stereo) {
-				if (get_user(c, userPtr++))
-					return -EFAULT;
-				data = (data << 16) + ((c ^ 0x80) << 8);
-			} else
-				data = (data << 16) + data;
-			userCount--;
-			bal += hSpeed;
-		}
-		*p++ = data;
-		frameLeft--;
-		bal -= sSpeed;
-	}
-	expand_bal = bal;
-	expand_data = data;
-	*frameUsed += (ftotal - frameLeft) * 4;
-	utotal -= userCount;
-	return stereo? utotal * 2: utotal;
-}
-
-
-static ssize_t cs4218_ctx_s16(const u_char *userPtr, size_t userCount,
-			    u_char frame[], ssize_t *frameUsed,
-			    ssize_t frameLeft)
-{
-	unsigned int *p = (unsigned int *) &frame[*frameUsed];
-	unsigned int data = expand_data;
-	unsigned short *up = (unsigned short *) userPtr;
-	int bal = expand_bal;
-	int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
-	int stereo = sound.soft.stereo;
-	int utotal, ftotal;
-
-	frameLeft >>= 2;
-	userCount >>= (stereo? 2: 1);
-	ftotal = frameLeft;
-	utotal = userCount;
-	while (frameLeft) {
-		unsigned short c;
-		if (bal < 0) {
-			if (userCount == 0)
-				break;
-			if (get_user(data, up++))
-				return -EFAULT;
-			if (stereo) {
-				if (get_user(c, up++))
-					return -EFAULT;
-				data = (data << 16) + c;
-			} else
-				data = (data << 16) + data;
-			userCount--;
-			bal += hSpeed;
-		}
-		*p++ = data;
-		frameLeft--;
-		bal -= sSpeed;
-	}
-	expand_bal = bal;
-	expand_data = data;
-	*frameUsed += (ftotal - frameLeft) * 4;
-	utotal -= userCount;
-	return stereo? utotal * 4: utotal * 2;
-}
-
-
-static ssize_t cs4218_ctx_u16(const u_char *userPtr, size_t userCount,
-			    u_char frame[], ssize_t *frameUsed,
-			    ssize_t frameLeft)
-{
-	int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
-	unsigned int *p = (unsigned int *) &frame[*frameUsed];
-	unsigned int data = expand_data;
-	unsigned short *up = (unsigned short *) userPtr;
-	int bal = expand_bal;
-	int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
-	int stereo = sound.soft.stereo;
-	int utotal, ftotal;
-
-	frameLeft >>= 2;
-	userCount >>= (stereo? 2: 1);
-	ftotal = frameLeft;
-	utotal = userCount;
-	while (frameLeft) {
-		unsigned short c;
-		if (bal < 0) {
-			if (userCount == 0)
-				break;
-			if (get_user(data, up++))
-				return -EFAULT;
-			data ^= mask;
-			if (stereo) {
-				if (get_user(c, up++))
-					return -EFAULT;
-				data = (data << 16) + (c ^ mask);
-			} else
-				data = (data << 16) + data;
-			userCount--;
-			bal += hSpeed;
-		}
-		*p++ = data;
-		frameLeft--;
-		bal -= sSpeed;
-	}
-	expand_bal = bal;
-	expand_data = data;
-	*frameUsed += (ftotal - frameLeft) * 4;
-	utotal -= userCount;
-	return stereo? utotal * 4: utotal * 2;
-}
-
-static ssize_t cs4218_ct_s8_read(const u_char *userPtr, size_t userCount,
-			  u_char frame[], ssize_t *frameUsed,
-			  ssize_t frameLeft)
-{
-	ssize_t count, used;
-	short *p = (short *) &frame[*frameUsed];
-	int val, stereo = sound.soft.stereo;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	used = count = min(userCount, frameLeft);
-	while (count > 0) {
-		u_char data;
-
-		val = *p++;
-		data = val >> 8;
-		if (put_user(data, (u_char *)userPtr++))
-			return -EFAULT;
-		if (stereo) {
-			val = *p;
-			data = val >> 8;
-			if (put_user(data, (u_char *)userPtr++))
-				return -EFAULT;
-		}
-		p++;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 2: used;
-}
-
-
-static ssize_t cs4218_ct_u8_read(const u_char *userPtr, size_t userCount,
-			  u_char frame[], ssize_t *frameUsed,
-			  ssize_t frameLeft)
-{
-	ssize_t count, used;
-	short *p = (short *) &frame[*frameUsed];
-	int val, stereo = sound.soft.stereo;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	used = count = min(userCount, frameLeft);
-	while (count > 0) {
-		u_char data;
-
-		val = *p++;
-		data = (val >> 8) ^ 0x80;
-		if (put_user(data, (u_char *)userPtr++))
-			return -EFAULT;
-		if (stereo) {
-			val = *p;
-			data = (val >> 8) ^ 0x80;
-			if (put_user(data, (u_char *)userPtr++))
-				return -EFAULT;
-		}
-		p++;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 2: used;
-}
-
-
-static ssize_t cs4218_ct_s16_read(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	ssize_t count, used;
-	int stereo = sound.soft.stereo;
-	short *fp = (short *) &frame[*frameUsed];
-
-	frameLeft >>= 2;
-	userCount >>= (stereo? 2: 1);
-	used = count = min(userCount, frameLeft);
-	if (!stereo) {
-		short *up = (short *) userPtr;
-		while (count > 0) {
-			short data;
-			data = *fp;
-			if (put_user(data, up++))
-				return -EFAULT;
-			fp+=2;
-			count--;
-		}
-	} else {
-		if (copy_to_user((u_char *)userPtr, fp, count * 4))
-			return -EFAULT;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 4: used * 2;
-}
-
-static ssize_t cs4218_ct_u16_read(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	ssize_t count, used;
-	int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
-	int stereo = sound.soft.stereo;
-	short *fp = (short *) &frame[*frameUsed];
-	short *up = (short *) userPtr;
-
-	frameLeft >>= 2;
-	userCount >>= (stereo? 2: 1);
-	used = count = min(userCount, frameLeft);
-	while (count > 0) {
-		int data;
-
-		data = *fp++;
-		data ^= mask;
-		if (put_user(data, up++))
-			return -EFAULT;
-		if (stereo) {
-			data = *fp;
-			data ^= mask;
-			if (put_user(data, up++))
-				return -EFAULT;
-		}
-		fp++;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 4: used * 2;
-}
-
-static TRANS transCSNormal = {
-	cs4218_ct_law, cs4218_ct_law, cs4218_ct_s8, cs4218_ct_u8,
-	cs4218_ct_s16, cs4218_ct_u16, cs4218_ct_s16, cs4218_ct_u16
-};
-
-static TRANS transCSExpand = {
-	cs4218_ctx_law, cs4218_ctx_law, cs4218_ctx_s8, cs4218_ctx_u8,
-	cs4218_ctx_s16, cs4218_ctx_u16, cs4218_ctx_s16, cs4218_ctx_u16
-};
-
-static TRANS transCSNormalRead = {
-	NULL, NULL, cs4218_ct_s8_read, cs4218_ct_u8_read,
-	cs4218_ct_s16_read, cs4218_ct_u16_read,
-	cs4218_ct_s16_read, cs4218_ct_u16_read
-};
-
-/*** Low level stuff *********************************************************/
-
-static void *CS_Alloc(unsigned int size, gfp_t flags)
-{
-	int	order;
-
-	size >>= 13;
-	for (order=0; order < 5; order++) {
-		if (size == 0)
-			break;
-		size >>= 1;
-	}
-	return (void *)__get_free_pages(flags, order);
-}
-
-static void CS_Free(void *ptr, unsigned int size)
-{
-	int	order;
-
-	size >>= 13;
-	for (order=0; order < 5; order++) {
-		if (size == 0)
-			break;
-		size >>= 1;
-	}
-	free_pages((ulong)ptr, order);
-}
-
-static int __init CS_IrqInit(void)
-{
-	cpm_install_handler(CPMVEC_SMC2, cs4218_intr, NULL);
-	return 1;
-}
-
-#ifdef MODULE
-static void CS_IrqCleanup(void)
-{
-	volatile smc_t		*sp;
-	volatile cpm8xx_t	*cp;
-
-	/* First disable transmitter and receiver.
-	*/
-	sp = &cpmp->cp_smc[1];
-	sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
-
-	/* And now shut down the SMC.
-	*/
-	cp = cpmp;	/* Get pointer to Communication Processor */
-	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2,
-				CPM_CR_STOP_TX) | CPM_CR_FLG;
-	while (cp->cp_cpcr & CPM_CR_FLG);
-
-	/* Release the interrupt handler.
-	*/
-	cpm_free_handler(CPMVEC_SMC2);
-
-	kfree(beep_buf);
-	kd_mksound = orig_mksound;
-}
-#endif /* MODULE */
-
-static void CS_Silence(void)
-{
-	volatile smc_t		*sp;
-
-	/* Disable transmitter.
-	*/
-	sp = &cpmp->cp_smc[1];
-	sp->smc_smcmr &= ~SMCMR_TEN;
-}
-
-/* Frequencies depend upon external oscillator.  There are two
- * choices, 12.288 and 11.2896 MHz.  The RPCG audio supports both through
- * and external control register selection bit.
- */
-static int cs4218_freqs[] = {
-    /* 12.288  11.2896  */
-	48000, 44100,
-	32000, 29400,
-	24000, 22050,
-	19200, 17640,
-	16000, 14700,
-	12000, 11025,
-	 9600,  8820,
-	 8000,  7350
-};
-
-static void CS_Init(void)
-{
-	int i, tolerance;
-
-	switch (sound.soft.format) {
-	case AFMT_S16_LE:
-	case AFMT_U16_LE:
-		sound.hard.format = AFMT_S16_LE;
-		break;
-	default:
-		sound.hard.format = AFMT_S16_BE;
-		break;
-	}
-	sound.hard.stereo = 1;
-	sound.hard.size = 16;
-
-	/*
-	 * If we have a sample rate which is within catchRadius percent
-	 * of the requested value, we don't have to expand the samples.
-	 * Otherwise choose the next higher rate.
-	 */
-	i = (sizeof(cs4218_freqs) / sizeof(int));
-	do {
-		tolerance = catchRadius * cs4218_freqs[--i] / 100;
-	} while (sound.soft.speed > cs4218_freqs[i] + tolerance && i > 0);
-	if (sound.soft.speed >= cs4218_freqs[i] - tolerance)
-		sound.trans_write = &transCSNormal;
-	else
-		sound.trans_write = &transCSExpand;
-	sound.trans_read = &transCSNormalRead;
-	sound.hard.speed = cs4218_freqs[i];
-	cs4218_rate_index = i;
-
-	/* The CS4218 has seven selectable clock dividers for the sample
-	 * clock.  The HIOX then provides one of two external rates.
-	 * An even numbered frequency table index uses the high external
-	 * clock rate.
-	 */
-	*(uint *)HIOX_CSR4_ADDR &= ~(HIOX_CSR4_AUDCLKHI | HIOX_CSR4_AUDCLKSEL);
-	if ((i & 1) == 0)
-		*(uint *)HIOX_CSR4_ADDR |= HIOX_CSR4_AUDCLKHI;
-	i >>= 1;
-	*(uint *)HIOX_CSR4_ADDR |= (i & HIOX_CSR4_AUDCLKSEL);
-
-	expand_bal = -sound.soft.speed;
-}
-
-static int CS_SetFormat(int format)
-{
-	int size;
-
-	switch (format) {
-	case AFMT_QUERY:
-		return sound.soft.format;
-	case AFMT_MU_LAW:
-	case AFMT_A_LAW:
-	case AFMT_U8:
-	case AFMT_S8:
-		size = 8;
-		break;
-	case AFMT_S16_BE:
-	case AFMT_U16_BE:
-	case AFMT_S16_LE:
-	case AFMT_U16_LE:
-		size = 16;
-		break;
-	default: /* :-) */
-		printk(KERN_ERR "dmasound: unknown format 0x%x, using AFMT_U8\n",
-		       format);
-		size = 8;
-		format = AFMT_U8;
-	}
-
-	sound.soft.format = format;
-	sound.soft.size = size;
-	if (sound.minDev == SND_DEV_DSP) {
-		sound.dsp.format = format;
-		sound.dsp.size = size;
-	}
-
-	CS_Init();
-
-	return format;
-}
-
-/* Volume is the amount of attenuation we tell the codec to impose
- * on the outputs.  There are 32 levels, with 0 the "loudest".
- */
-#define CS_VOLUME_TO_MASK(x)	(31 - ((((x) - 1) * 31) / 99))
-#define CS_MASK_TO_VOLUME(y)	(100 - ((y) * 99 / 31))
-
-static int cs_get_volume(uint reg)
-{
-	int volume;
-
-	volume = CS_MASK_TO_VOLUME(CS_LATTEN_GET(reg));
-	volume |= CS_MASK_TO_VOLUME(CS_RATTEN_GET(reg)) << 8;
-	return volume;
-}
-
-static int cs_volume_setter(int volume, int mute)
-{
-	uint tempctl;
-
-	if (mute && volume == 0) {
-		tempctl = cs4218_control | CS_MUTE;
-	} else {
-		tempctl = cs4218_control & ~CS_MUTE;
-		tempctl = tempctl & ~(CS_LATTEN | CS_RATTEN);
-		tempctl |= CS_LATTEN_SET(CS_VOLUME_TO_MASK(volume & 0xff));
-		tempctl |= CS_RATTEN_SET(CS_VOLUME_TO_MASK((volume >> 8) & 0xff));
-		volume = cs_get_volume(tempctl);
-	}
-	if (tempctl != cs4218_control) {
-		cs4218_ctl_write(tempctl);
-	}
-	return volume;
-}
-
-
-/* Gain has 16 steps from 0 to 15.  These are in 1.5dB increments from
- * 0 (no gain) to 22.5 dB.
- */
-#define CS_RECLEVEL_TO_GAIN(v) \
-	((v) < 0 ? 0 : (v) > 100 ? 15 : (v) * 3 / 20)
-#define CS_GAIN_TO_RECLEVEL(v) (((v) * 20 + 2) / 3)
-
-static int cs_get_gain(uint reg)
-{
-	int gain;
-
-	gain = CS_GAIN_TO_RECLEVEL(CS_LGAIN_GET(reg));
-	gain |= CS_GAIN_TO_RECLEVEL(CS_RGAIN_GET(reg)) << 8;
-	return gain;
-}
-
-static int cs_set_gain(int gain)
-{
-	uint tempctl;
-
-	tempctl = cs4218_control & ~(CS_LGAIN | CS_RGAIN);
-	tempctl |= CS_LGAIN_SET(CS_RECLEVEL_TO_GAIN(gain & 0xff));
-	tempctl |= CS_RGAIN_SET(CS_RECLEVEL_TO_GAIN((gain >> 8) & 0xff));
-	gain = cs_get_gain(tempctl);
-
-	if (tempctl != cs4218_control) {
-		cs4218_ctl_write(tempctl);
-	}
-	return gain;
-}
-
-static int CS_SetVolume(int volume)
-{
-	return cs_volume_setter(volume, CS_MUTE);
-}
-
-static void CS_Play(void)
-{
-	int i, count;
-	unsigned long flags;
-	volatile cbd_t	*bdp;
-	volatile cpm8xx_t *cp;
-
-	/* Protect buffer */
-	spin_lock_irqsave(&cs4218_lock, flags);
-#if 0
-	if (awacs_beep_state) {
-		/* sound takes precedence over beeps */
-		out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
-		out_le32(&awacs->control,
-			 (in_le32(&awacs->control) & ~0x1f00)
-			 | (awacs_rate_index << 8));
-		out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE);
-		out_le32(&awacs_txdma->cmdptr, virt_to_bus(&(awacs_tx_cmds[(sq.front+sq.active) % sq.max_count])));
-
-		beep_playing = 0;
-		awacs_beep_state = 0;
-	}
-#endif
-	i = sq.front + sq.active;
-	if (i >= sq.max_count)
-		i -= sq.max_count;
-	while (sq.active < 2 && sq.active < sq.count) {
-		count = (sq.count == sq.active + 1)?sq.rear_size:sq.block_size;
-		if (count < sq.block_size && !sq.syncing)
-			/* last block not yet filled, and we're not syncing. */
-			break;
-
-		bdp = &tx_base[i];
-		bdp->cbd_datlen = count;
-
-		flush_dcache_range((ulong)sound_buffers[i],
-					(ulong)(sound_buffers[i] + count));
-
-		if (++i >= sq.max_count)
-			i = 0;
-
-		if (sq.active == 0) {
-			/* The SMC does not load its fifo until the first
-			 * TDM frame pulse, so the transmit data gets shifted
-			 * by one word.  To compensate for this, we incorrectly
-			 * transmit the first buffer and shorten it by one
-			 * word.  Subsequent buffers are then aligned properly.
-			 */
-			bdp->cbd_datlen -= 2;
-
-			/* Start up the SMC Transmitter.
-			*/
-			cp = cpmp;
-			cp->cp_smc[1].smc_smcmr |= SMCMR_TEN;
-			cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2,
-					CPM_CR_RESTART_TX) | CPM_CR_FLG;
-			while (cp->cp_cpcr & CPM_CR_FLG);
-		}
-
-		/* Buffer is ready now.
-		*/
-		bdp->cbd_sc |= BD_SC_READY;
-
-		++sq.active;
-	}
-	spin_unlock_irqrestore(&cs4218_lock, flags);
-}
-
-
-static void CS_Record(void)
-{
-	unsigned long flags;
-	volatile smc_t		*sp;
-
-	if (read_sq.active)
-		return;
-
-	/* Protect buffer */
-	spin_lock_irqsave(&cs4218_lock, flags);
-
-	/* This is all we have to do......Just start it up.
-	*/
-	sp = &cpmp->cp_smc[1];
-	sp->smc_smcmr |= SMCMR_REN;
-
-	read_sq.active = 1;
-
-        spin_unlock_irqrestore(&cs4218_lock, flags);
-}
-
-
-static void
-cs4218_tdm_tx_intr(void *devid)
-{
-	int i = sq.front;
-	volatile cbd_t *bdp;
-
-	while (sq.active > 0) {
-		bdp = &tx_base[i];
-		if (bdp->cbd_sc & BD_SC_READY)
-			break;	/* this frame is still going */
-		--sq.count;
-		--sq.active;
-		if (++i >= sq.max_count)
-			i = 0;
-	}
-	if (i != sq.front)
-		WAKE_UP(sq.action_queue);
-	sq.front = i;
-
-	CS_Play();
-
-	if (!sq.active)
-		WAKE_UP(sq.sync_queue);
-}
-
-
-static void
-cs4218_tdm_rx_intr(void *devid)
-{
-
-	/* We want to blow 'em off when shutting down.
-	*/
-	if (read_sq.active == 0)
-		return;
-
-	/* Check multiple buffers in case we were held off from
-	 * interrupt processing for a long time.  Geeze, I really hope
-	 * this doesn't happen.
-	 */
-	while ((rx_base[read_sq.rear].cbd_sc & BD_SC_EMPTY) == 0) {
-
-		/* Invalidate the data cache range for this buffer.
-		*/
-		invalidate_dcache_range(
-		    (uint)(sound_read_buffers[read_sq.rear]),
-		    (uint)(sound_read_buffers[read_sq.rear] + read_sq.block_size));
-
-		/* Make buffer available again and move on.
-		*/
-		rx_base[read_sq.rear].cbd_sc |= BD_SC_EMPTY;
-		read_sq.rear++;
-
-		/* Wrap the buffer ring.
-		*/
-		if (read_sq.rear >= read_sq.max_active)
-			read_sq.rear = 0;
-
-		/* If we have caught up to the front buffer, bump it.
-		 * This will cause weird (but not fatal) results if the
-		 * read loop is currently using this buffer.  The user is
-		 * behind in this case anyway, so weird things are going
-		 * to happen.
-		 */
-		if (read_sq.rear == read_sq.front) {
-			read_sq.front++;
-			if (read_sq.front >= read_sq.max_active)
-				read_sq.front = 0;
-		}
-	}
-
-	WAKE_UP(read_sq.action_queue);
-}
-
-static void cs_nosound(unsigned long xx)
-{
-	unsigned long flags;
-
-	/* not sure if this is needed, since hardware command is #if 0'd */
-	spin_lock_irqsave(&cs4218_lock, flags);
-	if (beep_playing) {
-#if 0
-		st_le16(&beep_dbdma_cmd->command, DBDMA_STOP);
-#endif
-		beep_playing = 0;
-	}
-	spin_unlock_irqrestore(&cs4218_lock, flags);
-}
-
-static DEFINE_TIMER(beep_timer, cs_nosound, 0, 0);
-
-static void cs_mksound(unsigned int hz, unsigned int ticks)
-{
-	unsigned long flags;
-	int beep_speed = BEEP_SPEED;
-	int srate = cs4218_freqs[beep_speed];
-	int period, ncycles, nsamples;
-	int i, j, f;
-	short *p;
-	static int beep_hz_cache;
-	static int beep_nsamples_cache;
-	static int beep_volume_cache;
-
-	if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) {
-#if 1
-		/* this is a hack for broken X server code */
-		hz = 750;
-		ticks = 12;
-#else
-		/* cancel beep currently playing */
-		awacs_nosound(0);
-		return;
-#endif
-	}
-	/* lock while modifying beep_timer */
-	spin_lock_irqsave(&cs4218_lock, flags);
-	del_timer(&beep_timer);
-	if (ticks) {
-		beep_timer.expires = jiffies + ticks;
-		add_timer(&beep_timer);
-	}
-	if (beep_playing || sq.active || beep_buf == NULL) {
-		spin_unlock_irqrestore(&cs4218_lock, flags);
-		return;		/* too hard, sorry :-( */
-	}
-	beep_playing = 1;
-#if 0
-	st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS);
-#endif
-	spin_unlock_irqrestore(&cs4218_lock, flags);
-
-	if (hz == beep_hz_cache && beep_volume == beep_volume_cache) {
-		nsamples = beep_nsamples_cache;
-	} else {
-		period = srate * 256 / hz;	/* fixed point */
-		ncycles = BEEP_BUFLEN * 256 / period;
-		nsamples = (period * ncycles) >> 8;
-		f = ncycles * 65536 / nsamples;
-		j = 0;
-		p = beep_buf;
-		for (i = 0; i < nsamples; ++i, p += 2) {
-			p[0] = p[1] = beep_wform[j >> 8] * beep_volume;
-			j = (j + f) & 0xffff;
-		}
-		beep_hz_cache = hz;
-		beep_volume_cache = beep_volume;
-		beep_nsamples_cache = nsamples;
-	}
-
-#if 0
-	st_le16(&beep_dbdma_cmd->req_count, nsamples*4);
-	st_le16(&beep_dbdma_cmd->xfer_status, 0);
-	st_le32(&beep_dbdma_cmd->cmd_dep, virt_to_bus(beep_dbdma_cmd));
-	st_le32(&beep_dbdma_cmd->phy_addr, virt_to_bus(beep_buf));
-	awacs_beep_state = 1;
-
-	spin_lock_irqsave(&cs4218_lock, flags);
-	if (beep_playing) {	/* i.e. haven't been terminated already */
-		out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16);
-		out_le32(&awacs->control,
-			 (in_le32(&awacs->control) & ~0x1f00)
-			 | (beep_speed << 8));
-		out_le32(&awacs->byteswap, 0);
-		out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
-		out_le32(&awacs_txdma->control, RUN | (RUN << 16));
-	}
-	spin_unlock_irqrestore(&cs4218_lock, flags);
-#endif
-}
-
-static MACHINE mach_cs4218 = {
-	.owner =	THIS_MODULE,
-	.name =		"HIOX CS4218",
-	.name2 =	"Built-in Sound",
-	.dma_alloc =	CS_Alloc,
-	.dma_free =	CS_Free,
-	.irqinit =	CS_IrqInit,
-#ifdef MODULE
-	.irqcleanup =	CS_IrqCleanup,
-#endif /* MODULE */
-	.init =		CS_Init,
-	.silence =	CS_Silence,
-	.setFormat =	CS_SetFormat,
-	.setVolume =	CS_SetVolume,
-	.play =		CS_Play
-};
-
-
-/*** Mid level stuff *********************************************************/
-
-
-static void sound_silence(void)
-{
-	/* update hardware settings one more */
-	(*sound.mach.init)();
-
-	(*sound.mach.silence)();
-}
-
-
-static void sound_init(void)
-{
-	(*sound.mach.init)();
-}
-
-
-static int sound_set_format(int format)
-{
-	return(*sound.mach.setFormat)(format);
-}
-
-
-static int sound_set_speed(int speed)
-{
-	if (speed < 0)
-		return(sound.soft.speed);
-
-	sound.soft.speed = speed;
-	(*sound.mach.init)();
-	if (sound.minDev == SND_DEV_DSP)
-		sound.dsp.speed = sound.soft.speed;
-
-	return(sound.soft.speed);
-}
-
-
-static int sound_set_stereo(int stereo)
-{
-	if (stereo < 0)
-		return(sound.soft.stereo);
-
-	stereo = !!stereo;    /* should be 0 or 1 now */
-
-	sound.soft.stereo = stereo;
-	if (sound.minDev == SND_DEV_DSP)
-		sound.dsp.stereo = stereo;
-	(*sound.mach.init)();
-
-	return(stereo);
-}
-
-
-static int sound_set_volume(int volume)
-{
-	return(*sound.mach.setVolume)(volume);
-}
-
-static ssize_t sound_copy_translate(const u_char *userPtr,
-				    size_t userCount,
-				    u_char frame[], ssize_t *frameUsed,
-				    ssize_t frameLeft)
-{
-	ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t) = NULL;
-
-	switch (sound.soft.format) {
-	case AFMT_MU_LAW:
-		ct_func = sound.trans_write->ct_ulaw;
-		break;
-	case AFMT_A_LAW:
-		ct_func = sound.trans_write->ct_alaw;
-		break;
-	case AFMT_S8:
-		ct_func = sound.trans_write->ct_s8;
-		break;
-	case AFMT_U8:
-		ct_func = sound.trans_write->ct_u8;
-		break;
-	case AFMT_S16_BE:
-		ct_func = sound.trans_write->ct_s16be;
-		break;
-	case AFMT_U16_BE:
-		ct_func = sound.trans_write->ct_u16be;
-		break;
-	case AFMT_S16_LE:
-		ct_func = sound.trans_write->ct_s16le;
-		break;
-	case AFMT_U16_LE:
-		ct_func = sound.trans_write->ct_u16le;
-		break;
-	}
-	if (ct_func)
-		return ct_func(userPtr, userCount, frame, frameUsed, frameLeft);
-	else
-		return 0;
-}
-
-static ssize_t sound_copy_translate_read(const u_char *userPtr,
-				    size_t userCount,
-				    u_char frame[], ssize_t *frameUsed,
-				    ssize_t frameLeft)
-{
-	ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t) = NULL;
-
-	switch (sound.soft.format) {
-	case AFMT_MU_LAW:
-		ct_func = sound.trans_read->ct_ulaw;
-		break;
-	case AFMT_A_LAW:
-		ct_func = sound.trans_read->ct_alaw;
-		break;
-	case AFMT_S8:
-		ct_func = sound.trans_read->ct_s8;
-		break;
-	case AFMT_U8:
-		ct_func = sound.trans_read->ct_u8;
-		break;
-	case AFMT_S16_BE:
-		ct_func = sound.trans_read->ct_s16be;
-		break;
-	case AFMT_U16_BE:
-		ct_func = sound.trans_read->ct_u16be;
-		break;
-	case AFMT_S16_LE:
-		ct_func = sound.trans_read->ct_s16le;
-		break;
-	case AFMT_U16_LE:
-		ct_func = sound.trans_read->ct_u16le;
-		break;
-	}
-	if (ct_func)
-		return ct_func(userPtr, userCount, frame, frameUsed, frameLeft);
-	else
-		return 0;
-}
-
-
-/*
- * /dev/mixer abstraction
- */
-
-static int mixer_open(struct inode *inode, struct file *file)
-{
-	mixer.busy = 1;
-	return nonseekable_open(inode, file);
-}
-
-
-static int mixer_release(struct inode *inode, struct file *file)
-{
-	mixer.busy = 0;
-	return 0;
-}
-
-
-static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd,
-		       u_long arg)
-{
-	int data;
-	uint tmpcs;
-
-	if (_SIOC_DIR(cmd) & _SIOC_WRITE)
-	    mixer.modify_counter++;
-	if (cmd == OSS_GETVERSION)
-	    return IOCTL_OUT(arg, SOUND_VERSION);
-	switch (cmd) {
-		case SOUND_MIXER_INFO: {
-		    mixer_info info;
-		    strlcpy(info.id, "CS4218_TDM", sizeof(info.id));
-		    strlcpy(info.name, "CS4218_TDM", sizeof(info.name));
-		    info.name[sizeof(info.name)-1] = 0;
-		    info.modify_counter = mixer.modify_counter;
-		    if (copy_to_user((int *)arg, &info, sizeof(info)))
-		    		return -EFAULT;
-		    return 0;
-		}
-		case SOUND_MIXER_READ_DEVMASK:
-			data = SOUND_MASK_VOLUME | SOUND_MASK_LINE
-				| SOUND_MASK_MIC | SOUND_MASK_RECLEV
-				| SOUND_MASK_ALTPCM;
-			return IOCTL_OUT(arg, data);
-		case SOUND_MIXER_READ_RECMASK:
-			data = SOUND_MASK_LINE | SOUND_MASK_MIC;
-			return IOCTL_OUT(arg, data);
-		case SOUND_MIXER_READ_RECSRC:
-			if (cs4218_control & CS_DO1)
-				data = SOUND_MASK_LINE;
-			else
-				data = SOUND_MASK_MIC;
-			return IOCTL_OUT(arg, data);
-		case SOUND_MIXER_WRITE_RECSRC:
-			IOCTL_IN(arg, data);
-			data &= (SOUND_MASK_LINE | SOUND_MASK_MIC);
-			if (data & SOUND_MASK_LINE)
-				tmpcs = cs4218_control |
-						(CS_ISL | CS_ISR | CS_DO1);
-			if (data & SOUND_MASK_MIC)
-				tmpcs = cs4218_control &
-						~(CS_ISL | CS_ISR | CS_DO1);
-			if (tmpcs != cs4218_control)
-				cs4218_ctl_write(tmpcs);
-			return IOCTL_OUT(arg, data);
-		case SOUND_MIXER_READ_STEREODEVS:
-			data = SOUND_MASK_VOLUME | SOUND_MASK_RECLEV;
-			return IOCTL_OUT(arg, data);
-		case SOUND_MIXER_READ_CAPS:
-			return IOCTL_OUT(arg, 0);
-		case SOUND_MIXER_READ_VOLUME:
-			data = (cs4218_control & CS_MUTE)? 0:
-				cs_get_volume(cs4218_control);
-			return IOCTL_OUT(arg, data);
-		case SOUND_MIXER_WRITE_VOLUME:
-			IOCTL_IN(arg, data);
-			return IOCTL_OUT(arg, sound_set_volume(data));
-		case SOUND_MIXER_WRITE_ALTPCM:	/* really bell volume */
-			IOCTL_IN(arg, data);
-			beep_volume = data & 0xff;
-			/* fall through */
-		case SOUND_MIXER_READ_ALTPCM:
-			return IOCTL_OUT(arg, beep_volume);
-		case SOUND_MIXER_WRITE_RECLEV:
-			IOCTL_IN(arg, data);
-			data = cs_set_gain(data);
-			return IOCTL_OUT(arg, data);
-		case SOUND_MIXER_READ_RECLEV:
-			data = cs_get_gain(cs4218_control);
-			return IOCTL_OUT(arg, data);
-	}
-
-	return -EINVAL;
-}
-
-
-static const struct file_operations mixer_fops =
-{
-	.owner =	THIS_MODULE,
-	.llseek =	sound_lseek,
-	.ioctl =	mixer_ioctl,
-	.open =		mixer_open,
-	.release =	mixer_release,
-};
-
-
-static void __init mixer_init(void)
-{
-	mixer_unit = register_sound_mixer(&mixer_fops, -1);
-	if (mixer_unit < 0)
-		return;
-
-	mixer.busy = 0;
-	sound.treble = 0;
-	sound.bass = 0;
-
-	/* Set Line input, no gain, no attenuation.
-	*/
-	cs4218_control = CS_ISL | CS_ISR | CS_DO1;
-	cs4218_control |= CS_LGAIN_SET(0) | CS_RGAIN_SET(0);
-	cs4218_control |= CS_LATTEN_SET(0) | CS_RATTEN_SET(0);
-	cs4218_ctl_write(cs4218_control);
-}
-
-
-/*
- * Sound queue stuff, the heart of the driver
- */
-
-
-static int sq_allocate_buffers(void)
-{
-	int i;
-
-	if (sound_buffers)
-		return 0;
-	sound_buffers = kmalloc (numBufs * sizeof(char *), GFP_KERNEL);
-	if (!sound_buffers)
-		return -ENOMEM;
-	for (i = 0; i < numBufs; i++) {
-		sound_buffers[i] = sound.mach.dma_alloc (bufSize << 10, GFP_KERNEL);
-		if (!sound_buffers[i]) {
-			while (i--)
-				sound.mach.dma_free (sound_buffers[i], bufSize << 10);
-			kfree (sound_buffers);
-			sound_buffers = 0;
-			return -ENOMEM;
-		}
-	}
-	return 0;
-}
-
-
-static void sq_release_buffers(void)
-{
-	int i;
-
-	if (sound_buffers) {
-		for (i = 0; i < numBufs; i++)
-			sound.mach.dma_free (sound_buffers[i], bufSize << 10);
-		kfree (sound_buffers);
-		sound_buffers = 0;
-	}
-}
-
-
-static int sq_allocate_read_buffers(void)
-{
-	int i;
-
-	if (sound_read_buffers)
-		return 0;
-	sound_read_buffers = kmalloc(numReadBufs * sizeof(char *), GFP_KERNEL);
-	if (!sound_read_buffers)
-		return -ENOMEM;
-	for (i = 0; i < numBufs; i++) {
-		sound_read_buffers[i] = sound.mach.dma_alloc (readbufSize<<10,
-							      GFP_KERNEL);
-		if (!sound_read_buffers[i]) {
-			while (i--)
-				sound.mach.dma_free (sound_read_buffers[i],
-						     readbufSize << 10);
-			kfree (sound_read_buffers);
-			sound_read_buffers = 0;
-			return -ENOMEM;
-		}
-	}
-	return 0;
-}
-
-static void sq_release_read_buffers(void)
-{
-	int i;
-
-	if (sound_read_buffers) {
-		cpmp->cp_smc[1].smc_smcmr &= ~SMCMR_REN;
-		for (i = 0; i < numReadBufs; i++)
-			sound.mach.dma_free (sound_read_buffers[i],
-					     bufSize << 10);
-		kfree (sound_read_buffers);
-		sound_read_buffers = 0;
-	}
-}
-
-
-static void sq_setup(int numBufs, int bufSize, char **write_buffers)
-{
-	int i;
-	volatile cbd_t *bdp;
-	volatile cpm8xx_t	*cp;
-	volatile smc_t	*sp;
-
-	/* Make sure the SMC transmit is shut down.
-	*/
-	cp = cpmp;
-	sp = &cpmp->cp_smc[1];
-	sp->smc_smcmr &= ~SMCMR_TEN;
-
-	sq.max_count = numBufs;
-	sq.max_active = numBufs;
-	sq.block_size = bufSize;
-	sq.buffers = write_buffers;
-
-	sq.front = sq.count = 0;
-	sq.rear = -1;
-	sq.syncing = 0;
-	sq.active = 0;
-
-	bdp = tx_base;
-	for (i=0; i<numBufs; i++) {
-		bdp->cbd_bufaddr = virt_to_bus(write_buffers[i]);
-		bdp++;
-	}
-
-	/* This causes the SMC to sync up with the first buffer again.
-	*/
-	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, CPM_CR_INIT_TX) | CPM_CR_FLG;
-	while (cp->cp_cpcr & CPM_CR_FLG);
-}
-
-static void read_sq_setup(int numBufs, int bufSize, char **read_buffers)
-{
-	int i;
-	volatile cbd_t *bdp;
-	volatile cpm8xx_t	*cp;
-	volatile smc_t	*sp;
-
-	/* Make sure the SMC receive is shut down.
-	*/
-	cp = cpmp;
-	sp = &cpmp->cp_smc[1];
-	sp->smc_smcmr &= ~SMCMR_REN;
-
-	read_sq.max_count = numBufs;
-	read_sq.max_active = numBufs;
-	read_sq.block_size = bufSize;
-	read_sq.buffers = read_buffers;
-
-	read_sq.front = read_sq.count = 0;
-	read_sq.rear = 0;
-	read_sq.rear_size = 0;
-	read_sq.syncing = 0;
-	read_sq.active = 0;
-
-	bdp = rx_base;
-	for (i=0; i<numReadBufs; i++) {
-		bdp->cbd_bufaddr = virt_to_bus(read_buffers[i]);
-		bdp->cbd_datlen = read_sq.block_size;
-		bdp++;
-	}
-
-	/* This causes the SMC to sync up with the first buffer again.
-	*/
-	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, CPM_CR_INIT_RX) | CPM_CR_FLG;
-	while (cp->cp_cpcr & CPM_CR_FLG);
-}
-
-
-static void sq_play(void)
-{
-	(*sound.mach.play)();
-}
-
-
-/* ++TeSche: radically changed this one too */
-
-static ssize_t sq_write(struct file *file, const char *src, size_t uLeft,
-			loff_t *ppos)
-{
-	ssize_t uWritten = 0;
-	u_char *dest;
-	ssize_t uUsed, bUsed, bLeft;
-
-	/* ++TeSche: Is something like this necessary?
-	 * Hey, that's an honest question! Or does any other part of the
-	 * filesystem already checks this situation? I really don't know.
-	 */
-	if (uLeft == 0)
-		return 0;
-
-	/* The interrupt doesn't start to play the last, incomplete frame.
-	 * Thus we can append to it without disabling the interrupts! (Note
-	 * also that sq.rear isn't affected by the interrupt.)
-	 */
-
-	if (sq.count > 0 && (bLeft = sq.block_size-sq.rear_size) > 0) {
-		dest = sq_block_address(sq.rear);
-		bUsed = sq.rear_size;
-		uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft);
-		if (uUsed <= 0)
-			return uUsed;
-		src += uUsed;
-		uWritten += uUsed;
-		uLeft -= uUsed;
-		sq.rear_size = bUsed;
-	}
-
-	do {
-		while (sq.count == sq.max_active) {
-			sq_play();
-			if (NON_BLOCKING(sq.open_mode))
-				return uWritten > 0 ? uWritten : -EAGAIN;
-			SLEEP(sq.action_queue);
-			if (SIGNAL_RECEIVED)
-				return uWritten > 0 ? uWritten : -EINTR;
-		}
-
-		/* Here, we can avoid disabling the interrupt by first
-		 * copying and translating the data, and then updating
-		 * the sq variables. Until this is done, the interrupt
-		 * won't see the new frame and we can work on it
-		 * undisturbed.
-		 */
-
-		dest = sq_block_address((sq.rear+1) % sq.max_count);
-		bUsed = 0;
-		bLeft = sq.block_size;
-		uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft);
-		if (uUsed <= 0)
-			break;
-		src += uUsed;
-		uWritten += uUsed;
-		uLeft -= uUsed;
-		if (bUsed) {
-			sq.rear = (sq.rear+1) % sq.max_count;
-			sq.rear_size = bUsed;
-			sq.count++;
-		}
-	} while (bUsed);   /* uUsed may have been 0 */
-
-	sq_play();
-
-	return uUsed < 0? uUsed: uWritten;
-}
-
-
-/***********/
-
-/* Here is how the values are used for reading.
- * The value 'active' simply indicates the DMA is running.  This is
- * done so the driver semantics are DMA starts when the first read is
- * posted.  The value 'front' indicates the buffer we should next
- * send to the user.  The value 'rear' indicates the buffer the DMA is
- * currently filling.  When 'front' == 'rear' the buffer "ring" is
- * empty (we always have an empty available).  The 'rear_size' is used
- * to track partial offsets into the current buffer.  Right now, I just keep
- * The DMA running.  If the reader can't keep up, the interrupt tosses
- * the oldest buffer.  We could also shut down the DMA in this case.
- */
-static ssize_t sq_read(struct file *file, char *dst, size_t uLeft,
-                       loff_t *ppos)
-{
-
-	ssize_t	uRead, bLeft, bUsed, uUsed;
-
-	if (uLeft == 0)
-		return 0;
-
-	if (!read_sq.active)
-		CS_Record();	/* Kick off the record process. */
-
-	uRead = 0;
-
-	/* Move what the user requests, depending upon other options.
-	*/
-	while (uLeft > 0) {
-
-		/* When front == rear, the DMA is not done yet.
-		*/
-		while (read_sq.front == read_sq.rear) {
-			if (NON_BLOCKING(read_sq.open_mode)) {
-			       return uRead > 0 ? uRead : -EAGAIN;
-			}
-			SLEEP(read_sq.action_queue);
-			if (SIGNAL_RECEIVED)
-				return uRead > 0 ? uRead : -EINTR;
-		}
-
-		/* The amount we move is either what is left in the
-		 * current buffer or what the user wants.
-		 */
-		bLeft = read_sq.block_size - read_sq.rear_size;
-		bUsed = read_sq.rear_size;
-		uUsed = sound_copy_translate_read(dst, uLeft,
-			read_sq.buffers[read_sq.front], &bUsed, bLeft);
-		if (uUsed <= 0)
-			return uUsed;
-		dst += uUsed;
-		uRead += uUsed;
-		uLeft -= uUsed;
-		read_sq.rear_size += bUsed;
-		if (read_sq.rear_size >= read_sq.block_size) {
-			read_sq.rear_size = 0;
-			read_sq.front++;
-			if (read_sq.front >= read_sq.max_active)
-				read_sq.front = 0;
-		}
-	}
-	return uRead;
-}
-
-static int sq_open(struct inode *inode, struct file *file)
-{
-	int rc = 0;
-
-	if (file->f_mode & FMODE_WRITE) {
-		if (sq.busy) {
-			rc = -EBUSY;
-			if (NON_BLOCKING(file->f_flags))
-				goto err_out;
-			rc = -EINTR;
-			while (sq.busy) {
-				SLEEP(sq.open_queue);
-				if (SIGNAL_RECEIVED)
-					goto err_out;
-			}
-		}
-		sq.busy = 1; /* Let's play spot-the-race-condition */
-
-		if (sq_allocate_buffers()) goto err_out_nobusy;
-
-		sq_setup(numBufs, bufSize<<10,sound_buffers);
-		sq.open_mode = file->f_mode;
-	}
-
-
-	if (file->f_mode & FMODE_READ) {
-		if (read_sq.busy) {
-			rc = -EBUSY;
-			if (NON_BLOCKING(file->f_flags))
-				goto err_out;
-			rc = -EINTR;
-			while (read_sq.busy) {
-				SLEEP(read_sq.open_queue);
-				if (SIGNAL_RECEIVED)
-					goto err_out;
-			}
-			rc = 0;
-		}
-		read_sq.busy = 1;
-		if (sq_allocate_read_buffers()) goto err_out_nobusy;
-
-		read_sq_setup(numReadBufs,readbufSize<<10, sound_read_buffers);
-		read_sq.open_mode = file->f_mode;
-	}
-
-	/* Start up the 4218 by:
-	 * Reset.
-	 * Enable, unreset.
-	 */
-	*((volatile uint *)HIOX_CSR4_ADDR) &= ~HIOX_CSR4_RSTAUDIO;
-	eieio();
-	*((volatile uint *)HIOX_CSR4_ADDR) |= HIOX_CSR4_ENAUDIO;
-	mdelay(50);
-	*((volatile uint *)HIOX_CSR4_ADDR) |= HIOX_CSR4_RSTAUDIO;
-
-	/* We need to send the current control word in case someone
-	 * opened /dev/mixer and changed things while we were shut
-	 * down.  Chances are good the initialization that follows
-	 * would have done this, but it is still possible it wouldn't.
-	 */
-	cs4218_ctl_write(cs4218_control);
-
-	sound.minDev = iminor(inode) & 0x0f;
-	sound.soft = sound.dsp;
-	sound.hard = sound.dsp;
-	sound_init();
-	if ((iminor(inode) & 0x0f) == SND_DEV_AUDIO) {
-		sound_set_speed(8000);
-		sound_set_stereo(0);
-		sound_set_format(AFMT_MU_LAW);
-	}
-
-	return nonseekable_open(inode, file);
-
-err_out_nobusy:
-	if (file->f_mode & FMODE_WRITE) {
-		sq.busy = 0;
-		WAKE_UP(sq.open_queue);
-	}
-	if (file->f_mode & FMODE_READ) {
-		read_sq.busy = 0;
-		WAKE_UP(read_sq.open_queue);
-	}
-err_out:
-	return rc;
-}
-
-
-static void sq_reset(void)
-{
-	sound_silence();
-	sq.active = 0;
-	sq.count = 0;
-	sq.front = (sq.rear+1) % sq.max_count;
-#if 0
-	init_tdm_buffers();
-#endif
-}
-
-
-static int sq_fsync(struct file *filp, struct dentry *dentry)
-{
-	int rc = 0;
-
-	sq.syncing = 1;
-	sq_play();	/* there may be an incomplete frame waiting */
-
-	while (sq.active) {
-		SLEEP(sq.sync_queue);
-		if (SIGNAL_RECEIVED) {
-			/* While waiting for audio output to drain, an
-			 * interrupt occurred.  Stop audio output immediately
-			 * and clear the queue. */
-			sq_reset();
-			rc = -EINTR;
-			break;
-		}
-	}
-
-	sq.syncing = 0;
-	return rc;
-}
-
-static int sq_release(struct inode *inode, struct file *file)
-{
-	int rc = 0;
-
-	if (sq.busy)
-		rc = sq_fsync(file, file->f_path.dentry);
-	sound.soft = sound.dsp;
-	sound.hard = sound.dsp;
-	sound_silence();
-
-	sq_release_read_buffers();
-	sq_release_buffers();
-
-	if (file->f_mode & FMODE_READ) {
-		read_sq.busy = 0;
-		WAKE_UP(read_sq.open_queue);
-	}
-
-	if (file->f_mode & FMODE_WRITE) {
-		sq.busy = 0;
-		WAKE_UP(sq.open_queue);
-	}
-
-	/* Shut down the SMC.
-	*/
-	cpmp->cp_smc[1].smc_smcmr &= ~(SMCMR_TEN | SMCMR_REN);
-
-	/* Shut down the codec.
-	*/
-	*((volatile uint *)HIOX_CSR4_ADDR) |= HIOX_CSR4_RSTAUDIO;
-	eieio();
-	*((volatile uint *)HIOX_CSR4_ADDR) &= ~HIOX_CSR4_ENAUDIO;
-
-	/* Wake up a process waiting for the queue being released.
-	 * Note: There may be several processes waiting for a call
-	 * to open() returning. */
-
-	return rc;
-}
-
-
-static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd,
-		    u_long arg)
-{
-	u_long fmt;
-	int data;
-#if 0
-	int size, nbufs;
-#else
-	int size;
-#endif
-
-	switch (cmd) {
-	case SNDCTL_DSP_RESET:
-		sq_reset();
-		return 0;
-	case SNDCTL_DSP_POST:
-	case SNDCTL_DSP_SYNC:
-		return sq_fsync(file, file->f_path.dentry);
-
-		/* ++TeSche: before changing any of these it's
-		 * probably wise to wait until sound playing has
-		 * settled down. */
-	case SNDCTL_DSP_SPEED:
-		sq_fsync(file, file->f_path.dentry);
-		IOCTL_IN(arg, data);
-		return IOCTL_OUT(arg, sound_set_speed(data));
-	case SNDCTL_DSP_STEREO:
-		sq_fsync(file, file->f_path.dentry);
-		IOCTL_IN(arg, data);
-		return IOCTL_OUT(arg, sound_set_stereo(data));
-	case SOUND_PCM_WRITE_CHANNELS:
-		sq_fsync(file, file->f_path.dentry);
-		IOCTL_IN(arg, data);
-		return IOCTL_OUT(arg, sound_set_stereo(data-1)+1);
-	case SNDCTL_DSP_SETFMT:
-		sq_fsync(file, file->f_path.dentry);
-		IOCTL_IN(arg, data);
-		return IOCTL_OUT(arg, sound_set_format(data));
-	case SNDCTL_DSP_GETFMTS:
-		fmt = 0;
-		if (sound.trans_write) {
-			if (sound.trans_write->ct_ulaw)
-				fmt |= AFMT_MU_LAW;
-			if (sound.trans_write->ct_alaw)
-				fmt |= AFMT_A_LAW;
-			if (sound.trans_write->ct_s8)
-				fmt |= AFMT_S8;
-			if (sound.trans_write->ct_u8)
-				fmt |= AFMT_U8;
-			if (sound.trans_write->ct_s16be)
-				fmt |= AFMT_S16_BE;
-			if (sound.trans_write->ct_u16be)
-				fmt |= AFMT_U16_BE;
-			if (sound.trans_write->ct_s16le)
-				fmt |= AFMT_S16_LE;
-			if (sound.trans_write->ct_u16le)
-				fmt |= AFMT_U16_LE;
-		}
-		return IOCTL_OUT(arg, fmt);
-	case SNDCTL_DSP_GETBLKSIZE:
-		size = sq.block_size
-			* sound.soft.size * (sound.soft.stereo + 1)
-			/ (sound.hard.size * (sound.hard.stereo + 1));
-		return IOCTL_OUT(arg, size);
-	case SNDCTL_DSP_SUBDIVIDE:
-		break;
-#if 0	/* Sorry can't do this at the moment.  The CPM allocated buffers
-	 * long ago that can't be changed.
-	 */
-	case SNDCTL_DSP_SETFRAGMENT:
-		if (sq.count || sq.active || sq.syncing)
-			return -EINVAL;
-		IOCTL_IN(arg, size);
-		nbufs = size >> 16;
-		if (nbufs < 2 || nbufs > numBufs)
-			nbufs = numBufs;
-		size &= 0xffff;
-		if (size >= 8 && size <= 30) {
-			size = 1 << size;
-			size *= sound.hard.size * (sound.hard.stereo + 1);
-			size /= sound.soft.size * (sound.soft.stereo + 1);
-			if (size > (bufSize << 10))
-				size = bufSize << 10;
-		} else
-			size = bufSize << 10;
-		sq_setup(numBufs, size, sound_buffers);
-		sq.max_active = nbufs;
-		return 0;
-#endif
-
-	default:
-		return mixer_ioctl(inode, file, cmd, arg);
-	}
-	return -EINVAL;
-}
-
-
-
-static const struct file_operations sq_fops =
-{
-	.owner =	THIS_MODULE,
-	.llseek =	sound_lseek,
-	.read =		sq_read,			/* sq_read */
-	.write =	sq_write,
-	.ioctl =	sq_ioctl,
-	.open =		sq_open,
-	.release =	sq_release,
-};
-
-
-static void __init sq_init(void)
-{
-	sq_unit = register_sound_dsp(&sq_fops, -1);
-	if (sq_unit < 0)
-		return;
-
-	init_waitqueue_head(&sq.action_queue);
-	init_waitqueue_head(&sq.open_queue);
-	init_waitqueue_head(&sq.sync_queue);
-	init_waitqueue_head(&read_sq.action_queue);
-	init_waitqueue_head(&read_sq.open_queue);
-	init_waitqueue_head(&read_sq.sync_queue);
-
-	sq.busy = 0;
-	read_sq.busy = 0;
-
-	/* whatever you like as startup mode for /dev/dsp,
-	 * (/dev/audio hasn't got a startup mode). note that
-	 * once changed a new open() will *not* restore these!
-	 */
-	sound.dsp.format = AFMT_S16_BE;
-	sound.dsp.stereo = 1;
-	sound.dsp.size = 16;
-
-	/* set minimum rate possible without expanding */
-	sound.dsp.speed = 8000;
-
-	/* before the first open to /dev/dsp this wouldn't be set */
-	sound.soft = sound.dsp;
-	sound.hard = sound.dsp;
-
-	sound_silence();
-}
-
-/*
- * /dev/sndstat
- */
-
-
-/* state.buf should not overflow! */
-
-static int state_open(struct inode *inode, struct file *file)
-{
-	char *buffer = state.buf, *mach = "", cs4218_buf[50];
-	int len = 0;
-
-	if (state.busy)
-		return -EBUSY;
-
-	state.ptr = 0;
-	state.busy = 1;
-
-	sprintf(cs4218_buf, "Crystal CS4218 on TDM, ");
-	mach = cs4218_buf;
-
-	len += sprintf(buffer+len, "%sDMA sound driver:\n", mach);
-
-	len += sprintf(buffer+len, "\tsound.format = 0x%x", sound.soft.format);
-	switch (sound.soft.format) {
-	case AFMT_MU_LAW:
-		len += sprintf(buffer+len, " (mu-law)");
-		break;
-	case AFMT_A_LAW:
-		len += sprintf(buffer+len, " (A-law)");
-		break;
-	case AFMT_U8:
-		len += sprintf(buffer+len, " (unsigned 8 bit)");
-		break;
-	case AFMT_S8:
-		len += sprintf(buffer+len, " (signed 8 bit)");
-		break;
-	case AFMT_S16_BE:
-		len += sprintf(buffer+len, " (signed 16 bit big)");
-		break;
-	case AFMT_U16_BE:
-		len += sprintf(buffer+len, " (unsigned 16 bit big)");
-		break;
-	case AFMT_S16_LE:
-		len += sprintf(buffer+len, " (signed 16 bit little)");
-		break;
-	case AFMT_U16_LE:
-		len += sprintf(buffer+len, " (unsigned 16 bit little)");
-		break;
-	}
-	len += sprintf(buffer+len, "\n");
-	len += sprintf(buffer+len, "\tsound.speed = %dHz (phys. %dHz)\n",
-		       sound.soft.speed, sound.hard.speed);
-	len += sprintf(buffer+len, "\tsound.stereo = 0x%x (%s)\n",
-		       sound.soft.stereo, sound.soft.stereo ? "stereo" : "mono");
-	len += sprintf(buffer+len, "\tsq.block_size = %d sq.max_count = %d"
-		       " sq.max_active = %d\n",
-		       sq.block_size, sq.max_count, sq.max_active);
-	len += sprintf(buffer+len, "\tsq.count = %d sq.rear_size = %d\n", sq.count,
-		       sq.rear_size);
-	len += sprintf(buffer+len, "\tsq.active = %d sq.syncing = %d\n",
-		       sq.active, sq.syncing);
-	state.len = len;
-	return nonseekable_open(inode, file);
-}
-
-
-static int state_release(struct inode *inode, struct file *file)
-{
-	state.busy = 0;
-	return 0;
-}
-
-
-static ssize_t state_read(struct file *file, char *buf, size_t count,
-			  loff_t *ppos)
-{
-	int n = state.len - state.ptr;
-	if (n > count)
-		n = count;
-	if (n <= 0)
-		return 0;
-	if (copy_to_user(buf, &state.buf[state.ptr], n))
-		return -EFAULT;
-	state.ptr += n;
-	return n;
-}
-
-
-static const struct file_operations state_fops =
-{
-	.owner =	THIS_MODULE,
-	.llseek =	sound_lseek,
-	.read =		state_read,
-	.open =		state_open,
-	.release =	state_release,
-};
-
-
-static void __init state_init(void)
-{
-	state_unit = register_sound_special(&state_fops, SND_DEV_STATUS);
-	if (state_unit < 0)
-		return;
-	state.busy = 0;
-}
-
-
-/*** Common stuff ********************************************************/
-
-static long long sound_lseek(struct file *file, long long offset, int orig)
-{
-	return -ESPIPE;
-}
-
-
-/*** Config & Setup **********************************************************/
-
-
-int __init tdm8xx_sound_init(void)
-{
-	int i, has_sound;
-	uint			dp_offset;
-	volatile uint		*sirp;
-	volatile cbd_t		*bdp;
-	volatile cpm8xx_t	*cp;
-	volatile smc_t		*sp;
-	volatile smc_uart_t	*up;
-	volatile immap_t	*immap;
-
-	has_sound = 0;
-
-	/* Program the SI/TSA to use TDMa, connected to SMC2, for 4 bytes.
-	*/
-	cp = cpmp;	/* Get pointer to Communication Processor */
-	immap = (immap_t *)IMAP_ADDR;	/* and to internal registers */
-
-	/* Set all TDMa control bits to zero.  This enables most features
-	 * we want.
-	 */
-	cp->cp_simode &= ~0x00000fff;
-
-	/* Enable common receive/transmit clock pins, use IDL format.
-	 * Sync on falling edge, transmit rising clock, receive falling
-	 * clock, delay 1 bit on both Tx and Rx.  Common Tx/Rx clocks and
-	 * sync.
-	 * Connect SMC2 to TSA.
-	 */
-	cp->cp_simode |= 0x80000141;
-
-	/* Configure port A pins for TDMa operation.
-	 * The RPX-Lite (MPC850/823) loses SMC2 when TDM is used.
-	 */
-	immap->im_ioport.iop_papar |= 0x01c0; /* Enable TDMa functions */
-	immap->im_ioport.iop_padir |= 0x00c0; /* Enable TDMa Tx/Rx */
-	immap->im_ioport.iop_padir &= ~0x0100; /* Enable L1RCLKa */
-
-	immap->im_ioport.iop_pcpar |= 0x0800; /* Enable L1RSYNCa */
-	immap->im_ioport.iop_pcdir &= ~0x0800;
-
-	/* Initialize the SI TDM routing table.  We use TDMa only.
-	 * The receive table and transmit table each have only one
-	 * entry, to capture/send four bytes after each frame pulse.
-	 * The 16-bit ram entry is 0000 0001 1000 1111. (SMC2)
-	 */
-	cp->cp_sigmr = 0;
-	sirp = (uint *)cp->cp_siram;
-
-	*sirp = 0x018f0000;		/* Receive entry */
-	sirp += 64;
-	*sirp = 0x018f0000;		/* Tramsmit entry */
-
-	/* Enable single TDMa routing.
-	*/
-	cp->cp_sigmr = 0x04;
-
-	/* Initialize the SMC for transparent operation.
-	*/
-	sp = &cpmp->cp_smc[1];
-	up = (smc_uart_t *)&cp->cp_dparam[PROFF_SMC2];
-
-	/* We need to allocate a transmit and receive buffer
-	 * descriptors from dual port ram.
-	 */
-	dp_addr = cpm_dpalloc(sizeof(cbd_t) * numReadBufs, 8);
-
-	/* Set the physical address of the host memory
-	 * buffers in the buffer descriptors, and the
-	 * virtual address for us to work with.
-	 */
-	bdp = (cbd_t *)&cp->cp_dpmem[dp_addr];
-	up->smc_rbase = dp_offset;
-	rx_cur = rx_base = (cbd_t *)bdp;
-
-	for (i=0; i<(numReadBufs-1); i++) {
-		bdp->cbd_bufaddr = 0;
-		bdp->cbd_datlen = 0;
-		bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT;
-		bdp++;
-	}
-	bdp->cbd_bufaddr = 0;
-	bdp->cbd_datlen = 0;
-	bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT;
-
-	/* Now, do the same for the transmit buffers.
-	*/
-	dp_offset = cpm_dpalloc(sizeof(cbd_t) * numBufs, 8);
-
-	bdp = (cbd_t *)&cp->cp_dpmem[dp_addr];
-	up->smc_tbase = dp_offset;
-	tx_cur = tx_base = (cbd_t *)bdp;
-
-	for (i=0; i<(numBufs-1); i++) {
-		bdp->cbd_bufaddr = 0;
-		bdp->cbd_datlen = 0;
-		bdp->cbd_sc = BD_SC_INTRPT;
-		bdp++;
-	}
-	bdp->cbd_bufaddr = 0;
-	bdp->cbd_datlen = 0;
-	bdp->cbd_sc = (BD_SC_WRAP | BD_SC_INTRPT);
-
-	/* Set transparent SMC mode.
-	 * A few things are specific to our application.  The codec interface
-	 * is MSB first, hence the REVD selection.  The CD/CTS pulse are
-	 * used by the TSA to indicate the frame start to the SMC.
-	 */
-	up->smc_rfcr = SCC_EB;
-	up->smc_tfcr = SCC_EB;
-	up->smc_mrblr = readbufSize * 1024;
-
-	/* Set 16-bit reversed data, transparent mode.
-	*/
-	sp->smc_smcmr = smcr_mk_clen(15) |
-		SMCMR_SM_TRANS | SMCMR_REVD | SMCMR_BS;
-
-	/* Enable and clear events.
-	 * Because of FIFO delays, all we need is the receive interrupt
-	 * and we can process both the current receive and current
-	 * transmit interrupt within a few microseconds of the transmit.
-	 */
-	sp->smc_smce = 0xff;
-	sp->smc_smcm = SMCM_TXE | SMCM_TX | SMCM_RX;
-
-	/* Send the CPM an initialize command.
-	*/
-	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2,
-				CPM_CR_INIT_TRX) | CPM_CR_FLG;
-	while (cp->cp_cpcr & CPM_CR_FLG);
-
-	sound.mach = mach_cs4218;
-	has_sound = 1;
-
-	/* Initialize beep stuff */
-	orig_mksound = kd_mksound;
-	kd_mksound = cs_mksound;
-	beep_buf = kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL);
-	if (beep_buf == NULL)
-		printk(KERN_WARNING "dmasound: no memory for "
-		       "beep buffer\n");
-
-	if (!has_sound)
-		return -ENODEV;
-
-	/* Initialize the software SPI.
-	*/
-	sw_spi_init();
-
-	/* Set up sound queue, /dev/audio and /dev/dsp. */
-
-	/* Set default settings. */
-	sq_init();
-
-	/* Set up /dev/sndstat. */
-	state_init();
-
-	/* Set up /dev/mixer. */
-	mixer_init();
-
-	if (!sound.mach.irqinit()) {
-		printk(KERN_ERR "DMA sound driver: Interrupt initialization failed\n");
-		return -ENODEV;
-	}
-#ifdef MODULE
-	irq_installed = 1;
-#endif
-
-	printk(KERN_INFO "DMA sound driver installed, using %d buffers of %dk.\n",
-	       numBufs, bufSize);
-
-	return 0;
-}
-
-/* Due to FIFOs and bit delays, the transmit interrupt occurs a few
- * microseconds ahead of the receive interrupt.
- * When we get an interrupt, we service the transmit first, then
- * check for a receive to prevent the overhead of returning through
- * the interrupt handler only to get back here right away during
- * full duplex operation.
- */
-static void
-cs4218_intr(void *dev_id)
-{
-	volatile smc_t	*sp;
-	volatile cpm8xx_t	*cp;
-
-	sp = &cpmp->cp_smc[1];
-
-	if (sp->smc_smce & SCCM_TX) {
-		sp->smc_smce = SCCM_TX;
-		cs4218_tdm_tx_intr((void *)sp);
-	}
-
-	if (sp->smc_smce & SCCM_RX) {
-		sp->smc_smce = SCCM_RX;
-		cs4218_tdm_rx_intr((void *)sp);
-	}
-
-	if (sp->smc_smce & SCCM_TXE) {
-		/* Transmit underrun.  This happens with the application
-		 * didn't keep up sending buffers.  We tell the SMC to
-		 * restart, which will cause it to poll the current (next)
-		 * BD.  If the user supplied data since this occurred,
-		 * we just start running again.  If they didn't, the SMC
-		 * will poll the descriptor until data is placed there.
-		 */
-		sp->smc_smce = SCCM_TXE;
-		cp = cpmp;	/* Get pointer to Communication Processor */
-		cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2,
-					CPM_CR_RESTART_TX) | CPM_CR_FLG;
-		while (cp->cp_cpcr & CPM_CR_FLG);
-	}
-}
-
-
-#define MAXARGS		8	/* Should be sufficient for now */
-
-void __init dmasound_setup(char *str, int *ints)
-{
-	/* check the bootstrap parameter for "dmasound=" */
-
-	switch (ints[0]) {
-	case 3:
-		if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS))
-			printk("dmasound_setup: invalid catch radius, using default = %d\n", catchRadius);
-		else
-			catchRadius = ints[3];
-		/* fall through */
-	case 2:
-		if (ints[1] < MIN_BUFFERS)
-			printk("dmasound_setup: invalid number of buffers, using default = %d\n", numBufs);
-		else
-			numBufs = ints[1];
-		if (ints[2] < MIN_BUFSIZE || ints[2] > MAX_BUFSIZE)
-			printk("dmasound_setup: invalid buffer size, using default = %d\n", bufSize);
-		else
-			bufSize = ints[2];
-		break;
-	case 0:
-		break;
-	default:
-		printk("dmasound_setup: invalid number of arguments\n");
-	}
-}
-
-/* Software SPI functions.
- * These are on Port B.
- */
-#define PB_SPICLK	((uint)0x00000002)
-#define PB_SPIMOSI	((uint)0x00000004)
-#define PB_SPIMISO	((uint)0x00000008)
-
-static
-void	sw_spi_init(void)
-{
-	volatile cpm8xx_t	*cp;
-	volatile uint		*hcsr4;
-
-	hcsr4 = (volatile uint *)HIOX_CSR4_ADDR;
-	cp = cpmp;	/* Get pointer to Communication Processor */
-
-	*hcsr4 &= ~HIOX_CSR4_AUDSPISEL;	/* Disable SPI select */
-
-	/* Make these Port B signals general purpose I/O.
-	 * First, make sure the clock is low.
-	 */
-	cp->cp_pbdat &= ~PB_SPICLK;
-	cp->cp_pbpar &= ~(PB_SPICLK | PB_SPIMOSI | PB_SPIMISO);
-
-	/* Clock and Master Output are outputs.
-	*/
-	cp->cp_pbdir |= (PB_SPICLK | PB_SPIMOSI);
-
-	/* Master Input.
-	*/
-	cp->cp_pbdir &= ~PB_SPIMISO;
-
-}
-
-/* Write the CS4218 control word out the SPI port.  While the
- * the control word is going out, the status word is arriving.
- */
-static
-uint	cs4218_ctl_write(uint ctlreg)
-{
-	uint	status;
-
-	sw_spi_io((u_char *)&ctlreg, (u_char *)&status, 4);
-
-	/* Shadow the control register.....I guess we could do
-	 * the same for the status, but for now we just return it
-	 * and let the caller decide.
-	 */
-	cs4218_control = ctlreg;
-	return status;
-}
-
-static
-void	sw_spi_io(u_char *obuf, u_char *ibuf, uint bcnt)
-{
-	int	bits, i;
-	u_char	outbyte, inbyte;
-	volatile cpm8xx_t	*cp;
-	volatile uint		*hcsr4;
-
-	hcsr4 = (volatile uint *)HIOX_CSR4_ADDR;
-	cp = cpmp;	/* Get pointer to Communication Processor */
-
-	/* The timing on the bus is pretty slow.  Code inefficiency
-	 * and eieio() is our friend here :-).
-	 */
-	cp->cp_pbdat &= ~PB_SPICLK;
-	*hcsr4 |= HIOX_CSR4_AUDSPISEL;	/* Enable SPI select */
-	eieio();
-
-	/* Clock in/out the bytes.  Data is valid on the falling edge
-	 * of the clock.  Data is MSB first.
-	 */
-	for (i=0; i<bcnt; i++) {
-		outbyte = *obuf++;
-		inbyte = 0;
-		for (bits=0; bits<8; bits++) {
-			eieio();
-			cp->cp_pbdat |= PB_SPICLK;
-			eieio();
-			if (outbyte & 0x80)
-				cp->cp_pbdat |= PB_SPIMOSI;
-			else
-				cp->cp_pbdat &= ~PB_SPIMOSI;
-			eieio();
-			cp->cp_pbdat &= ~PB_SPICLK;
-			eieio();
-			outbyte <<= 1;
-			inbyte <<= 1;
-			if (cp->cp_pbdat & PB_SPIMISO)
-				inbyte |= 1;
-		}
-		*ibuf++ = inbyte;
-	}
-
-	*hcsr4 &= ~HIOX_CSR4_AUDSPISEL;	/* Disable SPI select */
-	eieio();
-}
-
-void cleanup_module(void)
-{
-	if (irq_installed) {
-		sound_silence();
-#ifdef MODULE
-		sound.mach.irqcleanup();
-#endif
-	}
-
-	sq_release_read_buffers();
-	sq_release_buffers();
-
-	if (mixer_unit >= 0)
-		unregister_sound_mixer(mixer_unit);
-	if (state_unit >= 0)
-		unregister_sound_special(state_unit);
-	if (sq_unit >= 0)
-		unregister_sound_dsp(sq_unit);
-}
-
-module_init(tdm8xx_sound_init);
-module_exit(cleanup_module);
-
diff --git a/arch/ppc/8xx_io/enet.c b/arch/ppc/8xx_io/enet.c
index b23c45b..bfa3f52 100644
--- a/arch/ppc/8xx_io/enet.c
+++ b/arch/ppc/8xx_io/enet.c
@@ -506,7 +506,6 @@
 			cep->stats.rx_dropped++;
 		}
 		else {
-			skb->dev = dev;
 			skb_put(skb,pkt_len-4);	/* Make room */
 			eth_copy_and_sum(skb,
 				cep->rx_vaddr[bdp - cep->rx_bd_base],
diff --git a/arch/ppc/8xx_io/fec.c b/arch/ppc/8xx_io/fec.c
index e6c28fb..57a9a61 100644
--- a/arch/ppc/8xx_io/fec.c
+++ b/arch/ppc/8xx_io/fec.c
@@ -724,7 +724,6 @@
 		printk("%s: Memory squeeze, dropping packet.\n", dev->name);
 		fep->stats.rx_dropped++;
 	} else {
-		skb->dev = dev;
 		skb_put(skb,pkt_len-4);	/* Make room */
 		eth_copy_and_sum(skb, data, pkt_len-4, 0);
 		skb->protocol=eth_type_trans(skb,dev);
diff --git a/arch/ppc/boot/common/misc-common.c b/arch/ppc/boot/common/misc-common.c
index 8e1fccd..9589969 100644
--- a/arch/ppc/boot/common/misc-common.c
+++ b/arch/ppc/boot/common/misc-common.c
@@ -57,7 +57,8 @@
 
 #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \
 	|| defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \
-	|| defined(CONFIG_SERIAL_MPSC_CONSOLE)
+	|| defined(CONFIG_SERIAL_MPSC_CONSOLE) \
+	|| defined(CONFIG_SERIAL_UARTLITE_CONSOLE)
 extern unsigned long com_port;
 
 extern int serial_tstc(unsigned long com_port);
@@ -80,7 +81,8 @@
 {
 #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \
 	|| defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \
-	|| defined(CONFIG_SERIAL_MPSC_CONSOLE)
+	|| defined(CONFIG_SERIAL_MPSC_CONSOLE) \
+	|| defined(CONFIG_SERIAL_UARTLITE_CONSOLE)
 	if(keyb_present)
 		return (CRT_tstc() || serial_tstc(com_port));
 	else
@@ -95,7 +97,8 @@
 	while (1) {
 #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \
 	|| defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \
-	|| defined(CONFIG_SERIAL_MPSC_CONSOLE)
+	|| defined(CONFIG_SERIAL_MPSC_CONSOLE) \
+	|| defined(CONFIG_SERIAL_UARTLITE_CONSOLE)
 		if (serial_tstc(com_port))
 			return (serial_getc(com_port));
 #endif /* serial console */
@@ -112,7 +115,8 @@
 
 #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \
 	|| defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \
-	|| defined(CONFIG_SERIAL_MPSC_CONSOLE)
+	|| defined(CONFIG_SERIAL_MPSC_CONSOLE) \
+	|| defined(CONFIG_SERIAL_UARTLITE_CONSOLE)
 	serial_putc(com_port, c);
 	if ( c == '\n' )
 		serial_putc(com_port, '\r');
@@ -161,7 +165,8 @@
 	while ( ( c = *s++ ) != '\0' ) {
 #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \
 	|| defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \
-	|| defined(CONFIG_SERIAL_MPSC_CONSOLE)
+	|| defined(CONFIG_SERIAL_MPSC_CONSOLE) \
+	|| defined(CONFIG_SERIAL_UARTLITE_CONSOLE)
 	        serial_putc(com_port, c);
 	        if ( c == '\n' ) serial_putc(com_port, '\r');
 #endif /* serial console */
diff --git a/arch/ppc/boot/simple/Makefile b/arch/ppc/boot/simple/Makefile
index bcfb6cd..5b87779 100644
--- a/arch/ppc/boot/simple/Makefile
+++ b/arch/ppc/boot/simple/Makefile
@@ -201,6 +201,7 @@
 endif
 boot-$(CONFIG_SERIAL_MPC52xx_CONSOLE)	+= mpc52xx_tty.o
 boot-$(CONFIG_SERIAL_MPSC_CONSOLE)	+= mv64x60_tty.o
+boot-$(CONFIG_SERIAL_UARTLITE_CONSOLE)	+= uartlite_tty.o
 
 LIBS				:= $(common)/lib.a $(bootlib)/lib.a
 ifeq ($(CONFIG_PPC_PREP),y)
diff --git a/arch/ppc/boot/simple/uartlite_tty.c b/arch/ppc/boot/simple/uartlite_tty.c
new file mode 100644
index 0000000..0eae1ea
--- /dev/null
+++ b/arch/ppc/boot/simple/uartlite_tty.c
@@ -0,0 +1,37 @@
+/*
+ * Xilinx UARTLITE bootloader driver
+ *
+ * Copyright (c) 2007 Secret Lab Technologies Ltd.
+ *
+ * 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.
+ */
+
+#include <linux/types.h>
+#include <asm/serial.h>
+#include <asm/io.h>
+#include <platforms/4xx/xparameters/xparameters.h>
+
+#define UARTLITE_BASEADDR ((void*)(XPAR_UARTLITE_0_BASEADDR))
+
+void
+serial_putc(unsigned long com_port, unsigned char c)
+{
+	while ((in_be32(UARTLITE_BASEADDR + 0x8) & 0x08) != 0); /* spin */
+	out_be32(UARTLITE_BASEADDR + 0x4, c);
+}
+
+unsigned char
+serial_getc(unsigned long com_port)
+{
+	while ((in_be32(UARTLITE_BASEADDR + 0x8) & 0x01) == 0); /* spin */
+	return in_be32(UARTLITE_BASEADDR);
+}
+
+int
+serial_tstc(unsigned long com_port)
+{
+	return ((in_be32(UARTLITE_BASEADDR + 0x8) & 0x01) != 0);
+}
diff --git a/arch/ppc/kernel/asm-offsets.c b/arch/ppc/kernel/asm-offsets.c
index 1f91eca..c5850a2 100644
--- a/arch/ppc/kernel/asm-offsets.c
+++ b/arch/ppc/kernel/asm-offsets.c
@@ -40,7 +40,6 @@
 	DEFINE(PTRACE, offsetof(struct task_struct, ptrace));
 	DEFINE(KSP, offsetof(struct thread_struct, ksp));
 	DEFINE(PGDIR, offsetof(struct thread_struct, pgdir));
-	DEFINE(LAST_SYSCALL, offsetof(struct thread_struct, last_syscall));
 	DEFINE(PT_REGS, offsetof(struct thread_struct, regs));
 	DEFINE(THREAD_FPEXC_MODE, offsetof(struct thread_struct, fpexc_mode));
 	DEFINE(THREAD_FPR0, offsetof(struct thread_struct, fpr[0]));
diff --git a/arch/ppc/kernel/entry.S b/arch/ppc/kernel/entry.S
index a9d4553..ab64256 100644
--- a/arch/ppc/kernel/entry.S
+++ b/arch/ppc/kernel/entry.S
@@ -191,7 +191,6 @@
 0:
 
 _GLOBAL(DoSyscall)
-	stw	r0,THREAD+LAST_SYSCALL(r2)
 	stw	r3,ORIG_GPR3(r1)
 	li	r12,0
 	stw	r12,RESULT(r1)
diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c
index 1318b6f..4ad4996 100644
--- a/arch/ppc/kernel/ppc_ksyms.c
+++ b/arch/ppc/kernel/ppc_ksyms.c
@@ -93,8 +93,6 @@
 EXPORT_SYMBOL(strcat);
 EXPORT_SYMBOL(strlen);
 EXPORT_SYMBOL(strcmp);
-EXPORT_SYMBOL(strcasecmp);
-EXPORT_SYMBOL(strncasecmp);
 EXPORT_SYMBOL(__div64_32);
 
 EXPORT_SYMBOL(csum_partial);
diff --git a/arch/ppc/lib/Makefile b/arch/ppc/lib/Makefile
index 50358e4..422bef9 100644
--- a/arch/ppc/lib/Makefile
+++ b/arch/ppc/lib/Makefile
@@ -2,7 +2,7 @@
 # Makefile for ppc-specific library files..
 #
 
-obj-y			:= checksum.o string.o strcase.o div64.o
+obj-y			:= checksum.o string.o div64.o
 
 obj-$(CONFIG_8xx)	+= rheap.o
 obj-$(CONFIG_CPM2)	+= rheap.o
diff --git a/arch/ppc/lib/strcase.c b/arch/ppc/lib/strcase.c
deleted file mode 100644
index 3b0094c..0000000
--- a/arch/ppc/lib/strcase.c
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <linux/ctype.h>
-#include <linux/types.h>
-
-int strcasecmp(const char *s1, const char *s2)
-{
-	int c1, c2;
-
-	do {
-		c1 = tolower(*s1++);
-		c2 = tolower(*s2++);
-	} while (c1 == c2 && c1 != 0);
-	return c1 - c2;
-}
-
-int strncasecmp(const char *s1, const char *s2, size_t n)
-{
-	int c1, c2;
-
-	do {
-		c1 = tolower(*s1++);
-		c2 = tolower(*s2++);
-	} while ((--n > 0) && c1 == c2 && c1 != 0);
-	return c1 - c2;
-}
diff --git a/arch/ppc/platforms/4xx/Kconfig b/arch/ppc/platforms/4xx/Kconfig
index 705ae56..76551b6 100644
--- a/arch/ppc/platforms/4xx/Kconfig
+++ b/arch/ppc/platforms/4xx/Kconfig
@@ -29,6 +29,7 @@
 
 config EP405
 	bool "EP405/EP405PC"
+	select EMBEDDEDBOOT
 	help
 	  This option enables support for the EP405/EP405PC boards.
 
@@ -54,11 +55,15 @@
 
 config XILINX_ML300
 	bool "Xilinx-ML300"
+	select XILINX_VIRTEX_II_PRO
+	select EMBEDDEDBOOT
 	help
 	  This option enables support for the Xilinx ML300 evaluation board.
 
 config XILINX_ML403
 	bool "Xilinx-ML403"
+	select XILINX_VIRTEX_4_FX
+	select EMBEDDEDBOOT
 	help
 	  This option enables support for the Xilinx ML403 evaluation board.
 endchoice
@@ -215,18 +220,14 @@
 
 config XILINX_VIRTEX_II_PRO
 	bool
-	depends on XILINX_ML300
-	default y
+	select XILINX_VIRTEX
 
 config XILINX_VIRTEX_4_FX
 	bool
-	depends on XILINX_ML403
-	default y
+	select XILINX_VIRTEX
 
 config XILINX_VIRTEX
 	bool
-	depends on XILINX_VIRTEX_II_PRO || XILINX_VIRTEX_4_FX
-	default y
 
 config STB03xxx
 	bool
@@ -235,8 +236,6 @@
 
 config EMBEDDEDBOOT
 	bool
-	depends on EP405 || XILINX_ML300 || XILINX_ML403
-	default y
 
 config IBM_OPENBIOS
 	bool
diff --git a/arch/ppc/platforms/4xx/Makefile b/arch/ppc/platforms/4xx/Makefile
index fa6610b..723ad79 100644
--- a/arch/ppc/platforms/4xx/Makefile
+++ b/arch/ppc/platforms/4xx/Makefile
@@ -28,5 +28,4 @@
 obj-$(CONFIG_440SPE)		+= ppc440spe.o
 obj-$(CONFIG_405EP)		+= ibm405ep.o
 obj-$(CONFIG_405GPR)		+= ibm405gpr.o
-obj-$(CONFIG_XILINX_VIRTEX)	+= virtex.o
 
diff --git a/arch/ppc/platforms/4xx/ocotea.c b/arch/ppc/platforms/4xx/ocotea.c
index 84e999d..5e994e1 100644
--- a/arch/ppc/platforms/4xx/ocotea.c
+++ b/arch/ppc/platforms/4xx/ocotea.c
@@ -178,7 +178,7 @@
 	/* Setup 2GB PCI->PLB inbound memory window at 0, enable MSIs */
 	PCIX_WRITEL(0x00000000, PCIX0_PIM0LAH);
 	PCIX_WRITEL(0x00000000, PCIX0_PIM0LAL);
-	PCIX_WRITEL(0xe0000007, PCIX0_PIM0SA);
+	PCIX_WRITEL(0x80000007, PCIX0_PIM0SA);
 
 	eieio();
 }
@@ -289,7 +289,7 @@
 	 * from FPGA, because it can be changed by on-board switches
 	 * --ebs
 	 */
-	ibm440gx_get_clocks(&clocks, 33333333, 6 * 1843200);
+	ibm440gx_get_clocks(&clocks, 33300000, 6 * 1843200);
 	ocp_sys_info.opb_bus_freq = clocks.opb;
 
 	/* Setup TODC access */
diff --git a/arch/ppc/platforms/4xx/taishan.c b/arch/ppc/platforms/4xx/taishan.c
index bb0253e..5d9af8d 100644
--- a/arch/ppc/platforms/4xx/taishan.c
+++ b/arch/ppc/platforms/4xx/taishan.c
@@ -235,7 +235,7 @@
 	/* Setup 2GB PCI->PLB inbound memory window at 0, enable MSIs */
 	PCIX_WRITEL(0x00000000, PCIX0_PIM0LAH);
 	PCIX_WRITEL(0x00000000, PCIX0_PIM0LAL);
-	PCIX_WRITEL(0xe0000007, PCIX0_PIM0SA);
+	PCIX_WRITEL(0x80000007, PCIX0_PIM0SA);
 	PCIX_WRITEL(0xffffffff, PCIX0_PIM0SAH);
 
 	iounmap(pcix_reg_base);
diff --git a/arch/ppc/platforms/4xx/virtex.c b/arch/ppc/platforms/4xx/virtex.c
deleted file mode 100644
index 133a831..0000000
--- a/arch/ppc/platforms/4xx/virtex.c
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Virtex-II Pro & Virtex-4 FX common infrastructure
- *
- * Maintainer: Grant Likely <grant.likely@secretlab.ca>
- *
- * Copyright 2005 Secret Lab Technologies Ltd.
- * Copyright 2005 General Dynamics Canada Ltd.
- * Copyright 2005 Freescale Semiconductor Inc.
- *
- * 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.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/serial_8250.h>
-#include <asm/ppc_sys.h>
-#include <platforms/4xx/virtex.h>
-#include <platforms/4xx/xparameters/xparameters.h>
-
-#define XPAR_UART(num) { \
-		.mapbase  = XPAR_UARTNS550_##num##_BASEADDR + 3, \
-		.irq	  = XPAR_INTC_0_UARTNS550_##num##_VEC_ID, \
-		.iotype	  = UPIO_MEM, \
-		.uartclk  = XPAR_UARTNS550_##num##_CLOCK_FREQ_HZ, \
-		.flags	  = UPF_BOOT_AUTOCONF, \
-		.regshift = 2, \
-	}
-
-struct plat_serial8250_port serial_platform_data[] = {
-#ifdef XPAR_UARTNS550_0_BASEADDR
-	XPAR_UART(0),
-#endif
-#ifdef XPAR_UARTNS550_1_BASEADDR
-	XPAR_UART(1),
-#endif
-#ifdef XPAR_UARTNS550_2_BASEADDR
-	XPAR_UART(2),
-#endif
-#ifdef XPAR_UARTNS550_3_BASEADDR
-	XPAR_UART(3),
-#endif
-	{ }, /* terminated by empty record */
-};
-
-struct platform_device ppc_sys_platform_devices[] = {
-	[VIRTEX_UART] = {
-		.name		= "serial8250",
-		.id		= 0,
-		.dev.platform_data = serial_platform_data,
-	},
-};
-
diff --git a/arch/ppc/platforms/4xx/virtex.h b/arch/ppc/platforms/4xx/virtex.h
index c14325d..7382804 100644
--- a/arch/ppc/platforms/4xx/virtex.h
+++ b/arch/ppc/platforms/4xx/virtex.h
@@ -1,35 +1,35 @@
 /*
- * arch/ppc/platforms/4xx/virtex.h
+ * Basic Virtex platform defines, included by <asm/ibm4xx.h>
  *
- * Include file that defines the Xilinx Virtex-II Pro processor
+ * 2005-2007 (c) Secret Lab Technologies Ltd.
+ * 2002-2004 (c) MontaVista Software, Inc.
  *
- * Author: MontaVista Software, Inc.
- *         source@mvista.com
- *
- * 2002-2004 (c) MontaVista Software, Inc.  This file is licensed under the
- * terms of the GNU General Public License version 2.  This program is licensed
- * "as is" without any warranty of any kind, whether express or implied.
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2.  This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
  */
 
 #ifdef __KERNEL__
 #ifndef __ASM_VIRTEX_H__
 #define __ASM_VIRTEX_H__
 
-/* serial defines */
-
 #include <asm/ibm405.h>
+#include <asm/ppcboot.h>
 
 /* Ugly, ugly, ugly! BASE_BAUD defined here to keep 8250.c happy. */
 #if !defined(BASE_BAUD)
  #define BASE_BAUD		(0) /* dummy value; not used */
 #endif
-  
-/* Device type enumeration for platform bus definitions */
+
 #ifndef __ASSEMBLY__
-enum ppc_sys_devices {
-	VIRTEX_UART, NUM_PPC_SYS_DEVS,
-};
-#endif
-  
+extern const char* virtex_machine_name;
+#define PPC4xx_MACHINE_NAME (virtex_machine_name)
+#endif /* !__ASSEMBLY__ */
+
+/* We don't need anything mapped.  Size of zero will accomplish that. */
+#define PPC4xx_ONB_IO_PADDR	0u
+#define PPC4xx_ONB_IO_VADDR	0u
+#define PPC4xx_ONB_IO_SIZE	0u
+
 #endif				/* __ASM_VIRTEX_H__ */
 #endif				/* __KERNEL__ */
diff --git a/arch/ppc/platforms/4xx/xilinx_ml300.c b/arch/ppc/platforms/4xx/xilinx_ml300.c
index fb5f0b5..6e522fe 100644
--- a/arch/ppc/platforms/4xx/xilinx_ml300.c
+++ b/arch/ppc/platforms/4xx/xilinx_ml300.c
@@ -18,9 +18,9 @@
 #include <linux/serialP.h>
 #include <asm/io.h>
 #include <asm/machdep.h>
-#include <asm/ppc_sys.h>
 
 #include <syslib/gen550.h>
+#include <syslib/virtex_devices.h>
 #include <platforms/4xx/xparameters/xparameters.h>
 
 /*
@@ -53,24 +53,9 @@
  *          ppc4xx_pic_init			arch/ppc/syslib/xilinx_pic.c
  */
 
-/* Board specifications structures */
-struct ppc_sys_spec *cur_ppc_sys_spec;
-struct ppc_sys_spec ppc_sys_specs[] = {
-	{
-		/* Only one entry, always assume the same design */
-		.ppc_sys_name	= "Xilinx ML300 Reference Design",
-		.mask 		= 0x00000000,
-		.value 		= 0x00000000,
-		.num_devices	= 1,
-		.device_list	= (enum ppc_sys_devices[])
-		{
-			VIRTEX_UART,
-		},
-	},
-};
+const char* virtex_machine_name = "ML300 Reference Design";
 
 #if defined(XPAR_POWER_0_POWERDOWN_BASEADDR)
-
 static volatile unsigned *powerdown_base =
     (volatile unsigned *) XPAR_POWER_0_POWERDOWN_BASEADDR;
 
@@ -95,52 +80,14 @@
 #endif
 }
 
-/* Early serial support functions */
-static void __init
-ml300_early_serial_init(int num, struct plat_serial8250_port *pdata)
-{
-#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
-	struct uart_port serial_req;
-
-	memset(&serial_req, 0, sizeof(serial_req));
-	serial_req.mapbase	= pdata->mapbase;
-	serial_req.membase	= pdata->membase;
-	serial_req.irq		= pdata->irq;
-	serial_req.uartclk	= pdata->uartclk;
-	serial_req.regshift	= pdata->regshift;
-	serial_req.iotype	= pdata->iotype;
-	serial_req.flags	= pdata->flags;
-	gen550_init(num, &serial_req);
-#endif
-}
-
-void __init
-ml300_early_serial_map(void)
-{
-#ifdef CONFIG_SERIAL_8250
-	struct plat_serial8250_port *pdata;
-	int i = 0;
-
-	pdata = (struct plat_serial8250_port *) ppc_sys_get_pdata(VIRTEX_UART);
-	while(pdata && pdata->flags)
-	{
-		pdata->membase = ioremap(pdata->mapbase, 0x100);
-		ml300_early_serial_init(i, pdata);
-		pdata++;
-		i++;
-	}
-#endif /* CONFIG_SERIAL_8250 */
-}
-
 void __init
 ml300_setup_arch(void)
 {
-	ml300_early_serial_map();
+	virtex_early_serial_map();
 	ppc4xx_setup_arch();	/* calls ppc4xx_find_bridges() */
 
 	/* Identify the system */
-	printk(KERN_INFO "Xilinx Virtex-II Pro port\n");
-	printk(KERN_INFO "Port by MontaVista Software, Inc. (source@mvista.com)\n");
+	printk(KERN_INFO "Xilinx ML300 Reference System (Virtex-II Pro)\n");
 }
 
 /* Called after board_setup_irq from ppc4xx_init_IRQ(). */
@@ -156,8 +103,6 @@
 {
 	ppc4xx_init(r3, r4, r5, r6, r7);
 
-	identify_ppc_sys_by_id(mfspr(SPRN_PVR));
-
 	ppc_md.setup_arch = ml300_setup_arch;
 	ppc_md.setup_io_mappings = ml300_map_io;
 	ppc_md.init_IRQ = ml300_init_irq;
@@ -167,7 +112,7 @@
 #endif
 
 #ifdef CONFIG_KGDB
-	ppc_md.early_serial_map = ml300_early_serial_map;
+	ppc_md.early_serial_map = virtex_early_serial_map;
 #endif
 }
 
diff --git a/arch/ppc/platforms/4xx/xilinx_ml300.h b/arch/ppc/platforms/4xx/xilinx_ml300.h
deleted file mode 100644
index 3d57332..0000000
--- a/arch/ppc/platforms/4xx/xilinx_ml300.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Include file that defines the Xilinx ML300 evaluation board
- *
- * Author: MontaVista Software, Inc.
- *         source@mvista.com
- *
- * 2002-2004 (c) MontaVista Software, Inc.  This file is licensed under the
- * terms of the GNU General Public License version 2.  This program is licensed
- * "as is" without any warranty of any kind, whether express or implied.
- */
-
-#ifdef __KERNEL__
-#ifndef __ASM_XILINX_ML300_H__
-#define __ASM_XILINX_ML300_H__
-
-/* ML300 has a Xilinx Virtex-II Pro processor */
-#include <platforms/4xx/virtex.h>
-
-#ifndef __ASSEMBLY__
-
-#include <linux/types.h>
-
-typedef struct board_info {
-	unsigned int	 bi_memsize;		/* DRAM installed, in bytes */
-	unsigned char	 bi_enetaddr[6];	/* Local Ethernet MAC address */
-	unsigned int	 bi_intfreq;		/* Processor speed, in Hz */
-	unsigned int	 bi_busfreq;		/* PLB Bus speed, in Hz */
-	unsigned int	 bi_pci_busfreq;	/* PCI Bus speed, in Hz */
-} bd_t;
-
-/* Some 4xx parts use a different timebase frequency from the internal clock.
-*/
-#define bi_tbfreq bi_intfreq
-
-#endif /* !__ASSEMBLY__ */
-
-/* We don't need anything mapped.  Size of zero will accomplish that. */
-#define PPC4xx_ONB_IO_PADDR	0u
-#define PPC4xx_ONB_IO_VADDR	0u
-#define PPC4xx_ONB_IO_SIZE	0u
-
-#define PPC4xx_MACHINE_NAME "Xilinx ML300 Reference System"
-
-#endif /* __ASM_XILINX_ML300_H__ */
-#endif /* __KERNEL__ */
diff --git a/arch/ppc/platforms/4xx/xilinx_ml403.c b/arch/ppc/platforms/4xx/xilinx_ml403.c
index cb3bf7a..bc3ace3 100644
--- a/arch/ppc/platforms/4xx/xilinx_ml403.c
+++ b/arch/ppc/platforms/4xx/xilinx_ml403.c
@@ -1,11 +1,9 @@
 /*
- * arch/ppc/platforms/4xx/xilinx_ml403.c
- *
  * Xilinx ML403 evaluation board initialization
  *
  * Author: Grant Likely <grant.likely@secretlab.ca>
  *
- * 2005 (c) Secret Lab Technologies Ltd.
+ * 2005-2007 (c) Secret Lab Technologies Ltd.
  * 2002-2004 (c) MontaVista Software, Inc.
  *
  * This file is licensed under the terms of the GNU General Public License
@@ -22,9 +20,9 @@
 #include <linux/serialP.h>
 #include <asm/io.h>
 #include <asm/machdep.h>
-#include <asm/ppc_sys.h>
 
 #include <syslib/gen550.h>
+#include <syslib/virtex_devices.h>
 #include <platforms/4xx/xparameters/xparameters.h>
 
 /*
@@ -57,24 +55,9 @@
  *          ppc4xx_pic_init			arch/ppc/syslib/xilinx_pic.c
  */
 
-/* Board specifications structures */
-struct ppc_sys_spec *cur_ppc_sys_spec;
-struct ppc_sys_spec ppc_sys_specs[] = {
-	{
-		/* Only one entry, always assume the same design */
-		.ppc_sys_name	= "Xilinx ML403 Reference Design",
-		.mask 		= 0x00000000,
-		.value 		= 0x00000000,
-		.num_devices	= 1,
-		.device_list	= (enum ppc_sys_devices[])
-		{
-			VIRTEX_UART,
-		},
-	},
-};
+const char* virtex_machine_name = "ML403 Reference Design";
 
 #if defined(XPAR_POWER_0_POWERDOWN_BASEADDR)
-
 static volatile unsigned *powerdown_base =
     (volatile unsigned *) XPAR_POWER_0_POWERDOWN_BASEADDR;
 
@@ -99,47 +82,10 @@
 #endif
 }
 
-/* Early serial support functions */
-static void __init
-ml403_early_serial_init(int num, struct plat_serial8250_port *pdata)
-{
-#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
-	struct uart_port serial_req;
-
-	memset(&serial_req, 0, sizeof(serial_req));
-	serial_req.mapbase	= pdata->mapbase;
-	serial_req.membase	= pdata->membase;
-	serial_req.irq		= pdata->irq;
-	serial_req.uartclk	= pdata->uartclk;
-	serial_req.regshift	= pdata->regshift;
-	serial_req.iotype	= pdata->iotype;
-	serial_req.flags	= pdata->flags;
-	gen550_init(num, &serial_req);
-#endif
-}
-
-void __init
-ml403_early_serial_map(void)
-{
-#ifdef CONFIG_SERIAL_8250
-	struct plat_serial8250_port *pdata;
-	int i = 0;
-
-	pdata = (struct plat_serial8250_port *) ppc_sys_get_pdata(VIRTEX_UART);
-	while(pdata && pdata->flags)
-	{
-		pdata->membase = ioremap(pdata->mapbase, 0x100);
-		ml403_early_serial_init(i, pdata);
-		pdata++;
-		i++;
-	}
-#endif /* CONFIG_SERIAL_8250 */
-}
-
 void __init
 ml403_setup_arch(void)
 {
-	ml403_early_serial_map();
+	virtex_early_serial_map();
 	ppc4xx_setup_arch();	/* calls ppc4xx_find_bridges() */
 
 	/* Identify the system */
@@ -159,8 +105,6 @@
 {
 	ppc4xx_init(r3, r4, r5, r6, r7);
 
-	identify_ppc_sys_by_id(mfspr(SPRN_PVR));
-
 	ppc_md.setup_arch = ml403_setup_arch;
 	ppc_md.setup_io_mappings = ml403_map_io;
 	ppc_md.init_IRQ = ml403_init_irq;
@@ -170,7 +114,7 @@
 #endif
 
 #ifdef CONFIG_KGDB
-	ppc_md.early_serial_map = ml403_early_serial_map;
+	ppc_md.early_serial_map = virtex_early_serial_map;
 #endif
 }
 
diff --git a/arch/ppc/platforms/4xx/xilinx_ml403.h b/arch/ppc/platforms/4xx/xilinx_ml403.h
deleted file mode 100644
index 4735969..0000000
--- a/arch/ppc/platforms/4xx/xilinx_ml403.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * arch/ppc/platforms/4xx/xilinx_ml403.h
- *
- * Include file that defines the Xilinx ML403 reference design
- *
- * Author: Grant Likely <grant.likely@secretlab.ca>
- *
- * 2005 (c) Secret Lab Technologies Ltd.
- * 2002-2004 (c) MontaVista Software, Inc.
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2.  This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- */
-
-#ifdef __KERNEL__
-#ifndef __ASM_XILINX_ML403_H__
-#define __ASM_XILINX_ML403_H__
-
-/* ML403 has a Xilinx Virtex-4 FPGA with a PPC405 hard core */
-#include <platforms/4xx/virtex.h>
-
-#ifndef __ASSEMBLY__
-
-#include <linux/types.h>
-
-typedef struct board_info {
-	unsigned int	 bi_memsize;		/* DRAM installed, in bytes */
-	unsigned char	 bi_enetaddr[6];	/* Local Ethernet MAC address */
-	unsigned int	 bi_intfreq;		/* Processor speed, in Hz */
-	unsigned int	 bi_busfreq;		/* PLB Bus speed, in Hz */
-	unsigned int	 bi_pci_busfreq;	/* PCI Bus speed, in Hz */
-} bd_t;
-
-/* Some 4xx parts use a different timebase frequency from the internal clock.
-*/
-#define bi_tbfreq bi_intfreq
-
-#endif /* !__ASSEMBLY__ */
-
-/* We don't need anything mapped.  Size of zero will accomplish that. */
-#define PPC4xx_ONB_IO_PADDR	0u
-#define PPC4xx_ONB_IO_VADDR	0u
-#define PPC4xx_ONB_IO_SIZE	0u
-
-#define PPC4xx_MACHINE_NAME "Xilinx ML403 Reference Design"
-
-#endif /* __ASM_XILINX_ML403_H__ */
-#endif /* __KERNEL__ */
diff --git a/arch/ppc/platforms/4xx/xparameters/xparameters.h b/arch/ppc/platforms/4xx/xparameters/xparameters.h
index 66ec5f3..01aa043 100644
--- a/arch/ppc/platforms/4xx/xparameters/xparameters.h
+++ b/arch/ppc/platforms/4xx/xparameters/xparameters.h
@@ -34,3 +34,63 @@
 	.io_type	 = SERIAL_IO_MEM,				\
   },
 #endif
+
+/*
+ * A few reasonable defaults for the #defines which could be missing depending
+ * on the IP version or variant (e.g. OPB vs PLB)
+ */
+
+#ifndef XPAR_EMAC_0_CAM_EXIST
+#define XPAR_EMAC_0_CAM_EXIST 0
+#endif
+#ifndef XPAR_EMAC_0_JUMBO_EXIST
+#define XPAR_EMAC_0_JUMBO_EXIST 0
+#endif
+#ifndef XPAR_EMAC_0_TX_DRE_TYPE
+#define XPAR_EMAC_0_TX_DRE_TYPE 0
+#endif
+#ifndef XPAR_EMAC_0_RX_DRE_TYPE
+#define XPAR_EMAC_0_RX_DRE_TYPE 0
+#endif
+#ifndef XPAR_EMAC_0_TX_INCLUDE_CSUM
+#define XPAR_EMAC_0_TX_INCLUDE_CSUM 0
+#endif
+#ifndef XPAR_EMAC_0_RX_INCLUDE_CSUM
+#define XPAR_EMAC_0_RX_INCLUDE_CSUM 0
+#endif
+
+#ifndef XPAR_EMAC_1_CAM_EXIST
+#define XPAR_EMAC_1_CAM_EXIST 0
+#endif
+#ifndef XPAR_EMAC_1_JUMBO_EXIST
+#define XPAR_EMAC_1_JUMBO_EXIST 0
+#endif
+#ifndef XPAR_EMAC_1_TX_DRE_TYPE
+#define XPAR_EMAC_1_TX_DRE_TYPE 0
+#endif
+#ifndef XPAR_EMAC_1_RX_DRE_TYPE
+#define XPAR_EMAC_1_RX_DRE_TYPE 0
+#endif
+#ifndef XPAR_EMAC_1_TX_INCLUDE_CSUM
+#define XPAR_EMAC_1_TX_INCLUDE_CSUM 0
+#endif
+#ifndef XPAR_EMAC_1_RX_INCLUDE_CSUM
+#define XPAR_EMAC_1_RX_INCLUDE_CSUM 0
+#endif
+
+#ifndef XPAR_GPIO_0_IS_DUAL
+#define XPAR_GPIO_0_IS_DUAL 0
+#endif
+#ifndef XPAR_GPIO_1_IS_DUAL
+#define XPAR_GPIO_1_IS_DUAL 0
+#endif
+#ifndef XPAR_GPIO_2_IS_DUAL
+#define XPAR_GPIO_2_IS_DUAL 0
+#endif
+#ifndef XPAR_GPIO_3_IS_DUAL
+#define XPAR_GPIO_3_IS_DUAL 0
+#endif
+#ifndef XPAR_GPIO_4_IS_DUAL
+#define XPAR_GPIO_4_IS_DUAL 0
+#endif
+
diff --git a/arch/ppc/platforms/rpxclassic.h b/arch/ppc/platforms/rpxclassic.h
index 57a2a55..a3c1118 100644
--- a/arch/ppc/platforms/rpxclassic.h
+++ b/arch/ppc/platforms/rpxclassic.h
@@ -69,10 +69,6 @@
 #define BCSR2_QSPACESEL		((uint)0x00004000)
 #define BCSR2_FETHLEDMODE	((uint)0x00000800)	/* CLLF */
 
-#if defined(CONFIG_HTDMSOUND)
-#include <platforms/rpxhiox.h>
-#endif
-
 /* define IO_BASE for pcmcia, CLLF only */
 #if !defined(CONFIG_PCI)
 #define _IO_BASE 0x80000000
diff --git a/arch/ppc/platforms/rpxhiox.h b/arch/ppc/platforms/rpxhiox.h
deleted file mode 100644
index c3fa5a6..0000000
--- a/arch/ppc/platforms/rpxhiox.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * The Embedded Planet HIOX expansion card definitions.
- * There were a few different versions of these cards, but only
- * the one that escaped real production is defined here.
- *
- * Copyright (c) 2000 Dan Malek (dmalek@jlc.net)
- */
-#ifndef __MACH_RPX_HIOX_DEFS
-#define __MACH_RPX_HIOX_DEFS
-
-#define HIOX_CSR_ADDR		((uint)0xfac00000)
-#define HIOX_CSR_SIZE		((uint)(4 * 1024))
-#define HIOX_CSR0_ADDR		HIOX_CSR_ADDR
-#define HIOX_CSR4_ADDR		((uint)0xfac00004)
-
-#define HIOX_CSR0_DEFAULT	((uint)0x380f3c00)
-#define HIOX_CSR0_ENSCC2	((uint)0x80000000)
-#define HIOX_CSR0_ENSMC2	((uint)0x04000000)
-#define HIOX_CSR0_ENVDOCLK	((uint)0x02000000)
-#define HIOX_CSR0_VDORST_HL	((uint)0x01000000)
-#define HIOX_CSR0_RS232SEL	((uint)0x0000c000)
-#define HIOX_CSR0_SCC3SEL	((uint)0x0000c000)
-#define HIOX_CSR0_SMC1SEL	((uint)0x00008000)
-#define HIOX_CSR0_SCC1SEL	((uint)0x00004000)
-#define HIOX_CSR0_ENTOUCH	((uint)0x00000080)
-#define HIOX_CSR0_PDOWN100	((uint)0x00000060)
-#define HIOX_CSR0_PDOWN10	((uint)0x00000040)
-#define HIOX_CSR0_PDOWN1	((uint)0x00000020)
-#define HIOX_CSR0_TSELSPI	((uint)0x00000010)
-#define HIOX_CSR0_TIRQSTAT	((uint)0x00000008)
-#define HIOX_CSR4_DEFAULT	((uint)0x00000000)
-#define HIOX_CSR4_ENTIRQ2	((uint)0x20000000)
-#define HIOX_CSR4_ENTIRQ3	((uint)0x10000000)
-#define HIOX_CSR4_ENAUDIO	((uint)0x00000080)
-#define HIOX_CSR4_RSTAUDIO	((uint)0x00000040)	/* 0 == reset */
-#define HIOX_CSR4_AUDCLKHI	((uint)0x00000020)
-#define HIOX_CSR4_AUDSPISEL	((uint)0x00000010)
-#define HIOX_CSR4_AUDIRQSTAT	((uint)0x00000008)
-#define HIOX_CSR4_AUDCLKSEL	((uint)0x00000007)
-
-#endif
diff --git a/arch/ppc/platforms/rpxlite.h b/arch/ppc/platforms/rpxlite.h
index 7197806..b615501 100644
--- a/arch/ppc/platforms/rpxlite.h
+++ b/arch/ppc/platforms/rpxlite.h
@@ -57,10 +57,6 @@
 #define BCSR1_PCVCTL6          ((uint)0x00020000)
 #define BCSR1_PCVCTL7          ((uint)0x00010000)
 
-#if defined(CONFIG_HTDMSOUND)
-#include <platforms/rpxhiox.h>
-#endif
-
 /* define IO_BASE for pcmcia */
 #define _IO_BASE 0x80000000
 #define _IO_BASE_SIZE 0x1000
diff --git a/arch/ppc/syslib/Makefile b/arch/ppc/syslib/Makefile
index 0991111..95694159 100644
--- a/arch/ppc/syslib/Makefile
+++ b/arch/ppc/syslib/Makefile
@@ -18,7 +18,8 @@
 obj-$(CONFIG_440SPE)		+= ibm440gx_common.o ibm440sp_common.o ppc440spe_pcie.o
 ifeq ($(CONFIG_4xx),y)
 ifeq ($(CONFIG_XILINX_VIRTEX),y)
-obj-$(CONFIG_40x)		+= xilinx_pic.o ppc_sys.o
+obj-$(CONFIG_40x)		+= xilinx_pic.o
+obj-y				+= virtex_devices.o
 else
 ifeq ($(CONFIG_403),y)
 obj-$(CONFIG_40x)		+= ppc403_pic.o
diff --git a/arch/ppc/syslib/cpc710.h b/arch/ppc/syslib/cpc710.h
deleted file mode 100644
index 5299bf8..0000000
--- a/arch/ppc/syslib/cpc710.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Definitions for the IBM CPC710 PCI Host Bridge
- *
- * Author: Matt Porter <mporter@mvista.com>
- *
- * 2001 (c) MontaVista, Software, Inc.  This file is licensed under
- * the terms of the GNU General Public License version 2.  This program
- * is licensed "as is" without any warranty of any kind, whether express
- * or implied.
- */
-
-#ifndef __PPC_PLATFORMS_CPC710_H
-#define __PPC_PLATFORMS_CPC710_H
-
-/* General bridge and memory controller registers */
-#define PIDR	0xff000008
-#define	CNFR	0xff00000c
-#define	RSTR	0xff000010
-#define UCTL	0xff001000
-#define	MPSR	0xff001010
-#define	SIOC	0xff001020
-#define	ABCNTL	0xff001030
-#define SRST	0xff001040
-#define	ERRC	0xff001050
-#define	SESR	0xff001060
-#define	SEAR	0xff001070
-#define	SIOC1	0xff001090
-#define	PGCHP	0xff001100
-#define	GPDIR	0xff001130
-#define	GPOUT	0xff001150
-#define	ATAS	0xff001160
-#define	AVDG	0xff001170
-#define	MCCR	0xff001200
-#define	MESR	0xff001220
-#define	MEAR	0xff001230
-#define	MCER0	0xff001300
-#define	MCER1	0xff001310
-#define	MCER2	0xff001320
-#define	MCER3	0xff001330
-#define	MCER4	0xff001340
-#define	MCER5	0xff001350
-#define	MCER6	0xff001360
-#define	MCER7	0xff001370
-
-/*
- * PCI32/64 configuration registers
- * Given as offsets from their
- * respective physical segment BAR
- */
-#define PIBAR	0x000f7800
-#define PMBAR	0x000f7810
-#define MSIZE	0x000f7f40
-#define IOSIZE	0x000f7f60
-#define SMBAR	0x000f7f80
-#define SIBAR	0x000f7fc0
-#define PSSIZE	0x000f8100
-#define PPSIZE	0x000f8110
-#define BARPS	0x000f8120
-#define BARPP	0x000f8130
-#define PSBAR	0x000f8140
-#define PPBAR	0x000f8150
-#define BPMDLK	0x000f8200      /* Bottom of Peripheral Memory Space */
-#define TPMDLK	0x000f8210      /* Top of Peripheral Memory Space */
-#define BIODLK	0x000f8220      /* Bottom of Peripheral I/O Space */
-#define TIODLK	0x000f8230      /* Top of Perioheral I/O Space */
-#define DLKCTRL	0x000f8240      /* Deadlock control */
-#define DLKDEV	0x000f8250      /* Deadlock device */
-
-/* System standard configuration registers space */
-#define	DCR	0xff200000
-#define	DID	0xff200004
-#define	BAR	0xff200018
-
-/* Device specific configuration space */
-#define	PCIENB	0xff201000
-
-/* Configuration space registers */
-#define CPC710_BUS_NUMBER	0x40
-#define CPC710_SUB_BUS_NUMBER	0x41
-
-#endif /* __PPC_PLATFORMS_CPC710_H */
diff --git a/arch/ppc/syslib/m8xx_setup.c b/arch/ppc/syslib/m8xx_setup.c
index 01e48d8..9caf850 100644
--- a/arch/ppc/syslib/m8xx_setup.c
+++ b/arch/ppc/syslib/m8xx_setup.c
@@ -413,7 +413,7 @@
 	io_block_mapping(_IO_BASE,_IO_BASE,_IO_BASE_SIZE, _PAGE_IO);
 #endif
 #endif
-#if defined(CONFIG_HTDMSOUND) || defined(CONFIG_RPXTOUCH) || defined(CONFIG_FB_RPX)
+#if defined(CONFIG_RPXTOUCH) || defined(CONFIG_FB_RPX)
 	io_block_mapping(HIOX_CSR_ADDR, HIOX_CSR_ADDR, HIOX_CSR_SIZE, _PAGE_IO);
 #endif
 #ifdef CONFIG_FADS
diff --git a/arch/ppc/syslib/ppc4xx_sgdma.c b/arch/ppc/syslib/ppc4xx_sgdma.c
index 2f83e16..939abe3 100644
--- a/arch/ppc/syslib/ppc4xx_sgdma.c
+++ b/arch/ppc/syslib/ppc4xx_sgdma.c
@@ -27,6 +27,7 @@
 
 #include <asm/system.h>
 #include <asm/io.h>
+#include <asm/dma-mapping.h>
 #include <asm/ppc4xx_dma.h>
 
 void
diff --git a/arch/ppc/syslib/virtex_devices.c b/arch/ppc/syslib/virtex_devices.c
new file mode 100644
index 0000000..1654678
--- /dev/null
+++ b/arch/ppc/syslib/virtex_devices.c
@@ -0,0 +1,233 @@
+/*
+ * Virtex hard ppc405 core common device listing
+ *
+ * Copyright 2005-2007 Secret Lab Technologies Ltd.
+ * Copyright 2005 Freescale Semiconductor Inc.
+ * Copyright 2002-2004 MontaVista Software, Inc.
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/serial_8250.h>
+#include <syslib/virtex_devices.h>
+#include <platforms/4xx/xparameters/xparameters.h>
+#include <asm/io.h>
+
+/*
+ * UARTLITE: shortcut macro for single instance
+ */
+#define XPAR_UARTLITE(num) { \
+	.name = "uartlite", \
+	.id = num, \
+	.num_resources = 2, \
+	.resource = (struct resource[]) { \
+		{ \
+			.start = XPAR_UARTLITE_##num##_BASEADDR + 3, \
+			.end = XPAR_UARTLITE_##num##_HIGHADDR, \
+			.flags = IORESOURCE_MEM, \
+		}, \
+		{ \
+			.start = XPAR_INTC_0_UARTLITE_##num##_VEC_ID, \
+			.flags = IORESOURCE_IRQ, \
+		}, \
+	}, \
+}
+
+/*
+ * Full UART: shortcut macro for single instance + platform data structure
+ */
+#define XPAR_UART(num) { \
+	.mapbase = XPAR_UARTNS550_##num##_BASEADDR + 3, \
+	.irq = XPAR_INTC_0_UARTNS550_##num##_VEC_ID, \
+	.iotype = UPIO_MEM, \
+	.uartclk = XPAR_UARTNS550_##num##_CLOCK_FREQ_HZ, \
+	.flags = UPF_BOOT_AUTOCONF, \
+	.regshift = 2, \
+}
+
+/*
+ * SystemACE: shortcut macro for single instance
+ */
+#define XPAR_SYSACE(num) { \
+	.name		= "xsysace", \
+	.id		= XPAR_SYSACE_##num##_DEVICE_ID, \
+	.num_resources	= 2, \
+	.resource = (struct resource[]) { \
+		{ \
+			.start	= XPAR_SYSACE_##num##_BASEADDR, \
+			.end	= XPAR_SYSACE_##num##_HIGHADDR, \
+			.flags	= IORESOURCE_MEM, \
+		}, \
+		{ \
+			.start	= XPAR_INTC_0_SYSACE_##num##_VEC_ID, \
+			.flags	= IORESOURCE_IRQ, \
+		}, \
+	}, \
+}
+
+
+/* UART 8250 driver platform data table */
+struct plat_serial8250_port virtex_serial_platform_data[] = {
+#if defined(XPAR_UARTNS550_0_BASEADDR)
+	XPAR_UART(0),
+#endif
+#if defined(XPAR_UARTNS550_1_BASEADDR)
+	XPAR_UART(1),
+#endif
+#if defined(XPAR_UARTNS550_2_BASEADDR)
+	XPAR_UART(2),
+#endif
+#if defined(XPAR_UARTNS550_3_BASEADDR)
+	XPAR_UART(3),
+#endif
+#if defined(XPAR_UARTNS550_4_BASEADDR)
+	XPAR_UART(4),
+#endif
+#if defined(XPAR_UARTNS550_5_BASEADDR)
+	XPAR_UART(5),
+#endif
+#if defined(XPAR_UARTNS550_6_BASEADDR)
+	XPAR_UART(6),
+#endif
+#if defined(XPAR_UARTNS550_7_BASEADDR)
+	XPAR_UART(7),
+#endif
+	{ }, /* terminated by empty record */
+};
+
+
+struct platform_device virtex_platform_devices[] = {
+	/* UARTLITE instances */
+#if defined(XPAR_UARTLITE_0_BASEADDR)
+	XPAR_UARTLITE(0),
+#endif
+#if defined(XPAR_UARTLITE_1_BASEADDR)
+	XPAR_UARTLITE(1),
+#endif
+#if defined(XPAR_UARTLITE_2_BASEADDR)
+	XPAR_UARTLITE(2),
+#endif
+#if defined(XPAR_UARTLITE_3_BASEADDR)
+	XPAR_UARTLITE(3),
+#endif
+#if defined(XPAR_UARTLITE_4_BASEADDR)
+	XPAR_UARTLITE(4),
+#endif
+#if defined(XPAR_UARTLITE_5_BASEADDR)
+	XPAR_UARTLITE(5),
+#endif
+#if defined(XPAR_UARTLITE_6_BASEADDR)
+	XPAR_UARTLITE(6),
+#endif
+#if defined(XPAR_UARTLITE_7_BASEADDR)
+	XPAR_UARTLITE(7),
+#endif
+
+	/* Full UART instances */
+#if defined(XPAR_UARTNS550_0_BASEADDR)
+	{
+		.name		= "serial8250",
+		.id		= 0,
+		.dev.platform_data = virtex_serial_platform_data,
+	},
+#endif
+
+	/* SystemACE instances */
+#if defined(XPAR_SYSACE_0_BASEADDR)
+	XPAR_SYSACE(0),
+#endif
+#if defined(XPAR_SYSACE_1_BASEADDR)
+	XPAR_SYSACE(1),
+#endif
+
+	/* ML300/403 reference design framebuffer */
+#if defined(XPAR_TFT_0_BASEADDR)
+	{
+		.name		= "xilinxfb",
+		.id		= 0,
+		.num_resources	= 1,
+		.resource = (struct resource[]) {
+			{
+				.start	= XPAR_TFT_0_BASEADDR,
+				.end	= XPAR_TFT_0_BASEADDR+7,
+				.flags	= IORESOURCE_IO,
+			},
+		},
+	},
+#endif
+};
+
+/* Early serial support functions */
+static void __init
+virtex_early_serial_init(int num, struct plat_serial8250_port *pdata)
+{
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+	struct uart_port serial_req;
+
+	memset(&serial_req, 0, sizeof(serial_req));
+	serial_req.mapbase	= pdata->mapbase;
+	serial_req.membase	= pdata->membase;
+	serial_req.irq		= pdata->irq;
+	serial_req.uartclk	= pdata->uartclk;
+	serial_req.regshift	= pdata->regshift;
+	serial_req.iotype	= pdata->iotype;
+	serial_req.flags	= pdata->flags;
+	gen550_init(num, &serial_req);
+#endif
+}
+
+void __init
+virtex_early_serial_map(void)
+{
+#ifdef CONFIG_SERIAL_8250
+	struct plat_serial8250_port *pdata;
+	int i = 0;
+
+	pdata = virtex_serial_platform_data;
+	while(pdata && pdata->flags) {
+		pdata->membase = ioremap(pdata->mapbase, 0x100);
+		virtex_early_serial_init(i, pdata);
+		pdata++;
+		i++;
+	}
+#endif /* CONFIG_SERIAL_8250 */
+}
+
+/*
+ * default fixup routine; do nothing and return success.
+ *
+ * Reimplement this routine in your custom board support file to
+ * override the default behaviour
+ */
+int __attribute__ ((weak))
+virtex_device_fixup(struct platform_device *dev)
+{
+	return 0;
+}
+
+static int __init virtex_init(void)
+{
+	struct platform_device *index = virtex_platform_devices;
+	unsigned int ret = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(virtex_platform_devices); i++, index++) {
+		if (virtex_device_fixup(index) != 0)
+			continue;
+
+		if (platform_device_register(index)) {
+			ret = 1;
+			printk(KERN_ERR "cannot register dev %s:%d\n",
+			       index->name, index->id);
+		}
+	}
+	return ret;
+}
+
+subsys_initcall(virtex_init);
diff --git a/arch/ppc/syslib/virtex_devices.h b/arch/ppc/syslib/virtex_devices.h
new file mode 100644
index 0000000..4a17dd3
--- /dev/null
+++ b/arch/ppc/syslib/virtex_devices.h
@@ -0,0 +1,27 @@
+/*
+ * Common support header for virtex ppc405 platforms
+ *
+ * Copyright 2007 Secret Lab Technologies Ltd.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2.  This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef __ASM_VIRTEX_DEVICES_H__
+#define __ASM_VIRTEX_DEVICES_H__
+
+#include <linux/platform_device.h>
+
+void __init virtex_early_serial_map(void);
+
+/* Prototype for device fixup routine.  Implement this routine in the
+ * board specific fixup code and the generic setup code will call it for
+ * each device is the platform device list.
+ *
+ * If the hook returns a non-zero value, then the device will not get
+ * registered with the platform bus
+ */
+int virtex_device_fixup(struct platform_device *dev);
+
+#endif  /* __ASM_VIRTEX_DEVICES_H__ */
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 0f293aa..e6ec418 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -41,6 +41,11 @@
 config GENERIC_TIME
 	def_bool y
 
+config GENERIC_BUG
+	bool
+	depends on BUG
+	default y
+
 config NO_IOMEM
 	def_bool y
 
@@ -514,6 +519,14 @@
 	  current kernel, and to start another kernel.  It is like a reboot
 	  but is independent of hardware/microcode support.
 
+config ZFCPDUMP
+	tristate "zfcpdump support"
+	select SMP
+	default n
+	help
+	  Select this option if you want to build an zfcpdump enabled kernel.
+	  Refer to "Documentation/s390/zfcpdump.txt" for more details on this.
+
 endmenu
 
 source "net/Kconfig"
diff --git a/arch/s390/Makefile b/arch/s390/Makefile
index b1e5584..68441e0 100644
--- a/arch/s390/Makefile
+++ b/arch/s390/Makefile
@@ -67,8 +67,10 @@
 
 ifeq ($(call cc-option-yn,-mstack-size=8192 -mstack-guard=128),y)
 cflags-$(CONFIG_CHECK_STACK) += -mstack-size=$(STACK_SIZE)
+ifneq ($(call cc-option-yn,-mstack-size=8192),y)
 cflags-$(CONFIG_CHECK_STACK) += -mstack-guard=$(CONFIG_STACK_GUARD)
 endif
+endif
 
 ifeq ($(call cc-option-yn,-mwarn-dynamicstack),y)
 cflags-$(CONFIG_WARN_STACK) += -mwarn-dynamicstack
@@ -103,6 +105,9 @@
 image: vmlinux
 	$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
 
+zfcpdump:
+	$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
+
 archclean:
 	$(Q)$(MAKE) $(clean)=$(boot)
 
diff --git a/arch/s390/appldata/appldata_base.c b/arch/s390/appldata/appldata_base.c
index 0c3cf4b..ee89b33 100644
--- a/arch/s390/appldata/appldata_base.c
+++ b/arch/s390/appldata/appldata_base.c
@@ -668,45 +668,7 @@
 EXPORT_SYMBOL_GPL(appldata_unregister_ops);
 EXPORT_SYMBOL_GPL(appldata_diag);
 
-#ifdef MODULE
-/*
- * Kernel symbols needed by appldata_mem and appldata_os modules.
- * However, if this file is compiled as a module (for testing only), these
- * symbols are not exported. In this case, we define them locally and export
- * those.
- */
-void si_swapinfo(struct sysinfo *val)
-{
-	val->freeswap = -1ul;
-	val->totalswap = -1ul;
-}
-
-unsigned long avenrun[3] = {-1 - FIXED_1/200, -1 - FIXED_1/200,
-				-1 - FIXED_1/200};
-int nr_threads = -1;
-
-void get_full_page_state(struct page_state *ps)
-{
-	memset(ps, -1, sizeof(struct page_state));
-}
-
-unsigned long nr_running(void)
-{
-	return -1;
-}
-
-unsigned long nr_iowait(void)
-{
-	return -1;
-}
-
-/*unsigned long nr_context_switches(void)
-{
-	return -1;
-}*/
-#endif /* MODULE */
 EXPORT_SYMBOL_GPL(si_swapinfo);
 EXPORT_SYMBOL_GPL(nr_threads);
 EXPORT_SYMBOL_GPL(nr_running);
 EXPORT_SYMBOL_GPL(nr_iowait);
-//EXPORT_SYMBOL_GPL(nr_context_switches);
diff --git a/arch/s390/appldata/appldata_net_sum.c b/arch/s390/appldata/appldata_net_sum.c
index f64b8c8..a43f348 100644
--- a/arch/s390/appldata/appldata_net_sum.c
+++ b/arch/s390/appldata/appldata_net_sum.c
@@ -108,9 +108,6 @@
 	collisions = 0;
 	read_lock(&dev_base_lock);
 	for (dev = dev_base; dev != NULL; dev = dev->next) {
-		if (dev->get_stats == NULL) {
-			continue;
-		}
 		stats = dev->get_stats(dev);
 		rx_packets += stats->rx_packets;
 		tx_packets += stats->tx_packets;
diff --git a/arch/s390/crypto/aes_s390.c b/arch/s390/crypto/aes_s390.c
index 9163635..3660ca6 100644
--- a/arch/s390/crypto/aes_s390.c
+++ b/arch/s390/crypto/aes_s390.c
@@ -119,7 +119,8 @@
 	.cra_name		=	"aes",
 	.cra_driver_name	=	"aes-s390",
 	.cra_priority		=	CRYPT_S390_PRIORITY,
-	.cra_flags		=	CRYPTO_ALG_TYPE_CIPHER,
+	.cra_flags		=	CRYPTO_ALG_TYPE_CIPHER |
+					CRYPTO_ALG_NEED_FALLBACK,
 	.cra_blocksize		=	AES_BLOCK_SIZE,
 	.cra_ctxsize		=	sizeof(struct s390_aes_ctx),
 	.cra_module		=	THIS_MODULE,
@@ -206,7 +207,8 @@
 	.cra_name		=	"ecb(aes)",
 	.cra_driver_name	=	"ecb-aes-s390",
 	.cra_priority		=	CRYPT_S390_COMPOSITE_PRIORITY,
-	.cra_flags		=	CRYPTO_ALG_TYPE_BLKCIPHER,
+	.cra_flags		=	CRYPTO_ALG_TYPE_BLKCIPHER |
+					CRYPTO_ALG_NEED_FALLBACK,
 	.cra_blocksize		=	AES_BLOCK_SIZE,
 	.cra_ctxsize		=	sizeof(struct s390_aes_ctx),
 	.cra_type		=	&crypto_blkcipher_type,
@@ -300,7 +302,8 @@
 	.cra_name		=	"cbc(aes)",
 	.cra_driver_name	=	"cbc-aes-s390",
 	.cra_priority		=	CRYPT_S390_COMPOSITE_PRIORITY,
-	.cra_flags		=	CRYPTO_ALG_TYPE_BLKCIPHER,
+	.cra_flags		=	CRYPTO_ALG_TYPE_BLKCIPHER |
+					CRYPTO_ALG_NEED_FALLBACK,
 	.cra_blocksize		=	AES_BLOCK_SIZE,
 	.cra_ctxsize		=	sizeof(struct s390_aes_ctx),
 	.cra_type		=	&crypto_blkcipher_type,
@@ -333,10 +336,14 @@
 		return -EOPNOTSUPP;
 
 	/* z9 109 and z9 BC/EC only support 128 bit key length */
-	if (keylen_flag == AES_KEYLEN_128)
+	if (keylen_flag == AES_KEYLEN_128) {
+		aes_alg.cra_u.cipher.cia_max_keysize = AES_MIN_KEY_SIZE;
+		ecb_aes_alg.cra_u.blkcipher.max_keysize = AES_MIN_KEY_SIZE;
+		cbc_aes_alg.cra_u.blkcipher.max_keysize = AES_MIN_KEY_SIZE;
 		printk(KERN_INFO
 		       "aes_s390: hardware acceleration only available for"
 		       "128 bit keys\n");
+	}
 
 	ret = crypto_register_alg(&aes_alg);
 	if (ret)
diff --git a/arch/s390/crypto/sha1_s390.c b/arch/s390/crypto/sha1_s390.c
index 969639f..af4460e 100644
--- a/arch/s390/crypto/sha1_s390.c
+++ b/arch/s390/crypto/sha1_s390.c
@@ -25,99 +25,100 @@
  */
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/mm.h>
 #include <linux/crypto.h>
-#include <asm/scatterlist.h>
-#include <asm/byteorder.h>
+
 #include "crypt_s390.h"
 
 #define SHA1_DIGEST_SIZE	20
 #define SHA1_BLOCK_SIZE		64
 
-struct crypt_s390_sha1_ctx {
-	u64 count;
+struct s390_sha1_ctx {
+	u64 count;		/* message length */
 	u32 state[5];
-	u32 buf_len;
-	u8 buffer[2 * SHA1_BLOCK_SIZE];
+	u8 buf[2 * SHA1_BLOCK_SIZE];
 };
 
 static void sha1_init(struct crypto_tfm *tfm)
 {
-	struct crypt_s390_sha1_ctx *ctx = crypto_tfm_ctx(tfm);
+	struct s390_sha1_ctx *sctx = crypto_tfm_ctx(tfm);
 
-	ctx->state[0] = 0x67452301;
-	ctx->state[1] =	0xEFCDAB89;
-	ctx->state[2] =	0x98BADCFE;
-	ctx->state[3] = 0x10325476;
-	ctx->state[4] =	0xC3D2E1F0;
-
-	ctx->count = 0;
-	ctx->buf_len = 0;
+	sctx->state[0] = 0x67452301;
+	sctx->state[1] = 0xEFCDAB89;
+	sctx->state[2] = 0x98BADCFE;
+	sctx->state[3] = 0x10325476;
+	sctx->state[4] = 0xC3D2E1F0;
+	sctx->count = 0;
 }
 
 static void sha1_update(struct crypto_tfm *tfm, const u8 *data,
 			unsigned int len)
 {
-	struct crypt_s390_sha1_ctx *sctx;
-	long imd_len;
+	struct s390_sha1_ctx *sctx = crypto_tfm_ctx(tfm);
+	unsigned int index;
+	int ret;
 
-	sctx = crypto_tfm_ctx(tfm);
-	sctx->count += len * 8; /* message bit length */
+	/* how much is already in the buffer? */
+	index = sctx->count & 0x3f;
 
-	/* anything in buffer yet? -> must be completed */
-	if (sctx->buf_len && (sctx->buf_len + len) >= SHA1_BLOCK_SIZE) {
-		/* complete full block and hash */
-		memcpy(sctx->buffer + sctx->buf_len, data,
-		       SHA1_BLOCK_SIZE - sctx->buf_len);
-		crypt_s390_kimd(KIMD_SHA_1, sctx->state, sctx->buffer,
-				SHA1_BLOCK_SIZE);
-		data += SHA1_BLOCK_SIZE - sctx->buf_len;
-		len -= SHA1_BLOCK_SIZE - sctx->buf_len;
-		sctx->buf_len = 0;
+	sctx->count += len;
+
+	if (index + len < SHA1_BLOCK_SIZE)
+		goto store;
+
+	/* process one stored block */
+	if (index) {
+		memcpy(sctx->buf + index, data, SHA1_BLOCK_SIZE - index);
+		ret = crypt_s390_kimd(KIMD_SHA_1, sctx->state, sctx->buf,
+				      SHA1_BLOCK_SIZE);
+		BUG_ON(ret != SHA1_BLOCK_SIZE);
+		data += SHA1_BLOCK_SIZE - index;
+		len -= SHA1_BLOCK_SIZE - index;
 	}
 
-	/* rest of data contains full blocks? */
-	imd_len = len & ~0x3ful;
-	if (imd_len) {
-		crypt_s390_kimd(KIMD_SHA_1, sctx->state, data, imd_len);
-		data += imd_len;
-		len -= imd_len;
+	/* process as many blocks as possible */
+	if (len >= SHA1_BLOCK_SIZE) {
+		ret = crypt_s390_kimd(KIMD_SHA_1, sctx->state, data,
+				      len & ~(SHA1_BLOCK_SIZE - 1));
+		BUG_ON(ret != (len & ~(SHA1_BLOCK_SIZE - 1)));
+		data += ret;
+		len -= ret;
 	}
-	/* anything left? store in buffer */
-	if (len) {
-		memcpy(sctx->buffer + sctx->buf_len , data, len);
-		sctx->buf_len += len;
-	}
-}
 
-
-static void pad_message(struct crypt_s390_sha1_ctx* sctx)
-{
-	int index;
-
-	index = sctx->buf_len;
-	sctx->buf_len = (sctx->buf_len < 56) ?
-			 SHA1_BLOCK_SIZE:2 * SHA1_BLOCK_SIZE;
-	/* start pad with 1 */
-	sctx->buffer[index] = 0x80;
-	/* pad with zeros */
-	index++;
-	memset(sctx->buffer + index, 0x00, sctx->buf_len - index);
-	/* append length */
-	memcpy(sctx->buffer + sctx->buf_len - 8, &sctx->count,
-	       sizeof sctx->count);
+store:
+	/* anything left? */
+	if (len)
+		memcpy(sctx->buf + index , data, len);
 }
 
 /* Add padding and return the message digest. */
 static void sha1_final(struct crypto_tfm *tfm, u8 *out)
 {
-	struct crypt_s390_sha1_ctx *sctx = crypto_tfm_ctx(tfm);
+	struct s390_sha1_ctx *sctx = crypto_tfm_ctx(tfm);
+	u64 bits;
+	unsigned int index, end;
+	int ret;
 
 	/* must perform manual padding */
-	pad_message(sctx);
-	crypt_s390_kimd(KIMD_SHA_1, sctx->state, sctx->buffer, sctx->buf_len);
+	index = sctx->count & 0x3f;
+	end =  (index < 56) ? SHA1_BLOCK_SIZE : (2 * SHA1_BLOCK_SIZE);
+
+	/* start pad with 1 */
+	sctx->buf[index] = 0x80;
+
+	/* pad with zeros */
+	index++;
+	memset(sctx->buf + index, 0x00, end - index - 8);
+
+	/* append message length */
+	bits = sctx->count * 8;
+	memcpy(sctx->buf + end - 8, &bits, sizeof(bits));
+
+	ret = crypt_s390_kimd(KIMD_SHA_1, sctx->state, sctx->buf, end);
+	BUG_ON(ret != end);
+
 	/* copy digest to out */
 	memcpy(out, sctx->state, SHA1_DIGEST_SIZE);
+
 	/* wipe context */
 	memset(sctx, 0, sizeof *sctx);
 }
@@ -128,7 +129,7 @@
 	.cra_priority	=	CRYPT_S390_PRIORITY,
 	.cra_flags	=	CRYPTO_ALG_TYPE_DIGEST,
 	.cra_blocksize	=	SHA1_BLOCK_SIZE,
-	.cra_ctxsize	=	sizeof(struct crypt_s390_sha1_ctx),
+	.cra_ctxsize	=	sizeof(struct s390_sha1_ctx),
 	.cra_module	=	THIS_MODULE,
 	.cra_list	=	LIST_HEAD_INIT(alg.cra_list),
 	.cra_u		=	{ .digest = {
diff --git a/arch/s390/crypto/sha256_s390.c b/arch/s390/crypto/sha256_s390.c
index 78436c6..2ced333 100644
--- a/arch/s390/crypto/sha256_s390.c
+++ b/arch/s390/crypto/sha256_s390.c
@@ -26,7 +26,7 @@
 #define SHA256_BLOCK_SIZE	64
 
 struct s390_sha256_ctx {
-	u64 count;
+	u64 count;		/* message length */
 	u32 state[8];
 	u8 buf[2 * SHA256_BLOCK_SIZE];
 };
@@ -54,10 +54,9 @@
 	int ret;
 
 	/* how much is already in the buffer? */
-	index = sctx->count / 8 & 0x3f;
+	index = sctx->count & 0x3f;
 
-	/* update message bit length */
-	sctx->count += len * 8;
+	sctx->count += len;
 
 	if ((index + len) < SHA256_BLOCK_SIZE)
 		goto store;
@@ -87,12 +86,17 @@
 		memcpy(sctx->buf + index , data, len);
 }
 
-static void pad_message(struct s390_sha256_ctx* sctx)
+/* Add padding and return the message digest */
+static void sha256_final(struct crypto_tfm *tfm, u8 *out)
 {
-	int index, end;
+	struct s390_sha256_ctx *sctx = crypto_tfm_ctx(tfm);
+	u64 bits;
+	unsigned int index, end;
+	int ret;
 
-	index = sctx->count / 8 & 0x3f;
-	end = index < 56 ? SHA256_BLOCK_SIZE : 2 * SHA256_BLOCK_SIZE;
+	/* must perform manual padding */
+	index = sctx->count & 0x3f;
+	end = (index < 56) ? SHA256_BLOCK_SIZE : (2 * SHA256_BLOCK_SIZE);
 
 	/* start pad with 1 */
 	sctx->buf[index] = 0x80;
@@ -102,21 +106,11 @@
 	memset(sctx->buf + index, 0x00, end - index - 8);
 
 	/* append message length */
-	memcpy(sctx->buf + end - 8, &sctx->count, sizeof sctx->count);
+	bits = sctx->count * 8;
+	memcpy(sctx->buf + end - 8, &bits, sizeof(bits));
 
-	sctx->count = end * 8;
-}
-
-/* Add padding and return the message digest */
-static void sha256_final(struct crypto_tfm *tfm, u8 *out)
-{
-	struct s390_sha256_ctx *sctx = crypto_tfm_ctx(tfm);
-
-	/* must perform manual padding */
-	pad_message(sctx);
-
-	crypt_s390_kimd(KIMD_SHA_256, sctx->state, sctx->buf,
-			sctx->count / 8);
+	ret = crypt_s390_kimd(KIMD_SHA_256, sctx->state, sctx->buf, end);
+	BUG_ON(ret != end);
 
 	/* copy digest to out */
 	memcpy(out, sctx->state, SHA256_DIGEST_SIZE);
diff --git a/arch/s390/defconfig b/arch/s390/defconfig
index 741d2bbb..0e4da8a 100644
--- a/arch/s390/defconfig
+++ b/arch/s390/defconfig
@@ -12,6 +12,7 @@
 # CONFIG_ARCH_HAS_ILOG2_U64 is not set
 CONFIG_GENERIC_HWEIGHT=y
 CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_BUG=y
 CONFIG_NO_IOMEM=y
 CONFIG_S390=y
 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
@@ -166,6 +167,7 @@
 CONFIG_NO_IDLE_HZ_INIT=y
 CONFIG_S390_HYPFS_FS=y
 CONFIG_KEXEC=y
+# CONFIG_ZFCPDUMP is not set
 
 #
 # Networking
@@ -705,6 +707,7 @@
 CONFIG_DEBUG_SPINLOCK_SLEEP=y
 # CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
 # CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
 # CONFIG_DEBUG_INFO is not set
 # CONFIG_DEBUG_VM is not set
 # CONFIG_DEBUG_LIST is not set
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile
index 5492d25..3195d37 100644
--- a/arch/s390/kernel/Makefile
+++ b/arch/s390/kernel/Makefile
@@ -6,7 +6,7 @@
 
 obj-y	:=  bitmap.o traps.o time.o process.o base.o early.o \
             setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \
-	    semaphore.o s390_ext.o debug.o irq.o ipl.o
+	    semaphore.o s390_ext.o debug.o irq.o ipl.o dis.o
 
 obj-y	+= $(if $(CONFIG_64BIT),entry64.o,entry.o)
 obj-y	+= $(if $(CONFIG_64BIT),reipl64.o,reipl.o)
diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c
index 664c669..5236fdb 100644
--- a/arch/s390/kernel/compat_linux.c
+++ b/arch/s390/kernel/compat_linux.c
@@ -495,29 +495,34 @@
  * sys32_execve() executes a new program after the asm stub has set
  * things up for us.  This should basically do what I want it to.
  */
-asmlinkage long
-sys32_execve(struct pt_regs regs)
+asmlinkage long sys32_execve(void)
 {
-        int error;
-        char * filename;
+	struct pt_regs *regs = task_pt_regs(current);
+	char *filename;
+	unsigned long result;
+	int rc;
 
-        filename = getname(compat_ptr(regs.orig_gpr2));
-        error = PTR_ERR(filename);
-        if (IS_ERR(filename))
+	filename = getname(compat_ptr(regs->orig_gpr2));
+	if (IS_ERR(filename)) {
+		result = PTR_ERR(filename);
                 goto out;
-        error = compat_do_execve(filename, compat_ptr(regs.gprs[3]),
-				 compat_ptr(regs.gprs[4]), &regs);
-	if (error == 0)
-	{
-		task_lock(current);
-		current->ptrace &= ~PT_DTRACE;
-		task_unlock(current);
-		current->thread.fp_regs.fpc=0;
-		asm volatile("sfpc %0,0" : : "d" (0));
 	}
+	rc = compat_do_execve(filename, compat_ptr(regs->gprs[3]),
+			      compat_ptr(regs->gprs[4]), regs);
+	if (rc) {
+		result = rc;
+		goto out_putname;
+	}
+	task_lock(current);
+	current->ptrace &= ~PT_DTRACE;
+	task_unlock(current);
+	current->thread.fp_regs.fpc=0;
+	asm volatile("sfpc %0,0" : : "d" (0));
+	result = regs->gprs[2];
+out_putname:
         putname(filename);
 out:
-        return error;
+	return result;
 }
 
 
@@ -918,19 +923,20 @@
 	return sys_write(fd, buf, count);
 }
 
-asmlinkage long sys32_clone(struct pt_regs regs)
+asmlinkage long sys32_clone(void)
 {
-        unsigned long clone_flags;
-        unsigned long newsp;
+	struct pt_regs *regs = task_pt_regs(current);
+	unsigned long clone_flags;
+	unsigned long newsp;
 	int __user *parent_tidptr, *child_tidptr;
 
-        clone_flags = regs.gprs[3] & 0xffffffffUL;
-        newsp = regs.orig_gpr2 & 0x7fffffffUL;
-	parent_tidptr = compat_ptr(regs.gprs[4]);
-	child_tidptr = compat_ptr(regs.gprs[5]);
-        if (!newsp)
-                newsp = regs.gprs[15];
-        return do_fork(clone_flags, newsp, &regs, 0,
+	clone_flags = regs->gprs[3] & 0xffffffffUL;
+	newsp = regs->orig_gpr2 & 0x7fffffffUL;
+	parent_tidptr = compat_ptr(regs->gprs[4]);
+	child_tidptr = compat_ptr(regs->gprs[5]);
+	if (!newsp)
+		newsp = regs->gprs[15];
+	return do_fork(clone_flags, newsp, regs, 0,
 		       parent_tidptr, child_tidptr);
 }
 
diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c
index 887a988..80a54a0 100644
--- a/arch/s390/kernel/compat_signal.c
+++ b/arch/s390/kernel/compat_signal.c
@@ -255,9 +255,9 @@
 }
 
 asmlinkage long
-sys32_sigaltstack(const stack_t32 __user *uss, stack_t32 __user *uoss,
-							struct pt_regs *regs)
+sys32_sigaltstack(const stack_t32 __user *uss, stack_t32 __user *uoss)
 {
+	struct pt_regs *regs = task_pt_regs(current);
 	stack_t kss, koss;
 	unsigned long ss_sp;
 	int ret, err = 0;
@@ -344,8 +344,9 @@
 	return 0;
 }
 
-asmlinkage long sys32_sigreturn(struct pt_regs *regs)
+asmlinkage long sys32_sigreturn(void)
 {
+	struct pt_regs *regs = task_pt_regs(current);
 	sigframe32 __user *frame = (sigframe32 __user *)regs->gprs[15];
 	sigset_t set;
 
@@ -370,8 +371,9 @@
 	return 0;
 }
 
-asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs)
+asmlinkage long sys32_rt_sigreturn(void)
 {
+	struct pt_regs *regs = task_pt_regs(current);
 	rt_sigframe32 __user *frame = (rt_sigframe32 __user *)regs->gprs[15];
 	sigset_t set;
 	stack_t st;
@@ -407,8 +409,8 @@
 	return regs->gprs[2];
 
 badframe:
-        force_sig(SIGSEGV, current);
-        return 0;
+	force_sig(SIGSEGV, current);
+	return 0;
 }	
 
 /*
diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c
new file mode 100644
index 0000000..dabaf98
--- /dev/null
+++ b/arch/s390/kernel/dis.c
@@ -0,0 +1,1278 @@
+/*
+ * arch/s390/kernel/dis.c
+ *
+ * Disassemble s390 instructions.
+ *
+ * Copyright IBM Corp. 2007
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include <linux/reboot.h>
+#include <linux/kprobes.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include <asm/mathemu.h>
+#include <asm/cpcmd.h>
+#include <asm/s390_ext.h>
+#include <asm/lowcore.h>
+#include <asm/debug.h>
+#include <asm/kdebug.h>
+
+#ifndef CONFIG_64BIT
+#define ONELONG "%08lx: "
+#else /* CONFIG_64BIT */
+#define ONELONG "%016lx: "
+#endif /* CONFIG_64BIT */
+
+#define OPERAND_GPR	0x1	/* Operand printed as %rx */
+#define OPERAND_FPR	0x2	/* Operand printed as %fx */
+#define OPERAND_AR	0x4	/* Operand printed as %ax */
+#define OPERAND_CR	0x8	/* Operand printed as %cx */
+#define OPERAND_DISP	0x10	/* Operand printed as displacement */
+#define OPERAND_BASE	0x20	/* Operand printed as base register */
+#define OPERAND_INDEX	0x40	/* Operand printed as index register */
+#define OPERAND_PCREL	0x80	/* Operand printed as pc-relative symbol */
+#define OPERAND_SIGNED	0x100	/* Operand printed as signed value */
+#define OPERAND_LENGTH	0x200	/* Operand printed as length (+1) */
+
+enum {
+	UNUSED,	/* Indicates the end of the operand list */
+	R_8,	/* GPR starting at position 8 */
+	R_12,	/* GPR starting at position 12 */
+	R_16,	/* GPR starting at position 16 */
+	R_20,	/* GPR starting at position 20 */
+	R_24,	/* GPR starting at position 24 */
+	R_28,	/* GPR starting at position 28 */
+	R_32,	/* GPR starting at position 32 */
+	F_8,	/* FPR starting at position 8 */
+	F_12,	/* FPR starting at position 12 */
+	F_16,	/* FPR starting at position 16 */
+	F_20,	/* FPR starting at position 16 */
+	F_24,	/* FPR starting at position 24 */
+	F_28,	/* FPR starting at position 28 */
+	F_32,	/* FPR starting at position 32 */
+	A_8,	/* Access reg. starting at position 8 */
+	A_12,	/* Access reg. starting at position 12 */
+	A_24,	/* Access reg. starting at position 24 */
+	A_28,	/* Access reg. starting at position 28 */
+	C_8,	/* Control reg. starting at position 8 */
+	C_12,	/* Control reg. starting at position 12 */
+	B_16,	/* Base register starting at position 16 */
+	B_32,	/* Base register starting at position 32 */
+	X_12,	/* Index register starting at position 12 */
+	D_20,	/* Displacement starting at position 20 */
+	D_36,	/* Displacement starting at position 36 */
+	D20_20,	/* 20 bit displacement starting at 20 */
+	L4_8,	/* 4 bit length starting at position 8 */
+	L4_12,	/* 4 bit length starting at position 12 */
+	L8_8,	/* 8 bit length starting at position 8 */
+	U4_8,	/* 4 bit unsigned value starting at 8 */
+	U4_12,	/* 4 bit unsigned value starting at 12 */
+	U4_16,	/* 4 bit unsigned value starting at 16 */
+	U4_20,	/* 4 bit unsigned value starting at 20 */
+	U8_8,	/* 8 bit unsigned value starting at 8 */
+	U8_16,	/* 8 bit unsigned value starting at 16 */
+	I16_16,	/* 16 bit signed value starting at 16 */
+	U16_16,	/* 16 bit unsigned value starting at 16 */
+	J16_16,	/* PC relative jump offset at 16 */
+	J32_16,	/* PC relative long offset at 16 */
+	I32_16,	/* 32 bit signed value starting at 16 */
+	U32_16,	/* 32 bit unsigned value starting at 16 */
+	M_16,	/* 4 bit optional mask starting at 16 */
+	RO_28,	/* optional GPR starting at position 28 */
+};
+
+/*
+ * Enumeration of the different instruction formats.
+ * For details consult the principles of operation.
+ */
+enum {
+	INSTR_INVALID,
+	INSTR_E, INSTR_RIE_RRP, INSTR_RIL_RI, INSTR_RIL_RP, INSTR_RIL_RU,
+	INSTR_RIL_UP, INSTR_RI_RI, INSTR_RI_RP, INSTR_RI_RU, INSTR_RI_UP,
+	INSTR_RRE_00, INSTR_RRE_0R, INSTR_RRE_AA, INSTR_RRE_AR, INSTR_RRE_F0,
+	INSTR_RRE_FF, INSTR_RRE_R0, INSTR_RRE_RA, INSTR_RRE_RF, INSTR_RRE_RR,
+	INSTR_RRE_RR_OPT, INSTR_RRF_F0FF, INSTR_RRF_FUFF, INSTR_RRF_M0RR,
+	INSTR_RRF_R0RR, INSTR_RRF_RURR, INSTR_RRF_U0FF, INSTR_RRF_U0RF,
+	INSTR_RR_FF, INSTR_RR_R0, INSTR_RR_RR, INSTR_RR_U0, INSTR_RR_UR,
+	INSTR_RSE_CCRD, INSTR_RSE_RRRD, INSTR_RSE_RURD, INSTR_RSI_RRP,
+	INSTR_RSL_R0RD, INSTR_RSY_AARD, INSTR_RSY_CCRD, INSTR_RSY_RRRD,
+	INSTR_RSY_RURD, INSTR_RS_AARD, INSTR_RS_CCRD, INSTR_RS_R0RD,
+	INSTR_RS_RRRD, INSTR_RS_RURD, INSTR_RXE_FRRD, INSTR_RXE_RRRD,
+	INSTR_RXF_FRRDF, INSTR_RXY_FRRD, INSTR_RXY_RRRD, INSTR_RX_FRRD,
+	INSTR_RX_RRRD, INSTR_RX_URRD, INSTR_SIY_URD, INSTR_SI_URD,
+	INSTR_SSE_RDRD, INSTR_SSF_RRDRD, INSTR_SS_L0RDRD, INSTR_SS_LIRDRD,
+	INSTR_SS_LLRDRD, INSTR_SS_RRRDRD, INSTR_SS_RRRDRD2, INSTR_SS_RRRDRD3,
+	INSTR_S_00, INSTR_S_RD,
+};
+
+struct operand {
+	int bits;		/* The number of bits in the operand. */
+	int shift;		/* The number of bits to shift. */
+	int flags;		/* One bit syntax flags. */
+};
+
+struct insn {
+	const char name[5];
+	unsigned char opfrag;
+	unsigned char format;
+};
+
+static const struct operand operands[] =
+{
+	[UNUSED]  = { 0, 0, 0 },
+	[R_8]	 = {  4,  8, OPERAND_GPR },
+	[R_12]	 = {  4, 12, OPERAND_GPR },
+	[R_16]	 = {  4, 16, OPERAND_GPR },
+	[R_20]	 = {  4, 20, OPERAND_GPR },
+	[R_24]	 = {  4, 24, OPERAND_GPR },
+	[R_28]	 = {  4, 28, OPERAND_GPR },
+	[R_32]	 = {  4, 32, OPERAND_GPR },
+	[F_8]	 = {  4,  8, OPERAND_FPR },
+	[F_12]	 = {  4, 12, OPERAND_FPR },
+	[F_16]	 = {  4, 16, OPERAND_FPR },
+	[F_20]	 = {  4, 16, OPERAND_FPR },
+	[F_24]	 = {  4, 24, OPERAND_FPR },
+	[F_28]	 = {  4, 28, OPERAND_FPR },
+	[F_32]	 = {  4, 32, OPERAND_FPR },
+	[A_8]	 = {  4,  8, OPERAND_AR },
+	[A_12]	 = {  4, 12, OPERAND_AR },
+	[A_24]	 = {  4, 24, OPERAND_AR },
+	[A_28]	 = {  4, 28, OPERAND_AR },
+	[C_8]	 = {  4,  8, OPERAND_CR },
+	[C_12]	 = {  4, 12, OPERAND_CR },
+	[B_16]	 = {  4, 16, OPERAND_BASE | OPERAND_GPR },
+	[B_32]	 = {  4, 32, OPERAND_BASE | OPERAND_GPR },
+	[X_12]	 = {  4, 12, OPERAND_INDEX | OPERAND_GPR },
+	[D_20]	 = { 12, 20, OPERAND_DISP },
+	[D_36]	 = { 12, 36, OPERAND_DISP },
+	[D20_20] = { 20, 20, OPERAND_DISP | OPERAND_SIGNED },
+	[L4_8]	 = {  4,  8, OPERAND_LENGTH },
+	[L4_12]  = {  4, 12, OPERAND_LENGTH },
+	[L8_8]	 = {  8,  8, OPERAND_LENGTH },
+	[U4_8]	 = {  4,  8, 0 },
+	[U4_12]  = {  4, 12, 0 },
+	[U4_16]  = {  4, 16, 0 },
+	[U4_20]  = {  4, 20, 0 },
+	[U8_8]	 = {  8,  8, 0 },
+	[U8_16]  = {  8, 16, 0 },
+	[I16_16] = { 16, 16, OPERAND_SIGNED },
+	[U16_16] = { 16, 16, 0 },
+	[J16_16] = { 16, 16, OPERAND_PCREL },
+	[J32_16] = { 32, 16, OPERAND_PCREL },
+	[I32_16] = { 32, 16, OPERAND_SIGNED },
+	[U32_16] = { 32, 16, 0 },
+	[M_16]	 = {  4, 16, 0 },
+	[RO_28]  = {  4, 28, OPERAND_GPR }
+};
+
+static const unsigned char formats[][7] = {
+	[INSTR_E]	  = { 0xff, 0,0,0,0,0,0 },	       /* e.g. pr    */
+	[INSTR_RIE_RRP]	  = { 0xff, R_8,R_12,J16_16,0,0,0 },   /* e.g. brxhg */
+	[INSTR_RIL_RP]	  = { 0x0f, R_8,J32_16,0,0,0,0 },      /* e.g. brasl */
+	[INSTR_RIL_UP]	  = { 0x0f, U4_8,J32_16,0,0,0,0 },     /* e.g. brcl  */
+	[INSTR_RIL_RI]	  = { 0x0f, R_8,I32_16,0,0,0,0 },      /* e.g. afi   */
+	[INSTR_RIL_RU]	  = { 0x0f, R_8,U32_16,0,0,0,0 },      /* e.g. alfi  */
+	[INSTR_RI_RI]	  = { 0x0f, R_8,I16_16,0,0,0,0 },      /* e.g. ahi   */
+	[INSTR_RI_RP]	  = { 0x0f, R_8,J16_16,0,0,0,0 },      /* e.g. brct  */
+	[INSTR_RI_RU]	  = { 0x0f, R_8,U16_16,0,0,0,0 },      /* e.g. tml   */
+	[INSTR_RI_UP]	  = { 0x0f, U4_8,J16_16,0,0,0,0 },     /* e.g. brc   */
+	[INSTR_RRE_00]	  = { 0xff, 0,0,0,0,0,0 },	       /* e.g. palb  */
+	[INSTR_RRE_0R]	  = { 0xff, R_28,0,0,0,0,0 },	       /* e.g. tb    */
+	[INSTR_RRE_AA]	  = { 0xff, A_24,A_28,0,0,0,0 },       /* e.g. cpya  */
+	[INSTR_RRE_AR]	  = { 0xff, A_24,R_28,0,0,0,0 },       /* e.g. sar   */
+	[INSTR_RRE_F0]	  = { 0xff, F_24,0,0,0,0,0 },	       /* e.g. sqer  */
+	[INSTR_RRE_FF]	  = { 0xff, F_24,F_28,0,0,0,0 },       /* e.g. debr  */
+	[INSTR_RRE_R0]	  = { 0xff, R_24,0,0,0,0,0 },	       /* e.g. ipm   */
+	[INSTR_RRE_RA]	  = { 0xff, R_24,A_28,0,0,0,0 },       /* e.g. ear   */
+	[INSTR_RRE_RF]	  = { 0xff, R_24,F_28,0,0,0,0 },       /* e.g. cefbr */
+	[INSTR_RRE_RR]	  = { 0xff, R_24,R_28,0,0,0,0 },       /* e.g. lura  */
+	[INSTR_RRE_RR_OPT]= { 0xff, R_24,RO_28,0,0,0,0 },      /* efpc, sfpc */
+	[INSTR_RRF_F0FF]  = { 0xff, F_16,F_24,F_28,0,0,0 },    /* e.g. madbr */
+	[INSTR_RRF_FUFF]  = { 0xff, F_24,F_16,F_28,U4_20,0,0 },/* e.g. didbr */
+	[INSTR_RRF_RURR]  = { 0xff, R_24,R_28,R_16,U4_20,0,0 },/* e.g. .insn */
+	[INSTR_RRF_R0RR]  = { 0xff, R_24,R_28,R_16,0,0,0 },    /* e.g. idte  */
+	[INSTR_RRF_U0FF]  = { 0xff, F_24,U4_16,F_28,0,0,0 },   /* e.g. fixr  */
+	[INSTR_RRF_U0RF]  = { 0xff, R_24,U4_16,F_28,0,0,0 },   /* e.g. cfebr */
+	[INSTR_RRF_M0RR]  = { 0xff, R_24,R_28,M_16,0,0,0 },    /* e.g. sske  */
+	[INSTR_RR_FF]	  = { 0xff, F_8,F_12,0,0,0,0 },        /* e.g. adr   */
+	[INSTR_RR_R0]	  = { 0xff, R_8, 0,0,0,0,0 },	       /* e.g. spm   */
+	[INSTR_RR_RR]	  = { 0xff, R_8,R_12,0,0,0,0 },        /* e.g. lr    */
+	[INSTR_RR_U0]	  = { 0xff, U8_8, 0,0,0,0,0 },	       /* e.g. svc   */
+	[INSTR_RR_UR]	  = { 0xff, U4_8,R_12,0,0,0,0 },       /* e.g. bcr   */
+	[INSTR_RSE_RRRD]  = { 0xff, R_8,R_12,D_20,B_16,0,0 },  /* e.g. lmh   */
+	[INSTR_RSE_CCRD]  = { 0xff, C_8,C_12,D_20,B_16,0,0 },  /* e.g. lmh   */
+	[INSTR_RSE_RURD]  = { 0xff, R_8,U4_12,D_20,B_16,0,0 }, /* e.g. icmh  */
+	[INSTR_RSL_R0RD]  = { 0xff, R_8,D_20,B_16,0,0,0 },     /* e.g. tp    */
+	[INSTR_RSI_RRP]	  = { 0xff, R_8,R_12,J16_16,0,0,0 },   /* e.g. brxh  */
+	[INSTR_RSY_RRRD]  = { 0xff, R_8,R_12,D20_20,B_16,0,0 },/* e.g. stmy  */
+	[INSTR_RSY_RURD]  = { 0xff, R_8,U4_12,D20_20,B_16,0,0 },
+							       /* e.g. icmh  */
+	[INSTR_RSY_AARD]  = { 0xff, A_8,A_12,D20_20,B_16,0,0 },/* e.g. lamy  */
+	[INSTR_RSY_CCRD]  = { 0xff, C_8,C_12,D20_20,B_16,0,0 },/* e.g. lamy  */
+	[INSTR_RS_AARD]	  = { 0xff, A_8,A_12,D_20,B_16,0,0 },  /* e.g. lam   */
+	[INSTR_RS_CCRD]	  = { 0xff, C_8,C_12,D_20,B_16,0,0 },  /* e.g. lctl  */
+	[INSTR_RS_R0RD]	  = { 0xff, R_8,D_20,B_16,0,0,0 },     /* e.g. sll   */
+	[INSTR_RS_RRRD]	  = { 0xff, R_8,R_12,D_20,B_16,0,0 },  /* e.g. cs    */
+	[INSTR_RS_RURD]	  = { 0xff, R_8,U4_12,D_20,B_16,0,0 }, /* e.g. icm   */
+	[INSTR_RXE_FRRD]  = { 0xff, F_8,D_20,X_12,B_16,0,0 },  /* e.g. axbr  */
+	[INSTR_RXE_RRRD]  = { 0xff, R_8,D_20,X_12,B_16,0,0 },  /* e.g. lg    */
+	[INSTR_RXF_FRRDF] = { 0xff, F_32,F_8,D_20,X_12,B_16,0 },
+							       /* e.g. madb  */
+	[INSTR_RXY_RRRD]  = { 0xff, R_8,D20_20,X_12,B_16,0,0 },/* e.g. ly    */
+	[INSTR_RXY_FRRD]  = { 0xff, F_8,D20_20,X_12,B_16,0,0 },/* e.g. ley   */
+	[INSTR_RX_FRRD]	  = { 0xff, F_8,D_20,X_12,B_16,0,0 },  /* e.g. ae    */
+	[INSTR_RX_RRRD]	  = { 0xff, R_8,D_20,X_12,B_16,0,0 },  /* e.g. l     */
+	[INSTR_RX_URRD]	  = { 0x00, U4_8,D_20,X_12,B_16,0,0 }, /* e.g. bc    */
+	[INSTR_SI_URD]	  = { 0x00, D_20,B_16,U8_8,0,0,0 },    /* e.g. cli   */
+	[INSTR_SIY_URD]	  = { 0xff, D20_20,B_16,U8_8,0,0,0 },  /* e.g. tmy   */
+	[INSTR_SSE_RDRD]  = { 0xff, D_20,B_16,D_36,B_32,0,0 }, /* e.g. mvsdk */
+	[INSTR_SS_L0RDRD] = { 0xff, D_20,L8_8,B_16,D_36,B_32,0 },
+							       /* e.g. mvc   */
+	[INSTR_SS_LIRDRD] = { 0xff, D_20,L4_8,B_16,D_36,B_32,U4_12 },
+							       /* e.g. srp   */
+	[INSTR_SS_LLRDRD] = { 0xff, D_20,L4_8,B_16,D_36,L4_12,B_32 },
+							       /* e.g. pack  */
+	[INSTR_SS_RRRDRD] = { 0xff, D_20,R_8,B_16,D_36,B_32,R_12 },
+							       /* e.g. mvck  */
+	[INSTR_SS_RRRDRD2]= { 0xff, R_8,D_20,B_16,R_12,D_36,B_32 },
+							       /* e.g. plo   */
+	[INSTR_SS_RRRDRD3]= { 0xff, R_8,R_12,D_20,B_16,D_36,B_32 },
+							       /* e.g. lmd   */
+	[INSTR_S_00]	  = { 0xff, 0,0,0,0,0,0 },	       /* e.g. hsch  */
+	[INSTR_S_RD]	  = { 0xff, D_20,B_16,0,0,0,0 },       /* e.g. lpsw  */
+	[INSTR_SSF_RRDRD] = { 0x00, D_20,B_16,D_36,B_32,R_8,0 },
+							       /* e.g. mvcos */
+};
+
+static struct insn opcode[] = {
+#ifdef CONFIG_64BIT
+	{ "lmd", 0xef, INSTR_SS_RRRDRD3 },
+#endif
+	{ "spm", 0x04, INSTR_RR_R0 },
+	{ "balr", 0x05, INSTR_RR_RR },
+	{ "bctr", 0x06, INSTR_RR_RR },
+	{ "bcr", 0x07, INSTR_RR_UR },
+	{ "svc", 0x0a, INSTR_RR_U0 },
+	{ "bsm", 0x0b, INSTR_RR_RR },
+	{ "bassm", 0x0c, INSTR_RR_RR },
+	{ "basr", 0x0d, INSTR_RR_RR },
+	{ "mvcl", 0x0e, INSTR_RR_RR },
+	{ "clcl", 0x0f, INSTR_RR_RR },
+	{ "lpr", 0x10, INSTR_RR_RR },
+	{ "lnr", 0x11, INSTR_RR_RR },
+	{ "ltr", 0x12, INSTR_RR_RR },
+	{ "lcr", 0x13, INSTR_RR_RR },
+	{ "nr", 0x14, INSTR_RR_RR },
+	{ "clr", 0x15, INSTR_RR_RR },
+	{ "or", 0x16, INSTR_RR_RR },
+	{ "xr", 0x17, INSTR_RR_RR },
+	{ "lr", 0x18, INSTR_RR_RR },
+	{ "cr", 0x19, INSTR_RR_RR },
+	{ "ar", 0x1a, INSTR_RR_RR },
+	{ "sr", 0x1b, INSTR_RR_RR },
+	{ "mr", 0x1c, INSTR_RR_RR },
+	{ "dr", 0x1d, INSTR_RR_RR },
+	{ "alr", 0x1e, INSTR_RR_RR },
+	{ "slr", 0x1f, INSTR_RR_RR },
+	{ "lpdr", 0x20, INSTR_RR_FF },
+	{ "lndr", 0x21, INSTR_RR_FF },
+	{ "ltdr", 0x22, INSTR_RR_FF },
+	{ "lcdr", 0x23, INSTR_RR_FF },
+	{ "hdr", 0x24, INSTR_RR_FF },
+	{ "ldxr", 0x25, INSTR_RR_FF },
+	{ "lrdr", 0x25, INSTR_RR_FF },
+	{ "mxr", 0x26, INSTR_RR_FF },
+	{ "mxdr", 0x27, INSTR_RR_FF },
+	{ "ldr", 0x28, INSTR_RR_FF },
+	{ "cdr", 0x29, INSTR_RR_FF },
+	{ "adr", 0x2a, INSTR_RR_FF },
+	{ "sdr", 0x2b, INSTR_RR_FF },
+	{ "mdr", 0x2c, INSTR_RR_FF },
+	{ "ddr", 0x2d, INSTR_RR_FF },
+	{ "awr", 0x2e, INSTR_RR_FF },
+	{ "swr", 0x2f, INSTR_RR_FF },
+	{ "lper", 0x30, INSTR_RR_FF },
+	{ "lner", 0x31, INSTR_RR_FF },
+	{ "lter", 0x32, INSTR_RR_FF },
+	{ "lcer", 0x33, INSTR_RR_FF },
+	{ "her", 0x34, INSTR_RR_FF },
+	{ "ledr", 0x35, INSTR_RR_FF },
+	{ "lrer", 0x35, INSTR_RR_FF },
+	{ "axr", 0x36, INSTR_RR_FF },
+	{ "sxr", 0x37, INSTR_RR_FF },
+	{ "ler", 0x38, INSTR_RR_FF },
+	{ "cer", 0x39, INSTR_RR_FF },
+	{ "aer", 0x3a, INSTR_RR_FF },
+	{ "ser", 0x3b, INSTR_RR_FF },
+	{ "mder", 0x3c, INSTR_RR_FF },
+	{ "mer", 0x3c, INSTR_RR_FF },
+	{ "der", 0x3d, INSTR_RR_FF },
+	{ "aur", 0x3e, INSTR_RR_FF },
+	{ "sur", 0x3f, INSTR_RR_FF },
+	{ "sth", 0x40, INSTR_RX_RRRD },
+	{ "la", 0x41, INSTR_RX_RRRD },
+	{ "stc", 0x42, INSTR_RX_RRRD },
+	{ "ic", 0x43, INSTR_RX_RRRD },
+	{ "ex", 0x44, INSTR_RX_RRRD },
+	{ "bal", 0x45, INSTR_RX_RRRD },
+	{ "bct", 0x46, INSTR_RX_RRRD },
+	{ "bc", 0x47, INSTR_RX_URRD },
+	{ "lh", 0x48, INSTR_RX_RRRD },
+	{ "ch", 0x49, INSTR_RX_RRRD },
+	{ "ah", 0x4a, INSTR_RX_RRRD },
+	{ "sh", 0x4b, INSTR_RX_RRRD },
+	{ "mh", 0x4c, INSTR_RX_RRRD },
+	{ "bas", 0x4d, INSTR_RX_RRRD },
+	{ "cvd", 0x4e, INSTR_RX_RRRD },
+	{ "cvb", 0x4f, INSTR_RX_RRRD },
+	{ "st", 0x50, INSTR_RX_RRRD },
+	{ "lae", 0x51, INSTR_RX_RRRD },
+	{ "n", 0x54, INSTR_RX_RRRD },
+	{ "cl", 0x55, INSTR_RX_RRRD },
+	{ "o", 0x56, INSTR_RX_RRRD },
+	{ "x", 0x57, INSTR_RX_RRRD },
+	{ "l", 0x58, INSTR_RX_RRRD },
+	{ "c", 0x59, INSTR_RX_RRRD },
+	{ "a", 0x5a, INSTR_RX_RRRD },
+	{ "s", 0x5b, INSTR_RX_RRRD },
+	{ "m", 0x5c, INSTR_RX_RRRD },
+	{ "d", 0x5d, INSTR_RX_RRRD },
+	{ "al", 0x5e, INSTR_RX_RRRD },
+	{ "sl", 0x5f, INSTR_RX_RRRD },
+	{ "std", 0x60, INSTR_RX_FRRD },
+	{ "mxd", 0x67, INSTR_RX_FRRD },
+	{ "ld", 0x68, INSTR_RX_FRRD },
+	{ "cd", 0x69, INSTR_RX_FRRD },
+	{ "ad", 0x6a, INSTR_RX_FRRD },
+	{ "sd", 0x6b, INSTR_RX_FRRD },
+	{ "md", 0x6c, INSTR_RX_FRRD },
+	{ "dd", 0x6d, INSTR_RX_FRRD },
+	{ "aw", 0x6e, INSTR_RX_FRRD },
+	{ "sw", 0x6f, INSTR_RX_FRRD },
+	{ "ste", 0x70, INSTR_RX_FRRD },
+	{ "ms", 0x71, INSTR_RX_RRRD },
+	{ "le", 0x78, INSTR_RX_FRRD },
+	{ "ce", 0x79, INSTR_RX_FRRD },
+	{ "ae", 0x7a, INSTR_RX_FRRD },
+	{ "se", 0x7b, INSTR_RX_FRRD },
+	{ "mde", 0x7c, INSTR_RX_FRRD },
+	{ "me", 0x7c, INSTR_RX_FRRD },
+	{ "de", 0x7d, INSTR_RX_FRRD },
+	{ "au", 0x7e, INSTR_RX_FRRD },
+	{ "su", 0x7f, INSTR_RX_FRRD },
+	{ "ssm", 0x80, INSTR_S_RD },
+	{ "lpsw", 0x82, INSTR_S_RD },
+	{ "diag", 0x83, INSTR_RS_RRRD },
+	{ "brxh", 0x84, INSTR_RSI_RRP },
+	{ "brxle", 0x85, INSTR_RSI_RRP },
+	{ "bxh", 0x86, INSTR_RS_RRRD },
+	{ "bxle", 0x87, INSTR_RS_RRRD },
+	{ "srl", 0x88, INSTR_RS_R0RD },
+	{ "sll", 0x89, INSTR_RS_R0RD },
+	{ "sra", 0x8a, INSTR_RS_R0RD },
+	{ "sla", 0x8b, INSTR_RS_R0RD },
+	{ "srdl", 0x8c, INSTR_RS_R0RD },
+	{ "sldl", 0x8d, INSTR_RS_R0RD },
+	{ "srda", 0x8e, INSTR_RS_R0RD },
+	{ "slda", 0x8f, INSTR_RS_R0RD },
+	{ "stm", 0x90, INSTR_RS_RRRD },
+	{ "tm", 0x91, INSTR_SI_URD },
+	{ "mvi", 0x92, INSTR_SI_URD },
+	{ "ts", 0x93, INSTR_S_RD },
+	{ "ni", 0x94, INSTR_SI_URD },
+	{ "cli", 0x95, INSTR_SI_URD },
+	{ "oi", 0x96, INSTR_SI_URD },
+	{ "xi", 0x97, INSTR_SI_URD },
+	{ "lm", 0x98, INSTR_RS_RRRD },
+	{ "trace", 0x99, INSTR_RS_RRRD },
+	{ "lam", 0x9a, INSTR_RS_AARD },
+	{ "stam", 0x9b, INSTR_RS_AARD },
+	{ "mvcle", 0xa8, INSTR_RS_RRRD },
+	{ "clcle", 0xa9, INSTR_RS_RRRD },
+	{ "stnsm", 0xac, INSTR_SI_URD },
+	{ "stosm", 0xad, INSTR_SI_URD },
+	{ "sigp", 0xae, INSTR_RS_RRRD },
+	{ "mc", 0xaf, INSTR_SI_URD },
+	{ "lra", 0xb1, INSTR_RX_RRRD },
+	{ "stctl", 0xb6, INSTR_RS_CCRD },
+	{ "lctl", 0xb7, INSTR_RS_CCRD },
+	{ "cs", 0xba, INSTR_RS_RRRD },
+	{ "cds", 0xbb, INSTR_RS_RRRD },
+	{ "clm", 0xbd, INSTR_RS_RURD },
+	{ "stcm", 0xbe, INSTR_RS_RURD },
+	{ "icm", 0xbf, INSTR_RS_RURD },
+	{ "mvn", 0xd1, INSTR_SS_L0RDRD },
+	{ "mvc", 0xd2, INSTR_SS_L0RDRD },
+	{ "mvz", 0xd3, INSTR_SS_L0RDRD },
+	{ "nc", 0xd4, INSTR_SS_L0RDRD },
+	{ "clc", 0xd5, INSTR_SS_L0RDRD },
+	{ "oc", 0xd6, INSTR_SS_L0RDRD },
+	{ "xc", 0xd7, INSTR_SS_L0RDRD },
+	{ "mvck", 0xd9, INSTR_SS_RRRDRD },
+	{ "mvcp", 0xda, INSTR_SS_RRRDRD },
+	{ "mvcs", 0xdb, INSTR_SS_RRRDRD },
+	{ "tr", 0xdc, INSTR_SS_L0RDRD },
+	{ "trt", 0xdd, INSTR_SS_L0RDRD },
+	{ "ed", 0xde, INSTR_SS_L0RDRD },
+	{ "edmk", 0xdf, INSTR_SS_L0RDRD },
+	{ "pku", 0xe1, INSTR_SS_L0RDRD },
+	{ "unpku", 0xe2, INSTR_SS_L0RDRD },
+	{ "mvcin", 0xe8, INSTR_SS_L0RDRD },
+	{ "pka", 0xe9, INSTR_SS_L0RDRD },
+	{ "unpka", 0xea, INSTR_SS_L0RDRD },
+	{ "plo", 0xee, INSTR_SS_RRRDRD2 },
+	{ "srp", 0xf0, INSTR_SS_LIRDRD },
+	{ "mvo", 0xf1, INSTR_SS_LLRDRD },
+	{ "pack", 0xf2, INSTR_SS_LLRDRD },
+	{ "unpk", 0xf3, INSTR_SS_LLRDRD },
+	{ "zap", 0xf8, INSTR_SS_LLRDRD },
+	{ "cp", 0xf9, INSTR_SS_LLRDRD },
+	{ "ap", 0xfa, INSTR_SS_LLRDRD },
+	{ "sp", 0xfb, INSTR_SS_LLRDRD },
+	{ "mp", 0xfc, INSTR_SS_LLRDRD },
+	{ "dp", 0xfd, INSTR_SS_LLRDRD },
+	{ "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_01[] = {
+#ifdef CONFIG_64BIT
+	{ "sam64", 0x0e, INSTR_E },
+#endif
+	{ "pr", 0x01, INSTR_E },
+	{ "upt", 0x02, INSTR_E },
+	{ "sckpf", 0x07, INSTR_E },
+	{ "tam", 0x0b, INSTR_E },
+	{ "sam24", 0x0c, INSTR_E },
+	{ "sam31", 0x0d, INSTR_E },
+	{ "trap2", 0xff, INSTR_E },
+	{ "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_a5[] = {
+#ifdef CONFIG_64BIT
+	{ "iihh", 0x00, INSTR_RI_RU },
+	{ "iihl", 0x01, INSTR_RI_RU },
+	{ "iilh", 0x02, INSTR_RI_RU },
+	{ "iill", 0x03, INSTR_RI_RU },
+	{ "nihh", 0x04, INSTR_RI_RU },
+	{ "nihl", 0x05, INSTR_RI_RU },
+	{ "nilh", 0x06, INSTR_RI_RU },
+	{ "nill", 0x07, INSTR_RI_RU },
+	{ "oihh", 0x08, INSTR_RI_RU },
+	{ "oihl", 0x09, INSTR_RI_RU },
+	{ "oilh", 0x0a, INSTR_RI_RU },
+	{ "oill", 0x0b, INSTR_RI_RU },
+	{ "llihh", 0x0c, INSTR_RI_RU },
+	{ "llihl", 0x0d, INSTR_RI_RU },
+	{ "llilh", 0x0e, INSTR_RI_RU },
+	{ "llill", 0x0f, INSTR_RI_RU },
+#endif
+	{ "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_a7[] = {
+#ifdef CONFIG_64BIT
+	{ "tmhh", 0x02, INSTR_RI_RU },
+	{ "tmhl", 0x03, INSTR_RI_RU },
+	{ "brctg", 0x07, INSTR_RI_RP },
+	{ "lghi", 0x09, INSTR_RI_RI },
+	{ "aghi", 0x0b, INSTR_RI_RI },
+	{ "mghi", 0x0d, INSTR_RI_RI },
+	{ "cghi", 0x0f, INSTR_RI_RI },
+#endif
+	{ "tmlh", 0x00, INSTR_RI_RU },
+	{ "tmll", 0x01, INSTR_RI_RU },
+	{ "brc", 0x04, INSTR_RI_UP },
+	{ "bras", 0x05, INSTR_RI_RP },
+	{ "brct", 0x06, INSTR_RI_RP },
+	{ "lhi", 0x08, INSTR_RI_RI },
+	{ "ahi", 0x0a, INSTR_RI_RI },
+	{ "mhi", 0x0c, INSTR_RI_RI },
+	{ "chi", 0x0e, INSTR_RI_RI },
+	{ "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_b2[] = {
+#ifdef CONFIG_64BIT
+	{ "sske", 0x2b, INSTR_RRF_M0RR },
+	{ "stckf", 0x7c, INSTR_S_RD },
+	{ "cu21", 0xa6, INSTR_RRF_M0RR },
+	{ "cuutf", 0xa6, INSTR_RRF_M0RR },
+	{ "cu12", 0xa7, INSTR_RRF_M0RR },
+	{ "cutfu", 0xa7, INSTR_RRF_M0RR },
+	{ "stfle", 0xb0, INSTR_S_RD },
+	{ "lpswe", 0xb2, INSTR_S_RD },
+#endif
+	{ "stidp", 0x02, INSTR_S_RD },
+	{ "sck", 0x04, INSTR_S_RD },
+	{ "stck", 0x05, INSTR_S_RD },
+	{ "sckc", 0x06, INSTR_S_RD },
+	{ "stckc", 0x07, INSTR_S_RD },
+	{ "spt", 0x08, INSTR_S_RD },
+	{ "stpt", 0x09, INSTR_S_RD },
+	{ "spka", 0x0a, INSTR_S_RD },
+	{ "ipk", 0x0b, INSTR_S_00 },
+	{ "ptlb", 0x0d, INSTR_S_00 },
+	{ "spx", 0x10, INSTR_S_RD },
+	{ "stpx", 0x11, INSTR_S_RD },
+	{ "stap", 0x12, INSTR_S_RD },
+	{ "sie", 0x14, INSTR_S_RD },
+	{ "pc", 0x18, INSTR_S_RD },
+	{ "sac", 0x19, INSTR_S_RD },
+	{ "cfc", 0x1a, INSTR_S_RD },
+	{ "ipte", 0x21, INSTR_RRE_RR },
+	{ "ipm", 0x22, INSTR_RRE_R0 },
+	{ "ivsk", 0x23, INSTR_RRE_RR },
+	{ "iac", 0x24, INSTR_RRE_R0 },
+	{ "ssar", 0x25, INSTR_RRE_R0 },
+	{ "epar", 0x26, INSTR_RRE_R0 },
+	{ "esar", 0x27, INSTR_RRE_R0 },
+	{ "pt", 0x28, INSTR_RRE_RR },
+	{ "iske", 0x29, INSTR_RRE_RR },
+	{ "rrbe", 0x2a, INSTR_RRE_RR },
+	{ "sske", 0x2b, INSTR_RRE_RR },
+	{ "tb", 0x2c, INSTR_RRE_0R },
+	{ "dxr", 0x2d, INSTR_RRE_F0 },
+	{ "pgin", 0x2e, INSTR_RRE_RR },
+	{ "pgout", 0x2f, INSTR_RRE_RR },
+	{ "csch", 0x30, INSTR_S_00 },
+	{ "hsch", 0x31, INSTR_S_00 },
+	{ "msch", 0x32, INSTR_S_RD },
+	{ "ssch", 0x33, INSTR_S_RD },
+	{ "stsch", 0x34, INSTR_S_RD },
+	{ "tsch", 0x35, INSTR_S_RD },
+	{ "tpi", 0x36, INSTR_S_RD },
+	{ "sal", 0x37, INSTR_S_00 },
+	{ "rsch", 0x38, INSTR_S_00 },
+	{ "stcrw", 0x39, INSTR_S_RD },
+	{ "stcps", 0x3a, INSTR_S_RD },
+	{ "rchp", 0x3b, INSTR_S_00 },
+	{ "schm", 0x3c, INSTR_S_00 },
+	{ "bakr", 0x40, INSTR_RRE_RR },
+	{ "cksm", 0x41, INSTR_RRE_RR },
+	{ "sqdr", 0x44, INSTR_RRE_F0 },
+	{ "sqer", 0x45, INSTR_RRE_F0 },
+	{ "stura", 0x46, INSTR_RRE_RR },
+	{ "msta", 0x47, INSTR_RRE_R0 },
+	{ "palb", 0x48, INSTR_RRE_00 },
+	{ "ereg", 0x49, INSTR_RRE_RR },
+	{ "esta", 0x4a, INSTR_RRE_RR },
+	{ "lura", 0x4b, INSTR_RRE_RR },
+	{ "tar", 0x4c, INSTR_RRE_AR },
+	{ "cpya", INSTR_RRE_AA },
+	{ "sar", 0x4e, INSTR_RRE_AR },
+	{ "ear", 0x4f, INSTR_RRE_RA },
+	{ "csp", 0x50, INSTR_RRE_RR },
+	{ "msr", 0x52, INSTR_RRE_RR },
+	{ "mvpg", 0x54, INSTR_RRE_RR },
+	{ "mvst", 0x55, INSTR_RRE_RR },
+	{ "cuse", 0x57, INSTR_RRE_RR },
+	{ "bsg", 0x58, INSTR_RRE_RR },
+	{ "bsa", 0x5a, INSTR_RRE_RR },
+	{ "clst", 0x5d, INSTR_RRE_RR },
+	{ "srst", 0x5e, INSTR_RRE_RR },
+	{ "cmpsc", 0x63, INSTR_RRE_RR },
+	{ "cmpsc", 0x63, INSTR_RRE_RR },
+	{ "siga", 0x74, INSTR_S_RD },
+	{ "xsch", 0x76, INSTR_S_00 },
+	{ "rp", 0x77, INSTR_S_RD },
+	{ "stcke", 0x78, INSTR_S_RD },
+	{ "sacf", 0x79, INSTR_S_RD },
+	{ "stsi", 0x7d, INSTR_S_RD },
+	{ "srnm", 0x99, INSTR_S_RD },
+	{ "stfpc", 0x9c, INSTR_S_RD },
+	{ "lfpc", 0x9d, INSTR_S_RD },
+	{ "tre", 0xa5, INSTR_RRE_RR },
+	{ "cuutf", 0xa6, INSTR_RRE_RR },
+	{ "cutfu", 0xa7, INSTR_RRE_RR },
+	{ "stfl", 0xb1, INSTR_S_RD },
+	{ "trap4", 0xff, INSTR_S_RD },
+	{ "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_b3[] = {
+#ifdef CONFIG_64BIT
+	{ "maylr", 0x38, INSTR_RRF_F0FF },
+	{ "mylr", 0x39, INSTR_RRF_F0FF },
+	{ "mayr", 0x3a, INSTR_RRF_F0FF },
+	{ "myr", 0x3b, INSTR_RRF_F0FF },
+	{ "mayhr", 0x3c, INSTR_RRF_F0FF },
+	{ "myhr", 0x3d, INSTR_RRF_F0FF },
+	{ "cegbr", 0xa4, INSTR_RRE_RR },
+	{ "cdgbr", 0xa5, INSTR_RRE_RR },
+	{ "cxgbr", 0xa6, INSTR_RRE_RR },
+	{ "cgebr", 0xa8, INSTR_RRF_U0RF },
+	{ "cgdbr", 0xa9, INSTR_RRF_U0RF },
+	{ "cgxbr", 0xaa, INSTR_RRF_U0RF },
+	{ "cfer", 0xb8, INSTR_RRF_U0RF },
+	{ "cfdr", 0xb9, INSTR_RRF_U0RF },
+	{ "cfxr", 0xba, INSTR_RRF_U0RF },
+	{ "cegr", 0xc4, INSTR_RRE_RR },
+	{ "cdgr", 0xc5, INSTR_RRE_RR },
+	{ "cxgr", 0xc6, INSTR_RRE_RR },
+	{ "cger", 0xc8, INSTR_RRF_U0RF },
+	{ "cgdr", 0xc9, INSTR_RRF_U0RF },
+	{ "cgxr", 0xca, INSTR_RRF_U0RF },
+#endif
+	{ "lpebr", 0x00, INSTR_RRE_FF },
+	{ "lnebr", 0x01, INSTR_RRE_FF },
+	{ "ltebr", 0x02, INSTR_RRE_FF },
+	{ "lcebr", 0x03, INSTR_RRE_FF },
+	{ "ldebr", 0x04, INSTR_RRE_FF },
+	{ "lxdbr", 0x05, INSTR_RRE_FF },
+	{ "lxebr", 0x06, INSTR_RRE_FF },
+	{ "mxdbr", 0x07, INSTR_RRE_FF },
+	{ "kebr", 0x08, INSTR_RRE_FF },
+	{ "cebr", 0x09, INSTR_RRE_FF },
+	{ "aebr", 0x0a, INSTR_RRE_FF },
+	{ "sebr", 0x0b, INSTR_RRE_FF },
+	{ "mdebr", 0x0c, INSTR_RRE_FF },
+	{ "debr", 0x0d, INSTR_RRE_FF },
+	{ "maebr", 0x0e, INSTR_RRF_F0FF },
+	{ "msebr", 0x0f, INSTR_RRF_F0FF },
+	{ "lpdbr", 0x10, INSTR_RRE_FF },
+	{ "lndbr", 0x11, INSTR_RRE_FF },
+	{ "ltdbr", 0x12, INSTR_RRE_FF },
+	{ "lcdbr", 0x13, INSTR_RRE_FF },
+	{ "sqebr", 0x14, INSTR_RRE_FF },
+	{ "sqdbr", 0x15, INSTR_RRE_FF },
+	{ "sqxbr", 0x16, INSTR_RRE_FF },
+	{ "meebr", 0x17, INSTR_RRE_FF },
+	{ "kdbr", 0x18, INSTR_RRE_FF },
+	{ "cdbr", 0x19, INSTR_RRE_FF },
+	{ "adbr", 0x1a, INSTR_RRE_FF },
+	{ "sdbr", 0x1b, INSTR_RRE_FF },
+	{ "mdbr", 0x1c, INSTR_RRE_FF },
+	{ "ddbr", 0x1d, INSTR_RRE_FF },
+	{ "madbr", 0x1e, INSTR_RRF_F0FF },
+	{ "msdbr", 0x1f, INSTR_RRF_F0FF },
+	{ "lder", 0x24, INSTR_RRE_FF },
+	{ "lxdr", 0x25, INSTR_RRE_FF },
+	{ "lxer", 0x26, INSTR_RRE_FF },
+	{ "maer", 0x2e, INSTR_RRF_F0FF },
+	{ "mser", 0x2f, INSTR_RRF_F0FF },
+	{ "sqxr", 0x36, INSTR_RRE_FF },
+	{ "meer", 0x37, INSTR_RRE_FF },
+	{ "madr", 0x3e, INSTR_RRF_F0FF },
+	{ "msdr", 0x3f, INSTR_RRF_F0FF },
+	{ "lpxbr", 0x40, INSTR_RRE_FF },
+	{ "lnxbr", 0x41, INSTR_RRE_FF },
+	{ "ltxbr", 0x42, INSTR_RRE_FF },
+	{ "lcxbr", 0x43, INSTR_RRE_FF },
+	{ "ledbr", 0x44, INSTR_RRE_FF },
+	{ "ldxbr", 0x45, INSTR_RRE_FF },
+	{ "lexbr", 0x46, INSTR_RRE_FF },
+	{ "fixbr", 0x47, INSTR_RRF_U0FF },
+	{ "kxbr", 0x48, INSTR_RRE_FF },
+	{ "cxbr", 0x49, INSTR_RRE_FF },
+	{ "axbr", 0x4a, INSTR_RRE_FF },
+	{ "sxbr", 0x4b, INSTR_RRE_FF },
+	{ "mxbr", 0x4c, INSTR_RRE_FF },
+	{ "dxbr", 0x4d, INSTR_RRE_FF },
+	{ "tbedr", 0x50, INSTR_RRF_U0FF },
+	{ "tbdr", 0x51, INSTR_RRF_U0FF },
+	{ "diebr", 0x53, INSTR_RRF_FUFF },
+	{ "fiebr", 0x57, INSTR_RRF_U0FF },
+	{ "thder", 0x58, INSTR_RRE_RR },
+	{ "thdr", 0x59, INSTR_RRE_RR },
+	{ "didbr", 0x5b, INSTR_RRF_FUFF },
+	{ "fidbr", 0x5f, INSTR_RRF_U0FF },
+	{ "lpxr", 0x60, INSTR_RRE_FF },
+	{ "lnxr", 0x61, INSTR_RRE_FF },
+	{ "ltxr", 0x62, INSTR_RRE_FF },
+	{ "lcxr", 0x63, INSTR_RRE_FF },
+	{ "lxr", 0x65, INSTR_RRE_RR },
+	{ "lexr", 0x66, INSTR_RRE_FF },
+	{ "fixr", 0x67, INSTR_RRF_U0FF },
+	{ "cxr", 0x69, INSTR_RRE_FF },
+	{ "lzer", 0x74, INSTR_RRE_R0 },
+	{ "lzdr", 0x75, INSTR_RRE_R0 },
+	{ "lzxr", 0x76, INSTR_RRE_R0 },
+	{ "fier", 0x77, INSTR_RRF_U0FF },
+	{ "fidr", 0x7f, INSTR_RRF_U0FF },
+	{ "sfpc", 0x84, INSTR_RRE_RR_OPT },
+	{ "efpc", 0x8c, INSTR_RRE_RR_OPT },
+	{ "cefbr", 0x94, INSTR_RRE_RF },
+	{ "cdfbr", 0x95, INSTR_RRE_RF },
+	{ "cxfbr", 0x96, INSTR_RRE_RF },
+	{ "cfebr", 0x98, INSTR_RRF_U0RF },
+	{ "cfdbr", 0x99, INSTR_RRF_U0RF },
+	{ "cfxbr", 0x9a, INSTR_RRF_U0RF },
+	{ "cefr", 0xb4, INSTR_RRE_RF },
+	{ "cdfr", 0xb5, INSTR_RRE_RF },
+	{ "cxfr", 0xb6, INSTR_RRE_RF },
+	{ "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_b9[] = {
+#ifdef CONFIG_64BIT
+	{ "lpgr", 0x00, INSTR_RRE_RR },
+	{ "lngr", 0x01, INSTR_RRE_RR },
+	{ "ltgr", 0x02, INSTR_RRE_RR },
+	{ "lcgr", 0x03, INSTR_RRE_RR },
+	{ "lgr", 0x04, INSTR_RRE_RR },
+	{ "lurag", 0x05, INSTR_RRE_RR },
+	{ "lgbr", 0x06, INSTR_RRE_RR },
+	{ "lghr", 0x07, INSTR_RRE_RR },
+	{ "agr", 0x08, INSTR_RRE_RR },
+	{ "sgr", 0x09, INSTR_RRE_RR },
+	{ "algr", 0x0a, INSTR_RRE_RR },
+	{ "slgr", 0x0b, INSTR_RRE_RR },
+	{ "msgr", 0x0c, INSTR_RRE_RR },
+	{ "dsgr", 0x0d, INSTR_RRE_RR },
+	{ "eregg", 0x0e, INSTR_RRE_RR },
+	{ "lrvgr", 0x0f, INSTR_RRE_RR },
+	{ "lpgfr", 0x10, INSTR_RRE_RR },
+	{ "lngfr", 0x11, INSTR_RRE_RR },
+	{ "ltgfr", 0x12, INSTR_RRE_RR },
+	{ "lcgfr", 0x13, INSTR_RRE_RR },
+	{ "lgfr", 0x14, INSTR_RRE_RR },
+	{ "llgfr", 0x16, INSTR_RRE_RR },
+	{ "llgtr", 0x17, INSTR_RRE_RR },
+	{ "agfr", 0x18, INSTR_RRE_RR },
+	{ "sgfr", 0x19, INSTR_RRE_RR },
+	{ "algfr", 0x1a, INSTR_RRE_RR },
+	{ "slgfr", 0x1b, INSTR_RRE_RR },
+	{ "msgfr", 0x1c, INSTR_RRE_RR },
+	{ "dsgfr", 0x1d, INSTR_RRE_RR },
+	{ "cgr", 0x20, INSTR_RRE_RR },
+	{ "clgr", 0x21, INSTR_RRE_RR },
+	{ "sturg", 0x25, INSTR_RRE_RR },
+	{ "lbr", 0x26, INSTR_RRE_RR },
+	{ "lhr", 0x27, INSTR_RRE_RR },
+	{ "cgfr", 0x30, INSTR_RRE_RR },
+	{ "clgfr", 0x31, INSTR_RRE_RR },
+	{ "bctgr", 0x46, INSTR_RRE_RR },
+	{ "ngr", 0x80, INSTR_RRE_RR },
+	{ "ogr", 0x81, INSTR_RRE_RR },
+	{ "xgr", 0x82, INSTR_RRE_RR },
+	{ "flogr", 0x83, INSTR_RRE_RR },
+	{ "llgcr", 0x84, INSTR_RRE_RR },
+	{ "llghr", 0x85, INSTR_RRE_RR },
+	{ "mlgr", 0x86, INSTR_RRE_RR },
+	{ "dlgr", 0x87, INSTR_RRE_RR },
+	{ "alcgr", 0x88, INSTR_RRE_RR },
+	{ "slbgr", 0x89, INSTR_RRE_RR },
+	{ "cspg", 0x8a, INSTR_RRE_RR },
+	{ "idte", 0x8e, INSTR_RRF_R0RR },
+	{ "llcr", 0x94, INSTR_RRE_RR },
+	{ "llhr", 0x95, INSTR_RRE_RR },
+	{ "esea", 0x9d, INSTR_RRE_R0 },
+	{ "lptea", 0xaa, INSTR_RRF_RURR },
+	{ "cu14", 0xb0, INSTR_RRF_M0RR },
+	{ "cu24", 0xb1, INSTR_RRF_M0RR },
+	{ "cu41", 0xb2, INSTR_RRF_M0RR },
+	{ "cu42", 0xb3, INSTR_RRF_M0RR },
+#endif
+	{ "kmac", 0x1e, INSTR_RRE_RR },
+	{ "lrvr", 0x1f, INSTR_RRE_RR },
+	{ "km", 0x2e, INSTR_RRE_RR },
+	{ "kmc", 0x2f, INSTR_RRE_RR },
+	{ "kimd", 0x3e, INSTR_RRE_RR },
+	{ "klmd", 0x3f, INSTR_RRE_RR },
+	{ "epsw", 0x8d, INSTR_RRE_RR },
+	{ "trtt", 0x90, INSTR_RRE_RR },
+	{ "trtt", 0x90, INSTR_RRF_M0RR },
+	{ "trto", 0x91, INSTR_RRE_RR },
+	{ "trto", 0x91, INSTR_RRF_M0RR },
+	{ "trot", 0x92, INSTR_RRE_RR },
+	{ "trot", 0x92, INSTR_RRF_M0RR },
+	{ "troo", 0x93, INSTR_RRE_RR },
+	{ "troo", 0x93, INSTR_RRF_M0RR },
+	{ "mlr", 0x96, INSTR_RRE_RR },
+	{ "dlr", 0x97, INSTR_RRE_RR },
+	{ "alcr", 0x98, INSTR_RRE_RR },
+	{ "slbr", 0x99, INSTR_RRE_RR },
+	{ "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_c0[] = {
+#ifdef CONFIG_64BIT
+	{ "lgfi", 0x01, INSTR_RIL_RI },
+	{ "xihf", 0x06, INSTR_RIL_RU },
+	{ "xilf", 0x07, INSTR_RIL_RU },
+	{ "iihf", 0x08, INSTR_RIL_RU },
+	{ "iilf", 0x09, INSTR_RIL_RU },
+	{ "nihf", 0x0a, INSTR_RIL_RU },
+	{ "nilf", 0x0b, INSTR_RIL_RU },
+	{ "oihf", 0x0c, INSTR_RIL_RU },
+	{ "oilf", 0x0d, INSTR_RIL_RU },
+	{ "llihf", 0x0e, INSTR_RIL_RU },
+	{ "llilf", 0x0f, INSTR_RIL_RU },
+#endif
+	{ "larl", 0x00, INSTR_RIL_RP },
+	{ "brcl", 0x04, INSTR_RIL_UP },
+	{ "brasl", 0x05, INSTR_RIL_RP },
+	{ "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_c2[] = {
+#ifdef CONFIG_64BIT
+	{ "slgfi", 0x04, INSTR_RIL_RU },
+	{ "slfi", 0x05, INSTR_RIL_RU },
+	{ "agfi", 0x08, INSTR_RIL_RI },
+	{ "afi", 0x09, INSTR_RIL_RI },
+	{ "algfi", 0x0a, INSTR_RIL_RU },
+	{ "alfi", 0x0b, INSTR_RIL_RU },
+	{ "cgfi", 0x0c, INSTR_RIL_RI },
+	{ "cfi", 0x0d, INSTR_RIL_RI },
+	{ "clgfi", 0x0e, INSTR_RIL_RU },
+	{ "clfi", 0x0f, INSTR_RIL_RU },
+#endif
+	{ "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_c8[] = {
+#ifdef CONFIG_64BIT
+	{ "mvcos", 0x00, INSTR_SSF_RRDRD },
+#endif
+	{ "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_e3[] = {
+#ifdef CONFIG_64BIT
+	{ "ltg", 0x02, INSTR_RXY_RRRD },
+	{ "lrag", 0x03, INSTR_RXY_RRRD },
+	{ "lg", 0x04, INSTR_RXY_RRRD },
+	{ "cvby", 0x06, INSTR_RXY_RRRD },
+	{ "ag", 0x08, INSTR_RXY_RRRD },
+	{ "sg", 0x09, INSTR_RXY_RRRD },
+	{ "alg", 0x0a, INSTR_RXY_RRRD },
+	{ "slg", 0x0b, INSTR_RXY_RRRD },
+	{ "msg", 0x0c, INSTR_RXY_RRRD },
+	{ "dsg", 0x0d, INSTR_RXY_RRRD },
+	{ "cvbg", 0x0e, INSTR_RXY_RRRD },
+	{ "lrvg", 0x0f, INSTR_RXY_RRRD },
+	{ "lt", 0x12, INSTR_RXY_RRRD },
+	{ "lray", 0x13, INSTR_RXY_RRRD },
+	{ "lgf", 0x14, INSTR_RXY_RRRD },
+	{ "lgh", 0x15, INSTR_RXY_RRRD },
+	{ "llgf", 0x16, INSTR_RXY_RRRD },
+	{ "llgt", 0x17, INSTR_RXY_RRRD },
+	{ "agf", 0x18, INSTR_RXY_RRRD },
+	{ "sgf", 0x19, INSTR_RXY_RRRD },
+	{ "algf", 0x1a, INSTR_RXY_RRRD },
+	{ "slgf", 0x1b, INSTR_RXY_RRRD },
+	{ "msgf", 0x1c, INSTR_RXY_RRRD },
+	{ "dsgf", 0x1d, INSTR_RXY_RRRD },
+	{ "cg", 0x20, INSTR_RXY_RRRD },
+	{ "clg", 0x21, INSTR_RXY_RRRD },
+	{ "stg", 0x24, INSTR_RXY_RRRD },
+	{ "cvdy", 0x26, INSTR_RXY_RRRD },
+	{ "cvdg", 0x2e, INSTR_RXY_RRRD },
+	{ "strvg", 0x2f, INSTR_RXY_RRRD },
+	{ "cgf", 0x30, INSTR_RXY_RRRD },
+	{ "clgf", 0x31, INSTR_RXY_RRRD },
+	{ "strvh", 0x3f, INSTR_RXY_RRRD },
+	{ "bctg", 0x46, INSTR_RXY_RRRD },
+	{ "sty", 0x50, INSTR_RXY_RRRD },
+	{ "msy", 0x51, INSTR_RXY_RRRD },
+	{ "ny", 0x54, INSTR_RXY_RRRD },
+	{ "cly", 0x55, INSTR_RXY_RRRD },
+	{ "oy", 0x56, INSTR_RXY_RRRD },
+	{ "xy", 0x57, INSTR_RXY_RRRD },
+	{ "ly", 0x58, INSTR_RXY_RRRD },
+	{ "cy", 0x59, INSTR_RXY_RRRD },
+	{ "ay", 0x5a, INSTR_RXY_RRRD },
+	{ "sy", 0x5b, INSTR_RXY_RRRD },
+	{ "aly", 0x5e, INSTR_RXY_RRRD },
+	{ "sly", 0x5f, INSTR_RXY_RRRD },
+	{ "sthy", 0x70, INSTR_RXY_RRRD },
+	{ "lay", 0x71, INSTR_RXY_RRRD },
+	{ "stcy", 0x72, INSTR_RXY_RRRD },
+	{ "icy", 0x73, INSTR_RXY_RRRD },
+	{ "lb", 0x76, INSTR_RXY_RRRD },
+	{ "lgb", 0x77, INSTR_RXY_RRRD },
+	{ "lhy", 0x78, INSTR_RXY_RRRD },
+	{ "chy", 0x79, INSTR_RXY_RRRD },
+	{ "ahy", 0x7a, INSTR_RXY_RRRD },
+	{ "shy", 0x7b, INSTR_RXY_RRRD },
+	{ "ng", 0x80, INSTR_RXY_RRRD },
+	{ "og", 0x81, INSTR_RXY_RRRD },
+	{ "xg", 0x82, INSTR_RXY_RRRD },
+	{ "mlg", 0x86, INSTR_RXY_RRRD },
+	{ "dlg", 0x87, INSTR_RXY_RRRD },
+	{ "alcg", 0x88, INSTR_RXY_RRRD },
+	{ "slbg", 0x89, INSTR_RXY_RRRD },
+	{ "stpq", 0x8e, INSTR_RXY_RRRD },
+	{ "lpq", 0x8f, INSTR_RXY_RRRD },
+	{ "llgc", 0x90, INSTR_RXY_RRRD },
+	{ "llgh", 0x91, INSTR_RXY_RRRD },
+	{ "llc", 0x94, INSTR_RXY_RRRD },
+	{ "llh", 0x95, INSTR_RXY_RRRD },
+#endif
+	{ "lrv", 0x1e, INSTR_RXY_RRRD },
+	{ "lrvh", 0x1f, INSTR_RXY_RRRD },
+	{ "strv", 0x3e, INSTR_RXY_RRRD },
+	{ "ml", 0x96, INSTR_RXY_RRRD },
+	{ "dl", 0x97, INSTR_RXY_RRRD },
+	{ "alc", 0x98, INSTR_RXY_RRRD },
+	{ "slb", 0x99, INSTR_RXY_RRRD },
+	{ "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_e5[] = {
+#ifdef CONFIG_64BIT
+	{ "strag", 0x02, INSTR_SSE_RDRD },
+#endif
+	{ "lasp", 0x00, INSTR_SSE_RDRD },
+	{ "tprot", 0x01, INSTR_SSE_RDRD },
+	{ "mvcsk", 0x0e, INSTR_SSE_RDRD },
+	{ "mvcdk", 0x0f, INSTR_SSE_RDRD },
+	{ "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_eb[] = {
+#ifdef CONFIG_64BIT
+	{ "lmg", 0x04, INSTR_RSY_RRRD },
+	{ "srag", 0x0a, INSTR_RSY_RRRD },
+	{ "slag", 0x0b, INSTR_RSY_RRRD },
+	{ "srlg", 0x0c, INSTR_RSY_RRRD },
+	{ "sllg", 0x0d, INSTR_RSY_RRRD },
+	{ "tracg", 0x0f, INSTR_RSY_RRRD },
+	{ "csy", 0x14, INSTR_RSY_RRRD },
+	{ "rllg", 0x1c, INSTR_RSY_RRRD },
+	{ "clmh", 0x20, INSTR_RSY_RURD },
+	{ "clmy", 0x21, INSTR_RSY_RURD },
+	{ "stmg", 0x24, INSTR_RSY_RRRD },
+	{ "stctg", 0x25, INSTR_RSY_CCRD },
+	{ "stmh", 0x26, INSTR_RSY_RRRD },
+	{ "stcmh", 0x2c, INSTR_RSY_RURD },
+	{ "stcmy", 0x2d, INSTR_RSY_RURD },
+	{ "lctlg", 0x2f, INSTR_RSY_CCRD },
+	{ "csg", 0x30, INSTR_RSY_RRRD },
+	{ "cdsy", 0x31, INSTR_RSY_RRRD },
+	{ "cdsg", 0x3e, INSTR_RSY_RRRD },
+	{ "bxhg", 0x44, INSTR_RSY_RRRD },
+	{ "bxleg", 0x45, INSTR_RSY_RRRD },
+	{ "tmy", 0x51, INSTR_SIY_URD },
+	{ "mviy", 0x52, INSTR_SIY_URD },
+	{ "niy", 0x54, INSTR_SIY_URD },
+	{ "cliy", 0x55, INSTR_SIY_URD },
+	{ "oiy", 0x56, INSTR_SIY_URD },
+	{ "xiy", 0x57, INSTR_SIY_URD },
+	{ "icmh", 0x80, INSTR_RSE_RURD },
+	{ "icmh", 0x80, INSTR_RSY_RURD },
+	{ "icmy", 0x81, INSTR_RSY_RURD },
+	{ "clclu", 0x8f, INSTR_RSY_RRRD },
+	{ "stmy", 0x90, INSTR_RSY_RRRD },
+	{ "lmh", 0x96, INSTR_RSY_RRRD },
+	{ "lmy", 0x98, INSTR_RSY_RRRD },
+	{ "lamy", 0x9a, INSTR_RSY_AARD },
+	{ "stamy", 0x9b, INSTR_RSY_AARD },
+#endif
+	{ "rll", 0x1d, INSTR_RSY_RRRD },
+	{ "mvclu", 0x8e, INSTR_RSY_RRRD },
+	{ "tp", 0xc0, INSTR_RSL_R0RD },
+	{ "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_ec[] = {
+#ifdef CONFIG_64BIT
+	{ "brxhg", 0x44, INSTR_RIE_RRP },
+	{ "brxlg", 0x45, INSTR_RIE_RRP },
+#endif
+	{ "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_ed[] = {
+#ifdef CONFIG_64BIT
+	{ "mayl", 0x38, INSTR_RXF_FRRDF },
+	{ "myl", 0x39, INSTR_RXF_FRRDF },
+	{ "may", 0x3a, INSTR_RXF_FRRDF },
+	{ "my", 0x3b, INSTR_RXF_FRRDF },
+	{ "mayh", 0x3c, INSTR_RXF_FRRDF },
+	{ "myh", 0x3d, INSTR_RXF_FRRDF },
+	{ "ley", 0x64, INSTR_RXY_FRRD },
+	{ "ldy", 0x65, INSTR_RXY_FRRD },
+	{ "stey", 0x66, INSTR_RXY_FRRD },
+	{ "stdy", 0x67, INSTR_RXY_FRRD },
+#endif
+	{ "ldeb", 0x04, INSTR_RXE_FRRD },
+	{ "lxdb", 0x05, INSTR_RXE_FRRD },
+	{ "lxeb", 0x06, INSTR_RXE_FRRD },
+	{ "mxdb", 0x07, INSTR_RXE_FRRD },
+	{ "keb", 0x08, INSTR_RXE_FRRD },
+	{ "ceb", 0x09, INSTR_RXE_FRRD },
+	{ "aeb", 0x0a, INSTR_RXE_FRRD },
+	{ "seb", 0x0b, INSTR_RXE_FRRD },
+	{ "mdeb", 0x0c, INSTR_RXE_FRRD },
+	{ "deb", 0x0d, INSTR_RXE_FRRD },
+	{ "maeb", 0x0e, INSTR_RXF_FRRDF },
+	{ "mseb", 0x0f, INSTR_RXF_FRRDF },
+	{ "tceb", 0x10, INSTR_RXE_FRRD },
+	{ "tcdb", 0x11, INSTR_RXE_FRRD },
+	{ "tcxb", 0x12, INSTR_RXE_FRRD },
+	{ "sqeb", 0x14, INSTR_RXE_FRRD },
+	{ "sqdb", 0x15, INSTR_RXE_FRRD },
+	{ "meeb", 0x17, INSTR_RXE_FRRD },
+	{ "kdb", 0x18, INSTR_RXE_FRRD },
+	{ "cdb", 0x19, INSTR_RXE_FRRD },
+	{ "adb", 0x1a, INSTR_RXE_FRRD },
+	{ "sdb", 0x1b, INSTR_RXE_FRRD },
+	{ "mdb", 0x1c, INSTR_RXE_FRRD },
+	{ "ddb", 0x1d, INSTR_RXE_FRRD },
+	{ "madb", 0x1e, INSTR_RXF_FRRDF },
+	{ "msdb", 0x1f, INSTR_RXF_FRRDF },
+	{ "lde", 0x24, INSTR_RXE_FRRD },
+	{ "lxd", 0x25, INSTR_RXE_FRRD },
+	{ "lxe", 0x26, INSTR_RXE_FRRD },
+	{ "mae", 0x2e, INSTR_RXF_FRRDF },
+	{ "mse", 0x2f, INSTR_RXF_FRRDF },
+	{ "sqe", 0x34, INSTR_RXE_FRRD },
+	{ "mee", 0x37, INSTR_RXE_FRRD },
+	{ "mad", 0x3e, INSTR_RXF_FRRDF },
+	{ "msd", 0x3f, INSTR_RXF_FRRDF },
+	{ "", 0, INSTR_INVALID }
+};
+
+/* Extracts an operand value from an instruction.  */
+static unsigned int extract_operand(unsigned char *code,
+				    const struct operand *operand)
+{
+	unsigned int val;
+	int bits;
+
+	/* Extract fragments of the operand byte for byte.  */
+	code += operand->shift / 8;
+	bits = (operand->shift & 7) + operand->bits;
+	val = 0;
+	do {
+		val <<= 8;
+		val |= (unsigned int) *code++;
+		bits -= 8;
+	} while (bits > 0);
+	val >>= -bits;
+	val &= ((1U << (operand->bits - 1)) << 1) - 1;
+
+	/* Check for special long displacement case.  */
+	if (operand->bits == 20 && operand->shift == 20)
+		val = (val & 0xff) << 12 | (val & 0xfff00) >> 8;
+
+	/* Sign extend value if the operand is signed or pc relative.  */
+	if ((operand->flags & (OPERAND_SIGNED | OPERAND_PCREL)) &&
+	    (val & (1U << (operand->bits - 1))))
+		val |= (-1U << (operand->bits - 1)) << 1;
+
+	/* Double value if the operand is pc relative.	*/
+	if (operand->flags & OPERAND_PCREL)
+		val <<= 1;
+
+	/* Length x in an instructions has real length x + 1.  */
+	if (operand->flags & OPERAND_LENGTH)
+		val++;
+	return val;
+}
+
+static inline int insn_length(unsigned char code)
+{
+	return ((((int) code + 64) >> 7) + 1) << 1;
+}
+
+static struct insn *find_insn(unsigned char *code)
+{
+	unsigned char opfrag = code[1];
+	unsigned char opmask;
+	struct insn *table;
+
+	switch (code[0]) {
+	case 0x01:
+		table = opcode_01;
+		break;
+	case 0xa5:
+		table = opcode_a5;
+		break;
+	case 0xa7:
+		table = opcode_a7;
+		break;
+	case 0xb2:
+		table = opcode_b2;
+		break;
+	case 0xb3:
+		table = opcode_b3;
+		break;
+	case 0xb9:
+		table = opcode_b9;
+		break;
+	case 0xc0:
+		table = opcode_c0;
+		break;
+	case 0xc2:
+		table = opcode_c2;
+		break;
+	case 0xc8:
+		table = opcode_c8;
+		break;
+	case 0xe3:
+		table = opcode_e3;
+		opfrag = code[5];
+		break;
+	case 0xe5:
+		table = opcode_e5;
+		break;
+	case 0xeb:
+		table = opcode_eb;
+		opfrag = code[5];
+		break;
+	case 0xec:
+		table = opcode_ec;
+		opfrag = code[5];
+		break;
+	case 0xed:
+		table = opcode_ed;
+		opfrag = code[5];
+		break;
+	default:
+		table = opcode;
+		opfrag = code[0];
+		break;
+	}
+	while (table->format != INSTR_INVALID) {
+		opmask = formats[table->format][0];
+		if (table->opfrag == (opfrag & opmask))
+			return table;
+		table++;
+	}
+	return NULL;
+}
+
+static int print_insn(char *buffer, unsigned char *code, unsigned long addr)
+{
+	struct insn *insn;
+	const unsigned char *ops;
+	const struct operand *operand;
+	unsigned int value;
+	char separator;
+	char *ptr;
+
+	ptr = buffer;
+	insn = find_insn(code);
+	if (insn) {
+		ptr += sprintf(ptr, "%.5s\t", insn->name);
+		/* Extract the operands. */
+		separator = 0;
+		for (ops = formats[insn->format] + 1; *ops != 0; ops++) {
+			operand = operands + *ops;
+			value = extract_operand(code, operand);
+			if ((operand->flags & OPERAND_INDEX)  && value == 0)
+				continue;
+			if ((operand->flags & OPERAND_BASE) &&
+			    value == 0 && separator == '(') {
+				separator = ',';
+				continue;
+			}
+			if (separator)
+				ptr += sprintf(ptr, "%c", separator);
+			if (operand->flags & OPERAND_GPR)
+				ptr += sprintf(ptr, "%%r%i", value);
+			else if (operand->flags & OPERAND_FPR)
+				ptr += sprintf(ptr, "%%f%i", value);
+			else if (operand->flags & OPERAND_AR)
+				ptr += sprintf(ptr, "%%a%i", value);
+			else if (operand->flags & OPERAND_CR)
+				ptr += sprintf(ptr, "%%c%i", value);
+			else if (operand->flags & OPERAND_PCREL)
+				ptr += sprintf(ptr, "%lx", value + addr);
+			else if (operand->flags & OPERAND_SIGNED)
+				ptr += sprintf(ptr, "%i", value);
+			else
+				ptr += sprintf(ptr, "%u", value);
+			if (operand->flags & OPERAND_DISP)
+				separator = '(';
+			else if (operand->flags & OPERAND_BASE) {
+				ptr += sprintf(ptr, ")");
+				separator = ',';
+			} else
+				separator = ',';
+		}
+	} else
+		ptr += sprintf(ptr, "unknown");
+	return (int) (ptr - buffer);
+}
+
+void show_code(struct pt_regs *regs)
+{
+	char *mode = (regs->psw.mask & PSW_MASK_PSTATE) ? "User" : "Krnl";
+	unsigned char code[64];
+	char buffer[64], *ptr;
+	mm_segment_t old_fs;
+	unsigned long addr;
+	int start, end, opsize, hops, i;
+
+	/* Get a snapshot of the 64 bytes surrounding the fault address. */
+	old_fs = get_fs();
+	set_fs((regs->psw.mask & PSW_MASK_PSTATE) ? USER_DS : KERNEL_DS);
+	for (start = 32; start && regs->psw.addr >= 34 - start; start -= 2) {
+		addr = regs->psw.addr - 34 + start;
+		if (__copy_from_user(code + start - 2,
+				     (char __user *) addr, 2))
+			break;
+	}
+	for (end = 32; end < 64; end += 2) {
+		addr = regs->psw.addr + end - 32;
+		if (__copy_from_user(code + end,
+				     (char __user *) addr, 2))
+			break;
+	}
+	set_fs(old_fs);
+	/* Code snapshot useable ? */
+	if ((regs->psw.addr & 1) || start >= end) {
+		printk("%s Code: Bad PSW.\n", mode);
+		return;
+	}
+	/* Find a starting point for the disassembly. */
+	while (start < 32) {
+		hops = 0;
+		for (i = 0, hops = 0; start + i < 32 && hops < 3; hops++) {
+			if (!find_insn(code + start + i))
+				break;
+			i += insn_length(code[start + i]);
+		}
+		if (start + i == 32)
+			/* Looks good, sequence ends at PSW. */
+			break;
+		start += 2;
+	}
+	/* Decode the instructions. */
+	ptr = buffer;
+	ptr += sprintf(ptr, "%s Code:", mode);
+	hops = 0;
+	while (start < end && hops < 8) {
+		*ptr++ = (start == 32) ? '>' : ' ';
+		addr = regs->psw.addr + start - 32;
+		ptr += sprintf(ptr, ONELONG, addr);
+		opsize = insn_length(code[start]);
+		if (start + opsize >= end)
+			break;
+		for (i = 0; i < opsize; i++)
+			ptr += sprintf(ptr, "%02x", code[start + i]);
+		*ptr++ = '\t';
+		if (i < 6)
+			*ptr++ = '\t';
+		ptr += print_insn(ptr, code + start, addr);
+		start += opsize;
+		printk(buffer);
+		ptr = buffer;
+		ptr += sprintf(ptr, "\n          ");
+		hops++;
+	}
+	printk("\n");
+}
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index 5e47936..50538e5 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -253,11 +253,10 @@
 			break;
 #endif
 		/*
-		 * Finish memory detection at the first hole, unless
-		 * - we reached the hsa -> skip it.
-		 * - we know there must be more.
+		 * Finish memory detection at the first hole
+		 * if storage size is unknown.
 		 */
-		if (cc == -1UL && !memsize && old_addr != ADDR2G)
+		if (cc == -1UL && !memsize)
 			break;
 		if (memsize && addr >= memsize)
 			break;
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S
index dddc3de..c8a2212 100644
--- a/arch/s390/kernel/entry.S
+++ b/arch/s390/kernel/entry.S
@@ -249,8 +249,6 @@
 	bnz	BASED(sysc_tracesys)
 	basr	%r14,%r8	  # call sys_xxxx
 	st	%r2,SP_R2(%r15)   # store return value (change R2 on stack)
-				  # ATTENTION: check sys_execve_glue before
-				  # changing anything here !!
 
 sysc_return:
 	tm	SP_PSW+1(%r15),0x01	# returning to user ?
@@ -381,50 +379,37 @@
 	b	BASED(sysc_return)
 
 #
-# clone, fork, vfork, exec and sigreturn need glue,
-# because they all expect pt_regs as parameter,
-# but are called with different parameter.
-# return-address is set up above
+# kernel_execve function needs to deal with pt_regs that is not
+# at the usual place
 #
-sys_clone_glue:
-	la	%r2,SP_PTREGS(%r15)	# load pt_regs
-	l	%r1,BASED(.Lclone)
-	br	%r1			# branch to sys_clone
-
-sys_fork_glue:
-	la	%r2,SP_PTREGS(%r15)	# load pt_regs
-	l	%r1,BASED(.Lfork)
-	br	%r1			# branch to sys_fork
-
-sys_vfork_glue:
-	la	%r2,SP_PTREGS(%r15)	# load pt_regs
-	l	%r1,BASED(.Lvfork)
-	br	%r1			# branch to sys_vfork
-
-sys_execve_glue:
-	la	%r2,SP_PTREGS(%r15)	# load pt_regs
-	l	%r1,BASED(.Lexecve)
-	lr	%r12,%r14		# save return address
-	basr	%r14,%r1		# call sys_execve
-	ltr	%r2,%r2			# check if execve failed
-	bnz	0(%r12)			# it did fail -> store result in gpr2
-	b	4(%r12)			# SKIP ST 2,SP_R2(15) after BASR 14,8
-					# in system_call/sysc_tracesys
-
-sys_sigreturn_glue:
-	la	%r2,SP_PTREGS(%r15)	# load pt_regs as parameter
-	l	%r1,BASED(.Lsigreturn)
-	br	%r1			# branch to sys_sigreturn
-
-sys_rt_sigreturn_glue:
-	la	%r2,SP_PTREGS(%r15)	# load pt_regs as parameter
-	l	%r1,BASED(.Lrt_sigreturn)
-	br	%r1			# branch to sys_sigreturn
-
-sys_sigaltstack_glue:
-	la	%r4,SP_PTREGS(%r15)	# load pt_regs as parameter
-	l	%r1,BASED(.Lsigaltstack)
-	br	%r1			# branch to sys_sigreturn
+	.globl	kernel_execve
+kernel_execve:
+	stm	%r12,%r15,48(%r15)
+	lr	%r14,%r15
+	l	%r13,__LC_SVC_NEW_PSW+4
+	s	%r15,BASED(.Lc_spsize)
+	st	%r14,__SF_BACKCHAIN(%r15)
+	la	%r12,SP_PTREGS(%r15)
+	xc	0(__PT_SIZE,%r12),0(%r12)
+	l	%r1,BASED(.Ldo_execve)
+	lr	%r5,%r12
+	basr	%r14,%r1
+	ltr	%r2,%r2
+	be	BASED(0f)
+	a	%r15,BASED(.Lc_spsize)
+	lm	%r12,%r15,48(%r15)
+	br	%r14
+	# execve succeeded.
+0:	stnsm	__SF_EMPTY(%r15),0xfc	# disable interrupts
+	l	%r15,__LC_KERNEL_STACK	# load ksp
+	s	%r15,BASED(.Lc_spsize)	# make room for registers & psw
+	l	%r9,__LC_THREAD_INFO
+	mvc	SP_PTREGS(__PT_SIZE,%r15),0(%r12)	# copy pt_regs
+	xc	__SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15)
+	stosm	__SF_EMPTY(%r15),0x03	# reenable interrupts
+	l	%r1,BASED(.Lexecve_tail)
+	basr	%r14,%r1
+	b	BASED(sysc_return)
 
 /*
  * Program check handler routine
@@ -1031,19 +1016,11 @@
 .Ldo_extint:	.long	do_extint
 .Ldo_signal:	.long	do_signal
 .Lhandle_per:	.long	do_single_step
+.Ldo_execve:	.long	do_execve
+.Lexecve_tail:	.long	execve_tail
 .Ljump_table:	.long	pgm_check_table
 .Lschedule:	.long	schedule
-.Lclone:	.long	sys_clone
-.Lexecve:	.long	sys_execve
-.Lfork: 	.long	sys_fork
-.Lrt_sigreturn: .long	sys_rt_sigreturn
-.Lrt_sigsuspend:
-		.long	sys_rt_sigsuspend
-.Lsigreturn:	.long	sys_sigreturn
-.Lsigsuspend:	.long	sys_sigsuspend
-.Lsigaltstack:	.long	sys_sigaltstack
 .Ltrace:	.long	syscall_trace
-.Lvfork:	.long	sys_vfork
 .Lschedtail:	.long	schedule_tail
 .Lsysc_table:	.long	sys_call_table
 #ifdef CONFIG_TRACE_IRQFLAGS
diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S
index 0f758c3..93745fd 100644
--- a/arch/s390/kernel/entry64.S
+++ b/arch/s390/kernel/entry64.S
@@ -244,8 +244,6 @@
 	jnz	sysc_tracesys
 	basr	%r14,%r8	# call sys_xxxx
 	stg	%r2,SP_R2(%r15) # store return value (change R2 on stack)
-				# ATTENTION: check sys_execve_glue before
-				# changing anything here !!
 
 sysc_return:
 	tm	SP_PSW+1(%r15),0x01	# returning to user ?
@@ -371,77 +369,35 @@
 	j	sysc_return
 
 #
-# clone, fork, vfork, exec and sigreturn need glue,
-# because they all expect pt_regs as parameter,
-# but are called with different parameter.
-# return-address is set up above
+# kernel_execve function needs to deal with pt_regs that is not
+# at the usual place
 #
-sys_clone_glue:
-	la	%r2,SP_PTREGS(%r15)	# load pt_regs
-	jg	sys_clone		# branch to sys_clone
-
-#ifdef CONFIG_COMPAT
-sys32_clone_glue:
-	la	%r2,SP_PTREGS(%r15)	# load pt_regs
-	jg	sys32_clone		# branch to sys32_clone
-#endif
-
-sys_fork_glue:
-	la	%r2,SP_PTREGS(%r15)	# load pt_regs
-	jg	sys_fork		# branch to sys_fork
-
-sys_vfork_glue:
-	la	%r2,SP_PTREGS(%r15)	# load pt_regs
-	jg	sys_vfork		# branch to sys_vfork
-
-sys_execve_glue:
-	la	%r2,SP_PTREGS(%r15)	# load pt_regs
-	lgr	%r12,%r14		# save return address
-	brasl	%r14,sys_execve 	# call sys_execve
-	ltgr	%r2,%r2 		# check if execve failed
-	bnz	0(%r12) 		# it did fail -> store result in gpr2
-	b	6(%r12) 		# SKIP STG 2,SP_R2(15) in
-					# system_call/sysc_tracesys
-#ifdef CONFIG_COMPAT
-sys32_execve_glue:
-	la	%r2,SP_PTREGS(%r15)	# load pt_regs
-	lgr	%r12,%r14		# save return address
-	brasl	%r14,sys32_execve	# call sys32_execve
-	ltgr	%r2,%r2 		# check if execve failed
-	bnz	0(%r12) 		# it did fail -> store result in gpr2
-	b	6(%r12) 		# SKIP STG 2,SP_R2(15) in
-					# system_call/sysc_tracesys
-#endif
-
-sys_sigreturn_glue:
-	la	%r2,SP_PTREGS(%r15)	# load pt_regs as parameter
-	jg	sys_sigreturn		# branch to sys_sigreturn
-
-#ifdef CONFIG_COMPAT
-sys32_sigreturn_glue:
-	la	%r2,SP_PTREGS(%r15)	# load pt_regs as parameter
-	jg	sys32_sigreturn 	# branch to sys32_sigreturn
-#endif
-
-sys_rt_sigreturn_glue:
-	la	%r2,SP_PTREGS(%r15)	# load pt_regs as parameter
-	jg	sys_rt_sigreturn	# branch to sys_sigreturn
-
-#ifdef CONFIG_COMPAT
-sys32_rt_sigreturn_glue:
-	la	%r2,SP_PTREGS(%r15)	# load pt_regs as parameter
-	jg	sys32_rt_sigreturn	# branch to sys32_sigreturn
-#endif
-
-sys_sigaltstack_glue:
-	la	%r4,SP_PTREGS(%r15)	# load pt_regs as parameter
-	jg	sys_sigaltstack 	# branch to sys_sigreturn
-
-#ifdef CONFIG_COMPAT
-sys32_sigaltstack_glue:
-	la	%r4,SP_PTREGS(%r15)	# load pt_regs as parameter
-	jg	sys32_sigaltstack_wrapper # branch to sys_sigreturn
-#endif
+	.globl	kernel_execve
+kernel_execve:
+	stmg	%r12,%r15,96(%r15)
+	lgr	%r14,%r15
+	aghi	%r15,-SP_SIZE
+	stg	%r14,__SF_BACKCHAIN(%r15)
+	la	%r12,SP_PTREGS(%r15)
+	xc	0(__PT_SIZE,%r12),0(%r12)
+	lgr	%r5,%r12
+	brasl	%r14,do_execve
+	ltgfr	%r2,%r2
+	je	0f
+	aghi	%r15,SP_SIZE
+	lmg	%r12,%r15,96(%r15)
+	br	%r14
+	# execve succeeded.
+0:	stnsm	__SF_EMPTY(%r15),0xfc	# disable interrupts
+	lg	%r15,__LC_KERNEL_STACK	# load ksp
+	aghi	%r15,-SP_SIZE		# make room for registers & psw
+	lg	%r13,__LC_SVC_NEW_PSW+8
+	lg	%r9,__LC_THREAD_INFO
+	mvc	SP_PTREGS(__PT_SIZE,%r15),0(%r12)	# copy pt_regs
+	xc	__SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
+	stosm	__SF_EMPTY(%r15),0x03	# reenable interrupts
+	brasl	%r14,execve_tail
+	j	sysc_return
 
 /*
  * Program check handler routine
diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S
index 3701070..a87b197 100644
--- a/arch/s390/kernel/head64.S
+++ b/arch/s390/kernel/head64.S
@@ -39,7 +39,69 @@
 	basr	%r13,0			# get base
 .LPG1:	sll	%r13,1			# remove high order bit
 	srl	%r13,1
-	lhi	%r1,1			# mode 1 = esame
+
+#ifdef CONFIG_ZFCPDUMP
+
+	# check if we have been ipled using zfcp dump:
+
+	tm	0xb9,0x01		# test if subchannel is enabled
+	jno	.nodump			# subchannel disabled
+	l	%r1,0xb8
+	la	%r5,.Lipl_schib-.LPG1(%r13)
+	stsch	0(%r5)			# get schib of subchannel
+	jne	.nodump			# schib not available
+	tm	5(%r5),0x01		# devno valid?
+	jno	.nodump
+	tm	4(%r5),0x80		# qdio capable device?
+	jno	.nodump
+	l	%r2,20(%r0)		# address of ipl parameter block
+	lhi	%r3,0
+	ic	%r3,0x148(%r2)		# get opt field
+	chi	%r3,0x20		# load with dump?
+	jne	.nodump
+
+	# store all prefix registers in case of load with dump:
+
+	la	%r7,0			# base register for 0 page
+	la	%r8,0			# first cpu
+	l	%r11,.Lpref_arr_ptr-.LPG1(%r13)	# address of prefix array
+	ahi	%r11,4			# skip boot cpu
+	lr	%r12,%r11
+	ahi	%r12,(CONFIG_NR_CPUS*4)	# end of prefix array
+	stap	.Lcurrent_cpu+2-.LPG1(%r13)	# store current cpu addr
+1:
+	cl	%r8,.Lcurrent_cpu-.LPG1(%r13)	# is ipl cpu ?
+	je	4f				# if yes get next cpu
+2:
+	lr	%r9,%r7
+	sigp	%r9,%r8,0x9		# stop & store status of cpu
+	brc	8,3f			# accepted
+	brc	4,4f			# status stored: next cpu
+	brc	2,2b			# busy: 	 try again
+	brc	1,4f			# not op:	 next cpu
+3:
+	mvc	0(4,%r11),264(%r7)	# copy prefix register to prefix array
+	ahi	%r11,4			# next element in prefix array
+	clr	%r11,%r12
+	je	5f			# no more space in prefix array
+4:
+	ahi	%r8,1				# next cpu (r8 += 1)
+	cl	%r8,.Llast_cpu-.LPG1(%r13)	# is last possible cpu ?
+	jl	1b				# jump if not last cpu
+5:
+	lhi	%r1,2			# mode 2 = esame (dump)
+	j	6f
+	.align 4
+.Lipl_schib:
+	.rept 13
+	.long 0
+	.endr
+.nodump:
+	lhi	%r1,1			# mode 1 = esame (normal ipl)
+6:
+#else
+	lhi	%r1,1			# mode 1 = esame (normal ipl)
+#endif /* CONFIG_ZFCPDUMP */
 	mvi	__LC_AR_MODE_ID,1	# set esame flag
 	slr	%r0,%r0 		# set cpuid to zero
 	sigp	%r1,%r0,0x12		# switch to esame mode
@@ -149,6 +211,14 @@
 .L4malign:.quad 0xffffffffffc00000
 .Lscan2g:.quad	0x80000000 + 0x20000 - 8	# 2GB + 128K - 8
 .Lnop:	.long	0x07000700
+#ifdef CONFIG_ZFCPDUMP
+.Lcurrent_cpu:
+	.long 0x0
+.Llast_cpu:
+	.long 0x0000ffff
+.Lpref_arr_ptr:
+	.long zfcpdump_prefix_array
+#endif /* CONFIG_ZFCPDUMP */
 .Lparmaddr:
 	.quad	PARMAREA
 	.align	64
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c
index f731185..06833ac 100644
--- a/arch/s390/kernel/ipl.c
+++ b/arch/s390/kernel/ipl.c
@@ -29,36 +29,21 @@
 #define SCCB_LOADPARM (&s390_readinfo_sccb.loadparm)
 #define SCCB_FLAG (s390_readinfo_sccb.flags)
 
-enum ipl_type {
-	IPL_TYPE_NONE	 = 1,
-	IPL_TYPE_UNKNOWN = 2,
-	IPL_TYPE_CCW	 = 4,
-	IPL_TYPE_FCP	 = 8,
-	IPL_TYPE_NSS	 = 16,
-};
-
-#define IPL_NONE_STR	 "none"
-#define IPL_UNKNOWN_STR  "unknown"
-#define IPL_CCW_STR	 "ccw"
-#define IPL_FCP_STR	 "fcp"
-#define IPL_NSS_STR	 "nss"
-
-/*
- * Must be in data section since the bss section
- * is not cleared when these are accessed.
- */
-u16 ipl_devno __attribute__((__section__(".data"))) = 0;
-u32 ipl_flags __attribute__((__section__(".data"))) = 0;
+#define IPL_UNKNOWN_STR		"unknown"
+#define IPL_CCW_STR		"ccw"
+#define IPL_FCP_STR		"fcp"
+#define IPL_FCP_DUMP_STR	"fcp_dump"
+#define IPL_NSS_STR		"nss"
 
 static char *ipl_type_str(enum ipl_type type)
 {
 	switch (type) {
-	case IPL_TYPE_NONE:
-		return IPL_NONE_STR;
 	case IPL_TYPE_CCW:
 		return IPL_CCW_STR;
 	case IPL_TYPE_FCP:
 		return IPL_FCP_STR;
+	case IPL_TYPE_FCP_DUMP:
+		return IPL_FCP_DUMP_STR;
 	case IPL_TYPE_NSS:
 		return IPL_NSS_STR;
 	case IPL_TYPE_UNKNOWN:
@@ -67,15 +52,55 @@
 	}
 }
 
+enum dump_type {
+	DUMP_TYPE_NONE	= 1,
+	DUMP_TYPE_CCW	= 2,
+	DUMP_TYPE_FCP	= 4,
+};
+
+#define DUMP_NONE_STR	 "none"
+#define DUMP_CCW_STR	 "ccw"
+#define DUMP_FCP_STR	 "fcp"
+
+static char *dump_type_str(enum dump_type type)
+{
+	switch (type) {
+	case DUMP_TYPE_NONE:
+		return DUMP_NONE_STR;
+	case DUMP_TYPE_CCW:
+		return DUMP_CCW_STR;
+	case DUMP_TYPE_FCP:
+		return DUMP_FCP_STR;
+	default:
+		return NULL;
+	}
+}
+
+/*
+ * Must be in data section since the bss section
+ * is not cleared when these are accessed.
+ */
+static u16 ipl_devno __attribute__((__section__(".data"))) = 0;
+u32 ipl_flags __attribute__((__section__(".data"))) = 0;
+
 enum ipl_method {
-	IPL_METHOD_NONE,
-	IPL_METHOD_CCW_CIO,
-	IPL_METHOD_CCW_DIAG,
-	IPL_METHOD_CCW_VM,
-	IPL_METHOD_FCP_RO_DIAG,
-	IPL_METHOD_FCP_RW_DIAG,
-	IPL_METHOD_FCP_RO_VM,
-	IPL_METHOD_NSS,
+	REIPL_METHOD_CCW_CIO,
+	REIPL_METHOD_CCW_DIAG,
+	REIPL_METHOD_CCW_VM,
+	REIPL_METHOD_FCP_RO_DIAG,
+	REIPL_METHOD_FCP_RW_DIAG,
+	REIPL_METHOD_FCP_RO_VM,
+	REIPL_METHOD_FCP_DUMP,
+	REIPL_METHOD_NSS,
+	REIPL_METHOD_DEFAULT,
+};
+
+enum dump_method {
+	DUMP_METHOD_NONE,
+	DUMP_METHOD_CCW_CIO,
+	DUMP_METHOD_CCW_DIAG,
+	DUMP_METHOD_CCW_VM,
+	DUMP_METHOD_FCP_DIAG,
 };
 
 enum shutdown_action {
@@ -107,15 +132,15 @@
 static int reipl_capabilities = IPL_TYPE_UNKNOWN;
 
 static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN;
-static enum ipl_method reipl_method = IPL_METHOD_NONE;
+static enum ipl_method reipl_method = REIPL_METHOD_DEFAULT;
 static struct ipl_parameter_block *reipl_block_fcp;
 static struct ipl_parameter_block *reipl_block_ccw;
 
 static char reipl_nss_name[NSS_NAME_SIZE + 1];
 
-static int dump_capabilities = IPL_TYPE_NONE;
-static enum ipl_type dump_type = IPL_TYPE_NONE;
-static enum ipl_method dump_method = IPL_METHOD_NONE;
+static int dump_capabilities = DUMP_TYPE_NONE;
+static enum dump_type dump_type = DUMP_TYPE_NONE;
+static enum dump_method dump_method = DUMP_METHOD_NONE;
 static struct ipl_parameter_block *dump_block_fcp;
 static struct ipl_parameter_block *dump_block_ccw;
 
@@ -134,6 +159,7 @@
 		: "d" (subcode) : "cc", "memory");
 	return _rc;
 }
+EXPORT_SYMBOL_GPL(diag308);
 
 /* SYSFS */
 
@@ -197,7 +223,7 @@
  * ipl section
  */
 
-static enum ipl_type ipl_get_type(void)
+static __init enum ipl_type get_ipl_type(void)
 {
 	struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START;
 
@@ -211,12 +237,44 @@
 		return IPL_TYPE_UNKNOWN;
 	if (ipl->hdr.pbt != DIAG308_IPL_TYPE_FCP)
 		return IPL_TYPE_UNKNOWN;
+	if (ipl->ipl_info.fcp.opt == DIAG308_IPL_OPT_DUMP)
+		return IPL_TYPE_FCP_DUMP;
 	return IPL_TYPE_FCP;
 }
 
+void __init setup_ipl_info(void)
+{
+	ipl_info.type = get_ipl_type();
+	switch (ipl_info.type) {
+	case IPL_TYPE_CCW:
+		ipl_info.data.ccw.dev_id.devno = ipl_devno;
+		ipl_info.data.ccw.dev_id.ssid = 0;
+		break;
+	case IPL_TYPE_FCP:
+	case IPL_TYPE_FCP_DUMP:
+		ipl_info.data.fcp.dev_id.devno =
+			IPL_PARMBLOCK_START->ipl_info.fcp.devno;
+		ipl_info.data.fcp.dev_id.ssid = 0;
+		ipl_info.data.fcp.wwpn = IPL_PARMBLOCK_START->ipl_info.fcp.wwpn;
+		ipl_info.data.fcp.lun = IPL_PARMBLOCK_START->ipl_info.fcp.lun;
+		break;
+	case IPL_TYPE_NSS:
+		strncpy(ipl_info.data.nss.name, kernel_nss_name,
+			sizeof(ipl_info.data.nss.name));
+		break;
+	case IPL_TYPE_UNKNOWN:
+	default:
+		/* We have no info to copy */
+		break;
+	}
+}
+
+struct ipl_info ipl_info;
+EXPORT_SYMBOL_GPL(ipl_info);
+
 static ssize_t ipl_type_show(struct subsystem *subsys, char *page)
 {
-	return sprintf(page, "%s\n", ipl_type_str(ipl_get_type()));
+	return sprintf(page, "%s\n", ipl_type_str(ipl_info.type));
 }
 
 static struct subsys_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type);
@@ -225,10 +283,11 @@
 {
 	struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START;
 
-	switch (ipl_get_type()) {
+	switch (ipl_info.type) {
 	case IPL_TYPE_CCW:
 		return sprintf(page, "0.0.%04x\n", ipl_devno);
 	case IPL_TYPE_FCP:
+	case IPL_TYPE_FCP_DUMP:
 		return sprintf(page, "0.0.%04x\n", ipl->ipl_info.fcp.devno);
 	default:
 		return 0;
@@ -485,23 +544,29 @@
 	switch(type) {
 	case IPL_TYPE_CCW:
 		if (MACHINE_IS_VM)
-			reipl_method = IPL_METHOD_CCW_VM;
+			reipl_method = REIPL_METHOD_CCW_VM;
 		else
-			reipl_method = IPL_METHOD_CCW_CIO;
+			reipl_method = REIPL_METHOD_CCW_CIO;
 		break;
 	case IPL_TYPE_FCP:
 		if (diag308_set_works)
-			reipl_method = IPL_METHOD_FCP_RW_DIAG;
+			reipl_method = REIPL_METHOD_FCP_RW_DIAG;
 		else if (MACHINE_IS_VM)
-			reipl_method = IPL_METHOD_FCP_RO_VM;
+			reipl_method = REIPL_METHOD_FCP_RO_VM;
 		else
-			reipl_method = IPL_METHOD_FCP_RO_DIAG;
+			reipl_method = REIPL_METHOD_FCP_RO_DIAG;
+		break;
+	case IPL_TYPE_FCP_DUMP:
+		reipl_method = REIPL_METHOD_FCP_DUMP;
 		break;
 	case IPL_TYPE_NSS:
-		reipl_method = IPL_METHOD_NSS;
+		reipl_method = REIPL_METHOD_NSS;
+		break;
+	case IPL_TYPE_UNKNOWN:
+		reipl_method = REIPL_METHOD_DEFAULT;
 		break;
 	default:
-		reipl_method = IPL_METHOD_NONE;
+		BUG();
 	}
 	reipl_type = type;
 	return 0;
@@ -579,22 +644,22 @@
 
 /* dump type */
 
-static int dump_set_type(enum ipl_type type)
+static int dump_set_type(enum dump_type type)
 {
 	if (!(dump_capabilities & type))
 		return -EINVAL;
 	switch(type) {
-	case IPL_TYPE_CCW:
+	case DUMP_TYPE_CCW:
 		if (MACHINE_IS_VM)
-			dump_method = IPL_METHOD_CCW_VM;
+			dump_method = DUMP_METHOD_CCW_VM;
 		else
-			dump_method = IPL_METHOD_CCW_CIO;
+			dump_method = DUMP_METHOD_CCW_CIO;
 		break;
-	case IPL_TYPE_FCP:
-		dump_method = IPL_METHOD_FCP_RW_DIAG;
+	case DUMP_TYPE_FCP:
+		dump_method = DUMP_METHOD_FCP_DIAG;
 		break;
 	default:
-		dump_method = IPL_METHOD_NONE;
+		dump_method = DUMP_METHOD_NONE;
 	}
 	dump_type = type;
 	return 0;
@@ -602,7 +667,7 @@
 
 static ssize_t dump_type_show(struct subsystem *subsys, char *page)
 {
-	return sprintf(page, "%s\n", ipl_type_str(dump_type));
+	return sprintf(page, "%s\n", dump_type_str(dump_type));
 }
 
 static ssize_t dump_type_store(struct subsystem *subsys, const char *buf,
@@ -610,12 +675,12 @@
 {
 	int rc = -EINVAL;
 
-	if (strncmp(buf, IPL_NONE_STR, strlen(IPL_NONE_STR)) == 0)
-		rc = dump_set_type(IPL_TYPE_NONE);
-	else if (strncmp(buf, IPL_CCW_STR, strlen(IPL_CCW_STR)) == 0)
-		rc = dump_set_type(IPL_TYPE_CCW);
-	else if (strncmp(buf, IPL_FCP_STR, strlen(IPL_FCP_STR)) == 0)
-		rc = dump_set_type(IPL_TYPE_FCP);
+	if (strncmp(buf, DUMP_NONE_STR, strlen(DUMP_NONE_STR)) == 0)
+		rc = dump_set_type(DUMP_TYPE_NONE);
+	else if (strncmp(buf, DUMP_CCW_STR, strlen(DUMP_CCW_STR)) == 0)
+		rc = dump_set_type(DUMP_TYPE_CCW);
+	else if (strncmp(buf, DUMP_FCP_STR, strlen(DUMP_FCP_STR)) == 0)
+		rc = dump_set_type(DUMP_TYPE_FCP);
 	return (rc != 0) ? rc : len;
 }
 
@@ -664,14 +729,14 @@
 	char loadparm[LOADPARM_LEN + 1];
 
 	switch (reipl_method) {
-	case IPL_METHOD_CCW_CIO:
+	case REIPL_METHOD_CCW_CIO:
 		devid.devno = reipl_block_ccw->ipl_info.ccw.devno;
-		if (ipl_get_type() == IPL_TYPE_CCW && devid.devno == ipl_devno)
+		if (ipl_info.type == IPL_TYPE_CCW && devid.devno == ipl_devno)
 			diag308(DIAG308_IPL, NULL);
 		devid.ssid  = 0;
 		reipl_ccw_dev(&devid);
 		break;
-	case IPL_METHOD_CCW_VM:
+	case REIPL_METHOD_CCW_VM:
 		reipl_get_ascii_loadparm(loadparm);
 		if (strlen(loadparm) == 0)
 			sprintf(buf, "IPL %X",
@@ -681,30 +746,32 @@
 				reipl_block_ccw->ipl_info.ccw.devno, loadparm);
 		__cpcmd(buf, NULL, 0, NULL);
 		break;
-	case IPL_METHOD_CCW_DIAG:
+	case REIPL_METHOD_CCW_DIAG:
 		diag308(DIAG308_SET, reipl_block_ccw);
 		diag308(DIAG308_IPL, NULL);
 		break;
-	case IPL_METHOD_FCP_RW_DIAG:
+	case REIPL_METHOD_FCP_RW_DIAG:
 		diag308(DIAG308_SET, reipl_block_fcp);
 		diag308(DIAG308_IPL, NULL);
 		break;
-	case IPL_METHOD_FCP_RO_DIAG:
+	case REIPL_METHOD_FCP_RO_DIAG:
 		diag308(DIAG308_IPL, NULL);
 		break;
-	case IPL_METHOD_FCP_RO_VM:
+	case REIPL_METHOD_FCP_RO_VM:
 		__cpcmd("IPL", NULL, 0, NULL);
 		break;
-	case IPL_METHOD_NSS:
+	case REIPL_METHOD_NSS:
 		sprintf(buf, "IPL %s", reipl_nss_name);
 		__cpcmd(buf, NULL, 0, NULL);
 		break;
-	case IPL_METHOD_NONE:
-	default:
+	case REIPL_METHOD_DEFAULT:
 		if (MACHINE_IS_VM)
 			__cpcmd("IPL", NULL, 0, NULL);
 		diag308(DIAG308_IPL, NULL);
 		break;
+	case REIPL_METHOD_FCP_DUMP:
+	default:
+		break;
 	}
 	signal_processor(smp_processor_id(), sigp_stop_and_store_status);
 }
@@ -715,28 +782,28 @@
 	static char buf[100];
 
 	switch (dump_method) {
-	case IPL_METHOD_CCW_CIO:
+	case DUMP_METHOD_CCW_CIO:
 		smp_send_stop();
 		devid.devno = dump_block_ccw->ipl_info.ccw.devno;
 		devid.ssid  = 0;
 		reipl_ccw_dev(&devid);
 		break;
-	case IPL_METHOD_CCW_VM:
+	case DUMP_METHOD_CCW_VM:
 		smp_send_stop();
 		sprintf(buf, "STORE STATUS");
 		__cpcmd(buf, NULL, 0, NULL);
 		sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno);
 		__cpcmd(buf, NULL, 0, NULL);
 		break;
-	case IPL_METHOD_CCW_DIAG:
+	case DUMP_METHOD_CCW_DIAG:
 		diag308(DIAG308_SET, dump_block_ccw);
 		diag308(DIAG308_DUMP, NULL);
 		break;
-	case IPL_METHOD_FCP_RW_DIAG:
+	case DUMP_METHOD_FCP_DIAG:
 		diag308(DIAG308_SET, dump_block_fcp);
 		diag308(DIAG308_DUMP, NULL);
 		break;
-	case IPL_METHOD_NONE:
+	case DUMP_METHOD_NONE:
 	default:
 		return;
 	}
@@ -777,12 +844,13 @@
 	rc = firmware_register(&ipl_subsys);
 	if (rc)
 		return rc;
-	switch (ipl_get_type()) {
+	switch (ipl_info.type) {
 	case IPL_TYPE_CCW:
 		rc = sysfs_create_group(&ipl_subsys.kset.kobj,
 					&ipl_ccw_attr_group);
 		break;
 	case IPL_TYPE_FCP:
+	case IPL_TYPE_FCP_DUMP:
 		rc = ipl_register_fcp_files();
 		break;
 	case IPL_TYPE_NSS:
@@ -852,7 +920,7 @@
 	/* FIXME: check for diag308_set_works when enabling diag ccw reipl */
 	if (!MACHINE_IS_VM)
 		sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO;
-	if (ipl_get_type() == IPL_TYPE_CCW)
+	if (ipl_info.type == IPL_TYPE_CCW)
 		reipl_block_ccw->ipl_info.ccw.devno = ipl_devno;
 	reipl_capabilities |= IPL_TYPE_CCW;
 	return 0;
@@ -862,9 +930,9 @@
 {
 	int rc;
 
-	if ((!diag308_set_works) && (ipl_get_type() != IPL_TYPE_FCP))
+	if ((!diag308_set_works) && (ipl_info.type != IPL_TYPE_FCP))
 		return 0;
-	if ((!diag308_set_works) && (ipl_get_type() == IPL_TYPE_FCP))
+	if ((!diag308_set_works) && (ipl_info.type == IPL_TYPE_FCP))
 		make_attrs_ro(reipl_fcp_attrs);
 
 	reipl_block_fcp = (void *) get_zeroed_page(GFP_KERNEL);
@@ -875,7 +943,7 @@
 		free_page((unsigned long)reipl_block_fcp);
 		return rc;
 	}
-	if (ipl_get_type() == IPL_TYPE_FCP) {
+	if (ipl_info.type == IPL_TYPE_FCP) {
 		memcpy(reipl_block_fcp, IPL_PARMBLOCK_START, PAGE_SIZE);
 	} else {
 		reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN;
@@ -909,7 +977,7 @@
 	rc = reipl_nss_init();
 	if (rc)
 		return rc;
-	rc = reipl_set_type(ipl_get_type());
+	rc = reipl_set_type(ipl_info.type);
 	if (rc)
 		return rc;
 	return 0;
@@ -931,7 +999,7 @@
 	dump_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION;
 	dump_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN;
 	dump_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW;
-	dump_capabilities |= IPL_TYPE_CCW;
+	dump_capabilities |= DUMP_TYPE_CCW;
 	return 0;
 }
 
@@ -956,7 +1024,7 @@
 	dump_block_fcp->hdr.blk0_len = IPL_PARM_BLK0_FCP_LEN;
 	dump_block_fcp->hdr.pbt = DIAG308_IPL_TYPE_FCP;
 	dump_block_fcp->ipl_info.fcp.opt = DIAG308_IPL_OPT_DUMP;
-	dump_capabilities |= IPL_TYPE_FCP;
+	dump_capabilities |= DUMP_TYPE_FCP;
 	return 0;
 }
 
@@ -995,7 +1063,7 @@
 	rc = dump_fcp_init();
 	if (rc)
 		return rc;
-	dump_set_type(IPL_TYPE_NONE);
+	dump_set_type(DUMP_TYPE_NONE);
 	return 0;
 }
 
@@ -1038,6 +1106,27 @@
 
 __initcall(s390_ipl_init);
 
+void __init ipl_save_parameters(void)
+{
+	struct cio_iplinfo iplinfo;
+	unsigned int *ipl_ptr;
+	void *src, *dst;
+
+	if (cio_get_iplinfo(&iplinfo))
+		return;
+
+	ipl_devno = iplinfo.devno;
+	ipl_flags |= IPL_DEVNO_VALID;
+	if (!iplinfo.is_qdio)
+		return;
+	ipl_flags |= IPL_PARMBLOCK_VALID;
+	ipl_ptr = (unsigned int *)__LC_IPL_PARMBLOCK_PTR;
+	src = (void *)(unsigned long)*ipl_ptr;
+	dst = (void *)IPL_PARMBLOCK_ORIGIN;
+	memmove(dst, src, PAGE_SIZE);
+	*ipl_ptr = IPL_PARMBLOCK_ORIGIN;
+}
+
 static LIST_HEAD(rcall);
 static DEFINE_MUTEX(rcall_mutex);
 
diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c
index 993f353..23c61f6 100644
--- a/arch/s390/kernel/kprobes.c
+++ b/arch/s390/kernel/kprobes.c
@@ -516,7 +516,7 @@
 	return 1;
 }
 
-static int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
+int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
 {
 	struct kprobe *cur = kprobe_running();
 	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
@@ -603,7 +603,6 @@
 			ret = NOTIFY_STOP;
 		break;
 	case DIE_TRAP:
-	case DIE_PAGE_FAULT:
 		/* kprobe_running() needs smp_processor_id() */
 		preempt_disable();
 		if (kprobe_running() &&
diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c
index 39d1dd7..59b4e79 100644
--- a/arch/s390/kernel/module.c
+++ b/arch/s390/kernel/module.c
@@ -31,6 +31,7 @@
 #include <linux/string.h>
 #include <linux/kernel.h>
 #include <linux/moduleloader.h>
+#include <linux/bug.h>
 
 #if 0
 #define DEBUGP printk
@@ -398,9 +399,10 @@
 		    struct module *me)
 {
 	vfree(me->arch.syminfo);
-	return 0;
+	return module_bug_finalize(hdr, sechdrs, me);
 }
 
 void module_arch_cleanup(struct module *mod)
 {
+	module_bug_cleanup(mod);
 }
diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c
index 5acfac6..11d9b01 100644
--- a/arch/s390/kernel/process.c
+++ b/arch/s390/kernel/process.c
@@ -280,24 +280,26 @@
         return 0;
 }
 
-asmlinkage long sys_fork(struct pt_regs regs)
+asmlinkage long sys_fork(void)
 {
-	return do_fork(SIGCHLD, regs.gprs[15], &regs, 0, NULL, NULL);
+	struct pt_regs *regs = task_pt_regs(current);
+	return do_fork(SIGCHLD, regs->gprs[15], regs, 0, NULL, NULL);
 }
 
-asmlinkage long sys_clone(struct pt_regs regs)
+asmlinkage long sys_clone(void)
 {
-        unsigned long clone_flags;
-        unsigned long newsp;
+	struct pt_regs *regs = task_pt_regs(current);
+	unsigned long clone_flags;
+	unsigned long newsp;
 	int __user *parent_tidptr, *child_tidptr;
 
-        clone_flags = regs.gprs[3];
-        newsp = regs.orig_gpr2;
-	parent_tidptr = (int __user *) regs.gprs[4];
-	child_tidptr = (int __user *) regs.gprs[5];
-        if (!newsp)
-                newsp = regs.gprs[15];
-        return do_fork(clone_flags, newsp, &regs, 0,
+	clone_flags = regs->gprs[3];
+	newsp = regs->orig_gpr2;
+	parent_tidptr = (int __user *) regs->gprs[4];
+	child_tidptr = (int __user *) regs->gprs[5];
+	if (!newsp)
+		newsp = regs->gprs[15];
+	return do_fork(clone_flags, newsp, regs, 0,
 		       parent_tidptr, child_tidptr);
 }
 
@@ -311,40 +313,52 @@
  * do not have enough call-clobbered registers to hold all
  * the information you need.
  */
-asmlinkage long sys_vfork(struct pt_regs regs)
+asmlinkage long sys_vfork(void)
 {
+	struct pt_regs *regs = task_pt_regs(current);
 	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD,
-		       regs.gprs[15], &regs, 0, NULL, NULL);
+		       regs->gprs[15], regs, 0, NULL, NULL);
+}
+
+asmlinkage void execve_tail(void)
+{
+	task_lock(current);
+	current->ptrace &= ~PT_DTRACE;
+	task_unlock(current);
+	current->thread.fp_regs.fpc = 0;
+	if (MACHINE_HAS_IEEE)
+		asm volatile("sfpc %0,%0" : : "d" (0));
 }
 
 /*
  * sys_execve() executes a new program.
  */
-asmlinkage long sys_execve(struct pt_regs regs)
+asmlinkage long sys_execve(void)
 {
-        int error;
-        char * filename;
+	struct pt_regs *regs = task_pt_regs(current);
+	char *filename;
+	unsigned long result;
+	int rc;
 
-        filename = getname((char __user *) regs.orig_gpr2);
-        error = PTR_ERR(filename);
-        if (IS_ERR(filename))
-                goto out;
-        error = do_execve(filename, (char __user * __user *) regs.gprs[3],
-			  (char __user * __user *) regs.gprs[4], &regs);
-	if (error == 0) {
-		task_lock(current);
-		current->ptrace &= ~PT_DTRACE;
-		task_unlock(current);
-		current->thread.fp_regs.fpc = 0;
-		if (MACHINE_HAS_IEEE)
-			asm volatile("sfpc %0,%0" : : "d" (0));
+	filename = getname((char __user *) regs->orig_gpr2);
+	if (IS_ERR(filename)) {
+		result = PTR_ERR(filename);
+		goto out;
 	}
-        putname(filename);
+	rc = do_execve(filename, (char __user * __user *) regs->gprs[3],
+		       (char __user * __user *) regs->gprs[4], regs);
+	if (rc) {
+		result = rc;
+		goto out_putname;
+	}
+	execve_tail();
+	result = regs->gprs[2];
+out_putname:
+	putname(filename);
 out:
-        return error;
+	return result;
 }
 
-
 /*
  * fill in the FPU structure for a core dump.
  */
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index 863c8d0..6bfb088 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -65,7 +65,7 @@
  * User copy operations.
  */
 struct uaccess_ops uaccess;
-EXPORT_SYMBOL_GPL(uaccess);
+EXPORT_SYMBOL(uaccess);
 
 /*
  * Machine setup..
@@ -74,6 +74,8 @@
 unsigned int console_devno = -1;
 unsigned int console_irq = -1;
 unsigned long machine_flags = 0;
+unsigned long elf_hwcap = 0;
+char elf_platform[ELF_PLATFORM_SIZE];
 
 struct mem_chunk __initdata memory_chunk[MEMORY_CHUNKS];
 volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */
@@ -285,6 +287,26 @@
 	}
 }
 
+#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
+static void __init setup_zfcpdump(unsigned int console_devno)
+{
+	static char str[64];
+
+	if (ipl_info.type != IPL_TYPE_FCP_DUMP)
+		return;
+	if (console_devno != -1)
+		sprintf(str, "cio_ignore=all,!0.0.%04x,!0.0.%04x",
+			ipl_info.data.fcp.dev_id.devno, console_devno);
+	else
+		sprintf(str, "cio_ignore=all,!0.0.%04x",
+			ipl_info.data.fcp.dev_id.devno);
+	strcat(COMMAND_LINE, str);
+	console_loglevel = 2;
+}
+#else
+static inline void setup_zfcpdump(unsigned int console_devno) {}
+#endif /* CONFIG_ZFCPDUMP */
+
 #ifdef CONFIG_SMP
 void (*_machine_restart)(char *command) = machine_restart_smp;
 void (*_machine_halt)(void) = machine_halt_smp;
@@ -586,13 +608,20 @@
 	}
 }
 
+unsigned long real_memory_size;
+EXPORT_SYMBOL_GPL(real_memory_size);
+
 static void __init setup_memory_end(void)
 {
-	unsigned long real_size, memory_size;
+	unsigned long memory_size;
 	unsigned long max_mem, max_phys;
 	int i;
 
-	memory_size = real_size = 0;
+#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
+	if (ipl_info.type == IPL_TYPE_FCP_DUMP)
+		memory_end = ZFCPDUMP_HSA_SIZE;
+#endif
+	memory_size = 0;
 	max_phys = VMALLOC_END_INIT - VMALLOC_MIN_SIZE;
 	memory_end &= PAGE_MASK;
 
@@ -601,7 +630,8 @@
 	for (i = 0; i < MEMORY_CHUNKS; i++) {
 		struct mem_chunk *chunk = &memory_chunk[i];
 
-		real_size = max(real_size, chunk->addr + chunk->size);
+		real_memory_size = max(real_memory_size,
+				       chunk->addr + chunk->size);
 		if (chunk->addr >= max_mem) {
 			memset(chunk, 0, sizeof(*chunk));
 			continue;
@@ -721,6 +751,98 @@
 #endif
 }
 
+static __init unsigned int stfl(void)
+{
+	asm volatile(
+		"	.insn	s,0xb2b10000,0(0)\n" /* stfl */
+		"0:\n"
+		EX_TABLE(0b,0b));
+	return S390_lowcore.stfl_fac_list;
+}
+
+static __init int stfle(unsigned long long *list, int doublewords)
+{
+	typedef struct { unsigned long long _[doublewords]; } addrtype;
+	register unsigned long __nr asm("0") = doublewords - 1;
+
+	asm volatile(".insn s,0xb2b00000,%0" /* stfle */
+		     : "=m" (*(addrtype *) list), "+d" (__nr) : : "cc");
+	return __nr + 1;
+}
+
+/*
+ * Setup hardware capabilities.
+ */
+static void __init setup_hwcaps(void)
+{
+	static const int stfl_bits[6] = { 0, 2, 7, 17, 19, 21 };
+	struct cpuinfo_S390 *cpuinfo = &S390_lowcore.cpu_data;
+	unsigned long long facility_list_extended;
+	unsigned int facility_list;
+	int i;
+
+	facility_list = stfl();
+	/*
+	 * The store facility list bits numbers as found in the principles
+	 * of operation are numbered with bit 1UL<<31 as number 0 to
+	 * bit 1UL<<0 as number 31.
+	 *   Bit 0: instructions named N3, "backported" to esa-mode
+	 *   Bit 2: z/Architecture mode is active
+	 *   Bit 7: the store-facility-list-extended facility is installed
+	 *   Bit 17: the message-security assist is installed
+	 *   Bit 19: the long-displacement facility is installed
+	 *   Bit 21: the extended-immediate facility is installed
+	 * These get translated to:
+	 *   HWCAP_S390_ESAN3 bit 0, HWCAP_S390_ZARCH bit 1,
+	 *   HWCAP_S390_STFLE bit 2, HWCAP_S390_MSA bit 3,
+	 *   HWCAP_S390_LDISP bit 4, and HWCAP_S390_EIMM bit 5.
+	 */
+	for (i = 0; i < 6; i++)
+		if (facility_list & (1UL << (31 - stfl_bits[i])))
+			elf_hwcap |= 1UL << i;
+
+	/*
+	 * Check for additional facilities with store-facility-list-extended.
+	 * stfle stores doublewords (8 byte) with bit 1ULL<<63 as bit 0
+	 * and 1ULL<<0 as bit 63. Bits 0-31 contain the same information
+	 * as stored by stfl, bits 32-xxx contain additional facilities.
+	 * How many facility words are stored depends on the number of
+	 * doublewords passed to the instruction. The additional facilites
+	 * are:
+	 *   Bit 43: decimal floating point facility is installed
+	 * translated to:
+	 *   HWCAP_S390_DFP bit 6.
+	 */
+	if ((elf_hwcap & (1UL << 2)) &&
+	    stfle(&facility_list_extended, 1) > 0) {
+		if (facility_list_extended & (1ULL << (64 - 43)))
+			elf_hwcap |= 1UL << 6;
+	}
+
+	switch (cpuinfo->cpu_id.machine) {
+	case 0x9672:
+#if !defined(CONFIG_64BIT)
+	default:	/* Use "g5" as default for 31 bit kernels. */
+#endif
+		strcpy(elf_platform, "g5");
+		break;
+	case 0x2064:
+	case 0x2066:
+#if defined(CONFIG_64BIT)
+	default:	/* Use "z900" as default for 64 bit kernels. */
+#endif
+		strcpy(elf_platform, "z900");
+		break;
+	case 0x2084:
+	case 0x2086:
+		strcpy(elf_platform, "z990");
+		break;
+	case 0x2094:
+		strcpy(elf_platform, "z9-109");
+		break;
+	}
+}
+
 /*
  * Setup function called from init/main.c just after the banner
  * was printed.
@@ -765,6 +887,7 @@
 
 	parse_early_param();
 
+	setup_ipl_info();
 	setup_memory_end();
 	setup_addressing_mode();
 	setup_memory();
@@ -776,12 +899,20 @@
 	smp_setup_cpu_possible_map();
 
 	/*
+	 * Setup capabilities (ELF_HWCAP & ELF_PLATFORM).
+	 */
+	setup_hwcaps();
+
+	/*
 	 * Create kernel page tables and switch to virtual addressing.
 	 */
         paging_init();
 
         /* Setup default console */
 	conmode_default();
+
+	/* Setup zfcpdump support */
+	setup_zfcpdump(console_devno);
 }
 
 void print_cpu_info(struct cpuinfo_S390 *cpuinfo)
@@ -807,8 +938,12 @@
 
 static int show_cpuinfo(struct seq_file *m, void *v)
 {
+	static const char *hwcap_str[7] = {
+		"esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp"
+	};
         struct cpuinfo_S390 *cpuinfo;
 	unsigned long n = (unsigned long) v - 1;
+	int i;
 
 	s390_adjust_jiffies();
 	preempt_disable();
@@ -818,7 +953,13 @@
 			       "bogomips per cpu: %lu.%02lu\n",
 			       num_online_cpus(), loops_per_jiffy/(500000/HZ),
 			       (loops_per_jiffy/(5000/HZ))%100);
+		seq_puts(m, "features\t: ");
+		for (i = 0; i < 7; i++)
+			if (hwcap_str[i] && (elf_hwcap & (1UL << i)))
+				seq_printf(m, "%s ", hwcap_str[i]);
+		seq_puts(m, "\n");
 	}
+
 	if (cpu_online(n)) {
 #ifdef CONFIG_SMP
 		if (smp_processor_id() == n)
diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c
index 554f9cf..3c41907 100644
--- a/arch/s390/kernel/signal.c
+++ b/arch/s390/kernel/signal.c
@@ -102,9 +102,9 @@
 }
 
 asmlinkage long
-sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss,
-					struct pt_regs *regs)
+sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss)
 {
+	struct pt_regs *regs = task_pt_regs(current);
 	return do_sigaltstack(uss, uoss, regs->gprs[15]);
 }
 
@@ -163,8 +163,9 @@
 	return 0;
 }
 
-asmlinkage long sys_sigreturn(struct pt_regs *regs)
+asmlinkage long sys_sigreturn(void)
 {
+	struct pt_regs *regs = task_pt_regs(current);
 	sigframe __user *frame = (sigframe __user *)regs->gprs[15];
 	sigset_t set;
 
@@ -189,8 +190,9 @@
 	return 0;
 }
 
-asmlinkage long sys_rt_sigreturn(struct pt_regs *regs)
+asmlinkage long sys_rt_sigreturn(void)
 {
+	struct pt_regs *regs = task_pt_regs(current);
 	rt_sigframe __user *frame = (rt_sigframe __user *)regs->gprs[15];
 	sigset_t set;
 
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index 97764f7..3754e20 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -1,12 +1,12 @@
 /*
  *  arch/s390/kernel/smp.c
  *
- *    Copyright (C) IBM Corp. 1999,2006
+ *    Copyright IBM Corp. 1999,2007
  *    Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
- *               Martin Schwidefsky (schwidefsky@de.ibm.com)
- *               Heiko Carstens (heiko.carstens@de.ibm.com)
+ *		 Martin Schwidefsky (schwidefsky@de.ibm.com)
+ *		 Heiko Carstens (heiko.carstens@de.ibm.com)
  *
- *  based on other smp stuff by 
+ *  based on other smp stuff by
  *    (c) 1995 Alan Cox, CymruNET Ltd  <alan@cymru.net>
  *    (c) 1998 Ingo Molnar
  *
@@ -31,6 +31,7 @@
 #include <linux/interrupt.h>
 #include <linux/cpu.h>
 #include <linux/timex.h>
+#include <linux/bootmem.h>
 #include <asm/ipl.h>
 #include <asm/setup.h>
 #include <asm/sigp.h>
@@ -40,17 +41,19 @@
 #include <asm/cpcmd.h>
 #include <asm/tlbflush.h>
 #include <asm/timer.h>
-
-extern volatile int __cpu_logical_map[];
+#include <asm/lowcore.h>
 
 /*
  * An array with a pointer the lowcore of every CPU.
  */
-
 struct _lowcore *lowcore_ptr[NR_CPUS];
+EXPORT_SYMBOL(lowcore_ptr);
 
 cpumask_t cpu_online_map = CPU_MASK_NONE;
+EXPORT_SYMBOL(cpu_online_map);
+
 cpumask_t cpu_possible_map = CPU_MASK_NONE;
+EXPORT_SYMBOL(cpu_possible_map);
 
 static struct task_struct *current_set[NR_CPUS];
 
@@ -70,7 +73,7 @@
 	int wait;
 };
 
-static struct call_data_struct * call_data;
+static struct call_data_struct *call_data;
 
 /*
  * 'Call function' interrupt callback
@@ -150,8 +153,8 @@
  *
  * Run a function on all other CPUs.
  *
- * You must not call this function with disabled interrupts or from a
- * hardware interrupt handler. You may call it from a bottom half.
+ * You must not call this function with disabled interrupts, from a
+ * hardware interrupt handler or from a bottom half.
  */
 int smp_call_function(void (*func) (void *info), void *info, int nonatomic,
 		      int wait)
@@ -177,11 +180,11 @@
  *
  * Run a function on one processor.
  *
- * You must not call this function with disabled interrupts or from a
- * hardware interrupt handler. You may call it from a bottom half.
+ * You must not call this function with disabled interrupts, from a
+ * hardware interrupt handler or from a bottom half.
  */
 int smp_call_function_on(void (*func) (void *info), void *info, int nonatomic,
-			  int wait, int cpu)
+			 int wait, int cpu)
 {
 	cpumask_t map = CPU_MASK_NONE;
 
@@ -195,9 +198,9 @@
 
 static void do_send_stop(void)
 {
-        int cpu, rc;
+	int cpu, rc;
 
-        /* stop all processors */
+	/* stop all processors */
 	for_each_online_cpu(cpu) {
 		if (cpu == smp_processor_id())
 			continue;
@@ -209,9 +212,9 @@
 
 static void do_store_status(void)
 {
-        int cpu, rc;
+	int cpu, rc;
 
-        /* store status of all processors in their lowcores (real 0) */
+	/* store status of all processors in their lowcores (real 0) */
 	for_each_online_cpu(cpu) {
 		if (cpu == smp_processor_id())
 			continue;
@@ -219,8 +222,8 @@
 			rc = signal_processor_p(
 				(__u32)(unsigned long) lowcore_ptr[cpu], cpu,
 				sigp_store_status_at_address);
-		} while(rc == sigp_busy);
-        }
+		} while (rc == sigp_busy);
+	}
 }
 
 static void do_wait_for_stop(void)
@@ -231,7 +234,7 @@
 	for_each_online_cpu(cpu) {
 		if (cpu == smp_processor_id())
 			continue;
-		while(!smp_cpu_not_running(cpu))
+		while (!smp_cpu_not_running(cpu))
 			cpu_relax();
 	}
 }
@@ -245,7 +248,7 @@
 	/* Disable all interrupts/machine checks */
 	__load_psw_mask(psw_kernel_bits & ~PSW_MASK_MCHECK);
 
-        /* write magic number to zero page (absolute 0) */
+	/* write magic number to zero page (absolute 0) */
 	lowcore_ptr[smp_processor_id()]->panic_magic = __PANIC_MAGIC;
 
 	/* stop other processors. */
@@ -261,8 +264,7 @@
 /*
  * Reboot, halt and power_off routines for SMP.
  */
-
-void machine_restart_smp(char * __unused) 
+void machine_restart_smp(char *__unused)
 {
 	smp_send_stop();
 	do_reipl();
@@ -293,17 +295,17 @@
 
 static void do_ext_call_interrupt(__u16 code)
 {
-        unsigned long bits;
+	unsigned long bits;
 
-        /*
-         * handle bit signal external calls
-         *
-         * For the ec_schedule signal we have to do nothing. All the work
-         * is done automatically when we return from the interrupt.
-         */
+	/*
+	 * handle bit signal external calls
+	 *
+	 * For the ec_schedule signal we have to do nothing. All the work
+	 * is done automatically when we return from the interrupt.
+	 */
 	bits = xchg(&S390_lowcore.ext_call_fast, 0);
 
-	if (test_bit(ec_call_function, &bits)) 
+	if (test_bit(ec_call_function, &bits))
 		do_call_function();
 }
 
@@ -313,11 +315,11 @@
  */
 static void smp_ext_bitcall(int cpu, ec_bit_sig sig)
 {
-        /*
-         * Set signaling bit in lowcore of target cpu and kick it
-         */
+	/*
+	 * Set signaling bit in lowcore of target cpu and kick it
+	 */
 	set_bit(sig, (unsigned long *) &lowcore_ptr[cpu]->ext_call_fast);
-	while(signal_processor(cpu, sigp_emergency_signal) == sigp_busy)
+	while (signal_processor(cpu, sigp_emergency_signal) == sigp_busy)
 		udelay(10);
 }
 
@@ -332,7 +334,7 @@
 
 void smp_ptlb_all(void)
 {
-        on_each_cpu(smp_ptlb_callback, NULL, 0, 1);
+	on_each_cpu(smp_ptlb_callback, NULL, 0, 1);
 }
 EXPORT_SYMBOL(smp_ptlb_all);
 #endif /* ! CONFIG_64BIT */
@@ -344,7 +346,7 @@
  */
 void smp_send_reschedule(int cpu)
 {
-        smp_ext_bitcall(cpu, ec_schedule);
+	smp_ext_bitcall(cpu, ec_schedule);
 }
 
 /*
@@ -358,11 +360,12 @@
 /*
  * callback for setting/clearing control bits
  */
-static void smp_ctl_bit_callback(void *info) {
+static void smp_ctl_bit_callback(void *info)
+{
 	struct ec_creg_mask_parms *pp = info;
 	unsigned long cregs[16];
 	int i;
-	
+
 	__ctl_store(cregs, 0, 15);
 	for (i = 0; i <= 15; i++)
 		cregs[i] = (cregs[i] & pp->andvals[i]) | pp->orvals[i];
@@ -381,6 +384,7 @@
 	parms.orvals[cr] = 1 << bit;
 	on_each_cpu(smp_ctl_bit_callback, &parms, 0, 1);
 }
+EXPORT_SYMBOL(smp_ctl_set_bit);
 
 /*
  * Clear a bit in a control register of all cpus
@@ -394,13 +398,72 @@
 	parms.andvals[cr] = ~(1L << bit);
 	on_each_cpu(smp_ctl_bit_callback, &parms, 0, 1);
 }
+EXPORT_SYMBOL(smp_ctl_clear_bit);
+
+#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
+
+/*
+ * zfcpdump_prefix_array holds prefix registers for the following scenario:
+ * 64 bit zfcpdump kernel and 31 bit kernel which is to be dumped. We have to
+ * save its prefix registers, since they get lost, when switching from 31 bit
+ * to 64 bit.
+ */
+unsigned int zfcpdump_prefix_array[NR_CPUS + 1] \
+	__attribute__((__section__(".data")));
+
+static void __init smp_get_save_areas(void)
+{
+	unsigned int cpu, cpu_num, rc;
+	__u16 boot_cpu_addr;
+
+	if (ipl_info.type != IPL_TYPE_FCP_DUMP)
+		return;
+	boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr;
+	cpu_num = 1;
+	for (cpu = 0; cpu <= 65535; cpu++) {
+		if ((u16) cpu == boot_cpu_addr)
+			continue;
+		__cpu_logical_map[1] = (__u16) cpu;
+		if (signal_processor(1, sigp_sense) == sigp_not_operational)
+			continue;
+		if (cpu_num >= NR_CPUS) {
+			printk("WARNING: Registers for cpu %i are not "
+			       "saved, since dump kernel was compiled with"
+			       "NR_CPUS=%i!\n", cpu_num, NR_CPUS);
+			continue;
+		}
+		zfcpdump_save_areas[cpu_num] =
+			alloc_bootmem(sizeof(union save_area));
+		while (1) {
+			rc = signal_processor(1, sigp_stop_and_store_status);
+			if (rc != sigp_busy)
+				break;
+			cpu_relax();
+		}
+		memcpy(zfcpdump_save_areas[cpu_num],
+		       (void *)(unsigned long) store_prefix() +
+		       SAVE_AREA_BASE, SAVE_AREA_SIZE);
+#ifdef __s390x__
+		/* copy original prefix register */
+		zfcpdump_save_areas[cpu_num]->s390x.pref_reg =
+			zfcpdump_prefix_array[cpu_num];
+#endif
+		cpu_num++;
+	}
+}
+
+union save_area *zfcpdump_save_areas[NR_CPUS + 1];
+EXPORT_SYMBOL_GPL(zfcpdump_save_areas);
+
+#else
+#define smp_get_save_areas() do { } while (0)
+#endif
 
 /*
  * Lets check how many CPUs we have.
  */
 
-static unsigned int
-__init smp_count_cpus(void)
+static unsigned int __init smp_count_cpus(void)
 {
 	unsigned int cpu, num_cpus;
 	__u16 boot_cpu_addr;
@@ -416,31 +479,30 @@
 		if ((__u16) cpu == boot_cpu_addr)
 			continue;
 		__cpu_logical_map[1] = (__u16) cpu;
-		if (signal_processor(1, sigp_sense) ==
-		    sigp_not_operational)
+		if (signal_processor(1, sigp_sense) == sigp_not_operational)
 			continue;
 		num_cpus++;
 	}
 
-	printk("Detected %d CPU's\n",(int) num_cpus);
+	printk("Detected %d CPU's\n", (int) num_cpus);
 	printk("Boot cpu address %2X\n", boot_cpu_addr);
 
 	return num_cpus;
 }
 
 /*
- *      Activate a secondary processor.
+ *	Activate a secondary processor.
  */
 int __devinit start_secondary(void *cpuvoid)
 {
-        /* Setup the cpu */
-        cpu_init();
+	/* Setup the cpu */
+	cpu_init();
 	preempt_disable();
 	/* Enable TOD clock interrupts on the secondary cpu. */
-        init_cpu_timer();
+	init_cpu_timer();
 #ifdef CONFIG_VIRT_TIMER
 	/* Enable cpu timer interrupts on the secondary cpu. */
-        init_cpu_vtimer();
+	init_cpu_vtimer();
 #endif
 	/* Enable pfault pseudo page faults on this cpu. */
 	pfault_init();
@@ -449,11 +511,11 @@
 	cpu_set(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 */
-        cpu_idle();
-        return 0;
+	/* Print info about this processor */
+	print_cpu_info(&S390_lowcore.cpu_data);
+	/* cpu_idle will call schedule for us */
+	cpu_idle();
+	return 0;
 }
 
 static void __init smp_create_idle(unsigned int cpu)
@@ -470,56 +532,13 @@
 	current_set[cpu] = p;
 }
 
-/* Reserving and releasing of CPUs */
-
-static DEFINE_SPINLOCK(smp_reserve_lock);
-static int smp_cpu_reserved[NR_CPUS];
-
-int
-smp_get_cpu(cpumask_t cpu_mask)
-{
-	unsigned long flags;
-	int cpu;
-
-	spin_lock_irqsave(&smp_reserve_lock, flags);
-	/* Try to find an already reserved cpu. */
-	for_each_cpu_mask(cpu, cpu_mask) {
-		if (smp_cpu_reserved[cpu] != 0) {
-			smp_cpu_reserved[cpu]++;
-			/* Found one. */
-			goto out;
-		}
-	}
-	/* Reserve a new cpu from cpu_mask. */
-	for_each_cpu_mask(cpu, cpu_mask) {
-		if (cpu_online(cpu)) {
-			smp_cpu_reserved[cpu]++;
-			goto out;
-		}
-	}
-	cpu = -ENODEV;
-out:
-	spin_unlock_irqrestore(&smp_reserve_lock, flags);
-	return cpu;
-}
-
-void
-smp_put_cpu(int cpu)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&smp_reserve_lock, flags);
-	smp_cpu_reserved[cpu]--;
-	spin_unlock_irqrestore(&smp_reserve_lock, flags);
-}
-
-static int
-cpu_stopped(int cpu)
+static int cpu_stopped(int cpu)
 {
 	__u32 status;
 
 	/* Check for stopped state */
-	if (signal_processor_ps(&status, 0, cpu, sigp_sense) == sigp_status_stored) {
+	if (signal_processor_ps(&status, 0, cpu, sigp_sense) ==
+	    sigp_status_stored) {
 		if (status & 0x40)
 			return 1;
 	}
@@ -528,14 +547,13 @@
 
 /* Upping and downing of CPUs */
 
-int
-__cpu_up(unsigned int cpu)
+int __cpu_up(unsigned int cpu)
 {
 	struct task_struct *idle;
-        struct _lowcore    *cpu_lowcore;
+	struct _lowcore *cpu_lowcore;
 	struct stack_frame *sf;
-        sigp_ccode          ccode;
-	int                 curr_cpu;
+	sigp_ccode ccode;
+	int curr_cpu;
 
 	for (curr_cpu = 0; curr_cpu <= 65535; curr_cpu++) {
 		__cpu_logical_map[cpu] = (__u16) curr_cpu;
@@ -548,7 +566,7 @@
 
 	ccode = signal_processor_p((__u32)(unsigned long)(lowcore_ptr[cpu]),
 				   cpu, sigp_set_prefix);
-	if (ccode){
+	if (ccode) {
 		printk("sigp_set_prefix failed for cpu %d "
 		       "with condition code %d\n",
 		       (int) cpu, (int) ccode);
@@ -556,9 +574,9 @@
 	}
 
 	idle = current_set[cpu];
-        cpu_lowcore = lowcore_ptr[cpu];
+	cpu_lowcore = lowcore_ptr[cpu];
 	cpu_lowcore->kernel_stack = (unsigned long)
-		task_stack_page(idle) + (THREAD_SIZE);
+		task_stack_page(idle) + THREAD_SIZE;
 	sf = (struct stack_frame *) (cpu_lowcore->kernel_stack
 				     - sizeof(struct pt_regs)
 				     - sizeof(struct stack_frame));
@@ -570,11 +588,11 @@
 		"	stam	0,15,0(%0)"
 		: : "a" (&cpu_lowcore->access_regs_save_area) : "memory");
 	cpu_lowcore->percpu_offset = __per_cpu_offset[cpu];
-        cpu_lowcore->current_task = (unsigned long) idle;
-        cpu_lowcore->cpu_data.cpu_nr = cpu;
+	cpu_lowcore->current_task = (unsigned long) idle;
+	cpu_lowcore->cpu_data.cpu_nr = cpu;
 	eieio();
 
-	while (signal_processor(cpu,sigp_restart) == sigp_busy)
+	while (signal_processor(cpu, sigp_restart) == sigp_busy)
 		udelay(10);
 
 	while (!cpu_online(cpu))
@@ -589,6 +607,7 @@
 {
 	unsigned int phy_cpus, pos_cpus, cpu;
 
+	smp_get_save_areas();
 	phy_cpus = smp_count_cpus();
 	pos_cpus = min(phy_cpus + additional_cpus, (unsigned int) NR_CPUS);
 
@@ -620,18 +639,11 @@
 }
 early_param("possible_cpus", setup_possible_cpus);
 
-int
-__cpu_disable(void)
+int __cpu_disable(void)
 {
-	unsigned long flags;
 	struct ec_creg_mask_parms cr_parms;
 	int cpu = smp_processor_id();
 
-	spin_lock_irqsave(&smp_reserve_lock, flags);
-	if (smp_cpu_reserved[cpu] != 0) {
-		spin_unlock_irqrestore(&smp_reserve_lock, flags);
-		return -EBUSY;
-	}
 	cpu_clear(cpu, cpu_online_map);
 
 	/* Disable pfault pseudo page faults on this cpu. */
@@ -642,24 +654,23 @@
 
 	/* disable all external interrupts */
 	cr_parms.orvals[0] = 0;
-	cr_parms.andvals[0] = ~(1<<15 | 1<<14 | 1<<13 | 1<<12 |
-				1<<11 | 1<<10 | 1<< 6 | 1<< 4);
+	cr_parms.andvals[0] = ~(1 << 15 | 1 << 14 | 1 << 13 | 1 << 12 |
+				1 << 11 | 1 << 10 | 1 <<  6 | 1 <<  4);
 	/* disable all I/O interrupts */
 	cr_parms.orvals[6] = 0;
-	cr_parms.andvals[6] = ~(1<<31 | 1<<30 | 1<<29 | 1<<28 |
-				1<<27 | 1<<26 | 1<<25 | 1<<24);
+	cr_parms.andvals[6] = ~(1 << 31 | 1 << 30 | 1 << 29 | 1 << 28 |
+				1 << 27 | 1 << 26 | 1 << 25 | 1 << 24);
 	/* disable most machine checks */
 	cr_parms.orvals[14] = 0;
-	cr_parms.andvals[14] = ~(1<<28 | 1<<27 | 1<<26 | 1<<25 | 1<<24);
+	cr_parms.andvals[14] = ~(1 << 28 | 1 << 27 | 1 << 26 |
+				 1 << 25 | 1 << 24);
 
 	smp_ctl_bit_callback(&cr_parms);
 
-	spin_unlock_irqrestore(&smp_reserve_lock, flags);
 	return 0;
 }
 
-void
-__cpu_die(unsigned int cpu)
+void __cpu_die(unsigned int cpu)
 {
 	/* Wait until target cpu is down */
 	while (!smp_cpu_not_running(cpu))
@@ -667,13 +678,12 @@
 	printk("Processor %d spun down\n", cpu);
 }
 
-void
-cpu_die(void)
+void cpu_die(void)
 {
 	idle_task_exit();
 	signal_processor(smp_processor_id(), sigp_stop);
 	BUG();
-	for(;;);
+	for (;;);
 }
 
 #endif /* CONFIG_HOTPLUG_CPU */
@@ -686,36 +696,36 @@
 {
 	unsigned long stack;
 	unsigned int cpu;
-        int i;
+	int i;
 
-        /* request the 0x1201 emergency signal external interrupt */
-        if (register_external_interrupt(0x1201, do_ext_call_interrupt) != 0)
-                panic("Couldn't request external interrupt 0x1201");
-        memset(lowcore_ptr,0,sizeof(lowcore_ptr));  
-        /*
-         *  Initialize prefix pages and stacks for all possible cpus
-         */
+	/* request the 0x1201 emergency signal external interrupt */
+	if (register_external_interrupt(0x1201, do_ext_call_interrupt) != 0)
+		panic("Couldn't request external interrupt 0x1201");
+	memset(lowcore_ptr, 0, sizeof(lowcore_ptr));
+	/*
+	 *  Initialize prefix pages and stacks for all possible cpus
+	 */
 	print_cpu_info(&S390_lowcore.cpu_data);
 
-        for_each_possible_cpu(i) {
+	for_each_possible_cpu(i) {
 		lowcore_ptr[i] = (struct _lowcore *)
-			__get_free_pages(GFP_KERNEL|GFP_DMA, 
-					sizeof(void*) == 8 ? 1 : 0);
-		stack = __get_free_pages(GFP_KERNEL,ASYNC_ORDER);
-		if (lowcore_ptr[i] == NULL || stack == 0ULL)
+			__get_free_pages(GFP_KERNEL | GFP_DMA,
+					 sizeof(void*) == 8 ? 1 : 0);
+		stack = __get_free_pages(GFP_KERNEL, ASYNC_ORDER);
+		if (!lowcore_ptr[i] || !stack)
 			panic("smp_boot_cpus failed to allocate memory\n");
 
 		*(lowcore_ptr[i]) = S390_lowcore;
-		lowcore_ptr[i]->async_stack = stack + (ASYNC_SIZE);
-		stack = __get_free_pages(GFP_KERNEL,0);
-		if (stack == 0ULL)
+		lowcore_ptr[i]->async_stack = stack + ASYNC_SIZE;
+		stack = __get_free_pages(GFP_KERNEL, 0);
+		if (!stack)
 			panic("smp_boot_cpus failed to allocate memory\n");
-		lowcore_ptr[i]->panic_stack = stack + (PAGE_SIZE);
+		lowcore_ptr[i]->panic_stack = stack + PAGE_SIZE;
 #ifndef CONFIG_64BIT
 		if (MACHINE_HAS_IEEE) {
 			lowcore_ptr[i]->extended_save_area_addr =
-				(__u32) __get_free_pages(GFP_KERNEL,0);
-			if (lowcore_ptr[i]->extended_save_area_addr == 0)
+				(__u32) __get_free_pages(GFP_KERNEL, 0);
+			if (!lowcore_ptr[i]->extended_save_area_addr)
 				panic("smp_boot_cpus failed to "
 				      "allocate memory\n");
 		}
@@ -754,34 +764,63 @@
  */
 int setup_profiling_timer(unsigned int multiplier)
 {
-        return 0;
+	return 0;
 }
 
 static DEFINE_PER_CPU(struct cpu, cpu_devices);
 
+static ssize_t show_capability(struct sys_device *dev, char *buf)
+{
+	unsigned int capability;
+	int rc;
+
+	rc = get_cpu_capability(&capability);
+	if (rc)
+		return rc;
+	return sprintf(buf, "%u\n", capability);
+}
+static SYSDEV_ATTR(capability, 0444, show_capability, NULL);
+
+static int __cpuinit smp_cpu_notify(struct notifier_block *self,
+				    unsigned long action, void *hcpu)
+{
+	unsigned int cpu = (unsigned int)(long)hcpu;
+	struct cpu *c = &per_cpu(cpu_devices, cpu);
+	struct sys_device *s = &c->sysdev;
+
+	switch (action) {
+	case CPU_ONLINE:
+		if (sysdev_create_file(s, &attr_capability))
+			return NOTIFY_BAD;
+		break;
+	case CPU_DEAD:
+		sysdev_remove_file(s, &attr_capability);
+		break;
+	}
+	return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata smp_cpu_nb = {
+	.notifier_call = smp_cpu_notify,
+};
+
 static int __init topology_init(void)
 {
 	int cpu;
-	int ret;
+
+	register_cpu_notifier(&smp_cpu_nb);
 
 	for_each_possible_cpu(cpu) {
 		struct cpu *c = &per_cpu(cpu_devices, cpu);
+		struct sys_device *s = &c->sysdev;
 
 		c->hotpluggable = 1;
-		ret = register_cpu(c, cpu);
-		if (ret)
-			printk(KERN_WARNING "topology_init: register_cpu %d "
-			       "failed (%d)\n", cpu, ret);
+		register_cpu(c, cpu);
+		if (!cpu_online(cpu))
+			continue;
+		s = &c->sysdev;
+		sysdev_create_file(s, &attr_capability);
 	}
 	return 0;
 }
-
 subsys_initcall(topology_init);
-
-EXPORT_SYMBOL(cpu_online_map);
-EXPORT_SYMBOL(cpu_possible_map);
-EXPORT_SYMBOL(lowcore_ptr);
-EXPORT_SYMBOL(smp_ctl_set_bit);
-EXPORT_SYMBOL(smp_ctl_clear_bit);
-EXPORT_SYMBOL(smp_get_cpu);
-EXPORT_SYMBOL(smp_put_cpu);
diff --git a/arch/s390/kernel/sys_s390.c b/arch/s390/kernel/sys_s390.c
index 584ed95..3a77c22 100644
--- a/arch/s390/kernel/sys_s390.c
+++ b/arch/s390/kernel/sys_s390.c
@@ -266,23 +266,3 @@
 		return -EFAULT;
 	return sys_fadvise64_64(a.fd, a.offset, a.len, a.advice);
 }
-
-/*
- * Do a system call from kernel instead of calling sys_execve so we
- * end up with proper pt_regs.
- */
-int kernel_execve(const char *filename, char *const argv[], char *const envp[])
-{
-	register const char *__arg1 asm("2") = filename;
-	register char *const*__arg2 asm("3") = argv;
-	register char *const*__arg3 asm("4") = envp;
-	register long __svcres asm("2");
-	asm volatile(
-		"svc %b1"
-		: "=d" (__svcres)
-		: "i" (__NR_execve),
-		  "0" (__arg1),
-		  "d" (__arg2),
-		  "d" (__arg3) : "memory");
-	return __svcres;
-}
diff --git a/arch/s390/kernel/syscalls.S b/arch/s390/kernel/syscalls.S
index c774f10..cd8d321 100644
--- a/arch/s390/kernel/syscalls.S
+++ b/arch/s390/kernel/syscalls.S
@@ -10,7 +10,7 @@
 
 NI_SYSCALL							/* 0 */
 SYSCALL(sys_exit,sys_exit,sys32_exit_wrapper)
-SYSCALL(sys_fork_glue,sys_fork_glue,sys_fork_glue)
+SYSCALL(sys_fork,sys_fork,sys_fork)
 SYSCALL(sys_read,sys_read,sys32_read_wrapper)
 SYSCALL(sys_write,sys_write,sys32_write_wrapper)
 SYSCALL(sys_open,sys_open,sys32_open_wrapper)			/* 5 */
@@ -19,7 +19,7 @@
 SYSCALL(sys_creat,sys_creat,sys32_creat_wrapper)
 SYSCALL(sys_link,sys_link,sys32_link_wrapper)
 SYSCALL(sys_unlink,sys_unlink,sys32_unlink_wrapper)		/* 10 */
-SYSCALL(sys_execve_glue,sys_execve_glue,sys32_execve_glue)
+SYSCALL(sys_execve,sys_execve,sys32_execve)
 SYSCALL(sys_chdir,sys_chdir,sys32_chdir_wrapper)
 SYSCALL(sys_time,sys_ni_syscall,sys32_time_wrapper)		/* old time syscall */
 SYSCALL(sys_mknod,sys_mknod,sys32_mknod_wrapper)
@@ -127,8 +127,8 @@
 SYSCALL(sys_sysinfo,sys_sysinfo,compat_sys_sysinfo_wrapper)
 SYSCALL(sys_ipc,sys_ipc,sys32_ipc_wrapper)
 SYSCALL(sys_fsync,sys_fsync,sys32_fsync_wrapper)
-SYSCALL(sys_sigreturn_glue,sys_sigreturn_glue,sys32_sigreturn_glue)
-SYSCALL(sys_clone_glue,sys_clone_glue,sys32_clone_glue)		/* 120 */
+SYSCALL(sys_sigreturn,sys_sigreturn,sys32_sigreturn)
+SYSCALL(sys_clone,sys_clone,sys32_clone)			/* 120 */
 SYSCALL(sys_setdomainname,sys_setdomainname,sys32_setdomainname_wrapper)
 SYSCALL(sys_newuname,s390x_newuname,sys32_newuname_wrapper)
 NI_SYSCALL							/* modify_ldt for i386 */
@@ -181,7 +181,7 @@
 SYSCALL(sys_setresgid16,sys_ni_syscall,sys32_setresgid16_wrapper)	/* 170 old setresgid16 syscall */
 SYSCALL(sys_getresgid16,sys_ni_syscall,sys32_getresgid16_wrapper)	/* old getresgid16 syscall */
 SYSCALL(sys_prctl,sys_prctl,sys32_prctl_wrapper)
-SYSCALL(sys_rt_sigreturn_glue,sys_rt_sigreturn_glue,sys32_rt_sigreturn_glue)
+SYSCALL(sys_rt_sigreturn,sys_rt_sigreturn,sys32_rt_sigreturn)
 SYSCALL(sys_rt_sigaction,sys_rt_sigaction,sys32_rt_sigaction_wrapper)
 SYSCALL(sys_rt_sigprocmask,sys_rt_sigprocmask,sys32_rt_sigprocmask_wrapper)	/* 175 */
 SYSCALL(sys_rt_sigpending,sys_rt_sigpending,sys32_rt_sigpending_wrapper)
@@ -194,11 +194,11 @@
 SYSCALL(sys_getcwd,sys_getcwd,sys32_getcwd_wrapper)
 SYSCALL(sys_capget,sys_capget,sys32_capget_wrapper)
 SYSCALL(sys_capset,sys_capset,sys32_capset_wrapper)		/* 185 */
-SYSCALL(sys_sigaltstack_glue,sys_sigaltstack_glue,sys32_sigaltstack_glue)
+SYSCALL(sys_sigaltstack,sys_sigaltstack,sys32_sigaltstack)
 SYSCALL(sys_sendfile,sys_sendfile64,sys32_sendfile_wrapper)
 NI_SYSCALL							/* streams1 */
 NI_SYSCALL							/* streams2 */
-SYSCALL(sys_vfork_glue,sys_vfork_glue,sys_vfork_glue)		/* 190 */
+SYSCALL(sys_vfork,sys_vfork,sys_vfork)				/* 190 */
 SYSCALL(sys_getrlimit,sys_getrlimit,compat_sys_getrlimit_wrapper)
 SYSCALL(sys_mmap2,sys_mmap2,sys32_mmap2_wrapper)
 SYSCALL(sys_truncate64,sys_ni_syscall,sys32_truncate64_wrapper)
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c
index e1ad464..711dae8 100644
--- a/arch/s390/kernel/time.c
+++ b/arch/s390/kernel/time.c
@@ -280,7 +280,6 @@
 }
 
 static void etr_reset(void);
-static void etr_init(void);
 static void etr_ext_handler(__u16);
 
 /*
@@ -355,7 +354,6 @@
 #ifdef CONFIG_VIRT_TIMER
 	vtime_init();
 #endif
-	etr_init();
 }
 
 /*
@@ -426,11 +424,11 @@
 static int etr_port1_uptodate;
 static unsigned long etr_events;
 static struct timer_list etr_timer;
-static struct tasklet_struct etr_tasklet;
 static DEFINE_PER_CPU(atomic_t, etr_sync_word);
 
 static void etr_timeout(unsigned long dummy);
-static void etr_tasklet_fn(unsigned long dummy);
+static void etr_work_fn(struct work_struct *work);
+static DECLARE_WORK(etr_work, etr_work_fn);
 
 /*
  * The etr get_clock function. It will write the current clock value
@@ -507,29 +505,31 @@
 	}
 }
 
-static void etr_init(void)
+static int __init etr_init(void)
 {
 	struct etr_aib aib;
 
 	if (test_bit(ETR_FLAG_ENOSYS, &etr_flags))
-		return;
+		return 0;
 	/* Check if this machine has the steai instruction. */
 	if (etr_steai(&aib, ETR_STEAI_STEPPING_PORT) == 0)
 		set_bit(ETR_FLAG_STEAI, &etr_flags);
 	setup_timer(&etr_timer, etr_timeout, 0UL);
-	tasklet_init(&etr_tasklet, etr_tasklet_fn, 0);
 	if (!etr_port0_online && !etr_port1_online)
 		set_bit(ETR_FLAG_EACCES, &etr_flags);
 	if (etr_port0_online) {
 		set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events);
-		tasklet_hi_schedule(&etr_tasklet);
+		schedule_work(&etr_work);
 	}
 	if (etr_port1_online) {
 		set_bit(ETR_EVENT_PORT1_CHANGE, &etr_events);
-		tasklet_hi_schedule(&etr_tasklet);
+		schedule_work(&etr_work);
 	}
+	return 0;
 }
 
+arch_initcall(etr_init);
+
 /*
  * Two sorts of ETR machine checks. The architecture reads:
  * "When a machine-check niterruption occurs and if a switch-to-local or
@@ -549,7 +549,7 @@
 		return;
 	etr_disable_sync_clock(NULL);
 	set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events);
-	tasklet_hi_schedule(&etr_tasklet);
+	schedule_work(&etr_work);
 }
 
 /*
@@ -564,7 +564,7 @@
 		return;
 	etr_disable_sync_clock(NULL);
 	set_bit(ETR_EVENT_SYNC_CHECK, &etr_events);
-	tasklet_hi_schedule(&etr_tasklet);
+	schedule_work(&etr_work);
 }
 
 /*
@@ -591,13 +591,13 @@
 		 * Both ports are not up-to-date now.
 		 */
 		set_bit(ETR_EVENT_PORT_ALERT, &etr_events);
-	tasklet_hi_schedule(&etr_tasklet);
+	schedule_work(&etr_work);
 }
 
 static void etr_timeout(unsigned long dummy)
 {
 	set_bit(ETR_EVENT_UPDATE, &etr_events);
-	tasklet_hi_schedule(&etr_tasklet);
+	schedule_work(&etr_work);
 }
 
 /*
@@ -927,7 +927,7 @@
 	if (!eacr.e0 && !eacr.e1)
 		return eacr;
 
-	/* Update port0 or port1 with aib stored in etr_tasklet_fn. */
+	/* Update port0 or port1 with aib stored in etr_work_fn. */
 	if (aib->esw.q == 0) {
 		/* Information for port 0 stored. */
 		if (eacr.p0 && !etr_port0_uptodate) {
@@ -1007,7 +1007,7 @@
  * particular this is the only function that calls etr_update_eacr(),
  * it "controls" the etr control register.
  */
-static void etr_tasklet_fn(unsigned long dummy)
+static void etr_work_fn(struct work_struct *work)
 {
 	unsigned long long now;
 	struct etr_eacr eacr;
@@ -1220,13 +1220,13 @@
 			return count;	/* Nothing to do. */
 		etr_port0_online = value;
 		set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events);
-		tasklet_hi_schedule(&etr_tasklet);
+		schedule_work(&etr_work);
 	} else {
 		if (etr_port1_online == value)
 			return count;	/* Nothing to do. */
 		etr_port1_online = value;
 		set_bit(ETR_EVENT_PORT1_CHANGE, &etr_events);
-		tasklet_hi_schedule(&etr_tasklet);
+		schedule_work(&etr_work);
 	}
 	return count;
 }
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c
index f0e5a32..49dec83 100644
--- a/arch/s390/kernel/traps.c
+++ b/arch/s390/kernel/traps.c
@@ -30,7 +30,7 @@
 #include <linux/kallsyms.h>
 #include <linux/reboot.h>
 #include <linux/kprobes.h>
-
+#include <linux/bug.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -188,18 +188,31 @@
 
 EXPORT_SYMBOL(dump_stack);
 
+static inline int mask_bits(struct pt_regs *regs, unsigned long bits)
+{
+	return (regs->psw.mask & bits) / ((~bits + 1) & bits);
+}
+
 void show_registers(struct pt_regs *regs)
 {
-	mm_segment_t old_fs;
 	char *mode;
-	int i;
 
 	mode = (regs->psw.mask & PSW_MASK_PSTATE) ? "User" : "Krnl";
 	printk("%s PSW : %p %p",
 	       mode, (void *) regs->psw.mask,
 	       (void *) regs->psw.addr);
 	print_symbol(" (%s)\n", regs->psw.addr & PSW_ADDR_INSN);
-	printk("%s GPRS: " FOURLONG, mode,
+	printk("           R:%x T:%x IO:%x EX:%x Key:%x M:%x W:%x "
+	       "P:%x AS:%x CC:%x PM:%x", mask_bits(regs, PSW_MASK_PER),
+	       mask_bits(regs, PSW_MASK_DAT), mask_bits(regs, PSW_MASK_IO),
+	       mask_bits(regs, PSW_MASK_EXT), mask_bits(regs, PSW_MASK_KEY),
+	       mask_bits(regs, PSW_MASK_MCHECK), mask_bits(regs, PSW_MASK_WAIT),
+	       mask_bits(regs, PSW_MASK_PSTATE), mask_bits(regs, PSW_MASK_ASC),
+	       mask_bits(regs, PSW_MASK_CC), mask_bits(regs, PSW_MASK_PM));
+#ifdef CONFIG_64BIT
+	printk(" EA:%x", mask_bits(regs, PSW_BASE_BITS));
+#endif
+	printk("\n%s GPRS: " FOURLONG, mode,
 	       regs->gprs[0], regs->gprs[1], regs->gprs[2], regs->gprs[3]);
 	printk("           " FOURLONG,
 	       regs->gprs[4], regs->gprs[5], regs->gprs[6], regs->gprs[7]);
@@ -208,41 +221,7 @@
 	printk("           " FOURLONG,
 	       regs->gprs[12], regs->gprs[13], regs->gprs[14], regs->gprs[15]);
 
-#if 0
-	/* FIXME: this isn't needed any more but it changes the ksymoops
-	 * input. To remove or not to remove ... */
-	save_access_regs(regs->acrs);
-	printk("%s ACRS: %08x %08x %08x %08x\n", mode,
-	       regs->acrs[0], regs->acrs[1], regs->acrs[2], regs->acrs[3]);
-	printk("           %08x %08x %08x %08x\n",
-	       regs->acrs[4], regs->acrs[5], regs->acrs[6], regs->acrs[7]);
-	printk("           %08x %08x %08x %08x\n",
-	       regs->acrs[8], regs->acrs[9], regs->acrs[10], regs->acrs[11]);
-	printk("           %08x %08x %08x %08x\n",
-	       regs->acrs[12], regs->acrs[13], regs->acrs[14], regs->acrs[15]);
-#endif
-
-	/*
-	 * Print the first 20 byte of the instruction stream at the
-	 * time of the fault.
-	 */
-	old_fs = get_fs();
-	if (regs->psw.mask & PSW_MASK_PSTATE)
-		set_fs(USER_DS);
-	else
-		set_fs(KERNEL_DS);
-	printk("%s Code: ", mode);
-	for (i = 0; i < 20; i++) {
-		unsigned char c;
-		if (__get_user(c, (char __user *)(regs->psw.addr + i))) {
-			printk(" Bad PSW.");
-			break;
-		}
-		printk("%02x ", c);
-	}
-	set_fs(old_fs);
-
-	printk("\n");
+	show_code(regs);
 }	
 
 /* This is called from fs/proc/array.c */
@@ -318,6 +297,11 @@
 #endif
 }
 
+int is_valid_bugaddr(unsigned long addr)
+{
+	return 1;
+}
+
 static void __kprobes inline do_trap(long interruption_code, int signr,
 					char *str, struct pt_regs *regs,
 					siginfo_t *info)
@@ -344,8 +328,14 @@
                 fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN);
                 if (fixup)
                         regs->psw.addr = fixup->fixup | PSW_ADDR_AMODE;
-                else
-                        die(str, regs, interruption_code);
+		else {
+			enum bug_trap_type btt;
+
+			btt = report_bug(regs->psw.addr & PSW_ADDR_INSN);
+			if (btt == BUG_TRAP_TYPE_WARN)
+				return;
+			die(str, regs, interruption_code);
+		}
         }
 }
 
diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S
index c30716a..418f642 100644
--- a/arch/s390/kernel/vmlinux.lds.S
+++ b/arch/s390/kernel/vmlinux.lds.S
@@ -45,6 +45,8 @@
   __ex_table : { *(__ex_table) }
   __stop___ex_table = .;
 
+  BUG_TABLE
+
   .data : {			/* Data */
 	*(.data)
 	CONSTRUCTORS
@@ -77,6 +79,12 @@
 	*(.init.text)
 	_einittext = .;
   }
+  /*
+   * .exit.text is discarded at runtime, not link time,
+   * to deal with references from __bug_table
+   */
+  .exit.text :	 { *(.exit.text) }
+
   .init.data : { *(.init.data) }
   . = ALIGN(256);
   __setup_start = .;
@@ -116,7 +124,7 @@
 
   /* Sections to be discarded */
   /DISCARD/ : {
-	*(.exit.text) *(.exit.data) *(.exitcall.exit)
+	*(.exit.data) *(.exitcall.exit)
 	}
 
   /* Stabs debugging sections.  */
diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c
index 9d5b028..1e1a6ee 100644
--- a/arch/s390/kernel/vtime.c
+++ b/arch/s390/kernel/vtime.c
@@ -128,7 +128,7 @@
 	S390_lowcore.last_update_timer = expires;
 
 	/* store expire time for this CPU timer */
-	per_cpu(virt_cpu_timer, smp_processor_id()).to_expire = expires;
+	__get_cpu_var(virt_cpu_timer).to_expire = expires;
 }
 #else
 static inline void set_vtimer(__u64 expires)
@@ -137,7 +137,7 @@
 	asm volatile ("SPT %0" : : "m" (S390_lowcore.last_update_timer));
 
 	/* store expire time for this CPU timer */
-	per_cpu(virt_cpu_timer, smp_processor_id()).to_expire = expires;
+	__get_cpu_var(virt_cpu_timer).to_expire = expires;
 }
 #endif
 
@@ -145,7 +145,7 @@
 {
 	struct vtimer_queue *vt_list;
 
-	vt_list = &per_cpu(virt_cpu_timer, smp_processor_id());
+	vt_list = &__get_cpu_var(virt_cpu_timer);
 
 	/* CPU timer interrupt is pending, don't reprogramm it */
 	if (vt_list->idle & 1LL<<63)
@@ -159,7 +159,7 @@
 {
 	struct vtimer_queue *vt_list;
 
-	vt_list = &per_cpu(virt_cpu_timer, smp_processor_id());
+	vt_list = &__get_cpu_var(virt_cpu_timer);
 
 	/* nothing to do */
 	if (list_empty(&vt_list->list)) {
@@ -219,7 +219,7 @@
 	if (list_empty(cb_list))
 		return;
 
-	vt_list = &per_cpu(virt_cpu_timer, smp_processor_id());
+	vt_list = &__get_cpu_var(virt_cpu_timer);
 
 	list_for_each_entry_safe(event, tmp, cb_list, entry) {
 		fn = event->function;
@@ -244,7 +244,6 @@
  */
 static void do_cpu_timer_interrupt(__u16 error_code)
 {
-	int cpu;
 	__u64 next, delta;
 	struct vtimer_queue *vt_list;
 	struct vtimer_list *event, *tmp;
@@ -253,8 +252,7 @@
 	struct list_head cb_list;
 
 	INIT_LIST_HEAD(&cb_list);
-	cpu = smp_processor_id();
-	vt_list = &per_cpu(virt_cpu_timer, cpu);
+	vt_list = &__get_cpu_var(virt_cpu_timer);
 
 	/* walk timer list, fire all expired events */
 	spin_lock(&vt_list->lock);
@@ -534,7 +532,7 @@
 	/* enable cpu timer interrupts */
 	__ctl_set_bit(0,10);
 
-	vt_list = &per_cpu(virt_cpu_timer, smp_processor_id());
+	vt_list = &__get_cpu_var(virt_cpu_timer);
 	INIT_LIST_HEAD(&vt_list->list);
 	spin_lock_init(&vt_list->lock);
 	vt_list->to_expire = 0;
diff --git a/arch/s390/lib/Makefile b/arch/s390/lib/Makefile
index 7a44fed..59aea65 100644
--- a/arch/s390/lib/Makefile
+++ b/arch/s390/lib/Makefile
@@ -5,6 +5,6 @@
 EXTRA_AFLAGS := -traditional
 
 lib-y += delay.o string.o uaccess_std.o uaccess_pt.o qrnnd.o
-lib-$(CONFIG_32BIT) += div64.o
+obj-$(CONFIG_32BIT) += div64.o
 lib-$(CONFIG_64BIT) += uaccess_mvcos.o
 lib-$(CONFIG_SMP) += spinlock.o
diff --git a/arch/s390/lib/div64.c b/arch/s390/lib/div64.c
index 0481f34..a5f8300 100644
--- a/arch/s390/lib/div64.c
+++ b/arch/s390/lib/div64.c
@@ -147,5 +147,3 @@
 }
 
 #endif /* MARCH_G5 */
-
-EXPORT_SYMBOL(__div64_32);
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c
index 7462aeb..91f705a 100644
--- a/arch/s390/mm/fault.c
+++ b/arch/s390/mm/fault.c
@@ -26,9 +26,9 @@
 #include <linux/module.h>
 #include <linux/hardirq.h>
 #include <linux/kprobes.h>
+#include <linux/uaccess.h>
 
 #include <asm/system.h>
-#include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/kdebug.h>
 #include <asm/s390_ext.h>
@@ -52,34 +52,24 @@
 extern void die(const char *,struct pt_regs *,long);
 
 #ifdef CONFIG_KPROBES
-static ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain);
-int register_page_fault_notifier(struct notifier_block *nb)
+static inline int notify_page_fault(struct pt_regs *regs, long err)
 {
-	return atomic_notifier_chain_register(&notify_page_fault_chain, nb);
-}
+	int ret = 0;
 
-int unregister_page_fault_notifier(struct notifier_block *nb)
-{
-	return atomic_notifier_chain_unregister(&notify_page_fault_chain, nb);
-}
+	/* kprobe_running() needs smp_processor_id() */
+	if (!user_mode(regs)) {
+		preempt_disable();
+		if (kprobe_running() && kprobe_fault_handler(regs, 14))
+			ret = 1;
+		preempt_enable();
+	}
 
-static inline int notify_page_fault(enum die_val val, const char *str,
-			struct pt_regs *regs, long err, int trap, int sig)
-{
-	struct die_args args = {
-		.regs = regs,
-		.str = str,
-		.err = err,
-		.trapnr = trap,
-		.signr = sig
-	};
-	return atomic_notifier_call_chain(&notify_page_fault_chain, val, &args);
+	return ret;
 }
 #else
-static inline int notify_page_fault(enum die_val val, const char *str,
-			struct pt_regs *regs, long err, int trap, int sig)
+static inline int notify_page_fault(struct pt_regs *regs, long err)
 {
-	return NOTIFY_DONE;
+	return 0;
 }
 #endif
 
@@ -170,74 +160,127 @@
 	force_sig_info(SIGSEGV, &si, current);
 }
 
+static void do_no_context(struct pt_regs *regs, unsigned long error_code,
+			  unsigned long address)
+{
+	const struct exception_table_entry *fixup;
+
+	/* Are we prepared to handle this kernel fault?  */
+	fixup = search_exception_tables(regs->psw.addr & __FIXUP_MASK);
+	if (fixup) {
+		regs->psw.addr = fixup->fixup | PSW_ADDR_AMODE;
+		return;
+	}
+
+	/*
+	 * Oops. The kernel tried to access some bad page. We'll have to
+	 * terminate things with extreme prejudice.
+	 */
+	if (check_space(current) == 0)
+		printk(KERN_ALERT "Unable to handle kernel pointer dereference"
+		       " at virtual kernel address %p\n", (void *)address);
+	else
+		printk(KERN_ALERT "Unable to handle kernel paging request"
+		       " at virtual user address %p\n", (void *)address);
+
+	die("Oops", regs, error_code);
+	do_exit(SIGKILL);
+}
+
+static void do_low_address(struct pt_regs *regs, unsigned long error_code)
+{
+	/* Low-address protection hit in kernel mode means
+	   NULL pointer write access in kernel mode.  */
+	if (regs->psw.mask & PSW_MASK_PSTATE) {
+		/* Low-address protection hit in user mode 'cannot happen'. */
+		die ("Low-address protection", regs, error_code);
+		do_exit(SIGKILL);
+	}
+
+	do_no_context(regs, error_code, 0);
+}
+
+/*
+ * We ran out of memory, or some other thing happened to us that made
+ * us unable to handle the page fault gracefully.
+ */
+static int do_out_of_memory(struct pt_regs *regs, unsigned long error_code,
+			    unsigned long address)
+{
+	struct task_struct *tsk = current;
+	struct mm_struct *mm = tsk->mm;
+
+	up_read(&mm->mmap_sem);
+	if (is_init(tsk)) {
+		yield();
+		down_read(&mm->mmap_sem);
+		return 1;
+	}
+	printk("VM: killing process %s\n", tsk->comm);
+	if (regs->psw.mask & PSW_MASK_PSTATE)
+		do_exit(SIGKILL);
+	do_no_context(regs, error_code, address);
+	return 0;
+}
+
+static void do_sigbus(struct pt_regs *regs, unsigned long error_code,
+		      unsigned long address)
+{
+	struct task_struct *tsk = current;
+	struct mm_struct *mm = tsk->mm;
+
+	up_read(&mm->mmap_sem);
+	/*
+	 * Send a sigbus, regardless of whether we were in kernel
+	 * or user mode.
+	 */
+	tsk->thread.prot_addr = address;
+	tsk->thread.trap_no = error_code;
+	force_sig(SIGBUS, tsk);
+
+	/* Kernel mode? Handle exceptions or die */
+	if (!(regs->psw.mask & PSW_MASK_PSTATE))
+		do_no_context(regs, error_code, address);
+}
+
 #ifdef CONFIG_S390_EXEC_PROTECT
 extern long sys_sigreturn(struct pt_regs *regs);
 extern long sys_rt_sigreturn(struct pt_regs *regs);
 extern long sys32_sigreturn(struct pt_regs *regs);
 extern long sys32_rt_sigreturn(struct pt_regs *regs);
 
-static inline void do_sigreturn(struct mm_struct *mm, struct pt_regs *regs,
-				int rt)
-{
-	up_read(&mm->mmap_sem);
-	clear_tsk_thread_flag(current, TIF_SINGLE_STEP);
-#ifdef CONFIG_COMPAT
-	if (test_tsk_thread_flag(current, TIF_31BIT)) {
-		if (rt)
-			sys32_rt_sigreturn(regs);
-		else
-			sys32_sigreturn(regs);
-		return;
-	}
-#endif /* CONFIG_COMPAT */
-	if (rt)
-		sys_rt_sigreturn(regs);
-	else
-		sys_sigreturn(regs);
-	return;
-}
-
 static int signal_return(struct mm_struct *mm, struct pt_regs *regs,
 			 unsigned long address, unsigned long error_code)
 {
-	pgd_t *pgd;
-	pmd_t *pmd;
-	pte_t *pte;
-	u16 *instruction;
-	unsigned long pfn, uaddr = regs->psw.addr;
+	u16 instruction;
+	int rc, compat;
 
-	spin_lock(&mm->page_table_lock);
-	pgd = pgd_offset(mm, uaddr);
-	if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
-		goto out_fault;
-	pmd = pmd_offset(pgd, uaddr);
-	if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd)))
-		goto out_fault;
-	pte = pte_offset_map(pmd_offset(pgd_offset(mm, uaddr), uaddr), uaddr);
-	if (!pte || !pte_present(*pte))
-		goto out_fault;
-	pfn = pte_pfn(*pte);
-	if (!pfn_valid(pfn))
-		goto out_fault;
-	spin_unlock(&mm->page_table_lock);
+	pagefault_disable();
+	rc = __get_user(instruction, (u16 __user *) regs->psw.addr);
+	pagefault_enable();
+	if (rc)
+		return -EFAULT;
 
-	instruction = (u16 *) ((pfn << PAGE_SHIFT) + (uaddr & (PAGE_SIZE-1)));
-	if (*instruction == 0x0a77)
-		do_sigreturn(mm, regs, 0);
-	else if (*instruction == 0x0aad)
-		do_sigreturn(mm, regs, 1);
+	up_read(&mm->mmap_sem);
+	clear_tsk_thread_flag(current, TIF_SINGLE_STEP);
+#ifdef CONFIG_COMPAT
+	compat = test_tsk_thread_flag(current, TIF_31BIT);
+	if (compat && instruction == 0x0a77)
+		sys32_sigreturn(regs);
+	else if (compat && instruction == 0x0aad)
+		sys32_rt_sigreturn(regs);
+	else
+#endif
+	if (instruction == 0x0a77)
+		sys_sigreturn(regs);
+	else if (instruction == 0x0aad)
+		sys_rt_sigreturn(regs);
 	else {
-		printk("- XXX - do_exception: task = %s, primary, NO EXEC "
-		       "-> SIGSEGV\n", current->comm);
-		up_read(&mm->mmap_sem);
 		current->thread.prot_addr = address;
 		current->thread.trap_no = error_code;
 		do_sigsegv(regs, error_code, SEGV_MAPERR, address);
 	}
 	return 0;
-out_fault:
-	spin_unlock(&mm->page_table_lock);
-	return -EFAULT;
 }
 #endif /* CONFIG_S390_EXEC_PROTECT */
 
@@ -253,49 +296,23 @@
  *   3b       Region third trans.  ->  Not present       (nullification)
  */
 static inline void
-do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection)
+do_exception(struct pt_regs *regs, unsigned long error_code, int write)
 {
-        struct task_struct *tsk;
-        struct mm_struct *mm;
-        struct vm_area_struct * vma;
-        unsigned long address;
-	const struct exception_table_entry *fixup;
-	int si_code;
+	struct task_struct *tsk;
+	struct mm_struct *mm;
+	struct vm_area_struct *vma;
+	unsigned long address;
 	int space;
+	int si_code;
 
-        tsk = current;
-        mm = tsk->mm;
-	
-	if (notify_page_fault(DIE_PAGE_FAULT, "page fault", regs, error_code, 14,
-					SIGSEGV) == NOTIFY_STOP)
+	if (notify_page_fault(regs, error_code))
 		return;
 
-	/* 
-         * Check for low-address protection.  This needs to be treated
-	 * as a special case because the translation exception code 
-	 * field is not guaranteed to contain valid data in this case.
-	 */
-	if (is_protection && !(S390_lowcore.trans_exc_code & 4)) {
+	tsk = current;
+	mm = tsk->mm;
 
-		/* Low-address protection hit in kernel mode means 
-		   NULL pointer write access in kernel mode.  */
- 		if (!(regs->psw.mask & PSW_MASK_PSTATE)) {
-			address = 0;
-			space = 0;
-			goto no_context;
-		}
-
-		/* Low-address protection hit in user mode 'cannot happen'.  */
-		die ("Low-address protection", regs, error_code);
-        	do_exit(SIGKILL);
-	}
-
-        /* 
-         * get the failing address 
-         * more specific the segment and page table portion of 
-         * the address 
-         */
-        address = S390_lowcore.trans_exc_code & __FAIL_ADDR_MASK;
+	/* get the failing address and the affected space */
+	address = S390_lowcore.trans_exc_code & __FAIL_ADDR_MASK;
 	space = check_space(tsk);
 
 	/*
@@ -313,7 +330,7 @@
 	 */
 	local_irq_enable();
 
-        down_read(&mm->mmap_sem);
+	down_read(&mm->mmap_sem);
 
 	si_code = SEGV_MAPERR;
 	vma = find_vma(mm, address);
@@ -330,19 +347,19 @@
 			return;
 #endif
 
-        if (vma->vm_start <= address) 
-                goto good_area;
-        if (!(vma->vm_flags & VM_GROWSDOWN))
-                goto bad_area;
-        if (expand_stack(vma, address))
-                goto bad_area;
+	if (vma->vm_start <= address)
+		goto good_area;
+	if (!(vma->vm_flags & VM_GROWSDOWN))
+		goto bad_area;
+	if (expand_stack(vma, address))
+		goto bad_area;
 /*
  * Ok, we have a good vm_area for this memory access, so
  * we can handle it..
  */
 good_area:
 	si_code = SEGV_ACCERR;
-	if (!is_protection) {
+	if (!write) {
 		/* page not present, check vm flags */
 		if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE)))
 			goto bad_area;
@@ -357,7 +374,7 @@
 	 * make sure we exit gracefully rather than endlessly redo
 	 * the fault.
 	 */
-	switch (handle_mm_fault(mm, vma, address, is_protection)) {
+	switch (handle_mm_fault(mm, vma, address, write)) {
 	case VM_FAULT_MINOR:
 		tsk->min_flt++;
 		break;
@@ -365,9 +382,12 @@
 		tsk->maj_flt++;
 		break;
 	case VM_FAULT_SIGBUS:
-		goto do_sigbus;
+		do_sigbus(regs, error_code, address);
+		return;
 	case VM_FAULT_OOM:
-		goto out_of_memory;
+		if (do_out_of_memory(regs, error_code, address))
+			goto survive;
+		return;
 	default:
 		BUG();
 	}
@@ -385,75 +405,34 @@
  * Fix it, but check if it's kernel or user first..
  */
 bad_area:
-        up_read(&mm->mmap_sem);
+	up_read(&mm->mmap_sem);
 
-        /* User mode accesses just cause a SIGSEGV */
-        if (regs->psw.mask & PSW_MASK_PSTATE) {
-                tsk->thread.prot_addr = address;
-                tsk->thread.trap_no = error_code;
+	/* User mode accesses just cause a SIGSEGV */
+	if (regs->psw.mask & PSW_MASK_PSTATE) {
+		tsk->thread.prot_addr = address;
+		tsk->thread.trap_no = error_code;
 		do_sigsegv(regs, error_code, si_code, address);
-                return;
+		return;
 	}
 
 no_context:
-        /* Are we prepared to handle this kernel fault?  */
-	fixup = search_exception_tables(regs->psw.addr & __FIXUP_MASK);
-	if (fixup) {
-		regs->psw.addr = fixup->fixup | PSW_ADDR_AMODE;
-                return;
-        }
-
-/*
- * Oops. The kernel tried to access some bad page. We'll have to
- * terminate things with extreme prejudice.
- */
-	if (space == 0)
-                printk(KERN_ALERT "Unable to handle kernel pointer dereference"
-        	       " at virtual kernel address %p\n", (void *)address);
-        else
-                printk(KERN_ALERT "Unable to handle kernel paging request"
-		       " at virtual user address %p\n", (void *)address);
-
-        die("Oops", regs, error_code);
-        do_exit(SIGKILL);
-
-
-/*
- * We ran out of memory, or some other thing happened to us that made
- * us unable to handle the page fault gracefully.
-*/
-out_of_memory:
-	up_read(&mm->mmap_sem);
-	if (is_init(tsk)) {
-		yield();
-		down_read(&mm->mmap_sem);
-		goto survive;
-	}
-	printk("VM: killing process %s\n", tsk->comm);
-	if (regs->psw.mask & PSW_MASK_PSTATE)
-		do_exit(SIGKILL);
-	goto no_context;
-
-do_sigbus:
-	up_read(&mm->mmap_sem);
-
-	/*
-	 * Send a sigbus, regardless of whether we were in kernel
-	 * or user mode.
-	 */
-        tsk->thread.prot_addr = address;
-        tsk->thread.trap_no = error_code;
-	force_sig(SIGBUS, tsk);
-
-	/* Kernel mode? Handle exceptions or die */
-	if (!(regs->psw.mask & PSW_MASK_PSTATE))
-		goto no_context;
+	do_no_context(regs, error_code, address);
 }
 
 void __kprobes do_protection_exception(struct pt_regs *regs,
 				       unsigned long error_code)
 {
+	/* Protection exception is supressing, decrement psw address. */
 	regs->psw.addr -= (error_code >> 16);
+	/*
+	 * Check for low-address protection.  This needs to be treated
+	 * as a special case because the translation exception code
+	 * field is not guaranteed to contain valid data in this case.
+	 */
+	if (unlikely(!(S390_lowcore.trans_exc_code & 4))) {
+		do_low_address(regs, error_code);
+		return;
+	}
 	do_exception(regs, 4, 1);
 }
 
diff --git a/arch/sh/boards/hp6xx/pm.c b/arch/sh/boards/hp6xx/pm.c
index d194773..8143d1b 100644
--- a/arch/sh/boards/hp6xx/pm.c
+++ b/arch/sh/boards/hp6xx/pm.c
@@ -27,9 +27,6 @@
 	u16 hd64461_stbcr;
 #endif
 
-	if (state != PM_SUSPEND_MEM)
-		return -EINVAL;
-
 #ifdef CONFIG_HD64461_ENABLER
 	outb(0, HD64461_PCC1CSCIER);
 
@@ -70,12 +67,9 @@
 	return 0;
 }
 
-/*
- * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk.
- */
 static struct pm_ops hp6x0_pm_ops = {
-	.pm_disk_mode	= PM_DISK_FIRMWARE,
 	.enter		= hp6x0_pm_enter,
+	.valid		= pm_valid_only_mem,
 };
 
 static int __init hp6x0_pm_init(void)
diff --git a/arch/sh/lib/Makefile b/arch/sh/lib/Makefile
index b5681e3..0b9cca5 100644
--- a/arch/sh/lib/Makefile
+++ b/arch/sh/lib/Makefile
@@ -3,7 +3,7 @@
 #
 
 lib-y  = delay.o memset.o memmove.o memchr.o \
-	 checksum.o strcasecmp.o strlen.o div64.o udivdi3.o \
+	 checksum.o strlen.o div64.o udivdi3.o \
 	 div64-generic.o
 
 memcpy-y			:= memcpy.o
diff --git a/arch/sh/lib/strcasecmp.c b/arch/sh/lib/strcasecmp.c
deleted file mode 100644
index 4e57a21..0000000
--- a/arch/sh/lib/strcasecmp.c
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- *  linux/arch/alpha/lib/strcasecmp.c
- */
-
-#include <linux/string.h>
-
-
-/* We handle nothing here except the C locale.  Since this is used in
-   only one place, on strings known to contain only 7 bit ASCII, this
-   is ok.  */
-
-int strcasecmp(const char *a, const char *b)
-{
-	int ca, cb;
-
-	do {
-		ca = *a++ & 0xff;
-		cb = *b++ & 0xff;
-		if (ca >= 'A' && ca <= 'Z')
-			ca += 'a' - 'A';
-		if (cb >= 'A' && cb <= 'Z')
-			cb += 'a' - 'A';
-	} while (ca == cb && ca != '\0');
-
-	return ca - cb;
-}
diff --git a/arch/sparc/kernel/ebus.c b/arch/sparc/kernel/ebus.c
index ba58c3a..7bb86b9c 100644
--- a/arch/sparc/kernel/ebus.c
+++ b/arch/sparc/kernel/ebus.c
@@ -25,7 +25,7 @@
 struct linux_ebus *ebus_chain = NULL;
 
 /* We are together with pcic.c under CONFIG_PCI. */
-extern unsigned int pcic_pin_to_irq(unsigned int, char *name);
+extern unsigned int pcic_pin_to_irq(unsigned int, const char *name);
 
 /*
  * IRQ Blacklist
@@ -69,7 +69,7 @@
 
 /*
  */
-int __init ebus_blacklist_irq(char *name)
+int __init ebus_blacklist_irq(const char *name)
 {
 	struct ebus_device_irq *dp;
 
@@ -86,8 +86,8 @@
 void __init fill_ebus_child(struct device_node *dp,
 			    struct linux_ebus_child *dev)
 {
-	int *regs;
-	int *irqs;
+	const int *regs;
+	const int *irqs;
 	int i, len;
 
 	dev->prom_node = dp;
@@ -146,9 +146,9 @@
 
 void __init fill_ebus_device(struct device_node *dp, struct linux_ebus_device *dev)
 {
-	struct linux_prom_registers *regs;
+	const struct linux_prom_registers *regs;
 	struct linux_ebus_child *child;
-	int *irqs;
+	const int *irqs;
 	int i, n, len;
 	unsigned long baseaddr;
 
@@ -269,7 +269,7 @@
 
 void __init ebus_init(void)
 {
-	struct linux_prom_pci_registers *regs;
+	const struct linux_prom_pci_registers *regs;
 	struct linux_pbm_info *pbm;
 	struct linux_ebus_device *dev;
 	struct linux_ebus *ebus;
diff --git a/arch/sparc/kernel/of_device.c b/arch/sparc/kernel/of_device.c
index 48c24f7..fd7f8cb 100644
--- a/arch/sparc/kernel/of_device.c
+++ b/arch/sparc/kernel/of_device.c
@@ -210,7 +210,7 @@
 				       int *addrc, int *sizec);
 	int		(*map)(u32 *addr, const u32 *range,
 			       int na, int ns, int pna);
-	unsigned int	(*get_flags)(u32 *addr);
+	unsigned int	(*get_flags)(const u32 *addr);
 };
 
 /*
@@ -270,7 +270,7 @@
 	return 0;
 }
 
-static unsigned int of_bus_default_get_flags(u32 *addr)
+static unsigned int of_bus_default_get_flags(const u32 *addr)
 {
 	return IORESOURCE_MEM;
 }
@@ -334,7 +334,7 @@
 	return 0;
 }
 
-static unsigned int of_bus_pci_get_flags(u32 *addr)
+static unsigned int of_bus_pci_get_flags(const u32 *addr)
 {
 	unsigned int flags = 0;
 	u32 w = addr[0];
@@ -375,7 +375,7 @@
 	return of_bus_default_map(addr, range, na, ns, pna);
 }
 
-static unsigned int of_bus_sbus_get_flags(u32 *addr)
+static unsigned int of_bus_sbus_get_flags(const u32 *addr)
 {
 	return IORESOURCE_MEM;
 }
@@ -432,7 +432,7 @@
 				     u32 *addr,
 				     int na, int ns, int pna)
 {
-	u32 *ranges;
+	const u32 *ranges;
 	unsigned int rlen;
 	int rone;
 
@@ -470,7 +470,7 @@
 	struct of_bus *bus;
 	int na, ns;
 	int index, num_reg;
-	void *preg;
+	const void *preg;
 
 	if (!parent)
 		return;
@@ -492,7 +492,7 @@
 	for (index = 0; index < num_reg; index++) {
 		struct resource *r = &op->resource[index];
 		u32 addr[OF_MAX_ADDR_CELLS];
-		u32 *reg = (preg + (index * ((na + ns) * 4)));
+		const u32 *reg = (preg + (index * ((na + ns) * 4)));
 		struct device_node *dp = op->node;
 		struct device_node *pp = p_op->node;
 		struct of_bus *pbus, *dbus;
@@ -559,7 +559,7 @@
 						 struct device *parent)
 {
 	struct of_device *op = kzalloc(sizeof(*op), GFP_KERNEL);
-	struct linux_prom_irqs *intr;
+	const struct linux_prom_irqs *intr;
 	int len, i;
 
 	if (!op)
@@ -579,7 +579,8 @@
 		for (i = 0; i < op->num_irqs; i++)
 			op->irqs[i] = intr[i].pri;
 	} else {
-		unsigned int *irq = of_get_property(dp, "interrupts", &len);
+		const unsigned int *irq =
+			of_get_property(dp, "interrupts", &len);
 
 		if (irq) {
 			op->num_irqs = len / sizeof(unsigned int);
@@ -594,7 +595,7 @@
 			0, 0, 1, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 0,
 		};
 		struct device_node *io_unit, *sbi = dp->parent;
-		struct linux_prom_registers *regs;
+		const struct linux_prom_registers *regs;
 		int board, slot;
 
 		while (sbi) {
diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c
index 1c927c5..5ca7e8f 100644
--- a/arch/sparc/kernel/pcic.c
+++ b/arch/sparc/kernel/pcic.c
@@ -37,8 +37,6 @@
 #include <asm/irq_regs.h>
 
 
-unsigned int pcic_pin_to_irq(unsigned int pin, char *name);
-
 /*
  * I studied different documents and many live PROMs both from 2.30
  * family and 3.xx versions. I came to the amazing conclusion: there is
@@ -681,7 +679,7 @@
  * pcic_pin_to_irq() is exported to ebus.c.
  */
 unsigned int
-pcic_pin_to_irq(unsigned int pin, char *name)
+pcic_pin_to_irq(unsigned int pin, const char *name)
 {
 	struct linux_pcic *pcic = &pcic0;
 	unsigned int irq;
diff --git a/arch/sparc/kernel/prom.c b/arch/sparc/kernel/prom.c
index 2cc302b..eed140b 100644
--- a/arch/sparc/kernel/prom.c
+++ b/arch/sparc/kernel/prom.c
@@ -32,12 +32,13 @@
  */
 static DEFINE_RWLOCK(devtree_lock);
 
-int of_device_is_compatible(struct device_node *device, const char *compat)
+int of_device_is_compatible(const struct device_node *device,
+			    const char *compat)
 {
 	const char* cp;
 	int cplen, l;
 
-	cp = (char *) of_get_property(device, "compatible", &cplen);
+	cp = of_get_property(device, "compatible", &cplen);
 	if (cp == NULL)
 		return 0;
 	while (cplen > 0) {
@@ -150,13 +151,14 @@
 }
 EXPORT_SYMBOL(of_find_compatible_node);
 
-struct property *of_find_property(struct device_node *np, const char *name,
+struct property *of_find_property(const struct device_node *np,
+				  const char *name,
 				  int *lenp)
 {
 	struct property *pp;
 
 	for (pp = np->properties; pp != 0; pp = pp->next) {
-		if (strcmp(pp->name, name) == 0) {
+		if (strcasecmp(pp->name, name) == 0) {
 			if (lenp != 0)
 				*lenp = pp->length;
 			break;
@@ -170,7 +172,8 @@
  * Find a property with a given name for a given node
  * and return the value.
  */
-void *of_get_property(struct device_node *np, const char *name, int *lenp)
+const void *of_get_property(const struct device_node *np, const char *name,
+			    int *lenp)
 {
 	struct property *pp = of_find_property(np,name,lenp);
 	return pp ? pp->value : NULL;
@@ -192,7 +195,7 @@
 
 int of_n_addr_cells(struct device_node *np)
 {
-	int* ip;
+	const int* ip;
 	do {
 		if (np->parent)
 			np = np->parent;
@@ -207,7 +210,7 @@
 
 int of_n_size_cells(struct device_node *np)
 {
-	int* ip;
+	const int* ip;
 	do {
 		if (np->parent)
 			np = np->parent;
@@ -239,7 +242,7 @@
 	while (*prevp) {
 		struct property *prop = *prevp;
 
-		if (!strcmp(prop->name, name)) {
+		if (!strcasecmp(prop->name, name)) {
 			void *old_val = prop->value;
 			int ret;
 
diff --git a/arch/sparc/kernel/time.c b/arch/sparc/kernel/time.c
index 9bb1240aa..f1401b5 100644
--- a/arch/sparc/kernel/time.c
+++ b/arch/sparc/kernel/time.c
@@ -301,7 +301,7 @@
 static int __devinit clock_probe(struct of_device *op, const struct of_device_id *match)
 {
 	struct device_node *dp = op->node;
-	char *model = of_get_property(dp, "model", NULL);
+	const char *model = of_get_property(dp, "model", NULL);
 
 	if (!model)
 		return -ENODEV;
diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig
index 1a6348b..590a41b 100644
--- a/arch/sparc64/Kconfig
+++ b/arch/sparc64/Kconfig
@@ -19,6 +19,14 @@
 	  SPARC64 ports; its web page is available at
 	  <http://www.ultralinux.org/>.
 
+config GENERIC_TIME
+	bool
+	default y
+
+config GENERIC_CLOCKEVENTS
+	bool
+	default y
+
 config 64BIT
 	def_bool y
 
@@ -34,10 +42,6 @@
 	bool
 	default y
 
-config TIME_INTERPOLATION
-	bool
-	default y
-
 config ARCH_MAY_HAVE_PC_FDC
 	bool
 	default y
@@ -113,6 +117,8 @@
 
 menu "General machine setup"
 
+source "kernel/time/Kconfig"
+
 config SMP
 	bool "Symmetric multi-processing support"
 	---help---
@@ -214,6 +220,7 @@
 
 config ARCH_SPARSEMEM_DEFAULT
 	def_bool y
+	select SPARSEMEM_STATIC
 
 config LARGE_ALLOCS
 	def_bool y
diff --git a/arch/sparc64/kernel/central.c b/arch/sparc64/kernel/central.c
index e724c54..c65b2f9 100644
--- a/arch/sparc64/kernel/central.c
+++ b/arch/sparc64/kernel/central.c
@@ -32,7 +32,7 @@
 static void central_ranges_init(struct linux_central *central)
 {
 	struct device_node *dp = central->prom_node;
-	void *pval;
+	const void *pval;
 	int len;
 	
 	central->num_central_ranges = 0;
@@ -47,7 +47,7 @@
 static void fhc_ranges_init(struct linux_fhc *fhc)
 {
 	struct device_node *dp = fhc->prom_node;
-	void *pval;
+	const void *pval;
 	int len;
 	
 	fhc->num_fhc_ranges = 0;
@@ -119,7 +119,7 @@
 static void probe_other_fhcs(void)
 {
 	struct device_node *dp;
-	struct linux_prom64_registers *fpregs;
+	const struct linux_prom64_registers *fpregs;
 
 	for_each_node_by_name(dp, "fhc") {
 		struct linux_fhc *fhc;
@@ -190,7 +190,8 @@
 			      struct device_node *fp)
 {
 	struct device_node *dp;
-	struct linux_prom_registers cregs[3], *pr;
+	struct linux_prom_registers cregs[3];
+	const struct linux_prom_registers *pr;
 	int nslots, tmp, nregs;
 
 	dp = fp->child;
@@ -299,7 +300,8 @@
 
 void central_probe(void)
 {
-	struct linux_prom_registers fpregs[6], *pr;
+	struct linux_prom_registers fpregs[6];
+	const struct linux_prom_registers *pr;
 	struct linux_fhc *fhc;
 	struct device_node *dp, *fp;
 	int err;
diff --git a/arch/sparc64/kernel/chmc.c b/arch/sparc64/kernel/chmc.c
index 9699abe..777d345 100644
--- a/arch/sparc64/kernel/chmc.c
+++ b/arch/sparc64/kernel/chmc.c
@@ -343,8 +343,8 @@
 {
 	struct mctrl_info *mp = kzalloc(sizeof(*mp), GFP_KERNEL);
 	int portid = of_getintprop_default(dp, "portid", -1);
-	struct linux_prom64_registers *regs;
-	void *pval;
+	const struct linux_prom64_registers *regs;
+	const void *pval;
 	int len;
 
 	if (!mp)
diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc64/kernel/ebus.c
index 35bf895..0ace17b 100644
--- a/arch/sparc64/kernel/ebus.c
+++ b/arch/sparc64/kernel/ebus.c
@@ -285,7 +285,7 @@
 				   int non_standard_regs)
 {
 	struct of_device *op;
-	int *regs;
+	const int *regs;
 	int i, len;
 
 	dev->prom_node = dp;
@@ -438,11 +438,9 @@
 
 void __init ebus_init(void)
 {
-	struct pci_pbm_info *pbm;
 	struct linux_ebus_device *dev;
 	struct linux_ebus *ebus;
 	struct pci_dev *pdev;
-	struct pcidev_cookie *cookie;
 	struct device_node *dp;
 	int is_rio;
 	int num_ebus = 0;
@@ -453,8 +451,7 @@
 		return;
 	}
 
-	cookie = pdev->sysdata;
-	dp = cookie->prom_node;
+	dp = pci_device_to_OF_node(pdev);
 
 	ebus_chain = ebus = ebus_alloc(sizeof(struct linux_ebus));
 	ebus->next = NULL;
@@ -480,8 +477,7 @@
 				break;
 			}
 			ebus->is_rio = is_rio;
-			cookie = pdev->sysdata;
-			dp = cookie->prom_node;
+			dp = pci_device_to_OF_node(pdev);
 			continue;
 		}
 		printk("ebus%d:", num_ebus);
@@ -489,7 +485,6 @@
 		ebus->index = num_ebus;
 		ebus->prom_node = dp;
 		ebus->self = pdev;
-		ebus->parent = pbm = cookie->pbm;
 
 		ebus->ofdev.node = dp;
 		ebus->ofdev.dev.parent = &pdev->dev;
@@ -531,8 +526,7 @@
 		if (!pdev)
 			break;
 
-		cookie = pdev->sysdata;
-		dp = cookie->prom_node;
+		dp = pci_device_to_OF_node(pdev);
 
 		ebus->next = ebus_alloc(sizeof(struct linux_ebus));
 		ebus = ebus->next;
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c
index c443db1..6241e3d 100644
--- a/arch/sparc64/kernel/irq.c
+++ b/arch/sparc64/kernel/irq.c
@@ -589,32 +589,6 @@
 	       ino, virt_irq);
 }
 
-#ifndef CONFIG_SMP
-extern irqreturn_t timer_interrupt(int, void *);
-
-void timer_irq(int irq, struct pt_regs *regs)
-{
-	unsigned long clr_mask = 1 << irq;
-	unsigned long tick_mask = tick_ops->softint_mask;
-	struct pt_regs *old_regs;
-
-	if (get_softint() & tick_mask) {
-		irq = 0;
-		clr_mask = tick_mask;
-	}
-	clear_softint(clr_mask);
-
-	old_regs = set_irq_regs(regs);
-	irq_enter();
-
-	kstat_this_cpu.irqs[0]++;
-	timer_interrupt(irq, NULL);
-
-	irq_exit();
-	set_irq_regs(old_regs);
-}
-#endif
-
 void handler_irq(int irq, struct pt_regs *regs)
 {
 	struct ino_bucket *bucket;
@@ -653,7 +627,7 @@
 static void map_prom_timers(void)
 {
 	struct device_node *dp;
-	unsigned int *addr;
+	const unsigned int *addr;
 
 	/* PROM timer node hangs out in the top level of device siblings... */
 	dp = of_find_node_by_path("/");
diff --git a/arch/sparc64/kernel/isa.c b/arch/sparc64/kernel/isa.c
index 98721a8..6a6882e 100644
--- a/arch/sparc64/kernel/isa.c
+++ b/arch/sparc64/kernel/isa.c
@@ -24,27 +24,9 @@
 
 static void __init isa_dev_get_resource(struct sparc_isa_device *isa_dev)
 {
-	struct linux_prom_registers *pregs;
-	unsigned long base, len;
-	int prop_len;
+	struct of_device *op = of_find_device_by_node(isa_dev->prom_node);
 
-	pregs = of_get_property(isa_dev->prom_node, "reg", &prop_len);
-	if (!pregs)
-		return;
-
-	/* Only the first one is interesting. */
-	len = pregs[0].reg_size;
-	base = (((unsigned long)pregs[0].which_io << 32) |
-		(unsigned long)pregs[0].phys_addr);
-	base += isa_dev->bus->parent->io_space.start;
-
-	isa_dev->resource.start = base;
-	isa_dev->resource.end   = (base + len - 1UL);
-	isa_dev->resource.flags = IORESOURCE_IO;
-	isa_dev->resource.name  = isa_dev->prom_node->name;
-
-	request_resource(&isa_dev->bus->parent->io_space,
-			 &isa_dev->resource);
+	memcpy(&isa_dev->resource, &op->resource[0], sizeof(struct resource));
 }
 
 static void __init isa_dev_get_irq(struct sparc_isa_device *isa_dev)
@@ -158,19 +140,10 @@
 
 	pdev = NULL;
 	while ((pdev = pci_get_device(vendor, device, pdev)) != NULL) {
-		struct pcidev_cookie *pdev_cookie;
-		struct pci_pbm_info *pbm;
 		struct sparc_isa_bridge *isa_br;
 		struct device_node *dp;
 
-		pdev_cookie = pdev->sysdata;
-		if (!pdev_cookie) {
-			printk("ISA: Warning, ISA bridge ignored due to "
-			       "lack of OBP data.\n");
-			continue;
-		}
-		pbm = pdev_cookie->pbm;
-		dp = pdev_cookie->prom_node;
+		dp = pci_device_to_OF_node(pdev);
 
 		isa_br = kzalloc(sizeof(*isa_br), GFP_KERNEL);
 		if (!isa_br) {
@@ -195,10 +168,9 @@
 		isa_br->next = isa_chain;
 		isa_chain = isa_br;
 
-		isa_br->parent = pbm;
 		isa_br->self = pdev;
 		isa_br->index = index++;
-		isa_br->prom_node = pdev_cookie->prom_node;
+		isa_br->prom_node = dp;
 
 		printk("isa%d:", isa_br->index);
 
diff --git a/arch/sparc64/kernel/of_device.c b/arch/sparc64/kernel/of_device.c
index fb9bf1e..9ac9a30 100644
--- a/arch/sparc64/kernel/of_device.c
+++ b/arch/sparc64/kernel/of_device.c
@@ -245,7 +245,7 @@
 				       int *addrc, int *sizec);
 	int		(*map)(u32 *addr, const u32 *range,
 			       int na, int ns, int pna);
-	unsigned int	(*get_flags)(u32 *addr);
+	unsigned int	(*get_flags)(const u32 *addr);
 };
 
 /*
@@ -305,7 +305,7 @@
 	return 0;
 }
 
-static unsigned int of_bus_default_get_flags(u32 *addr)
+static unsigned int of_bus_default_get_flags(const u32 *addr)
 {
 	return IORESOURCE_MEM;
 }
@@ -317,6 +317,11 @@
 static int of_bus_pci_match(struct device_node *np)
 {
 	if (!strcmp(np->type, "pci") || !strcmp(np->type, "pciex")) {
+		const char *model = of_get_property(np, "model", NULL);
+
+		if (model && !strcmp(model, "SUNW,simba"))
+			return 0;
+
 		/* Do not do PCI specific frobbing if the
 		 * PCI bridge lacks a ranges property.  We
 		 * want to pass it through up to the next
@@ -332,6 +337,21 @@
 	return 0;
 }
 
+static int of_bus_simba_match(struct device_node *np)
+{
+	const char *model = of_get_property(np, "model", NULL);
+
+	if (model && !strcmp(model, "SUNW,simba"))
+		return 1;
+	return 0;
+}
+
+static int of_bus_simba_map(u32 *addr, const u32 *range,
+			    int na, int ns, int pna)
+{
+	return 0;
+}
+
 static void of_bus_pci_count_cells(struct device_node *np,
 				   int *addrc, int *sizec)
 {
@@ -369,7 +389,7 @@
 	return 0;
 }
 
-static unsigned int of_bus_pci_get_flags(u32 *addr)
+static unsigned int of_bus_pci_get_flags(const u32 *addr)
 {
 	unsigned int flags = 0;
 	u32 w = addr[0];
@@ -436,6 +456,15 @@
 		.map = of_bus_pci_map,
 		.get_flags = of_bus_pci_get_flags,
 	},
+	/* SIMBA */
+	{
+		.name = "simba",
+		.addr_prop_name = "assigned-addresses",
+		.match = of_bus_simba_match,
+		.count_cells = of_bus_pci_count_cells,
+		.map = of_bus_simba_map,
+		.get_flags = of_bus_pci_get_flags,
+	},
 	/* SBUS */
 	{
 		.name = "sbus",
@@ -482,7 +511,7 @@
 				     u32 *addr,
 				     int na, int ns, int pna)
 {
-	u32 *ranges;
+	const u32 *ranges;
 	unsigned int rlen;
 	int rone;
 
@@ -513,7 +542,7 @@
 
 static int __init use_1to1_mapping(struct device_node *pp)
 {
-	char *model;
+	const char *model;
 
 	/* If this is on the PMU bus, don't try to translate it even
 	 * if a ranges property exists.
@@ -548,7 +577,7 @@
 	struct of_bus *bus;
 	int na, ns;
 	int index, num_reg;
-	void *preg;
+	const void *preg;
 
 	if (!parent)
 		return;
@@ -578,7 +607,7 @@
 	for (index = 0; index < num_reg; index++) {
 		struct resource *r = &op->resource[index];
 		u32 addr[OF_MAX_ADDR_CELLS];
-		u32 *reg = (preg + (index * ((na + ns) * 4)));
+		const u32 *reg = (preg + (index * ((na + ns) * 4)));
 		struct device_node *dp = op->node;
 		struct device_node *pp = p_op->node;
 		struct of_bus *pbus, *dbus;
@@ -643,14 +672,14 @@
 
 static struct device_node * __init
 apply_interrupt_map(struct device_node *dp, struct device_node *pp,
-		    u32 *imap, int imlen, u32 *imask,
+		    const u32 *imap, int imlen, const u32 *imask,
 		    unsigned int *irq_p)
 {
 	struct device_node *cp;
 	unsigned int irq = *irq_p;
 	struct of_bus *bus;
 	phandle handle;
-	u32 *reg;
+	const u32 *reg;
 	int na, num_reg, i;
 
 	bus = of_match_bus(pp);
@@ -705,7 +734,7 @@
 					   struct device_node *pp,
 					   unsigned int irq)
 {
-	struct linux_prom_pci_registers *regs;
+	const struct linux_prom_pci_registers *regs;
 	unsigned int bus, devfn, slot, ret;
 
 	if (irq < 1 || irq > 4)
@@ -730,12 +759,6 @@
 		 * D: 2-bit slot number, derived from PCI device number as
 		 *    (dev - 1) for bus A, or (dev - 2) for bus B
 		 * L: 2-bit line number
-		 *
-		 * Actually, more "portable" way to calculate the funky
-		 * slot number is to subtract pbm->pci_first_slot from the
-		 * device number, and that's exactly what the pre-OF
-		 * sparc64 code did, but we're building this stuff generically
-		 * using the OBP tree, not in the PCI controller layer.
 		 */
 		if (bus & 0x80) {
 			/* PBM-A */
@@ -794,7 +817,7 @@
 	pp = dp->parent;
 	ip = NULL;
 	while (pp) {
-		void *imap, *imsk;
+		const void *imap, *imsk;
 		int imlen;
 
 		imap = of_get_property(pp, "interrupt-map", &imlen);
@@ -859,7 +882,7 @@
 						 struct device *parent)
 {
 	struct of_device *op = kzalloc(sizeof(*op), GFP_KERNEL);
-	unsigned int *irq;
+	const unsigned int *irq;
 	int len, i;
 
 	if (!op)
diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c
index 1210988..023af41 100644
--- a/arch/sparc64/kernel/pci.c
+++ b/arch/sparc64/kernel/pci.c
@@ -1,9 +1,11 @@
-/* $Id: pci.c,v 1.39 2002/01/05 01:13:43 davem Exp $
- * pci.c: UltraSparc PCI controller support.
+/* pci.c: UltraSparc PCI controller support.
  *
  * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@redhat.com)
  * Copyright (C) 1998, 1999 Eddie C. Dost   (ecd@skynet.be)
  * Copyright (C) 1999 Jakub Jelinek   (jj@ultra.linux.cz)
+ *
+ * OF tree based PCI bus probing taken from the PowerPC port
+ * with minor modifications, see there for credits.
  */
 
 #include <linux/module.h>
@@ -24,6 +26,9 @@
 #include <asm/ebus.h>
 #include <asm/isa.h>
 #include <asm/prom.h>
+#include <asm/apb.h>
+
+#include "pci_impl.h"
 
 unsigned long pci_memspace_mask = 0xffffffffUL;
 
@@ -277,10 +282,10 @@
 	return pci_controller_scan(pci_is_controller);
 }
 
-struct pci_iommu_ops *pci_iommu_ops;
+const struct pci_iommu_ops *pci_iommu_ops;
 EXPORT_SYMBOL(pci_iommu_ops);
 
-extern struct pci_iommu_ops pci_sun4u_iommu_ops,
+extern const struct pci_iommu_ops pci_sun4u_iommu_ops,
 	pci_sun4v_iommu_ops;
 
 /* Find each controller in the system, attach and initialize
@@ -300,6 +305,467 @@
 	pci_controller_scan(pci_controller_init);
 }
 
+static unsigned long pci_parse_of_flags(u32 addr0)
+{
+	unsigned long flags = 0;
+
+	if (addr0 & 0x02000000) {
+		flags = IORESOURCE_MEM | PCI_BASE_ADDRESS_SPACE_MEMORY;
+		flags |= (addr0 >> 22) & PCI_BASE_ADDRESS_MEM_TYPE_64;
+		flags |= (addr0 >> 28) & PCI_BASE_ADDRESS_MEM_TYPE_1M;
+		if (addr0 & 0x40000000)
+			flags |= IORESOURCE_PREFETCH
+				 | PCI_BASE_ADDRESS_MEM_PREFETCH;
+	} else if (addr0 & 0x01000000)
+		flags = IORESOURCE_IO | PCI_BASE_ADDRESS_SPACE_IO;
+	return flags;
+}
+
+/* The of_device layer has translated all of the assigned-address properties
+ * into physical address resources, we only have to figure out the register
+ * mapping.
+ */
+static void pci_parse_of_addrs(struct of_device *op,
+			       struct device_node *node,
+			       struct pci_dev *dev)
+{
+	struct resource *op_res;
+	const u32 *addrs;
+	int proplen;
+
+	addrs = of_get_property(node, "assigned-addresses", &proplen);
+	if (!addrs)
+		return;
+	printk("    parse addresses (%d bytes) @ %p\n", proplen, addrs);
+	op_res = &op->resource[0];
+	for (; proplen >= 20; proplen -= 20, addrs += 5, op_res++) {
+		struct resource *res;
+		unsigned long flags;
+		int i;
+
+		flags = pci_parse_of_flags(addrs[0]);
+		if (!flags)
+			continue;
+		i = addrs[0] & 0xff;
+		printk("  start: %lx, end: %lx, i: %x\n",
+		       op_res->start, op_res->end, i);
+
+		if (PCI_BASE_ADDRESS_0 <= i && i <= PCI_BASE_ADDRESS_5) {
+			res = &dev->resource[(i - PCI_BASE_ADDRESS_0) >> 2];
+		} else if (i == dev->rom_base_reg) {
+			res = &dev->resource[PCI_ROM_RESOURCE];
+			flags |= IORESOURCE_READONLY | IORESOURCE_CACHEABLE;
+		} else {
+			printk(KERN_ERR "PCI: bad cfg reg num 0x%x\n", i);
+			continue;
+		}
+		res->start = op_res->start;
+		res->end = op_res->end;
+		res->flags = flags;
+		res->name = pci_name(dev);
+	}
+}
+
+struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
+				  struct device_node *node,
+				  struct pci_bus *bus, int devfn,
+				  int host_controller)
+{
+	struct dev_archdata *sd;
+	struct pci_dev *dev;
+	const char *type;
+	u32 class;
+
+	dev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL);
+	if (!dev)
+		return NULL;
+
+	sd = &dev->dev.archdata;
+	sd->iommu = pbm->iommu;
+	sd->stc = &pbm->stc;
+	sd->host_controller = pbm;
+	sd->prom_node = node;
+	sd->op = of_find_device_by_node(node);
+	sd->msi_num = 0xffffffff;
+
+	type = of_get_property(node, "device_type", NULL);
+	if (type == NULL)
+		type = "";
+
+	printk("    create device, devfn: %x, type: %s hostcontroller(%d)\n",
+	       devfn, type, host_controller);
+
+	dev->bus = bus;
+	dev->sysdata = node;
+	dev->dev.parent = bus->bridge;
+	dev->dev.bus = &pci_bus_type;
+	dev->devfn = devfn;
+	dev->multifunction = 0;		/* maybe a lie? */
+
+	if (host_controller) {
+		dev->vendor = 0x108e;
+		dev->device = 0x8000;
+		dev->subsystem_vendor = 0x0000;
+		dev->subsystem_device = 0x0000;
+		dev->cfg_size = 256;
+		dev->class = PCI_CLASS_BRIDGE_HOST << 8;
+		sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(bus),
+			0x00, PCI_SLOT(devfn), PCI_FUNC(devfn));
+	} else {
+		dev->vendor = of_getintprop_default(node, "vendor-id", 0xffff);
+		dev->device = of_getintprop_default(node, "device-id", 0xffff);
+		dev->subsystem_vendor =
+			of_getintprop_default(node, "subsystem-vendor-id", 0);
+		dev->subsystem_device =
+			of_getintprop_default(node, "subsystem-id", 0);
+
+		dev->cfg_size = pci_cfg_space_size(dev);
+
+		/* We can't actually use the firmware value, we have
+		 * to read what is in the register right now.  One
+		 * reason is that in the case of IDE interfaces the
+		 * firmware can sample the value before the the IDE
+		 * interface is programmed into native mode.
+		 */
+		pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
+		dev->class = class >> 8;
+
+		sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(bus),
+			dev->bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn));
+	}
+	printk("    class: 0x%x device name: %s\n",
+	       dev->class, pci_name(dev));
+
+	dev->current_state = 4;		/* unknown power state */
+	dev->error_state = pci_channel_io_normal;
+
+	if (host_controller) {
+		dev->hdr_type = PCI_HEADER_TYPE_BRIDGE;
+		dev->rom_base_reg = PCI_ROM_ADDRESS1;
+		dev->irq = PCI_IRQ_NONE;
+	} else {
+		if (!strcmp(type, "pci") || !strcmp(type, "pciex")) {
+			/* a PCI-PCI bridge */
+			dev->hdr_type = PCI_HEADER_TYPE_BRIDGE;
+			dev->rom_base_reg = PCI_ROM_ADDRESS1;
+		} else if (!strcmp(type, "cardbus")) {
+			dev->hdr_type = PCI_HEADER_TYPE_CARDBUS;
+		} else {
+			dev->hdr_type = PCI_HEADER_TYPE_NORMAL;
+			dev->rom_base_reg = PCI_ROM_ADDRESS;
+
+			dev->irq = sd->op->irqs[0];
+			if (dev->irq == 0xffffffff)
+				dev->irq = PCI_IRQ_NONE;
+		}
+	}
+	pci_parse_of_addrs(sd->op, node, dev);
+
+	printk("    adding to system ...\n");
+
+	pci_device_add(dev, bus);
+
+	return dev;
+}
+
+static void __init apb_calc_first_last(u8 map, u32 *first_p, u32 *last_p)
+{
+	u32 idx, first, last;
+
+	first = 8;
+	last = 0;
+	for (idx = 0; idx < 8; idx++) {
+		if ((map & (1 << idx)) != 0) {
+			if (first > idx)
+				first = idx;
+			if (last < idx)
+				last = idx;
+		}
+	}
+
+	*first_p = first;
+	*last_p = last;
+}
+
+static void __init pci_resource_adjust(struct resource *res,
+				       struct resource *root)
+{
+	res->start += root->start;
+	res->end += root->start;
+}
+
+/* Cook up fake bus resources for SUNW,simba PCI bridges which lack
+ * a proper 'ranges' property.
+ */
+static void __init apb_fake_ranges(struct pci_dev *dev,
+				   struct pci_bus *bus,
+				   struct pci_pbm_info *pbm)
+{
+	struct resource *res;
+	u32 first, last;
+	u8 map;
+
+	pci_read_config_byte(dev, APB_IO_ADDRESS_MAP, &map);
+	apb_calc_first_last(map, &first, &last);
+	res = bus->resource[0];
+	res->start = (first << 21);
+	res->end = (last << 21) + ((1 << 21) - 1);
+	res->flags = IORESOURCE_IO;
+	pci_resource_adjust(res, &pbm->io_space);
+
+	pci_read_config_byte(dev, APB_MEM_ADDRESS_MAP, &map);
+	apb_calc_first_last(map, &first, &last);
+	res = bus->resource[1];
+	res->start = (first << 21);
+	res->end = (last << 21) + ((1 << 21) - 1);
+	res->flags = IORESOURCE_MEM;
+	pci_resource_adjust(res, &pbm->mem_space);
+}
+
+static void __init pci_of_scan_bus(struct pci_pbm_info *pbm,
+				   struct device_node *node,
+				   struct pci_bus *bus);
+
+#define GET_64BIT(prop, i)	((((u64) (prop)[(i)]) << 32) | (prop)[(i)+1])
+
+void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm,
+				  struct device_node *node,
+				  struct pci_dev *dev)
+{
+	struct pci_bus *bus;
+	const u32 *busrange, *ranges;
+	int len, i, simba;
+	struct resource *res;
+	unsigned int flags;
+	u64 size;
+
+	printk("of_scan_pci_bridge(%s)\n", node->full_name);
+
+	/* parse bus-range property */
+	busrange = of_get_property(node, "bus-range", &len);
+	if (busrange == NULL || len != 8) {
+		printk(KERN_DEBUG "Can't get bus-range for PCI-PCI bridge %s\n",
+		       node->full_name);
+		return;
+	}
+	ranges = of_get_property(node, "ranges", &len);
+	simba = 0;
+	if (ranges == NULL) {
+		const char *model = of_get_property(node, "model", NULL);
+		if (model && !strcmp(model, "SUNW,simba")) {
+			simba = 1;
+		} else {
+			printk(KERN_DEBUG "Can't get ranges for PCI-PCI bridge %s\n",
+			       node->full_name);
+			return;
+		}
+	}
+
+	bus = pci_add_new_bus(dev->bus, dev, busrange[0]);
+	if (!bus) {
+		printk(KERN_ERR "Failed to create pci bus for %s\n",
+		       node->full_name);
+		return;
+	}
+
+	bus->primary = dev->bus->number;
+	bus->subordinate = busrange[1];
+	bus->bridge_ctl = 0;
+
+	/* parse ranges property, or cook one up by hand for Simba */
+	/* PCI #address-cells == 3 and #size-cells == 2 always */
+	res = &dev->resource[PCI_BRIDGE_RESOURCES];
+	for (i = 0; i < PCI_NUM_RESOURCES - PCI_BRIDGE_RESOURCES; ++i) {
+		res->flags = 0;
+		bus->resource[i] = res;
+		++res;
+	}
+	if (simba) {
+		apb_fake_ranges(dev, bus, pbm);
+		goto simba_cont;
+	}
+	i = 1;
+	for (; len >= 32; len -= 32, ranges += 8) {
+		struct resource *root;
+
+		flags = pci_parse_of_flags(ranges[0]);
+		size = GET_64BIT(ranges, 6);
+		if (flags == 0 || size == 0)
+			continue;
+		if (flags & IORESOURCE_IO) {
+			res = bus->resource[0];
+			if (res->flags) {
+				printk(KERN_ERR "PCI: ignoring extra I/O range"
+				       " for bridge %s\n", node->full_name);
+				continue;
+			}
+			root = &pbm->io_space;
+		} else {
+			if (i >= PCI_NUM_RESOURCES - PCI_BRIDGE_RESOURCES) {
+				printk(KERN_ERR "PCI: too many memory ranges"
+				       " for bridge %s\n", node->full_name);
+				continue;
+			}
+			res = bus->resource[i];
+			++i;
+			root = &pbm->mem_space;
+		}
+
+		res->start = GET_64BIT(ranges, 1);
+		res->end = res->start + size - 1;
+		res->flags = flags;
+
+		/* Another way to implement this would be to add an of_device
+		 * layer routine that can calculate a resource for a given
+		 * range property value in a PCI device.
+		 */
+		pci_resource_adjust(res, root);
+	}
+simba_cont:
+	sprintf(bus->name, "PCI Bus %04x:%02x", pci_domain_nr(bus),
+		bus->number);
+	printk("    bus name: %s\n", bus->name);
+
+	pci_of_scan_bus(pbm, node, bus);
+}
+
+static void __init pci_of_scan_bus(struct pci_pbm_info *pbm,
+				   struct device_node *node,
+				   struct pci_bus *bus)
+{
+	struct device_node *child;
+	const u32 *reg;
+	int reglen, devfn;
+	struct pci_dev *dev;
+
+	printk("PCI: scan_bus[%s] bus no %d\n",
+	       node->full_name, bus->number);
+
+	child = NULL;
+	while ((child = of_get_next_child(node, child)) != NULL) {
+		printk("  * %s\n", child->full_name);
+		reg = of_get_property(child, "reg", &reglen);
+		if (reg == NULL || reglen < 20)
+			continue;
+		devfn = (reg[0] >> 8) & 0xff;
+
+		/* create a new pci_dev for this device */
+		dev = of_create_pci_dev(pbm, child, bus, devfn, 0);
+		if (!dev)
+			continue;
+		printk("PCI: dev header type: %x\n", dev->hdr_type);
+
+		if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
+		    dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
+			of_scan_pci_bridge(pbm, child, dev);
+	}
+}
+
+static ssize_t
+show_pciobppath_attr(struct device * dev, struct device_attribute * attr, char * buf)
+{
+	struct pci_dev *pdev;
+	struct device_node *dp;
+
+	pdev = to_pci_dev(dev);
+	dp = pdev->dev.archdata.prom_node;
+
+	return snprintf (buf, PAGE_SIZE, "%s\n", dp->full_name);
+}
+
+static DEVICE_ATTR(obppath, S_IRUSR | S_IRGRP | S_IROTH, show_pciobppath_attr, NULL);
+
+static void __devinit pci_bus_register_of_sysfs(struct pci_bus *bus)
+{
+	struct pci_dev *dev;
+	struct pci_bus *child_bus;
+	int err;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		/* we don't really care if we can create this file or
+		 * not, but we need to assign the result of the call
+		 * or the world will fall under alien invasion and
+		 * everybody will be frozen on a spaceship ready to be
+		 * eaten on alpha centauri by some green and jelly
+		 * humanoid.
+		 */
+		err = sysfs_create_file(&dev->dev.kobj, &dev_attr_obppath.attr);
+	}
+	list_for_each_entry(child_bus, &bus->children, node)
+		pci_bus_register_of_sysfs(child_bus);
+}
+
+int pci_host_bridge_read_pci_cfg(struct pci_bus *bus_dev,
+				 unsigned int devfn,
+				 int where, int size,
+				 u32 *value)
+{
+	static u8 fake_pci_config[] = {
+		0x8e, 0x10, /* Vendor: 0x108e (Sun) */
+		0x00, 0x80, /* Device: 0x8000 (PBM) */
+		0x46, 0x01, /* Command: 0x0146 (SERR, PARITY, MASTER, MEM) */
+		0xa0, 0x22, /* Status: 0x02a0 (DEVSEL_MED, FB2B, 66MHZ) */
+		0x00, 0x00, 0x00, 0x06, /* Class: 0x06000000 host bridge */
+		0x00, /* Cacheline: 0x00 */
+		0x40, /* Latency: 0x40 */
+		0x00, /* Header-Type: 0x00 normal */
+	};
+
+	*value = 0;
+	if (where >= 0 && where < sizeof(fake_pci_config) &&
+	    (where + size) >= 0 &&
+	    (where + size) < sizeof(fake_pci_config) &&
+	    size <= sizeof(u32)) {
+		while (size--) {
+			*value <<= 8;
+			*value |= fake_pci_config[where + size];
+		}
+	}
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int pci_host_bridge_write_pci_cfg(struct pci_bus *bus_dev,
+				  unsigned int devfn,
+				  int where, int size,
+				  u32 value)
+{
+	return PCIBIOS_SUCCESSFUL;
+}
+
+struct pci_bus * __init pci_scan_one_pbm(struct pci_pbm_info *pbm)
+{
+	struct pci_controller_info *p = pbm->parent;
+	struct device_node *node = pbm->prom_node;
+	struct pci_dev *host_pdev;
+	struct pci_bus *bus;
+
+	printk("PCI: Scanning PBM %s\n", node->full_name);
+
+	/* XXX parent device? XXX */
+	bus = pci_create_bus(NULL, pbm->pci_first_busno, p->pci_ops, pbm);
+	if (!bus) {
+		printk(KERN_ERR "Failed to create bus for %s\n",
+		       node->full_name);
+		return NULL;
+	}
+	bus->secondary = pbm->pci_first_busno;
+	bus->subordinate = pbm->pci_last_busno;
+
+	bus->resource[0] = &pbm->io_space;
+	bus->resource[1] = &pbm->mem_space;
+
+	/* Create the dummy host bridge and link it in.  */
+	host_pdev = of_create_pci_dev(pbm, node, bus, 0x00, 1);
+	bus->self = host_pdev;
+
+	pci_of_scan_bus(pbm, node, bus);
+	pci_bus_add_devices(bus);
+	pci_bus_register_of_sysfs(bus);
+
+	return bus;
+}
+
 static void __init pci_scan_each_controller_bus(void)
 {
 	struct pci_controller_info *p;
@@ -360,8 +826,33 @@
 {
 }
 
-int pcibios_enable_device(struct pci_dev *pdev, int mask)
+int pcibios_enable_device(struct pci_dev *dev, int mask)
 {
+	u16 cmd, oldcmd;
+	int i;
+
+	pci_read_config_word(dev, PCI_COMMAND, &cmd);
+	oldcmd = cmd;
+
+	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+		struct resource *res = &dev->resource[i];
+
+		/* Only set up the requested stuff */
+		if (!(mask & (1<<i)))
+			continue;
+
+		if (res->flags & IORESOURCE_IO)
+			cmd |= PCI_COMMAND_IO;
+		if (res->flags & IORESOURCE_MEM)
+			cmd |= PCI_COMMAND_MEMORY;
+	}
+
+	if (cmd != oldcmd) {
+		printk(KERN_DEBUG "PCI: Enabling device: (%s), cmd %x\n",
+		       pci_name(dev), cmd);
+                /* Enable the appropriate bits in the PCI command register.  */
+		pci_write_config_word(dev, PCI_COMMAND, cmd);
+	}
 	return 0;
 }
 
@@ -380,7 +871,7 @@
 	else
 		root = &pbm->mem_space;
 
-	pbm->parent->resource_adjust(pdev, &zero_res, root);
+	pci_resource_adjust(&zero_res, root);
 
 	region->start = res->start - zero_res.start;
 	region->end = res->end - zero_res.start;
@@ -401,7 +892,7 @@
 	else
 		root = &pbm->mem_space;
 
-	pbm->parent->resource_adjust(pdev, res, root);
+	pci_resource_adjust(res, root);
 }
 EXPORT_SYMBOL(pcibios_bus_to_resource);
 
@@ -422,55 +913,17 @@
 static int __pci_mmap_make_offset_bus(struct pci_dev *pdev, struct vm_area_struct *vma,
 				      enum pci_mmap_state mmap_state)
 {
-	struct pcidev_cookie *pcp = pdev->sysdata;
-	struct pci_pbm_info *pbm;
+	struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
 	struct pci_controller_info *p;
 	unsigned long space_size, user_offset, user_size;
 
-	if (!pcp)
-		return -ENXIO;
-	pbm = pcp->pbm;
-	if (!pbm)
-		return -ENXIO;
-
 	p = pbm->parent;
-	if (p->pbms_same_domain) {
-		unsigned long lowest, highest;
-
-		lowest = ~0UL; highest = 0UL;
-		if (mmap_state == pci_mmap_io) {
-			if (p->pbm_A.io_space.flags) {
-				lowest = p->pbm_A.io_space.start;
-				highest = p->pbm_A.io_space.end + 1;
-			}
-			if (p->pbm_B.io_space.flags) {
-				if (lowest > p->pbm_B.io_space.start)
-					lowest = p->pbm_B.io_space.start;
-				if (highest < p->pbm_B.io_space.end + 1)
-					highest = p->pbm_B.io_space.end + 1;
-			}
-			space_size = highest - lowest;
-		} else {
-			if (p->pbm_A.mem_space.flags) {
-				lowest = p->pbm_A.mem_space.start;
-				highest = p->pbm_A.mem_space.end + 1;
-			}
-			if (p->pbm_B.mem_space.flags) {
-				if (lowest > p->pbm_B.mem_space.start)
-					lowest = p->pbm_B.mem_space.start;
-				if (highest < p->pbm_B.mem_space.end + 1)
-					highest = p->pbm_B.mem_space.end + 1;
-			}
-			space_size = highest - lowest;
-		}
+	if (mmap_state == pci_mmap_io) {
+		space_size = (pbm->io_space.end -
+			      pbm->io_space.start) + 1;
 	} else {
-		if (mmap_state == pci_mmap_io) {
-			space_size = (pbm->io_space.end -
-				      pbm->io_space.start) + 1;
-		} else {
-			space_size = (pbm->mem_space.end -
-				      pbm->mem_space.start) + 1;
-		}
+		space_size = (pbm->mem_space.end -
+			      pbm->mem_space.start) + 1;
 	}
 
 	/* Make sure the request is in range. */
@@ -481,31 +934,12 @@
 	    (user_offset + user_size) > space_size)
 		return -EINVAL;
 
-	if (p->pbms_same_domain) {
-		unsigned long lowest = ~0UL;
-
-		if (mmap_state == pci_mmap_io) {
-			if (p->pbm_A.io_space.flags)
-				lowest = p->pbm_A.io_space.start;
-			if (p->pbm_B.io_space.flags &&
-			    lowest > p->pbm_B.io_space.start)
-				lowest = p->pbm_B.io_space.start;
-		} else {
-			if (p->pbm_A.mem_space.flags)
-				lowest = p->pbm_A.mem_space.start;
-			if (p->pbm_B.mem_space.flags &&
-			    lowest > p->pbm_B.mem_space.start)
-				lowest = p->pbm_B.mem_space.start;
-		}
-		vma->vm_pgoff = (lowest + user_offset) >> PAGE_SHIFT;
+	if (mmap_state == pci_mmap_io) {
+		vma->vm_pgoff = (pbm->io_space.start +
+				 user_offset) >> PAGE_SHIFT;
 	} else {
-		if (mmap_state == pci_mmap_io) {
-			vma->vm_pgoff = (pbm->io_space.start +
-					 user_offset) >> PAGE_SHIFT;
-		} else {
-			vma->vm_pgoff = (pbm->mem_space.start +
-					 user_offset) >> PAGE_SHIFT;
-		}
+		vma->vm_pgoff = (pbm->mem_space.start +
+				 user_offset) >> PAGE_SHIFT;
 	}
 
 	return 0;
@@ -639,9 +1073,8 @@
 		struct pci_controller_info *p = pbm->parent;
 
 		ret = p->index;
-		if (p->pbms_same_domain == 0)
-			ret = ((ret << 1) +
-			       ((pbm == &pbm->parent->pbm_B) ? 1 : 0));
+		ret = ((ret << 1) +
+		       ((pbm == &pbm->parent->pbm_B) ? 1 : 0));
 	}
 
 	return ret;
@@ -651,8 +1084,7 @@
 #ifdef CONFIG_PCI_MSI
 int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
 {
-	struct pcidev_cookie *pcp = pdev->sysdata;
-	struct pci_pbm_info *pbm = pcp->pbm;
+	struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
 	struct pci_controller_info *p = pbm->parent;
 	int virt_irq, err;
 
@@ -670,8 +1102,7 @@
 {
 	struct msi_desc *entry = get_irq_msi(virt_irq);
 	struct pci_dev *pdev = entry->dev;
-	struct pcidev_cookie *pcp = pdev->sysdata;
-	struct pci_pbm_info *pbm = pcp->pbm;
+	struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
 	struct pci_controller_info *p = pbm->parent;
 
 	if (!pbm->msi_num || !p->setup_msi_irq)
@@ -683,9 +1114,7 @@
 
 struct device_node *pci_device_to_OF_node(struct pci_dev *pdev)
 {
-	struct pcidev_cookie *pc = pdev->sysdata;
-
-	return pc->op->node;
+	return pdev->dev.archdata.prom_node;
 }
 EXPORT_SYMBOL(pci_device_to_OF_node);
 
diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c
index 5a92cb9..1e6aeed 100644
--- a/arch/sparc64/kernel/pci_common.c
+++ b/arch/sparc64/kernel/pci_common.c
@@ -1,7 +1,6 @@
-/* $Id: pci_common.c,v 1.29 2002/02/01 00:56:03 davem Exp $
- * pci_common.c: PCI controller common support.
+/* pci_common.c: PCI controller common support.
  *
- * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1999, 2007 David S. Miller (davem@davemloft.net)
  */
 
 #include <linux/string.h>
@@ -16,715 +15,8 @@
 
 #include "pci_impl.h"
 
-/* Fix self device of BUS and hook it into BUS->self.
- * The pci_scan_bus does not do this for the host bridge.
- */
-void __init pci_fixup_host_bridge_self(struct pci_bus *pbus)
-{
-	struct pci_dev *pdev;
-
-	list_for_each_entry(pdev, &pbus->devices, bus_list) {
-		if (pdev->class >> 8 == PCI_CLASS_BRIDGE_HOST) {
-			pbus->self = pdev;
-			return;
-		}
-	}
-
-	prom_printf("PCI: Critical error, cannot find host bridge PDEV.\n");
-	prom_halt();
-}
-
-/* Find the OBP PROM device tree node for a PCI device.  */
-static struct device_node * __init
-find_device_prom_node(struct pci_pbm_info *pbm, struct pci_dev *pdev,
-		      struct device_node *bus_node,
-		      struct linux_prom_pci_registers **pregs,
-		      int *nregs)
-{
-	struct device_node *dp;
-
-	*nregs = 0;
-
-	/*
-	 * Return the PBM's PROM node in case we are it's PCI device,
-	 * as the PBM's reg property is different to standard PCI reg
-	 * properties. We would delete this device entry otherwise,
-	 * which confuses XFree86's device probing...
-	 */
-	if ((pdev->bus->number == pbm->pci_bus->number) && (pdev->devfn == 0) &&
-	    (pdev->vendor == PCI_VENDOR_ID_SUN) &&
-	    (pdev->device == PCI_DEVICE_ID_SUN_PBM ||
-	     pdev->device == PCI_DEVICE_ID_SUN_SCHIZO ||
-	     pdev->device == PCI_DEVICE_ID_SUN_TOMATILLO ||
-	     pdev->device == PCI_DEVICE_ID_SUN_SABRE ||
-	     pdev->device == PCI_DEVICE_ID_SUN_HUMMINGBIRD))
-		return bus_node;
-
-	dp = bus_node->child;
-	while (dp) {
-		struct linux_prom_pci_registers *regs;
-		struct property *prop;
-		int len;
-
-		prop = of_find_property(dp, "reg", &len);
-		if (!prop)
-			goto do_next_sibling;
-
-		regs = prop->value;
-		if (((regs[0].phys_hi >> 8) & 0xff) == pdev->devfn) {
-			*pregs = regs;
-			*nregs = len / sizeof(struct linux_prom_pci_registers);
-			return dp;
-		}
-
-	do_next_sibling:
-		dp = dp->sibling;
-	}
-
-	return NULL;
-}
-
-/* Older versions of OBP on PCI systems encode 64-bit MEM
- * space assignments incorrectly, this fixes them up.  We also
- * take the opportunity here to hide other kinds of bogus
- * assignments.
- */
-static void __init fixup_obp_assignments(struct pci_dev *pdev,
-					 struct pcidev_cookie *pcp)
-{
-	int i;
-
-	if (pdev->vendor == PCI_VENDOR_ID_AL &&
-	    (pdev->device == PCI_DEVICE_ID_AL_M7101 ||
-	     pdev->device == PCI_DEVICE_ID_AL_M1533)) {
-		int i;
-
-		/* Zap all of the normal resources, they are
-		 * meaningless and generate bogus resource collision
-		 * messages.  This is OpenBoot's ill-fated attempt to
-		 * represent the implicit resources that these devices
-		 * have.
-		 */
-		pcp->num_prom_assignments = 0;
-		for (i = 0; i < 6; i++) {
-			pdev->resource[i].start =
-				pdev->resource[i].end =
-				pdev->resource[i].flags = 0;
-		}
-		pdev->resource[PCI_ROM_RESOURCE].start =
-			pdev->resource[PCI_ROM_RESOURCE].end =
-			pdev->resource[PCI_ROM_RESOURCE].flags = 0;
-		return;
-	}
-
-	for (i = 0; i < pcp->num_prom_assignments; i++) {
-		struct linux_prom_pci_registers *ap;
-		int space;
-
-		ap = &pcp->prom_assignments[i];
-		space = ap->phys_hi >> 24;
-		if ((space & 0x3) == 2 &&
-		    (space & 0x4) != 0) {
-			ap->phys_hi &= ~(0x7 << 24);
-			ap->phys_hi |= 0x3 << 24;
-		}
-	}
-}
-
-static ssize_t
-show_pciobppath_attr(struct device * dev, struct device_attribute * attr, char * buf)
-{
-	struct pci_dev *pdev;
-	struct pcidev_cookie *sysdata;
-
-	pdev = to_pci_dev(dev);
-	sysdata = pdev->sysdata;
-
-	return snprintf (buf, PAGE_SIZE, "%s\n", sysdata->prom_node->full_name);
-}
-
-static DEVICE_ATTR(obppath, S_IRUSR | S_IRGRP | S_IROTH, show_pciobppath_attr, NULL);
-
-/* Fill in the PCI device cookie sysdata for the given
- * PCI device.  This cookie is the means by which one
- * can get to OBP and PCI controller specific information
- * for a PCI device.
- */
-static void __init pdev_cookie_fillin(struct pci_pbm_info *pbm,
-				      struct pci_dev *pdev,
-				      struct device_node *bus_node)
-{
-	struct linux_prom_pci_registers *pregs = NULL;
-	struct pcidev_cookie *pcp;
-	struct device_node *dp;
-	struct property *prop;
-	int nregs, len, err;
-
-	dp = find_device_prom_node(pbm, pdev, bus_node,
-				   &pregs, &nregs);
-	if (!dp) {
-		/* If it is not in the OBP device tree then
-		 * there must be a damn good reason for it.
-		 *
-		 * So what we do is delete the device from the
-		 * PCI device tree completely.  This scenario
-		 * is seen, for example, on CP1500 for the
-		 * second EBUS/HappyMeal pair if the external
-		 * connector for it is not present.
-		 */
-		pci_remove_bus_device(pdev);
-		return;
-	}
-
-	pcp = kzalloc(sizeof(*pcp), GFP_ATOMIC);
-	if (pcp == NULL) {
-		prom_printf("PCI_COOKIE: Fatal malloc error, aborting...\n");
-		prom_halt();
-	}
-	pcp->pbm = pbm;
-	pcp->prom_node = dp;
-	pcp->op = of_find_device_by_node(dp);
-	memcpy(pcp->prom_regs, pregs,
-	       nregs * sizeof(struct linux_prom_pci_registers));
-	pcp->num_prom_regs = nregs;
-
-	/* We can't have the pcidev_cookie assignments be just
-	 * direct pointers into the property value, since they
-	 * are potentially modified by the probing process.
-	 */
-	prop = of_find_property(dp, "assigned-addresses", &len);
-	if (!prop) {
-		pcp->num_prom_assignments = 0;
-	} else {
-		memcpy(pcp->prom_assignments, prop->value, len);
-		pcp->num_prom_assignments =
-			(len / sizeof(pcp->prom_assignments[0]));
-	}
-
-	if (strcmp(dp->name, "ebus") == 0) {
-		struct linux_prom_ebus_ranges *erng;
-		int iter;
-
-		/* EBUS is special... */
-		prop = of_find_property(dp, "ranges", &len);
-		if (!prop) {
-			prom_printf("EBUS: Fatal error, no range property\n");
-			prom_halt();
-		}
-		erng = prop->value;
-		len = (len / sizeof(erng[0]));
-		for (iter = 0; iter < len; iter++) {
-			struct linux_prom_ebus_ranges *ep = &erng[iter];
-			struct linux_prom_pci_registers *ap;
-
-			ap = &pcp->prom_assignments[iter];
-
-			ap->phys_hi = ep->parent_phys_hi;
-			ap->phys_mid = ep->parent_phys_mid;
-			ap->phys_lo = ep->parent_phys_lo;
-			ap->size_hi = 0;
-			ap->size_lo = ep->size;
-		}
-		pcp->num_prom_assignments = len;
-	}
-
-	fixup_obp_assignments(pdev, pcp);
-
-	pdev->sysdata = pcp;
-
-	/* we don't really care if we can create this file or not,
-	 * but we need to assign the result of the call or the world will fall
-	 * under alien invasion and everybody will be frozen on a spaceship
-	 * ready to be eaten on alpha centauri by some green and jelly humanoid.
-	 */
-	err = sysfs_create_file(&pdev->dev.kobj, &dev_attr_obppath.attr);
-}
-
-void __init pci_fill_in_pbm_cookies(struct pci_bus *pbus,
-				    struct pci_pbm_info *pbm,
-				    struct device_node *dp)
-{
-	struct pci_dev *pdev, *pdev_next;
-	struct pci_bus *this_pbus, *pbus_next;
-
-	/* This must be _safe because the cookie fillin
-	   routine can delete devices from the tree.  */
-	list_for_each_entry_safe(pdev, pdev_next, &pbus->devices, bus_list)
-		pdev_cookie_fillin(pbm, pdev, dp);
-
-	list_for_each_entry_safe(this_pbus, pbus_next, &pbus->children, node) {
-		struct pcidev_cookie *pcp = this_pbus->self->sysdata;
-
-		pci_fill_in_pbm_cookies(this_pbus, pbm, pcp->prom_node);
-	}
-}
-
-static void __init bad_assignment(struct pci_dev *pdev,
-				  struct linux_prom_pci_registers *ap,
-				  struct resource *res,
-				  int do_prom_halt)
-{
-	prom_printf("PCI: Bogus PROM assignment. BUS[%02x] DEVFN[%x]\n",
-		    pdev->bus->number, pdev->devfn);
-	if (ap)
-		prom_printf("PCI: phys[%08x:%08x:%08x] size[%08x:%08x]\n",
-			    ap->phys_hi, ap->phys_mid, ap->phys_lo,
-			    ap->size_hi, ap->size_lo);
-	if (res)
-		prom_printf("PCI: RES[%016lx-->%016lx:(%lx)]\n",
-			    res->start, res->end, res->flags);
-	if (do_prom_halt)
-		prom_halt();
-}
-
-static struct resource *
-__init get_root_resource(struct linux_prom_pci_registers *ap,
-			 struct pci_pbm_info *pbm)
-{
-	int space = (ap->phys_hi >> 24) & 3;
-
-	switch (space) {
-	case 0:
-		/* Configuration space, silently ignore it. */
-		return NULL;
-
-	case 1:
-		/* 16-bit IO space */
-		return &pbm->io_space;
-
-	case 2:
-		/* 32-bit MEM space */
-		return &pbm->mem_space;
-
-	case 3:
-		/* 64-bit MEM space, these are allocated out of
-		 * the 32-bit mem_space range for the PBM, ie.
-		 * we just zero out the upper 32-bits.
-		 */
-		return &pbm->mem_space;
-
-	default:
-		printk("PCI: What is resource space %x?\n", space);
-		return NULL;
-	};
-}
-
-static struct resource *
-__init get_device_resource(struct linux_prom_pci_registers *ap,
-			   struct pci_dev *pdev)
-{
-	struct resource *res;
-	int breg = (ap->phys_hi & 0xff);
-
-	switch (breg) {
-	case  PCI_ROM_ADDRESS:
-		/* Unfortunately I have seen several cases where
-		 * buggy FCODE uses a space value of '1' (I/O space)
-		 * in the register property for the ROM address
-		 * so disable this sanity check for now.
-		 */
-#if 0
-	{
-		int space = (ap->phys_hi >> 24) & 3;
-
-		/* It had better be MEM space. */
-		if (space != 2)
-			bad_assignment(pdev, ap, NULL, 0);
-	}
-#endif
-		res = &pdev->resource[PCI_ROM_RESOURCE];
-		break;
-
-	case PCI_BASE_ADDRESS_0:
-	case PCI_BASE_ADDRESS_1:
-	case PCI_BASE_ADDRESS_2:
-	case PCI_BASE_ADDRESS_3:
-	case PCI_BASE_ADDRESS_4:
-	case PCI_BASE_ADDRESS_5:
-		res = &pdev->resource[(breg - PCI_BASE_ADDRESS_0) / 4];
-		break;
-
-	default:
-		bad_assignment(pdev, ap, NULL, 0);
-		res = NULL;
-		break;
-	};
-
-	return res;
-}
-
-static void __init pdev_record_assignments(struct pci_pbm_info *pbm,
-					   struct pci_dev *pdev)
-{
-	struct pcidev_cookie *pcp = pdev->sysdata;
-	int i;
-
-	for (i = 0; i < pcp->num_prom_assignments; i++) {
-		struct linux_prom_pci_registers *ap;
-		struct resource *root, *res;
-
-		/* The format of this property is specified in
-		 * the PCI Bus Binding to IEEE1275-1994.
-		 */
-		ap = &pcp->prom_assignments[i];
-		root = get_root_resource(ap, pbm);
-		res = get_device_resource(ap, pdev);
-		if (root == NULL || res == NULL ||
-		    res->flags == 0)
-			continue;
-
-		/* Ok we know which resource this PROM assignment is
-		 * for, sanity check it.
-		 */
-		if ((res->start & 0xffffffffUL) != ap->phys_lo)
-			bad_assignment(pdev, ap, res, 1);
-
-		/* If it is a 64-bit MEM space assignment, verify that
-		 * the resource is too and that the upper 32-bits match.
-		 */
-		if (((ap->phys_hi >> 24) & 3) == 3) {
-			if (((res->flags & IORESOURCE_MEM) == 0) ||
-			    ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK)
-			     != PCI_BASE_ADDRESS_MEM_TYPE_64))
-				bad_assignment(pdev, ap, res, 1);
-			if ((res->start >> 32) != ap->phys_mid)
-				bad_assignment(pdev, ap, res, 1);
-
-			/* PBM cannot generate cpu initiated PIOs
-			 * to the full 64-bit space.  Therefore the
-			 * upper 32-bits better be zero.  If it is
-			 * not, just skip it and we will assign it
-			 * properly ourselves.
-			 */
-			if ((res->start >> 32) != 0UL) {
-				printk(KERN_ERR "PCI: OBP assigns out of range MEM address "
-				       "%016lx for region %ld on device %s\n",
-				       res->start, (res - &pdev->resource[0]), pci_name(pdev));
-				continue;
-			}
-		}
-
-		/* Adjust the resource into the physical address space
-		 * of this PBM.
-		 */
-		pbm->parent->resource_adjust(pdev, res, root);
-
-		if (request_resource(root, res) < 0) {
-			int rnum;
-
-			/* OK, there is some conflict.  But this is fine
-			 * since we'll reassign it in the fixup pass.
-			 *
-			 * Do not print the warning for ROM resources
-			 * as such a conflict is quite common and
-			 * harmless as the ROM bar is disabled.
-			 */
-			rnum = (res - &pdev->resource[0]);
-			if (rnum != PCI_ROM_RESOURCE)
-				printk(KERN_ERR "PCI: Resource collision, "
-				       "region %d "
-				       "[%016lx:%016lx] of device %s\n",
-				       rnum,
-				       res->start, res->end,
-				       pci_name(pdev));
-		}
-	}
-}
-
-void __init pci_record_assignments(struct pci_pbm_info *pbm,
-				   struct pci_bus *pbus)
-{
-	struct pci_dev *dev;
-	struct pci_bus *bus;
-
-	list_for_each_entry(dev, &pbus->devices, bus_list)
-		pdev_record_assignments(pbm, dev);
-
-	list_for_each_entry(bus, &pbus->children, node)
-		pci_record_assignments(pbm, bus);
-}
-
-/* Return non-zero if PDEV has implicit I/O resources even
- * though it may not have an I/O base address register
- * active.
- */
-static int __init has_implicit_io(struct pci_dev *pdev)
-{
-	int class = pdev->class >> 8;
-
-	if (class == PCI_CLASS_NOT_DEFINED ||
-	    class == PCI_CLASS_NOT_DEFINED_VGA ||
-	    class == PCI_CLASS_STORAGE_IDE ||
-	    (pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY)
-		return 1;
-
-	return 0;
-}
-
-static void __init pdev_assign_unassigned(struct pci_pbm_info *pbm,
-					  struct pci_dev *pdev)
-{
-	u32 reg;
-	u16 cmd;
-	int i, io_seen, mem_seen;
-
-	io_seen = mem_seen = 0;
-	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
-		struct resource *root, *res;
-		unsigned long size, min, max, align;
-
-		res = &pdev->resource[i];
-
-		if (res->flags & IORESOURCE_IO)
-			io_seen++;
-		else if (res->flags & IORESOURCE_MEM)
-			mem_seen++;
-
-		/* If it is already assigned or the resource does
-		 * not exist, there is nothing to do.
-		 */
-		if (res->parent != NULL || res->flags == 0UL)
-			continue;
-
-		/* Determine the root we allocate from. */
-		if (res->flags & IORESOURCE_IO) {
-			root = &pbm->io_space;
-			min = root->start + 0x400UL;
-			max = root->end;
-		} else {
-			root = &pbm->mem_space;
-			min = root->start;
-			max = min + 0x80000000UL;
-		}
-
-		size = res->end - res->start;
-		align = size + 1;
-		if (allocate_resource(root, res, size + 1, min, max, align, NULL, NULL) < 0) {
-			/* uh oh */
-			prom_printf("PCI: Failed to allocate resource %d for %s\n",
-				    i, pci_name(pdev));
-			prom_halt();
-		}
-
-		/* Update PCI config space. */
-		pbm->parent->base_address_update(pdev, i);
-	}
-
-	/* Special case, disable the ROM.  Several devices
-	 * act funny (ie. do not respond to memory space writes)
-	 * when it is left enabled.  A good example are Qlogic,ISP
-	 * adapters.
-	 */
-	pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &reg);
-	reg &= ~PCI_ROM_ADDRESS_ENABLE;
-	pci_write_config_dword(pdev, PCI_ROM_ADDRESS, reg);
-
-	/* If we saw I/O or MEM resources, enable appropriate
-	 * bits in PCI command register.
-	 */
-	if (io_seen || mem_seen) {
-		pci_read_config_word(pdev, PCI_COMMAND, &cmd);
-		if (io_seen || has_implicit_io(pdev))
-			cmd |= PCI_COMMAND_IO;
-		if (mem_seen)
-			cmd |= PCI_COMMAND_MEMORY;
-		pci_write_config_word(pdev, PCI_COMMAND, cmd);
-	}
-
-	/* If this is a PCI bridge or an IDE controller,
-	 * enable bus mastering.  In the former case also
-	 * set the cache line size correctly.
-	 */
-	if (((pdev->class >> 8) == PCI_CLASS_BRIDGE_PCI) ||
-	    (((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) &&
-	     ((pdev->class & 0x80) != 0))) {
-		pci_read_config_word(pdev, PCI_COMMAND, &cmd);
-		cmd |= PCI_COMMAND_MASTER;
-		pci_write_config_word(pdev, PCI_COMMAND, cmd);
-
-		if ((pdev->class >> 8) == PCI_CLASS_BRIDGE_PCI)
-			pci_write_config_byte(pdev,
-					      PCI_CACHE_LINE_SIZE,
-					      (64 / sizeof(u32)));
-	}
-}
-
-void __init pci_assign_unassigned(struct pci_pbm_info *pbm,
-				  struct pci_bus *pbus)
-{
-	struct pci_dev *dev;
-	struct pci_bus *bus;
-
-	list_for_each_entry(dev, &pbus->devices, bus_list)
-		pdev_assign_unassigned(pbm, dev);
-
-	list_for_each_entry(bus, &pbus->children, node)
-		pci_assign_unassigned(pbm, bus);
-}
-
-static void __init pdev_fixup_irq(struct pci_dev *pdev)
-{
-	struct pcidev_cookie *pcp = pdev->sysdata;
-	struct of_device *op = pcp->op;
-
-	if (op->irqs[0] == 0xffffffff) {
-		pdev->irq = PCI_IRQ_NONE;
-		return;
-	}
-
-	pdev->irq = op->irqs[0];
-
-	pci_write_config_byte(pdev, PCI_INTERRUPT_LINE,
-			      pdev->irq & PCI_IRQ_INO);
-}
-
-void __init pci_fixup_irq(struct pci_pbm_info *pbm,
-			  struct pci_bus *pbus)
-{
-	struct pci_dev *dev;
-	struct pci_bus *bus;
-
-	list_for_each_entry(dev, &pbus->devices, bus_list)
-		pdev_fixup_irq(dev);
-
-	list_for_each_entry(bus, &pbus->children, node)
-		pci_fixup_irq(pbm, bus);
-}
-
-static void pdev_setup_busmastering(struct pci_dev *pdev, int is_66mhz)
-{
-	u16 cmd;
-	u8 hdr_type, min_gnt, ltimer;
-
-	pci_read_config_word(pdev, PCI_COMMAND, &cmd);
-	cmd |= PCI_COMMAND_MASTER;
-	pci_write_config_word(pdev, PCI_COMMAND, cmd);
-
-	/* Read it back, if the mastering bit did not
-	 * get set, the device does not support bus
-	 * mastering so we have nothing to do here.
-	 */
-	pci_read_config_word(pdev, PCI_COMMAND, &cmd);
-	if ((cmd & PCI_COMMAND_MASTER) == 0)
-		return;
-
-	/* Set correct cache line size, 64-byte on all
-	 * Sparc64 PCI systems.  Note that the value is
-	 * measured in 32-bit words.
-	 */
-	pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE,
-			      64 / sizeof(u32));
-
-	pci_read_config_byte(pdev, PCI_HEADER_TYPE, &hdr_type);
-	hdr_type &= ~0x80;
-	if (hdr_type != PCI_HEADER_TYPE_NORMAL)
-		return;
-
-	/* If the latency timer is already programmed with a non-zero
-	 * value, assume whoever set it (OBP or whoever) knows what
-	 * they are doing.
-	 */
-	pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &ltimer);
-	if (ltimer != 0)
-		return;
-
-	/* XXX Since I'm tipping off the min grant value to
-	 * XXX choose a suitable latency timer value, I also
-	 * XXX considered making use of the max latency value
-	 * XXX as well.  Unfortunately I've seen too many bogusly
-	 * XXX low settings for it to the point where it lacks
-	 * XXX any usefulness.  In one case, an ethernet card
-	 * XXX claimed a min grant of 10 and a max latency of 5.
-	 * XXX Now, if I had two such cards on the same bus I
-	 * XXX could not set the desired burst period (calculated
-	 * XXX from min grant) without violating the max latency
-	 * XXX bound.  Duh...
-	 * XXX
-	 * XXX I blame dumb PC bios implementors for stuff like
-	 * XXX this, most of them don't even try to do something
-	 * XXX sensible with latency timer values and just set some
-	 * XXX default value (usually 32) into every device.
-	 */
-
-	pci_read_config_byte(pdev, PCI_MIN_GNT, &min_gnt);
-
-	if (min_gnt == 0) {
-		/* If no min_gnt setting then use a default
-		 * value.
-		 */
-		if (is_66mhz)
-			ltimer = 16;
-		else
-			ltimer = 32;
-	} else {
-		int shift_factor;
-
-		if (is_66mhz)
-			shift_factor = 2;
-		else
-			shift_factor = 3;
-
-		/* Use a default value when the min_gnt value
-		 * is erroneously high.
-		 */
-		if (((unsigned int) min_gnt << shift_factor) > 512 ||
-		    ((min_gnt << shift_factor) & 0xff) == 0) {
-			ltimer = 8 << shift_factor;
-		} else {
-			ltimer = min_gnt << shift_factor;
-		}
-	}
-
-	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, ltimer);
-}
-
-void pci_determine_66mhz_disposition(struct pci_pbm_info *pbm,
-				     struct pci_bus *pbus)
-{
-	struct pci_dev *pdev;
-	int all_are_66mhz;
-	u16 status;
-
-	if (pbm->is_66mhz_capable == 0) {
-		all_are_66mhz = 0;
-		goto out;
-	}
-
-	all_are_66mhz = 1;
-	list_for_each_entry(pdev, &pbus->devices, bus_list) {
-		pci_read_config_word(pdev, PCI_STATUS, &status);
-		if (!(status & PCI_STATUS_66MHZ)) {
-			all_are_66mhz = 0;
-			break;
-		}
-	}
-out:
-	pbm->all_devs_66mhz = all_are_66mhz;
-
-	printk("PCI%d(PBM%c): Bus running at %dMHz\n",
-	       pbm->parent->index,
-	       (pbm == &pbm->parent->pbm_A) ? 'A' : 'B',
-	       (all_are_66mhz ? 66 : 33));
-}
-
-void pci_setup_busmastering(struct pci_pbm_info *pbm,
-			    struct pci_bus *pbus)
-{
-	struct pci_dev *dev;
-	struct pci_bus *bus;
-	int is_66mhz;
-
-	is_66mhz = pbm->is_66mhz_capable && pbm->all_devs_66mhz;
-
-	list_for_each_entry(dev, &pbus->devices, bus_list)
-		pdev_setup_busmastering(dev, is_66mhz);
-
-	list_for_each_entry(bus, &pbus->children, node)
-		pci_setup_busmastering(pbm, bus);
-}
-
-void pci_register_legacy_regions(struct resource *io_res,
-				 struct resource *mem_res)
+static void pci_register_legacy_regions(struct resource *io_res,
+					struct resource *mem_res)
 {
 	struct resource *p;
 
@@ -760,6 +52,102 @@
 	request_resource(mem_res, p);
 }
 
+static void pci_register_iommu_region(struct pci_pbm_info *pbm)
+{
+	const u32 *vdma = of_get_property(pbm->prom_node, "virtual-dma", NULL);
+
+	if (vdma) {
+		struct resource *rp = kmalloc(sizeof(*rp), GFP_KERNEL);
+
+		if (!rp) {
+			prom_printf("Cannot allocate IOMMU resource.\n");
+			prom_halt();
+		}
+		rp->name = "IOMMU";
+		rp->start = pbm->mem_space.start + (unsigned long) vdma[0];
+		rp->end = rp->start + (unsigned long) vdma[1] - 1UL;
+		rp->flags = IORESOURCE_BUSY;
+		request_resource(&pbm->mem_space, rp);
+	}
+}
+
+void pci_determine_mem_io_space(struct pci_pbm_info *pbm)
+{
+	const struct linux_prom_pci_ranges *pbm_ranges;
+	int i, saw_mem, saw_io;
+	int num_pbm_ranges;
+
+	saw_mem = saw_io = 0;
+	pbm_ranges = of_get_property(pbm->prom_node, "ranges", &i);
+	num_pbm_ranges = i / sizeof(*pbm_ranges);
+
+	for (i = 0; i < num_pbm_ranges; i++) {
+		const struct linux_prom_pci_ranges *pr = &pbm_ranges[i];
+		unsigned long a;
+		u32 parent_phys_hi, parent_phys_lo;
+		int type;
+
+		parent_phys_hi = pr->parent_phys_hi;
+		parent_phys_lo = pr->parent_phys_lo;
+		if (tlb_type == hypervisor)
+			parent_phys_hi &= 0x0fffffff;
+
+		type = (pr->child_phys_hi >> 24) & 0x3;
+		a = (((unsigned long)parent_phys_hi << 32UL) |
+		     ((unsigned long)parent_phys_lo  <<  0UL));
+
+		switch (type) {
+		case 0:
+			/* PCI config space, 16MB */
+			pbm->config_space = a;
+			break;
+
+		case 1:
+			/* 16-bit IO space, 16MB */
+			pbm->io_space.start = a;
+			pbm->io_space.end = a + ((16UL*1024UL*1024UL) - 1UL);
+			pbm->io_space.flags = IORESOURCE_IO;
+			saw_io = 1;
+			break;
+
+		case 2:
+			/* 32-bit MEM space, 2GB */
+			pbm->mem_space.start = a;
+			pbm->mem_space.end = a + (0x80000000UL - 1UL);
+			pbm->mem_space.flags = IORESOURCE_MEM;
+			saw_mem = 1;
+			break;
+
+		case 3:
+			/* XXX 64-bit MEM handling XXX */
+
+		default:
+			break;
+		};
+	}
+
+	if (!saw_io || !saw_mem) {
+		prom_printf("%s: Fatal error, missing %s PBM range.\n",
+			    pbm->name,
+			    (!saw_io ? "IO" : "MEM"));
+		prom_halt();
+	}
+
+	printk("%s: PCI IO[%lx] MEM[%lx]\n",
+	       pbm->name,
+	       pbm->io_space.start,
+	       pbm->mem_space.start);
+
+	pbm->io_space.name = pbm->mem_space.name = pbm->name;
+
+	request_resource(&ioport_resource, &pbm->io_space);
+	request_resource(&iomem_resource, &pbm->mem_space);
+
+	pci_register_legacy_regions(&pbm->io_space,
+				    &pbm->mem_space);
+	pci_register_iommu_region(pbm);
+}
+
 /* Generic helper routines for PCI error reporting. */
 void pci_scan_for_target_abort(struct pci_controller_info *p,
 			       struct pci_pbm_info *pbm,
diff --git a/arch/sparc64/kernel/pci_impl.h b/arch/sparc64/kernel/pci_impl.h
index 971e2be..1208583 100644
--- a/arch/sparc64/kernel/pci_impl.h
+++ b/arch/sparc64/kernel/pci_impl.h
@@ -1,7 +1,6 @@
-/* $Id: pci_impl.h,v 1.9 2001/06/13 06:34:30 davem Exp $
- * pci_impl.h: Helper definitions for PCI controller support.
+/* pci_impl.h: Helper definitions for PCI controller support.
  *
- * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1999, 2007 David S. Miller (davem@davemloft.net)
  */
 
 #ifndef PCI_IMPL_H
@@ -13,26 +12,22 @@
 #include <asm/prom.h>
 
 extern struct pci_controller_info *pci_controller_root;
+extern unsigned long pci_memspace_mask;
 
 extern int pci_num_controllers;
 
 /* PCI bus scanning and fixup support. */
-extern void pci_fixup_host_bridge_self(struct pci_bus *pbus);
-extern void pci_fill_in_pbm_cookies(struct pci_bus *pbus,
-				    struct pci_pbm_info *pbm,
-				    struct device_node *prom_node);
-extern void pci_record_assignments(struct pci_pbm_info *pbm,
-				   struct pci_bus *pbus);
-extern void pci_assign_unassigned(struct pci_pbm_info *pbm,
-				  struct pci_bus *pbus);
-extern void pci_fixup_irq(struct pci_pbm_info *pbm,
-			  struct pci_bus *pbus);
-extern void pci_determine_66mhz_disposition(struct pci_pbm_info *pbm,
-					    struct pci_bus *pbus);
-extern void pci_setup_busmastering(struct pci_pbm_info *pbm,
-				   struct pci_bus *pbus);
-extern void pci_register_legacy_regions(struct resource *io_res,
-					struct resource *mem_res);
+extern struct pci_bus *pci_scan_one_pbm(struct pci_pbm_info *pbm);
+extern void pci_determine_mem_io_space(struct pci_pbm_info *pbm);
+
+extern int pci_host_bridge_read_pci_cfg(struct pci_bus *bus_dev,
+					unsigned int devfn,
+					int where, int size,
+					u32 *value);
+extern int pci_host_bridge_write_pci_cfg(struct pci_bus *bus_dev,
+					 unsigned int devfn,
+					 int where, int size,
+					 u32 value);
 
 /* Error reporting support. */
 extern void pci_scan_for_target_abort(struct pci_controller_info *, struct pci_pbm_info *, struct pci_bus *);
diff --git a/arch/sparc64/kernel/pci_iommu.c b/arch/sparc64/kernel/pci_iommu.c
index 7aca0f3..6671277 100644
--- a/arch/sparc64/kernel/pci_iommu.c
+++ b/arch/sparc64/kernel/pci_iommu.c
@@ -1,7 +1,6 @@
-/* $Id: pci_iommu.c,v 1.17 2001/12/17 07:05:09 davem Exp $
- * pci_iommu.c: UltraSparc PCI controller IOM/STC support.
+/* pci_iommu.c: UltraSparc PCI controller IOM/STC support.
  *
- * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1999, 2007 David S. Miller (davem@davemloft.net)
  * Copyright (C) 1999, 2000 Jakub Jelinek (jakub@redhat.com)
  */
 
@@ -36,7 +35,7 @@
 			       "i" (ASI_PHYS_BYPASS_EC_E))
 
 /* Must be invoked under the IOMMU lock. */
-static void __iommu_flushall(struct pci_iommu *iommu)
+static void __iommu_flushall(struct iommu *iommu)
 {
 	unsigned long tag;
 	int entry;
@@ -64,7 +63,7 @@
 #define IOPTE_IS_DUMMY(iommu, iopte)	\
 	((iopte_val(*iopte) & IOPTE_PAGE) == (iommu)->dummy_page_pa)
 
-static inline void iopte_make_dummy(struct pci_iommu *iommu, iopte_t *iopte)
+static inline void iopte_make_dummy(struct iommu *iommu, iopte_t *iopte)
 {
 	unsigned long val = iopte_val(*iopte);
 
@@ -75,9 +74,9 @@
 }
 
 /* Based largely upon the ppc64 iommu allocator.  */
-static long pci_arena_alloc(struct pci_iommu *iommu, unsigned long npages)
+static long pci_arena_alloc(struct iommu *iommu, unsigned long npages)
 {
-	struct pci_iommu_arena *arena = &iommu->arena;
+	struct iommu_arena *arena = &iommu->arena;
 	unsigned long n, i, start, end, limit;
 	int pass;
 
@@ -116,7 +115,7 @@
 	return n;
 }
 
-static void pci_arena_free(struct pci_iommu_arena *arena, unsigned long base, unsigned long npages)
+static void pci_arena_free(struct iommu_arena *arena, unsigned long base, unsigned long npages)
 {
 	unsigned long i;
 
@@ -124,7 +123,7 @@
 		__clear_bit(i, arena->map);
 }
 
-void pci_iommu_table_init(struct pci_iommu *iommu, int tsbsize, u32 dma_offset, u32 dma_addr_mask)
+void pci_iommu_table_init(struct iommu *iommu, int tsbsize, u32 dma_offset, u32 dma_addr_mask)
 {
 	unsigned long i, tsbbase, order, sz, num_tsb_entries;
 
@@ -170,7 +169,7 @@
 		iopte_make_dummy(iommu, &iommu->page_table[i]);
 }
 
-static inline iopte_t *alloc_npages(struct pci_iommu *iommu, unsigned long npages)
+static inline iopte_t *alloc_npages(struct iommu *iommu, unsigned long npages)
 {
 	long entry;
 
@@ -181,12 +180,12 @@
 	return iommu->page_table + entry;
 }
 
-static inline void free_npages(struct pci_iommu *iommu, dma_addr_t base, unsigned long npages)
+static inline void free_npages(struct iommu *iommu, dma_addr_t base, unsigned long npages)
 {
 	pci_arena_free(&iommu->arena, base >> IO_PAGE_SHIFT, npages);
 }
 
-static int iommu_alloc_ctx(struct pci_iommu *iommu)
+static int iommu_alloc_ctx(struct iommu *iommu)
 {
 	int lowest = iommu->ctx_lowest_free;
 	int sz = IOMMU_NUM_CTXS - lowest;
@@ -205,7 +204,7 @@
 	return n;
 }
 
-static inline void iommu_free_ctx(struct pci_iommu *iommu, int ctx)
+static inline void iommu_free_ctx(struct iommu *iommu, int ctx)
 {
 	if (likely(ctx)) {
 		__clear_bit(ctx, iommu->ctx_bitmap);
@@ -220,8 +219,7 @@
  */
 static void *pci_4u_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_addrp, gfp_t gfp)
 {
-	struct pcidev_cookie *pcp;
-	struct pci_iommu *iommu;
+	struct iommu *iommu;
 	iopte_t *iopte;
 	unsigned long flags, order, first_page;
 	void *ret;
@@ -237,8 +235,7 @@
 		return NULL;
 	memset((char *)first_page, 0, PAGE_SIZE << order);
 
-	pcp = pdev->sysdata;
-	iommu = pcp->pbm->iommu;
+	iommu = pdev->dev.archdata.iommu;
 
 	spin_lock_irqsave(&iommu->lock, flags);
 	iopte = alloc_npages(iommu, size >> IO_PAGE_SHIFT);
@@ -268,14 +265,12 @@
 /* Free and unmap a consistent DMA translation. */
 static void pci_4u_free_consistent(struct pci_dev *pdev, size_t size, void *cpu, dma_addr_t dvma)
 {
-	struct pcidev_cookie *pcp;
-	struct pci_iommu *iommu;
+	struct iommu *iommu;
 	iopte_t *iopte;
 	unsigned long flags, order, npages;
 
 	npages = IO_PAGE_ALIGN(size) >> IO_PAGE_SHIFT;
-	pcp = pdev->sysdata;
-	iommu = pcp->pbm->iommu;
+	iommu = pdev->dev.archdata.iommu;
 	iopte = iommu->page_table +
 		((dvma - iommu->page_table_map_base) >> IO_PAGE_SHIFT);
 
@@ -295,18 +290,16 @@
  */
 static dma_addr_t pci_4u_map_single(struct pci_dev *pdev, void *ptr, size_t sz, int direction)
 {
-	struct pcidev_cookie *pcp;
-	struct pci_iommu *iommu;
-	struct pci_strbuf *strbuf;
+	struct iommu *iommu;
+	struct strbuf *strbuf;
 	iopte_t *base;
 	unsigned long flags, npages, oaddr;
 	unsigned long i, base_paddr, ctx;
 	u32 bus_addr, ret;
 	unsigned long iopte_protection;
 
-	pcp = pdev->sysdata;
-	iommu = pcp->pbm->iommu;
-	strbuf = &pcp->pbm->stc;
+	iommu = pdev->dev.archdata.iommu;
+	strbuf = pdev->dev.archdata.stc;
 
 	if (unlikely(direction == PCI_DMA_NONE))
 		goto bad_no_ctx;
@@ -349,7 +342,7 @@
 	return PCI_DMA_ERROR_CODE;
 }
 
-static void pci_strbuf_flush(struct pci_strbuf *strbuf, struct pci_iommu *iommu, u32 vaddr, unsigned long ctx, unsigned long npages, int direction)
+static void pci_strbuf_flush(struct strbuf *strbuf, struct iommu *iommu, u32 vaddr, unsigned long ctx, unsigned long npages, int direction)
 {
 	int limit;
 
@@ -416,9 +409,8 @@
 /* Unmap a single streaming mode DMA translation. */
 static void pci_4u_unmap_single(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz, int direction)
 {
-	struct pcidev_cookie *pcp;
-	struct pci_iommu *iommu;
-	struct pci_strbuf *strbuf;
+	struct iommu *iommu;
+	struct strbuf *strbuf;
 	iopte_t *base;
 	unsigned long flags, npages, ctx, i;
 
@@ -428,9 +420,8 @@
 		return;
 	}
 
-	pcp = pdev->sysdata;
-	iommu = pcp->pbm->iommu;
-	strbuf = &pcp->pbm->stc;
+	iommu = pdev->dev.archdata.iommu;
+	strbuf = pdev->dev.archdata.stc;
 
 	npages = IO_PAGE_ALIGN(bus_addr + sz) - (bus_addr & IO_PAGE_MASK);
 	npages >>= IO_PAGE_SHIFT;
@@ -549,9 +540,8 @@
  */
 static int pci_4u_map_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction)
 {
-	struct pcidev_cookie *pcp;
-	struct pci_iommu *iommu;
-	struct pci_strbuf *strbuf;
+	struct iommu *iommu;
+	struct strbuf *strbuf;
 	unsigned long flags, ctx, npages, iopte_protection;
 	iopte_t *base;
 	u32 dma_base;
@@ -570,9 +560,8 @@
 		return 1;
 	}
 
-	pcp = pdev->sysdata;
-	iommu = pcp->pbm->iommu;
-	strbuf = &pcp->pbm->stc;
+	iommu = pdev->dev.archdata.iommu;
+	strbuf = pdev->dev.archdata.stc;
 	
 	if (unlikely(direction == PCI_DMA_NONE))
 		goto bad_no_ctx;
@@ -636,9 +625,8 @@
 /* Unmap a set of streaming mode DMA translations. */
 static void pci_4u_unmap_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction)
 {
-	struct pcidev_cookie *pcp;
-	struct pci_iommu *iommu;
-	struct pci_strbuf *strbuf;
+	struct iommu *iommu;
+	struct strbuf *strbuf;
 	iopte_t *base;
 	unsigned long flags, ctx, i, npages;
 	u32 bus_addr;
@@ -648,9 +636,8 @@
 			WARN_ON(1);
 	}
 
-	pcp = pdev->sysdata;
-	iommu = pcp->pbm->iommu;
-	strbuf = &pcp->pbm->stc;
+	iommu = pdev->dev.archdata.iommu;
+	strbuf = pdev->dev.archdata.stc;
 	
 	bus_addr = sglist->dma_address & IO_PAGE_MASK;
 
@@ -696,14 +683,12 @@
  */
 static void pci_4u_dma_sync_single_for_cpu(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz, int direction)
 {
-	struct pcidev_cookie *pcp;
-	struct pci_iommu *iommu;
-	struct pci_strbuf *strbuf;
+	struct iommu *iommu;
+	struct strbuf *strbuf;
 	unsigned long flags, ctx, npages;
 
-	pcp = pdev->sysdata;
-	iommu = pcp->pbm->iommu;
-	strbuf = &pcp->pbm->stc;
+	iommu = pdev->dev.archdata.iommu;
+	strbuf = pdev->dev.archdata.stc;
 
 	if (!strbuf->strbuf_enabled)
 		return;
@@ -736,15 +721,13 @@
  */
 static void pci_4u_dma_sync_sg_for_cpu(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction)
 {
-	struct pcidev_cookie *pcp;
-	struct pci_iommu *iommu;
-	struct pci_strbuf *strbuf;
+	struct iommu *iommu;
+	struct strbuf *strbuf;
 	unsigned long flags, ctx, npages, i;
 	u32 bus_addr;
 
-	pcp = pdev->sysdata;
-	iommu = pcp->pbm->iommu;
-	strbuf = &pcp->pbm->stc;
+	iommu = pdev->dev.archdata.iommu;
+	strbuf = pdev->dev.archdata.stc;
 
 	if (!strbuf->strbuf_enabled)
 		return;
@@ -775,7 +758,7 @@
 	spin_unlock_irqrestore(&iommu->lock, flags);
 }
 
-struct pci_iommu_ops pci_sun4u_iommu_ops = {
+const struct pci_iommu_ops pci_sun4u_iommu_ops = {
 	.alloc_consistent		= pci_4u_alloc_consistent,
 	.free_consistent		= pci_4u_free_consistent,
 	.map_single			= pci_4u_map_single,
@@ -809,13 +792,12 @@
 
 int pci_dma_supported(struct pci_dev *pdev, u64 device_mask)
 {
-	struct pcidev_cookie *pcp = pdev->sysdata;
 	u64 dma_addr_mask;
 
 	if (pdev == NULL) {
 		dma_addr_mask = 0xffffffff;
 	} else {
-		struct pci_iommu *iommu = pcp->pbm->iommu;
+		struct iommu *iommu = pdev->dev.archdata.iommu;
 
 		dma_addr_mask = iommu->dma_addr_mask;
 
diff --git a/arch/sparc64/kernel/pci_psycho.c b/arch/sparc64/kernel/pci_psycho.c
index fda5db2..253d40e 100644
--- a/arch/sparc64/kernel/pci_psycho.c
+++ b/arch/sparc64/kernel/pci_psycho.c
@@ -1,7 +1,6 @@
-/* $Id: pci_psycho.c,v 1.33 2002/02/01 00:58:33 davem Exp $
- * pci_psycho.c: PSYCHO/U2P specific PCI controller support.
+/* pci_psycho.c: PSYCHO/U2P specific PCI controller support.
  *
- * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@caipfs.rutgers.edu)
+ * Copyright (C) 1997, 1998, 1999, 2007 David S. Miller (davem@davemloft.net)
  * Copyright (C) 1998, 1999 Eddie C. Dost   (ecd@skynet.be)
  * Copyright (C) 1999 Jakub Jelinek   (jakub@redhat.com)
  */
@@ -119,6 +118,10 @@
 	u16 tmp16;
 	u8 tmp8;
 
+	if (bus_dev == pbm->pci_bus && devfn == 0x00)
+		return pci_host_bridge_read_pci_cfg(bus_dev, devfn, where,
+						    size, value);
+
 	switch (size) {
 	case 1:
 		*value = 0xff;
@@ -172,6 +175,9 @@
 	unsigned char bus = bus_dev->number;
 	u32 *addr;
 
+	if (bus_dev == pbm->pci_bus && devfn == 0x00)
+		return pci_host_bridge_write_pci_cfg(bus_dev, devfn, where,
+						     size, value);
 	addr = psycho_pci_config_mkaddr(pbm, bus, devfn, where);
 	if (!addr)
 		return PCIBIOS_SUCCESSFUL;
@@ -263,7 +269,7 @@
 				   struct pci_pbm_info *pbm,
 				   int is_pbm_a)
 {
-	struct pci_strbuf *strbuf = &pbm->stc;
+	struct strbuf *strbuf = &pbm->stc;
 	unsigned long regbase = p->pbm_A.controller_regs;
 	unsigned long err_base, tag_base, line_base;
 	u64 control;
@@ -412,7 +418,7 @@
 				     unsigned long afar,
 				     enum psycho_error_type type)
 {
-	struct pci_iommu *iommu = p->pbm_A.iommu;
+	struct iommu *iommu = p->pbm_A.iommu;
 	unsigned long iommu_tag[16];
 	unsigned long iommu_data[16];
 	unsigned long flags;
@@ -895,59 +901,6 @@
 }
 
 /* PSYCHO boot time probing and initialization. */
-static void psycho_resource_adjust(struct pci_dev *pdev,
-				   struct resource *res,
-				   struct resource *root)
-{
-	res->start += root->start;
-	res->end += root->start;
-}
-
-static void psycho_base_address_update(struct pci_dev *pdev, int resource)
-{
-	struct pcidev_cookie *pcp = pdev->sysdata;
-	struct pci_pbm_info *pbm = pcp->pbm;
-	struct resource *res, *root;
-	u32 reg;
-	int where, size, is_64bit;
-
-	res = &pdev->resource[resource];
-	if (resource < 6) {
-		where = PCI_BASE_ADDRESS_0 + (resource * 4);
-	} else if (resource == PCI_ROM_RESOURCE) {
-		where = pdev->rom_base_reg;
-	} else {
-		/* Somebody might have asked allocation of a non-standard resource */
-		return;
-	}
-
-	is_64bit = 0;
-	if (res->flags & IORESOURCE_IO)
-		root = &pbm->io_space;
-	else {
-		root = &pbm->mem_space;
-		if ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK)
-		    == PCI_BASE_ADDRESS_MEM_TYPE_64)
-			is_64bit = 1;
-	}
-
-	size = res->end - res->start;
-	pci_read_config_dword(pdev, where, &reg);
-	reg = ((reg & size) |
-	       (((u32)(res->start - root->start)) & ~size));
-	if (resource == PCI_ROM_RESOURCE) {
-		reg |= PCI_ROM_ADDRESS_ENABLE;
-		res->flags |= IORESOURCE_ROM_ENABLE;
-	}
-	pci_write_config_dword(pdev, where, reg);
-
-	/* This knows that the upper 32-bits of the address
-	 * must be zero.  Our PCI common layer enforces this.
-	 */
-	if (is_64bit)
-		pci_write_config_dword(pdev, where + 4, 0);
-}
-
 static void pbm_config_busmastering(struct pci_pbm_info *pbm)
 {
 	u8 *addr;
@@ -968,28 +921,7 @@
 static void pbm_scan_bus(struct pci_controller_info *p,
 			 struct pci_pbm_info *pbm)
 {
-	struct pcidev_cookie *cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
-
-	if (!cookie) {
-		prom_printf("PSYCHO: Critical allocation failure.\n");
-		prom_halt();
-	}
-
-	/* All we care about is the PBM. */
-	cookie->pbm = pbm;
-
-	pbm->pci_bus = pci_scan_bus(pbm->pci_first_busno,
-				    p->pci_ops,
-				    pbm);
-	pci_fixup_host_bridge_self(pbm->pci_bus);
-	pbm->pci_bus->self->sysdata = cookie;
-
-	pci_fill_in_pbm_cookies(pbm->pci_bus, pbm, pbm->prom_node);
-	pci_record_assignments(pbm, pbm->pci_bus);
-	pci_assign_unassigned(pbm, pbm->pci_bus);
-	pci_fixup_irq(pbm, pbm->pci_bus);
-	pci_determine_66mhz_disposition(pbm, pbm->pci_bus);
-	pci_setup_busmastering(pbm, pbm->pci_bus);
+	pbm->pci_bus = pci_scan_one_pbm(pbm);
 }
 
 static void psycho_scan_bus(struct pci_controller_info *p)
@@ -1009,7 +941,7 @@
 
 static void psycho_iommu_init(struct pci_controller_info *p)
 {
-	struct pci_iommu *iommu = p->pbm_A.iommu;
+	struct iommu *iommu = p->pbm_A.iommu;
 	unsigned long i;
 	u64 control;
 
@@ -1094,19 +1026,6 @@
 	psycho_write(p->pbm_A.controller_regs + PSYCHO_PCIB_DIAG, tmp);
 }
 
-static void pbm_register_toplevel_resources(struct pci_controller_info *p,
-					    struct pci_pbm_info *pbm)
-{
-	char *name = pbm->name;
-
-	pbm->io_space.name = pbm->mem_space.name = name;
-
-	request_resource(&ioport_resource, &pbm->io_space);
-	request_resource(&iomem_resource, &pbm->mem_space);
-	pci_register_legacy_regions(&pbm->io_space,
-				    &pbm->mem_space);
-}
-
 static void psycho_pbm_strbuf_init(struct pci_controller_info *p,
 				   struct pci_pbm_info *pbm,
 				   int is_pbm_a)
@@ -1172,19 +1091,11 @@
 	unsigned int *busrange;
 	struct property *prop;
 	struct pci_pbm_info *pbm;
-	int len;
 
-	if (is_pbm_a) {
+	if (is_pbm_a)
 		pbm = &p->pbm_A;
-		pbm->pci_first_slot = 1;
-		pbm->io_space.start = pbm->controller_regs + PSYCHO_IOSPACE_A;
-		pbm->mem_space.start = pbm->controller_regs + PSYCHO_MEMSPACE_A;
-	} else {
+	else
 		pbm = &p->pbm_B;
-		pbm->pci_first_slot = 2;
-		pbm->io_space.start = pbm->controller_regs + PSYCHO_IOSPACE_B;
-		pbm->mem_space.start = pbm->controller_regs + PSYCHO_MEMSPACE_B;
-	}
 
 	pbm->chip_type = PBM_CHIP_TYPE_PSYCHO;
 	pbm->chip_version = 0;
@@ -1196,41 +1107,15 @@
 	if (prop)
 		pbm->chip_revision = *(int *) prop->value;
 
-	pbm->io_space.end = pbm->io_space.start + PSYCHO_IOSPACE_SIZE;
-	pbm->io_space.flags = IORESOURCE_IO;
-	pbm->mem_space.end = pbm->mem_space.start + PSYCHO_MEMSPACE_SIZE;
-	pbm->mem_space.flags = IORESOURCE_MEM;
-
 	pbm->parent = p;
 	pbm->prom_node = dp;
 	pbm->name = dp->full_name;
 
-	pbm_register_toplevel_resources(p, pbm);
-
 	printk("%s: PSYCHO PCI Bus Module ver[%x:%x]\n",
 	       pbm->name,
 	       pbm->chip_version, pbm->chip_revision);
 
-	prop = of_find_property(dp, "ranges", &len);
-	if (prop) {
-		pbm->pbm_ranges = prop->value;
-		pbm->num_pbm_ranges =
-			(len / sizeof(struct linux_prom_pci_ranges));
-	} else {
-		pbm->num_pbm_ranges = 0;
-	}
-
-	prop = of_find_property(dp, "interrupt-map", &len);
-	if (prop) {
-		pbm->pbm_intmap = prop->value;
-		pbm->num_pbm_intmap =
-			(len / sizeof(struct linux_prom_pci_intmap));
-
-		prop = of_find_property(dp, "interrupt-map-mask", NULL);
-		pbm->pbm_intmask = prop->value;
-	} else {
-		pbm->num_pbm_intmap = 0;
-	}
+	pci_determine_mem_io_space(pbm);
 
 	prop = of_find_property(dp, "bus-range", NULL);
 	busrange = prop->value;
@@ -1246,7 +1131,7 @@
 {
 	struct linux_prom64_registers *pr_regs;
 	struct pci_controller_info *p;
-	struct pci_iommu *iommu;
+	struct iommu *iommu;
 	struct property *prop;
 	u32 upa_portid;
 	int is_pbm_a;
@@ -1269,7 +1154,7 @@
 		prom_printf("PSYCHO: Fatal memory allocation error.\n");
 		prom_halt();
 	}
-	iommu = kzalloc(sizeof(struct pci_iommu), GFP_ATOMIC);
+	iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC);
 	if (!iommu) {
 		prom_printf("PSYCHO: Fatal memory allocation error.\n");
 		prom_halt();
@@ -1282,10 +1167,7 @@
 	p->pbm_A.portid = upa_portid;
 	p->pbm_B.portid = upa_portid;
 	p->index = pci_num_controllers++;
-	p->pbms_same_domain = 0;
 	p->scan_bus = psycho_scan_bus;
-	p->base_address_update = psycho_base_address_update;
-	p->resource_adjust = psycho_resource_adjust;
 	p->pci_ops = &psycho_ops;
 
 	prop = of_find_property(dp, "reg", NULL);
diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc64/kernel/pci_sabre.c
index 94bb681..397862f 100644
--- a/arch/sparc64/kernel/pci_sabre.c
+++ b/arch/sparc64/kernel/pci_sabre.c
@@ -1,7 +1,6 @@
-/* $Id: pci_sabre.c,v 1.42 2002/01/23 11:27:32 davem Exp $
- * pci_sabre.c: Sabre specific PCI controller support.
+/* pci_sabre.c: Sabre specific PCI controller support.
  *
- * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@caipfs.rutgers.edu)
+ * Copyright (C) 1997, 1998, 1999, 2007 David S. Miller (davem@davemloft.net)
  * Copyright (C) 1998, 1999 Eddie C. Dost   (ecd@skynet.be)
  * Copyright (C) 1999 Jakub Jelinek   (jakub@redhat.com)
  */
@@ -254,9 +253,6 @@
 		return 0;
 
 	return ((pbm->parent == 0) ||
-		((pbm == &pbm->parent->pbm_B) &&
-		 (bus == pbm->pci_first_busno) &&
-		 PCI_SLOT(devfn) > 8) ||
 		((pbm == &pbm->parent->pbm_A) &&
 		 (bus == pbm->pci_first_busno) &&
 		 PCI_SLOT(devfn) > 8));
@@ -322,6 +318,12 @@
 static int sabre_read_pci_cfg(struct pci_bus *bus, unsigned int devfn,
 			      int where, int size, u32 *value)
 {
+	struct pci_pbm_info *pbm = bus->sysdata;
+
+	if (bus == pbm->pci_bus && devfn == 0x00)
+		return pci_host_bridge_read_pci_cfg(bus, devfn, where,
+						    size, value);
+
 	if (!bus->number && sabre_out_of_range(devfn)) {
 		switch (size) {
 		case 1:
@@ -438,6 +440,12 @@
 static int sabre_write_pci_cfg(struct pci_bus *bus, unsigned int devfn,
 			       int where, int size, u32 value)
 {
+	struct pci_pbm_info *pbm = bus->sysdata;
+
+	if (bus == pbm->pci_bus && devfn == 0x00)
+		return pci_host_bridge_write_pci_cfg(bus, devfn, where,
+						     size, value);
+
 	if (bus->number)
 		return __sabre_write_pci_cfg(bus, devfn, where, size, value);
 
@@ -490,7 +498,7 @@
 				    unsigned long afsr,
 				    unsigned long afar)
 {
-	struct pci_iommu *iommu = p->pbm_A.iommu;
+	struct iommu *iommu = p->pbm_A.iommu;
 	unsigned long iommu_tag[16];
 	unsigned long iommu_data[16];
 	unsigned long flags;
@@ -710,8 +718,8 @@
 			       p->index);
 		ret = IRQ_HANDLED;
 	}
-	pci_read_config_word(sabre_root_bus->self,
-			     PCI_STATUS, &stat);
+	pci_bus_read_config_word(sabre_root_bus, 0,
+				 PCI_STATUS, &stat);
 	if (stat & (PCI_STATUS_PARITY |
 		    PCI_STATUS_SIG_TARGET_ABORT |
 		    PCI_STATUS_REC_TARGET_ABORT |
@@ -719,8 +727,8 @@
 		    PCI_STATUS_SIG_SYSTEM_ERROR)) {
 		printk("SABRE%d: PCI bus error, PCI_STATUS[%04x]\n",
 		       p->index, stat);
-		pci_write_config_word(sabre_root_bus->self,
-				      PCI_STATUS, 0xffff);
+		pci_bus_write_config_word(sabre_root_bus, 0,
+					  PCI_STATUS, 0xffff);
 		ret = IRQ_HANDLED;
 	}
 	return ret;
@@ -800,12 +808,10 @@
 	if (error_bits & (SABRE_PIOAFSR_PTA | SABRE_PIOAFSR_STA)) {
 		sabre_check_iommu_error(p, afsr, afar);
 		pci_scan_for_target_abort(p, &p->pbm_A, p->pbm_A.pci_bus);
-		pci_scan_for_target_abort(p, &p->pbm_B, p->pbm_B.pci_bus);
 	}
-	if (error_bits & (SABRE_PIOAFSR_PMA | SABRE_PIOAFSR_SMA)) {
+	if (error_bits & (SABRE_PIOAFSR_PMA | SABRE_PIOAFSR_SMA))
 		pci_scan_for_master_abort(p, &p->pbm_A, p->pbm_A.pci_bus);
-		pci_scan_for_master_abort(p, &p->pbm_B, p->pbm_B.pci_bus);
-	}
+
 	/* For excessive retries, SABRE/PBM will abort the device
 	 * and there is no way to specifically check for excessive
 	 * retries in the config space status registers.  So what
@@ -813,10 +819,8 @@
 	 * abort events.
 	 */
 
-	if (error_bits & (SABRE_PIOAFSR_PPERR | SABRE_PIOAFSR_SPERR)) {
+	if (error_bits & (SABRE_PIOAFSR_PPERR | SABRE_PIOAFSR_SPERR))
 		pci_scan_for_parity_error(p, &p->pbm_A, p->pbm_A.pci_bus);
-		pci_scan_for_parity_error(p, &p->pbm_B, p->pbm_B.pci_bus);
-	}
 
 	return IRQ_HANDLED;
 }
@@ -869,144 +873,52 @@
 	sabre_write(base + SABRE_PCICTRL, tmp);
 }
 
-static void sabre_resource_adjust(struct pci_dev *pdev,
-				  struct resource *res,
-				  struct resource *root)
-{
-	struct pci_pbm_info *pbm = pdev->bus->sysdata;
-	unsigned long base;
-
-	if (res->flags & IORESOURCE_IO)
-		base = pbm->controller_regs + SABRE_IOSPACE;
-	else
-		base = pbm->controller_regs + SABRE_MEMSPACE;
-
-	res->start += base;
-	res->end += base;
-}
-
-static void sabre_base_address_update(struct pci_dev *pdev, int resource)
-{
-	struct pcidev_cookie *pcp = pdev->sysdata;
-	struct pci_pbm_info *pbm = pcp->pbm;
-	struct resource *res;
-	unsigned long base;
-	u32 reg;
-	int where, size, is_64bit;
-
-	res = &pdev->resource[resource];
-	if (resource < 6) {
-		where = PCI_BASE_ADDRESS_0 + (resource * 4);
-	} else if (resource == PCI_ROM_RESOURCE) {
-		where = pdev->rom_base_reg;
-	} else {
-		/* Somebody might have asked allocation of a non-standard resource */
-		return;
-	}
-
-	is_64bit = 0;
-	if (res->flags & IORESOURCE_IO)
-		base = pbm->controller_regs + SABRE_IOSPACE;
-	else {
-		base = pbm->controller_regs + SABRE_MEMSPACE;
-		if ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK)
-		    == PCI_BASE_ADDRESS_MEM_TYPE_64)
-			is_64bit = 1;
-	}
-
-	size = res->end - res->start;
-	pci_read_config_dword(pdev, where, &reg);
-	reg = ((reg & size) |
-	       (((u32)(res->start - base)) & ~size));
-	if (resource == PCI_ROM_RESOURCE) {
-		reg |= PCI_ROM_ADDRESS_ENABLE;
-		res->flags |= IORESOURCE_ROM_ENABLE;
-	}
-	pci_write_config_dword(pdev, where, reg);
-
-	/* This knows that the upper 32-bits of the address
-	 * must be zero.  Our PCI common layer enforces this.
-	 */
-	if (is_64bit)
-		pci_write_config_dword(pdev, where + 4, 0);
-}
-
 static void apb_init(struct pci_controller_info *p, struct pci_bus *sabre_bus)
 {
 	struct pci_dev *pdev;
 
 	list_for_each_entry(pdev, &sabre_bus->devices, bus_list) {
-
 		if (pdev->vendor == PCI_VENDOR_ID_SUN &&
 		    pdev->device == PCI_DEVICE_ID_SUN_SIMBA) {
-			u32 word32;
 			u16 word16;
 
-			sabre_read_pci_cfg(pdev->bus, pdev->devfn,
-					   PCI_COMMAND, 2, &word32);
-			word16 = (u16) word32;
+			pci_read_config_word(pdev, PCI_COMMAND, &word16);
 			word16 |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
 				PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY |
 				PCI_COMMAND_IO;
-			word32 = (u32) word16;
-			sabre_write_pci_cfg(pdev->bus, pdev->devfn,
-					    PCI_COMMAND, 2, word32);
+			pci_write_config_word(pdev, PCI_COMMAND, word16);
 
 			/* Status register bits are "write 1 to clear". */
-			sabre_write_pci_cfg(pdev->bus, pdev->devfn,
-					    PCI_STATUS, 2, 0xffff);
-			sabre_write_pci_cfg(pdev->bus, pdev->devfn,
-					    PCI_SEC_STATUS, 2, 0xffff);
+			pci_write_config_word(pdev, PCI_STATUS, 0xffff);
+			pci_write_config_word(pdev, PCI_SEC_STATUS, 0xffff);
 
 			/* Use a primary/seconday latency timer value
 			 * of 64.
 			 */
-			sabre_write_pci_cfg(pdev->bus, pdev->devfn,
-					    PCI_LATENCY_TIMER, 1, 64);
-			sabre_write_pci_cfg(pdev->bus, pdev->devfn,
-					    PCI_SEC_LATENCY_TIMER, 1, 64);
+			pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64);
+			pci_write_config_byte(pdev, PCI_SEC_LATENCY_TIMER, 64);
 
 			/* Enable reporting/forwarding of master aborts,
 			 * parity, and SERR.
 			 */
-			sabre_write_pci_cfg(pdev->bus, pdev->devfn,
-					    PCI_BRIDGE_CONTROL, 1,
-					    (PCI_BRIDGE_CTL_PARITY |
-					     PCI_BRIDGE_CTL_SERR |
-					     PCI_BRIDGE_CTL_MASTER_ABORT));
+			pci_write_config_byte(pdev, PCI_BRIDGE_CONTROL,
+					      (PCI_BRIDGE_CTL_PARITY |
+					       PCI_BRIDGE_CTL_SERR |
+					       PCI_BRIDGE_CTL_MASTER_ABORT));
 		}
 	}
 }
 
-static struct pcidev_cookie *alloc_bridge_cookie(struct pci_pbm_info *pbm)
-{
-	struct pcidev_cookie *cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
-
-	if (!cookie) {
-		prom_printf("SABRE: Critical allocation failure.\n");
-		prom_halt();
-	}
-
-	/* All we care about is the PBM. */
-	cookie->pbm = pbm;
-
-	return cookie;
-}
-
 static void sabre_scan_bus(struct pci_controller_info *p)
 {
 	static int once;
-	struct pci_bus *sabre_bus, *pbus;
-	struct pci_pbm_info *pbm;
-	struct pcidev_cookie *cookie;
-	int sabres_scanned;
+	struct pci_bus *pbus;
 
 	/* The APB bridge speaks to the Sabre host PCI bridge
 	 * at 66Mhz, but the front side of APB runs at 33Mhz
 	 * for both segments.
 	 */
 	p->pbm_A.is_66mhz_capable = 0;
-	p->pbm_B.is_66mhz_capable = 0;
 
 	/* This driver has not been verified to handle
 	 * multiple SABREs yet, so trap this.
@@ -1020,56 +932,13 @@
 	}
 	once++;
 
-	cookie = alloc_bridge_cookie(&p->pbm_A);
+	pbus = pci_scan_one_pbm(&p->pbm_A);
+	if (!pbus)
+		return;
 
-	sabre_bus = pci_scan_bus(p->pci_first_busno,
-				 p->pci_ops,
-				 &p->pbm_A);
-	pci_fixup_host_bridge_self(sabre_bus);
-	sabre_bus->self->sysdata = cookie;
+	sabre_root_bus = pbus;
 
-	sabre_root_bus = sabre_bus;
-
-	apb_init(p, sabre_bus);
-
-	sabres_scanned = 0;
-
-	list_for_each_entry(pbus, &sabre_bus->children, node) {
-
-		if (pbus->number == p->pbm_A.pci_first_busno) {
-			pbm = &p->pbm_A;
-		} else if (pbus->number == p->pbm_B.pci_first_busno) {
-			pbm = &p->pbm_B;
-		} else
-			continue;
-
-		cookie = alloc_bridge_cookie(pbm);
-		pbus->self->sysdata = cookie;
-
-		sabres_scanned++;
-
-		pbus->sysdata = pbm;
-		pbm->pci_bus = pbus;
-		pci_fill_in_pbm_cookies(pbus, pbm, pbm->prom_node);
-		pci_record_assignments(pbm, pbus);
-		pci_assign_unassigned(pbm, pbus);
-		pci_fixup_irq(pbm, pbus);
-		pci_determine_66mhz_disposition(pbm, pbus);
-		pci_setup_busmastering(pbm, pbus);
-	}
-
-	if (!sabres_scanned) {
-		/* Hummingbird, no APBs. */
-		pbm = &p->pbm_A;
-		sabre_bus->sysdata = pbm;
-		pbm->pci_bus = sabre_bus;
-		pci_fill_in_pbm_cookies(sabre_bus, pbm, pbm->prom_node);
-		pci_record_assignments(pbm, sabre_bus);
-		pci_assign_unassigned(pbm, sabre_bus);
-		pci_fixup_irq(pbm, sabre_bus);
-		pci_determine_66mhz_disposition(pbm, sabre_bus);
-		pci_setup_busmastering(pbm, sabre_bus);
-	}
+	apb_init(p, pbus);
 
 	sabre_register_error_handlers(p);
 }
@@ -1078,7 +947,7 @@
 			     int tsbsize, unsigned long dvma_offset,
 			     u32 dma_mask)
 {
-	struct pci_iommu *iommu = p->pbm_A.iommu;
+	struct iommu *iommu = p->pbm_A.iommu;
 	unsigned long i;
 	u64 control;
 
@@ -1126,224 +995,31 @@
 	sabre_write(p->pbm_A.controller_regs + SABRE_IOMMU_CONTROL, control);
 }
 
-static void pbm_register_toplevel_resources(struct pci_controller_info *p,
-					    struct pci_pbm_info *pbm)
-{
-	char *name = pbm->name;
-	unsigned long ibase = p->pbm_A.controller_regs + SABRE_IOSPACE;
-	unsigned long mbase = p->pbm_A.controller_regs + SABRE_MEMSPACE;
-	unsigned int devfn;
-	unsigned long first, last, i;
-	u8 *addr, map;
-
-	sprintf(name, "SABRE%d PBM%c",
-		p->index,
-		(pbm == &p->pbm_A ? 'A' : 'B'));
-	pbm->io_space.name = pbm->mem_space.name = name;
-
-	devfn = PCI_DEVFN(1, (pbm == &p->pbm_A) ? 0 : 1);
-	addr = sabre_pci_config_mkaddr(pbm, 0, devfn, APB_IO_ADDRESS_MAP);
-	map = 0;
-	pci_config_read8(addr, &map);
-
-	first = 8;
-	last = 0;
-	for (i = 0; i < 8; i++) {
-		if ((map & (1 << i)) != 0) {
-			if (first > i)
-				first = i;
-			if (last < i)
-				last = i;
-		}
-	}
-	pbm->io_space.start = ibase + (first << 21UL);
-	pbm->io_space.end   = ibase + (last << 21UL) + ((1 << 21UL) - 1);
-	pbm->io_space.flags = IORESOURCE_IO;
-
-	addr = sabre_pci_config_mkaddr(pbm, 0, devfn, APB_MEM_ADDRESS_MAP);
-	map = 0;
-	pci_config_read8(addr, &map);
-
-	first = 8;
-	last = 0;
-	for (i = 0; i < 8; i++) {
-		if ((map & (1 << i)) != 0) {
-			if (first > i)
-				first = i;
-			if (last < i)
-				last = i;
-		}
-	}
-	pbm->mem_space.start = mbase + (first << 29UL);
-	pbm->mem_space.end   = mbase + (last << 29UL) + ((1 << 29UL) - 1);
-	pbm->mem_space.flags = IORESOURCE_MEM;
-
-	if (request_resource(&ioport_resource, &pbm->io_space) < 0) {
-		prom_printf("Cannot register PBM-%c's IO space.\n",
-			    (pbm == &p->pbm_A ? 'A' : 'B'));
-		prom_halt();
-	}
-	if (request_resource(&iomem_resource, &pbm->mem_space) < 0) {
-		prom_printf("Cannot register PBM-%c's MEM space.\n",
-			    (pbm == &p->pbm_A ? 'A' : 'B'));
-		prom_halt();
-	}
-
-	/* Register legacy regions if this PBM covers that area. */
-	if (pbm->io_space.start == ibase &&
-	    pbm->mem_space.start == mbase)
-		pci_register_legacy_regions(&pbm->io_space,
-					    &pbm->mem_space);
-}
-
-static void sabre_pbm_init(struct pci_controller_info *p, struct device_node *dp, u32 dma_start, u32 dma_end)
+static void sabre_pbm_init(struct pci_controller_info *p, struct device_node *dp)
 {
 	struct pci_pbm_info *pbm;
-	struct device_node *node;
-	struct property *prop;
-	u32 *busrange;
-	int len, simbas_found;
 
-	simbas_found = 0;
-	node = dp->child;
-	while (node != NULL) {
-		if (strcmp(node->name, "pci"))
-			goto next_pci;
+	pbm = &p->pbm_A;
+	pbm->name = dp->full_name;
+	printk("%s: SABRE PCI Bus Module\n", pbm->name);
 
-		prop = of_find_property(node, "model", NULL);
-		if (!prop || strncmp(prop->value, "SUNW,simba", prop->length))
-			goto next_pci;
+	pbm->chip_type = PBM_CHIP_TYPE_SABRE;
+	pbm->parent = p;
+	pbm->prom_node = dp;
+	pbm->pci_first_busno = p->pci_first_busno;
+	pbm->pci_last_busno = p->pci_last_busno;
 
-		simbas_found++;
-
-		prop = of_find_property(node, "bus-range", NULL);
-		busrange = prop->value;
-		if (busrange[0] == 1)
-			pbm = &p->pbm_B;
-		else
-			pbm = &p->pbm_A;
-
-		pbm->name = node->full_name;
-		printk("%s: SABRE PCI Bus Module\n", pbm->name);
-
-		pbm->chip_type = PBM_CHIP_TYPE_SABRE;
-		pbm->parent = p;
-		pbm->prom_node = node;
-		pbm->pci_first_slot = 1;
-		pbm->pci_first_busno = busrange[0];
-		pbm->pci_last_busno = busrange[1];
-
-		prop = of_find_property(node, "ranges", &len);
-		if (prop) {
-			pbm->pbm_ranges = prop->value;
-			pbm->num_pbm_ranges =
-				(len / sizeof(struct linux_prom_pci_ranges));
-		} else {
-			pbm->num_pbm_ranges = 0;
-		}
-
-		prop = of_find_property(node, "interrupt-map", &len);
-		if (prop) {
-			pbm->pbm_intmap = prop->value;
-			pbm->num_pbm_intmap =
-				(len / sizeof(struct linux_prom_pci_intmap));
-
-			prop = of_find_property(node, "interrupt-map-mask",
-						NULL);
-			pbm->pbm_intmask = prop->value;
-		} else {
-			pbm->num_pbm_intmap = 0;
-		}
-
-		pbm_register_toplevel_resources(p, pbm);
-
-	next_pci:
-		node = node->sibling;
-	}
-	if (simbas_found == 0) {
-		struct resource *rp;
-
-		/* No APBs underneath, probably this is a hummingbird
-		 * system.
-		 */
-		pbm = &p->pbm_A;
-		pbm->parent = p;
-		pbm->prom_node = dp;
-		pbm->pci_first_busno = p->pci_first_busno;
-		pbm->pci_last_busno = p->pci_last_busno;
-
-		prop = of_find_property(dp, "ranges", &len);
-		if (prop) {
-			pbm->pbm_ranges = prop->value;
-			pbm->num_pbm_ranges =
-				(len / sizeof(struct linux_prom_pci_ranges));
-		} else {
-			pbm->num_pbm_ranges = 0;
-		}
-
-		prop = of_find_property(dp, "interrupt-map", &len);
-		if (prop) {
-			pbm->pbm_intmap = prop->value;
-			pbm->num_pbm_intmap =
-				(len / sizeof(struct linux_prom_pci_intmap));
-
-			prop = of_find_property(dp, "interrupt-map-mask",
-						NULL);
-			pbm->pbm_intmask = prop->value;
-		} else {
-			pbm->num_pbm_intmap = 0;
-		}
-
-		pbm->name = dp->full_name;
-		printk("%s: SABRE PCI Bus Module\n", pbm->name);
-
-		pbm->io_space.name = pbm->mem_space.name = pbm->name;
-
-		/* Hack up top-level resources. */
-		pbm->io_space.start = p->pbm_A.controller_regs + SABRE_IOSPACE;
-		pbm->io_space.end   = pbm->io_space.start + (1UL << 24) - 1UL;
-		pbm->io_space.flags = IORESOURCE_IO;
-
-		pbm->mem_space.start =
-			(p->pbm_A.controller_regs + SABRE_MEMSPACE);
-		pbm->mem_space.end =
-			(pbm->mem_space.start + ((1UL << 32UL) - 1UL));
-		pbm->mem_space.flags = IORESOURCE_MEM;
-
-		if (request_resource(&ioport_resource, &pbm->io_space) < 0) {
-			prom_printf("Cannot register Hummingbird's IO space.\n");
-			prom_halt();
-		}
-		if (request_resource(&iomem_resource, &pbm->mem_space) < 0) {
-			prom_printf("Cannot register Hummingbird's MEM space.\n");
-			prom_halt();
-		}
-
-		rp = kmalloc(sizeof(*rp), GFP_KERNEL);
-		if (!rp) {
-			prom_printf("Cannot allocate IOMMU resource.\n");
-			prom_halt();
-		}
-		rp->name = "IOMMU";
-		rp->start = pbm->mem_space.start + (unsigned long) dma_start;
-		rp->end = pbm->mem_space.start + (unsigned long) dma_end - 1UL;
-		rp->flags = IORESOURCE_BUSY;
-		request_resource(&pbm->mem_space, rp);
-
-		pci_register_legacy_regions(&pbm->io_space,
-					    &pbm->mem_space);
-	}
+	pci_determine_mem_io_space(pbm);
 }
 
 void sabre_init(struct device_node *dp, char *model_name)
 {
-	struct linux_prom64_registers *pr_regs;
+	const struct linux_prom64_registers *pr_regs;
 	struct pci_controller_info *p;
-	struct pci_iommu *iommu;
-	struct property *prop;
+	struct iommu *iommu;
 	int tsbsize;
-	u32 *busrange;
-	u32 *vdma;
+	const u32 *busrange;
+	const u32 *vdma;
 	u32 upa_portid, dma_mask;
 	u64 clear_irq;
 
@@ -1351,13 +1027,9 @@
 	if (!strcmp(model_name, "pci108e,a001"))
 		hummingbird_p = 1;
 	else if (!strcmp(model_name, "SUNW,sabre")) {
-		prop = of_find_property(dp, "compatible", NULL);
-		if (prop) {
-			const char *compat = prop->value;
-
-			if (!strcmp(compat, "pci108e,a001"))
-				hummingbird_p = 1;
-		}
+		const char *compat = of_get_property(dp, "compatible", NULL);
+		if (compat && !strcmp(compat, "pci108e,a001"))
+			hummingbird_p = 1;
 		if (!hummingbird_p) {
 			struct device_node *dp;
 
@@ -1381,37 +1053,28 @@
 		prom_printf("SABRE: Error, kmalloc(pci_iommu) failed.\n");
 		prom_halt();
 	}
-	p->pbm_A.iommu = p->pbm_B.iommu = iommu;
+	p->pbm_A.iommu = iommu;
 
-	upa_portid = 0xff;
-	prop = of_find_property(dp, "upa-portid", NULL);
-	if (prop)
-		upa_portid = *(u32 *) prop->value;
+	upa_portid = of_getintprop_default(dp, "upa-portid", 0xff);
 
 	p->next = pci_controller_root;
 	pci_controller_root = p;
 
 	p->pbm_A.portid = upa_portid;
-	p->pbm_B.portid = upa_portid;
 	p->index = pci_num_controllers++;
-	p->pbms_same_domain = 1;
 	p->scan_bus = sabre_scan_bus;
-	p->base_address_update = sabre_base_address_update;
-	p->resource_adjust = sabre_resource_adjust;
 	p->pci_ops = &sabre_ops;
 
 	/*
 	 * Map in SABRE register set and report the presence of this SABRE.
 	 */
 	
-	prop = of_find_property(dp, "reg", NULL);
-	pr_regs = prop->value;
+	pr_regs = of_get_property(dp, "reg", NULL);
 
 	/*
 	 * First REG in property is base of entire SABRE register space.
 	 */
 	p->pbm_A.controller_regs = pr_regs[0].phys_addr;
-	p->pbm_B.controller_regs = pr_regs[0].phys_addr;
 
 	/* Clear interrupts */
 
@@ -1429,11 +1092,10 @@
 		     SABRE_PCICTRL_ARBPARK | SABRE_PCICTRL_AEN));
 
 	/* Now map in PCI config space for entire SABRE. */
-	p->pbm_A.config_space = p->pbm_B.config_space =
+	p->pbm_A.config_space =
 		(p->pbm_A.controller_regs + SABRE_CONFIGSPACE);
 
-	prop = of_find_property(dp, "virtual-dma", NULL);
-	vdma = prop->value;
+	vdma = of_get_property(dp, "virtual-dma", NULL);
 
 	dma_mask = vdma[0];
 	switch(vdma[1]) {
@@ -1457,13 +1119,12 @@
 
 	sabre_iommu_init(p, tsbsize, vdma[0], dma_mask);
 
-	prop = of_find_property(dp, "bus-range", NULL);
-	busrange = prop->value;
+	busrange = of_get_property(dp, "bus-range", NULL);
 	p->pci_first_busno = busrange[0];
 	p->pci_last_busno = busrange[1];
 
 	/*
 	 * Look for APB underneath.
 	 */
-	sabre_pbm_init(p, dp, vdma[0], vdma[0] + vdma[1]);
+	sabre_pbm_init(p, dp);
 }
diff --git a/arch/sparc64/kernel/pci_schizo.c b/arch/sparc64/kernel/pci_schizo.c
index 66911b1..91a7385 100644
--- a/arch/sparc64/kernel/pci_schizo.c
+++ b/arch/sparc64/kernel/pci_schizo.c
@@ -1,7 +1,6 @@
-/* $Id: pci_schizo.c,v 1.24 2002/01/23 11:27:32 davem Exp $
- * pci_schizo.c: SCHIZO/TOMATILLO specific PCI controller support.
+/* pci_schizo.c: SCHIZO/TOMATILLO specific PCI controller support.
  *
- * Copyright (C) 2001, 2002, 2003 David S. Miller (davem@redhat.com)
+ * Copyright (C) 2001, 2002, 2003, 2007 David S. Miller (davem@davemloft.net)
  */
 
 #include <linux/kernel.h>
@@ -126,6 +125,9 @@
 	u16 tmp16;
 	u8 tmp8;
 
+	if (bus_dev == pbm->pci_bus && devfn == 0x00)
+		return pci_host_bridge_read_pci_cfg(bus_dev, devfn, where,
+						    size, value);
 	switch (size) {
 	case 1:
 		*value = 0xff;
@@ -179,6 +181,9 @@
 	unsigned char bus = bus_dev->number;
 	u32 *addr;
 
+	if (bus_dev == pbm->pci_bus && devfn == 0x00)
+		return pci_host_bridge_write_pci_cfg(bus_dev, devfn, where,
+						     size, value);
 	addr = schizo_pci_config_mkaddr(pbm, bus, devfn, where);
 	if (!addr)
 		return PCIBIOS_SUCCESSFUL;
@@ -274,7 +279,7 @@
 static void __schizo_check_stc_error_pbm(struct pci_pbm_info *pbm,
 					 enum schizo_error_type type)
 {
-	struct pci_strbuf *strbuf = &pbm->stc;
+	struct strbuf *strbuf = &pbm->stc;
 	unsigned long regbase = pbm->pbm_regs;
 	unsigned long err_base, tag_base, line_base;
 	u64 control;
@@ -382,7 +387,7 @@
 static void schizo_check_iommu_error_pbm(struct pci_pbm_info *pbm,
 					 enum schizo_error_type type)
 {
-	struct pci_iommu *iommu = pbm->iommu;
+	struct iommu *iommu = pbm->iommu;
 	unsigned long iommu_tag[16];
 	unsigned long iommu_data[16];
 	unsigned long flags;
@@ -1229,42 +1234,8 @@
 	pci_config_write8(addr, 64);
 }
 
-static void pbm_scan_bus(struct pci_controller_info *p,
-			 struct pci_pbm_info *pbm)
+static void schizo_scan_bus(struct pci_controller_info *p)
 {
-	struct pcidev_cookie *cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
-
-	if (!cookie) {
-		prom_printf("%s: Critical allocation failure.\n", pbm->name);
-		prom_halt();
-	}
-
-	/* All we care about is the PBM. */
-	cookie->pbm = pbm;
-
-	pbm->pci_bus = pci_scan_bus(pbm->pci_first_busno,
-				    p->pci_ops,
-				    pbm);
-	pci_fixup_host_bridge_self(pbm->pci_bus);
-	pbm->pci_bus->self->sysdata = cookie;
-
-	pci_fill_in_pbm_cookies(pbm->pci_bus, pbm, pbm->prom_node);
-	pci_record_assignments(pbm, pbm->pci_bus);
-	pci_assign_unassigned(pbm, pbm->pci_bus);
-	pci_fixup_irq(pbm, pbm->pci_bus);
-	pci_determine_66mhz_disposition(pbm, pbm->pci_bus);
-	pci_setup_busmastering(pbm, pbm->pci_bus);
-}
-
-static void __schizo_scan_bus(struct pci_controller_info *p,
-			      int chip_type)
-{
-	if (!p->pbm_B.prom_node || !p->pbm_A.prom_node) {
-		printk("PCI: Only one PCI bus module of controller found.\n");
-		printk("PCI: Ignoring entire controller.\n");
-		return;
-	}
-
 	pbm_config_busmastering(&p->pbm_B);
 	p->pbm_B.is_66mhz_capable =
 		(of_find_property(p->pbm_B.prom_node, "66mhz-capable", NULL)
@@ -1273,154 +1244,19 @@
 	p->pbm_A.is_66mhz_capable =
 		(of_find_property(p->pbm_A.prom_node, "66mhz-capable", NULL)
 		 != NULL);
-	pbm_scan_bus(p, &p->pbm_B);
-	pbm_scan_bus(p, &p->pbm_A);
+
+	p->pbm_B.pci_bus = pci_scan_one_pbm(&p->pbm_B);
+	p->pbm_A.pci_bus = pci_scan_one_pbm(&p->pbm_A);
 
 	/* After the PCI bus scan is complete, we can register
 	 * the error interrupt handlers.
 	 */
-	if (chip_type == PBM_CHIP_TYPE_TOMATILLO)
+	if (p->pbm_B.chip_type == PBM_CHIP_TYPE_TOMATILLO)
 		tomatillo_register_error_handlers(p);
 	else
 		schizo_register_error_handlers(p);
 }
 
-static void schizo_scan_bus(struct pci_controller_info *p)
-{
-	__schizo_scan_bus(p, PBM_CHIP_TYPE_SCHIZO);
-}
-
-static void tomatillo_scan_bus(struct pci_controller_info *p)
-{
-	__schizo_scan_bus(p, PBM_CHIP_TYPE_TOMATILLO);
-}
-
-static void schizo_base_address_update(struct pci_dev *pdev, int resource)
-{
-	struct pcidev_cookie *pcp = pdev->sysdata;
-	struct pci_pbm_info *pbm = pcp->pbm;
-	struct resource *res, *root;
-	u32 reg;
-	int where, size, is_64bit;
-
-	res = &pdev->resource[resource];
-	if (resource < 6) {
-		where = PCI_BASE_ADDRESS_0 + (resource * 4);
-	} else if (resource == PCI_ROM_RESOURCE) {
-		where = pdev->rom_base_reg;
-	} else {
-		/* Somebody might have asked allocation of a non-standard resource */
-		return;
-	}
-
-	is_64bit = 0;
-	if (res->flags & IORESOURCE_IO)
-		root = &pbm->io_space;
-	else {
-		root = &pbm->mem_space;
-		if ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK)
-		    == PCI_BASE_ADDRESS_MEM_TYPE_64)
-			is_64bit = 1;
-	}
-
-	size = res->end - res->start;
-	pci_read_config_dword(pdev, where, &reg);
-	reg = ((reg & size) |
-	       (((u32)(res->start - root->start)) & ~size));
-	if (resource == PCI_ROM_RESOURCE) {
-		reg |= PCI_ROM_ADDRESS_ENABLE;
-		res->flags |= IORESOURCE_ROM_ENABLE;
-	}
-	pci_write_config_dword(pdev, where, reg);
-
-	/* This knows that the upper 32-bits of the address
-	 * must be zero.  Our PCI common layer enforces this.
-	 */
-	if (is_64bit)
-		pci_write_config_dword(pdev, where + 4, 0);
-}
-
-static void schizo_resource_adjust(struct pci_dev *pdev,
-				   struct resource *res,
-				   struct resource *root)
-{
-	res->start += root->start;
-	res->end += root->start;
-}
-
-/* Use ranges property to determine where PCI MEM, I/O, and Config
- * space are for this PCI bus module.
- */
-static void schizo_determine_mem_io_space(struct pci_pbm_info *pbm)
-{
-	int i, saw_cfg, saw_mem, saw_io;
-
-	saw_cfg = saw_mem = saw_io = 0;
-	for (i = 0; i < pbm->num_pbm_ranges; i++) {
-		struct linux_prom_pci_ranges *pr = &pbm->pbm_ranges[i];
-		unsigned long a;
-		int type;
-
-		type = (pr->child_phys_hi >> 24) & 0x3;
-		a = (((unsigned long)pr->parent_phys_hi << 32UL) |
-		     ((unsigned long)pr->parent_phys_lo  <<  0UL));
-
-		switch (type) {
-		case 0:
-			/* PCI config space, 16MB */
-			pbm->config_space = a;
-			saw_cfg = 1;
-			break;
-
-		case 1:
-			/* 16-bit IO space, 16MB */
-			pbm->io_space.start = a;
-			pbm->io_space.end = a + ((16UL*1024UL*1024UL) - 1UL);
-			pbm->io_space.flags = IORESOURCE_IO;
-			saw_io = 1;
-			break;
-
-		case 2:
-			/* 32-bit MEM space, 2GB */
-			pbm->mem_space.start = a;
-			pbm->mem_space.end = a + (0x80000000UL - 1UL);
-			pbm->mem_space.flags = IORESOURCE_MEM;
-			saw_mem = 1;
-			break;
-
-		default:
-			break;
-		};
-	}
-
-	if (!saw_cfg || !saw_io || !saw_mem) {
-		prom_printf("%s: Fatal error, missing %s PBM range.\n",
-			    pbm->name,
-			    ((!saw_cfg ?
-			      "CFG" :
-			      (!saw_io ?
-			       "IO" : "MEM"))));
-		prom_halt();
-	}
-
-	printk("%s: PCI CFG[%lx] IO[%lx] MEM[%lx]\n",
-	       pbm->name,
-	       pbm->config_space,
-	       pbm->io_space.start,
-	       pbm->mem_space.start);
-}
-
-static void pbm_register_toplevel_resources(struct pci_controller_info *p,
-					    struct pci_pbm_info *pbm)
-{
-	pbm->io_space.name = pbm->mem_space.name = pbm->name;
-
-	request_resource(&ioport_resource, &pbm->io_space);
-	request_resource(&iomem_resource, &pbm->mem_space);
-	pci_register_legacy_regions(&pbm->io_space,
-				    &pbm->mem_space);
-}
-
 #define SCHIZO_STRBUF_CONTROL		(0x02800UL)
 #define SCHIZO_STRBUF_FLUSH		(0x02808UL)
 #define SCHIZO_STRBUF_FSYNC		(0x02810UL)
@@ -1472,7 +1308,7 @@
 
 static void schizo_pbm_iommu_init(struct pci_pbm_info *pbm)
 {
-	struct pci_iommu *iommu = pbm->iommu;
+	struct iommu *iommu = pbm->iommu;
 	unsigned long i, tagbase, database;
 	struct property *prop;
 	u32 vdma[2], dma_mask;
@@ -1654,14 +1490,12 @@
 			    struct device_node *dp, u32 portid,
 			    int chip_type)
 {
-	struct linux_prom64_registers *regs;
-	struct property *prop;
-	unsigned int *busrange;
+	const struct linux_prom64_registers *regs;
+	const unsigned int *busrange;
 	struct pci_pbm_info *pbm;
 	const char *chipset_name;
-	u32 *ino_bitmap;
+	const u32 *ino_bitmap;
 	int is_pbm_a;
-	int len;
 
 	switch (chip_type) {
 	case PBM_CHIP_TYPE_TOMATILLO:
@@ -1689,11 +1523,9 @@
 	 * 3) PBM PCI config space
 	 * 4) Ichip regs
 	 */
-	prop = of_find_property(dp, "reg", NULL);
-	regs = prop->value;
+	regs = of_get_property(dp, "reg", NULL);
 
 	is_pbm_a = ((regs[0].phys_addr & 0x00700000) == 0x00600000);
-
 	if (is_pbm_a)
 		pbm = &p->pbm_A;
 	else
@@ -1702,17 +1534,10 @@
 	pbm->portid = portid;
 	pbm->parent = p;
 	pbm->prom_node = dp;
-	pbm->pci_first_slot = 1;
 
 	pbm->chip_type = chip_type;
-	pbm->chip_version = 0;
-	prop = of_find_property(dp, "version#", NULL);
-	if (prop)
-		pbm->chip_version = *(int *) prop->value;
-	pbm->chip_revision = 0;
-	prop = of_find_property(dp, "module-revision#", NULL);
-	if (prop)
-		pbm->chip_revision = *(int *) prop->value;
+	pbm->chip_version = of_getintprop_default(dp, "version#", 0);
+	pbm->chip_revision = of_getintprop_default(dp, "module-version#", 0);
 
 	pbm->pbm_regs = regs[0].phys_addr;
 	pbm->controller_regs = regs[1].phys_addr - 0x10000UL;
@@ -1723,40 +1548,18 @@
 	pbm->name = dp->full_name;
 
 	printk("%s: %s PCI Bus Module ver[%x:%x]\n",
-	       pbm->name,
-	       (chip_type == PBM_CHIP_TYPE_TOMATILLO ?
-		"TOMATILLO" : "SCHIZO"),
+	       pbm->name, chipset_name,
 	       pbm->chip_version, pbm->chip_revision);
 
 	schizo_pbm_hw_init(pbm);
 
-	prop = of_find_property(dp, "ranges", &len);
-	pbm->pbm_ranges = prop->value;
-	pbm->num_pbm_ranges =
-		(len / sizeof(struct linux_prom_pci_ranges));
+	pci_determine_mem_io_space(pbm);
 
-	schizo_determine_mem_io_space(pbm);
-	pbm_register_toplevel_resources(p, pbm);
-
-	prop = of_find_property(dp, "interrupt-map", &len);
-	if (prop) {
-		pbm->pbm_intmap = prop->value;
-		pbm->num_pbm_intmap =
-			(len / sizeof(struct linux_prom_pci_intmap));
-
-		prop = of_find_property(dp, "interrupt-map-mask", NULL);
-		pbm->pbm_intmask = prop->value;
-	} else {
-		pbm->num_pbm_intmap = 0;
-	}
-
-	prop = of_find_property(dp, "ino-bitmap", NULL);
-	ino_bitmap = prop->value;
+	ino_bitmap = of_get_property(dp, "ino-bitmap", NULL);
 	pbm->ino_bitmap = (((u64)ino_bitmap[1] << 32UL) |
 			   ((u64)ino_bitmap[0] <<  0UL));
 
-	prop = of_find_property(dp, "bus-range", NULL);
-	busrange = prop->value;
+	busrange = of_get_property(dp, "bus-range", NULL);
 	pbm->pci_first_busno = busrange[0];
 	pbm->pci_last_busno = busrange[1];
 
@@ -1777,15 +1580,10 @@
 static void __schizo_init(struct device_node *dp, char *model_name, int chip_type)
 {
 	struct pci_controller_info *p;
-	struct pci_iommu *iommu;
-	struct property *prop;
-	int is_pbm_a;
+	struct iommu *iommu;
 	u32 portid;
 
-	portid = 0xff;
-	prop = of_find_property(dp, "portid", NULL);
-	if (prop)
-		portid = *(u32 *) prop->value;
+	portid = of_getintprop_default(dp, "portid", 0xff);
 
 	for (p = pci_controller_root; p; p = p->next) {
 		struct pci_pbm_info *pbm;
@@ -1798,48 +1596,43 @@
 		       &p->pbm_B);
 
 		if (portid_compare(pbm->portid, portid, chip_type)) {
-			is_pbm_a = (p->pbm_A.prom_node == NULL);
 			schizo_pbm_init(p, dp, portid, chip_type);
 			return;
 		}
 	}
 
 	p = kzalloc(sizeof(struct pci_controller_info), GFP_ATOMIC);
-	if (!p) {
-		prom_printf("SCHIZO: Fatal memory allocation error.\n");
-		prom_halt();
-	}
+	if (!p)
+		goto memfail;
 
-	iommu = kzalloc(sizeof(struct pci_iommu), GFP_ATOMIC);
-	if (!iommu) {
-		prom_printf("SCHIZO: Fatal memory allocation error.\n");
-		prom_halt();
-	}
+	iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC);
+	if (!iommu)
+		goto memfail;
+
 	p->pbm_A.iommu = iommu;
 
-	iommu = kzalloc(sizeof(struct pci_iommu), GFP_ATOMIC);
-	if (!iommu) {
-		prom_printf("SCHIZO: Fatal memory allocation error.\n");
-		prom_halt();
-	}
+	iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC);
+	if (!iommu)
+		goto memfail;
+
 	p->pbm_B.iommu = iommu;
 
 	p->next = pci_controller_root;
 	pci_controller_root = p;
 
 	p->index = pci_num_controllers++;
-	p->pbms_same_domain = 0;
-	p->scan_bus = (chip_type == PBM_CHIP_TYPE_TOMATILLO ?
-		       tomatillo_scan_bus :
-		       schizo_scan_bus);
-	p->base_address_update = schizo_base_address_update;
-	p->resource_adjust = schizo_resource_adjust;
+	p->scan_bus = schizo_scan_bus;
 	p->pci_ops = &schizo_ops;
 
 	/* Like PSYCHO we have a 2GB aligned area for memory space. */
 	pci_memspace_mask = 0x7fffffffUL;
 
 	schizo_pbm_init(p, dp, portid, chip_type);
+	return;
+
+memfail:
+	prom_printf("SCHIZO: Fatal memory allocation error.\n");
+	prom_halt();
 }
 
 void schizo_init(struct device_node *dp, char *model_name)
diff --git a/arch/sparc64/kernel/pci_sun4v.c b/arch/sparc64/kernel/pci_sun4v.c
index ec22cd6..94295c2 100644
--- a/arch/sparc64/kernel/pci_sun4v.c
+++ b/arch/sparc64/kernel/pci_sun4v.c
@@ -1,6 +1,6 @@
 /* pci_sun4v.c: SUN4V specific PCI controller support.
  *
- * Copyright (C) 2006 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 2006, 2007 David S. Miller (davem@davemloft.net)
  */
 
 #include <linux/kernel.h>
@@ -29,7 +29,7 @@
 
 #define PGLIST_NENTS	(PAGE_SIZE / sizeof(u64))
 
-struct pci_iommu_batch {
+struct iommu_batch {
 	struct pci_dev	*pdev;		/* Device mapping is for.	*/
 	unsigned long	prot;		/* IOMMU page protections	*/
 	unsigned long	entry;		/* Index into IOTSB.		*/
@@ -37,12 +37,12 @@
 	unsigned long	npages;		/* Number of pages in list.	*/
 };
 
-static DEFINE_PER_CPU(struct pci_iommu_batch, pci_iommu_batch);
+static DEFINE_PER_CPU(struct iommu_batch, pci_iommu_batch);
 
 /* Interrupts must be disabled.  */
 static inline void pci_iommu_batch_start(struct pci_dev *pdev, unsigned long prot, unsigned long entry)
 {
-	struct pci_iommu_batch *p = &__get_cpu_var(pci_iommu_batch);
+	struct iommu_batch *p = &__get_cpu_var(pci_iommu_batch);
 
 	p->pdev		= pdev;
 	p->prot		= prot;
@@ -51,10 +51,10 @@
 }
 
 /* Interrupts must be disabled.  */
-static long pci_iommu_batch_flush(struct pci_iommu_batch *p)
+static long pci_iommu_batch_flush(struct iommu_batch *p)
 {
-	struct pcidev_cookie *pcp = p->pdev->sysdata;
-	unsigned long devhandle = pcp->pbm->devhandle;
+	struct pci_pbm_info *pbm = p->pdev->dev.archdata.host_controller;
+	unsigned long devhandle = pbm->devhandle;
 	unsigned long prot = p->prot;
 	unsigned long entry = p->entry;
 	u64 *pglist = p->pglist;
@@ -89,7 +89,7 @@
 /* Interrupts must be disabled.  */
 static inline long pci_iommu_batch_add(u64 phys_page)
 {
-	struct pci_iommu_batch *p = &__get_cpu_var(pci_iommu_batch);
+	struct iommu_batch *p = &__get_cpu_var(pci_iommu_batch);
 
 	BUG_ON(p->npages >= PGLIST_NENTS);
 
@@ -103,14 +103,14 @@
 /* Interrupts must be disabled.  */
 static inline long pci_iommu_batch_end(void)
 {
-	struct pci_iommu_batch *p = &__get_cpu_var(pci_iommu_batch);
+	struct iommu_batch *p = &__get_cpu_var(pci_iommu_batch);
 
 	BUG_ON(p->npages >= PGLIST_NENTS);
 
 	return pci_iommu_batch_flush(p);
 }
 
-static long pci_arena_alloc(struct pci_iommu_arena *arena, unsigned long npages)
+static long pci_arena_alloc(struct iommu_arena *arena, unsigned long npages)
 {
 	unsigned long n, i, start, end, limit;
 	int pass;
@@ -149,7 +149,7 @@
 	return n;
 }
 
-static void pci_arena_free(struct pci_iommu_arena *arena, unsigned long base, unsigned long npages)
+static void pci_arena_free(struct iommu_arena *arena, unsigned long base, unsigned long npages)
 {
 	unsigned long i;
 
@@ -159,8 +159,7 @@
 
 static void *pci_4v_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_addrp, gfp_t gfp)
 {
-	struct pcidev_cookie *pcp;
-	struct pci_iommu *iommu;
+	struct iommu *iommu;
 	unsigned long flags, order, first_page, npages, n;
 	void *ret;
 	long entry;
@@ -178,8 +177,7 @@
 
 	memset((char *)first_page, 0, PAGE_SIZE << order);
 
-	pcp = pdev->sysdata;
-	iommu = pcp->pbm->iommu;
+	iommu = pdev->dev.archdata.iommu;
 
 	spin_lock_irqsave(&iommu->lock, flags);
 	entry = pci_arena_alloc(&iommu->arena, npages);
@@ -226,15 +224,15 @@
 
 static void pci_4v_free_consistent(struct pci_dev *pdev, size_t size, void *cpu, dma_addr_t dvma)
 {
-	struct pcidev_cookie *pcp;
-	struct pci_iommu *iommu;
+	struct pci_pbm_info *pbm;
+	struct iommu *iommu;
 	unsigned long flags, order, npages, entry;
 	u32 devhandle;
 
 	npages = IO_PAGE_ALIGN(size) >> IO_PAGE_SHIFT;
-	pcp = pdev->sysdata;
-	iommu = pcp->pbm->iommu;
-	devhandle = pcp->pbm->devhandle;
+	iommu = pdev->dev.archdata.iommu;
+	pbm = pdev->dev.archdata.host_controller;
+	devhandle = pbm->devhandle;
 	entry = ((dvma - iommu->page_table_map_base) >> IO_PAGE_SHIFT);
 
 	spin_lock_irqsave(&iommu->lock, flags);
@@ -259,16 +257,14 @@
 
 static dma_addr_t pci_4v_map_single(struct pci_dev *pdev, void *ptr, size_t sz, int direction)
 {
-	struct pcidev_cookie *pcp;
-	struct pci_iommu *iommu;
+	struct iommu *iommu;
 	unsigned long flags, npages, oaddr;
 	unsigned long i, base_paddr;
 	u32 bus_addr, ret;
 	unsigned long prot;
 	long entry;
 
-	pcp = pdev->sysdata;
-	iommu = pcp->pbm->iommu;
+	iommu = pdev->dev.archdata.iommu;
 
 	if (unlikely(direction == PCI_DMA_NONE))
 		goto bad;
@@ -324,8 +320,8 @@
 
 static void pci_4v_unmap_single(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz, int direction)
 {
-	struct pcidev_cookie *pcp;
-	struct pci_iommu *iommu;
+	struct pci_pbm_info *pbm;
+	struct iommu *iommu;
 	unsigned long flags, npages;
 	long entry;
 	u32 devhandle;
@@ -336,9 +332,9 @@
 		return;
 	}
 
-	pcp = pdev->sysdata;
-	iommu = pcp->pbm->iommu;
-	devhandle = pcp->pbm->devhandle;
+	iommu = pdev->dev.archdata.iommu;
+	pbm = pdev->dev.archdata.host_controller;
+	devhandle = pbm->devhandle;
 
 	npages = IO_PAGE_ALIGN(bus_addr + sz) - (bus_addr & IO_PAGE_MASK);
 	npages >>= IO_PAGE_SHIFT;
@@ -460,8 +456,7 @@
 
 static int pci_4v_map_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction)
 {
-	struct pcidev_cookie *pcp;
-	struct pci_iommu *iommu;
+	struct iommu *iommu;
 	unsigned long flags, npages, prot;
 	u32 dma_base;
 	struct scatterlist *sgtmp;
@@ -480,8 +475,7 @@
 		return 1;
 	}
 
-	pcp = pdev->sysdata;
-	iommu = pcp->pbm->iommu;
+	iommu = pdev->dev.archdata.iommu;
 	
 	if (unlikely(direction == PCI_DMA_NONE))
 		goto bad;
@@ -537,8 +531,8 @@
 
 static void pci_4v_unmap_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction)
 {
-	struct pcidev_cookie *pcp;
-	struct pci_iommu *iommu;
+	struct pci_pbm_info *pbm;
+	struct iommu *iommu;
 	unsigned long flags, i, npages;
 	long entry;
 	u32 devhandle, bus_addr;
@@ -548,9 +542,9 @@
 			WARN_ON(1);
 	}
 
-	pcp = pdev->sysdata;
-	iommu = pcp->pbm->iommu;
-	devhandle = pcp->pbm->devhandle;
+	iommu = pdev->dev.archdata.iommu;
+	pbm = pdev->dev.archdata.host_controller;
+	devhandle = pbm->devhandle;
 	
 	bus_addr = sglist->dma_address & IO_PAGE_MASK;
 
@@ -589,7 +583,7 @@
 	/* Nothing to do... */
 }
 
-struct pci_iommu_ops pci_sun4v_iommu_ops = {
+const struct pci_iommu_ops pci_sun4v_iommu_ops = {
 	.alloc_consistent		= pci_4v_alloc_consistent,
 	.free_consistent		= pci_4v_free_consistent,
 	.map_single			= pci_4v_map_single,
@@ -600,132 +594,12 @@
 	.dma_sync_sg_for_cpu		= pci_4v_dma_sync_sg_for_cpu,
 };
 
-/* SUN4V PCI configuration space accessors. */
-
-struct pdev_entry {
-	struct pdev_entry	*next;
-	u32			devhandle;
-	unsigned int		bus;
-	unsigned int		device;
-	unsigned int		func;
-};
-
-#define PDEV_HTAB_SIZE	16
-#define PDEV_HTAB_MASK	(PDEV_HTAB_SIZE - 1)
-static struct pdev_entry *pdev_htab[PDEV_HTAB_SIZE];
-
-static inline unsigned int pdev_hashfn(u32 devhandle, unsigned int bus, unsigned int device, unsigned int func)
-{
-	unsigned int val;
-
-	val = (devhandle ^ (devhandle >> 4));
-	val ^= bus;
-	val ^= device;
-	val ^= func;
-
-	return val & PDEV_HTAB_MASK;
-}
-
-static int pdev_htab_add(u32 devhandle, unsigned int bus, unsigned int device, unsigned int func)
-{
-	struct pdev_entry *p = kmalloc(sizeof(*p), GFP_KERNEL);
-	struct pdev_entry **slot;
-
-	if (!p)
-		return -ENOMEM;
-
-	slot = &pdev_htab[pdev_hashfn(devhandle, bus, device, func)];
-	p->next = *slot;
-	*slot = p;
-
-	p->devhandle = devhandle;
-	p->bus = bus;
-	p->device = device;
-	p->func = func;
-
-	return 0;
-}
-
-/* Recursively descend into the OBP device tree, rooted at toplevel_node,
- * looking for a PCI device matching bus and devfn.
- */
-static int obp_find(struct device_node *toplevel_node, unsigned int bus, unsigned int devfn)
-{
-	toplevel_node = toplevel_node->child;
-
-	while (toplevel_node != NULL) {
-		struct linux_prom_pci_registers *regs;
-		struct property *prop;
-		int ret;
-
-		ret = obp_find(toplevel_node, bus, devfn);
-		if (ret != 0)
-			return ret;
-
-		prop = of_find_property(toplevel_node, "reg", NULL);
-		if (!prop)
-			goto next_sibling;
-
-		regs = prop->value;
-		if (((regs->phys_hi >> 16) & 0xff) == bus &&
-		    ((regs->phys_hi >> 8) & 0xff) == devfn)
-			break;
-
-	next_sibling:
-		toplevel_node = toplevel_node->sibling;
-	}
-
-	return toplevel_node != NULL;
-}
-
-static int pdev_htab_populate(struct pci_pbm_info *pbm)
-{
-	u32 devhandle = pbm->devhandle;
-	unsigned int bus;
-
-	for (bus = pbm->pci_first_busno; bus <= pbm->pci_last_busno; bus++) {
-		unsigned int devfn;
-
-		for (devfn = 0; devfn < 256; devfn++) {
-			unsigned int device = PCI_SLOT(devfn);
-			unsigned int func = PCI_FUNC(devfn);
-
-			if (obp_find(pbm->prom_node, bus, devfn)) {
-				int err = pdev_htab_add(devhandle, bus,
-							device, func);
-				if (err)
-					return err;
-			}
-		}
-	}
-
-	return 0;
-}
-
-static struct pdev_entry *pdev_find(u32 devhandle, unsigned int bus, unsigned int device, unsigned int func)
-{
-	struct pdev_entry *p;
-
-	p = pdev_htab[pdev_hashfn(devhandle, bus, device, func)];
-	while (p) {
-		if (p->devhandle == devhandle &&
-		    p->bus == bus &&
-		    p->device == device &&
-		    p->func == func)
-			break;
-
-		p = p->next;
-	}
-
-	return p;
-}
-
 static inline int pci_sun4v_out_of_range(struct pci_pbm_info *pbm, unsigned int bus, unsigned int device, unsigned int func)
 {
 	if (bus < pbm->pci_first_busno ||
 	    bus > pbm->pci_last_busno)
 		return 1;
-	return pdev_find(pbm->devhandle, bus, device, func) == NULL;
+	return 0;
 }
 
 static int pci_sun4v_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
@@ -738,6 +612,9 @@
 	unsigned int func = PCI_FUNC(devfn);
 	unsigned long ret;
 
+	if (bus_dev == pbm->pci_bus && devfn == 0x00)
+		return pci_host_bridge_read_pci_cfg(bus_dev, devfn, where,
+						    size, value);
 	if (pci_sun4v_out_of_range(pbm, bus, device, func)) {
 		ret = ~0UL;
 	} else {
@@ -776,6 +653,9 @@
 	unsigned int func = PCI_FUNC(devfn);
 	unsigned long ret;
 
+	if (bus_dev == pbm->pci_bus && devfn == 0x00)
+		return pci_host_bridge_write_pci_cfg(bus_dev, devfn, where,
+						     size, value);
 	if (pci_sun4v_out_of_range(pbm, bus, device, func)) {
 		/* Do nothing. */
 	} else {
@@ -800,27 +680,7 @@
 static void pbm_scan_bus(struct pci_controller_info *p,
 			 struct pci_pbm_info *pbm)
 {
-	struct pcidev_cookie *cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
-
-	if (!cookie) {
-		prom_printf("%s: Critical allocation failure.\n", pbm->name);
-		prom_halt();
-	}
-
-	/* All we care about is the PBM. */
-	cookie->pbm = pbm;
-
-	pbm->pci_bus = pci_scan_bus(pbm->pci_first_busno, p->pci_ops, pbm);
-#if 0
-	pci_fixup_host_bridge_self(pbm->pci_bus);
-	pbm->pci_bus->self->sysdata = cookie;
-#endif
-	pci_fill_in_pbm_cookies(pbm->pci_bus, pbm, pbm->prom_node);
-	pci_record_assignments(pbm, pbm->pci_bus);
-	pci_assign_unassigned(pbm, pbm->pci_bus);
-	pci_fixup_irq(pbm, pbm->pci_bus);
-	pci_determine_66mhz_disposition(pbm, pbm->pci_bus);
-	pci_setup_busmastering(pbm, pbm->pci_bus);
+	pbm->pci_bus = pci_scan_one_pbm(pbm);
 }
 
 static void pci_sun4v_scan_bus(struct pci_controller_info *p)
@@ -844,130 +704,10 @@
 	/* XXX register error interrupt handlers XXX */
 }
 
-static void pci_sun4v_base_address_update(struct pci_dev *pdev, int resource)
-{
-	struct pcidev_cookie *pcp = pdev->sysdata;
-	struct pci_pbm_info *pbm = pcp->pbm;
-	struct resource *res, *root;
-	u32 reg;
-	int where, size, is_64bit;
-
-	res = &pdev->resource[resource];
-	if (resource < 6) {
-		where = PCI_BASE_ADDRESS_0 + (resource * 4);
-	} else if (resource == PCI_ROM_RESOURCE) {
-		where = pdev->rom_base_reg;
-	} else {
-		/* Somebody might have asked allocation of a non-standard resource */
-		return;
-	}
-
-	/* XXX 64-bit MEM handling is not %100 correct... XXX */
-	is_64bit = 0;
-	if (res->flags & IORESOURCE_IO)
-		root = &pbm->io_space;
-	else {
-		root = &pbm->mem_space;
-		if ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK)
-		    == PCI_BASE_ADDRESS_MEM_TYPE_64)
-			is_64bit = 1;
-	}
-
-	size = res->end - res->start;
-	pci_read_config_dword(pdev, where, &reg);
-	reg = ((reg & size) |
-	       (((u32)(res->start - root->start)) & ~size));
-	if (resource == PCI_ROM_RESOURCE) {
-		reg |= PCI_ROM_ADDRESS_ENABLE;
-		res->flags |= IORESOURCE_ROM_ENABLE;
-	}
-	pci_write_config_dword(pdev, where, reg);
-
-	/* This knows that the upper 32-bits of the address
-	 * must be zero.  Our PCI common layer enforces this.
-	 */
-	if (is_64bit)
-		pci_write_config_dword(pdev, where + 4, 0);
-}
-
-static void pci_sun4v_resource_adjust(struct pci_dev *pdev,
-				      struct resource *res,
-				      struct resource *root)
-{
-	res->start += root->start;
-	res->end += root->start;
-}
-
-/* Use ranges property to determine where PCI MEM, I/O, and Config
- * space are for this PCI bus module.
- */
-static void pci_sun4v_determine_mem_io_space(struct pci_pbm_info *pbm)
-{
-	int i, saw_mem, saw_io;
-
-	saw_mem = saw_io = 0;
-	for (i = 0; i < pbm->num_pbm_ranges; i++) {
-		struct linux_prom_pci_ranges *pr = &pbm->pbm_ranges[i];
-		unsigned long a;
-		int type;
-
-		type = (pr->child_phys_hi >> 24) & 0x3;
-		a = (((unsigned long)pr->parent_phys_hi << 32UL) |
-		     ((unsigned long)pr->parent_phys_lo  <<  0UL));
-
-		switch (type) {
-		case 1:
-			/* 16-bit IO space, 16MB */
-			pbm->io_space.start = a;
-			pbm->io_space.end = a + ((16UL*1024UL*1024UL) - 1UL);
-			pbm->io_space.flags = IORESOURCE_IO;
-			saw_io = 1;
-			break;
-
-		case 2:
-			/* 32-bit MEM space, 2GB */
-			pbm->mem_space.start = a;
-			pbm->mem_space.end = a + (0x80000000UL - 1UL);
-			pbm->mem_space.flags = IORESOURCE_MEM;
-			saw_mem = 1;
-			break;
-
-		case 3:
-			/* XXX 64-bit MEM handling XXX */
-
-		default:
-			break;
-		};
-	}
-
-	if (!saw_io || !saw_mem) {
-		prom_printf("%s: Fatal error, missing %s PBM range.\n",
-			    pbm->name,
-			    (!saw_io ? "IO" : "MEM"));
-		prom_halt();
-	}
-
-	printk("%s: PCI IO[%lx] MEM[%lx]\n",
-	       pbm->name,
-	       pbm->io_space.start,
-	       pbm->mem_space.start);
-}
-
-static void pbm_register_toplevel_resources(struct pci_controller_info *p,
-					    struct pci_pbm_info *pbm)
-{
-	pbm->io_space.name = pbm->mem_space.name = pbm->name;
-
-	request_resource(&ioport_resource, &pbm->io_space);
-	request_resource(&iomem_resource, &pbm->mem_space);
-	pci_register_legacy_regions(&pbm->io_space,
-				    &pbm->mem_space);
-}
-
 static unsigned long probe_existing_entries(struct pci_pbm_info *pbm,
-					    struct pci_iommu *iommu)
+					    struct iommu *iommu)
 {
-	struct pci_iommu_arena *arena = &iommu->arena;
+	struct iommu_arena *arena = &iommu->arena;
 	unsigned long i, cnt = 0;
 	u32 devhandle;
 
@@ -994,7 +734,7 @@
 
 static void pci_sun4v_iommu_init(struct pci_pbm_info *pbm)
 {
-	struct pci_iommu *iommu = pbm->iommu;
+	struct iommu *iommu = pbm->iommu;
 	struct property *prop;
 	unsigned long num_tsb_entries, sz;
 	u32 vdma[2], dma_mask, dma_offset;
@@ -1281,7 +1021,7 @@
 
 static void pci_sun4v_msi_init(struct pci_pbm_info *pbm)
 {
-	u32 *val;
+	const u32 *val;
 	int len;
 
 	val = of_get_property(pbm->prom_node, "#msi-eqs", &len);
@@ -1289,16 +1029,16 @@
 		goto no_msi;
 	pbm->msiq_num = *val;
 	if (pbm->msiq_num) {
-		struct msiq_prop {
+		const struct msiq_prop {
 			u32 first_msiq;
 			u32 num_msiq;
 			u32 first_devino;
 		} *mqp;
-		struct msi_range_prop {
+		const struct msi_range_prop {
 			u32 first_msi;
 			u32 num_msi;
 		} *mrng;
-		struct addr_range_prop {
+		const struct addr_range_prop {
 			u32 msi32_high;
 			u32 msi32_low;
 			u32 msi32_len;
@@ -1410,8 +1150,7 @@
 				   struct pci_dev *pdev,
 				   struct msi_desc *entry)
 {
-	struct pcidev_cookie *pcp = pdev->sysdata;
-	struct pci_pbm_info *pbm = pcp->pbm;
+	struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
 	unsigned long devino, msiqid;
 	struct msi_msg msg;
 	int msi_num, err;
@@ -1455,7 +1194,7 @@
 	if (pci_sun4v_msi_setvalid(pbm->devhandle, msi_num, HV_MSIVALID_VALID))
 		goto out_err;
 
-	pcp->msi_num = msi_num;
+	pdev->dev.archdata.msi_num = msi_num;
 
 	if (entry->msi_attrib.is_64) {
 		msg.address_hi = pbm->msi64_start >> 32;
@@ -1484,12 +1223,11 @@
 static void pci_sun4v_teardown_msi_irq(unsigned int virt_irq,
 				       struct pci_dev *pdev)
 {
-	struct pcidev_cookie *pcp = pdev->sysdata;
-	struct pci_pbm_info *pbm = pcp->pbm;
+	struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
 	unsigned long msiqid, err;
 	unsigned int msi_num;
 
-	msi_num = pcp->msi_num;
+	msi_num = pdev->dev.archdata.msi_num;
 	err = pci_sun4v_msi_getmsiq(pbm->devhandle, msi_num, &msiqid);
 	if (err) {
 		printk(KERN_ERR "%s: getmsiq gives error %lu\n",
@@ -1516,8 +1254,6 @@
 static void pci_sun4v_pbm_init(struct pci_controller_info *p, struct device_node *dp, u32 devhandle)
 {
 	struct pci_pbm_info *pbm;
-	struct property *prop;
-	int len, i;
 
 	if (devhandle & 0x40)
 		pbm = &p->pbm_B;
@@ -1526,7 +1262,6 @@
 
 	pbm->parent = p;
 	pbm->prom_node = dp;
-	pbm->pci_first_slot = 1;
 
 	pbm->devhandle = devhandle;
 
@@ -1534,39 +1269,17 @@
 
 	printk("%s: SUN4V PCI Bus Module\n", pbm->name);
 
-	prop = of_find_property(dp, "ranges", &len);
-	pbm->pbm_ranges = prop->value;
-	pbm->num_pbm_ranges =
-		(len / sizeof(struct linux_prom_pci_ranges));
-
-	/* Mask out the top 8 bits of the ranges, leaving the real
-	 * physical address.
-	 */
-	for (i = 0; i < pbm->num_pbm_ranges; i++)
-		pbm->pbm_ranges[i].parent_phys_hi &= 0x0fffffff;
-
-	pci_sun4v_determine_mem_io_space(pbm);
-	pbm_register_toplevel_resources(p, pbm);
-
-	prop = of_find_property(dp, "interrupt-map", &len);
-	pbm->pbm_intmap = prop->value;
-	pbm->num_pbm_intmap =
-		(len / sizeof(struct linux_prom_pci_intmap));
-
-	prop = of_find_property(dp, "interrupt-map-mask", NULL);
-	pbm->pbm_intmask = prop->value;
+	pci_determine_mem_io_space(pbm);
 
 	pci_sun4v_get_bus_range(pbm);
 	pci_sun4v_iommu_init(pbm);
 	pci_sun4v_msi_init(pbm);
-
-	pdev_htab_populate(pbm);
 }
 
 void sun4v_pci_init(struct device_node *dp, char *model_name)
 {
 	struct pci_controller_info *p;
-	struct pci_iommu *iommu;
+	struct iommu *iommu;
 	struct property *prop;
 	struct linux_prom64_registers *regs;
 	u32 devhandle;
@@ -1606,13 +1319,13 @@
 	if (!p)
 		goto fatal_memory_error;
 
-	iommu = kzalloc(sizeof(struct pci_iommu), GFP_ATOMIC);
+	iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC);
 	if (!iommu)
 		goto fatal_memory_error;
 
 	p->pbm_A.iommu = iommu;
 
-	iommu = kzalloc(sizeof(struct pci_iommu), GFP_ATOMIC);
+	iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC);
 	if (!iommu)
 		goto fatal_memory_error;
 
@@ -1622,11 +1335,8 @@
 	pci_controller_root = p;
 
 	p->index = pci_num_controllers++;
-	p->pbms_same_domain = 0;
 
 	p->scan_bus = pci_sun4v_scan_bus;
-	p->base_address_update = pci_sun4v_base_address_update;
-	p->resource_adjust = pci_sun4v_resource_adjust;
 #ifdef CONFIG_PCI_MSI
 	p->setup_msi_irq = pci_sun4v_setup_msi_irq;
 	p->teardown_msi_irq = pci_sun4v_teardown_msi_irq;
diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c
index b291060..a114151 100644
--- a/arch/sparc64/kernel/process.c
+++ b/arch/sparc64/kernel/process.c
@@ -28,6 +28,7 @@
 #include <linux/reboot.h>
 #include <linux/delay.h>
 #include <linux/compat.h>
+#include <linux/tick.h>
 #include <linux/init.h>
 
 #include <asm/oplib.h>
@@ -88,12 +89,14 @@
 	set_thread_flag(TIF_POLLING_NRFLAG);
 
 	while(1) {
-		if (need_resched()) {
-			preempt_enable_no_resched();
-			schedule();
-			preempt_disable();
-		}
-		sparc64_yield();
+		tick_nohz_stop_sched_tick();
+		while (!need_resched())
+			sparc64_yield();
+		tick_nohz_restart_sched_tick();
+
+		preempt_enable_no_resched();
+		schedule();
+		preempt_disable();
 	}
 }
 
diff --git a/arch/sparc64/kernel/prom.c b/arch/sparc64/kernel/prom.c
index 0917c24..5e1fcd0 100644
--- a/arch/sparc64/kernel/prom.c
+++ b/arch/sparc64/kernel/prom.c
@@ -36,12 +36,13 @@
  */
 static DEFINE_RWLOCK(devtree_lock);
 
-int of_device_is_compatible(struct device_node *device, const char *compat)
+int of_device_is_compatible(const struct device_node *device,
+			    const char *compat)
 {
 	const char* cp;
 	int cplen, l;
 
-	cp = (char *) of_get_property(device, "compatible", &cplen);
+	cp = of_get_property(device, "compatible", &cplen);
 	if (cp == NULL)
 		return 0;
 	while (cplen > 0) {
@@ -154,13 +155,14 @@
 }
 EXPORT_SYMBOL(of_find_compatible_node);
 
-struct property *of_find_property(struct device_node *np, const char *name,
+struct property *of_find_property(const struct device_node *np,
+				  const char *name,
 				  int *lenp)
 {
 	struct property *pp;
 
 	for (pp = np->properties; pp != 0; pp = pp->next) {
-		if (strcmp(pp->name, name) == 0) {
+		if (strcasecmp(pp->name, name) == 0) {
 			if (lenp != 0)
 				*lenp = pp->length;
 			break;
@@ -174,7 +176,8 @@
  * Find a property with a given name for a given node
  * and return the value.
  */
-void *of_get_property(struct device_node *np, const char *name, int *lenp)
+const void *of_get_property(const struct device_node *np, const char *name,
+		      int *lenp)
 {
 	struct property *pp = of_find_property(np,name,lenp);
 	return pp ? pp->value : NULL;
@@ -196,7 +199,7 @@
 
 int of_n_addr_cells(struct device_node *np)
 {
-	int* ip;
+	const int* ip;
 	do {
 		if (np->parent)
 			np = np->parent;
@@ -211,7 +214,7 @@
 
 int of_n_size_cells(struct device_node *np)
 {
-	int* ip;
+	const int* ip;
 	do {
 		if (np->parent)
 			np = np->parent;
@@ -243,7 +246,7 @@
 	while (*prevp) {
 		struct property *prop = *prevp;
 
-		if (!strcmp(prop->name, name)) {
+		if (!strcasecmp(prop->name, name)) {
 			void *old_val = prop->value;
 			int ret;
 
@@ -397,7 +400,7 @@
 
 static void psycho_irq_trans_init(struct device_node *dp)
 {
-	struct linux_prom64_registers *regs;
+	const struct linux_prom64_registers *regs;
 
 	dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
 	dp->irq_trans->irq_build = psycho_irq_build;
@@ -547,7 +550,7 @@
 static int sabre_device_needs_wsync(struct device_node *dp)
 {
 	struct device_node *parent = dp->parent;
-	char *parent_model, *parent_compat;
+	const char *parent_model, *parent_compat;
 
 	/* This traversal up towards the root is meant to
 	 * handle two cases:
@@ -589,7 +592,7 @@
 {
 	struct sabre_irq_data *irq_data = _data;
 	unsigned long controller_regs = irq_data->controller_regs;
-	struct linux_prom_pci_registers *regs;
+	const struct linux_prom_pci_registers *regs;
 	unsigned long imap, iclr;
 	unsigned long imap_off, iclr_off;
 	int inofixup = 0;
@@ -639,9 +642,9 @@
 
 static void sabre_irq_trans_init(struct device_node *dp)
 {
-	struct linux_prom64_registers *regs;
+	const struct linux_prom64_registers *regs;
 	struct sabre_irq_data *irq_data;
-	u32 *busrange;
+	const u32 *busrange;
 
 	dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
 	dp->irq_trans->irq_build = sabre_irq_build;
@@ -795,7 +798,7 @@
 
 static void __schizo_irq_trans_init(struct device_node *dp, int is_tomatillo)
 {
-	struct linux_prom64_registers *regs;
+	const struct linux_prom64_registers *regs;
 	struct schizo_irq_data *irq_data;
 
 	dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
@@ -836,7 +839,7 @@
 
 static void pci_sun4v_irq_trans_init(struct device_node *dp)
 {
-	struct linux_prom64_registers *regs;
+	const struct linux_prom64_registers *regs;
 
 	dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
 	dp->irq_trans->irq_build = pci_sun4v_irq_build;
@@ -940,7 +943,7 @@
 				      void *_data)
 {
 	unsigned long reg_base = (unsigned long) _data;
-	struct linux_prom_registers *regs;
+	const struct linux_prom_registers *regs;
 	unsigned long imap, iclr;
 	int sbus_slot = 0;
 	int sbus_level = 0;
@@ -994,7 +997,7 @@
 
 static void sbus_irq_trans_init(struct device_node *dp)
 {
-	struct linux_prom64_registers *regs;
+	const struct linux_prom64_registers *regs;
 
 	dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
 	dp->irq_trans->irq_build = sbus_of_build_irq;
@@ -1080,7 +1083,7 @@
 
 static void sun4v_vdev_irq_trans_init(struct device_node *dp)
 {
-	struct linux_prom64_registers *regs;
+	const struct linux_prom64_registers *regs;
 
 	dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
 	dp->irq_trans->irq_build = sun4v_vdev_irq_build;
diff --git a/arch/sparc64/kernel/sbus.c b/arch/sparc64/kernel/sbus.c
index 14f78fb..3b05428 100644
--- a/arch/sparc64/kernel/sbus.c
+++ b/arch/sparc64/kernel/sbus.c
@@ -26,23 +26,9 @@
 
 #define MAP_BASE	((u32)0xc0000000)
 
-struct sbus_iommu_arena {
-	unsigned long	*map;
-	unsigned int	hint;
-	unsigned int	limit;
-};
-
-struct sbus_iommu {
-	spinlock_t		lock;
-
-	struct sbus_iommu_arena	arena;
-
-	iopte_t			*page_table;
-	unsigned long		strbuf_regs;
-	unsigned long		iommu_regs;
-	unsigned long		sbus_control_reg;
-
-	volatile unsigned long	strbuf_flushflag;
+struct sbus_info {
+	struct iommu	iommu;
+	struct strbuf	strbuf;
 };
 
 /* Offsets from iommu_regs */
@@ -58,16 +44,17 @@
 
 #define IOMMU_DRAM_VALID	(1UL << 30UL)
 
-static void __iommu_flushall(struct sbus_iommu *iommu)
+static void __iommu_flushall(struct iommu *iommu)
 {
-	unsigned long tag = iommu->iommu_regs + IOMMU_TAGDIAG;
+	unsigned long tag;
 	int entry;
 
+	tag = iommu->iommu_control + (IOMMU_TAGDIAG - IOMMU_CONTROL);
 	for (entry = 0; entry < 16; entry++) {
 		upa_writeq(0, tag);
 		tag += 8UL;
 	}
-	upa_readq(iommu->sbus_control_reg);
+	upa_readq(iommu->write_complete_reg);
 }
 
 /* Offsets from strbuf_regs */
@@ -82,15 +69,14 @@
 
 #define STRBUF_TAG_VALID	0x02UL
 
-static void sbus_strbuf_flush(struct sbus_iommu *iommu, u32 base, unsigned long npages, int direction)
+static void sbus_strbuf_flush(struct iommu *iommu, struct strbuf *strbuf, u32 base, unsigned long npages, int direction)
 {
 	unsigned long n;
 	int limit;
 
 	n = npages;
 	while (n--)
-		upa_writeq(base + (n << IO_PAGE_SHIFT),
-			   iommu->strbuf_regs + STRBUF_PFLUSH);
+		upa_writeq(base + (n << IO_PAGE_SHIFT), strbuf->strbuf_pflush);
 
 	/* If the device could not have possibly put dirty data into
 	 * the streaming cache, no flush-flag synchronization needs
@@ -99,15 +85,14 @@
 	if (direction == SBUS_DMA_TODEVICE)
 		return;
 
-	iommu->strbuf_flushflag = 0UL;
+	*(strbuf->strbuf_flushflag) = 0UL;
 
 	/* Whoopee cushion! */
-	upa_writeq(__pa(&iommu->strbuf_flushflag),
-		   iommu->strbuf_regs + STRBUF_FSYNC);
-	upa_readq(iommu->sbus_control_reg);
+	upa_writeq(strbuf->strbuf_flushflag_pa, strbuf->strbuf_fsync);
+	upa_readq(iommu->write_complete_reg);
 
 	limit = 100000;
-	while (iommu->strbuf_flushflag == 0UL) {
+	while (*(strbuf->strbuf_flushflag) == 0UL) {
 		limit--;
 		if (!limit)
 			break;
@@ -121,9 +106,9 @@
 }
 
 /* Based largely upon the ppc64 iommu allocator.  */
-static long sbus_arena_alloc(struct sbus_iommu *iommu, unsigned long npages)
+static long sbus_arena_alloc(struct iommu *iommu, unsigned long npages)
 {
-	struct sbus_iommu_arena *arena = &iommu->arena;
+	struct iommu_arena *arena = &iommu->arena;
 	unsigned long n, i, start, end, limit;
 	int pass;
 
@@ -162,7 +147,7 @@
 	return n;
 }
 
-static void sbus_arena_free(struct sbus_iommu_arena *arena, unsigned long base, unsigned long npages)
+static void sbus_arena_free(struct iommu_arena *arena, unsigned long base, unsigned long npages)
 {
 	unsigned long i;
 
@@ -170,7 +155,7 @@
 		__clear_bit(i, arena->map);
 }
 
-static void sbus_iommu_table_init(struct sbus_iommu *iommu, unsigned int tsbsize)
+static void sbus_iommu_table_init(struct iommu *iommu, unsigned int tsbsize)
 {
 	unsigned long tsbbase, order, sz, num_tsb_entries;
 
@@ -178,13 +163,14 @@
 
 	/* Setup initial software IOMMU state. */
 	spin_lock_init(&iommu->lock);
+	iommu->page_table_map_base = MAP_BASE;
 
 	/* Allocate and initialize the free area map.  */
 	sz = num_tsb_entries / 8;
 	sz = (sz + 7UL) & ~7UL;
 	iommu->arena.map = kzalloc(sz, GFP_KERNEL);
 	if (!iommu->arena.map) {
-		prom_printf("PCI_IOMMU: Error, kmalloc(arena.map) failed.\n");
+		prom_printf("SBUS_IOMMU: Error, kmalloc(arena.map) failed.\n");
 		prom_halt();
 	}
 	iommu->arena.limit = num_tsb_entries;
@@ -200,7 +186,7 @@
 	memset(iommu->page_table, 0, tsbsize);
 }
 
-static inline iopte_t *alloc_npages(struct sbus_iommu *iommu, unsigned long npages)
+static inline iopte_t *alloc_npages(struct iommu *iommu, unsigned long npages)
 {
 	long entry;
 
@@ -211,14 +197,15 @@
 	return iommu->page_table + entry;
 }
 
-static inline void free_npages(struct sbus_iommu *iommu, dma_addr_t base, unsigned long npages)
+static inline void free_npages(struct iommu *iommu, dma_addr_t base, unsigned long npages)
 {
 	sbus_arena_free(&iommu->arena, base >> IO_PAGE_SHIFT, npages);
 }
 
 void *sbus_alloc_consistent(struct sbus_dev *sdev, size_t size, dma_addr_t *dvma_addr)
 {
-	struct sbus_iommu *iommu;
+	struct sbus_info *info;
+	struct iommu *iommu;
 	iopte_t *iopte;
 	unsigned long flags, order, first_page;
 	void *ret;
@@ -234,7 +221,8 @@
 		return NULL;
 	memset((char *)first_page, 0, PAGE_SIZE << order);
 
-	iommu = sdev->bus->iommu;
+	info = sdev->bus->iommu;
+	iommu = &info->iommu;
 
 	spin_lock_irqsave(&iommu->lock, flags);
 	iopte = alloc_npages(iommu, size >> IO_PAGE_SHIFT);
@@ -245,7 +233,7 @@
 		return NULL;
 	}
 
-	*dvma_addr = (MAP_BASE +
+	*dvma_addr = (iommu->page_table_map_base +
 		      ((iopte - iommu->page_table) << IO_PAGE_SHIFT));
 	ret = (void *) first_page;
 	npages = size >> IO_PAGE_SHIFT;
@@ -263,18 +251,20 @@
 
 void sbus_free_consistent(struct sbus_dev *sdev, size_t size, void *cpu, dma_addr_t dvma)
 {
-	struct sbus_iommu *iommu;
+	struct sbus_info *info;
+	struct iommu *iommu;
 	iopte_t *iopte;
 	unsigned long flags, order, npages;
 
 	npages = IO_PAGE_ALIGN(size) >> IO_PAGE_SHIFT;
-	iommu = sdev->bus->iommu;
+	info = sdev->bus->iommu;
+	iommu = &info->iommu;
 	iopte = iommu->page_table +
-		((dvma - MAP_BASE) >> IO_PAGE_SHIFT);
+		((dvma - iommu->page_table_map_base) >> IO_PAGE_SHIFT);
 
 	spin_lock_irqsave(&iommu->lock, flags);
 
-	free_npages(iommu, dvma - MAP_BASE, npages);
+	free_npages(iommu, dvma - iommu->page_table_map_base, npages);
 
 	spin_unlock_irqrestore(&iommu->lock, flags);
 
@@ -285,14 +275,16 @@
 
 dma_addr_t sbus_map_single(struct sbus_dev *sdev, void *ptr, size_t sz, int direction)
 {
-	struct sbus_iommu *iommu;
+	struct sbus_info *info;
+	struct iommu *iommu;
 	iopte_t *base;
 	unsigned long flags, npages, oaddr;
 	unsigned long i, base_paddr;
 	u32 bus_addr, ret;
 	unsigned long iopte_protection;
 
-	iommu = sdev->bus->iommu;
+	info = sdev->bus->iommu;
+	iommu = &info->iommu;
 
 	if (unlikely(direction == SBUS_DMA_NONE))
 		BUG();
@@ -308,7 +300,7 @@
 	if (unlikely(!base))
 		BUG();
 
-	bus_addr = (MAP_BASE +
+	bus_addr = (iommu->page_table_map_base +
 		    ((base - iommu->page_table) << IO_PAGE_SHIFT));
 	ret = bus_addr | (oaddr & ~IO_PAGE_MASK);
 	base_paddr = __pa(oaddr & IO_PAGE_MASK);
@@ -325,7 +317,9 @@
 
 void sbus_unmap_single(struct sbus_dev *sdev, dma_addr_t bus_addr, size_t sz, int direction)
 {
-	struct sbus_iommu *iommu = sdev->bus->iommu;
+	struct sbus_info *info = sdev->bus->iommu;
+	struct iommu *iommu = &info->iommu;
+	struct strbuf *strbuf = &info->strbuf;
 	iopte_t *base;
 	unsigned long flags, npages, i;
 
@@ -335,15 +329,15 @@
 	npages = IO_PAGE_ALIGN(bus_addr + sz) - (bus_addr & IO_PAGE_MASK);
 	npages >>= IO_PAGE_SHIFT;
 	base = iommu->page_table +
-		((bus_addr - MAP_BASE) >> IO_PAGE_SHIFT);
+		((bus_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT);
 
 	bus_addr &= IO_PAGE_MASK;
 
 	spin_lock_irqsave(&iommu->lock, flags);
-	sbus_strbuf_flush(iommu, bus_addr, npages, direction);
+	sbus_strbuf_flush(iommu, strbuf, bus_addr, npages, direction);
 	for (i = 0; i < npages; i++)
 		iopte_val(base[i]) = 0UL;
-	free_npages(iommu, bus_addr - MAP_BASE, npages);
+	free_npages(iommu, bus_addr - iommu->page_table_map_base, npages);
 	spin_unlock_irqrestore(&iommu->lock, flags);
 }
 
@@ -425,7 +419,8 @@
 
 int sbus_map_sg(struct sbus_dev *sdev, struct scatterlist *sglist, int nelems, int direction)
 {
-	struct sbus_iommu *iommu;
+	struct sbus_info *info;
+	struct iommu *iommu;
 	unsigned long flags, npages, iopte_protection;
 	iopte_t *base;
 	u32 dma_base;
@@ -442,7 +437,8 @@
 		return 1;
 	}
 
-	iommu = sdev->bus->iommu;
+	info = sdev->bus->iommu;
+	iommu = &info->iommu;
 
 	if (unlikely(direction == SBUS_DMA_NONE))
 		BUG();
@@ -456,7 +452,7 @@
 	if (unlikely(base == NULL))
 		BUG();
 
-	dma_base = MAP_BASE +
+	dma_base = iommu->page_table_map_base +
 		((base - iommu->page_table) << IO_PAGE_SHIFT);
 
 	/* Normalize DVMA addresses. */
@@ -485,7 +481,9 @@
 
 void sbus_unmap_sg(struct sbus_dev *sdev, struct scatterlist *sglist, int nelems, int direction)
 {
-	struct sbus_iommu *iommu;
+	struct sbus_info *info;
+	struct iommu *iommu;
+	struct strbuf *strbuf;
 	iopte_t *base;
 	unsigned long flags, i, npages;
 	u32 bus_addr;
@@ -493,7 +491,9 @@
 	if (unlikely(direction == SBUS_DMA_NONE))
 		BUG();
 
-	iommu = sdev->bus->iommu;
+	info = sdev->bus->iommu;
+	iommu = &info->iommu;
+	strbuf = &info->strbuf;
 
 	bus_addr = sglist->dma_address & IO_PAGE_MASK;
 
@@ -505,29 +505,33 @@
 		  bus_addr) >> IO_PAGE_SHIFT;
 
 	base = iommu->page_table +
-		((bus_addr - MAP_BASE) >> IO_PAGE_SHIFT);
+		((bus_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT);
 
 	spin_lock_irqsave(&iommu->lock, flags);
-	sbus_strbuf_flush(iommu, bus_addr, npages, direction);
+	sbus_strbuf_flush(iommu, strbuf, bus_addr, npages, direction);
 	for (i = 0; i < npages; i++)
 		iopte_val(base[i]) = 0UL;
-	free_npages(iommu, bus_addr - MAP_BASE, npages);
+	free_npages(iommu, bus_addr - iommu->page_table_map_base, npages);
 	spin_unlock_irqrestore(&iommu->lock, flags);
 }
 
 void sbus_dma_sync_single_for_cpu(struct sbus_dev *sdev, dma_addr_t bus_addr, size_t sz, int direction)
 {
-	struct sbus_iommu *iommu;
+	struct sbus_info *info;
+	struct iommu *iommu;
+	struct strbuf *strbuf;
 	unsigned long flags, npages;
 
-	iommu = sdev->bus->iommu;
+	info = sdev->bus->iommu;
+	iommu = &info->iommu;
+	strbuf = &info->strbuf;
 
 	npages = IO_PAGE_ALIGN(bus_addr + sz) - (bus_addr & IO_PAGE_MASK);
 	npages >>= IO_PAGE_SHIFT;
 	bus_addr &= IO_PAGE_MASK;
 
 	spin_lock_irqsave(&iommu->lock, flags);
-	sbus_strbuf_flush(iommu, bus_addr, npages, direction);
+	sbus_strbuf_flush(iommu, strbuf, bus_addr, npages, direction);
 	spin_unlock_irqrestore(&iommu->lock, flags);
 }
 
@@ -537,11 +541,15 @@
 
 void sbus_dma_sync_sg_for_cpu(struct sbus_dev *sdev, struct scatterlist *sglist, int nelems, int direction)
 {
-	struct sbus_iommu *iommu;
+	struct sbus_info *info;
+	struct iommu *iommu;
+	struct strbuf *strbuf;
 	unsigned long flags, npages, i;
 	u32 bus_addr;
 
-	iommu = sdev->bus->iommu;
+	info = sdev->bus->iommu;
+	iommu = &info->iommu;
+	strbuf = &info->strbuf;
 
 	bus_addr = sglist[0].dma_address & IO_PAGE_MASK;
 	for (i = 0; i < nelems; i++) {
@@ -553,7 +561,7 @@
 		  - bus_addr) >> IO_PAGE_SHIFT;
 
 	spin_lock_irqsave(&iommu->lock, flags);
-	sbus_strbuf_flush(iommu, bus_addr, npages, direction);
+	sbus_strbuf_flush(iommu, strbuf, bus_addr, npages, direction);
 	spin_unlock_irqrestore(&iommu->lock, flags);
 }
 
@@ -564,12 +572,13 @@
 /* Enable 64-bit DVMA mode for the given device. */
 void sbus_set_sbus64(struct sbus_dev *sdev, int bursts)
 {
-	struct sbus_iommu *iommu = sdev->bus->iommu;
+	struct sbus_info *info = sdev->bus->iommu;
+	struct iommu *iommu = &info->iommu;
 	int slot = sdev->slot;
 	unsigned long cfg_reg;
 	u64 val;
 
-	cfg_reg = iommu->sbus_control_reg;
+	cfg_reg = iommu->write_complete_reg;
 	switch (slot) {
 	case 0:
 		cfg_reg += 0x20UL;
@@ -704,8 +713,9 @@
 unsigned int sbus_build_irq(void *buscookie, unsigned int ino)
 {
 	struct sbus_bus *sbus = (struct sbus_bus *)buscookie;
-	struct sbus_iommu *iommu = sbus->iommu;
-	unsigned long reg_base = iommu->sbus_control_reg - 0x2000UL;
+	struct sbus_info *info = sbus->iommu;
+	struct iommu *iommu = &info->iommu;
+	unsigned long reg_base = iommu->write_complete_reg - 0x2000UL;
 	unsigned long imap, iclr;
 	int sbus_level = 0;
 
@@ -766,8 +776,9 @@
 static irqreturn_t sysio_ue_handler(int irq, void *dev_id)
 {
 	struct sbus_bus *sbus = dev_id;
-	struct sbus_iommu *iommu = sbus->iommu;
-	unsigned long reg_base = iommu->sbus_control_reg - 0x2000UL;
+	struct sbus_info *info = sbus->iommu;
+	struct iommu *iommu = &info->iommu;
+	unsigned long reg_base = iommu->write_complete_reg - 0x2000UL;
 	unsigned long afsr_reg, afar_reg;
 	unsigned long afsr, afar, error_bits;
 	int reported;
@@ -838,8 +849,9 @@
 static irqreturn_t sysio_ce_handler(int irq, void *dev_id)
 {
 	struct sbus_bus *sbus = dev_id;
-	struct sbus_iommu *iommu = sbus->iommu;
-	unsigned long reg_base = iommu->sbus_control_reg - 0x2000UL;
+	struct sbus_info *info = sbus->iommu;
+	struct iommu *iommu = &info->iommu;
+	unsigned long reg_base = iommu->write_complete_reg - 0x2000UL;
 	unsigned long afsr_reg, afar_reg;
 	unsigned long afsr, afar, error_bits;
 	int reported;
@@ -915,12 +927,13 @@
 static irqreturn_t sysio_sbus_error_handler(int irq, void *dev_id)
 {
 	struct sbus_bus *sbus = dev_id;
-	struct sbus_iommu *iommu = sbus->iommu;
+	struct sbus_info *info = sbus->iommu;
+	struct iommu *iommu = &info->iommu;
 	unsigned long afsr_reg, afar_reg, reg_base;
 	unsigned long afsr, afar, error_bits;
 	int reported;
 
-	reg_base = iommu->sbus_control_reg - 0x2000UL;
+	reg_base = iommu->write_complete_reg - 0x2000UL;
 	afsr_reg = reg_base + SYSIO_SBUS_AFSR;
 	afar_reg = reg_base + SYSIO_SBUS_AFAR;
 
@@ -982,8 +995,9 @@
 
 static void __init sysio_register_error_handlers(struct sbus_bus *sbus)
 {
-	struct sbus_iommu *iommu = sbus->iommu;
-	unsigned long reg_base = iommu->sbus_control_reg - 0x2000UL;
+	struct sbus_info *info = sbus->iommu;
+	struct iommu *iommu = &info->iommu;
+	unsigned long reg_base = iommu->write_complete_reg - 0x2000UL;
 	unsigned int irq;
 	u64 control;
 
@@ -1017,18 +1031,20 @@
 		    SYSIO_ECNTRL_CEEN),
 		   reg_base + ECC_CONTROL);
 
-	control = upa_readq(iommu->sbus_control_reg);
+	control = upa_readq(iommu->write_complete_reg);
 	control |= 0x100UL; /* SBUS Error Interrupt Enable */
-	upa_writeq(control, iommu->sbus_control_reg);
+	upa_writeq(control, iommu->write_complete_reg);
 }
 
 /* Boot time initialization. */
 static void __init sbus_iommu_init(int __node, struct sbus_bus *sbus)
 {
-	struct linux_prom64_registers *pr;
+	const struct linux_prom64_registers *pr;
 	struct device_node *dp;
-	struct sbus_iommu *iommu;
-	unsigned long regs;
+	struct sbus_info *info;
+	struct iommu *iommu;
+	struct strbuf *strbuf;
+	unsigned long regs, reg_base;
 	u64 control;
 	int i;
 
@@ -1043,33 +1059,42 @@
 	}
 	regs = pr->phys_addr;
 
-	iommu = kmalloc(sizeof(*iommu) + SMP_CACHE_BYTES, GFP_ATOMIC);
-	if (iommu == NULL) {
-		prom_printf("sbus_iommu_init: Fatal error, kmalloc(iommu) failed\n");
+	info = kzalloc(sizeof(*info), GFP_ATOMIC);
+	if (info == NULL) {
+		prom_printf("sbus_iommu_init: Fatal error, "
+			    "kmalloc(info) failed\n");
 		prom_halt();
 	}
 
-	/* Align on E$ line boundary. */
-	iommu = (struct sbus_iommu *)
-		(((unsigned long)iommu + (SMP_CACHE_BYTES - 1UL)) &
-		 ~(SMP_CACHE_BYTES - 1UL));
+	iommu = &info->iommu;
+	strbuf = &info->strbuf;
 
-	memset(iommu, 0, sizeof(*iommu));
+	reg_base = regs + SYSIO_IOMMUREG_BASE;
+	iommu->iommu_control = reg_base + IOMMU_CONTROL;
+	iommu->iommu_tsbbase = reg_base + IOMMU_TSBBASE;
+	iommu->iommu_flush = reg_base + IOMMU_FLUSH;
 
-	/* Setup spinlock. */
-	spin_lock_init(&iommu->lock);
+	reg_base = regs + SYSIO_STRBUFREG_BASE;
+	strbuf->strbuf_control = reg_base + STRBUF_CONTROL;
+	strbuf->strbuf_pflush = reg_base + STRBUF_PFLUSH;
+	strbuf->strbuf_fsync = reg_base + STRBUF_FSYNC;
 
-	/* Init register offsets. */
-	iommu->iommu_regs = regs + SYSIO_IOMMUREG_BASE;
-	iommu->strbuf_regs = regs + SYSIO_STRBUFREG_BASE;
+	strbuf->strbuf_enabled = 1;
+
+	strbuf->strbuf_flushflag = (volatile unsigned long *)
+		((((unsigned long)&strbuf->__flushflag_buf[0])
+		  + 63UL)
+		 & ~63UL);
+	strbuf->strbuf_flushflag_pa = (unsigned long)
+		__pa(strbuf->strbuf_flushflag);
 
 	/* The SYSIO SBUS control register is used for dummy reads
 	 * in order to ensure write completion.
 	 */
-	iommu->sbus_control_reg = regs + 0x2000UL;
+	iommu->write_complete_reg = regs + 0x2000UL;
 
 	/* Link into SYSIO software state. */
-	sbus->iommu = iommu;
+	sbus->iommu = info;
 
 	printk("SYSIO: UPA portID %x, at %016lx\n",
 	       sbus->portid, regs);
@@ -1077,40 +1102,44 @@
 	/* Setup for TSB_SIZE=7, TBW_SIZE=0, MMU_DE=1, MMU_EN=1 */
 	sbus_iommu_table_init(iommu, IO_TSB_SIZE);
 
-	control = upa_readq(iommu->iommu_regs + IOMMU_CONTROL);
+	control = upa_readq(iommu->iommu_control);
 	control = ((7UL << 16UL)	|
 		   (0UL << 2UL)		|
 		   (1UL << 1UL)		|
 		   (1UL << 0UL));
-	upa_writeq(control, iommu->iommu_regs + IOMMU_CONTROL);
+	upa_writeq(control, iommu->iommu_control);
 
 	/* Clean out any cruft in the IOMMU using
 	 * diagnostic accesses.
 	 */
 	for (i = 0; i < 16; i++) {
-		unsigned long dram = iommu->iommu_regs + IOMMU_DRAMDIAG;
-		unsigned long tag = iommu->iommu_regs + IOMMU_TAGDIAG;
+		unsigned long dram, tag;
+
+		dram = iommu->iommu_control + (IOMMU_DRAMDIAG - IOMMU_CONTROL);
+		tag = iommu->iommu_control + (IOMMU_TAGDIAG - IOMMU_CONTROL);
 
 		dram += (unsigned long)i * 8UL;
 		tag += (unsigned long)i * 8UL;
 		upa_writeq(0, dram);
 		upa_writeq(0, tag);
 	}
-	upa_readq(iommu->sbus_control_reg);
+	upa_readq(iommu->write_complete_reg);
 
 	/* Give the TSB to SYSIO. */
-	upa_writeq(__pa(iommu->page_table), iommu->iommu_regs + IOMMU_TSBBASE);
+	upa_writeq(__pa(iommu->page_table), iommu->iommu_tsbbase);
 
 	/* Setup streaming buffer, DE=1 SB_EN=1 */
 	control = (1UL << 1UL) | (1UL << 0UL);
-	upa_writeq(control, iommu->strbuf_regs + STRBUF_CONTROL);
+	upa_writeq(control, strbuf->strbuf_control);
 
 	/* Clear out the tags using diagnostics. */
 	for (i = 0; i < 16; i++) {
 		unsigned long ptag, ltag;
 
-		ptag = iommu->strbuf_regs + STRBUF_PTAGDIAG;
-		ltag = iommu->strbuf_regs + STRBUF_LTAGDIAG;
+		ptag = strbuf->strbuf_control +
+			(STRBUF_PTAGDIAG - STRBUF_CONTROL);
+		ltag = strbuf->strbuf_control +
+			(STRBUF_LTAGDIAG - STRBUF_CONTROL);
 		ptag += (unsigned long)i * 8UL;
 		ltag += (unsigned long)i * 8UL;
 
@@ -1119,9 +1148,9 @@
 	}
 
 	/* Enable DVMA arbitration for all devices/slots. */
-	control = upa_readq(iommu->sbus_control_reg);
+	control = upa_readq(iommu->write_complete_reg);
 	control |= 0x3fUL;
-	upa_writeq(control, iommu->sbus_control_reg);
+	upa_writeq(control, iommu->write_complete_reg);
 
 	/* Now some Xfire specific grot... */
 	if (this_is_starfire)
@@ -1133,7 +1162,7 @@
 void sbus_fill_device_irq(struct sbus_dev *sdev)
 {
 	struct device_node *dp = of_find_node_by_phandle(sdev->prom_node);
-	struct linux_prom_irqs *irqs;
+	const struct linux_prom_irqs *irqs;
 
 	irqs = of_get_property(dp, "interrupts", NULL);
 	if (!irqs) {
diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c
index fc99f7b..d4f0a70 100644
--- a/arch/sparc64/kernel/smp.c
+++ b/arch/sparc64/kernel/smp.c
@@ -45,7 +45,7 @@
 extern void calibrate_delay(void);
 
 /* Please don't make this stuff initdata!!!  --DaveM */
-static unsigned char boot_cpu_id;
+unsigned char boot_cpu_id;
 
 cpumask_t cpu_online_map __read_mostly = CPU_MASK_NONE;
 cpumask_t phys_cpu_present_map __read_mostly = CPU_MASK_NONE;
@@ -81,8 +81,6 @@
 	struct device_node *dp;
 	int def;
 
-	/* multiplier and counter set by
-	   smp_setup_percpu_timer()  */
 	cpu_data(id).udelay_val			= loops_per_jiffy;
 
 	cpu_find_by_mid(id, &dp);
@@ -125,7 +123,7 @@
 	       cpu_data(id).ecache_size, cpu_data(id).ecache_line_size);
 }
 
-static void smp_setup_percpu_timer(void);
+extern void setup_sparc64_timer(void);
 
 static volatile unsigned long callin_flag = 0;
 
@@ -140,7 +138,7 @@
 
 	__flush_tlb_all();
 
-	smp_setup_percpu_timer();
+	setup_sparc64_timer();
 
 	if (cheetah_pcache_forced_on)
 		cheetah_enable_pcache();
@@ -177,8 +175,6 @@
 	panic("SMP bolixed\n");
 }
 
-static unsigned long current_tick_offset __read_mostly;
-
 /* This tick register synchronization scheme is taken entirely from
  * the ia64 port, see arch/ia64/kernel/smpboot.c for details and credit.
  *
@@ -261,7 +257,7 @@
 				} else
 					adj = -delta;
 
-				tick_ops->add_tick(adj, current_tick_offset);
+				tick_ops->add_tick(adj);
 			}
 #if DEBUG_TICK_SYNC
 			t[i].rt = rt;
@@ -1180,117 +1176,15 @@
 	preempt_enable();
 }
 
-#define prof_multiplier(__cpu)		cpu_data(__cpu).multiplier
-#define prof_counter(__cpu)		cpu_data(__cpu).counter
-
-void smp_percpu_timer_interrupt(struct pt_regs *regs)
-{
-	unsigned long compare, tick, pstate;
-	int cpu = smp_processor_id();
-	int user = user_mode(regs);
-	struct pt_regs *old_regs;
-
-	/*
-	 * Check for level 14 softint.
-	 */
-	{
-		unsigned long tick_mask = tick_ops->softint_mask;
-
-		if (!(get_softint() & tick_mask)) {
-			extern void handler_irq(int, struct pt_regs *);
-
-			handler_irq(14, regs);
-			return;
-		}
-		clear_softint(tick_mask);
-	}
-
-	old_regs = set_irq_regs(regs);
-	do {
-		profile_tick(CPU_PROFILING);
-		if (!--prof_counter(cpu)) {
-			irq_enter();
-
-			if (cpu == boot_cpu_id) {
-				kstat_this_cpu.irqs[0]++;
-				timer_tick_interrupt(regs);
-			}
-
-			update_process_times(user);
-
-			irq_exit();
-
-			prof_counter(cpu) = prof_multiplier(cpu);
-		}
-
-		/* Guarantee that the following sequences execute
-		 * uninterrupted.
-		 */
-		__asm__ __volatile__("rdpr	%%pstate, %0\n\t"
-				     "wrpr	%0, %1, %%pstate"
-				     : "=r" (pstate)
-				     : "i" (PSTATE_IE));
-
-		compare = tick_ops->add_compare(current_tick_offset);
-		tick = tick_ops->get_tick();
-
-		/* Restore PSTATE_IE. */
-		__asm__ __volatile__("wrpr	%0, 0x0, %%pstate"
-				     : /* no outputs */
-				     : "r" (pstate));
-	} while (time_after_eq(tick, compare));
-	set_irq_regs(old_regs);
-}
-
-static void __init smp_setup_percpu_timer(void)
-{
-	int cpu = smp_processor_id();
-	unsigned long pstate;
-
-	prof_counter(cpu) = prof_multiplier(cpu) = 1;
-
-	/* Guarantee that the following sequences execute
-	 * uninterrupted.
-	 */
-	__asm__ __volatile__("rdpr	%%pstate, %0\n\t"
-			     "wrpr	%0, %1, %%pstate"
-			     : "=r" (pstate)
-			     : "i" (PSTATE_IE));
-
-	tick_ops->init_tick(current_tick_offset);
-
-	/* Restore PSTATE_IE. */
-	__asm__ __volatile__("wrpr	%0, 0x0, %%pstate"
-			     : /* no outputs */
-			     : "r" (pstate));
-}
-
 void __init smp_tick_init(void)
 {
 	boot_cpu_id = hard_smp_processor_id();
-	current_tick_offset = timer_tick_offset;
-
-	prof_counter(boot_cpu_id) = prof_multiplier(boot_cpu_id) = 1;
 }
 
 /* /proc/profile writes can call this, don't __init it please. */
-static DEFINE_SPINLOCK(prof_setup_lock);
-
 int setup_profiling_timer(unsigned int multiplier)
 {
-	unsigned long flags;
-	int i;
-
-	if ((!multiplier) || (timer_tick_offset / multiplier) < 1000)
-		return -EINVAL;
-
-	spin_lock_irqsave(&prof_setup_lock, flags);
-	for_each_possible_cpu(i)
-		prof_multiplier(i) = multiplier;
-	current_tick_offset = (timer_tick_offset / multiplier);
-	spin_unlock_irqrestore(&prof_setup_lock, flags);
-
-	return 0;
+	return -EINVAL;
 }
 
 static void __init smp_tune_scheduling(void)
diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c
index beffc82..d00f51a 100644
--- a/arch/sparc64/kernel/sparc64_ksyms.c
+++ b/arch/sparc64/kernel/sparc64_ksyms.c
@@ -212,7 +212,6 @@
 #ifdef CONFIG_PCI
 EXPORT_SYMBOL(ebus_chain);
 EXPORT_SYMBOL(isa_chain);
-EXPORT_SYMBOL(pci_memspace_mask);
 EXPORT_SYMBOL(pci_alloc_consistent);
 EXPORT_SYMBOL(pci_free_consistent);
 EXPORT_SYMBOL(pci_map_single);
diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c
index f84da4f..259063f 100644
--- a/arch/sparc64/kernel/time.c
+++ b/arch/sparc64/kernel/time.c
@@ -31,6 +31,9 @@
 #include <linux/profile.h>
 #include <linux/miscdevice.h>
 #include <linux/rtc.h>
+#include <linux/kernel_stat.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
 
 #include <asm/oplib.h>
 #include <asm/mostek.h>
@@ -60,6 +63,7 @@
 static int set_rtc_mmss(unsigned long);
 
 #define TICK_PRIV_BIT	(1UL << 63)
+#define TICKCMP_IRQ_BIT	(1UL << 63)
 
 #ifdef CONFIG_SMP
 unsigned long profile_pc(struct pt_regs *regs)
@@ -93,21 +97,22 @@
 	: "g2");
 }
 
-static void tick_init_tick(unsigned long offset)
+static void tick_disable_irq(void)
 {
-	tick_disable_protection();
-
 	__asm__ __volatile__(
-	"	rd	%%tick, %%g1\n"
-	"	andn	%%g1, %1, %%g1\n"
 	"	ba,pt	%%xcc, 1f\n"
-	"	 add	%%g1, %0, %%g1\n"
+	"	 nop\n"
 	"	.align	64\n"
-	"1:	wr	%%g1, 0x0, %%tick_cmpr\n"
+	"1:	wr	%0, 0x0, %%tick_cmpr\n"
 	"	rd	%%tick_cmpr, %%g0"
 	: /* no outputs */
-	: "r" (offset), "r" (TICK_PRIV_BIT)
-	: "g1");
+	: "r" (TICKCMP_IRQ_BIT));
+}
+
+static void tick_init_tick(void)
+{
+	tick_disable_protection();
+	tick_disable_irq();
 }
 
 static unsigned long tick_get_tick(void)
@@ -121,20 +126,14 @@
 	return ret & ~TICK_PRIV_BIT;
 }
 
-static unsigned long tick_get_compare(void)
+static int tick_add_compare(unsigned long adj)
 {
-	unsigned long ret;
+	unsigned long orig_tick, new_tick, new_compare;
 
-	__asm__ __volatile__("rd	%%tick_cmpr, %0\n\t"
-			     "mov	%0, %0"
-			     : "=r" (ret));
+	__asm__ __volatile__("rd	%%tick, %0"
+			     : "=r" (orig_tick));
 
-	return ret;
-}
-
-static unsigned long tick_add_compare(unsigned long adj)
-{
-	unsigned long new_compare;
+	orig_tick &= ~TICKCMP_IRQ_BIT;
 
 	/* Workaround for Spitfire Errata (#54 I think??), I discovered
 	 * this via Sun BugID 4008234, mentioned in Solaris-2.5.1 patch
@@ -145,44 +144,41 @@
 	 * at the start of an I-cache line, and perform a dummy
 	 * read back from %tick_cmpr right after writing to it. -DaveM
 	 */
-	__asm__ __volatile__("rd	%%tick_cmpr, %0\n\t"
-			     "ba,pt	%%xcc, 1f\n\t"
-			     " add	%0, %1, %0\n\t"
+	__asm__ __volatile__("ba,pt	%%xcc, 1f\n\t"
+			     " add	%1, %2, %0\n\t"
 			     ".align	64\n"
 			     "1:\n\t"
 			     "wr	%0, 0, %%tick_cmpr\n\t"
-			     "rd	%%tick_cmpr, %%g0"
-			     : "=&r" (new_compare)
-			     : "r" (adj));
+			     "rd	%%tick_cmpr, %%g0\n\t"
+			     : "=r" (new_compare)
+			     : "r" (orig_tick), "r" (adj));
 
-	return new_compare;
+	__asm__ __volatile__("rd	%%tick, %0"
+			     : "=r" (new_tick));
+	new_tick &= ~TICKCMP_IRQ_BIT;
+
+	return ((long)(new_tick - (orig_tick+adj))) > 0L;
 }
 
-static unsigned long tick_add_tick(unsigned long adj, unsigned long offset)
+static unsigned long tick_add_tick(unsigned long adj)
 {
-	unsigned long new_tick, tmp;
+	unsigned long new_tick;
 
 	/* Also need to handle Blackbird bug here too. */
 	__asm__ __volatile__("rd	%%tick, %0\n\t"
-			     "add	%0, %2, %0\n\t"
+			     "add	%0, %1, %0\n\t"
 			     "wrpr	%0, 0, %%tick\n\t"
-			     "andn	%0, %4, %1\n\t"
-			     "ba,pt	%%xcc, 1f\n\t"
-			     " add	%1, %3, %1\n\t"
-			     ".align	64\n"
-			     "1:\n\t"
-			     "wr	%1, 0, %%tick_cmpr\n\t"
-			     "rd	%%tick_cmpr, %%g0"
-			     : "=&r" (new_tick), "=&r" (tmp)
-			     : "r" (adj), "r" (offset), "r" (TICK_PRIV_BIT));
+			     : "=&r" (new_tick)
+			     : "r" (adj));
 
 	return new_tick;
 }
 
 static struct sparc64_tick_ops tick_operations __read_mostly = {
+	.name		=	"tick",
 	.init_tick	=	tick_init_tick,
+	.disable_irq	=	tick_disable_irq,
 	.get_tick	=	tick_get_tick,
-	.get_compare	=	tick_get_compare,
 	.add_tick	=	tick_add_tick,
 	.add_compare	=	tick_add_compare,
 	.softint_mask	=	1UL << 0,
@@ -190,7 +186,15 @@
 
 struct sparc64_tick_ops *tick_ops __read_mostly = &tick_operations;
 
-static void stick_init_tick(unsigned long offset)
+static void stick_disable_irq(void)
+{
+	__asm__ __volatile__(
+	"wr	%0, 0x0, %%asr25"
+	: /* no outputs */
+	: "r" (TICKCMP_IRQ_BIT));
+}
+
+static void stick_init_tick(void)
 {
 	/* Writes to the %tick and %stick register are not
 	 * allowed on sun4v.  The Hypervisor controls that
@@ -198,6 +202,7 @@
 	 */
 	if (tlb_type != hypervisor) {
 		tick_disable_protection();
+		tick_disable_irq();
 
 		/* Let the user get at STICK too. */
 		__asm__ __volatile__(
@@ -209,14 +214,7 @@
 		: "g1", "g2");
 	}
 
-	__asm__ __volatile__(
-	"	rd	%%asr24, %%g1\n"
-	"	andn	%%g1, %1, %%g1\n"
-	"	add	%%g1, %0, %%g1\n"
-	"	wr	%%g1, 0x0, %%asr25"
-	: /* no outputs */
-	: "r" (offset), "r" (TICK_PRIV_BIT)
-	: "g1");
+	stick_disable_irq();
 }
 
 static unsigned long stick_get_tick(void)
@@ -229,49 +227,43 @@
 	return ret & ~TICK_PRIV_BIT;
 }
 
-static unsigned long stick_get_compare(void)
+static unsigned long stick_add_tick(unsigned long adj)
 {
-	unsigned long ret;
-
-	__asm__ __volatile__("rd	%%asr25, %0"
-			     : "=r" (ret));
-
-	return ret;
-}
-
-static unsigned long stick_add_tick(unsigned long adj, unsigned long offset)
-{
-	unsigned long new_tick, tmp;
+	unsigned long new_tick;
 
 	__asm__ __volatile__("rd	%%asr24, %0\n\t"
-			     "add	%0, %2, %0\n\t"
+			     "add	%0, %1, %0\n\t"
 			     "wr	%0, 0, %%asr24\n\t"
-			     "andn	%0, %4, %1\n\t"
-			     "add	%1, %3, %1\n\t"
-			     "wr	%1, 0, %%asr25"
-			     : "=&r" (new_tick), "=&r" (tmp)
-			     : "r" (adj), "r" (offset), "r" (TICK_PRIV_BIT));
+			     : "=&r" (new_tick)
+			     : "r" (adj));
 
 	return new_tick;
 }
 
-static unsigned long stick_add_compare(unsigned long adj)
+static int stick_add_compare(unsigned long adj)
 {
-	unsigned long new_compare;
+	unsigned long orig_tick, new_tick;
 
-	__asm__ __volatile__("rd	%%asr25, %0\n\t"
-			     "add	%0, %1, %0\n\t"
-			     "wr	%0, 0, %%asr25"
-			     : "=&r" (new_compare)
-			     : "r" (adj));
+	__asm__ __volatile__("rd	%%asr24, %0"
+			     : "=r" (orig_tick));
+	orig_tick &= ~TICKCMP_IRQ_BIT;
 
-	return new_compare;
+	__asm__ __volatile__("wr	%0, 0, %%asr25"
+			     : /* no outputs */
+			     : "r" (orig_tick + adj));
+
+	__asm__ __volatile__("rd	%%asr24, %0"
+			     : "=r" (new_tick));
+	new_tick &= ~TICKCMP_IRQ_BIT;
+
+	return ((long)(new_tick - (orig_tick+adj))) > 0L;
 }
 
 static struct sparc64_tick_ops stick_operations __read_mostly = {
+	.name		=	"stick",
 	.init_tick	=	stick_init_tick,
+	.disable_irq	=	stick_disable_irq,
 	.get_tick	=	stick_get_tick,
-	.get_compare	=	stick_get_compare,
 	.add_tick	=	stick_add_tick,
 	.add_compare	=	stick_add_compare,
 	.softint_mask	=	1UL << 16,
@@ -320,20 +312,6 @@
 	return ret;
 }
 
-static unsigned long __hbird_read_compare(void)
-{
-	unsigned long low, high;
-	unsigned long addr = HBIRD_STICKCMP_ADDR;
-
-	__asm__ __volatile__("ldxa	[%2] %3, %0\n\t"
-			     "add	%2, 0x8, %2\n\t"
-			     "ldxa	[%2] %3, %1"
-			     : "=&r" (low), "=&r" (high), "=&r" (addr)
-			     : "i" (ASI_PHYS_BYPASS_EC_E), "2" (addr));
-
-	return (high << 32UL) | low;
-}
-
 static void __hbird_write_stick(unsigned long val)
 {
 	unsigned long low = (val & 0xffffffffUL);
@@ -364,10 +342,13 @@
 			       "i" (ASI_PHYS_BYPASS_EC_E));
 }
 
-static void hbtick_init_tick(unsigned long offset)
+static void hbtick_disable_irq(void)
 {
-	unsigned long val;
+	__hbird_write_compare(TICKCMP_IRQ_BIT);
+}
 
+static void hbtick_init_tick(void)
+{
 	tick_disable_protection();
 
 	/* XXX This seems to be necessary to 'jumpstart' Hummingbird
@@ -377,8 +358,7 @@
 	 */
 	__hbird_write_stick(__hbird_read_stick());
 
-	val = __hbird_read_stick() & ~TICK_PRIV_BIT;
-	__hbird_write_compare(val + offset);
+	hbtick_disable_irq();
 }
 
 static unsigned long hbtick_get_tick(void)
@@ -386,122 +366,95 @@
 	return __hbird_read_stick() & ~TICK_PRIV_BIT;
 }
 
-static unsigned long hbtick_get_compare(void)
-{
-	return __hbird_read_compare();
-}
-
-static unsigned long hbtick_add_tick(unsigned long adj, unsigned long offset)
+static unsigned long hbtick_add_tick(unsigned long adj)
 {
 	unsigned long val;
 
 	val = __hbird_read_stick() + adj;
 	__hbird_write_stick(val);
 
-	val &= ~TICK_PRIV_BIT;
-	__hbird_write_compare(val + offset);
-
 	return val;
 }
 
-static unsigned long hbtick_add_compare(unsigned long adj)
+static int hbtick_add_compare(unsigned long adj)
 {
-	unsigned long val = __hbird_read_compare() + adj;
+	unsigned long val = __hbird_read_stick();
+	unsigned long val2;
 
-	val &= ~TICK_PRIV_BIT;
+	val &= ~TICKCMP_IRQ_BIT;
+	val += adj;
 	__hbird_write_compare(val);
 
-	return val;
+	val2 = __hbird_read_stick() & ~TICKCMP_IRQ_BIT;
+
+	return ((long)(val2 - val)) > 0L;
 }
 
 static struct sparc64_tick_ops hbtick_operations __read_mostly = {
+	.name		=	"hbtick",
 	.init_tick	=	hbtick_init_tick,
+	.disable_irq	=	hbtick_disable_irq,
 	.get_tick	=	hbtick_get_tick,
-	.get_compare	=	hbtick_get_compare,
 	.add_tick	=	hbtick_add_tick,
 	.add_compare	=	hbtick_add_compare,
 	.softint_mask	=	1UL << 0,
 };
 
-/* timer_interrupt() needs to keep up the real-time clock,
- * as well as call the "do_timer()" routine every clocktick
- *
- * NOTE: On SUN5 systems the ticker interrupt comes in using 2
- *       interrupts, one at level14 and one with softint bit 0.
- */
-unsigned long timer_tick_offset __read_mostly;
-
 static unsigned long timer_ticks_per_nsec_quotient __read_mostly;
 
 #define TICK_SIZE (tick_nsec / 1000)
 
-static inline void timer_check_rtc(void)
+#define USEC_AFTER	500000
+#define USEC_BEFORE	500000
+
+static void sync_cmos_clock(unsigned long dummy);
+
+static DEFINE_TIMER(sync_cmos_timer, sync_cmos_clock, 0, 0);
+
+static void sync_cmos_clock(unsigned long dummy)
 {
-	/* last time the cmos clock got updated */
-	static long last_rtc_update;
+	struct timeval now, next;
+	int fail = 1;
 
-	/* Determine when to update the Mostek clock. */
-	if (ntp_synced() &&
-	    xtime.tv_sec > last_rtc_update + 660 &&
-	    (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 &&
-	    (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) {
-		if (set_rtc_mmss(xtime.tv_sec) == 0)
-			last_rtc_update = xtime.tv_sec;
-		else
-			last_rtc_update = xtime.tv_sec - 600;
-			/* do it again in 60 s */
-	}
-}
-
-irqreturn_t timer_interrupt(int irq, void *dev_id)
-{
-	unsigned long ticks, compare, pstate;
-
-	write_seqlock(&xtime_lock);
-
-	do {
-#ifndef CONFIG_SMP
-		profile_tick(CPU_PROFILING);
-		update_process_times(user_mode(get_irq_regs()));
-#endif
-		do_timer(1);
-
-		/* Guarantee that the following sequences execute
-		 * uninterrupted.
+	/*
+	 * If we have an externally synchronized Linux clock, then update
+	 * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
+	 * called as close as possible to 500 ms before the new second starts.
+	 * This code is run on a timer.  If the clock is set, that timer
+	 * may not expire at the correct time.  Thus, we adjust...
+	 */
+	if (!ntp_synced())
+		/*
+		 * Not synced, exit, do not restart a timer (if one is
+		 * running, let it run out).
 		 */
-		__asm__ __volatile__("rdpr	%%pstate, %0\n\t"
-				     "wrpr	%0, %1, %%pstate"
-				     : "=r" (pstate)
-				     : "i" (PSTATE_IE));
+		return;
 
-		compare = tick_ops->add_compare(timer_tick_offset);
-		ticks = tick_ops->get_tick();
+	do_gettimeofday(&now);
+	if (now.tv_usec >= USEC_AFTER - ((unsigned) TICK_SIZE) / 2 &&
+	    now.tv_usec <= USEC_BEFORE + ((unsigned) TICK_SIZE) / 2)
+		fail = set_rtc_mmss(now.tv_sec);
 
-		/* Restore PSTATE_IE. */
-		__asm__ __volatile__("wrpr	%0, 0x0, %%pstate"
-				     : /* no outputs */
-				     : "r" (pstate));
-	} while (time_after_eq(ticks, compare));
+	next.tv_usec = USEC_AFTER - now.tv_usec;
+	if (next.tv_usec <= 0)
+		next.tv_usec += USEC_PER_SEC;
 
-	timer_check_rtc();
+	if (!fail)
+		next.tv_sec = 659;
+	else
+		next.tv_sec = 0;
 
-	write_sequnlock(&xtime_lock);
-
-	return IRQ_HANDLED;
+	if (next.tv_usec >= USEC_PER_SEC) {
+		next.tv_sec++;
+		next.tv_usec -= USEC_PER_SEC;
+	}
+	mod_timer(&sync_cmos_timer, jiffies + timeval_to_jiffies(&next));
 }
 
-#ifdef CONFIG_SMP
-void timer_tick_interrupt(struct pt_regs *regs)
+void notify_arch_cmos_timer(void)
 {
-	write_seqlock(&xtime_lock);
-
-	do_timer(1);
-
-	timer_check_rtc();
-
-	write_sequnlock(&xtime_lock);
+	mod_timer(&sync_cmos_timer, jiffies + 1);
 }
-#endif
 
 /* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */
 static void __init kick_start_clock(void)
@@ -751,7 +704,7 @@
 	return -EOPNOTSUPP;
 }
 
-static int __init clock_model_matches(char *model)
+static int __init clock_model_matches(const char *model)
 {
 	if (strcmp(model, "mk48t02") &&
 	    strcmp(model, "mk48t08") &&
@@ -768,7 +721,7 @@
 static int __devinit clock_probe(struct of_device *op, const struct of_device_id *match)
 {
 	struct device_node *dp = op->node;
-	char *model = of_get_property(dp, "model", NULL);
+	const char *model = of_get_property(dp, "model", NULL);
 	unsigned long size, flags;
 	void __iomem *regs;
 
@@ -900,7 +853,6 @@
 		prop = of_find_property(dp, "stick-frequency", NULL);
 	}
 	clock = *(unsigned int *) prop->value;
-	timer_tick_offset = clock / HZ;
 
 #ifdef CONFIG_SMP
 	smp_tick_init();
@@ -909,26 +861,6 @@
 	return clock;
 }
 
-static void sparc64_start_timers(void)
-{
-	unsigned long pstate;
-
-	/* Guarantee that the following sequences execute
-	 * uninterrupted.
-	 */
-	__asm__ __volatile__("rdpr	%%pstate, %0\n\t"
-			     "wrpr	%0, %1, %%pstate"
-			     : "=r" (pstate)
-			     : "i" (PSTATE_IE));
-
-	tick_ops->init_tick(timer_tick_offset);
-
-	/* Restore PSTATE_IE. */
-	__asm__ __volatile__("wrpr	%0, 0x0, %%pstate"
-			     : /* no outputs */
-			     : "r" (pstate));
-}
-
 struct freq_table {
 	unsigned long clock_tick_ref;
 	unsigned int ref_freq;
@@ -975,29 +907,148 @@
 
 #endif /* CONFIG_CPU_FREQ */
 
-static struct time_interpolator sparc64_cpu_interpolator = {
-	.source		=	TIME_SOURCE_CPU,
-	.shift		=	16,
-	.mask		=	0xffffffffffffffffLL
+static int sparc64_next_event(unsigned long delta,
+			      struct clock_event_device *evt)
+{
+	return tick_ops->add_compare(delta) ? -ETIME : 0;
+}
+
+static void sparc64_timer_setup(enum clock_event_mode mode,
+				struct clock_event_device *evt)
+{
+	switch (mode) {
+	case CLOCK_EVT_MODE_ONESHOT:
+		break;
+
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		tick_ops->disable_irq();
+		break;
+
+	case CLOCK_EVT_MODE_PERIODIC:
+	case CLOCK_EVT_MODE_UNUSED:
+		WARN_ON(1);
+		break;
+	};
+}
+
+static struct clock_event_device sparc64_clockevent = {
+	.features	= CLOCK_EVT_FEAT_ONESHOT,
+	.set_mode	= sparc64_timer_setup,
+	.set_next_event	= sparc64_next_event,
+	.rating		= 100,
+	.shift		= 30,
+	.irq		= -1,
+};
+static DEFINE_PER_CPU(struct clock_event_device, sparc64_events);
+
+void timer_interrupt(int irq, struct pt_regs *regs)
+{
+	struct pt_regs *old_regs = set_irq_regs(regs);
+	unsigned long tick_mask = tick_ops->softint_mask;
+	int cpu = smp_processor_id();
+	struct clock_event_device *evt = &per_cpu(sparc64_events, cpu);
+
+	clear_softint(tick_mask);
+
+	irq_enter();
+
+	kstat_this_cpu.irqs[0]++;
+
+	if (unlikely(!evt->event_handler)) {
+		printk(KERN_WARNING
+		       "Spurious SPARC64 timer interrupt on cpu %d\n", cpu);
+	} else
+		evt->event_handler(evt);
+
+	irq_exit();
+
+	set_irq_regs(old_regs);
+}
+
+void __devinit setup_sparc64_timer(void)
+{
+	struct clock_event_device *sevt;
+	unsigned long pstate;
+
+	/* Guarantee that the following sequences execute
+	 * uninterrupted.
+	 */
+	__asm__ __volatile__("rdpr	%%pstate, %0\n\t"
+			     "wrpr	%0, %1, %%pstate"
+			     : "=r" (pstate)
+			     : "i" (PSTATE_IE));
+
+	tick_ops->init_tick();
+
+	/* Restore PSTATE_IE. */
+	__asm__ __volatile__("wrpr	%0, 0x0, %%pstate"
+			     : /* no outputs */
+			     : "r" (pstate));
+
+	sevt = &__get_cpu_var(sparc64_events);
+
+	memcpy(sevt, &sparc64_clockevent, sizeof(*sevt));
+	sevt->cpumask = cpumask_of_cpu(smp_processor_id());
+
+	clockevents_register_device(sevt);
+}
+
+#define SPARC64_NSEC_PER_CYC_SHIFT	32UL
+
+static struct clocksource clocksource_tick = {
+	.rating		= 100,
+	.mask		= CLOCKSOURCE_MASK(64),
+	.shift		= 16,
+	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
 };
 
-/* The quotient formula is taken from the IA64 port. */
-#define SPARC64_NSEC_PER_CYC_SHIFT	10UL
+static void __init setup_clockevent_multiplier(unsigned long hz)
+{
+	unsigned long mult, shift = 32;
+
+	while (1) {
+		mult = div_sc(hz, NSEC_PER_SEC, shift);
+		if (mult && (mult >> 32UL) == 0UL)
+			break;
+
+		shift--;
+	}
+
+	sparc64_clockevent.shift = shift;
+	sparc64_clockevent.mult = mult;
+}
+
 void __init time_init(void)
 {
 	unsigned long clock = sparc64_init_timers();
 
-	sparc64_cpu_interpolator.frequency = clock;
-	register_time_interpolator(&sparc64_cpu_interpolator);
-
-	/* Now that the interpolator is registered, it is
-	 * safe to start the timer ticking.
-	 */
-	sparc64_start_timers();
-
 	timer_ticks_per_nsec_quotient =
-		(((NSEC_PER_SEC << SPARC64_NSEC_PER_CYC_SHIFT) +
-		  (clock / 2)) / clock);
+		clocksource_hz2mult(clock, SPARC64_NSEC_PER_CYC_SHIFT);
+
+	clocksource_tick.name = tick_ops->name;
+	clocksource_tick.mult =
+		clocksource_hz2mult(clock,
+				    clocksource_tick.shift);
+	clocksource_tick.read = tick_ops->get_tick;
+
+	printk("clocksource: mult[%x] shift[%d]\n",
+	       clocksource_tick.mult, clocksource_tick.shift);
+
+	clocksource_register(&clocksource_tick);
+
+	sparc64_clockevent.name = tick_ops->name;
+
+	setup_clockevent_multiplier(clock);
+
+	sparc64_clockevent.max_delta_ns =
+		clockevent_delta2ns(0x7fffffffffffffff, &sparc64_clockevent);
+	sparc64_clockevent.min_delta_ns =
+		clockevent_delta2ns(0xF, &sparc64_clockevent);
+
+	printk("clockevent: mult[%lx] shift[%d]\n",
+	       sparc64_clockevent.mult, sparc64_clockevent.shift);
+
+	setup_sparc64_timer();
 
 #ifdef CONFIG_CPU_FREQ
 	cpufreq_register_notifier(&sparc64_cpufreq_notifier_block,
@@ -1126,10 +1177,6 @@
 #define RTC_IS_OPEN		0x01	/* means /dev/rtc is in use	*/
 static unsigned char mini_rtc_status;	/* bitmapped status byte.	*/
 
-/* months start at 0 now */
-static unsigned char days_in_mo[] =
-{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
-
 #define FEBRUARY	2
 #define	STARTOFTIME	1970
 #define SECDAY		86400L
@@ -1278,8 +1325,7 @@
 
 	case RTC_SET_TIME:	/* Set the RTC */
 	    {
-		int year;
-		unsigned char leap_yr;
+		int year, days;
 
 		if (!capable(CAP_SYS_TIME))
 			return -EACCES;
@@ -1288,14 +1334,14 @@
 			return -EFAULT;
 
 		year = wtime.tm_year + 1900;
-		leap_yr = ((!(year % 4) && (year % 100)) ||
-			   !(year % 400));
+		days = month_days[wtime.tm_mon] +
+		       ((wtime.tm_mon == 1) && leapyear(year));
 
-		if ((wtime.tm_mon < 0 || wtime.tm_mon > 11) || (wtime.tm_mday < 1))
+		if ((wtime.tm_mon < 0 || wtime.tm_mon > 11) ||
+		    (wtime.tm_mday < 1))
 			return -EINVAL;
 
-		if (wtime.tm_mday < 0 || wtime.tm_mday >
-		    (days_in_mo[wtime.tm_mon] + ((wtime.tm_mon == 1) && leap_yr)))
+		if (wtime.tm_mday < 0 || wtime.tm_mday > days)
 			return -EINVAL;
 
 		if (wtime.tm_hour < 0 || wtime.tm_hour >= 24 ||
diff --git a/arch/sparc64/kernel/ttable.S b/arch/sparc64/kernel/ttable.S
index d7d2a8b..7575aa3 100644
--- a/arch/sparc64/kernel/ttable.S
+++ b/arch/sparc64/kernel/ttable.S
@@ -60,11 +60,7 @@
 tl0_irq5:	TRAP_IRQ(handler_irq, 5)
 tl0_irq6:	BTRAP(0x46) BTRAP(0x47) BTRAP(0x48) BTRAP(0x49)
 tl0_irq10:	BTRAP(0x4a) BTRAP(0x4b) BTRAP(0x4c) BTRAP(0x4d)
-#ifndef CONFIG_SMP
-tl0_irq14:	TRAP_IRQ(timer_irq, 14)
-#else
-tl0_irq14:	TICK_SMP_IRQ
-#endif
+tl0_irq14:	TRAP_IRQ(timer_interrupt, 14)
 tl0_irq15:	TRAP_IRQ(handler_irq, 15)
 tl0_resv050:	BTRAP(0x50) BTRAP(0x51) BTRAP(0x52) BTRAP(0x53) BTRAP(0x54) BTRAP(0x55)
 tl0_resv056:	BTRAP(0x56) BTRAP(0x57) BTRAP(0x58) BTRAP(0x59) BTRAP(0x5a) BTRAP(0x5b)
diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c
index f146071..cafadcb 100644
--- a/arch/sparc64/mm/init.c
+++ b/arch/sparc64/mm/init.c
@@ -122,26 +122,21 @@
 				size = 0UL;
 			base = new_base;
 		}
+		if (size == 0UL) {
+			/* If it is empty, simply get rid of it.
+			 * This simplifies the logic of the other
+			 * functions that process these arrays.
+			 */
+			memmove(&regs[i], &regs[i + 1],
+				(ents - i - 1) * sizeof(regs[0]));
+			i--;
+			ents--;
+			continue;
+		}
 		regs[i].phys_addr = base;
 		regs[i].reg_size = size;
 	}
 
-	for (i = 0; i < ents; i++) {
-		if (regs[i].reg_size == 0UL) {
-			int j;
-
-			for (j = i; j < ents - 1; j++) {
-				regs[j].phys_addr =
-					regs[j+1].phys_addr;
-				regs[j].reg_size =
-					regs[j+1].reg_size;
-			}
-
-			ents--;
-			i--;
-		}
-	}
-
 	*num_ents = ents;
 
 	sort(regs, ents, sizeof(struct linux_prom64_registers),
@@ -154,15 +149,6 @@
 unsigned long kern_base __read_mostly;
 unsigned long kern_size __read_mostly;
 
-/* get_new_mmu_context() uses "cache + 1".  */
-DEFINE_SPINLOCK(ctx_alloc_lock);
-unsigned long tlb_context_cache = CTX_FIRST_VERSION - 1;
-#define CTX_BMAP_SLOTS (1UL << (CTX_NR_BITS - 6))
-unsigned long mmu_context_bmap[CTX_BMAP_SLOTS];
-
-/* References to special section boundaries */
-extern char  _start[], _end[];
-
 /* Initial ramdisk setup */
 extern unsigned long sparc_ramdisk_image64;
 extern unsigned int sparc_ramdisk_image;
@@ -406,19 +392,70 @@
 	if (tlb_type == spitfire) {
 		unsigned long kaddr;
 
-		for (kaddr = start; kaddr < end; kaddr += PAGE_SIZE)
-			__flush_icache_page(__get_phys(kaddr));
+		/* This code only runs on Spitfire cpus so this is
+		 * why we can assume _PAGE_PADDR_4U.
+		 */
+		for (kaddr = start; kaddr < end; kaddr += PAGE_SIZE) {
+			unsigned long paddr, mask = _PAGE_PADDR_4U;
+
+			if (kaddr >= PAGE_OFFSET)
+				paddr = kaddr & mask;
+			else {
+				pgd_t *pgdp = pgd_offset_k(kaddr);
+				pud_t *pudp = pud_offset(pgdp, kaddr);
+				pmd_t *pmdp = pmd_offset(pudp, kaddr);
+				pte_t *ptep = pte_offset_kernel(pmdp, kaddr);
+
+				paddr = pte_val(*ptep) & mask;
+			}
+			__flush_icache_page(paddr);
+		}
 	}
 }
 
 void show_mem(void)
 {
-	printk("Mem-info:\n");
+	unsigned long total = 0, reserved = 0;
+	unsigned long shared = 0, cached = 0;
+	pg_data_t *pgdat;
+
+	printk(KERN_INFO "Mem-info:\n");
 	show_free_areas();
-	printk("Free swap:       %6ldkB\n",
+	printk(KERN_INFO "Free swap:       %6ldkB\n",
 	       nr_swap_pages << (PAGE_SHIFT-10));
-	printk("%ld pages of RAM\n", num_physpages);
-	printk("%lu free pages\n", nr_free_pages());
+	for_each_online_pgdat(pgdat) {
+		unsigned long i, flags;
+
+		pgdat_resize_lock(pgdat, &flags);
+		for (i = 0; i < pgdat->node_spanned_pages; i++) {
+			struct page *page = pgdat_page_nr(pgdat, i);
+			total++;
+			if (PageReserved(page))
+				reserved++;
+			else if (PageSwapCache(page))
+				cached++;
+			else if (page_count(page))
+				shared += page_count(page) - 1;
+		}
+		pgdat_resize_unlock(pgdat, &flags);
+	}
+
+	printk(KERN_INFO "%lu pages of RAM\n", total);
+	printk(KERN_INFO "%lu reserved pages\n", reserved);
+	printk(KERN_INFO "%lu pages shared\n", shared);
+	printk(KERN_INFO "%lu pages swap cached\n", cached);
+
+	printk(KERN_INFO "%lu pages dirty\n",
+	       global_page_state(NR_FILE_DIRTY));
+	printk(KERN_INFO "%lu pages writeback\n",
+	       global_page_state(NR_WRITEBACK));
+	printk(KERN_INFO "%lu pages mapped\n",
+	       global_page_state(NR_FILE_MAPPED));
+	printk(KERN_INFO "%lu pages slab\n",
+		global_page_state(NR_SLAB_RECLAIMABLE) +
+		global_page_state(NR_SLAB_UNRECLAIMABLE));
+	printk(KERN_INFO "%lu pages pagetables\n",
+	       global_page_state(NR_PAGETABLE));
 }
 
 void mmu_info(struct seq_file *m)
@@ -658,6 +695,13 @@
 }
 #endif /* DCACHE_ALIASING_POSSIBLE */
 
+/* get_new_mmu_context() uses "cache + 1".  */
+DEFINE_SPINLOCK(ctx_alloc_lock);
+unsigned long tlb_context_cache = CTX_FIRST_VERSION - 1;
+#define MAX_CTX_NR	(1UL << CTX_NR_BITS)
+#define CTX_BMAP_SLOTS	BITS_TO_LONGS(MAX_CTX_NR)
+DECLARE_BITMAP(mmu_context_bmap, MAX_CTX_NR);
+
 /* Caller does TLB context flushing on local CPU if necessary.
  * The caller also ensures that CTX_VALID(mm->context) is false.
  *
@@ -717,95 +761,6 @@
 		smp_new_mmu_context_version();
 }
 
-void sparc_ultra_dump_itlb(void)
-{
-        int slot;
-
-	if (tlb_type == spitfire) {
-		printk ("Contents of itlb: ");
-		for (slot = 0; slot < 14; slot++) printk ("    ");
-		printk ("%2x:%016lx,%016lx\n",
-			0,
-			spitfire_get_itlb_tag(0), spitfire_get_itlb_data(0));
-		for (slot = 1; slot < 64; slot+=3) {
-			printk ("%2x:%016lx,%016lx %2x:%016lx,%016lx %2x:%016lx,%016lx\n", 
-				slot,
-				spitfire_get_itlb_tag(slot), spitfire_get_itlb_data(slot),
-				slot+1,
-				spitfire_get_itlb_tag(slot+1), spitfire_get_itlb_data(slot+1),
-				slot+2,
-				spitfire_get_itlb_tag(slot+2), spitfire_get_itlb_data(slot+2));
-		}
-	} else if (tlb_type == cheetah || tlb_type == cheetah_plus) {
-		printk ("Contents of itlb0:\n");
-		for (slot = 0; slot < 16; slot+=2) {
-			printk ("%2x:%016lx,%016lx %2x:%016lx,%016lx\n",
-				slot,
-				cheetah_get_litlb_tag(slot), cheetah_get_litlb_data(slot),
-				slot+1,
-				cheetah_get_litlb_tag(slot+1), cheetah_get_litlb_data(slot+1));
-		}
-		printk ("Contents of itlb2:\n");
-		for (slot = 0; slot < 128; slot+=2) {
-			printk ("%2x:%016lx,%016lx %2x:%016lx,%016lx\n",
-				slot,
-				cheetah_get_itlb_tag(slot), cheetah_get_itlb_data(slot),
-				slot+1,
-				cheetah_get_itlb_tag(slot+1), cheetah_get_itlb_data(slot+1));
-		}
-	}
-}
-
-void sparc_ultra_dump_dtlb(void)
-{
-        int slot;
-
-	if (tlb_type == spitfire) {
-		printk ("Contents of dtlb: ");
-		for (slot = 0; slot < 14; slot++) printk ("    ");
-		printk ("%2x:%016lx,%016lx\n", 0,
-			spitfire_get_dtlb_tag(0), spitfire_get_dtlb_data(0));
-		for (slot = 1; slot < 64; slot+=3) {
-			printk ("%2x:%016lx,%016lx %2x:%016lx,%016lx %2x:%016lx,%016lx\n", 
-				slot,
-				spitfire_get_dtlb_tag(slot), spitfire_get_dtlb_data(slot),
-				slot+1,
-				spitfire_get_dtlb_tag(slot+1), spitfire_get_dtlb_data(slot+1),
-				slot+2,
-				spitfire_get_dtlb_tag(slot+2), spitfire_get_dtlb_data(slot+2));
-		}
-	} else if (tlb_type == cheetah || tlb_type == cheetah_plus) {
-		printk ("Contents of dtlb0:\n");
-		for (slot = 0; slot < 16; slot+=2) {
-			printk ("%2x:%016lx,%016lx %2x:%016lx,%016lx\n",
-				slot,
-				cheetah_get_ldtlb_tag(slot), cheetah_get_ldtlb_data(slot),
-				slot+1,
-				cheetah_get_ldtlb_tag(slot+1), cheetah_get_ldtlb_data(slot+1));
-		}
-		printk ("Contents of dtlb2:\n");
-		for (slot = 0; slot < 512; slot+=2) {
-			printk ("%2x:%016lx,%016lx %2x:%016lx,%016lx\n",
-				slot,
-				cheetah_get_dtlb_tag(slot, 2), cheetah_get_dtlb_data(slot, 2),
-				slot+1,
-				cheetah_get_dtlb_tag(slot+1, 2), cheetah_get_dtlb_data(slot+1, 2));
-		}
-		if (tlb_type == cheetah_plus) {
-			printk ("Contents of dtlb3:\n");
-			for (slot = 0; slot < 512; slot+=2) {
-				printk ("%2x:%016lx,%016lx %2x:%016lx,%016lx\n",
-					slot,
-					cheetah_get_dtlb_tag(slot, 3), cheetah_get_dtlb_data(slot, 3),
-					slot+1,
-					cheetah_get_dtlb_tag(slot+1, 3), cheetah_get_dtlb_data(slot+1, 3));
-			}
-		}
-	}
-}
-
-extern unsigned long cmdline_memory_size;
-
 /* Find a free area for the bootmem map, avoiding the kernel image
  * and the initial ramdisk.
  */
@@ -815,8 +770,8 @@
 	unsigned long avoid_start, avoid_end, bootmap_size;
 	int i;
 
-	bootmap_size = ((end_pfn - start_pfn) + 7) / 8;
-	bootmap_size = ALIGN(bootmap_size, sizeof(long));
+	bootmap_size = bootmem_bootmap_pages(end_pfn - start_pfn);
+	bootmap_size <<= PAGE_SHIFT;
 
 	avoid_start = avoid_end = 0;
 #ifdef CONFIG_BLK_DEV_INITRD
@@ -983,6 +938,20 @@
 	}
 }
 
+/* About pages_avail, this is the value we will use to calculate
+ * the zholes_size[] argument given to free_area_init_node().  The
+ * page allocator uses this to calculate nr_kernel_pages,
+ * nr_all_pages and zone->present_pages.  On NUMA it is used
+ * to calculate zone->min_unmapped_pages and zone->min_slab_pages.
+ *
+ * So this number should really be set to what the page allocator
+ * actually ends up with.  This means:
+ * 1) It should include bootmem map pages, we'll release those.
+ * 2) It should not include the kernel image, except for the
+ *    __init sections which we will also release.
+ * 3) It should include the initrd image, since we'll release
+ *    that too.
+ */
 static unsigned long __init bootmem_init(unsigned long *pages_avail,
 					 unsigned long phys_base)
 {
@@ -1069,7 +1038,6 @@
 			initrd_start, initrd_end);
 #endif
 		reserve_bootmem(initrd_start, size);
-		*pages_avail -= PAGE_ALIGN(size) >> PAGE_SHIFT;
 
 		initrd_start += PAGE_OFFSET;
 		initrd_end += PAGE_OFFSET;
@@ -1082,6 +1050,11 @@
 	reserve_bootmem(kern_base, kern_size);
 	*pages_avail -= PAGE_ALIGN(kern_size) >> PAGE_SHIFT;
 
+	/* Add back in the initmem pages. */
+	size = ((unsigned long)(__init_end) & PAGE_MASK) -
+		PAGE_ALIGN((unsigned long)__init_begin);
+	*pages_avail += size >> PAGE_SHIFT;
+
 	/* Reserve the bootmem map.   We do not account for it
 	 * in pages_avail because we will release that memory
 	 * in free_all_bootmem.
@@ -1092,7 +1065,6 @@
 		    (bootmap_pfn << PAGE_SHIFT), size);
 #endif
 	reserve_bootmem((bootmap_pfn << PAGE_SHIFT), size);
-	*pages_avail -= PAGE_ALIGN(size) >> PAGE_SHIFT;
 
 	for (i = 0; i < pavail_ents; i++) {
 		unsigned long start_pfn, end_pfn;
@@ -1584,6 +1556,10 @@
 #ifdef CONFIG_DEBUG_BOOTMEM
 	prom_printf("mem_init: Calling free_all_bootmem().\n");
 #endif
+
+	/* We subtract one to account for the mem_map_zero page
+	 * allocated below.
+	 */
 	totalram_pages = num_physpages = free_all_bootmem() - 1;
 
 	/*
@@ -1883,62 +1859,6 @@
 	return val | paddr;
 }
 
-/*
- * Translate PROM's mapping we capture at boot time into physical address.
- * The second parameter is only set from prom_callback() invocations.
- */
-unsigned long prom_virt_to_phys(unsigned long promva, int *error)
-{
-	unsigned long mask;
-	int i;
-
-	mask = _PAGE_PADDR_4U;
-	if (tlb_type == hypervisor)
-		mask = _PAGE_PADDR_4V;
-
-	for (i = 0; i < prom_trans_ents; i++) {
-		struct linux_prom_translation *p = &prom_trans[i];
-
-		if (promva >= p->virt &&
-		    promva < (p->virt + p->size)) {
-			unsigned long base = p->data & mask;
-
-			if (error)
-				*error = 0;
-			return base + (promva & (8192 - 1));
-		}
-	}
-	if (error)
-		*error = 1;
-	return 0UL;
-}
-
-/* XXX We should kill off this ugly thing at so me point. XXX */
-unsigned long sun4u_get_pte(unsigned long addr)
-{
-	pgd_t *pgdp;
-	pud_t *pudp;
-	pmd_t *pmdp;
-	pte_t *ptep;
-	unsigned long mask = _PAGE_PADDR_4U;
-
-	if (tlb_type == hypervisor)
-		mask = _PAGE_PADDR_4V;
-
-	if (addr >= PAGE_OFFSET)
-		return addr & mask;
-
-	if ((addr >= LOW_OBP_ADDRESS) && (addr < HI_OBP_ADDRESS))
-		return prom_virt_to_phys(addr, NULL);
-
-	pgdp = pgd_offset_k(addr);
-	pudp = pud_offset(pgdp, addr);
-	pmdp = pmd_offset(pudp, addr);
-	ptep = pte_offset_kernel(pmdp, addr);
-
-	return pte_val(*ptep) & mask;
-}
-
 /* If not locked, zap it. */
 void __flush_tlb_all(void)
 {
diff --git a/arch/sparc64/solaris/misc.c b/arch/sparc64/solaris/misc.c
index 9fcaad6..542c808 100644
--- a/arch/sparc64/solaris/misc.c
+++ b/arch/sparc64/solaris/misc.c
@@ -224,7 +224,8 @@
 
 	*buffer = 0;
 	if (dp) {
-		char *val = of_get_property(dp, "system-board-serial#", &len);
+		const char *val =
+			of_get_property(dp, "system-board-serial#", &len);
 
 		if (val && len > 0) {
 			if (len > sz)
diff --git a/arch/um/drivers/daemon_kern.c b/arch/um/drivers/daemon_kern.c
index 9c2e7a7..adeece1 100644
--- a/arch/um/drivers/daemon_kern.c
+++ b/arch/um/drivers/daemon_kern.c
@@ -46,7 +46,7 @@
 {
 	*skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER);
 	if(*skb == NULL) return(-ENOMEM);
-	return(net_recvfrom(fd, (*skb)->mac.raw, 
+	return(net_recvfrom(fd, skb_mac_header(*skb),
 			    (*skb)->dev->mtu + ETH_HEADER_OTHER));
 }
 
diff --git a/arch/um/drivers/mcast_kern.c b/arch/um/drivers/mcast_kern.c
index 52ccb7b..e6b8e0d 100644
--- a/arch/um/drivers/mcast_kern.c
+++ b/arch/um/drivers/mcast_kern.c
@@ -50,7 +50,7 @@
 {
 	*skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER);
 	if(*skb == NULL) return(-ENOMEM);
-	return(net_recvfrom(fd, (*skb)->mac.raw, 
+	return(net_recvfrom(fd, skb_mac_header(*skb),
 			    (*skb)->dev->mtu + ETH_HEADER_OTHER));
 }
 
diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c
index 04e31f8..8593037 100644
--- a/arch/um/drivers/net_kern.c
+++ b/arch/um/drivers/net_kern.c
@@ -55,7 +55,7 @@
 
 	skb->dev = dev;
 	skb_put(skb, dev->mtu);
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	pkt_len = (*lp->read)(lp->fd, &skb, lp);
 
 	if (pkt_len > 0) {
diff --git a/arch/um/drivers/pcap_kern.c b/arch/um/drivers/pcap_kern.c
index e67362a..9488493 100644
--- a/arch/um/drivers/pcap_kern.c
+++ b/arch/um/drivers/pcap_kern.c
@@ -36,7 +36,7 @@
 {
 	*skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER);
 	if(*skb == NULL) return(-ENOMEM);
-	return(pcap_user_read(fd, (*skb)->mac.raw, 
+	return(pcap_user_read(fd, skb_mac_header(*skb),
 			      (*skb)->dev->mtu + ETH_HEADER_OTHER,
 			      (struct pcap_data *) &lp->user));
 }
diff --git a/arch/um/drivers/slip_kern.c b/arch/um/drivers/slip_kern.c
index 25634bd..125c44f 100644
--- a/arch/um/drivers/slip_kern.c
+++ b/arch/um/drivers/slip_kern.c
@@ -49,7 +49,7 @@
 static int slip_read(int fd, struct sk_buff **skb, 
 		       struct uml_net_private *lp)
 {
-	return(slip_user_read(fd, (*skb)->mac.raw, (*skb)->dev->mtu, 
+	return(slip_user_read(fd, skb_mac_header(*skb), (*skb)->dev->mtu,
 			      (struct slip_data *) &lp->user));
 }
 
diff --git a/arch/um/drivers/slirp_kern.c b/arch/um/drivers/slirp_kern.c
index b3ed8fb..0a0324a 100644
--- a/arch/um/drivers/slirp_kern.c
+++ b/arch/um/drivers/slirp_kern.c
@@ -53,7 +53,7 @@
 static int slirp_read(int fd, struct sk_buff **skb, 
 		       struct uml_net_private *lp)
 {
-	return(slirp_user_read(fd, (*skb)->mac.raw, (*skb)->dev->mtu, 
+	return(slirp_user_read(fd, skb_mac_header(*skb), (*skb)->dev->mtu,
 			      (struct slirp_data *) &lp->user));
 }
 
diff --git a/arch/um/os-Linux/drivers/ethertap_kern.c b/arch/um/os-Linux/drivers/ethertap_kern.c
index 7054182..1268914 100644
--- a/arch/um/os-Linux/drivers/ethertap_kern.c
+++ b/arch/um/os-Linux/drivers/ethertap_kern.c
@@ -43,7 +43,7 @@
 
 	*skb = ether_adjust_skb(*skb, ETH_HEADER_ETHERTAP);
 	if(*skb == NULL) return(-ENOMEM);
-	len = net_recvfrom(fd, (*skb)->mac.raw, 
+	len = net_recvfrom(fd, skb_mac_header(*skb),
 			   (*skb)->dev->mtu + 2 * ETH_HEADER_ETHERTAP);
 	if(len <= 0) return(len);
 	skb_pull(*skb, 2);
diff --git a/arch/um/os-Linux/drivers/tuntap_kern.c b/arch/um/os-Linux/drivers/tuntap_kern.c
index 76570a2..f1714e7 100644
--- a/arch/um/os-Linux/drivers/tuntap_kern.c
+++ b/arch/um/os-Linux/drivers/tuntap_kern.c
@@ -43,7 +43,7 @@
 {
 	*skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER);
 	if(*skb == NULL) return(-ENOMEM);
-	return(net_read(fd, (*skb)->mac.raw, 
+	return(net_read(fd, skb_mac_header(*skb),
 			(*skb)->dev->mtu + ETH_HEADER_OTHER));
 }
 
diff --git a/arch/x86_64/defconfig b/arch/x86_64/defconfig
index 7a1e251..b263788 100644
--- a/arch/x86_64/defconfig
+++ b/arch/x86_64/defconfig
@@ -631,7 +631,6 @@
 CONFIG_SATA_VIA=y
 # CONFIG_SATA_VITESSE is not set
 # CONFIG_SATA_INIC162X is not set
-CONFIG_SATA_INTEL_COMBINED=y
 CONFIG_SATA_ACPI=y
 # CONFIG_PATA_ALI is not set
 # CONFIG_PATA_AMD is not set
diff --git a/arch/xtensa/lib/Makefile b/arch/xtensa/lib/Makefile
index ed935b5..6c4fdd8 100644
--- a/arch/xtensa/lib/Makefile
+++ b/arch/xtensa/lib/Makefile
@@ -2,6 +2,6 @@
 # Makefile for Xtensa-specific library files.
 #
 
-lib-y	+= memcopy.o memset.o checksum.o strcasecmp.o \
+lib-y	+= memcopy.o memset.o checksum.o \
 	   usercopy.o strncpy_user.o strnlen_user.o
 lib-$(CONFIG_PCI) += pci-auto.o
diff --git a/arch/xtensa/lib/strcasecmp.c b/arch/xtensa/lib/strcasecmp.c
deleted file mode 100644
index 165b2d6..0000000
--- a/arch/xtensa/lib/strcasecmp.c
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- *  linux/arch/xtensa/lib/strcasecmp.c
- *
- *  This file is subject to the terms and conditions of the GNU General
- *  Public License.  See the file "COPYING" in the main directory of
- *  this archive for more details.
- *
- *  Copyright (C) 2002 Tensilica Inc.
- */
-
-#include <linux/string.h>
-
-
-/* We handle nothing here except the C locale.  Since this is used in
-   only one place, on strings known to contain only 7 bit ASCII, this
-   is ok.  */
-
-int strcasecmp(const char *a, const char *b)
-{
-	int ca, cb;
-
-	do {
-		ca = *a++ & 0xff;
-		cb = *b++ & 0xff;
-		if (ca >= 'A' && ca <= 'Z')
-			ca += 'a' - 'A';
-		if (cb >= 'A' && cb <= 'Z')
-			cb += 'a' - 'A';
-	} while (ca == cb && ca != '\0');
-
-	return ca - cb;
-}
diff --git a/arch/xtensa/platform-iss/network.c b/arch/xtensa/platform-iss/network.c
index 8ebfc87..ab05bff 100644
--- a/arch/xtensa/platform-iss/network.c
+++ b/arch/xtensa/platform-iss/network.c
@@ -386,7 +386,7 @@
 	/* Setup skb */
 
 	skb->dev = dev;
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	pkt_len = lp->tp.read(lp, &skb);
 	skb_put(skb, pkt_len);
 
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index f92ba2a..64df3fa 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -9,7 +9,6 @@
 #include <linux/module.h>
 #include <linux/blkdev.h>
 #include <linux/elevator.h>
-#include <linux/hash.h>
 #include <linux/rbtree.h>
 #include <linux/ioprio.h>
 
@@ -26,19 +25,17 @@
 static const int cfq_slice_async_rq = 2;
 static int cfq_slice_idle = HZ / 125;
 
+/*
+ * grace period before allowing idle class to get disk access
+ */
 #define CFQ_IDLE_GRACE		(HZ / 10)
-#define CFQ_SLICE_SCALE		(5)
-
-#define CFQ_KEY_ASYNC		(0)
 
 /*
- * for the hash of cfqq inside the cfqd
+ * below this threshold, we consider thinktime immediate
  */
-#define CFQ_QHASH_SHIFT		6
-#define CFQ_QHASH_ENTRIES	(1 << CFQ_QHASH_SHIFT)
-#define list_entry_qhash(entry)	hlist_entry((entry), struct cfq_queue, cfq_hash)
+#define CFQ_MIN_TT		(2)
 
-#define list_entry_cfqq(ptr)	list_entry((ptr), struct cfq_queue, cfq_list)
+#define CFQ_SLICE_SCALE		(5)
 
 #define RQ_CIC(rq)		((struct cfq_io_context*)(rq)->elevator_private)
 #define RQ_CFQQ(rq)		((rq)->elevator_private2)
@@ -56,17 +53,21 @@
 #define ASYNC			(0)
 #define SYNC			(1)
 
-#define cfq_cfqq_dispatched(cfqq)	\
-	((cfqq)->on_dispatch[ASYNC] + (cfqq)->on_dispatch[SYNC])
-
-#define cfq_cfqq_class_sync(cfqq)	((cfqq)->key != CFQ_KEY_ASYNC)
-
-#define cfq_cfqq_sync(cfqq)		\
-	(cfq_cfqq_class_sync(cfqq) || (cfqq)->on_dispatch[SYNC])
-
 #define sample_valid(samples)	((samples) > 80)
 
 /*
+ * Most of our rbtree usage is for sorting with min extraction, so
+ * if we cache the leftmost node we don't have to walk down the tree
+ * to find it. Idea borrowed from Ingo Molnars CFS scheduler. We should
+ * move this into the elevator for the rq sorting as well.
+ */
+struct cfq_rb_root {
+	struct rb_root rb;
+	struct rb_node *left;
+};
+#define CFQ_RB_ROOT	(struct cfq_rb_root) { RB_ROOT, NULL, }
+
+/*
  * Per block device queue structure
  */
 struct cfq_data {
@@ -75,18 +76,11 @@
 	/*
 	 * rr list of queues with requests and the count of them
 	 */
-	struct list_head rr_list[CFQ_PRIO_LISTS];
-	struct list_head busy_rr;
-	struct list_head cur_rr;
-	struct list_head idle_rr;
+	struct cfq_rb_root service_tree;
 	unsigned int busy_queues;
 
-	/*
-	 * cfqq lookup hash
-	 */
-	struct hlist_head *cfq_hash;
-
 	int rq_in_driver;
+	int sync_flight;
 	int hw_tag;
 
 	/*
@@ -97,12 +91,10 @@
 
 	struct cfq_queue *active_queue;
 	struct cfq_io_context *active_cic;
-	int cur_prio, cur_end_prio;
-	unsigned int dispatch_slice;
 
 	struct timer_list idle_class_timer;
 
-	sector_t last_sector;
+	sector_t last_position;
 	unsigned long last_end_request;
 
 	/*
@@ -117,6 +109,9 @@
 	unsigned int cfq_slice_idle;
 
 	struct list_head cic_list;
+
+	sector_t new_seek_mean;
+	u64 new_seek_total;
 };
 
 /*
@@ -127,12 +122,10 @@
 	atomic_t ref;
 	/* parent cfq_data */
 	struct cfq_data *cfqd;
-	/* cfqq lookup hash */
-	struct hlist_node cfq_hash;
-	/* hash key */
-	unsigned int key;
-	/* member of the rr/busy/cur/idle cfqd list */
-	struct list_head cfq_list;
+	/* service_tree member */
+	struct rb_node rb_node;
+	/* service_tree key */
+	unsigned long rb_key;
 	/* sorted list of pending requests */
 	struct rb_root sort_list;
 	/* if fifo isn't expired, next request to serve */
@@ -147,11 +140,10 @@
 	struct list_head fifo;
 
 	unsigned long slice_end;
-	unsigned long service_last;
 	long slice_resid;
 
-	/* number of requests that are on the dispatch list */
-	int on_dispatch[2];
+	/* number of requests that are on the dispatch list or inside driver */
+	int dispatched;
 
 	/* io prio of this group */
 	unsigned short ioprio, org_ioprio;
@@ -159,6 +151,8 @@
 
 	/* various state flags, see below */
 	unsigned int flags;
+
+	sector_t last_request_pos;
 };
 
 enum cfqq_state_flags {
@@ -172,6 +166,7 @@
 	CFQ_CFQQ_FLAG_prio_changed,	/* task priority has changed */
 	CFQ_CFQQ_FLAG_queue_new,	/* queue never been serviced */
 	CFQ_CFQQ_FLAG_slice_new,	/* no requests dispatched in slice */
+	CFQ_CFQQ_FLAG_sync,		/* synchronous queue */
 };
 
 #define CFQ_CFQQ_FNS(name)						\
@@ -198,11 +193,38 @@
 CFQ_CFQQ_FNS(prio_changed);
 CFQ_CFQQ_FNS(queue_new);
 CFQ_CFQQ_FNS(slice_new);
+CFQ_CFQQ_FNS(sync);
 #undef CFQ_CFQQ_FNS
 
-static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *, unsigned int, unsigned short);
 static void cfq_dispatch_insert(request_queue_t *, struct request *);
-static struct cfq_queue *cfq_get_queue(struct cfq_data *cfqd, unsigned int key, struct task_struct *tsk, gfp_t gfp_mask);
+static struct cfq_queue *cfq_get_queue(struct cfq_data *, int,
+				       struct task_struct *, gfp_t);
+static struct cfq_io_context *cfq_cic_rb_lookup(struct cfq_data *,
+						struct io_context *);
+
+static inline struct cfq_queue *cic_to_cfqq(struct cfq_io_context *cic,
+					    int is_sync)
+{
+	return cic->cfqq[!!is_sync];
+}
+
+static inline void cic_set_cfqq(struct cfq_io_context *cic,
+				struct cfq_queue *cfqq, int is_sync)
+{
+	cic->cfqq[!!is_sync] = cfqq;
+}
+
+/*
+ * We regard a request as SYNC, if it's either a read or has the SYNC bit
+ * set (in which case it could also be direct WRITE).
+ */
+static inline int cfq_bio_sync(struct bio *bio)
+{
+	if (bio_data_dir(bio) == READ || bio_sync(bio))
+		return 1;
+
+	return 0;
+}
 
 /*
  * scheduler run of queue, if there are requests pending and no one in the
@@ -221,44 +243,31 @@
 	return !cfqd->busy_queues;
 }
 
-static inline pid_t cfq_queue_pid(struct task_struct *task, int rw, int is_sync)
-{
-	/*
-	 * Use the per-process queue, for read requests and syncronous writes
-	 */
-	if (!(rw & REQ_RW) || is_sync)
-		return task->pid;
-
-	return CFQ_KEY_ASYNC;
-}
-
 /*
  * Scale schedule slice based on io priority. Use the sync time slice only
  * if a queue is marked sync and has sync io queued. A sync queue with async
  * io only, should not get full sync slice length.
  */
+static inline int cfq_prio_slice(struct cfq_data *cfqd, int sync,
+				 unsigned short prio)
+{
+	const int base_slice = cfqd->cfq_slice[sync];
+
+	WARN_ON(prio >= IOPRIO_BE_NR);
+
+	return base_slice + (base_slice/CFQ_SLICE_SCALE * (4 - prio));
+}
+
 static inline int
 cfq_prio_to_slice(struct cfq_data *cfqd, struct cfq_queue *cfqq)
 {
-	const int base_slice = cfqd->cfq_slice[cfq_cfqq_sync(cfqq)];
-
-	WARN_ON(cfqq->ioprio >= IOPRIO_BE_NR);
-
-	return base_slice + (base_slice/CFQ_SLICE_SCALE * (4 - cfqq->ioprio));
+	return cfq_prio_slice(cfqd, cfq_cfqq_sync(cfqq), cfqq->ioprio);
 }
 
 static inline void
 cfq_set_prio_slice(struct cfq_data *cfqd, struct cfq_queue *cfqq)
 {
 	cfqq->slice_end = cfq_prio_to_slice(cfqd, cfqq) + jiffies;
-	cfqq->slice_end += cfqq->slice_resid;
-
-	/*
-	 * Don't carry over residual for more than one slice, we only want
-	 * to slightly correct the fairness. Carrying over forever would
-	 * easily introduce oscillations.
-	 */
-	cfqq->slice_resid = 0;
 }
 
 /*
@@ -307,7 +316,7 @@
 	s1 = rq1->sector;
 	s2 = rq2->sector;
 
-	last = cfqd->last_sector;
+	last = cfqd->last_position;
 
 	/*
 	 * by definition, 1KiB is 2 sectors
@@ -372,6 +381,26 @@
 }
 
 /*
+ * The below is leftmost cache rbtree addon
+ */
+static struct rb_node *cfq_rb_first(struct cfq_rb_root *root)
+{
+	if (!root->left)
+		root->left = rb_first(&root->rb);
+
+	return root->left;
+}
+
+static void cfq_rb_erase(struct rb_node *n, struct cfq_rb_root *root)
+{
+	if (root->left == n)
+		root->left = NULL;
+
+	rb_erase(n, &root->rb);
+	RB_CLEAR_NODE(n);
+}
+
+/*
  * would be nice to take fifo expire time into account as well
  */
 static struct request *
@@ -398,78 +427,96 @@
 	return cfq_choose_req(cfqd, next, prev);
 }
 
-static void cfq_resort_rr_list(struct cfq_queue *cfqq, int preempted)
+static unsigned long cfq_slice_offset(struct cfq_data *cfqd,
+				      struct cfq_queue *cfqq)
 {
-	struct cfq_data *cfqd = cfqq->cfqd;
-	struct list_head *list, *n;
-	struct cfq_queue *__cfqq;
+	/*
+	 * just an approximation, should be ok.
+	 */
+	return (cfqd->busy_queues - 1) * (cfq_prio_slice(cfqd, 1, 0) -
+		       cfq_prio_slice(cfqd, cfq_cfqq_sync(cfqq), cfqq->ioprio));
+}
 
+/*
+ * The cfqd->service_tree holds all pending cfq_queue's that have
+ * requests waiting to be processed. It is sorted in the order that
+ * we will service the queues.
+ */
+static void cfq_service_tree_add(struct cfq_data *cfqd,
+				    struct cfq_queue *cfqq, int add_front)
+{
+	struct rb_node **p = &cfqd->service_tree.rb.rb_node;
+	struct rb_node *parent = NULL;
+	unsigned long rb_key;
+	int left;
+
+	if (!add_front) {
+		rb_key = cfq_slice_offset(cfqd, cfqq) + jiffies;
+		rb_key += cfqq->slice_resid;
+		cfqq->slice_resid = 0;
+	} else
+		rb_key = 0;
+
+	if (!RB_EMPTY_NODE(&cfqq->rb_node)) {
+		/*
+		 * same position, nothing more to do
+		 */
+		if (rb_key == cfqq->rb_key)
+			return;
+
+		cfq_rb_erase(&cfqq->rb_node, &cfqd->service_tree);
+	}
+
+	left = 1;
+	while (*p) {
+		struct cfq_queue *__cfqq;
+		struct rb_node **n;
+
+		parent = *p;
+		__cfqq = rb_entry(parent, struct cfq_queue, rb_node);
+
+		/*
+		 * sort RT queues first, we always want to give
+		 * preference to them. IDLE queues goes to the back.
+		 * after that, sort on the next service time.
+		 */
+		if (cfq_class_rt(cfqq) > cfq_class_rt(__cfqq))
+			n = &(*p)->rb_left;
+		else if (cfq_class_rt(cfqq) < cfq_class_rt(__cfqq))
+			n = &(*p)->rb_right;
+		else if (cfq_class_idle(cfqq) < cfq_class_idle(__cfqq))
+			n = &(*p)->rb_left;
+		else if (cfq_class_idle(cfqq) > cfq_class_idle(__cfqq))
+			n = &(*p)->rb_right;
+		else if (rb_key < __cfqq->rb_key)
+			n = &(*p)->rb_left;
+		else
+			n = &(*p)->rb_right;
+
+		if (n == &(*p)->rb_right)
+			left = 0;
+
+		p = n;
+	}
+
+	if (left)
+		cfqd->service_tree.left = &cfqq->rb_node;
+
+	cfqq->rb_key = rb_key;
+	rb_link_node(&cfqq->rb_node, parent, p);
+	rb_insert_color(&cfqq->rb_node, &cfqd->service_tree.rb);
+}
+
+/*
+ * Update cfqq's position in the service tree.
+ */
+static void cfq_resort_rr_list(struct cfq_data *cfqd, struct cfq_queue *cfqq)
+{
 	/*
 	 * Resorting requires the cfqq to be on the RR list already.
 	 */
-	if (!cfq_cfqq_on_rr(cfqq))
-		return;
-
-	list_del(&cfqq->cfq_list);
-
-	if (cfq_class_rt(cfqq))
-		list = &cfqd->cur_rr;
-	else if (cfq_class_idle(cfqq))
-		list = &cfqd->idle_rr;
-	else {
-		/*
-		 * if cfqq has requests in flight, don't allow it to be
-		 * found in cfq_set_active_queue before it has finished them.
-		 * this is done to increase fairness between a process that
-		 * has lots of io pending vs one that only generates one
-		 * sporadically or synchronously
-		 */
-		if (cfq_cfqq_dispatched(cfqq))
-			list = &cfqd->busy_rr;
-		else
-			list = &cfqd->rr_list[cfqq->ioprio];
-	}
-
-	if (preempted || cfq_cfqq_queue_new(cfqq)) {
-		/*
-		 * If this queue was preempted or is new (never been serviced),
-		 * let it be added first for fairness but beind other new
-		 * queues.
-		 */
-		n = list;
-		while (n->next != list) {
-			__cfqq = list_entry_cfqq(n->next);
-			if (!cfq_cfqq_queue_new(__cfqq))
-				break;
-
-			n = n->next;
-		}
-		list_add_tail(&cfqq->cfq_list, n);
-	} else if (!cfq_cfqq_class_sync(cfqq)) {
-		/*
-		 * async queue always goes to the end. this wont be overly
-		 * unfair to writes, as the sort of the sync queue wont be
-		 * allowed to pass the async queue again.
-		 */
-		list_add_tail(&cfqq->cfq_list, list);
-	} else {
-		/*
-		 * sort by last service, but don't cross a new or async
-		 * queue. we don't cross a new queue because it hasn't been
-		 * service before, and we don't cross an async queue because
-		 * it gets added to the end on expire.
-		 */
-		n = list;
-		while ((n = n->prev) != list) {
-			struct cfq_queue *__cfqq = list_entry_cfqq(n);
-
-			if (!cfq_cfqq_class_sync(cfqq) || !__cfqq->service_last)
-				break;
-			if (time_before(__cfqq->service_last, cfqq->service_last))
-				break;
-		}
-		list_add(&cfqq->cfq_list, n);
-	}
+	if (cfq_cfqq_on_rr(cfqq))
+		cfq_service_tree_add(cfqd, cfqq, 0);
 }
 
 /*
@@ -483,15 +530,21 @@
 	cfq_mark_cfqq_on_rr(cfqq);
 	cfqd->busy_queues++;
 
-	cfq_resort_rr_list(cfqq, 0);
+	cfq_resort_rr_list(cfqd, cfqq);
 }
 
+/*
+ * Called when the cfqq no longer has requests pending, remove it from
+ * the service tree.
+ */
 static inline void
 cfq_del_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq)
 {
 	BUG_ON(!cfq_cfqq_on_rr(cfqq));
 	cfq_clear_cfqq_on_rr(cfqq);
-	list_del_init(&cfqq->cfq_list);
+
+	if (!RB_EMPTY_NODE(&cfqq->rb_node))
+		cfq_rb_erase(&cfqq->rb_node, &cfqd->service_tree);
 
 	BUG_ON(!cfqd->busy_queues);
 	cfqd->busy_queues--;
@@ -552,10 +605,14 @@
 cfq_find_rq_fmerge(struct cfq_data *cfqd, struct bio *bio)
 {
 	struct task_struct *tsk = current;
-	pid_t key = cfq_queue_pid(tsk, bio_data_dir(bio), bio_sync(bio));
+	struct cfq_io_context *cic;
 	struct cfq_queue *cfqq;
 
-	cfqq = cfq_find_cfq_hash(cfqd, key, tsk->ioprio);
+	cic = cfq_cic_rb_lookup(cfqd, tsk->io_context);
+	if (!cic)
+		return NULL;
+
+	cfqq = cic_to_cfqq(cic, cfq_bio_sync(bio));
 	if (cfqq) {
 		sector_t sector = bio->bi_sector + bio_sectors(bio);
 
@@ -579,6 +636,8 @@
 	 */
 	if (!cfqd->hw_tag && cfqd->rq_in_driver > 4)
 		cfqd->hw_tag = 1;
+
+	cfqd->last_position = rq->hard_sector + rq->hard_nr_sectors;
 }
 
 static void cfq_deactivate_request(request_queue_t *q, struct request *rq)
@@ -605,8 +664,7 @@
 	}
 }
 
-static int
-cfq_merge(request_queue_t *q, struct request **req, struct bio *bio)
+static int cfq_merge(request_queue_t *q, struct request **req, struct bio *bio)
 {
 	struct cfq_data *cfqd = q->elevator->elevator_data;
 	struct request *__rq;
@@ -648,23 +706,24 @@
 			   struct bio *bio)
 {
 	struct cfq_data *cfqd = q->elevator->elevator_data;
-	const int rw = bio_data_dir(bio);
+	struct cfq_io_context *cic;
 	struct cfq_queue *cfqq;
-	pid_t key;
 
 	/*
 	 * Disallow merge of a sync bio into an async request.
 	 */
-	if ((bio_data_dir(bio) == READ || bio_sync(bio)) && !rq_is_sync(rq))
+	if (cfq_bio_sync(bio) && !rq_is_sync(rq))
 		return 0;
 
 	/*
 	 * Lookup the cfqq that this bio will be queued with. Allow
 	 * merge only if rq is queued there.
 	 */
-	key = cfq_queue_pid(current, rw, bio_sync(bio));
-	cfqq = cfq_find_cfq_hash(cfqd, key, current->ioprio);
+	cic = cfq_cic_rb_lookup(cfqd, current->io_context);
+	if (!cic)
+		return 0;
 
+	cfqq = cic_to_cfqq(cic, cfq_bio_sync(bio));
 	if (cfqq == RQ_CFQQ(rq))
 		return 1;
 
@@ -684,6 +743,7 @@
 		cfq_clear_cfqq_must_alloc_slice(cfqq);
 		cfq_clear_cfqq_fifo_expire(cfqq);
 		cfq_mark_cfqq_slice_new(cfqq);
+		cfq_clear_cfqq_queue_new(cfqq);
 	}
 
 	cfqd->active_queue = cfqq;
@@ -694,23 +754,21 @@
  */
 static void
 __cfq_slice_expired(struct cfq_data *cfqd, struct cfq_queue *cfqq,
-		    int preempted, int timed_out)
+		    int timed_out)
 {
 	if (cfq_cfqq_wait_request(cfqq))
 		del_timer(&cfqd->idle_slice_timer);
 
 	cfq_clear_cfqq_must_dispatch(cfqq);
 	cfq_clear_cfqq_wait_request(cfqq);
-	cfq_clear_cfqq_queue_new(cfqq);
 
 	/*
-	 * store what was left of this slice, if the queue idled out
-	 * or was preempted
+	 * store what was left of this slice, if the queue idled/timed out
 	 */
 	if (timed_out && !cfq_cfqq_slice_new(cfqq))
 		cfqq->slice_resid = cfqq->slice_end - jiffies;
 
-	cfq_resort_rr_list(cfqq, preempted);
+	cfq_resort_rr_list(cfqd, cfqq);
 
 	if (cfqq == cfqd->active_queue)
 		cfqd->active_queue = NULL;
@@ -719,163 +777,152 @@
 		put_io_context(cfqd->active_cic->ioc);
 		cfqd->active_cic = NULL;
 	}
-
-	cfqd->dispatch_slice = 0;
 }
 
-static inline void cfq_slice_expired(struct cfq_data *cfqd, int preempted,
-				     int timed_out)
+static inline void cfq_slice_expired(struct cfq_data *cfqd, int timed_out)
 {
 	struct cfq_queue *cfqq = cfqd->active_queue;
 
 	if (cfqq)
-		__cfq_slice_expired(cfqd, cfqq, preempted, timed_out);
+		__cfq_slice_expired(cfqd, cfqq, timed_out);
 }
 
 /*
- * 0
- * 0,1
- * 0,1,2
- * 0,1,2,3
- * 0,1,2,3,4
- * 0,1,2,3,4,5
- * 0,1,2,3,4,5,6
- * 0,1,2,3,4,5,6,7
+ * Get next queue for service. Unless we have a queue preemption,
+ * we'll simply select the first cfqq in the service tree.
  */
-static int cfq_get_next_prio_level(struct cfq_data *cfqd)
+static struct cfq_queue *cfq_get_next_queue(struct cfq_data *cfqd)
 {
-	int prio, wrap;
+	struct cfq_queue *cfqq;
+	struct rb_node *n;
 
-	prio = -1;
-	wrap = 0;
-	do {
-		int p;
+	if (RB_EMPTY_ROOT(&cfqd->service_tree.rb))
+		return NULL;
 
-		for (p = cfqd->cur_prio; p <= cfqd->cur_end_prio; p++) {
-			if (!list_empty(&cfqd->rr_list[p])) {
-				prio = p;
-				break;
-			}
+	n = cfq_rb_first(&cfqd->service_tree);
+	cfqq = rb_entry(n, struct cfq_queue, rb_node);
+
+	if (cfq_class_idle(cfqq)) {
+		unsigned long end;
+
+		/*
+		 * if we have idle queues and no rt or be queues had
+		 * pending requests, either allow immediate service if
+		 * the grace period has passed or arm the idle grace
+		 * timer
+		 */
+		end = cfqd->last_end_request + CFQ_IDLE_GRACE;
+		if (time_before(jiffies, end)) {
+			mod_timer(&cfqd->idle_class_timer, end);
+			cfqq = NULL;
 		}
-
-		if (prio != -1)
-			break;
-		cfqd->cur_prio = 0;
-		if (++cfqd->cur_end_prio == CFQ_PRIO_LISTS) {
-			cfqd->cur_end_prio = 0;
-			if (wrap)
-				break;
-			wrap = 1;
-		}
-	} while (1);
-
-	if (unlikely(prio == -1))
-		return -1;
-
-	BUG_ON(prio >= CFQ_PRIO_LISTS);
-
-	list_splice_init(&cfqd->rr_list[prio], &cfqd->cur_rr);
-
-	cfqd->cur_prio = prio + 1;
-	if (cfqd->cur_prio > cfqd->cur_end_prio) {
-		cfqd->cur_end_prio = cfqd->cur_prio;
-		cfqd->cur_prio = 0;
-	}
-	if (cfqd->cur_end_prio == CFQ_PRIO_LISTS) {
-		cfqd->cur_prio = 0;
-		cfqd->cur_end_prio = 0;
 	}
 
-	return prio;
+	return cfqq;
 }
 
+/*
+ * Get and set a new active queue for service.
+ */
 static struct cfq_queue *cfq_set_active_queue(struct cfq_data *cfqd)
 {
-	struct cfq_queue *cfqq = NULL;
+	struct cfq_queue *cfqq;
 
-	if (!list_empty(&cfqd->cur_rr) || cfq_get_next_prio_level(cfqd) != -1) {
-		/*
-		 * if current list is non-empty, grab first entry. if it is
-		 * empty, get next prio level and grab first entry then if any
-		 * are spliced
-		 */
-		cfqq = list_entry_cfqq(cfqd->cur_rr.next);
-	} else if (!list_empty(&cfqd->busy_rr)) {
-		/*
-		 * If no new queues are available, check if the busy list has
-		 * some before falling back to idle io.
-		 */
-		cfqq = list_entry_cfqq(cfqd->busy_rr.next);
-	} else if (!list_empty(&cfqd->idle_rr)) {
-		/*
-		 * if we have idle queues and no rt or be queues had pending
-		 * requests, either allow immediate service if the grace period
-		 * has passed or arm the idle grace timer
-		 */
-		unsigned long end = cfqd->last_end_request + CFQ_IDLE_GRACE;
-
-		if (time_after_eq(jiffies, end))
-			cfqq = list_entry_cfqq(cfqd->idle_rr.next);
-		else
-			mod_timer(&cfqd->idle_class_timer, end);
-	}
-
+	cfqq = cfq_get_next_queue(cfqd);
 	__cfq_set_active_queue(cfqd, cfqq);
 	return cfqq;
 }
 
-#define CIC_SEEKY(cic) ((cic)->seek_mean > (128 * 1024))
+static inline sector_t cfq_dist_from_last(struct cfq_data *cfqd,
+					  struct request *rq)
+{
+	if (rq->sector >= cfqd->last_position)
+		return rq->sector - cfqd->last_position;
+	else
+		return cfqd->last_position - rq->sector;
+}
 
-static int cfq_arm_slice_timer(struct cfq_data *cfqd)
+static inline int cfq_rq_close(struct cfq_data *cfqd, struct request *rq)
+{
+	struct cfq_io_context *cic = cfqd->active_cic;
+
+	if (!sample_valid(cic->seek_samples))
+		return 0;
+
+	return cfq_dist_from_last(cfqd, rq) <= cic->seek_mean;
+}
+
+static int cfq_close_cooperator(struct cfq_data *cfq_data,
+				struct cfq_queue *cfqq)
+{
+	/*
+	 * We should notice if some of the queues are cooperating, eg
+	 * working closely on the same area of the disk. In that case,
+	 * we can group them together and don't waste time idling.
+	 */
+	return 0;
+}
+
+#define CIC_SEEKY(cic) ((cic)->seek_mean > (8 * 1024))
+
+static void cfq_arm_slice_timer(struct cfq_data *cfqd)
 {
 	struct cfq_queue *cfqq = cfqd->active_queue;
 	struct cfq_io_context *cic;
 	unsigned long sl;
 
 	WARN_ON(!RB_EMPTY_ROOT(&cfqq->sort_list));
+	WARN_ON(cfq_cfqq_slice_new(cfqq));
 
 	/*
 	 * idle is disabled, either manually or by past process history
 	 */
-	if (!cfqd->cfq_slice_idle)
-		return 0;
-	if (!cfq_cfqq_idle_window(cfqq))
-		return 0;
+	if (!cfqd->cfq_slice_idle || !cfq_cfqq_idle_window(cfqq))
+		return;
+
 	/*
 	 * task has exited, don't wait
 	 */
 	cic = cfqd->active_cic;
 	if (!cic || !cic->ioc->task)
-		return 0;
+		return;
+
+	/*
+	 * See if this prio level has a good candidate
+	 */
+	if (cfq_close_cooperator(cfqd, cfqq) &&
+	    (sample_valid(cic->ttime_samples) && cic->ttime_mean > 2))
+		return;
 
 	cfq_mark_cfqq_must_dispatch(cfqq);
 	cfq_mark_cfqq_wait_request(cfqq);
 
-	sl = min(cfqq->slice_end - 1, (unsigned long) cfqd->cfq_slice_idle);
-
 	/*
 	 * we don't want to idle for seeks, but we do want to allow
 	 * fair distribution of slice time for a process doing back-to-back
 	 * seeks. so allow a little bit of time for him to submit a new rq
 	 */
+	sl = cfqd->cfq_slice_idle;
 	if (sample_valid(cic->seek_samples) && CIC_SEEKY(cic))
-		sl = min(sl, msecs_to_jiffies(2));
+		sl = min(sl, msecs_to_jiffies(CFQ_MIN_TT));
 
 	mod_timer(&cfqd->idle_slice_timer, jiffies + sl);
-	return 1;
 }
 
+/*
+ * Move request from internal lists to the request queue dispatch list.
+ */
 static void cfq_dispatch_insert(request_queue_t *q, struct request *rq)
 {
 	struct cfq_data *cfqd = q->elevator->elevator_data;
 	struct cfq_queue *cfqq = RQ_CFQQ(rq);
 
 	cfq_remove_request(rq);
-	cfqq->on_dispatch[rq_is_sync(rq)]++;
+	cfqq->dispatched++;
 	elv_dispatch_sort(q, rq);
 
-	rq = list_entry(q->queue_head.prev, struct request, queuelist);
-	cfqd->last_sector = rq->sector + rq->nr_sectors;
+	if (cfq_cfqq_sync(cfqq))
+		cfqd->sync_flight++;
 }
 
 /*
@@ -895,13 +942,13 @@
 	if (list_empty(&cfqq->fifo))
 		return NULL;
 
-	fifo = cfq_cfqq_class_sync(cfqq);
+	fifo = cfq_cfqq_sync(cfqq);
 	rq = rq_entry_fifo(cfqq->fifo.next);
 
-	if (time_after(jiffies, rq->start_time + cfqd->cfq_fifo_expire[fifo]))
-		return rq;
+	if (time_before(jiffies, rq->start_time + cfqd->cfq_fifo_expire[fifo]))
+		return NULL;
 
-	return NULL;
+	return rq;
 }
 
 static inline int
@@ -915,7 +962,8 @@
 }
 
 /*
- * get next queue for service
+ * Select a queue for service. If we have a current active queue,
+ * check whether to continue servicing it, or retrieve and set a new one.
  */
 static struct cfq_queue *cfq_select_queue(struct cfq_data *cfqd)
 {
@@ -926,33 +974,41 @@
 		goto new_queue;
 
 	/*
-	 * slice has expired
+	 * The active queue has run out of time, expire it and select new.
 	 */
-	if (!cfq_cfqq_must_dispatch(cfqq) && cfq_slice_used(cfqq))
+	if (cfq_slice_used(cfqq))
 		goto expire;
 
 	/*
-	 * if queue has requests, dispatch one. if not, check if
-	 * enough slice is left to wait for one
+	 * The active queue has requests and isn't expired, allow it to
+	 * dispatch.
 	 */
 	if (!RB_EMPTY_ROOT(&cfqq->sort_list))
 		goto keep_queue;
-	else if (cfq_cfqq_slice_new(cfqq) || cfq_cfqq_dispatched(cfqq)) {
+
+	/*
+	 * No requests pending. If the active queue still has requests in
+	 * flight or is idling for a new request, allow either of these
+	 * conditions to happen (or time out) before selecting a new queue.
+	 */
+	if (timer_pending(&cfqd->idle_slice_timer) ||
+	    (cfqq->dispatched && cfq_cfqq_idle_window(cfqq))) {
 		cfqq = NULL;
 		goto keep_queue;
-	} else if (cfq_cfqq_class_sync(cfqq)) {
-		if (cfq_arm_slice_timer(cfqd))
-			return NULL;
 	}
 
 expire:
-	cfq_slice_expired(cfqd, 0, 0);
+	cfq_slice_expired(cfqd, 0);
 new_queue:
 	cfqq = cfq_set_active_queue(cfqd);
 keep_queue:
 	return cfqq;
 }
 
+/*
+ * Dispatch some requests from cfqq, moving them to the request queue
+ * dispatch list.
+ */
 static int
 __cfq_dispatch_requests(struct cfq_data *cfqd, struct cfq_queue *cfqq,
 			int max_dispatch)
@@ -975,7 +1031,6 @@
 		 */
 		cfq_dispatch_insert(cfqd->queue, rq);
 
-		cfqd->dispatch_slice++;
 		dispatched++;
 
 		if (!cfqd->active_cic) {
@@ -993,57 +1048,54 @@
 	 * queue always expire after 1 dispatch round.
 	 */
 	if (cfqd->busy_queues > 1 && ((!cfq_cfqq_sync(cfqq) &&
-	    cfqd->dispatch_slice >= cfq_prio_to_maxrq(cfqd, cfqq)) ||
+	    dispatched >= cfq_prio_to_maxrq(cfqd, cfqq)) ||
 	    cfq_class_idle(cfqq))) {
 		cfqq->slice_end = jiffies + 1;
-		cfq_slice_expired(cfqd, 0, 0);
+		cfq_slice_expired(cfqd, 0);
 	}
 
 	return dispatched;
 }
 
-static int
-cfq_forced_dispatch_cfqqs(struct list_head *list)
+static inline int __cfq_forced_dispatch_cfqq(struct cfq_queue *cfqq)
 {
-	struct cfq_queue *cfqq, *next;
-	int dispatched;
+	int dispatched = 0;
 
-	dispatched = 0;
-	list_for_each_entry_safe(cfqq, next, list, cfq_list) {
-		while (cfqq->next_rq) {
-			cfq_dispatch_insert(cfqq->cfqd->queue, cfqq->next_rq);
-			dispatched++;
-		}
-		BUG_ON(!list_empty(&cfqq->fifo));
+	while (cfqq->next_rq) {
+		cfq_dispatch_insert(cfqq->cfqd->queue, cfqq->next_rq);
+		dispatched++;
 	}
 
+	BUG_ON(!list_empty(&cfqq->fifo));
 	return dispatched;
 }
 
-static int
-cfq_forced_dispatch(struct cfq_data *cfqd)
+/*
+ * Drain our current requests. Used for barriers and when switching
+ * io schedulers on-the-fly.
+ */
+static int cfq_forced_dispatch(struct cfq_data *cfqd)
 {
-	int i, dispatched = 0;
+	int dispatched = 0;
+	struct rb_node *n;
 
-	for (i = 0; i < CFQ_PRIO_LISTS; i++)
-		dispatched += cfq_forced_dispatch_cfqqs(&cfqd->rr_list[i]);
+	while ((n = cfq_rb_first(&cfqd->service_tree)) != NULL) {
+		struct cfq_queue *cfqq = rb_entry(n, struct cfq_queue, rb_node);
 
-	dispatched += cfq_forced_dispatch_cfqqs(&cfqd->busy_rr);
-	dispatched += cfq_forced_dispatch_cfqqs(&cfqd->cur_rr);
-	dispatched += cfq_forced_dispatch_cfqqs(&cfqd->idle_rr);
+		dispatched += __cfq_forced_dispatch_cfqq(cfqq);
+	}
 
-	cfq_slice_expired(cfqd, 0, 0);
+	cfq_slice_expired(cfqd, 0);
 
 	BUG_ON(cfqd->busy_queues);
 
 	return dispatched;
 }
 
-static int
-cfq_dispatch_requests(request_queue_t *q, int force)
+static int cfq_dispatch_requests(request_queue_t *q, int force)
 {
 	struct cfq_data *cfqd = q->elevator->elevator_data;
-	struct cfq_queue *cfqq, *prev_cfqq;
+	struct cfq_queue *cfqq;
 	int dispatched;
 
 	if (!cfqd->busy_queues)
@@ -1053,36 +1105,28 @@
 		return cfq_forced_dispatch(cfqd);
 
 	dispatched = 0;
-	prev_cfqq = NULL;
 	while ((cfqq = cfq_select_queue(cfqd)) != NULL) {
 		int max_dispatch;
 
-		if (cfqd->busy_queues > 1) {
-			/*
-			 * Don't repeat dispatch from the previous queue.
-			 */
-			if (prev_cfqq == cfqq)
-				break;
-
-			/*
-			 * So we have dispatched before in this round, if the
-			 * next queue has idling enabled (must be sync), don't
-			 * allow it service until the previous have continued.
-			 */
-			if (cfqd->rq_in_driver && cfq_cfqq_idle_window(cfqq))
-				break;
-		}
-
-		cfq_clear_cfqq_must_dispatch(cfqq);
-		cfq_clear_cfqq_wait_request(cfqq);
-		del_timer(&cfqd->idle_slice_timer);
-
 		max_dispatch = cfqd->cfq_quantum;
 		if (cfq_class_idle(cfqq))
 			max_dispatch = 1;
 
+		if (cfqq->dispatched >= max_dispatch) {
+			if (cfqd->busy_queues > 1)
+				break;
+			if (cfqq->dispatched >= 4 * max_dispatch)
+				break;
+		}
+
+		if (cfqd->sync_flight && !cfq_cfqq_sync(cfqq))
+			break;
+
+		cfq_clear_cfqq_must_dispatch(cfqq);
+		cfq_clear_cfqq_wait_request(cfqq);
+		del_timer(&cfqd->idle_slice_timer);
+
 		dispatched += __cfq_dispatch_requests(cfqd, cfqq, max_dispatch);
-		prev_cfqq = cfqq;
 	}
 
 	return dispatched;
@@ -1108,48 +1152,21 @@
 	BUG_ON(cfq_cfqq_on_rr(cfqq));
 
 	if (unlikely(cfqd->active_queue == cfqq)) {
-		__cfq_slice_expired(cfqd, cfqq, 0, 0);
+		__cfq_slice_expired(cfqd, cfqq, 0);
 		cfq_schedule_dispatch(cfqd);
 	}
 
-	/*
-	 * it's on the empty list and still hashed
-	 */
-	list_del(&cfqq->cfq_list);
-	hlist_del(&cfqq->cfq_hash);
 	kmem_cache_free(cfq_pool, cfqq);
 }
 
-static struct cfq_queue *
-__cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned int key, unsigned int prio,
-		    const int hashval)
-{
-	struct hlist_head *hash_list = &cfqd->cfq_hash[hashval];
-	struct hlist_node *entry;
-	struct cfq_queue *__cfqq;
-
-	hlist_for_each_entry(__cfqq, entry, hash_list, cfq_hash) {
-		const unsigned short __p = IOPRIO_PRIO_VALUE(__cfqq->org_ioprio_class, __cfqq->org_ioprio);
-
-		if (__cfqq->key == key && (__p == prio || !prio))
-			return __cfqq;
-	}
-
-	return NULL;
-}
-
-static struct cfq_queue *
-cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned int key, unsigned short prio)
-{
-	return __cfq_find_cfq_hash(cfqd, key, prio, hash_long(key, CFQ_QHASH_SHIFT));
-}
-
 static void cfq_free_io_context(struct io_context *ioc)
 {
 	struct cfq_io_context *__cic;
 	struct rb_node *n;
 	int freed = 0;
 
+	ioc->ioc_data = NULL;
+
 	while ((n = rb_first(&ioc->cic_root)) != NULL) {
 		__cic = rb_entry(n, struct cfq_io_context, rb_node);
 		rb_erase(&__cic->rb_node, &ioc->cic_root);
@@ -1166,7 +1183,7 @@
 static void cfq_exit_cfqq(struct cfq_data *cfqd, struct cfq_queue *cfqq)
 {
 	if (unlikely(cfqq == cfqd->active_queue)) {
-		__cfq_slice_expired(cfqd, cfqq, 0, 0);
+		__cfq_slice_expired(cfqd, cfqq, 0);
 		cfq_schedule_dispatch(cfqd);
 	}
 
@@ -1191,10 +1208,6 @@
 	}
 }
 
-
-/*
- * Called with interrupts disabled
- */
 static void cfq_exit_single_io_context(struct cfq_io_context *cic)
 {
 	struct cfq_data *cfqd = cic->key;
@@ -1208,15 +1221,20 @@
 	}
 }
 
+/*
+ * The process that ioc belongs to has exited, we need to clean up
+ * and put the internal structures we have that belongs to that process.
+ */
 static void cfq_exit_io_context(struct io_context *ioc)
 {
 	struct cfq_io_context *__cic;
 	struct rb_node *n;
 
+	ioc->ioc_data = NULL;
+
 	/*
 	 * put the reference this task is holding to the various queues
 	 */
-
 	n = rb_first(&ioc->cic_root);
 	while (n != NULL) {
 		__cic = rb_entry(n, struct cfq_io_context, rb_node);
@@ -1284,8 +1302,6 @@
 	 */
 	cfqq->org_ioprio = cfqq->ioprio;
 	cfqq->org_ioprio_class = cfqq->ioprio_class;
-
-	cfq_resort_rr_list(cfqq, 0);
 	cfq_clear_cfqq_prio_changed(cfqq);
 }
 
@@ -1303,7 +1319,7 @@
 	cfqq = cic->cfqq[ASYNC];
 	if (cfqq) {
 		struct cfq_queue *new_cfqq;
-		new_cfqq = cfq_get_queue(cfqd, CFQ_KEY_ASYNC, cic->ioc->task,
+		new_cfqq = cfq_get_queue(cfqd, ASYNC, cic->ioc->task,
 					 GFP_ATOMIC);
 		if (new_cfqq) {
 			cic->cfqq[ASYNC] = new_cfqq;
@@ -1335,16 +1351,16 @@
 }
 
 static struct cfq_queue *
-cfq_get_queue(struct cfq_data *cfqd, unsigned int key, struct task_struct *tsk,
+cfq_get_queue(struct cfq_data *cfqd, int is_sync, struct task_struct *tsk,
 	      gfp_t gfp_mask)
 {
-	const int hashval = hash_long(key, CFQ_QHASH_SHIFT);
 	struct cfq_queue *cfqq, *new_cfqq = NULL;
-	unsigned short ioprio;
+	struct cfq_io_context *cic;
 
 retry:
-	ioprio = tsk->ioprio;
-	cfqq = __cfq_find_cfq_hash(cfqd, key, ioprio, hashval);
+	cic = cfq_cic_rb_lookup(cfqd, tsk->io_context);
+	/* cic always exists here */
+	cfqq = cic_to_cfqq(cic, is_sync);
 
 	if (!cfqq) {
 		if (new_cfqq) {
@@ -1369,20 +1385,20 @@
 
 		memset(cfqq, 0, sizeof(*cfqq));
 
-		INIT_HLIST_NODE(&cfqq->cfq_hash);
-		INIT_LIST_HEAD(&cfqq->cfq_list);
+		RB_CLEAR_NODE(&cfqq->rb_node);
 		INIT_LIST_HEAD(&cfqq->fifo);
 
-		cfqq->key = key;
-		hlist_add_head(&cfqq->cfq_hash, &cfqd->cfq_hash[hashval]);
 		atomic_set(&cfqq->ref, 0);
 		cfqq->cfqd = cfqd;
 
-		if (key != CFQ_KEY_ASYNC)
+		if (is_sync) {
 			cfq_mark_cfqq_idle_window(cfqq);
+			cfq_mark_cfqq_sync(cfqq);
+		}
 
 		cfq_mark_cfqq_prio_changed(cfqq);
 		cfq_mark_cfqq_queue_new(cfqq);
+
 		cfq_init_prio_data(cfqq);
 	}
 
@@ -1395,10 +1411,17 @@
 	return cfqq;
 }
 
+/*
+ * We drop cfq io contexts lazily, so we may find a dead one.
+ */
 static void
 cfq_drop_dead_cic(struct io_context *ioc, struct cfq_io_context *cic)
 {
 	WARN_ON(!list_empty(&cic->queue_list));
+
+	if (ioc->ioc_data == cic)
+		ioc->ioc_data = NULL;
+
 	rb_erase(&cic->rb_node, &ioc->cic_root);
 	kmem_cache_free(cfq_ioc_pool, cic);
 	elv_ioc_count_dec(ioc_count);
@@ -1411,6 +1434,16 @@
 	struct cfq_io_context *cic;
 	void *k, *key = cfqd;
 
+	if (unlikely(!ioc))
+		return NULL;
+
+	/*
+	 * we maintain a last-hit cache, to avoid browsing over the tree
+	 */
+	cic = ioc->ioc_data;
+	if (cic && cic->key == cfqd)
+		return cic;
+
 restart:
 	n = ioc->cic_root.rb_node;
 	while (n) {
@@ -1426,8 +1459,10 @@
 			n = n->rb_left;
 		else if (key > k)
 			n = n->rb_right;
-		else
+		else {
+			ioc->ioc_data = cic;
 			return cic;
+		}
 	}
 
 	return NULL;
@@ -1524,7 +1559,8 @@
 }
 
 static void
-cfq_update_io_seektime(struct cfq_io_context *cic, struct request *rq)
+cfq_update_io_seektime(struct cfq_data *cfqd, struct cfq_io_context *cic,
+		       struct request *rq)
 {
 	sector_t sdist;
 	u64 total;
@@ -1534,6 +1570,11 @@
 	else
 		sdist = cic->last_request_pos - rq->sector;
 
+	if (!cic->seek_samples) {
+		cfqd->new_seek_total = (7*cic->seek_total + (u64)256*sdist) / 8;
+		cfqd->new_seek_mean = cfqd->new_seek_total / 256;
+	}
+
 	/*
 	 * Don't allow the seek distance to get too large from the
 	 * odd fragment, pagein, etc
@@ -1558,7 +1599,12 @@
 cfq_update_idle_window(struct cfq_data *cfqd, struct cfq_queue *cfqq,
 		       struct cfq_io_context *cic)
 {
-	int enable_idle = cfq_cfqq_idle_window(cfqq);
+	int enable_idle;
+
+	if (!cfq_cfqq_sync(cfqq))
+		return;
+
+	enable_idle = cfq_cfqq_idle_window(cfqq);
 
 	if (!cic->ioc->task || !cfqd->cfq_slice_idle ||
 	    (cfqd->hw_tag && CIC_SEEKY(cic)))
@@ -1584,24 +1630,28 @@
 cfq_should_preempt(struct cfq_data *cfqd, struct cfq_queue *new_cfqq,
 		   struct request *rq)
 {
-	struct cfq_queue *cfqq = cfqd->active_queue;
+	struct cfq_queue *cfqq;
+
+	cfqq = cfqd->active_queue;
+	if (!cfqq)
+		return 0;
+
+	if (cfq_slice_used(cfqq))
+		return 1;
 
 	if (cfq_class_idle(new_cfqq))
 		return 0;
 
-	if (!cfqq)
-		return 0;
-
 	if (cfq_class_idle(cfqq))
 		return 1;
-	if (!cfq_cfqq_wait_request(new_cfqq))
-		return 0;
+
 	/*
 	 * if the new request is sync, but the currently running queue is
 	 * not, let the sync request have priority.
 	 */
 	if (rq_is_sync(rq) && !cfq_cfqq_sync(cfqq))
 		return 1;
+
 	/*
 	 * So both queues are sync. Let the new request get disk time if
 	 * it's a metadata request and the current queue is doing regular IO.
@@ -1609,6 +1659,16 @@
 	if (rq_is_meta(rq) && !cfqq->meta_pending)
 		return 1;
 
+	if (!cfqd->active_cic || !cfq_cfqq_wait_request(cfqq))
+		return 0;
+
+	/*
+	 * if this request is as-good as one we would expect from the
+	 * current cfqq, let it preempt
+	 */
+	if (cfq_rq_close(cfqd, rq))
+		return 1;
+
 	return 0;
 }
 
@@ -1618,14 +1678,15 @@
  */
 static void cfq_preempt_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq)
 {
-	cfq_slice_expired(cfqd, 1, 1);
+	cfq_slice_expired(cfqd, 1);
 
 	/*
 	 * Put the new queue at the front of the of the current list,
 	 * so we know that it will be selected next.
 	 */
 	BUG_ON(!cfq_cfqq_on_rr(cfqq));
-	list_move(&cfqq->cfq_list, &cfqd->cur_rr);
+
+	cfq_service_tree_add(cfqd, cfqq, 1);
 
 	cfqq->slice_end = 0;
 	cfq_mark_cfqq_slice_new(cfqq);
@@ -1644,28 +1705,12 @@
 	if (rq_is_meta(rq))
 		cfqq->meta_pending++;
 
-	/*
-	 * we never wait for an async request and we don't allow preemption
-	 * of an async request. so just return early
-	 */
-	if (!rq_is_sync(rq)) {
-		/*
-		 * sync process issued an async request, if it's waiting
-		 * then expire it and kick rq handling.
-		 */
-		if (cic == cfqd->active_cic &&
-		    del_timer(&cfqd->idle_slice_timer)) {
-			cfq_slice_expired(cfqd, 0, 0);
-			blk_start_queueing(cfqd->queue);
-		}
-		return;
-	}
-
 	cfq_update_io_thinktime(cfqd, cic);
-	cfq_update_io_seektime(cic, rq);
+	cfq_update_io_seektime(cfqd, cic, rq);
 	cfq_update_idle_window(cfqd, cfqq, cic);
 
 	cic->last_request_pos = rq->sector + rq->nr_sectors;
+	cfqq->last_request_pos = cic->last_request_pos;
 
 	if (cfqq == cfqd->active_queue) {
 		/*
@@ -1714,16 +1759,16 @@
 	now = jiffies;
 
 	WARN_ON(!cfqd->rq_in_driver);
-	WARN_ON(!cfqq->on_dispatch[sync]);
+	WARN_ON(!cfqq->dispatched);
 	cfqd->rq_in_driver--;
-	cfqq->on_dispatch[sync]--;
-	cfqq->service_last = now;
+	cfqq->dispatched--;
+
+	if (cfq_cfqq_sync(cfqq))
+		cfqd->sync_flight--;
 
 	if (!cfq_class_idle(cfqq))
 		cfqd->last_end_request = now;
 
-	cfq_resort_rr_list(cfqq, 0);
-
 	if (sync)
 		RQ_CIC(rq)->last_end_request = now;
 
@@ -1737,12 +1782,13 @@
 			cfq_clear_cfqq_slice_new(cfqq);
 		}
 		if (cfq_slice_used(cfqq))
-			cfq_slice_expired(cfqd, 0, 1);
-		else if (sync && RB_EMPTY_ROOT(&cfqq->sort_list)) {
-			if (!cfq_arm_slice_timer(cfqd))
-				cfq_schedule_dispatch(cfqd);
-		}
+			cfq_slice_expired(cfqd, 1);
+		else if (sync && RB_EMPTY_ROOT(&cfqq->sort_list))
+			cfq_arm_slice_timer(cfqd);
 	}
+
+	if (!cfqd->rq_in_driver)
+		cfq_schedule_dispatch(cfqd);
 }
 
 /*
@@ -1751,9 +1797,6 @@
  */
 static void cfq_prio_boost(struct cfq_queue *cfqq)
 {
-	const int ioprio_class = cfqq->ioprio_class;
-	const int ioprio = cfqq->ioprio;
-
 	if (has_fs_excl()) {
 		/*
 		 * boost idle prio on transactions that would lock out other
@@ -1772,12 +1815,6 @@
 		if (cfqq->ioprio != cfqq->org_ioprio)
 			cfqq->ioprio = cfqq->org_ioprio;
 	}
-
-	/*
-	 * refile between round-robin lists if we moved the priority class
-	 */
-	if ((ioprio_class != cfqq->ioprio_class || ioprio != cfqq->ioprio))
-		cfq_resort_rr_list(cfqq, 0);
 }
 
 static inline int __cfq_may_queue(struct cfq_queue *cfqq)
@@ -1795,10 +1832,8 @@
 {
 	struct cfq_data *cfqd = q->elevator->elevator_data;
 	struct task_struct *tsk = current;
+	struct cfq_io_context *cic;
 	struct cfq_queue *cfqq;
-	unsigned int key;
-
-	key = cfq_queue_pid(tsk, rw, rw & REQ_RW_SYNC);
 
 	/*
 	 * don't force setup of a queue from here, as a call to may_queue
@@ -1806,7 +1841,11 @@
 	 * so just lookup a possibly existing queue, or return 'may queue'
 	 * if that fails
 	 */
-	cfqq = cfq_find_cfq_hash(cfqd, key, tsk->ioprio);
+	cic = cfq_cic_rb_lookup(cfqd, tsk->io_context);
+	if (!cic)
+		return ELV_MQUEUE_MAY;
+
+	cfqq = cic_to_cfqq(cic, rw & REQ_RW_SYNC);
 	if (cfqq) {
 		cfq_init_prio_data(cfqq);
 		cfq_prio_boost(cfqq);
@@ -1850,7 +1889,6 @@
 	struct cfq_io_context *cic;
 	const int rw = rq_data_dir(rq);
 	const int is_sync = rq_is_sync(rq);
-	pid_t key = cfq_queue_pid(tsk, rw, is_sync);
 	struct cfq_queue *cfqq;
 	unsigned long flags;
 
@@ -1863,14 +1901,15 @@
 	if (!cic)
 		goto queue_fail;
 
-	if (!cic->cfqq[is_sync]) {
-		cfqq = cfq_get_queue(cfqd, key, tsk, gfp_mask);
+	cfqq = cic_to_cfqq(cic, is_sync);
+	if (!cfqq) {
+		cfqq = cfq_get_queue(cfqd, is_sync, tsk, gfp_mask);
+
 		if (!cfqq)
 			goto queue_fail;
 
-		cic->cfqq[is_sync] = cfqq;
-	} else
-		cfqq = cic->cfqq[is_sync];
+		cic_set_cfqq(cic, cfqq, is_sync);
+	}
 
 	cfqq->allocated[rw]++;
 	cfq_clear_cfqq_must_alloc(cfqq);
@@ -1940,7 +1979,7 @@
 		}
 	}
 expire:
-	cfq_slice_expired(cfqd, 0, timed_out);
+	cfq_slice_expired(cfqd, timed_out);
 out_kick:
 	cfq_schedule_dispatch(cfqd);
 out_cont:
@@ -1986,7 +2025,7 @@
 	spin_lock_irq(q->queue_lock);
 
 	if (cfqd->active_queue)
-		__cfq_slice_expired(cfqd, cfqd->active_queue, 0, 0);
+		__cfq_slice_expired(cfqd, cfqd->active_queue, 0);
 
 	while (!list_empty(&cfqd->cic_list)) {
 		struct cfq_io_context *cic = list_entry(cfqd->cic_list.next,
@@ -2000,14 +2039,12 @@
 
 	cfq_shutdown_timer_wq(cfqd);
 
-	kfree(cfqd->cfq_hash);
 	kfree(cfqd);
 }
 
 static void *cfq_init_queue(request_queue_t *q)
 {
 	struct cfq_data *cfqd;
-	int i;
 
 	cfqd = kmalloc_node(sizeof(*cfqd), GFP_KERNEL, q->node);
 	if (!cfqd)
@@ -2015,21 +2052,9 @@
 
 	memset(cfqd, 0, sizeof(*cfqd));
 
-	for (i = 0; i < CFQ_PRIO_LISTS; i++)
-		INIT_LIST_HEAD(&cfqd->rr_list[i]);
-
-	INIT_LIST_HEAD(&cfqd->busy_rr);
-	INIT_LIST_HEAD(&cfqd->cur_rr);
-	INIT_LIST_HEAD(&cfqd->idle_rr);
+	cfqd->service_tree = CFQ_RB_ROOT;
 	INIT_LIST_HEAD(&cfqd->cic_list);
 
-	cfqd->cfq_hash = kmalloc_node(sizeof(struct hlist_head) * CFQ_QHASH_ENTRIES, GFP_KERNEL, q->node);
-	if (!cfqd->cfq_hash)
-		goto out_free;
-
-	for (i = 0; i < CFQ_QHASH_ENTRIES; i++)
-		INIT_HLIST_HEAD(&cfqd->cfq_hash[i]);
-
 	cfqd->queue = q;
 
 	init_timer(&cfqd->idle_slice_timer);
@@ -2053,9 +2078,6 @@
 	cfqd->cfq_slice_idle = cfq_slice_idle;
 
 	return cfqd;
-out_free:
-	kfree(cfqd);
-	return NULL;
 }
 
 static void cfq_slab_kill(void)
@@ -2087,7 +2109,6 @@
 /*
  * sysfs parts below -->
  */
-
 static ssize_t
 cfq_var_show(unsigned int var, char *page)
 {
diff --git a/block/elevator.c b/block/elevator.c
index 96a00c8..ce866eb 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -134,13 +134,13 @@
 {
 	struct elevator_type *e;
 
-	spin_lock_irq(&elv_list_lock);
+	spin_lock(&elv_list_lock);
 
 	e = elevator_find(name);
 	if (e && !try_module_get(e->elevator_owner))
 		e = NULL;
 
-	spin_unlock_irq(&elv_list_lock);
+	spin_unlock(&elv_list_lock);
 
 	return e;
 }
@@ -965,10 +965,11 @@
 int elv_register(struct elevator_type *e)
 {
 	char *def = "";
-	spin_lock_irq(&elv_list_lock);
+
+	spin_lock(&elv_list_lock);
 	BUG_ON(elevator_find(e->elevator_name));
 	list_add_tail(&e->list, &elv_list);
-	spin_unlock_irq(&elv_list_lock);
+	spin_unlock(&elv_list_lock);
 
 	if (!strcmp(e->elevator_name, chosen_elevator) ||
 			(!*chosen_elevator &&
@@ -998,9 +999,9 @@
 		read_unlock(&tasklist_lock);
 	}
 
-	spin_lock_irq(&elv_list_lock);
+	spin_lock(&elv_list_lock);
 	list_del_init(&e->list);
-	spin_unlock_irq(&elv_list_lock);
+	spin_unlock(&elv_list_lock);
 }
 EXPORT_SYMBOL_GPL(elv_unregister);
 
@@ -1118,7 +1119,7 @@
 	struct list_head *entry;
 	int len = 0;
 
-	spin_lock_irq(&elv_list_lock);
+	spin_lock(&elv_list_lock);
 	list_for_each(entry, &elv_list) {
 		struct elevator_type *__e;
 
@@ -1128,7 +1129,7 @@
 		else
 			len += sprintf(name+len, "%s ", __e->elevator_name);
 	}
-	spin_unlock_irq(&elv_list_lock);
+	spin_unlock(&elv_list_lock);
 
 	len += sprintf(len+name, "\n");
 	return len;
diff --git a/block/ll_rw_blk.c b/block/ll_rw_blk.c
index 3de0695..123003a 100644
--- a/block/ll_rw_blk.c
+++ b/block/ll_rw_blk.c
@@ -3741,6 +3741,7 @@
 		ret->nr_batch_requests = 0; /* because this is 0 */
 		ret->aic = NULL;
 		ret->cic_root.rb_node = NULL;
+		ret->ioc_data = NULL;
 		/* make sure set_task_ioprio() sees the settings above */
 		smp_wmb();
 		tsk->io_context = ret;
diff --git a/crypto/michael_mic.c b/crypto/michael_mic.c
index 094397b..9e917b8 100644
--- a/crypto/michael_mic.c
+++ b/crypto/michael_mic.c
@@ -3,7 +3,7 @@
  *
  * Michael MIC (IEEE 802.11i/TKIP) keyed digest
  *
- * Copyright (c) 2004 Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright (c) 2004 Jouni Malinen <j@w1.fi>
  *
  * 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
@@ -173,4 +173,4 @@
 
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("Michael MIC");
-MODULE_AUTHOR("Jouni Malinen <jkmaline@cc.hut.fi>");
+MODULE_AUTHOR("Jouni Malinen <j@w1.fi>");
diff --git a/drivers/Makefile b/drivers/Makefile
index 3a718f5..920c975 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -72,7 +72,6 @@
 obj-$(CONFIG_MMC)		+= mmc/
 obj-$(CONFIG_NEW_LEDS)		+= leds/
 obj-$(CONFIG_INFINIBAND)	+= infiniband/
-obj-$(CONFIG_IPATH_CORE)	+= infiniband/
 obj-$(CONFIG_SGI_SN)		+= sn/
 obj-y				+= firmware/
 obj-$(CONFIG_CRYPTO)		+= crypto/
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index e2ce4a9..139f41f 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -85,8 +85,8 @@
 	depends on ACPI
 	default y
 	---help---
-	  Procfs interface for ACPI is made optional for back-compatible.
-	  As the same functions are duplicated in sysfs interface
+	  The Procfs interface for ACPI is made optional for backward compatibility.
+	  As the same functions are duplicated in the sysfs interface
 	  and this proc interface will be removed some time later,
 	  it's marked as deprecated.
 	  ( /proc/acpi/debug_layer && debug_level are deprecated by
@@ -218,43 +218,6 @@
 	  NOTE: This driver is deprecated and will probably be removed soon,
 	  use asus-laptop instead.
 
-config ACPI_IBM
-	tristate "IBM ThinkPad Laptop Extras"
-	depends on X86
-	select BACKLIGHT_CLASS_DEVICE
-	---help---
-	  This is a Linux ACPI driver for the IBM ThinkPad laptops. It adds
-	  support for Fn-Fx key combinations, Bluetooth control, video
-	  output switching, ThinkLight control, UltraBay eject and more.
-	  For more information about this driver see <file:Documentation/ibm-acpi.txt>
-	  and <http://ibm-acpi.sf.net/> .
-
-	  If you have an IBM ThinkPad laptop, say Y or M here.
-
-config ACPI_IBM_DOCK
-	bool "Legacy Docking Station Support"
-	depends on ACPI_IBM
-	depends on ACPI_DOCK=n
-	default n
-	---help---
-	  Allows the ibm_acpi driver to handle docking station events.
-	  This support is obsoleted by CONFIG_HOTPLUG_PCI_ACPI.  It will
-	  allow locking and removing the laptop from the docking station,
-	  but will not properly connect PCI devices.
-
-	  If you are not sure, say N here.
-
-config ACPI_IBM_BAY
-	bool "Legacy Removable Bay Support"
-	depends on ACPI_IBM
-	default y
-	---help---
-	  Allows the ibm_acpi driver to handle removable bays.  It will allow
-	  disabling the device in the bay, and also generate notifications when
-	  the bay lever is ejected or inserted.
-
-	  If you are not sure, say Y here.
-
 config ACPI_TOSHIBA
 	tristate "Toshiba Laptop Extras"
 	depends on X86
@@ -388,11 +351,10 @@
 
 config ACPI_SBS
 	tristate "Smart Battery System (EXPERIMENTAL)"
-	depends on X86 && I2C
+	depends on X86
 	depends on EXPERIMENTAL
 	help
 	  This driver adds support for the Smart Battery System.
-	  Depends on I2C (Device Drivers ---> I2C support)
 	  A "Smart Battery" is quite old and quite rare compared
 	  to today's ACPI "Control Method" battery.
 
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 5956e9f..d4336f1 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -1,6 +1,6 @@
 #
 # Makefile for the Linux ACPI interpreter
-# 
+#
 
 export ACPI_CFLAGS
 
@@ -32,16 +32,17 @@
 processor-objs	+= processor_core.o processor_throttling.o \
 				processor_idle.o processor_thermal.o
 ifdef CONFIG_CPU_FREQ
-processor-objs	+= processor_perflib.o			
+processor-objs	+= processor_perflib.o
 endif
 
 obj-y				+= sleep/
 obj-y				+= bus.o glue.o
 obj-y				+= scan.o
+# Keep EC driver first. Initialization of others depend on it.
+obj-$(CONFIG_ACPI_EC)		+= ec.o
 obj-$(CONFIG_ACPI_AC) 		+= ac.o
 obj-$(CONFIG_ACPI_BATTERY)	+= battery.o
 obj-$(CONFIG_ACPI_BUTTON)	+= button.o
-obj-$(CONFIG_ACPI_EC)		+= ec.o
 obj-$(CONFIG_ACPI_FAN)		+= fan.o
 obj-$(CONFIG_ACPI_DOCK)		+= dock.o
 obj-$(CONFIG_ACPI_BAY)		+= bay.o
@@ -55,8 +56,7 @@
 obj-$(CONFIG_ACPI_DEBUG)	+= debug.o
 obj-$(CONFIG_ACPI_NUMA)		+= numa.o
 obj-$(CONFIG_ACPI_ASUS)		+= asus_acpi.o
-obj-$(CONFIG_ACPI_IBM)		+= ibm_acpi.o
 obj-$(CONFIG_ACPI_TOSHIBA)	+= toshiba_acpi.o
 obj-$(CONFIG_ACPI_HOTPLUG_MEMORY)	+= acpi_memhotplug.o
 obj-y				+= cm_sbs.o
-obj-$(CONFIG_ACPI_SBS)		+= i2c_ec.o sbs.o
+obj-$(CONFIG_ACPI_SBS)		+= sbs.o
diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c
index c261726..e65628a 100644
--- a/drivers/acpi/acpi_memhotplug.c
+++ b/drivers/acpi/acpi_memhotplug.c
@@ -44,11 +44,6 @@
 MODULE_DESCRIPTION("Hotplug Mem Driver");
 MODULE_LICENSE("GPL");
 
-/* ACPI _STA method values */
-#define ACPI_MEMORY_STA_PRESENT		(0x00000001UL)
-#define ACPI_MEMORY_STA_ENABLED		(0x00000002UL)
-#define ACPI_MEMORY_STA_FUNCTIONAL	(0x00000008UL)
-
 /* Memory Device States */
 #define MEMORY_INVALID_STATE	0
 #define MEMORY_POWER_ON_STATE	1
@@ -204,9 +199,9 @@
 	 * Check for device status. Device should be
 	 * present/enabled/functioning.
 	 */
-	if (!((current_status & ACPI_MEMORY_STA_PRESENT)
-	      && (current_status & ACPI_MEMORY_STA_ENABLED)
-	      && (current_status & ACPI_MEMORY_STA_FUNCTIONAL)))
+	if (!((current_status & ACPI_STA_DEVICE_PRESENT)
+	      && (current_status & ACPI_STA_DEVICE_ENABLED)
+	      && (current_status & ACPI_STA_DEVICE_FUNCTIONING)))
 		return -ENODEV;
 
 	return 0;
@@ -286,7 +281,7 @@
 		return -ENODEV;
 
 	/* Check for device status.  Device should be disabled */
-	if (current_status & ACPI_MEMORY_STA_ENABLED)
+	if (current_status & ACPI_STA_DEVICE_ENABLED)
 		return -EINVAL;
 
 	return 0;
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index dd49ea0..e5084ec 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -103,7 +103,9 @@
 	else if (device->parent)
 		device->status = device->parent->status;
 	else
-		STRUCT_TO_INT(device->status) = 0x0F;
+		STRUCT_TO_INT(device->status) =
+		    ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED |
+		    ACPI_STA_DEVICE_UI      | ACPI_STA_DEVICE_FUNCTIONING;
 
 	if (device->status.functional && !device->status.present) {
 		printk(KERN_WARNING PREFIX "Device [%s] status [%08x]: "
diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c
index 0930d94..0dd3bf7 100644
--- a/drivers/acpi/container.c
+++ b/drivers/acpi/container.c
@@ -49,8 +49,6 @@
 MODULE_DESCRIPTION("ACPI container driver");
 MODULE_LICENSE("GPL");
 
-#define ACPI_STA_PRESENT		(0x00000001)
-
 static int acpi_container_add(struct acpi_device *device);
 static int acpi_container_remove(struct acpi_device *device, int type);
 
@@ -75,13 +73,13 @@
 
 	status = acpi_get_handle(handle, "_STA", &temp);
 	if (ACPI_FAILURE(status))
-		return 1;	/* _STA not found, assmue device present */
+		return 1;	/* _STA not found, assume device present */
 
 	status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
 	if (ACPI_FAILURE(status))
 		return 0;	/* Firmware error */
 
-	return ((sta & ACPI_STA_PRESENT) == ACPI_STA_PRESENT);
+	return ((sta & ACPI_STA_DEVICE_PRESENT) == ACPI_STA_DEVICE_PRESENT);
 }
 
 /*******************************************************************/
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
index 54a697f..4546bf8 100644
--- a/drivers/acpi/dock.c
+++ b/drivers/acpi/dock.c
@@ -29,6 +29,7 @@
 #include <linux/notifier.h>
 #include <linux/platform_device.h>
 #include <linux/jiffies.h>
+#include <linux/stddef.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
 
@@ -667,6 +668,23 @@
 }
 DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock);
 
+/*
+ * show_dock_uid - read method for "uid" file in sysfs
+ */
+static ssize_t show_dock_uid(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	unsigned long lbuf;
+	acpi_status status = acpi_evaluate_integer(dock_station->handle, "_UID", NULL, &lbuf);
+	if(ACPI_FAILURE(status)) {
+	    return 0;
+	}
+	return snprintf(buf, PAGE_SIZE, "%lx\n", lbuf);
+}
+DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL);
+
+
+
 /**
  * dock_add - add a new dock station
  * @handle: the dock station handle
@@ -715,6 +733,13 @@
 		kfree(dock_station);
 		return ret;
 	}
+	ret = device_create_file(&dock_device.dev, &dev_attr_uid);
+	if (ret) {
+		printk("Error %d adding sysfs file\n", ret);
+		platform_device_unregister(&dock_device);
+		kfree(dock_station);
+		return ret;
+	}
 
 	/* Find dependent devices */
 	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index a802962..e08cf98 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -1,6 +1,8 @@
 /*
- *  acpi_ec.c - ACPI Embedded Controller Driver ($Revision: 38 $)
+ *  ec.c - ACPI Embedded Controller Driver (v2.0)
  *
+ *  Copyright (C) 2006, 2007 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com>
+ *  Copyright (C) 2006 Denis Sadykov <denis.m.sadykov@intel.com>
  *  Copyright (C) 2004 Luming Yu <luming.yu@intel.com>
  *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
  *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
@@ -91,9 +93,9 @@
 };
 
 /* If we find an EC via the ECDT, we need to keep a ptr to its context */
+/* External interfaces use first EC only, so remember */
 static struct acpi_ec {
 	acpi_handle handle;
-	unsigned long uid;
 	unsigned long gpe;
 	unsigned long command_addr;
 	unsigned long data_addr;
@@ -101,12 +103,8 @@
 	struct mutex lock;
 	atomic_t query_pending;
 	atomic_t event_count;
-	atomic_t leaving_burst;	/* 0 : No, 1 : Yes, 2: abort */
 	wait_queue_head_t wait;
-} *ec_ecdt;
-
-/* External interfaces use first EC only, so remember */
-static struct acpi_device *first_ec;
+} *boot_ec, *first_ec;
 
 /* --------------------------------------------------------------------------
                              Transaction Management
@@ -173,56 +171,6 @@
 	return -ETIME;
 }
 
-#ifdef ACPI_FUTURE_USAGE
-/*
- * Note: samsung nv5000 doesn't work with ec burst mode.
- * http://bugzilla.kernel.org/show_bug.cgi?id=4980
- */
-int acpi_ec_enter_burst_mode(struct acpi_ec *ec)
-{
-	u8 tmp = 0;
-	u8 status = 0;
-
-	status = acpi_ec_read_status(ec);
-	if (status != -EINVAL && !(status & ACPI_EC_FLAG_BURST)) {
-		status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0);
-		if (status)
-			goto end;
-		acpi_ec_write_cmd(ec, ACPI_EC_BURST_ENABLE);
-		status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1);
-		tmp = acpi_ec_read_data(ec);
-		if (tmp != 0x90) {	/* Burst ACK byte */
-			return -EINVAL;
-		}
-	}
-
-	atomic_set(&ec->leaving_burst, 0);
-	return 0;
-      end:
-	ACPI_EXCEPTION((AE_INFO, status, "EC wait, burst mode"));
-	return -1;
-}
-
-int acpi_ec_leave_burst_mode(struct acpi_ec *ec)
-{
-	u8 status = 0;
-
-	status = acpi_ec_read_status(ec);
-	if (status != -EINVAL && (status & ACPI_EC_FLAG_BURST)) {
-		status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0);
-		if (status)
-			goto end;
-		acpi_ec_write_cmd(ec, ACPI_EC_BURST_DISABLE);
-		acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0);
-	}
-	atomic_set(&ec->leaving_burst, 1);
-	return 0;
-      end:
-	ACPI_EXCEPTION((AE_INFO, status, "EC leave burst mode"));
-	return -1;
-}
-#endif				/* ACPI_FUTURE_USAGE */
-
 static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command,
 					const u8 * wdata, unsigned wdata_len,
 					u8 * rdata, unsigned rdata_len)
@@ -312,6 +260,21 @@
 	return status;
 }
 
+/*
+ * Note: samsung nv5000 doesn't work with ec burst mode.
+ * http://bugzilla.kernel.org/show_bug.cgi?id=4980
+ */
+int acpi_ec_burst_enable(struct acpi_ec *ec)
+{
+	u8 d;
+	return acpi_ec_transaction(ec, ACPI_EC_BURST_ENABLE, NULL, 0, &d, 1);
+}
+
+int acpi_ec_burst_disable(struct acpi_ec *ec)
+{
+	return acpi_ec_transaction(ec, ACPI_EC_BURST_DISABLE, NULL, 0, NULL, 0);
+}
+
 static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data)
 {
 	int result;
@@ -333,18 +296,33 @@
 /*
  * Externally callable EC access functions. For now, assume 1 EC only
  */
+int ec_burst_enable(void)
+{
+	if (!first_ec)
+		return -ENODEV;
+	return acpi_ec_burst_enable(first_ec);
+}
+
+EXPORT_SYMBOL(ec_burst_enable);
+
+int ec_burst_disable(void)
+{
+	if (!first_ec)
+		return -ENODEV;
+	return acpi_ec_burst_disable(first_ec);
+}
+
+EXPORT_SYMBOL(ec_burst_disable);
+
 int ec_read(u8 addr, u8 * val)
 {
-	struct acpi_ec *ec;
 	int err;
 	u8 temp_data;
 
 	if (!first_ec)
 		return -ENODEV;
 
-	ec = acpi_driver_data(first_ec);
-
-	err = acpi_ec_read(ec, addr, &temp_data);
+	err = acpi_ec_read(first_ec, addr, &temp_data);
 
 	if (!err) {
 		*val = temp_data;
@@ -357,15 +335,12 @@
 
 int ec_write(u8 addr, u8 val)
 {
-	struct acpi_ec *ec;
 	int err;
 
 	if (!first_ec)
 		return -ENODEV;
 
-	ec = acpi_driver_data(first_ec);
-
-	err = acpi_ec_write(ec, addr, val);
+	err = acpi_ec_write(first_ec, addr, val);
 
 	return err;
 }
@@ -376,14 +351,10 @@
 		   const u8 * wdata, unsigned wdata_len,
 		   u8 * rdata, unsigned rdata_len)
 {
-	struct acpi_ec *ec;
-
 	if (!first_ec)
 		return -ENODEV;
 
-	ec = acpi_driver_data(first_ec);
-
-	return acpi_ec_transaction(ec, command, wdata,
+	return acpi_ec_transaction(first_ec, command, wdata,
 				   wdata_len, rdata, rdata_len);
 }
 
@@ -420,7 +391,7 @@
 
 static void acpi_ec_gpe_query(void *ec_cxt)
 {
-	struct acpi_ec *ec = (struct acpi_ec *)ec_cxt;
+	struct acpi_ec *ec = ec_cxt;
 	u8 value = 0;
 	char object_name[8];
 
@@ -438,8 +409,9 @@
 {
 	acpi_status status = AE_OK;
 	u8 value;
-	struct acpi_ec *ec = (struct acpi_ec *)data;
+	struct acpi_ec *ec = data;
 	atomic_inc(&ec->event_count);
+
 	if (acpi_ec_mode == EC_INTR) {
 		wake_up(&ec->wait);
 	}
@@ -482,7 +454,7 @@
 		      void *handler_context, void *region_context)
 {
 	int result = 0;
-	struct acpi_ec *ec = NULL;
+	struct acpi_ec *ec = handler_context;
 	u64 temp = *value;
 	acpi_integer f_v = 0;
 	int i = 0;
@@ -494,8 +466,6 @@
 		return AE_BAD_PARAMETER;
 	}
 
-	ec = (struct acpi_ec *)handler_context;
-
       next_byte:
 	switch (function) {
 	case ACPI_READ:
@@ -551,18 +521,16 @@
 
 static int acpi_ec_read_info(struct seq_file *seq, void *offset)
 {
-	struct acpi_ec *ec = (struct acpi_ec *)seq->private;
+	struct acpi_ec *ec = seq->private;
 
 	if (!ec)
 		goto end;
 
-	seq_printf(seq, "gpe:                 0x%02x\n", (u32) ec->gpe);
-	seq_printf(seq, "ports:                   0x%02x, 0x%02x\n",
-		   (u32) ec->command_addr, (u32) ec->data_addr);
-	seq_printf(seq, "use global lock:         %s\n",
+	seq_printf(seq, "gpe:\t\t\t0x%02x\n", (u32) ec->gpe);
+	seq_printf(seq, "ports:\t\t\t0x%02x, 0x%02x\n",
+		   (unsigned)ec->command_addr, (unsigned)ec->data_addr);
+	seq_printf(seq, "use global lock:\t%s\n",
 		   ec->global_lock ? "yes" : "no");
-	acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR);
-
       end:
 	return 0;
 }
@@ -619,154 +587,122 @@
 /* --------------------------------------------------------------------------
                                Driver Interface
    -------------------------------------------------------------------------- */
+static acpi_status
+ec_parse_io_ports(struct acpi_resource *resource, void *context);
+
+static acpi_status
+ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval);
+
+static struct acpi_ec *make_acpi_ec(void)
+{
+	struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL);
+	if (!ec)
+		return NULL;
+
+	atomic_set(&ec->query_pending, 1);
+	atomic_set(&ec->event_count, 1);
+	mutex_init(&ec->lock);
+	init_waitqueue_head(&ec->wait);
+
+	return ec;
+}
 
 static int acpi_ec_add(struct acpi_device *device)
 {
-	int result = 0;
 	acpi_status status = AE_OK;
 	struct acpi_ec *ec = NULL;
 
 	if (!device)
 		return -EINVAL;
 
-	ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL);
+	strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME);
+	strcpy(acpi_device_class(device), ACPI_EC_CLASS);
+
+	ec = make_acpi_ec();
 	if (!ec)
 		return -ENOMEM;
 
-	ec->handle = device->handle;
-	ec->uid = -1;
-	mutex_init(&ec->lock);
-	atomic_set(&ec->query_pending, 0);
-	atomic_set(&ec->event_count, 1);
-	if (acpi_ec_mode == EC_INTR) {
-		atomic_set(&ec->leaving_burst, 1);
-		init_waitqueue_head(&ec->wait);
+	status = ec_parse_device(device->handle, 0, ec, NULL);
+	if (status != AE_CTRL_TERMINATE) {
+		kfree(ec);
+		return -EINVAL;
 	}
-	strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME);
-	strcpy(acpi_device_class(device), ACPI_EC_CLASS);
+
+	/* Check if we found the boot EC */
+	if (boot_ec) {
+		if (boot_ec->gpe == ec->gpe) {
+			/* We might have incorrect info for GL at boot time */
+			mutex_lock(&boot_ec->lock);
+			boot_ec->global_lock = ec->global_lock;
+			mutex_unlock(&boot_ec->lock);
+			kfree(ec);
+			ec = boot_ec;
+		}
+	} else
+		first_ec = ec;
+	ec->handle = device->handle;
 	acpi_driver_data(device) = ec;
 
-	/* Use the global lock for all EC transactions? */
-	acpi_evaluate_integer(ec->handle, "_GLK", NULL, &ec->global_lock);
-
-	/* XXX we don't test uids, because on some boxes ecdt uid = 0, see:
-	   http://bugzilla.kernel.org/show_bug.cgi?id=6111 */
-	if (ec_ecdt) {
-		acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
-						  ACPI_ADR_SPACE_EC,
-						  &acpi_ec_space_handler);
-
-		acpi_remove_gpe_handler(NULL, ec_ecdt->gpe,
-					&acpi_ec_gpe_handler);
-
-		kfree(ec_ecdt);
-	}
-
-	/* Get GPE bit assignment (EC events). */
-	/* TODO: Add support for _GPE returning a package */
-	status = acpi_evaluate_integer(ec->handle, "_GPE", NULL, &ec->gpe);
-	if (ACPI_FAILURE(status)) {
-		ACPI_EXCEPTION((AE_INFO, status,
-				"Obtaining GPE bit assignment"));
-		result = -ENODEV;
-		goto end;
-	}
-
-	result = acpi_ec_add_fs(device);
-	if (result)
-		goto end;
+	acpi_ec_add_fs(device);
 
 	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s [%s] (gpe %d) interrupt mode.",
 			  acpi_device_name(device), acpi_device_bid(device),
 			  (u32) ec->gpe));
 
-	if (!first_ec)
-		first_ec = device;
-
-      end:
-	if (result)
-		kfree(ec);
-
-	return result;
+	return 0;
 }
 
 static int acpi_ec_remove(struct acpi_device *device, int type)
 {
-	struct acpi_ec *ec = NULL;
+	struct acpi_ec *ec;
 
 	if (!device)
 		return -EINVAL;
 
 	ec = acpi_driver_data(device);
-
 	acpi_ec_remove_fs(device);
+	acpi_driver_data(device) = NULL;
+	if (ec == first_ec)
+		first_ec = NULL;
 
-	kfree(ec);
-
+	/* Don't touch boot EC */
+	if (boot_ec != ec)
+		kfree(ec);
 	return 0;
 }
 
 static acpi_status
-acpi_ec_io_ports(struct acpi_resource *resource, void *context)
+ec_parse_io_ports(struct acpi_resource *resource, void *context)
 {
-	struct acpi_ec *ec = (struct acpi_ec *)context;
+	struct acpi_ec *ec = context;
 
-	if (resource->type != ACPI_RESOURCE_TYPE_IO) {
+	if (resource->type != ACPI_RESOURCE_TYPE_IO)
 		return AE_OK;
-	}
 
 	/*
 	 * The first address region returned is the data port, and
 	 * the second address region returned is the status/command
 	 * port.
 	 */
-	if (ec->data_addr == 0) {
+	if (ec->data_addr == 0)
 		ec->data_addr = resource->data.io.minimum;
-	} else if (ec->command_addr == 0) {
+	else if (ec->command_addr == 0)
 		ec->command_addr = resource->data.io.minimum;
-	} else {
+	else
 		return AE_CTRL_TERMINATE;
-	}
 
 	return AE_OK;
 }
 
-static int acpi_ec_start(struct acpi_device *device)
+static int ec_install_handlers(struct acpi_ec *ec)
 {
-	acpi_status status = AE_OK;
-	struct acpi_ec *ec = NULL;
-
-	if (!device)
-		return -EINVAL;
-
-	ec = acpi_driver_data(device);
-
-	if (!ec)
-		return -EINVAL;
-
-	/*
-	 * Get I/O port addresses. Convert to GAS format.
-	 */
-	status = acpi_walk_resources(ec->handle, METHOD_NAME__CRS,
-				     acpi_ec_io_ports, ec);
-	if (ACPI_FAILURE(status) || ec->command_addr == 0) {
-		ACPI_EXCEPTION((AE_INFO, status,
-				"Error getting I/O port addresses"));
-		return -ENODEV;
-	}
-
-	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02lx, ports=0x%2lx,0x%2lx",
-			  ec->gpe, ec->command_addr, ec->data_addr));
-
-	/*
-	 * Install GPE handler
-	 */
+	acpi_status status;
 	status = acpi_install_gpe_handler(NULL, ec->gpe,
 					  ACPI_GPE_EDGE_TRIGGERED,
 					  &acpi_ec_gpe_handler, ec);
-	if (ACPI_FAILURE(status)) {
+	if (ACPI_FAILURE(status))
 		return -ENODEV;
-	}
+
 	acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME);
 	acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR);
 
@@ -779,19 +715,50 @@
 		return -ENODEV;
 	}
 
-	return AE_OK;
+	/* EC is fully operational, allow queries */
+	atomic_set(&ec->query_pending, 0);
+
+	return 0;
 }
 
-static int acpi_ec_stop(struct acpi_device *device, int type)
+static int acpi_ec_start(struct acpi_device *device)
 {
-	acpi_status status = AE_OK;
-	struct acpi_ec *ec = NULL;
+	struct acpi_ec *ec;
 
 	if (!device)
 		return -EINVAL;
 
 	ec = acpi_driver_data(device);
 
+	if (!ec)
+		return -EINVAL;
+
+	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02lx, ports=0x%2lx,0x%2lx",
+			  ec->gpe, ec->command_addr, ec->data_addr));
+
+	/* Boot EC is already working */
+	if (ec == boot_ec)
+		return 0;
+
+	return ec_install_handlers(ec);
+}
+
+static int acpi_ec_stop(struct acpi_device *device, int type)
+{
+	acpi_status status;
+	struct acpi_ec *ec;
+
+	if (!device)
+		return -EINVAL;
+
+	ec = acpi_driver_data(device);
+	if (!ec)
+		return -EINVAL;
+
+	/* Don't touch boot EC */
+	if (ec == boot_ec)
+		return 0;
+
 	status = acpi_remove_address_space_handler(ec->handle,
 						   ACPI_ADR_SPACE_EC,
 						   &acpi_ec_space_handler);
@@ -805,164 +772,67 @@
 	return 0;
 }
 
-static acpi_status __init
-acpi_fake_ecdt_callback(acpi_handle handle,
-			u32 Level, void *context, void **retval)
+static acpi_status
+ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval)
 {
 	acpi_status status;
 
-	mutex_init(&ec_ecdt->lock);
-	atomic_set(&ec_ecdt->event_count, 1);
-	if (acpi_ec_mode == EC_INTR) {
-		init_waitqueue_head(&ec_ecdt->wait);
-	}
+	struct acpi_ec *ec = context;
 	status = acpi_walk_resources(handle, METHOD_NAME__CRS,
-				     acpi_ec_io_ports, ec_ecdt);
+				     ec_parse_io_ports, ec);
 	if (ACPI_FAILURE(status))
 		return status;
 
-	ec_ecdt->uid = -1;
-	acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->uid);
-
-	status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->gpe);
+	/* Get GPE bit assignment (EC events). */
+	/* TODO: Add support for _GPE returning a package */
+	status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe);
 	if (ACPI_FAILURE(status))
 		return status;
-	ec_ecdt->global_lock = TRUE;
-	ec_ecdt->handle = handle;
+
+	/* Use the global lock for all EC transactions? */
+	acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock);
+
+	ec->handle = handle;
 
 	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02lx, ports=0x%2lx, 0x%2lx",
-			  ec_ecdt->gpe, ec_ecdt->command_addr,
-			  ec_ecdt->data_addr));
+			  ec->gpe, ec->command_addr, ec->data_addr));
 
 	return AE_CTRL_TERMINATE;
 }
 
-/*
- * Some BIOS (such as some from Gateway laptops) access EC region very early
- * such as in BAT0._INI or EC._INI before an EC device is found and
- * do not provide an ECDT. According to ACPI spec, ECDT isn't mandatorily
- * required, but if EC regison is accessed early, it is required.
- * The routine tries to workaround the BIOS bug by pre-scan EC device
- * It assumes that _CRS, _HID, _GPE, _UID methods of EC don't touch any
- * op region (since _REG isn't invoked yet). The assumption is true for
- * all systems found.
- */
-static int __init acpi_ec_fake_ecdt(void)
+int __init acpi_ec_ecdt_probe(void)
 {
-	acpi_status status;
-	int ret = 0;
-
-	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Try to make an fake ECDT"));
-
-	ec_ecdt = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL);
-	if (!ec_ecdt) {
-		ret = -ENOMEM;
-		goto error;
-	}
-
-	status = acpi_get_devices(ACPI_EC_HID,
-				  acpi_fake_ecdt_callback, NULL, NULL);
-	if (ACPI_FAILURE(status)) {
-		kfree(ec_ecdt);
-		ec_ecdt = NULL;
-		ret = -ENODEV;
-		ACPI_EXCEPTION((AE_INFO, status, "Can't make an fake ECDT"));
-		goto error;
-	}
-	return 0;
-      error:
-	return ret;
-}
-
-static int __init acpi_ec_get_real_ecdt(void)
-{
+	int ret;
 	acpi_status status;
 	struct acpi_table_ecdt *ecdt_ptr;
 
+	boot_ec = make_acpi_ec();
+	if (!boot_ec)
+		return -ENOMEM;
+	/*
+	 * Generate a boot ec context
+	 */
+
 	status = acpi_get_table(ACPI_SIG_ECDT, 1,
 				(struct acpi_table_header **)&ecdt_ptr);
 	if (ACPI_FAILURE(status))
-		return -ENODEV;
+		goto error;
 
 	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found ECDT"));
 
-	/*
-	 * Generate a temporary ec context to use until the namespace is scanned
-	 */
-	ec_ecdt = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL);
-	if (!ec_ecdt)
-		return -ENOMEM;
+	boot_ec->command_addr = ecdt_ptr->control.address;
+	boot_ec->data_addr = ecdt_ptr->data.address;
+	boot_ec->gpe = ecdt_ptr->gpe;
+	boot_ec->handle = ACPI_ROOT_OBJECT;
 
-	mutex_init(&ec_ecdt->lock);
-	atomic_set(&ec_ecdt->event_count, 1);
-	if (acpi_ec_mode == EC_INTR) {
-		init_waitqueue_head(&ec_ecdt->wait);
-	}
-	ec_ecdt->command_addr = ecdt_ptr->control.address;
-	ec_ecdt->data_addr = ecdt_ptr->data.address;
-	ec_ecdt->gpe = ecdt_ptr->gpe;
-	/* use the GL just to be safe */
-	ec_ecdt->global_lock = TRUE;
-	ec_ecdt->uid = ecdt_ptr->uid;
-
-	status = acpi_get_handle(NULL, ecdt_ptr->id, &ec_ecdt->handle);
-	if (ACPI_FAILURE(status)) {
-		goto error;
-	}
-
-	return 0;
-      error:
-	ACPI_EXCEPTION((AE_INFO, status, "Could not use ECDT"));
-	kfree(ec_ecdt);
-	ec_ecdt = NULL;
-
-	return -ENODEV;
-}
-
-static int __initdata acpi_fake_ecdt_enabled;
-int __init acpi_ec_ecdt_probe(void)
-{
-	acpi_status status;
-	int ret;
-
-	ret = acpi_ec_get_real_ecdt();
-	/* Try to make a fake ECDT */
-	if (ret && acpi_fake_ecdt_enabled) {
-		ret = acpi_ec_fake_ecdt();
-	}
-
-	if (ret)
+	ret = ec_install_handlers(boot_ec);
+	if (!ret) {
+		first_ec = boot_ec;
 		return 0;
-
-	/*
-	 * Install GPE handler
-	 */
-	status = acpi_install_gpe_handler(NULL, ec_ecdt->gpe,
-					  ACPI_GPE_EDGE_TRIGGERED,
-					  &acpi_ec_gpe_handler, ec_ecdt);
-	if (ACPI_FAILURE(status)) {
-		goto error;
 	}
-	acpi_set_gpe_type(NULL, ec_ecdt->gpe, ACPI_GPE_TYPE_RUNTIME);
-	acpi_enable_gpe(NULL, ec_ecdt->gpe, ACPI_NOT_ISR);
-
-	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
-						    ACPI_ADR_SPACE_EC,
-						    &acpi_ec_space_handler,
-						    &acpi_ec_space_setup,
-						    ec_ecdt);
-	if (ACPI_FAILURE(status)) {
-		acpi_remove_gpe_handler(NULL, ec_ecdt->gpe,
-					&acpi_ec_gpe_handler);
-		goto error;
-	}
-
-	return 0;
-
       error:
-	ACPI_EXCEPTION((AE_INFO, status, "Could not use ECDT"));
-	kfree(ec_ecdt);
-	ec_ecdt = NULL;
+	kfree(boot_ec);
+	boot_ec = NULL;
 
 	return -ENODEV;
 }
@@ -1003,13 +873,6 @@
 }
 #endif				/* 0 */
 
-static int __init acpi_fake_ecdt_setup(char *str)
-{
-	acpi_fake_ecdt_enabled = 1;
-	return 1;
-}
-
-__setup("acpi_fake_ecdt", acpi_fake_ecdt_setup);
 static int __init acpi_ec_set_intr_mode(char *str)
 {
 	int intr;
@@ -1017,12 +880,8 @@
 	if (!get_option(&str, &intr))
 		return 0;
 
-	if (intr) {
-		acpi_ec_mode = EC_INTR;
-	} else {
-		acpi_ec_mode = EC_POLL;
-	}
-	acpi_ec_driver.ops.add = acpi_ec_add;
+	acpi_ec_mode = (intr) ? EC_INTR : EC_POLL;
+
 	printk(KERN_NOTICE PREFIX "%s mode.\n", intr ? "interrupt" : "polling");
 
 	return 1;
diff --git a/drivers/acpi/i2c_ec.c b/drivers/acpi/i2c_ec.c
deleted file mode 100644
index acab4a4..0000000
--- a/drivers/acpi/i2c_ec.c
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- * SMBus driver for ACPI Embedded Controller ($Revision: 1.3 $)
- *
- * Copyright (c) 2002, 2005 Ducrot Bruno
- * Copyright (c) 2005 Rich Townsend (tiny hacks & tweaks)
- *
- * 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/version.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/stddef.h>
-#include <linux/init.h>
-#include <linux/i2c.h>
-#include <linux/acpi.h>
-#include <linux/delay.h>
-
-#include "i2c_ec.h"
-
-#define	xudelay(t)	udelay(t)
-#define	xmsleep(t)	msleep(t)
-
-#define ACPI_EC_HC_COMPONENT	0x00080000
-#define ACPI_EC_HC_CLASS	"ec_hc_smbus"
-#define ACPI_EC_HC_HID		"ACPI0001"
-#define ACPI_EC_HC_DEVICE_NAME	"EC HC smbus"
-
-#define _COMPONENT		ACPI_EC_HC_COMPONENT
-
-ACPI_MODULE_NAME("i2c_ec");
-
-static int acpi_ec_hc_add(struct acpi_device *device);
-static int acpi_ec_hc_remove(struct acpi_device *device, int type);
-
-static struct acpi_driver acpi_ec_hc_driver = {
-	.name = "i2c_ec",
-	.class = ACPI_EC_HC_CLASS,
-	.ids = ACPI_EC_HC_HID,
-	.ops = {
-		.add = acpi_ec_hc_add,
-		.remove = acpi_ec_hc_remove,
-		},
-};
-
-/* Various bit mask for EC_SC (R) */
-#define OBF		0x01
-#define IBF		0x02
-#define CMD		0x08
-#define BURST		0x10
-#define SCI_EVT		0x20
-#define SMI_EVT		0x40
-
-/* Commands for EC_SC (W) */
-#define RD_EC		0x80
-#define WR_EC		0x81
-#define BE_EC		0x82
-#define BD_EC		0x83
-#define QR_EC		0x84
-
-/*
- * ACPI 2.0 chapter 13 SMBus 2.0 EC register model
- */
-
-#define ACPI_EC_SMB_PRTCL	0x00	/* protocol, PEC */
-#define ACPI_EC_SMB_STS		0x01	/* status */
-#define ACPI_EC_SMB_ADDR	0x02	/* address */
-#define ACPI_EC_SMB_CMD		0x03	/* command */
-#define ACPI_EC_SMB_DATA	0x04	/* 32 data registers */
-#define ACPI_EC_SMB_BCNT	0x24	/* number of data bytes */
-#define ACPI_EC_SMB_ALRM_A	0x25	/* alarm address */
-#define ACPI_EC_SMB_ALRM_D	0x26	/* 2 bytes alarm data */
-
-#define ACPI_EC_SMB_STS_DONE	0x80
-#define ACPI_EC_SMB_STS_ALRM	0x40
-#define ACPI_EC_SMB_STS_RES	0x20
-#define ACPI_EC_SMB_STS_STATUS	0x1f
-
-#define ACPI_EC_SMB_STATUS_OK		0x00
-#define ACPI_EC_SMB_STATUS_FAIL		0x07
-#define ACPI_EC_SMB_STATUS_DNAK		0x10
-#define ACPI_EC_SMB_STATUS_DERR		0x11
-#define ACPI_EC_SMB_STATUS_CMD_DENY	0x12
-#define ACPI_EC_SMB_STATUS_UNKNOWN	0x13
-#define ACPI_EC_SMB_STATUS_ACC_DENY	0x17
-#define ACPI_EC_SMB_STATUS_TIMEOUT	0x18
-#define ACPI_EC_SMB_STATUS_NOTSUP	0x19
-#define ACPI_EC_SMB_STATUS_BUSY		0x1A
-#define ACPI_EC_SMB_STATUS_PEC		0x1F
-
-#define ACPI_EC_SMB_PRTCL_WRITE			0x00
-#define ACPI_EC_SMB_PRTCL_READ			0x01
-#define ACPI_EC_SMB_PRTCL_QUICK			0x02
-#define ACPI_EC_SMB_PRTCL_BYTE			0x04
-#define ACPI_EC_SMB_PRTCL_BYTE_DATA		0x06
-#define ACPI_EC_SMB_PRTCL_WORD_DATA		0x08
-#define ACPI_EC_SMB_PRTCL_BLOCK_DATA		0x0a
-#define ACPI_EC_SMB_PRTCL_PROC_CALL		0x0c
-#define ACPI_EC_SMB_PRTCL_BLOCK_PROC_CALL	0x0d
-#define ACPI_EC_SMB_PRTCL_I2C_BLOCK_DATA	0x4a
-#define ACPI_EC_SMB_PRTCL_PEC			0x80
-
-/* Length of pre/post transaction sleep (msec) */
-#define ACPI_EC_SMB_TRANSACTION_SLEEP		1
-#define ACPI_EC_SMB_ACCESS_SLEEP1		1
-#define ACPI_EC_SMB_ACCESS_SLEEP2		10
-
-static int acpi_ec_smb_read(struct acpi_ec_smbus *smbus, u8 address, u8 * data)
-{
-	u8 val;
-	int err;
-
-	err = ec_read(smbus->base + address, &val);
-	if (!err) {
-		*data = val;
-	}
-	xmsleep(ACPI_EC_SMB_TRANSACTION_SLEEP);
-	return (err);
-}
-
-static int acpi_ec_smb_write(struct acpi_ec_smbus *smbus, u8 address, u8 data)
-{
-	int err;
-
-	err = ec_write(smbus->base + address, data);
-	return (err);
-}
-
-static int
-acpi_ec_smb_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
-		   char read_write, u8 command, int size,
-		   union i2c_smbus_data *data)
-{
-	struct acpi_ec_smbus *smbus = adap->algo_data;
-	unsigned char protocol, len = 0, pec, temp[2] = { 0, 0 };
-	int i;
-
-	if (read_write == I2C_SMBUS_READ) {
-		protocol = ACPI_EC_SMB_PRTCL_READ;
-	} else {
-		protocol = ACPI_EC_SMB_PRTCL_WRITE;
-	}
-	pec = (flags & I2C_CLIENT_PEC) ? ACPI_EC_SMB_PRTCL_PEC : 0;
-
-	switch (size) {
-
-	case I2C_SMBUS_QUICK:
-		protocol |= ACPI_EC_SMB_PRTCL_QUICK;
-		read_write = I2C_SMBUS_WRITE;
-		break;
-
-	case I2C_SMBUS_BYTE:
-		if (read_write == I2C_SMBUS_WRITE) {
-			acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->byte);
-		}
-		protocol |= ACPI_EC_SMB_PRTCL_BYTE;
-		break;
-
-	case I2C_SMBUS_BYTE_DATA:
-		acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
-		if (read_write == I2C_SMBUS_WRITE) {
-			acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->byte);
-		}
-		protocol |= ACPI_EC_SMB_PRTCL_BYTE_DATA;
-		break;
-
-	case I2C_SMBUS_WORD_DATA:
-		acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
-		if (read_write == I2C_SMBUS_WRITE) {
-			acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->word);
-			acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + 1,
-					  data->word >> 8);
-		}
-		protocol |= ACPI_EC_SMB_PRTCL_WORD_DATA | pec;
-		break;
-
-	case I2C_SMBUS_BLOCK_DATA:
-		acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
-		if (read_write == I2C_SMBUS_WRITE) {
-			len = min_t(u8, data->block[0], 32);
-			acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len);
-			for (i = 0; i < len; i++)
-				acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i,
-						  data->block[i + 1]);
-		}
-		protocol |= ACPI_EC_SMB_PRTCL_BLOCK_DATA | pec;
-		break;
-
-	case I2C_SMBUS_I2C_BLOCK_DATA:
-		len = min_t(u8, data->block[0], 32);
-		acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
-		acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len);
-		if (read_write == I2C_SMBUS_WRITE) {
-			for (i = 0; i < len; i++) {
-				acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i,
-						  data->block[i + 1]);
-			}
-		}
-		protocol |= ACPI_EC_SMB_PRTCL_I2C_BLOCK_DATA;
-		break;
-
-	case I2C_SMBUS_PROC_CALL:
-		acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
-		acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->word);
-		acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + 1, data->word >> 8);
-		protocol = ACPI_EC_SMB_PRTCL_PROC_CALL | pec;
-		read_write = I2C_SMBUS_READ;
-		break;
-
-	case I2C_SMBUS_BLOCK_PROC_CALL:
-		protocol |= pec;
-		len = min_t(u8, data->block[0], 31);
-		acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
-		acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len);
-		for (i = 0; i < len; i++)
-			acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i,
-					  data->block[i + 1]);
-		protocol = ACPI_EC_SMB_PRTCL_BLOCK_PROC_CALL | pec;
-		read_write = I2C_SMBUS_READ;
-		break;
-
-	default:
-		ACPI_DEBUG_PRINT((ACPI_DB_WARN, "EC SMBus adapter: "
-				  "Unsupported transaction %d\n", size));
-		return (-1);
-	}
-
-	acpi_ec_smb_write(smbus, ACPI_EC_SMB_ADDR, addr << 1);
-	acpi_ec_smb_write(smbus, ACPI_EC_SMB_PRTCL, protocol);
-
-	acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0);
-
-	if (~temp[0] & ACPI_EC_SMB_STS_DONE) {
-		xudelay(500);
-		acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0);
-	}
-	if (~temp[0] & ACPI_EC_SMB_STS_DONE) {
-		xmsleep(ACPI_EC_SMB_ACCESS_SLEEP2);
-		acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0);
-	}
-	if ((~temp[0] & ACPI_EC_SMB_STS_DONE)
-	    || (temp[0] & ACPI_EC_SMB_STS_STATUS)) {
-		return (-1);
-	}
-
-	if (read_write == I2C_SMBUS_WRITE) {
-		return (0);
-	}
-
-	switch (size) {
-
-	case I2C_SMBUS_BYTE:
-	case I2C_SMBUS_BYTE_DATA:
-		acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA, &data->byte);
-		break;
-
-	case I2C_SMBUS_WORD_DATA:
-	case I2C_SMBUS_PROC_CALL:
-		acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA, temp + 0);
-		acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA + 1, temp + 1);
-		data->word = (temp[1] << 8) | temp[0];
-		break;
-
-	case I2C_SMBUS_BLOCK_DATA:
-	case I2C_SMBUS_BLOCK_PROC_CALL:
-		len = 0;
-		acpi_ec_smb_read(smbus, ACPI_EC_SMB_BCNT, &len);
-		len = min_t(u8, len, 32);
-	case I2C_SMBUS_I2C_BLOCK_DATA:
-		for (i = 0; i < len; i++)
-			acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA + i,
-					 data->block + i + 1);
-		data->block[0] = len;
-		break;
-	}
-
-	return (0);
-}
-
-static u32 acpi_ec_smb_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 |
-		I2C_FUNC_SMBUS_PROC_CALL |
-		I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
-		I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_HWPEC_CALC);
-}
-
-static const struct i2c_algorithm acpi_ec_smbus_algorithm = {
-	.smbus_xfer = acpi_ec_smb_access,
-	.functionality = acpi_ec_smb_func,
-};
-
-static int acpi_ec_hc_add(struct acpi_device *device)
-{
-	int status;
-	unsigned long val;
-	struct acpi_ec_hc *ec_hc;
-	struct acpi_ec_smbus *smbus;
-
-	if (!device) {
-		return -EINVAL;
-	}
-
-	ec_hc = kzalloc(sizeof(struct acpi_ec_hc), GFP_KERNEL);
-	if (!ec_hc) {
-		return -ENOMEM;
-	}
-
-	smbus = kzalloc(sizeof(struct acpi_ec_smbus), GFP_KERNEL);
-	if (!smbus) {
-		kfree(ec_hc);
-		return -ENOMEM;
-	}
-
-	ec_hc->handle = device->handle;
-	strcpy(acpi_device_name(device), ACPI_EC_HC_DEVICE_NAME);
-	strcpy(acpi_device_class(device), ACPI_EC_HC_CLASS);
-	acpi_driver_data(device) = ec_hc;
-
-	status = acpi_evaluate_integer(ec_hc->handle, "_EC", NULL, &val);
-	if (ACPI_FAILURE(status)) {
-		ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Error obtaining _EC\n"));
-		kfree(ec_hc);
-		kfree(smbus);
-		return -EIO;
-	}
-
-	smbus->ec = acpi_driver_data(device->parent);
-	smbus->base = (val & 0xff00ull) >> 8;
-	smbus->alert = val & 0xffull;
-
-	smbus->adapter.owner = THIS_MODULE;
-	smbus->adapter.algo = &acpi_ec_smbus_algorithm;
-	smbus->adapter.algo_data = smbus;
-	smbus->adapter.dev.parent = &device->dev;
-
-	if (i2c_add_adapter(&smbus->adapter)) {
-		ACPI_DEBUG_PRINT((ACPI_DB_WARN,
-				  "EC SMBus adapter: Failed to register adapter\n"));
-		kfree(smbus);
-		kfree(ec_hc);
-		return -EIO;
-	}
-
-	ec_hc->smbus = smbus;
-
-	printk(KERN_INFO PREFIX "%s [%s]\n",
-	       acpi_device_name(device), acpi_device_bid(device));
-
-	return AE_OK;
-}
-
-static int acpi_ec_hc_remove(struct acpi_device *device, int type)
-{
-	struct acpi_ec_hc *ec_hc;
-
-	if (!device) {
-		return -EINVAL;
-	}
-	ec_hc = acpi_driver_data(device);
-
-	i2c_del_adapter(&ec_hc->smbus->adapter);
-	kfree(ec_hc->smbus);
-	kfree(ec_hc);
-
-	return AE_OK;
-}
-
-static int __init acpi_ec_hc_init(void)
-{
-	int result;
-
-	result = acpi_bus_register_driver(&acpi_ec_hc_driver);
-	if (result < 0) {
-		return -ENODEV;
-	}
-	return 0;
-}
-
-static void __exit acpi_ec_hc_exit(void)
-{
-	acpi_bus_unregister_driver(&acpi_ec_hc_driver);
-}
-
-struct acpi_ec_hc *acpi_get_ec_hc(struct acpi_device *device)
-{
-	return acpi_driver_data(device->parent);
-}
-
-EXPORT_SYMBOL(acpi_get_ec_hc);
-
-module_init(acpi_ec_hc_init);
-module_exit(acpi_ec_hc_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Ducrot Bruno");
-MODULE_DESCRIPTION("ACPI EC SMBus driver");
diff --git a/drivers/acpi/i2c_ec.h b/drivers/acpi/i2c_ec.h
deleted file mode 100644
index 7c53fb7..0000000
--- a/drivers/acpi/i2c_ec.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * SMBus driver for ACPI Embedded Controller ($Revision: 1.2 $)
- *
- * Copyright (c) 2002, 2005 Ducrot Bruno
- *
- * 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.
- */
-
-struct acpi_ec_smbus {
-	struct i2c_adapter adapter;
-	union acpi_ec *ec;
-	int base;
-	int alert;
-};
-
-struct acpi_ec_hc {
-	acpi_handle handle;
-	struct acpi_ec_smbus *smbus;
-};
-
-struct acpi_ec_hc *acpi_get_ec_hc(struct acpi_device *device);
diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c
deleted file mode 100644
index dc10966..0000000
--- a/drivers/acpi/ibm_acpi.c
+++ /dev/null
@@ -1,2798 +0,0 @@
-/*
- *  ibm_acpi.c - IBM ThinkPad ACPI Extras
- *
- *
- *  Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>
- *  Copyright (C) 2006 Henrique de Moraes Holschuh <hmh@hmh.eng.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; 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 IBM_VERSION "0.13"
-
-/*
- *  Changelog:
- *
- *  2006-11-22	0.13	new maintainer
- *  			changelog now lives in git commit history, and will
- *  			not be updated further in-file.
- *  
- *  2005-08-17  0.12	fix compilation on 2.6.13-rc kernels
- *  2005-03-17	0.11	support for 600e, 770x
- *			    thanks to Jamie Lentin <lentinj@dial.pipex.com>
- *			support for 770e, G41
- *			G40 and G41 don't have a thinklight
- *			temperatures no longer experimental
- *			experimental brightness control
- *			experimental volume control
- *			experimental fan enable/disable
- *  2005-01-16	0.10	fix module loading on R30, R31 
- *  2005-01-16	0.9	support for 570, R30, R31
- *			ultrabay support on A22p, A3x
- *			limit arg for cmos, led, beep, drop experimental status
- *			more capable led control on A21e, A22p, T20-22, X20
- *			experimental temperatures and fan speed
- *			experimental embedded controller register dump
- *			mark more functions as __init, drop incorrect __exit
- *			use MODULE_VERSION
- *			    thanks to Henrik Brix Andersen <brix@gentoo.org>
- *			fix parameter passing on module loading
- *			    thanks to Rusty Russell <rusty@rustcorp.com.au>
- *			    thanks to Jim Radford <radford@blackbean.org>
- *  2004-11-08	0.8	fix init error case, don't return from a macro
- *			    thanks to Chris Wright <chrisw@osdl.org>
- *  2004-10-23	0.7	fix module loading on A21e, A22p, T20, T21, X20
- *			fix led control on A21e
- *  2004-10-19	0.6	use acpi_bus_register_driver() to claim HKEY device
- *  2004-10-18	0.5	thinklight support on A21e, G40, R32, T20, T21, X20
- *			proc file format changed
- *			video_switch command
- *			experimental cmos control
- *			experimental led control
- *			experimental acpi sounds
- *  2004-09-16	0.4	support for module parameters
- *			hotkey mask can be prefixed by 0x
- *			video output switching
- *			video expansion control
- *			ultrabay eject support
- *			removed lcd brightness/on/off control, didn't work
- *  2004-08-17	0.3	support for R40
- *			lcd off, brightness control
- *			thinklight on/off
- *  2004-08-14	0.2	support for T series, X20
- *			bluetooth enable/disable
- *			hotkey events disabled by default
- *			removed fan control, currently useless
- *  2004-08-09	0.1	initial release, support for X series
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/string.h>
-
-#include <linux/proc_fs.h>
-#include <linux/backlight.h>
-#include <linux/fb.h>
-#include <asm/uaccess.h>
-
-#include <linux/dmi.h>
-#include <linux/jiffies.h>
-#include <linux/workqueue.h>
-
-#include <acpi/acpi_drivers.h>
-#include <acpi/acnamesp.h>
-
-#define IBM_NAME "ibm"
-#define IBM_DESC "IBM ThinkPad ACPI Extras"
-#define IBM_FILE "ibm_acpi"
-#define IBM_URL "http://ibm-acpi.sf.net/"
-
-MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh");
-MODULE_DESCRIPTION(IBM_DESC);
-MODULE_VERSION(IBM_VERSION);
-MODULE_LICENSE("GPL");
-
-#define IBM_DIR IBM_NAME
-
-#define IBM_LOG IBM_FILE ": "
-#define IBM_ERR	   KERN_ERR    IBM_LOG
-#define IBM_NOTICE KERN_NOTICE IBM_LOG
-#define IBM_INFO   KERN_INFO   IBM_LOG
-#define IBM_DEBUG  KERN_DEBUG  IBM_LOG
-
-#define IBM_MAX_ACPI_ARGS 3
-
-#define __unused __attribute__ ((unused))
-
-static int experimental;
-module_param(experimental, int, 0);
-
-static acpi_handle root_handle = NULL;
-
-#define IBM_HANDLE(object, parent, paths...)			\
-	static acpi_handle  object##_handle;			\
-	static acpi_handle *object##_parent = &parent##_handle;	\
-	static char        *object##_path;			\
-	static char        *object##_paths[] = { paths }
-
-IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0",	/* 240, 240x */
-	   "\\_SB.PCI.ISA.EC",	/* 570 */
-	   "\\_SB.PCI0.ISA0.EC0",	/* 600e/x, 770e, 770x */
-	   "\\_SB.PCI0.ISA.EC",	/* A21e, A2xm/p, T20-22, X20-21 */
-	   "\\_SB.PCI0.AD4S.EC0",	/* i1400, R30 */
-	   "\\_SB.PCI0.ICH3.EC0",	/* R31 */
-	   "\\_SB.PCI0.LPC.EC",	/* all others */
-    );
-
-IBM_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA",	/* 570 */
-	   "\\_SB.PCI0.AGP0.VID0",	/* 600e/x, 770x */
-	   "\\_SB.PCI0.VID0",	/* 770e */
-	   "\\_SB.PCI0.VID",	/* A21e, G4x, R50e, X30, X40 */
-	   "\\_SB.PCI0.AGP.VID",	/* all others */
-    );				/* R30, R31 */
-
-IBM_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID");	/* G41 */
-
-IBM_HANDLE(cmos, root, "\\UCMS",	/* R50, R50e, R50p, R51, T4x, X31, X40 */
-	   "\\CMOS",		/* A3x, G4x, R32, T23, T30, X22-24, X30 */
-	   "\\CMS",		/* R40, R40e */
-    );				/* all others */
-#ifdef CONFIG_ACPI_IBM_DOCK
-IBM_HANDLE(dock, root, "\\_SB.GDCK",	/* X30, X31, X40 */
-	   "\\_SB.PCI0.DOCK",	/* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */
-	   "\\_SB.PCI0.PCI1.DOCK",	/* all others */
-	   "\\_SB.PCI.ISA.SLCE",	/* 570 */
-    );				/* A21e,G4x,R30,R31,R32,R40,R40e,R50e */
-#endif
-#ifdef CONFIG_ACPI_IBM_BAY
-IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST",	/* 570 */
-	   "\\_SB.PCI0.IDE0.IDES.IDSM",	/* 600e/x, 770e, 770x */
-	   "\\_SB.PCI0.SATA.SCND.MSTR",	/* T60, X60, Z60 */ 
-	   "\\_SB.PCI0.IDE0.SCND.MSTR",	/* all others */
-    );				/* A21e, R30, R31 */
-
-IBM_HANDLE(bay_ej, bay, "_EJ3",	/* 600e/x, A2xm/p, A3x */
-	   "_EJ0",		/* all others */
-    );				/* 570,A21e,G4x,R30,R31,R32,R40e,R50e */
-
-IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV",	/* A3x, R32 */
-	   "\\_SB.PCI0.IDE0.IDEP.IDPS",	/* 600e/x, 770e, 770x */
-    );				/* all others */
-
-IBM_HANDLE(bay2_ej, bay2, "_EJ3",	/* 600e/x, 770e, A3x */
-	   "_EJ0",		/* 770x */
-    );				/* all others */
-#endif /* CONFIG_ACPI_IBM_BAY */
-
-/* don't list other alternatives as we install a notify handler on the 570 */
-IBM_HANDLE(pci, root, "\\_SB.PCI");	/* 570 */
-
-IBM_HANDLE(hkey, ec, "\\_SB.HKEY",	/* 600e/x, 770e, 770x */
-	   "^HKEY",		/* R30, R31 */
-	   "HKEY",		/* all others */
-    );				/* 570 */
-
-IBM_HANDLE(lght, root, "\\LGHT");	/* A21e, A2xm/p, T20-22, X20-21 */
-IBM_HANDLE(ledb, ec, "LEDB");	/* G4x */
-
-IBM_HANDLE(led, ec, "SLED",	/* 570 */
-	   "SYSL",		/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
-	   "LED",		/* all others */
-    );				/* R30, R31 */
-
-IBM_HANDLE(beep, ec, "BEEP");	/* all except R30, R31 */
-IBM_HANDLE(ecrd, ec, "ECRD");	/* 570 */
-IBM_HANDLE(ecwr, ec, "ECWR");	/* 570 */
-IBM_HANDLE(fans, ec, "FANS");	/* X31, X40, X41 */
-
-IBM_HANDLE(gfan, ec, "GFAN",	/* 570 */
-	   "\\FSPD",		/* 600e/x, 770e, 770x */
-    );				/* all others */
-
-IBM_HANDLE(sfan, ec, "SFAN",	/* 570 */
-	   "JFNS",		/* 770x-JL */
-    );				/* all others */
-
-#define IBM_HKEY_HID	"IBM0068"
-#define IBM_PCI_HID	"PNP0A03"
-
-enum thermal_access_mode {
-	IBMACPI_THERMAL_NONE = 0,	/* No thermal support */
-	IBMACPI_THERMAL_ACPI_TMP07,	/* Use ACPI TMP0-7 */
-	IBMACPI_THERMAL_ACPI_UPDT,	/* Use ACPI TMP0-7 with UPDT */
-	IBMACPI_THERMAL_TPEC_8,		/* Use ACPI EC regs, 8 sensors */
-	IBMACPI_THERMAL_TPEC_16,	/* Use ACPI EC regs, 16 sensors */
-};
-
-#define IBMACPI_MAX_THERMAL_SENSORS 16	/* Max thermal sensors supported */
-struct ibm_thermal_sensors_struct {
-	s32 temp[IBMACPI_MAX_THERMAL_SENSORS];
-};
-
-/*
- * FAN ACCESS MODES
- *
- * IBMACPI_FAN_RD_ACPI_GFAN:
- * 	ACPI GFAN method: returns fan level
- *
- * 	see IBMACPI_FAN_WR_ACPI_SFAN
- * 	EC 0x2f not available if GFAN exists
- *
- * IBMACPI_FAN_WR_ACPI_SFAN:
- * 	ACPI SFAN method: sets fan level, 0 (stop) to 7 (max)
- *
- * 	EC 0x2f might be available *for reading*, but never for writing.
- *
- * IBMACPI_FAN_WR_TPEC:
- * 	ThinkPad EC register 0x2f (HFSP): fan control loop mode Supported
- * 	on almost all ThinkPads
- *
- * 	Fan speed changes of any sort (including those caused by the
- * 	disengaged mode) are usually done slowly by the firmware as the
- * 	maximum ammount of fan duty cycle change per second seems to be
- * 	limited.
- *
- * 	Reading is not available if GFAN exists.
- * 	Writing is not available if SFAN exists.
- *
- * 	Bits
- *	 7	automatic mode engaged;
- *  		(default operation mode of the ThinkPad)
- * 		fan level is ignored in this mode.
- *	 6	disengage mode (takes precedence over bit 7);
- *		not available on all thinkpads.  May disable
- *		the tachometer, and speeds up fan to 100% duty-cycle,
- *		which speeds it up far above the standard RPM
- *		levels.  It is not impossible that it could cause
- *		hardware damage.
- *	5-3	unused in some models.  Extra bits for fan level
- *		in others, but still useless as all values above
- *		7 map to the same speed as level 7 in these models.
- *	2-0	fan level (0..7 usually)
- *			0x00 = stop
- * 			0x07 = max (set when temperatures critical)
- * 		Some ThinkPads may have other levels, see
- * 		IBMACPI_FAN_WR_ACPI_FANS (X31/X40/X41)
- *
- *	FIRMWARE BUG: on some models, EC 0x2f might not be initialized at
- *	boot. Apparently the EC does not intialize it, so unless ACPI DSDT
- *	does so, its initial value is meaningless (0x07).
- *
- *	For firmware bugs, refer to:
- *	http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
- *
- * 	----
- *
- *	ThinkPad EC register 0x84 (LSB), 0x85 (MSB):
- *	Main fan tachometer reading (in RPM)
- *
- *	This register is present on all ThinkPads with a new-style EC, and
- *	it is known not to be present on the A21m/e, and T22, as there is
- *	something else in offset 0x84 according to the ACPI DSDT.  Other
- *	ThinkPads from this same time period (and earlier) probably lack the
- *	tachometer as well.
- *
- *	Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare
- *	was never fixed by IBM to report the EC firmware version string
- *	probably support the tachometer (like the early X models), so
- *	detecting it is quite hard.  We need more data to know for sure.
- *
- *	FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings
- *	might result.
- *
- *	FIRMWARE BUG: when EC 0x2f bit 6 is set (disengaged mode), this
- *	register is not invalidated in ThinkPads that disable tachometer
- *	readings.  Thus, the tachometer readings go stale.
- *
- *	For firmware bugs, refer to:
- *	http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
- *
- * IBMACPI_FAN_WR_ACPI_FANS:
- *	ThinkPad X31, X40, X41.  Not available in the X60.
- *
- *	FANS ACPI handle: takes three arguments: low speed, medium speed,
- *	high speed.  ACPI DSDT seems to map these three speeds to levels
- *	as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH
- *	(this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3")
- *
- * 	The speeds are stored on handles
- * 	(FANA:FAN9), (FANC:FANB), (FANE:FAND).
- *
- * 	There are three default speed sets, acessible as handles:
- * 	FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H
- *
- * 	ACPI DSDT switches which set is in use depending on various
- * 	factors.
- *
- * 	IBMACPI_FAN_WR_TPEC is also available and should be used to
- * 	command the fan.  The X31/X40/X41 seems to have 8 fan levels,
- * 	but the ACPI tables just mention level 7.
- */
-
-enum fan_status_access_mode {
-	IBMACPI_FAN_NONE = 0,		/* No fan status or control */
-	IBMACPI_FAN_RD_ACPI_GFAN,	/* Use ACPI GFAN */
-	IBMACPI_FAN_RD_TPEC,		/* Use ACPI EC regs 0x2f, 0x84-0x85 */
-};
-
-enum fan_control_access_mode {
-	IBMACPI_FAN_WR_NONE = 0,	/* No fan control */
-	IBMACPI_FAN_WR_ACPI_SFAN,	/* Use ACPI SFAN */
-	IBMACPI_FAN_WR_TPEC,		/* Use ACPI EC reg 0x2f */
-	IBMACPI_FAN_WR_ACPI_FANS,	/* Use ACPI FANS and EC reg 0x2f */
-};
-
-enum fan_control_commands {
-	IBMACPI_FAN_CMD_SPEED 	= 0x0001,	/* speed command */
-	IBMACPI_FAN_CMD_LEVEL 	= 0x0002,	/* level command  */
-	IBMACPI_FAN_CMD_ENABLE	= 0x0004,	/* enable/disable cmd,
-						 * and also watchdog cmd */
-};
-
-enum {					/* Fan control constants */
-	fan_status_offset = 0x2f,	/* EC register 0x2f */
-	fan_rpm_offset = 0x84,		/* EC register 0x84: LSB, 0x85 MSB (RPM)
-					 * 0x84 must be read before 0x85 */
-
-	IBMACPI_FAN_EC_DISENGAGED 	= 0x40,	/* EC mode: tachometer
-						 * disengaged */
-	IBMACPI_FAN_EC_AUTO		= 0x80, /* EC mode: auto fan
-						 * control */
-};
-
-static char *ibm_thinkpad_ec_found = NULL;
-
-struct ibm_struct {
-	char *name;
-	char param[32];
-
-	char *hid;
-	struct acpi_driver *driver;
-
-	int (*init) (void);
-	int (*read) (char *);
-	int (*write) (char *);
-	void (*exit) (void);
-
-	void (*notify) (struct ibm_struct *, u32);
-	acpi_handle *handle;
-	int type;
-	struct acpi_device *device;
-
-	int driver_registered;
-	int proc_created;
-	int init_called;
-	int notify_installed;
-
-	int experimental;
-};
-
-static struct proc_dir_entry *proc_dir = NULL;
-
-static struct backlight_device *ibm_backlight_device = NULL;
-
-#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off")
-#define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
-#define strlencmp(a,b) (strncmp((a), (b), strlen(b)))
-
-static int acpi_evalf(acpi_handle handle,
-		      void *res, char *method, char *fmt, ...)
-{
-	char *fmt0 = fmt;
-	struct acpi_object_list params;
-	union acpi_object in_objs[IBM_MAX_ACPI_ARGS];
-	struct acpi_buffer result, *resultp;
-	union acpi_object out_obj;
-	acpi_status status;
-	va_list ap;
-	char res_type;
-	int success;
-	int quiet;
-
-	if (!*fmt) {
-		printk(IBM_ERR "acpi_evalf() called with empty format\n");
-		return 0;
-	}
-
-	if (*fmt == 'q') {
-		quiet = 1;
-		fmt++;
-	} else
-		quiet = 0;
-
-	res_type = *(fmt++);
-
-	params.count = 0;
-	params.pointer = &in_objs[0];
-
-	va_start(ap, fmt);
-	while (*fmt) {
-		char c = *(fmt++);
-		switch (c) {
-		case 'd':	/* int */
-			in_objs[params.count].integer.value = va_arg(ap, int);
-			in_objs[params.count++].type = ACPI_TYPE_INTEGER;
-			break;
-			/* add more types as needed */
-		default:
-			printk(IBM_ERR "acpi_evalf() called "
-			       "with invalid format character '%c'\n", c);
-			return 0;
-		}
-	}
-	va_end(ap);
-
-	if (res_type != 'v') {
-		result.length = sizeof(out_obj);
-		result.pointer = &out_obj;
-		resultp = &result;
-	} else
-		resultp = NULL;
-
-	status = acpi_evaluate_object(handle, method, &params, resultp);
-
-	switch (res_type) {
-	case 'd':		/* int */
-		if (res)
-			*(int *)res = out_obj.integer.value;
-		success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER;
-		break;
-	case 'v':		/* void */
-		success = status == AE_OK;
-		break;
-		/* add more types as needed */
-	default:
-		printk(IBM_ERR "acpi_evalf() called "
-		       "with invalid format character '%c'\n", res_type);
-		return 0;
-	}
-
-	if (!success && !quiet)
-		printk(IBM_ERR "acpi_evalf(%s, %s, ...) failed: %d\n",
-		       method, fmt0, status);
-
-	return success;
-}
-
-static void __unused acpi_print_int(acpi_handle handle, char *method)
-{
-	int i;
-
-	if (acpi_evalf(handle, &i, method, "d"))
-		printk(IBM_INFO "%s = 0x%x\n", method, i);
-	else
-		printk(IBM_ERR "error calling %s\n", method);
-}
-
-static char *next_cmd(char **cmds)
-{
-	char *start = *cmds;
-	char *end;
-
-	while ((end = strchr(start, ',')) && end == start)
-		start = end + 1;
-
-	if (!end)
-		return NULL;
-
-	*end = 0;
-	*cmds = end + 1;
-	return start;
-}
-
-static int ibm_acpi_driver_init(void)
-{
-	printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION);
-	printk(IBM_INFO "%s\n", IBM_URL);
-
-	if (ibm_thinkpad_ec_found)
-		printk(IBM_INFO "ThinkPad EC firmware %s\n",
-		       ibm_thinkpad_ec_found);
-
-	return 0;
-}
-
-static int driver_read(char *p)
-{
-	int len = 0;
-
-	len += sprintf(p + len, "driver:\t\t%s\n", IBM_DESC);
-	len += sprintf(p + len, "version:\t%s\n", IBM_VERSION);
-
-	return len;
-}
-
-static int hotkey_supported;
-static int hotkey_mask_supported;
-static int hotkey_orig_status;
-static int hotkey_orig_mask;
-
-static int hotkey_get(int *status, int *mask)
-{
-	if (!acpi_evalf(hkey_handle, status, "DHKC", "d"))
-		return 0;
-
-	if (hotkey_mask_supported)
-		if (!acpi_evalf(hkey_handle, mask, "DHKN", "d"))
-			return 0;
-
-	return 1;
-}
-
-static int hotkey_set(int status, int mask)
-{
-	int i;
-
-	if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status))
-		return 0;
-
-	if (hotkey_mask_supported)
-		for (i = 0; i < 32; i++) {
-			int bit = ((1 << i) & mask) != 0;
-			if (!acpi_evalf(hkey_handle,
-					NULL, "MHKM", "vdd", i + 1, bit))
-				return 0;
-		}
-
-	return 1;
-}
-
-static int hotkey_init(void)
-{
-	/* hotkey not supported on 570 */
-	hotkey_supported = hkey_handle != NULL;
-
-	if (hotkey_supported) {
-		/* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
-		   A30, R30, R31, T20-22, X20-21, X22-24 */
-		hotkey_mask_supported =
-		    acpi_evalf(hkey_handle, NULL, "DHKN", "qv");
-
-		if (!hotkey_get(&hotkey_orig_status, &hotkey_orig_mask))
-			return -ENODEV;
-	}
-
-	return 0;
-}
-
-static int hotkey_read(char *p)
-{
-	int status, mask;
-	int len = 0;
-
-	if (!hotkey_supported) {
-		len += sprintf(p + len, "status:\t\tnot supported\n");
-		return len;
-	}
-
-	if (!hotkey_get(&status, &mask))
-		return -EIO;
-
-	len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0));
-	if (hotkey_mask_supported) {
-		len += sprintf(p + len, "mask:\t\t0x%04x\n", mask);
-		len += sprintf(p + len,
-			       "commands:\tenable, disable, reset, <mask>\n");
-	} else {
-		len += sprintf(p + len, "mask:\t\tnot supported\n");
-		len += sprintf(p + len, "commands:\tenable, disable, reset\n");
-	}
-
-	return len;
-}
-
-static int hotkey_write(char *buf)
-{
-	int status, mask;
-	char *cmd;
-	int do_cmd = 0;
-
-	if (!hotkey_supported)
-		return -ENODEV;
-
-	if (!hotkey_get(&status, &mask))
-		return -EIO;
-
-	while ((cmd = next_cmd(&buf))) {
-		if (strlencmp(cmd, "enable") == 0) {
-			status = 1;
-		} else if (strlencmp(cmd, "disable") == 0) {
-			status = 0;
-		} else if (strlencmp(cmd, "reset") == 0) {
-			status = hotkey_orig_status;
-			mask = hotkey_orig_mask;
-		} else if (sscanf(cmd, "0x%x", &mask) == 1) {
-			/* mask set */
-		} else if (sscanf(cmd, "%x", &mask) == 1) {
-			/* mask set */
-		} else
-			return -EINVAL;
-		do_cmd = 1;
-	}
-
-	if (do_cmd && !hotkey_set(status, mask))
-		return -EIO;
-
-	return 0;
-}
-
-static void hotkey_exit(void)
-{
-	if (hotkey_supported)
-		hotkey_set(hotkey_orig_status, hotkey_orig_mask);
-}
-
-static void hotkey_notify(struct ibm_struct *ibm, u32 event)
-{
-	int hkey;
-
-	if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d"))
-		acpi_bus_generate_event(ibm->device, event, hkey);
-	else {
-		printk(IBM_ERR "unknown hotkey event %d\n", event);
-		acpi_bus_generate_event(ibm->device, event, 0);
-	}
-}
-
-static int bluetooth_supported;
-
-static int bluetooth_init(void)
-{
-	/* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
-	   G4x, R30, R31, R40e, R50e, T20-22, X20-21 */
-	bluetooth_supported = hkey_handle &&
-	    acpi_evalf(hkey_handle, NULL, "GBDC", "qv");
-
-	return 0;
-}
-
-static int bluetooth_status(void)
-{
-	int status;
-
-	if (!bluetooth_supported ||
-	    !acpi_evalf(hkey_handle, &status, "GBDC", "d"))
-		status = 0;
-
-	return status;
-}
-
-static int bluetooth_read(char *p)
-{
-	int len = 0;
-	int status = bluetooth_status();
-
-	if (!bluetooth_supported)
-		len += sprintf(p + len, "status:\t\tnot supported\n");
-	else if (!(status & 1))
-		len += sprintf(p + len, "status:\t\tnot installed\n");
-	else {
-		len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1));
-		len += sprintf(p + len, "commands:\tenable, disable\n");
-	}
-
-	return len;
-}
-
-static int bluetooth_write(char *buf)
-{
-	int status = bluetooth_status();
-	char *cmd;
-	int do_cmd = 0;
-
-	if (!bluetooth_supported)
-		return -ENODEV;
-
-	while ((cmd = next_cmd(&buf))) {
-		if (strlencmp(cmd, "enable") == 0) {
-			status |= 2;
-		} else if (strlencmp(cmd, "disable") == 0) {
-			status &= ~2;
-		} else
-			return -EINVAL;
-		do_cmd = 1;
-	}
-
-	if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
-		return -EIO;
-
-	return 0;
-}
-
-static int wan_supported;
-
-static int wan_init(void)
-{
-	wan_supported = hkey_handle &&
-	    acpi_evalf(hkey_handle, NULL, "GWAN", "qv");
-
-	return 0;
-}
-
-static int wan_status(void)
-{
-	int status;
-
-	if (!wan_supported || !acpi_evalf(hkey_handle, &status, "GWAN", "d"))
-		status = 0;
-
-	return status;
-}
-
-static int wan_read(char *p)
-{
-	int len = 0;
-	int status = wan_status();
-
-	if (!wan_supported)
-		len += sprintf(p + len, "status:\t\tnot supported\n");
-	else if (!(status & 1))
-		len += sprintf(p + len, "status:\t\tnot installed\n");
-	else {
-		len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1));
-		len += sprintf(p + len, "commands:\tenable, disable\n");
-	}
-
-	return len;
-}
-
-static int wan_write(char *buf)
-{
-	int status = wan_status();
-	char *cmd;
-	int do_cmd = 0;
-
-	if (!wan_supported)
-		return -ENODEV;
-
-	while ((cmd = next_cmd(&buf))) {
-		if (strlencmp(cmd, "enable") == 0) {
-			status |= 2;
-		} else if (strlencmp(cmd, "disable") == 0) {
-			status &= ~2;
-		} else
-			return -EINVAL;
-		do_cmd = 1;
-	}
-
-	if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
-		return -EIO;
-
-	return 0;
-}
-
-enum video_access_mode {
-	IBMACPI_VIDEO_NONE = 0,
-	IBMACPI_VIDEO_570,	/* 570 */
-	IBMACPI_VIDEO_770,	/* 600e/x, 770e, 770x */
-	IBMACPI_VIDEO_NEW,	/* all others */
-};
-
-static enum video_access_mode video_supported;
-static int video_orig_autosw;
-
-static int video_init(void)
-{
-	int ivga;
-
-	if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga)
-		/* G41, assume IVGA doesn't change */
-		vid_handle = vid2_handle;
-
-	if (!vid_handle)
-		/* video switching not supported on R30, R31 */
-		video_supported = IBMACPI_VIDEO_NONE;
-	else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd"))
-		/* 570 */
-		video_supported = IBMACPI_VIDEO_570;
-	else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd"))
-		/* 600e/x, 770e, 770x */
-		video_supported = IBMACPI_VIDEO_770;
-	else
-		/* all others */
-		video_supported = IBMACPI_VIDEO_NEW;
-
-	return 0;
-}
-
-static int video_status(void)
-{
-	int status = 0;
-	int i;
-
-	if (video_supported == IBMACPI_VIDEO_570) {
-		if (acpi_evalf(NULL, &i, "\\_SB.PHS", "dd", 0x87))
-			status = i & 3;
-	} else if (video_supported == IBMACPI_VIDEO_770) {
-		if (acpi_evalf(NULL, &i, "\\VCDL", "d"))
-			status |= 0x01 * i;
-		if (acpi_evalf(NULL, &i, "\\VCDC", "d"))
-			status |= 0x02 * i;
-	} else if (video_supported == IBMACPI_VIDEO_NEW) {
-		acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1);
-		if (acpi_evalf(NULL, &i, "\\VCDC", "d"))
-			status |= 0x02 * i;
-
-		acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0);
-		if (acpi_evalf(NULL, &i, "\\VCDL", "d"))
-			status |= 0x01 * i;
-		if (acpi_evalf(NULL, &i, "\\VCDD", "d"))
-			status |= 0x08 * i;
-	}
-
-	return status;
-}
-
-static int video_autosw(void)
-{
-	int autosw = 0;
-
-	if (video_supported == IBMACPI_VIDEO_570)
-		acpi_evalf(vid_handle, &autosw, "SWIT", "d");
-	else if (video_supported == IBMACPI_VIDEO_770 ||
-		 video_supported == IBMACPI_VIDEO_NEW)
-		acpi_evalf(vid_handle, &autosw, "^VDEE", "d");
-
-	return autosw & 1;
-}
-
-static int video_read(char *p)
-{
-	int status = video_status();
-	int autosw = video_autosw();
-	int len = 0;
-
-	if (!video_supported) {
-		len += sprintf(p + len, "status:\t\tnot supported\n");
-		return len;
-	}
-
-	len += sprintf(p + len, "status:\t\tsupported\n");
-	len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0));
-	len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1));
-	if (video_supported == IBMACPI_VIDEO_NEW)
-		len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3));
-	len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0));
-	len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n");
-	len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n");
-	if (video_supported == IBMACPI_VIDEO_NEW)
-		len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n");
-	len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n");
-	len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n");
-
-	return len;
-}
-
-static int video_switch(void)
-{
-	int autosw = video_autosw();
-	int ret;
-
-	if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
-		return -EIO;
-	ret = video_supported == IBMACPI_VIDEO_570 ?
-	    acpi_evalf(ec_handle, NULL, "_Q16", "v") :
-	    acpi_evalf(vid_handle, NULL, "VSWT", "v");
-	acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw);
-
-	return ret;
-}
-
-static int video_expand(void)
-{
-	if (video_supported == IBMACPI_VIDEO_570)
-		return acpi_evalf(ec_handle, NULL, "_Q17", "v");
-	else if (video_supported == IBMACPI_VIDEO_770)
-		return acpi_evalf(vid_handle, NULL, "VEXP", "v");
-	else
-		return acpi_evalf(NULL, NULL, "\\VEXP", "v");
-}
-
-static int video_switch2(int status)
-{
-	int ret;
-
-	if (video_supported == IBMACPI_VIDEO_570) {
-		ret = acpi_evalf(NULL, NULL,
-				 "\\_SB.PHS2", "vdd", 0x8b, status | 0x80);
-	} else if (video_supported == IBMACPI_VIDEO_770) {
-		int autosw = video_autosw();
-		if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
-			return -EIO;
-
-		ret = acpi_evalf(vid_handle, NULL,
-				 "ASWT", "vdd", status * 0x100, 0);
-
-		acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw);
-	} else {
-		ret = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) &&
-		    acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1);
-	}
-
-	return ret;
-}
-
-static int video_write(char *buf)
-{
-	char *cmd;
-	int enable, disable, status;
-
-	if (!video_supported)
-		return -ENODEV;
-
-	enable = disable = 0;
-
-	while ((cmd = next_cmd(&buf))) {
-		if (strlencmp(cmd, "lcd_enable") == 0) {
-			enable |= 0x01;
-		} else if (strlencmp(cmd, "lcd_disable") == 0) {
-			disable |= 0x01;
-		} else if (strlencmp(cmd, "crt_enable") == 0) {
-			enable |= 0x02;
-		} else if (strlencmp(cmd, "crt_disable") == 0) {
-			disable |= 0x02;
-		} else if (video_supported == IBMACPI_VIDEO_NEW &&
-			   strlencmp(cmd, "dvi_enable") == 0) {
-			enable |= 0x08;
-		} else if (video_supported == IBMACPI_VIDEO_NEW &&
-			   strlencmp(cmd, "dvi_disable") == 0) {
-			disable |= 0x08;
-		} else if (strlencmp(cmd, "auto_enable") == 0) {
-			if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
-				return -EIO;
-		} else if (strlencmp(cmd, "auto_disable") == 0) {
-			if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 0))
-				return -EIO;
-		} else if (strlencmp(cmd, "video_switch") == 0) {
-			if (!video_switch())
-				return -EIO;
-		} else if (strlencmp(cmd, "expand_toggle") == 0) {
-			if (!video_expand())
-				return -EIO;
-		} else
-			return -EINVAL;
-	}
-
-	if (enable || disable) {
-		status = (video_status() & 0x0f & ~disable) | enable;
-		if (!video_switch2(status))
-			return -EIO;
-	}
-
-	return 0;
-}
-
-static void video_exit(void)
-{
-	acpi_evalf(vid_handle, NULL, "_DOS", "vd", video_orig_autosw);
-}
-
-static int light_supported;
-static int light_status_supported;
-
-static int light_init(void)
-{
-	/* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */
-	light_supported = (cmos_handle || lght_handle) && !ledb_handle;
-
-	if (light_supported)
-		/* light status not supported on
-		   570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */
-		light_status_supported = acpi_evalf(ec_handle, NULL,
-						    "KBLT", "qv");
-
-	return 0;
-}
-
-static int light_read(char *p)
-{
-	int len = 0;
-	int status = 0;
-
-	if (!light_supported) {
-		len += sprintf(p + len, "status:\t\tnot supported\n");
-	} else if (!light_status_supported) {
-		len += sprintf(p + len, "status:\t\tunknown\n");
-		len += sprintf(p + len, "commands:\ton, off\n");
-	} else {
-		if (!acpi_evalf(ec_handle, &status, "KBLT", "d"))
-			return -EIO;
-		len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0));
-		len += sprintf(p + len, "commands:\ton, off\n");
-	}
-
-	return len;
-}
-
-static int light_write(char *buf)
-{
-	int cmos_cmd, lght_cmd;
-	char *cmd;
-	int success;
-
-	if (!light_supported)
-		return -ENODEV;
-
-	while ((cmd = next_cmd(&buf))) {
-		if (strlencmp(cmd, "on") == 0) {
-			cmos_cmd = 0x0c;
-			lght_cmd = 1;
-		} else if (strlencmp(cmd, "off") == 0) {
-			cmos_cmd = 0x0d;
-			lght_cmd = 0;
-		} else
-			return -EINVAL;
-
-		success = cmos_handle ?
-		    acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) :
-		    acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd);
-		if (!success)
-			return -EIO;
-	}
-
-	return 0;
-}
-
-#if defined(CONFIG_ACPI_IBM_DOCK) || defined(CONFIG_ACPI_IBM_BAY)
-static int _sta(acpi_handle handle)
-{
-	int status;
-
-	if (!handle || !acpi_evalf(handle, &status, "_STA", "d"))
-		status = 0;
-
-	return status;
-}
-#endif
-
-#ifdef CONFIG_ACPI_IBM_DOCK
-#define dock_docked() (_sta(dock_handle) & 1)
-
-static int dock_read(char *p)
-{
-	int len = 0;
-	int docked = dock_docked();
-
-	if (!dock_handle)
-		len += sprintf(p + len, "status:\t\tnot supported\n");
-	else if (!docked)
-		len += sprintf(p + len, "status:\t\tundocked\n");
-	else {
-		len += sprintf(p + len, "status:\t\tdocked\n");
-		len += sprintf(p + len, "commands:\tdock, undock\n");
-	}
-
-	return len;
-}
-
-static int dock_write(char *buf)
-{
-	char *cmd;
-
-	if (!dock_docked())
-		return -ENODEV;
-
-	while ((cmd = next_cmd(&buf))) {
-		if (strlencmp(cmd, "undock") == 0) {
-			if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) ||
-			    !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1))
-				return -EIO;
-		} else if (strlencmp(cmd, "dock") == 0) {
-			if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1))
-				return -EIO;
-		} else
-			return -EINVAL;
-	}
-
-	return 0;
-}
-
-static void dock_notify(struct ibm_struct *ibm, u32 event)
-{
-	int docked = dock_docked();
-	int pci = ibm->hid && strstr(ibm->hid, IBM_PCI_HID);
-
-	if (event == 1 && !pci)	/* 570 */
-		acpi_bus_generate_event(ibm->device, event, 1);	/* button */
-	else if (event == 1 && pci)	/* 570 */
-		acpi_bus_generate_event(ibm->device, event, 3);	/* dock */
-	else if (event == 3 && docked)
-		acpi_bus_generate_event(ibm->device, event, 1);	/* button */
-	else if (event == 3 && !docked)
-		acpi_bus_generate_event(ibm->device, event, 2);	/* undock */
-	else if (event == 0 && docked)
-		acpi_bus_generate_event(ibm->device, event, 3);	/* dock */
-	else {
-		printk(IBM_ERR "unknown dock event %d, status %d\n",
-		       event, _sta(dock_handle));
-		acpi_bus_generate_event(ibm->device, event, 0);	/* unknown */
-	}
-}
-#endif
-
-#ifdef CONFIG_ACPI_IBM_BAY
-static int bay_status_supported;
-static int bay_status2_supported;
-static int bay_eject_supported;
-static int bay_eject2_supported;
-
-static int bay_init(void)
-{
-	bay_status_supported = bay_handle &&
-	    acpi_evalf(bay_handle, NULL, "_STA", "qv");
-	bay_status2_supported = bay2_handle &&
-	    acpi_evalf(bay2_handle, NULL, "_STA", "qv");
-
-	bay_eject_supported = bay_handle && bay_ej_handle &&
-	    (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental);
-	bay_eject2_supported = bay2_handle && bay2_ej_handle &&
-	    (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental);
-
-	return 0;
-}
-
-#define bay_occupied(b) (_sta(b##_handle) & 1)
-
-static int bay_read(char *p)
-{
-	int len = 0;
-	int occupied = bay_occupied(bay);
-	int occupied2 = bay_occupied(bay2);
-	int eject, eject2;
-
-	len += sprintf(p + len, "status:\t\t%s\n", bay_status_supported ?
-		       (occupied ? "occupied" : "unoccupied") :
-		       "not supported");
-	if (bay_status2_supported)
-		len += sprintf(p + len, "status2:\t%s\n", occupied2 ?
-			       "occupied" : "unoccupied");
-
-	eject = bay_eject_supported && occupied;
-	eject2 = bay_eject2_supported && occupied2;
-
-	if (eject && eject2)
-		len += sprintf(p + len, "commands:\teject, eject2\n");
-	else if (eject)
-		len += sprintf(p + len, "commands:\teject\n");
-	else if (eject2)
-		len += sprintf(p + len, "commands:\teject2\n");
-
-	return len;
-}
-
-static int bay_write(char *buf)
-{
-	char *cmd;
-
-	if (!bay_eject_supported && !bay_eject2_supported)
-		return -ENODEV;
-
-	while ((cmd = next_cmd(&buf))) {
-		if (bay_eject_supported && strlencmp(cmd, "eject") == 0) {
-			if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1))
-				return -EIO;
-		} else if (bay_eject2_supported &&
-			   strlencmp(cmd, "eject2") == 0) {
-			if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1))
-				return -EIO;
-		} else
-			return -EINVAL;
-	}
-
-	return 0;
-}
-
-static void bay_notify(struct ibm_struct *ibm, u32 event)
-{
-	acpi_bus_generate_event(ibm->device, event, 0);
-}
-#endif /* CONFIG_ACPI_IBM_BAY */
-
-static int cmos_read(char *p)
-{
-	int len = 0;
-
-	/* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
-	   R30, R31, T20-22, X20-21 */
-	if (!cmos_handle)
-		len += sprintf(p + len, "status:\t\tnot supported\n");
-	else {
-		len += sprintf(p + len, "status:\t\tsupported\n");
-		len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n");
-	}
-
-	return len;
-}
-
-static int cmos_eval(int cmos_cmd)
-{
-	if (cmos_handle)
-		return acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd);
-	else
-		return 1;
-}
-
-static int cmos_write(char *buf)
-{
-	char *cmd;
-	int cmos_cmd;
-
-	if (!cmos_handle)
-		return -EINVAL;
-
-	while ((cmd = next_cmd(&buf))) {
-		if (sscanf(cmd, "%u", &cmos_cmd) == 1 &&
-		    cmos_cmd >= 0 && cmos_cmd <= 21) {
-			/* cmos_cmd set */
-		} else
-			return -EINVAL;
-
-		if (!cmos_eval(cmos_cmd))
-			return -EIO;
-	}
-
-	return 0;
-}
-
-enum led_access_mode {
-	IBMACPI_LED_NONE = 0,
-	IBMACPI_LED_570,	/* 570 */
-	IBMACPI_LED_OLD,	/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
-	IBMACPI_LED_NEW,	/* all others */
-};
-static enum led_access_mode led_supported;
-
-static int led_init(void)
-{
-	if (!led_handle)
-		/* led not supported on R30, R31 */
-		led_supported = IBMACPI_LED_NONE;
-	else if (strlencmp(led_path, "SLED") == 0)
-		/* 570 */
-		led_supported = IBMACPI_LED_570;
-	else if (strlencmp(led_path, "SYSL") == 0)
-		/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
-		led_supported = IBMACPI_LED_OLD;
-	else
-		/* all others */
-		led_supported = IBMACPI_LED_NEW;
-
-	return 0;
-}
-
-#define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking"))
-
-static int led_read(char *p)
-{
-	int len = 0;
-
-	if (!led_supported) {
-		len += sprintf(p + len, "status:\t\tnot supported\n");
-		return len;
-	}
-	len += sprintf(p + len, "status:\t\tsupported\n");
-
-	if (led_supported == IBMACPI_LED_570) {
-		/* 570 */
-		int i, status;
-		for (i = 0; i < 8; i++) {
-			if (!acpi_evalf(ec_handle,
-					&status, "GLED", "dd", 1 << i))
-				return -EIO;
-			len += sprintf(p + len, "%d:\t\t%s\n",
-				       i, led_status(status));
-		}
-	}
-
-	len += sprintf(p + len, "commands:\t"
-		       "<led> on, <led> off, <led> blink (<led> is 0-7)\n");
-
-	return len;
-}
-
-/* off, on, blink */
-static const int led_sled_arg1[] = { 0, 1, 3 };
-static const int led_exp_hlbl[] = { 0, 0, 1 };	/* led# * */
-static const int led_exp_hlcl[] = { 0, 1, 1 };	/* led# * */
-static const int led_led_arg1[] = { 0, 0x80, 0xc0 };
-
-#define EC_HLCL 0x0c
-#define EC_HLBL 0x0d
-#define EC_HLMS 0x0e
-
-static int led_write(char *buf)
-{
-	char *cmd;
-	int led, ind, ret;
-
-	if (!led_supported)
-		return -ENODEV;
-
-	while ((cmd = next_cmd(&buf))) {
-		if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7)
-			return -EINVAL;
-
-		if (strstr(cmd, "off")) {
-			ind = 0;
-		} else if (strstr(cmd, "on")) {
-			ind = 1;
-		} else if (strstr(cmd, "blink")) {
-			ind = 2;
-		} else
-			return -EINVAL;
-
-		if (led_supported == IBMACPI_LED_570) {
-			/* 570 */
-			led = 1 << led;
-			if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
-					led, led_sled_arg1[ind]))
-				return -EIO;
-		} else if (led_supported == IBMACPI_LED_OLD) {
-			/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
-			led = 1 << led;
-			ret = ec_write(EC_HLMS, led);
-			if (ret >= 0)
-				ret =
-				    ec_write(EC_HLBL, led * led_exp_hlbl[ind]);
-			if (ret >= 0)
-				ret =
-				    ec_write(EC_HLCL, led * led_exp_hlcl[ind]);
-			if (ret < 0)
-				return ret;
-		} else {
-			/* all others */
-			if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
-					led, led_led_arg1[ind]))
-				return -EIO;
-		}
-	}
-
-	return 0;
-}
-
-static int beep_read(char *p)
-{
-	int len = 0;
-
-	if (!beep_handle)
-		len += sprintf(p + len, "status:\t\tnot supported\n");
-	else {
-		len += sprintf(p + len, "status:\t\tsupported\n");
-		len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n");
-	}
-
-	return len;
-}
-
-static int beep_write(char *buf)
-{
-	char *cmd;
-	int beep_cmd;
-
-	if (!beep_handle)
-		return -ENODEV;
-
-	while ((cmd = next_cmd(&buf))) {
-		if (sscanf(cmd, "%u", &beep_cmd) == 1 &&
-		    beep_cmd >= 0 && beep_cmd <= 17) {
-			/* beep_cmd set */
-		} else
-			return -EINVAL;
-		if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0))
-			return -EIO;
-	}
-
-	return 0;
-}
-
-static int acpi_ec_read(int i, u8 * p)
-{
-	int v;
-
-	if (ecrd_handle) {
-		if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i))
-			return 0;
-		*p = v;
-	} else {
-		if (ec_read(i, p) < 0)
-			return 0;
-	}
-
-	return 1;
-}
-
-static int acpi_ec_write(int i, u8 v)
-{
-	if (ecwr_handle) {
-		if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v))
-			return 0;
-	} else {
-		if (ec_write(i, v) < 0)
-			return 0;
-	}
-
-	return 1;
-}
-
-static enum thermal_access_mode thermal_read_mode;
-
-static int thermal_init(void)
-{
-	u8 t, ta1, ta2;
-	int i;
-	int acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
-
-	if (ibm_thinkpad_ec_found && experimental) {
-		/*
-		 * Direct EC access mode: sensors at registers
-		 * 0x78-0x7F, 0xC0-0xC7.  Registers return 0x00 for
-		 * non-implemented, thermal sensors return 0x80 when
-		 * not available
-		 */
-
-		ta1 = ta2 = 0;
-		for (i = 0; i < 8; i++) {
-			if (likely(acpi_ec_read(0x78 + i, &t))) {
-				ta1 |= t;
-			} else {
-				ta1 = 0;
-				break;
-			}
-			if (likely(acpi_ec_read(0xC0 + i, &t))) {
-				ta2 |= t;
-			} else {
-				ta1 = 0;
-				break;
-			}
-		}
-		if (ta1 == 0) {
-			/* This is sheer paranoia, but we handle it anyway */
-			if (acpi_tmp7) {
-				printk(IBM_ERR
-				       "ThinkPad ACPI EC access misbehaving, "
-				       "falling back to ACPI TMPx access mode\n");
-				thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07;
-			} else {
-				printk(IBM_ERR
-				       "ThinkPad ACPI EC access misbehaving, "
-				       "disabling thermal sensors access\n");
-				thermal_read_mode = IBMACPI_THERMAL_NONE;
-			}
-		} else {
-			thermal_read_mode =
-			    (ta2 != 0) ?
-			    IBMACPI_THERMAL_TPEC_16 : IBMACPI_THERMAL_TPEC_8;
-		}
-	} else if (acpi_tmp7) {
-		if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
-			/* 600e/x, 770e, 770x */
-			thermal_read_mode = IBMACPI_THERMAL_ACPI_UPDT;
-		} else {
-			/* Standard ACPI TMPx access, max 8 sensors */
-			thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07;
-		}
-	} else {
-		/* temperatures not supported on 570, G4x, R30, R31, R32 */
-		thermal_read_mode = IBMACPI_THERMAL_NONE;
-	}
-
-	return 0;
-}
-
-static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s)
-{
-	int i, t;
-	s8 tmp;
-	char tmpi[] = "TMPi";
-
-	if (!s)
-		return -EINVAL;
-
-	switch (thermal_read_mode) {
-#if IBMACPI_MAX_THERMAL_SENSORS >= 16
-	case IBMACPI_THERMAL_TPEC_16:
-		for (i = 0; i < 8; i++) {
-			if (!acpi_ec_read(0xC0 + i, &tmp))
-				return -EIO;
-			s->temp[i + 8] = tmp * 1000;
-		}
-		/* fallthrough */
-#endif
-	case IBMACPI_THERMAL_TPEC_8:
-		for (i = 0; i < 8; i++) {
-			if (!acpi_ec_read(0x78 + i, &tmp))
-				return -EIO;
-			s->temp[i] = tmp * 1000;
-		}
-		return (thermal_read_mode == IBMACPI_THERMAL_TPEC_16) ? 16 : 8;
-
-	case IBMACPI_THERMAL_ACPI_UPDT:
-		if (!acpi_evalf(ec_handle, NULL, "UPDT", "v"))
-			return -EIO;
-		for (i = 0; i < 8; i++) {
-			tmpi[3] = '0' + i;
-			if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
-				return -EIO;
-			s->temp[i] = (t - 2732) * 100;
-		}
-		return 8;
-
-	case IBMACPI_THERMAL_ACPI_TMP07:
-		for (i = 0; i < 8; i++) {
-			tmpi[3] = '0' + i;
-			if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
-				return -EIO;
-			s->temp[i] = t * 1000;
-		}
-		return 8;
-
-	case IBMACPI_THERMAL_NONE:
-	default:
-		return 0;
-	}
-}
-
-static int thermal_read(char *p)
-{
-	int len = 0;
-	int n, i;
-	struct ibm_thermal_sensors_struct t;
-
-	n = thermal_get_sensors(&t);
-	if (unlikely(n < 0))
-		return n;
-
-	len += sprintf(p + len, "temperatures:\t");
-
-	if (n > 0) {
-		for (i = 0; i < (n - 1); i++)
-			len += sprintf(p + len, "%d ", t.temp[i] / 1000);
-		len += sprintf(p + len, "%d\n", t.temp[i] / 1000);
-	} else
-		len += sprintf(p + len, "not supported\n");
-
-	return len;
-}
-
-static u8 ecdump_regs[256];
-
-static int ecdump_read(char *p)
-{
-	int len = 0;
-	int i, j;
-	u8 v;
-
-	len += sprintf(p + len, "EC      "
-		       " +00 +01 +02 +03 +04 +05 +06 +07"
-		       " +08 +09 +0a +0b +0c +0d +0e +0f\n");
-	for (i = 0; i < 256; i += 16) {
-		len += sprintf(p + len, "EC 0x%02x:", i);
-		for (j = 0; j < 16; j++) {
-			if (!acpi_ec_read(i + j, &v))
-				break;
-			if (v != ecdump_regs[i + j])
-				len += sprintf(p + len, " *%02x", v);
-			else
-				len += sprintf(p + len, "  %02x", v);
-			ecdump_regs[i + j] = v;
-		}
-		len += sprintf(p + len, "\n");
-		if (j != 16)
-			break;
-	}
-
-	/* These are way too dangerous to advertise openly... */
-#if 0
-	len += sprintf(p + len, "commands:\t0x<offset> 0x<value>"
-		       " (<offset> is 00-ff, <value> is 00-ff)\n");
-	len += sprintf(p + len, "commands:\t0x<offset> <value>  "
-		       " (<offset> is 00-ff, <value> is 0-255)\n");
-#endif
-	return len;
-}
-
-static int ecdump_write(char *buf)
-{
-	char *cmd;
-	int i, v;
-
-	while ((cmd = next_cmd(&buf))) {
-		if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) {
-			/* i and v set */
-		} else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) {
-			/* i and v set */
-		} else
-			return -EINVAL;
-		if (i >= 0 && i < 256 && v >= 0 && v < 256) {
-			if (!acpi_ec_write(i, v))
-				return -EIO;
-		} else
-			return -EINVAL;
-	}
-
-	return 0;
-}
-
-static int brightness_offset = 0x31;
-
-static int brightness_get(struct backlight_device *bd)
-{
-	u8 level;
-	if (!acpi_ec_read(brightness_offset, &level))
-		return -EIO;
-
-	level &= 0x7;
-
-	return level;
-}
-
-static int brightness_read(char *p)
-{
-	int len = 0;
-	int level;
-
-	if ((level = brightness_get(NULL)) < 0) {
-		len += sprintf(p + len, "level:\t\tunreadable\n");
-	} else {
-		len += sprintf(p + len, "level:\t\t%d\n", level & 0x7);
-		len += sprintf(p + len, "commands:\tup, down\n");
-		len += sprintf(p + len, "commands:\tlevel <level>"
-			       " (<level> is 0-7)\n");
-	}
-
-	return len;
-}
-
-#define BRIGHTNESS_UP	4
-#define BRIGHTNESS_DOWN	5
-
-static int brightness_set(int value)
-{
-	int cmos_cmd, inc, i;
-	int current_value = brightness_get(NULL);
-
-	value &= 7;
-
-	cmos_cmd = value > current_value ? BRIGHTNESS_UP : BRIGHTNESS_DOWN;
-	inc = value > current_value ? 1 : -1;
-	for (i = current_value; i != value; i += inc) {
-		if (!cmos_eval(cmos_cmd))
-			return -EIO;
-		if (!acpi_ec_write(brightness_offset, i + inc))
-			return -EIO;
-	}
-
-	return 0;
-}
-
-static int brightness_write(char *buf)
-{
-	int level;
-	int new_level;
-	char *cmd;
-
-	while ((cmd = next_cmd(&buf))) {
-		if ((level = brightness_get(NULL)) < 0)
-			return level;
-		level &= 7;
-
-		if (strlencmp(cmd, "up") == 0) {
-			new_level = level == 7 ? 7 : level + 1;
-		} else if (strlencmp(cmd, "down") == 0) {
-			new_level = level == 0 ? 0 : level - 1;
-		} else if (sscanf(cmd, "level %d", &new_level) == 1 &&
-			   new_level >= 0 && new_level <= 7) {
-			/* new_level set */
-		} else
-			return -EINVAL;
-
-		brightness_set(new_level);
-	}
-
-	return 0;
-}
-
-static int brightness_update_status(struct backlight_device *bd)
-{
-	return brightness_set(
-		(bd->props.fb_blank == FB_BLANK_UNBLANK &&
-		 bd->props.power == FB_BLANK_UNBLANK) ?
-				bd->props.brightness : 0);
-}
-
-static struct backlight_ops ibm_backlight_data = {
-        .get_brightness = brightness_get,
-        .update_status  = brightness_update_status,
-};
-
-static int brightness_init(void)
-{
-	int b;
-
-	b = brightness_get(NULL);
-	if (b < 0)
-		return b;
-
-	ibm_backlight_device = backlight_device_register("ibm", NULL, NULL,
-							 &ibm_backlight_data);
-	if (IS_ERR(ibm_backlight_device)) {
-		printk(IBM_ERR "Could not register backlight device\n");
-		return PTR_ERR(ibm_backlight_device);
-	}
-
-	ibm_backlight_device->props.max_brightness = 7;
-	ibm_backlight_device->props.brightness = b;
-	backlight_update_status(ibm_backlight_device);
-
-	return 0;
-}
-
-static void brightness_exit(void)
-{
-	if (ibm_backlight_device) {
-		backlight_device_unregister(ibm_backlight_device);
-		ibm_backlight_device = NULL;
-	}
-}
-
-static int volume_offset = 0x30;
-
-static int volume_read(char *p)
-{
-	int len = 0;
-	u8 level;
-
-	if (!acpi_ec_read(volume_offset, &level)) {
-		len += sprintf(p + len, "level:\t\tunreadable\n");
-	} else {
-		len += sprintf(p + len, "level:\t\t%d\n", level & 0xf);
-		len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6));
-		len += sprintf(p + len, "commands:\tup, down, mute\n");
-		len += sprintf(p + len, "commands:\tlevel <level>"
-			       " (<level> is 0-15)\n");
-	}
-
-	return len;
-}
-
-#define VOLUME_DOWN	0
-#define VOLUME_UP	1
-#define VOLUME_MUTE	2
-
-static int volume_write(char *buf)
-{
-	int cmos_cmd, inc, i;
-	u8 level, mute;
-	int new_level, new_mute;
-	char *cmd;
-
-	while ((cmd = next_cmd(&buf))) {
-		if (!acpi_ec_read(volume_offset, &level))
-			return -EIO;
-		new_mute = mute = level & 0x40;
-		new_level = level = level & 0xf;
-
-		if (strlencmp(cmd, "up") == 0) {
-			if (mute)
-				new_mute = 0;
-			else
-				new_level = level == 15 ? 15 : level + 1;
-		} else if (strlencmp(cmd, "down") == 0) {
-			if (mute)
-				new_mute = 0;
-			else
-				new_level = level == 0 ? 0 : level - 1;
-		} else if (sscanf(cmd, "level %d", &new_level) == 1 &&
-			   new_level >= 0 && new_level <= 15) {
-			/* new_level set */
-		} else if (strlencmp(cmd, "mute") == 0) {
-			new_mute = 0x40;
-		} else
-			return -EINVAL;
-
-		if (new_level != level) {	/* mute doesn't change */
-			cmos_cmd = new_level > level ? VOLUME_UP : VOLUME_DOWN;
-			inc = new_level > level ? 1 : -1;
-
-			if (mute && (!cmos_eval(cmos_cmd) ||
-				     !acpi_ec_write(volume_offset, level)))
-				return -EIO;
-
-			for (i = level; i != new_level; i += inc)
-				if (!cmos_eval(cmos_cmd) ||
-				    !acpi_ec_write(volume_offset, i + inc))
-					return -EIO;
-
-			if (mute && (!cmos_eval(VOLUME_MUTE) ||
-				     !acpi_ec_write(volume_offset,
-						    new_level + mute)))
-				return -EIO;
-		}
-
-		if (new_mute != mute) {	/* level doesn't change */
-			cmos_cmd = new_mute ? VOLUME_MUTE : VOLUME_UP;
-
-			if (!cmos_eval(cmos_cmd) ||
-			    !acpi_ec_write(volume_offset, level + new_mute))
-				return -EIO;
-		}
-	}
-
-	return 0;
-}
-
-static enum fan_status_access_mode fan_status_access_mode;
-static enum fan_control_access_mode fan_control_access_mode;
-static enum fan_control_commands fan_control_commands;
-
-static int fan_control_status_known;
-static u8 fan_control_initial_status;
-
-static void fan_watchdog_fire(struct work_struct *ignored);
-static int fan_watchdog_maxinterval;
-static DECLARE_DELAYED_WORK(fan_watchdog_task, fan_watchdog_fire);
-
-static int fan_init(void)
-{
-	fan_status_access_mode = IBMACPI_FAN_NONE;
-	fan_control_access_mode = IBMACPI_FAN_WR_NONE;
-	fan_control_commands = 0;
-	fan_control_status_known = 1;
-	fan_watchdog_maxinterval = 0;
-
-	if (gfan_handle) {
-		/* 570, 600e/x, 770e, 770x */
-		fan_status_access_mode = IBMACPI_FAN_RD_ACPI_GFAN;
-	} else {
-		/* all other ThinkPads: note that even old-style
-		 * ThinkPad ECs supports the fan control register */
-		if (likely(acpi_ec_read(fan_status_offset,
-					&fan_control_initial_status))) {
-			fan_status_access_mode = IBMACPI_FAN_RD_TPEC;
-
-			/* In some ThinkPads, neither the EC nor the ACPI
-			 * DSDT initialize the fan status, and it ends up
-			 * being set to 0x07 when it *could* be either
-			 * 0x07 or 0x80.
-			 *
-			 * Enable for TP-1Y (T43), TP-78 (R51e),
-			 * TP-76 (R52), TP-70 (T43, R52), which are known
-			 * to be buggy. */
-			if (fan_control_initial_status == 0x07 &&
-			    ibm_thinkpad_ec_found &&
-			    ((ibm_thinkpad_ec_found[0] == '1' &&
-			      ibm_thinkpad_ec_found[1] == 'Y') ||
-			     (ibm_thinkpad_ec_found[0] == '7' &&
-			      (ibm_thinkpad_ec_found[1] == '6' ||
-			       ibm_thinkpad_ec_found[1] == '8' ||
-			       ibm_thinkpad_ec_found[1] == '0'))
-			    )) {
-				printk(IBM_NOTICE
-				       "fan_init: initial fan status is "
-				       "unknown, assuming it is in auto "
-				       "mode\n");
-				fan_control_status_known = 0;
-			}
-		} else {
-			printk(IBM_ERR
-			       "ThinkPad ACPI EC access misbehaving, "
-			       "fan status and control unavailable\n");
-			return 0;
-		}
-	}
-
-	if (sfan_handle) {
-		/* 570, 770x-JL */
-		fan_control_access_mode = IBMACPI_FAN_WR_ACPI_SFAN;
-		fan_control_commands |=
-		    IBMACPI_FAN_CMD_LEVEL | IBMACPI_FAN_CMD_ENABLE;
-	} else {
-		if (!gfan_handle) {
-			/* gfan without sfan means no fan control */
-			/* all other models implement TP EC 0x2f control */
-
-			if (fans_handle) {
-				/* X31, X40, X41 */
-				fan_control_access_mode =
-				    IBMACPI_FAN_WR_ACPI_FANS;
-				fan_control_commands |=
-				    IBMACPI_FAN_CMD_SPEED |
-				    IBMACPI_FAN_CMD_LEVEL |
-				    IBMACPI_FAN_CMD_ENABLE;
-			} else {
-				fan_control_access_mode = IBMACPI_FAN_WR_TPEC;
-				fan_control_commands |=
-				    IBMACPI_FAN_CMD_LEVEL |
-				    IBMACPI_FAN_CMD_ENABLE;
-			}
-		}
-	}
-
-	return 0;
-}
-
-static int fan_get_status(u8 *status)
-{
-	u8 s;
-
-	/* TODO:
-	 * Add IBMACPI_FAN_RD_ACPI_FANS ? */
-
-	switch (fan_status_access_mode) {
-	case IBMACPI_FAN_RD_ACPI_GFAN:
-		/* 570, 600e/x, 770e, 770x */
-
-		if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d")))
-			return -EIO;
-
-		if (likely(status))
-			*status = s & 0x07;
-
-		break;
-
-	case IBMACPI_FAN_RD_TPEC:
-		/* all except 570, 600e/x, 770e, 770x */
-		if (unlikely(!acpi_ec_read(fan_status_offset, &s)))
-			return -EIO;
-
-		if (likely(status))
-			*status = s;
-
-		break;
-
-	default:
-		return -ENXIO;
-	}
-
-	return 0;
-}
-
-static int fan_get_speed(unsigned int *speed)
-{
-	u8 hi, lo;
-
-	switch (fan_status_access_mode) {
-	case IBMACPI_FAN_RD_TPEC:
-		/* all except 570, 600e/x, 770e, 770x */
-		if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) ||
-			     !acpi_ec_read(fan_rpm_offset + 1, &hi)))
-			return -EIO;
-
-		if (likely(speed))
-			*speed = (hi << 8) | lo;
-
-		break;
-
-	default:
-		return -ENXIO;
-	}
-
-	return 0;
-}
-
-static void fan_exit(void)
-{
-	cancel_delayed_work(&fan_watchdog_task);
-	flush_scheduled_work();
-}
-
-static void fan_watchdog_reset(void)
-{
-	static int fan_watchdog_active = 0;
-
-	if (fan_watchdog_active)
-		cancel_delayed_work(&fan_watchdog_task);
-
-	if (fan_watchdog_maxinterval > 0) {
-		fan_watchdog_active = 1;
-		if (!schedule_delayed_work(&fan_watchdog_task,
-				msecs_to_jiffies(fan_watchdog_maxinterval
-						 * 1000))) {
-			printk(IBM_ERR "failed to schedule the fan watchdog, "
-			       "watchdog will not trigger\n");
-		}
-	} else
-		fan_watchdog_active = 0;
-}
-
-static int fan_read(char *p)
-{
-	int len = 0;
-	int rc;
-	u8 status;
-	unsigned int speed = 0;
-
-	switch (fan_status_access_mode) {
-	case IBMACPI_FAN_RD_ACPI_GFAN:
-		/* 570, 600e/x, 770e, 770x */
-		if ((rc = fan_get_status(&status)) < 0)
-			return rc;
-
-		len += sprintf(p + len, "status:\t\t%s\n"
-			       "level:\t\t%d\n",
-			       (status != 0) ? "enabled" : "disabled", status);
-		break;
-
-	case IBMACPI_FAN_RD_TPEC:
-		/* all except 570, 600e/x, 770e, 770x */
-		if ((rc = fan_get_status(&status)) < 0)
-			return rc;
-
-		if (unlikely(!fan_control_status_known)) {
-			if (status != fan_control_initial_status)
-				fan_control_status_known = 1;
-			else
-				/* Return most likely status. In fact, it
-				 * might be the only possible status */
-				status = IBMACPI_FAN_EC_AUTO;
-		}
-
-		len += sprintf(p + len, "status:\t\t%s\n",
-			       (status != 0) ? "enabled" : "disabled");
-
-		/* No ThinkPad boots on disengaged mode, we can safely
-		 * assume the tachometer is online if fan control status
-		 * was unknown */
-		if ((rc = fan_get_speed(&speed)) < 0)
-			return rc;
-
-		len += sprintf(p + len, "speed:\t\t%d\n", speed);
-
-		if (status & IBMACPI_FAN_EC_DISENGAGED)
-			/* Disengaged mode takes precedence */
-			len += sprintf(p + len, "level:\t\tdisengaged\n");
-		else if (status & IBMACPI_FAN_EC_AUTO)
-			len += sprintf(p + len, "level:\t\tauto\n");
-		else
-			len += sprintf(p + len, "level:\t\t%d\n", status);
-		break;
-
-	case IBMACPI_FAN_NONE:
-	default:
-		len += sprintf(p + len, "status:\t\tnot supported\n");
-	}
-
-	if (fan_control_commands & IBMACPI_FAN_CMD_LEVEL) {
-		len += sprintf(p + len, "commands:\tlevel <level>");
-
-		switch (fan_control_access_mode) {
-		case IBMACPI_FAN_WR_ACPI_SFAN:
-			len += sprintf(p + len, " (<level> is 0-7)\n");
-			break;
-
-		default:
-			len += sprintf(p + len, " (<level> is 0-7, "
-				       "auto, disengaged)\n");
-			break;
-		}
-	}
-
-	if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE)
-		len += sprintf(p + len, "commands:\tenable, disable\n"
-			       "commands:\twatchdog <timeout> (<timeout> is 0 (off), "
-			       "1-120 (seconds))\n");
-
-	if (fan_control_commands & IBMACPI_FAN_CMD_SPEED)
-		len += sprintf(p + len, "commands:\tspeed <speed>"
-			       " (<speed> is 0-65535)\n");
-
-	return len;
-}
-
-static int fan_set_level(int level)
-{
-	switch (fan_control_access_mode) {
-	case IBMACPI_FAN_WR_ACPI_SFAN:
-		if (level >= 0 && level <= 7) {
-			if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
-				return -EIO;
-		} else
-			return -EINVAL;
-		break;
-
-	case IBMACPI_FAN_WR_ACPI_FANS:
-	case IBMACPI_FAN_WR_TPEC:
-		if ((level != IBMACPI_FAN_EC_AUTO) &&
-		    (level != IBMACPI_FAN_EC_DISENGAGED) &&
-		    ((level < 0) || (level > 7)))
-			return -EINVAL;
-
-		if (!acpi_ec_write(fan_status_offset, level))
-			return -EIO;
-		else
-			fan_control_status_known = 1;
-		break;
-
-	default:
-		return -ENXIO;
-	}
-	return 0;
-}
-
-static int fan_set_enable(void)
-{
-	u8 s;
-	int rc;
-
-	switch (fan_control_access_mode) {
-	case IBMACPI_FAN_WR_ACPI_FANS:
-	case IBMACPI_FAN_WR_TPEC:
-		if ((rc = fan_get_status(&s)) < 0)
-			return rc;
-
-		/* Don't go out of emergency fan mode */
-		if (s != 7)
-			s = IBMACPI_FAN_EC_AUTO;
-
-		if (!acpi_ec_write(fan_status_offset, s))
-			return -EIO;
-		else
-			fan_control_status_known = 1;
-		break;
-
-	case IBMACPI_FAN_WR_ACPI_SFAN:
-		if ((rc = fan_get_status(&s)) < 0)
-			return rc;
-
-		s &= 0x07;
-
-		/* Set fan to at least level 4 */
-		if (s < 4)
-			s = 4;
-
-		if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s))
-			return -EIO;
-		break;
-
-	default:
-		return -ENXIO;
-	}
-	return 0;
-}
-
-static int fan_set_disable(void)
-{
-	switch (fan_control_access_mode) {
-	case IBMACPI_FAN_WR_ACPI_FANS:
-	case IBMACPI_FAN_WR_TPEC:
-		if (!acpi_ec_write(fan_status_offset, 0x00))
-			return -EIO;
-		else
-			fan_control_status_known = 1;
-		break;
-
-	case IBMACPI_FAN_WR_ACPI_SFAN:
-		if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00))
-			return -EIO;
-		break;
-
-	default:
-		return -ENXIO;
-	}
-	return 0;
-}
-
-static int fan_set_speed(int speed)
-{
-	switch (fan_control_access_mode) {
-	case IBMACPI_FAN_WR_ACPI_FANS:
-		if (speed >= 0 && speed <= 65535) {
-			if (!acpi_evalf(fans_handle, NULL, NULL, "vddd",
-					speed, speed, speed))
-				return -EIO;
-		} else
-			return -EINVAL;
-		break;
-
-	default:
-		return -ENXIO;
-	}
-	return 0;
-}
-
-static int fan_write_cmd_level(const char *cmd, int *rc)
-{
-	int level;
-
-	if (strlencmp(cmd, "level auto") == 0)
-		level = IBMACPI_FAN_EC_AUTO;
-	else if (strlencmp(cmd, "level disengaged") == 0)
-		level = IBMACPI_FAN_EC_DISENGAGED;
-	else if (sscanf(cmd, "level %d", &level) != 1)
-		return 0;
-
-	if ((*rc = fan_set_level(level)) == -ENXIO)
-		printk(IBM_ERR "level command accepted for unsupported "
-		       "access mode %d", fan_control_access_mode);
-
-	return 1;
-}
-
-static int fan_write_cmd_enable(const char *cmd, int *rc)
-{
-	if (strlencmp(cmd, "enable") != 0)
-		return 0;
-
-	if ((*rc = fan_set_enable()) == -ENXIO)
-		printk(IBM_ERR "enable command accepted for unsupported "
-		       "access mode %d", fan_control_access_mode);
-
-	return 1;
-}
-
-static int fan_write_cmd_disable(const char *cmd, int *rc)
-{
-	if (strlencmp(cmd, "disable") != 0)
-		return 0;
-
-	if ((*rc = fan_set_disable()) == -ENXIO)
-		printk(IBM_ERR "disable command accepted for unsupported "
-		       "access mode %d", fan_control_access_mode);
-
-	return 1;
-}
-
-static int fan_write_cmd_speed(const char *cmd, int *rc)
-{
-	int speed;
-
-	/* TODO:
-	 * Support speed <low> <medium> <high> ? */
-
-	if (sscanf(cmd, "speed %d", &speed) != 1)
-		return 0;
-
-	if ((*rc = fan_set_speed(speed)) == -ENXIO)
-		printk(IBM_ERR "speed command accepted for unsupported "
-		       "access mode %d", fan_control_access_mode);
-
-	return 1;
-}
-
-static int fan_write_cmd_watchdog(const char *cmd, int *rc)
-{
-	int interval;
-
-	if (sscanf(cmd, "watchdog %d", &interval) != 1)
-		return 0;
-
-	if (interval < 0 || interval > 120)
-		*rc = -EINVAL;
-	else
-		fan_watchdog_maxinterval = interval;
-
-	return 1;
-}
-
-static int fan_write(char *buf)
-{
-	char *cmd;
-	int rc = 0;
-
-	while (!rc && (cmd = next_cmd(&buf))) {
-		if (!((fan_control_commands & IBMACPI_FAN_CMD_LEVEL) &&
-		      fan_write_cmd_level(cmd, &rc)) &&
-		    !((fan_control_commands & IBMACPI_FAN_CMD_ENABLE) &&
-		      (fan_write_cmd_enable(cmd, &rc) ||
-		       fan_write_cmd_disable(cmd, &rc) ||
-		       fan_write_cmd_watchdog(cmd, &rc))) &&
-		    !((fan_control_commands & IBMACPI_FAN_CMD_SPEED) &&
-		      fan_write_cmd_speed(cmd, &rc))
-		    )
-			rc = -EINVAL;
-		else if (!rc)
-			fan_watchdog_reset();
-	}
-
-	return rc;
-}
-
-static void fan_watchdog_fire(struct work_struct *ignored)
-{
-	printk(IBM_NOTICE "fan watchdog: enabling fan\n");
-	if (fan_set_enable()) {
-		printk(IBM_ERR "fan watchdog: error while enabling fan\n");
-		/* reschedule for later */
-		fan_watchdog_reset();
-	}
-}
-
-static struct ibm_struct ibms[] = {
-	{
-	 .name = "driver",
-	 .init = ibm_acpi_driver_init,
-	 .read = driver_read,
-	 },
-	{
-	 .name = "hotkey",
-	 .hid = IBM_HKEY_HID,
-	 .init = hotkey_init,
-	 .read = hotkey_read,
-	 .write = hotkey_write,
-	 .exit = hotkey_exit,
-	 .notify = hotkey_notify,
-	 .handle = &hkey_handle,
-	 .type = ACPI_DEVICE_NOTIFY,
-	 },
-	{
-	 .name = "bluetooth",
-	 .init = bluetooth_init,
-	 .read = bluetooth_read,
-	 .write = bluetooth_write,
-	 },
-	{
-	 .name = "wan",
-	 .init = wan_init,
-	 .read = wan_read,
-	 .write = wan_write,
-	 .experimental = 1,
-	 },
-	{
-	 .name = "video",
-	 .init = video_init,
-	 .read = video_read,
-	 .write = video_write,
-	 .exit = video_exit,
-	 },
-	{
-	 .name = "light",
-	 .init = light_init,
-	 .read = light_read,
-	 .write = light_write,
-	 },
-#ifdef CONFIG_ACPI_IBM_DOCK
-	{
-	 .name = "dock",
-	 .read = dock_read,
-	 .write = dock_write,
-	 .notify = dock_notify,
-	 .handle = &dock_handle,
-	 .type = ACPI_SYSTEM_NOTIFY,
-	 },
-	{
-	 .name = "dock",
-	 .hid = IBM_PCI_HID,
-	 .notify = dock_notify,
-	 .handle = &pci_handle,
-	 .type = ACPI_SYSTEM_NOTIFY,
-	 },
-#endif
-#ifdef CONFIG_ACPI_IBM_BAY
-	{
-	 .name = "bay",
-	 .init = bay_init,
-	 .read = bay_read,
-	 .write = bay_write,
-	 .notify = bay_notify,
-	 .handle = &bay_handle,
-	 .type = ACPI_SYSTEM_NOTIFY,
-	 },
-#endif /* CONFIG_ACPI_IBM_BAY */
-	{
-	 .name = "cmos",
-	 .read = cmos_read,
-	 .write = cmos_write,
-	 },
-	{
-	 .name = "led",
-	 .init = led_init,
-	 .read = led_read,
-	 .write = led_write,
-	 },
-	{
-	 .name = "beep",
-	 .read = beep_read,
-	 .write = beep_write,
-	 },
-	{
-	 .name = "thermal",
-	 .init = thermal_init,
-	 .read = thermal_read,
-	 },
-	{
-	 .name = "ecdump",
-	 .read = ecdump_read,
-	 .write = ecdump_write,
-	 .experimental = 1,
-	 },
-	{
-	 .name = "brightness",
-	 .read = brightness_read,
-	 .write = brightness_write,
-	 .init = brightness_init,
-	 .exit = brightness_exit,
-	 },
-	{
-	 .name = "volume",
-	 .read = volume_read,
-	 .write = volume_write,
-	 },
-	{
-	 .name = "fan",
-	 .read = fan_read,
-	 .write = fan_write,
-	 .init = fan_init,
-	 .exit = fan_exit,
-	 .experimental = 1,
-	 },
-};
-
-static int dispatch_read(char *page, char **start, off_t off, int count,
-			 int *eof, void *data)
-{
-	struct ibm_struct *ibm = data;
-	int len;
-
-	if (!ibm || !ibm->read)
-		return -EINVAL;
-
-	len = ibm->read(page);
-	if (len < 0)
-		return len;
-
-	if (len <= off + count)
-		*eof = 1;
-	*start = page + off;
-	len -= off;
-	if (len > count)
-		len = count;
-	if (len < 0)
-		len = 0;
-
-	return len;
-}
-
-static int dispatch_write(struct file *file, const char __user * userbuf,
-			  unsigned long count, void *data)
-{
-	struct ibm_struct *ibm = data;
-	char *kernbuf;
-	int ret;
-
-	if (!ibm || !ibm->write)
-		return -EINVAL;
-
-	kernbuf = kmalloc(count + 2, GFP_KERNEL);
-	if (!kernbuf)
-		return -ENOMEM;
-
-	if (copy_from_user(kernbuf, userbuf, count)) {
-		kfree(kernbuf);
-		return -EFAULT;
-	}
-
-	kernbuf[count] = 0;
-	strcat(kernbuf, ",");
-	ret = ibm->write(kernbuf);
-	if (ret == 0)
-		ret = count;
-
-	kfree(kernbuf);
-
-	return ret;
-}
-
-static void dispatch_notify(acpi_handle handle, u32 event, void *data)
-{
-	struct ibm_struct *ibm = data;
-
-	if (!ibm || !ibm->notify)
-		return;
-
-	ibm->notify(ibm, event);
-}
-
-static int __init setup_notify(struct ibm_struct *ibm)
-{
-	acpi_status status;
-	int ret;
-
-	if (!*ibm->handle)
-		return 0;
-
-	ret = acpi_bus_get_device(*ibm->handle, &ibm->device);
-	if (ret < 0) {
-		printk(IBM_ERR "%s device not present\n", ibm->name);
-		return -ENODEV;
-	}
-
-	acpi_driver_data(ibm->device) = ibm;
-	sprintf(acpi_device_class(ibm->device), "%s/%s", IBM_NAME, ibm->name);
-
-	status = acpi_install_notify_handler(*ibm->handle, ibm->type,
-					     dispatch_notify, ibm);
-	if (ACPI_FAILURE(status)) {
-		if (status == AE_ALREADY_EXISTS) {
-			printk(IBM_NOTICE "another device driver is already handling %s events\n",
-				ibm->name);
-		} else {
-			printk(IBM_ERR "acpi_install_notify_handler(%s) failed: %d\n",
-				ibm->name, status);
-		}
-		return -ENODEV;
-	}
-	ibm->notify_installed = 1;
-	return 0;
-}
-
-static int __init ibm_device_add(struct acpi_device *device)
-{
-	return 0;
-}
-
-static int __init register_driver(struct ibm_struct *ibm)
-{
-	int ret;
-
-	ibm->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL);
-	if (!ibm->driver) {
-		printk(IBM_ERR "kmalloc(ibm->driver) failed\n");
-		return -1;
-	}
-
-	sprintf(ibm->driver->name, "%s_%s", IBM_NAME, ibm->name);
-	ibm->driver->ids = ibm->hid;
-	ibm->driver->ops.add = &ibm_device_add;
-
-	ret = acpi_bus_register_driver(ibm->driver);
-	if (ret < 0) {
-		printk(IBM_ERR "acpi_bus_register_driver(%s) failed: %d\n",
-		       ibm->hid, ret);
-		kfree(ibm->driver);
-	}
-
-	return ret;
-}
-
-static void ibm_exit(struct ibm_struct *ibm);
-
-static int __init ibm_init(struct ibm_struct *ibm)
-{
-	int ret;
-	struct proc_dir_entry *entry;
-
-	if (ibm->experimental && !experimental)
-		return 0;
-
-	if (ibm->hid) {
-		ret = register_driver(ibm);
-		if (ret < 0)
-			return ret;
-		ibm->driver_registered = 1;
-	}
-
-	if (ibm->init) {
-		ret = ibm->init();
-		if (ret != 0)
-			return ret;
-		ibm->init_called = 1;
-	}
-
-	if (ibm->read) {
-		entry = create_proc_entry(ibm->name,
-					  S_IFREG | S_IRUGO | S_IWUSR,
-					  proc_dir);
-		if (!entry) {
-			printk(IBM_ERR "unable to create proc entry %s\n",
-			       ibm->name);
-			return -ENODEV;
-		}
-		entry->owner = THIS_MODULE;
-		entry->data = ibm;
-		entry->read_proc = &dispatch_read;
-		if (ibm->write)
-			entry->write_proc = &dispatch_write;
-		ibm->proc_created = 1;
-	}
-
-	if (ibm->notify) {
-		ret = setup_notify(ibm);
-		if (ret == -ENODEV) {
-			printk(IBM_NOTICE "disabling subdriver %s\n",
-				ibm->name);
-			ibm_exit(ibm);
-			return 0;
-		}
-		if (ret < 0)
-			return ret;
-	}
-
-	return 0;
-}
-
-static void ibm_exit(struct ibm_struct *ibm)
-{
-	if (ibm->notify_installed)
-		acpi_remove_notify_handler(*ibm->handle, ibm->type,
-					   dispatch_notify);
-
-	if (ibm->proc_created)
-		remove_proc_entry(ibm->name, proc_dir);
-
-	if (ibm->init_called && ibm->exit)
-		ibm->exit();
-
-	if (ibm->driver_registered) {
-		acpi_bus_unregister_driver(ibm->driver);
-		kfree(ibm->driver);
-	}
-}
-
-static void __init ibm_handle_init(char *name,
-				   acpi_handle * handle, acpi_handle parent,
-				   char **paths, int num_paths, char **path)
-{
-	int i;
-	acpi_status status;
-
-	for (i = 0; i < num_paths; i++) {
-		status = acpi_get_handle(parent, paths[i], handle);
-		if (ACPI_SUCCESS(status)) {
-			*path = paths[i];
-			return;
-		}
-	}
-
-	*handle = NULL;
-}
-
-#define IBM_HANDLE_INIT(object)						\
-	ibm_handle_init(#object, &object##_handle, *object##_parent,	\
-		object##_paths, ARRAY_SIZE(object##_paths), &object##_path)
-
-static int __init set_ibm_param(const char *val, struct kernel_param *kp)
-{
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(ibms); i++)
-		if (strcmp(ibms[i].name, kp->name) == 0 && ibms[i].write) {
-			if (strlen(val) > sizeof(ibms[i].param) - 2)
-				return -ENOSPC;
-			strcpy(ibms[i].param, val);
-			strcat(ibms[i].param, ",");
-			return 0;
-		}
-
-	return -EINVAL;
-}
-
-#define IBM_PARAM(feature) \
-	module_param_call(feature, set_ibm_param, NULL, NULL, 0)
-
-IBM_PARAM(hotkey);
-IBM_PARAM(bluetooth);
-IBM_PARAM(video);
-IBM_PARAM(light);
-#ifdef CONFIG_ACPI_IBM_DOCK
-IBM_PARAM(dock);
-#endif
-#ifdef CONFIG_ACPI_IBM_BAY
-IBM_PARAM(bay);
-#endif /* CONFIG_ACPI_IBM_BAY */
-IBM_PARAM(cmos);
-IBM_PARAM(led);
-IBM_PARAM(beep);
-IBM_PARAM(ecdump);
-IBM_PARAM(brightness);
-IBM_PARAM(volume);
-IBM_PARAM(fan);
-
-static void acpi_ibm_exit(void)
-{
-	int i;
-
-	for (i = ARRAY_SIZE(ibms) - 1; i >= 0; i--)
-		ibm_exit(&ibms[i]);
-
-	if (proc_dir)
-		remove_proc_entry(IBM_DIR, acpi_root_dir);
-
-	if (ibm_thinkpad_ec_found)
-		kfree(ibm_thinkpad_ec_found);
-}
-
-static char* __init check_dmi_for_ec(void)
-{
-	struct dmi_device *dev = NULL;
-	char ec_fw_string[18];
-
-	/*
-	 * ThinkPad T23 or newer, A31 or newer, R50e or newer,
-	 * X32 or newer, all Z series;  Some models must have an
-	 * up-to-date BIOS or they will not be detected.
-	 *
-	 * See http://thinkwiki.org/wiki/List_of_DMI_IDs
-	 */
-	while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
-		if (sscanf(dev->name,
-			   "IBM ThinkPad Embedded Controller -[%17c",
-			   ec_fw_string) == 1) {
-			ec_fw_string[sizeof(ec_fw_string) - 1] = 0;
-			ec_fw_string[strcspn(ec_fw_string, " ]")] = 0;
-			return kstrdup(ec_fw_string, GFP_KERNEL);
-		}
-	}
-	return NULL;
-}
-
-static int __init acpi_ibm_init(void)
-{
-	int ret, i;
-
-	if (acpi_disabled)
-		return -ENODEV;
-
-	/* ec is required because many other handles are relative to it */
-	IBM_HANDLE_INIT(ec);
-	if (!ec_handle) {
-		printk(IBM_ERR "ec object not found\n");
-		return -ENODEV;
-	}
-
-	/* Models with newer firmware report the EC in DMI */
-	ibm_thinkpad_ec_found = check_dmi_for_ec();
-
-	/* these handles are not required */
-	IBM_HANDLE_INIT(vid);
-	IBM_HANDLE_INIT(vid2);
-	IBM_HANDLE_INIT(ledb);
-	IBM_HANDLE_INIT(led);
-	IBM_HANDLE_INIT(hkey);
-	IBM_HANDLE_INIT(lght);
-	IBM_HANDLE_INIT(cmos);
-#ifdef CONFIG_ACPI_IBM_DOCK
-	IBM_HANDLE_INIT(dock);
-#endif
-	IBM_HANDLE_INIT(pci);
-#ifdef CONFIG_ACPI_IBM_BAY
-	IBM_HANDLE_INIT(bay);
-	if (bay_handle)
-		IBM_HANDLE_INIT(bay_ej);
-	IBM_HANDLE_INIT(bay2);
-	if (bay2_handle)
-		IBM_HANDLE_INIT(bay2_ej);
-#endif /* CONFIG_ACPI_IBM_BAY */
-	IBM_HANDLE_INIT(beep);
-	IBM_HANDLE_INIT(ecrd);
-	IBM_HANDLE_INIT(ecwr);
-	IBM_HANDLE_INIT(fans);
-	IBM_HANDLE_INIT(gfan);
-	IBM_HANDLE_INIT(sfan);
-
-	proc_dir = proc_mkdir(IBM_DIR, acpi_root_dir);
-	if (!proc_dir) {
-		printk(IBM_ERR "unable to create proc dir %s", IBM_DIR);
-		acpi_ibm_exit();
-		return -ENODEV;
-	}
-	proc_dir->owner = THIS_MODULE;
-
-	for (i = 0; i < ARRAY_SIZE(ibms); i++) {
-		ret = ibm_init(&ibms[i]);
-		if (ret >= 0 && *ibms[i].param)
-			ret = ibms[i].write(ibms[i].param);
-		if (ret < 0) {
-			acpi_ibm_exit();
-			return ret;
-		}
-	}
-
-	return 0;
-}
-
-module_init(acpi_ibm_init);
-module_exit(acpi_ibm_exit);
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
index 99d1516..f7de02a 100644
--- a/drivers/acpi/processor_core.c
+++ b/drivers/acpi/processor_core.c
@@ -70,8 +70,6 @@
 #define ACPI_PROCESSOR_LIMIT_USER	0
 #define ACPI_PROCESSOR_LIMIT_THERMAL	1
 
-#define ACPI_STA_PRESENT 0x00000001
-
 #define _COMPONENT		ACPI_PROCESSOR_COMPONENT
 ACPI_MODULE_NAME("processor_core");
 
@@ -779,7 +777,7 @@
 
 
 	status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
-	if (ACPI_FAILURE(status) || !(sta & ACPI_STA_PRESENT)) {
+	if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT)) {
 		ACPI_EXCEPTION((AE_INFO, status, "Processor Device is not present"));
 		return 0;
 	}
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index cdf7894..ae0654c 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -51,14 +51,6 @@
 #include <asm/apic.h>
 #endif
 
-/*
- * Include the apic definitions for x86 to have the APIC timer related defines
- * available also for UP (on SMP it gets magically included via linux/smp.h).
- */
-#ifdef CONFIG_X86
-#include <asm/apic.h>
-#endif
-
 #include <asm/io.h>
 #include <asm/uaccess.h>
 
diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c
index 59640d9..c1bae10 100644
--- a/drivers/acpi/sbs.c
+++ b/drivers/acpi/sbs.c
@@ -30,30 +30,10 @@
 #include <linux/seq_file.h>
 #include <asm/uaccess.h>
 #include <linux/acpi.h>
-#include <linux/i2c.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
 #include <linux/delay.h>
 
-#include "i2c_ec.h"
-
-#define	DEF_CAPACITY_UNIT	3
-#define	MAH_CAPACITY_UNIT	1
-#define	MWH_CAPACITY_UNIT	2
-#define	CAPACITY_UNIT		DEF_CAPACITY_UNIT
-
-#define	REQUEST_UPDATE_MODE	1
-#define	QUEUE_UPDATE_MODE	2
-
-#define	DATA_TYPE_COMMON	0
-#define	DATA_TYPE_INFO		1
-#define	DATA_TYPE_STATE		2
-#define	DATA_TYPE_ALARM		3
-#define	DATA_TYPE_AC_STATE	4
-
-extern struct proc_dir_entry *acpi_lock_ac_dir(void);
-extern struct proc_dir_entry *acpi_lock_battery_dir(void);
-extern void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir);
-extern void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);
-
 #define ACPI_SBS_COMPONENT		0x00080000
 #define ACPI_SBS_CLASS			"sbs"
 #define ACPI_AC_CLASS			"ac_adapter"
@@ -74,39 +54,75 @@
 
 #define _COMPONENT			ACPI_SBS_COMPONENT
 
-#define	MAX_SBS_BAT			4
-#define	MAX_SMBUS_ERR			1
-
 ACPI_MODULE_NAME("sbs");
 
 MODULE_AUTHOR("Rich Townsend");
 MODULE_DESCRIPTION("Smart Battery System ACPI interface driver");
 MODULE_LICENSE("GPL");
 
-static struct semaphore sbs_sem;
+#define	xmsleep(t)	msleep(t)
 
-#define	UPDATE_MODE		QUEUE_UPDATE_MODE
-/* REQUEST_UPDATE_MODE  QUEUE_UPDATE_MODE */
-#define	UPDATE_INFO_MODE	0
-#define	UPDATE_TIME		60
-#define	UPDATE_TIME2		0
+#define ACPI_EC_SMB_PRTCL	0x00	/* protocol, PEC */
 
-static int capacity_mode = CAPACITY_UNIT;
-static int update_mode = UPDATE_MODE;
-static int update_info_mode = UPDATE_INFO_MODE;
-static int update_time = UPDATE_TIME;
-static int update_time2 = UPDATE_TIME2;
+#define ACPI_EC_SMB_STS		0x01	/* status */
+#define ACPI_EC_SMB_ADDR	0x02	/* address */
+#define ACPI_EC_SMB_CMD		0x03	/* command */
+#define ACPI_EC_SMB_DATA	0x04	/* 32 data registers */
+#define ACPI_EC_SMB_BCNT	0x24	/* number of data bytes */
 
-module_param(capacity_mode, int, 0);
-module_param(update_mode, int, 0);
-module_param(update_info_mode, int, 0);
-module_param(update_time, int, 0);
-module_param(update_time2, int, 0);
+#define ACPI_EC_SMB_STS_DONE	0x80
+#define ACPI_EC_SMB_STS_STATUS	0x1f
+
+#define ACPI_EC_SMB_PRTCL_WRITE		0x00
+#define ACPI_EC_SMB_PRTCL_READ		0x01
+#define ACPI_EC_SMB_PRTCL_WORD_DATA	0x08
+#define ACPI_EC_SMB_PRTCL_BLOCK_DATA	0x0a
+
+#define ACPI_EC_SMB_TRANSACTION_SLEEP	1
+#define ACPI_EC_SMB_ACCESS_SLEEP1	1
+#define ACPI_EC_SMB_ACCESS_SLEEP2	10
+
+#define	DEF_CAPACITY_UNIT	3
+#define	MAH_CAPACITY_UNIT	1
+#define	MWH_CAPACITY_UNIT	2
+#define	CAPACITY_UNIT		DEF_CAPACITY_UNIT
+
+#define	REQUEST_UPDATE_MODE	1
+#define	QUEUE_UPDATE_MODE	2
+
+#define	DATA_TYPE_COMMON	0
+#define	DATA_TYPE_INFO		1
+#define	DATA_TYPE_STATE		2
+#define	DATA_TYPE_ALARM		3
+#define	DATA_TYPE_AC_STATE	4
+
+extern struct proc_dir_entry *acpi_lock_ac_dir(void);
+extern struct proc_dir_entry *acpi_lock_battery_dir(void);
+extern void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir);
+extern void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);
+
+#define	MAX_SBS_BAT			4
+#define ACPI_SBS_BLOCK_MAX		32
+
+#define ACPI_SBS_SMBUS_READ		1
+#define ACPI_SBS_SMBUS_WRITE		2
+
+#define ACPI_SBS_WORD_DATA		1
+#define ACPI_SBS_BLOCK_DATA		2
+
+#define	UPDATE_DELAY	10
+
+/* 0 - every time, > 0 - by update_time */
+static unsigned int update_time = 120;
+
+static unsigned int capacity_mode = CAPACITY_UNIT;
+
+module_param(update_time, uint, 0644);
+module_param(capacity_mode, uint, 0444);
 
 static int acpi_sbs_add(struct acpi_device *device);
 static int acpi_sbs_remove(struct acpi_device *device, int type);
-static void acpi_battery_smbus_err_handler(struct acpi_ec_smbus *smbus);
-static void acpi_sbs_update_queue(void *data);
+static int acpi_sbs_resume(struct acpi_device *device);
 
 static struct acpi_driver acpi_sbs_driver = {
 	.name = "sbs",
@@ -115,9 +131,14 @@
 	.ops = {
 		.add = acpi_sbs_add,
 		.remove = acpi_sbs_remove,
+		.resume = acpi_sbs_resume,
 		},
 };
 
+struct acpi_ac {
+	int ac_present;
+};
+
 struct acpi_battery_info {
 	int capacity_mode;
 	s16 full_charge_capacity;
@@ -126,18 +147,16 @@
 	int vscale;
 	int ipscale;
 	s16 serial_number;
-	char manufacturer_name[I2C_SMBUS_BLOCK_MAX + 3];
-	char device_name[I2C_SMBUS_BLOCK_MAX + 3];
-	char device_chemistry[I2C_SMBUS_BLOCK_MAX + 3];
+	char manufacturer_name[ACPI_SBS_BLOCK_MAX + 3];
+	char device_name[ACPI_SBS_BLOCK_MAX + 3];
+	char device_chemistry[ACPI_SBS_BLOCK_MAX + 3];
 };
 
 struct acpi_battery_state {
 	s16 voltage;
 	s16 amperage;
 	s16 remaining_capacity;
-	s16 average_time_to_empty;
-	s16 average_time_to_full;
-	s16 battery_status;
+	s16 battery_state;
 };
 
 struct acpi_battery_alarm {
@@ -146,9 +165,9 @@
 
 struct acpi_battery {
 	int alive;
-	int battery_present;
 	int id;
 	int init_state;
+	int battery_present;
 	struct acpi_sbs *sbs;
 	struct acpi_battery_info info;
 	struct acpi_battery_state state;
@@ -158,186 +177,251 @@
 
 struct acpi_sbs {
 	acpi_handle handle;
+	int base;
 	struct acpi_device *device;
 	struct acpi_ec_smbus *smbus;
+	struct mutex mutex;
 	int sbsm_present;
 	int sbsm_batteries_supported;
-	int ac_present;
 	struct proc_dir_entry *ac_entry;
+	struct acpi_ac ac;
 	struct acpi_battery battery[MAX_SBS_BAT];
-	int update_info_mode;
 	int zombie;
-	int update_time;
-	int update_time2;
 	struct timer_list update_timer;
+	int run_cnt;
+	int update_proc_flg;
 };
 
-static void acpi_update_delay(struct acpi_sbs *sbs);
-static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type);
+static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type);
+static void acpi_sbs_update_time(void *data);
+
+union sbs_rw_data {
+	u16 word;
+	u8 block[ACPI_SBS_BLOCK_MAX + 2];
+};
+
+static int acpi_ec_sbs_access(struct acpi_sbs *sbs, u16 addr,
+			      char read_write, u8 command, int size,
+			      union sbs_rw_data *data);
 
 /* --------------------------------------------------------------------------
                                SMBus Communication
    -------------------------------------------------------------------------- */
 
-static void acpi_battery_smbus_err_handler(struct acpi_ec_smbus *smbus)
+static int acpi_ec_sbs_read(struct acpi_sbs *sbs, u8 address, u8 * data)
 {
-	union i2c_smbus_data data;
-	int result = 0;
-	char *err_str;
-	int err_number;
+	u8 val;
+	int err;
 
-	data.word = 0;
+	err = ec_read(sbs->base + address, &val);
+	if (!err) {
+		*data = val;
+	}
+	xmsleep(ACPI_EC_SMB_TRANSACTION_SLEEP);
+	return (err);
+}
 
-	result = smbus->adapter.algo->
-	    smbus_xfer(&smbus->adapter,
-		       ACPI_SB_SMBUS_ADDR,
-		       0, I2C_SMBUS_READ, 0x16, I2C_SMBUS_BLOCK_DATA, &data);
+static int acpi_ec_sbs_write(struct acpi_sbs *sbs, u8 address, u8 data)
+{
+	int err;
 
-	err_number = (data.word & 0x000f);
+	err = ec_write(sbs->base + address, data);
+	return (err);
+}
 
-	switch (data.word & 0x000f) {
-	case 0x0000:
-		err_str = "unexpected bus error";
+static int
+acpi_ec_sbs_access(struct acpi_sbs *sbs, u16 addr,
+		   char read_write, u8 command, int size,
+		   union sbs_rw_data *data)
+{
+	unsigned char protocol, len = 0, temp[2] = { 0, 0 };
+	int i;
+
+	if (read_write == ACPI_SBS_SMBUS_READ) {
+		protocol = ACPI_EC_SMB_PRTCL_READ;
+	} else {
+		protocol = ACPI_EC_SMB_PRTCL_WRITE;
+	}
+
+	switch (size) {
+
+	case ACPI_SBS_WORD_DATA:
+		acpi_ec_sbs_write(sbs, ACPI_EC_SMB_CMD, command);
+		if (read_write == ACPI_SBS_SMBUS_WRITE) {
+			acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA, data->word);
+			acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA + 1,
+					  data->word >> 8);
+		}
+		protocol |= ACPI_EC_SMB_PRTCL_WORD_DATA;
 		break;
-	case 0x0001:
-		err_str = "busy";
-		break;
-	case 0x0002:
-		err_str = "reserved command";
-		break;
-	case 0x0003:
-		err_str = "unsupported command";
-		break;
-	case 0x0004:
-		err_str = "access denied";
-		break;
-	case 0x0005:
-		err_str = "overflow/underflow";
-		break;
-	case 0x0006:
-		err_str = "bad size";
-		break;
-	case 0x0007:
-		err_str = "unknown error";
+	case ACPI_SBS_BLOCK_DATA:
+		acpi_ec_sbs_write(sbs, ACPI_EC_SMB_CMD, command);
+		if (read_write == ACPI_SBS_SMBUS_WRITE) {
+			len = min_t(u8, data->block[0], 32);
+			acpi_ec_sbs_write(sbs, ACPI_EC_SMB_BCNT, len);
+			for (i = 0; i < len; i++)
+				acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA + i,
+						  data->block[i + 1]);
+		}
+		protocol |= ACPI_EC_SMB_PRTCL_BLOCK_DATA;
 		break;
 	default:
-		err_str = "unrecognized error";
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"unsupported transaction %d", size));
+		return (-1);
 	}
-	ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-			  "%s: ret %i, err %i\n", err_str, result, err_number));
+
+	acpi_ec_sbs_write(sbs, ACPI_EC_SMB_ADDR, addr << 1);
+	acpi_ec_sbs_write(sbs, ACPI_EC_SMB_PRTCL, protocol);
+
+	acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp);
+
+	if (~temp[0] & ACPI_EC_SMB_STS_DONE) {
+		xmsleep(ACPI_EC_SMB_ACCESS_SLEEP1);
+		acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp);
+	}
+	if (~temp[0] & ACPI_EC_SMB_STS_DONE) {
+		xmsleep(ACPI_EC_SMB_ACCESS_SLEEP2);
+		acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp);
+	}
+	if ((~temp[0] & ACPI_EC_SMB_STS_DONE)
+	    || (temp[0] & ACPI_EC_SMB_STS_STATUS)) {
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"transaction %d error", size));
+		return (-1);
+	}
+
+	if (read_write == ACPI_SBS_SMBUS_WRITE) {
+		return (0);
+	}
+
+	switch (size) {
+
+	case ACPI_SBS_WORD_DATA:
+		acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA, temp);
+		acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA + 1, temp + 1);
+		data->word = (temp[1] << 8) | temp[0];
+		break;
+
+	case ACPI_SBS_BLOCK_DATA:
+		len = 0;
+		acpi_ec_sbs_read(sbs, ACPI_EC_SMB_BCNT, &len);
+		len = min_t(u8, len, 32);
+		for (i = 0; i < len; i++)
+			acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA + i,
+					 data->block + i + 1);
+		data->block[0] = len;
+		break;
+	default:
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"unsupported transaction %d", size));
+		return (-1);
+	}
+
+	return (0);
 }
 
 static int
-acpi_sbs_smbus_read_word(struct acpi_ec_smbus *smbus, int addr, int func,
-			 u16 * word,
-			 void (*err_handler) (struct acpi_ec_smbus * smbus))
+acpi_sbs_read_word(struct acpi_sbs *sbs, int addr, int func, u16 * word)
 {
-	union i2c_smbus_data data;
+	union sbs_rw_data data;
 	int result = 0;
-	int i;
 
-	if (err_handler == NULL) {
-		err_handler = acpi_battery_smbus_err_handler;
-	}
-
-	for (i = 0; i < MAX_SMBUS_ERR; i++) {
-		result =
-		    smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0,
-						    I2C_SMBUS_READ, func,
-						    I2C_SMBUS_WORD_DATA, &data);
-		if (result) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "try %i: smbus->adapter.algo->smbus_xfer() failed\n",
-					  i));
-			if (err_handler) {
-				err_handler(smbus);
-			}
-		} else {
-			*word = data.word;
-			break;
-		}
+	result = acpi_ec_sbs_access(sbs, addr,
+				    ACPI_SBS_SMBUS_READ, func,
+				    ACPI_SBS_WORD_DATA, &data);
+	if (result) {
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_ec_sbs_access() failed"));
+	} else {
+		*word = data.word;
 	}
 
 	return result;
 }
 
 static int
-acpi_sbs_smbus_read_str(struct acpi_ec_smbus *smbus, int addr, int func,
-			char *str,
-			void (*err_handler) (struct acpi_ec_smbus * smbus))
+acpi_sbs_read_str(struct acpi_sbs *sbs, int addr, int func, char *str)
 {
-	union i2c_smbus_data data;
+	union sbs_rw_data data;
 	int result = 0;
-	int i;
 
-	if (err_handler == NULL) {
-		err_handler = acpi_battery_smbus_err_handler;
-	}
-
-	for (i = 0; i < MAX_SMBUS_ERR; i++) {
-		result =
-		    smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0,
-						    I2C_SMBUS_READ, func,
-						    I2C_SMBUS_BLOCK_DATA,
-						    &data);
-		if (result) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "try %i: smbus->adapter.algo->smbus_xfer() failed\n",
-					  i));
-			if (err_handler) {
-				err_handler(smbus);
-			}
-		} else {
-			strncpy(str, (const char *)data.block + 1,
-				data.block[0]);
-			str[data.block[0]] = 0;
-			break;
-		}
+	result = acpi_ec_sbs_access(sbs, addr,
+				    ACPI_SBS_SMBUS_READ, func,
+				    ACPI_SBS_BLOCK_DATA, &data);
+	if (result) {
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_ec_sbs_access() failed"));
+	} else {
+		strncpy(str, (const char *)data.block + 1, data.block[0]);
+		str[data.block[0]] = 0;
 	}
 
 	return result;
 }
 
 static int
-acpi_sbs_smbus_write_word(struct acpi_ec_smbus *smbus, int addr, int func,
-			  int word,
-			  void (*err_handler) (struct acpi_ec_smbus * smbus))
+acpi_sbs_write_word(struct acpi_sbs *sbs, int addr, int func, int word)
 {
-	union i2c_smbus_data data;
+	union sbs_rw_data data;
 	int result = 0;
-	int i;
-
-	if (err_handler == NULL) {
-		err_handler = acpi_battery_smbus_err_handler;
-	}
 
 	data.word = word;
 
-	for (i = 0; i < MAX_SMBUS_ERR; i++) {
-		result =
-		    smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0,
-						    I2C_SMBUS_WRITE, func,
-						    I2C_SMBUS_WORD_DATA, &data);
-		if (result) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "try %i: smbus->adapter.algo"
-					  "->smbus_xfer() failed\n", i));
-			if (err_handler) {
-				err_handler(smbus);
-			}
-		} else {
-			break;
-		}
+	result = acpi_ec_sbs_access(sbs, addr,
+				    ACPI_SBS_SMBUS_WRITE, func,
+				    ACPI_SBS_WORD_DATA, &data);
+	if (result) {
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_ec_sbs_access() failed"));
 	}
 
 	return result;
 }
 
+static int sbs_zombie(struct acpi_sbs *sbs)
+{
+	return (sbs->zombie);
+}
+
+static int sbs_mutex_lock(struct acpi_sbs *sbs)
+{
+	if (sbs_zombie(sbs)) {
+		return -ENODEV;
+	}
+	mutex_lock(&sbs->mutex);
+	return 0;
+}
+
+static void sbs_mutex_unlock(struct acpi_sbs *sbs)
+{
+	mutex_unlock(&sbs->mutex);
+}
+
 /* --------------------------------------------------------------------------
                             Smart Battery System Management
    -------------------------------------------------------------------------- */
 
-/* Smart Battery */
+static int acpi_check_update_proc(struct acpi_sbs *sbs)
+{
+	acpi_status status = AE_OK;
+
+	if (update_time == 0) {
+		sbs->update_proc_flg = 0;
+		return 0;
+	}
+	if (sbs->update_proc_flg == 0) {
+		status = acpi_os_execute(OSL_GPE_HANDLER,
+					 acpi_sbs_update_time, sbs);
+		if (status != AE_OK) {
+			ACPI_EXCEPTION((AE_INFO, status,
+					"acpi_os_execute() failed"));
+			return 1;
+		}
+		sbs->update_proc_flg = 1;
+	}
+	return 0;
+}
 
 static int acpi_sbs_generate_event(struct acpi_device *device,
 				   int event, int state, char *bid, char *class)
@@ -366,12 +450,11 @@
 	int result = 0;
 	int is_present = 0;
 
-	result = acpi_sbs_smbus_read_word(battery->sbs->smbus,
-					  ACPI_SBSM_SMBUS_ADDR, 0x01,
-					  &state, NULL);
+	result = acpi_sbs_read_word(battery->sbs,
+				    ACPI_SBSM_SMBUS_ADDR, 0x01, &state);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_read_word() failed"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_read_word() failed"));
 	}
 	if (!result) {
 		is_present = (state & 0x000f) & (1 << battery->id);
@@ -381,45 +464,33 @@
 	return result;
 }
 
-static int acpi_battery_is_present(struct acpi_battery *battery)
-{
-	return (battery->battery_present);
-}
-
-static int acpi_ac_is_present(struct acpi_sbs *sbs)
-{
-	return (sbs->ac_present);
-}
-
 static int acpi_battery_select(struct acpi_battery *battery)
 {
-	struct acpi_ec_smbus *smbus = battery->sbs->smbus;
+	struct acpi_sbs *sbs = battery->sbs;
 	int result = 0;
 	s16 state;
 	int foo;
 
-	if (battery->sbs->sbsm_present) {
+	if (sbs->sbsm_present) {
 
 		/* Take special care not to knobble other nibbles of
 		 * state (aka selector_state), since
 		 * it causes charging to halt on SBSELs */
 
 		result =
-		    acpi_sbs_smbus_read_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x01,
-					     &state, NULL);
+		    acpi_sbs_read_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x01, &state);
 		if (result) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "acpi_sbs_smbus_read_word() failed\n"));
+			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+					"acpi_sbs_read_word() failed"));
 			goto end;
 		}
 
 		foo = (state & 0x0fff) | (1 << (battery->id + 12));
 		result =
-		    acpi_sbs_smbus_write_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x01,
-					      foo, NULL);
+		    acpi_sbs_write_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x01, foo);
 		if (result) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "acpi_sbs_smbus_write_word() failed\n"));
+			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+					"acpi_sbs_write_word() failed"));
 			goto end;
 		}
 	}
@@ -430,15 +501,14 @@
 
 static int acpi_sbsm_get_info(struct acpi_sbs *sbs)
 {
-	struct acpi_ec_smbus *smbus = sbs->smbus;
 	int result = 0;
 	s16 battery_system_info;
 
-	result = acpi_sbs_smbus_read_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x04,
-					  &battery_system_info, NULL);
+	result = acpi_sbs_read_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x04,
+				    &battery_system_info);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_read_word() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_read_word() failed"));
 		goto end;
 	}
 
@@ -451,53 +521,50 @@
 
 static int acpi_battery_get_info(struct acpi_battery *battery)
 {
-	struct acpi_ec_smbus *smbus = battery->sbs->smbus;
+	struct acpi_sbs *sbs = battery->sbs;
 	int result = 0;
 	s16 battery_mode;
 	s16 specification_info;
 
-	result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x03,
-					  &battery_mode,
-					  &acpi_battery_smbus_err_handler);
+	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x03,
+				    &battery_mode);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_read_word() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_read_word() failed"));
 		goto end;
 	}
 	battery->info.capacity_mode = (battery_mode & 0x8000) >> 15;
 
-	result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x10,
-					  &battery->info.full_charge_capacity,
-					  &acpi_battery_smbus_err_handler);
+	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x10,
+				    &battery->info.full_charge_capacity);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_read_word() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_read_word() failed"));
 		goto end;
 	}
 
-	result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x18,
-					  &battery->info.design_capacity,
-					  &acpi_battery_smbus_err_handler);
+	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x18,
+				    &battery->info.design_capacity);
 
 	if (result) {
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_read_word() failed"));
 		goto end;
 	}
 
-	result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x19,
-					  &battery->info.design_voltage,
-					  &acpi_battery_smbus_err_handler);
+	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x19,
+				    &battery->info.design_voltage);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_read_word() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_read_word() failed"));
 		goto end;
 	}
 
-	result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x1a,
-					  &specification_info,
-					  &acpi_battery_smbus_err_handler);
+	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x1a,
+				    &specification_info);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_read_word() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_read_word() failed"));
 		goto end;
 	}
 
@@ -529,37 +596,35 @@
 		battery->info.ipscale = 1;
 	}
 
-	result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x1c,
-					  &battery->info.serial_number,
-					  &acpi_battery_smbus_err_handler);
+	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x1c,
+				    &battery->info.serial_number);
 	if (result) {
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_read_word() failed"));
 		goto end;
 	}
 
-	result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x20,
-					 battery->info.manufacturer_name,
-					 &acpi_battery_smbus_err_handler);
+	result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x20,
+				   battery->info.manufacturer_name);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_read_str() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_read_str() failed"));
 		goto end;
 	}
 
-	result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x21,
-					 battery->info.device_name,
-					 &acpi_battery_smbus_err_handler);
+	result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x21,
+				   battery->info.device_name);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_read_str() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_read_str() failed"));
 		goto end;
 	}
 
-	result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x22,
-					 battery->info.device_chemistry,
-					 &acpi_battery_smbus_err_handler);
+	result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x22,
+				   battery->info.device_chemistry);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_read_str() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_read_str() failed"));
 		goto end;
 	}
 
@@ -567,103 +632,60 @@
 	return result;
 }
 
-static void acpi_update_delay(struct acpi_sbs *sbs)
-{
-	if (sbs->zombie) {
-		return;
-	}
-	if (sbs->update_time2 > 0) {
-		msleep(sbs->update_time2 * 1000);
-	}
-}
-
 static int acpi_battery_get_state(struct acpi_battery *battery)
 {
-	struct acpi_ec_smbus *smbus = battery->sbs->smbus;
+	struct acpi_sbs *sbs = battery->sbs;
 	int result = 0;
 
-	acpi_update_delay(battery->sbs);
-	result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x09,
-					  &battery->state.voltage,
-					  &acpi_battery_smbus_err_handler);
+	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x09,
+				    &battery->state.voltage);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_read_word() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_read_word() failed"));
 		goto end;
 	}
 
-	acpi_update_delay(battery->sbs);
-	result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x0a,
-					  &battery->state.amperage,
-					  &acpi_battery_smbus_err_handler);
+	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x0a,
+				    &battery->state.amperage);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_read_word() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_read_word() failed"));
 		goto end;
 	}
 
-	acpi_update_delay(battery->sbs);
-	result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x0f,
-					  &battery->state.remaining_capacity,
-					  &acpi_battery_smbus_err_handler);
+	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x0f,
+				    &battery->state.remaining_capacity);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_read_word() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_read_word() failed"));
 		goto end;
 	}
 
-	acpi_update_delay(battery->sbs);
-	result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x12,
-					  &battery->state.average_time_to_empty,
-					  &acpi_battery_smbus_err_handler);
+	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x16,
+				    &battery->state.battery_state);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_read_word() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_read_word() failed"));
 		goto end;
 	}
 
-	acpi_update_delay(battery->sbs);
-	result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x13,
-					  &battery->state.average_time_to_full,
-					  &acpi_battery_smbus_err_handler);
-	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_read_word() failed\n"));
-		goto end;
-	}
-
-	acpi_update_delay(battery->sbs);
-	result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x16,
-					  &battery->state.battery_status,
-					  &acpi_battery_smbus_err_handler);
-	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_read_word() failed\n"));
-		goto end;
-	}
-
-	acpi_update_delay(battery->sbs);
-
       end:
 	return result;
 }
 
 static int acpi_battery_get_alarm(struct acpi_battery *battery)
 {
-	struct acpi_ec_smbus *smbus = battery->sbs->smbus;
+	struct acpi_sbs *sbs = battery->sbs;
 	int result = 0;
 
-	result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01,
-					  &battery->alarm.remaining_capacity,
-					  &acpi_battery_smbus_err_handler);
+	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01,
+				    &battery->alarm.remaining_capacity);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_read_word() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_read_word() failed"));
 		goto end;
 	}
 
-	acpi_update_delay(battery->sbs);
-
       end:
 
 	return result;
@@ -672,15 +694,15 @@
 static int acpi_battery_set_alarm(struct acpi_battery *battery,
 				  unsigned long alarm)
 {
-	struct acpi_ec_smbus *smbus = battery->sbs->smbus;
+	struct acpi_sbs *sbs = battery->sbs;
 	int result = 0;
 	s16 battery_mode;
 	int foo;
 
 	result = acpi_battery_select(battery);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_battery_select() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_battery_select() failed"));
 		goto end;
 	}
 
@@ -688,33 +710,29 @@
 
 	if (alarm > 0) {
 		result =
-		    acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x03,
-					     &battery_mode,
-					     &acpi_battery_smbus_err_handler);
+		    acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x03,
+				       &battery_mode);
 		if (result) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "acpi_sbs_smbus_read_word() failed\n"));
+			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+					"acpi_sbs_read_word() failed"));
 			goto end;
 		}
 
 		result =
-		    acpi_sbs_smbus_write_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01,
-					      battery_mode & 0xbfff,
-					      &acpi_battery_smbus_err_handler);
+		    acpi_sbs_write_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01,
+					battery_mode & 0xbfff);
 		if (result) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "acpi_sbs_smbus_write_word() failed\n"));
+			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+					"acpi_sbs_write_word() failed"));
 			goto end;
 		}
 	}
 
 	foo = alarm / (battery->info.capacity_mode ? 10 : 1);
-	result = acpi_sbs_smbus_write_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01,
-					   foo,
-					   &acpi_battery_smbus_err_handler);
+	result = acpi_sbs_write_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01, foo);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_write_word() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_write_word() failed"));
 		goto end;
 	}
 
@@ -725,6 +743,7 @@
 
 static int acpi_battery_set_mode(struct acpi_battery *battery)
 {
+	struct acpi_sbs *sbs = battery->sbs;
 	int result = 0;
 	s16 battery_mode;
 
@@ -732,12 +751,11 @@
 		goto end;
 	}
 
-	result = acpi_sbs_smbus_read_word(battery->sbs->smbus,
-					  ACPI_SB_SMBUS_ADDR, 0x03,
-					  &battery_mode, NULL);
+	result = acpi_sbs_read_word(sbs,
+				    ACPI_SB_SMBUS_ADDR, 0x03, &battery_mode);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_read_word() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_read_word() failed"));
 		goto end;
 	}
 
@@ -746,21 +764,19 @@
 	} else {
 		battery_mode |= 0x8000;
 	}
-	result = acpi_sbs_smbus_write_word(battery->sbs->smbus,
-					   ACPI_SB_SMBUS_ADDR, 0x03,
-					   battery_mode, NULL);
+	result = acpi_sbs_write_word(sbs,
+				     ACPI_SB_SMBUS_ADDR, 0x03, battery_mode);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_write_word() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_write_word() failed"));
 		goto end;
 	}
 
-	result = acpi_sbs_smbus_read_word(battery->sbs->smbus,
-					  ACPI_SB_SMBUS_ADDR, 0x03,
-					  &battery_mode, NULL);
+	result = acpi_sbs_read_word(sbs,
+				    ACPI_SB_SMBUS_ADDR, 0x03, &battery_mode);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_read_word() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_read_word() failed"));
 		goto end;
 	}
 
@@ -774,36 +790,36 @@
 
 	result = acpi_battery_select(battery);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_battery_init() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_battery_select() failed"));
 		goto end;
 	}
 
 	result = acpi_battery_set_mode(battery);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_battery_set_mode() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_battery_set_mode() failed"));
 		goto end;
 	}
 
 	result = acpi_battery_get_info(battery);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_battery_get_info() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_battery_get_info() failed"));
 		goto end;
 	}
 
 	result = acpi_battery_get_state(battery);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_battery_get_state() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_battery_get_state() failed"));
 		goto end;
 	}
 
 	result = acpi_battery_get_alarm(battery);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_battery_get_alarm() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_battery_get_alarm() failed"));
 		goto end;
 	}
 
@@ -813,20 +829,19 @@
 
 static int acpi_ac_get_present(struct acpi_sbs *sbs)
 {
-	struct acpi_ec_smbus *smbus = sbs->smbus;
 	int result = 0;
 	s16 charger_status;
 
-	result = acpi_sbs_smbus_read_word(smbus, ACPI_SBC_SMBUS_ADDR, 0x13,
-					  &charger_status, NULL);
+	result = acpi_sbs_read_word(sbs, ACPI_SBC_SMBUS_ADDR, 0x13,
+				    &charger_status);
 
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_smbus_read_word() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_read_word() failed"));
 		goto end;
 	}
 
-	sbs->ac_present = (charger_status & 0x8000) >> 15;
+	sbs->ac.ac_present = (charger_status & 0x8000) >> 15;
 
       end:
 
@@ -852,8 +867,8 @@
 	if (!*dir) {
 		*dir = proc_mkdir(dir_name, parent_dir);
 		if (!*dir) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "proc_mkdir() failed\n"));
+			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+					"proc_mkdir() failed"));
 			return -ENODEV;
 		}
 		(*dir)->owner = THIS_MODULE;
@@ -863,8 +878,8 @@
 	if (info_fops) {
 		entry = create_proc_entry(ACPI_SBS_FILE_INFO, S_IRUGO, *dir);
 		if (!entry) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "create_proc_entry() failed\n"));
+			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+					"create_proc_entry() failed"));
 		} else {
 			entry->proc_fops = info_fops;
 			entry->data = data;
@@ -876,8 +891,8 @@
 	if (state_fops) {
 		entry = create_proc_entry(ACPI_SBS_FILE_STATE, S_IRUGO, *dir);
 		if (!entry) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "create_proc_entry() failed\n"));
+			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+					"create_proc_entry() failed"));
 		} else {
 			entry->proc_fops = state_fops;
 			entry->data = data;
@@ -889,8 +904,8 @@
 	if (alarm_fops) {
 		entry = create_proc_entry(ACPI_SBS_FILE_ALARM, S_IRUGO, *dir);
 		if (!entry) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "create_proc_entry() failed\n"));
+			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+					"create_proc_entry() failed"));
 		} else {
 			entry->proc_fops = alarm_fops;
 			entry->data = data;
@@ -923,24 +938,27 @@
 static int acpi_battery_read_info(struct seq_file *seq, void *offset)
 {
 	struct acpi_battery *battery = seq->private;
+	struct acpi_sbs *sbs = battery->sbs;
 	int cscale;
 	int result = 0;
 
-	if (battery->sbs->zombie) {
+	if (sbs_mutex_lock(sbs)) {
 		return -ENODEV;
 	}
 
-	down(&sbs_sem);
+	result = acpi_check_update_proc(sbs);
+	if (result)
+		goto end;
 
-	if (update_mode == REQUEST_UPDATE_MODE) {
-		result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_INFO);
+	if (update_time == 0) {
+		result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_INFO);
 		if (result) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "acpi_sbs_update_run() failed\n"));
+			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+					"acpi_sbs_update_run() failed"));
 		}
 	}
 
-	if (acpi_battery_is_present(battery)) {
+	if (battery->battery_present) {
 		seq_printf(seq, "present:                 yes\n");
 	} else {
 		seq_printf(seq, "present:                 no\n");
@@ -952,13 +970,13 @@
 	} else {
 		cscale = battery->info.ipscale;
 	}
-	seq_printf(seq, "design capacity:         %i%s",
+	seq_printf(seq, "design capacity:         %i%s\n",
 		   battery->info.design_capacity * cscale,
-		   battery->info.capacity_mode ? "0 mWh\n" : " mAh\n");
+		   battery->info.capacity_mode ? "0 mWh" : " mAh");
 
-	seq_printf(seq, "last full capacity:      %i%s",
+	seq_printf(seq, "last full capacity:      %i%s\n",
 		   battery->info.full_charge_capacity * cscale,
-		   battery->info.capacity_mode ? "0 mWh\n" : " mAh\n");
+		   battery->info.capacity_mode ? "0 mWh" : " mAh");
 
 	seq_printf(seq, "battery technology:      rechargeable\n");
 
@@ -984,7 +1002,7 @@
 
       end:
 
-	up(&sbs_sem);
+	sbs_mutex_unlock(sbs);
 
 	return result;
 }
@@ -996,26 +1014,29 @@
 
 static int acpi_battery_read_state(struct seq_file *seq, void *offset)
 {
-	struct acpi_battery *battery = (struct acpi_battery *)seq->private;
+	struct acpi_battery *battery = seq->private;
+	struct acpi_sbs *sbs = battery->sbs;
 	int result = 0;
 	int cscale;
 	int foo;
 
-	if (battery->sbs->zombie) {
+	if (sbs_mutex_lock(sbs)) {
 		return -ENODEV;
 	}
 
-	down(&sbs_sem);
+	result = acpi_check_update_proc(sbs);
+	if (result)
+		goto end;
 
-	if (update_mode == REQUEST_UPDATE_MODE) {
-		result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_STATE);
+	if (update_time == 0) {
+		result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_STATE);
 		if (result) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "acpi_sbs_update_run() failed\n"));
+			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+					"acpi_sbs_update_run() failed"));
 		}
 	}
 
-	if (acpi_battery_is_present(battery)) {
+	if (battery->battery_present) {
 		seq_printf(seq, "present:                 yes\n");
 	} else {
 		seq_printf(seq, "present:                 no\n");
@@ -1028,7 +1049,7 @@
 		cscale = battery->info.ipscale;
 	}
 
-	if (battery->state.battery_status & 0x0010) {
+	if (battery->state.battery_state & 0x0010) {
 		seq_printf(seq, "capacity state:          critical\n");
 	} else {
 		seq_printf(seq, "capacity state:          ok\n");
@@ -1052,16 +1073,16 @@
 			   battery->info.capacity_mode ? "mW" : "mA");
 	}
 
-	seq_printf(seq, "remaining capacity:      %i%s",
+	seq_printf(seq, "remaining capacity:      %i%s\n",
 		   battery->state.remaining_capacity * cscale,
-		   battery->info.capacity_mode ? "0 mWh\n" : " mAh\n");
+		   battery->info.capacity_mode ? "0 mWh" : " mAh");
 
 	seq_printf(seq, "present voltage:         %i mV\n",
 		   battery->state.voltage * battery->info.vscale);
 
       end:
 
-	up(&sbs_sem);
+	sbs_mutex_unlock(sbs);
 
 	return result;
 }
@@ -1074,24 +1095,27 @@
 static int acpi_battery_read_alarm(struct seq_file *seq, void *offset)
 {
 	struct acpi_battery *battery = seq->private;
+	struct acpi_sbs *sbs = battery->sbs;
 	int result = 0;
 	int cscale;
 
-	if (battery->sbs->zombie) {
+	if (sbs_mutex_lock(sbs)) {
 		return -ENODEV;
 	}
 
-	down(&sbs_sem);
+	result = acpi_check_update_proc(sbs);
+	if (result)
+		goto end;
 
-	if (update_mode == REQUEST_UPDATE_MODE) {
-		result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_ALARM);
+	if (update_time == 0) {
+		result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_ALARM);
 		if (result) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "acpi_sbs_update_run() failed\n"));
+			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+					"acpi_sbs_update_run() failed"));
 		}
 	}
 
-	if (!acpi_battery_is_present(battery)) {
+	if (!battery->battery_present) {
 		seq_printf(seq, "present:                 no\n");
 		goto end;
 	}
@@ -1104,16 +1128,16 @@
 
 	seq_printf(seq, "alarm:                   ");
 	if (battery->alarm.remaining_capacity) {
-		seq_printf(seq, "%i%s",
+		seq_printf(seq, "%i%s\n",
 			   battery->alarm.remaining_capacity * cscale,
-			   battery->info.capacity_mode ? "0 mWh\n" : " mAh\n");
+			   battery->info.capacity_mode ? "0 mWh" : " mAh");
 	} else {
 		seq_printf(seq, "disabled\n");
 	}
 
       end:
 
-	up(&sbs_sem);
+	sbs_mutex_unlock(sbs);
 
 	return result;
 }
@@ -1124,16 +1148,19 @@
 {
 	struct seq_file *seq = file->private_data;
 	struct acpi_battery *battery = seq->private;
+	struct acpi_sbs *sbs = battery->sbs;
 	char alarm_string[12] = { '\0' };
 	int result, old_alarm, new_alarm;
 
-	if (battery->sbs->zombie) {
+	if (sbs_mutex_lock(sbs)) {
 		return -ENODEV;
 	}
 
-	down(&sbs_sem);
+	result = acpi_check_update_proc(sbs);
+	if (result)
+		goto end;
 
-	if (!acpi_battery_is_present(battery)) {
+	if (!battery->battery_present) {
 		result = -ENODEV;
 		goto end;
 	}
@@ -1155,21 +1182,21 @@
 
 	result = acpi_battery_set_alarm(battery, new_alarm);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_battery_set_alarm() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_battery_set_alarm() failed"));
 		acpi_battery_set_alarm(battery, old_alarm);
 		goto end;
 	}
 	result = acpi_battery_get_alarm(battery);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_battery_get_alarm() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_battery_get_alarm() failed"));
 		acpi_battery_set_alarm(battery, old_alarm);
 		goto end;
 	}
 
       end:
-	up(&sbs_sem);
+	sbs_mutex_unlock(sbs);
 
 	if (result) {
 		return result;
@@ -1217,24 +1244,22 @@
 	struct acpi_sbs *sbs = seq->private;
 	int result;
 
-	if (sbs->zombie) {
+	if (sbs_mutex_lock(sbs)) {
 		return -ENODEV;
 	}
 
-	down(&sbs_sem);
-
-	if (update_mode == REQUEST_UPDATE_MODE) {
-		result = acpi_sbs_update_run(sbs, DATA_TYPE_AC_STATE);
+	if (update_time == 0) {
+		result = acpi_sbs_update_run(sbs, -1, DATA_TYPE_AC_STATE);
 		if (result) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "acpi_sbs_update_run() failed\n"));
+			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+					"acpi_sbs_update_run() failed"));
 		}
 	}
 
 	seq_printf(seq, "state:                   %s\n",
-		   sbs->ac_present ? "on-line" : "off-line");
+		   sbs->ac.ac_present ? "on-line" : "off-line");
 
-	up(&sbs_sem);
+	sbs_mutex_unlock(sbs);
 
 	return 0;
 }
@@ -1275,25 +1300,25 @@
 
 	result = acpi_battery_select(battery);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_battery_select() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_battery_select() failed"));
 		goto end;
 	}
 
 	result = acpi_battery_get_present(battery);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_battery_get_present() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_battery_get_present() failed"));
 		goto end;
 	}
 
-	is_present = acpi_battery_is_present(battery);
+	is_present = battery->battery_present;
 
 	if (is_present) {
 		result = acpi_battery_init(battery);
 		if (result) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "acpi_battery_init() failed\n"));
+			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+					"acpi_battery_init() failed"));
 			goto end;
 		}
 		battery->init_state = 1;
@@ -1308,12 +1333,16 @@
 					 &acpi_battery_state_fops,
 					 &acpi_battery_alarm_fops, battery);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_generic_add_fs() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_generic_add_fs() failed"));
 		goto end;
 	}
 	battery->alive = 1;
 
+	printk(KERN_INFO PREFIX "%s [%s]: Battery Slot [%s] (battery %s)\n",
+	       ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), dir_name,
+	       sbs->battery->battery_present ? "present" : "absent");
+
       end:
 	return result;
 }
@@ -1333,8 +1362,8 @@
 
 	result = acpi_ac_get_present(sbs);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_ac_get_present() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_ac_get_present() failed"));
 		goto end;
 	}
 
@@ -1343,11 +1372,15 @@
 					 ACPI_AC_DIR_NAME,
 					 NULL, &acpi_ac_state_fops, NULL, sbs);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_generic_add_fs() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_generic_add_fs() failed"));
 		goto end;
 	}
 
+	printk(KERN_INFO PREFIX "%s [%s]: AC Adapter [%s] (%s)\n",
+	       ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device),
+	       ACPI_AC_DIR_NAME, sbs->ac.ac_present ? "on-line" : "off-line");
+
       end:
 
 	return result;
@@ -1361,45 +1394,85 @@
 	}
 }
 
-static void acpi_sbs_update_queue_run(unsigned long data)
+static void acpi_sbs_update_time_run(unsigned long data)
 {
-	acpi_os_execute(OSL_GPE_HANDLER, acpi_sbs_update_queue, (void *)data);
+	acpi_os_execute(OSL_GPE_HANDLER, acpi_sbs_update_time, (void *)data);
 }
 
-static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type)
+static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type)
 {
 	struct acpi_battery *battery;
-	int result = 0;
-	int old_ac_present;
-	int old_battery_present;
-	int new_ac_present;
-	int new_battery_present;
-	int id;
+	int result = 0, cnt;
+	int old_ac_present = -1;
+	int old_battery_present = -1;
+	int new_ac_present = -1;
+	int new_battery_present = -1;
+	int id_min = 0, id_max = MAX_SBS_BAT - 1;
 	char dir_name[32];
-	int do_battery_init, do_ac_init;
-	s16 old_remaining_capacity;
+	int do_battery_init = 0, do_ac_init = 0;
+	int old_remaining_capacity = 0;
+	int update_ac = 1, update_battery = 1;
+	int up_tm = update_time;
 
-	if (sbs->zombie) {
+	if (sbs_zombie(sbs)) {
 		goto end;
 	}
 
-	old_ac_present = acpi_ac_is_present(sbs);
+	if (id >= 0) {
+		id_min = id_max = id;
+	}
+
+	if (data_type == DATA_TYPE_COMMON && up_tm > 0) {
+		cnt = up_tm / (up_tm > UPDATE_DELAY ? UPDATE_DELAY : up_tm);
+		if (sbs->run_cnt % cnt != 0) {
+			update_battery = 0;
+		}
+	}
+
+	sbs->run_cnt++;
+
+	if (!update_ac && !update_battery) {
+		goto end;
+	}
+
+	old_ac_present = sbs->ac.ac_present;
 
 	result = acpi_ac_get_present(sbs);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_ac_get_present() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_ac_get_present() failed"));
 	}
 
-	new_ac_present = acpi_ac_is_present(sbs);
+	new_ac_present = sbs->ac.ac_present;
 
 	do_ac_init = (old_ac_present != new_ac_present);
+	if (sbs->run_cnt == 1 && data_type == DATA_TYPE_COMMON) {
+		do_ac_init = 1;
+	}
 
-	if (data_type == DATA_TYPE_AC_STATE) {
+	if (do_ac_init) {
+		result = acpi_sbs_generate_event(sbs->device,
+						 ACPI_SBS_AC_NOTIFY_STATUS,
+						 new_ac_present,
+						 ACPI_AC_DIR_NAME,
+						 ACPI_AC_CLASS);
+		if (result) {
+			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+					"acpi_sbs_generate_event() failed"));
+		}
+	}
+
+	if (data_type == DATA_TYPE_COMMON) {
+		if (!do_ac_init && !update_battery) {
+			goto end;
+		}
+	}
+
+	if (data_type == DATA_TYPE_AC_STATE && !do_ac_init) {
 		goto end;
 	}
 
-	for (id = 0; id < MAX_SBS_BAT; id++) {
+	for (id = id_min; id <= id_max; id++) {
 		battery = &sbs->battery[id];
 		if (battery->alive == 0) {
 			continue;
@@ -1407,81 +1480,92 @@
 
 		old_remaining_capacity = battery->state.remaining_capacity;
 
-		old_battery_present = acpi_battery_is_present(battery);
+		old_battery_present = battery->battery_present;
 
 		result = acpi_battery_select(battery);
 		if (result) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "acpi_battery_select() failed\n"));
-		}
-		if (sbs->zombie) {
-			goto end;
+			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+					"acpi_battery_select() failed"));
 		}
 
 		result = acpi_battery_get_present(battery);
 		if (result) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "acpi_battery_get_present() failed\n"));
-		}
-		if (sbs->zombie) {
-			goto end;
+			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+					"acpi_battery_get_present() failed"));
 		}
 
-		new_battery_present = acpi_battery_is_present(battery);
+		new_battery_present = battery->battery_present;
 
 		do_battery_init = ((old_battery_present != new_battery_present)
 				   && new_battery_present);
-
-		if (sbs->zombie) {
-			goto end;
-		}
-		if (do_ac_init || do_battery_init ||
-		    update_info_mode || sbs->update_info_mode) {
-			if (sbs->update_info_mode) {
-				sbs->update_info_mode = 0;
-			} else {
-				sbs->update_info_mode = 1;
-			}
+		if (!new_battery_present)
+			goto event;
+		if (do_ac_init || do_battery_init) {
 			result = acpi_battery_init(battery);
 			if (result) {
-				ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-						  "acpi_battery_init() "
-						  "failed\n"));
+				ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+						"acpi_battery_init() "
+						"failed"));
+			}
+		}
+		if (sbs_zombie(sbs)) {
+			goto end;
+		}
+
+		if ((data_type == DATA_TYPE_COMMON
+		     || data_type == DATA_TYPE_INFO)
+		    && new_battery_present) {
+			result = acpi_battery_get_info(battery);
+			if (result) {
+				ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+						"acpi_battery_get_info() failed"));
 			}
 		}
 		if (data_type == DATA_TYPE_INFO) {
 			continue;
 		}
-
-		if (sbs->zombie) {
+		if (sbs_zombie(sbs)) {
 			goto end;
 		}
-		if (new_battery_present) {
-			result = acpi_battery_get_alarm(battery);
-			if (result) {
-				ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-						  "acpi_battery_get_alarm() "
-						  "failed\n"));
-			}
-			if (data_type == DATA_TYPE_ALARM) {
-				continue;
-			}
 
+		if ((data_type == DATA_TYPE_COMMON
+		     || data_type == DATA_TYPE_STATE)
+		    && new_battery_present) {
 			result = acpi_battery_get_state(battery);
 			if (result) {
-				ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-						  "acpi_battery_get_state() "
-						  "failed\n"));
+				ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+						"acpi_battery_get_state() failed"));
 			}
 		}
-		if (sbs->zombie) {
+		if (data_type == DATA_TYPE_STATE) {
+			goto event;
+		}
+		if (sbs_zombie(sbs)) {
 			goto end;
 		}
-		if (data_type != DATA_TYPE_COMMON) {
+
+		if ((data_type == DATA_TYPE_COMMON
+		     || data_type == DATA_TYPE_ALARM)
+		    && new_battery_present) {
+			result = acpi_battery_get_alarm(battery);
+			if (result) {
+				ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+						"acpi_battery_get_alarm() "
+						"failed"));
+			}
+		}
+		if (data_type == DATA_TYPE_ALARM) {
 			continue;
 		}
+		if (sbs_zombie(sbs)) {
+			goto end;
+		}
 
-		if (old_battery_present != new_battery_present) {
+	      event:
+
+		if (old_battery_present != new_battery_present || do_ac_init ||
+		    old_remaining_capacity !=
+		    battery->state.remaining_capacity) {
 			sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id);
 			result = acpi_sbs_generate_event(sbs->device,
 							 ACPI_SBS_BATTERY_NOTIFY_STATUS,
@@ -1489,151 +1573,120 @@
 							 dir_name,
 							 ACPI_BATTERY_CLASS);
 			if (result) {
-				ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-						  "acpi_sbs_generate_event() "
-						  "failed\n"));
+				ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+						"acpi_sbs_generate_event() "
+						"failed"));
 			}
 		}
-		if (old_remaining_capacity != battery->state.remaining_capacity) {
-			sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id);
-			result = acpi_sbs_generate_event(sbs->device,
-							 ACPI_SBS_BATTERY_NOTIFY_STATUS,
-							 new_battery_present,
-							 dir_name,
-							 ACPI_BATTERY_CLASS);
-			if (result) {
-				ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-						  "acpi_sbs_generate_event() failed\n"));
-			}
-		}
-
-	}
-	if (sbs->zombie) {
-		goto end;
-	}
-	if (data_type != DATA_TYPE_COMMON) {
-		goto end;
-	}
-
-	if (old_ac_present != new_ac_present) {
-		result = acpi_sbs_generate_event(sbs->device,
-						 ACPI_SBS_AC_NOTIFY_STATUS,
-						 new_ac_present,
-						 ACPI_AC_DIR_NAME,
-						 ACPI_AC_CLASS);
-		if (result) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "acpi_sbs_generate_event() failed\n"));
-		}
 	}
 
       end:
+
 	return result;
 }
 
-static void acpi_sbs_update_queue(void *data)
+static void acpi_sbs_update_time(void *data)
 {
 	struct acpi_sbs *sbs = data;
 	unsigned long delay = -1;
 	int result;
+	unsigned int up_tm = update_time;
 
-	if (sbs->zombie) {
-		goto end;
-	}
+	if (sbs_mutex_lock(sbs))
+		return;
 
-	result = acpi_sbs_update_run(sbs, DATA_TYPE_COMMON);
+	result = acpi_sbs_update_run(sbs, -1, DATA_TYPE_COMMON);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_sbs_update_run() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_sbs_update_run() failed"));
 	}
 
-	if (sbs->zombie) {
+	if (sbs_zombie(sbs)) {
 		goto end;
 	}
 
-	if (update_mode == REQUEST_UPDATE_MODE) {
-		goto end;
+	if (!up_tm) {
+		if (timer_pending(&sbs->update_timer))
+			del_timer(&sbs->update_timer);
+	} else {
+		delay = (up_tm > UPDATE_DELAY ? UPDATE_DELAY : up_tm);
+		delay = jiffies + HZ * delay;
+		if (timer_pending(&sbs->update_timer)) {
+			mod_timer(&sbs->update_timer, delay);
+		} else {
+			sbs->update_timer.data = (unsigned long)data;
+			sbs->update_timer.function = acpi_sbs_update_time_run;
+			sbs->update_timer.expires = delay;
+			add_timer(&sbs->update_timer);
+		}
 	}
 
-	delay = jiffies + HZ * update_time;
-	sbs->update_timer.data = (unsigned long)data;
-	sbs->update_timer.function = acpi_sbs_update_queue_run;
-	sbs->update_timer.expires = delay;
-	add_timer(&sbs->update_timer);
       end:
-	;
+
+	sbs_mutex_unlock(sbs);
 }
 
 static int acpi_sbs_add(struct acpi_device *device)
 {
 	struct acpi_sbs *sbs = NULL;
-	struct acpi_ec_hc *ec_hc = NULL;
-	int result, remove_result = 0;
+	int result = 0, remove_result = 0;
 	unsigned long sbs_obj;
-	int id, cnt;
+	int id;
 	acpi_status status = AE_OK;
+	unsigned long val;
+
+	status =
+	    acpi_evaluate_integer(device->parent->handle, "_EC", NULL, &val);
+	if (ACPI_FAILURE(status)) {
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Error obtaining _EC"));
+		return -EIO;
+	}
 
 	sbs = kzalloc(sizeof(struct acpi_sbs), GFP_KERNEL);
 	if (!sbs) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "kmalloc() failed\n"));
-		return -ENOMEM;
-	}
-
-	cnt = 0;
-	while (cnt < 10) {
-		cnt++;
-		ec_hc = acpi_get_ec_hc(device);
-		if (ec_hc) {
-			break;
-		}
-		msleep(1000);
-	}
-
-	if (!ec_hc) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_get_ec_hc() failed: "
-				  "NO driver found for EC HC SMBus\n"));
-		result = -ENODEV;
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR, "kzalloc() failed"));
+		result = -ENOMEM;
 		goto end;
 	}
 
+	mutex_init(&sbs->mutex);
+
+	sbs_mutex_lock(sbs);
+
+	sbs->base = (val & 0xff00ull) >> 8;
 	sbs->device = device;
-	sbs->smbus = ec_hc->smbus;
 
 	strcpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME);
 	strcpy(acpi_device_class(device), ACPI_SBS_CLASS);
 	acpi_driver_data(device) = sbs;
 
-	sbs->update_time = 0;
-	sbs->update_time2 = 0;
-
 	result = acpi_ac_add(sbs);
 	if (result) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "acpi_ac_add() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR, "acpi_ac_add() failed"));
 		goto end;
 	}
-	result = acpi_evaluate_integer(device->handle, "_SBS", NULL, &sbs_obj);
-	if (ACPI_FAILURE(result)) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_evaluate_integer() failed\n"));
+	status = acpi_evaluate_integer(device->handle, "_SBS", NULL, &sbs_obj);
+	if (status) {
+		ACPI_EXCEPTION((AE_INFO, status,
+				"acpi_evaluate_integer() failed"));
 		result = -EIO;
 		goto end;
 	}
-
 	if (sbs_obj > 0) {
 		result = acpi_sbsm_get_info(sbs);
 		if (result) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "acpi_sbsm_get_info() failed\n"));
+			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+					"acpi_sbsm_get_info() failed"));
 			goto end;
 		}
 		sbs->sbsm_present = 1;
 	}
+
 	if (sbs->sbsm_present == 0) {
 		result = acpi_battery_add(sbs, 0);
 		if (result) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "acpi_battery_add() failed\n"));
+			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+					"acpi_battery_add() failed"));
 			goto end;
 		}
 	} else {
@@ -1641,9 +1694,8 @@
 			if ((sbs->sbsm_batteries_supported & (1 << id))) {
 				result = acpi_battery_add(sbs, id);
 				if (result) {
-					ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-							  "acpi_battery_add() "
-							  "failed\n"));
+					ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+							"acpi_battery_add() failed"));
 					goto end;
 				}
 			}
@@ -1653,33 +1705,26 @@
 	sbs->handle = device->handle;
 
 	init_timer(&sbs->update_timer);
-	if (update_mode == QUEUE_UPDATE_MODE) {
-		status = acpi_os_execute(OSL_GPE_HANDLER,
-					 acpi_sbs_update_queue, sbs);
-		if (status != AE_OK) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "acpi_os_execute() failed\n"));
-		}
-	}
-	sbs->update_time = update_time;
-	sbs->update_time2 = update_time2;
-
-	printk(KERN_INFO PREFIX "%s [%s]\n",
-	       acpi_device_name(device), acpi_device_bid(device));
+	result = acpi_check_update_proc(sbs);
+	if (result)
+		goto end;
 
       end:
+
+	sbs_mutex_unlock(sbs);
+
 	if (result) {
 		remove_result = acpi_sbs_remove(device, 0);
 		if (remove_result) {
-			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-					  "acpi_sbs_remove() failed\n"));
+			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+					"acpi_sbs_remove() failed"));
 		}
 	}
 
 	return result;
 }
 
-int acpi_sbs_remove(struct acpi_device *device, int type)
+static int acpi_sbs_remove(struct acpi_device *device, int type)
 {
 	struct acpi_sbs *sbs;
 	int id;
@@ -1688,15 +1733,14 @@
 		return -EINVAL;
 	}
 
-	sbs = (struct acpi_sbs *)acpi_driver_data(device);
-
+	sbs = acpi_driver_data(device);
 	if (!sbs) {
 		return -EINVAL;
 	}
 
+	sbs_mutex_lock(sbs);
+
 	sbs->zombie = 1;
-	sbs->update_time = 0;
-	sbs->update_time2 = 0;
 	del_timer_sync(&sbs->update_timer);
 	acpi_os_wait_events_complete(NULL);
 	del_timer_sync(&sbs->update_timer);
@@ -1707,11 +1751,41 @@
 
 	acpi_ac_remove(sbs);
 
+	sbs_mutex_unlock(sbs);
+
+	mutex_destroy(&sbs->mutex);
+
 	kfree(sbs);
 
 	return 0;
 }
 
+static void acpi_sbs_rmdirs(void)
+{
+	if (acpi_ac_dir) {
+		acpi_unlock_ac_dir(acpi_ac_dir);
+		acpi_ac_dir = NULL;
+	}
+	if (acpi_battery_dir) {
+		acpi_unlock_battery_dir(acpi_battery_dir);
+		acpi_battery_dir = NULL;
+	}
+}
+
+static int acpi_sbs_resume(struct acpi_device *device)
+{
+	struct acpi_sbs *sbs;
+
+	if (!device)
+		return -EINVAL;
+
+	sbs = device->driver_data;
+
+	sbs->run_cnt = 0;
+
+	return 0;
+}
+
 static int __init acpi_sbs_init(void)
 {
 	int result = 0;
@@ -1719,35 +1793,34 @@
 	if (acpi_disabled)
 		return -ENODEV;
 
-	init_MUTEX(&sbs_sem);
-
 	if (capacity_mode != DEF_CAPACITY_UNIT
 	    && capacity_mode != MAH_CAPACITY_UNIT
 	    && capacity_mode != MWH_CAPACITY_UNIT) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "acpi_sbs_init: "
-				  "invalid capacity_mode = %d\n",
-				  capacity_mode));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"invalid capacity_mode = %d", capacity_mode));
 		return -EINVAL;
 	}
 
 	acpi_ac_dir = acpi_lock_ac_dir();
 	if (!acpi_ac_dir) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_lock_ac_dir() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_lock_ac_dir() failed"));
 		return -ENODEV;
 	}
 
 	acpi_battery_dir = acpi_lock_battery_dir();
 	if (!acpi_battery_dir) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_lock_battery_dir() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_lock_battery_dir() failed"));
+		acpi_sbs_rmdirs();
 		return -ENODEV;
 	}
 
 	result = acpi_bus_register_driver(&acpi_sbs_driver);
 	if (result < 0) {
-		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "acpi_bus_register_driver() failed\n"));
+		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+				"acpi_bus_register_driver() failed"));
+		acpi_sbs_rmdirs();
 		return -ENODEV;
 	}
 
@@ -1756,13 +1829,9 @@
 
 static void __exit acpi_sbs_exit(void)
 {
-
 	acpi_bus_unregister_driver(&acpi_sbs_driver);
 
-	acpi_unlock_ac_dir(acpi_ac_dir);
-	acpi_ac_dir = NULL;
-	acpi_unlock_battery_dir(acpi_battery_dir);
-	acpi_battery_dir = NULL;
+	acpi_sbs_rmdirs();
 
 	return;
 }
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index bb0e0da..d80dd84 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1068,7 +1068,9 @@
 		}
 		break;
 	default:
-		STRUCT_TO_INT(device->status) = 0x0F;
+		STRUCT_TO_INT(device->status) =
+		    ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED |
+		    ACPI_STA_DEVICE_UI      | ACPI_STA_DEVICE_FUNCTIONING;
 		break;
 	}
 
diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c
index 37a0930..f8c6341 100644
--- a/drivers/acpi/sleep/main.c
+++ b/drivers/acpi/sleep/main.c
@@ -168,9 +168,18 @@
 
 static int acpi_pm_state_valid(suspend_state_t pm_state)
 {
-	u32 acpi_state = acpi_suspend_states[pm_state];
+	u32 acpi_state;
 
-	return sleep_states[acpi_state];
+	switch (pm_state) {
+	case PM_SUSPEND_ON:
+	case PM_SUSPEND_STANDBY:
+	case PM_SUSPEND_MEM:
+		acpi_state = acpi_suspend_states[pm_state];
+
+		return sleep_states[acpi_state];
+	default:
+		return 0;
+	}
 }
 
 static struct pm_ops acpi_pm_ops = {
diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c
index ccc11b3..2d912b7 100644
--- a/drivers/acpi/sleep/proc.c
+++ b/drivers/acpi/sleep/proc.c
@@ -350,21 +350,31 @@
 {
 	struct list_head *node, *next;
 
-	seq_printf(seq, "Device	Sleep state	Status\n");
+	seq_printf(seq, "Device\tS-state\t  Status   Sysfs node\n");
 
 	spin_lock(&acpi_device_lock);
 	list_for_each_safe(node, next, &acpi_wakeup_device_list) {
 		struct acpi_device *dev =
 		    container_of(node, struct acpi_device, wakeup_list);
+		struct device *ldev;
 
 		if (!dev->wakeup.flags.valid)
 			continue;
 		spin_unlock(&acpi_device_lock);
-		seq_printf(seq, "%4s	%4d		%s%8s\n",
+
+		ldev = acpi_get_physical_device(dev->handle);
+		seq_printf(seq, "%s\t  S%d\t%c%-8s  ",
 			   dev->pnp.bus_id,
 			   (u32) dev->wakeup.sleep_state,
-			   dev->wakeup.flags.run_wake ? "*" : "",
+			   dev->wakeup.flags.run_wake ? '*' : ' ',
 			   dev->wakeup.state.enabled ? "enabled" : "disabled");
+		if (ldev)
+			seq_printf(seq, "%s:%s",
+				ldev->bus ? ldev->bus->name : "no-bus",
+				ldev->bus_id);
+		seq_printf(seq, "\n");
+		put_device(ldev);
+
 		spin_lock(&acpi_device_lock);
 	}
 	spin_unlock(&acpi_device_lock);
diff --git a/drivers/acpi/tables/tbfadt.c b/drivers/acpi/tables/tbfadt.c
index 807c711..1db833e 100644
--- a/drivers/acpi/tables/tbfadt.c
+++ b/drivers/acpi/tables/tbfadt.c
@@ -347,6 +347,18 @@
 		acpi_gbl_xpm1b_enable.space_id = acpi_gbl_FADT.xpm1a_event_block.space_id;
 
 	}
+
+	/*
+	 * For ACPI 1.0 FADTs, ensure that reserved fields (which should be zero)
+	 * are indeed zero. This will workaround BIOSs that inadvertently placed
+	 * values in these fields.
+	 */
+	if (acpi_gbl_FADT.header.revision < 3) {
+		acpi_gbl_FADT.preferred_profile = 0;
+		acpi_gbl_FADT.pstate_control = 0;
+		acpi_gbl_FADT.cst_control = 0;
+		acpi_gbl_FADT.boot_flags = 0;
+	}
 }
 
 /******************************************************************************
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index fd54750..268e301 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -47,14 +47,13 @@
 static int amba_uevent(struct device *dev, char **envp, int nr_env, char *buf, int bufsz)
 {
 	struct amba_device *pcdev = to_amba_device(dev);
+	int retval = 0, i = 0, len = 0;
 
-	if (nr_env < 2)
-		return -ENOMEM;
-
-	snprintf(buf, bufsz, "AMBA_ID=%08x", pcdev->periphid);
-	*envp++ = buf;
-	*envp++ = NULL;
-	return 0;
+	retval = add_uevent_var(envp, nr_env, &i,
+				buf, bufsz, &len,
+				"AMBA_ID=%08x", pcdev->periphid);
+	envp[i] = NULL;
+	return retval;
 }
 #else
 #define amba_uevent NULL
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 7bdbe5a..365c306 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -156,11 +156,6 @@
 	help
 	  This option enables support for Initio 162x Serial ATA.
 
-config SATA_INTEL_COMBINED
-	bool
-	depends on IDE=y && !BLK_DEV_IDE_SATA && (SATA_AHCI || ATA_PIIX)
-	default y
-
 config SATA_ACPI
 	bool
 	depends on ACPI && PCI
@@ -184,7 +179,7 @@
 	  If unsure, say N.
 
 config PATA_AMD
-	tristate "AMD/NVidia PATA support (Experimental)"
+	tristate "AMD/NVidia PATA support"
 	depends on PCI
 	help
 	  This option enables support for the AMD and NVidia PATA
@@ -209,6 +204,16 @@
 
 	  If unsure, say N.
 
+config PATA_CMD640_PCI
+	tristate "CMD640 PCI PATA support (Very Experimental)"
+	depends on PCI && EXPERIMENTAL
+	help
+	  This option enables support for the CMD640 PCI IDE
+	  interface chip. Only the primary channel is currently
+	  supported.
+
+	  If unsure, say N.
+
 config PATA_CMD64X
 	tristate "CMD64x PATA support (Very Experimental)"
 	depends on PCI&& EXPERIMENTAL
@@ -273,7 +278,7 @@
 	  If unsure, say N.
 
 config PATA_HPT366
-	tristate "HPT 366/368 PATA support (Very Experimental)"
+	tristate "HPT 366/368 PATA support (Experimental)"
 	depends on PCI && EXPERIMENTAL
 	help
 	  This option enables support for the HPT 366 and 368
@@ -282,7 +287,7 @@
 	  If unsure, say N.
 
 config PATA_HPT37X
-	tristate "HPT 370/370A/371/372/374/302 PATA support (Very Experimental)"
+	tristate "HPT 370/370A/371/372/374/302 PATA support (Experimental)"
 	depends on PCI && EXPERIMENTAL
 	help
 	  This option enables support for the majority of the later HPT
@@ -309,7 +314,7 @@
 	  If unsure, say N.
 
 config PATA_ISAPNP
-	tristate "ISA Plug and Play PATA support (Very Experimental)"
+	tristate "ISA Plug and Play PATA support (Experimental)"
 	depends on EXPERIMENTAL && ISAPNP
 	help
 	  This option enables support for ISA plug & play ATA
@@ -318,8 +323,8 @@
 	  If unsure, say N.
 
 config PATA_IT821X
-	tristate "IT8211/2 PATA support (Experimental)"
-	depends on PCI && EXPERIMENTAL
+	tristate "IT8211/2 PATA support"
+	depends on PCI
 	help
 	  This option enables support for the ITE 8211 and 8212
 	  PATA controllers via the new ATA layer, including RAID
@@ -390,10 +395,10 @@
 	  If unsure, say N.
 
 config PATA_OLDPIIX
-	tristate "Intel PATA old PIIX support (Experimental)"
-	depends on PCI && EXPERIMENTAL
+	tristate "Intel PATA old PIIX support"
+	depends on PCI
 	help
-	  This option enables support for old(?) PIIX PATA support.
+	  This option enables support for early PIIX PATA support.
 
 	  If unsure, say N.
 
@@ -444,7 +449,7 @@
 	  If unsure, say N.
 
 config PATA_PDC_OLD
-	tristate "Older Promise PATA controller support (Very Experimental)"
+	tristate "Older Promise PATA controller support (Experimental)"
 	depends on PCI && EXPERIMENTAL
 	help
 	  This option enables support for the Promise 20246, 20262, 20263,
@@ -459,7 +464,7 @@
 	  Support for QDI 6500 and 6580 PATA controllers on VESA local bus.
 
 config PATA_RADISYS
-	tristate "RADISYS 82600 PATA support (Very experimental)"
+	tristate "RADISYS 82600 PATA support (Very Experimental)"
 	depends on PCI && EXPERIMENTAL
 	help
 	  This option enables support for the RADISYS 82600
@@ -477,7 +482,7 @@
 	  If unsure, say N.
 
 config PATA_SC1200
-	tristate "SC1200 PATA support (Raving Lunatic)"
+	tristate "SC1200 PATA support (Very Experimental)"
 	depends on PCI && EXPERIMENTAL
 	help
 	  This option enables support for the NatSemi/AMD SC1200 SoC
@@ -486,8 +491,8 @@
 	  If unsure, say N.
 
 config PATA_SERVERWORKS
-	tristate "SERVERWORKS OSB4/CSB5/CSB6/HT1000 PATA support (Experimental)"
-	depends on PCI && EXPERIMENTAL
+	tristate "SERVERWORKS OSB4/CSB5/CSB6/HT1000 PATA support"
+	depends on PCI
 	help
 	  This option enables support for the Serverworks OSB4/CSB5/CSB6 and
 	  HT1000 PATA controllers, via the new ATA layer.
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 13d7397..b7055e3 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -22,6 +22,7 @@
 obj-$(CONFIG_PATA_AMD)		+= pata_amd.o
 obj-$(CONFIG_PATA_ARTOP)	+= pata_artop.o
 obj-$(CONFIG_PATA_ATIIXP)	+= pata_atiixp.o
+obj-$(CONFIG_PATA_CMD640_PCI)	+= pata_cmd640.o
 obj-$(CONFIG_PATA_CMD64X)	+= pata_cmd64x.o
 obj-$(CONFIG_PATA_CS5520)	+= pata_cs5520.o
 obj-$(CONFIG_PATA_CS5530)	+= pata_cs5530.o
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index fd27227..34c5534 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -170,6 +170,10 @@
 	AHCI_FLAG_IGN_IRQ_IF_ERR	= (1 << 25), /* ignore IRQ_IF_ERR */
 	AHCI_FLAG_HONOR_PI		= (1 << 26), /* honor PORTS_IMPL */
 	AHCI_FLAG_IGN_SERR_INTERNAL	= (1 << 27), /* ignore SERR_INTERNAL */
+
+	AHCI_FLAG_COMMON		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+					  ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
+					  ATA_FLAG_SKIP_D2H_BSY,
 };
 
 struct ahci_cmd_hdr {
@@ -188,8 +192,10 @@
 };
 
 struct ahci_host_priv {
-	u32			cap;	/* cache of HOST_CAP register */
-	u32			port_map; /* cache of HOST_PORTS_IMPL reg */
+	u32			cap;		/* cap to use */
+	u32			port_map;	/* port map to use */
+	u32			saved_cap;	/* saved initial cap */
+	u32			saved_port_map;	/* saved initial port_map */
 };
 
 struct ahci_port_priv {
@@ -209,7 +215,6 @@
 static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
 static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
 static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc);
-static irqreturn_t ahci_interrupt (int irq, void *dev_instance);
 static void ahci_irq_clear(struct ata_port *ap);
 static int ahci_port_start(struct ata_port *ap);
 static void ahci_port_stop(struct ata_port *ap);
@@ -263,7 +268,6 @@
 	.qc_prep		= ahci_qc_prep,
 	.qc_issue		= ahci_qc_issue,
 
-	.irq_handler		= ahci_interrupt,
 	.irq_clear		= ahci_irq_clear,
 	.irq_on			= ata_dummy_irq_on,
 	.irq_ack		= ata_dummy_irq_ack,
@@ -298,7 +302,6 @@
 	.qc_prep		= ahci_qc_prep,
 	.qc_issue		= ahci_qc_issue,
 
-	.irq_handler		= ahci_interrupt,
 	.irq_clear		= ahci_irq_clear,
 	.irq_on			= ata_dummy_irq_on,
 	.irq_ack		= ata_dummy_irq_ack,
@@ -324,58 +327,41 @@
 static const struct ata_port_info ahci_port_info[] = {
 	/* board_ahci */
 	{
-		.sht		= &ahci_sht,
-		.flags		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
-				  ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
-				  ATA_FLAG_SKIP_D2H_BSY,
+		.flags		= AHCI_FLAG_COMMON,
 		.pio_mask	= 0x1f, /* pio0-4 */
 		.udma_mask	= 0x7f, /* udma0-6 ; FIXME */
 		.port_ops	= &ahci_ops,
 	},
 	/* board_ahci_pi */
 	{
-		.sht		= &ahci_sht,
-		.flags		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
-				  ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
-				  ATA_FLAG_SKIP_D2H_BSY | AHCI_FLAG_HONOR_PI,
+		.flags		= AHCI_FLAG_COMMON | AHCI_FLAG_HONOR_PI,
 		.pio_mask	= 0x1f, /* pio0-4 */
 		.udma_mask	= 0x7f, /* udma0-6 ; FIXME */
 		.port_ops	= &ahci_ops,
 	},
 	/* board_ahci_vt8251 */
 	{
-		.sht		= &ahci_sht,
-		.flags		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
-				  ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
-				  ATA_FLAG_SKIP_D2H_BSY |
-				  ATA_FLAG_HRST_TO_RESUME | AHCI_FLAG_NO_NCQ,
+		.flags		= AHCI_FLAG_COMMON | ATA_FLAG_HRST_TO_RESUME |
+				  AHCI_FLAG_NO_NCQ,
 		.pio_mask	= 0x1f, /* pio0-4 */
 		.udma_mask	= 0x7f, /* udma0-6 ; FIXME */
 		.port_ops	= &ahci_vt8251_ops,
 	},
 	/* board_ahci_ign_iferr */
 	{
-		.sht		= &ahci_sht,
-		.flags		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
-				  ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
-				  ATA_FLAG_SKIP_D2H_BSY |
-				  AHCI_FLAG_IGN_IRQ_IF_ERR,
+		.flags		= AHCI_FLAG_COMMON | AHCI_FLAG_IGN_IRQ_IF_ERR,
 		.pio_mask	= 0x1f, /* pio0-4 */
 		.udma_mask	= 0x7f, /* udma0-6 ; FIXME */
 		.port_ops	= &ahci_ops,
 	},
 	/* board_ahci_sb600 */
 	{
-		.sht		= &ahci_sht,
-		.flags		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
-				  ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
-				  ATA_FLAG_SKIP_D2H_BSY |
+		.flags		= AHCI_FLAG_COMMON |
 				  AHCI_FLAG_IGN_SERR_INTERNAL,
 		.pio_mask	= 0x1f, /* pio0-4 */
 		.udma_mask	= 0x7f, /* udma0-6 ; FIXME */
 		.port_ops	= &ahci_ops,
 	},
-
 };
 
 static const struct pci_device_id ahci_pci_tbl[] = {
@@ -413,11 +399,11 @@
 	  PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci_ign_iferr },
 
 	/* ATI */
-	{ PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 non-raid */
-	{ PCI_VDEVICE(ATI, 0x4381), board_ahci }, /* ATI SB600 raid */
+	{ PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 */
 
 	/* VIA */
 	{ PCI_VDEVICE(VIA, 0x3349), board_ahci_vt8251 }, /* VIA VT8251 */
+	{ PCI_VDEVICE(VIA, 0x6287), board_ahci_vt8251 }, /* VIA VT8251 */
 
 	/* NVIDIA */
 	{ PCI_VDEVICE(NVIDIA, 0x044c), board_ahci },		/* MCP65 */
@@ -471,10 +457,100 @@
 	return (cap & 0x1f) + 1;
 }
 
-static inline void __iomem *ahci_port_base(void __iomem *base,
-					   unsigned int port)
+static inline void __iomem *ahci_port_base(struct ata_port *ap)
 {
-	return base + 0x100 + (port * 0x80);
+	void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
+
+	return mmio + 0x100 + (ap->port_no * 0x80);
+}
+
+/**
+ *	ahci_save_initial_config - Save and fixup initial config values
+ *	@pdev: target PCI device
+ *	@pi: associated ATA port info
+ *	@hpriv: host private area to store config values
+ *
+ *	Some registers containing configuration info might be setup by
+ *	BIOS and might be cleared on reset.  This function saves the
+ *	initial values of those registers into @hpriv such that they
+ *	can be restored after controller reset.
+ *
+ *	If inconsistent, config values are fixed up by this function.
+ *
+ *	LOCKING:
+ *	None.
+ */
+static void ahci_save_initial_config(struct pci_dev *pdev,
+				     const struct ata_port_info *pi,
+				     struct ahci_host_priv *hpriv)
+{
+	void __iomem *mmio = pcim_iomap_table(pdev)[AHCI_PCI_BAR];
+	u32 cap, port_map;
+	int i;
+
+	/* Values prefixed with saved_ are written back to host after
+	 * reset.  Values without are used for driver operation.
+	 */
+	hpriv->saved_cap = cap = readl(mmio + HOST_CAP);
+	hpriv->saved_port_map = port_map = readl(mmio + HOST_PORTS_IMPL);
+
+	/* fixup zero port_map */
+	if (!port_map) {
+		port_map = (1 << ahci_nr_ports(hpriv->cap)) - 1;
+		dev_printk(KERN_WARNING, &pdev->dev,
+			   "PORTS_IMPL is zero, forcing 0x%x\n", port_map);
+
+		/* write the fixed up value to the PI register */
+		hpriv->saved_port_map = port_map;
+	}
+
+	/* cross check port_map and cap.n_ports */
+	if (pi->flags & AHCI_FLAG_HONOR_PI) {
+		u32 tmp_port_map = port_map;
+		int n_ports = ahci_nr_ports(cap);
+
+		for (i = 0; i < AHCI_MAX_PORTS && n_ports; i++) {
+			if (tmp_port_map & (1 << i)) {
+				n_ports--;
+				tmp_port_map &= ~(1 << i);
+			}
+		}
+
+		/* Whine if inconsistent.  No need to update cap.
+		 * port_map is used to determine number of ports.
+		 */
+		if (n_ports || tmp_port_map)
+			dev_printk(KERN_WARNING, &pdev->dev,
+				   "nr_ports (%u) and implemented port map "
+				   "(0x%x) don't match\n",
+				   ahci_nr_ports(cap), port_map);
+	} else {
+		/* fabricate port_map from cap.nr_ports */
+		port_map = (1 << ahci_nr_ports(cap)) - 1;
+	}
+
+	/* record values to use during operation */
+	hpriv->cap = cap;
+	hpriv->port_map = port_map;
+}
+
+/**
+ *	ahci_restore_initial_config - Restore initial config
+ *	@host: target ATA host
+ *
+ *	Restore initial config stored by ahci_save_initial_config().
+ *
+ *	LOCKING:
+ *	None.
+ */
+static void ahci_restore_initial_config(struct ata_host *host)
+{
+	struct ahci_host_priv *hpriv = host->private_data;
+	void __iomem *mmio = host->iomap[AHCI_PCI_BAR];
+
+	writel(hpriv->saved_cap, mmio + HOST_CAP);
+	writel(hpriv->saved_port_map, mmio + HOST_PORTS_IMPL);
+	(void) readl(mmio + HOST_PORTS_IMPL);	/* flush */
 }
 
 static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in)
@@ -511,8 +587,9 @@
 	writel(val, ap->ioaddr.scr_addr + (sc_reg * 4));
 }
 
-static void ahci_start_engine(void __iomem *port_mmio)
+static void ahci_start_engine(struct ata_port *ap)
 {
+	void __iomem *port_mmio = ahci_port_base(ap);
 	u32 tmp;
 
 	/* start DMA */
@@ -522,8 +599,9 @@
 	readl(port_mmio + PORT_CMD); /* flush */
 }
 
-static int ahci_stop_engine(void __iomem *port_mmio)
+static int ahci_stop_engine(struct ata_port *ap)
 {
+	void __iomem *port_mmio = ahci_port_base(ap);
 	u32 tmp;
 
 	tmp = readl(port_mmio + PORT_CMD);
@@ -545,19 +623,23 @@
 	return 0;
 }
 
-static void ahci_start_fis_rx(void __iomem *port_mmio, u32 cap,
-			      dma_addr_t cmd_slot_dma, dma_addr_t rx_fis_dma)
+static void ahci_start_fis_rx(struct ata_port *ap)
 {
+	void __iomem *port_mmio = ahci_port_base(ap);
+	struct ahci_host_priv *hpriv = ap->host->private_data;
+	struct ahci_port_priv *pp = ap->private_data;
 	u32 tmp;
 
 	/* set FIS registers */
-	if (cap & HOST_CAP_64)
-		writel((cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI);
-	writel(cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR);
+	if (hpriv->cap & HOST_CAP_64)
+		writel((pp->cmd_slot_dma >> 16) >> 16,
+		       port_mmio + PORT_LST_ADDR_HI);
+	writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR);
 
-	if (cap & HOST_CAP_64)
-		writel((rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI);
-	writel(rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR);
+	if (hpriv->cap & HOST_CAP_64)
+		writel((pp->rx_fis_dma >> 16) >> 16,
+		       port_mmio + PORT_FIS_ADDR_HI);
+	writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR);
 
 	/* enable FIS reception */
 	tmp = readl(port_mmio + PORT_CMD);
@@ -568,8 +650,9 @@
 	readl(port_mmio + PORT_CMD);
 }
 
-static int ahci_stop_fis_rx(void __iomem *port_mmio)
+static int ahci_stop_fis_rx(struct ata_port *ap)
 {
+	void __iomem *port_mmio = ahci_port_base(ap);
 	u32 tmp;
 
 	/* disable FIS reception */
@@ -586,14 +669,16 @@
 	return 0;
 }
 
-static void ahci_power_up(void __iomem *port_mmio, u32 cap)
+static void ahci_power_up(struct ata_port *ap)
 {
+	struct ahci_host_priv *hpriv = ap->host->private_data;
+	void __iomem *port_mmio = ahci_port_base(ap);
 	u32 cmd;
 
 	cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK;
 
 	/* spin up device */
-	if (cap & HOST_CAP_SSS) {
+	if (hpriv->cap & HOST_CAP_SSS) {
 		cmd |= PORT_CMD_SPIN_UP;
 		writel(cmd, port_mmio + PORT_CMD);
 	}
@@ -603,11 +688,13 @@
 }
 
 #ifdef CONFIG_PM
-static void ahci_power_down(void __iomem *port_mmio, u32 cap)
+static void ahci_power_down(struct ata_port *ap)
 {
+	struct ahci_host_priv *hpriv = ap->host->private_data;
+	void __iomem *port_mmio = ahci_port_base(ap);
 	u32 cmd, scontrol;
 
-	if (!(cap & HOST_CAP_SSS))
+	if (!(hpriv->cap & HOST_CAP_SSS))
 		return;
 
 	/* put device into listen mode, first set PxSCTL.DET to 0 */
@@ -622,29 +709,28 @@
 }
 #endif
 
-static void ahci_init_port(void __iomem *port_mmio, u32 cap,
-			   dma_addr_t cmd_slot_dma, dma_addr_t rx_fis_dma)
+static void ahci_init_port(struct ata_port *ap)
 {
 	/* enable FIS reception */
-	ahci_start_fis_rx(port_mmio, cap, cmd_slot_dma, rx_fis_dma);
+	ahci_start_fis_rx(ap);
 
 	/* enable DMA */
-	ahci_start_engine(port_mmio);
+	ahci_start_engine(ap);
 }
 
-static int ahci_deinit_port(void __iomem *port_mmio, u32 cap, const char **emsg)
+static int ahci_deinit_port(struct ata_port *ap, const char **emsg)
 {
 	int rc;
 
 	/* disable DMA */
-	rc = ahci_stop_engine(port_mmio);
+	rc = ahci_stop_engine(ap);
 	if (rc) {
 		*emsg = "failed to stop engine";
 		return rc;
 	}
 
 	/* disable FIS reception */
-	rc = ahci_stop_fis_rx(port_mmio);
+	rc = ahci_stop_fis_rx(ap);
 	if (rc) {
 		*emsg = "failed stop FIS RX";
 		return rc;
@@ -653,12 +739,11 @@
 	return 0;
 }
 
-static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev)
+static int ahci_reset_controller(struct ata_host *host)
 {
-	u32 cap_save, impl_save, tmp;
-
-	cap_save = readl(mmio + HOST_CAP);
-	impl_save = readl(mmio + HOST_PORTS_IMPL);
+	struct pci_dev *pdev = to_pci_dev(host->dev);
+	void __iomem *mmio = host->iomap[AHCI_PCI_BAR];
+	u32 tmp;
 
 	/* global controller reset */
 	tmp = readl(mmio + HOST_CTL);
@@ -674,7 +759,7 @@
 
 	tmp = readl(mmio + HOST_CTL);
 	if (tmp & HOST_RESET) {
-		dev_printk(KERN_ERR, &pdev->dev,
+		dev_printk(KERN_ERR, host->dev,
 			   "controller reset failed (0x%x)\n", tmp);
 		return -EIO;
 	}
@@ -683,18 +768,8 @@
 	writel(HOST_AHCI_EN, mmio + HOST_CTL);
 	(void) readl(mmio + HOST_CTL);	/* flush */
 
-	/* These write-once registers are normally cleared on reset.
-	 * Restore BIOS values... which we HOPE were present before
-	 * reset.
-	 */
-	if (!impl_save) {
-		impl_save = (1 << ahci_nr_ports(cap_save)) - 1;
-		dev_printk(KERN_WARNING, &pdev->dev,
-			   "PORTS_IMPL is zero, forcing 0x%x\n", impl_save);
-	}
-	writel(cap_save, mmio + HOST_CAP);
-	writel(impl_save, mmio + HOST_PORTS_IMPL);
-	(void) readl(mmio + HOST_PORTS_IMPL);	/* flush */
+	/* some registers might be cleared on reset.  restore initial values */
+	ahci_restore_initial_config(host);
 
 	if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
 		u16 tmp16;
@@ -708,23 +783,23 @@
 	return 0;
 }
 
-static void ahci_init_controller(void __iomem *mmio, struct pci_dev *pdev,
-				 int n_ports, unsigned int port_flags,
-				 struct ahci_host_priv *hpriv)
+static void ahci_init_controller(struct ata_host *host)
 {
+	struct pci_dev *pdev = to_pci_dev(host->dev);
+	void __iomem *mmio = host->iomap[AHCI_PCI_BAR];
 	int i, rc;
 	u32 tmp;
 
-	for (i = 0; i < n_ports; i++) {
-		void __iomem *port_mmio = ahci_port_base(mmio, i);
+	for (i = 0; i < host->n_ports; i++) {
+		struct ata_port *ap = host->ports[i];
+		void __iomem *port_mmio = ahci_port_base(ap);
 		const char *emsg = NULL;
 
-		if ((port_flags & AHCI_FLAG_HONOR_PI) &&
-		    !(hpriv->port_map & (1 << i)))
+		if (ata_port_is_dummy(ap))
 			continue;
 
 		/* make sure port is not active */
-		rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg);
+		rc = ahci_deinit_port(ap, &emsg);
 		if (rc)
 			dev_printk(KERN_WARNING, &pdev->dev,
 				   "%s (%d)\n", emsg, rc);
@@ -752,7 +827,7 @@
 
 static unsigned int ahci_dev_classify(struct ata_port *ap)
 {
-	void __iomem *port_mmio = ap->ioaddr.cmd_addr;
+	void __iomem *port_mmio = ahci_port_base(ap);
 	struct ata_taskfile tf;
 	u32 tmp;
 
@@ -802,8 +877,7 @@
 static int ahci_softreset(struct ata_port *ap, unsigned int *class)
 {
 	struct ahci_port_priv *pp = ap->private_data;
-	void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
-	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+	void __iomem *port_mmio = ahci_port_base(ap);
 	const u32 cmd_fis_len = 5; /* five dwords */
 	const char *reason = NULL;
 	struct ata_taskfile tf;
@@ -820,7 +894,7 @@
 	}
 
 	/* prepare for SRST (AHCI-1.1 10.4.1) */
-	rc = ahci_stop_engine(port_mmio);
+	rc = ahci_stop_engine(ap);
 	if (rc) {
 		reason = "failed to stop engine";
 		goto fail_restart;
@@ -840,7 +914,7 @@
 	}
 
 	/* restart engine */
-	ahci_start_engine(port_mmio);
+	ahci_start_engine(ap);
 
 	ata_tf_init(ap->device, &tf);
 	fis = pp->cmd_tbl;
@@ -899,7 +973,7 @@
 	return 0;
 
  fail_restart:
-	ahci_start_engine(port_mmio);
+	ahci_start_engine(ap);
  fail:
 	ata_port_printk(ap, KERN_ERR, "softreset failed (%s)\n", reason);
 	return rc;
@@ -910,13 +984,11 @@
 	struct ahci_port_priv *pp = ap->private_data;
 	u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
 	struct ata_taskfile tf;
-	void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
-	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
 	int rc;
 
 	DPRINTK("ENTER\n");
 
-	ahci_stop_engine(port_mmio);
+	ahci_stop_engine(ap);
 
 	/* clear D2H reception area to properly wait for D2H FIS */
 	ata_tf_init(ap->device, &tf);
@@ -925,7 +997,7 @@
 
 	rc = sata_std_hardreset(ap, class);
 
-	ahci_start_engine(port_mmio);
+	ahci_start_engine(ap);
 
 	if (rc == 0 && ata_port_online(ap))
 		*class = ahci_dev_classify(ap);
@@ -938,20 +1010,18 @@
 
 static int ahci_vt8251_hardreset(struct ata_port *ap, unsigned int *class)
 {
-	void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
-	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
 	int rc;
 
 	DPRINTK("ENTER\n");
 
-	ahci_stop_engine(port_mmio);
+	ahci_stop_engine(ap);
 
 	rc = sata_port_hardreset(ap, sata_ehc_deb_timing(&ap->eh_context));
 
 	/* vt8251 needs SError cleared for the port to operate */
 	ahci_scr_write(ap, SCR_ERROR, ahci_scr_read(ap, SCR_ERROR));
 
-	ahci_start_engine(port_mmio);
+	ahci_start_engine(ap);
 
 	DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class);
 
@@ -963,7 +1033,7 @@
 
 static void ahci_postreset(struct ata_port *ap, unsigned int *class)
 {
-	void __iomem *port_mmio = ap->ioaddr.cmd_addr;
+	void __iomem *port_mmio = ahci_port_base(ap);
 	u32 new_tmp, tmp;
 
 	ata_std_postreset(ap, class);
@@ -1131,8 +1201,7 @@
 
 static void ahci_host_intr(struct ata_port *ap)
 {
-	void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
-	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+	void __iomem *port_mmio = ap->ioaddr.cmd_addr;
 	struct ata_eh_info *ehi = &ap->eh_info;
 	struct ahci_port_priv *pp = ap->private_data;
 	u32 status, qc_active;
@@ -1283,7 +1352,7 @@
 static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
 {
 	struct ata_port *ap = qc->ap;
-	void __iomem *port_mmio = ap->ioaddr.cmd_addr;
+	void __iomem *port_mmio = ahci_port_base(ap);
 
 	if (qc->tf.protocol == ATA_PROT_NCQ)
 		writel(1 << qc->tag, port_mmio + PORT_SCR_ACT);
@@ -1295,8 +1364,7 @@
 
 static void ahci_freeze(struct ata_port *ap)
 {
-	void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
-	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+	void __iomem *port_mmio = ahci_port_base(ap);
 
 	/* turn IRQ off */
 	writel(0, port_mmio + PORT_IRQ_MASK);
@@ -1305,7 +1373,7 @@
 static void ahci_thaw(struct ata_port *ap)
 {
 	void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
-	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+	void __iomem *port_mmio = ahci_port_base(ap);
 	u32 tmp;
 
 	/* clear IRQ */
@@ -1319,13 +1387,10 @@
 
 static void ahci_error_handler(struct ata_port *ap)
 {
-	void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
-	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
-
 	if (!(ap->pflags & ATA_PFLAG_FROZEN)) {
 		/* restart engine */
-		ahci_stop_engine(port_mmio);
-		ahci_start_engine(port_mmio);
+		ahci_stop_engine(ap);
+		ahci_start_engine(ap);
 	}
 
 	/* perform recovery */
@@ -1335,13 +1400,10 @@
 
 static void ahci_vt8251_error_handler(struct ata_port *ap)
 {
-	void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
-	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
-
 	if (!(ap->pflags & ATA_PFLAG_FROZEN)) {
 		/* restart engine */
-		ahci_stop_engine(port_mmio);
-		ahci_start_engine(port_mmio);
+		ahci_stop_engine(ap);
+		ahci_start_engine(ap);
 	}
 
 	/* perform recovery */
@@ -1352,36 +1414,26 @@
 static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
 {
 	struct ata_port *ap = qc->ap;
-	void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
-	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
 
-	if (qc->flags & ATA_QCFLAG_FAILED)
-		qc->err_mask |= AC_ERR_OTHER;
-
-	if (qc->err_mask) {
+	if (qc->flags & ATA_QCFLAG_FAILED) {
 		/* make DMA engine forget about the failed command */
-		ahci_stop_engine(port_mmio);
-		ahci_start_engine(port_mmio);
+		ahci_stop_engine(ap);
+		ahci_start_engine(ap);
 	}
 }
 
 #ifdef CONFIG_PM
 static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg)
 {
-	struct ahci_host_priv *hpriv = ap->host->private_data;
-	struct ahci_port_priv *pp = ap->private_data;
-	void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
-	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
 	const char *emsg = NULL;
 	int rc;
 
-	rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg);
+	rc = ahci_deinit_port(ap, &emsg);
 	if (rc == 0)
-		ahci_power_down(port_mmio, hpriv->cap);
+		ahci_power_down(ap);
 	else {
 		ata_port_printk(ap, KERN_ERR, "%s (%d)\n", emsg, rc);
-		ahci_init_port(port_mmio, hpriv->cap,
-			       pp->cmd_slot_dma, pp->rx_fis_dma);
+		ahci_init_port(ap);
 	}
 
 	return rc;
@@ -1389,13 +1441,8 @@
 
 static int ahci_port_resume(struct ata_port *ap)
 {
-	struct ahci_port_priv *pp = ap->private_data;
-	struct ahci_host_priv *hpriv = ap->host->private_data;
-	void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
-	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
-
-	ahci_power_up(port_mmio, hpriv->cap);
-	ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma);
+	ahci_power_up(ap);
+	ahci_init_port(ap);
 
 	return 0;
 }
@@ -1423,8 +1470,6 @@
 static int ahci_pci_device_resume(struct pci_dev *pdev)
 {
 	struct ata_host *host = dev_get_drvdata(&pdev->dev);
-	struct ahci_host_priv *hpriv = host->private_data;
-	void __iomem *mmio = host->iomap[AHCI_PCI_BAR];
 	int rc;
 
 	rc = ata_pci_device_do_resume(pdev);
@@ -1432,12 +1477,11 @@
 		return rc;
 
 	if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
-		rc = ahci_reset_controller(mmio, pdev);
+		rc = ahci_reset_controller(host);
 		if (rc)
 			return rc;
 
-		ahci_init_controller(mmio, pdev, host->n_ports,
-				     host->ports[0]->flags, hpriv);
+		ahci_init_controller(host);
 	}
 
 	ata_host_resume(host);
@@ -1449,10 +1493,7 @@
 static int ahci_port_start(struct ata_port *ap)
 {
 	struct device *dev = ap->host->dev;
-	struct ahci_host_priv *hpriv = ap->host->private_data;
 	struct ahci_port_priv *pp;
-	void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
-	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
 	void *mem;
 	dma_addr_t mem_dma;
 	int rc;
@@ -1500,85 +1541,29 @@
 	ap->private_data = pp;
 
 	/* power up port */
-	ahci_power_up(port_mmio, hpriv->cap);
+	ahci_power_up(ap);
 
 	/* initialize port */
-	ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma);
+	ahci_init_port(ap);
 
 	return 0;
 }
 
 static void ahci_port_stop(struct ata_port *ap)
 {
-	struct ahci_host_priv *hpriv = ap->host->private_data;
-	void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
-	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
 	const char *emsg = NULL;
 	int rc;
 
 	/* de-initialize port */
-	rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg);
+	rc = ahci_deinit_port(ap, &emsg);
 	if (rc)
 		ata_port_printk(ap, KERN_WARNING, "%s (%d)\n", emsg, rc);
 }
 
-static void ahci_setup_port(struct ata_ioports *port, void __iomem *base,
-			    unsigned int port_idx)
+static int ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac)
 {
-	VPRINTK("ENTER, base==0x%lx, port_idx %u\n", base, port_idx);
-	base = ahci_port_base(base, port_idx);
-	VPRINTK("base now==0x%lx\n", base);
-
-	port->cmd_addr		= base;
-	port->scr_addr		= base + PORT_SCR;
-
-	VPRINTK("EXIT\n");
-}
-
-static int ahci_host_init(struct ata_probe_ent *probe_ent)
-{
-	struct ahci_host_priv *hpriv = probe_ent->private_data;
-	struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
-	void __iomem *mmio = probe_ent->iomap[AHCI_PCI_BAR];
-	unsigned int i, cap_n_ports, using_dac;
 	int rc;
 
-	rc = ahci_reset_controller(mmio, pdev);
-	if (rc)
-		return rc;
-
-	hpriv->cap = readl(mmio + HOST_CAP);
-	hpriv->port_map = readl(mmio + HOST_PORTS_IMPL);
-	cap_n_ports = ahci_nr_ports(hpriv->cap);
-
-	VPRINTK("cap 0x%x  port_map 0x%x  n_ports %d\n",
-		hpriv->cap, hpriv->port_map, cap_n_ports);
-
-	if (probe_ent->port_flags & AHCI_FLAG_HONOR_PI) {
-		unsigned int n_ports = cap_n_ports;
-		u32 port_map = hpriv->port_map;
-		int max_port = 0;
-
-		for (i = 0; i < AHCI_MAX_PORTS && n_ports; i++) {
-			if (port_map & (1 << i)) {
-				n_ports--;
-				port_map &= ~(1 << i);
-				max_port = i;
-			} else
-				probe_ent->dummy_port_mask |= 1 << i;
-		}
-
-		if (n_ports || port_map)
-			dev_printk(KERN_WARNING, &pdev->dev,
-				   "nr_ports (%u) and implemented port map "
-				   "(0x%x) don't match\n",
-				   cap_n_ports, hpriv->port_map);
-
-		probe_ent->n_ports = max_port + 1;
-	} else
-		probe_ent->n_ports = cap_n_ports;
-
-	using_dac = hpriv->cap & HOST_CAP_64;
 	if (using_dac &&
 	    !pci_set_dma_mask(pdev, DMA_64BIT_MASK)) {
 		rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);
@@ -1604,23 +1589,14 @@
 			return rc;
 		}
 	}
-
-	for (i = 0; i < probe_ent->n_ports; i++)
-		ahci_setup_port(&probe_ent->port[i], mmio, i);
-
-	ahci_init_controller(mmio, pdev, probe_ent->n_ports,
-			     probe_ent->port_flags, hpriv);
-
-	pci_set_master(pdev);
-
 	return 0;
 }
 
-static void ahci_print_info(struct ata_probe_ent *probe_ent)
+static void ahci_print_info(struct ata_host *host)
 {
-	struct ahci_host_priv *hpriv = probe_ent->private_data;
-	struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
-	void __iomem *mmio = probe_ent->iomap[AHCI_PCI_BAR];
+	struct ahci_host_priv *hpriv = host->private_data;
+	struct pci_dev *pdev = to_pci_dev(host->dev);
+	void __iomem *mmio = host->iomap[AHCI_PCI_BAR];
 	u32 vers, cap, impl, speed;
 	const char *speed_s;
 	u16 cc;
@@ -1690,11 +1666,12 @@
 static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	static int printed_version;
-	unsigned int board_idx = (unsigned int) ent->driver_data;
+	struct ata_port_info pi = ahci_port_info[ent->driver_data];
+	const struct ata_port_info *ppi[] = { &pi, NULL };
 	struct device *dev = &pdev->dev;
-	struct ata_probe_ent *probe_ent;
 	struct ahci_host_priv *hpriv;
-	int rc;
+	struct ata_host *host;
+	int i, rc;
 
 	VPRINTK("ENTER\n");
 
@@ -1703,6 +1680,7 @@
 	if (!printed_version++)
 		dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
 
+	/* acquire resources */
 	rc = pcim_enable_device(pdev);
 	if (rc)
 		return rc;
@@ -1716,44 +1694,49 @@
 	if (pci_enable_msi(pdev))
 		pci_intx(pdev, 1);
 
-	probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL);
-	if (probe_ent == NULL)
-		return -ENOMEM;
-
-	probe_ent->dev = pci_dev_to_dev(pdev);
-	INIT_LIST_HEAD(&probe_ent->node);
-
 	hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL);
 	if (!hpriv)
 		return -ENOMEM;
 
-	probe_ent->sht		= ahci_port_info[board_idx].sht;
-	probe_ent->port_flags	= ahci_port_info[board_idx].flags;
-	probe_ent->pio_mask	= ahci_port_info[board_idx].pio_mask;
-	probe_ent->udma_mask	= ahci_port_info[board_idx].udma_mask;
-	probe_ent->port_ops	= ahci_port_info[board_idx].port_ops;
+	/* save initial config */
+	ahci_save_initial_config(pdev, &pi, hpriv);
 
-       	probe_ent->irq = pdev->irq;
-       	probe_ent->irq_flags = IRQF_SHARED;
-	probe_ent->iomap = pcim_iomap_table(pdev);
-	probe_ent->private_data = hpriv;
+	/* prepare host */
+	if (!(pi.flags & AHCI_FLAG_NO_NCQ) && (hpriv->cap & HOST_CAP_NCQ))
+		pi.flags |= ATA_FLAG_NCQ;
+
+	host = ata_host_alloc_pinfo(&pdev->dev, ppi, fls(hpriv->port_map));
+	if (!host)
+		return -ENOMEM;
+	host->iomap = pcim_iomap_table(pdev);
+	host->private_data = hpriv;
+
+	for (i = 0; i < host->n_ports; i++) {
+		if (hpriv->port_map & (1 << i)) {
+			struct ata_port *ap = host->ports[i];
+			void __iomem *port_mmio = ahci_port_base(ap);
+
+			ap->ioaddr.cmd_addr = port_mmio;
+			ap->ioaddr.scr_addr = port_mmio + PORT_SCR;
+		} else
+			host->ports[i]->ops = &ata_dummy_port_ops;
+	}
 
 	/* initialize adapter */
-	rc = ahci_host_init(probe_ent);
+	rc = ahci_configure_dma_masks(pdev, hpriv->cap & HOST_CAP_64);
 	if (rc)
 		return rc;
 
-	if (!(probe_ent->port_flags & AHCI_FLAG_NO_NCQ) &&
-	    (hpriv->cap & HOST_CAP_NCQ))
-		probe_ent->port_flags |= ATA_FLAG_NCQ;
+	rc = ahci_reset_controller(host);
+	if (rc)
+		return rc;
 
-	ahci_print_info(probe_ent);
+	ahci_init_controller(host);
+	ahci_print_info(host);
 
-	if (!ata_device_add(probe_ent))
-		return -ENODEV;
-
-	devm_kfree(dev, probe_ent);
-	return 0;
+	pci_set_master(pdev);
+	return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED,
+				 &ahci_sht);
 }
 
 static int __init ahci_init(void)
diff --git a/drivers/ata/ata_generic.c b/drivers/ata/ata_generic.c
index d8e7988..92a491d 100644
--- a/drivers/ata/ata_generic.c
+++ b/drivers/ata/ata_generic.c
@@ -33,35 +33,6 @@
  */
 
 /**
- *	generic_pre_reset		-	probe begin
- *	@ap: ATA port
- *
- *	Set up cable type and use generic probe init
- */
-
-static int generic_pre_reset(struct ata_port *ap)
-{
-	ap->cbl = ATA_CBL_PATA80;
-	return ata_std_prereset(ap);
-}
-
-
-/**
- *	generic_error_handler - Probe specified port on PATA host controller
- *	@ap: Port to probe
- *	@classes:
- *
- *	LOCKING:
- *	None (inherited from caller).
- */
-
-
-static void generic_error_handler(struct ata_port *ap)
-{
-	ata_bmdma_drive_eh(ap, generic_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
-/**
  *	generic_set_mode	-	mode setting
  *	@ap: interface to set up
  *	@unused: returned device on error
@@ -144,8 +115,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= generic_error_handler,
+	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_unknown,
 
 	.qc_prep 	= ata_qc_prep,
 	.qc_issue	= ata_qc_issue_prot,
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c
index b952c58..55d306a 100644
--- a/drivers/ata/ata_piix.c
+++ b/drivers/ata/ata_piix.c
@@ -93,7 +93,7 @@
 #include <linux/libata.h>
 
 #define DRV_NAME	"ata_piix"
-#define DRV_VERSION	"2.10ac1"
+#define DRV_VERSION	"2.11"
 
 enum {
 	PIIX_IOCFG		= 0x54, /* IDE I/O configuration register */
@@ -155,11 +155,11 @@
 static int piix_init_one (struct pci_dev *pdev,
 				    const struct pci_device_id *ent);
 static void piix_pata_error_handler(struct ata_port *ap);
-static void ich_pata_error_handler(struct ata_port *ap);
 static void piix_sata_error_handler(struct ata_port *ap);
 static void piix_set_piomode (struct ata_port *ap, struct ata_device *adev);
 static void piix_set_dmamode (struct ata_port *ap, struct ata_device *adev);
 static void ich_set_dmamode (struct ata_port *ap, struct ata_device *adev);
+static int ich_pata_cable_detect(struct ata_port *ap);
 
 static unsigned int in_module_init = 1;
 
@@ -305,6 +305,7 @@
 	.thaw			= ata_bmdma_thaw,
 	.error_handler		= piix_pata_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
+	.cable_detect		= ata_cable_40wire,
 
 	.irq_handler		= ata_interrupt,
 	.irq_clear		= ata_bmdma_irq_clear,
@@ -336,8 +337,9 @@
 
 	.freeze			= ata_bmdma_freeze,
 	.thaw			= ata_bmdma_thaw,
-	.error_handler		= ich_pata_error_handler,
+	.error_handler		= piix_pata_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
+	.cable_detect		= ich_pata_cable_detect,
 
 	.irq_handler		= ata_interrupt,
 	.irq_clear		= ata_bmdma_irq_clear,
@@ -580,12 +582,13 @@
 	/* devid, subvendor, subdev */
 	{ 0x27DF, 0x0005, 0x0280 },	/* ICH7 on Acer 5602WLMi */
 	{ 0x27DF, 0x1025, 0x0110 },	/* ICH7 on Acer 3682WLMi */
+	{ 0x27DF, 0x1043, 0x1267 },	/* ICH7 on Asus W5F */
 	/* end marker */
 	{ 0, }
 };
 
 /**
- *	piix_pata_cbl_detect - Probe host controller cable detect info
+ *	ich_pata_cable_detect - Probe host controller cable detect info
  *	@ap: Port for which cable detect info is desired
  *
  *	Read 80c cable indicator from ATA PCI device's PCI config
@@ -595,23 +598,18 @@
  *	None (inherited from caller).
  */
 
-static void ich_pata_cbl_detect(struct ata_port *ap)
+static int ich_pata_cable_detect(struct ata_port *ap)
 {
 	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
 	const struct ich_laptop *lap = &ich_laptop[0];
 	u8 tmp, mask;
 
-	/* no 80c support in host controller? */
-	if ((ap->udma_mask & ~ATA_UDMA_MASK_40C) == 0)
-		goto cbl40;
-
 	/* Check for specials - Acer Aspire 5602WLMi */
 	while (lap->device) {
 		if (lap->device == pdev->device &&
 		    lap->subvendor == pdev->subsystem_vendor &&
 		    lap->subdevice == pdev->subsystem_device) {
-			ap->cbl = ATA_CBL_PATA40_SHORT;
-		    	return;
+			return ATA_CBL_PATA40_SHORT;
 		}
 		lap++;
 	}
@@ -620,20 +618,14 @@
 	mask = ap->port_no == 0 ? PIIX_80C_PRI : PIIX_80C_SEC;
 	pci_read_config_byte(pdev, PIIX_IOCFG, &tmp);
 	if ((tmp & mask) == 0)
-		goto cbl40;
-
-	ap->cbl = ATA_CBL_PATA80;
-	return;
-
-cbl40:
-	ap->cbl = ATA_CBL_PATA40;
+		return ATA_CBL_PATA40;
+	return ATA_CBL_PATA80;
 }
 
 /**
  *	piix_pata_prereset - prereset for PATA host controller
  *	@ap: Target port
  *
- *
  *	LOCKING:
  *	None (inherited from caller).
  */
@@ -643,8 +635,6 @@
 
 	if (!pci_test_config_bits(pdev, &piix_enable_bits[ap->port_no]))
 		return -ENOENT;
-
-	ap->cbl = ATA_CBL_PATA40;
 	return ata_std_prereset(ap);
 }
 
@@ -655,30 +645,6 @@
 }
 
 
-/**
- *	ich_pata_prereset - prereset for PATA host controller
- *	@ap: Target port
- *
- *
- *	LOCKING:
- *	None (inherited from caller).
- */
-static int ich_pata_prereset(struct ata_port *ap)
-{
-	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
-
-	if (!pci_test_config_bits(pdev, &piix_enable_bits[ap->port_no]))
-		return -ENOENT;
-	ich_pata_cbl_detect(ap);
-	return ata_std_prereset(ap);
-}
-
-static void ich_pata_error_handler(struct ata_port *ap)
-{
-	ata_bmdma_drive_eh(ap, ich_pata_prereset, ata_std_softreset, NULL,
-			   ata_std_postreset);
-}
-
 static void piix_sata_error_handler(struct ata_port *ap)
 {
 	ata_bmdma_drive_eh(ap, ata_std_prereset, ata_std_softreset, NULL,
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 0abd72d..ca67484 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -72,7 +72,7 @@
 static unsigned int ata_dev_set_xfermode(struct ata_device *dev);
 static void ata_dev_xfermask(struct ata_device *dev);
 
-static unsigned int ata_print_id = 1;
+unsigned int ata_print_id = 1;
 static struct workqueue_struct *ata_wq;
 
 struct workqueue_struct *ata_aux_wq;
@@ -89,6 +89,10 @@
 module_param_named(fua, libata_fua, int, 0444);
 MODULE_PARM_DESC(fua, "FUA support (0=off, 1=on)");
 
+static int ata_ignore_hpa = 0;
+module_param_named(ignore_hpa, ata_ignore_hpa, int, 0644);
+MODULE_PARM_DESC(ignore_hpa, "Ignore HPA limit (0=keep BIOS limits, 1=ignore limits, using full disk)");
+
 static int ata_probe_timeout = ATA_TMOUT_INTERNAL / HZ;
 module_param(ata_probe_timeout, int, 0444);
 MODULE_PARM_DESC(ata_probe_timeout, "Set ATA probing timeout (seconds)");
@@ -808,6 +812,205 @@
 	*p = '\0';
 }
 
+static u64 ata_tf_to_lba48(struct ata_taskfile *tf)
+{
+	u64 sectors = 0;
+
+	sectors |= ((u64)(tf->hob_lbah & 0xff)) << 40;
+	sectors |= ((u64)(tf->hob_lbam & 0xff)) << 32;
+	sectors |= (tf->hob_lbal & 0xff) << 24;
+	sectors |= (tf->lbah & 0xff) << 16;
+	sectors |= (tf->lbam & 0xff) << 8;
+	sectors |= (tf->lbal & 0xff);
+
+	return ++sectors;
+}
+
+static u64 ata_tf_to_lba(struct ata_taskfile *tf)
+{
+	u64 sectors = 0;
+
+	sectors |= (tf->device & 0x0f) << 24;
+	sectors |= (tf->lbah & 0xff) << 16;
+	sectors |= (tf->lbam & 0xff) << 8;
+	sectors |= (tf->lbal & 0xff);
+
+	return ++sectors;
+}
+
+/**
+ *	ata_read_native_max_address_ext	-	LBA48 native max query
+ *	@dev: Device to query
+ *
+ *	Perform an LBA48 size query upon the device in question. Return the
+ *	actual LBA48 size or zero if the command fails.
+ */
+
+static u64 ata_read_native_max_address_ext(struct ata_device *dev)
+{
+	unsigned int err;
+	struct ata_taskfile tf;
+
+	ata_tf_init(dev, &tf);
+
+	tf.command = ATA_CMD_READ_NATIVE_MAX_EXT;
+	tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48 | ATA_TFLAG_ISADDR;
+	tf.protocol |= ATA_PROT_NODATA;
+	tf.device |= 0x40;
+
+	err = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0);
+	if (err)
+		return 0;
+
+	return ata_tf_to_lba48(&tf);
+}
+
+/**
+ *	ata_read_native_max_address	-	LBA28 native max query
+ *	@dev: Device to query
+ *
+ *	Performa an LBA28 size query upon the device in question. Return the
+ *	actual LBA28 size or zero if the command fails.
+ */
+
+static u64 ata_read_native_max_address(struct ata_device *dev)
+{
+	unsigned int err;
+	struct ata_taskfile tf;
+
+	ata_tf_init(dev, &tf);
+
+	tf.command = ATA_CMD_READ_NATIVE_MAX;
+	tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR;
+	tf.protocol |= ATA_PROT_NODATA;
+	tf.device |= 0x40;
+
+	err = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0);
+	if (err)
+		return 0;
+
+	return ata_tf_to_lba(&tf);
+}
+
+/**
+ *	ata_set_native_max_address_ext	-	LBA48 native max set
+ *	@dev: Device to query
+ *
+ *	Perform an LBA48 size set max upon the device in question. Return the
+ *	actual LBA48 size or zero if the command fails.
+ */
+
+static u64 ata_set_native_max_address_ext(struct ata_device *dev, u64 new_sectors)
+{
+	unsigned int err;
+	struct ata_taskfile tf;
+
+	new_sectors--;
+
+	ata_tf_init(dev, &tf);
+
+	tf.command = ATA_CMD_SET_MAX_EXT;
+	tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48 | ATA_TFLAG_ISADDR;
+	tf.protocol |= ATA_PROT_NODATA;
+	tf.device |= 0x40;
+
+	tf.lbal = (new_sectors >> 0) & 0xff;
+	tf.lbam = (new_sectors >> 8) & 0xff;
+	tf.lbah = (new_sectors >> 16) & 0xff;
+
+	tf.hob_lbal = (new_sectors >> 24) & 0xff;
+	tf.hob_lbam = (new_sectors >> 32) & 0xff;
+	tf.hob_lbah = (new_sectors >> 40) & 0xff;
+
+	err = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0);
+	if (err)
+		return 0;
+
+	return ata_tf_to_lba48(&tf);
+}
+
+/**
+ *	ata_set_native_max_address	-	LBA28 native max set
+ *	@dev: Device to query
+ *
+ *	Perform an LBA28 size set max upon the device in question. Return the
+ *	actual LBA28 size or zero if the command fails.
+ */
+
+static u64 ata_set_native_max_address(struct ata_device *dev, u64 new_sectors)
+{
+	unsigned int err;
+	struct ata_taskfile tf;
+
+	new_sectors--;
+
+	ata_tf_init(dev, &tf);
+
+	tf.command = ATA_CMD_SET_MAX;
+	tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR;
+	tf.protocol |= ATA_PROT_NODATA;
+
+	tf.lbal = (new_sectors >> 0) & 0xff;
+	tf.lbam = (new_sectors >> 8) & 0xff;
+	tf.lbah = (new_sectors >> 16) & 0xff;
+	tf.device |= ((new_sectors >> 24) & 0x0f) | 0x40;
+
+	err = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0);
+	if (err)
+		return 0;
+
+	return ata_tf_to_lba(&tf);
+}
+
+/**
+ *	ata_hpa_resize		-	Resize a device with an HPA set
+ *	@dev: Device to resize
+ *
+ *	Read the size of an LBA28 or LBA48 disk with HPA features and resize
+ *	it if required to the full size of the media. The caller must check
+ *	the drive has the HPA feature set enabled.
+ */
+
+static u64 ata_hpa_resize(struct ata_device *dev)
+{
+	u64 sectors = dev->n_sectors;
+	u64 hpa_sectors;
+	
+	if (ata_id_has_lba48(dev->id))
+		hpa_sectors = ata_read_native_max_address_ext(dev);
+	else
+		hpa_sectors = ata_read_native_max_address(dev);
+
+	/* if no hpa, both should be equal */
+	ata_dev_printk(dev, KERN_INFO, "%s 1: sectors = %lld, "
+				"hpa_sectors = %lld\n",
+		__FUNCTION__, (long long)sectors, (long long)hpa_sectors);
+
+	if (hpa_sectors > sectors) {
+		ata_dev_printk(dev, KERN_INFO,
+			"Host Protected Area detected:\n"
+			"\tcurrent size: %lld sectors\n"
+			"\tnative size: %lld sectors\n",
+			(long long)sectors, (long long)hpa_sectors);
+
+		if (ata_ignore_hpa) {
+			if (ata_id_has_lba48(dev->id))
+				hpa_sectors = ata_set_native_max_address_ext(dev, hpa_sectors);
+			else
+				hpa_sectors = ata_set_native_max_address(dev,
+								hpa_sectors);
+
+			if (hpa_sectors) {
+				ata_dev_printk(dev, KERN_INFO, "native size "
+					"increased to %lld sectors\n",
+					(long long)hpa_sectors);
+				return hpa_sectors;
+			}
+		}
+	}
+	return sectors;
+}
+
 static u64 ata_id_n_sectors(const u16 *id)
 {
 	if (ata_id_has_lba(id)) {
@@ -1270,12 +1473,16 @@
 	if (ap->ops->post_internal_cmd)
 		ap->ops->post_internal_cmd(qc);
 
-	if ((qc->flags & ATA_QCFLAG_FAILED) && !qc->err_mask) {
-		if (ata_msg_warn(ap))
-			ata_dev_printk(dev, KERN_WARNING,
-				"zero err_mask for failed "
-				"internal command, assuming AC_ERR_OTHER\n");
-		qc->err_mask |= AC_ERR_OTHER;
+	/* perform minimal error analysis */
+	if (qc->flags & ATA_QCFLAG_FAILED) {
+		if (qc->result_tf.command & (ATA_ERR | ATA_DF))
+			qc->err_mask |= AC_ERR_DEV;
+
+		if (!qc->err_mask)
+			qc->err_mask |= AC_ERR_OTHER;
+
+		if (qc->err_mask & ~AC_ERR_OTHER)
+			qc->err_mask &= ~AC_ERR_OTHER;
 	}
 
 	/* finish up */
@@ -1379,30 +1586,44 @@
  *	Check if the current speed of the device requires IORDY. Used
  *	by various controllers for chip configuration.
  */
-
+ 
 unsigned int ata_pio_need_iordy(const struct ata_device *adev)
 {
-	int pio;
-	int speed = adev->pio_mode - XFER_PIO_0;
-
-	if (speed < 2)
+	/* Controller doesn't support  IORDY. Probably a pointless check
+	   as the caller should know this */
+	if (adev->ap->flags & ATA_FLAG_NO_IORDY)
 		return 0;
-	if (speed > 2)
+	/* PIO3 and higher it is mandatory */
+	if (adev->pio_mode > XFER_PIO_2)
 		return 1;
+	/* We turn it on when possible */
+	if (ata_id_has_iordy(adev->id))
+		return 1;
+	return 0;
+}
 
+/**
+ *	ata_pio_mask_no_iordy	-	Return the non IORDY mask
+ *	@adev: ATA device
+ *
+ *	Compute the highest mode possible if we are not using iordy. Return
+ *	-1 if no iordy mode is available.
+ */
+ 
+static u32 ata_pio_mask_no_iordy(const struct ata_device *adev)
+{
 	/* If we have no drive specific rule, then PIO 2 is non IORDY */
-
 	if (adev->id[ATA_ID_FIELD_VALID] & 2) {	/* EIDE */
-		pio = adev->id[ATA_ID_EIDE_PIO];
+		u16 pio = adev->id[ATA_ID_EIDE_PIO];
 		/* Is the speed faster than the drive allows non IORDY ? */
 		if (pio) {
 			/* This is cycle times not frequency - watch the logic! */
 			if (pio > 240)	/* PIO2 is 240nS per cycle */
-				return 1;
-			return 0;
+				return 3 << ATA_SHIFT_PIO;
+			return 7 << ATA_SHIFT_PIO;
 		}
 	}
-	return 0;
+	return 3 << ATA_SHIFT_PIO;
 }
 
 /**
@@ -1431,13 +1652,13 @@
 	struct ata_taskfile tf;
 	unsigned int err_mask = 0;
 	const char *reason;
+	int tried_spinup = 0;
 	int rc;
 
 	if (ata_msg_ctl(ap))
 		ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER\n", __FUNCTION__);
 
 	ata_dev_select(ap, dev->devno, 1, 1); /* select device 0/1 */
-
  retry:
 	ata_tf_init(dev, &tf);
 
@@ -1494,6 +1715,32 @@
 			goto err_out;
 	}
 
+	if (!tried_spinup && (id[2] == 0x37c8 || id[2] == 0x738c)) {
+		tried_spinup = 1;
+		/*
+		 * Drive powered-up in standby mode, and requires a specific
+		 * SET_FEATURES spin-up subcommand before it will accept
+		 * anything other than the original IDENTIFY command.
+		 */
+		ata_tf_init(dev, &tf);
+		tf.command = ATA_CMD_SET_FEATURES;
+		tf.feature = SETFEATURES_SPINUP;
+		tf.protocol = ATA_PROT_NODATA;
+		tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+		err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0);
+		if (err_mask) {
+			rc = -EIO;
+			reason = "SPINUP failed";
+			goto err_out;
+		}
+		/*
+		 * If the drive initially returned incomplete IDENTIFY info,
+		 * we now must reissue the IDENTIFY command.
+		 */
+		if (id[2] == 0x37c8)
+			goto retry;
+	}
+
 	if ((flags & ATA_READID_POSTRESET) && class == ATA_DEV_ATA) {
 		/*
 		 * The exact sequence expected by certain pre-ATA4 drives is:
@@ -1560,20 +1807,6 @@
 		snprintf(desc, desc_sz, "NCQ (depth %d/%d)", hdepth, ddepth);
 }
 
-static void ata_set_port_max_cmd_len(struct ata_port *ap)
-{
-	int i;
-
-	if (ap->scsi_host) {
-		unsigned int len = 0;
-
-		for (i = 0; i < ATA_MAX_DEVICES; i++)
-			len = max(len, ap->device[i].cdb_len);
-
-		ap->scsi_host->max_cmd_len = len;
-	}
-}
-
 /**
  *	ata_dev_configure - Configure the specified ATA/ATAPI device
  *	@dev: Target device to configure
@@ -1658,6 +1891,7 @@
 			snprintf(revbuf, 7, "ATA-%d",  ata_id_major_version(id));
 
 		dev->n_sectors = ata_id_n_sectors(id);
+		dev->n_sectors_boot = dev->n_sectors;
 
 		/* SCSI only uses 4-char revisions, dump full 8 chars from ATA */
 		ata_id_c_string(dev->id, fwrevbuf, ATA_ID_FW_REV,
@@ -1684,6 +1918,9 @@
 					dev->flags |= ATA_DFLAG_FLUSH_EXT;
 			}
 
+			if (ata_id_hpa_enabled(dev->id))
+				dev->n_sectors = ata_hpa_resize(dev);
+
 			/* config NCQ */
 			ata_dev_config_ncq(dev, ncq_desc, sizeof(ncq_desc));
 
@@ -1773,8 +2010,6 @@
 		}
 	}
 
-	ata_set_port_max_cmd_len(ap);
-
 	/* limit bridge transfers to udma5, 200 sectors */
 	if (ata_dev_knobble(dev)) {
 		if (ata_msg_drv(ap) && print_info)
@@ -1785,14 +2020,15 @@
 	}
 
 	if (ata_device_blacklisted(dev) & ATA_HORKAGE_MAX_SEC_128)
-		dev->max_sectors = min(ATA_MAX_SECTORS_128, dev->max_sectors);
+		dev->max_sectors = min_t(unsigned int, ATA_MAX_SECTORS_128,
+					 dev->max_sectors);
 
 	/* limit ATAPI DMA to R/W commands only */
 	if (ata_device_blacklisted(dev) & ATA_HORKAGE_DMA_RW_ONLY)
 		dev->horkage |= ATA_HORKAGE_DMA_RW_ONLY;
 
 	if (ap->ops->dev_config)
-		ap->ops->dev_config(ap, dev);
+		ap->ops->dev_config(dev);
 
 	if (ata_msg_probe(ap))
 		ata_dev_printk(dev, KERN_DEBUG, "%s: EXIT, drv_stat = 0x%x\n",
@@ -1807,6 +2043,56 @@
 }
 
 /**
+ *	ata_cable_40wire	-	return 40 wire cable type
+ *	@ap: port
+ *
+ *	Helper method for drivers which want to hardwire 40 wire cable
+ *	detection.
+ */
+
+int ata_cable_40wire(struct ata_port *ap)
+{
+	return ATA_CBL_PATA40;
+}
+
+/**
+ *	ata_cable_80wire	-	return 80 wire cable type
+ *	@ap: port
+ *
+ *	Helper method for drivers which want to hardwire 80 wire cable
+ *	detection.
+ */
+
+int ata_cable_80wire(struct ata_port *ap)
+{
+	return ATA_CBL_PATA80;
+}
+
+/**
+ *	ata_cable_unknown	-	return unknown PATA cable.
+ *	@ap: port
+ *
+ *	Helper method for drivers which have no PATA cable detection.
+ */
+
+int ata_cable_unknown(struct ata_port *ap)
+{
+	return ATA_CBL_PATA_UNK;
+}
+
+/**
+ *	ata_cable_sata	-	return SATA cable type
+ *	@ap: port
+ *
+ *	Helper method for drivers which have SATA cables
+ */
+
+int ata_cable_sata(struct ata_port *ap)
+{
+	return ATA_CBL_SATA;
+}
+
+/**
  *	ata_bus_probe - Reset and probe ATA bus
  *	@ap: Bus to probe
  *
@@ -1876,6 +2162,10 @@
 			goto fail;
 	}
 
+	/* Now ask for the cable type as PDIAG- should have been released */
+	if (ap->ops->cable_detect)
+		ap->cbl = ap->ops->cable_detect(ap);
+
 	/* After the identify sequence we can now set up the devices. We do
 	   this in the normal order so that the user doesn't get confused */
 
@@ -1958,7 +2248,7 @@
  *	LOCKING:
  *	None.
  */
-static void sata_print_link_status(struct ata_port *ap)
+void sata_print_link_status(struct ata_port *ap)
 {
 	u32 sstatus, scontrol, tmp;
 
@@ -2352,6 +2642,12 @@
 		t->active += (t->cycle - (t->active + t->recover)) / 2;
 		t->recover = t->cycle - t->active;
 	}
+	
+	/* In a few cases quantisation may produce enough errors to
+	   leave t->cycle too low for the sum of active and recovery
+	   if so we must correct this */
+	if (t->active + t->recover > t->cycle)
+		t->cycle = t->active + t->recover;
 
 	return 0;
 }
@@ -2481,12 +2777,13 @@
 }
 
 /**
- *	ata_set_mode - Program timings and issue SET FEATURES - XFER
+ *	ata_do_set_mode - Program timings and issue SET FEATURES - XFER
  *	@ap: port on which timings will be programmed
  *	@r_failed_dev: out paramter for failed device
  *
- *	Set ATA device disk transfer mode (PIO3, UDMA6, etc.).  If
- *	ata_set_mode() fails, pointer to the failing device is
+ *	Standard implementation of the function used to tune and set
+ *	ATA device disk transfer mode (PIO3, UDMA6, etc.).  If
+ *	ata_dev_set_mode() fails, pointer to the failing device is
  *	returned in @r_failed_dev.
  *
  *	LOCKING:
@@ -2495,14 +2792,12 @@
  *	RETURNS:
  *	0 on success, negative errno otherwise
  */
-int ata_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev)
+
+int ata_do_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev)
 {
 	struct ata_device *dev;
 	int i, rc = 0, used_dma = 0, found = 0;
 
-	/* has private set_mode? */
-	if (ap->ops->set_mode)
-		return ap->ops->set_mode(ap, r_failed_dev);
 
 	/* step 1: calculate xfer_mask */
 	for (i = 0; i < ATA_MAX_DEVICES; i++) {
@@ -2587,6 +2882,29 @@
 }
 
 /**
+ *	ata_set_mode - Program timings and issue SET FEATURES - XFER
+ *	@ap: port on which timings will be programmed
+ *	@r_failed_dev: out paramter for failed device
+ *
+ *	Set ATA device disk transfer mode (PIO3, UDMA6, etc.).  If
+ *	ata_set_mode() fails, pointer to the failing device is
+ *	returned in @r_failed_dev.
+ *
+ *	LOCKING:
+ *	PCI/etc. bus probe sem.
+ *
+ *	RETURNS:
+ *	0 on success, negative errno otherwise
+ */
+int ata_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev)
+{
+	/* has private set_mode? */
+	if (ap->ops->set_mode)
+		return ap->ops->set_mode(ap, r_failed_dev);
+	return ata_do_set_mode(ap, r_failed_dev);
+}
+
+/**
  *	ata_tf_to_host - issue ATA taskfile to host controller
  *	@ap: port to which command is being issued
  *	@tf: ATA taskfile register set
@@ -3267,6 +3585,11 @@
 			       "%llu != %llu\n",
 			       (unsigned long long)dev->n_sectors,
 			       (unsigned long long)new_n_sectors);
+		/* Are we the boot time size - if so we appear to be the
+		   same disk at this point and our HPA got reapplied */
+		if (ata_ignore_hpa && dev->n_sectors_boot == new_n_sectors 
+		    && ata_id_hpa_enabled(new_id))
+			return 1;
 		return 0;
 	}
 
@@ -3441,19 +3764,7 @@
 	xfer_mask = ata_pack_xfermask(ap->pio_mask,
 				      ap->mwdma_mask, ap->udma_mask);
 
-	/* Apply cable rule here.  Don't apply it early because when
-	 * we handle hot plug the cable type can itself change.
-	 */
-	if (ap->cbl == ATA_CBL_PATA40)
-		xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA);
-	/* Apply drive side cable rule. Unknown or 80 pin cables reported
-	 * host side are checked drive side as well. Cases where we know a
-	 * 40wire cable is used safely for 80 are not checked here.
-	 */
-        if (ata_drive_40wire(dev->id) && (ap->cbl == ATA_CBL_PATA_UNK || ap->cbl == ATA_CBL_PATA80))
-		xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA);
-
-
+	/* drive modes available */
 	xfer_mask &= ata_pack_xfermask(dev->pio_mask,
 				       dev->mwdma_mask, dev->udma_mask);
 	xfer_mask &= ata_id_xfermask(dev->id);
@@ -3482,8 +3793,30 @@
 			       "other device, disabling DMA\n");
 	}
 
+	if (ap->flags & ATA_FLAG_NO_IORDY)
+		xfer_mask &= ata_pio_mask_no_iordy(dev);
+
 	if (ap->ops->mode_filter)
-		xfer_mask = ap->ops->mode_filter(ap, dev, xfer_mask);
+		xfer_mask = ap->ops->mode_filter(dev, xfer_mask);
+
+	/* Apply cable rule here.  Don't apply it early because when
+	 * we handle hot plug the cable type can itself change.
+	 * Check this last so that we know if the transfer rate was
+	 * solely limited by the cable.
+	 * Unknown or 80 wire cables reported host side are checked
+	 * drive side as well. Cases where we know a 40wire cable
+	 * is used safely for 80 are not checked here.
+	 */
+	if (xfer_mask & (0xF8 << ATA_SHIFT_UDMA))
+		/* UDMA/44 or higher would be available */
+		if((ap->cbl == ATA_CBL_PATA40) ||
+   		    (ata_drive_40wire(dev->id) &&
+		     (ap->cbl == ATA_CBL_PATA_UNK ||
+                     ap->cbl == ATA_CBL_PATA80))) {
+		      	ata_dev_printk(dev, KERN_WARNING,
+				 "limited to UDMA/33 due to 40-wire cable\n");
+			xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA);
+		}
 
 	ata_unpack_xfermask(xfer_mask, &dev->pio_mask,
 			    &dev->mwdma_mask, &dev->udma_mask);
@@ -4022,10 +4355,10 @@
 
 
 /**
- *	ata_pio_sector - Transfer ATA_SECT_SIZE (512 bytes) of data.
+ *	ata_pio_sector - Transfer a sector of data.
  *	@qc: Command on going
  *
- *	Transfer ATA_SECT_SIZE of data from/to the ATA device.
+ *	Transfer qc->sect_size bytes of data from/to the ATA device.
  *
  *	LOCKING:
  *	Inherited from caller.
@@ -4040,7 +4373,7 @@
 	unsigned int offset;
 	unsigned char *buf;
 
-	if (qc->curbytes == qc->nbytes - ATA_SECT_SIZE)
+	if (qc->curbytes == qc->nbytes - qc->sect_size)
 		ap->hsm_task_state = HSM_ST_LAST;
 
 	page = sg[qc->cursg].page;
@@ -4060,17 +4393,17 @@
 		buf = kmap_atomic(page, KM_IRQ0);
 
 		/* do the actual data transfer */
-		ap->ops->data_xfer(qc->dev, buf + offset, ATA_SECT_SIZE, do_write);
+		ap->ops->data_xfer(qc->dev, buf + offset, qc->sect_size, do_write);
 
 		kunmap_atomic(buf, KM_IRQ0);
 		local_irq_restore(flags);
 	} else {
 		buf = page_address(page);
-		ap->ops->data_xfer(qc->dev, buf + offset, ATA_SECT_SIZE, do_write);
+		ap->ops->data_xfer(qc->dev, buf + offset, qc->sect_size, do_write);
 	}
 
-	qc->curbytes += ATA_SECT_SIZE;
-	qc->cursg_ofs += ATA_SECT_SIZE;
+	qc->curbytes += qc->sect_size;
+	qc->cursg_ofs += qc->sect_size;
 
 	if (qc->cursg_ofs == (&sg[qc->cursg])->length) {
 		qc->cursg++;
@@ -4079,10 +4412,10 @@
 }
 
 /**
- *	ata_pio_sectors - Transfer one or many 512-byte sectors.
+ *	ata_pio_sectors - Transfer one or many sectors.
  *	@qc: Command on going
  *
- *	Transfer one or many ATA_SECT_SIZE of data from/to the
+ *	Transfer one or many sectors of data from/to the
  *	ATA device for the DRQ request.
  *
  *	LOCKING:
@@ -4097,7 +4430,7 @@
 
 		WARN_ON(qc->dev->multi_count == 0);
 
-		nsect = min((qc->nbytes - qc->curbytes) / ATA_SECT_SIZE,
+		nsect = min((qc->nbytes - qc->curbytes) / qc->sect_size,
 			    qc->dev->multi_count);
 		while (nsect--)
 			ata_pio_sector(qc);
@@ -5577,42 +5910,35 @@
 }
 
 /**
- *	ata_port_init - Initialize an ata_port structure
- *	@ap: Structure to initialize
- *	@host: Collection of hosts to which @ap belongs
- *	@ent: Probe information provided by low-level driver
- *	@port_no: Port number associated with this ata_port
+ *	ata_port_alloc - allocate and initialize basic ATA port resources
+ *	@host: ATA host this allocated port belongs to
  *
- *	Initialize a new ata_port structure.
+ *	Allocate and initialize basic ATA port resources.
+ *
+ *	RETURNS:
+ *	Allocate ATA port on success, NULL on failure.
  *
  *	LOCKING:
- *	Inherited from caller.
+ *	Inherited from calling layer (may sleep).
  */
-void ata_port_init(struct ata_port *ap, struct ata_host *host,
-		   const struct ata_probe_ent *ent, unsigned int port_no)
+struct ata_port *ata_port_alloc(struct ata_host *host)
 {
+	struct ata_port *ap;
 	unsigned int i;
 
+	DPRINTK("ENTER\n");
+
+	ap = kzalloc(sizeof(*ap), GFP_KERNEL);
+	if (!ap)
+		return NULL;
+
 	ap->lock = &host->lock;
 	ap->flags = ATA_FLAG_DISABLED;
-	ap->print_id = ata_print_id++;
+	ap->print_id = -1;
 	ap->ctl = ATA_DEVCTL_OBS;
 	ap->host = host;
-	ap->dev = ent->dev;
-	ap->port_no = port_no;
-	if (port_no == 1 && ent->pinfo2) {
-		ap->pio_mask = ent->pinfo2->pio_mask;
-		ap->mwdma_mask = ent->pinfo2->mwdma_mask;
-		ap->udma_mask = ent->pinfo2->udma_mask;
-		ap->flags |= ent->pinfo2->flags;
-		ap->ops = ent->pinfo2->port_ops;
-	} else {
-		ap->pio_mask = ent->pio_mask;
-		ap->mwdma_mask = ent->mwdma_mask;
-		ap->udma_mask = ent->udma_mask;
-		ap->flags |= ent->port_flags;
-		ap->ops = ent->port_ops;
-	}
+	ap->dev = host->dev;
+
 	ap->hw_sata_spd_limit = UINT_MAX;
 	ap->active_tag = ATA_TAG_POISON;
 	ap->last_ctl = 0xFF;
@@ -5632,10 +5958,7 @@
 	INIT_LIST_HEAD(&ap->eh_done_q);
 	init_waitqueue_head(&ap->eh_wait_q);
 
-	/* set cable type */
 	ap->cbl = ATA_CBL_NONE;
-	if (ap->flags & ATA_FLAG_SATA)
-		ap->cbl = ATA_CBL_SATA;
 
 	for (i = 0; i < ATA_MAX_DEVICES; i++) {
 		struct ata_device *dev = &ap->device[i];
@@ -5648,72 +5971,6 @@
 	ap->stats.unhandled_irq = 1;
 	ap->stats.idle_irq = 1;
 #endif
-
-	memcpy(&ap->ioaddr, &ent->port[port_no], sizeof(struct ata_ioports));
-}
-
-/**
- *	ata_port_init_shost - Initialize SCSI host associated with ATA port
- *	@ap: ATA port to initialize SCSI host for
- *	@shost: SCSI host associated with @ap
- *
- *	Initialize SCSI host @shost associated with ATA port @ap.
- *
- *	LOCKING:
- *	Inherited from caller.
- */
-static void ata_port_init_shost(struct ata_port *ap, struct Scsi_Host *shost)
-{
-	ap->scsi_host = shost;
-
-	shost->unique_id = ap->print_id;
-	shost->max_id = 16;
-	shost->max_lun = 1;
-	shost->max_channel = 1;
-	shost->max_cmd_len = 12;
-}
-
-/**
- *	ata_port_add - Attach low-level ATA driver to system
- *	@ent: Information provided by low-level driver
- *	@host: Collections of ports to which we add
- *	@port_no: Port number associated with this host
- *
- *	Attach low-level ATA driver to system.
- *
- *	LOCKING:
- *	PCI/etc. bus probe sem.
- *
- *	RETURNS:
- *	New ata_port on success, for NULL on error.
- */
-static struct ata_port * ata_port_add(const struct ata_probe_ent *ent,
-				      struct ata_host *host,
-				      unsigned int port_no)
-{
-	struct Scsi_Host *shost;
-	struct ata_port *ap;
-
-	DPRINTK("ENTER\n");
-
-	if (!ent->port_ops->error_handler &&
-	    !(ent->port_flags & (ATA_FLAG_SATA_RESET | ATA_FLAG_SRST))) {
-		printk(KERN_ERR "ata%u: no reset mechanism available\n",
-		       port_no);
-		return NULL;
-	}
-
-	shost = scsi_host_alloc(ent->sht, sizeof(struct ata_port));
-	if (!shost)
-		return NULL;
-
-	shost->transportt = &ata_scsi_transport_template;
-
-	ap = ata_shost_to_port(shost);
-
-	ata_port_init(ap, host, ent, port_no);
-	ata_port_init_shost(ap, shost);
-
 	return ap;
 }
 
@@ -5725,19 +5982,26 @@
 	for (i = 0; i < host->n_ports; i++) {
 		struct ata_port *ap = host->ports[i];
 
-		if (ap && ap->ops->port_stop)
+		if (!ap)
+			continue;
+
+		if ((host->flags & ATA_HOST_STARTED) && ap->ops->port_stop)
 			ap->ops->port_stop(ap);
 	}
 
-	if (host->ops->host_stop)
+	if ((host->flags & ATA_HOST_STARTED) && host->ops->host_stop)
 		host->ops->host_stop(host);
 
 	for (i = 0; i < host->n_ports; i++) {
 		struct ata_port *ap = host->ports[i];
 
-		if (ap)
+		if (!ap)
+			continue;
+
+		if (ap->scsi_host)
 			scsi_host_put(ap->scsi_host);
 
+		kfree(ap);
 		host->ports[i] = NULL;
 	}
 
@@ -5745,6 +6009,174 @@
 }
 
 /**
+ *	ata_host_alloc - allocate and init basic ATA host resources
+ *	@dev: generic device this host is associated with
+ *	@max_ports: maximum number of ATA ports associated with this host
+ *
+ *	Allocate and initialize basic ATA host resources.  LLD calls
+ *	this function to allocate a host, initializes it fully and
+ *	attaches it using ata_host_register().
+ *
+ *	@max_ports ports are allocated and host->n_ports is
+ *	initialized to @max_ports.  The caller is allowed to decrease
+ *	host->n_ports before calling ata_host_register().  The unused
+ *	ports will be automatically freed on registration.
+ *
+ *	RETURNS:
+ *	Allocate ATA host on success, NULL on failure.
+ *
+ *	LOCKING:
+ *	Inherited from calling layer (may sleep).
+ */
+struct ata_host *ata_host_alloc(struct device *dev, int max_ports)
+{
+	struct ata_host *host;
+	size_t sz;
+	int i;
+
+	DPRINTK("ENTER\n");
+
+	if (!devres_open_group(dev, NULL, GFP_KERNEL))
+		return NULL;
+
+	/* alloc a container for our list of ATA ports (buses) */
+	sz = sizeof(struct ata_host) + (max_ports + 1) * sizeof(void *);
+	/* alloc a container for our list of ATA ports (buses) */
+	host = devres_alloc(ata_host_release, sz, GFP_KERNEL);
+	if (!host)
+		goto err_out;
+
+	devres_add(dev, host);
+	dev_set_drvdata(dev, host);
+
+	spin_lock_init(&host->lock);
+	host->dev = dev;
+	host->n_ports = max_ports;
+
+	/* allocate ports bound to this host */
+	for (i = 0; i < max_ports; i++) {
+		struct ata_port *ap;
+
+		ap = ata_port_alloc(host);
+		if (!ap)
+			goto err_out;
+
+		ap->port_no = i;
+		host->ports[i] = ap;
+	}
+
+	devres_remove_group(dev, NULL);
+	return host;
+
+ err_out:
+	devres_release_group(dev, NULL);
+	return NULL;
+}
+
+/**
+ *	ata_host_alloc_pinfo - alloc host and init with port_info array
+ *	@dev: generic device this host is associated with
+ *	@ppi: array of ATA port_info to initialize host with
+ *	@n_ports: number of ATA ports attached to this host
+ *
+ *	Allocate ATA host and initialize with info from @ppi.  If NULL
+ *	terminated, @ppi may contain fewer entries than @n_ports.  The
+ *	last entry will be used for the remaining ports.
+ *
+ *	RETURNS:
+ *	Allocate ATA host on success, NULL on failure.
+ *
+ *	LOCKING:
+ *	Inherited from calling layer (may sleep).
+ */
+struct ata_host *ata_host_alloc_pinfo(struct device *dev,
+				      const struct ata_port_info * const * ppi,
+				      int n_ports)
+{
+	const struct ata_port_info *pi;
+	struct ata_host *host;
+	int i, j;
+
+	host = ata_host_alloc(dev, n_ports);
+	if (!host)
+		return NULL;
+
+	for (i = 0, j = 0, pi = NULL; i < host->n_ports; i++) {
+		struct ata_port *ap = host->ports[i];
+
+		if (ppi[j])
+			pi = ppi[j++];
+
+		ap->pio_mask = pi->pio_mask;
+		ap->mwdma_mask = pi->mwdma_mask;
+		ap->udma_mask = pi->udma_mask;
+		ap->flags |= pi->flags;
+		ap->ops = pi->port_ops;
+
+		if (!host->ops && (pi->port_ops != &ata_dummy_port_ops))
+			host->ops = pi->port_ops;
+		if (!host->private_data && pi->private_data)
+			host->private_data = pi->private_data;
+	}
+
+	return host;
+}
+
+/**
+ *	ata_host_start - start and freeze ports of an ATA host
+ *	@host: ATA host to start ports for
+ *
+ *	Start and then freeze ports of @host.  Started status is
+ *	recorded in host->flags, so this function can be called
+ *	multiple times.  Ports are guaranteed to get started only
+ *	once.  If host->ops isn't initialized yet, its set to the
+ *	first non-dummy port ops.
+ *
+ *	LOCKING:
+ *	Inherited from calling layer (may sleep).
+ *
+ *	RETURNS:
+ *	0 if all ports are started successfully, -errno otherwise.
+ */
+int ata_host_start(struct ata_host *host)
+{
+	int i, rc;
+
+	if (host->flags & ATA_HOST_STARTED)
+		return 0;
+
+	for (i = 0; i < host->n_ports; i++) {
+		struct ata_port *ap = host->ports[i];
+
+		if (!host->ops && !ata_port_is_dummy(ap))
+			host->ops = ap->ops;
+
+		if (ap->ops->port_start) {
+			rc = ap->ops->port_start(ap);
+			if (rc) {
+				ata_port_printk(ap, KERN_ERR, "failed to "
+						"start port (errno=%d)\n", rc);
+				goto err_out;
+			}
+		}
+
+		ata_eh_freeze_port(ap);
+	}
+
+	host->flags |= ATA_HOST_STARTED;
+	return 0;
+
+ err_out:
+	while (--i >= 0) {
+		struct ata_port *ap = host->ports[i];
+
+		if (ap->ops->port_stop)
+			ap->ops->port_stop(ap);
+	}
+	return rc;
+}
+
+/**
  *	ata_sas_host_init - Initialize a host struct
  *	@host:	host to initialize
  *	@dev:	device host is attached to
@@ -5755,7 +6187,7 @@
  *	PCI/etc. bus probe sem.
  *
  */
-
+/* KILLME - the only user left is ipr */
 void ata_host_init(struct ata_host *host, struct device *dev,
 		   unsigned long flags, const struct ata_port_operations *ops)
 {
@@ -5766,137 +6198,58 @@
 }
 
 /**
- *	ata_device_add - Register hardware device with ATA and SCSI layers
- *	@ent: Probe information describing hardware device to be registered
+ *	ata_host_register - register initialized ATA host
+ *	@host: ATA host to register
+ *	@sht: template for SCSI host
  *
- *	This function processes the information provided in the probe
- *	information struct @ent, allocates the necessary ATA and SCSI
- *	host information structures, initializes them, and registers
- *	everything with requisite kernel subsystems.
- *
- *	This function requests irqs, probes the ATA bus, and probes
- *	the SCSI bus.
+ *	Register initialized ATA host.  @host is allocated using
+ *	ata_host_alloc() and fully initialized by LLD.  This function
+ *	starts ports, registers @host with ATA and SCSI layers and
+ *	probe registered devices.
  *
  *	LOCKING:
- *	PCI/etc. bus probe sem.
+ *	Inherited from calling layer (may sleep).
  *
  *	RETURNS:
- *	Number of ports registered.  Zero on error (no ports registered).
+ *	0 on success, -errno otherwise.
  */
-int ata_device_add(const struct ata_probe_ent *ent)
+int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
 {
-	unsigned int i;
-	struct device *dev = ent->dev;
-	struct ata_host *host;
-	int rc;
+	int i, rc;
 
-	DPRINTK("ENTER\n");
-
-	if (ent->irq == 0) {
-		dev_printk(KERN_ERR, dev, "is not available: No interrupt assigned.\n");
-		return 0;
+	/* host must have been started */
+	if (!(host->flags & ATA_HOST_STARTED)) {
+		dev_printk(KERN_ERR, host->dev,
+			   "BUG: trying to register unstarted host\n");
+		WARN_ON(1);
+		return -EINVAL;
 	}
 
-	if (!devres_open_group(dev, ata_device_add, GFP_KERNEL))
-		return 0;
+	/* Blow away unused ports.  This happens when LLD can't
+	 * determine the exact number of ports to allocate at
+	 * allocation time.
+	 */
+	for (i = host->n_ports; host->ports[i]; i++)
+		kfree(host->ports[i]);
 
-	/* alloc a container for our list of ATA ports (buses) */
-	host = devres_alloc(ata_host_release, sizeof(struct ata_host) +
-			    (ent->n_ports * sizeof(void *)), GFP_KERNEL);
-	if (!host)
-		goto err_out;
-	devres_add(dev, host);
-	dev_set_drvdata(dev, host);
+	/* give ports names and add SCSI hosts */
+	for (i = 0; i < host->n_ports; i++)
+		host->ports[i]->print_id = ata_print_id++;
 
-	ata_host_init(host, dev, ent->_host_flags, ent->port_ops);
-	host->n_ports = ent->n_ports;
-	host->irq = ent->irq;
-	host->irq2 = ent->irq2;
-	host->iomap = ent->iomap;
-	host->private_data = ent->private_data;
+	rc = ata_scsi_add_hosts(host, sht);
+	if (rc)
+		return rc;
 
-	/* register each port bound to this device */
-	for (i = 0; i < host->n_ports; i++) {
-		struct ata_port *ap;
-		unsigned long xfer_mode_mask;
-		int irq_line = ent->irq;
-
-		ap = ata_port_add(ent, host, i);
-		host->ports[i] = ap;
-		if (!ap)
-			goto err_out;
-
-		/* dummy? */
-		if (ent->dummy_port_mask & (1 << i)) {
-			ata_port_printk(ap, KERN_INFO, "DUMMY\n");
-			ap->ops = &ata_dummy_port_ops;
-			continue;
-		}
-
-		/* start port */
-		rc = ap->ops->port_start(ap);
-		if (rc) {
-			host->ports[i] = NULL;
-			scsi_host_put(ap->scsi_host);
-			goto err_out;
-		}
-
-		/* Report the secondary IRQ for second channel legacy */
-		if (i == 1 && ent->irq2)
-			irq_line = ent->irq2;
-
-		xfer_mode_mask =(ap->udma_mask << ATA_SHIFT_UDMA) |
-				(ap->mwdma_mask << ATA_SHIFT_MWDMA) |
-				(ap->pio_mask << ATA_SHIFT_PIO);
-
-		/* print per-port info to dmesg */
-		ata_port_printk(ap, KERN_INFO, "%cATA max %s cmd 0x%p "
-				"ctl 0x%p bmdma 0x%p irq %d\n",
-				ap->flags & ATA_FLAG_SATA ? 'S' : 'P',
-				ata_mode_string(xfer_mode_mask),
-				ap->ioaddr.cmd_addr,
-				ap->ioaddr.ctl_addr,
-				ap->ioaddr.bmdma_addr,
-				irq_line);
-
-		/* freeze port before requesting IRQ */
-		ata_eh_freeze_port(ap);
-	}
-
-	/* obtain irq, that may be shared between channels */
-	rc = devm_request_irq(dev, ent->irq, ent->port_ops->irq_handler,
-			      ent->irq_flags, DRV_NAME, host);
-	if (rc) {
-		dev_printk(KERN_ERR, dev, "irq %lu request failed: %d\n",
-			   ent->irq, rc);
-		goto err_out;
-	}
-
-	/* do we have a second IRQ for the other channel, eg legacy mode */
-	if (ent->irq2) {
-		/* We will get weird core code crashes later if this is true
-		   so trap it now */
-		BUG_ON(ent->irq == ent->irq2);
-
-		rc = devm_request_irq(dev, ent->irq2,
-				ent->port_ops->irq_handler, ent->irq_flags,
-				DRV_NAME, host);
-		if (rc) {
-			dev_printk(KERN_ERR, dev, "irq %lu request failed: %d\n",
-				   ent->irq2, rc);
-			goto err_out;
-		}
-	}
-
-	/* resource acquisition complete */
-	devres_remove_group(dev, ata_device_add);
-
-	/* perform each probe synchronously */
-	DPRINTK("probe begin\n");
+	/* set cable, sata_spd_limit and report */
 	for (i = 0; i < host->n_ports; i++) {
 		struct ata_port *ap = host->ports[i];
+		int irq_line;
 		u32 scontrol;
-		int rc;
+		unsigned long xfer_mask;
+
+		/* set SATA cable type if still unset */
+		if (ap->cbl == ATA_CBL_NONE && (ap->flags & ATA_FLAG_SATA))
+			ap->cbl = ATA_CBL_SATA;
 
 		/* init sata_spd_limit to the current value */
 		if (sata_scr_read(ap, SCR_CONTROL, &scontrol) == 0) {
@@ -5905,16 +6258,35 @@
 		}
 		ap->sata_spd_limit = ap->hw_sata_spd_limit;
 
-		rc = scsi_add_host(ap->scsi_host, dev);
-		if (rc) {
-			ata_port_printk(ap, KERN_ERR, "scsi_add_host failed\n");
-			/* FIXME: do something useful here */
-			/* FIXME: handle unconditional calls to
-			 * scsi_scan_host and ata_host_remove, below,
-			 * at the very least
-			 */
-		}
+		/* report the secondary IRQ for second channel legacy */
+		irq_line = host->irq;
+		if (i == 1 && host->irq2)
+			irq_line = host->irq2;
 
+		xfer_mask = ata_pack_xfermask(ap->pio_mask, ap->mwdma_mask,
+					      ap->udma_mask);
+
+		/* print per-port info to dmesg */
+		if (!ata_port_is_dummy(ap))
+			ata_port_printk(ap, KERN_INFO, "%cATA max %s cmd 0x%p "
+					"ctl 0x%p bmdma 0x%p irq %d\n",
+					ap->cbl == ATA_CBL_SATA ? 'S' : 'P',
+					ata_mode_string(xfer_mask),
+					ap->ioaddr.cmd_addr,
+					ap->ioaddr.ctl_addr,
+					ap->ioaddr.bmdma_addr,
+					irq_line);
+		else
+			ata_port_printk(ap, KERN_INFO, "DUMMY\n");
+	}
+
+	/* perform each probe synchronously */
+	DPRINTK("probe begin\n");
+	for (i = 0; i < host->n_ports; i++) {
+		struct ata_port *ap = host->ports[i];
+		int rc;
+
+		/* probe */
 		if (ap->ops->error_handler) {
 			struct ata_eh_info *ehi = &ap->eh_info;
 			unsigned long flags;
@@ -5959,16 +6331,52 @@
 		ata_scsi_scan_host(ap);
 	}
 
-	VPRINTK("EXIT, returning %u\n", ent->n_ports);
-	return ent->n_ports; /* success */
-
- err_out:
-	devres_release_group(dev, ata_device_add);
-	VPRINTK("EXIT, returning %d\n", rc);
 	return 0;
 }
 
 /**
+ *	ata_host_activate - start host, request IRQ and register it
+ *	@host: target ATA host
+ *	@irq: IRQ to request
+ *	@irq_handler: irq_handler used when requesting IRQ
+ *	@irq_flags: irq_flags used when requesting IRQ
+ *	@sht: scsi_host_template to use when registering the host
+ *
+ *	After allocating an ATA host and initializing it, most libata
+ *	LLDs perform three steps to activate the host - start host,
+ *	request IRQ and register it.  This helper takes necessasry
+ *	arguments and performs the three steps in one go.
+ *
+ *	LOCKING:
+ *	Inherited from calling layer (may sleep).
+ *
+ *	RETURNS:
+ *	0 on success, -errno otherwise.
+ */
+int ata_host_activate(struct ata_host *host, int irq,
+		      irq_handler_t irq_handler, unsigned long irq_flags,
+		      struct scsi_host_template *sht)
+{
+	int rc;
+
+	rc = ata_host_start(host);
+	if (rc)
+		return rc;
+
+	rc = devm_request_irq(host->dev, irq, irq_handler, irq_flags,
+			      dev_driver_string(host->dev), host);
+	if (rc)
+		return rc;
+
+	rc = ata_host_register(host, sht);
+	/* if failed, just free the IRQ and leave ports alone */
+	if (rc)
+		devm_free_irq(host->dev, irq, host);
+
+	return rc;
+}
+
+/**
  *	ata_port_detach - Detach ATA port in prepration of device removal
  *	@ap: ATA port to be detached
  *
@@ -6043,32 +6451,6 @@
 		ata_port_detach(host->ports[i]);
 }
 
-struct ata_probe_ent *
-ata_probe_ent_alloc(struct device *dev, const struct ata_port_info *port)
-{
-	struct ata_probe_ent *probe_ent;
-
-	probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL);
-	if (!probe_ent) {
-		printk(KERN_ERR DRV_NAME "(%s): out of memory\n",
-		       kobject_name(&(dev->kobj)));
-		return NULL;
-	}
-
-	INIT_LIST_HEAD(&probe_ent->node);
-	probe_ent->dev = dev;
-
-	probe_ent->sht = port->sht;
-	probe_ent->port_flags = port->flags;
-	probe_ent->pio_mask = port->pio_mask;
-	probe_ent->mwdma_mask = port->mwdma_mask;
-	probe_ent->udma_mask = port->udma_mask;
-	probe_ent->port_ops = port->port_ops;
-	probe_ent->private_data = port->private_data;
-
-	return probe_ent;
-}
-
 /**
  *	ata_std_ports - initialize ioaddr with standard port offsets.
  *	@ioaddr: IO address structure to be initialized
@@ -6334,6 +6716,10 @@
 	.port_stop		= ata_dummy_noret,
 };
 
+const struct ata_port_info ata_dummy_port_info = {
+	.port_ops		= &ata_dummy_port_ops,
+};
+
 /*
  * libata is essentially a library of internal helper functions for
  * low-level ATA host controller drivers.  As such, the API/ABI is
@@ -6345,10 +6731,15 @@
 EXPORT_SYMBOL_GPL(sata_deb_timing_hotplug);
 EXPORT_SYMBOL_GPL(sata_deb_timing_long);
 EXPORT_SYMBOL_GPL(ata_dummy_port_ops);
+EXPORT_SYMBOL_GPL(ata_dummy_port_info);
 EXPORT_SYMBOL_GPL(ata_std_bios_param);
 EXPORT_SYMBOL_GPL(ata_std_ports);
 EXPORT_SYMBOL_GPL(ata_host_init);
-EXPORT_SYMBOL_GPL(ata_device_add);
+EXPORT_SYMBOL_GPL(ata_host_alloc);
+EXPORT_SYMBOL_GPL(ata_host_alloc_pinfo);
+EXPORT_SYMBOL_GPL(ata_host_start);
+EXPORT_SYMBOL_GPL(ata_host_register);
+EXPORT_SYMBOL_GPL(ata_host_activate);
 EXPORT_SYMBOL_GPL(ata_host_detach);
 EXPORT_SYMBOL_GPL(ata_sg_init);
 EXPORT_SYMBOL_GPL(ata_sg_init_one);
@@ -6360,6 +6751,7 @@
 EXPORT_SYMBOL_GPL(ata_tf_read);
 EXPORT_SYMBOL_GPL(ata_noop_dev_select);
 EXPORT_SYMBOL_GPL(ata_std_dev_select);
+EXPORT_SYMBOL_GPL(sata_print_link_status);
 EXPORT_SYMBOL_GPL(ata_tf_to_fis);
 EXPORT_SYMBOL_GPL(ata_tf_from_fis);
 EXPORT_SYMBOL_GPL(ata_check_status);
@@ -6367,6 +6759,7 @@
 EXPORT_SYMBOL_GPL(ata_exec_command);
 EXPORT_SYMBOL_GPL(ata_port_start);
 EXPORT_SYMBOL_GPL(ata_interrupt);
+EXPORT_SYMBOL_GPL(ata_do_set_mode);
 EXPORT_SYMBOL_GPL(ata_data_xfer);
 EXPORT_SYMBOL_GPL(ata_data_xfer_noirq);
 EXPORT_SYMBOL_GPL(ata_qc_prep);
@@ -6429,7 +6822,8 @@
 
 #ifdef CONFIG_PCI
 EXPORT_SYMBOL_GPL(pci_test_config_bits);
-EXPORT_SYMBOL_GPL(ata_pci_init_native_mode);
+EXPORT_SYMBOL_GPL(ata_pci_init_native_host);
+EXPORT_SYMBOL_GPL(ata_pci_prepare_native_host);
 EXPORT_SYMBOL_GPL(ata_pci_init_one);
 EXPORT_SYMBOL_GPL(ata_pci_remove_one);
 #ifdef CONFIG_PM
@@ -6461,3 +6855,8 @@
 EXPORT_SYMBOL_GPL(ata_irq_ack);
 EXPORT_SYMBOL_GPL(ata_dummy_irq_ack);
 EXPORT_SYMBOL_GPL(ata_dev_try_classify);
+
+EXPORT_SYMBOL_GPL(ata_cable_40wire);
+EXPORT_SYMBOL_GPL(ata_cable_80wire);
+EXPORT_SYMBOL_GPL(ata_cable_unknown);
+EXPORT_SYMBOL_GPL(ata_cable_sata);
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 39f556c..2bff9ad 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -1056,7 +1056,7 @@
 	}
 	if (serror & SERR_INTERNAL) {
 		err_mask |= AC_ERR_SYSTEM;
-		action |= ATA_EH_SOFTRESET;
+		action |= ATA_EH_HARDRESET;
 	}
 	if (serror & (SERR_PHYRDY_CHG | SERR_DEV_XCHG))
 		ata_ehi_hotplugged(&ehc->i);
@@ -1151,7 +1151,9 @@
 		return ATA_EH_SOFTRESET;
 	}
 
-	if (!(qc->err_mask & AC_ERR_DEV))
+	if (stat & (ATA_ERR | ATA_DF))
+		qc->err_mask |= AC_ERR_DEV;
+	else
 		return 0;
 
 	switch (qc->dev->class) {
@@ -1669,7 +1671,10 @@
 				reset == softreset ? "soft" : "hard");
 
 	/* mark that this EH session started with reset */
-	ehc->i.flags |= ATA_EHI_DID_RESET;
+	if (reset == hardreset)
+		ehc->i.flags |= ATA_EHI_DID_HARDRESET;
+	else
+		ehc->i.flags |= ATA_EHI_DID_SOFTRESET;
 
 	rc = ata_do_reset(ap, reset, classes);
 
@@ -1808,6 +1813,10 @@
 		}
 	}
 
+	/* PDIAG- should have been released, ask cable type if post-reset */
+	if ((ehc->i.flags & ATA_EHI_DID_RESET) && ap->ops->cable_detect)
+		ap->cbl = ap->ops->cable_detect(ap);
+
 	/* Configure new devices forward such that user doesn't see
 	 * device detection messages backwards.
 	 */
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index e936443..9afba2b 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -104,7 +104,7 @@
  * libata transport template.  libata doesn't do real transport stuff.
  * It just needs the eh_timed_out hook.
  */
-struct scsi_transport_template ata_scsi_transport_template = {
+static struct scsi_transport_template ata_scsi_transport_template = {
 	.eh_strategy_handler	= ata_scsi_error,
 	.eh_timed_out		= ata_scsi_timed_out,
 	.user_scan		= ata_scsi_user_scan,
@@ -2678,6 +2678,18 @@
 		tf->device = qc->dev->devno ?
 			tf->device | ATA_DEV1 : tf->device & ~ATA_DEV1;
 
+	/* READ/WRITE LONG use a non-standard sect_size */
+	qc->sect_size = ATA_SECT_SIZE;
+	switch (tf->command) {
+	case ATA_CMD_READ_LONG:
+	case ATA_CMD_READ_LONG_ONCE:
+	case ATA_CMD_WRITE_LONG:
+	case ATA_CMD_WRITE_LONG_ONCE:
+		if (tf->protocol != ATA_PROT_PIO || tf->nsect != 1)
+			goto invalid_fld;
+		qc->sect_size = scmd->request_bufflen;
+	}
+
 	/*
 	 * Filter SET_FEATURES - XFER MODE command -- otherwise,
 	 * SET_FEATURES - XFER MODE must be preceded/succeeded
@@ -2792,8 +2804,9 @@
 {
 	int rc = 0;
 
-	if (unlikely(!scmd->cmd_len)) {
-		ata_dev_printk(dev, KERN_WARNING, "WARNING: zero len CDB\n");
+	if (unlikely(!scmd->cmd_len || scmd->cmd_len > dev->cdb_len)) {
+		DPRINTK("bad CDB len=%u, max=%u\n",
+			scmd->cmd_len, dev->cdb_len);
 		scmd->result = DID_ERROR << 16;
 		done(scmd);
 		return 0;
@@ -2948,6 +2961,48 @@
 	}
 }
 
+int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht)
+{
+	int i, rc;
+
+	for (i = 0; i < host->n_ports; i++) {
+		struct ata_port *ap = host->ports[i];
+		struct Scsi_Host *shost;
+
+		rc = -ENOMEM;
+		shost = scsi_host_alloc(sht, sizeof(struct ata_port *));
+		if (!shost)
+			goto err_alloc;
+
+		*(struct ata_port **)&shost->hostdata[0] = ap;
+		ap->scsi_host = shost;
+
+		shost->transportt = &ata_scsi_transport_template;
+		shost->unique_id = ap->print_id;
+		shost->max_id = 16;
+		shost->max_lun = 1;
+		shost->max_channel = 1;
+		shost->max_cmd_len = 16;
+
+		rc = scsi_add_host(ap->scsi_host, ap->host->dev);
+		if (rc)
+			goto err_add;
+	}
+
+	return 0;
+
+ err_add:
+	scsi_host_put(host->ports[i]->scsi_host);
+ err_alloc:
+	while (--i >= 0) {
+		struct Scsi_Host *shost = host->ports[i]->scsi_host;
+
+		scsi_remove_host(shost);
+		scsi_host_put(shost);
+	}
+	return rc;
+}
+
 void ata_scsi_scan_host(struct ata_port *ap)
 {
 	unsigned int i;
@@ -3224,21 +3279,21 @@
 				    struct ata_port_info *port_info,
 				    struct Scsi_Host *shost)
 {
-	struct ata_port *ap = kzalloc(sizeof(*ap), GFP_KERNEL);
-	struct ata_probe_ent *ent;
+	struct ata_port *ap;
 
+	ap = ata_port_alloc(host);
 	if (!ap)
 		return NULL;
 
-	ent = ata_probe_ent_alloc(host->dev, port_info);
-	if (!ent) {
-		kfree(ap);
-		return NULL;
-	}
-
-	ata_port_init(ap, host, ent, 0);
+	ap->port_no = 0;
 	ap->lock = shost->host_lock;
-	devm_kfree(host->dev, ent);
+	ap->pio_mask = port_info->pio_mask;
+	ap->mwdma_mask = port_info->mwdma_mask;
+	ap->udma_mask = port_info->udma_mask;
+	ap->flags |= port_info->flags;
+	ap->ops = port_info->port_ops;
+	ap->cbl = ATA_CBL_SATA;
+
 	return ap;
 }
 EXPORT_SYMBOL_GPL(ata_sas_port_alloc);
@@ -3294,8 +3349,10 @@
 {
 	int rc = ap->ops->port_start(ap);
 
-	if (!rc)
+	if (!rc) {
+		ap->print_id = ata_print_id++;
 		rc = ata_bus_probe(ap);
+	}
 
 	return rc;
 }
diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c
index 2ffcca0..d211db6 100644
--- a/drivers/ata/libata-sff.c
+++ b/drivers/ata/libata-sff.c
@@ -526,169 +526,400 @@
 	port = port * 2;
 	for (i = 0; i < 2; i ++) {
 		if (pci_resource_start(pdev, port + i) == 0 ||
-			pci_resource_len(pdev, port + i) == 0)
-		return 0;
+		    pci_resource_len(pdev, port + i) == 0)
+			return 0;
 	}
 	return 1;
 }
 
 /**
- *	ata_pci_init_native_mode - Initialize native-mode driver
- *	@pdev:  pci device to be initialized
- *	@port:  array[2] of pointers to port info structures.
- *	@ports: bitmap of ports present
+ *	ata_pci_init_bmdma - acquire PCI BMDMA resources and init ATA host
+ *	@host: target ATA host
  *
- *	Utility function which allocates and initializes an
- *	ata_probe_ent structure for a standard dual-port
- *	PIO-based IDE controller.  The returned ata_probe_ent
- *	structure can be passed to ata_device_add().  The returned
- *	ata_probe_ent structure should then be freed with kfree().
+ *	Acquire PCI BMDMA resources and initialize @host accordingly.
  *
- *	The caller need only pass the address of the primary port, the
- *	secondary will be deduced automatically. If the device has non
- *	standard secondary port mappings this function can be called twice,
- *	once for each interface.
+ *	LOCKING:
+ *	Inherited from calling layer (may sleep).
+ *
+ *	RETURNS:
+ *	0 on success, -errno otherwise.
  */
-
-struct ata_probe_ent *
-ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int ports)
+static int ata_pci_init_bmdma(struct ata_host *host)
 {
-	struct ata_probe_ent *probe_ent;
-	int i, p = 0;
-	void __iomem * const *iomap;
+	struct device *gdev = host->dev;
+	struct pci_dev *pdev = to_pci_dev(gdev);
+	int i, rc;
 
-	/* iomap BARs */
-	for (i = 0; i < 4; i++) {
-		if (pcim_iomap(pdev, i, 0) == NULL) {
-			dev_printk(KERN_ERR, &pdev->dev,
-				   "failed to iomap PCI BAR %d\n", i);
-			return NULL;
-		}
+	/* TODO: If we get no DMA mask we should fall back to PIO */
+	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
+	if (rc)
+		return rc;
+	rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
+	if (rc)
+		return rc;
+
+	/* request and iomap DMA region */
+	rc = pcim_iomap_regions(pdev, 1 << 4, DRV_NAME);
+	if (rc) {
+		dev_printk(KERN_ERR, gdev, "failed to request/iomap BAR4\n");
+		return -ENOMEM;
+	}
+	host->iomap = pcim_iomap_table(pdev);
+
+	for (i = 0; i < host->n_ports; i++) {
+		struct ata_port *ap = host->ports[i];
+		void __iomem *bmdma = host->iomap[4] + 8 * i;
+
+		if (ata_port_is_dummy(ap))
+			continue;
+
+		ap->ioaddr.bmdma_addr = bmdma;
+		if ((!(ap->flags & ATA_FLAG_IGN_SIMPLEX)) &&
+		    (ioread8(bmdma + 2) & 0x80))
+			host->flags |= ATA_HOST_SIMPLEX;
 	}
 
-	pcim_iomap(pdev, 4, 0); /* may fail */
-	iomap = pcim_iomap_table(pdev);
-
-	/* alloc and init probe_ent */
-	probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]);
-	if (!probe_ent)
-		return NULL;
-
-	probe_ent->irq = pdev->irq;
-	probe_ent->irq_flags = IRQF_SHARED;
-
-	/* Discard disabled ports. Some controllers show their
-	   unused channels this way */
-	if (ata_resources_present(pdev, 0) == 0)
-		ports &= ~ATA_PORT_PRIMARY;
-	if (ata_resources_present(pdev, 1) == 0)
-		ports &= ~ATA_PORT_SECONDARY;
-
-	if (ports & ATA_PORT_PRIMARY) {
-		probe_ent->port[p].cmd_addr = iomap[0];
-		probe_ent->port[p].altstatus_addr =
-		probe_ent->port[p].ctl_addr = (void __iomem *)
-			((unsigned long)iomap[1] | ATA_PCI_CTL_OFS);
-		if (iomap[4]) {
-			if ((!(port[p]->flags & ATA_FLAG_IGN_SIMPLEX)) &&
-			    (ioread8(iomap[4] + 2) & 0x80))
-				probe_ent->_host_flags |= ATA_HOST_SIMPLEX;
-			probe_ent->port[p].bmdma_addr = iomap[4];
-		}
-		ata_std_ports(&probe_ent->port[p]);
-		p++;
-	}
-
-	if (ports & ATA_PORT_SECONDARY) {
-		probe_ent->port[p].cmd_addr = iomap[2];
-		probe_ent->port[p].altstatus_addr =
-		probe_ent->port[p].ctl_addr = (void __iomem *)
-			((unsigned long)iomap[3] | ATA_PCI_CTL_OFS);
-		if (iomap[4]) {
-			if ((!(port[p]->flags & ATA_FLAG_IGN_SIMPLEX)) &&
-			    (ioread8(iomap[4] + 10) & 0x80))
-				probe_ent->_host_flags |= ATA_HOST_SIMPLEX;
-			probe_ent->port[p].bmdma_addr = iomap[4] + 8;
-		}
-		ata_std_ports(&probe_ent->port[p]);
-		probe_ent->pinfo2 = port[1];
-		p++;
-	}
-
-	probe_ent->n_ports = p;
-	return probe_ent;
+	return 0;
 }
 
-static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev,
-				struct ata_port_info **port, int port_mask)
+/**
+ *	ata_pci_init_native_host - acquire native ATA resources and init host
+ *	@host: target ATA host
+ *	@port_mask: ports to consider
+ *
+ *	Acquire native PCI ATA resources for @host and initialize
+ *	@host accordoingly.
+ *
+ *	LOCKING:
+ *	Inherited from calling layer (may sleep).
+ *
+ *	RETURNS:
+ *	0 on success, -errno otherwise.
+ */
+int ata_pci_init_native_host(struct ata_host *host, unsigned int port_mask)
 {
-	struct ata_probe_ent *probe_ent;
-	void __iomem *iomap[5] = { }, *bmdma;
+	struct device *gdev = host->dev;
+	struct pci_dev *pdev = to_pci_dev(gdev);
+	int i, rc;
 
-	if (port_mask & ATA_PORT_PRIMARY) {
-		iomap[0] = devm_ioport_map(&pdev->dev, ATA_PRIMARY_CMD, 8);
-		iomap[1] = devm_ioport_map(&pdev->dev, ATA_PRIMARY_CTL, 1);
-		if (!iomap[0] || !iomap[1])
-			return NULL;
-	}
-
-	if (port_mask & ATA_PORT_SECONDARY) {
-		iomap[2] = devm_ioport_map(&pdev->dev, ATA_SECONDARY_CMD, 8);
-		iomap[3] = devm_ioport_map(&pdev->dev, ATA_SECONDARY_CTL, 1);
-		if (!iomap[2] || !iomap[3])
-			return NULL;
-	}
-
-	bmdma = pcim_iomap(pdev, 4, 16); /* may fail */
-
-	/* alloc and init probe_ent */
-	probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]);
-	if (!probe_ent)
-		return NULL;
-
-	probe_ent->n_ports = 2;
-	probe_ent->irq_flags = IRQF_SHARED;
-
-	if (port_mask & ATA_PORT_PRIMARY) {
-		probe_ent->irq = ATA_PRIMARY_IRQ(pdev);
-		probe_ent->port[0].cmd_addr = iomap[0];
-		probe_ent->port[0].altstatus_addr =
-		probe_ent->port[0].ctl_addr = iomap[1];
-		if (bmdma) {
-			probe_ent->port[0].bmdma_addr = bmdma;
-			if ((!(port[0]->flags & ATA_FLAG_IGN_SIMPLEX)) &&
-			    (ioread8(bmdma + 2) & 0x80))
-				probe_ent->_host_flags |= ATA_HOST_SIMPLEX;
+	/* Discard disabled ports.  Some controllers show their unused
+	 * channels this way.  Disabled ports are made dummy.
+	 */
+	for (i = 0; i < 2; i++) {
+		if ((port_mask & (1 << i)) && !ata_resources_present(pdev, i)) {
+			host->ports[i]->ops = &ata_dummy_port_ops;
+			port_mask &= ~(1 << i);
 		}
-		ata_std_ports(&probe_ent->port[0]);
-	} else
-		probe_ent->dummy_port_mask |= ATA_PORT_PRIMARY;
+	}
 
-	if (port_mask & ATA_PORT_SECONDARY) {
-		if (probe_ent->irq)
-			probe_ent->irq2 = ATA_SECONDARY_IRQ(pdev);
+	if (!port_mask) {
+		dev_printk(KERN_ERR, gdev, "no available port\n");
+		return -ENODEV;
+	}
+
+	/* request, iomap BARs and init port addresses accordingly */
+	for (i = 0; i < 2; i++) {
+		struct ata_port *ap = host->ports[i];
+		int base = i * 2;
+		void __iomem * const *iomap;
+
+		if (!(port_mask & (1 << i)))
+			continue;
+
+		rc = pcim_iomap_regions(pdev, 0x3 << base, DRV_NAME);
+		if (rc) {
+			dev_printk(KERN_ERR, gdev, "failed to request/iomap "
+				   "BARs for port %d (errno=%d)\n", i, rc);
+			if (rc == -EBUSY)
+				pcim_pin_device(pdev);
+			return rc;
+		}
+		host->iomap = iomap = pcim_iomap_table(pdev);
+
+		ap->ioaddr.cmd_addr = iomap[base];
+		ap->ioaddr.altstatus_addr =
+		ap->ioaddr.ctl_addr = (void __iomem *)
+			((unsigned long)iomap[base + 1] | ATA_PCI_CTL_OFS);
+		ata_std_ports(&ap->ioaddr);
+	}
+
+	return 0;
+}
+
+/**
+ *	ata_pci_prepare_native_host - helper to prepare native PCI ATA host
+ *	@pdev: target PCI device
+ *	@ppi: array of port_info
+ *	@n_ports: number of ports to allocate
+ *	@r_host: out argument for the initialized ATA host
+ *
+ *	Helper to allocate ATA host for @pdev, acquire all native PCI
+ *	resources and initialize it accordingly in one go.
+ *
+ *	LOCKING:
+ *	Inherited from calling layer (may sleep).
+ *
+ *	RETURNS:
+ *	0 on success, -errno otherwise.
+ */
+int ata_pci_prepare_native_host(struct pci_dev *pdev,
+				const struct ata_port_info * const * ppi,
+				int n_ports, struct ata_host **r_host)
+{
+	struct ata_host *host;
+	unsigned int port_mask;
+	int rc;
+
+	if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL))
+		return -ENOMEM;
+
+	host = ata_host_alloc_pinfo(&pdev->dev, ppi, 2);
+	if (!host) {
+		dev_printk(KERN_ERR, &pdev->dev,
+			   "failed to allocate ATA host\n");
+		rc = -ENOMEM;
+		goto err_out;
+	}
+
+	port_mask = ATA_PORT_PRIMARY;
+	if (n_ports > 1)
+		port_mask |= ATA_PORT_SECONDARY;
+
+	rc = ata_pci_init_native_host(host, port_mask);
+	if (rc)
+		goto err_out;
+
+	/* init DMA related stuff */
+	rc = ata_pci_init_bmdma(host);
+	if (rc)
+		goto err_bmdma;
+
+	devres_remove_group(&pdev->dev, NULL);
+	*r_host = host;
+	return 0;
+
+ err_bmdma:
+	/* This is necessary because PCI and iomap resources are
+	 * merged and releasing the top group won't release the
+	 * acquired resources if some of those have been acquired
+	 * before entering this function.
+	 */
+	pcim_iounmap_regions(pdev, 0xf);
+ err_out:
+	devres_release_group(&pdev->dev, NULL);
+	return rc;
+}
+
+struct ata_legacy_devres {
+	unsigned int	mask;
+	unsigned long	cmd_port[2];
+	void __iomem *	cmd_addr[2];
+	void __iomem *	ctl_addr[2];
+	unsigned int	irq[2];
+	void *		irq_dev_id[2];
+};
+
+static void ata_legacy_free_irqs(struct ata_legacy_devres *legacy_dr)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		if (!legacy_dr->irq[i])
+			continue;
+
+		free_irq(legacy_dr->irq[i], legacy_dr->irq_dev_id[i]);
+		legacy_dr->irq[i] = 0;
+		legacy_dr->irq_dev_id[i] = NULL;
+	}
+}
+
+static void ata_legacy_release(struct device *gdev, void *res)
+{
+	struct ata_legacy_devres *this = res;
+	int i;
+
+	ata_legacy_free_irqs(this);
+
+	for (i = 0; i < 2; i++) {
+		if (this->cmd_addr[i])
+			ioport_unmap(this->cmd_addr[i]);
+		if (this->ctl_addr[i])
+			ioport_unmap(this->ctl_addr[i]);
+		if (this->cmd_port[i])
+			release_region(this->cmd_port[i], 8);
+	}
+}
+
+static int ata_init_legacy_port(struct ata_port *ap,
+				struct ata_legacy_devres *legacy_dr)
+{
+	struct ata_host *host = ap->host;
+	int port_no = ap->port_no;
+	unsigned long cmd_port, ctl_port;
+
+	if (port_no == 0) {
+		cmd_port = ATA_PRIMARY_CMD;
+		ctl_port = ATA_PRIMARY_CTL;
+	} else {
+		cmd_port = ATA_SECONDARY_CMD;
+		ctl_port = ATA_SECONDARY_CTL;
+	}
+
+	/* request cmd_port */
+	if (request_region(cmd_port, 8, "libata"))
+		legacy_dr->cmd_port[port_no] = cmd_port;
+	else {
+		dev_printk(KERN_WARNING, host->dev,
+			   "0x%0lX IDE port busy\n", cmd_port);
+		return -EBUSY;
+	}
+
+	/* iomap cmd and ctl ports */
+	legacy_dr->cmd_addr[port_no] = ioport_map(cmd_port, 8);
+	legacy_dr->ctl_addr[port_no] = ioport_map(ctl_port, 1);
+	if (!legacy_dr->cmd_addr[port_no] || !legacy_dr->ctl_addr[port_no])
+		return -ENOMEM;
+
+	/* init IO addresses */
+	ap->ioaddr.cmd_addr = legacy_dr->cmd_addr[port_no];
+	ap->ioaddr.altstatus_addr = legacy_dr->ctl_addr[port_no];
+	ap->ioaddr.ctl_addr = legacy_dr->ctl_addr[port_no];
+	ata_std_ports(&ap->ioaddr);
+
+	return 0;
+}
+
+/**
+ *	ata_init_legacy_host - acquire legacy ATA resources and init ATA host
+ *	@host: target ATA host
+ *	@legacy_mask: out parameter, mask indicating ports is in legacy mode
+ *	@was_busy: out parameter, indicates whether any port was busy
+ *
+ *	Acquire legacy ATA resources for ports.
+ *
+ *	LOCKING:
+ *	Inherited from calling layer (may sleep).
+ *
+ *	RETURNS:
+ *	0 on success, -errno otherwise.
+ */
+static int ata_init_legacy_host(struct ata_host *host,
+				unsigned int *legacy_mask, int *was_busy)
+{
+	struct device *gdev = host->dev;
+	struct ata_legacy_devres *legacy_dr;
+	int i, rc;
+
+	if (!devres_open_group(gdev, NULL, GFP_KERNEL))
+		return -ENOMEM;
+
+	rc = -ENOMEM;
+	legacy_dr = devres_alloc(ata_legacy_release, sizeof(*legacy_dr),
+				 GFP_KERNEL);
+	if (!legacy_dr)
+		goto err_out;
+	devres_add(gdev, legacy_dr);
+
+	for (i = 0; i < 2; i++) {
+		*legacy_mask &= ~(1 << i);
+		rc = ata_init_legacy_port(host->ports[i], legacy_dr);
+		if (rc == 0)
+			legacy_dr->mask |= 1 << i;
+		else if (rc == -EBUSY)
+			(*was_busy)++;
+	}
+
+	if (!legacy_dr->mask)
+		return -EBUSY;
+
+	for (i = 0; i < 2; i++)
+		if (!(legacy_dr->mask & (1 << i)))
+			host->ports[i]->ops = &ata_dummy_port_ops;
+
+	*legacy_mask |= legacy_dr->mask;
+
+	devres_remove_group(gdev, NULL);
+	return 0;
+
+ err_out:
+	devres_release_group(gdev, NULL);
+	return rc;
+}
+
+/**
+ *	ata_request_legacy_irqs - request legacy ATA IRQs
+ *	@host: target ATA host
+ *	@handler: array of IRQ handlers
+ *	@irq_flags: array of IRQ flags
+ *	@dev_id: array of IRQ dev_ids
+ *
+ *	Request legacy IRQs for non-dummy legacy ports in @host.  All
+ *	IRQ parameters are passed as array to allow ports to have
+ *	separate IRQ handlers.
+ *
+ *	LOCKING:
+ *	Inherited from calling layer (may sleep).
+ *
+ *	RETURNS:
+ *	0 on success, -errno otherwise.
+ */
+static int ata_request_legacy_irqs(struct ata_host *host,
+				   irq_handler_t const *handler,
+				   const unsigned int *irq_flags,
+				   void * const *dev_id)
+{
+	struct device *gdev = host->dev;
+	struct ata_legacy_devres *legacy_dr;
+	int i, rc;
+
+	legacy_dr = devres_find(host->dev, ata_legacy_release, NULL, NULL);
+	BUG_ON(!legacy_dr);
+
+	for (i = 0; i < host->n_ports; i++) {
+		unsigned int irq;
+
+		/* FIXME: ATA_*_IRQ() should take generic device not pci_dev */
+		if (i == 0)
+			irq = ATA_PRIMARY_IRQ(to_pci_dev(gdev));
 		else
-			probe_ent->irq = ATA_SECONDARY_IRQ(pdev);
-		probe_ent->port[1].cmd_addr = iomap[2];
-		probe_ent->port[1].altstatus_addr =
-		probe_ent->port[1].ctl_addr = iomap[3];
-		if (bmdma) {
-			probe_ent->port[1].bmdma_addr = bmdma + 8;
-			if ((!(port[1]->flags & ATA_FLAG_IGN_SIMPLEX)) &&
-			    (ioread8(bmdma + 10) & 0x80))
-				probe_ent->_host_flags |= ATA_HOST_SIMPLEX;
+			irq = ATA_SECONDARY_IRQ(to_pci_dev(gdev));
+
+		if (!(legacy_dr->mask & (1 << i)))
+			continue;
+
+		if (!handler[i]) {
+			dev_printk(KERN_ERR, gdev,
+				   "NULL handler specified for port %d\n", i);
+			rc = -EINVAL;
+			goto err_out;
 		}
-		ata_std_ports(&probe_ent->port[1]);
 
-		/* FIXME: could be pointing to stack area; must copy */
-		probe_ent->pinfo2 = port[1];
-	} else
-		probe_ent->dummy_port_mask |= ATA_PORT_SECONDARY;
+		rc = request_irq(irq, handler[i], irq_flags[i], DRV_NAME,
+				 dev_id[i]);
+		if (rc) {
+			dev_printk(KERN_ERR, gdev,
+				"irq %u request failed (errno=%d)\n", irq, rc);
+			goto err_out;
+		}
 
-	return probe_ent;
+		/* record irq allocation in legacy_dr */
+		legacy_dr->irq[i] = irq;
+		legacy_dr->irq_dev_id[i] = dev_id[i];
+
+		/* only used to print info */
+		if (i == 0)
+			host->irq = irq;
+		else
+			host->irq2 = irq;
+	}
+
+	return 0;
+
+ err_out:
+	ata_legacy_free_irqs(legacy_dr);
+	return rc;
 }
 
-
 /**
  *	ata_pci_init_one - Initialize/register PCI IDE host controller
  *	@pdev: Controller to be initialized
@@ -718,8 +949,8 @@
 		      unsigned int n_ports)
 {
 	struct device *dev = &pdev->dev;
-	struct ata_probe_ent *probe_ent = NULL;
-	struct ata_port_info *port[2];
+	struct ata_host *host = NULL;
+	const struct ata_port_info *port[2];
 	u8 mask;
 	unsigned int legacy_mode = 0;
 	int rc;
@@ -732,10 +963,7 @@
 	BUG_ON(n_ports < 1 || n_ports > 2);
 
 	port[0] = port_info[0];
-	if (n_ports > 1)
-		port[1] = port_info[1];
-	else
-		port[1] = port[0];
+	port[1] = (n_ports > 1) ? port_info[1] : NULL;
 
 	/* FIXME: Really for ATA it isn't safe because the device may be
 	   multi-purpose and we want to leave it alone if it was already
@@ -743,7 +971,7 @@
 
 	   Checking dev->is_enabled is insufficient as this is not set at
 	   boot for the primary video which is BIOS enabled
-         */
+	  */
 
 	rc = pcim_enable_device(pdev);
 	if (rc)
@@ -769,96 +997,68 @@
 #endif
 	}
 
-	if (!legacy_mode) {
-		rc = pci_request_regions(pdev, DRV_NAME);
-		if (rc) {
-			pcim_pin_device(pdev);
-			goto err_out;
-		}
-	} else {
-		/* Deal with combined mode hack. This side of the logic all
-		   goes away once the combined mode hack is killed in 2.6.21 */
-		if (!devm_request_region(dev, ATA_PRIMARY_CMD, 8, "libata")) {
-			struct resource *conflict, res;
-			res.start = ATA_PRIMARY_CMD;
-			res.end = ATA_PRIMARY_CMD + 8 - 1;
-			conflict = ____request_resource(&ioport_resource, &res);
-			while (conflict->child)
-				conflict = ____request_resource(conflict, &res);
-			if (!strcmp(conflict->name, "libata"))
-				legacy_mode |= ATA_PORT_PRIMARY;
-			else {
-				pcim_pin_device(pdev);
-				printk(KERN_WARNING "ata: 0x%0X IDE port busy\n" \
-						    "ata: conflict with %s\n",
-						    ATA_PRIMARY_CMD,
-						    conflict->name);
-			}
-		} else
-			legacy_mode |= ATA_PORT_PRIMARY;
-
-		if (!devm_request_region(dev, ATA_SECONDARY_CMD, 8, "libata")) {
-			struct resource *conflict, res;
-			res.start = ATA_SECONDARY_CMD;
-			res.end = ATA_SECONDARY_CMD + 8 - 1;
-			conflict = ____request_resource(&ioport_resource, &res);
-			while (conflict->child)
-				conflict = ____request_resource(conflict, &res);
-			if (!strcmp(conflict->name, "libata"))
-				legacy_mode |= ATA_PORT_SECONDARY;
-			else {
-				pcim_pin_device(pdev);
-				printk(KERN_WARNING "ata: 0x%X IDE port busy\n" \
-						    "ata: conflict with %s\n",
-						    ATA_SECONDARY_CMD,
-						    conflict->name);
-			}
-		} else
-			legacy_mode |= ATA_PORT_SECONDARY;
-
-		if (legacy_mode & ATA_PORT_PRIMARY)
-			pci_request_region(pdev, 1, DRV_NAME);
-		if (legacy_mode & ATA_PORT_SECONDARY)
-			pci_request_region(pdev, 3, DRV_NAME);
-		/* If there is a DMA resource, allocate it */
-		pci_request_region(pdev, 4, DRV_NAME);
-	}
-
-	/* we have legacy mode, but all ports are unavailable */
-	if (legacy_mode == (1 << 3)) {
-		rc = -EBUSY;
-		goto err_out;
-	}
-
-	/* TODO: If we get no DMA mask we should fall back to PIO */
-	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
-	if (rc)
-		goto err_out;
-	rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
-	if (rc)
-		goto err_out;
-
-	if (legacy_mode) {
-		probe_ent = ata_pci_init_legacy_port(pdev, port, legacy_mode);
-	} else {
-		if (n_ports == 2)
-			probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
-		else
-			probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY);
-	}
-	if (!probe_ent) {
+	/* alloc and init host */
+	host = ata_host_alloc_pinfo(dev, port, n_ports);
+	if (!host) {
+		dev_printk(KERN_ERR, &pdev->dev,
+			   "failed to allocate ATA host\n");
 		rc = -ENOMEM;
 		goto err_out;
 	}
 
-	pci_set_master(pdev);
+	if (!legacy_mode) {
+		unsigned int port_mask;
 
-	if (!ata_device_add(probe_ent)) {
-		rc = -ENODEV;
-		goto err_out;
+		port_mask = ATA_PORT_PRIMARY;
+		if (n_ports > 1)
+			port_mask |= ATA_PORT_SECONDARY;
+
+		rc = ata_pci_init_native_host(host, port_mask);
+		if (rc)
+			goto err_out;
+	} else {
+		int was_busy = 0;
+
+		rc = ata_init_legacy_host(host, &legacy_mode, &was_busy);
+		if (was_busy)
+			pcim_pin_device(pdev);
+		if (rc)
+			goto err_out;
+
+		/* request respective PCI regions, may fail */
+		rc = pci_request_region(pdev, 1, DRV_NAME);
+		rc = pci_request_region(pdev, 3, DRV_NAME);
 	}
 
-	devm_kfree(dev, probe_ent);
+	/* init BMDMA, may fail */
+	ata_pci_init_bmdma(host);
+	pci_set_master(pdev);
+
+	/* start host and request IRQ */
+	rc = ata_host_start(host);
+	if (rc)
+		goto err_out;
+
+	if (!legacy_mode)
+		rc = devm_request_irq(dev, pdev->irq,
+				      port_info[0]->port_ops->irq_handler,
+				      IRQF_SHARED, DRV_NAME, host);
+	else {
+		irq_handler_t handler[2] = { host->ops->irq_handler,
+					     host->ops->irq_handler };
+		unsigned int irq_flags[2] = { IRQF_SHARED, IRQF_SHARED };
+		void *dev_id[2] = { host, host };
+
+		rc = ata_request_legacy_irqs(host, handler, irq_flags, dev_id);
+	}
+	if (rc)
+		goto err_out;
+
+	/* register */
+	rc = ata_host_register(host, port_info[0]->sht);
+	if (rc)
+		goto err_out;
+
 	devres_remove_group(dev, NULL);
 	return 0;
 
@@ -893,12 +1093,12 @@
 	return 0;
 }
 
-unsigned long ata_pci_default_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long xfer_mask)
+unsigned long ata_pci_default_filter(struct ata_device *adev, unsigned long xfer_mask)
 {
 	/* Filter out DMA modes if the device has been configured by
 	   the BIOS as PIO only */
 
-	if (ap->ioaddr.bmdma_addr == 0)
+	if (adev->ap->ioaddr.bmdma_addr == 0)
 		xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA);
 	return xfer_mask;
 }
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index 1f1e3a5..5f4d40c 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -52,6 +52,7 @@
 	ATA_DNXFER_QUIET	= (1 << 31),
 };
 
+extern unsigned int ata_print_id;
 extern struct workqueue_struct *ata_aux_wq;
 extern int atapi_enabled;
 extern int atapi_dmadir;
@@ -92,10 +93,7 @@
 extern void ata_dev_init(struct ata_device *dev);
 extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg);
 extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg);
-extern void ata_port_init(struct ata_port *ap, struct ata_host *host,
-			  const struct ata_probe_ent *ent, unsigned int port_no);
-extern struct ata_probe_ent *ata_probe_ent_alloc(struct device *dev,
-						 const struct ata_port_info *port);
+extern struct ata_port *ata_port_alloc(struct ata_host *host);
 
 /* libata-acpi.c */
 #ifdef CONFIG_SATA_ACPI
@@ -113,8 +111,8 @@
 #endif
 
 /* libata-scsi.c */
-extern struct scsi_transport_template ata_scsi_transport_template;
-
+extern int ata_scsi_add_hosts(struct ata_host *host,
+			      struct scsi_host_template *sht);
 extern void ata_scsi_scan_host(struct ata_port *ap);
 extern int ata_scsi_offline_dev(struct ata_device *dev);
 extern void ata_scsi_hotplug(struct work_struct *work);
diff --git a/drivers/ata/pata_ali.c b/drivers/ata/pata_ali.c
index 11ea552..d40edeb 100644
--- a/drivers/ata/pata_ali.c
+++ b/drivers/ata/pata_ali.c
@@ -34,7 +34,7 @@
 #include <linux/dmi.h>
 
 #define DRV_NAME "pata_ali"
-#define DRV_VERSION "0.7.3"
+#define DRV_VERSION "0.7.4"
 
 /*
  *	Cable special cases
@@ -90,59 +90,6 @@
 }
 
 /**
- *	ali_early_error_handler	-	reset for eary chip
- *	@ap: ATA port
- *
- *	Handle the reset callback for the later chips with cable detect
- */
-
-static int ali_c2_pre_reset(struct ata_port *ap)
-{
-	ap->cbl = ali_c2_cable_detect(ap);
-	return ata_std_prereset(ap);
-}
-
-static void ali_c2_error_handler(struct ata_port *ap)
-{
-	ata_bmdma_drive_eh(ap, ali_c2_pre_reset,
-			       ata_std_softreset, NULL,
-			       ata_std_postreset);
-}
-
-/**
- *	ali_early_cable_detect	-	cable detection
- *	@ap: ATA port
- *
- *	Perform cable detection for older chipsets. This turns out to be
- *	rather easy to implement
- */
-
-static int ali_early_cable_detect(struct ata_port *ap)
-{
-	return ATA_CBL_PATA40;
-}
-
-/**
- *	ali_early_probe_init	-	reset for early chip
- *	@ap: ATA port
- *
- *	Handle the reset callback for the early (pre cable detect) chips.
- */
-
-static int ali_early_pre_reset(struct ata_port *ap)
-{
-	ap->cbl = ali_early_cable_detect(ap);
-	return ata_std_prereset(ap);
-}
-
-static void ali_early_error_handler(struct ata_port *ap)
-{
-	return ata_bmdma_drive_eh(ap, ali_early_pre_reset,
-				     ata_std_softreset, NULL,
-				     ata_std_postreset);
-}
-
-/**
  *	ali_20_filter		-	filter for earlier ALI DMA
  *	@ap: ALi ATA port
  *	@adev: attached device
@@ -151,7 +98,7 @@
  *	fix that later on. Also ensure we do not do UDMA on WDC drives
  */
 
-static unsigned long ali_20_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask)
+static unsigned long ali_20_filter(struct ata_device *adev, unsigned long mask)
 {
 	char model_num[ATA_ID_PROD_LEN + 1];
 	/* No DMA on anything but a disk for now */
@@ -160,7 +107,7 @@
 	ata_id_c_string(adev->id, model_num, ATA_ID_PROD, sizeof(model_num));
 	if (strstr(model_num, "WDC"))
 		return mask &= ~ATA_MASK_UDMA;
-	return ata_pci_default_filter(ap, adev, mask);
+	return ata_pci_default_filter(adev, mask);
 }
 
 /**
@@ -314,7 +261,6 @@
 
 /**
  *	ali_lock_sectors	-	Keep older devices to 255 sector mode
- *	@ap: ATA port
  *	@adev: Device
  *
  *	Called during the bus probe for each device that is found. We use
@@ -324,7 +270,7 @@
  *	slower PIO methods
  */
 
-static void ali_lock_sectors(struct ata_port *ap, struct ata_device *adev)
+static void ali_lock_sectors(struct ata_device *adev)
 {
 	adev->max_sectors = 255;
 }
@@ -366,8 +312,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= ali_early_error_handler,
+	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.qc_prep 	= ata_qc_prep,
 	.qc_issue	= ata_qc_issue_prot,
@@ -402,8 +349,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= ali_early_error_handler,
+	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
@@ -440,8 +388,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= ali_c2_error_handler,
+	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ali_c2_cable_detect,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
@@ -477,8 +426,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= ali_c2_error_handler,
+	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ali_c2_cable_detect,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
diff --git a/drivers/ata/pata_amd.c b/drivers/ata/pata_amd.c
index 1838176..536ee89 100644
--- a/drivers/ata/pata_amd.c
+++ b/drivers/ata/pata_amd.c
@@ -25,7 +25,7 @@
 #include <linux/libata.h>
 
 #define DRV_NAME "pata_amd"
-#define DRV_VERSION "0.2.8"
+#define DRV_VERSION "0.3.8"
 
 /**
  *	timing_setup		-	shared timing computation and load
@@ -119,32 +119,25 @@
 }
 
 /**
- *	amd_probe_init		-	cable detection
+ *	amd_probe_init		-	perform reset handling
  *	@ap: ATA port
  *
- *	Perform cable detection. The BIOS stores this in PCI config
- *	space for us.
+ *	Reset sequence checking enable bits to see which ports are
+ *	active.
  */
 
 static int amd_pre_reset(struct ata_port *ap)
 {
-	static const u32 bitmask[2] = {0x03, 0x0C};
 	static const struct pci_bits amd_enable_bits[] = {
 		{ 0x40, 1, 0x02, 0x02 },
 		{ 0x40, 1, 0x01, 0x01 }
 	};
 
 	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
-	u8 ata66;
 
 	if (!pci_test_config_bits(pdev, &amd_enable_bits[ap->port_no]))
 		return -ENOENT;
 
-	pci_read_config_byte(pdev, 0x42, &ata66);
-	if (ata66 & bitmask[ap->port_no])
-		ap->cbl = ATA_CBL_PATA80;
-	else
-		ap->cbl = ATA_CBL_PATA40;
 	return ata_std_prereset(ap);
 
 }
@@ -156,28 +149,16 @@
 				      ata_std_postreset);
 }
 
-static int amd_early_pre_reset(struct ata_port *ap)
+static int amd_cable_detect(struct ata_port *ap)
 {
+	static const u32 bitmask[2] = {0x03, 0x0C};
 	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
-	static struct pci_bits amd_enable_bits[] = {
-		{ 0x40, 1, 0x02, 0x02 },
-		{ 0x40, 1, 0x01, 0x01 }
-	};
+	u8 ata66;
 
-	if (!pci_test_config_bits(pdev, &amd_enable_bits[ap->port_no]))
-		return -ENOENT;
-
-	/* No host side cable detection */
-	ap->cbl = ATA_CBL_PATA80;
-	return ata_std_prereset(ap);
-
-}
-
-static void amd_early_error_handler(struct ata_port *ap)
-{
-	ata_bmdma_drive_eh(ap, amd_early_pre_reset,
-			       ata_std_softreset, NULL,
-			       ata_std_postreset);
+	pci_read_config_byte(pdev, 0x42, &ata66);
+	if (ata66 & bitmask[ap->port_no])
+		return ATA_CBL_PATA80;
+	return ATA_CBL_PATA40;
 }
 
 /**
@@ -247,31 +228,16 @@
  */
 
 static int nv_pre_reset(struct ata_port *ap) {
-	static const u8 bitmask[2] = {0x03, 0x0C};
 	static const struct pci_bits nv_enable_bits[] = {
 		{ 0x50, 1, 0x02, 0x02 },
 		{ 0x50, 1, 0x01, 0x01 }
 	};
 
 	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
-	u8 ata66;
-	u16 udma;
 
 	if (!pci_test_config_bits(pdev, &nv_enable_bits[ap->port_no]))
 		return -ENOENT;
 
-	pci_read_config_byte(pdev, 0x52, &ata66);
-	if (ata66 & bitmask[ap->port_no])
-		ap->cbl = ATA_CBL_PATA80;
-	else
-		ap->cbl = ATA_CBL_PATA40;
-
-	/* We now have to double check because the Nvidia boxes BIOS
-	   doesn't always set the cable bits but does set mode bits */
-
-	pci_read_config_word(pdev, 0x62 - 2 * ap->port_no, &udma);
-	if ((udma & 0xC4) == 0xC4 || (udma & 0xC400) == 0xC400)
-		ap->cbl = ATA_CBL_PATA80;
 	return ata_std_prereset(ap);
 }
 
@@ -281,6 +247,29 @@
 			       ata_std_softreset, NULL,
 			       ata_std_postreset);
 }
+
+static int nv_cable_detect(struct ata_port *ap)
+{
+	static const u8 bitmask[2] = {0x03, 0x0C};
+	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+	u8 ata66;
+	u16 udma;
+	int cbl;
+
+	pci_read_config_byte(pdev, 0x52, &ata66);
+	if (ata66 & bitmask[ap->port_no])
+		cbl = ATA_CBL_PATA80;
+	else
+		cbl = ATA_CBL_PATA40;
+
+ 	/* We now have to double check because the Nvidia boxes BIOS
+ 	   doesn't always set the cable bits but does set mode bits */
+ 	pci_read_config_word(pdev, 0x62 - 2 * ap->port_no, &udma);
+ 	if ((udma & 0xC4) == 0xC4 || (udma & 0xC400) == 0xC400)
+		cbl = ATA_CBL_PATA80;
+	return cbl;
+}
+
 /**
  *	nv100_set_piomode	-	set initial PIO mode data
  *	@ap: ATA interface
@@ -353,8 +342,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= amd_early_error_handler,
+	.error_handler	= amd_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
@@ -387,8 +377,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= amd_early_error_handler,
+	.error_handler	= amd_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_unknown,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
@@ -423,6 +414,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= amd_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_unknown,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
@@ -457,6 +449,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= amd_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= amd_cable_detect,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
@@ -491,6 +484,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= nv_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= nv_cable_detect,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
@@ -525,6 +519,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= nv_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= nv_cable_detect,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
diff --git a/drivers/ata/pata_artop.c b/drivers/ata/pata_artop.c
index 21c3028..00e9ec3 100644
--- a/drivers/ata/pata_artop.c
+++ b/drivers/ata/pata_artop.c
@@ -49,8 +49,6 @@
 
 	if (!pci_test_config_bits(pdev, &artop_enable_bits[ap->port_no]))
 		return -ENOENT;
-
-	ap->cbl = ATA_CBL_PATA40;
 	return ata_std_prereset(ap);
 }
 
@@ -85,18 +83,28 @@
 	};
 
 	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
-	u8 tmp;
 
 	/* Odd numbered device ids are the units with enable bits (the -R cards) */
 	if (pdev->device % 1 && !pci_test_config_bits(pdev, &artop_enable_bits[ap->port_no]))
 		return -ENOENT;
+	return ata_std_prereset(ap);
+}
 
+/**
+ *	artop6260_cable_detect	-	identify cable type
+ *	@ap: Port
+ *
+ *	Identify the cable type for the ARTOp interface in question
+ */
+ 
+static int artop6260_cable_detect(struct ata_port *ap)
+{
+	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+	u8 tmp;
 	pci_read_config_byte(pdev, 0x49, &tmp);
 	if (tmp & (1 << ap->port_no))
-		ap->cbl = ATA_CBL_PATA40;
-	else
-		ap->cbl = ATA_CBL_PATA80;
-	return ata_std_prereset(ap);
+		return ATA_CBL_PATA40;
+	return ATA_CBL_PATA80;
 }
 
 /**
@@ -225,7 +233,7 @@
 /**
  *	artop6210_set_dmamode - Initialize host controller PATA PIO timings
  *	@ap: Port whose timings we are configuring
- *	@adev: um
+ *	@adev: Device whose timings we are configuring
  *
  *	Set DMA mode for device, in host controller PCI config space.
  *
@@ -333,6 +341,7 @@
 	.thaw			= ata_bmdma_thaw,
 	.error_handler		= artop6210_error_handler,
 	.post_internal_cmd 	= ata_bmdma_post_internal_cmd,
+	.cable_detect		= ata_cable_40wire,
 
 	.bmdma_setup		= ata_bmdma_setup,
 	.bmdma_start		= ata_bmdma_start,
@@ -366,6 +375,7 @@
 	.thaw			= ata_bmdma_thaw,
 	.error_handler		= artop6260_error_handler,
 	.post_internal_cmd 	= ata_bmdma_post_internal_cmd,
+	.cable_detect		= artop6260_cable_detect,
 
 	.bmdma_setup		= ata_bmdma_setup,
 	.bmdma_start		= ata_bmdma_start,
diff --git a/drivers/ata/pata_atiixp.c b/drivers/ata/pata_atiixp.c
index 51d9923..39c871a 100644
--- a/drivers/ata/pata_atiixp.c
+++ b/drivers/ata/pata_atiixp.c
@@ -22,7 +22,7 @@
 #include <linux/libata.h>
 
 #define DRV_NAME "pata_atiixp"
-#define DRV_VERSION "0.4.4"
+#define DRV_VERSION "0.4.5"
 
 enum {
 	ATIIXP_IDE_PIO_TIMING	= 0x40,
@@ -35,23 +35,15 @@
 
 static int atiixp_pre_reset(struct ata_port *ap)
 {
-	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
 	static const struct pci_bits atiixp_enable_bits[] = {
 		{ 0x48, 1, 0x01, 0x00 },
 		{ 0x48, 1, 0x08, 0x00 }
 	};
-	u8 udma;
+	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
 
 	if (!pci_test_config_bits(pdev, &atiixp_enable_bits[ap->port_no]))
 		return -ENOENT;
 
-	/* Hack from drivers/ide/pci. Really we want to know how to do the
-	   raw detection not play follow the bios mode guess */
-	pci_read_config_byte(pdev, ATIIXP_IDE_UDMA_MODE + ap->port_no, &udma);
-	if ((udma & 0x07) >= 0x04 || (udma & 0x70) >= 0x40)
-		ap->cbl = ATA_CBL_PATA80;
-	else
-		ap->cbl = ATA_CBL_PATA40;
 	return ata_std_prereset(ap);
 }
 
@@ -60,6 +52,19 @@
 	ata_bmdma_drive_eh(ap, atiixp_pre_reset, ata_std_softreset, NULL,   ata_std_postreset);
 }
 
+static int atiixp_cable_detect(struct ata_port *ap)
+{
+	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+	u8 udma;
+
+	/* Hack from drivers/ide/pci. Really we want to know how to do the
+	   raw detection not play follow the bios mode guess */
+	pci_read_config_byte(pdev, ATIIXP_IDE_UDMA_MODE + ap->port_no, &udma);
+	if ((udma & 0x07) >= 0x04 || (udma & 0x70) >= 0x40)
+		return  ATA_CBL_PATA80;
+	return ATA_CBL_PATA40;
+}
+
 /**
  *	atiixp_set_pio_timing	-	set initial PIO mode data
  *	@ap: ATA interface
@@ -245,6 +250,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= atiixp_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= atiixp_cable_detect,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= atiixp_bmdma_start,
diff --git a/drivers/ata/pata_cmd640.c b/drivers/ata/pata_cmd640.c
new file mode 100644
index 0000000..2105985
--- /dev/null
+++ b/drivers/ata/pata_cmd640.c
@@ -0,0 +1,312 @@
+/*
+ * pata_cmd640.c 	- CMD640 PCI PATA for new ATA layer
+ *			  (C) 2007 Red Hat Inc
+ *			  Alan Cox <alan@redhat.com>
+ *
+ * Based upon
+ *  linux/drivers/ide/pci/cmd640.c		Version 1.02  Sep 01, 1996
+ *
+ *  Copyright (C) 1995-1996  Linus Torvalds & authors (see driver)
+ *
+ *	This drives only the PCI version of the controller. If you have a
+ *	VLB one then we have enough docs to support it but you can write
+ *	your own code.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+
+#define DRV_NAME "pata_cmd640"
+#define DRV_VERSION "0.0.5"
+
+struct cmd640_reg {
+	int last;
+	u8 reg58[ATA_MAX_DEVICES];
+};
+
+enum {
+	CFR = 0x50,
+	CNTRL = 0x51,
+	CMDTIM = 0x52,
+	ARTIM0 = 0x53,
+	DRWTIM0 = 0x54,
+	ARTIM23 = 0x57,
+	DRWTIM23 = 0x58,
+	BRST = 0x59
+};
+
+/**
+ *	cmd640_set_piomode	-	set initial PIO mode data
+ *	@ap: ATA port
+ *	@adev: ATA device
+ *
+ *	Called to do the PIO mode setup.
+ */
+
+static void cmd640_set_piomode(struct ata_port *ap, struct ata_device *adev)
+{
+	struct cmd640_reg *timing = ap->private_data;
+	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+	struct ata_timing t;
+	const unsigned long T = 1000000 / 33;
+	const u8 setup_data[] = { 0x40, 0x40, 0x40, 0x80, 0x00 };
+	u8 reg;
+	int arttim = ARTIM0 + 2 * adev->devno;
+	struct ata_device *pair = ata_dev_pair(adev);
+
+	if (ata_timing_compute(adev, adev->pio_mode, &t, T, 0) < 0) {
+		printk(KERN_ERR DRV_NAME ": mode computation failed.\n");
+		return;
+	}
+
+	/* The second channel has shared timings and the setup timing is
+	   messy to switch to merge it for worst case */
+	if (ap->port_no && pair) {
+		struct ata_timing p;
+		ata_timing_compute(pair, pair->pio_mode, &p, T, 1);
+		ata_timing_merge(&p, &t, &t, ATA_TIMING_SETUP);
+	}
+
+	/* Make the timings fit */
+	if (t.recover > 16) {
+		t.active += t.recover - 16;
+		t.recover = 16;
+	}
+	if (t.active > 16)
+		t.active = 16;
+
+	/* Now convert the clocks into values we can actually stuff into
+	   the chip */
+
+	if (t.recover > 1)
+		t.recover--;	/* 640B only */
+	else
+		t.recover = 15;
+
+	if (t.setup > 4)
+		t.setup = 0xC0;
+	else
+		t.setup = setup_data[t.setup];
+
+	if (ap->port_no == 0) {
+		t.active &= 0x0F;	/* 0 = 16 */
+
+		/* Load setup timing */
+		pci_read_config_byte(pdev, arttim, &reg);
+		reg &= 0x3F;
+		reg |= t.setup;
+		pci_write_config_byte(pdev, arttim, reg);
+
+		/* Load active/recovery */
+		pci_write_config_byte(pdev, arttim + 1, (t.active << 4) | t.recover);
+	} else {
+		/* Save the shared timings for channel, they will be loaded
+		   by qc_issue_prot. Reloading the setup time is expensive 
+		   so we keep a merged one loaded */
+		pci_read_config_byte(pdev, ARTIM23, &reg);
+		reg &= 0x3F;
+		reg |= t.setup;
+		pci_write_config_byte(pdev, ARTIM23, reg);
+		timing->reg58[adev->devno] = (t.active << 4) | t.recover;
+	}
+}
+
+
+/**
+ *	cmd640_qc_issue_prot	-	command preparation hook
+ *	@qc: Command to be issued
+ *
+ *	Channel 1 has shared timings. We must reprogram the
+ *	clock each drive 2/3 switch we do.
+ */
+
+static unsigned int cmd640_qc_issue_prot(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct ata_device *adev = qc->dev;
+	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+	struct cmd640_reg *timing = ap->private_data;
+
+	if (ap->port_no != 0 && adev->devno != timing->last) {
+		pci_write_config_byte(pdev, DRWTIM23, timing->reg58[adev->devno]);
+		timing->last = adev->devno;
+	}
+	return ata_qc_issue_prot(qc);
+}
+
+/**
+ *	cmd640_port_start	-	port setup
+ *	@ap: ATA port being set up
+ *
+ *	The CMD640 needs to maintain private data structures so we
+ *	allocate space here.
+ */
+
+static int cmd640_port_start(struct ata_port *ap)
+{
+	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+	struct cmd640_reg *timing;
+
+	int ret = ata_port_start(ap);
+	if (ret < 0)
+		return ret;
+
+	timing = devm_kzalloc(&pdev->dev, sizeof(struct cmd640_reg), GFP_KERNEL);
+	if (timing == NULL)
+		return -ENOMEM;
+	timing->last = -1;	/* Force a load */
+	ap->private_data = timing;
+	return ret;
+}
+
+static struct scsi_host_template cmd640_sht = {
+	.module			= THIS_MODULE,
+	.name			= DRV_NAME,
+	.ioctl			= ata_scsi_ioctl,
+	.queuecommand		= ata_scsi_queuecmd,
+	.can_queue		= ATA_DEF_QUEUE,
+	.this_id		= ATA_SHT_THIS_ID,
+	.sg_tablesize		= LIBATA_MAX_PRD,
+	.cmd_per_lun		= ATA_SHT_CMD_PER_LUN,
+	.emulated		= ATA_SHT_EMULATED,
+	.use_clustering		= ATA_SHT_USE_CLUSTERING,
+	.proc_name		= DRV_NAME,
+	.dma_boundary		= ATA_DMA_BOUNDARY,
+	.slave_configure	= ata_scsi_slave_config,
+	.slave_destroy		= ata_scsi_slave_destroy,
+	.bios_param		= ata_std_bios_param,
+#ifdef CONFIG_PM
+	.resume			= ata_scsi_device_resume,
+	.suspend		= ata_scsi_device_suspend,
+#endif
+};
+
+static struct ata_port_operations cmd640_port_ops = {
+	.port_disable	= ata_port_disable,
+	.set_piomode	= cmd640_set_piomode,
+	.mode_filter	= ata_pci_default_filter,
+	.tf_load	= ata_tf_load,
+	.tf_read	= ata_tf_read,
+	.check_status 	= ata_check_status,
+	.exec_command	= ata_exec_command,
+	.dev_select 	= ata_std_dev_select,
+
+	.freeze		= ata_bmdma_freeze,
+	.thaw		= ata_bmdma_thaw,
+	.error_handler	= ata_bmdma_error_handler,
+	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
+
+	.bmdma_setup 	= ata_bmdma_setup,
+	.bmdma_start 	= ata_bmdma_start,
+	.bmdma_stop	= ata_bmdma_stop,
+	.bmdma_status 	= ata_bmdma_status,
+
+	.qc_prep 	= ata_qc_prep,
+	.qc_issue	= cmd640_qc_issue_prot,
+
+	/* In theory this is not needed once we kill the prefetcher */
+	.data_xfer	= ata_data_xfer_noirq,
+
+	.irq_handler	= ata_interrupt,
+	.irq_clear	= ata_bmdma_irq_clear,
+	.irq_on		= ata_irq_on,
+	.irq_ack	= ata_irq_ack,
+
+	.port_start	= cmd640_port_start,
+};
+
+static void cmd640_hardware_init(struct pci_dev *pdev)
+{
+	u8 r;
+	u8 ctrl;
+
+	/* CMD640 detected, commiserations */
+	pci_write_config_byte(pdev, 0x5B, 0x00);
+	/* Get version info */
+	pci_read_config_byte(pdev, CFR, &r);
+	/* PIO0 command cycles */
+	pci_write_config_byte(pdev, CMDTIM, 0);
+	/* 512 byte bursts (sector) */
+	pci_write_config_byte(pdev, BRST, 0x40);
+	/* 
+	 * A reporter a long time ago
+	 * Had problems with the data fifo
+	 * So don't run the risk
+	 * Of putting crap on the disk
+	 * For its better just to go slow
+	 */
+	/* Do channel 0 */
+	pci_read_config_byte(pdev, CNTRL, &ctrl);
+	pci_write_config_byte(pdev, CNTRL, ctrl | 0xC0);
+	/* Ditto for channel 1 */
+	pci_read_config_byte(pdev, ARTIM23, &ctrl);
+	ctrl |= 0x0C;
+	pci_write_config_byte(pdev, ARTIM23, ctrl);
+}
+
+static int cmd640_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	static struct ata_port_info info = {
+		.sht = &cmd640_sht,
+		.flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST,
+		.pio_mask = 0x1f,
+		.port_ops = &cmd640_port_ops
+	};
+
+	static struct ata_port_info *port_info[2] = { &info, &info };
+
+	cmd640_hardware_init(pdev);
+	return ata_pci_init_one(pdev, port_info, 2);
+}
+
+static int cmd640_reinit_one(struct pci_dev *pdev)
+{
+	cmd640_hardware_init(pdev);
+#ifdef CONFIG_PM
+	return ata_pci_device_resume(pdev);
+#else
+	return 0;
+#endif
+}
+
+static const struct pci_device_id cmd640[] = {
+	{ PCI_VDEVICE(CMD, 0x640), 0 },
+	{ },
+};
+
+static struct pci_driver cmd640_pci_driver = {
+	.name 		= DRV_NAME,
+	.id_table	= cmd640,
+	.probe 		= cmd640_init_one,
+	.remove		= ata_pci_remove_one,
+#ifdef CONFIG_PM
+	.suspend	= ata_pci_device_suspend,
+#endif
+	.resume		= cmd640_reinit_one,
+};
+
+static int __init cmd640_init(void)
+{
+	return pci_register_driver(&cmd640_pci_driver);
+}
+
+static void __exit cmd640_exit(void)
+{
+	pci_unregister_driver(&cmd640_pci_driver);
+}
+
+MODULE_AUTHOR("Alan Cox");
+MODULE_DESCRIPTION("low-level driver for CMD640 PATA controllers");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, cmd640);
+MODULE_VERSION(DRV_VERSION);
+
+module_init(cmd640_init);
+module_exit(cmd640_exit);
diff --git a/drivers/ata/pata_cmd64x.c b/drivers/ata/pata_cmd64x.c
index 5b13bdd..3989cc5 100644
--- a/drivers/ata/pata_cmd64x.c
+++ b/drivers/ata/pata_cmd64x.c
@@ -75,13 +75,7 @@
 	DTPR1		= 0x7C
 };
 
-static int cmd64x_pre_reset(struct ata_port *ap)
-{
-	ap->cbl = ATA_CBL_PATA40;
-	return ata_std_prereset(ap);
-}
-
-static int cmd648_pre_reset(struct ata_port *ap)
+static int cmd648_cable_detect(struct ata_port *ap)
 {
 	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
 	u8 r;
@@ -89,21 +83,8 @@
 	/* Check cable detect bits */
 	pci_read_config_byte(pdev, BMIDECSR, &r);
 	if (r & (1 << ap->port_no))
-		ap->cbl = ATA_CBL_PATA80;
-	else
-		ap->cbl = ATA_CBL_PATA40;
-
-	return ata_std_prereset(ap);
-}
-
-static void cmd64x_error_handler(struct ata_port *ap)
-{
-	return ata_bmdma_drive_eh(ap, cmd64x_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
-static void cmd648_error_handler(struct ata_port *ap)
-{
-	ata_bmdma_drive_eh(ap, cmd648_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
+		return ATA_CBL_PATA80;
+	return ATA_CBL_PATA40;
 }
 
 /**
@@ -304,8 +285,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= cmd64x_error_handler,
+	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
@@ -338,8 +320,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= cmd64x_error_handler,
+	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
@@ -372,8 +355,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= cmd648_error_handler,
+	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= cmd648_cable_detect,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
diff --git a/drivers/ata/pata_cs5520.c b/drivers/ata/pata_cs5520.c
index 55cc293..79bef0d 100644
--- a/drivers/ata/pata_cs5520.c
+++ b/drivers/ata/pata_cs5520.c
@@ -139,18 +139,6 @@
 	cs5520_set_timings(ap, adev, adev->pio_mode);
 }
 
-
-static int cs5520_pre_reset(struct ata_port *ap)
-{
-	ap->cbl = ATA_CBL_PATA40;
-	return ata_std_prereset(ap);
-}
-
-static void cs5520_error_handler(struct ata_port *ap)
-{
-	return ata_bmdma_drive_eh(ap, cs5520_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
 static struct scsi_host_template cs5520_sht = {
 	.module			= THIS_MODULE,
 	.name			= DRV_NAME,
@@ -186,8 +174,9 @@
 
 	.freeze			= ata_bmdma_freeze,
 	.thaw			= ata_bmdma_thaw,
-	.error_handler		= cs5520_error_handler,
+	.error_handler		= ata_bmdma_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
+	.cable_detect		= ata_cable_40wire,
 
 	.bmdma_setup		= ata_bmdma_setup,
 	.bmdma_start		= ata_bmdma_start,
@@ -197,7 +186,6 @@
 	.qc_issue		= ata_qc_issue_prot,
 	.data_xfer		= ata_data_xfer,
 
-	.irq_handler		= ata_interrupt,
 	.irq_clear		= ata_bmdma_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -205,91 +193,104 @@
 	.port_start		= ata_port_start,
 };
 
-static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_device_id *id)
+static int __devinit cs5520_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
 {
+	struct ata_port_info pi = {
+		.flags		= ATA_FLAG_SLAVE_POSS,
+		.pio_mask	= 0x1f,
+		.port_ops	= &cs5520_port_ops,
+	};
+	const struct ata_port_info *ppi[2];
 	u8 pcicfg;
-	void __iomem *iomap[5];
-	static struct ata_probe_ent probe[2];
-	int ports = 0;
+	void *iomap[5];
+	struct ata_host *host;
+	struct ata_ioports *ioaddr;
+	int i, rc;
 
 	/* IDE port enable bits */
-	pci_read_config_byte(dev, 0x60, &pcicfg);
+	pci_read_config_byte(pdev, 0x60, &pcicfg);
 
 	/* Check if the ATA ports are enabled */
 	if ((pcicfg & 3) == 0)
 		return -ENODEV;
 
+	ppi[0] = ppi[1] = &ata_dummy_port_info;
+	if (pcicfg & 1)
+		ppi[0] = &pi;
+	if (pcicfg & 2)
+		ppi[1] = &pi;
+
 	if ((pcicfg & 0x40) == 0) {
-		printk(KERN_WARNING DRV_NAME ": DMA mode disabled. Enabling.\n");
-		pci_write_config_byte(dev, 0x60, pcicfg | 0x40);
+		dev_printk(KERN_WARNING, &pdev->dev,
+			   "DMA mode disabled. Enabling.\n");
+		pci_write_config_byte(pdev, 0x60, pcicfg | 0x40);
 	}
 
+	pi.mwdma_mask = id->driver_data;
+
+	host = ata_host_alloc_pinfo(&pdev->dev, ppi, 2);
+	if (!host)
+		return -ENOMEM;
+
 	/* Perform set up for DMA */
-	if (pci_enable_device_bars(dev, 1<<2)) {
+	if (pci_enable_device_bars(pdev, 1<<2)) {
 		printk(KERN_ERR DRV_NAME ": unable to configure BAR2.\n");
 		return -ENODEV;
 	}
-	pci_set_master(dev);
-	if (pci_set_dma_mask(dev, DMA_32BIT_MASK)) {
+
+	if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
 		printk(KERN_ERR DRV_NAME ": unable to configure DMA mask.\n");
 		return -ENODEV;
 	}
-	if (pci_set_consistent_dma_mask(dev, DMA_32BIT_MASK)) {
+	if (pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)) {
 		printk(KERN_ERR DRV_NAME ": unable to configure consistent DMA mask.\n");
 		return -ENODEV;
 	}
 
-	/* Map IO ports */
-	iomap[0] = devm_ioport_map(&dev->dev, 0x1F0, 8);
-	iomap[1] = devm_ioport_map(&dev->dev, 0x3F6, 1);
-	iomap[2] = devm_ioport_map(&dev->dev, 0x170, 8);
-	iomap[3] = devm_ioport_map(&dev->dev, 0x376, 1);
-	iomap[4] = pcim_iomap(dev, 2, 0);
+	/* Map IO ports and initialize host accordingly */
+	iomap[0] = devm_ioport_map(&pdev->dev, 0x1F0, 8);
+	iomap[1] = devm_ioport_map(&pdev->dev, 0x3F6, 1);
+	iomap[2] = devm_ioport_map(&pdev->dev, 0x170, 8);
+	iomap[3] = devm_ioport_map(&pdev->dev, 0x376, 1);
+	iomap[4] = pcim_iomap(pdev, 2, 0);
 
 	if (!iomap[0] || !iomap[1] || !iomap[2] || !iomap[3] || !iomap[4])
 		return -ENOMEM;
 
-	/* We have to do our own plumbing as the PCI setup for this
-	   chipset is non-standard so we can't punt to the libata code */
+	ioaddr = &host->ports[0]->ioaddr;
+	ioaddr->cmd_addr = iomap[0];
+	ioaddr->ctl_addr = iomap[1];
+	ioaddr->altstatus_addr = iomap[1];
+	ioaddr->bmdma_addr = iomap[4];
+	ata_std_ports(ioaddr);
 
-	INIT_LIST_HEAD(&probe[0].node);
-	probe[0].dev = pci_dev_to_dev(dev);
-	probe[0].port_ops = &cs5520_port_ops;
-	probe[0].sht = &cs5520_sht;
-	probe[0].pio_mask = 0x1F;
-	probe[0].mwdma_mask = id->driver_data;
-	probe[0].irq = 14;
-	probe[0].irq_flags = 0;
-	probe[0].port_flags = ATA_FLAG_SLAVE_POSS|ATA_FLAG_SRST;
-	probe[0].n_ports = 1;
-	probe[0].port[0].cmd_addr = iomap[0];
-	probe[0].port[0].ctl_addr = iomap[1];
-	probe[0].port[0].altstatus_addr = iomap[1];
-	probe[0].port[0].bmdma_addr = iomap[4];
+	ioaddr = &host->ports[1]->ioaddr;
+	ioaddr->cmd_addr = iomap[2];
+	ioaddr->ctl_addr = iomap[3];
+	ioaddr->altstatus_addr = iomap[3];
+	ioaddr->bmdma_addr = iomap[4] + 8;
+	ata_std_ports(ioaddr);
 
-	/* The secondary lurks at different addresses but is otherwise
-	   the same beastie */
+	/* activate the host */
+	pci_set_master(pdev);
+	rc = ata_host_start(host);
+	if (rc)
+		return rc;
 
-	probe[1] = probe[0];
-	INIT_LIST_HEAD(&probe[1].node);
-	probe[1].irq = 15;
-	probe[1].port[0].cmd_addr = iomap[2];
-	probe[1].port[0].ctl_addr = iomap[3];
-	probe[1].port[0].altstatus_addr = iomap[3];
-	probe[1].port[0].bmdma_addr = iomap[4] + 8;
+	for (i = 0; i < 2; i++) {
+		static const int irq[] = { 14, 15 };
+		struct ata_port *ap = host->ports[0];
 
-	/* Let libata fill in the port details */
-	ata_std_ports(&probe[0].port[0]);
-	ata_std_ports(&probe[1].port[0]);
+		if (ata_port_is_dummy(ap))
+			continue;
 
-	/* Now add the ports that are active */
-	if (pcicfg & 1)
-		ports += ata_device_add(&probe[0]);
-	if (pcicfg & 2)
-		ports += ata_device_add(&probe[1]);
-	if (ports)
-		return 0;
-	return -ENODEV;
+		rc = devm_request_irq(&pdev->dev, irq[ap->port_no],
+				      ata_interrupt, 0, DRV_NAME, host);
+		if (rc)
+			return rc;
+	}
+
+	return ata_host_register(host, &cs5520_sht);
 }
 
 /**
diff --git a/drivers/ata/pata_cs5530.c b/drivers/ata/pata_cs5530.c
index db63e80..29642d5 100644
--- a/drivers/ata/pata_cs5530.c
+++ b/drivers/ata/pata_cs5530.c
@@ -160,18 +160,6 @@
 	return ata_qc_issue_prot(qc);
 }
 
-static int cs5530_pre_reset(struct ata_port *ap)
-{
-	ap->cbl = ATA_CBL_PATA40;
-	return ata_std_prereset(ap);
-}
-
-static void cs5530_error_handler(struct ata_port *ap)
-{
-	return ata_bmdma_drive_eh(ap, cs5530_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
-
 static struct scsi_host_template cs5530_sht = {
 	.module			= THIS_MODULE,
 	.name			= DRV_NAME,
@@ -213,8 +201,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= cs5530_error_handler,
+	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.qc_prep 	= ata_qc_prep,
 	.qc_issue	= cs5530_qc_issue_prot,
diff --git a/drivers/ata/pata_cs5535.c b/drivers/ata/pata_cs5535.c
index 1572e5c..08cccc9 100644
--- a/drivers/ata/pata_cs5535.c
+++ b/drivers/ata/pata_cs5535.c
@@ -70,36 +70,23 @@
 #define CS5535_BAD_PIO(timings) ( (timings&~0x80000000UL)==0x00009172 )
 
 /**
- *	cs5535_pre_reset	-	detect cable type
+ *	cs5535_cable_detect	-	detect cable type
  *	@ap: Port to detect on
  *
  *	Perform cable detection for ATA66 capable cable. Return a libata
  *	cable type.
  */
 
-static int cs5535_pre_reset(struct ata_port *ap)
+static int cs5535_cable_detect(struct ata_port *ap)
 {
 	u8 cable;
 	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
 
 	pci_read_config_byte(pdev, CS5535_CABLE_DETECT, &cable);
 	if (cable & 1)
-		ap->cbl = ATA_CBL_PATA80;
+		return ATA_CBL_PATA80;
 	else
-		ap->cbl = ATA_CBL_PATA40;
-	return ata_std_prereset(ap);
-}
-
-/**
- *	cs5535_error_handler		-	reset/probe
- *	@ap: Port to reset
- *
- *	Reset and configure a port
- */
-
-static void cs5535_error_handler(struct ata_port *ap)
-{
-	ata_bmdma_drive_eh(ap, cs5535_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
+		return ATA_CBL_PATA40;
 }
 
 /**
@@ -205,8 +192,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= cs5535_error_handler,
+	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= cs5535_cable_detect,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
diff --git a/drivers/ata/pata_cypress.c b/drivers/ata/pata_cypress.c
index f69dde5..6ec049c 100644
--- a/drivers/ata/pata_cypress.c
+++ b/drivers/ata/pata_cypress.c
@@ -41,17 +41,6 @@
 	CY82_INDEX_TIMEOUT	= 0x32
 };
 
-static int cy82c693_pre_reset(struct ata_port *ap)
-{
-	ap->cbl = ATA_CBL_PATA40;
-	return ata_std_prereset(ap);
-}
-
-static void cy82c693_error_handler(struct ata_port *ap)
-{
-	ata_bmdma_drive_eh(ap, cy82c693_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
 /**
  *	cy82c693_set_piomode	-	set initial PIO mode data
  *	@ap: ATA interface
@@ -156,8 +145,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= cy82c693_error_handler,
+	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
diff --git a/drivers/ata/pata_efar.c b/drivers/ata/pata_efar.c
index dac7a65..a321685 100644
--- a/drivers/ata/pata_efar.c
+++ b/drivers/ata/pata_efar.c
@@ -22,10 +22,10 @@
 #include <linux/ata.h>
 
 #define DRV_NAME	"pata_efar"
-#define DRV_VERSION	"0.4.3"
+#define DRV_VERSION	"0.4.4"
 
 /**
- *	efar_pre_reset	-	check for 40/80 pin
+ *	efar_pre_reset	-	Enable bits
  *	@ap: Port
  *
  *	Perform cable detection for the EFAR ATA interface. This is
@@ -38,18 +38,11 @@
 		{ 0x41U, 1U, 0x80UL, 0x80UL },	/* port 0 */
 		{ 0x43U, 1U, 0x80UL, 0x80UL },	/* port 1 */
 	};
-
 	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
-	u8 tmp;
 
 	if (!pci_test_config_bits(pdev, &efar_enable_bits[ap->port_no]))
 		return -ENOENT;
 
-	pci_read_config_byte(pdev, 0x47, &tmp);
-	if (tmp & (2 >> ap->port_no))
-		ap->cbl = ATA_CBL_PATA40;
-	else
-		ap->cbl = ATA_CBL_PATA80;
 	return ata_std_prereset(ap);
 }
 
@@ -67,6 +60,25 @@
 }
 
 /**
+ *	efar_cable_detect	-	check for 40/80 pin
+ *	@ap: Port
+ *
+ *	Perform cable detection for the EFAR ATA interface. This is
+ *	different to the PIIX arrangement
+ */
+
+static int efar_cable_detect(struct ata_port *ap)
+{
+	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+	u8 tmp;
+
+	pci_read_config_byte(pdev, 0x47, &tmp);
+	if (tmp & (2 >> ap->port_no))
+		return ATA_CBL_PATA40;
+	return ATA_CBL_PATA80;
+}
+
+/**
  *	efar_set_piomode - Initialize host controller PATA PIO timings
  *	@ap: Port whose timings we are configuring
  *	@adev: um
@@ -256,6 +268,7 @@
 	.thaw			= ata_bmdma_thaw,
 	.error_handler		= efar_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
+	.cable_detect		= efar_cable_detect,
 
 	.bmdma_setup		= ata_bmdma_setup,
 	.bmdma_start		= ata_bmdma_start,
diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c
index baf35f8..93cfa6d 100644
--- a/drivers/ata/pata_hpt366.c
+++ b/drivers/ata/pata_hpt366.c
@@ -27,7 +27,7 @@
 #include <linux/libata.h>
 
 #define DRV_NAME	"pata_hpt366"
-#define DRV_VERSION	"0.6.0"
+#define DRV_VERSION	"0.6.1"
 
 struct hpt_clock {
 	u8	xfer_speed;
@@ -169,13 +169,12 @@
 
 /**
  *	hpt366_filter	-	mode selection filter
- *	@ap: ATA interface
  *	@adev: ATA device
  *
  *	Block UDMA on devices that cause trouble with this controller.
  */
 
-static unsigned long hpt366_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask)
+static unsigned long hpt366_filter(struct ata_device *adev, unsigned long mask)
 {
 	if (adev->class == ATA_DEV_ATA) {
 		if (hpt_dma_blacklisted(adev, "UDMA",  bad_ata33))
@@ -185,7 +184,7 @@
 		if (hpt_dma_blacklisted(adev, "UDMA4", bad_ata66_4))
 			mask &= ~(0x0F << ATA_SHIFT_UDMA);
 	}
-	return ata_pci_default_filter(ap, adev, mask);
+	return ata_pci_default_filter(adev, mask);
 }
 
 /**
@@ -210,24 +209,28 @@
 	return 0xffffffffU;	/* silence compiler warning */
 }
 
+static int hpt36x_cable_detect(struct ata_port *ap)
+{
+	u8 ata66;
+	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+
+	pci_read_config_byte(pdev, 0x5A, &ata66);
+	if (ata66 & (1 << ap->port_no))
+		return ATA_CBL_PATA40;
+	return ATA_CBL_PATA80;
+}
+
 static int hpt36x_pre_reset(struct ata_port *ap)
 {
 	static const struct pci_bits hpt36x_enable_bits[] = {
 		{ 0x50, 1, 0x04, 0x04 },
 		{ 0x54, 1, 0x04, 0x04 }
 	};
-
-	u8 ata66;
 	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
 
 	if (!pci_test_config_bits(pdev, &hpt36x_enable_bits[ap->port_no]))
 		return -ENOENT;
 
-	pci_read_config_byte(pdev, 0x5A, &ata66);
-	if (ata66 & (1 << ap->port_no))
-		ap->cbl = ATA_CBL_PATA40;
-	else
-		ap->cbl = ATA_CBL_PATA80;
 	return ata_std_prereset(ap);
 }
 
@@ -354,6 +357,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= hpt36x_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= hpt36x_cable_detect,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
diff --git a/drivers/ata/pata_hpt37x.c b/drivers/ata/pata_hpt37x.c
index f331eee..41d83129 100644
--- a/drivers/ata/pata_hpt37x.c
+++ b/drivers/ata/pata_hpt37x.c
@@ -8,6 +8,7 @@
  * Copyright (C) 1999-2003		Andre Hedrick <andre@linux-ide.org>
  * Portions Copyright (C) 2001	        Sun Microsystems, Inc.
  * Portions Copyright (C) 2003		Red Hat Inc
+ * Portions Copyright (C) 2005-2006	MontaVista Software, Inc.
  *
  * TODO
  *	PLL mode
@@ -25,7 +26,7 @@
 #include <linux/libata.h>
 
 #define DRV_NAME	"pata_hpt37x"
-#define DRV_VERSION	"0.6.0"
+#define DRV_VERSION	"0.6.5"
 
 struct hpt_clock {
 	u8	xfer_speed;
@@ -61,201 +62,75 @@
  * 31     FIFO enable.
  */
 
-/* from highpoint documentation. these are old values */
-static const struct hpt_clock hpt370_timings_33[] = {
-/*	{	XFER_UDMA_5,	0x1A85F442,	0x16454e31	}, */
-	{	XFER_UDMA_5,	0x16454e31	},
-	{	XFER_UDMA_4,	0x16454e31	},
-	{	XFER_UDMA_3,	0x166d4e31	},
-	{	XFER_UDMA_2,	0x16494e31	},
-	{	XFER_UDMA_1,	0x164d4e31	},
-	{	XFER_UDMA_0,	0x16514e31	},
+static struct hpt_clock hpt37x_timings_33[] = {
+	{ XFER_UDMA_6,		0x12446231 },	/* 0x12646231 ?? */
+	{ XFER_UDMA_5,		0x12446231 },
+	{ XFER_UDMA_4,		0x12446231 },
+	{ XFER_UDMA_3,		0x126c6231 },
+	{ XFER_UDMA_2,		0x12486231 },
+	{ XFER_UDMA_1,		0x124c6233 },
+	{ XFER_UDMA_0,		0x12506297 },
 
-	{	XFER_MW_DMA_2,	0x26514e21	},
-	{	XFER_MW_DMA_1,	0x26514e33	},
-	{	XFER_MW_DMA_0,	0x26514e97	},
+	{ XFER_MW_DMA_2,	0x22406c31 },
+	{ XFER_MW_DMA_1,	0x22406c33 },
+	{ XFER_MW_DMA_0,	0x22406c97 },
 
-	{	XFER_PIO_4,	0x06514e21	},
-	{	XFER_PIO_3,	0x06514e22	},
-	{	XFER_PIO_2,	0x06514e33	},
-	{	XFER_PIO_1,	0x06914e43	},
-	{	XFER_PIO_0,	0x06914e57	},
-	{	0,		0x06514e57	}
+	{ XFER_PIO_4,		0x06414e31 },
+	{ XFER_PIO_3,		0x06414e42 },
+	{ XFER_PIO_2,		0x06414e53 },
+	{ XFER_PIO_1,		0x06814e93 },
+	{ XFER_PIO_0,		0x06814ea7 }
 };
 
-static const struct hpt_clock hpt370_timings_66[] = {
-	{	XFER_UDMA_5,	0x14846231	},
-	{	XFER_UDMA_4,	0x14886231	},
-	{	XFER_UDMA_3,	0x148c6231	},
-	{	XFER_UDMA_2,	0x148c6231	},
-	{	XFER_UDMA_1,	0x14906231	},
-	{	XFER_UDMA_0,	0x14986231	},
+static struct hpt_clock hpt37x_timings_50[] = {
+	{ XFER_UDMA_6,		0x12848242 },
+	{ XFER_UDMA_5,		0x12848242 },
+	{ XFER_UDMA_4,		0x12ac8242 },
+	{ XFER_UDMA_3,		0x128c8242 },
+	{ XFER_UDMA_2,		0x120c8242 },
+	{ XFER_UDMA_1,		0x12148254 },
+	{ XFER_UDMA_0,		0x121882ea },
 
-	{	XFER_MW_DMA_2,	0x26514e21	},
-	{	XFER_MW_DMA_1,	0x26514e33	},
-	{	XFER_MW_DMA_0,	0x26514e97	},
+	{ XFER_MW_DMA_2,	0x22808242 },
+	{ XFER_MW_DMA_1,	0x22808254 },
+	{ XFER_MW_DMA_0,	0x228082ea },
 
-	{	XFER_PIO_4,	0x06514e21	},
-	{	XFER_PIO_3,	0x06514e22	},
-	{	XFER_PIO_2,	0x06514e33	},
-	{	XFER_PIO_1,	0x06914e43	},
-	{	XFER_PIO_0,	0x06914e57	},
-	{	0,		0x06514e57	}
+	{ XFER_PIO_4,		0x0a81f442 },
+	{ XFER_PIO_3,		0x0a81f443 },
+	{ XFER_PIO_2,		0x0a81f454 },
+	{ XFER_PIO_1,		0x0ac1f465 },
+	{ XFER_PIO_0,		0x0ac1f48a }
 };
 
-/* these are the current (4 sep 2001) timings from highpoint */
-static const struct hpt_clock hpt370a_timings_33[] = {
-	{	XFER_UDMA_5,	0x12446231	},
-	{	XFER_UDMA_4,	0x12446231	},
-	{	XFER_UDMA_3,	0x126c6231	},
-	{	XFER_UDMA_2,	0x12486231	},
-	{	XFER_UDMA_1,	0x124c6233	},
-	{	XFER_UDMA_0,	0x12506297	},
+static struct hpt_clock hpt37x_timings_66[] = {
+	{ XFER_UDMA_6,		0x1c869c62 },
+	{ XFER_UDMA_5,		0x1cae9c62 },	/* 0x1c8a9c62 */
+	{ XFER_UDMA_4,		0x1c8a9c62 },
+	{ XFER_UDMA_3,		0x1c8e9c62 },
+	{ XFER_UDMA_2,		0x1c929c62 },
+	{ XFER_UDMA_1,		0x1c9a9c62 },
+	{ XFER_UDMA_0,		0x1c829c62 },
 
-	{	XFER_MW_DMA_2,	0x22406c31	},
-	{	XFER_MW_DMA_1,	0x22406c33	},
-	{	XFER_MW_DMA_0,	0x22406c97	},
+	{ XFER_MW_DMA_2,	0x2c829c62 },
+	{ XFER_MW_DMA_1,	0x2c829c66 },
+	{ XFER_MW_DMA_0,	0x2c829d2e },
 
-	{	XFER_PIO_4,	0x06414e31	},
-	{	XFER_PIO_3,	0x06414e42	},
-	{	XFER_PIO_2,	0x06414e53	},
-	{	XFER_PIO_1,	0x06814e93	},
-	{	XFER_PIO_0,	0x06814ea7	},
-	{	0,		0x06814ea7	}
+	{ XFER_PIO_4,		0x0c829c62 },
+	{ XFER_PIO_3,		0x0c829c84 },
+	{ XFER_PIO_2,		0x0c829ca6 },
+	{ XFER_PIO_1,		0x0d029d26 },
+	{ XFER_PIO_0,		0x0d029d5e }
 };
 
-/* 2x 33MHz timings */
-static const struct hpt_clock hpt370a_timings_66[] = {
-	{	XFER_UDMA_5,	0x1488e673	},
-	{	XFER_UDMA_4,	0x1488e673	},
-	{	XFER_UDMA_3,	0x1498e673	},
-	{	XFER_UDMA_2,	0x1490e673	},
-	{	XFER_UDMA_1,	0x1498e677	},
-	{	XFER_UDMA_0,	0x14a0e73f	},
-
-	{	XFER_MW_DMA_2,	0x2480fa73	},
-	{	XFER_MW_DMA_1,	0x2480fa77	},
-	{	XFER_MW_DMA_0,	0x2480fb3f	},
-
-	{	XFER_PIO_4,	0x0c82be73	},
-	{	XFER_PIO_3,	0x0c82be95	},
-	{	XFER_PIO_2,	0x0c82beb7	},
-	{	XFER_PIO_1,	0x0d02bf37	},
-	{	XFER_PIO_0,	0x0d02bf5f	},
-	{	0,		0x0d02bf5f	}
-};
-
-static const struct hpt_clock hpt370a_timings_50[] = {
-	{	XFER_UDMA_5,	0x12848242	},
-	{	XFER_UDMA_4,	0x12ac8242	},
-	{	XFER_UDMA_3,	0x128c8242	},
-	{	XFER_UDMA_2,	0x120c8242	},
-	{	XFER_UDMA_1,	0x12148254	},
-	{	XFER_UDMA_0,	0x121882ea	},
-
-	{	XFER_MW_DMA_2,	0x22808242	},
-	{	XFER_MW_DMA_1,	0x22808254	},
-	{	XFER_MW_DMA_0,	0x228082ea	},
-
-	{	XFER_PIO_4,	0x0a81f442	},
-	{	XFER_PIO_3,	0x0a81f443	},
-	{	XFER_PIO_2,	0x0a81f454	},
-	{	XFER_PIO_1,	0x0ac1f465	},
-	{	XFER_PIO_0,	0x0ac1f48a	},
-	{	0,		0x0ac1f48a	}
-};
-
-static const struct hpt_clock hpt372_timings_33[] = {
-	{	XFER_UDMA_6,	0x1c81dc62	},
-	{	XFER_UDMA_5,	0x1c6ddc62	},
-	{	XFER_UDMA_4,	0x1c8ddc62	},
-	{	XFER_UDMA_3,	0x1c8edc62	},	/* checkme */
-	{	XFER_UDMA_2,	0x1c91dc62	},
-	{	XFER_UDMA_1,	0x1c9adc62	},	/* checkme */
-	{	XFER_UDMA_0,	0x1c82dc62	},	/* checkme */
-
-	{	XFER_MW_DMA_2,	0x2c829262	},
-	{	XFER_MW_DMA_1,	0x2c829266	},	/* checkme */
-	{	XFER_MW_DMA_0,	0x2c82922e	},	/* checkme */
-
-	{	XFER_PIO_4,	0x0c829c62	},
-	{	XFER_PIO_3,	0x0c829c84	},
-	{	XFER_PIO_2,	0x0c829ca6	},
-	{	XFER_PIO_1,	0x0d029d26	},
-	{	XFER_PIO_0,	0x0d029d5e	},
-	{	0,		0x0d029d5e	}
-};
-
-static const struct hpt_clock hpt372_timings_50[] = {
-	{	XFER_UDMA_5,	0x12848242	},
-	{	XFER_UDMA_4,	0x12ac8242	},
-	{	XFER_UDMA_3,	0x128c8242	},
-	{	XFER_UDMA_2,	0x120c8242	},
-	{	XFER_UDMA_1,	0x12148254	},
-	{	XFER_UDMA_0,	0x121882ea	},
-
-	{	XFER_MW_DMA_2,	0x22808242	},
-	{	XFER_MW_DMA_1,	0x22808254	},
-	{	XFER_MW_DMA_0,	0x228082ea	},
-
-	{	XFER_PIO_4,	0x0a81f442	},
-	{	XFER_PIO_3,	0x0a81f443	},
-	{	XFER_PIO_2,	0x0a81f454	},
-	{	XFER_PIO_1,	0x0ac1f465	},
-	{	XFER_PIO_0,	0x0ac1f48a	},
-	{	0,		0x0a81f443	}
-};
-
-static const struct hpt_clock hpt372_timings_66[] = {
-	{	XFER_UDMA_6,	0x1c869c62	},
-	{	XFER_UDMA_5,	0x1cae9c62	},
-	{	XFER_UDMA_4,	0x1c8a9c62	},
-	{	XFER_UDMA_3,	0x1c8e9c62	},
-	{	XFER_UDMA_2,	0x1c929c62	},
-	{	XFER_UDMA_1,	0x1c9a9c62	},
-	{	XFER_UDMA_0,	0x1c829c62	},
-
-	{	XFER_MW_DMA_2,	0x2c829c62	},
-	{	XFER_MW_DMA_1,	0x2c829c66	},
-	{	XFER_MW_DMA_0,	0x2c829d2e	},
-
-	{	XFER_PIO_4,	0x0c829c62	},
-	{	XFER_PIO_3,	0x0c829c84	},
-	{	XFER_PIO_2,	0x0c829ca6	},
-	{	XFER_PIO_1,	0x0d029d26	},
-	{	XFER_PIO_0,	0x0d029d5e	},
-	{	0,		0x0d029d26	}
-};
-
-static const struct hpt_clock hpt374_timings_33[] = {
-	{	XFER_UDMA_6,	0x12808242	},
-	{	XFER_UDMA_5,	0x12848242	},
-	{	XFER_UDMA_4,	0x12ac8242	},
-	{	XFER_UDMA_3,	0x128c8242	},
-	{	XFER_UDMA_2,	0x120c8242	},
-	{	XFER_UDMA_1,	0x12148254	},
-	{	XFER_UDMA_0,	0x121882ea	},
-
-	{	XFER_MW_DMA_2,	0x22808242	},
-	{	XFER_MW_DMA_1,	0x22808254	},
-	{	XFER_MW_DMA_0,	0x228082ea	},
-
-	{	XFER_PIO_4,	0x0a81f442	},
-	{	XFER_PIO_3,	0x0a81f443	},
-	{	XFER_PIO_2,	0x0a81f454	},
-	{	XFER_PIO_1,	0x0ac1f465	},
-	{	XFER_PIO_0,	0x0ac1f48a	},
-	{	0,		0x06814e93	}
-};
 
 static const struct hpt_chip hpt370 = {
 	"HPT370",
 	48,
 	{
-		hpt370_timings_33,
+		hpt37x_timings_33,
 		NULL,
 		NULL,
-		hpt370_timings_66
+		NULL
 	}
 };
 
@@ -263,10 +138,10 @@
 	"HPT370A",
 	48,
 	{
-		hpt370a_timings_33,
+		hpt37x_timings_33,
 		NULL,
-		hpt370a_timings_50,
-		hpt370a_timings_66
+		hpt37x_timings_50,
+		NULL
 	}
 };
 
@@ -274,10 +149,10 @@
 	"HPT372",
 	55,
 	{
-		hpt372_timings_33,
+		hpt37x_timings_33,
 		NULL,
-		hpt372_timings_50,
-		hpt372_timings_66
+		hpt37x_timings_50,
+		hpt37x_timings_66
 	}
 };
 
@@ -285,10 +160,10 @@
 	"HPT302",
 	66,
 	{
-		hpt372_timings_33,
+		hpt37x_timings_33,
 		NULL,
-		hpt372_timings_50,
-		hpt372_timings_66
+		hpt37x_timings_50,
+		hpt37x_timings_66
 	}
 };
 
@@ -296,10 +171,10 @@
 	"HPT371",
 	66,
 	{
-		hpt372_timings_33,
+		hpt37x_timings_33,
 		NULL,
-		hpt372_timings_50,
-		hpt372_timings_66
+		hpt37x_timings_50,
+		hpt37x_timings_66
 	}
 };
 
@@ -307,10 +182,10 @@
 	"HPT372A",
 	66,
 	{
-		hpt372_timings_33,
+		hpt37x_timings_33,
 		NULL,
-		hpt372_timings_50,
-		hpt372_timings_66
+		hpt37x_timings_50,
+		hpt37x_timings_66
 	}
 };
 
@@ -318,7 +193,7 @@
 	"HPT374",
 	48,
 	{
-		hpt374_timings_33,
+		hpt37x_timings_33,
 		NULL,
 		NULL,
 		NULL
@@ -397,13 +272,12 @@
 
 /**
  *	hpt370_filter	-	mode selection filter
- *	@ap: ATA interface
  *	@adev: ATA device
  *
  *	Block UDMA on devices that cause trouble with this controller.
  */
 
-static unsigned long hpt370_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask)
+static unsigned long hpt370_filter(struct ata_device *adev, unsigned long mask)
 {
 	if (adev->class == ATA_DEV_ATA) {
 		if (hpt_dma_blacklisted(adev, "UDMA", bad_ata33))
@@ -411,24 +285,23 @@
 		if (hpt_dma_blacklisted(adev, "UDMA100", bad_ata100_5))
 			mask &= ~(0x1F << ATA_SHIFT_UDMA);
 	}
-	return ata_pci_default_filter(ap, adev, mask);
+	return ata_pci_default_filter(adev, mask);
 }
 
 /**
  *	hpt370a_filter	-	mode selection filter
- *	@ap: ATA interface
  *	@adev: ATA device
  *
  *	Block UDMA on devices that cause trouble with this controller.
  */
 
-static unsigned long hpt370a_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask)
+static unsigned long hpt370a_filter(struct ata_device *adev, unsigned long mask)
 {
 	if (adev->class != ATA_DEV_ATA) {
 		if (hpt_dma_blacklisted(adev, "UDMA100", bad_ata100_5))
 			mask &= ~ (0x1F << ATA_SHIFT_UDMA);
 	}
-	return ata_pci_default_filter(ap, adev, mask);
+	return ata_pci_default_filter(adev, mask);
 }
 
 /**
@@ -462,8 +335,7 @@
 		ap->cbl = ATA_CBL_PATA80;
 
 	/* Reset the state machine */
-	pci_write_config_byte(pdev, 0x50, 0x37);
-	pci_write_config_byte(pdev, 0x54, 0x37);
+	pci_write_config_byte(pdev, 0x50 + 4 * ap->port_no, 0x37);
 	udelay(100);
 
 	return ata_std_prereset(ap);
@@ -513,8 +385,7 @@
 		ap->cbl = ATA_CBL_PATA80;
 
 	/* Reset the state machine */
-	pci_write_config_byte(pdev, 0x50, 0x37);
-	pci_write_config_byte(pdev, 0x54, 0x37);
+	pci_write_config_byte(pdev, 0x50 + 4 * ap->port_no, 0x37);
 	udelay(100);
 
 	return ata_std_prereset(ap);
@@ -1032,6 +903,24 @@
 		.udma_mask = 0x3f,
 		.port_ops = &hpt370a_port_ops
 	};
+	/* HPT370 - UDMA100 */
+	static struct ata_port_info info_hpt370_33 = {
+		.sht = &hpt37x_sht,
+		.flags = ATA_FLAG_SLAVE_POSS|ATA_FLAG_SRST,
+		.pio_mask = 0x1f,
+		.mwdma_mask = 0x07,
+		.udma_mask = 0x0f,
+		.port_ops = &hpt370_port_ops
+	};
+	/* HPT370A - UDMA100 */
+	static struct ata_port_info info_hpt370a_33 = {
+		.sht = &hpt37x_sht,
+		.flags = ATA_FLAG_SLAVE_POSS|ATA_FLAG_SRST,
+		.pio_mask = 0x1f,
+		.mwdma_mask = 0x07,
+		.udma_mask = 0x0f,
+		.port_ops = &hpt370a_port_ops
+	};
 	/* HPT371, 372 and friends - UDMA133 */
 	static struct ata_port_info info_hpt372 = {
 		.sht = &hpt37x_sht,
@@ -1067,7 +956,11 @@
 
 	u8 irqmask;
 	u32 class_rev;
+	u8 mcr1;
 	u32 freq;
+	int prefer_dpll = 1;
+	
+	unsigned long iobase = pci_resource_start(dev, 4);
 
 	const struct hpt_chip *chip_table;
 	int clock_slot;
@@ -1088,10 +981,12 @@
 			case 3:
 				port = &info_hpt370;
 				chip_table = &hpt370;
+				prefer_dpll = 0;
 				break;
 			case 4:
 				port = &info_hpt370a;
 				chip_table = &hpt370a;
+				prefer_dpll = 0;
 				break;
 			case 5:
 				port = &info_hpt372;
@@ -1119,8 +1014,16 @@
 				chip_table = &hpt302;
 				break;
 			case PCI_DEVICE_ID_TTI_HPT371:
+				if (class_rev > 1)
+					return -ENODEV;
 				port = &info_hpt372;
 				chip_table = &hpt371;
+				/* Single channel device, master is not present
+				   but the BIOS (or us for non x86) must mark it
+				   absent */
+				pci_read_config_byte(dev, 0x50, &mcr1);
+				mcr1 &= ~0x04;
+				pci_write_config_byte(dev, 0x50, mcr1);
 				break;
 			case PCI_DEVICE_ID_TTI_HPT374:
 				chip_table = &hpt374;
@@ -1150,8 +1053,18 @@
 	 */
 
 	pci_write_config_byte(dev, 0x5b, 0x23);
+	
+	/*
+	 * HighPoint does this for HPT372A.
+	 * NOTE: This register is only writeable via I/O space.
+	 */
+	if (chip_table == &hpt372a)
+		outb(0x0e, iobase + 0x9c);
 
-	pci_read_config_dword(dev, 0x70, &freq);
+	/* Some devices do not let this value be accessed via PCI space
+	   according to the old driver */
+
+	freq = inl(iobase + 0x90);
 	if ((freq >> 12) != 0xABCDE) {
 		int i;
 		u8 sr;
@@ -1162,7 +1075,7 @@
 		/* This is the process the HPT371 BIOS is reported to use */
 		for(i = 0; i < 128; i++) {
 			pci_read_config_byte(dev, 0x78, &sr);
-			total += sr;
+			total += sr & 0x1FF;
 			udelay(15);
 		}
 		freq = total / 128;
@@ -1173,15 +1086,27 @@
 	 *	Turn the frequency check into a band and then find a timing
 	 *	table to match it.
 	 */
-
+	 
 	clock_slot = hpt37x_clock_slot(freq, chip_table->base);
-	if (chip_table->clocks[clock_slot] == NULL) {
+	if (chip_table->clocks[clock_slot] == NULL || prefer_dpll) {
 		/*
 		 *	We need to try PLL mode instead
+		 *
+		 *	For non UDMA133 capable devices we should
+		 *	use a 50MHz DPLL by choice
 		 */
-		unsigned int f_low = (MHz[clock_slot] * chip_table->base) / 192;
-		unsigned int f_high = f_low + 2;
+		unsigned int f_low, f_high;
 		int adjust;
+		
+		clock_slot = 2;
+		if (port->udma_mask & 0xE0)
+			clock_slot = 3;
+		
+		f_low = (MHz[clock_slot] * chip_table->base) / 192;
+		f_high = f_low + 2;
+
+		/* Select the DPLL clock. */
+		pci_write_config_byte(dev, 0x5b, 0x21);
 
 		for(adjust = 0; adjust < 8; adjust++) {
 			if (hpt37x_calibrate_dpll(dev))
@@ -1197,25 +1122,27 @@
 			printk(KERN_WARNING "hpt37x: DPLL did not stabilize.\n");
 			return -ENODEV;
 		}
-		/* Check if this works for all cases */
-		port->private_data = (void *)hpt370_timings_66;
+		if (clock_slot == 3)
+			port->private_data = (void *)hpt37x_timings_66;
+		else
+			port->private_data = (void *)hpt37x_timings_50;
 
 		printk(KERN_INFO "hpt37x: Bus clock %dMHz, using DPLL.\n", MHz[clock_slot]);
 	} else {
 		port->private_data = (void *)chip_table->clocks[clock_slot];
 		/*
-		 *	Perform a final fixup. The 371 and 372 clock determines
-		 *	if UDMA133 is available.
-		 */
+		 *	Perform a final fixup. Note that we will have used the
+		 *	DPLL on the HPT372 which means we don't have to worry
+		 *	about lack of UDMA133 support on lower clocks
+ 		 */
 
-		if (clock_slot == 2 && chip_table == &hpt372) {	/* 50Mhz */
-			printk(KERN_WARNING "pata_hpt37x: No UDMA133 support available with 50MHz bus clock.\n");
-			if (port == &info_hpt372)
-				port = &info_hpt372_50;
-			else BUG();
-		}
+		if (clock_slot < 2 && port == &info_hpt370)
+			port = &info_hpt370_33;
+		if (clock_slot < 2 && port == &info_hpt370a)
+			port = &info_hpt370a_33;
 		printk(KERN_INFO "hpt37x: %s: Bus clock %dMHz.\n", chip_table->name, MHz[clock_slot]);
 	}
+
 	port_info[0] = port_info[1] = port;
 	/* Now kick off ATA set up */
 	return ata_pci_init_one(dev, port_info, 2);
diff --git a/drivers/ata/pata_hpt3x2n.c b/drivers/ata/pata_hpt3x2n.c
index 65f2e18..6a34521 100644
--- a/drivers/ata/pata_hpt3x2n.c
+++ b/drivers/ata/pata_hpt3x2n.c
@@ -8,10 +8,10 @@
  * Copyright (C) 1999-2003		Andre Hedrick <andre@linux-ide.org>
  * Portions Copyright (C) 2001	        Sun Microsystems, Inc.
  * Portions Copyright (C) 2003		Red Hat Inc
+ * Portions Copyright (C) 2005-2006	MontaVista Software, Inc.
  *
  *
  * TODO
- *	371N
  *	Work out best PLL policy
  */
 
@@ -25,7 +25,7 @@
 #include <linux/libata.h>
 
 #define DRV_NAME	"pata_hpt3x2n"
-#define DRV_VERSION	"0.3.2"
+#define DRV_VERSION	"0.3.3"
 
 enum {
 	HPT_PCI_FAST	=	(1 << 31),
@@ -115,14 +115,13 @@
 }
 
 /**
- *	hpt3x2n_pre_reset	-	reset the hpt3x2n bus
- *	@ap: ATA port to reset
+ *	hpt3x2n_cable_detect	-	Detect the cable type
+ *	@ap: ATA port to detect on
  *
- *	Perform the initial reset handling for the 3x2n series controllers.
- *	Reset the hardware and state machine, obtain the cable type.
+ *	Return the cable type attached to this port
  */
 
-static int hpt3xn_pre_reset(struct ata_port *ap)
+static int hpt3x2n_cable_detect(struct ata_port *ap)
 {
 	u8 scr2, ata66;
 	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
@@ -135,15 +134,26 @@
 	pci_write_config_byte(pdev, 0x5B, scr2);
 
 	if (ata66 & (1 << ap->port_no))
-		ap->cbl = ATA_CBL_PATA40;
+		return ATA_CBL_PATA40;
 	else
-		ap->cbl = ATA_CBL_PATA80;
+		return ATA_CBL_PATA80;
+}
 
+/**
+ *	hpt3x2n_pre_reset	-	reset the hpt3x2n bus
+ *	@ap: ATA port to reset
+ *	@deadline: deadline jiffies for the operation
+ *
+ *	Perform the initial reset handling for the 3x2n series controllers.
+ *	Reset the hardware and state machine,
+ */
+
+static int hpt3xn_pre_reset(struct ata_port *ap)
+{
+	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
 	/* Reset the state machine */
-	pci_write_config_byte(pdev, 0x50, 0x37);
-	pci_write_config_byte(pdev, 0x54, 0x37);
+	pci_write_config_byte(pdev, 0x50 + 4 * ap->port_no, 0x37);
 	udelay(100);
-
 	return ata_std_prereset(ap);
 }
 
@@ -364,6 +374,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= hpt3x2n_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= hpt3x2n_cable_detect,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
@@ -422,8 +433,9 @@
 {
 	unsigned long freq;
 	u32 fcnt;
+	unsigned long iobase = pci_resource_start(pdev, 4);
 
-	pci_read_config_dword(pdev, 0x70/*CHECKME*/, &fcnt);
+	fcnt = inl(iobase + 0x90);	/* Not PCI readable for some chips */
 	if ((fcnt >> 12) != 0xABCDE) {
 		printk(KERN_WARNING "hpt3xn: BIOS clock data not set.\n");
 		return 33;	/* Not BIOS set */
@@ -492,6 +504,7 @@
 	unsigned int pci_mhz;
 	unsigned int f_low, f_high;
 	int adjust;
+	unsigned long iobase = pci_resource_start(dev, 4);
 
 	pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
 	class_rev &= 0xFF;
@@ -501,6 +514,11 @@
 			if (class_rev < 6)
 				return -ENODEV;
 			break;
+		case PCI_DEVICE_ID_TTI_HPT371:
+			if (class_rev < 2)
+				return -ENODEV;
+			/* 371N if rev > 1 */
+			break;
 		case PCI_DEVICE_ID_TTI_HPT372:
 			/* 372N if rev >= 1*/
 			if (class_rev == 0)
@@ -528,6 +546,19 @@
 	irqmask &= ~0x10;
 	pci_write_config_byte(dev, 0x5a, irqmask);
 
+	/*
+	 * HPT371 chips physically have only one channel, the secondary one,
+	 * but the primary channel registers do exist!  Go figure...
+	 * So,  we manually disable the non-existing channel here
+	 * (if the BIOS hasn't done this already).
+	 */
+	if (dev->device == PCI_DEVICE_ID_TTI_HPT371) {
+		u8 mcr1;
+		pci_read_config_byte(dev, 0x50, &mcr1);
+		mcr1 &= ~0x04;
+		pci_write_config_byte(dev, 0x50, mcr1);
+	}
+
 	/* Tune the PLL. HPT recommend using 75 for SATA, 66 for UDMA133 or
 	   50 for UDMA100. Right now we always use 66 */
 
@@ -546,14 +577,24 @@
 			break;
 		pci_write_config_dword(dev, 0x5C, (f_high << 16) | f_low);
 	}
-	if (adjust == 8)
-		printk(KERN_WARNING "hpt3xn: DPLL did not stabilize.\n");
+	if (adjust == 8) {
+		printk(KERN_WARNING "hpt3x2n: DPLL did not stabilize.\n");
+		return -ENODEV;
+	}
 
 	/* Set our private data up. We only need a few flags so we use
 	   it directly */
 	port->private_data = NULL;
-	if (pci_mhz > 60)
+	if (pci_mhz > 60) {
 		port->private_data = (void *)PCI66;
+		/*
+		 * On  HPT371N, if ATA clock is 66 MHz we must set bit 2 in
+		 * the MISC. register to stretch the UltraDMA Tss timing.
+		 * NOTE: This register is only writeable via I/O space.
+		 */
+		if (dev->device == PCI_DEVICE_ID_TTI_HPT371)
+			outb(inb(iobase + 0x9c) | 0x04, iobase + 0x9c);
+	}
 
 	/* Now kick off ATA set up */
 	port_info[0] = port_info[1] = port;
@@ -562,6 +603,7 @@
 
 static const struct pci_device_id hpt3x2n[] = {
 	{ PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT366), },
+	{ PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT371), },
 	{ PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT372), },
 	{ PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT302), },
 	{ PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT372N), },
diff --git a/drivers/ata/pata_hpt3x3.c b/drivers/ata/pata_hpt3x3.c
index 813485c..ac28ec8 100644
--- a/drivers/ata/pata_hpt3x3.c
+++ b/drivers/ata/pata_hpt3x3.c
@@ -25,25 +25,6 @@
 #define DRV_NAME	"pata_hpt3x3"
 #define DRV_VERSION	"0.4.2"
 
-static int hpt3x3_probe_init(struct ata_port *ap)
-{
-	ap->cbl = ATA_CBL_PATA40;
-	return ata_std_prereset(ap);
-}
-
-/**
- *	hpt3x3_probe_reset	-	reset the hpt3x3 bus
- *	@ap: ATA port to reset
- *
- *	Perform the housekeeping when doing an ATA bus reeset. We just
- *	need to force the cable type.
- */
-
-static void hpt3x3_error_handler(struct ata_port *ap)
-{
-	return ata_bmdma_drive_eh(ap, hpt3x3_probe_init, ata_std_softreset, NULL, ata_std_postreset);
-}
-
 /**
  *	hpt3x3_set_piomode		-	PIO setup
  *	@ap: ATA interface
@@ -139,8 +120,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= hpt3x3_error_handler,
+	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
diff --git a/drivers/ata/pata_isapnp.c b/drivers/ata/pata_isapnp.c
index 1a61cc8..d042efd 100644
--- a/drivers/ata/pata_isapnp.c
+++ b/drivers/ata/pata_isapnp.c
@@ -49,13 +49,13 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.qc_prep 	= ata_qc_prep,
 	.qc_issue	= ata_qc_issue_prot,
 
 	.data_xfer	= ata_data_xfer,
 
-	.irq_handler	= ata_interrupt,
 	.irq_clear	= ata_bmdma_irq_clear,
 	.irq_on		= ata_irq_on,
 	.irq_ack	= ata_irq_ack,
@@ -74,8 +74,10 @@
 
 static int isapnp_init_one(struct pnp_dev *idev, const struct pnp_device_id *dev_id)
 {
-	struct ata_probe_ent ae;
+	struct ata_host *host;
+	struct ata_port *ap;
 	void __iomem *cmd_addr, *ctl_addr;
+	int rc;
 
 	if (pnp_port_valid(idev, 0) == 0)
 		return -ENODEV;
@@ -84,34 +86,36 @@
 	if (pnp_irq_valid(idev, 0) == 0)
 		return -ENODEV;
 
+	/* allocate host */
+	host = ata_host_alloc(&idev->dev, 1);
+	if (!host)
+		return -ENOMEM;
+
+	/* acquire resources and fill host */
 	cmd_addr = devm_ioport_map(&idev->dev, pnp_port_start(idev, 0), 8);
 	if (!cmd_addr)
 		return -ENOMEM;
 
-	memset(&ae, 0, sizeof(struct ata_probe_ent));
-	INIT_LIST_HEAD(&ae.node);
-	ae.dev = &idev->dev;
-	ae.port_ops = &isapnp_port_ops;
-	ae.sht = &isapnp_sht;
-	ae.n_ports = 1;
-	ae.pio_mask = 1;		/* ISA so PIO 0 cycles */
-	ae.irq = pnp_irq(idev, 0);
-	ae.irq_flags = 0;
-	ae.port_flags = ATA_FLAG_SLAVE_POSS;
-	ae.port[0].cmd_addr = cmd_addr;
+	ap = host->ports[0];
+
+	ap->ops = &isapnp_port_ops;
+	ap->pio_mask = 1;
+	ap->flags |= ATA_FLAG_SLAVE_POSS;
+
+	ap->ioaddr.cmd_addr = cmd_addr;
 
 	if (pnp_port_valid(idev, 1) == 0) {
 		ctl_addr = devm_ioport_map(&idev->dev,
 					   pnp_port_start(idev, 1), 1);
-		ae.port[0].altstatus_addr = ctl_addr;
-		ae.port[0].ctl_addr = ctl_addr;
-		ae.port_flags |= ATA_FLAG_SRST;
+		ap->ioaddr.altstatus_addr = ctl_addr;
+		ap->ioaddr.ctl_addr = ctl_addr;
 	}
-	ata_std_ports(&ae.port[0]);
 
-	if (ata_device_add(&ae) == 0)
-		return -ENODEV;
-	return 0;
+	ata_std_ports(&ap->ioaddr);
+
+	/* activate */
+	return ata_host_activate(host, pnp_irq(idev, 0), ata_interrupt, 0,
+				 &isapnp_sht);
 }
 
 /**
diff --git a/drivers/ata/pata_it8213.c b/drivers/ata/pata_it8213.c
index ea73470..011306e 100644
--- a/drivers/ata/pata_it8213.c
+++ b/drivers/ata/pata_it8213.c
@@ -25,8 +25,8 @@
  *	it8213_pre_reset	-	check for 40/80 pin
  *	@ap: Port
  *
- *	Perform cable detection for the 8213 ATA interface. This is
- *	different to the PIIX arrangement
+ *	Filter out ports by the enable bits before doing the normal reset
+ *	and probe.
  */
 
 static int it8213_pre_reset(struct ata_port *ap)
@@ -34,23 +34,14 @@
 	static const struct pci_bits it8213_enable_bits[] = {
 		{ 0x41U, 1U, 0x80UL, 0x80UL },	/* port 0 */
 	};
-
 	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
-	u8 tmp;
-
 	if (!pci_test_config_bits(pdev, &it8213_enable_bits[ap->port_no]))
 		return -ENOENT;
-
-	pci_read_config_byte(pdev, 0x42, &tmp);
-	if (tmp & 2)	/* The initial docs are incorrect */
-		ap->cbl = ATA_CBL_PATA40;
-	else
-		ap->cbl = ATA_CBL_PATA80;
 	return ata_std_prereset(ap);
 }
 
 /**
- *	it8213_probe_reset - Probe specified port on PATA host controller
+ *	it8213_error_handler - Probe specified port on PATA host controller
  *	@ap: Port to probe
  *
  *	LOCKING:
@@ -63,9 +54,27 @@
 }
 
 /**
+ *	it8213_cable_detect	-	check for 40/80 pin
+ *	@ap: Port
+ *
+ *	Perform cable detection for the 8213 ATA interface. This is
+ *	different to the PIIX arrangement
+ */
+
+static int it8213_cable_detect(struct ata_port *ap)
+{
+	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+	u8 tmp;
+	pci_read_config_byte(pdev, 0x42, &tmp);
+	if (tmp & 2)	/* The initial docs are incorrect */
+		return ATA_CBL_PATA40;
+	return ATA_CBL_PATA80;
+}
+
+/**
  *	it8213_set_piomode - Initialize host controller PATA PIO timings
  *	@ap: Port whose timings we are configuring
- *	@adev: um
+ *	@adev: Device whose timings we are configuring
  *
  *	Set PIO mode for device, in host controller PCI config space.
  *
@@ -268,6 +277,7 @@
 	.thaw			= ata_bmdma_thaw,
 	.error_handler		= it8213_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
+	.cable_detect		= it8213_cable_detect,
 
 	.bmdma_setup		= ata_bmdma_setup,
 	.bmdma_start		= ata_bmdma_start,
diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c
index 35ecb2b..f1f8cec 100644
--- a/drivers/ata/pata_it821x.c
+++ b/drivers/ata/pata_it821x.c
@@ -80,7 +80,7 @@
 
 
 #define DRV_NAME "pata_it821x"
-#define DRV_VERSION "0.3.4"
+#define DRV_VERSION "0.3.6"
 
 struct it821x_dev
 {
@@ -113,31 +113,6 @@
 static int it8212_noraid;
 
 /**
- *	it821x_pre_reset	-	probe
- *	@ap: ATA port
- *
- *	Set the cable type
- */
-
-static int it821x_pre_reset(struct ata_port *ap)
-{
-	ap->cbl = ATA_CBL_PATA80;
-	return ata_std_prereset(ap);
-}
-
-/**
- *	it821x_error_handler	-	probe/reset
- *	@ap: ATA port
- *
- *	Set the cable type and trigger a probe
- */
-
-static void it821x_error_handler(struct ata_port *ap)
-{
-	return ata_bmdma_drive_eh(ap, it821x_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
-/**
  *	it821x_program	-	program the PIO/MWDMA registers
  *	@ap: ATA port
  *	@adev: Device to program
@@ -520,7 +495,6 @@
 
 /**
  *	it821x_dev_config	-	Called each device identify
- *	@ap: ATA port
  *	@adev: Device that has just been identified
  *
  *	Perform the initial setup needed for each device that is chip
@@ -531,7 +505,7 @@
  *	basically we need to filter commands for this chip.
  */
 
-static void it821x_dev_config(struct ata_port *ap, struct ata_device *adev)
+static void it821x_dev_config(struct ata_device *adev)
 {
 	unsigned char model_num[ATA_ID_PROD_LEN + 1];
 
@@ -667,8 +641,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= it821x_error_handler,
+	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_unknown,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
@@ -703,8 +678,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= it821x_error_handler,
+	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_unknown,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= it821x_passthru_bmdma_start,
diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c
index c6f0e19..420c343 100644
--- a/drivers/ata/pata_ixp4xx_cf.c
+++ b/drivers/ata/pata_ixp4xx_cf.c
@@ -129,8 +129,8 @@
 	.qc_issue	= ata_qc_issue_prot,
 	.eng_timeout	= ata_eng_timeout,
 	.data_xfer	= ixp4xx_mmio_data_xfer,
+	.cable_detect	= ata_cable_40wire,
 
-	.irq_handler	= ata_interrupt,
 	.irq_clear	= ixp4xx_irq_clear,
 	.irq_on		= ata_irq_on,
 	.irq_ack	= ata_irq_ack,
@@ -173,12 +173,12 @@
 
 static __devinit int ixp4xx_pata_probe(struct platform_device *pdev)
 {
-	int ret;
 	unsigned int irq;
 	struct resource *cs0, *cs1;
-	struct ata_probe_ent ae;
-
+	struct ata_host *host;
+	struct ata_port *ap;
 	struct ixp4xx_pata_data *data = pdev->dev.platform_data;
+	int rc;
 
 	cs0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	cs1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
@@ -186,6 +186,12 @@
 	if (!cs0 || !cs1)
 		return -EINVAL;
 
+	/* allocate host */
+	host = ata_host_alloc(&pdev->dev, 1);
+	if (!host)
+		return -ENOMEM;
+
+	/* acquire resources and fill host */
 	pdev->dev.coherent_dma_mask = DMA_32BIT_MASK;
 
 	data->cs0 = devm_ioremap(&pdev->dev, cs0->start, 0x1000);
@@ -199,32 +205,22 @@
 	*data->cs0_cfg = data->cs0_bits;
 	*data->cs1_cfg = data->cs1_bits;
 
-	memset(&ae, 0, sizeof(struct ata_probe_ent));
-	INIT_LIST_HEAD(&ae.node);
+	ap = host->ports[0];
 
-	ae.dev		= &pdev->dev;
-	ae.port_ops	= &ixp4xx_port_ops;
-	ae.sht		= &ixp4xx_sht;
-	ae.n_ports	= 1;
-	ae.pio_mask	= 0x1f; /* PIO4 */
-	ae.irq		= irq;
-	ae.irq_flags	= 0;
-	ae.port_flags	= ATA_FLAG_MMIO | ATA_FLAG_NO_LEGACY
-			| ATA_FLAG_NO_ATAPI | ATA_FLAG_SRST;
+	ap->ops	= &ixp4xx_port_ops;
+	ap->pio_mask = 0x1f; /* PIO4 */
+	ap->flags |= ATA_FLAG_MMIO | ATA_FLAG_NO_LEGACY | ATA_FLAG_NO_ATAPI;
 
 	/* run in polling mode if no irq has been assigned */
 	if (!irq)
-		ae.port_flags |= ATA_FLAG_PIO_POLLING;
+		ap->flags |= ATA_FLAG_PIO_POLLING;
 
-	ixp4xx_setup_port(&ae.port[0], data);
+	ixp4xx_setup_port(&ap->ioaddr, data);
 
 	dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n");
 
-	ret = ata_device_add(&ae);
-	if (ret == 0)
-		return -ENODEV;
-
-	return 0;
+	/* activate host */
+	return ata_host_activate(host, irq, ata_interrupt, 0, &ixp4xx_sht);
 }
 
 static __devexit int ixp4xx_pata_remove(struct platform_device *dev)
diff --git a/drivers/ata/pata_legacy.c b/drivers/ata/pata_legacy.c
index 86fbcd6..7070992 100644
--- a/drivers/ata/pata_legacy.c
+++ b/drivers/ata/pata_legacy.c
@@ -162,6 +162,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.qc_prep 	= ata_qc_prep,
 	.qc_issue	= ata_qc_issue_prot,
@@ -185,6 +186,7 @@
 	.check_status 	= ata_check_status,
 	.exec_command	= ata_exec_command,
 	.dev_select 	= ata_std_dev_select,
+	.cable_detect	= ata_cable_40wire,
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
@@ -305,6 +307,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.qc_prep 	= ata_qc_prep,
 	.qc_issue	= ata_qc_issue_prot,
@@ -360,6 +363,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.qc_prep 	= ata_qc_prep,
 	.qc_issue	= ata_qc_issue_prot,
@@ -426,6 +430,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.qc_prep 	= ata_qc_prep,
 	.qc_issue	= ata_qc_issue_prot,
@@ -547,6 +552,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.qc_prep 	= ata_qc_prep,
 	.qc_issue	= ata_qc_issue_prot,
@@ -680,6 +686,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.qc_prep 	= ata_qc_prep,
 	.qc_issue	= opti82c46x_qc_issue_prot,
@@ -709,7 +716,8 @@
 static __init int legacy_init_one(int port, unsigned long io, unsigned long ctrl, int irq)
 {
 	struct legacy_data *ld = &legacy_data[nr_legacy_host];
-	struct ata_probe_ent ae;
+	struct ata_host *host;
+	struct ata_port *ap;
 	struct platform_device *pdev;
 	struct ata_port_operations *ops = &legacy_port_ops;
 	void __iomem *io_addr, *ctrl_addr;
@@ -791,24 +799,23 @@
 	if (ops == &legacy_port_ops && (autospeed & mask))
 		ops = &simple_port_ops;
 
-	memset(&ae, 0, sizeof(struct ata_probe_ent));
-	INIT_LIST_HEAD(&ae.node);
-	ae.dev = &pdev->dev;
-	ae.port_ops = ops;
-	ae.sht = &legacy_sht;
-	ae.n_ports = 1;
-	ae.pio_mask = pio_modes;
-	ae.irq = irq;
-	ae.irq_flags = 0;
-	ae.port_flags = ATA_FLAG_SLAVE_POSS|ATA_FLAG_SRST|iordy;
-	ae.port[0].cmd_addr = io_addr;
-	ae.port[0].altstatus_addr = ctrl_addr;
-	ae.port[0].ctl_addr = ctrl_addr;
-	ata_std_ports(&ae.port[0]);
-	ae.private_data = ld;
+	ret = -ENOMEM;
+	host = ata_host_alloc(&pdev->dev, 1);
+	if (!host)
+		goto fail;
+	ap = host->ports[0];
 
-	ret = -ENODEV;
-	if (!ata_device_add(&ae))
+	ap->ops = ops;
+	ap->pio_mask = pio_modes;
+	ap->flags |= ATA_FLAG_SLAVE_POSS | iordy;
+	ap->ioaddr.cmd_addr = io_addr;
+	ap->ioaddr.altstatus_addr = ctrl_addr;
+	ap->ioaddr.ctl_addr = ctrl_addr;
+	ata_std_ports(&ap->ioaddr);
+	ap->private_data = ld;
+
+	ret = ata_host_activate(host, irq, ata_interrupt, 0, &legacy_sht);
+	if (ret)
 		goto fail;
 
 	legacy_host[nr_legacy_host++] = dev_get_drvdata(&pdev->dev);
diff --git a/drivers/ata/pata_marvell.c b/drivers/ata/pata_marvell.c
index 6dd7c4e..d9b94a1 100644
--- a/drivers/ata/pata_marvell.c
+++ b/drivers/ata/pata_marvell.c
@@ -20,7 +20,7 @@
 #include <linux/ata.h>
 
 #define DRV_NAME	"pata_marvell"
-#define DRV_VERSION	"0.1.1"
+#define DRV_VERSION	"0.1.4"
 
 /**
  *	marvell_pre_reset	-	check for 40/80 pin
@@ -52,22 +52,23 @@
 	if ((pdev->device == 0x6145) && (ap->port_no == 0) &&
 	    (!(devices & 0x10)))	/* PATA enable ? */
 		return -ENOENT;
+	return ata_std_prereset(ap);
+}
 
+static int marvell_cable_detect(struct ata_port *ap)
+{
 	/* Cable type */
 	switch(ap->port_no)
 	{
 	case 0:
 		if (ioread8(ap->ioaddr.bmdma_addr + 1) & 1)
-			ap->cbl = ATA_CBL_PATA40;
-		else
-			ap->cbl = ATA_CBL_PATA80;
-		break;
-
+			return ATA_CBL_PATA40;
+		return ATA_CBL_PATA80;
 	case 1: /* Legacy SATA port */
-		ap->cbl = ATA_CBL_SATA;
-		break;
+		return ATA_CBL_SATA;
 	}
-	return ata_std_prereset(ap);
+	BUG();
+	return 0;	/* Our BUG macro needs the right markup */
 }
 
 /**
@@ -123,6 +124,7 @@
 	.thaw			= ata_bmdma_thaw,
 	.error_handler		= marvell_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
+	.cable_detect		= marvell_cable_detect,
 
 	/* BMDMA handling is PCI ATA format, use helpers */
 	.bmdma_setup		= ata_bmdma_setup,
diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c
index 882c36e..9587a89 100644
--- a/drivers/ata/pata_mpc52xx.c
+++ b/drivers/ata/pata_mpc52xx.c
@@ -24,7 +24,7 @@
 
 
 #define DRV_NAME	"mpc52xx_ata"
-#define DRV_VERSION	"0.1.0"
+#define DRV_VERSION	"0.1.0ac2"
 
 
 /* Private structures used by the driver */
@@ -297,38 +297,37 @@
 	.freeze			= ata_bmdma_freeze,
 	.thaw			= ata_bmdma_thaw,
 	.error_handler		= mpc52xx_ata_error_handler,
+	.cable_detect		= ata_cable_40wire,
 	.qc_prep		= ata_qc_prep,
 	.qc_issue		= ata_qc_issue_prot,
 	.data_xfer		= ata_data_xfer,
-	.irq_handler		= ata_interrupt,
 	.irq_clear		= ata_bmdma_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
 	.port_start		= ata_port_start,
 };
 
-static struct ata_probe_ent mpc52xx_ata_probe_ent = {
-	.port_ops	= &mpc52xx_ata_port_ops,
-	.sht		= &mpc52xx_ata_sht,
-	.n_ports	= 1,
-	.pio_mask	= 0x1f,		/* Up to PIO4 */
-	.mwdma_mask	= 0x00,		/* No MWDMA   */
-	.udma_mask	= 0x00,		/* No UDMA    */
-	.port_flags	= ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST,
-	.irq_flags	= 0,
-};
-
 static int __devinit
 mpc52xx_ata_init_one(struct device *dev, struct mpc52xx_ata_priv *priv)
 {
-	struct ata_probe_ent *ae = &mpc52xx_ata_probe_ent;
-	struct ata_ioports *aio = &ae->port[0];
-	int rv;
+	struct ata_host *host;
+	struct ata_port *ap;
+	struct ata_ioports *aio;
+	int rc;
 
-	INIT_LIST_HEAD(&ae->node);
-	ae->dev = dev;
-	ae->irq = priv->ata_irq;
+	host = ata_host_alloc(dev, 1);
+	if (!host)
+		return -ENOMEM;
 
+	ap = host->ports[0];
+	ap->flags		|= ATA_FLAG_SLAVE_POSS;
+	ap->pio_mask		= 0x1f;	/* Up to PIO4 */
+	ap->mwdma_mask		= 0x00;	/* No MWDMA   */
+	ap->udma_mask		= 0x00;	/* No UDMA    */
+	ap->ops			= &mpc52xx_ata_port_ops;
+	host->private_data	= priv;
+
+	aio = &ap->ioaddr;
 	aio->cmd_addr		= NULL;	/* Don't have a classic reg block */
 	aio->altstatus_addr	= &priv->ata_regs->tf_control;
 	aio->ctl_addr		= &priv->ata_regs->tf_control;
@@ -343,11 +342,9 @@
 	aio->status_addr	= &priv->ata_regs->tf_command;
 	aio->command_addr	= &priv->ata_regs->tf_command;
 
-	ae->private_data = priv;
-
-	rv = ata_device_add(ae);
-
-	return rv ? 0 : -EINVAL;
+	/* activate host */
+	return ata_host_activate(host, priv->ata_irq, ata_interrupt, 0,
+				 &mpc52xx_ata_sht);
 }
 
 static struct mpc52xx_ata_priv *
diff --git a/drivers/ata/pata_mpiix.c b/drivers/ata/pata_mpiix.c
index 4abe45a..987c5fa 100644
--- a/drivers/ata/pata_mpiix.c
+++ b/drivers/ata/pata_mpiix.c
@@ -35,7 +35,7 @@
 #include <linux/libata.h>
 
 #define DRV_NAME "pata_mpiix"
-#define DRV_VERSION "0.7.5"
+#define DRV_VERSION "0.7.6"
 
 enum {
 	IDETIM = 0x6C,		/* IDE control register */
@@ -53,7 +53,6 @@
 
 	if (!pci_test_config_bits(pdev, &mpiix_enable_bits))
 		return -ENOENT;
-	ap->cbl = ATA_CBL_PATA40;
 	return ata_std_prereset(ap);
 }
 
@@ -185,12 +184,12 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= mpiix_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.qc_prep 	= ata_qc_prep,
 	.qc_issue	= mpiix_qc_issue_prot,
 	.data_xfer	= ata_data_xfer,
 
-	.irq_handler	= ata_interrupt,
 	.irq_clear	= ata_bmdma_irq_clear,
 	.irq_on		= ata_irq_on,
 	.irq_ack	= ata_irq_ack,
@@ -201,8 +200,9 @@
 static int mpiix_init_one(struct pci_dev *dev, const struct pci_device_id *id)
 {
 	/* Single threaded by the PCI probe logic */
-	static struct ata_probe_ent probe;
 	static int printed_version;
+	struct ata_host *host;
+	struct ata_port *ap;
 	void __iomem *cmd_addr, *ctl_addr;
 	u16 idetim;
 	int irq;
@@ -210,6 +210,10 @@
 	if (!printed_version++)
 		dev_printk(KERN_DEBUG, &dev->dev, "version " DRV_VERSION "\n");
 
+	host = ata_host_alloc(&dev->dev, 1);
+	if (!host)
+		return -ENOMEM;
+
 	/* MPIIX has many functions which can be turned on or off according
 	   to other devices present. Make sure IDE is enabled before we try
 	   and use it */
@@ -238,27 +242,21 @@
 	   without BARs set fools the setup.  #2 If you pci_disable_device
 	   the MPIIX your box goes castors up */
 
-	INIT_LIST_HEAD(&probe.node);
-	probe.dev = pci_dev_to_dev(dev);
-	probe.port_ops = &mpiix_port_ops;
-	probe.sht = &mpiix_sht;
-	probe.pio_mask = 0x1F;
-	probe.irq_flags = IRQF_SHARED;
-	probe.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST;
-	probe.n_ports = 1;
+	ap = host->ports[0];
+	ap->ops = &mpiix_port_ops;
+	ap->pio_mask = 0x1F;
+	ap->flags |= ATA_FLAG_SLAVE_POSS;
 
-	probe.irq = irq;
-	probe.port[0].cmd_addr = cmd_addr;
-	probe.port[0].ctl_addr = ctl_addr;
-	probe.port[0].altstatus_addr = ctl_addr;
+	ap->ioaddr.cmd_addr = cmd_addr;
+	ap->ioaddr.ctl_addr = ctl_addr;
+	ap->ioaddr.altstatus_addr = ctl_addr;
 
 	/* Let libata fill in the port details */
-	ata_std_ports(&probe.port[0]);
+	ata_std_ports(&ap->ioaddr);
 
-	/* Now add the port that is active */
-	if (ata_device_add(&probe))
-		return 0;
-	return -ENODEV;
+	/* activate host */
+	return ata_host_activate(host, irq, ata_interrupt, IRQF_SHARED,
+				 &mpiix_sht);
 }
 
 static const struct pci_device_id mpiix[] = {
diff --git a/drivers/ata/pata_netcell.c b/drivers/ata/pata_netcell.c
index 38f99b3..dbba5b7 100644
--- a/drivers/ata/pata_netcell.c
+++ b/drivers/ata/pata_netcell.c
@@ -16,33 +16,7 @@
 #include <linux/ata.h>
 
 #define DRV_NAME	"pata_netcell"
-#define DRV_VERSION	"0.1.6"
-
-/**
- *	netcell_probe_init	-	check for 40/80 pin
- *	@ap: Port
- *
- *	Cables are handled by the RAID controller. Report 80 pin.
- */
-
-static int netcell_pre_reset(struct ata_port *ap)
-{
-	ap->cbl = ATA_CBL_PATA80;
-	return ata_std_prereset(ap);
-}
-
-/**
- *	netcell_probe_reset - Probe specified port on PATA host controller
- *	@ap: Port to probe
- *
- *	LOCKING:
- *	None (inherited from caller).
- */
-
-static void netcell_error_handler(struct ata_port *ap)
-{
-	return ata_bmdma_drive_eh(ap, netcell_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
+#define DRV_VERSION	"0.1.7"
 
 /* No PIO or DMA methods needed for this device */
 
@@ -81,8 +55,9 @@
 
 	.freeze			= ata_bmdma_freeze,
 	.thaw			= ata_bmdma_thaw,
-	.error_handler		= netcell_error_handler,
+	.error_handler		= ata_bmdma_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
+	.cable_detect		= ata_cable_80wire,
 
 	/* BMDMA handling is PCI ATA format, use helpers */
 	.bmdma_setup		= ata_bmdma_setup,
diff --git a/drivers/ata/pata_ns87410.c b/drivers/ata/pata_ns87410.c
index 9944a28..078aeda 100644
--- a/drivers/ata/pata_ns87410.c
+++ b/drivers/ata/pata_ns87410.c
@@ -28,13 +28,13 @@
 #include <linux/libata.h>
 
 #define DRV_NAME "pata_ns87410"
-#define DRV_VERSION "0.4.3"
+#define DRV_VERSION "0.4.6"
 
 /**
  *	ns87410_pre_reset		-	probe begin
  *	@ap: ATA port
  *
- *	Set up cable type and use generic probe init
+ *	Check enabled ports
  */
 
 static int ns87410_pre_reset(struct ata_port *ap)
@@ -47,7 +47,6 @@
 
 	if (!pci_test_config_bits(pdev, &ns87410_enable_bits[ap->port_no]))
 		return -ENOENT;
-	ap->cbl = ATA_CBL_PATA40;
 	return ata_std_prereset(ap);
 }
 
@@ -177,6 +176,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= ns87410_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.qc_prep 	= ata_qc_prep,
 	.qc_issue	= ns87410_qc_issue_prot,
diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c
index da68cd1..dea4690 100644
--- a/drivers/ata/pata_oldpiix.c
+++ b/drivers/ata/pata_oldpiix.c
@@ -25,7 +25,7 @@
 #include <linux/ata.h>
 
 #define DRV_NAME	"pata_oldpiix"
-#define DRV_VERSION	"0.5.4"
+#define DRV_VERSION	"0.5.5"
 
 /**
  *	oldpiix_pre_reset		-	probe begin
@@ -44,7 +44,6 @@
 
 	if (!pci_test_config_bits(pdev, &oldpiix_enable_bits[ap->port_no]))
 		return -ENOENT;
-	ap->cbl = ATA_CBL_PATA40;
 	return ata_std_prereset(ap);
 }
 
@@ -65,7 +64,7 @@
 /**
  *	oldpiix_set_piomode - Initialize host controller PATA PIO timings
  *	@ap: Port whose timings we are configuring
- *	@adev: um
+ *	@adev: Device whose timings we are configuring
  *
  *	Set PIO mode for device, in host controller PCI config space.
  *
@@ -255,6 +254,7 @@
 	.thaw			= ata_bmdma_thaw,
 	.error_handler		= oldpiix_pata_error_handler,
 	.post_internal_cmd 	= ata_bmdma_post_internal_cmd,
+	.cable_detect		= ata_cable_40wire,
 
 	.bmdma_setup		= ata_bmdma_setup,
 	.bmdma_start		= ata_bmdma_start,
diff --git a/drivers/ata/pata_opti.c b/drivers/ata/pata_opti.c
index 3fd3a35..13b63e2 100644
--- a/drivers/ata/pata_opti.c
+++ b/drivers/ata/pata_opti.c
@@ -34,7 +34,7 @@
 #include <linux/libata.h>
 
 #define DRV_NAME "pata_opti"
-#define DRV_VERSION "0.2.8"
+#define DRV_VERSION "0.2.9"
 
 enum {
 	READ_REG	= 0,	/* index of Read cycle timing register */
@@ -61,8 +61,6 @@
 
 	if (!pci_test_config_bits(pdev, &opti_enable_bits[ap->port_no]))
 		return -ENOENT;
-
-	ap->cbl = ATA_CBL_PATA40;
 	return ata_std_prereset(ap);
 }
 
@@ -198,6 +196,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= opti_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
diff --git a/drivers/ata/pata_optidma.c b/drivers/ata/pata_optidma.c
index 9764907..b70e04c 100644
--- a/drivers/ata/pata_optidma.c
+++ b/drivers/ata/pata_optidma.c
@@ -33,7 +33,7 @@
 #include <linux/libata.h>
 
 #define DRV_NAME "pata_optidma"
-#define DRV_VERSION "0.2.4"
+#define DRV_VERSION "0.3.2"
 
 enum {
 	READ_REG	= 0,	/* index of Read cycle timing register */
@@ -62,7 +62,6 @@
 	if (ap->port_no && !pci_test_config_bits(pdev, &optidma_enable_bits))
 		return -ENOENT;
 
-	ap->cbl = ATA_CBL_PATA40;
 	return ata_std_prereset(ap);
 }
 
@@ -115,7 +114,7 @@
 }
 
 /**
- *	optidma_set_mode	-	set mode data
+ *	optidma_mode_setup	-	set mode data
  *	@ap: ATA interface
  *	@adev: ATA device
  *	@mode: Mode to set
@@ -128,7 +127,7 @@
  *	IRQ here we depend on the host set locking to avoid catastrophe.
  */
 
-static void optidma_set_mode(struct ata_port *ap, struct ata_device *adev, u8 mode)
+static void optidma_mode_setup(struct ata_port *ap, struct ata_device *adev, u8 mode)
 {
 	struct ata_device *pair = ata_dev_pair(adev);
 	int pio = adev->pio_mode - XFER_PIO_0;
@@ -202,7 +201,7 @@
 }
 
 /**
- *	optiplus_set_mode	-	DMA setup for Firestar Plus
+ *	optiplus_mode_setup	-	DMA setup for Firestar Plus
  *	@ap: ATA port
  *	@adev: device
  *	@mode: desired mode
@@ -213,7 +212,7 @@
  *	one
  */
 
-static void optiplus_set_mode(struct ata_port *ap, struct ata_device *adev, u8 mode)
+static void optiplus_mode_setup(struct ata_port *ap, struct ata_device *adev, u8 mode)
 {
 	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
 	u8 udcfg;
@@ -225,7 +224,7 @@
 	pci_read_config_byte(pdev, 0x44, &udcfg);
 	if (mode <= XFER_UDMA_0) {
 		udcfg &= ~(1 << unit);
-		optidma_set_mode(ap, adev, adev->dma_mode);
+		optidma_mode_setup(ap, adev, adev->dma_mode);
 	} else {
 		udcfg |=  (1 << unit);
 		if (ap->port_no) {
@@ -253,7 +252,7 @@
 
 static void optidma_set_pio_mode(struct ata_port *ap, struct ata_device *adev)
 {
-	optidma_set_mode(ap, adev, adev->pio_mode);
+	optidma_mode_setup(ap, adev, adev->pio_mode);
 }
 
 /**
@@ -268,7 +267,7 @@
 
 static void optidma_set_dma_mode(struct ata_port *ap, struct ata_device *adev)
 {
-	optidma_set_mode(ap, adev, adev->dma_mode);
+	optidma_mode_setup(ap, adev, adev->dma_mode);
 }
 
 /**
@@ -283,7 +282,7 @@
 
 static void optiplus_set_pio_mode(struct ata_port *ap, struct ata_device *adev)
 {
-	optiplus_set_mode(ap, adev, adev->pio_mode);
+	optiplus_mode_setup(ap, adev, adev->pio_mode);
 }
 
 /**
@@ -298,7 +297,7 @@
 
 static void optiplus_set_dma_mode(struct ata_port *ap, struct ata_device *adev)
 {
-	optiplus_set_mode(ap, adev, adev->dma_mode);
+	optiplus_mode_setup(ap, adev, adev->dma_mode);
 }
 
 /**
@@ -322,26 +321,29 @@
 }
 
 /**
- *	optidma_post_set_mode	-	finalize PCI setup
+ *	optidma_set_mode	-	mode setup
  *	@ap: port to set up
  *
- *	Finalise the configuration by writing the nibble of extra bits
- *	of data into the chip.
+ *	Use the standard setup to tune the chipset and then finalise the
+ *	configuration by writing the nibble of extra bits of data into
+ *	the chip.
  */
 
-static void optidma_post_set_mode(struct ata_port *ap)
+static int optidma_set_mode(struct ata_port *ap, struct ata_device **r_failed)
 {
 	u8 r;
 	int nybble = 4 * ap->port_no;
 	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+	int rc  = ata_do_set_mode(ap, r_failed);
+	if (rc == 0) {
+		pci_read_config_byte(pdev, 0x43, &r);
 
-	pci_read_config_byte(pdev, 0x43, &r);
-
-	r &= (0x0F << nybble);
-	r |= (optidma_make_bits43(&ap->device[0]) +
-	     (optidma_make_bits43(&ap->device[0]) << 2)) << nybble;
-
-	pci_write_config_byte(pdev, 0x43, r);
+		r &= (0x0F << nybble);
+		r |= (optidma_make_bits43(&ap->device[0]) +
+		     (optidma_make_bits43(&ap->device[0]) << 2)) << nybble;
+		pci_write_config_byte(pdev, 0x43, r);
+	}
+	return rc;
 }
 
 static struct scsi_host_template optidma_sht = {
@@ -381,7 +383,8 @@
 	.thaw		= ata_bmdma_thaw,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
 	.error_handler	= optidma_error_handler,
-	.post_set_mode	= optidma_post_set_mode,
+	.set_mode	= optidma_set_mode,
+	.cable_detect	= ata_cable_40wire,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
@@ -416,7 +419,8 @@
 	.thaw		= ata_bmdma_thaw,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
 	.error_handler	= optidma_error_handler,
-	.post_set_mode	= optidma_post_set_mode,
+	.set_mode	= optidma_set_mode,
+	.cable_detect	= ata_cable_40wire,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c
index 103720f..75dc847 100644
--- a/drivers/ata/pata_pcmcia.c
+++ b/drivers/ata/pata_pcmcia.c
@@ -42,7 +42,7 @@
 
 
 #define DRV_NAME "pata_pcmcia"
-#define DRV_VERSION "0.3.0"
+#define DRV_VERSION "0.3.1"
 
 /*
  *	Private data structure to glue stuff together
@@ -54,6 +54,39 @@
 	dev_node_t	node;
 };
 
+/**
+ *	pcmcia_set_mode	-	PCMCIA specific mode setup
+ *	@ap: Port
+ *	@r_failed_dev: Return pointer for failed device
+ *
+ *	Perform the tuning and setup of the devices and timings, which
+ *	for PCMCIA is the same as any other controller. We wrap it however
+ *	as we need to spot hardware with incorrect or missing master/slave
+ *	decode, which alas is embarrassingly common in the PC world
+ */
+
+static int pcmcia_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev)
+{
+	struct ata_device *master = &ap->device[0];
+	struct ata_device *slave = &ap->device[1];
+
+	if (!ata_dev_enabled(master) || !ata_dev_enabled(slave))
+		return ata_do_set_mode(ap, r_failed_dev);
+
+	if (memcmp(master->id + ATA_ID_FW_REV,  slave->id + ATA_ID_FW_REV,
+			   ATA_ID_FW_REV_LEN + ATA_ID_PROD_LEN) == 0)
+	{
+		/* Suspicious match, but could be two cards from
+		   the same vendor - check serial */
+		if (memcmp(master->id + ATA_ID_SERNO, slave->id + ATA_ID_SERNO,
+			   ATA_ID_SERNO_LEN) == 0 && master->id[ATA_ID_SERNO] >> 8) {
+			ata_dev_printk(slave, KERN_WARNING, "is a ghost device, ignoring.\n");
+			ata_dev_disable(slave);
+		}
+	}
+	return ata_do_set_mode(ap, r_failed_dev);
+}
+
 static struct scsi_host_template pcmcia_sht = {
 	.module			= THIS_MODULE,
 	.name			= DRV_NAME,
@@ -73,6 +106,7 @@
 };
 
 static struct ata_port_operations pcmcia_port_ops = {
+	.set_mode	= pcmcia_set_mode,
 	.port_disable	= ata_port_disable,
 	.tf_load	= ata_tf_load,
 	.tf_read	= ata_tf_read,
@@ -84,13 +118,13 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.qc_prep 	= ata_qc_prep,
 	.qc_issue	= ata_qc_issue_prot,
 
 	.data_xfer	= ata_data_xfer_noirq,
 
-	.irq_handler	= ata_interrupt,
 	.irq_clear	= ata_bmdma_irq_clear,
 	.irq_on		= ata_irq_on,
 	.irq_ack	= ata_irq_ack,
@@ -111,7 +145,8 @@
 
 static int pcmcia_init_one(struct pcmcia_device *pdev)
 {
-	struct ata_probe_ent ae;
+	struct ata_host *host;
+	struct ata_port *ap;
 	struct ata_pcmcia_info *info;
 	tuple_t tuple;
 	struct {
@@ -255,24 +290,24 @@
  	 *	Having done the PCMCIA plumbing the ATA side is relatively
  	 *	sane.
 	 */
+	ret = -ENOMEM;
+	host = ata_host_alloc(&pdev->dev, 1);
+	if (!host)
+		goto failed;
+	ap = host->ports[0];
 
-	memset(&ae, 0, sizeof(struct ata_probe_ent));
-	INIT_LIST_HEAD(&ae.node);
-	ae.dev = &pdev->dev;
-	ae.port_ops = &pcmcia_port_ops;
-	ae.sht = &pcmcia_sht;
-	ae.n_ports = 1;
-	ae.pio_mask = 1;		/* ISA so PIO 0 cycles */
-	ae.irq = pdev->irq.AssignedIRQ;
-	ae.irq_flags = IRQF_SHARED;
-	ae.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST;
-	ae.port[0].cmd_addr = io_addr;
-	ae.port[0].altstatus_addr = ctl_addr;
-	ae.port[0].ctl_addr = ctl_addr;
-	ata_std_ports(&ae.port[0]);
+	ap->ops = &pcmcia_port_ops;
+	ap->pio_mask = 1;		/* ISA so PIO 0 cycles */
+	ap->flags |= ATA_FLAG_SLAVE_POSS;
+	ap->ioaddr.cmd_addr = io_addr;
+	ap->ioaddr.altstatus_addr = ctl_addr;
+	ap->ioaddr.ctl_addr = ctl_addr;
+	ata_std_ports(&ap->ioaddr);
 
-	ret = -ENODEV;
-	if (ata_device_add(&ae) == 0)
+	/* activate */
+	ret = ata_host_activate(host, pdev->irq.AssignedIRQ, ata_interrupt,
+				IRQF_SHARED, &pcmcia_sht);
+	if (ret)
 		goto failed;
 
 	info->ndev = 1;
diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c
index 93bcdad..a61cbc1 100644
--- a/drivers/ata/pata_pdc2027x.c
+++ b/drivers/ata/pata_pdc2027x.c
@@ -35,7 +35,7 @@
 #include <linux/libata.h>
 
 #define DRV_NAME	"pata_pdc2027x"
-#define DRV_VERSION	"0.8"
+#define DRV_VERSION	"0.9"
 #undef PDC_DEBUG
 
 #ifdef PDC_DEBUG
@@ -66,8 +66,10 @@
 static void pdc2027x_error_handler(struct ata_port *ap);
 static void pdc2027x_set_piomode(struct ata_port *ap, struct ata_device *adev);
 static void pdc2027x_set_dmamode(struct ata_port *ap, struct ata_device *adev);
-static void pdc2027x_post_set_mode(struct ata_port *ap);
 static int pdc2027x_check_atapi_dma(struct ata_queued_cmd *qc);
+static unsigned long pdc2027x_mode_filter(struct ata_device *adev, unsigned long mask);
+static int pdc2027x_cable_detect(struct ata_port *ap);
+static int pdc2027x_set_mode(struct ata_port *ap, struct ata_device **r_failed);
 
 /*
  * ATA Timing Tables based on 133MHz controller clock.
@@ -146,6 +148,7 @@
 
 static struct ata_port_operations pdc2027x_pata100_ops = {
 	.port_disable		= ata_port_disable,
+	.mode_filter		= ata_pci_default_filter,
 
 	.tf_load		= ata_tf_load,
 	.tf_read		= ata_tf_read,
@@ -166,8 +169,8 @@
 	.thaw			= ata_bmdma_thaw,
 	.error_handler		= pdc2027x_error_handler,
 	.post_internal_cmd 	= ata_bmdma_post_internal_cmd,
+	.cable_detect		= pdc2027x_cable_detect,
 
-	.irq_handler		= ata_interrupt,
 	.irq_clear		= ata_bmdma_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -179,7 +182,8 @@
 	.port_disable		= ata_port_disable,
 	.set_piomode		= pdc2027x_set_piomode,
 	.set_dmamode		= pdc2027x_set_dmamode,
-	.post_set_mode		= pdc2027x_post_set_mode,
+	.set_mode		= pdc2027x_set_mode,
+	.mode_filter		= pdc2027x_mode_filter,
 
 	.tf_load		= ata_tf_load,
 	.tf_read		= ata_tf_read,
@@ -200,8 +204,8 @@
 	.thaw			= ata_bmdma_thaw,
 	.error_handler		= pdc2027x_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
+	.cable_detect		= pdc2027x_cable_detect,
 
-	.irq_handler		= ata_interrupt,
 	.irq_clear		= ata_bmdma_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -212,7 +216,6 @@
 static struct ata_port_info pdc2027x_port_info[] = {
 	/* PDC_UDMA_100 */
 	{
-		.sht		= &pdc2027x_sht,
 		.flags		= ATA_FLAG_NO_LEGACY | ATA_FLAG_SLAVE_POSS |
 		                  ATA_FLAG_MMIO,
 		.pio_mask	= 0x1f, /* pio0-4 */
@@ -222,7 +225,6 @@
 	},
 	/* PDC_UDMA_133 */
 	{
-		.sht		= &pdc2027x_sht,
 		.flags		= ATA_FLAG_NO_LEGACY | ATA_FLAG_SLAVE_POSS |
                         	  ATA_FLAG_MMIO,
 		.pio_mask	= 0x1f, /* pio0-4 */
@@ -261,7 +263,7 @@
 }
 
 /**
- *	pdc2027x_pata_cbl_detect - Probe host controller cable detect info
+ *	pdc2027x_pata_cable_detect - Probe host controller cable detect info
  *	@ap: Port for which cable detect info is desired
  *
  *	Read 80c cable indicator from Promise extended register.
@@ -270,7 +272,7 @@
  *	LOCKING:
  *	None (inherited from caller).
  */
-static void pdc2027x_cbl_detect(struct ata_port *ap)
+static int pdc2027x_cable_detect(struct ata_port *ap)
 {
 	u32 cgcr;
 
@@ -281,13 +283,10 @@
 
 	PDPRINTK("No cable or 80-conductor cable on port %d\n", ap->port_no);
 
-	ap->cbl = ATA_CBL_PATA80;
-	return;
-
+	return ATA_CBL_PATA80;
 cbl40:
 	printk(KERN_INFO DRV_NAME ": 40-conductor cable detected on port %d\n", ap->port_no);
-	ap->cbl = ATA_CBL_PATA40;
-	ap->udma_mask &= ATA_UDMA_MASK_40C;
+	return ATA_CBL_PATA40;
 }
 
 /**
@@ -314,7 +313,6 @@
 	/* Check whether port enabled */
 	if (!pdc2027x_port_enabled(ap))
 		return -ENOENT;
-	pdc2027x_cbl_detect(ap);
 	return ata_std_prereset(ap);
 }
 
@@ -334,6 +332,32 @@
 }
 
 /**
+ *	pdc2720x_mode_filter	-	mode selection filter
+ *	@adev: ATA device
+ *	@mask: list of modes proposed
+ *
+ *	Block UDMA on devices that cause trouble with this controller.
+ */
+
+static unsigned long pdc2027x_mode_filter(struct ata_device *adev, unsigned long mask)
+{
+	unsigned char model_num[ATA_ID_PROD_LEN + 1];
+	struct ata_device *pair = ata_dev_pair(adev);
+
+	if (adev->class != ATA_DEV_ATA || adev->devno == 0 || pair == NULL)
+		return ata_pci_default_filter(adev, mask);
+
+	/* Check for slave of a Maxtor at UDMA6 */
+	ata_id_c_string(pair->id, model_num, ATA_ID_PROD,
+			  ATA_ID_PROD_LEN + 1);
+	/* If the master is a maxtor in UDMA6 then the slave should not use UDMA 6 */
+	if(strstr(model_num, "Maxtor") == 0 && pair->dma_mode == XFER_UDMA_6)
+		mask &= ~ (1 << (6 + ATA_SHIFT_UDMA));
+
+	return ata_pci_default_filter(adev, mask);
+}
+
+/**
  *	pdc2027x_set_piomode - Initialize host controller PATA PIO timings
  *	@ap: Port to configure
  *	@adev: um
@@ -444,17 +468,22 @@
 }
 
 /**
- *	pdc2027x_post_set_mode - Set the timing registers back to correct values.
+ *	pdc2027x_set_mode - Set the timing registers back to correct values.
  *	@ap: Port to configure
+ *	@r_failed: Returned device for failure
  *
  *	The pdc2027x hardware will look at "SET FEATURES" and change the timing registers
  *	automatically. The values set by the hardware might be incorrect, under 133Mhz PLL.
  *	This function overwrites the possibly incorrect values set by the hardware to be correct.
  */
-static void pdc2027x_post_set_mode(struct ata_port *ap)
+static int pdc2027x_set_mode(struct ata_port *ap, struct ata_device **r_failed)
 {
 	int i;
 
+	i = ata_do_set_mode(ap, r_failed);
+	if (i < 0)
+		return i;
+
 	for (i = 0; i < ATA_MAX_DEVICES; i++) {
 		struct ata_device *dev = &ap->device[i];
 
@@ -476,6 +505,7 @@
 			}
 		}
 	}
+	return 0;
 }
 
 /**
@@ -521,12 +551,12 @@
 
 /**
  * pdc_read_counter - Read the ctr counter
- * @probe_ent: for the port address
+ * @host: target ATA host
  */
 
-static long pdc_read_counter(struct ata_probe_ent *probe_ent)
+static long pdc_read_counter(struct ata_host *host)
 {
-	void __iomem *mmio_base = probe_ent->iomap[PDC_MMIO_BAR];
+	void __iomem *mmio_base = host->iomap[PDC_MMIO_BAR];
 	long counter;
 	int retry = 1;
 	u32 bccrl, bccrh, bccrlv, bccrhv;
@@ -564,12 +594,12 @@
  * adjust_pll - Adjust the PLL input clock in Hz.
  *
  * @pdc_controller: controller specific information
- * @probe_ent: For the port address
+ * @host: target ATA host
  * @pll_clock: The input of PLL in HZ
  */
-static void pdc_adjust_pll(struct ata_probe_ent *probe_ent, long pll_clock, unsigned int board_idx)
+static void pdc_adjust_pll(struct ata_host *host, long pll_clock, unsigned int board_idx)
 {
-	void __iomem *mmio_base = probe_ent->iomap[PDC_MMIO_BAR];
+	void __iomem *mmio_base = host->iomap[PDC_MMIO_BAR];
 	u16 pll_ctl;
 	long pll_clock_khz = pll_clock / 1000;
 	long pout_required = board_idx? PDC_133_MHZ:PDC_100_MHZ;
@@ -649,19 +679,19 @@
 
 /**
  * detect_pll_input_clock - Detect the PLL input clock in Hz.
- * @probe_ent: for the port address
+ * @host: target ATA host
  * Ex. 16949000 on 33MHz PCI bus for pdc20275.
  *     Half of the PCI clock.
  */
-static long pdc_detect_pll_input_clock(struct ata_probe_ent *probe_ent)
+static long pdc_detect_pll_input_clock(struct ata_host *host)
 {
-	void __iomem *mmio_base = probe_ent->iomap[PDC_MMIO_BAR];
+	void __iomem *mmio_base = host->iomap[PDC_MMIO_BAR];
 	u32 scr;
 	long start_count, end_count;
 	long pll_clock;
 
 	/* Read current counter value */
-	start_count = pdc_read_counter(probe_ent);
+	start_count = pdc_read_counter(host);
 
 	/* Start the test mode */
 	scr = readl(mmio_base + PDC_SYS_CTL);
@@ -673,7 +703,7 @@
 	mdelay(100);
 
 	/* Read the counter values again */
-	end_count = pdc_read_counter(probe_ent);
+	end_count = pdc_read_counter(host);
 
 	/* Stop the test mode */
 	scr = readl(mmio_base + PDC_SYS_CTL);
@@ -692,11 +722,10 @@
 
 /**
  * pdc_hardware_init - Initialize the hardware.
- * @pdev: instance of pci_dev found
- * @pdc_controller: controller specific information
- * @pe:  for the port address
+ * @host: target ATA host
+ * @board_idx: board identifier
  */
-static int pdc_hardware_init(struct pci_dev *pdev, struct ata_probe_ent *pe, unsigned int board_idx)
+static int pdc_hardware_init(struct ata_host *host, unsigned int board_idx)
 {
 	long pll_clock;
 
@@ -706,15 +735,15 @@
 	 * Ex. 25MHz or 40MHz, we have to adjust the cycle_time.
 	 * The pdc20275 controller employs PLL circuit to help correct timing registers setting.
 	 */
-	pll_clock = pdc_detect_pll_input_clock(pe);
+	pll_clock = pdc_detect_pll_input_clock(host);
 
 	if (pll_clock < 0) /* counter overflow? Try again. */
-		pll_clock = pdc_detect_pll_input_clock(pe);
+		pll_clock = pdc_detect_pll_input_clock(host);
 
-	dev_printk(KERN_INFO, &pdev->dev, "PLL input clock %ld kHz\n", pll_clock/1000);
+	dev_printk(KERN_INFO, host->dev, "PLL input clock %ld kHz\n", pll_clock/1000);
 
 	/* Adjust PLL control register */
-	pdc_adjust_pll(pe, pll_clock, board_idx);
+	pdc_adjust_pll(host, pll_clock, board_idx);
 
 	return 0;
 }
@@ -746,8 +775,7 @@
  * Called when an instance of PCI adapter is inserted.
  * This function checks whether the hardware is supported,
  * initialize hardware and register an instance of ata_host to
- * libata by providing struct ata_probe_ent and ata_device_add().
- * (implements struct pci_driver.probe() )
+ * libata.  (implements struct pci_driver.probe() )
  *
  * @pdev: instance of pci_dev found
  * @ent:  matching entry in the id_tbl[]
@@ -756,14 +784,21 @@
 {
 	static int printed_version;
 	unsigned int board_idx = (unsigned int) ent->driver_data;
-
-	struct ata_probe_ent *probe_ent;
+	const struct ata_port_info *ppi[] =
+		{ &pdc2027x_port_info[board_idx], NULL };
+	struct ata_host *host;
 	void __iomem *mmio_base;
 	int rc;
 
 	if (!printed_version++)
 		dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
 
+	/* alloc host */
+	host = ata_host_alloc_pinfo(&pdev->dev, ppi, 2);
+	if (!host)
+		return -ENOMEM;
+
+	/* acquire resources and fill host */
 	rc = pcim_enable_device(pdev);
 	if (rc)
 		return rc;
@@ -771,6 +806,7 @@
 	rc = pcim_iomap_regions(pdev, 1 << PDC_MMIO_BAR, DRV_NAME);
 	if (rc)
 		return rc;
+	host->iomap = pcim_iomap_table(pdev);
 
 	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
 	if (rc)
@@ -780,46 +816,22 @@
 	if (rc)
 		return rc;
 
-	/* Prepare the probe entry */
-	probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL);
-	if (probe_ent == NULL)
-		return -ENOMEM;
+	mmio_base = host->iomap[PDC_MMIO_BAR];
 
-	probe_ent->dev = pci_dev_to_dev(pdev);
-	INIT_LIST_HEAD(&probe_ent->node);
+	pdc_ata_setup_port(&host->ports[0]->ioaddr, mmio_base + 0x17c0);
+	host->ports[0]->ioaddr.bmdma_addr = mmio_base + 0x1000;
+	pdc_ata_setup_port(&host->ports[1]->ioaddr, mmio_base + 0x15c0);
+	host->ports[1]->ioaddr.bmdma_addr = mmio_base + 0x1008;
 
-	probe_ent->sht		= pdc2027x_port_info[board_idx].sht;
-	probe_ent->port_flags	= pdc2027x_port_info[board_idx].flags;
-	probe_ent->pio_mask	= pdc2027x_port_info[board_idx].pio_mask;
-	probe_ent->mwdma_mask	= pdc2027x_port_info[board_idx].mwdma_mask;
-	probe_ent->udma_mask	= pdc2027x_port_info[board_idx].udma_mask;
-	probe_ent->port_ops	= pdc2027x_port_info[board_idx].port_ops;
-
-       	probe_ent->irq = pdev->irq;
-       	probe_ent->irq_flags = IRQF_SHARED;
-	probe_ent->iomap = pcim_iomap_table(pdev);
-
-	mmio_base = probe_ent->iomap[PDC_MMIO_BAR];
-
-	pdc_ata_setup_port(&probe_ent->port[0], mmio_base + 0x17c0);
-	probe_ent->port[0].bmdma_addr = mmio_base + 0x1000;
-	pdc_ata_setup_port(&probe_ent->port[1], mmio_base + 0x15c0);
-	probe_ent->port[1].bmdma_addr = mmio_base + 0x1008;
-
-	probe_ent->n_ports = 2;
-
-	pci_set_master(pdev);
 	//pci_enable_intx(pdev);
 
 	/* initialize adapter */
-	if (pdc_hardware_init(pdev, probe_ent, board_idx) != 0)
+	if (pdc_hardware_init(host, board_idx) != 0)
 		return -EIO;
 
-	if (!ata_device_add(probe_ent))
-		return -ENODEV;
-
-	devm_kfree(&pdev->dev, probe_ent);
-	return 0;
+	pci_set_master(pdev);
+	return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED,
+				 &pdc2027x_sht);
 }
 
 /**
diff --git a/drivers/ata/pata_pdc202xx_old.c b/drivers/ata/pata_pdc202xx_old.c
index 0a14933..ee636be 100644
--- a/drivers/ata/pata_pdc202xx_old.c
+++ b/drivers/ata/pata_pdc202xx_old.c
@@ -22,45 +22,17 @@
 #include <linux/libata.h>
 
 #define DRV_NAME "pata_pdc202xx_old"
-#define DRV_VERSION "0.4.0"
+#define DRV_VERSION "0.4.2"
 
-/**
- *	pdc2024x_pre_reset		-	probe begin
- *	@ap: ATA port
- *
- *	Set up cable type and use generic probe init
- */
-
-static int pdc2024x_pre_reset(struct ata_port *ap)
-{
-	ap->cbl = ATA_CBL_PATA40;
-	return ata_std_prereset(ap);
-}
-
-
-static void pdc2024x_error_handler(struct ata_port *ap)
-{
-	ata_bmdma_drive_eh(ap, pdc2024x_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
-
-static int pdc2026x_pre_reset(struct ata_port *ap)
+static int pdc2026x_cable_detect(struct ata_port *ap)
 {
 	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
 	u16 cis;
 
 	pci_read_config_word(pdev, 0x50, &cis);
 	if (cis & (1 << (10 + ap->port_no)))
-		ap->cbl = ATA_CBL_PATA80;
-	else
-		ap->cbl = ATA_CBL_PATA40;
-
-	return ata_std_prereset(ap);
-}
-
-static void pdc2026x_error_handler(struct ata_port *ap)
-{
-	ata_bmdma_drive_eh(ap, pdc2026x_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
+		return ATA_CBL_PATA80;
+	return ATA_CBL_PATA40;
 }
 
 /**
@@ -244,7 +216,6 @@
 
 /**
  *	pdc2026x_dev_config	-	device setup hook
- *	@ap: ATA port
  *	@adev: newly found device
  *
  *	Perform chip specific early setup. We need to lock the transfer
@@ -252,7 +223,7 @@
  *	barf.
  */
 
-static void pdc2026x_dev_config(struct ata_port *ap, struct ata_device *adev)
+static void pdc2026x_dev_config(struct ata_device *adev)
 {
 	adev->max_sectors = 256;
 }
@@ -292,8 +263,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= pdc2024x_error_handler,
+	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
@@ -326,8 +298,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= pdc2026x_error_handler,
+	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= pdc2026x_cable_detect,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= pdc2026x_bmdma_start,
diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c
index 4b82a54..a0a650c 100644
--- a/drivers/ata/pata_platform.c
+++ b/drivers/ata/pata_platform.c
@@ -80,13 +80,13 @@
 	.thaw			= ata_bmdma_thaw,
 	.error_handler		= ata_bmdma_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
+	.cable_detect		= ata_cable_unknown,
 
 	.qc_prep		= ata_qc_prep,
 	.qc_issue		= ata_qc_issue_prot,
 
 	.data_xfer		= ata_data_xfer_noirq,
 
-	.irq_handler		= ata_interrupt,
 	.irq_clear		= ata_bmdma_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -135,7 +135,8 @@
 static int __devinit pata_platform_probe(struct platform_device *pdev)
 {
 	struct resource *io_res, *ctl_res;
-	struct ata_probe_ent ae;
+	struct ata_host *host;
+	struct ata_port *ap;
 	unsigned int mmio;
 
 	/*
@@ -175,44 +176,41 @@
 	/*
 	 * Now that that's out of the way, wire up the port..
 	 */
-	memset(&ae, 0, sizeof(struct ata_probe_ent));
-	INIT_LIST_HEAD(&ae.node);
-	ae.dev = &pdev->dev;
-	ae.port_ops = &pata_platform_port_ops;
-	ae.sht = &pata_platform_sht;
-	ae.n_ports = 1;
-	ae.pio_mask = pio_mask;
-	ae.irq = platform_get_irq(pdev, 0);
-	ae.irq_flags = 0;
-	ae.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST;
+	host = ata_host_alloc(&pdev->dev, 1);
+	if (!host)
+		return -ENOMEM;
+	ap = host->ports[0];
+
+	ap->ops = &pata_platform_port_ops;
+	ap->pio_mask = pio_mask;
+	ap->flags |= ATA_FLAG_SLAVE_POSS;
 
 	/*
 	 * Handle the MMIO case
 	 */
 	if (mmio) {
-		ae.port[0].cmd_addr = devm_ioremap(&pdev->dev, io_res->start,
+		ap->ioaddr.cmd_addr = devm_ioremap(&pdev->dev, io_res->start,
 				io_res->end - io_res->start + 1);
-		ae.port[0].ctl_addr = devm_ioremap(&pdev->dev, ctl_res->start,
+		ap->ioaddr.ctl_addr = devm_ioremap(&pdev->dev, ctl_res->start,
 				ctl_res->end - ctl_res->start + 1);
 	} else {
-		ae.port[0].cmd_addr = devm_ioport_map(&pdev->dev, io_res->start,
+		ap->ioaddr.cmd_addr = devm_ioport_map(&pdev->dev, io_res->start,
 				io_res->end - io_res->start + 1);
-		ae.port[0].ctl_addr = devm_ioport_map(&pdev->dev, ctl_res->start,
+		ap->ioaddr.ctl_addr = devm_ioport_map(&pdev->dev, ctl_res->start,
 				ctl_res->end - ctl_res->start + 1);
 	}
-	if (!ae.port[0].cmd_addr || !ae.port[0].ctl_addr) {
+	if (!ap->ioaddr.cmd_addr || !ap->ioaddr.ctl_addr) {
 		dev_err(&pdev->dev, "failed to map IO/CTL base\n");
 		return -ENOMEM;
 	}
 
-	ae.port[0].altstatus_addr = ae.port[0].ctl_addr;
+	ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr;
 
-	pata_platform_setup_port(&ae.port[0], pdev->dev.platform_data);
+	pata_platform_setup_port(&ap->ioaddr, pdev->dev.platform_data);
 
-	if (unlikely(ata_device_add(&ae) == 0))
-		return -ENODEV;
-
-	return 0;
+	/* activate */
+	return ata_host_activate(host, platform_get_irq(pdev, 0), ata_interrupt,
+				 0, &pata_platform_sht);
 }
 
 /**
diff --git a/drivers/ata/pata_qdi.c b/drivers/ata/pata_qdi.c
index c381001..27685ce 100644
--- a/drivers/ata/pata_qdi.c
+++ b/drivers/ata/pata_qdi.c
@@ -183,13 +183,13 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.qc_prep 	= ata_qc_prep,
 	.qc_issue	= qdi_qc_issue_prot,
 
 	.data_xfer	= qdi_data_xfer,
 
-	.irq_handler	= ata_interrupt,
 	.irq_clear	= ata_bmdma_irq_clear,
 	.irq_on		= ata_irq_on,
 	.irq_ack	= ata_irq_ack,
@@ -211,13 +211,13 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.qc_prep 	= ata_qc_prep,
 	.qc_issue	= qdi_qc_issue_prot,
 
 	.data_xfer	= qdi_data_xfer,
 
-	.irq_handler	= ata_interrupt,
 	.irq_clear	= ata_bmdma_irq_clear,
 	.irq_on		= ata_irq_on,
 	.irq_ack	= ata_irq_ack,
@@ -238,8 +238,9 @@
 
 static __init int qdi_init_one(unsigned long port, int type, unsigned long io, int irq, int fast)
 {
-	struct ata_probe_ent ae;
 	struct platform_device *pdev;
+	struct ata_host *host;
+	struct ata_port *ap;
 	void __iomem *io_addr, *ctl_addr;
 	int ret;
 
@@ -257,34 +258,31 @@
 	if (!io_addr || !ctl_addr)
 		goto fail;
 
-	memset(&ae, 0, sizeof(struct ata_probe_ent));
-	INIT_LIST_HEAD(&ae.node);
-	ae.dev = &pdev->dev;
+	ret = -ENOMEM;
+	host = ata_host_alloc(&pdev->dev, 1);
+	if (!host)
+		goto fail;
+	ap = host->ports[0];
 
 	if (type == 6580) {
-		ae.port_ops = &qdi6580_port_ops;
-		ae.pio_mask = 0x1F;
-		ae.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST;
+		ap->ops = &qdi6580_port_ops;
+		ap->pio_mask = 0x1F;
+		ap->flags |= ATA_FLAG_SLAVE_POSS;
 	} else {
-		ae.port_ops = &qdi6500_port_ops;
-		ae.pio_mask = 0x07;	/* Actually PIO3 !IORDY is possible */
-		ae.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST |
-				ATA_FLAG_NO_IORDY;
+		ap->ops = &qdi6500_port_ops;
+		ap->pio_mask = 0x07;	/* Actually PIO3 !IORDY is possible */
+		ap->flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_IORDY;
 	}
 
-	ae.sht = &qdi_sht;
-	ae.n_ports = 1;
-	ae.irq = irq;
-	ae.irq_flags = 0;
-	ae.port[0].cmd_addr = io_addr;
-	ae.port[0].altstatus_addr = ctl_addr;
-	ae.port[0].ctl_addr = ctl_addr;
-	ata_std_ports(&ae.port[0]);
+	ap->ioaddr.cmd_addr = io_addr;
+	ap->ioaddr.altstatus_addr = ctl_addr;
+	ap->ioaddr.ctl_addr = ctl_addr;
+	ata_std_ports(&ap->ioaddr);
 
 	/*
 	 *	Hook in a private data structure per channel
 	 */
-	ae.private_data = &qdi_data[nr_qdi_host];
+	ap->private_data = &qdi_data[nr_qdi_host];
 
 	qdi_data[nr_qdi_host].timing = port;
 	qdi_data[nr_qdi_host].fast = fast;
@@ -292,8 +290,9 @@
 
 	printk(KERN_INFO DRV_NAME": qd%d at 0x%lx.\n", type, io);
 
-	ret = -ENODEV;
-	if (!ata_device_add(&ae))
+	/* activate */
+	ret = ata_host_activate(host, irq, ata_interrupt, 0, &qdi_sht);
+	if (ret)
 		goto fail;
 
 	qdi_host[nr_qdi_host++] = dev_get_drvdata(&pdev->dev);
diff --git a/drivers/ata/pata_radisys.c b/drivers/ata/pata_radisys.c
index 9a9132c..1c54673 100644
--- a/drivers/ata/pata_radisys.c
+++ b/drivers/ata/pata_radisys.c
@@ -24,40 +24,12 @@
 #include <linux/ata.h>
 
 #define DRV_NAME	"pata_radisys"
-#define DRV_VERSION	"0.4.1"
-
-/**
- *	radisys_probe_init		-	probe begin
- *	@ap: ATA port
- *
- *	Set up cable type and use generic probe init
- */
-
-static int radisys_pre_reset(struct ata_port *ap)
-{
-	ap->cbl = ATA_CBL_PATA80;
-	return ata_std_prereset(ap);
-}
-
-
-/**
- *	radisys_pata_error_handler - Probe specified port on PATA host controller
- *	@ap: Port to probe
- *	@classes:
- *
- *	LOCKING:
- *	None (inherited from caller).
- */
-
-static void radisys_pata_error_handler(struct ata_port *ap)
-{
-	ata_bmdma_drive_eh(ap, radisys_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
+#define DRV_VERSION	"0.4.4"
 
 /**
  *	radisys_set_piomode - Initialize host controller PATA PIO timings
- *	@ap: Port whose timings we are configuring
- *	@adev: um
+ *	@ap: ATA port
+ *	@adev: Device whose timings we are configuring
  *
  *	Set PIO mode for device, in host controller PCI config space.
  *
@@ -248,8 +220,9 @@
 
 	.freeze			= ata_bmdma_freeze,
 	.thaw			= ata_bmdma_thaw,
-	.error_handler		= radisys_pata_error_handler,
+	.error_handler		= ata_bmdma_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
+	.cable_detect		= ata_cable_unknown,
 
 	.bmdma_setup		= ata_bmdma_setup,
 	.bmdma_start		= ata_bmdma_start,
diff --git a/drivers/ata/pata_rz1000.c b/drivers/ata/pata_rz1000.c
index f522daa..85c4529 100644
--- a/drivers/ata/pata_rz1000.c
+++ b/drivers/ata/pata_rz1000.c
@@ -25,31 +25,6 @@
 
 
 /**
- *	rz1000_prereset		-	probe begin
- *	@ap: ATA port
- *
- *	Set up cable type and use generics
- */
-
-static int rz1000_prereset(struct ata_port *ap)
-{
-	ap->cbl = ATA_CBL_PATA40;
-	return ata_std_prereset(ap);
-}
-
-/**
- *	rz1000_error_handler		-	probe reset
- *	@ap: ATA port
- *
- *	Perform the ATA standard reset sequence
- */
-
-static void rz1000_error_handler(struct ata_port *ap)
-{
-	ata_bmdma_drive_eh(ap, rz1000_prereset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
-/**
  *	rz1000_set_mode		-	mode setting function
  *	@ap: ATA interface
  *	@unused: returned device on set_mode failure
@@ -122,8 +97,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= rz1000_error_handler,
+	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.irq_handler	= ata_interrupt,
 	.irq_clear	= ata_bmdma_irq_clear,
diff --git a/drivers/ata/pata_sc1200.c b/drivers/ata/pata_sc1200.c
index 93b3ed0..66e8ff4 100644
--- a/drivers/ata/pata_sc1200.c
+++ b/drivers/ata/pata_sc1200.c
@@ -216,6 +216,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
diff --git a/drivers/ata/pata_scc.c b/drivers/ata/pata_scc.c
index f3ed141..5df354d 100644
--- a/drivers/ata/pata_scc.c
+++ b/drivers/ata/pata_scc.c
@@ -1016,7 +1016,6 @@
 	.error_handler		= scc_error_handler,
 	.post_internal_cmd	= scc_bmdma_stop,
 
-	.irq_handler		= ata_interrupt,
 	.irq_clear		= scc_bmdma_irq_clear,
 	.irq_on			= scc_irq_on,
 	.irq_ack		= scc_irq_ack,
@@ -1027,7 +1026,6 @@
 
 static struct ata_port_info scc_port_info[] = {
 	{
-		.sht		= &scc_sht,
 		.flags		= ATA_FLAG_SLAVE_POSS | ATA_FLAG_MMIO | ATA_FLAG_NO_LEGACY,
 		.pio_mask	= 0x1f,	/* pio0-4 */
 		.mwdma_mask	= 0x00,
@@ -1040,10 +1038,10 @@
  *	scc_reset_controller - initialize SCC PATA controller.
  */
 
-static int scc_reset_controller(struct ata_probe_ent *probe_ent)
+static int scc_reset_controller(struct ata_host *host)
 {
-	void __iomem *ctrl_base = probe_ent->iomap[SCC_CTRL_BAR];
-	void __iomem *bmid_base = probe_ent->iomap[SCC_BMID_BAR];
+	void __iomem *ctrl_base = host->iomap[SCC_CTRL_BAR];
+	void __iomem *bmid_base = host->iomap[SCC_BMID_BAR];
 	void __iomem *cckctrl_port = ctrl_base + SCC_CTL_CCKCTRL;
 	void __iomem *mode_port = ctrl_base + SCC_CTL_MODEREG;
 	void __iomem *ecmode_port = ctrl_base + SCC_CTL_ECMODE;
@@ -1104,17 +1102,15 @@
 	ioaddr->command_addr = ioaddr->cmd_addr + SCC_REG_CMD;
 }
 
-static int scc_host_init(struct ata_probe_ent *probe_ent)
+static int scc_host_init(struct ata_host *host)
 {
-	struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
+	struct pci_dev *pdev = to_pci_dev(host->dev);
 	int rc;
 
-	rc = scc_reset_controller(probe_ent);
+	rc = scc_reset_controller(host);
 	if (rc)
 		return rc;
 
-	probe_ent->n_ports = 1;
-
 	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
 	if (rc)
 		return rc;
@@ -1122,7 +1118,7 @@
 	if (rc)
 		return rc;
 
-	scc_setup_ports(&probe_ent->port[0], probe_ent->iomap[SCC_BMID_BAR]);
+	scc_setup_ports(&host->ports[0]->ioaddr, host->iomap[SCC_BMID_BAR]);
 
 	pci_set_master(pdev);
 
@@ -1145,14 +1141,18 @@
 {
 	static int printed_version;
 	unsigned int board_idx = (unsigned int) ent->driver_data;
+	const struct ata_port_info *ppi[] = { &scc_port_info[board_idx], NULL };
 	struct device *dev = &pdev->dev;
-	struct ata_probe_ent *probe_ent;
 	int rc;
 
 	if (!printed_version++)
 		dev_printk(KERN_DEBUG, &pdev->dev,
 			   "version " DRV_VERSION "\n");
 
+	host = ata_port_alloc_pinfo(&pdev->dev, ppi, 1);
+	if (!host)
+		return -ENOMEM;
+
 	rc = pcim_enable_device(pdev);
 	if (rc)
 		return rc;
@@ -1162,33 +1162,14 @@
 		pcim_pin_device(pdev);
 	if (rc)
 		return rc;
+	host->iomap = pcim_iomap_table(pdev);
 
-	probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL);
-	if (!probe_ent)
-		return -ENOMEM;
-
-	probe_ent->dev = dev;
-	INIT_LIST_HEAD(&probe_ent->node);
-
-	probe_ent->sht 		= scc_port_info[board_idx].sht;
-	probe_ent->port_flags 	= scc_port_info[board_idx].flags;
-	probe_ent->pio_mask 	= scc_port_info[board_idx].pio_mask;
-	probe_ent->udma_mask 	= scc_port_info[board_idx].udma_mask;
-	probe_ent->port_ops 	= scc_port_info[board_idx].port_ops;
-
-	probe_ent->irq = pdev->irq;
-	probe_ent->irq_flags = IRQF_SHARED;
-	probe_ent->iomap = pcim_iomap_table(pdev);
-
-	rc = scc_host_init(probe_ent);
+	rc = scc_host_init(host);
 	if (rc)
 		return rc;
 
-	if (!ata_device_add(probe_ent))
-		return -ENODEV;
-
-	devm_kfree(dev, probe_ent);
-	return 0;
+	return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED,
+				 &scc_sht);
 }
 
 static struct pci_driver scc_pci_driver = {
diff --git a/drivers/ata/pata_serverworks.c b/drivers/ata/pata_serverworks.c
index 598eef8..3956ef2 100644
--- a/drivers/ata/pata_serverworks.c
+++ b/drivers/ata/pata_serverworks.c
@@ -1,5 +1,5 @@
 /*
- * ata-serverworks.c 	- Serverworks PATA for new ATA layer
+ * pata_serverworks.c 	- Serverworks PATA for new ATA layer
  *			  (C) 2005 Red Hat Inc
  *			  Alan Cox <alan@redhat.com>
  *
@@ -137,14 +137,14 @@
 };
 
 /**
- *	serverworks_pre_reset		-	cable detection
+ *	serverworks_cable_detect	-	cable detection
  *	@ap: ATA port
  *
  *	Perform cable detection according to the device and subvendor
  *	identifications
  */
 
-static int serverworks_pre_reset(struct ata_port *ap) {
+static int serverworks_cable_detect(struct ata_port *ap) {
 	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
 	struct sv_cable_table *cb = cable_detect;
 
@@ -152,8 +152,7 @@
 		if (cb->device == pdev->device &&
 		    (cb->subvendor == pdev->subsystem_vendor ||
 		      cb->subvendor == PCI_ANY_ID)) {
-			ap->cbl = cb->cable_detect(ap);
-			return ata_std_prereset(ap);
+			return cb->cable_detect(ap);
 		}
 		cb++;
 	}
@@ -162,11 +161,6 @@
 	return -1;	/* kill compiler warning */
 }
 
-static void serverworks_error_handler(struct ata_port *ap)
-{
-	return ata_bmdma_drive_eh(ap, serverworks_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
 /**
  *	serverworks_is_csb	-	Check for CSB or OSB
  *	@pdev: PCI device to check
@@ -191,31 +185,31 @@
 
 /**
  *	serverworks_osb4_filter	-	mode selection filter
- *	@ap: ATA interface
  *	@adev: ATA device
+ *	@mask: Mask of proposed modes
  *
  *	Filter the offered modes for the device to apply controller
  *	specific rules. OSB4 requires no UDMA for disks due to a FIFO
  *	bug we hit.
  */
 
-static unsigned long serverworks_osb4_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask)
+static unsigned long serverworks_osb4_filter(struct ata_device *adev, unsigned long mask)
 {
 	if (adev->class == ATA_DEV_ATA)
 		mask &= ~ATA_MASK_UDMA;
-	return ata_pci_default_filter(ap, adev, mask);
+	return ata_pci_default_filter(adev, mask);
 }
 
 
 /**
  *	serverworks_csb_filter	-	mode selection filter
- *	@ap: ATA interface
  *	@adev: ATA device
+ *	@mask: Mask of proposed modes
  *
  *	Check the blacklist and disable UDMA5 if matched
  */
 
-static unsigned long serverworks_csb_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask)
+static unsigned long serverworks_csb_filter(struct ata_device *adev, unsigned long mask)
 {
 	const char *p;
 	char model_num[ATA_ID_PROD_LEN + 1];
@@ -223,7 +217,7 @@
 
 	/* Disk, UDMA */
 	if (adev->class != ATA_DEV_ATA)
-		return ata_pci_default_filter(ap, adev, mask);
+		return ata_pci_default_filter(adev, mask);
 
 	/* Actually do need to check */
 	ata_id_c_string(adev->id, model_num, ATA_ID_PROD, sizeof(model_num));
@@ -232,7 +226,7 @@
 		if (!strcmp(p, model_num))
 			mask &= ~(0x1F << ATA_SHIFT_UDMA);
 	}
-	return ata_pci_default_filter(ap, adev, mask);
+	return ata_pci_default_filter(adev, mask);
 }
 
 
@@ -339,8 +333,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= serverworks_error_handler,
+	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= serverworks_cable_detect,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
@@ -374,8 +369,9 @@
 
 	.freeze		= ata_bmdma_freeze,
 	.thaw		= ata_bmdma_thaw,
-	.error_handler	= serverworks_error_handler,
+	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= serverworks_cable_detect,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
diff --git a/drivers/ata/pata_sil680.c b/drivers/ata/pata_sil680.c
index dab2889..6770820 100644
--- a/drivers/ata/pata_sil680.c
+++ b/drivers/ata/pata_sil680.c
@@ -33,7 +33,7 @@
 #include <linux/libata.h>
 
 #define DRV_NAME "pata_sil680"
-#define DRV_VERSION "0.4.5"
+#define DRV_VERSION "0.4.6"
 
 /**
  *	sil680_selreg		-	return register base
@@ -91,12 +91,6 @@
 		return ATA_CBL_PATA40;
 }
 
-static int sil680_pre_reset(struct ata_port *ap)
-{
-	ap->cbl = sil680_cable_detect(ap);
-	return ata_std_prereset(ap);
-}
-
 /**
  *	sil680_bus_reset	-	reset the SIL680 bus
  *	@ap: ATA port to reset
@@ -119,7 +113,7 @@
 
 static void sil680_error_handler(struct ata_port *ap)
 {
-	ata_bmdma_drive_eh(ap, sil680_pre_reset, sil680_bus_reset, NULL, ata_std_postreset);
+	ata_bmdma_drive_eh(ap, ata_std_prereset, sil680_bus_reset, NULL, ata_std_postreset);
 }
 
 /**
@@ -257,6 +251,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= sil680_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= sil680_cable_detect,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c
index 8dc3bc4..a3fbcee 100644
--- a/drivers/ata/pata_sis.c
+++ b/drivers/ata/pata_sis.c
@@ -35,7 +35,7 @@
 #include "sis.h"
 
 #define DRV_NAME	"pata_sis"
-#define DRV_VERSION	"0.5.0"
+#define DRV_VERSION	"0.5.1"
 
 struct sis_chipset {
 	u16 device;			/* PCI host ID */
@@ -86,14 +86,55 @@
 }
 
 /**
- *	sis_133_pre_reset	-	check for 40/80 pin
+ *	sis_133_cable_detect	-	check for 40/80 pin
  *	@ap: Port
  *
  *	Perform cable detection for the later UDMA133 capable
  *	SiS chipset.
  */
 
-static int sis_133_pre_reset(struct ata_port *ap)
+static int sis_133_cable_detect(struct ata_port *ap)
+{
+	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+	u16 tmp;
+
+	/* The top bit of this register is the cable detect bit */
+	pci_read_config_word(pdev, 0x50 + 2 * ap->port_no, &tmp);
+	if ((tmp & 0x8000) && !sis_short_ata40(pdev))
+		return ATA_CBL_PATA40;
+	return ATA_CBL_PATA80;
+}
+
+/**
+ *	sis_66_cable_detect	-	check for 40/80 pin
+ *	@ap: Port
+ *
+ *	Perform cable detection on the UDMA66, UDMA100 and early UDMA133
+ *	SiS IDE controllers.
+ */
+
+static int sis_66_cable_detect(struct ata_port *ap)
+{
+	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+	u8 tmp;
+
+	/* Older chips keep cable detect in bits 4/5 of reg 0x48 */
+	pci_read_config_byte(pdev, 0x48, &tmp);
+	tmp >>= ap->port_no;
+	if ((tmp & 0x10) && !sis_short_ata40(pdev))
+		return ATA_CBL_PATA40;
+	return ATA_CBL_PATA80;
+}
+
+
+/**
+ *	sis_pre_reset		-	probe begin
+ *	@ap: ATA port
+ *
+ *	Set up cable type and use generic probe init
+ */
+
+static int sis_pre_reset(struct ata_port *ap)
 {
 	static const struct pci_bits sis_enable_bits[] = {
 		{ 0x4aU, 1U, 0x02UL, 0x02UL },	/* port 0 */
@@ -101,21 +142,13 @@
 	};
 
 	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
-	u16 tmp;
 
 	if (!pci_test_config_bits(pdev, &sis_enable_bits[ap->port_no]))
 		return -ENOENT;
-
-	/* The top bit of this register is the cable detect bit */
-	pci_read_config_word(pdev, 0x50 + 2 * ap->port_no, &tmp);
-	if ((tmp & 0x8000) && !sis_short_ata40(pdev))
-		ap->cbl = ATA_CBL_PATA40;
-	else
-		ap->cbl = ATA_CBL_PATA80;
-
 	return ata_std_prereset(ap);
 }
 
+
 /**
  *	sis_error_handler - Probe specified port on PATA host controller
  *	@ap: Port to probe
@@ -124,97 +157,9 @@
  *	None (inherited from caller).
  */
 
-static void sis_133_error_handler(struct ata_port *ap)
+static void sis_error_handler(struct ata_port *ap)
 {
-	ata_bmdma_drive_eh(ap, sis_133_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
-
-/**
- *	sis_66_pre_reset	-	check for 40/80 pin
- *	@ap: Port
- *
- *	Perform cable detection on the UDMA66, UDMA100 and early UDMA133
- *	SiS IDE controllers.
- */
-
-static int sis_66_pre_reset(struct ata_port *ap)
-{
-	static const struct pci_bits sis_enable_bits[] = {
-		{ 0x4aU, 1U, 0x02UL, 0x02UL },	/* port 0 */
-		{ 0x4aU, 1U, 0x04UL, 0x04UL },	/* port 1 */
-	};
-
-	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
-	u8 tmp;
-
-	if (!pci_test_config_bits(pdev, &sis_enable_bits[ap->port_no])) {
-		ata_port_disable(ap);
-		ata_port_printk(ap, KERN_INFO, "port disabled. ignoring.\n");
-		return 0;
-	}
-	/* Older chips keep cable detect in bits 4/5 of reg 0x48 */
-	pci_read_config_byte(pdev, 0x48, &tmp);
-	tmp >>= ap->port_no;
-	if ((tmp & 0x10) && !sis_short_ata40(pdev))
-		ap->cbl = ATA_CBL_PATA40;
-	else
-		ap->cbl = ATA_CBL_PATA80;
-
-	return ata_std_prereset(ap);
-}
-
-/**
- *	sis_66_error_handler - Probe specified port on PATA host controller
- *	@ap: Port to probe
- *	@classes:
- *
- *	LOCKING:
- *	None (inherited from caller).
- */
-
-static void sis_66_error_handler(struct ata_port *ap)
-{
-	ata_bmdma_drive_eh(ap, sis_66_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
-/**
- *	sis_old_pre_reset		-	probe begin
- *	@ap: ATA port
- *
- *	Set up cable type and use generic probe init
- */
-
-static int sis_old_pre_reset(struct ata_port *ap)
-{
-	static const struct pci_bits sis_enable_bits[] = {
-		{ 0x4aU, 1U, 0x02UL, 0x02UL },	/* port 0 */
-		{ 0x4aU, 1U, 0x04UL, 0x04UL },	/* port 1 */
-	};
-
-	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
-
-	if (!pci_test_config_bits(pdev, &sis_enable_bits[ap->port_no])) {
-		ata_port_disable(ap);
-		ata_port_printk(ap, KERN_INFO, "port disabled. ignoring.\n");
-		return 0;
-	}
-	ap->cbl = ATA_CBL_PATA40;
-	return ata_std_prereset(ap);
-}
-
-
-/**
- *	sis_old_error_handler - Probe specified port on PATA host controller
- *	@ap: Port to probe
- *
- *	LOCKING:
- *	None (inherited from caller).
- */
-
-static void sis_old_error_handler(struct ata_port *ap)
-{
-	ata_bmdma_drive_eh(ap, sis_old_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
+	ata_bmdma_drive_eh(ap, sis_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
 }
 
 /**
@@ -494,7 +439,7 @@
 	int drive_pci = sis_port_base(adev);
 	u16 timing;
 
-	const u16 udma_bits[]  = { 0x8F00, 0x8A00, 0x8700, 0x8500, 0x8300, 0x8200, 0x8100};
+	static const u16 udma_bits[]  = { 0x8F00, 0x8A00, 0x8700, 0x8500, 0x8300, 0x8200, 0x8100};
 
 	pci_read_config_word(pdev, drive_pci, &timing);
 
@@ -531,8 +476,8 @@
 	u32 reg54;
 
 	/* bits 4- cycle time 8 - cvs time */
-	const u32 timing_u100[] = { 0x6B0, 0x470, 0x350, 0x140, 0x120, 0x110, 0x000 };
-	const u32 timing_u133[] = { 0x9F0, 0x6A0, 0x470, 0x250, 0x230, 0x220, 0x210 };
+	static const u32 timing_u100[] = { 0x6B0, 0x470, 0x350, 0x140, 0x120, 0x110, 0x000 };
+	static const u32 timing_u133[] = { 0x9F0, 0x6A0, 0x470, 0x250, 0x230, 0x220, 0x210 };
 
 	/* If bit 14 is set then the registers are mapped at 0x70 not 0x40 */
 	pci_read_config_dword(pdev, 0x54, &reg54);
@@ -595,8 +540,9 @@
 
 	.freeze			= ata_bmdma_freeze,
 	.thaw			= ata_bmdma_thaw,
-	.error_handler		= sis_133_error_handler,
+	.error_handler		= sis_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
+	.cable_detect		= sis_133_cable_detect,
 
 	.bmdma_setup		= ata_bmdma_setup,
 	.bmdma_start		= ata_bmdma_start,
@@ -628,8 +574,9 @@
 
 	.freeze			= ata_bmdma_freeze,
 	.thaw			= ata_bmdma_thaw,
-	.error_handler		= sis_66_error_handler,
+	.error_handler		= sis_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
+	.cable_detect		= sis_66_cable_detect,
 
 	.bmdma_setup		= ata_bmdma_setup,
 	.bmdma_start		= ata_bmdma_start,
@@ -661,9 +608,9 @@
 
 	.freeze			= ata_bmdma_freeze,
 	.thaw			= ata_bmdma_thaw,
-	.error_handler		= sis_66_error_handler,
+	.error_handler		= sis_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
-
+	.cable_detect		= sis_66_cable_detect,
 
 	.bmdma_setup		= ata_bmdma_setup,
 	.bmdma_start		= ata_bmdma_start,
@@ -692,10 +639,11 @@
 	.check_status		= ata_check_status,
 	.exec_command		= ata_exec_command,
 	.dev_select		= ata_std_dev_select,
+	.cable_detect		= sis_66_cable_detect,
 
 	.freeze			= ata_bmdma_freeze,
 	.thaw			= ata_bmdma_thaw,
-	.error_handler		= sis_66_error_handler,
+	.error_handler		= sis_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
 
 	.bmdma_setup		= ata_bmdma_setup,
@@ -728,8 +676,9 @@
 
 	.freeze			= ata_bmdma_freeze,
 	.thaw			= ata_bmdma_thaw,
-	.error_handler		= sis_old_error_handler,
+	.error_handler		= sis_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
+	.cable_detect		= ata_cable_40wire,
 
 	.bmdma_setup		= ata_bmdma_setup,
 	.bmdma_start		= ata_bmdma_start,
diff --git a/drivers/ata/pata_sl82c105.c b/drivers/ata/pata_sl82c105.c
index b681441..da9e22b 100644
--- a/drivers/ata/pata_sl82c105.c
+++ b/drivers/ata/pata_sl82c105.c
@@ -58,7 +58,6 @@
 
 	if (ap->port_no && !pci_test_config_bits(pdev, &sl82c105_enable_bits[ap->port_no]))
 		return -ENOENT;
-	ap->cbl = ATA_CBL_PATA40;
 	return ata_std_prereset(ap);
 }
 
@@ -238,6 +237,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= sl82c105_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= sl82c105_bmdma_start,
diff --git a/drivers/ata/pata_triflex.c b/drivers/ata/pata_triflex.c
index 71418f2..e618ffd 100644
--- a/drivers/ata/pata_triflex.c
+++ b/drivers/ata/pata_triflex.c
@@ -43,7 +43,7 @@
 #include <linux/libata.h>
 
 #define DRV_NAME "pata_triflex"
-#define DRV_VERSION "0.2.7"
+#define DRV_VERSION "0.2.8"
 
 /**
  *	triflex_prereset		-	probe begin
@@ -63,7 +63,6 @@
 
 	if (!pci_test_config_bits(pdev, &triflex_enable_bits[ap->port_no]))
 		return -ENOENT;
-	ap->cbl = ATA_CBL_PATA40;
 	return ata_std_prereset(ap);
 }
 
@@ -214,6 +213,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= triflex_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= triflex_bmdma_start,
diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c
index 946ade0..96b7179 100644
--- a/drivers/ata/pata_via.c
+++ b/drivers/ata/pata_via.c
@@ -62,7 +62,7 @@
 #include <linux/libata.h>
 
 #define DRV_NAME "pata_via"
-#define DRV_VERSION "0.2.1"
+#define DRV_VERSION "0.3.1"
 
 /*
  *	The following comes directly from Vojtech Pavlik's ide/pci/via82cxxx
@@ -135,16 +135,23 @@
  */
 
 static int via_cable_detect(struct ata_port *ap) {
+	const struct via_isa_bridge *config = ap->host->private_data;
 	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
 	u32 ata66;
 
+	/* Early chips are 40 wire */
+	if ((config->flags & VIA_UDMA) < VIA_UDMA_66)
+		return ATA_CBL_PATA40;
+	/* UDMA 66 chips have only drive side logic */
+	else if((config->flags & VIA_UDMA) < VIA_UDMA_100)
+		return ATA_CBL_PATA_UNK;
+	/* UDMA 100 or later */
 	pci_read_config_dword(pdev, 0x50, &ata66);
 	/* Check both the drive cable reporting bits, we might not have
 	   two drives */
 	if (ata66 & (0x10100000 >> (16 * ap->port_no)))
 		return ATA_CBL_PATA80;
-	else
-		return ATA_CBL_PATA40;
+	return ATA_CBL_PATA40;
 }
 
 static int via_pre_reset(struct ata_port *ap)
@@ -156,22 +163,10 @@
 			{ 0x40, 1, 0x02, 0x02 },
 			{ 0x40, 1, 0x01, 0x01 }
 		};
-
 		struct pci_dev *pdev = to_pci_dev(ap->host->dev);
-
 		if (!pci_test_config_bits(pdev, &via_enable_bits[ap->port_no]))
 			return -ENOENT;
 	}
-
-	if ((config->flags & VIA_UDMA) >= VIA_UDMA_100)
-		ap->cbl = via_cable_detect(ap);
-	/* The UDMA66 series has no cable detect so do drive side detect */
-	else if ((config->flags & VIA_UDMA) < VIA_UDMA_66)
-		ap->cbl = ATA_CBL_PATA40;
-	else
-		ap->cbl = ATA_CBL_PATA_UNK;
-
-
 	return ata_std_prereset(ap);
 }
 
@@ -327,6 +322,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= via_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= via_cable_detect,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
@@ -362,6 +358,7 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= via_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= via_cable_detect,
 
 	.bmdma_setup 	= ata_bmdma_setup,
 	.bmdma_start 	= ata_bmdma_start,
diff --git a/drivers/ata/pata_winbond.c b/drivers/ata/pata_winbond.c
index 6c11103..cc4ad27 100644
--- a/drivers/ata/pata_winbond.c
+++ b/drivers/ata/pata_winbond.c
@@ -8,7 +8,6 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/pci.h>
 #include <linux/init.h>
 #include <linux/blkdev.h>
 #include <linux/delay.h>
@@ -36,7 +35,7 @@
 static int probe_winbond;
 #endif
 
-static spinlock_t winbond_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(winbond_lock);
 
 static void winbond_writecfg(unsigned long port, u8 reg, u8 val)
 {
@@ -152,13 +151,13 @@
 	.thaw		= ata_bmdma_thaw,
 	.error_handler	= ata_bmdma_error_handler,
 	.post_internal_cmd = ata_bmdma_post_internal_cmd,
+	.cable_detect	= ata_cable_40wire,
 
 	.qc_prep 	= ata_qc_prep,
 	.qc_issue	= ata_qc_issue_prot,
 
 	.data_xfer	= winbond_data_xfer,
 
-	.irq_handler	= ata_interrupt,
 	.irq_clear	= ata_bmdma_irq_clear,
 	.irq_on		= ata_irq_on,
 	.irq_ack	= ata_irq_ack,
@@ -179,11 +178,9 @@
 
 static __init int winbond_init_one(unsigned long port)
 {
-	struct ata_probe_ent ae;
 	struct platform_device *pdev;
-	int ret;
 	u8 reg;
-	int i;
+	int i, rc;
 
 	reg = winbond_readcfg(port, 0x81);
 	reg |= 0x80;	/* jumpered mode off */
@@ -202,58 +199,56 @@
 
 	for (i = 0; i < 2 ; i ++) {
 		unsigned long cmd_port = 0x1F0 - (0x80 * i);
+		struct ata_host *host;
+		struct ata_port *ap;
 		void __iomem *cmd_addr, *ctl_addr;
 
-		if (reg & (1 << i)) {
-			/*
-			 *	Fill in a probe structure first of all
-			 */
+		if (!(reg & (1 << i)))
+			continue;
 
-			pdev = platform_device_register_simple(DRV_NAME, nr_winbond_host, NULL, 0);
-			if (IS_ERR(pdev))
-				return PTR_ERR(pdev);
+		pdev = platform_device_register_simple(DRV_NAME, nr_winbond_host, NULL, 0);
+		if (IS_ERR(pdev))
+			return PTR_ERR(pdev);
 
-			cmd_addr = devm_ioport_map(&pdev->dev, cmd_port, 8);
-			ctl_addr = devm_ioport_map(&pdev->dev, cmd_port + 0x0206, 1);
-			if (!cmd_addr || !ctl_addr) {
-				platform_device_unregister(pdev);
-				return -ENOMEM;
-			}
+		rc = -ENOMEM;
+		host = ata_host_alloc(&pdev->dev, 1);
+		if (!host)
+			goto err_unregister;
 
-			memset(&ae, 0, sizeof(struct ata_probe_ent));
-			INIT_LIST_HEAD(&ae.node);
-			ae.dev = &pdev->dev;
+		rc = -ENOMEM;
+		cmd_addr = devm_ioport_map(&pdev->dev, cmd_port, 8);
+		ctl_addr = devm_ioport_map(&pdev->dev, cmd_port + 0x0206, 1);
+		if (!cmd_addr || !ctl_addr)
+			goto err_unregister;
 
-			ae.port_ops = &winbond_port_ops;
-			ae.pio_mask = 0x1F;
+		ap = host->ports[0];
+		ap->ops = &winbond_port_ops;
+		ap->pio_mask = 0x1F;
+		ap->flags |= ATA_FLAG_SLAVE_POSS;
+		ap->ioaddr.cmd_addr = cmd_addr;
+		ap->ioaddr.altstatus_addr = ctl_addr;
+		ap->ioaddr.ctl_addr = ctl_addr;
+		ata_std_ports(&ap->ioaddr);
 
-			ae.sht = &winbond_sht;
+		/* hook in a private data structure per channel */
+		host->private_data = &winbond_data[nr_winbond_host];
+		winbond_data[nr_winbond_host].config = port;
+		winbond_data[nr_winbond_host].platform_dev = pdev;
 
-			ae.n_ports = 1;
-			ae.irq = 14 + i;
-			ae.irq_flags = 0;
-			ae.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST;
-			ae.port[0].cmd_addr = cmd_addr;
-			ae.port[0].altstatus_addr = ctl_addr;
-			ae.port[0].ctl_addr = ctl_addr;
-			ata_std_ports(&ae.port[0]);
-			/*
-			 *	Hook in a private data structure per channel
-			 */
-			ae.private_data = &winbond_data[nr_winbond_host];
-			winbond_data[nr_winbond_host].config = port;
-			winbond_data[nr_winbond_host].platform_dev = pdev;
+		/* activate */
+		rc = ata_host_activate(host, 14 + i, ata_interrupt, 0,
+				       &winbond_sht);
+		if (rc)
+			goto err_unregister;
 
-			ret = ata_device_add(&ae);
-			if (ret == 0) {
-				platform_device_unregister(pdev);
-				return -ENODEV;
-			}
-			winbond_host[nr_winbond_host++] = dev_get_drvdata(&pdev->dev);
-		}
+		winbond_host[nr_winbond_host++] = dev_get_drvdata(&pdev->dev);
 	}
 
 	return 0;
+
+ err_unregister:
+	platform_device_unregister(pdev);
+	return rc;
 }
 
 /**
diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c
index 5dd3ca8..52b6953 100644
--- a/drivers/ata/pdc_adma.c
+++ b/drivers/ata/pdc_adma.c
@@ -52,9 +52,9 @@
 /* macro to calculate base address for ADMA regs */
 #define ADMA_REGS(base,port_no)		((base) + 0x80 + ((port_no) * 0x20))
 
-/* macro to obtain addresses from ata_host */
-#define ADMA_HOST_REGS(host,port_no) \
-	ADMA_REGS((host)->iomap[ADMA_MMIO_BAR], port_no)
+/* macro to obtain addresses from ata_port */
+#define ADMA_PORT_REGS(ap) \
+	ADMA_REGS((ap)->host->iomap[ADMA_MMIO_BAR], ap->port_no)
 
 enum {
 	ADMA_MMIO_BAR		= 4,
@@ -128,7 +128,6 @@
 
 static int adma_ata_init_one (struct pci_dev *pdev,
 				const struct pci_device_id *ent);
-static irqreturn_t adma_intr (int irq, void *dev_instance);
 static int adma_port_start(struct ata_port *ap);
 static void adma_host_stop(struct ata_host *host);
 static void adma_port_stop(struct ata_port *ap);
@@ -172,7 +171,6 @@
 	.qc_issue		= adma_qc_issue,
 	.eng_timeout		= adma_eng_timeout,
 	.data_xfer		= ata_data_xfer,
-	.irq_handler		= adma_intr,
 	.irq_clear		= adma_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -186,7 +184,6 @@
 static struct ata_port_info adma_port_info[] = {
 	/* board_1841_idx */
 	{
-		.sht		= &adma_ata_sht,
 		.flags		= ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST |
 				  ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO |
 				  ATA_FLAG_PIO_POLLING,
@@ -229,8 +226,10 @@
 	/* nothing */
 }
 
-static void adma_reset_engine(void __iomem *chan)
+static void adma_reset_engine(struct ata_port *ap)
 {
+	void __iomem *chan = ADMA_PORT_REGS(ap);
+
 	/* reset ADMA to idle state */
 	writew(aPIOMD4 | aNIEN | aRSTADM, chan + ADMA_CONTROL);
 	udelay(2);
@@ -241,14 +240,14 @@
 static void adma_reinit_engine(struct ata_port *ap)
 {
 	struct adma_port_priv *pp = ap->private_data;
-	void __iomem *chan = ADMA_HOST_REGS(ap->host, ap->port_no);
+	void __iomem *chan = ADMA_PORT_REGS(ap);
 
 	/* mask/clear ATA interrupts */
 	writeb(ATA_NIEN, ap->ioaddr.ctl_addr);
 	ata_check_status(ap);
 
 	/* reset the ADMA engine */
-	adma_reset_engine(chan);
+	adma_reset_engine(ap);
 
 	/* set in-FIFO threshold to 0x100 */
 	writew(0x100, chan + ADMA_FIFO_IN);
@@ -268,7 +267,7 @@
 
 static inline void adma_enter_reg_mode(struct ata_port *ap)
 {
-	void __iomem *chan = ADMA_HOST_REGS(ap->host, ap->port_no);
+	void __iomem *chan = ADMA_PORT_REGS(ap);
 
 	writew(aPIOMD4, chan + ADMA_CONTROL);
 	readb(chan + ADMA_STATUS);	/* flush */
@@ -415,7 +414,7 @@
 static inline void adma_packet_start(struct ata_queued_cmd *qc)
 {
 	struct ata_port *ap = qc->ap;
-	void __iomem *chan = ADMA_HOST_REGS(ap->host, ap->port_no);
+	void __iomem *chan = ADMA_PORT_REGS(ap);
 
 	VPRINTK("ENTER, ap %p\n", ap);
 
@@ -453,7 +452,7 @@
 		struct ata_port *ap = host->ports[port_no];
 		struct adma_port_priv *pp;
 		struct ata_queued_cmd *qc;
-		void __iomem *chan = ADMA_HOST_REGS(host, port_no);
+		void __iomem *chan = ADMA_PORT_REGS(ap);
 		u8 status = readb(chan + ADMA_STATUS);
 
 		if (status == 0)
@@ -575,7 +574,7 @@
 
 static void adma_port_stop(struct ata_port *ap)
 {
-	adma_reset_engine(ADMA_HOST_REGS(ap->host, ap->port_no));
+	adma_reset_engine(ap);
 }
 
 static void adma_host_stop(struct ata_host *host)
@@ -583,21 +582,19 @@
 	unsigned int port_no;
 
 	for (port_no = 0; port_no < ADMA_PORTS; ++port_no)
-		adma_reset_engine(ADMA_HOST_REGS(host, port_no));
+		adma_reset_engine(host->ports[port_no]);
 }
 
-static void adma_host_init(unsigned int chip_id,
-				struct ata_probe_ent *probe_ent)
+static void adma_host_init(struct ata_host *host, unsigned int chip_id)
 {
 	unsigned int port_no;
-	void __iomem *mmio_base = probe_ent->iomap[ADMA_MMIO_BAR];
 
 	/* enable/lock aGO operation */
-	writeb(7, mmio_base + ADMA_MODE_LOCK);
+	writeb(7, host->iomap[ADMA_MMIO_BAR] + ADMA_MODE_LOCK);
 
 	/* reset the ADMA logic */
 	for (port_no = 0; port_no < ADMA_PORTS; ++port_no)
-		adma_reset_engine(ADMA_REGS(mmio_base, port_no));
+		adma_reset_engine(host->ports[port_no]);
 }
 
 static int adma_set_dma_masks(struct pci_dev *pdev, void __iomem *mmio_base)
@@ -623,14 +620,21 @@
 			     const struct pci_device_id *ent)
 {
 	static int printed_version;
-	struct ata_probe_ent *probe_ent = NULL;
-	void __iomem *mmio_base;
 	unsigned int board_idx = (unsigned int) ent->driver_data;
+	const struct ata_port_info *ppi[] = { &adma_port_info[board_idx], NULL };
+	struct ata_host *host;
+	void __iomem *mmio_base;
 	int rc, port_no;
 
 	if (!printed_version++)
 		dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
 
+	/* alloc host */
+	host = ata_host_alloc_pinfo(&pdev->dev, ppi, ADMA_PORTS);
+	if (!host)
+		return -ENOMEM;
+
+	/* acquire resources and fill host */
 	rc = pcim_enable_device(pdev);
 	if (rc)
 		return rc;
@@ -641,46 +645,23 @@
 	rc = pcim_iomap_regions(pdev, 1 << ADMA_MMIO_BAR, DRV_NAME);
 	if (rc)
 		return rc;
-	mmio_base = pcim_iomap_table(pdev)[ADMA_MMIO_BAR];
+	host->iomap = pcim_iomap_table(pdev);
+	mmio_base = host->iomap[ADMA_MMIO_BAR];
 
 	rc = adma_set_dma_masks(pdev, mmio_base);
 	if (rc)
 		return rc;
 
-	probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL);
-	if (probe_ent == NULL)
-		return -ENOMEM;
-
-	probe_ent->dev = pci_dev_to_dev(pdev);
-	INIT_LIST_HEAD(&probe_ent->node);
-
-	probe_ent->sht		= adma_port_info[board_idx].sht;
-	probe_ent->port_flags	= adma_port_info[board_idx].flags;
-	probe_ent->pio_mask	= adma_port_info[board_idx].pio_mask;
-	probe_ent->mwdma_mask	= adma_port_info[board_idx].mwdma_mask;
-	probe_ent->udma_mask	= adma_port_info[board_idx].udma_mask;
-	probe_ent->port_ops	= adma_port_info[board_idx].port_ops;
-
-	probe_ent->irq		= pdev->irq;
-	probe_ent->irq_flags	= IRQF_SHARED;
-	probe_ent->n_ports	= ADMA_PORTS;
-	probe_ent->iomap	= pcim_iomap_table(pdev);
-
-	for (port_no = 0; port_no < probe_ent->n_ports; ++port_no) {
-		adma_ata_setup_port(&probe_ent->port[port_no],
+	for (port_no = 0; port_no < ADMA_PORTS; ++port_no)
+		adma_ata_setup_port(&host->ports[port_no]->ioaddr,
 				    ADMA_ATA_REGS(mmio_base, port_no));
-	}
-
-	pci_set_master(pdev);
 
 	/* initialize adapter */
-	adma_host_init(board_idx, probe_ent);
+	adma_host_init(host, board_idx);
 
-	if (!ata_device_add(probe_ent))
-		return -ENODEV;
-
-	devm_kfree(&pdev->dev, probe_ent);
-	return 0;
+	pci_set_master(pdev);
+	return ata_host_activate(host, pdev->irq, adma_intr, IRQF_SHARED,
+				 &adma_ata_sht);
 }
 
 static int __init adma_ata_init(void)
diff --git a/drivers/ata/sata_inic162x.c b/drivers/ata/sata_inic162x.c
index 1e21688..f099a1d 100644
--- a/drivers/ata/sata_inic162x.c
+++ b/drivers/ata/sata_inic162x.c
@@ -488,11 +488,11 @@
 static void inic_post_internal_cmd(struct ata_queued_cmd *qc)
 {
 	/* make DMA engine forget about the failed command */
-	if (qc->err_mask)
+	if (qc->flags & ATA_QCFLAG_FAILED)
 		inic_reset_port(inic_port_base(qc->ap));
 }
 
-static void inic_dev_config(struct ata_port *ap, struct ata_device *dev)
+static void inic_dev_config(struct ata_device *dev)
 {
 	/* inic can only handle upto LBA28 max sectors */
 	if (dev->max_sectors > ATA_MAX_SECTORS)
@@ -559,7 +559,6 @@
 	.bmdma_stop		= inic_bmdma_stop,
 	.bmdma_status		= inic_bmdma_status,
 
-	.irq_handler		= inic_interrupt,
 	.irq_clear		= inic_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -580,7 +579,6 @@
 };
 
 static struct ata_port_info inic_port_info = {
-	.sht			= &inic_sht,
 	/* For some reason, ATA_PROT_ATAPI is broken on this
 	 * controller, and no, PIO_POLLING does't fix it.  It somehow
 	 * manages to report the wrong ireason and ignoring ireason
@@ -642,7 +640,9 @@
 	void __iomem *mmio_base = host->iomap[MMIO_BAR];
 	int rc;
 
-	ata_pci_device_do_resume(pdev);
+	rc = ata_pci_device_do_resume(pdev);
+	if (rc)
+		return rc;
 
 	if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
 		rc = init_controller(mmio_base, hpriv->cached_hctl);
@@ -659,8 +659,8 @@
 static int inic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	static int printed_version;
-	struct ata_port_info *pinfo = &inic_port_info;
-	struct ata_probe_ent *probe_ent;
+	const struct ata_port_info *ppi[] = { &inic_port_info, NULL };
+	struct ata_host *host;
 	struct inic_host_priv *hpriv;
 	void __iomem * const *iomap;
 	int i, rc;
@@ -668,6 +668,15 @@
 	if (!printed_version++)
 		dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
 
+	/* alloc host */
+	host = ata_host_alloc_pinfo(&pdev->dev, ppi, NR_PORTS);
+	hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL);
+	if (!host || !hpriv)
+		return -ENOMEM;
+
+	host->private_data = hpriv;
+
+	/* acquire resources and fill host */
 	rc = pcim_enable_device(pdev);
 	if (rc)
 		return rc;
@@ -675,7 +684,22 @@
 	rc = pcim_iomap_regions(pdev, 0x3f, DRV_NAME);
 	if (rc)
 		return rc;
-	iomap = pcim_iomap_table(pdev);
+	host->iomap = iomap = pcim_iomap_table(pdev);
+
+	for (i = 0; i < NR_PORTS; i++) {
+		struct ata_ioports *port = &host->ports[i]->ioaddr;
+		void __iomem *port_base = iomap[MMIO_BAR] + i * PORT_SIZE;
+
+		port->cmd_addr = iomap[2 * i];
+		port->altstatus_addr =
+		port->ctl_addr = (void __iomem *)
+			((unsigned long)iomap[2 * i + 1] | ATA_PCI_CTL_OFS);
+		port->scr_addr = port_base + PORT_SCR;
+
+		ata_std_ports(port);
+	}
+
+	hpriv->cached_hctl = readw(iomap[MMIO_BAR] + HOST_CTL);
 
 	/* Set dma_mask.  This devices doesn't support 64bit addressing. */
 	rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
@@ -692,43 +716,6 @@
 		return rc;
 	}
 
-	probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL);
-	hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL);
-	if (!probe_ent || !hpriv)
-		return -ENOMEM;
-
-	probe_ent->dev = &pdev->dev;
-	INIT_LIST_HEAD(&probe_ent->node);
-
-	probe_ent->sht			= pinfo->sht;
-	probe_ent->port_flags		= pinfo->flags;
-	probe_ent->pio_mask		= pinfo->pio_mask;
-	probe_ent->mwdma_mask		= pinfo->mwdma_mask;
-	probe_ent->udma_mask		= pinfo->udma_mask;
-	probe_ent->port_ops		= pinfo->port_ops;
-	probe_ent->n_ports		= NR_PORTS;
-
-	probe_ent->irq = pdev->irq;
-	probe_ent->irq_flags = IRQF_SHARED;
-
-	probe_ent->iomap = iomap;
-
-	for (i = 0; i < NR_PORTS; i++) {
-		struct ata_ioports *port = &probe_ent->port[i];
-		void __iomem *port_base = iomap[MMIO_BAR] + i * PORT_SIZE;
-
-		port->cmd_addr = iomap[2 * i];
-		port->altstatus_addr =
-		port->ctl_addr = (void __iomem *)
-			((unsigned long)iomap[2 * i + 1] | ATA_PCI_CTL_OFS);
-		port->scr_addr = port_base + PORT_SCR;
-
-		ata_std_ports(port);
-	}
-
-	probe_ent->private_data = hpriv;
-	hpriv->cached_hctl = readw(iomap[MMIO_BAR] + HOST_CTL);
-
 	rc = init_controller(iomap[MMIO_BAR], hpriv->cached_hctl);
 	if (rc) {
 		dev_printk(KERN_ERR, &pdev->dev,
@@ -737,13 +724,8 @@
 	}
 
 	pci_set_master(pdev);
-
-	if (!ata_device_add(probe_ent))
-		return -ENODEV;
-
-	devm_kfree(&pdev->dev, probe_ent);
-
-	return 0;
+	return ata_host_activate(host, pdev->irq, inic_interrupt, IRQF_SHARED,
+				 &inic_sht);
 }
 
 static const struct pci_device_id inic_pci_tbl[] = {
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index a65ba63..cb9b9ac 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -253,10 +253,7 @@
 #define IS_GEN_IIE(hpriv) ((hpriv)->hp_flags & MV_HP_GEN_IIE)
 
 enum {
-	/* Our DMA boundary is determined by an ePRD being unable to handle
-	 * anything larger than 64KB
-	 */
-	MV_DMA_BOUNDARY		= 0xffffU,
+	MV_DMA_BOUNDARY		= 0xffffffffU,
 
 	EDMA_REQ_Q_BASE_LO_MASK	= 0xfffffc00U,
 
@@ -350,7 +347,6 @@
 static void mv_qc_prep(struct ata_queued_cmd *qc);
 static void mv_qc_prep_iie(struct ata_queued_cmd *qc);
 static unsigned int mv_qc_issue(struct ata_queued_cmd *qc);
-static irqreturn_t mv_interrupt(int irq, void *dev_instance);
 static void mv_eng_timeout(struct ata_port *ap);
 static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
 
@@ -384,10 +380,10 @@
 	.queuecommand		= ata_scsi_queuecmd,
 	.can_queue		= MV_USE_Q_DEPTH,
 	.this_id		= ATA_SHT_THIS_ID,
-	.sg_tablesize		= MV_MAX_SG_CT / 2,
+	.sg_tablesize		= MV_MAX_SG_CT,
 	.cmd_per_lun		= ATA_SHT_CMD_PER_LUN,
 	.emulated		= ATA_SHT_EMULATED,
-	.use_clustering		= ATA_SHT_USE_CLUSTERING,
+	.use_clustering		= 1,
 	.proc_name		= DRV_NAME,
 	.dma_boundary		= MV_DMA_BOUNDARY,
 	.slave_configure	= ata_scsi_slave_config,
@@ -405,6 +401,7 @@
 	.dev_select		= ata_std_dev_select,
 
 	.phy_reset		= mv_phy_reset,
+	.cable_detect		= ata_cable_sata,
 
 	.qc_prep		= mv_qc_prep,
 	.qc_issue		= mv_qc_issue,
@@ -412,7 +409,6 @@
 
 	.eng_timeout		= mv_eng_timeout,
 
-	.irq_handler		= mv_interrupt,
 	.irq_clear		= mv_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -434,6 +430,7 @@
 	.dev_select		= ata_std_dev_select,
 
 	.phy_reset		= mv_phy_reset,
+	.cable_detect		= ata_cable_sata,
 
 	.qc_prep		= mv_qc_prep,
 	.qc_issue		= mv_qc_issue,
@@ -441,7 +438,6 @@
 
 	.eng_timeout		= mv_eng_timeout,
 
-	.irq_handler		= mv_interrupt,
 	.irq_clear		= mv_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -463,6 +459,7 @@
 	.dev_select		= ata_std_dev_select,
 
 	.phy_reset		= mv_phy_reset,
+	.cable_detect		= ata_cable_sata,
 
 	.qc_prep		= mv_qc_prep_iie,
 	.qc_issue		= mv_qc_issue,
@@ -470,7 +467,6 @@
 
 	.eng_timeout		= mv_eng_timeout,
 
-	.irq_handler		= mv_interrupt,
 	.irq_clear		= mv_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -484,35 +480,30 @@
 
 static const struct ata_port_info mv_port_info[] = {
 	{  /* chip_504x */
-		.sht		= &mv_sht,
 		.flags		= MV_COMMON_FLAGS,
 		.pio_mask	= 0x1f,	/* pio0-4 */
 		.udma_mask	= 0x7f,	/* udma0-6 */
 		.port_ops	= &mv5_ops,
 	},
 	{  /* chip_508x */
-		.sht		= &mv_sht,
 		.flags		= (MV_COMMON_FLAGS | MV_FLAG_DUAL_HC),
 		.pio_mask	= 0x1f,	/* pio0-4 */
 		.udma_mask	= 0x7f,	/* udma0-6 */
 		.port_ops	= &mv5_ops,
 	},
 	{  /* chip_5080 */
-		.sht		= &mv_sht,
 		.flags		= (MV_COMMON_FLAGS | MV_FLAG_DUAL_HC),
 		.pio_mask	= 0x1f,	/* pio0-4 */
 		.udma_mask	= 0x7f,	/* udma0-6 */
 		.port_ops	= &mv5_ops,
 	},
 	{  /* chip_604x */
-		.sht		= &mv_sht,
 		.flags		= (MV_COMMON_FLAGS | MV_6XXX_FLAGS),
 		.pio_mask	= 0x1f,	/* pio0-4 */
 		.udma_mask	= 0x7f,	/* udma0-6 */
 		.port_ops	= &mv6_ops,
 	},
 	{  /* chip_608x */
-		.sht		= &mv_sht,
 		.flags		= (MV_COMMON_FLAGS | MV_6XXX_FLAGS |
 				   MV_FLAG_DUAL_HC),
 		.pio_mask	= 0x1f,	/* pio0-4 */
@@ -520,14 +511,12 @@
 		.port_ops	= &mv6_ops,
 	},
 	{  /* chip_6042 */
-		.sht		= &mv_sht,
 		.flags		= (MV_COMMON_FLAGS | MV_6XXX_FLAGS),
 		.pio_mask	= 0x1f,	/* pio0-4 */
 		.udma_mask	= 0x7f,	/* udma0-6 */
 		.port_ops	= &mv_iie_ops,
 	},
 	{  /* chip_7042 */
-		.sht		= &mv_sht,
 		.flags		= (MV_COMMON_FLAGS | MV_6XXX_FLAGS),
 		.pio_mask	= 0x1f,	/* pio0-4 */
 		.udma_mask	= 0x7f,	/* udma0-6 */
@@ -551,6 +540,9 @@
 
 	{ PCI_VDEVICE(TTI, 0x2310), chip_7042 },
 
+	/* add Marvell 7042 support */
+	{ PCI_VDEVICE(MARVELL, 0x7042), chip_7042 },
+
 	{ }			/* terminate list */
 };
 
@@ -585,6 +577,39 @@
 static int msi;	      /* Use PCI msi; either zero (off, default) or non-zero */
 
 
+/* move to PCI layer or libata core? */
+static int pci_go_64(struct pci_dev *pdev)
+{
+	int rc;
+
+	if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) {
+		rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);
+		if (rc) {
+			rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+			if (rc) {
+				dev_printk(KERN_ERR, &pdev->dev,
+					   "64-bit DMA enable failed\n");
+				return rc;
+			}
+		}
+	} else {
+		rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+		if (rc) {
+			dev_printk(KERN_ERR, &pdev->dev,
+				   "32-bit DMA enable failed\n");
+			return rc;
+		}
+		rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+		if (rc) {
+			dev_printk(KERN_ERR, &pdev->dev,
+				   "32-bit consistent DMA enable failed\n");
+			return rc;
+		}
+	}
+
+	return rc;
+}
+
 /*
  * Functions
  */
@@ -798,20 +823,18 @@
 {
 	unsigned int ofs = mv_scr_offset(sc_reg_in);
 
-	if (0xffffffffU != ofs) {
+	if (0xffffffffU != ofs)
 		return readl(mv_ap_base(ap) + ofs);
-	} else {
+	else
 		return (u32) ofs;
-	}
 }
 
 static void mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val)
 {
 	unsigned int ofs = mv_scr_offset(sc_reg_in);
 
-	if (0xffffffffU != ofs) {
+	if (0xffffffffU != ofs)
 		writelfl(val, mv_ap_base(ap) + ofs);
-	}
 }
 
 static void mv_edma_cfg(struct mv_host_priv *hpriv, void __iomem *port_mmio)
@@ -959,38 +982,30 @@
  *      LOCKING:
  *      Inherited from caller.
  */
-static void mv_fill_sg(struct ata_queued_cmd *qc)
+static unsigned int mv_fill_sg(struct ata_queued_cmd *qc)
 {
 	struct mv_port_priv *pp = qc->ap->private_data;
-	unsigned int i = 0;
+	unsigned int n_sg = 0;
 	struct scatterlist *sg;
+	struct mv_sg *mv_sg;
 
+	mv_sg = pp->sg_tbl;
 	ata_for_each_sg(sg, qc) {
-		dma_addr_t addr;
-		u32 sg_len, len, offset;
+		dma_addr_t addr = sg_dma_address(sg);
+		u32 sg_len = sg_dma_len(sg);
 
-		addr = sg_dma_address(sg);
-		sg_len = sg_dma_len(sg);
+		mv_sg->addr = cpu_to_le32(addr & 0xffffffff);
+		mv_sg->addr_hi = cpu_to_le32((addr >> 16) >> 16);
+		mv_sg->flags_size = cpu_to_le32(sg_len & 0xffff);
 
-		while (sg_len) {
-			offset = addr & MV_DMA_BOUNDARY;
-			len = sg_len;
-			if ((offset + sg_len) > 0x10000)
-				len = 0x10000 - offset;
+		if (ata_sg_is_last(sg, qc))
+			mv_sg->flags_size |= cpu_to_le32(EPRD_FLAG_END_OF_TBL);
 
-			pp->sg_tbl[i].addr = cpu_to_le32(addr & 0xffffffff);
-			pp->sg_tbl[i].addr_hi = cpu_to_le32((addr >> 16) >> 16);
-			pp->sg_tbl[i].flags_size = cpu_to_le32(len & 0xffff);
-
-			sg_len -= len;
-			addr += len;
-
-			if (!sg_len && ata_sg_is_last(sg, qc))
-				pp->sg_tbl[i].flags_size |= cpu_to_le32(EPRD_FLAG_END_OF_TBL);
-
-			i++;
-		}
+		mv_sg++;
+		n_sg++;
 	}
+
+	return n_sg;
 }
 
 static inline unsigned mv_inc_q_index(unsigned index)
@@ -1320,17 +1335,15 @@
 	int shift, port, port0, hard_port, handled;
 	unsigned int err_mask;
 
-	if (hc == 0) {
+	if (hc == 0)
 		port0 = 0;
-	} else {
+	else
 		port0 = MV_PORTS_PER_HC;
-	}
 
 	/* we'll need the HC success int register in most cases */
 	hc_irq_cause = readl(hc_mmio + HC_IRQ_CAUSE_OFS);
-	if (hc_irq_cause) {
+	if (hc_irq_cause)
 		writelfl(~hc_irq_cause, hc_mmio + HC_IRQ_CAUSE_OFS);
-	}
 
 	VPRINTK("ENTER, hc%u relevant=0x%08x HC IRQ cause=0x%08x\n",
 		hc,relevant,hc_irq_cause);
@@ -1425,9 +1438,8 @@
 	/* check the cases where we either have nothing pending or have read
 	 * a bogus register value which can indicate HW removal or PCI fault
 	 */
-	if (!irq_stat || (0xffffffffU == irq_stat)) {
+	if (!irq_stat || (0xffffffffU == irq_stat))
 		return IRQ_NONE;
-	}
 
 	n_hcs = mv_get_hc_count(host->ports[0]->flags);
 	spin_lock(&host->lock);
@@ -1952,7 +1964,6 @@
 		ata_port_disable(ap);
 		return;
 	}
-	ap->cbl = ATA_CBL_SATA;
 
 	/* even after SStatus reflects that device is ready,
 	 * it seems to take a while for link to be fully
@@ -2077,9 +2088,10 @@
 		readl(port_mmio + EDMA_ERR_IRQ_MASK_OFS));
 }
 
-static int mv_chip_id(struct pci_dev *pdev, struct mv_host_priv *hpriv,
-		      unsigned int board_idx)
+static int mv_chip_id(struct ata_host *host, unsigned int board_idx)
 {
+	struct pci_dev *pdev = to_pci_dev(host->dev);
+	struct mv_host_priv *hpriv = host->private_data;
 	u8 rev_id;
 	u32 hp_flags = hpriv->hp_flags;
 
@@ -2177,8 +2189,8 @@
 
 /**
  *      mv_init_host - Perform some early initialization of the host.
- *	@pdev: host PCI device
- *      @probe_ent: early data struct representing the host
+ *	@host: ATA host to initialize
+ *      @board_idx: controller index
  *
  *      If possible, do an early global reset of the host.  Then do
  *      our port init and clear/unmask all/relevant host interrupts.
@@ -2186,24 +2198,23 @@
  *      LOCKING:
  *      Inherited from caller.
  */
-static int mv_init_host(struct pci_dev *pdev, struct ata_probe_ent *probe_ent,
-			unsigned int board_idx)
+static int mv_init_host(struct ata_host *host, unsigned int board_idx)
 {
 	int rc = 0, n_hc, port, hc;
-	void __iomem *mmio = probe_ent->iomap[MV_PRIMARY_BAR];
-	struct mv_host_priv *hpriv = probe_ent->private_data;
+	struct pci_dev *pdev = to_pci_dev(host->dev);
+	void __iomem *mmio = host->iomap[MV_PRIMARY_BAR];
+	struct mv_host_priv *hpriv = host->private_data;
 
 	/* global interrupt mask */
 	writel(0, mmio + HC_MAIN_IRQ_MASK_OFS);
 
-	rc = mv_chip_id(pdev, hpriv, board_idx);
+	rc = mv_chip_id(host, board_idx);
 	if (rc)
 		goto done;
 
-	n_hc = mv_get_hc_count(probe_ent->port_flags);
-	probe_ent->n_ports = MV_PORTS_PER_HC * n_hc;
+	n_hc = mv_get_hc_count(host->ports[0]->flags);
 
-	for (port = 0; port < probe_ent->n_ports; port++)
+	for (port = 0; port < host->n_ports; port++)
 		hpriv->ops->read_preamp(hpriv, port, mmio);
 
 	rc = hpriv->ops->reset_hc(hpriv, mmio, n_hc);
@@ -2214,7 +2225,7 @@
 	hpriv->ops->reset_bus(pdev, mmio);
 	hpriv->ops->enable_leds(hpriv, mmio);
 
-	for (port = 0; port < probe_ent->n_ports; port++) {
+	for (port = 0; port < host->n_ports; port++) {
 		if (IS_60XX(hpriv)) {
 			void __iomem *port_mmio = mv_port_base(mmio, port);
 
@@ -2227,9 +2238,9 @@
 		hpriv->ops->phy_errata(hpriv, mmio, port);
 	}
 
-	for (port = 0; port < probe_ent->n_ports; port++) {
+	for (port = 0; port < host->n_ports; port++) {
 		void __iomem *port_mmio = mv_port_base(mmio, port);
-		mv_port_init(&probe_ent->port[port], port_mmio);
+		mv_port_init(&host->ports[port]->ioaddr, port_mmio);
 	}
 
 	for (hc = 0; hc < n_hc; hc++) {
@@ -2268,17 +2279,17 @@
 
 /**
  *      mv_print_info - Dump key info to kernel log for perusal.
- *      @probe_ent: early data struct representing the host
+ *      @host: ATA host to print info about
  *
  *      FIXME: complete this.
  *
  *      LOCKING:
  *      Inherited from caller.
  */
-static void mv_print_info(struct ata_probe_ent *probe_ent)
+static void mv_print_info(struct ata_host *host)
 {
-	struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
-	struct mv_host_priv *hpriv = probe_ent->private_data;
+	struct pci_dev *pdev = to_pci_dev(host->dev);
+	struct mv_host_priv *hpriv = host->private_data;
 	u8 rev_id, scc;
 	const char *scc_s;
 
@@ -2297,7 +2308,7 @@
 
 	dev_printk(KERN_INFO, &pdev->dev,
 	       "%u slots %u ports %s mode IRQ via %s\n",
-	       (unsigned)MV_MAX_Q_DEPTH, probe_ent->n_ports,
+	       (unsigned)MV_MAX_Q_DEPTH, host->n_ports,
 	       scc_s, (MV_HP_FLAG_MSI & hpriv->hp_flags) ? "MSI" : "INTx");
 }
 
@@ -2312,50 +2323,42 @@
 static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	static int printed_version = 0;
-	struct device *dev = &pdev->dev;
-	struct ata_probe_ent *probe_ent;
-	struct mv_host_priv *hpriv;
 	unsigned int board_idx = (unsigned int)ent->driver_data;
-	int rc;
+	const struct ata_port_info *ppi[] = { &mv_port_info[board_idx], NULL };
+	struct ata_host *host;
+	struct mv_host_priv *hpriv;
+	int n_ports, rc;
 
 	if (!printed_version++)
 		dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n");
 
+	/* allocate host */
+	n_ports = mv_get_hc_count(ppi[0]->flags) * MV_PORTS_PER_HC;
+
+	host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports);
+	hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL);
+	if (!host || !hpriv)
+		return -ENOMEM;
+	host->private_data = hpriv;
+
+	/* acquire resources */
 	rc = pcim_enable_device(pdev);
 	if (rc)
 		return rc;
-	pci_set_master(pdev);
 
 	rc = pcim_iomap_regions(pdev, 1 << MV_PRIMARY_BAR, DRV_NAME);
 	if (rc == -EBUSY)
 		pcim_pin_device(pdev);
 	if (rc)
 		return rc;
+	host->iomap = pcim_iomap_table(pdev);
 
-	probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL);
-	if (probe_ent == NULL)
-		return -ENOMEM;
-
-	probe_ent->dev = pci_dev_to_dev(pdev);
-	INIT_LIST_HEAD(&probe_ent->node);
-
-	hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL);
-	if (!hpriv)
-		return -ENOMEM;
-
-	probe_ent->sht = mv_port_info[board_idx].sht;
-	probe_ent->port_flags = mv_port_info[board_idx].flags;
-	probe_ent->pio_mask = mv_port_info[board_idx].pio_mask;
-	probe_ent->udma_mask = mv_port_info[board_idx].udma_mask;
-	probe_ent->port_ops = mv_port_info[board_idx].port_ops;
-
-	probe_ent->irq = pdev->irq;
-	probe_ent->irq_flags = IRQF_SHARED;
-	probe_ent->iomap = pcim_iomap_table(pdev);
-	probe_ent->private_data = hpriv;
+	rc = pci_go_64(pdev);
+	if (rc)
+		return rc;
 
 	/* initialize adapter */
-	rc = mv_init_host(pdev, probe_ent, board_idx);
+	rc = mv_init_host(host, board_idx);
 	if (rc)
 		return rc;
 
@@ -2364,13 +2367,11 @@
 		pci_intx(pdev, 1);
 
 	mv_dump_pci_cfg(pdev, 0x68);
-	mv_print_info(probe_ent);
+	mv_print_info(host);
 
-	if (ata_device_add(probe_ent) == 0)
-		return -ENODEV;
-
-	devm_kfree(dev, probe_ent);
-	return 0;
+	pci_set_master(pdev);
+	return ata_host_activate(host, pdev->irq, mv_interrupt, IRQF_SHARED,
+				 &mv_sht);
 }
 
 static int __init mv_init(void)
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
index 9d9670a..0216974 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -260,6 +260,7 @@
 static void nv_adma_error_handler(struct ata_port *ap);
 static void nv_adma_host_stop(struct ata_host *host);
 static void nv_adma_post_internal_cmd(struct ata_queued_cmd *qc);
+static void nv_adma_tf_read(struct ata_port *ap, struct ata_taskfile *tf);
 
 enum nv_host_type
 {
@@ -368,7 +369,6 @@
 	.error_handler		= nv_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
 	.data_xfer		= ata_data_xfer,
-	.irq_handler		= nv_generic_interrupt,
 	.irq_clear		= ata_bmdma_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -395,7 +395,6 @@
 	.error_handler		= nv_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
 	.data_xfer		= ata_data_xfer,
-	.irq_handler		= nv_nf2_interrupt,
 	.irq_clear		= ata_bmdma_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -422,7 +421,6 @@
 	.error_handler		= nv_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
 	.data_xfer		= ata_data_xfer,
-	.irq_handler		= nv_ck804_interrupt,
 	.irq_clear		= ata_bmdma_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -435,7 +433,7 @@
 static const struct ata_port_operations nv_adma_ops = {
 	.port_disable		= ata_port_disable,
 	.tf_load		= ata_tf_load,
-	.tf_read		= ata_tf_read,
+	.tf_read		= nv_adma_tf_read,
 	.check_atapi_dma	= nv_adma_check_atapi_dma,
 	.exec_command		= ata_exec_command,
 	.check_status		= ata_check_status,
@@ -451,7 +449,6 @@
 	.error_handler		= nv_adma_error_handler,
 	.post_internal_cmd	= nv_adma_post_internal_cmd,
 	.data_xfer		= ata_data_xfer,
-	.irq_handler		= nv_adma_interrupt,
 	.irq_clear		= nv_adma_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -476,6 +473,7 @@
 		.mwdma_mask	= NV_MWDMA_MASK,
 		.udma_mask	= NV_UDMA_MASK,
 		.port_ops	= &nv_generic_ops,
+		.irq_handler	= nv_generic_interrupt,
 	},
 	/* nforce2/3 */
 	{
@@ -486,6 +484,7 @@
 		.mwdma_mask	= NV_MWDMA_MASK,
 		.udma_mask	= NV_UDMA_MASK,
 		.port_ops	= &nv_nf2_ops,
+		.irq_handler	= nv_nf2_interrupt,
 	},
 	/* ck804 */
 	{
@@ -496,6 +495,7 @@
 		.mwdma_mask	= NV_MWDMA_MASK,
 		.udma_mask	= NV_UDMA_MASK,
 		.port_ops	= &nv_ck804_ops,
+		.irq_handler	= nv_ck804_interrupt,
 	},
 	/* ADMA */
 	{
@@ -507,6 +507,7 @@
 		.mwdma_mask	= NV_MWDMA_MASK,
 		.udma_mask	= NV_UDMA_MASK,
 		.port_ops	= &nv_adma_ops,
+		.irq_handler	= nv_adma_interrupt,
 	},
 };
 
@@ -667,6 +668,18 @@
 	return !(pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE);
 }
 
+static void nv_adma_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
+{
+	/* Since commands where a result TF is requested are not
+	   executed in ADMA mode, the only time this function will be called
+	   in ADMA mode will be if a command fails. In this case we
+	   don't care about going into register mode with ADMA commands
+	   pending, as the commands will all shortly be aborted anyway. */
+	nv_adma_register_mode(ap);
+
+	ata_tf_read(ap, tf);
+}
+
 static unsigned int nv_adma_tf_to_cpb(struct ata_taskfile *tf, __le16 *cpb)
 {
 	unsigned int idx = 0;
@@ -738,19 +751,11 @@
 		return 1;
 	}
 
-	if (flags & NV_CPB_RESP_DONE) {
+	if (likely(flags & NV_CPB_RESP_DONE)) {
 		struct ata_queued_cmd *qc = ata_qc_from_tag(ap, cpb_num);
 		VPRINTK("CPB flags done, flags=0x%x\n", flags);
 		if (likely(qc)) {
-			/* Grab the ATA port status for non-NCQ commands.
-			   For NCQ commands the current status may have nothing to do with
-			   the command just completed. */
-			if (qc->tf.protocol != ATA_PROT_NCQ) {
-				u8 ata_status = readb(pp->ctl_block + (ATA_REG_STATUS * 4));
-				qc->err_mask |= ac_err_mask(ata_status);
-			}
-			DPRINTK("Completing qc from tag %d with err_mask %u\n",cpb_num,
-				qc->err_mask);
+			DPRINTK("Completing qc from tag %d\n",cpb_num);
 			ata_qc_complete(qc);
 		} else {
 			struct ata_eh_info *ehi = &ap->eh_info;
@@ -1074,14 +1079,14 @@
 }
 #endif
 
-static void nv_adma_setup_port(struct ata_probe_ent *probe_ent, unsigned int port)
+static void nv_adma_setup_port(struct ata_port *ap)
 {
-	void __iomem *mmio = probe_ent->iomap[NV_MMIO_BAR];
-	struct ata_ioports *ioport = &probe_ent->port[port];
+	void __iomem *mmio = ap->host->iomap[NV_MMIO_BAR];
+	struct ata_ioports *ioport = &ap->ioaddr;
 
 	VPRINTK("ENTER\n");
 
-	mmio += NV_ADMA_PORT + port * NV_ADMA_PORT_SIZE;
+	mmio += NV_ADMA_PORT + ap->port_no * NV_ADMA_PORT_SIZE;
 
 	ioport->cmd_addr	= mmio;
 	ioport->data_addr	= mmio + (ATA_REG_DATA * 4);
@@ -1098,9 +1103,9 @@
 	ioport->ctl_addr	= mmio + 0x20;
 }
 
-static int nv_adma_host_init(struct ata_probe_ent *probe_ent)
+static int nv_adma_host_init(struct ata_host *host)
 {
-	struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
+	struct pci_dev *pdev = to_pci_dev(host->dev);
 	unsigned int i;
 	u32 tmp32;
 
@@ -1115,8 +1120,8 @@
 
 	pci_write_config_dword(pdev, NV_MCP_SATA_CFG_20, tmp32);
 
-	for (i = 0; i < probe_ent->n_ports; i++)
-		nv_adma_setup_port(probe_ent, i);
+	for (i = 0; i < host->n_ports; i++)
+		nv_adma_setup_port(host->ports[i]);
 
 	return 0;
 }
@@ -1167,9 +1172,11 @@
 	struct nv_adma_port_priv *pp = qc->ap->private_data;
 
 	/* ADMA engine can only be used for non-ATAPI DMA commands,
-	   or interrupt-driven no-data commands. */
+	   or interrupt-driven no-data commands, where a result taskfile
+	   is not required. */
 	if((pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE) ||
-	   (qc->tf.flags & ATA_TFLAG_POLLING))
+	   (qc->tf.flags & ATA_TFLAG_POLLING) ||
+	   (qc->flags & ATA_QCFLAG_RESULT_TF))
 		return 1;
 
 	if((qc->flags & ATA_QCFLAG_DMAMAP) ||
@@ -1473,14 +1480,13 @@
 static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	static int printed_version = 0;
-	struct ata_port_info *ppi[2];
-	struct ata_probe_ent *probe_ent;
+	const struct ata_port_info *ppi[2];
+	struct ata_host *host;
 	struct nv_host_priv *hpriv;
 	int rc;
 	u32 bar;
 	void __iomem *base;
 	unsigned long type = ent->driver_data;
-	int mask_set = 0;
 
         // Make sure this is a SATA controller by counting the number of bars
         // (NVIDIA SATA controllers will always have six bars).  Otherwise,
@@ -1496,50 +1502,38 @@
 	if (rc)
 		return rc;
 
-	rc = pci_request_regions(pdev, DRV_NAME);
-	if (rc) {
-		pcim_pin_device(pdev);
-		return rc;
-	}
-
-	if(type >= CK804 && adma_enabled) {
+	/* determine type and allocate host */
+	if (type >= CK804 && adma_enabled) {
 		dev_printk(KERN_NOTICE, &pdev->dev, "Using ADMA mode\n");
 		type = ADMA;
-		if(!pci_set_dma_mask(pdev, DMA_64BIT_MASK) &&
-		   !pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK))
-			mask_set = 1;
 	}
 
-	if(!mask_set) {
-		rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
-		if (rc)
-			return rc;
-		rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
-		if (rc)
-			return rc;
-	}
-
-	rc = -ENOMEM;
+	ppi[0] = ppi[1] = &nv_port_info[type];
+	rc = ata_pci_prepare_native_host(pdev, ppi, 2, &host);
+	if (rc)
+		return rc;
 
 	hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL);
 	if (!hpriv)
 		return -ENOMEM;
-
-	ppi[0] = ppi[1] = &nv_port_info[type];
-	probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
-	if (!probe_ent)
-		return -ENOMEM;
-
-	if (!pcim_iomap(pdev, NV_MMIO_BAR, 0))
-		return -EIO;
-	probe_ent->iomap = pcim_iomap_table(pdev);
-
-	probe_ent->private_data = hpriv;
 	hpriv->type = type;
+	host->private_data = hpriv;
 
-	base = probe_ent->iomap[NV_MMIO_BAR];
-	probe_ent->port[0].scr_addr = base + NV_PORT0_SCR_REG_OFFSET;
-	probe_ent->port[1].scr_addr = base + NV_PORT1_SCR_REG_OFFSET;
+	/* set 64bit dma masks, may fail */
+	if (type == ADMA) {
+		if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) == 0)
+			pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);
+	}
+
+	/* request and iomap NV_MMIO_BAR */
+	rc = pcim_iomap_regions(pdev, 1 << NV_MMIO_BAR, DRV_NAME);
+	if (rc)
+		return rc;
+
+	/* configure SCR access */
+	base = host->iomap[NV_MMIO_BAR];
+	host->ports[0]->ioaddr.scr_addr = base + NV_PORT0_SCR_REG_OFFSET;
+	host->ports[1]->ioaddr.scr_addr = base + NV_PORT1_SCR_REG_OFFSET;
 
 	/* enable SATA space for CK804 */
 	if (type >= CK804) {
@@ -1550,20 +1544,16 @@
 		pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval);
 	}
 
-	pci_set_master(pdev);
-
+	/* init ADMA */
 	if (type == ADMA) {
-		rc = nv_adma_host_init(probe_ent);
+		rc = nv_adma_host_init(host);
 		if (rc)
 			return rc;
 	}
 
-	rc = ata_device_add(probe_ent);
-	if (rc != NV_PORTS)
-		return -ENODEV;
-
-	devm_kfree(&pdev->dev, probe_ent);
-	return 0;
+	pci_set_master(pdev);
+	return ata_host_activate(host, pdev->irq, ppi[0]->irq_handler,
+				 IRQF_SHARED, ppi[0]->sht);
 }
 
 static void nv_remove_one (struct pci_dev *pdev)
diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c
index 2339813..f56549b 100644
--- a/drivers/ata/sata_promise.c
+++ b/drivers/ata/sata_promise.c
@@ -45,10 +45,11 @@
 #include "sata_promise.h"
 
 #define DRV_NAME	"sata_promise"
-#define DRV_VERSION	"2.00"
+#define DRV_VERSION	"2.05"
 
 
 enum {
+	PDC_MAX_PORTS		= 4,
 	PDC_MMIO_BAR		= 3,
 
 	/* register offsets */
@@ -70,14 +71,31 @@
 	PDC_TBG_MODE		= 0x41C, /* TBG mode (not SATAII) */
 	PDC_SLEW_CTL		= 0x470, /* slew rate control reg (not SATAII) */
 
-	PDC_ERR_MASK		= (1<<19) | (1<<20) | (1<<21) | (1<<22) |
-				  (1<<8) | (1<<9) | (1<<10),
+	/* PDC_GLOBAL_CTL bit definitions */
+	PDC_PH_ERR		= (1 <<  8), /* PCI error while loading packet */
+	PDC_SH_ERR		= (1 <<  9), /* PCI error while loading S/G table */
+	PDC_DH_ERR		= (1 << 10), /* PCI error while loading data */
+	PDC2_HTO_ERR		= (1 << 12), /* host bus timeout */
+	PDC2_ATA_HBA_ERR	= (1 << 13), /* error during SATA DATA FIS transmission */
+	PDC2_ATA_DMA_CNT_ERR	= (1 << 14), /* DMA DATA FIS size differs from S/G count */
+	PDC_OVERRUN_ERR		= (1 << 19), /* S/G byte count larger than HD requires */
+	PDC_UNDERRUN_ERR	= (1 << 20), /* S/G byte count less than HD requires */
+	PDC_DRIVE_ERR		= (1 << 21), /* drive error */
+	PDC_PCI_SYS_ERR		= (1 << 22), /* PCI system error */
+	PDC1_PCI_PARITY_ERR	= (1 << 23), /* PCI parity error (from SATA150 driver) */
+	PDC1_ERR_MASK		= PDC1_PCI_PARITY_ERR,
+	PDC2_ERR_MASK		= PDC2_HTO_ERR | PDC2_ATA_HBA_ERR | PDC2_ATA_DMA_CNT_ERR,
+	PDC_ERR_MASK		= (PDC_PH_ERR | PDC_SH_ERR | PDC_DH_ERR | PDC_OVERRUN_ERR
+				   | PDC_UNDERRUN_ERR | PDC_DRIVE_ERR | PDC_PCI_SYS_ERR
+				   | PDC1_ERR_MASK | PDC2_ERR_MASK),
 
 	board_2037x		= 0,	/* FastTrak S150 TX2plus */
-	board_20319		= 1,	/* FastTrak S150 TX4 */
-	board_20619		= 2,	/* FastTrak TX4000 */
-	board_2057x		= 3,	/* SATAII150 Tx2plus */
-	board_40518		= 4,	/* SATAII150 Tx4 */
+	board_2037x_pata	= 1,	/* FastTrak S150 TX2plus PATA port */
+	board_20319		= 2,	/* FastTrak S150 TX4 */
+	board_20619		= 3,	/* FastTrak TX4000 */
+	board_2057x		= 4,	/* SATAII150 Tx2plus */
+	board_2057x_pata	= 5,	/* SATAII150 Tx2plus */
+	board_40518		= 6,	/* SATAII150 Tx4 */
 
 	PDC_HAS_PATA		= (1 << 1), /* PDC20375/20575 has PATA */
 
@@ -100,8 +118,10 @@
 				  ATA_FLAG_MMIO |
 				  ATA_FLAG_PIO_POLLING,
 
-	/* hp->flags bits */
-	PDC_FLAG_GEN_II		= (1 << 0),
+	/* ap->flags bits */
+	PDC_FLAG_GEN_II		= (1 << 24),
+	PDC_FLAG_SATA_PATA	= (1 << 25), /* supports SATA + PATA */
+	PDC_FLAG_4_PORTS	= (1 << 26), /* 4 ports */
 };
 
 
@@ -110,28 +130,25 @@
 	dma_addr_t		pkt_dma;
 };
 
-struct pdc_host_priv {
-	unsigned long		flags;
-	unsigned long		port_flags[ATA_MAX_PORTS];
-};
-
 static u32 pdc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg);
 static void pdc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
 static int pdc_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
-static irqreturn_t pdc_interrupt (int irq, void *dev_instance);
-static int pdc_port_start(struct ata_port *ap);
+static int pdc_common_port_start(struct ata_port *ap);
+static int pdc_sata_port_start(struct ata_port *ap);
 static void pdc_qc_prep(struct ata_queued_cmd *qc);
 static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf);
 static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf);
 static int pdc_check_atapi_dma(struct ata_queued_cmd *qc);
-static int pdc_old_check_atapi_dma(struct ata_queued_cmd *qc);
+static int pdc_old_sata_check_atapi_dma(struct ata_queued_cmd *qc);
 static void pdc_irq_clear(struct ata_port *ap);
 static unsigned int pdc_qc_issue_prot(struct ata_queued_cmd *qc);
 static void pdc_freeze(struct ata_port *ap);
 static void pdc_thaw(struct ata_port *ap);
-static void pdc_error_handler(struct ata_port *ap);
+static void pdc_pata_error_handler(struct ata_port *ap);
+static void pdc_sata_error_handler(struct ata_port *ap);
 static void pdc_post_internal_cmd(struct ata_queued_cmd *qc);
-
+static int pdc_pata_cable_detect(struct ata_port *ap);
+static int pdc_sata_cable_detect(struct ata_port *ap);
 
 static struct scsi_host_template pdc_ata_sht = {
 	.module			= THIS_MODULE,
@@ -164,17 +181,17 @@
 	.qc_issue		= pdc_qc_issue_prot,
 	.freeze			= pdc_freeze,
 	.thaw			= pdc_thaw,
-	.error_handler		= pdc_error_handler,
+	.error_handler		= pdc_sata_error_handler,
 	.post_internal_cmd	= pdc_post_internal_cmd,
+	.cable_detect		= pdc_sata_cable_detect,
 	.data_xfer		= ata_data_xfer,
-	.irq_handler		= pdc_interrupt,
 	.irq_clear		= pdc_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
 
 	.scr_read		= pdc_sata_scr_read,
 	.scr_write		= pdc_sata_scr_write,
-	.port_start		= pdc_port_start,
+	.port_start		= pdc_sata_port_start,
 };
 
 /* First-generation chips need a more restrictive ->check_atapi_dma op */
@@ -185,23 +202,23 @@
 	.check_status		= ata_check_status,
 	.exec_command		= pdc_exec_command_mmio,
 	.dev_select		= ata_std_dev_select,
-	.check_atapi_dma	= pdc_old_check_atapi_dma,
+	.check_atapi_dma	= pdc_old_sata_check_atapi_dma,
 
 	.qc_prep		= pdc_qc_prep,
 	.qc_issue		= pdc_qc_issue_prot,
 	.freeze			= pdc_freeze,
 	.thaw			= pdc_thaw,
-	.error_handler		= pdc_error_handler,
+	.error_handler		= pdc_sata_error_handler,
 	.post_internal_cmd	= pdc_post_internal_cmd,
+	.cable_detect		= pdc_sata_cable_detect,
 	.data_xfer		= ata_data_xfer,
-	.irq_handler		= pdc_interrupt,
 	.irq_clear		= pdc_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
 
 	.scr_read		= pdc_sata_scr_read,
 	.scr_write		= pdc_sata_scr_write,
-	.port_start		= pdc_port_start,
+	.port_start		= pdc_sata_port_start,
 };
 
 static const struct ata_port_operations pdc_pata_ops = {
@@ -217,32 +234,41 @@
 	.qc_issue		= pdc_qc_issue_prot,
 	.freeze			= pdc_freeze,
 	.thaw			= pdc_thaw,
-	.error_handler		= pdc_error_handler,
+	.error_handler		= pdc_pata_error_handler,
 	.post_internal_cmd	= pdc_post_internal_cmd,
+	.cable_detect		= pdc_pata_cable_detect,
 	.data_xfer		= ata_data_xfer,
-	.irq_handler		= pdc_interrupt,
 	.irq_clear		= pdc_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
 
-	.port_start		= pdc_port_start,
+	.port_start		= pdc_common_port_start,
 };
 
 static const struct ata_port_info pdc_port_info[] = {
 	/* board_2037x */
 	{
-		.sht		= &pdc_ata_sht,
-		.flags		= PDC_COMMON_FLAGS,
+		.flags		= PDC_COMMON_FLAGS | ATA_FLAG_SATA |
+				  PDC_FLAG_SATA_PATA,
 		.pio_mask	= 0x1f, /* pio0-4 */
 		.mwdma_mask	= 0x07, /* mwdma0-2 */
 		.udma_mask	= 0x7f, /* udma0-6 ; FIXME */
 		.port_ops	= &pdc_old_sata_ops,
 	},
 
+	/* board_2037x_pata */
+	{
+		.flags		= PDC_COMMON_FLAGS | ATA_FLAG_SLAVE_POSS,
+		.pio_mask	= 0x1f, /* pio0-4 */
+		.mwdma_mask	= 0x07, /* mwdma0-2 */
+		.udma_mask	= 0x7f, /* udma0-6 ; FIXME */
+		.port_ops	= &pdc_pata_ops,
+	},
+
 	/* board_20319 */
 	{
-		.sht		= &pdc_ata_sht,
-		.flags		= PDC_COMMON_FLAGS | ATA_FLAG_SATA,
+		.flags		= PDC_COMMON_FLAGS | ATA_FLAG_SATA |
+				  PDC_FLAG_4_PORTS,
 		.pio_mask	= 0x1f, /* pio0-4 */
 		.mwdma_mask	= 0x07, /* mwdma0-2 */
 		.udma_mask	= 0x7f, /* udma0-6 ; FIXME */
@@ -251,8 +277,8 @@
 
 	/* board_20619 */
 	{
-		.sht		= &pdc_ata_sht,
-		.flags		= PDC_COMMON_FLAGS | ATA_FLAG_SLAVE_POSS,
+		.flags		= PDC_COMMON_FLAGS | ATA_FLAG_SLAVE_POSS |
+				  PDC_FLAG_4_PORTS,
 		.pio_mask	= 0x1f, /* pio0-4 */
 		.mwdma_mask	= 0x07, /* mwdma0-2 */
 		.udma_mask	= 0x7f, /* udma0-6 ; FIXME */
@@ -261,18 +287,28 @@
 
 	/* board_2057x */
 	{
-		.sht		= &pdc_ata_sht,
-		.flags		= PDC_COMMON_FLAGS,
+		.flags		= PDC_COMMON_FLAGS | ATA_FLAG_SATA |
+				  PDC_FLAG_GEN_II | PDC_FLAG_SATA_PATA,
 		.pio_mask	= 0x1f, /* pio0-4 */
 		.mwdma_mask	= 0x07, /* mwdma0-2 */
 		.udma_mask	= 0x7f, /* udma0-6 ; FIXME */
 		.port_ops	= &pdc_sata_ops,
 	},
 
+	/* board_2057x_pata */
+	{
+		.flags		= PDC_COMMON_FLAGS | ATA_FLAG_SLAVE_POSS,
+				  PDC_FLAG_GEN_II,
+		.pio_mask	= 0x1f, /* pio0-4 */
+		.mwdma_mask	= 0x07, /* mwdma0-2 */
+		.udma_mask	= 0x7f, /* udma0-6 ; FIXME */
+		.port_ops	= &pdc_pata_ops,
+	},
+
 	/* board_40518 */
 	{
-		.sht		= &pdc_ata_sht,
-		.flags		= PDC_COMMON_FLAGS | ATA_FLAG_SATA,
+		.flags		= PDC_COMMON_FLAGS | ATA_FLAG_SATA |
+				  PDC_FLAG_GEN_II | PDC_FLAG_4_PORTS,
 		.pio_mask	= 0x1f, /* pio0-4 */
 		.mwdma_mask	= 0x07, /* mwdma0-2 */
 		.udma_mask	= 0x7f, /* udma0-6 ; FIXME */
@@ -313,18 +349,12 @@
 };
 
 
-static int pdc_port_start(struct ata_port *ap)
+static int pdc_common_port_start(struct ata_port *ap)
 {
 	struct device *dev = ap->host->dev;
-	struct pdc_host_priv *hp = ap->host->private_data;
 	struct pdc_port_priv *pp;
 	int rc;
 
-	/* fix up port flags and cable type for SATA+PATA chips */
-	ap->flags |= hp->port_flags[ap->port_no];
-	if (ap->flags & ATA_FLAG_SATA)
-		ap->cbl = ATA_CBL_SATA;
-
 	rc = ata_port_start(ap);
 	if (rc)
 		return rc;
@@ -339,8 +369,19 @@
 
 	ap->private_data = pp;
 
+	return 0;
+}
+
+static int pdc_sata_port_start(struct ata_port *ap)
+{
+	int rc;
+
+	rc = pdc_common_port_start(ap);
+	if (rc)
+		return rc;
+
 	/* fix up PHYMODE4 align timing */
-	if ((hp->flags & PDC_FLAG_GEN_II) && sata_scr_valid(ap)) {
+	if (ap->flags & PDC_FLAG_GEN_II) {
 		void __iomem *mmio = (void __iomem *) ap->ioaddr.scr_addr;
 		unsigned int tmp;
 
@@ -374,23 +415,25 @@
 	readl(mmio);	/* flush */
 }
 
-static void pdc_pata_cbl_detect(struct ata_port *ap)
+static int pdc_pata_cable_detect(struct ata_port *ap)
 {
 	u8 tmp;
 	void __iomem *mmio = (void __iomem *) ap->ioaddr.cmd_addr + PDC_CTLSTAT + 0x03;
 
 	tmp = readb(mmio);
+	if (tmp & 0x01)
+		return ATA_CBL_PATA40;
+	return ATA_CBL_PATA80;
+}
 
-	if (tmp & 0x01) {
-		ap->cbl = ATA_CBL_PATA40;
-		ap->udma_mask &= ATA_UDMA_MASK_40C;
-	} else
-		ap->cbl = ATA_CBL_PATA80;
+static int pdc_sata_cable_detect(struct ata_port *ap)
+{
+	return ATA_CBL_SATA;
 }
 
 static u32 pdc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg)
 {
-	if (sc_reg > SCR_CONTROL || ap->cbl != ATA_CBL_SATA)
+	if (sc_reg > SCR_CONTROL)
 		return 0xffffffffU;
 	return readl(ap->ioaddr.scr_addr + (sc_reg * 4));
 }
@@ -399,7 +442,7 @@
 static void pdc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg,
 			       u32 val)
 {
-	if (sc_reg > SCR_CONTROL || ap->cbl != ATA_CBL_SATA)
+	if (sc_reg > SCR_CONTROL)
 		return;
 	writel(val, ap->ioaddr.scr_addr + (sc_reg * 4));
 }
@@ -555,52 +598,79 @@
 	readl(mmio + PDC_CTLSTAT); /* flush */
 }
 
-static int pdc_pre_reset(struct ata_port *ap)
+static void pdc_common_error_handler(struct ata_port *ap, ata_reset_fn_t hardreset)
 {
-	if (!sata_scr_valid(ap))
-		pdc_pata_cbl_detect(ap);
-	return ata_std_prereset(ap);
-}
-
-static void pdc_error_handler(struct ata_port *ap)
-{
-	ata_reset_fn_t hardreset;
-
 	if (!(ap->pflags & ATA_PFLAG_FROZEN))
 		pdc_reset_port(ap);
 
-	hardreset = NULL;
-	if (sata_scr_valid(ap))
-		hardreset = sata_std_hardreset;
-
 	/* perform recovery */
-	ata_do_eh(ap, pdc_pre_reset, ata_std_softreset, hardreset,
+	ata_do_eh(ap, ata_std_prereset, ata_std_softreset, hardreset,
 		  ata_std_postreset);
 }
 
+static void pdc_pata_error_handler(struct ata_port *ap)
+{
+	pdc_common_error_handler(ap, NULL);
+}
+
+static void pdc_sata_error_handler(struct ata_port *ap)
+{
+	pdc_common_error_handler(ap, sata_std_hardreset);
+}
+
 static void pdc_post_internal_cmd(struct ata_queued_cmd *qc)
 {
 	struct ata_port *ap = qc->ap;
 
-	if (qc->flags & ATA_QCFLAG_FAILED)
-		qc->err_mask |= AC_ERR_OTHER;
-
 	/* make DMA engine forget about the failed command */
-	if (qc->err_mask)
+	if (qc->flags & ATA_QCFLAG_FAILED)
 		pdc_reset_port(ap);
 }
 
+static void pdc_error_intr(struct ata_port *ap, struct ata_queued_cmd *qc,
+			   u32 port_status, u32 err_mask)
+{
+	struct ata_eh_info *ehi = &ap->eh_info;
+	unsigned int ac_err_mask = 0;
+
+	ata_ehi_clear_desc(ehi);
+	ata_ehi_push_desc(ehi, "port_status 0x%08x", port_status);
+	port_status &= err_mask;
+
+	if (port_status & PDC_DRIVE_ERR)
+		ac_err_mask |= AC_ERR_DEV;
+	if (port_status & (PDC_OVERRUN_ERR | PDC_UNDERRUN_ERR))
+		ac_err_mask |= AC_ERR_HSM;
+	if (port_status & (PDC2_ATA_HBA_ERR | PDC2_ATA_DMA_CNT_ERR))
+		ac_err_mask |= AC_ERR_ATA_BUS;
+	if (port_status & (PDC_PH_ERR | PDC_SH_ERR | PDC_DH_ERR | PDC2_HTO_ERR
+			   | PDC_PCI_SYS_ERR | PDC1_PCI_PARITY_ERR))
+		ac_err_mask |= AC_ERR_HOST_BUS;
+
+	if (sata_scr_valid(ap))
+		ehi->serror |= pdc_sata_scr_read(ap, SCR_ERROR);
+
+	qc->err_mask |= ac_err_mask;
+
+	pdc_reset_port(ap);
+}
+
 static inline unsigned int pdc_host_intr( struct ata_port *ap,
                                           struct ata_queued_cmd *qc)
 {
 	unsigned int handled = 0;
-	u32 tmp;
-	void __iomem *mmio = ap->ioaddr.cmd_addr + PDC_GLOBAL_CTL;
+	void __iomem *port_mmio = ap->ioaddr.cmd_addr;
+	u32 port_status, err_mask;
 
-	tmp = readl(mmio);
-	if (tmp & PDC_ERR_MASK) {
-		qc->err_mask |= AC_ERR_DEV;
-		pdc_reset_port(ap);
+	err_mask = PDC_ERR_MASK;
+	if (ap->flags & PDC_FLAG_GEN_II)
+		err_mask &= ~PDC1_ERR_MASK;
+	else
+		err_mask &= ~PDC2_ERR_MASK;
+	port_status = readl(port_mmio + PDC_GLOBAL_CTL);
+	if (unlikely(port_status & err_mask)) {
+		pdc_error_intr(ap, qc, port_status, err_mask);
+		return 1;
 	}
 
 	switch (qc->tf.protocol) {
@@ -767,44 +837,40 @@
 	return pio;
 }
 
-static int pdc_old_check_atapi_dma(struct ata_queued_cmd *qc)
+static int pdc_old_sata_check_atapi_dma(struct ata_queued_cmd *qc)
 {
-	struct ata_port *ap = qc->ap;
-
 	/* First generation chips cannot use ATAPI DMA on SATA ports */
-	if (sata_scr_valid(ap))
-		return 1;
-	return pdc_check_atapi_dma(qc);
+	return 1;
 }
 
-static void pdc_ata_setup_port(struct ata_ioports *port, void __iomem *base,
-			       void __iomem *scr_addr)
+static void pdc_ata_setup_port(struct ata_port *ap,
+			       void __iomem *base, void __iomem *scr_addr)
 {
-	port->cmd_addr		= base;
-	port->data_addr		= base;
-	port->feature_addr	=
-	port->error_addr	= base + 0x4;
-	port->nsect_addr	= base + 0x8;
-	port->lbal_addr		= base + 0xc;
-	port->lbam_addr		= base + 0x10;
-	port->lbah_addr		= base + 0x14;
-	port->device_addr	= base + 0x18;
-	port->command_addr	=
-	port->status_addr	= base + 0x1c;
-	port->altstatus_addr	=
-	port->ctl_addr		= base + 0x38;
-	port->scr_addr		= scr_addr;
+	ap->ioaddr.cmd_addr		= base;
+	ap->ioaddr.data_addr		= base;
+	ap->ioaddr.feature_addr		=
+	ap->ioaddr.error_addr		= base + 0x4;
+	ap->ioaddr.nsect_addr		= base + 0x8;
+	ap->ioaddr.lbal_addr		= base + 0xc;
+	ap->ioaddr.lbam_addr		= base + 0x10;
+	ap->ioaddr.lbah_addr		= base + 0x14;
+	ap->ioaddr.device_addr		= base + 0x18;
+	ap->ioaddr.command_addr		=
+	ap->ioaddr.status_addr		= base + 0x1c;
+	ap->ioaddr.altstatus_addr	=
+	ap->ioaddr.ctl_addr		= base + 0x38;
+	ap->ioaddr.scr_addr		= scr_addr;
 }
 
 
-static void pdc_host_init(unsigned int chip_id, struct ata_probe_ent *pe)
+static void pdc_host_init(struct ata_host *host)
 {
-	void __iomem *mmio = pe->iomap[PDC_MMIO_BAR];
-	struct pdc_host_priv *hp = pe->private_data;
+	void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
+	int is_gen2 = host->ports[0]->flags & PDC_FLAG_GEN_II;
 	int hotplug_offset;
 	u32 tmp;
 
-	if (hp->flags & PDC_FLAG_GEN_II)
+	if (is_gen2)
 		hotplug_offset = PDC2_SATA_PLUG_CSR;
 	else
 		hotplug_offset = PDC_SATA_PLUG_CSR;
@@ -818,7 +884,7 @@
 	/* enable BMR_BURST, maybe change FIFO_SHD to 8 dwords */
 	tmp = readl(mmio + PDC_FLASH_CTL);
 	tmp |= 0x02000;	/* bit 13 (enable bmr burst) */
-	if (!(hp->flags & PDC_FLAG_GEN_II))
+	if (!is_gen2)
 		tmp |= 0x10000;	/* bit 16 (fifo threshold at 8 dw) */
 	writel(tmp, mmio + PDC_FLASH_CTL);
 
@@ -831,7 +897,7 @@
 	writel(tmp | 0xff0000, mmio + hotplug_offset);
 
 	/* don't initialise TBG or SLEW on 2nd generation chips */
-	if (hp->flags & PDC_FLAG_GEN_II)
+	if (is_gen2)
 		return;
 
 	/* reduce TBG clock to 133 Mhz. */
@@ -853,16 +919,16 @@
 static int pdc_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	static int printed_version;
-	struct ata_probe_ent *probe_ent;
-	struct pdc_host_priv *hp;
+	const struct ata_port_info *pi = &pdc_port_info[ent->driver_data];
+	const struct ata_port_info *ppi[PDC_MAX_PORTS];
+	struct ata_host *host;
 	void __iomem *base;
-	unsigned int board_idx = (unsigned int) ent->driver_data;
-	int rc;
-	u8 tmp;
+	int n_ports, i, rc;
 
 	if (!printed_version++)
 		dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
 
+	/* enable and acquire resources */
 	rc = pcim_enable_device(pdev);
 	if (rc)
 		return rc;
@@ -872,6 +938,37 @@
 		pcim_pin_device(pdev);
 	if (rc)
 		return rc;
+	base = pcim_iomap_table(pdev)[PDC_MMIO_BAR];
+
+	/* determine port configuration and setup host */
+	n_ports = 2;
+	if (pi->flags & PDC_FLAG_4_PORTS)
+		n_ports = 4;
+	for (i = 0; i < n_ports; i++)
+		ppi[i] = pi;
+
+	if (pi->flags & PDC_FLAG_SATA_PATA) {
+		u8 tmp = readb(base + PDC_FLASH_CTL+1);
+		if (!(tmp & 0x80)) {
+			ppi[n_ports++] = pi + 1;
+			dev_printk(KERN_INFO, &pdev->dev, "PATA port found\n");
+		}
+	}
+
+	host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports);
+	if (!host) {
+		dev_printk(KERN_ERR, &pdev->dev, "failed to allocate host\n");
+		return -ENOMEM;
+	}
+	host->iomap = pcim_iomap_table(pdev);
+
+	for (i = 0; i < host->n_ports; i++)
+		pdc_ata_setup_port(host->ports[i],
+				   base + 0x200 + i * 0x80,
+				   base + 0x400 + i * 0x100);
+
+	/* initialize adapter */
+	pdc_host_init(host);
 
 	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
 	if (rc)
@@ -880,81 +977,10 @@
 	if (rc)
 		return rc;
 
-	probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL);
-	if (probe_ent == NULL)
-		return -ENOMEM;
-
-	probe_ent->dev = pci_dev_to_dev(pdev);
-	INIT_LIST_HEAD(&probe_ent->node);
-
-	hp = devm_kzalloc(&pdev->dev, sizeof(*hp), GFP_KERNEL);
-	if (hp == NULL)
-		return -ENOMEM;
-
-	probe_ent->private_data = hp;
-
-	probe_ent->sht		= pdc_port_info[board_idx].sht;
-	probe_ent->port_flags	= pdc_port_info[board_idx].flags;
-	probe_ent->pio_mask	= pdc_port_info[board_idx].pio_mask;
-	probe_ent->mwdma_mask	= pdc_port_info[board_idx].mwdma_mask;
-	probe_ent->udma_mask	= pdc_port_info[board_idx].udma_mask;
-	probe_ent->port_ops	= pdc_port_info[board_idx].port_ops;
-
-       	probe_ent->irq = pdev->irq;
-       	probe_ent->irq_flags = IRQF_SHARED;
-	probe_ent->iomap = pcim_iomap_table(pdev);
-
-	base = probe_ent->iomap[PDC_MMIO_BAR];
-
-	pdc_ata_setup_port(&probe_ent->port[0], base + 0x200, base + 0x400);
-	pdc_ata_setup_port(&probe_ent->port[1], base + 0x280, base + 0x500);
-
-	/* notice 4-port boards */
-	switch (board_idx) {
-	case board_40518:
-		hp->flags |= PDC_FLAG_GEN_II;
-		/* Fall through */
-	case board_20319:
-       		probe_ent->n_ports = 4;
-		pdc_ata_setup_port(&probe_ent->port[2], base + 0x300, base + 0x600);
-		pdc_ata_setup_port(&probe_ent->port[3], base + 0x380, base + 0x700);
-		break;
-	case board_2057x:
-		hp->flags |= PDC_FLAG_GEN_II;
-		/* Fall through */
-	case board_2037x:
-		/* TX2plus boards also have a PATA port */
-		tmp = readb(base + PDC_FLASH_CTL+1);
-		if (!(tmp & 0x80)) {
-			probe_ent->n_ports = 3;
-			pdc_ata_setup_port(&probe_ent->port[2], base + 0x300, NULL);
-			hp->port_flags[2] = ATA_FLAG_SLAVE_POSS;
-			printk(KERN_INFO DRV_NAME " PATA port found\n");
-		} else
-			probe_ent->n_ports = 2;
-		hp->port_flags[0] = ATA_FLAG_SATA;
-		hp->port_flags[1] = ATA_FLAG_SATA;
-		break;
-	case board_20619:
-		probe_ent->n_ports = 4;
-		pdc_ata_setup_port(&probe_ent->port[2], base + 0x300, NULL);
-		pdc_ata_setup_port(&probe_ent->port[3], base + 0x380, NULL);
-		break;
-	default:
-		BUG();
-		break;
-	}
-
+	/* start host, request IRQ and attach */
 	pci_set_master(pdev);
-
-	/* initialize adapter */
-	pdc_host_init(board_idx, probe_ent);
-
-	if (!ata_device_add(probe_ent))
-		return -ENODEV;
-
-	devm_kfree(&pdev->dev, probe_ent);
-	return 0;
+	return ata_host_activate(host, pdev->irq, pdc_interrupt, IRQF_SHARED,
+				 &pdc_ata_sht);
 }
 
 
diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c
index 8786b45..f5a05de 100644
--- a/drivers/ata/sata_qstor.c
+++ b/drivers/ata/sata_qstor.c
@@ -114,7 +114,6 @@
 static u32 qs_scr_read (struct ata_port *ap, unsigned int sc_reg);
 static void qs_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
 static int qs_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
-static irqreturn_t qs_intr (int irq, void *dev_instance);
 static int qs_port_start(struct ata_port *ap);
 static void qs_host_stop(struct ata_host *host);
 static void qs_phy_reset(struct ata_port *ap);
@@ -158,7 +157,6 @@
 	.qc_issue		= qs_qc_issue,
 	.data_xfer		= ata_data_xfer,
 	.eng_timeout		= qs_eng_timeout,
-	.irq_handler		= qs_intr,
 	.irq_clear		= qs_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -173,7 +171,6 @@
 static const struct ata_port_info qs_port_info[] = {
 	/* board_2068_idx */
 	{
-		.sht		= &qs_ata_sht,
 		.flags		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
 				  ATA_FLAG_SATA_RESET |
 				  //FIXME ATA_FLAG_SRST |
@@ -530,16 +527,16 @@
 	writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */
 }
 
-static void qs_host_init(unsigned int chip_id, struct ata_probe_ent *pe)
+static void qs_host_init(struct ata_host *host, unsigned int chip_id)
 {
-	void __iomem *mmio_base = pe->iomap[QS_MMIO_BAR];
+	void __iomem *mmio_base = host->iomap[QS_MMIO_BAR];
 	unsigned int port_no;
 
 	writeb(0, mmio_base + QS_HCT_CTRL); /* disable host interrupts */
 	writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */
 
 	/* reset each channel in turn */
-	for (port_no = 0; port_no < pe->n_ports; ++port_no) {
+	for (port_no = 0; port_no < host->n_ports; ++port_no) {
 		u8 __iomem *chan = mmio_base + (port_no * 0x4000);
 		writeb(QS_CTR1_RDEV|QS_CTR1_RCHN, chan + QS_CCT_CTR1);
 		writeb(QS_CTR0_REG, chan + QS_CCT_CTR0);
@@ -547,7 +544,7 @@
 	}
 	writeb(QS_SERD3_PHY_ENA, mmio_base + QS_HVS_SERD3); /* enable phy */
 
-	for (port_no = 0; port_no < pe->n_ports; ++port_no) {
+	for (port_no = 0; port_no < host->n_ports; ++port_no) {
 		u8 __iomem *chan = mmio_base + (port_no * 0x4000);
 		/* set FIFO depths to same settings as Windows driver */
 		writew(32, chan + QS_CFC_HUFT);
@@ -607,14 +604,20 @@
 				const struct pci_device_id *ent)
 {
 	static int printed_version;
-	struct ata_probe_ent *probe_ent;
-	void __iomem * const *iomap;
 	unsigned int board_idx = (unsigned int) ent->driver_data;
+	const struct ata_port_info *ppi[] = { &qs_port_info[board_idx], NULL };
+	struct ata_host *host;
 	int rc, port_no;
 
 	if (!printed_version++)
 		dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
 
+	/* alloc host */
+	host = ata_host_alloc_pinfo(&pdev->dev, ppi, QS_PORTS);
+	if (!host)
+		return -ENOMEM;
+
+	/* acquire resources and fill host */
 	rc = pcim_enable_device(pdev);
 	if (rc)
 		return rc;
@@ -625,47 +628,24 @@
 	rc = pcim_iomap_regions(pdev, 1 << QS_MMIO_BAR, DRV_NAME);
 	if (rc)
 		return rc;
-	iomap = pcim_iomap_table(pdev);
+	host->iomap = pcim_iomap_table(pdev);
 
-	rc = qs_set_dma_masks(pdev, iomap[QS_MMIO_BAR]);
+	rc = qs_set_dma_masks(pdev, host->iomap[QS_MMIO_BAR]);
 	if (rc)
 		return rc;
 
-	probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL);
-	if (probe_ent == NULL)
-		return -ENOMEM;
-
-	probe_ent->dev = pci_dev_to_dev(pdev);
-	INIT_LIST_HEAD(&probe_ent->node);
-
-	probe_ent->sht		= qs_port_info[board_idx].sht;
-	probe_ent->port_flags	= qs_port_info[board_idx].flags;
-	probe_ent->pio_mask	= qs_port_info[board_idx].pio_mask;
-	probe_ent->mwdma_mask	= qs_port_info[board_idx].mwdma_mask;
-	probe_ent->udma_mask	= qs_port_info[board_idx].udma_mask;
-	probe_ent->port_ops	= qs_port_info[board_idx].port_ops;
-
-	probe_ent->irq		= pdev->irq;
-	probe_ent->irq_flags	= IRQF_SHARED;
-	probe_ent->iomap	= iomap;
-	probe_ent->n_ports	= QS_PORTS;
-
-	for (port_no = 0; port_no < probe_ent->n_ports; ++port_no) {
+	for (port_no = 0; port_no < host->n_ports; ++port_no) {
 		void __iomem *chan =
-			probe_ent->iomap[QS_MMIO_BAR] + (port_no * 0x4000);
-		qs_ata_setup_port(&probe_ent->port[port_no], chan);
+			host->iomap[QS_MMIO_BAR] + (port_no * 0x4000);
+		qs_ata_setup_port(&host->ports[port_no]->ioaddr, chan);
 	}
 
-	pci_set_master(pdev);
-
 	/* initialize adapter */
-	qs_host_init(board_idx, probe_ent);
+	qs_host_init(host, board_idx);
 
-	if (ata_device_add(probe_ent) != QS_PORTS)
-		return -EIO;
-
-	devm_kfree(&pdev->dev, probe_ent);
-	return 0;
+	pci_set_master(pdev);
+	return ata_host_activate(host, pdev->irq, qs_intr, IRQF_SHARED,
+				 &qs_ata_sht);
 }
 
 static int __init qs_ata_init(void)
diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c
index 917b7ea..0a1e417 100644
--- a/drivers/ata/sata_sil.c
+++ b/drivers/ata/sata_sil.c
@@ -46,7 +46,7 @@
 #include <linux/libata.h>
 
 #define DRV_NAME	"sata_sil"
-#define DRV_VERSION	"2.1"
+#define DRV_VERSION	"2.2"
 
 enum {
 	SIL_MMIO_BAR		= 5,
@@ -114,11 +114,10 @@
 #ifdef CONFIG_PM
 static int sil_pci_device_resume(struct pci_dev *pdev);
 #endif
-static void sil_dev_config(struct ata_port *ap, struct ata_device *dev);
+static void sil_dev_config(struct ata_device *dev);
 static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg);
 static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
-static void sil_post_set_mode (struct ata_port *ap);
-static irqreturn_t sil_interrupt(int irq, void *dev_instance);
+static int sil_set_mode (struct ata_port *ap, struct ata_device **r_failed);
 static void sil_freeze(struct ata_port *ap);
 static void sil_thaw(struct ata_port *ap);
 
@@ -197,7 +196,7 @@
 	.check_status		= ata_check_status,
 	.exec_command		= ata_exec_command,
 	.dev_select		= ata_std_dev_select,
-	.post_set_mode		= sil_post_set_mode,
+	.set_mode		= sil_set_mode,
 	.bmdma_setup            = ata_bmdma_setup,
 	.bmdma_start            = ata_bmdma_start,
 	.bmdma_stop		= ata_bmdma_stop,
@@ -209,7 +208,6 @@
 	.thaw			= sil_thaw,
 	.error_handler		= ata_bmdma_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
-	.irq_handler		= sil_interrupt,
 	.irq_clear		= ata_bmdma_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -221,7 +219,6 @@
 static const struct ata_port_info sil_port_info[] = {
 	/* sil_3112 */
 	{
-		.sht		= &sil_sht,
 		.flags		= SIL_DFL_PORT_FLAGS | SIL_FLAG_MOD15WRITE,
 		.pio_mask	= 0x1f,			/* pio0-4 */
 		.mwdma_mask	= 0x07,			/* mwdma0-2 */
@@ -230,7 +227,6 @@
 	},
 	/* sil_3112_no_sata_irq */
 	{
-		.sht		= &sil_sht,
 		.flags		= SIL_DFL_PORT_FLAGS | SIL_FLAG_MOD15WRITE |
 				  SIL_FLAG_NO_SATA_IRQ,
 		.pio_mask	= 0x1f,			/* pio0-4 */
@@ -240,7 +236,6 @@
 	},
 	/* sil_3512 */
 	{
-		.sht		= &sil_sht,
 		.flags		= SIL_DFL_PORT_FLAGS | SIL_FLAG_RERR_ON_DMA_ACT,
 		.pio_mask	= 0x1f,			/* pio0-4 */
 		.mwdma_mask	= 0x07,			/* mwdma0-2 */
@@ -249,7 +244,6 @@
 	},
 	/* sil_3114 */
 	{
-		.sht		= &sil_sht,
 		.flags		= SIL_DFL_PORT_FLAGS | SIL_FLAG_RERR_ON_DMA_ACT,
 		.pio_mask	= 0x1f,			/* pio0-4 */
 		.mwdma_mask	= 0x07,			/* mwdma0-2 */
@@ -297,7 +291,16 @@
 	return cache_line;
 }
 
-static void sil_post_set_mode (struct ata_port *ap)
+/**
+ *	sil_set_mode		-	wrap set_mode functions
+ *	@ap: port to set up
+ *	@r_failed: returned device when we fail
+ *
+ *	Wrap the libata method for device setup as after the setup we need
+ *	to inspect the results and do some configuration work
+ */
+
+static int sil_set_mode (struct ata_port *ap, struct ata_device **r_failed)
 {
 	struct ata_host *host = ap->host;
 	struct ata_device *dev;
@@ -305,6 +308,11 @@
 	void __iomem *addr = mmio_base + sil_port[ap->port_no].xfer_mode;
 	u32 tmp, dev_mode[2];
 	unsigned int i;
+	int rc;
+	
+	rc = ata_do_set_mode(ap, r_failed);
+	if (rc)
+		return rc;
 
 	for (i = 0; i < 2; i++) {
 		dev = &ap->device[i];
@@ -323,6 +331,7 @@
 	tmp |= (dev_mode[1] << 4);
 	writel(tmp, addr);
 	readl(addr);	/* flush */
+	return 0;
 }
 
 static inline void __iomem *sil_scr_addr(struct ata_port *ap, unsigned int sc_reg)
@@ -521,7 +530,6 @@
 
 /**
  *	sil_dev_config - Apply device/host-specific errata fixups
- *	@ap: Port containing device to be examined
  *	@dev: Device to be examined
  *
  *	After the IDENTIFY [PACKET] DEVICE step is complete, and a
@@ -548,8 +556,9 @@
  *	appreciated.
  *	- But then again UDMA5 is hardly anything to complain about
  */
-static void sil_dev_config(struct ata_port *ap, struct ata_device *dev)
+static void sil_dev_config(struct ata_device *dev)
 {
+	struct ata_port *ap = dev->ap;
 	int print_info = ap->eh_context.i.flags & ATA_EHI_PRINTINFO;
 	unsigned int n, quirks = 0;
 	unsigned char model_num[ATA_ID_PROD_LEN + 1];
@@ -583,10 +592,10 @@
 	}
 }
 
-static void sil_init_controller(struct pci_dev *pdev,
-				int n_ports, unsigned long port_flags,
-				void __iomem *mmio_base)
+static void sil_init_controller(struct ata_host *host)
 {
+	struct pci_dev *pdev = to_pci_dev(host->dev);
+	void __iomem *mmio_base = host->iomap[SIL_MMIO_BAR];
 	u8 cls;
 	u32 tmp;
 	int i;
@@ -596,7 +605,7 @@
 	if (cls) {
 		cls >>= 3;
 		cls++;  /* cls = (line_size/8)+1 */
-		for (i = 0; i < n_ports; i++)
+		for (i = 0; i < host->n_ports; i++)
 			writew(cls << 8 | cls,
 			       mmio_base + sil_port[i].fifo_cfg);
 	} else
@@ -604,10 +613,10 @@
 			   "cache line size not set.  Driver may not function\n");
 
 	/* Apply R_ERR on DMA activate FIS errata workaround */
-	if (port_flags & SIL_FLAG_RERR_ON_DMA_ACT) {
+	if (host->ports[0]->flags & SIL_FLAG_RERR_ON_DMA_ACT) {
 		int cnt;
 
-		for (i = 0, cnt = 0; i < n_ports; i++) {
+		for (i = 0, cnt = 0; i < host->n_ports; i++) {
 			tmp = readl(mmio_base + sil_port[i].sfis_cfg);
 			if ((tmp & 0x3) != 0x01)
 				continue;
@@ -620,7 +629,7 @@
 		}
 	}
 
-	if (n_ports == 4) {
+	if (host->n_ports == 4) {
 		/* flip the magic "make 4 ports work" bit */
 		tmp = readl(mmio_base + sil_port[2].bmdma);
 		if ((tmp & SIL_INTR_STEERING) == 0)
@@ -632,15 +641,26 @@
 static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	static int printed_version;
-	struct device *dev = &pdev->dev;
-	struct ata_probe_ent *probe_ent;
+	int board_id = ent->driver_data;
+	const struct ata_port_info *ppi[] = { &sil_port_info[board_id], NULL };
+	struct ata_host *host;
 	void __iomem *mmio_base;
-	int rc;
+	int n_ports, rc;
 	unsigned int i;
 
 	if (!printed_version++)
 		dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
 
+	/* allocate host */
+	n_ports = 2;
+	if (board_id == sil_3114)
+		n_ports = 4;
+
+	host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports);
+	if (!host)
+		return -ENOMEM;
+
+	/* acquire resources and fill host */
 	rc = pcim_enable_device(pdev);
 	if (rc)
 		return rc;
@@ -650,6 +670,7 @@
 		pcim_pin_device(pdev);
 	if (rc)
 		return rc;
+	host->iomap = pcim_iomap_table(pdev);
 
 	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
 	if (rc)
@@ -658,45 +679,25 @@
 	if (rc)
 		return rc;
 
-	probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL);
-	if (probe_ent == NULL)
-		return -ENOMEM;
+	mmio_base = host->iomap[SIL_MMIO_BAR];
 
-	INIT_LIST_HEAD(&probe_ent->node);
-	probe_ent->dev = pci_dev_to_dev(pdev);
-	probe_ent->port_ops = sil_port_info[ent->driver_data].port_ops;
-	probe_ent->sht = sil_port_info[ent->driver_data].sht;
-	probe_ent->n_ports = (ent->driver_data == sil_3114) ? 4 : 2;
-	probe_ent->pio_mask = sil_port_info[ent->driver_data].pio_mask;
-	probe_ent->mwdma_mask = sil_port_info[ent->driver_data].mwdma_mask;
-	probe_ent->udma_mask = sil_port_info[ent->driver_data].udma_mask;
-       	probe_ent->irq = pdev->irq;
-       	probe_ent->irq_flags = IRQF_SHARED;
-	probe_ent->port_flags = sil_port_info[ent->driver_data].flags;
+	for (i = 0; i < host->n_ports; i++) {
+		struct ata_ioports *ioaddr = &host->ports[i]->ioaddr;
 
-	probe_ent->iomap = pcim_iomap_table(pdev);
-
-	mmio_base = probe_ent->iomap[SIL_MMIO_BAR];
-
-	for (i = 0; i < probe_ent->n_ports; i++) {
-		probe_ent->port[i].cmd_addr = mmio_base + sil_port[i].tf;
-		probe_ent->port[i].altstatus_addr =
-		probe_ent->port[i].ctl_addr = mmio_base + sil_port[i].ctl;
-		probe_ent->port[i].bmdma_addr = mmio_base + sil_port[i].bmdma;
-		probe_ent->port[i].scr_addr = mmio_base + sil_port[i].scr;
-		ata_std_ports(&probe_ent->port[i]);
+		ioaddr->cmd_addr = mmio_base + sil_port[i].tf;
+		ioaddr->altstatus_addr =
+		ioaddr->ctl_addr = mmio_base + sil_port[i].ctl;
+		ioaddr->bmdma_addr = mmio_base + sil_port[i].bmdma;
+		ioaddr->scr_addr = mmio_base + sil_port[i].scr;
+		ata_std_ports(ioaddr);
 	}
 
-	sil_init_controller(pdev, probe_ent->n_ports, probe_ent->port_flags,
-			    mmio_base);
+	/* initialize and activate */
+	sil_init_controller(host);
 
 	pci_set_master(pdev);
-
-	if (!ata_device_add(probe_ent))
-		return -ENODEV;
-
-	devm_kfree(dev, probe_ent);
-	return 0;
+	return ata_host_activate(host, pdev->irq, sil_interrupt, IRQF_SHARED,
+				 &sil_sht);
 }
 
 #ifdef CONFIG_PM
@@ -709,8 +710,7 @@
 	if (rc)
 		return rc;
 
-	sil_init_controller(pdev, host->n_ports, host->ports[0]->flags,
-			    host->iomap[SIL_MMIO_BAR]);
+	sil_init_controller(host);
 	ata_host_resume(host);
 
 	return 0;
diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c
index 5614df8..e6223ba 100644
--- a/drivers/ata/sata_sil24.c
+++ b/drivers/ata/sata_sil24.c
@@ -323,7 +323,7 @@
 	struct ata_taskfile tf;			/* Cached taskfile registers */
 };
 
-static void sil24_dev_config(struct ata_port *ap, struct ata_device *dev);
+static void sil24_dev_config(struct ata_device *dev);
 static u8 sil24_check_status(struct ata_port *ap);
 static u32 sil24_scr_read(struct ata_port *ap, unsigned sc_reg);
 static void sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val);
@@ -331,7 +331,6 @@
 static void sil24_qc_prep(struct ata_queued_cmd *qc);
 static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc);
 static void sil24_irq_clear(struct ata_port *ap);
-static irqreturn_t sil24_interrupt(int irq, void *dev_instance);
 static void sil24_freeze(struct ata_port *ap);
 static void sil24_thaw(struct ata_port *ap);
 static void sil24_error_handler(struct ata_port *ap);
@@ -401,7 +400,6 @@
 	.qc_prep		= sil24_qc_prep,
 	.qc_issue		= sil24_qc_issue,
 
-	.irq_handler		= sil24_interrupt,
 	.irq_clear		= sil24_irq_clear,
 	.irq_on			= ata_dummy_irq_on,
 	.irq_ack		= ata_dummy_irq_ack,
@@ -424,10 +422,9 @@
 #define SIL24_NPORTS2FLAG(nports)	((((unsigned)(nports) - 1) & 0x3) << 30)
 #define SIL24_FLAG2NPORTS(flag)		((((flag) >> 30) & 0x3) + 1)
 
-static struct ata_port_info sil24_port_info[] = {
+static const struct ata_port_info sil24_port_info[] = {
 	/* sil_3124 */
 	{
-		.sht		= &sil24_sht,
 		.flags		= SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(4) |
 				  SIL24_FLAG_PCIX_IRQ_WOC,
 		.pio_mask	= 0x1f,			/* pio0-4 */
@@ -437,7 +434,6 @@
 	},
 	/* sil_3132 */
 	{
-		.sht		= &sil24_sht,
 		.flags		= SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(2),
 		.pio_mask	= 0x1f,			/* pio0-4 */
 		.mwdma_mask	= 0x07,			/* mwdma0-2 */
@@ -446,7 +442,6 @@
 	},
 	/* sil_3131/sil_3531 */
 	{
-		.sht		= &sil24_sht,
 		.flags		= SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(1),
 		.pio_mask	= 0x1f,			/* pio0-4 */
 		.mwdma_mask	= 0x07,			/* mwdma0-2 */
@@ -462,9 +457,9 @@
 	return tag;
 }
 
-static void sil24_dev_config(struct ata_port *ap, struct ata_device *dev)
+static void sil24_dev_config(struct ata_device *dev)
 {
-	void __iomem *port = ap->ioaddr.cmd_addr;
+	void __iomem *port = dev->ap->ioaddr.cmd_addr;
 
 	if (dev->cdb_len == 16)
 		writel(PORT_CS_CDB16, port + PORT_CTRL_STAT);
@@ -924,11 +919,8 @@
 {
 	struct ata_port *ap = qc->ap;
 
-	if (qc->flags & ATA_QCFLAG_FAILED)
-		qc->err_mask |= AC_ERR_OTHER;
-
 	/* make DMA engine forget about the failed command */
-	if (qc->err_mask)
+	if (qc->flags & ATA_QCFLAG_FAILED)
 		sil24_init_port(ap);
 }
 
@@ -964,11 +956,10 @@
 	return 0;
 }
 
-static void sil24_init_controller(struct pci_dev *pdev, int n_ports,
-				  unsigned long port_flags,
-				  void __iomem *host_base,
-				  void __iomem *port_base)
+static void sil24_init_controller(struct ata_host *host)
 {
+	void __iomem *host_base = host->iomap[SIL24_HOST_BAR];
+	void __iomem *port_base = host->iomap[SIL24_PORT_BAR];
 	u32 tmp;
 	int i;
 
@@ -979,7 +970,7 @@
 	writel(0, host_base + HOST_CTRL);
 
 	/* init ports */
-	for (i = 0; i < n_ports; i++) {
+	for (i = 0; i < host->n_ports; i++) {
 		void __iomem *port = port_base + i * PORT_REGS_SIZE;
 
 		/* Initial PHY setting */
@@ -993,12 +984,12 @@
 						PORT_CS_PORT_RST,
 						PORT_CS_PORT_RST, 10, 100);
 			if (tmp & PORT_CS_PORT_RST)
-				dev_printk(KERN_ERR, &pdev->dev,
+				dev_printk(KERN_ERR, host->dev,
 				           "failed to clear port RST\n");
 		}
 
 		/* Configure IRQ WoC */
-		if (port_flags & SIL24_FLAG_PCIX_IRQ_WOC)
+		if (host->ports[0]->flags & SIL24_FLAG_PCIX_IRQ_WOC)
 			writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_STAT);
 		else
 			writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR);
@@ -1026,18 +1017,17 @@
 static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	static int printed_version = 0;
-	struct device *dev = &pdev->dev;
-	unsigned int board_id = (unsigned int)ent->driver_data;
-	struct ata_port_info *pinfo = &sil24_port_info[board_id];
-	struct ata_probe_ent *probe_ent;
-	void __iomem *host_base;
-	void __iomem *port_base;
+	struct ata_port_info pi = sil24_port_info[ent->driver_data];
+	const struct ata_port_info *ppi[] = { &pi, NULL };
+	void __iomem * const *iomap;
+	struct ata_host *host;
 	int i, rc;
 	u32 tmp;
 
 	if (!printed_version++)
 		dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
 
+	/* acquire resources */
 	rc = pcim_enable_device(pdev);
 	if (rc)
 		return rc;
@@ -1047,33 +1037,36 @@
 				DRV_NAME);
 	if (rc)
 		return rc;
+	iomap = pcim_iomap_table(pdev);
 
-	/* allocate & init probe_ent */
-	probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL);
-	if (!probe_ent)
+	/* apply workaround for completion IRQ loss on PCI-X errata */
+	if (pi.flags & SIL24_FLAG_PCIX_IRQ_WOC) {
+		tmp = readl(iomap[SIL24_HOST_BAR] + HOST_CTRL);
+		if (tmp & (HOST_CTRL_TRDY | HOST_CTRL_STOP | HOST_CTRL_DEVSEL))
+			dev_printk(KERN_INFO, &pdev->dev,
+				   "Applying completion IRQ loss on PCI-X "
+				   "errata fix\n");
+		else
+			pi.flags &= ~SIL24_FLAG_PCIX_IRQ_WOC;
+	}
+
+	/* allocate and fill host */
+	host = ata_host_alloc_pinfo(&pdev->dev, ppi,
+				    SIL24_FLAG2NPORTS(ppi[0]->flags));
+	if (!host)
 		return -ENOMEM;
+	host->iomap = iomap;
 
-	probe_ent->dev = pci_dev_to_dev(pdev);
-	INIT_LIST_HEAD(&probe_ent->node);
+	for (i = 0; i < host->n_ports; i++) {
+		void __iomem *port = iomap[SIL24_PORT_BAR] + i * PORT_REGS_SIZE;
 
-	probe_ent->sht		= pinfo->sht;
-	probe_ent->port_flags	= pinfo->flags;
-	probe_ent->pio_mask	= pinfo->pio_mask;
-	probe_ent->mwdma_mask	= pinfo->mwdma_mask;
-	probe_ent->udma_mask	= pinfo->udma_mask;
-	probe_ent->port_ops	= pinfo->port_ops;
-	probe_ent->n_ports	= SIL24_FLAG2NPORTS(pinfo->flags);
+		host->ports[i]->ioaddr.cmd_addr = port;
+		host->ports[i]->ioaddr.scr_addr = port + PORT_SCONTROL;
 
-	probe_ent->irq = pdev->irq;
-	probe_ent->irq_flags = IRQF_SHARED;
-	probe_ent->iomap = pcim_iomap_table(pdev);
+		ata_std_ports(&host->ports[i]->ioaddr);
+	}
 
-	host_base = probe_ent->iomap[SIL24_HOST_BAR];
-	port_base = probe_ent->iomap[SIL24_PORT_BAR];
-
-	/*
-	 * Configure the device
-	 */
+	/* configure and activate the device */
 	if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) {
 		rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);
 		if (rc) {
@@ -1099,36 +1092,11 @@
 		}
 	}
 
-	/* Apply workaround for completion IRQ loss on PCI-X errata */
-	if (probe_ent->port_flags & SIL24_FLAG_PCIX_IRQ_WOC) {
-		tmp = readl(host_base + HOST_CTRL);
-		if (tmp & (HOST_CTRL_TRDY | HOST_CTRL_STOP | HOST_CTRL_DEVSEL))
-			dev_printk(KERN_INFO, &pdev->dev,
-				   "Applying completion IRQ loss on PCI-X "
-				   "errata fix\n");
-		else
-			probe_ent->port_flags &= ~SIL24_FLAG_PCIX_IRQ_WOC;
-	}
-
-	for (i = 0; i < probe_ent->n_ports; i++) {
-		void __iomem *port = port_base + i * PORT_REGS_SIZE;
-
-		probe_ent->port[i].cmd_addr = port;
-		probe_ent->port[i].scr_addr = port + PORT_SCONTROL;
-
-		ata_std_ports(&probe_ent->port[i]);
-	}
-
-	sil24_init_controller(pdev, probe_ent->n_ports, probe_ent->port_flags,
-			      host_base, port_base);
+	sil24_init_controller(host);
 
 	pci_set_master(pdev);
-
-	if (!ata_device_add(probe_ent))
-		return -ENODEV;
-
-	devm_kfree(dev, probe_ent);
-	return 0;
+	return ata_host_activate(host, pdev->irq, sil24_interrupt, IRQF_SHARED,
+				 &sil24_sht);
 }
 
 #ifdef CONFIG_PM
@@ -1136,7 +1104,6 @@
 {
 	struct ata_host *host = dev_get_drvdata(&pdev->dev);
 	void __iomem *host_base = host->iomap[SIL24_HOST_BAR];
-	void __iomem *port_base = host->iomap[SIL24_PORT_BAR];
 	int rc;
 
 	rc = ata_pci_device_do_resume(pdev);
@@ -1146,8 +1113,7 @@
 	if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND)
 		writel(HOST_CTRL_GLOBAL_RST, host_base + HOST_CTRL);
 
-	sil24_init_controller(pdev, host->n_ports, host->ports[0]->flags,
-			      host_base, port_base);
+	sil24_init_controller(host);
 
 	ata_host_resume(host);
 
diff --git a/drivers/ata/sata_sis.c b/drivers/ata/sata_sis.c
index a787f0d..d8ee062 100644
--- a/drivers/ata/sata_sis.c
+++ b/drivers/ata/sata_sis.c
@@ -121,7 +121,6 @@
 	.thaw			= ata_bmdma_thaw,
 	.error_handler		= ata_bmdma_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
-	.irq_handler		= ata_interrupt,
 	.irq_clear		= ata_bmdma_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -131,7 +130,6 @@
 };
 
 static struct ata_port_info sis_port_info = {
-	.sht		= &sis_sht,
 	.flags		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY,
 	.pio_mask	= 0x1f,
 	.mwdma_mask	= 0x7,
@@ -256,12 +254,13 @@
 static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	static int printed_version;
-	struct ata_probe_ent *probe_ent = NULL;
-	int rc;
+	struct ata_port_info pi = sis_port_info;
+	const struct ata_port_info *ppi[2] = { &pi, &pi };
+	struct ata_host *host;
 	u32 genctl, val;
-	struct ata_port_info pi = sis_port_info, *ppi[2] = { &pi, &pi };
 	u8 pmr;
 	u8 port2_start = 0x20;
+	int rc;
 
 	if (!printed_version++)
 		dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n");
@@ -270,19 +269,6 @@
 	if (rc)
 		return rc;
 
-	rc = pci_request_regions(pdev, DRV_NAME);
-	if (rc) {
-		pcim_pin_device(pdev);
-		return rc;
-	}
-
-	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
-	if (rc)
-		return rc;
-	rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
-	if (rc)
-		return rc;
-
 	/* check and see if the SCRs are in IO space or PCI cfg space */
 	pci_read_config_dword(pdev, SIS_GENCTL, &genctl);
 	if ((genctl & GENCTL_IOMAPPED_SCR) == 0)
@@ -349,30 +335,26 @@
 		break;
 	}
 
-	probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
-	if (!probe_ent)
-		return -ENOMEM;
+	rc = ata_pci_prepare_native_host(pdev, ppi, 2, &host);
+	if (rc)
+		return rc;
 
-	if (!(probe_ent->port_flags & SIS_FLAG_CFGSCR)) {
+	if (!(pi.flags & SIS_FLAG_CFGSCR)) {
 		void __iomem *mmio;
 
-		mmio = pcim_iomap(pdev, SIS_SCR_PCI_BAR, 0);
-		if (!mmio)
-			return -ENOMEM;
+		rc = pcim_iomap_regions(pdev, 1 << SIS_SCR_PCI_BAR, DRV_NAME);
+		if (rc)
+			return rc;
+		mmio = host->iomap[SIS_SCR_PCI_BAR];
 
-		probe_ent->port[0].scr_addr = mmio;
-		probe_ent->port[1].scr_addr = mmio + port2_start;
+		host->ports[0]->ioaddr.scr_addr = mmio;
+		host->ports[1]->ioaddr.scr_addr = mmio + port2_start;
 	}
 
 	pci_set_master(pdev);
 	pci_intx(pdev, 1);
-
-	if (!ata_device_add(probe_ent))
-		return -EIO;
-
-	devm_kfree(&pdev->dev, probe_ent);
-	return 0;
-
+	return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED,
+				 &sis_sht);
 }
 
 static int __init sis_init(void)
diff --git a/drivers/ata/sata_svw.c b/drivers/ata/sata_svw.c
index b121195..cc07aac 100644
--- a/drivers/ata/sata_svw.c
+++ b/drivers/ata/sata_svw.c
@@ -56,7 +56,9 @@
 #define DRV_VERSION	"2.1"
 
 enum {
-	K2_FLAG_NO_ATAPI_DMA		= (1 << 29),
+	/* ap->flags bits */
+	K2_FLAG_SATA_8_PORTS		= (1 << 24),
+	K2_FLAG_NO_ATAPI_DMA		= (1 << 25),
 
 	/* Taskfile registers offsets */
 	K2_SATA_TF_CMD_OFFSET		= 0x00,
@@ -90,17 +92,6 @@
 	board_svw8			= 1,
 };
 
-static const struct k2_board_info {
-	unsigned int		n_ports;
-	unsigned long		port_flags;
-} k2_board_info[] = {
-	/* board_svw4 */
-	{ 4, K2_FLAG_NO_ATAPI_DMA },
-
-	/* board_svw8 */
-	{ 8, K2_FLAG_NO_ATAPI_DMA },
-};
-
 static u8 k2_stat_check_status(struct ata_port *ap);
 
 
@@ -354,7 +345,6 @@
 	.thaw			= ata_bmdma_thaw,
 	.error_handler		= ata_bmdma_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
-	.irq_handler		= ata_interrupt,
 	.irq_clear		= ata_bmdma_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -363,6 +353,28 @@
 	.port_start		= ata_port_start,
 };
 
+static const struct ata_port_info k2_port_info[] = {
+	/* board_svw4 */
+	{
+		.flags		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+				  ATA_FLAG_MMIO | K2_FLAG_NO_ATAPI_DMA,
+		.pio_mask	= 0x1f,
+		.mwdma_mask	= 0x07,
+		.udma_mask	= 0x7f,
+		.port_ops	= &k2_sata_ops,
+	},
+	/* board_svw8 */
+	{
+		.flags		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+				  ATA_FLAG_MMIO | K2_FLAG_NO_ATAPI_DMA |
+				  K2_FLAG_SATA_8_PORTS,
+		.pio_mask	= 0x1f,
+		.mwdma_mask	= 0x07,
+		.udma_mask	= 0x7f,
+		.port_ops	= &k2_sata_ops,
+	},
+};
+
 static void k2_sata_setup_port(struct ata_ioports *port, void __iomem *base)
 {
 	port->cmd_addr		= base + K2_SATA_TF_CMD_OFFSET;
@@ -386,17 +398,24 @@
 static int k2_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	static int printed_version;
-	struct device *dev = &pdev->dev;
-	struct ata_probe_ent *probe_ent;
+	const struct ata_port_info *ppi[] =
+		{ &k2_port_info[ent->driver_data], NULL };
+	struct ata_host *host;
 	void __iomem *mmio_base;
-	const struct k2_board_info *board_info =
-			&k2_board_info[ent->driver_data];
-	int rc;
-	int i;
+	int n_ports, i, rc;
 
 	if (!printed_version++)
 		dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
 
+	/* allocate host */
+	n_ports = 4;
+	if (ppi[0]->flags & K2_FLAG_SATA_8_PORTS)
+		n_ports = 8;
+
+	host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports);
+	if (!host)
+		return -ENOMEM;
+
 	/*
 	 * If this driver happens to only be useful on Apple's K2, then
 	 * we should check that here as it has a normal Serverworks ID
@@ -404,6 +423,7 @@
 	rc = pcim_enable_device(pdev);
 	if (rc)
 		return rc;
+
 	/*
 	 * Check if we have resources mapped at all (second function may
 	 * have been disabled by firmware)
@@ -417,6 +437,15 @@
 		pcim_pin_device(pdev);
 	if (rc)
 		return rc;
+	host->iomap = pcim_iomap_table(pdev);
+	mmio_base = host->iomap[5];
+
+	/* different controllers have different number of ports - currently 4 or 8 */
+	/* All ports are on the same function. Multi-function device is no
+	 * longer available. This should not be seen in any system. */
+	for (i = 0; i < host->n_ports; i++)
+		k2_sata_setup_port(&host->ports[i]->ioaddr,
+				   mmio_base + i * K2_SATA_PORT_OFFSET);
 
 	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
 	if (rc)
@@ -425,38 +454,6 @@
 	if (rc)
 		return rc;
 
-	probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL);
-	if (probe_ent == NULL)
-		return -ENOMEM;
-
-	probe_ent->dev = pci_dev_to_dev(pdev);
-	INIT_LIST_HEAD(&probe_ent->node);
-
-	probe_ent->sht = &k2_sata_sht;
-	probe_ent->port_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
-				ATA_FLAG_MMIO | board_info->port_flags;
-	probe_ent->port_ops = &k2_sata_ops;
-	probe_ent->n_ports = 4;
-	probe_ent->irq = pdev->irq;
-	probe_ent->irq_flags = IRQF_SHARED;
-	probe_ent->iomap = pcim_iomap_table(pdev);
-
-	/* We don't care much about the PIO/UDMA masks, but the core won't like us
-	 * if we don't fill these
-	 */
-	probe_ent->pio_mask = 0x1f;
-	probe_ent->mwdma_mask = 0x7;
-	probe_ent->udma_mask = 0x7f;
-
-	mmio_base = probe_ent->iomap[5];
-
-	/* different controllers have different number of ports - currently 4 or 8 */
-	/* All ports are on the same function. Multi-function device is no
-	 * longer available. This should not be seen in any system. */
-	for (i = 0; i < board_info->n_ports; i++)
-		k2_sata_setup_port(&probe_ent->port[i],
-				   mmio_base + i * K2_SATA_PORT_OFFSET);
-
 	/* Clear a magic bit in SCR1 according to Darwin, those help
 	 * some funky seagate drives (though so far, those were already
 	 * set by the firmware on the machines I had access to)
@@ -469,12 +466,8 @@
 	writel(0x0, mmio_base + K2_SATA_SIM_OFFSET);
 
 	pci_set_master(pdev);
-
-	if (!ata_device_add(probe_ent))
-		return -ENODEV;
-
-	devm_kfree(dev, probe_ent);
-	return 0;
+	return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED,
+				 &k2_sata_sht);
 }
 
 /* 0x240 is device ID for Apple K2 device
diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c
index 1a081c3..3a4f445 100644
--- a/drivers/ata/sata_sx4.c
+++ b/drivers/ata/sata_sx4.c
@@ -151,24 +151,23 @@
 
 
 static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
-static irqreturn_t pdc20621_interrupt (int irq, void *dev_instance);
 static void pdc_eng_timeout(struct ata_port *ap);
 static void pdc_20621_phy_reset (struct ata_port *ap);
 static int pdc_port_start(struct ata_port *ap);
 static void pdc20621_qc_prep(struct ata_queued_cmd *qc);
 static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf);
 static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf);
-static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe);
-static int pdc20621_detect_dimm(struct ata_probe_ent *pe);
-static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe,
+static unsigned int pdc20621_dimm_init(struct ata_host *host);
+static int pdc20621_detect_dimm(struct ata_host *host);
+static unsigned int pdc20621_i2c_read(struct ata_host *host,
 				      u32 device, u32 subaddr, u32 *pdata);
-static int pdc20621_prog_dimm0(struct ata_probe_ent *pe);
-static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe);
+static int pdc20621_prog_dimm0(struct ata_host *host);
+static unsigned int pdc20621_prog_dimm_global(struct ata_host *host);
 #ifdef ATA_VERBOSE_DEBUG
-static void pdc20621_get_from_dimm(struct ata_probe_ent *pe,
+static void pdc20621_get_from_dimm(struct ata_host *host,
 				   void *psource, u32 offset, u32 size);
 #endif
-static void pdc20621_put_to_dimm(struct ata_probe_ent *pe,
+static void pdc20621_put_to_dimm(struct ata_host *host,
 				 void *psource, u32 offset, u32 size);
 static void pdc20621_irq_clear(struct ata_port *ap);
 static unsigned int pdc20621_qc_issue_prot(struct ata_queued_cmd *qc);
@@ -204,7 +203,6 @@
 	.qc_issue		= pdc20621_qc_issue_prot,
 	.data_xfer		= ata_data_xfer,
 	.eng_timeout		= pdc_eng_timeout,
-	.irq_handler		= pdc20621_interrupt,
 	.irq_clear		= pdc20621_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -214,7 +212,6 @@
 static const struct ata_port_info pdc_port_info[] = {
 	/* board_20621 */
 	{
-		.sht		= &pdc_sata_sht,
 		.flags		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
 				  ATA_FLAG_SRST | ATA_FLAG_MMIO |
 				  ATA_FLAG_NO_ATAPI | ATA_FLAG_PIO_POLLING,
@@ -882,15 +879,15 @@
 
 
 #ifdef ATA_VERBOSE_DEBUG
-static void pdc20621_get_from_dimm(struct ata_probe_ent *pe, void *psource,
+static void pdc20621_get_from_dimm(struct ata_host *host, void *psource,
 				   u32 offset, u32 size)
 {
 	u32 window_size;
 	u16 idx;
 	u8 page_mask;
 	long dist;
-	void __iomem *mmio = pe->iomap[PDC_MMIO_BAR];
-	void __iomem *dimm_mmio = pe->iomap[PDC_DIMM_BAR];
+	void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
+	void __iomem *dimm_mmio = host->iomap[PDC_DIMM_BAR];
 
 	/* hard-code chip #0 */
 	mmio += PDC_CHIP0_OFS;
@@ -937,15 +934,15 @@
 #endif
 
 
-static void pdc20621_put_to_dimm(struct ata_probe_ent *pe, void *psource,
+static void pdc20621_put_to_dimm(struct ata_host *host, void *psource,
 				 u32 offset, u32 size)
 {
 	u32 window_size;
 	u16 idx;
 	u8 page_mask;
 	long dist;
-	void __iomem *mmio = pe->iomap[PDC_MMIO_BAR];
-	void __iomem *dimm_mmio = pe->iomap[PDC_DIMM_BAR];
+	void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
+	void __iomem *dimm_mmio = host->iomap[PDC_DIMM_BAR];
 
 	/* hard-code chip #0 */
 	mmio += PDC_CHIP0_OFS;
@@ -987,10 +984,10 @@
 }
 
 
-static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe, u32 device,
+static unsigned int pdc20621_i2c_read(struct ata_host *host, u32 device,
 				      u32 subaddr, u32 *pdata)
 {
-	void __iomem *mmio = pe->iomap[PDC_MMIO_BAR];
+	void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
 	u32 i2creg  = 0;
 	u32 status;
 	u32 count =0;
@@ -1023,17 +1020,17 @@
 }
 
 
-static int pdc20621_detect_dimm(struct ata_probe_ent *pe)
+static int pdc20621_detect_dimm(struct ata_host *host)
 {
 	u32 data=0 ;
-  	if (pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS,
+	if (pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS,
 			     PDC_DIMM_SPD_SYSTEM_FREQ, &data)) {
    		if (data == 100)
 			return 100;
   	} else
 		return 0;
 
-   	if (pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, 9, &data)) {
+	if (pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, 9, &data)) {
 		if(data <= 0x75)
 			return 133;
    	} else
@@ -1043,13 +1040,13 @@
 }
 
 
-static int pdc20621_prog_dimm0(struct ata_probe_ent *pe)
+static int pdc20621_prog_dimm0(struct ata_host *host)
 {
 	u32 spd0[50];
 	u32 data = 0;
    	int size, i;
    	u8 bdimmsize;
-	void __iomem *mmio = pe->iomap[PDC_MMIO_BAR];
+	void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
 	static const struct {
 		unsigned int reg;
 		unsigned int ofs;
@@ -1072,7 +1069,7 @@
 	mmio += PDC_CHIP0_OFS;
 
 	for(i=0; i<ARRAY_SIZE(pdc_i2c_read_data); i++)
-		pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS,
+		pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS,
 				  pdc_i2c_read_data[i].reg,
 				  &spd0[pdc_i2c_read_data[i].ofs]);
 
@@ -1108,11 +1105,11 @@
 }
 
 
-static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe)
+static unsigned int pdc20621_prog_dimm_global(struct ata_host *host)
 {
 	u32 data, spd0;
 	int error, i;
-	void __iomem *mmio = pe->iomap[PDC_MMIO_BAR];
+	void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
 
 	/* hard-code chip #0 */
    	mmio += PDC_CHIP0_OFS;
@@ -1129,7 +1126,7 @@
 	readl(mmio + PDC_SDRAM_CONTROL_OFFSET);
 
 	/* Turn on for ECC */
-	pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS,
+	pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS,
 			  PDC_DIMM_SPD_TYPE, &spd0);
 	if (spd0 == 0x02) {
 		data |= (0x01 << 16);
@@ -1156,7 +1153,7 @@
 }
 
 
-static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe)
+static unsigned int pdc20621_dimm_init(struct ata_host *host)
 {
 	int speed, size, length;
 	u32 addr,spd0,pci_status;
@@ -1166,7 +1163,7 @@
 	u32 ticks=0;
 	u32 clock=0;
 	u32 fparam=0;
-	void __iomem *mmio = pe->iomap[PDC_MMIO_BAR];
+	void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
 
 	/* hard-code chip #0 */
    	mmio += PDC_CHIP0_OFS;
@@ -1225,18 +1222,18 @@
 	   Read SPD of DIMM by I2C interface,
 	   and program the DIMM Module Controller.
 	*/
- 	if (!(speed = pdc20621_detect_dimm(pe))) {
+	if (!(speed = pdc20621_detect_dimm(host))) {
 		printk(KERN_ERR "Detect Local DIMM Fail\n");
 		return 1;	/* DIMM error */
    	}
    	VPRINTK("Local DIMM Speed = %d\n", speed);
 
    	/* Programming DIMM0 Module Control Register (index_CID0:80h) */
-   	size = pdc20621_prog_dimm0(pe);
+	size = pdc20621_prog_dimm0(host);
    	VPRINTK("Local DIMM Size = %dMB\n",size);
 
    	/* Programming DIMM Module Global Control Register (index_CID0:88h) */
-   	if (pdc20621_prog_dimm_global(pe)) {
+	if (pdc20621_prog_dimm_global(host)) {
 		printk(KERN_ERR "Programming DIMM Module Global Control Register Fail\n");
 		return 1;
    	}
@@ -1249,20 +1246,20 @@
   				'9','8','0','3','1','6','1','2',0,0};
 		u8 test_parttern2[40] = {0};
 
-		pdc20621_put_to_dimm(pe, (void *) test_parttern2, 0x10040, 40);
-		pdc20621_put_to_dimm(pe, (void *) test_parttern2, 0x40, 40);
+		pdc20621_put_to_dimm(host, (void *) test_parttern2, 0x10040, 40);
+		pdc20621_put_to_dimm(host, (void *) test_parttern2, 0x40, 40);
 
-		pdc20621_put_to_dimm(pe, (void *) test_parttern1, 0x10040, 40);
-		pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x40, 40);
+		pdc20621_put_to_dimm(host, (void *) test_parttern1, 0x10040, 40);
+		pdc20621_get_from_dimm(host, (void *) test_parttern2, 0x40, 40);
 		printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0],
 		       test_parttern2[1], &(test_parttern2[2]));
-		pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x10040,
+		pdc20621_get_from_dimm(host, (void *) test_parttern2, 0x10040,
 				       40);
 		printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0],
 		       test_parttern2[1], &(test_parttern2[2]));
 
-		pdc20621_put_to_dimm(pe, (void *) test_parttern1, 0x40, 40);
-		pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x40, 40);
+		pdc20621_put_to_dimm(host, (void *) test_parttern1, 0x40, 40);
+		pdc20621_get_from_dimm(host, (void *) test_parttern2, 0x40, 40);
 		printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0],
 		       test_parttern2[1], &(test_parttern2[2]));
 	}
@@ -1270,14 +1267,14 @@
 
 	/* ECC initiliazation. */
 
-	pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS,
+	pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS,
 			  PDC_DIMM_SPD_TYPE, &spd0);
 	if (spd0 == 0x02) {
 		VPRINTK("Start ECC initialization\n");
 		addr = 0;
 		length = size * 1024 * 1024;
 		while (addr < length) {
-			pdc20621_put_to_dimm(pe, (void *) &tmp, addr,
+			pdc20621_put_to_dimm(host, (void *) &tmp, addr,
 					     sizeof(u32));
 			addr += sizeof(u32);
 		}
@@ -1287,10 +1284,10 @@
 }
 
 
-static void pdc_20621_init(struct ata_probe_ent *pe)
+static void pdc_20621_init(struct ata_host *host)
 {
 	u32 tmp;
-	void __iomem *mmio = pe->iomap[PDC_MMIO_BAR];
+	void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
 
 	/* hard-code chip #0 */
 	mmio += PDC_CHIP0_OFS;
@@ -1321,15 +1318,25 @@
 static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	static int printed_version;
-	struct ata_probe_ent *probe_ent;
+	const struct ata_port_info *ppi[] =
+		{ &pdc_port_info[ent->driver_data], NULL };
+	struct ata_host *host;
 	void __iomem *base;
 	struct pdc_host_priv *hpriv;
-	unsigned int board_idx = (unsigned int) ent->driver_data;
 	int rc;
 
 	if (!printed_version++)
 		dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
 
+	/* allocate host */
+	host = ata_host_alloc_pinfo(&pdev->dev, ppi, 4);
+	hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL);
+	if (!host || !hpriv)
+		return -ENOMEM;
+
+	host->private_data = hpriv;
+
+	/* acquire resources and fill host */
 	rc = pcim_enable_device(pdev);
 	if (rc)
 		return rc;
@@ -1340,7 +1347,15 @@
 		pcim_pin_device(pdev);
 	if (rc)
 		return rc;
+	host->iomap = pcim_iomap_table(pdev);
 
+	base = host->iomap[PDC_MMIO_BAR] + PDC_CHIP0_OFS;
+	pdc_sata_setup_port(&host->ports[0]->ioaddr, base + 0x200);
+	pdc_sata_setup_port(&host->ports[1]->ioaddr, base + 0x280);
+	pdc_sata_setup_port(&host->ports[2]->ioaddr, base + 0x300);
+	pdc_sata_setup_port(&host->ports[3]->ioaddr, base + 0x380);
+
+	/* configure and activate */
 	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
 	if (rc)
 		return rc;
@@ -1348,50 +1363,13 @@
 	if (rc)
 		return rc;
 
-	probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL);
-	if (probe_ent == NULL)
+	if (pdc20621_dimm_init(host))
 		return -ENOMEM;
-
-	probe_ent->dev = pci_dev_to_dev(pdev);
-	INIT_LIST_HEAD(&probe_ent->node);
-
-	hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL);
-	if (!hpriv)
-		return -ENOMEM;
-
-	probe_ent->sht		= pdc_port_info[board_idx].sht;
-	probe_ent->port_flags	= pdc_port_info[board_idx].flags;
-	probe_ent->pio_mask	= pdc_port_info[board_idx].pio_mask;
-	probe_ent->mwdma_mask	= pdc_port_info[board_idx].mwdma_mask;
-	probe_ent->udma_mask	= pdc_port_info[board_idx].udma_mask;
-	probe_ent->port_ops	= pdc_port_info[board_idx].port_ops;
-
-       	probe_ent->irq = pdev->irq;
-       	probe_ent->irq_flags = IRQF_SHARED;
-	probe_ent->iomap = pcim_iomap_table(pdev);
-
-	probe_ent->private_data = hpriv;
-	base = probe_ent->iomap[PDC_MMIO_BAR] + PDC_CHIP0_OFS;
-
-	probe_ent->n_ports = 4;
-	pdc_sata_setup_port(&probe_ent->port[0], base + 0x200);
-	pdc_sata_setup_port(&probe_ent->port[1], base + 0x280);
-	pdc_sata_setup_port(&probe_ent->port[2], base + 0x300);
-	pdc_sata_setup_port(&probe_ent->port[3], base + 0x380);
+	pdc_20621_init(host);
 
 	pci_set_master(pdev);
-
-	/* initialize adapter */
-	/* initialize local dimm */
-	if (pdc20621_dimm_init(probe_ent))
-		return -ENOMEM;
-	pdc_20621_init(probe_ent);
-
-	if (!ata_device_add(probe_ent))
-		return -ENODEV;
-
-	devm_kfree(&pdev->dev, probe_ent);
-	return 0;
+	return ata_host_activate(host, pdev->irq, pdc20621_interrupt,
+				 IRQF_SHARED, &pdc_sata_sht);
 }
 
 
diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c
index d659ace..f74e383 100644
--- a/drivers/ata/sata_uli.c
+++ b/drivers/ata/sata_uli.c
@@ -115,7 +115,6 @@
 	.error_handler		= ata_bmdma_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
 
-	.irq_handler		= ata_interrupt,
 	.irq_clear		= ata_bmdma_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -127,7 +126,6 @@
 };
 
 static struct ata_port_info uli_port_info = {
-	.sht            = &uli_sht,
 	.flags		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
 			  ATA_FLAG_IGN_SIMPLEX,
 	.pio_mask       = 0x1f,		/* pio0-4 */
@@ -185,12 +183,13 @@
 static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	static int printed_version;
-	struct ata_probe_ent *probe_ent;
-	struct ata_port_info *ppi[2];
-	int rc;
+	const struct ata_port_info *ppi[] = { &uli_port_info, NULL };
 	unsigned int board_idx = (unsigned int) ent->driver_data;
+	struct ata_host *host;
 	struct uli_priv *hpriv;
 	void __iomem * const *iomap;
+	struct ata_ioports *ioaddr;
+	int n_ports, rc;
 
 	if (!printed_version++)
 		dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n");
@@ -199,54 +198,42 @@
 	if (rc)
 		return rc;
 
-	rc = pci_request_regions(pdev, DRV_NAME);
-	if (rc) {
-		pcim_pin_device(pdev);
-		return rc;
-	}
-
-	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
+	n_ports = 2;
+	if (board_idx == uli_5287)
+		n_ports = 4;
+	rc = ata_pci_prepare_native_host(pdev, ppi, n_ports, &host);
 	if (rc)
 		return rc;
-	rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
-	if (rc)
-		return rc;
-
-	ppi[0] = ppi[1] = &uli_port_info;
-	probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
-	if (!probe_ent)
-		return -ENOMEM;
 
 	hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL);
 	if (!hpriv)
 		return -ENOMEM;
+	host->private_data = hpriv;
 
-	probe_ent->private_data = hpriv;
-
-	iomap = pcim_iomap_table(pdev);
+	iomap = host->iomap;
 
 	switch (board_idx) {
 	case uli_5287:
 		hpriv->scr_cfg_addr[0] = ULI5287_BASE;
 		hpriv->scr_cfg_addr[1] = ULI5287_BASE + ULI5287_OFFS;
-       		probe_ent->n_ports = 4;
 
-		probe_ent->port[2].cmd_addr = iomap[0] + 8;
-		probe_ent->port[2].altstatus_addr =
-		probe_ent->port[2].ctl_addr = (void __iomem *)
+		ioaddr = &host->ports[2]->ioaddr;
+		ioaddr->cmd_addr = iomap[0] + 8;
+		ioaddr->altstatus_addr =
+		ioaddr->ctl_addr = (void __iomem *)
 			((unsigned long)iomap[1] | ATA_PCI_CTL_OFS) + 4;
-		probe_ent->port[2].bmdma_addr = iomap[4] + 16;
+		ioaddr->bmdma_addr = iomap[4] + 16;
 		hpriv->scr_cfg_addr[2] = ULI5287_BASE + ULI5287_OFFS*4;
+		ata_std_ports(ioaddr);
 
-		probe_ent->port[3].cmd_addr = iomap[2] + 8;
-		probe_ent->port[3].altstatus_addr =
-		probe_ent->port[3].ctl_addr = (void __iomem *)
+		ioaddr = &host->ports[3]->ioaddr;
+		ioaddr->cmd_addr = iomap[2] + 8;
+		ioaddr->altstatus_addr =
+		ioaddr->ctl_addr = (void __iomem *)
 			((unsigned long)iomap[3] | ATA_PCI_CTL_OFS) + 4;
-		probe_ent->port[3].bmdma_addr = iomap[4] + 24;
+		ioaddr->bmdma_addr = iomap[4] + 24;
 		hpriv->scr_cfg_addr[3] = ULI5287_BASE + ULI5287_OFFS*5;
-
-		ata_std_ports(&probe_ent->port[2]);
-		ata_std_ports(&probe_ent->port[3]);
+		ata_std_ports(ioaddr);
 		break;
 
 	case uli_5289:
@@ -266,12 +253,8 @@
 
 	pci_set_master(pdev);
 	pci_intx(pdev, 1);
-
-	if (!ata_device_add(probe_ent))
-		return -ENODEV;
-
-	devm_kfree(&pdev->dev, probe_ent);
-	return 0;
+	return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED,
+				 &uli_sht);
 }
 
 static int __init uli_init(void)
diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c
index 598e6a2..1d855f5 100644
--- a/drivers/ata/sata_via.c
+++ b/drivers/ata/sata_via.c
@@ -64,8 +64,6 @@
 	PORT0			= (1 << 1),
 	PORT1			= (1 << 0),
 	ALL_PORTS		= PORT0 | PORT1,
-	PATA_PORT		= 2,	/* PATA is port 2 */
-	N_PORTS			= 3,
 
 	NATIVE_MODE_ALL		= (1 << 7) | (1 << 6) | (1 << 5) | (1 << 4),
 
@@ -78,11 +76,9 @@
 static void svia_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
 static void svia_noop_freeze(struct ata_port *ap);
 static void vt6420_error_handler(struct ata_port *ap);
-static void vt6421_sata_error_handler(struct ata_port *ap);
-static void vt6421_pata_error_handler(struct ata_port *ap);
+static int vt6421_pata_cable_detect(struct ata_port *ap);
 static void vt6421_set_pio_mode(struct ata_port *ap, struct ata_device *adev);
 static void vt6421_set_dma_mode(struct ata_port *ap, struct ata_device *adev);
-static int vt6421_port_start(struct ata_port *ap);
 
 static const struct pci_device_id svia_pci_tbl[] = {
 	{ PCI_VDEVICE(VIA, 0x5337), vt6420 },
@@ -141,7 +137,6 @@
 	.error_handler		= vt6420_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
 
-	.irq_handler		= ata_interrupt,
 	.irq_clear		= ata_bmdma_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -172,15 +167,15 @@
 
 	.freeze			= ata_bmdma_freeze,
 	.thaw			= ata_bmdma_thaw,
-	.error_handler		= vt6421_pata_error_handler,
+	.error_handler		= ata_bmdma_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
+	.cable_detect		= vt6421_pata_cable_detect,
 
-	.irq_handler		= ata_interrupt,
 	.irq_clear		= ata_bmdma_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
 
-	.port_start		= vt6421_port_start,
+	.port_start		= ata_port_start,
 };
 
 static const struct ata_port_operations vt6421_sata_ops = {
@@ -203,10 +198,10 @@
 
 	.freeze			= ata_bmdma_freeze,
 	.thaw			= ata_bmdma_thaw,
-	.error_handler		= vt6421_sata_error_handler,
+	.error_handler		= ata_bmdma_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
+	.cable_detect		= ata_cable_sata,
 
-	.irq_handler		= ata_interrupt,
 	.irq_clear		= ata_bmdma_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -214,11 +209,10 @@
 	.scr_read		= svia_scr_read,
 	.scr_write		= svia_scr_write,
 
-	.port_start		= vt6421_port_start,
+	.port_start		= ata_port_start,
 };
 
-static struct ata_port_info vt6420_port_info = {
-	.sht		= &svia_sht,
+static const struct ata_port_info vt6420_port_info = {
 	.flags		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY,
 	.pio_mask	= 0x1f,
 	.mwdma_mask	= 0x07,
@@ -226,6 +220,22 @@
 	.port_ops	= &vt6420_sata_ops,
 };
 
+static struct ata_port_info vt6421_sport_info = {
+	.flags		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY,
+	.pio_mask	= 0x1f,
+	.mwdma_mask	= 0x07,
+	.udma_mask	= 0x7f,
+	.port_ops	= &vt6421_sata_ops,
+};
+
+static struct ata_port_info vt6421_pport_info = {
+	.flags		= ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_LEGACY,
+	.pio_mask	= 0x1f,
+	.mwdma_mask	= 0,
+	.udma_mask	= 0x7f,
+	.port_ops	= &vt6421_pata_ops,
+};
+
 MODULE_AUTHOR("Jeff Garzik");
 MODULE_DESCRIPTION("SCSI low-level driver for VIA SATA controllers");
 MODULE_LICENSE("GPL");
@@ -330,35 +340,15 @@
 				  NULL, ata_std_postreset);
 }
 
-static int vt6421_pata_prereset(struct ata_port *ap)
+static int vt6421_pata_cable_detect(struct ata_port *ap)
 {
 	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
 	u8 tmp;
 
 	pci_read_config_byte(pdev, PATA_UDMA_TIMING, &tmp);
 	if (tmp & 0x10)
-		ap->cbl = ATA_CBL_PATA40;
-	else
-		ap->cbl = ATA_CBL_PATA80;
-	return 0;
-}
-
-static void vt6421_pata_error_handler(struct ata_port *ap)
-{
-	return ata_bmdma_drive_eh(ap, vt6421_pata_prereset, ata_std_softreset,
-				  NULL, ata_std_postreset);
-}
-
-static int vt6421_sata_prereset(struct ata_port *ap)
-{
-	ap->cbl = ATA_CBL_SATA;
-	return 0;
-}
-
-static void vt6421_sata_error_handler(struct ata_port *ap)
-{
-	return ata_bmdma_drive_eh(ap, vt6421_sata_prereset, ata_std_softreset,
-				  NULL, ata_std_postreset);
+		return ATA_CBL_PATA40;
+	return ATA_CBL_PATA80;
 }
 
 static void vt6421_set_pio_mode(struct ata_port *ap, struct ata_device *adev)
@@ -375,16 +365,6 @@
 	pci_write_config_byte(pdev, PATA_UDMA_TIMING, udma_bits[adev->pio_mode - XFER_UDMA_0]);
 }
 
-static int vt6421_port_start(struct ata_port *ap)
-{
-	if (ap->port_no == PATA_PORT) {
-		ap->ops = &vt6421_pata_ops;
-		ap->mwdma_mask = 0;
-		ap->flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_LEGACY | ATA_FLAG_SRST;
-	}
-	return ata_port_start(ap);
-}
-
 static const unsigned int svia_bar_sizes[] = {
 	8, 4, 8, 4, 16, 256
 };
@@ -403,79 +383,78 @@
 	return addr + (port * 64);
 }
 
-static void vt6421_init_addrs(struct ata_probe_ent *probe_ent,
-			      void __iomem * const *iomap, unsigned int port)
+static void vt6421_init_addrs(struct ata_port *ap)
 {
-	void __iomem *reg_addr = iomap[port];
-	void __iomem *bmdma_addr = iomap[4] + (port * 8);
+	void __iomem * const * iomap = ap->host->iomap;
+	void __iomem *reg_addr = iomap[ap->port_no];
+	void __iomem *bmdma_addr = iomap[4] + (ap->port_no * 8);
+	struct ata_ioports *ioaddr = &ap->ioaddr;
 
-	probe_ent->port[port].cmd_addr = reg_addr;
-	probe_ent->port[port].altstatus_addr =
-	probe_ent->port[port].ctl_addr = (void __iomem *)
+	ioaddr->cmd_addr = reg_addr;
+	ioaddr->altstatus_addr =
+	ioaddr->ctl_addr = (void __iomem *)
 		((unsigned long)(reg_addr + 8) | ATA_PCI_CTL_OFS);
-	probe_ent->port[port].bmdma_addr = bmdma_addr;
-	probe_ent->port[port].scr_addr = vt6421_scr_addr(iomap[5], port);
+	ioaddr->bmdma_addr = bmdma_addr;
+	ioaddr->scr_addr = vt6421_scr_addr(iomap[5], ap->port_no);
 
-	ata_std_ports(&probe_ent->port[port]);
+	ata_std_ports(ioaddr);
 }
 
-static struct ata_probe_ent *vt6420_init_probe_ent(struct pci_dev *pdev)
+static int vt6420_prepare_host(struct pci_dev *pdev, struct ata_host **r_host)
 {
-	struct ata_probe_ent *probe_ent;
-	struct ata_port_info *ppi[2];
-	void __iomem *bar5;
+	const struct ata_port_info *ppi[] = { &vt6420_port_info, NULL };
+	struct ata_host *host;
+	int rc;
 
-	ppi[0] = ppi[1] = &vt6420_port_info;
-	probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
-	if (!probe_ent)
-		return NULL;
+	rc = ata_pci_prepare_native_host(pdev, ppi, 2, &host);
+	if (rc)
+		return rc;
+	*r_host = host;
 
-	bar5 = pcim_iomap(pdev, 5, 0);
-	if (!bar5) {
+	rc = pcim_iomap_regions(pdev, 1 << 5, DRV_NAME);
+	if (rc) {
 		dev_printk(KERN_ERR, &pdev->dev, "failed to iomap PCI BAR 5\n");
-		return NULL;
+		return rc;
 	}
 
-	probe_ent->port[0].scr_addr = svia_scr_addr(bar5, 0);
-	probe_ent->port[1].scr_addr = svia_scr_addr(bar5, 1);
+	host->ports[0]->ioaddr.scr_addr = svia_scr_addr(host->iomap[5], 0);
+	host->ports[1]->ioaddr.scr_addr = svia_scr_addr(host->iomap[5], 1);
 
-	return probe_ent;
+	return 0;
 }
 
-static struct ata_probe_ent *vt6421_init_probe_ent(struct pci_dev *pdev)
+static int vt6421_prepare_host(struct pci_dev *pdev, struct ata_host **r_host)
 {
-	struct ata_probe_ent *probe_ent;
-	unsigned int i;
+	const struct ata_port_info *ppi[] =
+		{ &vt6421_sport_info, &vt6421_sport_info, &vt6421_pport_info };
+	struct ata_host *host;
+	int i, rc;
 
-	probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL);
-	if (!probe_ent)
-		return NULL;
+	*r_host = host = ata_host_alloc_pinfo(&pdev->dev, ppi, ARRAY_SIZE(ppi));
+	if (!host) {
+		dev_printk(KERN_ERR, &pdev->dev, "failed to allocate host\n");
+		return -ENOMEM;
+	}
 
-	memset(probe_ent, 0, sizeof(*probe_ent));
-	probe_ent->dev = pci_dev_to_dev(pdev);
-	INIT_LIST_HEAD(&probe_ent->node);
+	rc = pcim_iomap_regions(pdev, 0x1f, DRV_NAME);
+	if (rc) {
+		dev_printk(KERN_ERR, &pdev->dev, "failed to request/iomap "
+			   "PCI BARs (errno=%d)\n", rc);
+		return rc;
+	}
+	host->iomap = pcim_iomap_table(pdev);
 
-	probe_ent->sht		= &svia_sht;
-	probe_ent->port_flags	= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY;
-	probe_ent->port_ops	= &vt6421_sata_ops;
-	probe_ent->n_ports	= N_PORTS;
-	probe_ent->irq		= pdev->irq;
-	probe_ent->irq_flags	= IRQF_SHARED;
-	probe_ent->pio_mask	= 0x1f;
-	probe_ent->mwdma_mask	= 0x07;
-	probe_ent->udma_mask	= 0x7f;
+	for (i = 0; i < host->n_ports; i++)
+		vt6421_init_addrs(host->ports[i]);
 
-	for (i = 0; i < 6; i++)
-		if (!pcim_iomap(pdev, i, 0)) {
-			dev_printk(KERN_ERR, &pdev->dev,
-				   "failed to iomap PCI BAR %d\n", i);
-			return NULL;
-		}
+	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
+	if (rc)
+		return rc;
+	rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
+	if (rc)
+		return rc;
 
-	for (i = 0; i < N_PORTS; i++)
-		vt6421_init_addrs(probe_ent, pcim_iomap_table(pdev), i);
-
-	return probe_ent;
+	return 0;
 }
 
 static void svia_configure(struct pci_dev *pdev)
@@ -522,7 +501,7 @@
 	static int printed_version;
 	unsigned int i;
 	int rc;
-	struct ata_probe_ent *probe_ent;
+	struct ata_host *host;
 	int board_id = (int) ent->driver_data;
 	const int *bar_sizes;
 	u8 tmp8;
@@ -534,12 +513,6 @@
 	if (rc)
 		return rc;
 
-	rc = pci_request_regions(pdev, DRV_NAME);
-	if (rc) {
-		pcim_pin_device(pdev);
-		return rc;
-	}
-
 	if (board_id == vt6420) {
 		pci_read_config_byte(pdev, SATA_PATA_SHARING, &tmp8);
 		if (tmp8 & SATA_2DEV) {
@@ -565,32 +538,18 @@
 			return -ENODEV;
 		}
 
-	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
-	if (rc)
-		return rc;
-	rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
-	if (rc)
-		return rc;
-
 	if (board_id == vt6420)
-		probe_ent = vt6420_init_probe_ent(pdev);
+		rc = vt6420_prepare_host(pdev, &host);
 	else
-		probe_ent = vt6421_init_probe_ent(pdev);
-
-	if (!probe_ent) {
-		dev_printk(KERN_ERR, &pdev->dev, "out of memory\n");
-		return -ENOMEM;
-	}
+		rc = vt6421_prepare_host(pdev, &host);
+	if (rc)
+		return rc;
 
 	svia_configure(pdev);
 
 	pci_set_master(pdev);
-
-	if (!ata_device_add(probe_ent))
-		return -ENODEV;
-
-	devm_kfree(&pdev->dev, probe_ent);
-	return 0;
+	return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED,
+				 &svia_sht);
 }
 
 static int __init svia_init(void)
diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c
index 170bad1..80126f8 100644
--- a/drivers/ata/sata_vsc.c
+++ b/drivers/ata/sata_vsc.c
@@ -333,7 +333,6 @@
 	.thaw			= vsc_thaw,
 	.error_handler		= ata_bmdma_error_handler,
 	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
-	.irq_handler		= vsc_sata_interrupt,
 	.irq_clear		= ata_bmdma_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -367,30 +366,50 @@
 
 static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
 {
+	static const struct ata_port_info pi = {
+		.flags		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+				  ATA_FLAG_MMIO,
+		.pio_mask	= 0x1f,
+		.mwdma_mask	= 0x07,
+		.udma_mask	= 0x7f,
+		.port_ops	= &vsc_sata_ops,
+	};
+	const struct ata_port_info *ppi[] = { &pi, NULL };
 	static int printed_version;
-	struct ata_probe_ent *probe_ent;
+	struct ata_host *host;
 	void __iomem *mmio_base;
-	int rc;
+	int i, rc;
 	u8 cls;
 
 	if (!printed_version++)
 		dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
 
+	/* allocate host */
+	host = ata_host_alloc_pinfo(&pdev->dev, ppi, 4);
+	if (!host)
+		return -ENOMEM;
+
 	rc = pcim_enable_device(pdev);
 	if (rc)
 		return rc;
 
-	/*
-	 * Check if we have needed resource mapped.
-	 */
+	/* check if we have needed resource mapped */
 	if (pci_resource_len(pdev, 0) == 0)
 		return -ENODEV;
 
+	/* map IO regions and intialize host accordingly */
 	rc = pcim_iomap_regions(pdev, 1 << VSC_MMIO_BAR, DRV_NAME);
 	if (rc == -EBUSY)
 		pcim_pin_device(pdev);
 	if (rc)
 		return rc;
+	host->iomap = pcim_iomap_table(pdev);
+
+	mmio_base = host->iomap[VSC_MMIO_BAR];
+
+	for (i = 0; i < host->n_ports; i++)
+		vsc_sata_setup_port(&host->ports[i]->ioaddr,
+				    mmio_base + (i + 1) * VSC_SATA_PORT_OFFSET);
 
 	/*
 	 * Use 32 bit DMA mask, because 64 bit address support is poor.
@@ -402,12 +421,6 @@
 	if (rc)
 		return rc;
 
-	probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL);
-	if (probe_ent == NULL)
-		return -ENOMEM;
-	probe_ent->dev = pci_dev_to_dev(pdev);
-	INIT_LIST_HEAD(&probe_ent->node);
-
 	/*
 	 * Due to a bug in the chip, the default cache line size can't be
 	 * used (unless the default is non-zero).
@@ -418,33 +431,6 @@
 
 	if (pci_enable_msi(pdev) == 0)
 		pci_intx(pdev, 0);
-	else
-		probe_ent->irq_flags = IRQF_SHARED;
-
-	probe_ent->sht = &vsc_sata_sht;
-	probe_ent->port_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
-				ATA_FLAG_MMIO;
-	probe_ent->port_ops = &vsc_sata_ops;
-	probe_ent->n_ports = 4;
-	probe_ent->irq = pdev->irq;
-	probe_ent->iomap = pcim_iomap_table(pdev);
-
-	/* We don't care much about the PIO/UDMA masks, but the core won't like us
-	 * if we don't fill these
-	 */
-	probe_ent->pio_mask = 0x1f;
-	probe_ent->mwdma_mask = 0x07;
-	probe_ent->udma_mask = 0x7f;
-
-	mmio_base = probe_ent->iomap[VSC_MMIO_BAR];
-
-	/* We have 4 ports per PCI function */
-	vsc_sata_setup_port(&probe_ent->port[0], mmio_base + 1 * VSC_SATA_PORT_OFFSET);
-	vsc_sata_setup_port(&probe_ent->port[1], mmio_base + 2 * VSC_SATA_PORT_OFFSET);
-	vsc_sata_setup_port(&probe_ent->port[2], mmio_base + 3 * VSC_SATA_PORT_OFFSET);
-	vsc_sata_setup_port(&probe_ent->port[3], mmio_base + 4 * VSC_SATA_PORT_OFFSET);
-
-	pci_set_master(pdev);
 
 	/*
 	 * Config offset 0x98 is "Extended Control and Status Register 0"
@@ -454,11 +440,9 @@
 	 */
 	pci_write_config_dword(pdev, 0x98, 0);
 
-	if (!ata_device_add(probe_ent))
-		return -ENODEV;
-
-	devm_kfree(&pdev->dev, probe_ent);
-	return 0;
+	pci_set_master(pdev);
+	return ata_host_activate(host, pdev->irq, vsc_sata_interrupt,
+				 IRQF_SHARED, &vsc_sata_sht);
 }
 
 static const struct pci_device_id vsc_sata_pci_tbl[] = {
diff --git a/drivers/atm/ambassador.c b/drivers/atm/ambassador.c
index 3c372e0..59651ab 100644
--- a/drivers/atm/ambassador.c
+++ b/drivers/atm/ambassador.c
@@ -821,7 +821,7 @@
     }
     // cast needed as there is no %? for pointer differences
     PRINTD (DBG_SKB, "allocated skb at %p, head %p, area %li",
-	    skb, skb->head, (long) (skb->end - skb->head));
+	    skb, skb->head, (long) (skb_end_pointer(skb) - skb->head));
     rx.handle = virt_to_bus (skb);
     rx.host_address = cpu_to_be32 (virt_to_bus (skb->data));
     if (rx_give (dev, &rx, pool))
diff --git a/drivers/atm/atmtcp.c b/drivers/atm/atmtcp.c
index fc518d8..02ad83d 100644
--- a/drivers/atm/atmtcp.c
+++ b/drivers/atm/atmtcp.c
@@ -221,7 +221,7 @@
 	hdr->vpi = htons(vcc->vpi);
 	hdr->vci = htons(vcc->vci);
 	hdr->length = htonl(skb->len);
-	memcpy(skb_put(new_skb,skb->len),skb->data,skb->len);
+	skb_copy_from_linear_data(skb, skb_put(new_skb, skb->len), skb->len);
 	if (vcc->pop) vcc->pop(vcc,skb);
 	else dev_kfree_skb(skb);
 	out_vcc->push(out_vcc,new_skb);
@@ -310,7 +310,7 @@
 		goto done;
 	}
 	__net_timestamp(new_skb);
-	memcpy(skb_put(new_skb,skb->len),skb->data,skb->len);
+	skb_copy_from_linear_data(skb, skb_put(new_skb, skb->len), skb->len);
 	out_vcc->push(out_vcc,new_skb);
 	atomic_inc(&vcc->stats->tx);
 	atomic_inc(&out_vcc->stats->rx);
@@ -352,7 +352,7 @@
 	.ops		= &atmtcp_c_dev_ops,
 	.type		= "atmtcp",
 	.number		= 999,
-	.lock		= SPIN_LOCK_UNLOCKED
+	.lock		= __SPIN_LOCK_UNLOCKED(atmtcp_control_dev.lock)
 };
 
 
diff --git a/drivers/atm/eni.c b/drivers/atm/eni.c
index 8fccf01..0d3a38b 100644
--- a/drivers/atm/eni.c
+++ b/drivers/atm/eni.c
@@ -536,7 +536,7 @@
 		return 0;
 	}
 	skb_put(skb,length);
-	skb_set_timestamp(skb, &eni_vcc->timestamp);
+	skb->tstamp = eni_vcc->timestamp;
 	DPRINTK("got len %ld\n",length);
 	if (do_rx_dma(vcc,skb,1,length >> 2,length >> 2)) return 1;
 	eni_vcc->rxing++;
@@ -701,7 +701,7 @@
 			DPRINTK("Grr, servicing VCC %ld twice\n",vci);
 			continue;
 		}
-		do_gettimeofday(&ENI_VCC(vcc)->timestamp);
+		ENI_VCC(vcc)->timestamp = ktime_get_real();
 		ENI_VCC(vcc)->next = NULL;
 		if (vcc->qos.rxtp.traffic_class == ATM_CBR) {
 			if (eni_dev->fast)
diff --git a/drivers/atm/eni.h b/drivers/atm/eni.h
index 385090c..d04fefb 100644
--- a/drivers/atm/eni.h
+++ b/drivers/atm/eni.h
@@ -59,7 +59,7 @@
 	int rxing;			/* number of pending PDUs */
 	int servicing;			/* number of waiting VCs (0 or 1) */
 	int txing;			/* number of pending TX bytes */
-	struct timeval timestamp;	/* for RX timing */
+	ktime_t timestamp;		/* for RX timing */
 	struct atm_vcc *next;		/* next pending RX */
 	struct sk_buff *last;		/* last PDU being DMAed (used to carry
 					   discard information) */
diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c
index a7c0ed3..405ee5e 100644
--- a/drivers/atm/fore200e.c
+++ b/drivers/atm/fore200e.c
@@ -1,6 +1,4 @@
 /*
-  $Id: fore200e.c,v 1.5 2000/04/14 10:10:34 davem Exp $
-
   A FORE Systems 200E-series driver for ATM on Linux.
   Christophe Lizzi (lizzi@cnam.fr), October 1999-March 2003.
 
@@ -1502,9 +1500,9 @@
     /* pseudo-CBR bandwidth requested? */
     if ((vcc->qos.txtp.traffic_class == ATM_CBR) && (vcc->qos.txtp.max_pcr > 0)) {
 	
-	down(&fore200e->rate_sf);
+	mutex_lock(&fore200e->rate_mtx);
 	if (fore200e->available_cell_rate < vcc->qos.txtp.max_pcr) {
-	    up(&fore200e->rate_sf);
+	    mutex_unlock(&fore200e->rate_mtx);
 
 	    kfree(fore200e_vcc);
 	    vc_map->vcc = NULL;
@@ -1513,7 +1511,7 @@
 
 	/* reserve bandwidth */
 	fore200e->available_cell_rate -= vcc->qos.txtp.max_pcr;
-	up(&fore200e->rate_sf);
+	mutex_unlock(&fore200e->rate_mtx);
     }
     
     vcc->itf = vcc->dev->number;
@@ -1599,9 +1597,9 @@
     /* release reserved bandwidth, if any */
     if ((vcc->qos.txtp.traffic_class == ATM_CBR) && (vcc->qos.txtp.max_pcr > 0)) {
 
-	down(&fore200e->rate_sf);
+	mutex_lock(&fore200e->rate_mtx);
 	fore200e->available_cell_rate += vcc->qos.txtp.max_pcr;
-	up(&fore200e->rate_sf);
+	mutex_unlock(&fore200e->rate_mtx);
 
 	clear_bit(ATM_VF_HASQOS, &vcc->flags);
     }
@@ -2064,16 +2062,16 @@
 
     if ((qos->txtp.traffic_class == ATM_CBR) && (qos->txtp.max_pcr > 0)) {
 
-	down(&fore200e->rate_sf);
+	mutex_lock(&fore200e->rate_mtx);
 	if (fore200e->available_cell_rate + vcc->qos.txtp.max_pcr < qos->txtp.max_pcr) {
-	    up(&fore200e->rate_sf);
+	    mutex_unlock(&fore200e->rate_mtx);
 	    return -EAGAIN;
 	}
 
 	fore200e->available_cell_rate += vcc->qos.txtp.max_pcr;
 	fore200e->available_cell_rate -= qos->txtp.max_pcr;
 
-	up(&fore200e->rate_sf);
+	mutex_unlock(&fore200e->rate_mtx);
 	
 	memcpy(&vcc->qos, qos, sizeof(struct atm_qos));
 	
@@ -2459,7 +2457,7 @@
 
     DPRINTK(2, "device %s being initialized\n", fore200e->name);
 
-    init_MUTEX(&fore200e->rate_sf);
+    mutex_init(&fore200e->rate_mtx);
     spin_lock_init(&fore200e->q_lock);
 
     cpq = fore200e->cp_queues = fore200e->virt_base + FORE200E_CP_QUEUES_OFFSET;
diff --git a/drivers/atm/fore200e.h b/drivers/atm/fore200e.h
index f9abfda..b85a546 100644
--- a/drivers/atm/fore200e.h
+++ b/drivers/atm/fore200e.h
@@ -869,7 +869,7 @@
 
     struct stats*              stats;                  /* last snapshot of the stats         */
     
-    struct semaphore           rate_sf;                /* protects rate reservation ops      */
+    struct mutex               rate_mtx;               /* protects rate reservation ops      */
     spinlock_t                 q_lock;                 /* protects queue ops                 */
 #ifdef FORE200E_USE_TASKLET
     struct tasklet_struct      tx_tasklet;             /* performs tx interrupt work         */
diff --git a/drivers/atm/he.c b/drivers/atm/he.c
index 8510026..d33aba6 100644
--- a/drivers/atm/he.c
+++ b/drivers/atm/he.c
@@ -1901,13 +1901,13 @@
 			case ATM_AAL0:
 				/* 2.10.1.5 raw cell receive */
 				skb->len = ATM_AAL0_SDU;
-				skb->tail = skb->data + skb->len;
+				skb_set_tail_pointer(skb, skb->len);
 				break;
 			case ATM_AAL5:
 				/* 2.10.1.2 aal5 receive */
 
 				skb->len = AAL5_LEN(skb->data, he_vcc->pdu_len);
-				skb->tail = skb->data + skb->len;
+				skb_set_tail_pointer(skb, skb->len);
 #ifdef USE_CHECKSUM_HW
 				if (vcc->vpi == 0 && vcc->vci >= ATM_NOT_RSV_VCI) {
 					skb->ip_summed = CHECKSUM_COMPLETE;
diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c
index b4b8014..057efbc 100644
--- a/drivers/atm/idt77252.c
+++ b/drivers/atm/idt77252.c
@@ -1065,7 +1065,8 @@
 	vcc = vc->rx_vcc;
 
 	pci_dma_sync_single_for_cpu(card->pcidev, IDT77252_PRV_PADDR(skb),
-				    skb->end - skb->data, PCI_DMA_FROMDEVICE);
+				    skb_end_pointer(skb) - skb->data,
+				    PCI_DMA_FROMDEVICE);
 
 	if ((vcc->qos.aal == ATM_AAL0) ||
 	    (vcc->qos.aal == ATM_AAL34)) {
@@ -1194,7 +1195,8 @@
 		}
 
 		pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb),
-				 skb->end - skb->data, PCI_DMA_FROMDEVICE);
+				 skb_end_pointer(skb) - skb->data,
+				 PCI_DMA_FROMDEVICE);
 		sb_pool_remove(card, skb);
 
 		skb_trim(skb, len);
@@ -1267,7 +1269,7 @@
 	tail = readl(SAR_REG_RAWCT);
 
 	pci_dma_sync_single_for_cpu(card->pcidev, IDT77252_PRV_PADDR(queue),
-				    queue->end - queue->head - 16,
+				    skb_end_pointer(queue) - queue->head - 16,
 				    PCI_DMA_FROMDEVICE);
 
 	while (head != tail) {
@@ -1363,7 +1365,8 @@
 				queue = card->raw_cell_head;
 				pci_dma_sync_single_for_cpu(card->pcidev,
 							    IDT77252_PRV_PADDR(queue),
-							    queue->end - queue->data,
+							    (skb_end_pointer(queue) -
+							     queue->data),
 							    PCI_DMA_FROMDEVICE);
 			} else {
 				card->raw_cell_head = NULL;
@@ -1816,7 +1819,8 @@
 	u32 handle;
 	u32 addr;
 
-	skb->data = skb->tail = skb->head;
+	skb->data = skb->head;
+	skb_reset_tail_pointer(skb);
 	skb->len = 0;
 
 	skb_reserve(skb, 16);
@@ -1835,7 +1839,6 @@
 		skb_put(skb, SAR_FB_SIZE_3);
 		break;
 	default:
-		dev_kfree_skb(skb);
 		return -1;
 	}
 
@@ -1874,7 +1877,7 @@
 		}
 
 		paddr = pci_map_single(card->pcidev, skb->data,
-				       skb->end - skb->data,
+				       skb_end_pointer(skb) - skb->data,
 				       PCI_DMA_FROMDEVICE);
 		IDT77252_PRV_PADDR(skb) = paddr;
 
@@ -1888,7 +1891,7 @@
 
 outunmap:
 	pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb),
-			 skb->end - skb->data, PCI_DMA_FROMDEVICE);
+			 skb_end_pointer(skb) - skb->data, PCI_DMA_FROMDEVICE);
 
 	handle = IDT77252_PRV_POOL(skb);
 	card->sbpool[POOL_QUEUE(handle)].skb[POOL_INDEX(handle)] = NULL;
@@ -1905,12 +1908,14 @@
 	int err;
 
 	pci_dma_sync_single_for_device(card->pcidev, IDT77252_PRV_PADDR(skb),
-				       skb->end - skb->data, PCI_DMA_FROMDEVICE);
+				       skb_end_pointer(skb) - skb->data,
+				       PCI_DMA_FROMDEVICE);
 
 	err = push_rx_skb(card, skb, POOL_QUEUE(handle));
 	if (err) {
 		pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb),
-				 skb->end - skb->data, PCI_DMA_FROMDEVICE);
+				 skb_end_pointer(skb) - skb->data,
+				 PCI_DMA_FROMDEVICE);
 		sb_pool_remove(card, skb);
 		dev_kfree_skb(skb);
 	}
@@ -3122,7 +3127,8 @@
 			if (skb) {
 				pci_unmap_single(card->pcidev,
 						 IDT77252_PRV_PADDR(skb),
-						 skb->end - skb->data,
+						 (skb_end_pointer(skb) -
+						  skb->data),
 						 PCI_DMA_FROMDEVICE);
 				card->sbpool[i].skb[j] = NULL;
 				dev_kfree_skb(skb);
diff --git a/drivers/atm/nicstar.c b/drivers/atm/nicstar.c
index aab9b37..14ced85 100644
--- a/drivers/atm/nicstar.c
+++ b/drivers/atm/nicstar.c
@@ -2208,7 +2208,7 @@
          if (i == 1 && ns_rsqe_eopdu(rsqe))
             *((u32 *) sb->data) |= 0x00000002;
          skb_put(sb, NS_AAL0_HEADER);
-         memcpy(sb->tail, cell, ATM_CELL_PAYLOAD);
+         memcpy(skb_tail_pointer(sb), cell, ATM_CELL_PAYLOAD);
          skb_put(sb, ATM_CELL_PAYLOAD);
          ATM_SKB(sb)->vcc = vcc;
 	 __net_timestamp(sb);
@@ -2252,7 +2252,8 @@
       vc->rx_iov = iovb;
       NS_SKB(iovb)->iovcnt = 0;
       iovb->len = 0;
-      iovb->tail = iovb->data = iovb->head;
+      iovb->data = iovb->head;
+      skb_reset_tail_pointer(iovb);
       NS_SKB(iovb)->vcc = vcc;
       /* IMPORTANT: a pointer to the sk_buff containing the small or large
                     buffer is stored as iovec base, NOT a pointer to the 
@@ -2265,7 +2266,8 @@
       recycle_iovec_rx_bufs(card, (struct iovec *) iovb->data, NS_MAX_IOVECS);
       NS_SKB(iovb)->iovcnt = 0;
       iovb->len = 0;
-      iovb->tail = iovb->data = iovb->head;
+      iovb->data = iovb->head;
+      skb_reset_tail_pointer(iovb);
       NS_SKB(iovb)->vcc = vcc;
    }
    iov = &((struct iovec *) iovb->data)[NS_SKB(iovb)->iovcnt++];
@@ -2393,7 +2395,7 @@
                skb->destructor = ns_lb_destructor;
 #endif /* NS_USE_DESTRUCTORS */
                skb_push(skb, NS_SMBUFSIZE);
-               memcpy(skb->data, sb->data, NS_SMBUFSIZE);
+               skb_copy_from_linear_data(sb, skb->data, NS_SMBUFSIZE);
                skb_put(skb, len - NS_SMBUFSIZE);
                ATM_SKB(skb)->vcc = vcc;
 	       __net_timestamp(skb);
@@ -2477,7 +2479,7 @@
 	 {
             /* Copy the small buffer to the huge buffer */
             sb = (struct sk_buff *) iov->iov_base;
-            memcpy(hb->data, sb->data, iov->iov_len);
+            skb_copy_from_linear_data(sb, hb->data, iov->iov_len);
             skb_put(hb, iov->iov_len);
             remaining = len - iov->iov_len;
             iov++;
@@ -2489,7 +2491,7 @@
             {
                lb = (struct sk_buff *) iov->iov_base;
                tocopy = min_t(int, remaining, iov->iov_len);
-               memcpy(hb->tail, lb->data, tocopy);
+               skb_copy_from_linear_data(lb, skb_tail_pointer(hb), tocopy);
                skb_put(hb, tocopy);
                iov++;
                remaining -= tocopy;
diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c
index 2222073..1ec0654 100644
--- a/drivers/base/attribute_container.c
+++ b/drivers/base/attribute_container.c
@@ -62,7 +62,7 @@
 
 static struct list_head attribute_container_list;
 
-static DECLARE_MUTEX(attribute_container_mutex);
+static DEFINE_MUTEX(attribute_container_mutex);
 
 /**
  * attribute_container_register - register an attribute container
@@ -77,9 +77,9 @@
 	klist_init(&cont->containers,internal_container_klist_get,
 		   internal_container_klist_put);
 		
-	down(&attribute_container_mutex);
+	mutex_lock(&attribute_container_mutex);
 	list_add_tail(&cont->node, &attribute_container_list);
-	up(&attribute_container_mutex);
+	mutex_unlock(&attribute_container_mutex);
 
 	return 0;
 }
@@ -94,7 +94,7 @@
 attribute_container_unregister(struct attribute_container *cont)
 {
 	int retval = -EBUSY;
-	down(&attribute_container_mutex);
+	mutex_lock(&attribute_container_mutex);
 	spin_lock(&cont->containers.k_lock);
 	if (!list_empty(&cont->containers.k_list))
 		goto out;
@@ -102,7 +102,7 @@
 	list_del(&cont->node);
  out:
 	spin_unlock(&cont->containers.k_lock);
-	up(&attribute_container_mutex);
+	mutex_unlock(&attribute_container_mutex);
 	return retval;
 		
 }
@@ -145,7 +145,7 @@
 {
 	struct attribute_container *cont;
 
-	down(&attribute_container_mutex);
+	mutex_lock(&attribute_container_mutex);
 	list_for_each_entry(cont, &attribute_container_list, node) {
 		struct internal_container *ic;
 
@@ -173,7 +173,7 @@
 			attribute_container_add_class_device(&ic->classdev);
 		klist_add_tail(&ic->node, &cont->containers);
 	}
-	up(&attribute_container_mutex);
+	mutex_unlock(&attribute_container_mutex);
 }
 
 /* FIXME: can't break out of this unless klist_iter_exit is also
@@ -211,7 +211,7 @@
 {
 	struct attribute_container *cont;
 
-	down(&attribute_container_mutex);
+	mutex_lock(&attribute_container_mutex);
 	list_for_each_entry(cont, &attribute_container_list, node) {
 		struct internal_container *ic;
 		struct klist_iter iter;
@@ -234,7 +234,7 @@
 			}
 		}
 	}
-	up(&attribute_container_mutex);
+	mutex_unlock(&attribute_container_mutex);
 }
 
 /**
@@ -255,7 +255,7 @@
 {
 	struct attribute_container *cont;
 
-	down(&attribute_container_mutex);
+	mutex_lock(&attribute_container_mutex);
 	list_for_each_entry(cont, &attribute_container_list, node) {
 		struct internal_container *ic;
 		struct klist_iter iter;
@@ -273,7 +273,7 @@
 				fn(cont, dev, &ic->classdev);
 		}
 	}
-	up(&attribute_container_mutex);
+	mutex_unlock(&attribute_container_mutex);
 }
 
 /**
@@ -295,12 +295,12 @@
 {
 	struct attribute_container *cont;
 
-	down(&attribute_container_mutex);
+	mutex_lock(&attribute_container_mutex);
 	list_for_each_entry(cont, &attribute_container_list, node) {
 		if (cont->match(cont, dev))
 			fn(cont, dev);
 	}
-	up(&attribute_container_mutex);
+	mutex_unlock(&attribute_container_mutex);
 }
 
 /**
diff --git a/drivers/base/base.h b/drivers/base/base.h
index de7e144..d597f26 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -16,7 +16,7 @@
 extern int attribute_container_init(void);
 
 extern int bus_add_device(struct device * dev);
-extern int bus_attach_device(struct device * dev);
+extern void bus_attach_device(struct device * dev);
 extern void bus_remove_device(struct device * dev);
 extern struct bus_type *get_bus(struct bus_type * bus);
 extern void put_bus(struct bus_type * bus);
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 253868e..1d76e23 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -27,6 +27,9 @@
 #define to_driver(obj) container_of(obj, struct device_driver, kobj)
 
 
+static int __must_check bus_rescan_devices_helper(struct device *dev,
+						void *data);
+
 static ssize_t
 drv_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
 {
@@ -60,8 +63,19 @@
 
 static void driver_release(struct kobject * kobj)
 {
-	struct device_driver * drv = to_driver(kobj);
-	complete(&drv->unloaded);
+	/*
+	 * Yes this is an empty release function, it is this way because struct
+	 * device is always a static object, not a dynamic one.  Yes, this is
+	 * not nice and bad, but remember, drivers are code, reference counted
+	 * by the module count, not a device, which is really data.  And yes,
+	 * in the future I do want to have all drivers be created dynamically,
+	 * and am working toward that goal, but it will take a bit longer...
+	 *
+	 * But do not let this example give _anyone_ the idea that they can
+	 * create a release function without any code in it at all, to do that
+	 * is almost always wrong.  If you have any questions about this,
+	 * please send an email to <greg@kroah.com>
+	 */
 }
 
 static struct kobj_type ktype_driver = {
@@ -133,7 +147,6 @@
 
 
 #ifdef CONFIG_HOTPLUG
-
 /* Manually detach a device from its associated driver. */
 static int driver_helper(struct device *dev, void *data)
 {
@@ -199,6 +212,33 @@
 }
 static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind);
 
+static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)
+{
+	return sprintf(buf, "%d\n", bus->drivers_autoprobe);
+}
+
+static ssize_t store_drivers_autoprobe(struct bus_type *bus,
+				       const char *buf, size_t count)
+{
+	if (buf[0] == '0')
+		bus->drivers_autoprobe = 0;
+	else
+		bus->drivers_autoprobe = 1;
+	return count;
+}
+
+static ssize_t store_drivers_probe(struct bus_type *bus,
+				   const char *buf, size_t count)
+{
+	struct device *dev;
+
+	dev = bus_find_device(bus, NULL, (void *)buf, driver_helper);
+	if (!dev)
+		return -ENODEV;
+	if (bus_rescan_devices_helper(dev, NULL) != 0)
+		return -EINVAL;
+	return count;
+}
 #endif
 
 static struct device * next_device(struct klist_iter * i)
@@ -418,21 +458,21 @@
  *	- Add device to bus's list of devices.
  *	- Try to attach to driver.
  */
-int bus_attach_device(struct device * dev)
+void bus_attach_device(struct device * dev)
 {
 	struct bus_type *bus = dev->bus;
 	int ret = 0;
 
 	if (bus) {
 		dev->is_registered = 1;
-		ret = device_attach(dev);
-		if (ret >= 0) {
+		if (bus->drivers_autoprobe)
+			ret = device_attach(dev);
+		WARN_ON(ret < 0);
+		if (ret >= 0)
 			klist_add_tail(&dev->knode_bus, &bus->klist_devices);
-			ret = 0;
-		} else
+		else
 			dev->is_registered = 0;
 	}
-	return ret;
 }
 
 /**
@@ -515,9 +555,41 @@
 	driver_remove_file(drv, &driver_attr_bind);
 	driver_remove_file(drv, &driver_attr_unbind);
 }
+
+static int add_probe_files(struct bus_type *bus)
+{
+	int retval;
+
+	bus->drivers_probe_attr.attr.name = "drivers_probe";
+	bus->drivers_probe_attr.attr.mode = S_IWUSR;
+	bus->drivers_probe_attr.attr.owner = bus->owner;
+	bus->drivers_probe_attr.store = store_drivers_probe;
+	retval = bus_create_file(bus, &bus->drivers_probe_attr);
+	if (retval)
+		goto out;
+
+	bus->drivers_autoprobe_attr.attr.name = "drivers_autoprobe";
+	bus->drivers_autoprobe_attr.attr.mode = S_IWUSR | S_IRUGO;
+	bus->drivers_autoprobe_attr.attr.owner = bus->owner;
+	bus->drivers_autoprobe_attr.show = show_drivers_autoprobe;
+	bus->drivers_autoprobe_attr.store = store_drivers_autoprobe;
+	retval = bus_create_file(bus, &bus->drivers_autoprobe_attr);
+	if (retval)
+		bus_remove_file(bus, &bus->drivers_probe_attr);
+out:
+	return retval;
+}
+
+static void remove_probe_files(struct bus_type *bus)
+{
+	bus_remove_file(bus, &bus->drivers_autoprobe_attr);
+	bus_remove_file(bus, &bus->drivers_probe_attr);
+}
 #else
 static inline int add_bind_files(struct device_driver *drv) { return 0; }
 static inline void remove_bind_files(struct device_driver *drv) {}
+static inline int add_probe_files(struct bus_type *bus) { return 0; }
+static inline void remove_probe_files(struct bus_type *bus) {}
 #endif
 
 /**
@@ -531,7 +603,7 @@
 	int error = 0;
 
 	if (!bus)
-		return 0;
+		return -EINVAL;
 
 	pr_debug("bus %s: add driver %s\n", bus->name, drv->name);
 	error = kobject_set_name(&drv->kobj, "%s", drv->name);
@@ -541,9 +613,11 @@
 	if ((error = kobject_register(&drv->kobj)))
 		goto out_put_bus;
 
-	error = driver_attach(drv);
-	if (error)
-		goto out_unregister;
+	if (drv->bus->drivers_autoprobe) {
+		error = driver_attach(drv);
+		if (error)
+			goto out_unregister;
+	}
 	klist_add_tail(&drv->knode_bus, &bus->klist_drivers);
 	module_add_driver(drv->owner, drv);
 
@@ -605,8 +679,6 @@
 		ret = device_attach(dev);
 		if (dev->parent)
 			up(&dev->parent->sem);
-		if (ret > 0)
-			ret = 0;
 	}
 	return ret < 0 ? ret : 0;
 }
@@ -762,6 +834,12 @@
 
 	klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);
 	klist_init(&bus->klist_drivers, NULL, NULL);
+
+	bus->drivers_autoprobe = 1;
+	retval = add_probe_files(bus);
+	if (retval)
+		goto bus_probe_files_fail;
+
 	retval = bus_add_attrs(bus);
 	if (retval)
 		goto bus_attrs_fail;
@@ -770,6 +848,8 @@
 	return 0;
 
 bus_attrs_fail:
+	remove_probe_files(bus);
+bus_probe_files_fail:
 	kset_unregister(&bus->drivers);
 bus_drivers_fail:
 	kset_unregister(&bus->devices);
@@ -779,7 +859,6 @@
 	return retval;
 }
 
-
 /**
  *	bus_unregister - remove a bus from the system
  *	@bus:	bus.
@@ -791,6 +870,7 @@
 {
 	pr_debug("bus %s: unregistering\n", bus->name);
 	bus_remove_attrs(bus);
+	remove_probe_files(bus);
 	kset_unregister(&bus->drivers);
 	kset_unregister(&bus->devices);
 	subsystem_unregister(&bus->subsys);
diff --git a/drivers/base/class.c b/drivers/base/class.c
index d596812..80bbb20 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -145,6 +145,7 @@
 	INIT_LIST_HEAD(&cls->children);
 	INIT_LIST_HEAD(&cls->devices);
 	INIT_LIST_HEAD(&cls->interfaces);
+	kset_init(&cls->class_dirs);
 	init_MUTEX(&cls->sem);
 	error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name);
 	if (error)
@@ -163,7 +164,6 @@
 void class_unregister(struct class * cls)
 {
 	pr_debug("device class '%s': unregistering\n", cls->name);
-	kobject_unregister(cls->virtual_dir);
 	remove_class_attrs(cls);
 	subsystem_unregister(&cls->subsys);
 }
diff --git a/drivers/base/core.c b/drivers/base/core.c
index d7fcf82..8aa090d 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -43,7 +43,8 @@
 const char *dev_driver_string(struct device *dev)
 {
 	return dev->driver ? dev->driver->name :
-			(dev->bus ? dev->bus->name : "");
+			(dev->bus ? dev->bus->name :
+			(dev->class ? dev->class->name : ""));
 }
 EXPORT_SYMBOL(dev_driver_string);
 
@@ -119,6 +120,8 @@
 
 	if (ktype == &ktype_device) {
 		struct device *dev = to_dev(kobj);
+		if (dev->uevent_suppress)
+			return 0;
 		if (dev->bus)
 			return 1;
 		if (dev->class)
@@ -156,6 +159,11 @@
 			       "MINOR=%u", MINOR(dev->devt));
 	}
 
+	if (dev->type && dev->type->name)
+		add_uevent_var(envp, num_envp, &i,
+			       buffer, buffer_size, &length,
+			       "DEVTYPE=%s", dev->type->name);
+
 	if (dev->driver)
 		add_uevent_var(envp, num_envp, &i,
 			       buffer, buffer_size, &length,
@@ -238,71 +246,152 @@
 	.uevent =	dev_uevent,
 };
 
+static ssize_t show_uevent(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	struct kobject *top_kobj;
+	struct kset *kset;
+	char *envp[32];
+	char data[PAGE_SIZE];
+	char *pos;
+	int i;
+	size_t count = 0;
+	int retval;
+
+	/* search the kset, the device belongs to */
+	top_kobj = &dev->kobj;
+	if (!top_kobj->kset && top_kobj->parent) {
+		do {
+			top_kobj = top_kobj->parent;
+		} while (!top_kobj->kset && top_kobj->parent);
+	}
+	if (!top_kobj->kset)
+		goto out;
+	kset = top_kobj->kset;
+	if (!kset->uevent_ops || !kset->uevent_ops->uevent)
+		goto out;
+
+	/* respect filter */
+	if (kset->uevent_ops && kset->uevent_ops->filter)
+		if (!kset->uevent_ops->filter(kset, &dev->kobj))
+			goto out;
+
+	/* let the kset specific function add its keys */
+	pos = data;
+	retval = kset->uevent_ops->uevent(kset, &dev->kobj,
+					  envp, ARRAY_SIZE(envp),
+					  pos, PAGE_SIZE);
+	if (retval)
+		goto out;
+
+	/* copy keys to file */
+	for (i = 0; envp[i]; i++) {
+		pos = &buf[count];
+		count += sprintf(pos, "%s\n", envp[i]);
+	}
+out:
+	return count;
+}
+
 static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,
 			    const char *buf, size_t count)
 {
+	if (memcmp(buf, "add", 3) != 0)
+		dev_err(dev, "uevent: unsupported action-string; this will "
+			"be ignored in a future kernel version");
 	kobject_uevent(&dev->kobj, KOBJ_ADD);
 	return count;
 }
 
-static int device_add_groups(struct device *dev)
+static int device_add_attributes(struct device *dev,
+				 struct device_attribute *attrs)
 {
-	int i;
 	int error = 0;
+	int i;
 
-	if (dev->groups) {
-		for (i = 0; dev->groups[i]; i++) {
-			error = sysfs_create_group(&dev->kobj, dev->groups[i]);
-			if (error) {
-				while (--i >= 0)
-					sysfs_remove_group(&dev->kobj, dev->groups[i]);
-				goto out;
-			}
+	if (attrs) {
+		for (i = 0; attr_name(attrs[i]); i++) {
+			error = device_create_file(dev, &attrs[i]);
+			if (error)
+				break;
 		}
+		if (error)
+			while (--i >= 0)
+				device_remove_file(dev, &attrs[i]);
 	}
-out:
 	return error;
 }
 
-static void device_remove_groups(struct device *dev)
+static void device_remove_attributes(struct device *dev,
+				     struct device_attribute *attrs)
 {
 	int i;
-	if (dev->groups) {
-		for (i = 0; dev->groups[i]; i++) {
-			sysfs_remove_group(&dev->kobj, dev->groups[i]);
+
+	if (attrs)
+		for (i = 0; attr_name(attrs[i]); i++)
+			device_remove_file(dev, &attrs[i]);
+}
+
+static int device_add_groups(struct device *dev,
+			     struct attribute_group **groups)
+{
+	int error = 0;
+	int i;
+
+	if (groups) {
+		for (i = 0; groups[i]; i++) {
+			error = sysfs_create_group(&dev->kobj, groups[i]);
+			if (error) {
+				while (--i >= 0)
+					sysfs_remove_group(&dev->kobj, groups[i]);
+				break;
+			}
 		}
 	}
+	return error;
+}
+
+static void device_remove_groups(struct device *dev,
+				 struct attribute_group **groups)
+{
+	int i;
+
+	if (groups)
+		for (i = 0; groups[i]; i++)
+			sysfs_remove_group(&dev->kobj, groups[i]);
 }
 
 static int device_add_attrs(struct device *dev)
 {
 	struct class *class = dev->class;
 	struct device_type *type = dev->type;
-	int error = 0;
-	int i;
+	int error;
 
-	if (class && class->dev_attrs) {
-		for (i = 0; attr_name(class->dev_attrs[i]); i++) {
-			error = device_create_file(dev, &class->dev_attrs[i]);
-			if (error)
-				break;
-		}
+	if (class) {
+		error = device_add_attributes(dev, class->dev_attrs);
 		if (error)
-			while (--i >= 0)
-				device_remove_file(dev, &class->dev_attrs[i]);
+			return error;
 	}
 
-	if (type && type->attrs) {
-		for (i = 0; attr_name(type->attrs[i]); i++) {
-			error = device_create_file(dev, &type->attrs[i]);
-			if (error)
-				break;
-		}
+	if (type) {
+		error = device_add_groups(dev, type->groups);
 		if (error)
-			while (--i >= 0)
-				device_remove_file(dev, &type->attrs[i]);
+			goto err_remove_class_attrs;
 	}
 
+	error = device_add_groups(dev, dev->groups);
+	if (error)
+		goto err_remove_type_groups;
+
+	return 0;
+
+ err_remove_type_groups:
+	if (type)
+		device_remove_groups(dev, type->groups);
+ err_remove_class_attrs:
+	if (class)
+		device_remove_attributes(dev, class->dev_attrs);
+
 	return error;
 }
 
@@ -310,17 +399,14 @@
 {
 	struct class *class = dev->class;
 	struct device_type *type = dev->type;
-	int i;
 
-	if (class && class->dev_attrs) {
-		for (i = 0; attr_name(class->dev_attrs[i]); i++)
-			device_remove_file(dev, &class->dev_attrs[i]);
-	}
+	device_remove_groups(dev, dev->groups);
 
-	if (type && type->attrs) {
-		for (i = 0; attr_name(type->attrs[i]); i++)
-			device_remove_file(dev, &type->attrs[i]);
-	}
+	if (type)
+		device_remove_groups(dev, type->groups);
+
+	if (class)
+		device_remove_attributes(dev, class->dev_attrs);
 }
 
 
@@ -394,9 +480,10 @@
 EXPORT_SYMBOL_GPL(device_remove_bin_file);
 
 /**
- * device_schedule_callback - helper to schedule a callback for a device
+ * device_schedule_callback_owner - helper to schedule a callback for a device
  * @dev: device.
  * @func: callback function to invoke later.
+ * @owner: module owning the callback routine
  *
  * Attribute methods must not unregister themselves or their parent device
  * (which would amount to the same thing).  Attempts to do so will deadlock,
@@ -407,20 +494,23 @@
  * argument in the workqueue's process context.  @dev will be pinned until
  * @func returns.
  *
+ * This routine is usually called via the inline device_schedule_callback(),
+ * which automatically sets @owner to THIS_MODULE.
+ *
  * Returns 0 if the request was submitted, -ENOMEM if storage could not
- * be allocated.
+ * be allocated, -ENODEV if a reference to @owner isn't available.
  *
  * NOTE: This routine won't work if CONFIG_SYSFS isn't set!  It uses an
  * underlying sysfs routine (since it is intended for use by attribute
  * methods), and if sysfs isn't available you'll get nothing but -ENOSYS.
  */
-int device_schedule_callback(struct device *dev,
-		void (*func)(struct device *))
+int device_schedule_callback_owner(struct device *dev,
+		void (*func)(struct device *), struct module *owner)
 {
 	return sysfs_schedule_callback(&dev->kobj,
-			(void (*)(void *)) func, dev);
+			(void (*)(void *)) func, dev, owner);
 }
-EXPORT_SYMBOL_GPL(device_schedule_callback);
+EXPORT_SYMBOL_GPL(device_schedule_callback_owner);
 
 static void klist_children_get(struct klist_node *n)
 {
@@ -477,34 +567,58 @@
 	return NULL;
 }
 #else
-static struct kobject * virtual_device_parent(struct device *dev)
+static struct kobject *virtual_device_parent(struct device *dev)
 {
-	if (!dev->class)
-		return ERR_PTR(-ENODEV);
+	static struct kobject *virtual_dir = NULL;
 
-	if (!dev->class->virtual_dir) {
-		static struct kobject *virtual_dir = NULL;
+	if (!virtual_dir)
+		virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual");
 
-		if (!virtual_dir)
-			virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual");
-		dev->class->virtual_dir = kobject_add_dir(virtual_dir, dev->class->name);
-	}
-
-	return dev->class->virtual_dir;
+	return virtual_dir;
 }
 
 static struct kobject * get_device_parent(struct device *dev,
 					  struct device *parent)
 {
-	/* if this is a class device, and has no parent, create one */
-	if ((dev->class) && (parent == NULL)) {
-		return virtual_device_parent(dev);
-	} else if (parent)
+	if (dev->class) {
+		struct kobject *kobj = NULL;
+		struct kobject *parent_kobj;
+		struct kobject *k;
+
+		/*
+		 * If we have no parent, we live in "virtual".
+		 * Class-devices with a bus-device as parent, live
+		 * in a class-directory to prevent namespace collisions.
+		 */
+		if (parent == NULL)
+			parent_kobj = virtual_device_parent(dev);
+		else if (parent->class)
+			return &parent->kobj;
+		else
+			parent_kobj = &parent->kobj;
+
+		/* find our class-directory at the parent and reference it */
+		spin_lock(&dev->class->class_dirs.list_lock);
+		list_for_each_entry(k, &dev->class->class_dirs.list, entry)
+			if (k->parent == parent_kobj) {
+				kobj = kobject_get(k);
+				break;
+			}
+		spin_unlock(&dev->class->class_dirs.list_lock);
+		if (kobj)
+			return kobj;
+
+		/* or create a new class-directory at the parent device */
+		return kobject_kset_add_dir(&dev->class->class_dirs,
+					    parent_kobj, dev->class->name);
+	}
+
+	if (parent)
 		return &parent->kobj;
 	return NULL;
 }
-
 #endif
+
 static int setup_parent(struct device *dev, struct device *parent)
 {
 	struct kobject *kobj;
@@ -541,7 +655,6 @@
 	pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id);
 
 	parent = get_device(dev->parent);
-
 	error = setup_parent(dev, parent);
 	if (error)
 		goto Error;
@@ -562,10 +675,11 @@
 					     BUS_NOTIFY_ADD_DEVICE, dev);
 
 	dev->uevent_attr.attr.name = "uevent";
-	dev->uevent_attr.attr.mode = S_IWUSR;
+	dev->uevent_attr.attr.mode = S_IRUGO | S_IWUSR;
 	if (dev->driver)
 		dev->uevent_attr.attr.owner = dev->driver->owner;
 	dev->uevent_attr.store = store_uevent;
+	dev->uevent_attr.show = show_uevent;
 	error = device_create_file(dev, &dev->uevent_attr);
 	if (error)
 		goto attrError;
@@ -614,16 +728,12 @@
 
 	if ((error = device_add_attrs(dev)))
 		goto AttrsError;
-	if ((error = device_add_groups(dev)))
-		goto GroupError;
 	if ((error = device_pm_add(dev)))
 		goto PMError;
 	if ((error = bus_add_device(dev)))
 		goto BusError;
-	if (!dev->uevent_suppress)
-		kobject_uevent(&dev->kobj, KOBJ_ADD);
-	if ((error = bus_attach_device(dev)))
-		goto AttachError;
+	kobject_uevent(&dev->kobj, KOBJ_ADD);
+	bus_attach_device(dev);
 	if (parent)
 		klist_add_tail(&dev->knode_parent, &parent->klist_children);
 
@@ -639,19 +749,15 @@
 		up(&dev->class->sem);
 	}
  Done:
- 	kfree(class_name);
+	kfree(class_name);
 	put_device(dev);
 	return error;
- AttachError:
-	bus_remove_device(dev);
  BusError:
 	device_pm_remove(dev);
  PMError:
 	if (dev->bus)
 		blocking_notifier_call_chain(&dev->bus->bus_notifier,
 					     BUS_NOTIFY_DEL_DEVICE, dev);
-	device_remove_groups(dev);
- GroupError:
 	device_remove_attrs(dev);
  AttrsError:
 	if (dev->devt_attr) {
@@ -677,15 +783,6 @@
 #endif
 			sysfs_remove_link(&dev->kobj, "device");
 		}
-
-		down(&dev->class->sem);
-		/* notify any interfaces that the device is now gone */
-		list_for_each_entry(class_intf, &dev->class->interfaces, node)
-			if (class_intf->remove_dev)
-				class_intf->remove_dev(dev, class_intf);
-		/* remove the device from the class list */
-		list_del_init(&dev->node);
-		up(&dev->class->sem);
 	}
  ueventattrError:
 	device_remove_file(dev, &dev->uevent_attr);
@@ -796,9 +893,33 @@
 		/* remove the device from the class list */
 		list_del_init(&dev->node);
 		up(&dev->class->sem);
+
+		/* If we live in a parent class-directory, unreference it */
+		if (dev->kobj.parent->kset == &dev->class->class_dirs) {
+			struct device *d;
+			int other = 0;
+
+			/*
+			 * if we are the last child of our class, delete
+			 * our class-directory at this parent
+			 */
+			down(&dev->class->sem);
+			list_for_each_entry(d, &dev->class->devices, node) {
+				if (d == dev)
+					continue;
+				if (d->kobj.parent == dev->kobj.parent) {
+					other = 1;
+					break;
+				}
+			}
+			if (!other)
+				kobject_del(dev->kobj.parent);
+
+			kobject_put(dev->kobj.parent);
+			up(&dev->class->sem);
+		}
 	}
 	device_remove_file(dev, &dev->uevent_attr);
-	device_remove_groups(dev);
 	device_remove_attrs(dev);
 	bus_remove_device(dev);
 
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 6a48824..18dba8e 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -94,19 +94,11 @@
 	return ret;
 }
 
-struct stupid_thread_structure {
-	struct device_driver *drv;
-	struct device *dev;
-};
-
 static atomic_t probe_count = ATOMIC_INIT(0);
 static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue);
 
-static int really_probe(void *void_data)
+static int really_probe(struct device *dev, struct device_driver *drv)
 {
-	struct stupid_thread_structure *data = void_data;
-	struct device_driver *drv = data->drv;
-	struct device *dev = data->dev;
 	int ret = 0;
 
 	atomic_inc(&probe_count);
@@ -154,7 +146,6 @@
 	 */
 	ret = 0;
 done:
-	kfree(data);
 	atomic_dec(&probe_count);
 	wake_up(&probe_waitqueue);
 	return ret;
@@ -186,16 +177,14 @@
  * format of the ID structures, nor what is to be considered a match and
  * what is not.
  *
- * This function returns 1 if a match is found, an error if one occurs
- * (that is not -ENODEV or -ENXIO), and 0 otherwise.
+ * This function returns 1 if a match is found, -ENODEV if the device is
+ * not registered, and 0 otherwise.
  *
  * This function must be called with @dev->sem held.  When called for a
  * USB interface, @dev->parent->sem must be held as well.
  */
 int driver_probe_device(struct device_driver * drv, struct device * dev)
 {
-	struct stupid_thread_structure *data;
-	struct task_struct *probe_task;
 	int ret = 0;
 
 	if (!device_is_registered(dev))
@@ -206,19 +195,7 @@
 	pr_debug("%s: Matched Device %s with Driver %s\n",
 		 drv->bus->name, dev->bus_id, drv->name);
 
-	data = kmalloc(sizeof(*data), GFP_KERNEL);
-	if (!data)
-		return -ENOMEM;
-	data->drv = drv;
-	data->dev = dev;
-
-	if (drv->multithread_probe) {
-		probe_task = kthread_run(really_probe, data,
-					 "probe-%s", dev->bus_id);
-		if (IS_ERR(probe_task))
-			ret = really_probe(data);
-	} else
-		ret = really_probe(data);
+	ret = really_probe(dev, drv);
 
 done:
 	return ret;
@@ -230,30 +207,57 @@
 	return driver_probe_device(drv, dev);
 }
 
+static int device_probe_drivers(void *data)
+{
+	struct device *dev = data;
+	int ret = 0;
+
+	if (dev->bus) {
+		down(&dev->sem);
+		ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
+		up(&dev->sem);
+	}
+	return ret;
+}
+
 /**
  *	device_attach - try to attach device to a driver.
  *	@dev:	device.
  *
  *	Walk the list of drivers that the bus has and call
  *	driver_probe_device() for each pair. If a compatible
- *	pair is found, break out and return.
+ *	pair is found, break out and return. If the bus specifies
+ *	multithreaded probing, walking the list of drivers is done
+ *	on a probing thread.
  *
  *	Returns 1 if the device was bound to a driver;
- *	0 if no matching device was found; error code otherwise.
+ *	0 if no matching device was found or multithreaded probing is done;
+ *	-ENODEV if the device is not registered.
  *
  *	When called for a USB interface, @dev->parent->sem must be held.
  */
 int device_attach(struct device * dev)
 {
 	int ret = 0;
+	struct task_struct *probe_task = ERR_PTR(-ENOMEM);
 
 	down(&dev->sem);
 	if (dev->driver) {
 		ret = device_bind_driver(dev);
 		if (ret == 0)
 			ret = 1;
-	} else
-		ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
+		else {
+			dev->driver = NULL;
+			ret = 0;
+		}
+	} else {
+		if (dev->bus->multithread_probe)
+			probe_task = kthread_run(device_probe_drivers, dev,
+						 "probe-%s", dev->bus_id);
+		if(IS_ERR(probe_task))
+			ret = bus_for_each_drv(dev->bus, NULL, dev,
+					       __device_attach);
+	}
 	up(&dev->sem);
 	return ret;
 }
diff --git a/drivers/base/dmapool.c b/drivers/base/dmapool.c
index cd467c9..9406259 100644
--- a/drivers/base/dmapool.c
+++ b/drivers/base/dmapool.c
@@ -37,7 +37,7 @@
 
 #define	POOL_TIMEOUT_JIFFIES	((100 /* msec */ * HZ) / 1000)
 
-static DECLARE_MUTEX (pools_lock);
+static DEFINE_MUTEX (pools_lock);
 
 static ssize_t
 show_pools (struct device *dev, struct device_attribute *attr, char *buf)
@@ -55,7 +55,7 @@
 	size -= temp;
 	next += temp;
 
-	down (&pools_lock);
+	mutex_lock(&pools_lock);
 	list_for_each_entry(pool, &dev->dma_pools, pools) {
 		unsigned pages = 0;
 		unsigned blocks = 0;
@@ -73,7 +73,7 @@
 		size -= temp;
 		next += temp;
 	}
-	up (&pools_lock);
+	mutex_unlock(&pools_lock);
 
 	return PAGE_SIZE - size;
 }
@@ -143,7 +143,7 @@
 	if (dev) {
 		int ret;
 
-		down (&pools_lock);
+		mutex_lock(&pools_lock);
 		if (list_empty (&dev->dma_pools))
 			ret = device_create_file (dev, &dev_attr_pools);
 		else
@@ -155,7 +155,7 @@
 			kfree(retval);
 			retval = NULL;
 		}
-		up (&pools_lock);
+		mutex_unlock(&pools_lock);
 	} else
 		INIT_LIST_HEAD (&retval->pools);
 
@@ -231,11 +231,11 @@
 void
 dma_pool_destroy (struct dma_pool *pool)
 {
-	down (&pools_lock);
+	mutex_lock(&pools_lock);
 	list_del (&pool->pools);
 	if (pool->dev && list_empty (&pool->dev->dma_pools))
 		device_remove_file (pool->dev, &dev_attr_pools);
-	up (&pools_lock);
+	mutex_unlock(&pools_lock);
 
 	while (!list_empty (&pool->page_list)) {
 		struct dma_page		*page;
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index 082bfde..eb11475 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -149,10 +149,6 @@
  *	We pass off most of the work to the bus_add_driver() call,
  *	since most of the things we have to do deal with the bus
  *	structures.
- *
- *	The one interesting aspect is that we setup @drv->unloaded
- *	as a completion that gets complete when the driver reference
- *	count reaches 0.
  */
 int driver_register(struct device_driver * drv)
 {
@@ -162,35 +158,19 @@
 		printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name);
 	}
 	klist_init(&drv->klist_devices, NULL, NULL);
-	init_completion(&drv->unloaded);
 	return bus_add_driver(drv);
 }
 
-
 /**
  *	driver_unregister - remove driver from system.
  *	@drv:	driver.
  *
  *	Again, we pass off most of the work to the bus-level call.
- *
- *	Though, once that is done, we wait until @drv->unloaded is completed.
- *	This will block until the driver refcount reaches 0, and it is
- *	released. Only modular drivers will call this function, and we
- *	have to guarantee that it won't complete, letting the driver
- *	unload until all references are gone.
  */
 
 void driver_unregister(struct device_driver * drv)
 {
 	bus_remove_driver(drv);
-	/*
-	 * If the driver is a module, we are probably in
-	 * the module unload path, and we want to wait
-	 * for everything to unload before we can actually
-	 * finish the unload.
-	 */
-	if (drv->owner)
-		wait_for_completion(&drv->unloaded);
 }
 
 /**
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index c0a979a5..97ab5bd 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -31,8 +31,6 @@
 	FW_STATUS_LOADING,
 	FW_STATUS_DONE,
 	FW_STATUS_ABORT,
-	FW_STATUS_READY,
-	FW_STATUS_READY_NOHOTPLUG,
 };
 
 static int loading_timeout = 60;	/* In seconds */
@@ -96,9 +94,6 @@
 	struct firmware_priv *fw_priv = dev_get_drvdata(dev);
 	int i = 0, len = 0;
 
-	if (!test_bit(FW_STATUS_READY, &fw_priv->status))
-		return -ENODEV;
-
 	if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
 			   "FIRMWARE=%s", fw_priv->fw_id))
 		return -ENOMEM;
@@ -333,6 +328,7 @@
 	f_dev->parent = device;
 	f_dev->class = &firmware_class;
 	dev_set_drvdata(f_dev, fw_priv);
+	f_dev->uevent_suppress = 1;
 	retval = device_register(f_dev);
 	if (retval) {
 		printk(KERN_ERR "%s: device_register failed\n",
@@ -382,9 +378,7 @@
 	}
 
 	if (uevent)
-                set_bit(FW_STATUS_READY, &fw_priv->status);
-        else
-                set_bit(FW_STATUS_READY_NOHOTPLUG, &fw_priv->status);
+		f_dev->uevent_suppress = 0;
 	*dev_p = f_dev;
 	goto out;
 
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index bbbb973..05dc876 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -29,6 +29,9 @@
 DECLARE_MUTEX(dpm_sem);
 DECLARE_MUTEX(dpm_list_sem);
 
+int (*platform_enable_wakeup)(struct device *dev, int is_on);
+
+
 /**
  *	device_pm_set_parent - Specify power dependency.
  *	@dev:		Device who needs power.
diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c
index 020be36..a2c6418 100644
--- a/drivers/base/power/resume.c
+++ b/drivers/base/power/resume.c
@@ -26,7 +26,9 @@
 
 	TRACE_DEVICE(dev);
 	TRACE_RESUME(0);
+
 	down(&dev->sem);
+
 	if (dev->power.pm_parent
 			&& dev->power.pm_parent->power.power_state.event) {
 		dev_err(dev, "PM: resume from %d, parent %s still %d\n",
@@ -34,15 +36,24 @@
 			dev->power.pm_parent->bus_id,
 			dev->power.pm_parent->power.power_state.event);
 	}
+
 	if (dev->bus && dev->bus->resume) {
 		dev_dbg(dev,"resuming\n");
 		error = dev->bus->resume(dev);
 	}
-	if (dev->class && dev->class->resume) {
+
+	if (!error && dev->type && dev->type->resume) {
+		dev_dbg(dev,"resuming\n");
+		error = dev->type->resume(dev);
+	}
+
+	if (!error && dev->class && dev->class->resume) {
 		dev_dbg(dev,"class resume\n");
 		error = dev->class->resume(dev);
 	}
+
 	up(&dev->sem);
+
 	TRACE_RESUME(error);
 	return error;
 }
diff --git a/drivers/base/power/shutdown.c b/drivers/base/power/shutdown.c
index 3483ae4..58b6f77 100644
--- a/drivers/base/power/shutdown.c
+++ b/drivers/base/power/shutdown.c
@@ -36,7 +36,6 @@
 {
 	struct device * dev, *devn;
 
-	down_write(&devices_subsys.rwsem);
 	list_for_each_entry_safe_reverse(dev, devn, &devices_subsys.kset.list,
 				kobj.entry) {
 		if (dev->bus && dev->bus->shutdown) {
@@ -47,7 +46,6 @@
 			dev->driver->shutdown(dev);
 		}
 	}
-	up_write(&devices_subsys.rwsem);
 
 	sysdev_shutdown();
 }
diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c
index ece136b..42d2b86 100644
--- a/drivers/base/power/suspend.c
+++ b/drivers/base/power/suspend.c
@@ -78,6 +78,18 @@
 		suspend_report_result(dev->class->suspend, error);
 	}
 
+	if (!error && dev->type && dev->type->suspend && !dev->power.power_state.event) {
+		dev_dbg(dev, "%s%s\n",
+			suspend_verb(state.event),
+			((state.event == PM_EVENT_SUSPEND)
+					&& device_may_wakeup(dev))
+				? ", may wakeup"
+				: ""
+			);
+		error = dev->type->suspend(dev, state);
+		suspend_report_result(dev->type->suspend, error);
+	}
+
 	if (!error && dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
 		dev_dbg(dev, "%s%s\n",
 			suspend_verb(state.event),
diff --git a/drivers/block/aoe/aoe.h b/drivers/block/aoe/aoe.h
index 2308e83..1d84668 100644
--- a/drivers/block/aoe/aoe.h
+++ b/drivers/block/aoe/aoe.h
@@ -48,6 +48,15 @@
 	__be32 tag;
 };
 
+#ifdef __KERNEL__
+#include <linux/skbuff.h>
+
+static inline struct aoe_hdr *aoe_hdr(const struct sk_buff *skb)
+{
+	return (struct aoe_hdr *)skb_mac_header(skb);
+}
+#endif
+
 struct aoe_atahdr {
 	unsigned char aflags;
 	unsigned char errfeat;
diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c
index 8d17d8d..1a6aeac 100644
--- a/drivers/block/aoe/aoecmd.c
+++ b/drivers/block/aoe/aoecmd.c
@@ -27,7 +27,8 @@
 
 	skb = alloc_skb(len, GFP_ATOMIC);
 	if (skb) {
-		skb->nh.raw = skb->mac.raw = skb->data;
+		skb_reset_mac_header(skb);
+		skb_reset_network_header(skb);
 		skb->protocol = __constant_htons(ETH_P_AOE);
 		skb->priority = 0;
 		skb->next = skb->prev = NULL;
@@ -118,7 +119,7 @@
 
 	/* initialize the headers & frame */
 	skb = f->skb;
-	h = (struct aoe_hdr *) skb->mac.raw;
+	h = aoe_hdr(skb);
 	ah = (struct aoe_atahdr *) (h+1);
 	skb_put(skb, sizeof *h + sizeof *ah);
 	memset(h, 0, skb->len);
@@ -207,7 +208,7 @@
 		skb->dev = ifp;
 		if (sl_tail == NULL)
 			sl_tail = skb;
-		h = (struct aoe_hdr *) skb->mac.raw;
+		h = aoe_hdr(skb);
 		memset(h, 0, sizeof *h + sizeof *ch);
 
 		memset(h->dst, 0xff, sizeof h->dst);
@@ -300,7 +301,7 @@
 	aoechr_error(buf);
 
 	skb = f->skb;
-	h = (struct aoe_hdr *) skb->mac.raw;
+	h = aoe_hdr(skb);
 	ah = (struct aoe_atahdr *) (h+1);
 	f->tag = n;
 	h->tag = cpu_to_be32(n);
@@ -529,7 +530,7 @@
 	char ebuf[128];
 	u16 aoemajor;
 
-	hin = (struct aoe_hdr *) skb->mac.raw;
+	hin = aoe_hdr(skb);
 	aoemajor = be16_to_cpu(get_unaligned(&hin->major));
 	d = aoedev_by_aoeaddr(aoemajor, hin->minor);
 	if (d == NULL) {
@@ -561,7 +562,7 @@
 	calc_rttavg(d, tsince(f->tag));
 
 	ahin = (struct aoe_atahdr *) (hin+1);
-	hout = (struct aoe_hdr *) f->skb->mac.raw;
+	hout = aoe_hdr(f->skb);
 	ahout = (struct aoe_atahdr *) (hout+1);
 	buf = f->buf;
 
@@ -695,7 +696,7 @@
 
 	/* initialize the headers & frame */
 	skb = f->skb;
-	h = (struct aoe_hdr *) skb->mac.raw;
+	h = aoe_hdr(skb);
 	ah = (struct aoe_atahdr *) (h+1);
 	skb_put(skb, sizeof *h + sizeof *ah);
 	memset(h, 0, skb->len);
@@ -726,7 +727,7 @@
 	enum { MAXFRAMES = 16 };
 	u16 n;
 
-	h = (struct aoe_hdr *) skb->mac.raw;
+	h = aoe_hdr(skb);
 	ch = (struct aoe_cfghdr *) (h+1);
 
 	/*
diff --git a/drivers/block/aoe/aoenet.c b/drivers/block/aoe/aoenet.c
index aab6d91..f9ddfda 100644
--- a/drivers/block/aoe/aoenet.c
+++ b/drivers/block/aoe/aoenet.c
@@ -123,7 +123,7 @@
 		goto exit;
 	skb_push(skb, ETH_HLEN);	/* (1) */
 
-	h = (struct aoe_hdr *) skb->mac.raw;
+	h = aoe_hdr(skb);
 	n = be32_to_cpu(get_unaligned(&h->tag));
 	if ((h->verfl & AOEFL_RSP) == 0 || (n & 1<<31))
 		goto exit;
diff --git a/drivers/block/ub.c b/drivers/block/ub.c
index 2098eff..746a118 100644
--- a/drivers/block/ub.c
+++ b/drivers/block/ub.c
@@ -2132,10 +2132,13 @@
 		if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
 				== USB_ENDPOINT_XFER_BULK) {
 			/* BULK in or out? */
-			if (ep->bEndpointAddress & USB_DIR_IN)
-				ep_in = ep;
-			else
-				ep_out = ep;
+			if (ep->bEndpointAddress & USB_DIR_IN) {
+				if (ep_in == NULL)
+					ep_in = ep;
+			} else {
+				if (ep_out == NULL)
+					ep_out = ep;
+			}
 		}
 	}
 
diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c
index 4c766f3..b990805 100644
--- a/drivers/bluetooth/bfusb.c
+++ b/drivers/bluetooth/bfusb.c
@@ -527,7 +527,7 @@
 		buf[2] = (size == BFUSB_MAX_BLOCK_SIZE) ? 0 : size;
 
 		memcpy(skb_put(nskb, 3), buf, 3);
-		memcpy(skb_put(nskb, size), skb->data + sent, size);
+		skb_copy_from_linear_data_offset(skb, sent, skb_put(nskb, size), size);
 
 		sent  += size;
 		count -= size;
diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c
index acfb6a4..851de4d 100644
--- a/drivers/bluetooth/bluecard_cs.c
+++ b/drivers/bluetooth/bluecard_cs.c
@@ -461,20 +461,20 @@
 				switch (info->rx_state) {
 
 				case RECV_WAIT_EVENT_HEADER:
-					eh = (struct hci_event_hdr *)(info->rx_skb->data);
+					eh = hci_event_hdr(info->rx_skb);
 					info->rx_state = RECV_WAIT_DATA;
 					info->rx_count = eh->plen;
 					break;
 
 				case RECV_WAIT_ACL_HEADER:
-					ah = (struct hci_acl_hdr *)(info->rx_skb->data);
+					ah = hci_acl_hdr(info->rx_skb);
 					dlen = __le16_to_cpu(ah->dlen);
 					info->rx_state = RECV_WAIT_DATA;
 					info->rx_count = dlen;
 					break;
 
 				case RECV_WAIT_SCO_HEADER:
-					sh = (struct hci_sco_hdr *)(info->rx_skb->data);
+					sh = hci_sco_hdr(info->rx_skb);
 					info->rx_state = RECV_WAIT_DATA;
 					info->rx_count = sh->dlen;
 					break;
diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c
index 9fca651..e8ebd5d 100644
--- a/drivers/bluetooth/bpa10x.c
+++ b/drivers/bluetooth/bpa10x.c
@@ -231,7 +231,7 @@
 		cr = (struct usb_ctrlrequest *) urb->setup_packet;
 		cr->wLength = __cpu_to_le16(skb->len);
 
-		memcpy(urb->transfer_buffer, skb->data, skb->len);
+		skb_copy_from_linear_data(skb, urb->transfer_buffer, skb->len);
 		urb->transfer_buffer_length = skb->len;
 
 		err = usb_submit_urb(urb, GFP_ATOMIC);
@@ -250,7 +250,7 @@
 		skb = skb_dequeue(&data->tx_queue);
 
 	if (skb) {
-		memcpy(urb->transfer_buffer, skb->data, skb->len);
+		skb_copy_from_linear_data(skb, urb->transfer_buffer, skb->len);
 		urb->transfer_buffer_length = skb->len;
 
 		err = usb_submit_urb(urb, GFP_ATOMIC);
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
index 18b0f39..3951607 100644
--- a/drivers/bluetooth/bt3c_cs.c
+++ b/drivers/bluetooth/bt3c_cs.c
@@ -303,20 +303,20 @@
 				switch (info->rx_state) {
 
 				case RECV_WAIT_EVENT_HEADER:
-					eh = (struct hci_event_hdr *)(info->rx_skb->data);
+					eh = hci_event_hdr(info->rx_skb);
 					info->rx_state = RECV_WAIT_DATA;
 					info->rx_count = eh->plen;
 					break;
 
 				case RECV_WAIT_ACL_HEADER:
-					ah = (struct hci_acl_hdr *)(info->rx_skb->data);
+					ah = hci_acl_hdr(info->rx_skb);
 					dlen = __le16_to_cpu(ah->dlen);
 					info->rx_state = RECV_WAIT_DATA;
 					info->rx_count = dlen;
 					break;
 
 				case RECV_WAIT_SCO_HEADER:
-					sh = (struct hci_sco_hdr *)(info->rx_skb->data);
+					sh = hci_sco_hdr(info->rx_skb);
 					info->rx_state = RECV_WAIT_DATA;
 					info->rx_count = sh->dlen;
 					break;
diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c
index c1bce75..d7d2ea0d 100644
--- a/drivers/bluetooth/btuart_cs.c
+++ b/drivers/bluetooth/btuart_cs.c
@@ -250,20 +250,20 @@
 				switch (info->rx_state) {
 
 				case RECV_WAIT_EVENT_HEADER:
-					eh = (struct hci_event_hdr *)(info->rx_skb->data);
+					eh = hci_event_hdr(info->rx_skb);
 					info->rx_state = RECV_WAIT_DATA;
 					info->rx_count = eh->plen;
 					break;
 
 				case RECV_WAIT_ACL_HEADER:
-					ah = (struct hci_acl_hdr *)(info->rx_skb->data);
+					ah = hci_acl_hdr(info->rx_skb);
 					dlen = __le16_to_cpu(ah->dlen);
 					info->rx_state = RECV_WAIT_DATA;
 					info->rx_count = dlen;
 					break;
 
 				case RECV_WAIT_SCO_HEADER:
-					sh = (struct hci_sco_hdr *)(info->rx_skb->data);
+					sh = hci_sco_hdr(info->rx_skb);
 					info->rx_state = RECV_WAIT_DATA;
 					info->rx_count = sh->dlen;
 					break;
diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c
index 459aa97..7f9c54b 100644
--- a/drivers/bluetooth/dtl1_cs.c
+++ b/drivers/bluetooth/dtl1_cs.c
@@ -425,7 +425,7 @@
 		return -ENOMEM;
 
 	skb_reserve(s, NSHL);
-	memcpy(skb_put(s, skb->len), skb->data, skb->len);
+	skb_copy_from_linear_data(skb, skb_put(s, skb->len), skb->len);
 	if (skb->len & 0x0001)
 		*skb_put(s, 1) = 0;	/* PAD */
 
diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c
index 34f0afc..bfbae14 100644
--- a/drivers/bluetooth/hci_h4.c
+++ b/drivers/bluetooth/hci_h4.c
@@ -188,7 +188,7 @@
 				continue;
 
 			case H4_W4_EVENT_HDR:
-				eh = (struct hci_event_hdr *) h4->rx_skb->data;
+				eh = hci_event_hdr(h4->rx_skb);
 
 				BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen);
 
@@ -196,7 +196,7 @@
 				continue;
 
 			case H4_W4_ACL_HDR:
-				ah = (struct hci_acl_hdr *) h4->rx_skb->data;
+				ah = hci_acl_hdr(h4->rx_skb);
 				dlen = __le16_to_cpu(ah->dlen);
 
 				BT_DBG("ACL header: dlen %d", dlen);
@@ -205,7 +205,7 @@
 				continue;
 
 			case H4_W4_SCO_HDR:
-				sh = (struct hci_sco_hdr *) h4->rx_skb->data;
+				sh = hci_sco_hdr(h4->rx_skb);
 
 				BT_DBG("SCO header: dlen %d", sh->dlen);
 
diff --git a/drivers/char/agp/ali-agp.c b/drivers/char/agp/ali-agp.c
index 5b684fd..4941ddb 100644
--- a/drivers/char/agp/ali-agp.c
+++ b/drivers/char/agp/ali-agp.c
@@ -145,6 +145,7 @@
 	void *addr = agp_generic_alloc_page(agp_bridge);
 	u32 temp;
 
+	global_flush_tlb();
 	if (!addr)
 		return NULL;
 
@@ -160,6 +161,7 @@
 	if (addr) {
 		global_cache_flush();	/* is this really needed?  --hch */
 		agp_generic_destroy_page(addr);
+		global_flush_tlb();
 	}
 }
 
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c
index f902d71..45aeb91 100644
--- a/drivers/char/agp/generic.c
+++ b/drivers/char/agp/generic.c
@@ -51,28 +51,6 @@
  */
 EXPORT_SYMBOL_GPL(agp_memory_reserved);
 
-#if defined(CONFIG_X86)
-int map_page_into_agp(struct page *page)
-{
-	int i;
-	i = change_page_attr(page, 1, PAGE_KERNEL_NOCACHE);
-	/* Caller's responsibility to call global_flush_tlb() for
-	 * performance reasons */
-	return i;
-}
-EXPORT_SYMBOL_GPL(map_page_into_agp);
-
-int unmap_page_from_agp(struct page *page)
-{
-	int i;
-	i = change_page_attr(page, 1, PAGE_KERNEL);
-	/* Caller's responsibility to call global_flush_tlb() for
-	 * performance reasons */
-	return i;
-}
-EXPORT_SYMBOL_GPL(unmap_page_from_agp);
-#endif
-
 /*
  * Generic routines for handling agp_memory structures -
  * They use the basic page allocation routines to do the brunt of the work.
diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c
index 55392a4..9c69f2e 100644
--- a/drivers/char/agp/intel-agp.c
+++ b/drivers/char/agp/intel-agp.c
@@ -186,8 +186,9 @@
 		return NULL;
 
 	if (change_page_attr(page, 4, PAGE_KERNEL_NOCACHE) < 0) {
+		change_page_attr(page, 4, PAGE_KERNEL);
 		global_flush_tlb();
-		__free_page(page);
+		__free_pages(page, 2);
 		return NULL;
 	}
 	global_flush_tlb();
@@ -209,7 +210,7 @@
 	global_flush_tlb();
 	put_page(page);
 	unlock_page(page);
-	free_pages((unsigned long)addr, 2);
+	__free_pages(page, 2);
 	atomic_dec(&agp_bridge->current_memory_agp);
 }
 
@@ -315,9 +316,6 @@
 	struct agp_memory *new;
 	void *addr;
 
-	if (pg_count != 1 && pg_count != 4)
-		return NULL;
-
 	switch (pg_count) {
 	case 1: addr = agp_bridge->driver->agp_alloc_page(agp_bridge);
 		global_flush_tlb();
diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c
index 0c9dab5..6cd7373 100644
--- a/drivers/char/agp/nvidia-agp.c
+++ b/drivers/char/agp/nvidia-agp.c
@@ -320,11 +320,11 @@
 	u8 cap_ptr;
 
 	nvidia_private.dev_1 =
-		pci_find_slot((unsigned int)pdev->bus->number, PCI_DEVFN(0, 1));
+		pci_get_bus_and_slot((unsigned int)pdev->bus->number, PCI_DEVFN(0, 1));
 	nvidia_private.dev_2 =
-		pci_find_slot((unsigned int)pdev->bus->number, PCI_DEVFN(0, 2));
+		pci_get_bus_and_slot((unsigned int)pdev->bus->number, PCI_DEVFN(0, 2));
 	nvidia_private.dev_3 =
-		pci_find_slot((unsigned int)pdev->bus->number, PCI_DEVFN(30, 0));
+		pci_get_bus_and_slot((unsigned int)pdev->bus->number, PCI_DEVFN(30, 0));
 
 	if (!nvidia_private.dev_1 || !nvidia_private.dev_2 || !nvidia_private.dev_3) {
 		printk(KERN_INFO PFX "Detected an NVIDIA nForce/nForce2 "
@@ -443,6 +443,9 @@
 static void __exit agp_nvidia_cleanup(void)
 {
 	pci_unregister_driver(&agp_nvidia_pci_driver);
+	pci_dev_put(nvidia_private.dev_1);
+	pci_dev_put(nvidia_private.dev_2);
+	pci_dev_put(nvidia_private.dev_3);
 }
 
 module_init(agp_nvidia_init);
diff --git a/drivers/char/agp/sgi-agp.c b/drivers/char/agp/sgi-agp.c
index ee8f50e..cda608c 100644
--- a/drivers/char/agp/sgi-agp.c
+++ b/drivers/char/agp/sgi-agp.c
@@ -47,9 +47,8 @@
 
 	nid = info->ca_closest_node;
 	page = alloc_pages_node(nid, GFP_KERNEL, 0);
-	if (page == NULL) {
-		return 0;
-	}
+	if (!page)
+		return NULL;
 
 	get_page(page);
 	SetPageLocked(page);
diff --git a/drivers/char/agp/sis-agp.c b/drivers/char/agp/sis-agp.c
index 125f428..eb1a1c7 100644
--- a/drivers/char/agp/sis-agp.c
+++ b/drivers/char/agp/sis-agp.c
@@ -143,96 +143,6 @@
 	.agp_type_to_mask_type  = agp_generic_type_to_mask_type,
 };
 
-static struct agp_device_ids sis_agp_device_ids[] __devinitdata =
-{
-	{
-		.device_id	= PCI_DEVICE_ID_SI_5591_AGP,
-		.chipset_name	= "5591",
-	},
-	{
-		.device_id	= PCI_DEVICE_ID_SI_530,
-		.chipset_name	= "530",
-	},
-	{
-		.device_id	= PCI_DEVICE_ID_SI_540,
-		.chipset_name	= "540",
-	},
-	{
-		.device_id	= PCI_DEVICE_ID_SI_550,
-		.chipset_name	= "550",
-	},
-	{
-		.device_id	= PCI_DEVICE_ID_SI_620,
-		.chipset_name	= "620",
-	},
-	{
-		.device_id	= PCI_DEVICE_ID_SI_630,
-		.chipset_name	= "630",
-	},
-	{
-		.device_id	= PCI_DEVICE_ID_SI_635,
-		.chipset_name	= "635",
-	},
-	{
-		.device_id	= PCI_DEVICE_ID_SI_645,
-		.chipset_name	= "645",
-	},
-	{
-		.device_id	= PCI_DEVICE_ID_SI_646,
-		.chipset_name	= "646",
-	},
-	{
-		.device_id	= PCI_DEVICE_ID_SI_648,
-		.chipset_name	= "648",
-	},
-	{
-		.device_id	= PCI_DEVICE_ID_SI_650,
-		.chipset_name	= "650",
-	},
-	{
-		.device_id  = PCI_DEVICE_ID_SI_651,
-		.chipset_name   = "651",
-	},
-	{
-		.device_id	= PCI_DEVICE_ID_SI_655,
-		.chipset_name	= "655",
-	},
-	{
-		.device_id	= PCI_DEVICE_ID_SI_661,
-		.chipset_name	= "661",
-	},
-	{
-		.device_id	= PCI_DEVICE_ID_SI_730,
-		.chipset_name	= "730",
-	},
-	{
-		.device_id	= PCI_DEVICE_ID_SI_735,
-		.chipset_name	= "735",
-	},
-	{
-		.device_id	= PCI_DEVICE_ID_SI_740,
-		.chipset_name	= "740",
-	},
-	{
-		.device_id	= PCI_DEVICE_ID_SI_741,
-		.chipset_name	= "741",
-	},
-	{
-		.device_id	= PCI_DEVICE_ID_SI_745,
-		.chipset_name	= "745",
-	},
-	{
-		.device_id	= PCI_DEVICE_ID_SI_746,
-		.chipset_name	= "746",
-	},
-	{
-		.device_id	= PCI_DEVICE_ID_SI_760,
-		.chipset_name	= "760",
-	},
-	{ }, /* dummy final entry, always present */
-};
-
-
 // chipsets that require the 'delay hack'
 static int sis_broken_chipsets[] __devinitdata = {
 	PCI_DEVICE_ID_SI_648,
@@ -269,29 +179,15 @@
 static int __devinit agp_sis_probe(struct pci_dev *pdev,
 				   const struct pci_device_id *ent)
 {
-	struct agp_device_ids *devs = sis_agp_device_ids;
 	struct agp_bridge_data *bridge;
 	u8 cap_ptr;
-	int j;
 
 	cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
 	if (!cap_ptr)
 		return -ENODEV;
 
-	/* probe for known chipsets */
-	for (j = 0; devs[j].chipset_name; j++) {
-		if (pdev->device == devs[j].device_id) {
-			printk(KERN_INFO PFX "Detected SiS %s chipset\n",
-					devs[j].chipset_name);
-			goto found;
-		}
-	}
 
-	printk(KERN_ERR PFX "Unsupported SiS chipset (device id: %04x)\n",
-		    pdev->device);
-	return -ENODEV;
-
-found:
+	printk(KERN_INFO PFX "Detected SiS chipset - id:%i\n", pdev->device);
 	bridge = agp_alloc_bridge();
 	if (!bridge)
 		return -ENOMEM;
@@ -320,12 +216,172 @@
 
 static struct pci_device_id agp_sis_pci_table[] = {
 	{
-	.class		= (PCI_CLASS_BRIDGE_HOST << 8),
-	.class_mask	= ~0,
-	.vendor		= PCI_VENDOR_ID_SI,
-	.device		= PCI_ANY_ID,
-	.subvendor	= PCI_ANY_ID,
-	.subdevice	= PCI_ANY_ID,
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_5591_AGP,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_530,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_540,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_550,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_620,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_630,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_635,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_645,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_646,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_648,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_650,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_651,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_655,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_661,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_730,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_735,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_740,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_741,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_745,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_746,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{
+		.class		= (PCI_CLASS_BRIDGE_HOST << 8),
+		.class_mask	= ~0,
+		.vendor		= PCI_VENDOR_ID_SI,
+		.device		= PCI_DEVICE_ID_SI_760,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
 	},
 	{ }
 };
diff --git a/drivers/char/agp/sworks-agp.c b/drivers/char/agp/sworks-agp.c
index 55212a3..551ef25 100644
--- a/drivers/char/agp/sworks-agp.c
+++ b/drivers/char/agp/sworks-agp.c
@@ -455,15 +455,6 @@
 	u32 temp, temp2;
 	u8 cap_ptr = 0;
 
-	/* Everything is on func 1 here so we are hardcoding function one */
-	bridge_dev = pci_find_slot((unsigned int)pdev->bus->number,
-			PCI_DEVFN(0, 1));
-	if (!bridge_dev) {
-		printk(KERN_INFO PFX "Detected a Serverworks chipset "
-		       "but could not find the secondary device.\n");
-		return -ENODEV;
-	}
-
 	cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
 
 	switch (pdev->device) {
@@ -483,6 +474,15 @@
 		return -ENODEV;
 	}
 
+	/* Everything is on func 1 here so we are hardcoding function one */
+	bridge_dev = pci_get_bus_and_slot((unsigned int)pdev->bus->number,
+			PCI_DEVFN(0, 1));
+	if (!bridge_dev) {
+		printk(KERN_INFO PFX "Detected a Serverworks chipset "
+		       "but could not find the secondary device.\n");
+		return -ENODEV;
+	}
+
 	serverworks_private.svrwrks_dev = bridge_dev;
 	serverworks_private.gart_addr_ofs = 0x10;
 
@@ -515,7 +515,7 @@
 
 	bridge->driver = &sworks_driver;
 	bridge->dev_private_data = &serverworks_private,
-	bridge->dev = pdev;
+	bridge->dev = pci_dev_get(pdev);
 
 	pci_set_drvdata(pdev, bridge);
 	return agp_add_bridge(bridge);
@@ -525,8 +525,11 @@
 {
 	struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
 
+	pci_dev_put(bridge->dev);
 	agp_remove_bridge(bridge);
 	agp_put_bridge(bridge);
+	pci_dev_put(serverworks_private.svrwrks_dev);
+	serverworks_private.svrwrks_dev = NULL;
 }
 
 static struct pci_device_id agp_serverworks_pci_table[] = {
diff --git a/drivers/char/briq_panel.c b/drivers/char/briq_panel.c
index 8dcf9d2..c70d52a 100644
--- a/drivers/char/briq_panel.c
+++ b/drivers/char/briq_panel.c
@@ -202,13 +202,16 @@
 
 static int __init briq_panel_init(void)
 {
-	struct device_node *root = find_path_device("/");
+	struct device_node *root = of_find_node_by_path("/");
 	const char *machine;
 	int i;
 
 	machine = get_property(root, "model", NULL);
-	if (!machine || strncmp(machine, "TotalImpact,BRIQ-1", 18) != 0)
+	if (!machine || strncmp(machine, "TotalImpact,BRIQ-1", 18) != 0) {
+		of_node_put(root);
 		return -ENODEV;
+	}
+	of_node_put(root);
 
 	printk(KERN_INFO
 		"briq_panel: v%s Dr. Karsten Jeppesen (kj@totalimpact.com)\n",
diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c
index a0a88aa2..0f9ed7b 100644
--- a/drivers/char/hvc_console.c
+++ b/drivers/char/hvc_console.c
@@ -47,8 +47,6 @@
 #define HVC_MAJOR	229
 #define HVC_MINOR	0
 
-#define TIMEOUT		(10)
-
 /*
  * Wait this long per iteration while trying to push buffered data to the
  * hypervisor before allowing the tty to complete a close operation.
@@ -104,12 +102,12 @@
 /*
  * This value is used to assign a tty->index value to a hvc_struct based
  * upon order of exposure via hvc_probe(), when we can not match it to
- * a console canidate registered with hvc_instantiate().
+ * a console candidate registered with hvc_instantiate().
  */
 static int last_hvc = -1;
 
 /*
- * Do not call this function with either the hvc_strucst_lock or the hvc_struct
+ * Do not call this function with either the hvc_structs_lock or the hvc_struct
  * lock held.  If successful, this function increments the kobject reference
  * count against the target hvc_struct so it should be released when finished.
  */
@@ -162,7 +160,7 @@
 	if (index >= MAX_NR_HVC_CONSOLES)
 		return;
 
-	/* This console adapter was removed so it is not useable. */
+	/* This console adapter was removed so it is not usable. */
 	if (vtermnos[index] < 0)
 		return;
 
@@ -220,7 +218,7 @@
 };
 
 /*
- * Early console initialization.  Preceeds driver initialization.
+ * Early console initialization.  Precedes driver initialization.
  *
  * (1) we are first, and the user specified another driver
  * -- index will remain -1
@@ -257,7 +255,7 @@
 	if (vtermnos[index] != -1)
 		return -1;
 
-	/* make sure no no tty has been registerd in this index */
+	/* make sure no no tty has been registered in this index */
 	hp = hvc_get_by_index(index);
 	if (hp) {
 		kobject_put(&hp->kobj);
@@ -267,7 +265,7 @@
 	vtermnos[index] = vtermno;
 	cons_ops[index] = ops;
 
-	/* reserve all indices upto and including this index */
+	/* reserve all indices up to and including this index */
 	if (last_hvc < index)
 		last_hvc = index;
 
@@ -528,7 +526,7 @@
 
 /*
  * This is actually a contract between the driver and the tty layer outlining
- * how much write room the driver can guarentee will be sent OR BUFFERED.  This
+ * how much write room the driver can guarantee will be sent OR BUFFERED.  This
  * driver MUST honor the return value.
  */
 static int hvc_write_room(struct tty_struct *tty)
@@ -550,6 +548,18 @@
 	return hp->n_outbuf;
 }
 
+/*
+ * timeout will vary between the MIN and MAX values defined here.  By default
+ * and during console activity we will use a default MIN_TIMEOUT of 10.  When
+ * the console is idle, we increase the timeout value on each pass through
+ * msleep until we reach the max.  This may be noticeable as a brief (average
+ * one second) delay on the console before the console responds to input when
+ * there has been no input for some time.
+ */
+#define MIN_TIMEOUT		(10)
+#define MAX_TIMEOUT		(2000)
+static u32 timeout = MIN_TIMEOUT;
+
 #define HVC_POLL_READ	0x00000001
 #define HVC_POLL_WRITE	0x00000002
 
@@ -642,9 +652,14 @@
  bail:
 	spin_unlock_irqrestore(&hp->lock, flags);
 
-	if (read_total)
+	if (read_total) {
+		/* Activity is occurring, so reset the polling backoff value to
+		   a minimum for performance. */
+		timeout = MIN_TIMEOUT;
+
 		tty_flip_buffer_push(tty);
-	
+	}
+
 	return poll_mask;
 }
 
@@ -688,8 +703,12 @@
 		if (!hvc_kicked) {
 			if (poll_mask == 0)
 				schedule();
-			else
-				msleep_interruptible(TIMEOUT);
+			else {
+				if (timeout < MAX_TIMEOUT)
+					timeout += (timeout >> 6) + 1;
+
+				msleep_interruptible(timeout);
+			}
 		}
 		__set_current_state(TASK_RUNNING);
 	} while (!kthread_should_stop());
@@ -794,7 +813,7 @@
 
 	/*
 	 * We 'put' the instance that was grabbed when the kobject instance
-	 * was intialized using kobject_init().  Let the last holder of this
+	 * was initialized using kobject_init().  Let the last holder of this
 	 * kobject cause it to be removed, which will probably be the tty_hangup
 	 * below.
 	 */
@@ -850,7 +869,7 @@
 }
 module_init(hvc_init);
 
-/* This isn't particularily necessary due to this being a console driver
+/* This isn't particularly necessary due to this being a console driver
  * but it is nice to be thorough.
  */
 static void __exit hvc_exit(void)
diff --git a/drivers/char/hvc_iseries.c b/drivers/char/hvc_iseries.c
index f144a94..ec420fe 100644
--- a/drivers/char/hvc_iseries.c
+++ b/drivers/char/hvc_iseries.c
@@ -575,7 +575,7 @@
 				(num_found >= VTTY_PORTS))
 			break;
 
-		vtermno = get_property(vty, "reg", NULL);
+		vtermno = of_get_property(vty, "reg", NULL);
 		if (!vtermno)
 			continue;
 
diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c
index f9c0084..94a542e 100644
--- a/drivers/char/hvc_vio.c
+++ b/drivers/char/hvc_vio.c
@@ -153,7 +153,7 @@
 		if (num_found >= MAX_NR_HVC_CONSOLES)
 			break;
 
-		vtermno = get_property(vty, "reg", NULL);
+		vtermno = of_get_property(vty, "reg", NULL);
 		if (!vtermno)
 			continue;
 
diff --git a/drivers/char/hvsi.c b/drivers/char/hvsi.c
index 50315d6..d5a752d 100644
--- a/drivers/char/hvsi.c
+++ b/drivers/char/hvsi.c
@@ -1279,8 +1279,8 @@
 		struct hvsi_struct *hp;
 		const uint32_t *vtermno, *irq;
 
-		vtermno = get_property(vty, "reg", NULL);
-		irq = get_property(vty, "interrupts", NULL);
+		vtermno = of_get_property(vty, "reg", NULL);
+		irq = of_get_property(vty, "interrupts", NULL);
 		if (!vtermno || !irq)
 			continue;
 
diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c
index 8d025e9..157b1d0 100644
--- a/drivers/char/pcmcia/synclink_cs.c
+++ b/drivers/char/pcmcia/synclink_cs.c
@@ -4169,7 +4169,7 @@
 	netif_stop_queue(dev);
 
 	/* copy data to device buffers */
-	memcpy(info->tx_buf, skb->data, skb->len);
+	skb_copy_from_linear_data(skb, info->tx_buf, skb->len);
 	info->tx_get = 0;
 	info->tx_put = info->tx_count = skb->len;
 
diff --git a/drivers/char/random.c b/drivers/char/random.c
index b9dc7aa..46c1b9774 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -881,15 +881,15 @@
  */
 static void init_std_data(struct entropy_store *r)
 {
-	struct timeval tv;
+	ktime_t now;
 	unsigned long flags;
 
 	spin_lock_irqsave(&r->lock, flags);
 	r->entropy_count = 0;
 	spin_unlock_irqrestore(&r->lock, flags);
 
-	do_gettimeofday(&tv);
-	add_entropy_words(r, (__u32 *)&tv, sizeof(tv)/4);
+	now = ktime_get_real();
+	add_entropy_words(r, (__u32 *)&now, sizeof(now)/4);
 	add_entropy_words(r, (__u32 *)utsname(),
 			  sizeof(*(utsname()))/4);
 }
@@ -911,14 +911,12 @@
 		return;
 
 	/*
-	 * If kmalloc returns null, we just won't use that entropy
+	 * If kzalloc returns null, we just won't use that entropy
 	 * source.
 	 */
-	state = kmalloc(sizeof(struct timer_rand_state), GFP_KERNEL);
-	if (state) {
-		memset(state, 0, sizeof(struct timer_rand_state));
+	state = kzalloc(sizeof(struct timer_rand_state), GFP_KERNEL);
+	if (state)
 		irq_timer_state[irq] = state;
-	}
 }
 
 #ifdef CONFIG_BLOCK
@@ -927,14 +925,12 @@
 	struct timer_rand_state *state;
 
 	/*
-	 * If kmalloc returns null, we just won't use that entropy
+	 * If kzalloc returns null, we just won't use that entropy
 	 * source.
 	 */
-	state = kmalloc(sizeof(struct timer_rand_state), GFP_KERNEL);
-	if (state) {
-		memset(state, 0, sizeof(struct timer_rand_state));
+	state = kzalloc(sizeof(struct timer_rand_state), GFP_KERNEL);
+	if (state)
 		disk->random = state;
-	}
 }
 #endif
 
@@ -1469,7 +1465,6 @@
 __u32 secure_tcpv6_sequence_number(__be32 *saddr, __be32 *daddr,
 				   __be16 sport, __be16 dport)
 {
-	struct timeval tv;
 	__u32 seq;
 	__u32 hash[12];
 	struct keydata *keyptr = get_keyptr();
@@ -1485,8 +1480,7 @@
 	seq = twothirdsMD4Transform((const __u32 *)daddr, hash) & HASH_MASK;
 	seq += keyptr->count;
 
-	do_gettimeofday(&tv);
-	seq += tv.tv_usec + tv.tv_sec * 1000000;
+	seq += ktime_get_real().tv64;
 
 	return seq;
 }
@@ -1521,7 +1515,6 @@
 __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
 				 __be16 sport, __be16 dport)
 {
-	struct timeval tv;
 	__u32 seq;
 	__u32 hash[4];
 	struct keydata *keyptr = get_keyptr();
@@ -1543,12 +1536,11 @@
 	 *	As close as possible to RFC 793, which
 	 *	suggests using a 250 kHz clock.
 	 *	Further reading shows this assumes 2 Mb/s networks.
-	 *	For 10 Mb/s Ethernet, a 1 MHz clock is appropriate.
+	 *	For 10 Gb/s Ethernet, a 1 GHz clock is appropriate.
 	 *	That's funny, Linux has one built in!  Use it!
 	 *	(Networks are faster now - should this be increased?)
 	 */
-	do_gettimeofday(&tv);
-	seq += tv.tv_usec + tv.tv_sec * 1000000;
+	seq += ktime_get_real().tv64;
 #if 0
 	printk("init_seq(%lx, %lx, %d, %d) = %d\n",
 	       saddr, daddr, sport, dport, seq);
@@ -1556,8 +1548,6 @@
 	return seq;
 }
 
-EXPORT_SYMBOL(secure_tcp_sequence_number);
-
 /* Generate secure starting point for ephemeral IPV4 transport port search */
 u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport)
 {
@@ -1598,7 +1588,6 @@
 u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr,
 				__be16 sport, __be16 dport)
 {
-	struct timeval tv;
 	u64 seq;
 	__u32 hash[4];
 	struct keydata *keyptr = get_keyptr();
@@ -1611,8 +1600,7 @@
 	seq = half_md4_transform(hash, keyptr->secret);
 	seq |= ((u64)keyptr->count) << (32 - HASH_BITS);
 
-	do_gettimeofday(&tv);
-	seq += tv.tv_usec + tv.tv_sec * 1000000;
+	seq += ktime_get_real().tv64;
 	seq &= (1ull << 48) - 1;
 #if 0
 	printk("dccp init_seq(%lx, %lx, %d, %d) = %d\n",
diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c
index 7823757..3ef593a 100644
--- a/drivers/char/sonypi.c
+++ b/drivers/char/sonypi.c
@@ -1,6 +1,8 @@
 /*
  * Sony Programmable I/O Control Device driver for VAIO
  *
+ * Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
+ *
  * Copyright (C) 2001-2005 Stelian Pop <stelian@popies.net>
  *
  * Copyright (C) 2005 Narayanan R S <nars@kadamba.org>
@@ -95,6 +97,11 @@
 MODULE_PARM_DESC(useinput,
 		 "set this if you would like sonypi to feed events to the input subsystem");
 
+static int check_ioport = 1;
+module_param(check_ioport, int, 0444);
+MODULE_PARM_DESC(check_ioport,
+		 "set this to 0 if you think the automatic ioport check for sony-laptop is wrong");
+
 #define SONYPI_DEVICE_MODEL_TYPE1	1
 #define SONYPI_DEVICE_MODEL_TYPE2	2
 #define SONYPI_DEVICE_MODEL_TYPE3	3
@@ -477,7 +484,7 @@
 	u16 evtype_offset;
 	int camera_power;
 	int bluetooth_power;
-	struct semaphore lock;
+	struct mutex lock;
 	struct kfifo *fifo;
 	spinlock_t fifo_lock;
 	wait_queue_head_t fifo_proc_list;
@@ -884,7 +891,7 @@
 	if (!camera)
 		return -EIO;
 
-	down(&sonypi_device.lock);
+	mutex_lock(&sonypi_device.lock);
 
 	switch (command) {
 	case SONYPI_COMMAND_SETCAMERA:
@@ -919,7 +926,7 @@
 		       command);
 		break;
 	}
-	up(&sonypi_device.lock);
+	mutex_unlock(&sonypi_device.lock);
 	return 0;
 }
 
@@ -938,20 +945,20 @@
 static int sonypi_misc_release(struct inode *inode, struct file *file)
 {
 	sonypi_misc_fasync(-1, file, 0);
-	down(&sonypi_device.lock);
+	mutex_lock(&sonypi_device.lock);
 	sonypi_device.open_count--;
-	up(&sonypi_device.lock);
+	mutex_unlock(&sonypi_device.lock);
 	return 0;
 }
 
 static int sonypi_misc_open(struct inode *inode, struct file *file)
 {
-	down(&sonypi_device.lock);
+	mutex_lock(&sonypi_device.lock);
 	/* Flush input queue on first open */
 	if (!sonypi_device.open_count)
 		kfifo_reset(sonypi_device.fifo);
 	sonypi_device.open_count++;
-	up(&sonypi_device.lock);
+	mutex_unlock(&sonypi_device.lock);
 	return 0;
 }
 
@@ -1001,7 +1008,7 @@
 	u8 val8;
 	u16 val16;
 
-	down(&sonypi_device.lock);
+	mutex_lock(&sonypi_device.lock);
 	switch (cmd) {
 	case SONYPI_IOCGBRT:
 		if (sonypi_ec_read(SONYPI_LCD_LIGHT, &val8)) {
@@ -1101,7 +1108,7 @@
 	default:
 		ret = -EINVAL;
 	}
-	up(&sonypi_device.lock);
+	mutex_unlock(&sonypi_device.lock);
 	return ret;
 }
 
@@ -1260,6 +1267,28 @@
 static int __devinit sonypi_setup_ioports(struct sonypi_device *dev,
 				const struct sonypi_ioport_list *ioport_list)
 {
+	/* try to detect if sony-laptop is being used and thus
+	 * has already requested one of the known ioports.
+	 * As in the deprecated check_region this is racy has we have
+	 * multiple ioports available and one of them can be requested
+	 * between this check and the subsequent request. Anyway, as an
+	 * attempt to be some more user-friendly as we currently are,
+	 * this is enough.
+	 */
+	const struct sonypi_ioport_list *check = ioport_list;
+	while (check_ioport && check->port1) {
+		if (!request_region(check->port1,
+				   sonypi_device.region_size,
+				   "Sony Programable I/O Device Check")) {
+			printk(KERN_ERR "sonypi: ioport 0x%.4x busy, using sony-laptop? "
+					"if not use check_ioport=0\n",
+					check->port1);
+			return -EBUSY;
+		}
+		release_region(check->port1, sonypi_device.region_size);
+		check++;
+	}
+
 	while (ioport_list->port1) {
 
 		if (request_region(ioport_list->port1,
@@ -1321,6 +1350,10 @@
 	struct pci_dev *pcidev;
 	int error;
 
+	printk(KERN_WARNING "sonypi: please try the sony-laptop module instead "
+			"and report failures, see also "
+			"http://www.linux.it/~malattia/wiki/index.php/Sony_drivers\n");
+
 	spin_lock_init(&sonypi_device.fifo_lock);
 	sonypi_device.fifo = kfifo_alloc(SONYPI_BUF_SIZE, GFP_KERNEL,
 					 &sonypi_device.fifo_lock);
@@ -1330,7 +1363,7 @@
 	}
 
 	init_waitqueue_head(&sonypi_device.fifo_proc_list);
-	init_MUTEX(&sonypi_device.lock);
+	mutex_init(&sonypi_device.lock);
 	sonypi_device.bluetooth_power = -1;
 
 	if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
diff --git a/drivers/char/tpm/tpm_atmel.h b/drivers/char/tpm/tpm_atmel.h
index aefd683..3c85200 100644
--- a/drivers/char/tpm/tpm_atmel.h
+++ b/drivers/char/tpm/tpm_atmel.h
@@ -53,8 +53,8 @@
 	}
 
 	reg = get_property(dn, "reg", &reglen);
-	naddrc = prom_n_addr_cells(dn);
-	nsizec = prom_n_size_cells(dn);
+	naddrc = of_n_addr_cells(dn);
+	nsizec = of_n_size_cells(dn);
 
 	of_node_put(dn);
 
diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig
index e812aa1..60198a7 100644
--- a/drivers/char/watchdog/Kconfig
+++ b/drivers/char/watchdog/Kconfig
@@ -548,7 +548,7 @@
 	depends on WATCHDOG && MV64X60
 
 config BOOKE_WDT
-	tristate "PowerPC Book-E Watchdog Timer"
+	bool "PowerPC Book-E Watchdog Timer"
 	depends on WATCHDOG && (BOOKE || 4xx)
 	---help---
 	  Please see Documentation/watchdog/watchdog-api.txt for
diff --git a/drivers/clocksource/acpi_pm.c b/drivers/clocksource/acpi_pm.c
index 5ac309e..5cfcff5 100644
--- a/drivers/clocksource/acpi_pm.c
+++ b/drivers/clocksource/acpi_pm.c
@@ -26,7 +26,7 @@
 /*
  * The I/O port the PMTMR resides at.
  * The location is detected during setup_arch(),
- * in arch/i386/acpi/boot.c
+ * in arch/i386/kernel/acpi/boot.c
  */
 u32 pmtmr_ioport __read_mostly;
 
diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c
index a905f78..a7b9e9b 100644
--- a/drivers/connector/connector.c
+++ b/drivers/connector/connector.c
@@ -212,7 +212,7 @@
 	skb = skb_get(__skb);
 
 	if (skb->len >= NLMSG_SPACE(0)) {
-		nlh = (struct nlmsghdr *)skb->data;
+		nlh = nlmsg_hdr(skb);
 
 		if (nlh->nlmsg_len < sizeof(struct cn_msg) ||
 		    skb->len < nlh->nlmsg_len ||
@@ -448,7 +448,7 @@
 
 	dev->nls = netlink_kernel_create(NETLINK_CONNECTOR,
 					 CN_NETLINK_USERS + 0xf,
-					 dev->input, THIS_MODULE);
+					 dev->input, NULL, THIS_MODULE);
 	if (!dev->nls)
 		return -EIO;
 
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 850788f..8fbe9fd 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -36,5 +36,7 @@
 
 	If unsure, say N
 
+source "drivers/hid/usbhid/Kconfig"
+
 endmenu
 
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 52e97d8..68d1376 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -6,3 +6,7 @@
 obj-$(CONFIG_HID)		+= hid.o
 hid-$(CONFIG_HID_DEBUG)		+= hid-debug.o
 
+obj-$(CONFIG_USB_HID)		+= usbhid/
+obj-$(CONFIG_USB_MOUSE)		+= usbhid/
+obj-$(CONFIG_USB_KBD)		+= usbhid/
+
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 1cca32f..62e21cc 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -4,7 +4,7 @@
  *  Copyright (c) 1999 Andreas Gal
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
- *  Copyright (c) 2006 Jiri Kosina
+ *  Copyright (c) 2006-2007 Jiri Kosina
  */
 
 /*
@@ -37,7 +37,7 @@
  */
 
 #define DRIVER_VERSION "v2.6"
-#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik"
+#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik, Jiri Kosina"
 #define DRIVER_DESC "HID core driver"
 #define DRIVER_LICENSE "GPL"
 
@@ -872,8 +872,13 @@
 	unsigned count = field->report_count;
 	unsigned offset = field->report_offset;
 	unsigned size = field->report_size;
+	unsigned bitsused = offset + count * size;
 	unsigned n;
 
+	/* make sure the unused bits in the last byte are zeros */
+	if (count > 0 && size > 0 && (bitsused % 8) != 0)
+		data[(bitsused-1)/8] &= (1 << (bitsused % 8)) - 1;
+
 	for (n = 0; n < count; n++) {
 		if (field->logical_minimum < 0)	/* signed values */
 			implement(data, offset + n * size, size, s32ton(field->value[n], size));
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index c843402..a19b65e 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -431,6 +431,15 @@
 				case 0x000: goto ignore;
 				case 0x034: map_key_clear(KEY_SLEEP);		break;
 				case 0x036: map_key_clear(BTN_MISC);		break;
+				/*
+				 * The next three are reported by Belkin wireless
+				 * keyboard (1020:0006). These values are "reserved"
+				 * in HUT 1.12.
+				 */
+				case 0x03a: map_key_clear(KEY_SOUND);           break;
+				case 0x03b: map_key_clear(KEY_CAMERA);          break;
+				case 0x03c: map_key_clear(KEY_DOCUMENTS);       break;
+
 				case 0x040: map_key_clear(KEY_MENU);		break;
 				case 0x045: map_key_clear(KEY_RADIO);		break;
 
@@ -531,10 +540,26 @@
 				case 0x302: map_key_clear(KEY_PROG2);		break;
 				case 0x303: map_key_clear(KEY_PROG3);		break;
 
-				/* Reported on Logitech S510 wireless keyboard */
+				/* Reported on certain Logitech wireless keyboards */
+				case 0x1001: map_key_clear(KEY_MESSENGER);	break;
+				case 0x1003: map_key_clear(KEY_SOUND);		break;
+				case 0x1004: map_key_clear(KEY_VIDEO);		break;
+				case 0x1005: map_key_clear(KEY_AUDIO);		break;
+				case 0x100a: map_key_clear(KEY_DOCUMENTS);	break;
+				case 0x1011: map_key_clear(KEY_PREVIOUSSONG);	break;
+				case 0x1012: map_key_clear(KEY_NEXTSONG);	break;
+				case 0x1013: map_key_clear(KEY_CAMERA);		break;
+				case 0x1014: map_key_clear(KEY_MESSENGER);	break;
+				case 0x1015: map_key_clear(KEY_RECORD);		break;
+				case 0x1016: map_key_clear(KEY_PLAYER);		break;
+				case 0x1017: map_key_clear(KEY_EJECTCD);	break;
+				case 0x1019: map_key_clear(KEY_PROG1);		break;
+				case 0x101a: map_key_clear(KEY_PROG2);		break;
+				case 0x101b: map_key_clear(KEY_PROG3);		break;
 				case 0x101f: map_key_clear(KEY_ZOOMIN);		break;
 				case 0x1020: map_key_clear(KEY_ZOOMOUT);	break;
 				case 0x1021: map_key_clear(KEY_ZOOMRESET);	break;
+				case 0x1023: map_key_clear(KEY_CLOSE);		break;
 				/* this one is marked as 'Rotate' */
 				case 0x1028: map_key_clear(KEY_ANGLE);		break;
 				case 0x1029: map_key_clear(KEY_SHUFFLE);	break;
diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig
new file mode 100644
index 0000000..7c87bdc
--- /dev/null
+++ b/drivers/hid/usbhid/Kconfig
@@ -0,0 +1,149 @@
+comment "USB Input Devices"
+	depends on USB
+
+config USB_HID
+	tristate "USB Human Interface Device (full HID) support"
+	default y
+	depends on USB && INPUT
+	select HID
+	---help---
+	  Say Y here if you want full HID support to connect USB keyboards,
+	  mice, joysticks, graphic tablets, or any other HID based devices
+	  to your computer via USB, as well as Uninterruptible Power Supply
+	  (UPS) and monitor control devices.
+
+	  You can't use this driver and the HIDBP (Boot Protocol) keyboard
+	  and mouse drivers at the same time. More information is available:
+	  <file:Documentation/input/input.txt>.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called usbhid.
+
+comment "Input core support is needed for USB HID input layer or HIDBP support"
+	depends on USB_HID && INPUT=n
+
+config USB_HIDINPUT_POWERBOOK
+	bool "Enable support for iBook/PowerBook special keys"
+	default n
+	depends on USB_HID
+	help
+	  Say Y here if you want support for the special keys (Fn, Numlock) on
+	  Apple iBooks and PowerBooks.
+
+	  If unsure, say N.
+
+config HID_FF
+	bool "Force feedback support (EXPERIMENTAL)"
+	depends on USB_HID && EXPERIMENTAL
+	help
+	  Say Y here is you want force feedback support for a few HID devices.
+	  See below for a list of supported devices.
+
+	  See <file:Documentation/input/ff.txt> for a description of the force
+	  feedback API.
+
+	  If unsure, say N.
+
+config HID_PID
+	bool "PID device support"
+	depends on HID_FF
+	help
+	  Say Y here if you have a PID-compliant device and wish to enable force
+	  feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such
+	  devices.
+
+config LOGITECH_FF
+	bool "Logitech devices support"
+	depends on HID_FF
+	select INPUT_FF_MEMLESS if USB_HID
+	help
+	  Say Y here if you have one of these devices:
+	  - Logitech WingMan Cordless RumblePad
+	  - Logitech WingMan Cordless RumblePad 2
+	  - Logitech WingMan Force 3D
+	  - Logitech Formula Force EX
+	  - Logitech MOMO Force wheel
+
+	  and if you want to enable force feedback for them.
+	  Note: if you say N here, this device will still be supported, but without
+	  force feedback.
+
+config PANTHERLORD_FF
+	bool "PantherLord USB/PS2 2in1 Adapter support"
+	depends on HID_FF
+	select INPUT_FF_MEMLESS if USB_HID
+	help
+	  Say Y here if you have a PantherLord USB/PS2 2in1 Adapter and want
+	  to enable force feedback support for it.
+
+config THRUSTMASTER_FF
+	bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)"
+	depends on HID_FF && EXPERIMENTAL
+	select INPUT_FF_MEMLESS if USB_HID
+	help
+	  Say Y here if you have a THRUSTMASTER FireStore Dual Power 2,
+	  and want to enable force feedback support for it.
+	  Note: if you say N here, this device will still be supported, but without
+	  force feedback.
+
+config ZEROPLUS_FF
+	bool "Zeroplus based game controller support"
+	depends on HID_FF
+	select INPUT_FF_MEMLESS if USB_HID
+	help
+	  Say Y here if you have a Zeroplus based game controller and want to
+	  enable force feedback for it.
+
+config USB_HIDDEV
+	bool "/dev/hiddev raw HID device support"
+	depends on USB_HID
+	help
+	  Say Y here if you want to support HID devices (from the USB
+	  specification standpoint) that aren't strictly user interface
+	  devices, like monitor controls and Uninterruptable Power Supplies.
+
+	  This module supports these devices separately using a separate
+	  event interface on /dev/usb/hiddevX (char 180:96 to 180:111).
+
+	  If unsure, say Y.
+
+menu "USB HID Boot Protocol drivers"
+	depends on USB!=n && USB_HID!=y
+
+config USB_KBD
+	tristate "USB HIDBP Keyboard (simple Boot) support"
+	depends on USB && INPUT
+	---help---
+	  Say Y here only if you are absolutely sure that you don't want
+	  to use the generic HID driver for your USB keyboard and prefer
+	  to use the keyboard in its limited Boot Protocol mode instead.
+
+	  This is almost certainly not what you want.  This is mostly
+	  useful for embedded applications or simple keyboards.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called usbkbd.
+
+	  If even remotely unsure, say N.
+
+config USB_MOUSE
+	tristate "USB HIDBP Mouse (simple Boot) support"
+	depends on USB && INPUT
+	---help---
+	  Say Y here only if you are absolutely sure that you don't want
+	  to use the generic HID driver for your USB mouse and prefer
+	  to use the mouse in its limited Boot Protocol mode instead.
+
+	  This is almost certainly not what you want.  This is mostly
+	  useful for embedded applications or simple mice.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called usbmouse.
+
+	  If even remotely unsure, say N.
+
+endmenu
+
+
diff --git a/drivers/hid/usbhid/Makefile b/drivers/hid/usbhid/Makefile
new file mode 100644
index 0000000..8e6ab5b
--- /dev/null
+++ b/drivers/hid/usbhid/Makefile
@@ -0,0 +1,35 @@
+#
+# Makefile for the USB input drivers
+#
+
+# Multipart objects.
+usbhid-objs	:= hid-core.o hid-quirks.o
+
+# Optional parts of multipart objects.
+
+ifeq ($(CONFIG_USB_HIDDEV),y)
+	usbhid-objs	+= hiddev.o
+endif
+ifeq ($(CONFIG_HID_PID),y)
+	usbhid-objs	+= hid-pidff.o
+endif
+ifeq ($(CONFIG_LOGITECH_FF),y)
+	usbhid-objs	+= hid-lgff.o
+endif
+ifeq ($(CONFIG_PANTHERLORD_FF),y)
+	usbhid-objs	+= hid-plff.o
+endif
+ifeq ($(CONFIG_THRUSTMASTER_FF),y)
+	usbhid-objs	+= hid-tmff.o
+endif
+ifeq ($(CONFIG_ZEROPLUS_FF),y)
+	usbhid-objs	+= hid-zpff.o
+endif
+ifeq ($(CONFIG_HID_FF),y)
+	usbhid-objs	+= hid-ff.o
+endif
+
+obj-$(CONFIG_USB_HID)		+= usbhid.o
+obj-$(CONFIG_USB_KBD)		+= usbkbd.o
+obj-$(CONFIG_USB_MOUSE)		+= usbmouse.o
+
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
new file mode 100644
index 0000000..91d6103
--- /dev/null
+++ b/drivers/hid/usbhid/hid-core.c
@@ -0,0 +1,1114 @@
+/*
+ *  USB HID support for Linux
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ */
+
+/*
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+#include <linux/input.h>
+#include <linux/wait.h>
+
+#include <linux/usb.h>
+
+#include <linux/hid.h>
+#include <linux/hiddev.h>
+#include <linux/hid-debug.h>
+#include "usbhid.h"
+
+/*
+ * Version Information
+ */
+
+#define DRIVER_VERSION "v2.6"
+#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik, Jiri Kosina"
+#define DRIVER_DESC "USB HID core driver"
+#define DRIVER_LICENSE "GPL"
+
+static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick",
+				"Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"};
+/*
+ * Module parameters.
+ */
+
+static unsigned int hid_mousepoll_interval;
+module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644);
+MODULE_PARM_DESC(mousepoll, "Polling interval of mice");
+
+/* Quirks specified at module load time */
+static char *quirks_param[MAX_USBHID_BOOT_QUIRKS] = { [ 0 ... (MAX_USBHID_BOOT_QUIRKS - 1) ] = NULL };
+module_param_array_named(quirks, quirks_param, charp, NULL, 0444);
+MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying "
+		" quirks=vendorID:productID:quirks"
+		" where vendorID, productID, and quirks are all in"
+		" 0x-prefixed hex");
+/*
+ * Input submission and I/O error handler.
+ */
+
+static void hid_io_error(struct hid_device *hid);
+
+/* Start up the input URB */
+static int hid_start_in(struct hid_device *hid)
+{
+	unsigned long flags;
+	int rc = 0;
+	struct usbhid_device *usbhid = hid->driver_data;
+
+	spin_lock_irqsave(&usbhid->inlock, flags);
+	if (hid->open > 0 && !test_bit(HID_SUSPENDED, &usbhid->iofl) &&
+			!test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
+		rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
+		if (rc != 0)
+			clear_bit(HID_IN_RUNNING, &usbhid->iofl);
+	}
+	spin_unlock_irqrestore(&usbhid->inlock, flags);
+	return rc;
+}
+
+/* I/O retry timer routine */
+static void hid_retry_timeout(unsigned long _hid)
+{
+	struct hid_device *hid = (struct hid_device *) _hid;
+	struct usbhid_device *usbhid = hid->driver_data;
+
+	dev_dbg(&usbhid->intf->dev, "retrying intr urb\n");
+	if (hid_start_in(hid))
+		hid_io_error(hid);
+}
+
+/* Workqueue routine to reset the device or clear a halt */
+static void hid_reset(struct work_struct *work)
+{
+	struct usbhid_device *usbhid =
+		container_of(work, struct usbhid_device, reset_work);
+	struct hid_device *hid = usbhid->hid;
+	int rc_lock, rc = 0;
+
+	if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) {
+		dev_dbg(&usbhid->intf->dev, "clear halt\n");
+		rc = usb_clear_halt(hid_to_usb_dev(hid), usbhid->urbin->pipe);
+		clear_bit(HID_CLEAR_HALT, &usbhid->iofl);
+		hid_start_in(hid);
+	}
+
+	else if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) {
+		dev_dbg(&usbhid->intf->dev, "resetting device\n");
+		rc = rc_lock = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf);
+		if (rc_lock >= 0) {
+			rc = usb_reset_composite_device(hid_to_usb_dev(hid), usbhid->intf);
+			if (rc_lock)
+				usb_unlock_device(hid_to_usb_dev(hid));
+		}
+		clear_bit(HID_RESET_PENDING, &usbhid->iofl);
+	}
+
+	switch (rc) {
+	case 0:
+		if (!test_bit(HID_IN_RUNNING, &usbhid->iofl))
+			hid_io_error(hid);
+		break;
+	default:
+		err("can't reset device, %s-%s/input%d, status %d",
+				hid_to_usb_dev(hid)->bus->bus_name,
+				hid_to_usb_dev(hid)->devpath,
+				usbhid->ifnum, rc);
+		/* FALLTHROUGH */
+	case -EHOSTUNREACH:
+	case -ENODEV:
+	case -EINTR:
+		break;
+	}
+}
+
+/* Main I/O error handler */
+static void hid_io_error(struct hid_device *hid)
+{
+	unsigned long flags;
+	struct usbhid_device *usbhid = hid->driver_data;
+
+	spin_lock_irqsave(&usbhid->inlock, flags);
+
+	/* Stop when disconnected */
+	if (usb_get_intfdata(usbhid->intf) == NULL)
+		goto done;
+
+	/* If it has been a while since the last error, we'll assume
+	 * this a brand new error and reset the retry timeout. */
+	if (time_after(jiffies, usbhid->stop_retry + HZ/2))
+		usbhid->retry_delay = 0;
+
+	/* When an error occurs, retry at increasing intervals */
+	if (usbhid->retry_delay == 0) {
+		usbhid->retry_delay = 13;	/* Then 26, 52, 104, 104, ... */
+		usbhid->stop_retry = jiffies + msecs_to_jiffies(1000);
+	} else if (usbhid->retry_delay < 100)
+		usbhid->retry_delay *= 2;
+
+	if (time_after(jiffies, usbhid->stop_retry)) {
+
+		/* Retries failed, so do a port reset */
+		if (!test_and_set_bit(HID_RESET_PENDING, &usbhid->iofl)) {
+			schedule_work(&usbhid->reset_work);
+			goto done;
+		}
+	}
+
+	mod_timer(&usbhid->io_retry,
+			jiffies + msecs_to_jiffies(usbhid->retry_delay));
+done:
+	spin_unlock_irqrestore(&usbhid->inlock, flags);
+}
+
+/*
+ * Input interrupt completion handler.
+ */
+
+static void hid_irq_in(struct urb *urb)
+{
+	struct hid_device	*hid = urb->context;
+	struct usbhid_device 	*usbhid = hid->driver_data;
+	int			status;
+
+	switch (urb->status) {
+		case 0:			/* success */
+			usbhid->retry_delay = 0;
+			hid_input_report(urb->context, HID_INPUT_REPORT,
+					 urb->transfer_buffer,
+					 urb->actual_length, 1);
+			break;
+		case -EPIPE:		/* stall */
+			clear_bit(HID_IN_RUNNING, &usbhid->iofl);
+			set_bit(HID_CLEAR_HALT, &usbhid->iofl);
+			schedule_work(&usbhid->reset_work);
+			return;
+		case -ECONNRESET:	/* unlink */
+		case -ENOENT:
+		case -ESHUTDOWN:	/* unplug */
+			clear_bit(HID_IN_RUNNING, &usbhid->iofl);
+			return;
+		case -EILSEQ:		/* protocol error or unplug */
+		case -EPROTO:		/* protocol error or unplug */
+		case -ETIME:		/* protocol error or unplug */
+		case -ETIMEDOUT:	/* Should never happen, but... */
+			clear_bit(HID_IN_RUNNING, &usbhid->iofl);
+			hid_io_error(hid);
+			return;
+		default:		/* error */
+			warn("input irq status %d received", urb->status);
+	}
+
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status) {
+		clear_bit(HID_IN_RUNNING, &usbhid->iofl);
+		if (status != -EPERM) {
+			err("can't resubmit intr, %s-%s/input%d, status %d",
+					hid_to_usb_dev(hid)->bus->bus_name,
+					hid_to_usb_dev(hid)->devpath,
+					usbhid->ifnum, status);
+			hid_io_error(hid);
+		}
+	}
+}
+
+static int hid_submit_out(struct hid_device *hid)
+{
+	struct hid_report *report;
+	struct usbhid_device *usbhid = hid->driver_data;
+
+	report = usbhid->out[usbhid->outtail];
+
+	hid_output_report(report, usbhid->outbuf);
+	usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0);
+	usbhid->urbout->dev = hid_to_usb_dev(hid);
+
+	dbg("submitting out urb");
+
+	if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) {
+		err("usb_submit_urb(out) failed");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int hid_submit_ctrl(struct hid_device *hid)
+{
+	struct hid_report *report;
+	unsigned char dir;
+	int len;
+	struct usbhid_device *usbhid = hid->driver_data;
+
+	report = usbhid->ctrl[usbhid->ctrltail].report;
+	dir = usbhid->ctrl[usbhid->ctrltail].dir;
+
+	len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
+	if (dir == USB_DIR_OUT) {
+		hid_output_report(report, usbhid->ctrlbuf);
+		usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
+		usbhid->urbctrl->transfer_buffer_length = len;
+	} else {
+		int maxpacket, padlen;
+
+		usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0);
+		maxpacket = usb_maxpacket(hid_to_usb_dev(hid), usbhid->urbctrl->pipe, 0);
+		if (maxpacket > 0) {
+			padlen = (len + maxpacket - 1) / maxpacket;
+			padlen *= maxpacket;
+			if (padlen > usbhid->bufsize)
+				padlen = usbhid->bufsize;
+		} else
+			padlen = 0;
+		usbhid->urbctrl->transfer_buffer_length = padlen;
+	}
+	usbhid->urbctrl->dev = hid_to_usb_dev(hid);
+
+	usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
+	usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT;
+	usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id);
+	usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum);
+	usbhid->cr->wLength = cpu_to_le16(len);
+
+	dbg("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u",
+		usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report",
+		usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength);
+
+	if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) {
+		err("usb_submit_urb(ctrl) failed");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Output interrupt completion handler.
+ */
+
+static void hid_irq_out(struct urb *urb)
+{
+	struct hid_device *hid = urb->context;
+	struct usbhid_device *usbhid = hid->driver_data;
+	unsigned long flags;
+	int unplug = 0;
+
+	switch (urb->status) {
+		case 0:			/* success */
+			break;
+		case -ESHUTDOWN:	/* unplug */
+			unplug = 1;
+		case -EILSEQ:		/* protocol error or unplug */
+		case -EPROTO:		/* protocol error or unplug */
+		case -ECONNRESET:	/* unlink */
+		case -ENOENT:
+			break;
+		default:		/* error */
+			warn("output irq status %d received", urb->status);
+	}
+
+	spin_lock_irqsave(&usbhid->outlock, flags);
+
+	if (unplug)
+		usbhid->outtail = usbhid->outhead;
+	else
+		usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
+
+	if (usbhid->outhead != usbhid->outtail) {
+		if (hid_submit_out(hid)) {
+			clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
+			wake_up(&hid->wait);
+		}
+		spin_unlock_irqrestore(&usbhid->outlock, flags);
+		return;
+	}
+
+	clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
+	spin_unlock_irqrestore(&usbhid->outlock, flags);
+	wake_up(&hid->wait);
+}
+
+/*
+ * Control pipe completion handler.
+ */
+
+static void hid_ctrl(struct urb *urb)
+{
+	struct hid_device *hid = urb->context;
+	struct usbhid_device *usbhid = hid->driver_data;
+	unsigned long flags;
+	int unplug = 0;
+
+	spin_lock_irqsave(&usbhid->ctrllock, flags);
+
+	switch (urb->status) {
+		case 0:			/* success */
+			if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN)
+				hid_input_report(urb->context, usbhid->ctrl[usbhid->ctrltail].report->type,
+						urb->transfer_buffer, urb->actual_length, 0);
+			break;
+		case -ESHUTDOWN:	/* unplug */
+			unplug = 1;
+		case -EILSEQ:		/* protocol error or unplug */
+		case -EPROTO:		/* protocol error or unplug */
+		case -ECONNRESET:	/* unlink */
+		case -ENOENT:
+		case -EPIPE:		/* report not available */
+			break;
+		default:		/* error */
+			warn("ctrl urb status %d received", urb->status);
+	}
+
+	if (unplug)
+		usbhid->ctrltail = usbhid->ctrlhead;
+	else
+		usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
+
+	if (usbhid->ctrlhead != usbhid->ctrltail) {
+		if (hid_submit_ctrl(hid)) {
+			clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
+			wake_up(&hid->wait);
+		}
+		spin_unlock_irqrestore(&usbhid->ctrllock, flags);
+		return;
+	}
+
+	clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
+	spin_unlock_irqrestore(&usbhid->ctrllock, flags);
+	wake_up(&hid->wait);
+}
+
+void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir)
+{
+	int head;
+	unsigned long flags;
+	struct usbhid_device *usbhid = hid->driver_data;
+
+	if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN)
+		return;
+
+	if (usbhid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) {
+
+		spin_lock_irqsave(&usbhid->outlock, flags);
+
+		if ((head = (usbhid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == usbhid->outtail) {
+			spin_unlock_irqrestore(&usbhid->outlock, flags);
+			warn("output queue full");
+			return;
+		}
+
+		usbhid->out[usbhid->outhead] = report;
+		usbhid->outhead = head;
+
+		if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl))
+			if (hid_submit_out(hid))
+				clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
+
+		spin_unlock_irqrestore(&usbhid->outlock, flags);
+		return;
+	}
+
+	spin_lock_irqsave(&usbhid->ctrllock, flags);
+
+	if ((head = (usbhid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == usbhid->ctrltail) {
+		spin_unlock_irqrestore(&usbhid->ctrllock, flags);
+		warn("control queue full");
+		return;
+	}
+
+	usbhid->ctrl[usbhid->ctrlhead].report = report;
+	usbhid->ctrl[usbhid->ctrlhead].dir = dir;
+	usbhid->ctrlhead = head;
+
+	if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl))
+		if (hid_submit_ctrl(hid))
+			clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
+
+	spin_unlock_irqrestore(&usbhid->ctrllock, flags);
+}
+
+static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	struct hid_device *hid = dev->private;
+	struct hid_field *field;
+	int offset;
+
+	if (type == EV_FF)
+		return input_ff_event(dev, type, code, value);
+
+	if (type != EV_LED)
+		return -1;
+
+	if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) {
+		warn("event field not found");
+		return -1;
+	}
+
+	hid_set_field(field, offset, value);
+	usbhid_submit_report(hid, field->report, USB_DIR_OUT);
+
+	return 0;
+}
+
+int usbhid_wait_io(struct hid_device *hid)
+{
+	struct usbhid_device *usbhid = hid->driver_data;
+
+	if (!wait_event_timeout(hid->wait, (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl) &&
+					!test_bit(HID_OUT_RUNNING, &usbhid->iofl)),
+					10*HZ)) {
+		dbg("timeout waiting for ctrl or out queue to clear");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle)
+{
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, (idle << 8) | report,
+		ifnum, NULL, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+static int hid_get_class_descriptor(struct usb_device *dev, int ifnum,
+		unsigned char type, void *buf, int size)
+{
+	int result, retries = 4;
+
+	memset(buf, 0, size);
+
+	do {
+		result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+				USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN,
+				(type << 8), ifnum, buf, size, USB_CTRL_GET_TIMEOUT);
+		retries--;
+	} while (result < size && retries);
+	return result;
+}
+
+int usbhid_open(struct hid_device *hid)
+{
+	++hid->open;
+	if (hid_start_in(hid))
+		hid_io_error(hid);
+	return 0;
+}
+
+void usbhid_close(struct hid_device *hid)
+{
+	struct usbhid_device *usbhid = hid->driver_data;
+
+	if (!--hid->open)
+		usb_kill_urb(usbhid->urbin);
+}
+
+/*
+ * Initialize all reports
+ */
+
+void usbhid_init_reports(struct hid_device *hid)
+{
+	struct hid_report *report;
+	struct usbhid_device *usbhid = hid->driver_data;
+	int err, ret;
+
+	list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list)
+		usbhid_submit_report(hid, report, USB_DIR_IN);
+
+	list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
+		usbhid_submit_report(hid, report, USB_DIR_IN);
+
+	err = 0;
+	ret = usbhid_wait_io(hid);
+	while (ret) {
+		err |= ret;
+		if (test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
+			usb_kill_urb(usbhid->urbctrl);
+		if (test_bit(HID_OUT_RUNNING, &usbhid->iofl))
+			usb_kill_urb(usbhid->urbout);
+		ret = usbhid_wait_io(hid);
+	}
+
+	if (err)
+		warn("timeout initializing reports");
+}
+
+/*
+ * Reset LEDs which BIOS might have left on. For now, just NumLock (0x01).
+ */
+static int hid_find_field_early(struct hid_device *hid, unsigned int page,
+    unsigned int hid_code, struct hid_field **pfield)
+{
+	struct hid_report *report;
+	struct hid_field *field;
+	struct hid_usage *usage;
+	int i, j;
+
+	list_for_each_entry(report, &hid->report_enum[HID_OUTPUT_REPORT].report_list, list) {
+		for (i = 0; i < report->maxfield; i++) {
+			field = report->field[i];
+			for (j = 0; j < field->maxusage; j++) {
+				usage = &field->usage[j];
+				if ((usage->hid & HID_USAGE_PAGE) == page &&
+				    (usage->hid & 0xFFFF) == hid_code) {
+					*pfield = field;
+					return j;
+				}
+			}
+		}
+	}
+	return -1;
+}
+
+static void usbhid_set_leds(struct hid_device *hid)
+{
+	struct hid_field *field;
+	int offset;
+
+	if ((offset = hid_find_field_early(hid, HID_UP_LED, 0x01, &field)) != -1) {
+		hid_set_field(field, offset, 0);
+		usbhid_submit_report(hid, field->report, USB_DIR_OUT);
+	}
+}
+
+/*
+ * Traverse the supplied list of reports and find the longest
+ */
+static void hid_find_max_report(struct hid_device *hid, unsigned int type, int *max)
+{
+	struct hid_report *report;
+	int size;
+
+	list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
+		size = ((report->size - 1) >> 3) + 1;
+		if (type == HID_INPUT_REPORT && hid->report_enum[type].numbered)
+			size++;
+		if (*max < size)
+			*max = size;
+	}
+}
+
+static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid)
+{
+	struct usbhid_device *usbhid = hid->driver_data;
+
+	if (!(usbhid->inbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->inbuf_dma)))
+		return -1;
+	if (!(usbhid->outbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->outbuf_dma)))
+		return -1;
+	if (!(usbhid->cr = usb_buffer_alloc(dev, sizeof(*(usbhid->cr)), GFP_ATOMIC, &usbhid->cr_dma)))
+		return -1;
+	if (!(usbhid->ctrlbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->ctrlbuf_dma)))
+		return -1;
+
+	return 0;
+}
+
+static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
+{
+	struct usbhid_device *usbhid = hid->driver_data;
+
+	if (usbhid->inbuf)
+		usb_buffer_free(dev, usbhid->bufsize, usbhid->inbuf, usbhid->inbuf_dma);
+	if (usbhid->outbuf)
+		usb_buffer_free(dev, usbhid->bufsize, usbhid->outbuf, usbhid->outbuf_dma);
+	if (usbhid->cr)
+		usb_buffer_free(dev, sizeof(*(usbhid->cr)), usbhid->cr, usbhid->cr_dma);
+	if (usbhid->ctrlbuf)
+		usb_buffer_free(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma);
+}
+
+/*
+ * Cherry Cymotion keyboard have an invalid HID report descriptor,
+ * that needs fixing before we can parse it.
+ */
+
+static void hid_fixup_cymotion_descriptor(char *rdesc, int rsize)
+{
+	if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) {
+		info("Fixing up Cherry Cymotion report descriptor");
+		rdesc[11] = rdesc[16] = 0xff;
+		rdesc[12] = rdesc[17] = 0x03;
+	}
+}
+
+/*
+ * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
+ * to "operational".  Without this, the ps3 controller will not report any
+ * events.
+ */
+static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum)
+{
+	int result;
+	char *buf = kmalloc(18, GFP_KERNEL);
+
+	if (!buf)
+		return;
+
+	result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+				 HID_REQ_GET_REPORT,
+				 USB_DIR_IN | USB_TYPE_CLASS |
+				 USB_RECIP_INTERFACE,
+				 (3 << 8) | 0xf2, ifnum, buf, 17,
+				 USB_CTRL_GET_TIMEOUT);
+
+	if (result < 0)
+		err("%s failed: %d\n", __func__, result);
+
+	kfree(buf);
+}
+
+/*
+ * Certain Logitech keyboards send in report #3 keys which are far
+ * above the logical maximum described in descriptor. This extends
+ * the original value of 0x28c of logical maximum to 0x104d
+ */
+static void hid_fixup_logitech_descriptor(unsigned char *rdesc, int rsize)
+{
+	if (rsize >= 90 && rdesc[83] == 0x26
+			&& rdesc[84] == 0x8c
+			&& rdesc[85] == 0x02) {
+		info("Fixing up Logitech keyboard report descriptor");
+		rdesc[84] = rdesc[89] = 0x4d;
+		rdesc[85] = rdesc[90] = 0x10;
+	}
+}
+
+static struct hid_device *usb_hid_configure(struct usb_interface *intf)
+{
+	struct usb_host_interface *interface = intf->cur_altsetting;
+	struct usb_device *dev = interface_to_usbdev (intf);
+	struct hid_descriptor *hdesc;
+	struct hid_device *hid;
+	u32 quirks = 0;
+	unsigned rsize = 0;
+	char *rdesc;
+	int n, len, insize = 0;
+	struct usbhid_device *usbhid;
+
+	quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor),
+			le16_to_cpu(dev->descriptor.idProduct));
+
+	/* Many keyboards and mice don't like to be polled for reports,
+	 * so we will always set the HID_QUIRK_NOGET flag for them. */
+	if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) {
+		if (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD ||
+			interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)
+				quirks |= HID_QUIRK_NOGET;
+	}
+
+	if (quirks & HID_QUIRK_IGNORE)
+		return NULL;
+
+	if ((quirks & HID_QUIRK_IGNORE_MOUSE) &&
+		(interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE))
+			return NULL;
+
+
+	if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) &&
+	    (!interface->desc.bNumEndpoints ||
+	     usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {
+		dbg("class descriptor not present\n");
+		return NULL;
+	}
+
+	for (n = 0; n < hdesc->bNumDescriptors; n++)
+		if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT)
+			rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);
+
+	if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
+		dbg("weird size of report descriptor (%u)", rsize);
+		return NULL;
+	}
+
+	if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
+		dbg("couldn't allocate rdesc memory");
+		return NULL;
+	}
+
+	hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0);
+
+	if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) {
+		dbg("reading report descriptor failed");
+		kfree(rdesc);
+		return NULL;
+	}
+
+	if ((quirks & HID_QUIRK_CYMOTION))
+		hid_fixup_cymotion_descriptor(rdesc, rsize);
+
+	if (quirks & HID_QUIRK_LOGITECH_DESCRIPTOR)
+		hid_fixup_logitech_descriptor(rdesc, rsize);
+
+#ifdef CONFIG_HID_DEBUG
+	printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n);
+	for (n = 0; n < rsize; n++)
+		printk(" %02x", (unsigned char) rdesc[n]);
+	printk("\n");
+#endif
+
+	if (!(hid = hid_parse_report(rdesc, n))) {
+		dbg("parsing report descriptor failed");
+		kfree(rdesc);
+		return NULL;
+	}
+
+	kfree(rdesc);
+	hid->quirks = quirks;
+
+	if (!(usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL)))
+		goto fail;
+
+	hid->driver_data = usbhid;
+	usbhid->hid = hid;
+
+	usbhid->bufsize = HID_MIN_BUFFER_SIZE;
+	hid_find_max_report(hid, HID_INPUT_REPORT, &usbhid->bufsize);
+	hid_find_max_report(hid, HID_OUTPUT_REPORT, &usbhid->bufsize);
+	hid_find_max_report(hid, HID_FEATURE_REPORT, &usbhid->bufsize);
+
+	if (usbhid->bufsize > HID_MAX_BUFFER_SIZE)
+		usbhid->bufsize = HID_MAX_BUFFER_SIZE;
+
+	hid_find_max_report(hid, HID_INPUT_REPORT, &insize);
+
+	if (insize > HID_MAX_BUFFER_SIZE)
+		insize = HID_MAX_BUFFER_SIZE;
+
+	if (hid_alloc_buffers(dev, hid)) {
+		hid_free_buffers(dev, hid);
+		goto fail;
+	}
+
+	for (n = 0; n < interface->desc.bNumEndpoints; n++) {
+
+		struct usb_endpoint_descriptor *endpoint;
+		int pipe;
+		int interval;
+
+		endpoint = &interface->endpoint[n].desc;
+		if ((endpoint->bmAttributes & 3) != 3)		/* Not an interrupt endpoint */
+			continue;
+
+		interval = endpoint->bInterval;
+
+		/* Change the polling interval of mice. */
+		if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0)
+			interval = hid_mousepoll_interval;
+
+		if (usb_endpoint_dir_in(endpoint)) {
+			if (usbhid->urbin)
+				continue;
+			if (!(usbhid->urbin = usb_alloc_urb(0, GFP_KERNEL)))
+				goto fail;
+			pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+			usb_fill_int_urb(usbhid->urbin, dev, pipe, usbhid->inbuf, insize,
+					 hid_irq_in, hid, interval);
+			usbhid->urbin->transfer_dma = usbhid->inbuf_dma;
+			usbhid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+		} else {
+			if (usbhid->urbout)
+				continue;
+			if (!(usbhid->urbout = usb_alloc_urb(0, GFP_KERNEL)))
+				goto fail;
+			pipe = usb_sndintpipe(dev, endpoint->bEndpointAddress);
+			usb_fill_int_urb(usbhid->urbout, dev, pipe, usbhid->outbuf, 0,
+					 hid_irq_out, hid, interval);
+			usbhid->urbout->transfer_dma = usbhid->outbuf_dma;
+			usbhid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+		}
+	}
+
+	if (!usbhid->urbin) {
+		err("couldn't find an input interrupt endpoint");
+		goto fail;
+	}
+
+	init_waitqueue_head(&hid->wait);
+
+	INIT_WORK(&usbhid->reset_work, hid_reset);
+	setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
+
+	spin_lock_init(&usbhid->inlock);
+	spin_lock_init(&usbhid->outlock);
+	spin_lock_init(&usbhid->ctrllock);
+
+	hid->version = le16_to_cpu(hdesc->bcdHID);
+	hid->country = hdesc->bCountryCode;
+	hid->dev = &intf->dev;
+	usbhid->intf = intf;
+	usbhid->ifnum = interface->desc.bInterfaceNumber;
+
+	hid->name[0] = 0;
+
+	if (dev->manufacturer)
+		strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));
+
+	if (dev->product) {
+		if (dev->manufacturer)
+			strlcat(hid->name, " ", sizeof(hid->name));
+		strlcat(hid->name, dev->product, sizeof(hid->name));
+	}
+
+	if (!strlen(hid->name))
+		snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",
+			 le16_to_cpu(dev->descriptor.idVendor),
+			 le16_to_cpu(dev->descriptor.idProduct));
+
+	hid->bus = BUS_USB;
+	hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
+	hid->product = le16_to_cpu(dev->descriptor.idProduct);
+
+	usb_make_path(dev, hid->phys, sizeof(hid->phys));
+	strlcat(hid->phys, "/input", sizeof(hid->phys));
+	len = strlen(hid->phys);
+	if (len < sizeof(hid->phys) - 1)
+		snprintf(hid->phys + len, sizeof(hid->phys) - len,
+			 "%d", intf->altsetting[0].desc.bInterfaceNumber);
+
+	if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
+		hid->uniq[0] = 0;
+
+	usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);
+	if (!usbhid->urbctrl)
+		goto fail;
+
+	usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr,
+			     usbhid->ctrlbuf, 1, hid_ctrl, hid);
+	usbhid->urbctrl->setup_dma = usbhid->cr_dma;
+	usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
+	usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
+	hid->hidinput_input_event = usb_hidinput_input_event;
+	hid->hid_open = usbhid_open;
+	hid->hid_close = usbhid_close;
+#ifdef CONFIG_USB_HIDDEV
+	hid->hiddev_hid_event = hiddev_hid_event;
+	hid->hiddev_report_event = hiddev_report_event;
+#endif
+	return hid;
+
+fail:
+	usb_free_urb(usbhid->urbin);
+	usb_free_urb(usbhid->urbout);
+	usb_free_urb(usbhid->urbctrl);
+	hid_free_buffers(dev, hid);
+	hid_free_device(hid);
+
+	return NULL;
+}
+
+static void hid_disconnect(struct usb_interface *intf)
+{
+	struct hid_device *hid = usb_get_intfdata (intf);
+	struct usbhid_device *usbhid;
+
+	if (!hid)
+		return;
+
+	usbhid = hid->driver_data;
+
+	spin_lock_irq(&usbhid->inlock);	/* Sync with error handler */
+	usb_set_intfdata(intf, NULL);
+	spin_unlock_irq(&usbhid->inlock);
+	usb_kill_urb(usbhid->urbin);
+	usb_kill_urb(usbhid->urbout);
+	usb_kill_urb(usbhid->urbctrl);
+
+	del_timer_sync(&usbhid->io_retry);
+	flush_scheduled_work();
+
+	if (hid->claimed & HID_CLAIMED_INPUT)
+		hidinput_disconnect(hid);
+	if (hid->claimed & HID_CLAIMED_HIDDEV)
+		hiddev_disconnect(hid);
+
+	usb_free_urb(usbhid->urbin);
+	usb_free_urb(usbhid->urbctrl);
+	usb_free_urb(usbhid->urbout);
+
+	hid_free_buffers(hid_to_usb_dev(hid), hid);
+	hid_free_device(hid);
+}
+
+static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct hid_device *hid;
+	char path[64];
+	int i;
+	char *c;
+
+	dbg("HID probe called for ifnum %d",
+			intf->altsetting->desc.bInterfaceNumber);
+
+	if (!(hid = usb_hid_configure(intf)))
+		return -ENODEV;
+
+	usbhid_init_reports(hid);
+	hid_dump_device(hid);
+	if (hid->quirks & HID_QUIRK_RESET_LEDS)
+		usbhid_set_leds(hid);
+
+	if (!hidinput_connect(hid))
+		hid->claimed |= HID_CLAIMED_INPUT;
+	if (!hiddev_connect(hid))
+		hid->claimed |= HID_CLAIMED_HIDDEV;
+
+	usb_set_intfdata(intf, hid);
+
+	if (!hid->claimed) {
+		printk ("HID device not claimed by input or hiddev\n");
+		hid_disconnect(intf);
+		return -ENODEV;
+	}
+
+	if ((hid->claimed & HID_CLAIMED_INPUT))
+		hid_ff_init(hid);
+
+	if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER)
+		hid_fixup_sony_ps3_controller(interface_to_usbdev(intf),
+			intf->cur_altsetting->desc.bInterfaceNumber);
+
+	printk(KERN_INFO);
+
+	if (hid->claimed & HID_CLAIMED_INPUT)
+		printk("input");
+	if (hid->claimed == (HID_CLAIMED_INPUT | HID_CLAIMED_HIDDEV))
+		printk(",");
+	if (hid->claimed & HID_CLAIMED_HIDDEV)
+		printk("hiddev%d", hid->minor);
+
+	c = "Device";
+	for (i = 0; i < hid->maxcollection; i++) {
+		if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
+		    (hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK &&
+		    (hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) {
+			c = hid_types[hid->collection[i].usage & 0xffff];
+			break;
+		}
+	}
+
+	usb_make_path(interface_to_usbdev(intf), path, 63);
+
+	printk(": USB HID v%x.%02x %s [%s] on %s\n",
+		hid->version >> 8, hid->version & 0xff, c, hid->name, path);
+
+	return 0;
+}
+
+static int hid_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct hid_device *hid = usb_get_intfdata (intf);
+	struct usbhid_device *usbhid = hid->driver_data;
+
+	spin_lock_irq(&usbhid->inlock);	/* Sync with error handler */
+	set_bit(HID_SUSPENDED, &usbhid->iofl);
+	spin_unlock_irq(&usbhid->inlock);
+	del_timer(&usbhid->io_retry);
+	usb_kill_urb(usbhid->urbin);
+	dev_dbg(&intf->dev, "suspend\n");
+	return 0;
+}
+
+static int hid_resume(struct usb_interface *intf)
+{
+	struct hid_device *hid = usb_get_intfdata (intf);
+	struct usbhid_device *usbhid = hid->driver_data;
+	int status;
+
+	clear_bit(HID_SUSPENDED, &usbhid->iofl);
+	usbhid->retry_delay = 0;
+	status = hid_start_in(hid);
+	dev_dbg(&intf->dev, "resume status %d\n", status);
+	return status;
+}
+
+/* Treat USB reset pretty much the same as suspend/resume */
+static void hid_pre_reset(struct usb_interface *intf)
+{
+	/* FIXME: What if the interface is already suspended? */
+	hid_suspend(intf, PMSG_ON);
+}
+
+static void hid_post_reset(struct usb_interface *intf)
+{
+	struct usb_device *dev = interface_to_usbdev (intf);
+
+	hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0);
+	/* FIXME: Any more reinitialization needed? */
+
+	hid_resume(intf);
+}
+
+static struct usb_device_id hid_usb_ids [] = {
+	{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
+		.bInterfaceClass = USB_INTERFACE_CLASS_HID },
+	{ }						/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, hid_usb_ids);
+
+static struct usb_driver hid_driver = {
+	.name =		"usbhid",
+	.probe =	hid_probe,
+	.disconnect =	hid_disconnect,
+	.suspend =	hid_suspend,
+	.resume =	hid_resume,
+	.pre_reset =	hid_pre_reset,
+	.post_reset =	hid_post_reset,
+	.id_table =	hid_usb_ids,
+};
+
+static int __init hid_init(void)
+{
+	int retval;
+	retval = usbhid_quirks_init(quirks_param);
+	if (retval)
+		goto usbhid_quirks_init_fail;
+	retval = hiddev_init();
+	if (retval)
+		goto hiddev_init_fail;
+	retval = usb_register(&hid_driver);
+	if (retval)
+		goto usb_register_fail;
+	info(DRIVER_VERSION ":" DRIVER_DESC);
+
+	return 0;
+usb_register_fail:
+	hiddev_exit();
+hiddev_init_fail:
+	usbhid_quirks_exit();
+usbhid_quirks_init_fail:
+	return retval;
+}
+
+static void __exit hid_exit(void)
+{
+	usb_deregister(&hid_driver);
+	hiddev_exit();
+	usbhid_quirks_exit();
+}
+
+module_init(hid_init);
+module_exit(hid_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
diff --git a/drivers/usb/input/hid-ff.c b/drivers/hid/usbhid/hid-ff.c
similarity index 96%
rename from drivers/usb/input/hid-ff.c
rename to drivers/hid/usbhid/hid-ff.c
index e431faa..23431fb 100644
--- a/drivers/usb/input/hid-ff.c
+++ b/drivers/hid/usbhid/hid-ff.c
@@ -56,6 +56,7 @@
 	{ 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */
 	{ 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */
 	{ 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */
+	{ 0x46d, 0xc286, hid_lgff_init }, /* Logitech Force 3D Pro Joystick */
 	{ 0x46d, 0xc294, hid_lgff_init }, /* Logitech Formula Force EX */
 	{ 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */
 	{ 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */
@@ -64,6 +65,7 @@
 	{ 0x810, 0x0001, hid_plff_init },
 #endif
 #ifdef CONFIG_THRUSTMASTER_FF
+	{ 0x44f, 0xb300, hid_tmff_init },
 	{ 0x44f, 0xb304, hid_tmff_init },
 #endif
 #ifdef CONFIG_ZEROPLUS_FF
diff --git a/drivers/usb/input/hid-lgff.c b/drivers/hid/usbhid/hid-lgff.c
similarity index 98%
rename from drivers/usb/input/hid-lgff.c
rename to drivers/hid/usbhid/hid-lgff.c
index e6f3af3..92d2553 100644
--- a/drivers/usb/input/hid-lgff.c
+++ b/drivers/hid/usbhid/hid-lgff.c
@@ -52,6 +52,7 @@
 	{ 0x046d, 0xc211, ff_rumble },
 	{ 0x046d, 0xc219, ff_rumble },
 	{ 0x046d, 0xc283, ff_joystick },
+	{ 0x046d, 0xc286, ff_joystick },
 	{ 0x046d, 0xc294, ff_joystick },
 	{ 0x046d, 0xc295, ff_joystick },
 	{ 0x046d, 0xca03, ff_joystick },
diff --git a/drivers/usb/input/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
similarity index 100%
rename from drivers/usb/input/hid-pidff.c
rename to drivers/hid/usbhid/hid-pidff.c
diff --git a/drivers/usb/input/hid-plff.c b/drivers/hid/usbhid/hid-plff.c
similarity index 100%
rename from drivers/usb/input/hid-plff.c
rename to drivers/hid/usbhid/hid-plff.c
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
new file mode 100644
index 0000000..17a8755
--- /dev/null
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -0,0 +1,681 @@
+/*
+ *  USB HID quirks support for Linux
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ */
+
+/*
+ * 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.
+ */
+
+#include <linux/hid.h>
+
+#define USB_VENDOR_ID_A4TECH		0x09da
+#define USB_DEVICE_ID_A4TECH_WCP32PU	0x0006
+
+#define USB_VENDOR_ID_AASHIMA		0x06d6
+#define USB_DEVICE_ID_AASHIMA_GAMEPAD	0x0025
+#define USB_DEVICE_ID_AASHIMA_PREDATOR	0x0026
+
+#define USB_VENDOR_ID_ACECAD		0x0460
+#define USB_DEVICE_ID_ACECAD_FLAIR	0x0004
+#define USB_DEVICE_ID_ACECAD_302	0x0008
+
+#define USB_VENDOR_ID_AIPTEK		0x08ca
+#define USB_DEVICE_ID_AIPTEK_01		0x0001
+#define USB_DEVICE_ID_AIPTEK_10		0x0010
+#define USB_DEVICE_ID_AIPTEK_20		0x0020
+#define USB_DEVICE_ID_AIPTEK_21		0x0021
+#define USB_DEVICE_ID_AIPTEK_22		0x0022
+#define USB_DEVICE_ID_AIPTEK_23		0x0023
+#define USB_DEVICE_ID_AIPTEK_24		0x0024
+
+#define USB_VENDOR_ID_AIRCABLE		0x16CA
+#define USB_DEVICE_ID_AIRCABLE1		0x1502
+
+#define USB_VENDOR_ID_ALCOR		0x058f
+#define USB_DEVICE_ID_ALCOR_USBRS232	0x9720
+
+#define USB_VENDOR_ID_ALPS		0x0433
+#define USB_DEVICE_ID_IBM_GAMEPAD	0x1101
+
+#define USB_VENDOR_ID_APPLE		0x05ac
+#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE	0x0304
+#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI	0x020e
+#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO	0x020f
+#define USB_DEVICE_ID_APPLE_GEYSER_ANSI	0x0214
+#define USB_DEVICE_ID_APPLE_GEYSER_ISO	0x0215
+#define USB_DEVICE_ID_APPLE_GEYSER_JIS	0x0216
+#define USB_DEVICE_ID_APPLE_GEYSER3_ANSI	0x0217
+#define USB_DEVICE_ID_APPLE_GEYSER3_ISO	0x0218
+#define USB_DEVICE_ID_APPLE_GEYSER3_JIS	0x0219
+#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI	0x021a
+#define USB_DEVICE_ID_APPLE_GEYSER4_ISO	0x021b
+#define USB_DEVICE_ID_APPLE_GEYSER4_JIS	0x021c
+#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY	0x030a
+#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY	0x030b
+#define USB_DEVICE_ID_APPLE_IR		0x8240
+
+#define USB_VENDOR_ID_ATEN		0x0557
+#define USB_DEVICE_ID_ATEN_UC100KM	0x2004
+#define USB_DEVICE_ID_ATEN_CS124U	0x2202
+#define USB_DEVICE_ID_ATEN_2PORTKVM	0x2204
+#define USB_DEVICE_ID_ATEN_4PORTKVM	0x2205
+#define USB_DEVICE_ID_ATEN_4PORTKVMC	0x2208
+
+#define USB_VENDOR_ID_BELKIN           0x050d
+#define USB_DEVICE_ID_FLIP_KVM         0x3201
+
+#define USB_VENDOR_ID_BERKSHIRE		0x0c98
+#define USB_DEVICE_ID_BERKSHIRE_PCWD	0x1140
+
+#define USB_VENDOR_ID_CHERRY		0x046a
+#define USB_DEVICE_ID_CHERRY_CYMOTION	0x0023
+
+#define USB_VENDOR_ID_CHIC		0x05fe
+#define USB_DEVICE_ID_CHIC_GAMEPAD	0x0014
+
+#define USB_VENDOR_ID_CIDC		0x1677
+
+#define USB_VENDOR_ID_CODEMERCS		0x07c0
+#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST	0x1500
+#define USB_DEVICE_ID_CODEMERCS_IOW_LAST	0x15ff
+
+#define USB_VENDOR_ID_CYPRESS		0x04b4
+#define USB_DEVICE_ID_CYPRESS_MOUSE	0x0001
+#define USB_DEVICE_ID_CYPRESS_HIDCOM	0x5500
+#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE	0x7417
+
+#define USB_VENDOR_ID_DELL		0x413c
+#define USB_DEVICE_ID_DELL_W7658	0x2005
+
+#define USB_VENDOR_ID_DELORME		0x1163
+#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100
+#define USB_DEVICE_ID_DELORME_EM_LT20	0x0200
+
+#define USB_VENDOR_ID_ESSENTIAL_REALITY	0x0d7f
+#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
+
+#define USB_VENDOR_ID_GLAB		0x06c2
+#define USB_DEVICE_ID_4_PHIDGETSERVO_30	0x0038
+#define USB_DEVICE_ID_1_PHIDGETSERVO_30	0x0039
+#define USB_DEVICE_ID_0_0_4_IF_KIT	0x0040
+#define USB_DEVICE_ID_0_16_16_IF_KIT	0x0044
+#define USB_DEVICE_ID_8_8_8_IF_KIT	0x0045
+#define USB_DEVICE_ID_0_8_7_IF_KIT	0x0051
+#define USB_DEVICE_ID_0_8_8_IF_KIT	0x0053
+#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL	0x0058
+
+#define USB_VENDOR_ID_GRIFFIN		0x077d
+#define USB_DEVICE_ID_POWERMATE		0x0410
+#define USB_DEVICE_ID_SOUNDKNOB		0x04AA
+
+#define USB_VENDOR_ID_GTCO		0x078c
+#define USB_DEVICE_ID_GTCO_90		0x0090
+#define USB_DEVICE_ID_GTCO_100		0x0100
+#define USB_DEVICE_ID_GTCO_101		0x0101
+#define USB_DEVICE_ID_GTCO_103		0x0103
+#define USB_DEVICE_ID_GTCO_104		0x0104
+#define USB_DEVICE_ID_GTCO_105		0x0105
+#define USB_DEVICE_ID_GTCO_106		0x0106
+#define USB_DEVICE_ID_GTCO_107		0x0107
+#define USB_DEVICE_ID_GTCO_108		0x0108
+#define USB_DEVICE_ID_GTCO_200		0x0200
+#define USB_DEVICE_ID_GTCO_201		0x0201
+#define USB_DEVICE_ID_GTCO_202		0x0202
+#define USB_DEVICE_ID_GTCO_203		0x0203
+#define USB_DEVICE_ID_GTCO_204		0x0204
+#define USB_DEVICE_ID_GTCO_205		0x0205
+#define USB_DEVICE_ID_GTCO_206		0x0206
+#define USB_DEVICE_ID_GTCO_207		0x0207
+#define USB_DEVICE_ID_GTCO_300		0x0300
+#define USB_DEVICE_ID_GTCO_301		0x0301
+#define USB_DEVICE_ID_GTCO_302		0x0302
+#define USB_DEVICE_ID_GTCO_303		0x0303
+#define USB_DEVICE_ID_GTCO_304		0x0304
+#define USB_DEVICE_ID_GTCO_305		0x0305
+#define USB_DEVICE_ID_GTCO_306		0x0306
+#define USB_DEVICE_ID_GTCO_307		0x0307
+#define USB_DEVICE_ID_GTCO_308		0x0308
+#define USB_DEVICE_ID_GTCO_309		0x0309
+#define USB_DEVICE_ID_GTCO_400		0x0400
+#define USB_DEVICE_ID_GTCO_401		0x0401
+#define USB_DEVICE_ID_GTCO_402		0x0402
+#define USB_DEVICE_ID_GTCO_403		0x0403
+#define USB_DEVICE_ID_GTCO_404		0x0404
+#define USB_DEVICE_ID_GTCO_405		0x0405
+#define USB_DEVICE_ID_GTCO_500		0x0500
+#define USB_DEVICE_ID_GTCO_501		0x0501
+#define USB_DEVICE_ID_GTCO_502		0x0502
+#define USB_DEVICE_ID_GTCO_503		0x0503
+#define USB_DEVICE_ID_GTCO_504		0x0504
+#define USB_DEVICE_ID_GTCO_1000		0x1000
+#define USB_DEVICE_ID_GTCO_1001		0x1001
+#define USB_DEVICE_ID_GTCO_1002		0x1002
+#define USB_DEVICE_ID_GTCO_1003		0x1003
+#define USB_DEVICE_ID_GTCO_1004		0x1004
+#define USB_DEVICE_ID_GTCO_1005		0x1005
+#define USB_DEVICE_ID_GTCO_1006		0x1006
+
+#define USB_VENDOR_ID_HAPP		0x078b
+#define USB_DEVICE_ID_UGCI_DRIVING	0x0010
+#define USB_DEVICE_ID_UGCI_FLYING	0x0020
+#define USB_DEVICE_ID_UGCI_FIGHTING	0x0030
+
+#define USB_VENDOR_ID_IMATION		0x0718
+#define USB_DEVICE_ID_DISC_STAKKA	0xd000
+
+#define USB_VENDOR_ID_KBGEAR		0x084e
+#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO	0x1001
+
+#define USB_VENDOR_ID_LD		0x0f11
+#define USB_DEVICE_ID_LD_CASSY		0x1000
+#define USB_DEVICE_ID_LD_POCKETCASSY	0x1010
+#define USB_DEVICE_ID_LD_MOBILECASSY	0x1020
+#define USB_DEVICE_ID_LD_JWM		0x1080
+#define USB_DEVICE_ID_LD_DMMP		0x1081
+#define USB_DEVICE_ID_LD_UMIP		0x1090
+#define USB_DEVICE_ID_LD_XRAY1		0x1100
+#define USB_DEVICE_ID_LD_XRAY2		0x1101
+#define USB_DEVICE_ID_LD_VIDEOCOM	0x1200
+#define USB_DEVICE_ID_LD_COM3LAB	0x2000
+#define USB_DEVICE_ID_LD_TELEPORT	0x2010
+#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020
+#define USB_DEVICE_ID_LD_POWERCONTROL	0x2030
+#define USB_DEVICE_ID_LD_MACHINETEST	0x2040
+
+#define USB_VENDOR_ID_LOGITECH		0x046d
+#define USB_DEVICE_ID_LOGITECH_RECEIVER	0xc101
+#define USB_DEVICE_ID_S510_RECEIVER	0xc50c
+#define USB_DEVICE_ID_S510_RECEIVER_2	0xc517
+#define USB_DEVICE_ID_MX3000_RECEIVER	0xc513
+#define USB_DEVICE_ID_DINOVO_EDGE	0xc714
+
+#define USB_VENDOR_ID_MCC		0x09db
+#define USB_DEVICE_ID_MCC_PMD1024LS	0x0076
+#define USB_DEVICE_ID_MCC_PMD1208LS	0x007a
+
+#define USB_VENDOR_ID_MGE		0x0463
+#define USB_DEVICE_ID_MGE_UPS		0xffff
+#define USB_DEVICE_ID_MGE_UPS1		0x0001
+
+#define USB_VENDOR_ID_NEC		0x073e
+#define USB_DEVICE_ID_NEC_USB_GAME_PAD	0x0301
+
+#define USB_VENDOR_ID_ONTRAK		0x0a07
+#define USB_DEVICE_ID_ONTRAK_ADU100	0x0064
+
+#define USB_VENDOR_ID_PANJIT		0x134c
+
+#define USB_VENDOR_ID_PANTHERLORD	0x0810
+#define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK	0x0001
+
+#define USB_VENDOR_ID_PLAYDOTCOM	0x0b43
+#define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII	0x0003
+
+#define USB_VENDOR_ID_SAITEK		0x06a3
+#define USB_DEVICE_ID_SAITEK_RUMBLEPAD	0xff17
+
+#define USB_VENDOR_ID_SONY			0x054c
+#define USB_DEVICE_ID_SONY_PS3_CONTROLLER	0x0268
+
+#define USB_VENDOR_ID_SUN		0x0430
+#define USB_DEVICE_ID_RARITAN_KVM_DONGLE	0xcdab
+
+#define USB_VENDOR_ID_TOPMAX		0x0663
+#define USB_DEVICE_ID_TOPMAX_COBRAPAD	0x0103
+
+#define USB_VENDOR_ID_TURBOX		0x062a
+#define USB_DEVICE_ID_TURBOX_KEYBOARD	0x0201
+
+#define USB_VENDOR_ID_VERNIER		0x08f7
+#define USB_DEVICE_ID_VERNIER_LABPRO	0x0001
+#define USB_DEVICE_ID_VERNIER_GOTEMP	0x0002
+#define USB_DEVICE_ID_VERNIER_SKIP	0x0003
+#define USB_DEVICE_ID_VERNIER_CYCLOPS	0x0004
+
+#define USB_VENDOR_ID_WACOM		0x056a
+
+#define USB_VENDOR_ID_WISEGROUP		0x0925
+#define USB_DEVICE_ID_1_PHIDGETSERVO_20	0x8101
+#define USB_DEVICE_ID_4_PHIDGETSERVO_20	0x8104
+#define USB_DEVICE_ID_8_8_4_IF_KIT	0x8201
+#define USB_DEVICE_ID_QUAD_USB_JOYPAD	0x8800
+#define USB_DEVICE_ID_DUAL_USB_JOYPAD	0x8866
+
+#define USB_VENDOR_ID_WISEGROUP_LTD	0x6677
+#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802
+
+#define USB_VENDOR_ID_YEALINK		0x6993
+#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K	0xb001
+
+/*
+ * Alphabetically sorted blacklist by quirk type.
+ */
+
+static const struct hid_blacklist {
+	__u16 idVendor;
+	__u16 idProduct;
+	__u32 quirks;
+} hid_blacklist[] = {
+
+	{ USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_7 },
+	{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_5 },
+
+	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER, HID_QUIRK_BAD_RELATIVE_KEYS },
+
+	{ USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD },
+	{ USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD },
+	{ USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD },
+	{ USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD },
+	{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
+	{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
+	{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
+	{ USB_VENDOR_ID_NEC, USB_DEVICE_ID_NEC_USB_GAME_PAD, HID_QUIRK_BADPAD },
+	{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
+	{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
+	
+	{ USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION },
+
+	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES },
+
+	{ USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV },
+
+	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IR, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_CIDC, 0x0103, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_103, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_104, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_105, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_106, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_107, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_108, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_200, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_201, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_202, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_203, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_204, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_205, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_206, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_207, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_300, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_301, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_302, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_303, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_304, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_305, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_306, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_307, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_308, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_309, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_400, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_401, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_402, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_403, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_404, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_500, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_501, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_503, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1001, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1003, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_PANJIT, 0x0001, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_PANJIT, 0x0002, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_PANJIT, 0x0003, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_PANJIT, 0x0004, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE },
+
+	{ USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE },
+
+	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER, HID_QUIRK_LOGITECH_DESCRIPTOR },
+	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER, HID_QUIRK_LOGITECH_DESCRIPTOR },
+	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2, HID_QUIRK_LOGITECH_DESCRIPTOR },
+
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL },
+
+	{ USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
+	{ USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT },
+
+	{ USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER, HID_QUIRK_SONY_PS3_CONTROLLER },
+
+	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
+	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
+	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
+	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
+	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
+	{ USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
+	{ USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET },
+	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
+	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
+
+	{ USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
+
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD},
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD},
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD},
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
+
+	{ USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658, HID_QUIRK_RESET_LEDS },
+
+	{ 0, 0 }
+};
+
+/* Dynamic HID quirks list - specified at runtime */
+struct quirks_list_struct {
+	struct hid_blacklist hid_bl_item;
+	struct list_head node;
+};
+
+static LIST_HEAD(dquirks_list);
+static DECLARE_RWSEM(dquirks_rwsem);
+
+/* Runtime ("dynamic") quirks manipulation functions */
+
+/**
+ * usbhid_exists_dquirk: find any dynamic quirks for a USB HID device
+ * @idVendor: the 16-bit USB vendor ID, in native byteorder
+ * @idProduct: the 16-bit USB product ID, in native byteorder
+ *
+ * Description:
+ *         Scans dquirks_list for a matching dynamic quirk and returns
+ *         the pointer to the relevant struct hid_blacklist if found.
+ *         Must be called with a read lock held on dquirks_rwsem.
+ *
+ * Returns: NULL if no quirk found, struct hid_blacklist * if found.
+ */
+static struct hid_blacklist *usbhid_exists_dquirk(const u16 idVendor,
+		const u16 idProduct)
+{
+	struct quirks_list_struct *q;
+	struct hid_blacklist *bl_entry = NULL;
+
+	list_for_each_entry(q, &dquirks_list, node) {
+		if (q->hid_bl_item.idVendor == idVendor &&
+				q->hid_bl_item.idProduct == idProduct) {
+			bl_entry = &q->hid_bl_item;
+			break;
+		}
+	}
+
+	if (bl_entry != NULL)
+		dbg("Found dynamic quirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n",
+				bl_entry->quirks, bl_entry->idVendor,
+				bl_entry->idProduct);
+
+	return bl_entry;
+}
+
+
+/**
+ * usbhid_modify_dquirk: add/replace a HID quirk
+ * @idVendor: the 16-bit USB vendor ID, in native byteorder
+ * @idProduct: the 16-bit USB product ID, in native byteorder
+ * @quirks: the u32 quirks value to add/replace
+ *
+ * Description:
+ *         If an dynamic quirk exists in memory for this (idVendor,
+ *         idProduct) pair, replace its quirks value with what was
+ *         provided.  Otherwise, add the quirk to the dynamic quirks list.
+ *
+ * Returns: 0 OK, -error on failure.
+ */
+int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct,
+		const u32 quirks)
+{
+	struct quirks_list_struct *q_new, *q;
+	int list_edited = 0;
+
+	if (!idVendor) {
+		dbg("Cannot add a quirk with idVendor = 0");
+		return -EINVAL;
+	}
+
+	q_new = kmalloc(sizeof(struct quirks_list_struct), GFP_KERNEL);
+	if (!q_new) {
+		dbg("Could not allocate quirks_list_struct");
+		return -ENOMEM;
+	}
+
+	q_new->hid_bl_item.idVendor = idVendor;
+	q_new->hid_bl_item.idProduct = idProduct;
+	q_new->hid_bl_item.quirks = quirks;
+
+	down_write(&dquirks_rwsem);
+
+	list_for_each_entry(q, &dquirks_list, node) {
+
+		if (q->hid_bl_item.idVendor == idVendor &&
+				q->hid_bl_item.idProduct == idProduct) {
+
+			list_replace(&q->node, &q_new->node);
+			kfree(q);
+			list_edited = 1;
+			break;
+
+		}
+
+	}
+
+	if (!list_edited)
+		list_add_tail(&q_new->node, &dquirks_list);
+
+	up_write(&dquirks_rwsem);
+
+	return 0;
+}
+
+
+/**
+ * usbhid_remove_all_dquirks: remove all runtime HID quirks from memory
+ *
+ * Description:
+ *         Free all memory associated with dynamic quirks - called before
+ *         module unload.
+ *
+ */
+static void usbhid_remove_all_dquirks(void)
+{
+	struct quirks_list_struct *q, *temp;
+
+	down_write(&dquirks_rwsem);
+	list_for_each_entry_safe(q, temp, &dquirks_list, node) {
+		list_del(&q->node);
+		kfree(q);
+	}
+	up_write(&dquirks_rwsem);
+
+}
+
+/** 
+ * usbhid_quirks_init: apply USB HID quirks specified at module load time
+ */
+int usbhid_quirks_init(char **quirks_param)
+{
+	u16 idVendor, idProduct;
+	u32 quirks;
+	int n = 0, m;
+
+	for (; quirks_param[n] && n < MAX_USBHID_BOOT_QUIRKS; n++) {
+
+		m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x",
+				&idVendor, &idProduct, &quirks);
+
+		if (m != 3 ||
+				usbhid_modify_dquirk(idVendor, idProduct, quirks) != 0) {
+			printk(KERN_WARNING
+					"Could not parse HID quirk module param %s\n",
+					quirks_param[n]);
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * usbhid_quirks_exit: release memory associated with dynamic_quirks
+ *
+ * Description:
+ *     Release all memory associated with dynamic quirks.  Called upon
+ *     module unload.
+ *
+ * Returns: nothing
+ */
+void usbhid_quirks_exit(void)
+{
+	usbhid_remove_all_dquirks();
+}
+
+/**
+ * usbhid_exists_squirk: return any static quirks for a USB HID device
+ * @idVendor: the 16-bit USB vendor ID, in native byteorder
+ * @idProduct: the 16-bit USB product ID, in native byteorder
+ *
+ * Description:
+ *     Given a USB vendor ID and product ID, return a pointer to
+ *     the hid_blacklist entry associated with that device.
+ *
+ * Returns: pointer if quirk found, or NULL if no quirks found.
+ */
+static const struct hid_blacklist *usbhid_exists_squirk(const u16 idVendor,
+		const u16 idProduct)
+{
+	const struct hid_blacklist *bl_entry = NULL;
+	int n = 0;
+
+	for (; hid_blacklist[n].idVendor; n++)
+		if (hid_blacklist[n].idVendor == idVendor &&
+				hid_blacklist[n].idProduct == idProduct)
+			bl_entry = &hid_blacklist[n];
+
+	if (bl_entry != NULL)
+		dbg("Found squirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n",
+				bl_entry->quirks, bl_entry->idVendor, 
+				bl_entry->idProduct);
+	return bl_entry;
+}
+
+/**
+ * usbhid_lookup_quirk: return any quirks associated with a USB HID device
+ * @idVendor: the 16-bit USB vendor ID, in native byteorder
+ * @idProduct: the 16-bit USB product ID, in native byteorder
+ *
+ * Description:
+ *     Given a USB vendor ID and product ID, return any quirks associated
+ *     with that device.
+ *
+ * Returns: a u32 quirks value.
+ */
+u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct)
+{
+	u32 quirks = 0;
+	const struct hid_blacklist *bl_entry = NULL;
+
+	/* Ignore all Wacom devices */
+	if (idVendor == USB_VENDOR_ID_WACOM)
+		return HID_QUIRK_IGNORE;
+
+	/* ignore all Code Mercenaries IOWarrior devices */
+	if (idVendor == USB_VENDOR_ID_CODEMERCS)
+		if (idProduct >= USB_DEVICE_ID_CODEMERCS_IOW_FIRST &&
+				idProduct <= USB_DEVICE_ID_CODEMERCS_IOW_LAST)
+			return HID_QUIRK_IGNORE;
+
+	down_read(&dquirks_rwsem);
+	bl_entry = usbhid_exists_dquirk(idVendor, idProduct);
+	if (!bl_entry)
+		bl_entry = usbhid_exists_squirk(idVendor, idProduct);
+	if (bl_entry)
+		quirks = bl_entry->quirks;
+	up_read(&dquirks_rwsem);
+
+	return quirks;
+}
+
diff --git a/drivers/usb/input/hid-tmff.c b/drivers/hid/usbhid/hid-tmff.c
similarity index 100%
rename from drivers/usb/input/hid-tmff.c
rename to drivers/hid/usbhid/hid-tmff.c
diff --git a/drivers/usb/input/hid-zpff.c b/drivers/hid/usbhid/hid-zpff.c
similarity index 100%
rename from drivers/usb/input/hid-zpff.c
rename to drivers/hid/usbhid/hid-zpff.c
diff --git a/drivers/usb/input/hiddev.c b/drivers/hid/usbhid/hiddev.c
similarity index 100%
rename from drivers/usb/input/hiddev.c
rename to drivers/hid/usbhid/hiddev.c
diff --git a/drivers/usb/input/usbhid.h b/drivers/hid/usbhid/usbhid.h
similarity index 100%
rename from drivers/usb/input/usbhid.h
rename to drivers/hid/usbhid/usbhid.h
diff --git a/drivers/usb/input/usbkbd.c b/drivers/hid/usbhid/usbkbd.c
similarity index 97%
rename from drivers/usb/input/usbkbd.c
rename to drivers/hid/usbhid/usbkbd.c
index 3749f4a..65aa12e8 100644
--- a/drivers/usb/input/usbkbd.c
+++ b/drivers/hid/usbhid/usbkbd.c
@@ -228,6 +228,7 @@
 	struct usb_kbd *kbd;
 	struct input_dev *input_dev;
 	int i, pipe, maxp;
+	int error = -ENOMEM;
 
 	interface = iface->cur_altsetting;
 
@@ -306,15 +307,19 @@
 	kbd->led->transfer_dma = kbd->leds_dma;
 	kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
 
-	input_register_device(kbd->dev);
+	error = input_register_device(kbd->dev);
+	if (error)
+		goto fail2;
 
 	usb_set_intfdata(iface, kbd);
 	return 0;
 
-fail2:	usb_kbd_free_mem(dev, kbd);
-fail1:	input_free_device(input_dev);
+fail2:	
+	usb_kbd_free_mem(dev, kbd);
+fail1:	
+	input_free_device(input_dev);
 	kfree(kbd);
-	return -ENOMEM;
+	return error;
 }
 
 static void usb_kbd_disconnect(struct usb_interface *intf)
diff --git a/drivers/usb/input/usbmouse.c b/drivers/hid/usbhid/usbmouse.c
similarity index 95%
rename from drivers/usb/input/usbmouse.c
rename to drivers/hid/usbhid/usbmouse.c
index 692fd60..573776d 100644
--- a/drivers/usb/input/usbmouse.c
+++ b/drivers/hid/usbhid/usbmouse.c
@@ -120,6 +120,7 @@
 	struct usb_mouse *mouse;
 	struct input_dev *input_dev;
 	int pipe, maxp;
+	int error = -ENOMEM;
 
 	interface = intf->cur_altsetting;
 
@@ -188,15 +189,21 @@
 	mouse->irq->transfer_dma = mouse->data_dma;
 	mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 
-	input_register_device(mouse->dev);
+	error = input_register_device(mouse->dev);
+	if (error)
+		goto fail3;
 
 	usb_set_intfdata(intf, mouse);
 	return 0;
 
-fail2:	usb_buffer_free(dev, 8, mouse->data, mouse->data_dma);
-fail1:	input_free_device(input_dev);
+fail3:	
+	usb_free_urb(mouse->irq);
+fail2:	
+	usb_buffer_free(dev, 8, mouse->data, mouse->data_dma);
+fail1:	
+	input_free_device(input_dev);
 	kfree(mouse);
-	return -ENOMEM;
+	return error;
 }
 
 static void usb_mouse_disconnect(struct usb_interface *intf)
diff --git a/drivers/hwmon/ams/ams-core.c b/drivers/hwmon/ams/ams-core.c
index f1f0f5d..f5ebad5 100644
--- a/drivers/hwmon/ams/ams-core.c
+++ b/drivers/hwmon/ams/ams-core.c
@@ -141,10 +141,10 @@
 int ams_sensor_attach(void)
 {
 	int result;
-	u32 *prop;
+	const u32 *prop;
 
 	/* Get orientation */
-	prop = (u32*)get_property(ams_info.of_node, "orientation", NULL);
+	prop = get_property(ams_info.of_node, "orientation", NULL);
 	if (!prop)
 		return -ENODEV;
 	ams_info.orient1 = *prop;
diff --git a/drivers/hwmon/ams/ams-i2c.c b/drivers/hwmon/ams/ams-i2c.c
index 0d24bdf..485d333 100644
--- a/drivers/hwmon/ams/ams-i2c.c
+++ b/drivers/hwmon/ams/ams-i2c.c
@@ -263,7 +263,7 @@
 {
 	char *tmp_bus;
 	int result;
-	u32 *prop;
+	const u32 *prop;
 
 	mutex_lock(&ams_info.lock);
 
@@ -276,7 +276,7 @@
 	ams_info.bustype = BUS_I2C;
 
 	/* look for bus either using "reg" or by path */
-	prop = (u32*)get_property(ams_info.of_node, "reg", NULL);
+	prop = get_property(ams_info.of_node, "reg", NULL);
 	if (!prop) {
 		result = -ENODEV;
 
diff --git a/drivers/hwmon/ams/ams-pmu.c b/drivers/hwmon/ams/ams-pmu.c
index 4636ae0..1b01c21 100644
--- a/drivers/hwmon/ams/ams-pmu.c
+++ b/drivers/hwmon/ams/ams-pmu.c
@@ -146,7 +146,7 @@
 
 int __init ams_pmu_init(struct device_node *np)
 {
-	u32 *prop;
+	const u32 *prop;
 	int result;
 
 	mutex_lock(&ams_info.lock);
@@ -160,7 +160,7 @@
 	ams_info.bustype = BUS_HOST;
 
 	/* Get PMU command, should be 0x4e, but we can never know */
-	prop = (u32*)get_property(ams_info.of_node, "reg", NULL);
+	prop = get_property(ams_info.of_node, "reg", NULL);
 	if (!prop) {
 		result = -ENODEV;
 		goto exit;
diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c
index afb71c6..a9e0b30 100644
--- a/drivers/ide/ide-proc.c
+++ b/drivers/ide/ide-proc.c
@@ -310,14 +310,12 @@
 	ide_driver_t	*ide_drv;
 	int		len;
 
-	down_read(&dev->bus->subsys.rwsem);
 	if (dev->driver) {
 		ide_drv = container_of(dev->driver, ide_driver_t, gen_driver);
 		len = sprintf(page, "%s version %s\n",
 				dev->driver->name, ide_drv->version);
 	} else
 		len = sprintf(page, "ide-default version 0.9.newide\n");
-	up_read(&dev->bus->subsys.rwsem);
 	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
 }
 
@@ -327,7 +325,6 @@
 	int ret = 1;
 	int err;
 
-	down_write(&dev->bus->subsys.rwsem);
 	device_release_driver(dev);
 	/* FIXME: device can still be in use by previous driver */
 	strlcpy(drive->driver_req, driver, sizeof(drive->driver_req));
@@ -345,7 +342,6 @@
 	}
 	if (dev->driver && !strcmp(dev->driver->name, driver))
 		ret = 0;
-	up_write(&dev->bus->subsys.rwsem);
 
 	return ret;
 }
diff --git a/drivers/ieee1394/Kconfig b/drivers/ieee1394/Kconfig
index cd84a55..61d7809 100644
--- a/drivers/ieee1394/Kconfig
+++ b/drivers/ieee1394/Kconfig
@@ -1,11 +1,8 @@
-# -*- shell-script -*-
-
 menu "IEEE 1394 (FireWire) support"
 
 config IEEE1394
 	tristate "IEEE 1394 (FireWire) support"
 	depends on PCI || BROKEN
-	select NET
 	help
 	  IEEE 1394 describes a high performance serial bus, which is also
 	  known as FireWire(tm) or i.Link(tm) and is used for connecting all
@@ -35,24 +32,7 @@
 	  Say Y if you really want or need the debugging output, everyone
 	  else says N.
 
-config IEEE1394_EXTRA_CONFIG_ROMS
-	bool "Build in extra config rom entries for certain functionality"
-	depends on IEEE1394
-	help
-	  Some IEEE1394 functionality depends on extra config rom entries
-	  being available in the host adapters CSR. These options will
-	  allow you to choose which ones.
-
-config IEEE1394_CONFIG_ROM_IP1394
-	bool "IP-1394 Entry"
-	depends on IEEE1394_EXTRA_CONFIG_ROMS && IEEE1394
-	help
-	  Adds an entry for using IP-over-1394. If you want to use your
-	  IEEE1394 bus as a network for IP systems (including interacting
-	  with MacOSX and WinXP IP-over-1394), enable this option and the
-	  eth1394 option below.
-
-comment "Device Drivers"
+comment "Controllers"
 	depends on IEEE1394
 
 comment "Texas Instruments PCILynx requires I2C"
@@ -70,6 +50,10 @@
 	  To compile this driver as a module, say M here: the
 	  module will be called pcilynx.
 
+	  Only some old and now very rare PCI and CardBus cards and
+	  PowerMacs G3 B&W contain the PCILynx controller.  Therefore
+	  almost everybody can say N here.
+
 config IEEE1394_OHCI1394
 	tristate "OHCI-1394 support"
 	depends on PCI && IEEE1394
@@ -83,7 +67,7 @@
 	  To compile this driver as a module, say M here: the
 	  module will be called ohci1394.
 
-comment "Protocol Drivers"
+comment "Protocols"
 	depends on IEEE1394
 
 config IEEE1394_VIDEO1394
@@ -121,11 +105,15 @@
 	  This option is buggy and currently broken on some architectures.
 	  If unsure, say N.
 
+config IEEE1394_ETH1394_ROM_ENTRY
+	depends on IEEE1394
+	bool
+	default n
+
 config IEEE1394_ETH1394
-	tristate "Ethernet over 1394"
+	tristate "IP over 1394"
 	depends on IEEE1394 && EXPERIMENTAL && INET
-	select IEEE1394_CONFIG_ROM_IP1394
-	select IEEE1394_EXTRA_CONFIG_ROMS
+	select IEEE1394_ETH1394_ROM_ENTRY
 	help
 	  This driver implements a functional majority of RFC 2734: IPv4 over
 	  1394.  It will provide IP connectivity with implementations of RFC
@@ -134,6 +122,8 @@
 	  This driver is still considered experimental.  It does not yet support
 	  MCAP, therefore multicast support is significantly limited.
 
+	  The module is called eth1394 although it does not emulate Ethernet.
+
 config IEEE1394_DV1394
 	tristate "OHCI-DV I/O support (deprecated)"
 	depends on IEEE1394 && IEEE1394_OHCI1394
@@ -146,12 +136,12 @@
 	tristate "Raw IEEE1394 I/O support"
 	depends on IEEE1394
 	help
-	  Say Y here if you want support for the raw device. This is generally
-	  a good idea, so you should say Y here. The raw device enables
-	  direct communication of user programs with the IEEE 1394 bus and
-	  thus with the attached peripherals.
+	  This option adds support for the raw1394 device file which enables
+	  direct communication of user programs with the IEEE 1394 bus and thus
+	  with the attached peripherals.  Almost all application programs which
+	  access FireWire require this option.
 
-	  To compile this driver as a module, say M here: the
-	  module will be called raw1394.
+	  To compile this driver as a module, say M here: the module will be
+	  called raw1394.
 
 endmenu
diff --git a/drivers/ieee1394/config_roms.c b/drivers/ieee1394/config_roms.c
index e2de6fa..1b98120 100644
--- a/drivers/ieee1394/config_roms.c
+++ b/drivers/ieee1394/config_roms.c
@@ -26,12 +26,6 @@
 	/* Base initialization, called at module load */
 	int (*init)(void);
 
-	/* Add entry to specified host */
-	int (*add)(struct hpsb_host *host);
-
-	/* Remove entry from specified host */
-	void (*remove)(struct hpsb_host *host);
-
 	/* Cleanup called at module exit */
 	void (*cleanup)(void);
 
@@ -39,7 +33,7 @@
 	unsigned int flag;
 };
 
-
+/* The default host entry. This must succeed. */
 int hpsb_default_host_entry(struct hpsb_host *host)
 {
 	struct csr1212_keyval *root;
@@ -63,9 +57,9 @@
 		return -ENOMEM;
 	}
 
-	ret = csr1212_associate_keyval(vend_id, text);
+	csr1212_associate_keyval(vend_id, text);
 	csr1212_release_keyval(text);
-	ret |= csr1212_attach_keyval_to_directory(root, vend_id);
+	ret = csr1212_attach_keyval_to_directory(root, vend_id);
 	csr1212_release_keyval(vend_id);
 	if (ret != CSR1212_SUCCESS) {
 		csr1212_destroy_csr(host->csr.rom);
@@ -78,7 +72,7 @@
 }
 
 
-#ifdef CONFIG_IEEE1394_CONFIG_ROM_IP1394
+#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY
 #include "eth1394.h"
 
 static struct csr1212_keyval *ip1394_ud;
@@ -103,10 +97,12 @@
 	if (!ip1394_ud || !spec_id || !spec_desc || !ver || !ver_desc)
 		goto ip1394_fail;
 
-	if (csr1212_associate_keyval(spec_id, spec_desc) == CSR1212_SUCCESS &&
-	    csr1212_associate_keyval(ver, ver_desc) == CSR1212_SUCCESS &&
-	    csr1212_attach_keyval_to_directory(ip1394_ud, spec_id) == CSR1212_SUCCESS &&
-	    csr1212_attach_keyval_to_directory(ip1394_ud, ver) == CSR1212_SUCCESS)
+	csr1212_associate_keyval(spec_id, spec_desc);
+	csr1212_associate_keyval(ver, ver_desc);
+	if (csr1212_attach_keyval_to_directory(ip1394_ud, spec_id)
+			== CSR1212_SUCCESS &&
+	    csr1212_attach_keyval_to_directory(ip1394_ud, ver)
+			== CSR1212_SUCCESS)
 		ret = 0;
 
 ip1394_fail:
@@ -135,7 +131,7 @@
 	}
 }
 
-static int config_rom_ip1394_add(struct hpsb_host *host)
+int hpsb_config_rom_ip1394_add(struct hpsb_host *host)
 {
 	if (!ip1394_ud)
 		return -ENODEV;
@@ -144,92 +140,55 @@
 					       ip1394_ud) != CSR1212_SUCCESS)
 		return -ENOMEM;
 
+	host->config_roms |= HPSB_CONFIG_ROM_ENTRY_IP1394;
+	host->update_config_rom = 1;
 	return 0;
 }
+EXPORT_SYMBOL_GPL(hpsb_config_rom_ip1394_add);
 
-static void config_rom_ip1394_remove(struct hpsb_host *host)
+void hpsb_config_rom_ip1394_remove(struct hpsb_host *host)
 {
 	csr1212_detach_keyval_from_directory(host->csr.rom->root_kv, ip1394_ud);
+	host->config_roms &= ~HPSB_CONFIG_ROM_ENTRY_IP1394;
+	host->update_config_rom = 1;
 }
+EXPORT_SYMBOL_GPL(hpsb_config_rom_ip1394_remove);
 
 static struct hpsb_config_rom_entry ip1394_entry = {
 	.name		= "ip1394",
 	.init		= config_rom_ip1394_init,
-	.add		= config_rom_ip1394_add,
-	.remove		= config_rom_ip1394_remove,
 	.cleanup	= config_rom_ip1394_cleanup,
 	.flag		= HPSB_CONFIG_ROM_ENTRY_IP1394,
 };
-#endif /* CONFIG_IEEE1394_CONFIG_ROM_IP1394 */
 
+#endif /* CONFIG_IEEE1394_ETH1394_ROM_ENTRY */
 
 static struct hpsb_config_rom_entry *const config_rom_entries[] = {
-#ifdef CONFIG_IEEE1394_CONFIG_ROM_IP1394
+#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY
 	&ip1394_entry,
 #endif
-	NULL,
 };
 
-
+/* Initialize all config roms */
 int hpsb_init_config_roms(void)
 {
 	int i, error = 0;
 
-	for (i = 0; config_rom_entries[i]; i++) {
-		if (!config_rom_entries[i]->init)
-			continue;
-
+	for (i = 0; i < ARRAY_SIZE(config_rom_entries); i++)
 		if (config_rom_entries[i]->init()) {
 			HPSB_ERR("Failed to initialize config rom entry `%s'",
 				 config_rom_entries[i]->name);
 			error = -1;
-		} else
-			HPSB_DEBUG("Initialized config rom entry `%s'",
-				   config_rom_entries[i]->name);
-	}
+		}
 
 	return error;
 }
 
+/* Cleanup all config roms */
 void hpsb_cleanup_config_roms(void)
 {
 	int i;
 
-	for (i = 0; config_rom_entries[i]; i++) {
-		if (config_rom_entries[i]->cleanup)
-			config_rom_entries[i]->cleanup();
-	}
-}
-
-int hpsb_add_extra_config_roms(struct hpsb_host *host)
-{
-	int i, error = 0;
-
-	for (i = 0; config_rom_entries[i]; i++) {
-		if (config_rom_entries[i]->add(host)) {
-			HPSB_ERR("fw-host%d: Failed to attach config rom entry `%s'",
-				 host->id, config_rom_entries[i]->name);
-			error = -1;
-		} else {
-			host->config_roms |= config_rom_entries[i]->flag;
-			host->update_config_rom = 1;
-		}
-	}
-
-	return error;
-}
-
-void hpsb_remove_extra_config_roms(struct hpsb_host *host)
-{
-	int i;
-
-	for (i = 0; config_rom_entries[i]; i++) {
-		if (!(host->config_roms & config_rom_entries[i]->flag))
-			continue;
-
-		config_rom_entries[i]->remove(host);
-
-		host->config_roms &= ~config_rom_entries[i]->flag;
-		host->update_config_rom = 1;
-	}
+	for (i = 0; i < ARRAY_SIZE(config_rom_entries); i++)
+		config_rom_entries[i]->cleanup();
 }
diff --git a/drivers/ieee1394/config_roms.h b/drivers/ieee1394/config_roms.h
index 0a70544..1f5cd1f 100644
--- a/drivers/ieee1394/config_roms.h
+++ b/drivers/ieee1394/config_roms.h
@@ -1,27 +1,19 @@
 #ifndef _IEEE1394_CONFIG_ROMS_H
 #define _IEEE1394_CONFIG_ROMS_H
 
-#include "ieee1394_types.h"
-#include "hosts.h"
+struct hpsb_host;
 
-/* The default host entry. This must succeed. */
 int hpsb_default_host_entry(struct hpsb_host *host);
-
-/* Initialize all config roms */
 int hpsb_init_config_roms(void);
-
-/* Cleanup all config roms */
 void hpsb_cleanup_config_roms(void);
 
-/* Add extra config roms to specified host */
-int hpsb_add_extra_config_roms(struct hpsb_host *host);
-
-/* Remove extra config roms from specified host */
-void hpsb_remove_extra_config_roms(struct hpsb_host *host);
-
-
 /* List of flags to check if a host contains a certain extra config rom
  * entry. Available in the host->config_roms member. */
 #define HPSB_CONFIG_ROM_ENTRY_IP1394		0x00000001
 
+#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY
+int hpsb_config_rom_ip1394_add(struct hpsb_host *host);
+void hpsb_config_rom_ip1394_remove(struct hpsb_host *host);
+#endif
+
 #endif /* _IEEE1394_CONFIG_ROMS_H */
diff --git a/drivers/ieee1394/csr1212.c b/drivers/ieee1394/csr1212.c
index c28f639..d08166b 100644
--- a/drivers/ieee1394/csr1212.c
+++ b/drivers/ieee1394/csr1212.c
@@ -31,12 +31,13 @@
 /* TODO List:
  * - Verify interface consistency: i.e., public functions that take a size
  *   parameter expect size to be in bytes.
- * - Convenience functions for reading a block of data from a given offset.
  */
 
-#ifndef __KERNEL__
-#include <string.h>
-#endif
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <asm/bug.h>
+#include <asm/byteorder.h>
 
 #include "csr1212.h"
 
@@ -46,7 +47,7 @@
 #define __C (1 << CSR1212_KV_TYPE_CSR_OFFSET)
 #define __D (1 << CSR1212_KV_TYPE_DIRECTORY)
 #define __L (1 << CSR1212_KV_TYPE_LEAF)
-static const u_int8_t csr1212_key_id_type_map[0x30] = {
+static const u8 csr1212_key_id_type_map[0x30] = {
 	__C,			/* used by Apple iSight */
 	__D | __L,		/* Descriptor */
 	__I | __D | __L,	/* Bus_Dependent_Info */
@@ -82,10 +83,10 @@
 #undef __L
 
 
-#define quads_to_bytes(_q) ((_q) * sizeof(u_int32_t))
-#define bytes_to_quads(_b) (((_b) + sizeof(u_int32_t) - 1) / sizeof(u_int32_t))
+#define quads_to_bytes(_q) ((_q) * sizeof(u32))
+#define bytes_to_quads(_b) (((_b) + sizeof(u32) - 1) / sizeof(u32))
 
-static inline void free_keyval(struct csr1212_keyval *kv)
+static void free_keyval(struct csr1212_keyval *kv)
 {
 	if ((kv->key.type == CSR1212_KV_TYPE_LEAF) &&
 	    (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM))
@@ -94,14 +95,14 @@
 	CSR1212_FREE(kv);
 }
 
-static u_int16_t csr1212_crc16(const u_int32_t *buffer, size_t length)
+static u16 csr1212_crc16(const u32 *buffer, size_t length)
 {
 	int shift;
-	u_int32_t data;
-	u_int16_t sum, crc = 0;
+	u32 data;
+	u16 sum, crc = 0;
 
 	for (; length; length--) {
-		data = CSR1212_BE32_TO_CPU(*buffer);
+		data = be32_to_cpu(*buffer);
 		buffer++;
 		for (shift = 28; shift >= 0; shift -= 4 ) {
 			sum = ((crc >> 12) ^ (data >> shift)) & 0xf;
@@ -110,21 +111,18 @@
 		crc &= 0xffff;
 	}
 
-	return CSR1212_CPU_TO_BE16(crc);
+	return cpu_to_be16(crc);
 }
 
-#if 0
-/* Microsoft computes the CRC with the bytes in reverse order.  Therefore we
- * have a special version of the CRC algorithm to account for their buggy
- * software. */
-static u_int16_t csr1212_msft_crc16(const u_int32_t *buffer, size_t length)
+/* Microsoft computes the CRC with the bytes in reverse order. */
+static u16 csr1212_msft_crc16(const u32 *buffer, size_t length)
 {
 	int shift;
-	u_int32_t data;
-	u_int16_t sum, crc = 0;
+	u32 data;
+	u16 sum, crc = 0;
 
 	for (; length; length--) {
-		data = CSR1212_LE32_TO_CPU(*buffer);
+		data = le32_to_cpu(*buffer);
 		buffer++;
 		for (shift = 28; shift >= 0; shift -= 4 ) {
 			sum = ((crc >> 12) ^ (data >> shift)) & 0xf;
@@ -133,38 +131,35 @@
 		crc &= 0xffff;
 	}
 
-	return CSR1212_CPU_TO_BE16(crc);
+	return cpu_to_be16(crc);
 }
-#endif
 
-static inline struct csr1212_dentry *csr1212_find_keyval(struct csr1212_keyval *dir,
-							 struct csr1212_keyval *kv)
+static struct csr1212_dentry *
+csr1212_find_keyval(struct csr1212_keyval *dir, struct csr1212_keyval *kv)
 {
 	struct csr1212_dentry *pos;
 
 	for (pos = dir->value.directory.dentries_head;
-	     pos != NULL; pos = pos->next) {
+	     pos != NULL; pos = pos->next)
 		if (pos->kv == kv)
 			return pos;
-	}
 	return NULL;
 }
 
-
-static inline struct csr1212_keyval *csr1212_find_keyval_offset(struct csr1212_keyval *kv_list,
-								u_int32_t offset)
+static struct csr1212_keyval *
+csr1212_find_keyval_offset(struct csr1212_keyval *kv_list, u32 offset)
 {
 	struct csr1212_keyval *kv;
 
-	for (kv = kv_list->next; kv && (kv != kv_list); kv = kv->next) {
+	for (kv = kv_list->next; kv && (kv != kv_list); kv = kv->next)
 		if (kv->offset == offset)
 			return kv;
-	}
 	return NULL;
 }
 
 
 /* Creation Routines */
+
 struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops,
 				       size_t bus_info_size, void *private)
 {
@@ -202,27 +197,17 @@
 	return csr;
 }
 
-
-
 void csr1212_init_local_csr(struct csr1212_csr *csr,
-			    const u_int32_t *bus_info_data, int max_rom)
+			    const u32 *bus_info_data, int max_rom)
 {
 	static const int mr_map[] = { 4, 64, 1024, 0 };
 
-#ifdef __KERNEL__
 	BUG_ON(max_rom & ~0x3);
 	csr->max_rom = mr_map[max_rom];
-#else
-	if (max_rom & ~0x3) /* caller supplied invalid argument */
-		csr->max_rom = 0;
-	else
-		csr->max_rom = mr_map[max_rom];
-#endif
 	memcpy(csr->bus_info_data, bus_info_data, csr->bus_info_len);
 }
 
-
-static struct csr1212_keyval *csr1212_new_keyval(u_int8_t type, u_int8_t key)
+static struct csr1212_keyval *csr1212_new_keyval(u8 type, u8 key)
 {
 	struct csr1212_keyval *kv;
 
@@ -246,10 +231,11 @@
 	return kv;
 }
 
-struct csr1212_keyval *csr1212_new_immediate(u_int8_t key, u_int32_t value)
+struct csr1212_keyval *csr1212_new_immediate(u8 key, u32 value)
 {
-	struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_IMMEDIATE, key);
+	struct csr1212_keyval *kv;
 
+	kv = csr1212_new_keyval(CSR1212_KV_TYPE_IMMEDIATE, key);
 	if (!kv)
 		return NULL;
 
@@ -258,10 +244,12 @@
 	return kv;
 }
 
-struct csr1212_keyval *csr1212_new_leaf(u_int8_t key, const void *data, size_t data_len)
+static struct csr1212_keyval *
+csr1212_new_leaf(u8 key, const void *data, size_t data_len)
 {
-	struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, key);
+	struct csr1212_keyval *kv;
 
+	kv = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, key);
 	if (!kv)
 		return NULL;
 
@@ -285,10 +273,12 @@
 	return kv;
 }
 
-struct csr1212_keyval *csr1212_new_csr_offset(u_int8_t key, u_int32_t csr_offset)
+static struct csr1212_keyval *
+csr1212_new_csr_offset(u8 key, u32 csr_offset)
 {
-	struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_CSR_OFFSET, key);
+	struct csr1212_keyval *kv;
 
+	kv = csr1212_new_keyval(CSR1212_KV_TYPE_CSR_OFFSET, key);
 	if (!kv)
 		return NULL;
 
@@ -299,10 +289,11 @@
 	return kv;
 }
 
-struct csr1212_keyval *csr1212_new_directory(u_int8_t key)
+struct csr1212_keyval *csr1212_new_directory(u8 key)
 {
-	struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_DIRECTORY, key);
+	struct csr1212_keyval *kv;
 
+	kv = csr1212_new_keyval(CSR1212_KV_TYPE_DIRECTORY, key);
 	if (!kv)
 		return NULL;
 
@@ -314,43 +305,29 @@
 	return kv;
 }
 
-int csr1212_associate_keyval(struct csr1212_keyval *kv,
-			     struct csr1212_keyval *associate)
+void csr1212_associate_keyval(struct csr1212_keyval *kv,
+			      struct csr1212_keyval *associate)
 {
-	if (!kv || !associate)
-		return CSR1212_EINVAL;
-
-	if (kv->key.id == CSR1212_KV_ID_DESCRIPTOR ||
-	   (associate->key.id != CSR1212_KV_ID_DESCRIPTOR &&
-	    associate->key.id != CSR1212_KV_ID_DEPENDENT_INFO &&
-	    associate->key.id != CSR1212_KV_ID_EXTENDED_KEY &&
-	    associate->key.id != CSR1212_KV_ID_EXTENDED_DATA &&
-	    associate->key.id < 0x30))
-		return CSR1212_EINVAL;
-
-	if (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID &&
-	   associate->key.id != CSR1212_KV_ID_EXTENDED_KEY)
-		return CSR1212_EINVAL;
-
-	if (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY &&
-	   associate->key.id != CSR1212_KV_ID_EXTENDED_DATA)
-		return CSR1212_EINVAL;
-
-	if (associate->key.id == CSR1212_KV_ID_EXTENDED_KEY &&
-	   kv->key.id != CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID)
-		return CSR1212_EINVAL;
-
-	if (associate->key.id == CSR1212_KV_ID_EXTENDED_DATA &&
-	   kv->key.id != CSR1212_KV_ID_EXTENDED_KEY)
-		return CSR1212_EINVAL;
+	BUG_ON(!kv || !associate || kv->key.id == CSR1212_KV_ID_DESCRIPTOR ||
+	       (associate->key.id != CSR1212_KV_ID_DESCRIPTOR &&
+		associate->key.id != CSR1212_KV_ID_DEPENDENT_INFO &&
+		associate->key.id != CSR1212_KV_ID_EXTENDED_KEY &&
+		associate->key.id != CSR1212_KV_ID_EXTENDED_DATA &&
+		associate->key.id < 0x30) ||
+	       (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID &&
+		associate->key.id != CSR1212_KV_ID_EXTENDED_KEY) ||
+	       (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY &&
+		associate->key.id != CSR1212_KV_ID_EXTENDED_DATA) ||
+	       (associate->key.id == CSR1212_KV_ID_EXTENDED_KEY &&
+		kv->key.id != CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) ||
+	       (associate->key.id == CSR1212_KV_ID_EXTENDED_DATA &&
+		kv->key.id != CSR1212_KV_ID_EXTENDED_KEY));
 
 	if (kv->associate)
 		csr1212_release_keyval(kv->associate);
 
 	associate->refcnt++;
 	kv->associate = associate;
-
-	return CSR1212_SUCCESS;
 }
 
 int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir,
@@ -358,12 +335,11 @@
 {
 	struct csr1212_dentry *dentry;
 
-	if (!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY)
-		return CSR1212_EINVAL;
+	BUG_ON(!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY);
 
 	dentry = CSR1212_MALLOC(sizeof(*dentry));
 	if (!dentry)
-		return CSR1212_ENOMEM;
+		return -ENOMEM;
 
 	dentry->kv = kv;
 
@@ -382,66 +358,22 @@
 	return CSR1212_SUCCESS;
 }
 
-struct csr1212_keyval *csr1212_new_extended_immediate(u_int32_t spec, u_int32_t key,
-						      u_int32_t value)
-{
-	struct csr1212_keyval *kvs, *kvk, *kvv;
+#define CSR1212_DESCRIPTOR_LEAF_DATA(kv) \
+	(&((kv)->value.leaf.data[1]))
 
-	kvs = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID, spec);
-	kvk = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY, key);
-	kvv = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_DATA, value);
+#define CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, type) \
+	((kv)->value.leaf.data[0] = \
+	 cpu_to_be32(CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) | \
+		     ((type) << CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT)))
+#define CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, spec_id) \
+	((kv)->value.leaf.data[0] = \
+	 cpu_to_be32((CSR1212_DESCRIPTOR_LEAF_TYPE(kv) << \
+		      CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) | \
+		     ((spec_id) & CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK)))
 
-	if (!kvs || !kvk || !kvv) {
-		if (kvs)
-			free_keyval(kvs);
-		if (kvk)
-			free_keyval(kvk);
-		if (kvv)
-			free_keyval(kvv);
-		return NULL;
-	}
-
-	/* Don't keep a local reference to the extended key or value. */
-	kvk->refcnt = 0;
-	kvv->refcnt = 0;
-
-	csr1212_associate_keyval(kvk, kvv);
-	csr1212_associate_keyval(kvs, kvk);
-
-	return kvs;
-}
-
-struct csr1212_keyval *csr1212_new_extended_leaf(u_int32_t spec, u_int32_t key,
-						 const void *data, size_t data_len)
-{
-	struct csr1212_keyval *kvs, *kvk, *kvv;
-
-	kvs = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID, spec);
-	kvk = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY, key);
-	kvv = csr1212_new_leaf(CSR1212_KV_ID_EXTENDED_DATA, data, data_len);
-
-	if (!kvs || !kvk || !kvv) {
-		if (kvs)
-			free_keyval(kvs);
-		if (kvk)
-			free_keyval(kvk);
-		if (kvv)
-			free_keyval(kvv);
-		return NULL;
-	}
-
-	/* Don't keep a local reference to the extended key or value. */
-	kvk->refcnt = 0;
-	kvv->refcnt = 0;
-
-	csr1212_associate_keyval(kvk, kvv);
-	csr1212_associate_keyval(kvs, kvk);
-
-	return kvs;
-}
-
-struct csr1212_keyval *csr1212_new_descriptor_leaf(u_int8_t dtype, u_int32_t specifier_id,
-						   const void *data, size_t data_len)
+static struct csr1212_keyval *
+csr1212_new_descriptor_leaf(u8 dtype, u32 specifier_id,
+			    const void *data, size_t data_len)
 {
 	struct csr1212_keyval *kv;
 
@@ -453,194 +385,69 @@
 	CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, dtype);
 	CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, specifier_id);
 
-	if (data) {
+	if (data)
 		memcpy(CSR1212_DESCRIPTOR_LEAF_DATA(kv), data, data_len);
-	}
 
 	return kv;
 }
 
-
-struct csr1212_keyval *csr1212_new_textual_descriptor_leaf(u_int8_t cwidth,
-							   u_int16_t cset,
-							   u_int16_t language,
-							   const void *data,
-							   size_t data_len)
-{
-	struct csr1212_keyval *kv;
-	char *lstr;
-
-	kv = csr1212_new_descriptor_leaf(0, 0, NULL, data_len +
-					 CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD);
-	if (!kv)
-		return NULL;
-
-	CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_WIDTH(kv, cwidth);
-	CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_CHAR_SET(kv, cset);
-	CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language);
-
-	lstr = (char*)CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv);
-
-	/* make sure last quadlet is zeroed out */
-	*((u_int32_t*)&(lstr[(data_len - 1) & ~0x3])) = 0;
-
-	/* don't copy the NUL terminator */
-	memcpy(lstr, data, data_len);
-
-	return kv;
-}
-
+/* Check if string conforms to minimal ASCII as per IEEE 1212 clause 7.4 */
 static int csr1212_check_minimal_ascii(const char *s)
 {
 	static const char minimal_ascii_table[] = {
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
-		0x00, 0x00, 0x0a, 0x00, 0x0C, 0x0D, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x20, 0x21, 0x22, 0x00, 0x00, 0x25, 0x26, 0x27,
-		0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
-		0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
-		0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
-		0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
-		0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
-		0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
-		0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x5f,
-		0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
-		0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
-		0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
-		0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00,
+					/*  1   2   4   8  16  32  64  128 */
+		128,			/* --, --, --, --, --, --, --, 07, */
+		4 + 16 + 32,		/* --, --, 0a, --, 0C, 0D, --, --, */
+		0,			/* --, --, --, --, --, --, --, --, */
+		0,			/* --, --, --, --, --, --, --, --, */
+		255 - 8 - 16,		/* 20, 21, 22, --, --, 25, 26, 27, */
+		255,			/* 28, 29, 2a, 2b, 2c, 2d, 2e, 2f, */
+		255,			/* 30, 31, 32, 33, 34, 35, 36, 37, */
+		255,			/* 38, 39, 3a, 3b, 3c, 3d, 3e, 3f, */
+		255,			/* 40, 41, 42, 43, 44, 45, 46, 47, */
+		255,			/* 48, 49, 4a, 4b, 4c, 4d, 4e, 4f, */
+		255,			/* 50, 51, 52, 53, 54, 55, 56, 57, */
+		1 + 2 + 4 + 128,	/* 58, 59, 5a, --, --, --, --, 5f, */
+		255 - 1,		/* --, 61, 62, 63, 64, 65, 66, 67, */
+		255,			/* 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, */
+		255,			/* 70, 71, 72, 73, 74, 75, 76, 77, */
+		1 + 2 + 4,		/* 78, 79, 7a, --, --, --, --, --, */
 	};
+	int i, j;
+
 	for (; *s; s++) {
-		if (minimal_ascii_table[*s & 0x7F] != *s)
-			return -1; /* failed */
+		i = *s >> 3;		/*  i = *s / 8;		*/
+		j = 1 << (*s & 3);	/*  j = 1 << (*s % 8);	*/
+
+		if (i >= ARRAY_SIZE(minimal_ascii_table) ||
+		    !(minimal_ascii_table[i] & j))
+			return -EINVAL;
 	}
-	/* String conforms to minimal-ascii, as specified by IEEE 1212,
-	 * par. 7.4 */
 	return 0;
 }
 
+/* IEEE 1212 clause 7.5.4.1 textual descriptors (English, minimal ASCII) */
 struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s)
 {
-	/* Check if string conform to minimal_ascii format */
-	if (csr1212_check_minimal_ascii(s))
-		return NULL;
-
-	/* IEEE 1212, par. 7.5.4.1  Textual descriptors (minimal ASCII) */
-	return csr1212_new_textual_descriptor_leaf(0, 0, 0, s, strlen(s));
-}
-
-struct csr1212_keyval *csr1212_new_icon_descriptor_leaf(u_int32_t version,
-							u_int8_t palette_depth,
-							u_int8_t color_space,
-							u_int16_t language,
-							u_int16_t hscan,
-							u_int16_t vscan,
-							u_int32_t *palette,
-							u_int32_t *pixels)
-{
-	static const int pd[4] = { 0, 4, 16, 256 };
-	static const int cs[16] = { 4, 2 };
 	struct csr1212_keyval *kv;
-	int palette_size;
-	int pixel_size = (hscan * vscan + 3) & ~0x3;
+	u32 *text;
+	size_t str_len, quads;
 
-	if (!pixels || (!palette && palette_depth) ||
-	    (palette_depth & ~0x3) || (color_space & ~0xf))
+	if (!s || !*s || csr1212_check_minimal_ascii(s))
 		return NULL;
 
-	palette_size = pd[palette_depth] * cs[color_space];
-
-	kv = csr1212_new_descriptor_leaf(1, 0, NULL,
-					 palette_size + pixel_size +
-					 CSR1212_ICON_DESCRIPTOR_LEAF_OVERHEAD);
+	str_len = strlen(s);
+	quads = bytes_to_quads(str_len);
+	kv = csr1212_new_descriptor_leaf(0, 0, NULL, quads_to_bytes(quads) +
+				      CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD);
 	if (!kv)
 		return NULL;
 
-	CSR1212_ICON_DESCRIPTOR_LEAF_SET_VERSION(kv, version);
-	CSR1212_ICON_DESCRIPTOR_LEAF_SET_PALETTE_DEPTH(kv, palette_depth);
-	CSR1212_ICON_DESCRIPTOR_LEAF_SET_COLOR_SPACE(kv, color_space);
-	CSR1212_ICON_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language);
-	CSR1212_ICON_DESCRIPTOR_LEAF_SET_HSCAN(kv, hscan);
-	CSR1212_ICON_DESCRIPTOR_LEAF_SET_VSCAN(kv, vscan);
+	kv->value.leaf.data[1] = 0;	/* width, character_set, language */
+	text = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv);
+	text[quads - 1] = 0;		/* padding */
+	memcpy(text, s, str_len);
 
-	if (palette_size)
-		memcpy(CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE(kv), palette,
-		       palette_size);
-
-	memcpy(CSR1212_ICON_DESCRIPTOR_LEAF_PIXELS(kv), pixels, pixel_size);
-
-	return kv;
-}
-
-struct csr1212_keyval *csr1212_new_modifiable_descriptor_leaf(u_int16_t max_size,
-							      u_int64_t address)
-{
-	struct csr1212_keyval *kv;
-
-	/* IEEE 1212, par. 7.5.4.3  Modifiable descriptors */
-	kv = csr1212_new_leaf(CSR1212_KV_ID_MODIFIABLE_DESCRIPTOR, NULL, sizeof(u_int64_t));
-	if(!kv)
-		return NULL;
-
-	CSR1212_MODIFIABLE_DESCRIPTOR_SET_MAX_SIZE(kv, max_size);
-	CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_HI(kv, address);
-	CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_LO(kv, address);
-
-	return kv;
-}
-
-static int csr1212_check_keyword(const char *s)
-{
-	for (; *s; s++) {
-
-		if (('A' <= *s) && (*s <= 'Z'))
-			continue;
-		if (('0' <= *s) && (*s <= '9'))
-			continue;
-		if (*s == '-')
-			continue;
-
-		return -1; /* failed */
-	}
-	/* String conforms to keyword, as specified by IEEE 1212,
-	 * par. 7.6.5 */
-	return CSR1212_SUCCESS;
-}
-
-struct csr1212_keyval *csr1212_new_keyword_leaf(int strc, const char *strv[])
-{
-	struct csr1212_keyval *kv;
-	char *buffer;
-	int i, data_len = 0;
-
-	/* Check all keywords to see if they conform to restrictions:
-	 * Only the following characters is allowed ['A'..'Z','0'..'9','-']
-	 * Each word is zero-terminated.
-	 * Also calculate the total length of the keywords.
-	 */
-	for (i = 0; i < strc; i++) {
-		if (!strv[i] || csr1212_check_keyword(strv[i])) {
-			return NULL;
-		}
-		data_len += strlen(strv[i]) + 1; /* Add zero-termination char. */
-	}
-
-	/* IEEE 1212, par. 7.6.5 Keyword leaves */
-	kv = csr1212_new_leaf(CSR1212_KV_ID_KEYWORD, NULL, data_len);
-	if (!kv)
-		return NULL;
-
-	buffer = (char *)kv->value.leaf.data;
-
-	/* make sure last quadlet is zeroed out */
-	*((u_int32_t*)&(buffer[(data_len - 1) & ~0x3])) = 0;
-
-	/* Copy keyword(s) into leaf data buffer */
-	for (i = 0; i < strc; i++) {
-		int len = strlen(strv[i]) + 1;
-		memcpy(buffer, strv[i], len);
-		buffer += len;
-	}
 	return kv;
 }
 
@@ -674,23 +481,12 @@
 	csr1212_release_keyval(kv);
 }
 
-
-void csr1212_disassociate_keyval(struct csr1212_keyval *kv)
-{
-	if (kv->associate) {
-		csr1212_release_keyval(kv->associate);
-	}
-
-	kv->associate = NULL;
-}
-
-
 /* This function is used to free the memory taken by a keyval.  If the given
  * keyval is a directory type, then any keyvals contained in that directory
  * will be destroyed as well if their respective refcnts are 0.  By means of
  * list manipulation, this routine will descend a directory structure in a
  * non-recursive manner. */
-void _csr1212_destroy_keyval(struct csr1212_keyval *kv)
+static void csr1212_destroy_keyval(struct csr1212_keyval *kv)
 {
 	struct csr1212_keyval *k, *a;
 	struct csr1212_dentry dentry;
@@ -715,11 +511,13 @@
 			a = k->associate;
 
 			if (k->key.type == CSR1212_KV_TYPE_DIRECTORY) {
-				/* If the current entry is a directory, then move all
+				/* If the current entry is a directory, move all
 				 * the entries to the destruction list. */
 				if (k->value.directory.dentries_head) {
-					tail->next = k->value.directory.dentries_head;
-					k->value.directory.dentries_head->prev = tail;
+					tail->next =
+					    k->value.directory.dentries_head;
+					k->value.directory.dentries_head->prev =
+					    tail;
 					tail = k->value.directory.dentries_tail;
 				}
 			}
@@ -729,15 +527,22 @@
 
 		head = head->next;
 		if (head) {
-			if (head->prev && head->prev != &dentry) {
+			if (head->prev && head->prev != &dentry)
 				CSR1212_FREE(head->prev);
-			}
 			head->prev = NULL;
-		} else if (tail != &dentry)
+		} else if (tail != &dentry) {
 			CSR1212_FREE(tail);
+		}
 	}
 }
 
+void csr1212_release_keyval(struct csr1212_keyval *kv)
+{
+	if (kv->refcnt > 1)
+		kv->refcnt--;
+	else
+		csr1212_destroy_keyval(kv);
+}
 
 void csr1212_destroy_csr(struct csr1212_csr *csr)
 {
@@ -763,49 +568,51 @@
 }
 
 
-
 /* CSR Image Creation */
 
 static int csr1212_append_new_cache(struct csr1212_csr *csr, size_t romsize)
 {
 	struct csr1212_csr_rom_cache *cache;
-	u_int64_t csr_addr;
+	u64 csr_addr;
 
-	if (!csr || !csr->ops || !csr->ops->allocate_addr_range ||
-	    !csr->ops->release_addr || csr->max_rom < 1)
-		return CSR1212_EINVAL;
+	BUG_ON(!csr || !csr->ops || !csr->ops->allocate_addr_range ||
+	       !csr->ops->release_addr || csr->max_rom < 1);
 
 	/* ROM size must be a multiple of csr->max_rom */
 	romsize = (romsize + (csr->max_rom - 1)) & ~(csr->max_rom - 1);
 
-	csr_addr = csr->ops->allocate_addr_range(romsize, csr->max_rom, csr->private);
-	if (csr_addr == CSR1212_INVALID_ADDR_SPACE) {
-		return CSR1212_ENOMEM;
-	}
+	csr_addr = csr->ops->allocate_addr_range(romsize, csr->max_rom,
+						 csr->private);
+	if (csr_addr == CSR1212_INVALID_ADDR_SPACE)
+		return -ENOMEM;
+
 	if (csr_addr < CSR1212_REGISTER_SPACE_BASE) {
 		/* Invalid address returned from allocate_addr_range(). */
 		csr->ops->release_addr(csr_addr, csr->private);
-		return CSR1212_ENOMEM;
+		return -ENOMEM;
 	}
 
-	cache = csr1212_rom_cache_malloc(csr_addr - CSR1212_REGISTER_SPACE_BASE, romsize);
+	cache = csr1212_rom_cache_malloc(csr_addr - CSR1212_REGISTER_SPACE_BASE,
+					 romsize);
 	if (!cache) {
 		csr->ops->release_addr(csr_addr, csr->private);
-		return CSR1212_ENOMEM;
+		return -ENOMEM;
 	}
 
-	cache->ext_rom = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, CSR1212_KV_ID_EXTENDED_ROM);
+	cache->ext_rom = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF,
+					    CSR1212_KV_ID_EXTENDED_ROM);
 	if (!cache->ext_rom) {
 		csr->ops->release_addr(csr_addr, csr->private);
 		CSR1212_FREE(cache);
-		return CSR1212_ENOMEM;
+		return -ENOMEM;
 	}
 
-	if (csr1212_attach_keyval_to_directory(csr->root_kv, cache->ext_rom) != CSR1212_SUCCESS) {
+	if (csr1212_attach_keyval_to_directory(csr->root_kv, cache->ext_rom) !=
+	    CSR1212_SUCCESS) {
 		csr1212_release_keyval(cache->ext_rom);
 		csr->ops->release_addr(csr_addr, csr->private);
 		CSR1212_FREE(cache);
-		return CSR1212_ENOMEM;
+		return -ENOMEM;
 	}
 	cache->ext_rom->offset = csr_addr - CSR1212_REGISTER_SPACE_BASE;
 	cache->ext_rom->value.leaf.len = -1;
@@ -818,8 +625,8 @@
 	return CSR1212_SUCCESS;
 }
 
-static inline void csr1212_remove_cache(struct csr1212_csr *csr,
-					struct csr1212_csr_rom_cache *cache)
+static void csr1212_remove_cache(struct csr1212_csr *csr,
+				 struct csr1212_csr_rom_cache *cache)
 {
 	if (csr->cache_head == cache)
 		csr->cache_head = cache->next;
@@ -832,7 +639,8 @@
 		cache->next->prev = cache->prev;
 
 	if (cache->ext_rom) {
-		csr1212_detach_keyval_from_directory(csr->root_kv, cache->ext_rom);
+		csr1212_detach_keyval_from_directory(csr->root_kv,
+						     cache->ext_rom);
 		csr1212_release_keyval(cache->ext_rom);
 	}
 
@@ -852,28 +660,29 @@
 	     dentry = dentry->next) {
 		for (dkv = dentry->kv; dkv; dkv = dkv->associate) {
 			/* Special Case: Extended Key Specifier_ID */
-			if (dkv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) {
-				if (last_extkey_spec == NULL) {
+			if (dkv->key.id ==
+			    CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) {
+				if (last_extkey_spec == NULL)
 					last_extkey_spec = dkv;
-				} else if (dkv->value.immediate != last_extkey_spec->value.immediate) {
+				else if (dkv->value.immediate !=
+					 last_extkey_spec->value.immediate)
 					last_extkey_spec = dkv;
-				} else {
+				else
 					continue;
-				}
 			/* Special Case: Extended Key */
 			} else if (dkv->key.id == CSR1212_KV_ID_EXTENDED_KEY) {
-				if (last_extkey == NULL) {
+				if (last_extkey == NULL)
 					last_extkey = dkv;
-				} else if (dkv->value.immediate != last_extkey->value.immediate) {
+				else if (dkv->value.immediate !=
+					 last_extkey->value.immediate)
 					last_extkey = dkv;
-				} else {
+				else
 					continue;
-				}
 			}
 
 			num_entries += 1;
 
-			switch(dkv->key.type) {
+			switch (dkv->key.type) {
 			default:
 			case CSR1212_KV_TYPE_IMMEDIATE:
 			case CSR1212_KV_TYPE_CSR_OFFSET:
@@ -891,8 +700,9 @@
 				/* Special case: Extended ROM leafs */
 				if (dkv->key.id == CSR1212_KV_ID_EXTENDED_ROM) {
 					dkv->value.leaf.len = -1;
-					/* Don't add Extended ROM leafs in the layout list,
-					 * they are handled differently. */
+					/* Don't add Extended ROM leafs in the
+					 * layout list, they are handled
+					 * differently. */
 					break;
 				}
 
@@ -908,20 +718,21 @@
 	return num_entries;
 }
 
-size_t csr1212_generate_layout_order(struct csr1212_keyval *kv)
+static size_t csr1212_generate_layout_order(struct csr1212_keyval *kv)
 {
 	struct csr1212_keyval *ltail = kv;
 	size_t agg_size = 0;
 
-	while(kv) {
-		switch(kv->key.type) {
+	while (kv) {
+		switch (kv->key.type) {
 		case CSR1212_KV_TYPE_LEAF:
 			/* Add 1 quadlet for crc/len field */
 			agg_size += kv->value.leaf.len + 1;
 			break;
 
 		case CSR1212_KV_TYPE_DIRECTORY:
-			kv->value.directory.len = csr1212_generate_layout_subdir(kv, &ltail);
+			kv->value.directory.len =
+				csr1212_generate_layout_subdir(kv, &ltail);
 			/* Add 1 quadlet for crc/len field */
 			agg_size += kv->value.directory.len + 1;
 			break;
@@ -931,9 +742,9 @@
 	return quads_to_bytes(agg_size);
 }
 
-struct csr1212_keyval *csr1212_generate_positions(struct csr1212_csr_rom_cache *cache,
-						  struct csr1212_keyval *start_kv,
-						  int start_pos)
+static struct csr1212_keyval *
+csr1212_generate_positions(struct csr1212_csr_rom_cache *cache,
+			   struct csr1212_keyval *start_kv, int start_pos)
 {
 	struct csr1212_keyval *kv = start_kv;
 	struct csr1212_keyval *okv = start_kv;
@@ -942,13 +753,12 @@
 
 	cache->layout_head = kv;
 
-	while(kv && pos < cache->size) {
+	while (kv && pos < cache->size) {
 		/* Special case: Extended ROM leafs */
-		if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) {
+		if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM)
 			kv->offset = cache->offset + pos;
-		}
 
-		switch(kv->key.type) {
+		switch (kv->key.type) {
 		case CSR1212_KV_TYPE_LEAF:
 			kv_len = kv->value.leaf.len;
 			break;
@@ -959,6 +769,7 @@
 
 		default:
 			/* Should never get here */
+			WARN_ON(1);
 			break;
 		}
 
@@ -972,46 +783,55 @@
 	}
 
 	cache->layout_tail = okv;
-	cache->len = (okv->offset - cache->offset) + quads_to_bytes(okv_len + 1);
+	cache->len = okv->offset - cache->offset + quads_to_bytes(okv_len + 1);
 
 	return kv;
 }
 
-static void csr1212_generate_tree_subdir(struct csr1212_keyval *dir,
-					 u_int32_t *data_buffer)
+#define CSR1212_KV_KEY_SHIFT		24
+#define CSR1212_KV_KEY_TYPE_SHIFT	6
+#define CSR1212_KV_KEY_ID_MASK		0x3f
+#define CSR1212_KV_KEY_TYPE_MASK	0x3	/* after shift */
+
+static void
+csr1212_generate_tree_subdir(struct csr1212_keyval *dir, u32 *data_buffer)
 {
 	struct csr1212_dentry *dentry;
 	struct csr1212_keyval *last_extkey_spec = NULL;
 	struct csr1212_keyval *last_extkey = NULL;
 	int index = 0;
 
-	for (dentry = dir->value.directory.dentries_head; dentry; dentry = dentry->next) {
+	for (dentry = dir->value.directory.dentries_head;
+	     dentry;
+	     dentry = dentry->next) {
 		struct csr1212_keyval *a;
 
 		for (a = dentry->kv; a; a = a->associate) {
-			u_int32_t value = 0;
+			u32 value = 0;
 
 			/* Special Case: Extended Key Specifier_ID */
-			if (a->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) {
-				if (last_extkey_spec == NULL) {
+			if (a->key.id ==
+			    CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) {
+				if (last_extkey_spec == NULL)
 					last_extkey_spec = a;
-				} else if (a->value.immediate != last_extkey_spec->value.immediate) {
+				else if (a->value.immediate !=
+					 last_extkey_spec->value.immediate)
 					last_extkey_spec = a;
-				} else {
+				else
 					continue;
-				}
+
 			/* Special Case: Extended Key */
 			} else if (a->key.id == CSR1212_KV_ID_EXTENDED_KEY) {
-				if (last_extkey == NULL) {
+				if (last_extkey == NULL)
 					last_extkey = a;
-				} else if (a->value.immediate != last_extkey->value.immediate) {
+				else if (a->value.immediate !=
+					 last_extkey->value.immediate)
 					last_extkey = a;
-				} else {
+				else
 					continue;
-				}
 			}
 
-			switch(a->key.type) {
+			switch (a->key.type) {
 			case CSR1212_KV_TYPE_IMMEDIATE:
 				value = a->value.immediate;
 				break;
@@ -1030,32 +850,46 @@
 				break;
 			default:
 				/* Should never get here */
-				break; /* GDB breakpoint */
+				WARN_ON(1);
+				break;
 			}
 
-			value |= (a->key.id & CSR1212_KV_KEY_ID_MASK) << CSR1212_KV_KEY_SHIFT;
+			value |= (a->key.id & CSR1212_KV_KEY_ID_MASK) <<
+				 CSR1212_KV_KEY_SHIFT;
 			value |= (a->key.type & CSR1212_KV_KEY_TYPE_MASK) <<
-				(CSR1212_KV_KEY_SHIFT + CSR1212_KV_KEY_TYPE_SHIFT);
-			data_buffer[index] = CSR1212_CPU_TO_BE32(value);
+				 (CSR1212_KV_KEY_SHIFT +
+				  CSR1212_KV_KEY_TYPE_SHIFT);
+			data_buffer[index] = cpu_to_be32(value);
 			index++;
 		}
 	}
 }
 
-void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache)
+struct csr1212_keyval_img {
+	u16 length;
+	u16 crc;
+
+	/* Must be last */
+	u32 data[0];	/* older gcc can't handle [] which is standard */
+};
+
+static void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache)
 {
 	struct csr1212_keyval *kv, *nkv;
 	struct csr1212_keyval_img *kvi;
 
-	for (kv = cache->layout_head; kv != cache->layout_tail->next; kv = nkv) {
-		kvi = (struct csr1212_keyval_img *)
-			(cache->data + bytes_to_quads(kv->offset - cache->offset));
-		switch(kv->key.type) {
+	for (kv = cache->layout_head;
+	     kv != cache->layout_tail->next;
+	     kv = nkv) {
+		kvi = (struct csr1212_keyval_img *)(cache->data +
+				bytes_to_quads(kv->offset - cache->offset));
+		switch (kv->key.type) {
 		default:
 		case CSR1212_KV_TYPE_IMMEDIATE:
 		case CSR1212_KV_TYPE_CSR_OFFSET:
 			/* Should never get here */
-			break; /* GDB breakpoint */
+			WARN_ON(1);
+			break;
 
 		case CSR1212_KV_TYPE_LEAF:
 			/* Don't copy over Extended ROM areas, they are
@@ -1064,15 +898,16 @@
 				memcpy(kvi->data, kv->value.leaf.data,
 				       quads_to_bytes(kv->value.leaf.len));
 
-			kvi->length = CSR1212_CPU_TO_BE16(kv->value.leaf.len);
+			kvi->length = cpu_to_be16(kv->value.leaf.len);
 			kvi->crc = csr1212_crc16(kvi->data, kv->value.leaf.len);
 			break;
 
 		case CSR1212_KV_TYPE_DIRECTORY:
 			csr1212_generate_tree_subdir(kv, kvi->data);
 
-			kvi->length = CSR1212_CPU_TO_BE16(kv->value.directory.len);
-			kvi->crc = csr1212_crc16(kvi->data, kv->value.directory.len);
+			kvi->length = cpu_to_be16(kv->value.directory.len);
+			kvi->crc = csr1212_crc16(kvi->data,
+						 kv->value.directory.len);
 			break;
 		}
 
@@ -1086,6 +921,10 @@
 	}
 }
 
+/* This size is arbitrarily chosen.
+ * The struct overhead is subtracted for more economic allocations. */
+#define CSR1212_EXTENDED_ROM_SIZE (2048 - sizeof(struct csr1212_csr_rom_cache))
+
 int csr1212_generate_csr_image(struct csr1212_csr *csr)
 {
 	struct csr1212_bus_info_block_img *bi;
@@ -1095,8 +934,7 @@
 	int ret;
 	int init_offset;
 
-	if (!csr)
-		return CSR1212_EINVAL;
+	BUG_ON(!csr);
 
 	cache = csr->cache_head;
 
@@ -1113,18 +951,21 @@
 
 	init_offset = csr->bus_info_len;
 
-	for (kv = csr->root_kv, cache = csr->cache_head; kv; cache = cache->next) {
+	for (kv = csr->root_kv, cache = csr->cache_head;
+	     kv;
+	     cache = cache->next) {
 		if (!cache) {
 			/* Estimate approximate number of additional cache
 			 * regions needed (it assumes that the cache holding
 			 * the first 1K Config ROM space always exists). */
 			int est_c = agg_size / (CSR1212_EXTENDED_ROM_SIZE -
-						(2 * sizeof(u_int32_t))) + 1;
+						(2 * sizeof(u32))) + 1;
 
 			/* Add additional cache regions, extras will be
 			 * removed later */
 			for (; est_c; est_c--) {
-				ret = csr1212_append_new_cache(csr, CSR1212_EXTENDED_ROM_SIZE);
+				ret = csr1212_append_new_cache(csr,
+						CSR1212_EXTENDED_ROM_SIZE);
 				if (ret != CSR1212_SUCCESS)
 					return ret;
 			}
@@ -1136,7 +977,7 @@
 		}
 		kv = csr1212_generate_positions(cache, kv, init_offset);
 		agg_size -= cache->len;
-		init_offset = sizeof(u_int32_t);
+		init_offset = sizeof(u32);
 	}
 
 	/* Remove unused, excess cache regions */
@@ -1149,15 +990,14 @@
 
 	/* Go through the list backward so that when done, the correct CRC
 	 * will be calculated for the Extended ROM areas. */
-	for(cache = csr->cache_tail; cache; cache = cache->prev) {
+	for (cache = csr->cache_tail; cache; cache = cache->prev) {
 		/* Only Extended ROM caches should have this set. */
 		if (cache->ext_rom) {
 			int leaf_size;
 
 			/* Make sure the Extended ROM leaf is a multiple of
 			 * max_rom in size. */
-			if (csr->max_rom < 1)
-				return CSR1212_EINVAL;
+			BUG_ON(csr->max_rom < 1);
 			leaf_size = (cache->len + (csr->max_rom - 1)) &
 				~(csr->max_rom - 1);
 
@@ -1166,7 +1006,7 @@
 			       leaf_size - cache->len);
 
 			/* Subtract leaf header */
-			leaf_size -= sizeof(u_int32_t);
+			leaf_size -= sizeof(u32);
 
 			/* Update the Extended ROM leaf length */
 			cache->ext_rom->value.leaf.len =
@@ -1184,33 +1024,31 @@
 			/* Set the length and CRC of the extended ROM. */
 			struct csr1212_keyval_img *kvi =
 				(struct csr1212_keyval_img*)cache->data;
+			u16 len = bytes_to_quads(cache->len) - 1;
 
-			kvi->length = CSR1212_CPU_TO_BE16(bytes_to_quads(cache->len) - 1);
-			kvi->crc = csr1212_crc16(kvi->data,
-						 bytes_to_quads(cache->len) - 1);
-
+			kvi->length = cpu_to_be16(len);
+			kvi->crc = csr1212_crc16(kvi->data, len);
 		}
 	}
 
 	return CSR1212_SUCCESS;
 }
 
-int csr1212_read(struct csr1212_csr *csr, u_int32_t offset, void *buffer, u_int32_t len)
+int csr1212_read(struct csr1212_csr *csr, u32 offset, void *buffer, u32 len)
 {
 	struct csr1212_csr_rom_cache *cache;
 
-	for (cache = csr->cache_head; cache; cache = cache->next) {
+	for (cache = csr->cache_head; cache; cache = cache->next)
 		if (offset >= cache->offset &&
 		    (offset + len) <= (cache->offset + cache->size)) {
-			memcpy(buffer,
-			       &cache->data[bytes_to_quads(offset - cache->offset)],
+			memcpy(buffer, &cache->data[
+					bytes_to_quads(offset - cache->offset)],
 			       len);
 			return CSR1212_SUCCESS;
 		}
-	}
-	return CSR1212_ENOENT;
-}
 
+	return -ENOENT;
+}
 
 
 /* Parse a chunk of data as a Config ROM */
@@ -1227,46 +1065,43 @@
 	 * Unfortunately, many IEEE 1394 devices do not abide by that, so the
 	 * bus info block will be read 1 quadlet at a time.  The rest of the
 	 * ConfigROM will be read according to the max_rom field. */
-	for (i = 0; i < csr->bus_info_len; i += sizeof(csr1212_quad_t)) {
+	for (i = 0; i < csr->bus_info_len; i += sizeof(u32)) {
 		ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i,
-					 sizeof(csr1212_quad_t),
-					 &csr->cache_head->data[bytes_to_quads(i)],
-					 csr->private);
+			sizeof(u32), &csr->cache_head->data[bytes_to_quads(i)],
+			csr->private);
 		if (ret != CSR1212_SUCCESS)
 			return ret;
 
 		/* check ROM header's info_length */
 		if (i == 0 &&
-		    CSR1212_BE32_TO_CPU(csr->cache_head->data[0]) >> 24 !=
+		    be32_to_cpu(csr->cache_head->data[0]) >> 24 !=
 		    bytes_to_quads(csr->bus_info_len) - 1)
-			return CSR1212_EINVAL;
+			return -EINVAL;
 	}
 
 	bi = (struct csr1212_bus_info_block_img*)csr->cache_head->data;
 	csr->crc_len = quads_to_bytes(bi->crc_length);
 
-	/* IEEE 1212 recommends that crc_len be equal to bus_info_len, but that is not
-	 * always the case, so read the rest of the crc area 1 quadlet at a time. */
-	for (i = csr->bus_info_len; i <= csr->crc_len; i += sizeof(csr1212_quad_t)) {
+	/* IEEE 1212 recommends that crc_len be equal to bus_info_len, but that
+	 * is not always the case, so read the rest of the crc area 1 quadlet at
+	 * a time. */
+	for (i = csr->bus_info_len; i <= csr->crc_len; i += sizeof(u32)) {
 		ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i,
-					 sizeof(csr1212_quad_t),
-					 &csr->cache_head->data[bytes_to_quads(i)],
-					 csr->private);
+			sizeof(u32), &csr->cache_head->data[bytes_to_quads(i)],
+			csr->private);
 		if (ret != CSR1212_SUCCESS)
 			return ret;
 	}
 
-#if 0
-	/* Apparently there are too many differnt wrong implementations of the
-	 * CRC algorithm that verifying them is moot. */
+	/* Apparently there are many different wrong implementations of the CRC
+	 * algorithm.  We don't fail, we just warn. */
 	if ((csr1212_crc16(bi->data, bi->crc_length) != bi->crc) &&
 	    (csr1212_msft_crc16(bi->data, bi->crc_length) != bi->crc))
-		return CSR1212_EINVAL;
-#endif
+		printk(KERN_DEBUG "IEEE 1394 device has ROM CRC error\n");
 
 	cr = CSR1212_MALLOC(sizeof(*cr));
 	if (!cr)
-		return CSR1212_ENOMEM;
+		return -ENOMEM;
 
 	cr->next = NULL;
 	cr->prev = NULL;
@@ -1279,21 +1114,26 @@
 	return CSR1212_SUCCESS;
 }
 
-static int csr1212_parse_dir_entry(struct csr1212_keyval *dir,
-				   csr1212_quad_t ki,
-				   u_int32_t kv_pos)
+#define CSR1212_KV_KEY(q)	(be32_to_cpu(q) >> CSR1212_KV_KEY_SHIFT)
+#define CSR1212_KV_KEY_TYPE(q)	(CSR1212_KV_KEY(q) >> CSR1212_KV_KEY_TYPE_SHIFT)
+#define CSR1212_KV_KEY_ID(q)	(CSR1212_KV_KEY(q) & CSR1212_KV_KEY_ID_MASK)
+#define CSR1212_KV_VAL_MASK	0xffffff
+#define CSR1212_KV_VAL(q)	(be32_to_cpu(q) & CSR1212_KV_VAL_MASK)
+
+static int
+csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos)
 {
 	int ret = CSR1212_SUCCESS;
 	struct csr1212_keyval *k = NULL;
-	u_int32_t offset;
+	u32 offset;
 
-	switch(CSR1212_KV_KEY_TYPE(ki)) {
+	switch (CSR1212_KV_KEY_TYPE(ki)) {
 	case CSR1212_KV_TYPE_IMMEDIATE:
 		k = csr1212_new_immediate(CSR1212_KV_KEY_ID(ki),
 					  CSR1212_KV_VAL(ki));
 		if (!k) {
-			ret = CSR1212_ENOMEM;
-			goto fail;
+			ret = -ENOMEM;
+			goto out;
 		}
 
 		k->refcnt = 0;	/* Don't keep local reference when parsing. */
@@ -1303,8 +1143,8 @@
 		k = csr1212_new_csr_offset(CSR1212_KV_KEY_ID(ki),
 					   CSR1212_KV_VAL(ki));
 		if (!k) {
-			ret = CSR1212_ENOMEM;
-			goto fail;
+			ret = -ENOMEM;
+			goto out;
 		}
 		k->refcnt = 0;	/* Don't keep local reference when parsing. */
 		break;
@@ -1316,8 +1156,8 @@
 			/* Uh-oh.  Can't have a relative offset of 0 for Leaves
 			 * or Directories.  The Config ROM image is most likely
 			 * messed up, so we'll just abort here. */
-			ret = CSR1212_EIO;
-			goto fail;
+			ret = -EIO;
+			goto out;
 		}
 
 		k = csr1212_find_keyval_offset(dir, offset);
@@ -1325,14 +1165,14 @@
 		if (k)
 			break;		/* Found it. */
 
-		if (CSR1212_KV_KEY_TYPE(ki) == CSR1212_KV_TYPE_DIRECTORY) {
+		if (CSR1212_KV_KEY_TYPE(ki) == CSR1212_KV_TYPE_DIRECTORY)
 			k = csr1212_new_directory(CSR1212_KV_KEY_ID(ki));
-		} else {
+		else
 			k = csr1212_new_leaf(CSR1212_KV_KEY_ID(ki), NULL, 0);
-		}
+
 		if (!k) {
-			ret = CSR1212_ENOMEM;
-			goto fail;
+			ret = -ENOMEM;
+			goto out;
 		}
 		k->refcnt = 0;	/* Don't keep local reference when parsing. */
 		k->valid = 0;	/* Contents not read yet so it's not valid. */
@@ -1344,16 +1184,12 @@
 		dir->next = k;
 	}
 	ret = csr1212_attach_keyval_to_directory(dir, k);
-
-fail:
-	if (ret != CSR1212_SUCCESS) {
-		if (k)
-			free_keyval(k);
-	}
+out:
+	if (ret != CSR1212_SUCCESS && k != NULL)
+		free_keyval(k);
 	return ret;
 }
 
-
 int csr1212_parse_keyval(struct csr1212_keyval *kv,
 			 struct csr1212_csr_rom_cache *cache)
 {
@@ -1362,24 +1198,20 @@
 	int ret = CSR1212_SUCCESS;
 	int kvi_len;
 
-	kvi = (struct csr1212_keyval_img*)&cache->data[bytes_to_quads(kv->offset -
-								      cache->offset)];
-	kvi_len = CSR1212_BE16_TO_CPU(kvi->length);
+	kvi = (struct csr1212_keyval_img*)
+		&cache->data[bytes_to_quads(kv->offset - cache->offset)];
+	kvi_len = be16_to_cpu(kvi->length);
 
-#if 0
-	/* Apparently there are too many differnt wrong implementations of the
-	 * CRC algorithm that verifying them is moot. */
+	/* Apparently there are many different wrong implementations of the CRC
+	 * algorithm.  We don't fail, we just warn. */
 	if ((csr1212_crc16(kvi->data, kvi_len) != kvi->crc) &&
-	    (csr1212_msft_crc16(kvi->data, kvi_len) != kvi->crc)) {
-		ret = CSR1212_EINVAL;
-		goto fail;
-	}
-#endif
+	    (csr1212_msft_crc16(kvi->data, kvi_len) != kvi->crc))
+		printk(KERN_DEBUG "IEEE 1394 device has ROM CRC error\n");
 
-	switch(kv->key.type) {
+	switch (kv->key.type) {
 	case CSR1212_KV_TYPE_DIRECTORY:
 		for (i = 0; i < kvi_len; i++) {
-			csr1212_quad_t ki = kvi->data[i];
+			u32 ki = kvi->data[i];
 
 			/* Some devices put null entries in their unit
 			 * directories.  If we come across such an entry,
@@ -1387,76 +1219,72 @@
 			if (ki == 0x0)
 				continue;
 			ret = csr1212_parse_dir_entry(kv, ki,
-						      (kv->offset +
-						       quads_to_bytes(i + 1)));
+					kv->offset + quads_to_bytes(i + 1));
 		}
 		kv->value.directory.len = kvi_len;
 		break;
 
 	case CSR1212_KV_TYPE_LEAF:
 		if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) {
-			kv->value.leaf.data = CSR1212_MALLOC(quads_to_bytes(kvi_len));
+			size_t size = quads_to_bytes(kvi_len);
+
+			kv->value.leaf.data = CSR1212_MALLOC(size);
 			if (!kv->value.leaf.data) {
-				ret = CSR1212_ENOMEM;
-				goto fail;
+				ret = -ENOMEM;
+				goto out;
 			}
 
 			kv->value.leaf.len = kvi_len;
-			memcpy(kv->value.leaf.data, kvi->data, quads_to_bytes(kvi_len));
+			memcpy(kv->value.leaf.data, kvi->data, size);
 		}
 		break;
 	}
 
 	kv->valid = 1;
-
-fail:
+out:
 	return ret;
 }
 
-
-int _csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv)
+static int
+csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv)
 {
 	struct csr1212_cache_region *cr, *ncr, *newcr = NULL;
 	struct csr1212_keyval_img *kvi = NULL;
 	struct csr1212_csr_rom_cache *cache;
 	int cache_index;
-	u_int64_t addr;
-	u_int32_t *cache_ptr;
-	u_int16_t kv_len = 0;
+	u64 addr;
+	u32 *cache_ptr;
+	u16 kv_len = 0;
 
-	if (!csr || !kv || csr->max_rom < 1)
-		return CSR1212_EINVAL;
+	BUG_ON(!csr || !kv || csr->max_rom < 1);
 
 	/* First find which cache the data should be in (or go in if not read
 	 * yet). */
-	for (cache = csr->cache_head; cache; cache = cache->next) {
+	for (cache = csr->cache_head; cache; cache = cache->next)
 		if (kv->offset >= cache->offset &&
 		    kv->offset < (cache->offset + cache->size))
 			break;
-	}
 
 	if (!cache) {
-		csr1212_quad_t q;
-		u_int32_t cache_size;
+		u32 q, cache_size;
 
 		/* Only create a new cache for Extended ROM leaves. */
 		if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM)
-			return CSR1212_EINVAL;
+			return -EINVAL;
 
 		if (csr->ops->bus_read(csr,
 				       CSR1212_REGISTER_SPACE_BASE + kv->offset,
-				       sizeof(csr1212_quad_t), &q, csr->private)) {
-			return CSR1212_EIO;
-		}
+				       sizeof(u32), &q, csr->private))
+			return -EIO;
 
-		kv->value.leaf.len = CSR1212_BE32_TO_CPU(q) >> 16;
+		kv->value.leaf.len = be32_to_cpu(q) >> 16;
 
 		cache_size = (quads_to_bytes(kv->value.leaf.len + 1) +
 			      (csr->max_rom - 1)) & ~(csr->max_rom - 1);
 
 		cache = csr1212_rom_cache_malloc(kv->offset, cache_size);
 		if (!cache)
-			return CSR1212_ENOMEM;
+			return -ENOMEM;
 
 		kv->value.leaf.data = &cache->data[1];
 		csr->cache_tail->next = cache;
@@ -1465,12 +1293,11 @@
 		csr->cache_tail = cache;
 		cache->filled_head =
 			CSR1212_MALLOC(sizeof(*cache->filled_head));
-		if (!cache->filled_head) {
-			return CSR1212_ENOMEM;
-		}
+		if (!cache->filled_head)
+			return -ENOMEM;
 
 		cache->filled_head->offset_start = 0;
-		cache->filled_head->offset_end = sizeof(csr1212_quad_t);
+		cache->filled_head->offset_end = sizeof(u32);
 		cache->filled_tail = cache->filled_head;
 		cache->filled_head->next = NULL;
 		cache->filled_head->prev = NULL;
@@ -1488,7 +1315,7 @@
 		if (cache_index < cr->offset_start) {
 			newcr = CSR1212_MALLOC(sizeof(*newcr));
 			if (!newcr)
-				return CSR1212_ENOMEM;
+				return -ENOMEM;
 
 			newcr->offset_start = cache_index & ~(csr->max_rom - 1);
 			newcr->offset_end = newcr->offset_start;
@@ -1501,18 +1328,18 @@
 			   (cache_index < cr->offset_end)) {
 			kvi = (struct csr1212_keyval_img*)
 				(&cache->data[bytes_to_quads(cache_index)]);
-			kv_len = quads_to_bytes(CSR1212_BE16_TO_CPU(kvi->length) +
-						1);
+			kv_len = quads_to_bytes(be16_to_cpu(kvi->length) + 1);
 			break;
-		} else if (cache_index == cr->offset_end)
+		} else if (cache_index == cr->offset_end) {
 			break;
+		}
 	}
 
 	if (!cr) {
 		cr = cache->filled_tail;
 		newcr = CSR1212_MALLOC(sizeof(*newcr));
 		if (!newcr)
-			return CSR1212_ENOMEM;
+			return -ENOMEM;
 
 		newcr->offset_start = cache_index & ~(csr->max_rom - 1);
 		newcr->offset_end = newcr->offset_start;
@@ -1534,7 +1361,7 @@
 				       csr->private)) {
 			if (csr->max_rom == 4)
 				/* We've got problems! */
-				return CSR1212_EIO;
+				return -EIO;
 
 			/* Apperently the max_rom value was a lie, set it to
 			 * do quadlet reads and try again. */
@@ -1548,8 +1375,7 @@
 		if (!kvi && (cr->offset_end > cache_index)) {
 			kvi = (struct csr1212_keyval_img*)
 				(&cache->data[bytes_to_quads(cache_index)]);
-			kv_len = quads_to_bytes(CSR1212_BE16_TO_CPU(kvi->length) +
-						1);
+			kv_len = quads_to_bytes(be16_to_cpu(kvi->length) + 1);
 		}
 
 		if ((kv_len + (kv->offset - cache->offset)) > cache->size) {
@@ -1557,7 +1383,7 @@
 			 * beyond the ConfigROM image region and thus beyond the
 			 * end of our cache region.  Therefore, we abort now
 			 * rather than seg faulting later. */
-			return CSR1212_EIO;
+			return -EIO;
 		}
 
 		ncr = cr->next;
@@ -1579,7 +1405,16 @@
 	return csr1212_parse_keyval(kv, cache);
 }
 
-
+struct csr1212_keyval *
+csr1212_get_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv)
+{
+	if (!kv)
+		return NULL;
+	if (!kv->valid)
+		if (csr1212_read_keyval(csr, kv) != CSR1212_SUCCESS)
+			return NULL;
+	return kv;
+}
 
 int csr1212_parse_csr(struct csr1212_csr *csr)
 {
@@ -1587,20 +1422,19 @@
 	struct csr1212_dentry *dentry;
 	int ret;
 
-	if (!csr || !csr->ops || !csr->ops->bus_read)
-		return CSR1212_EINVAL;
+	BUG_ON(!csr || !csr->ops || !csr->ops->bus_read);
 
 	ret = csr1212_parse_bus_info_block(csr);
 	if (ret != CSR1212_SUCCESS)
 		return ret;
 
-	if (!csr->ops->get_max_rom)
+	if (!csr->ops->get_max_rom) {
 		csr->max_rom = mr_map[0];	/* default value */
-	else {
+	} else {
 		int i = csr->ops->get_max_rom(csr->bus_info_data,
 					      csr->private);
 		if (i & ~0x3)
-			return CSR1212_EINVAL;
+			return -EINVAL;
 		csr->max_rom = mr_map[i];
 	}
 
@@ -1613,7 +1447,7 @@
 	csr->root_kv->valid = 0;
 	csr->root_kv->next = csr->root_kv;
 	csr->root_kv->prev = csr->root_kv;
-	ret = _csr1212_read_keyval(csr, csr->root_kv);
+	ret = csr1212_read_keyval(csr, csr->root_kv);
 	if (ret != CSR1212_SUCCESS)
 		return ret;
 
@@ -1623,7 +1457,7 @@
 	     dentry; dentry = dentry->next) {
 		if (dentry->kv->key.id == CSR1212_KV_ID_EXTENDED_ROM &&
 			!dentry->kv->valid) {
-			ret = _csr1212_read_keyval(csr, dentry->kv);
+			ret = csr1212_read_keyval(csr, dentry->kv);
 			if (ret != CSR1212_SUCCESS)
 				return ret;
 		}
diff --git a/drivers/ieee1394/csr1212.h b/drivers/ieee1394/csr1212.h
index 17ddd72..df909ce 100644
--- a/drivers/ieee1394/csr1212.h
+++ b/drivers/ieee1394/csr1212.h
@@ -30,94 +30,13 @@
 #ifndef __CSR1212_H__
 #define __CSR1212_H__
 
-
-/* Compatibility layer */
-#ifdef __KERNEL__
-
 #include <linux/types.h>
 #include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/vmalloc.h>
-#include <asm/pgalloc.h>
 
-#define CSR1212_MALLOC(size)		vmalloc((size))
-#define CSR1212_FREE(ptr)		vfree(ptr)
-#define CSR1212_BE16_TO_CPU(quad)	be16_to_cpu(quad)
-#define CSR1212_CPU_TO_BE16(quad)	cpu_to_be16(quad)
-#define CSR1212_BE32_TO_CPU(quad)	be32_to_cpu(quad)
-#define CSR1212_CPU_TO_BE32(quad)	cpu_to_be32(quad)
-#define CSR1212_BE64_TO_CPU(quad)	be64_to_cpu(quad)
-#define CSR1212_CPU_TO_BE64(quad)	cpu_to_be64(quad)
+#define CSR1212_MALLOC(size)	kmalloc((size), GFP_KERNEL)
+#define CSR1212_FREE(ptr)	kfree(ptr)
 
-#define CSR1212_LE16_TO_CPU(quad)	le16_to_cpu(quad)
-#define CSR1212_CPU_TO_LE16(quad)	cpu_to_le16(quad)
-#define CSR1212_LE32_TO_CPU(quad)	le32_to_cpu(quad)
-#define CSR1212_CPU_TO_LE32(quad)	cpu_to_le32(quad)
-#define CSR1212_LE64_TO_CPU(quad)	le64_to_cpu(quad)
-#define CSR1212_CPU_TO_LE64(quad)	cpu_to_le64(quad)
-
-#include <linux/errno.h>
 #define CSR1212_SUCCESS (0)
-#define CSR1212_EINVAL	(-EINVAL)
-#define CSR1212_ENOMEM	(-ENOMEM)
-#define CSR1212_ENOENT	(-ENOENT)
-#define CSR1212_EIO	(-EIO)
-#define CSR1212_EBUSY	(-EBUSY)
-
-#else	/* Userspace */
-
-#include <sys/types.h>
-#include <malloc.h>
-#define CSR1212_MALLOC(size)		malloc(size)
-#define CSR1212_FREE(ptr)		free(ptr)
-#include <endian.h>
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-#include <byteswap.h>
-#define CSR1212_BE16_TO_CPU(quad)	bswap_16(quad)
-#define CSR1212_CPU_TO_BE16(quad)	bswap_16(quad)
-#define CSR1212_BE32_TO_CPU(quad)	bswap_32(quad)
-#define CSR1212_CPU_TO_BE32(quad)	bswap_32(quad)
-#define CSR1212_BE64_TO_CPU(quad)	bswap_64(quad)
-#define CSR1212_CPU_TO_BE64(quad)	bswap_64(quad)
-
-#define CSR1212_LE16_TO_CPU(quad)	(quad)
-#define CSR1212_CPU_TO_LE16(quad)	(quad)
-#define CSR1212_LE32_TO_CPU(quad)	(quad)
-#define CSR1212_CPU_TO_LE32(quad)	(quad)
-#define CSR1212_LE64_TO_CPU(quad)	(quad)
-#define CSR1212_CPU_TO_LE64(quad)	(quad)
-#else
-#define CSR1212_BE16_TO_CPU(quad)	(quad)
-#define CSR1212_CPU_TO_BE16(quad)	(quad)
-#define CSR1212_BE32_TO_CPU(quad)	(quad)
-#define CSR1212_CPU_TO_BE32(quad)	(quad)
-#define CSR1212_BE64_TO_CPU(quad)	(quad)
-#define CSR1212_CPU_TO_BE64(quad)	(quad)
-
-#define CSR1212_LE16_TO_CPU(quad)	bswap_16(quad)
-#define CSR1212_CPU_TO_LE16(quad)	bswap_16(quad)
-#define CSR1212_LE32_TO_CPU(quad)	bswap_32(quad)
-#define CSR1212_CPU_TO_LE32(quad)	bswap_32(quad)
-#define CSR1212_LE64_TO_CPU(quad)	bswap_64(quad)
-#define CSR1212_CPU_TO_LE64(quad)	bswap_64(quad)
-#endif
-
-#include <errno.h>
-#define CSR1212_SUCCESS (0)
-#define CSR1212_EINVAL	(EINVAL)
-#define CSR1212_ENOMEM	(ENOMEM)
-#define CSR1212_ENOENT	(ENOENT)
-#define CSR1212_EIO	(EIO)
-#define CSR1212_EBUSY	(EBUSY)
-
-#endif
-
-
-#define CSR1212_KV_VAL_MASK			0xffffff
-#define CSR1212_KV_KEY_SHIFT			24
-#define CSR1212_KV_KEY_TYPE_SHIFT		6
-#define CSR1212_KV_KEY_ID_MASK			0x3f
-#define CSR1212_KV_KEY_TYPE_MASK		0x3		/* After shift */
 
 
 /* CSR 1212 key types */
@@ -190,48 +109,22 @@
 #define  CSR1212_UNITS_SPACE_END		(CSR1212_UNITS_SPACE_BASE + CSR1212_UNITS_SPACE_SIZE)
 #define  CSR1212_UNITS_SPACE_OFFSET		(CSR1212_UNITS_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE)
 
-#define  CSR1212_EXTENDED_ROM_SIZE		(0x10000 * sizeof(u_int32_t))
-
 #define  CSR1212_INVALID_ADDR_SPACE		-1
 
+
 /* Config ROM image structures */
 struct csr1212_bus_info_block_img {
-	u_int8_t length;
-	u_int8_t crc_length;
-	u_int16_t crc;
+	u8 length;
+	u8 crc_length;
+	u16 crc;
 
 	/* Must be last */
-	u_int32_t data[0];	/* older gcc can't handle [] which is standard */
-};
-
-#define CSR1212_KV_KEY(quad)		(CSR1212_BE32_TO_CPU(quad) >> CSR1212_KV_KEY_SHIFT)
-#define CSR1212_KV_KEY_TYPE(quad)	(CSR1212_KV_KEY(quad) >> CSR1212_KV_KEY_TYPE_SHIFT)
-#define CSR1212_KV_KEY_ID(quad)		(CSR1212_KV_KEY(quad) & CSR1212_KV_KEY_ID_MASK)
-#define CSR1212_KV_VAL(quad)		(CSR1212_BE32_TO_CPU(quad) & CSR1212_KV_VAL_MASK)
-
-#define CSR1212_SET_KV_KEY(quad, key)	((quad) = \
-	CSR1212_CPU_TO_BE32(CSR1212_KV_VAL(quad) | ((key) << CSR1212_KV_KEY_SHIFT)))
-#define CSR1212_SET_KV_VAL(quad, val)	((quad) = \
-	CSR1212_CPU_TO_BE32((CSR1212_KV_KEY(quad) << CSR1212_KV_KEY_SHIFT) | (val)))
-#define CSR1212_SET_KV_TYPEID(quad, type, id)	((quad) = \
-	CSR1212_CPU_TO_BE32(CSR1212_KV_VAL(quad) | \
-	(((((type) & CSR1212_KV_KEY_TYPE_MASK) << CSR1212_KV_KEY_TYPE_SHIFT) | \
-	  ((id) & CSR1212_KV_KEY_ID_MASK)) << CSR1212_KV_KEY_SHIFT)))
-
-typedef u_int32_t csr1212_quad_t;
-
-
-struct csr1212_keyval_img {
-	u_int16_t length;
-	u_int16_t crc;
-
-	/* Must be last */
-	csr1212_quad_t data[0];	/* older gcc can't handle [] which is standard */
+	u32 data[0];	/* older gcc can't handle [] which is standard */
 };
 
 struct csr1212_leaf {
 	int len;
-	u_int32_t *data;
+	u32 *data;
 };
 
 struct csr1212_dentry {
@@ -246,12 +139,12 @@
 
 struct csr1212_keyval {
 	struct {
-		u_int8_t type;
-		u_int8_t id;
+		u8 type;
+		u8 id;
 	} key;
 	union {
-		u_int32_t immediate;
-		u_int32_t csr_offset;
+		u32 immediate;
+		u32 csr_offset;
 		struct csr1212_leaf leaf;
 		struct csr1212_directory directory;
 	} value;
@@ -260,15 +153,15 @@
 
 	/* used in generating and/or parsing CSR image */
 	struct csr1212_keyval *next, *prev;	/* flat list of CSR elements */
-	u_int32_t offset;	/* position in CSR from 0xffff f000 0000 */
-	u_int8_t valid;		/* flag indicating keyval has valid data*/
+	u32 offset;	/* position in CSR from 0xffff f000 0000 */
+	u8 valid;	/* flag indicating keyval has valid data*/
 };
 
 
 struct csr1212_cache_region {
 	struct csr1212_cache_region *next, *prev;
-	u_int32_t offset_start;		/* inclusive */
-	u_int32_t offset_end;		/* exclusive */
+	u32 offset_start;	/* inclusive */
+	u32 offset_end;		/* exclusive */
 };
 
 struct csr1212_csr_rom_cache {
@@ -276,18 +169,18 @@
 	struct csr1212_cache_region *filled_head, *filled_tail;
 	struct csr1212_keyval *layout_head, *layout_tail;
 	size_t size;
-	u_int32_t offset;
+	u32 offset;
 	struct csr1212_keyval *ext_rom;
 	size_t len;
 
 	/* Must be last */
-	u_int32_t data[0];	/* older gcc can't handle [] which is standard */
+	u32 data[0];	/* older gcc can't handle [] which is standard */
 };
 
 struct csr1212_csr {
 	size_t bus_info_len;	/* bus info block length in bytes */
 	size_t crc_len;		/* crc length in bytes */
-	u_int32_t *bus_info_data;	/* bus info data incl bus name and EUI */
+	u32 *bus_info_data;	/* bus info data incl bus name and EUI */
 
 	void *private;		/* private, bus specific data */
 	struct csr1212_bus_ops *ops;
@@ -305,52 +198,38 @@
 	 * from remote nodes when parsing a Config ROM (i.e., read Config ROM
 	 * entries located in the Units Space.  Must return 0 on success
 	 * anything else indicates an error. */
-	int (*bus_read) (struct csr1212_csr *csr, u_int64_t addr,
-			 u_int16_t length, void *buffer, void *private);
+	int (*bus_read) (struct csr1212_csr *csr, u64 addr,
+			 u16 length, void *buffer, void *private);
 
 	/* This function is used by csr1212 to allocate a region in units space
 	 * in the event that Config ROM entries don't all fit in the predefined
 	 * 1K region.  The void *private parameter is private member of struct
 	 * csr1212_csr. */
-	u_int64_t (*allocate_addr_range) (u_int64_t size, u_int32_t alignment,
-					  void *private);
-
+	u64 (*allocate_addr_range) (u64 size, u32 alignment, void *private);
 
 	/* This function is used by csr1212 to release a region in units space
 	 * that is no longer needed. */
-	void (*release_addr) (u_int64_t addr, void *private);
+	void (*release_addr) (u64 addr, void *private);
 
 	/* This function is used by csr1212 to determine the max read request
 	 * supported by a remote node when reading the ConfigROM space.  Must
 	 * return 0, 1, or 2 per IEEE 1212.  */
-	int (*get_max_rom) (u_int32_t *bus_info, void *private);
+	int (*get_max_rom) (u32 *bus_info, void *private);
 };
 
 
-
-
 /* Descriptor Leaf manipulation macros */
 #define CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT 24
 #define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK 0xffffff
-#define CSR1212_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u_int32_t))
+#define CSR1212_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u32))
 
 #define CSR1212_DESCRIPTOR_LEAF_TYPE(kv) \
-	(CSR1212_BE32_TO_CPU((kv)->value.leaf.data[0]) >> CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT)
+	(be32_to_cpu((kv)->value.leaf.data[0]) >> \
+	 CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT)
 #define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) \
-	(CSR1212_BE32_TO_CPU((kv)->value.leaf.data[0]) & \
+	(be32_to_cpu((kv)->value.leaf.data[0]) & \
 	 CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK)
-#define CSR1212_DESCRIPTOR_LEAF_DATA(kv) \
-	(&((kv)->value.leaf.data[1]))
 
-#define CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, type) \
-	((kv)->value.leaf.data[0] = \
-	 CSR1212_CPU_TO_BE32(CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) | \
-			     ((type) << CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT)))
-#define CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, spec_id) \
-	((kv)->value.leaf.data[0] = \
-	 CSR1212_CPU_TO_BE32((CSR1212_DESCRIPTOR_LEAF_TYPE(kv) << \
-			      CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) | \
-			     ((spec_id) & CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK)))
 
 /* Text Descriptor Leaf manipulation macros */
 #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT 28
@@ -358,182 +237,21 @@
 #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT 16
 #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK 0xfff  /* after shift */
 #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK 0xffff
-#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u_int32_t))
+#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u32))
 
 #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) \
-	(CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]) >> \
+	(be32_to_cpu((kv)->value.leaf.data[1]) >> \
 	 CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT)
 #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) \
-	((CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]) >> \
-			     CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT) & \
-			    CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK)
+	((be32_to_cpu((kv)->value.leaf.data[1]) >> \
+	  CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT) & \
+	 CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK)
 #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) \
-	(CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]) & \
+	(be32_to_cpu((kv)->value.leaf.data[1]) & \
 	 CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK)
 #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv) \
 	(&((kv)->value.leaf.data[2]))
 
-#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_WIDTH(kv, width) \
-	((kv)->value.leaf.data[1] = \
-	 ((kv)->value.leaf.data[1] & \
-	  CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK << \
-				CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT))) | \
-	 CSR1212_CPU_TO_BE32(((width) & \
-			      CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK) << \
-			     CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT))
-#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_CHAR_SET(kv, char_set) \
-	((kv)->value.leaf.data[1] = \
-	 ((kv)->value.leaf.data[1] & \
-	  CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK << \
-				CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT))) | \
-	 CSR1212_CPU_TO_BE32(((char_set) & \
-			      CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK) << \
-			     CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT))
-#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language) \
-	((kv)->value.leaf.data[1] = \
-	 ((kv)->value.leaf.data[1] & \
-	  CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK))) | \
-	 CSR1212_CPU_TO_BE32(((language) & \
-			      CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK)))
-
-
-/* Icon Descriptor Leaf manipulation macros */
-#define CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK 0xffffff
-#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT 30
-#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK 0x3 /* after shift */
-#define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT 16
-#define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK 0xf /* after shift */
-#define CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK 0xffff
-#define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT 16
-#define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK 0xffff /* after shift */
-#define CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK 0xffff
-#define CSR1212_ICON_DESCRIPTOR_LEAF_OVERHEAD (3 * sizeof(u_int32_t))
-
-#define CSR1212_ICON_DESCRIPTOR_LEAF_VERSION(kv) \
-	(CSR1212_BE32_TO_CPU((kv)->value.leaf.data[2]) & \
-	 CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK)
-
-#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH(kv) \
-	(CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) >> \
-	 CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT)
-
-#define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE(kv) \
-	((CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) >> \
-	  CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT) & \
-	 CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK)
-
-#define CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE(kv) \
-	(CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) & \
-	 CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK)
-
-#define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN(kv) \
-	((CSR1212_BE32_TO_CPU((kv)->value.leaf.data[4]) >> \
-	  CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_HSCAN_SHIFT) & \
-	 CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_HSCAN_MASK)
-
-#define CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN(kv) \
-	(CSR1212_BE32_TO_CPU((kv)->value.leaf.data[4]) & \
-	 CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK)
-
-#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE(kv) \
-	(&((kv)->value.leaf.data[5]))
-
-static inline u_int32_t *CSR1212_ICON_DESCRIPTOR_LEAF_PIXELS(struct csr1212_keyval *kv)
-{
-	static const int pd[4] = { 0, 4, 16, 256 };
-	static const int cs[16] = { 4, 2 };
-	int ps = pd[CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH(kv)];
-
-	return &kv->value.leaf.data[5 +
-				    (ps * cs[CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE(kv)]) /
-			   sizeof(u_int32_t)];
-}
-
-#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_VERSION(kv, version) \
-	((kv)->value.leaf.data[2] = \
-	 ((kv)->value.leaf.data[2] & \
-	  CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK))) | \
-	 CSR1212_CPU_TO_BE32(((version) & \
-			      CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK)))
-
-#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_PALETTE_DEPTH(kv, palette_depth) \
-	((kv)->value.leaf.data[3] = \
-	 ((kv)->value.leaf.data[3] & \
-	  CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK << \
-				CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT))) | \
-	 CSR1212_CPU_TO_BE32(((palette_depth) & \
-			      CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK) << \
-			     CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT))
-
-#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_COLOR_SPACE(kv, color_space) \
-	((kv)->value.leaf.data[3] = \
-	 ((kv)->value.leaf.data[3] & \
-	  CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK << \
-				CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT))) | \
-	 CSR1212_CPU_TO_BE32(((color_space) & \
-			      CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK) << \
-			     CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT))
-
-#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language) \
-	((kv)->value.leaf.data[3] = \
-	 ((kv)->value.leaf.data[3] & \
-	  CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK))) | \
-	 CSR1212_CPU_TO_BE32(((language) & \
-			      CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK)))
-
-#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_HSCAN(kv, hscan) \
-	((kv)->value.leaf.data[4] = \
-	 ((kv)->value.leaf.data[4] & \
-	  CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK << \
-				CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT))) | \
-	 CSR1212_CPU_TO_BE32(((hscan) & \
-			      CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK) << \
-			     CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT))
-
-#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_VSCAN(kv, vscan) \
-	((kv)->value.leaf.data[4] = \
-	 (((kv)->value.leaf.data[4] & \
-	  CSR1212_CPU_TO_BE32(~CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK))) | \
-	 CSR1212_CPU_TO_BE32(((vscan) & \
-			      CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK)))
-
-
-/* Modifiable Descriptor Leaf manipulation macros */
-#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT 16
-#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK 0xffff
-#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_SHIFT 32
-#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK 0xffff
-#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_LO_MASK 0xffffffffULL
-
-#define CSR1212_MODIFIABLE_DESCRIPTOR_MAX_SIZE(kv) \
-	CSR1212_BE16_TO_CPU((kv)->value.leaf.data[0] >> CSR1212_MODIFIABLE_DESCRIPTOR_MAX_SIZE_SHIFT)
-
-#define CSR1212_MODIFIABLE_DESCRIPTOR_ADDRESS(kv) \
-	(CSR1212_BE16_TO_CPU(((u_int64_t)((kv)->value.leaf.data[0])) << \
-			     CSR1212_MODIFIABLE_DESCRIPTOR_ADDR_HI_SHIFT) | \
-	 CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]))
-
-#define CSR1212_MODIFIABLE_DESCRIPTOR_SET_MAX_SIZE(kv, size) \
-	((kv)->value.leaf.data[0] = \
-	 ((kv)->value.leaf.data[0] & \
-	  CSR1212_CPU_TO_BE32(~(CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK << \
-				CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT))) | \
-	 CSR1212_CPU_TO_BE32(((size) & \
-			      CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK) << \
-			     CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT))
-
-#define CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_HI(kv, addr) \
-	((kv)->value.leaf.data[0] = \
-	 ((kv)->value.leaf.data[0] & \
-	  CSR1212_CPU_TO_BE32(~(CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK))) | \
-	  CSR1212_CPU_TO_BE32(((addr) & \
-			       CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK)))
-
-#define CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_LO(kv, addr) \
-	((kv)->value.leaf.data[1] = \
-	 CSR1212_CPU_TO_BE32(addr & CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_LO_MASK))
-
-
 
 /* The following 2 function are for creating new Configuration ROM trees.  The
  * first function is used for both creating local trees and parsing remote
@@ -543,11 +261,10 @@
 					      size_t bus_info_size,
 					      void *private);
 extern void csr1212_init_local_csr(struct csr1212_csr *csr,
-				   const u_int32_t *bus_info_data, int max_rom);
+				   const u32 *bus_info_data, int max_rom);
 
 
-/* The following function destroys a Configuration ROM tree and release all
- * memory taken by the tree. */
+/* Destroy a Configuration ROM tree and release all memory taken by the tree. */
 extern void csr1212_destroy_csr(struct csr1212_csr *csr);
 
 
@@ -555,50 +272,20 @@
  * a Configuration ROM tree.  Code that creates new keyvals with these functions
  * must release those keyvals with csr1212_release_keyval() when they are no
  * longer needed. */
-extern struct csr1212_keyval *csr1212_new_immediate(u_int8_t key, u_int32_t value);
-extern struct csr1212_keyval *csr1212_new_leaf(u_int8_t key, const void *data,
-					       size_t data_len);
-extern struct csr1212_keyval *csr1212_new_csr_offset(u_int8_t key,
-						     u_int32_t csr_offset);
-extern struct csr1212_keyval *csr1212_new_directory(u_int8_t key);
-extern struct csr1212_keyval *csr1212_new_extended_immediate(u_int32_t spec,
-							     u_int32_t key,
-							     u_int32_t value);
-extern struct csr1212_keyval *csr1212_new_extended_leaf(u_int32_t spec,
-							u_int32_t key,
-							const void *data,
-							size_t data_len);
-extern struct csr1212_keyval *csr1212_new_descriptor_leaf(u_int8_t dtype,
-							  u_int32_t specifier_id,
-							  const void *data,
-							  size_t data_len);
-extern struct csr1212_keyval *csr1212_new_textual_descriptor_leaf(u_int8_t cwidth,
-								  u_int16_t cset,
-								  u_int16_t language,
-								  const void *data,
-								  size_t data_len);
+extern struct csr1212_keyval *csr1212_new_immediate(u8 key, u32 value);
+extern struct csr1212_keyval *csr1212_new_directory(u8 key);
 extern struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s);
-extern struct csr1212_keyval *csr1212_new_icon_descriptor_leaf(u_int32_t version,
-							       u_int8_t palette_depth,
-							       u_int8_t color_space,
-							       u_int16_t language,
-							       u_int16_t hscan,
-							       u_int16_t vscan,
-							       u_int32_t *palette,
-							       u_int32_t *pixels);
-extern struct csr1212_keyval *csr1212_new_modifiable_descriptor_leaf(u_int16_t max_size,
-								     u_int64_t address);
-extern struct csr1212_keyval *csr1212_new_keyword_leaf(int strc,
-						       const char *strv[]);
 
 
-/* The following functions manage association between keyvals.  Typically,
+/* The following function manages association between keyvals.  Typically,
  * Descriptor Leaves and Directories will be associated with another keyval and
  * it is desirable for the Descriptor keyval to be place immediately after the
- * keyval that it is associated with.*/
-extern int csr1212_associate_keyval(struct csr1212_keyval *kv,
-				    struct csr1212_keyval *associate);
-extern void csr1212_disassociate_keyval(struct csr1212_keyval *kv);
+ * keyval that it is associated with.
+ * Take care with subsequent ROM modifications:  There is no function to remove
+ * previously specified associations.
+ */
+extern void csr1212_associate_keyval(struct csr1212_keyval *kv,
+				     struct csr1212_keyval *associate);
 
 
 /* The following functions manage the association of a keyval and directories.
@@ -609,23 +296,15 @@
 						 struct csr1212_keyval *kv);
 
 
-/* The following functions create a Configuration ROM image from the tree of
- * keyvals provided.  csr1212_generate_csr_image() creates a complete image in
- * the list of caches available via csr->cache_head.  The other functions are
- * provided should there be a need to create a flat image without restrictions
- * placed by IEEE 1212. */
-extern struct csr1212_keyval *csr1212_generate_positions(struct csr1212_csr_rom_cache *cache,
-							 struct csr1212_keyval *start_kv,
-							 int start_pos);
-extern size_t csr1212_generate_layout_order(struct csr1212_keyval *kv);
-extern void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache);
+/* Creates a complete Configuration ROM image in the list of caches available
+ * via csr->cache_head. */
 extern int csr1212_generate_csr_image(struct csr1212_csr *csr);
 
 
 /* This is a convience function for reading a block of data out of one of the
  * caches in the csr->cache_head list. */
-extern int csr1212_read(struct csr1212_csr *csr, u_int32_t offset, void *buffer,
-			u_int32_t len);
+extern int csr1212_read(struct csr1212_csr *csr, u32 offset, void *buffer,
+			u32 len);
 
 
 /* The following functions are in place for parsing Configuration ROM images.
@@ -635,15 +314,11 @@
 				struct csr1212_csr_rom_cache *cache);
 extern int csr1212_parse_csr(struct csr1212_csr *csr);
 
-/* These are internal functions referenced by inline functions below. */
-extern int _csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv);
-extern void _csr1212_destroy_keyval(struct csr1212_keyval *kv);
-
 
 /* This function allocates a new cache which may be used for either parsing or
  * generating sub-sets of Configuration ROM images. */
-static inline struct csr1212_csr_rom_cache *csr1212_rom_cache_malloc(u_int32_t offset,
-								     size_t size)
+static inline struct csr1212_csr_rom_cache *
+csr1212_rom_cache_malloc(u32 offset, size_t size)
 {
 	struct csr1212_csr_rom_cache *cache;
 
@@ -667,16 +342,8 @@
 
 /* This function ensures that a keyval contains data when referencing a keyval
  * created by parsing a Configuration ROM. */
-static inline struct csr1212_keyval *csr1212_get_keyval(struct csr1212_csr *csr,
-							struct csr1212_keyval *kv)
-{
-	if (!kv)
-		return NULL;
-	if (!kv->valid)
-		if (_csr1212_read_keyval(csr, kv) != CSR1212_SUCCESS)
-			return NULL;
-	return kv;
-}
+extern struct csr1212_keyval *
+csr1212_get_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv);
 
 
 /* This function increments the reference count for a keyval should there be a
@@ -691,37 +358,29 @@
  * keyval when there are no more users of the keyval.  This should be called by
  * any code that calls csr1212_keep_keyval() or any of the keyval creation
  * routines csr1212_new_*(). */
-static inline void csr1212_release_keyval(struct csr1212_keyval *kv)
-{
-	if (kv->refcnt > 1)
-		kv->refcnt--;
-	else
-		_csr1212_destroy_keyval(kv);
-}
+extern void csr1212_release_keyval(struct csr1212_keyval *kv);
 
 
 /*
  * This macro allows for looping over the keyval entries in a directory and it
  * ensures that keyvals from remote ConfigROMs are parsed properly.
  *
- * _csr is a struct csr1212_csr * that points to CSR associated with dir.
- * _kv is a struct csr1212_keyval * that'll point to the current keyval (loop index).
- * _dir is a struct csr1212_keyval * that points to the directory to be looped.
- * _pos is a struct csr1212_dentry * that is used internally for indexing.
+ * struct csr1212_csr *_csr points to the CSR associated with dir.
+ * struct csr1212_keyval *_kv points to the current keyval (loop index).
+ * struct csr1212_keyval *_dir points to the directory to be looped.
+ * struct csr1212_dentry *_pos is used internally for indexing.
  *
  * kv will be NULL upon exit of the loop.
  */
-#define csr1212_for_each_dir_entry(_csr, _kv, _dir, _pos)			\
-	for (csr1212_get_keyval((_csr), (_dir)),				\
-	     _pos = (_dir)->value.directory.dentries_head,			\
-	     _kv = (_pos) ? csr1212_get_keyval((_csr), _pos->kv) : NULL;	\
-	     (_kv) && (_pos);							\
-	     (_kv->associate == NULL) ?						\
-		     ((_pos = _pos->next), 					\
-		      (_kv = (_pos) ? csr1212_get_keyval((_csr), _pos->kv) :	\
-                          NULL)) :						\
+#define csr1212_for_each_dir_entry(_csr, _kv, _dir, _pos)		\
+	for (csr1212_get_keyval((_csr), (_dir)),			\
+	     _pos = (_dir)->value.directory.dentries_head,		\
+	     _kv = (_pos) ? csr1212_get_keyval((_csr), _pos->kv) : NULL;\
+	     (_kv) && (_pos);						\
+	     (_kv->associate == NULL) ?					\
+		     ((_pos = _pos->next), (_kv = (_pos) ?		\
+				csr1212_get_keyval((_csr), _pos->kv) :	\
+				NULL)) :				\
 		     (_kv = csr1212_get_keyval((_csr), _kv->associate)))
 
-
-
 #endif /* __CSR1212_H__ */
diff --git a/drivers/ieee1394/dma.c b/drivers/ieee1394/dma.c
index c68f328..45d6055 100644
--- a/drivers/ieee1394/dma.c
+++ b/drivers/ieee1394/dma.c
@@ -62,6 +62,9 @@
 
 /* dma_region */
 
+/**
+ * dma_region_init - clear out all fields but do not allocate anything
+ */
 void dma_region_init(struct dma_region *dma)
 {
 	dma->kvirt = NULL;
@@ -71,6 +74,9 @@
 	dma->sglist = NULL;
 }
 
+/**
+ * dma_region_alloc - allocate the buffer and map it to the IOMMU
+ */
 int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes,
 		     struct pci_dev *dev, int direction)
 {
@@ -128,6 +134,9 @@
 	return -ENOMEM;
 }
 
+/**
+ * dma_region_free - unmap and free the buffer
+ */
 void dma_region_free(struct dma_region *dma)
 {
 	if (dma->n_dma_pages) {
@@ -167,6 +176,12 @@
 	return i;
 }
 
+/**
+ * dma_region_offset_to_bus - get bus address of an offset within a DMA region
+ *
+ * Returns the DMA bus address of the byte with the given @offset relative to
+ * the beginning of the @dma.
+ */
 dma_addr_t dma_region_offset_to_bus(struct dma_region * dma,
 				    unsigned long offset)
 {
@@ -177,6 +192,9 @@
 	return sg_dma_address(sg) + rem;
 }
 
+/**
+ * dma_region_sync_for_cpu - sync the CPU's view of the buffer
+ */
 void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset,
 			     unsigned long len)
 {
@@ -193,6 +211,9 @@
 				dma->direction);
 }
 
+/**
+ * dma_region_sync_for_device - sync the IO bus' view of the buffer
+ */
 void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset,
 				unsigned long len)
 {
@@ -244,6 +265,9 @@
 	.nopage = dma_region_pagefault,
 };
 
+/**
+ * dma_region_mmap - map the buffer into a user space process
+ */
 int dma_region_mmap(struct dma_region *dma, struct file *file,
 		    struct vm_area_struct *vma)
 {
diff --git a/drivers/ieee1394/dma.h b/drivers/ieee1394/dma.h
index a1682ab..2727bcd 100644
--- a/drivers/ieee1394/dma.h
+++ b/drivers/ieee1394/dma.h
@@ -66,35 +66,23 @@
 	int direction;
 };
 
-/* clear out all fields but do not allocate anything */
 void dma_region_init(struct dma_region *dma);
-
-/* allocate the buffer and map it to the IOMMU */
 int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes,
 		     struct pci_dev *dev, int direction);
-
-/* unmap and free the buffer */
 void dma_region_free(struct dma_region *dma);
-
-/* sync the CPU's view of the buffer */
 void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset,
 			     unsigned long len);
-
-/* sync the IO bus' view of the buffer */
 void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset,
 				unsigned long len);
-
-/* map the buffer into a user space process */
 int  dma_region_mmap(struct dma_region *dma, struct file *file,
 		     struct vm_area_struct *vma);
-
-/* macro to index into a DMA region (or dma_prog_region) */
-#define dma_region_i(_dma, _type, _index) \
-	( ((_type*) ((_dma)->kvirt)) + (_index) )
-
-/* return the DMA bus address of the byte with the given offset
- * relative to the beginning of the dma_region */
 dma_addr_t dma_region_offset_to_bus(struct dma_region *dma,
 				    unsigned long offset);
 
+/**
+ * dma_region_i - macro to index into a DMA region (or dma_prog_region)
+ */
+#define dma_region_i(_dma, _type, _index) \
+	( ((_type*) ((_dma)->kvirt)) + (_index) )
+
 #endif /* IEEE1394_DMA_H */
diff --git a/drivers/ieee1394/eth1394.c b/drivers/ieee1394/eth1394.c
index 03e44b3..2296d43 100644
--- a/drivers/ieee1394/eth1394.c
+++ b/drivers/ieee1394/eth1394.c
@@ -1,5 +1,5 @@
 /*
- * eth1394.c -- Ethernet driver for Linux IEEE-1394 Subsystem
+ * eth1394.c -- IPv4 driver for Linux IEEE-1394 Subsystem
  *
  * Copyright (C) 2001-2003 Ben Collins <bcollins@debian.org>
  *               2000 Bonin Franck <boninf@free.fr>
@@ -22,10 +22,9 @@
  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
-/* This driver intends to support RFC 2734, which describes a method for
- * transporting IPv4 datagrams over IEEE-1394 serial busses. This driver
- * will ultimately support that method, but currently falls short in
- * several areas.
+/*
+ * This driver intends to support RFC 2734, which describes a method for
+ * transporting IPv4 datagrams over IEEE-1394 serial busses.
  *
  * TODO:
  * RFC 2734 related:
@@ -40,7 +39,6 @@
  * - Consider garbage collecting old partial datagrams after X amount of time
  */
 
-
 #include <linux/module.h>
 
 #include <linux/kernel.h>
@@ -52,7 +50,6 @@
 
 #include <linux/netdevice.h>
 #include <linux/inetdevice.h>
-#include <linux/etherdevice.h>
 #include <linux/if_arp.h>
 #include <linux/if_ether.h>
 #include <linux/ip.h>
@@ -84,10 +81,6 @@
 #define ETH1394_PRINT(level, dev_name, fmt, args...) \
 	printk(level "%s: %s: " fmt, driver_name, dev_name, ## args)
 
-#define DEBUG(fmt, args...) \
-	printk(KERN_ERR "%s:%s[%d]: " fmt "\n", driver_name, __FUNCTION__, __LINE__, ## args)
-#define TRACE() printk(KERN_ERR "%s:%s[%d] ---- TRACE\n", driver_name, __FUNCTION__, __LINE__)
-
 struct fragment_info {
 	struct list_head list;
 	int offset;
@@ -105,9 +98,9 @@
 };
 
 struct pdg_list {
-	struct list_head list;		/* partial datagram list per node	*/
-	unsigned int sz;		/* partial datagram list size per node	*/
-	spinlock_t lock;		/* partial datagram lock		*/
+	struct list_head list;	/* partial datagram list per node	*/
+	unsigned int sz;	/* partial datagram list size per node	*/
+	spinlock_t lock;	/* partial datagram lock		*/
 };
 
 struct eth1394_host_info {
@@ -121,16 +114,14 @@
 };
 
 struct eth1394_node_info {
-	u16 maxpayload;			/* Max payload			*/
-	u8 sspd;			/* Max speed			*/
-	u64 fifo;			/* FIFO address			*/
-	struct pdg_list pdg;		/* partial RX datagram lists	*/
-	int dgl;			/* Outgoing datagram label	*/
+	u16 maxpayload;		/* max payload			*/
+	u8 sspd;		/* max speed			*/
+	u64 fifo;		/* FIFO address			*/
+	struct pdg_list pdg;	/* partial RX datagram lists	*/
+	int dgl;		/* outgoing datagram label	*/
 };
 
-/* Our ieee1394 highlevel driver */
-#define ETH1394_DRIVER_NAME "eth1394"
-static const char driver_name[] = ETH1394_DRIVER_NAME;
+static const char driver_name[] = "eth1394";
 
 static struct kmem_cache *packet_task_cache;
 
@@ -138,18 +129,12 @@
 
 /* Use common.lf to determine header len */
 static const int hdr_type_len[] = {
-	sizeof (struct eth1394_uf_hdr),
-	sizeof (struct eth1394_ff_hdr),
-	sizeof (struct eth1394_sf_hdr),
-	sizeof (struct eth1394_sf_hdr)
+	sizeof(struct eth1394_uf_hdr),
+	sizeof(struct eth1394_ff_hdr),
+	sizeof(struct eth1394_sf_hdr),
+	sizeof(struct eth1394_sf_hdr)
 };
 
-/* Change this to IEEE1394_SPEED_S100 to make testing easier */
-#define ETH1394_SPEED_DEF	IEEE1394_SPEED_MAX
-
-/* For now, this needs to be 1500, so that XP works with us */
-#define ETH1394_DATA_LEN	ETH_DATA_LEN
-
 static const u16 eth1394_speedto_maxpayload[] = {
 /*     S100, S200, S400, S800, S1600, S3200 */
 	512, 1024, 2048, 4096,  4096,  4096
@@ -159,7 +144,8 @@
 MODULE_DESCRIPTION("IEEE 1394 IPv4 Driver (IPv4-over-1394 as per RFC 2734)");
 MODULE_LICENSE("GPL");
 
-/* The max_partial_datagrams parameter is the maximum number of fragmented
+/*
+ * The max_partial_datagrams parameter is the maximum number of fragmented
  * datagrams per node that eth1394 will keep in memory.  Providing an upper
  * bound allows us to limit the amount of memory that partial datagrams
  * consume in the event that some partial datagrams are never completed.
@@ -179,10 +165,7 @@
 static int ether1394_header_cache(struct neighbour *neigh, struct hh_cache *hh);
 static void ether1394_header_cache_update(struct hh_cache *hh,
 					  struct net_device *dev,
-					  unsigned char * haddr);
-static int ether1394_mac_addr(struct net_device *dev, void *p);
-
-static void purge_partial_datagram(struct list_head *old);
+					  unsigned char *haddr);
 static int ether1394_tx(struct sk_buff *skb, struct net_device *dev);
 static void ether1394_iso(struct hpsb_iso *iso);
 
@@ -190,9 +173,9 @@
 
 static int ether1394_write(struct hpsb_host *host, int srcid, int destid,
 			   quadlet_t *data, u64 addr, size_t len, u16 flags);
-static void ether1394_add_host (struct hpsb_host *host);
-static void ether1394_remove_host (struct hpsb_host *host);
-static void ether1394_host_reset (struct hpsb_host *host);
+static void ether1394_add_host(struct hpsb_host *host);
+static void ether1394_remove_host(struct hpsb_host *host);
+static void ether1394_host_reset(struct hpsb_host *host);
 
 /* Function for incoming 1394 packets */
 static struct hpsb_address_ops addr_ops = {
@@ -207,89 +190,107 @@
 	.host_reset =	ether1394_host_reset,
 };
 
-
-/* This is called after an "ifup" */
-static int ether1394_open (struct net_device *dev)
+static int ether1394_recv_init(struct eth1394_priv *priv)
 {
-	struct eth1394_priv *priv = netdev_priv(dev);
-	int ret = 0;
+	unsigned int iso_buf_size;
 
-	/* Something bad happened, don't even try */
-	if (priv->bc_state == ETHER1394_BC_ERROR) {
-		/* we'll try again */
-		priv->iso = hpsb_iso_recv_init(priv->host,
-					       ETHER1394_ISO_BUF_SIZE,
-					       ETHER1394_GASP_BUFFERS,
-					       priv->broadcast_channel,
-					       HPSB_ISO_DMA_PACKET_PER_BUFFER,
-					       1, ether1394_iso);
-		if (priv->iso == NULL) {
-			ETH1394_PRINT(KERN_ERR, dev->name,
-				      "Could not allocate isochronous receive "
-				      "context for the broadcast channel\n");
-			priv->bc_state = ETHER1394_BC_ERROR;
-			ret = -EAGAIN;
-		} else {
-			if (hpsb_iso_recv_start(priv->iso, -1, (1 << 3), -1) < 0)
-				priv->bc_state = ETHER1394_BC_STOPPED;
-			else
-				priv->bc_state = ETHER1394_BC_RUNNING;
-		}
+	/* FIXME: rawiso limits us to PAGE_SIZE */
+	iso_buf_size = min((unsigned int)PAGE_SIZE,
+			   2 * (1U << (priv->host->csr.max_rec + 1)));
+
+	priv->iso = hpsb_iso_recv_init(priv->host,
+				       ETHER1394_GASP_BUFFERS * iso_buf_size,
+				       ETHER1394_GASP_BUFFERS,
+				       priv->broadcast_channel,
+				       HPSB_ISO_DMA_PACKET_PER_BUFFER,
+				       1, ether1394_iso);
+	if (priv->iso == NULL) {
+		ETH1394_PRINT_G(KERN_ERR, "Failed to allocate IR context\n");
+		priv->bc_state = ETHER1394_BC_ERROR;
+		return -EAGAIN;
 	}
 
-	if (ret)
-		return ret;
+	if (hpsb_iso_recv_start(priv->iso, -1, (1 << 3), -1) < 0)
+		priv->bc_state = ETHER1394_BC_STOPPED;
+	else
+		priv->bc_state = ETHER1394_BC_RUNNING;
+	return 0;
+}
 
-	netif_start_queue (dev);
+/* This is called after an "ifup" */
+static int ether1394_open(struct net_device *dev)
+{
+	struct eth1394_priv *priv = netdev_priv(dev);
+	int ret;
+
+	if (priv->bc_state == ETHER1394_BC_ERROR) {
+		ret = ether1394_recv_init(priv);
+		if (ret)
+			return ret;
+	}
+	netif_start_queue(dev);
 	return 0;
 }
 
 /* This is called after an "ifdown" */
-static int ether1394_stop (struct net_device *dev)
+static int ether1394_stop(struct net_device *dev)
 {
-	netif_stop_queue (dev);
+	netif_stop_queue(dev);
 	return 0;
 }
 
 /* Return statistics to the caller */
-static struct net_device_stats *ether1394_stats (struct net_device *dev)
+static struct net_device_stats *ether1394_stats(struct net_device *dev)
 {
 	return &(((struct eth1394_priv *)netdev_priv(dev))->stats);
 }
 
-/* What to do if we timeout. I think a host reset is probably in order, so
- * that's what we do. Should we increment the stat counters too?  */
-static void ether1394_tx_timeout (struct net_device *dev)
+/* FIXME: What to do if we timeout? I think a host reset is probably in order,
+ * so that's what we do. Should we increment the stat counters too?  */
+static void ether1394_tx_timeout(struct net_device *dev)
 {
-	ETH1394_PRINT (KERN_ERR, dev->name, "Timeout, resetting host %s\n",
-		       ((struct eth1394_priv *)netdev_priv(dev))->host->driver->name);
+	struct hpsb_host *host =
+			((struct eth1394_priv *)netdev_priv(dev))->host;
 
-	highlevel_host_reset (((struct eth1394_priv *)netdev_priv(dev))->host);
+	ETH1394_PRINT(KERN_ERR, dev->name, "Timeout, resetting host\n");
+	ether1394_host_reset(host);
+}
 
-	netif_wake_queue (dev);
+static inline int ether1394_max_mtu(struct hpsb_host* host)
+{
+	return (1 << (host->csr.max_rec + 1))
+			- sizeof(union eth1394_hdr) - ETHER1394_GASP_OVERHEAD;
 }
 
 static int ether1394_change_mtu(struct net_device *dev, int new_mtu)
 {
-	struct eth1394_priv *priv = netdev_priv(dev);
+	int max_mtu;
 
-	if ((new_mtu < 68) ||
-	    (new_mtu > min(ETH1394_DATA_LEN,
-			   (int)((1 << (priv->host->csr.max_rec + 1)) -
-				 (sizeof(union eth1394_hdr) +
-				  ETHER1394_GASP_OVERHEAD)))))
+	if (new_mtu < 68)
 		return -EINVAL;
+
+	max_mtu = ether1394_max_mtu(
+			((struct eth1394_priv *)netdev_priv(dev))->host);
+	if (new_mtu > max_mtu) {
+		ETH1394_PRINT(KERN_INFO, dev->name,
+			      "Local node constrains MTU to %d\n", max_mtu);
+		return -ERANGE;
+	}
+
 	dev->mtu = new_mtu;
 	return 0;
 }
 
 static void purge_partial_datagram(struct list_head *old)
 {
-	struct partial_datagram *pd = list_entry(old, struct partial_datagram, list);
+	struct partial_datagram *pd;
 	struct list_head *lh, *n;
+	struct fragment_info *fi;
+
+	pd = list_entry(old, struct partial_datagram, list);
 
 	list_for_each_safe(lh, n, &pd->frag_info) {
-		struct fragment_info *fi = list_entry(lh, struct fragment_info, list);
+		fi = list_entry(lh, struct fragment_info, list);
 		list_del(lh);
 		kfree(fi);
 	}
@@ -330,35 +331,26 @@
 							 nodeid_t nodeid)
 {
 	struct eth1394_node_ref *node;
-	list_for_each_entry(node, inl, list) {
+
+	list_for_each_entry(node, inl, list)
 		if (node->ud->ne->nodeid == nodeid)
 			return node;
-	}
 
 	return NULL;
 }
 
-static int eth1394_probe(struct device *dev)
+static int eth1394_new_node(struct eth1394_host_info *hi,
+			    struct unit_directory *ud)
 {
-	struct unit_directory *ud;
-	struct eth1394_host_info *hi;
 	struct eth1394_priv *priv;
 	struct eth1394_node_ref *new_node;
 	struct eth1394_node_info *node_info;
 
-	ud = container_of(dev, struct unit_directory, device);
-
-	hi = hpsb_get_hostinfo(&eth1394_highlevel, ud->ne->host);
-	if (!hi)
-		return -ENOENT;
-
-	new_node = kmalloc(sizeof(*new_node),
-			   in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+	new_node = kmalloc(sizeof(*new_node), GFP_KERNEL);
 	if (!new_node)
 		return -ENOMEM;
 
-	node_info = kmalloc(sizeof(*node_info),
-			    in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+	node_info = kmalloc(sizeof(*node_info), GFP_KERNEL);
 	if (!node_info) {
 		kfree(new_node);
 		return -ENOMEM;
@@ -374,10 +366,22 @@
 
 	priv = netdev_priv(hi->dev);
 	list_add_tail(&new_node->list, &priv->ip_node_list);
-
 	return 0;
 }
 
+static int eth1394_probe(struct device *dev)
+{
+	struct unit_directory *ud;
+	struct eth1394_host_info *hi;
+
+	ud = container_of(dev, struct unit_directory, device);
+	hi = hpsb_get_hostinfo(&eth1394_highlevel, ud->ne->host);
+	if (!hi)
+		return -ENOENT;
+
+	return eth1394_new_node(hi, ud);
+}
+
 static int eth1394_remove(struct device *dev)
 {
 	struct unit_directory *ud;
@@ -396,24 +400,23 @@
 	priv = netdev_priv(hi->dev);
 
 	old_node = eth1394_find_node(&priv->ip_node_list, ud);
+	if (!old_node)
+		return 0;
 
-	if (old_node) {
-		list_del(&old_node->list);
-		kfree(old_node);
+	list_del(&old_node->list);
+	kfree(old_node);
 
-		node_info = (struct eth1394_node_info*)ud->device.driver_data;
+	node_info = (struct eth1394_node_info*)ud->device.driver_data;
 
-		spin_lock_irqsave(&node_info->pdg.lock, flags);
-		/* The partial datagram list should be empty, but we'll just
-		 * make sure anyway... */
-		list_for_each_safe(lh, n, &node_info->pdg.list) {
-			purge_partial_datagram(lh);
-		}
-		spin_unlock_irqrestore(&node_info->pdg.lock, flags);
+	spin_lock_irqsave(&node_info->pdg.lock, flags);
+	/* The partial datagram list should be empty, but we'll just
+	 * make sure anyway... */
+	list_for_each_safe(lh, n, &node_info->pdg.list)
+		purge_partial_datagram(lh);
+	spin_unlock_irqrestore(&node_info->pdg.lock, flags);
 
-		kfree(node_info);
-		ud->device.driver_data = NULL;
-	}
+	kfree(node_info);
+	ud->device.driver_data = NULL;
 	return 0;
 }
 
@@ -422,44 +425,19 @@
 	struct eth1394_host_info *hi;
 	struct eth1394_priv *priv;
 	struct eth1394_node_ref *node;
-	struct eth1394_node_info *node_info;
 
 	hi = hpsb_get_hostinfo(&eth1394_highlevel, ud->ne->host);
 	if (!hi)
 		return -ENOENT;
 
 	priv = netdev_priv(hi->dev);
-
 	node = eth1394_find_node(&priv->ip_node_list, ud);
+	if (node)
+		return 0;
 
-	if (!node) {
-		node = kmalloc(sizeof(*node),
-			       in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
-		if (!node)
-			return -ENOMEM;
-
-		node_info = kmalloc(sizeof(*node_info),
-				    in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
-		if (!node_info) {
-			kfree(node);
-			return -ENOMEM;
-		}
-
-		spin_lock_init(&node_info->pdg.lock);
-		INIT_LIST_HEAD(&node_info->pdg.list);
-		node_info->pdg.sz = 0;
-
-		ud->device.driver_data = node_info;
-		node->ud = ud;
-
-		priv = netdev_priv(hi->dev);
-		list_add_tail(&node->list, &priv->ip_node_list);
-	}
-
-	return 0;
+	return eth1394_new_node(hi, ud);
 }
 
-
 static struct ieee1394_device_id eth1394_id_table[] = {
 	{
 		.match_flags = (IEEE1394_MATCH_SPECIFIER_ID |
@@ -473,7 +451,7 @@
 MODULE_DEVICE_TABLE(ieee1394, eth1394_id_table);
 
 static struct hpsb_protocol_driver eth1394_proto_driver = {
-	.name		= ETH1394_DRIVER_NAME,
+	.name		= driver_name,
 	.id_table	= eth1394_id_table,
 	.update		= eth1394_update,
 	.driver		= {
@@ -482,47 +460,50 @@
 	},
 };
 
-
-static void ether1394_reset_priv (struct net_device *dev, int set_mtu)
+static void ether1394_reset_priv(struct net_device *dev, int set_mtu)
 {
 	unsigned long flags;
 	int i;
 	struct eth1394_priv *priv = netdev_priv(dev);
 	struct hpsb_host *host = priv->host;
-	u64 guid = get_unaligned((u64*)&(host->csr.rom->bus_info_data[3]));
-	u16 maxpayload = 1 << (host->csr.max_rec + 1);
+	u64 guid = get_unaligned((u64 *)&(host->csr.rom->bus_info_data[3]));
 	int max_speed = IEEE1394_SPEED_MAX;
 
-	spin_lock_irqsave (&priv->lock, flags);
+	spin_lock_irqsave(&priv->lock, flags);
 
-	memset(priv->ud_list, 0, sizeof(struct node_entry*) * ALL_NODES);
+	memset(priv->ud_list, 0, sizeof(priv->ud_list));
 	priv->bc_maxpayload = 512;
 
 	/* Determine speed limit */
-	for (i = 0; i < host->node_count; i++)
+	/* FIXME: This is broken for nodes with link speed < PHY speed,
+	 * and it is suboptimal for S200B...S800B hardware.
+	 * The result of nodemgr's speed probe should be used somehow. */
+	for (i = 0; i < host->node_count; i++) {
+		/* take care of S100B...S400B PHY ports */
+		if (host->speed[i] == SELFID_SPEED_UNKNOWN) {
+			max_speed = IEEE1394_SPEED_100;
+			break;
+		}
 		if (max_speed > host->speed[i])
 			max_speed = host->speed[i];
+	}
 	priv->bc_sspd = max_speed;
 
-	/* We'll use our maxpayload as the default mtu */
 	if (set_mtu) {
-		dev->mtu = min(ETH1394_DATA_LEN,
-			       (int)(maxpayload -
-				     (sizeof(union eth1394_hdr) +
-				      ETHER1394_GASP_OVERHEAD)));
+		/* Use the RFC 2734 default 1500 octets or the maximum payload
+		 * as initial MTU */
+		dev->mtu = min(1500, ether1394_max_mtu(host));
 
 		/* Set our hardware address while we're at it */
 		memcpy(dev->dev_addr, &guid, sizeof(u64));
 		memset(dev->broadcast, 0xff, sizeof(u64));
 	}
 
-	spin_unlock_irqrestore (&priv->lock, flags);
+	spin_unlock_irqrestore(&priv->lock, flags);
 }
 
-/* This function is called right before register_netdev */
-static void ether1394_init_dev (struct net_device *dev)
+static void ether1394_init_dev(struct net_device *dev)
 {
-	/* Our functions */
 	dev->open		= ether1394_open;
 	dev->stop		= ether1394_stop;
 	dev->hard_start_xmit	= ether1394_tx;
@@ -535,10 +516,9 @@
 	dev->hard_header_cache	= ether1394_header_cache;
 	dev->header_cache_update= ether1394_header_cache_update;
 	dev->hard_header_parse	= ether1394_header_parse;
-	dev->set_mac_address	= ether1394_mac_addr;
+
 	SET_ETHTOOL_OPS(dev, &ethtool_ops);
 
-	/* Some constants */
 	dev->watchdog_timeo	= ETHER1394_TIMEOUT;
 	dev->flags		= IFF_BROADCAST | IFF_MULTICAST;
 	dev->features		= NETIF_F_HIGHDMA;
@@ -546,7 +526,8 @@
 	dev->hard_header_len 	= ETH1394_HLEN;
 	dev->type		= ARPHRD_IEEE1394;
 
-	ether1394_reset_priv (dev, 1);
+	/* FIXME: This value was copied from ether_setup(). Is it too much? */
+	dev->tx_queue_len	= 1000;
 }
 
 /*
@@ -554,34 +535,33 @@
  * when the module is installed. This is where we add all of our ethernet
  * devices. One for each host.
  */
-static void ether1394_add_host (struct hpsb_host *host)
+static void ether1394_add_host(struct hpsb_host *host)
 {
 	struct eth1394_host_info *hi = NULL;
 	struct net_device *dev = NULL;
 	struct eth1394_priv *priv;
 	u64 fifo_addr;
 
-	if (!(host->config_roms & HPSB_CONFIG_ROM_ENTRY_IP1394))
+	if (hpsb_config_rom_ip1394_add(host) != 0) {
+		ETH1394_PRINT_G(KERN_ERR, "Can't add IP-over-1394 ROM entry\n");
 		return;
+	}
 
 	fifo_addr = hpsb_allocate_and_register_addrspace(
 			&eth1394_highlevel, host, &addr_ops,
 			ETHER1394_REGION_ADDR_LEN, ETHER1394_REGION_ADDR_LEN,
 			CSR1212_INVALID_ADDR_SPACE, CSR1212_INVALID_ADDR_SPACE);
-	if (fifo_addr == CSR1212_INVALID_ADDR_SPACE)
-		goto out;
+	if (fifo_addr == CSR1212_INVALID_ADDR_SPACE) {
+		ETH1394_PRINT_G(KERN_ERR, "Cannot register CSR space\n");
+		hpsb_config_rom_ip1394_remove(host);
+		return;
+	}
 
-	/* We should really have our own alloc_hpsbdev() function in
-	 * net_init.c instead of calling the one for ethernet then hijacking
-	 * it for ourselves.  That way we'd be a real networking device. */
-	dev = alloc_etherdev(sizeof (struct eth1394_priv));
-
+	dev = alloc_netdev(sizeof(*priv), "eth%d", ether1394_init_dev);
 	if (dev == NULL) {
-		ETH1394_PRINT_G (KERN_ERR, "Out of memory trying to allocate "
-				 "etherdevice for IEEE 1394 device %s-%d\n",
-				 host->driver->name, host->id);
+		ETH1394_PRINT_G(KERN_ERR, "Out of memory\n");
 		goto out;
-        }
+	}
 
 	SET_MODULE_OWNER(dev);
 #if 0
@@ -590,31 +570,26 @@
 #endif
 
 	priv = netdev_priv(dev);
-
 	INIT_LIST_HEAD(&priv->ip_node_list);
-
 	spin_lock_init(&priv->lock);
 	priv->host = host;
 	priv->local_fifo = fifo_addr;
 
 	hi = hpsb_create_hostinfo(&eth1394_highlevel, host, sizeof(*hi));
-
 	if (hi == NULL) {
-		ETH1394_PRINT_G (KERN_ERR, "Out of memory trying to create "
-				 "hostinfo for IEEE 1394 device %s-%d\n",
-				 host->driver->name, host->id);
-		goto out;
-        }
-
-	ether1394_init_dev(dev);
-
-	if (register_netdev (dev)) {
-		ETH1394_PRINT (KERN_ERR, dev->name, "Error registering network driver\n");
+		ETH1394_PRINT_G(KERN_ERR, "Out of memory\n");
 		goto out;
 	}
 
-	ETH1394_PRINT (KERN_INFO, dev->name, "IEEE-1394 IPv4 over 1394 Ethernet (fw-host%d)\n",
-		       host->id);
+	ether1394_reset_priv(dev, 1);
+
+	if (register_netdev(dev)) {
+		ETH1394_PRINT_G(KERN_ERR, "Cannot register the driver\n");
+		goto out;
+	}
+
+	ETH1394_PRINT(KERN_INFO, dev->name, "IPv4 over IEEE 1394 (fw-host%d)\n",
+		      host->id);
 
 	hi->host = host;
 	hi->dev = dev;
@@ -623,61 +598,37 @@
 	 * be checked when the eth device is opened. */
 	priv->broadcast_channel = host->csr.broadcast_channel & 0x3f;
 
-	priv->iso = hpsb_iso_recv_init(host,
-				       ETHER1394_ISO_BUF_SIZE,
-				       ETHER1394_GASP_BUFFERS,
-				       priv->broadcast_channel,
-				       HPSB_ISO_DMA_PACKET_PER_BUFFER,
-				       1, ether1394_iso);
-	if (priv->iso == NULL) {
-		ETH1394_PRINT(KERN_ERR, dev->name,
-			      "Could not allocate isochronous receive context "
-			      "for the broadcast channel\n");
-		priv->bc_state = ETHER1394_BC_ERROR;
-	} else {
-		if (hpsb_iso_recv_start(priv->iso, -1, (1 << 3), -1) < 0)
-			priv->bc_state = ETHER1394_BC_STOPPED;
-		else
-			priv->bc_state = ETHER1394_BC_RUNNING;
-	}
-
+	ether1394_recv_init(priv);
 	return;
-
 out:
-	if (dev != NULL)
+	if (dev)
 		free_netdev(dev);
 	if (hi)
 		hpsb_destroy_hostinfo(&eth1394_highlevel, host);
-
-	return;
+	hpsb_unregister_addrspace(&eth1394_highlevel, host, fifo_addr);
+	hpsb_config_rom_ip1394_remove(host);
 }
 
 /* Remove a card from our list */
-static void ether1394_remove_host (struct hpsb_host *host)
+static void ether1394_remove_host(struct hpsb_host *host)
 {
 	struct eth1394_host_info *hi;
+	struct eth1394_priv *priv;
 
 	hi = hpsb_get_hostinfo(&eth1394_highlevel, host);
-	if (hi != NULL) {
-		struct eth1394_priv *priv = netdev_priv(hi->dev);
-
-		hpsb_unregister_addrspace(&eth1394_highlevel, host,
-					  priv->local_fifo);
-
-		if (priv->iso != NULL)
-			hpsb_iso_shutdown(priv->iso);
-
-		if (hi->dev) {
-			unregister_netdev (hi->dev);
-			free_netdev(hi->dev);
-		}
-	}
-
-	return;
+	if (!hi)
+		return;
+	priv = netdev_priv(hi->dev);
+	hpsb_unregister_addrspace(&eth1394_highlevel, host, priv->local_fifo);
+	hpsb_config_rom_ip1394_remove(host);
+	if (priv->iso)
+		hpsb_iso_shutdown(priv->iso);
+	unregister_netdev(hi->dev);
+	free_netdev(hi->dev);
 }
 
-/* A reset has just arisen */
-static void ether1394_host_reset (struct hpsb_host *host)
+/* A bus reset happened */
+static void ether1394_host_reset(struct hpsb_host *host)
 {
 	struct eth1394_host_info *hi;
 	struct eth1394_priv *priv;
@@ -690,24 +641,23 @@
 	hi = hpsb_get_hostinfo(&eth1394_highlevel, host);
 
 	/* This can happen for hosts that we don't use */
-	if (hi == NULL)
+	if (!hi)
 		return;
 
 	dev = hi->dev;
-	priv = (struct eth1394_priv *)netdev_priv(dev);
+	priv = netdev_priv(dev);
 
-	/* Reset our private host data, but not our mtu */
-	netif_stop_queue (dev);
-	ether1394_reset_priv (dev, 0);
+	/* Reset our private host data, but not our MTU */
+	netif_stop_queue(dev);
+	ether1394_reset_priv(dev, 0);
 
 	list_for_each_entry(node, &priv->ip_node_list, list) {
-		node_info = (struct eth1394_node_info*)node->ud->device.driver_data;
+		node_info = node->ud->device.driver_data;
 
 		spin_lock_irqsave(&node_info->pdg.lock, flags);
 
-		list_for_each_safe(lh, n, &node_info->pdg.list) {
+		list_for_each_safe(lh, n, &node_info->pdg.list)
 			purge_partial_datagram(lh);
-		}
 
 		INIT_LIST_HEAD(&(node_info->pdg.list));
 		node_info->pdg.sz = 0;
@@ -715,7 +665,7 @@
 		spin_unlock_irqrestore(&node_info->pdg.lock, flags);
 	}
 
-	netif_wake_queue (dev);
+	netif_wake_queue(dev);
 }
 
 /******************************************
@@ -723,7 +673,6 @@
  ******************************************/
 /* These functions have been adapted from net/ethernet/eth.c */
 
-
 /* Create a fake MAC header for an arbitrary protocol layer.
  * saddr=NULL means use device source address
  * daddr=NULL means leave destination address (eg unresolved arp). */
@@ -731,25 +680,24 @@
 			    unsigned short type, void *daddr, void *saddr,
 			    unsigned len)
 {
-	struct eth1394hdr *eth = (struct eth1394hdr *)skb_push(skb, ETH1394_HLEN);
+	struct eth1394hdr *eth =
+			(struct eth1394hdr *)skb_push(skb, ETH1394_HLEN);
 
 	eth->h_proto = htons(type);
 
-	if (dev->flags & (IFF_LOOPBACK|IFF_NOARP)) {
+	if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
 		memset(eth->h_dest, 0, dev->addr_len);
-		return(dev->hard_header_len);
+		return dev->hard_header_len;
 	}
 
 	if (daddr) {
-		memcpy(eth->h_dest,daddr,dev->addr_len);
+		memcpy(eth->h_dest, daddr, dev->addr_len);
 		return dev->hard_header_len;
 	}
 
 	return -dev->hard_header_len;
-
 }
 
-
 /* Rebuild the faked MAC header. This is called after an ARP
  * (or in future other address resolution) has completed on this
  * sk_buff. We now let ARP fill in the other fields.
@@ -760,38 +708,30 @@
 static int ether1394_rebuild_header(struct sk_buff *skb)
 {
 	struct eth1394hdr *eth = (struct eth1394hdr *)skb->data;
-	struct net_device *dev = skb->dev;
 
-	switch (eth->h_proto) {
+	if (eth->h_proto == htons(ETH_P_IP))
+		return arp_find((unsigned char *)&eth->h_dest, skb);
 
-#ifdef CONFIG_INET
-	case __constant_htons(ETH_P_IP):
- 		return arp_find((unsigned char*)&eth->h_dest, skb);
-#endif
-	default:
-		ETH1394_PRINT(KERN_DEBUG, dev->name,
-			      "unable to resolve type %04x addresses.\n",
-			      ntohs(eth->h_proto));
-		break;
-	}
-
+	ETH1394_PRINT(KERN_DEBUG, skb->dev->name,
+		      "unable to resolve type %04x addresses\n",
+		      ntohs(eth->h_proto));
 	return 0;
 }
 
 static int ether1394_header_parse(struct sk_buff *skb, unsigned char *haddr)
 {
 	struct net_device *dev = skb->dev;
+
 	memcpy(haddr, dev->dev_addr, ETH1394_ALEN);
 	return ETH1394_ALEN;
 }
 
-
 static int ether1394_header_cache(struct neighbour *neigh, struct hh_cache *hh)
 {
 	unsigned short type = hh->hh_type;
-	struct eth1394hdr *eth = (struct eth1394hdr*)(((u8*)hh->hh_data) +
-						      (16 - ETH1394_HLEN));
 	struct net_device *dev = neigh->dev;
+	struct eth1394hdr *eth =
+		(struct eth1394hdr *)((u8 *)hh->hh_data + 16 - ETH1394_HLEN);
 
 	if (type == htons(ETH_P_802_3))
 		return -1;
@@ -808,38 +748,25 @@
 					  struct net_device *dev,
 					  unsigned char * haddr)
 {
-	memcpy(((u8*)hh->hh_data) + (16 - ETH1394_HLEN), haddr, dev->addr_len);
+	memcpy((u8 *)hh->hh_data + 16 - ETH1394_HLEN, haddr, dev->addr_len);
 }
 
-static int ether1394_mac_addr(struct net_device *dev, void *p)
-{
-	if (netif_running(dev))
-		return -EBUSY;
-
-	/* Not going to allow setting the MAC address, we really need to use
-	 * the real one supplied by the hardware */
-	 return -EINVAL;
- }
-
-
-
 /******************************************
  * Datagram reception code
  ******************************************/
 
 /* Copied from net/ethernet/eth.c */
-static inline u16 ether1394_type_trans(struct sk_buff *skb,
-				       struct net_device *dev)
+static u16 ether1394_type_trans(struct sk_buff *skb, struct net_device *dev)
 {
 	struct eth1394hdr *eth;
 	unsigned char *rawp;
 
-	skb->mac.raw = skb->data;
-	skb_pull (skb, ETH1394_HLEN);
+	skb_reset_mac_header(skb);
+	skb_pull(skb, ETH1394_HLEN);
 	eth = eth1394_hdr(skb);
 
 	if (*eth->h_dest & 1) {
-		if (memcmp(eth->h_dest, dev->broadcast, dev->addr_len)==0)
+		if (memcmp(eth->h_dest, dev->broadcast, dev->addr_len) == 0)
 			skb->pkt_type = PACKET_BROADCAST;
 #if 0
 		else
@@ -848,47 +775,45 @@
 	} else {
 		if (memcmp(eth->h_dest, dev->dev_addr, dev->addr_len))
 			skb->pkt_type = PACKET_OTHERHOST;
-        }
+	}
 
-	if (ntohs (eth->h_proto) >= 1536)
+	if (ntohs(eth->h_proto) >= 1536)
 		return eth->h_proto;
 
 	rawp = skb->data;
 
-        if (*(unsigned short *)rawp == 0xFFFF)
-		return htons (ETH_P_802_3);
+	if (*(unsigned short *)rawp == 0xFFFF)
+		return htons(ETH_P_802_3);
 
-        return htons (ETH_P_802_2);
+	return htons(ETH_P_802_2);
 }
 
 /* Parse an encapsulated IP1394 header into an ethernet frame packet.
  * We also perform ARP translation here, if need be.  */
-static inline u16 ether1394_parse_encap(struct sk_buff *skb,
-					struct net_device *dev,
-					nodeid_t srcid, nodeid_t destid,
-					u16 ether_type)
+static u16 ether1394_parse_encap(struct sk_buff *skb, struct net_device *dev,
+				 nodeid_t srcid, nodeid_t destid,
+				 u16 ether_type)
 {
 	struct eth1394_priv *priv = netdev_priv(dev);
 	u64 dest_hw;
 	unsigned short ret = 0;
 
-	/* Setup our hw addresses. We use these to build the
-	 * ethernet header.  */
+	/* Setup our hw addresses. We use these to build the ethernet header. */
 	if (destid == (LOCAL_BUS | ALL_NODES))
 		dest_hw = ~0ULL;  /* broadcast */
 	else
-		dest_hw = cpu_to_be64((((u64)priv->host->csr.guid_hi) << 32) |
+		dest_hw = cpu_to_be64((u64)priv->host->csr.guid_hi << 32 |
 				      priv->host->csr.guid_lo);
 
 	/* If this is an ARP packet, convert it. First, we want to make
 	 * use of some of the fields, since they tell us a little bit
 	 * about the sending machine.  */
 	if (ether_type == htons(ETH_P_ARP)) {
-		struct eth1394_arp *arp1394 = (struct eth1394_arp*)skb->data;
+		struct eth1394_arp *arp1394 = (struct eth1394_arp *)skb->data;
 		struct arphdr *arp = (struct arphdr *)skb->data;
 		unsigned char *arp_ptr = (unsigned char *)(arp + 1);
 		u64 fifo_addr = (u64)ntohs(arp1394->fifo_hi) << 32 |
-			ntohl(arp1394->fifo_lo);
+					   ntohl(arp1394->fifo_lo);
 		u8 max_rec = min(priv->host->csr.max_rec,
 				 (u8)(arp1394->max_rec));
 		int sspd = arp1394->sspd;
@@ -902,16 +827,17 @@
 		if (sspd > 5 || sspd < 0)
 			sspd = 0;
 
-		maxpayload = min(eth1394_speedto_maxpayload[sspd], (u16)(1 << (max_rec + 1)));
+		maxpayload = min(eth1394_speedto_maxpayload[sspd],
+				 (u16)(1 << (max_rec + 1)));
 
 		guid = get_unaligned(&arp1394->s_uniq_id);
 		node = eth1394_find_node_guid(&priv->ip_node_list,
 					      be64_to_cpu(guid));
-		if (!node) {
+		if (!node)
 			return 0;
-		}
 
-		node_info = (struct eth1394_node_info*)node->ud->device.driver_data;
+		node_info =
+		    (struct eth1394_node_info *)node->ud->device.driver_data;
 
 		/* Update our speed/payload/fifo_offset table */
 		node_info->maxpayload =	maxpayload;
@@ -930,7 +856,7 @@
 
 		arp->ar_hln = 8;
 		arp_ptr += arp->ar_hln;		/* skip over sender unique id */
-		*(u32*)arp_ptr = arp1394->sip;	/* move sender IP addr */
+		*(u32 *)arp_ptr = arp1394->sip;	/* move sender IP addr */
 		arp_ptr += arp->ar_pln;		/* skip over sender IP addr */
 
 		if (arp->ar_op == htons(ARPOP_REQUEST))
@@ -947,65 +873,65 @@
 	return ret;
 }
 
-static inline int fragment_overlap(struct list_head *frag_list, int offset, int len)
+static int fragment_overlap(struct list_head *frag_list, int offset, int len)
 {
 	struct fragment_info *fi;
+	int end = offset + len;
 
-	list_for_each_entry(fi, frag_list, list) {
-		if ( ! ((offset > (fi->offset + fi->len - 1)) ||
-		       ((offset + len - 1) < fi->offset)))
+	list_for_each_entry(fi, frag_list, list)
+		if (offset < fi->offset + fi->len && end > fi->offset)
 			return 1;
-	}
+
 	return 0;
 }
 
-static inline struct list_head *find_partial_datagram(struct list_head *pdgl, int dgl)
+static struct list_head *find_partial_datagram(struct list_head *pdgl, int dgl)
 {
 	struct partial_datagram *pd;
 
-	list_for_each_entry(pd, pdgl, list) {
+	list_for_each_entry(pd, pdgl, list)
 		if (pd->dgl == dgl)
 			return &pd->list;
-	}
+
 	return NULL;
 }
 
 /* Assumes that new fragment does not overlap any existing fragments */
-static inline int new_fragment(struct list_head *frag_info, int offset, int len)
+static int new_fragment(struct list_head *frag_info, int offset, int len)
 {
 	struct list_head *lh;
 	struct fragment_info *fi, *fi2, *new;
 
 	list_for_each(lh, frag_info) {
 		fi = list_entry(lh, struct fragment_info, list);
-		if ((fi->offset + fi->len) == offset) {
+		if (fi->offset + fi->len == offset) {
 			/* The new fragment can be tacked on to the end */
 			fi->len += len;
 			/* Did the new fragment plug a hole? */
 			fi2 = list_entry(lh->next, struct fragment_info, list);
-			if ((fi->offset + fi->len) == fi2->offset) {
+			if (fi->offset + fi->len == fi2->offset) {
 				/* glue fragments together */
 				fi->len += fi2->len;
 				list_del(lh->next);
 				kfree(fi2);
 			}
 			return 0;
-		} else if ((offset + len) == fi->offset) {
+		} else if (offset + len == fi->offset) {
 			/* The new fragment can be tacked on to the beginning */
 			fi->offset = offset;
 			fi->len += len;
 			/* Did the new fragment plug a hole? */
 			fi2 = list_entry(lh->prev, struct fragment_info, list);
-			if ((fi2->offset + fi2->len) == fi->offset) {
+			if (fi2->offset + fi2->len == fi->offset) {
 				/* glue fragments together */
 				fi2->len += fi->len;
 				list_del(lh);
 				kfree(fi);
 			}
 			return 0;
-		} else if (offset > (fi->offset + fi->len)) {
+		} else if (offset > fi->offset + fi->len) {
 			break;
-		} else if ((offset + len) < fi->offset) {
+		} else if (offset + len < fi->offset) {
 			lh = lh->prev;
 			break;
 		}
@@ -1019,14 +945,12 @@
 	new->len = len;
 
 	list_add(&new->list, lh);
-
 	return 0;
 }
 
-static inline int new_partial_datagram(struct net_device *dev,
-				       struct list_head *pdgl, int dgl,
-				       int dg_size, char *frag_buf,
-				       int frag_off, int frag_len)
+static int new_partial_datagram(struct net_device *dev, struct list_head *pdgl,
+				int dgl, int dg_size, char *frag_buf,
+				int frag_off, int frag_len)
 {
 	struct partial_datagram *new;
 
@@ -1059,33 +983,33 @@
 	memcpy(new->pbuf + frag_off, frag_buf, frag_len);
 
 	list_add(&new->list, pdgl);
-
 	return 0;
 }
 
-static inline int update_partial_datagram(struct list_head *pdgl, struct list_head *lh,
-					  char *frag_buf, int frag_off, int frag_len)
+static int update_partial_datagram(struct list_head *pdgl, struct list_head *lh,
+				   char *frag_buf, int frag_off, int frag_len)
 {
-	struct partial_datagram *pd = list_entry(lh, struct partial_datagram, list);
+	struct partial_datagram *pd =
+			list_entry(lh, struct partial_datagram, list);
 
-	if (new_fragment(&pd->frag_info, frag_off, frag_len) < 0) {
+	if (new_fragment(&pd->frag_info, frag_off, frag_len) < 0)
 		return -ENOMEM;
-	}
 
 	memcpy(pd->pbuf + frag_off, frag_buf, frag_len);
 
 	/* Move list entry to beginnig of list so that oldest partial
 	 * datagrams percolate to the end of the list */
 	list_move(lh, pdgl);
-
 	return 0;
 }
 
-static inline int is_datagram_complete(struct list_head *lh, int dg_size)
+static int is_datagram_complete(struct list_head *lh, int dg_size)
 {
-	struct partial_datagram *pd = list_entry(lh, struct partial_datagram, list);
-	struct fragment_info *fi = list_entry(pd->frag_info.next,
-					      struct fragment_info, list);
+	struct partial_datagram *pd;
+	struct fragment_info *fi;
+
+	pd = list_entry(lh, struct partial_datagram, list);
+	fi = list_entry(pd->frag_info.next, struct fragment_info, list);
 
 	return (fi->len == dg_size);
 }
@@ -1108,7 +1032,7 @@
 	if (!ud) {
 		struct eth1394_node_ref *node;
 		node = eth1394_find_node_nodeid(&priv->ip_node_list, srcid);
-		if (!node) {
+		if (unlikely(!node)) {
 			HPSB_PRINT(KERN_ERR, "ether1394 rx: sender nodeid "
 				   "lookup failure: " NODE_BUS_FMT,
 				   NODE_BUS_ARGS(priv->host, srcid));
@@ -1120,7 +1044,7 @@
 		priv->ud_list[NODEID_TO_NODE(srcid)] = ud;
 	}
 
-	node_info = (struct eth1394_node_info*)ud->device.driver_data;
+	node_info = (struct eth1394_node_info *)ud->device.driver_data;
 
 	/* First, did we receive a fragmented or unfragmented datagram? */
 	hdr->words.word1 = ntohs(hdr->words.word1);
@@ -1133,13 +1057,14 @@
 		 * high level network layer. */
 
 		skb = dev_alloc_skb(len + dev->hard_header_len + 15);
-		if (!skb) {
-			HPSB_PRINT (KERN_ERR, "ether1394 rx: low on mem\n");
+		if (unlikely(!skb)) {
+			ETH1394_PRINT_G(KERN_ERR, "Out of memory\n");
 			priv->stats.rx_dropped++;
 			return -1;
 		}
 		skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
-		memcpy(skb_put(skb, len - hdr_len), buf + hdr_len, len - hdr_len);
+		memcpy(skb_put(skb, len - hdr_len), buf + hdr_len,
+		       len - hdr_len);
 		ether_type = hdr->uf.ether_type;
 	} else {
 		/* A datagram fragment has been received, now the fun begins. */
@@ -1224,9 +1149,8 @@
 
 		pd = list_entry(lh, struct partial_datagram, list);
 
-		if (hdr->common.lf == ETH1394_HDR_LF_FF) {
+		if (hdr->common.lf == ETH1394_HDR_LF_FF)
 			pd->ether_type = ether_type;
-		}
 
 		if (is_datagram_complete(lh, dg_size)) {
 			ether_type = pd->ether_type;
@@ -1253,8 +1177,8 @@
 	skb->protocol = ether1394_parse_encap(skb, dev, srcid, destid,
 					      ether_type);
 
-
 	spin_lock_irqsave(&priv->lock, flags);
+
 	if (!skb->protocol) {
 		priv->stats.rx_errors++;
 		priv->stats.rx_dropped++;
@@ -1288,9 +1212,9 @@
 	struct eth1394_host_info *hi;
 
 	hi = hpsb_get_hostinfo(&eth1394_highlevel, host);
-	if (hi == NULL) {
-		ETH1394_PRINT_G(KERN_ERR, "Could not find net device for host %s\n",
-				host->driver->name);
+	if (unlikely(!hi)) {
+		ETH1394_PRINT_G(KERN_ERR, "No net device at fw-host%d\n",
+				host->id);
 		return RCODE_ADDRESS_ERROR;
 	}
 
@@ -1314,9 +1238,9 @@
 	int nready;
 
 	hi = hpsb_get_hostinfo(&eth1394_highlevel, iso->host);
-	if (hi == NULL) {
-		ETH1394_PRINT_G(KERN_ERR, "Could not find net device for host %s\n",
-				iso->host->driver->name);
+	if (unlikely(!hi)) {
+		ETH1394_PRINT_G(KERN_ERR, "No net device at fw-host%d\n",
+				iso->host->id);
 		return;
 	}
 
@@ -1326,20 +1250,20 @@
 	for (i = 0; i < nready; i++) {
 		struct hpsb_iso_packet_info *info =
 			&iso->infos[(iso->first_packet + i) % iso->buf_packets];
-		data = (quadlet_t*) (iso->data_buf.kvirt + info->offset);
+		data = (quadlet_t *)(iso->data_buf.kvirt + info->offset);
 
 		/* skip over GASP header */
 		buf = (char *)data + 8;
 		len = info->len - 8;
 
-		specifier_id = (((be32_to_cpu(data[0]) & 0xffff) << 8) |
-				((be32_to_cpu(data[1]) & 0xff000000) >> 24));
+		specifier_id = (be32_to_cpu(data[0]) & 0xffff) << 8 |
+			       (be32_to_cpu(data[1]) & 0xff000000) >> 24;
 		source_id = be32_to_cpu(data[0]) >> 16;
 
 		priv = netdev_priv(dev);
 
-		if (info->channel != (iso->host->csr.broadcast_channel & 0x3f) ||
-		   specifier_id != ETHER1394_GASP_SPECIFIER_ID) {
+		if (info->channel != (iso->host->csr.broadcast_channel & 0x3f)
+		    || specifier_id != ETHER1394_GASP_SPECIFIER_ID) {
 			/* This packet is not for us */
 			continue;
 		}
@@ -1367,35 +1291,31 @@
  * speed, and unicast FIFO address information between the sender_unique_id
  * and the IP addresses.
  */
-static inline void ether1394_arp_to_1394arp(struct sk_buff *skb,
-					    struct net_device *dev)
+static void ether1394_arp_to_1394arp(struct sk_buff *skb,
+				     struct net_device *dev)
 {
 	struct eth1394_priv *priv = netdev_priv(dev);
-
 	struct arphdr *arp = (struct arphdr *)skb->data;
 	unsigned char *arp_ptr = (unsigned char *)(arp + 1);
 	struct eth1394_arp *arp1394 = (struct eth1394_arp *)skb->data;
 
-	/* Believe it or not, all that need to happen is sender IP get moved
-	 * and set hw_addr_len, max_rec, sspd, fifo_hi and fifo_lo.  */
 	arp1394->hw_addr_len	= 16;
 	arp1394->sip		= *(u32*)(arp_ptr + ETH1394_ALEN);
 	arp1394->max_rec	= priv->host->csr.max_rec;
 	arp1394->sspd		= priv->host->csr.lnk_spd;
-	arp1394->fifo_hi	= htons (priv->local_fifo >> 32);
-	arp1394->fifo_lo	= htonl (priv->local_fifo & ~0x0);
-
-	return;
+	arp1394->fifo_hi	= htons(priv->local_fifo >> 32);
+	arp1394->fifo_lo	= htonl(priv->local_fifo & ~0x0);
 }
 
 /* We need to encapsulate the standard header with our own. We use the
  * ethernet header's proto for our own. */
-static inline unsigned int ether1394_encapsulate_prep(unsigned int max_payload,
-						      __be16 proto,
-						      union eth1394_hdr *hdr,
-						      u16 dg_size, u16 dgl)
+static unsigned int ether1394_encapsulate_prep(unsigned int max_payload,
+					       __be16 proto,
+					       union eth1394_hdr *hdr,
+					       u16 dg_size, u16 dgl)
 {
-	unsigned int adj_max_payload = max_payload - hdr_type_len[ETH1394_HDR_LF_UF];
+	unsigned int adj_max_payload =
+				max_payload - hdr_type_len[ETH1394_HDR_LF_UF];
 
 	/* Does it all fit in one packet? */
 	if (dg_size <= adj_max_payload) {
@@ -1408,19 +1328,19 @@
 		hdr->ff.dgl = dgl;
 		adj_max_payload = max_payload - hdr_type_len[ETH1394_HDR_LF_FF];
 	}
-	return((dg_size + (adj_max_payload - 1)) / adj_max_payload);
+	return (dg_size + adj_max_payload - 1) / adj_max_payload;
 }
 
-static inline unsigned int ether1394_encapsulate(struct sk_buff *skb,
-						 unsigned int max_payload,
-						 union eth1394_hdr *hdr)
+static unsigned int ether1394_encapsulate(struct sk_buff *skb,
+					  unsigned int max_payload,
+					  union eth1394_hdr *hdr)
 {
 	union eth1394_hdr *bufhdr;
 	int ftype = hdr->common.lf;
 	int hdrsz = hdr_type_len[ftype];
 	unsigned int adj_max_payload = max_payload - hdrsz;
 
-	switch(ftype) {
+	switch (ftype) {
 	case ETH1394_HDR_LF_UF:
 		bufhdr = (union eth1394_hdr *)skb_push(skb, hdrsz);
 		bufhdr->words.word1 = htons(hdr->words.word1);
@@ -1449,11 +1369,10 @@
 		bufhdr->words.word3 = htons(hdr->words.word3);
 		bufhdr->words.word4 = 0;
 	}
-
 	return min(max_payload, skb->len);
 }
 
-static inline struct hpsb_packet *ether1394_alloc_common_packet(struct hpsb_host *host)
+static struct hpsb_packet *ether1394_alloc_common_packet(struct hpsb_host *host)
 {
 	struct hpsb_packet *p;
 
@@ -1466,61 +1385,57 @@
 	return p;
 }
 
-static inline int ether1394_prep_write_packet(struct hpsb_packet *p,
-					      struct hpsb_host *host,
-					      nodeid_t node, u64 addr,
-					      void * data, int tx_len)
+static int ether1394_prep_write_packet(struct hpsb_packet *p,
+				       struct hpsb_host *host, nodeid_t node,
+				       u64 addr, void *data, int tx_len)
 {
 	p->node_id = node;
 	p->data = NULL;
 
 	p->tcode = TCODE_WRITEB;
-	p->header[1] = (host->node_id << 16) | (addr >> 32);
+	p->header[1] = host->node_id << 16 | addr >> 32;
 	p->header[2] = addr & 0xffffffff;
 
 	p->header_size = 16;
 	p->expect_response = 1;
 
 	if (hpsb_get_tlabel(p)) {
-		ETH1394_PRINT_G(KERN_ERR, "No more tlabels left while sending "
-				"to node " NODE_BUS_FMT "\n", NODE_BUS_ARGS(host, node));
+		ETH1394_PRINT_G(KERN_ERR, "Out of tlabels\n");
 		return -1;
 	}
-	p->header[0] = (p->node_id << 16) | (p->tlabel << 10)
-		| (1 << 8) | (TCODE_WRITEB << 4);
+	p->header[0] =
+		p->node_id << 16 | p->tlabel << 10 | 1 << 8 | TCODE_WRITEB << 4;
 
 	p->header[3] = tx_len << 16;
 	p->data_size = (tx_len + 3) & ~3;
-	p->data = (quadlet_t*)data;
+	p->data = data;
 
 	return 0;
 }
 
-static inline void ether1394_prep_gasp_packet(struct hpsb_packet *p,
-					      struct eth1394_priv *priv,
-					      struct sk_buff *skb, int length)
+static void ether1394_prep_gasp_packet(struct hpsb_packet *p,
+				       struct eth1394_priv *priv,
+				       struct sk_buff *skb, int length)
 {
 	p->header_size = 4;
 	p->tcode = TCODE_STREAM_DATA;
 
-	p->header[0] = (length << 16) | (3 << 14)
-		| ((priv->broadcast_channel) << 8)
-		| (TCODE_STREAM_DATA << 4);
+	p->header[0] = length << 16 | 3 << 14 | priv->broadcast_channel << 8 |
+		       TCODE_STREAM_DATA << 4;
 	p->data_size = length;
-	p->data = ((quadlet_t*)skb->data) - 2;
-	p->data[0] = cpu_to_be32((priv->host->node_id << 16) |
+	p->data = (quadlet_t *)skb->data - 2;
+	p->data[0] = cpu_to_be32(priv->host->node_id << 16 |
 				 ETHER1394_GASP_SPECIFIER_ID_HI);
-	p->data[1] = cpu_to_be32((ETHER1394_GASP_SPECIFIER_ID_LO << 24) |
+	p->data[1] = cpu_to_be32(ETHER1394_GASP_SPECIFIER_ID_LO << 24 |
 				 ETHER1394_GASP_VERSION);
 
-	/* Setting the node id to ALL_NODES (not LOCAL_BUS | ALL_NODES)
-	 * prevents hpsb_send_packet() from setting the speed to an arbitrary
-	 * value based on packet->node_id if packet->node_id is not set. */
-	p->node_id = ALL_NODES;
 	p->speed_code = priv->bc_sspd;
+
+	/* prevent hpsb_send_packet() from overriding our speed code */
+	p->node_id = LOCAL_BUS | ALL_NODES;
 }
 
-static inline void ether1394_free_packet(struct hpsb_packet *packet)
+static void ether1394_free_packet(struct hpsb_packet *packet)
 {
 	if (packet->tcode != TCODE_STREAM_DATA)
 		hpsb_free_tlabel(packet);
@@ -1539,7 +1454,7 @@
 		return -1;
 
 	if (ptask->tx_type == ETH1394_GASP) {
-		int length = tx_len + (2 * sizeof(quadlet_t));
+		int length = tx_len + 2 * sizeof(quadlet_t);
 
 		ether1394_prep_gasp_packet(packet, priv, ptask->skb, length);
 	} else if (ether1394_prep_write_packet(packet, priv->host,
@@ -1562,13 +1477,11 @@
 	return 0;
 }
 
-
 /* Task function to be run when a datagram transmission is completed */
-static inline void ether1394_dg_complete(struct packet_task *ptask, int fail)
+static void ether1394_dg_complete(struct packet_task *ptask, int fail)
 {
 	struct sk_buff *skb = ptask->skb;
-	struct net_device *dev = skb->dev;
-	struct eth1394_priv *priv = netdev_priv(dev);
+	struct eth1394_priv *priv = netdev_priv(skb->dev);
 	unsigned long flags;
 
 	/* Statistics */
@@ -1586,7 +1499,6 @@
 	kmem_cache_free(packet_task_cache, ptask);
 }
 
-
 /* Callback for when a packet has been sent and the status of that packet is
  * known */
 static void ether1394_complete_cb(void *__ptask)
@@ -1614,19 +1526,15 @@
 	}
 }
 
-
-
 /* Transmit a packet (called by kernel) */
-static int ether1394_tx (struct sk_buff *skb, struct net_device *dev)
+static int ether1394_tx(struct sk_buff *skb, struct net_device *dev)
 {
-	gfp_t kmflags = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
 	struct eth1394hdr *eth;
 	struct eth1394_priv *priv = netdev_priv(dev);
 	__be16 proto;
 	unsigned long flags;
 	nodeid_t dest_node;
 	eth1394_tx_type tx_type;
-	int ret = 0;
 	unsigned int tx_len;
 	unsigned int max_payload;
 	u16 dg_size;
@@ -1635,29 +1543,24 @@
 	struct eth1394_node_ref *node;
 	struct eth1394_node_info *node_info = NULL;
 
-	ptask = kmem_cache_alloc(packet_task_cache, kmflags);
-	if (ptask == NULL) {
-		ret = -ENOMEM;
+	ptask = kmem_cache_alloc(packet_task_cache, GFP_ATOMIC);
+	if (ptask == NULL)
 		goto fail;
-	}
 
 	/* XXX Ignore this for now. Noticed that when MacOSX is the IRM,
 	 * it does not set our validity bit. We need to compensate for
 	 * that somewhere else, but not in eth1394. */
 #if 0
-	if ((priv->host->csr.broadcast_channel & 0xc0000000) != 0xc0000000) {
-		ret = -EAGAIN;
+	if ((priv->host->csr.broadcast_channel & 0xc0000000) != 0xc0000000)
 		goto fail;
-	}
 #endif
 
-	if ((skb = skb_share_check (skb, kmflags)) == NULL) {
-		ret = -ENOMEM;
+	skb = skb_share_check(skb, GFP_ATOMIC);
+	if (!skb)
 		goto fail;
-	}
 
 	/* Get rid of the fake eth1394 header, but save a pointer */
-	eth = (struct eth1394hdr*)skb->data;
+	eth = (struct eth1394hdr *)skb->data;
 	skb_pull(skb, ETH1394_HLEN);
 
 	proto = eth->h_proto;
@@ -1668,11 +1571,11 @@
 	if (memcmp(eth->h_dest, dev->broadcast, ETH1394_ALEN) == 0 ||
 	    proto == htons(ETH_P_ARP) ||
 	    (proto == htons(ETH_P_IP) &&
-	     IN_MULTICAST(ntohl(skb->nh.iph->daddr)))) {
+	     IN_MULTICAST(ntohl(ip_hdr(skb)->daddr)))) {
 		tx_type = ETH1394_GASP;
 		dest_node = LOCAL_BUS | ALL_NODES;
 		max_payload = priv->bc_maxpayload - ETHER1394_GASP_OVERHEAD;
-		BUG_ON(max_payload < (512 - ETHER1394_GASP_OVERHEAD));
+		BUG_ON(max_payload < 512 - ETHER1394_GASP_OVERHEAD);
 		dgl = priv->bc_dgl;
 		if (max_payload < dg_size + hdr_type_len[ETH1394_HDR_LF_UF])
 			priv->bc_dgl++;
@@ -1681,19 +1584,17 @@
 
 		node = eth1394_find_node_guid(&priv->ip_node_list,
 					      be64_to_cpu(guid));
-		if (!node) {
-			ret = -EAGAIN;
+		if (!node)
 			goto fail;
-		}
-		node_info = (struct eth1394_node_info*)node->ud->device.driver_data;
-		if (node_info->fifo == CSR1212_INVALID_ADDR_SPACE) {
-			ret = -EAGAIN;
+
+		node_info =
+		    (struct eth1394_node_info *)node->ud->device.driver_data;
+		if (node_info->fifo == CSR1212_INVALID_ADDR_SPACE)
 			goto fail;
-		}
 
 		dest_node = node->ud->ne->nodeid;
 		max_payload = node_info->maxpayload;
-		BUG_ON(max_payload < (512 - ETHER1394_GASP_OVERHEAD));
+		BUG_ON(max_payload < 512 - ETHER1394_GASP_OVERHEAD);
 
 		dgl = node_info->dgl;
 		if (max_payload < dg_size + hdr_type_len[ETH1394_HDR_LF_UF])
@@ -1703,7 +1604,7 @@
 
 	/* If this is an ARP packet, convert it */
 	if (proto == htons(ETH_P_ARP))
-		ether1394_arp_to_1394arp (skb, dev);
+		ether1394_arp_to_1394arp(skb, dev);
 
 	ptask->hdr.words.word1 = 0;
 	ptask->hdr.words.word2 = 0;
@@ -1726,9 +1627,8 @@
 
 	ptask->tx_type = tx_type;
 	ptask->max_payload = max_payload;
-        ptask->outstanding_pkts = ether1394_encapsulate_prep(max_payload, proto,
-							     &ptask->hdr, dg_size,
-							     dgl);
+	ptask->outstanding_pkts = ether1394_encapsulate_prep(max_payload,
+					proto, &ptask->hdr, dg_size, dgl);
 
 	/* Add the encapsulation header to the fragment */
 	tx_len = ether1394_encapsulate(skb, max_payload, &ptask->hdr);
@@ -1737,7 +1637,7 @@
 		goto fail;
 
 	netif_wake_queue(dev);
-	return 0;
+	return NETDEV_TX_OK;
 fail:
 	if (ptask)
 		kmem_cache_free(packet_task_cache, ptask);
@@ -1745,40 +1645,56 @@
 	if (skb != NULL)
 		dev_kfree_skb(skb);
 
-	spin_lock_irqsave (&priv->lock, flags);
+	spin_lock_irqsave(&priv->lock, flags);
 	priv->stats.tx_dropped++;
 	priv->stats.tx_errors++;
-	spin_unlock_irqrestore (&priv->lock, flags);
+	spin_unlock_irqrestore(&priv->lock, flags);
 
 	if (netif_queue_stopped(dev))
 		netif_wake_queue(dev);
 
-	return 0;  /* returning non-zero causes serious problems */
+	/*
+	 * FIXME: According to a patch from 2003-02-26, "returning non-zero
+	 * causes serious problems" here, allegedly.  Before that patch,
+	 * -ERRNO was returned which is not appropriate under Linux 2.6.
+	 * Perhaps more needs to be done?  Stop the queue in serious
+	 * conditions and restart it elsewhere?
+	 */
+	/* return NETDEV_TX_BUSY; */
+	return NETDEV_TX_OK;
 }
 
-static void ether1394_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+static void ether1394_get_drvinfo(struct net_device *dev,
+				  struct ethtool_drvinfo *info)
 {
-	strcpy (info->driver, driver_name);
-	/* FIXME XXX provide sane businfo */
-	strcpy (info->bus_info, "ieee1394");
+	strcpy(info->driver, driver_name);
+	strcpy(info->bus_info, "ieee1394"); /* FIXME provide more detail? */
 }
 
 static struct ethtool_ops ethtool_ops = {
 	.get_drvinfo = ether1394_get_drvinfo
 };
 
-static int __init ether1394_init_module (void)
+static int __init ether1394_init_module(void)
 {
-	packet_task_cache = kmem_cache_create("packet_task", sizeof(struct packet_task),
+	int err;
+
+	packet_task_cache = kmem_cache_create("packet_task",
+					      sizeof(struct packet_task),
 					      0, 0, NULL, NULL);
+	if (!packet_task_cache)
+		return -ENOMEM;
 
-	/* Register ourselves as a highlevel driver */
 	hpsb_register_highlevel(&eth1394_highlevel);
-
-	return hpsb_register_protocol(&eth1394_proto_driver);
+	err = hpsb_register_protocol(&eth1394_proto_driver);
+	if (err) {
+		hpsb_unregister_highlevel(&eth1394_highlevel);
+		kmem_cache_destroy(packet_task_cache);
+	}
+	return err;
 }
 
-static void __exit ether1394_exit_module (void)
+static void __exit ether1394_exit_module(void)
 {
 	hpsb_unregister_protocol(&eth1394_proto_driver);
 	hpsb_unregister_highlevel(&eth1394_highlevel);
diff --git a/drivers/ieee1394/eth1394.h b/drivers/ieee1394/eth1394.h
index c45cbff..a3439ee 100644
--- a/drivers/ieee1394/eth1394.h
+++ b/drivers/ieee1394/eth1394.h
@@ -25,8 +25,11 @@
 #define __ETH1394_H
 
 #include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <asm/byteorder.h>
 
 #include "ieee1394.h"
+#include "ieee1394_types.h"
 
 /* Register for incoming packets. This is 4096 bytes, which supports up to
  * S3200 (per Table 16-3 of IEEE 1394b-2002). */
@@ -34,22 +37,15 @@
 
 /* GASP identifier numbers for IPv4 over IEEE 1394 */
 #define ETHER1394_GASP_SPECIFIER_ID	0x00005E
-#define ETHER1394_GASP_SPECIFIER_ID_HI	((ETHER1394_GASP_SPECIFIER_ID >> 8) & 0xffff)
-#define ETHER1394_GASP_SPECIFIER_ID_LO	(ETHER1394_GASP_SPECIFIER_ID & 0xff)
+#define ETHER1394_GASP_SPECIFIER_ID_HI	((0x00005E >> 8) & 0xffff)
+#define ETHER1394_GASP_SPECIFIER_ID_LO	(0x00005E & 0xff)
 #define ETHER1394_GASP_VERSION		1
 
-#define ETHER1394_GASP_OVERHEAD (2 * sizeof(quadlet_t))  /* GASP header overhead */
+#define ETHER1394_GASP_OVERHEAD	(2 * sizeof(quadlet_t))	/* for GASP header */
 
-#define ETHER1394_GASP_BUFFERS 16
+#define ETHER1394_GASP_BUFFERS	16
 
-/* rawiso buffer size - due to a limitation in rawiso, we must limit each
- * GASP buffer to be less than PAGE_SIZE. */
-#define ETHER1394_ISO_BUF_SIZE	ETHER1394_GASP_BUFFERS *                        \
-				   min((unsigned int)PAGE_SIZE,                 \
-				       2 * (1U << (priv->host->csr.max_rec + 1)))
-
-/* Node set == 64 */
-#define NODE_SET			(ALL_NODES + 1)
+#define NODE_SET		(ALL_NODES + 1)		/* Node set == 64 */
 
 enum eth1394_bc_states { ETHER1394_BC_ERROR,
 			 ETHER1394_BC_RUNNING,
@@ -85,19 +81,14 @@
 	unsigned short	h_proto;		/* packet type ID field	*/
 }  __attribute__((packed));
 
-#ifdef __KERNEL__
-#include <linux/skbuff.h>
-
 static inline struct eth1394hdr *eth1394_hdr(const struct sk_buff *skb)
 {
-	return (struct eth1394hdr *)skb->mac.raw;
+	return (struct eth1394hdr *)skb_mac_header(skb);
 }
-#endif
 
 typedef enum {ETH1394_GASP, ETH1394_WRREQ} eth1394_tx_type;
 
 /* IP1394 headers */
-#include <asm/byteorder.h>
 
 /* Unfragmented */
 #if defined __BIG_ENDIAN_BITFIELD
diff --git a/drivers/ieee1394/highlevel.c b/drivers/ieee1394/highlevel.c
index 694da82..83a49331 100644
--- a/drivers/ieee1394/highlevel.c
+++ b/drivers/ieee1394/highlevel.c
@@ -70,8 +70,12 @@
 	return NULL;
 }
 
-/* Returns a per host/driver data structure that was previously stored by
- * hpsb_create_hostinfo. */
+/**
+ * hpsb_get_hostinfo - retrieve a hostinfo pointer bound to this driver/host
+ *
+ * Returns a per @host and @hl driver data structure that was previously stored
+ * by hpsb_create_hostinfo.
+ */
 void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host)
 {
 	struct hl_host_info *hi = hl_get_hostinfo(hl, host);
@@ -79,7 +83,13 @@
 	return hi ? hi->data : NULL;
 }
 
-/* If size is zero, then the return here is only valid for error checking */
+/**
+ * hpsb_create_hostinfo - allocate a hostinfo pointer bound to this driver/host
+ *
+ * Allocate a hostinfo pointer backed by memory with @data_size and bind it to
+ * to this @hl driver and @host.  If @data_size is zero, then the return here is
+ * only valid for error checking.
+ */
 void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
 			   size_t data_size)
 {
@@ -113,6 +123,11 @@
 	return data;
 }
 
+/**
+ * hpsb_set_hostinfo - set the hostinfo pointer to something useful
+ *
+ * Usually follows a call to hpsb_create_hostinfo, where the size is 0.
+ */
 int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
 		      void *data)
 {
@@ -132,6 +147,11 @@
 	return -EINVAL;
 }
 
+/**
+ * hpsb_destroy_hostinfo - free and remove a hostinfo pointer
+ *
+ * Free and remove the hostinfo pointer bound to this @hl driver and @host.
+ */
 void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host)
 {
 	struct hl_host_info *hi;
@@ -147,6 +167,12 @@
 	return;
 }
 
+/**
+ * hpsb_set_hostinfo_key - set an alternate lookup key for an hostinfo
+ *
+ * Sets an alternate lookup key for the hostinfo bound to this @hl driver and
+ * @host.
+ */
 void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host,
 			   unsigned long key)
 {
@@ -158,6 +184,9 @@
 	return;
 }
 
+/**
+ * hpsb_get_hostinfo_bykey - retrieve a hostinfo pointer by its alternate key
+ */
 void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key)
 {
 	struct hl_host_info *hi;
@@ -189,6 +218,12 @@
 	return 0;
 }
 
+/**
+ * hpsb_register_highlevel - register highlevel driver
+ *
+ * The name pointer in @hl has to stay valid at all times because the string is
+ * not copied.
+ */
 void hpsb_register_highlevel(struct hpsb_highlevel *hl)
 {
 	unsigned long flags;
@@ -258,6 +293,9 @@
 	return 0;
 }
 
+/**
+ * hpsb_unregister_highlevel - unregister highlevel driver
+ */
 void hpsb_unregister_highlevel(struct hpsb_highlevel *hl)
 {
 	unsigned long flags;
@@ -273,6 +311,19 @@
 	nodemgr_for_each_host(hl, highlevel_for_each_host_unreg);
 }
 
+/**
+ * hpsb_allocate_and_register_addrspace - alloc' and reg' a host address space
+ *
+ * @start and @end are 48 bit pointers and have to be quadlet aligned.
+ * @end points to the first address behind the handled addresses.  This
+ * function can be called multiple times for a single hpsb_highlevel @hl to
+ * implement sparse register sets.  The requested region must not overlap any
+ * previously allocated region, otherwise registering will fail.
+ *
+ * It returns true for successful allocation.  Address spaces can be
+ * unregistered with hpsb_unregister_addrspace.  All remaining address spaces
+ * are automatically deallocated together with the hpsb_highlevel @hl.
+ */
 u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl,
 					 struct hpsb_host *host,
 					 struct hpsb_address_ops *ops,
@@ -348,6 +399,19 @@
 	return retval;
 }
 
+/**
+ * hpsb_register_addrspace - register a host address space
+ *
+ * @start and @end are 48 bit pointers and have to be quadlet aligned.
+ * @end points to the first address behind the handled addresses.  This
+ * function can be called multiple times for a single hpsb_highlevel @hl to
+ * implement sparse register sets.  The requested region must not overlap any
+ * previously allocated region, otherwise registering will fail.
+ *
+ * It returns true for successful allocation.  Address spaces can be
+ * unregistered with hpsb_unregister_addrspace.  All remaining address spaces
+ * are automatically deallocated together with the hpsb_highlevel @hl.
+ */
 int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
 			    struct hpsb_address_ops *ops, u64 start, u64 end)
 {
@@ -419,6 +483,11 @@
 	return retval;
 }
 
+/**
+ * hpsb_listen_channel - enable receving a certain isochronous channel
+ *
+ * Reception is handled through the @hl's iso_receive op.
+ */
 int hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
 			unsigned int channel)
 {
@@ -431,6 +500,9 @@
 	return 0;
 }
 
+/**
+ * hpsb_unlisten_channel - disable receving a certain isochronous channel
+ */
 void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
 			   unsigned int channel)
 {
@@ -528,6 +600,17 @@
 	read_unlock_irqrestore(&hl_irqs_lock, flags);
 }
 
+/*
+ * highlevel_read, highlevel_write, highlevel_lock, highlevel_lock64:
+ *
+ * These functions are called to handle transactions. They are called when a
+ * packet arrives.  The flags argument contains the second word of the first
+ * header quadlet of the incoming packet (containing transaction label, retry
+ * code, transaction code and priority).  These functions either return a
+ * response code or a negative number.  In the first case a response will be
+ * generated.  In the latter case, no response will be sent and the driver which
+ * handled the request will send the response itself.
+ */
 int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr,
 		   unsigned int length, u16 flags)
 {
diff --git a/drivers/ieee1394/highlevel.h b/drivers/ieee1394/highlevel.h
index 4b33011..63474f7 100644
--- a/drivers/ieee1394/highlevel.h
+++ b/drivers/ieee1394/highlevel.h
@@ -99,16 +99,6 @@
 void highlevel_add_host(struct hpsb_host *host);
 void highlevel_remove_host(struct hpsb_host *host);
 void highlevel_host_reset(struct hpsb_host *host);
-
-/*
- * These functions are called to handle transactions. They are called when a
- * packet arrives.  The flags argument contains the second word of the first
- * header quadlet of the incoming packet (containing transaction label, retry
- * code, transaction code and priority).  These functions either return a
- * response code or a negative number.  In the first case a response will be
- * generated.  In the latter case, no response will be sent and the driver which
- * handled the request will send the response itself.
- */
 int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr,
 		   unsigned int length, u16 flags);
 int highlevel_write(struct hpsb_host *host, int nodeid, int destid, void *data,
@@ -119,30 +109,13 @@
 int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store,
 		     u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
 		     u16 flags);
-
 void highlevel_iso_receive(struct hpsb_host *host, void *data, size_t length);
 void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction,
 			   void *data, size_t length);
 
-/*
- * Register highlevel driver.  The name pointer has to stay valid at all times
- * because the string is not copied.
- */
 void hpsb_register_highlevel(struct hpsb_highlevel *hl);
 void hpsb_unregister_highlevel(struct hpsb_highlevel *hl);
 
-/*
- * Register handlers for host address spaces.  Start and end are 48 bit pointers
- * and have to be quadlet aligned.  Argument "end" points to the first address
- * behind the handled addresses.  This function can be called multiple times for
- * a single hpsb_highlevel to implement sparse register sets.  The requested
- * region must not overlap any previously allocated region, otherwise
- * registering will fail.
- *
- * It returns true for successful allocation.  Address spaces can be
- * unregistered with hpsb_unregister_addrspace.  All remaining address spaces
- * are automatically deallocated together with the hpsb_highlevel.
- */
 u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl,
 					 struct hpsb_host *host,
 					 struct hpsb_address_ops *ops,
@@ -152,45 +125,19 @@
 			    struct hpsb_address_ops *ops, u64 start, u64 end);
 int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
 			      u64 start);
-
-/*
- * Enable or disable receving a certain isochronous channel through the
- * iso_receive op.
- */
 int hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
-			 unsigned int channel);
+			unsigned int channel);
 void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
 			   unsigned int channel);
 
-/* Retrieve a hostinfo pointer bound to this driver/host */
 void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host);
-
-/* Allocate a hostinfo pointer of data_size bound to this driver/host */
 void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
 			   size_t data_size);
-
-/* Free and remove the hostinfo pointer bound to this driver/host */
 void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host);
-
-/* Set an alternate lookup key for the hostinfo bound to this driver/host */
 void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host,
 			   unsigned long key);
-
-/* Retrieve the alternate lookup key for the hostinfo bound to this
- * driver/host */
-unsigned long hpsb_get_hostinfo_key(struct hpsb_highlevel *hl,
-				    struct hpsb_host *host);
-
-/* Retrieve a hostinfo pointer bound to this driver using its alternate key */
 void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key);
-
-/* Set the hostinfo pointer to something useful. Usually follows a call to
- * hpsb_create_hostinfo, where the size is 0. */
 int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
 		      void *data);
 
-/* Retrieve hpsb_host using a highlevel handle and a key */
-struct hpsb_host *hpsb_get_host_bykey(struct hpsb_highlevel *hl,
-				      unsigned long key);
-
 #endif /* IEEE1394_HIGHLEVEL_H */
diff --git a/drivers/ieee1394/hosts.c b/drivers/ieee1394/hosts.c
index 32a1309..6164a9a 100644
--- a/drivers/ieee1394/hosts.c
+++ b/drivers/ieee1394/hosts.c
@@ -94,14 +94,6 @@
 	return 0;
 }
 
-/*
- * The pending_packet_queue is special in that it's processed
- * from hardirq context too (such as hpsb_bus_reset()). Hence
- * split the lock class from the usual networking skb-head
- * lock class by using a separate key for it:
- */
-static struct lock_class_key pending_packet_queue_key;
-
 static DEFINE_MUTEX(host_num_alloc);
 
 /**
@@ -137,9 +129,7 @@
 	h->hostdata = h + 1;
 	h->driver = drv;
 
-	skb_queue_head_init(&h->pending_packet_queue);
-	lockdep_set_class(&h->pending_packet_queue.lock,
-			   &pending_packet_queue_key);
+	INIT_LIST_HEAD(&h->pending_packets);
 	INIT_LIST_HEAD(&h->addr_space);
 
 	for (i = 2; i < 16; i++)
@@ -190,7 +180,7 @@
 {
 	if (hpsb_default_host_entry(host))
 		return -ENOMEM;
-	hpsb_add_extra_config_roms(host);
+
 	highlevel_add_host(host);
 	return 0;
 }
@@ -212,12 +202,19 @@
 
 	host->driver = &dummy_driver;
 	highlevel_remove_host(host);
-	hpsb_remove_extra_config_roms(host);
 
 	class_device_unregister(&host->class_dev);
 	device_unregister(&host->device);
 }
 
+/**
+ * hpsb_update_config_rom_image - updates configuration ROM image of a host
+ *
+ * Updates the configuration ROM image of a host.  rom_version must be the
+ * current version, otherwise it will fail with return value -1. If this
+ * host does not support config-rom-update, it will return -%EINVAL.
+ * Return value 0 indicates success.
+ */
 int hpsb_update_config_rom_image(struct hpsb_host *host)
 {
 	unsigned long reset_delay;
diff --git a/drivers/ieee1394/hosts.h b/drivers/ieee1394/hosts.h
index 4bf4fb7..feb55d0 100644
--- a/drivers/ieee1394/hosts.h
+++ b/drivers/ieee1394/hosts.h
@@ -3,7 +3,6 @@
 
 #include <linux/device.h>
 #include <linux/list.h>
-#include <linux/skbuff.h>
 #include <linux/timer.h>
 #include <linux/types.h>
 #include <linux/workqueue.h>
@@ -25,8 +24,7 @@
 
 	atomic_t generation;
 
-	struct sk_buff_head pending_packet_queue;
-
+	struct list_head pending_packets;
 	struct timer_list timeout;
 	unsigned long timeout_interval;
 
@@ -202,12 +200,6 @@
 int hpsb_add_host(struct hpsb_host *host);
 void hpsb_resume_host(struct hpsb_host *host);
 void hpsb_remove_host(struct hpsb_host *host);
-
-/* Updates the configuration rom image of a host.  rom_version must be the
- * current version, otherwise it will fail with return value -1. If this
- * host does not support config-rom-update, it will return -EINVAL.
- * Return value 0 indicates success.
- */
 int hpsb_update_config_rom_image(struct hpsb_host *host);
 
 #endif /* _IEEE1394_HOSTS_H */
diff --git a/drivers/ieee1394/ieee1394_core.c b/drivers/ieee1394/ieee1394_core.c
index d791d08..8f71b6a 100644
--- a/drivers/ieee1394/ieee1394_core.c
+++ b/drivers/ieee1394/ieee1394_core.c
@@ -30,7 +30,6 @@
 #include <linux/moduleparam.h>
 #include <linux/bitops.h>
 #include <linux/kdev_t.h>
-#include <linux/skbuff.h>
 #include <linux/suspend.h>
 #include <linux/kthread.h>
 #include <linux/preempt.h>
@@ -96,13 +95,15 @@
 
 
 /**
- * hpsb_set_packet_complete_task - set the task that runs when a packet
- * completes. You cannot call this more than once on a single packet
- * before it is sent.
- *
+ * hpsb_set_packet_complete_task - set task that runs when a packet completes
  * @packet: the packet whose completion we want the task added to
  * @routine: function to call
  * @data: data (if any) to pass to the above function
+ *
+ * Set the task that runs when a packet completes. You cannot call this more
+ * than once on a single packet before it is sent.
+ *
+ * Typically, the complete @routine is responsible to call hpsb_free_packet().
  */
 void hpsb_set_packet_complete_task(struct hpsb_packet *packet,
 				   void (*routine)(void *), void *data)
@@ -115,12 +116,12 @@
 
 /**
  * hpsb_alloc_packet - allocate new packet structure
- * @data_size: size of the data block to be allocated
+ * @data_size: size of the data block to be allocated, in bytes
  *
  * This function allocates, initializes and returns a new &struct hpsb_packet.
- * It can be used in interrupt context.  A header block is always included, its
- * size is big enough to contain all possible 1394 headers.  The data block is
- * only allocated when @data_size is not zero.
+ * It can be used in interrupt context.  A header block is always included and
+ * initialized with zeros.  Its size is big enough to contain all possible 1394
+ * headers.  The data block is only allocated if @data_size is not zero.
  *
  * For packets for which responses will be received the @data_size has to be big
  * enough to contain the response's data block since no further allocation
@@ -135,50 +136,49 @@
  */
 struct hpsb_packet *hpsb_alloc_packet(size_t data_size)
 {
-	struct hpsb_packet *packet = NULL;
-	struct sk_buff *skb;
+	struct hpsb_packet *packet;
 
 	data_size = ((data_size + 3) & ~3);
 
-	skb = alloc_skb(data_size + sizeof(*packet), GFP_ATOMIC);
-	if (skb == NULL)
+	packet = kzalloc(sizeof(*packet) + data_size, GFP_ATOMIC);
+	if (!packet)
 		return NULL;
 
-	memset(skb->data, 0, data_size + sizeof(*packet));
-
-	packet = (struct hpsb_packet *)skb->data;
-	packet->skb = skb;
-
-	packet->header = packet->embedded_header;
 	packet->state = hpsb_unused;
 	packet->generation = -1;
 	INIT_LIST_HEAD(&packet->driver_list);
+	INIT_LIST_HEAD(&packet->queue);
 	atomic_set(&packet->refcnt, 1);
 
 	if (data_size) {
-		packet->data = (quadlet_t *)(skb->data + sizeof(*packet));
-		packet->data_size = data_size;
+		packet->data = packet->embedded_data;
+		packet->allocated_data_size = data_size;
 	}
-
 	return packet;
 }
 
-
 /**
  * hpsb_free_packet - free packet and data associated with it
  * @packet: packet to free (is NULL safe)
  *
- * This function will free packet->data and finally the packet itself.
+ * Frees @packet->data only if it was allocated through hpsb_alloc_packet().
  */
 void hpsb_free_packet(struct hpsb_packet *packet)
 {
 	if (packet && atomic_dec_and_test(&packet->refcnt)) {
-		BUG_ON(!list_empty(&packet->driver_list));
-		kfree_skb(packet->skb);
+		BUG_ON(!list_empty(&packet->driver_list) ||
+		       !list_empty(&packet->queue));
+		kfree(packet);
 	}
 }
 
-
+/**
+ * hpsb_reset_bus - initiate bus reset on the given host
+ * @host: host controller whose bus to reset
+ * @type: one of enum reset_types
+ *
+ * Returns 1 if bus reset already in progress, 0 otherwise.
+ */
 int hpsb_reset_bus(struct hpsb_host *host, int type)
 {
 	if (!host->in_bus_reset) {
@@ -229,6 +229,14 @@
 	return 0;
 }
 
+/**
+ * hpsb_bus_reset - notify a bus reset to the core
+ *
+ * For host driver module usage.  Safe to use in interrupt context, although
+ * quite complex; so you may want to run it in the bottom rather than top half.
+ *
+ * Returns 1 if bus reset already in progress, 0 otherwise.
+ */
 int hpsb_bus_reset(struct hpsb_host *host)
 {
 	if (host->in_bus_reset) {
@@ -405,6 +413,14 @@
 }
 
 
+/**
+ * hpsb_selfid_received - hand over received selfid packet to the core
+ *
+ * For host driver module usage.  Safe to use in interrupt context.
+ *
+ * The host driver should have done a successful complement check (second
+ * quadlet is complement of first) beforehand.
+ */
 void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid)
 {
 	if (host->in_bus_reset) {
@@ -416,6 +432,15 @@
 	}
 }
 
+/**
+ * hpsb_selfid_complete - notify completion of SelfID stage to the core
+ *
+ * For host driver module usage.  Safe to use in interrupt context, although
+ * quite complex; so you may want to run it in the bottom rather than top half.
+ *
+ * Notify completion of SelfID stage to the core and report new physical ID
+ * and whether host is root now.
+ */
 void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot)
 {
 	if (!host->in_bus_reset)
@@ -462,30 +487,41 @@
 	highlevel_host_reset(host);
 }
 
+static spinlock_t pending_packets_lock = SPIN_LOCK_UNLOCKED;
 
+/**
+ * hpsb_packet_sent - notify core of sending a packet
+ *
+ * For host driver module usage.  Safe to call from within a transmit packet
+ * routine.
+ *
+ * Notify core of sending a packet.  Ackcode is the ack code returned for async
+ * transmits or ACKX_SEND_ERROR if the transmission failed completely; ACKX_NONE
+ * for other cases (internal errors that don't justify a panic).
+ */
 void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet,
 		      int ackcode)
 {
 	unsigned long flags;
 
-	spin_lock_irqsave(&host->pending_packet_queue.lock, flags);
+	spin_lock_irqsave(&pending_packets_lock, flags);
 
 	packet->ack_code = ackcode;
 
 	if (packet->no_waiter || packet->state == hpsb_complete) {
 		/* if packet->no_waiter, must not have a tlabel allocated */
-		spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags);
+		spin_unlock_irqrestore(&pending_packets_lock, flags);
 		hpsb_free_packet(packet);
 		return;
 	}
 
 	atomic_dec(&packet->refcnt);	/* drop HC's reference */
-	/* here the packet must be on the host->pending_packet_queue */
+	/* here the packet must be on the host->pending_packets queue */
 
 	if (ackcode != ACK_PENDING || !packet->expect_response) {
 		packet->state = hpsb_complete;
-		__skb_unlink(packet->skb, &host->pending_packet_queue);
-		spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags);
+		list_del_init(&packet->queue);
+		spin_unlock_irqrestore(&pending_packets_lock, flags);
 		queue_packet_complete(packet);
 		return;
 	}
@@ -493,7 +529,7 @@
 	packet->state = hpsb_pending;
 	packet->sendtime = jiffies;
 
-	spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags);
+	spin_unlock_irqrestore(&pending_packets_lock, flags);
 
 	mod_timer(&host->timeout, jiffies + host->timeout_interval);
 }
@@ -504,9 +540,10 @@
  * @rootid: root whose force_root bit should get set (-1 = don't set force_root)
  * @gapcnt: gap count value to set (-1 = don't set gap count)
  *
- * This function sends a PHY config packet on the bus through the specified host.
+ * This function sends a PHY config packet on the bus through the specified
+ * host.
  *
- * Return value: 0 for success or error number otherwise.
+ * Return value: 0 for success or negative error number otherwise.
  */
 int hpsb_send_phy_config(struct hpsb_host *host, int rootid, int gapcnt)
 {
@@ -567,12 +604,16 @@
 	WARN_ON(packet->no_waiter && packet->expect_response);
 
 	if (!packet->no_waiter || packet->expect_response) {
+		unsigned long flags;
+
 		atomic_inc(&packet->refcnt);
 		/* Set the initial "sendtime" to 10 seconds from now, to
 		   prevent premature expiry.  If a packet takes more than
 		   10 seconds to hit the wire, we have bigger problems :) */
 		packet->sendtime = jiffies + 10 * HZ;
-		skb_queue_tail(&host->pending_packet_queue, packet->skb);
+		spin_lock_irqsave(&pending_packets_lock, flags);
+		list_add_tail(&packet->queue, &host->pending_packets);
+		spin_unlock_irqrestore(&pending_packets_lock, flags);
 	}
 
 	if (packet->node_id == host->node_id) {
@@ -621,6 +662,12 @@
 	complete((struct completion *) data);
 }
 
+/**
+ * hpsb_send_packet_and_wait - enqueue packet, block until transaction completes
+ * @packet: packet to send
+ *
+ * Return value: 0 on success, negative errno on failure.
+ */
 int hpsb_send_packet_and_wait(struct hpsb_packet *packet)
 {
 	struct completion done;
@@ -642,86 +689,97 @@
 	}
 }
 
+static size_t packet_size_to_data_size(size_t packet_size, size_t header_size,
+				       size_t buffer_size, int tcode)
+{
+	size_t ret = packet_size <= header_size ? 0 : packet_size - header_size;
+
+	if (unlikely(ret > buffer_size))
+		ret = buffer_size;
+
+	if (unlikely(ret + header_size != packet_size))
+		HPSB_ERR("unexpected packet size %zd (tcode %d), bug?",
+			 packet_size, tcode);
+	return ret;
+}
 
 static void handle_packet_response(struct hpsb_host *host, int tcode,
 				   quadlet_t *data, size_t size)
 {
-	struct hpsb_packet *packet = NULL;
-	struct sk_buff *skb;
-	int tcode_match = 0;
-	int tlabel;
+	struct hpsb_packet *packet;
+	int tlabel = (data[0] >> 10) & 0x3f;
+	size_t header_size;
 	unsigned long flags;
 
-	tlabel = (data[0] >> 10) & 0x3f;
+	spin_lock_irqsave(&pending_packets_lock, flags);
 
-	spin_lock_irqsave(&host->pending_packet_queue.lock, flags);
+	list_for_each_entry(packet, &host->pending_packets, queue)
+		if (packet->tlabel == tlabel &&
+		    packet->node_id == (data[1] >> 16))
+			goto found;
 
-	skb_queue_walk(&host->pending_packet_queue, skb) {
-		packet = (struct hpsb_packet *)skb->data;
-		if ((packet->tlabel == tlabel)
-		    && (packet->node_id == (data[1] >> 16))){
-			break;
-		}
+	spin_unlock_irqrestore(&pending_packets_lock, flags);
+	HPSB_DEBUG("unsolicited response packet received - %s",
+		   "no tlabel match");
+	dump_packet("contents", data, 16, -1);
+	return;
 
-		packet = NULL;
-	}
-
-	if (packet == NULL) {
-		HPSB_DEBUG("unsolicited response packet received - no tlabel match");
-		dump_packet("contents", data, 16, -1);
-		spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags);
-		return;
-	}
-
+found:
 	switch (packet->tcode) {
 	case TCODE_WRITEQ:
 	case TCODE_WRITEB:
-		if (tcode != TCODE_WRITE_RESPONSE)
+		if (unlikely(tcode != TCODE_WRITE_RESPONSE))
 			break;
-		tcode_match = 1;
-		memcpy(packet->header, data, 12);
-		break;
+		header_size = 12;
+		size = 0;
+		goto dequeue;
+
 	case TCODE_READQ:
-		if (tcode != TCODE_READQ_RESPONSE)
+		if (unlikely(tcode != TCODE_READQ_RESPONSE))
 			break;
-		tcode_match = 1;
-		memcpy(packet->header, data, 16);
-		break;
+		header_size = 16;
+		size = 0;
+		goto dequeue;
+
 	case TCODE_READB:
-		if (tcode != TCODE_READB_RESPONSE)
+		if (unlikely(tcode != TCODE_READB_RESPONSE))
 			break;
-		tcode_match = 1;
-		BUG_ON(packet->skb->len - sizeof(*packet) < size - 16);
-		memcpy(packet->header, data, 16);
-		memcpy(packet->data, data + 4, size - 16);
-		break;
+		header_size = 16;
+		size = packet_size_to_data_size(size, header_size,
+						packet->allocated_data_size,
+						tcode);
+		goto dequeue;
+
 	case TCODE_LOCK_REQUEST:
-		if (tcode != TCODE_LOCK_RESPONSE)
+		if (unlikely(tcode != TCODE_LOCK_RESPONSE))
 			break;
-		tcode_match = 1;
-		size = min((size - 16), (size_t)8);
-		BUG_ON(packet->skb->len - sizeof(*packet) < size);
-		memcpy(packet->header, data, 16);
-		memcpy(packet->data, data + 4, size);
-		break;
+		header_size = 16;
+		size = packet_size_to_data_size(min(size, (size_t)(16 + 8)),
+						header_size,
+						packet->allocated_data_size,
+						tcode);
+		goto dequeue;
 	}
 
-	if (!tcode_match) {
-		spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags);
-		HPSB_INFO("unsolicited response packet received - tcode mismatch");
-		dump_packet("contents", data, 16, -1);
-		return;
-	}
+	spin_unlock_irqrestore(&pending_packets_lock, flags);
+	HPSB_DEBUG("unsolicited response packet received - %s",
+		   "tcode mismatch");
+	dump_packet("contents", data, 16, -1);
+	return;
 
-	__skb_unlink(skb, &host->pending_packet_queue);
+dequeue:
+	list_del_init(&packet->queue);
+	spin_unlock_irqrestore(&pending_packets_lock, flags);
 
 	if (packet->state == hpsb_queued) {
 		packet->sendtime = jiffies;
 		packet->ack_code = ACK_PENDING;
 	}
-
 	packet->state = hpsb_complete;
-	spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags);
+
+	memcpy(packet->header, data, header_size);
+	if (size)
+		memcpy(packet->data, data + 4, size);
 
 	queue_packet_complete(packet);
 }
@@ -735,6 +793,7 @@
 	p = hpsb_alloc_packet(dsize);
 	if (unlikely(p == NULL)) {
 		/* FIXME - send data_error response */
+		HPSB_ERR("out of memory, cannot send response packet");
 		return NULL;
 	}
 
@@ -784,7 +843,6 @@
 static void fill_async_write_resp(struct hpsb_packet *packet, int rcode)
 {
 	PREP_ASYNC_HEAD_RCODE(TCODE_WRITE_RESPONSE);
-	packet->header[2] = 0;
 	packet->header_size = 12;
 	packet->data_size = 0;
 }
@@ -801,12 +859,9 @@
 	packet->data_size = length;
 }
 
-#define PREP_REPLY_PACKET(length) \
-		packet = create_reply_packet(host, data, length); \
-		if (packet == NULL) break
-
 static void handle_incoming_packet(struct hpsb_host *host, int tcode,
-				   quadlet_t *data, size_t size, int write_acked)
+				   quadlet_t *data, size_t size,
+				   int write_acked)
 {
 	struct hpsb_packet *packet;
 	int length, rcode, extcode;
@@ -816,74 +871,72 @@
 	u16 flags = (u16) data[0];
 	u64 addr;
 
-	/* big FIXME - no error checking is done for an out of bounds length */
+	/* FIXME?
+	 * Out-of-bounds lengths are left for highlevel_read|write to cap. */
 
 	switch (tcode) {
 	case TCODE_WRITEQ:
 		addr = (((u64)(data[1] & 0xffff)) << 32) | data[2];
-		rcode = highlevel_write(host, source, dest, data+3,
+		rcode = highlevel_write(host, source, dest, data + 3,
 					addr, 4, flags);
-
-		if (!write_acked
-		    && (NODEID_TO_NODE(data[0] >> 16) != NODE_MASK)
-		    && (rcode >= 0)) {
-			/* not a broadcast write, reply */
-			PREP_REPLY_PACKET(0);
-			fill_async_write_resp(packet, rcode);
-			send_packet_nocare(packet);
-		}
-		break;
+		goto handle_write_request;
 
 	case TCODE_WRITEB:
 		addr = (((u64)(data[1] & 0xffff)) << 32) | data[2];
-		rcode = highlevel_write(host, source, dest, data+4,
-					addr, data[3]>>16, flags);
-
-		if (!write_acked
-		    && (NODEID_TO_NODE(data[0] >> 16) != NODE_MASK)
-		    && (rcode >= 0)) {
-			/* not a broadcast write, reply */
-			PREP_REPLY_PACKET(0);
+		rcode = highlevel_write(host, source, dest, data + 4,
+					addr, data[3] >> 16, flags);
+handle_write_request:
+		if (rcode < 0 || write_acked ||
+		    NODEID_TO_NODE(data[0] >> 16) == NODE_MASK)
+			return;
+		/* not a broadcast write, reply */
+		packet = create_reply_packet(host, data, 0);
+		if (packet) {
 			fill_async_write_resp(packet, rcode);
 			send_packet_nocare(packet);
 		}
-		break;
+		return;
 
 	case TCODE_READQ:
 		addr = (((u64)(data[1] & 0xffff)) << 32) | data[2];
 		rcode = highlevel_read(host, source, &buffer, addr, 4, flags);
+		if (rcode < 0)
+			return;
 
-		if (rcode >= 0) {
-			PREP_REPLY_PACKET(0);
+		packet = create_reply_packet(host, data, 0);
+		if (packet) {
 			fill_async_readquad_resp(packet, rcode, buffer);
 			send_packet_nocare(packet);
 		}
-		break;
+		return;
 
 	case TCODE_READB:
 		length = data[3] >> 16;
-		PREP_REPLY_PACKET(length);
+		packet = create_reply_packet(host, data, length);
+		if (!packet)
+			return;
 
 		addr = (((u64)(data[1] & 0xffff)) << 32) | data[2];
 		rcode = highlevel_read(host, source, packet->data, addr,
 				       length, flags);
-
-		if (rcode >= 0) {
-			fill_async_readblock_resp(packet, rcode, length);
-			send_packet_nocare(packet);
-		} else {
+		if (rcode < 0) {
 			hpsb_free_packet(packet);
+			return;
 		}
-		break;
+		fill_async_readblock_resp(packet, rcode, length);
+		send_packet_nocare(packet);
+		return;
 
 	case TCODE_LOCK_REQUEST:
 		length = data[3] >> 16;
 		extcode = data[3] & 0xffff;
 		addr = (((u64)(data[1] & 0xffff)) << 32) | data[2];
 
-		PREP_REPLY_PACKET(8);
+		packet = create_reply_packet(host, data, 8);
+		if (!packet)
+			return;
 
-		if ((extcode == 0) || (extcode >= 7)) {
+		if (extcode == 0 || extcode >= 7) {
 			/* let switch default handle error */
 			length = 0;
 		}
@@ -891,12 +944,12 @@
 		switch (length) {
 		case 4:
 			rcode = highlevel_lock(host, source, packet->data, addr,
-					       data[4], 0, extcode,flags);
+					       data[4], 0, extcode, flags);
 			fill_async_lock_resp(packet, rcode, extcode, 4);
 			break;
 		case 8:
-			if ((extcode != EXTCODE_FETCH_ADD)
-			    && (extcode != EXTCODE_LITTLE_ADD)) {
+			if (extcode != EXTCODE_FETCH_ADD &&
+			    extcode != EXTCODE_LITTLE_ADD) {
 				rcode = highlevel_lock(host, source,
 						       packet->data, addr,
 						       data[5], data[4],
@@ -920,29 +973,38 @@
 			break;
 		default:
 			rcode = RCODE_TYPE_ERROR;
-			fill_async_lock_resp(packet, rcode,
-					     extcode, 0);
+			fill_async_lock_resp(packet, rcode, extcode, 0);
 		}
 
-		if (rcode >= 0) {
-			send_packet_nocare(packet);
-		} else {
+		if (rcode < 0)
 			hpsb_free_packet(packet);
-		}
-		break;
+		else
+			send_packet_nocare(packet);
+		return;
 	}
-
 }
-#undef PREP_REPLY_PACKET
 
-
+/**
+ * hpsb_packet_received - hand over received packet to the core
+ *
+ * For host driver module usage.
+ *
+ * The contents of data are expected to be the full packet but with the CRCs
+ * left out (data block follows header immediately), with the header (i.e. the
+ * first four quadlets) in machine byte order and the data block in big endian.
+ * *@data can be safely overwritten after this call.
+ *
+ * If the packet is a write request, @write_acked is to be set to true if it was
+ * ack_complete'd already, false otherwise.  This argument is ignored for any
+ * other packet type.
+ */
 void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size,
 			  int write_acked)
 {
 	int tcode;
 
-	if (host->in_bus_reset) {
-		HPSB_INFO("received packet during reset; ignoring");
+	if (unlikely(host->in_bus_reset)) {
+		HPSB_DEBUG("received packet during reset; ignoring");
 		return;
 	}
 
@@ -976,23 +1038,27 @@
 		break;
 
 	default:
-		HPSB_NOTICE("received packet with bogus transaction code %d",
-			    tcode);
+		HPSB_DEBUG("received packet with bogus transaction code %d",
+			   tcode);
 		break;
 	}
 }
 
-
 static void abort_requests(struct hpsb_host *host)
 {
-	struct hpsb_packet *packet;
-	struct sk_buff *skb;
+	struct hpsb_packet *packet, *p;
+	struct list_head tmp;
+	unsigned long flags;
 
 	host->driver->devctl(host, CANCEL_REQUESTS, 0);
 
-	while ((skb = skb_dequeue(&host->pending_packet_queue)) != NULL) {
-		packet = (struct hpsb_packet *)skb->data;
+	INIT_LIST_HEAD(&tmp);
+	spin_lock_irqsave(&pending_packets_lock, flags);
+	list_splice_init(&host->pending_packets, &tmp);
+	spin_unlock_irqrestore(&pending_packets_lock, flags);
 
+	list_for_each_entry_safe(packet, p, &tmp, queue) {
+		list_del_init(&packet->queue);
 		packet->state = hpsb_complete;
 		packet->ack_code = ACKX_ABORTED;
 		queue_packet_complete(packet);
@@ -1002,87 +1068,90 @@
 void abort_timedouts(unsigned long __opaque)
 {
 	struct hpsb_host *host = (struct hpsb_host *)__opaque;
-	unsigned long flags;
-	struct hpsb_packet *packet;
-	struct sk_buff *skb;
-	unsigned long expire;
+	struct hpsb_packet *packet, *p;
+	struct list_head tmp;
+	unsigned long flags, expire, j;
 
 	spin_lock_irqsave(&host->csr.lock, flags);
 	expire = host->csr.expire;
 	spin_unlock_irqrestore(&host->csr.lock, flags);
 
-	/* Hold the lock around this, since we aren't dequeuing all
-	 * packets, just ones we need. */
-	spin_lock_irqsave(&host->pending_packet_queue.lock, flags);
+	j = jiffies;
+	INIT_LIST_HEAD(&tmp);
+	spin_lock_irqsave(&pending_packets_lock, flags);
 
-	while (!skb_queue_empty(&host->pending_packet_queue)) {
-		skb = skb_peek(&host->pending_packet_queue);
-
-		packet = (struct hpsb_packet *)skb->data;
-
-		if (time_before(packet->sendtime + expire, jiffies)) {
-			__skb_unlink(skb, &host->pending_packet_queue);
-			packet->state = hpsb_complete;
-			packet->ack_code = ACKX_TIMEOUT;
-			queue_packet_complete(packet);
-		} else {
+	list_for_each_entry_safe(packet, p, &host->pending_packets, queue) {
+		if (time_before(packet->sendtime + expire, j))
+			list_move_tail(&packet->queue, &tmp);
+		else
 			/* Since packets are added to the tail, the oldest
 			 * ones are first, always. When we get to one that
 			 * isn't timed out, the rest aren't either. */
 			break;
-		}
 	}
+	if (!list_empty(&host->pending_packets))
+		mod_timer(&host->timeout, j + host->timeout_interval);
 
-	if (!skb_queue_empty(&host->pending_packet_queue))
-		mod_timer(&host->timeout, jiffies + host->timeout_interval);
+	spin_unlock_irqrestore(&pending_packets_lock, flags);
 
-	spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags);
+	list_for_each_entry_safe(packet, p, &tmp, queue) {
+		list_del_init(&packet->queue);
+		packet->state = hpsb_complete;
+		packet->ack_code = ACKX_TIMEOUT;
+		queue_packet_complete(packet);
+	}
 }
 
-
-/* Kernel thread and vars, which handles packets that are completed. Only
- * packets that have a "complete" function are sent here. This way, the
- * completion is run out of kernel context, and doesn't block the rest of
- * the stack. */
 static struct task_struct *khpsbpkt_thread;
-static struct sk_buff_head hpsbpkt_queue;
+static LIST_HEAD(hpsbpkt_queue);
 
 static void queue_packet_complete(struct hpsb_packet *packet)
 {
+	unsigned long flags;
+
 	if (packet->no_waiter) {
 		hpsb_free_packet(packet);
 		return;
 	}
 	if (packet->complete_routine != NULL) {
-		skb_queue_tail(&hpsbpkt_queue, packet->skb);
+		spin_lock_irqsave(&pending_packets_lock, flags);
+		list_add_tail(&packet->queue, &hpsbpkt_queue);
+		spin_unlock_irqrestore(&pending_packets_lock, flags);
 		wake_up_process(khpsbpkt_thread);
 	}
 	return;
 }
 
+/*
+ * Kernel thread which handles packets that are completed.  This way the
+ * packet's "complete" function is asynchronously run in process context.
+ * Only packets which have a "complete" function may be sent here.
+ */
 static int hpsbpkt_thread(void *__hi)
 {
-	struct sk_buff *skb;
-	struct hpsb_packet *packet;
-	void (*complete_routine)(void*);
-	void *complete_data;
+	struct hpsb_packet *packet, *p;
+	struct list_head tmp;
+	int may_schedule;
 
 	current->flags |= PF_NOFREEZE;
 
 	while (!kthread_should_stop()) {
-		while ((skb = skb_dequeue(&hpsbpkt_queue)) != NULL) {
-			packet = (struct hpsb_packet *)skb->data;
 
-			complete_routine = packet->complete_routine;
-			complete_data = packet->complete_data;
+		INIT_LIST_HEAD(&tmp);
+		spin_lock_irq(&pending_packets_lock);
+		list_splice_init(&hpsbpkt_queue, &tmp);
+		spin_unlock_irq(&pending_packets_lock);
 
-			packet->complete_routine = packet->complete_data = NULL;
-
-			complete_routine(complete_data);
+		list_for_each_entry_safe(packet, p, &tmp, queue) {
+			list_del_init(&packet->queue);
+			packet->complete_routine(packet->complete_data);
 		}
 
 		set_current_state(TASK_INTERRUPTIBLE);
-		if (!skb_peek(&hpsbpkt_queue))
+		spin_lock_irq(&pending_packets_lock);
+		may_schedule = list_empty(&hpsbpkt_queue);
+		spin_unlock_irq(&pending_packets_lock);
+		if (may_schedule)
 			schedule();
 		__set_current_state(TASK_RUNNING);
 	}
@@ -1093,8 +1162,6 @@
 {
 	int i, ret;
 
-	skb_queue_head_init(&hpsbpkt_queue);
-
 	/* non-fatal error */
 	if (hpsb_init_config_roms()) {
 		HPSB_ERR("Failed to initialize some config rom entries.\n");
@@ -1268,7 +1335,6 @@
 EXPORT_SYMBOL(hpsb_set_hostinfo_key);
 EXPORT_SYMBOL(hpsb_get_hostinfo_bykey);
 EXPORT_SYMBOL(hpsb_set_hostinfo);
-EXPORT_SYMBOL(highlevel_host_reset);
 
 /** nodemgr.c **/
 EXPORT_SYMBOL(hpsb_node_fill_packet);
@@ -1311,11 +1377,10 @@
 EXPORT_SYMBOL(hpsb_iso_recv_flush);
 
 /** csr1212.c **/
-EXPORT_SYMBOL(csr1212_new_directory);
 EXPORT_SYMBOL(csr1212_attach_keyval_to_directory);
 EXPORT_SYMBOL(csr1212_detach_keyval_from_directory);
-EXPORT_SYMBOL(csr1212_release_keyval);
-EXPORT_SYMBOL(csr1212_read);
+EXPORT_SYMBOL(csr1212_get_keyval);
+EXPORT_SYMBOL(csr1212_new_directory);
 EXPORT_SYMBOL(csr1212_parse_keyval);
-EXPORT_SYMBOL(_csr1212_read_keyval);
-EXPORT_SYMBOL(_csr1212_destroy_keyval);
+EXPORT_SYMBOL(csr1212_read);
+EXPORT_SYMBOL(csr1212_release_keyval);
diff --git a/drivers/ieee1394/ieee1394_core.h b/drivers/ieee1394/ieee1394_core.h
index bd29d8e..ad52652 100644
--- a/drivers/ieee1394/ieee1394_core.h
+++ b/drivers/ieee1394/ieee1394_core.h
@@ -4,7 +4,6 @@
 #include <linux/device.h>
 #include <linux/fs.h>
 #include <linux/list.h>
-#include <linux/skbuff.h>
 #include <linux/types.h>
 #include <asm/atomic.h>
 
@@ -13,7 +12,7 @@
 
 struct hpsb_packet {
 	/* This struct is basically read-only for hosts with the exception of
-	 * the data buffer contents and xnext - see below. */
+	 * the data buffer contents and driver_list. */
 
 	/* This can be used for host driver internal linking.
 	 *
@@ -49,134 +48,65 @@
 	/* Speed to transmit with: 0 = 100Mbps, 1 = 200Mbps, 2 = 400Mbps */
 	unsigned speed_code:2;
 
-	/*
-	 * *header and *data are guaranteed to be 32-bit DMAable and may be
-	 * overwritten to allow in-place byte swapping.  Neither of these is
-	 * CRCed (the sizes also don't include CRC), but contain space for at
-	 * least one additional quadlet to allow in-place CRCing.  The memory is
-	 * also guaranteed to be DMA mappable.
-	 */
-	quadlet_t *header;
-	quadlet_t *data;
-	size_t header_size;
-	size_t data_size;
-
 	struct hpsb_host *host;
 	unsigned int generation;
 
 	atomic_t refcnt;
+	struct list_head queue;
 
 	/* Function (and possible data to pass to it) to call when this
 	 * packet is completed.  */
 	void (*complete_routine)(void *);
 	void *complete_data;
 
-	/* XXX This is just a hack at the moment */
-	struct sk_buff *skb;
-
 	/* Store jiffies for implementing bus timeouts. */
 	unsigned long sendtime;
 
-	quadlet_t embedded_header[5];
+	/* Sizes are in bytes. *data can be DMA-mapped. */
+	size_t allocated_data_size;	/* as allocated */
+	size_t data_size;		/* as filled in */
+	size_t header_size;		/* as filled in, not counting the CRC */
+	quadlet_t *data;
+	quadlet_t header[5];
+	quadlet_t embedded_data[0];	/* keep as last member */
 };
 
-/* Set a task for when a packet completes */
 void hpsb_set_packet_complete_task(struct hpsb_packet *packet,
 				   void (*routine)(void *), void *data);
-
 static inline struct hpsb_packet *driver_packet(struct list_head *l)
 {
 	return list_entry(l, struct hpsb_packet, driver_list);
 }
-
 void abort_timedouts(unsigned long __opaque);
-
 struct hpsb_packet *hpsb_alloc_packet(size_t data_size);
 void hpsb_free_packet(struct hpsb_packet *packet);
 
-/*
- * Generation counter for the complete 1394 subsystem.  Generation gets
- * incremented on every change in the subsystem (e.g. bus reset).
+/**
+ * get_hpsb_generation - generation counter for the complete 1394 subsystem
  *
- * Use the functions, not the variable.
+ * Generation gets incremented on every change in the subsystem (notably on bus
+ * resets). Use the functions, not the variable.
  */
 static inline unsigned int get_hpsb_generation(struct hpsb_host *host)
 {
 	return atomic_read(&host->generation);
 }
 
-/*
- * Send a PHY configuration packet, return 0 on success, negative
- * errno on failure.
- */
 int hpsb_send_phy_config(struct hpsb_host *host, int rootid, int gapcnt);
-
-/*
- * Queue packet for transmitting, return 0 on success, negative errno
- * on failure.
- */
 int hpsb_send_packet(struct hpsb_packet *packet);
-
-/*
- * Queue packet for transmitting, and block until the transaction
- * completes. Return 0 on success, negative errno on failure.
- */
 int hpsb_send_packet_and_wait(struct hpsb_packet *packet);
-
-/* Initiate bus reset on the given host.  Returns 1 if bus reset already in
- * progress, 0 otherwise. */
 int hpsb_reset_bus(struct hpsb_host *host, int type);
-
 int hpsb_read_cycle_timer(struct hpsb_host *host, u32 *cycle_timer,
 			  u64 *local_time);
 
-/*
- * The following functions are exported for host driver module usage.  All of
- * them are safe to use in interrupt contexts, although some are quite
- * complicated so you may want to run them in bottom halves instead of calling
- * them directly.
- */
-
-/* Notify a bus reset to the core.  Returns 1 if bus reset already in progress,
- * 0 otherwise. */
 int hpsb_bus_reset(struct hpsb_host *host);
-
-/*
- * Hand over received selfid packet to the core.  Complement check (second
- * quadlet is complement of first) is expected to be done and successful.
- */
 void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid);
-
-/*
- * Notify completion of SelfID stage to the core and report new physical ID
- * and whether host is root now.
- */
 void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot);
-
-/*
- * Notify core of sending a packet.  Ackcode is the ack code returned for async
- * transmits or ACKX_SEND_ERROR if the transmission failed completely; ACKX_NONE
- * for other cases (internal errors that don't justify a panic).  Safe to call
- * from within a transmit packet routine.
- */
 void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet,
 		      int ackcode);
-
-/*
- * Hand over received packet to the core.  The contents of data are expected to
- * be the full packet but with the CRCs left out (data block follows header
- * immediately), with the header (i.e. the first four quadlets) in machine byte
- * order and the data block in big endian.  *data can be safely overwritten
- * after this call.
- *
- * If the packet is a write request, write_acked is to be set to true if it was
- * ack_complete'd already, false otherwise.  This arg is ignored for any other
- * packet type.
- */
 void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size,
 			  int write_acked);
 
-
 /*
  * CHARACTER DEVICE DISPATCHING
  *
@@ -217,7 +147,9 @@
 #define IEEE1394_EXPERIMENTAL_DEV MKDEV(IEEE1394_MAJOR, \
 					IEEE1394_MINOR_BLOCK_EXPERIMENTAL * 16)
 
-/* return the index (within a minor number block) of a file */
+/**
+ * ieee1394_file_to_instance - get the index within a minor number block
+ */
 static inline unsigned char ieee1394_file_to_instance(struct file *file)
 {
 	return file->f_path.dentry->d_inode->i_cindex;
diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c
index 0833fc9..40078ce 100644
--- a/drivers/ieee1394/ieee1394_transactions.c
+++ b/drivers/ieee1394/ieee1394_transactions.c
@@ -10,11 +10,16 @@
  */
 
 #include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/hardirq.h>
 #include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/sched.h>  /* because linux/wait.h is broken if CONFIG_SMP=n */
 #include <linux/wait.h>
 
 #include <asm/bug.h>
 #include <asm/errno.h>
+#include <asm/system.h>
 
 #include "ieee1394.h"
 #include "ieee1394_types.h"
@@ -32,7 +37,7 @@
 #ifndef HPSB_DEBUG_TLABELS
 static
 #endif
-spinlock_t hpsb_tlabel_lock = SPIN_LOCK_UNLOCKED;
+DEFINE_SPINLOCK(hpsb_tlabel_lock);
 
 static DECLARE_WAIT_QUEUE_HEAD(tlabel_wq);
 
@@ -212,6 +217,15 @@
 	wake_up_interruptible(&tlabel_wq);
 }
 
+/**
+ * hpsb_packet_success - Make sense of the ack and reply codes
+ *
+ * Make sense of the ack and reply codes and return more convenient error codes:
+ * 0 = success.  -%EBUSY = node is busy, try again.  -%EAGAIN = error which can
+ * probably resolved by retry.  -%EREMOTEIO = node suffers from an internal
+ * error.  -%EACCES = this transaction is not allowed on requested address.
+ * -%EINVAL = invalid address at node.
+ */
 int hpsb_packet_success(struct hpsb_packet *packet)
 {
 	switch (packet->ack_code) {
@@ -364,6 +378,13 @@
 	}
 	packet->host = host;
 
+	/* Because it is too difficult to determine all PHY speeds and link
+	 * speeds here, we use S100... */
+	packet->speed_code = IEEE1394_SPEED_100;
+
+	/* ...and prevent hpsb_send_packet() from overriding it. */
+	packet->node_id = LOCAL_BUS | ALL_NODES;
+
 	if (hpsb_get_tlabel(packet)) {
 		hpsb_free_packet(packet);
 		return NULL;
@@ -493,6 +514,16 @@
  * avoid in kernel buffers for user space callers
  */
 
+/**
+ * hpsb_read - generic read function
+ *
+ * Recognizes the local node ID and act accordingly.  Automatically uses a
+ * quadlet read request if @length == 4 and and a block read request otherwise.
+ * It does not yet support lengths that are not a multiple of 4.
+ *
+ * You must explicitly specifiy the @generation for which the node ID is valid,
+ * to avoid sending packets to the wrong nodes when we race with a bus reset.
+ */
 int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation,
 	      u64 addr, quadlet_t * buffer, size_t length)
 {
@@ -532,6 +563,16 @@
 	return retval;
 }
 
+/**
+ * hpsb_write - generic write function
+ *
+ * Recognizes the local node ID and act accordingly.  Automatically uses a
+ * quadlet write request if @length == 4 and and a block write request
+ * otherwise.  It does not yet support lengths that are not a multiple of 4.
+ *
+ * You must explicitly specifiy the @generation for which the node ID is valid,
+ * to avoid sending packets to the wrong nodes when we race with a bus reset.
+ */
 int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation,
 	       u64 addr, quadlet_t * buffer, size_t length)
 {
diff --git a/drivers/ieee1394/ieee1394_transactions.h b/drivers/ieee1394/ieee1394_transactions.h
index c1369c4..86b8ee6 100644
--- a/drivers/ieee1394/ieee1394_transactions.h
+++ b/drivers/ieee1394/ieee1394_transactions.h
@@ -27,27 +27,7 @@
 struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 *buffer,
                                            int length, int channel, int tag,
 					   int sync);
-
-/*
- * hpsb_packet_success - Make sense of the ack and reply codes and
- * return more convenient error codes:
- * 0           success
- * -EBUSY      node is busy, try again
- * -EAGAIN     error which can probably resolved by retry
- * -EREMOTEIO  node suffers from an internal error
- * -EACCES     this transaction is not allowed on requested address
- * -EINVAL     invalid address at node
- */
 int hpsb_packet_success(struct hpsb_packet *packet);
-
-/*
- * The generic read and write functions.  All recognize the local node ID
- * and act accordingly.  Read and write automatically use quadlet commands if
- * length == 4 and and block commands otherwise (however, they do not yet
- * support lengths that are not a multiple of 4).  You must explicitly specifiy
- * the generation for which the node ID is valid, to avoid sending packets to
- * the wrong nodes when we race with a bus reset.
- */
 int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation,
 	      u64 addr, quadlet_t *buffer, size_t length);
 int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation,
diff --git a/drivers/ieee1394/iso.c b/drivers/ieee1394/iso.c
index c6227e5..07ca35c 100644
--- a/drivers/ieee1394/iso.c
+++ b/drivers/ieee1394/iso.c
@@ -10,11 +10,15 @@
  */
 
 #include <linux/pci.h>
+#include <linux/sched.h>
 #include <linux/slab.h>
 
 #include "hosts.h"
 #include "iso.h"
 
+/**
+ * hpsb_iso_stop - stop DMA
+ */
 void hpsb_iso_stop(struct hpsb_iso *iso)
 {
 	if (!(iso->flags & HPSB_ISO_DRIVER_STARTED))
@@ -25,6 +29,9 @@
 	iso->flags &= ~HPSB_ISO_DRIVER_STARTED;
 }
 
+/**
+ * hpsb_iso_shutdown - deallocate buffer and DMA context
+ */
 void hpsb_iso_shutdown(struct hpsb_iso *iso)
 {
 	if (iso->flags & HPSB_ISO_DRIVER_INIT) {
@@ -130,6 +137,9 @@
 	return NULL;
 }
 
+/**
+ * hpsb_iso_n_ready - returns number of packets ready to send or receive
+ */
 int hpsb_iso_n_ready(struct hpsb_iso *iso)
 {
 	unsigned long flags;
@@ -142,6 +152,9 @@
 	return val;
 }
 
+/**
+ * hpsb_iso_xmit_init - allocate the buffer and DMA context
+ */
 struct hpsb_iso *hpsb_iso_xmit_init(struct hpsb_host *host,
 				    unsigned int data_buf_size,
 				    unsigned int buf_packets,
@@ -172,6 +185,11 @@
 	return NULL;
 }
 
+/**
+ * hpsb_iso_recv_init - allocate the buffer and DMA context
+ *
+ * Note, if channel = -1, multi-channel receive is enabled.
+ */
 struct hpsb_iso *hpsb_iso_recv_init(struct hpsb_host *host,
 				    unsigned int data_buf_size,
 				    unsigned int buf_packets,
@@ -199,6 +217,11 @@
 	return NULL;
 }
 
+/**
+ * hpsb_iso_recv_listen_channel
+ *
+ * multi-channel only
+ */
 int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel)
 {
 	if (iso->type != HPSB_ISO_RECV || iso->channel != -1 || channel >= 64)
@@ -206,6 +229,11 @@
 	return iso->host->driver->isoctl(iso, RECV_LISTEN_CHANNEL, channel);
 }
 
+/**
+ * hpsb_iso_recv_unlisten_channel
+ *
+ * multi-channel only
+ */
 int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel)
 {
 	if (iso->type != HPSB_ISO_RECV || iso->channel != -1 || channel >= 64)
@@ -213,6 +241,11 @@
 	return iso->host->driver->isoctl(iso, RECV_UNLISTEN_CHANNEL, channel);
 }
 
+/**
+ * hpsb_iso_recv_set_channel_mask
+ *
+ * multi-channel only
+ */
 int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask)
 {
 	if (iso->type != HPSB_ISO_RECV || iso->channel != -1)
@@ -221,6 +254,12 @@
 					 (unsigned long)&mask);
 }
 
+/**
+ * hpsb_iso_recv_flush - check for arrival of new packets
+ *
+ * check for arrival of new packets immediately (even if irq_interval
+ * has not yet been reached)
+ */
 int hpsb_iso_recv_flush(struct hpsb_iso *iso)
 {
 	if (iso->type != HPSB_ISO_RECV)
@@ -238,6 +277,9 @@
 	return retval;
 }
 
+/**
+ * hpsb_iso_xmit_start - start DMA
+ */
 int hpsb_iso_xmit_start(struct hpsb_iso *iso, int cycle, int prebuffer)
 {
 	if (iso->type != HPSB_ISO_XMIT)
@@ -270,6 +312,9 @@
 	return 0;
 }
 
+/**
+ * hpsb_iso_recv_start - start DMA
+ */
 int hpsb_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync)
 {
 	int retval = 0;
@@ -306,8 +351,7 @@
 }
 
 /* check to make sure the user has not supplied bogus values of offset/len
-   that would cause the kernel to access memory outside the buffer */
-
+ * that would cause the kernel to access memory outside the buffer */
 static int hpsb_iso_check_offset_len(struct hpsb_iso *iso,
 				     unsigned int offset, unsigned short len,
 				     unsigned int *out_offset,
@@ -331,6 +375,12 @@
 	return 0;
 }
 
+/**
+ * hpsb_iso_xmit_queue_packet - queue a packet for transmission.
+ *
+ * @offset is relative to the beginning of the DMA buffer, where the packet's
+ * data payload should already have been placed.
+ */
 int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len,
 			       u8 tag, u8 sy)
 {
@@ -380,6 +430,9 @@
 	return rv;
 }
 
+/**
+ * hpsb_iso_xmit_sync - wait until all queued packets have been transmitted
+ */
 int hpsb_iso_xmit_sync(struct hpsb_iso *iso)
 {
 	if (iso->type != HPSB_ISO_XMIT)
@@ -390,6 +443,15 @@
 					iso->buf_packets);
 }
 
+/**
+ * hpsb_iso_packet_sent
+ *
+ * Available to low-level drivers.
+ *
+ * Call after a packet has been transmitted to the bus (interrupt context is
+ * OK).  @cycle is the _exact_ cycle the packet was sent on.  @error should be
+ * non-zero if some sort of error occurred when sending the packet.
+ */
 void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error)
 {
 	unsigned long flags;
@@ -413,6 +475,13 @@
 	spin_unlock_irqrestore(&iso->lock, flags);
 }
 
+/**
+ * hpsb_iso_packet_received
+ *
+ * Available to low-level drivers.
+ *
+ * Call after a packet has been received (interrupt context is OK).
+ */
 void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len,
 			      u16 total_len, u16 cycle, u8 channel, u8 tag,
 			      u8 sy)
@@ -442,6 +511,11 @@
 	spin_unlock_irqrestore(&iso->lock, flags);
 }
 
+/**
+ * hpsb_iso_recv_release_packets - release packets, reuse buffer
+ *
+ * @n_packets have been read out of the buffer, re-use the buffer space
+ */
 int hpsb_iso_recv_release_packets(struct hpsb_iso *iso, unsigned int n_packets)
 {
 	unsigned long flags;
@@ -477,6 +551,13 @@
 	return rv;
 }
 
+/**
+ * hpsb_iso_wake
+ *
+ * Available to low-level drivers.
+ *
+ * Call to wake waiting processes after buffer space has opened up.
+ */
 void hpsb_iso_wake(struct hpsb_iso *iso)
 {
 	wake_up_interruptible(&iso->waitq);
diff --git a/drivers/ieee1394/iso.h b/drivers/ieee1394/iso.h
index 1210a97..b94e55e 100644
--- a/drivers/ieee1394/iso.h
+++ b/drivers/ieee1394/iso.h
@@ -150,8 +150,6 @@
 
 /* functions available to high-level drivers (e.g. raw1394) */
 
-/* allocate the buffer and DMA context */
-
 struct hpsb_iso* hpsb_iso_xmit_init(struct hpsb_host *host,
 				    unsigned int data_buf_size,
 				    unsigned int buf_packets,
@@ -159,8 +157,6 @@
 				    int speed,
 				    int irq_interval,
 				    void (*callback)(struct hpsb_iso*));
-
-/* note: if channel = -1, multi-channel receive is enabled */
 struct hpsb_iso* hpsb_iso_recv_init(struct hpsb_host *host,
 				    unsigned int data_buf_size,
 				    unsigned int buf_packets,
@@ -168,56 +164,29 @@
 				    int dma_mode,
 				    int irq_interval,
 				    void (*callback)(struct hpsb_iso*));
-
-/* multi-channel only */
 int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel);
 int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel);
 int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask);
-
-/* start/stop DMA */
 int hpsb_iso_xmit_start(struct hpsb_iso *iso, int start_on_cycle,
 			int prebuffer);
 int hpsb_iso_recv_start(struct hpsb_iso *iso, int start_on_cycle,
 			int tag_mask, int sync);
 void hpsb_iso_stop(struct hpsb_iso *iso);
-
-/* deallocate buffer and DMA context */
 void hpsb_iso_shutdown(struct hpsb_iso *iso);
-
-/* queue a packet for transmission.
- * 'offset' is relative to the beginning of the DMA buffer, where the packet's
- * data payload should already have been placed. */
 int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len,
 			       u8 tag, u8 sy);
-
-/* wait until all queued packets have been transmitted to the bus */
 int hpsb_iso_xmit_sync(struct hpsb_iso *iso);
-
-/* N packets have been read out of the buffer, re-use the buffer space */
-int  hpsb_iso_recv_release_packets(struct hpsb_iso *recv,
-				   unsigned int n_packets);
-
-/* check for arrival of new packets immediately (even if irq_interval
- * has not yet been reached) */
+int hpsb_iso_recv_release_packets(struct hpsb_iso *recv,
+				  unsigned int n_packets);
 int hpsb_iso_recv_flush(struct hpsb_iso *iso);
-
-/* returns # of packets ready to send or receive */
 int hpsb_iso_n_ready(struct hpsb_iso *iso);
 
 /* the following are callbacks available to low-level drivers */
 
-/* call after a packet has been transmitted to the bus (interrupt context is OK)
- * 'cycle' is the _exact_ cycle the packet was sent on
- * 'error' should be non-zero if some sort of error occurred when sending the
- *  packet */
 void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error);
-
-/* call after a packet has been received (interrupt context OK) */
 void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len,
 			      u16 total_len, u16 cycle, u8 channel, u8 tag,
 			      u8 sy);
-
-/* call to wake waiting processes after buffer space has opened up. */
 void hpsb_iso_wake(struct hpsb_iso *iso);
 
 #endif /* IEEE1394_ISO_H */
diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c
index c5ace19..6a1a057 100644
--- a/drivers/ieee1394/nodemgr.c
+++ b/drivers/ieee1394/nodemgr.c
@@ -16,6 +16,7 @@
 #include <linux/kthread.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/mutex.h>
 #include <linux/freezer.h>
 #include <asm/atomic.h>
 
@@ -115,7 +116,7 @@
 
 static int nodemgr_get_max_rom(quadlet_t *bus_info_data, void *__ci)
 {
-	return (CSR1212_BE32_TO_CPU(bus_info_data[2]) >> 8) & 0x3;
+	return (be32_to_cpu(bus_info_data[2]) >> 8) & 0x3;
 }
 
 static struct csr1212_bus_ops nodemgr_csr_ops = {
@@ -370,9 +371,7 @@
 
 	if (state == 1) {
 		ud->ignore_driver = 1;
-		down_write(&ieee1394_bus_type.subsys.rwsem);
 		device_release_driver(dev);
-		up_write(&ieee1394_bus_type.subsys.rwsem);
 	} else if (state == 0)
 		ud->ignore_driver = 0;
 
@@ -582,7 +581,7 @@
 			goto fail;
 	return;
 fail:
-	HPSB_ERR("Failed to add sysfs attribute for driver %s", driver->name);
+	HPSB_ERR("Failed to add sysfs attribute");
 }
 
 
@@ -606,8 +605,7 @@
 			goto fail;
 	return;
 fail:
-	HPSB_ERR("Failed to add sysfs attribute for node %016Lx",
-		 (unsigned long long)ne->guid);
+	HPSB_ERR("Failed to add sysfs attribute");
 }
 
 
@@ -621,7 +619,7 @@
 			goto fail;
 	return;
 fail:
-	HPSB_ERR("Failed to add sysfs attribute for host %d", host->id);
+	HPSB_ERR("Failed to add sysfs attribute");
 }
 
 
@@ -681,8 +679,7 @@
 	}
 	return;
 fail:
-	HPSB_ERR("Failed to add sysfs attributes for unit %s",
-		 ud->device.bus_id);
+	HPSB_ERR("Failed to add sysfs attribute");
 }
 
 
@@ -1146,13 +1143,13 @@
 		last_key_id = kv->key.id;
 	}
 
-	if (ne->vendor_name_kv &&
-	    device_create_file(&ne->device, &dev_attr_ne_vendor_name_kv))
-		goto fail;
-	return;
-fail:
-	HPSB_ERR("Failed to add sysfs attribute for node %016Lx",
-		 (unsigned long long)ne->guid);
+	if (ne->vendor_name_kv) {
+		int error = device_create_file(&ne->device,
+					       &dev_attr_ne_vendor_name_kv);
+
+		if (error && error != -EEXIST)
+			HPSB_ERR("Failed to add sysfs attribute");
+	}
 }
 
 #ifdef CONFIG_HOTPLUG
@@ -1163,6 +1160,7 @@
 	struct unit_directory *ud;
 	int i = 0;
 	int length = 0;
+	int retval = 0;
 	/* ieee1394:venNmoNspNverN */
 	char buf[8 + 1 + 3 + 8 + 2 + 8 + 2 + 8 + 3 + 8 + 1];
 
@@ -1176,14 +1174,11 @@
 
 #define PUT_ENVP(fmt,val) 					\
 do {								\
-    	int printed;						\
-	envp[i++] = buffer;					\
-	printed = snprintf(buffer, buffer_size - length,	\
-			   fmt, val);				\
-	if ((buffer_size - (length+printed) <= 0) || (i >= num_envp))	\
-		return -ENOMEM;					\
-	length += printed+1;					\
-	buffer += printed+1;					\
+	retval = add_uevent_var(envp, num_envp, &i,		\
+				buffer, buffer_size, &length,	\
+				fmt, val);			\
+	if (retval)						\
+		return retval;					\
 } while (0)
 
 	PUT_ENVP("VENDOR_ID=%06x", ud->vendor_id);
@@ -1393,12 +1388,10 @@
 		if (ud->ne != ne)
 			continue;
 
-		down_write(&ieee1394_bus_type.subsys.rwsem);
 		if (ud->device.driver &&
 		    (!ud->device.driver->suspend ||
 		      ud->device.driver->suspend(&ud->device, PMSG_SUSPEND)))
 			device_release_driver(&ud->device);
-		up_write(&ieee1394_bus_type.subsys.rwsem);
 	}
 	up(&nodemgr_ud_class.sem);
 }
@@ -1418,10 +1411,8 @@
 		if (ud->ne != ne)
 			continue;
 
-		down_read(&ieee1394_bus_type.subsys.rwsem);
 		if (ud->device.driver && ud->device.driver->resume)
 			ud->device.driver->resume(&ud->device);
-		up_read(&ieee1394_bus_type.subsys.rwsem);
 	}
 	up(&nodemgr_ud_class.sem);
 
@@ -1442,7 +1433,6 @@
 		if (ud->ne != ne)
 			continue;
 
-		down_write(&ieee1394_bus_type.subsys.rwsem);
 		if (ud->device.driver) {
 			pdrv = container_of(ud->device.driver,
 					    struct hpsb_protocol_driver,
@@ -1450,7 +1440,6 @@
 			if (pdrv->update && pdrv->update(ud))
 				device_release_driver(&ud->device);
 		}
-		up_write(&ieee1394_bus_type.subsys.rwsem);
 	}
 	up(&nodemgr_ud_class.sem);
 }
@@ -1748,7 +1737,19 @@
 	return 0;
 }
 
-int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *))
+/**
+ * nodemgr_for_each_host - call a function for each IEEE 1394 host
+ * @data: an address to supply to the callback
+ * @cb: function to call for each host
+ *
+ * Iterate the hosts, calling a given function with supplied data for each host.
+ * If the callback fails on a host, i.e. if it returns a non-zero value, the
+ * iteration is stopped.
+ *
+ * Return value: 0 on success, non-zero on failure (same as returned by last run
+ * of the callback).
+ */
+int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *))
 {
 	struct class_device *cdev;
 	struct hpsb_host *host;
@@ -1758,7 +1759,7 @@
 	list_for_each_entry(cdev, &hpsb_host_class.children, node) {
 		host = container_of(cdev, struct hpsb_host, class_dev);
 
-		if ((error = cb(host, __data)))
+		if ((error = cb(host, data)))
 			break;
 	}
 	up(&hpsb_host_class.sem);
@@ -1766,7 +1767,7 @@
 	return error;
 }
 
-/* The following four convenience functions use a struct node_entry
+/* The following two convenience functions use a struct node_entry
  * for addressing a node on the bus.  They are intended for use by any
  * process context, not just the nodemgr thread, so we need to be a
  * little careful when reading out the node ID and generation.  The
@@ -1781,12 +1782,20 @@
  * ID's.
  */
 
-void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *pkt)
+/**
+ * hpsb_node_fill_packet - fill some destination information into a packet
+ * @ne: destination node
+ * @packet: packet to fill in
+ *
+ * This will fill in the given, pre-initialised hpsb_packet with the current
+ * information from the node entry (host, node ID, bus generation number).
+ */
+void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *packet)
 {
-	pkt->host = ne->host;
-	pkt->generation = ne->generation;
+	packet->host = ne->host;
+	packet->generation = ne->generation;
 	barrier();
-	pkt->node_id = ne->nodeid;
+	packet->node_id = ne->nodeid;
 }
 
 int hpsb_node_write(struct node_entry *ne, u64 addr,
diff --git a/drivers/ieee1394/nodemgr.h b/drivers/ieee1394/nodemgr.h
index 4147303..e7ac683 100644
--- a/drivers/ieee1394/nodemgr.h
+++ b/drivers/ieee1394/nodemgr.h
@@ -153,30 +153,10 @@
 {
 	return ne->generation == get_hpsb_generation(ne->host);
 }
-
-/*
- * This will fill in the given, pre-initialised hpsb_packet with the current
- * information from the node entry (host, node ID, generation number).  It will
- * return false if the node owning the GUID is not accessible (and not modify
- * the hpsb_packet) and return true otherwise.
- *
- * Note that packet sending may still fail in hpsb_send_packet if a bus reset
- * happens while you are trying to set up the packet (due to obsolete generation
- * number).  It will at least reliably fail so that you don't accidentally and
- * unknowingly send your packet to the wrong node.
- */
-void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *pkt);
-
-int hpsb_node_read(struct node_entry *ne, u64 addr,
-		   quadlet_t *buffer, size_t length);
+void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *packet);
 int hpsb_node_write(struct node_entry *ne, u64 addr,
 		    quadlet_t *buffer, size_t length);
-int hpsb_node_lock(struct node_entry *ne, u64 addr,
-		   int extcode, quadlet_t *data, quadlet_t arg);
-
-/* Iterate the hosts, calling a given function with supplied data for each
- * host. */
-int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *));
+int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *));
 
 int init_ieee1394_nodemgr(void);
 void cleanup_ieee1394_nodemgr(void);
diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c
index 06fac0d..5dadfd2 100644
--- a/drivers/ieee1394/ohci1394.c
+++ b/drivers/ieee1394/ohci1394.c
@@ -507,9 +507,8 @@
 	/* Set up self-id dma buffer */
 	reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->selfid_buf_bus);
 
-	/* enable self-id and phys */
-	reg_write(ohci, OHCI1394_LinkControlSet, OHCI1394_LinkControl_RcvSelfID |
-		  OHCI1394_LinkControl_RcvPhyPkt);
+	/* enable self-id */
+	reg_write(ohci, OHCI1394_LinkControlSet, OHCI1394_LinkControl_RcvSelfID);
 
 	/* Set the Config ROM mapping register */
 	reg_write(ohci, OHCI1394_ConfigROMmap, ohci->csr_config_rom_bus);
@@ -518,9 +517,6 @@
 	ohci->max_packet_size =
 		1<<(((reg_read(ohci, OHCI1394_BusOptions)>>12)&0xf)+1);
 		
-	/* Don't accept phy packets into AR request context */
-	reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400);
-
 	/* Clear the interrupt mask */
 	reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff);
 	reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff);
@@ -617,7 +613,7 @@
 #endif
 
 		PRINT(KERN_DEBUG, "Serial EEPROM has suspicious values, "
-                      "attempting to setting max_packet_size to 512 bytes");
+                      "attempting to set max_packet_size to 512 bytes");
 		reg_write(ohci, OHCI1394_BusOptions,
 			  (reg_read(ohci, OHCI1394_BusOptions) & 0xf007) | 0x8002);
 		ohci->max_packet_size = 512;
@@ -2377,6 +2373,7 @@
 	if (event & OHCI1394_postedWriteErr) {
 		PRINT(KERN_ERR, "physical posted write error");
 		/* no recovery strategy yet, had to involve protocol drivers */
+		event &= ~OHCI1394_postedWriteErr;
 	}
 	if (event & OHCI1394_cycleTooLong) {
 		if(printk_ratelimit())
@@ -3658,6 +3655,7 @@
 /* essentially the only purpose of this code is to allow another
    module to hook into ohci's interrupt handler */
 
+/* returns zero if successful, one if DMA context is locked up */
 int ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg)
 {
 	int i=0;
diff --git a/drivers/ieee1394/ohci1394.h b/drivers/ieee1394/ohci1394.h
index fa05f11..f1ad539 100644
--- a/drivers/ieee1394/ohci1394.h
+++ b/drivers/ieee1394/ohci1394.h
@@ -461,9 +461,7 @@
 				  struct ohci1394_iso_tasklet *tasklet);
 void ohci1394_unregister_iso_tasklet(struct ti_ohci *ohci,
 				     struct ohci1394_iso_tasklet *tasklet);
-
-/* returns zero if successful, one if DMA context is locked up */
-int ohci1394_stop_context      (struct ti_ohci *ohci, int reg, char *msg);
+int ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg);
 struct ti_ohci *ohci1394_get_struct(int card_num);
 
 #endif
diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c
index bb897a3..c6aefd9 100644
--- a/drivers/ieee1394/raw1394.c
+++ b/drivers/ieee1394/raw1394.c
@@ -938,7 +938,8 @@
 	int header_length = req->req.misc & 0xffff;
 	int expect_response = req->req.misc >> 16;
 
-	if ((header_length > req->req.length) || (header_length < 12)) {
+	if (header_length > req->req.length || header_length < 12 ||
+	    header_length > FIELD_SIZEOF(struct hpsb_packet, header)) {
 		req->req.error = RAW1394_ERROR_INVALID_ARG;
 		req->req.length = 0;
 		queue_complete_req(req);
diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c
index 4edfff4..4cb6fa2 100644
--- a/drivers/ieee1394/sbp2.c
+++ b/drivers/ieee1394/sbp2.c
@@ -59,8 +59,10 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
+#include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/stat.h>
@@ -469,19 +471,13 @@
 static int sbp2util_create_command_orb_pool(struct sbp2_lu *lu)
 {
 	struct sbp2_fwhost_info *hi = lu->hi;
-	int i;
-	unsigned long flags, orbs;
 	struct sbp2_command_info *cmd;
+	int i, orbs = sbp2_serialize_io ? 2 : SBP2_MAX_CMDS;
 
-	orbs = sbp2_serialize_io ? 2 : SBP2_MAX_CMDS;
-
-	spin_lock_irqsave(&lu->cmd_orb_lock, flags);
 	for (i = 0; i < orbs; i++) {
-		cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC);
-		if (!cmd) {
-			spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
+		cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+		if (!cmd)
 			return -ENOMEM;
-		}
 		cmd->command_orb_dma = dma_map_single(hi->host->device.parent,
 						&cmd->command_orb,
 						sizeof(struct sbp2_command_orb),
@@ -489,11 +485,10 @@
 		cmd->sge_dma = dma_map_single(hi->host->device.parent,
 					&cmd->scatter_gather_element,
 					sizeof(cmd->scatter_gather_element),
-					DMA_BIDIRECTIONAL);
+					DMA_TO_DEVICE);
 		INIT_LIST_HEAD(&cmd->list);
 		list_add_tail(&cmd->list, &lu->cmd_orb_completed);
 	}
-	spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
 	return 0;
 }
 
@@ -514,7 +509,7 @@
 					 DMA_TO_DEVICE);
 			dma_unmap_single(host->device.parent, cmd->sge_dma,
 					 sizeof(cmd->scatter_gather_element),
-					 DMA_BIDIRECTIONAL);
+					 DMA_TO_DEVICE);
 			kfree(cmd);
 		}
 	spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
@@ -757,6 +752,11 @@
 			SBP2_ERR("failed to register lower 4GB address range");
 			goto failed_alloc;
 		}
+#else
+		if (dma_set_mask(hi->host->device.parent, DMA_32BIT_MASK)) {
+			SBP2_ERR("failed to set 4GB DMA mask");
+			goto failed_alloc;
+		}
 #endif
 	}
 
@@ -865,11 +865,8 @@
 	if (!lu->login_orb)
 		goto alloc_fail;
 
-	if (sbp2util_create_command_orb_pool(lu)) {
-		SBP2_ERR("sbp2util_create_command_orb_pool failed!");
-		sbp2_remove_device(lu);
-		return -ENOMEM;
-	}
+	if (sbp2util_create_command_orb_pool(lu))
+		goto alloc_fail;
 
 	/* Wait a second before trying to log in. Previously logged in
 	 * initiators need a chance to reconnect. */
@@ -1628,7 +1625,7 @@
 				   DMA_TO_DEVICE);
 	dma_sync_single_for_device(hi->host->device.parent, cmd->sge_dma,
 				   sizeof(cmd->scatter_gather_element),
-				   DMA_BIDIRECTIONAL);
+				   DMA_TO_DEVICE);
 
 	/* check to see if there are any previous orbs to use */
 	spin_lock_irqsave(&lu->cmd_orb_lock, flags);
@@ -1794,7 +1791,7 @@
 					DMA_TO_DEVICE);
 		dma_sync_single_for_cpu(hi->host->device.parent, cmd->sge_dma,
 					sizeof(cmd->scatter_gather_element),
-					DMA_BIDIRECTIONAL);
+					DMA_TO_DEVICE);
 		/* Grab SCSI command pointers and check status. */
 		/*
 		 * FIXME: If the src field in the status is 1, the ORB DMA must
@@ -1926,7 +1923,7 @@
 					DMA_TO_DEVICE);
 		dma_sync_single_for_cpu(hi->host->device.parent, cmd->sge_dma,
 					sizeof(cmd->scatter_gather_element),
-					DMA_BIDIRECTIONAL);
+					DMA_TO_DEVICE);
 		sbp2util_mark_command_completed(lu, cmd);
 		if (cmd->Current_SCpnt) {
 			cmd->Current_SCpnt->result = status << 16;
@@ -2057,7 +2054,7 @@
 			dma_sync_single_for_cpu(hi->host->device.parent,
 					cmd->sge_dma,
 					sizeof(cmd->scatter_gather_element),
-					DMA_BIDIRECTIONAL);
+					DMA_TO_DEVICE);
 			sbp2util_mark_command_completed(lu, cmd);
 			if (cmd->Current_SCpnt) {
 				cmd->Current_SCpnt->result = DID_ABORT << 16;
diff --git a/drivers/ieee1394/sbp2.h b/drivers/ieee1394/sbp2.h
index 9ae8423..44402b9 100644
--- a/drivers/ieee1394/sbp2.h
+++ b/drivers/ieee1394/sbp2.h
@@ -250,15 +250,15 @@
 /* Per SCSI command */
 struct sbp2_command_info {
 	struct list_head list;
-	struct sbp2_command_orb command_orb ____cacheline_aligned;
-	dma_addr_t command_orb_dma ____cacheline_aligned;
+	struct sbp2_command_orb command_orb;
+	dma_addr_t command_orb_dma;
 	struct scsi_cmnd *Current_SCpnt;
 	void (*Current_done)(struct scsi_cmnd *);
 
 	/* Also need s/g structure for each sbp2 command */
 	struct sbp2_unrestricted_page_table
-			scatter_gather_element[SG_ALL] ____cacheline_aligned;
-	dma_addr_t sge_dma ____cacheline_aligned;
+		scatter_gather_element[SG_ALL] __attribute__((aligned(8)));
+	dma_addr_t sge_dma;
 	void *sge_buffer;
 	dma_addr_t cmd_dma;
 	enum sbp2_dma_types dma_type;
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index 13efd41..6edfecf 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2005 Voltaire, Inc. All rights reserved.
+ * Copyright (c) 2004-2007 Voltaire, Inc. All rights reserved.
  * Copyright (c) 2005 Intel Corporation.  All rights reserved.
  * Copyright (c) 2005 Mellanox Technologies Ltd.  All rights reserved.
  *
@@ -31,7 +31,6 @@
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  *
- * $Id: mad.c 5596 2006-03-03 01:00:07Z sean.hefty $
  */
 #include <linux/dma-mapping.h>
 #include <rdma/ib_cache.h>
@@ -668,7 +667,7 @@
 static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
 				  struct ib_mad_send_wr_private *mad_send_wr)
 {
-	int ret;
+	int ret = 0;
 	struct ib_smp *smp = mad_send_wr->send_buf.mad;
 	unsigned long flags;
 	struct ib_mad_local_private *local;
@@ -688,14 +687,15 @@
 	 */
 	if ((ib_get_smp_direction(smp) ? smp->dr_dlid : smp->dr_slid) ==
 	     IB_LID_PERMISSIVE &&
-	    !smi_handle_dr_smp_send(smp, device->node_type, port_num)) {
+	     smi_handle_dr_smp_send(smp, device->node_type, port_num) ==
+	     IB_SMI_DISCARD) {
 		ret = -EINVAL;
 		printk(KERN_ERR PFX "Invalid directed route\n");
 		goto out;
 	}
+
 	/* Check to post send on QP or process locally */
-	ret = smi_check_local_smp(smp, device);
-	if (!ret)
+	if (smi_check_local_smp(smp, device) == IB_SMI_DISCARD)
 		goto out;
 
 	local = kmalloc(sizeof *local, GFP_ATOMIC);
@@ -1874,18 +1874,22 @@
 
 	if (recv->mad.mad.mad_hdr.mgmt_class ==
 	    IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) {
-		if (!smi_handle_dr_smp_recv(&recv->mad.smp,
-					    port_priv->device->node_type,
-					    port_priv->port_num,
-					    port_priv->device->phys_port_cnt))
+		if (smi_handle_dr_smp_recv(&recv->mad.smp,
+					   port_priv->device->node_type,
+					   port_priv->port_num,
+					   port_priv->device->phys_port_cnt) ==
+					   IB_SMI_DISCARD)
 			goto out;
-		if (!smi_check_forward_dr_smp(&recv->mad.smp))
+
+		if (smi_check_forward_dr_smp(&recv->mad.smp) == IB_SMI_LOCAL)
 			goto local;
-		if (!smi_handle_dr_smp_send(&recv->mad.smp,
-					    port_priv->device->node_type,
-					    port_priv->port_num))
+
+		if (smi_handle_dr_smp_send(&recv->mad.smp,
+					   port_priv->device->node_type,
+					   port_priv->port_num) == IB_SMI_DISCARD)
 			goto out;
-		if (!smi_check_local_smp(&recv->mad.smp, port_priv->device))
+
+		if (smi_check_local_smp(&recv->mad.smp, port_priv->device) == IB_SMI_DISCARD)
 			goto out;
 	}
 
diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c
index 68db633..9a7eaad 100644
--- a/drivers/infiniband/core/sa_query.c
+++ b/drivers/infiniband/core/sa_query.c
@@ -57,6 +57,7 @@
 struct ib_sa_sm_ah {
 	struct ib_ah        *ah;
 	struct kref          ref;
+	u8		     src_path_mask;
 };
 
 struct ib_sa_port {
@@ -380,6 +381,7 @@
 	}
 
 	kref_init(&new_ah->ref);
+	new_ah->src_path_mask = (1 << port_attr.lmc) - 1;
 
 	memset(&ah_attr, 0, sizeof ah_attr);
 	ah_attr.dlid     = port_attr.sm_lid;
@@ -460,6 +462,25 @@
 }
 EXPORT_SYMBOL(ib_sa_cancel_query);
 
+static u8 get_src_path_mask(struct ib_device *device, u8 port_num)
+{
+	struct ib_sa_device *sa_dev;
+	struct ib_sa_port   *port;
+	unsigned long flags;
+	u8 src_path_mask;
+
+	sa_dev = ib_get_client_data(device, &sa_client);
+	if (!sa_dev)
+		return 0x7f;
+
+	port  = &sa_dev->port[port_num - sa_dev->start_port];
+	spin_lock_irqsave(&port->ah_lock, flags);
+	src_path_mask = port->sm_ah ? port->sm_ah->src_path_mask : 0x7f;
+	spin_unlock_irqrestore(&port->ah_lock, flags);
+
+	return src_path_mask;
+}
+
 int ib_init_ah_from_path(struct ib_device *device, u8 port_num,
 			 struct ib_sa_path_rec *rec, struct ib_ah_attr *ah_attr)
 {
@@ -469,7 +490,8 @@
 	memset(ah_attr, 0, sizeof *ah_attr);
 	ah_attr->dlid = be16_to_cpu(rec->dlid);
 	ah_attr->sl = rec->sl;
-	ah_attr->src_path_bits = be16_to_cpu(rec->slid) & 0x7f;
+	ah_attr->src_path_bits = be16_to_cpu(rec->slid) &
+				 get_src_path_mask(device, port_num);
 	ah_attr->port_num = port_num;
 	ah_attr->static_rate = rec->rate;
 
diff --git a/drivers/infiniband/core/smi.c b/drivers/infiniband/core/smi.c
index 54b81e1..2bca753 100644
--- a/drivers/infiniband/core/smi.c
+++ b/drivers/infiniband/core/smi.c
@@ -3,7 +3,7 @@
  * Copyright (c) 2004, 2005 Infinicon Corporation.  All rights reserved.
  * Copyright (c) 2004, 2005 Intel Corporation.  All rights reserved.
  * Copyright (c) 2004, 2005 Topspin Corporation.  All rights reserved.
- * Copyright (c) 2004, 2005 Voltaire Corporation.  All rights reserved.
+ * Copyright (c) 2004-2007 Voltaire Corporation.  All rights reserved.
  * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
@@ -34,7 +34,6 @@
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  *
- * $Id: smi.c 1389 2004-12-27 22:56:47Z roland $
  */
 
 #include <rdma/ib_smi.h>
@@ -44,9 +43,8 @@
  * Fixup a directed route SMP for sending
  * Return 0 if the SMP should be discarded
  */
-int smi_handle_dr_smp_send(struct ib_smp *smp,
-			   u8 node_type,
-			   int port_num)
+enum smi_action smi_handle_dr_smp_send(struct ib_smp *smp,
+				       u8 node_type, int port_num)
 {
 	u8 hop_ptr, hop_cnt;
 
@@ -59,18 +57,18 @@
 		if (hop_cnt && hop_ptr == 0) {
 			smp->hop_ptr++;
 			return (smp->initial_path[smp->hop_ptr] ==
-				port_num);
+				port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
 		}
 
 		/* C14-9:2 */
 		if (hop_ptr && hop_ptr < hop_cnt) {
 			if (node_type != RDMA_NODE_IB_SWITCH)
-				return 0;
+				return IB_SMI_DISCARD;
 
 			/* smp->return_path set when received */
 			smp->hop_ptr++;
 			return (smp->initial_path[smp->hop_ptr] ==
-				port_num);
+				port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
 		}
 
 		/* C14-9:3 -- We're at the end of the DR segment of path */
@@ -78,29 +76,30 @@
 			/* smp->return_path set when received */
 			smp->hop_ptr++;
 			return (node_type == RDMA_NODE_IB_SWITCH ||
-				smp->dr_dlid == IB_LID_PERMISSIVE);
+				smp->dr_dlid == IB_LID_PERMISSIVE ?
+				IB_SMI_HANDLE : IB_SMI_DISCARD);
 		}
 
 		/* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
 		/* C14-9:5 -- Fail unreasonable hop pointer */
-		return (hop_ptr == hop_cnt + 1);
+		return (hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
 
 	} else {
 		/* C14-13:1 */
 		if (hop_cnt && hop_ptr == hop_cnt + 1) {
 			smp->hop_ptr--;
 			return (smp->return_path[smp->hop_ptr] ==
-				port_num);
+				port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
 		}
 
 		/* C14-13:2 */
 		if (2 <= hop_ptr && hop_ptr <= hop_cnt) {
 			if (node_type != RDMA_NODE_IB_SWITCH)
-				return 0;
+				return IB_SMI_DISCARD;
 
 			smp->hop_ptr--;
 			return (smp->return_path[smp->hop_ptr] ==
-				port_num);
+				port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
 		}
 
 		/* C14-13:3 -- at the end of the DR segment of path */
@@ -108,15 +107,16 @@
 			smp->hop_ptr--;
 			/* C14-13:3 -- SMPs destined for SM shouldn't be here */
 			return (node_type == RDMA_NODE_IB_SWITCH ||
-				smp->dr_slid == IB_LID_PERMISSIVE);
+				smp->dr_slid == IB_LID_PERMISSIVE ?
+				IB_SMI_HANDLE : IB_SMI_DISCARD);
 		}
 
 		/* C14-13:4 -- hop_ptr = 0 -> should have gone to SM */
 		if (hop_ptr == 0)
-			return 1;
+			return IB_SMI_HANDLE;
 
 		/* C14-13:5 -- Check for unreasonable hop pointer */
-		return 0;
+		return IB_SMI_DISCARD;
 	}
 }
 
@@ -124,10 +124,8 @@
  * Adjust information for a received SMP
  * Return 0 if the SMP should be dropped
  */
-int smi_handle_dr_smp_recv(struct ib_smp *smp,
-			   u8 node_type,
-			   int port_num,
-			   int phys_port_cnt)
+enum smi_action smi_handle_dr_smp_recv(struct ib_smp *smp, u8 node_type,
+				       int port_num, int phys_port_cnt)
 {
 	u8 hop_ptr, hop_cnt;
 
@@ -138,16 +136,17 @@
 	if (!ib_get_smp_direction(smp)) {
 		/* C14-9:1 -- sender should have incremented hop_ptr */
 		if (hop_cnt && hop_ptr == 0)
-			return 0;
+			return IB_SMI_DISCARD;
 
 		/* C14-9:2 -- intermediate hop */
 		if (hop_ptr && hop_ptr < hop_cnt) {
 			if (node_type != RDMA_NODE_IB_SWITCH)
-				return 0;
+				return IB_SMI_DISCARD;
 
 			smp->return_path[hop_ptr] = port_num;
 			/* smp->hop_ptr updated when sending */
-			return (smp->initial_path[hop_ptr+1] <= phys_port_cnt);
+			return (smp->initial_path[hop_ptr+1] <= phys_port_cnt ?
+				IB_SMI_HANDLE : IB_SMI_DISCARD);
 		}
 
 		/* C14-9:3 -- We're at the end of the DR segment of path */
@@ -157,12 +156,13 @@
 			/* smp->hop_ptr updated when sending */
 
 			return (node_type == RDMA_NODE_IB_SWITCH ||
-				smp->dr_dlid == IB_LID_PERMISSIVE);
+				smp->dr_dlid == IB_LID_PERMISSIVE ?
+				IB_SMI_HANDLE : IB_SMI_DISCARD);
 		}
 
 		/* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
 		/* C14-9:5 -- fail unreasonable hop pointer */
-		return (hop_ptr == hop_cnt + 1);
+		return (hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
 
 	} else {
 
@@ -170,16 +170,17 @@
 		if (hop_cnt && hop_ptr == hop_cnt + 1) {
 			smp->hop_ptr--;
 			return (smp->return_path[smp->hop_ptr] ==
-				port_num);
+				port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
 		}
 
 		/* C14-13:2 */
 		if (2 <= hop_ptr && hop_ptr <= hop_cnt) {
 			if (node_type != RDMA_NODE_IB_SWITCH)
-				return 0;
+				return IB_SMI_DISCARD;
 
 			/* smp->hop_ptr updated when sending */
-			return (smp->return_path[hop_ptr-1] <= phys_port_cnt);
+			return (smp->return_path[hop_ptr-1] <= phys_port_cnt ?
+				IB_SMI_HANDLE : IB_SMI_DISCARD);
 		}
 
 		/* C14-13:3 -- We're at the end of the DR segment of path */
@@ -187,23 +188,20 @@
 			if (smp->dr_slid == IB_LID_PERMISSIVE) {
 				/* giving SMP to SM - update hop_ptr */
 				smp->hop_ptr--;
-				return 1;
+				return IB_SMI_HANDLE;
 			}
 			/* smp->hop_ptr updated when sending */
-			return (node_type == RDMA_NODE_IB_SWITCH);
+			return (node_type == RDMA_NODE_IB_SWITCH ?
+				IB_SMI_HANDLE: IB_SMI_DISCARD);
 		}
 
 		/* C14-13:4 -- hop_ptr = 0 -> give to SM */
 		/* C14-13:5 -- Check for unreasonable hop pointer */
-		return (hop_ptr == 0);
+		return (hop_ptr == 0 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
 	}
 }
 
-/*
- * Return 1 if the received DR SMP should be forwarded to the send queue
- * Return 0 if the SMP should be completed up the stack
- */
-int smi_check_forward_dr_smp(struct ib_smp *smp)
+enum smi_forward_action smi_check_forward_dr_smp(struct ib_smp *smp)
 {
 	u8 hop_ptr, hop_cnt;
 
@@ -213,23 +211,25 @@
 	if (!ib_get_smp_direction(smp)) {
 		/* C14-9:2 -- intermediate hop */
 		if (hop_ptr && hop_ptr < hop_cnt)
-			return 1;
+			return IB_SMI_SEND;
 
 		/* C14-9:3 -- at the end of the DR segment of path */
 		if (hop_ptr == hop_cnt)
-			return (smp->dr_dlid == IB_LID_PERMISSIVE);
+			return (smp->dr_dlid == IB_LID_PERMISSIVE ?
+				IB_SMI_SEND : IB_SMI_LOCAL);
 
 		/* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
 		if (hop_ptr == hop_cnt + 1)
-			return 1;
+			return IB_SMI_SEND;
 	} else {
-		/* C14-13:2 */
+		/* C14-13:2  -- intermediate hop */
 		if (2 <= hop_ptr && hop_ptr <= hop_cnt)
-			return 1;
+			return IB_SMI_SEND;
 
 		/* C14-13:3 -- at the end of the DR segment of path */
 		if (hop_ptr == 1)
-			return (smp->dr_slid != IB_LID_PERMISSIVE);
+			return (smp->dr_slid != IB_LID_PERMISSIVE ?
+				IB_SMI_SEND : IB_SMI_LOCAL);
 	}
-	return 0;
+	return IB_SMI_LOCAL;
 }
diff --git a/drivers/infiniband/core/smi.h b/drivers/infiniband/core/smi.h
index 3011bfd..9a4b349 100644
--- a/drivers/infiniband/core/smi.h
+++ b/drivers/infiniband/core/smi.h
@@ -3,7 +3,7 @@
  * Copyright (c) 2004 Infinicon Corporation.  All rights reserved.
  * Copyright (c) 2004 Intel Corporation.  All rights reserved.
  * Copyright (c) 2004 Topspin Corporation.  All rights reserved.
- * Copyright (c) 2004 Voltaire Corporation.  All rights reserved.
+ * Copyright (c) 2004-2007 Voltaire Corporation.  All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -33,7 +33,6 @@
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  *
- * $Id: smi.h 1389 2004-12-27 22:56:47Z roland $
  */
 
 #ifndef __SMI_H_
@@ -41,26 +40,33 @@
 
 #include <rdma/ib_smi.h>
 
-int smi_handle_dr_smp_recv(struct ib_smp *smp,
-			   u8 node_type,
-			   int port_num,
-			   int phys_port_cnt);
-extern int smi_check_forward_dr_smp(struct ib_smp *smp);
-extern int smi_handle_dr_smp_send(struct ib_smp *smp,
-				  u8 node_type,
-				  int port_num);
+enum smi_action {
+	IB_SMI_DISCARD,
+	IB_SMI_HANDLE
+};
+
+enum smi_forward_action {
+	IB_SMI_LOCAL,	/* SMP should be completed up the stack */
+	IB_SMI_SEND,	/* received DR SMP should be forwarded to the send queue */
+};
+
+enum smi_action smi_handle_dr_smp_recv(struct ib_smp *smp, u8 node_type,
+				       int port_num, int phys_port_cnt);
+extern enum smi_forward_action smi_check_forward_dr_smp(struct ib_smp *smp);
+extern enum smi_action smi_handle_dr_smp_send(struct ib_smp *smp,
+					      u8 node_type, int port_num);
 
 /*
  * Return 1 if the SMP should be handled by the local SMA/SM via process_mad
  */
-static inline int smi_check_local_smp(struct ib_smp *smp,
-				      struct ib_device *device)
+static inline enum smi_action smi_check_local_smp(struct ib_smp *smp,
+						  struct ib_device *device)
 {
 	/* C14-9:3 -- We're at the end of the DR segment of path */
 	/* C14-9:4 -- Hop Pointer = Hop Count + 1 -> give to SMA/SM */
 	return ((device->process_mad &&
 		!ib_get_smp_direction(smp) &&
-		(smp->hop_ptr == smp->hop_cnt + 1)));
+		(smp->hop_ptr == smp->hop_cnt + 1)) ?
+		IB_SMI_HANDLE : IB_SMI_DISCARD);
 }
-
 #endif	/* __SMI_H_ */
diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c
index 000c086..08c299e 100644
--- a/drivers/infiniband/core/sysfs.c
+++ b/drivers/infiniband/core/sysfs.c
@@ -683,6 +683,7 @@
 
 	class_dev->class      = &ib_class;
 	class_dev->class_data = device;
+	class_dev->dev	      = device->dma_device;
 	strlcpy(class_dev->class_id, device->name, BUS_ID_SIZE);
 
 	INIT_LIST_HEAD(&device->port_list);
diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c
index ee51d79..2586a3e 100644
--- a/drivers/infiniband/core/ucm.c
+++ b/drivers/infiniband/core/ucm.c
@@ -407,29 +407,18 @@
 
 	mutex_lock(&file->file_mutex);
 	while (list_empty(&file->events)) {
-
-		if (file->filp->f_flags & O_NONBLOCK) {
-			result = -EAGAIN;
-			break;
-		}
-
-		if (signal_pending(current)) {
-			result = -ERESTARTSYS;
-			break;
-		}
-
-		prepare_to_wait(&file->poll_wait, &wait, TASK_INTERRUPTIBLE);
-
 		mutex_unlock(&file->file_mutex);
-		schedule();
+
+		if (file->filp->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		if (wait_event_interruptible(file->poll_wait,
+					     !list_empty(&file->events)))
+			return -ERESTARTSYS;
+
 		mutex_lock(&file->file_mutex);
-
-		finish_wait(&file->poll_wait, &wait);
 	}
 
-	if (result)
-		goto done;
-
 	uevent = list_entry(file->events.next, struct ib_ucm_event, file_list);
 
 	if (ib_ucm_new_cm_id(uevent->resp.event)) {
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index c859134..53b4c94 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -306,25 +306,17 @@
 
 	mutex_lock(&file->mut);
 	while (list_empty(&file->event_list)) {
-		if (file->filp->f_flags & O_NONBLOCK) {
-			ret = -EAGAIN;
-			break;
-		}
-
-		if (signal_pending(current)) {
-			ret = -ERESTARTSYS;
-			break;
-		}
-
-		prepare_to_wait(&file->poll_wait, &wait, TASK_INTERRUPTIBLE);
 		mutex_unlock(&file->mut);
-		schedule();
-		mutex_lock(&file->mut);
-		finish_wait(&file->poll_wait, &wait);
-	}
 
-	if (ret)
-		goto done;
+		if (file->filp->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		if (wait_event_interruptible(file->poll_wait,
+					     !list_empty(&file->event_list)))
+			return -ERESTARTSYS;
+
+		mutex_lock(&file->mut);
+	}
 
 	uevent = list_entry(file->event_list.next, struct ucma_event, list);
 
diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c
index c069ebeb..8199b83 100644
--- a/drivers/infiniband/core/user_mad.c
+++ b/drivers/infiniband/core/user_mad.c
@@ -135,7 +135,7 @@
 
 static DEFINE_SPINLOCK(port_lock);
 static struct ib_umad_port *umad_port[IB_UMAD_MAX_PORTS];
-static DECLARE_BITMAP(dev_map, IB_UMAD_MAX_PORTS * 2);
+static DECLARE_BITMAP(dev_map, IB_UMAD_MAX_PORTS);
 
 static void ib_umad_add_one(struct ib_device *device);
 static void ib_umad_remove_one(struct ib_device *device);
@@ -231,12 +231,17 @@
 	packet->mad.hdr.path_bits = mad_recv_wc->wc->dlid_path_bits;
 	packet->mad.hdr.grh_present = !!(mad_recv_wc->wc->wc_flags & IB_WC_GRH);
 	if (packet->mad.hdr.grh_present) {
-		/* XXX parse GRH */
-		packet->mad.hdr.gid_index 	= 0;
-		packet->mad.hdr.hop_limit 	= 0;
-		packet->mad.hdr.traffic_class	= 0;
-		memset(packet->mad.hdr.gid, 0, 16);
-		packet->mad.hdr.flow_label	= 0;
+		struct ib_ah_attr ah_attr;
+
+		ib_init_ah_from_wc(agent->device, agent->port_num,
+				   mad_recv_wc->wc, mad_recv_wc->recv_buf.grh,
+				   &ah_attr);
+
+		packet->mad.hdr.gid_index = ah_attr.grh.sgid_index;
+		packet->mad.hdr.hop_limit = ah_attr.grh.hop_limit;
+		packet->mad.hdr.traffic_class = ah_attr.grh.traffic_class;
+		memcpy(packet->mad.hdr.gid, &ah_attr.grh.dgid, 16);
+		packet->mad.hdr.flow_label = cpu_to_be32(ah_attr.grh.flow_label);
 	}
 
 	if (queue_packet(file, agent, packet))
@@ -473,6 +478,7 @@
 	if (packet->mad.hdr.grh_present) {
 		ah_attr.ah_flags = IB_AH_GRH;
 		memcpy(ah_attr.grh.dgid.raw, packet->mad.hdr.gid, 16);
+		ah_attr.grh.sgid_index	   = packet->mad.hdr.gid_index;
 		ah_attr.grh.flow_label 	   = be32_to_cpu(packet->mad.hdr.flow_label);
 		ah_attr.grh.hop_limit  	   = packet->mad.hdr.hop_limit;
 		ah_attr.grh.traffic_class  = packet->mad.hdr.traffic_class;
diff --git a/drivers/infiniband/hw/amso1100/c2.c b/drivers/infiniband/hw/amso1100/c2.c
index 59243d9..58bc272 100644
--- a/drivers/infiniband/hw/amso1100/c2.c
+++ b/drivers/infiniband/hw/amso1100/c2.c
@@ -439,7 +439,8 @@
 	}
 
 	/* Setup the skb for reuse since we're dropping this pkt */
-	elem->skb->tail = elem->skb->data = elem->skb->head;
+	elem->skb->data = elem->skb->head;
+	skb_reset_tail_pointer(elem->skb);
 
 	/* Zero out the rxp hdr in the sk_buff */
 	memset(elem->skb->data, 0, sizeof(*rxp_hdr));
@@ -521,9 +522,8 @@
 		 * "sizeof(struct c2_rxp_hdr)".
 		 */
 		skb->data += sizeof(*rxp_hdr);
-		skb->tail = skb->data + buflen;
+		skb_set_tail_pointer(skb, buflen);
 		skb->len = buflen;
-		skb->dev = netdev;
 		skb->protocol = eth_type_trans(skb, netdev);
 
 		netif_rx(skb);
diff --git a/drivers/infiniband/hw/amso1100/c2_provider.c b/drivers/infiniband/hw/amso1100/c2_provider.c
index fef9727..607c09b 100644
--- a/drivers/infiniband/hw/amso1100/c2_provider.c
+++ b/drivers/infiniband/hw/amso1100/c2_provider.c
@@ -796,7 +796,6 @@
 	memcpy(&dev->ibdev.node_guid, dev->pseudo_netdev->dev_addr, 6);
 	dev->ibdev.phys_port_cnt = 1;
 	dev->ibdev.dma_device = &dev->pcidev->dev;
-	dev->ibdev.class_dev.dev = &dev->pcidev->dev;
 	dev->ibdev.query_device = c2_query_device;
 	dev->ibdev.query_port = c2_query_port;
 	dev->ibdev.modify_port = c2_modify_port;
diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c
index 2d2de9b..3b4b0ac 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_cm.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c
@@ -477,7 +477,7 @@
 	BUG_ON(skb_cloned(skb));
 
 	mpalen = sizeof(*mpa) + ep->plen;
-	if (skb->data + mpalen + sizeof(*req) > skb->end) {
+	if (skb->data + mpalen + sizeof(*req) > skb_end_pointer(skb)) {
 		kfree_skb(skb);
 		skb=alloc_skb(mpalen + sizeof(*req), GFP_KERNEL);
 		if (!skb) {
@@ -507,7 +507,7 @@
 	 */
 	skb_get(skb);
 	set_arp_failure_handler(skb, arp_failure_discard);
-	skb->h.raw = skb->data;
+	skb_reset_transport_header(skb);
 	len = skb->len;
 	req = (struct tx_data_wr *) skb_push(skb, sizeof(*req));
 	req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_TX_DATA));
@@ -559,7 +559,7 @@
 	skb_get(skb);
 	skb->priority = CPL_PRIORITY_DATA;
 	set_arp_failure_handler(skb, arp_failure_discard);
-	skb->h.raw = skb->data;
+	skb_reset_transport_header(skb);
 	req = (struct tx_data_wr *) skb_push(skb, sizeof(*req));
 	req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_TX_DATA));
 	req->wr_lo = htonl(V_WR_TID(ep->hwtid));
@@ -610,7 +610,7 @@
 	 */
 	skb_get(skb);
 	set_arp_failure_handler(skb, arp_failure_discard);
-	skb->h.raw = skb->data;
+	skb_reset_transport_header(skb);
 	len = skb->len;
 	req = (struct tx_data_wr *) skb_push(skb, sizeof(*req));
 	req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_TX_DATA));
@@ -821,7 +821,8 @@
 	/*
 	 * copy the new data into our accumulation buffer.
 	 */
-	memcpy(&(ep->mpa_pkt[ep->mpa_pkt_len]), skb->data, skb->len);
+	skb_copy_from_linear_data(skb, &(ep->mpa_pkt[ep->mpa_pkt_len]),
+				  skb->len);
 	ep->mpa_pkt_len += skb->len;
 
 	/*
@@ -940,7 +941,8 @@
 	/*
 	 * Copy the new data into our accumulation buffer.
 	 */
-	memcpy(&(ep->mpa_pkt[ep->mpa_pkt_len]), skb->data, skb->len);
+	skb_copy_from_linear_data(skb, &(ep->mpa_pkt[ep->mpa_pkt_len]),
+				  skb->len);
 	ep->mpa_pkt_len += skb->len;
 
 	/*
@@ -1619,7 +1621,8 @@
 	PDBG("%s ep %p\n", __FUNCTION__, ep);
 	skb_pull(skb, sizeof(struct cpl_rdma_terminate));
 	PDBG("%s saving %d bytes of term msg\n", __FUNCTION__, skb->len);
-	memcpy(ep->com.qp->attr.terminate_buffer, skb->data, skb->len);
+	skb_copy_from_linear_data(skb, ep->com.qp->attr.terminate_buffer,
+				  skb->len);
 	ep->com.qp->attr.terminate_msg_len = skb->len;
 	ep->com.qp->attr.is_terminate_local = 0;
 	return CPL_RET_BUF_DONE;
diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c
index 24e0df0..af28a31 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_provider.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c
@@ -1108,7 +1108,6 @@
 	memcpy(dev->ibdev.node_desc, IWCH_NODE_DESC, sizeof(IWCH_NODE_DESC));
 	dev->ibdev.phys_port_cnt = dev->rdev.port_info.nports;
 	dev->ibdev.dma_device = &(dev->rdev.rnic_info.pdev->dev);
-	dev->ibdev.class_dev.dev = &(dev->rdev.rnic_info.pdev->dev);
 	dev->ibdev.query_device = iwch_query_device;
 	dev->ibdev.query_port = iwch_query_port;
 	dev->ibdev.modify_port = iwch_modify_port;
diff --git a/drivers/infiniband/hw/ehca/ehca_classes.h b/drivers/infiniband/hw/ehca/ehca_classes.h
index 82ded44..10fb8fb 100644
--- a/drivers/infiniband/hw/ehca/ehca_classes.h
+++ b/drivers/infiniband/hw/ehca/ehca_classes.h
@@ -106,6 +106,7 @@
 	struct ehca_mr *maxmr;
 	struct ehca_pd *pd;
 	struct h_galpas galpas;
+	struct mutex modify_mutex;
 };
 
 struct ehca_pd {
diff --git a/drivers/infiniband/hw/ehca/ehca_hca.c b/drivers/infiniband/hw/ehca/ehca_hca.c
index 30eb45d..32b55a4 100644
--- a/drivers/infiniband/hw/ehca/ehca_hca.c
+++ b/drivers/infiniband/hw/ehca/ehca_hca.c
@@ -147,6 +147,7 @@
 		break;
 	}
 
+	props->port_cap_flags  = rblock->capability_mask;
 	props->gid_tbl_len     = rblock->gid_tbl_len;
 	props->max_msg_sz      = rblock->max_msg_sz;
 	props->bad_pkey_cntr   = rblock->bad_pkey_cntr;
@@ -236,10 +237,60 @@
 	return ret;
 }
 
+const u32 allowed_port_caps = (
+	IB_PORT_SM | IB_PORT_LED_INFO_SUP | IB_PORT_CM_SUP |
+	IB_PORT_SNMP_TUNNEL_SUP | IB_PORT_DEVICE_MGMT_SUP |
+	IB_PORT_VENDOR_CLASS_SUP);
+
 int ehca_modify_port(struct ib_device *ibdev,
 		     u8 port, int port_modify_mask,
 		     struct ib_port_modify *props)
 {
-	/* Not implemented yet */
-	return -EFAULT;
+	int ret = 0;
+	struct ehca_shca *shca = container_of(ibdev, struct ehca_shca, ib_device);
+	struct hipz_query_port *rblock;
+	u32 cap;
+	u64 hret;
+
+	if ((props->set_port_cap_mask | props->clr_port_cap_mask)
+	    & ~allowed_port_caps) {
+		ehca_err(&shca->ib_device, "Non-changeable bits set in masks  "
+			 "set=%x  clr=%x  allowed=%x", props->set_port_cap_mask,
+			 props->clr_port_cap_mask, allowed_port_caps);
+		return -EINVAL;
+	}
+
+	if (mutex_lock_interruptible(&shca->modify_mutex))
+                return -ERESTARTSYS;
+
+	rblock = ehca_alloc_fw_ctrlblock(GFP_KERNEL);
+	if (!rblock) {
+		ehca_err(&shca->ib_device,  "Can't allocate rblock memory.");
+		ret = -ENOMEM;
+		goto modify_port1;
+	}
+
+	if (hipz_h_query_port(shca->ipz_hca_handle, port, rblock) != H_SUCCESS) {
+		ehca_err(&shca->ib_device, "Can't query port properties");
+		ret = -EINVAL;
+		goto modify_port2;
+	}
+
+	cap = (rblock->capability_mask | props->set_port_cap_mask)
+		& ~props->clr_port_cap_mask;
+
+	hret = hipz_h_modify_port(shca->ipz_hca_handle, port,
+				  cap, props->init_type, port_modify_mask);
+	if (hret != H_SUCCESS) {
+		ehca_err(&shca->ib_device, "Modify port failed  hret=%lx", hret);
+		ret = -EINVAL;
+	}
+
+modify_port2:
+	ehca_free_fw_ctrlblock(rblock);
+
+modify_port1:
+        mutex_unlock(&shca->modify_mutex);
+
+	return ret;
 }
diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c
index 059da96..4700085 100644
--- a/drivers/infiniband/hw/ehca/ehca_main.c
+++ b/drivers/infiniband/hw/ehca/ehca_main.c
@@ -565,11 +565,11 @@
 				const struct of_device_id *id)
 {
 	struct ehca_shca *shca;
-	u64 *handle;
+	const u64 *handle;
 	struct ib_pd *ibpd;
 	int ret;
 
-	handle = (u64 *)get_property(dev->ofdev.node, "ibm,hca-handle", NULL);
+	handle = get_property(dev->ofdev.node, "ibm,hca-handle", NULL);
 	if (!handle) {
 		ehca_gen_err("Cannot get eHCA handle for adapter: %s.",
 			     dev->ofdev.node->full_name);
@@ -587,6 +587,7 @@
 		ehca_gen_err("Cannot allocate shca memory.");
 		return -ENOMEM;
 	}
+	mutex_init(&shca->modify_mutex);
 
 	shca->ibmebus_dev = dev;
 	shca->ipz_hca_handle.handle = *handle;
diff --git a/drivers/infiniband/hw/ehca/hcp_if.c b/drivers/infiniband/hw/ehca/hcp_if.c
index 3fb46e6..b564fcd 100644
--- a/drivers/infiniband/hw/ehca/hcp_if.c
+++ b/drivers/infiniband/hw/ehca/hcp_if.c
@@ -70,6 +70,10 @@
 #define H_ALL_RES_QP_SQUEUE_SIZE_PAGES  EHCA_BMASK_IBM(0, 31)
 #define H_ALL_RES_QP_RQUEUE_SIZE_PAGES  EHCA_BMASK_IBM(32, 63)
 
+#define H_MP_INIT_TYPE                  EHCA_BMASK_IBM(44, 47)
+#define H_MP_SHUTDOWN                   EHCA_BMASK_IBM(48, 48)
+#define H_MP_RESET_QKEY_CTR             EHCA_BMASK_IBM(49, 49)
+
 /* direct access qp controls */
 #define DAQP_CTRL_ENABLE    0x01
 #define DAQP_CTRL_SEND_COMP 0x20
@@ -364,6 +368,26 @@
 	return ret;
 }
 
+u64 hipz_h_modify_port(const struct ipz_adapter_handle adapter_handle,
+		       const u8 port_id, const u32 port_cap,
+		       const u8 init_type, const int modify_mask)
+{
+	u64 port_attributes = port_cap;
+
+	if (modify_mask & IB_PORT_SHUTDOWN)
+		port_attributes |= EHCA_BMASK_SET(H_MP_SHUTDOWN, 1);
+	if (modify_mask & IB_PORT_INIT_TYPE)
+		port_attributes |= EHCA_BMASK_SET(H_MP_INIT_TYPE, init_type);
+	if (modify_mask & IB_PORT_RESET_QKEY_CNTR)
+		port_attributes |= EHCA_BMASK_SET(H_MP_RESET_QKEY_CTR, 1);
+
+	return ehca_plpar_hcall_norets(H_MODIFY_PORT,
+				       adapter_handle.handle, /* r4 */
+				       port_id,               /* r5 */
+				       port_attributes,       /* r6 */
+				       0, 0, 0, 0);
+}
+
 u64 hipz_h_query_hca(const struct ipz_adapter_handle adapter_handle,
 		     struct hipz_query_hca *query_hca_rblock)
 {
diff --git a/drivers/infiniband/hw/ehca/hcp_if.h b/drivers/infiniband/hw/ehca/hcp_if.h
index 587ebd4..2869f7d 100644
--- a/drivers/infiniband/hw/ehca/hcp_if.h
+++ b/drivers/infiniband/hw/ehca/hcp_if.h
@@ -85,6 +85,10 @@
 		      const u8 port_id,
 		      struct hipz_query_port *query_port_response_block);
 
+u64 hipz_h_modify_port(const struct ipz_adapter_handle adapter_handle,
+		       const u8 port_id, const u32 port_cap,
+		       const u8 init_type, const int modify_mask);
+
 u64 hipz_h_query_hca(const struct ipz_adapter_handle adapter_handle,
 		     struct hipz_query_hca *query_hca_rblock);
 
diff --git a/drivers/infiniband/hw/ipath/ipath_common.h b/drivers/infiniband/hw/ipath/ipath_common.h
index 54139d3..10c008f 100644
--- a/drivers/infiniband/hw/ipath/ipath_common.h
+++ b/drivers/infiniband/hw/ipath/ipath_common.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 QLogic, Inc. All rights reserved.
+ * Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved.
  * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
@@ -78,6 +78,8 @@
 #define IPATH_IB_LINKINIT		3
 #define IPATH_IB_LINKDOWN_SLEEP		4
 #define IPATH_IB_LINKDOWN_DISABLE	5
+#define IPATH_IB_LINK_LOOPBACK	6 /* enable local loopback */
+#define IPATH_IB_LINK_EXTERNAL	7 /* normal, disable local loopback */
 
 /*
  * stats maintained by the driver.  For now, at least, this is global
@@ -316,11 +318,17 @@
 	/* address of readonly memory copy of the rcvhdrq tail register. */
 	__u64 spi_rcvhdr_tailaddr;
 
-	/* shared memory pages for subports if IPATH_RUNTIME_MASTER is set */
+	/* shared memory pages for subports if port is shared */
 	__u64 spi_subport_uregbase;
 	__u64 spi_subport_rcvegrbuf;
 	__u64 spi_subport_rcvhdr_base;
 
+	/* shared memory page for hardware port if it is shared */
+	__u64 spi_port_uregbase;
+	__u64 spi_port_rcvegrbuf;
+	__u64 spi_port_rcvhdr_base;
+	__u64 spi_port_rcvhdr_tailaddr;
+
 } __attribute__ ((aligned(8)));
 
 
@@ -344,7 +352,7 @@
  * may not be implemented; the user code must deal with this if it
  * cares, or it must abort after initialization reports the difference.
  */
-#define IPATH_USER_SWMINOR 3
+#define IPATH_USER_SWMINOR 5
 
 #define IPATH_USER_SWVERSION ((IPATH_USER_SWMAJOR<<16) | IPATH_USER_SWMINOR)
 
@@ -418,11 +426,14 @@
 #define IPATH_CMD_TID_UPDATE	19	/* update expected TID entries */
 #define IPATH_CMD_TID_FREE	20	/* free expected TID entries */
 #define IPATH_CMD_SET_PART_KEY	21	/* add partition key */
-#define IPATH_CMD_SLAVE_INFO	22	/* return info on slave processes */
+#define __IPATH_CMD_SLAVE_INFO	22	/* return info on slave processes (for old user code) */
 #define IPATH_CMD_ASSIGN_PORT	23	/* allocate HCA and port */
 #define IPATH_CMD_USER_INIT 	24	/* set up userspace */
+#define IPATH_CMD_UNUSED_1	25
+#define IPATH_CMD_UNUSED_2	26
+#define IPATH_CMD_PIOAVAILUPD	27	/* force an update of PIOAvail reg */
 
-#define IPATH_CMD_MAX		24
+#define IPATH_CMD_MAX		27
 
 struct ipath_port_info {
 	__u32 num_active;	/* number of active units */
@@ -430,7 +441,7 @@
 	__u16 port;		/* port on unit assigned to caller */
 	__u16 subport;		/* subport on unit assigned to caller */
 	__u16 num_ports;	/* number of ports available on unit */
-	__u16 num_subports;	/* number of subport slaves opened on port */
+	__u16 num_subports;	/* number of subports opened on port */
 };
 
 struct ipath_tid_info {
diff --git a/drivers/infiniband/hw/ipath/ipath_cq.c b/drivers/infiniband/hw/ipath/ipath_cq.c
index 87462e0..ea78e6d 100644
--- a/drivers/infiniband/hw/ipath/ipath_cq.c
+++ b/drivers/infiniband/hw/ipath/ipath_cq.c
@@ -76,7 +76,20 @@
 		}
 		return;
 	}
-	wc->queue[head] = *entry;
+	wc->queue[head].wr_id = entry->wr_id;
+	wc->queue[head].status = entry->status;
+	wc->queue[head].opcode = entry->opcode;
+	wc->queue[head].vendor_err = entry->vendor_err;
+	wc->queue[head].byte_len = entry->byte_len;
+	wc->queue[head].imm_data = (__u32 __force)entry->imm_data;
+	wc->queue[head].qp_num = entry->qp->qp_num;
+	wc->queue[head].src_qp = entry->src_qp;
+	wc->queue[head].wc_flags = entry->wc_flags;
+	wc->queue[head].pkey_index = entry->pkey_index;
+	wc->queue[head].slid = entry->slid;
+	wc->queue[head].sl = entry->sl;
+	wc->queue[head].dlid_path_bits = entry->dlid_path_bits;
+	wc->queue[head].port_num = entry->port_num;
 	wc->head = next;
 
 	if (cq->notify == IB_CQ_NEXT_COMP ||
@@ -122,9 +135,30 @@
 	if (tail > (u32) cq->ibcq.cqe)
 		tail = (u32) cq->ibcq.cqe;
 	for (npolled = 0; npolled < num_entries; ++npolled, ++entry) {
+		struct ipath_qp *qp;
+
 		if (tail == wc->head)
 			break;
-		*entry = wc->queue[tail];
+
+		qp = ipath_lookup_qpn(&to_idev(cq->ibcq.device)->qp_table,
+				      wc->queue[tail].qp_num);
+		entry->qp = &qp->ibqp;
+		if (atomic_dec_and_test(&qp->refcount))
+			wake_up(&qp->wait);
+
+		entry->wr_id = wc->queue[tail].wr_id;
+		entry->status = wc->queue[tail].status;
+		entry->opcode = wc->queue[tail].opcode;
+		entry->vendor_err = wc->queue[tail].vendor_err;
+		entry->byte_len = wc->queue[tail].byte_len;
+		entry->imm_data = wc->queue[tail].imm_data;
+		entry->src_qp = wc->queue[tail].src_qp;
+		entry->wc_flags = wc->queue[tail].wc_flags;
+		entry->pkey_index = wc->queue[tail].pkey_index;
+		entry->slid = wc->queue[tail].slid;
+		entry->sl = wc->queue[tail].sl;
+		entry->dlid_path_bits = wc->queue[tail].dlid_path_bits;
+		entry->port_num = wc->queue[tail].port_num;
 		if (tail >= cq->ibcq.cqe)
 			tail = 0;
 		else
diff --git a/drivers/infiniband/hw/ipath/ipath_debug.h b/drivers/infiniband/hw/ipath/ipath_debug.h
index df69f0d8..42bfbdb 100644
--- a/drivers/infiniband/hw/ipath/ipath_debug.h
+++ b/drivers/infiniband/hw/ipath/ipath_debug.h
@@ -57,6 +57,7 @@
 #define __IPATH_PROCDBG     0x100
 /* print mmap/nopage stuff, not using VDBG any more */
 #define __IPATH_MMDBG       0x200
+#define __IPATH_ERRPKTDBG   0x400
 #define __IPATH_USER_SEND   0x1000	/* use user mode send */
 #define __IPATH_KERNEL_SEND 0x2000	/* use kernel mode send */
 #define __IPATH_EPKTDBG     0x4000	/* print ethernet packet data */
diff --git a/drivers/infiniband/hw/ipath/ipath_diag.c b/drivers/infiniband/hw/ipath/ipath_diag.c
index 0f13a21..63e8368 100644
--- a/drivers/infiniband/hw/ipath/ipath_diag.c
+++ b/drivers/infiniband/hw/ipath/ipath_diag.c
@@ -296,7 +296,7 @@
 	}
 
 	fp->private_data = dd;
-	ipath_diag_inuse = 1;
+	ipath_diag_inuse = -2;
 	diag_set_link = 0;
 	ret = 0;
 
@@ -461,6 +461,8 @@
 	else if ((count % 4) || (*off % 4))
 		/* address or length is not 32-bit aligned, hence invalid */
 		ret = -EINVAL;
+	else if (ipath_diag_inuse < 1 && (*off || count != 8))
+		ret = -EINVAL;  /* prevent cat /dev/ipath_diag* */
 	else if ((count % 8) || (*off % 8))
 		/* address or length not 64-bit aligned; do 32-bit reads */
 		ret = ipath_read_umem32(dd, data, kreg_base + *off, count);
@@ -470,6 +472,8 @@
 	if (ret >= 0) {
 		*off += count;
 		ret = count;
+		if (ipath_diag_inuse == -2)
+			ipath_diag_inuse++;
 	}
 
 	return ret;
@@ -489,6 +493,9 @@
 	else if ((count % 4) || (*off % 4))
 		/* address or length is not 32-bit aligned, hence invalid */
 		ret = -EINVAL;
+	else if ((ipath_diag_inuse == -1 && (*off || count != 8)) ||
+		 ipath_diag_inuse == -2)  /* read qw off 0, write qw off 0 */
+		ret = -EINVAL;  /* before any other write allowed */
 	else if ((count % 8) || (*off % 8))
 		/* address or length not 64-bit aligned; do 32-bit writes */
 		ret = ipath_write_umem32(dd, kreg_base + *off, data, count);
@@ -498,6 +505,8 @@
 	if (ret >= 0) {
 		*off += count;
 		ret = count;
+		if (ipath_diag_inuse == -1)
+			ipath_diag_inuse = 1; /* all read/write OK now */
 	}
 
 	return ret;
diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c
index ae7f21a..e3a2232 100644
--- a/drivers/infiniband/hw/ipath/ipath_driver.c
+++ b/drivers/infiniband/hw/ipath/ipath_driver.c
@@ -390,15 +390,23 @@
 
 	/* setup the chip-specific functions, as early as possible. */
 	switch (ent->device) {
-#ifdef CONFIG_HT_IRQ
 	case PCI_DEVICE_ID_INFINIPATH_HT:
+#ifdef CONFIG_HT_IRQ
 		ipath_init_iba6110_funcs(dd);
 		break;
+#else
+		ipath_dev_err(dd, "QLogic HT device 0x%x cannot work if "
+			      "CONFIG_HT_IRQ is not enabled\n", ent->device);
+		return -ENODEV;
 #endif
-#ifdef CONFIG_PCI_MSI
 	case PCI_DEVICE_ID_INFINIPATH_PE800:
+#ifdef CONFIG_PCI_MSI
 		ipath_init_iba6120_funcs(dd);
 		break;
+#else
+		ipath_dev_err(dd, "QLogic PCIE device 0x%x cannot work if "
+			      "CONFIG_PCI_MSI is not enabled\n", ent->device);
+		return -ENODEV;
 #endif
 	default:
 		ipath_dev_err(dd, "Found unknown QLogic deviceid 0x%x, "
@@ -486,7 +494,7 @@
 
 	ret = ipath_init_chip(dd, 0);	/* do the chip-specific init */
 	if (ret)
-		goto bail_iounmap;
+		goto bail_irqsetup;
 
 	ret = ipath_enable_wc(dd);
 
@@ -505,6 +513,9 @@
 
 	goto bail;
 
+bail_irqsetup:
+	if (pdev->irq) free_irq(pdev->irq, dd);
+
 bail_iounmap:
 	iounmap((volatile void __iomem *) dd->ipath_kregbase);
 
@@ -525,8 +536,6 @@
 {
 	int port;
 
-	ipath_shutdown_device(dd);
-
 	if (*dd->ipath_statusp & IPATH_STATUS_CHIP_PRESENT) {
 		/* can't do anything more with chip; needs re-init */
 		*dd->ipath_statusp &= ~IPATH_STATUS_CHIP_PRESENT;
@@ -594,8 +603,9 @@
 
 		ipath_cdbg(VERBOSE, "Free shadow page tid array at %p\n",
 			   dd->ipath_pageshadow);
-		vfree(dd->ipath_pageshadow);
+		tmpp = dd->ipath_pageshadow;
 		dd->ipath_pageshadow = NULL;
+		vfree(tmpp);
 	}
 
 	/*
@@ -622,6 +632,12 @@
 
 	ipath_cdbg(VERBOSE, "removing, pdev=%p, dd=%p\n", pdev, dd);
 
+	/*
+	 * disable the IB link early, to be sure no new packets arrive, which
+	 * complicates the shutdown process
+	 */
+	ipath_shutdown_device(dd);
+
 	if (dd->verbs_dev)
 		ipath_unregister_ib_device(dd->verbs_dev);
 
@@ -754,9 +770,42 @@
 	return (dd->ipath_flags & state) ? 0 : -ETIMEDOUT;
 }
 
-void ipath_decode_err(char *buf, size_t blen, ipath_err_t err)
+/*
+ * Decode the error status into strings, deciding whether to always
+ * print * it or not depending on "normal packet errors" vs everything
+ * else.   Return 1 if "real" errors, otherwise 0 if only packet
+ * errors, so caller can decide what to print with the string.
+ */
+int ipath_decode_err(char *buf, size_t blen, ipath_err_t err)
 {
+	int iserr = 1;
 	*buf = '\0';
+	if (err & INFINIPATH_E_PKTERRS) {
+		if (!(err & ~INFINIPATH_E_PKTERRS))
+			iserr = 0; // if only packet errors.
+		if (ipath_debug & __IPATH_ERRPKTDBG) {
+			if (err & INFINIPATH_E_REBP)
+				strlcat(buf, "EBP ", blen);
+			if (err & INFINIPATH_E_RVCRC)
+				strlcat(buf, "VCRC ", blen);
+			if (err & INFINIPATH_E_RICRC) {
+				strlcat(buf, "CRC ", blen);
+				// clear for check below, so only once
+				err &= INFINIPATH_E_RICRC;
+			}
+			if (err & INFINIPATH_E_RSHORTPKTLEN)
+				strlcat(buf, "rshortpktlen ", blen);
+			if (err & INFINIPATH_E_SDROPPEDDATAPKT)
+				strlcat(buf, "sdroppeddatapkt ", blen);
+			if (err & INFINIPATH_E_SPKTLEN)
+				strlcat(buf, "spktlen ", blen);
+		}
+		if ((err & INFINIPATH_E_RICRC) &&
+			!(err&(INFINIPATH_E_RVCRC|INFINIPATH_E_REBP)))
+			strlcat(buf, "CRC ", blen);
+		if (!iserr)
+			goto done;
+	}
 	if (err & INFINIPATH_E_RHDRLEN)
 		strlcat(buf, "rhdrlen ", blen);
 	if (err & INFINIPATH_E_RBADTID)
@@ -767,12 +816,12 @@
 		strlcat(buf, "rhdr ", blen);
 	if (err & INFINIPATH_E_RLONGPKTLEN)
 		strlcat(buf, "rlongpktlen ", blen);
-	if (err & INFINIPATH_E_RSHORTPKTLEN)
-		strlcat(buf, "rshortpktlen ", blen);
 	if (err & INFINIPATH_E_RMAXPKTLEN)
 		strlcat(buf, "rmaxpktlen ", blen);
 	if (err & INFINIPATH_E_RMINPKTLEN)
 		strlcat(buf, "rminpktlen ", blen);
+	if (err & INFINIPATH_E_SMINPKTLEN)
+		strlcat(buf, "sminpktlen ", blen);
 	if (err & INFINIPATH_E_RFORMATERR)
 		strlcat(buf, "rformaterr ", blen);
 	if (err & INFINIPATH_E_RUNSUPVL)
@@ -781,32 +830,20 @@
 		strlcat(buf, "runexpchar ", blen);
 	if (err & INFINIPATH_E_RIBFLOW)
 		strlcat(buf, "ribflow ", blen);
-	if (err & INFINIPATH_E_REBP)
-		strlcat(buf, "EBP ", blen);
 	if (err & INFINIPATH_E_SUNDERRUN)
 		strlcat(buf, "sunderrun ", blen);
 	if (err & INFINIPATH_E_SPIOARMLAUNCH)
 		strlcat(buf, "spioarmlaunch ", blen);
 	if (err & INFINIPATH_E_SUNEXPERRPKTNUM)
 		strlcat(buf, "sunexperrpktnum ", blen);
-	if (err & INFINIPATH_E_SDROPPEDDATAPKT)
-		strlcat(buf, "sdroppeddatapkt ", blen);
 	if (err & INFINIPATH_E_SDROPPEDSMPPKT)
 		strlcat(buf, "sdroppedsmppkt ", blen);
 	if (err & INFINIPATH_E_SMAXPKTLEN)
 		strlcat(buf, "smaxpktlen ", blen);
-	if (err & INFINIPATH_E_SMINPKTLEN)
-		strlcat(buf, "sminpktlen ", blen);
 	if (err & INFINIPATH_E_SUNSUPVL)
 		strlcat(buf, "sunsupVL ", blen);
-	if (err & INFINIPATH_E_SPKTLEN)
-		strlcat(buf, "spktlen ", blen);
 	if (err & INFINIPATH_E_INVALIDADDR)
 		strlcat(buf, "invalidaddr ", blen);
-	if (err & INFINIPATH_E_RICRC)
-		strlcat(buf, "CRC ", blen);
-	if (err & INFINIPATH_E_RVCRC)
-		strlcat(buf, "VCRC ", blen);
 	if (err & INFINIPATH_E_RRCVEGRFULL)
 		strlcat(buf, "rcvegrfull ", blen);
 	if (err & INFINIPATH_E_RRCVHDRFULL)
@@ -819,6 +856,8 @@
 		strlcat(buf, "hardware ", blen);
 	if (err & INFINIPATH_E_RESET)
 		strlcat(buf, "reset ", blen);
+done:
+	return iserr;
 }
 
 /**
@@ -1662,6 +1701,22 @@
 		lstate = IPATH_LINKACTIVE;
 		break;
 
+	case IPATH_IB_LINK_LOOPBACK:
+		dev_info(&dd->pcidev->dev, "Enabling IB local loopback\n");
+		dd->ipath_ibcctrl |= INFINIPATH_IBCC_LOOPBACK;
+		ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl,
+				 dd->ipath_ibcctrl);
+		ret = 0;
+		goto bail; // no state change to wait for
+
+	case IPATH_IB_LINK_EXTERNAL:
+		dev_info(&dd->pcidev->dev, "Disabling IB local loopback (normal)\n");
+		dd->ipath_ibcctrl &= ~INFINIPATH_IBCC_LOOPBACK;
+		ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl,
+				 dd->ipath_ibcctrl);
+		ret = 0;
+		goto bail; // no state change to wait for
+
 	default:
 		ipath_dbg("Invalid linkstate 0x%x requested\n", newstate);
 		ret = -EINVAL;
@@ -1765,29 +1820,6 @@
 	return 0;
 }
 
-/**
- * ipath_read_kreg64_port - read a device's per-port 64-bit kernel register
- * @dd: the infinipath device
- * @regno: the register number to read
- * @port: the port containing the register
- *
- * Registers that vary with the chip implementation constants (port)
- * use this routine.
- */
-u64 ipath_read_kreg64_port(const struct ipath_devdata *dd, ipath_kreg regno,
-			   unsigned port)
-{
-	u16 where;
-
-	if (port < dd->ipath_portcnt &&
-	    (regno == dd->ipath_kregs->kr_rcvhdraddr ||
-	     regno == dd->ipath_kregs->kr_rcvhdrtailaddr))
-		where = regno + port;
-	else
-		where = -1;
-
-	return ipath_read_kreg64(dd, where);
-}
 
 /**
  * ipath_write_kreg_port - write a device's per-port 64-bit kernel register
@@ -1973,7 +2005,8 @@
 {
 	int ret;
 
-	ipath_dbg(KERN_INFO DRIVER_LOAD_MSG "%s", ib_ipath_version);
+	if (ipath_debug & __IPATH_DBG)
+		printk(KERN_INFO DRIVER_LOAD_MSG "%s", ib_ipath_version);
 
 	/*
 	 * These must be called before the driver is registered with
diff --git a/drivers/infiniband/hw/ipath/ipath_eeprom.c b/drivers/infiniband/hw/ipath/ipath_eeprom.c
index a4019a6..030185f 100644
--- a/drivers/infiniband/hw/ipath/ipath_eeprom.c
+++ b/drivers/infiniband/hw/ipath/ipath_eeprom.c
@@ -626,6 +626,10 @@
 	} else
 		memcpy(dd->ipath_serial, ifp->if_serial,
 		       sizeof ifp->if_serial);
+	if (!strstr(ifp->if_comment, "Tested successfully"))
+		ipath_dev_err(dd, "Board SN %s did not pass functional "
+			"test: %s\n", dd->ipath_serial,
+			ifp->if_comment);
 
 	ipath_cdbg(VERBOSE, "Initted GUID to %llx from eeprom\n",
 		   (unsigned long long) be64_to_cpu(dd->ipath_guid));
diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c
index 5d64ff8..1272aaf 100644
--- a/drivers/infiniband/hw/ipath/ipath_file_ops.c
+++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 QLogic, Inc. All rights reserved.
+ * Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved.
  * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
@@ -41,12 +41,6 @@
 #include "ipath_kernel.h"
 #include "ipath_common.h"
 
-/*
- * mmap64 doesn't allow all 64 bits for 32-bit applications
- * so only use the low 43 bits.
- */
-#define MMAP64_MASK	0x7FFFFFFFFFFUL
-
 static int ipath_open(struct inode *, struct file *);
 static int ipath_close(struct inode *, struct file *);
 static ssize_t ipath_write(struct file *, const char __user *, size_t,
@@ -63,6 +57,24 @@
 	.mmap = ipath_mmap
 };
 
+/*
+ * Convert kernel virtual addresses to physical addresses so they don't
+ * potentially conflict with the chip addresses used as mmap offsets.
+ * It doesn't really matter what mmap offset we use as long as we can
+ * interpret it correctly.
+ */
+static u64 cvt_kvaddr(void *p)
+{
+	struct page *page;
+	u64 paddr = 0;
+
+	page = vmalloc_to_page(p);
+	if (page)
+		paddr = page_to_pfn(page) << PAGE_SHIFT;
+
+	return paddr;
+}
+
 static int ipath_get_base_info(struct file *fp,
 			       void __user *ubase, size_t ubase_size)
 {
@@ -87,7 +99,7 @@
 	sz = sizeof(*kinfo);
 	/* If port sharing is not requested, allow the old size structure */
 	if (!shared)
-		sz -= 3 * sizeof(u64);
+		sz -= 7 * sizeof(u64);
 	if (ubase_size < sz) {
 		ipath_cdbg(PROC,
 			   "Base size %zu, need %zu (version mismatch?)\n",
@@ -165,24 +177,41 @@
 		kinfo->spi_piobufbase = (u64) pd->port_piobufs +
 			dd->ipath_palign *
 			(dd->ipath_pbufsport - kinfo->spi_piocnt);
-		kinfo->__spi_uregbase = (u64) dd->ipath_uregbase +
-			dd->ipath_palign * pd->port_port;
 	} else {
 		unsigned slave = subport_fp(fp) - 1;
 
 		kinfo->spi_piocnt = dd->ipath_pbufsport / subport_cnt;
 		kinfo->spi_piobufbase = (u64) pd->port_piobufs +
 			dd->ipath_palign * kinfo->spi_piocnt * slave;
-		kinfo->__spi_uregbase = ((u64) pd->subport_uregbase +
-			PAGE_SIZE * slave) & MMAP64_MASK;
+	}
+	if (shared) {
+		kinfo->spi_port_uregbase = (u64) dd->ipath_uregbase +
+			dd->ipath_palign * pd->port_port;
+		kinfo->spi_port_rcvegrbuf = kinfo->spi_rcv_egrbufs;
+		kinfo->spi_port_rcvhdr_base = kinfo->spi_rcvhdr_base;
+		kinfo->spi_port_rcvhdr_tailaddr = kinfo->spi_rcvhdr_tailaddr;
 
-		kinfo->spi_rcvhdr_base = ((u64) pd->subport_rcvhdr_base +
-			pd->port_rcvhdrq_size * slave) & MMAP64_MASK;
-		kinfo->spi_rcvhdr_tailaddr =
-			(u64) pd->port_rcvhdrqtailaddr_phys & MMAP64_MASK;
-		kinfo->spi_rcv_egrbufs = ((u64) pd->subport_rcvegrbuf +
-			dd->ipath_rcvegrcnt * dd->ipath_rcvegrbufsize * slave) &
-			MMAP64_MASK;
+		kinfo->__spi_uregbase = cvt_kvaddr(pd->subport_uregbase +
+			PAGE_SIZE * subport_fp(fp));
+
+		kinfo->spi_rcvhdr_base = cvt_kvaddr(pd->subport_rcvhdr_base +
+			pd->port_rcvhdrq_size * subport_fp(fp));
+		kinfo->spi_rcvhdr_tailaddr = 0;
+		kinfo->spi_rcv_egrbufs = cvt_kvaddr(pd->subport_rcvegrbuf +
+			pd->port_rcvegrbuf_chunks * pd->port_rcvegrbuf_size *
+			subport_fp(fp));
+
+		kinfo->spi_subport_uregbase =
+			cvt_kvaddr(pd->subport_uregbase);
+		kinfo->spi_subport_rcvegrbuf =
+			cvt_kvaddr(pd->subport_rcvegrbuf);
+		kinfo->spi_subport_rcvhdr_base =
+			cvt_kvaddr(pd->subport_rcvhdr_base);
+		ipath_cdbg(PROC, "port %u flags %x %llx %llx %llx\n",
+			kinfo->spi_port, kinfo->spi_runtime_flags,
+			(unsigned long long) kinfo->spi_subport_uregbase,
+			(unsigned long long) kinfo->spi_subport_rcvegrbuf,
+			(unsigned long long) kinfo->spi_subport_rcvhdr_base);
 	}
 
 	kinfo->spi_pioindex = (kinfo->spi_piobufbase - dd->ipath_piobufbase) /
@@ -199,20 +228,10 @@
 
 	if (master) {
 		kinfo->spi_runtime_flags |= IPATH_RUNTIME_MASTER;
-		kinfo->spi_subport_uregbase =
-			(u64) pd->subport_uregbase & MMAP64_MASK;
-		kinfo->spi_subport_rcvegrbuf =
-			(u64) pd->subport_rcvegrbuf & MMAP64_MASK;
-		kinfo->spi_subport_rcvhdr_base =
-			(u64) pd->subport_rcvhdr_base & MMAP64_MASK;
-		ipath_cdbg(PROC, "port %u flags %x %llx %llx %llx\n",
-			kinfo->spi_port, kinfo->spi_runtime_flags,
-			(unsigned long long) kinfo->spi_subport_uregbase,
-			(unsigned long long) kinfo->spi_subport_rcvegrbuf,
-			(unsigned long long) kinfo->spi_subport_rcvhdr_base);
 	}
 
-	if (copy_to_user(ubase, kinfo, sizeof(*kinfo)))
+	sz = (ubase_size < sizeof(*kinfo)) ? ubase_size : sizeof(*kinfo);
+	if (copy_to_user(ubase, kinfo, sz))
 		ret = -EFAULT;
 
 bail:
@@ -1132,67 +1151,55 @@
 	struct ipath_devdata *dd;
 	void *addr;
 	size_t size;
-	int ret;
+	int ret = 0;
 
 	/* If the port is not shared, all addresses should be physical */
-	if (!pd->port_subport_cnt) {
-		ret = -EINVAL;
+	if (!pd->port_subport_cnt)
 		goto bail;
-	}
 
 	dd = pd->port_dd;
 	size = pd->port_rcvegrbuf_chunks * pd->port_rcvegrbuf_size;
 
 	/*
-	 * Master has all the slave uregbase, rcvhdrq, and
-	 * rcvegrbufs mmapped.
+	 * Each process has all the subport uregbase, rcvhdrq, and
+	 * rcvegrbufs mmapped - as an array for all the processes,
+	 * and also separately for this process.
 	 */
-	if (subport == 0) {
-		unsigned num_slaves = pd->port_subport_cnt - 1;
-
-		if (pgaddr == ((u64) pd->subport_uregbase & MMAP64_MASK)) {
-			addr = pd->subport_uregbase;
-			size = PAGE_SIZE * num_slaves;
-		} else if (pgaddr == ((u64) pd->subport_rcvhdr_base &
-				      MMAP64_MASK)) {
-			addr = pd->subport_rcvhdr_base;
-			size = pd->port_rcvhdrq_size * num_slaves;
-		} else if (pgaddr == ((u64) pd->subport_rcvegrbuf &
-				      MMAP64_MASK)) {
-			addr = pd->subport_rcvegrbuf;
-			size *= num_slaves;
-		} else {
-			ret = -EINVAL;
-			goto bail;
-		}
-	} else if (pgaddr == (((u64) pd->subport_uregbase +
-			       PAGE_SIZE * (subport - 1)) & MMAP64_MASK)) {
-		addr = pd->subport_uregbase + PAGE_SIZE * (subport - 1);
-		size = PAGE_SIZE;
-	} else if (pgaddr == (((u64) pd->subport_rcvhdr_base +
-			       pd->port_rcvhdrq_size * (subport - 1)) &
-			      MMAP64_MASK)) {
-		addr = pd->subport_rcvhdr_base +
-			pd->port_rcvhdrq_size * (subport - 1);
-		size = pd->port_rcvhdrq_size;
-	} else if (pgaddr == (((u64) pd->subport_rcvegrbuf +
-			       size * (subport - 1)) & MMAP64_MASK)) {
-		addr = pd->subport_rcvegrbuf + size * (subport - 1);
-		/* rcvegrbufs are read-only on the slave */
-		if (vma->vm_flags & VM_WRITE) {
-			dev_info(&dd->pcidev->dev,
-				 "Can't map eager buffers as "
-				 "writable (flags=%lx)\n", vma->vm_flags);
-			ret = -EPERM;
-			goto bail;
-		}
-		/*
-		 * Don't allow permission to later change to writeable
-		 * with mprotect.
-		 */
-		vma->vm_flags &= ~VM_MAYWRITE;
+	if (pgaddr == cvt_kvaddr(pd->subport_uregbase)) {
+		addr = pd->subport_uregbase;
+		size = PAGE_SIZE * pd->port_subport_cnt;
+	} else if (pgaddr == cvt_kvaddr(pd->subport_rcvhdr_base)) {
+		addr = pd->subport_rcvhdr_base;
+		size = pd->port_rcvhdrq_size * pd->port_subport_cnt;
+	} else if (pgaddr == cvt_kvaddr(pd->subport_rcvegrbuf)) {
+		addr = pd->subport_rcvegrbuf;
+		size *= pd->port_subport_cnt;
+        } else if (pgaddr == cvt_kvaddr(pd->subport_uregbase +
+                                        PAGE_SIZE * subport)) {
+                addr = pd->subport_uregbase + PAGE_SIZE * subport;
+                size = PAGE_SIZE;
+        } else if (pgaddr == cvt_kvaddr(pd->subport_rcvhdr_base +
+                                pd->port_rcvhdrq_size * subport)) {
+                addr = pd->subport_rcvhdr_base +
+                        pd->port_rcvhdrq_size * subport;
+                size = pd->port_rcvhdrq_size;
+        } else if (pgaddr == cvt_kvaddr(pd->subport_rcvegrbuf +
+                               size * subport)) {
+                addr = pd->subport_rcvegrbuf + size * subport;
+                /* rcvegrbufs are read-only on the slave */
+                if (vma->vm_flags & VM_WRITE) {
+                        dev_info(&dd->pcidev->dev,
+                                 "Can't map eager buffers as "
+                                 "writable (flags=%lx)\n", vma->vm_flags);
+                        ret = -EPERM;
+                        goto bail;
+                }
+                /*
+                 * Don't allow permission to later change to writeable
+                 * with mprotect.
+                 */
+                vma->vm_flags &= ~VM_MAYWRITE;
 	} else {
-		ret = -EINVAL;
 		goto bail;
 	}
 	len = vma->vm_end - vma->vm_start;
@@ -1205,7 +1212,7 @@
 	vma->vm_pgoff = (unsigned long) addr >> PAGE_SHIFT;
 	vma->vm_ops = &ipath_file_vm_ops;
 	vma->vm_flags |= VM_RESERVED | VM_DONTEXPAND;
-	ret = 0;
+	ret = 1;
 
 bail:
 	return ret;
@@ -1265,19 +1272,20 @@
 	 * Check for kernel virtual addresses first, anything else must
 	 * match a HW or memory address.
 	 */
-	if (pgaddr >= (1ULL<<40)) {
-		ret = mmap_kvaddr(vma, pgaddr, pd, subport_fp(fp));
+	ret = mmap_kvaddr(vma, pgaddr, pd, subport_fp(fp));
+	if (ret) {
+		if (ret > 0)
+			ret = 0;
 		goto bail;
 	}
 
+	ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port;
 	if (!pd->port_subport_cnt) {
 		/* port is not shared */
-		ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port;
 		piocnt = dd->ipath_pbufsport;
 		piobufs = pd->port_piobufs;
 	} else if (!subport_fp(fp)) {
 		/* caller is the master */
-		ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port;
 		piocnt = (dd->ipath_pbufsport / pd->port_subport_cnt) +
 			 (dd->ipath_pbufsport % pd->port_subport_cnt);
 		piobufs = pd->port_piobufs +
@@ -1286,7 +1294,6 @@
 		unsigned slave = subport_fp(fp) - 1;
 
 		/* caller is a slave */
-		ureg = 0;
 		piocnt = dd->ipath_pbufsport / pd->port_subport_cnt;
 		piobufs = pd->port_piobufs + dd->ipath_palign * piocnt * slave;
 	}
@@ -1300,9 +1307,6 @@
 		ret = ipath_mmap_mem(vma, pd, PAGE_SIZE, 0,
 			      	     (void *) dd->ipath_pioavailregs_dma,
 				     "pioavail registers");
-	else if (subport_fp(fp))
-		/* Subports don't mmap the physical receive buffers */
-		ret = -EINVAL;
 	else if (pgaddr == pd->port_rcvegr_phys)
 		ret = mmap_rcvegrbufs(vma, pd);
 	else if (pgaddr == (u64) pd->port_rcvhdrq_phys)
@@ -1400,32 +1404,41 @@
 			 const struct ipath_user_info *uinfo)
 {
 	int ret = 0;
-	unsigned num_slaves;
+	unsigned num_subports;
 	size_t size;
 
-	/* Old user binaries don't know about subports */
-	if ((uinfo->spu_userversion & 0xffff) != IPATH_USER_SWMINOR)
-		goto bail;
 	/*
 	 * If the user is requesting zero or one port,
 	 * skip the subport allocation.
 	 */
 	if (uinfo->spu_subport_cnt <= 1)
 		goto bail;
-	if (uinfo->spu_subport_cnt > 4) {
+
+	/* Old user binaries don't know about new subport implementation */
+	if ((uinfo->spu_userversion & 0xffff) != IPATH_USER_SWMINOR) {
+		dev_info(&dd->pcidev->dev,
+			 "Mismatched user minor version (%d) and driver "
+                         "minor version (%d) while port sharing. Ensure "
+                         "that driver and library are from the same "
+                         "release.\n",
+                         (int) (uinfo->spu_userversion & 0xffff),
+	                 IPATH_USER_SWMINOR);
+		goto bail;
+	}
+	if (uinfo->spu_subport_cnt > INFINIPATH_MAX_SUBPORT) {
 		ret = -EINVAL;
 		goto bail;
 	}
 
-	num_slaves = uinfo->spu_subport_cnt - 1;
-	pd->subport_uregbase = vmalloc(PAGE_SIZE * num_slaves);
+	num_subports = uinfo->spu_subport_cnt;
+	pd->subport_uregbase = vmalloc(PAGE_SIZE * num_subports);
 	if (!pd->subport_uregbase) {
 		ret = -ENOMEM;
 		goto bail;
 	}
 	/* Note: pd->port_rcvhdrq_size isn't initialized yet. */
 	size = ALIGN(dd->ipath_rcvhdrcnt * dd->ipath_rcvhdrentsize *
-		     sizeof(u32), PAGE_SIZE) * num_slaves;
+		     sizeof(u32), PAGE_SIZE) * num_subports;
 	pd->subport_rcvhdr_base = vmalloc(size);
 	if (!pd->subport_rcvhdr_base) {
 		ret = -ENOMEM;
@@ -1434,7 +1447,7 @@
 
 	pd->subport_rcvegrbuf = vmalloc(pd->port_rcvegrbuf_chunks *
 					pd->port_rcvegrbuf_size *
-					num_slaves);
+					num_subports);
 	if (!pd->subport_rcvegrbuf) {
 		ret = -ENOMEM;
 		goto bail_rhdr;
@@ -1443,6 +1456,12 @@
 	pd->port_subport_cnt = uinfo->spu_subport_cnt;
 	pd->port_subport_id = uinfo->spu_subport_id;
 	pd->active_slaves = 1;
+	set_bit(IPATH_PORT_MASTER_UNINIT, &pd->port_flag);
+	memset(pd->subport_uregbase, 0, PAGE_SIZE * num_subports);
+	memset(pd->subport_rcvhdr_base, 0, size);
+	memset(pd->subport_rcvegrbuf, 0, pd->port_rcvegrbuf_chunks *
+				         pd->port_rcvegrbuf_size *
+				         num_subports);
 	goto bail;
 
 bail_rhdr:
@@ -1573,18 +1592,19 @@
 	 */
 	if (!cpus_empty(current->cpus_allowed) &&
 	    !cpus_full(current->cpus_allowed)) {
-		int ncpus = num_online_cpus(), curcpu = -1;
+		int ncpus = num_online_cpus(), curcpu = -1, nset = 0;
 		for (i = 0; i < ncpus; i++)
 			if (cpu_isset(i, current->cpus_allowed)) {
 				ipath_cdbg(PROC, "%s[%u] affinity set for "
-					   "cpu %d\n", current->comm,
-					   current->pid, i);
+					   "cpu %d/%d\n", current->comm,
+					   current->pid, i, ncpus);
 				curcpu = i;
+				nset++;
 			}
-		if (curcpu != -1) {
+		if (curcpu != -1 && nset != ncpus) {
 			if (npresent) {
 				prefunit = curcpu / (ncpus / npresent);
-				ipath_dbg("%s[%u] %d chips, %d cpus, "
+				ipath_cdbg(PROC,"%s[%u] %d chips, %d cpus, "
 					  "%d cpus/chip, select unit %d\n",
 					  current->comm, current->pid,
 					  npresent, ncpus, ncpus / npresent,
@@ -1764,11 +1784,17 @@
 			      const struct ipath_user_info *uinfo)
 {
 	int ret;
-	struct ipath_portdata *pd;
+	struct ipath_portdata *pd = port_fp(fp);
 	struct ipath_devdata *dd;
 	u32 head32;
 
-	pd = port_fp(fp);
+	/* Subports don't need to initialize anything since master did it. */
+	if (subport_fp(fp)) {
+		ret = wait_event_interruptible(pd->port_wait,
+			!test_bit(IPATH_PORT_MASTER_UNINIT, &pd->port_flag));
+		goto done;
+	}
+
 	dd = pd->port_dd;
 
 	if (uinfo->spu_rcvhdrsize) {
@@ -1826,6 +1852,11 @@
 			 dd->ipath_rcvctrl & ~INFINIPATH_R_TAILUPD);
 	ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
 			 dd->ipath_rcvctrl);
+	/* Notify any waiting slaves */
+	if (pd->port_subport_cnt) {
+		clear_bit(IPATH_PORT_MASTER_UNINIT, &pd->port_flag);
+		wake_up(&pd->port_wait);
+	}
 done:
 	return ret;
 }
@@ -2017,6 +2048,17 @@
 	return ret;
 }
 
+static int ipath_force_pio_avail_update(struct ipath_devdata *dd)
+{
+	u64 reg = dd->ipath_sendctrl;
+
+	clear_bit(IPATH_S_PIOBUFAVAILUPD, &reg);
+	ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, reg);
+	ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, dd->ipath_sendctrl);
+
+	return 0;
+}
+
 static ssize_t ipath_write(struct file *fp, const char __user *data,
 			   size_t count, loff_t *off)
 {
@@ -2071,27 +2113,35 @@
 		dest = &cmd.cmd.part_key;
 		src = &ucmd->cmd.part_key;
 		break;
-	case IPATH_CMD_SLAVE_INFO:
+	case __IPATH_CMD_SLAVE_INFO:
 		copy = sizeof(cmd.cmd.slave_mask_addr);
 		dest = &cmd.cmd.slave_mask_addr;
 		src = &ucmd->cmd.slave_mask_addr;
 		break;
+	case IPATH_CMD_PIOAVAILUPD:	// force an update of PIOAvail reg
+		copy = 0;
+		src = NULL;
+		dest = NULL;
+		break;
 	default:
 		ret = -EINVAL;
 		goto bail;
 	}
 
-	if ((count - consumed) < copy) {
-		ret = -EINVAL;
-		goto bail;
+	if (copy) {
+		if ((count - consumed) < copy) {
+			ret = -EINVAL;
+			goto bail;
+		}
+
+		if (copy_from_user(dest, src, copy)) {
+			ret = -EFAULT;
+			goto bail;
+		}
+
+		consumed += copy;
 	}
 
-	if (copy_from_user(dest, src, copy)) {
-		ret = -EFAULT;
-		goto bail;
-	}
-
-	consumed += copy;
 	pd = port_fp(fp);
 	if (!pd && cmd.type != __IPATH_CMD_USER_INIT &&
 		cmd.type != IPATH_CMD_ASSIGN_PORT) {
@@ -2137,11 +2187,14 @@
 	case IPATH_CMD_SET_PART_KEY:
 		ret = ipath_set_part_key(pd, cmd.cmd.part_key);
 		break;
-	case IPATH_CMD_SLAVE_INFO:
+	case __IPATH_CMD_SLAVE_INFO:
 		ret = ipath_get_slave_info(pd,
 					   (void __user *) (unsigned long)
 					   cmd.cmd.slave_mask_addr);
 		break;
+	case IPATH_CMD_PIOAVAILUPD:
+		ret = ipath_force_pio_avail_update(pd->port_dd);
+		break;
 	}
 
 	if (ret >= 0)
diff --git a/drivers/infiniband/hw/ipath/ipath_iba6110.c b/drivers/infiniband/hw/ipath/ipath_iba6110.c
index 9934825..4171198 100644
--- a/drivers/infiniband/hw/ipath/ipath_iba6110.c
+++ b/drivers/infiniband/hw/ipath/ipath_iba6110.c
@@ -43,6 +43,9 @@
 #include "ipath_kernel.h"
 #include "ipath_registers.h"
 
+static void ipath_setup_ht_setextled(struct ipath_devdata *, u64, u64);
+
+
 /*
  * This lists the InfiniPath registers, in the actual chip layout.
  * This structure should never be directly accessed.
@@ -208,8 +211,8 @@
 	.kr_serdesstatus = IPATH_KREG_OFFSET(SerdesStatus),
 	.kr_xgxsconfig = IPATH_KREG_OFFSET(XGXSConfig),
 	/*
-	 * These should not be used directly via ipath_read_kreg64(),
-	 * use them with ipath_read_kreg64_port(),
+	 * These should not be used directly via ipath_write_kreg64(),
+	 * use them with ipath_write_kreg64_port(),
 	 */
 	.kr_rcvhdraddr = IPATH_KREG_OFFSET(RcvHdrAddr0),
 	.kr_rcvhdrtailaddr = IPATH_KREG_OFFSET(RcvHdrTailAddr0)
@@ -284,6 +287,14 @@
 #define INFINIPATH_EXTS_MEMBIST_ENDTEST     0x0000000000004000
 #define INFINIPATH_EXTS_MEMBIST_CORRECT     0x0000000000008000
 
+
+/* TID entries (memory), HT-only */
+#define INFINIPATH_RT_ADDR_MASK 0xFFFFFFFFFFULL	/* 40 bits valid */
+#define INFINIPATH_RT_VALID 0x8000000000000000ULL
+#define INFINIPATH_RT_ADDR_SHIFT 0
+#define INFINIPATH_RT_BUFSIZE_MASK 0x3FFFULL
+#define INFINIPATH_RT_BUFSIZE_SHIFT 48
+
 /*
  * masks and bits that are different in different chips, or present only
  * in one
@@ -402,6 +413,14 @@
 	INFINIPATH_HWE_MSG(SERDESPLLFAILED, "SerDes PLL"),
 };
 
+#define TXE_PIO_PARITY ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | \
+		        INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) \
+		        << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)
+#define RXE_EAGER_PARITY (INFINIPATH_HWE_RXEMEMPARITYERR_EAGERTID \
+			  << INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT)
+
+static int ipath_ht_txe_recover(struct ipath_devdata *);
+
 /**
  * ipath_ht_handle_hwerrors - display hardware errors.
  * @dd: the infinipath device
@@ -450,13 +469,12 @@
 
 	/*
 	 * make sure we get this much out, unless told to be quiet,
+	 * it's a parity error we may recover from,
 	 * or it's occurred within the last 5 seconds
 	 */
-	if ((hwerrs & ~(dd->ipath_lasthwerror |
-			((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF |
-			  INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC)
-			<< INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT))) ||
-	    (ipath_debug & __IPATH_VERBDBG))
+	if ((hwerrs & ~(dd->ipath_lasthwerror | TXE_PIO_PARITY |
+		RXE_EAGER_PARITY)) ||
+		(ipath_debug & __IPATH_VERBDBG))
 		dev_info(&dd->pcidev->dev, "Hardware error: hwerr=0x%llx "
 			 "(cleared)\n", (unsigned long long) hwerrs);
 	dd->ipath_lasthwerror |= hwerrs;
@@ -467,7 +485,7 @@
 			      (hwerrs & ~dd->ipath_hwe_bitsextant));
 
 	ctrl = ipath_read_kreg32(dd, dd->ipath_kregs->kr_control);
-	if (ctrl & INFINIPATH_C_FREEZEMODE) {
+	if ((ctrl & INFINIPATH_C_FREEZEMODE) && !ipath_diag_inuse) {
 		/*
 		 * parity errors in send memory are recoverable,
 		 * just cancel the send (if indicated in * sendbuffererror),
@@ -476,50 +494,14 @@
 		 * occur if a processor speculative read is done to the PIO
 		 * buffer while we are sending a packet, for example.
 		 */
-		if (hwerrs & ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF |
-			       INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC)
-			      << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) {
-			ipath_stats.sps_txeparity++;
-			ipath_dbg("Recovering from TXE parity error (%llu), "
-			    	  "hwerrstatus=%llx\n",
-				  (unsigned long long) ipath_stats.sps_txeparity,
-				  (unsigned long long) hwerrs);
-			ipath_disarm_senderrbufs(dd);
-			hwerrs &= ~((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF |
-				     INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC)
-				    << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT);
-			if (!hwerrs) { /* else leave in freeze mode */
-				ipath_write_kreg(dd,
-						 dd->ipath_kregs->kr_control,
-						 dd->ipath_control);
-				return;
-			}
-		}
-		if (hwerrs) {
-			/*
-			 * if any set that we aren't ignoring; only
-			 * make the complaint once, in case it's stuck
-			 * or recurring, and we get here multiple
-			 * times.
-			 */
-			if (dd->ipath_flags & IPATH_INITTED) {
-				ipath_dev_err(dd, "Fatal Hardware Error (freeze "
-					      "mode), no longer usable, SN %.16s\n",
-						  dd->ipath_serial);
-				isfatal = 1;
-			}
-			*dd->ipath_statusp &= ~IPATH_STATUS_IB_READY;
-			/* mark as having had error */
-			*dd->ipath_statusp |= IPATH_STATUS_HWERROR;
-			/*
-			 * mark as not usable, at a minimum until driver
-			 * is reloaded, probably until reboot, since no
-			 * other reset is possible.
-			 */
-			dd->ipath_flags &= ~IPATH_INITTED;
-		} else {
-			ipath_dbg("Clearing freezemode on ignored hardware "
-				  "error\n");
+		if ((hwerrs & TXE_PIO_PARITY) && ipath_ht_txe_recover(dd))
+			hwerrs &= ~TXE_PIO_PARITY;
+		if (hwerrs & RXE_EAGER_PARITY)
+			ipath_dev_err(dd, "RXE parity, Eager TID error is not "
+				"recoverable\n");
+		if (!hwerrs) {
+			ipath_dbg("Clearing freezemode on ignored or "
+				  "recovered hardware error\n");
 			ctrl &= ~INFINIPATH_C_FREEZEMODE;
 			ipath_write_kreg(dd, dd->ipath_kregs->kr_control,
 					 ctrl);
@@ -587,7 +569,39 @@
 				 dd->ipath_hwerrmask);
 	}
 
-	ipath_dev_err(dd, "%s hardware error\n", msg);
+	if (hwerrs) {
+		/*
+		 * if any set that we aren't ignoring; only
+		 * make the complaint once, in case it's stuck
+		 * or recurring, and we get here multiple
+		 * times.
+		 * force link down, so switch knows, and
+		 * LEDs are turned off
+		 */
+		if (dd->ipath_flags & IPATH_INITTED) {
+			ipath_set_linkstate(dd, IPATH_IB_LINKDOWN);
+			ipath_setup_ht_setextled(dd,
+				INFINIPATH_IBCS_L_STATE_DOWN,
+				INFINIPATH_IBCS_LT_STATE_DISABLED);
+			ipath_dev_err(dd, "Fatal Hardware Error (freeze "
+					  "mode), no longer usable, SN %.16s\n",
+					  dd->ipath_serial);
+			isfatal = 1;
+		}
+		*dd->ipath_statusp &= ~IPATH_STATUS_IB_READY;
+		/* mark as having had error */
+		*dd->ipath_statusp |= IPATH_STATUS_HWERROR;
+		/*
+		 * mark as not usable, at a minimum until driver
+		 * is reloaded, probably until reboot, since no
+		 * other reset is possible.
+		 */
+		dd->ipath_flags &= ~IPATH_INITTED;
+	}
+	else
+		*msg = 0; /* recovered from all of them */
+	if (*msg)
+		ipath_dev_err(dd, "%s hardware error\n", msg);
 	if (isfatal && !ipath_diag_inuse && dd->ipath_freezemsg)
 		/*
 		 * for status file; if no trailing brace is copied,
@@ -658,7 +672,8 @@
 	if (n)
 		snprintf(name, namelen, "%s", n);
 
-	if (dd->ipath_majrev != 3 || (dd->ipath_minrev < 2 || dd->ipath_minrev > 3)) {
+	if (dd->ipath_majrev != 3 || (dd->ipath_minrev < 2 ||
+		dd->ipath_minrev > 3)) {
 		/*
 		 * This version of the driver only supports Rev 3.2 and 3.3
 		 */
@@ -1163,6 +1178,8 @@
 
 	if (!(extsval & INFINIPATH_EXTS_MEMBIST_ENDTEST))
 		ipath_dev_err(dd, "MemBIST did not complete!\n");
+	if (extsval & INFINIPATH_EXTS_MEMBIST_CORRECT)
+		ipath_dbg("MemBIST corrected\n");
 
 	ipath_check_htlink(dd);
 
@@ -1366,6 +1383,9 @@
 			     u64 __iomem *tidptr, u32 type,
 			     unsigned long pa)
 {
+	if (!dd->ipath_kregbase)
+		return;
+
 	if (pa != dd->ipath_tidinvalid) {
 		if (unlikely((pa & ~INFINIPATH_RT_ADDR_MASK))) {
 			dev_info(&dd->pcidev->dev,
@@ -1382,10 +1402,10 @@
 			pa |= lenvalid | INFINIPATH_RT_VALID;
 		}
 	}
-	if (dd->ipath_kregbase)
-		writeq(pa, tidptr);
+	writeq(pa, tidptr);
 }
 
+
 /**
  * ipath_ht_clear_tid - clear all TID entries for a port, expected and eager
  * @dd: the infinipath device
@@ -1515,7 +1535,7 @@
 			 INFINIPATH_S_ABORT);
 
 	ipath_get_eeprom_info(dd);
-	if(dd->ipath_boardrev == 5 && dd->ipath_serial[0] == '1' &&
+	if (dd->ipath_boardrev == 5 && dd->ipath_serial[0] == '1' &&
 		dd->ipath_serial[1] == '2' && dd->ipath_serial[2] == '8') {
 		/*
 		 * Later production QHT7040 has same changes as QHT7140, so
@@ -1528,6 +1548,24 @@
 	return 0;
 }
 
+
+static int ipath_ht_txe_recover(struct ipath_devdata *dd)
+{
+	int cnt = ++ipath_stats.sps_txeparity;
+	if (cnt >= IPATH_MAX_PARITY_ATTEMPTS)  {
+		if (cnt == IPATH_MAX_PARITY_ATTEMPTS)
+			ipath_dev_err(dd,
+				"Too many attempts to recover from "
+				"TXE parity, giving up\n");
+		return 0;
+	}
+	dev_info(&dd->pcidev->dev,
+		"Recovering from TXE PIO parity error\n");
+	ipath_disarm_senderrbufs(dd, 1);
+	return 1;
+}
+
+
 /**
  * ipath_init_ht_get_base_info - set chip-specific flags for user code
  * @dd: the infinipath device
diff --git a/drivers/infiniband/hw/ipath/ipath_iba6120.c b/drivers/infiniband/hw/ipath/ipath_iba6120.c
index 05918e1..1b9c308 100644
--- a/drivers/infiniband/hw/ipath/ipath_iba6120.c
+++ b/drivers/infiniband/hw/ipath/ipath_iba6120.c
@@ -43,6 +43,8 @@
 #include "ipath_kernel.h"
 #include "ipath_registers.h"
 
+static void ipath_setup_pe_setextled(struct ipath_devdata *, u64, u64);
+
 /*
  * This file contains all the chip-specific register information and
  * access functions for the QLogic InfiniPath PCI-Express chip.
@@ -207,8 +209,8 @@
 	.kr_ibpllcfg = IPATH_KREG_OFFSET(IBPLLCfg),
 
 	/*
-	 * These should not be used directly via ipath_read_kreg64(),
-	 * use them with ipath_read_kreg64_port()
+	 * These should not be used directly via ipath_write_kreg64(),
+	 * use them with ipath_write_kreg64_port(),
 	 */
 	.kr_rcvhdraddr = IPATH_KREG_OFFSET(RcvHdrAddr0),
 	.kr_rcvhdrtailaddr = IPATH_KREG_OFFSET(RcvHdrTailAddr0),
@@ -321,6 +323,12 @@
 	INFINIPATH_HWE_MSG(SERDESPLLFAILED, "SerDes PLL"),
 };
 
+#define TXE_PIO_PARITY ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | \
+		        INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) \
+		        << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)
+
+static int ipath_pe_txe_recover(struct ipath_devdata *);
+
 /**
  * ipath_pe_handle_hwerrors - display hardware errors.
  * @dd: the infinipath device
@@ -394,32 +402,21 @@
 		 * occur if a processor speculative read is done to the PIO
 		 * buffer while we are sending a packet, for example.
 		 */
-		if (hwerrs & ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF |
-			       INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC)
-			      << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) {
-			ipath_stats.sps_txeparity++;
-			ipath_dbg("Recovering from TXE parity error (%llu), "
-			    	  "hwerrstatus=%llx\n",
-				  (unsigned long long) ipath_stats.sps_txeparity,
-				  (unsigned long long) hwerrs);
-			ipath_disarm_senderrbufs(dd);
-			hwerrs &= ~((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF |
-				     INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC)
-				    << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT);
-			if (!hwerrs) { /* else leave in freeze mode */
-				ipath_write_kreg(dd,
-						 dd->ipath_kregs->kr_control,
-						 dd->ipath_control);
-			    return;
-			}
-		}
+		if ((hwerrs & TXE_PIO_PARITY) && ipath_pe_txe_recover(dd))
+			hwerrs &= ~TXE_PIO_PARITY;
 		if (hwerrs) {
 			/*
 			 * if any set that we aren't ignoring only make the
 			 * complaint once, in case it's stuck or recurring,
 			 * and we get here multiple times
+			 * Force link down, so switch knows, and
+			 * LEDs are turned off
 			 */
 			if (dd->ipath_flags & IPATH_INITTED) {
+				ipath_set_linkstate(dd, IPATH_IB_LINKDOWN);
+				ipath_setup_pe_setextled(dd,
+					INFINIPATH_IBCS_L_STATE_DOWN,
+					INFINIPATH_IBCS_LT_STATE_DISABLED);
 				ipath_dev_err(dd, "Fatal Hardware Error (freeze "
 					      "mode), no longer usable, SN %.16s\n",
 						  dd->ipath_serial);
@@ -493,7 +490,8 @@
 				 dd->ipath_hwerrmask);
 	}
 
-	ipath_dev_err(dd, "%s hardware error\n", msg);
+	if (*msg)
+		ipath_dev_err(dd, "%s hardware error\n", msg);
 	if (isfatal && !ipath_diag_inuse && dd->ipath_freezemsg) {
 		/*
 		 * for /sys status file ; if no trailing } is copied, we'll
@@ -581,6 +579,8 @@
 
 	if (!(extsval & INFINIPATH_EXTS_MEMBIST_ENDTEST))
 		ipath_dev_err(dd, "MemBIST did not complete!\n");
+	if (extsval & INFINIPATH_EXTS_MEMBIST_FOUND)
+		ipath_dbg("MemBIST corrected\n");
 
 	val = ~0ULL;	/* barring bugs, all hwerrors become interrupts, */
 
@@ -1330,6 +1330,35 @@
 	dd->ipath_irq = 0;
 }
 
+/*
+ * On platforms using this chip, and not having ordered WC stores, we
+ * can get TXE parity errors due to speculative reads to the PIO buffers,
+ * and this, due to a chip bug can result in (many) false parity error
+ * reports.  So it's a debug print on those, and an info print on systems
+ * where the speculative reads don't occur.
+ * Because we can get lots of false errors, we have no upper limit
+ * on recovery attempts on those platforms.
+ */
+static int ipath_pe_txe_recover(struct ipath_devdata *dd)
+{
+	if (ipath_unordered_wc())
+		ipath_dbg("Recovering from TXE PIO parity error\n");
+	else {
+		int cnt = ++ipath_stats.sps_txeparity;
+		if (cnt >= IPATH_MAX_PARITY_ATTEMPTS)  {
+			if (cnt == IPATH_MAX_PARITY_ATTEMPTS)
+				ipath_dev_err(dd,
+					"Too many attempts to recover from "
+					"TXE parity, giving up\n");
+			return 0;
+		}
+		dev_info(&dd->pcidev->dev,
+			"Recovering from TXE PIO parity error\n");
+	}
+	ipath_disarm_senderrbufs(dd, 1);
+	return 1;
+}
+
 /**
  * ipath_init_iba6120_funcs - set up the chip-specific function pointers
  * @dd: the infinipath device
diff --git a/drivers/infiniband/hw/ipath/ipath_init_chip.c b/drivers/infiniband/hw/ipath/ipath_init_chip.c
index d4f6b523..7045ba6 100644
--- a/drivers/infiniband/hw/ipath/ipath_init_chip.c
+++ b/drivers/infiniband/hw/ipath/ipath_init_chip.c
@@ -216,6 +216,20 @@
 	return ret;
 }
 
+static struct ipath_portdata *create_portdata0(struct ipath_devdata *dd)
+{
+	struct ipath_portdata *pd = NULL;
+
+	pd = kzalloc(sizeof(*pd), GFP_KERNEL);
+	if (pd) {
+		pd->port_dd = dd;
+		pd->port_cnt = 1;
+		/* The port 0 pkey table is used by the layer interface. */
+		pd->port_pkeys[0] = IPATH_DEFAULT_P_KEY;
+	}
+	return pd;
+}
+
 static int init_chip_first(struct ipath_devdata *dd,
 			   struct ipath_portdata **pdp)
 {
@@ -271,20 +285,16 @@
 		goto done;
 	}
 
-	dd->ipath_pd[0] = kzalloc(sizeof(*pd), GFP_KERNEL);
+	pd = create_portdata0(dd);
 
-	if (!dd->ipath_pd[0]) {
+	if (!pd) {
 		ipath_dev_err(dd, "Unable to allocate portdata for port "
 			      "0, failing\n");
 		ret = -ENOMEM;
 		goto done;
 	}
-	pd = dd->ipath_pd[0];
-	pd->port_dd = dd;
-	pd->port_port = 0;
-	pd->port_cnt = 1;
-	/* The port 0 pkey table is used by the layer interface. */
-	pd->port_pkeys[0] = IPATH_DEFAULT_P_KEY;
+	dd->ipath_pd[0] = pd;
+
 	dd->ipath_rcvtidcnt =
 		ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvtidcnt);
 	dd->ipath_rcvtidbase =
@@ -590,6 +600,10 @@
 		goto done;
 	}
 
+
+	/* clear diagctrl register, in case diags were running and crashed */
+	ipath_write_kreg (dd, dd->ipath_kregs->kr_hwdiagctrl, 0);
+
 	/* clear the initial reset flag, in case first driver load */
 	ipath_write_kreg(dd, dd->ipath_kregs->kr_errorclear,
 			 INFINIPATH_E_RESET);
@@ -668,6 +682,7 @@
 {
 	int ret = 0, i;
 	u32 val32, kpiobufs;
+	u32 piobufs, uports;
 	u64 val;
 	struct ipath_portdata *pd = NULL; /* keep gcc4 happy */
 	gfp_t gfp_flags = GFP_USER | __GFP_COMP;
@@ -702,16 +717,17 @@
 	 * the in memory DMA'ed copies of the registers.  This has to
 	 * be done early, before we calculate lastport, etc.
 	 */
-	val = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k;
+	piobufs = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k;
 	/*
 	 * calc number of pioavail registers, and save it; we have 2
 	 * bits per buffer.
 	 */
-	dd->ipath_pioavregs = ALIGN(val, sizeof(u64) * BITS_PER_BYTE / 2)
+	dd->ipath_pioavregs = ALIGN(piobufs, sizeof(u64) * BITS_PER_BYTE / 2)
 		/ (sizeof(u64) * BITS_PER_BYTE / 2);
+	uports = dd->ipath_cfgports ? dd->ipath_cfgports - 1 : 0;
 	if (ipath_kpiobufs == 0) {
 		/* not set by user (this is default) */
-		if ((dd->ipath_piobcnt2k + dd->ipath_piobcnt4k) > 128)
+		if (piobufs >= (uports * IPATH_MIN_USER_PORT_BUFCNT) + 32)
 			kpiobufs = 32;
 		else
 			kpiobufs = 16;
@@ -719,31 +735,25 @@
 	else
 		kpiobufs = ipath_kpiobufs;
 
-	if (kpiobufs >
-	    (dd->ipath_piobcnt2k + dd->ipath_piobcnt4k -
-	     (dd->ipath_cfgports * IPATH_MIN_USER_PORT_BUFCNT))) {
-		i = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k -
-			(dd->ipath_cfgports * IPATH_MIN_USER_PORT_BUFCNT);
+	if (kpiobufs + (uports * IPATH_MIN_USER_PORT_BUFCNT) > piobufs) {
+		i = (int) piobufs -
+			(int) (uports * IPATH_MIN_USER_PORT_BUFCNT);
 		if (i < 0)
 			i = 0;
-		dev_info(&dd->pcidev->dev, "Allocating %d PIO bufs for "
-			 "kernel leaves too few for %d user ports "
+		dev_info(&dd->pcidev->dev, "Allocating %d PIO bufs of "
+			 "%d for kernel leaves too few for %d user ports "
 			 "(%d each); using %u\n", kpiobufs,
-			 dd->ipath_cfgports - 1,
-			 IPATH_MIN_USER_PORT_BUFCNT, i);
+			 piobufs, uports, IPATH_MIN_USER_PORT_BUFCNT, i);
 		/*
 		 * shouldn't change ipath_kpiobufs, because could be
 		 * different for different devices...
 		 */
 		kpiobufs = i;
 	}
-	dd->ipath_lastport_piobuf =
-		dd->ipath_piobcnt2k + dd->ipath_piobcnt4k - kpiobufs;
-	dd->ipath_pbufsport = dd->ipath_cfgports > 1
-		? dd->ipath_lastport_piobuf / (dd->ipath_cfgports - 1)
-		: 0;
-	val32 = dd->ipath_lastport_piobuf -
-		(dd->ipath_pbufsport * (dd->ipath_cfgports - 1));
+	dd->ipath_lastport_piobuf = piobufs - kpiobufs;
+	dd->ipath_pbufsport =
+		uports ? dd->ipath_lastport_piobuf / uports : 0;
+	val32 = dd->ipath_lastport_piobuf - (dd->ipath_pbufsport * uports);
 	if (val32 > 0) {
 		ipath_dbg("allocating %u pbufs/port leaves %u unused, "
 			  "add to kernel\n", dd->ipath_pbufsport, val32);
@@ -754,8 +764,7 @@
 	dd->ipath_lastpioindex = dd->ipath_lastport_piobuf;
 	ipath_cdbg(VERBOSE, "%d PIO bufs for kernel out of %d total %u "
 		   "each for %u user ports\n", kpiobufs,
-		   dd->ipath_piobcnt2k + dd->ipath_piobcnt4k,
-		   dd->ipath_pbufsport, dd->ipath_cfgports - 1);
+		   piobufs, dd->ipath_pbufsport, uports);
 
 	dd->ipath_f_early_init(dd);
 
@@ -839,11 +848,24 @@
 	 * Set up the port 0 (kernel) rcvhdr q and egr TIDs.  If doing
 	 * re-init, the simplest way to handle this is to free
 	 * existing, and re-allocate.
+	 * Need to re-create rest of port 0 portdata as well.
 	 */
 	if (reinit) {
-		struct ipath_portdata *pd = dd->ipath_pd[0];
-		dd->ipath_pd[0] = NULL;
-		ipath_free_pddata(dd, pd);
+		/* Alloc and init new ipath_portdata for port0,
+		 * Then free old pd. Could lead to fragmentation, but also
+		 * makes later support for hot-swap easier.
+		 */
+		struct ipath_portdata *npd;
+		npd = create_portdata0(dd);
+		if (npd) {
+			ipath_free_pddata(dd, pd);
+			dd->ipath_pd[0] = pd = npd;
+		} else {
+			ipath_dev_err(dd, "Unable to allocate portdata for"
+				      "  port 0, failing\n");
+			ret = -ENOMEM;
+			goto done;
+		}
 	}
 	dd->ipath_f_tidtemplate(dd);
 	ret = ipath_create_rcvhdrq(dd, pd);
diff --git a/drivers/infiniband/hw/ipath/ipath_intr.c b/drivers/infiniband/hw/ipath/ipath_intr.c
index 72b9e27..45d0331 100644
--- a/drivers/infiniband/hw/ipath/ipath_intr.c
+++ b/drivers/infiniband/hw/ipath/ipath_intr.c
@@ -38,10 +38,39 @@
 #include "ipath_common.h"
 
 /*
+ * clear (write) a pio buffer, to clear a parity error.   This routine
+ * should only be called when in freeze mode, and the buffer should be
+ * canceled afterwards.
+ */
+static void ipath_clrpiobuf(struct ipath_devdata *dd, u32 pnum)
+{
+	u32 __iomem *pbuf;
+	u32 dwcnt; /* dword count to write */
+	if (pnum < dd->ipath_piobcnt2k) {
+		pbuf = (u32 __iomem *) (dd->ipath_pio2kbase + pnum *
+			dd->ipath_palign);
+		dwcnt = dd->ipath_piosize2k >> 2;
+	}
+	else {
+		pbuf = (u32 __iomem *) (dd->ipath_pio4kbase +
+			(pnum - dd->ipath_piobcnt2k) * dd->ipath_4kalign);
+		dwcnt = dd->ipath_piosize4k >> 2;
+	}
+	dev_info(&dd->pcidev->dev,
+		"Rewrite PIO buffer %u, to recover from parity error\n",
+		pnum);
+	*pbuf = dwcnt+1; /* no flush required, since already in freeze */
+	while(--dwcnt)
+		*pbuf++ = 0;
+}
+
+/*
  * Called when we might have an error that is specific to a particular
  * PIO buffer, and may need to cancel that buffer, so it can be re-used.
+ * If rewrite is true, and bits are set in the sendbufferror registers,
+ * we'll write to the buffer, for error recovery on parity errors.
  */
-void ipath_disarm_senderrbufs(struct ipath_devdata *dd)
+void ipath_disarm_senderrbufs(struct ipath_devdata *dd, int rewrite)
 {
 	u32 piobcnt;
 	unsigned long sbuf[4];
@@ -74,8 +103,11 @@
 		}
 
 		for (i = 0; i < piobcnt; i++)
-			if (test_bit(i, sbuf))
+			if (test_bit(i, sbuf)) {
+				if (rewrite)
+					ipath_clrpiobuf(dd, i);
 				ipath_disarm_piobufs(dd, i, 1);
+			}
 		dd->ipath_lastcancel = jiffies+3; /* no armlaunch for a bit */
 	}
 }
@@ -114,7 +146,7 @@
 {
 	u64 ignore_this_time = 0;
 
-	ipath_disarm_senderrbufs(dd);
+	ipath_disarm_senderrbufs(dd, 0);
 	if ((errs & E_SUM_LINK_PKTERRS) &&
 	    !(dd->ipath_flags & IPATH_LINKACTIVE)) {
 		/*
@@ -403,10 +435,13 @@
 	 * happens so often we never want to count it.
 	 */
 	if (dd->ipath_lasterror & ~INFINIPATH_E_IBSTATUSCHANGED) {
-		ipath_decode_err(msg, sizeof msg, dd->ipath_lasterror &
-				 ~INFINIPATH_E_IBSTATUSCHANGED);
+		int iserr;
+		iserr = ipath_decode_err(msg, sizeof msg,
+				dd->ipath_lasterror &
+				~INFINIPATH_E_IBSTATUSCHANGED);
 		if (dd->ipath_lasterror &
-		    ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL))
+			~(INFINIPATH_E_RRCVEGRFULL |
+			INFINIPATH_E_RRCVHDRFULL | INFINIPATH_E_PKTERRS))
 			ipath_dev_err(dd, "Suppressed %u messages for "
 				      "fast-repeating errors (%s) (%llx)\n",
 				      supp_msgs, msg,
@@ -420,8 +455,13 @@
 			 * them. So only complain about these at debug
 			 * level.
 			 */
-			ipath_dbg("Suppressed %u messages for %s\n",
-				  supp_msgs, msg);
+			if (iserr)
+				ipath_dbg("Suppressed %u messages for %s\n",
+					  supp_msgs, msg);
+			else
+				ipath_cdbg(ERRPKT,
+					"Suppressed %u messages for %s\n",
+					  supp_msgs, msg);
 		}
 	}
 }
@@ -462,7 +502,7 @@
 {
 	char msg[512];
 	u64 ignore_this_time = 0;
-	int i;
+	int i, iserr = 0;
 	int chkerrpkts = 0, noprint = 0;
 	unsigned supp_msgs;
 
@@ -502,6 +542,7 @@
 	}
 
 	if (supp_msgs == 250000) {
+		int s_iserr;
 		/*
 		 * It's not entirely reasonable assuming that the errors set
 		 * in the last clear period are all responsible for the
@@ -511,17 +552,17 @@
 		dd->ipath_maskederrs |= dd->ipath_lasterror | errs;
 		ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask,
 				 ~dd->ipath_maskederrs);
-		ipath_decode_err(msg, sizeof msg,
+		s_iserr = ipath_decode_err(msg, sizeof msg,
 				 (dd->ipath_maskederrs & ~dd->
 				  ipath_ignorederrs));
 
 		if ((dd->ipath_maskederrs & ~dd->ipath_ignorederrs) &
-		    ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL))
-			ipath_dev_err(dd, "Disabling error(s) %llx because "
-				      "occurring too frequently (%s)\n",
-				      (unsigned long long)
-				      (dd->ipath_maskederrs &
-				       ~dd->ipath_ignorederrs), msg);
+			~(INFINIPATH_E_RRCVEGRFULL |
+			INFINIPATH_E_RRCVHDRFULL | INFINIPATH_E_PKTERRS))
+			ipath_dev_err(dd, "Temporarily disabling "
+			    "error(s) %llx reporting; too frequent (%s)\n",
+				(unsigned long long) (dd->ipath_maskederrs &
+				~dd->ipath_ignorederrs), msg);
 		else {
 			/*
 			 * rcvegrfull and rcvhdrqfull are "normal",
@@ -530,8 +571,15 @@
 			 * processing them.  So only complain about
 			 * these at debug level.
 			 */
-			ipath_dbg("Disabling frequent queue full errors "
-				  "(%s)\n", msg);
+			if (s_iserr)
+				ipath_dbg("Temporarily disabling reporting "
+				    "too frequent queue full errors (%s)\n",
+				    msg);
+			else
+				ipath_cdbg(ERRPKT,
+				    "Temporarily disabling reporting too"
+				    " frequent packet errors (%s)\n",
+				    msg);
 		}
 
 		/*
@@ -589,6 +637,8 @@
 		ipath_stats.sps_crcerrs++;
 		chkerrpkts = 1;
 	}
+	iserr = errs & ~(E_SUM_PKTERRS | INFINIPATH_E_PKTERRS);
+
 
 	/*
 	 * We don't want to print these two as they happen, or we can make
@@ -677,8 +727,13 @@
 		*dd->ipath_statusp &= ~IPATH_STATUS_IB_CONF;
 	}
 
-	if (!noprint && *msg)
-		ipath_dev_err(dd, "%s error\n", msg);
+	if (!noprint && *msg) {
+		if (iserr)
+			ipath_dev_err(dd, "%s error\n", msg);
+		else
+			dev_info(&dd->pcidev->dev, "%s packet problems\n",
+				msg);
+	}
 	if (dd->ipath_state_wanted & dd->ipath_flags) {
 		ipath_cdbg(VERBOSE, "driver wanted state %x, iflags now %x, "
 			   "waking\n", dd->ipath_state_wanted,
@@ -819,11 +874,10 @@
 		struct ipath_portdata *pd = dd->ipath_pd[i];
 		if (portr & (1 << i) && pd && pd->port_cnt &&
 			test_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag)) {
-			int rcbit;
 			clear_bit(IPATH_PORT_WAITING_RCV,
 				  &pd->port_flag);
-			rcbit = i + INFINIPATH_R_INTRAVAIL_SHIFT;
-			clear_bit(1UL << rcbit, &dd->ipath_rcvctrl);
+			clear_bit(i + INFINIPATH_R_INTRAVAIL_SHIFT,
+				  &dd->ipath_rcvctrl);
 			wake_up_interruptible(&pd->port_wait);
 			rcvdint = 1;
 		}
diff --git a/drivers/infiniband/hw/ipath/ipath_kernel.h b/drivers/infiniband/hw/ipath/ipath_kernel.h
index 6d8d05f..e900c25 100644
--- a/drivers/infiniband/hw/ipath/ipath_kernel.h
+++ b/drivers/infiniband/hw/ipath/ipath_kernel.h
@@ -590,7 +590,6 @@
 void ipath_disable_wc(struct ipath_devdata *dd);
 int ipath_count_units(int *npresentp, int *nupp, u32 *maxportsp);
 void ipath_shutdown_device(struct ipath_devdata *);
-void ipath_disarm_senderrbufs(struct ipath_devdata *);
 
 struct file_operations;
 int ipath_cdev_init(int minor, char *name, const struct file_operations *fops,
@@ -611,7 +610,7 @@
 extern int ipath_diag_inuse;
 
 irqreturn_t ipath_intr(int irq, void *devid);
-void ipath_decode_err(char *buf, size_t blen, ipath_err_t err);
+int ipath_decode_err(char *buf, size_t blen, ipath_err_t err);
 #if __IPATH_INFO || __IPATH_DBG
 extern const char *ipath_ibcstatus_str[];
 #endif
@@ -701,6 +700,8 @@
 #define IPATH_PORT_WAITING_RCV   2
 		/* waiting for a PIO buffer to be available */
 #define IPATH_PORT_WAITING_PIO   3
+		/* master has not finished initializing */
+#define IPATH_PORT_MASTER_UNINIT 4
 
 /* free up any allocated data at closes */
 void ipath_free_data(struct ipath_portdata *dd);
@@ -711,6 +712,7 @@
 void ipath_init_iba6110_funcs(struct ipath_devdata *);
 void ipath_get_eeprom_info(struct ipath_devdata *);
 u64 ipath_snap_cntr(struct ipath_devdata *, ipath_creg);
+void ipath_disarm_senderrbufs(struct ipath_devdata *, int);
 
 /*
  * number of words used for protocol header if not set by ipath_userinit();
@@ -754,8 +756,6 @@
 /* these are used for the registers that vary with port */
 void ipath_write_kreg_port(const struct ipath_devdata *, ipath_kreg,
 			   unsigned, u64);
-u64 ipath_read_kreg64_port(const struct ipath_devdata *, ipath_kreg,
-			   unsigned);
 
 /*
  * We could have a single register get/put routine, that takes a group type,
@@ -897,6 +897,8 @@
 
 extern unsigned ipath_debug; /* debugging bit mask */
 
+#define IPATH_MAX_PARITY_ATTEMPTS 10000 /* max times to try recovery */
+
 const char *ipath_get_unit_name(int unit);
 
 extern struct mutex ipath_mutex;
diff --git a/drivers/infiniband/hw/ipath/ipath_keys.c b/drivers/infiniband/hw/ipath/ipath_keys.c
index 851763d..dd487c1 100644
--- a/drivers/infiniband/hw/ipath/ipath_keys.c
+++ b/drivers/infiniband/hw/ipath/ipath_keys.c
@@ -61,7 +61,7 @@
 		r = (r + 1) & (rkt->max - 1);
 		if (r == n) {
 			spin_unlock_irqrestore(&rkt->lock, flags);
-			ipath_dbg(KERN_INFO "LKEY table full\n");
+			ipath_dbg("LKEY table full\n");
 			ret = 0;
 			goto bail;
 		}
@@ -133,6 +133,12 @@
 	 * being reversible by calling bus_to_virt().
 	 */
 	if (sge->lkey == 0) {
+		struct ipath_pd *pd = to_ipd(qp->ibqp.pd);
+
+		if (pd->user) {
+			ret = 0;
+			goto bail;
+		}
 		isge->mr = NULL;
 		isge->vaddr = (void *) sge->addr;
 		isge->length = sge->length;
@@ -206,6 +212,12 @@
 	 * (see ipath_get_dma_mr and ipath_dma.c).
 	 */
 	if (rkey == 0) {
+		struct ipath_pd *pd = to_ipd(qp->ibqp.pd);
+
+		if (pd->user) {
+			ret = 0;
+			goto bail;
+		}
 		sge->mr = NULL;
 		sge->vaddr = (void *) vaddr;
 		sge->length = len;
diff --git a/drivers/infiniband/hw/ipath/ipath_mr.c b/drivers/infiniband/hw/ipath/ipath_mr.c
index 8cc8598..31e7073 100644
--- a/drivers/infiniband/hw/ipath/ipath_mr.c
+++ b/drivers/infiniband/hw/ipath/ipath_mr.c
@@ -210,9 +210,15 @@
 	m = 0;
 	n = 0;
 	list_for_each_entry(chunk, &region->chunk_list, list) {
-		for (i = 0; i < chunk->nmap; i++) {
-			mr->mr.map[m]->segs[n].vaddr =
-				page_address(chunk->page_list[i].page);
+		for (i = 0; i < chunk->nents; i++) {
+			void *vaddr;
+
+			vaddr = page_address(chunk->page_list[i].page);
+			if (!vaddr) {
+				ret = ERR_PTR(-EINVAL);
+				goto bail;
+			}
+			mr->mr.map[m]->segs[n].vaddr = vaddr;
 			mr->mr.map[m]->segs[n].length = region->page_size;
 			n++;
 			if (n == IPATH_SEGSZ) {
diff --git a/drivers/infiniband/hw/ipath/ipath_qp.c b/drivers/infiniband/hw/ipath/ipath_qp.c
index 64f07b1..16db9ac 100644
--- a/drivers/infiniband/hw/ipath/ipath_qp.c
+++ b/drivers/infiniband/hw/ipath/ipath_qp.c
@@ -81,11 +81,51 @@
 	32768			/* 1E */
 };
 
-static u32 alloc_qpn(struct ipath_qp_table *qpt)
+
+static void get_map_page(struct ipath_qp_table *qpt, struct qpn_map *map)
+{
+	unsigned long page = get_zeroed_page(GFP_KERNEL);
+	unsigned long flags;
+
+	/*
+	 * Free the page if someone raced with us installing it.
+	 */
+
+	spin_lock_irqsave(&qpt->lock, flags);
+	if (map->page)
+		free_page(page);
+	else
+		map->page = (void *)page;
+	spin_unlock_irqrestore(&qpt->lock, flags);
+}
+
+
+static int alloc_qpn(struct ipath_qp_table *qpt, enum ib_qp_type type)
 {
 	u32 i, offset, max_scan, qpn;
 	struct qpn_map *map;
-	u32 ret;
+	u32 ret = -1;
+
+	if (type == IB_QPT_SMI)
+		ret = 0;
+	else if (type == IB_QPT_GSI)
+		ret = 1;
+
+	if (ret != -1) {
+		map = &qpt->map[0];
+		if (unlikely(!map->page)) {
+			get_map_page(qpt, map);
+			if (unlikely(!map->page)) {
+				ret = -ENOMEM;
+				goto bail;
+			}
+		}
+		if (!test_and_set_bit(ret, map->page))
+			atomic_dec(&map->n_free);
+		else
+			ret = -EBUSY;
+		goto bail;
+	}
 
 	qpn = qpt->last + 1;
 	if (qpn >= QPN_MAX)
@@ -95,19 +135,7 @@
 	max_scan = qpt->nmaps - !offset;
 	for (i = 0;;) {
 		if (unlikely(!map->page)) {
-			unsigned long page = get_zeroed_page(GFP_KERNEL);
-			unsigned long flags;
-
-			/*
-			 * Free the page if someone raced with us
-			 * installing it:
-			 */
-			spin_lock_irqsave(&qpt->lock, flags);
-			if (map->page)
-				free_page(page);
-			else
-				map->page = (void *)page;
-			spin_unlock_irqrestore(&qpt->lock, flags);
+			get_map_page(qpt, map);
 			if (unlikely(!map->page))
 				break;
 		}
@@ -151,7 +179,7 @@
 		qpn = mk_qpn(qpt, map, offset);
 	}
 
-	ret = 0;
+	ret = -ENOMEM;
 
 bail:
 	return ret;
@@ -180,29 +208,19 @@
 			   enum ib_qp_type type)
 {
 	unsigned long flags;
-	u32 qpn;
 	int ret;
 
-	if (type == IB_QPT_SMI)
-		qpn = 0;
-	else if (type == IB_QPT_GSI)
-		qpn = 1;
-	else {
-		/* Allocate the next available QPN */
-		qpn = alloc_qpn(qpt);
-		if (qpn == 0) {
-			ret = -ENOMEM;
-			goto bail;
-		}
-	}
-	qp->ibqp.qp_num = qpn;
+	ret = alloc_qpn(qpt, type);
+	if (ret < 0)
+		goto bail;
+	qp->ibqp.qp_num = ret;
 
 	/* Add the QP to the hash table. */
 	spin_lock_irqsave(&qpt->lock, flags);
 
-	qpn %= qpt->max;
-	qp->next = qpt->table[qpn];
-	qpt->table[qpn] = qp;
+	ret %= qpt->max;
+	qp->next = qpt->table[ret];
+	qpt->table[ret] = qp;
 	atomic_inc(&qp->refcount);
 
 	spin_unlock_irqrestore(&qpt->lock, flags);
@@ -245,9 +263,7 @@
 	if (!fnd)
 		return;
 
-	/* If QPN is not reserved, mark QPN free in the bitmap. */
-	if (qp->ibqp.qp_num > 1)
-		free_qpn(qpt, qp->ibqp.qp_num);
+	free_qpn(qpt, qp->ibqp.qp_num);
 
 	wait_event(qp->wait, !atomic_read(&qp->refcount));
 }
@@ -270,11 +286,10 @@
 
 		while (qp) {
 			nqp = qp->next;
-			if (qp->ibqp.qp_num > 1)
-				free_qpn(qpt, qp->ibqp.qp_num);
+			free_qpn(qpt, qp->ibqp.qp_num);
 			if (!atomic_dec_and_test(&qp->refcount) ||
 			    !ipath_destroy_qp(&qp->ibqp))
-				ipath_dbg(KERN_INFO "QP memory leak!\n");
+				ipath_dbg("QP memory leak!\n");
 			qp = nqp;
 		}
 	}
@@ -320,7 +335,8 @@
 	qp->remote_qpn = 0;
 	qp->qkey = 0;
 	qp->qp_access_flags = 0;
-	clear_bit(IPATH_S_BUSY, &qp->s_flags);
+	qp->s_busy = 0;
+	qp->s_flags &= ~IPATH_S_SIGNAL_REQ_WR;
 	qp->s_hdrwords = 0;
 	qp->s_psn = 0;
 	qp->r_psn = 0;
@@ -333,7 +349,6 @@
 		qp->r_state = IB_OPCODE_UC_SEND_LAST;
 	}
 	qp->s_ack_state = IB_OPCODE_RC_ACKNOWLEDGE;
-	qp->r_ack_state = IB_OPCODE_RC_ACKNOWLEDGE;
 	qp->r_nak_state = 0;
 	qp->r_wrid_valid = 0;
 	qp->s_rnr_timeout = 0;
@@ -344,6 +359,10 @@
 	qp->s_ssn = 1;
 	qp->s_lsn = 0;
 	qp->s_wait_credit = 0;
+	memset(qp->s_ack_queue, 0, sizeof(qp->s_ack_queue));
+	qp->r_head_ack_queue = 0;
+	qp->s_tail_ack_queue = 0;
+	qp->s_num_rd_atomic = 0;
 	if (qp->r_rq.wq) {
 		qp->r_rq.wq->head = 0;
 		qp->r_rq.wq->tail = 0;
@@ -357,7 +376,7 @@
  * @err: the receive completion error to signal if a RWQE is active
  *
  * Flushes both send and receive work queues.
- * QP s_lock should be held and interrupts disabled.
+ * The QP s_lock should be held and interrupts disabled.
  */
 
 void ipath_error_qp(struct ipath_qp *qp, enum ib_wc_status err)
@@ -365,7 +384,7 @@
 	struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
 	struct ib_wc wc;
 
-	ipath_dbg(KERN_INFO "QP%d/%d in error state\n",
+	ipath_dbg("QP%d/%d in error state\n",
 		  qp->ibqp.qp_num, qp->remote_qpn);
 
 	spin_lock(&dev->pending_lock);
@@ -389,6 +408,8 @@
 	wc.port_num = 0;
 	if (qp->r_wrid_valid) {
 		qp->r_wrid_valid = 0;
+		wc.wr_id = qp->r_wr_id;
+		wc.opcode = IB_WC_RECV;
 		wc.status = err;
 		ipath_cq_enter(to_icq(qp->ibqp.send_cq), &wc, 1);
 	}
@@ -503,13 +524,17 @@
 		    attr->path_mig_state != IB_MIG_REARM)
 			goto inval;
 
+	if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC)
+		if (attr->max_dest_rd_atomic > IPATH_MAX_RDMA_ATOMIC)
+			goto inval;
+
 	switch (new_state) {
 	case IB_QPS_RESET:
 		ipath_reset_qp(qp);
 		break;
 
 	case IB_QPS_ERR:
-		ipath_error_qp(qp, IB_WC_GENERAL_ERR);
+		ipath_error_qp(qp, IB_WC_WR_FLUSH_ERR);
 		break;
 
 	default:
@@ -559,6 +584,12 @@
 	if (attr_mask & IB_QP_QKEY)
 		qp->qkey = attr->qkey;
 
+	if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC)
+		qp->r_max_rd_atomic = attr->max_dest_rd_atomic;
+
+	if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC)
+		qp->s_max_rd_atomic = attr->max_rd_atomic;
+
 	qp->state = new_state;
 	spin_unlock_irqrestore(&qp->s_lock, flags);
 
@@ -598,8 +629,8 @@
 	attr->alt_pkey_index = 0;
 	attr->en_sqd_async_notify = 0;
 	attr->sq_draining = 0;
-	attr->max_rd_atomic = 1;
-	attr->max_dest_rd_atomic = 1;
+	attr->max_rd_atomic = qp->s_max_rd_atomic;
+	attr->max_dest_rd_atomic = qp->r_max_rd_atomic;
 	attr->min_rnr_timer = qp->r_min_rnr_timer;
 	attr->port_num = 1;
 	attr->timeout = qp->timeout;
@@ -614,7 +645,7 @@
 	init_attr->recv_cq = qp->ibqp.recv_cq;
 	init_attr->srq = qp->ibqp.srq;
 	init_attr->cap = attr->cap;
-	if (qp->s_flags & (1 << IPATH_S_SIGNAL_REQ_WR))
+	if (qp->s_flags & IPATH_S_SIGNAL_REQ_WR)
 		init_attr->sq_sig_type = IB_SIGNAL_REQ_WR;
 	else
 		init_attr->sq_sig_type = IB_SIGNAL_ALL_WR;
@@ -786,7 +817,7 @@
 		qp->s_size = init_attr->cap.max_send_wr + 1;
 		qp->s_max_sge = init_attr->cap.max_send_sge;
 		if (init_attr->sq_sig_type == IB_SIGNAL_REQ_WR)
-			qp->s_flags = 1 << IPATH_S_SIGNAL_REQ_WR;
+			qp->s_flags = IPATH_S_SIGNAL_REQ_WR;
 		else
 			qp->s_flags = 0;
 		dev = to_idev(ibpd->device);
@@ -958,7 +989,7 @@
  * @wc: the WC responsible for putting the QP in this state
  *
  * Flushes the send work queue.
- * The QP s_lock should be held.
+ * The QP s_lock should be held and interrupts disabled.
  */
 
 void ipath_sqerror_qp(struct ipath_qp *qp, struct ib_wc *wc)
@@ -966,7 +997,7 @@
 	struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
 	struct ipath_swqe *wqe = get_swqe_ptr(qp, qp->s_last);
 
-	ipath_dbg(KERN_INFO "Send queue error on QP%d/%d: err: %d\n",
+	ipath_dbg("Send queue error on QP%d/%d: err: %d\n",
 		  qp->ibqp.qp_num, qp->remote_qpn, wc->status);
 
 	spin_lock(&dev->pending_lock);
@@ -984,12 +1015,12 @@
 	wc->status = IB_WC_WR_FLUSH_ERR;
 
 	while (qp->s_last != qp->s_head) {
+		wqe = get_swqe_ptr(qp, qp->s_last);
 		wc->wr_id = wqe->wr.wr_id;
 		wc->opcode = ib_ipath_wc_opcode[wqe->wr.opcode];
 		ipath_cq_enter(to_icq(qp->ibqp.send_cq), wc, 1);
 		if (++qp->s_last >= qp->s_size)
 			qp->s_last = 0;
-		wqe = get_swqe_ptr(qp, qp->s_last);
 	}
 	qp->s_cur = qp->s_tail = qp->s_head;
 	qp->state = IB_QPS_SQE;
diff --git a/drivers/infiniband/hw/ipath/ipath_rc.c b/drivers/infiniband/hw/ipath/ipath_rc.c
index 5ff20cb..b4b88d0 100644
--- a/drivers/infiniband/hw/ipath/ipath_rc.c
+++ b/drivers/infiniband/hw/ipath/ipath_rc.c
@@ -37,6 +37,19 @@
 /* cut down ridiculously long IB macro names */
 #define OP(x) IB_OPCODE_RC_##x
 
+static u32 restart_sge(struct ipath_sge_state *ss, struct ipath_swqe *wqe,
+		       u32 psn, u32 pmtu)
+{
+	u32 len;
+
+	len = ((psn - wqe->psn) & IPATH_PSN_MASK) * pmtu;
+	ss->sge = wqe->sg_list[0];
+	ss->sg_list = wqe->sg_list + 1;
+	ss->num_sge = wqe->wr.num_sge;
+	ipath_skip_sge(ss, len);
+	return wqe->length - len;
+}
+
 /**
  * ipath_init_restart- initialize the qp->s_sge after a restart
  * @qp: the QP who's SGE we're restarting
@@ -47,15 +60,9 @@
 static void ipath_init_restart(struct ipath_qp *qp, struct ipath_swqe *wqe)
 {
 	struct ipath_ibdev *dev;
-	u32 len;
 
-	len = ((qp->s_psn - wqe->psn) & IPATH_PSN_MASK) *
-		ib_mtu_enum_to_int(qp->path_mtu);
-	qp->s_sge.sge = wqe->sg_list[0];
-	qp->s_sge.sg_list = wqe->sg_list + 1;
-	qp->s_sge.num_sge = wqe->wr.num_sge;
-	ipath_skip_sge(&qp->s_sge, len);
-	qp->s_len = wqe->length - len;
+	qp->s_len = restart_sge(&qp->s_sge, wqe, qp->s_psn,
+				ib_mtu_enum_to_int(qp->path_mtu));
 	dev = to_idev(qp->ibqp.device);
 	spin_lock(&dev->pending_lock);
 	if (list_empty(&qp->timerwait))
@@ -70,107 +77,123 @@
  * @ohdr: a pointer to the IB header being constructed
  * @pmtu: the path MTU
  *
- * Return bth0 if constructed; otherwise, return 0.
+ * Return 1 if constructed; otherwise, return 0.
+ * Note that we are in the responder's side of the QP context.
  * Note the QP s_lock must be held.
  */
-u32 ipath_make_rc_ack(struct ipath_qp *qp,
-		      struct ipath_other_headers *ohdr,
-		      u32 pmtu)
+static int ipath_make_rc_ack(struct ipath_qp *qp,
+			     struct ipath_other_headers *ohdr,
+			     u32 pmtu, u32 *bth0p, u32 *bth2p)
 {
+	struct ipath_ack_entry *e;
 	u32 hwords;
 	u32 len;
 	u32 bth0;
+	u32 bth2;
 
 	/* header size in 32-bit words LRH+BTH = (8+12)/4. */
 	hwords = 5;
 
-	/*
-	 * Send a response.  Note that we are in the responder's
-	 * side of the QP context.
-	 */
 	switch (qp->s_ack_state) {
-	case OP(RDMA_READ_REQUEST):
-		qp->s_cur_sge = &qp->s_rdma_sge;
-		len = qp->s_rdma_len;
-		if (len > pmtu) {
-			len = pmtu;
-			qp->s_ack_state = OP(RDMA_READ_RESPONSE_FIRST);
-		} else
-			qp->s_ack_state = OP(RDMA_READ_RESPONSE_ONLY);
-		qp->s_rdma_len -= len;
+	case OP(RDMA_READ_RESPONSE_LAST):
+	case OP(RDMA_READ_RESPONSE_ONLY):
+	case OP(ATOMIC_ACKNOWLEDGE):
+		qp->s_ack_state = OP(ACKNOWLEDGE);
+		/* FALLTHROUGH */
+	case OP(ACKNOWLEDGE):
+		/* Check for no next entry in the queue. */
+		if (qp->r_head_ack_queue == qp->s_tail_ack_queue) {
+			if (qp->s_flags & IPATH_S_ACK_PENDING)
+				goto normal;
+			goto bail;
+		}
+
+		e = &qp->s_ack_queue[qp->s_tail_ack_queue];
+		if (e->opcode == OP(RDMA_READ_REQUEST)) {
+			/* Copy SGE state in case we need to resend */
+			qp->s_ack_rdma_sge = e->rdma_sge;
+			qp->s_cur_sge = &qp->s_ack_rdma_sge;
+			len = e->rdma_sge.sge.sge_length;
+			if (len > pmtu) {
+				len = pmtu;
+				qp->s_ack_state = OP(RDMA_READ_RESPONSE_FIRST);
+			} else {
+				qp->s_ack_state = OP(RDMA_READ_RESPONSE_ONLY);
+				if (++qp->s_tail_ack_queue >
+				    IPATH_MAX_RDMA_ATOMIC)
+					qp->s_tail_ack_queue = 0;
+			}
+			ohdr->u.aeth = ipath_compute_aeth(qp);
+			hwords++;
+			qp->s_ack_rdma_psn = e->psn;
+			bth2 = qp->s_ack_rdma_psn++ & IPATH_PSN_MASK;
+		} else {
+			/* COMPARE_SWAP or FETCH_ADD */
+			qp->s_cur_sge = NULL;
+			len = 0;
+			qp->s_ack_state = OP(ATOMIC_ACKNOWLEDGE);
+			ohdr->u.at.aeth = ipath_compute_aeth(qp);
+			ohdr->u.at.atomic_ack_eth[0] =
+				cpu_to_be32(e->atomic_data >> 32);
+			ohdr->u.at.atomic_ack_eth[1] =
+				cpu_to_be32(e->atomic_data);
+			hwords += sizeof(ohdr->u.at) / sizeof(u32);
+			bth2 = e->psn;
+			if (++qp->s_tail_ack_queue > IPATH_MAX_RDMA_ATOMIC)
+				qp->s_tail_ack_queue = 0;
+		}
 		bth0 = qp->s_ack_state << 24;
-		ohdr->u.aeth = ipath_compute_aeth(qp);
-		hwords++;
 		break;
 
 	case OP(RDMA_READ_RESPONSE_FIRST):
 		qp->s_ack_state = OP(RDMA_READ_RESPONSE_MIDDLE);
 		/* FALLTHROUGH */
 	case OP(RDMA_READ_RESPONSE_MIDDLE):
-		qp->s_cur_sge = &qp->s_rdma_sge;
-		len = qp->s_rdma_len;
+		len = qp->s_ack_rdma_sge.sge.sge_length;
 		if (len > pmtu)
 			len = pmtu;
 		else {
 			ohdr->u.aeth = ipath_compute_aeth(qp);
 			hwords++;
 			qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST);
+			if (++qp->s_tail_ack_queue > IPATH_MAX_RDMA_ATOMIC)
+				qp->s_tail_ack_queue = 0;
 		}
-		qp->s_rdma_len -= len;
 		bth0 = qp->s_ack_state << 24;
-		break;
-
-	case OP(RDMA_READ_RESPONSE_LAST):
-	case OP(RDMA_READ_RESPONSE_ONLY):
-		/*
-		 * We have to prevent new requests from changing
-		 * the r_sge state while a ipath_verbs_send()
-		 * is in progress.
-		 */
-		qp->s_ack_state = OP(ACKNOWLEDGE);
-		bth0 = 0;
-		goto bail;
-
-	case OP(COMPARE_SWAP):
-	case OP(FETCH_ADD):
-		qp->s_cur_sge = NULL;
-		len = 0;
-		/*
-		 * Set the s_ack_state so the receive interrupt handler
-		 * won't try to send an ACK (out of order) until this one
-		 * is actually sent.
-		 */
-		qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST);
-		bth0 = OP(ATOMIC_ACKNOWLEDGE) << 24;
-		ohdr->u.at.aeth = ipath_compute_aeth(qp);
-		ohdr->u.at.atomic_ack_eth = cpu_to_be64(qp->r_atomic_data);
-		hwords += sizeof(ohdr->u.at) / 4;
+		bth2 = qp->s_ack_rdma_psn++ & IPATH_PSN_MASK;
 		break;
 
 	default:
-		/* Send a regular ACK. */
-		qp->s_cur_sge = NULL;
-		len = 0;
+	normal:
 		/*
-		 * Set the s_ack_state so the receive interrupt handler
-		 * won't try to send an ACK (out of order) until this one
-		 * is actually sent.
+		 * Send a regular ACK.
+		 * Set the s_ack_state so we wait until after sending
+		 * the ACK before setting s_ack_state to ACKNOWLEDGE
+		 * (see above).
 		 */
-		qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST);
-		bth0 = OP(ACKNOWLEDGE) << 24;
+		qp->s_ack_state = OP(ATOMIC_ACKNOWLEDGE);
+		qp->s_flags &= ~IPATH_S_ACK_PENDING;
+		qp->s_cur_sge = NULL;
 		if (qp->s_nak_state)
-			ohdr->u.aeth = cpu_to_be32((qp->r_msn & IPATH_MSN_MASK) |
-						    (qp->s_nak_state <<
-						     IPATH_AETH_CREDIT_SHIFT));
+			ohdr->u.aeth =
+				cpu_to_be32((qp->r_msn & IPATH_MSN_MASK) |
+					    (qp->s_nak_state <<
+					     IPATH_AETH_CREDIT_SHIFT));
 		else
 			ohdr->u.aeth = ipath_compute_aeth(qp);
 		hwords++;
+		len = 0;
+		bth0 = OP(ACKNOWLEDGE) << 24;
+		bth2 = qp->s_ack_psn & IPATH_PSN_MASK;
 	}
 	qp->s_hdrwords = hwords;
 	qp->s_cur_size = len;
+	*bth0p = bth0;
+	*bth2p = bth2;
+	return 1;
 
 bail:
-	return bth0;
+	return 0;
 }
 
 /**
@@ -197,9 +220,16 @@
 	u32 bth2;
 	char newreq;
 
+	/* Sending responses has higher priority over sending requests. */
+	if ((qp->r_head_ack_queue != qp->s_tail_ack_queue ||
+	     (qp->s_flags & IPATH_S_ACK_PENDING) ||
+	     qp->s_ack_state != IB_OPCODE_RC_ACKNOWLEDGE) &&
+	    ipath_make_rc_ack(qp, ohdr, pmtu, bth0p, bth2p))
+		goto done;
+
 	if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_SEND_OK) ||
 	    qp->s_rnr_timeout)
-		goto done;
+		goto bail;
 
 	/* Limit the number of packets sent without an ACK. */
 	if (ipath_cmp24(qp->s_psn, qp->s_last_psn + IPATH_PSN_CREDIT) > 0) {
@@ -210,7 +240,7 @@
 			list_add_tail(&qp->timerwait,
 				      &dev->pending[dev->pending_index]);
 		spin_unlock(&dev->pending_lock);
-		goto done;
+		goto bail;
 	}
 
 	/* header size in 32-bit words LRH+BTH = (8+12)/4. */
@@ -232,7 +262,16 @@
 		if (qp->s_cur == qp->s_tail) {
 			/* Check if send work queue is empty. */
 			if (qp->s_tail == qp->s_head)
-				goto done;
+				goto bail;
+			/*
+			 * If a fence is requested, wait for previous
+			 * RDMA read and atomic operations to finish.
+			 */
+			if ((wqe->wr.send_flags & IB_SEND_FENCE) &&
+			    qp->s_num_rd_atomic) {
+				qp->s_flags |= IPATH_S_FENCE_PENDING;
+				goto bail;
+			}
 			wqe->psn = qp->s_next_psn;
 			newreq = 1;
 		}
@@ -250,7 +289,7 @@
 			/* If no credit, return. */
 			if (qp->s_lsn != (u32) -1 &&
 			    ipath_cmp24(wqe->ssn, qp->s_lsn + 1) > 0)
-				goto done;
+				goto bail;
 			wqe->lpsn = wqe->psn;
 			if (len > pmtu) {
 				wqe->lpsn += (len - 1) / pmtu;
@@ -281,13 +320,13 @@
 			/* If no credit, return. */
 			if (qp->s_lsn != (u32) -1 &&
 			    ipath_cmp24(wqe->ssn, qp->s_lsn + 1) > 0)
-				goto done;
+				goto bail;
 			ohdr->u.rc.reth.vaddr =
 				cpu_to_be64(wqe->wr.wr.rdma.remote_addr);
 			ohdr->u.rc.reth.rkey =
 				cpu_to_be32(wqe->wr.wr.rdma.rkey);
 			ohdr->u.rc.reth.length = cpu_to_be32(len);
-			hwords += sizeof(struct ib_reth) / 4;
+			hwords += sizeof(struct ib_reth) / sizeof(u32);
 			wqe->lpsn = wqe->psn;
 			if (len > pmtu) {
 				wqe->lpsn += (len - 1) / pmtu;
@@ -312,14 +351,17 @@
 			break;
 
 		case IB_WR_RDMA_READ:
-			ohdr->u.rc.reth.vaddr =
-				cpu_to_be64(wqe->wr.wr.rdma.remote_addr);
-			ohdr->u.rc.reth.rkey =
-				cpu_to_be32(wqe->wr.wr.rdma.rkey);
-			ohdr->u.rc.reth.length = cpu_to_be32(len);
-			qp->s_state = OP(RDMA_READ_REQUEST);
-			hwords += sizeof(ohdr->u.rc.reth) / 4;
+			/*
+			 * Don't allow more operations to be started
+			 * than the QP limits allow.
+			 */
 			if (newreq) {
+				if (qp->s_num_rd_atomic >=
+				    qp->s_max_rd_atomic) {
+					qp->s_flags |= IPATH_S_RDMAR_PENDING;
+					goto bail;
+				}
+				qp->s_num_rd_atomic++;
 				if (qp->s_lsn != (u32) -1)
 					qp->s_lsn++;
 				/*
@@ -330,6 +372,13 @@
 					qp->s_next_psn += (len - 1) / pmtu;
 				wqe->lpsn = qp->s_next_psn++;
 			}
+			ohdr->u.rc.reth.vaddr =
+				cpu_to_be64(wqe->wr.wr.rdma.remote_addr);
+			ohdr->u.rc.reth.rkey =
+				cpu_to_be32(wqe->wr.wr.rdma.rkey);
+			ohdr->u.rc.reth.length = cpu_to_be32(len);
+			qp->s_state = OP(RDMA_READ_REQUEST);
+			hwords += sizeof(ohdr->u.rc.reth) / sizeof(u32);
 			ss = NULL;
 			len = 0;
 			if (++qp->s_cur == qp->s_size)
@@ -338,32 +387,48 @@
 
 		case IB_WR_ATOMIC_CMP_AND_SWP:
 		case IB_WR_ATOMIC_FETCH_AND_ADD:
-			if (wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP)
-				qp->s_state = OP(COMPARE_SWAP);
-			else
-				qp->s_state = OP(FETCH_ADD);
-			ohdr->u.atomic_eth.vaddr = cpu_to_be64(
-				wqe->wr.wr.atomic.remote_addr);
-			ohdr->u.atomic_eth.rkey = cpu_to_be32(
-				wqe->wr.wr.atomic.rkey);
-			ohdr->u.atomic_eth.swap_data = cpu_to_be64(
-				wqe->wr.wr.atomic.swap);
-			ohdr->u.atomic_eth.compare_data = cpu_to_be64(
-				wqe->wr.wr.atomic.compare_add);
-			hwords += sizeof(struct ib_atomic_eth) / 4;
+			/*
+			 * Don't allow more operations to be started
+			 * than the QP limits allow.
+			 */
 			if (newreq) {
+				if (qp->s_num_rd_atomic >=
+				    qp->s_max_rd_atomic) {
+					qp->s_flags |= IPATH_S_RDMAR_PENDING;
+					goto bail;
+				}
+				qp->s_num_rd_atomic++;
 				if (qp->s_lsn != (u32) -1)
 					qp->s_lsn++;
 				wqe->lpsn = wqe->psn;
 			}
-			if (++qp->s_cur == qp->s_size)
-				qp->s_cur = 0;
+			if (wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP) {
+				qp->s_state = OP(COMPARE_SWAP);
+				ohdr->u.atomic_eth.swap_data = cpu_to_be64(
+					wqe->wr.wr.atomic.swap);
+				ohdr->u.atomic_eth.compare_data = cpu_to_be64(
+					wqe->wr.wr.atomic.compare_add);
+			} else {
+				qp->s_state = OP(FETCH_ADD);
+				ohdr->u.atomic_eth.swap_data = cpu_to_be64(
+					wqe->wr.wr.atomic.compare_add);
+				ohdr->u.atomic_eth.compare_data = 0;
+			}
+			ohdr->u.atomic_eth.vaddr[0] = cpu_to_be32(
+				wqe->wr.wr.atomic.remote_addr >> 32);
+			ohdr->u.atomic_eth.vaddr[1] = cpu_to_be32(
+				wqe->wr.wr.atomic.remote_addr);
+			ohdr->u.atomic_eth.rkey = cpu_to_be32(
+				wqe->wr.wr.atomic.rkey);
+			hwords += sizeof(struct ib_atomic_eth) / sizeof(u32);
 			ss = NULL;
 			len = 0;
+			if (++qp->s_cur == qp->s_size)
+				qp->s_cur = 0;
 			break;
 
 		default:
-			goto done;
+			goto bail;
 		}
 		qp->s_sge.sge = wqe->sg_list[0];
 		qp->s_sge.sg_list = wqe->sg_list + 1;
@@ -379,7 +444,7 @@
 			qp->s_psn = wqe->lpsn + 1;
 		else {
 			qp->s_psn++;
-			if ((int)(qp->s_psn - qp->s_next_psn) > 0)
+			if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0)
 				qp->s_next_psn = qp->s_psn;
 		}
 		/*
@@ -406,7 +471,7 @@
 		/* FALLTHROUGH */
 	case OP(SEND_MIDDLE):
 		bth2 = qp->s_psn++ & IPATH_PSN_MASK;
-		if ((int)(qp->s_psn - qp->s_next_psn) > 0)
+		if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0)
 			qp->s_next_psn = qp->s_psn;
 		ss = &qp->s_sge;
 		len = qp->s_len;
@@ -442,7 +507,7 @@
 		/* FALLTHROUGH */
 	case OP(RDMA_WRITE_MIDDLE):
 		bth2 = qp->s_psn++ & IPATH_PSN_MASK;
-		if ((int)(qp->s_psn - qp->s_next_psn) > 0)
+		if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0)
 			qp->s_next_psn = qp->s_psn;
 		ss = &qp->s_sge;
 		len = qp->s_len;
@@ -479,9 +544,9 @@
 			cpu_to_be32(wqe->wr.wr.rdma.rkey);
 		ohdr->u.rc.reth.length = cpu_to_be32(qp->s_len);
 		qp->s_state = OP(RDMA_READ_REQUEST);
-		hwords += sizeof(ohdr->u.rc.reth) / 4;
+		hwords += sizeof(ohdr->u.rc.reth) / sizeof(u32);
 		bth2 = qp->s_psn++ & IPATH_PSN_MASK;
-		if ((int)(qp->s_psn - qp->s_next_psn) > 0)
+		if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0)
 			qp->s_next_psn = qp->s_psn;
 		ss = NULL;
 		len = 0;
@@ -489,20 +554,6 @@
 		if (qp->s_cur == qp->s_size)
 			qp->s_cur = 0;
 		break;
-
-	case OP(RDMA_READ_REQUEST):
-	case OP(COMPARE_SWAP):
-	case OP(FETCH_ADD):
-		/*
-		 * We shouldn't start anything new until this request is
-		 * finished.  The ACK will handle rescheduling us.  XXX The
-		 * number of outstanding ones is negotiated at connection
-		 * setup time (see pg. 258,289)?  XXX Also, if we support
-		 * multiple outstanding requests, we need to check the WQE
-		 * IB_SEND_FENCE flag and not send a new request if a RDMA
-		 * read or atomic is pending.
-		 */
-		goto done;
 	}
 	if (ipath_cmp24(qp->s_psn, qp->s_last_psn + IPATH_PSN_CREDIT - 1) >= 0)
 		bth2 |= 1 << 31;	/* Request ACK. */
@@ -512,9 +563,10 @@
 	qp->s_cur_size = len;
 	*bth0p = bth0 | (qp->s_state << 24);
 	*bth2p = bth2;
+done:
 	return 1;
 
-done:
+bail:
 	return 0;
 }
 
@@ -524,7 +576,8 @@
  *
  * This is called from ipath_rc_rcv() and only uses the receive
  * side QP state.
- * Note that RDMA reads are handled in the send side QP state and tasklet.
+ * Note that RDMA reads and atomics are handled in the
+ * send side QP state and tasklet.
  */
 static void send_rc_ack(struct ipath_qp *qp)
 {
@@ -535,6 +588,10 @@
 	struct ipath_ib_header hdr;
 	struct ipath_other_headers *ohdr;
 
+	/* Don't send ACK or NAK if a RDMA read or atomic is pending. */
+	if (qp->r_head_ack_queue != qp->s_tail_ack_queue)
+		goto queue_ack;
+
 	/* Construct the header. */
 	ohdr = &hdr.u.oth;
 	lrh0 = IPATH_LRH_BTH;
@@ -548,19 +605,14 @@
 		lrh0 = IPATH_LRH_GRH;
 	}
 	/* read pkey_index w/o lock (its atomic) */
-	bth0 = ipath_get_pkey(dev->dd, qp->s_pkey_index);
+	bth0 = ipath_get_pkey(dev->dd, qp->s_pkey_index) |
+		OP(ACKNOWLEDGE) << 24;
 	if (qp->r_nak_state)
 		ohdr->u.aeth = cpu_to_be32((qp->r_msn & IPATH_MSN_MASK) |
 					    (qp->r_nak_state <<
 					     IPATH_AETH_CREDIT_SHIFT));
 	else
 		ohdr->u.aeth = ipath_compute_aeth(qp);
-	if (qp->r_ack_state >= OP(COMPARE_SWAP)) {
-		bth0 |= OP(ATOMIC_ACKNOWLEDGE) << 24;
-		ohdr->u.at.atomic_ack_eth = cpu_to_be64(qp->r_atomic_data);
-		hwords += sizeof(ohdr->u.at.atomic_ack_eth) / 4;
-	} else
-		bth0 |= OP(ACKNOWLEDGE) << 24;
 	lrh0 |= qp->remote_ah_attr.sl << 4;
 	hdr.lrh[0] = cpu_to_be16(lrh0);
 	hdr.lrh[1] = cpu_to_be16(qp->remote_ah_attr.dlid);
@@ -574,31 +626,31 @@
 	 * If we can send the ACK, clear the ACK state.
 	 */
 	if (ipath_verbs_send(dev->dd, hwords, (u32 *) &hdr, 0, NULL) == 0) {
-		qp->r_ack_state = OP(ACKNOWLEDGE);
 		dev->n_unicast_xmit++;
-	} else {
-		/*
-		 * We are out of PIO buffers at the moment.
-		 * Pass responsibility for sending the ACK to the
-		 * send tasklet so that when a PIO buffer becomes
-		 * available, the ACK is sent ahead of other outgoing
-		 * packets.
-		 */
-		dev->n_rc_qacks++;
-		spin_lock_irq(&qp->s_lock);
-		/* Don't coalesce if a RDMA read or atomic is pending. */
-		if (qp->s_ack_state == OP(ACKNOWLEDGE) ||
-		    qp->s_ack_state < OP(RDMA_READ_REQUEST)) {
-			qp->s_ack_state = qp->r_ack_state;
-			qp->s_nak_state = qp->r_nak_state;
-			qp->s_ack_psn = qp->r_ack_psn;
-			qp->r_ack_state = OP(ACKNOWLEDGE);
-		}
-		spin_unlock_irq(&qp->s_lock);
-
-		/* Call ipath_do_rc_send() in another thread. */
-		tasklet_hi_schedule(&qp->s_task);
+		goto done;
 	}
+
+	/*
+	 * We are out of PIO buffers at the moment.
+	 * Pass responsibility for sending the ACK to the
+	 * send tasklet so that when a PIO buffer becomes
+	 * available, the ACK is sent ahead of other outgoing
+	 * packets.
+	 */
+	dev->n_rc_qacks++;
+
+queue_ack:
+	spin_lock_irq(&qp->s_lock);
+	qp->s_flags |= IPATH_S_ACK_PENDING;
+	qp->s_nak_state = qp->r_nak_state;
+	qp->s_ack_psn = qp->r_ack_psn;
+	spin_unlock_irq(&qp->s_lock);
+
+	/* Call ipath_do_rc_send() in another thread. */
+	tasklet_hi_schedule(&qp->s_task);
+
+done:
+	return;
 }
 
 /**
@@ -727,7 +779,7 @@
 	if (wqe->wr.opcode == IB_WR_RDMA_READ)
 		dev->n_rc_resends++;
 	else
-		dev->n_rc_resends += (int)qp->s_psn - (int)psn;
+		dev->n_rc_resends += (qp->s_psn - psn) & IPATH_PSN_MASK;
 
 	reset_psn(qp, psn);
 	tasklet_hi_schedule(&qp->s_task);
@@ -775,10 +827,6 @@
 		list_del_init(&qp->timerwait);
 	spin_unlock(&dev->pending_lock);
 
-	/* Nothing is pending to ACK/NAK. */
-	if (unlikely(qp->s_last == qp->s_tail))
-		goto bail;
-
 	/*
 	 * Note that NAKs implicitly ACK outstanding SEND and RDMA write
 	 * requests and implicitly NAK RDMA read and atomic requests issued
@@ -806,7 +854,7 @@
 		 */
 		if ((wqe->wr.opcode == IB_WR_RDMA_READ &&
 		     (opcode != OP(RDMA_READ_RESPONSE_LAST) ||
-		       ipath_cmp24(ack_psn, wqe->lpsn) != 0)) ||
+		      ipath_cmp24(ack_psn, wqe->lpsn) != 0)) ||
 		    ((wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP ||
 		      wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) &&
 		     (opcode != OP(ATOMIC_ACKNOWLEDGE) ||
@@ -824,20 +872,33 @@
 			 */
 			goto bail;
 		}
-		if (wqe->wr.opcode == IB_WR_RDMA_READ ||
-		    wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP ||
-		    wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD)
-			tasklet_hi_schedule(&qp->s_task);
+		if (qp->s_num_rd_atomic &&
+		    (wqe->wr.opcode == IB_WR_RDMA_READ ||
+		     wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP ||
+		     wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD)) {
+			qp->s_num_rd_atomic--;
+			/* Restart sending task if fence is complete */
+			if ((qp->s_flags & IPATH_S_FENCE_PENDING) &&
+			    !qp->s_num_rd_atomic) {
+				qp->s_flags &= ~IPATH_S_FENCE_PENDING;
+				tasklet_hi_schedule(&qp->s_task);
+			} else if (qp->s_flags & IPATH_S_RDMAR_PENDING) {
+				qp->s_flags &= ~IPATH_S_RDMAR_PENDING;
+				tasklet_hi_schedule(&qp->s_task);
+			}
+		}
 		/* Post a send completion queue entry if requested. */
-		if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &qp->s_flags) ||
+		if (!(qp->s_flags & IPATH_S_SIGNAL_REQ_WR) ||
 		    (wqe->wr.send_flags & IB_SEND_SIGNALED)) {
 			wc.wr_id = wqe->wr.wr_id;
 			wc.status = IB_WC_SUCCESS;
 			wc.opcode = ib_ipath_wc_opcode[wqe->wr.opcode];
 			wc.vendor_err = 0;
 			wc.byte_len = wqe->length;
+			wc.imm_data = 0;
 			wc.qp = &qp->ibqp;
 			wc.src_qp = qp->remote_qpn;
+			wc.wc_flags = 0;
 			wc.pkey_index = 0;
 			wc.slid = qp->remote_ah_attr.dlid;
 			wc.sl = qp->remote_ah_attr.sl;
@@ -854,15 +915,19 @@
 		if (qp->s_last == qp->s_cur) {
 			if (++qp->s_cur >= qp->s_size)
 				qp->s_cur = 0;
+			qp->s_last = qp->s_cur;
+			if (qp->s_last == qp->s_tail)
+				break;
 			wqe = get_swqe_ptr(qp, qp->s_cur);
 			qp->s_state = OP(SEND_LAST);
 			qp->s_psn = wqe->psn;
+		} else {
+			if (++qp->s_last >= qp->s_size)
+				qp->s_last = 0;
+			if (qp->s_last == qp->s_tail)
+				break;
+			wqe = get_swqe_ptr(qp, qp->s_last);
 		}
-		if (++qp->s_last >= qp->s_size)
-			qp->s_last = 0;
-		wqe = get_swqe_ptr(qp, qp->s_last);
-		if (qp->s_last == qp->s_tail)
-			break;
 	}
 
 	switch (aeth >> 29) {
@@ -874,6 +939,18 @@
 			list_add_tail(&qp->timerwait,
 				      &dev->pending[dev->pending_index]);
 			spin_unlock(&dev->pending_lock);
+			/*
+			 * If we get a partial ACK for a resent operation,
+			 * we can stop resending the earlier packets and
+			 * continue with the next packet the receiver wants.
+			 */
+			if (ipath_cmp24(qp->s_psn, psn) <= 0) {
+				reset_psn(qp, psn + 1);
+				tasklet_hi_schedule(&qp->s_task);
+			}
+		} else if (ipath_cmp24(qp->s_psn, psn) <= 0) {
+			qp->s_state = OP(SEND_LAST);
+			qp->s_psn = psn + 1;
 		}
 		ipath_get_credit(qp, aeth);
 		qp->s_rnr_retry = qp->s_rnr_retry_cnt;
@@ -884,22 +961,23 @@
 
 	case 1:		/* RNR NAK */
 		dev->n_rnr_naks++;
+		if (qp->s_last == qp->s_tail)
+			goto bail;
 		if (qp->s_rnr_retry == 0) {
-			if (qp->s_last == qp->s_tail)
-				goto bail;
-
 			wc.status = IB_WC_RNR_RETRY_EXC_ERR;
 			goto class_b;
 		}
 		if (qp->s_rnr_retry_cnt < 7)
 			qp->s_rnr_retry--;
-		if (qp->s_last == qp->s_tail)
-			goto bail;
 
 		/* The last valid PSN is the previous PSN. */
 		update_last_psn(qp, psn - 1);
 
-		dev->n_rc_resends += (int)qp->s_psn - (int)psn;
+		if (wqe->wr.opcode == IB_WR_RDMA_READ)
+			dev->n_rc_resends++;
+		else
+			dev->n_rc_resends +=
+				(qp->s_psn - psn) & IPATH_PSN_MASK;
 
 		reset_psn(qp, psn);
 
@@ -910,26 +988,20 @@
 		goto bail;
 
 	case 3:		/* NAK */
-		/* The last valid PSN seen is the previous request's. */
-		if (qp->s_last != qp->s_tail)
-			update_last_psn(qp, wqe->psn - 1);
+		if (qp->s_last == qp->s_tail)
+			goto bail;
+		/* The last valid PSN is the previous PSN. */
+		update_last_psn(qp, psn - 1);
 		switch ((aeth >> IPATH_AETH_CREDIT_SHIFT) &
 			IPATH_AETH_CREDIT_MASK) {
 		case 0:	/* PSN sequence error */
 			dev->n_seq_naks++;
 			/*
-			 * Back up to the responder's expected PSN.  XXX
+			 * Back up to the responder's expected PSN.
 			 * Note that we might get a NAK in the middle of an
 			 * RDMA READ response which terminates the RDMA
 			 * READ.
 			 */
-			if (qp->s_last == qp->s_tail)
-				break;
-
-			if (ipath_cmp24(psn, wqe->psn) < 0)
-				break;
-
-			/* Retry the request. */
 			ipath_restart_rc(qp, psn, &wc);
 			break;
 
@@ -1003,6 +1075,7 @@
 				     u32 psn, u32 hdrsize, u32 pmtu,
 				     int header_in_data)
 {
+	struct ipath_swqe *wqe;
 	unsigned long flags;
 	struct ib_wc wc;
 	int diff;
@@ -1032,6 +1105,10 @@
 		goto ack_done;
 	}
 
+	if (unlikely(qp->s_last == qp->s_tail))
+		goto ack_done;
+	wqe = get_swqe_ptr(qp, qp->s_last);
+
 	switch (opcode) {
 	case OP(ACKNOWLEDGE):
 	case OP(ATOMIC_ACKNOWLEDGE):
@@ -1042,38 +1119,49 @@
 			aeth = be32_to_cpu(((__be32 *) data)[0]);
 			data += sizeof(__be32);
 		}
-		if (opcode == OP(ATOMIC_ACKNOWLEDGE))
-			*(u64 *) qp->s_sge.sge.vaddr = *(u64 *) data;
+		if (opcode == OP(ATOMIC_ACKNOWLEDGE)) {
+			u64 val;
+
+			if (!header_in_data) {
+				__be32 *p = ohdr->u.at.atomic_ack_eth;
+
+				val = ((u64) be32_to_cpu(p[0]) << 32) |
+					be32_to_cpu(p[1]);
+			} else
+				val = be64_to_cpu(((__be64 *) data)[0]);
+			*(u64 *) wqe->sg_list[0].vaddr = val;
+		}
 		if (!do_rc_ack(qp, aeth, psn, opcode) ||
 		    opcode != OP(RDMA_READ_RESPONSE_FIRST))
 			goto ack_done;
 		hdrsize += 4;
+		if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ))
+			goto ack_op_err;
 		/*
-		 * do_rc_ack() has already checked the PSN so skip
-		 * the sequence check.
+		 * If this is a response to a resent RDMA read, we
+		 * have to be careful to copy the data to the right
+		 * location.
 		 */
-		goto rdma_read;
+		qp->s_rdma_read_len = restart_sge(&qp->s_rdma_read_sge,
+						  wqe, psn, pmtu);
+		goto read_middle;
 
 	case OP(RDMA_READ_RESPONSE_MIDDLE):
 		/* no AETH, no ACK */
 		if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) {
 			dev->n_rdma_seq++;
-			if (qp->s_last != qp->s_tail)
-				ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
+			ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
 			goto ack_done;
 		}
-	rdma_read:
-		if (unlikely(qp->s_state != OP(RDMA_READ_REQUEST)))
-			goto ack_done;
+		if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ))
+			goto ack_op_err;
+	read_middle:
 		if (unlikely(tlen != (hdrsize + pmtu + 4)))
-			goto ack_done;
-		if (unlikely(pmtu >= qp->s_len))
-			goto ack_done;
+			goto ack_len_err;
+		if (unlikely(pmtu >= qp->s_rdma_read_len))
+			goto ack_len_err;
+
 		/* We got a response so update the timeout. */
-		if (unlikely(qp->s_last == qp->s_tail ||
-			     get_swqe_ptr(qp, qp->s_last)->wr.opcode !=
-			     IB_WR_RDMA_READ))
-			goto ack_done;
 		spin_lock(&dev->pending_lock);
 		if (qp->s_rnr_timeout == 0 && !list_empty(&qp->timerwait))
 			list_move_tail(&qp->timerwait,
@@ -1082,67 +1170,97 @@
 		/*
 		 * Update the RDMA receive state but do the copy w/o
 		 * holding the locks and blocking interrupts.
-		 * XXX Yet another place that affects relaxed RDMA order
-		 * since we don't want s_sge modified.
 		 */
-		qp->s_len -= pmtu;
+		qp->s_rdma_read_len -= pmtu;
 		update_last_psn(qp, psn);
 		spin_unlock_irqrestore(&qp->s_lock, flags);
-		ipath_copy_sge(&qp->s_sge, data, pmtu);
+		ipath_copy_sge(&qp->s_rdma_read_sge, data, pmtu);
 		goto bail;
 
+	case OP(RDMA_READ_RESPONSE_ONLY):
+		if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) {
+			dev->n_rdma_seq++;
+			ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
+			goto ack_done;
+		}
+		if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ))
+			goto ack_op_err;
+		/* Get the number of bytes the message was padded by. */
+		pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
+		/*
+		 * Check that the data size is >= 0 && <= pmtu.
+		 * Remember to account for the AETH header (4) and
+		 * ICRC (4).
+		 */
+		if (unlikely(tlen < (hdrsize + pad + 8)))
+			goto ack_len_err;
+		/*
+		 * If this is a response to a resent RDMA read, we
+		 * have to be careful to copy the data to the right
+		 * location.
+		 */
+		qp->s_rdma_read_len = restart_sge(&qp->s_rdma_read_sge,
+						  wqe, psn, pmtu);
+		goto read_last;
+
 	case OP(RDMA_READ_RESPONSE_LAST):
 		/* ACKs READ req. */
 		if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) {
 			dev->n_rdma_seq++;
-			if (qp->s_last != qp->s_tail)
-				ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
+			ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
 			goto ack_done;
 		}
-		/* FALLTHROUGH */
-	case OP(RDMA_READ_RESPONSE_ONLY):
-		if (unlikely(qp->s_state != OP(RDMA_READ_REQUEST)))
-			goto ack_done;
-		/*
-		 * Get the number of bytes the message was padded by.
-		 */
+		if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ))
+			goto ack_op_err;
+		/* Get the number of bytes the message was padded by. */
 		pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
 		/*
 		 * Check that the data size is >= 1 && <= pmtu.
 		 * Remember to account for the AETH header (4) and
 		 * ICRC (4).
 		 */
-		if (unlikely(tlen <= (hdrsize + pad + 8))) {
-			/* XXX Need to generate an error CQ entry. */
-			goto ack_done;
-		}
+		if (unlikely(tlen <= (hdrsize + pad + 8)))
+			goto ack_len_err;
+	read_last:
 		tlen -= hdrsize + pad + 8;
-		if (unlikely(tlen != qp->s_len)) {
-			/* XXX Need to generate an error CQ entry. */
-			goto ack_done;
-		}
+		if (unlikely(tlen != qp->s_rdma_read_len))
+			goto ack_len_err;
 		if (!header_in_data)
 			aeth = be32_to_cpu(ohdr->u.aeth);
 		else {
 			aeth = be32_to_cpu(((__be32 *) data)[0]);
 			data += sizeof(__be32);
 		}
-		ipath_copy_sge(&qp->s_sge, data, tlen);
-		if (do_rc_ack(qp, aeth, psn, OP(RDMA_READ_RESPONSE_LAST))) {
-			/*
-			 * Change the state so we contimue
-			 * processing new requests and wake up the
-			 * tasklet if there are posted sends.
-			 */
-			qp->s_state = OP(SEND_LAST);
-			if (qp->s_tail != qp->s_head)
-				tasklet_hi_schedule(&qp->s_task);
-		}
+		ipath_copy_sge(&qp->s_rdma_read_sge, data, tlen);
+		(void) do_rc_ack(qp, aeth, psn, OP(RDMA_READ_RESPONSE_LAST));
 		goto ack_done;
 	}
 
 ack_done:
 	spin_unlock_irqrestore(&qp->s_lock, flags);
+	goto bail;
+
+ack_op_err:
+	wc.status = IB_WC_LOC_QP_OP_ERR;
+	goto ack_err;
+
+ack_len_err:
+	wc.status = IB_WC_LOC_LEN_ERR;
+ack_err:
+	wc.wr_id = wqe->wr.wr_id;
+	wc.opcode = ib_ipath_wc_opcode[wqe->wr.opcode];
+	wc.vendor_err = 0;
+	wc.byte_len = 0;
+	wc.imm_data = 0;
+	wc.qp = &qp->ibqp;
+	wc.src_qp = qp->remote_qpn;
+	wc.wc_flags = 0;
+	wc.pkey_index = 0;
+	wc.slid = qp->remote_ah_attr.dlid;
+	wc.sl = qp->remote_ah_attr.sl;
+	wc.dlid_path_bits = 0;
+	wc.port_num = 0;
+	ipath_sqerror_qp(qp, &wc);
 bail:
 	return;
 }
@@ -1162,7 +1280,7 @@
  * incoming RC packet for the given QP.
  * Called at interrupt level.
  * Return 1 if no more processing is needed; otherwise return 0 to
- * schedule a response to be sent and the s_lock unlocked.
+ * schedule a response to be sent.
  */
 static inline int ipath_rc_rcv_error(struct ipath_ibdev *dev,
 				     struct ipath_other_headers *ohdr,
@@ -1173,25 +1291,23 @@
 				     int diff,
 				     int header_in_data)
 {
-	struct ib_reth *reth;
+	struct ipath_ack_entry *e;
+	u8 i, prev;
+	int old_req;
 
 	if (diff > 0) {
 		/*
 		 * Packet sequence error.
 		 * A NAK will ACK earlier sends and RDMA writes.
-		 * Don't queue the NAK if a RDMA read, atomic, or
-		 * NAK is pending though.
+		 * Don't queue the NAK if we already sent one.
 		 */
-		if (qp->s_ack_state != OP(ACKNOWLEDGE) ||
-		    qp->r_nak_state != 0)
-			goto done;
-		if (qp->r_ack_state < OP(COMPARE_SWAP)) {
-			qp->r_ack_state = OP(SEND_ONLY);
+		if (!qp->r_nak_state) {
 			qp->r_nak_state = IB_NAK_PSN_ERROR;
 			/* Use the expected PSN. */
 			qp->r_ack_psn = qp->r_psn;
+			goto send_ack;
 		}
-		goto send_ack;
+		goto done;
 	}
 
 	/*
@@ -1204,8 +1320,46 @@
 	 * can coalesce an outstanding duplicate ACK.  We have to
 	 * send the earliest so that RDMA reads can be restarted at
 	 * the requester's expected PSN.
+	 *
+	 * First, find where this duplicate PSN falls within the
+	 * ACKs previously sent.
 	 */
-	if (opcode == OP(RDMA_READ_REQUEST)) {
+	psn &= IPATH_PSN_MASK;
+	e = NULL;
+	old_req = 1;
+	spin_lock_irq(&qp->s_lock);
+	for (i = qp->r_head_ack_queue; ; i = prev) {
+		if (i == qp->s_tail_ack_queue)
+			old_req = 0;
+		if (i)
+			prev = i - 1;
+		else
+			prev = IPATH_MAX_RDMA_ATOMIC;
+		if (prev == qp->r_head_ack_queue) {
+			e = NULL;
+			break;
+		}
+		e = &qp->s_ack_queue[prev];
+		if (!e->opcode) {
+			e = NULL;
+			break;
+		}
+		if (ipath_cmp24(psn, e->psn) >= 0)
+			break;
+	}
+	switch (opcode) {
+	case OP(RDMA_READ_REQUEST): {
+		struct ib_reth *reth;
+		u32 offset;
+		u32 len;
+
+		/*
+		 * If we didn't find the RDMA read request in the ack queue,
+		 * or the send tasklet is already backed up to send an
+		 * earlier entry, we can ignore this request.
+		 */
+		if (!e || e->opcode != OP(RDMA_READ_REQUEST) || old_req)
+			goto unlock_done;
 		/* RETH comes after BTH */
 		if (!header_in_data)
 			reth = &ohdr->u.rc.reth;
@@ -1214,88 +1368,87 @@
 			data += sizeof(*reth);
 		}
 		/*
-		 * If we receive a duplicate RDMA request, it means the
-		 * requester saw a sequence error and needs to restart
-		 * from an earlier point.  We can abort the current
-		 * RDMA read send in that case.
+		 * Address range must be a subset of the original
+		 * request and start on pmtu boundaries.
+		 * We reuse the old ack_queue slot since the requester
+		 * should not back up and request an earlier PSN for the
+		 * same request.
 		 */
-		spin_lock_irq(&qp->s_lock);
-		if (qp->s_ack_state != OP(ACKNOWLEDGE) &&
-		    (qp->s_hdrwords || ipath_cmp24(psn, qp->s_ack_psn) >= 0)) {
-			/*
-			 * We are already sending earlier requested data.
-			 * Don't abort it to send later out of sequence data.
-			 */
-			spin_unlock_irq(&qp->s_lock);
-			goto done;
-		}
-		qp->s_rdma_len = be32_to_cpu(reth->length);
-		if (qp->s_rdma_len != 0) {
+		offset = ((psn - e->psn) & IPATH_PSN_MASK) *
+			ib_mtu_enum_to_int(qp->path_mtu);
+		len = be32_to_cpu(reth->length);
+		if (unlikely(offset + len > e->rdma_sge.sge.sge_length))
+			goto unlock_done;
+		if (len != 0) {
 			u32 rkey = be32_to_cpu(reth->rkey);
 			u64 vaddr = be64_to_cpu(reth->vaddr);
 			int ok;
 
-			/*
-			 * Address range must be a subset of the original
-			 * request and start on pmtu boundaries.
-			 */
-			ok = ipath_rkey_ok(qp, &qp->s_rdma_sge,
-					   qp->s_rdma_len, vaddr, rkey,
+			ok = ipath_rkey_ok(qp, &e->rdma_sge,
+					   len, vaddr, rkey,
 					   IB_ACCESS_REMOTE_READ);
-			if (unlikely(!ok)) {
-				spin_unlock_irq(&qp->s_lock);
-				goto done;
-			}
+			if (unlikely(!ok))
+				goto unlock_done;
 		} else {
-			qp->s_rdma_sge.sg_list = NULL;
-			qp->s_rdma_sge.num_sge = 0;
-			qp->s_rdma_sge.sge.mr = NULL;
-			qp->s_rdma_sge.sge.vaddr = NULL;
-			qp->s_rdma_sge.sge.length = 0;
-			qp->s_rdma_sge.sge.sge_length = 0;
+			e->rdma_sge.sg_list = NULL;
+			e->rdma_sge.num_sge = 0;
+			e->rdma_sge.sge.mr = NULL;
+			e->rdma_sge.sge.vaddr = NULL;
+			e->rdma_sge.sge.length = 0;
+			e->rdma_sge.sge.sge_length = 0;
 		}
-		qp->s_ack_state = opcode;
-		qp->s_ack_psn = psn;
-		spin_unlock_irq(&qp->s_lock);
-		tasklet_hi_schedule(&qp->s_task);
-		goto send_ack;
-	}
-
-	/*
-	 * A pending RDMA read will ACK anything before it so
-	 * ignore earlier duplicate requests.
-	 */
-	if (qp->s_ack_state != OP(ACKNOWLEDGE))
-		goto done;
-
-	/*
-	 * If an ACK is pending, don't replace the pending ACK
-	 * with an earlier one since the later one will ACK the earlier.
-	 * Also, if we already have a pending atomic, send it.
-	 */
-	if (qp->r_ack_state != OP(ACKNOWLEDGE) &&
-	    (ipath_cmp24(psn, qp->r_ack_psn) <= 0 ||
-	     qp->r_ack_state >= OP(COMPARE_SWAP)))
-		goto send_ack;
-	switch (opcode) {
-	case OP(COMPARE_SWAP):
-	case OP(FETCH_ADD):
-		/*
-		 * Check for the PSN of the last atomic operation
-		 * performed and resend the result if found.
-		 */
-		if ((psn & IPATH_PSN_MASK) != qp->r_atomic_psn)
-			goto done;
+		e->psn = psn;
+		qp->s_ack_state = OP(ACKNOWLEDGE);
+		qp->s_tail_ack_queue = prev;
 		break;
 	}
-	qp->r_ack_state = opcode;
-	qp->r_nak_state = 0;
-	qp->r_ack_psn = psn;
-send_ack:
-	return 0;
 
+	case OP(COMPARE_SWAP):
+	case OP(FETCH_ADD): {
+		/*
+		 * If we didn't find the atomic request in the ack queue
+		 * or the send tasklet is already backed up to send an
+		 * earlier entry, we can ignore this request.
+		 */
+		if (!e || e->opcode != (u8) opcode || old_req)
+			goto unlock_done;
+		qp->s_ack_state = OP(ACKNOWLEDGE);
+		qp->s_tail_ack_queue = prev;
+		break;
+	}
+
+	default:
+		if (old_req)
+			goto unlock_done;
+		/*
+		 * Resend the most recent ACK if this request is
+		 * after all the previous RDMA reads and atomics.
+		 */
+		if (i == qp->r_head_ack_queue) {
+			spin_unlock_irq(&qp->s_lock);
+			qp->r_nak_state = 0;
+			qp->r_ack_psn = qp->r_psn - 1;
+			goto send_ack;
+		}
+		/*
+		 * Resend the RDMA read or atomic op which
+		 * ACKs this duplicate request.
+		 */
+		qp->s_ack_state = OP(ACKNOWLEDGE);
+		qp->s_tail_ack_queue = i;
+		break;
+	}
+	qp->r_nak_state = 0;
+	spin_unlock_irq(&qp->s_lock);
+	tasklet_hi_schedule(&qp->s_task);
+
+unlock_done:
+	spin_unlock_irq(&qp->s_lock);
 done:
 	return 1;
+
+send_ack:
+	return 0;
 }
 
 static void ipath_rc_error(struct ipath_qp *qp, enum ib_wc_status err)
@@ -1391,15 +1544,7 @@
 		    opcode == OP(SEND_LAST_WITH_IMMEDIATE))
 			break;
 	nack_inv:
-		/*
-		 * A NAK will ACK earlier sends and RDMA writes.
-		 * Don't queue the NAK if a RDMA read, atomic, or NAK
-		 * is pending though.
-		 */
-		if (qp->r_ack_state >= OP(COMPARE_SWAP))
-			goto send_ack;
 		ipath_rc_error(qp, IB_WC_REM_INV_REQ_ERR);
-		qp->r_ack_state = OP(SEND_ONLY);
 		qp->r_nak_state = IB_NAK_INVALID_REQUEST;
 		qp->r_ack_psn = qp->r_psn;
 		goto send_ack;
@@ -1441,9 +1586,8 @@
 			 * Don't queue the NAK if a RDMA read or atomic
 			 * is pending though.
 			 */
-			if (qp->r_ack_state >= OP(COMPARE_SWAP))
-				goto send_ack;
-			qp->r_ack_state = OP(SEND_ONLY);
+			if (qp->r_nak_state)
+				goto done;
 			qp->r_nak_state = IB_RNR_NAK | qp->r_min_rnr_timer;
 			qp->r_ack_psn = qp->r_psn;
 			goto send_ack;
@@ -1567,7 +1711,19 @@
 			goto rnr_nak;
 		goto send_last_imm;
 
-	case OP(RDMA_READ_REQUEST):
+	case OP(RDMA_READ_REQUEST): {
+		struct ipath_ack_entry *e;
+		u32 len;
+		u8 next;
+
+		if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_READ)))
+			goto nack_acc;
+		next = qp->r_head_ack_queue + 1;
+		if (next > IPATH_MAX_RDMA_ATOMIC)
+			next = 0;
+		if (unlikely(next == qp->s_tail_ack_queue))
+			goto nack_inv;
+		e = &qp->s_ack_queue[qp->r_head_ack_queue];
 		/* RETH comes after BTH */
 		if (!header_in_data)
 			reth = &ohdr->u.rc.reth;
@@ -1575,72 +1731,75 @@
 			reth = (struct ib_reth *)data;
 			data += sizeof(*reth);
 		}
-		if (unlikely(!(qp->qp_access_flags &
-			       IB_ACCESS_REMOTE_READ)))
-			goto nack_acc;
-		spin_lock_irq(&qp->s_lock);
-		qp->s_rdma_len = be32_to_cpu(reth->length);
-		if (qp->s_rdma_len != 0) {
+		len = be32_to_cpu(reth->length);
+		if (len) {
 			u32 rkey = be32_to_cpu(reth->rkey);
 			u64 vaddr = be64_to_cpu(reth->vaddr);
 			int ok;
 
 			/* Check rkey & NAK */
-			ok = ipath_rkey_ok(qp, &qp->s_rdma_sge,
-					   qp->s_rdma_len, vaddr, rkey,
-					   IB_ACCESS_REMOTE_READ);
-			if (unlikely(!ok)) {
-				spin_unlock_irq(&qp->s_lock);
+			ok = ipath_rkey_ok(qp, &e->rdma_sge, len, vaddr,
+					   rkey, IB_ACCESS_REMOTE_READ);
+			if (unlikely(!ok))
 				goto nack_acc;
-			}
 			/*
 			 * Update the next expected PSN.  We add 1 later
 			 * below, so only add the remainder here.
 			 */
-			if (qp->s_rdma_len > pmtu)
-				qp->r_psn += (qp->s_rdma_len - 1) / pmtu;
+			if (len > pmtu)
+				qp->r_psn += (len - 1) / pmtu;
 		} else {
-			qp->s_rdma_sge.sg_list = NULL;
-			qp->s_rdma_sge.num_sge = 0;
-			qp->s_rdma_sge.sge.mr = NULL;
-			qp->s_rdma_sge.sge.vaddr = NULL;
-			qp->s_rdma_sge.sge.length = 0;
-			qp->s_rdma_sge.sge.sge_length = 0;
+			e->rdma_sge.sg_list = NULL;
+			e->rdma_sge.num_sge = 0;
+			e->rdma_sge.sge.mr = NULL;
+			e->rdma_sge.sge.vaddr = NULL;
+			e->rdma_sge.sge.length = 0;
+			e->rdma_sge.sge.sge_length = 0;
 		}
+		e->opcode = opcode;
+		e->psn = psn;
 		/*
 		 * We need to increment the MSN here instead of when we
 		 * finish sending the result since a duplicate request would
 		 * increment it more than once.
 		 */
 		qp->r_msn++;
-
-		qp->s_ack_state = opcode;
-		qp->s_ack_psn = psn;
-		spin_unlock_irq(&qp->s_lock);
-
 		qp->r_psn++;
 		qp->r_state = opcode;
 		qp->r_nak_state = 0;
+		barrier();
+		qp->r_head_ack_queue = next;
 
 		/* Call ipath_do_rc_send() in another thread. */
 		tasklet_hi_schedule(&qp->s_task);
 
 		goto done;
+	}
 
 	case OP(COMPARE_SWAP):
 	case OP(FETCH_ADD): {
 		struct ib_atomic_eth *ateth;
+		struct ipath_ack_entry *e;
 		u64 vaddr;
+		atomic64_t *maddr;
 		u64 sdata;
 		u32 rkey;
+		u8 next;
 
+		if (unlikely(!(qp->qp_access_flags &
+			       IB_ACCESS_REMOTE_ATOMIC)))
+			goto nack_acc;
+		next = qp->r_head_ack_queue + 1;
+		if (next > IPATH_MAX_RDMA_ATOMIC)
+			next = 0;
+		if (unlikely(next == qp->s_tail_ack_queue))
+			goto nack_inv;
 		if (!header_in_data)
 			ateth = &ohdr->u.atomic_eth;
-		else {
+		else
 			ateth = (struct ib_atomic_eth *)data;
-			data += sizeof(*ateth);
-		}
-		vaddr = be64_to_cpu(ateth->vaddr);
+		vaddr = ((u64) be32_to_cpu(ateth->vaddr[0]) << 32) |
+			be32_to_cpu(ateth->vaddr[1]);
 		if (unlikely(vaddr & (sizeof(u64) - 1)))
 			goto nack_inv;
 		rkey = be32_to_cpu(ateth->rkey);
@@ -1649,63 +1808,50 @@
 					    sizeof(u64), vaddr, rkey,
 					    IB_ACCESS_REMOTE_ATOMIC)))
 			goto nack_acc;
-		if (unlikely(!(qp->qp_access_flags &
-			       IB_ACCESS_REMOTE_ATOMIC)))
-			goto nack_acc;
 		/* Perform atomic OP and save result. */
+		maddr = (atomic64_t *) qp->r_sge.sge.vaddr;
 		sdata = be64_to_cpu(ateth->swap_data);
-		spin_lock_irq(&dev->pending_lock);
-		qp->r_atomic_data = *(u64 *) qp->r_sge.sge.vaddr;
-		if (opcode == OP(FETCH_ADD))
-			*(u64 *) qp->r_sge.sge.vaddr =
-				qp->r_atomic_data + sdata;
-		else if (qp->r_atomic_data ==
-			 be64_to_cpu(ateth->compare_data))
-			*(u64 *) qp->r_sge.sge.vaddr = sdata;
-		spin_unlock_irq(&dev->pending_lock);
+		e = &qp->s_ack_queue[qp->r_head_ack_queue];
+		e->atomic_data = (opcode == OP(FETCH_ADD)) ?
+			(u64) atomic64_add_return(sdata, maddr) - sdata :
+			(u64) cmpxchg((u64 *) qp->r_sge.sge.vaddr,
+				      be64_to_cpu(ateth->compare_data),
+				      sdata);
+		e->opcode = opcode;
+		e->psn = psn & IPATH_PSN_MASK;
 		qp->r_msn++;
-		qp->r_atomic_psn = psn & IPATH_PSN_MASK;
-		psn |= 1 << 31;
-		break;
+		qp->r_psn++;
+		qp->r_state = opcode;
+		qp->r_nak_state = 0;
+		barrier();
+		qp->r_head_ack_queue = next;
+
+		/* Call ipath_do_rc_send() in another thread. */
+		tasklet_hi_schedule(&qp->s_task);
+
+		goto done;
 	}
 
 	default:
-		/* Drop packet for unknown opcodes. */
-		goto done;
+		/* NAK unknown opcodes. */
+		goto nack_inv;
 	}
 	qp->r_psn++;
 	qp->r_state = opcode;
+	qp->r_ack_psn = psn;
 	qp->r_nak_state = 0;
 	/* Send an ACK if requested or required. */
-	if (psn & (1 << 31)) {
-		/*
-		 * Coalesce ACKs unless there is a RDMA READ or
-		 * ATOMIC pending.
-		 */
-		if (qp->r_ack_state < OP(COMPARE_SWAP)) {
-			qp->r_ack_state = opcode;
-			qp->r_ack_psn = psn;
-		}
+	if (psn & (1 << 31))
 		goto send_ack;
-	}
 	goto done;
 
 nack_acc:
-	/*
-	 * A NAK will ACK earlier sends and RDMA writes.
-	 * Don't queue the NAK if a RDMA read, atomic, or NAK
-	 * is pending though.
-	 */
-	if (qp->r_ack_state < OP(COMPARE_SWAP)) {
-		ipath_rc_error(qp, IB_WC_REM_ACCESS_ERR);
-		qp->r_ack_state = OP(RDMA_WRITE_ONLY);
-		qp->r_nak_state = IB_NAK_REMOTE_ACCESS_ERROR;
-		qp->r_ack_psn = qp->r_psn;
-	}
+	ipath_rc_error(qp, IB_WC_REM_ACCESS_ERR);
+	qp->r_nak_state = IB_NAK_REMOTE_ACCESS_ERROR;
+	qp->r_ack_psn = qp->r_psn;
+
 send_ack:
-	/* Send ACK right away unless the send tasklet has a pending ACK. */
-	if (qp->s_ack_state == OP(ACKNOWLEDGE))
-		send_rc_ack(qp);
+	send_rc_ack(qp);
 
 done:
 	return;
diff --git a/drivers/infiniband/hw/ipath/ipath_registers.h b/drivers/infiniband/hw/ipath/ipath_registers.h
index dffc760..c182bcd 100644
--- a/drivers/infiniband/hw/ipath/ipath_registers.h
+++ b/drivers/infiniband/hw/ipath/ipath_registers.h
@@ -126,9 +126,18 @@
 #define INFINIPATH_E_RESET           0x0004000000000000ULL
 #define INFINIPATH_E_HARDWARE        0x0008000000000000ULL
 
+/*
+ * this is used to print "common" packet errors only when the
+ * __IPATH_ERRPKTDBG bit is set in ipath_debug.
+ */
+#define INFINIPATH_E_PKTERRS ( INFINIPATH_E_SPKTLEN \
+		| INFINIPATH_E_SDROPPEDDATAPKT | INFINIPATH_E_RVCRC \
+		| INFINIPATH_E_RICRC | INFINIPATH_E_RSHORTPKTLEN \
+		| INFINIPATH_E_REBP )
+
 /* kr_hwerrclear, kr_hwerrmask, kr_hwerrstatus, bits */
 /* TXEMEMPARITYERR bit 0: PIObuf, 1: PIOpbc, 2: launchfifo
- * RXEMEMPARITYERR bit 0: rcvbuf, 1: lookupq, 2: eagerTID, 3: expTID
+ * RXEMEMPARITYERR bit 0: rcvbuf, 1: lookupq, 2:  expTID, 3: eagerTID
  * 		bit 4: flag buffer, 5: datainfo, 6: header info */
 #define INFINIPATH_HWE_TXEMEMPARITYERR_MASK 0xFULL
 #define INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT 40
@@ -143,8 +152,8 @@
 /* rxe mem parity errors (shift by INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) */
 #define INFINIPATH_HWE_RXEMEMPARITYERR_RCVBUF   0x01ULL
 #define INFINIPATH_HWE_RXEMEMPARITYERR_LOOKUPQ  0x02ULL
-#define INFINIPATH_HWE_RXEMEMPARITYERR_EAGERTID 0x04ULL
-#define INFINIPATH_HWE_RXEMEMPARITYERR_EXPTID   0x08ULL
+#define INFINIPATH_HWE_RXEMEMPARITYERR_EXPTID   0x04ULL
+#define INFINIPATH_HWE_RXEMEMPARITYERR_EAGERTID 0x08ULL
 #define INFINIPATH_HWE_RXEMEMPARITYERR_FLAGBUF  0x10ULL
 #define INFINIPATH_HWE_RXEMEMPARITYERR_DATAINFO 0x20ULL
 #define INFINIPATH_HWE_RXEMEMPARITYERR_HDRINFO  0x40ULL
@@ -299,13 +308,6 @@
 #define INFINIPATH_XGXS_RX_POL_SHIFT 19
 #define INFINIPATH_XGXS_RX_POL_MASK 0xfULL
 
-#define INFINIPATH_RT_ADDR_MASK 0xFFFFFFFFFFULL	/* 40 bits valid */
-
-/* TID entries (memory), HT-only */
-#define INFINIPATH_RT_VALID 0x8000000000000000ULL
-#define INFINIPATH_RT_ADDR_SHIFT 0
-#define INFINIPATH_RT_BUFSIZE_MASK 0x3FFF
-#define INFINIPATH_RT_BUFSIZE_SHIFT 48
 
 /*
  * IPATH_PIO_MAXIBHDR is the max IB header size allowed for in our
diff --git a/drivers/infiniband/hw/ipath/ipath_ruc.c b/drivers/infiniband/hw/ipath/ipath_ruc.c
index e86cb17..d9c2a9b 100644
--- a/drivers/infiniband/hw/ipath/ipath_ruc.c
+++ b/drivers/infiniband/hw/ipath/ipath_ruc.c
@@ -202,6 +202,7 @@
 	wq->tail = tail;
 
 	ret = 1;
+	qp->r_wrid_valid = 1;
 	if (handler) {
 		u32 n;
 
@@ -229,7 +230,6 @@
 		}
 	}
 	spin_unlock_irqrestore(&rq->lock, flags);
-	qp->r_wrid_valid = 1;
 
 bail:
 	return ret;
@@ -255,6 +255,7 @@
 	unsigned long flags;
 	struct ib_wc wc;
 	u64 sdata;
+	atomic64_t *maddr;
 
 	qp = ipath_lookup_qpn(&dev->qp_table, sqp->remote_qpn);
 	if (!qp) {
@@ -265,7 +266,8 @@
 again:
 	spin_lock_irqsave(&sqp->s_lock, flags);
 
-	if (!(ib_ipath_state_ops[sqp->state] & IPATH_PROCESS_SEND_OK)) {
+	if (!(ib_ipath_state_ops[sqp->state] & IPATH_PROCESS_SEND_OK) ||
+	    qp->s_rnr_timeout) {
 		spin_unlock_irqrestore(&sqp->s_lock, flags);
 		goto done;
 	}
@@ -310,7 +312,7 @@
 				sqp->s_rnr_retry--;
 			dev->n_rnr_naks++;
 			sqp->s_rnr_timeout =
-				ib_ipath_rnr_table[sqp->r_min_rnr_timer];
+				ib_ipath_rnr_table[qp->r_min_rnr_timer];
 			ipath_insert_rnr_queue(sqp);
 			goto done;
 		}
@@ -343,20 +345,22 @@
 			wc.sl = sqp->remote_ah_attr.sl;
 			wc.dlid_path_bits = 0;
 			wc.port_num = 0;
+			spin_lock_irqsave(&sqp->s_lock, flags);
 			ipath_sqerror_qp(sqp, &wc);
+			spin_unlock_irqrestore(&sqp->s_lock, flags);
 			goto done;
 		}
 		break;
 
 	case IB_WR_RDMA_READ:
+		if (unlikely(!(qp->qp_access_flags &
+			       IB_ACCESS_REMOTE_READ)))
+			goto acc_err;
 		if (unlikely(!ipath_rkey_ok(qp, &sqp->s_sge, wqe->length,
 					    wqe->wr.wr.rdma.remote_addr,
 					    wqe->wr.wr.rdma.rkey,
 					    IB_ACCESS_REMOTE_READ)))
 			goto acc_err;
-		if (unlikely(!(qp->qp_access_flags &
-			       IB_ACCESS_REMOTE_READ)))
-			goto acc_err;
 		qp->r_sge.sge = wqe->sg_list[0];
 		qp->r_sge.sg_list = wqe->sg_list + 1;
 		qp->r_sge.num_sge = wqe->wr.num_sge;
@@ -364,22 +368,22 @@
 
 	case IB_WR_ATOMIC_CMP_AND_SWP:
 	case IB_WR_ATOMIC_FETCH_AND_ADD:
+		if (unlikely(!(qp->qp_access_flags &
+			       IB_ACCESS_REMOTE_ATOMIC)))
+			goto acc_err;
 		if (unlikely(!ipath_rkey_ok(qp, &qp->r_sge, sizeof(u64),
-					    wqe->wr.wr.rdma.remote_addr,
-					    wqe->wr.wr.rdma.rkey,
+					    wqe->wr.wr.atomic.remote_addr,
+					    wqe->wr.wr.atomic.rkey,
 					    IB_ACCESS_REMOTE_ATOMIC)))
 			goto acc_err;
 		/* Perform atomic OP and save result. */
-		sdata = wqe->wr.wr.atomic.swap;
-		spin_lock_irqsave(&dev->pending_lock, flags);
-		qp->r_atomic_data = *(u64 *) qp->r_sge.sge.vaddr;
-		if (wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD)
-			*(u64 *) qp->r_sge.sge.vaddr =
-				qp->r_atomic_data + sdata;
-		else if (qp->r_atomic_data == wqe->wr.wr.atomic.compare_add)
-			*(u64 *) qp->r_sge.sge.vaddr = sdata;
-		spin_unlock_irqrestore(&dev->pending_lock, flags);
-		*(u64 *) sqp->s_sge.sge.vaddr = qp->r_atomic_data;
+		maddr = (atomic64_t *) qp->r_sge.sge.vaddr;
+		sdata = wqe->wr.wr.atomic.compare_add;
+		*(u64 *) sqp->s_sge.sge.vaddr =
+			(wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) ?
+			(u64) atomic64_add_return(sdata, maddr) - sdata :
+			(u64) cmpxchg((u64 *) qp->r_sge.sge.vaddr,
+				      sdata, wqe->wr.wr.atomic.swap);
 		goto send_comp;
 
 	default:
@@ -440,7 +444,7 @@
 send_comp:
 	sqp->s_rnr_retry = sqp->s_rnr_retry_cnt;
 
-	if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &sqp->s_flags) ||
+	if (!(sqp->s_flags & IPATH_S_SIGNAL_REQ_WR) ||
 	    (wqe->wr.send_flags & IB_SEND_SIGNALED)) {
 		wc.wr_id = wqe->wr.wr_id;
 		wc.status = IB_WC_SUCCESS;
@@ -502,7 +506,7 @@
 	 * We clear the tasklet flag now since we are committing to return
 	 * from the tasklet function.
 	 */
-	clear_bit(IPATH_S_BUSY, &qp->s_flags);
+	clear_bit(IPATH_S_BUSY, &qp->s_busy);
 	tasklet_unlock(&qp->s_task);
 	want_buffer(dev->dd);
 	dev->n_piowait++;
@@ -541,6 +545,9 @@
 		    wr->sg_list[0].addr & (sizeof(u64) - 1))) {
 		ret = -EINVAL;
 		goto bail;
+	} else if (wr->opcode >= IB_WR_RDMA_READ && !qp->s_max_rd_atomic) {
+		ret = -EINVAL;
+		goto bail;
 	}
 	/* IB spec says that num_sge == 0 is OK. */
 	if (wr->num_sge > qp->s_max_sge) {
@@ -647,7 +654,7 @@
 	u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu);
 	struct ipath_other_headers *ohdr;
 
-	if (test_and_set_bit(IPATH_S_BUSY, &qp->s_flags))
+	if (test_and_set_bit(IPATH_S_BUSY, &qp->s_busy))
 		goto bail;
 
 	if (unlikely(qp->remote_ah_attr.dlid == dev->dd->ipath_lid)) {
@@ -683,19 +690,15 @@
 	 */
 	spin_lock_irqsave(&qp->s_lock, flags);
 
-	/* Sending responses has higher priority over sending requests. */
-	if (qp->s_ack_state != IB_OPCODE_RC_ACKNOWLEDGE &&
-	    (bth0 = ipath_make_rc_ack(qp, ohdr, pmtu)) != 0)
-		bth2 = qp->s_ack_psn++ & IPATH_PSN_MASK;
-	else if (!((qp->ibqp.qp_type == IB_QPT_RC) ?
-		   ipath_make_rc_req(qp, ohdr, pmtu, &bth0, &bth2) :
-		   ipath_make_uc_req(qp, ohdr, pmtu, &bth0, &bth2))) {
+	if (!((qp->ibqp.qp_type == IB_QPT_RC) ?
+	       ipath_make_rc_req(qp, ohdr, pmtu, &bth0, &bth2) :
+	       ipath_make_uc_req(qp, ohdr, pmtu, &bth0, &bth2))) {
 		/*
 		 * Clear the busy bit before unlocking to avoid races with
 		 * adding new work queue items and then failing to process
 		 * them.
 		 */
-		clear_bit(IPATH_S_BUSY, &qp->s_flags);
+		clear_bit(IPATH_S_BUSY, &qp->s_busy);
 		spin_unlock_irqrestore(&qp->s_lock, flags);
 		goto bail;
 	}
@@ -728,7 +731,7 @@
 	goto again;
 
 clear:
-	clear_bit(IPATH_S_BUSY, &qp->s_flags);
+	clear_bit(IPATH_S_BUSY, &qp->s_busy);
 bail:
 	return;
 }
diff --git a/drivers/infiniband/hw/ipath/ipath_stats.c b/drivers/infiniband/hw/ipath/ipath_stats.c
index 30a8259..9307f71 100644
--- a/drivers/infiniband/hw/ipath/ipath_stats.c
+++ b/drivers/infiniband/hw/ipath/ipath_stats.c
@@ -207,7 +207,7 @@
 	 * don't access the chip while running diags, or memory diags can
 	 * fail
 	 */
-	if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_PRESENT) ||
+	if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_INITTED) ||
 	    ipath_diag_inuse)
 		/* but re-arm the timer, for diags case; won't hurt other */
 		goto done;
@@ -237,11 +237,13 @@
 	if ((dd->ipath_maskederrs & ~dd->ipath_ignorederrs)
 	    && time_after(jiffies, dd->ipath_unmasktime)) {
 		char ebuf[256];
-		ipath_decode_err(ebuf, sizeof ebuf,
+		int iserr;
+		iserr = ipath_decode_err(ebuf, sizeof ebuf,
 				 (dd->ipath_maskederrs & ~dd->
 				  ipath_ignorederrs));
 		if ((dd->ipath_maskederrs & ~dd->ipath_ignorederrs) &
-		    ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL))
+				~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL |
+				INFINIPATH_E_PKTERRS ))
 			ipath_dev_err(dd, "Re-enabling masked errors "
 				      "(%s)\n", ebuf);
 		else {
@@ -252,8 +254,12 @@
 			 * them.  So only complain about these at debug
 			 * level.
 			 */
-			ipath_dbg("Disabling frequent queue full errors "
-				  "(%s)\n", ebuf);
+			if (iserr)
+					ipath_dbg("Re-enabling queue full errors (%s)\n",
+							ebuf);
+			else
+				ipath_cdbg(ERRPKT, "Re-enabling packet"
+						" problem interrupt (%s)\n", ebuf);
 		}
 		dd->ipath_maskederrs = dd->ipath_ignorederrs;
 		ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask,
diff --git a/drivers/infiniband/hw/ipath/ipath_uc.c b/drivers/infiniband/hw/ipath/ipath_uc.c
index 325d663..1c2b03c 100644
--- a/drivers/infiniband/hw/ipath/ipath_uc.c
+++ b/drivers/infiniband/hw/ipath/ipath_uc.c
@@ -42,7 +42,7 @@
 {
 	if (++qp->s_last == qp->s_size)
 		qp->s_last = 0;
-	if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &qp->s_flags) ||
+	if (!(qp->s_flags & IPATH_S_SIGNAL_REQ_WR) ||
 	    (wqe->wr.send_flags & IB_SEND_SIGNALED)) {
 		wc->wr_id = wqe->wr.wr_id;
 		wc->status = IB_WC_SUCCESS;
@@ -344,13 +344,13 @@
 	send_first:
 		if (qp->r_reuse_sge) {
 			qp->r_reuse_sge = 0;
-			qp->r_sge = qp->s_rdma_sge;
+			qp->r_sge = qp->s_rdma_read_sge;
 		} else if (!ipath_get_rwqe(qp, 0)) {
 			dev->n_pkt_drops++;
 			goto done;
 		}
 		/* Save the WQE so we can reuse it in case of an error. */
-		qp->s_rdma_sge = qp->r_sge;
+		qp->s_rdma_read_sge = qp->r_sge;
 		qp->r_rcv_len = 0;
 		if (opcode == OP(SEND_ONLY))
 			goto send_last;
diff --git a/drivers/infiniband/hw/ipath/ipath_ud.c b/drivers/infiniband/hw/ipath/ipath_ud.c
index 9a3e546..a518f7c 100644
--- a/drivers/infiniband/hw/ipath/ipath_ud.c
+++ b/drivers/infiniband/hw/ipath/ipath_ud.c
@@ -308,6 +308,11 @@
 		goto bail;
 	}
 
+	if (wr->wr.ud.ah->pd != qp->ibqp.pd) {
+		ret = -EPERM;
+		goto bail;
+	}
+
 	/* IB spec says that num_sge == 0 is OK. */
 	if (wr->num_sge > qp->s_max_sge) {
 		ret = -EINVAL;
@@ -467,7 +472,7 @@
 
 done:
 	/* Queue the completion status entry. */
-	if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &qp->s_flags) ||
+	if (!(qp->s_flags & IPATH_S_SIGNAL_REQ_WR) ||
 	    (wr->send_flags & IB_SEND_SIGNALED)) {
 		wc.wr_id = wr->wr_id;
 		wc.status = IB_WC_SUCCESS;
@@ -647,6 +652,7 @@
 		ipath_skip_sge(&qp->r_sge, sizeof(struct ib_grh));
 	ipath_copy_sge(&qp->r_sge, data,
 		       wc.byte_len - sizeof(struct ib_grh));
+	qp->r_wrid_valid = 0;
 	wc.wr_id = qp->r_wr_id;
 	wc.status = IB_WC_SUCCESS;
 	wc.opcode = IB_WC_RECV;
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.c b/drivers/infiniband/hw/ipath/ipath_verbs.c
index 2aaacdb..18c6df2 100644
--- a/drivers/infiniband/hw/ipath/ipath_verbs.c
+++ b/drivers/infiniband/hw/ipath/ipath_verbs.c
@@ -438,6 +438,10 @@
 		struct ipath_mcast *mcast;
 		struct ipath_mcast_qp *p;
 
+		if (lnh != IPATH_LRH_GRH) {
+			dev->n_pkt_drops++;
+			goto bail;
+		}
 		mcast = ipath_mcast_find(&hdr->u.l.grh.dgid);
 		if (mcast == NULL) {
 			dev->n_pkt_drops++;
@@ -445,8 +449,7 @@
 		}
 		dev->n_multicast_rcv++;
 		list_for_each_entry_rcu(p, &mcast->qp_list, list)
-			ipath_qp_rcv(dev, hdr, lnh == IPATH_LRH_GRH, data,
-				     tlen, p->qp);
+			ipath_qp_rcv(dev, hdr, 1, data, tlen, p->qp);
 		/*
 		 * Notify ipath_multicast_detach() if it is waiting for us
 		 * to finish.
@@ -773,7 +776,6 @@
 	/* +1 is for the qword padding of pbc */
 	plen = hdrwords + ((len + 3) >> 2) + 1;
 	if (unlikely((plen << 2) > dd->ipath_ibmaxlen)) {
-		ipath_dbg("packet len 0x%x too long, failing\n", plen);
 		ret = -EINVAL;
 		goto bail;
 	}
@@ -980,14 +982,14 @@
 	props->max_cqe = ib_ipath_max_cqes;
 	props->max_mr = dev->lk_table.max;
 	props->max_pd = ib_ipath_max_pds;
-	props->max_qp_rd_atom = 1;
-	props->max_qp_init_rd_atom = 1;
+	props->max_qp_rd_atom = IPATH_MAX_RDMA_ATOMIC;
+	props->max_qp_init_rd_atom = 255;
 	/* props->max_res_rd_atom */
 	props->max_srq = ib_ipath_max_srqs;
 	props->max_srq_wr = ib_ipath_max_srq_wrs;
 	props->max_srq_sge = ib_ipath_max_srq_sges;
 	/* props->local_ca_ack_delay */
-	props->atomic_cap = IB_ATOMIC_HCA;
+	props->atomic_cap = IB_ATOMIC_GLOB;
 	props->max_pkeys = ipath_get_npkeys(dev->dd);
 	props->max_mcast_grp = ib_ipath_max_mcast_grps;
 	props->max_mcast_qp_attach = ib_ipath_max_mcast_qp_attached;
@@ -1557,7 +1559,6 @@
 	dev->node_type = RDMA_NODE_IB_CA;
 	dev->phys_port_cnt = 1;
 	dev->dma_device = &dd->pcidev->dev;
-	dev->class_dev.dev = dev->dma_device;
 	dev->query_device = ipath_query_device;
 	dev->modify_device = ipath_modify_device;
 	dev->query_port = ipath_query_port;
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.h b/drivers/infiniband/hw/ipath/ipath_verbs.h
index c0c8d5b..7c4929f 100644
--- a/drivers/infiniband/hw/ipath/ipath_verbs.h
+++ b/drivers/infiniband/hw/ipath/ipath_verbs.h
@@ -40,9 +40,12 @@
 #include <linux/interrupt.h>
 #include <linux/kref.h>
 #include <rdma/ib_pack.h>
+#include <rdma/ib_user_verbs.h>
 
 #include "ipath_layer.h"
 
+#define IPATH_MAX_RDMA_ATOMIC	4
+
 #define QPN_MAX                 (1 << 24)
 #define QPNMAP_ENTRIES          (QPN_MAX / PAGE_SIZE / BITS_PER_BYTE)
 
@@ -89,7 +92,7 @@
 } __attribute__ ((packed));
 
 struct ib_atomic_eth {
-	__be64 vaddr;
+	__be32 vaddr[2];	/* unaligned so access as 2 32-bit words */
 	__be32 rkey;
 	__be64 swap_data;
 	__be64 compare_data;
@@ -108,7 +111,7 @@
 		} rc;
 		struct {
 			__be32 aeth;
-			__be64 atomic_ack_eth;
+			__be32 atomic_ack_eth[2];
 		} at;
 		__be32 imm_data;
 		__be32 aeth;
@@ -186,7 +189,7 @@
 struct ipath_cq_wc {
 	u32 head;		/* index of next entry to fill */
 	u32 tail;		/* index of next ib_poll_cq() entry */
-	struct ib_wc queue[1];	/* this is actually size ibcq.cqe + 1 */
+	struct ib_uverbs_wc queue[1]; /* this is actually size ibcq.cqe + 1 */
 };
 
 /*
@@ -312,6 +315,19 @@
 };
 
 /*
+ * This structure holds the information that the send tasklet needs
+ * to send a RDMA read response or atomic operation.
+ */
+struct ipath_ack_entry {
+	u8 opcode;
+	u32 psn;
+	union {
+		struct ipath_sge_state rdma_sge;
+		u64 atomic_data;
+	};
+};
+
+/*
  * Variables prefixed with s_ are for the requester (sender).
  * Variables prefixed with r_ are for the responder (receiver).
  * Variables prefixed with ack_ are for responder replies.
@@ -333,24 +349,24 @@
 	struct ipath_mmap_info *ip;
 	struct ipath_sge_state *s_cur_sge;
 	struct ipath_sge_state s_sge;	/* current send request data */
-	/* current RDMA read send data */
-	struct ipath_sge_state s_rdma_sge;
+	struct ipath_ack_entry s_ack_queue[IPATH_MAX_RDMA_ATOMIC + 1];
+	struct ipath_sge_state s_ack_rdma_sge;
+	struct ipath_sge_state s_rdma_read_sge;
 	struct ipath_sge_state r_sge;	/* current receive data */
 	spinlock_t s_lock;
-	unsigned long s_flags;
+	unsigned long s_busy;
 	u32 s_hdrwords;		/* size of s_hdr in 32 bit words */
 	u32 s_cur_size;		/* size of send packet in bytes */
 	u32 s_len;		/* total length of s_sge */
-	u32 s_rdma_len;		/* total length of s_rdma_sge */
+	u32 s_rdma_read_len;	/* total length of s_rdma_read_sge */
 	u32 s_next_psn;		/* PSN for next request */
 	u32 s_last_psn;		/* last response PSN processed */
 	u32 s_psn;		/* current packet sequence number */
-	u32 s_ack_psn;		/* PSN for RDMA_READ */
+	u32 s_ack_rdma_psn;	/* PSN for sending RDMA read responses */
+	u32 s_ack_psn;		/* PSN for acking sends and RDMA writes */
 	u32 s_rnr_timeout;	/* number of milliseconds for RNR timeout */
 	u32 r_ack_psn;		/* PSN for next ACK or atomic ACK */
 	u64 r_wr_id;		/* ID for current receive WQE */
-	u64 r_atomic_data;	/* data for last atomic op */
-	u32 r_atomic_psn;	/* PSN of last atomic op */
 	u32 r_len;		/* total length of r_sge */
 	u32 r_rcv_len;		/* receive data len processed */
 	u32 r_psn;		/* expected rcv packet sequence number */
@@ -360,12 +376,13 @@
 	u8 s_ack_state;		/* opcode of packet to ACK */
 	u8 s_nak_state;		/* non-zero if NAK is pending */
 	u8 r_state;		/* opcode of last packet received */
-	u8 r_ack_state;		/* opcode of packet to ACK */
 	u8 r_nak_state;		/* non-zero if NAK is pending */
 	u8 r_min_rnr_timer;	/* retry timeout value for RNR NAKs */
 	u8 r_reuse_sge;		/* for UC receive errors */
 	u8 r_sge_inx;		/* current index into sg_list */
 	u8 r_wrid_valid;	/* r_wrid set but CQ entry not yet made */
+	u8 r_max_rd_atomic;	/* max number of RDMA read/atomic to receive */
+	u8 r_head_ack_queue;	/* index into s_ack_queue[] */
 	u8 qp_access_flags;
 	u8 s_max_sge;		/* size of s_wq->sg_list */
 	u8 s_retry_cnt;		/* number of times to retry */
@@ -374,6 +391,10 @@
 	u8 s_rnr_retry;		/* requester RNR retry counter */
 	u8 s_wait_credit;	/* limit number of unacked packets sent */
 	u8 s_pkey_index;	/* PKEY index to use */
+	u8 s_max_rd_atomic;	/* max number of RDMA read/atomic to send */
+	u8 s_num_rd_atomic;	/* number of RDMA read/atomic pending */
+	u8 s_tail_ack_queue;	/* index into s_ack_queue[] */
+	u8 s_flags;
 	u8 timeout;		/* Timeout for this QP */
 	enum ib_mtu path_mtu;
 	u32 remote_qpn;
@@ -390,11 +411,16 @@
 	struct ipath_sge r_sg_list[0];	/* verified SGEs */
 };
 
+/* Bit definition for s_busy. */
+#define IPATH_S_BUSY		0
+
 /*
  * Bit definitions for s_flags.
  */
-#define IPATH_S_BUSY		0
-#define IPATH_S_SIGNAL_REQ_WR	1
+#define IPATH_S_SIGNAL_REQ_WR	0x01
+#define IPATH_S_FENCE_PENDING	0x02
+#define IPATH_S_RDMAR_PENDING	0x04
+#define IPATH_S_ACK_PENDING	0x08
 
 #define IPATH_PSN_CREDIT	2048
 
@@ -706,8 +732,6 @@
 
 int ipath_destroy_srq(struct ib_srq *ibsrq);
 
-void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int sig);
-
 int ipath_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry);
 
 struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries,
@@ -757,9 +781,6 @@
 
 void ipath_do_ruc_send(unsigned long data);
 
-u32 ipath_make_rc_ack(struct ipath_qp *qp, struct ipath_other_headers *ohdr,
-		      u32 pmtu);
-
 int ipath_make_rc_req(struct ipath_qp *qp, struct ipath_other_headers *ohdr,
 		      u32 pmtu, u32 *bth0p, u32 *bth2p);
 
diff --git a/drivers/infiniband/hw/mthca/mthca_main.c b/drivers/infiniband/hw/mthca/mthca_main.c
index 0d9b7d0..773145e 100644
--- a/drivers/infiniband/hw/mthca/mthca_main.c
+++ b/drivers/infiniband/hw/mthca/mthca_main.c
@@ -1013,14 +1013,14 @@
 	u64 latest_fw;
 	u32 flags;
 } mthca_hca_table[] = {
-	[TAVOR]        = { .latest_fw = MTHCA_FW_VER(3, 4, 0),
+	[TAVOR]        = { .latest_fw = MTHCA_FW_VER(3, 5, 0),
 			   .flags     = 0 },
-	[ARBEL_COMPAT] = { .latest_fw = MTHCA_FW_VER(4, 7, 600),
+	[ARBEL_COMPAT] = { .latest_fw = MTHCA_FW_VER(4, 8, 200),
 			   .flags     = MTHCA_FLAG_PCIE },
-	[ARBEL_NATIVE] = { .latest_fw = MTHCA_FW_VER(5, 1, 400),
+	[ARBEL_NATIVE] = { .latest_fw = MTHCA_FW_VER(5, 2, 0),
 			   .flags     = MTHCA_FLAG_MEMFREE |
 					MTHCA_FLAG_PCIE },
-	[SINAI]        = { .latest_fw = MTHCA_FW_VER(1, 1, 0),
+	[SINAI]        = { .latest_fw = MTHCA_FW_VER(1, 2, 0),
 			   .flags     = MTHCA_FLAG_MEMFREE |
 					MTHCA_FLAG_PCIE    |
 					MTHCA_FLAG_SINAI_OPT }
@@ -1135,7 +1135,7 @@
 		goto err_cmd;
 
 	if (mdev->fw_ver < mthca_hca_table[hca_type].latest_fw) {
-		mthca_warn(mdev, "HCA FW version %d.%d.%d is old (%d.%d.%d is current).\n",
+		mthca_warn(mdev, "HCA FW version %d.%d.%3d is old (%d.%d.%3d is current).\n",
 			   (int) (mdev->fw_ver >> 32), (int) (mdev->fw_ver >> 16) & 0xffff,
 			   (int) (mdev->fw_ver & 0xffff),
 			   (int) (mthca_hca_table[hca_type].latest_fw >> 32),
diff --git a/drivers/infiniband/hw/mthca/mthca_mr.c b/drivers/infiniband/hw/mthca/mthca_mr.c
index ee561c5..aa6c70a 100644
--- a/drivers/infiniband/hw/mthca/mthca_mr.c
+++ b/drivers/infiniband/hw/mthca/mthca_mr.c
@@ -297,7 +297,8 @@
 
 int mthca_write_mtt_size(struct mthca_dev *dev)
 {
-	if (dev->mr_table.fmr_mtt_buddy != &dev->mr_table.mtt_buddy)
+	if (dev->mr_table.fmr_mtt_buddy != &dev->mr_table.mtt_buddy ||
+	    !(dev->mthca_flags & MTHCA_FLAG_FMR))
 		/*
 		 * Be friendly to WRITE_MTT command
 		 * and leave two empty slots for the
@@ -355,7 +356,8 @@
 	int size = mthca_write_mtt_size(dev);
 	int chunk;
 
-	if (dev->mr_table.fmr_mtt_buddy != &dev->mr_table.mtt_buddy)
+	if (dev->mr_table.fmr_mtt_buddy != &dev->mr_table.mtt_buddy ||
+	    !(dev->mthca_flags & MTHCA_FLAG_FMR))
 		return __mthca_write_mtt(dev, mtt, start_index, buffer_list, list_len);
 
 	while (list_len > 0) {
diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c
index 0725ad7..47e6fd46d 100644
--- a/drivers/infiniband/hw/mthca/mthca_provider.c
+++ b/drivers/infiniband/hw/mthca/mthca_provider.c
@@ -1293,7 +1293,6 @@
 	dev->ib_dev.node_type            = RDMA_NODE_IB_CA;
 	dev->ib_dev.phys_port_cnt        = dev->limits.num_ports;
 	dev->ib_dev.dma_device           = &dev->pdev->dev;
-	dev->ib_dev.class_dev.dev        = &dev->pdev->dev;
 	dev->ib_dev.query_device         = mthca_query_device;
 	dev->ib_dev.query_port           = mthca_query_port;
 	dev->ib_dev.modify_device        = mthca_modify_device;
diff --git a/drivers/infiniband/hw/mthca/mthca_qp.c b/drivers/infiniband/hw/mthca/mthca_qp.c
index 1c6b63a..8fe6fee 100644
--- a/drivers/infiniband/hw/mthca/mthca_qp.c
+++ b/drivers/infiniband/hw/mthca/mthca_qp.c
@@ -1419,11 +1419,10 @@
 	 * unref the mem-free tables and free the QPN in our table.
 	 */
 	if (!qp->ibqp.uobject) {
-		mthca_cq_clean(dev, to_mcq(qp->ibqp.send_cq), qp->qpn,
+		mthca_cq_clean(dev, recv_cq, qp->qpn,
 			       qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL);
-		if (qp->ibqp.send_cq != qp->ibqp.recv_cq)
-			mthca_cq_clean(dev, to_mcq(qp->ibqp.recv_cq), qp->qpn,
-				       qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL);
+		if (send_cq != recv_cq)
+			mthca_cq_clean(dev, send_cq, qp->qpn, NULL);
 
 		mthca_free_memfree(dev, qp);
 		mthca_free_wqe_buf(dev, qp);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index 2b242a4..0c4e59b 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -228,7 +228,6 @@
 	struct net_device *dev = cm_id->context;
 	struct ipoib_dev_priv *priv = netdev_priv(dev);
 	struct ipoib_cm_rx *p;
-	unsigned long flags;
 	unsigned psn;
 	int ret;
 
@@ -257,9 +256,9 @@
 
 	cm_id->context = p;
 	p->jiffies = jiffies;
-	spin_lock_irqsave(&priv->lock, flags);
+	spin_lock_irq(&priv->lock);
 	list_add(&p->list, &priv->cm.passive_ids);
-	spin_unlock_irqrestore(&priv->lock, flags);
+	spin_unlock_irq(&priv->lock);
 	queue_delayed_work(ipoib_workqueue,
 			   &priv->cm.stale_task, IPOIB_CM_RX_DELAY);
 	return 0;
@@ -277,7 +276,6 @@
 {
 	struct ipoib_cm_rx *p;
 	struct ipoib_dev_priv *priv;
-	unsigned long flags;
 	int ret;
 
 	switch (event->event) {
@@ -290,14 +288,14 @@
 	case IB_CM_REJ_RECEIVED:
 		p = cm_id->context;
 		priv = netdev_priv(p->dev);
-		spin_lock_irqsave(&priv->lock, flags);
+		spin_lock_irq(&priv->lock);
 		if (list_empty(&p->list))
 			ret = 0; /* Connection is going away already. */
 		else {
 			list_del_init(&p->list);
 			ret = -ECONNRESET;
 		}
-		spin_unlock_irqrestore(&priv->lock, flags);
+		spin_unlock_irq(&priv->lock);
 		if (ret) {
 			ib_destroy_qp(p->qp);
 			kfree(p);
@@ -351,8 +349,8 @@
 	u64 mapping[IPOIB_CM_RX_SG];
 	int frags;
 
-	ipoib_dbg_data(priv, "cm recv completion: id %d, op %d, status: %d\n",
-		       wr_id, wc->opcode, wc->status);
+	ipoib_dbg_data(priv, "cm recv completion: id %d, status: %d\n",
+		       wr_id, wc->status);
 
 	if (unlikely(wr_id >= ipoib_recvq_size)) {
 		ipoib_warn(priv, "cm recv completion event with wrid %d (> %d)\n",
@@ -408,7 +406,7 @@
 	skb_put_frags(skb, IPOIB_CM_HEAD_SIZE, wc->byte_len, newskb);
 
 	skb->protocol = ((struct ipoib_header *) skb->data)->proto;
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	skb_pull(skb, IPOIB_ENCAP_LEN);
 
 	dev->last_rx = jiffies;
@@ -504,8 +502,8 @@
 	struct ipoib_tx_buf *tx_req;
 	unsigned long flags;
 
-	ipoib_dbg_data(priv, "cm send completion: id %d, op %d, status: %d\n",
-		       wr_id, wc->opcode, wc->status);
+	ipoib_dbg_data(priv, "cm send completion: id %d, status: %d\n",
+		       wr_id, wc->status);
 
 	if (unlikely(wr_id >= ipoib_sendq_size)) {
 		ipoib_warn(priv, "cm send completion event with wrid %d (> %d)\n",
@@ -612,23 +610,22 @@
 {
 	struct ipoib_dev_priv *priv = netdev_priv(dev);
 	struct ipoib_cm_rx *p;
-	unsigned long flags;
 
 	if (!IPOIB_CM_SUPPORTED(dev->dev_addr))
 		return;
 
 	ib_destroy_cm_id(priv->cm.id);
-	spin_lock_irqsave(&priv->lock, flags);
+	spin_lock_irq(&priv->lock);
 	while (!list_empty(&priv->cm.passive_ids)) {
 		p = list_entry(priv->cm.passive_ids.next, typeof(*p), list);
 		list_del_init(&p->list);
-		spin_unlock_irqrestore(&priv->lock, flags);
+		spin_unlock_irq(&priv->lock);
 		ib_destroy_cm_id(p->id);
 		ib_destroy_qp(p->qp);
 		kfree(p);
-		spin_lock_irqsave(&priv->lock, flags);
+		spin_lock_irq(&priv->lock);
 	}
-	spin_unlock_irqrestore(&priv->lock, flags);
+	spin_unlock_irq(&priv->lock);
 
 	cancel_delayed_work(&priv->cm.stale_task);
 }
@@ -642,7 +639,6 @@
 	struct ib_qp_attr qp_attr;
 	int qp_attr_mask, ret;
 	struct sk_buff *skb;
-	unsigned long flags;
 
 	p->mtu = be32_to_cpu(data->mtu);
 
@@ -680,12 +676,12 @@
 
 	skb_queue_head_init(&skqueue);
 
-	spin_lock_irqsave(&priv->lock, flags);
+	spin_lock_irq(&priv->lock);
 	set_bit(IPOIB_FLAG_OPER_UP, &p->flags);
 	if (p->neigh)
 		while ((skb = __skb_dequeue(&p->neigh->queue)))
 			__skb_queue_tail(&skqueue, skb);
-	spin_unlock_irqrestore(&priv->lock, flags);
+	spin_unlock_irq(&priv->lock);
 
 	while ((skb = __skb_dequeue(&skqueue))) {
 		skb->dev = p->dev;
@@ -895,7 +891,6 @@
 	struct ipoib_dev_priv *priv = netdev_priv(tx->dev);
 	struct net_device *dev = priv->dev;
 	struct ipoib_neigh *neigh;
-	unsigned long flags;
 	int ret;
 
 	switch (event->event) {
@@ -914,7 +909,7 @@
 	case IB_CM_REJ_RECEIVED:
 	case IB_CM_TIMEWAIT_EXIT:
 		ipoib_dbg(priv, "CM error %d.\n", event->event);
-		spin_lock_irqsave(&priv->tx_lock, flags);
+		spin_lock_irq(&priv->tx_lock);
 		spin_lock(&priv->lock);
 		neigh = tx->neigh;
 
@@ -934,7 +929,7 @@
 		}
 
 		spin_unlock(&priv->lock);
-		spin_unlock_irqrestore(&priv->tx_lock, flags);
+		spin_unlock_irq(&priv->tx_lock);
 		break;
 	default:
 		break;
@@ -1023,21 +1018,20 @@
 	struct ipoib_dev_priv *priv = container_of(work, struct ipoib_dev_priv,
 						   cm.reap_task);
 	struct ipoib_cm_tx *p;
-	unsigned long flags;
 
-	spin_lock_irqsave(&priv->tx_lock, flags);
+	spin_lock_irq(&priv->tx_lock);
 	spin_lock(&priv->lock);
 	while (!list_empty(&priv->cm.reap_list)) {
 		p = list_entry(priv->cm.reap_list.next, typeof(*p), list);
 		list_del(&p->list);
 		spin_unlock(&priv->lock);
-		spin_unlock_irqrestore(&priv->tx_lock, flags);
+		spin_unlock_irq(&priv->tx_lock);
 		ipoib_cm_tx_destroy(p);
-		spin_lock_irqsave(&priv->tx_lock, flags);
+		spin_lock_irq(&priv->tx_lock);
 		spin_lock(&priv->lock);
 	}
 	spin_unlock(&priv->lock);
-	spin_unlock_irqrestore(&priv->tx_lock, flags);
+	spin_unlock_irq(&priv->tx_lock);
 }
 
 static void ipoib_cm_skb_reap(struct work_struct *work)
@@ -1046,15 +1040,14 @@
 						   cm.skb_task);
 	struct net_device *dev = priv->dev;
 	struct sk_buff *skb;
-	unsigned long flags;
 
 	unsigned mtu = priv->mcast_mtu;
 
-	spin_lock_irqsave(&priv->tx_lock, flags);
+	spin_lock_irq(&priv->tx_lock);
 	spin_lock(&priv->lock);
 	while ((skb = skb_dequeue(&priv->cm.skb_queue))) {
 		spin_unlock(&priv->lock);
-		spin_unlock_irqrestore(&priv->tx_lock, flags);
+		spin_unlock_irq(&priv->tx_lock);
 		if (skb->protocol == htons(ETH_P_IP))
 			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
@@ -1062,11 +1055,11 @@
 			icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev);
 #endif
 		dev_kfree_skb_any(skb);
-		spin_lock_irqsave(&priv->tx_lock, flags);
+		spin_lock_irq(&priv->tx_lock);
 		spin_lock(&priv->lock);
 	}
 	spin_unlock(&priv->lock);
-	spin_unlock_irqrestore(&priv->tx_lock, flags);
+	spin_unlock_irq(&priv->tx_lock);
 }
 
 void ipoib_cm_skb_too_long(struct net_device* dev, struct sk_buff *skb,
@@ -1088,9 +1081,8 @@
 	struct ipoib_dev_priv *priv = container_of(work, struct ipoib_dev_priv,
 						   cm.stale_task.work);
 	struct ipoib_cm_rx *p;
-	unsigned long flags;
 
-	spin_lock_irqsave(&priv->lock, flags);
+	spin_lock_irq(&priv->lock);
 	while (!list_empty(&priv->cm.passive_ids)) {
 		/* List if sorted by LRU, start from tail,
 		 * stop when we see a recently used entry */
@@ -1098,13 +1090,13 @@
 		if (time_before_eq(jiffies, p->jiffies + IPOIB_CM_RX_TIMEOUT))
 			break;
 		list_del_init(&p->list);
-		spin_unlock_irqrestore(&priv->lock, flags);
+		spin_unlock_irq(&priv->lock);
 		ib_destroy_cm_id(p->id);
 		ib_destroy_qp(p->qp);
 		kfree(p);
-		spin_lock_irqsave(&priv->lock, flags);
+		spin_lock_irq(&priv->lock);
 	}
-	spin_unlock_irqrestore(&priv->lock, flags);
+	spin_unlock_irq(&priv->lock);
 }
 
 
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
index ba0ee5c..1bdb910 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
@@ -172,8 +172,8 @@
 	struct sk_buff *skb;
 	u64 addr;
 
-	ipoib_dbg_data(priv, "recv completion: id %d, op %d, status: %d\n",
-		       wr_id, wc->opcode, wc->status);
+	ipoib_dbg_data(priv, "recv completion: id %d, status: %d\n",
+		       wr_id, wc->status);
 
 	if (unlikely(wr_id >= ipoib_recvq_size)) {
 		ipoib_warn(priv, "recv completion event with wrid %d (> %d)\n",
@@ -216,7 +216,7 @@
 	if (wc->slid != priv->local_lid ||
 	    wc->src_qp != priv->qp->qp_num) {
 		skb->protocol = ((struct ipoib_header *) skb->data)->proto;
-		skb->mac.raw = skb->data;
+		skb_reset_mac_header(skb);
 		skb_pull(skb, IPOIB_ENCAP_LEN);
 
 		dev->last_rx = jiffies;
@@ -245,8 +245,8 @@
 	struct ipoib_tx_buf *tx_req;
 	unsigned long flags;
 
-	ipoib_dbg_data(priv, "send completion: id %d, op %d, status: %d\n",
-		       wr_id, wc->opcode, wc->status);
+	ipoib_dbg_data(priv, "send completion: id %d, status: %d\n",
+		       wr_id, wc->status);
 
 	if (unlikely(wr_id >= ipoib_sendq_size)) {
 		ipoib_warn(priv, "send completion event with wrid %d (> %d)\n",
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index f2a40ae..b4c380c 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -395,14 +395,10 @@
 	skb_queue_head_init(&skqueue);
 
 	if (!status) {
-		struct ib_ah_attr av = {
-			.dlid 	       = be16_to_cpu(pathrec->dlid),
-			.sl 	       = pathrec->sl,
-			.port_num      = priv->port,
-			.static_rate   = pathrec->rate
-		};
+		struct ib_ah_attr av;
 
-		ah = ipoib_create_ah(dev, priv->pd, &av);
+		if (!ib_init_ah_from_path(priv->ca, priv->port, pathrec, &av))
+			ah = ipoib_create_ah(dev, priv->pd, &av);
 	}
 
 	spin_lock_irqsave(&priv->lock, flags);
diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c
index a00fe47..bd686a2 100644
--- a/drivers/input/gameport/gameport.c
+++ b/drivers/input/gameport/gameport.c
@@ -190,16 +190,14 @@
  * Basic gameport -> driver core mappings
  */
 
-static void gameport_bind_driver(struct gameport *gameport, struct gameport_driver *drv)
+static int gameport_bind_driver(struct gameport *gameport, struct gameport_driver *drv)
 {
 	int error;
 
-	down_write(&gameport_bus.subsys.rwsem);
-
 	gameport->dev.driver = &drv->driver;
 	if (drv->connect(gameport, drv)) {
 		gameport->dev.driver = NULL;
-		goto out;
+		return -ENODEV;
 	}
 
 	error = device_bind_driver(&gameport->dev);
@@ -211,31 +209,21 @@
 			drv->description, error);
 		drv->disconnect(gameport);
 		gameport->dev.driver = NULL;
-		goto out;
+		return error;
 	}
 
- out:
-	up_write(&gameport_bus.subsys.rwsem);
-}
-
-static void gameport_release_driver(struct gameport *gameport)
-{
-	down_write(&gameport_bus.subsys.rwsem);
-	device_release_driver(&gameport->dev);
-	up_write(&gameport_bus.subsys.rwsem);
+	return 0;
 }
 
 static void gameport_find_driver(struct gameport *gameport)
 {
 	int error;
 
-	down_write(&gameport_bus.subsys.rwsem);
 	error = device_attach(&gameport->dev);
 	if (error < 0)
 		printk(KERN_WARNING
 			"gameport: device_attach() failed for %s (%s), error: %d\n",
 			gameport->phys, gameport->name, error);
-	up_write(&gameport_bus.subsys.rwsem);
 }
 
 
@@ -483,13 +471,12 @@
 {
 	struct gameport *gameport = to_gameport_port(dev);
 	struct device_driver *drv;
-	int retval;
+	int error;
 
-	retval = mutex_lock_interruptible(&gameport_mutex);
-	if (retval)
-		return retval;
+	error = mutex_lock_interruptible(&gameport_mutex);
+	if (error)
+		return error;
 
-	retval = count;
 	if (!strncmp(buf, "none", count)) {
 		gameport_disconnect_port(gameport);
 	} else if (!strncmp(buf, "reconnect", count)) {
@@ -499,15 +486,15 @@
 		gameport_find_driver(gameport);
 	} else if ((drv = driver_find(buf, &gameport_bus)) != NULL) {
 		gameport_disconnect_port(gameport);
-		gameport_bind_driver(gameport, to_gameport_driver(drv));
+		error = gameport_bind_driver(gameport, to_gameport_driver(drv));
 		put_driver(drv);
 	} else {
-		retval = -EINVAL;
+		error = -EINVAL;
 	}
 
 	mutex_unlock(&gameport_mutex);
 
-	return retval;
+	return error ? error : count;
 }
 
 static struct device_attribute gameport_device_attrs[] = {
@@ -655,7 +642,7 @@
 		do {
 			parent = s->parent;
 
-			gameport_release_driver(s);
+			device_release_driver(&s->dev);
 			gameport_destroy_port(s);
 		} while ((s = parent) != gameport);
 	}
@@ -663,7 +650,7 @@
 	/*
 	 * Ok, no children left, now disconnect this port
 	 */
-	gameport_release_driver(gameport);
+	device_release_driver(&gameport->dev);
 }
 
 void gameport_rescan(struct gameport *gameport)
diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
index a15e531..5895202 100644
--- a/drivers/input/serio/serio.c
+++ b/drivers/input/serio/serio.c
@@ -115,18 +115,18 @@
  * Basic serio -> driver core mappings
  */
 
-static void serio_bind_driver(struct serio *serio, struct serio_driver *drv)
+static int serio_bind_driver(struct serio *serio, struct serio_driver *drv)
 {
 	int error;
 
-	down_write(&serio_bus.subsys.rwsem);
-
 	if (serio_match_port(drv->id_table, serio)) {
+
 		serio->dev.driver = &drv->driver;
 		if (serio_connect_driver(serio, drv)) {
 			serio->dev.driver = NULL;
-			goto out;
+			return -ENODEV;
 		}
+
 		error = device_bind_driver(&serio->dev);
 		if (error) {
 			printk(KERN_WARNING
@@ -136,31 +136,21 @@
 				drv->description, error);
 			serio_disconnect_driver(serio);
 			serio->dev.driver = NULL;
-			goto out;
+			return error;
 		}
 	}
- out:
-	up_write(&serio_bus.subsys.rwsem);
-}
-
-static void serio_release_driver(struct serio *serio)
-{
-	down_write(&serio_bus.subsys.rwsem);
-	device_release_driver(&serio->dev);
-	up_write(&serio_bus.subsys.rwsem);
+	return 0;
 }
 
 static void serio_find_driver(struct serio *serio)
 {
 	int error;
 
-	down_write(&serio_bus.subsys.rwsem);
 	error = device_attach(&serio->dev);
 	if (error < 0)
 		printk(KERN_WARNING
 			"serio: device_attach() failed for %s (%s), error: %d\n",
 			serio->phys, serio->name, error);
-	up_write(&serio_bus.subsys.rwsem);
 }
 
 
@@ -470,13 +460,12 @@
 {
 	struct serio *serio = to_serio_port(dev);
 	struct device_driver *drv;
-	int retval;
+	int error;
 
-	retval = mutex_lock_interruptible(&serio_mutex);
-	if (retval)
-		return retval;
+	error = mutex_lock_interruptible(&serio_mutex);
+	if (error)
+		return error;
 
-	retval = count;
 	if (!strncmp(buf, "none", count)) {
 		serio_disconnect_port(serio);
 	} else if (!strncmp(buf, "reconnect", count)) {
@@ -486,15 +475,15 @@
 		serio_find_driver(serio);
 	} else if ((drv = driver_find(buf, &serio_bus)) != NULL) {
 		serio_disconnect_port(serio);
-		serio_bind_driver(serio, to_serio_driver(drv));
+		error = serio_bind_driver(serio, to_serio_driver(drv));
 		put_driver(drv);
 	} else {
-		retval = -EINVAL;
+		error = -EINVAL;
 	}
 
 	mutex_unlock(&serio_mutex);
 
-	return retval;
+	return error ? error : count;
 }
 
 static ssize_t serio_show_bind_mode(struct device *dev, struct device_attribute *attr, char *buf)
@@ -665,7 +654,7 @@
 		do {
 			parent = s->parent;
 
-			serio_release_driver(s);
+			device_release_driver(&s->dev);
 			serio_destroy_port(s);
 		} while ((s = parent) != serio);
 	}
@@ -673,7 +662,7 @@
 	/*
 	 * Ok, no children left, now disconnect this port
 	 */
-	serio_release_driver(serio);
+	device_release_driver(&serio->dev);
 }
 
 void serio_rescan(struct serio *serio)
diff --git a/drivers/isdn/act2000/module.c b/drivers/isdn/act2000/module.c
index e3e5c13..ee2b0b9 100644
--- a/drivers/isdn/act2000/module.c
+++ b/drivers/isdn/act2000/module.c
@@ -442,7 +442,7 @@
 			return 0;
 		}
 		skb_reserve(xmit_skb, 19);
-		memcpy(skb_put(xmit_skb, len), skb->data, len);
+		skb_copy_from_linear_data(skb, skb_put(xmit_skb, len), len);
 	} else {
 		xmit_skb = skb_clone(skb, GFP_ATOMIC);
 		if (!xmit_skb) {
diff --git a/drivers/isdn/gigaset/usb-gigaset.c b/drivers/isdn/gigaset/usb-gigaset.c
index 2baef34..c8e1c35 100644
--- a/drivers/isdn/gigaset/usb-gigaset.c
+++ b/drivers/isdn/gigaset/usb-gigaset.c
@@ -652,7 +652,7 @@
 	 * transmit data
 	 */
 	count = min(bcs->tx_skb->len, (unsigned) ucs->bulk_out_size);
-	memcpy(ucs->bulk_out_buffer, bcs->tx_skb->data, count);
+	skb_copy_from_linear_data(bcs->tx_skb, ucs->bulk_out_buffer, count);
 	skb_pull(bcs->tx_skb, count);
 	atomic_set(&ucs->busy, 1);
 	gig_dbg(DEBUG_OUTPUT, "write_modem: send %d bytes", count);
diff --git a/drivers/isdn/hardware/avm/b1dma.c b/drivers/isdn/hardware/avm/b1dma.c
index 1e2d38e..428872b 100644
--- a/drivers/isdn/hardware/avm/b1dma.c
+++ b/drivers/isdn/hardware/avm/b1dma.c
@@ -404,7 +404,8 @@
 		printk(KERN_DEBUG "tx: put 0x%x len=%d\n", 
 		       skb->data[2], txlen);
 #endif
-		memcpy(dma->sendbuf.dmabuf, skb->data+2, skb->len-2);
+		skb_copy_from_linear_data_offset(skb, 2, dma->sendbuf.dmabuf,
+						 skb->len - 2);
 	}
 	txlen = (txlen + 3) & ~3;
 
diff --git a/drivers/isdn/hardware/avm/c4.c b/drivers/isdn/hardware/avm/c4.c
index 6f5efa8d..d58f927 100644
--- a/drivers/isdn/hardware/avm/c4.c
+++ b/drivers/isdn/hardware/avm/c4.c
@@ -457,7 +457,8 @@
 		printk(KERN_DEBUG "%s: tx put 0x%x len=%d\n",
 				card->name, skb->data[2], txlen);
 #endif
-		memcpy(dma->sendbuf.dmabuf, skb->data+2, skb->len-2);
+		skb_copy_from_linear_data_offset(skb, 2, dma->sendbuf.dmabuf,
+						 skb->len - 2);
 	}
 	txlen = (txlen + 3) & ~3;
 
diff --git a/drivers/isdn/hisax/elsa_ser.c b/drivers/isdn/hisax/elsa_ser.c
index ae377e8..1642dca 100644
--- a/drivers/isdn/hisax/elsa_ser.c
+++ b/drivers/isdn/hisax/elsa_ser.c
@@ -254,14 +254,16 @@
 	count = len;
 	if (count > MAX_MODEM_BUF - fp) {
 		count = MAX_MODEM_BUF - fp;
-		memcpy(cs->hw.elsa.transbuf + fp, bcs->tx_skb->data, count);
+		skb_copy_from_linear_data(bcs->tx_skb,
+					  cs->hw.elsa.transbuf + fp, count);
 		skb_pull(bcs->tx_skb, count);
 		cs->hw.elsa.transcnt += count;
 		ret = count;
 		count = len - count;
 		fp = 0;
 	}
-	memcpy((cs->hw.elsa.transbuf + fp), bcs->tx_skb->data, count);
+	skb_copy_from_linear_data(bcs->tx_skb,
+				  cs->hw.elsa.transbuf + fp, count);
 	skb_pull(bcs->tx_skb, count);
 	cs->hw.elsa.transcnt += count;
 	ret += count;
diff --git a/drivers/isdn/hisax/isdnl2.c b/drivers/isdn/hisax/isdnl2.c
index cd3b5ad..3446f24 100644
--- a/drivers/isdn/hisax/isdnl2.c
+++ b/drivers/isdn/hisax/isdnl2.c
@@ -1293,7 +1293,8 @@
 		oskb = skb;
 		skb = alloc_skb(oskb->len + i, GFP_ATOMIC);
 		memcpy(skb_put(skb, i), header, i);
-		memcpy(skb_put(skb, oskb->len), oskb->data, oskb->len);
+		skb_copy_from_linear_data(oskb,
+					  skb_put(skb, oskb->len), oskb->len);
 		dev_kfree_skb(oskb);
 	}
 	st->l2.l2l1(st, PH_PULL | INDICATION, skb);
diff --git a/drivers/isdn/hysdn/hycapi.c b/drivers/isdn/hysdn/hycapi.c
index b2ae4ec..f854501 100644
--- a/drivers/isdn/hysdn/hycapi.c
+++ b/drivers/isdn/hysdn/hycapi.c
@@ -398,8 +398,9 @@
 			_len = CAPIMSG_LEN(skb->data);
 			if (_len > 22) {
 				_len2 = _len - 22;
-				memcpy(msghead, skb->data, 22);
-				memcpy(skb->data + _len2, msghead, 22);
+				skb_copy_from_linear_data(skb, msghead, 22);
+				skb_copy_to_linear_data_offset(skb, _len2,
+							       msghead, 22);
 				skb_pull(skb, _len2);
 				CAPIMSG_SETLEN(skb->data, 22);
 				retval = capilib_data_b3_req(&cinfo->ncci_head,
diff --git a/drivers/isdn/hysdn/hysdn_net.c b/drivers/isdn/hysdn/hysdn_net.c
index 557d96c..cfa8fa5 100644
--- a/drivers/isdn/hysdn/hysdn_net.c
+++ b/drivers/isdn/hysdn/hysdn_net.c
@@ -214,8 +214,6 @@
 		lp->stats.rx_dropped++;
 		return;
 	}
-	skb->dev = &lp->netdev;
-
 	/* copy the data */
 	memcpy(skb_put(skb, len), buf, len);
 
diff --git a/drivers/isdn/hysdn/hysdn_sched.c b/drivers/isdn/hysdn/hysdn_sched.c
index b7b5aa4..81db4a1 100644
--- a/drivers/isdn/hysdn/hysdn_sched.c
+++ b/drivers/isdn/hysdn/hysdn_sched.c
@@ -113,7 +113,8 @@
 	    (skb = hysdn_tx_netget(card)) != NULL) 
 	{
 		if (skb->len <= maxlen) {
-			memcpy(buf, skb->data, skb->len);	/* copy the packet to the buffer */
+			/* copy the packet to the buffer */
+			skb_copy_from_linear_data(skb, buf, skb->len);
 			*len = skb->len;
 			*chan = CHAN_NDIS_DATA;
 			card->net_tx_busy = 1;	/* we are busy sending network data */
@@ -126,7 +127,7 @@
 	    ((skb = hycapi_tx_capiget(card)) != NULL) )
 	{
 		if (skb->len <= maxlen) {
-			memcpy(buf, skb->data, skb->len);
+			skb_copy_from_linear_data(skb, buf, skb->len);
 			*len = skb->len;
 			*chan = CHAN_CAPI;
 			hycapi_tx_capiack(card);
diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c
index 9c926e4..c97330b 100644
--- a/drivers/isdn/i4l/isdn_common.c
+++ b/drivers/isdn/i4l/isdn_common.c
@@ -829,7 +829,7 @@
 				dflag = 0;
 			}
 			count_put = count_pull;
-			memcpy(cp, skb->data, count_put);
+			skb_copy_from_linear_data(skb, cp, count_put);
 			cp += count_put;
 			len -= count_put;
 #ifdef CONFIG_ISDN_AUDIO
diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c
index 838b373..aa83277 100644
--- a/drivers/isdn/i4l/isdn_net.c
+++ b/drivers/isdn/i4l/isdn_net.c
@@ -872,7 +872,8 @@
 static void
 isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp)
 {
-	u_char *p = skb->nh.raw; /* hopefully, this was set correctly */
+	/* hopefully, this was set correctly */
+	const u_char *p = skb_network_header(skb);
 	unsigned short proto = ntohs(skb->protocol);
 	int data_ofs;
 	ip_ports *ipp;
@@ -880,7 +881,7 @@
 
 	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) {
+	if (p < skb->data || skb->network_header >= skb->tail) {
 		/* fall back to old isdn_net_log_packet method() */
 		char * buf = skb->data;
 
@@ -1121,7 +1122,7 @@
 	if (!skb)
 		return;
 	if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
-		int pullsize = (ulong)skb->nh.raw - (ulong)skb->data - ETH_HLEN;
+		const int pullsize = skb_network_offset(skb) - ETH_HLEN;
 		if (pullsize > 0) {
 			printk(KERN_DEBUG "isdn_net: Pull junk %d\n", pullsize);
 			skb_pull(skb, pullsize);
@@ -1366,7 +1367,7 @@
 	struct ethhdr *eth;
 	unsigned char *rawp;
 
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	skb_pull(skb, ETH_HLEN);
 	eth = eth_hdr(skb);
 
@@ -1786,7 +1787,7 @@
 	}
 	skb->dev = ndev;
 	skb->pkt_type = PACKET_HOST;
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 #ifdef ISDN_DEBUG_NET_DUMP
 	isdn_dumppkt("R:", skb->data, skb->len, 40);
 #endif
diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c
index 1b2df80..387392c 100644
--- a/drivers/isdn/i4l/isdn_ppp.c
+++ b/drivers/isdn/i4l/isdn_ppp.c
@@ -1100,7 +1100,8 @@
 					goto drop_packet;
 				}
 				skb_put(skb, skb_old->len + 128);
-				memcpy(skb->data, skb_old->data, skb_old->len);
+				skb_copy_from_linear_data(skb_old, skb->data,
+							  skb_old->len);
 				if (net_dev->local->ppp_slot < 0) {
 					printk(KERN_ERR "%s: net_dev->local->ppp_slot(%d) out of range\n",
 						__FUNCTION__, net_dev->local->ppp_slot);
@@ -1167,7 +1168,7 @@
 		mlp->huptimer = 0;
 #endif /* CONFIG_IPPP_FILTER */
 	skb->dev = dev;
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	netif_rx(skb);
 	/* net_dev->local->stats.rx_packets++; done in isdn_net.c */
 	return;
@@ -1902,7 +1903,9 @@
 		while( from != to ) {
 			unsigned int len = from->len - MP_HEADER_LEN;
 
-			memcpy(skb_put(skb,len), from->data+MP_HEADER_LEN, len);
+			skb_copy_from_linear_data_offset(from, MP_HEADER_LEN,
+							 skb_put(skb,len),
+							 len);
 			frag = from->next;
 			isdn_ppp_mp_free_skb(mp, from);
 			from = frag; 
diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c
index e3add27..e93ad59 100644
--- a/drivers/isdn/isdnloop/isdnloop.c
+++ b/drivers/isdn/isdnloop/isdnloop.c
@@ -415,7 +415,8 @@
 		spin_lock_irqsave(&card->isdnloop_lock, flags);
 		nskb = dev_alloc_skb(skb->len);
 		if (nskb) {
-			memcpy(skb_put(nskb, len), skb->data, len);
+			skb_copy_from_linear_data(skb,
+						  skb_put(nskb, len), len);
 			skb_queue_tail(&card->bqueue[channel], nskb);
 			dev_kfree_skb(skb);
 		} else
diff --git a/drivers/isdn/pcbit/capi.c b/drivers/isdn/pcbit/capi.c
index 47c59e9..7b55e15 100644
--- a/drivers/isdn/pcbit/capi.c
+++ b/drivers/isdn/pcbit/capi.c
@@ -429,8 +429,9 @@
 		if (!(info->data.setup.CallingPN = kmalloc(len - count + 1, GFP_ATOMIC)))
 			return -1;
        
-		memcpy(info->data.setup.CallingPN, skb->data + count + 1, 
-		       len - count);
+		skb_copy_from_linear_data_offset(skb, count + 1,
+						 info->data.setup.CallingPN,
+						 len - count);
 		info->data.setup.CallingPN[len - count] = 0;
 
 	}
@@ -457,8 +458,9 @@
 		if (!(info->data.setup.CalledPN = kmalloc(len - count + 1, GFP_ATOMIC)))
 			return -1;
         
-		memcpy(info->data.setup.CalledPN, skb->data + count + 1, 
-		       len - count); 
+		skb_copy_from_linear_data_offset(skb, count + 1,
+						 info->data.setup.CalledPN,
+						 len - count);
 		info->data.setup.CalledPN[len - count] = 0;
 
 	}
@@ -539,7 +541,7 @@
 
 #ifdef DEBUG
 	if (len > 1 && len < 31) {
-		memcpy(str, skb->data + 2, len - 1);
+		skb_copy_from_linear_data_offset(skb, 2, str, len - 1);
 		str[len] = 0;
 		printk(KERN_DEBUG "Connected Party Number: %s\n", str);
 	}
diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c
index f729eeb..adfea3c 100644
--- a/drivers/macintosh/adb.c
+++ b/drivers/macintosh/adb.c
@@ -90,7 +90,7 @@
 int __adb_probe_sync;
 
 #ifdef CONFIG_PM
-static int adb_notify_sleep(struct pmu_sleep_notifier *self, int when);
+static void adb_notify_sleep(struct pmu_sleep_notifier *self, int when);
 static struct pmu_sleep_notifier adb_sleep_notifier = {
 	adb_notify_sleep,
 	SLEEP_LEVEL_ADB,
@@ -340,11 +340,9 @@
 /*
  * notify clients before sleep and reset bus afterwards
  */
-int
+void
 adb_notify_sleep(struct pmu_sleep_notifier *self, int when)
 {
-	int ret;
-	
 	switch (when) {
 	case PBOOK_SLEEP_REQUEST:
 		adb_got_sleep = 1;
@@ -353,22 +351,8 @@
 		/* Stop autopoll */
 		if (adb_controller->autopoll)
 			adb_controller->autopoll(0);
-		ret = blocking_notifier_call_chain(&adb_client_list,
-				ADB_MSG_POWERDOWN, NULL);
-		if (ret & NOTIFY_STOP_MASK) {
-			up(&adb_probe_mutex);
-			return PBOOK_SLEEP_REFUSE;
-		}
-		break;
-	case PBOOK_SLEEP_REJECT:
-		if (adb_got_sleep) {
-			adb_got_sleep = 0;
-			up(&adb_probe_mutex);
-			adb_reset_bus();
-		}
-		break;
-		
-	case PBOOK_SLEEP_NOW:
+		blocking_notifier_call_chain(&adb_client_list,
+			ADB_MSG_POWERDOWN, NULL);
 		break;
 	case PBOOK_WAKE:
 		adb_got_sleep = 0;
@@ -376,14 +360,13 @@
 		adb_reset_bus();
 		break;
 	}
-	return PBOOK_SLEEP_OK;
 }
 #endif /* CONFIG_PM */
 
 static int
 do_adb_reset_bus(void)
 {
-	int ret, nret;
+	int ret;
 	
 	if (adb_controller == NULL)
 		return -ENXIO;
@@ -391,13 +374,8 @@
 	if (adb_controller->autopoll)
 		adb_controller->autopoll(0);
 
-	nret = blocking_notifier_call_chain(&adb_client_list,
-			ADB_MSG_PRE_RESET, NULL);
-	if (nret & NOTIFY_STOP_MASK) {
-		if (adb_controller->autopoll)
-			adb_controller->autopoll(autopoll_devs);
-		return -EBUSY;
-	}
+	blocking_notifier_call_chain(&adb_client_list,
+		ADB_MSG_PRE_RESET, NULL);
 
 	if (sleepy_trackpad) {
 		/* Let the trackpad settle down */
@@ -427,10 +405,8 @@
 	}
 	up(&adb_handler_sem);
 
-	nret = blocking_notifier_call_chain(&adb_client_list,
-			ADB_MSG_POST_RESET, NULL);
-	if (nret & NOTIFY_STOP_MASK)
-		return -EBUSY;
+	blocking_notifier_call_chain(&adb_client_list,
+		ADB_MSG_POST_RESET, NULL);
 	
 	return ret;
 }
diff --git a/drivers/macintosh/ans-lcd.c b/drivers/macintosh/ans-lcd.c
index cdd5a0f..e54c4d9 100644
--- a/drivers/macintosh/ans-lcd.c
+++ b/drivers/macintosh/ans-lcd.c
@@ -145,11 +145,12 @@
 	int retval;
 	struct device_node* node;
 
-	node = find_devices("lcd");
-	if (!node || !node->parent)
+	node = of_find_node_by_name(NULL, "lcd");
+	if (!node || !node->parent || strcmp(node->parent->name, "gc")) {
+		of_node_put(node);
 		return -ENODEV;
-	if (strcmp(node->parent->name, "gc"))
-		return -ENODEV;
+	}
+	of_node_put(node);
 
 	anslcd_ptr = ioremap(ANSLCD_ADDR, 0x20);
 	
diff --git a/drivers/macintosh/apm_emu.c b/drivers/macintosh/apm_emu.c
index c5e4d43..cdb0bea 100644
--- a/drivers/macintosh/apm_emu.c
+++ b/drivers/macintosh/apm_emu.c
@@ -96,7 +96,7 @@
 static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
 static struct apm_user *	user_list;
 
-static int apm_notify_sleep(struct pmu_sleep_notifier *self, int when);
+static void apm_notify_sleep(struct pmu_sleep_notifier *self, int when);
 static struct pmu_sleep_notifier apm_sleep_notifier = {
 	apm_notify_sleep,
 	SLEEP_LEVEL_USERLAND,
@@ -352,7 +352,7 @@
  * doesn't provide a way to NAK, but this could be added
  * here.
  */
-static int wait_all_suspend(void)
+static void wait_all_suspend(void)
 {
 	DECLARE_WAITQUEUE(wait, current);
 
@@ -366,24 +366,19 @@
 	remove_wait_queue(&apm_suspend_waitqueue, &wait);
 
 	DBG("apm_emu: wait_all_suspend() - complete !\n");
-	
-	return 1;
 }
 
-static int apm_notify_sleep(struct pmu_sleep_notifier *self, int when)
+static void apm_notify_sleep(struct pmu_sleep_notifier *self, int when)
 {
 	switch(when) {
 		case PBOOK_SLEEP_REQUEST:
 			queue_event(APM_SYS_SUSPEND, NULL);
-			if (!wait_all_suspend())
-				return PBOOK_SLEEP_REFUSE;
+			wait_all_suspend();
 			break;
-		case PBOOK_SLEEP_REJECT:
 		case PBOOK_WAKE:
 			queue_event(APM_NORMAL_RESUME, NULL);
 			break;
 	}
-	return PBOOK_SLEEP_OK;
 }
 
 #define APM_CRITICAL		10
diff --git a/drivers/macintosh/mac_hid.c b/drivers/macintosh/mac_hid.c
index c1fd816..1599dc3 100644
--- a/drivers/macintosh/mac_hid.c
+++ b/drivers/macintosh/mac_hid.c
@@ -102,8 +102,6 @@
 	return 0;
 }
 
-EXPORT_SYMBOL(mac_hid_mouse_emulate_buttons);
-
 static int emumousebtn_input_register(void)
 {
 	int ret;
diff --git a/drivers/macintosh/macio-adb.c b/drivers/macintosh/macio-adb.c
index 026b67f..79119f5 100644
--- a/drivers/macintosh/macio-adb.c
+++ b/drivers/macintosh/macio-adb.c
@@ -82,7 +82,14 @@
 
 int macio_probe(void)
 {
-	return find_compatible_devices("adb", "chrp,adb0")? 0: -ENODEV;
+	struct device_node *np;
+
+	np = of_find_compatible_node(NULL, "adb", "chrp,adb0");
+	if (np) {
+		of_node_put(np);
+		return 0;
+	}
+	return -ENODEV;
 }
 
 int macio_init(void)
@@ -91,12 +98,14 @@
 	struct resource r;
 	unsigned int irq;
 
-	adbs = find_compatible_devices("adb", "chrp,adb0");
+	adbs = of_find_compatible_node(NULL, "adb", "chrp,adb0");
 	if (adbs == 0)
 		return -ENXIO;
 
-	if (of_address_to_resource(adbs, 0, &r))
+	if (of_address_to_resource(adbs, 0, &r)) {
+		of_node_put(adbs);
 		return -ENXIO;
+	}
 	adb = ioremap(r.start, sizeof(struct adb_regs));
 
 	out_8(&adb->ctrl.r, 0);
@@ -107,6 +116,7 @@
 	out_8(&adb->autopoll.r, APE);
 
 	irq = irq_of_parse_and_map(adbs, 0);
+	of_node_put(adbs);
 	if (request_irq(irq, macio_adb_interrupt, 0, "ADB", (void *)0)) {
 		printk(KERN_ERR "ADB: can't get irq %d\n", irq);
 		return -EAGAIN;
diff --git a/drivers/macintosh/macio_asic.c b/drivers/macintosh/macio_asic.c
index d562160..c96b7fe 100644
--- a/drivers/macintosh/macio_asic.c
+++ b/drivers/macintosh/macio_asic.c
@@ -134,108 +134,12 @@
 	return 0;
 }
 
-static int macio_uevent(struct device *dev, char **envp, int num_envp,
-                          char *buffer, int buffer_size)
-{
-	struct macio_dev * macio_dev;
-	struct of_device * of;
-	char *scratch;
-	const char *compat, *compat2;
-
-	int i = 0;
-	int length, cplen, cplen2, seen = 0;
-
-	if (!dev)
-		return -ENODEV;
-
-	macio_dev = to_macio_device(dev);
-	if (!macio_dev)
-		return -ENODEV;
-
-	of = &macio_dev->ofdev;
-
-	/* stuff we want to pass to /sbin/hotplug */
-	envp[i++] = scratch = buffer;
-	length = scnprintf (scratch, buffer_size, "OF_NAME=%s", of->node->name);
-	++length;
-	buffer_size -= length;
-	if ((buffer_size <= 0) || (i >= num_envp))
-		return -ENOMEM;
-	scratch += length;
-
-	envp[i++] = scratch;
-	length = scnprintf (scratch, buffer_size, "OF_TYPE=%s", of->node->type);
-	++length;
-	buffer_size -= length;
-	if ((buffer_size <= 0) || (i >= num_envp))
-		return -ENOMEM;
-	scratch += length;
-
-        /* Since the compatible field can contain pretty much anything
-         * it's not really legal to split it out with commas. We split it
-         * up using a number of environment variables instead. */
-
-	compat = get_property(of->node, "compatible", &cplen);
-	compat2 = compat;
-	cplen2= cplen;
-	while (compat && cplen > 0) {
-                envp[i++] = scratch;
-		length = scnprintf (scratch, buffer_size,
-		                     "OF_COMPATIBLE_%d=%s", seen, compat);
-		++length;
-		buffer_size -= length;
-		if ((buffer_size <= 0) || (i >= num_envp))
-			return -ENOMEM;
-		scratch += length;
-		length = strlen (compat) + 1;
-		compat += length;
-		cplen -= length;
-		seen++;
-	}
-
-	envp[i++] = scratch;
-	length = scnprintf (scratch, buffer_size, "OF_COMPATIBLE_N=%d", seen);
-	++length;
-	buffer_size -= length;
-	if ((buffer_size <= 0) || (i >= num_envp))
-		return -ENOMEM;
-	scratch += length;
-
-	envp[i++] = scratch;
-	length = scnprintf (scratch, buffer_size, "MODALIAS=of:N%sT%s",
-			of->node->name, of->node->type);
-	/* overwrite '\0' */
-	buffer_size -= length;
-	if ((buffer_size <= 0) || (i >= num_envp))
-		return -ENOMEM;
-	scratch += length;
-
-	if (!compat2) {
-		compat2 = "";
-		cplen2 = 1;
-	}
-	while (cplen2 > 0) {
-		length = snprintf (scratch, buffer_size, "C%s", compat2);
-		buffer_size -= length;
-		if (buffer_size <= 0)
-			return -ENOMEM;
-		scratch += length;
-		length = strlen (compat2) + 1;
-		compat2 += length;
-		cplen2 -= length;
-	}
-
-	envp[i] = NULL;
-
-	return 0;
-}
-
 extern struct device_attribute macio_dev_attrs[];
 
 struct bus_type macio_bus_type = {
        .name	= "macio",
        .match	= macio_bus_match,
-       .uevent = macio_uevent,
+       .uevent = of_device_uevent,
        .probe	= macio_device_probe,
        .remove	= macio_device_remove,
        .shutdown = macio_device_shutdown,
@@ -491,7 +395,7 @@
 #endif
 			MAX_NODE_NAME_SIZE, np->name);
 	} else {
-		reg = get_property(np, "reg", NULL);
+		reg = of_get_property(np, "reg", NULL);
 		sprintf(dev->ofdev.dev.bus_id, "%1d.%08x:%.*s",
 			chip->lbus.index,
 			reg ? *reg : 0, MAX_NODE_NAME_SIZE, np->name);
diff --git a/drivers/macintosh/macio_sysfs.c b/drivers/macintosh/macio_sysfs.c
index 8566bdf..cc82679 100644
--- a/drivers/macintosh/macio_sysfs.c
+++ b/drivers/macintosh/macio_sysfs.c
@@ -21,7 +21,7 @@
 	int length = 0;
 
 	of = &to_macio_device (dev)->ofdev;
-	compat = get_property(of->node, "compatible", &cplen);
+	compat = of_get_property(of->node, "compatible", &cplen);
 	if (!compat) {
 		*buf = '\0';
 		return 0;
@@ -47,18 +47,20 @@
 	int length;
 
 	of = &to_macio_device (dev)->ofdev;
-	compat = get_property(of->node, "compatible", &cplen);
+	compat = of_get_property(of->node, "compatible", &cplen);
 	if (!compat) compat = "", cplen = 1;
 	length = sprintf (buf, "of:N%sT%s", of->node->name, of->node->type);
 	buf += length;
 	while (cplen > 0) {
 		int l;
-		length += sprintf (buf, "C%s", compat);
-		buf += length;
+		l = sprintf (buf, "C%s", compat);
+		length += l;
+		buf += l;
 		l = strlen (compat) + 1;
 		compat += l;
 		cplen -= l;
 	}
+	length += sprintf(buf, "\n");
 
 	return length;
 }
diff --git a/drivers/macintosh/rack-meter.c b/drivers/macintosh/rack-meter.c
index f83fad2..4177ff0 100644
--- a/drivers/macintosh/rack-meter.c
+++ b/drivers/macintosh/rack-meter.c
@@ -387,7 +387,7 @@
 	       if (strcmp(np->name, "lightshow") == 0)
 		       break;
 	       if ((strcmp(np->name, "sound") == 0) &&
-		   get_property(np, "virtual", NULL) != NULL)
+		   of_get_property(np, "virtual", NULL) != NULL)
 		       break;
 	}
 	if (np == NULL) {
diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c
index c9f3dc4..a98a328 100644
--- a/drivers/macintosh/smu.c
+++ b/drivers/macintosh/smu.c
@@ -491,7 +491,7 @@
 		printk(KERN_ERR "SMU: Can't find doorbell GPIO !\n");
 		goto fail;
 	}
-	data = get_property(smu->db_node, "reg", NULL);
+	data = of_get_property(smu->db_node, "reg", NULL);
 	if (data == NULL) {
 		of_node_put(smu->db_node);
 		smu->db_node = NULL;
@@ -512,7 +512,7 @@
 		smu->msg_node = of_find_node_by_name(NULL, "smu-interrupt");
 		if (smu->msg_node == NULL)
 			break;
-		data = get_property(smu->msg_node, "reg", NULL);
+		data = of_get_property(smu->msg_node, "reg", NULL);
 		if (data == NULL) {
 			of_node_put(smu->msg_node);
 			smu->msg_node = NULL;
@@ -952,7 +952,7 @@
 	prop->name = ((char *)prop) + tlen - 18;
 	sprintf(prop->name, "sdb-partition-%02x", id);
 	prop->length = len;
-	prop->value = (unsigned char *)hdr;
+	prop->value = hdr;
 	prop->next = NULL;
 
 	/* Read the datablock */
@@ -1004,7 +1004,7 @@
 	} else
 		mutex_lock(&smu_part_access);
 
-	part = get_property(smu->of_node, pname, size);
+	part = of_get_property(smu->of_node, pname, size);
 	if (part == NULL) {
 		DPRINTK("trying to extract from SMU ...\n");
 		part = smu_create_sdb_partition(id);
diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c
index a7ce5592..2289034 100644
--- a/drivers/macintosh/therm_adt746x.c
+++ b/drivers/macintosh/therm_adt746x.c
@@ -567,13 +567,13 @@
 	else
 		return -ENODEV;
 
-	prop = get_property(np, "hwsensor-params-version", NULL);
+	prop = of_get_property(np, "hwsensor-params-version", NULL);
 	printk(KERN_INFO "adt746x: version %d (%ssupported)\n", *prop,
 			 (*prop == 1)?"":"un");
 	if (*prop != 1)
 		return -ENODEV;
 
-	prop = get_property(np, "reg", NULL);
+	prop = of_get_property(np, "reg", NULL);
 	if (!prop)
 		return -ENODEV;
 
@@ -591,9 +591,9 @@
 			 "limit_adjust: %d, fan_speed: %d\n",
 			 therm_bus, therm_address, limit_adjust, fan_speed);
 
-	if (get_property(np, "hwsensor-location", NULL)) {
+	if (of_get_property(np, "hwsensor-location", NULL)) {
 		for (i = 0; i < 3; i++) {
-			sensor_location[i] = get_property(np,
+			sensor_location[i] = of_get_property(np,
 					"hwsensor-location", NULL) + offset;
 
 			if (sensor_location[i] == NULL)
diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c
index 2e4ad44..78ff186 100644
--- a/drivers/macintosh/therm_pm72.c
+++ b/drivers/macintosh/therm_pm72.c
@@ -674,7 +674,7 @@
 		printk(KERN_ERR "therm_pm72: Failed to retrieve cpuid node from device-tree\n");
 		return -ENODEV;
 	}
-	data = get_property(np, "cpuid", &len);
+	data = of_get_property(np, "cpuid", &len);
 	if (data == NULL) {
 		printk(KERN_ERR "therm_pm72: Failed to retrieve cpuid property from device-tree\n");
 		of_node_put(np);
@@ -1337,7 +1337,7 @@
 	 */
 	u3 = of_find_node_by_path("/u3@0,f8000000");
 	if (u3 != NULL) {
-		const u32 *vers = get_property(u3, "device-rev", NULL);
+		const u32 *vers = of_get_property(u3, "device-rev", NULL);
 		if (vers)
 			if (((*vers) & 0x3f) < 0x34)
 				u3h = 0;
@@ -2129,8 +2129,8 @@
 			continue;
 
 		/* Lookup for a matching location */
-		loc = get_property(np, "location", NULL);
-		reg = get_property(np, "reg", NULL);
+		loc = of_get_property(np, "location", NULL);
+		reg = of_get_property(np, "reg", NULL);
 		if (loc == NULL || reg == NULL)
 			continue;
 		DBG(" matching location: %s, reg: 0x%08x\n", loc, *reg);
diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c
index a1d3a98..35233de 100644
--- a/drivers/macintosh/therm_windtunnel.c
+++ b/drivers/macintosh/therm_windtunnel.c
@@ -492,7 +492,7 @@
 
 	if( !(np=of_find_node_by_name(NULL, "power-mgt")) )
 		return -ENODEV;
-	info = get_property(np, "thermal-info", NULL);
+	info = of_get_property(np, "thermal-info", NULL);
 	of_node_put(np);
 
 	if( !info || !machine_is_compatible("PowerMac3,6") )
diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c
index d58fcf6..76d2177 100644
--- a/drivers/macintosh/via-cuda.c
+++ b/drivers/macintosh/via-cuda.c
@@ -131,7 +131,7 @@
     if (vias == 0)
 	return 0;
 
-    reg = get_property(vias, "reg", NULL);
+    reg = of_get_property(vias, "reg", NULL);
     if (reg == NULL) {
 	    printk(KERN_ERR "via-cuda: No \"reg\" property !\n");
 	    goto fail;
diff --git a/drivers/macintosh/via-pmu-led.c b/drivers/macintosh/via-pmu-led.c
index 179af10..fc89a70 100644
--- a/drivers/macintosh/via-pmu-led.c
+++ b/drivers/macintosh/via-pmu-led.c
@@ -81,7 +81,7 @@
 };
 
 #ifdef CONFIG_PM
-static int pmu_led_sleep_call(struct pmu_sleep_notifier *self, int when)
+static void pmu_led_sleep_call(struct pmu_sleep_notifier *self, int when)
 {
 	unsigned long flags;
 
@@ -99,8 +99,6 @@
 		break;
 	}
 	spin_unlock_irqrestore(&pmu_blink_lock, flags);
-
-	return PBOOK_SLEEP_OK;
 }
 
 static struct pmu_sleep_notifier via_pmu_led_sleep_notif = {
@@ -120,11 +118,13 @@
 	dt = of_find_node_by_path("/");
 	if (dt == NULL)
 		return -ENODEV;
-	model = get_property(dt, "model", NULL);
+	model = of_get_property(dt, "model", NULL);
 	if (model == NULL)
 		return -ENODEV;
 	if (strncmp(model, "PowerBook", strlen("PowerBook")) != 0 &&
-	    strncmp(model, "iBook", strlen("iBook")) != 0) {
+	    strncmp(model, "iBook", strlen("iBook")) != 0 &&
+	    strcmp(model, "PowerMac7,2") != 0 &&
+	    strcmp(model, "PowerMac7,3") != 0) {
 		of_node_put(dt);
 		/* ignore */
 		return -ENODEV;
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
index b6073bd..1729d3f 100644
--- a/drivers/macintosh/via-pmu.c
+++ b/drivers/macintosh/via-pmu.c
@@ -289,7 +289,7 @@
 	if (vias == NULL)
 		return 0;
 
-	reg = get_property(vias, "reg", NULL);
+	reg = of_get_property(vias, "reg", NULL);
 	if (reg == NULL) {
 		printk(KERN_ERR "via-pmu: No \"reg\" property !\n");
 		goto fail;
@@ -319,10 +319,13 @@
 	else if (device_is_compatible(vias->parent, "Keylargo")
 		 || device_is_compatible(vias->parent, "K2-Keylargo")) {
 		struct device_node *gpiop;
+		struct device_node *adbp;
 		u64 gaddr = OF_BAD_ADDR;
 
 		pmu_kind = PMU_KEYLARGO_BASED;
-		pmu_has_adb = (find_type_devices("adb") != NULL);
+		adbp = of_find_node_by_type(NULL, "adb");
+		pmu_has_adb = (adbp != NULL);
+		of_node_put(adbp);
 		pmu_intr_mask =	PMU_INT_PCEJECT |
 				PMU_INT_SNDBRT |
 				PMU_INT_ADB |
@@ -331,7 +334,7 @@
 		
 		gpiop = of_find_node_by_name(NULL, "gpio");
 		if (gpiop) {
-			reg = get_property(gpiop, "reg", NULL);
+			reg = of_get_property(gpiop, "reg", NULL);
 			if (reg)
 				gaddr = of_translate_address(gpiop, reg);
 			if (gaddr != OF_BAD_ADDR)
@@ -484,10 +487,11 @@
 		pmu_batteries[0].flags |= PMU_BATT_TYPE_SMART;
 		pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART;
 	} else {
-		struct device_node* prim = find_devices("power-mgt");
+		struct device_node* prim =
+			of_find_node_by_name(NULL, "power-mgt");
 		const u32 *prim_info = NULL;
 		if (prim)
-			prim_info = get_property(prim, "prim-info", NULL);
+			prim_info = of_get_property(prim, "prim-info", NULL);
 		if (prim_info) {
 			/* Other stuffs here yet unknown */
 			pmu_battery_count = (prim_info[6] >> 16) & 0xff;
@@ -495,6 +499,7 @@
 			if (pmu_battery_count > 1)
 				pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART;
 		}
+		of_node_put(prim);
 	}
 #endif /* CONFIG_PPC32 */
 
@@ -1769,35 +1774,21 @@
 #if defined(CONFIG_PM) && defined(CONFIG_PPC32)
 
 /* Sleep is broadcast last-to-first */
-static int
-broadcast_sleep(int when, int fallback)
+static void broadcast_sleep(int when)
 {
-	int ret = PBOOK_SLEEP_OK;
 	struct list_head *list;
 	struct pmu_sleep_notifier *notifier;
 
 	for (list = sleep_notifiers.prev; list != &sleep_notifiers;
 	     list = list->prev) {
 		notifier = list_entry(list, struct pmu_sleep_notifier, list);
-		ret = notifier->notifier_call(notifier, when);
-		if (ret != PBOOK_SLEEP_OK) {
-			printk(KERN_DEBUG "sleep %d rejected by %p (%p)\n",
-			       when, notifier, notifier->notifier_call);
-			for (; list != &sleep_notifiers; list = list->next) {
-				notifier = list_entry(list, struct pmu_sleep_notifier, list);
-				notifier->notifier_call(notifier, fallback);
-			}
-			return ret;
-		}
+		notifier->notifier_call(notifier, when);
 	}
-	return ret;
 }
 
 /* Wake is broadcast first-to-last */
-static int
-broadcast_wake(void)
+static void broadcast_wake(void)
 {
-	int ret = PBOOK_SLEEP_OK;
 	struct list_head *list;
 	struct pmu_sleep_notifier *notifier;
 
@@ -1806,7 +1797,6 @@
 		notifier = list_entry(list, struct pmu_sleep_notifier, list);
 		notifier->notifier_call(notifier, PBOOK_WAKE);
 	}
-	return ret;
 }
 
 /*
@@ -2013,12 +2003,8 @@
 
 	pm_prepare_console();
 	
-	/* Notify old-style device drivers & userland */
-	ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, PBOOK_SLEEP_REJECT);
-	if (ret != PBOOK_SLEEP_OK) {
-		printk(KERN_ERR "Sleep rejected by drivers\n");
-		return -EBUSY;
-	}
+	/* Notify old-style device drivers */
+	broadcast_sleep(PBOOK_SLEEP_REQUEST);
 
 	/* Sync the disks. */
 	/* XXX It would be nice to have some way to ensure that
@@ -2028,12 +2014,7 @@
 	 */
 	sys_sync();
 
-	/* Sleep can fail now. May not be very robust but useful for debugging */
-	ret = broadcast_sleep(PBOOK_SLEEP_NOW, PBOOK_WAKE);
-	if (ret != PBOOK_SLEEP_OK) {
-		printk(KERN_ERR "Driver sleep failed\n");
-		return -EBUSY;
-	}
+	broadcast_sleep(PBOOK_SLEEP_NOW);
 
 	/* Send suspend call to devices, hold the device core's dpm_sem */
 	ret = device_suspend(PMSG_SUSPEND);
@@ -2154,7 +2135,7 @@
 	int ret;
 	struct pci_dev *grackle;
 
-	grackle = pci_find_slot(0, 0);
+	grackle = pci_get_bus_and_slot(0, 0);
 	if (!grackle)
 		return -ENODEV;
 
@@ -2202,6 +2183,8 @@
 	pmcr1 &= ~(GRACKLE_PM|GRACKLE_DOZE|GRACKLE_SLEEP|GRACKLE_NAP); 
 	pci_write_config_word(grackle, 0x70, pmcr1);
 
+	pci_dev_put(grackle);
+
 	/* Make sure the PMU is idle */
 	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
 	restore_via_state();
diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c
index 3f7967f..ab4d1b6 100644
--- a/drivers/macintosh/windfarm_lm75_sensor.c
+++ b/drivers/macintosh/windfarm_lm75_sensor.c
@@ -176,7 +176,7 @@
 	for (dev = NULL;
 	     (dev = of_get_next_child(busnode, dev)) != NULL;) {
 		const char *loc =
-			get_property(dev, "hwsensor-location", NULL);
+			of_get_property(dev, "hwsensor-location", NULL);
 		u8 addr;
 
 		/* We must re-match the adapter in order to properly check
diff --git a/drivers/macintosh/windfarm_max6690_sensor.c b/drivers/macintosh/windfarm_max6690_sensor.c
index eae1189..eaa74af 100644
--- a/drivers/macintosh/windfarm_max6690_sensor.c
+++ b/drivers/macintosh/windfarm_max6690_sensor.c
@@ -134,7 +134,7 @@
 		if (!device_is_compatible(dev, "max6690"))
 			continue;
 		addr = pmac_i2c_get_dev_addr(dev);
-		loc = get_property(dev, "hwsensor-location", NULL);
+		loc = of_get_property(dev, "hwsensor-location", NULL);
 		if (loc == NULL || addr == 0)
 			continue;
 		printk("found max6690, loc=%s addr=0x%02x\n", loc, addr);
diff --git a/drivers/macintosh/windfarm_smu_controls.c b/drivers/macintosh/windfarm_smu_controls.c
index 31b750d..ff398ad 100644
--- a/drivers/macintosh/windfarm_smu_controls.c
+++ b/drivers/macintosh/windfarm_smu_controls.c
@@ -167,7 +167,7 @@
 	if (fct == NULL)
 		return NULL;
 	fct->ctrl.ops = &smu_fan_ops;
-	l = get_property(node, "location", NULL);
+	l = of_get_property(node, "location", NULL);
 	if (l == NULL)
 		goto fail;
 
@@ -224,17 +224,17 @@
 		goto fail;
 
 	/* Get min & max values*/
-	v = get_property(node, "min-value", NULL);
+	v = of_get_property(node, "min-value", NULL);
 	if (v == NULL)
 		goto fail;
 	fct->min = *v;
-	v = get_property(node, "max-value", NULL);
+	v = of_get_property(node, "max-value", NULL);
 	if (v == NULL)
 		goto fail;
 	fct->max = *v;
 
 	/* Get "reg" value */
-	reg = get_property(node, "reg", NULL);
+	reg = of_get_property(node, "reg", NULL);
 	if (reg == NULL)
 		goto fail;
 	fct->reg = *reg;
diff --git a/drivers/macintosh/windfarm_smu_sat.c b/drivers/macintosh/windfarm_smu_sat.c
index 83f79de..9a6c2cf 100644
--- a/drivers/macintosh/windfarm_smu_sat.c
+++ b/drivers/macintosh/windfarm_smu_sat.c
@@ -241,7 +241,7 @@
 	char *name;
 	int vsens[2], isens[2];
 
-	reg = get_property(dev, "reg", NULL);
+	reg = of_get_property(dev, "reg", NULL);
 	if (reg == NULL)
 		return;
 	addr = *reg;
@@ -268,9 +268,9 @@
 	isens[0] = isens[1] = -1;
 	child = NULL;
 	while ((child = of_get_next_child(dev, child)) != NULL) {
-		reg = get_property(child, "reg", NULL);
-		type = get_property(child, "device_type", NULL);
-		loc = get_property(child, "location", NULL);
+		reg = of_get_property(child, "reg", NULL);
+		type = of_get_property(child, "device_type", NULL);
+		loc = of_get_property(child, "location", NULL);
 		if (reg == NULL || loc == NULL)
 			continue;
 
diff --git a/drivers/macintosh/windfarm_smu_sensors.c b/drivers/macintosh/windfarm_smu_sensors.c
index 01b4c50..9c567b9 100644
--- a/drivers/macintosh/windfarm_smu_sensors.c
+++ b/drivers/macintosh/windfarm_smu_sensors.c
@@ -204,8 +204,8 @@
 	ads = kmalloc(sizeof(struct smu_ad_sensor), GFP_KERNEL);
 	if (ads == NULL)
 		return NULL;
-	c = get_property(node, "device_type", NULL);
-	l = get_property(node, "location", NULL);
+	c = of_get_property(node, "device_type", NULL);
+	l = of_get_property(node, "location", NULL);
 	if (c == NULL || l == NULL)
 		goto fail;
 
@@ -255,7 +255,7 @@
 	} else
 		goto fail;
 
-	v = get_property(node, "reg", NULL);
+	v = of_get_property(node, "reg", NULL);
 	if (v == NULL)
 		goto fail;
 	ads->reg = *v;
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 4c2471e..d812123 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -867,7 +867,7 @@
 		goto bad4;
 	}
 
-	cc->bs = bioset_create(MIN_IOS, MIN_IOS, 4);
+	cc->bs = bioset_create(MIN_IOS, MIN_IOS);
 	if (!cc->bs) {
 		ti->error = "Cannot allocate crypt bioset";
 		goto bad_bs;
diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c
index 4eb73d3..8bdc8a8 100644
--- a/drivers/md/dm-io.c
+++ b/drivers/md/dm-io.c
@@ -60,7 +60,7 @@
 		if (!_io_pool)
 			return -ENOMEM;
 
-		_bios = bioset_create(16, 16, 4);
+		_bios = bioset_create(16, 16);
 		if (!_bios) {
 			mempool_destroy(_io_pool);
 			_io_pool = NULL;
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 3668b17..11a98df 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1012,7 +1012,7 @@
 	if (!md->tio_pool)
 		goto bad3;
 
-	md->bs = bioset_create(16, 16, 4);
+	md->bs = bioset_create(16, 16);
 	if (!md->bs)
 		goto bad_no_bioset;
 
diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c
index 03b47a2..cbd1184 100644
--- a/drivers/media/common/ir-keymaps.c
+++ b/drivers/media/common/ir-keymaps.c
@@ -667,7 +667,7 @@
 	[ 0x1f ] = KEY_L,
 	[ 0x2b ] = KEY_I,
 
-	[ 0x2d ] = KEY_ZOOM,
+	[ 0x2d ] = KEY_SCREEN,
 	[ 0x1e ] = KEY_ZOOM,
 	[ 0x1b ] = KEY_VOLUMEUP,
 	[ 0x0f ] = KEY_VOLUMEDOWN,
@@ -682,12 +682,12 @@
 
 	[ 0x3f ] = KEY_UP,
 	[ 0x3e ] = KEY_DOWN,
-	[ 0x1a ] = KEY_PAUSE,
+	[ 0x1a ] = KEY_ENTER,
 
 	[ 0x1d ] = KEY_MENU,
-	[ 0x19 ] = KEY_PLAY,
-	[ 0x16 ] = KEY_REWIND,
-	[ 0x13 ] = KEY_FORWARD,
+	[ 0x19 ] = KEY_AGAIN,
+	[ 0x16 ] = KEY_PREVIOUSSONG,
+	[ 0x13 ] = KEY_NEXTSONG,
 	[ 0x15 ] = KEY_PAUSE,
 	[ 0x0e ] = KEY_REWIND,
 	[ 0x0d ] = KEY_PLAY,
@@ -1739,7 +1739,7 @@
 
 EXPORT_SYMBOL_GPL(ir_codes_encore_enltv);
 
-/* for the Technotrend 1500 bundled remote: */
+/* for the Technotrend 1500 bundled remotes (grey and black): */
 IR_KEYTAB_TYPE ir_codes_tt_1500[IR_KEYTAB_SIZE] = {
 	[ 0x01 ] = KEY_POWER,
 	[ 0x02 ] = KEY_SHUFFLE,	/* ? double-arrow key */
@@ -1774,6 +1774,12 @@
 	[ 0x25 ] = KEY_VOLUMEUP,
 	[ 0x26 ] = KEY_VOLUMEDOWN,
 	[ 0x27 ] = KEY_SETUP,
+	[ 0x3a ] = KEY_RECORD, /* these keys are only in the black remote */
+	[ 0x3b ] = KEY_PLAY,
+	[ 0x3c ] = KEY_STOP,
+	[ 0x3d ] = KEY_REWIND,
+	[ 0x3e ] = KEY_PAUSE,
+	[ 0x3f ] = KEY_FORWARD,
 };
 
 EXPORT_SYMBOL_GPL(ir_codes_tt_1500);
diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c
index 7e0cedc5..e3d04a4 100644
--- a/drivers/media/common/saa7146_video.c
+++ b/drivers/media/common/saa7146_video.c
@@ -1428,6 +1428,7 @@
 {
 	struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data;
 	struct saa7146_vv *vv = dev->vv_data;
+	struct videobuf_queue *q = &fh->video_q;
 	int err;
 
 	if (IS_CAPTURE_ACTIVE(fh) != 0) {
@@ -1436,6 +1437,11 @@
 		err = saa7146_stop_preview(fh);
 	}
 
+	// release all capture buffers
+	mutex_lock(&q->lock);
+	videobuf_read_stop(q);
+	mutex_unlock(&q->lock);
+
 	/* hmm, why is this function declared void? */
 	/* return err */
 }
diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig
index 7987595..a0dcd59 100644
--- a/drivers/media/dvb/b2c2/Kconfig
+++ b/drivers/media/dvb/b2c2/Kconfig
@@ -9,7 +9,6 @@
 	select DVB_STV0297 if !DVB_FE_CUSTOMISE
 	select DVB_BCM3510 if !DVB_FE_CUSTOMISE
 	select DVB_LGDT330X if !DVB_FE_CUSTOMISE
-	select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE
 	help
 	  Support for the digital TV receiver chip made by B2C2 Inc. included in
 	  Technisats PCI cards and USB boxes.
diff --git a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
index 752cf79..b02c2fd 100644
--- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
+++ b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
@@ -14,7 +14,6 @@
 #include "stv0297.h"
 #include "mt312.h"
 #include "lgdt330x.h"
-#include "lgh06xf.h"
 #include "dvb-pll.h"
 
 /* lnb control */
@@ -507,7 +506,7 @@
 	/* try the air atsc 3nd generation (lgdt3303) */
 	if ((fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, &fc->i2c_adap)) != NULL) {
 		fc->dev_type          = FC_AIR_ATSC3;
-		dvb_attach(lgh06xf_attach, fc->fe, &fc->i2c_adap);
+		dvb_attach(dvb_pll_attach, fc->fe, 0x61, &fc->i2c_adap, &dvb_pll_lg_tdvs_h06xf);
 		info("found the lgdt3303 at i2c address: 0x%02x",air2pc_atsc_hd5000_config.demod_address);
 	} else
 	/* try the air atsc 1nd generation (bcm3510)/panasonic ct10s */
diff --git a/drivers/media/dvb/b2c2/flexcop-pci.c b/drivers/media/dvb/b2c2/flexcop-pci.c
index 6e16680..01af4d2 100644
--- a/drivers/media/dvb/b2c2/flexcop-pci.c
+++ b/drivers/media/dvb/b2c2/flexcop-pci.c
@@ -127,10 +127,11 @@
 {
 	struct flexcop_pci *fc_pci = dev_id;
 	struct flexcop_device *fc = fc_pci->fc_dev;
+	unsigned long flags;
 	flexcop_ibi_value v;
 	irqreturn_t ret = IRQ_HANDLED;
 
-	spin_lock_irq(&fc_pci->irq_lock);
+	spin_lock_irqsave(&fc_pci->irq_lock,flags);
 
 	v = fc->read_ibi_reg(fc,irq_20c);
 
@@ -194,7 +195,7 @@
 		ret = IRQ_NONE;
 	}
 
-	spin_unlock_irq(&fc_pci->irq_lock);
+	spin_unlock_irqrestore(&fc_pci->irq_lock,flags);
 
 	return ret;
 }
@@ -293,12 +294,12 @@
 	}
 
 	pci_set_drvdata(fc_pci->pdev, fc_pci);
-
+	spin_lock_init(&fc_pci->irq_lock);
 	if ((ret = request_irq(fc_pci->pdev->irq, flexcop_pci_isr,
 					IRQF_SHARED, DRIVER_NAME, fc_pci)) != 0)
 		goto err_pci_iounmap;
 
-	spin_lock_init(&fc_pci->irq_lock);
+
 
 	fc_pci->init_state |= FC_PCI_INIT;
 	return ret;
diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig
index dd66b60..cfd6fb7 100644
--- a/drivers/media/dvb/bt8xx/Kconfig
+++ b/drivers/media/dvb/bt8xx/Kconfig
@@ -7,7 +7,7 @@
 	select DVB_CX24110 if !DVB_FE_CUSTOMISE
 	select DVB_OR51211 if !DVB_FE_CUSTOMISE
 	select DVB_LGDT330X if !DVB_FE_CUSTOMISE
-	select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE
+	select DVB_PLL
 	select DVB_ZL10353 if !DVB_FE_CUSTOMISE
 	select FW_LOADER
 	help
diff --git a/drivers/media/dvb/bt8xx/bt878.c b/drivers/media/dvb/bt8xx/bt878.c
index 83b090e..df72b4b 100644
--- a/drivers/media/dvb/bt8xx/bt878.c
+++ b/drivers/media/dvb/bt8xx/bt878.c
@@ -393,9 +393,7 @@
 	{ 0xdb1118ac, BTTV_BOARD_DVICO_DVBT_LITE,		"Ultraview DVB-T Lite" },
 	{ 0xd50018ac, BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE,	"DViCO FusionHDTV 5 Lite" },
 	{ 0x20007063, BTTV_BOARD_PC_HDTV,			"pcHDTV HD-2000 TV" },
-	{ 0x00261822, BTTV_BOARD_TWINHAN_DST,			"DNTV Live! Mini" },
-
-	{ 0, -1, NULL }
+	{ 0x00261822, BTTV_BOARD_TWINHAN_DST,			"DNTV Live! Mini" }
 };
 
 
diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
index 58f69f6..4f1c09b 100644
--- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c
+++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
@@ -610,7 +610,8 @@
 		lgdt330x_reset(card);
 		card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter);
 		if (card->fe != NULL) {
-			dvb_attach(lgh06xf_attach, card->fe, card->i2c_adapter);
+			dvb_attach(dvb_pll_attach, card->fe, 0x61,
+				   card->i2c_adapter, &dvb_pll_lg_tdvs_h06xf);
 			dprintk ("dvb_bt8xx: lgdt330x detected\n");
 		}
 		break;
diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.h b/drivers/media/dvb/bt8xx/dvb-bt8xx.h
index e75f417..436880e 100644
--- a/drivers/media/dvb/bt8xx/dvb-bt8xx.h
+++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.h
@@ -37,8 +37,8 @@
 #include "cx24110.h"
 #include "or51211.h"
 #include "lgdt330x.h"
-#include "lgh06xf.h"
 #include "zl10353.h"
+#include "dvb-pll.h"
 
 struct dvb_bt8xx_card {
 	struct mutex lock;
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index a5c0e1a..275df65 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -132,6 +132,11 @@
 	if (mutex_lock_interruptible(&dmxdev->mutex))
 		return -ERESTARTSYS;
 
+	if (dmxdev->exit) {
+		mutex_unlock(&dmxdev->mutex);
+		return -ENODEV;
+	}
+
 	if ((file->f_flags & O_ACCMODE) == O_RDWR) {
 		if (!(dmxdev->capabilities & DMXDEV_CAP_DUPLEX)) {
 			mutex_unlock(&dmxdev->mutex);
@@ -171,6 +176,7 @@
 		dmxdev->demux->disconnect_frontend(dmxdev->demux);
 		dmxdev->demux->connect_frontend(dmxdev->demux, front);
 	}
+	dvbdev->users++;
 	mutex_unlock(&dmxdev->mutex);
 	return 0;
 }
@@ -198,7 +204,16 @@
 			vfree(mem);
 		}
 	}
-	mutex_unlock(&dmxdev->mutex);
+	/* TODO */
+	dvbdev->users--;
+	if(dvbdev->users==-1 && dmxdev->exit==1) {
+		fops_put(file->f_op);
+		file->f_op = NULL;
+		mutex_unlock(&dmxdev->mutex);
+		wake_up(&dvbdev->wait_queue);
+	} else
+		mutex_unlock(&dmxdev->mutex);
+
 	return 0;
 }
 
@@ -215,6 +230,11 @@
 		return -EINVAL;
 	if (mutex_lock_interruptible(&dmxdev->mutex))
 		return -ERESTARTSYS;
+
+	if (dmxdev->exit) {
+		mutex_unlock(&dmxdev->mutex);
+		return -ENODEV;
+	}
 	ret = dmxdev->demux->write(dmxdev->demux, buf, count);
 	mutex_unlock(&dmxdev->mutex);
 	return ret;
@@ -227,6 +247,11 @@
 	struct dmxdev *dmxdev = dvbdev->priv;
 	int ret;
 
+	if (dmxdev->exit) {
+		mutex_unlock(&dmxdev->mutex);
+		return -ENODEV;
+	}
+
 	//mutex_lock(&dmxdev->mutex);
 	ret = dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer,
 				     file->f_flags & O_NONBLOCK,
@@ -665,6 +690,8 @@
 	dmxdevfilter->feed.ts = NULL;
 	init_timer(&dmxdevfilter->timer);
 
+	dvbdev->users++;
+
 	mutex_unlock(&dmxdev->mutex);
 	return 0;
 }
@@ -943,7 +970,21 @@
 	struct dmxdev_filter *dmxdevfilter = file->private_data;
 	struct dmxdev *dmxdev = dmxdevfilter->dev;
 
-	return dvb_dmxdev_filter_free(dmxdev, dmxdevfilter);
+	int ret;
+
+	ret = dvb_dmxdev_filter_free(dmxdev, dmxdevfilter);
+
+	mutex_lock(&dmxdev->mutex);
+	dmxdev->dvbdev->users--;
+	if(dmxdev->dvbdev->users==1 && dmxdev->exit==1) {
+		fops_put(file->f_op);
+		file->f_op = NULL;
+		mutex_unlock(&dmxdev->mutex);
+		wake_up(&dmxdev->dvbdev->wait_queue);
+	} else
+		mutex_unlock(&dmxdev->mutex);
+
+	return ret;
 }
 
 static struct file_operations dvb_demux_fops = {
@@ -1027,6 +1068,7 @@
 static struct dvb_device dvbdev_dvr = {
 	.priv = NULL,
 	.readers = 1,
+	.users = 1,
 	.fops = &dvb_dvr_fops
 };
 
@@ -1064,6 +1106,16 @@
 
 void dvb_dmxdev_release(struct dmxdev *dmxdev)
 {
+	dmxdev->exit=1;
+	if (dmxdev->dvbdev->users > 1) {
+		wait_event(dmxdev->dvbdev->wait_queue,
+				dmxdev->dvbdev->users==1);
+	}
+	if (dmxdev->dvr_dvbdev->users > 1) {
+		wait_event(dmxdev->dvr_dvbdev->wait_queue,
+				dmxdev->dvr_dvbdev->users==1);
+	}
+
 	dvb_unregister_device(dmxdev->dvbdev);
 	dvb_unregister_device(dmxdev->dvr_dvbdev);
 
diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h
index d2bee9f..29746e7 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.h
+++ b/drivers/media/dvb/dvb-core/dmxdev.h
@@ -91,6 +91,8 @@
 
 	int filternum;
 	int capabilities;
+
+	unsigned int exit:1;
 #define DMXDEV_CAP_DUPLEX 1
 	struct dmx_frontend *dvr_orig_fe;
 
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
index a21a894..f4e4ca2 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -606,6 +606,7 @@
 		return;
 
 	kthread_stop(fepriv->thread);
+
 	init_MUTEX (&fepriv->sem);
 	fepriv->state = FESTATE_IDLE;
 
@@ -1023,6 +1024,7 @@
 	struct dvb_device *dvbdev = file->private_data;
 	struct dvb_frontend *fe = dvbdev->priv;
 	struct dvb_frontend_private *fepriv = fe->frontend_priv;
+	int ret;
 
 	dprintk ("%s\n", __FUNCTION__);
 
@@ -1032,7 +1034,14 @@
 	if (fe->ops.ts_bus_ctrl)
 		fe->ops.ts_bus_ctrl (fe, 0);
 
-	return dvb_generic_release (inode, file);
+	ret = dvb_generic_release (inode, file);
+
+	if (dvbdev->users==-1 && fepriv->exit==1) {
+		fops_put(file->f_op);
+		file->f_op = NULL;
+		wake_up(&dvbdev->wait_queue);
+	}
+	return ret;
 }
 
 static struct file_operations dvb_frontend_fops = {
@@ -1092,8 +1101,15 @@
 	dprintk ("%s\n", __FUNCTION__);
 
 	mutex_lock(&frontend_mutex);
-	dvb_unregister_device (fepriv->dvbdev);
 	dvb_frontend_stop (fe);
+	mutex_unlock(&frontend_mutex);
+
+	if (fepriv->dvbdev->users < -1)
+		wait_event(fepriv->dvbdev->wait_queue,
+				fepriv->dvbdev->users==-1);
+
+	mutex_lock(&frontend_mutex);
+	dvb_unregister_device (fepriv->dvbdev);
 
 	/* fe is invalid now */
 	kfree(fepriv);
diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c
index 76e9c36..4ebf33a5 100644
--- a/drivers/media/dvb/dvb-core/dvb_net.c
+++ b/drivers/media/dvb/dvb-core/dvb_net.c
@@ -174,7 +174,7 @@
 	struct ethhdr *eth;
 	unsigned char *rawp;
 
-	skb->mac.raw=skb->data;
+	skb_reset_mac_header(skb);
 	skb_pull(skb,dev->hard_header_len);
 	eth = eth_hdr(skb);
 
@@ -600,6 +600,7 @@
 			/* Check CRC32, we've got it in our skb already. */
 			unsigned short ulen = htons(priv->ule_sndu_len);
 			unsigned short utype = htons(priv->ule_sndu_type);
+			const u8 *tail;
 			struct kvec iov[3] = {
 				{ &ulen, sizeof ulen },
 				{ &utype, sizeof utype },
@@ -613,10 +614,11 @@
 			}
 
 			ule_crc = iov_crc32(ule_crc, iov, 3);
-			expected_crc = *((u8 *)priv->ule_skb->tail - 4) << 24 |
-				       *((u8 *)priv->ule_skb->tail - 3) << 16 |
-				       *((u8 *)priv->ule_skb->tail - 2) << 8 |
-				       *((u8 *)priv->ule_skb->tail - 1);
+			tail = skb_tail_pointer(priv->ule_skb);
+			expected_crc = *(tail - 4) << 24 |
+				       *(tail - 3) << 16 |
+				       *(tail - 2) << 8 |
+				       *(tail - 1);
 			if (ule_crc != expected_crc) {
 				printk(KERN_WARNING "%lu: CRC32 check FAILED: %08x / %08x, SNDU len %d type %#x, ts_remain %d, next 2: %x.\n",
 				       priv->ts_count, ule_crc, expected_crc, priv->ule_sndu_len, priv->ule_sndu_type, ts_remain, ts_remain > 2 ? *(unsigned short *)from_where : 0);
@@ -695,7 +697,9 @@
 					}
 					else
 					{
-						memcpy(dest_addr,  priv->ule_skb->data, ETH_ALEN);
+						skb_copy_from_linear_data(priv->ule_skb,
+							      dest_addr,
+							      ETH_ALEN);
 						skb_pull(priv->ule_skb, ETH_ALEN);
 					}
 				}
@@ -1435,11 +1439,36 @@
 	return dvb_usercopy(inode, file, cmd, arg, dvb_net_do_ioctl);
 }
 
+static int dvb_net_close(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct dvb_net *dvbnet = dvbdev->priv;
+
+	if (!dvbdev)
+		return -ENODEV;
+
+	if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+		dvbdev->readers++;
+	} else {
+		dvbdev->writers++;
+	}
+
+	dvbdev->users++;
+
+	if(dvbdev->users == 1 && dvbnet->exit==1) {
+		fops_put(file->f_op);
+		file->f_op = NULL;
+		wake_up(&dvbdev->wait_queue);
+	}
+	return 0;
+}
+
+
 static struct file_operations dvb_net_fops = {
 	.owner = THIS_MODULE,
 	.ioctl = dvb_net_ioctl,
 	.open =	dvb_generic_open,
-	.release = dvb_generic_release,
+	.release = dvb_net_close,
 };
 
 static struct dvb_device dvbdev_net = {
@@ -1454,6 +1483,11 @@
 {
 	int i;
 
+	dvbnet->exit = 1;
+	if (dvbnet->dvbdev->users < 1)
+		wait_event(dvbnet->dvbdev->wait_queue,
+				dvbnet->dvbdev->users==1);
+
 	dvb_unregister_device(dvbnet->dvbdev);
 
 	for (i=0; i<DVB_NET_DEVICES_MAX; i++) {
diff --git a/drivers/media/dvb/dvb-core/dvb_net.h b/drivers/media/dvb/dvb-core/dvb_net.h
index f14e4ca..3a3126ca 100644
--- a/drivers/media/dvb/dvb-core/dvb_net.h
+++ b/drivers/media/dvb/dvb-core/dvb_net.h
@@ -36,6 +36,7 @@
 	struct dvb_device *dvbdev;
 	struct net_device *device[DVB_NET_DEVICES_MAX];
 	int state[DVB_NET_DEVICES_MAX];
+	unsigned int exit:1;
 	struct dmx_demux *demux;
 };
 
diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c
index 14a372a..e23d8a0 100644
--- a/drivers/media/dvb/dvb-core/dvbdev.c
+++ b/drivers/media/dvb/dvb-core/dvbdev.c
@@ -233,6 +233,7 @@
 	dvbdev->adapter = adap;
 	dvbdev->priv = priv;
 	dvbdev->fops = dvbdevfops;
+	init_waitqueue_head (&dvbdev->wait_queue);
 
 	memcpy(dvbdev->fops, template->fops, sizeof(struct file_operations));
 	dvbdev->fops->owner = adap->module;
diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h
index 620e788..6dff10e 100644
--- a/drivers/media/dvb/dvb-core/dvbdev.h
+++ b/drivers/media/dvb/dvb-core/dvbdev.h
@@ -69,6 +69,7 @@
 	int writers;
 	int users;
 
+	wait_queue_head_t	  wait_queue;
 	/* don't really need those !? -- FIXME: use video_usercopy  */
 	int (*kernel_ioctl)(struct inode *inode, struct file *file,
 			    unsigned int cmd, void *arg);
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index 80f67a5..5448873 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -33,6 +33,7 @@
 config DVB_USB_DIBUSB_MB
 	tristate "DiBcom USB DVB-T devices (based on the DiB3000M-B) (see help for device list)"
 	depends on DVB_USB
+	select DVB_PLL
 	select DVB_DIB3000MB
 	select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
 	help
@@ -88,6 +89,7 @@
 config DVB_USB_UMT_010
 	tristate "HanfTek UMT-010 DVB-T USB2.0 support"
 	depends on DVB_USB
+	select DVB_PLL
 	select DVB_DIB3000MC
 	select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
 	help
@@ -96,9 +98,9 @@
 config DVB_USB_CXUSB
 	tristate "Conexant USB2.0 hybrid reference design support"
 	depends on DVB_USB
+	select DVB_PLL
 	select DVB_CX22702 if !DVB_FE_CUSTOMISE
 	select DVB_LGDT330X if !DVB_FE_CUSTOMISE
-	select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE
 	select DVB_MT352 if !DVB_FE_CUSTOMISE
 	select DVB_ZL10353 if !DVB_FE_CUSTOMISE
 	help
@@ -140,6 +142,7 @@
 config DVB_USB_DIGITV
 	tristate "Nebula Electronics uDigiTV DVB-T USB2.0 support"
 	depends on DVB_USB
+	select DVB_PLL
 	select DVB_NXT6000 if !DVB_FE_CUSTOMISE
 	select DVB_MT352 if !DVB_FE_CUSTOMISE
 	help
@@ -208,3 +211,10 @@
 	  The receivers are also known as DTT200U (Yakumo) and UB300 (Yuan).
 
 	  The WT-220U and its clones are pen-sized.
+
+config DVB_USB_OPERA1
+	tristate "Opera1 DVB-S USB2.0 receiver"
+	depends on DVB_USB
+	select DVB_STV0299 if !DVB_FE_CUSTOMISE
+	help
+	  Say Y here to support the Opera DVB-S USB2.0 receiver.
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
index 40f28f5..976f840 100644
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ b/drivers/media/dvb/dvb-usb/Makefile
@@ -51,4 +51,8 @@
 dvb-usb-dib0700-objs = dib0700_core.o dib0700_devices.o
 obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb-dib0700.o
 
+dvb-usb-opera-objs = opera1.o
+obj-$(CONFIG_DVB_USB_OPERA1) += dvb-usb-opera.o
+
+
 EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
diff --git a/drivers/media/dvb/dvb-usb/au6610.c b/drivers/media/dvb/dvb-usb/au6610.c
index 0dc66a8..18e0b16 100644
--- a/drivers/media/dvb/dvb-usb/au6610.c
+++ b/drivers/media/dvb/dvb-usb/au6610.c
@@ -40,7 +40,7 @@
 	}
 
 	ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), operation,
-			      USB_TYPE_VENDOR|USB_DIR_IN, addr, index, usb_buf,
+			      USB_TYPE_VENDOR|USB_DIR_IN, addr << 1, index, usb_buf,
 			      sizeof(usb_buf), AU6610_USB_TIMEOUT);
 
 	if (ret < 0)
@@ -124,7 +124,7 @@
 }
 
 static struct zl10353_config au6610_zl10353_config = {
-	.demod_address = 0x1e,
+	.demod_address = 0x0f,
 	.no_tuner = 1,
 	.parallel_ts = 1,
 };
@@ -140,7 +140,7 @@
 }
 
 static struct qt1010_config au6610_qt1010_config = {
-	.i2c_address = 0xc4
+	.i2c_address = 0x62
 };
 
 static int au6610_qt1010_tuner_attach(struct dvb_usb_adapter *adap)
diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c
index 127a94b..bac2ae3 100644
--- a/drivers/media/dvb/dvb-usb/cxusb.c
+++ b/drivers/media/dvb/dvb-usb/cxusb.c
@@ -27,7 +27,6 @@
 
 #include "cx22702.h"
 #include "lgdt330x.h"
-#include "lgh06xf.h"
 #include "mt352.h"
 #include "mt352_priv.h"
 #include "zl10353.h"
@@ -388,7 +387,8 @@
 
 static int cxusb_lgh064f_tuner_attach(struct dvb_usb_adapter *adap)
 {
-	dvb_attach(lgh06xf_attach, adap->fe, &adap->dev->i2c_adap);
+	dvb_attach(dvb_pll_attach, adap->fe, 0x61, &adap->dev->i2c_adap,
+		   &dvb_pll_lg_tdvs_h06xf);
 	return 0;
 }
 
diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c
index 6a4d150..dddf164 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_core.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_core.c
@@ -56,10 +56,6 @@
 	if (txlen > 3)
 		index |= tx[3];
 
-	/* think about swapping here */
-	value = le16_to_cpu(value);
-	index = le16_to_cpu(index);
-
 	status = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev,0), tx[0],
 			USB_TYPE_VENDOR | USB_DIR_IN, value, index, rx, rxlen,
 			USB_CTRL_GET_TIMEOUT);
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 148386a..97715f7 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -13,6 +13,7 @@
 #define USB_VID_ADSTECH				0x06e1
 #define USB_VID_ALCOR_MICRO		0x058f
 #define USB_VID_ANCHOR				0x0547
+#define USB_VID_ANUBIS_ELECTRONIC		0x10fd
 #define USB_VID_AVERMEDIA			0x07ca
 #define USB_VID_COMPRO				0x185b
 #define USB_VID_COMPRO_UNK			0x145f
@@ -31,6 +32,7 @@
 #define USB_VID_LITEON				0x04ca
 #define USB_VID_MEDION				0x1660
 #define USB_VID_MSI				0x0db0
+#define USB_VID_OPERA1				0x695c
 #define USB_VID_PINNACLE			0x2304
 #define USB_VID_VISIONPLUS			0x13d3
 #define USB_VID_TWINHAN				0x1822
@@ -127,6 +129,7 @@
 #define USB_PID_KYE_DVB_T_WARM				0x701f
 #define USB_PID_PCTV_200E				0x020e
 #define USB_PID_PCTV_400E				0x020f
+#define USB_PID_PCTV_450E				0x0222
 #define USB_PID_LITEON_DVB_T_COLD			0xf000
 #define USB_PID_LITEON_DVB_T_WARM			0xf001
 #define USB_PID_DIGIVOX_MINI_SL_COLD			0xe360
@@ -139,6 +142,9 @@
 #define USB_PID_GENPIX_8PSK_COLD			0x0200
 #define USB_PID_GENPIX_8PSK_WARM			0x0201
 #define USB_PID_SIGMATEK_DVB_110			0x6610
+#define USB_PID_MSI_DIGI_VOX_MINI_II			0x1513
+#define USB_PID_OPERA1_COLD				0x2830
+#define USB_PID_OPERA1_WARM				0x3829
 
 
 #endif
diff --git a/drivers/media/dvb/dvb-usb/gl861.c b/drivers/media/dvb/dvb-usb/gl861.c
index c9f38a5..e0587e6 100644
--- a/drivers/media/dvb/dvb-usb/gl861.c
+++ b/drivers/media/dvb/dvb-usb/gl861.c
@@ -12,7 +12,7 @@
 #include "qt1010.h"
 
 /* debug */
-int dvb_usb_gl861_debug;
+static int dvb_usb_gl861_debug;
 module_param_named(debug,dvb_usb_gl861_debug, int, 0644);
 MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
 
@@ -20,7 +20,7 @@
 			 u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
 {
 	u16 index;
-	u16 value = addr << 8;
+	u16 value = addr << (8 + 1);
 	int wo = (rbuf == NULL || rlen == 0); /* write-only */
 	u8 req, type;
 
@@ -101,7 +101,7 @@
 }
 
 static struct zl10353_config gl861_zl10353_config = {
-	.demod_address = 0x1e,
+	.demod_address = 0x0f,
 	.no_tuner = 1,
 	.parallel_ts = 1,
 };
@@ -117,7 +117,7 @@
 }
 
 static struct qt1010_config gl861_qt1010_config = {
-	.i2c_address = 0xc4
+	.i2c_address = 0x62
 };
 
 static int gl861_tuner_attach(struct dvb_usb_adapter *adap)
diff --git a/drivers/media/dvb/dvb-usb/m920x.c b/drivers/media/dvb/dvb-usb/m920x.c
index d48b24d..45d7bc2 100644
--- a/drivers/media/dvb/dvb-usb/m920x.c
+++ b/drivers/media/dvb/dvb-usb/m920x.c
@@ -14,6 +14,8 @@
 #include "mt352.h"
 #include "mt352_priv.h"
 #include "qt1010.h"
+#include "tda1004x.h"
+#include "tda827x.h"
 
 /* debug */
 static int dvb_usb_m920x_debug;
@@ -47,11 +49,15 @@
 	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
 			      request, USB_TYPE_VENDOR | USB_DIR_IN,
 			      value, index, data, size, 2000);
-	if (ret < 0)
+	if (ret < 0) {
+		printk(KERN_INFO "m920x_read = error: %d\n", ret);
 		return ret;
+	}
 
-	if (ret != size)
+	if (ret != size) {
+		deb_rc("m920x_read = no data\n");
 		return -EIO;
+	}
 
 	return 0;
 }
@@ -64,19 +70,22 @@
 	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
 			      request, USB_TYPE_VENDOR | USB_DIR_OUT,
 			      value, index, NULL, 0, 2000);
+
 	return ret;
 }
 
-static int m9206_rc_init(struct usb_device *udev)
+static int m9206_init(struct dvb_usb_device *d)
 {
 	int ret = 0;
 
 	/* Remote controller init. */
-	if ((ret = m9206_write(udev, M9206_CORE, 0xa8, M9206_RC_INIT2)) != 0)
-		return ret;
+	if (d->props.rc_query) {
+		if ((ret = m9206_write(d->udev, M9206_CORE, 0xa8, M9206_RC_INIT2)) != 0)
+			return ret;
 
-	if ((ret = m9206_write(udev, M9206_CORE, 0x51, M9206_RC_INIT1)) != 0)
-		return ret;
+		if ((ret = m9206_write(d->udev, M9206_CORE, 0x51, M9206_RC_INIT1)) != 0)
+			return ret;
+	}
 
 	return ret;
 }
@@ -87,16 +96,15 @@
 	int i, ret = 0;
 	u8 rc_state[2];
 
-
 	if ((ret = m9206_read(d->udev, M9206_CORE, 0x0, M9206_RC_STATE, rc_state, 1)) != 0)
 		goto unlock;
 
 	if ((ret = m9206_read(d->udev, M9206_CORE, 0x0, M9206_RC_KEY, rc_state + 1, 1)) != 0)
 		goto unlock;
 
-	for (i = 0; i < ARRAY_SIZE(megasky_rc_keys); i++)
-		if (megasky_rc_keys[i].data == rc_state[1]) {
-			*event = megasky_rc_keys[i].event;
+	for (i = 0; i < d->props.rc_key_map_size; i++)
+		if (d->props.rc_key_map[i].data == rc_state[1]) {
+			*event = d->props.rc_key_map[i].event;
 
 			switch(rc_state[0]) {
 			case 0x80:
@@ -137,53 +145,51 @@
 			  int num)
 {
 	struct dvb_usb_device *d = i2c_get_adapdata(adap);
-	struct m9206_state *m = d->priv;
-	int i;
+	int i, j;
 	int ret = 0;
 
+	if (!num)
+		return -EINVAL;
+
 	if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
 		return -EAGAIN;
 
-	if (num > 2)
-		return -EINVAL;
-
 	for (i = 0; i < num; i++) {
-		if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].addr, 0x80)) != 0)
+		if (msg[i].flags & (I2C_M_NO_RD_ACK|I2C_M_IGNORE_NAK|I2C_M_TEN) ||
+		    msg[i].len == 0) {
+			/* For a 0 byte message, I think sending the address to index 0x80|0x40
+			 * would be the correct thing to do.  However, zero byte messages are
+			 * only used for probing, and since we don't know how to get the slave's
+			 * ack, we can't probe. */
+			ret = -ENOTSUPP;
 			goto unlock;
-
-		if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].buf[0], 0x0)) != 0)
-			goto unlock;
-
-		if (i + 1 < num && msg[i + 1].flags & I2C_M_RD) {
-			int i2c_i;
-
-			for (i2c_i = 0; i2c_i < M9206_I2C_MAX; i2c_i++)
-				if (msg[i].addr == m->i2c_r[i2c_i].addr)
-					break;
-
-			if (i2c_i >= M9206_I2C_MAX) {
-				deb_rc("No magic for i2c addr!\n");
-				ret = -EINVAL;
+		}
+		/* Send START & address/RW bit */
+		if (!(msg[i].flags & I2C_M_NOSTART)) {
+			if ((ret = m9206_write(d->udev, M9206_I2C, (msg[i].addr<<1)|(msg[i].flags&I2C_M_RD?0x01:0), 0x80)) != 0)
 				goto unlock;
+			/* Should check for ack here, if we knew how. */
+		}
+		if (msg[i].flags & I2C_M_RD) {
+			for (j = 0; j < msg[i].len; j++) {
+				/* Last byte of transaction? Send STOP, otherwise send ACK. */
+				int stop = (i+1 == num && j+1 == msg[i].len)?0x40:0x01;
+				if ((ret = m9206_read(d->udev, M9206_I2C, 0x0, 0x20|stop, &msg[i].buf[j], 1)) != 0)
+					goto unlock;
 			}
-
-			if ((ret = m9206_write(d->udev, M9206_I2C, m->i2c_r[i2c_i].magic, 0x80)) != 0)
-				goto unlock;
-
-			if ((ret = m9206_read(d->udev, M9206_I2C, 0x0, 0x60, msg[i + 1].buf, msg[i + 1].len)) != 0)
-				goto unlock;
-
-			i++;
 		} else {
-			if (msg[i].len != 2)
-				return -EINVAL;
-
-			if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].buf[1], 0x40)) != 0)
-				goto unlock;
+			for (j = 0; j < msg[i].len; j++) {
+				/* Last byte of transaction? Then send STOP. */
+				int stop = (i+1 == num && j+1 == msg[i].len)?0x40:0x00;
+				if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].buf[j], stop)) != 0)
+					goto unlock;
+				/* Should check for ack here too. */
+			}
 		}
 	}
-	ret = i;
-	unlock:
+	ret = num;
+
+unlock:
 	mutex_unlock(&d->i2c_mutex);
 
 	return ret;
@@ -324,6 +330,7 @@
 			i += size;
 		}
 		if (i != fw->size) {
+			deb_rc("bad firmware file!\n");
 			ret = -EINVAL;
 			goto done;
 		}
@@ -342,10 +349,10 @@
 }
 
 /* Callbacks for DVB USB */
-static int megasky_identify_state(struct usb_device *udev,
-				  struct dvb_usb_device_properties *props,
-				  struct dvb_usb_device_description **desc,
-				  int *cold)
+static int m920x_identify_state(struct usb_device *udev,
+				struct dvb_usb_device_properties *props,
+				struct dvb_usb_device_description **desc,
+				int *cold)
 {
 	struct usb_host_interface *alt;
 
@@ -381,20 +388,15 @@
 }
 
 static struct mt352_config megasky_mt352_config = {
-	.demod_address = 0x1e,
+	.demod_address = 0x0f,
 	.no_tuner = 1,
 	.demod_init = megasky_mt352_demod_init,
 };
 
 static int megasky_mt352_frontend_attach(struct dvb_usb_adapter *adap)
 {
-	struct m9206_state *m = adap->dev->priv;
-
 	deb_rc("megasky_frontend_attach!\n");
 
-	m->i2c_r[M9206_I2C_DEMOD].addr = megasky_mt352_config.demod_address;
-	m->i2c_r[M9206_I2C_DEMOD].magic = 0x1f;
-
 	if ((adap->fe = dvb_attach(mt352_attach, &megasky_mt352_config, &adap->dev->i2c_adap)) == NULL)
 		return -EIO;
 
@@ -402,16 +404,11 @@
 }
 
 static struct qt1010_config megasky_qt1010_config = {
-	.i2c_address = 0xc4
+	.i2c_address = 0x62
 };
 
 static int megasky_qt1010_tuner_attach(struct dvb_usb_adapter *adap)
 {
-	struct m9206_state *m = adap->dev->priv;
-
-	m->i2c_r[M9206_I2C_TUNER].addr = megasky_qt1010_config.i2c_address;
-	m->i2c_r[M9206_I2C_TUNER].magic = 0xc5;
-
 	if (dvb_attach(qt1010_attach, adap->fe, &adap->dev->i2c_adap,
 		       &megasky_qt1010_config) == NULL)
 		return -ENODEV;
@@ -419,8 +416,40 @@
 	return 0;
 }
 
+static struct tda1004x_config digivox_tda10046_config = {
+	.demod_address = 0x08,
+	.invert = 0,
+	.invert_oclk = 0,
+	.ts_mode = TDA10046_TS_SERIAL,
+	.xtal_freq = TDA10046_XTAL_16M,
+	.if_freq = TDA10046_FREQ_045,
+	.agc_config = TDA10046_AGC_TDA827X,
+	.gpio_config = TDA10046_GPTRI,
+	.request_firmware = NULL,
+};
+
+static int digivox_tda10046_frontend_attach(struct dvb_usb_adapter *adap)
+{
+	deb_rc("digivox_tda10046_frontend_attach!\n");
+
+	if ((adap->fe = dvb_attach(tda10046_attach, &digivox_tda10046_config,
+				   &adap->dev->i2c_adap)) == NULL)
+		return -EIO;
+
+	return 0;
+}
+
+static int digivox_tda8275_tuner_attach(struct dvb_usb_adapter *adap)
+{
+	if (dvb_attach(tda827x_attach, adap->fe, 0x60, &adap->dev->i2c_adap,
+		       NULL) == NULL)
+		return -ENODEV;
+	return 0;
+}
+
 /* DVB USB Driver stuff */
 static struct dvb_usb_device_properties megasky_properties;
+static struct dvb_usb_device_properties digivox_mini_ii_properties;
 
 static int m920x_probe(struct usb_interface *intf,
 		       const struct usb_device_id *id)
@@ -429,30 +458,36 @@
 	struct usb_host_interface *alt;
 	int ret;
 
-	if ((ret = dvb_usb_device_init(intf, &megasky_properties, THIS_MODULE, &d)) == 0) {
-		deb_rc("probed!\n");
+	deb_rc("Probed!\n");
 
-		alt = usb_altnum_to_altsetting(intf, 1);
-		if (alt == NULL) {
-			deb_rc("not alt found!\n");
-			return -ENODEV;
-		}
+	if (((ret = dvb_usb_device_init(intf, &megasky_properties, THIS_MODULE, &d)) == 0) ||
+	    ((ret = dvb_usb_device_init(intf, &digivox_mini_ii_properties, THIS_MODULE, &d)) == 0))
+		goto found;
 
-		ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber,
-					alt->desc.bAlternateSetting);
-		if (ret < 0)
-			return ret;
+	return ret;
 
-		deb_rc("Changed to alternate setting!\n");
-
-		if ((ret = m9206_rc_init(d->udev)) != 0)
-			return ret;
+found:
+	alt = usb_altnum_to_altsetting(intf, 1);
+	if (alt == NULL) {
+		deb_rc("No alt found!\n");
+		return -ENODEV;
 	}
+
+	ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber,
+				alt->desc.bAlternateSetting);
+	if (ret < 0)
+		return ret;
+
+	if ((ret = m9206_init(d)) != 0)
+		return ret;
+
 	return ret;
 }
 
 static struct usb_device_id m920x_table [] = {
 		{ USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580) },
+		{ USB_DEVICE(USB_VID_ANUBIS_ELECTRONIC,
+			     USB_PID_MSI_DIGI_VOX_MINI_II) },
 		{ }		/* Terminating entry */
 };
 MODULE_DEVICE_TABLE (usb, m920x_table);
@@ -471,7 +506,7 @@
 
 	.size_of_priv     = sizeof(struct m9206_state),
 
-	.identify_state   = megasky_identify_state,
+	.identify_state   = m920x_identify_state,
 	.num_adapters = 1,
 	.adapter = {{
 		.caps = DVB_USB_ADAP_HAS_PID_FILTER |
@@ -502,6 +537,50 @@
 		{   "MSI Mega Sky 580 DVB-T USB2.0",
 			{ &m920x_table[0], NULL },
 			{ NULL },
+		}
+	}
+};
+
+static struct dvb_usb_device_properties digivox_mini_ii_properties = {
+	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+	.usb_ctrl = DEVICE_SPECIFIC,
+	.firmware = "dvb-usb-digivox-02.fw",
+	.download_firmware = m9206_firmware_download,
+
+	.size_of_priv     = sizeof(struct m9206_state),
+
+	.identify_state   = m920x_identify_state,
+	.num_adapters = 1,
+	.adapter = {{
+		.caps = DVB_USB_ADAP_HAS_PID_FILTER |
+		DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+
+		.pid_filter_count = 8,
+		.pid_filter       = m9206_pid_filter,
+		.pid_filter_ctrl  = m9206_pid_filter_ctrl,
+
+		.frontend_attach  = digivox_tda10046_frontend_attach,
+		.tuner_attach     = digivox_tda8275_tuner_attach,
+
+		.stream = {
+			.type = USB_BULK,
+			.count = 8,
+			.endpoint = 0x81,
+			.u = {
+				.bulk = {
+					.buffersize = 0x4000,
+				}
+			}
+		},
+	}},
+	.i2c_algo         = &m9206_i2c_algo,
+
+	.num_device_descs = 1,
+	.devices = {
+		{   "MSI DIGI VOX mini II DVB-T USB2.0",
+			{ &m920x_table[1], NULL },
+			{ NULL },
 		},
 	}
 };
diff --git a/drivers/media/dvb/dvb-usb/m920x.h b/drivers/media/dvb/dvb-usb/m920x.h
index c354196..7dd3db6 100644
--- a/drivers/media/dvb/dvb-usb/m920x.h
+++ b/drivers/media/dvb/dvb-usb/m920x.h
@@ -19,17 +19,49 @@
 
 #define M9206_MAX_FILTERS 8
 
-#define M9206_I2C_TUNER	0
-#define M9206_I2C_DEMOD	1
-#define M9206_I2C_MAX	2
+/*
+sequences found in logs:
+[index value]
+0x80 write addr
+(0x00 out byte)*
+0x40 out byte
+
+0x80 write addr
+(0x00 out byte)*
+0x80 read addr
+(0x21 in byte)*
+0x60 in byte
+
+this sequence works:
+0x80 read addr
+(0x21 in byte)*
+0x60 in byte
+
+Guess at API of the I2C function:
+I2C operation is done one byte at a time with USB control messages.  The
+index the messages is sent to is made up of a set of flags that control
+the I2C bus state:
+0x80:  Send START condition.  After a START condition, one would normally
+       always send the 7-bit slave I2C address as the 7 MSB, followed by
+       the read/write bit as the LSB.
+0x40:  Send STOP condition.  This should be set on the last byte of an
+       I2C transaction.
+0x20:  Read a byte from the slave.  As opposed to writing a byte to the
+       slave.  The slave will normally not produce any data unless you
+       set the R/W bit to 1 when sending the slave's address after the
+       START condition.
+0x01:  Respond with ACK, as opposed to a NACK.  For a multi-byte read,
+       the master should send an ACK, that is pull SDA low during the 9th
+       clock cycle, after every byte but the last.  This flags only makes
+       sense when bit 0x20 is set, indicating a read.
+
+What any other bits might mean, or how to get the slave's ACK/NACK
+response to a write, is unknown.
+*/
 
 struct m9206_state {
 	u16 filters[M9206_MAX_FILTERS];
 	int filtering_enabled;
 	int rep_count;
-	struct {
-		unsigned char addr;
-		unsigned char magic;
-	}i2c_r[M9206_I2C_MAX];
 };
 #endif
diff --git a/drivers/media/dvb/dvb-usb/opera1.c b/drivers/media/dvb/dvb-usb/opera1.c
new file mode 100644
index 0000000..518d7ad
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/opera1.c
@@ -0,0 +1,581 @@
+/* DVB USB framework compliant Linux driver for the Opera1 DVB-S Card
+*
+* Copyright (C) 2006 Mario Hlawitschka (dh1pa@amsat.org)
+* Copyright (C) 2006 Marco Gittler (g.marco@freenet.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, version 2.
+*
+* see Documentation/dvb/README.dvb-usb for more information
+*/
+
+#include "opera1.h"
+#include "stv0299.h"
+
+#define OPERA_READ_MSG 0
+#define OPERA_WRITE_MSG 1
+#define OPERA_I2C_TUNER 0xd1
+
+#define READ_FX2_REG_REQ  0xba
+#define READ_MAC_ADDR 0x08
+#define OPERA_WRITE_FX2 0xbb
+#define OPERA_TUNER_REQ 0xb1
+#define REG_1F_SYMBOLRATE_BYTE0 0x1f
+#define REG_20_SYMBOLRATE_BYTE1 0x20
+#define REG_21_SYMBOLRATE_BYTE2 0x21
+
+#define ADDR_B600_VOLTAGE_13V (0x02)
+#define ADDR_B601_VOLTAGE_18V (0x03)
+#define ADDR_B1A6_STREAM_CTRL (0x04)
+#define ADDR_B880_READ_REMOTE (0x05)
+
+struct opera1_state {
+	u32 last_key_pressed;
+};
+struct opera_rc_keys {
+	u32 keycode;
+	u32 event;
+};
+
+int dvb_usb_opera1_debug;
+module_param_named(debug, dvb_usb_opera1_debug, int, 0644);
+MODULE_PARM_DESC(debug,
+		 "set debugging level (1=info,xfer=2,pll=4,ts=8,err=16,rc=32,fw=64 (or-able))."
+		 DVB_USB_DEBUG_STATUS);
+
+static int opera1_xilinx_rw(struct usb_device *dev, u8 request, u16 value,
+			    u8 * data, u16 len, int flags)
+{
+	int ret;
+	u8 r;
+	u8 u8buf[len];
+
+	unsigned int pipe = (flags == OPERA_READ_MSG) ?
+		usb_rcvctrlpipe(dev,0) : usb_sndctrlpipe(dev, 0);
+	u8 request_type = (flags == OPERA_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT;
+
+	if (flags == OPERA_WRITE_MSG)
+		memcpy(u8buf, data, len);
+	ret =
+		usb_control_msg(dev, pipe, request, request_type | USB_TYPE_VENDOR,
+			value, 0x0, u8buf, len, 2000);
+
+	if (request == OPERA_TUNER_REQ) {
+		if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+				OPERA_TUNER_REQ, USB_DIR_IN | USB_TYPE_VENDOR,
+				0x01, 0x0, &r, 1, 2000)<1 || r!=0x08)
+					return 0;
+	}
+	if (flags == OPERA_READ_MSG)
+		memcpy(data, u8buf, len);
+	return ret;
+}
+
+/* I2C */
+
+static int opera1_usb_i2c_msgxfer(struct dvb_usb_device *dev, u16 addr,
+				  u8 * buf, u16 len)
+{
+	int ret = 0;
+	u8 request;
+	u16 value;
+
+	if (!dev) {
+		info("no usb_device");
+		return -EINVAL;
+	}
+	if (mutex_lock_interruptible(&dev->usb_mutex) < 0)
+		return -EAGAIN;
+
+	switch (addr>>1){
+		case ADDR_B600_VOLTAGE_13V:
+			request=0xb6;
+			value=0x00;
+			break;
+		case ADDR_B601_VOLTAGE_18V:
+			request=0xb6;
+			value=0x01;
+			break;
+		case ADDR_B1A6_STREAM_CTRL:
+			request=0xb1;
+			value=0xa6;
+			break;
+		case ADDR_B880_READ_REMOTE:
+			request=0xb8;
+			value=0x80;
+			break;
+		default:
+			request=0xb1;
+			value=addr;
+	}
+	ret = opera1_xilinx_rw(dev->udev, request,
+		value, buf, len,
+		addr&0x01?OPERA_READ_MSG:OPERA_WRITE_MSG);
+
+	mutex_unlock(&dev->usb_mutex);
+	return ret;
+}
+
+static int opera1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+			   int num)
+{
+	struct dvb_usb_device *d = i2c_get_adapdata(adap);
+	int i = 0, tmp = 0;
+
+	if (!d)
+		return -ENODEV;
+	if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+		return -EAGAIN;
+
+	for (i = 0; i < num; i++) {
+		if ((tmp = opera1_usb_i2c_msgxfer(d,
+					(msg[i].addr<<1)|(msg[i].flags&I2C_M_RD?0x01:0),
+					msg[i].buf,
+					msg[i].len
+					)!= msg[i].len)) {
+			break;
+		}
+		if (dvb_usb_opera1_debug & 0x10)
+			info("sending i2c mesage %d %d", tmp, msg[i].len);
+	}
+	mutex_unlock(&d->i2c_mutex);
+	return num;
+}
+
+static u32 opera1_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm opera1_i2c_algo = {
+	.master_xfer = opera1_i2c_xfer,
+	.functionality = opera1_i2c_func,
+};
+
+static int opera1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+	static u8 command_13v[1]={0x00};
+	static u8 command_18v[1]={0x01};
+	struct i2c_msg msg[] = {
+		{.addr = ADDR_B600_VOLTAGE_13V,.flags = 0,.buf = command_13v,.len = 1},
+	};
+	struct dvb_usb_adapter *udev_adap =
+	    (struct dvb_usb_adapter *)(fe->dvb->priv);
+	if (voltage == SEC_VOLTAGE_18) {
+		msg[0].addr = ADDR_B601_VOLTAGE_18V;
+		msg[0].buf = command_18v;
+	}
+	i2c_transfer(&udev_adap->dev->i2c_adap, msg, 1);
+	return 0;
+}
+
+static int opera1_stv0299_set_symbol_rate(struct dvb_frontend *fe, u32 srate,
+					  u32 ratio)
+{
+	stv0299_writereg(fe, 0x13, 0x98);
+	stv0299_writereg(fe, 0x14, 0x95);
+	stv0299_writereg(fe, REG_1F_SYMBOLRATE_BYTE0, (ratio >> 16) & 0xff);
+	stv0299_writereg(fe, REG_20_SYMBOLRATE_BYTE1, (ratio >> 8) & 0xff);
+	stv0299_writereg(fe, REG_21_SYMBOLRATE_BYTE2, (ratio) & 0xf0);
+	return 0;
+
+}
+static u8 opera1_inittab[] = {
+	0x00, 0xa1,
+	0x01, 0x15,
+	0x02, 0x00,
+	0x03, 0x00,
+	0x04, 0x7d,
+	0x05, 0x05,
+	0x06, 0x02,
+	0x07, 0x00,
+	0x0b, 0x00,
+	0x0c, 0x01,
+	0x0d, 0x81,
+	0x0e, 0x44,
+	0x0f, 0x19,
+	0x10, 0x3f,
+	0x11, 0x84,
+	0x12, 0xda,
+	0x13, 0x98,
+	0x14, 0x95,
+	0x15, 0xc9,
+	0x16, 0xeb,
+	0x17, 0x00,
+	0x18, 0x19,
+	0x19, 0x8b,
+	0x1a, 0x00,
+	0x1b, 0x82,
+	0x1c, 0x7f,
+	0x1d, 0x00,
+	0x1e, 0x00,
+	REG_1F_SYMBOLRATE_BYTE0, 0x06,
+	REG_20_SYMBOLRATE_BYTE1, 0x50,
+	REG_21_SYMBOLRATE_BYTE2, 0x10,
+	0x22, 0x00,
+	0x23, 0x00,
+	0x24, 0x37,
+	0x25, 0xbc,
+	0x26, 0x00,
+	0x27, 0x00,
+	0x28, 0x00,
+	0x29, 0x1e,
+	0x2a, 0x14,
+	0x2b, 0x1f,
+	0x2c, 0x09,
+	0x2d, 0x0a,
+	0x2e, 0x00,
+	0x2f, 0x00,
+	0x30, 0x00,
+	0x31, 0x1f,
+	0x32, 0x19,
+	0x33, 0xfc,
+	0x34, 0x13,
+	0xff, 0xff,
+};
+
+static struct stv0299_config opera1_stv0299_config = {
+	.demod_address = 0xd0>>1,
+	.min_delay_ms = 100,
+	.mclk = 88000000UL,
+	.invert = 1,
+	.skip_reinit = 0,
+	.lock_output = STV0229_LOCKOUTPUT_0,
+	.volt13_op0_op1 = STV0299_VOLT13_OP0,
+	.inittab = opera1_inittab,
+	.set_symbol_rate = opera1_stv0299_set_symbol_rate,
+};
+
+static int opera1_frontend_attach(struct dvb_usb_adapter *d)
+{
+	if ((d->fe =
+	     dvb_attach(stv0299_attach, &opera1_stv0299_config,
+			&d->dev->i2c_adap)) != NULL) {
+		d->fe->ops.set_voltage = opera1_set_voltage;
+		return 0;
+	}
+	info("not attached stv0299");
+	return -EIO;
+}
+
+static int opera1_tuner_attach(struct dvb_usb_adapter *adap)
+{
+	dvb_attach(
+		dvb_pll_attach, adap->fe, 0xc0>>1,
+		&adap->dev->i2c_adap, &dvb_pll_opera1
+	);
+	return 0;
+}
+
+static int opera1_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+	u8 val = onoff ? 0x01 : 0x00;
+
+	if (dvb_usb_opera1_debug)
+		info("power %s", onoff ? "on" : "off");
+	return opera1_xilinx_rw(d->udev, 0xb7, val,
+				&val, 1, OPERA_WRITE_MSG);
+}
+
+static int opera1_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+	static u8 buf_start[2] = { 0xff, 0x03 };
+	static u8 buf_stop[2] = { 0xff, 0x00 };
+	struct i2c_msg start_tuner[] = {
+		{.addr = ADDR_B1A6_STREAM_CTRL,.buf = onoff ? buf_start : buf_stop,.len = 2},
+	};
+	if (dvb_usb_opera1_debug)
+		info("streaming %s", onoff ? "on" : "off");
+	i2c_transfer(&adap->dev->i2c_adap, start_tuner, 1);
+	return 0;
+}
+
+static int opera1_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid,
+			     int onoff)
+{
+	u8 b_pid[3];
+	struct i2c_msg msg[] = {
+		{.addr = ADDR_B1A6_STREAM_CTRL,.buf = b_pid,.len = 3},
+	};
+	if (dvb_usb_opera1_debug)
+		info("pidfilter index: %d pid: %d %s", index, pid,
+			onoff ? "on" : "off");
+	b_pid[0] = (2 * index) + 4;
+	b_pid[1] = onoff ? (pid & 0xff) : (0x00);
+	b_pid[2] = onoff ? ((pid >> 8) & 0xff) : (0x00);
+	i2c_transfer(&adap->dev->i2c_adap, msg, 1);
+	return 0;
+}
+
+static int opera1_pid_filter_control(struct dvb_usb_adapter *adap, int onoff)
+{
+	int u = 0x04;
+	u8 b_pid[3];
+	struct i2c_msg msg[] = {
+		{.addr = ADDR_B1A6_STREAM_CTRL,.buf = b_pid,.len = 3},
+	};
+	if (dvb_usb_opera1_debug)
+		info("%s hw-pidfilter", onoff ? "enable" : "disable");
+	for (; u < 0x7e; u += 2) {
+		b_pid[0] = u;
+		b_pid[1] = 0;
+		b_pid[2] = 0x80;
+		i2c_transfer(&adap->dev->i2c_adap, msg, 1);
+	}
+	return 0;
+}
+
+static struct dvb_usb_rc_key opera1_rc_keys[] = {
+	{0x5f, 0xa0, KEY_1},
+	{0x51, 0xaf, KEY_2},
+	{0x5d, 0xa2, KEY_3},
+	{0x41, 0xbe, KEY_4},
+	{0x0b, 0xf5, KEY_5},
+	{0x43, 0xbd, KEY_6},
+	{0x47, 0xb8, KEY_7},
+	{0x49, 0xb6, KEY_8},
+	{0x05, 0xfa, KEY_9},
+	{0x45, 0xba, KEY_0},
+	{0x09, 0xf6, KEY_UP},	/*chanup */
+	{0x1b, 0xe5, KEY_DOWN},	/*chandown */
+	{0x5d, 0xa3, KEY_LEFT},	/*voldown */
+	{0x5f, 0xa1, KEY_RIGHT},	/*volup */
+	{0x07, 0xf8, KEY_SPACE},	/*tab */
+	{0x1f, 0xe1, KEY_ENTER},	/*play ok */
+	{0x1b, 0xe4, KEY_Z},	/*zoom */
+	{0x59, 0xa6, KEY_M},	/*mute */
+	{0x5b, 0xa5, KEY_F},	/*tv/f */
+	{0x19, 0xe7, KEY_R},	/*rec */
+	{0x01, 0xfe, KEY_S},	/*Stop */
+	{0x03, 0xfd, KEY_P},	/*pause */
+	{0x03, 0xfc, KEY_W},	/*<- -> */
+	{0x07, 0xf9, KEY_C},	/*capture */
+	{0x47, 0xb9, KEY_Q},	/*exit */
+	{0x43, 0xbc, KEY_O},	/*power */
+
+};
+
+static int opera1_rc_query(struct dvb_usb_device *dev, u32 * event, int *state)
+{
+	struct opera1_state *opst = dev->priv;
+	u8 rcbuffer[32];
+	const u16 startmarker1 = 0x10ed;
+	const u16 startmarker2 = 0x11ec;
+	struct i2c_msg read_remote[] = {
+		{.addr = ADDR_B880_READ_REMOTE,.buf = rcbuffer,.flags = I2C_M_RD,.len = 32},
+	};
+	int i = 0;
+	u32 send_key = 0;
+
+	if (i2c_transfer(&dev->i2c_adap, read_remote, 1) == 1) {
+		for (i = 0; i < 32; i++) {
+			if (rcbuffer[i])
+				send_key |= 1;
+			if (i < 31)
+				send_key = send_key << 1;
+		}
+		if (send_key & 0x8000)
+			send_key = (send_key << 1) | (send_key >> 15 & 0x01);
+
+		if (send_key == 0xffff && opst->last_key_pressed != 0) {
+			*state = REMOTE_KEY_REPEAT;
+			*event = opst->last_key_pressed;
+			return 0;
+		}
+		for (; send_key != 0;) {
+			if (send_key >> 16 == startmarker2) {
+				break;
+			} else if (send_key >> 16 == startmarker1) {
+				send_key =
+					(send_key & 0xfffeffff) | (startmarker1 << 16);
+				break;
+			} else
+				send_key >>= 1;
+		}
+
+		if (send_key == 0)
+			return 0;
+
+		send_key = (send_key & 0xffff) | 0x0100;
+
+		for (i = 0; i < ARRAY_SIZE(opera1_rc_keys); i++) {
+			if ((opera1_rc_keys[i].custom * 256 +
+					opera1_rc_keys[i].data) == (send_key & 0xffff)) {
+				*state = REMOTE_KEY_PRESSED;
+				*event = opera1_rc_keys[i].event;
+				opst->last_key_pressed =
+					opera1_rc_keys[i].event;
+				break;
+			}
+			opst->last_key_pressed = 0;
+		}
+	} else
+		*state = REMOTE_NO_KEY_PRESSED;
+	return 0;
+}
+
+static struct usb_device_id opera1_table[] = {
+	{USB_DEVICE(USB_VID_CYPRESS, USB_PID_OPERA1_COLD)},
+	{USB_DEVICE(USB_VID_OPERA1, USB_PID_OPERA1_WARM)},
+	{}
+};
+
+MODULE_DEVICE_TABLE(usb, opera1_table);
+
+static int opera1_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
+{
+	u8 command[] = { READ_MAC_ADDR };
+	opera1_xilinx_rw(d->udev, 0xb1, 0xa0, command, 1, OPERA_WRITE_MSG);
+	opera1_xilinx_rw(d->udev, 0xb1, 0xa1, mac, 6, OPERA_READ_MSG);
+	return 0;
+}
+static int opera1_xilinx_load_firmware(struct usb_device *dev,
+				       const char *filename)
+{
+	const struct firmware *fw = NULL;
+	u8 *b, *p;
+	int ret = 0, i;
+	u8 testval;
+	info("start downloading fpga firmware");
+
+	if ((ret = request_firmware(&fw, filename, &dev->dev)) != 0) {
+		err("did not find the firmware file. (%s) "
+			"Please see linux/Documentation/dvb/ for more details on firmware-problems.",
+			filename);
+		return ret;
+	} else {
+		p = kmalloc(fw->size, GFP_KERNEL);
+		opera1_xilinx_rw(dev, 0xbc, 0x00, &testval, 1, OPERA_READ_MSG);
+		if (p != NULL && testval != 0x67) {
+
+			u8 reset = 0, fpga_command = 0;
+			memcpy(p, fw->data, fw->size);
+			/* clear fpga ? */
+			opera1_xilinx_rw(dev, 0xbc, 0xaa, &fpga_command, 1,
+					 OPERA_WRITE_MSG);
+			for (i = 0; p[i] != 0 && i < fw->size;) {
+				b = (u8 *) p + i;
+				if (opera1_xilinx_rw
+					(dev, OPERA_WRITE_FX2, 0x0, b + 1, b[0],
+						OPERA_WRITE_MSG) != b[0]
+					) {
+					err("error while transferring firmware");
+					ret = -EINVAL;
+					break;
+				}
+				i = i + 1 + b[0];
+			}
+			/* restart the CPU */
+			if (ret || opera1_xilinx_rw
+					(dev, 0xa0, 0xe600, &reset, 1,
+					OPERA_WRITE_MSG) != 1) {
+				err("could not restart the USB controller CPU.");
+				ret = -EINVAL;
+			}
+			kfree(p);
+		}
+	}
+	if (fw) {
+		release_firmware(fw);
+	}
+	return ret;
+}
+
+static struct dvb_usb_device_properties opera1_properties = {
+	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
+	.usb_ctrl = CYPRESS_FX2,
+	.firmware = "dvb-usb-opera-01.fw",
+	.size_of_priv = sizeof(struct opera1_state),
+
+	.power_ctrl = opera1_power_ctrl,
+	.i2c_algo = &opera1_i2c_algo,
+
+	.rc_key_map = opera1_rc_keys,
+	.rc_key_map_size = ARRAY_SIZE(opera1_rc_keys),
+	.rc_interval = 200,
+	.rc_query = opera1_rc_query,
+	.read_mac_address = opera1_read_mac_address,
+	.generic_bulk_ctrl_endpoint = 0x00,
+	/* parameter for the MPEG2-data transfer */
+	.num_adapters = 1,
+	.adapter = {
+		{
+			.frontend_attach = opera1_frontend_attach,
+			.streaming_ctrl = opera1_streaming_ctrl,
+			.tuner_attach = opera1_tuner_attach,
+			.caps =
+				DVB_USB_ADAP_HAS_PID_FILTER |
+				DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+			.pid_filter = opera1_pid_filter,
+			.pid_filter_ctrl = opera1_pid_filter_control,
+			.pid_filter_count = 252,
+			.stream = {
+				.type = USB_BULK,
+				.count = 10,
+				.endpoint = 0x82,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			},
+		}
+	},
+	.num_device_descs = 1,
+	.devices = {
+		{"Opera1 DVB-S USB2.0",
+			{&opera1_table[0], NULL},
+			{&opera1_table[1], NULL},
+		},
+	}
+};
+
+static int opera1_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	struct dvb_usb_device *d;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	if (udev->descriptor.idProduct == USB_PID_OPERA1_WARM &&
+		udev->descriptor.idVendor == USB_VID_OPERA1 &&
+		(d == NULL
+			|| opera1_xilinx_load_firmware(udev, "dvb-usb-opera1-fpga.fw") != 0)
+		) {
+		return -EINVAL;
+	}
+
+	if (dvb_usb_device_init(intf, &opera1_properties, THIS_MODULE, &d) != 0)
+		return -EINVAL;
+	return 0;
+}
+
+static struct usb_driver opera1_driver = {
+	.name = "opera1",
+	.probe = opera1_probe,
+	.disconnect = dvb_usb_device_exit,
+	.id_table = opera1_table,
+};
+
+static int __init opera1_module_init(void)
+{
+	int result = 0;
+	if ((result = usb_register(&opera1_driver))) {
+		err("usb_register failed. Error number %d", result);
+	}
+	return result;
+}
+
+static void __exit opera1_module_exit(void)
+{
+	usb_deregister(&opera1_driver);
+}
+
+module_init(opera1_module_init);
+module_exit(opera1_module_exit);
+
+MODULE_AUTHOR("Mario Hlawitschka (c) dh1pa@amsat.org");
+MODULE_AUTHOR("Marco Gittler (c) g.marco@freenet.de");
+MODULE_DESCRIPTION("Driver for Opera1 DVB-S device");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/opera1.h b/drivers/media/dvb/dvb-usb/opera1.h
new file mode 100644
index 0000000..5317442
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/opera1.h
@@ -0,0 +1,9 @@
+#ifndef _OPERA1_H_
+#define _OPERA1_H_
+
+#define DVB_USB_LOG_PREFIX "opera"
+#include "dvb-usb.h"
+
+extern int dvb_usb_opera1_debug;
+#define deb_xfer(args...) dprintk(dvb_usb_opera1_debug,0x02,args)
+#endif
diff --git a/drivers/media/dvb/dvb-usb/ttusb2.c b/drivers/media/dvb/dvb-usb/ttusb2.c
index 95d2997..88dc436 100644
--- a/drivers/media/dvb/dvb-usb/ttusb2.c
+++ b/drivers/media/dvb/dvb-usb/ttusb2.c
@@ -184,6 +184,7 @@
 
 static struct usb_device_id ttusb2_table [] = {
 		{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_400E) },
+		{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_450E) },
 		{}		/* Terminating entry */
 };
 MODULE_DEVICE_TABLE (usb, ttusb2_table);
@@ -227,12 +228,16 @@
 
 	.generic_bulk_ctrl_endpoint = 0x01,
 
-	.num_device_descs = 1,
+	.num_device_descs = 2,
 	.devices = {
 		{   "Pinnacle 400e DVB-S USB2.0",
 			{ &ttusb2_table[0], NULL },
 			{ NULL },
 		},
+		{   "Pinnacle 450e DVB-S USB2.0",
+			{ &ttusb2_table[1], NULL },
+			{ NULL },
+		},
 	}
 };
 
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index 22c2cf2..ff44876 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -205,6 +205,13 @@
 	help
 	  A DVB-C tuner module. Say Y when you want to support this frontend.
 
+config DVB_TDA10023
+	tristate "Philips TDA10023 based"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A DVB-C tuner module. Say Y when you want to support this frontend.
+
 config DVB_STV0297
 	tristate "ST STV0297 based"
 	depends on DVB_CORE && I2C
@@ -280,8 +287,12 @@
 	depends on DVB_CORE
 
 config DVB_PLL
-	tristate
+	tristate "Generic I2C PLL based tuners"
 	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  This module driver a number of tuners based on PLL chips with a
+	  common I2C interface. Say Y when you want to support these tuners.
 
 config DVB_TDA826X
 	tristate "Philips TDA826X silicon tuner"
@@ -290,6 +301,13 @@
 	help
 	  A DVB-S silicon tuner module. Say Y when you want to support this tuner.
 
+config DVB_TDA827X
+	tristate "Philips TDA827X silicon tuner"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A DVB-T silicon tuner module. Say Y when you want to support this tuner.
+
 config DVB_TUNER_QT1010
 	tristate "Quantek QT1010 silicon tuner"
 	depends on DVB_CORE && I2C
@@ -304,14 +322,6 @@
 	help
 	  A driver for the silicon IF tuner MT2060 from Microtune.
 
-config DVB_TUNER_LGH06XF
-	tristate "LG TDVS-H06xF ATSC tuner"
-	depends on DVB_CORE && I2C
-	select DVB_PLL
-	default m if DVB_FE_CUSTOMISE
-	help
-	  A driver for the LG TDVS-H06xF ATSC tuner family.
-
 comment "Miscellaneous devices"
 	depends on DVB_CORE
 
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index a646d99..27f3865 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -25,6 +25,7 @@
 obj-$(CONFIG_DVB_ZL10353) += zl10353.o
 obj-$(CONFIG_DVB_CX22702) += cx22702.o
 obj-$(CONFIG_DVB_TDA10021) += tda10021.o
+obj-$(CONFIG_DVB_TDA10023) += tda10023.o
 obj-$(CONFIG_DVB_STV0297) += stv0297.o
 obj-$(CONFIG_DVB_NXT200X) += nxt200x.o
 obj-$(CONFIG_DVB_OR51211) += or51211.o
@@ -37,7 +38,7 @@
 obj-$(CONFIG_DVB_ISL6421) += isl6421.o
 obj-$(CONFIG_DVB_TDA10086) += tda10086.o
 obj-$(CONFIG_DVB_TDA826X) += tda826x.o
+obj-$(CONFIG_DVB_TDA827X) += tda827x.o
 obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o
 obj-$(CONFIG_DVB_TUNER_QT1010) += qt1010.o
 obj-$(CONFIG_DVB_TUA6100) += tua6100.o
-obj-$(CONFIG_DVB_TUNER_LGH06XF) += lgh06xf.o
diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c
index 62de760..5f96ffd 100644
--- a/drivers/media/dvb/frontends/dvb-pll.c
+++ b/drivers/media/dvb/frontends/dvb-pll.c
@@ -27,17 +27,29 @@
 /* ----------------------------------------------------------- */
 /* descriptions                                                */
 
+/* Set AGC TOP value to 103 dBuV:
+	0x80 = Control Byte
+	0x40 = 250 uA charge pump (irrelevant)
+	0x18 = Aux Byte to follow
+	0x06 = 64.5 kHz divider (irrelevant)
+	0x01 = Disable Vt (aka sleep)
+
+	0x00 = AGC Time constant 2s Iagc = 300 nA (vs 0x80 = 9 nA)
+	0x50 = AGC Take over point = 103 dBuV */
+static u8 tua603x_agc103[] = { 2, 0x80|0x40|0x18|0x06|0x01, 0x00|0x50 };
+
 struct dvb_pll_desc dvb_pll_thomson_dtt7579 = {
 	.name  = "Thomson dtt7579",
 	.min   = 177000000,
 	.max   = 858000000,
-	.count = 5,
+	.iffreq= 36166667,
+	.sleepdata = (u8[]){ 2, 0xb4, 0x03 },
+	.count = 4,
 	.entries = {
-		{          0, 36166667, 166666, 0xb4, 0x03 }, /* go sleep */
-		{  443250000, 36166667, 166666, 0xb4, 0x02 },
-		{  542000000, 36166667, 166666, 0xb4, 0x08 },
-		{  771000000, 36166667, 166666, 0xbc, 0x08 },
-		{  999999999, 36166667, 166666, 0xf4, 0x08 },
+		{  443250000, 166667, 0xb4, 0x02 },
+		{  542000000, 166667, 0xb4, 0x08 },
+		{  771000000, 166667, 0xbc, 0x08 },
+		{  999999999, 166667, 0xf4, 0x08 },
 	},
 };
 EXPORT_SYMBOL(dvb_pll_thomson_dtt7579);
@@ -46,11 +58,12 @@
 	.name  = "Thomson dtt7610",
 	.min   =  44000000,
 	.max   = 958000000,
+	.iffreq= 44000000,
 	.count = 3,
 	.entries = {
-		{ 157250000, 44000000, 62500, 0x8e, 0x39 },
-		{ 454000000, 44000000, 62500, 0x8e, 0x3a },
-		{ 999999999, 44000000, 62500, 0x8e, 0x3c },
+		{ 157250000, 62500, 0x8e, 0x39 },
+		{ 454000000, 62500, 0x8e, 0x3a },
+		{ 999999999, 62500, 0x8e, 0x3c },
 	},
 };
 EXPORT_SYMBOL(dvb_pll_thomson_dtt7610);
@@ -66,14 +79,15 @@
 	.min   = 177000000,
 	.max   = 896000000,
 	.setbw = thomson_dtt759x_bw,
-	.count = 6,
+	.iffreq= 36166667,
+	.sleepdata = (u8[]){ 2, 0x84, 0x03 },
+	.count = 5,
 	.entries = {
-		{          0, 36166667, 166666, 0x84, 0x03 },
-		{  264000000, 36166667, 166666, 0xb4, 0x02 },
-		{  470000000, 36166667, 166666, 0xbc, 0x02 },
-		{  735000000, 36166667, 166666, 0xbc, 0x08 },
-		{  835000000, 36166667, 166666, 0xf4, 0x08 },
-		{  999999999, 36166667, 166666, 0xfc, 0x08 },
+		{  264000000, 166667, 0xb4, 0x02 },
+		{  470000000, 166667, 0xbc, 0x02 },
+		{  735000000, 166667, 0xbc, 0x08 },
+		{  835000000, 166667, 0xf4, 0x08 },
+		{  999999999, 166667, 0xfc, 0x08 },
 	},
 };
 EXPORT_SYMBOL(dvb_pll_thomson_dtt759x);
@@ -82,14 +96,15 @@
 	.name  = "LG z201",
 	.min   = 174000000,
 	.max   = 862000000,
-	.count = 6,
+	.iffreq= 36166667,
+	.sleepdata = (u8[]){ 2, 0xbc, 0x03 },
+	.count = 5,
 	.entries = {
-		{          0, 36166667, 166666, 0xbc, 0x03 },
-		{  157500000, 36166667, 166666, 0xbc, 0x01 },
-		{  443250000, 36166667, 166666, 0xbc, 0x02 },
-		{  542000000, 36166667, 166666, 0xbc, 0x04 },
-		{  830000000, 36166667, 166666, 0xf4, 0x04 },
-		{  999999999, 36166667, 166666, 0xfc, 0x04 },
+		{  157500000, 166667, 0xbc, 0x01 },
+		{  443250000, 166667, 0xbc, 0x02 },
+		{  542000000, 166667, 0xbc, 0x04 },
+		{  830000000, 166667, 0xf4, 0x04 },
+		{  999999999, 166667, 0xfc, 0x04 },
 	},
 };
 EXPORT_SYMBOL(dvb_pll_lg_z201);
@@ -98,11 +113,12 @@
 	.name  = "Microtune 4042 FI5",
 	.min   =  57000000,
 	.max   = 858000000,
+	.iffreq= 44000000,
 	.count = 3,
 	.entries = {
-		{ 162000000, 44000000, 62500, 0x8e, 0xa1 },
-		{ 457000000, 44000000, 62500, 0x8e, 0x91 },
-		{ 999999999, 44000000, 62500, 0x8e, 0x31 },
+		{ 162000000, 62500, 0x8e, 0xa1 },
+		{ 457000000, 62500, 0x8e, 0x91 },
+		{ 999999999, 62500, 0x8e, 0x31 },
 	},
 };
 EXPORT_SYMBOL(dvb_pll_microtune_4042);
@@ -112,11 +128,13 @@
 	.name  = "Thomson dtt761x",
 	.min   =  57000000,
 	.max   = 863000000,
+	.iffreq= 44000000,
 	.count = 3,
+	.initdata = tua603x_agc103,
 	.entries = {
-		{ 147000000, 44000000, 62500, 0x8e, 0x39 },
-		{ 417000000, 44000000, 62500, 0x8e, 0x3a },
-		{ 999999999, 44000000, 62500, 0x8e, 0x3c },
+		{ 147000000, 62500, 0x8e, 0x39 },
+		{ 417000000, 62500, 0x8e, 0x3a },
+		{ 999999999, 62500, 0x8e, 0x3c },
 	},
 };
 EXPORT_SYMBOL(dvb_pll_thomson_dtt761x);
@@ -125,17 +143,18 @@
 	.name  = "unknown 1", /* used by dntv live dvb-t */
 	.min   = 174000000,
 	.max   = 862000000,
+	.iffreq= 36166667,
 	.count = 9,
 	.entries = {
-		{  150000000, 36166667, 166666, 0xb4, 0x01 },
-		{  173000000, 36166667, 166666, 0xbc, 0x01 },
-		{  250000000, 36166667, 166666, 0xb4, 0x02 },
-		{  400000000, 36166667, 166666, 0xbc, 0x02 },
-		{  420000000, 36166667, 166666, 0xf4, 0x02 },
-		{  470000000, 36166667, 166666, 0xfc, 0x02 },
-		{  600000000, 36166667, 166666, 0xbc, 0x08 },
-		{  730000000, 36166667, 166666, 0xf4, 0x08 },
-		{  999999999, 36166667, 166666, 0xfc, 0x08 },
+		{  150000000, 166667, 0xb4, 0x01 },
+		{  173000000, 166667, 0xbc, 0x01 },
+		{  250000000, 166667, 0xb4, 0x02 },
+		{  400000000, 166667, 0xbc, 0x02 },
+		{  420000000, 166667, 0xf4, 0x02 },
+		{  470000000, 166667, 0xfc, 0x02 },
+		{  600000000, 166667, 0xbc, 0x08 },
+		{  730000000, 166667, 0xf4, 0x08 },
+		{  999999999, 166667, 0xfc, 0x08 },
 	},
 };
 EXPORT_SYMBOL(dvb_pll_unknown_1);
@@ -147,11 +166,12 @@
 	.name  = "Infineon TUA6010XS",
 	.min   =  44250000,
 	.max   = 858000000,
+	.iffreq= 36125000,
 	.count = 3,
 	.entries = {
-		{  115750000, 36125000, 62500, 0x8e, 0x03 },
-		{  403250000, 36125000, 62500, 0x8e, 0x06 },
-		{  999999999, 36125000, 62500, 0x8e, 0x85 },
+		{  115750000, 62500, 0x8e, 0x03 },
+		{  403250000, 62500, 0x8e, 0x06 },
+		{  999999999, 62500, 0x8e, 0x85 },
 	},
 };
 EXPORT_SYMBOL(dvb_pll_tua6010xs);
@@ -161,12 +181,13 @@
 	.name  = "Panasonic ENV57H1XD5",
 	.min   =  44250000,
 	.max   = 858000000,
+	.iffreq= 36125000,
 	.count = 4,
 	.entries = {
-		{  153000000, 36291666, 166666, 0xc2, 0x41 },
-		{  470000000, 36291666, 166666, 0xc2, 0x42 },
-		{  526000000, 36291666, 166666, 0xc2, 0x84 },
-		{  999999999, 36291666, 166666, 0xc2, 0xa4 },
+		{  153000000, 166667, 0xc2, 0x41 },
+		{  470000000, 166667, 0xc2, 0x42 },
+		{  526000000, 166667, 0xc2, 0x84 },
+		{  999999999, 166667, 0xc2, 0xa4 },
 	},
 };
 EXPORT_SYMBOL(dvb_pll_env57h1xd5);
@@ -185,20 +206,21 @@
 	.min   =  44250000,
 	.max   = 858000000,
 	.setbw = tda665x_bw,
+	.iffreq= 36166667,
 	.count = 12,
 	.entries = {
-		{   93834000, 36249333, 166667, 0xca, 0x61 /* 011 0 0 0  01 */ },
-		{  123834000, 36249333, 166667, 0xca, 0xa1 /* 101 0 0 0  01 */ },
-		{  161000000, 36249333, 166667, 0xca, 0xa1 /* 101 0 0 0  01 */ },
-		{  163834000, 36249333, 166667, 0xca, 0xc2 /* 110 0 0 0  10 */ },
-		{  253834000, 36249333, 166667, 0xca, 0x62 /* 011 0 0 0  10 */ },
-		{  383834000, 36249333, 166667, 0xca, 0xa2 /* 101 0 0 0  10 */ },
-		{  443834000, 36249333, 166667, 0xca, 0xc2 /* 110 0 0 0  10 */ },
-		{  444000000, 36249333, 166667, 0xca, 0xc4 /* 110 0 0 1  00 */ },
-		{  583834000, 36249333, 166667, 0xca, 0x64 /* 011 0 0 1  00 */ },
-		{  793834000, 36249333, 166667, 0xca, 0xa4 /* 101 0 0 1  00 */ },
-		{  444834000, 36249333, 166667, 0xca, 0xc4 /* 110 0 0 1  00 */ },
-		{  861000000, 36249333, 166667, 0xca, 0xe4 /* 111 0 0 1  00 */ },
+		{   93834000, 166667, 0xca, 0x61 /* 011 0 0 0  01 */ },
+		{  123834000, 166667, 0xca, 0xa1 /* 101 0 0 0  01 */ },
+		{  161000000, 166667, 0xca, 0xa1 /* 101 0 0 0  01 */ },
+		{  163834000, 166667, 0xca, 0xc2 /* 110 0 0 0  10 */ },
+		{  253834000, 166667, 0xca, 0x62 /* 011 0 0 0  10 */ },
+		{  383834000, 166667, 0xca, 0xa2 /* 101 0 0 0  10 */ },
+		{  443834000, 166667, 0xca, 0xc2 /* 110 0 0 0  10 */ },
+		{  444000000, 166667, 0xca, 0xc4 /* 110 0 0 1  00 */ },
+		{  583834000, 166667, 0xca, 0x64 /* 011 0 0 1  00 */ },
+		{  793834000, 166667, 0xca, 0xa4 /* 101 0 0 1  00 */ },
+		{  444834000, 166667, 0xca, 0xc4 /* 110 0 0 1  00 */ },
+		{  861000000, 166667, 0xca, 0xe4 /* 111 0 0 1  00 */ },
 	}
 };
 EXPORT_SYMBOL(dvb_pll_tda665x);
@@ -216,12 +238,13 @@
 	.name  = "Infineon TUA6034",
 	.min   =  44250000,
 	.max   = 858000000,
+	.iffreq= 36166667,
 	.count = 3,
 	.setbw = tua6034_bw,
 	.entries = {
-		{  174500000, 36166667, 62500, 0xce, 0x01 },
-		{  230000000, 36166667, 62500, 0xce, 0x02 },
-		{  999999999, 36166667, 62500, 0xce, 0x04 },
+		{  174500000, 62500, 0xce, 0x01 },
+		{  230000000, 62500, 0xce, 0x02 },
+		{  999999999, 62500, 0xce, 0x04 },
 	},
 };
 EXPORT_SYMBOL(dvb_pll_tua6034);
@@ -233,11 +256,13 @@
 	.name  = "LG TDVS-H06xF",
 	.min   =  54000000,
 	.max   = 863000000,
+	.iffreq= 44000000,
+	.initdata = tua603x_agc103,
 	.count = 3,
 	.entries = {
-		{  165000000, 44000000, 62500, 0xce, 0x01 },
-		{  450000000, 44000000, 62500, 0xce, 0x02 },
-		{  999999999, 44000000, 62500, 0xce, 0x04 },
+		{  165000000, 62500, 0xce, 0x01 },
+		{  450000000, 62500, 0xce, 0x02 },
+		{  999999999, 62500, 0xce, 0x04 },
 	},
 };
 EXPORT_SYMBOL(dvb_pll_lg_tdvs_h06xf);
@@ -255,16 +280,17 @@
 	.name = "Philips FMD1216ME",
 	.min = 50870000,
 	.max = 858000000,
+	.iffreq= 36125000,
 	.setbw = fmd1216me_bw,
 	.count = 7,
 	.entries = {
-		{ 143870000, 36213333, 166667, 0xbc, 0x41 },
-		{ 158870000, 36213333, 166667, 0xf4, 0x41 },
-		{ 329870000, 36213333, 166667, 0xbc, 0x42 },
-		{ 441870000, 36213333, 166667, 0xf4, 0x42 },
-		{ 625870000, 36213333, 166667, 0xbc, 0x44 },
-		{ 803870000, 36213333, 166667, 0xf4, 0x44 },
-		{ 999999999, 36213333, 166667, 0xfc, 0x44 },
+		{ 143870000, 166667, 0xbc, 0x41 },
+		{ 158870000, 166667, 0xf4, 0x41 },
+		{ 329870000, 166667, 0xbc, 0x42 },
+		{ 441870000, 166667, 0xf4, 0x42 },
+		{ 625870000, 166667, 0xbc, 0x44 },
+		{ 803870000, 166667, 0xf4, 0x44 },
+		{ 999999999, 166667, 0xfc, 0x44 },
 	}
 };
 EXPORT_SYMBOL(dvb_pll_fmd1216me);
@@ -282,13 +308,14 @@
 	.name = "ALPS TDED4",
 	.min = 47000000,
 	.max = 863000000,
+	.iffreq= 36166667,
 	.setbw = tded4_bw,
 	.count = 4,
 	.entries = {
-		{ 153000000, 36166667, 166667, 0x85, 0x01 },
-		{ 470000000, 36166667, 166667, 0x85, 0x02 },
-		{ 823000000, 36166667, 166667, 0x85, 0x08 },
-		{ 999999999, 36166667, 166667, 0x85, 0x88 },
+		{ 153000000, 166667, 0x85, 0x01 },
+		{ 470000000, 166667, 0x85, 0x02 },
+		{ 823000000, 166667, 0x85, 0x08 },
+		{ 999999999, 166667, 0x85, 0x88 },
 	}
 };
 EXPORT_SYMBOL(dvb_pll_tded4);
@@ -300,12 +327,13 @@
 	.name = "ALPS TDHU2",
 	.min = 54000000,
 	.max = 864000000,
+	.iffreq= 44000000,
 	.count = 4,
 	.entries = {
-		{ 162000000, 44000000, 62500, 0x85, 0x01 },
-		{ 426000000, 44000000, 62500, 0x85, 0x02 },
-		{ 782000000, 44000000, 62500, 0x85, 0x08 },
-		{ 999999999, 44000000, 62500, 0x85, 0x88 },
+		{ 162000000, 62500, 0x85, 0x01 },
+		{ 426000000, 62500, 0x85, 0x02 },
+		{ 782000000, 62500, 0x85, 0x08 },
+		{ 999999999, 62500, 0x85, 0x88 },
 	}
 };
 EXPORT_SYMBOL(dvb_pll_tdhu2);
@@ -317,11 +345,12 @@
 	.name  = "Philips TUV1236D",
 	.min   =  54000000,
 	.max   = 864000000,
+	.iffreq= 44000000,
 	.count = 3,
 	.entries = {
-		{ 157250000, 44000000, 62500, 0xc6, 0x41 },
-		{ 454000000, 44000000, 62500, 0xc6, 0x42 },
-		{ 999999999, 44000000, 62500, 0xc6, 0x44 },
+		{ 157250000, 62500, 0xc6, 0x41 },
+		{ 454000000, 62500, 0xc6, 0x42 },
+		{ 999999999, 62500, 0xc6, 0x44 },
 	},
 };
 EXPORT_SYMBOL(dvb_pll_tuv1236d);
@@ -333,14 +362,15 @@
 	.name = "Samsung TBMV30111IN / TBMV30712IN1",
 	.min = 54000000,
 	.max = 860000000,
+	.iffreq= 44000000,
 	.count = 6,
 	.entries = {
-		{ 172000000, 44000000, 166666, 0xb4, 0x01 },
-		{ 214000000, 44000000, 166666, 0xb4, 0x02 },
-		{ 467000000, 44000000, 166666, 0xbc, 0x02 },
-		{ 721000000, 44000000, 166666, 0xbc, 0x08 },
-		{ 841000000, 44000000, 166666, 0xf4, 0x08 },
-		{ 999999999, 44000000, 166666, 0xfc, 0x02 },
+		{ 172000000, 166667, 0xb4, 0x01 },
+		{ 214000000, 166667, 0xb4, 0x02 },
+		{ 467000000, 166667, 0xbc, 0x02 },
+		{ 721000000, 166667, 0xbc, 0x08 },
+		{ 841000000, 166667, 0xf4, 0x08 },
+		{ 999999999, 166667, 0xfc, 0x02 },
 	}
 };
 EXPORT_SYMBOL(dvb_pll_samsung_tbmv);
@@ -352,12 +382,13 @@
 	.name  = "Philips SD1878",
 	.min   =  950000,
 	.max   = 2150000,
+	.iffreq= 249, /* zero-IF, offset 249 is to round up */
 	.count = 4,
 	.entries = {
-		{ 1250000, 499, 500, 0xc4, 0x00},
-		{ 1550000, 499, 500, 0xc4, 0x40},
-		{ 2050000, 499, 500, 0xc4, 0x80},
-		{ 2150000, 499, 500, 0xc4, 0xc0},
+		{ 1250000, 500, 0xc4, 0x00},
+		{ 1550000, 500, 0xc4, 0x40},
+		{ 2050000, 500, 0xc4, 0x80},
+		{ 2150000, 500, 0xc4, 0xc0},
 	},
 };
 EXPORT_SYMBOL(dvb_pll_philips_sd1878_tda8261);
@@ -388,18 +419,19 @@
 	.name  = "Philips TD1316",
 	.min   =  87000000,
 	.max   = 895000000,
+	.iffreq= 36166667,
 	.setbw = td1316_bw,
 	.count = 9,
 	.entries = {
-		{  93834000, 36166000, 166666, 0xca, 0x60},
-		{ 123834000, 36166000, 166666, 0xca, 0xa0},
-		{ 163834000, 36166000, 166666, 0xca, 0xc0},
-		{ 253834000, 36166000, 166666, 0xca, 0x60},
-		{ 383834000, 36166000, 166666, 0xca, 0xa0},
-		{ 443834000, 36166000, 166666, 0xca, 0xc0},
-		{ 583834000, 36166000, 166666, 0xca, 0x60},
-		{ 793834000, 36166000, 166666, 0xca, 0xa0},
-		{ 858834000, 36166000, 166666, 0xca, 0xe0},
+		{  93834000, 166667, 0xca, 0x60},
+		{ 123834000, 166667, 0xca, 0xa0},
+		{ 163834000, 166667, 0xca, 0xc0},
+		{ 253834000, 166667, 0xca, 0x60},
+		{ 383834000, 166667, 0xca, 0xa0},
+		{ 443834000, 166667, 0xca, 0xc0},
+		{ 583834000, 166667, 0xca, 0x60},
+		{ 793834000, 166667, 0xca, 0xa0},
+		{ 858834000, 166667, 0xca, 0xe0},
 	},
 };
 EXPORT_SYMBOL(dvb_pll_philips_td1316);
@@ -409,15 +441,41 @@
 	.name = "Thomson FE6600",
 	.min =  44250000,
 	.max = 858000000,
+	.iffreq= 36125000,
 	.count = 4,
 	.entries = {
-		{ 250000000, 36213333, 166667, 0xb4, 0x12 },
-		{ 455000000, 36213333, 166667, 0xfe, 0x11 },
-		{ 775500000, 36213333, 166667, 0xbc, 0x18 },
-		{ 999999999, 36213333, 166667, 0xf4, 0x18 },
+		{ 250000000, 166667, 0xb4, 0x12 },
+		{ 455000000, 166667, 0xfe, 0x11 },
+		{ 775500000, 166667, 0xbc, 0x18 },
+		{ 999999999, 166667, 0xf4, 0x18 },
 	}
 };
 EXPORT_SYMBOL(dvb_pll_thomson_fe6600);
+static void opera1_bw(u8 *buf, u32 freq, int bandwidth)
+{
+	if (bandwidth == BANDWIDTH_8_MHZ)
+		buf[2] |= 0x08;
+}
+
+struct dvb_pll_desc dvb_pll_opera1 = {
+	.name  = "Opera Tuner",
+	.min   =  900000,
+	.max   = 2250000,
+	.iffreq= 0,
+	.setbw = opera1_bw,
+	.count = 8,
+	.entries = {
+		{ 1064000, 500, 0xe5, 0xc6 },
+		{ 1169000, 500, 0xe5, 0xe6 },
+		{ 1299000, 500, 0xe5, 0x24 },
+		{ 1444000, 500, 0xe5, 0x44 },
+		{ 1606000, 500, 0xe5, 0x64 },
+		{ 1777000, 500, 0xe5, 0x84 },
+		{ 1941000, 500, 0xe5, 0xa4 },
+		{ 2250000, 500, 0xe5, 0xc4 },
+	}
+};
+EXPORT_SYMBOL(dvb_pll_opera1);
 
 struct dvb_pll_priv {
 	/* i2c details */
@@ -459,7 +517,8 @@
 	if (i == desc->count)
 		return -EINVAL;
 
-	div = (freq + desc->entries[i].offset) / desc->entries[i].stepsize;
+	div = (freq + desc->iffreq + desc->entries[i].stepsize/2) /
+	      desc->entries[i].stepsize;
 	buf[0] = div >> 8;
 	buf[1] = div & 0xff;
 	buf[2] = desc->entries[i].config;
@@ -473,7 +532,7 @@
 		       desc->name, div, buf[0], buf[1], buf[2], buf[3]);
 
 	// calculate the frequency we set it to
-	return (div * desc->entries[i].stepsize) - desc->entries[i].offset;
+	return (div * desc->entries[i].stepsize) - desc->iffreq;
 }
 EXPORT_SYMBOL(dvb_pll_configure);
 
@@ -487,35 +546,27 @@
 static int dvb_pll_sleep(struct dvb_frontend *fe)
 {
 	struct dvb_pll_priv *priv = fe->tuner_priv;
-	u8 buf[4];
-	struct i2c_msg msg =
-		{ .addr = priv->pll_i2c_address, .flags = 0,
-		  .buf = buf, .len = sizeof(buf) };
-	int i;
-	int result;
 
 	if (priv->i2c == NULL)
 		return -EINVAL;
 
-	for (i = 0; i < priv->pll_desc->count; i++) {
-		if (priv->pll_desc->entries[i].limit == 0)
-			break;
-	}
-	if (i == priv->pll_desc->count)
+	if (priv->pll_desc->sleepdata) {
+		struct i2c_msg msg = { .flags = 0,
+			.addr = priv->pll_i2c_address,
+			.buf = priv->pll_desc->sleepdata + 1,
+			.len = priv->pll_desc->sleepdata[0] };
+
+		int result;
+
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 1);
+		if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
+			return result;
+		}
 		return 0;
-
-	buf[0] = 0;
-	buf[1] = 0;
-	buf[2] = priv->pll_desc->entries[i].config;
-	buf[3] = priv->pll_desc->entries[i].cb;
-
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
-		return result;
 	}
-
-	return 0;
+	/* Shouldn't be called when initdata is NULL, maybe BUG()? */
+	return -EINVAL;
 }
 
 static int dvb_pll_set_params(struct dvb_frontend *fe,
@@ -599,9 +650,35 @@
 	return 0;
 }
 
+static int dvb_pll_init(struct dvb_frontend *fe)
+{
+	struct dvb_pll_priv *priv = fe->tuner_priv;
+
+	if (priv->i2c == NULL)
+		return -EINVAL;
+
+	if (priv->pll_desc->initdata) {
+		struct i2c_msg msg = { .flags = 0,
+			.addr = priv->pll_i2c_address,
+			.buf = priv->pll_desc->initdata + 1,
+			.len = priv->pll_desc->initdata[0] };
+
+		int result;
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 1);
+		if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
+			return result;
+		}
+		return 0;
+	}
+	/* Shouldn't be called when initdata is NULL, maybe BUG()? */
+	return -EINVAL;
+}
+
 static struct dvb_tuner_ops dvb_pll_tuner_ops = {
 	.release = dvb_pll_release,
 	.sleep = dvb_pll_sleep,
+	.init = dvb_pll_init,
 	.set_params = dvb_pll_set_params,
 	.calc_regs = dvb_pll_calc_regs,
 	.get_frequency = dvb_pll_get_frequency,
@@ -640,9 +717,14 @@
 	memcpy(&fe->ops.tuner_ops, &dvb_pll_tuner_ops,
 	       sizeof(struct dvb_tuner_ops));
 
-	strncpy(fe->ops.tuner_ops.info.name, desc->name, 128);
+	strncpy(fe->ops.tuner_ops.info.name, desc->name,
+		sizeof(fe->ops.tuner_ops.info.name));
 	fe->ops.tuner_ops.info.frequency_min = desc->min;
 	fe->ops.tuner_ops.info.frequency_min = desc->max;
+	if (!desc->initdata)
+		fe->ops.tuner_ops.init = NULL;
+	if (!desc->sleepdata)
+		fe->ops.tuner_ops.sleep = NULL;
 
 	fe->tuner_priv = priv;
 	return fe;
diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h
index 681186a..5209f46 100644
--- a/drivers/media/dvb/frontends/dvb-pll.h
+++ b/drivers/media/dvb/frontends/dvb-pll.h
@@ -12,11 +12,13 @@
 	char *name;
 	u32  min;
 	u32  max;
+	u32  iffreq;
 	void (*setbw)(u8 *buf, u32 freq, int bandwidth);
+	u8   *initdata;
+	u8   *sleepdata;
 	int  count;
 	struct {
 		u32 limit;
-		u32 offset;
 		u32 stepsize;
 		u8  config;
 		u8  cb;
@@ -46,6 +48,7 @@
 extern struct dvb_pll_desc dvb_pll_philips_td1316;
 
 extern struct dvb_pll_desc dvb_pll_thomson_fe6600;
+extern struct dvb_pll_desc dvb_pll_opera1;
 
 extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
 			     u32 freq, int bandwidth);
@@ -59,9 +62,20 @@
  * @param desc dvb_pll_desc to use.
  * @return Frontend pointer on success, NULL on failure
  */
+#if defined(CONFIG_DVB_PLL) || (defined(CONFIG_DVB_PLL_MODULE) && defined(MODULE))
 extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe,
 					   int pll_addr,
 					   struct i2c_adapter *i2c,
 					   struct dvb_pll_desc *desc);
+#else
+static inline struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe,
+					   int pll_addr,
+					   struct i2c_adapter *i2c,
+					   struct dvb_pll_desc *desc)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+	return NULL;
+}
+#endif
 
 #endif
diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb/frontends/lgdt330x.c
index 68aad0f..e25286e 100644
--- a/drivers/media/dvb/frontends/lgdt330x.c
+++ b/drivers/media/dvb/frontends/lgdt330x.c
@@ -475,7 +475,7 @@
 			*status |= FE_HAS_CARRIER;
 		break;
 	default:
-		printk("KERN_WARNING lgdt330x: %s: Modulation set to unsupported value\n", __FUNCTION__);
+		printk(KERN_WARNING "lgdt330x: %s: Modulation set to unsupported value\n", __FUNCTION__);
 	}
 
 	return 0;
@@ -534,7 +534,7 @@
 		}
 		break;
 	default:
-		printk("KERN_WARNING lgdt330x: %s: Modulation set to unsupported value\n", __FUNCTION__);
+		printk(KERN_WARNING "lgdt330x: %s: Modulation set to unsupported value\n", __FUNCTION__);
 	}
 	return 0;
 }
diff --git a/drivers/media/dvb/frontends/lgh06xf.c b/drivers/media/dvb/frontends/lgh06xf.c
deleted file mode 100644
index 2202d0c..0000000
--- a/drivers/media/dvb/frontends/lgh06xf.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- *  lgh06xf.c - ATSC Tuner support for LG TDVS-H06xF
- *
- *  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 "dvb-pll.h"
-#include "lgh06xf.h"
-
-#define LG_H06XF_PLL_I2C_ADDR 0x61
-
-struct lgh06xf_priv {
-	struct i2c_adapter *i2c;
-	u32 frequency;
-};
-
-static int lgh06xf_release(struct dvb_frontend *fe)
-{
-	kfree(fe->tuner_priv);
-	fe->tuner_priv = NULL;
-	return 0;
-}
-
-static int lgh06xf_set_params(struct dvb_frontend* fe,
-			      struct dvb_frontend_parameters* params)
-{
-	struct lgh06xf_priv *priv = fe->tuner_priv;
-	u8 buf[4];
-	struct i2c_msg msg = { .addr = LG_H06XF_PLL_I2C_ADDR, .flags = 0,
-			       .buf = buf, .len = sizeof(buf) };
-	u32 frequency;
-	int result;
-
-	if ((result = dvb_pll_configure(&dvb_pll_lg_tdvs_h06xf, buf,
-					params->frequency, 0)) < 0)
-		return result;
-	else
-		frequency = result;
-
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
-		printk(KERN_WARNING "lgh06xf: %s error "
-		       "(addr %02x <- %02x, result = %i)\n",
-		       __FUNCTION__, buf[0], buf[1], result);
-		if (result < 0)
-			return result;
-		else
-			return -EREMOTEIO;
-	}
-
-	/* Set the Auxiliary Byte. */
-	buf[0] = buf[2];
-	buf[0] &= ~0x20;
-	buf[0] |= 0x18;
-	buf[1] = 0x50;
-	msg.len = 2;
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
-		printk(KERN_WARNING "lgh06xf: %s error "
-		       "(addr %02x <- %02x, result = %i)\n",
-		       __FUNCTION__, buf[0], buf[1], result);
-		if (result < 0)
-			return result;
-		else
-			return -EREMOTEIO;
-	}
-
-	priv->frequency = frequency;
-
-	return 0;
-}
-
-static int lgh06xf_get_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
-	struct lgh06xf_priv *priv = fe->tuner_priv;
-	*frequency = priv->frequency;
-	return 0;
-}
-
-static struct dvb_tuner_ops lgh06xf_tuner_ops = {
-	.release       = lgh06xf_release,
-	.set_params    = lgh06xf_set_params,
-	.get_frequency = lgh06xf_get_frequency,
-};
-
-struct dvb_frontend* lgh06xf_attach(struct dvb_frontend *fe,
-				    struct i2c_adapter *i2c)
-{
-	struct lgh06xf_priv *priv = NULL;
-
-	priv = kzalloc(sizeof(struct lgh06xf_priv), GFP_KERNEL);
-	if (priv == NULL)
-		return NULL;
-
-	priv->i2c = i2c;
-
-	memcpy(&fe->ops.tuner_ops, &lgh06xf_tuner_ops,
-	       sizeof(struct dvb_tuner_ops));
-
-	strlcpy(fe->ops.tuner_ops.info.name, dvb_pll_lg_tdvs_h06xf.name,
-		sizeof(fe->ops.tuner_ops.info.name));
-
-	fe->ops.tuner_ops.info.frequency_min = dvb_pll_lg_tdvs_h06xf.min;
-	fe->ops.tuner_ops.info.frequency_max = dvb_pll_lg_tdvs_h06xf.max;
-
-	fe->tuner_priv = priv;
-	return fe;
-}
-
-EXPORT_SYMBOL(lgh06xf_attach);
-
-MODULE_DESCRIPTION("LG TDVS-H06xF ATSC Tuner support");
-MODULE_AUTHOR("Michael Krufky");
-MODULE_LICENSE("GPL");
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/dvb/frontends/lgh06xf.h b/drivers/media/dvb/frontends/lgh06xf.h
deleted file mode 100644
index 510b4be..0000000
--- a/drivers/media/dvb/frontends/lgh06xf.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- *  lgh06xf.h - ATSC Tuner support for LG TDVS-H06xF
- *
- *  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.
- */
-
-#ifndef _LGH06XF_H_
-#define _LGH06XF_H_
-#include "dvb_frontend.h"
-
-#if defined(CONFIG_DVB_TUNER_LGH06XF) || (defined(CONFIG_DVB_TUNER_LGH06XF_MODULE) && defined(MODULE))
-extern struct dvb_frontend* lgh06xf_attach(struct dvb_frontend* fe,
-					    struct i2c_adapter *i2c);
-#else
-static inline struct dvb_frontend* lgh06xf_attach(struct dvb_frontend* fe,
-						  struct i2c_adapter *i2c)
-{
-	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
-	return NULL;
-}
-#endif /* CONFIG_DVB_TUNER_LGH06XF */
-
-#endif /* _LGH06XF_H_ */
diff --git a/drivers/media/dvb/frontends/or51132.c b/drivers/media/dvb/frontends/or51132.c
index 5a3a6e5..4e0aca7 100644
--- a/drivers/media/dvb/frontends/or51132.c
+++ b/drivers/media/dvb/frontends/or51132.c
@@ -1,6 +1,9 @@
 /*
  *    Support for OR51132 (pcHDTV HD-3000) - VSB/QAM
  *
+ *
+ *    Copyright (C) 2007 Trent Piepho <xyzzy@speakeasy.org>
+ *
  *    Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com>
  *
  *    Based on code from Jack Kelliher (kelliher@xmission.com)
@@ -69,46 +72,70 @@
 	u32 current_frequency;
 };
 
-static int i2c_writebytes (struct or51132_state* state, u8 reg, u8 *buf, int len)
+
+/* Write buffer to demod */
+static int or51132_writebuf(struct or51132_state *state, const u8 *buf, int len)
 {
 	int err;
-	struct i2c_msg msg;
-	msg.addr  = reg;
-	msg.flags = 0;
-	msg.len   = len;
-	msg.buf   = buf;
+	struct i2c_msg msg = { .addr = state->config->demod_address,
+			       .flags = 0, .buf = (u8*)buf, .len = len };
 
+	/* msleep(20); */ /* doesn't appear to be necessary */
 	if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
-		printk(KERN_WARNING "or51132: i2c_writebytes error (addr %02x, err == %i)\n", reg, err);
+		printk(KERN_WARNING "or51132: I2C write (addr 0x%02x len %d) error: %d\n",
+		       msg.addr, msg.len, err);
 		return -EREMOTEIO;
 	}
-
 	return 0;
 }
 
-static u8 i2c_readbytes (struct or51132_state* state, u8 reg, u8* buf, int len)
+/* Write constant bytes, e.g. or51132_writebytes(state, 0x04, 0x42, 0x00);
+   Less code and more efficient that loading a buffer on the stack with
+   the bytes to send and then calling or51132_writebuf() on that. */
+#define or51132_writebytes(state, data...)  \
+	({ const static u8 _data[] = {data}; \
+	or51132_writebuf(state, _data, sizeof(_data)); })
+
+/* Read data from demod into buffer.  Returns 0 on success. */
+static int or51132_readbuf(struct or51132_state *state, u8 *buf, int len)
 {
 	int err;
-	struct i2c_msg msg;
-	msg.addr   = reg;
-	msg.flags = I2C_M_RD;
-	msg.len = len;
-	msg.buf = buf;
+	struct i2c_msg msg = { .addr = state->config->demod_address,
+			       .flags = I2C_M_RD, .buf = buf, .len = len };
 
+	/* msleep(20); */ /* doesn't appear to be necessary */
 	if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
-		printk(KERN_WARNING "or51132: i2c_readbytes error (addr %02x, err == %i)\n", reg, err);
+		printk(KERN_WARNING "or51132: I2C read (addr 0x%02x len %d) error: %d\n",
+		       msg.addr, msg.len, err);
 		return -EREMOTEIO;
 	}
-
 	return 0;
 }
 
+/* Reads a 16-bit demod register.  Returns <0 on error. */
+static int or51132_readreg(struct or51132_state *state, u8 reg)
+{
+	u8 buf[2] = { 0x04, reg };
+	struct i2c_msg msg[2] = {
+		{.addr = state->config->demod_address, .flags = 0,
+		 .buf = buf, .len = 2 },
+		{.addr = state->config->demod_address, .flags = I2C_M_RD,
+		 .buf = buf, .len = 2 }};
+	int err;
+
+	if ((err = i2c_transfer(state->i2c, msg, 2)) != 2) {
+		printk(KERN_WARNING "or51132: I2C error reading register %d: %d\n",
+		       reg, err);
+		return -EREMOTEIO;
+	}
+	return le16_to_cpup((u16*)buf);
+}
+
 static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware *fw)
 {
 	struct or51132_state* state = fe->demodulator_priv;
-	static u8 run_buf[] = {0x7F,0x01};
+	const static u8 run_buf[] = {0x7F,0x01};
 	u8 rec_buf[8];
-	u8 cmd_buf[3];
 	u32 firmwareAsize, firmwareBsize;
 	int i,ret;
 
@@ -121,30 +148,21 @@
 	dprintk("FirmwareB is %i bytes\n",firmwareBsize);
 
 	/* Upload firmware */
-	if ((ret = i2c_writebytes(state,state->config->demod_address,
-				 &fw->data[8],firmwareAsize))) {
+	if ((ret = or51132_writebuf(state, &fw->data[8], firmwareAsize))) {
 		printk(KERN_WARNING "or51132: load_firmware error 1\n");
 		return ret;
 	}
-	msleep(1); /* 1ms */
-	if ((ret = i2c_writebytes(state,state->config->demod_address,
-				 &fw->data[8+firmwareAsize],firmwareBsize))) {
+	if ((ret = or51132_writebuf(state, &fw->data[8+firmwareAsize],
+				    firmwareBsize))) {
 		printk(KERN_WARNING "or51132: load_firmware error 2\n");
 		return ret;
 	}
-	msleep(1); /* 1ms */
 
-	if ((ret = i2c_writebytes(state,state->config->demod_address,
-				 run_buf,2))) {
+	if ((ret = or51132_writebuf(state, run_buf, 2))) {
 		printk(KERN_WARNING "or51132: load_firmware error 3\n");
 		return ret;
 	}
-
-	/* Wait at least 5 msec */
-	msleep(20); /* 10ms */
-
-	if ((ret = i2c_writebytes(state,state->config->demod_address,
-				 run_buf,2))) {
+	if ((ret = or51132_writebuf(state, run_buf, 2))) {
 		printk(KERN_WARNING "or51132: load_firmware error 4\n");
 		return ret;
 	}
@@ -154,43 +172,25 @@
 
 	/* Read back ucode version to besure we loaded correctly and are really up and running */
 	/* Get uCode version */
-	cmd_buf[0] = 0x10;
-	cmd_buf[1] = 0x10;
-	cmd_buf[2] = 0x00;
-	msleep(20); /* 20ms */
-	if ((ret = i2c_writebytes(state,state->config->demod_address,
-				 cmd_buf,3))) {
+	if ((ret = or51132_writebytes(state, 0x10, 0x10, 0x00))) {
 		printk(KERN_WARNING "or51132: load_firmware error a\n");
 		return ret;
 	}
-
-	cmd_buf[0] = 0x04;
-	cmd_buf[1] = 0x17;
-	msleep(20); /* 20ms */
-	if ((ret = i2c_writebytes(state,state->config->demod_address,
-				 cmd_buf,2))) {
+	if ((ret = or51132_writebytes(state, 0x04, 0x17))) {
 		printk(KERN_WARNING "or51132: load_firmware error b\n");
 		return ret;
 	}
-
-	cmd_buf[0] = 0x00;
-	cmd_buf[1] = 0x00;
-	msleep(20); /* 20ms */
-	if ((ret = i2c_writebytes(state,state->config->demod_address,
-				 cmd_buf,2))) {
+	if ((ret = or51132_writebytes(state, 0x00, 0x00))) {
 		printk(KERN_WARNING "or51132: load_firmware error c\n");
 		return ret;
 	}
-
-	for(i=0;i<4;i++) {
-		msleep(20); /* 20ms */
+	for (i=0;i<4;i++) {
 		/* Once upon a time, this command might have had something
 		   to do with getting the firmware version, but it's
 		   not used anymore:
 		   {0x04,0x00,0x30,0x00,i+1} */
 		/* Read 8 bytes, two bytes at a time */
-		if ((ret = i2c_readbytes(state,state->config->demod_address,
-					&rec_buf[i*2],2))) {
+		if ((ret = or51132_readbuf(state, &rec_buf[i*2], 2))) {
 			printk(KERN_WARNING
 			       "or51132: load_firmware error d - %d\n",i);
 			return ret;
@@ -204,12 +204,7 @@
 	       rec_buf[3],rec_buf[2]>>4,rec_buf[2]&0x0f,
 	       rec_buf[5],rec_buf[4]>>4,rec_buf[4]&0x0f);
 
-	cmd_buf[0] = 0x10;
-	cmd_buf[1] = 0x00;
-	cmd_buf[2] = 0x00;
-	msleep(20); /* 20ms */
-	if ((ret = i2c_writebytes(state,state->config->demod_address,
-				 cmd_buf,3))) {
+	if ((ret = or51132_writebytes(state, 0x10, 0x00, 0x00))) {
 		printk(KERN_WARNING "or51132: load_firmware error e\n");
 		return ret;
 	}
@@ -241,70 +236,55 @@
 static int or51132_setmode(struct dvb_frontend* fe)
 {
 	struct or51132_state* state = fe->demodulator_priv;
-	unsigned char cmd_buf[3];
+	u8 cmd_buf1[3] = {0x04, 0x01, 0x5f};
+	u8 cmd_buf2[3] = {0x1c, 0x00, 0 };
 
 	dprintk("setmode %d\n",(int)state->current_modulation);
-	/* set operation mode in Receiver 1 register; */
-	cmd_buf[0] = 0x04;
-	cmd_buf[1] = 0x01;
+
 	switch (state->current_modulation) {
-	case QAM_256:
-	case QAM_64:
-	case QAM_AUTO:
-		/* Auto-deinterleave; MPEG ser, MPEG2tr, phase noise-high*/
-		cmd_buf[2] = 0x5F;
-		break;
 	case VSB_8:
-		/* Auto CH, Auto NTSC rej, MPEGser, MPEG2tr, phase noise-high*/
-		cmd_buf[2] = 0x50;
+		/* Auto CH, Auto NTSC rej, MPEGser, MPEG2tr, phase noise-high */
+		cmd_buf1[2] = 0x50;
+		/* REC MODE inv IF spectrum, Normal */
+		cmd_buf2[1] = 0x03;
+		/* Channel MODE ATSC/VSB8 */
+		cmd_buf2[2] = 0x06;
+		break;
+	/* All QAM modes are:
+	   Auto-deinterleave; MPEGser, MPEG2tr, phase noise-high
+	   REC MODE Normal Carrier Lock */
+	case QAM_AUTO:
+		/* Channel MODE Auto QAM64/256 */
+		cmd_buf2[2] = 0x4f;
+		break;
+	case QAM_256:
+		/* Channel MODE QAM256 */
+		cmd_buf2[2] = 0x45;
+		break;
+	case QAM_64:
+		/* Channel MODE QAM64 */
+		cmd_buf2[2] = 0x43;
 		break;
 	default:
-		printk("setmode:Modulation set to unsupported value\n");
-	};
-	if (i2c_writebytes(state,state->config->demod_address,
-			   cmd_buf,3)) {
-		printk(KERN_WARNING "or51132: set_mode error 1\n");
-		return -1;
+		printk(KERN_WARNING
+		       "or51132: setmode: Modulation set to unsupported value (%d)\n",
+		       state->current_modulation);
+		return -EINVAL;
 	}
-	dprintk("or51132: set #1 to %02x\n", cmd_buf[2]);
+
+	/* Set Receiver 1 register */
+	if (or51132_writebuf(state, cmd_buf1, 3)) {
+		printk(KERN_WARNING "or51132: set_mode error 1\n");
+		return -EREMOTEIO;
+	}
+	dprintk("set #1 to %02x\n", cmd_buf1[2]);
 
 	/* Set operation mode in Receiver 6 register */
-	cmd_buf[0] = 0x1C;
-	switch (state->current_modulation) {
-	case QAM_AUTO:
-		/* REC MODE Normal Carrier Lock */
-		cmd_buf[1] = 0x00;
-		/* Channel MODE Auto QAM64/256 */
-		cmd_buf[2] = 0x4f;
-		break;
-	case QAM_256:
-		/* REC MODE Normal Carrier Lock */
-		cmd_buf[1] = 0x00;
-		/* Channel MODE QAM256 */
-		cmd_buf[2] = 0x45;
-		break;
-	case QAM_64:
-		/* REC MODE Normal Carrier Lock */
-		cmd_buf[1] = 0x00;
-		/* Channel MODE QAM64 */
-		cmd_buf[2] = 0x43;
-		break;
-	case VSB_8:
-		 /* REC MODE inv IF spectrum, Normal */
-		cmd_buf[1] = 0x03;
-		/* Channel MODE ATSC/VSB8 */
-		cmd_buf[2] = 0x06;
-		break;
-	default:
-		printk("setmode: Modulation set to unsupported value\n");
-	};
-	msleep(20); /* 20ms */
-	if (i2c_writebytes(state,state->config->demod_address,
-			   cmd_buf,3)) {
+	if (or51132_writebuf(state, cmd_buf2, 3)) {
 		printk(KERN_WARNING "or51132: set_mode error 2\n");
-		return -1;
+		return -EREMOTEIO;
 	}
-	dprintk("or51132: set #6 to 0x%02x%02x\n", cmd_buf[1], cmd_buf[2]);
+	dprintk("set #6 to 0x%02x%02x\n", cmd_buf2[1], cmd_buf2[2]);
 
 	return 0;
 }
@@ -401,28 +381,23 @@
 				  struct dvb_frontend_parameters *param)
 {
 	struct or51132_state* state = fe->demodulator_priv;
-	u8 buf[2];
+	int status;
+	int retry = 1;
 
+start:
 	/* Receiver Status */
-	buf[0]=0x04;
-	buf[1]=0x00;
-	msleep(30); /* 30ms */
-	if (i2c_writebytes(state,state->config->demod_address,buf,2)) {
-		printk(KERN_WARNING "or51132: get_parameters write error\n");
+	if ((status = or51132_readreg(state, 0x00)) < 0) {
+		printk(KERN_WARNING "or51132: get_parameters: error reading receiver status\n");
 		return -EREMOTEIO;
 	}
-	msleep(30); /* 30ms */
-	if (i2c_readbytes(state,state->config->demod_address,buf,2)) {
-		printk(KERN_WARNING "or51132: get_parameters read error\n");
-		return -EREMOTEIO;
-	}
-	switch(buf[0]) {
+	switch(status&0xff) {
 		case 0x06: param->u.vsb.modulation = VSB_8; break;
 		case 0x43: param->u.vsb.modulation = QAM_64; break;
 		case 0x45: param->u.vsb.modulation = QAM_256; break;
 		default:
+			if (retry--) goto start;
 			printk(KERN_WARNING "or51132: unknown status 0x%02x\n",
-			       buf[0]);
+			       status&0xff);
 			return -EREMOTEIO;
 	}
 
@@ -438,32 +413,21 @@
 static int or51132_read_status(struct dvb_frontend* fe, fe_status_t* status)
 {
 	struct or51132_state* state = fe->demodulator_priv;
-	unsigned char rec_buf[2];
-	unsigned char snd_buf[2];
-	*status = 0;
+	int reg;
 
 	/* Receiver Status */
-	snd_buf[0]=0x04;
-	snd_buf[1]=0x00;
-	msleep(30); /* 30ms */
-	if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
-		printk(KERN_WARNING "or51132: read_status write error\n");
-		return -1;
+	if ((reg = or51132_readreg(state, 0x00)) < 0) {
+		printk(KERN_WARNING "or51132: read_status: error reading receiver status: %d\n", reg);
+		*status = 0;
+		return -EREMOTEIO;
 	}
-	msleep(30); /* 30ms */
-	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
-		printk(KERN_WARNING "or51132: read_status read error\n");
-		return -1;
-	}
-	dprintk("read_status %x %x\n",rec_buf[0],rec_buf[1]);
+	dprintk("%s: read_status %04x\n", __FUNCTION__, reg);
 
-	if (rec_buf[1] & 0x01) { /* Receiver Lock */
-		*status |= FE_HAS_SIGNAL;
-		*status |= FE_HAS_CARRIER;
-		*status |= FE_HAS_VITERBI;
-		*status |= FE_HAS_SYNC;
-		*status |= FE_HAS_LOCK;
-	}
+	if (reg & 0x0100) /* Receiver Lock */
+		*status = FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI|
+			  FE_HAS_SYNC|FE_HAS_LOCK;
+	else
+		*status = 0;
 	return 0;
 }
 
@@ -506,47 +470,30 @@
 static int or51132_read_snr(struct dvb_frontend* fe, u16* snr)
 {
 	struct or51132_state* state = fe->demodulator_priv;
-	u8 rec_buf[2];
-	u8 snd_buf[2];
-	u32 noise;
-	u32 c;
-	u32 usK;
+	int noise, reg;
+	u32 c, usK = 0;
+	int retry = 1;
 
-	/* Register is same for VSB or QAM firmware */
-	snd_buf[0]=0x04;
-	snd_buf[1]=0x02; /* SNR after Equalizer */
-	msleep(30); /* 30ms */
-	if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
-		printk(KERN_WARNING "or51132: snr write error\n");
+start:
+	/* SNR after Equalizer */
+	noise = or51132_readreg(state, 0x02);
+	if (noise < 0) {
+		printk(KERN_WARNING "or51132: read_snr: error reading equalizer\n");
 		return -EREMOTEIO;
 	}
-	msleep(30); /* 30ms */
-	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
-		printk(KERN_WARNING "or51132: snr read error\n");
-		return -EREMOTEIO;
-	}
-	noise = rec_buf[0] | (rec_buf[1] << 8);
-	dprintk("read_snr noise %x %x (%i)\n",rec_buf[0],rec_buf[1],noise);
+	dprintk("read_snr noise (%d)\n", noise);
 
 	/* Read status, contains modulation type for QAM_AUTO and
 	   NTSC filter for VSB */
-	snd_buf[0]=0x04;
-	snd_buf[1]=0x00; /* Status register */
-	msleep(30); /* 30ms */
-	if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
-		printk(KERN_WARNING "or51132: status write error\n");
-		return -EREMOTEIO;
-	}
-	msleep(30); /* 30ms */
-	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
-		printk(KERN_WARNING "or51132: status read error\n");
+	reg = or51132_readreg(state, 0x00);
+	if (reg < 0) {
+		printk(KERN_WARNING "or51132: read_snr: error reading receiver status\n");
 		return -EREMOTEIO;
 	}
 
-	usK = 0;
-	switch (rec_buf[0]) {
+	switch (reg&0xff) {
 	case 0x06:
-		usK = (rec_buf[1] & 0x10) ? 0x03000000 : 0;
+		if (reg & 0x1000) usK = 3 << 24;
 		/* Fall through to QAM64 case */
 	case 0x43:
 		c = 150204167;
@@ -555,11 +502,12 @@
 		c = 150290396;
 		break;
 	default:
-		printk(KERN_ERR "or51132: unknown status 0x%02x\n", rec_buf[0]);
+		printk(KERN_WARNING "or51132: unknown status 0x%02x\n", reg&0xff);
+		if (retry--) goto start;
 		return -EREMOTEIO;
 	}
 	dprintk("%s: modulation %02x, NTSC rej O%s\n", __FUNCTION__,
-		rec_buf[0], rec_buf[1]&0x10?"n":"ff");
+		reg&0xff, reg&0x1000?"n":"ff");
 
 	/* Calculate SNR using noise, c, and NTSC rejection correction */
 	state->snr = calculate_snr(noise, c) - usK;
@@ -671,6 +619,7 @@
 
 MODULE_DESCRIPTION("OR51132 ATSC [pcHDTV HD-3000] (8VSB & ITU J83 AnnexB FEC QAM64/256) Demodulator Driver");
 MODULE_AUTHOR("Kirk Lapray");
+MODULE_AUTHOR("Trent Piepho");
 MODULE_LICENSE("GPL");
 
 EXPORT_SYMBOL(or51132_attach);
diff --git a/drivers/media/dvb/frontends/tda10021.c b/drivers/media/dvb/frontends/tda10021.c
index 5b9c5bb..1105368 100644
--- a/drivers/media/dvb/frontends/tda10021.c
+++ b/drivers/media/dvb/frontends/tda10021.c
@@ -30,13 +30,13 @@
 #include <linux/slab.h>
 
 #include "dvb_frontend.h"
-#include "tda10021.h"
+#include "tda1002x.h"
 
 
 struct tda10021_state {
 	struct i2c_adapter* i2c;
 	/* configuration settings */
-	const struct tda10021_config* config;
+	const struct tda1002x_config* config;
 	struct dvb_frontend frontend;
 
 	u8 pwm;
@@ -53,9 +53,6 @@
 static int verbose;
 
 #define XIN 57840000UL
-#define DISABLE_INVERSION(reg0)		do { reg0 |= 0x20; } while (0)
-#define ENABLE_INVERSION(reg0)		do { reg0 &= ~0x20; } while (0)
-#define HAS_INVERSION(reg0)		(!(reg0 & 0x20))
 
 #define FIN (XIN >> 4)
 
@@ -64,7 +61,7 @@
 {
 	0x73, 0x6a, 0x23, 0x0a, 0x02, 0x37, 0x77, 0x1a,
 	0x37, 0x6a, 0x17, 0x8a, 0x1e, 0x86, 0x43, 0x40,
-	0xb8, 0x3f, 0xa0, 0x00, 0xcd, 0x01, 0x00, 0xff,
+	0xb8, 0x3f, 0xa1, 0x00, 0xcd, 0x01, 0x00, 0xff,
 	0x11, 0x00, 0x7c, 0x31, 0x30, 0x20, 0x00, 0x00,
 	0x02, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00,
 	0x07, 0x00, 0x33, 0x11, 0x0d, 0x95, 0x08, 0x58,
@@ -97,7 +94,8 @@
 	int ret;
 
 	ret = i2c_transfer (state->i2c, msg, 2);
-	if (ret != 2)
+	// Don't print an error message if the id is read.
+	if (ret != 2 && reg != 0x1a)
 		printk("DVB: TDA10021: %s: readreg error (ret == %i)\n",
 				__FUNCTION__, ret);
 	return b1[0];
@@ -136,10 +134,10 @@
 {
 	reg0 |= state->reg0 & 0x63;
 
-	if (INVERSION_ON == inversion)
-		ENABLE_INVERSION(reg0);
-	else if (INVERSION_OFF == inversion)
-		DISABLE_INVERSION(reg0);
+	if ((INVERSION_ON == inversion) ^ (state->config->invert == 0))
+		reg0 &= ~0x20;
+	else
+		reg0 |= 0x20;
 
 	_tda10021_writereg (state, 0x00, reg0 & 0xfe);
 	_tda10021_writereg (state, 0x00, reg0 | 0x01);
@@ -201,16 +199,6 @@
 	return 0;
 }
 
-static int tda10021_write(struct dvb_frontend* fe, u8 *buf, int len)
-{
-	struct tda10021_state* state = fe->demodulator_priv;
-
-	if (len != 2)
-		return -EINVAL;
-
-	return _tda10021_writereg(state, buf[0], buf[1]);
-}
-
 static int tda10021_init (struct dvb_frontend *fe)
 {
 	struct tda10021_state* state = fe->demodulator_priv;
@@ -258,6 +246,9 @@
 	if (qam < 0 || qam > 5)
 		return -EINVAL;
 
+	if (p->inversion != INVERSION_ON && p->inversion != INVERSION_OFF)
+		return -EINVAL;
+
 	//printk("tda10021: set frequency to %d qam=%d symrate=%d\n", p->frequency,qam,p->u.qam.symbol_rate);
 
 	if (fe->ops.tuner_ops.set_params) {
@@ -366,7 +357,7 @@
 		       -((s32)p->u.qam.symbol_rate * afc) >> 10);
 	}
 
-	p->inversion = HAS_INVERSION(state->reg0) ? INVERSION_ON : INVERSION_OFF;
+	p->inversion = ((state->reg0 & 0x20) == 0x20) ^ (state->config->invert != 0) ? INVERSION_ON : INVERSION_OFF;
 	p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16;
 
 	p->u.qam.fec_inner = FEC_NONE;
@@ -408,11 +399,12 @@
 
 static struct dvb_frontend_ops tda10021_ops;
 
-struct dvb_frontend* tda10021_attach(const struct tda10021_config* config,
+struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config,
 				     struct i2c_adapter* i2c,
 				     u8 pwm)
 {
 	struct tda10021_state* state = NULL;
+	u8 id;
 
 	/* allocate memory for the internal state */
 	state = kmalloc(sizeof(struct tda10021_state), GFP_KERNEL);
@@ -425,7 +417,11 @@
 	state->reg0 = tda10021_inittab[0];
 
 	/* check if the demod is there */
-	if ((tda10021_readreg(state, 0x1a) & 0xf0) != 0x70) goto error;
+	id = tda10021_readreg(state, 0x1a);
+	if ((id & 0xf0) != 0x70) goto error;
+
+	printk("TDA10021: i2c-addr = 0x%02x, id = 0x%02x\n",
+	       state->config->demod_address, id);
 
 	/* create dvb_frontend */
 	memcpy(&state->frontend.ops, &tda10021_ops, sizeof(struct dvb_frontend_ops));
@@ -447,7 +443,7 @@
 		.frequency_max = 858000000,
 		.symbol_rate_min = (XIN/2)/64,     /* SACLK/64 == (XIN/2)/64 */
 		.symbol_rate_max = (XIN/2)/4,      /* SACLK/4 */
-#if 0
+	#if 0
 		.frequency_tolerance = ???,
 		.symbol_rate_tolerance = ???,  /* ppm */  /* == 8% (spec p. 5) */
 	#endif
@@ -461,7 +457,6 @@
 
 	.init = tda10021_init,
 	.sleep = tda10021_sleep,
-	.write = tda10021_write,
 	.i2c_gate_ctrl = tda10021_i2c_gate_ctrl,
 
 	.set_frontend = tda10021_set_parameters,
diff --git a/drivers/media/dvb/frontends/tda10023.c b/drivers/media/dvb/frontends/tda10023.c
new file mode 100644
index 0000000..da796e7
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda10023.c
@@ -0,0 +1,540 @@
+/*
+    TDA10023  - DVB-C decoder
+    (as used in Philips CU1216-3 NIM and the Reelbox DVB-C tuner card)
+
+    Copyright (C) 2005 Georg Acher, BayCom GmbH (acher at baycom dot de)
+    Copyright (c) 2006 Hartmut Birr (e9hack at gmail dot com)
+
+    Remotely based on tda10021.c
+    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+    Copyright (C) 2004 Markus Schulz <msc@antzsystem.de>
+		   Support for TDA10021
+
+    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/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "tda1002x.h"
+
+
+struct tda10023_state {
+	struct i2c_adapter* i2c;
+	/* configuration settings */
+	const struct tda1002x_config* config;
+	struct dvb_frontend frontend;
+
+	u8 pwm;
+	u8 reg0;
+};
+
+
+#define dprintk(x...)
+
+static int verbose;
+
+#define XTAL   28920000UL
+#define PLL_M  8UL
+#define PLL_P  4UL
+#define PLL_N  1UL
+#define SYSCLK (XTAL*PLL_M/(PLL_N*PLL_P))  // -> 57840000
+
+static u8 tda10023_inittab[]={
+	// reg mask val
+	0x2a,0xff,0x02,  // PLL3, Bypass, Power Down
+	0xff,0x64,0x00,  // Sleep 100ms
+	0x2a,0xff,0x03,  // PLL3, Bypass, Power Down
+	0xff,0x64,0x00,  // Sleep 100ms
+	0x28,0xff,PLL_M-1,  // PLL1 M=8
+	0x29,0xff,((PLL_P-1)<<6)|(PLL_N-1),  // PLL2
+	0x00,0xff,0x23,  // GPR FSAMPLING=1
+	0x2a,0xff,0x08,  // PLL3 PSACLK=1
+	0xff,0x64,0x00,  // Sleep 100ms
+	0x1f,0xff,0x00,  // RESET
+	0xff,0x64,0x00,  // Sleep 100ms
+	0xe6,0x0c,0x04,  // RSCFG_IND
+	0x10,0xc0,0x80,  // DECDVBCFG1 PBER=1
+
+	0x0e,0xff,0x82,  // GAIN1
+	0x03,0x08,0x08,  // CLKCONF DYN=1
+	0x2e,0xbf,0x30,  // AGCCONF2 TRIAGC=0,POSAGC=ENAGCIF=1 PPWMTUN=0 PPWMIF=0
+	0x01,0xff,0x30,  // AGCREF
+	0x1e,0x84,0x84,  // CONTROL SACLK_ON=1
+	0x1b,0xff,0xc8,  // ADC TWOS=1
+	0x3b,0xff,0xff,  // IFMAX
+	0x3c,0xff,0x00,  // IFMIN
+	0x34,0xff,0x00,  // PWMREF
+	0x35,0xff,0xff,  // TUNMAX
+	0x36,0xff,0x00,  // TUNMIN
+	0x06,0xff,0x7f,  // EQCONF1 POSI=7 ENADAPT=ENEQUAL=DFE=1    // 0x77
+	0x1c,0x30,0x30,  // EQCONF2 STEPALGO=SGNALGO=1
+	0x37,0xff,0xf6,  // DELTAF_LSB
+	0x38,0xff,0xff,  // DELTAF_MSB
+	0x02,0xff,0x93,  // AGCCONF1  IFS=1 KAGCIF=2 KAGCTUN=3
+	0x2d,0xff,0xf6,  // SWEEP SWPOS=1 SWDYN=7 SWSTEP=1 SWLEN=2
+	0x04,0x10,0x00,   // SWRAMP=1
+	0x12,0xff,0xa1,  // INTP1 POCLKP=1 FEL=1 MFS=0
+	0x2b,0x01,0xa1,  // INTS1
+	0x20,0xff,0x04,  // INTP2 SWAPP=? MSBFIRSTP=? INTPSEL=?
+	0x2c,0xff,0x0d,  // INTP/S TRIP=0 TRIS=0
+	0xc4,0xff,0x00,
+	0xc3,0x30,0x00,
+	0xb5,0xff,0x19,  // ERAGC_THD
+	0x00,0x03,0x01,  // GPR, CLBS soft reset
+	0x00,0x03,0x03,  // GPR, CLBS soft reset
+	0xff,0x64,0x00,  // Sleep 100ms
+	0xff,0xff,0xff
+};
+
+static u8 tda10023_readreg (struct tda10023_state* state, u8 reg)
+{
+	u8 b0 [] = { reg };
+	u8 b1 [] = { 0 };
+	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+				  { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+	int ret;
+
+	ret = i2c_transfer (state->i2c, msg, 2);
+	if (ret != 2)
+		printk("DVB: TDA10023: %s: readreg error (ret == %i)\n",
+				 __FUNCTION__, ret);
+	return b1[0];
+}
+
+static int tda10023_writereg (struct tda10023_state* state, u8 reg, u8 data)
+{
+	u8 buf[] = { reg, data };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+	int ret;
+
+	ret = i2c_transfer (state->i2c, &msg, 1);
+	if (ret != 1)
+		printk("DVB: TDA10023(%d): %s, writereg error "
+			"(reg == 0x%02x, val == 0x%02x, ret == %i)\n",
+			state->frontend.dvb->num, __FUNCTION__, reg, data, ret);
+
+	return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+
+static int tda10023_writebit (struct tda10023_state* state, u8 reg, u8 mask,u8 data)
+{
+	if (mask==0xff)
+		return tda10023_writereg(state, reg, data);
+	else {
+		u8 val;
+		val=tda10023_readreg(state,reg);
+		val&=~mask;
+		val|=(data&mask);
+		return tda10023_writereg(state, reg, val);
+	}
+}
+
+static void tda10023_writetab(struct tda10023_state* state, u8* tab)
+{
+	u8 r,m,v;
+	while (1) {
+		r=*tab++;
+		m=*tab++;
+		v=*tab++;
+		if (r==0xff) {
+			if (m==0xff)
+				break;
+			else
+				msleep(m);
+		}
+		else
+			tda10023_writebit(state,r,m,v);
+	}
+}
+
+//get access to tuner
+static int lock_tuner(struct tda10023_state* state)
+{
+	u8 buf[2] = { 0x0f, 0xc0 };
+	struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2};
+
+	if(i2c_transfer(state->i2c, &msg, 1) != 1)
+	{
+		printk("tda10023: lock tuner fails\n");
+		return -EREMOTEIO;
+	}
+	return 0;
+}
+
+//release access from tuner
+static int unlock_tuner(struct tda10023_state* state)
+{
+	u8 buf[2] = { 0x0f, 0x40 };
+	struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2};
+
+	if(i2c_transfer(state->i2c, &msg_post, 1) != 1)
+	{
+		printk("tda10023: unlock tuner fails\n");
+		return -EREMOTEIO;
+	}
+	return 0;
+}
+
+static int tda10023_setup_reg0 (struct tda10023_state* state, u8 reg0)
+{
+	reg0 |= state->reg0 & 0x63;
+
+	tda10023_writereg (state, 0x00, reg0 & 0xfe);
+	tda10023_writereg (state, 0x00, reg0 | 0x01);
+
+	state->reg0 = reg0;
+	return 0;
+}
+
+static int tda10023_set_symbolrate (struct tda10023_state* state, u32 sr)
+{
+	s32 BDR;
+	s32 BDRI;
+	s16 SFIL=0;
+	u16 NDEC = 0;
+
+	if (sr > (SYSCLK/(2*4)))
+		sr=SYSCLK/(2*4);
+
+	if (sr<870000)
+		sr=870000;
+
+	if (sr < (u32)(SYSCLK/98.40)) {
+		NDEC=3;
+		SFIL=1;
+	} else if (sr<(u32)(SYSCLK/64.0)) {
+		NDEC=3;
+		SFIL=0;
+	} else if (sr<(u32)(SYSCLK/49.2)) {
+		NDEC=2;
+		SFIL=1;
+	} else if (sr<(u32)(SYSCLK/32.0)) {
+		NDEC=2;
+		SFIL=0;
+	} else if (sr<(u32)(SYSCLK/24.6)) {
+		NDEC=1;
+		SFIL=1;
+	} else if (sr<(u32)(SYSCLK/16.0)) {
+		NDEC=1;
+		SFIL=0;
+	} else if (sr<(u32)(SYSCLK/12.3)) {
+		NDEC=0;
+		SFIL=1;
+	}
+
+	BDRI=SYSCLK*16;
+	BDRI>>=NDEC;
+	BDRI +=sr/2;
+	BDRI /=sr;
+
+	if (BDRI>255)
+		BDRI=255;
+
+	{
+		u64 BDRX;
+
+		BDRX=1<<(24+NDEC);
+		BDRX*=sr;
+		do_div(BDRX,SYSCLK); 	// BDRX/=SYSCLK;
+
+		BDR=(s32)BDRX;
+	}
+//	printk("Symbolrate %i, BDR %i BDRI %i, NDEC %i\n",sr,BDR,BDRI,NDEC);
+	tda10023_writebit (state, 0x03, 0xc0, NDEC<<6);
+	tda10023_writereg (state, 0x0a, BDR&255);
+	tda10023_writereg (state, 0x0b, (BDR>>8)&255);
+	tda10023_writereg (state, 0x0c, (BDR>>16)&31);
+	tda10023_writereg (state, 0x0d, BDRI);
+	tda10023_writereg (state, 0x3d, (SFIL<<7));
+	return 0;
+}
+
+static int tda10023_init (struct dvb_frontend *fe)
+{
+	struct tda10023_state* state = fe->demodulator_priv;
+
+	dprintk("DVB: TDA10023(%d): init chip\n", fe->adapter->num);
+
+	tda10023_writetab(state, tda10023_inittab);
+
+	return 0;
+}
+
+static int tda10023_set_parameters (struct dvb_frontend *fe,
+			    struct dvb_frontend_parameters *p)
+{
+	struct tda10023_state* state = fe->demodulator_priv;
+
+	static int qamvals[6][6] = {
+		//  QAM   LOCKTHR  MSETH   AREF AGCREFNYQ  ERAGCNYQ_THD
+		{ (5<<2),  0x78,    0x8c,   0x96,   0x78,   0x4c  },  // 4 QAM
+		{ (0<<2),  0x87,    0xa2,   0x91,   0x8c,   0x57  },  // 16 QAM
+		{ (1<<2),  0x64,    0x74,   0x96,   0x8c,   0x57  },  // 32 QAM
+		{ (2<<2),  0x46,    0x43,   0x6a,   0x6a,   0x44  },  // 64 QAM
+		{ (3<<2),  0x36,    0x34,   0x7e,   0x78,   0x4c  },  // 128 QAM
+		{ (4<<2),  0x26,    0x23,   0x6c,   0x5c,   0x3c  },  // 256 QAM
+	};
+
+	int qam = p->u.qam.modulation;
+
+	if (qam < 0 || qam > 5)
+		return -EINVAL;
+
+	if (fe->ops.tuner_ops.set_params) {
+		fe->ops.tuner_ops.set_params(fe, p);
+		if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
+	}
+
+	tda10023_set_symbolrate (state, p->u.qam.symbol_rate);
+	tda10023_writereg (state, 0x05, qamvals[qam][1]);
+	tda10023_writereg (state, 0x08, qamvals[qam][2]);
+	tda10023_writereg (state, 0x09, qamvals[qam][3]);
+	tda10023_writereg (state, 0xb4, qamvals[qam][4]);
+	tda10023_writereg (state, 0xb6, qamvals[qam][5]);
+
+//	tda10023_writereg (state, 0x04, (p->inversion?0x12:0x32));
+//	tda10023_writebit (state, 0x04, 0x60, (p->inversion?0:0x20));
+	tda10023_writebit (state, 0x04, 0x40, 0x40);
+	tda10023_setup_reg0 (state, qamvals[qam][0]);
+
+	return 0;
+}
+
+static int tda10023_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct tda10023_state* state = fe->demodulator_priv;
+	int sync;
+
+	*status = 0;
+
+	//0x11[1] == CARLOCK -> Carrier locked
+	//0x11[2] == FSYNC -> Frame synchronisation
+	//0x11[3] == FEL -> Front End locked
+	//0x11[6] == NODVB -> DVB Mode Information
+	sync = tda10023_readreg (state, 0x11);
+
+	if (sync & 2)
+		*status |= FE_HAS_SIGNAL|FE_HAS_CARRIER;
+
+	if (sync & 4)
+		*status |= FE_HAS_SYNC|FE_HAS_VITERBI;
+
+	if (sync & 8)
+		*status |= FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int tda10023_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct tda10023_state* state = fe->demodulator_priv;
+	u8 a,b,c;
+	a=tda10023_readreg(state, 0x14);
+	b=tda10023_readreg(state, 0x15);
+	c=tda10023_readreg(state, 0x16)&0xf;
+	tda10023_writebit (state, 0x10, 0xc0, 0x00);
+
+	*ber = a | (b<<8)| (c<<16);
+	return 0;
+}
+
+static int tda10023_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+	struct tda10023_state* state = fe->demodulator_priv;
+	u8 ifgain=tda10023_readreg(state, 0x2f);
+
+	u16 gain = ((255-tda10023_readreg(state, 0x17))) + (255-ifgain)/16;
+	// Max raw value is about 0xb0 -> Normalize to >0xf0 after 0x90
+	if (gain>0x90)
+		gain=gain+2*(gain-0x90);
+	if (gain>255)
+		gain=255;
+
+	*strength = (gain<<8)|gain;
+	return 0;
+}
+
+static int tda10023_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct tda10023_state* state = fe->demodulator_priv;
+
+	u8 quality = ~tda10023_readreg(state, 0x18);
+	*snr = (quality << 8) | quality;
+	return 0;
+}
+
+static int tda10023_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct tda10023_state* state = fe->demodulator_priv;
+	u8 a,b,c,d;
+	a= tda10023_readreg (state, 0x74);
+	b= tda10023_readreg (state, 0x75);
+	c= tda10023_readreg (state, 0x76);
+	d= tda10023_readreg (state, 0x77);
+	*ucblocks = a | (b<<8)|(c<<16)|(d<<24);
+
+	tda10023_writebit (state, 0x10, 0x20,0x00);
+	tda10023_writebit (state, 0x10, 0x20,0x20);
+	tda10023_writebit (state, 0x13, 0x01, 0x00);
+
+	return 0;
+}
+
+static int tda10023_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct tda10023_state* state = fe->demodulator_priv;
+	int sync,inv;
+	s8 afc = 0;
+
+	sync = tda10023_readreg(state, 0x11);
+	afc = tda10023_readreg(state, 0x19);
+	inv = tda10023_readreg(state, 0x04);
+
+	if (verbose) {
+		/* AFC only valid when carrier has been recovered */
+		printk(sync & 2 ? "DVB: TDA10023(%d): AFC (%d) %dHz\n" :
+				  "DVB: TDA10023(%d): [AFC (%d) %dHz]\n",
+			state->frontend.dvb->num, afc,
+		       -((s32)p->u.qam.symbol_rate * afc) >> 10);
+	}
+
+	p->inversion = (inv&0x20?0:1);
+	p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16;
+
+	p->u.qam.fec_inner = FEC_NONE;
+	p->frequency = ((p->frequency + 31250) / 62500) * 62500;
+
+	if (sync & 2)
+		p->frequency -= ((s32)p->u.qam.symbol_rate * afc) >> 10;
+
+	return 0;
+}
+
+static int tda10023_sleep(struct dvb_frontend* fe)
+{
+	struct tda10023_state* state = fe->demodulator_priv;
+
+	tda10023_writereg (state, 0x1b, 0x02);  /* pdown ADC */
+	tda10023_writereg (state, 0x00, 0x80);  /* standby */
+
+	return 0;
+}
+
+static int tda10023_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
+{
+	struct tda10023_state* state = fe->demodulator_priv;
+
+	if (enable) {
+		lock_tuner(state);
+	} else {
+		unlock_tuner(state);
+	}
+	return 0;
+}
+
+static void tda10023_release(struct dvb_frontend* fe)
+{
+	struct tda10023_state* state = fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops tda10023_ops;
+
+struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config,
+				     struct i2c_adapter* i2c,
+				     u8 pwm)
+{
+	struct tda10023_state* state = NULL;
+	int i;
+
+	/* allocate memory for the internal state */
+	state = kmalloc(sizeof(struct tda10023_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops));
+	state->pwm = pwm;
+	for (i=0; i < sizeof(tda10023_inittab)/sizeof(*tda10023_inittab);i+=3) {
+		if (tda10023_inittab[i] == 0x00) {
+			state->reg0 = tda10023_inittab[i+2];
+			break;
+		}
+	}
+
+	// Wakeup if in standby
+	tda10023_writereg (state, 0x00, 0x33);
+	/* check if the demod is there */
+	if ((tda10023_readreg(state, 0x1a) & 0xf0) != 0x70) goto error;
+
+	/* create dvb_frontend */
+	memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops));
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops tda10023_ops = {
+
+	.info = {
+		.name = "Philips TDA10023 DVB-C",
+		.type = FE_QAM,
+		.frequency_stepsize = 62500,
+		.frequency_min = 51000000,
+		.frequency_max = 858000000,
+		.symbol_rate_min = (SYSCLK/2)/64,     /* SACLK/64 == (SYSCLK/2)/64 */
+		.symbol_rate_max = (SYSCLK/2)/4,      /* SACLK/4 */
+		.caps = 0x400 | //FE_CAN_QAM_4
+			FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
+			FE_CAN_QAM_128 | FE_CAN_QAM_256 |
+			FE_CAN_FEC_AUTO
+	},
+
+	.release = tda10023_release,
+
+	.init = tda10023_init,
+	.sleep = tda10023_sleep,
+	.i2c_gate_ctrl = tda10023_i2c_gate_ctrl,
+
+	.set_frontend = tda10023_set_parameters,
+	.get_frontend = tda10023_get_frontend,
+
+	.read_status = tda10023_read_status,
+	.read_ber = tda10023_read_ber,
+	.read_signal_strength = tda10023_read_signal_strength,
+	.read_snr = tda10023_read_snr,
+	.read_ucblocks = tda10023_read_ucblocks,
+};
+
+
+MODULE_DESCRIPTION("Philips TDA10023 DVB-C demodulator driver");
+MODULE_AUTHOR("Georg Acher, Hartmut Birr");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(tda10023_attach);
diff --git a/drivers/media/dvb/frontends/tda10021.h b/drivers/media/dvb/frontends/tda1002x.h
similarity index 65%
rename from drivers/media/dvb/frontends/tda10021.h
rename to drivers/media/dvb/frontends/tda1002x.h
index e3da780..e9094d8 100644
--- a/drivers/media/dvb/frontends/tda10021.h
+++ b/drivers/media/dvb/frontends/tda1002x.h
@@ -1,6 +1,6 @@
 /*
-    TDA10021  - Single Chip Cable Channel Receiver driver module
-	       used on the the Siemens DVB-C cards
+    TDA10021/TDA10023  - Single Chip Cable Channel Receiver driver module
+			 used on the the Siemens DVB-C cards
 
     Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
     Copyright (C) 2004 Markus Schulz <msc@antzsystem.de>
@@ -21,22 +21,23 @@
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
-#ifndef TDA10021_H
-#define TDA10021_H
+#ifndef TDA1002x_H
+#define TDA1002x_H
 
 #include <linux/dvb/frontend.h>
 
-struct tda10021_config
+struct tda1002x_config
 {
 	/* the demodulator's i2c address */
 	u8 demod_address;
+	u8 invert;
 };
 
 #if defined(CONFIG_DVB_TDA10021) || (defined(CONFIG_DVB_TDA10021_MODULE) && defined(MODULE))
-extern struct dvb_frontend* tda10021_attach(const struct tda10021_config* config,
+extern struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config,
 					    struct i2c_adapter* i2c, u8 pwm);
 #else
-static inline struct dvb_frontend* tda10021_attach(const struct tda10021_config* config,
+static inline struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config,
 					    struct i2c_adapter* i2c, u8 pwm)
 {
 	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
@@ -44,12 +45,16 @@
 }
 #endif // CONFIG_DVB_TDA10021
 
-static inline int tda10021_writereg(struct dvb_frontend *fe, u8 reg, u8 val) {
-	int r = 0;
-	u8 buf[] = {reg, val};
-	if (fe->ops.write)
-		r = fe->ops.write(fe, buf, 2);
-	return r;
+#if defined(CONFIG_DVB_TDA10023) || (defined(CONFIG_DVB_TDA10023_MODULE) && defined(MODULE))
+extern struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config,
+					    struct i2c_adapter* i2c, u8 pwm);
+#else
+static inline struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config,
+					    struct i2c_adapter* i2c, u8 pwm)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+	return NULL;
 }
+#endif // CONFIG_DVB_TDA10023
 
-#endif // TDA10021_H
+#endif // TDA1002x_H
diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c
index f4a9cf9..33a8437 100644
--- a/drivers/media/dvb/frontends/tda1004x.c
+++ b/drivers/media/dvb/frontends/tda1004x.c
@@ -40,20 +40,6 @@
 #include "dvb_frontend.h"
 #include "tda1004x.h"
 
-enum tda1004x_demod {
-	TDA1004X_DEMOD_TDA10045,
-	TDA1004X_DEMOD_TDA10046,
-};
-
-struct tda1004x_state {
-	struct i2c_adapter* i2c;
-	const struct tda1004x_config* config;
-	struct dvb_frontend frontend;
-
-	/* private demod data */
-	enum tda1004x_demod demod_type;
-};
-
 static int debug;
 #define dprintk(args...) \
 	do { \
@@ -507,35 +493,51 @@
 		tda1004x_write_byteI(state, TDA1004X_CONFC4, 0x80);
 	}
 	tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 1, 0);
+	/* set GPIO 1 and 3 */
+	if (state->config->gpio_config != TDA10046_GPTRI) {
+		tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE2, 0x33);
+		tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x0f, state->config->gpio_config &0x0f);
+	}
 	/* let the clocks recover from sleep */
-	msleep(5);
+	msleep(10);
 
 	/* The PLLs need to be reprogrammed after sleep */
 	tda10046_init_plls(fe);
+	tda1004x_write_mask(state, TDA1004X_CONFADC2, 0xc0, 0);
 
 	/* don't re-upload unless necessary */
 	if (tda1004x_check_upload_ok(state) == 0)
 		return 0;
 
+	printk(KERN_INFO "tda1004x: trying to boot from eeprom\n");
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 4, 4);
+	msleep(300);
+	/* don't re-upload unless necessary */
+	if (tda1004x_check_upload_ok(state) == 0)
+		return 0;
+
 	if (state->config->request_firmware != NULL) {
 		/* request the firmware, this will block until someone uploads it */
 		printk(KERN_INFO "tda1004x: waiting for firmware upload...\n");
 		ret = state->config->request_firmware(fe, &fw, TDA10046_DEFAULT_FIRMWARE);
 		if (ret) {
-			printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n");
-			return ret;
+			/* remain compatible to old bug: try to load with tda10045 image name */
+			ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE);
+			if (ret) {
+				printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n");
+				return ret;
+			} else {
+				printk(KERN_INFO "tda1004x: please rename the firmware file to %s\n",
+						  TDA10046_DEFAULT_FIRMWARE);
+			}
 		}
-		tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST
-		ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10046H_CODE_CPT, TDA10046H_CODE_IN);
-		release_firmware(fw);
-		if (ret)
-			return ret;
 	} else {
-		/* boot from firmware eeprom */
-		printk(KERN_INFO "tda1004x: booting from eeprom\n");
-		tda1004x_write_mask(state, TDA1004X_CONFC4, 4, 4);
-		msleep(300);
+		printk(KERN_ERR "tda1004x: no request function defined, can't upload from file\n");
+		return -EIO;
 	}
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST
+	ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10046H_CODE_CPT, TDA10046H_CODE_IN);
+	release_firmware(fw);
 	return tda1004x_check_upload_ok(state);
 }
 
@@ -638,37 +640,33 @@
 	switch (state->config->agc_config) {
 	case TDA10046_AGC_DEFAULT:
 		tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x00); // AGC setup
-		tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities
+		tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60);  // set AGC polarities
 		break;
 	case TDA10046_AGC_IFO_AUTO_NEG:
 		tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup
-		tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities
+		tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60);  // set AGC polarities
 		break;
 	case TDA10046_AGC_IFO_AUTO_POS:
 		tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup
-		tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x00); // set AGC polarities
+		tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x00);  // set AGC polarities
 		break;
-	case TDA10046_AGC_TDA827X_GP11:
+	case TDA10046_AGC_TDA827X:
 		tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02);   // AGC setup
 		tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70);    // AGC Threshold
 		tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize
-		tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x6a); // set AGC polarities
-		break;
-	case TDA10046_AGC_TDA827X_GP00:
-		tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02);   // AGC setup
-		tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70);    // AGC Threshold
-		tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize
-		tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities
-		break;
-	case TDA10046_AGC_TDA827X_GP01:
-		tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02);   // AGC setup
-		tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70);    // AGC Threshold
-		tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize
-		tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x62); // set AGC polarities
+		tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60);  // set AGC polarities
 		break;
 	}
+	if (state->config->ts_mode == 0) {
+		tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0xc0, 0x40);
+		tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7);
+	} else {
+		tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0xc0, 0x80);
+		tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x10,
+							state->config->invert_oclk << 4);
+	}
 	tda1004x_write_byteI(state, TDA1004X_CONFADC2, 0x38);
-	tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0x61); // Turn both AGC outputs on
+	tda1004x_write_mask (state, TDA10046H_CONF_TRISTATE1, 0x3e, 0x38); // Turn IF AGC output on
 	tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MIN, 0);	  // }
 	tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values
 	tda1004x_write_byteI(state, TDA10046H_AGC_IF_MIN, 0);	  // }
@@ -678,7 +676,6 @@
 	tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 7); // MPEG2 interface config
 	tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0xc0); // MPEG2 interface config
 	// tda1004x_write_mask(state, 0x50, 0x80, 0x80);         // handle out of guard echoes
-	tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7);
 
 	return 0;
 }
@@ -705,7 +702,8 @@
 	// set frequency
 	if (fe->ops.tuner_ops.set_params) {
 		fe->ops.tuner_ops.set_params(fe, fe_params);
-		if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 0);
 	}
 
 	// Hardcoded to use auto as much as possible on the TDA10045 as it
@@ -1165,6 +1163,7 @@
 static int tda1004x_sleep(struct dvb_frontend* fe)
 {
 	struct tda1004x_state* state = fe->demodulator_priv;
+	int gpio_conf;
 
 	switch (state->demod_type) {
 	case TDA1004X_DEMOD_TDA10045:
@@ -1174,6 +1173,13 @@
 	case TDA1004X_DEMOD_TDA10046:
 		/* set outputs to tristate */
 		tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0xff);
+		/* invert GPIO 1 and 3 if desired*/
+		gpio_conf = state->config->gpio_config;
+		if (gpio_conf >= TDA10046_GP00_I)
+			tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x0f,
+							(gpio_conf & 0x0f) ^ 0x0a);
+
+		tda1004x_write_mask(state, TDA1004X_CONFADC2, 0xc0, 0xc0);
 		tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1);
 		break;
 	}
diff --git a/drivers/media/dvb/frontends/tda1004x.h b/drivers/media/dvb/frontends/tda1004x.h
index ec502d7..abae843 100644
--- a/drivers/media/dvb/frontends/tda1004x.h
+++ b/drivers/media/dvb/frontends/tda1004x.h
@@ -35,9 +35,23 @@
 	TDA10046_AGC_DEFAULT,		/* original configuration */
 	TDA10046_AGC_IFO_AUTO_NEG,	/* IF AGC only, automatic, negtive */
 	TDA10046_AGC_IFO_AUTO_POS,	/* IF AGC only, automatic, positive */
-	TDA10046_AGC_TDA827X_GP11,	/* IF AGC only, special setup for tda827x */
-	TDA10046_AGC_TDA827X_GP00,	/* same as above, but GPIOs 0 */
-	TDA10046_AGC_TDA827X_GP01,	/* same as above, but GPIO3=0 GPIO1=1*/
+	TDA10046_AGC_TDA827X,		/* IF AGC only, special setup for tda827x */
+};
+
+/* Many (hybrid) boards use GPIO 1 and 3
+	GPIO1	analog - dvb switch
+	GPIO3	firmware eeprom address switch
+*/
+enum tda10046_gpio {
+	TDA10046_GPTRI  = 0x00,		/* All GPIOs tristate */
+	TDA10046_GP00   = 0x40,		/* GPIO3=0, GPIO1=0 */
+	TDA10046_GP01   = 0x42,		/* GPIO3=0, GPIO1=1 */
+	TDA10046_GP10   = 0x48,		/* GPIO3=1, GPIO1=0 */
+	TDA10046_GP11   = 0x4a,		/* GPIO3=1, GPIO1=1 */
+	TDA10046_GP00_I = 0x80,		/* GPIO3=0, GPIO1=0, invert in sleep mode*/
+	TDA10046_GP01_I = 0x82,		/* GPIO3=0, GPIO1=1, invert in sleep mode */
+	TDA10046_GP10_I = 0x88,		/* GPIO3=1, GPIO1=0, invert in sleep mode */
+	TDA10046_GP11_I = 0x8a,		/* GPIO3=1, GPIO1=1, invert in sleep mode */
 };
 
 enum tda10046_if {
@@ -47,6 +61,11 @@
 	TDA10046_FREQ_052,		/* low IF, 5.1667 MHZ for tda9889 */
 };
 
+enum tda10046_tsout {
+	TDA10046_TS_PARALLEL  = 0x00,	/* parallel transport stream, default */
+	TDA10046_TS_SERIAL    = 0x01,	/* serial transport stream */
+};
+
 struct tda1004x_config
 {
 	/* the demodulator's i2c address */
@@ -58,6 +77,9 @@
 	/* Does the OCLK signal need inverted? */
 	u8 invert_oclk;
 
+	/* parallel or serial transport stream */
+	enum tda10046_tsout ts_mode;
+
 	/* Xtal frequency, 4 or 16MHz*/
 	enum tda10046_xtal xtal_freq;
 
@@ -67,11 +89,35 @@
 	/* AGC configuration */
 	enum tda10046_agc agc_config;
 
+	/* setting of GPIO1 and 3 */
+	enum tda10046_gpio gpio_config;
+
+	/* slave address and configuration of the tuner */
+	u8 tuner_address;
+	u8 tuner_config;
+	u8 antenna_switch;
+
+	/* if the board uses another I2c Bridge (tda8290), its address */
+	u8 i2c_gate;
+
 	/* request firmware for device */
-	/* set this to NULL if the card has a firmware EEPROM */
 	int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
 };
 
+enum tda1004x_demod {
+	TDA1004X_DEMOD_TDA10045,
+	TDA1004X_DEMOD_TDA10046,
+};
+
+struct tda1004x_state {
+	struct i2c_adapter* i2c;
+	const struct tda1004x_config* config;
+	struct dvb_frontend frontend;
+
+	/* private demod data */
+	enum tda1004x_demod demod_type;
+};
+
 #if defined(CONFIG_DVB_TDA1004X) || (defined(CONFIG_DVB_TDA1004X_MODULE) && defined(MODULE))
 extern struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
 					    struct i2c_adapter* i2c);
diff --git a/drivers/media/dvb/frontends/tda827x.c b/drivers/media/dvb/frontends/tda827x.c
new file mode 100644
index 0000000..256fc4b
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda827x.c
@@ -0,0 +1,512 @@
+/*
+ *
+ * (c) 2005 Hartmut Hackmann
+ * (c) 2007 Michael Krufky
+ *
+ *  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/module.h>
+#include <linux/dvb/frontend.h>
+#include <asm/types.h>
+
+#include "tda827x.h"
+
+static int debug = 0;
+#define dprintk(args...) \
+	do {					    \
+		if (debug) printk(KERN_DEBUG "tda827x: " args); \
+	} while (0)
+
+struct tda827x_priv {
+	int i2c_addr;
+	struct i2c_adapter *i2c_adap;
+	struct tda827x_config *cfg;
+	u32 frequency;
+	u32 bandwidth;
+};
+
+struct tda827x_data {
+	u32 lomax;
+	u8  spd;
+	u8  bs;
+	u8  bp;
+	u8  cp;
+	u8  gc3;
+	u8 div1p5;
+};
+
+static const struct tda827x_data tda827x_dvbt[] = {
+	{ .lomax =  62000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1},
+	{ .lomax =  66000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1},
+	{ .lomax =  76000000, .spd = 3, .bs = 1, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0},
+	{ .lomax =  84000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0},
+	{ .lomax =  93000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0},
+	{ .lomax =  98000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0},
+	{ .lomax = 109000000, .spd = 3, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0},
+	{ .lomax = 123000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1},
+	{ .lomax = 133000000, .spd = 2, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1},
+	{ .lomax = 151000000, .spd = 2, .bs = 1, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0},
+	{ .lomax = 154000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0},
+	{ .lomax = 181000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 0, .div1p5 = 0},
+	{ .lomax = 185000000, .spd = 2, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0},
+	{ .lomax = 217000000, .spd = 2, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0},
+	{ .lomax = 244000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1},
+	{ .lomax = 265000000, .spd = 1, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1},
+	{ .lomax = 302000000, .spd = 1, .bs = 1, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0},
+	{ .lomax = 324000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0},
+	{ .lomax = 370000000, .spd = 1, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0},
+	{ .lomax = 454000000, .spd = 1, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0},
+	{ .lomax = 493000000, .spd = 0, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1},
+	{ .lomax = 530000000, .spd = 0, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1},
+	{ .lomax = 554000000, .spd = 0, .bs = 1, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0},
+	{ .lomax = 604000000, .spd = 0, .bs = 1, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0},
+	{ .lomax = 696000000, .spd = 0, .bs = 2, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0},
+	{ .lomax = 740000000, .spd = 0, .bs = 2, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0},
+	{ .lomax = 820000000, .spd = 0, .bs = 3, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0},
+	{ .lomax = 865000000, .spd = 0, .bs = 3, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0},
+	{ .lomax =         0, .spd = 0, .bs = 0, .bp = 0, .cp = 0, .gc3 = 0, .div1p5 = 0}
+};
+
+static int tda827xo_set_params(struct dvb_frontend *fe,
+			       struct dvb_frontend_parameters *params)
+{
+	struct tda827x_priv *priv = fe->tuner_priv;
+	u8 buf[14];
+
+	struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
+			       .buf = buf, .len = sizeof(buf) };
+	int i, tuner_freq, if_freq;
+	u32 N;
+
+	dprintk("%s:\n", __FUNCTION__);
+	switch (params->u.ofdm.bandwidth) {
+	case BANDWIDTH_6_MHZ:
+		if_freq = 4000000;
+		break;
+	case BANDWIDTH_7_MHZ:
+		if_freq = 4500000;
+		break;
+	default:		   /* 8 MHz or Auto */
+		if_freq = 5000000;
+		break;
+	}
+	tuner_freq = params->frequency + if_freq;
+
+	i = 0;
+	while (tda827x_dvbt[i].lomax < tuner_freq) {
+		if(tda827x_dvbt[i + 1].lomax == 0)
+			break;
+		i++;
+	}
+
+	N = ((tuner_freq + 125000) / 250000) << (tda827x_dvbt[i].spd + 2);
+	buf[0] = 0;
+	buf[1] = (N>>8) | 0x40;
+	buf[2] = N & 0xff;
+	buf[3] = 0;
+	buf[4] = 0x52;
+	buf[5] = (tda827x_dvbt[i].spd << 6) + (tda827x_dvbt[i].div1p5 << 5) +
+				(tda827x_dvbt[i].bs << 3) + tda827x_dvbt[i].bp;
+	buf[6] = (tda827x_dvbt[i].gc3 << 4) + 0x8f;
+	buf[7] = 0xbf;
+	buf[8] = 0x2a;
+	buf[9] = 0x05;
+	buf[10] = 0xff;
+	buf[11] = 0x00;
+	buf[12] = 0x00;
+	buf[13] = 0x40;
+
+	msg.len = 14;
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(priv->i2c_adap, &msg, 1) != 1) {
+		printk("%s: could not write to tuner at addr: 0x%02x\n",
+		       __FUNCTION__, priv->i2c_addr << 1);
+		return -EIO;
+	}
+	msleep(500);
+	/* correct CP value */
+	buf[0] = 0x30;
+	buf[1] = 0x50 + tda827x_dvbt[i].cp;
+	msg.len = 2;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	i2c_transfer(priv->i2c_adap, &msg, 1);
+
+	priv->frequency = tuner_freq - if_freq; // FIXME
+	priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
+
+	return 0;
+}
+
+static int tda827xo_sleep(struct dvb_frontend *fe)
+{
+	struct tda827x_priv *priv = fe->tuner_priv;
+	static u8 buf[] = { 0x30, 0xd0 };
+	struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
+			       .buf = buf, .len = sizeof(buf) };
+
+	dprintk("%s:\n", __FUNCTION__);
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	i2c_transfer(priv->i2c_adap, &msg, 1);
+
+	if (priv->cfg && priv->cfg->sleep)
+		priv->cfg->sleep(fe);
+
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+struct tda827xa_data {
+	u32 lomax;
+	u8  svco;
+	u8  spd;
+	u8  scr;
+	u8  sbs;
+	u8  gc3;
+};
+
+static const struct tda827xa_data tda827xa_dvbt[] = {
+	{ .lomax =  56875000, .svco = 3, .spd = 4, .scr = 0, .sbs = 0, .gc3 = 1},
+	{ .lomax =  67250000, .svco = 0, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1},
+	{ .lomax =  81250000, .svco = 1, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1},
+	{ .lomax =  97500000, .svco = 2, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1},
+	{ .lomax = 113750000, .svco = 3, .spd = 3, .scr = 0, .sbs = 1, .gc3 = 1},
+	{ .lomax = 134500000, .svco = 0, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
+	{ .lomax = 154000000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
+	{ .lomax = 162500000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
+	{ .lomax = 183000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
+	{ .lomax = 195000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1},
+	{ .lomax = 227500000, .svco = 3, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1},
+	{ .lomax = 269000000, .svco = 0, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1},
+	{ .lomax = 290000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1},
+	{ .lomax = 325000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1},
+	{ .lomax = 390000000, .svco = 2, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1},
+	{ .lomax = 455000000, .svco = 3, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1},
+	{ .lomax = 520000000, .svco = 0, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1},
+	{ .lomax = 538000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1},
+	{ .lomax = 550000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1},
+	{ .lomax = 620000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
+	{ .lomax = 650000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
+	{ .lomax = 700000000, .svco = 2, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
+	{ .lomax = 780000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
+	{ .lomax = 820000000, .svco = 3, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
+	{ .lomax = 870000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
+	{ .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 0},
+	{ .lomax =         0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0}
+};
+
+static int tda827xa_set_params(struct dvb_frontend *fe,
+			       struct dvb_frontend_parameters *params)
+{
+	struct tda827x_priv *priv = fe->tuner_priv;
+	u8 buf[11];
+
+	struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
+			       .buf = buf, .len = sizeof(buf) };
+
+	int i, tuner_freq, if_freq;
+	u32 N;
+
+	dprintk("%s:\n", __FUNCTION__);
+	if (priv->cfg && priv->cfg->lna_gain)
+		priv->cfg->lna_gain(fe, 1);
+	msleep(20);
+
+	switch (params->u.ofdm.bandwidth) {
+	case BANDWIDTH_6_MHZ:
+		if_freq = 4000000;
+		break;
+	case BANDWIDTH_7_MHZ:
+		if_freq = 4500000;
+		break;
+	default:		   /* 8 MHz or Auto */
+		if_freq = 5000000;
+		break;
+	}
+	tuner_freq = params->frequency + if_freq;
+
+	i = 0;
+	while (tda827xa_dvbt[i].lomax < tuner_freq) {
+		if(tda827xa_dvbt[i + 1].lomax == 0)
+			break;
+		i++;
+	}
+
+	N = ((tuner_freq + 31250) / 62500) << tda827xa_dvbt[i].spd;
+	buf[0] = 0;            // subaddress
+	buf[1] = N >> 8;
+	buf[2] = N & 0xff;
+	buf[3] = 0;
+	buf[4] = 0x16;
+	buf[5] = (tda827xa_dvbt[i].spd << 5) + (tda827xa_dvbt[i].svco << 3) +
+			tda827xa_dvbt[i].sbs;
+	buf[6] = 0x4b + (tda827xa_dvbt[i].gc3 << 4);
+	buf[7] = 0x1c;
+	buf[8] = 0x06;
+	buf[9] = 0x24;
+	buf[10] = 0x00;
+	msg.len = 11;
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(priv->i2c_adap, &msg, 1) != 1) {
+		printk("%s: could not write to tuner at addr: 0x%02x\n",
+		       __FUNCTION__, priv->i2c_addr << 1);
+		return -EIO;
+	}
+	buf[0] = 0x90;
+	buf[1] = 0xff;
+	buf[2] = 0x60;
+	buf[3] = 0x00;
+	buf[4] = 0x59;  // lpsel, for 6MHz + 2
+	msg.len = 5;
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	i2c_transfer(priv->i2c_adap, &msg, 1);
+
+	buf[0] = 0xa0;
+	buf[1] = 0x40;
+	msg.len = 2;
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	i2c_transfer(priv->i2c_adap, &msg, 1);
+
+	msleep(11);
+	msg.flags = I2C_M_RD;
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	i2c_transfer(priv->i2c_adap, &msg, 1);
+	msg.flags = 0;
+
+	buf[1] >>= 4;
+	dprintk("tda8275a AGC2 gain is: %d\n", buf[1]);
+	if ((buf[1]) < 2) {
+		if (priv->cfg && priv->cfg->lna_gain)
+			priv->cfg->lna_gain(fe, 0);
+		buf[0] = 0x60;
+		buf[1] = 0x0c;
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 1);
+		i2c_transfer(priv->i2c_adap, &msg, 1);
+	}
+
+	buf[0] = 0xc0;
+	buf[1] = 0x99;    // lpsel, for 6MHz + 2
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	i2c_transfer(priv->i2c_adap, &msg, 1);
+
+	buf[0] = 0x60;
+	buf[1] = 0x3c;
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	i2c_transfer(priv->i2c_adap, &msg, 1);
+
+	/* correct CP value */
+	buf[0] = 0x30;
+	buf[1] = 0x10 + tda827xa_dvbt[i].scr;
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	i2c_transfer(priv->i2c_adap, &msg, 1);
+
+	msleep(163);
+	buf[0] = 0xc0;
+	buf[1] = 0x39;  // lpsel, for 6MHz + 2
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	i2c_transfer(priv->i2c_adap, &msg, 1);
+
+	msleep(3);
+	/* freeze AGC1 */
+	buf[0] = 0x50;
+	buf[1] = 0x4f + (tda827xa_dvbt[i].gc3 << 4);
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	i2c_transfer(priv->i2c_adap, &msg, 1);
+
+	priv->frequency = tuner_freq - if_freq; // FIXME
+	priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
+
+	return 0;
+}
+
+static int tda827xa_sleep(struct dvb_frontend *fe)
+{
+	struct tda827x_priv *priv = fe->tuner_priv;
+	static u8 buf[] = { 0x30, 0x90 };
+	struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
+			       .buf = buf, .len = sizeof(buf) };
+
+	dprintk("%s:\n", __FUNCTION__);
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+
+	i2c_transfer(priv->i2c_adap, &msg, 1);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	if (priv->cfg && priv->cfg->sleep)
+		priv->cfg->sleep(fe);
+
+	return 0;
+}
+
+static int tda827x_release(struct dvb_frontend *fe)
+{
+	kfree(fe->tuner_priv);
+	fe->tuner_priv = NULL;
+	return 0;
+}
+
+static int tda827x_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+	struct tda827x_priv *priv = fe->tuner_priv;
+	*frequency = priv->frequency;
+	return 0;
+}
+
+static int tda827x_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+	struct tda827x_priv *priv = fe->tuner_priv;
+	*bandwidth = priv->bandwidth;
+	return 0;
+}
+
+static int tda827x_init(struct dvb_frontend *fe)
+{
+	struct tda827x_priv *priv = fe->tuner_priv;
+	dprintk("%s:\n", __FUNCTION__);
+	if (priv->cfg && priv->cfg->init)
+		priv->cfg->init(fe);
+
+	return 0;
+}
+
+static int tda827x_probe_version(struct dvb_frontend *fe);
+
+static int tda827x_initial_init(struct dvb_frontend *fe)
+{
+	int ret;
+	ret = tda827x_probe_version(fe);
+	if (ret)
+		return ret;
+	return fe->ops.tuner_ops.init(fe);
+}
+
+static int tda827x_initial_sleep(struct dvb_frontend *fe)
+{
+	int ret;
+	ret = tda827x_probe_version(fe);
+	if (ret)
+		return ret;
+	return fe->ops.tuner_ops.sleep(fe);
+}
+
+static struct dvb_tuner_ops tda827xo_tuner_ops = {
+	.info = {
+		.name = "Philips TDA827X",
+		.frequency_min  =  55000000,
+		.frequency_max  = 860000000,
+		.frequency_step =    250000
+	},
+	.release = tda827x_release,
+	.init = tda827x_initial_init,
+	.sleep = tda827x_initial_sleep,
+	.set_params = tda827xo_set_params,
+	.get_frequency = tda827x_get_frequency,
+	.get_bandwidth = tda827x_get_bandwidth,
+};
+
+static struct dvb_tuner_ops tda827xa_tuner_ops = {
+	.info = {
+		.name = "Philips TDA827XA",
+		.frequency_min  =  44000000,
+		.frequency_max  = 906000000,
+		.frequency_step =     62500
+	},
+	.release = tda827x_release,
+	.init = tda827x_init,
+	.sleep = tda827xa_sleep,
+	.set_params = tda827xa_set_params,
+	.get_frequency = tda827x_get_frequency,
+	.get_bandwidth = tda827x_get_bandwidth,
+};
+
+static int tda827x_probe_version(struct dvb_frontend *fe)
+{	u8 data;
+	struct tda827x_priv *priv = fe->tuner_priv;
+	struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = I2C_M_RD,
+			       .buf = &data, .len = 1 };
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(priv->i2c_adap, &msg, 1) != 1) {
+		printk("%s: could not read from tuner at addr: 0x%02x\n",
+		       __FUNCTION__, msg.addr << 1);
+		return -EIO;
+	}
+	if ((data & 0x3c) == 0) {
+		dprintk("tda827x tuner found\n");
+		fe->ops.tuner_ops.init  = tda827x_init;
+		fe->ops.tuner_ops.sleep = tda827xo_sleep;
+	} else {
+		dprintk("tda827xa tuner found\n");
+		memcpy(&fe->ops.tuner_ops, &tda827xa_tuner_ops, sizeof(struct dvb_tuner_ops));
+	}
+	return 0;
+}
+
+struct dvb_frontend *tda827x_attach(struct dvb_frontend *fe, int addr,
+				    struct i2c_adapter *i2c,
+				    struct tda827x_config *cfg)
+{
+	struct tda827x_priv *priv = NULL;
+
+	dprintk("%s:\n", __FUNCTION__);
+	priv = kzalloc(sizeof(struct tda827x_priv), GFP_KERNEL);
+	if (priv == NULL)
+		return NULL;
+
+	priv->i2c_addr = addr;
+	priv->i2c_adap = i2c;
+	priv->cfg = cfg;
+	memcpy(&fe->ops.tuner_ops, &tda827xo_tuner_ops, sizeof(struct dvb_tuner_ops));
+
+	fe->tuner_priv = priv;
+
+	return fe;
+}
+
+EXPORT_SYMBOL(tda827x_attach);
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("DVB TDA827x driver");
+MODULE_AUTHOR("Hartmut Hackmann <hartmut.hackmann@t-online.de>");
+MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
+MODULE_LICENSE("GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/tda827x.h b/drivers/media/dvb/frontends/tda827x.h
new file mode 100644
index 0000000..69e8263
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda827x.h
@@ -0,0 +1,62 @@
+  /*
+     DVB Driver for Philips tda827x / tda827xa Silicon tuners
+
+     (c) 2005 Hartmut Hackmann
+     (c) 2007 Michael Krufky
+
+     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.
+
+  */
+
+#ifndef __DVB_TDA827X_H__
+#define __DVB_TDA827X_H__
+
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+
+struct tda827x_config
+{
+	void (*lna_gain) (struct dvb_frontend *fe, int high);
+	int (*init) (struct dvb_frontend *fe);
+	int (*sleep) (struct dvb_frontend *fe);
+};
+
+
+/**
+ * Attach a tda827x tuner to the supplied frontend structure.
+ *
+ * @param fe Frontend to attach to.
+ * @param addr i2c address of the tuner.
+ * @param i2c i2c adapter to use.
+ * @param cfg optional callback function pointers.
+ * @return FE pointer on success, NULL on failure.
+ */
+#if defined(CONFIG_DVB_TDA827X) || (defined(CONFIG_DVB_TDA827X_MODULE) && defined(MODULE))
+extern struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe, int addr,
+					   struct i2c_adapter *i2c,
+					   struct tda827x_config *cfg);
+#else
+static inline struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe,
+						  int addr,
+						  struct i2c_adapter *i2c,
+						  struct tda827x_config *cfg)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+	return NULL;
+}
+#endif // CONFIG_DVB_TDA827X
+
+#endif // __DVB_TDA827X_H__
diff --git a/drivers/media/dvb/pluto2/Kconfig b/drivers/media/dvb/pluto2/Kconfig
index 9b84b1b..7d8e6e8 100644
--- a/drivers/media/dvb/pluto2/Kconfig
+++ b/drivers/media/dvb/pluto2/Kconfig
@@ -2,7 +2,6 @@
 	tristate "Pluto2 cards"
 	depends on DVB_CORE && PCI && I2C
 	select I2C_ALGOBIT
-	select DVB_PLL
 	select DVB_TDA1004X
 	help
 	  Support for PCI cards based on the Pluto2 FPGA like the Satelco
diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig
index eec7ccf..7751628 100644
--- a/drivers/media/dvb/ttpci/Kconfig
+++ b/drivers/media/dvb/ttpci/Kconfig
@@ -3,7 +3,6 @@
 	depends on DVB_CORE && PCI && I2C && VIDEO_V4L1
 	select FW_LOADER if !DVB_AV7110_FIRMWARE
 	select VIDEO_SAA7146_VV
-	select DVB_PLL
 	select DVB_VES1820 if !DVB_FE_CUSTOMISE
 	select DVB_VES1X93 if !DVB_FE_CUSTOMISE
 	select DVB_STV0299 if !DVB_FE_CUSTOMISE
@@ -62,13 +61,13 @@
 	tristate "Budget cards"
 	depends on DVB_CORE && PCI && I2C && VIDEO_V4L1
 	select VIDEO_SAA7146
-	select DVB_PLL
 	select DVB_STV0299 if !DVB_FE_CUSTOMISE
 	select DVB_VES1X93 if !DVB_FE_CUSTOMISE
 	select DVB_VES1820 if !DVB_FE_CUSTOMISE
 	select DVB_L64781 if !DVB_FE_CUSTOMISE
 	select DVB_TDA8083 if !DVB_FE_CUSTOMISE
 	select DVB_TDA10021 if !DVB_FE_CUSTOMISE
+	select DVB_TDA10023 if !DVB_FE_CUSTOMISE
 	select DVB_S5H1420 if !DVB_FE_CUSTOMISE
 	select DVB_TDA10086 if !DVB_FE_CUSTOMISE
 	select DVB_TDA826X if !DVB_FE_CUSTOMISE
@@ -87,7 +86,6 @@
 	tristate "Budget cards with onboard CI connector"
 	depends on DVB_CORE && PCI && I2C && VIDEO_V4L1
 	select VIDEO_SAA7146
-	select DVB_PLL
 	select DVB_STV0297 if !DVB_FE_CUSTOMISE
 	select DVB_STV0299 if !DVB_FE_CUSTOMISE
 	select DVB_TDA1004X if !DVB_FE_CUSTOMISE
@@ -114,6 +112,7 @@
 	select DVB_STV0299 if !DVB_FE_CUSTOMISE
 	select DVB_TDA1004X if !DVB_FE_CUSTOMISE
 	select DVB_TDA10021 if !DVB_FE_CUSTOMISE
+	select DVB_TDA10023 if !DVB_FE_CUSTOMISE
 	select DVB_TUA6100 if !DVB_FE_CUSTOMISE
 	select FW_LOADER
 	help
@@ -130,7 +129,6 @@
 	tristate "AV7110 cards with Budget Patch"
 	depends on DVB_CORE && DVB_BUDGET && VIDEO_V4L1
 	select DVB_AV7110
-	select DVB_PLL
 	select DVB_STV0299 if !DVB_FE_CUSTOMISE
 	select DVB_VES1X93 if !DVB_FE_CUSTOMISE
 	select DVB_TDA8083 if !DVB_FE_CUSTOMISE
diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c
index 29ed532..67becdd 100644
--- a/drivers/media/dvb/ttpci/av7110.c
+++ b/drivers/media/dvb/ttpci/av7110.c
@@ -219,7 +219,10 @@
 		av7110->recover(av7110);
 
 	restart_feeds(av7110);
-	av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, av7110->ir_config);
+
+#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE)
+	av7110_check_ir_config(av7110, true);
+#endif
 }
 
 static void av7110_arm_sync(struct av7110 *av7110)
@@ -250,6 +253,10 @@
 		if (!av7110->arm_ready)
 			continue;
 
+#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE)
+		av7110_check_ir_config(av7110, false);
+#endif
+
 		if (mutex_lock_interruptible(&av7110->dcomlock))
 			break;
 		newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2);
@@ -667,8 +674,8 @@
 		return;
 
 	case DATA_IRCOMMAND:
-		if (av7110->ir_handler)
-			av7110->ir_handler(av7110,
+		if (av7110->ir.ir_handler)
+			av7110->ir.ir_handler(av7110,
 				swahw32(irdebi(av7110, DEBINOSWAP, Reserved, 0, 4)));
 		iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2);
 		break;
@@ -1907,8 +1914,10 @@
 	if (av7110->fe_synced == synced)
 		return 0;
 
-	if (av7110->playing)
+	if (av7110->playing) {
+		av7110->fe_synced = synced;
 		return 0;
+	}
 
 	if (mutex_lock_interruptible(&av7110->pid_mutex))
 		return -ERESTARTSYS;
diff --git a/drivers/media/dvb/ttpci/av7110.h b/drivers/media/dvb/ttpci/av7110.h
index b98bd45..115002b 100644
--- a/drivers/media/dvb/ttpci/av7110.h
+++ b/drivers/media/dvb/ttpci/av7110.h
@@ -5,6 +5,7 @@
 #include <linux/socket.h>
 #include <linux/netdevice.h>
 #include <linux/i2c.h>
+#include <linux/input.h>
 
 #include <linux/dvb/video.h>
 #include <linux/dvb/audio.h>
@@ -66,6 +67,27 @@
 };
 
 
+struct av7110;
+
+/* infrared remote control */
+struct infrared {
+	u16	key_map[256];
+	struct input_dev	*input_dev;
+	char			input_phys[32];
+	struct timer_list	keyup_timer;
+	struct tasklet_struct	ir_tasklet;
+	void			(*ir_handler)(struct av7110 *av7110, u32 ircom);
+	u32			ir_command;
+	u32			ir_config;
+	u32			device_mask;
+	u8			protocol;
+	u8			inversion;
+	u16			last_key;
+	u16			last_toggle;
+	u8			delay_timer_finished;
+};
+
+
 /* place to store all the necessary device information */
 struct av7110 {
 
@@ -227,10 +249,7 @@
 	u16			wssMode;
 	u16			wssData;
 
-	u32			ir_config;
-	u32			ir_command;
-	void			(*ir_handler)(struct av7110 *av7110, u32 ircom);
-	struct tasklet_struct	ir_tasklet;
+	struct infrared		ir;
 
 	/* firmware stuff */
 	unsigned char *bin_fw;
@@ -268,6 +287,7 @@
 extern int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid,
 		       u16 subpid, u16 pcrpid);
 
+extern int av7110_check_ir_config(struct av7110 *av7110, int force);
 extern int av7110_ir_init(struct av7110 *av7110);
 extern void av7110_ir_exit(struct av7110 *av7110);
 
diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/dvb/ttpci/av7110_av.c
index e719af8..654c9e9 100644
--- a/drivers/media/dvb/ttpci/av7110_av.c
+++ b/drivers/media/dvb/ttpci/av7110_av.c
@@ -1009,7 +1009,7 @@
 		if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY)
 			ret = av7110_av_stop(av7110, RP_VIDEO);
 		else
-			ret = vidcom(av7110, VIDEO_CMD_STOP,
+			ret = vidcom(av7110, AV_VIDEO_CMD_STOP,
 			       av7110->videostate.video_blank ? 0 : 1);
 		if (!ret)
 			av7110->trickmode = TRICK_NONE;
@@ -1019,7 +1019,7 @@
 		av7110->trickmode = TRICK_NONE;
 		if (av7110->videostate.play_state == VIDEO_FREEZED) {
 			av7110->videostate.play_state = VIDEO_PLAYING;
-			ret = vidcom(av7110, VIDEO_CMD_PLAY, 0);
+			ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0);
 			if (ret)
 				break;
 		}
@@ -1034,7 +1034,7 @@
 			ret = av7110_av_start_play(av7110, RP_VIDEO);
 		}
 		if (!ret)
-			ret = vidcom(av7110, VIDEO_CMD_PLAY, 0);
+			ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0);
 		if (!ret)
 			av7110->videostate.play_state = VIDEO_PLAYING;
 		break;
@@ -1044,7 +1044,7 @@
 		if (av7110->playing & RP_VIDEO)
 			ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Pause, 0);
 		else
-			ret = vidcom(av7110, VIDEO_CMD_FREEZE, 1);
+			ret = vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1);
 		if (!ret)
 			av7110->trickmode = TRICK_FREEZE;
 		break;
@@ -1053,7 +1053,7 @@
 		if (av7110->playing & RP_VIDEO)
 			ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Continue, 0);
 		if (!ret)
-			ret = vidcom(av7110, VIDEO_CMD_PLAY, 0);
+			ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0);
 		if (!ret) {
 			av7110->videostate.play_state = VIDEO_PLAYING;
 			av7110->trickmode = TRICK_NONE;
@@ -1136,7 +1136,7 @@
 			ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
 					    __Scan_I, 2, AV_PES, 0);
 		else
-			ret = vidcom(av7110, VIDEO_CMD_FFWD, arg);
+			ret = vidcom(av7110, AV_VIDEO_CMD_FFWD, arg);
 		if (!ret) {
 			av7110->trickmode = TRICK_FAST;
 			av7110->videostate.play_state = VIDEO_PLAYING;
@@ -1147,13 +1147,13 @@
 		if (av7110->playing&RP_VIDEO) {
 			ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0);
 			if (!ret)
-				ret = vidcom(av7110, VIDEO_CMD_SLOW, arg);
+				ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg);
 		} else {
-			ret = vidcom(av7110, VIDEO_CMD_PLAY, 0);
+			ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0);
 			if (!ret)
-				ret = vidcom(av7110, VIDEO_CMD_STOP, 0);
+				ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 0);
 			if (!ret)
-				ret = vidcom(av7110, VIDEO_CMD_SLOW, arg);
+				ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg);
 		}
 		if (!ret) {
 			av7110->trickmode = TRICK_SLOW;
@@ -1182,10 +1182,10 @@
 				ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
 						    __Slow, 2, 0, 0);
 				if (!ret)
-					ret = vidcom(av7110, VIDEO_CMD_SLOW, arg);
+					ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg);
 			}
 			if (av7110->trickmode == TRICK_FREEZE)
-				ret = vidcom(av7110, VIDEO_CMD_STOP, 1);
+				ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 1);
 		}
 		break;
 
diff --git a/drivers/media/dvb/ttpci/av7110_hw.h b/drivers/media/dvb/ttpci/av7110_hw.h
index 4e173c6..673d9b3 100644
--- a/drivers/media/dvb/ttpci/av7110_hw.h
+++ b/drivers/media/dvb/ttpci/av7110_hw.h
@@ -216,11 +216,11 @@
 #define VID_CENTRE_CUT_PREF	0x05	/* PanScan with zero vector */
 
 /* MPEG video decoder commands */
-#define VIDEO_CMD_STOP		0x000e
-#define VIDEO_CMD_PLAY		0x000d
-#define VIDEO_CMD_FREEZE	0x0102
-#define VIDEO_CMD_FFWD		0x0016
-#define VIDEO_CMD_SLOW		0x0022
+#define AV_VIDEO_CMD_STOP	0x000e
+#define AV_VIDEO_CMD_PLAY	0x000d
+#define AV_VIDEO_CMD_FREEZE	0x0102
+#define AV_VIDEO_CMD_FFWD	0x0016
+#define AV_VIDEO_CMD_SLOW	0x0022
 
 /* MPEG audio decoder commands */
 #define AUDIO_CMD_MUTE		0x0001
diff --git a/drivers/media/dvb/ttpci/av7110_ir.c b/drivers/media/dvb/ttpci/av7110_ir.c
index f59465b..a97f166 100644
--- a/drivers/media/dvb/ttpci/av7110_ir.c
+++ b/drivers/media/dvb/ttpci/av7110_ir.c
@@ -1,8 +1,31 @@
+/*
+ * Driver for the remote control of SAA7146 based AV7110 cards
+ *
+ * Copyright (C) 1999-2003 Holger Waechtler <holger@convergence.de>
+ * Copyright (C) 2003-2007 Oliver Endriss <o.endriss@gmx.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.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+
 #include <linux/types.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
-#include <linux/input.h>
 #include <linux/proc_fs.h>
 #include <linux/kernel.h>
 #include <asm/bitops.h>
@@ -10,18 +33,37 @@
 #include "av7110.h"
 #include "av7110_hw.h"
 
-#define UP_TIMEOUT (HZ*7/25)
 
-/* enable ir debugging by or'ing debug with 16 */
+#define AV_CNT		4
+
+#define IR_RC5		0
+#define IR_RCMM		1
+#define IR_RC5_EXT	2 /* internal only */
+
+#define IR_ALL		0xffffffff
+
+#define UP_TIMEOUT	(HZ*7/25)
+
+
+/* Note: enable ir debugging by or'ing debug with 16 */
+
+static int ir_protocol[AV_CNT] = { IR_RCMM, IR_RCMM, IR_RCMM, IR_RCMM};
+module_param_array(ir_protocol, int, NULL, 0644);
+MODULE_PARM_DESC(ir_protocol, "Infrared protocol: 0 RC5, 1 RCMM (default)");
+
+static int ir_inversion[AV_CNT];
+module_param_array(ir_inversion, int, NULL, 0644);
+MODULE_PARM_DESC(ir_inversion, "Inversion of infrared signal: 0 not inverted (default), 1 inverted");
+
+static uint ir_device_mask[AV_CNT] = { IR_ALL, IR_ALL, IR_ALL, IR_ALL };
+module_param_array(ir_device_mask, uint, NULL, 0644);
+MODULE_PARM_DESC(ir_device_mask, "Bitmask of infrared devices: bit 0..31 = device 0..31 (default: all)");
+
 
 static int av_cnt;
-static struct av7110 *av_list[4];
-static struct input_dev *input_dev;
-static char input_phys[32];
+static struct av7110 *av_list[AV_CNT];
 
-static u8 delay_timer_finished;
-
-static u16 key_map [256] = {
+static u16 default_key_map [256] = {
 	KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7,
 	KEY_8, KEY_9, KEY_BACK, 0, KEY_POWER, KEY_MUTE, 0, KEY_INFO,
 	KEY_VOLUMEUP, KEY_VOLUMEDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -45,141 +87,194 @@
 };
 
 
-static void av7110_emit_keyup(unsigned long data)
+/* key-up timer */
+static void av7110_emit_keyup(unsigned long parm)
 {
-	if (!data || !test_bit(data, input_dev->key))
+	struct infrared *ir = (struct infrared *) parm;
+
+	if (!ir || !test_bit(ir->last_key, ir->input_dev->key))
 		return;
 
-	input_report_key(input_dev, data, 0);
-	input_sync(input_dev);
+	input_report_key(ir->input_dev, ir->last_key, 0);
+	input_sync(ir->input_dev);
 }
 
 
-static struct timer_list keyup_timer = { .function = av7110_emit_keyup };
-
-
+/* tasklet */
 static void av7110_emit_key(unsigned long parm)
 {
-	struct av7110 *av7110 = (struct av7110 *) parm;
-	u32 ir_config = av7110->ir_config;
-	u32 ircom = av7110->ir_command;
+	struct infrared *ir = (struct infrared *) parm;
+	u32 ircom = ir->ir_command;
 	u8 data;
 	u8 addr;
-	static u16 old_toggle = 0;
-	u16 new_toggle;
+	u16 toggle;
 	u16 keycode;
 
 	/* extract device address and data */
-	switch (ir_config & 0x0003) {
-	case 0:	/* RC5: 5 bits device address, 6 bits data */
+	switch (ir->protocol) {
+	case IR_RC5: /* RC5: 5 bits device address, 6 bits data */
 		data = ircom & 0x3f;
 		addr = (ircom >> 6) & 0x1f;
+		toggle = ircom & 0x0800;
 		break;
 
-	case 1:	/* RCMM: 8(?) bits device address, 8(?) bits data */
+	case IR_RCMM: /* RCMM: ? bits device address, ? bits data */
 		data = ircom & 0xff;
-		addr = (ircom >> 8) & 0xff;
+		addr = (ircom >> 8) & 0x1f;
+		toggle = ircom & 0x8000;
 		break;
 
-	case 2:	/* extended RC5: 5 bits device address, 7 bits data */
+	case IR_RC5_EXT: /* extended RC5: 5 bits device address, 7 bits data */
 		data = ircom & 0x3f;
 		addr = (ircom >> 6) & 0x1f;
 		/* invert 7th data bit for backward compatibility with RC5 keymaps */
 		if (!(ircom & 0x1000))
 			data |= 0x40;
+		toggle = ircom & 0x0800;
 		break;
 
 	default:
-		printk("invalid ir_config %x\n", ir_config);
+		printk("%s invalid protocol %x\n", __FUNCTION__, ir->protocol);
 		return;
 	}
 
-	keycode = key_map[data];
+	input_event(ir->input_dev, EV_MSC, MSC_RAW, (addr << 16) | data);
+	input_event(ir->input_dev, EV_MSC, MSC_SCAN, data);
 
-	dprintk(16, "code %08x -> addr %i data 0x%02x -> keycode %i\n",
-		ircom, addr, data, keycode);
+	keycode = ir->key_map[data];
 
-	/* check device address (if selected) */
-	if (ir_config & 0x4000)
-		if (addr != ((ir_config >> 16) & 0xff))
-			return;
+	dprintk(16, "%s: code %08x -> addr %i data 0x%02x -> keycode %i\n",
+		__FUNCTION__, ircom, addr, data, keycode);
+
+	/* check device address */
+	if (!(ir->device_mask & (1 << addr)))
+		return;
 
 	if (!keycode) {
-		printk ("%s: unknown key 0x%02x!!\n", __FUNCTION__, data);
+		printk ("%s: code %08x -> addr %i data 0x%02x -> unknown key!\n",
+			__FUNCTION__, ircom, addr, data);
 		return;
 	}
 
-	if ((ir_config & 0x0003) == 1)
-		new_toggle = 0; /* RCMM */
-	else
-		new_toggle = (ircom & 0x800); /* RC5, extended RC5 */
-
-	if (timer_pending(&keyup_timer)) {
-		del_timer(&keyup_timer);
-		if (keyup_timer.data != keycode || new_toggle != old_toggle) {
-			delay_timer_finished = 0;
-			input_event(input_dev, EV_KEY, keyup_timer.data, 0);
-			input_event(input_dev, EV_KEY, keycode, 1);
-			input_sync(input_dev);
-		} else if (delay_timer_finished) {
-			input_event(input_dev, EV_KEY, keycode, 2);
-			input_sync(input_dev);
+	if (timer_pending(&ir->keyup_timer)) {
+		del_timer(&ir->keyup_timer);
+		if (ir->last_key != keycode || toggle != ir->last_toggle) {
+			ir->delay_timer_finished = 0;
+			input_event(ir->input_dev, EV_KEY, ir->last_key, 0);
+			input_event(ir->input_dev, EV_KEY, keycode, 1);
+			input_sync(ir->input_dev);
+		} else if (ir->delay_timer_finished) {
+			input_event(ir->input_dev, EV_KEY, keycode, 2);
+			input_sync(ir->input_dev);
 		}
 	} else {
-		delay_timer_finished = 0;
-		input_event(input_dev, EV_KEY, keycode, 1);
-		input_sync(input_dev);
+		ir->delay_timer_finished = 0;
+		input_event(ir->input_dev, EV_KEY, keycode, 1);
+		input_sync(ir->input_dev);
 	}
 
-	keyup_timer.expires = jiffies + UP_TIMEOUT;
-	keyup_timer.data = keycode;
+	ir->last_key = keycode;
+	ir->last_toggle = toggle;
 
-	add_timer(&keyup_timer);
+	ir->keyup_timer.expires = jiffies + UP_TIMEOUT;
+	add_timer(&ir->keyup_timer);
 
-	old_toggle = new_toggle;
 }
 
-static void input_register_keys(void)
+
+/* register with input layer */
+static void input_register_keys(struct infrared *ir)
 {
 	int i;
 
-	memset(input_dev->keybit, 0, sizeof(input_dev->keybit));
+	set_bit(EV_KEY, ir->input_dev->evbit);
+	set_bit(EV_REP, ir->input_dev->evbit);
+	set_bit(EV_MSC, ir->input_dev->evbit);
 
-	for (i = 0; i < ARRAY_SIZE(key_map); i++) {
-		if (key_map[i] > KEY_MAX)
-			key_map[i] = 0;
-		else if (key_map[i] > KEY_RESERVED)
-			set_bit(key_map[i], input_dev->keybit);
+	set_bit(MSC_RAW, ir->input_dev->mscbit);
+	set_bit(MSC_SCAN, ir->input_dev->mscbit);
+
+	memset(ir->input_dev->keybit, 0, sizeof(ir->input_dev->keybit));
+
+	for (i = 0; i < ARRAY_SIZE(ir->key_map); i++) {
+		if (ir->key_map[i] > KEY_MAX)
+			ir->key_map[i] = 0;
+		else if (ir->key_map[i] > KEY_RESERVED)
+			set_bit(ir->key_map[i], ir->input_dev->keybit);
 	}
+
+	ir->input_dev->keycode = ir->key_map;
+	ir->input_dev->keycodesize = sizeof(ir->key_map[0]);
+	ir->input_dev->keycodemax = ARRAY_SIZE(ir->key_map);
 }
 
 
-static void input_repeat_key(unsigned long data)
+/* called by the input driver after rep[REP_DELAY] ms */
+static void input_repeat_key(unsigned long parm)
 {
-	/* called by the input driver after rep[REP_DELAY] ms */
-	delay_timer_finished = 1;
+	struct infrared *ir = (struct infrared *) parm;
+
+	ir->delay_timer_finished = 1;
 }
 
 
-static int av7110_setup_irc_config(struct av7110 *av7110, u32 ir_config)
+/* check for configuration changes */
+int av7110_check_ir_config(struct av7110 *av7110, int force)
 {
-	int ret = 0;
+	int i;
+	int modified = force;
+	int ret = -ENODEV;
 
-	dprintk(4, "%p\n", av7110);
-	if (av7110) {
-		ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, ir_config);
-		av7110->ir_config = ir_config;
+	for (i = 0; i < av_cnt; i++)
+		if (av7110 == av_list[i])
+			break;
+
+	if (i < av_cnt && av7110) {
+		if ((av7110->ir.protocol & 1) != ir_protocol[i] ||
+		    av7110->ir.inversion != ir_inversion[i])
+			modified = true;
+
+		if (modified) {
+			/* protocol */
+			if (ir_protocol[i]) {
+				ir_protocol[i] = 1;
+				av7110->ir.protocol = IR_RCMM;
+				av7110->ir.ir_config = 0x0001;
+			} else if (FW_VERSION(av7110->arm_app) >= 0x2620) {
+				av7110->ir.protocol = IR_RC5_EXT;
+				av7110->ir.ir_config = 0x0002;
+			} else {
+				av7110->ir.protocol = IR_RC5;
+				av7110->ir.ir_config = 0x0000;
+			}
+			/* inversion */
+			if (ir_inversion[i]) {
+				ir_inversion[i] = 1;
+				av7110->ir.ir_config |= 0x8000;
+			}
+			av7110->ir.inversion = ir_inversion[i];
+			/* update ARM */
+			ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1,
+						av7110->ir.ir_config);
+		} else
+			ret = 0;
+
+		/* address */
+		if (av7110->ir.device_mask != ir_device_mask[i])
+			av7110->ir.device_mask = ir_device_mask[i];
 	}
+
 	return ret;
 }
 
 
+/* /proc/av7110_ir interface */
 static int av7110_ir_write_proc(struct file *file, const char __user *buffer,
 				unsigned long count, void *data)
 {
 	char *page;
-	int size = 4 + 256 * sizeof(u16);
 	u32 ir_config;
+	int size = sizeof ir_config + sizeof av_list[0]->ir.key_map;
 	int i;
 
 	if (count < size)
@@ -194,71 +289,86 @@
 		return -EFAULT;
 	}
 
-	memcpy(&ir_config, page, 4);
-	memcpy(&key_map, page + 4, 256 * sizeof(u16));
+	memcpy(&ir_config, page, sizeof ir_config);
+
+	for (i = 0; i < av_cnt; i++) {
+		/* keymap */
+		memcpy(av_list[i]->ir.key_map, page + sizeof ir_config,
+			sizeof(av_list[i]->ir.key_map));
+		/* protocol, inversion, address */
+		ir_protocol[i] = ir_config & 0x0001;
+		ir_inversion[i] = ir_config & 0x8000 ? 1 : 0;
+		if (ir_config & 0x4000)
+			ir_device_mask[i] = 1 << ((ir_config >> 16) & 0x1f);
+		else
+			ir_device_mask[i] = IR_ALL;
+		/* update configuration */
+		av7110_check_ir_config(av_list[i], false);
+		input_register_keys(&av_list[i]->ir);
+	}
 	vfree(page);
-	if (FW_VERSION(av_list[0]->arm_app) >= 0x2620 && !(ir_config & 0x0001))
-		ir_config |= 0x0002; /* enable extended RC5 */
-	for (i = 0; i < av_cnt; i++)
-		av7110_setup_irc_config(av_list[i], ir_config);
-	input_register_keys();
 	return count;
 }
 
 
+/* interrupt handler */
 static void ir_handler(struct av7110 *av7110, u32 ircom)
 {
-	dprintk(4, "ircommand = %08x\n", ircom);
-	av7110->ir_command = ircom;
-	tasklet_schedule(&av7110->ir_tasklet);
+	dprintk(4, "ir command = %08x\n", ircom);
+	av7110->ir.ir_command = ircom;
+	tasklet_schedule(&av7110->ir.ir_tasklet);
 }
 
 
 int __devinit av7110_ir_init(struct av7110 *av7110)
 {
+	struct input_dev *input_dev;
 	static struct proc_dir_entry *e;
 	int err;
 
 	if (av_cnt >= ARRAY_SIZE(av_list))
 		return -ENOSPC;
 
-	av7110_setup_irc_config(av7110, 0x0001);
 	av_list[av_cnt++] = av7110;
+	av7110_check_ir_config(av7110, true);
+
+	init_timer(&av7110->ir.keyup_timer);
+	av7110->ir.keyup_timer.function = av7110_emit_keyup;
+	av7110->ir.keyup_timer.data = (unsigned long) &av7110->ir;
+
+	input_dev = input_allocate_device();
+	if (!input_dev)
+		return -ENOMEM;
+
+	av7110->ir.input_dev = input_dev;
+	snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys),
+		"pci-%s/ir0", pci_name(av7110->dev->pci));
+
+	input_dev->name = "DVB on-card IR receiver";
+
+	input_dev->phys = av7110->ir.input_phys;
+	input_dev->id.bustype = BUS_PCI;
+	input_dev->id.version = 2;
+	if (av7110->dev->pci->subsystem_vendor) {
+		input_dev->id.vendor = av7110->dev->pci->subsystem_vendor;
+		input_dev->id.product = av7110->dev->pci->subsystem_device;
+	} else {
+		input_dev->id.vendor = av7110->dev->pci->vendor;
+		input_dev->id.product = av7110->dev->pci->device;
+	}
+	input_dev->cdev.dev = &av7110->dev->pci->dev;
+	/* initial keymap */
+	memcpy(av7110->ir.key_map, default_key_map, sizeof av7110->ir.key_map);
+	input_register_keys(&av7110->ir);
+	err = input_register_device(input_dev);
+	if (err) {
+		input_free_device(input_dev);
+		return err;
+	}
+	input_dev->timer.function = input_repeat_key;
+	input_dev->timer.data = (unsigned long) &av7110->ir;
 
 	if (av_cnt == 1) {
-		init_timer(&keyup_timer);
-		keyup_timer.data = 0;
-
-		input_dev = input_allocate_device();
-		if (!input_dev)
-			return -ENOMEM;
-
-		snprintf(input_phys, sizeof(input_phys),
-			"pci-%s/ir0", pci_name(av7110->dev->pci));
-
-		input_dev->name = "DVB on-card IR receiver";
-
-		input_dev->phys = input_phys;
-		input_dev->id.bustype = BUS_PCI;
-		input_dev->id.version = 1;
-		if (av7110->dev->pci->subsystem_vendor) {
-			input_dev->id.vendor = av7110->dev->pci->subsystem_vendor;
-			input_dev->id.product = av7110->dev->pci->subsystem_device;
-		} else {
-			input_dev->id.vendor = av7110->dev->pci->vendor;
-			input_dev->id.product = av7110->dev->pci->device;
-		}
-		input_dev->cdev.dev = &av7110->dev->pci->dev;
-		set_bit(EV_KEY, input_dev->evbit);
-		set_bit(EV_REP, input_dev->evbit);
-		input_register_keys();
-		err = input_register_device(input_dev);
-		if (err) {
-			input_free_device(input_dev);
-			return err;
-		}
-		input_dev->timer.function = input_repeat_key;
-
 		e = create_proc_entry("av7110_ir", S_IFREG | S_IRUGO | S_IWUSR, NULL);
 		if (e) {
 			e->write_proc = av7110_ir_write_proc;
@@ -266,8 +376,8 @@
 		}
 	}
 
-	tasklet_init(&av7110->ir_tasklet, av7110_emit_key, (unsigned long) av7110);
-	av7110->ir_handler = ir_handler;
+	tasklet_init(&av7110->ir.ir_tasklet, av7110_emit_key, (unsigned long) &av7110->ir);
+	av7110->ir.ir_handler = ir_handler;
 
 	return 0;
 }
@@ -280,8 +390,10 @@
 	if (av_cnt == 0)
 		return;
 
-	av7110->ir_handler = NULL;
-	tasklet_kill(&av7110->ir_tasklet);
+	del_timer_sync(&av7110->ir.keyup_timer);
+	av7110->ir.ir_handler = NULL;
+	tasklet_kill(&av7110->ir.ir_tasklet);
+
 	for (i = 0; i < av_cnt; i++)
 		if (av_list[i] == av7110) {
 			av_list[i] = av_list[av_cnt-1];
@@ -289,14 +401,13 @@
 			break;
 		}
 
-	if (av_cnt == 1) {
-		del_timer_sync(&keyup_timer);
+	if (av_cnt == 1)
 		remove_proc_entry("av7110_ir", NULL);
-		input_unregister_device(input_dev);
-	}
+
+	input_unregister_device(av7110->ir.input_dev);
 
 	av_cnt--;
 }
 
-//MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>");
+//MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>, Oliver Endriss <o.endriss@gmx.de>");
 //MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c
index 3035b22..0e817d6 100644
--- a/drivers/media/dvb/ttpci/budget-av.c
+++ b/drivers/media/dvb/ttpci/budget-av.c
@@ -35,7 +35,7 @@
 
 #include "budget.h"
 #include "stv0299.h"
-#include "tda10021.h"
+#include "tda1002x.h"
 #include "tda1004x.h"
 #include "tua6100.h"
 #include "dvb-pll.h"
@@ -66,9 +66,6 @@
 	int slot_status;
 	struct dvb_ca_en50221 ca;
 	u8 reinitialise_demod:1;
-	u8 tda10021_poclkp:1;
-	u8 tda10021_ts_enabled;
-	int (*tda10021_set_frontend)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p);
 };
 
 static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot);
@@ -234,12 +231,6 @@
 	if (budget_av->reinitialise_demod)
 		dvb_frontend_reinitialise(budget_av->budget.dvb_frontend);
 
-	/* set tda10021 back to original clock configuration on reset */
-	if (budget_av->tda10021_poclkp) {
-		tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa0);
-		budget_av->tda10021_ts_enabled = 0;
-	}
-
 	return 0;
 }
 
@@ -256,11 +247,6 @@
 	ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
 	budget_av->slot_status = SLOTSTATUS_NONE;
 
-	/* set tda10021 back to original clock configuration when cam removed */
-	if (budget_av->tda10021_poclkp) {
-		tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa0);
-		budget_av->tda10021_ts_enabled = 0;
-	}
 	return 0;
 }
 
@@ -276,12 +262,6 @@
 
 	ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA);
 
-	/* tda10021 seems to need a different TS clock config when data is routed to the CAM */
-	if (budget_av->tda10021_poclkp) {
-		tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa1);
-		budget_av->tda10021_ts_enabled = 1;
-	}
-
 	return 0;
 }
 
@@ -631,37 +611,62 @@
 static int philips_cu1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
 {
 	struct budget *budget = (struct budget *) fe->dvb->priv;
-	u8 buf[4];
+	u8 buf[6];
 	struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) };
+	int i;
 
+#define CU1216_IF 36125000
 #define TUNER_MUL 62500
 
-	u32 div = (params->frequency + 36125000 + TUNER_MUL / 2) / TUNER_MUL;
+	u32 div = (params->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL;
 
 	buf[0] = (div >> 8) & 0x7f;
 	buf[1] = div & 0xff;
-	buf[2] = 0x86;
+	buf[2] = 0xce;
 	buf[3] = (params->frequency < 150000000 ? 0x01 :
 		  params->frequency < 445000000 ? 0x02 : 0x04);
+	buf[4] = 0xde;
+	buf[5] = 0x20;
 
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 1);
 	if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
 		return -EIO;
+
+	/* wait for the pll lock */
+	msg.flags = I2C_M_RD;
+	msg.len = 1;
+	for (i = 0; i < 20; i++) {
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 1);
+		if (i2c_transfer(&budget->i2c_adap, &msg, 1) == 1 && (buf[0] & 0x40))
+			break;
+		msleep(10);
+	}
+
+	/* switch the charge pump to the lower current */
+	msg.flags = 0;
+	msg.len = 2;
+	msg.buf = &buf[2];
+	buf[2] &= ~0x40;
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+
 	return 0;
 }
 
-static struct tda10021_config philips_cu1216_config = {
+static struct tda1002x_config philips_cu1216_config = {
 	.demod_address = 0x0c,
+	.invert = 1,
 };
 
-static struct tda10021_config philips_cu1216_config_altaddress = {
+static struct tda1002x_config philips_cu1216_config_altaddress = {
 	.demod_address = 0x0d,
+	.invert = 0,
 };
 
-
-
-
 static int philips_tu1216_tuner_init(struct dvb_frontend *fe)
 {
 	struct budget *budget = (struct budget *) fe->dvb->priv;
@@ -908,41 +913,28 @@
 	return pwm;
 }
 
-#define SUBID_DVBS_KNC1		0x0010
-#define SUBID_DVBS_KNC1_PLUS	0x0011
-#define SUBID_DVBS_TYPHOON	0x4f56
-#define SUBID_DVBS_CINERGY1200	0x1154
-#define SUBID_DVBS_CYNERGY1200N 0x1155
+#define SUBID_DVBS_KNC1			0x0010
+#define SUBID_DVBS_KNC1_PLUS		0x0011
+#define SUBID_DVBS_TYPHOON		0x4f56
+#define SUBID_DVBS_CINERGY1200		0x1154
+#define SUBID_DVBS_CYNERGY1200N 	0x1155
+#define SUBID_DVBS_TV_STAR		0x0014
+#define SUBID_DVBS_TV_STAR_CI		0x0016
+#define SUBID_DVBS_EASYWATCH_1  	0x001a
+#define SUBID_DVBS_EASYWATCH		0x001e
 
-#define SUBID_DVBS_TV_STAR	0x0014
-#define SUBID_DVBS_TV_STAR_CI	0x0016
-#define SUBID_DVBS_EASYWATCH_1  0x001a
-#define SUBID_DVBS_EASYWATCH	0x001e
-#define SUBID_DVBC_EASYWATCH	0x002a
-#define SUBID_DVBC_KNC1		0x0020
-#define SUBID_DVBC_KNC1_PLUS	0x0021
-#define SUBID_DVBC_CINERGY1200	0x1156
+#define SUBID_DVBC_EASYWATCH		0x002a
+#define SUBID_DVBC_EASYWATCH_MK3	0x002c
+#define SUBID_DVBC_KNC1			0x0020
+#define SUBID_DVBC_KNC1_PLUS		0x0021
+#define SUBID_DVBC_KNC1_MK3		0x0022
+#define SUBID_DVBC_KNC1_PLUS_MK3	0x0023
+#define SUBID_DVBC_CINERGY1200		0x1156
+#define SUBID_DVBC_CINERGY1200_MK3	0x1176
 
-#define SUBID_DVBT_KNC1_PLUS	0x0031
-#define SUBID_DVBT_KNC1		0x0030
-#define SUBID_DVBT_CINERGY1200	0x1157
-
-
-static int tda10021_set_frontend(struct dvb_frontend *fe,
-				 struct dvb_frontend_parameters *p)
-{
-	struct budget_av* budget_av = fe->dvb->priv;
-	int result;
-
-	result = budget_av->tda10021_set_frontend(fe, p);
-	if (budget_av->tda10021_ts_enabled) {
-		tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa1);
-	} else {
-		tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa0);
-	}
-
-	return result;
-}
+#define SUBID_DVBT_KNC1_PLUS		0x0031
+#define SUBID_DVBT_KNC1			0x0030
+#define SUBID_DVBT_CINERGY1200		0x1157
 
 static void frontend_init(struct budget_av *budget_av)
 {
@@ -961,6 +953,7 @@
 		case SUBID_DVBC_KNC1_PLUS:
 		case SUBID_DVBT_KNC1_PLUS:
 		case SUBID_DVBC_EASYWATCH:
+		case SUBID_DVBC_KNC1_PLUS_MK3:
 			saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI);
 			break;
 	}
@@ -1017,6 +1010,7 @@
 	case SUBID_DVBC_CINERGY1200:
 	case SUBID_DVBC_EASYWATCH:
 		budget_av->reinitialise_demod = 1;
+		budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240;
 		fe = dvb_attach(tda10021_attach, &philips_cu1216_config,
 				     &budget_av->budget.i2c_adap,
 				     read_pwm(budget_av));
@@ -1025,9 +1019,20 @@
 					     &budget_av->budget.i2c_adap,
 					     read_pwm(budget_av));
 		if (fe) {
-			budget_av->tda10021_poclkp = 1;
-			budget_av->tda10021_set_frontend = fe->ops.set_frontend;
-			fe->ops.set_frontend = tda10021_set_frontend;
+			fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params;
+		}
+		break;
+
+	case SUBID_DVBC_EASYWATCH_MK3:
+	case SUBID_DVBC_CINERGY1200_MK3:
+	case SUBID_DVBC_KNC1_MK3:
+	case SUBID_DVBC_KNC1_PLUS_MK3:
+		budget_av->reinitialise_demod = 1;
+		budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240;
+		fe = dvb_attach(tda10023_attach, &philips_cu1216_config,
+				     &budget_av->budget.i2c_adap,
+				     read_pwm(budget_av));
+		if (fe) {
 			fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params;
 		}
 		break;
@@ -1260,12 +1265,16 @@
 MAKE_BUDGET_INFO(satewpls, "Satelco EasyWatch DVB-S light", BUDGET_TVSTAR);
 MAKE_BUDGET_INFO(satewpls1, "Satelco EasyWatch DVB-S light", BUDGET_KNC1S);
 MAKE_BUDGET_INFO(satewplc, "Satelco EasyWatch DVB-C", BUDGET_KNC1CP);
+MAKE_BUDGET_INFO(satewcmk3, "Satelco EasyWatch DVB-C MK3", BUDGET_KNC1C_MK3);
 MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP);
 MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP);
+MAKE_BUDGET_INFO(knc1cmk3, "KNC1 DVB-C MK3", BUDGET_KNC1C_MK3);
+MAKE_BUDGET_INFO(knc1cpmk3, "KNC1 DVB-C Plus MK3", BUDGET_KNC1CP_MK3);
 MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP);
 MAKE_BUDGET_INFO(cin1200s, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S);
 MAKE_BUDGET_INFO(cin1200sn, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S);
 MAKE_BUDGET_INFO(cin1200c, "Terratec Cinergy 1200 DVB-C", BUDGET_CIN1200C);
+MAKE_BUDGET_INFO(cin1200cmk3, "Terratec Cinergy 1200 DVB-C MK3", BUDGET_CIN1200C_MK3);
 MAKE_BUDGET_INFO(cin1200t, "Terratec Cinergy 1200 DVB-T", BUDGET_CIN1200T);
 
 static struct pci_device_id pci_tbl[] = {
@@ -1279,13 +1288,17 @@
 	MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e),
 	MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a),
 	MAKE_EXTENSION_PCI(satewplc, 0x1894, 0x002a),
+	MAKE_EXTENSION_PCI(satewcmk3, 0x1894, 0x002c),
 	MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020),
 	MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021),
+	MAKE_EXTENSION_PCI(knc1cmk3, 0x1894, 0x0022),
+	MAKE_EXTENSION_PCI(knc1cpmk3, 0x1894, 0x0023),
 	MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030),
 	MAKE_EXTENSION_PCI(knc1tp, 0x1894, 0x0031),
 	MAKE_EXTENSION_PCI(cin1200s, 0x153b, 0x1154),
 	MAKE_EXTENSION_PCI(cin1200sn, 0x153b, 0x1155),
 	MAKE_EXTENSION_PCI(cin1200c, 0x153b, 0x1156),
+	MAKE_EXTENSION_PCI(cin1200cmk3, 0x153b, 0x1176),
 	MAKE_EXTENSION_PCI(cin1200t, 0x153b, 0x1157),
 	{
 	 .vendor = 0,
diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c
index 464feaf..4ed4599 100644
--- a/drivers/media/dvb/ttpci/budget-ci.c
+++ b/drivers/media/dvb/ttpci/budget-ci.c
@@ -73,21 +73,15 @@
 #define SLOTSTATUS_READY	8
 #define SLOTSTATUS_OCCUPIED	(SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY)
 
-/* Milliseconds during which key presses are regarded as key repeat and during
- * which the debounce logic is active
+/*
+ * Milliseconds during which a key is regarded as pressed.
+ * If an identical command arrives within this time, the timer will start over.
  */
-#define IR_REPEAT_TIMEOUT	350
+#define IR_KEYPRESS_TIMEOUT	250
 
 /* RC5 device wildcard */
 #define IR_DEVICE_ANY		255
 
-/* Some remotes sends multiple sequences per keypress (e.g. Zenith sends two),
- * this setting allows the superflous sequences to be ignored
- */
-static int debounce = 0;
-module_param(debounce, int, 0644);
-MODULE_PARM_DESC(debounce, "ignore repeated IR sequences (default: 0 = ignore no sequences)");
-
 static int rc5_device = -1;
 module_param(rc5_device, int, 0644);
 MODULE_PARM_DESC(rc5_device, "only IR commands to given RC5 device (device = 0 - 31, any device = 255, default: autodetect)");
@@ -99,10 +93,14 @@
 struct budget_ci_ir {
 	struct input_dev *dev;
 	struct tasklet_struct msp430_irq_tasklet;
+	struct timer_list timer_keyup;
 	char name[72]; /* 40 + 32 for (struct saa7146_dev).name */
 	char phys[32];
 	struct ir_input_state state;
 	int rc5_device;
+	u32 last_raw;
+	u32 ir_key;
+	bool have_command;
 };
 
 struct budget_ci {
@@ -125,13 +123,8 @@
 {
 	struct budget_ci *budget_ci = (struct budget_ci *) data;
 	struct input_dev *dev = budget_ci->ir.dev;
-	static int bounces = 0;
-	int device;
-	int toggle;
-	static int prev_toggle = -1;
-	static u32 ir_key;
-	static int state = 0;
 	u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8;
+	u32 raw;
 
 	/*
 	 * The msp430 chip can generate two different bytes, command and device
@@ -143,7 +136,7 @@
 	 * bytes and one or more device bytes. For the repeated bytes, the
 	 * highest bit (X) is set. The first command byte is always generated
 	 * before the first device byte. Other than that, no specific order
-	 * seems to apply.
+	 * seems to apply. To make life interesting, bytes can also be lost.
 	 *
 	 * Only when we have a command and device byte, a keypress is
 	 * generated.
@@ -152,53 +145,35 @@
 	if (ir_debug)
 		printk("budget_ci: received byte 0x%02x\n", command);
 
-	/* Is this a repeated byte? */
-	if (command & 0x80)
-		return;
+	/* Remove repeat bit, we use every command */
+	command = command & 0x7f;
 
 	/* Is this a RC5 command byte? */
 	if (command & 0x40) {
-		state = 1;
-		ir_key = command & 0x3f;
+		budget_ci->ir.have_command = true;
+		budget_ci->ir.ir_key = command & 0x3f;
 		return;
 	}
 
 	/* It's a RC5 device byte */
-	if (!state)
+	if (!budget_ci->ir.have_command)
 		return;
-	state = 0;
-	device = command & 0x1f;
-	toggle = command & 0x20;
+	budget_ci->ir.have_command = false;
 
-	if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && budget_ci->ir.rc5_device != device)
+	if (budget_ci->ir.rc5_device != IR_DEVICE_ANY &&
+	    budget_ci->ir.rc5_device != (command & 0x1f))
 		return;
 
-	/* Ignore repeated key sequences if requested */
-	if (toggle == prev_toggle && ir_key == dev->repeat_key &&
-	    bounces > 0 && timer_pending(&dev->timer)) {
-		if (ir_debug)
-			printk("budget_ci: debounce logic ignored IR command\n");
-		bounces--;
-		return;
-	}
-	prev_toggle = toggle;
-
-	/* Are we still waiting for a keyup event? */
-	if (del_timer(&dev->timer))
+	/* Is this a repeated key sequence? (same device, command, toggle) */
+	raw = budget_ci->ir.ir_key | (command << 8);
+	if (budget_ci->ir.last_raw != raw || !timer_pending(&budget_ci->ir.timer_keyup)) {
 		ir_input_nokey(dev, &budget_ci->ir.state);
-
-	/* Generate keypress */
-	if (ir_debug)
-		printk("budget_ci: generating keypress 0x%02x\n", ir_key);
-	ir_input_keydown(dev, &budget_ci->ir.state, ir_key, (ir_key & (command << 8)));
-
-	/* Do we want to delay the keyup event? */
-	if (debounce) {
-		bounces = debounce;
-		mod_timer(&dev->timer, jiffies + msecs_to_jiffies(IR_REPEAT_TIMEOUT));
-	} else {
-		ir_input_nokey(dev, &budget_ci->ir.state);
+		ir_input_keydown(dev, &budget_ci->ir.state,
+				 budget_ci->ir.ir_key, raw);
+		budget_ci->ir.last_raw = raw;
 	}
+
+	mod_timer(&budget_ci->ir.timer_keyup, jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT));
 }
 
 static int msp430_ir_init(struct budget_ci *budget_ci)
@@ -271,16 +246,21 @@
 		break;
 	}
 
-	/* initialise the key-up debounce timeout handler */
-	input_dev->timer.function = msp430_ir_keyup;
-	input_dev->timer.data = (unsigned long) &budget_ci->ir;
-
+	/* initialise the key-up timeout handler */
+	init_timer(&budget_ci->ir.timer_keyup);
+	budget_ci->ir.timer_keyup.function = msp430_ir_keyup;
+	budget_ci->ir.timer_keyup.data = (unsigned long) &budget_ci->ir;
+	budget_ci->ir.last_raw = 0xffff; /* An impossible value */
 	error = input_register_device(input_dev);
 	if (error) {
 		printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error);
 		goto out2;
 	}
 
+	/* note: these must be after input_register_device */
+	input_dev->rep[REP_DELAY] = 400;
+	input_dev->rep[REP_PERIOD] = 250;
+
 	tasklet_init(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt,
 		     (unsigned long) budget_ci);
 
@@ -304,10 +284,8 @@
 	saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT);
 	tasklet_kill(&budget_ci->ir.msp430_irq_tasklet);
 
-	if (del_timer(&dev->timer)) {
-		ir_input_nokey(dev, &budget_ci->ir.state);
-		input_sync(dev);
-	}
+	del_timer_sync(&dev->timer);
+	ir_input_nokey(dev, &budget_ci->ir.state);
 
 	input_unregister_device(dev);
 }
diff --git a/drivers/media/dvb/ttpci/budget-core.c b/drivers/media/dvb/ttpci/budget-core.c
index e15562f..6b97dc1 100644
--- a/drivers/media/dvb/ttpci/budget-core.c
+++ b/drivers/media/dvb/ttpci/budget-core.c
@@ -41,11 +41,14 @@
 
 #define TS_WIDTH		(2 * TS_SIZE)
 #define TS_WIDTH_ACTIVY		TS_SIZE
+#define TS_WIDTH_DVBC		TS_SIZE
 #define TS_HEIGHT_MASK		0xf00
 #define TS_HEIGHT_MASK_ACTIVY	0xc00
+#define TS_HEIGHT_MASK_DVBC	0xe00
 #define TS_MIN_BUFSIZE_K	188
 #define TS_MAX_BUFSIZE_K	1410
 #define TS_MAX_BUFSIZE_K_ACTIVY	564
+#define TS_MAX_BUFSIZE_K_DVBC	1316
 #define BUFFER_WARNING_WAIT	(30*HZ)
 
 int budget_debug;
@@ -106,6 +109,19 @@
 		saa7146_write(dev, MC2, (MASK_10 | MASK_26));
 		saa7146_write(dev, BRS_CTRL, 0x60000000);
 		break;
+	case BUDGET_CIN1200C_MK3:
+	case BUDGET_KNC1C_MK3:
+	case BUDGET_KNC1CP_MK3:
+		if (budget->video_port == BUDGET_VIDEO_PORTA) {
+			saa7146_write(dev, DD1_INIT, 0x06000200);
+			saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+			saa7146_write(dev, BRS_CTRL, 0x00000000);
+		} else {
+			saa7146_write(dev, DD1_INIT, 0x00000600);
+			saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+			saa7146_write(dev, BRS_CTRL, 0x60000000);
+		}
+		break;
 	default:
 		if (budget->video_port == BUDGET_VIDEO_PORTA) {
 			saa7146_write(dev, DD1_INIT, 0x06000200);
@@ -122,7 +138,13 @@
 	mdelay(10);
 
 	saa7146_write(dev, BASE_ODD3, 0);
-	saa7146_write(dev, BASE_EVEN3, 0);
+	if (budget->buffer_size > budget->buffer_height * budget->buffer_width) {
+		// using odd/even buffers
+		saa7146_write(dev, BASE_EVEN3, budget->buffer_height * budget->buffer_width);
+	} else {
+		// using a single buffer
+		saa7146_write(dev, BASE_EVEN3, 0);
+	}
 	saa7146_write(dev, PROT_ADDR3, budget->buffer_size);
 	saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x90);
 
@@ -399,11 +421,25 @@
 	budget->card = bi;
 	budget->dev = (struct saa7146_dev *) dev;
 
-	if (budget->card->type == BUDGET_FS_ACTIVY) {
+	switch(budget->card->type) {
+	case BUDGET_FS_ACTIVY:
 		budget->buffer_width = TS_WIDTH_ACTIVY;
 		max_bufsize = TS_MAX_BUFSIZE_K_ACTIVY;
 		height_mask = TS_HEIGHT_MASK_ACTIVY;
-	} else {
+		break;
+
+	case BUDGET_KNC1C:
+	case BUDGET_KNC1CP:
+	case BUDGET_CIN1200C:
+	case BUDGET_KNC1C_MK3:
+	case BUDGET_KNC1CP_MK3:
+	case BUDGET_CIN1200C_MK3:
+		budget->buffer_width = TS_WIDTH_DVBC;
+		max_bufsize = TS_MAX_BUFSIZE_K_DVBC;
+		height_mask = TS_HEIGHT_MASK_DVBC;
+		break;
+
+	default:
 		budget->buffer_width = TS_WIDTH;
 		max_bufsize = TS_MAX_BUFSIZE_K;
 		height_mask = TS_HEIGHT_MASK;
@@ -415,14 +451,22 @@
 		dma_buffer_size = max_bufsize;
 
 	budget->buffer_height = dma_buffer_size * 1024 / budget->buffer_width;
-	budget->buffer_height &= height_mask;
-	budget->buffer_size = budget->buffer_height * budget->buffer_width;
+	if (budget->buffer_height > 0xfff) {
+		budget->buffer_height /= 2;
+		budget->buffer_height &= height_mask;
+		budget->buffer_size = 2 * budget->buffer_height * budget->buffer_width;
+	} else {
+		budget->buffer_height &= height_mask;
+		budget->buffer_size = budget->buffer_height * budget->buffer_width;
+	}
 	budget->buffer_warning_threshold = budget->buffer_size * 80/100;
 	budget->buffer_warnings = 0;
 	budget->buffer_warning_time = jiffies;
 
-	dprintk(2, "%s: width = %d, height = %d\n",
-		budget->dev->name, budget->buffer_width, budget->buffer_height);
+	dprintk(2, "%s: buffer type = %s, width = %d, height = %d\n",
+		budget->dev->name,
+		budget->buffer_size > budget->buffer_width * budget->buffer_height ? "odd/even" : "single",
+		budget->buffer_width, budget->buffer_height);
 	printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size);
 
 	if ((ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name, owner, &budget->dev->pci->dev)) < 0) {
diff --git a/drivers/media/dvb/ttpci/budget.h b/drivers/media/dvb/ttpci/budget.h
index e8a5c79..d764ffa 100644
--- a/drivers/media/dvb/ttpci/budget.h
+++ b/drivers/media/dvb/ttpci/budget.h
@@ -99,6 +99,9 @@
 #define BUDGET_KNC1CP		   12
 #define BUDGET_KNC1TP		   13
 #define BUDGET_TVSTAR		   14
+#define BUDGET_CIN1200C_MK3	   15
+#define BUDGET_KNC1C_MK3	   16
+#define BUDGET_KNC1CP_MK3	   17
 
 #define BUDGET_VIDEO_PORTA         0
 #define BUDGET_VIDEO_PORTB         1
diff --git a/drivers/media/dvb/ttusb-budget/Kconfig b/drivers/media/dvb/ttusb-budget/Kconfig
index e78ea92..f546bcc 100644
--- a/drivers/media/dvb/ttusb-budget/Kconfig
+++ b/drivers/media/dvb/ttusb-budget/Kconfig
@@ -1,7 +1,6 @@
 config DVB_TTUSB_BUDGET
 	tristate "Technotrend/Hauppauge Nova-USB devices"
 	depends on DVB_CORE && USB && I2C
-	select DVB_PLL
 	select DVB_CX22700 if !DVB_FE_CUSTOMISE
 	select DVB_TDA1004X if !DVB_FE_CUSTOMISE
 	select DVB_VES1820 if !DVB_FE_CUSTOMISE
diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c
index b2e88ad..5adc27c 100644
--- a/drivers/media/radio/radio-aimslab.c
+++ b/drivers/media/radio/radio-aimslab.c
@@ -231,129 +231,149 @@
 	}
 };
 
-static int rt_do_ioctl(struct inode *inode, struct file *file,
-		       unsigned int cmd, void *arg)
+static int vidioc_querycap(struct file *file, void  *priv,
+					struct v4l2_capability *v)
 {
-	struct video_device *dev = video_devdata(file);
-	struct rt_device *rt=dev->priv;
-
-	switch(cmd)
-	{
-		case VIDIOC_QUERYCAP:
-		{
-			struct v4l2_capability *v = arg;
-			memset(v,0,sizeof(*v));
-			strlcpy(v->driver, "radio-aimslab", sizeof (v->driver));
-			strlcpy(v->card, "RadioTrack", sizeof (v->card));
-			sprintf(v->bus_info,"ISA");
-			v->version = RADIO_VERSION;
-			v->capabilities = V4L2_CAP_TUNER;
-
-			return 0;
-		}
-		case VIDIOC_G_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			memset(v,0,sizeof(*v));
-			strcpy(v->name, "FM");
-			v->type = V4L2_TUNER_RADIO;
-
-			v->rangelow=(87*16000);
-			v->rangehigh=(108*16000);
-			v->rxsubchans =V4L2_TUNER_SUB_MONO;
-			v->capability=V4L2_TUNER_CAP_LOW;
-			v->audmode = V4L2_TUNER_MODE_MONO;
-			v->signal=0xFFFF*rt_getsigstr(rt);
-
-			return 0;
-		}
-		case VIDIOC_S_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			return 0;
-		}
-		case VIDIOC_S_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			rt->curfreq = f->frequency;
-			rt_setfreq(rt, rt->curfreq);
-			return 0;
-		}
-		case VIDIOC_G_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			f->type = V4L2_TUNER_RADIO;
-			f->frequency = rt->curfreq;
-
-			return 0;
-		}
-		case VIDIOC_QUERYCTRL:
-		{
-			struct v4l2_queryctrl *qc = arg;
-			int i;
-
-			for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-				if (qc->id && qc->id == radio_qctrl[i].id) {
-					memcpy(qc, &(radio_qctrl[i]),
-								sizeof(*qc));
-					return (0);
-				}
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_G_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-					ctrl->value=rt->muted;
-					return (0);
-				case V4L2_CID_AUDIO_VOLUME:
-					ctrl->value=rt->curvol * 6554;
-					return (0);
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_S_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-					if (ctrl->value) {
-						rt_mute(rt);
-					} else {
-						rt_setvol(rt,rt->curvol);
-					}
-					return (0);
-				case V4L2_CID_AUDIO_VOLUME:
-					rt_setvol(rt,ctrl->value);
-					return (0);
-			}
-			return -EINVAL;
-		}
-
-		default:
-			return v4l_compat_translate_ioctl(inode,file,cmd,arg,
-							  rt_do_ioctl);
-	}
+	strlcpy(v->driver, "radio-aimslab", sizeof(v->driver));
+	strlcpy(v->card, "RadioTrack", sizeof(v->card));
+	sprintf(v->bus_info, "ISA");
+	v->version = RADIO_VERSION;
+	v->capabilities = V4L2_CAP_TUNER;
+	return 0;
 }
 
-static int rt_ioctl(struct inode *inode, struct file *file,
-		    unsigned int cmd, unsigned long arg)
+static int vidioc_g_tuner(struct file *file, void *priv,
+					struct v4l2_tuner *v)
 {
-	return video_usercopy(inode, file, cmd, arg, rt_do_ioctl);
+	struct video_device *dev = video_devdata(file);
+	struct rt_device *rt = dev->priv;
+
+	if (v->index > 0)
+		return -EINVAL;
+
+	strcpy(v->name, "FM");
+	v->type = V4L2_TUNER_RADIO;
+	v->rangelow = (87*16000);
+	v->rangehigh = (108*16000);
+	v->rxsubchans = V4L2_TUNER_SUB_MONO;
+	v->capability = V4L2_TUNER_CAP_LOW;
+	v->audmode = V4L2_TUNER_MODE_MONO;
+	v->signal = 0xffff*rt_getsigstr(rt);
+	return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+					struct v4l2_tuner *v)
+{
+	if (v->index > 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *f)
+{
+	struct video_device *dev = video_devdata(file);
+	struct rt_device *rt = dev->priv;
+
+	rt->curfreq = f->frequency;
+	rt_setfreq(rt, rt->curfreq);
+	return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *f)
+{
+	struct video_device *dev = video_devdata(file);
+	struct rt_device *rt = dev->priv;
+
+	f->type = V4L2_TUNER_RADIO;
+	f->frequency = rt->curfreq;
+	return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+					struct v4l2_queryctrl *qc)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+		if (qc->id && qc->id == radio_qctrl[i].id) {
+			memcpy(qc, &(radio_qctrl[i]),
+						sizeof(*qc));
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+					struct v4l2_control *ctrl)
+{
+	struct video_device *dev = video_devdata(file);
+	struct rt_device *rt = dev->priv;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		ctrl->value = rt->muted;
+		return 0;
+	case V4L2_CID_AUDIO_VOLUME:
+		ctrl->value = rt->curvol * 6554;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+					struct v4l2_control *ctrl)
+{
+	struct video_device *dev = video_devdata(file);
+	struct rt_device *rt = dev->priv;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		if (ctrl->value)
+			rt_mute(rt);
+		else
+			rt_setvol(rt,rt->curvol);
+		return 0;
+	case V4L2_CID_AUDIO_VOLUME:
+		rt_setvol(rt,ctrl->value);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_g_audio (struct file *file, void *priv,
+					struct v4l2_audio *a)
+{
+	if (a->index > 1)
+		return -EINVAL;
+
+	strcpy(a->name, "Radio");
+	a->capability = V4L2_AUDCAP_STEREO;
+	return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+	if (i != 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+					struct v4l2_audio *a)
+{
+	if (a->index != 0)
+		return -EINVAL;
+	return 0;
 }
 
 static struct rt_device rtrack_unit;
@@ -362,7 +382,7 @@
 	.owner		= THIS_MODULE,
 	.open           = video_exclusive_open,
 	.release        = video_exclusive_release,
-	.ioctl		= rt_ioctl,
+	.ioctl		= video_ioctl2,
 	.compat_ioctl	= v4l_compat_ioctl32,
 	.llseek         = no_llseek,
 };
@@ -374,6 +394,18 @@
 	.type		= VID_TYPE_TUNER,
 	.hardware	= 0,
 	.fops           = &rtrack_fops,
+	.vidioc_querycap    = vidioc_querycap,
+	.vidioc_g_tuner     = vidioc_g_tuner,
+	.vidioc_s_tuner     = vidioc_s_tuner,
+	.vidioc_g_audio     = vidioc_g_audio,
+	.vidioc_s_audio     = vidioc_s_audio,
+	.vidioc_g_input     = vidioc_g_input,
+	.vidioc_s_input     = vidioc_s_input,
+	.vidioc_g_frequency = vidioc_g_frequency,
+	.vidioc_s_frequency = vidioc_s_frequency,
+	.vidioc_queryctrl   = vidioc_queryctrl,
+	.vidioc_g_ctrl      = vidioc_g_ctrl,
+	.vidioc_s_ctrl      = vidioc_s_ctrl,
 };
 
 static int __init rtrack_init(void)
diff --git a/drivers/media/radio/radio-gemtek-pci.c b/drivers/media/radio/radio-gemtek-pci.c
index 74976cba..fdf5d6e 100644
--- a/drivers/media/radio/radio-gemtek-pci.c
+++ b/drivers/media/radio/radio-gemtek-pci.c
@@ -192,131 +192,158 @@
 	return ( inb( card->iobase ) & 0x08 ) ? 0 : 1;
 }
 
-static int gemtek_pci_do_ioctl(struct inode *inode, struct file *file,
-			       unsigned int cmd, void *arg)
+static int vidioc_querycap(struct file *file, void *priv,
+					struct v4l2_capability *v)
+{
+	strlcpy(v->driver, "radio-gemtek-pci", sizeof(v->driver));
+	strlcpy(v->card, "GemTek PCI Radio", sizeof(v->card));
+	sprintf(v->bus_info, "ISA");
+	v->version = RADIO_VERSION;
+	v->capabilities = V4L2_CAP_TUNER;
+	return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+					struct v4l2_tuner *v)
 {
 	struct video_device *dev = video_devdata(file);
 	struct gemtek_pci_card *card = dev->priv;
 
-	switch ( cmd ) {
-		case VIDIOC_QUERYCAP:
-		{
-			struct v4l2_capability *v = arg;
-			memset(v,0,sizeof(*v));
-			strlcpy(v->driver, "radio-gemtek-pci", sizeof (v->driver));
-			strlcpy(v->card, "GemTek PCI Radio", sizeof (v->card));
-			sprintf(v->bus_info,"ISA");
-			v->version = RADIO_VERSION;
-			v->capabilities = V4L2_CAP_TUNER;
+	if (v->index > 0)
+		return -EINVAL;
 
-			return 0;
-		}
-		case VIDIOC_G_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			memset(v,0,sizeof(*v));
-			strcpy(v->name, "FM");
-			v->type = V4L2_TUNER_RADIO;
-
-			v->rangelow = GEMTEK_PCI_RANGE_LOW;
-			v->rangehigh = GEMTEK_PCI_RANGE_HIGH;
-			v->rxsubchans =V4L2_TUNER_SUB_MONO;
-			v->capability=V4L2_TUNER_CAP_LOW;
-			v->audmode = V4L2_TUNER_MODE_MONO;
-			v->signal=0xFFFF*gemtek_pci_getsignal( card );
-
-			return 0;
-		}
-		case VIDIOC_S_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			return 0;
-		}
-		case VIDIOC_S_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			if ( (f->frequency < GEMTEK_PCI_RANGE_LOW) ||
-			     (f->frequency > GEMTEK_PCI_RANGE_HIGH) )
-				return -EINVAL;
-
-
-			gemtek_pci_setfrequency( card, f->frequency );
-			card->current_frequency = f->frequency;
-			card->mute = false;
-			return 0;
-		}
-		case VIDIOC_QUERYCTRL:
-		{
-			struct v4l2_queryctrl *qc = arg;
-			int i;
-
-			for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-				if (qc->id && qc->id == radio_qctrl[i].id) {
-					memcpy(qc, &(radio_qctrl[i]),
-								sizeof(*qc));
-					return (0);
-				}
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_G_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-					ctrl->value=card->mute;
-					return (0);
-				case V4L2_CID_AUDIO_VOLUME:
-					if (card->mute)
-						ctrl->value=0;
-					else
-						ctrl->value=65535;
-					return (0);
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_S_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-					if (ctrl->value) {
-						gemtek_pci_mute(card);
-					} else {
-						gemtek_pci_unmute(card);
-					}
-					return (0);
-				case V4L2_CID_AUDIO_VOLUME:
-					if (ctrl->value) {
-						gemtek_pci_unmute(card);
-					} else {
-						gemtek_pci_mute(card);
-					}
-					return (0);
-			}
-			return -EINVAL;
-		}
-		default:
-			return v4l_compat_translate_ioctl(inode,file,cmd,arg,
-							  gemtek_pci_do_ioctl);
-	}
+	strcpy(v->name, "FM");
+	v->type = V4L2_TUNER_RADIO;
+	v->rangelow = GEMTEK_PCI_RANGE_LOW;
+	v->rangehigh = GEMTEK_PCI_RANGE_HIGH;
+	v->rxsubchans = V4L2_TUNER_SUB_MONO;
+	v->capability = V4L2_TUNER_CAP_LOW;
+	v->audmode = V4L2_TUNER_MODE_MONO;
+	v->signal = 0xffff * gemtek_pci_getsignal(card);
+	return 0;
 }
 
-static int gemtek_pci_ioctl(struct inode *inode, struct file *file,
-			    unsigned int cmd, unsigned long arg)
+static int vidioc_s_tuner(struct file *file, void *priv,
+					struct v4l2_tuner *v)
 {
-	return video_usercopy(inode, file, cmd, arg, gemtek_pci_do_ioctl);
+	if (v->index > 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *f)
+{
+	struct video_device *dev = video_devdata(file);
+	struct gemtek_pci_card *card = dev->priv;
+
+	if ( (f->frequency < GEMTEK_PCI_RANGE_LOW) ||
+			(f->frequency > GEMTEK_PCI_RANGE_HIGH) )
+		return -EINVAL;
+	gemtek_pci_setfrequency(card, f->frequency);
+	card->current_frequency = f->frequency;
+	card->mute = false;
+	return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *f)
+{
+	struct video_device *dev = video_devdata(file);
+	struct gemtek_pci_card *card = dev->priv;
+
+	f->type = V4L2_TUNER_RADIO;
+	f->frequency = card->current_frequency;
+	return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+					struct v4l2_queryctrl *qc)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+		if (qc->id && qc->id == radio_qctrl[i].id) {
+			memcpy(qc, &(radio_qctrl[i]),
+						sizeof(*qc));
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+					struct v4l2_control *ctrl)
+{
+	struct video_device *dev = video_devdata(file);
+	struct gemtek_pci_card *card = dev->priv;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		ctrl->value = card->mute;
+		return 0;
+	case V4L2_CID_AUDIO_VOLUME:
+		if (card->mute)
+			ctrl->value = 0;
+		else
+			ctrl->value = 65535;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+					struct v4l2_control *ctrl)
+{
+	struct video_device *dev = video_devdata(file);
+	struct gemtek_pci_card *card = dev->priv;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		if (ctrl->value)
+			gemtek_pci_mute(card);
+		else
+			gemtek_pci_unmute(card);
+		return 0;
+	case V4L2_CID_AUDIO_VOLUME:
+		if (ctrl->value)
+			gemtek_pci_unmute(card);
+		else
+			gemtek_pci_mute(card);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv,
+					struct v4l2_audio *a)
+{
+	if (a->index > 1)
+		return -EINVAL;
+
+	strcpy(a->name, "Radio");
+	a->capability = V4L2_AUDCAP_STEREO;
+	return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+	if (i != 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+					struct v4l2_audio *a)
+{
+	if (a->index != 0)
+		return -EINVAL;
+	return 0;
 }
 
 enum {
@@ -342,7 +369,7 @@
 	.owner		= THIS_MODULE,
 	.open           = video_exclusive_open,
 	.release        = video_exclusive_release,
-	.ioctl		= gemtek_pci_ioctl,
+	.ioctl		= video_ioctl2,
 	.compat_ioctl	= v4l_compat_ioctl32,
 	.llseek         = no_llseek,
 };
@@ -353,6 +380,18 @@
 	.type          = VID_TYPE_TUNER,
 	.hardware      = 0,
 	.fops          = &gemtek_pci_fops,
+	.vidioc_querycap    = vidioc_querycap,
+	.vidioc_g_tuner     = vidioc_g_tuner,
+	.vidioc_s_tuner     = vidioc_s_tuner,
+	.vidioc_g_audio     = vidioc_g_audio,
+	.vidioc_s_audio     = vidioc_s_audio,
+	.vidioc_g_input     = vidioc_g_input,
+	.vidioc_s_input     = vidioc_s_input,
+	.vidioc_g_frequency = vidioc_g_frequency,
+	.vidioc_s_frequency = vidioc_s_frequency,
+	.vidioc_queryctrl   = vidioc_queryctrl,
+	.vidioc_g_ctrl      = vidioc_g_ctrl,
+	.vidioc_s_ctrl      = vidioc_s_ctrl,
 };
 
 static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci_device_id *pci_id )
diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c
index 36c4be6..b04b6a7 100644
--- a/drivers/media/radio/radio-gemtek.c
+++ b/drivers/media/radio/radio-gemtek.c
@@ -161,137 +161,157 @@
 	return 1;		/* signal present */
 }
 
-static int gemtek_do_ioctl(struct inode *inode, struct file *file,
-			   unsigned int cmd, void *arg)
+static int vidioc_querycap(struct file *file, void  *priv,
+					struct v4l2_capability *v)
 {
-	struct video_device *dev = video_devdata(file);
-	struct gemtek_device *rt=dev->priv;
-
-	switch(cmd)
-	{
-		case VIDIOC_QUERYCAP:
-		{
-			struct v4l2_capability *v = arg;
-			memset(v,0,sizeof(*v));
-			strlcpy(v->driver, "radio-gemtek", sizeof (v->driver));
-			strlcpy(v->card, "GemTek", sizeof (v->card));
-			sprintf(v->bus_info,"ISA");
-			v->version = RADIO_VERSION;
-			v->capabilities = V4L2_CAP_TUNER;
-
-			return 0;
-		}
-		case VIDIOC_G_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			memset(v,0,sizeof(*v));
-			strcpy(v->name, "FM");
-			v->type = V4L2_TUNER_RADIO;
-
-			v->rangelow=(87*16000);
-			v->rangehigh=(108*16000);
-			v->rxsubchans =V4L2_TUNER_SUB_MONO;
-			v->capability=V4L2_TUNER_CAP_LOW;
-			v->audmode = V4L2_TUNER_MODE_MONO;
-			v->signal=0xFFFF*gemtek_getsigstr(rt);
-
-			return 0;
-		}
-		case VIDIOC_S_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			return 0;
-		}
-		case VIDIOC_S_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			rt->curfreq = f->frequency;
-			/* needs to be called twice in order for getsigstr to work */
-			gemtek_setfreq(rt, rt->curfreq);
-			gemtek_setfreq(rt, rt->curfreq);
-			return 0;
-		}
-		case VIDIOC_G_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			f->type = V4L2_TUNER_RADIO;
-			f->frequency = rt->curfreq;
-
-			return 0;
-		}
-		case VIDIOC_QUERYCTRL:
-		{
-			struct v4l2_queryctrl *qc = arg;
-			int i;
-
-			for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-				if (qc->id && qc->id == radio_qctrl[i].id) {
-					memcpy(qc, &(radio_qctrl[i]),
-								sizeof(*qc));
-					return (0);
-				}
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_G_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-					ctrl->value=rt->muted;
-					return (0);
-				case V4L2_CID_AUDIO_VOLUME:
-					if (rt->muted)
-						ctrl->value=0;
-					else
-						ctrl->value=65535;
-					return (0);
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_S_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-					if (ctrl->value) {
-						gemtek_mute(rt);
-					} else {
-						gemtek_unmute(rt);
-					}
-					return (0);
-				case V4L2_CID_AUDIO_VOLUME:
-					if (ctrl->value) {
-						gemtek_unmute(rt);
-					} else {
-						gemtek_mute(rt);
-					}
-					return (0);
-			}
-			return -EINVAL;
-		}
-		default:
-			return v4l_compat_translate_ioctl(inode,file,cmd,arg,
-							  gemtek_do_ioctl);
-	}
+	strlcpy(v->driver, "radio-gemtek", sizeof(v->driver));
+	strlcpy(v->card, "GemTek", sizeof(v->card));
+	sprintf(v->bus_info, "ISA");
+	v->version = RADIO_VERSION;
+	v->capabilities = V4L2_CAP_TUNER;
+	return 0;
 }
 
-static int gemtek_ioctl(struct inode *inode, struct file *file,
-			unsigned int cmd, unsigned long arg)
+static int vidioc_g_tuner(struct file *file, void *priv,
+					struct v4l2_tuner *v)
 {
-	return video_usercopy(inode, file, cmd, arg, gemtek_do_ioctl);
+	struct video_device *dev = video_devdata(file);
+	struct gemtek_device *rt = dev->priv;
+
+	if (v->index > 0)
+		return -EINVAL;
+
+	strcpy(v->name, "FM");
+	v->type = V4L2_TUNER_RADIO;
+	v->rangelow = (87*16000);
+	v->rangehigh = (108*16000);
+	v->rxsubchans = V4L2_TUNER_SUB_MONO;
+	v->capability = V4L2_TUNER_CAP_LOW;
+	v->audmode = V4L2_TUNER_MODE_MONO;
+	v->signal = 0xffff*gemtek_getsigstr(rt);
+	return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+					struct v4l2_tuner *v)
+{
+	if (v->index > 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *f)
+{
+	struct video_device *dev = video_devdata(file);
+	struct gemtek_device *rt = dev->priv;
+
+	rt->curfreq = f->frequency;
+	/* needs to be called twice in order for getsigstr to work */
+	gemtek_setfreq(rt, rt->curfreq);
+	gemtek_setfreq(rt, rt->curfreq);
+	return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *f)
+{
+	struct video_device *dev = video_devdata(file);
+	struct gemtek_device *rt = dev->priv;
+
+	f->type = V4L2_TUNER_RADIO;
+	f->frequency = rt->curfreq;
+	return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+					struct v4l2_queryctrl *qc)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+		if (qc->id && qc->id == radio_qctrl[i].id) {
+			memcpy(qc, &(radio_qctrl[i]),
+						sizeof(*qc));
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+					struct v4l2_control *ctrl)
+{
+	struct video_device *dev = video_devdata(file);
+	struct gemtek_device *rt = dev->priv;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		ctrl->value = rt->muted;
+		return 0;
+	case V4L2_CID_AUDIO_VOLUME:
+		if (rt->muted)
+			ctrl->value = 0;
+		else
+			ctrl->value = 65535;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+					struct v4l2_control *ctrl)
+{
+	struct video_device *dev = video_devdata(file);
+	struct gemtek_device *rt = dev->priv;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		if (ctrl->value)
+			gemtek_mute(rt);
+		else
+			gemtek_unmute(rt);
+		return 0;
+	case V4L2_CID_AUDIO_VOLUME:
+		if (ctrl->value)
+			gemtek_unmute(rt);
+		else
+			gemtek_mute(rt);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_g_audio (struct file *file, void *priv,
+					struct v4l2_audio *a)
+{
+	if (a->index > 1)
+		return -EINVAL;
+
+	strcpy(a->name, "Radio");
+	a->capability = V4L2_AUDCAP_STEREO;
+	return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+	if (i != 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+					struct v4l2_audio *a)
+{
+	if (a->index != 0)
+		return -EINVAL;
+	return 0;
 }
 
 static struct gemtek_device gemtek_unit;
@@ -300,7 +320,7 @@
 	.owner		= THIS_MODULE,
 	.open           = video_exclusive_open,
 	.release        = video_exclusive_release,
-	.ioctl		= gemtek_ioctl,
+	.ioctl		= video_ioctl2,
 	.compat_ioctl	= v4l_compat_ioctl32,
 	.llseek         = no_llseek,
 };
@@ -312,6 +332,18 @@
 	.type		= VID_TYPE_TUNER,
 	.hardware	= 0,
 	.fops           = &gemtek_fops,
+	.vidioc_querycap    = vidioc_querycap,
+	.vidioc_g_tuner     = vidioc_g_tuner,
+	.vidioc_s_tuner     = vidioc_s_tuner,
+	.vidioc_g_audio     = vidioc_g_audio,
+	.vidioc_s_audio     = vidioc_s_audio,
+	.vidioc_g_input     = vidioc_g_input,
+	.vidioc_s_input     = vidioc_s_input,
+	.vidioc_g_frequency = vidioc_g_frequency,
+	.vidioc_s_frequency = vidioc_s_frequency,
+	.vidioc_queryctrl   = vidioc_queryctrl,
+	.vidioc_g_ctrl      = vidioc_g_ctrl,
+	.vidioc_s_ctrl      = vidioc_s_ctrl,
 };
 
 static int __init gemtek_init(void)
diff --git a/drivers/media/radio/radio-maestro.c b/drivers/media/radio/radio-maestro.c
index e67b7f2..11f80ca 100644
--- a/drivers/media/radio/radio-maestro.c
+++ b/drivers/media/radio/radio-maestro.c
@@ -75,8 +75,6 @@
 static int radio_nr = -1;
 module_param(radio_nr, int, 0);
 
-static int radio_ioctl(struct inode *inode, struct file *file,
-	unsigned int cmd, unsigned long arg);
 static int maestro_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
 static void maestro_remove(struct pci_dev *pdev);
 
@@ -102,18 +100,11 @@
 	.owner		= THIS_MODULE,
 	.open           = video_exclusive_open,
 	.release        = video_exclusive_release,
-	.ioctl		= radio_ioctl,
+	.ioctl		= video_ioctl2,
 	.compat_ioctl	= v4l_compat_ioctl32,
 	.llseek         = no_llseek,
 };
 
-static struct video_device maestro_radio = {
-	.name		= "Maestro radio",
-	.type		= VID_TYPE_TUNER,
-	.hardware	= 0,
-	.fops		= &maestro_fops,
-};
-
 struct radio_device {
 	u16	io,	/* base of Maestro card radio io (GPIO_DATA)*/
 		muted,	/* VIDEO_AUDIO_MUTE */
@@ -190,142 +181,153 @@
 	msleep(125);
 }
 
-static inline int radio_function(struct inode *inode, struct file *file,
-	unsigned int cmd, void *arg)
+static int vidioc_querycap(struct file *file, void  *priv,
+					struct v4l2_capability *v)
 {
-	struct video_device *dev = video_devdata(file);
-	struct radio_device *card = video_get_drvdata(dev);
-
-	switch (cmd) {
-		case VIDIOC_QUERYCAP:
-		{
-			struct v4l2_capability *v = arg;
-			memset(v,0,sizeof(*v));
-			strlcpy(v->driver, "radio-maestro", sizeof (v->driver));
-			strlcpy(v->card, "Maestro Radio", sizeof (v->card));
-			sprintf(v->bus_info,"PCI");
-			v->version = RADIO_VERSION;
-			v->capabilities = V4L2_CAP_TUNER;
-
-			return 0;
-		}
-		case VIDIOC_G_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			(void)radio_bits_get(card);
-
-			memset(v,0,sizeof(*v));
-			strcpy(v->name, "FM");
-			v->type = V4L2_TUNER_RADIO;
-
-			v->rangelow = FREQ_LO;
-			v->rangehigh = FREQ_HI;
-			v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
-			v->capability=V4L2_TUNER_CAP_LOW;
-			if(card->stereo)
-				v->audmode = V4L2_TUNER_MODE_STEREO;
-			else
-				v->audmode = V4L2_TUNER_MODE_MONO;
-			v->signal=card->tuned;
-
-			return 0;
-		}
-		case VIDIOC_S_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			return 0;
-		}
-		case VIDIOC_S_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
-				return -EINVAL;
-			radio_bits_set(card, FREQ2BITS(f->frequency));
-
-			return 0;
-		}
-		case VIDIOC_G_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			f->type = V4L2_TUNER_RADIO;
-			f->frequency = BITS2FREQ(radio_bits_get(card));
-
-			return 0;
-		}
-		case VIDIOC_QUERYCTRL:
-		{
-			struct v4l2_queryctrl *qc = arg;
-			int i;
-
-			for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-				if (qc->id && qc->id == radio_qctrl[i].id) {
-					memcpy(qc, &(radio_qctrl[i]),
-								sizeof(*qc));
-					return (0);
-				}
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_G_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-					ctrl->value=card->muted;
-					return (0);
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_S_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-				{
-					register u16 io = card->io;
-					register u16 omask = inw(io + IO_MASK);
-					outw(~STR_WREN, io + IO_MASK);
-					outw((card->muted = ctrl->value ) ?
-						STR_WREN : 0, io);
-					udelay(4);
-					outw(omask, io + IO_MASK);
-					msleep(125);
-
-					return (0);
-				}
-			}
-			return -EINVAL;
-		}
-		default:
-			return v4l_compat_translate_ioctl(inode,file,cmd,arg,
-							  radio_function);
-	}
+	strlcpy(v->driver, "radio-maestro", sizeof(v->driver));
+	strlcpy(v->card, "Maestro Radio", sizeof(v->card));
+	sprintf(v->bus_info, "PCI");
+	v->version = RADIO_VERSION;
+	v->capabilities = V4L2_CAP_TUNER;
+	return 0;
 }
 
-static int radio_ioctl(struct inode *inode, struct file *file,
-	unsigned int cmd, unsigned long arg)
+static int vidioc_g_tuner(struct file *file, void *priv,
+					struct v4l2_tuner *v)
 {
 	struct video_device *dev = video_devdata(file);
 	struct radio_device *card = video_get_drvdata(dev);
-	int ret;
 
-	mutex_lock(&card->lock);
-	ret = video_usercopy(inode, file, cmd, arg, radio_function);
-	mutex_unlock(&card->lock);
+	if (v->index > 0)
+		return -EINVAL;
 
-	return ret;
+	(void)radio_bits_get(card);
+
+	strcpy(v->name, "FM");
+	v->type = V4L2_TUNER_RADIO;
+	v->rangelow = FREQ_LO;
+	v->rangehigh = FREQ_HI;
+	v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
+	v->capability = V4L2_TUNER_CAP_LOW;
+	if(card->stereo)
+		v->audmode = V4L2_TUNER_MODE_STEREO;
+	else
+		v->audmode = V4L2_TUNER_MODE_MONO;
+	v->signal = card->tuned;
+	return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+					struct v4l2_tuner *v)
+{
+	if (v->index > 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *f)
+{
+	struct video_device *dev = video_devdata(file);
+	struct radio_device *card = video_get_drvdata(dev);
+
+	if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
+		return -EINVAL;
+	radio_bits_set(card, FREQ2BITS(f->frequency));
+	return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *f)
+{
+	struct video_device *dev = video_devdata(file);
+	struct radio_device *card = video_get_drvdata(dev);
+
+	f->type = V4L2_TUNER_RADIO;
+	f->frequency = BITS2FREQ(radio_bits_get(card));
+	return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+					struct v4l2_queryctrl *qc)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+		if (qc->id && qc->id == radio_qctrl[i].id) {
+			memcpy(qc, &(radio_qctrl[i]),
+						sizeof(*qc));
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+					struct v4l2_control *ctrl)
+{
+	struct video_device *dev = video_devdata(file);
+	struct radio_device *card = video_get_drvdata(dev);
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		ctrl->value = card->muted;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+					struct v4l2_control *ctrl)
+{
+	struct video_device *dev = video_devdata(file);
+	struct radio_device *card = video_get_drvdata(dev);
+	register u16 io = card->io;
+	register u16 omask = inw(io + IO_MASK);
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		outw(~STR_WREN, io + IO_MASK);
+		outw((card->muted = ctrl->value ) ?
+				STR_WREN : 0, io);
+		udelay(4);
+		outw(omask, io + IO_MASK);
+		msleep(125);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv,
+					struct v4l2_audio *a)
+{
+	if (a->index > 1)
+		return -EINVAL;
+
+	strcpy(a->name, "Radio");
+	a->capability = V4L2_AUDCAP_STEREO;
+	return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+	if (i != 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+					struct v4l2_audio *a)
+{
+	if (a->index != 0)
+		return -EINVAL;
+	return 0;
 }
 
 static u16 __devinit radio_power_on(struct radio_device *dev)
@@ -352,6 +354,24 @@
 	return (ofreq == radio_bits_get(dev));
 }
 
+static struct video_device maestro_radio = {
+	.name           = "Maestro radio",
+	.type           = VID_TYPE_TUNER,
+	.fops           = &maestro_fops,
+	.vidioc_querycap    = vidioc_querycap,
+	.vidioc_g_tuner     = vidioc_g_tuner,
+	.vidioc_s_tuner     = vidioc_s_tuner,
+	.vidioc_g_audio     = vidioc_g_audio,
+	.vidioc_s_audio     = vidioc_s_audio,
+	.vidioc_g_input     = vidioc_g_input,
+	.vidioc_s_input     = vidioc_s_input,
+	.vidioc_g_frequency = vidioc_g_frequency,
+	.vidioc_s_frequency = vidioc_s_frequency,
+	.vidioc_queryctrl   = vidioc_queryctrl,
+	.vidioc_g_ctrl      = vidioc_g_ctrl,
+	.vidioc_s_ctrl      = vidioc_s_ctrl,
+};
+
 static int __devinit maestro_probe(struct pci_dev *pdev,
 	const struct pci_device_id *ent)
 {
diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c
index f668387..9b493b3 100644
--- a/drivers/media/radio/radio-rtrack2.c
+++ b/drivers/media/radio/radio-rtrack2.c
@@ -122,6 +122,26 @@
 	return 0;
 }
 
+static int vidioc_querycap(struct file *file, void *priv,
+				struct v4l2_capability *v)
+{
+	strlcpy(v->driver, "radio-rtrack2", sizeof(v->driver));
+	strlcpy(v->card, "RadioTrack II", sizeof(v->card));
+	sprintf(v->bus_info, "ISA");
+	v->version = RADIO_VERSION;
+	v->capabilities = V4L2_CAP_TUNER;
+	return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+				struct v4l2_tuner *v)
+{
+	if (v->index > 0)
+		return -EINVAL;
+
+	return 0;
+}
+
 static int rt_getsigstr(struct rt_device *dev)
 {
 	if (inb(io) & 2)	/* bit set = no signal present	*/
@@ -129,135 +149,136 @@
 	return 1;		/* signal present		*/
 }
 
-static int rt_do_ioctl(struct inode *inode, struct file *file,
-		       unsigned int cmd, void *arg)
+static int vidioc_g_tuner(struct file *file, void *priv,
+				struct v4l2_tuner *v)
 {
 	struct video_device *dev = video_devdata(file);
-	struct rt_device *rt=dev->priv;
+	struct rt_device *rt = dev->priv;
 
-	switch(cmd)
-	{
-		case VIDIOC_QUERYCAP:
-		{
-			struct v4l2_capability *v = arg;
-			memset(v,0,sizeof(*v));
-			strlcpy(v->driver, "radio-rtrack2", sizeof (v->driver));
-			strlcpy(v->card, "RadioTrack II", sizeof (v->card));
-			sprintf(v->bus_info,"ISA");
-			v->version = RADIO_VERSION;
-			v->capabilities = V4L2_CAP_TUNER;
+	if (v->index > 0)
+		return -EINVAL;
 
-			return 0;
-		}
-		case VIDIOC_G_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			memset(v,0,sizeof(*v));
-			strcpy(v->name, "FM");
-			v->type = V4L2_TUNER_RADIO;
-
-			v->rangelow=(88*16000);
-			v->rangehigh=(108*16000);
-			v->rxsubchans =V4L2_TUNER_SUB_MONO;
-			v->capability=V4L2_TUNER_CAP_LOW;
-			v->audmode = V4L2_TUNER_MODE_MONO;
-			v->signal=0xFFFF*rt_getsigstr(rt);
-
-			return 0;
-		}
-		case VIDIOC_S_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			return 0;
-		}
-		case VIDIOC_S_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			rt->curfreq = f->frequency;
-			rt_setfreq(rt, rt->curfreq);
-			return 0;
-		}
-		case VIDIOC_G_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			f->type = V4L2_TUNER_RADIO;
-			f->frequency = rt->curfreq;
-
-			return 0;
-		}
-		case VIDIOC_QUERYCTRL:
-		{
-			struct v4l2_queryctrl *qc = arg;
-			int i;
-
-			for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-				if (qc->id && qc->id == radio_qctrl[i].id) {
-					memcpy(qc, &(radio_qctrl[i]),
-								sizeof(*qc));
-					return (0);
-				}
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_G_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-					ctrl->value=rt->muted;
-					return (0);
-				case V4L2_CID_AUDIO_VOLUME:
-					if (rt->muted)
-						ctrl->value=0;
-					else
-						ctrl->value=65535;
-					return (0);
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_S_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-					if (ctrl->value) {
-						rt_mute(rt);
-					} else {
-						rt_unmute(rt);
-					}
-					return (0);
-				case V4L2_CID_AUDIO_VOLUME:
-					if (ctrl->value) {
-						rt_unmute(rt);
-					} else {
-						rt_mute(rt);
-					}
-					return (0);
-			}
-			return -EINVAL;
-		}
-		default:
-			return v4l_compat_translate_ioctl(inode,file,cmd,arg,
-							  rt_do_ioctl);
-	}
+	strcpy(v->name, "FM");
+	v->type = V4L2_TUNER_RADIO;
+	v->rangelow = (88*16000);
+	v->rangehigh = (108*16000);
+	v->rxsubchans = V4L2_TUNER_SUB_MONO;
+	v->capability = V4L2_TUNER_CAP_LOW;
+	v->audmode = V4L2_TUNER_MODE_MONO;
+	v->signal = 0xFFFF*rt_getsigstr(rt);
+	return 0;
 }
 
-static int rt_ioctl(struct inode *inode, struct file *file,
-		    unsigned int cmd, unsigned long arg)
+static int vidioc_s_frequency(struct file *file, void *priv,
+				struct v4l2_frequency *f)
 {
-	return video_usercopy(inode, file, cmd, arg, rt_do_ioctl);
+	struct video_device *dev = video_devdata(file);
+	struct rt_device *rt = dev->priv;
+
+	rt->curfreq = f->frequency;
+	rt_setfreq(rt, rt->curfreq);
+	return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+				struct v4l2_frequency *f)
+{
+	struct video_device *dev = video_devdata(file);
+	struct rt_device *rt = dev->priv;
+
+	f->type = V4L2_TUNER_RADIO;
+	f->frequency = rt->curfreq;
+	return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+				struct v4l2_queryctrl *qc)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+		if (qc->id && qc->id == radio_qctrl[i].id) {
+			memcpy(qc, &(radio_qctrl[i]),
+						sizeof(*qc));
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+				struct v4l2_control *ctrl)
+{
+	struct video_device *dev = video_devdata(file);
+	struct rt_device *rt = dev->priv;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		ctrl->value = rt->muted;
+		return 0;
+	case V4L2_CID_AUDIO_VOLUME:
+		if (rt->muted)
+			ctrl->value = 0;
+		else
+			ctrl->value = 65535;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+				struct v4l2_control *ctrl)
+{
+	struct video_device *dev = video_devdata(file);
+	struct rt_device *rt = dev->priv;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		if (ctrl->value)
+			rt_mute(rt);
+		else
+			rt_unmute(rt);
+		return 0;
+	case V4L2_CID_AUDIO_VOLUME:
+		if (ctrl->value)
+			rt_unmute(rt);
+		else
+			rt_mute(rt);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv,
+				struct v4l2_audio *a)
+{
+	if (a->index > 1)
+		return -EINVAL;
+
+	strcpy(a->name, "Radio");
+	a->capability = V4L2_AUDCAP_STEREO;
+	return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+	if (i != 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+				struct v4l2_audio *a)
+{
+	if (a->index != 0)
+		return -EINVAL;
+	return 0;
 }
 
 static struct rt_device rtrack2_unit;
@@ -266,7 +287,7 @@
 	.owner		= THIS_MODULE,
 	.open           = video_exclusive_open,
 	.release        = video_exclusive_release,
-	.ioctl		= rt_ioctl,
+	.ioctl		= video_ioctl2,
 	.compat_ioctl	= v4l_compat_ioctl32,
 	.llseek         = no_llseek,
 };
@@ -278,6 +299,18 @@
 	.type		= VID_TYPE_TUNER,
 	.hardware	= 0,
 	.fops           = &rtrack2_fops,
+	.vidioc_querycap    = vidioc_querycap,
+	.vidioc_g_tuner     = vidioc_g_tuner,
+	.vidioc_s_tuner     = vidioc_s_tuner,
+	.vidioc_g_frequency = vidioc_g_frequency,
+	.vidioc_s_frequency = vidioc_s_frequency,
+	.vidioc_queryctrl   = vidioc_queryctrl,
+	.vidioc_g_ctrl      = vidioc_g_ctrl,
+	.vidioc_s_ctrl      = vidioc_s_ctrl,
+	.vidioc_g_audio     = vidioc_g_audio,
+	.vidioc_s_audio     = vidioc_s_audio,
+	.vidioc_g_input     = vidioc_g_input,
+	.vidioc_s_input     = vidioc_s_input,
 };
 
 static int __init rtrack2_init(void)
diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
index f4619e4..dc33f19 100644
--- a/drivers/media/radio/radio-sf16fmi.c
+++ b/drivers/media/radio/radio-sf16fmi.c
@@ -130,137 +130,155 @@
 	return (res & 2) ? 0 : 0xFFFF;
 }
 
-static int fmi_do_ioctl(struct inode *inode, struct file *file,
-			unsigned int cmd, void *arg)
+static int vidioc_querycap(struct file *file, void  *priv,
+					struct v4l2_capability *v)
 {
-	struct video_device *dev = video_devdata(file);
-	struct fmi_device *fmi=dev->priv;
-
-	switch(cmd)
-	{
-		case VIDIOC_QUERYCAP:
-		{
-			struct v4l2_capability *v = arg;
-			memset(v,0,sizeof(*v));
-			strlcpy(v->driver, "radio-sf16fmi", sizeof (v->driver));
-			strlcpy(v->card, "SF16-FMx radio", sizeof (v->card));
-			sprintf(v->bus_info,"ISA");
-			v->version = RADIO_VERSION;
-			v->capabilities = V4L2_CAP_TUNER;
-
-			return 0;
-		}
-		case VIDIOC_G_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-			int mult;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			memset(v,0,sizeof(*v));
-			strcpy(v->name, "FM");
-			v->type = V4L2_TUNER_RADIO;
-
-			mult = (fmi->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000;
-			v->rangelow = RSF16_MINFREQ/mult;
-			v->rangehigh = RSF16_MAXFREQ/mult;
-			v->rxsubchans =V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO;
-			v->capability=fmi->flags&V4L2_TUNER_CAP_LOW;
-			v->audmode = V4L2_TUNER_MODE_STEREO;
-			v->signal = fmi_getsigstr(fmi);
-
-			return 0;
-		}
-		case VIDIOC_S_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			return 0;
-		}
-		case VIDIOC_S_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			if (!(fmi->flags & V4L2_TUNER_CAP_LOW))
-				f->frequency *= 1000;
-			if (f->frequency < RSF16_MINFREQ ||
-					f->frequency > RSF16_MAXFREQ )
-				return -EINVAL;
-			/*rounding in steps of 800 to match th freq
-			  that will be used */
-			fmi->curfreq = (f->frequency/800)*800;
-			fmi_setfreq(fmi);
-
-			return 0;
-		}
-		case VIDIOC_G_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			f->type = V4L2_TUNER_RADIO;
-			f->frequency = fmi->curfreq;
-			if (!(fmi->flags & V4L2_TUNER_CAP_LOW))
-				f->frequency /= 1000;
-
-			return 0;
-		}
-		case VIDIOC_QUERYCTRL:
-		{
-			struct v4l2_queryctrl *qc = arg;
-			int i;
-
-			for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-				if (qc->id && qc->id == radio_qctrl[i].id) {
-					memcpy(qc, &(radio_qctrl[i]),
-								sizeof(*qc));
-					return (0);
-				}
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_G_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-					ctrl->value=fmi->curvol;
-					return (0);
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_S_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-				{
-					if (ctrl->value)
-						fmi_mute(fmi->port);
-					else
-						fmi_unmute(fmi->port);
-
-					fmi->curvol=ctrl->value;
-					return (0);
-				}
-			}
-			return -EINVAL;
-		}
-		default:
-			return v4l_compat_translate_ioctl(inode,file,cmd,arg,
-							  fmi_do_ioctl);
-	}
+	strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver));
+	strlcpy(v->card, "SF16-FMx radio", sizeof(v->card));
+	sprintf(v->bus_info, "ISA");
+	v->version = RADIO_VERSION;
+	v->capabilities = V4L2_CAP_TUNER;
+	return 0;
 }
 
-static int fmi_ioctl(struct inode *inode, struct file *file,
-		     unsigned int cmd, unsigned long arg)
+static int vidioc_g_tuner(struct file *file, void *priv,
+					struct v4l2_tuner *v)
 {
-	return video_usercopy(inode, file, cmd, arg, fmi_do_ioctl);
+	int mult;
+	struct video_device *dev = video_devdata(file);
+	struct fmi_device *fmi = dev->priv;
+
+	if (v->index > 0)
+		return -EINVAL;
+
+	strcpy(v->name, "FM");
+	v->type = V4L2_TUNER_RADIO;
+	mult = (fmi->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000;
+	v->rangelow = RSF16_MINFREQ/mult;
+	v->rangehigh = RSF16_MAXFREQ/mult;
+	v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO;
+	v->capability = fmi->flags&V4L2_TUNER_CAP_LOW;
+	v->audmode = V4L2_TUNER_MODE_STEREO;
+	v->signal = fmi_getsigstr(fmi);
+	return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+					struct v4l2_tuner *v)
+{
+	if (v->index > 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *f)
+{
+	struct video_device *dev = video_devdata(file);
+	struct fmi_device *fmi = dev->priv;
+
+	if (!(fmi->flags & V4L2_TUNER_CAP_LOW))
+		f->frequency *= 1000;
+	if (f->frequency < RSF16_MINFREQ ||
+			f->frequency > RSF16_MAXFREQ )
+		return -EINVAL;
+	/*rounding in steps of 800 to match th freq
+	that will be used */
+	fmi->curfreq = (f->frequency/800)*800;
+	fmi_setfreq(fmi);
+	return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *f)
+{
+	struct video_device *dev = video_devdata(file);
+	struct fmi_device *fmi = dev->priv;
+
+	f->type = V4L2_TUNER_RADIO;
+	f->frequency = fmi->curfreq;
+	if (!(fmi->flags & V4L2_TUNER_CAP_LOW))
+		f->frequency /= 1000;
+	return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+					struct v4l2_queryctrl *qc)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+		if (qc->id && qc->id == radio_qctrl[i].id) {
+			memcpy(qc, &(radio_qctrl[i]),
+						sizeof(*qc));
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+					struct v4l2_control *ctrl)
+{
+	struct video_device *dev = video_devdata(file);
+	struct fmi_device *fmi = dev->priv;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		ctrl->value = fmi->curvol;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+					struct v4l2_control *ctrl)
+{
+	struct video_device *dev = video_devdata(file);
+	struct fmi_device *fmi = dev->priv;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		if (ctrl->value)
+			fmi_mute(fmi->port);
+		else
+			fmi_unmute(fmi->port);
+		fmi->curvol = ctrl->value;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv,
+					struct v4l2_audio *a)
+{
+	if (a->index > 1)
+		return -EINVAL;
+
+	strcpy(a->name, "Radio");
+	a->capability = V4L2_AUDCAP_STEREO;
+	return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+	if (i != 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+					struct v4l2_audio *a)
+{
+	if (a->index != 0)
+		return -EINVAL;
+	return 0;
 }
 
 static struct fmi_device fmi_unit;
@@ -269,7 +287,7 @@
 	.owner		= THIS_MODULE,
 	.open           = video_exclusive_open,
 	.release        = video_exclusive_release,
-	.ioctl		= fmi_ioctl,
+	.ioctl		= video_ioctl2,
 	.compat_ioctl	= v4l_compat_ioctl32,
 	.llseek         = no_llseek,
 };
@@ -281,6 +299,18 @@
 	.type		= VID_TYPE_TUNER,
 	.hardware	= 0,
 	.fops           = &fmi_fops,
+	.vidioc_querycap    = vidioc_querycap,
+	.vidioc_g_tuner     = vidioc_g_tuner,
+	.vidioc_s_tuner     = vidioc_s_tuner,
+	.vidioc_g_audio     = vidioc_g_audio,
+	.vidioc_s_audio     = vidioc_s_audio,
+	.vidioc_g_input     = vidioc_g_input,
+	.vidioc_s_input     = vidioc_s_input,
+	.vidioc_g_frequency = vidioc_g_frequency,
+	.vidioc_s_frequency = vidioc_s_frequency,
+	.vidioc_queryctrl   = vidioc_queryctrl,
+	.vidioc_g_ctrl      = vidioc_g_ctrl,
+	.vidioc_s_ctrl      = vidioc_s_ctrl,
 };
 
 /* ladis: this is my card. does any other types exist? */
diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c
index b96fafe..e6c125d 100644
--- a/drivers/media/radio/radio-sf16fmr2.c
+++ b/drivers/media/radio/radio-sf16fmr2.c
@@ -226,186 +226,204 @@
 	return 0;
 }
 
-static int fmr2_do_ioctl(struct inode *inode, struct file *file,
-		      unsigned int cmd, void *arg)
+static int vidioc_querycap(struct file *file, void  *priv,
+					struct v4l2_capability *v)
+{
+	strlcpy(v->driver, "radio-sf16fmr2", sizeof(v->driver));
+	strlcpy(v->card, "SF16-FMR2 radio", sizeof(v->card));
+	sprintf(v->bus_info, "ISA");
+	v->version = RADIO_VERSION;
+	v->capabilities = V4L2_CAP_TUNER;
+	return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+					struct v4l2_tuner *v)
+{
+	int mult;
+	struct video_device *dev = video_devdata(file);
+	struct fmr2_device *fmr2 = dev->priv;
+
+	if (v->index > 0)
+		return -EINVAL;
+
+	strcpy(v->name, "FM");
+	v->type = V4L2_TUNER_RADIO;
+
+	mult = (fmr2->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000;
+	v->rangelow = RSF16_MINFREQ/mult;
+	v->rangehigh = RSF16_MAXFREQ/mult;
+	v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO;
+	v->capability = fmr2->flags&V4L2_TUNER_CAP_LOW;
+	v->audmode = fmr2->stereo ? V4L2_TUNER_MODE_STEREO:
+				V4L2_TUNER_MODE_MONO;
+	mutex_lock(&lock);
+	v->signal = fmr2_getsigstr(fmr2);
+	mutex_unlock(&lock);
+	return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+					struct v4l2_tuner *v)
+{
+	if (v->index > 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *f)
 {
 	struct video_device *dev = video_devdata(file);
 	struct fmr2_device *fmr2 = dev->priv;
-	debug_print((KERN_DEBUG "freq %ld flags %d vol %d mute %d "
-		"stereo %d type %d\n",
-		fmr2->curfreq, fmr2->flags, fmr2->curvol, fmr2->mute,
-		fmr2->stereo, fmr2->card_type));
 
-	switch(cmd)
-	{
-		case VIDIOC_QUERYCAP:
-		{
-			struct v4l2_capability *v = arg;
-			memset(v,0,sizeof(*v));
-			strlcpy(v->driver, "radio-sf16fmr2", sizeof (v->driver));
-			strlcpy(v->card, "SF16-FMR2 radio", sizeof (v->card));
-			sprintf(v->bus_info,"ISA");
-			v->version = RADIO_VERSION;
-			v->capabilities = V4L2_CAP_TUNER;
+	if (!(fmr2->flags & V4L2_TUNER_CAP_LOW))
+		f->frequency *= 1000;
+	if (f->frequency < RSF16_MINFREQ ||
+			f->frequency > RSF16_MAXFREQ )
+		return -EINVAL;
+	/*rounding in steps of 200 to match th freq
+	that will be used */
+	fmr2->curfreq = (f->frequency/200)*200;
 
-			return 0;
-		}
-		case VIDIOC_G_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-			int mult;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			memset(v,0,sizeof(*v));
-			strcpy(v->name, "FM");
-			v->type = V4L2_TUNER_RADIO;
-
-			mult = (fmr2->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000;
-			v->rangelow = RSF16_MINFREQ/mult;
-			v->rangehigh = RSF16_MAXFREQ/mult;
-			v->rxsubchans =V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO;
-			v->capability=fmr2->flags&V4L2_TUNER_CAP_LOW;
-
-			v->audmode = fmr2->stereo ? V4L2_TUNER_MODE_STEREO:
-						    V4L2_TUNER_MODE_MONO;
-			mutex_lock(&lock);
-			v->signal = fmr2_getsigstr(fmr2);
-			mutex_unlock(&lock);
-
-			return 0;
-		}
-		case VIDIOC_S_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			return 0;
-		}
-		case VIDIOC_S_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			if (!(fmr2->flags & V4L2_TUNER_CAP_LOW))
-				f->frequency *= 1000;
-			if (f->frequency < RSF16_MINFREQ ||
-					f->frequency > RSF16_MAXFREQ )
-				return -EINVAL;
-			/*rounding in steps of 200 to match th freq
-			  that will be used */
-			fmr2->curfreq = (f->frequency/200)*200;
-
-			/* set card freq (if not muted) */
-			if (fmr2->curvol && !fmr2->mute)
-			{
-				mutex_lock(&lock);
-				fmr2_setfreq(fmr2);
-				mutex_unlock(&lock);
-			}
-
-			return 0;
-		}
-		case VIDIOC_G_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			f->type = V4L2_TUNER_RADIO;
-			f->frequency = fmr2->curfreq;
-			if (!(fmr2->flags & V4L2_TUNER_CAP_LOW))
-				f->frequency /= 1000;
-
-			return 0;
-		}
-		case VIDIOC_QUERYCTRL:
-		{
-			struct v4l2_queryctrl *qc = arg;
-			int i;
-
-			for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-				if ((fmr2->card_type != 11)
-						&& V4L2_CID_AUDIO_VOLUME)
-					radio_qctrl[i].step=65535;
-				if (qc->id && qc->id == radio_qctrl[i].id) {
-					memcpy(qc, &(radio_qctrl[i]),
-								sizeof(*qc));
-					return (0);
-				}
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_G_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-					ctrl->value=fmr2->mute;
-					return (0);
-				case V4L2_CID_AUDIO_VOLUME:
-					ctrl->value=fmr2->curvol;
-					return (0);
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_S_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-					fmr2->mute=ctrl->value;
-					if (fmr2->card_type != 11) {
-						if (!fmr2->mute) {
-							fmr2->curvol = 65535;
-						} else {
-							fmr2->curvol = 0;
-						}
-					}
-					break;
-				case V4L2_CID_AUDIO_VOLUME:
-					fmr2->curvol = ctrl->value;
-					if (fmr2->card_type != 11) {
-						if (fmr2->curvol) {
-							fmr2->curvol = 65535;
-							fmr2->mute = 0;
-						} else {
-							fmr2->curvol = 0;
-							fmr2->mute = 1;
-						}
-					}
-					break;
-				default:
-					return -EINVAL;
-			}
-#ifdef DEBUG
-			if (fmr2->curvol && !fmr2->mute)
-				printk(KERN_DEBUG "unmute\n");
-			else
-				printk(KERN_DEBUG "mute\n");
-#endif
-			mutex_lock(&lock);
-			if (fmr2->curvol && !fmr2->mute) {
-				fmr2_setvolume(fmr2);
-				fmr2_setfreq(fmr2);
-			} else
-				fmr2_mute(fmr2->port);
-			mutex_unlock(&lock);
-			return (0);
-		}
-		default:
-			return v4l_compat_translate_ioctl(inode,file,cmd,arg,
-							  fmr2_do_ioctl);
-
+	/* set card freq (if not muted) */
+	if (fmr2->curvol && !fmr2->mute) {
+		mutex_lock(&lock);
+		fmr2_setfreq(fmr2);
+		mutex_unlock(&lock);
 	}
+	return 0;
 }
 
-static int fmr2_ioctl(struct inode *inode, struct file *file,
-		      unsigned int cmd, unsigned long arg)
- {
-	return video_usercopy(inode, file, cmd, arg, fmr2_do_ioctl);
+static int vidioc_g_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *f)
+{
+	struct video_device *dev = video_devdata(file);
+	struct fmr2_device *fmr2 = dev->priv;
+
+	f->type = V4L2_TUNER_RADIO;
+	f->frequency = fmr2->curfreq;
+	if (!(fmr2->flags & V4L2_TUNER_CAP_LOW))
+		f->frequency /= 1000;
+	return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+					struct v4l2_queryctrl *qc)
+{
+	int i;
+	struct video_device *dev = video_devdata(file);
+	struct fmr2_device *fmr2 = dev->priv;
+
+	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+		if ((fmr2->card_type != 11)
+				&& V4L2_CID_AUDIO_VOLUME)
+			radio_qctrl[i].step = 65535;
+		if (qc->id && qc->id == radio_qctrl[i].id) {
+			memcpy(qc, &(radio_qctrl[i]),
+						sizeof(*qc));
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+					struct v4l2_control *ctrl)
+{
+	struct video_device *dev = video_devdata(file);
+	struct fmr2_device *fmr2 = dev->priv;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		ctrl->value = fmr2->mute;
+		return 0;
+	case V4L2_CID_AUDIO_VOLUME:
+		ctrl->value = fmr2->curvol;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+					struct v4l2_control *ctrl)
+{
+	struct video_device *dev = video_devdata(file);
+	struct fmr2_device *fmr2 = dev->priv;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		fmr2->mute = ctrl->value;
+		if (fmr2->card_type != 11) {
+			if (!fmr2->mute)
+				fmr2->curvol = 65535;
+			else
+				fmr2->curvol = 0;
+		}
+		break;
+	case V4L2_CID_AUDIO_VOLUME:
+		fmr2->curvol = ctrl->value;
+		if (fmr2->card_type != 11) {
+			if (fmr2->curvol) {
+				fmr2->curvol = 65535;
+				fmr2->mute = 0;
+			} else {
+				fmr2->curvol = 0;
+				fmr2->mute = 1;
+			}
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+#ifdef DEBUG
+	if (fmr2->curvol && !fmr2->mute)
+		printk(KERN_DEBUG "unmute\n");
+	else
+		printk(KERN_DEBUG "mute\n");
+#endif
+
+	mutex_lock(&lock);
+	if (fmr2->curvol && !fmr2->mute) {
+		fmr2_setvolume(fmr2);
+		fmr2_setfreq(fmr2);
+	} else
+		fmr2_mute(fmr2->port);
+	mutex_unlock(&lock);
+	return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv,
+					struct v4l2_audio *a)
+{
+	if (a->index > 1)
+		return -EINVAL;
+
+	strcpy(a->name, "Radio");
+	a->capability = V4L2_AUDCAP_STEREO;
+	return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+	if (i != 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+					struct v4l2_audio *a)
+{
+	if (a->index != 0)
+		return -EINVAL;
+	return 0;
 }
 
 static struct fmr2_device fmr2_unit;
@@ -414,7 +432,7 @@
 	.owner          = THIS_MODULE,
 	.open           = video_exclusive_open,
 	.release        = video_exclusive_release,
-	.ioctl          = fmr2_ioctl,
+	.ioctl          = video_ioctl2,
 	.compat_ioctl	= v4l_compat_ioctl32,
 	.llseek         = no_llseek,
 };
@@ -426,6 +444,18 @@
 	. type		= VID_TYPE_TUNER,
 	.hardware	= 0,
 	.fops		= &fmr2_fops,
+	.vidioc_querycap    = vidioc_querycap,
+	.vidioc_g_tuner     = vidioc_g_tuner,
+	.vidioc_s_tuner     = vidioc_s_tuner,
+	.vidioc_g_audio     = vidioc_g_audio,
+	.vidioc_s_audio     = vidioc_s_audio,
+	.vidioc_g_input     = vidioc_g_input,
+	.vidioc_s_input     = vidioc_s_input,
+	.vidioc_g_frequency = vidioc_g_frequency,
+	.vidioc_s_frequency = vidioc_s_frequency,
+	.vidioc_queryctrl   = vidioc_queryctrl,
+	.vidioc_g_ctrl      = vidioc_g_ctrl,
+	.vidioc_s_ctrl      = vidioc_s_ctrl,
 };
 
 static int __init fmr2_init(void)
diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c
index d59a27a..e43acfd 100644
--- a/drivers/media/radio/radio-terratec.c
+++ b/drivers/media/radio/radio-terratec.c
@@ -205,135 +205,152 @@
 	return 1;		/* signal present		*/
 }
 
-
-/* implement the video4linux api */
-
-static int tt_do_ioctl(struct inode *inode, struct file *file,
-		       unsigned int cmd, void *arg)
+static int vidioc_querycap(struct file *file, void *priv,
+					struct v4l2_capability *v)
 {
-	struct video_device *dev = video_devdata(file);
-	struct tt_device *tt=dev->priv;
-
-	switch(cmd)
-	{
-		case VIDIOC_QUERYCAP:
-		{
-			struct v4l2_capability *v = arg;
-			memset(v,0,sizeof(*v));
-			strlcpy(v->driver, "radio-terratec", sizeof (v->driver));
-			strlcpy(v->card, "ActiveRadio", sizeof (v->card));
-			sprintf(v->bus_info,"ISA");
-			v->version = RADIO_VERSION;
-			v->capabilities = V4L2_CAP_TUNER;
-
-			return 0;
-		}
-		case VIDIOC_G_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			memset(v,0,sizeof(*v));
-			strcpy(v->name, "FM");
-			v->type = V4L2_TUNER_RADIO;
-
-			v->rangelow=(87*16000);
-			v->rangehigh=(108*16000);
-			v->rxsubchans =V4L2_TUNER_SUB_MONO;
-			v->capability=V4L2_TUNER_CAP_LOW;
-			v->audmode = V4L2_TUNER_MODE_MONO;
-			v->signal=0xFFFF*tt_getsigstr(tt);
-
-			return 0;
-		}
-		case VIDIOC_S_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			return 0;
-		}
-		case VIDIOC_S_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			tt->curfreq = f->frequency;
-			tt_setfreq(tt, tt->curfreq);
-			return 0;
-		}
-		case VIDIOC_G_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			f->type = V4L2_TUNER_RADIO;
-			f->frequency = tt->curfreq;
-
-			return 0;
-		}
-		case VIDIOC_QUERYCTRL:
-		{
-			struct v4l2_queryctrl *qc = arg;
-			int i;
-
-			for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-				if (qc->id && qc->id == radio_qctrl[i].id) {
-					memcpy(qc, &(radio_qctrl[i]),
-								sizeof(*qc));
-					return (0);
-				}
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_G_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-					if (tt->muted)
-						ctrl->value=1;
-					else
-						ctrl->value=0;
-					return (0);
-				case V4L2_CID_AUDIO_VOLUME:
-					ctrl->value=tt->curvol * 6554;
-					return (0);
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_S_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-					if (ctrl->value) {
-						tt_mute(tt);
-					} else {
-						tt_setvol(tt,tt->curvol);
-					}
-					return (0);
-				case V4L2_CID_AUDIO_VOLUME:
-					tt_setvol(tt,ctrl->value);
-					return (0);
-			}
-			return -EINVAL;
-		}
-
-		default:
-			return v4l_compat_translate_ioctl(inode,file,cmd,arg,
-							  tt_do_ioctl);
-	}
+	strlcpy(v->driver, "radio-terratec", sizeof(v->driver));
+	strlcpy(v->card, "ActiveRadio", sizeof(v->card));
+	sprintf(v->bus_info, "ISA");
+	v->version = RADIO_VERSION;
+	v->capabilities = V4L2_CAP_TUNER;
+	return 0;
 }
 
-static int tt_ioctl(struct inode *inode, struct file *file,
-		    unsigned int cmd, unsigned long arg)
+static int vidioc_g_tuner(struct file *file, void *priv,
+					struct v4l2_tuner *v)
 {
-	return video_usercopy(inode, file, cmd, arg, tt_do_ioctl);
+	struct video_device *dev = video_devdata(file);
+	struct tt_device *tt = dev->priv;
+
+	if (v->index > 0)
+		return -EINVAL;
+
+	strcpy(v->name, "FM");
+	v->type = V4L2_TUNER_RADIO;
+	v->rangelow = (87*16000);
+	v->rangehigh = (108*16000);
+	v->rxsubchans = V4L2_TUNER_SUB_MONO;
+	v->capability = V4L2_TUNER_CAP_LOW;
+	v->audmode = V4L2_TUNER_MODE_MONO;
+	v->signal = 0xFFFF*tt_getsigstr(tt);
+	return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+					struct v4l2_tuner *v)
+{
+	if (v->index > 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *f)
+{
+	struct video_device *dev = video_devdata(file);
+	struct tt_device *tt = dev->priv;
+
+	tt->curfreq = f->frequency;
+	tt_setfreq(tt, tt->curfreq);
+	return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *f)
+{
+	struct video_device *dev = video_devdata(file);
+	struct tt_device *tt = dev->priv;
+
+	f->type = V4L2_TUNER_RADIO;
+	f->frequency = tt->curfreq;
+	return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+					struct v4l2_queryctrl *qc)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+		if (qc->id && qc->id == radio_qctrl[i].id) {
+			memcpy(qc, &(radio_qctrl[i]),
+						sizeof(*qc));
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+					struct v4l2_control *ctrl)
+{
+	struct video_device *dev = video_devdata(file);
+	struct tt_device *tt = dev->priv;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		if (tt->muted)
+			ctrl->value = 1;
+		else
+			ctrl->value = 0;
+		return 0;
+	case V4L2_CID_AUDIO_VOLUME:
+		ctrl->value = tt->curvol * 6554;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+					struct v4l2_control *ctrl)
+{
+	struct video_device *dev = video_devdata(file);
+	struct tt_device *tt = dev->priv;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		if (ctrl->value)
+			tt_mute(tt);
+		else
+			tt_setvol(tt,tt->curvol);
+		return 0;
+	case V4L2_CID_AUDIO_VOLUME:
+		tt_setvol(tt,ctrl->value);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv,
+					struct v4l2_audio *a)
+{
+	if (a->index > 1)
+		return -EINVAL;
+
+	strcpy(a->name, "Radio");
+	a->capability = V4L2_AUDCAP_STEREO;
+	return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+	if (i != 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+					struct v4l2_audio *a)
+{
+	if (a->index != 0)
+		return -EINVAL;
+	return 0;
 }
 
 static struct tt_device terratec_unit;
@@ -342,7 +359,7 @@
 	.owner		= THIS_MODULE,
 	.open           = video_exclusive_open,
 	.release        = video_exclusive_release,
-	.ioctl		= tt_ioctl,
+	.ioctl		= video_ioctl2,
 	.compat_ioctl	= v4l_compat_ioctl32,
 	.llseek         = no_llseek,
 };
@@ -354,6 +371,18 @@
 	.type		= VID_TYPE_TUNER,
 	.hardware	= 0,
 	.fops           = &terratec_fops,
+	.vidioc_querycap    = vidioc_querycap,
+	.vidioc_g_tuner     = vidioc_g_tuner,
+	.vidioc_s_tuner     = vidioc_s_tuner,
+	.vidioc_g_frequency = vidioc_g_frequency,
+	.vidioc_s_frequency = vidioc_s_frequency,
+	.vidioc_queryctrl   = vidioc_queryctrl,
+	.vidioc_g_ctrl      = vidioc_g_ctrl,
+	.vidioc_s_ctrl      = vidioc_s_ctrl,
+	.vidioc_g_audio     = vidioc_g_audio,
+	.vidioc_s_audio     = vidioc_s_audio,
+	.vidioc_g_input     = vidioc_g_input,
+	.vidioc_s_input     = vidioc_s_input,
 };
 
 static int __init terratec_init(void)
diff --git a/drivers/media/radio/radio-trust.c b/drivers/media/radio/radio-trust.c
index 6d7f1e7..c27c629 100644
--- a/drivers/media/radio/radio-trust.c
+++ b/drivers/media/radio/radio-trust.c
@@ -192,144 +192,154 @@
 	write_i2c(5, TSA6060T_ADDR, (f << 1) | 1, f >> 7, 0x60 | ((f >> 15) & 1), 0);
 }
 
-static int tr_do_ioctl(struct inode *inode, struct file *file,
-		       unsigned int cmd, void *arg)
+static int vidioc_querycap(struct file *file, void *priv,
+				struct v4l2_capability *v)
 {
-	switch(cmd)
-	{
-		case VIDIOC_QUERYCAP:
-		{
-			struct v4l2_capability *v = arg;
-			memset(v,0,sizeof(*v));
-			strlcpy(v->driver, "radio-trust", sizeof (v->driver));
-			strlcpy(v->card, "Trust FM Radio", sizeof (v->card));
-			sprintf(v->bus_info,"ISA");
-			v->version = RADIO_VERSION;
-			v->capabilities = V4L2_CAP_TUNER;
-
-			return 0;
-		}
-		case VIDIOC_G_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			memset(v,0,sizeof(*v));
-			strcpy(v->name, "FM");
-			v->type = V4L2_TUNER_RADIO;
-
-			v->rangelow=(87.5*16000);
-			v->rangehigh=(108*16000);
-			v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
-			v->capability=V4L2_TUNER_CAP_LOW;
-			if(tr_getstereo())
-				v->audmode = V4L2_TUNER_MODE_STEREO;
-			else
-				v->audmode = V4L2_TUNER_MODE_MONO;
-			v->signal=tr_getsigstr();
-
-			return 0;
-		}
-		case VIDIOC_S_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			return 0;
-		}
-		case VIDIOC_S_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			curfreq = f->frequency;
-			tr_setfreq(curfreq);
-			return 0;
-		}
-		case VIDIOC_G_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			f->type = V4L2_TUNER_RADIO;
-			f->frequency = curfreq;
-
-			return 0;
-		}
-		case VIDIOC_QUERYCTRL:
-		{
-			struct v4l2_queryctrl *qc = arg;
-			int i;
-
-			for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-				if (qc->id && qc->id == radio_qctrl[i].id) {
-					memcpy(qc, &(radio_qctrl[i]),
-								sizeof(*qc));
-					return (0);
-				}
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_G_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-					ctrl->value=curmute;
-					return (0);
-				case V4L2_CID_AUDIO_VOLUME:
-					ctrl->value= curvol * 2048;
-					return (0);
-				case V4L2_CID_AUDIO_BASS:
-					ctrl->value= curbass * 4370;
-					return (0);
-				case V4L2_CID_AUDIO_TREBLE:
-					ctrl->value= curtreble * 4370;
-					return (0);
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_S_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-					tr_setmute(ctrl->value);
-					return 0;
-				case V4L2_CID_AUDIO_VOLUME:
-					tr_setvol(ctrl->value);
-					return 0;
-				case V4L2_CID_AUDIO_BASS:
-					tr_setbass(ctrl->value);
-					return 0;
-				case V4L2_CID_AUDIO_TREBLE:
-					tr_settreble(ctrl->value);
-					return (0);
-			}
-			return -EINVAL;
-		}
-
-		default:
-			return v4l_compat_translate_ioctl(inode,file,cmd,arg,
-							  tr_do_ioctl);
-	}
+	strlcpy(v->driver, "radio-trust", sizeof(v->driver));
+	strlcpy(v->card, "Trust FM Radio", sizeof(v->card));
+	sprintf(v->bus_info, "ISA");
+	v->version = RADIO_VERSION;
+	v->capabilities = V4L2_CAP_TUNER;
+	return 0;
 }
 
-static int tr_ioctl(struct inode *inode, struct file *file,
-		    unsigned int cmd, unsigned long arg)
+static int vidioc_g_tuner(struct file *file, void *priv,
+				struct v4l2_tuner *v)
 {
-	return video_usercopy(inode, file, cmd, arg, tr_do_ioctl);
+	if (v->index > 0)
+		return -EINVAL;
+
+	strcpy(v->name, "FM");
+	v->type = V4L2_TUNER_RADIO;
+	v->rangelow = (87.5*16000);
+	v->rangehigh = (108*16000);
+	v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
+	v->capability = V4L2_TUNER_CAP_LOW;
+	if (tr_getstereo())
+		v->audmode = V4L2_TUNER_MODE_STEREO;
+	else
+		v->audmode = V4L2_TUNER_MODE_MONO;
+	v->signal = tr_getsigstr();
+	return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+				struct v4l2_tuner *v)
+{
+	if (v->index > 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+				struct v4l2_frequency *f)
+{
+	curfreq = f->frequency;
+	tr_setfreq(curfreq);
+	return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+				struct v4l2_frequency *f)
+{
+	f->type = V4L2_TUNER_RADIO;
+	f->frequency = curfreq;
+	return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+				struct v4l2_queryctrl *qc)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+		if (qc->id && qc->id == radio_qctrl[i].id) {
+			memcpy(qc, &(radio_qctrl[i]),
+						sizeof(*qc));
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+				struct v4l2_control *ctrl)
+{
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		ctrl->value = curmute;
+		return 0;
+	case V4L2_CID_AUDIO_VOLUME:
+		ctrl->value = curvol * 2048;
+		return 0;
+	case V4L2_CID_AUDIO_BASS:
+		ctrl->value = curbass * 4370;
+		return 0;
+	case V4L2_CID_AUDIO_TREBLE:
+		ctrl->value = curtreble * 4370;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+				struct v4l2_control *ctrl)
+{
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		tr_setmute(ctrl->value);
+		return 0;
+	case V4L2_CID_AUDIO_VOLUME:
+		tr_setvol(ctrl->value);
+		return 0;
+	case V4L2_CID_AUDIO_BASS:
+		tr_setbass(ctrl->value);
+		return 0;
+	case V4L2_CID_AUDIO_TREBLE:
+		tr_settreble(ctrl->value);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv,
+				struct v4l2_audio *a)
+{
+	if (a->index > 1)
+		return -EINVAL;
+
+	strcpy(a->name, "Radio");
+	a->capability = V4L2_AUDCAP_STEREO;
+	return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+	if (i != 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+				struct v4l2_audio *a)
+{
+	if (a->index != 0)
+		return -EINVAL;
+	return 0;
 }
 
 static const struct file_operations trust_fops = {
 	.owner		= THIS_MODULE,
 	.open           = video_exclusive_open,
 	.release        = video_exclusive_release,
-	.ioctl		= tr_ioctl,
+	.ioctl		= video_ioctl2,
 	.compat_ioctl	= v4l_compat_ioctl32,
 	.llseek         = no_llseek,
 };
@@ -341,6 +351,18 @@
 	.type		= VID_TYPE_TUNER,
 	.hardware	= 0,
 	.fops           = &trust_fops,
+	.vidioc_querycap    = vidioc_querycap,
+	.vidioc_g_tuner     = vidioc_g_tuner,
+	.vidioc_s_tuner     = vidioc_s_tuner,
+	.vidioc_g_frequency = vidioc_g_frequency,
+	.vidioc_s_frequency = vidioc_s_frequency,
+	.vidioc_queryctrl   = vidioc_queryctrl,
+	.vidioc_g_ctrl      = vidioc_g_ctrl,
+	.vidioc_s_ctrl      = vidioc_s_ctrl,
+	.vidioc_g_audio     = vidioc_g_audio,
+	.vidioc_s_audio     = vidioc_s_audio,
+	.vidioc_g_input     = vidioc_g_input,
+	.vidioc_s_input     = vidioc_s_input,
 };
 
 static int __init trust_init(void)
diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c
index 3031fef..8ff5a23 100644
--- a/drivers/media/radio/radio-typhoon.c
+++ b/drivers/media/radio/radio-typhoon.c
@@ -93,8 +93,6 @@
 static void typhoon_mute(struct typhoon_device *dev);
 static void typhoon_unmute(struct typhoon_device *dev);
 static int typhoon_setvol(struct typhoon_device *dev, int vol);
-static int typhoon_ioctl(struct inode *inode, struct file *file,
-			 unsigned int cmd, unsigned long arg);
 #ifdef CONFIG_RADIO_TYPHOON_PROC_FS
 static int typhoon_get_info(char *buf, char **start, off_t offset, int len);
 #endif
@@ -186,129 +184,148 @@
 	return 0;
 }
 
+static int vidioc_querycap(struct file *file, void  *priv,
+					struct v4l2_capability *v)
+{
+	strlcpy(v->driver, "radio-typhoon", sizeof(v->driver));
+	strlcpy(v->card, "Typhoon Radio", sizeof(v->card));
+	sprintf(v->bus_info, "ISA");
+	v->version = RADIO_VERSION;
+	v->capabilities = V4L2_CAP_TUNER;
+	return 0;
+}
 
-static int typhoon_do_ioctl(struct inode *inode, struct file *file,
-			    unsigned int cmd, void *arg)
+static int vidioc_g_tuner(struct file *file, void *priv,
+					struct v4l2_tuner *v)
+{
+	if (v->index > 0)
+		return -EINVAL;
+
+	strcpy(v->name, "FM");
+	v->type = V4L2_TUNER_RADIO;
+	v->rangelow = (87.5*16000);
+	v->rangehigh = (108*16000);
+	v->rxsubchans = V4L2_TUNER_SUB_MONO;
+	v->capability = V4L2_TUNER_CAP_LOW;
+	v->audmode = V4L2_TUNER_MODE_MONO;
+	v->signal = 0xFFFF;     /* We can't get the signal strength */
+	return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+					struct v4l2_tuner *v)
+{
+	if (v->index > 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *f)
 {
 	struct video_device *dev = video_devdata(file);
 	struct typhoon_device *typhoon = dev->priv;
 
-	switch (cmd) {
-		case VIDIOC_QUERYCAP:
-		{
-			struct v4l2_capability *v = arg;
-			memset(v,0,sizeof(*v));
-			strlcpy(v->driver, "radio-typhoon", sizeof (v->driver));
-			strlcpy(v->card, "Typhoon Radio", sizeof (v->card));
-			sprintf(v->bus_info,"ISA");
-			v->version = RADIO_VERSION;
-			v->capabilities = V4L2_CAP_TUNER;
-
-			return 0;
-		}
-		case VIDIOC_G_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			memset(v,0,sizeof(*v));
-			strcpy(v->name, "FM");
-			v->type = V4L2_TUNER_RADIO;
-
-			v->rangelow=(87.5*16000);
-			v->rangehigh=(108*16000);
-			v->rxsubchans =V4L2_TUNER_SUB_MONO;
-			v->capability=V4L2_TUNER_CAP_LOW;
-			v->audmode = V4L2_TUNER_MODE_MONO;
-			v->signal = 0xFFFF;	/* We can't get the signal strength */
-
-			return 0;
-		}
-		case VIDIOC_S_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			return 0;
-		}
-		case VIDIOC_S_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			typhoon->curfreq = f->frequency;
-			typhoon_setfreq(typhoon, typhoon->curfreq);
-			return 0;
-		}
-		case VIDIOC_G_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			f->type = V4L2_TUNER_RADIO;
-			f->frequency = typhoon->curfreq;
-
-			return 0;
-		}
-		case VIDIOC_QUERYCTRL:
-		{
-			struct v4l2_queryctrl *qc = arg;
-			int i;
-
-			for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-				if (qc->id && qc->id == radio_qctrl[i].id) {
-					memcpy(qc, &(radio_qctrl[i]),
-								sizeof(*qc));
-					return (0);
-				}
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_G_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-					ctrl->value=typhoon->muted;
-					return (0);
-				case V4L2_CID_AUDIO_VOLUME:
-					ctrl->value=typhoon->curvol;
-					return (0);
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_S_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-					if (ctrl->value) {
-						typhoon_mute(typhoon);
-					} else {
-						typhoon_unmute(typhoon);
-					}
-					return (0);
-				case V4L2_CID_AUDIO_VOLUME:
-					typhoon_setvol(typhoon, ctrl->value);
-					return (0);
-			}
-			return -EINVAL;
-		}
-
-		default:
-			return v4l_compat_translate_ioctl(inode,file,cmd,arg,
-							  typhoon_do_ioctl);
-	}
+	typhoon->curfreq = f->frequency;
+	typhoon_setfreq(typhoon, typhoon->curfreq);
+	return 0;
 }
 
-static int typhoon_ioctl(struct inode *inode, struct file *file,
-			 unsigned int cmd, unsigned long arg)
+static int vidioc_g_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *f)
 {
-	return video_usercopy(inode, file, cmd, arg, typhoon_do_ioctl);
+	struct video_device *dev = video_devdata(file);
+	struct typhoon_device *typhoon = dev->priv;
+
+	f->type = V4L2_TUNER_RADIO;
+	f->frequency = typhoon->curfreq;
+
+	return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+					struct v4l2_queryctrl *qc)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+		if (qc->id && qc->id == radio_qctrl[i].id) {
+			memcpy(qc, &(radio_qctrl[i]),
+						sizeof(*qc));
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+					struct v4l2_control *ctrl)
+{
+	struct video_device *dev = video_devdata(file);
+	struct typhoon_device *typhoon = dev->priv;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		ctrl->value = typhoon->muted;
+		return 0;
+	case V4L2_CID_AUDIO_VOLUME:
+		ctrl->value = typhoon->curvol;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_s_ctrl (struct file *file, void *priv,
+					struct v4l2_control *ctrl)
+{
+	struct video_device *dev = video_devdata(file);
+	struct typhoon_device *typhoon = dev->priv;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		if (ctrl->value)
+			typhoon_mute(typhoon);
+		else
+			typhoon_unmute(typhoon);
+		return 0;
+	case V4L2_CID_AUDIO_VOLUME:
+		typhoon_setvol(typhoon, ctrl->value);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv,
+					struct v4l2_audio *a)
+{
+	if (a->index > 1)
+		return -EINVAL;
+
+	strcpy(a->name, "Radio");
+	a->capability = V4L2_AUDCAP_STEREO;
+	return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+	if (i != 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+					struct v4l2_audio *a)
+{
+	if (a->index != 0)
+		return -EINVAL;
+	return 0;
 }
 
 static struct typhoon_device typhoon_unit =
@@ -322,7 +339,7 @@
 	.owner		= THIS_MODULE,
 	.open           = video_exclusive_open,
 	.release        = video_exclusive_release,
-	.ioctl		= typhoon_ioctl,
+	.ioctl		= video_ioctl2,
 	.compat_ioctl	= v4l_compat_ioctl32,
 	.llseek         = no_llseek,
 };
@@ -334,6 +351,18 @@
 	.type		= VID_TYPE_TUNER,
 	.hardware	= 0,
 	.fops           = &typhoon_fops,
+	.vidioc_querycap    = vidioc_querycap,
+	.vidioc_g_tuner     = vidioc_g_tuner,
+	.vidioc_s_tuner     = vidioc_s_tuner,
+	.vidioc_g_audio     = vidioc_g_audio,
+	.vidioc_s_audio     = vidioc_s_audio,
+	.vidioc_g_input     = vidioc_g_input,
+	.vidioc_s_input     = vidioc_s_input,
+	.vidioc_g_frequency = vidioc_g_frequency,
+	.vidioc_s_frequency = vidioc_s_frequency,
+	.vidioc_queryctrl   = vidioc_queryctrl,
+	.vidioc_g_ctrl      = vidioc_g_ctrl,
+	.vidioc_s_ctrl      = vidioc_s_ctrl,
 };
 
 #ifdef CONFIG_RADIO_TYPHOON_PROC_FS
diff --git a/drivers/media/radio/radio-zoltrix.c b/drivers/media/radio/radio-zoltrix.c
index ec08491..a471590 100644
--- a/drivers/media/radio/radio-zoltrix.c
+++ b/drivers/media/radio/radio-zoltrix.c
@@ -230,121 +230,123 @@
 	return 0;
 }
 
-static int zol_do_ioctl(struct inode *inode, struct file *file,
-			unsigned int cmd, void *arg)
+static int vidioc_querycap(struct file *file, void  *priv,
+					struct v4l2_capability *v)
+{
+	strlcpy(v->driver, "radio-zoltrix", sizeof(v->driver));
+	strlcpy(v->card, "Zoltrix Radio", sizeof(v->card));
+	sprintf(v->bus_info, "ISA");
+	v->version = RADIO_VERSION;
+	v->capabilities = V4L2_CAP_TUNER;
+	return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+					struct v4l2_tuner *v)
 {
 	struct video_device *dev = video_devdata(file);
 	struct zol_device *zol = dev->priv;
 
-	switch (cmd) {
-		case VIDIOC_QUERYCAP:
-		{
-			struct v4l2_capability *v = arg;
-			memset(v,0,sizeof(*v));
-			strlcpy(v->driver, "radio-zoltrix", sizeof (v->driver));
-			strlcpy(v->card, "Zoltrix Radio", sizeof (v->card));
-			sprintf(v->bus_info,"ISA");
-			v->version = RADIO_VERSION;
-			v->capabilities = V4L2_CAP_TUNER;
+	if (v->index > 0)
+		return -EINVAL;
 
+	strcpy(v->name, "FM");
+	v->type = V4L2_TUNER_RADIO;
+	v->rangelow = (88*16000);
+	v->rangehigh = (108*16000);
+	v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
+	v->capability = V4L2_TUNER_CAP_LOW;
+	if (zol_is_stereo(zol))
+		v->audmode = V4L2_TUNER_MODE_STEREO;
+	else
+		v->audmode = V4L2_TUNER_MODE_MONO;
+	v->signal = 0xFFFF*zol_getsigstr(zol);
+	return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+					struct v4l2_tuner *v)
+{
+	if (v->index > 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *f)
+{
+	struct video_device *dev = video_devdata(file);
+	struct zol_device *zol = dev->priv;
+
+	zol->curfreq = f->frequency;
+	zol_setfreq(zol, zol->curfreq);
+	return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *f)
+{
+	struct video_device *dev = video_devdata(file);
+	struct zol_device *zol = dev->priv;
+
+	f->type = V4L2_TUNER_RADIO;
+	f->frequency = zol->curfreq;
+	return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+					struct v4l2_queryctrl *qc)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+		if (qc->id && qc->id == radio_qctrl[i].id) {
+			memcpy(qc, &(radio_qctrl[i]),
+						sizeof(*qc));
 			return 0;
 		}
-		case VIDIOC_G_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
+	}
+	return -EINVAL;
+}
 
-			if (v->index > 0)
-				return -EINVAL;
+static int vidioc_g_ctrl(struct file *file, void *priv,
+				struct v4l2_control *ctrl)
+{
+	struct video_device *dev = video_devdata(file);
+	struct zol_device *zol = dev->priv;
 
-			memset(v,0,sizeof(*v));
-			strcpy(v->name, "FM");
-			v->type = V4L2_TUNER_RADIO;
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		ctrl->value = zol->muted;
+		return 0;
+	case V4L2_CID_AUDIO_VOLUME:
+		ctrl->value = zol->curvol * 4096;
+		return 0;
+	}
+	return -EINVAL;
+}
 
-			v->rangelow=(88*16000);
-			v->rangehigh=(108*16000);
-			v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
-			v->capability=V4L2_TUNER_CAP_LOW;
-			if(zol_is_stereo(zol))
-				v->audmode = V4L2_TUNER_MODE_STEREO;
-			else
-				v->audmode = V4L2_TUNER_MODE_MONO;
-			v->signal=0xFFFF*zol_getsigstr(zol);
+static int vidioc_s_ctrl(struct file *file, void *priv,
+				struct v4l2_control *ctrl)
+{
+	struct video_device *dev = video_devdata(file);
+	struct zol_device *zol = dev->priv;
 
-			return 0;
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		if (ctrl->value)
+			zol_mute(zol);
+		else {
+			zol_unmute(zol);
+			zol_setvol(zol,zol->curvol);
 		}
-		case VIDIOC_S_TUNER:
-		{
-			struct v4l2_tuner *v = arg;
-
-			if (v->index > 0)
-				return -EINVAL;
-
-			return 0;
-		}
-		case VIDIOC_S_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			zol->curfreq = f->frequency;
-			zol_setfreq(zol, zol->curfreq);
-			return 0;
-		}
-		case VIDIOC_G_FREQUENCY:
-		{
-			struct v4l2_frequency *f = arg;
-
-			f->type = V4L2_TUNER_RADIO;
-			f->frequency = zol->curfreq;
-
-			return 0;
-		}
-		case VIDIOC_QUERYCTRL:
-		{
-			struct v4l2_queryctrl *qc = arg;
-			int i;
-
-			for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-				if (qc->id && qc->id == radio_qctrl[i].id) {
-					memcpy(qc, &(radio_qctrl[i]),
-								sizeof(*qc));
-					return (0);
-				}
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_G_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-					ctrl->value=zol->muted;
-					return (0);
-				case V4L2_CID_AUDIO_VOLUME:
-					ctrl->value=zol->curvol * 4096;
-					return (0);
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_S_CTRL:
-		{
-			struct v4l2_control *ctrl= arg;
-
-			switch (ctrl->id) {
-				case V4L2_CID_AUDIO_MUTE:
-					if (ctrl->value) {
-						zol_mute(zol);
-					} else {
-						zol_unmute(zol);
-						zol_setvol(zol,zol->curvol);
-					}
-					return (0);
-				case V4L2_CID_AUDIO_VOLUME:
-					zol_setvol(zol,ctrl->value/4096);
-					return (0);
-			}
-			zol->stereo = 1;
-			zol_setfreq(zol, zol->curfreq);
+		return 0;
+	case V4L2_CID_AUDIO_VOLUME:
+		zol_setvol(zol,ctrl->value/4096);
+		return 0;
+	}
+	zol->stereo = 1;
+	zol_setfreq(zol, zol->curfreq);
 #if 0
 /* FIXME: Implement stereo/mono switch on V4L2 */
 			if (v->mode & VIDEO_SOUND_STEREO) {
@@ -356,19 +358,39 @@
 				zol_setfreq(zol, zol->curfreq);
 			}
 #endif
-			return -EINVAL;
-		}
-
-		default:
-			return v4l_compat_translate_ioctl(inode,file,cmd,arg,
-							  zol_do_ioctl);
-	}
+	return -EINVAL;
 }
 
-static int zol_ioctl(struct inode *inode, struct file *file,
-		     unsigned int cmd, unsigned long arg)
+static int vidioc_g_audio(struct file *file, void *priv,
+					struct v4l2_audio *a)
 {
-	return video_usercopy(inode, file, cmd, arg, zol_do_ioctl);
+	if (a->index > 1)
+		return -EINVAL;
+
+	strcpy(a->name, "Radio");
+	a->capability = V4L2_AUDCAP_STEREO;
+	return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+	if (i != 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+					struct v4l2_audio *a)
+{
+	if (a->index != 0)
+		return -EINVAL;
+	return 0;
 }
 
 static struct zol_device zoltrix_unit;
@@ -378,7 +400,7 @@
 	.owner		= THIS_MODULE,
 	.open           = video_exclusive_open,
 	.release        = video_exclusive_release,
-	.ioctl		= zol_ioctl,
+	.ioctl		= video_ioctl2,
 	.compat_ioctl	= v4l_compat_ioctl32,
 	.llseek         = no_llseek,
 };
@@ -390,6 +412,18 @@
 	.type		= VID_TYPE_TUNER,
 	.hardware	= 0,
 	.fops           = &zoltrix_fops,
+	.vidioc_querycap    = vidioc_querycap,
+	.vidioc_g_tuner     = vidioc_g_tuner,
+	.vidioc_s_tuner     = vidioc_s_tuner,
+	.vidioc_g_audio     = vidioc_g_audio,
+	.vidioc_s_audio     = vidioc_s_audio,
+	.vidioc_g_input     = vidioc_g_input,
+	.vidioc_s_input     = vidioc_s_input,
+	.vidioc_g_frequency = vidioc_g_frequency,
+	.vidioc_s_frequency = vidioc_s_frequency,
+	.vidioc_queryctrl   = vidioc_queryctrl,
+	.vidioc_g_ctrl      = vidioc_g_ctrl,
+	.vidioc_s_ctrl      = vidioc_s_ctrl,
 };
 
 static int __init zoltrix_init(void)
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 7a61051..bc77378 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -577,14 +577,14 @@
 
 config VIDEO_MEYE
 	tristate "Sony Vaio Picturebook Motion Eye Video For Linux"
-	depends on PCI && SONYPI && VIDEO_V4L1
+	depends on PCI && SONY_LAPTOP && VIDEO_V4L1
 	---help---
 	  This is the video4linux driver for the Motion Eye camera found
 	  in the Vaio Picturebook laptops. Please read the material in
 	  <file:Documentation/video4linux/meye.txt> for more information.
 
-	  If you say Y or M here, you need to say Y or M to "Sony Programmable
-	  I/O Control Device" in the character device section.
+	  If you say Y or M here, you need to say Y or M to "Sony Laptop
+	  Extras" in the misc device section.
 
 	  To compile this driver as a module, choose M here: the
 	  module will be called meye.
@@ -647,6 +647,8 @@
 
 source "drivers/media/video/cx88/Kconfig"
 
+source "drivers/media/video/ivtv/Kconfig"
+
 config VIDEO_M32R_AR
 	tristate "AR devices"
 	depends on M32R && VIDEO_V4L1
@@ -761,6 +763,18 @@
 
 source "drivers/media/video/pwc/Kconfig"
 
+config USB_ZR364XX
+	tristate "USB ZR364XX Camera support"
+	depends on USB && VIDEO_V4L2
+	---help---
+	  Say Y here if you want to connect this type of camera to your
+	  computer's USB port.
+	  See <file:Documentation/video4linux/zr364xx.txt> for more info
+	  and list of supported cameras.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called zr364xx.
+
 endmenu # V4L USB devices
 
 endmenu
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 44ccaed..9c2de50 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -61,6 +61,7 @@
 obj-$(CONFIG_VIDEO_MEYE) += meye.o
 obj-$(CONFIG_VIDEO_SAA7134) += ir-kbd-i2c.o saa7134/
 obj-$(CONFIG_VIDEO_CX88) += cx88/
+obj-$(CONFIG_VIDEO_IVTV) += ivtv/
 obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
 obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
 obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
@@ -99,6 +100,7 @@
 obj-$(CONFIG_USB_SE401)         += se401.o
 obj-$(CONFIG_USB_STV680)        += stv680.o
 obj-$(CONFIG_USB_W9968CF)       += w9968cf.o
+obj-$(CONFIG_USB_ZR364XX)       += zr364xx.o
 
 obj-$(CONFIG_USB_SN9C102)       += sn9c102/
 obj-$(CONFIG_USB_ET61X251)      += et61x251/
diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c
index 6addc42..6b31e50 100644
--- a/drivers/media/video/bt8xx/bttv-cards.c
+++ b/drivers/media/video/bt8xx/bttv-cards.c
@@ -291,6 +291,9 @@
 
 	{ 0x15409511, BTTV_BOARD_ACORP_Y878F, "Acorp Y878F" },
 
+	{ 0x53534149, BTTV_BOARD_SSAI_SECURITY, "SSAI Security Video Interface" },
+	{ 0x5353414a, BTTV_BOARD_SSAI_ULTRASOUND, "SSAI Ultrasound Video Interface" },
+
 	/* likely broken, vendor id doesn't match the other magic views ...
 	 * { 0xa0fca04f, BTTV_BOARD_MAGICTVIEW063, "Guillemot Maxi TV Video 3" }, */
 
@@ -2907,6 +2910,28 @@
 		.has_radio      = 1,
 		.has_remote     = 1,
 	},
+	[BTTV_BOARD_SSAI_SECURITY] = {
+		.name		= "SSAI Security Video Interface",
+		.video_inputs	= 4,
+		.audio_inputs	= 0,
+		.tuner		= -1,
+		.svhs		= -1,
+		.muxsel		= { 0, 1, 2, 3 },
+		.tuner_type	= -1,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+	},
+	[BTTV_BOARD_SSAI_ULTRASOUND] = {
+		.name		= "SSAI Ultrasound Video Interface",
+		.video_inputs	= 2,
+		.audio_inputs	= 0,
+		.tuner		= -1,
+		.svhs		= 1,
+		.muxsel		= { 2, 0, 1, 3 },
+		.tuner_type	= -1,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+	},
 };
 
 static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards);
@@ -2970,20 +2995,20 @@
 
 	if (UNSET != audiomux[0]) {
 		gpiobits = 0;
-		for (i = 0; i < 4; i++) {
+		for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) {
 			bttv_tvcards[btv->c.type].gpiomux[i] = audiomux[i];
 			gpiobits |= audiomux[i];
 		}
 	} else {
 		gpiobits = audioall;
-		for (i = 0; i < 4; i++) {
+		for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) {
 			bttv_tvcards[btv->c.type].gpiomux[i] = audioall;
 		}
 	}
 	bttv_tvcards[btv->c.type].gpiomask = (UNSET != gpiomask) ? gpiomask : gpiobits;
 	printk(KERN_INFO "bttv%d: gpio config override: mask=0x%x, mux=",
 	       btv->c.nr,bttv_tvcards[btv->c.type].gpiomask);
-	for (i = 0; i < 5; i++) {
+	for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) {
 		printk("%s0x%x", i ? "," : "", bttv_tvcards[btv->c.type].gpiomux[i]);
 	}
 	printk("\n");
@@ -3638,7 +3663,7 @@
 
 	for (n = 0; n < microlen; n++) {
 		bits = micro[n];
-		for ( i = 0 ; i < 8 ; i++ ) {
+		for (i = 0 ; i < 8 ; i++) {
 			gpio_bits(BTTV_ALT_DCLK,0);
 			if (bits & 0x01)
 				gpio_bits(BTTV_ALT_DATA,BTTV_ALT_DATA);
@@ -3691,7 +3716,7 @@
 	       /* this might be an antique... check for MMAC label in eeprom */
 	       if ((ee[0]=='M') && (ee[1]=='M') && (ee[2]=='A') && (ee[3]=='C')) {
 		       unsigned char checksum = 0;
-		       for (i =0; i<21; i++)
+		       for (i = 0; i < 21; i++)
 			       checksum += ee[i];
 		       if (checksum != ee[21])
 			       return;
@@ -3703,12 +3728,13 @@
 	       unsigned short type;
 	       int offset = 4*16;
 
-	       for(; offset < 8*16; offset += 16) {
+	       for (; offset < 8*16; offset += 16) {
 		       unsigned short checksum = 0;
 		       /* verify the checksum */
-		       for(i = 0; i<14; i++) checksum += ee[i+offset];
-			       checksum = ~checksum;  /* no idea why */
-			       if ((((checksum>>8)&0x0FF) == ee[offset+14]) &&
+		       for (i = 0; i < 14; i++)
+				checksum += ee[i+offset];
+			checksum = ~checksum;  /* no idea why */
+			if ((((checksum>>8)&0x0FF) == ee[offset+14]) &&
 				   ((checksum & 0x0FF) == ee[offset+15])) {
 			       break;
 		       }
@@ -3721,7 +3747,6 @@
 	       type = (ee[offset+4]<<8) | (ee[offset+5]);
 
 	       switch(type) {
-
 	       /* 848 based */
 	       case 0x0004:
 		       btv->c.type = BTTV_BOARD_OSPREY1x0_848;
@@ -4149,8 +4174,7 @@
 	}
 
 	dprintk("bttv%d: tea5757:",btv->c.nr);
-	for(i = 0; i < 24; i++)
-	{
+	for (i = 0; i < 24; i++) {
 		udelay(5);
 		bus_high(btv,btv->mbox_clk);
 		udelay(5);
@@ -4182,8 +4206,7 @@
 	dprintk("bttv%d: tea5757: write 0x%X\n", btv->c.nr, value);
 	bus_low(btv,btv->mbox_clk);
 	bus_high(btv,btv->mbox_we);
-	for(i = 0; i < 25; i++)
-	{
+	for (i = 0; i < 25; i++) {
 		if (reg & 0x1000000)
 			bus_high(btv,btv->mbox_data);
 		else
@@ -4755,7 +4778,7 @@
 	gpio_write(1 << 9);	/* reset MUX */
 	gpio_write(0);
 	/* Preset camera 0 to the 4 controllers */
-	for (ix=0; ix<4; ix++) {
+	for (ix = 0; ix < 4; ix++) {
 		sw_status[ix] = ix;
 		kodicom4400r_write(btv, ix, ix, 1);
 	}
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
index 5720b77..1c38723 100644
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -164,6 +164,24 @@
 static CLASS_DEVICE_ATTR(card, S_IRUGO, show_card, NULL);
 
 /* ----------------------------------------------------------------------- */
+/* dvb auto-load setup                                                     */
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+	request_module("dvb-bt8xx");
+}
+
+static void request_modules(struct bttv *dev)
+{
+	INIT_WORK(&dev->request_module_wk, request_module_async);
+	schedule_work(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+
+/* ----------------------------------------------------------------------- */
 /* static data                                                             */
 
 /* special timing tables from conexant... */
@@ -4769,9 +4787,11 @@
 		disclaim_video_lines(btv);
 	}
 
-	/* add subdevices */
-	if (bttv_tvcards[btv->c.type].has_dvb)
+	/* add subdevices and autoload dvb-bt8xx if needed */
+	if (bttv_tvcards[btv->c.type].has_dvb) {
 		bttv_sub_add_device(&btv->c, "dvb");
+		request_modules(btv);
+	}
 
 	bttv_input_init(btv);
 
diff --git a/drivers/media/video/bt8xx/bttv-gpio.c b/drivers/media/video/bt8xx/bttv-gpio.c
index ba081f6..84154c2 100644
--- a/drivers/media/video/bt8xx/bttv-gpio.c
+++ b/drivers/media/video/bt8xx/bttv-gpio.c
@@ -71,7 +71,6 @@
 	.probe  = bttv_sub_probe,
 	.remove = bttv_sub_remove,
 };
-EXPORT_SYMBOL(bttv_sub_bus_type);
 
 static void release_sub_device(struct device *dev)
 {
@@ -152,7 +151,6 @@
 	btwrite(data,BT848_GPIO_OUT_EN);
 	spin_unlock_irqrestore(&btv->gpio_lock,flags);
 }
-EXPORT_SYMBOL(bttv_gpio_inout);
 
 u32 bttv_gpio_read(struct bttv_core *core)
 {
@@ -162,7 +160,6 @@
 	value = btread(BT848_GPIO_DATA);
 	return value;
 }
-EXPORT_SYMBOL(bttv_gpio_read);
 
 void bttv_gpio_write(struct bttv_core *core, u32 value)
 {
@@ -170,7 +167,6 @@
 
 	btwrite(value,BT848_GPIO_DATA);
 }
-EXPORT_SYMBOL(bttv_gpio_write);
 
 void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits)
 {
@@ -185,7 +181,6 @@
 	btwrite(data,BT848_GPIO_DATA);
 	spin_unlock_irqrestore(&btv->gpio_lock,flags);
 }
-EXPORT_SYMBOL(bttv_gpio_bits);
 
 /*
  * Local variables:
diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c
index 62b8730..0dfa49b 100644
--- a/drivers/media/video/bt8xx/bttv-i2c.c
+++ b/drivers/media/video/bt8xx/bttv-i2c.c
@@ -412,7 +412,7 @@
 	unsigned char buf;
 	int i,rc;
 
-	for (i = 0; i < 128; i++) {
+	for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
 		c->addr = i;
 		rc = i2c_master_recv(c,&buf,0);
 		if (rc < 0)
diff --git a/drivers/media/video/bt8xx/bttv-if.c b/drivers/media/video/bt8xx/bttv-if.c
index 19b564a..ecf0798 100644
--- a/drivers/media/video/bt8xx/bttv-if.c
+++ b/drivers/media/video/bt8xx/bttv-if.c
@@ -33,32 +33,16 @@
 
 #include "bttvp.h"
 
-EXPORT_SYMBOL(bttv_get_cardinfo);
 EXPORT_SYMBOL(bttv_get_pcidev);
-EXPORT_SYMBOL(bttv_get_id);
 EXPORT_SYMBOL(bttv_gpio_enable);
 EXPORT_SYMBOL(bttv_read_gpio);
 EXPORT_SYMBOL(bttv_write_gpio);
-EXPORT_SYMBOL(bttv_get_gpio_queue);
-EXPORT_SYMBOL(bttv_i2c_call);
 
 /* ----------------------------------------------------------------------- */
 /* Exported functions - for other modules which want to access the         */
 /*                      gpio ports (IR for example)                        */
 /*                      see bttv.h for comments                            */
 
-int bttv_get_cardinfo(unsigned int card, int *type, unsigned *cardid)
-{
-	printk("The bttv_* interface is obsolete and will go away,\n"
-	       "please use the new, sysfs based interface instead.\n");
-	if (card >= bttv_num) {
-		return -1;
-	}
-	*type   = bttvs[card].c.type;
-	*cardid = bttvs[card].cardid;
-	return 0;
-}
-
 struct pci_dev* bttv_get_pcidev(unsigned int card)
 {
 	if (card >= bttv_num)
@@ -66,16 +50,6 @@
 	return bttvs[card].c.pci;
 }
 
-int bttv_get_id(unsigned int card)
-{
-	printk("The bttv_* interface is obsolete and will go away,\n"
-	       "please use the new, sysfs based interface instead.\n");
-	if (card >= bttv_num) {
-		return -1;
-	}
-	return bttvs[card].c.type;
-}
-
 
 int bttv_gpio_enable(unsigned int card, unsigned long mask, unsigned long data)
 {
@@ -130,28 +104,6 @@
 	return 0;
 }
 
-wait_queue_head_t* bttv_get_gpio_queue(unsigned int card)
-{
-	struct bttv *btv;
-
-	if (card >= bttv_num) {
-		return NULL;
-	}
-
-	btv = &bttvs[card];
-	if (bttvs[card].shutdown) {
-		return NULL;
-	}
-	return &btv->gpioq;
-}
-
-void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg)
-{
-	if (card >= bttv_num)
-		return;
-	bttv_call_i2c_clients(&bttvs[card], cmd, arg);
-}
-
 /*
  * Local variables:
  * c-basic-offset: 8
diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h
index 5491acb..f821ba6 100644
--- a/drivers/media/video/bt8xx/bttv.h
+++ b/drivers/media/video/bt8xx/bttv.h
@@ -168,6 +168,8 @@
 #define BTTV_BOARD_SABRENT_TVFM   	   0x8e
 #define BTTV_BOARD_HAUPPAUGE_IMPACTVCB     0x8f
 #define BTTV_BOARD_MACHTV_MAGICTV          0x90
+#define BTTV_BOARD_SSAI_SECURITY	   0x91
+#define BTTV_BOARD_SSAI_ULTRASOUND	   0x92
 
 /* more card-specific defines */
 #define PT2254_L_CHANNEL 0x10
@@ -260,17 +262,8 @@
 /* this obsolete -- please use the sysfs-based
    interface below for new code */
 
-/* returns card type + card ID (for bt878-based ones)
-   for possible values see lines below beginning with #define BTTV_BOARD_UNKNOWN
-   returns negative value if error occurred
-*/
-extern int bttv_get_cardinfo(unsigned int card, int *type,
-			     unsigned int *cardid);
 extern struct pci_dev* bttv_get_pcidev(unsigned int card);
 
-/* obsolete, use bttv_get_cardinfo instead */
-extern int bttv_get_id(unsigned int card);
-
 /* sets GPOE register (BT848_GPIO_OUT_EN) to new value:
    data | (current_GPOE_value & ~mask)
    returns negative value if error occurred
@@ -290,20 +283,6 @@
 extern int bttv_write_gpio(unsigned int card,
 			   unsigned long mask, unsigned long data);
 
-/* returns pointer to task queue which can be used as parameter to
-   interruptible_sleep_on
-   in interrupt handler if BT848_INT_GPINT bit is set - this queue is activated
-   (wake_up_interruptible) and following call to the function bttv_read_gpio
-   should return new value of GPDATA,
-   returns NULL value if error occurred or queue is not available
-   WARNING: because there is no buffer for GPIO data, one MUST
-   process data ASAP
-*/
-extern wait_queue_head_t* bttv_get_gpio_queue(unsigned int card);
-
-/* call i2c clients
-*/
-extern void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg);
 
 
 
diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h
index ad79b8d..8f44f02 100644
--- a/drivers/media/video/bt8xx/bttvp.h
+++ b/drivers/media/video/bt8xx/bttvp.h
@@ -434,6 +434,9 @@
 	unsigned int users;
 	struct bttv_fh init;
 
+	/* used to make dvb-bt8xx autoloadable */
+	struct work_struct request_module_wk;
+
 	/* Default (0) and current (1) video capturing and overlay
 	   cropping parameters in bttv_tvnorm.cropcap units. Protected
 	   by bttv.lock. */
diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c
index 710c11a..96254db 100644
--- a/drivers/media/video/cafe_ccic.c
+++ b/drivers/media/video/cafe_ccic.c
@@ -4,6 +4,7 @@
  * sensor.
  *
  * Copyright 2006 One Laptop Per Child Association, Inc.
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
  *
  * Written by Jonathan Corbet, corbet@lwn.net.
  *
@@ -22,6 +23,7 @@
 #include <linux/spinlock.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
 #include <linux/device.h>
 #include <linux/wait.h>
 #include <linux/list.h>
@@ -36,7 +38,7 @@
 
 #include "cafe_ccic-regs.h"
 
-#define CAFE_VERSION 0x000001
+#define CAFE_VERSION 0x000002
 
 
 /*
@@ -164,7 +166,7 @@
 	struct tasklet_struct s_tasklet;
 
 	/* Current operating parameters */
-	enum v4l2_chip_ident sensor_type;		/* Currently ov7670 only */
+	u32 sensor_type;		/* Currently ov7670 only */
 	struct v4l2_pix_format pix_format;
 
 	/* Locks */
@@ -704,7 +706,13 @@
 	cafe_reg_write(cam, REG_GL_CSR, GCSR_SRS|GCSR_MRS); /* Needed? */
 	cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRC);
 	cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRS);
+	/*
+	 * Here we must wait a bit for the controller to come around.
+	 */
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
 	mdelay(5);	/* FIXME revisit this */
+	spin_lock_irqsave(&cam->dev_lock, flags);
+
 	cafe_reg_write(cam, REG_GL_CSR, GCSR_CCIC_EN|GCSR_SRC|GCSR_MRC);
 	cafe_reg_set_bit(cam, REG_GL_IMASK, GIMSK_CCIC_EN);
 	/*
@@ -772,9 +780,9 @@
 	 * Control 1 is power down, set to 0 to operate.
 	 */
 	cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */
-	mdelay(1); /* Marvell says 1ms will do it */
+//	mdelay(1); /* Marvell says 1ms will do it */
 	cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0);
-	mdelay(1); /* Enough? */
+//	mdelay(1); /* Enough? */
 	spin_unlock_irqrestore(&cam->dev_lock, flags);
 }
 
@@ -818,6 +826,7 @@
  */
 static int cafe_cam_init(struct cafe_camera *cam)
 {
+	struct v4l2_chip_ident chip = { V4L2_CHIP_MATCH_I2C_ADDR, 0, 0, 0 };
 	int ret;
 
 	mutex_lock(&cam->s_mutex);
@@ -827,9 +836,11 @@
 	ret = __cafe_cam_reset(cam);
 	if (ret)
 		goto out;
-	ret = __cafe_cam_cmd(cam, VIDIOC_INT_G_CHIP_IDENT, &cam->sensor_type);
+	chip.match_chip = cam->sensor->addr;
+	ret = __cafe_cam_cmd(cam, VIDIOC_G_CHIP_IDENT, &chip);
 	if (ret)
 		goto out;
+	cam->sensor_type = chip.ident;
 //	if (cam->sensor->addr != OV7xx0_SID) {
 	if (cam->sensor_type != V4L2_IDENT_OV7670) {
 		cam_err(cam, "Unsupported sensor type %d", cam->sensor->addr);
@@ -1792,18 +1803,19 @@
 		if (list_empty(&cam->sb_avail))
 			break;  /* Leave it valid, hope for better later */
 		clear_bit(bufno, &cam->flags);
-		/*
-		 * We could perhaps drop the spinlock during this
-		 * big copy.  Something to consider.
-		 */
 		sbuf = list_entry(cam->sb_avail.next,
 				struct cafe_sio_buffer, list);
+		/*
+		 * Drop the lock during the big copy.  This *should* be safe...
+		 */
+		spin_unlock_irqrestore(&cam->dev_lock, flags);
 		memcpy(sbuf->buffer, cam->dma_bufs[bufno],
 				cam->pix_format.sizeimage);
 		sbuf->v4lbuf.bytesused = cam->pix_format.sizeimage;
 		sbuf->v4lbuf.sequence = cam->buf_seq[bufno];
 		sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED;
 		sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE;
+		spin_lock_irqsave(&cam->dev_lock, flags);
 		list_move_tail(&sbuf->list, &cam->sb_full);
 	}
 	if (! list_empty(&cam->sb_full))
@@ -2107,6 +2119,7 @@
 	cam->v4ldev = cafe_v4l_template;
 	cam->v4ldev.debug = 0;
 //	cam->v4ldev.debug = V4L2_DEBUG_IOCTL_ARG;
+	cam->v4ldev.dev = &pdev->dev;
 	ret = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER, -1);
 	if (ret)
 		goto out_smbus;
@@ -2176,10 +2189,52 @@
 }
 
 
+#ifdef CONFIG_PM
+/*
+ * Basic power management.
+ */
+static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct cafe_camera *cam = cafe_find_by_pdev(pdev);
+	int ret;
+
+	ret = pci_save_state(pdev);
+	if (ret)
+		return ret;
+	cafe_ctlr_stop_dma(cam);
+	cafe_ctlr_power_down(cam);
+	pci_disable_device(pdev);
+	return 0;
+}
+
+
+static int cafe_pci_resume(struct pci_dev *pdev)
+{
+	struct cafe_camera *cam = cafe_find_by_pdev(pdev);
+	int ret = 0;
+
+	ret = pci_restore_state(pdev);
+	if (ret)
+		return ret;
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		cam_warn(cam, "Unable to re-enable device on resume!\n");
+		return ret;
+	}
+	cafe_ctlr_init(cam);
+	cafe_ctlr_power_up(cam);
+	set_bit(CF_CONFIG_NEEDED, &cam->flags);
+	if (cam->state == S_SPECREAD)
+		cam->state = S_IDLE;  /* Don't bother restarting */
+	else if (cam->state == S_SINGLEREAD || cam->state == S_STREAMING)
+		ret = cafe_read_setup(cam, cam->state);
+	return ret;
+}
+
+#endif  /* CONFIG_PM */
 
 
 static struct pci_device_id cafe_ids[] = {
-	{ PCI_DEVICE(0x1148, 0x4340) }, /* Temporary ID on devel board */
 	{ PCI_DEVICE(0x11ab, 0x4100) }, /* Eventual real ID */
 	{ PCI_DEVICE(0x11ab, 0x4102) }, /* Really eventual real ID */
 	{ 0, }
@@ -2192,6 +2247,10 @@
 	.id_table = cafe_ids,
 	.probe = cafe_pci_probe,
 	.remove = cafe_pci_remove,
+#ifdef CONFIG_PM
+	.suspend = cafe_pci_suspend,
+	.resume = cafe_pci_resume,
+#endif
 };
 
 
diff --git a/drivers/media/video/cpia_pp.c b/drivers/media/video/cpia_pp.c
index b12cec9..19711aa 100644
--- a/drivers/media/video/cpia_pp.c
+++ b/drivers/media/video/cpia_pp.c
@@ -62,7 +62,6 @@
 #define PPCPIA_PARPORT_OFF -2
 #define PPCPIA_PARPORT_NONE -1
 
-#ifdef MODULE
 static int parport_nr[PARPORT_MAX] = {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC};
 static char *parport[PARPORT_MAX] = {NULL,};
 
@@ -72,11 +71,6 @@
 
 module_param_array(parport, charp, NULL, 0);
 MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp.");
-#else
-static int parport_nr[PARPORT_MAX] __initdata =
-	{[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC};
-static int parport_ptr = 0;
-#endif
 
 struct pp_cam_entry {
 	struct pardevice *pdev;
@@ -141,7 +135,6 @@
 	cam = container_of(work, struct pp_cam_entry, cb_task);
 	cb_func = cam->cb_func;
 	cb_data = cam->cb_data;
-	work_release(work);
 
 	cb_func(cb_data);
 }
@@ -682,7 +675,7 @@
 	if(cam->port->irq != PARPORT_IRQ_NONE) {
 		cam->cb_func = cb;
 		cam->cb_data = cbdata;
-		INIT_WORK_NAR(&cam->cb_task, cpia_pp_run_callback);
+		INIT_WORK(&cam->cb_task, cpia_pp_run_callback);
 	} else {
 		retval = -1;
 	}
@@ -820,7 +813,7 @@
 	.detach = cpia_pp_detach,
 };
 
-static int cpia_pp_init(void)
+static int __init cpia_pp_init(void)
 {
 	printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT,
 	       CPIA_PP_MAJ_VER,CPIA_PP_MIN_VER,CPIA_PP_PATCH_VER);
@@ -839,8 +832,7 @@
 	return 0;
 }
 
-#ifdef MODULE
-int init_module(void)
+static int __init cpia_init(void)
 {
 	if (parport[0]) {
 		/* The user gave some parameters.  Let's see what they were. */
@@ -867,38 +859,11 @@
 	return cpia_pp_init();
 }
 
-void cleanup_module(void)
+static void __exit cpia_cleanup(void)
 {
-	parport_unregister_driver (&cpia_pp_driver);
+	parport_unregister_driver(&cpia_pp_driver);
 	return;
 }
 
-#else /* !MODULE */
-
-static int __init cpia_pp_setup(char *str)
-{
-	int err;
-
-	if (!strncmp(str, "parport", 7)) {
-		int n = simple_strtoul(str + 7, NULL, 10);
-		if (parport_ptr < PARPORT_MAX) {
-			parport_nr[parport_ptr++] = n;
-		} else {
-			LOG("too many ports, %s ignored.\n", str);
-		}
-	} else if (!strcmp(str, "auto")) {
-		parport_nr[0] = PPCPIA_PARPORT_AUTO;
-	} else if (!strcmp(str, "none")) {
-		parport_nr[parport_ptr++] = PPCPIA_PARPORT_NONE;
-	}
-
-	err=cpia_pp_init();
-	if (err)
-		return err;
-
-	return 1;
-}
-
-__setup("cpia_pp=", cpia_pp_setup);
-
-#endif /* !MODULE */
+module_init(cpia_init);
+module_exit(cpia_cleanup);
diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c
index de87247..a73e285 100644
--- a/drivers/media/video/cs53l32a.c
+++ b/drivers/media/video/cs53l32a.c
@@ -28,6 +28,7 @@
 #include <linux/i2c-id.h>
 #include <linux/videodev.h>
 #include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
 
 MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC");
 MODULE_AUTHOR("Martin Vaughan");
@@ -103,6 +104,9 @@
 		cs53l32a_write(client, 0x05, (u8) ctrl->value);
 		break;
 
+	case VIDIOC_G_CHIP_IDENT:
+		return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_CS53l32A, 0);
+
 	case VIDIOC_LOG_STATUS:
 		{
 			u8 v = cs53l32a_read(client, 0x01);
diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c
index d60cd5e..88dbddd 100644
--- a/drivers/media/video/cx2341x.c
+++ b/drivers/media/video/cx2341x.c
@@ -51,6 +51,7 @@
 	V4L2_CID_MPEG_AUDIO_MODE_EXTENSION,
 	V4L2_CID_MPEG_AUDIO_EMPHASIS,
 	V4L2_CID_MPEG_AUDIO_CRC,
+	V4L2_CID_MPEG_AUDIO_MUTE,
 	V4L2_CID_MPEG_VIDEO_ENCODING,
 	V4L2_CID_MPEG_VIDEO_ASPECT,
 	V4L2_CID_MPEG_VIDEO_B_FRAMES,
@@ -60,6 +61,8 @@
 	V4L2_CID_MPEG_VIDEO_BITRATE,
 	V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
 	V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION,
+	V4L2_CID_MPEG_VIDEO_MUTE,
+	V4L2_CID_MPEG_VIDEO_MUTE_YUV,
 	V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE,
 	V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
 	V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE,
@@ -71,6 +74,7 @@
 	V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP,
 	V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM,
 	V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP,
+	V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS,
 	0
 };
 
@@ -102,6 +106,9 @@
 	case V4L2_CID_MPEG_AUDIO_CRC:
 		ctrl->value = params->audio_crc;
 		break;
+	case V4L2_CID_MPEG_AUDIO_MUTE:
+		ctrl->value = params->audio_mute;
+		break;
 	case V4L2_CID_MPEG_VIDEO_ENCODING:
 		ctrl->value = params->video_encoding;
 		break;
@@ -129,6 +136,12 @@
 	case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
 		ctrl->value = params->video_temporal_decimation;
 		break;
+	case V4L2_CID_MPEG_VIDEO_MUTE:
+		ctrl->value = params->video_mute;
+		break;
+	case V4L2_CID_MPEG_VIDEO_MUTE_YUV:
+		ctrl->value = params->video_mute_yuv;
+		break;
 	case V4L2_CID_MPEG_STREAM_TYPE:
 		ctrl->value = params->stream_type;
 		break;
@@ -168,6 +181,9 @@
 	case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
 		ctrl->value = params->video_chroma_median_filter_bottom;
 		break;
+	case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+		ctrl->value = params->stream_insert_nav_packets;
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -201,6 +217,9 @@
 	case V4L2_CID_MPEG_AUDIO_CRC:
 		params->audio_crc = ctrl->value;
 		break;
+	case V4L2_CID_MPEG_AUDIO_MUTE:
+		params->audio_mute = ctrl->value;
+		break;
 	case V4L2_CID_MPEG_VIDEO_ASPECT:
 		params->video_aspect = ctrl->value;
 		break;
@@ -243,6 +262,12 @@
 	case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
 		params->video_temporal_decimation = ctrl->value;
 		break;
+	case V4L2_CID_MPEG_VIDEO_MUTE:
+		params->video_mute = (ctrl->value != 0);
+		break;
+	case V4L2_CID_MPEG_VIDEO_MUTE_YUV:
+		params->video_mute_yuv = ctrl->value;
+		break;
 	case V4L2_CID_MPEG_STREAM_TYPE:
 		params->stream_type = ctrl->value;
 		params->video_encoding =
@@ -290,6 +315,9 @@
 	case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
 		params->video_chroma_median_filter_bottom = ctrl->value;
 		break;
+	case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+		params->stream_insert_nav_packets = ctrl->value;
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -336,6 +364,9 @@
 	case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
 		name = "Median Chroma Filter Minimum";
 		break;
+	case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+		name = "Insert Navigation Packets";
+		break;
 
 	default:
 		return v4l2_ctrl_query_fill(qctrl, min, max, step, def);
@@ -350,6 +381,12 @@
 		min = 0;
 		step = 1;
 		break;
+	case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+		qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
+		min = 0;
+		max = 1;
+		step = 1;
+		break;
 	default:
 		qctrl->type = V4L2_CTRL_TYPE_INTEGER;
 		break;
@@ -505,6 +542,9 @@
 		       qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
 		return 0;
 
+	case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+		return cx2341x_ctrl_query_fill(qctrl, 0, 1, 1, 0);
+
 	default:
 		return v4l2_ctrl_query_fill_std(qctrl);
 
@@ -656,6 +696,7 @@
 	/* stream */
 	.stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS,
 	.stream_vbi_fmt = V4L2_MPEG_STREAM_VBI_FMT_NONE,
+	.stream_insert_nav_packets = 0,
 
 	/* audio */
 	.audio_sampling_freq = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
@@ -665,6 +706,7 @@
 	.audio_mode_extension = V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4,
 	.audio_emphasis = V4L2_MPEG_AUDIO_EMPHASIS_NONE,
 	.audio_crc = V4L2_MPEG_AUDIO_CRC_NONE,
+	.audio_mute = 0,
 
 	/* video */
 	.video_encoding = V4L2_MPEG_VIDEO_ENCODING_MPEG_2,
@@ -676,6 +718,8 @@
 	.video_bitrate = 6000000,
 	.video_bitrate_peak = 8000000,
 	.video_temporal_decimation = 0,
+	.video_mute = 0,
+	.video_mute_yuv = 0x008080,  /* YCbCr value for black */
 
 	/* encoding filters */
 	.video_spatial_filter_mode = V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
@@ -779,6 +823,10 @@
 		err = cx2341x_api(priv, func, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new->audio_properties);
 		if (err) return err;
 	}
+	if (old == NULL || old->audio_mute != new->audio_mute) {
+		err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_AUDIO, 1, new->audio_mute);
+		if (err) return err;
+	}
 	if (old == NULL || old->video_bitrate_mode != new->video_bitrate_mode ||
 		old->video_bitrate != new->video_bitrate ||
 		old->video_bitrate_peak != new->video_bitrate_peak) {
@@ -826,6 +874,15 @@
 			new->video_temporal_decimation);
 		if (err) return err;
 	}
+	if (old == NULL || old->video_mute != new->video_mute ||
+			(new->video_mute && old->video_mute_yuv != new->video_mute_yuv)) {
+		err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_VIDEO, 1, new->video_mute | (new->video_mute_yuv << 8));
+		if (err) return err;
+	}
+	if (old == NULL || old->stream_insert_nav_packets != new->stream_insert_nav_packets) {
+		err = cx2341x_api(priv, func, CX2341X_ENC_MISC, 2, 7, new->stream_insert_nav_packets);
+		if (err) return err;
+	}
 	return 0;
 }
 
@@ -854,18 +911,22 @@
 	int temporal = p->video_temporal_filter;
 
 	/* Stream */
-	printk(KERN_INFO "%s: Stream: %s\n",
+	printk(KERN_INFO "%s: Stream: %s",
 		prefix,
 		cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_TYPE));
+	if (p->stream_insert_nav_packets)
+		printk(" (with navigation packets)");
+	printk("\n");
 	printk(KERN_INFO "%s: VBI Format: %s\n",
 		prefix,
 		cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_VBI_FMT));
 
 	/* Video */
-	printk(KERN_INFO "%s: Video:  %dx%d, %d fps\n",
+	printk(KERN_INFO "%s: Video:  %dx%d, %d fps%s\n",
 		prefix,
 		p->width / (is_mpeg1 ? 2 : 1), p->height / (is_mpeg1 ? 2 : 1),
-		p->is_50hz ? 25 : 30);
+		p->is_50hz ? 25 : 30,
+		(p->video_mute) ? " (muted)" : "");
 	printk(KERN_INFO "%s: Video:  %s, %s, %s, %d",
 		prefix,
 		cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ENCODING),
@@ -886,12 +947,13 @@
 	}
 
 	/* Audio */
-	printk(KERN_INFO "%s: Audio:  %s, %s, %s, %s",
+	printk(KERN_INFO "%s: Audio:  %s, %s, %s, %s%s",
 		prefix,
 		cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ),
 		cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_ENCODING),
 		cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_L2_BITRATE),
-		cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE));
+		cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE),
+		p->audio_mute ? " (muted)" : "");
 	if (p->audio_mode == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) {
 		printk(", %s",
 			cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE_EXTENSION));
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index 774d253..1757a58 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -35,6 +35,7 @@
 #include <linux/videodev2.h>
 #include <linux/i2c.h>
 #include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
 #include <media/cx25840.h>
 
 #include "cx25840-core.h"
@@ -827,9 +828,8 @@
 			cx25840_initialize(client, 0);
 		break;
 
-	case VIDIOC_INT_G_CHIP_IDENT:
-		*(enum v4l2_chip_ident *)arg = state->id;
-		break;
+	case VIDIOC_G_CHIP_IDENT:
+		return v4l2_chip_ident_i2c_client(client, arg, state->id, state->rev);
 
 	default:
 		return -EINVAL;
@@ -847,7 +847,7 @@
 {
 	struct i2c_client *client;
 	struct cx25840_state *state;
-	enum v4l2_chip_ident id;
+	u32 id;
 	u16 device_id;
 
 	/* Check if the adapter supports the needed features
@@ -902,6 +902,7 @@
 	state->audmode = V4L2_TUNER_MODE_LANG1;
 	state->vbi_line_offset = 8;
 	state->id = id;
+	state->rev = device_id;
 
 	i2c_attach_client(client);
 
diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h
index 2804906..f4b56d2 100644
--- a/drivers/media/video/cx25840/cx25840-core.h
+++ b/drivers/media/video/cx25840/cx25840-core.h
@@ -43,7 +43,8 @@
 	u32 audclk_freq;
 	int audmode;
 	int vbi_line_offset;
-	enum v4l2_chip_ident id;
+	u32 id;
+	u32 rev;
 	int is_cx25836;
 };
 
diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c
index 0e86b9d..e852024 100644
--- a/drivers/media/video/cx25840/cx25840-firmware.c
+++ b/drivers/media/video/cx25840/cx25840-firmware.c
@@ -17,7 +17,6 @@
 
 #include <linux/module.h>
 #include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
 #include <linux/firmware.h>
 #include <media/v4l2-common.h>
 #include <media/cx25840.h>
diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig
index b2a66ba..0f9d969 100644
--- a/drivers/media/video/cx88/Kconfig
+++ b/drivers/media/video/cx88/Kconfig
@@ -53,7 +53,6 @@
 	select DVB_OR51132 if !DVB_FE_CUSTOMISE
 	select DVB_CX22702 if !DVB_FE_CUSTOMISE
 	select DVB_LGDT330X if !DVB_FE_CUSTOMISE
-	select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE
 	select DVB_NXT200X if !DVB_FE_CUSTOMISE
 	select DVB_CX24123 if !DVB_FE_CUSTOMISE
 	select DVB_ISL6421 if !DVB_FE_CUSTOMISE
diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c
index e4355fd..3956c25 100644
--- a/drivers/media/video/cx88/cx88-alsa.c
+++ b/drivers/media/video/cx88/cx88-alsa.c
@@ -232,7 +232,8 @@
 	cx_write(MO_AUD_INTSTAT, status);
 	if (debug > 1  ||  (status & mask & ~0xff))
 		cx88_print_irqbits(core->name, "irq aud",
-				   cx88_aud_irqs, status, mask);
+				   cx88_aud_irqs, ARRAY_SIZE(cx88_aud_irqs),
+				   status, mask);
 	/* risc op code error */
 	if (status & (1 << 16)) {
 		printk(KERN_WARNING "%s/0: audio risc op code error\n",core->name);
@@ -413,11 +414,9 @@
 
 	dprintk(1,"Setting buffer\n");
 
-	buf = kmalloc(sizeof(*buf),GFP_KERNEL);
+	buf = kzalloc(sizeof(*buf),GFP_KERNEL);
 	if (NULL == buf)
 		return -ENOMEM;
-	memset(buf,0,sizeof(*buf));
-
 
 	buf->vb.memory = V4L2_MEMORY_MMAP;
 	buf->vb.width  = chip->period_size;
@@ -682,7 +681,7 @@
 		return err;
 	}
 
-	if (!pci_dma_supported(pci,0xffffffff)) {
+	if (!pci_dma_supported(pci,DMA_32BIT_MASK)) {
 		dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n",core->name);
 		err = -EIO;
 		cx88_core_put(core,pci);
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index 65e9d809..e61102d 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -885,6 +885,12 @@
 		.input          = {{
 			.type   = CX88_VMUX_DVB,
 			.vmux   = 0,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
 		}},
 		.mpeg           = CX88_MPEG_DVB,
 	},
@@ -1537,10 +1543,10 @@
 	},{
 		.subvendor = 0x17de,
 		.subdevice = 0x0840,
-	       .card      = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
-       },{
-	       .subvendor = 0x1421,
-	       .subdevice = 0x0305,
+		.card      = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
+	},{
+		.subvendor = 0x1421,
+		.subdevice = 0x0305,
 		.card      = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
 	},{
 		.subvendor = 0x18ac,
@@ -1631,6 +1637,10 @@
 		.subvendor = 0x0070,
 		.subdevice = 0x1402,
 		.card      = CX88_BOARD_HAUPPAUGE_HVR3000,
+	},{
+		.subvendor = 0x1421,
+		.subdevice = 0x0341, /* ADS Tech InstantTV DVB-S */
+		.card      = CX88_BOARD_KWORLD_DVBS_100,
 	},
 };
 const unsigned int cx88_idcount = ARRAY_SIZE(cx88_subids);
@@ -1786,7 +1796,7 @@
 		{ 0x03, 0x0C },
 	};
 
-	for (i = 0; i < 13; i++) {
+	for (i = 0; i < ARRAY_SIZE(init_bufs); i++) {
 		msg.buf = init_bufs[i];
 		msg.len = (i != 12 ? 5 : 2);
 		err = i2c_transfer(&core->i2c_adap, &msg, 1);
@@ -1913,12 +1923,21 @@
 		if (0 == core->i2c_rc) {
 			/* enable tuner */
 			int i;
-			static const u8 buffer [] = { 0x10,0x12,0x13,0x04,0x16,0x00,0x14,0x04,0x017,0x00 };
+			static const u8 buffer [][2] = {
+				{0x10,0x12},
+				{0x13,0x04},
+				{0x16,0x00},
+				{0x14,0x04},
+				{0x17,0x00}
+			};
 			core->i2c_client.addr = 0x0a;
 
-			for (i = 0; i < 5; i++)
-				if (2 != i2c_master_send(&core->i2c_client,&buffer[i*2],2))
-					printk(KERN_WARNING "%s: Unable to enable tuner(%i).\n",
+			for (i = 0; i < ARRAY_SIZE(buffer); i++)
+				if (2 != i2c_master_send(&core->i2c_client,
+							buffer[i],2))
+					printk(KERN_WARNING
+						"%s: Unable to enable "
+						"tuner(%i).\n",
 						core->name, i);
 		}
 		break;
diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c
index d86813b..f31ec96 100644
--- a/drivers/media/video/cx88/cx88-core.c
+++ b/drivers/media/video/cx88/cx88-core.c
@@ -489,12 +489,12 @@
 };
 
 void cx88_print_irqbits(char *name, char *tag, char **strings,
-			u32 bits, u32 mask)
+			int len, u32 bits, u32 mask)
 {
 	unsigned int i;
 
 	printk(KERN_DEBUG "%s: %s [0x%x]", name, tag, bits);
-	for (i = 0; i < 32; i++) {
+	for (i = 0; i < len; i++) {
 		if (!(bits & (1 << i)))
 			continue;
 		if (strings[i])
@@ -520,8 +520,8 @@
 	}
 	if (!handled)
 		cx88_print_irqbits(core->name, "irq pci",
-				   cx88_pci_irqs, status,
-				   core->pci_irqmask);
+				   cx88_pci_irqs, ARRAY_SIZE(cx88_pci_irqs),
+				   status, core->pci_irqmask);
 	return handled;
 }
 
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index 4f55602..dbfe4dc 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -42,7 +42,6 @@
 #include "cx22702.h"
 #include "or51132.h"
 #include "lgdt330x.h"
-#include "lgh06xf.h"
 #include "nxt200x.h"
 #include "cx24123.h"
 #include "isl6421.h"
@@ -476,6 +475,8 @@
 	case CX88_BOARD_WINFAST_DTV2000H:
 	case CX88_BOARD_HAUPPAUGE_HVR1100:
 	case CX88_BOARD_HAUPPAUGE_HVR1100LP:
+	case CX88_BOARD_HAUPPAUGE_HVR1300:
+	case CX88_BOARD_HAUPPAUGE_HVR3000:
 		dev->dvb.frontend = dvb_attach(cx22702_attach,
 					       &hauppauge_hvr_config,
 					       &dev->core->i2c_adap);
@@ -631,8 +632,9 @@
 					       &fusionhdtv_5_gold,
 					       &dev->core->i2c_adap);
 		if (dev->dvb.frontend != NULL) {
-			dvb_attach(lgh06xf_attach, dev->dvb.frontend,
-				   &dev->core->i2c_adap);
+			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+				   &dev->core->i2c_adap,
+				   &dvb_pll_lg_tdvs_h06xf);
 		}
 		}
 		break;
@@ -650,8 +652,9 @@
 					       &pchdtv_hd5500,
 					       &dev->core->i2c_adap);
 		if (dev->dvb.frontend != NULL) {
-			dvb_attach(lgh06xf_attach, dev->dvb.frontend,
-				   &dev->core->i2c_adap);
+			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+				   &dev->core->i2c_adap,
+				   &dvb_pll_lg_tdvs_h06xf);
 		}
 		}
 		break;
@@ -692,24 +695,6 @@
 			dev->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage;
 		}
 		break;
-	case CX88_BOARD_HAUPPAUGE_HVR1300:
-		dev->dvb.frontend = dvb_attach(cx22702_attach,
-					       &hauppauge_hvr_config,
-					       &dev->core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
-			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
-				   &dev->core->i2c_adap, &dvb_pll_fmd1216me);
-		}
-		break;
-	case CX88_BOARD_HAUPPAUGE_HVR3000:
-		dev->dvb.frontend = dvb_attach(cx22702_attach,
-					       &hauppauge_hvr_config,
-					       &dev->core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
-			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
-				   &dev->core->i2c_adap, &dvb_pll_fmd1216me);
-		}
-		break;
 	default:
 		printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n",
 		       dev->core->name);
diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c
index 9830d5c..7919a1f 100644
--- a/drivers/media/video/cx88/cx88-i2c.c
+++ b/drivers/media/video/cx88/cx88-i2c.c
@@ -1,3 +1,4 @@
+
 /*
 
     cx88-i2c.c  --  all the i2c code is here
@@ -195,7 +196,7 @@
 	unsigned char buf;
 	int i,rc;
 
-	for (i = 0; i < 128; i++) {
+	for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
 		c->addr = i;
 		rc = i2c_master_recv(c,&buf,0);
 		if (rc < 0)
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c
index 1fe1a83..b2eb32e 100644
--- a/drivers/media/video/cx88/cx88-mpeg.c
+++ b/drivers/media/video/cx88/cx88-mpeg.c
@@ -49,6 +49,27 @@
 #define mpeg_dbg(level,fmt, arg...)	if (debug >= level) \
 	printk(KERN_DEBUG "%s/2-mpeg: " fmt, core->name, ## arg)
 
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+	struct cx8802_dev *dev=container_of(work, struct cx8802_dev, request_module_wk);
+
+	if (cx88_boards[dev->core->board].mpeg & CX88_MPEG_DVB)
+		request_module("cx88-dvb");
+	if (cx88_boards[dev->core->board].mpeg & CX88_MPEG_BLACKBIRD)
+		request_module("cx88-blackbird");
+}
+
+static void request_modules(struct cx8802_dev *dev)
+{
+	INIT_WORK(&dev->request_module_wk, request_module_async);
+	schedule_work(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+
 static LIST_HEAD(cx8802_devlist);
 /* ------------------------------------------------------------------ */
 
@@ -345,7 +366,8 @@
 
 	if (debug || (status & mask & ~0xff))
 		cx88_print_irqbits(core->name, "irq mpeg ",
-				   cx88_mpeg_irqs, status, mask);
+				   cx88_mpeg_irqs, ARRAY_SIZE(cx88_mpeg_irqs),
+				   status, mask);
 
 	/* risc op code error */
 	if (status & (1 << 16)) {
@@ -427,7 +449,7 @@
 	if (pci_enable_device(dev->pci))
 		return -EIO;
 	pci_set_master(dev->pci);
-	if (!pci_dma_supported(dev->pci,0xffffffff)) {
+	if (!pci_dma_supported(dev->pci,DMA_32BIT_MASK)) {
 		printk("%s/2: Oops: no 32bit PCI DMA ???\n",dev->core->name);
 		return -EIO;
 	}
@@ -778,6 +800,9 @@
 
 	/* Maintain a reference so cx88-video can query the 8802 device. */
 	core->dvbdev = dev;
+
+	/* now autoload cx88-dvb or cx88-blackbird */
+	request_modules(dev);
 	return 0;
 
  fail_free:
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
index bdfe2af..fbce1d5 100644
--- a/drivers/media/video/cx88/cx88-video.c
+++ b/drivers/media/video/cx88/cx88-video.c
@@ -1555,7 +1555,8 @@
 	cx_write(MO_VID_INTSTAT, status);
 	if (irq_debug  ||  (status & mask & ~0xff))
 		cx88_print_irqbits(core->name, "irq vid",
-				   cx88_vid_irqs, status, mask);
+				   cx88_vid_irqs, ARRAY_SIZE(cx88_vid_irqs),
+				   status, mask);
 
 	/* risc op code error */
 	if (status & (1 << 16)) {
@@ -1778,7 +1779,7 @@
 	       dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0));
 
 	pci_set_master(pci_dev);
-	if (!pci_dma_supported(pci_dev,0xffffffff)) {
+	if (!pci_dma_supported(pci_dev,DMA_32BIT_MASK)) {
 		printk("%s/0: Oops: no 32bit PCI DMA ???\n",core->name);
 		err = -EIO;
 		goto fail_core;
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index a4f7bef..738d4f2 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -481,6 +481,8 @@
 
 	/* List of attached drivers */
 	struct cx8802_driver       drvlist;
+	struct work_struct request_module_wk;
+
 };
 
 /* ----------------------------------------------------------- */
@@ -510,7 +512,7 @@
 /* cx88-core.c                                                 */
 
 extern void cx88_print_irqbits(char *name, char *tag, char **strings,
-			       u32 bits, u32 mask);
+			       int len, u32 bits, u32 mask);
 
 extern int cx88_core_irq(struct cx88_core *core, u32 status);
 extern void cx88_wakeup(struct cx88_core *core,
diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c
index d829d8f..563a831 100644
--- a/drivers/media/video/em28xx/em28xx-i2c.c
+++ b/drivers/media/video/em28xx/em28xx-i2c.c
@@ -523,7 +523,7 @@
 	unsigned char buf;
 	int i, rc;
 
-	for (i = 0; i < 128; i++) {
+	for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
 		c->addr = i;
 		rc = i2c_master_recv(c, &buf, 0);
 		if (rc < 0)
diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c
index 210582d..ed92b6f 100644
--- a/drivers/media/video/ir-kbd-i2c.c
+++ b/drivers/media/video/ir-kbd-i2c.c
@@ -173,7 +173,7 @@
 		return -EIO;
 	}
 
-	for (start = 0; start<4; start++) {
+	for (start = 0; start < ARRAY_SIZE(b); start++) {
 		if (b[start] == marker) {
 			code=b[(start+parity_offset+1)%4];
 			parity=b[(start+parity_offset)%4];
diff --git a/drivers/media/video/ivtv/Kconfig b/drivers/media/video/ivtv/Kconfig
new file mode 100644
index 0000000..e854f3f
--- /dev/null
+++ b/drivers/media/video/ivtv/Kconfig
@@ -0,0 +1,26 @@
+config VIDEO_IVTV
+	tristate "Conexant cx23416/cx23415 MPEG encoder/decoder support"
+	depends on VIDEO_V4L1 && VIDEO_V4L2 && USB && I2C && EXPERIMENTAL
+	select FW_LOADER
+	select VIDEO_TUNER
+	select VIDEO_TVEEPROM
+	select VIDEO_CX2341X
+	select VIDEO_CX25840
+	select VIDEO_MSP3400
+	select VIDEO_SAA711X
+	select VIDEO_SAA7127
+	select VIDEO_TVAUDIO
+	select VIDEO_CS53L32A
+	select VIDEO_WM8775
+	select VIDEO_WM8739
+	select VIDEO_UPD64031A
+	select VIDEO_UPD64083
+	---help---
+	  This is a video4linux driver for Conexant cx23416 or cx23416 based
+	  PCI personal video recorder devices.
+
+	  This is used in devices such as the Hauppauge PVR-150/250/350/500
+	  cards.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ivtv.
diff --git a/drivers/media/video/ivtv/Makefile b/drivers/media/video/ivtv/Makefile
new file mode 100644
index 0000000..7e95148
--- /dev/null
+++ b/drivers/media/video/ivtv/Makefile
@@ -0,0 +1,7 @@
+ivtv-objs	:= ivtv-audio.o ivtv-cards.o ivtv-controls.o \
+		   ivtv-driver.o ivtv-fileops.o ivtv-firmware.o \
+		   ivtv-gpio.o ivtv-i2c.o ivtv-ioctl.o ivtv-irq.o \
+		   ivtv-mailbox.o ivtv-queue.o ivtv-streams.o ivtv-udma.o \
+		   ivtv-vbi.o ivtv-video.o ivtv-yuv.o
+
+obj-$(CONFIG_VIDEO_IVTV) += ivtv.o
diff --git a/drivers/media/video/ivtv/ivtv-audio.c b/drivers/media/video/ivtv/ivtv-audio.c
new file mode 100644
index 0000000..d702b8b
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-audio.c
@@ -0,0 +1,74 @@
+/*
+    Audio-related ivtv functions.
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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 "ivtv-driver.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-i2c.h"
+#include "ivtv-gpio.h"
+#include "ivtv-cards.h"
+#include "ivtv-audio.h"
+#include <media/msp3400.h>
+#include <linux/videodev.h>
+
+/* Selects the audio input and output according to the current
+   settings. */
+int ivtv_audio_set_io(struct ivtv *itv)
+{
+	struct v4l2_routing route;
+	u32 audio_input;
+	int mux_input;
+
+	/* Determine which input to use */
+	if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
+		audio_input = itv->card->radio_input.audio_input;
+		mux_input = itv->card->radio_input.muxer_input;
+	} else {
+		audio_input = itv->card->audio_inputs[itv->audio_input].audio_input;
+		mux_input = itv->card->audio_inputs[itv->audio_input].muxer_input;
+	}
+
+	/* handle muxer chips */
+	route.input = mux_input;
+	route.output = 0;
+	ivtv_i2c_hw(itv, itv->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route);
+
+	route.input = audio_input;
+	if (itv->card->hw_audio & IVTV_HW_MSP34XX) {
+		route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
+	}
+	return ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, &route);
+}
+
+void ivtv_audio_set_route(struct ivtv *itv, struct v4l2_routing *route)
+{
+	ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, route);
+}
+
+void ivtv_audio_set_audio_clock_freq(struct ivtv *itv, u8 freq)
+{
+	static u32 freqs[3] = { 44100, 48000, 32000 };
+
+	/* The audio clock of the digitizer must match the codec sample
+	   rate otherwise you get some very strange effects. */
+	if (freq > 2)
+		return;
+	ivtv_call_i2c_clients(itv, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[freq]);
+}
+
diff --git a/drivers/media/video/ivtv/ivtv-audio.h b/drivers/media/video/ivtv/ivtv-audio.h
new file mode 100644
index 0000000..9c42846
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-audio.h
@@ -0,0 +1,23 @@
+/*
+    Audio-related ivtv functions.
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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
+ */
+
+int ivtv_audio_set_io(struct ivtv *itv);
+void ivtv_audio_set_route(struct ivtv *itv, struct v4l2_routing *route);
+void ivtv_audio_set_audio_clock_freq(struct ivtv *itv, u8 freq);
diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c
new file mode 100644
index 0000000..8eab0208
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-cards.c
@@ -0,0 +1,964 @@
+/*
+    Functions to query card hardware
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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 "ivtv-driver.h"
+#include "ivtv-cards.h"
+#include "ivtv-i2c.h"
+
+#include <media/msp3400.h>
+#include <media/wm8775.h>
+#include <media/cs53l32a.h>
+#include <media/cx25840.h>
+#include <media/upd64031a.h>
+
+#define MSP_TUNER  MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \
+				MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER)
+#define MSP_SCART1 MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \
+				MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+#define MSP_SCART2 MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, \
+				MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+#define MSP_SCART3 MSP_INPUT(MSP_IN_SCART3, MSP_IN_TUNER1, \
+				MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+#define MSP_MONO   MSP_INPUT(MSP_IN_MONO, MSP_IN_TUNER1, \
+				MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+
+/********************** card configuration *******************************/
+
+/* Please add new PCI IDs to: http://pci-ids.ucw.cz/iii
+   This keeps the PCI ID database up to date. Note that the entries
+   must be added under vendor 0x4444 (Conexant) as subsystem IDs.
+   New vendor IDs should still be added to the vendor ID list. */
+
+/* Hauppauge PVR-250 cards */
+
+/* Note: for Hauppauge cards the tveeprom information is used instead of PCI IDs */
+static const struct ivtv_card ivtv_card_pvr250 = {
+	.type = IVTV_CARD_PVR_250,
+	.name = "Hauppauge WinTV PVR-250",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7115,
+	.hw_audio = IVTV_HW_MSP34XX,
+	.hw_audio_ctrl = IVTV_HW_MSP34XX,
+	.hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 |
+		  IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+		{ IVTV_CARD_INPUT_SVIDEO2,    2, IVTV_SAA71XX_SVIDEO1    },
+		{ IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 },
+		{ IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  MSP_TUNER  },
+		{ IVTV_CARD_INPUT_LINE_IN1,   MSP_SCART1 },
+		{ IVTV_CARD_INPUT_LINE_IN2,   MSP_SCART3 },
+	},
+	.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 },
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Hauppauge PVR-350 cards */
+
+/* Outputs for Hauppauge PVR350 cards */
+static struct ivtv_card_output ivtv_pvr350_outputs[] = {
+	{
+		.name = "S-Video + Composite",
+		.video_output = 0,
+	}, {
+		.name = "Composite",
+		.video_output = 1,
+	}, {
+		.name = "S-Video",
+		.video_output = 2,
+	}, {
+		.name = "RGB",
+		.video_output = 3,
+	}, {
+		.name = "YUV C",
+		.video_output = 4,
+	}, {
+		.name = "YUV V",
+		.video_output = 5,
+	}
+};
+
+static const struct ivtv_card ivtv_card_pvr350 = {
+	.type = IVTV_CARD_PVR_350,
+	.name = "Hauppauge WinTV PVR-350",
+	.v4l2_capabilities = IVTV_CAP_ENCODER | IVTV_CAP_DECODER,
+	.video_outputs = ivtv_pvr350_outputs,
+	.nof_outputs = ARRAY_SIZE(ivtv_pvr350_outputs),
+	.hw_video = IVTV_HW_SAA7115,
+	.hw_audio = IVTV_HW_MSP34XX,
+	.hw_audio_ctrl = IVTV_HW_MSP34XX,
+	.hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 |
+		  IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+		{ IVTV_CARD_INPUT_SVIDEO2,    2, IVTV_SAA71XX_SVIDEO1    },
+		{ IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 },
+		{ IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  MSP_TUNER  },
+		{ IVTV_CARD_INPUT_LINE_IN1,   MSP_SCART1 },
+		{ IVTV_CARD_INPUT_LINE_IN2,   MSP_SCART3 },
+	},
+	.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 },
+};
+
+/* PVR-350 V1 boards have a different audio tuner input and use a
+   saa7114 instead of a saa7115.
+   Note that the info below comes from a pre-production model so it may
+   not be correct. Especially the audio behaves strangely (mono only it seems) */
+static const struct ivtv_card ivtv_card_pvr350_v1 = {
+	.type = IVTV_CARD_PVR_350_V1,
+	.name = "Hauppauge WinTV PVR-350 (V1)",
+	.v4l2_capabilities = IVTV_CAP_ENCODER | IVTV_CAP_DECODER,
+	.video_outputs = ivtv_pvr350_outputs,
+	.nof_outputs = ARRAY_SIZE(ivtv_pvr350_outputs),
+	.hw_video = IVTV_HW_SAA7114,
+	.hw_audio = IVTV_HW_MSP34XX,
+	.hw_audio_ctrl = IVTV_HW_MSP34XX,
+	.hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7114 |
+		  IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+		{ IVTV_CARD_INPUT_SVIDEO2,    2, IVTV_SAA71XX_SVIDEO1    },
+		{ IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 },
+		{ IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  MSP_MONO   },
+		{ IVTV_CARD_INPUT_LINE_IN1,   MSP_SCART1 },
+		{ IVTV_CARD_INPUT_LINE_IN2,   MSP_SCART3 },
+	},
+	.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 },
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Hauppauge PVR-150/PVR-500 cards */
+
+static const struct ivtv_card ivtv_card_pvr150 = {
+	.type = IVTV_CARD_PVR_150,
+	.name = "Hauppauge WinTV PVR-150",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_CX25840,
+	.hw_audio = IVTV_HW_CX25840,
+	.hw_audio_ctrl = IVTV_HW_CX25840,
+	.hw_muxer = IVTV_HW_WM8775,
+	.hw_all = IVTV_HW_WM8775 | IVTV_HW_CX25840 |
+		  IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE7 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, CX25840_SVIDEO1    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE3 },
+		{ IVTV_CARD_INPUT_SVIDEO2,    2, CX25840_SVIDEO2    },
+		{ IVTV_CARD_INPUT_COMPOSITE2, 2, CX25840_COMPOSITE4 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,
+		  CX25840_AUDIO8, WM8775_AIN2 },
+		{ IVTV_CARD_INPUT_LINE_IN1,
+		  CX25840_AUDIO_SERIAL, WM8775_AIN2 },
+		{ IVTV_CARD_INPUT_LINE_IN2,
+		  CX25840_AUDIO_SERIAL, WM8775_AIN3 },
+	},
+	.radio_input = { IVTV_CARD_INPUT_AUD_TUNER,
+			 CX25840_AUDIO_SERIAL, WM8775_AIN4 },
+	/* apparently needed for the IR blaster */
+	.gpio_init = { .direction = 0x1f01, .initial_value = 0x26f3 },
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AVerMedia M179 cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_m179[] = {
+	{ PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_AVERMEDIA, 0xa3cf },
+	{ PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_AVERMEDIA, 0xa3ce },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_m179 = {
+	.type = IVTV_CARD_M179,
+	.name = "AVerMedia M179",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7114,
+	.hw_audio = IVTV_HW_GPIO,
+	.hw_audio_ctrl = IVTV_HW_GPIO,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+	},
+	.gpio_init = { .direction = 0xe380, .initial_value = 0x8290 },
+	.gpio_audio_input  = { .mask = 0x8040, .tuner  = 0x8000, .linein = 0x0000 },
+	.gpio_audio_mute   = { .mask = 0x2000, .mute   = 0x2000 },
+	.gpio_audio_mode   = { .mask = 0x4300, .mono   = 0x4000, .stereo = 0x0200,
+			      .lang1 = 0x0200, .lang2  = 0x0100, .both   = 0x0000 },
+	.gpio_audio_freq   = { .mask = 0x0018, .f32000 = 0x0000,
+			     .f44100 = 0x0008, .f48000 = 0x0010 },
+	.gpio_audio_detect = { .mask = 0x4000, .stereo = 0x0000 },
+	.tuners = {
+		/* As far as we know all M179 cards use this tuner */
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_NTSC },
+	},
+	.pci_list = ivtv_pci_m179,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPG600/Kuroutoshikou ITVC16-STVLP cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_mpg600[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0xfff3 },
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0xffff },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_mpg600 = {
+	.type = IVTV_CARD_MPG600,
+	.name = "Yuan MPG600, Kuroutoshikou ITVC16-STVLP",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7115,
+	.hw_audio = IVTV_HW_GPIO,
+	.hw_audio_ctrl = IVTV_HW_GPIO,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+	},
+	.gpio_init = { .direction = 0x3080, .initial_value = 0x0004 },
+	.gpio_audio_input  = { .mask = 0x3000, .tuner  = 0x0000, .linein = 0x2000 },
+	.gpio_audio_mute   = { .mask = 0x0001, .mute   = 0x0001 },
+	.gpio_audio_mode   = { .mask = 0x000e, .mono   = 0x0006, .stereo = 0x0004,
+			      .lang1 = 0x0004, .lang2  = 0x0000, .both   = 0x0008 },
+	.gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 },
+	.tuners = {
+		/* The PAL tuner is confirmed */
+		{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME },
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
+	},
+	.pci_list = ivtv_pci_mpg600,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPG160/Kuroutoshikou ITVC15-STVLP cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_mpg160[] = {
+	{ PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_YUAN1, 0 },
+	{ PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_IODATA, 0x40a0 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_mpg160 = {
+	.type = IVTV_CARD_MPG160,
+	.name = "YUAN MPG160, Kuroutoshikou ITVC15-STVLP, I/O Data GV-M2TV/PCI",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7114,
+	.hw_audio = IVTV_HW_GPIO,
+	.hw_audio_ctrl = IVTV_HW_GPIO,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+	},
+	.gpio_init = { .direction = 0x7080, .initial_value = 0x400c },
+	.gpio_audio_input  = { .mask = 0x3000, .tuner  = 0x0000, .linein = 0x2000 },
+	.gpio_audio_mute   = { .mask = 0x0001, .mute   = 0x0001 },
+	.gpio_audio_mode   = { .mask = 0x000e, .mono   = 0x0006, .stereo = 0x0004,
+			      .lang1 = 0x0004, .lang2  = 0x0000, .both   = 0x0008 },
+	.gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 },
+	.tuners = {
+		{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME },
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
+	},
+	.pci_list = ivtv_pci_mpg160,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan PG600/Diamond PVR-550 cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_pg600[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_DIAMONDMM, 0x0070 },
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3,     0x0600 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_pg600 = {
+	.type = IVTV_CARD_PG600,
+	.name = "Yuan PG600, Diamond PVR-550",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_CX25840,
+	.hw_audio = IVTV_HW_CX25840,
+	.hw_audio_ctrl = IVTV_HW_CX25840,
+	.hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE2 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1,
+		  CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  CX25840_AUDIO5       },
+		{ IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL },
+	},
+	.tuners = {
+		{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME },
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
+	},
+	.pci_list = ivtv_pci_pg600,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Adaptec VideOh! AVC-2410 card */
+
+static const struct ivtv_card_pci_info ivtv_pci_avc2410[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ADAPTEC, 0x0093 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_avc2410 = {
+	.type = IVTV_CARD_AVC2410,
+	.name = "Adaptec VideOh! AVC-2410",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7115,
+	.hw_audio = IVTV_HW_MSP34XX,
+	.hw_audio_ctrl = IVTV_HW_MSP34XX,
+	.hw_muxer = IVTV_HW_CS53L32A,
+	.hw_all = IVTV_HW_MSP34XX | IVTV_HW_CS53L32A |
+		  IVTV_HW_SAA7115 | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,
+		  MSP_TUNER, CS53L32A_IN0 },
+		{ IVTV_CARD_INPUT_LINE_IN1,
+		  MSP_SCART1, CS53L32A_IN2 },
+	},
+	/* This card has no eeprom and in fact the Windows driver relies
+	   on the country/region setting of the user to decide which tuner
+	   is available. */
+	.tuners = {
+		/* This tuner has been verified for the AVC2410 */
+		{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+		/* This is a good guess, but I'm not totally sure this is
+		   the correct tuner for NTSC. */
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+	},
+	.pci_list = ivtv_pci_avc2410,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Adaptec VideOh! AVC-2010 card */
+
+static const struct ivtv_card_pci_info ivtv_pci_avc2010[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ADAPTEC, 0x0092 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_avc2010 = {
+	.type = IVTV_CARD_AVC2010,
+	.name = "Adaptec VideOh! AVC-2010",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7115,
+	.hw_audio = IVTV_HW_CS53L32A,
+	.hw_audio_ctrl = IVTV_HW_CS53L32A,
+	.hw_all = IVTV_HW_CS53L32A | IVTV_HW_SAA7115,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_SVIDEO1,    0, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 0, IVTV_SAA71XX_COMPOSITE3 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_LINE_IN1,   CS53L32A_IN2 },
+	},
+	/* Does not have a tuner */
+	.pci_list = ivtv_pci_avc2010,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Nagase Transgear 5000TV card */
+
+static const struct ivtv_card_pci_info ivtv_pci_tg5000tv[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xbfff },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_tg5000tv = {
+	.type = IVTV_CARD_TG5000TV,
+	.name = "Nagase Transgear 5000TV",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7114 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X |
+	IVTV_HW_GPIO,
+	.hw_audio = IVTV_HW_GPIO,
+	.hw_audio_ctrl = IVTV_HW_GPIO,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER |
+		  IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_SVIDEO0 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO2 },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+	},
+	.gr_config = UPD64031A_VERTICAL_EXTERNAL,
+	.gpio_init = { .direction = 0xe080, .initial_value = 0x8000 },
+	.gpio_audio_input  = { .mask = 0x8080, .tuner  = 0x8000, .linein = 0x0080 },
+	.gpio_audio_mute   = { .mask = 0x6000, .mute   = 0x6000 },
+	.gpio_audio_mode   = { .mask = 0x4300, .mono   = 0x4000, .stereo = 0x0200,
+			      .lang1 = 0x0300, .lang2  = 0x0000, .both   = 0x0200 },
+	.gpio_video_input  = { .mask = 0x0030, .tuner  = 0x0000,
+			  .composite = 0x0010, .svideo = 0x0020 },
+	.tuners = {
+		{ .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
+	},
+	.pci_list = ivtv_pci_tg5000tv,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AOpen VA2000MAX-SNT6 card */
+
+static const struct ivtv_card_pci_info ivtv_pci_va2000[] = {
+	{ PCI_DEVICE_ID_IVTV16, 0, 0xff5f },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_va2000 = {
+	.type = IVTV_CARD_VA2000MAX_SNT6,
+	.name = "AOpen VA2000MAX-SNT6",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD6408X,
+	.hw_audio = IVTV_HW_MSP34XX,
+	.hw_audio_ctrl = IVTV_HW_MSP34XX,
+	.hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 |
+		  IVTV_HW_UPD6408X | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER },
+	},
+	.tuners = {
+		{ .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
+	},
+	.pci_list = ivtv_pci_va2000,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPG600GR/Kuroutoshikou CX23416GYC-STVLP cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_cx23416gyc[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0x0600 },
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN4, 0x0600 },
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_MELCO, 0x0523 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_cx23416gyc = {
+	.type = IVTV_CARD_CX23416GYC,
+	.name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO |
+		IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+	.hw_audio = IVTV_HW_SAA717X,
+	.hw_audio_ctrl = IVTV_HW_SAA717X,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER |
+		  IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_SVIDEO3 |
+						 IVTV_SAA717X_TUNER_FLAG },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0 },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO3 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_SAA717X_IN2 },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_SAA717X_IN0 },
+	},
+	.gr_config = UPD64031A_VERTICAL_EXTERNAL,
+	.gpio_init = { .direction = 0xf880, .initial_value = 0x8800 },
+	.gpio_video_input  = { .mask = 0x0020, .tuner  = 0x0000,
+			       .composite = 0x0020, .svideo = 0x0020 },
+	.gpio_audio_freq   = { .mask = 0xc000, .f32000 = 0x0000,
+			     .f44100 = 0x4000, .f48000 = 0x8000 },
+	.tuners = {
+		{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+	},
+	.pci_list = ivtv_pci_cx23416gyc,
+};
+
+static const struct ivtv_card ivtv_card_cx23416gyc_nogr = {
+	.type = IVTV_CARD_CX23416GYC_NOGR,
+	.name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP (no GR)",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO | IVTV_HW_UPD6408X,
+	.hw_audio = IVTV_HW_SAA717X,
+	.hw_audio_ctrl = IVTV_HW_SAA717X,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER |
+		  IVTV_HW_UPD6408X,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 |
+						 IVTV_SAA717X_TUNER_FLAG },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_SAA717X_IN2 },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_SAA717X_IN0 },
+	},
+	.gpio_init = { .direction = 0xf880, .initial_value = 0x8800 },
+	.gpio_video_input  = { .mask = 0x0020, .tuner  = 0x0000,
+			       .composite = 0x0020, .svideo = 0x0020 },
+	.gpio_audio_freq   = { .mask = 0xc000, .f32000 = 0x0000,
+			     .f44100 = 0x4000, .f48000 = 0x8000 },
+	.tuners = {
+		{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+	},
+};
+
+static const struct ivtv_card ivtv_card_cx23416gyc_nogrycs = {
+	.type = IVTV_CARD_CX23416GYC_NOGRYCS,
+	.name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP (no GR/YCS)",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO,
+	.hw_audio = IVTV_HW_SAA717X,
+	.hw_audio_ctrl = IVTV_HW_SAA717X,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 |
+						 IVTV_SAA717X_TUNER_FLAG },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_SAA717X_IN2 },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_SAA717X_IN0 },
+	},
+	.gpio_init = { .direction = 0xf880, .initial_value = 0x8800 },
+	.gpio_video_input  = { .mask = 0x0020, .tuner  = 0x0000,
+			       .composite = 0x0020, .svideo = 0x0020 },
+	.gpio_audio_freq   = { .mask = 0xc000, .f32000 = 0x0000,
+			     .f44100 = 0x4000, .f48000 = 0x8000 },
+	.tuners = {
+		{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+	},
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* I/O Data GV-MVP/RX & GV-MVP/RX2W (dual tuner) cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_gv_mvprx[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd01e },
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd038 }, /* 2W unit #1 */
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd039 }, /* 2W unit #2 */
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_gv_mvprx = {
+	.type = IVTV_CARD_GV_MVPRX,
+	.name = "I/O Data GV-MVP/RX, GV-MVP/RX2W (dual tuner)",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+	.hw_audio = IVTV_HW_GPIO,
+	.hw_audio_ctrl = IVTV_HW_WM8739,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TVAUDIO |
+		  IVTV_HW_TUNER | IVTV_HW_WM8739 |
+		  IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO1    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2    },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+	},
+	.gpio_init = { .direction = 0xc301, .initial_value = 0x0200 },
+	.gpio_audio_input  = { .mask = 0xffff, .tuner  = 0x0200, .linein = 0x0300 },
+	.tuners = {
+		/* This card has the Panasonic VP27 tuner */
+		{ .std = V4L2_STD_525_60, .tuner = TUNER_PANASONIC_VP27 },
+	},
+	.pci_list = ivtv_pci_gv_mvprx,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* I/O Data GV-MVP/RX2E card */
+
+static const struct ivtv_card_pci_info ivtv_pci_gv_mvprx2e[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd025 },
+	{0, 0, 0}
+};
+
+static const struct ivtv_card ivtv_card_gv_mvprx2e = {
+	.type = IVTV_CARD_GV_MVPRX2E,
+	.name = "I/O Data GV-MVP/RX2E",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7115,
+	.hw_audio = IVTV_HW_GPIO,
+	.hw_audio_ctrl = IVTV_HW_WM8739,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER |
+		  IVTV_HW_TVAUDIO | IVTV_HW_WM8739,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+	},
+	.gpio_init = { .direction = 0xc301, .initial_value = 0x0200 },
+	.gpio_audio_input  = { .mask = 0xffff, .tuner  = 0x0200, .linein = 0x0300 },
+	.tuners = {
+		/* This card has the Panasonic VP27 tuner */
+		{ .std = V4L2_STD_525_60, .tuner = TUNER_PANASONIC_VP27 },
+	},
+	.pci_list = ivtv_pci_gv_mvprx2e,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* GotVIEW PCI DVD card */
+
+static const struct ivtv_card_pci_info ivtv_pci_gotview_pci_dvd[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0x0600 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_gotview_pci_dvd = {
+	.type = IVTV_CARD_GOTVIEW_PCI_DVD,
+	.name = "GotView PCI DVD",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA717X,
+	.hw_audio = IVTV_HW_SAA717X,
+	.hw_audio_ctrl = IVTV_HW_SAA717X,
+	.hw_all = IVTV_HW_SAA717X | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE1 },  /* pin 116 */
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0 },     /* pin 114/109 */
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },  /* pin 118 */
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_SAA717X_IN0 },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_SAA717X_IN2 },
+	},
+	.gpio_init = { .direction = 0xf000, .initial_value = 0xA000 },
+	.tuners = {
+		/* This card has a Philips FQ1216ME MK3 tuner */
+		{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+	},
+	.pci_list = ivtv_pci_gotview_pci_dvd,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* GotVIEW PCI DVD2 Deluxe card */
+
+static const struct ivtv_card_pci_info ivtv_pci_gotview_pci_dvd2[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_GOTVIEW1, 0x0600 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_gotview_pci_dvd2 = {
+	.type = IVTV_CARD_GOTVIEW_PCI_DVD2,
+	.name = "GotView PCI DVD2 Deluxe",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_CX25840,
+	.hw_audio = IVTV_HW_CX25840,
+	.hw_audio_ctrl = IVTV_HW_CX25840,
+	.hw_muxer = IVTV_HW_GPIO,
+	.hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE2 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1,
+		  CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  CX25840_AUDIO5,       0 },
+		{ IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL, 1 },
+	},
+	.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 },
+	.gpio_init = { .direction = 0x0800, .initial_value = 0 },
+	.gpio_audio_input  = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 },
+	.tuners = {
+		/* This card has a Philips FQ1216ME MK5 tuner */
+		{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+	},
+	.pci_list = ivtv_pci_gotview_pci_dvd2,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPC622 miniPCI card */
+
+static const struct ivtv_card_pci_info ivtv_pci_yuan_mpc622[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN2, 0xd998 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_yuan_mpc622 = {
+	.type = IVTV_CARD_YUAN_MPC622,
+	.name = "Yuan MPC622",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_CX25840,
+	.hw_audio = IVTV_HW_CX25840,
+	.hw_audio_ctrl = IVTV_HW_CX25840,
+	.hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE2 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1,
+		  CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  CX25840_AUDIO5       },
+		{ IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL },
+	},
+	.gpio_init = { .direction = 0x00ff, .initial_value = 0x0002 },
+	.tuners = {
+		/* This card has the TDA8290/TDA8275 tuner chips */
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_TDA8290 },
+	},
+	.pci_list = ivtv_pci_yuan_mpc622,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* DIGITAL COWBOY DCT-MTVP1 card */
+
+static const struct ivtv_card_pci_info ivtv_pci_dctmvtvp1[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xbfff },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_dctmvtvp1 = {
+	.type = IVTV_CARD_DCTMTVP1,
+	.name = "Digital Cowboy DCT-MTVP1",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X |
+		IVTV_HW_GPIO,
+	.hw_audio = IVTV_HW_GPIO,
+	.hw_audio_ctrl = IVTV_HW_GPIO,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER |
+		IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO2    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+	},
+	.gpio_init = { .direction = 0xe080, .initial_value = 0x8000 },
+	.gpio_audio_input  = { .mask = 0x8080, .tuner  = 0x8000, .linein = 0x0080 },
+	.gpio_audio_mute   = { .mask = 0x6000, .mute   = 0x6000 },
+	.gpio_audio_mode   = { .mask = 0x4300, .mono   = 0x4000, .stereo = 0x0200,
+			      .lang1 = 0x0300, .lang2  = 0x0000, .both   = 0x0200 },
+	.gpio_video_input  = { .mask = 0x0030, .tuner  = 0x0000,
+			       .composite = 0x0010, .svideo = 0x0020},
+	.tuners = {
+		{ .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
+	},
+	.pci_list = ivtv_pci_dctmvtvp1,
+};
+
+/* ------------------------------------------------------------------------- */
+
+#ifdef HAVE_XC3028
+
+/* Yuan PG600-2/GotView PCI DVD Lite/Club3D ZAP-TV1x01 cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_pg600v2[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3,     0x0600 },
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_GOTVIEW2,  0x0600 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_pg600v2 = {
+	.type = IVTV_CARD_PG600V2,
+	.name = "Yuan PG600-2, GotView PCI DVD Lite, Club3D ZAP-TV1x01",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_CX25840,
+	.hw_audio = IVTV_HW_CX25840,
+	.hw_audio_ctrl = IVTV_HW_CX25840,
+	.hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE2 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1,
+		  CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  CX25840_AUDIO5       },
+		{ IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL },
+	},
+	.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
+	.tuners = {
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_XCEIVE_XC3028 },
+	},
+	.gpio_init = { .direction = 0x1000, .initial_value = 0x1000 }, /* tuner reset */
+	.pci_list = ivtv_pci_pg600v2,
+};
+#endif
+
+static const struct ivtv_card *ivtv_card_list[] = {
+	&ivtv_card_pvr250,
+	&ivtv_card_pvr350,
+	&ivtv_card_pvr150,
+	&ivtv_card_m179,
+	&ivtv_card_mpg600,
+	&ivtv_card_mpg160,
+	&ivtv_card_pg600,
+	&ivtv_card_avc2410,
+	&ivtv_card_avc2010,
+	&ivtv_card_tg5000tv,
+	&ivtv_card_va2000,
+	&ivtv_card_cx23416gyc,
+	&ivtv_card_gv_mvprx,
+	&ivtv_card_gv_mvprx2e,
+	&ivtv_card_gotview_pci_dvd,
+	&ivtv_card_gotview_pci_dvd2,
+	&ivtv_card_yuan_mpc622,
+	&ivtv_card_dctmvtvp1,
+#ifdef HAVE_XC3028
+	&ivtv_card_pg600v2,
+#endif
+
+	/* Variations of standard cards but with the same PCI IDs.
+	   These cards must come last in this list. */
+	&ivtv_card_pvr350_v1,
+	&ivtv_card_cx23416gyc_nogr,
+	&ivtv_card_cx23416gyc_nogrycs,
+};
+
+const struct ivtv_card *ivtv_get_card(u16 index)
+{
+	if (index >= ARRAY_SIZE(ivtv_card_list))
+		return NULL;
+	return ivtv_card_list[index];
+}
+
+int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input)
+{
+	const struct ivtv_card_video_input *card_input = itv->card->video_inputs + index;
+	static const char * const input_strs[] = {
+		"Tuner 1",
+		"S-Video 1",
+		"S-Video 2",
+		"Composite 1",
+		"Composite 2",
+		"Composite 3"
+	};
+
+	memset(input, 0, sizeof(*input));
+	if (index >= itv->nof_inputs)
+		return -EINVAL;
+	input->index = index;
+	strcpy(input->name, input_strs[card_input->video_type - 1]);
+	input->type = (card_input->video_type == IVTV_CARD_INPUT_VID_TUNER ?
+			V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA);
+	input->audioset = (1 << itv->nof_audio_inputs) - 1;
+	input->std = (input->type == V4L2_INPUT_TYPE_TUNER) ?
+				itv->tuner_std : V4L2_STD_ALL;
+	return 0;
+}
+
+int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output)
+{
+	const struct ivtv_card_output *card_output = itv->card->video_outputs + index;
+
+	memset(output, 0, sizeof(*output));
+	if (index >= itv->card->nof_outputs)
+		return -EINVAL;
+	output->index = index;
+	strcpy(output->name, card_output->name);
+	output->type = V4L2_OUTPUT_TYPE_ANALOG;
+	output->audioset = 1;
+	output->std = V4L2_STD_ALL;
+	return 0;
+}
+
+int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *audio)
+{
+	const struct ivtv_card_audio_input *aud_input = itv->card->audio_inputs + index;
+	static const char * const input_strs[] = {
+		"Tuner 1",
+		"Line In 1",
+		"Line In 2"
+	};
+
+	memset(audio, 0, sizeof(*audio));
+	if (index >= itv->nof_audio_inputs)
+		return -EINVAL;
+	strcpy(audio->name, input_strs[aud_input->audio_type - 1]);
+	audio->index = index;
+	audio->capability = V4L2_AUDCAP_STEREO;
+	return 0;
+}
+
+int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *aud_output)
+{
+	memset(aud_output, 0, sizeof(*aud_output));
+	if (itv->card->video_outputs == NULL || index != 0)
+		return -EINVAL;
+	strcpy(aud_output->name, "A/V Audio Out");
+	return 0;
+}
diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h
new file mode 100644
index 0000000..15012f8
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-cards.h
@@ -0,0 +1,207 @@
+/*
+    Functions to query card hardware
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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
+ */
+
+/* hardware flags */
+#define IVTV_HW_CX25840   (1 << 0)
+#define IVTV_HW_SAA7115   (1 << 1)
+#define IVTV_HW_SAA7127   (1 << 2)
+#define IVTV_HW_MSP34XX   (1 << 3)
+#define IVTV_HW_TUNER     (1 << 4)
+#define IVTV_HW_WM8775    (1 << 5)
+#define IVTV_HW_CS53L32A  (1 << 6)
+#define IVTV_HW_TVEEPROM  (1 << 7)
+#define IVTV_HW_SAA7114   (1 << 8)
+#define IVTV_HW_TVAUDIO   (1 << 9)
+#define IVTV_HW_UPD64031A (1 << 10)
+#define IVTV_HW_UPD6408X  (1 << 11)
+#define IVTV_HW_SAA717X   (1 << 12)
+#define IVTV_HW_WM8739    (1 << 13)
+#define IVTV_HW_GPIO      (1 << 14)
+
+#define IVTV_HW_SAA711X   (IVTV_HW_SAA7115 | IVTV_HW_SAA7114)
+
+/* video inputs */
+#define	IVTV_CARD_INPUT_VID_TUNER	1
+#define	IVTV_CARD_INPUT_SVIDEO1 	2
+#define	IVTV_CARD_INPUT_SVIDEO2 	3
+#define	IVTV_CARD_INPUT_COMPOSITE1 	4
+#define	IVTV_CARD_INPUT_COMPOSITE2 	5
+#define	IVTV_CARD_INPUT_COMPOSITE3 	6
+
+/* audio inputs */
+#define	IVTV_CARD_INPUT_AUD_TUNER	1
+#define	IVTV_CARD_INPUT_LINE_IN1 	2
+#define	IVTV_CARD_INPUT_LINE_IN2 	3
+
+#define IVTV_CARD_MAX_VIDEO_INPUTS 6
+#define IVTV_CARD_MAX_AUDIO_INPUTS 3
+#define IVTV_CARD_MAX_TUNERS  	   2
+
+/* SAA71XX HW inputs */
+#define IVTV_SAA71XX_COMPOSITE0 0
+#define IVTV_SAA71XX_COMPOSITE1 1
+#define IVTV_SAA71XX_COMPOSITE2 2
+#define IVTV_SAA71XX_COMPOSITE3 3
+#define IVTV_SAA71XX_COMPOSITE4 4
+#define IVTV_SAA71XX_COMPOSITE5 5
+#define IVTV_SAA71XX_SVIDEO0    6
+#define IVTV_SAA71XX_SVIDEO1    7
+#define IVTV_SAA71XX_SVIDEO2    8
+#define IVTV_SAA71XX_SVIDEO3    9
+
+/* SAA717X needs to mark the tuner input by ORing with this flag */
+#define IVTV_SAA717X_TUNER_FLAG 0x80
+
+/* Dummy HW input */
+#define IVTV_DUMMY_AUDIO        0
+
+/* GPIO HW inputs */
+#define IVTV_GPIO_TUNER   0
+#define IVTV_GPIO_LINE_IN 1
+
+/* SAA717X HW inputs */
+#define IVTV_SAA717X_IN0 0
+#define IVTV_SAA717X_IN1 1
+#define IVTV_SAA717X_IN2 2
+
+/* V4L2 capability aliases */
+#define IVTV_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \
+			  V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE | \
+			  V4L2_CAP_SLICED_VBI_CAPTURE)
+#define IVTV_CAP_DECODER (V4L2_CAP_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT | \
+			  V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_OVERLAY | V4L2_CAP_VIDEO_OUTPUT_POS)
+
+struct ivtv_card_video_input {
+	u8  video_type; 	/* video input type */
+	u8  audio_index;	/* index in ivtv_card_audio_input array */
+	u16 video_input;	/* hardware video input */
+};
+
+struct ivtv_card_audio_input {
+	u8  audio_type;		/* audio input type */
+	u32 audio_input;	/* hardware audio input */
+	u16 muxer_input;	/* hardware muxer input for boards with a
+				   multiplexer chip */
+};
+
+struct ivtv_card_output {
+	u8  name[32];
+	u16 video_output;  /* hardware video output */
+};
+
+struct ivtv_card_pci_info {
+	u16 device;
+	u16 subsystem_vendor;
+	u16 subsystem_device;
+};
+
+/* GPIO definitions */
+
+/* The mask is the set of bits used by the operation */
+
+struct ivtv_gpio_init { 	/* set initial GPIO DIR and OUT values */
+	u16 direction; 		/* DIR setting. Leave to 0 if no init is needed */
+	u16 initial_value;
+};
+
+struct ivtv_gpio_video_input { 	/* select tuner/line in input */
+	u16 mask; 		/* leave to 0 if not supported */
+	u16 tuner;
+	u16 composite;
+	u16 svideo;
+};
+
+struct ivtv_gpio_audio_input { 	/* select tuner/line in input */
+	u16 mask; 		/* leave to 0 if not supported */
+	u16 tuner;
+	u16 linein;
+	u16 radio;
+};
+
+struct ivtv_gpio_audio_mute {
+	u16 mask; 		/* leave to 0 if not supported */
+	u16 mute;		/* set this value to mute, 0 to unmute */
+};
+
+struct ivtv_gpio_audio_mode {
+	u16 mask; 		/* leave to 0 if not supported */
+	u16 mono; 		/* set audio to mono */
+	u16 stereo; 		/* set audio to stereo */
+	u16 lang1;		/* set audio to the first language */
+	u16 lang2;		/* set audio to the second language */
+	u16 both; 		/* both languages are output */
+};
+
+struct ivtv_gpio_audio_freq {
+	u16 mask; 		/* leave to 0 if not supported */
+	u16 f32000;
+	u16 f44100;
+	u16 f48000;
+};
+
+struct ivtv_gpio_audio_detect {
+	u16 mask; 		/* leave to 0 if not supported */
+	u16 stereo; 		/* if the input matches this value then
+				   stereo is detected */
+};
+
+struct ivtv_card_tuner {
+	v4l2_std_id std; 	/* standard for which the tuner is suitable */
+	int 	    tuner; 	/* tuner ID (from tuner.h) */
+};
+
+/* for card information/parameters */
+struct ivtv_card {
+	int type;
+	char *name;
+	u32 v4l2_capabilities;
+	u32 hw_video;		/* hardware used to process video */
+	u32 hw_audio;		/* hardware used to process audio */
+	u32 hw_audio_ctrl;	/* hardware used for the V4L2 controls (only 1 dev allowed) */
+	u32 hw_muxer;		/* hardware used to multiplex audio input */
+	u32 hw_all;		/* all hardware used by the board */
+	struct ivtv_card_video_input video_inputs[IVTV_CARD_MAX_VIDEO_INPUTS];
+	struct ivtv_card_audio_input audio_inputs[IVTV_CARD_MAX_AUDIO_INPUTS];
+	struct ivtv_card_audio_input radio_input;
+	int nof_outputs;
+	const struct ivtv_card_output *video_outputs;
+	u8 gr_config; 		/* config byte for the ghost reduction device */
+
+	/* GPIO card-specific settings */
+	struct ivtv_gpio_init 		gpio_init;
+	struct ivtv_gpio_video_input	gpio_video_input;
+	struct ivtv_gpio_audio_input 	gpio_audio_input;
+	struct ivtv_gpio_audio_mute 	gpio_audio_mute;
+	struct ivtv_gpio_audio_mode 	gpio_audio_mode;
+	struct ivtv_gpio_audio_freq 	gpio_audio_freq;
+	struct ivtv_gpio_audio_detect 	gpio_audio_detect;
+
+	struct ivtv_card_tuner tuners[IVTV_CARD_MAX_TUNERS];
+
+	/* list of device and subsystem vendor/devices that
+	   correspond to this card type. */
+	const struct ivtv_card_pci_info *pci_list;
+};
+
+int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input);
+int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output);
+int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *input);
+int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *output);
+const struct ivtv_card *ivtv_get_card(u16 index);
diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c
new file mode 100644
index 0000000..7a876c3
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-controls.c
@@ -0,0 +1,303 @@
+/*
+    ioctl control functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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 "ivtv-driver.h"
+#include "ivtv-cards.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-audio.h"
+#include "ivtv-i2c.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-controls.h"
+
+static const u32 user_ctrls[] = {
+	V4L2_CID_USER_CLASS,
+	V4L2_CID_BRIGHTNESS,
+	V4L2_CID_CONTRAST,
+	V4L2_CID_SATURATION,
+	V4L2_CID_HUE,
+	V4L2_CID_AUDIO_VOLUME,
+	V4L2_CID_AUDIO_BALANCE,
+	V4L2_CID_AUDIO_BASS,
+	V4L2_CID_AUDIO_TREBLE,
+	V4L2_CID_AUDIO_MUTE,
+	V4L2_CID_AUDIO_LOUDNESS,
+	0
+};
+
+static const u32 *ctrl_classes[] = {
+	user_ctrls,
+	cx2341x_mpeg_ctrls,
+	NULL
+};
+
+static int ivtv_queryctrl(struct ivtv *itv, struct v4l2_queryctrl *qctrl)
+{
+	const char *name;
+
+	IVTV_DEBUG_IOCTL("VIDIOC_QUERYCTRL(%08x)\n", qctrl->id);
+
+	qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
+	if (qctrl->id == 0)
+		return -EINVAL;
+
+	switch (qctrl->id) {
+	/* Standard V4L2 controls */
+	case V4L2_CID_BRIGHTNESS:
+	case V4L2_CID_HUE:
+	case V4L2_CID_SATURATION:
+	case V4L2_CID_CONTRAST:
+		if (itv->video_dec_func(itv, VIDIOC_QUERYCTRL, qctrl))
+			qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+		return 0;
+
+	case V4L2_CID_AUDIO_VOLUME:
+	case V4L2_CID_AUDIO_MUTE:
+	case V4L2_CID_AUDIO_BALANCE:
+	case V4L2_CID_AUDIO_BASS:
+	case V4L2_CID_AUDIO_TREBLE:
+	case V4L2_CID_AUDIO_LOUDNESS:
+		if (ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl))
+			qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+		return 0;
+
+	default:
+		if (cx2341x_ctrl_query(&itv->params, qctrl))
+			qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+		return 0;
+	}
+	strncpy(qctrl->name, name, sizeof(qctrl->name) - 1);
+	qctrl->name[sizeof(qctrl->name) - 1] = 0;
+	return 0;
+}
+
+static int ivtv_querymenu(struct ivtv *itv, struct v4l2_querymenu *qmenu)
+{
+	struct v4l2_queryctrl qctrl;
+
+	qctrl.id = qmenu->id;
+	ivtv_queryctrl(itv, &qctrl);
+	return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id));
+}
+
+static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
+{
+	s32 v = vctrl->value;
+
+	IVTV_DEBUG_IOCTL("VIDIOC_S_CTRL(%08x, %x)\n", vctrl->id, v);
+
+	switch (vctrl->id) {
+		/* Standard V4L2 controls */
+	case V4L2_CID_BRIGHTNESS:
+	case V4L2_CID_HUE:
+	case V4L2_CID_SATURATION:
+	case V4L2_CID_CONTRAST:
+		return itv->video_dec_func(itv, VIDIOC_S_CTRL, vctrl);
+
+	case V4L2_CID_AUDIO_VOLUME:
+	case V4L2_CID_AUDIO_MUTE:
+	case V4L2_CID_AUDIO_BALANCE:
+	case V4L2_CID_AUDIO_BASS:
+	case V4L2_CID_AUDIO_TREBLE:
+	case V4L2_CID_AUDIO_LOUDNESS:
+		return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl);
+
+	default:
+		IVTV_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
+{
+	IVTV_DEBUG_IOCTL("VIDIOC_G_CTRL(%08x)\n", vctrl->id);
+
+	switch (vctrl->id) {
+		/* Standard V4L2 controls */
+	case V4L2_CID_BRIGHTNESS:
+	case V4L2_CID_HUE:
+	case V4L2_CID_SATURATION:
+	case V4L2_CID_CONTRAST:
+		return itv->video_dec_func(itv, VIDIOC_G_CTRL, vctrl);
+
+	case V4L2_CID_AUDIO_VOLUME:
+	case V4L2_CID_AUDIO_MUTE:
+	case V4L2_CID_AUDIO_BALANCE:
+	case V4L2_CID_AUDIO_BASS:
+	case V4L2_CID_AUDIO_TREBLE:
+	case V4L2_CID_AUDIO_LOUDNESS:
+		return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl);
+	default:
+		IVTV_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fmt)
+{
+	if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE))
+		return -EINVAL;
+	if (atomic_read(&itv->capturing) > 0)
+		return -EBUSY;
+
+	/* First try to allocate sliced VBI buffers if needed. */
+	if (fmt && itv->vbi.sliced_mpeg_data[0] == NULL) {
+		int i;
+
+		for (i = 0; i < IVTV_VBI_FRAMES; i++) {
+			/* Yuck, hardcoded. Needs to be a define */
+			itv->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL);
+			if (itv->vbi.sliced_mpeg_data[i] == NULL) {
+				while (--i >= 0) {
+					kfree(itv->vbi.sliced_mpeg_data[i]);
+					itv->vbi.sliced_mpeg_data[i] = NULL;
+				}
+				return -ENOMEM;
+			}
+		}
+	}
+
+	itv->vbi.insert_mpeg = fmt;
+
+	if (itv->vbi.insert_mpeg == 0) {
+		return 0;
+	}
+	/* Need sliced data for mpeg insertion */
+	if (get_service_set(itv->vbi.sliced_in) == 0) {
+		if (itv->is_60hz)
+			itv->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525;
+		else
+			itv->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625;
+		expand_service_set(itv->vbi.sliced_in, itv->is_50hz);
+	}
+	return 0;
+}
+
+int ivtv_control_ioctls(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+	struct v4l2_control ctrl;
+
+	switch (cmd) {
+	case VIDIOC_QUERYMENU:
+		IVTV_DEBUG_IOCTL("VIDIOC_QUERYMENU\n");
+		return ivtv_querymenu(itv, arg);
+
+	case VIDIOC_QUERYCTRL:
+		return ivtv_queryctrl(itv, arg);
+
+	case VIDIOC_S_CTRL:
+		return ivtv_s_ctrl(itv, arg);
+
+	case VIDIOC_G_CTRL:
+		return ivtv_g_ctrl(itv, arg);
+
+	case VIDIOC_S_EXT_CTRLS:
+	{
+		struct v4l2_ext_controls *c = arg;
+
+		if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
+			int i;
+			int err = 0;
+
+			for (i = 0; i < c->count; i++) {
+				ctrl.id = c->controls[i].id;
+				ctrl.value = c->controls[i].value;
+				err = ivtv_s_ctrl(itv, &ctrl);
+				c->controls[i].value = ctrl.value;
+				if (err) {
+					c->error_idx = i;
+					break;
+				}
+			}
+			return err;
+		}
+		IVTV_DEBUG_IOCTL("VIDIOC_S_EXT_CTRLS\n");
+		if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+			struct cx2341x_mpeg_params p = itv->params;
+			int err = cx2341x_ext_ctrls(&p, arg, cmd);
+
+			if (err)
+				return err;
+
+			if (p.video_encoding != itv->params.video_encoding) {
+				int is_mpeg1 = p.video_encoding ==
+						V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
+				struct v4l2_format fmt;
+
+				/* fix videodecoder resolution */
+				fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+				fmt.fmt.pix.width = itv->params.width / (is_mpeg1 ? 2 : 1);
+				fmt.fmt.pix.height = itv->params.height;
+				itv->video_dec_func(itv, VIDIOC_S_FMT, &fmt);
+			}
+			err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p);
+			if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt) {
+				err = ivtv_setup_vbi_fmt(itv, p.stream_vbi_fmt);
+			}
+			itv->params = p;
+			itv->dualwatch_stereo_mode = p.audio_properties & 0x0300;
+			ivtv_audio_set_audio_clock_freq(itv, p.audio_properties & 0x03);
+			return err;
+		}
+		return -EINVAL;
+	}
+
+	case VIDIOC_G_EXT_CTRLS:
+	{
+		struct v4l2_ext_controls *c = arg;
+
+		if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
+			int i;
+			int err = 0;
+
+			for (i = 0; i < c->count; i++) {
+				ctrl.id = c->controls[i].id;
+				ctrl.value = c->controls[i].value;
+				err = ivtv_g_ctrl(itv, &ctrl);
+				c->controls[i].value = ctrl.value;
+				if (err) {
+					c->error_idx = i;
+					break;
+				}
+			}
+			return err;
+		}
+		IVTV_DEBUG_IOCTL("VIDIOC_G_EXT_CTRLS\n");
+		if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
+			return cx2341x_ext_ctrls(&itv->params, arg, cmd);
+		return -EINVAL;
+	}
+
+	case VIDIOC_TRY_EXT_CTRLS:
+	{
+		struct v4l2_ext_controls *c = arg;
+
+		IVTV_DEBUG_IOCTL("VIDIOC_TRY_EXT_CTRLS\n");
+		if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
+			return cx2341x_ext_ctrls(&itv->params, arg, cmd);
+		return -EINVAL;
+	}
+
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
diff --git a/drivers/media/video/ivtv/ivtv-controls.h b/drivers/media/video/ivtv/ivtv-controls.h
new file mode 100644
index 0000000..5a11149
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-controls.h
@@ -0,0 +1,21 @@
+/*
+    ioctl control functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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
+ */
+
+int ivtv_control_ioctls(struct ivtv *itv, unsigned int cmd, void *arg);
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
new file mode 100644
index 0000000..45b9328
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -0,0 +1,1374 @@
+/*
+    ivtv driver initialization and card probing
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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
+ */
+
+/* Main Driver file for the ivtv project:
+ * Driver for the Conexant CX23415/CX23416 chip.
+ * Author: Kevin Thayer (nufan_wfk at yahoo.com)
+ * License: GPL
+ * http://www.ivtvdriver.org
+ *
+ * -----
+ * MPG600/MPG160 support by  T.Adachi <tadachi@tadachi-net.com>
+ *                      and Takeru KOMORIYA<komoriya@paken.org>
+ *
+ * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
+ *                using information provided by Jiun-Kuei Jung @ AVerMedia.
+ *
+ * Kurouto Sikou CX23416GYC-STVLP tested by K.Ohta <alpha292@bremen.or.jp>
+ *                using information from T.Adachi,Takeru KOMORIYA and others :-)
+ *
+ * Nagase TRANSGEAR 5000TV, Aopen VA2000MAX-STN6 and I/O data GV-MVP/RX
+ *                version by T.Adachi. Special thanks  Mr.Suzuki
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-version.h"
+#include "ivtv-fileops.h"
+#include "ivtv-i2c.h"
+#include "ivtv-firmware.h"
+#include "ivtv-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-streams.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-cards.h"
+#include "ivtv-vbi.h"
+#include "ivtv-audio.h"
+#include "ivtv-gpio.h"
+#include "ivtv-yuv.h"
+
+#include <linux/vermagic.h>
+#include <media/tveeprom.h>
+#include <media/v4l2-chip-ident.h>
+
+/* var to keep track of the number of array elements in use */
+int ivtv_cards_active = 0;
+
+/* If you have already X v4l cards, then set this to X. This way
+   the device numbers stay matched. Example: you have a WinTV card
+   without radio and a PVR-350 with. Normally this would give a
+   video1 device together with a radio0 device for the PVR. By
+   setting this to 1 you ensure that radio0 is now also radio1. */
+int ivtv_first_minor = 0;
+
+/* Master variable for all ivtv info */
+struct ivtv *ivtv_cards[IVTV_MAX_CARDS];
+
+/* Protects ivtv_cards_active */
+spinlock_t ivtv_cards_lock = SPIN_LOCK_UNLOCKED;
+
+/* add your revision and whatnot here */
+static struct pci_device_id ivtv_pci_tbl[] __devinitdata = {
+	{PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV16,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci,ivtv_pci_tbl);
+
+const u32 yuv_offset[4] = {
+	IVTV_YUV_BUFFER_OFFSET,
+	IVTV_YUV_BUFFER_OFFSET_1,
+	IVTV_YUV_BUFFER_OFFSET_2,
+	IVTV_YUV_BUFFER_OFFSET_3
+};
+
+/* Parameter declarations */
+static int cardtype[IVTV_MAX_CARDS];
+static int tuner[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
+static int radio[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
+
+static int cardtype_c = 1;
+static int tuner_c = 1;
+static int radio_c = 1;
+static char pal[] = "--";
+static char secam[] = "--";
+static char ntsc[] = "-";
+
+/* Buffers */
+static int enc_mpg_buffers = IVTV_DEFAULT_ENC_MPG_BUFFERS;
+static int enc_yuv_buffers = IVTV_DEFAULT_ENC_YUV_BUFFERS;
+static int enc_vbi_buffers = IVTV_DEFAULT_ENC_VBI_BUFFERS;
+static int enc_pcm_buffers = IVTV_DEFAULT_ENC_PCM_BUFFERS;
+static int dec_mpg_buffers = IVTV_DEFAULT_DEC_MPG_BUFFERS;
+static int dec_yuv_buffers = IVTV_DEFAULT_DEC_YUV_BUFFERS;
+static int dec_vbi_buffers = IVTV_DEFAULT_DEC_VBI_BUFFERS;
+
+static int ivtv_yuv_mode = 0;
+static int ivtv_yuv_threshold=-1;
+static int ivtv_pci_latency = 1;
+
+int ivtv_debug = 0;
+
+static int newi2c = -1;
+
+module_param_array(tuner, int, &tuner_c, 0644);
+module_param_array(radio, bool, &radio_c, 0644);
+module_param_array(cardtype, int, &cardtype_c, 0644);
+module_param_string(pal, pal, sizeof(pal), 0644);
+module_param_string(secam, secam, sizeof(secam), 0644);
+module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
+module_param_named(debug,ivtv_debug, int, 0644);
+module_param(ivtv_pci_latency, int, 0644);
+module_param(ivtv_yuv_mode, int, 0644);
+module_param(ivtv_yuv_threshold, int, 0644);
+module_param(ivtv_first_minor, int, 0644);
+
+module_param(enc_mpg_buffers, int, 0644);
+module_param(enc_yuv_buffers, int, 0644);
+module_param(enc_vbi_buffers, int, 0644);
+module_param(enc_pcm_buffers, int, 0644);
+module_param(dec_mpg_buffers, int, 0644);
+module_param(dec_yuv_buffers, int, 0644);
+module_param(dec_vbi_buffers, int, 0644);
+
+module_param(newi2c, int, 0644);
+
+MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
+			"\t\t\tsee tuner.h for values");
+MODULE_PARM_DESC(radio,
+		 "Enable or disable the radio. Use only if autodetection\n"
+		 "\t\t\tfails. 0 = disable, 1 = enable");
+MODULE_PARM_DESC(cardtype,
+		 "Only use this option if your card is not detected properly.\n"
+		 "\t\tSpecify card type:\n"
+		 "\t\t\t 1 = WinTV PVR 250\n"
+		 "\t\t\t 2 = WinTV PVR 350\n"
+		 "\t\t\t 3 = WinTV PVR-150 or PVR-500\n"
+		 "\t\t\t 4 = AVerMedia M179\n"
+		 "\t\t\t 5 = YUAN MPG600/Kuroutoshikou iTVC16-STVLP\n"
+		 "\t\t\t 6 = YUAN MPG160/Kuroutoshikou iTVC15-STVLP\n"
+		 "\t\t\t 7 = YUAN PG600/DIAMONDMM PVR-550 (CX Falcon 2)\n"
+		 "\t\t\t 8 = Adaptec AVC-2410\n"
+		 "\t\t\t 9 = Adaptec AVC-2010\n"
+		 "\t\t\t10 = NAGASE TRANSGEAR 5000TV\n"
+		 "\t\t\t11 = AOpen VA2000MAX-STN6\n"
+		 "\t\t\t12 = YUAN MPG600GR/Kuroutoshikou CX23416GYC-STVLP\n"
+		 "\t\t\t13 = I/O Data GV-MVP/RX\n"
+		 "\t\t\t14 = I/O Data GV-MVP/RX2E\n"
+		 "\t\t\t15 = GOTVIEW PCI DVD\n"
+		 "\t\t\t16 = GOTVIEW PCI DVD2 Deluxe\n"
+		 "\t\t\t17 = Yuan MPC622\n"
+		 "\t\t\t18 = Digital Cowboy DCT-MTVP1\n"
+#ifdef HAVE_XC3028
+		 "\t\t\t19 = Yuan PG600V2/GotView PCI DVD Lite/Club3D ZAP-TV1x01\n"
+#endif
+		 "\t\t\t 0 = Autodetect (default)\n"
+		 "\t\t\t-1 = Ignore this card\n\t\t");
+MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60");
+MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC");
+MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K");
+MODULE_PARM_DESC(debug,
+		 "Debug level (bitmask). Default: errors only\n"
+		 "\t\t\t(debug = 511 gives full debugging)");
+MODULE_PARM_DESC(ivtv_pci_latency,
+		 "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
+		 "\t\t\tDefault: Yes");
+MODULE_PARM_DESC(ivtv_yuv_mode,
+		 "Specify the yuv playback mode:\n"
+		 "\t\t\t0 = interlaced\n\t\t\t1 = progressive\n\t\t\t2 = auto\n"
+		 "\t\t\tDefault: 0 (interlaced)");
+MODULE_PARM_DESC(ivtv_yuv_threshold,
+		 "If ivtv_yuv_mode is 2 (auto) then playback content as\n\t\tprogressive if src height <= ivtv_yuvthreshold\n"
+		 "\t\t\tDefault: 480");;
+MODULE_PARM_DESC(enc_mpg_buffers,
+		 "Encoder MPG Buffers (in MB)\n"
+		 "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_MPG_BUFFERS));
+MODULE_PARM_DESC(enc_yuv_buffers,
+		 "Encoder YUV Buffers (in MB)\n"
+		 "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_YUV_BUFFERS));
+MODULE_PARM_DESC(enc_vbi_buffers,
+		 "Encoder VBI Buffers (in MB)\n"
+		 "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_VBI_BUFFERS));
+MODULE_PARM_DESC(enc_pcm_buffers,
+		 "Encoder PCM buffers (in MB)\n"
+		 "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_PCM_BUFFERS));
+MODULE_PARM_DESC(dec_mpg_buffers,
+		 "Decoder MPG buffers (in MB)\n"
+		 "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_MPG_BUFFERS));
+MODULE_PARM_DESC(dec_yuv_buffers,
+		 "Decoder YUV buffers (in MB)\n"
+		 "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_YUV_BUFFERS));
+MODULE_PARM_DESC(dec_vbi_buffers,
+		 "Decoder VBI buffers (in MB)\n"
+		 "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_VBI_BUFFERS));
+MODULE_PARM_DESC(newi2c,
+		 "Use new I2C implementation\n"
+		 "\t\t\t-1 is autodetect, 0 is off, 1 is on\n"
+		 "\t\t\tDefault is autodetect");
+
+MODULE_PARM_DESC(ivtv_first_minor, "Set minor assigned to first card");
+
+MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil");
+MODULE_DESCRIPTION("CX23415/CX23416 driver");
+MODULE_SUPPORTED_DEVICE
+    ("CX23415/CX23416 MPEG2 encoder (WinTV PVR-150/250/350/500,\n"
+		"\t\t\tYuan MPG series and similar)");
+MODULE_LICENSE("GPL");
+
+MODULE_VERSION(IVTV_VERSION);
+
+void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask)
+{
+	itv->irqmask &= ~mask;
+	write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK);
+}
+
+void ivtv_set_irq_mask(struct ivtv *itv, u32 mask)
+{
+	itv->irqmask |= mask;
+	write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK);
+}
+
+int ivtv_set_output_mode(struct ivtv *itv, int mode)
+{
+    int old_mode;
+
+    spin_lock(&itv->lock);
+    old_mode = itv->output_mode;
+    if (old_mode == 0)
+	itv->output_mode = old_mode = mode;
+    spin_unlock(&itv->lock);
+    return old_mode;
+}
+
+struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv)
+{
+	switch (itv->output_mode) {
+	case OUT_MPG:
+		return &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+	case OUT_YUV:
+		return &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+	default:
+		return NULL;
+	}
+}
+
+int ivtv_waitq(wait_queue_head_t *waitq)
+{
+	DEFINE_WAIT(wait);
+
+	prepare_to_wait(waitq, &wait, TASK_INTERRUPTIBLE);
+	schedule();
+	finish_wait(waitq, &wait);
+	return signal_pending(current) ? -EINTR : 0;
+}
+
+/* Generic utility functions */
+int ivtv_sleep_timeout(int timeout, int intr)
+{
+	int ret;
+
+	do {
+		set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+		timeout = schedule_timeout(timeout);
+		if (intr && (ret = signal_pending(current)))
+			return ret;
+	} while (timeout);
+	return 0;
+}
+
+/* Release ioremapped memory */
+static void ivtv_iounmap(struct ivtv *itv)
+{
+	if (itv == NULL)
+		return;
+
+	/* Release registers memory */
+	if (itv->reg_mem != NULL) {
+		IVTV_DEBUG_INFO("releasing reg_mem\n");
+		iounmap(itv->reg_mem);
+		itv->reg_mem = NULL;
+	}
+	/* Release io memory */
+	if (itv->has_cx23415 && itv->dec_mem != NULL) {
+		IVTV_DEBUG_INFO("releasing dec_mem\n");
+		iounmap(itv->dec_mem);
+	}
+	itv->dec_mem = NULL;
+
+	/* Release io memory */
+	if (itv->enc_mem != NULL) {
+		IVTV_DEBUG_INFO("releasing enc_mem\n");
+		iounmap(itv->enc_mem);
+		itv->enc_mem = NULL;
+	}
+}
+
+/* Hauppauge card? get values from tveeprom */
+void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv)
+{
+	u8 eedata[256];
+
+	itv->i2c_client.addr = 0xA0 >> 1;
+	tveeprom_read(&itv->i2c_client, eedata, sizeof(eedata));
+	tveeprom_hauppauge_analog(&itv->i2c_client, tv, eedata);
+}
+
+static void ivtv_process_eeprom(struct ivtv *itv)
+{
+	struct tveeprom tv;
+	int pci_slot = PCI_SLOT(itv->dev->devfn);
+
+	ivtv_read_eeprom(itv, &tv);
+
+	/* Many thanks to Steven Toth from Hauppauge for providing the
+	   model numbers */
+	switch (tv.model) {
+		/* In a few cases the PCI subsystem IDs do not correctly
+		   identify the card. A better method is to check the
+		   model number from the eeprom instead. */
+		case 32000 ... 32999:
+		case 48000 ... 48099:  /* 48??? range are PVR250s with a cx23415 */
+		case 48400 ... 48599:
+			itv->card = ivtv_get_card(IVTV_CARD_PVR_250);
+			break;
+		case 48100 ... 48399:
+		case 48600 ... 48999:
+			itv->card = ivtv_get_card(IVTV_CARD_PVR_350);
+			break;
+		case 23000 ... 23999:  /* PVR500 */
+		case 25000 ... 25999:  /* Low profile PVR150 */
+		case 26000 ... 26999:  /* Regular PVR150 */
+			itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
+			break;
+		case 0:
+			IVTV_ERR("Invalid EEPROM\n");
+			return;
+		default:
+			IVTV_ERR("Unknown model %d, defaulting to PVR-150\n", tv.model);
+			itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
+			break;
+	}
+
+	switch (tv.model) {
+		/* Old style PVR350 (with an saa7114) uses this input for
+		   the tuner. */
+		case 48254:
+			itv->card = ivtv_get_card(IVTV_CARD_PVR_350_V1);
+			break;
+		default:
+			break;
+	}
+
+	itv->v4l2_cap = itv->card->v4l2_capabilities;
+	itv->card_name = itv->card->name;
+
+	/* If this is a PVR500 then it should be possible to detect whether it is the
+	   first or second unit by looking at the subsystem device ID: is bit 4 is
+	   set, then it is the second unit (according to info from Hauppauge).
+
+	   However, while this works for most cards, I have seen a few PVR500 cards
+	   where both units have the same subsystem ID.
+
+	   So instead I look at the reported 'PCI slot' (which is the slot on the PVR500
+	   PCI bridge) and if it is 8, then it is assumed to be the first unit, otherwise
+	   it is the second unit. It is possible that it is a different slot when ivtv is
+	   used in Xen, in that case I ignore this card here. The worst that can happen
+	   is that the card presents itself with a non-working radio device.
+
+	   This detection is needed since the eeprom reports incorrectly that a radio is
+	   present on the second unit. */
+	if (tv.model / 1000 == 23) {
+		itv->card_name = "WinTV PVR 500";
+		if (pci_slot == 8 || pci_slot == 9) {
+			int is_first = (pci_slot & 1) == 0;
+
+			itv->card_name = is_first ? "WinTV PVR 500 (unit #1)" :
+						    "WinTV PVR 500 (unit #2)";
+			if (!is_first) {
+				IVTV_INFO("Correcting tveeprom data: no radio present on second unit\n");
+				tv.has_radio = 0;
+			}
+		}
+	}
+	IVTV_INFO("Autodetected %s\n", itv->card_name);
+
+	switch (tv.tuner_hauppauge_model) {
+		case 85:
+		case 99:
+		case 112:
+			itv->pvr150_workaround = 1;
+			break;
+		default:
+			break;
+	}
+	if (tv.tuner_type == TUNER_ABSENT)
+		IVTV_ERR("tveeprom cannot autodetect tuner!");
+
+	if (itv->options.tuner == -1)
+		itv->options.tuner = tv.tuner_type;
+	if (itv->options.radio == -1)
+		itv->options.radio = (tv.has_radio != 0);
+	/* only enable newi2c if an IR blaster is present */
+	/* FIXME: for 2.6.20 the test against 2 should be removed */
+	if (itv->options.newi2c == -1 && tv.has_ir != -1 && tv.has_ir != 2) {
+		itv->options.newi2c = (tv.has_ir & 2) ? 1 : 0;
+		if (itv->options.newi2c) {
+		    IVTV_INFO("reopen i2c bus for IR-blaster support\n");
+		    exit_ivtv_i2c(itv);
+		    init_ivtv_i2c(itv);
+		}
+	}
+
+	if (itv->std != 0)
+		/* user specified tuner standard */
+		return;
+
+	/* autodetect tuner standard */
+	if (tv.tuner_formats & V4L2_STD_PAL) {
+		IVTV_DEBUG_INFO("PAL tuner detected\n");
+		itv->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+	} else if (tv.tuner_formats & V4L2_STD_NTSC) {
+		IVTV_DEBUG_INFO("NTSC tuner detected\n");
+		itv->std |= V4L2_STD_NTSC_M;
+	} else if (tv.tuner_formats & V4L2_STD_SECAM) {
+		IVTV_DEBUG_INFO("SECAM tuner detected\n");
+		itv->std |= V4L2_STD_SECAM_L;
+	} else {
+		IVTV_INFO("No tuner detected, default to NTSC-M\n");
+		itv->std |= V4L2_STD_NTSC_M;
+	}
+}
+
+static v4l2_std_id ivtv_parse_std(struct ivtv *itv)
+{
+	switch (pal[0]) {
+		case '6':
+			return V4L2_STD_PAL_60;
+		case 'b':
+		case 'B':
+		case 'g':
+		case 'G':
+			return V4L2_STD_PAL_BG;
+		case 'h':
+		case 'H':
+			return V4L2_STD_PAL_H;
+		case 'n':
+		case 'N':
+			if (pal[1] == 'c' || pal[1] == 'C')
+				return V4L2_STD_PAL_Nc;
+			return V4L2_STD_PAL_N;
+		case 'i':
+		case 'I':
+			return V4L2_STD_PAL_I;
+		case 'd':
+		case 'D':
+		case 'k':
+		case 'K':
+			return V4L2_STD_PAL_DK;
+		case 'M':
+		case 'm':
+			return V4L2_STD_PAL_M;
+		case '-':
+			break;
+		default:
+			IVTV_WARN("pal= argument not recognised\n");
+			return 0;
+	}
+
+	switch (secam[0]) {
+		case 'b':
+		case 'B':
+		case 'g':
+		case 'G':
+		case 'h':
+		case 'H':
+			return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
+		case 'd':
+		case 'D':
+		case 'k':
+		case 'K':
+			return V4L2_STD_SECAM_DK;
+		case 'l':
+		case 'L':
+			if (secam[1] == 'C' || secam[1] == 'c')
+				return V4L2_STD_SECAM_LC;
+			return V4L2_STD_SECAM_L;
+		case '-':
+			break;
+		default:
+			IVTV_WARN("secam= argument not recognised\n");
+			return 0;
+	}
+
+	switch (ntsc[0]) {
+		case 'm':
+		case 'M':
+			return V4L2_STD_NTSC_M;
+		case 'j':
+		case 'J':
+			return V4L2_STD_NTSC_M_JP;
+		case 'k':
+		case 'K':
+			return V4L2_STD_NTSC_M_KR;
+		case '-':
+			break;
+		default:
+			IVTV_WARN("ntsc= argument not recognised\n");
+			return 0;
+	}
+
+	/* no match found */
+	return 0;
+}
+
+static void ivtv_process_options(struct ivtv *itv)
+{
+	const char *chipname;
+	int i, j;
+
+	itv->options.megabytes[IVTV_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
+	itv->options.megabytes[IVTV_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers;
+	itv->options.megabytes[IVTV_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers;
+	itv->options.megabytes[IVTV_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
+	itv->options.megabytes[IVTV_DEC_STREAM_TYPE_MPG] = dec_mpg_buffers;
+	itv->options.megabytes[IVTV_DEC_STREAM_TYPE_YUV] = dec_yuv_buffers;
+	itv->options.megabytes[IVTV_DEC_STREAM_TYPE_VBI] = dec_vbi_buffers;
+	itv->options.cardtype = cardtype[itv->num];
+	itv->options.tuner = tuner[itv->num];
+	itv->options.radio = radio[itv->num];
+	itv->options.newi2c = newi2c;
+
+	itv->std = ivtv_parse_std(itv);
+	itv->has_cx23415 = (itv->dev->device == PCI_DEVICE_ID_IVTV15);
+	chipname = itv->has_cx23415 ? "cx23415" : "cx23416";
+	if (itv->options.cardtype == -1) {
+		IVTV_INFO("Ignore card (detected %s based chip)\n", chipname);
+		return;
+	}
+	if ((itv->card = ivtv_get_card(itv->options.cardtype - 1))) {
+		IVTV_INFO("User specified %s card (detected %s based chip)\n",
+				itv->card->name, chipname);
+	} else if (itv->options.cardtype != 0) {
+		IVTV_ERR("Unknown user specified type, trying to autodetect card\n");
+	}
+	if (itv->card == NULL) {
+		if (itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE ||
+		    itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT1 ||
+		    itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT2) {
+			itv->card = ivtv_get_card(itv->has_cx23415 ? IVTV_CARD_PVR_350 : IVTV_CARD_PVR_150);
+			IVTV_INFO("Autodetected Hauppauge card (%s based)\n",
+					chipname);
+		}
+	}
+	if (itv->card == NULL) {
+		for (i = 0; (itv->card = ivtv_get_card(i)); i++) {
+			if (itv->card->pci_list == NULL)
+				continue;
+			for (j = 0; itv->card->pci_list[j].device; j++) {
+				if (itv->dev->device !=
+				    itv->card->pci_list[j].device)
+					continue;
+				if (itv->dev->subsystem_vendor !=
+				    itv->card->pci_list[j].subsystem_vendor)
+					continue;
+				if (itv->dev->subsystem_device !=
+				    itv->card->pci_list[j].subsystem_device)
+					continue;
+				IVTV_INFO("Autodetected %s card (%s based)\n",
+						itv->card->name, chipname);
+				goto done;
+			}
+		}
+	}
+done:
+
+	if (itv->card == NULL) {
+		itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
+		IVTV_ERR("Unknown card: vendor/device: %04x/%04x\n",
+		     itv->dev->vendor, itv->dev->device);
+		IVTV_ERR("              subsystem vendor/device: %04x/%04x\n",
+		     itv->dev->subsystem_vendor, itv->dev->subsystem_device);
+		IVTV_ERR("              %s based\n", chipname);
+		IVTV_ERR("Defaulting to %s card\n", itv->card->name);
+		IVTV_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
+		IVTV_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n");
+		IVTV_ERR("Prefix your subject line with [UNKNOWN CARD].\n");
+	}
+	itv->v4l2_cap = itv->card->v4l2_capabilities;
+	itv->card_name = itv->card->name;
+}
+
+/* Precondition: the ivtv structure has been memset to 0. Only
+   the dev and num fields have been filled in.
+   No assumptions on the card type may be made here (see ivtv_init_struct2
+   for that).
+ */
+static int __devinit ivtv_init_struct1(struct ivtv *itv)
+{
+	itv->base_addr = pci_resource_start(itv->dev, 0);
+	itv->enc_mbox.max_mbox = 2; /* the encoder has 3 mailboxes (0-2) */
+	itv->dec_mbox.max_mbox = 1; /* the decoder has 2 mailboxes (0-1) */
+
+	mutex_init(&itv->i2c_bus_lock);
+	mutex_init(&itv->udma.lock);
+
+	spin_lock_init(&itv->lock);
+	spin_lock_init(&itv->dma_reg_lock);
+
+	itv->irq_work_queues = create_workqueue(itv->name);
+	if (itv->irq_work_queues == NULL) {
+		IVTV_ERR("Could not create ivtv workqueue\n");
+		return -1;
+	}
+
+	INIT_WORK(&itv->irq_work_queue, ivtv_irq_work_handler);
+
+	/* start counting open_id at 1 */
+	itv->open_id = 1;
+
+	/* Initial settings */
+	cx2341x_fill_defaults(&itv->params);
+	itv->params.port = CX2341X_PORT_MEMORY;
+	itv->params.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
+	init_waitqueue_head(&itv->cap_w);
+	init_waitqueue_head(&itv->event_waitq);
+	init_waitqueue_head(&itv->vsync_waitq);
+	init_waitqueue_head(&itv->dma_waitq);
+	init_timer(&itv->dma_timer);
+	itv->dma_timer.function = ivtv_unfinished_dma;
+	itv->dma_timer.data = (unsigned long)itv;
+
+	itv->cur_dma_stream = -1;
+	itv->audio_stereo_mode = AUDIO_STEREO;
+	itv->audio_bilingual_mode = AUDIO_MONO_LEFT;
+
+	/* Ctrls */
+	itv->speed = 1000;
+
+	/* VBI */
+	itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+	itv->vbi.sliced_in = &itv->vbi.in.fmt.sliced;
+
+	/* OSD */
+	itv->osd_global_alpha_state = 1;
+	itv->osd_global_alpha = 255;
+
+	/* YUV */
+	atomic_set(&itv->yuv_info.next_dma_frame, -1);
+	itv->yuv_info.lace_mode = ivtv_yuv_mode;
+	itv->yuv_info.lace_threshold = ivtv_yuv_threshold;
+	return 0;
+}
+
+/* Second initialization part. Here the card type has been
+   autodetected. */
+static void __devinit ivtv_init_struct2(struct ivtv *itv)
+{
+	int i;
+
+	for (i = 0; i < IVTV_CARD_MAX_VIDEO_INPUTS; i++)
+		if (itv->card->video_inputs[i].video_type == 0)
+			break;
+	itv->nof_inputs = i;
+	for (i = 0; i < IVTV_CARD_MAX_AUDIO_INPUTS; i++)
+		if (itv->card->audio_inputs[i].audio_type == 0)
+			break;
+	itv->nof_audio_inputs = i;
+
+	/* 0x00EF = saa7114(239) 0x00F0 = saa7115(240) 0x0106 = micro */
+	if (itv->card->hw_all & (IVTV_HW_SAA7115 | IVTV_HW_SAA717X))
+		itv->digitizer = 0xF1;
+	else if (itv->card->hw_all & IVTV_HW_SAA7114)
+		itv->digitizer = 0xEF;
+	else /* cx25840 */
+		itv->digitizer = 0x140;
+
+	if (itv->card->hw_all & IVTV_HW_CX25840) {
+		itv->vbi.sliced_size = 288;  /* multiple of 16, real size = 284 */
+	} else {
+		itv->vbi.sliced_size = 64;   /* multiple of 16, real size = 52 */
+	}
+
+	/* Find tuner input */
+	for (i = 0; i < itv->nof_inputs; i++) {
+		if (itv->card->video_inputs[i].video_type ==
+				IVTV_CARD_INPUT_VID_TUNER)
+			break;
+	}
+	if (i == itv->nof_inputs)
+		i = 0;
+	itv->active_input = i;
+	itv->audio_input = itv->card->video_inputs[i].audio_index;
+	if (itv->card->hw_all & IVTV_HW_CX25840)
+		itv->video_dec_func = ivtv_cx25840;
+	else if (itv->card->hw_all & IVTV_HW_SAA717X)
+		itv->video_dec_func = ivtv_saa717x;
+	else
+		itv->video_dec_func = ivtv_saa7115;
+}
+
+static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev,
+			  const struct pci_device_id *pci_id)
+{
+	u16 cmd;
+	unsigned char pci_latency;
+
+	IVTV_DEBUG_INFO("Enabling pci device\n");
+
+	if (pci_enable_device(dev)) {
+		IVTV_ERR("Can't enable device %d!\n", itv->num);
+		return -EIO;
+	}
+	if (pci_set_dma_mask(dev, 0xffffffff)) {
+		IVTV_ERR("No suitable DMA available on card %d.\n", itv->num);
+		return -EIO;
+	}
+	if (!request_mem_region(itv->base_addr, IVTV_ENCODER_SIZE, "ivtv encoder")) {
+		IVTV_ERR("Cannot request encoder memory region on card %d.\n", itv->num);
+		return -EIO;
+	}
+
+	if (!request_mem_region(itv->base_addr + IVTV_REG_OFFSET,
+				IVTV_REG_SIZE, "ivtv registers")) {
+		IVTV_ERR("Cannot request register memory region on card %d.\n", itv->num);
+		release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+		return -EIO;
+	}
+
+	if (itv->has_cx23415 &&
+	    !request_mem_region(itv->base_addr + IVTV_DECODER_OFFSET,
+				IVTV_DECODER_SIZE, "ivtv decoder")) {
+		IVTV_ERR("Cannot request decoder memory region on card %d.\n", itv->num);
+		release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+		release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+		return -EIO;
+	}
+
+	/* Check for bus mastering */
+	pci_read_config_word(dev, PCI_COMMAND, &cmd);
+	if (!(cmd & PCI_COMMAND_MASTER)) {
+		IVTV_DEBUG_INFO("Attempting to enable Bus Mastering\n");
+		pci_set_master(dev);
+		pci_read_config_word(dev, PCI_COMMAND, &cmd);
+		if (!(cmd & PCI_COMMAND_MASTER)) {
+			IVTV_ERR("Bus Mastering is not enabled\n");
+			return -ENXIO;
+		}
+	}
+	IVTV_DEBUG_INFO("Bus Mastering Enabled.\n");
+
+	pci_read_config_byte(dev, PCI_CLASS_REVISION, &itv->card_rev);
+	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency);
+
+	if (pci_latency < 64 && ivtv_pci_latency) {
+		IVTV_INFO("Unreasonably low latency timer, "
+			       "setting to 64 (was %d)\n", pci_latency);
+		pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
+		pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency);
+	}
+	/* This config space value relates to DMA latencies. The
+	   default value 0x8080 is too low however and will lead
+	   to DMA errors. 0xffff is the max value which solves
+	   these problems. */
+	pci_write_config_dword(dev, 0x40, 0xffff);
+
+	IVTV_DEBUG_INFO("%d (rev %d) at %02x:%02x.%x, "
+		   "irq: %d, latency: %d, memory: 0x%lx\n",
+		   itv->dev->device, itv->card_rev, dev->bus->number,
+		   PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
+		   itv->dev->irq, pci_latency, (unsigned long)itv->base_addr);
+
+	return 0;
+}
+
+static void ivtv_request_module(struct ivtv *itv, const char *name)
+{
+	if (request_module(name) != 0) {
+		IVTV_ERR("Failed to load module %s\n", name);
+	} else {
+		IVTV_DEBUG_INFO("Loaded module %s\n", name);
+	}
+}
+
+static void ivtv_load_and_init_modules(struct ivtv *itv)
+{
+	struct v4l2_control ctrl;
+	u32 hw = itv->card->hw_all;
+	int i;
+
+	/* load modules */
+#ifndef CONFIG_VIDEO_TUNER
+	if (hw & IVTV_HW_TUNER) {
+		ivtv_request_module(itv, "tuner");
+#ifdef HAVE_XC3028
+		if (itv->options.tuner == TUNER_XCEIVE_XC3028)
+			ivtv_request_module(itv, "xc3028-tuner");
+#endif
+	}
+#endif
+#ifndef CONFIG_VIDEO_CX25840
+	if (hw & IVTV_HW_CX25840)
+		ivtv_request_module(itv, "cx25840");
+#endif
+#ifndef CONFIG_VIDEO_SAA711X
+	if (hw & IVTV_HW_SAA711X)
+		ivtv_request_module(itv, "saa7115");
+#endif
+#ifndef CONFIG_VIDEO_SAA7127
+	if (hw & IVTV_HW_SAA7127)
+		ivtv_request_module(itv, "saa7127");
+#endif
+	if (hw & IVTV_HW_SAA717X)
+		ivtv_request_module(itv, "saa717x");
+#ifndef CONFIG_VIDEO_UPD64031A
+	if (hw & IVTV_HW_UPD64031A)
+		ivtv_request_module(itv, "upd64031a");
+#endif
+#ifndef CONFIG_VIDEO_UPD64083
+	if (hw & IVTV_HW_UPD6408X)
+		ivtv_request_module(itv, "upd64083");
+#endif
+#ifndef CONFIG_VIDEO_MSP3400
+	if (hw & IVTV_HW_MSP34XX)
+		ivtv_request_module(itv, "msp3400");
+#endif
+	if (hw & IVTV_HW_TVAUDIO)
+		ivtv_request_module(itv, "tvaudio");
+#ifndef CONFIG_VIDEO_WM8775
+	if (hw & IVTV_HW_WM8775)
+		ivtv_request_module(itv, "wm8775");
+#endif
+#ifndef CONFIG_VIDEO_WM8739
+	if (hw & IVTV_HW_WM8739)
+		ivtv_request_module(itv, "wm8739");
+#endif
+#ifndef CONFIG_VIDEO_CS53L32A
+	if (hw & IVTV_HW_CS53L32A)
+		ivtv_request_module(itv, "cs53l32a");
+#endif
+
+	/* check which i2c devices are actually found */
+	for (i = 0; i < 32; i++) {
+		u32 device = 1 << i;
+
+		if (!(device & hw))
+			continue;
+		if (device == IVTV_HW_GPIO) {
+			/* GPIO is always available */
+			itv->hw_flags |= IVTV_HW_GPIO;
+			continue;
+		}
+		if (ivtv_i2c_hw_addr(itv, device) > 0)
+			itv->hw_flags |= device;
+	}
+
+	hw = itv->hw_flags;
+
+	if (itv->card->type == IVTV_CARD_CX23416GYC) {
+		/* Several variations of this card exist, detect which card
+		   type should be used. */
+		if ((hw & (IVTV_HW_UPD64031A | IVTV_HW_UPD6408X)) == 0)
+			itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGRYCS);
+		else if ((hw & IVTV_HW_UPD64031A) == 0)
+			itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGR);
+	}
+
+	if (hw & IVTV_HW_CX25840) {
+		/* CX25840_CID_ENABLE_PVR150_WORKAROUND */
+		ctrl.id = V4L2_CID_PRIVATE_BASE;
+		ctrl.value = itv->pvr150_workaround;
+		itv->video_dec_func(itv, VIDIOC_S_CTRL, &ctrl);
+
+		itv->vbi.raw_decoder_line_size = 1444;
+		itv->vbi.raw_decoder_sav_odd_field = 0x20;
+		itv->vbi.raw_decoder_sav_even_field = 0x60;
+		itv->vbi.sliced_decoder_line_size = 272;
+		itv->vbi.sliced_decoder_sav_odd_field = 0xB0;
+		itv->vbi.sliced_decoder_sav_even_field = 0xF0;
+	}
+
+	if (hw & IVTV_HW_SAA711X) {
+		struct v4l2_chip_ident v = { V4L2_CHIP_MATCH_I2C_DRIVER, I2C_DRIVERID_SAA711X };
+
+		/* determine the exact saa711x model */
+		itv->hw_flags &= ~IVTV_HW_SAA711X;
+
+		ivtv_saa7115(itv, VIDIOC_G_CHIP_IDENT, &v);
+		if (v.ident == V4L2_IDENT_SAA7114) {
+			itv->hw_flags |= IVTV_HW_SAA7114;
+			/* VBI is not yet supported by the saa7114 driver. */
+			itv->v4l2_cap &= ~(V4L2_CAP_SLICED_VBI_CAPTURE|V4L2_CAP_VBI_CAPTURE);
+		}
+		else {
+			itv->hw_flags |= IVTV_HW_SAA7115;
+		}
+		itv->vbi.raw_decoder_line_size = 1443;
+		itv->vbi.raw_decoder_sav_odd_field = 0x25;
+		itv->vbi.raw_decoder_sav_even_field = 0x62;
+		itv->vbi.sliced_decoder_line_size = 51;
+		itv->vbi.sliced_decoder_sav_odd_field = 0xAB;
+		itv->vbi.sliced_decoder_sav_even_field = 0xEC;
+	}
+
+	if (hw & IVTV_HW_SAA717X) {
+		itv->vbi.raw_decoder_line_size = 1443;
+		itv->vbi.raw_decoder_sav_odd_field = 0x25;
+		itv->vbi.raw_decoder_sav_even_field = 0x62;
+		itv->vbi.sliced_decoder_line_size = 51;
+		itv->vbi.sliced_decoder_sav_odd_field = 0xAB;
+		itv->vbi.sliced_decoder_sav_even_field = 0xEC;
+	}
+}
+
+static int __devinit ivtv_probe(struct pci_dev *dev,
+				const struct pci_device_id *pci_id)
+{
+	int retval = 0;
+	int video_input;
+	int yuv_buf_size;
+	int vbi_buf_size;
+	int fw_retry_count = 3;
+	struct ivtv *itv;
+	struct v4l2_frequency vf;
+
+	spin_lock(&ivtv_cards_lock);
+
+	/* Make sure we've got a place for this card */
+	if (ivtv_cards_active == IVTV_MAX_CARDS) {
+		printk(KERN_ERR "ivtv:  Maximum number of cards detected (%d).\n",
+			      ivtv_cards_active);
+		spin_unlock(&ivtv_cards_lock);
+		return -ENOMEM;
+	}
+
+	itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC);
+	if (itv == 0) {
+		spin_unlock(&ivtv_cards_lock);
+		return -ENOMEM;
+	}
+	ivtv_cards[ivtv_cards_active] = itv;
+	itv->dev = dev;
+	itv->num = ivtv_cards_active++;
+	snprintf(itv->name, sizeof(itv->name) - 1, "ivtv%d", itv->num);
+	if (itv->num) {
+		printk(KERN_INFO "ivtv:  ======================  NEXT CARD  ======================\n");
+	}
+
+	spin_unlock(&ivtv_cards_lock);
+
+	ivtv_process_options(itv);
+	if (itv->options.cardtype == -1) {
+		retval = -ENODEV;
+		goto err;
+	}
+	if (ivtv_init_struct1(itv)) {
+		retval = -ENOMEM;
+		goto err;
+	}
+
+	IVTV_DEBUG_INFO("base addr: 0x%08x\n", itv->base_addr);
+
+	/* PCI Device Setup */
+	if ((retval = ivtv_setup_pci(itv, dev, pci_id)) != 0) {
+		if (retval == -EIO)
+			goto free_workqueue;
+		else if (retval == -ENXIO)
+			goto free_mem;
+	}
+	/* save itv in the pci struct for later use */
+	pci_set_drvdata(dev, itv);
+
+	/* map io memory */
+	IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
+		   itv->base_addr + IVTV_ENCODER_OFFSET, IVTV_ENCODER_SIZE);
+	itv->enc_mem = ioremap_nocache(itv->base_addr + IVTV_ENCODER_OFFSET,
+				       IVTV_ENCODER_SIZE);
+	if (!itv->enc_mem) {
+		IVTV_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n");
+		IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n");
+		retval = -ENOMEM;
+		goto free_mem;
+	}
+
+	if (itv->has_cx23415) {
+		IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
+				itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
+		itv->dec_mem = ioremap_nocache(itv->base_addr + IVTV_DECODER_OFFSET,
+				IVTV_DECODER_SIZE);
+		if (!itv->dec_mem) {
+			IVTV_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n");
+			IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n");
+			retval = -ENOMEM;
+			goto free_mem;
+		}
+	}
+	else {
+		itv->dec_mem = itv->enc_mem;
+	}
+
+	/* map registers memory */
+	IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
+		   itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+	itv->reg_mem =
+	    ioremap_nocache(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+	if (!itv->reg_mem) {
+		IVTV_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n");
+		IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n");
+		retval = -ENOMEM;
+		goto free_io;
+	}
+
+	while (--fw_retry_count > 0) {
+		/* load firmware */
+		if (ivtv_firmware_init(itv) == 0)
+			break;
+		if (fw_retry_count > 1)
+			IVTV_WARN("Retry loading firmware\n");
+	}
+	if (fw_retry_count == 0) {
+		IVTV_ERR("Error initializing firmware\n");
+		goto free_i2c;
+	}
+
+	/* Try and get firmware versions */
+	IVTV_DEBUG_INFO("Getting firmware version..\n");
+	ivtv_firmware_versions(itv);
+
+	/* Check yuv output filter table */
+	if (itv->has_cx23415) ivtv_yuv_filter_check(itv);
+
+	ivtv_gpio_init(itv);
+
+	/* active i2c  */
+	IVTV_DEBUG_INFO("activating i2c...\n");
+	if (init_ivtv_i2c(itv)) {
+		IVTV_ERR("Could not initialize i2c\n");
+		goto free_irq;
+	}
+
+	IVTV_DEBUG_INFO("Active card count: %d.\n", ivtv_cards_active);
+
+	if (itv->card->hw_all & IVTV_HW_TVEEPROM) {
+#ifdef CONFIG_VIDEO_TVEEPROM_MODULE
+		ivtv_request_module(itv, "tveeprom");
+#endif
+		/* Based on the model number the cardtype may be changed.
+		   The PCI IDs are not always reliable. */
+		ivtv_process_eeprom(itv);
+	}
+
+	if (itv->std == 0) {
+		itv->std = V4L2_STD_NTSC_M;
+	}
+
+	if (itv->options.tuner == -1) {
+		int i;
+
+		for (i = 0; i < IVTV_CARD_MAX_TUNERS; i++) {
+			if ((itv->std & itv->card->tuners[i].std) == 0)
+				continue;
+			itv->options.tuner = itv->card->tuners[i].tuner;
+			break;
+		}
+	}
+	/* if no tuner was found, then pick the first tuner in the card list */
+	if (itv->options.tuner == -1 && itv->card->tuners[0].std) {
+		itv->std = itv->card->tuners[0].std;
+		itv->options.tuner = itv->card->tuners[0].tuner;
+	}
+	if (itv->options.radio == -1)
+		itv->options.radio = (itv->card->radio_input.audio_type != 0);
+
+	/* The card is now fully identified, continue with card-specific
+	   initialization. */
+	ivtv_init_struct2(itv);
+
+	ivtv_load_and_init_modules(itv);
+
+	if (itv->std & V4L2_STD_525_60) {
+		itv->is_60hz = 1;
+		itv->is_out_60hz = 1;
+	} else {
+		itv->is_50hz = 1;
+		itv->is_out_50hz = 1;
+	}
+	itv->params.video_gop_size = itv->is_60hz ? 15 : 12;
+
+	itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG] = 0x08000;
+	itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM] = 0x01200;
+	itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_MPG] = 0x10000;
+
+	/* 0x15180 == 720 * 480 / 4, 0x19500 == 720 * 576 / 4 */
+	yuv_buf_size = itv->is_60hz ? 0x15180 : 0x19500;
+	itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_YUV] = yuv_buf_size / 2;
+	itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_YUV] = yuv_buf_size / 8;
+
+	/* Setup VBI Raw Size. Should be big enough to hold PAL.
+	   It is possible to switch between PAL and NTSC, so we need to
+	   take the largest size here. */
+	/* 1456 is multiple of 16, real size = 1444 */
+	itv->vbi.raw_size = 1456;
+	/* We use a buffer size of 1/2 of the total size needed for a
+	   frame. This is actually very useful, since we now receive
+	   a field at a time and that makes 'compressing' the raw data
+	   down to size by stripping off the SAV codes a lot easier.
+	   Note: having two different buffer sizes prevents standard
+	   switching on the fly. We need to find a better solution... */
+	vbi_buf_size = itv->vbi.raw_size * (itv->is_60hz ? 24 : 36) / 2;
+	itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_VBI] = vbi_buf_size;
+	itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_VBI] = sizeof(struct v4l2_sliced_vbi_data) * 36;
+
+	if (itv->options.radio > 0)
+		itv->v4l2_cap |= V4L2_CAP_RADIO;
+
+	if (itv->options.tuner > -1) {
+		struct tuner_setup setup;
+
+		setup.addr = ADDR_UNSET;
+		setup.type = itv->options.tuner;
+		setup.mode_mask = T_ANALOG_TV;  /* matches TV tuners */
+#ifdef HAVE_XC3028
+		setup.initmode = V4L2_TUNER_ANALOG_TV;
+		if (itv->options.tuner == TUNER_XCEIVE_XC3028) {
+			setup.gpio_write = ivtv_reset_tuner_gpio;
+			setup.gpio_priv = itv;
+		}
+#endif
+		ivtv_call_i2c_clients(itv, TUNER_SET_TYPE_ADDR, &setup);
+	}
+
+	vf.tuner = 0;
+	vf.type = V4L2_TUNER_ANALOG_TV;
+	vf.frequency = 6400; /* the tuner 'baseline' frequency */
+	if (itv->std & V4L2_STD_NTSC_M) {
+		/* Why on earth? */
+		vf.frequency = 1076;	/* ch. 4 67250*16/1000 */
+	}
+
+	/* The tuner is fixed to the standard. The other inputs (e.g. S-Video)
+	   are not. */
+	itv->tuner_std = itv->std;
+
+	video_input = itv->active_input;
+	itv->active_input++;	/* Force update of input */
+	ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_INPUT, &video_input);
+
+	/* Let the VIDIOC_S_STD ioctl do all the work, keeps the code
+	   in one place. */
+	itv->std++;		/* Force full standard initialization */
+	itv->std_out = itv->std;
+	ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_FREQUENCY, &vf);
+
+	retval = ivtv_streams_setup(itv);
+	if (retval) {
+		IVTV_ERR("Error %d setting up streams\n", retval);
+		goto free_i2c;
+	}
+
+	if (itv->card->v4l2_capabilities & V4L2_CAP_VIDEO_OUTPUT) {
+		ivtv_init_mpeg_decoder(itv);
+	}
+	ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_STD, &itv->tuner_std);
+
+	IVTV_DEBUG_IRQ("Masking interrupts\n");
+	/* clear interrupt mask, effectively disabling interrupts */
+	ivtv_set_irq_mask(itv, 0xffffffff);
+
+	/* Register IRQ */
+	retval = request_irq(itv->dev->irq, ivtv_irq_handler,
+			     IRQF_SHARED | IRQF_DISABLED, itv->name, (void *)itv);
+	if (retval) {
+		IVTV_ERR("Failed to register irq %d\n", retval);
+		goto free_streams;
+	}
+
+	/* On a cx23416 this seems to be able to enable DMA to the chip? */
+	if (!itv->has_cx23415)
+		write_reg_sync(0x03, IVTV_REG_DMACONTROL);
+
+	/* Default interrupts enabled. For the PVR350 this includes the
+	   decoder VSYNC interrupt, which is always on. It is not only used
+	   during decoding but also by the OSD.
+	   Some old PVR250 cards had a cx23415, so testing for that is too
+	   general. Instead test if the card has video output capability. */
+	if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
+		ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT | IVTV_IRQ_DEC_VSYNC);
+	else
+		ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT);
+
+	if (itv->has_cx23415)
+		ivtv_set_osd_alpha(itv);
+
+	IVTV_INFO("Initialized %s, card #%d\n", itv->card_name, itv->num);
+
+	return 0;
+
+      free_irq:
+	free_irq(itv->dev->irq, (void *)itv);
+      free_streams:
+	ivtv_streams_cleanup(itv);
+      free_i2c:
+	exit_ivtv_i2c(itv);
+      free_io:
+	ivtv_iounmap(itv);
+      free_mem:
+	release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+	release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+	if (itv->has_cx23415)
+		release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
+      free_workqueue:
+	destroy_workqueue(itv->irq_work_queues);
+      err:
+	if (retval == 0)
+		retval = -ENODEV;
+	IVTV_ERR("Error %d on initialization\n", retval);
+
+	kfree(ivtv_cards[ivtv_cards_active]);
+	ivtv_cards[ivtv_cards_active] = NULL;
+	return retval;
+}
+
+static void ivtv_remove(struct pci_dev *pci_dev)
+{
+	struct ivtv *itv = pci_get_drvdata(pci_dev);
+
+	IVTV_DEBUG_INFO("Removing Card #%d.\n", itv->num);
+
+	/* Stop all captures */
+	IVTV_DEBUG_INFO(" Stopping all streams.\n");
+	if (atomic_read(&itv->capturing) > 0)
+		ivtv_stop_all_captures(itv);
+
+	/* Stop all decoding */
+	IVTV_DEBUG_INFO(" Stopping decoding.\n");
+	if (atomic_read(&itv->decoding) > 0) {
+		int type;
+
+		if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags))
+			type = IVTV_DEC_STREAM_TYPE_YUV;
+		else
+			type = IVTV_DEC_STREAM_TYPE_MPG;
+		ivtv_stop_v4l2_decode_stream(&itv->streams[type],
+			VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0);
+	}
+
+	/* Interrupts */
+	IVTV_DEBUG_INFO(" Disabling interrupts.\n");
+	ivtv_set_irq_mask(itv, 0xffffffff);
+	del_timer_sync(&itv->dma_timer);
+
+	/* Stop all Work Queues */
+	IVTV_DEBUG_INFO(" Stop Work Queues.\n");
+	flush_workqueue(itv->irq_work_queues);
+	destroy_workqueue(itv->irq_work_queues);
+
+	IVTV_DEBUG_INFO(" Stopping Firmware.\n");
+	ivtv_halt_firmware(itv);
+
+	IVTV_DEBUG_INFO(" Unregistering v4l devices.\n");
+	ivtv_streams_cleanup(itv);
+	IVTV_DEBUG_INFO(" Freeing dma resources.\n");
+	ivtv_udma_free(itv);
+
+	exit_ivtv_i2c(itv);
+
+	IVTV_DEBUG_INFO(" Releasing irq.\n");
+	free_irq(itv->dev->irq, (void *)itv);
+
+	if (itv->dev) {
+		ivtv_iounmap(itv);
+	}
+
+	IVTV_DEBUG_INFO(" Releasing mem.\n");
+	release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+	release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+	if (itv->has_cx23415)
+		release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
+
+	pci_disable_device(itv->dev);
+
+	IVTV_INFO("Removed %s, card #%d\n", itv->card_name, itv->num);
+}
+
+/* define a pci_driver for card detection */
+static struct pci_driver ivtv_pci_driver = {
+      .name =     "ivtv",
+      .id_table = ivtv_pci_tbl,
+      .probe =    ivtv_probe,
+      .remove =   ivtv_remove,
+};
+
+static int module_start(void)
+{
+	printk(KERN_INFO "ivtv:  ==================== START INIT IVTV ====================\n");
+	printk(KERN_INFO "ivtv:  version %s (" VERMAGIC_STRING ") loading\n", IVTV_VERSION);
+
+	memset(ivtv_cards, 0, sizeof(ivtv_cards));
+
+	/* Validate parameters */
+	if (ivtv_first_minor < 0 || ivtv_first_minor >= IVTV_MAX_CARDS) {
+		printk(KERN_ERR "ivtv:  ivtv_first_minor must be between 0 and %d. Exiting...\n",
+		     IVTV_MAX_CARDS - 1);
+		return -1;
+	}
+
+	if (ivtv_debug < 0 || ivtv_debug > 511) {
+		ivtv_debug = 0;
+		printk(KERN_INFO "ivtv:  debug value must be >= 0 and <= 511!\n");
+	}
+
+	if (pci_register_driver(&ivtv_pci_driver)) {
+		printk(KERN_ERR "ivtv:  Error detecting PCI card\n");
+		return -ENODEV;
+	}
+	printk(KERN_INFO "ivtv:  ====================  END INIT IVTV  ====================\n");
+	return 0;
+}
+
+static void module_cleanup(void)
+{
+	int i, j;
+
+	pci_unregister_driver(&ivtv_pci_driver);
+
+	for (i = 0; i < ivtv_cards_active; i++) {
+		if (ivtv_cards[i] == NULL)
+			continue;
+		for (j = 0; j < IVTV_VBI_FRAMES; j++) {
+			kfree(ivtv_cards[i]->vbi.sliced_mpeg_data[j]);
+		}
+		kfree(ivtv_cards[i]);
+	}
+}
+
+/* Note: These symbols are exported because they are used by the ivtv-fb
+   framebuffer module and an infrared module for the IR-blaster. */
+EXPORT_SYMBOL(ivtv_set_irq_mask);
+EXPORT_SYMBOL(ivtv_cards_active);
+EXPORT_SYMBOL(ivtv_cards);
+EXPORT_SYMBOL(ivtv_api);
+EXPORT_SYMBOL(ivtv_vapi);
+EXPORT_SYMBOL(ivtv_vapi_result);
+EXPORT_SYMBOL(ivtv_clear_irq_mask);
+EXPORT_SYMBOL(ivtv_debug);
+EXPORT_SYMBOL(ivtv_reset_ir_gpio);
+EXPORT_SYMBOL(ivtv_udma_setup);
+EXPORT_SYMBOL(ivtv_udma_unmap);
+EXPORT_SYMBOL(ivtv_udma_alloc);
+EXPORT_SYMBOL(ivtv_udma_prepare);
+
+module_init(module_start);
+module_exit(module_cleanup);
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
new file mode 100644
index 0000000..9a412d6
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -0,0 +1,868 @@
+/*
+    ivtv driver internal defines and structures
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef IVTV_DRIVER_H
+#define IVTV_DRIVER_H
+
+/* Internal header for ivtv project:
+ * Driver for the cx23415/6 chip.
+ * Author: Kevin Thayer (nufan_wfk at yahoo.com)
+ * License: GPL
+ * http://www.ivtvdriver.org
+ *
+ * -----
+ * MPG600/MPG160 support by  T.Adachi <tadachi@tadachi-net.com>
+ *                      and Takeru KOMORIYA<komoriya@paken.org>
+ *
+ * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
+ *                using information provided by Jiun-Kuei Jung @ AVerMedia.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/list.h>
+#include <linux/unistd.h>
+#include <linux/byteorder/swab.h>
+#include <linux/pagemap.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/dvb/video.h>
+#include <linux/dvb/audio.h>
+#include <media/v4l2-common.h>
+#include <media/tuner.h>
+#include <media/cx2341x.h>
+
+/* #define HAVE_XC3028 1 */
+
+#include <media/ivtv.h>
+
+#ifdef CONFIG_LIRC_I2C
+#  error "This driver is not compatible with the LIRC I2C kernel configuration option."
+#endif /* CONFIG_LIRC_I2C */
+
+#ifndef CONFIG_PCI
+#  error "This driver requires kernel PCI support."
+#endif /* CONFIG_PCI */
+
+#define IVTV_ENCODER_OFFSET	0x00000000
+#define IVTV_ENCODER_SIZE	0x00800000	/* Last half isn't needed 0x01000000 */
+
+#define IVTV_DECODER_OFFSET	0x01000000
+#define IVTV_DECODER_SIZE	0x00800000	/* Last half isn't needed 0x01000000 */
+
+#define IVTV_REG_OFFSET 	0x02000000
+#define IVTV_REG_SIZE		0x00010000
+
+/* Buffers on hardware offsets */
+#define IVTV_YUV_BUFFER_OFFSET    0x001a8600	/* First YUV Buffer */
+#define IVTV_YUV_BUFFER_OFFSET_1  0x00240400	/* Second YUV Buffer */
+#define IVTV_YUV_BUFFER_OFFSET_2  0x002d8200	/* Third YUV Buffer */
+#define IVTV_YUV_BUFFER_OFFSET_3  0x00370000	/* Fourth YUV Buffer */
+#define IVTV_YUV_BUFFER_UV_OFFSET 0x65400	/* Offset to UV Buffer */
+
+/* Offset to filter table in firmware */
+#define IVTV_YUV_HORIZONTAL_FILTER_OFFSET 0x025d8
+#define IVTV_YUV_VERTICAL_FILTER_OFFSET 0x03358
+
+extern const u32 yuv_offset[4];
+
+/* Maximum ivtv driver instances.
+   Based on 6 PVR500s each with two PVR15s...
+   TODO: make this dynamic. I believe it is only a global in order to support
+    ivtv-fb. There must be a better way to do that. */
+#define IVTV_MAX_CARDS 12
+
+/* Supported cards */
+#define IVTV_CARD_PVR_250 	      0	/* WinTV PVR 250 */
+#define IVTV_CARD_PVR_350 	      1	/* encoder, decoder, tv-out */
+#define IVTV_CARD_PVR_150 	      2	/* WinTV PVR 150 and PVR 500 (really just two
+					   PVR150s on one PCI board) */
+#define IVTV_CARD_M179    	      3	/* AVerMedia M179 (encoder only) */
+#define IVTV_CARD_MPG600  	      4	/* Kuroutoshikou ITVC16-STVLP/YUAN MPG600, encoder only */
+#define IVTV_CARD_MPG160  	      5	/* Kuroutoshikou ITVC15-STVLP/YUAN MPG160
+					   cx23415 based, but does not have tv-out */
+#define IVTV_CARD_PG600 	      6	/* YUAN PG600/DIAMONDMM PVR-550 based on the CX Falcon 2 */
+#define IVTV_CARD_AVC2410 	      7	/* Adaptec AVC-2410 */
+#define IVTV_CARD_AVC2010 	      8	/* Adaptec AVD-2010 (No Tuner) */
+#define IVTV_CARD_TG5000TV   	      9 /* NAGASE TRANSGEAR 5000TV, encoder only */
+#define IVTV_CARD_VA2000MAX_SNT6     10 /* VA2000MAX-STN6 */
+#define IVTV_CARD_CX23416GYC 	     11 /* Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */
+#define IVTV_CARD_GV_MVPRX   	     12 /* I/O Data GV-MVP/RX, RX2, RX2W */
+#define IVTV_CARD_GV_MVPRX2E 	     13 /* I/O Data GV-MVP/RX2E */
+#define IVTV_CARD_GOTVIEW_PCI_DVD    14	/* GotView PCI DVD */
+#define IVTV_CARD_GOTVIEW_PCI_DVD2   15	/* GotView PCI DVD2 */
+#define IVTV_CARD_YUAN_MPC622        16	/* Yuan MPC622 miniPCI */
+#define IVTV_CARD_DCTMTVP1 	     17 /* DIGITAL COWBOY DCT-MTVP1 */
+#ifdef HAVE_XC3028
+#define IVTV_CARD_PG600V2	     18 /* Yuan PG600V2/GotView PCI DVD Lite/Club3D ZAP-TV1x01 */
+#define IVTV_CARD_LAST 		     18
+#else
+#define IVTV_CARD_LAST 		     17
+#endif
+
+/* Variants of existing cards but with the same PCI IDs. The driver
+   detects these based on other device information.
+   These cards must always come last.
+   New cards must be inserted above, and the indices of the cards below
+   must be adjusted accordingly. */
+
+/* PVR-350 V1 (uses saa7114) */
+#define IVTV_CARD_PVR_350_V1 	     (IVTV_CARD_LAST+1)
+/* 2 variants of Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */
+#define IVTV_CARD_CX23416GYC_NOGR    (IVTV_CARD_LAST+2)
+#define IVTV_CARD_CX23416GYC_NOGRYCS (IVTV_CARD_LAST+3)
+
+#define IVTV_ENC_STREAM_TYPE_MPG  0
+#define IVTV_ENC_STREAM_TYPE_YUV  1
+#define IVTV_ENC_STREAM_TYPE_VBI  2
+#define IVTV_ENC_STREAM_TYPE_PCM  3
+#define IVTV_ENC_STREAM_TYPE_RAD  4
+#define IVTV_DEC_STREAM_TYPE_MPG  5
+#define IVTV_DEC_STREAM_TYPE_VBI  6
+#define IVTV_DEC_STREAM_TYPE_VOUT 7
+#define IVTV_DEC_STREAM_TYPE_YUV  8
+#define IVTV_MAX_STREAMS	  9
+
+#define IVTV_V4L2_DEC_MPG_OFFSET  16	/* offset from 0 to register decoder mpg v4l2 minors on */
+#define IVTV_V4L2_ENC_PCM_OFFSET  24	/* offset from 0 to register pcm v4l2 minors on */
+#define IVTV_V4L2_ENC_YUV_OFFSET  32	/* offset from 0 to register yuv v4l2 minors on */
+#define IVTV_V4L2_DEC_YUV_OFFSET  48	/* offset from 0 to register decoder yuv v4l2 minors on */
+#define IVTV_V4L2_DEC_VBI_OFFSET   8	/* offset from 0 to register decoder vbi input v4l2 minors on */
+#define IVTV_V4L2_DEC_VOUT_OFFSET 16	/* offset from 0 to register vbi output v4l2 minors on */
+
+#define IVTV_ENC_MEM_START 0x00000000
+#define IVTV_DEC_MEM_START 0x01000000
+
+/* system vendor and device IDs */
+#define PCI_VENDOR_ID_ICOMP  0x4444
+#define PCI_DEVICE_ID_IVTV15 0x0803
+#define PCI_DEVICE_ID_IVTV16 0x0016
+
+/* subsystem vendor ID */
+#define IVTV_PCI_ID_HAUPPAUGE 		0x0070
+#define IVTV_PCI_ID_HAUPPAUGE_ALT1 	0x0270
+#define IVTV_PCI_ID_HAUPPAUGE_ALT2 	0x4070
+#define IVTV_PCI_ID_ADAPTEC 		0x9005
+#define IVTV_PCI_ID_AVERMEDIA 		0x1461
+#define IVTV_PCI_ID_YUAN1		0x12ab
+#define IVTV_PCI_ID_YUAN2 		0xff01
+#define IVTV_PCI_ID_YUAN3 		0xffab
+#define IVTV_PCI_ID_YUAN4 		0xfbab
+#define IVTV_PCI_ID_DIAMONDMM 		0xff92
+#define IVTV_PCI_ID_IODATA 		0x10fc
+#define IVTV_PCI_ID_MELCO 		0x1154
+#define IVTV_PCI_ID_GOTVIEW1		0xffac
+#define IVTV_PCI_ID_GOTVIEW2 		0xffad
+
+/* Decoder Buffer hardware size on Chip */
+#define IVTV_DEC_MAX_BUF        0x00100000	/* max bytes in decoder buffer */
+#define IVTV_DEC_MIN_BUF        0x00010000	/* min bytes in dec buffer */
+
+/* ======================================================================== */
+/* ========================== START USER SETTABLE DMA VARIABLES =========== */
+/* ======================================================================== */
+
+#define IVTV_DMA_SG_OSD_ENT	(2883584/PAGE_SIZE)	/* sg entities */
+
+/* DMA Buffers, Default size in MB allocated */
+#define IVTV_DEFAULT_ENC_MPG_BUFFERS 4
+#define IVTV_DEFAULT_ENC_YUV_BUFFERS 2
+#define IVTV_DEFAULT_ENC_VBI_BUFFERS 1
+#define IVTV_DEFAULT_ENC_PCM_BUFFERS 1
+#define IVTV_DEFAULT_DEC_MPG_BUFFERS 1
+#define IVTV_DEFAULT_DEC_YUV_BUFFERS 1
+#define IVTV_DEFAULT_DEC_VBI_BUFFERS 1
+
+/* ======================================================================== */
+/* ========================== END USER SETTABLE DMA VARIABLES ============= */
+/* ======================================================================== */
+
+/* Decoder Status Register */
+#define IVTV_DMA_ERR_LIST 	0x00000010
+#define IVTV_DMA_ERR_WRITE 	0x00000008
+#define IVTV_DMA_ERR_READ 	0x00000004
+#define IVTV_DMA_SUCCESS_WRITE 	0x00000002
+#define IVTV_DMA_SUCCESS_READ 	0x00000001
+#define IVTV_DMA_READ_ERR 	(IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_READ)
+#define IVTV_DMA_WRITE_ERR 	(IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_WRITE)
+#define IVTV_DMA_ERR 		(IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_WRITE | IVTV_DMA_ERR_READ)
+
+/* DMA Registers */
+#define IVTV_REG_DMAXFER 	(0x0000)
+#define IVTV_REG_DMASTATUS 	(0x0004)
+#define IVTV_REG_DECDMAADDR 	(0x0008)
+#define IVTV_REG_ENCDMAADDR 	(0x000c)
+#define IVTV_REG_DMACONTROL 	(0x0010)
+#define IVTV_REG_IRQSTATUS 	(0x0040)
+#define IVTV_REG_IRQMASK 	(0x0048)
+
+/* Setup Registers */
+#define IVTV_REG_ENC_SDRAM_REFRESH 	(0x07F8)
+#define IVTV_REG_ENC_SDRAM_PRECHARGE 	(0x07FC)
+#define IVTV_REG_DEC_SDRAM_REFRESH 	(0x08F8)
+#define IVTV_REG_DEC_SDRAM_PRECHARGE 	(0x08FC)
+#define IVTV_REG_VDM 			(0x2800)
+#define IVTV_REG_AO 			(0x2D00)
+#define IVTV_REG_BYTEFLUSH 		(0x2D24)
+#define IVTV_REG_SPU 			(0x9050)
+#define IVTV_REG_HW_BLOCKS 		(0x9054)
+#define IVTV_REG_VPU 			(0x9058)
+#define IVTV_REG_APU 			(0xA064)
+
+#define IVTV_IRQ_ENC_START_CAP		(0x1 << 31)
+#define IVTV_IRQ_ENC_EOS		(0x1 << 30)
+#define IVTV_IRQ_ENC_VBI_CAP		(0x1 << 29)
+#define IVTV_IRQ_ENC_VIM_RST		(0x1 << 28)
+#define IVTV_IRQ_ENC_DMA_COMPLETE	(0x1 << 27)
+#define IVTV_IRQ_DEC_AUD_MODE_CHG	(0x1 << 24)
+#define IVTV_IRQ_DEC_DATA_REQ		(0x1 << 22)
+#define IVTV_IRQ_DEC_DMA_COMPLETE	(0x1 << 20)
+#define IVTV_IRQ_DEC_VBI_RE_INSERT	(0x1 << 19)
+#define IVTV_IRQ_DMA_ERR		(0x1 << 18)
+#define IVTV_IRQ_DMA_WRITE		(0x1 << 17)
+#define IVTV_IRQ_DMA_READ		(0x1 << 16)
+#define IVTV_IRQ_DEC_VSYNC		(0x1 << 10)
+
+/* IRQ Masks */
+#define IVTV_IRQ_MASK_INIT (IVTV_IRQ_DMA_ERR|IVTV_IRQ_ENC_DMA_COMPLETE|IVTV_IRQ_DMA_READ)
+
+#define IVTV_IRQ_MASK_CAPTURE (IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_EOS)
+#define IVTV_IRQ_MASK_DECODE  (IVTV_IRQ_DEC_DATA_REQ|IVTV_IRQ_DEC_AUD_MODE_CHG)
+
+/* i2c stuff */
+#define I2C_CLIENTS_MAX 16
+
+/* debugging */
+
+#define IVTV_DBGFLG_WARN  (1 << 0)
+#define IVTV_DBGFLG_INFO  (1 << 1)
+#define IVTV_DBGFLG_API   (1 << 2)
+#define IVTV_DBGFLG_DMA   (1 << 3)
+#define IVTV_DBGFLG_IOCTL (1 << 4)
+#define IVTV_DBGFLG_I2C   (1 << 5)
+#define IVTV_DBGFLG_IRQ   (1 << 6)
+#define IVTV_DBGFLG_DEC   (1 << 7)
+#define IVTV_DBGFLG_YUV   (1 << 8)
+
+/* NOTE: extra space before comma in 'itv->num , ## args' is required for
+   gcc-2.95, otherwise it won't compile. */
+#define IVTV_DEBUG(x, type, fmt, args...) \
+	do { \
+		if ((x) & ivtv_debug) \
+			printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \
+	} while (0)
+#define IVTV_DEBUG_WARN(fmt, args...)  IVTV_DEBUG(IVTV_DBGFLG_WARN, "warning", fmt , ## args)
+#define IVTV_DEBUG_INFO(fmt, args...)  IVTV_DEBUG(IVTV_DBGFLG_INFO, "info",fmt , ## args)
+#define IVTV_DEBUG_API(fmt, args...)   IVTV_DEBUG(IVTV_DBGFLG_API, "api", fmt , ## args)
+#define IVTV_DEBUG_DMA(fmt, args...)   IVTV_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args)
+#define IVTV_DEBUG_IOCTL(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args)
+#define IVTV_DEBUG_I2C(fmt, args...)   IVTV_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args)
+#define IVTV_DEBUG_IRQ(fmt, args...)   IVTV_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args)
+#define IVTV_DEBUG_DEC(fmt, args...)   IVTV_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args)
+#define IVTV_DEBUG_YUV(fmt, args...)   IVTV_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args)
+
+#define IVTV_FB_DEBUG(x, type, fmt, args...) \
+	do { \
+		if ((x) & ivtv_debug) \
+			printk(KERN_INFO "ivtv%d-fb " type ": " fmt, itv->num , ## args); \
+	} while (0)
+#define IVTV_FB_DEBUG_WARN(fmt, args...)  IVTV_FB_DEBUG(IVTV_DBGFLG_WARN, "warning", fmt , ## args)
+#define IVTV_FB_DEBUG_INFO(fmt, args...)  IVTV_FB_DEBUG(IVTV_DBGFLG_INFO, "info", fmt , ## args)
+#define IVTV_FB_DEBUG_API(fmt, args...)   IVTV_FB_DEBUG(IVTV_DBGFLG_API, "api", fmt , ## args)
+#define IVTV_FB_DEBUG_DMA(fmt, args...)   IVTV_FB_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args)
+#define IVTV_FB_DEBUG_IOCTL(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args)
+#define IVTV_FB_DEBUG_I2C(fmt, args...)   IVTV_FB_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args)
+#define IVTV_FB_DEBUG_IRQ(fmt, args...)   IVTV_FB_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args)
+#define IVTV_FB_DEBUG_DEC(fmt, args...)   IVTV_FB_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args)
+#define IVTV_FB_DEBUG_YUV(fmt, args...)   IVTV_FB_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args)
+
+/* Standard kernel messages */
+#define IVTV_ERR(fmt, args...)      printk(KERN_ERR  "ivtv%d: " fmt, itv->num , ## args)
+#define IVTV_WARN(fmt, args...)     printk(KERN_WARNING "ivtv%d: " fmt, itv->num , ## args)
+#define IVTV_INFO(fmt, args...)     printk(KERN_INFO "ivtv%d: " fmt, itv->num , ## args)
+#define IVTV_FB_ERR(fmt, args...)   printk(KERN_ERR  "ivtv%d-fb: " fmt, itv->num , ## args)
+#define IVTV_FB_WARN(fmt, args...)  printk(KERN_WARNING  "ivtv%d-fb: " fmt, itv->num , ## args)
+#define IVTV_FB_INFO(fmt, args...)  printk(KERN_INFO "ivtv%d-fb: " fmt, itv->num , ## args)
+
+/* Values for IVTV_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */
+#define MPEG_FRAME_TYPE_IFRAME 1
+#define MPEG_FRAME_TYPE_IFRAME_PFRAME 3
+#define MPEG_FRAME_TYPE_ALL 7
+
+/* output modes (cx23415 only) */
+#define OUT_NONE        0
+#define OUT_MPG         1
+#define OUT_YUV         2
+#define OUT_UDMA_YUV    3
+#define OUT_PASSTHROUGH 4
+
+#define IVTV_MAX_PGM_INDEX (400)
+
+extern int ivtv_debug;
+
+
+struct ivtv_options {
+	int megabytes[IVTV_MAX_STREAMS]; /* Size in megabytes of each stream */
+	int cardtype;		/* force card type on load */
+	int tuner;		/* set tuner on load */
+	int radio;		/* enable/disable radio */
+	int newi2c;		/* New I2C algorithm */
+};
+
+#define IVTV_MBOX_DMA_START 6
+#define IVTV_MBOX_DMA_END 8
+#define IVTV_MBOX_DMA 9
+#define IVTV_MBOX_FIELD_DISPLAYED 8
+
+/* ivtv-specific mailbox template */
+struct ivtv_mailbox {
+	u32 flags;
+	u32 cmd;
+	u32 retval;
+	u32 timeout;
+	u32 data[CX2341X_MBOX_MAX_DATA];
+};
+
+struct ivtv_api_cache {
+	unsigned long last_jiffies;		/* when last command was issued */
+	u32 data[CX2341X_MBOX_MAX_DATA];	/* last sent api data */
+};
+
+struct ivtv_mailbox_data {
+	volatile struct ivtv_mailbox __iomem *mbox;
+	/* Bits 0-2 are for the encoder mailboxes, 0-1 are for the decoder mailboxes.
+	   If the bit is set, then the corresponding mailbox is in use by the driver. */
+	unsigned long busy;
+	u8 max_mbox;
+};
+
+/* per-buffer bit flags */
+#define IVTV_F_B_NEED_BUF_SWAP  0	/* this buffer should be byte swapped */
+
+/* per-stream, s_flags */
+#define IVTV_F_S_DMA_PENDING	0	/* this stream has pending DMA */
+#define IVTV_F_S_DMA_HAS_VBI	1       /* the current DMA request also requests VBI data */
+#define IVTV_F_S_NEEDS_DATA	2 	/* this decoding stream needs more data */
+
+#define IVTV_F_S_CLAIMED 	3	/* this stream is claimed */
+#define IVTV_F_S_STREAMING      4	/* the fw is decoding/encoding this stream */
+#define IVTV_F_S_INTERNAL_USE	5	/* this stream is used internally (sliced VBI processing) */
+#define IVTV_F_S_PASSTHROUGH	6	/* this stream is in passthrough mode */
+#define IVTV_F_S_STREAMOFF	7	/* signal end of stream EOS */
+#define IVTV_F_S_APPL_IO        8	/* this stream is used read/written by an application */
+
+/* per-ivtv, i_flags */
+#define IVTV_F_I_DMA		   0 	/* DMA in progress */
+#define IVTV_F_I_UDMA		   1 	/* UDMA in progress */
+#define IVTV_F_I_UDMA_PENDING	   2 	/* UDMA pending */
+#define IVTV_F_I_SPEED_CHANGE	   3 	/* A speed change is in progress */
+#define IVTV_F_I_EOS		   4 	/* End of encoder stream reached */
+#define IVTV_F_I_RADIO_USER	   5 	/* The radio tuner is selected */
+#define IVTV_F_I_DIG_RST	   6 	/* Reset digitizer */
+#define IVTV_F_I_DEC_YUV	   7 	/* YUV instead of MPG is being decoded */
+#define IVTV_F_I_ENC_VBI	   8 	/* VBI DMA */
+#define IVTV_F_I_UPDATE_CC	   9  	/* CC should be updated */
+#define IVTV_F_I_UPDATE_WSS	   10 	/* WSS should be updated */
+#define IVTV_F_I_UPDATE_VPS	   11 	/* VPS should be updated */
+#define IVTV_F_I_DECODING_YUV	   12 	/* this stream is YUV frame decoding */
+#define IVTV_F_I_ENC_PAUSED	   13 	/* the encoder is paused */
+#define IVTV_F_I_VALID_DEC_TIMINGS 14 	/* last_dec_timing is valid */
+#define IVTV_F_I_WORK_HANDLER_VBI  15	/* there is work to be done for VBI */
+#define IVTV_F_I_WORK_HANDLER_YUV  16	/* there is work to be done for YUV */
+
+/* Event notifications */
+#define IVTV_F_I_EV_DEC_STOPPED	   28	/* decoder stopped event */
+#define IVTV_F_I_EV_VSYNC	   29 	/* VSYNC event */
+#define IVTV_F_I_EV_VSYNC_FIELD    30 	/* VSYNC event field (0 = first, 1 = second field) */
+#define IVTV_F_I_EV_VSYNC_ENABLED  31 	/* VSYNC event enabled */
+
+/* Scatter-Gather array element, used in DMA transfers */
+struct ivtv_SG_element {
+	u32 src;
+	u32 dst;
+	u32 size;
+};
+
+struct ivtv_user_dma {
+	struct mutex lock;
+	int page_count;
+	struct page *map[IVTV_DMA_SG_OSD_ENT];
+
+	/* Base Dev SG Array for cx23415/6 */
+	struct ivtv_SG_element SGarray[IVTV_DMA_SG_OSD_ENT];
+	dma_addr_t SG_handle;
+	int SG_length;
+
+	/* SG List of Buffers */
+	struct scatterlist SGlist[IVTV_DMA_SG_OSD_ENT];
+};
+
+struct ivtv_dma_page_info {
+	unsigned long uaddr;
+	unsigned long first;
+	unsigned long last;
+	unsigned int offset;
+	unsigned int tail;
+	int page_count;
+};
+
+struct ivtv_buffer {
+	struct list_head list;
+	dma_addr_t dma_handle;
+	unsigned long b_flags;
+	char *buf;
+
+	u32 bytesused;
+	u32 readpos;
+};
+
+struct ivtv_queue {
+	struct list_head list;
+	u32 buffers;
+	u32 length;
+	u32 bytesused;
+};
+
+struct ivtv;	/* forward reference */
+
+struct ivtv_stream {
+	/* These first four fields are always set, even if the stream
+	   is not actually created. */
+	struct video_device *v4l2dev;	/* NULL when stream not created */
+	struct ivtv *itv; 		/* for ease of use */
+	const char *name;		/* name of the stream */
+	int type;			/* stream type */
+
+	u32 id;
+	spinlock_t qlock; 	/* locks access to the queues */
+	unsigned long s_flags;	/* status flags, see above */
+	int dma;		/* can be PCI_DMA_TODEVICE,
+				   PCI_DMA_FROMDEVICE or
+				   PCI_DMA_NONE */
+	u32 dma_offset;
+	u32 dma_backup;
+	u64 dma_pts;
+
+	int subtype;
+	wait_queue_head_t waitq;
+	u32 dma_last_offset;
+
+	/* Buffer Stats */
+	u32 buffers;
+	u32 buf_size;
+	u32 buffers_stolen;
+
+	/* Buffer Queues */
+	struct ivtv_queue q_free;	/* free buffers */
+	struct ivtv_queue q_full;	/* full buffers */
+	struct ivtv_queue q_io;		/* waiting for I/O */
+	struct ivtv_queue q_dma;	/* waiting for DMA */
+	struct ivtv_queue q_predma;	/* waiting for DMA */
+
+	/* Base Dev SG Array for cx23415/6 */
+	struct ivtv_SG_element *SGarray;
+	dma_addr_t SG_handle;
+	int SG_length;
+
+	/* SG List of Buffers */
+	struct scatterlist *SGlist;
+};
+
+struct ivtv_open_id {
+	u32 open_id;
+	int type;
+	enum v4l2_priority prio;
+	struct ivtv *itv;
+};
+
+#define IVTV_YUV_UPDATE_HORIZONTAL  0x01
+#define IVTV_YUV_UPDATE_VERTICAL    0x02
+
+struct yuv_frame_info
+{
+	u32 update;
+	int src_x;
+	int src_y;
+	unsigned int src_w;
+	unsigned int src_h;
+	int dst_x;
+	int dst_y;
+	unsigned int dst_w;
+	unsigned int dst_h;
+	int pan_x;
+	int pan_y;
+	u32 vis_w;
+	u32 vis_h;
+	u32 interlaced_y;
+	u32 interlaced_uv;
+	int tru_x;
+	u32 tru_w;
+	u32 tru_h;
+	u32 offset_y;
+};
+
+#define IVTV_YUV_MODE_INTERLACED	0x00
+#define IVTV_YUV_MODE_PROGRESSIVE	0x01
+#define IVTV_YUV_MODE_AUTO		0x02
+#define IVTV_YUV_MODE_MASK		0x03
+
+#define IVTV_YUV_SYNC_EVEN		0x00
+#define IVTV_YUV_SYNC_ODD		0x04
+#define IVTV_YUV_SYNC_MASK		0x04
+
+struct yuv_playback_info
+{
+	u32 reg_2834;
+	u32 reg_2838;
+	u32 reg_283c;
+	u32 reg_2840;
+	u32 reg_2844;
+	u32 reg_2848;
+	u32 reg_2854;
+	u32 reg_285c;
+	u32 reg_2864;
+
+	u32 reg_2870;
+	u32 reg_2874;
+	u32 reg_2890;
+	u32 reg_2898;
+	u32 reg_289c;
+
+	u32 reg_2918;
+	u32 reg_291c;
+	u32 reg_2920;
+	u32 reg_2924;
+	u32 reg_2928;
+	u32 reg_292c;
+	u32 reg_2930;
+
+	u32 reg_2934;
+
+	u32 reg_2938;
+	u32 reg_293c;
+	u32 reg_2940;
+	u32 reg_2944;
+	u32 reg_2948;
+	u32 reg_294c;
+	u32 reg_2950;
+	u32 reg_2954;
+	u32 reg_2958;
+	u32 reg_295c;
+	u32 reg_2960;
+	u32 reg_2964;
+	u32 reg_2968;
+	u32 reg_296c;
+
+	u32 reg_2970;
+
+	int v_filter_1;
+	int v_filter_2;
+	int h_filter;
+
+	u32 osd_x_offset;
+	u32 osd_y_offset;
+
+	u32 osd_x_pan;
+	u32 osd_y_pan;
+
+	u32 osd_vis_w;
+	u32 osd_vis_h;
+
+	int decode_height;
+
+	int frame_interlaced;
+	int frame_interlaced_last;
+
+	int lace_mode;
+	int lace_threshold;
+	int lace_sync_field;
+
+	atomic_t next_dma_frame;
+	atomic_t next_fill_frame;
+
+	u32 yuv_forced_update;
+	int update_frame;
+	struct yuv_frame_info new_frame_info[4];
+	struct yuv_frame_info old_frame_info;
+	struct yuv_frame_info old_frame_info_args;
+
+	void *blanking_ptr;
+	dma_addr_t blanking_dmaptr;
+};
+
+#define IVTV_VBI_FRAMES 32
+
+/* VBI data */
+struct vbi_info {
+	u32 dec_start;
+	u32 enc_start, enc_size;
+	int fpi;
+	u32 frame;
+	u32 dma_offset;
+	u8 cc_data_odd[256];
+	u8 cc_data_even[256];
+	int cc_pos;
+	u8 cc_no_update;
+	u8 vps[5];
+	u8 vps_found;
+	int wss;
+	u8 wss_found;
+	u8 wss_no_update;
+	u32 raw_decoder_line_size;
+	u8 raw_decoder_sav_odd_field;
+	u8 raw_decoder_sav_even_field;
+	u32 sliced_decoder_line_size;
+	u8 sliced_decoder_sav_odd_field;
+	u8 sliced_decoder_sav_even_field;
+	struct v4l2_format in;
+	/* convenience pointer to sliced struct in vbi_in union */
+	struct v4l2_sliced_vbi_format *sliced_in;
+	u32 service_set_in;
+	u32 service_set_out;
+	int insert_mpeg;
+
+	/* Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines.
+	   One for /dev/vbi0 and one for /dev/vbi8 */
+	struct v4l2_sliced_vbi_data sliced_data[36];
+	struct v4l2_sliced_vbi_data sliced_dec_data[36];
+
+	/* Buffer for VBI data inserted into MPEG stream.
+	   The first byte is a dummy byte that's never used.
+	   The next 16 bytes contain the MPEG header for the VBI data,
+	   the remainder is the actual VBI data.
+	   The max size accepted by the MPEG VBI reinsertion turns out
+	   to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes,
+	   where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is
+	   a single line header byte and 2 * 18 is the number of VBI lines per frame.
+
+	   However, it seems that the data must be 1K aligned, so we have to
+	   pad the data until the 1 or 2 K boundary.
+
+	   This pointer array will allocate 2049 bytes to store each VBI frame. */
+	u8 *sliced_mpeg_data[IVTV_VBI_FRAMES];
+	u32 sliced_mpeg_size[IVTV_VBI_FRAMES];
+	struct ivtv_buffer sliced_mpeg_buf;
+	u32 inserted_frame;
+
+	u32 start[2], count;
+	u32 raw_size;
+	u32 sliced_size;
+};
+
+/* forward declaration of struct defined in ivtv-cards.h */
+struct ivtv_card;
+
+/* Struct to hold info about ivtv cards */
+struct ivtv {
+	int num;		/* board number, -1 during init! */
+	char name[8];		/* board name for printk and interrupts (e.g. 'ivtv0') */
+	struct pci_dev *dev;	/* PCI device */
+	const struct ivtv_card *card;	/* card information */
+	const char *card_name;  /* full name of the card */
+	u8 has_cx23415;		/* 1 if it is a cx23415 based card, 0 for cx23416 */
+	u8 is_50hz;
+	u8 is_60hz;
+	u8 is_out_50hz;
+	u8 is_out_60hz;
+	u8 pvr150_workaround;   /* 1 if the cx25840 needs to workaround a PVR150 bug */
+	u8 nof_inputs;		/* number of video inputs */
+	u8 nof_audio_inputs;	/* number of audio inputs */
+	u32 v4l2_cap;		/* V4L2 capabilities of card */
+	u32 hw_flags; 		/* Hardware description of the board */
+
+	/* controlling Video decoder function */
+	int (*video_dec_func)(struct ivtv *, unsigned int, void *);
+
+	struct ivtv_options options; 	/* User options */
+	int stream_buf_size[IVTV_MAX_STREAMS]; /* Stream buffer size */
+	struct ivtv_stream streams[IVTV_MAX_STREAMS]; 	/* Stream data */
+	int speed;
+	u8 speed_mute_audio;
+	unsigned long i_flags;  /* global ivtv flags */
+	atomic_t capturing;	/* count number of active capture streams */
+	atomic_t decoding;	/* count number of active decoding streams */
+	u32 irq_rr_idx; /* Round-robin stream index */
+	int cur_dma_stream;	/* index of stream doing DMA */
+	u32 dma_data_req_offset;
+	u32 dma_data_req_size;
+	int output_mode;        /* NONE, MPG, YUV, UDMA YUV, passthrough */
+	spinlock_t lock;        /* lock access to this struct */
+	int search_pack_header;
+
+	spinlock_t dma_reg_lock; /* lock access to DMA engine registers */
+
+	/* User based DMA for OSD */
+	struct ivtv_user_dma udma;
+
+	int open_id;		/* incremented each time an open occurs, used as unique ID.
+				   starts at 1, so 0 can be used as uninitialized value
+				   in the stream->id. */
+
+	u32 base_addr;
+	u32 irqmask;
+
+	struct v4l2_prio_state prio;
+	struct workqueue_struct *irq_work_queues;
+	struct work_struct irq_work_queue;
+	struct timer_list dma_timer; /* Timer used to catch unfinished DMAs */
+
+	struct vbi_info vbi;
+
+	struct ivtv_mailbox_data enc_mbox;
+	struct ivtv_mailbox_data dec_mbox;
+	struct ivtv_api_cache api_cache[256]; 	/* Cached API Commands */
+
+	u8 card_rev;
+	volatile void __iomem *enc_mem, *dec_mem, *reg_mem;
+
+	u32 pgm_info_offset;
+	u32 pgm_info_num;
+	u32 pgm_info_write_idx;
+	u32 pgm_info_read_idx;
+	struct v4l2_enc_idx_entry pgm_info[IVTV_MAX_PGM_INDEX];
+
+	u64 mpg_data_received;
+	u64 vbi_data_inserted;
+
+	wait_queue_head_t cap_w;
+	/* when the next decoder event arrives this queue is woken up */
+	wait_queue_head_t event_waitq;
+	/* when the next decoder vsync arrives this queue is woken up */
+	wait_queue_head_t vsync_waitq;
+	/* when the current DMA is finished this queue is woken up */
+	wait_queue_head_t dma_waitq;
+
+	/* OSD support */
+	unsigned long osd_video_pbase;
+	int osd_global_alpha_state; /* 0=off : 1=on */
+	int osd_local_alpha_state;  /* 0=off : 1=on */
+	int osd_color_key_state;    /* 0=off : 1=on */
+	u8  osd_global_alpha;       /* Current global alpha */
+	u32 osd_color_key;          /* Current color key */
+	u32 osd_pixelformat; 	    /* Current pixel format */
+	struct v4l2_rect osd_rect;  /* Current OSD position and size */
+	struct v4l2_rect main_rect; /* Current Main window position and size */
+
+	u32 last_dec_timing[3];     /* Store last retrieved pts/scr/frame values */
+
+	/* i2c */
+	struct i2c_adapter i2c_adap;
+	struct i2c_algo_bit_data i2c_algo;
+	struct i2c_client i2c_client;
+	struct mutex i2c_bus_lock;
+	int i2c_state;
+	struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];
+
+	/* v4l2 and User settings */
+
+	/* codec settings */
+	struct cx2341x_mpeg_params params;
+	u32 audio_input;
+	u32 active_input;
+	u32 active_output;
+	v4l2_std_id std;
+	v4l2_std_id std_out;
+	v4l2_std_id tuner_std;	/* The norm of the tuner (fixed) */
+	u8 audio_stereo_mode;
+	u8 audio_bilingual_mode;
+
+	/* dualwatch */
+	unsigned long dualwatch_jiffies;
+	u16 dualwatch_stereo_mode;
+
+	/* Digitizer type */
+	int digitizer;		/* 0x00EF = saa7114 0x00FO = saa7115 0x0106 = mic */
+
+	u32 lastVsyncFrame;
+
+	struct yuv_playback_info yuv_info;
+	struct osd_info *osd_info;
+};
+
+/* Globals */
+extern struct ivtv *ivtv_cards[];
+extern int ivtv_cards_active;
+extern int ivtv_first_minor;
+extern spinlock_t ivtv_cards_lock;
+
+/*==============Prototypes==================*/
+
+/* Hardware/IRQ */
+void ivtv_set_irq_mask(struct ivtv *itv, u32 mask);
+void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask);
+
+/* try to set output mode, return current mode. */
+int ivtv_set_output_mode(struct ivtv *itv, int mode);
+
+/* return current output stream based on current mode */
+struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv);
+
+/* Return non-zero if a signal is pending */
+int ivtv_sleep_timeout(int timeout, int intr);
+
+/* Wait on queue, returns -EINTR if interrupted */
+int ivtv_waitq(wait_queue_head_t *waitq);
+
+/* Read Hauppauge eeprom */
+struct tveeprom; /* forward reference */
+void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv);
+
+/* This is a PCI post thing, where if the pci register is not read, then
+   the write doesn't always take effect right away. By reading back the
+   register any pending PCI writes will be performed (in order), and so
+   you can be sure that the writes are guaranteed to be done.
+
+   Rarely needed, only in some timing sensitive cases.
+   Apparently if this is not done some motherboards seem
+   to kill the firmware and get into the broken state until computer is
+   rebooted. */
+#define write_sync(val, reg) \
+	do { writel(val, reg); readl(reg); } while (0)
+
+#define read_reg(reg) readl(itv->reg_mem + (reg))
+#define write_reg(val, reg) writel(val, itv->reg_mem + (reg))
+#define write_reg_sync(val, reg) \
+	do { write_reg(val, reg); read_reg(reg); } while (0)
+
+#define read_enc(addr) readl(itv->enc_mem + (u32)(addr))
+#define write_enc(val, addr) writel(val, itv->enc_mem + (u32)(addr))
+#define write_enc_sync(val, addr) \
+	do { write_enc(val, addr); read_enc(addr); } while (0)
+
+#define read_dec(addr) readl(itv->dec_mem + (u32)(addr))
+#define write_dec(val, addr) writel(val, itv->dec_mem + (u32)(addr))
+#define write_dec_sync(val, addr) \
+	do { write_dec(val, addr); read_dec(addr); } while (0)
+
+#endif /* IVTV_DRIVER_H */
diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c
new file mode 100644
index 0000000..1637097
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-fileops.c
@@ -0,0 +1,921 @@
+/*
+    file operation functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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 "ivtv-driver.h"
+#include "ivtv-fileops.h"
+#include "ivtv-i2c.h"
+#include "ivtv-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+#include "ivtv-vbi.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-audio.h"
+#include "ivtv-streams.h"
+#include "ivtv-yuv.h"
+#include "ivtv-controls.h"
+#include "ivtv-ioctl.h"
+
+/* This function tries to claim the stream for a specific file descriptor.
+   If no one else is using this stream then the stream is claimed and
+   associated VBI streams are also automatically claimed.
+   Possible error returns: -EBUSY if someone else has claimed
+   the stream or 0 on success. */
+int ivtv_claim_stream(struct ivtv_open_id *id, int type)
+{
+	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[type];
+	struct ivtv_stream *s_vbi;
+	int vbi_type;
+
+	if (test_and_set_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
+		/* someone already claimed this stream */
+		if (s->id == id->open_id) {
+			/* yes, this file descriptor did. So that's OK. */
+			return 0;
+		}
+		if (s->id == -1 && (type == IVTV_DEC_STREAM_TYPE_VBI ||
+					 type == IVTV_ENC_STREAM_TYPE_VBI)) {
+			/* VBI is handled already internally, now also assign
+			   the file descriptor to this stream for external
+			   reading of the stream. */
+			s->id = id->open_id;
+			IVTV_DEBUG_INFO("Start Read VBI\n");
+			return 0;
+		}
+		/* someone else is using this stream already */
+		IVTV_DEBUG_INFO("Stream %d is busy\n", type);
+		return -EBUSY;
+	}
+	s->id = id->open_id;
+	if (type == IVTV_DEC_STREAM_TYPE_VBI) {
+		/* Enable reinsertion interrupt */
+		ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+	}
+
+	/* IVTV_DEC_STREAM_TYPE_MPG needs to claim IVTV_DEC_STREAM_TYPE_VBI,
+	   IVTV_ENC_STREAM_TYPE_MPG needs to claim IVTV_ENC_STREAM_TYPE_VBI
+	   (provided VBI insertion is on and sliced VBI is selected), for all
+	   other streams we're done */
+	if (type == IVTV_DEC_STREAM_TYPE_MPG) {
+		vbi_type = IVTV_DEC_STREAM_TYPE_VBI;
+	} else if (type == IVTV_ENC_STREAM_TYPE_MPG &&
+		   itv->vbi.insert_mpeg && itv->vbi.sliced_in->service_set) {
+		vbi_type = IVTV_ENC_STREAM_TYPE_VBI;
+	} else {
+		return 0;
+	}
+	s_vbi = &itv->streams[vbi_type];
+
+	if (!test_and_set_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags)) {
+		/* Enable reinsertion interrupt */
+		if (vbi_type == IVTV_DEC_STREAM_TYPE_VBI)
+			ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+	}
+	/* mark that it is used internally */
+	set_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags);
+	return 0;
+}
+
+/* This function releases a previously claimed stream. It will take into
+   account associated VBI streams. */
+void ivtv_release_stream(struct ivtv_stream *s)
+{
+	struct ivtv *itv = s->itv;
+	struct ivtv_stream *s_vbi;
+
+	s->id = -1;
+	if ((s->type == IVTV_DEC_STREAM_TYPE_VBI || s->type == IVTV_ENC_STREAM_TYPE_VBI) &&
+		test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) {
+		/* this stream is still in use internally */
+		return;
+	}
+	if (!test_and_clear_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
+		IVTV_DEBUG_WARN("Release stream %s not in use!\n", s->name);
+		return;
+	}
+
+	ivtv_flush_queues(s);
+
+	/* disable reinsertion interrupt */
+	if (s->type == IVTV_DEC_STREAM_TYPE_VBI)
+		ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+
+	/* IVTV_DEC_STREAM_TYPE_MPG needs to release IVTV_DEC_STREAM_TYPE_VBI,
+	   IVTV_ENC_STREAM_TYPE_MPG needs to release IVTV_ENC_STREAM_TYPE_VBI,
+	   for all other streams we're done */
+	if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+		s_vbi = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI];
+	else if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
+		s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+	else
+		return;
+
+	/* clear internal use flag */
+	if (!test_and_clear_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags)) {
+		/* was already cleared */
+		return;
+	}
+	if (s_vbi->id != -1) {
+		/* VBI stream still claimed by a file descriptor */
+		return;
+	}
+	/* disable reinsertion interrupt */
+	if (s_vbi->type == IVTV_DEC_STREAM_TYPE_VBI)
+		ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+	clear_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags);
+	ivtv_flush_queues(s_vbi);
+}
+
+static void ivtv_dualwatch(struct ivtv *itv)
+{
+	struct v4l2_tuner vt;
+	u16 new_bitmap;
+	u16 new_stereo_mode;
+	const u16 stereo_mask = 0x0300;
+	const u16 dual = 0x0200;
+
+	new_stereo_mode = itv->params.audio_properties & stereo_mask;
+	memset(&vt, 0, sizeof(vt));
+	ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, &vt);
+	if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
+		new_stereo_mode = dual;
+
+	if (new_stereo_mode == itv->dualwatch_stereo_mode)
+		return;
+
+	new_bitmap = new_stereo_mode | (itv->params.audio_properties & ~stereo_mask);
+
+	IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n",
+			   itv->dualwatch_stereo_mode, new_stereo_mode, new_bitmap);
+
+	if (ivtv_vapi(itv, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new_bitmap) == 0) {
+		itv->dualwatch_stereo_mode = new_stereo_mode;
+		return;
+	}
+	IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
+}
+
+static void ivtv_update_pgm_info(struct ivtv *itv)
+{
+	u32 wr_idx = (read_enc(itv->pgm_info_offset) - itv->pgm_info_offset - 4) / 24;
+	int cnt;
+	int i = 0;
+
+	if (wr_idx >= itv->pgm_info_num) {
+		IVTV_DEBUG_WARN("Invalid PGM index %d (>= %d)\n", wr_idx, itv->pgm_info_num);
+		return;
+	}
+	cnt = (wr_idx + itv->pgm_info_num - itv->pgm_info_write_idx) % itv->pgm_info_num;
+	while (i < cnt) {
+		int idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num;
+		struct v4l2_enc_idx_entry *e = itv->pgm_info + idx;
+		u32 addr = itv->pgm_info_offset + 4 + idx * 24;
+		const int mapping[] = { V4L2_ENC_IDX_FRAME_P, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_B, 0 };
+
+		e->offset = read_enc(addr + 4) + ((u64)read_enc(addr + 8) << 32);
+		if (e->offset > itv->mpg_data_received) {
+			break;
+		}
+		e->offset += itv->vbi_data_inserted;
+		e->length = read_enc(addr);
+		e->pts = read_enc(addr + 16) + ((u64)(read_enc(addr + 20) & 1) << 32);
+		e->flags = mapping[read_enc(addr + 12) & 3];
+		i++;
+	}
+	itv->pgm_info_write_idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num;
+}
+
+static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block, int *err)
+{
+	struct ivtv *itv = s->itv;
+	struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+	struct ivtv_buffer *buf;
+	DEFINE_WAIT(wait);
+
+	*err = 0;
+	while (1) {
+		if (s->type == IVTV_ENC_STREAM_TYPE_MPG) {
+			/* Process pending program info updates and pending VBI data */
+			ivtv_update_pgm_info(itv);
+
+			if (jiffies - itv->dualwatch_jiffies > HZ) {
+				itv->dualwatch_jiffies = jiffies;
+				ivtv_dualwatch(itv);
+			}
+
+			if (test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+			    !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) {
+				while ((buf = ivtv_dequeue(s_vbi, &s_vbi->q_full))) {
+					/* byteswap and process VBI data */
+					ivtv_process_vbi_data(itv, buf, s_vbi->dma_pts, s_vbi->type);
+					ivtv_enqueue(s_vbi, buf, &s_vbi->q_free);
+				}
+			}
+			buf = &itv->vbi.sliced_mpeg_buf;
+			if (buf->readpos != buf->bytesused) {
+				return buf;
+			}
+		}
+
+		/* do we have leftover data? */
+		buf = ivtv_dequeue(s, &s->q_io);
+		if (buf)
+			return buf;
+
+		/* do we have new data? */
+		buf = ivtv_dequeue(s, &s->q_full);
+		if (buf) {
+			if (!test_and_clear_bit(IVTV_F_B_NEED_BUF_SWAP, &buf->b_flags))
+				return buf;
+			if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
+				/* byteswap MPG data */
+				ivtv_buf_swap(buf);
+			else if (s->type != IVTV_DEC_STREAM_TYPE_VBI) {
+				/* byteswap and process VBI data */
+				ivtv_process_vbi_data(itv, buf, s->dma_pts, s->type);
+			}
+			return buf;
+		}
+		/* return if file was opened with O_NONBLOCK */
+		if (non_block) {
+			*err = -EAGAIN;
+			return NULL;
+		}
+
+		/* return if end of stream */
+		if (s->type != IVTV_DEC_STREAM_TYPE_VBI && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+			clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+			IVTV_DEBUG_INFO("EOS %s\n", s->name);
+			return NULL;
+		}
+
+		/* wait for more data to arrive */
+		prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
+		/* New buffers might have become available before we were added to the waitqueue */
+		if (!s->q_full.buffers)
+			schedule();
+		finish_wait(&s->waitq, &wait);
+		if (signal_pending(current)) {
+			/* return if a signal was received */
+			IVTV_DEBUG_INFO("User stopped %s\n", s->name);
+			*err = -EINTR;
+			return NULL;
+		}
+	}
+}
+
+static void ivtv_setup_sliced_vbi_buf(struct ivtv *itv)
+{
+	int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES;
+
+	itv->vbi.sliced_mpeg_buf.buf = itv->vbi.sliced_mpeg_data[idx];
+	itv->vbi.sliced_mpeg_buf.bytesused = itv->vbi.sliced_mpeg_size[idx];
+	itv->vbi.sliced_mpeg_buf.readpos = 0;
+}
+
+static size_t ivtv_copy_buf_to_user(struct ivtv_stream *s, struct ivtv_buffer *buf,
+		char __user *ubuf, size_t ucount)
+{
+	struct ivtv *itv = s->itv;
+	size_t len = buf->bytesused - buf->readpos;
+
+	if (len > ucount) len = ucount;
+	if (itv->vbi.insert_mpeg && s->type == IVTV_ENC_STREAM_TYPE_MPG &&
+	    itv->vbi.sliced_in->service_set && buf != &itv->vbi.sliced_mpeg_buf) {
+		const char *start = buf->buf + buf->readpos;
+		const char *p = start + 1;
+		const u8 *q;
+		u8 ch = itv->search_pack_header ? 0xba : 0xe0;
+		int stuffing, i;
+
+		while (start + len > p && (q = memchr(p, 0, start + len - p))) {
+			p = q + 1;
+			if ((char *)q + 15 >= buf->buf + buf->bytesused ||
+			    q[1] != 0 || q[2] != 1 || q[3] != ch) {
+				continue;
+			}
+			if (!itv->search_pack_header) {
+				if ((q[6] & 0xc0) != 0x80)
+					continue;
+				if (((q[7] & 0xc0) == 0x80 && (q[9] & 0xf0) == 0x20) ||
+				    ((q[7] & 0xc0) == 0xc0 && (q[9] & 0xf0) == 0x30)) {
+					ch = 0xba;
+					itv->search_pack_header = 1;
+					p = q + 9;
+				}
+				continue;
+			}
+			stuffing = q[13] & 7;
+			/* all stuffing bytes must be 0xff */
+			for (i = 0; i < stuffing; i++)
+				if (q[14 + i] != 0xff)
+					break;
+			if (i == stuffing && (q[4] & 0xc4) == 0x44 && (q[12] & 3) == 3 &&
+					q[14 + stuffing] == 0 && q[15 + stuffing] == 0 &&
+					q[16 + stuffing] == 1) {
+				itv->search_pack_header = 0;
+				len = (char *)q - start;
+				ivtv_setup_sliced_vbi_buf(itv);
+				break;
+			}
+		}
+	}
+	if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) {
+		IVTV_DEBUG_WARN("copy %zd bytes to user failed for %s\n", len, s->name);
+		return -EFAULT;
+	}
+	/*IVTV_INFO("copied %lld %d %d %d %d %d vbi %d\n", itv->mpg_data_received, len, ucount,
+			buf->readpos, buf->bytesused, buf->bytesused - buf->readpos - len,
+			buf == &itv->vbi.sliced_mpeg_buf); */
+	buf->readpos += len;
+	if (s->type == IVTV_ENC_STREAM_TYPE_MPG && buf != &itv->vbi.sliced_mpeg_buf)
+		itv->mpg_data_received += len;
+	return len;
+}
+
+static ssize_t ivtv_read(struct ivtv_stream *s, char __user *ubuf, size_t tot_count, int non_block)
+{
+	struct ivtv *itv = s->itv;
+	size_t tot_written = 0;
+	int single_frame = 0;
+
+	if (atomic_read(&itv->capturing) == 0 && s->id == -1) {
+		/* shouldn't happen */
+		IVTV_DEBUG_WARN("Stream %s not initialized before read\n", s->name);
+		return -EIO;
+	}
+
+	/* Each VBI buffer is one frame, the v4l2 API says that for VBI the frames should
+	   arrive one-by-one, so make sure we never output more than one VBI frame at a time */
+	if (s->type == IVTV_DEC_STREAM_TYPE_VBI ||
+			(s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set))
+		single_frame = 1;
+
+	for (;;) {
+		struct ivtv_buffer *buf;
+		int rc;
+
+		buf = ivtv_get_buffer(s, non_block, &rc);
+		if (buf == NULL && rc == -EAGAIN && tot_written)
+			break;
+		if (buf == NULL)
+			return rc;
+		rc = ivtv_copy_buf_to_user(s, buf, ubuf + tot_written, tot_count - tot_written);
+		if (buf != &itv->vbi.sliced_mpeg_buf) {
+			ivtv_enqueue(s, buf, (buf->readpos == buf->bytesused) ? &s->q_free : &s->q_io);
+		}
+		else if (buf->readpos == buf->bytesused) {
+			int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES;
+			itv->vbi.sliced_mpeg_size[idx] = 0;
+			itv->vbi.inserted_frame++;
+			itv->vbi_data_inserted += buf->bytesused;
+		}
+		if (rc < 0)
+			return rc;
+		tot_written += rc;
+
+		if (tot_written == tot_count || single_frame)
+			break;
+	}
+	return tot_written;
+}
+
+static ssize_t ivtv_read_pos(struct ivtv_stream *s, char __user *ubuf, size_t count,
+			loff_t *pos, int non_block)
+{
+	ssize_t rc = count ? ivtv_read(s, ubuf, count, non_block) : 0;
+	struct ivtv *itv = s->itv;
+
+	IVTV_DEBUG_INFO("read %zd from %s, got %zd\n", count, s->name, rc);
+	if (rc > 0)
+		pos += rc;
+	return rc;
+}
+
+int ivtv_start_capture(struct ivtv_open_id *id)
+{
+	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[id->type];
+	struct ivtv_stream *s_vbi;
+
+	if (s->type == IVTV_ENC_STREAM_TYPE_RAD ||
+	    s->type == IVTV_DEC_STREAM_TYPE_MPG ||
+	    s->type == IVTV_DEC_STREAM_TYPE_YUV ||
+	    s->type == IVTV_DEC_STREAM_TYPE_VOUT) {
+		/* you cannot read from these stream types. */
+		return -EPERM;
+	}
+
+	/* Try to claim this stream. */
+	if (ivtv_claim_stream(id, s->type))
+		return -EBUSY;
+
+	/* This stream does not need to start capturing */
+	if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+		set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+		return 0;
+	}
+
+	/* If capture is already in progress, then we also have to
+	   do nothing extra. */
+	if (test_bit(IVTV_F_S_STREAMOFF, &s->s_flags) || test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+		set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+		return 0;
+	}
+
+	/* Start VBI capture if required */
+	s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+	if (s->type == IVTV_ENC_STREAM_TYPE_MPG &&
+	    test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+	    !test_and_set_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) {
+		/* Note: the IVTV_ENC_STREAM_TYPE_VBI is claimed
+		   automatically when the MPG stream is claimed.
+		   We only need to start the VBI capturing. */
+		if (ivtv_start_v4l2_encode_stream(s_vbi)) {
+			IVTV_DEBUG_WARN("VBI capture start failed\n");
+
+			/* Failure, clean up and return an error */
+			clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags);
+			clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+			/* also releases the associated VBI stream */
+			ivtv_release_stream(s);
+			return -EIO;
+		}
+		IVTV_DEBUG_INFO("VBI insertion started\n");
+	}
+
+	/* Tell the card to start capturing */
+	if (!ivtv_start_v4l2_encode_stream(s)) {
+		/* We're done */
+		set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+		/* Resume a possibly paused encoder */
+		if (test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
+			ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1);
+		return 0;
+	}
+
+	/* failure, clean up */
+	IVTV_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name);
+
+	/* Note: the IVTV_ENC_STREAM_TYPE_VBI is released
+	   automatically when the MPG stream is released.
+	   We only need to stop the VBI capturing. */
+	if (s->type == IVTV_ENC_STREAM_TYPE_MPG &&
+	    test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) {
+		ivtv_stop_v4l2_encode_stream(s_vbi, 0);
+		clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags);
+	}
+	clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+	ivtv_release_stream(s);
+	return -EIO;
+}
+
+ssize_t ivtv_v4l2_read(struct file * filp, char __user *buf, size_t count, loff_t * pos)
+{
+	struct ivtv_open_id *id = filp->private_data;
+	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[id->type];
+	int rc;
+
+	IVTV_DEBUG_IOCTL("read %zd bytes from %s\n", count, s->name);
+
+	rc = ivtv_start_capture(id);
+	if (rc)
+		return rc;
+	return ivtv_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK);
+}
+
+int ivtv_start_decoding(struct ivtv_open_id *id, int speed)
+{
+	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[id->type];
+
+	if (atomic_read(&itv->decoding) == 0) {
+		if (ivtv_claim_stream(id, s->type)) {
+			/* someone else is using this stream already */
+			IVTV_DEBUG_WARN("start decode, stream already claimed\n");
+			return -EBUSY;
+		}
+		ivtv_start_v4l2_decode_stream(s, 0);
+	}
+	if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+		return ivtv_set_speed(itv, speed);
+	return 0;
+}
+
+ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *pos)
+{
+	struct ivtv_open_id *id = filp->private_data;
+	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[id->type];
+	struct ivtv_buffer *buf;
+	struct ivtv_queue q;
+	int bytes_written = 0;
+	int mode;
+	int rc;
+	DEFINE_WAIT(wait);
+
+	IVTV_DEBUG_IOCTL("write %zd bytes to %s\n", count, s->name);
+
+	if (s->type != IVTV_DEC_STREAM_TYPE_MPG &&
+	    s->type != IVTV_DEC_STREAM_TYPE_YUV &&
+	    s->type != IVTV_DEC_STREAM_TYPE_VOUT)
+		/* not decoder streams */
+		return -EPERM;
+
+	/* Try to claim this stream */
+	if (ivtv_claim_stream(id, s->type))
+		return -EBUSY;
+
+	/* This stream does not need to start any decoding */
+	if (s->type == IVTV_DEC_STREAM_TYPE_VOUT) {
+		set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+		return ivtv_write_vbi(itv, user_buf, count);
+	}
+
+	mode = s->type == IVTV_DEC_STREAM_TYPE_MPG ? OUT_MPG : OUT_YUV;
+
+	if (ivtv_set_output_mode(itv, mode) != mode) {
+	    ivtv_release_stream(s);
+	    return -EBUSY;
+	}
+	ivtv_queue_init(&q);
+	set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+
+retry:
+	for (;;) {
+		/* Gather buffers */
+		while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_io)))
+			ivtv_enqueue(s, buf, &q);
+		while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_free))) {
+			ivtv_enqueue(s, buf, &q);
+		}
+		if (q.buffers)
+			break;
+		if (filp->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
+		/* New buffers might have become free before we were added to the waitqueue */
+		if (!s->q_free.buffers)
+			schedule();
+		finish_wait(&s->waitq, &wait);
+		if (signal_pending(current)) {
+			IVTV_DEBUG_INFO("User stopped %s\n", s->name);
+			return -EINTR;
+		}
+	}
+
+	/* copy user data into buffers */
+	while ((buf = ivtv_dequeue(s, &q))) {
+		/* Make sure we really got all the user data */
+		rc = ivtv_buf_copy_from_user(s, buf, user_buf, count);
+
+		if (rc < 0) {
+			ivtv_queue_move(s, &q, NULL, &s->q_free, 0);
+			return rc;
+		}
+		user_buf += rc;
+		count -= rc;
+		bytes_written += rc;
+
+		if (buf->bytesused != s->buf_size) {
+			/* incomplete, leave in q_io for next time */
+			ivtv_enqueue(s, buf, &s->q_io);
+			break;
+		}
+		/* Byteswap MPEG buffer */
+		if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+			ivtv_buf_swap(buf);
+		ivtv_enqueue(s, buf, &s->q_full);
+	}
+
+	/* Start decoder (returns 0 if already started) */
+	rc = ivtv_start_decoding(id, itv->speed);
+	if (rc) {
+		IVTV_DEBUG_WARN("Failed start decode stream %s\n", s->name);
+
+		/* failure, clean up */
+		clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+		clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+		return rc;
+	}
+	if (test_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags)) {
+		if (s->q_full.length >= itv->dma_data_req_size) {
+			int got_sig;
+
+			prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
+			while (!(got_sig = signal_pending(current)) &&
+					test_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) {
+				schedule();
+			}
+			finish_wait(&itv->dma_waitq, &wait);
+			if (got_sig) {
+				IVTV_DEBUG_INFO("User interrupted %s\n", s->name);
+				return -EINTR;
+			}
+
+			clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
+			ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size);
+			ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 1);
+		}
+	}
+	/* more user data is available, wait until buffers become free
+	   to transfer the rest. */
+	if (count && !(filp->f_flags & O_NONBLOCK))
+		goto retry;
+	IVTV_DEBUG_INFO("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused);
+	return bytes_written;
+}
+
+unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait)
+{
+	struct ivtv_open_id *id = filp->private_data;
+	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[id->type];
+	int res = 0;
+
+	/* add stream's waitq to the poll list */
+	poll_wait(filp, &s->waitq, wait);
+
+	set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
+	if (test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags) ||
+	    test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags))
+		res = POLLPRI;
+
+	/* Allow write if buffers are available for writing */
+	if (s->q_free.buffers)
+		res |= POLLOUT | POLLWRNORM;
+	return res;
+}
+
+unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait)
+{
+	struct ivtv_open_id *id = filp->private_data;
+	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[id->type];
+	int eof = test_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+
+	/* Start a capture if there is none */
+	if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+		int rc = ivtv_start_capture(id);
+
+		if (rc) {
+			IVTV_DEBUG_INFO("Could not start capture for %s (%d)\n",
+					s->name, rc);
+			return POLLERR;
+		}
+	}
+
+	/* add stream's waitq to the poll list */
+	poll_wait(filp, &s->waitq, wait);
+
+	if (eof || s->q_full.length)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end)
+{
+	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[id->type];
+
+	IVTV_DEBUG_IOCTL("close() of %s\n", s->name);
+
+	/* 'Unclaim' this stream */
+
+	/* Stop capturing */
+	if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+		struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+
+		IVTV_DEBUG_INFO("close stopping capture\n");
+		/* Special case: a running VBI capture for VBI insertion
+		   in the mpeg stream. Need to stop that too. */
+		if (id->type == IVTV_ENC_STREAM_TYPE_MPG &&
+		    test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags) &&
+		    !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) {
+			IVTV_DEBUG_INFO("close stopping embedded VBI capture\n");
+			ivtv_stop_v4l2_encode_stream(s_vbi, 0);
+		}
+		if ((id->type == IVTV_DEC_STREAM_TYPE_VBI ||
+		     id->type == IVTV_ENC_STREAM_TYPE_VBI) &&
+		    test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) {
+			/* Also used internally, don't stop capturing */
+			s->id = -1;
+		}
+		else {
+			ivtv_stop_v4l2_encode_stream(s, gop_end);
+		}
+	}
+	clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+	clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+
+	ivtv_release_stream(s);
+}
+
+static void ivtv_stop_decoding(struct ivtv_open_id *id, int flags, u64 pts)
+{
+	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[id->type];
+
+	IVTV_DEBUG_IOCTL("close() of %s\n", s->name);
+
+	/* Stop decoding */
+	if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+		IVTV_DEBUG_INFO("close stopping decode\n");
+
+		ivtv_stop_v4l2_decode_stream(s, flags, pts);
+	}
+	clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+	clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+	if (id->type == IVTV_DEC_STREAM_TYPE_YUV && test_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags)) {
+		/* Restore registers we've changed & clean up any mess we've made */
+		ivtv_yuv_close(itv);
+	}
+	if (s->type == IVTV_DEC_STREAM_TYPE_YUV && itv->output_mode == OUT_YUV)
+	    itv->output_mode = OUT_NONE;
+	else if (s->type == IVTV_DEC_STREAM_TYPE_MPG && itv->output_mode == OUT_MPG)
+	    itv->output_mode = OUT_NONE;
+
+	itv->speed = 0;
+	ivtv_release_stream(s);
+}
+
+int ivtv_v4l2_close(struct inode *inode, struct file *filp)
+{
+	struct ivtv_open_id *id = filp->private_data;
+	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[id->type];
+
+	IVTV_DEBUG_IOCTL("close() of %s\n", s->name);
+
+	v4l2_prio_close(&itv->prio, &id->prio);
+
+	/* Easy case first: this stream was never claimed by us */
+	if (s->id != id->open_id) {
+		kfree(id);
+		return 0;
+	}
+
+	/* 'Unclaim' this stream */
+
+	/* Stop radio */
+	if (id->type == IVTV_ENC_STREAM_TYPE_RAD) {
+		/* Closing radio device, return to TV mode */
+		ivtv_mute(itv);
+		/* Mark that the radio is no longer in use */
+		clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
+		/* Switch tuner to TV */
+		ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std);
+		/* Select correct audio input (i.e. TV tuner or Line in) */
+		ivtv_audio_set_io(itv);
+		/* Done! Unmute and continue. */
+		ivtv_unmute(itv);
+		ivtv_release_stream(s);
+	} else if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) {
+		ivtv_stop_decoding(id, VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0);
+	} else {
+		ivtv_stop_capture(id, 0);
+	}
+	kfree(id);
+	return 0;
+}
+
+int ivtv_v4l2_open(struct inode *inode, struct file *filp)
+{
+	int x, y = 0;
+	struct ivtv_open_id *item;
+	struct ivtv *itv = NULL;
+	struct ivtv_stream *s = NULL;
+	int minor = MINOR(inode->i_rdev);
+
+	/* Find which card this open was on */
+	spin_lock(&ivtv_cards_lock);
+	for (x = 0; itv == NULL && x < ivtv_cards_active; x++) {
+		/* find out which stream this open was on */
+		for (y = 0; y < IVTV_MAX_STREAMS; y++) {
+			s = &ivtv_cards[x]->streams[y];
+			if (s->v4l2dev && s->v4l2dev->minor == minor) {
+				itv = ivtv_cards[x];
+				break;
+			}
+		}
+	}
+	spin_unlock(&ivtv_cards_lock);
+
+	if (itv == NULL) {
+		/* Couldn't find a device registered
+		   on that minor, shouldn't happen! */
+		printk(KERN_WARNING "ivtv: no ivtv device found on minor %d\n", minor);
+		return -ENXIO;
+	}
+
+	if (y == IVTV_DEC_STREAM_TYPE_MPG &&
+		test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_YUV].s_flags))
+		return -EBUSY;
+
+	if (y == IVTV_DEC_STREAM_TYPE_YUV &&
+		test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_MPG].s_flags))
+		return -EBUSY;
+
+	if (y == IVTV_DEC_STREAM_TYPE_YUV) {
+		if (read_reg(0x82c) == 0) {
+			IVTV_ERR("Tried to open YUV output device but need to send data to mpeg decoder before it can be used\n");
+			/* return -ENODEV; */
+		}
+		ivtv_udma_alloc(itv);
+	}
+
+	/* Allocate memory */
+	item = kmalloc(sizeof(struct ivtv_open_id), GFP_KERNEL);
+	if (NULL == item) {
+		IVTV_DEBUG_WARN("nomem on v4l2 open\n");
+		return -ENOMEM;
+	}
+	item->itv = itv;
+	item->type = y;
+	v4l2_prio_open(&itv->prio, &item->prio);
+
+	item->open_id = itv->open_id++;
+	filp->private_data = item;
+
+	if (item->type == IVTV_ENC_STREAM_TYPE_RAD) {
+		/* Try to claim this stream */
+		if (ivtv_claim_stream(item, item->type)) {
+			/* No, it's already in use */
+			kfree(item);
+			return -EBUSY;
+		}
+
+		/* We have the radio */
+		ivtv_mute(itv);
+		/* Switch tuner to radio */
+		ivtv_call_i2c_clients(itv, AUDC_SET_RADIO, NULL);
+		/* Mark that the radio is being used. */
+		set_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
+		/* Select the correct audio input (i.e. radio tuner) */
+		ivtv_audio_set_io(itv);
+		/* Done! Unmute and continue. */
+		ivtv_unmute(itv);
+	}
+
+	/* YUV or MPG Decoding Mode? */
+	if (y == IVTV_DEC_STREAM_TYPE_MPG)
+		clear_bit(IVTV_F_I_DEC_YUV, &itv->i_flags);
+	else if (y == IVTV_DEC_STREAM_TYPE_YUV)
+	{
+		set_bit(IVTV_F_I_DEC_YUV, &itv->i_flags);
+	}
+
+	return 0;
+}
+
+void ivtv_mute(struct ivtv *itv)
+{
+	struct v4l2_control ctrl = { V4L2_CID_AUDIO_MUTE, 1 };
+
+	/* Mute sound to avoid pop */
+	ivtv_control_ioctls(itv, VIDIOC_S_CTRL, &ctrl);
+
+	if (atomic_read(&itv->capturing))
+		ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 1);
+
+	IVTV_DEBUG_INFO("Mute\n");
+}
+
+void ivtv_unmute(struct ivtv *itv)
+{
+	struct v4l2_control ctrl = { V4L2_CID_AUDIO_MUTE, 0 };
+
+	/* initialize or refresh input */
+	if (atomic_read(&itv->capturing) == 0)
+		ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
+
+	ivtv_sleep_timeout(HZ / 10, 0);
+
+	if (atomic_read(&itv->capturing)) {
+		ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12);
+		ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 0);
+	}
+
+	/* Unmute */
+	ivtv_control_ioctls(itv, VIDIOC_S_CTRL, &ctrl);
+	IVTV_DEBUG_INFO("Unmute\n");
+}
diff --git a/drivers/media/video/ivtv/ivtv-fileops.h b/drivers/media/video/ivtv/ivtv-fileops.h
new file mode 100644
index 0000000..74a1745
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-fileops.h
@@ -0,0 +1,44 @@
+/*
+    file operation functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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
+ */
+
+/* Testing/Debugging */
+int ivtv_v4l2_open(struct inode *inode, struct file *filp);
+ssize_t ivtv_v4l2_read(struct file *filp, char __user *buf, size_t count,
+		      loff_t * pos);
+ssize_t ivtv_v4l2_write(struct file *filp, const char __user *buf, size_t count,
+		       loff_t * pos);
+int ivtv_v4l2_close(struct inode *inode, struct file *filp);
+unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait);
+unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table * wait);
+int ivtv_start_capture(struct ivtv_open_id *id);
+void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end);
+int ivtv_start_decoding(struct ivtv_open_id *id, int speed);
+void ivtv_mute(struct ivtv *itv);
+void ivtv_unmute(struct ivtv *itv);
+
+/* Utilities */
+
+/* Try to claim a stream for the filehandle. Return 0 on success,
+   -EBUSY if stream already claimed. Once a stream is claimed, it
+   remains claimed until the associated filehandle is closed. */
+int ivtv_claim_stream(struct ivtv_open_id *id, int type);
+
+/* Release a previously claimed stream. */
+void ivtv_release_stream(struct ivtv_stream *s);
diff --git a/drivers/media/video/ivtv/ivtv-firmware.c b/drivers/media/video/ivtv/ivtv-firmware.c
new file mode 100644
index 0000000..d4c910b
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-firmware.c
@@ -0,0 +1,272 @@
+/*
+    ivtv firmware functions.
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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 "ivtv-driver.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-firmware.h"
+#include <linux/firmware.h>
+
+#define IVTV_MASK_SPU_ENABLE 		0xFFFFFFFE
+#define IVTV_MASK_VPU_ENABLE15 		0xFFFFFFF6
+#define IVTV_MASK_VPU_ENABLE16 		0xFFFFFFFB
+#define IVTV_CMD_VDM_STOP 		0x00000000
+#define IVTV_CMD_AO_STOP 		0x00000005
+#define IVTV_CMD_APU_PING 		0x00000000
+#define IVTV_CMD_VPU_STOP15 		0xFFFFFFFE
+#define IVTV_CMD_VPU_STOP16 		0xFFFFFFEE
+#define IVTV_CMD_HW_BLOCKS_RST 		0xFFFFFFFF
+#define IVTV_CMD_SPU_STOP 		0x00000001
+#define IVTV_CMD_SDRAM_PRECHARGE_INIT 	0x0000001A
+#define IVTV_CMD_SDRAM_REFRESH_INIT 	0x80000640
+#define IVTV_SDRAM_SLEEPTIME 		(60 * HZ / 100)	/* 600 ms */
+
+#define IVTV_DECODE_INIT_MPEG_FILENAME 	"v4l-cx2341x-init.mpg"
+#define IVTV_DECODE_INIT_MPEG_SIZE 	(152*1024)
+
+/* Encoder/decoder firmware sizes */
+#define IVTV_FW_ENC_SIZE 		(376836)
+#define IVTV_FW_DEC_SIZE 		(256*1024)
+
+static int load_fw_direct(const char *fn, volatile u8 __iomem *mem, struct ivtv *itv, long size)
+{
+	const struct firmware *fw = NULL;
+	int retries = 3;
+
+retry:
+	if (retries && request_firmware(&fw, fn, &itv->dev->dev) == 0) {
+		int i;
+		volatile u32 __iomem *dst = (volatile u32 __iomem *)mem;
+		const u32 *src = (const u32 *)fw->data;
+
+		/* temporarily allow 256 KB encoding firmwares as well for
+		   compatibility with blackbird cards */
+		if (fw->size != size && fw->size != 256 * 1024) {
+			/* Due to race conditions in firmware loading (esp. with udev <0.95)
+			   the wrong file was sometimes loaded. So we check filesizes to
+			   see if at least the right-sized file was loaded. If not, then we
+			   retry. */
+			IVTV_INFO("retry: file loaded was not %s (expected size %ld, got %zd)\n", fn, size, fw->size);
+			release_firmware(fw);
+			retries--;
+			goto retry;
+		}
+		for (i = 0; i < fw->size; i += 4) {
+			/* no need for endianness conversion on the ppc */
+			__raw_writel(*src, dst);
+			dst++;
+			src++;
+		}
+		release_firmware(fw);
+		IVTV_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size);
+		return size;
+	}
+	IVTV_ERR("unable to open firmware %s (must be %ld bytes)\n", fn, size);
+	IVTV_ERR("did you put the firmware in the hotplug firmware directory?\n");
+	return -ENOMEM;
+}
+
+void ivtv_halt_firmware(struct ivtv *itv)
+{
+	IVTV_DEBUG_INFO("Preparing for firmware halt.\n");
+	if (itv->has_cx23415 && itv->dec_mbox.mbox)
+		ivtv_vapi(itv, CX2341X_DEC_HALT_FW, 0);
+	if (itv->enc_mbox.mbox)
+		ivtv_vapi(itv, CX2341X_ENC_HALT_FW, 0);
+
+	ivtv_sleep_timeout(HZ / 100, 0);
+	itv->enc_mbox.mbox = itv->dec_mbox.mbox = NULL;
+
+	IVTV_DEBUG_INFO("Stopping VDM\n");
+	write_reg(IVTV_CMD_VDM_STOP, IVTV_REG_VDM);
+
+	IVTV_DEBUG_INFO("Stopping AO\n");
+	write_reg(IVTV_CMD_AO_STOP, IVTV_REG_AO);
+
+	IVTV_DEBUG_INFO("pinging (?) APU\n");
+	write_reg(IVTV_CMD_APU_PING, IVTV_REG_APU);
+
+	IVTV_DEBUG_INFO("Stopping VPU\n");
+	if (!itv->has_cx23415)
+		write_reg(IVTV_CMD_VPU_STOP16, IVTV_REG_VPU);
+	else
+		write_reg(IVTV_CMD_VPU_STOP15, IVTV_REG_VPU);
+
+	IVTV_DEBUG_INFO("Resetting Hw Blocks\n");
+	write_reg(IVTV_CMD_HW_BLOCKS_RST, IVTV_REG_HW_BLOCKS);
+
+	IVTV_DEBUG_INFO("Stopping SPU\n");
+	write_reg(IVTV_CMD_SPU_STOP, IVTV_REG_SPU);
+
+	ivtv_sleep_timeout(HZ / 100, 0);
+
+	IVTV_DEBUG_INFO("init Encoder SDRAM pre-charge\n");
+	write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_ENC_SDRAM_PRECHARGE);
+
+	IVTV_DEBUG_INFO("init Encoder SDRAM refresh to 1us\n");
+	write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_ENC_SDRAM_REFRESH);
+
+	if (itv->has_cx23415) {
+		IVTV_DEBUG_INFO("init Decoder SDRAM pre-charge\n");
+		write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_DEC_SDRAM_PRECHARGE);
+
+		IVTV_DEBUG_INFO("init Decoder SDRAM refresh to 1us\n");
+		write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_DEC_SDRAM_REFRESH);
+	}
+
+	IVTV_DEBUG_INFO("Sleeping for %dms (600 recommended)\n",
+		   (int)(IVTV_SDRAM_SLEEPTIME * 1000 / HZ));
+	ivtv_sleep_timeout(IVTV_SDRAM_SLEEPTIME, 0);
+}
+
+void ivtv_firmware_versions(struct ivtv *itv)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+
+	/* Encoder */
+	ivtv_vapi_result(itv, data, CX2341X_ENC_GET_VERSION, 0);
+	IVTV_INFO("Encoder revision: 0x%08x\n", data[0]);
+
+	if (data[0] != 0x02060039)
+		IVTV_WARN("Recommended firmware version is 0x02060039.\n");
+
+	if (itv->has_cx23415) {
+		/* Decoder */
+		ivtv_vapi_result(itv, data, CX2341X_DEC_GET_VERSION, 0);
+		IVTV_INFO("Decoder revision: 0x%08x\n", data[0]);
+	}
+}
+
+static int ivtv_firmware_copy(struct ivtv *itv)
+{
+	IVTV_DEBUG_INFO("Loading encoder image\n");
+	if (load_fw_direct(CX2341X_FIRM_ENC_FILENAME,
+		   itv->enc_mem, itv, IVTV_FW_ENC_SIZE) != IVTV_FW_ENC_SIZE) {
+		IVTV_DEBUG_WARN("failed loading encoder firmware\n");
+		return -3;
+	}
+	if (!itv->has_cx23415)
+		return 0;
+
+	IVTV_DEBUG_INFO("Loading decoder image\n");
+	if (load_fw_direct(CX2341X_FIRM_DEC_FILENAME,
+		   itv->dec_mem, itv, IVTV_FW_DEC_SIZE) != IVTV_FW_DEC_SIZE) {
+		IVTV_DEBUG_WARN("failed loading decoder firmware\n");
+		return -1;
+	}
+	return 0;
+}
+
+static volatile struct ivtv_mailbox __iomem *ivtv_search_mailbox(const volatile u8 __iomem *mem, u32 size)
+{
+	int i;
+
+	/* mailbox is preceeded by a 16 byte 'magic cookie' starting at a 256-byte
+	   address boundary */
+	for (i = 0; i < size; i += 0x100) {
+		if (readl(mem + i)      == 0x12345678 &&
+		    readl(mem + i + 4)  == 0x34567812 &&
+		    readl(mem + i + 8)  == 0x56781234 &&
+		    readl(mem + i + 12) == 0x78123456) {
+			return (volatile struct ivtv_mailbox __iomem *)(mem + i + 16);
+		}
+	}
+	return NULL;
+}
+
+int ivtv_firmware_init(struct ivtv *itv)
+{
+	int err;
+
+	ivtv_halt_firmware(itv);
+
+	/* load firmware */
+	err = ivtv_firmware_copy(itv);
+	if (err) {
+		IVTV_DEBUG_WARN("Error %d loading firmware\n", err);
+		return err;
+	}
+
+	/* start firmware */
+	write_reg(read_reg(IVTV_REG_SPU) & IVTV_MASK_SPU_ENABLE, IVTV_REG_SPU);
+	ivtv_sleep_timeout(HZ / 10, 0);
+	if (itv->has_cx23415)
+		write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE15, IVTV_REG_VPU);
+	else
+		write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE16, IVTV_REG_VPU);
+	ivtv_sleep_timeout(HZ / 10, 0);
+
+	/* find mailboxes and ping firmware */
+	itv->enc_mbox.mbox = ivtv_search_mailbox(itv->enc_mem, IVTV_ENCODER_SIZE);
+	if (itv->enc_mbox.mbox == NULL)
+		IVTV_ERR("Encoder mailbox not found\n");
+	else if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0)) {
+		IVTV_ERR("Encoder firmware dead!\n");
+		itv->enc_mbox.mbox = NULL;
+	}
+	if (itv->enc_mbox.mbox == NULL)
+		return -ENODEV;
+
+	if (!itv->has_cx23415)
+		return 0;
+
+	itv->dec_mbox.mbox = ivtv_search_mailbox(itv->dec_mem, IVTV_DECODER_SIZE);
+	if (itv->dec_mbox.mbox == NULL)
+		IVTV_ERR("Decoder mailbox not found\n");
+	else if (itv->has_cx23415 && ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0)) {
+		IVTV_ERR("Decoder firmware dead!\n");
+		itv->dec_mbox.mbox = NULL;
+	}
+	return itv->dec_mbox.mbox ? 0 : -ENODEV;
+}
+
+void ivtv_init_mpeg_decoder(struct ivtv *itv)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	long readbytes;
+	volatile u8 __iomem *mem_offset;
+
+	data[0] = 0;
+	data[1] = itv->params.width;	/* YUV source width */
+	data[2] = itv->params.height;
+	data[3] = itv->params.audio_properties;	/* Audio settings to use,
+							   bitmap. see docs. */
+	if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) {
+		IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n");
+		return;
+	}
+
+	if (ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1) != 0) {
+		IVTV_ERR("ivtv_init_mpeg_decoder failed to start playback\n");
+		return;
+	}
+	ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data);
+	mem_offset = itv->dec_mem + data[1];
+
+	if ((readbytes = load_fw_direct(IVTV_DECODE_INIT_MPEG_FILENAME,
+		mem_offset, itv, IVTV_DECODE_INIT_MPEG_SIZE)) <= 0) {
+		IVTV_DEBUG_WARN("failed to read mpeg decoder initialisation file %s\n",
+				IVTV_DECODE_INIT_MPEG_FILENAME);
+	} else {
+		ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, readbytes, 0);
+		ivtv_sleep_timeout(HZ / 10, 0);
+	}
+	ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 4, 0, 0, 0, 1);
+}
diff --git a/drivers/media/video/ivtv/ivtv-firmware.h b/drivers/media/video/ivtv/ivtv-firmware.h
new file mode 100644
index 0000000..8b2ffe6
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-firmware.h
@@ -0,0 +1,25 @@
+/*
+    ivtv firmware functions.
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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
+ */
+
+int ivtv_firmware_init(struct ivtv *itv);
+void ivtv_firmware_versions(struct ivtv *itv);
+void ivtv_halt_firmware(struct ivtv *itv);
+void ivtv_init_mpeg_decoder(struct ivtv *itv);
diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c
new file mode 100644
index 0000000..bc8f8ca
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-gpio.c
@@ -0,0 +1,307 @@
+/*
+    gpio functions.
+    Merging GPIO support into driver:
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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 "ivtv-driver.h"
+#include "ivtv-cards.h"
+#include "ivtv-gpio.h"
+#include <media/tuner.h>
+
+/*
+ * GPIO assignment of Yuan MPG600/MPG160
+ *
+ *    bit 15  14  13  12 |  11  10   9   8 |   7   6   5   4 |   3   2   1   0
+ * OUTPUT         IN1 IN0                                       AM3 AM2 AM1 AM0
+ *  INPUT                   DM1         DM0
+ *
+ *   IN* : Input selection
+ *          IN1 IN0
+ *           1   1  N/A
+ *           1   0  Line
+ *           0   1  N/A
+ *           0   0  Tuner
+ *
+ *   AM* : Audio Mode
+ *          AM3  0: Normal        1: Mixed(Sub+Main channel)
+ *          AM2  0: Subchannel    1: Main channel
+ *          AM1  0: Stereo        1: Mono
+ *          AM0  0: Normal        1: Mute
+ *
+ *   DM* : Detected tuner audio Mode
+ *          DM1  0: Stereo        1: Mono
+ *          DM0  0: Multiplex     1: Normal
+ *
+ * GPIO Initial Settings
+ *           MPG600   MPG160
+ *     DIR   0x3080   0x7080
+ *  OUTPUT   0x000C   0x400C
+ *
+ *  Special thanks to Makoto Iguchi <iguchi@tahoo.org> and Mr. Anonymous
+ *  for analyzing GPIO of MPG160.
+ *
+ *****************************************************************************
+ *
+ * GPIO assignment of Avermedia M179 (per information direct from AVerMedia)
+ *
+ *    bit 15  14  13  12 |  11  10   9   8 |   7   6   5   4 |   3   2   1   0
+ * OUTPUT IN0 AM0 IN1               AM1 AM2       IN2     BR0   BR1
+ *  INPUT
+ *
+ *   IN* : Input selection
+ *          IN0 IN1 IN2
+ *           *   1   *  Mute
+ *           0   0   0  Line-In
+ *           1   0   0  TV Tuner Audio
+ *           0   0   1  FM Audio
+ *           1   0   1  Mute
+ *
+ *   AM* : Audio Mode
+ *          AM0 AM1 AM2
+ *           0   0   0  TV Tuner Audio: L_OUT=(L+R)/2, R_OUT=SAP
+ *           0   0   1  TV Tuner Audio: L_OUT=R_OUT=SAP   (SAP)
+ *           0   1   0  TV Tuner Audio: L_OUT=L, R_OUT=R   (stereo)
+ *           0   1   1  TV Tuner Audio: mute
+ *           1   *   *  TV Tuner Audio: L_OUT=R_OUT=(L+R)/2   (mono)
+ *
+ *   BR* : Audio Sample Rate (BR stands for bitrate for some reason)
+ *          BR0 BR1
+ *           0   0   32 kHz
+ *           0   1   44.1 kHz
+ *           1   0   48 kHz
+ *
+ *   DM* : Detected tuner audio Mode
+ *         Unknown currently
+ *
+ * Special thanks to AVerMedia Technologies, Inc. and Jiun-Kuei Jung at
+ * AVerMedia for providing the GPIO information used to add support
+ * for the M179 cards.
+ */
+
+/********************* GPIO stuffs *********************/
+
+/* GPIO registers */
+#define IVTV_REG_GPIO_IN    0x9008
+#define IVTV_REG_GPIO_OUT   0x900c
+#define IVTV_REG_GPIO_DIR   0x9020
+
+void ivtv_reset_ir_gpio(struct ivtv *itv)
+{
+	int curdir, curout;
+
+	if (itv->card->type != IVTV_CARD_PVR_150)
+		return;
+	IVTV_DEBUG_INFO("Resetting PVR150 IR\n");
+	curout = read_reg(IVTV_REG_GPIO_OUT);
+	curdir = read_reg(IVTV_REG_GPIO_DIR);
+	curdir |= 0x80;
+	write_reg(curdir, IVTV_REG_GPIO_DIR);
+	curout = (curout & ~0xF) | 1;
+	write_reg(curout, IVTV_REG_GPIO_OUT);
+	/* We could use something else for smaller time */
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout(1);
+	curout |= 2;
+	write_reg(curout, IVTV_REG_GPIO_OUT);
+	curdir &= ~0x80;
+	write_reg(curdir, IVTV_REG_GPIO_DIR);
+}
+
+#ifdef HAVE_XC3028
+int ivtv_reset_tuner_gpio(enum v4l2_tuner_type mode, void *priv, int ptr)
+{
+	int curdir, curout;
+	struct ivtv *itv = (struct ivtv *) priv;
+
+	if (itv->card->type != IVTV_CARD_PG600V2 || itv->options.tuner != TUNER_XCEIVE_XC3028)
+		return -EINVAL;
+	IVTV_INFO("Resetting tuner.\n");
+	curout = read_reg(IVTV_REG_GPIO_OUT);
+	curdir = read_reg(IVTV_REG_GPIO_DIR);
+	curdir |= (1 << 12);  /* GPIO bit 12 */
+
+	curout &= ~(1 << 12);
+	write_reg(curout, IVTV_REG_GPIO_OUT);
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout(1);
+
+	curout |= (1 << 12);
+	write_reg(curout, IVTV_REG_GPIO_OUT);
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout(1);
+
+	return 0;
+}
+#endif
+
+void ivtv_gpio_init(struct ivtv *itv)
+{
+	if (itv->card->gpio_init.direction == 0)
+		return;
+
+	IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n",
+		   read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT));
+
+	/* init output data then direction */
+	write_reg(itv->card->gpio_init.initial_value, IVTV_REG_GPIO_OUT);
+	write_reg(itv->card->gpio_init.direction, IVTV_REG_GPIO_DIR);
+}
+
+static struct v4l2_queryctrl gpio_ctrl_mute = {
+	.id            = V4L2_CID_AUDIO_MUTE,
+	.type          = V4L2_CTRL_TYPE_BOOLEAN,
+	.name          = "Mute",
+	.minimum       = 0,
+	.maximum       = 1,
+	.step          = 1,
+	.default_value = 1,
+	.flags         = 0,
+};
+
+int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg)
+{
+	struct v4l2_tuner *tuner = arg;
+	struct v4l2_control *ctrl = arg;
+	struct v4l2_routing *route = arg;
+	u16 mask, data;
+
+	switch (command) {
+	case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+		mask = itv->card->gpio_audio_freq.mask;
+		switch (*(u32 *)arg) {
+		case 32000:
+			data = itv->card->gpio_audio_freq.f32000;
+			break;
+		case 44100:
+			data = itv->card->gpio_audio_freq.f44100;
+			break;
+		case 48000:
+		default:
+			data = itv->card->gpio_audio_freq.f48000;
+			break;
+		}
+		break;
+
+	case VIDIOC_G_TUNER:
+		mask = itv->card->gpio_audio_detect.mask;
+		if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask))
+			tuner->rxsubchans = V4L2_TUNER_MODE_STEREO |
+			       V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+		else
+			tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
+		return 0;
+
+	case VIDIOC_S_TUNER:
+		mask = itv->card->gpio_audio_mode.mask;
+		switch (tuner->audmode) {
+		case V4L2_TUNER_MODE_LANG1:
+			data = itv->card->gpio_audio_mode.lang1;
+			break;
+		case V4L2_TUNER_MODE_LANG2:
+			data = itv->card->gpio_audio_mode.lang2;
+			break;
+		case V4L2_TUNER_MODE_MONO:
+			data = itv->card->gpio_audio_mode.mono;
+			break;
+		case V4L2_TUNER_MODE_STEREO:
+		case V4L2_TUNER_MODE_LANG1_LANG2:
+		default:
+			data = itv->card->gpio_audio_mode.stereo;
+			break;
+		}
+		break;
+
+	case AUDC_SET_RADIO:
+		mask = itv->card->gpio_audio_input.mask;
+		data = itv->card->gpio_audio_input.radio;
+		break;
+
+	case VIDIOC_S_STD:
+		mask = itv->card->gpio_audio_input.mask;
+		data = itv->card->gpio_audio_input.tuner;
+		break;
+
+	case VIDIOC_INT_S_AUDIO_ROUTING:
+		if (route->input > 2)
+			return -EINVAL;
+		mask = itv->card->gpio_audio_input.mask;
+		switch (route->input) {
+			case 0:
+				data = itv->card->gpio_audio_input.tuner;
+				break;
+			case 1:
+				data = itv->card->gpio_audio_input.linein;
+				break;
+			case 2:
+			default:
+				data = itv->card->gpio_audio_input.radio;
+				break;
+		}
+		break;
+
+	case VIDIOC_G_CTRL:
+		if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+			return -EINVAL;
+		mask = itv->card->gpio_audio_mute.mask;
+		data = itv->card->gpio_audio_mute.mute;
+		ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data;
+		return 0;
+
+	case VIDIOC_S_CTRL:
+		if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+			return -EINVAL;
+		mask = itv->card->gpio_audio_mute.mask;
+		data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0;
+		break;
+
+	case VIDIOC_QUERYCTRL:
+	{
+		struct v4l2_queryctrl *qc = arg;
+
+		if (qc->id != V4L2_CID_AUDIO_MUTE)
+			return -EINVAL;
+		*qc = gpio_ctrl_mute;
+		return 0;
+	}
+
+	case VIDIOC_LOG_STATUS:
+		IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n",
+			read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT),
+			read_reg(IVTV_REG_GPIO_IN));
+		return 0;
+
+	case VIDIOC_INT_S_VIDEO_ROUTING:
+		if (route->input > 2) /* 0:Tuner 1:Composite 2:S-Video */
+			return -EINVAL;
+		mask = itv->card->gpio_video_input.mask;
+		if  (route->input == 0)
+			data = itv->card->gpio_video_input.tuner;
+		else if  (route->input == 1)
+			data = itv->card->gpio_video_input.composite;
+		else
+			data = itv->card->gpio_video_input.svideo;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	if (mask)
+		write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+	return 0;
+}
diff --git a/drivers/media/video/ivtv/ivtv-gpio.h b/drivers/media/video/ivtv/ivtv-gpio.h
new file mode 100644
index 0000000..c301d2a
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-gpio.h
@@ -0,0 +1,25 @@
+/*
+    gpio functions.
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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
+ */
+
+/* GPIO stuff */
+void ivtv_gpio_init(struct ivtv *itv);
+void ivtv_reset_ir_gpio(struct ivtv *itv);
+int ivtv_reset_tuner_gpio(enum v4l2_tuner_type mode, void *priv, int ptr);
+int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg);
diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c
new file mode 100644
index 0000000..50624c6
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-i2c.c
@@ -0,0 +1,748 @@
+/*
+    I2C functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+    This file includes an i2c implementation that was reverse engineered
+    from the Hauppauge windows driver.  Older ivtv versions used i2c-algo-bit,
+    which whilst fine under most circumstances, had trouble with the Zilog
+    CPU on the PVR-150 which handles IR functions (occasional inability to
+    communicate with the chip until it was reset) and also with the i2c
+    bus being completely unreachable when multiple PVR cards were present.
+
+    The implementation is very similar to i2c-algo-bit, but there are enough
+    subtle differences that the two are hard to merge.  The general strategy
+    employed by i2c-algo-bit is to use udelay() to implement the timing
+    when putting out bits on the scl/sda lines.  The general strategy taken
+    here is to poll the lines for state changes (see ivtv_waitscl and
+    ivtv_waitsda).  In addition there are small delays at various locations
+    which poll the SCL line 5 times (ivtv_scldelay).  I would guess that
+    since this is memory mapped I/O that the length of those delays is tied
+    to the PCI bus clock.  There is some extra code to do with recovery
+    and retries.  Since it is not known what causes the actual i2c problems
+    in the first place, the only goal if one was to attempt to use
+    i2c-algo-bit would be to try to make it follow the same code path.
+    This would be a lot of work, and I'm also not convinced that it would
+    provide a generic benefit to i2c-algo-bit.  Therefore consider this
+    an engineering solution -- not pretty, but it works.
+
+    Some more general comments about what we are doing:
+
+    The i2c bus is a 2 wire serial bus, with clock (SCL) and data (SDA)
+    lines.  To communicate on the bus (as a master, we don't act as a slave),
+    we first initiate a start condition (ivtv_start).  We then write the
+    address of the device that we want to communicate with, along with a flag
+    that indicates whether this is a read or a write.  The slave then issues
+    an ACK signal (ivtv_ack), which tells us that it is ready for reading /
+    writing.  We then proceed with reading or writing (ivtv_read/ivtv_write),
+    and finally issue a stop condition (ivtv_stop) to make the bus available
+    to other masters.
+
+    There is an additional form of transaction where a write may be
+    immediately followed by a read.  In this case, there is no intervening
+    stop condition.  (Only the msp3400 chip uses this method of data transfer).
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-cards.h"
+#include "ivtv-gpio.h"
+#include "ivtv-i2c.h"
+
+#include <media/ir-kbd-i2c.h>
+
+/* i2c implementation for cx23415/6 chip, ivtv project.
+ * Author: Kevin Thayer (nufan_wfk at yahoo.com)
+ */
+/* i2c stuff */
+#define IVTV_REG_I2C_SETSCL_OFFSET 0x7000
+#define IVTV_REG_I2C_SETSDA_OFFSET 0x7004
+#define IVTV_REG_I2C_GETSCL_OFFSET 0x7008
+#define IVTV_REG_I2C_GETSDA_OFFSET 0x700c
+
+#ifndef I2C_ADAP_CLASS_TV_ANALOG
+#define I2C_ADAP_CLASS_TV_ANALOG I2C_CLASS_TV_ANALOG
+#endif /* I2C_ADAP_CLASS_TV_ANALOG */
+
+#define IVTV_CS53L32A_I2C_ADDR		0x11
+#define IVTV_CX25840_I2C_ADDR 		0x44
+#define IVTV_SAA7115_I2C_ADDR 		0x21
+#define IVTV_SAA7127_I2C_ADDR 		0x44
+#define IVTV_SAA717x_I2C_ADDR 		0x21
+#define IVTV_MSP3400_I2C_ADDR 		0x40
+#define IVTV_HAUPPAUGE_I2C_ADDR 	0x50
+#define IVTV_WM8739_I2C_ADDR 		0x1a
+#define IVTV_WM8775_I2C_ADDR		0x1b
+#define IVTV_TEA5767_I2C_ADDR		0x60
+#define IVTV_UPD64031A_I2C_ADDR 	0x12
+#define IVTV_UPD64083_I2C_ADDR 		0x5c
+#define IVTV_TDA985X_I2C_ADDR      	0x5b
+
+/* This array should match the IVTV_HW_ defines */
+static const u8 hw_driverids[] = {
+	I2C_DRIVERID_CX25840,
+	I2C_DRIVERID_SAA711X,
+	I2C_DRIVERID_SAA7127,
+	I2C_DRIVERID_MSP3400,
+	I2C_DRIVERID_TUNER,
+	I2C_DRIVERID_WM8775,
+	I2C_DRIVERID_CS53L32A,
+	I2C_DRIVERID_TVEEPROM,
+	I2C_DRIVERID_SAA711X,
+	I2C_DRIVERID_TVAUDIO,
+	I2C_DRIVERID_UPD64031A,
+	I2C_DRIVERID_UPD64083,
+	I2C_DRIVERID_SAA717X,
+	I2C_DRIVERID_WM8739,
+	0 		/* IVTV_HW_GPIO dummy driver ID */
+};
+
+/* This array should match the IVTV_HW_ defines */
+static const char * const hw_drivernames[] = {
+	"cx2584x",
+	"saa7115",
+	"saa7127",
+	"msp3400",
+	"tuner",
+	"wm8775",
+	"cs53l32a",
+	"tveeprom",
+	"saa7114",
+	"tvaudio",
+	"upd64031a",
+	"upd64083",
+	"saa717x",
+	"wm8739",
+	"gpio",
+};
+
+static int attach_inform(struct i2c_client *client)
+{
+	struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter);
+	int i;
+
+	IVTV_DEBUG_I2C("i2c client attach\n");
+	for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+		if (itv->i2c_clients[i] == NULL) {
+			itv->i2c_clients[i] = client;
+			break;
+		}
+	}
+	if (i == I2C_CLIENTS_MAX) {
+		IVTV_ERR("insufficient room for new I2C client!\n");
+	}
+	return 0;
+}
+
+static int detach_inform(struct i2c_client *client)
+{
+	int i;
+	struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter);
+
+	IVTV_DEBUG_I2C("i2c client detach\n");
+	for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+		if (itv->i2c_clients[i] == client) {
+			itv->i2c_clients[i] = NULL;
+			break;
+		}
+	}
+	IVTV_DEBUG_I2C("i2c detach [client=%s,%s]\n",
+		   client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed");
+
+	return 0;
+}
+
+/* Set the serial clock line to the desired state */
+static void ivtv_setscl(struct ivtv *itv, int state)
+{
+	/* write them out */
+	/* write bits are inverted */
+	write_reg(~state, IVTV_REG_I2C_SETSCL_OFFSET);
+}
+
+/* Set the serial data line to the desired state */
+static void ivtv_setsda(struct ivtv *itv, int state)
+{
+	/* write them out */
+	/* write bits are inverted */
+	write_reg(~state & 1, IVTV_REG_I2C_SETSDA_OFFSET);
+}
+
+/* Read the serial clock line */
+static int ivtv_getscl(struct ivtv *itv)
+{
+	return read_reg(IVTV_REG_I2C_GETSCL_OFFSET) & 1;
+}
+
+/* Read the serial data line */
+static int ivtv_getsda(struct ivtv *itv)
+{
+	return read_reg(IVTV_REG_I2C_GETSDA_OFFSET) & 1;
+}
+
+/* Implement a short delay by polling the serial clock line */
+static void ivtv_scldelay(struct ivtv *itv)
+{
+	int i;
+
+	for (i = 0; i < 5; ++i)
+		ivtv_getscl(itv);
+}
+
+/* Wait for the serial clock line to become set to a specific value */
+static int ivtv_waitscl(struct ivtv *itv, int val)
+{
+	int i;
+
+	ivtv_scldelay(itv);
+	for (i = 0; i < 1000; ++i) {
+		if (ivtv_getscl(itv) == val)
+			return 1;
+	}
+	return 0;
+}
+
+/* Wait for the serial data line to become set to a specific value */
+static int ivtv_waitsda(struct ivtv *itv, int val)
+{
+	int i;
+
+	ivtv_scldelay(itv);
+	for (i = 0; i < 1000; ++i) {
+		if (ivtv_getsda(itv) == val)
+			return 1;
+	}
+	return 0;
+}
+
+/* Wait for the slave to issue an ACK */
+static int ivtv_ack(struct ivtv *itv)
+{
+	int ret = 0;
+
+	if (ivtv_getscl(itv) == 1) {
+		IVTV_DEBUG_I2C("SCL was high starting an ack\n");
+		ivtv_setscl(itv, 0);
+		if (!ivtv_waitscl(itv, 0)) {
+			IVTV_DEBUG_I2C("Could not set SCL low starting an ack\n");
+			return -EREMOTEIO;
+		}
+	}
+	ivtv_setsda(itv, 1);
+	ivtv_scldelay(itv);
+	ivtv_setscl(itv, 1);
+	if (!ivtv_waitsda(itv, 0)) {
+		IVTV_DEBUG_I2C("Slave did not ack\n");
+		ret = -EREMOTEIO;
+	}
+	ivtv_setscl(itv, 0);
+	if (!ivtv_waitscl(itv, 0)) {
+		IVTV_DEBUG_I2C("Failed to set SCL low after ACK\n");
+		ret = -EREMOTEIO;
+	}
+	return ret;
+}
+
+/* Write a single byte to the i2c bus and wait for the slave to ACK */
+static int ivtv_sendbyte(struct ivtv *itv, unsigned char byte)
+{
+	int i, bit;
+
+	IVTV_DEBUG_I2C("write %x\n",byte);
+	for (i = 0; i < 8; ++i, byte<<=1) {
+		ivtv_setscl(itv, 0);
+		if (!ivtv_waitscl(itv, 0)) {
+			IVTV_DEBUG_I2C("Error setting SCL low\n");
+			return -EREMOTEIO;
+		}
+		bit = (byte>>7)&1;
+		ivtv_setsda(itv, bit);
+		if (!ivtv_waitsda(itv, bit)) {
+			IVTV_DEBUG_I2C("Error setting SDA\n");
+			return -EREMOTEIO;
+		}
+		ivtv_setscl(itv, 1);
+		if (!ivtv_waitscl(itv, 1)) {
+			IVTV_DEBUG_I2C("Slave not ready for bit\n");
+			return -EREMOTEIO;
+		}
+	}
+	ivtv_setscl(itv, 0);
+	if (!ivtv_waitscl(itv, 0)) {
+		IVTV_DEBUG_I2C("Error setting SCL low\n");
+		return -EREMOTEIO;
+	}
+	return ivtv_ack(itv);
+}
+
+/* Read a byte from the i2c bus and send a NACK if applicable (i.e. for the
+   final byte) */
+static int ivtv_readbyte(struct ivtv *itv, unsigned char *byte, int nack)
+{
+	int i;
+
+	*byte = 0;
+
+	ivtv_setsda(itv, 1);
+	ivtv_scldelay(itv);
+	for (i = 0; i < 8; ++i) {
+		ivtv_setscl(itv, 0);
+		ivtv_scldelay(itv);
+		ivtv_setscl(itv, 1);
+		if (!ivtv_waitscl(itv, 1)) {
+			IVTV_DEBUG_I2C("Error setting SCL high\n");
+			return -EREMOTEIO;
+		}
+		*byte = ((*byte)<<1)|ivtv_getsda(itv);
+	}
+	ivtv_setscl(itv, 0);
+	ivtv_scldelay(itv);
+	ivtv_setsda(itv, nack);
+	ivtv_scldelay(itv);
+	ivtv_setscl(itv, 1);
+	ivtv_scldelay(itv);
+	ivtv_setscl(itv, 0);
+	ivtv_scldelay(itv);
+	IVTV_DEBUG_I2C("read %x\n",*byte);
+	return 0;
+}
+
+/* Issue a start condition on the i2c bus to alert slaves to prepare for
+   an address write */
+static int ivtv_start(struct ivtv *itv)
+{
+	int sda;
+
+	sda = ivtv_getsda(itv);
+	if (sda != 1) {
+		IVTV_DEBUG_I2C("SDA was low at start\n");
+		ivtv_setsda(itv, 1);
+		if (!ivtv_waitsda(itv, 1)) {
+			IVTV_DEBUG_I2C("SDA stuck low\n");
+			return -EREMOTEIO;
+		}
+	}
+	if (ivtv_getscl(itv) != 1) {
+		ivtv_setscl(itv, 1);
+		if (!ivtv_waitscl(itv, 1)) {
+			IVTV_DEBUG_I2C("SCL stuck low at start\n");
+			return -EREMOTEIO;
+		}
+	}
+	ivtv_setsda(itv, 0);
+	ivtv_scldelay(itv);
+	return 0;
+}
+
+/* Issue a stop condition on the i2c bus to release it */
+static int ivtv_stop(struct ivtv *itv)
+{
+	int i;
+
+	if (ivtv_getscl(itv) != 0) {
+		IVTV_DEBUG_I2C("SCL not low when stopping\n");
+		ivtv_setscl(itv, 0);
+		if (!ivtv_waitscl(itv, 0)) {
+			IVTV_DEBUG_I2C("SCL could not be set low\n");
+		}
+	}
+	ivtv_setsda(itv, 0);
+	ivtv_scldelay(itv);
+	ivtv_setscl(itv, 1);
+	if (!ivtv_waitscl(itv, 1)) {
+		IVTV_DEBUG_I2C("SCL could not be set high\n");
+		return -EREMOTEIO;
+	}
+	ivtv_scldelay(itv);
+	ivtv_setsda(itv, 1);
+	if (!ivtv_waitsda(itv, 1)) {
+		IVTV_DEBUG_I2C("resetting I2C\n");
+		for (i = 0; i < 16; ++i) {
+			ivtv_setscl(itv, 0);
+			ivtv_scldelay(itv);
+			ivtv_setscl(itv, 1);
+			ivtv_scldelay(itv);
+			ivtv_setsda(itv, 1);
+		}
+		ivtv_waitsda(itv, 1);
+		return -EREMOTEIO;
+	}
+	return 0;
+}
+
+/* Write a message to the given i2c slave.  do_stop may be 0 to prevent
+   issuing the i2c stop condition (when following with a read) */
+static int ivtv_write(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len, int do_stop)
+{
+	int retry, ret = -EREMOTEIO;
+	u32 i;
+
+	for (retry = 0; ret != 0 && retry < 8; ++retry) {
+		ret = ivtv_start(itv);
+
+		if (ret == 0) {
+			ret = ivtv_sendbyte(itv, addr<<1);
+			for (i = 0; ret == 0 && i < len; ++i)
+				ret = ivtv_sendbyte(itv, data[i]);
+		}
+		if (ret != 0 || do_stop) {
+			ivtv_stop(itv);
+		}
+	}
+	if (ret)
+		IVTV_DEBUG_I2C("i2c write to %x failed\n", addr);
+	return ret;
+}
+
+/* Read data from the given i2c slave.  A stop condition is always issued. */
+static int ivtv_read(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len)
+{
+	int retry, ret = -EREMOTEIO;
+	u32 i;
+
+	for (retry = 0; ret != 0 && retry < 8; ++retry) {
+		ret = ivtv_start(itv);
+		if (ret == 0)
+			ret = ivtv_sendbyte(itv, (addr << 1) | 1);
+		for (i = 0; ret == 0 && i < len; ++i) {
+			ret = ivtv_readbyte(itv, &data[i], i == len - 1);
+		}
+		ivtv_stop(itv);
+	}
+	if (ret)
+		IVTV_DEBUG_I2C("i2c read from %x failed\n", addr);
+	return ret;
+}
+
+/* Kernel i2c transfer implementation.  Takes a number of messages to be read
+   or written.  If a read follows a write, this will occur without an
+   intervening stop condition */
+static int ivtv_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
+{
+	struct ivtv *itv = i2c_get_adapdata(i2c_adap);
+	int retval;
+	int i;
+
+	mutex_lock(&itv->i2c_bus_lock);
+	for (i = retval = 0; retval == 0 && i < num; i++) {
+		if (msgs[i].flags & I2C_M_RD)
+			retval = ivtv_read(itv, msgs[i].addr, msgs[i].buf, msgs[i].len);
+		else {
+			/* if followed by a read, don't stop */
+			int stop = !(i + 1 < num && msgs[i + 1].flags == I2C_M_RD);
+
+			retval = ivtv_write(itv, msgs[i].addr, msgs[i].buf, msgs[i].len, stop);
+		}
+	}
+	mutex_unlock(&itv->i2c_bus_lock);
+	return retval ? retval : num;
+}
+
+/* Kernel i2c capabilities */
+static u32 ivtv_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm ivtv_algo = {
+	.master_xfer   = ivtv_xfer,
+	.functionality = ivtv_functionality,
+};
+
+/* template for our-bit banger */
+static struct i2c_adapter ivtv_i2c_adap_hw_template = {
+	.name = "ivtv i2c driver",
+	.id = I2C_HW_B_CX2341X,
+	.algo = &ivtv_algo,
+	.algo_data = NULL,			/* filled from template */
+	.client_register = attach_inform,
+	.client_unregister = detach_inform,
+	.owner = THIS_MODULE,
+#ifdef I2C_ADAP_CLASS_TV_ANALOG
+	.class = I2C_ADAP_CLASS_TV_ANALOG,
+#endif
+};
+
+static void ivtv_setscl_old(void *data, int state)
+{
+	struct ivtv *itv = (struct ivtv *)data;
+
+	if (state)
+		itv->i2c_state |= 0x01;
+	else
+		itv->i2c_state &= ~0x01;
+
+	/* write them out */
+	/* write bits are inverted */
+	write_reg(~itv->i2c_state, IVTV_REG_I2C_SETSCL_OFFSET);
+}
+
+static void ivtv_setsda_old(void *data, int state)
+{
+	struct ivtv *itv = (struct ivtv *)data;
+
+	if (state)
+		itv->i2c_state |= 0x01;
+	else
+		itv->i2c_state &= ~0x01;
+
+	/* write them out */
+	/* write bits are inverted */
+	write_reg(~itv->i2c_state, IVTV_REG_I2C_SETSDA_OFFSET);
+}
+
+static int ivtv_getscl_old(void *data)
+{
+	struct ivtv *itv = (struct ivtv *)data;
+
+	return read_reg(IVTV_REG_I2C_GETSCL_OFFSET) & 1;
+}
+
+static int ivtv_getsda_old(void *data)
+{
+	struct ivtv *itv = (struct ivtv *)data;
+
+	return read_reg(IVTV_REG_I2C_GETSDA_OFFSET) & 1;
+}
+
+/* template for i2c-bit-algo */
+static struct i2c_adapter ivtv_i2c_adap_template = {
+	.name = "ivtv i2c driver",
+	.id = I2C_HW_B_CX2341X,  	/* algo-bit is OR'd with this */
+	.algo = NULL,                   /* set by i2c-algo-bit */
+	.algo_data = NULL,              /* filled from template */
+	.client_register = attach_inform,
+	.client_unregister = detach_inform,
+	.owner = THIS_MODULE,
+#ifdef I2C_ADAP_CLASS_TV_ANALOG
+	.class = I2C_ADAP_CLASS_TV_ANALOG,
+#endif
+};
+
+static struct i2c_algo_bit_data ivtv_i2c_algo_template = {
+	NULL,                   /* ?? */
+	ivtv_setsda_old,        /* setsda function */
+	ivtv_setscl_old,        /* " */
+	ivtv_getsda_old,        /* " */
+	ivtv_getscl_old,        /* " */
+	10,                     /* udelay */
+	200                     /* timeout */
+};
+
+static struct i2c_client ivtv_i2c_client_template = {
+	.name = "ivtv internal",
+};
+
+int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg)
+{
+	struct i2c_client *client;
+	int retval;
+	int i;
+
+	IVTV_DEBUG_I2C("call_i2c_client addr=%02x\n", addr);
+	for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+		client = itv->i2c_clients[i];
+		if (client == NULL) {
+			continue;
+		}
+		if (client->driver->command == NULL) {
+			continue;
+		}
+		if (addr == client->addr) {
+			retval = client->driver->command(client, cmd, arg);
+			return retval;
+		}
+	}
+	if (cmd != VIDIOC_G_CHIP_IDENT)
+		IVTV_ERR("i2c addr 0x%02x not found for command 0x%x!\n", addr, cmd);
+	return -ENODEV;
+}
+
+/* Find the i2c device based on the driver ID and return
+   its i2c address or -ENODEV if no matching device was found. */
+static int ivtv_i2c_id_addr(struct ivtv *itv, u32 id)
+{
+	struct i2c_client *client;
+	int retval = -ENODEV;
+	int i;
+
+	for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+		client = itv->i2c_clients[i];
+		if (client == NULL)
+			continue;
+		if (id == client->driver->id) {
+			retval = client->addr;
+			break;
+		}
+	}
+	return retval;
+}
+
+/* Find the i2c device name matching the DRIVERID */
+static const char *ivtv_i2c_id_name(u32 id)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
+		if (hw_driverids[i] == id)
+			return hw_drivernames[i];
+	return "unknown device";
+}
+
+/* Find the i2c device name matching the IVTV_HW_ flag */
+static const char *ivtv_i2c_hw_name(u32 hw)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
+		if (1 << i == hw)
+			return hw_drivernames[i];
+	return "unknown device";
+}
+
+/* Find the i2c device matching the IVTV_HW_ flag and return
+   its i2c address or -ENODEV if no matching device was found. */
+int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
+		if (1 << i == hw)
+			return ivtv_i2c_id_addr(itv, hw_driverids[i]);
+	return -ENODEV;
+}
+
+/* Calls i2c device based on IVTV_HW_ flag. If hw == 0, then do nothing.
+   If hw == IVTV_HW_GPIO then call the gpio handler. */
+int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg)
+{
+	int addr;
+
+	if (hw == IVTV_HW_GPIO)
+		return ivtv_gpio(itv, cmd, arg);
+	if (hw == 0)
+		return 0;
+
+	addr = ivtv_i2c_hw_addr(itv, hw);
+	if (addr < 0) {
+		IVTV_ERR("i2c hardware 0x%08x (%s) not found for command 0x%x!\n",
+			       hw, ivtv_i2c_hw_name(hw), cmd);
+		return addr;
+	}
+	return ivtv_call_i2c_client(itv, addr, cmd, arg);
+}
+
+/* Calls i2c device based on I2C driver ID. */
+int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg)
+{
+	int addr;
+
+	addr = ivtv_i2c_id_addr(itv, id);
+	if (addr < 0) {
+		if (cmd != VIDIOC_G_CHIP_IDENT)
+			IVTV_ERR("i2c ID 0x%08x (%s) not found for command 0x%x!\n",
+				id, ivtv_i2c_id_name(id), cmd);
+		return addr;
+	}
+	return ivtv_call_i2c_client(itv, addr, cmd, arg);
+}
+
+int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+	return ivtv_call_i2c_client(itv, IVTV_CX25840_I2C_ADDR, cmd, arg);
+}
+
+int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+	return ivtv_call_i2c_client(itv, IVTV_SAA7115_I2C_ADDR, cmd, arg);
+}
+
+int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+	return ivtv_call_i2c_client(itv, IVTV_SAA7127_I2C_ADDR, cmd, arg);
+}
+
+int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+	return ivtv_call_i2c_client(itv, IVTV_SAA717x_I2C_ADDR, cmd, arg);
+}
+
+int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+	return ivtv_call_i2c_client(itv, IVTV_UPD64031A_I2C_ADDR, cmd, arg);
+}
+
+int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+	return ivtv_call_i2c_client(itv, IVTV_UPD64083_I2C_ADDR, cmd, arg);
+}
+
+/* broadcast cmd for all I2C clients and for the gpio subsystem */
+void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+	if (itv->i2c_adap.algo == NULL) {
+		IVTV_ERR("adapter is not set");
+		return;
+	}
+	i2c_clients_command(&itv->i2c_adap, cmd, arg);
+	if (itv->hw_flags & IVTV_HW_GPIO)
+		ivtv_gpio(itv, cmd, arg);
+}
+
+/* init + register i2c algo-bit adapter */
+int __devinit init_ivtv_i2c(struct ivtv *itv)
+{
+	IVTV_DEBUG_I2C("i2c init\n");
+
+	if (itv->options.newi2c > 0) {
+		memcpy(&itv->i2c_adap, &ivtv_i2c_adap_hw_template,
+		       sizeof(struct i2c_adapter));
+	} else {
+		memcpy(&itv->i2c_adap, &ivtv_i2c_adap_template,
+		       sizeof(struct i2c_adapter));
+		memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template,
+		       sizeof(struct i2c_algo_bit_data));
+		itv->i2c_algo.data = itv;
+		itv->i2c_adap.algo_data = &itv->i2c_algo;
+	}
+
+	sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name), " #%d",
+		itv->num);
+	i2c_set_adapdata(&itv->i2c_adap, itv);
+
+	memcpy(&itv->i2c_client, &ivtv_i2c_client_template,
+	       sizeof(struct i2c_client));
+	itv->i2c_client.adapter = &itv->i2c_adap;
+	itv->i2c_adap.dev.parent = &itv->dev->dev;
+
+	IVTV_DEBUG_I2C("setting scl and sda to 1\n");
+	ivtv_setscl(itv, 1);
+	ivtv_setsda(itv, 1);
+
+	if (itv->options.newi2c > 0)
+		return i2c_add_adapter(&itv->i2c_adap);
+	else
+		return i2c_bit_add_bus(&itv->i2c_adap);
+}
+
+void __devexit exit_ivtv_i2c(struct ivtv *itv)
+{
+	IVTV_DEBUG_I2C("i2c exit\n");
+
+	i2c_del_adapter(&itv->i2c_adap);
+}
diff --git a/drivers/media/video/ivtv/ivtv-i2c.h b/drivers/media/video/ivtv/ivtv-i2c.h
new file mode 100644
index 0000000..5d210ad
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-i2c.h
@@ -0,0 +1,36 @@
+/*
+    I2C functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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
+ */
+
+int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg);
+int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg);
+int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg);
+int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg);
+int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg);
+int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg);
+
+int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw);
+int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg);
+int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg);
+int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg);
+void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg);
+
+/* init + register i2c algo-bit adapter */
+int __devinit init_ivtv_i2c(struct ivtv *itv);
+void __devexit exit_ivtv_i2c(struct ivtv *itv);
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
new file mode 100644
index 0000000..794a6a0
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -0,0 +1,1567 @@
+/*
+    ioctl system call
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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 "ivtv-driver.h"
+#include "ivtv-version.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-i2c.h"
+#include "ivtv-queue.h"
+#include "ivtv-fileops.h"
+#include "ivtv-vbi.h"
+#include "ivtv-audio.h"
+#include "ivtv-video.h"
+#include "ivtv-streams.h"
+#include "ivtv-yuv.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-gpio.h"
+#include "ivtv-controls.h"
+#include "ivtv-cards.h"
+#include <media/saa7127.h>
+#include <media/tveeprom.h>
+#include <media/v4l2-chip-ident.h>
+#include <linux/dvb/audio.h>
+#include <linux/i2c-id.h>
+
+u16 service2vbi(int type)
+{
+	switch (type) {
+		case V4L2_SLICED_TELETEXT_B:
+			return IVTV_SLICED_TYPE_TELETEXT_B;
+		case V4L2_SLICED_CAPTION_525:
+			return IVTV_SLICED_TYPE_CAPTION_525;
+		case V4L2_SLICED_WSS_625:
+			return IVTV_SLICED_TYPE_WSS_625;
+		case V4L2_SLICED_VPS:
+			return IVTV_SLICED_TYPE_VPS;
+		default:
+			return 0;
+	}
+}
+
+static int valid_service_line(int field, int line, int is_pal)
+{
+	return (is_pal && line >= 6 && (line != 23 || field == 0)) ||
+	       (!is_pal && line >= 10 && line < 22);
+}
+
+static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
+{
+	u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);
+	int i;
+
+	set = set & valid_set;
+	if (set == 0 || !valid_service_line(field, line, is_pal)) {
+		return 0;
+	}
+	if (!is_pal) {
+		if (line == 21 && (set & V4L2_SLICED_CAPTION_525))
+			return V4L2_SLICED_CAPTION_525;
+	}
+	else {
+		if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS))
+			return V4L2_SLICED_VPS;
+		if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625))
+			return V4L2_SLICED_WSS_625;
+		if (line == 23)
+			return 0;
+	}
+	for (i = 0; i < 32; i++) {
+		if ((1 << i) & set)
+			return 1 << i;
+	}
+	return 0;
+}
+
+void expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+	u16 set = fmt->service_set;
+	int f, l;
+
+	fmt->service_set = 0;
+	for (f = 0; f < 2; f++) {
+		for (l = 0; l < 24; l++) {
+			fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal);
+		}
+	}
+}
+
+static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+	int f, l;
+	u16 set = 0;
+
+	for (f = 0; f < 2; f++) {
+		for (l = 0; l < 24; l++) {
+			fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
+			set |= fmt->service_lines[f][l];
+		}
+	}
+	return set != 0;
+}
+
+u16 get_service_set(struct v4l2_sliced_vbi_format *fmt)
+{
+	int f, l;
+	u16 set = 0;
+
+	for (f = 0; f < 2; f++) {
+		for (l = 0; l < 24; l++) {
+			set |= fmt->service_lines[f][l];
+		}
+	}
+	return set;
+}
+
+static const struct {
+	v4l2_std_id  std;
+	char        *name;
+} enum_stds[] = {
+	{ V4L2_STD_PAL_BG | V4L2_STD_PAL_H, "PAL-BGH" },
+	{ V4L2_STD_PAL_DK,    "PAL-DK"    },
+	{ V4L2_STD_PAL_I,     "PAL-I"     },
+	{ V4L2_STD_PAL_M,     "PAL-M"     },
+	{ V4L2_STD_PAL_N,     "PAL-N"     },
+	{ V4L2_STD_PAL_Nc,    "PAL-Nc"    },
+	{ V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, "SECAM-BGH" },
+	{ V4L2_STD_SECAM_DK,  "SECAM-DK"  },
+	{ V4L2_STD_SECAM_L,   "SECAM-L"   },
+	{ V4L2_STD_SECAM_LC,  "SECAM-L'"  },
+	{ V4L2_STD_NTSC_M,    "NTSC-M"    },
+	{ V4L2_STD_NTSC_M_JP, "NTSC-J"    },
+	{ V4L2_STD_NTSC_M_KR, "NTSC-K"    },
+};
+
+static const struct v4l2_standard ivtv_std_60hz =
+{
+	.frameperiod = {.numerator = 1001, .denominator = 30000},
+	.framelines = 525,
+};
+
+static const struct v4l2_standard ivtv_std_50hz =
+{
+	.frameperiod = {.numerator = 1, .denominator = 25},
+	.framelines = 625,
+};
+
+void ivtv_set_osd_alpha(struct ivtv *itv)
+{
+	ivtv_vapi(itv, CX2341X_OSD_SET_GLOBAL_ALPHA, 3,
+		itv->osd_global_alpha_state, itv->osd_global_alpha, !itv->osd_local_alpha_state);
+	ivtv_vapi(itv, CX2341X_OSD_SET_CHROMA_KEY, 2, itv->osd_color_key_state, itv->osd_color_key);
+}
+
+int ivtv_set_speed(struct ivtv *itv, int speed)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	struct ivtv_stream *s;
+	int single_step = (speed == 1 || speed == -1);
+	DEFINE_WAIT(wait);
+
+	if (speed == 0) speed = 1000;
+
+	/* No change? */
+	if (speed == itv->speed && !single_step)
+		return 0;
+
+	s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+
+	if (single_step && (speed < 0) == (itv->speed < 0)) {
+		/* Single step video and no need to change direction */
+		ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0);
+		itv->speed = speed;
+		return 0;
+	}
+	if (single_step)
+		/* Need to change direction */
+		speed = speed < 0 ? -1000 : 1000;
+
+	data[0] = (speed > 1000 || speed < -1000) ? 0x80000000 : 0;
+	data[0] |= (speed > 1000 || speed < -1500) ? 0x40000000 : 0;
+	data[1] = (speed < 0);
+	data[2] = speed < 0 ? 3 : 7;
+	data[3] = itv->params.video_b_frames;
+	data[4] = (speed == 1500 || speed == 500) ? itv->speed_mute_audio : 0;
+	data[5] = 0;
+	data[6] = 0;
+
+	if (speed == 1500 || speed == -1500) data[0] |= 1;
+	else if (speed == 2000 || speed == -2000) data[0] |= 2;
+	else if (speed > -1000 && speed < 0) data[0] |= (-1000 / speed);
+	else if (speed < 1000 && speed > 0) data[0] |= (1000 / speed);
+
+	/* If not decoding, just change speed setting */
+	if (atomic_read(&itv->decoding) > 0) {
+		int got_sig = 0;
+
+		/* Stop all DMA and decoding activity */
+		ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1, 0);
+
+		/* Wait for any DMA to finish */
+		prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
+		while (itv->i_flags & IVTV_F_I_DMA) {
+			got_sig = signal_pending(current);
+			if (got_sig)
+				break;
+			got_sig = 0;
+			schedule();
+		}
+		finish_wait(&itv->dma_waitq, &wait);
+		if (got_sig)
+			return -EINTR;
+
+		/* Change Speed safely */
+		ivtv_api(itv, CX2341X_DEC_SET_PLAYBACK_SPEED, 7, data);
+		IVTV_DEBUG_INFO("Setting Speed to 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
+				data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
+	}
+	if (single_step) {
+		speed = (speed < 0) ? -1 : 1;
+		ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0);
+	}
+	itv->speed = speed;
+	return 0;
+}
+
+static int ivtv_validate_speed(int cur_speed, int new_speed)
+{
+	int fact = new_speed < 0 ? -1 : 1;
+	int s;
+
+	if (new_speed < 0) new_speed = -new_speed;
+	if (cur_speed < 0) cur_speed = -cur_speed;
+
+	if (cur_speed <= new_speed) {
+		if (new_speed > 1500) return fact * 2000;
+		if (new_speed > 1000) return fact * 1500;
+	}
+	else {
+		if (new_speed >= 2000) return fact * 2000;
+		if (new_speed >= 1500) return fact * 1500;
+		if (new_speed >= 1000) return fact * 1000;
+	}
+	if (new_speed == 0) return 1000;
+	if (new_speed == 1 || new_speed == 1000) return fact * new_speed;
+
+	s = new_speed;
+	new_speed = 1000 / new_speed;
+	if (1000 / cur_speed == new_speed)
+		new_speed += (cur_speed < s) ? -1 : 1;
+	if (new_speed > 60) return 1000 / (fact * 60);
+	return 1000 / (fact * new_speed);
+}
+
+static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id,
+		struct video_command *vc, int try)
+{
+	struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+
+	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+		return -EINVAL;
+
+	switch (vc->cmd) {
+	case VIDEO_CMD_PLAY: {
+		vc->flags = 0;
+		vc->play.speed = ivtv_validate_speed(itv->speed, vc->play.speed);
+		if (vc->play.speed < 0)
+			vc->play.format = VIDEO_PLAY_FMT_GOP;
+		if (try) break;
+
+		if (ivtv_set_output_mode(itv, OUT_MPG) != OUT_MPG)
+			return -EBUSY;
+		return ivtv_start_decoding(id, vc->play.speed);
+	}
+
+	case VIDEO_CMD_STOP:
+		vc->flags &= VIDEO_CMD_STOP_IMMEDIATELY|VIDEO_CMD_STOP_TO_BLACK;
+		if (vc->flags & VIDEO_CMD_STOP_IMMEDIATELY)
+			vc->stop.pts = 0;
+		if (try) break;
+		if (atomic_read(&itv->decoding) == 0)
+			return 0;
+		if (itv->output_mode != OUT_MPG)
+			return -EBUSY;
+
+		itv->output_mode = OUT_NONE;
+		return ivtv_stop_v4l2_decode_stream(s, vc->flags, vc->stop.pts);
+
+	case VIDEO_CMD_FREEZE:
+		vc->flags &= VIDEO_CMD_FREEZE_TO_BLACK;
+		if (try) break;
+		if (itv->output_mode != OUT_MPG)
+			return -EBUSY;
+		if (atomic_read(&itv->decoding) > 0) {
+			ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1,
+				(vc->flags & VIDEO_CMD_FREEZE_TO_BLACK) ? 1 : 0);
+		}
+		break;
+
+	case VIDEO_CMD_CONTINUE:
+		vc->flags = 0;
+		if (try) break;
+		if (itv->output_mode != OUT_MPG)
+			return -EBUSY;
+		if (atomic_read(&itv->decoding) > 0) {
+			ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 0);
+		}
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+	struct v4l2_register *regs = arg;
+	unsigned long flags;
+	volatile u8 __iomem *reg_start;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (regs->reg >= IVTV_REG_OFFSET && regs->reg < IVTV_REG_OFFSET + IVTV_REG_SIZE)
+		reg_start = itv->reg_mem - IVTV_REG_OFFSET;
+	else if (itv->has_cx23415 && regs->reg >= IVTV_DECODER_OFFSET &&
+			regs->reg < IVTV_DECODER_OFFSET + IVTV_DECODER_SIZE)
+		reg_start = itv->dec_mem - IVTV_DECODER_OFFSET;
+	else if (regs->reg >= 0 && regs->reg < IVTV_ENCODER_SIZE)
+		reg_start = itv->enc_mem;
+	else
+		return -EINVAL;
+
+	spin_lock_irqsave(&ivtv_cards_lock, flags);
+	if (cmd == VIDIOC_DBG_G_REGISTER) {
+		regs->val = readl(regs->reg + reg_start);
+	} else {
+		writel(regs->val, regs->reg + reg_start);
+	}
+	spin_unlock_irqrestore(&ivtv_cards_lock, flags);
+	return 0;
+}
+
+static int ivtv_get_fmt(struct ivtv *itv, int streamtype, struct v4l2_format *fmt)
+{
+	switch (fmt->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+		if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+			return -EINVAL;
+		fmt->fmt.pix.left = itv->main_rect.left;
+		fmt->fmt.pix.top = itv->main_rect.top;
+		fmt->fmt.pix.width = itv->main_rect.width;
+		fmt->fmt.pix.height = itv->main_rect.height;
+		fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+		fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
+		if (itv->output_mode == OUT_UDMA_YUV) {
+			switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) {
+			case IVTV_YUV_MODE_INTERLACED:
+				fmt->fmt.pix.field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) ?
+					V4L2_FIELD_INTERLACED_BT : V4L2_FIELD_INTERLACED_TB;
+				break;
+			case IVTV_YUV_MODE_PROGRESSIVE:
+				fmt->fmt.pix.field = V4L2_FIELD_NONE;
+				break;
+			default:
+				fmt->fmt.pix.field = V4L2_FIELD_ANY;
+				break;
+			}
+			fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12;
+			/* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
+			fmt->fmt.pix.sizeimage =
+				fmt->fmt.pix.height * fmt->fmt.pix.width +
+				fmt->fmt.pix.height * (fmt->fmt.pix.width / 2);
+		}
+		else if (itv->output_mode == OUT_YUV ||
+				streamtype == IVTV_ENC_STREAM_TYPE_YUV ||
+				streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+			fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12;
+			/* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
+			fmt->fmt.pix.sizeimage =
+				fmt->fmt.pix.height * fmt->fmt.pix.width +
+				fmt->fmt.pix.height * (fmt->fmt.pix.width / 2);
+		} else {
+			fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+			fmt->fmt.pix.sizeimage = 128 * 1024;
+		}
+		break;
+
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		fmt->fmt.pix.left = 0;
+		fmt->fmt.pix.top = 0;
+		fmt->fmt.pix.width = itv->params.width;
+		fmt->fmt.pix.height = itv->params.height;
+		fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+		fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
+		if (streamtype == IVTV_ENC_STREAM_TYPE_YUV ||
+				streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+			fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12;
+			/* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
+			fmt->fmt.pix.sizeimage =
+				fmt->fmt.pix.height * fmt->fmt.pix.width +
+				fmt->fmt.pix.height * (fmt->fmt.pix.width / 2);
+		} else {
+			fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+			fmt->fmt.pix.sizeimage = 128 * 1024;
+		}
+		break;
+
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+		if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+			return -EINVAL;
+		fmt->fmt.win.chromakey = itv->osd_color_key;
+		fmt->fmt.win.global_alpha = itv->osd_global_alpha;
+		break;
+
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		fmt->fmt.vbi.sampling_rate = 27000000;
+		fmt->fmt.vbi.offset = 248;
+		fmt->fmt.vbi.samples_per_line = itv->vbi.raw_decoder_line_size - 4;
+		fmt->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+		fmt->fmt.vbi.start[0] = itv->vbi.start[0];
+		fmt->fmt.vbi.start[1] = itv->vbi.start[1];
+		fmt->fmt.vbi.count[0] = fmt->fmt.vbi.count[1] = itv->vbi.count;
+		break;
+
+	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+	{
+		struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+		if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))
+			return -EINVAL;
+		vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+		memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved));
+		memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
+		if (itv->is_60hz) {
+			vbifmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+			vbifmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
+		} else {
+			vbifmt->service_lines[0][23] = V4L2_SLICED_WSS_625;
+			vbifmt->service_lines[0][16] = V4L2_SLICED_VPS;
+		}
+		vbifmt->service_set = get_service_set(vbifmt);
+		break;
+	}
+
+	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+	{
+		struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+		vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+		memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved));
+		memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
+
+		if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) {
+			vbifmt->service_set = itv->is_50hz ? V4L2_SLICED_VBI_625 :
+						 V4L2_SLICED_VBI_525;
+			expand_service_set(vbifmt, itv->is_50hz);
+			break;
+		}
+
+		itv->video_dec_func(itv, VIDIOC_G_FMT, fmt);
+		vbifmt->service_set = get_service_set(vbifmt);
+		break;
+	}
+	case V4L2_BUF_TYPE_VBI_OUTPUT:
+	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int ivtv_try_or_set_fmt(struct ivtv *itv, int streamtype,
+		struct v4l2_format *fmt, int set_fmt)
+{
+	struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+	u16 set;
+
+	if (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+		struct v4l2_rect r;
+		int field;
+
+		if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+			return -EINVAL;
+		field = fmt->fmt.pix.field;
+		r.top = fmt->fmt.pix.top;
+		r.left = fmt->fmt.pix.left;
+		r.width = fmt->fmt.pix.width;
+		r.height = fmt->fmt.pix.height;
+		ivtv_get_fmt(itv, streamtype, fmt);
+		if (itv->output_mode != OUT_UDMA_YUV) {
+			/* TODO: would setting the rect also be valid for this mode? */
+			fmt->fmt.pix.top = r.top;
+			fmt->fmt.pix.left = r.left;
+			fmt->fmt.pix.width = r.width;
+			fmt->fmt.pix.height = r.height;
+		}
+		if (itv->output_mode == OUT_UDMA_YUV) {
+			/* TODO: add checks for validity */
+			fmt->fmt.pix.field = field;
+		}
+		if (set_fmt) {
+			if (itv->output_mode == OUT_UDMA_YUV) {
+				switch (field) {
+				case V4L2_FIELD_NONE:
+					itv->yuv_info.lace_mode = IVTV_YUV_MODE_PROGRESSIVE;
+					break;
+				case V4L2_FIELD_ANY:
+					itv->yuv_info.lace_mode = IVTV_YUV_MODE_AUTO;
+					break;
+				case V4L2_FIELD_INTERLACED_BT:
+					itv->yuv_info.lace_mode =
+						IVTV_YUV_MODE_INTERLACED|IVTV_YUV_SYNC_ODD;
+					break;
+				case V4L2_FIELD_INTERLACED_TB:
+				default:
+					itv->yuv_info.lace_mode = IVTV_YUV_MODE_INTERLACED;
+					break;
+				}
+				itv->yuv_info.lace_sync_field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) == IVTV_YUV_SYNC_EVEN ? 0 : 1;
+
+				/* Force update of yuv registers */
+				itv->yuv_info.yuv_forced_update = 1;
+				return 0;
+			}
+			if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
+				 r.width, r.height, r.left, r.top))
+				itv->main_rect = r;
+			else
+				return -EINVAL;
+		}
+		return 0;
+	}
+
+	if (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY) {
+		if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+			return -EINVAL;
+		if (set_fmt) {
+			itv->osd_color_key = fmt->fmt.win.chromakey;
+			itv->osd_global_alpha = fmt->fmt.win.global_alpha;
+			ivtv_set_osd_alpha(itv);
+		}
+		return 0;
+	}
+
+	/* set window size */
+	if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		int w = fmt->fmt.pix.width;
+		int h = fmt->fmt.pix.height;
+
+		if (w > 720) w = 720;
+		else if (w < 1) w = 1;
+		if (h > (itv->is_50hz ? 576 : 480)) h = (itv->is_50hz ? 576 : 480);
+		else if (h < 2) h = 2;
+		ivtv_get_fmt(itv, streamtype, fmt);
+		fmt->fmt.pix.width = w;
+		fmt->fmt.pix.height = h;
+
+		if (!set_fmt || (itv->params.width == w && itv->params.height == h))
+			return 0;
+		if (atomic_read(&itv->capturing) > 0)
+			return -EBUSY;
+
+		itv->params.width = w;
+		itv->params.height = h;
+		if (w != 720 || h != (itv->is_50hz ? 576 : 480))
+			itv->params.video_temporal_filter = 0;
+		else
+			itv->params.video_temporal_filter = 8;
+		itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
+		return ivtv_get_fmt(itv, streamtype, fmt);
+	}
+
+	/* set raw VBI format */
+	if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+		if (set_fmt && streamtype == IVTV_ENC_STREAM_TYPE_VBI &&
+		    itv->vbi.sliced_in->service_set &&
+		    atomic_read(&itv->capturing) > 0) {
+			return -EBUSY;
+		}
+		if (set_fmt) {
+			itv->vbi.sliced_in->service_set = 0;
+			itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in);
+		}
+		return ivtv_get_fmt(itv, streamtype, fmt);
+	}
+
+	/* set sliced VBI output
+	   In principle the user could request that only certain
+	   VBI types are output and that the others are ignored.
+	   I.e., suppress CC in the even fields or only output
+	   WSS and no VPS. Currently though there is no choice. */
+	if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT)
+		return ivtv_get_fmt(itv, streamtype, fmt);
+
+	/* any else but sliced VBI capture is an error */
+	if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+		return -EINVAL;
+
+	if (streamtype == IVTV_DEC_STREAM_TYPE_VBI)
+		return ivtv_get_fmt(itv, streamtype, fmt);
+
+	/* set sliced VBI capture format */
+	vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+	memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved));
+
+	if (vbifmt->service_set)
+		expand_service_set(vbifmt, itv->is_50hz);
+	set = check_service_set(vbifmt, itv->is_50hz);
+	vbifmt->service_set = get_service_set(vbifmt);
+
+	if (!set_fmt)
+		return 0;
+	if (set == 0)
+		return -EINVAL;
+	if (atomic_read(&itv->capturing) > 0 && itv->vbi.sliced_in->service_set == 0) {
+		return -EBUSY;
+	}
+	itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
+	memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in));
+	return 0;
+}
+
+static int ivtv_debug_ioctls(struct file *filp, unsigned int cmd, void *arg)
+{
+	struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data;
+	struct ivtv *itv = id->itv;
+	struct v4l2_register *reg = arg;
+
+	switch (cmd) {
+	/* ioctls to allow direct access to the encoder registers for testing */
+	case VIDIOC_DBG_G_REGISTER:
+		if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
+			return ivtv_itvc(itv, cmd, arg);
+		if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+			return ivtv_i2c_id(itv, reg->match_chip, cmd, arg);
+		return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg);
+
+	case VIDIOC_DBG_S_REGISTER:
+		if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
+			return ivtv_itvc(itv, cmd, arg);
+		if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+			return ivtv_i2c_id(itv, reg->match_chip, cmd, arg);
+		return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg);
+
+	case VIDIOC_G_CHIP_IDENT: {
+		struct v4l2_chip_ident *chip = arg;
+
+		chip->ident = V4L2_IDENT_NONE;
+		chip->revision = 0;
+		if (reg->match_type == V4L2_CHIP_MATCH_HOST) {
+			if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) {
+				struct v4l2_chip_ident *chip = arg;
+
+				chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416;
+			}
+			return 0;
+		}
+		if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+			return ivtv_i2c_id(itv, reg->match_chip, cmd, arg);
+		if (reg->match_type == V4L2_CHIP_MATCH_I2C_ADDR)
+			return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg);
+		return -EINVAL;
+	}
+
+	case VIDIOC_INT_S_AUDIO_ROUTING: {
+		struct v4l2_routing *route = arg;
+
+		ivtv_audio_set_route(itv, route);
+		break;
+	}
+
+	case VIDIOC_INT_RESET:
+		ivtv_reset_ir_gpio(itv);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void *arg)
+{
+	struct ivtv_open_id *id = NULL;
+
+	if (filp) id = (struct ivtv_open_id *)filp->private_data;
+
+	switch (cmd) {
+	case VIDIOC_G_PRIORITY:
+	{
+		enum v4l2_priority *p = arg;
+
+		*p = v4l2_prio_max(&itv->prio);
+		break;
+	}
+
+	case VIDIOC_S_PRIORITY:
+	{
+		enum v4l2_priority *prio = arg;
+
+		return v4l2_prio_change(&itv->prio, &id->prio, *prio);
+	}
+
+	case VIDIOC_QUERYCAP:{
+		struct v4l2_capability *vcap = arg;
+
+		memset(vcap, 0, sizeof(*vcap));
+		strcpy(vcap->driver, IVTV_DRIVER_NAME);     /* driver name */
+		strcpy(vcap->card, itv->card_name); 	    /* card type */
+		strcpy(vcap->bus_info, pci_name(itv->dev)); /* bus info... */
+		vcap->version = IVTV_DRIVER_VERSION; 	    /* version */
+		vcap->capabilities = itv->v4l2_cap; 	    /* capabilities */
+
+		/* reserved.. must set to 0! */
+		vcap->reserved[0] = vcap->reserved[1] =
+			vcap->reserved[2] = vcap->reserved[3] = 0;
+		break;
+	}
+
+	case VIDIOC_ENUMAUDIO:{
+		struct v4l2_audio *vin = arg;
+
+		return ivtv_get_audio_input(itv, vin->index, vin);
+	}
+
+	case VIDIOC_G_AUDIO:{
+		struct v4l2_audio *vin = arg;
+
+		vin->index = itv->audio_input;
+		return ivtv_get_audio_input(itv, vin->index, vin);
+	}
+
+	case VIDIOC_S_AUDIO:{
+		struct v4l2_audio *vout = arg;
+
+		if (vout->index >= itv->nof_audio_inputs)
+			return -EINVAL;
+		itv->audio_input = vout->index;
+		ivtv_audio_set_io(itv);
+		break;
+	}
+
+	case VIDIOC_ENUMAUDOUT:{
+		struct v4l2_audioout *vin = arg;
+
+		/* set it to defaults from our table */
+		return ivtv_get_audio_output(itv, vin->index, vin);
+	}
+
+	case VIDIOC_G_AUDOUT:{
+		struct v4l2_audioout *vin = arg;
+
+		vin->index = 0;
+		return ivtv_get_audio_output(itv, vin->index, vin);
+	}
+
+	case VIDIOC_S_AUDOUT:{
+		struct v4l2_audioout *vout = arg;
+
+		return ivtv_get_audio_output(itv, vout->index, vout);
+	}
+
+	case VIDIOC_ENUMINPUT:{
+		struct v4l2_input *vin = arg;
+
+		/* set it to defaults from our table */
+		return ivtv_get_input(itv, vin->index, vin);
+	}
+
+	case VIDIOC_ENUMOUTPUT:{
+		struct v4l2_output *vout = arg;
+
+		return ivtv_get_output(itv, vout->index, vout);
+	}
+
+	case VIDIOC_TRY_FMT:
+	case VIDIOC_S_FMT: {
+		struct v4l2_format *fmt = arg;
+
+		return ivtv_try_or_set_fmt(itv, id->type, fmt, cmd == VIDIOC_S_FMT);
+	}
+
+	case VIDIOC_G_FMT: {
+		struct v4l2_format *fmt = arg;
+		int type = fmt->type;
+
+		memset(fmt, 0, sizeof(*fmt));
+		fmt->type = type;
+		return ivtv_get_fmt(itv, id->type, fmt);
+	}
+
+	case VIDIOC_S_CROP: {
+		struct v4l2_crop *crop = arg;
+
+		if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		return itv->video_dec_func(itv, VIDIOC_S_CROP, arg);
+	}
+
+	case VIDIOC_G_CROP: {
+		struct v4l2_crop *crop = arg;
+
+		if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		return itv->video_dec_func(itv, VIDIOC_G_CROP, arg);
+	}
+
+	case VIDIOC_ENUM_FMT: {
+		static struct v4l2_fmtdesc formats[] = {
+			{ 0, 0, 0,
+			  "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12,
+			  { 0, 0, 0, 0 }
+			},
+			{ 1, 0, V4L2_FMT_FLAG_COMPRESSED,
+			  "MPEG", V4L2_PIX_FMT_MPEG,
+			  { 0, 0, 0, 0 }
+			}
+		};
+		struct v4l2_fmtdesc *fmt = arg;
+		enum v4l2_buf_type type = fmt->type;
+
+		switch (type) {
+		case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+			break;
+		case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+			if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+				return -EINVAL;
+			break;
+		default:
+			return -EINVAL;
+		}
+		if (fmt->index > 1)
+			return -EINVAL;
+		*fmt = formats[fmt->index];
+		fmt->type = type;
+		return 0;
+	}
+
+	case VIDIOC_G_INPUT:{
+		*(int *)arg = itv->active_input;
+		break;
+	}
+
+	case VIDIOC_S_INPUT:{
+		int inp = *(int *)arg;
+
+		if (inp < 0 || inp >= itv->nof_inputs)
+			return -EINVAL;
+
+		if (inp == itv->active_input) {
+			IVTV_DEBUG_INFO("Input unchanged\n");
+			break;
+		}
+		IVTV_DEBUG_INFO("Changing input from %d to %d\n",
+				itv->active_input, inp);
+
+		itv->active_input = inp;
+		/* Set the audio input to whatever is appropriate for the
+		   input type. */
+		itv->audio_input = itv->card->video_inputs[inp].audio_index;
+
+		/* prevent others from messing with the streams until
+		   we're finished changing inputs. */
+		ivtv_mute(itv);
+		ivtv_video_set_io(itv);
+		ivtv_audio_set_io(itv);
+		ivtv_unmute(itv);
+		break;
+	}
+
+	case VIDIOC_G_OUTPUT:{
+		if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+			return -EINVAL;
+		*(int *)arg = itv->active_output;
+		break;
+	}
+
+	case VIDIOC_S_OUTPUT:{
+		int outp = *(int *)arg;
+		struct v4l2_routing route;
+
+		if (outp >= itv->card->nof_outputs)
+			return -EINVAL;
+
+		if (outp == itv->active_output) {
+			IVTV_DEBUG_INFO("Output unchanged\n");
+			break;
+		}
+		IVTV_DEBUG_INFO("Changing output from %d to %d\n",
+			   itv->active_output, outp);
+
+		itv->active_output = outp;
+		route.input = SAA7127_INPUT_TYPE_NORMAL;
+		route.output = itv->card->video_outputs[outp].video_output;
+		ivtv_saa7127(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+		break;
+	}
+
+	case VIDIOC_G_FREQUENCY:{
+		struct v4l2_frequency *vf = arg;
+
+		if (vf->tuner != 0)
+			return -EINVAL;
+		ivtv_call_i2c_clients(itv, cmd, arg);
+		break;
+	}
+
+	case VIDIOC_S_FREQUENCY:{
+		struct v4l2_frequency vf = *(struct v4l2_frequency *)arg;
+
+		if (vf.tuner != 0)
+			return -EINVAL;
+
+		ivtv_mute(itv);
+		IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf.frequency);
+		ivtv_call_i2c_clients(itv, cmd, &vf);
+		ivtv_unmute(itv);
+		break;
+	}
+
+	case VIDIOC_ENUMSTD:{
+		struct v4l2_standard *vs = arg;
+		int idx = vs->index;
+
+		if (idx < 0 || idx >= ARRAY_SIZE(enum_stds))
+			return -EINVAL;
+
+		*vs = (enum_stds[idx].std & V4L2_STD_525_60) ?
+				ivtv_std_60hz : ivtv_std_50hz;
+		vs->index = idx;
+		vs->id = enum_stds[idx].std;
+		strcpy(vs->name, enum_stds[idx].name);
+		break;
+	}
+
+	case VIDIOC_G_STD:{
+		*(v4l2_std_id *) arg = itv->std;
+		break;
+	}
+
+	case VIDIOC_S_STD: {
+		v4l2_std_id std = *(v4l2_std_id *) arg;
+
+		if ((std & V4L2_STD_ALL) == 0)
+			return -EINVAL;
+
+		if (std == itv->std)
+			break;
+
+		if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ||
+		    atomic_read(&itv->capturing) > 0 ||
+		    atomic_read(&itv->decoding) > 0) {
+			/* Switching standard would turn off the radio or mess
+			   with already running streams, prevent that by
+			   returning EBUSY. */
+			return -EBUSY;
+		}
+
+		itv->std = std;
+		itv->is_60hz = (std & V4L2_STD_525_60) ? 1 : 0;
+		itv->params.is_50hz = itv->is_50hz = !itv->is_60hz;
+		itv->params.width = 720;
+		itv->params.height = itv->is_50hz ? 576 : 480;
+		itv->vbi.count = itv->is_50hz ? 18 : 12;
+		itv->vbi.start[0] = itv->is_50hz ? 6 : 10;
+		itv->vbi.start[1] = itv->is_50hz ? 318 : 273;
+		if (itv->hw_flags & IVTV_HW_CX25840) {
+			itv->vbi.sliced_decoder_line_size = itv->is_60hz ? 272 : 284;
+		}
+		IVTV_DEBUG_INFO("Switching standard to %llx.\n", itv->std);
+
+		/* Tuner */
+		ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std);
+
+		if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+			/* set display standard */
+			itv->std_out = std;
+			itv->is_out_60hz = itv->is_60hz;
+			itv->is_out_50hz = itv->is_50hz;
+			ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std_out);
+			ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz);
+			itv->main_rect.left = itv->main_rect.top = 0;
+			itv->main_rect.width = 720;
+			itv->main_rect.height = itv->params.height;
+			ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
+				720, itv->main_rect.height, 0, 0);
+		}
+		break;
+	}
+
+	case VIDIOC_S_TUNER: {	/* Setting tuner can only set audio mode */
+		struct v4l2_tuner *vt = arg;
+
+		if (vt->index != 0)
+			return -EINVAL;
+
+		ivtv_call_i2c_clients(itv, VIDIOC_S_TUNER, vt);
+		break;
+	}
+
+	case VIDIOC_G_TUNER: {
+		struct v4l2_tuner *vt = arg;
+
+		if (vt->index != 0)
+			return -EINVAL;
+
+		memset(vt, 0, sizeof(*vt));
+		ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, vt);
+
+		if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
+			strcpy(vt->name, "ivtv Radio Tuner");
+			vt->type = V4L2_TUNER_RADIO;
+		} else {
+			strcpy(vt->name, "ivtv TV Tuner");
+			vt->type = V4L2_TUNER_ANALOG_TV;
+		}
+		break;
+	}
+
+	case VIDIOC_G_SLICED_VBI_CAP: {
+		struct v4l2_sliced_vbi_cap *cap = arg;
+		int set = itv->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
+		int f, l;
+		enum v4l2_buf_type type = cap->type;
+
+		memset(cap, 0, sizeof(*cap));
+		cap->type = type;
+		if (type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
+			for (f = 0; f < 2; f++) {
+				for (l = 0; l < 24; l++) {
+					if (valid_service_line(f, l, itv->is_50hz)) {
+						cap->service_lines[f][l] = set;
+					}
+				}
+			}
+			return 0;
+		}
+		if (type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
+			if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))
+				return -EINVAL;
+			if (itv->is_60hz) {
+				cap->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+				cap->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
+			} else {
+				cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
+				cap->service_lines[0][16] = V4L2_SLICED_VPS;
+			}
+			return 0;
+		}
+		return -EINVAL;
+	}
+
+	case VIDIOC_G_ENC_INDEX: {
+		struct v4l2_enc_idx *idx = arg;
+		int i;
+
+		idx->entries = (itv->pgm_info_write_idx + IVTV_MAX_PGM_INDEX - itv->pgm_info_read_idx) %
+					IVTV_MAX_PGM_INDEX;
+		if (idx->entries > V4L2_ENC_IDX_ENTRIES)
+			idx->entries = V4L2_ENC_IDX_ENTRIES;
+		for (i = 0; i < idx->entries; i++) {
+			idx->entry[i] = itv->pgm_info[(itv->pgm_info_read_idx + i) % IVTV_MAX_PGM_INDEX];
+		}
+		itv->pgm_info_read_idx = (itv->pgm_info_read_idx + idx->entries) % IVTV_MAX_PGM_INDEX;
+		break;
+	}
+
+	case VIDIOC_ENCODER_CMD:
+	case VIDIOC_TRY_ENCODER_CMD: {
+		struct v4l2_encoder_cmd *enc = arg;
+		int try = cmd == VIDIOC_TRY_ENCODER_CMD;
+
+		memset(&enc->raw, 0, sizeof(enc->raw));
+		switch (enc->cmd) {
+		case V4L2_ENC_CMD_START:
+			enc->flags = 0;
+			if (try)
+				return 0;
+			return ivtv_start_capture(id);
+
+		case V4L2_ENC_CMD_STOP:
+			enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
+			if (try)
+				return 0;
+			ivtv_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
+			return 0;
+
+		case V4L2_ENC_CMD_PAUSE:
+			enc->flags = 0;
+			if (try)
+				return 0;
+			if (!atomic_read(&itv->capturing))
+				return -EPERM;
+			if (test_and_set_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
+				return 0;
+			ivtv_mute(itv);
+			ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 0);
+			break;
+
+		case V4L2_ENC_CMD_RESUME:
+			enc->flags = 0;
+			if (try)
+				return 0;
+			if (!atomic_read(&itv->capturing))
+				return -EPERM;
+			if (!test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
+				return 0;
+			ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1);
+			ivtv_unmute(itv);
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	}
+
+	case VIDIOC_G_FBUF: {
+		struct v4l2_framebuffer *fb = arg;
+
+		memset(fb, 0, sizeof(*fb));
+		if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+			break;
+		fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | V4L2_FBUF_CAP_CHROMAKEY |
+			V4L2_FBUF_CAP_LOCAL_ALPHA | V4L2_FBUF_CAP_GLOBAL_ALPHA;
+		fb->fmt.pixelformat = itv->osd_pixelformat;
+		fb->fmt.width = itv->osd_rect.width;
+		fb->fmt.height = itv->osd_rect.height;
+		fb->fmt.left = itv->osd_rect.left;
+		fb->fmt.top = itv->osd_rect.top;
+		fb->base = (void *)itv->osd_video_pbase;
+		if (itv->osd_global_alpha_state)
+			fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA;
+		if (itv->osd_local_alpha_state)
+			fb->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
+		if (itv->osd_color_key_state)
+			fb->flags |= V4L2_FBUF_FLAG_CHROMAKEY;
+		break;
+	}
+
+	case VIDIOC_S_FBUF: {
+		struct v4l2_framebuffer *fb = arg;
+
+		if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+			break;
+		itv->osd_global_alpha_state = (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0;
+		itv->osd_local_alpha_state = (fb->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) != 0;
+		itv->osd_color_key_state = (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0;
+		break;
+	}
+
+	case VIDIOC_LOG_STATUS:
+	{
+		int has_output = itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT;
+		struct v4l2_input vidin;
+		struct v4l2_audio audin;
+		int i;
+
+		IVTV_INFO("=================  START STATUS CARD #%d  =================\n", itv->num);
+		if (itv->hw_flags & IVTV_HW_TVEEPROM) {
+			struct tveeprom tv;
+
+			ivtv_read_eeprom(itv, &tv);
+		}
+		ivtv_call_i2c_clients(itv, VIDIOC_LOG_STATUS, NULL);
+		ivtv_get_input(itv, itv->active_input, &vidin);
+		ivtv_get_audio_input(itv, itv->audio_input, &audin);
+		IVTV_INFO("Video Input: %s\n", vidin.name);
+		IVTV_INFO("Audio Input: %s\n", audin.name);
+		if (has_output) {
+			struct v4l2_output vidout;
+			struct v4l2_audioout audout;
+			int mode = itv->output_mode;
+			static const char * const output_modes[] = {
+				"None",
+				"MPEG Streaming",
+				"YUV Streaming",
+				"YUV Frames",
+				"Passthrough",
+			};
+
+			ivtv_get_output(itv, itv->active_output, &vidout);
+			ivtv_get_audio_output(itv, 0, &audout);
+			IVTV_INFO("Video Output: %s\n", vidout.name);
+			IVTV_INFO("Audio Output: %s\n", audout.name);
+			if (mode < 0 || mode > OUT_PASSTHROUGH)
+				mode = OUT_NONE;
+			IVTV_INFO("Output Mode: %s\n", output_modes[mode]);
+		}
+		IVTV_INFO("Tuner: %s\n",
+			test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV");
+		cx2341x_log_status(&itv->params, itv->name);
+		IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags);
+		for (i = 0; i < IVTV_MAX_STREAMS; i++) {
+			struct ivtv_stream *s = &itv->streams[i];
+
+			if (s->v4l2dev == NULL || s->buffers == 0)
+				continue;
+			IVTV_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags,
+					(s->buffers - s->q_free.buffers) * 100 / s->buffers,
+					(s->buffers * s->buf_size) / 1024, s->buffers);
+		}
+		IVTV_INFO("Read MPEG/VBI: %lld/%lld bytes\n", itv->mpg_data_received, itv->vbi_data_inserted);
+		IVTV_INFO("==================  END STATUS CARD #%d  ==================\n", itv->num);
+		break;
+	}
+
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg)
+{
+	struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data;
+	struct ivtv *itv = id->itv;
+	int nonblocking = filp->f_flags & O_NONBLOCK;
+	struct ivtv_stream *s = &itv->streams[id->type];
+
+	switch (cmd) {
+	case IVTV_IOC_DMA_FRAME: {
+		struct ivtv_dma_frame *args = arg;
+
+		IVTV_DEBUG_IOCTL("IVTV_IOC_DMA_FRAME\n");
+		if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+			return -EINVAL;
+		if (args->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+			return -EINVAL;
+		if (itv->output_mode == OUT_UDMA_YUV && args->y_source == NULL)
+			return 0;
+		if (ivtv_claim_stream(id, id->type)) {
+			return -EBUSY;
+		}
+		if (ivtv_set_output_mode(itv, OUT_UDMA_YUV) != OUT_UDMA_YUV) {
+			ivtv_release_stream(s);
+			return -EBUSY;
+		}
+		if (args->y_source == NULL)
+			return 0;
+		return ivtv_yuv_prep_frame(itv, args);
+	}
+
+	case VIDEO_GET_PTS: {
+		u32 data[CX2341X_MBOX_MAX_DATA];
+		u64 *pts = arg;
+
+		IVTV_DEBUG_IOCTL("VIDEO_GET_PTS\n");
+		if (s->type < IVTV_DEC_STREAM_TYPE_MPG) {
+			*pts = s->dma_pts;
+			break;
+		}
+		if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+			return -EINVAL;
+
+		if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) {
+			*pts = (u64) ((u64)itv->last_dec_timing[2] << 32) |
+					(u64)itv->last_dec_timing[1];
+			break;
+		}
+		*pts = 0;
+		if (atomic_read(&itv->decoding)) {
+			if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) {
+				IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n");
+				return -EIO;
+			}
+			memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing));
+			set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
+			*pts = (u64) ((u64) data[2] << 32) | (u64) data[1];
+			/*timing->scr = (u64) (((u64) data[4] << 32) | (u64) (data[3]));*/
+		}
+		break;
+	}
+
+	case VIDEO_GET_FRAME_COUNT: {
+		u32 data[CX2341X_MBOX_MAX_DATA];
+		u64 *frame = arg;
+
+		IVTV_DEBUG_IOCTL("VIDEO_GET_FRAME_COUNT\n");
+		if (s->type < IVTV_DEC_STREAM_TYPE_MPG) {
+			*frame = 0;
+			break;
+		}
+		if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+			return -EINVAL;
+
+		if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) {
+			*frame = itv->last_dec_timing[0];
+			break;
+		}
+		*frame = 0;
+		if (atomic_read(&itv->decoding)) {
+			if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) {
+				IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n");
+				return -EIO;
+			}
+			memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing));
+			set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
+			*frame = data[0];
+		}
+		break;
+	}
+
+	case VIDEO_PLAY: {
+		struct video_command vc;
+
+		IVTV_DEBUG_IOCTL("VIDEO_PLAY\n");
+		memset(&vc, 0, sizeof(vc));
+		vc.cmd = VIDEO_CMD_PLAY;
+		return ivtv_video_command(itv, id, &vc, 0);
+	}
+
+	case VIDEO_STOP: {
+		struct video_command vc;
+
+		IVTV_DEBUG_IOCTL("VIDEO_STOP\n");
+		memset(&vc, 0, sizeof(vc));
+		vc.cmd = VIDEO_CMD_STOP;
+		vc.flags = VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY;
+		return ivtv_video_command(itv, id, &vc, 0);
+	}
+
+	case VIDEO_FREEZE: {
+		struct video_command vc;
+
+		IVTV_DEBUG_IOCTL("VIDEO_FREEZE\n");
+		memset(&vc, 0, sizeof(vc));
+		vc.cmd = VIDEO_CMD_FREEZE;
+		return ivtv_video_command(itv, id, &vc, 0);
+	}
+
+	case VIDEO_CONTINUE: {
+		struct video_command vc;
+
+		IVTV_DEBUG_IOCTL("VIDEO_CONTINUE\n");
+		memset(&vc, 0, sizeof(vc));
+		vc.cmd = VIDEO_CMD_CONTINUE;
+		return ivtv_video_command(itv, id, &vc, 0);
+	}
+
+	case VIDEO_COMMAND:
+	case VIDEO_TRY_COMMAND: {
+		struct video_command *vc = arg;
+		int try = (cmd == VIDEO_TRY_COMMAND);
+
+		if (try)
+			IVTV_DEBUG_IOCTL("VIDEO_TRY_COMMAND\n");
+		else
+			IVTV_DEBUG_IOCTL("VIDEO_COMMAND\n");
+		return ivtv_video_command(itv, id, vc, try);
+	}
+
+	case VIDEO_GET_EVENT: {
+		struct video_event *ev = arg;
+		DEFINE_WAIT(wait);
+
+		IVTV_DEBUG_IOCTL("VIDEO_GET_EVENT\n");
+		if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+			return -EINVAL;
+		memset(ev, 0, sizeof(*ev));
+		set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
+
+		while (1) {
+			if (test_and_clear_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags))
+				ev->type = VIDEO_EVENT_DECODER_STOPPED;
+			else if (test_and_clear_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags)) {
+				ev->type = VIDEO_EVENT_VSYNC;
+				ev->u.vsync_field = test_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags) ?
+					VIDEO_VSYNC_FIELD_ODD : VIDEO_VSYNC_FIELD_EVEN;
+				if (itv->output_mode == OUT_UDMA_YUV &&
+					(itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) ==
+								IVTV_YUV_MODE_PROGRESSIVE) {
+					ev->u.vsync_field = VIDEO_VSYNC_FIELD_PROGRESSIVE;
+				}
+			}
+			if (ev->type)
+				return 0;
+			if (nonblocking)
+				return -EAGAIN;
+			/* wait for event */
+			prepare_to_wait(&itv->event_waitq, &wait, TASK_INTERRUPTIBLE);
+			if ((itv->i_flags & (IVTV_F_I_EV_DEC_STOPPED|IVTV_F_I_EV_VSYNC)) == 0)
+				schedule();
+			finish_wait(&itv->event_waitq, &wait);
+			if (signal_pending(current)) {
+				/* return if a signal was received */
+				IVTV_DEBUG_INFO("User stopped wait for event\n");
+				return -EINTR;
+			}
+		}
+		break;
+	}
+
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int ivtv_v4l2_do_ioctl(struct inode *inode, struct file *filp,
+			      unsigned int cmd, void *arg)
+{
+	struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data;
+	struct ivtv *itv = id->itv;
+	int ret;
+
+	/* check priority */
+	switch (cmd) {
+	case VIDIOC_S_CTRL:
+	case VIDIOC_S_STD:
+	case VIDIOC_S_INPUT:
+	case VIDIOC_S_OUTPUT:
+	case VIDIOC_S_TUNER:
+	case VIDIOC_S_FREQUENCY:
+	case VIDIOC_S_FMT:
+	case VIDIOC_S_CROP:
+	case VIDIOC_S_AUDIO:
+	case VIDIOC_S_AUDOUT:
+	case VIDIOC_S_EXT_CTRLS:
+	case VIDIOC_S_FBUF:
+		ret = v4l2_prio_check(&itv->prio, &id->prio);
+		if (ret)
+			return ret;
+	}
+
+	switch (cmd) {
+	case VIDIOC_DBG_G_REGISTER:
+	case VIDIOC_DBG_S_REGISTER:
+	case VIDIOC_G_CHIP_IDENT:
+	case VIDIOC_INT_S_AUDIO_ROUTING:
+	case VIDIOC_INT_RESET:
+		if (ivtv_debug & IVTV_DBGFLG_IOCTL) {
+			printk(KERN_INFO "ivtv%d ioctl: ", itv->num);
+			v4l_printk_ioctl(cmd);
+		}
+		return ivtv_debug_ioctls(filp, cmd, arg);
+
+	case VIDIOC_G_PRIORITY:
+	case VIDIOC_S_PRIORITY:
+	case VIDIOC_QUERYCAP:
+	case VIDIOC_ENUMINPUT:
+	case VIDIOC_G_INPUT:
+	case VIDIOC_S_INPUT:
+	case VIDIOC_ENUMOUTPUT:
+	case VIDIOC_G_OUTPUT:
+	case VIDIOC_S_OUTPUT:
+	case VIDIOC_G_FMT:
+	case VIDIOC_S_FMT:
+	case VIDIOC_TRY_FMT:
+	case VIDIOC_ENUM_FMT:
+	case VIDIOC_G_CROP:
+	case VIDIOC_S_CROP:
+	case VIDIOC_G_FREQUENCY:
+	case VIDIOC_S_FREQUENCY:
+	case VIDIOC_ENUMSTD:
+	case VIDIOC_G_STD:
+	case VIDIOC_S_STD:
+	case VIDIOC_S_TUNER:
+	case VIDIOC_G_TUNER:
+	case VIDIOC_ENUMAUDIO:
+	case VIDIOC_S_AUDIO:
+	case VIDIOC_G_AUDIO:
+	case VIDIOC_ENUMAUDOUT:
+	case VIDIOC_S_AUDOUT:
+	case VIDIOC_G_AUDOUT:
+	case VIDIOC_G_SLICED_VBI_CAP:
+	case VIDIOC_LOG_STATUS:
+	case VIDIOC_G_ENC_INDEX:
+	case VIDIOC_ENCODER_CMD:
+	case VIDIOC_TRY_ENCODER_CMD:
+	case VIDIOC_G_FBUF:
+	case VIDIOC_S_FBUF:
+		if (ivtv_debug & IVTV_DBGFLG_IOCTL) {
+			printk(KERN_INFO "ivtv%d ioctl: ", itv->num);
+			v4l_printk_ioctl(cmd);
+		}
+		return ivtv_v4l2_ioctls(itv, filp, cmd, arg);
+
+	case VIDIOC_QUERYMENU:
+	case VIDIOC_QUERYCTRL:
+	case VIDIOC_S_CTRL:
+	case VIDIOC_G_CTRL:
+	case VIDIOC_S_EXT_CTRLS:
+	case VIDIOC_G_EXT_CTRLS:
+	case VIDIOC_TRY_EXT_CTRLS:
+		if (ivtv_debug & IVTV_DBGFLG_IOCTL) {
+			printk(KERN_INFO "ivtv%d ioctl: ", itv->num);
+			v4l_printk_ioctl(cmd);
+		}
+		return ivtv_control_ioctls(itv, cmd, arg);
+
+	case IVTV_IOC_DMA_FRAME:
+	case VIDEO_GET_PTS:
+	case VIDEO_GET_FRAME_COUNT:
+	case VIDEO_GET_EVENT:
+	case VIDEO_PLAY:
+	case VIDEO_STOP:
+	case VIDEO_FREEZE:
+	case VIDEO_CONTINUE:
+	case VIDEO_COMMAND:
+	case VIDEO_TRY_COMMAND:
+		return ivtv_decoder_ioctls(filp, cmd, arg);
+
+	case 0x00005401:	/* Handle isatty() calls */
+		return -EINVAL;
+	default:
+		return v4l_compat_translate_ioctl(inode, filp, cmd, arg,
+						   ivtv_v4l2_do_ioctl);
+	}
+	return 0;
+}
+
+int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg)
+{
+	struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data;
+	struct ivtv *itv = id->itv;
+
+	/* Filter dvb ioctls that cannot be handled by video_usercopy */
+	switch (cmd) {
+	case VIDEO_SELECT_SOURCE:
+		IVTV_DEBUG_IOCTL("VIDEO_SELECT_SOURCE\n");
+		if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+			return -EINVAL;
+		return ivtv_passthrough_mode(itv, arg == VIDEO_SOURCE_DEMUX);
+
+	case AUDIO_SET_MUTE:
+		IVTV_DEBUG_IOCTL("AUDIO_SET_MUTE\n");
+		itv->speed_mute_audio = arg;
+		return 0;
+
+	case AUDIO_CHANNEL_SELECT:
+		IVTV_DEBUG_IOCTL("AUDIO_CHANNEL_SELECT\n");
+		if (arg > AUDIO_STEREO_SWAPPED)
+			return -EINVAL;
+		itv->audio_stereo_mode = arg;
+		ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
+		return 0;
+
+	case AUDIO_BILINGUAL_CHANNEL_SELECT:
+		IVTV_DEBUG_IOCTL("AUDIO_BILINGUAL_CHANNEL_SELECT\n");
+		if (arg > AUDIO_STEREO_SWAPPED)
+			return -EINVAL;
+		itv->audio_bilingual_mode = arg;
+		ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
+		return 0;
+
+	default:
+		break;
+	}
+	return video_usercopy(inode, filp, cmd, arg, ivtv_v4l2_do_ioctl);
+}
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.h b/drivers/media/video/ivtv/ivtv-ioctl.h
new file mode 100644
index 0000000..cbccf7a
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-ioctl.h
@@ -0,0 +1,28 @@
+/*
+    ioctl system call
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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
+ */
+
+u16 service2vbi(int type);
+void expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal);
+u16 get_service_set(struct v4l2_sliced_vbi_format *fmt);
+int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg);
+int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void *arg);
+void ivtv_set_osd_alpha(struct ivtv *itv);
+int ivtv_set_speed(struct ivtv *itv, int speed);
diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c
new file mode 100644
index 0000000..c3a047b
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-irq.c
@@ -0,0 +1,838 @@
+/* interrupt handling
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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 "ivtv-driver.h"
+#include "ivtv-firmware.h"
+#include "ivtv-fileops.h"
+#include "ivtv-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-vbi.h"
+#include "ivtv-yuv.h"
+
+#define DMA_MAGIC_COOKIE 0x000001fe
+
+#define SLICED_VBI_PIO 1
+
+static void ivtv_dma_dec_start(struct ivtv_stream *s);
+
+static const int ivtv_stream_map[] = {
+	IVTV_ENC_STREAM_TYPE_MPG,
+	IVTV_ENC_STREAM_TYPE_YUV,
+	IVTV_ENC_STREAM_TYPE_PCM,
+	IVTV_ENC_STREAM_TYPE_VBI,
+};
+
+static inline int ivtv_use_pio(struct ivtv_stream *s)
+{
+	struct ivtv *itv = s->itv;
+
+	return s->dma == PCI_DMA_NONE ||
+	    (SLICED_VBI_PIO && s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set);
+}
+
+void ivtv_irq_work_handler(struct work_struct *work)
+{
+	struct ivtv *itv = container_of(work, struct ivtv, irq_work_queue);
+
+	DEFINE_WAIT(wait);
+
+	if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags))
+		vbi_work_handler(itv);
+
+	if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags))
+		ivtv_yuv_work_handler(itv);
+}
+
+/* Determine the required DMA size, setup enough buffers in the predma queue and
+   actually copy the data from the card to the buffers in case a PIO transfer is
+   required for this stream.
+ */
+static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MAX_DATA])
+{
+	struct ivtv *itv = s->itv;
+	struct ivtv_buffer *buf;
+	struct list_head *p;
+	u32 bytes_needed = 0;
+	u32 offset, size;
+	u32 UVoffset = 0, UVsize = 0;
+	int skip_bufs = s->q_predma.buffers;
+	int idx = s->SG_length;
+	int rc;
+
+	/* sanity checks */
+	if (s->v4l2dev == NULL) {
+		IVTV_DEBUG_WARN("Stream %s not started\n", s->name);
+		return -1;
+	}
+	if (!test_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
+		IVTV_DEBUG_WARN("Stream %s not open\n", s->name);
+		return -1;
+	}
+
+	/* determine offset, size and PTS for the various streams */
+	switch (s->type) {
+		case IVTV_ENC_STREAM_TYPE_MPG:
+			offset = data[1];
+			size = data[2];
+			s->dma_pts = 0;
+			break;
+
+		case IVTV_ENC_STREAM_TYPE_YUV:
+			offset = data[1];
+			size = data[2];
+			UVoffset = data[3];
+			UVsize = data[4];
+			s->dma_pts = ((u64) data[5] << 32) | data[6];
+			break;
+
+		case IVTV_ENC_STREAM_TYPE_PCM:
+			offset = data[1] + 12;
+			size = data[2] - 12;
+			s->dma_pts = read_dec(offset - 8) |
+				((u64)(read_dec(offset - 12)) << 32);
+			if (itv->has_cx23415)
+				offset += IVTV_DECODER_OFFSET;
+			break;
+
+		case IVTV_ENC_STREAM_TYPE_VBI:
+			size = itv->vbi.enc_size * itv->vbi.fpi;
+			offset = read_enc(itv->vbi.enc_start - 4) + 12;
+			if (offset == 12) {
+				IVTV_DEBUG_INFO("VBI offset == 0\n");
+				return -1;
+			}
+			s->dma_pts = read_enc(offset - 4) | ((u64)read_enc(offset - 8) << 32);
+			break;
+
+		case IVTV_DEC_STREAM_TYPE_VBI:
+			size = read_dec(itv->vbi.dec_start + 4) + 8;
+			offset = read_dec(itv->vbi.dec_start) + itv->vbi.dec_start;
+			s->dma_pts = 0;
+			offset += IVTV_DECODER_OFFSET;
+			break;
+		default:
+			/* shouldn't happen */
+			return -1;
+	}
+
+	/* if this is the start of the DMA then fill in the magic cookie */
+	if (s->SG_length == 0) {
+		if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM ||
+		    s->type == IVTV_DEC_STREAM_TYPE_VBI)) {
+			s->dma_backup = read_dec(offset - IVTV_DECODER_OFFSET);
+			write_dec_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset - IVTV_DECODER_OFFSET);
+		}
+		else {
+			s->dma_backup = read_enc(offset);
+			write_enc_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset);
+		}
+		s->dma_offset = offset;
+	}
+
+	bytes_needed = size;
+	if (s->type == IVTV_ENC_STREAM_TYPE_YUV) {
+		/* The size for the Y samples needs to be rounded upwards to a
+		   multiple of the buf_size. The UV samples then start in the
+		   next buffer. */
+		bytes_needed = s->buf_size * ((bytes_needed + s->buf_size - 1) / s->buf_size);
+		bytes_needed += UVsize;
+	}
+
+	IVTV_DEBUG_DMA("%s %s: 0x%08x bytes at 0x%08x\n",
+		ivtv_use_pio(s) ? "PIO" : "DMA", s->name, bytes_needed, offset);
+
+	rc = ivtv_queue_move(s, &s->q_free, &s->q_full, &s->q_predma, bytes_needed);
+	if (rc < 0) { /* Insufficient buffers */
+		IVTV_DEBUG_WARN("Cannot obtain %d bytes for %s data transfer\n",
+				bytes_needed, s->name);
+		return -1;
+	}
+	if (rc && !s->buffers_stolen && (s->s_flags & IVTV_F_S_APPL_IO)) {
+		IVTV_WARN("All %s stream buffers are full. Dropping data.\n", s->name);
+		IVTV_WARN("Cause: the application is not reading fast enough.\n");
+	}
+	s->buffers_stolen = rc;
+
+	/* got the buffers, now fill in SGarray (DMA) or copy the data from the card
+	   to the buffers (PIO). */
+	buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list);
+	memset(buf->buf, 0, 128);
+	list_for_each(p, &s->q_predma.list) {
+		struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list);
+
+		if (skip_bufs-- > 0)
+			continue;
+		if (!ivtv_use_pio(s)) {
+			s->SGarray[idx].dst = cpu_to_le32(buf->dma_handle);
+			s->SGarray[idx].src = cpu_to_le32(offset);
+			s->SGarray[idx].size = cpu_to_le32(s->buf_size);
+		}
+		buf->bytesused = (size < s->buf_size) ? size : s->buf_size;
+
+		/* If PIO, then copy the data from the card to the buffer */
+		if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+			memcpy_fromio(buf->buf, itv->dec_mem + offset - IVTV_DECODER_OFFSET, buf->bytesused);
+		}
+		else if (ivtv_use_pio(s)) {
+			memcpy_fromio(buf->buf, itv->enc_mem + offset, buf->bytesused);
+		}
+
+		s->q_predma.bytesused += buf->bytesused;
+		size -= buf->bytesused;
+		offset += s->buf_size;
+
+		/* Sync SG buffers */
+		ivtv_buf_sync_for_device(s, buf);
+
+		if (size == 0) {	/* YUV */
+			/* process the UV section */
+			offset = UVoffset;
+			size = UVsize;
+		}
+		idx++;
+	}
+	s->SG_length = idx;
+	return 0;
+}
+
+static void dma_post(struct ivtv_stream *s)
+{
+	struct ivtv *itv = s->itv;
+	struct ivtv_buffer *buf = NULL;
+	struct list_head *p;
+	u32 offset;
+	u32 *u32buf;
+	int x = 0;
+
+	if (ivtv_use_pio(s)) {
+		if (s->q_predma.bytesused)
+			ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
+		s->SG_length = 0;
+	}
+	IVTV_DEBUG_DMA("%s %s completed (%x)\n", ivtv_use_pio(s) ? "PIO" : "DMA",
+			s->name, s->dma_offset);
+	list_for_each(p, &s->q_dma.list) {
+		buf = list_entry(p, struct ivtv_buffer, list);
+		u32buf = (u32 *)buf->buf;
+
+		/* Sync Buffer */
+		ivtv_buf_sync_for_cpu(s, buf);
+
+		if (x == 0) {
+			offset = s->dma_last_offset;
+			if (u32buf[offset / 4] != DMA_MAGIC_COOKIE)
+			{
+				for (offset = 0; offset < 64; offset++) {
+					if (u32buf[offset] == DMA_MAGIC_COOKIE) {
+						break;
+					}
+				}
+				offset *= 4;
+				if (offset == 256) {
+					IVTV_DEBUG_WARN("%s: Couldn't find start of buffer within the first 256 bytes\n", s->name);
+					offset = s->dma_last_offset;
+				}
+				if (s->dma_last_offset != offset)
+					IVTV_DEBUG_WARN("%s: offset %d -> %d\n", s->name, s->dma_last_offset, offset);
+				s->dma_last_offset = offset;
+			}
+			if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM ||
+						s->type == IVTV_DEC_STREAM_TYPE_VBI)) {
+				write_dec_sync(0, s->dma_offset - IVTV_DECODER_OFFSET);
+			}
+			else {
+				write_enc_sync(0, s->dma_offset);
+			}
+			if (offset) {
+				buf->bytesused -= offset;
+				memcpy(buf->buf, buf->buf + offset, buf->bytesused + offset);
+			}
+			*u32buf = cpu_to_le32(s->dma_backup);
+		}
+		x++;
+		/* flag byteswap ABCD -> DCBA for MPG & VBI data outside irq */
+		if (s->type == IVTV_ENC_STREAM_TYPE_MPG ||
+		    s->type == IVTV_ENC_STREAM_TYPE_VBI)
+			set_bit(IVTV_F_B_NEED_BUF_SWAP, &buf->b_flags);
+	}
+	if (buf)
+		buf->bytesused += s->dma_last_offset;
+	if (buf && s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+		/* Parse and Groom VBI Data */
+		s->q_dma.bytesused -= buf->bytesused;
+		ivtv_process_vbi_data(itv, buf, 0, s->type);
+		s->q_dma.bytesused += buf->bytesused;
+		if (s->id == -1) {
+			ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0);
+			return;
+		}
+	}
+	ivtv_queue_move(s, &s->q_dma, NULL, &s->q_full, s->q_dma.bytesused);
+	if (s->id != -1)
+		wake_up(&s->waitq);
+}
+
+void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock)
+{
+	struct ivtv *itv = s->itv;
+	struct ivtv_buffer *buf;
+	struct list_head *p;
+	u32 y_size = itv->params.height * itv->params.width;
+	u32 uv_offset = offset + IVTV_YUV_BUFFER_UV_OFFSET;
+	int y_done = 0;
+	int bytes_written = 0;
+	unsigned long flags = 0;
+	int idx = 0;
+
+	IVTV_DEBUG_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset);
+	buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list);
+	list_for_each(p, &s->q_predma.list) {
+		struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list);
+
+		/* YUV UV Offset from Y Buffer */
+		if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done && bytes_written >= y_size) {
+			offset = uv_offset;
+			y_done = 1;
+		}
+		s->SGarray[idx].src = cpu_to_le32(buf->dma_handle);
+		s->SGarray[idx].dst = cpu_to_le32(offset);
+		s->SGarray[idx].size = cpu_to_le32(buf->bytesused);
+
+		offset += buf->bytesused;
+		bytes_written += buf->bytesused;
+
+		/* Sync SG buffers */
+		ivtv_buf_sync_for_device(s, buf);
+		idx++;
+	}
+	s->SG_length = idx;
+
+	/* Mark last buffer size for Interrupt flag */
+	s->SGarray[s->SG_length - 1].size |= cpu_to_le32(0x80000000);
+
+	/* Sync Hardware SG List of buffers */
+	ivtv_stream_sync_for_device(s);
+	if (lock)
+		spin_lock_irqsave(&itv->dma_reg_lock, flags);
+	if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
+		ivtv_dma_dec_start(s);
+	}
+	else {
+		set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
+	}
+	if (lock)
+		spin_unlock_irqrestore(&itv->dma_reg_lock, flags);
+}
+
+/* start the encoder DMA */
+static void ivtv_dma_enc_start(struct ivtv_stream *s)
+{
+	struct ivtv *itv = s->itv;
+	struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+	int i;
+
+	if (s->q_predma.bytesused)
+		ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
+	IVTV_DEBUG_DMA("start DMA for %s\n", s->name);
+	s->SGarray[s->SG_length - 1].size = cpu_to_le32(le32_to_cpu(s->SGarray[s->SG_length - 1].size) + 256);
+
+	/* If this is an MPEG stream, and VBI data is also pending, then append the
+	   VBI DMA to the MPEG DMA and transfer both sets of data at once.
+
+	   VBI DMA is a second class citizen compared to MPEG and mixing them together
+	   will confuse the firmware (the end of a VBI DMA is seen as the end of a
+	   MPEG DMA, thus effectively dropping an MPEG frame). So instead we make
+	   sure we only use the MPEG DMA to transfer the VBI DMA if both are in
+	   use. This way no conflicts occur. */
+	clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);
+	if (s->type == IVTV_ENC_STREAM_TYPE_MPG && s_vbi->SG_length &&
+			s->SG_length + s_vbi->SG_length <= s->buffers) {
+		ivtv_queue_move(s_vbi, &s_vbi->q_predma, NULL, &s_vbi->q_dma, s_vbi->q_predma.bytesused);
+		s_vbi->SGarray[s_vbi->SG_length - 1].size = cpu_to_le32(le32_to_cpu(s_vbi->SGarray[s->SG_length - 1].size) + 256);
+		for (i = 0; i < s_vbi->SG_length; i++) {
+			s->SGarray[s->SG_length++] = s_vbi->SGarray[i];
+		}
+		itv->vbi.dma_offset = s_vbi->dma_offset;
+		s_vbi->SG_length = 0;
+		set_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);
+		IVTV_DEBUG_DMA("include DMA for %s\n", s->name);
+	}
+
+	/* Mark last buffer size for Interrupt flag */
+	s->SGarray[s->SG_length - 1].size |= cpu_to_le32(0x80000000);
+
+	/* Sync Hardware SG List of buffers */
+	ivtv_stream_sync_for_device(s);
+	write_reg(s->SG_handle, IVTV_REG_ENCDMAADDR);
+	write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x02, IVTV_REG_DMAXFER);
+	set_bit(IVTV_F_I_DMA, &itv->i_flags);
+	itv->cur_dma_stream = s->type;
+	itv->dma_timer.expires = jiffies + HZ / 10;
+	add_timer(&itv->dma_timer);
+}
+
+static void ivtv_dma_dec_start(struct ivtv_stream *s)
+{
+	struct ivtv *itv = s->itv;
+
+	if (s->q_predma.bytesused)
+		ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
+	IVTV_DEBUG_DMA("start DMA for %s\n", s->name);
+	/* put SG Handle into register 0x0c */
+	write_reg(s->SG_handle, IVTV_REG_DECDMAADDR);
+	write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);
+	set_bit(IVTV_F_I_DMA, &itv->i_flags);
+	itv->cur_dma_stream = s->type;
+	itv->dma_timer.expires = jiffies + HZ / 10;
+	add_timer(&itv->dma_timer);
+}
+
+static void ivtv_irq_dma_read(struct ivtv *itv)
+{
+	struct ivtv_stream *s = NULL;
+	struct ivtv_buffer *buf;
+	int hw_stream_type;
+
+	IVTV_DEBUG_IRQ("DEC DMA READ\n");
+	del_timer(&itv->dma_timer);
+	if (read_reg(IVTV_REG_DMASTATUS) & 0x14) {
+		IVTV_DEBUG_WARN("DEC DMA ERROR %x\n", read_reg(IVTV_REG_DMASTATUS));
+		write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+	}
+	if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
+		if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) {
+			s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+			hw_stream_type = 2;
+		}
+		else {
+			s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+			hw_stream_type = 0;
+		}
+		IVTV_DEBUG_DMA("DEC DATA READ %s: %d\n", s->name, s->q_dma.bytesused);
+
+		ivtv_stream_sync_for_cpu(s);
+
+		/* For some reason must kick the firmware, like PIO mode,
+		   I think this tells the firmware we are done and the size
+		   of the xfer so it can calculate what we need next.
+		   I think we can do this part ourselves but would have to
+		   fully calculate xfer info ourselves and not use interrupts
+		 */
+		ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, s->q_dma.bytesused,
+				hw_stream_type);
+
+		/* Free last DMA call */
+		while ((buf = ivtv_dequeue(s, &s->q_dma)) != NULL) {
+			ivtv_buf_sync_for_cpu(s, buf);
+			ivtv_enqueue(s, buf, &s->q_free);
+		}
+		wake_up(&s->waitq);
+	}
+	clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
+	clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+	itv->cur_dma_stream = -1;
+	wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_enc_dma_complete(struct ivtv *itv)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	struct ivtv_stream *s;
+
+	del_timer(&itv->dma_timer);
+	ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data);
+	IVTV_DEBUG_IRQ("ENC DMA COMPLETE %x %d\n", data[0], data[1]);
+	if (test_and_clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags))
+		data[1] = 3;
+	else if (data[1] > 2)
+		return;
+	s = &itv->streams[ivtv_stream_map[data[1]]];
+	if (data[0] & 0x18) {
+		IVTV_DEBUG_WARN("ENC DMA ERROR %x\n", data[0]);
+		write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+		ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, data[1]);
+	}
+	s->SG_length = 0;
+	clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+	itv->cur_dma_stream = -1;
+	dma_post(s);
+	ivtv_stream_sync_for_cpu(s);
+	if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) {
+		u32 tmp;
+
+		s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+		tmp = s->dma_offset;
+		s->dma_offset = itv->vbi.dma_offset;
+		dma_post(s);
+		s->dma_offset = tmp;
+	}
+	wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_dma_err(struct ivtv *itv)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+
+	del_timer(&itv->dma_timer);
+	ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data);
+	IVTV_DEBUG_WARN("DMA ERROR %08x %08x %08x %d\n", data[0], data[1],
+					read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream);
+	if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) &&
+	    itv->cur_dma_stream >= 0 && itv->cur_dma_stream < IVTV_MAX_STREAMS) {
+		struct ivtv_stream *s = &itv->streams[itv->cur_dma_stream];
+
+		/* retry */
+		write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+		if (s->type >= IVTV_DEC_STREAM_TYPE_MPG)
+			ivtv_dma_dec_start(s);
+		else
+			ivtv_dma_enc_start(s);
+		return;
+	}
+	clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
+	clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+	itv->cur_dma_stream = -1;
+	wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_enc_start_cap(struct ivtv *itv)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	struct ivtv_stream *s;
+
+	/* Get DMA destination and size arguments from card */
+	ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA, data);
+	IVTV_DEBUG_IRQ("ENC START CAP %d: %08x %08x\n", data[0], data[1], data[2]);
+
+	if (data[0] > 2 || data[1] == 0 || data[2] == 0) {
+		IVTV_DEBUG_WARN("Unknown input: %08x %08x %08x\n",
+				data[0], data[1], data[2]);
+		return;
+	}
+	clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags);
+	s = &itv->streams[ivtv_stream_map[data[0]]];
+	if (!stream_enc_dma_append(s, data)) {
+		if (ivtv_use_pio(s)) {
+			dma_post(s);
+			ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, data[0]);
+		}
+		else {
+			set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
+		}
+	}
+}
+
+static void ivtv_irq_enc_vbi_cap(struct ivtv *itv)
+{
+	struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG];
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	struct ivtv_stream *s;
+
+	IVTV_DEBUG_IRQ("ENC START VBI CAP\n");
+	s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+
+	if (ivtv_use_pio(s)) {
+		if (stream_enc_dma_append(s, data))
+			return;
+		if (s->q_predma.bytesused)
+			ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
+		s->SG_length = 0;
+		dma_post(s);
+		return;
+	}
+	/* If more than two VBI buffers are pending, then
+	   clear the old ones and start with this new one.
+	   This can happen during transition stages when MPEG capturing is
+	   started, but the first interrupts haven't arrived yet. During
+	   that period VBI requests can accumulate without being able to
+	   DMA the data. Since at most four VBI DMA buffers are available,
+	   we just drop the old requests when there are already three
+	   requests queued. */
+	if (s->SG_length > 2) {
+		struct list_head *p;
+		list_for_each(p, &s->q_predma.list) {
+			struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list);
+			ivtv_buf_sync_for_cpu(s, buf);
+		}
+		ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0);
+		s->SG_length = 0;
+	}
+	/* if we can append the data, and the MPEG stream isn't capturing,
+	   then start a DMA request for just the VBI data. */
+	if (!stream_enc_dma_append(s, data) &&
+			!test_bit(IVTV_F_S_STREAMING, &s_mpg->s_flags)) {
+		set_bit(IVTV_F_I_ENC_VBI, &itv->i_flags);
+		set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
+	}
+}
+
+static void ivtv_irq_dev_vbi_reinsert(struct ivtv *itv)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI];
+
+	IVTV_DEBUG_IRQ("DEC VBI REINSERT\n");
+	if (test_bit(IVTV_F_S_CLAIMED, &s->s_flags) &&
+			!stream_enc_dma_append(s, data)) {
+		dma_post(s);
+	}
+}
+
+static void ivtv_irq_dec_data_req(struct ivtv *itv)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	struct ivtv_stream *s;
+
+	/* YUV or MPG */
+	ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data);
+
+	if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) {
+		itv->dma_data_req_size = itv->params.width * itv->params.height * 3 / 2;
+		itv->dma_data_req_offset = data[1] ? data[1] : yuv_offset[0];
+		s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+	}
+	else {
+		itv->dma_data_req_size = data[2] >= 0x10000 ? 0x10000 : data[2];
+		itv->dma_data_req_offset = data[1];
+		s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+	}
+	IVTV_DEBUG_IRQ("DEC DATA REQ %s: %d %08x %u\n", s->name, s->q_full.bytesused,
+		       itv->dma_data_req_offset, itv->dma_data_req_size);
+	if (itv->dma_data_req_size == 0 || s->q_full.bytesused < itv->dma_data_req_size) {
+		set_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
+	}
+	else {
+		clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
+		ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size);
+		ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 0);
+	}
+}
+
+static void ivtv_irq_vsync(struct ivtv *itv)
+{
+	/* The vsync interrupt is unusual in that it won't clear until
+	 * the end of the first line for the current field, at which
+	 * point it clears itself. This can result in repeated vsync
+	 * interrupts, or a missed vsync. Read some of the registers
+	 * to determine the line being displayed and ensure we handle
+	 * one vsync per frame.
+	 */
+	unsigned int frame = read_reg(0x28c0) & 1;
+	int last_dma_frame = atomic_read(&itv->yuv_info.next_dma_frame);
+
+	if (0) IVTV_DEBUG_IRQ("DEC VSYNC\n");
+
+	if (((frame ^ itv->yuv_info.lace_sync_field) == 0 && ((itv->lastVsyncFrame & 1) ^ itv->yuv_info.lace_sync_field)) ||
+			(frame != (itv->lastVsyncFrame & 1) && !itv->yuv_info.frame_interlaced)) {
+		int next_dma_frame = last_dma_frame;
+
+		if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&itv->yuv_info.next_fill_frame)) {
+			write_reg(yuv_offset[next_dma_frame] >> 4, 0x82c);
+			write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830);
+			write_reg(yuv_offset[next_dma_frame] >> 4, 0x834);
+			write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838);
+			next_dma_frame = (next_dma_frame + 1) & 0x3;
+			atomic_set(&itv->yuv_info.next_dma_frame, next_dma_frame);
+		}
+	}
+	if (frame != (itv->lastVsyncFrame & 1)) {
+		struct ivtv_stream *s = ivtv_get_output_stream(itv);
+		int work = 0;
+
+		itv->lastVsyncFrame += 1;
+		if (frame == 0) {
+			clear_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
+			clear_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags);
+		}
+		else {
+			set_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags);
+		}
+		if (test_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags)) {
+			set_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags);
+			wake_up(&itv->event_waitq);
+		}
+		wake_up(&itv->vsync_waitq);
+		if (s)
+			wake_up(&s->waitq);
+
+		/* Send VBI to saa7127 */
+		if (frame) {
+			set_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags);
+			work = 1;
+		}
+
+		/* Check if we need to update the yuv registers */
+		if ((itv->yuv_info.yuv_forced_update || itv->yuv_info.new_frame_info[last_dma_frame].update) && last_dma_frame != -1) {
+			if (!itv->yuv_info.new_frame_info[last_dma_frame].update)
+				last_dma_frame = (last_dma_frame - 1) & 3;
+
+			if (itv->yuv_info.new_frame_info[last_dma_frame].src_w) {
+				itv->yuv_info.update_frame = last_dma_frame;
+				itv->yuv_info.new_frame_info[last_dma_frame].update = 0;
+				itv->yuv_info.yuv_forced_update = 0;
+				set_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags);
+				work = 1;
+			}
+		}
+		if (work)
+			queue_work(itv->irq_work_queues, &itv->irq_work_queue);
+	}
+}
+
+#define IVTV_IRQ_DMA (IVTV_IRQ_DMA_READ | IVTV_IRQ_ENC_DMA_COMPLETE | IVTV_IRQ_DMA_ERR | IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_VBI_CAP | IVTV_IRQ_DEC_DATA_REQ)
+
+irqreturn_t ivtv_irq_handler(int irq, void *dev_id)
+{
+	struct ivtv *itv = (struct ivtv *)dev_id;
+	u32 combo;
+	u32 stat;
+	int i;
+	u8 vsync_force = 0;
+
+	spin_lock(&itv->dma_reg_lock);
+	/* get contents of irq status register */
+	stat = read_reg(IVTV_REG_IRQSTATUS);
+
+	combo = ~itv->irqmask & stat;
+
+	/* Clear out IRQ */
+	if (combo) write_reg(combo, IVTV_REG_IRQSTATUS);
+
+	if (0 == combo) {
+		/* The vsync interrupt is unusual and clears itself. If we
+		 * took too long, we may have missed it. Do some checks
+		 */
+		if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) {
+			/* vsync is enabled, see if we're in a new field */
+			if ((itv->lastVsyncFrame & 1) != (read_reg(0x28c0) & 1)) {
+				/* New field, looks like we missed it */
+				IVTV_DEBUG_YUV("VSync interrupt missed %d\n",read_reg(0x28c0)>>16);
+				vsync_force = 1;
+			}
+		}
+
+		if (!vsync_force) {
+			/* No Vsync expected, wasn't for us */
+			spin_unlock(&itv->dma_reg_lock);
+			return IRQ_NONE;
+		}
+	}
+
+	/* Exclude interrupts noted below from the output, otherwise the log is flooded with
+	   these messages */
+	if (combo & ~0xff6d0400)
+		IVTV_DEBUG_IRQ("======= valid IRQ bits: 0x%08x ======\n", combo);
+
+	if (combo & IVTV_IRQ_DEC_DMA_COMPLETE) {
+		IVTV_DEBUG_IRQ("DEC DMA COMPLETE\n");
+	}
+
+	if (combo & IVTV_IRQ_DMA_READ) {
+		ivtv_irq_dma_read(itv);
+	}
+
+	if (combo & IVTV_IRQ_ENC_DMA_COMPLETE) {
+		ivtv_irq_enc_dma_complete(itv);
+	}
+
+	if (combo & IVTV_IRQ_DMA_ERR) {
+		ivtv_irq_dma_err(itv);
+	}
+
+	if (combo & IVTV_IRQ_ENC_START_CAP) {
+		ivtv_irq_enc_start_cap(itv);
+	}
+
+	if (combo & IVTV_IRQ_ENC_VBI_CAP) {
+		ivtv_irq_enc_vbi_cap(itv);
+	}
+
+	if (combo & IVTV_IRQ_DEC_VBI_RE_INSERT) {
+		ivtv_irq_dev_vbi_reinsert(itv);
+	}
+
+	if (combo & IVTV_IRQ_ENC_EOS) {
+		IVTV_DEBUG_IRQ("ENC EOS\n");
+		set_bit(IVTV_F_I_EOS, &itv->i_flags);
+		wake_up(&itv->cap_w);
+	}
+
+	if (combo & IVTV_IRQ_DEC_DATA_REQ) {
+		ivtv_irq_dec_data_req(itv);
+	}
+
+	/* Decoder Vertical Sync - We can't rely on 'combo', so check if vsync enabled */
+	if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) {
+		ivtv_irq_vsync(itv);
+	}
+
+	if (combo & IVTV_IRQ_ENC_VIM_RST) {
+		IVTV_DEBUG_IRQ("VIM RST\n");
+		/*ivtv_vapi(itv, CX2341X_ENC_REFRESH_INPUT, 0); */
+	}
+
+	if (combo & IVTV_IRQ_DEC_AUD_MODE_CHG) {
+		IVTV_DEBUG_INFO("Stereo mode changed\n");
+	}
+
+	if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
+		for (i = 0; i < IVTV_MAX_STREAMS; i++) {
+			int idx = (i + itv->irq_rr_idx++) % IVTV_MAX_STREAMS;
+			struct ivtv_stream *s = &itv->streams[idx];
+
+			if (!test_and_clear_bit(IVTV_F_S_DMA_PENDING, &s->s_flags))
+				continue;
+			if (s->type >= IVTV_DEC_STREAM_TYPE_MPG)
+				ivtv_dma_dec_start(s);
+			else
+				ivtv_dma_enc_start(s);
+			break;
+		}
+		if (i == IVTV_MAX_STREAMS && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) {
+			ivtv_udma_start(itv);
+		}
+	}
+
+	spin_unlock(&itv->dma_reg_lock);
+
+	/* If we've just handled a 'forced' vsync, it's safest to say it
+	 * wasn't ours. Another device may have triggered it at just
+	 * the right time.
+	 */
+	return vsync_force ? IRQ_NONE : IRQ_HANDLED;
+}
+
+void ivtv_unfinished_dma(unsigned long arg)
+{
+	struct ivtv *itv = (struct ivtv *)arg;
+
+	if (!test_bit(IVTV_F_I_DMA, &itv->i_flags))
+		return;
+	IVTV_ERR("DMA TIMEOUT %08x %d\n", read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream);
+
+	write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+	clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
+	clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+	itv->cur_dma_stream = -1;
+	wake_up(&itv->dma_waitq);
+}
diff --git a/drivers/media/video/ivtv/ivtv-irq.h b/drivers/media/video/ivtv/ivtv-irq.h
new file mode 100644
index 0000000..a43348a
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-irq.h
@@ -0,0 +1,26 @@
+/*
+    interrupt handling
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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
+ */
+
+irqreturn_t ivtv_irq_handler(int irq, void *dev_id);
+
+void ivtv_irq_work_handler(struct work_struct *work);
+void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock);
+void ivtv_unfinished_dma(unsigned long arg);
diff --git a/drivers/media/video/ivtv/ivtv-mailbox.c b/drivers/media/video/ivtv/ivtv-mailbox.c
new file mode 100644
index 0000000..6ae42a3
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-mailbox.c
@@ -0,0 +1,360 @@
+/*
+    mailbox functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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 <stdarg.h>
+
+#include "ivtv-driver.h"
+#include "ivtv-mailbox.h"
+
+/* Firmware mailbox flags*/
+#define IVTV_MBOX_FIRMWARE_DONE 0x00000004
+#define IVTV_MBOX_DRIVER_DONE   0x00000002
+#define IVTV_MBOX_DRIVER_BUSY   0x00000001
+#define IVTV_MBOX_FREE 		0x00000000
+
+/* Firmware mailbox standard timeout */
+#define IVTV_API_STD_TIMEOUT 	0x02000000
+
+#define API_CACHE 	 (1 << 0) 	/* Allow the command to be stored in the cache */
+#define API_RESULT	 (1 << 1) 	/* Allow 1 second for this cmd to end */
+#define API_FAST_RESULT	 (3 << 1)	/* Allow 0.1 second for this cmd to end */
+#define API_DMA 	 (1 << 3)	/* DMA mailbox, has special handling */
+#define API_NO_WAIT_MB 	 (1 << 4)	/* Command may not wait for a free mailbox */
+#define API_NO_WAIT_RES	 (1 << 5)	/* Command may not wait for the result */
+
+struct ivtv_api_info {
+	int flags;		/* Flags, see above */
+	const char *name; 	/* The name of the command */
+};
+
+#define API_ENTRY(x, f) [x] = { (f), #x }
+
+static const struct ivtv_api_info api_info[256] = {
+	/* MPEG encoder API */
+	API_ENTRY(CX2341X_ENC_PING_FW, 			API_FAST_RESULT),
+	API_ENTRY(CX2341X_ENC_START_CAPTURE, 		API_RESULT),
+	API_ENTRY(CX2341X_ENC_STOP_CAPTURE, 		API_RESULT),
+	API_ENTRY(CX2341X_ENC_SET_AUDIO_ID, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_VIDEO_ID, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_PCR_ID, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_FRAME_RATE, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_FRAME_SIZE, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_BIT_RATE, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_GOP_PROPERTIES, 	API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_ASPECT_RATIO, 	API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_MODE, 	API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_PROPS, 	API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_CORING_LEVELS, 	API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 	API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_VBI_LINE, 		API_RESULT),
+	API_ENTRY(CX2341X_ENC_SET_STREAM_TYPE, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_OUTPUT_PORT, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_AUDIO_PROPERTIES, 	API_CACHE),
+	API_ENTRY(CX2341X_ENC_HALT_FW, 			API_FAST_RESULT),
+	API_ENTRY(CX2341X_ENC_GET_VERSION, 		API_FAST_RESULT),
+	API_ENTRY(CX2341X_ENC_SET_GOP_CLOSURE, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_GET_SEQ_END, 		API_RESULT),
+	API_ENTRY(CX2341X_ENC_SET_PGM_INDEX_INFO, 	API_FAST_RESULT),
+	API_ENTRY(CX2341X_ENC_SET_VBI_CONFIG, 		API_RESULT),
+	API_ENTRY(CX2341X_ENC_SET_DMA_BLOCK_SIZE, 	API_CACHE),
+	API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_10, 	API_FAST_RESULT),
+	API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_9, 	API_FAST_RESULT),
+	API_ENTRY(CX2341X_ENC_SCHED_DMA_TO_HOST, 	API_DMA),
+	API_ENTRY(CX2341X_ENC_INITIALIZE_INPUT, 	API_RESULT),
+	API_ENTRY(CX2341X_ENC_SET_FRAME_DROP_RATE, 	API_CACHE),
+	API_ENTRY(CX2341X_ENC_PAUSE_ENCODER, 		API_RESULT),
+	API_ENTRY(CX2341X_ENC_REFRESH_INPUT, 		API_NO_WAIT_MB),
+	API_ENTRY(CX2341X_ENC_SET_COPYRIGHT, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_EVENT_NOTIFICATION, 	API_RESULT),
+	API_ENTRY(CX2341X_ENC_SET_NUM_VSYNC_LINES, 	API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_PLACEHOLDER, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_MUTE_VIDEO, 		API_RESULT),
+	API_ENTRY(CX2341X_ENC_MUTE_AUDIO, 		API_RESULT),
+	API_ENTRY(CX2341X_ENC_SET_VERT_CROP_LINE,	API_FAST_RESULT),
+	API_ENTRY(CX2341X_ENC_MISC, 			API_FAST_RESULT),
+	/* Obsolete PULLDOWN API command */
+	API_ENTRY(0xb1, 				API_CACHE),
+
+	/* MPEG decoder API */
+	API_ENTRY(CX2341X_DEC_PING_FW, 			API_FAST_RESULT),
+	API_ENTRY(CX2341X_DEC_START_PLAYBACK, 		API_RESULT),
+	API_ENTRY(CX2341X_DEC_STOP_PLAYBACK, 		API_RESULT),
+	API_ENTRY(CX2341X_DEC_SET_PLAYBACK_SPEED, 	API_RESULT),
+	API_ENTRY(CX2341X_DEC_STEP_VIDEO, 		API_RESULT),
+	API_ENTRY(CX2341X_DEC_SET_DMA_BLOCK_SIZE, 	API_CACHE),
+	API_ENTRY(CX2341X_DEC_GET_XFER_INFO, 		API_FAST_RESULT),
+	API_ENTRY(CX2341X_DEC_GET_DMA_STATUS, 		API_FAST_RESULT),
+	API_ENTRY(CX2341X_DEC_SCHED_DMA_FROM_HOST, 	API_DMA),
+	API_ENTRY(CX2341X_DEC_PAUSE_PLAYBACK, 		API_RESULT),
+	API_ENTRY(CX2341X_DEC_HALT_FW, 			API_FAST_RESULT),
+	API_ENTRY(CX2341X_DEC_SET_STANDARD, 		API_CACHE),
+	API_ENTRY(CX2341X_DEC_GET_VERSION, 		API_FAST_RESULT),
+	API_ENTRY(CX2341X_DEC_SET_STREAM_INPUT, 	API_CACHE),
+	API_ENTRY(CX2341X_DEC_GET_TIMING_INFO, 		API_RESULT /*| API_NO_WAIT_RES*/),
+	API_ENTRY(CX2341X_DEC_SET_AUDIO_MODE, 		API_CACHE),
+	API_ENTRY(CX2341X_DEC_SET_EVENT_NOTIFICATION, 	API_RESULT),
+	API_ENTRY(CX2341X_DEC_SET_DISPLAY_BUFFERS, 	API_CACHE),
+	API_ENTRY(CX2341X_DEC_EXTRACT_VBI, 		API_RESULT),
+	API_ENTRY(CX2341X_DEC_SET_DECODER_SOURCE, 	API_FAST_RESULT),
+	API_ENTRY(CX2341X_DEC_SET_PREBUFFERING, 	API_CACHE),
+
+	/* OSD API */
+	API_ENTRY(CX2341X_OSD_GET_FRAMEBUFFER, 		API_FAST_RESULT),
+	API_ENTRY(CX2341X_OSD_GET_PIXEL_FORMAT, 	API_FAST_RESULT),
+	API_ENTRY(CX2341X_OSD_SET_PIXEL_FORMAT, 	API_CACHE),
+	API_ENTRY(CX2341X_OSD_GET_STATE, 		API_FAST_RESULT),
+	API_ENTRY(CX2341X_OSD_SET_STATE, 		API_CACHE),
+	API_ENTRY(CX2341X_OSD_GET_OSD_COORDS, 		API_FAST_RESULT),
+	API_ENTRY(CX2341X_OSD_SET_OSD_COORDS, 		API_CACHE),
+	API_ENTRY(CX2341X_OSD_GET_SCREEN_COORDS, 	API_FAST_RESULT),
+	API_ENTRY(CX2341X_OSD_SET_SCREEN_COORDS, 	API_CACHE),
+	API_ENTRY(CX2341X_OSD_GET_GLOBAL_ALPHA, 	API_FAST_RESULT),
+	API_ENTRY(CX2341X_OSD_SET_GLOBAL_ALPHA, 	API_CACHE),
+	API_ENTRY(CX2341X_OSD_SET_BLEND_COORDS, 	API_CACHE),
+	API_ENTRY(CX2341X_OSD_GET_FLICKER_STATE, 	API_FAST_RESULT),
+	API_ENTRY(CX2341X_OSD_SET_FLICKER_STATE, 	API_CACHE),
+	API_ENTRY(CX2341X_OSD_BLT_COPY, 		API_RESULT),
+	API_ENTRY(CX2341X_OSD_BLT_FILL, 		API_RESULT),
+	API_ENTRY(CX2341X_OSD_BLT_TEXT, 		API_RESULT),
+	API_ENTRY(CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 	API_CACHE),
+	API_ENTRY(CX2341X_OSD_SET_CHROMA_KEY, 		API_CACHE),
+	API_ENTRY(CX2341X_OSD_GET_ALPHA_CONTENT_INDEX, 	API_FAST_RESULT),
+	API_ENTRY(CX2341X_OSD_SET_ALPHA_CONTENT_INDEX, 	API_CACHE)
+};
+
+static int try_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int mb)
+{
+	u32 flags = readl(&mbdata->mbox[mb].flags);
+	int is_free = flags == IVTV_MBOX_FREE || (flags & IVTV_MBOX_FIRMWARE_DONE);
+
+	/* if the mailbox is free, then try to claim it */
+	if (is_free && !test_and_set_bit(mb, &mbdata->busy)) {
+		write_sync(IVTV_MBOX_DRIVER_BUSY, &mbdata->mbox[mb].flags);
+		return 1;
+	}
+	return 0;
+}
+
+/* Try to find a free mailbox. Note mailbox 0 is reserved for DMA and so is not
+   attempted here. */
+static int get_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int flags)
+{
+	unsigned long then = jiffies;
+	int i, mb;
+	int max_mbox = mbdata->max_mbox;
+	int retries = 100;
+
+	/* All slow commands use the same mailbox, serializing them and also
+	   leaving the other mailbox free for simple fast commands. */
+	if ((flags & API_FAST_RESULT) == API_RESULT)
+		max_mbox = 1;
+
+	/* find free non-DMA mailbox */
+	for (i = 0; i < retries; i++) {
+		for (mb = 1; mb <= max_mbox; mb++)
+			if (try_mailbox(itv, mbdata, mb))
+				return mb;
+
+		/* Sleep before a retry, if not atomic */
+		if (!(flags & API_NO_WAIT_MB)) {
+			if (jiffies - then > retries * HZ / 100)
+			       break;
+			ivtv_sleep_timeout(HZ / 100, 0);
+		}
+	}
+	return -ENODEV;
+}
+
+static void write_mailbox(volatile struct ivtv_mailbox __iomem *mbox, int cmd, int args, u32 data[])
+{
+	int i;
+
+	write_sync(cmd, &mbox->cmd);
+	write_sync(IVTV_API_STD_TIMEOUT, &mbox->timeout);
+
+	for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++)
+		write_sync(data[i], &mbox->data[i]);
+
+	write_sync(IVTV_MBOX_DRIVER_DONE | IVTV_MBOX_DRIVER_BUSY, &mbox->flags);
+}
+
+static void clear_all_mailboxes(struct ivtv *itv, struct ivtv_mailbox_data *mbdata)
+{
+	int i;
+
+	for (i = 0; i <= mbdata->max_mbox; i++) {
+		IVTV_DEBUG_WARN("Clearing mailbox %d: cmd 0x%08x flags 0x%08x\n",
+			i, readl(&mbdata->mbox[i].cmd), readl(&mbdata->mbox[i].flags));
+		write_sync(0, &mbdata->mbox[i].flags);
+		clear_bit(i, &mbdata->busy);
+	}
+}
+
+static int ivtv_api_call(struct ivtv *itv, int cmd, int args, u32 data[])
+{
+	struct ivtv_mailbox_data *mbdata = (cmd >= 128) ? &itv->enc_mbox : &itv->dec_mbox;
+	volatile struct ivtv_mailbox __iomem *mbox;
+	int api_timeout = HZ;
+	int flags, mb, i;
+	unsigned long then;
+
+	/* sanity checks */
+	if (NULL == mbdata) {
+		IVTV_ERR("No mailbox allocated\n");
+		return -ENODEV;
+	}
+	if (args < 0 || args > CX2341X_MBOX_MAX_DATA ||
+	    cmd < 0 || cmd > 255 || api_info[cmd].name == NULL) {
+		IVTV_ERR("Invalid API call: cmd = 0x%02x, args = %d\n", cmd, args);
+		return -EINVAL;
+	}
+
+	IVTV_DEBUG_API("API Call: %s\n", api_info[cmd].name);
+
+	/* clear possibly uninitialized part of data array */
+	for (i = args; i < CX2341X_MBOX_MAX_DATA; i++)
+		data[i] = 0;
+
+	/* If this command was issued within the last 30 minutes and with identical
+	   data, then just return 0 as there is no need to issue this command again.
+	   Just an optimization to prevent unnecessary use of mailboxes. */
+	if (itv->api_cache[cmd].last_jiffies &&
+	    jiffies - itv->api_cache[cmd].last_jiffies < HZ * 1800 &&
+	    !memcmp(data, itv->api_cache[cmd].data, sizeof(itv->api_cache[cmd].data))) {
+		itv->api_cache[cmd].last_jiffies = jiffies;
+		return 0;
+	}
+
+	flags = api_info[cmd].flags;
+
+	if (flags & API_DMA) {
+		for (i = 0; i < 100; i++) {
+			mb = i % (mbdata->max_mbox + 1);
+			if (try_mailbox(itv, mbdata, mb)) {
+				write_mailbox(&mbdata->mbox[mb], cmd, args, data);
+				clear_bit(mb, &mbdata->busy);
+				return 0;
+			}
+			IVTV_DEBUG_WARN("%s: mailbox %d not free %08x\n",
+					api_info[cmd].name, mb, readl(&mbdata->mbox[mb].flags));
+		}
+		IVTV_WARN("Could not find free DMA mailbox for %s\n", api_info[cmd].name);
+		clear_all_mailboxes(itv, mbdata);
+		return -EBUSY;
+	}
+
+	if ((flags & API_FAST_RESULT) == API_FAST_RESULT)
+		api_timeout = HZ / 10;
+
+	mb = get_mailbox(itv, mbdata, flags);
+	if (mb < 0) {
+		IVTV_DEBUG_WARN("No free mailbox found (%s)\n", api_info[cmd].name);
+		clear_all_mailboxes(itv, mbdata);
+		return -EBUSY;
+	}
+	mbox = &mbdata->mbox[mb];
+	write_mailbox(mbox, cmd, args, data);
+	if (flags & API_CACHE) {
+		memcpy(itv->api_cache[cmd].data, data, sizeof(itv->api_cache[cmd].data));
+		itv->api_cache[cmd].last_jiffies = jiffies;
+	}
+	if ((flags & API_RESULT) == 0) {
+		clear_bit(mb, &mbdata->busy);
+		return 0;
+	}
+
+	/* Get results */
+	then = jiffies;
+
+	while (!(readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE)) {
+		if (jiffies - then > api_timeout) {
+			IVTV_DEBUG_WARN("Could not get result (%s)\n", api_info[cmd].name);
+			/* reset the mailbox, but it is likely too late already */
+			write_sync(0, &mbox->flags);
+			clear_bit(mb, &mbdata->busy);
+			return -EIO;
+		}
+		if (flags & API_NO_WAIT_RES)
+			mdelay(1);
+		else
+			ivtv_sleep_timeout(HZ / 100, 0);
+	}
+	if (jiffies - then > HZ / 10)
+		IVTV_DEBUG_WARN("%s took %lu jiffies (%d per HZ)\n",
+				api_info[cmd].name, jiffies - then, HZ);
+
+	for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++)
+		data[i] = readl(&mbox->data[i]);
+	write_sync(0, &mbox->flags);
+	clear_bit(mb, &mbdata->busy);
+	return 0;
+}
+
+int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[])
+{
+	int res = ivtv_api_call(itv, cmd, args, data);
+
+	/* Allow a single retry, probably already too late though.
+	   If there is no free mailbox then that is usually an indication
+	   of a more serious problem. */
+	return (res == -EBUSY) ? ivtv_api_call(itv, cmd, args, data) : res;
+}
+
+int ivtv_api_func(void *priv, int cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA])
+{
+	return ivtv_api(priv, cmd, in, data);
+}
+
+int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...)
+{
+	va_list ap;
+	int i;
+
+	va_start(ap, args);
+	for (i = 0; i < args; i++) {
+		data[i] = va_arg(ap, u32);
+	}
+	va_end(ap);
+	return ivtv_api(itv, cmd, args, data);
+}
+
+int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	va_list ap;
+	int i;
+
+	va_start(ap, args);
+	for (i = 0; i < args; i++) {
+		data[i] = va_arg(ap, u32);
+	}
+	va_end(ap);
+	return ivtv_api(itv, cmd, args, data);
+}
+
+/* This one is for stuff that can't sleep.. irq handlers, etc.. */
+void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb, u32 data[])
+{
+	int i;
+
+	for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++)
+		data[i] = readl(&mbdata->mbox[mb].data[i]);
+}
diff --git a/drivers/media/video/ivtv/ivtv-mailbox.h b/drivers/media/video/ivtv/ivtv-mailbox.h
new file mode 100644
index 0000000..79b8aec
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-mailbox.h
@@ -0,0 +1,25 @@
+/*
+    mailbox functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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
+ */
+
+void ivtv_api_get_data(struct ivtv_mailbox_data *mbox, int mb, u32 data[]);
+int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]);
+int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...);
+int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...);
+int ivtv_api_func(void *priv, int cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]);
diff --git a/drivers/media/video/ivtv/ivtv-queue.c b/drivers/media/video/ivtv/ivtv-queue.c
new file mode 100644
index 0000000..ccfcef1
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-queue.c
@@ -0,0 +1,262 @@
+/*
+    buffer queues.
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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 "ivtv-driver.h"
+#include "ivtv-streams.h"
+#include "ivtv-queue.h"
+#include "ivtv-mailbox.h"
+
+int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes)
+{
+	if (s->buf_size - buf->bytesused < copybytes)
+		copybytes = s->buf_size - buf->bytesused;
+	if (copy_from_user(buf->buf + buf->bytesused, src, copybytes)) {
+		return -EFAULT;
+	}
+	buf->bytesused += copybytes;
+	return copybytes;
+}
+
+void ivtv_buf_swap(struct ivtv_buffer *buf)
+{
+	int i;
+
+	for (i = 0; i < buf->bytesused; i += 4)
+		swab32s((u32 *)(buf->buf + i));
+}
+
+void ivtv_queue_init(struct ivtv_queue *q)
+{
+	INIT_LIST_HEAD(&q->list);
+	q->buffers = 0;
+	q->length = 0;
+	q->bytesused = 0;
+}
+
+void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q)
+{
+	unsigned long flags = 0;
+
+	/* clear the buffer if it is going to be enqueued to the free queue */
+	if (q == &s->q_free) {
+		buf->bytesused = 0;
+		buf->readpos = 0;
+		buf->b_flags = 0;
+	}
+	spin_lock_irqsave(&s->qlock, flags);
+	list_add_tail(&buf->list, &q->list);
+	q->buffers++;
+	q->length += s->buf_size;
+	q->bytesused += buf->bytesused - buf->readpos;
+	spin_unlock_irqrestore(&s->qlock, flags);
+}
+
+struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q)
+{
+	struct ivtv_buffer *buf = NULL;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&s->qlock, flags);
+	if (!list_empty(&q->list)) {
+		buf = list_entry(q->list.next, struct ivtv_buffer, list);
+		list_del_init(q->list.next);
+		q->buffers--;
+		q->length -= s->buf_size;
+		q->bytesused -= buf->bytesused - buf->readpos;
+	}
+	spin_unlock_irqrestore(&s->qlock, flags);
+	return buf;
+}
+
+static void ivtv_queue_move_buf(struct ivtv_stream *s, struct ivtv_queue *from,
+		struct ivtv_queue *to, int clear, int full)
+{
+	struct ivtv_buffer *buf = list_entry(from->list.next, struct ivtv_buffer, list);
+
+	list_move_tail(from->list.next, &to->list);
+	from->buffers--;
+	from->length -= s->buf_size;
+	from->bytesused -= buf->bytesused - buf->readpos;
+	/* special handling for q_free */
+	if (clear)
+		buf->bytesused = buf->readpos = buf->b_flags = 0;
+	else if (full) {
+		/* special handling for stolen buffers, assume
+		   all bytes are used. */
+		buf->bytesused = s->buf_size;
+		buf->readpos = buf->b_flags = 0;
+	}
+	to->buffers++;
+	to->length += s->buf_size;
+	to->bytesused += buf->bytesused - buf->readpos;
+}
+
+/* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'.
+   If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'.
+   If 'steal' != NULL, then buffers may also taken from that queue if
+   needed.
+
+   The buffer is automatically cleared if it goes to the free queue. It is
+   also cleared if buffers need to be taken from the 'steal' queue and
+   the 'from' queue is the free queue.
+
+   When 'from' is q_free, then needed_bytes is compared to the total
+   available buffer length, otherwise needed_bytes is compared to the
+   bytesused value. For the 'steal' queue the total available buffer
+   length is always used.
+
+   -ENOMEM is returned if the buffers could not be obtained, 0 if all
+   buffers where obtained from the 'from' list and if non-zero then
+   the number of stolen buffers is returned. */
+int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal,
+		    struct ivtv_queue *to, int needed_bytes)
+{
+	unsigned long flags;
+	int rc = 0;
+	int from_free = from == &s->q_free;
+	int to_free = to == &s->q_free;
+	int bytes_available;
+
+	spin_lock_irqsave(&s->qlock, flags);
+	if (needed_bytes == 0) {
+		from_free = 1;
+		needed_bytes = from->length;
+	}
+
+	bytes_available = from_free ? from->length : from->bytesused;
+	bytes_available += steal ? steal->length : 0;
+
+	if (bytes_available < needed_bytes) {
+		spin_unlock_irqrestore(&s->qlock, flags);
+		return -ENOMEM;
+	}
+	if (from_free) {
+		u32 old_length = to->length;
+
+		while (to->length - old_length < needed_bytes) {
+			if (list_empty(&from->list))
+				from = steal;
+			if (from == steal)
+				rc++; 		/* keep track of 'stolen' buffers */
+			ivtv_queue_move_buf(s, from, to, 1, 0);
+		}
+	}
+	else {
+		u32 old_bytesused = to->bytesused;
+
+		while (to->bytesused - old_bytesused < needed_bytes) {
+			if (list_empty(&from->list))
+				from = steal;
+			if (from == steal)
+				rc++; 		/* keep track of 'stolen' buffers */
+			ivtv_queue_move_buf(s, from, to, to_free, rc);
+		}
+	}
+	spin_unlock_irqrestore(&s->qlock, flags);
+	return rc;
+}
+
+void ivtv_flush_queues(struct ivtv_stream *s)
+{
+	ivtv_queue_move(s, &s->q_io, NULL, &s->q_free, 0);
+	ivtv_queue_move(s, &s->q_full, NULL, &s->q_free, 0);
+	ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0);
+	ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0);
+}
+
+int ivtv_stream_alloc(struct ivtv_stream *s)
+{
+	struct ivtv *itv = s->itv;
+	int SGsize = sizeof(struct ivtv_SG_element) * s->buffers;
+	int i;
+
+	if (s->buffers == 0)
+		return 0;
+
+	IVTV_DEBUG_INFO("Allocate %s%s stream: %d x %d buffers (%dkB total)\n",
+		s->dma != PCI_DMA_NONE ? "DMA " : "",
+		s->name, s->buffers, s->buf_size, s->buffers * s->buf_size / 1024);
+
+	/* Allocate DMA SG Arrays */
+	if (s->dma != PCI_DMA_NONE) {
+		s->SGarray = (struct ivtv_SG_element *)kzalloc(SGsize, GFP_KERNEL);
+		if (s->SGarray == NULL) {
+			IVTV_ERR("Could not allocate SGarray for %s stream\n", s->name);
+			return -ENOMEM;
+		}
+		s->SG_length = 0;
+		s->SG_handle = pci_map_single(itv->dev, s->SGarray, SGsize, s->dma);
+		ivtv_stream_sync_for_cpu(s);
+	}
+
+	/* allocate stream buffers. Initially all buffers are in q_free. */
+	for (i = 0; i < s->buffers; i++) {
+		struct ivtv_buffer *buf = kzalloc(sizeof(struct ivtv_buffer), GFP_KERNEL);
+
+		if (buf == NULL)
+			break;
+		buf->buf = kmalloc(s->buf_size + 256, GFP_KERNEL);
+		if (buf->buf == NULL) {
+			kfree(buf);
+			break;
+		}
+		INIT_LIST_HEAD(&buf->list);
+		if (s->dma != PCI_DMA_NONE) {
+			buf->dma_handle = pci_map_single(s->itv->dev,
+				buf->buf, s->buf_size + 256, s->dma);
+			ivtv_buf_sync_for_cpu(s, buf);
+		}
+		ivtv_enqueue(s, buf, &s->q_free);
+	}
+	if (i == s->buffers)
+		return 0;
+	IVTV_ERR("Couldn't allocate buffers for %s stream\n", s->name);
+	ivtv_stream_free(s);
+	return -ENOMEM;
+}
+
+void ivtv_stream_free(struct ivtv_stream *s)
+{
+	struct ivtv_buffer *buf;
+
+	/* move all buffers to q_free */
+	ivtv_flush_queues(s);
+
+	/* empty q_free */
+	while ((buf = ivtv_dequeue(s, &s->q_free))) {
+		if (s->dma != PCI_DMA_NONE)
+			pci_unmap_single(s->itv->dev, buf->dma_handle,
+				s->buf_size + 256, s->dma);
+		kfree(buf->buf);
+		kfree(buf);
+	}
+
+	/* Free SG Array/Lists */
+	if (s->SGarray != NULL) {
+		if (s->SG_handle != IVTV_DMA_UNMAPPED) {
+			pci_unmap_single(s->itv->dev, s->SG_handle,
+				 sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE);
+			s->SG_handle = IVTV_DMA_UNMAPPED;
+		}
+		s->SGarray = NULL;
+		s->SG_length = 0;
+	}
+}
diff --git a/drivers/media/video/ivtv/ivtv-queue.h b/drivers/media/video/ivtv/ivtv-queue.h
new file mode 100644
index 0000000..903edd4
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-queue.h
@@ -0,0 +1,64 @@
+/*
+    buffer queues.
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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 IVTV_DMA_UNMAPPED	((u32) -1)
+
+/* ivtv_buffer utility functions */
+static inline void ivtv_buf_sync_for_cpu(struct ivtv_stream *s, struct ivtv_buffer *buf)
+{
+	if (s->dma != PCI_DMA_NONE)
+		pci_dma_sync_single_for_cpu(s->itv->dev, buf->dma_handle,
+				s->buf_size + 256, s->dma);
+}
+
+static inline void ivtv_buf_sync_for_device(struct ivtv_stream *s, struct ivtv_buffer *buf)
+{
+	if (s->dma != PCI_DMA_NONE)
+		pci_dma_sync_single_for_device(s->itv->dev, buf->dma_handle,
+				s->buf_size + 256, s->dma);
+}
+
+int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes);
+void ivtv_buf_swap(struct ivtv_buffer *buf);
+
+/* ivtv_queue utility functions */
+void ivtv_queue_init(struct ivtv_queue *q);
+void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q);
+struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q);
+int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal,
+		    struct ivtv_queue *to, int needed_bytes);
+void ivtv_flush_queues(struct ivtv_stream *s);
+
+/* ivtv_stream utility functions */
+int ivtv_stream_alloc(struct ivtv_stream *s);
+void ivtv_stream_free(struct ivtv_stream *s);
+
+static inline void ivtv_stream_sync_for_cpu(struct ivtv_stream *s)
+{
+	pci_dma_sync_single_for_cpu(s->itv->dev, s->SG_handle,
+		sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE);
+}
+
+static inline void ivtv_stream_sync_for_device(struct ivtv_stream *s)
+{
+	pci_dma_sync_single_for_device(s->itv->dev, s->SG_handle,
+		sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE);
+}
diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c
new file mode 100644
index 0000000..01a41a8
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-streams.c
@@ -0,0 +1,977 @@
+/*
+    init/start/stop/exit stream functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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
+ */
+
+/* License: GPL
+ * Author: Kevin Thayer <nufan_wfk at yahoo dot com>
+ *
+ * This file will hold API related functions, both internal (firmware api)
+ * and external (v4l2, etc)
+ *
+ * -----
+ * MPG600/MPG160 support by  T.Adachi <tadachi@tadachi-net.com>
+ *                      and Takeru KOMORIYA<komoriya@paken.org>
+ *
+ * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
+ *                using information provided by Jiun-Kuei Jung @ AVerMedia.
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-fileops.h"
+#include "ivtv-i2c.h"
+#include "ivtv-queue.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-audio.h"
+#include "ivtv-video.h"
+#include "ivtv-vbi.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-irq.h"
+#include "ivtv-streams.h"
+#include "ivtv-cards.h"
+
+static struct file_operations ivtv_v4l2_enc_fops = {
+      .owner = THIS_MODULE,
+      .read = ivtv_v4l2_read,
+      .write = ivtv_v4l2_write,
+      .open = ivtv_v4l2_open,
+      .ioctl = ivtv_v4l2_ioctl,
+      .release = ivtv_v4l2_close,
+      .poll = ivtv_v4l2_enc_poll,
+};
+
+static struct file_operations ivtv_v4l2_dec_fops = {
+      .owner = THIS_MODULE,
+      .read = ivtv_v4l2_read,
+      .write = ivtv_v4l2_write,
+      .open = ivtv_v4l2_open,
+      .ioctl = ivtv_v4l2_ioctl,
+      .release = ivtv_v4l2_close,
+      .poll = ivtv_v4l2_dec_poll,
+};
+
+static struct {
+	const char *name;
+	int vfl_type;
+	int minor_offset;
+	int dma, pio;
+	enum v4l2_buf_type buf_type;
+	struct file_operations *fops;
+} ivtv_stream_info[] = {
+	{	/* IVTV_ENC_STREAM_TYPE_MPG */
+		"encoder MPEG",
+		VFL_TYPE_GRABBER, 0,
+		PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+		&ivtv_v4l2_enc_fops
+	},
+	{	/* IVTV_ENC_STREAM_TYPE_YUV */
+		"encoder YUV",
+		VFL_TYPE_GRABBER, IVTV_V4L2_ENC_YUV_OFFSET,
+		PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+		&ivtv_v4l2_enc_fops
+	},
+	{	/* IVTV_ENC_STREAM_TYPE_VBI */
+		"encoder VBI",
+		VFL_TYPE_VBI, 0,
+		PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VBI_CAPTURE,
+		&ivtv_v4l2_enc_fops
+	},
+	{	/* IVTV_ENC_STREAM_TYPE_PCM */
+		"encoder PCM audio",
+		VFL_TYPE_GRABBER, IVTV_V4L2_ENC_PCM_OFFSET,
+		PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_PRIVATE,
+		&ivtv_v4l2_enc_fops
+	},
+	{	/* IVTV_ENC_STREAM_TYPE_RAD */
+		"encoder radio",
+		VFL_TYPE_RADIO, 0,
+		PCI_DMA_NONE, 1, V4L2_BUF_TYPE_PRIVATE,
+		&ivtv_v4l2_enc_fops
+	},
+	{	/* IVTV_DEC_STREAM_TYPE_MPG */
+		"decoder MPEG",
+		VFL_TYPE_GRABBER, IVTV_V4L2_DEC_MPG_OFFSET,
+		PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT,
+		&ivtv_v4l2_dec_fops
+	},
+	{	/* IVTV_DEC_STREAM_TYPE_VBI */
+		"decoder VBI",
+		VFL_TYPE_VBI, IVTV_V4L2_DEC_VBI_OFFSET,
+		PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_CAPTURE,
+		&ivtv_v4l2_enc_fops
+	},
+	{	/* IVTV_DEC_STREAM_TYPE_VOUT */
+		"decoder VOUT",
+		VFL_TYPE_VBI, IVTV_V4L2_DEC_VOUT_OFFSET,
+		PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_OUTPUT,
+		&ivtv_v4l2_dec_fops
+	},
+	{	/* IVTV_DEC_STREAM_TYPE_YUV */
+		"decoder YUV",
+		VFL_TYPE_GRABBER, IVTV_V4L2_DEC_YUV_OFFSET,
+		PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT,
+		&ivtv_v4l2_dec_fops
+	}
+};
+
+static void ivtv_stream_init(struct ivtv *itv, int type)
+{
+	struct ivtv_stream *s = &itv->streams[type];
+	struct video_device *dev = s->v4l2dev;
+
+	/* we need to keep v4l2dev, so restore it afterwards */
+	memset(s, 0, sizeof(*s));
+	s->v4l2dev = dev;
+
+	/* initialize ivtv_stream fields */
+	s->itv = itv;
+	s->type = type;
+	s->name = ivtv_stream_info[type].name;
+
+	if (ivtv_stream_info[type].pio)
+		s->dma = PCI_DMA_NONE;
+	else
+		s->dma = ivtv_stream_info[type].dma;
+	s->buf_size = itv->stream_buf_size[type];
+	if (s->buf_size)
+		s->buffers = itv->options.megabytes[type] * 1024 * 1024 / s->buf_size;
+	spin_lock_init(&s->qlock);
+	init_waitqueue_head(&s->waitq);
+	s->id = -1;
+	s->SG_handle = IVTV_DMA_UNMAPPED;
+	ivtv_queue_init(&s->q_free);
+	ivtv_queue_init(&s->q_full);
+	ivtv_queue_init(&s->q_dma);
+	ivtv_queue_init(&s->q_predma);
+	ivtv_queue_init(&s->q_io);
+}
+
+static int ivtv_reg_dev(struct ivtv *itv, int type)
+{
+	struct ivtv_stream *s = &itv->streams[type];
+	int vfl_type = ivtv_stream_info[type].vfl_type;
+	int minor_offset = ivtv_stream_info[type].minor_offset;
+	int minor;
+
+	/* These four fields are always initialized. If v4l2dev == NULL, then
+	   this stream is not in use. In that case no other fields but these
+	   four can be used. */
+	s->v4l2dev = NULL;
+	s->itv = itv;
+	s->type = type;
+	s->name = ivtv_stream_info[type].name;
+
+	/* Check whether the radio is supported */
+	if (type == IVTV_ENC_STREAM_TYPE_RAD && !(itv->v4l2_cap & V4L2_CAP_RADIO))
+		return 0;
+	if (type >= IVTV_DEC_STREAM_TYPE_MPG && !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+		return 0;
+
+	if (minor_offset >= 0)
+		/* card number + user defined offset + device offset */
+		minor = itv->num + ivtv_first_minor + minor_offset;
+	else
+		minor = -1;
+
+	/* User explicitly selected 0 buffers for these streams, so don't
+	   create them. */
+	if (minor >= 0 && ivtv_stream_info[type].dma != PCI_DMA_NONE &&
+	    itv->options.megabytes[type] == 0) {
+		IVTV_INFO("Disabled %s device\n", ivtv_stream_info[type].name);
+		return 0;
+	}
+
+	ivtv_stream_init(itv, type);
+
+	/* allocate and initialize the v4l2 video device structure */
+	s->v4l2dev = video_device_alloc();
+	if (s->v4l2dev == NULL) {
+		IVTV_ERR("Couldn't allocate v4l2 video_device for %s\n", s->name);
+		return -ENOMEM;
+	}
+
+	s->v4l2dev->type = VID_TYPE_CAPTURE | VID_TYPE_TUNER | VID_TYPE_TELETEXT |
+		    VID_TYPE_CLIPPING | VID_TYPE_SCALES | VID_TYPE_MPEG_ENCODER;
+	if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+		s->v4l2dev->type |= VID_TYPE_MPEG_DECODER;
+	}
+	snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "ivtv%d %s",
+			itv->num, s->name);
+
+	s->v4l2dev->minor = minor;
+	s->v4l2dev->dev = &itv->dev->dev;
+	s->v4l2dev->fops = ivtv_stream_info[type].fops;
+	s->v4l2dev->release = video_device_release;
+
+	if (minor >= 0) {
+		/* Register device. First try the desired minor, then any free one. */
+		if (video_register_device(s->v4l2dev, vfl_type, minor) &&
+		    video_register_device(s->v4l2dev, vfl_type, -1)) {
+			IVTV_ERR("Couldn't register v4l2 device for %s minor %d\n",
+					s->name, minor);
+			video_device_release(s->v4l2dev);
+			s->v4l2dev = NULL;
+			return -ENOMEM;
+		}
+	}
+	else {
+		/* Don't register a 'hidden' stream (OSD) */
+		IVTV_INFO("Created framebuffer stream for %s\n", s->name);
+		return 0;
+	}
+
+	switch (vfl_type) {
+	case VFL_TYPE_GRABBER:
+		IVTV_INFO("Registered device video%d for %s (%d MB)\n",
+			s->v4l2dev->minor, s->name, itv->options.megabytes[type]);
+		break;
+	case VFL_TYPE_RADIO:
+		IVTV_INFO("Registered device radio%d for %s\n",
+			s->v4l2dev->minor - MINOR_VFL_TYPE_RADIO_MIN, s->name);
+		break;
+	case VFL_TYPE_VBI:
+		if (itv->options.megabytes[type])
+			IVTV_INFO("Registered device vbi%d for %s (%d MB)\n",
+				s->v4l2dev->minor - MINOR_VFL_TYPE_VBI_MIN,
+				s->name, itv->options.megabytes[type]);
+		else
+			IVTV_INFO("Registered device vbi%d for %s\n",
+				s->v4l2dev->minor - MINOR_VFL_TYPE_VBI_MIN, s->name);
+		break;
+	}
+	return 0;
+}
+
+/* Initialize v4l2 variables and register v4l2 devices */
+int ivtv_streams_setup(struct ivtv *itv)
+{
+	int type;
+
+	/* Setup V4L2 Devices */
+	for (type = 0; type < IVTV_MAX_STREAMS; type++) {
+		/* Register Device */
+		if (ivtv_reg_dev(itv, type))
+			break;
+
+		if (itv->streams[type].v4l2dev == NULL)
+			continue;
+
+		/* Allocate Stream */
+		if (ivtv_stream_alloc(&itv->streams[type]))
+			break;
+	}
+	if (type == IVTV_MAX_STREAMS) {
+		return 0;
+	}
+
+	/* One or more streams could not be initialized. Clean 'em all up. */
+	ivtv_streams_cleanup(itv);
+	return -ENOMEM;
+}
+
+/* Unregister v4l2 devices */
+void ivtv_streams_cleanup(struct ivtv *itv)
+{
+	int type;
+
+	/* Teardown all streams */
+	for (type = 0; type < IVTV_MAX_STREAMS; type++) {
+		struct video_device *vdev = itv->streams[type].v4l2dev;
+
+		itv->streams[type].v4l2dev = NULL;
+		if (vdev == NULL)
+			continue;
+
+		ivtv_stream_free(&itv->streams[type]);
+		/* Free Device */
+		if (vdev->minor == -1) /* 'Hidden' never registered stream (OSD) */
+			video_device_release(vdev);
+		else    /* All others, just unregister. */
+			video_unregister_device(vdev);
+	}
+}
+
+static void ivtv_vbi_setup(struct ivtv *itv)
+{
+	int raw = itv->vbi.sliced_in->service_set == 0;
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	int lines;
+	int i;
+
+	/* If Embed then streamtype must be Program */
+	/* TODO: should we really do this? */
+	if (0 && !raw && itv->vbi.insert_mpeg) {
+		itv->params.stream_type = 0;
+
+		/* assign stream type */
+		ivtv_vapi(itv, CX2341X_ENC_SET_STREAM_TYPE, 1, itv->params.stream_type);
+	}
+
+	/* Reset VBI */
+	ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0);
+
+	if (itv->is_60hz) {
+		itv->vbi.count = 12;
+		itv->vbi.start[0] = 10;
+		itv->vbi.start[1] = 273;
+	} else {        /* PAL/SECAM */
+		itv->vbi.count = 18;
+		itv->vbi.start[0] = 6;
+		itv->vbi.start[1] = 318;
+	}
+
+	/* setup VBI registers */
+	itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in);
+
+	/* determine number of lines and total number of VBI bytes.
+	   A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1
+	   The '- 1' byte is probably an unused U or V byte. Or something...
+	   A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal
+	   header, 42 data bytes + checksum (to be confirmed) */
+	if (raw) {
+		lines = itv->vbi.count * 2;
+	} else {
+		lines = itv->is_60hz ? 24 : 38;
+		if (itv->is_60hz && (itv->hw_flags & IVTV_HW_CX25840))
+			lines += 2;
+	}
+
+	itv->vbi.enc_size = lines * (raw ? itv->vbi.raw_size : itv->vbi.sliced_size);
+
+	/* Note: sliced vs raw flag doesn't seem to have any effect
+	   TODO: check mode (0x02) value with older ivtv versions. */
+	data[0] = raw | 0x02 | (0xbd << 8);
+
+	/* Every X number of frames a VBI interrupt arrives (frames as in 25 or 30 fps) */
+	data[1] = 1;
+	/* The VBI frames are stored in a ringbuffer with this size (with a VBI frame as unit) */
+	data[2] = raw ? 4 : 8;
+	/* The start/stop codes determine which VBI lines end up in the raw VBI data area.
+	   The codes are from table 24 in the saa7115 datasheet. Each raw/sliced/video line
+	   is framed with codes FF0000XX where XX is the SAV/EAV (Start/End of Active Video)
+	   code. These values for raw VBI are obtained from a driver disassembly. The sliced
+	   start/stop codes was deduced from this, but they do not appear in the driver.
+	   Other code pairs that I found are: 0x250E6249/0x13545454 and 0x25256262/0x38137F54.
+	   However, I have no idea what these values are for. */
+	if (itv->hw_flags & IVTV_HW_CX25840) {
+		/* Setup VBI for the cx25840 digitizer */
+		if (raw) {
+			data[3] = 0x20602060;
+			data[4] = 0x30703070;
+		} else {
+			data[3] = 0xB0F0B0F0;
+			data[4] = 0xA0E0A0E0;
+		}
+		/* Lines per frame */
+		data[5] = lines;
+		/* bytes per line */
+		data[6] = (raw ? itv->vbi.raw_size : itv->vbi.sliced_size);
+	} else {
+		/* Setup VBI for the saa7115 digitizer */
+		if (raw) {
+			data[3] = 0x25256262;
+			data[4] = 0x387F7F7F;
+		} else {
+			data[3] = 0xABABECEC;
+			data[4] = 0xB6F1F1F1;
+		}
+		/* Lines per frame */
+		data[5] = lines;
+		/* bytes per line */
+		data[6] = itv->vbi.enc_size / lines;
+	}
+
+	IVTV_DEBUG_INFO(
+		"Setup VBI API header 0x%08x pkts %d buffs %d ln %d sz %d\n",
+			data[0], data[1], data[2], data[5], data[6]);
+
+	ivtv_api(itv, CX2341X_ENC_SET_VBI_CONFIG, 7, data);
+
+	/* returns the VBI encoder memory area. */
+	itv->vbi.enc_start = data[2];
+	itv->vbi.fpi = data[0];
+	if (!itv->vbi.fpi)
+		itv->vbi.fpi = 1;
+
+	IVTV_DEBUG_INFO("Setup VBI start 0x%08x frames %d fpi %d lines 0x%08x\n",
+		itv->vbi.enc_start, data[1], itv->vbi.fpi, itv->digitizer);
+
+	/* select VBI lines.
+	   Note that the sliced argument seems to have no effect. */
+	for (i = 2; i <= 24; i++) {
+		int valid;
+
+		if (itv->is_60hz) {
+			valid = i >= 10 && i < 22;
+		} else {
+			valid = i >= 6 && i < 24;
+		}
+		ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, i - 1,
+				valid, 0 , 0, 0);
+		ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, (i - 1) | 0x80000000,
+				valid, 0, 0, 0);
+	}
+
+	/* Remaining VBI questions:
+	   - Is it possible to select particular VBI lines only for inclusion in the MPEG
+	   stream? Currently you can only get the first X lines.
+	   - Is mixed raw and sliced VBI possible?
+	   - What's the meaning of the raw/sliced flag?
+	   - What's the meaning of params 2, 3 & 4 of the Select VBI command? */
+}
+
+int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	struct ivtv *itv = s->itv;
+	int captype = 0, subtype = 0;
+	int enable_passthrough = 0;
+
+	if (s->v4l2dev == NULL)
+		return -EINVAL;
+
+	IVTV_DEBUG_INFO("Start encoder stream %s\n", s->name);
+
+	switch (s->type) {
+	case IVTV_ENC_STREAM_TYPE_MPG:
+		captype = 0;
+		subtype = 3;
+
+		/* Stop Passthrough */
+		if (itv->output_mode == OUT_PASSTHROUGH) {
+			ivtv_passthrough_mode(itv, 0);
+			enable_passthrough = 1;
+		}
+		itv->mpg_data_received = itv->vbi_data_inserted = 0;
+		itv->dualwatch_jiffies = jiffies;
+		itv->dualwatch_stereo_mode = itv->params.audio_properties & 0x0300;
+		itv->search_pack_header = 0;
+		break;
+
+	case IVTV_ENC_STREAM_TYPE_YUV:
+		if (itv->output_mode == OUT_PASSTHROUGH) {
+			captype = 2;
+			subtype = 11;	/* video+audio+decoder */
+			break;
+		}
+		captype = 1;
+		subtype = 1;
+		break;
+	case IVTV_ENC_STREAM_TYPE_PCM:
+		captype = 1;
+		subtype = 2;
+		break;
+	case IVTV_ENC_STREAM_TYPE_VBI:
+		captype = 1;
+		subtype = 4;
+
+		itv->vbi.frame = 0;
+		itv->vbi.inserted_frame = 0;
+		memset(itv->vbi.sliced_mpeg_size,
+			0, sizeof(itv->vbi.sliced_mpeg_size));
+		break;
+	default:
+		return -EINVAL;
+	}
+	s->subtype = subtype;
+	s->buffers_stolen = 0;
+
+	/* mute/unmute video */
+	ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1, test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? 1 : 0);
+
+	/* Clear Streamoff flags in case left from last capture */
+	clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+
+	if (atomic_read(&itv->capturing) == 0) {
+		/* Always use frame based mode. Experiments have demonstrated that byte
+		   stream based mode results in dropped frames and corruption. Not often,
+		   but occasionally. Many thanks go to Leonard Orb who spent a lot of
+		   effort and time trying to trace the cause of the drop outs. */
+		/* 1 frame per DMA */
+		/*ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 128, 0); */
+		ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 1, 1);
+
+		/* Stuff from Windows, we don't know what it is */
+		ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1, 0);
+		/* According to the docs, this should be correct. However, this is
+		   untested. I don't dare enable this without having tested it.
+		   Only very few old cards actually have this hardware combination.
+		ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1,
+			((itv->hw_flags & IVTV_HW_SAA7114) && itv->is_60hz) ? 10001 : 0);
+		*/
+		ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 3, !itv->has_cx23415);
+		ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 8, 0);
+		ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 4, 1);
+		ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12);
+
+		/* assign placeholder */
+		ivtv_vapi(itv, CX2341X_ENC_SET_PLACEHOLDER, 12,
+			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+		ivtv_vapi(itv, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, itv->digitizer, itv->digitizer);
+
+		/* Setup VBI */
+		if (itv->v4l2_cap & V4L2_CAP_VBI_CAPTURE) {
+			ivtv_vbi_setup(itv);
+		}
+
+		/* assign program index info. Mask 7: select I/P/B, Num_req: 400 max */
+		ivtv_vapi_result(itv, data, CX2341X_ENC_SET_PGM_INDEX_INFO, 2, 7, 400);
+		itv->pgm_info_offset = data[0];
+		itv->pgm_info_num = data[1];
+		itv->pgm_info_write_idx = 0;
+		itv->pgm_info_read_idx = 0;
+
+		IVTV_DEBUG_INFO("PGM Index at 0x%08x with %d elements\n",
+				itv->pgm_info_offset, itv->pgm_info_num);
+
+		/* Setup API for Stream */
+		cx2341x_update(itv, ivtv_api_func, NULL, &itv->params);
+	}
+
+	/* Vsync Setup */
+	if (itv->has_cx23415 && !test_and_set_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) {
+		/* event notification (on) */
+		ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_ENC_VIM_RST, -1);
+		ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST);
+	}
+
+	if (atomic_read(&itv->capturing) == 0) {
+		/* Clear all Pending Interrupts */
+		ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
+
+		clear_bit(IVTV_F_I_EOS, &itv->i_flags);
+
+		/* Initialize Digitizer for Capture */
+		ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
+
+		ivtv_sleep_timeout(HZ / 10, 0);
+	}
+
+	/* begin_capture */
+	if (ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, captype, subtype))
+	{
+		IVTV_DEBUG_WARN( "Error starting capture!\n");
+		return -EINVAL;
+	}
+
+	/* Start Passthrough */
+	if (enable_passthrough) {
+		ivtv_passthrough_mode(itv, 1);
+	}
+
+	if (s->type == IVTV_ENC_STREAM_TYPE_VBI)
+		ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP);
+	else
+		ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
+
+	/* you're live! sit back and await interrupts :) */
+	atomic_inc(&itv->capturing);
+	return 0;
+}
+
+static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	struct ivtv *itv = s->itv;
+	int datatype;
+
+	if (s->v4l2dev == NULL)
+		return -EINVAL;
+
+	IVTV_DEBUG_INFO("Setting some initial decoder settings\n");
+
+	/* disable VBI signals, if the MPEG stream contains VBI data,
+	   then that data will be processed automatically for you. */
+	ivtv_disable_vbi(itv);
+
+	/* set audio mode to left/stereo  for dual/stereo mode. */
+	ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
+
+	/* set number of internal decoder buffers */
+	ivtv_vapi(itv, CX2341X_DEC_SET_DISPLAY_BUFFERS, 1, 0);
+
+	/* prebuffering */
+	ivtv_vapi(itv, CX2341X_DEC_SET_PREBUFFERING, 1, 1);
+
+	/* extract from user packets */
+	ivtv_vapi_result(itv, data, CX2341X_DEC_EXTRACT_VBI, 1, 1);
+	itv->vbi.dec_start = data[0];
+
+	IVTV_DEBUG_INFO("Decoder VBI RE-Insert start 0x%08x size 0x%08x\n",
+		itv->vbi.dec_start, data[1]);
+
+	/* set decoder source settings */
+	/* Data type: 0 = mpeg from host,
+	   1 = yuv from encoder,
+	   2 = yuv_from_host */
+	switch (s->type) {
+	case IVTV_DEC_STREAM_TYPE_YUV:
+		datatype = itv->output_mode == OUT_PASSTHROUGH ? 1 : 2;
+		IVTV_DEBUG_INFO("Setup DEC YUV Stream data[0] = %d\n", datatype);
+		break;
+	case IVTV_DEC_STREAM_TYPE_MPG:
+	default:
+		datatype = 0;
+		break;
+	}
+	if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype,
+			itv->params.width, itv->params.height, itv->params.audio_properties)) {
+		IVTV_DEBUG_WARN("COULDN'T INITIALIZE DECODER SOURCE\n");
+	}
+	return 0;
+}
+
+int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset)
+{
+	struct ivtv *itv = s->itv;
+
+	if (s->v4l2dev == NULL)
+		return -EINVAL;
+
+	if (test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags))
+		return 0;	/* already started */
+
+	IVTV_DEBUG_INFO("Starting decode stream %s (gop_offset %d)\n", s->name, gop_offset);
+
+	/* Clear Streamoff */
+	if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
+		/* Initialize Decoder */
+		/* Reprogram Decoder YUV Buffers for YUV */
+		write_reg(yuv_offset[0] >> 4, 0x82c);
+		write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830);
+		write_reg(yuv_offset[0] >> 4, 0x834);
+		write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838);
+
+		write_reg_sync(0x00000000 | (0x0c << 16) | (0x0b << 8), 0x2d24);
+
+		write_reg_sync(0x00108080, 0x2898);
+		/* Enable YUV decoder output */
+		write_reg_sync(0x01, IVTV_REG_VDM);
+	}
+
+	ivtv_setup_v4l2_decode_stream(s);
+
+	/* set dma size to 65536 bytes */
+	ivtv_vapi(itv, CX2341X_DEC_SET_DMA_BLOCK_SIZE, 1, 65536);
+
+	clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+
+	/* Zero out decoder counters */
+	writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[0]);
+	writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[1]);
+	writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[2]);
+	writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[3]);
+	writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[0]);
+	writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[1]);
+	writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[2]);
+	writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[3]);
+
+	/* turn on notification of dual/stereo mode change */
+	ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_DEC_AUD_MODE_CHG, -1);
+
+	/* start playback */
+	ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, gop_offset, 0);
+
+	/* Clear the following Interrupt mask bits for decoding */
+	ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_DECODE);
+	IVTV_DEBUG_IRQ("IRQ Mask is now: 0x%08x\n", itv->irqmask);
+
+	/* you're live! sit back and await interrupts :) */
+	atomic_inc(&itv->decoding);
+	return 0;
+}
+
+void ivtv_stop_all_captures(struct ivtv *itv)
+{
+	int i;
+
+	for (i = IVTV_MAX_STREAMS - 1; i >= 0; i--) {
+		struct ivtv_stream *s = &itv->streams[i];
+
+		if (s->v4l2dev == NULL)
+			continue;
+		if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+			ivtv_stop_v4l2_encode_stream(s, 0);
+		}
+	}
+}
+
+int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end)
+{
+	struct ivtv *itv = s->itv;
+	DECLARE_WAITQUEUE(wait, current);
+	int cap_type;
+	unsigned long then;
+	int stopmode;
+	u32 data[CX2341X_MBOX_MAX_DATA];
+
+	if (s->v4l2dev == NULL)
+		return -EINVAL;
+
+	/* This function assumes that you are allowed to stop the capture
+	   and that we are actually capturing */
+
+	IVTV_DEBUG_INFO("Stop Capture\n");
+
+	if (s->type == IVTV_DEC_STREAM_TYPE_VOUT)
+		return 0;
+	if (atomic_read(&itv->capturing) == 0)
+		return 0;
+
+	switch (s->type) {
+	case IVTV_ENC_STREAM_TYPE_YUV:
+		cap_type = 1;
+		break;
+	case IVTV_ENC_STREAM_TYPE_PCM:
+		cap_type = 1;
+		break;
+	case IVTV_ENC_STREAM_TYPE_VBI:
+		cap_type = 1;
+		break;
+	case IVTV_ENC_STREAM_TYPE_MPG:
+	default:
+		cap_type = 0;
+		break;
+	}
+
+	/* Stop Capture Mode */
+	if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) {
+		stopmode = 0;
+	} else {
+		stopmode = 1;
+	}
+
+	/* end_capture */
+	/* when: 0 =  end of GOP  1 = NOW!, type: 0 = mpeg, subtype: 3 = video+audio */
+	ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, stopmode, cap_type, s->subtype);
+
+	/* only run these if we're shutting down the last cap */
+	if (atomic_read(&itv->capturing) - 1 == 0) {
+		/* event notification (off) */
+		if (test_and_clear_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) {
+			/* type: 0 = refresh */
+			/* on/off: 0 = off, intr: 0x10000000, mbox_id: -1: none */
+			ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_ENC_VIM_RST, -1);
+			ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST);
+		}
+	}
+
+	then = jiffies;
+
+	if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) {
+		if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) {
+			/* only run these if we're shutting down the last cap */
+			unsigned long duration;
+
+			then = jiffies;
+			add_wait_queue(&itv->cap_w, &wait);
+
+			set_current_state(TASK_INTERRUPTIBLE);
+
+			/* wait 2s for EOS interrupt */
+			while (!test_bit(IVTV_F_I_EOS, &itv->i_flags) && jiffies < then + 2 * HZ) {
+				schedule_timeout(HZ / 100);
+			}
+
+			/* To convert jiffies to ms, we must multiply by 1000
+			 * and divide by HZ.  To avoid runtime division, we
+			 * convert this to multiplication by 1000/HZ.
+			 * Since integer division truncates, we get the best
+			 * accuracy if we do a rounding calculation of the constant.
+			 * Think of the case where HZ is 1024.
+			 */
+			duration = ((1000 + HZ / 2) / HZ) * (jiffies - then);
+
+			if (!test_bit(IVTV_F_I_EOS, &itv->i_flags)) {
+				IVTV_DEBUG_WARN("%s: EOS interrupt not received! stopping anyway.\n", s->name);
+				IVTV_DEBUG_WARN("%s: waited %lu ms.\n", s->name, duration);
+			} else {
+				IVTV_DEBUG_INFO("%s: EOS took %lu ms to occur.\n", s->name, duration);
+			}
+			set_current_state(TASK_RUNNING);
+			remove_wait_queue(&itv->cap_w, &wait);
+		}
+
+		then = jiffies;
+		/* Make sure DMA is complete */
+		add_wait_queue(&s->waitq, &wait);
+		set_current_state(TASK_INTERRUPTIBLE);
+		do {
+			/* check if DMA is pending */
+			if ((s->type == IVTV_ENC_STREAM_TYPE_MPG) &&	/* MPG Only */
+			    (read_reg(IVTV_REG_DMASTATUS) & 0x02)) {
+				/* Check for last DMA */
+				ivtv_vapi_result(itv, data, CX2341X_ENC_GET_SEQ_END, 2, 0, 0);
+
+				if (data[0] == 1) {
+					IVTV_DEBUG_DMA("%s: Last DMA of size 0x%08x\n", s->name, data[1]);
+					break;
+				}
+			} else if (read_reg(IVTV_REG_DMASTATUS) & 0x02) {
+				break;
+			}
+
+			ivtv_sleep_timeout(HZ / 100, 1);
+		} while (then + HZ * 2 > jiffies);
+
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&s->waitq, &wait);
+	}
+
+	atomic_dec(&itv->capturing);
+
+	/* Clear capture and no-read bits */
+	clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+
+	if (s->type == IVTV_ENC_STREAM_TYPE_VBI)
+		ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP);
+
+	if (atomic_read(&itv->capturing) > 0) {
+		return 0;
+	}
+
+	/* Set the following Interrupt mask bits for capture */
+	ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
+
+	wake_up(&s->waitq);
+
+	return 0;
+}
+
+int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts)
+{
+	struct ivtv *itv = s->itv;
+
+	if (s->v4l2dev == NULL)
+		return -EINVAL;
+
+	if (s->type != IVTV_DEC_STREAM_TYPE_YUV && s->type != IVTV_DEC_STREAM_TYPE_MPG)
+		return -EINVAL;
+
+	if (!test_bit(IVTV_F_S_STREAMING, &s->s_flags))
+		return 0;
+
+	IVTV_DEBUG_INFO("Stop Decode at %llu, flags: %x\n", pts, flags);
+
+	/* Stop Decoder */
+	if (!(flags & VIDEO_CMD_STOP_IMMEDIATELY) || pts) {
+		u32 tmp = 0;
+
+		/* Wait until the decoder is no longer running */
+		if (pts) {
+			ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3,
+				0, (u32)(pts & 0xffffffff), (u32)(pts >> 32));
+		}
+		while (1) {
+			u32 data[CX2341X_MBOX_MAX_DATA];
+			ivtv_vapi_result(itv, data, CX2341X_DEC_GET_XFER_INFO, 0);
+			if (s->q_full.buffers + s->q_dma.buffers == 0) {
+				if (tmp == data[3])
+					break;
+				tmp = data[3];
+			}
+			if (ivtv_sleep_timeout(HZ/10, 1))
+				break;
+		}
+	}
+	ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & VIDEO_CMD_STOP_TO_BLACK, 0, 0);
+
+	/* turn off notification of dual/stereo mode change */
+	ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_DEC_AUD_MODE_CHG, -1);
+
+	ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_DECODE);
+
+	clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
+	clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+	ivtv_flush_queues(s);
+
+	if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) {
+		/* disable VBI on TV-out */
+		ivtv_disable_vbi(itv);
+	}
+
+	/* decrement decoding */
+	atomic_dec(&itv->decoding);
+
+	set_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags);
+	wake_up(&itv->event_waitq);
+
+	/* wake up wait queues */
+	wake_up(&s->waitq);
+
+	return 0;
+}
+
+int ivtv_passthrough_mode(struct ivtv *itv, int enable)
+{
+	struct ivtv_stream *yuv_stream = &itv->streams[IVTV_ENC_STREAM_TYPE_YUV];
+	struct ivtv_stream *dec_stream = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+
+	if (yuv_stream->v4l2dev == NULL || dec_stream->v4l2dev == NULL)
+		return -EINVAL;
+
+	IVTV_DEBUG_INFO("ivtv ioctl: Select passthrough mode\n");
+
+	/* Prevent others from starting/stopping streams while we
+	   initiate/terminate passthrough mode */
+	if (enable) {
+		if (itv->output_mode == OUT_PASSTHROUGH) {
+			return 0;
+		}
+		if (ivtv_set_output_mode(itv, OUT_PASSTHROUGH) != OUT_PASSTHROUGH)
+			return -EBUSY;
+
+		/* Fully initialize stream, and then unflag init */
+		set_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
+		set_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
+
+		/* Setup YUV Decoder */
+		ivtv_setup_v4l2_decode_stream(dec_stream);
+
+		/* Start Decoder */
+		ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1);
+		atomic_inc(&itv->decoding);
+
+		/* Setup capture if not already done */
+		if (atomic_read(&itv->capturing) == 0) {
+			cx2341x_update(itv, ivtv_api_func, NULL, &itv->params);
+		}
+
+		/* Start Passthrough Mode */
+		ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, 2, 11);
+		atomic_inc(&itv->capturing);
+		return 0;
+	}
+
+	if (itv->output_mode != OUT_PASSTHROUGH)
+		return 0;
+
+	/* Stop Passthrough Mode */
+	ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 11);
+	ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, 1, 0, 0);
+
+	atomic_dec(&itv->capturing);
+	atomic_dec(&itv->decoding);
+	clear_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
+	clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
+	itv->output_mode = OUT_NONE;
+
+	return 0;
+}
diff --git a/drivers/media/video/ivtv/ivtv-streams.h b/drivers/media/video/ivtv/ivtv-streams.h
new file mode 100644
index 0000000..8597b75
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-streams.h
@@ -0,0 +1,31 @@
+/*
+    init/start/stop/exit stream functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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
+ */
+
+int ivtv_streams_setup(struct ivtv *itv);
+void ivtv_streams_cleanup(struct ivtv *itv);
+
+/* Capture related */
+int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s);
+int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end);
+int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset);
+int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts);
+
+void ivtv_stop_all_captures(struct ivtv *itv);
+int ivtv_passthrough_mode(struct ivtv *itv, int enable);
diff --git a/drivers/media/video/ivtv/ivtv-udma.c b/drivers/media/video/ivtv/ivtv-udma.c
new file mode 100644
index 0000000..bd642e1
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-udma.c
@@ -0,0 +1,200 @@
+/*
+    User DMA
+
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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 "ivtv-driver.h"
+#include "ivtv-streams.h"
+#include "ivtv-udma.h"
+
+void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size)
+{
+	dma_page->uaddr = first & PAGE_MASK;
+	dma_page->offset = first & ~PAGE_MASK;
+	dma_page->tail = 1 + ((first+size-1) & ~PAGE_MASK);
+	dma_page->first = (first & PAGE_MASK) >> PAGE_SHIFT;
+	dma_page->last = ((first+size-1) & PAGE_MASK) >> PAGE_SHIFT;
+	dma_page->page_count = dma_page->last - dma_page->first + 1;
+	if (dma_page->page_count == 1) dma_page->tail -= dma_page->offset;
+}
+
+int ivtv_udma_fill_sg_list (struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset)
+{
+	int i, offset;
+
+	offset = dma_page->offset;
+
+	/* Fill SG Array with new values */
+	for (i = 0; i < dma_page->page_count; i++) {
+		if (i == dma_page->page_count - 1) {
+			dma->SGlist[map_offset].length = dma_page->tail;
+		}
+		else {
+			dma->SGlist[map_offset].length = PAGE_SIZE - offset;
+		}
+		dma->SGlist[map_offset].offset = offset;
+		dma->SGlist[map_offset].page = dma->map[map_offset];
+		offset = 0;
+		map_offset++;
+	}
+	return map_offset;
+}
+
+void ivtv_udma_fill_sg_array (struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split) {
+	int i;
+	struct scatterlist *sg;
+
+	for (i = 0, sg = dma->SGlist; i < dma->SG_length; i++, sg++) {
+		dma->SGarray[i].size = cpu_to_le32(sg_dma_len(sg));
+		dma->SGarray[i].src = cpu_to_le32(sg_dma_address(sg));
+		dma->SGarray[i].dst = cpu_to_le32(buffer_offset);
+		buffer_offset += sg_dma_len(sg);
+
+		split -= sg_dma_len(sg);
+		if (split == 0)
+			buffer_offset = buffer_offset_2;
+	}
+}
+
+/* User DMA Buffers */
+void ivtv_udma_alloc(struct ivtv *itv)
+{
+	if (itv->udma.SG_handle == 0) {
+		/* Map DMA Page Array Buffer */
+		itv->udma.SG_handle = pci_map_single(itv->dev, itv->udma.SGarray,
+			   sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+		ivtv_udma_sync_for_cpu(itv);
+	}
+}
+
+int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
+		       void __user *userbuf, int size_in_bytes)
+{
+	struct ivtv_dma_page_info user_dma;
+	struct ivtv_user_dma *dma = &itv->udma;
+	int err;
+
+	IVTV_DEBUG_DMA("ivtv_udma_setup, dst: 0x%08x\n", (unsigned int)ivtv_dest_addr);
+
+	/* Still in USE */
+	if (dma->SG_length || dma->page_count) {
+		IVTV_DEBUG_WARN("ivtv_udma_setup: SG_length %d page_count %d still full?\n",
+			   dma->SG_length, dma->page_count);
+		return -EBUSY;
+	}
+
+	ivtv_udma_get_page_info(&user_dma, (unsigned long)userbuf, size_in_bytes);
+
+	if (user_dma.page_count <= 0) {
+		IVTV_DEBUG_WARN("ivtv_udma_setup: Error %d page_count from %d bytes %d offset\n",
+			   user_dma.page_count, size_in_bytes, user_dma.offset);
+		return -EINVAL;
+	}
+
+	/* Get user pages for DMA Xfer */
+	down_read(&current->mm->mmap_sem);
+	err = get_user_pages(current, current->mm,
+			user_dma.uaddr, user_dma.page_count, 0, 1, dma->map, NULL);
+	up_read(&current->mm->mmap_sem);
+
+	if (user_dma.page_count != err) {
+		IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n",
+			   err, user_dma.page_count);
+		return -EINVAL;
+	}
+
+	dma->page_count = user_dma.page_count;
+
+	/* Fill SG List with new values */
+	ivtv_udma_fill_sg_list(dma, &user_dma, 0);
+
+	/* Map SG List */
+	dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
+
+	/* Fill SG Array with new values */
+	ivtv_udma_fill_sg_array (dma, ivtv_dest_addr, 0, -1);
+
+	/* Tag SG Array with Interrupt Bit */
+	dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000);
+
+	ivtv_udma_sync_for_device(itv);
+	return dma->page_count;
+}
+
+void ivtv_udma_unmap(struct ivtv *itv)
+{
+	struct ivtv_user_dma *dma = &itv->udma;
+	int i;
+
+	IVTV_DEBUG_INFO("ivtv_unmap_user_dma\n");
+
+	/* Nothing to free */
+	if (dma->page_count == 0)
+		return;
+
+	/* Unmap Scatterlist */
+	if (dma->SG_length) {
+		pci_unmap_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
+		dma->SG_length = 0;
+	}
+	/* sync DMA */
+	ivtv_udma_sync_for_cpu(itv);
+
+	/* Release User Pages */
+	for (i = 0; i < dma->page_count; i++) {
+		put_page(dma->map[i]);
+	}
+	dma->page_count = 0;
+}
+
+void ivtv_udma_free(struct ivtv *itv)
+{
+	/* Unmap SG Array */
+	if (itv->udma.SG_handle) {
+		pci_unmap_single(itv->dev, itv->udma.SG_handle,
+			 sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+	}
+
+	/* Unmap Scatterlist */
+	if (itv->udma.SG_length) {
+		pci_unmap_sg(itv->dev, itv->udma.SGlist, itv->udma.page_count, PCI_DMA_TODEVICE);
+	}
+}
+
+void ivtv_udma_start(struct ivtv *itv)
+{
+	IVTV_DEBUG_DMA("start UDMA\n");
+	write_reg(itv->udma.SG_handle, IVTV_REG_DECDMAADDR);
+	write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);
+	set_bit(IVTV_F_I_DMA, &itv->i_flags);
+	set_bit(IVTV_F_I_UDMA, &itv->i_flags);
+}
+
+void ivtv_udma_prepare(struct ivtv *itv)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&itv->dma_reg_lock, flags);
+	if (!test_bit(IVTV_F_I_DMA, &itv->i_flags))
+		ivtv_udma_start(itv);
+	else
+		set_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags);
+	spin_unlock_irqrestore(&itv->dma_reg_lock, flags);
+}
diff --git a/drivers/media/video/ivtv/ivtv-udma.h b/drivers/media/video/ivtv/ivtv-udma.h
new file mode 100644
index 0000000..e131bcc
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-udma.h
@@ -0,0 +1,43 @@
+/*
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2006-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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
+ */
+
+/* User DMA functions */
+void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size);
+int ivtv_udma_fill_sg_list(struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset);
+void ivtv_udma_fill_sg_array(struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split);
+int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
+		       void __user *userbuf, int size_in_bytes);
+void ivtv_udma_unmap(struct ivtv *itv);
+void ivtv_udma_free(struct ivtv *itv);
+void ivtv_udma_alloc(struct ivtv *itv);
+void ivtv_udma_prepare(struct ivtv *itv);
+void ivtv_udma_start(struct ivtv *itv);
+
+static inline void ivtv_udma_sync_for_device(struct ivtv *itv)
+{
+	pci_dma_sync_single_for_device((struct pci_dev *)itv->dev, itv->udma.SG_handle,
+		sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+}
+
+static inline void ivtv_udma_sync_for_cpu(struct ivtv *itv)
+{
+	pci_dma_sync_single_for_cpu((struct pci_dev *)itv->dev, itv->udma.SG_handle,
+		sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+}
diff --git a/drivers/media/video/ivtv/ivtv-vbi.c b/drivers/media/video/ivtv/ivtv-vbi.c
new file mode 100644
index 0000000..5efa5a8
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-vbi.c
@@ -0,0 +1,538 @@
+/*
+    Vertical Blank Interval support functions
+    Copyright (C) 2004-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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 "ivtv-driver.h"
+#include "ivtv-video.h"
+#include "ivtv-vbi.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-queue.h"
+
+static int odd_parity(u8 c)
+{
+	c ^= (c >> 4);
+	c ^= (c >> 2);
+	c ^= (c >> 1);
+
+	return c & 1;
+}
+
+static void passthrough_vbi_data(struct ivtv *itv, int cnt)
+{
+	int wss = 0;
+	u8 cc[4] = { 0x80, 0x80, 0x80, 0x80 };
+	u8 vps[13];
+	int found_cc = 0;
+	int found_wss = 0;
+	int found_vps = 0;
+	int cc_pos = itv->vbi.cc_pos;
+	int i;
+
+	for (i = 0; i < cnt; i++) {
+		struct v4l2_sliced_vbi_data *d = itv->vbi.sliced_dec_data + i;
+
+		if (d->id == V4L2_SLICED_CAPTION_525 && d->line == 21) {
+			found_cc = 1;
+			if (d->field) {
+				cc[2] = d->data[0];
+				cc[3] = d->data[1];
+			} else {
+				cc[0] = d->data[0];
+				cc[1] = d->data[1];
+			}
+		}
+		else if (d->id == V4L2_SLICED_VPS && d->line == 16 && d->field == 0) {
+			memcpy(vps, d->data, sizeof(vps));
+			found_vps = 1;
+		}
+		else if (d->id == V4L2_SLICED_WSS_625 && d->line == 23 && d->field == 0) {
+			wss = d->data[0] | d->data[1] << 8;
+			found_wss = 1;
+		}
+	}
+
+	if (itv->vbi.wss_found != found_wss || itv->vbi.wss != wss) {
+		itv->vbi.wss = wss;
+		itv->vbi.wss_found = found_wss;
+		set_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags);
+	}
+
+	if (found_vps || itv->vbi.vps_found) {
+		itv->vbi.vps[0] = vps[2];
+		itv->vbi.vps[1] = vps[8];
+		itv->vbi.vps[2] = vps[9];
+		itv->vbi.vps[3] = vps[10];
+		itv->vbi.vps[4] = vps[11];
+		itv->vbi.vps_found = found_vps;
+		set_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags);
+	}
+
+	if (found_cc && cc_pos < sizeof(itv->vbi.cc_data_even)) {
+		itv->vbi.cc_data_odd[cc_pos] = cc[0];
+		itv->vbi.cc_data_odd[cc_pos + 1] = cc[1];
+		itv->vbi.cc_data_even[cc_pos] = cc[2];
+		itv->vbi.cc_data_even[cc_pos + 1] = cc[3];
+		itv->vbi.cc_pos = cc_pos + 2;
+		set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
+	}
+}
+
+static void copy_vbi_data(struct ivtv *itv, int lines, u32 pts_stamp)
+{
+	int line = 0;
+	int i;
+	u32 linemask[2] = { 0, 0 };
+	unsigned short size;
+	static const u8 mpeg_hdr_data[] = {
+		0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x0c, 0x66,
+		0x24, 0x01, 0x01, 0xd1, 0xd3, 0xfa, 0xff, 0xff,
+		0x00, 0x00, 0x01, 0xbd, 0x00, 0x1a, 0x84, 0x80,
+		0x07, 0x21, 0x00, 0x5d, 0x63, 0xa7, 0xff, 0xff
+	};
+	const int sd = sizeof(mpeg_hdr_data);	/* start of vbi data */
+	int idx = itv->vbi.frame % IVTV_VBI_FRAMES;
+	u8 *dst = &itv->vbi.sliced_mpeg_data[idx][0];
+
+	for (i = 0; i < lines; i++) {
+		int f, l;
+
+		if (itv->vbi.sliced_data[i].id == 0)
+			continue;
+
+		l = itv->vbi.sliced_data[i].line - 6;
+		f = itv->vbi.sliced_data[i].field;
+		if (f)
+			l += 18;
+		if (l < 32)
+			linemask[0] |= (1 << l);
+		else
+			linemask[1] |= (1 << (l - 32));
+		dst[sd + 12 + line * 43] = service2vbi(itv->vbi.sliced_data[i].id);
+		memcpy(dst + sd + 12 + line * 43 + 1, itv->vbi.sliced_data[i].data, 42);
+		line++;
+	}
+	memcpy(dst, mpeg_hdr_data, sizeof(mpeg_hdr_data));
+	if (line == 36) {
+		/* All lines are used, so there is no space for the linemask
+		   (the max size of the VBI data is 36 * 43 + 4 bytes).
+		   So in this case we use the magic number 'ITV0'. */
+		memcpy(dst + sd, "ITV0", 4);
+		memcpy(dst + sd + 4, dst + sd + 12, line * 43);
+		size = 4 + ((43 * line + 3) & ~3);
+	} else {
+		memcpy(dst + sd, "itv0", 4);
+		memcpy(dst + sd + 4, &linemask[0], 8);
+		size = 12 + ((43 * line + 3) & ~3);
+	}
+	dst[4+16] = (size + 10) >> 8;
+	dst[5+16] = (size + 10) & 0xff;
+	dst[9+16] = 0x21 | ((pts_stamp >> 29) & 0x6);
+	dst[10+16] = (pts_stamp >> 22) & 0xff;
+	dst[11+16] = 1 | ((pts_stamp >> 14) & 0xff);
+	dst[12+16] = (pts_stamp >> 7) & 0xff;
+	dst[13+16] = 1 | ((pts_stamp & 0x7f) << 1);
+	itv->vbi.sliced_mpeg_size[idx] = sd + size;
+}
+
+static int ivtv_convert_ivtv_vbi(struct ivtv *itv, u8 *p)
+{
+	u32 linemask[2];
+	int i, l, id2;
+	int line = 0;
+
+	if (!memcmp(p, "itv0", 4)) {
+		memcpy(linemask, p + 4, 8);
+		p += 12;
+	} else if (!memcmp(p, "ITV0", 4)) {
+		linemask[0] = 0xffffffff;
+		linemask[1] = 0xf;
+		p += 4;
+	} else {
+		/* unknown VBI data stream */
+		return 0;
+	}
+	for (i = 0; i < 36; i++) {
+		int err = 0;
+
+		if (i < 32 && !(linemask[0] & (1 << i)))
+			continue;
+		if (i >= 32 && !(linemask[1] & (1 << (i - 32))))
+			continue;
+		id2 = *p & 0xf;
+		switch (id2) {
+		case IVTV_SLICED_TYPE_TELETEXT_B:
+			id2 = V4L2_SLICED_TELETEXT_B;
+			break;
+		case IVTV_SLICED_TYPE_CAPTION_525:
+			id2 = V4L2_SLICED_CAPTION_525;
+			err = !odd_parity(p[1]) || !odd_parity(p[2]);
+			break;
+		case IVTV_SLICED_TYPE_VPS:
+			id2 = V4L2_SLICED_VPS;
+			break;
+		case IVTV_SLICED_TYPE_WSS_625:
+			id2 = V4L2_SLICED_WSS_625;
+			break;
+		default:
+			id2 = 0;
+			break;
+		}
+		if (err == 0) {
+			l = (i < 18) ? i + 6 : i - 18 + 6;
+			itv->vbi.sliced_dec_data[line].line = l;
+			itv->vbi.sliced_dec_data[line].field = i >= 18;
+			itv->vbi.sliced_dec_data[line].id = id2;
+			memcpy(itv->vbi.sliced_dec_data[line].data, p + 1, 42);
+			line++;
+		}
+		p += 43;
+	}
+	while (line < 36) {
+		itv->vbi.sliced_dec_data[line].id = 0;
+		itv->vbi.sliced_dec_data[line].line = 0;
+		itv->vbi.sliced_dec_data[line].field = 0;
+		line++;
+	}
+	return line * sizeof(itv->vbi.sliced_dec_data[0]);
+}
+
+ssize_t ivtv_write_vbi(struct ivtv *itv, const char __user *ubuf, size_t count)
+{
+	/* Should be a __user pointer, but sparse doesn't parse this bit correctly. */
+	const struct v4l2_sliced_vbi_data *p = (const struct v4l2_sliced_vbi_data *)ubuf;
+	u8 cc[4] = { 0x80, 0x80, 0x80, 0x80 };
+	int found_cc = 0;
+	int cc_pos = itv->vbi.cc_pos;
+
+	if (itv->vbi.service_set_out == 0)
+		return -EPERM;
+
+	while (count >= sizeof(struct v4l2_sliced_vbi_data)) {
+		switch (p->id) {
+		case V4L2_SLICED_CAPTION_525:
+			if (p->id == V4L2_SLICED_CAPTION_525 &&
+			    p->line == 21 &&
+			    (itv->vbi.service_set_out &
+				V4L2_SLICED_CAPTION_525) == 0) {
+				break;
+			}
+			found_cc = 1;
+			if (p->field) {
+				cc[2] = p->data[0];
+				cc[3] = p->data[1];
+			} else {
+				cc[0] = p->data[0];
+				cc[1] = p->data[1];
+			}
+			break;
+
+		case V4L2_SLICED_VPS:
+			if (p->line == 16 && p->field == 0 &&
+			    (itv->vbi.service_set_out & V4L2_SLICED_VPS)) {
+				itv->vbi.vps[0] = p->data[2];
+				itv->vbi.vps[1] = p->data[8];
+				itv->vbi.vps[2] = p->data[9];
+				itv->vbi.vps[3] = p->data[10];
+				itv->vbi.vps[4] = p->data[11];
+				itv->vbi.vps_found = 1;
+				set_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags);
+			}
+			break;
+
+		case V4L2_SLICED_WSS_625:
+			if (p->line == 23 && p->field == 0 &&
+			    (itv->vbi.service_set_out & V4L2_SLICED_WSS_625)) {
+				/* No lock needed for WSS */
+				itv->vbi.wss = p->data[0] | (p->data[1] << 8);
+				itv->vbi.wss_found = 1;
+				set_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags);
+			}
+			break;
+
+		default:
+			break;
+		}
+		count -= sizeof(*p);
+		p++;
+	}
+
+	if (found_cc && cc_pos < sizeof(itv->vbi.cc_data_even)) {
+		itv->vbi.cc_data_odd[cc_pos] = cc[0];
+		itv->vbi.cc_data_odd[cc_pos + 1] = cc[1];
+		itv->vbi.cc_data_even[cc_pos] = cc[2];
+		itv->vbi.cc_data_even[cc_pos + 1] = cc[3];
+		itv->vbi.cc_pos = cc_pos + 2;
+		set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
+	}
+
+	return (const char __user *)p - ubuf;
+}
+
+/* Compress raw VBI format, removes leading SAV codes and surplus space after the
+   field.
+   Returns new compressed size. */
+static u32 compress_raw_buf(struct ivtv *itv, u8 *buf, u32 size)
+{
+	u32 line_size = itv->vbi.raw_decoder_line_size;
+	u32 lines = itv->vbi.count;
+	u8 sav1 = itv->vbi.raw_decoder_sav_odd_field;
+	u8 sav2 = itv->vbi.raw_decoder_sav_even_field;
+	u8 *q = buf;
+	u8 *p;
+	int i;
+
+	for (i = 0; i < lines; i++) {
+		p = buf + i * line_size;
+
+		/* Look for SAV code */
+		if (p[0] != 0xff || p[1] || p[2] || (p[3] != sav1 && p[3] != sav2)) {
+			break;
+		}
+		memcpy(q, p + 4, line_size - 4);
+		q += line_size - 4;
+	}
+	return lines * (line_size - 4);
+}
+
+
+/* Compressed VBI format, all found sliced blocks put next to one another
+   Returns new compressed size */
+static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 sav)
+{
+	u32 line_size = itv->vbi.sliced_decoder_line_size;
+	struct v4l2_decode_vbi_line vbi;
+	int i;
+
+	/* find the first valid line */
+	for (i = 0; i < size; i++, buf++) {
+		if (buf[0] == 0xff && !buf[1] && !buf[2] && buf[3] == sav)
+			break;
+	}
+
+	size -= i;
+	if (size < line_size) {
+		return line;
+	}
+	for (i = 0; i < size / line_size; i++) {
+		u8 *p = buf + i * line_size;
+
+		/* Look for SAV code  */
+		if (p[0] != 0xff || p[1] || p[2] || p[3] != sav) {
+			continue;
+		}
+		vbi.p = p + 4;
+		itv->video_dec_func(itv, VIDIOC_INT_DECODE_VBI_LINE, &vbi);
+		if (vbi.type) {
+			itv->vbi.sliced_data[line].id = vbi.type;
+			itv->vbi.sliced_data[line].field = vbi.is_second_field;
+			itv->vbi.sliced_data[line].line = vbi.line;
+			memcpy(itv->vbi.sliced_data[line].data, vbi.p, 42);
+			line++;
+		}
+	}
+	return line;
+}
+
+void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf,
+			   u64 pts_stamp, int streamtype)
+{
+	u8 *p = (u8 *) buf->buf;
+	u32 size = buf->bytesused;
+	int y;
+
+	/* Raw VBI data */
+	if (streamtype == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set == 0) {
+		u8 type;
+
+		ivtv_buf_swap(buf);
+
+		type = p[3];
+
+		size = buf->bytesused = compress_raw_buf(itv, p, size);
+
+		/* second field of the frame? */
+		if (type == itv->vbi.raw_decoder_sav_even_field) {
+			/* Dirty hack needed for backwards
+			   compatibility of old VBI software. */
+			p += size - 4;
+			memcpy(p, &itv->vbi.frame, 4);
+			itv->vbi.frame++;
+		}
+		return;
+	}
+
+	/* Sliced VBI data with data insertion */
+	if (streamtype == IVTV_ENC_STREAM_TYPE_VBI) {
+		int lines;
+
+		ivtv_buf_swap(buf);
+
+		/* first field */
+		lines = compress_sliced_buf(itv, 0, p, size / 2,
+			itv->vbi.sliced_decoder_sav_odd_field);
+		/* second field */
+		/* experimentation shows that the second half does not always begin
+		   at the exact address. So start a bit earlier (hence 32). */
+		lines = compress_sliced_buf(itv, lines, p + size / 2 - 32, size / 2 + 32,
+			itv->vbi.sliced_decoder_sav_even_field);
+		/* always return at least one empty line */
+		if (lines == 0) {
+			itv->vbi.sliced_data[0].id = 0;
+			itv->vbi.sliced_data[0].line = 0;
+			itv->vbi.sliced_data[0].field = 0;
+			lines = 1;
+		}
+		buf->bytesused = size = lines * sizeof(itv->vbi.sliced_data[0]);
+		memcpy(p, &itv->vbi.sliced_data[0], size);
+
+		if (itv->vbi.insert_mpeg) {
+			copy_vbi_data(itv, lines, pts_stamp);
+		}
+		itv->vbi.frame++;
+		return;
+	}
+
+	/* Sliced VBI re-inserted from an MPEG stream */
+	if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) {
+		/* If the size is not 4-byte aligned, then the starting address
+		   for the swapping is also shifted. After swapping the data the
+		   real start address of the VBI data is exactly 4 bytes after the
+		   original start. It's a bit fiddly but it works like a charm.
+		   Non-4-byte alignment happens when an lseek is done on the input
+		   mpeg file to a non-4-byte aligned position. So on arrival here
+		   the VBI data is also non-4-byte aligned. */
+		int offset = size & 3;
+		int cnt;
+
+		if (offset) {
+			p += 4 - offset;
+		}
+		/* Swap Buffer */
+		for (y = 0; y < size; y += 4) {
+		       swab32s((u32 *)(p + y));
+		}
+
+		cnt = ivtv_convert_ivtv_vbi(itv, p + offset);
+		memcpy(buf->buf, itv->vbi.sliced_dec_data, cnt);
+		buf->bytesused = cnt;
+
+		passthrough_vbi_data(itv, cnt / sizeof(itv->vbi.sliced_dec_data[0]));
+		return;
+	}
+}
+
+void ivtv_disable_vbi(struct ivtv *itv)
+{
+	clear_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags);
+	clear_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags);
+	clear_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
+	ivtv_set_wss(itv, 0, 0);
+	ivtv_set_cc(itv, 0, 0, 0, 0, 0);
+	ivtv_set_vps(itv, 0, 0, 0, 0, 0, 0);
+	itv->vbi.vps_found = itv->vbi.wss_found = 0;
+	itv->vbi.wss = 0;
+	itv->vbi.cc_pos = 0;
+}
+
+
+void vbi_work_handler(struct ivtv *itv)
+{
+	struct v4l2_sliced_vbi_data data;
+
+	/* Lock */
+	if (itv->output_mode == OUT_PASSTHROUGH) {
+		/* Note: currently only the saa7115 is used in a PVR350,
+		   so these commands are for now saa7115 specific. */
+		if (itv->is_50hz) {
+			data.id = V4L2_SLICED_WSS_625;
+			data.field = 0;
+
+			if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
+				ivtv_set_wss(itv, 1, data.data[0] & 0xf);
+				itv->vbi.wss_no_update = 0;
+			} else if (itv->vbi.wss_no_update == 4) {
+				ivtv_set_wss(itv, 1, 0x8);  /* 4x3 full format */
+			} else {
+				itv->vbi.wss_no_update++;
+			}
+		}
+		else {
+			u8 c1 = 0, c2 = 0, c3 = 0, c4 = 0;
+			int mode = 0;
+
+			data.id = V4L2_SLICED_CAPTION_525;
+			data.field = 0;
+			if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
+				mode |= 1;
+				c1 = data.data[0];
+				c2 = data.data[1];
+			}
+			data.field = 1;
+			if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
+				mode |= 2;
+				c3 = data.data[0];
+				c4 = data.data[1];
+			}
+			if (mode) {
+				itv->vbi.cc_no_update = 0;
+				ivtv_set_cc(itv, mode, c1, c2, c3, c4);
+			} else if (itv->vbi.cc_no_update == 4) {
+				ivtv_set_cc(itv, 0, 0, 0, 0, 0);
+			} else {
+				itv->vbi.cc_no_update++;
+			}
+		}
+		return;
+	}
+
+	if (test_and_clear_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags)) {
+		/* Lock */
+		ivtv_set_wss(itv, itv->vbi.wss_found, itv->vbi.wss & 0xf);
+	}
+
+	if (test_and_clear_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags)) {
+		if (itv->vbi.cc_pos == 0) {
+			ivtv_set_cc(itv, 3, 0x80, 0x80, 0x80, 0x80);
+		}
+		while (itv->vbi.cc_pos) {
+			u8 cc_odd0 = itv->vbi.cc_data_odd[0];
+			u8 cc_odd1 = itv->vbi.cc_data_odd[1];
+			u8 cc_even0 = itv->vbi.cc_data_even[0];
+			u8 cc_even1 = itv->vbi.cc_data_even[1];
+
+			memcpy(itv->vbi.cc_data_odd, itv->vbi.cc_data_odd + 2, sizeof(itv->vbi.cc_data_odd) - 2);
+			memcpy(itv->vbi.cc_data_even, itv->vbi.cc_data_even + 2, sizeof(itv->vbi.cc_data_even) - 2);
+			itv->vbi.cc_pos -= 2;
+			if (itv->vbi.cc_pos && cc_odd0 == 0x80 && cc_odd1 == 0x80)
+				continue;
+
+			/* Send to Saa7127 */
+			ivtv_set_cc(itv, 3, cc_odd0, cc_odd1, cc_even0, cc_even1);
+			if (itv->vbi.cc_pos == 0)
+				set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
+			break;
+		}
+	}
+
+	if (test_and_clear_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags)) {
+		/* Lock */
+		ivtv_set_vps(itv, itv->vbi.vps_found,
+			itv->vbi.vps[0], itv->vbi.vps[1],
+			itv->vbi.vps[2], itv->vbi.vps[3], itv->vbi.vps[4]);
+	}
+}
diff --git a/drivers/media/video/ivtv/ivtv-vbi.h b/drivers/media/video/ivtv/ivtv-vbi.h
new file mode 100644
index 0000000..cdaea69
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-vbi.h
@@ -0,0 +1,26 @@
+/*
+    Vertical Blank Interval support functions
+    Copyright (C) 2004-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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
+ */
+
+ssize_t ivtv_write_vbi(struct ivtv *itv, const char __user *ubuf, size_t count);
+void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf,
+			   u64 pts_stamp, int streamtype);
+int ivtv_used_line(struct ivtv *itv, int line, int field);
+void ivtv_disable_vbi(struct ivtv *itv);
+void ivtv_set_vbi(unsigned long arg);
+void vbi_work_handler(struct ivtv *itv);
diff --git a/drivers/media/video/ivtv/ivtv-version.h b/drivers/media/video/ivtv/ivtv-version.h
new file mode 100644
index 0000000..85530a3
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-version.h
@@ -0,0 +1,26 @@
+/*
+    ivtv driver version information
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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 IVTV_DRIVER_NAME "ivtv"
+#define IVTV_DRIVER_VERSION_MAJOR 1
+#define IVTV_DRIVER_VERSION_MINOR 0
+#define IVTV_DRIVER_VERSION_PATCHLEVEL 0
+
+#define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL)
+#define IVTV_DRIVER_VERSION KERNEL_VERSION(IVTV_DRIVER_VERSION_MAJOR,IVTV_DRIVER_VERSION_MINOR,IVTV_DRIVER_VERSION_PATCHLEVEL)
diff --git a/drivers/media/video/ivtv/ivtv-video.c b/drivers/media/video/ivtv/ivtv-video.c
new file mode 100644
index 0000000..5858b19
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-video.c
@@ -0,0 +1,142 @@
+/*
+    saa7127 interface functions
+    Copyright (C) 2004-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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 "ivtv-driver.h"
+#include "ivtv-video.h"
+#include "ivtv-i2c.h"
+#include "ivtv-gpio.h"
+#include "ivtv-cards.h"
+#include <media/upd64031a.h>
+#include <media/upd64083.h>
+
+void ivtv_set_vps(struct ivtv *itv, int enabled, u8 vps1, u8 vps2, u8 vps3,
+		  u8 vps4, u8 vps5)
+{
+	struct v4l2_sliced_vbi_data data;
+
+	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+		return;
+	data.id = V4L2_SLICED_VPS;
+	data.field = 0;
+	data.line = enabled ? 16 : 0;
+	data.data[4] = vps1;
+	data.data[10] = vps2;
+	data.data[11] = vps3;
+	data.data[12] = vps4;
+	data.data[13] = vps5;
+	ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+}
+
+void ivtv_set_cc(struct ivtv *itv, int mode, u8 cc1, u8 cc2, u8 cc3, u8 cc4)
+{
+	struct v4l2_sliced_vbi_data data;
+
+	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+		return;
+	data.id = V4L2_SLICED_CAPTION_525;
+	data.field = 0;
+	data.line = (mode & 1) ? 21 : 0;
+	data.data[0] = cc1;
+	data.data[1] = cc2;
+	ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+	data.field = 1;
+	data.line = (mode & 2) ? 21 : 0;
+	data.data[0] = cc3;
+	data.data[1] = cc4;
+	ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+}
+
+void ivtv_set_wss(struct ivtv *itv, int enabled, int mode)
+{
+	struct v4l2_sliced_vbi_data data;
+
+	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+		return;
+	/* When using a 50 Hz system, always turn on the
+	   wide screen signal with 4x3 ratio as the default.
+	   Turning this signal on and off can confuse certain
+	   TVs. As far as I can tell there is no reason not to
+	   transmit this signal. */
+	if ((itv->std & V4L2_STD_625_50) && !enabled) {
+		enabled = 1;
+		mode = 0x08;  /* 4x3 full format */
+	}
+	data.id = V4L2_SLICED_WSS_625;
+	data.field = 0;
+	data.line = enabled ? 23 : 0;
+	data.data[0] = mode & 0xff;
+	data.data[1] = (mode >> 8) & 0xff;
+	ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+}
+
+void ivtv_video_set_io(struct ivtv *itv)
+{
+	struct v4l2_routing route;
+	int inp = itv->active_input;
+	u32 type;
+
+	route.input = itv->card->video_inputs[inp].video_input;
+	route.output = 0;
+	itv->video_dec_func(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+
+	type = itv->card->video_inputs[inp].video_type;
+
+	if (type == IVTV_CARD_INPUT_VID_TUNER) {
+		route.input = 0;  /* Tuner */
+	} else if (type < IVTV_CARD_INPUT_COMPOSITE1) {
+		route.input = 2;  /* S-Video */
+	} else {
+		route.input = 1;  /* Composite */
+	}
+
+	if (itv->card->hw_video & IVTV_HW_GPIO)
+		ivtv_gpio(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+
+	if (itv->card->hw_video & IVTV_HW_UPD64031A) {
+		if (type == IVTV_CARD_INPUT_VID_TUNER ||
+		    type >= IVTV_CARD_INPUT_COMPOSITE1) {
+			/* Composite: GR on, connect to 3DYCS */
+			route.input = UPD64031A_GR_ON | UPD64031A_3DYCS_COMPOSITE;
+		} else {
+			/* S-Video: GR bypassed, turn it off */
+			route.input = UPD64031A_GR_OFF | UPD64031A_3DYCS_DISABLE;
+		}
+		route.input |= itv->card->gr_config;
+
+		ivtv_upd64031a(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+	}
+
+	if (itv->card->hw_video & IVTV_HW_UPD6408X) {
+		route.input = UPD64083_YCS_MODE;
+		if (type > IVTV_CARD_INPUT_VID_TUNER &&
+		    type < IVTV_CARD_INPUT_COMPOSITE1) {
+			/* S-Video uses YCNR mode and internal Y-ADC, the upd64031a
+			   is not used. */
+			route.input |= UPD64083_YCNR_MODE;
+		}
+		else if (itv->card->hw_video & IVTV_HW_UPD64031A) {
+		  /* Use upd64031a output for tuner and composite(CX23416GYC only) inputs */
+		  if ((type == IVTV_CARD_INPUT_VID_TUNER)||
+		      (itv->card->type == IVTV_CARD_CX23416GYC)) {
+		    route.input |= UPD64083_EXT_Y_ADC;
+		  }
+		}
+		ivtv_upd64083(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+	}
+}
diff --git a/drivers/media/video/ivtv/ivtv-video.h b/drivers/media/video/ivtv/ivtv-video.h
new file mode 100644
index 0000000..c8ade5d
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-video.h
@@ -0,0 +1,24 @@
+/*
+    saa7127 interface functions
+    Copyright (C) 2004-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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
+ */
+
+void ivtv_set_wss(struct ivtv *itv, int enabled, int mode);
+void ivtv_set_cc(struct ivtv *itv, int mode, u8 cc1, u8 cc2, u8 cc3, u8 cc4);
+void ivtv_set_vps(struct ivtv *itv, int enabled, u8 vps1, u8 vps2, u8 vps3,
+		  u8 vps4, u8 vps5);
+void ivtv_video_set_io(struct ivtv *itv);
diff --git a/drivers/media/video/ivtv/ivtv-yuv.c b/drivers/media/video/ivtv/ivtv-yuv.c
new file mode 100644
index 0000000..bcea0954
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-yuv.c
@@ -0,0 +1,1129 @@
+/*
+    yuv support
+
+    Copyright (C) 2007  Ian Armstrong <ian@iarmst.demon.co.uk>
+
+    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 "ivtv-driver.h"
+#include "ivtv-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+#include "ivtv-yuv.h"
+
+static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
+				 struct ivtv_dma_frame *args)
+{
+	struct ivtv_dma_page_info y_dma;
+	struct ivtv_dma_page_info uv_dma;
+
+	int i;
+	int y_pages, uv_pages;
+
+	unsigned long y_buffer_offset, uv_buffer_offset;
+	int y_decode_height, uv_decode_height, y_size;
+	int frame = atomic_read(&itv->yuv_info.next_fill_frame);
+
+	y_buffer_offset = IVTV_DEC_MEM_START + yuv_offset[frame];
+	uv_buffer_offset = y_buffer_offset + IVTV_YUV_BUFFER_UV_OFFSET;
+
+	y_decode_height = uv_decode_height = args->src.height + args->src.top;
+
+	if (y_decode_height < 512-16)
+		y_buffer_offset += 720 * 16;
+
+	if (y_decode_height & 15)
+		y_decode_height = (y_decode_height + 16) & ~15;
+
+	if (uv_decode_height & 31)
+		uv_decode_height = (uv_decode_height + 32) & ~31;
+
+	y_size = 720 * y_decode_height;
+
+	/* Still in USE */
+	if (dma->SG_length || dma->page_count) {
+		IVTV_DEBUG_WARN("prep_user_dma: SG_length %d page_count %d still full?\n",
+				dma->SG_length, dma->page_count);
+		return -EBUSY;
+	}
+
+	ivtv_udma_get_page_info (&y_dma, (unsigned long)args->y_source, 720 * y_decode_height);
+	ivtv_udma_get_page_info (&uv_dma, (unsigned long)args->uv_source, 360 * uv_decode_height);
+
+	/* Get user pages for DMA Xfer */
+	down_read(&current->mm->mmap_sem);
+	y_pages = get_user_pages(current, current->mm, y_dma.uaddr, y_dma.page_count, 0, 1, &dma->map[0], NULL);
+	uv_pages = get_user_pages(current, current->mm, uv_dma.uaddr, uv_dma.page_count, 0, 1, &dma->map[y_pages], NULL);
+	up_read(&current->mm->mmap_sem);
+
+	dma->page_count = y_dma.page_count + uv_dma.page_count;
+
+	if (y_pages + uv_pages != dma->page_count) {
+		IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n",
+				y_pages + uv_pages, dma->page_count);
+
+		for (i = 0; i < dma->page_count; i++) {
+			put_page(dma->map[i]);
+		}
+		dma->page_count = 0;
+		return -EINVAL;
+	}
+
+	/* Fill & map SG List */
+	ivtv_udma_fill_sg_list (dma, &uv_dma, ivtv_udma_fill_sg_list (dma, &y_dma, 0));
+	dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
+
+	/* Fill SG Array with new values */
+	ivtv_udma_fill_sg_array (dma, y_buffer_offset, uv_buffer_offset, y_size);
+
+	/* If we've offset the y plane, ensure top area is blanked */
+	if (args->src.height + args->src.top < 512-16) {
+		if (itv->yuv_info.blanking_dmaptr) {
+			dma->SGarray[dma->SG_length].size = cpu_to_le32(720*16);
+			dma->SGarray[dma->SG_length].src = cpu_to_le32(itv->yuv_info.blanking_dmaptr);
+			dma->SGarray[dma->SG_length].dst = cpu_to_le32(IVTV_DEC_MEM_START + yuv_offset[frame]);
+			dma->SG_length++;
+		}
+	}
+
+	/* Tag SG Array with Interrupt Bit */
+	dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000);
+
+	ivtv_udma_sync_for_device(itv);
+	return 0;
+}
+
+/* We rely on a table held in the firmware - Quick check. */
+int ivtv_yuv_filter_check(struct ivtv *itv)
+{
+	int i, offset_y, offset_uv;
+
+	for (i=0, offset_y = 16, offset_uv = 4; i<16; i++, offset_y += 24, offset_uv += 12) {
+		if ((read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + offset_y) != i << 16) ||
+		    (read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + offset_uv) != i << 16)) {
+			IVTV_WARN ("YUV filter table not found in firmware.\n");
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static void ivtv_yuv_filter(struct ivtv *itv, int h_filter, int v_filter_1, int v_filter_2)
+{
+	int filter_index, filter_line;
+
+	/* If any filter is -1, then don't update it */
+	if (h_filter > -1) {
+		if (h_filter > 4) h_filter = 4;
+		filter_index = h_filter * 384;
+		filter_line = 0;
+		while (filter_line < 16) {
+			write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02804);
+			write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0281c);
+			filter_index += 4;
+			write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02808);
+			write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02820);
+			filter_index += 4;
+			write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0280c);
+			write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02824);
+			filter_index += 4;
+			write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02810);
+			write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02828);
+			filter_index += 4;
+			write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02814);
+			write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0282c);
+			filter_index += 8;
+			write_reg(0, 0x02818);
+			write_reg(0, 0x02830);
+			filter_line ++;
+		}
+		IVTV_DEBUG_YUV("h_filter -> %d\n",h_filter);
+	}
+
+	if (v_filter_1 > -1) {
+		if (v_filter_1 > 4) v_filter_1 = 4;
+		filter_index = v_filter_1 * 192;
+		filter_line = 0;
+		while (filter_line < 16) {
+			write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02900);
+			filter_index += 4;
+			write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02904);
+			filter_index += 8;
+			write_reg(0, 0x02908);
+			filter_line ++;
+		}
+		IVTV_DEBUG_YUV("v_filter_1 -> %d\n",v_filter_1);
+	}
+
+	if (v_filter_2 > -1) {
+		if (v_filter_2 > 4) v_filter_2 = 4;
+		filter_index = v_filter_2 * 192;
+		filter_line = 0;
+		while (filter_line < 16) {
+			write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x0290c);
+			filter_index += 4;
+			write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02910);
+			filter_index += 8;
+			write_reg(0, 0x02914);
+			filter_line ++;
+		}
+		IVTV_DEBUG_YUV("v_filter_2 -> %d\n",v_filter_2);
+	}
+}
+
+static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *window)
+{
+	u32 reg_2834, reg_2838, reg_283c;
+	u32 reg_2844, reg_2854, reg_285c;
+	u32 reg_2864, reg_2874, reg_2890;
+	u32 reg_2870, reg_2870_base, reg_2870_offset;
+	int x_cutoff;
+	int h_filter;
+	u32 master_width;
+
+	IVTV_DEBUG_WARN( "Need to adjust to width %d src_w %d dst_w %d src_x %d dst_x %d\n",
+			 window->tru_w, window->src_w, window->dst_w,window->src_x, window->dst_x);
+
+	/* How wide is the src image */
+	x_cutoff  = window->src_w + window->src_x;
+
+	/* Set the display width */
+	reg_2834 = window->dst_w;
+	reg_2838 = reg_2834;
+
+	/* Set the display position */
+	reg_2890 = window->dst_x;
+
+	/* Index into the image horizontally */
+	reg_2870 = 0;
+
+	/* 2870 is normally fudged to align video coords with osd coords.
+	   If running full screen, it causes an unwanted left shift
+	   Remove the fudge if we almost fill the screen.
+	   Gradually adjust the offset to avoid the video 'snapping'
+	   left/right if it gets dragged through this region.
+	   Only do this if osd is full width. */
+	if (window->vis_w == 720) {
+		if ((window->tru_x - window->pan_x > -1) && (window->tru_x - window->pan_x <= 40) && (window->dst_w >= 680)){
+			reg_2870 = 10 - (window->tru_x - window->pan_x) / 4;
+		}
+		else if ((window->tru_x - window->pan_x < 0) && (window->tru_x - window->pan_x >= -20) && (window->dst_w >= 660)) {
+			reg_2870 = (10 + (window->tru_x - window->pan_x) / 2);
+		}
+
+		if (window->dst_w >= window->src_w)
+			reg_2870 = reg_2870 << 16 | reg_2870;
+		else
+			reg_2870 = ((reg_2870 & ~1) << 15) | (reg_2870 & ~1);
+	}
+
+	if (window->dst_w < window->src_w)
+		reg_2870 = 0x000d000e - reg_2870;
+	else
+		reg_2870 = 0x0012000e - reg_2870;
+
+	/* We're also using 2870 to shift the image left (src_x & negative dst_x) */
+	reg_2870_offset = (window->src_x*((window->dst_w << 21)/window->src_w))>>19;
+
+	if (window->dst_w >= window->src_w) {
+		x_cutoff &= ~1;
+		master_width = (window->src_w * 0x00200000) / (window->dst_w);
+		if (master_width * window->dst_w != window->src_w * 0x00200000) master_width ++;
+		reg_2834 = (reg_2834 << 16) | x_cutoff;
+		reg_2838 = (reg_2838 << 16) | x_cutoff;
+		reg_283c = master_width >> 2;
+		reg_2844 = master_width >> 2;
+		reg_2854 = master_width;
+		reg_285c = master_width >> 1;
+		reg_2864 = master_width >> 1;
+
+		/* We also need to factor in the scaling
+		   (src_w - dst_w) / (src_w / 4) */
+		if (window->dst_w > window->src_w)
+			reg_2870_base = ((window->dst_w - window->src_w)<<16) / (window->src_w <<14);
+		else
+			reg_2870_base = 0;
+
+		reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 2) + (reg_2870_base << 17 | reg_2870_base);
+		reg_2874 = 0;
+	}
+	else if (window->dst_w < window->src_w / 2) {
+		master_width = (window->src_w * 0x00080000) / window->dst_w;
+		if (master_width * window->dst_w != window->src_w * 0x00080000) master_width ++;
+		reg_2834 = (reg_2834 << 16) | x_cutoff;
+		reg_2838 = (reg_2838 << 16) | x_cutoff;
+		reg_283c = master_width >> 2;
+		reg_2844 = master_width >> 1;
+		reg_2854 = master_width;
+		reg_285c = master_width >> 1;
+		reg_2864 = master_width >> 1;
+		reg_2870 += (((reg_2870_offset << 15) & 0xFFFF0000) | reg_2870_offset);
+		reg_2870 += (5 - (((window->src_w + window->src_w / 2) - 1) / window->dst_w)) << 16;
+		reg_2874 = 0x00000012;
+	}
+	else {
+		master_width = (window->src_w * 0x00100000) / window->dst_w;
+		if (master_width * window->dst_w != window->src_w * 0x00100000) master_width ++;
+		reg_2834 = (reg_2834 << 16) | x_cutoff;
+		reg_2838 = (reg_2838 << 16) | x_cutoff;
+		reg_283c = master_width >> 2;
+		reg_2844 = master_width >> 1;
+		reg_2854 = master_width;
+		reg_285c = master_width >> 1;
+		reg_2864 = master_width >> 1;
+		reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 1);
+		reg_2870 += (5 - (((window->src_w * 3) - 1) / window->dst_w)) << 16;
+		reg_2874 = 0x00000001;
+	}
+
+	/* Select the horizontal filter */
+	if (window->src_w == window->dst_w) {
+		/* An exact size match uses filter 0 */
+		h_filter = 0;
+	}
+	else {
+		/* Figure out which filter to use */
+		h_filter = ((window->src_w << 16) / window->dst_w) >> 15;
+		h_filter = (h_filter >> 1) + (h_filter & 1);
+		/* Only an exact size match can use filter 0 */
+		if (h_filter == 0) h_filter = 1;
+	}
+
+	write_reg(reg_2834, 0x02834);
+	write_reg(reg_2838, 0x02838);
+	IVTV_DEBUG_YUV("Update reg 0x2834 %08x->%08x 0x2838 %08x->%08x\n",itv->yuv_info.reg_2834, reg_2834, itv->yuv_info.reg_2838, reg_2838);
+
+	write_reg(reg_283c, 0x0283c);
+	write_reg(reg_2844, 0x02844);
+
+	IVTV_DEBUG_YUV("Update reg 0x283c %08x->%08x 0x2844 %08x->%08x\n",itv->yuv_info.reg_283c, reg_283c, itv->yuv_info.reg_2844, reg_2844);
+
+	write_reg(0x00080514, 0x02840);
+	write_reg(0x00100514, 0x02848);
+	IVTV_DEBUG_YUV("Update reg 0x2840 %08x->%08x 0x2848 %08x->%08x\n",itv->yuv_info.reg_2840, 0x00080514, itv->yuv_info.reg_2848, 0x00100514);
+
+	write_reg(reg_2854, 0x02854);
+	IVTV_DEBUG_YUV("Update reg 0x2854 %08x->%08x \n",itv->yuv_info.reg_2854, reg_2854);
+
+	write_reg(reg_285c, 0x0285c);
+	write_reg(reg_2864, 0x02864);
+	IVTV_DEBUG_YUV("Update reg 0x285c %08x->%08x 0x2864 %08x->%08x\n",itv->yuv_info.reg_285c, reg_285c, itv->yuv_info.reg_2864, reg_2864);
+
+	write_reg(reg_2874, 0x02874);
+	IVTV_DEBUG_YUV("Update reg 0x2874 %08x->%08x\n",itv->yuv_info.reg_2874, reg_2874);
+
+	write_reg(reg_2870, 0x02870);
+	IVTV_DEBUG_YUV("Update reg 0x2870 %08x->%08x\n",itv->yuv_info.reg_2870, reg_2870);
+
+	write_reg( reg_2890,0x02890);
+	IVTV_DEBUG_YUV("Update reg 0x2890 %08x->%08x\n",itv->yuv_info.reg_2890, reg_2890);
+
+	/* Only update the filter if we really need to */
+	if (h_filter != itv->yuv_info.h_filter) {
+		ivtv_yuv_filter (itv,h_filter,-1,-1);
+		itv->yuv_info.h_filter = h_filter;
+	}
+}
+
+static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *window)
+{
+	u32 master_height;
+	u32 reg_2918, reg_291c, reg_2920, reg_2928;
+	u32 reg_2930, reg_2934, reg_293c;
+	u32 reg_2940, reg_2944, reg_294c;
+	u32 reg_2950, reg_2954, reg_2958, reg_295c;
+	u32 reg_2960, reg_2964, reg_2968, reg_296c;
+	u32 reg_289c;
+	u32 src_y_major_y, src_y_minor_y;
+	u32 src_y_major_uv, src_y_minor_uv;
+	u32 reg_2964_base, reg_2968_base;
+	int v_filter_1, v_filter_2;
+
+	IVTV_DEBUG_WARN("Need to adjust to height %d src_h %d dst_h %d src_y %d dst_y %d\n",
+		window->tru_h, window->src_h, window->dst_h,window->src_y, window->dst_y);
+
+	/* What scaling mode is being used... */
+	if (window->interlaced_y) {
+		IVTV_DEBUG_YUV("Scaling mode Y: Interlaced\n");
+	}
+	else {
+		IVTV_DEBUG_YUV("Scaling mode Y: Progressive\n");
+	}
+
+	if (window->interlaced_uv) {
+		IVTV_DEBUG_YUV("Scaling mode UV: Interlaced\n");
+	}
+	else {
+		IVTV_DEBUG_YUV("Scaling mode UV: Progressive\n");
+	}
+
+	/* What is the source video being treated as... */
+	if (itv->yuv_info.frame_interlaced) {
+		IVTV_DEBUG_WARN("Source video: Interlaced\n");
+	}
+	else {
+		IVTV_DEBUG_WARN("Source video: Non-interlaced\n");
+	}
+
+	/* We offset into the image using two different index methods, so split
+	   the y source coord into two parts. */
+	if (window->src_y < 8) {
+		src_y_minor_uv = window->src_y;
+		src_y_major_uv = 0;
+	}
+	else {
+		src_y_minor_uv = 8;
+		src_y_major_uv = window->src_y - 8;
+	}
+
+	src_y_minor_y = src_y_minor_uv;
+	src_y_major_y = src_y_major_uv;
+
+	if (window->offset_y) src_y_minor_y += 16;
+
+	if (window->interlaced_y)
+		reg_2918 = (window->dst_h << 16) | (window->src_h + src_y_minor_y);
+	else
+		reg_2918 = (window->dst_h << 16) | ((window->src_h + src_y_minor_y) << 1);
+
+	if (window->interlaced_uv)
+		reg_291c = (window->dst_h << 16) | ((window->src_h + src_y_minor_uv) >> 1);
+	else
+		reg_291c = (window->dst_h << 16) | (window->src_h + src_y_minor_uv);
+
+	reg_2964_base = (src_y_minor_y * ((window->dst_h << 16)/window->src_h)) >> 14;
+	reg_2968_base = (src_y_minor_uv * ((window->dst_h << 16)/window->src_h)) >> 14;
+
+	if (window->dst_h / 2 >= window->src_h && !window->interlaced_y) {
+		master_height = (window->src_h * 0x00400000) / window->dst_h;
+		if ((window->src_h * 0x00400000) - (master_height * window->dst_h) >= window->dst_h / 2) master_height ++;
+		reg_2920 = master_height >> 2;
+		reg_2928 = master_height >> 3;
+		reg_2930 = master_height;
+		reg_2940 = master_height >> 1;
+		reg_2964_base >>= 3;
+		reg_2968_base >>= 3;
+		reg_296c = 0x00000000;
+	}
+	else if (window->dst_h >= window->src_h) {
+		master_height = (window->src_h * 0x00400000) / window->dst_h;
+		master_height = (master_height >> 1) + (master_height & 1);
+		reg_2920 = master_height >> 2;
+		reg_2928 = master_height >> 2;
+		reg_2930 = master_height;
+		reg_2940 = master_height >> 1;
+		reg_296c = 0x00000000;
+		if (window->interlaced_y) {
+			reg_2964_base >>= 3;
+		}
+		else {
+			reg_296c ++;
+			reg_2964_base >>= 2;
+		}
+		if (window->interlaced_uv) reg_2928 >>= 1;
+		reg_2968_base >>= 3;
+	}
+	else if (window->dst_h >= window->src_h / 2) {
+		master_height = (window->src_h * 0x00200000) / window->dst_h;
+		master_height = (master_height >> 1) + (master_height & 1);
+		reg_2920 = master_height >> 2;
+		reg_2928 = master_height >> 2;
+		reg_2930 = master_height;
+		reg_2940 = master_height;
+		reg_296c = 0x00000101;
+		if (window->interlaced_y) {
+			reg_2964_base >>= 2;
+		}
+		else {
+			reg_296c ++;
+			reg_2964_base >>= 1;
+		}
+		if (window->interlaced_uv) reg_2928 >>= 1;
+		reg_2968_base >>= 2;
+	}
+	else {
+		master_height = (window->src_h * 0x00100000) / window->dst_h;
+		master_height = (master_height >> 1) + (master_height & 1);
+		reg_2920 = master_height >> 2;
+		reg_2928 = master_height >> 2;
+		reg_2930 = master_height;
+		reg_2940 = master_height;
+		reg_2964_base >>= 1;
+		reg_2968_base >>= 2;
+		reg_296c = 0x00000102;
+	}
+
+	/* FIXME These registers change depending on scaled / unscaled output
+	   We really need to work out what they should be */
+	if (window->src_h == window->dst_h){
+		reg_2934 = 0x00020000;
+		reg_293c = 0x00100000;
+		reg_2944 = 0x00040000;
+		reg_294c = 0x000b0000;
+	}
+	else {
+		reg_2934 = 0x00000FF0;
+		reg_293c = 0x00000FF0;
+		reg_2944 = 0x00000FF0;
+		reg_294c = 0x00000FF0;
+	}
+
+	/* The first line to be displayed */
+	reg_2950 = 0x00010000 + src_y_major_y;
+	if (window->interlaced_y) reg_2950 += 0x00010000;
+	reg_2954 = reg_2950 + 1;
+
+	reg_2958 = 0x00010000 + (src_y_major_y >> 1);
+	if (window->interlaced_uv) reg_2958 += 0x00010000;
+	reg_295c = reg_2958 + 1;
+
+	if (itv->yuv_info.decode_height == 480)
+		reg_289c = 0x011e0017;
+	else
+		reg_289c = 0x01500017;
+
+	if (window->dst_y < 0)
+		reg_289c = (reg_289c - ((window->dst_y & ~1)<<15))-(window->dst_y >>1);
+	else
+		reg_289c = (reg_289c + ((window->dst_y & ~1)<<15))+(window->dst_y >>1);
+
+	/* How much of the source to decode.
+	   Take into account the source offset */
+	reg_2960 = ((src_y_minor_y + window->src_h + src_y_major_y) - 1 ) |
+			((((src_y_minor_uv + window->src_h + src_y_major_uv) - 1) & ~1) << 15);
+
+	/* Calculate correct value for register 2964 */
+	if (window->src_h == window->dst_h)
+		reg_2964 = 1;
+	else {
+		reg_2964 = 2 + ((window->dst_h << 1) / window->src_h);
+		reg_2964 = (reg_2964 >> 1) + (reg_2964 & 1);
+	}
+	reg_2968 = (reg_2964 << 16) + reg_2964 + (reg_2964 >> 1);
+	reg_2964 = (reg_2964 << 16) + reg_2964 + (reg_2964 * 46 / 94);
+
+	/* Okay, we've wasted time working out the correct value,
+	   but if we use it, it fouls the the window alignment.
+	   Fudge it to what we want... */
+	reg_2964 = 0x00010001 + ((reg_2964 & 0x0000FFFF) - (reg_2964 >> 16));
+	reg_2968 = 0x00010001 + ((reg_2968 & 0x0000FFFF) - (reg_2968 >> 16));
+
+	/* Deviate further from what it should be. I find the flicker headache
+	   inducing so try to reduce it slightly. Leave 2968 as-is otherwise
+	   colours foul. */
+	if ((reg_2964 != 0x00010001) && (window->dst_h / 2 <= window->src_h))
+		reg_2964 = (reg_2964 & 0xFFFF0000) + ((reg_2964 & 0x0000FFFF)/2);
+
+	if (!window->interlaced_y) reg_2964 -= 0x00010001;
+	if (!window->interlaced_uv) reg_2968 -= 0x00010001;
+
+	reg_2964 += ((reg_2964_base << 16) | reg_2964_base);
+	reg_2968 += ((reg_2968_base << 16) | reg_2968_base);
+
+	/* Select the vertical filter */
+	if (window->src_h == window->dst_h) {
+		/* An exact size match uses filter 0/1 */
+		v_filter_1 = 0;
+		v_filter_2 = 1;
+	}
+	else {
+		/* Figure out which filter to use */
+		v_filter_1 = ((window->src_h << 16) / window->dst_h) >> 15;
+		v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1);
+		/* Only an exact size match can use filter 0 */
+		if (v_filter_1 == 0) v_filter_1 = 1;
+		v_filter_2 = v_filter_1;
+	}
+
+	write_reg(reg_2934, 0x02934);
+	write_reg(reg_293c, 0x0293c);
+	IVTV_DEBUG_YUV("Update reg 0x2934 %08x->%08x 0x293c %08x->%08x\n",itv->yuv_info.reg_2934, reg_2934, itv->yuv_info.reg_293c, reg_293c);
+	write_reg(reg_2944, 0x02944);
+	write_reg(reg_294c, 0x0294c);
+	IVTV_DEBUG_YUV("Update reg 0x2944 %08x->%08x 0x294c %08x->%08x\n",itv->yuv_info.reg_2944, reg_2944, itv->yuv_info.reg_294c, reg_294c);
+
+	/* Ensure 2970 is 0 (does it ever change ?) */
+/*	write_reg(0,0x02970); */
+/*	IVTV_DEBUG_YUV("Update reg 0x2970 %08x->%08x\n",itv->yuv_info.reg_2970, 0); */
+
+	write_reg(reg_2930, 0x02938);
+	write_reg(reg_2930, 0x02930);
+	IVTV_DEBUG_YUV("Update reg 0x2930 %08x->%08x 0x2938 %08x->%08x\n",itv->yuv_info.reg_2930, reg_2930, itv->yuv_info.reg_2938, reg_2930);
+
+	write_reg(reg_2928, 0x02928);
+	write_reg(reg_2928+0x514, 0x0292C);
+	IVTV_DEBUG_YUV("Update reg 0x2928 %08x->%08x 0x292c %08x->%08x\n",itv->yuv_info.reg_2928, reg_2928, itv->yuv_info.reg_292c, reg_2928+0x514);
+
+	write_reg(reg_2920, 0x02920);
+	write_reg(reg_2920+0x514, 0x02924);
+	IVTV_DEBUG_YUV("Update reg 0x2920 %08x->%08x 0x2924 %08x->%08x\n",itv->yuv_info.reg_2920, reg_2920, itv->yuv_info.reg_2924, 0x514+reg_2920);
+
+	write_reg (reg_2918,0x02918);
+	write_reg (reg_291c,0x0291C);
+	IVTV_DEBUG_YUV("Update reg 0x2918 %08x->%08x 0x291C %08x->%08x\n",itv->yuv_info.reg_2918,reg_2918,itv->yuv_info.reg_291c,reg_291c);
+
+	write_reg(reg_296c, 0x0296c);
+	IVTV_DEBUG_YUV("Update reg 0x296c %08x->%08x\n",itv->yuv_info.reg_296c, reg_296c);
+
+	write_reg(reg_2940, 0x02948);
+	write_reg(reg_2940, 0x02940);
+	IVTV_DEBUG_YUV("Update reg 0x2940 %08x->%08x 0x2948 %08x->%08x\n",itv->yuv_info.reg_2940, reg_2940, itv->yuv_info.reg_2948, reg_2940);
+
+	write_reg(reg_2950, 0x02950);
+	write_reg(reg_2954, 0x02954);
+	IVTV_DEBUG_YUV("Update reg 0x2950 %08x->%08x 0x2954 %08x->%08x\n",itv->yuv_info.reg_2950, reg_2950, itv->yuv_info.reg_2954, reg_2954);
+
+	write_reg(reg_2958, 0x02958);
+	write_reg(reg_295c, 0x0295C);
+	IVTV_DEBUG_YUV("Update reg 0x2958 %08x->%08x 0x295C %08x->%08x\n",itv->yuv_info.reg_2958, reg_2958, itv->yuv_info.reg_295c, reg_295c);
+
+	write_reg(reg_2960, 0x02960);
+	IVTV_DEBUG_YUV("Update reg 0x2960 %08x->%08x \n",itv->yuv_info.reg_2960, reg_2960);
+
+	write_reg(reg_2964, 0x02964);
+	write_reg(reg_2968, 0x02968);
+	IVTV_DEBUG_YUV("Update reg 0x2964 %08x->%08x 0x2968 %08x->%08x\n",itv->yuv_info.reg_2964, reg_2964, itv->yuv_info.reg_2968, reg_2968);
+
+	write_reg( reg_289c,0x0289c);
+	IVTV_DEBUG_YUV("Update reg 0x289c %08x->%08x\n",itv->yuv_info.reg_289c, reg_289c);
+
+	/* Only update filter 1 if we really need to */
+	if (v_filter_1 != itv->yuv_info.v_filter_1) {
+		ivtv_yuv_filter (itv,-1,v_filter_1,-1);
+		itv->yuv_info.v_filter_1 = v_filter_1;
+	}
+
+	/* Only update filter 2 if we really need to */
+	if (v_filter_2 != itv->yuv_info.v_filter_2) {
+		ivtv_yuv_filter (itv,-1,-1,v_filter_2);
+		itv->yuv_info.v_filter_2 = v_filter_2;
+	}
+
+	itv->yuv_info.frame_interlaced_last = itv->yuv_info.frame_interlaced;
+}
+
+/* Modify the supplied coordinate information to fit the visible osd area */
+static u32 ivtv_yuv_window_setup (struct ivtv *itv, struct yuv_frame_info *window)
+{
+	int osd_crop, lace_threshold;
+	u32 osd_scale;
+	u32 yuv_update = 0;
+
+	lace_threshold = itv->yuv_info.lace_threshold;
+	if (lace_threshold < 0)
+		lace_threshold = itv->yuv_info.decode_height - 1;
+
+	/* Work out the lace settings */
+	switch (itv->yuv_info.lace_mode) {
+		case IVTV_YUV_MODE_PROGRESSIVE: /* Progressive mode */
+			itv->yuv_info.frame_interlaced = 0;
+			if (window->tru_h < 512 || (window->tru_h > 576 && window->tru_h < 1021))
+				window->interlaced_y = 0;
+			else
+				window->interlaced_y = 1;
+
+			if (window->tru_h < 1021 && (window->dst_h >= window->src_h /2))
+				window->interlaced_uv = 0;
+			else
+				window->interlaced_uv = 1;
+			break;
+
+		case IVTV_YUV_MODE_AUTO:
+			if (window->tru_h <= lace_threshold || window->tru_h > 576 || window->tru_w > 720){
+				itv->yuv_info.frame_interlaced = 0;
+				if ((window->tru_h < 512) ||
+				  (window->tru_h > 576 && window->tru_h < 1021) ||
+				  (window->tru_w > 720 && window->tru_h < 1021))
+					window->interlaced_y = 0;
+				else
+					window->interlaced_y = 1;
+
+				if (window->tru_h < 1021 && (window->dst_h >= window->src_h /2))
+					window->interlaced_uv = 0;
+				else
+					window->interlaced_uv = 1;
+			}
+			else {
+				itv->yuv_info.frame_interlaced = 1;
+				window->interlaced_y = 1;
+				window->interlaced_uv = 1;
+			}
+			break;
+
+			case IVTV_YUV_MODE_INTERLACED: /* Interlace mode */
+		default:
+			itv->yuv_info.frame_interlaced = 1;
+			window->interlaced_y = 1;
+			window->interlaced_uv = 1;
+			break;
+	}
+
+	/* Sorry, but no negative coords for src */
+	if (window->src_x < 0) window->src_x = 0;
+	if (window->src_y < 0) window->src_y = 0;
+
+	/* Can only reduce width down to 1/4 original size */
+	if ((osd_crop = window->src_w - ( 4 * window->dst_w )) > 0) {
+		window->src_x += osd_crop / 2;
+		window->src_w = (window->src_w - osd_crop) & ~3;
+		window->dst_w = window->src_w / 4;
+		window->dst_w += window->dst_w & 1;
+	}
+
+	/* Can only reduce height down to 1/4 original size */
+	if (window->src_h / window->dst_h >= 2) {
+		/* Overflow may be because we're running progressive, so force mode switch */
+		window->interlaced_y = 1;
+		/* Make sure we're still within limits for interlace */
+		if ((osd_crop = window->src_h - ( 4 * window->dst_h )) > 0) {
+			/* If we reach here we'll have to force the height. */
+			window->src_y += osd_crop / 2;
+			window->src_h = (window->src_h - osd_crop) & ~3;
+			window->dst_h = window->src_h / 4;
+			window->dst_h += window->dst_h & 1;
+		}
+	}
+
+	/* If there's nothing to safe to display, we may as well stop now */
+	if ((int)window->dst_w <= 2 || (int)window->dst_h <= 2 || (int)window->src_w <= 2 || (int)window->src_h <= 2) {
+		return 0;
+	}
+
+	/* Ensure video remains inside OSD area */
+	osd_scale = (window->src_h << 16) / window->dst_h;
+
+	if ((osd_crop = window->pan_y - window->dst_y) > 0) {
+		/* Falls off the upper edge - crop */
+		window->src_y += (osd_scale * osd_crop) >> 16;
+		window->src_h -= (osd_scale * osd_crop) >> 16;
+		window->dst_h -= osd_crop;
+		window->dst_y = 0;
+	}
+	else {
+		window->dst_y -= window->pan_y;
+	}
+
+	if ((osd_crop = window->dst_h + window->dst_y - window->vis_h) > 0) {
+		/* Falls off the lower edge - crop */
+		window->dst_h -= osd_crop;
+		window->src_h -= (osd_scale * osd_crop) >> 16;
+	}
+
+	osd_scale = (window->src_w << 16) / window->dst_w;
+
+	if ((osd_crop = window->pan_x - window->dst_x) > 0) {
+		/* Fall off the left edge - crop */
+		window->src_x += (osd_scale * osd_crop) >> 16;
+		window->src_w -= (osd_scale * osd_crop) >> 16;
+		window->dst_w -= osd_crop;
+		window->dst_x = 0;
+	}
+	else {
+		window->dst_x -= window->pan_x;
+	}
+
+	if ((osd_crop = window->dst_w + window->dst_x - window->vis_w) > 0) {
+		/* Falls off the right edge - crop */
+		window->dst_w -= osd_crop;
+		window->src_w -= (osd_scale * osd_crop) >> 16;
+	}
+
+	/* The OSD can be moved. Track to it */
+	window->dst_x += itv->yuv_info.osd_x_offset;
+	window->dst_y += itv->yuv_info.osd_y_offset;
+
+	/* Width & height for both src & dst must be even.
+	   Same for coordinates. */
+	window->dst_w &= ~1;
+	window->dst_x &= ~1;
+
+	window->src_w += window->src_x & 1;
+	window->src_x &= ~1;
+
+	window->src_w &= ~1;
+	window->dst_w &= ~1;
+
+	window->dst_h &= ~1;
+	window->dst_y &= ~1;
+
+	window->src_h += window->src_y & 1;
+	window->src_y &= ~1;
+
+	window->src_h &= ~1;
+	window->dst_h &= ~1;
+
+	/* Due to rounding, we may have reduced the output size to <1/4 of the source
+	   Check again, but this time just resize. Don't change source coordinates */
+	if (window->dst_w < window->src_w / 4) {
+		window->src_w &= ~3;
+		window->dst_w = window->src_w / 4;
+		window->dst_w += window->dst_w & 1;
+	}
+	if (window->dst_h < window->src_h / 4) {
+		window->src_h &= ~3;
+		window->dst_h = window->src_h / 4;
+		window->dst_h += window->dst_h & 1;
+	}
+
+	/* Check again. If there's nothing to safe to display, stop now */
+	if ((int)window->dst_w <= 2 || (int)window->dst_h <= 2 || (int)window->src_w <= 2 || (int)window->src_h <= 2) {
+		return 0;
+	}
+
+	/* Both x offset & width are linked, so they have to be done together */
+	if ((itv->yuv_info.old_frame_info.dst_w != window->dst_w) ||
+	    (itv->yuv_info.old_frame_info.src_w != window->src_w) ||
+	    (itv->yuv_info.old_frame_info.dst_x != window->dst_x) ||
+	    (itv->yuv_info.old_frame_info.src_x != window->src_x) ||
+	    (itv->yuv_info.old_frame_info.pan_x != window->pan_x) ||
+	    (itv->yuv_info.old_frame_info.vis_w != window->vis_w)) {
+		yuv_update |= IVTV_YUV_UPDATE_HORIZONTAL;
+	}
+
+	if ((itv->yuv_info.old_frame_info.src_h != window->src_h) ||
+	    (itv->yuv_info.old_frame_info.dst_h != window->dst_h) ||
+	    (itv->yuv_info.old_frame_info.dst_y != window->dst_y) ||
+	    (itv->yuv_info.old_frame_info.src_y != window->src_y) ||
+	    (itv->yuv_info.old_frame_info.pan_y != window->pan_y) ||
+	    (itv->yuv_info.old_frame_info.vis_h != window->vis_h) ||
+	    (itv->yuv_info.old_frame_info.interlaced_y != window->interlaced_y) ||
+	    (itv->yuv_info.old_frame_info.interlaced_uv != window->interlaced_uv)) {
+		yuv_update |= IVTV_YUV_UPDATE_VERTICAL;
+	}
+
+	return yuv_update;
+}
+
+/* Update the scaling register to the requested value */
+void ivtv_yuv_work_handler (struct ivtv *itv)
+{
+	struct yuv_frame_info window;
+	u32 yuv_update;
+
+	int frame = itv->yuv_info.update_frame;
+
+/*	IVTV_DEBUG_YUV("Update yuv registers for frame %d\n",frame); */
+	memcpy(&window, &itv->yuv_info.new_frame_info[frame], sizeof (window));
+
+	/* Update the osd pan info */
+	window.pan_x = itv->yuv_info.osd_x_pan;
+	window.pan_y = itv->yuv_info.osd_y_pan;
+	window.vis_w = itv->yuv_info.osd_vis_w;
+	window.vis_h = itv->yuv_info.osd_vis_h;
+
+	/* Calculate the display window coordinates. Exit if nothing left */
+	if (!(yuv_update = ivtv_yuv_window_setup (itv, &window)))
+		return;
+
+	/* Update horizontal settings */
+	if (yuv_update & IVTV_YUV_UPDATE_HORIZONTAL)
+		ivtv_yuv_handle_horizontal(itv, &window);
+
+	if (yuv_update & IVTV_YUV_UPDATE_VERTICAL)
+		ivtv_yuv_handle_vertical(itv, &window);
+
+	memcpy(&itv->yuv_info.old_frame_info, &window, sizeof (itv->yuv_info.old_frame_info));
+}
+
+static void ivtv_yuv_init (struct ivtv *itv)
+{
+	IVTV_DEBUG_YUV("ivtv_yuv_init\n");
+
+	/* Take a snapshot of the current register settings */
+	itv->yuv_info.reg_2834 = read_reg(0x02834);
+	itv->yuv_info.reg_2838 = read_reg(0x02838);
+	itv->yuv_info.reg_283c = read_reg(0x0283c);
+	itv->yuv_info.reg_2840 = read_reg(0x02840);
+	itv->yuv_info.reg_2844 = read_reg(0x02844);
+	itv->yuv_info.reg_2848 = read_reg(0x02848);
+	itv->yuv_info.reg_2854 = read_reg(0x02854);
+	itv->yuv_info.reg_285c = read_reg(0x0285c);
+	itv->yuv_info.reg_2864 = read_reg(0x02864);
+	itv->yuv_info.reg_2870 = read_reg(0x02870);
+	itv->yuv_info.reg_2874 = read_reg(0x02874);
+	itv->yuv_info.reg_2898 = read_reg(0x02898);
+	itv->yuv_info.reg_2890 = read_reg(0x02890);
+
+	itv->yuv_info.reg_289c = read_reg(0x0289c);
+	itv->yuv_info.reg_2918 = read_reg(0x02918);
+	itv->yuv_info.reg_291c = read_reg(0x0291c);
+	itv->yuv_info.reg_2920 = read_reg(0x02920);
+	itv->yuv_info.reg_2924 = read_reg(0x02924);
+	itv->yuv_info.reg_2928 = read_reg(0x02928);
+	itv->yuv_info.reg_292c = read_reg(0x0292c);
+	itv->yuv_info.reg_2930 = read_reg(0x02930);
+	itv->yuv_info.reg_2934 = read_reg(0x02934);
+	itv->yuv_info.reg_2938 = read_reg(0x02938);
+	itv->yuv_info.reg_293c = read_reg(0x0293c);
+	itv->yuv_info.reg_2940 = read_reg(0x02940);
+	itv->yuv_info.reg_2944 = read_reg(0x02944);
+	itv->yuv_info.reg_2948 = read_reg(0x02948);
+	itv->yuv_info.reg_294c = read_reg(0x0294c);
+	itv->yuv_info.reg_2950 = read_reg(0x02950);
+	itv->yuv_info.reg_2954 = read_reg(0x02954);
+	itv->yuv_info.reg_2958 = read_reg(0x02958);
+	itv->yuv_info.reg_295c = read_reg(0x0295c);
+	itv->yuv_info.reg_2960 = read_reg(0x02960);
+	itv->yuv_info.reg_2964 = read_reg(0x02964);
+	itv->yuv_info.reg_2968 = read_reg(0x02968);
+	itv->yuv_info.reg_296c = read_reg(0x0296c);
+	itv->yuv_info.reg_2970 = read_reg(0x02970);
+
+	itv->yuv_info.v_filter_1 = -1;
+	itv->yuv_info.v_filter_2 = -1;
+	itv->yuv_info.h_filter = -1;
+
+	/* Set some valid size info */
+	itv->yuv_info.osd_x_offset = read_reg(0x02a04) & 0x00000FFF;
+	itv->yuv_info.osd_y_offset = (read_reg(0x02a04) >> 16) & 0x00000FFF;
+
+	/* Bit 2 of reg 2878 indicates current decoder output format
+	   0 : NTSC    1 : PAL */
+	if (read_reg(0x2878) & 4)
+		itv->yuv_info.decode_height = 576;
+	else
+		itv->yuv_info.decode_height = 480;
+
+	/* If no visible size set, assume full size */
+	if (!itv->yuv_info.osd_vis_w) itv->yuv_info.osd_vis_w = 720 - itv->yuv_info.osd_x_offset;
+	if (!itv->yuv_info.osd_vis_h) itv->yuv_info.osd_vis_h = itv->yuv_info.decode_height - itv->yuv_info.osd_y_offset;
+
+	/* We need a buffer for blanking when Y plane is offset - non-fatal if we can't get one */
+	itv->yuv_info.blanking_ptr = kzalloc(720*16,GFP_KERNEL);
+	if (itv->yuv_info.blanking_ptr) {
+		itv->yuv_info.blanking_dmaptr = pci_map_single(itv->dev, itv->yuv_info.blanking_ptr, 720*16, PCI_DMA_TODEVICE);
+	}
+	else {
+		itv->yuv_info.blanking_dmaptr = 0;
+		IVTV_DEBUG_WARN ("Failed to allocate yuv blanking buffer\n");
+	}
+
+	IVTV_DEBUG_WARN("Enable video output\n");
+	write_reg_sync(0x00108080, 0x2898);
+
+	/* Enable YUV decoder output */
+	write_reg_sync(0x01, IVTV_REG_VDM);
+
+	set_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags);
+	atomic_set(&itv->yuv_info.next_dma_frame,0);
+}
+
+int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
+{
+	DEFINE_WAIT(wait);
+	int rc = 0;
+	int got_sig = 0;
+	int frame, next_fill_frame, last_fill_frame;
+
+	IVTV_DEBUG_INFO("yuv_prep_frame\n");
+
+	if (atomic_read(&itv->yuv_info.next_dma_frame) == -1) ivtv_yuv_init(itv);
+
+	frame = atomic_read(&itv->yuv_info.next_fill_frame);
+	next_fill_frame = (frame + 1) & 0x3;
+	last_fill_frame = (atomic_read(&itv->yuv_info.next_dma_frame)+1) & 0x3;
+
+	if (next_fill_frame != last_fill_frame && last_fill_frame != frame) {
+		/* Buffers are full - Overwrite the last frame */
+		next_fill_frame = frame;
+		frame = (frame - 1) & 3;
+	}
+
+	/* Take a snapshot of the yuv coordinate information */
+	itv->yuv_info.new_frame_info[frame].src_x = args->src.left;
+	itv->yuv_info.new_frame_info[frame].src_y = args->src.top;
+	itv->yuv_info.new_frame_info[frame].src_w = args->src.width;
+	itv->yuv_info.new_frame_info[frame].src_h = args->src.height;
+	itv->yuv_info.new_frame_info[frame].dst_x = args->dst.left;
+	itv->yuv_info.new_frame_info[frame].dst_y = args->dst.top;
+	itv->yuv_info.new_frame_info[frame].dst_w = args->dst.width;
+	itv->yuv_info.new_frame_info[frame].dst_h = args->dst.height;
+	itv->yuv_info.new_frame_info[frame].tru_x = args->dst.left;
+	itv->yuv_info.new_frame_info[frame].tru_w = args->src_width;
+	itv->yuv_info.new_frame_info[frame].tru_h = args->src_height;
+
+	/* Are we going to offset the Y plane */
+	if (args->src.height + args->src.top < 512-16)
+		itv->yuv_info.new_frame_info[frame].offset_y = 1;
+	else
+		itv->yuv_info.new_frame_info[frame].offset_y = 0;
+
+	/* Snapshot the osd pan info */
+	itv->yuv_info.new_frame_info[frame].pan_x = itv->yuv_info.osd_x_pan;
+	itv->yuv_info.new_frame_info[frame].pan_y = itv->yuv_info.osd_y_pan;
+	itv->yuv_info.new_frame_info[frame].vis_w = itv->yuv_info.osd_vis_w;
+	itv->yuv_info.new_frame_info[frame].vis_h = itv->yuv_info.osd_vis_h;
+
+	itv->yuv_info.new_frame_info[frame].update = 0;
+	itv->yuv_info.new_frame_info[frame].interlaced_y = 0;
+	itv->yuv_info.new_frame_info[frame].interlaced_uv = 0;
+
+	if (memcmp (&itv->yuv_info.old_frame_info_args, &itv->yuv_info.new_frame_info[frame],
+	    sizeof (itv->yuv_info.new_frame_info[frame]))) {
+		memcpy(&itv->yuv_info.old_frame_info_args, &itv->yuv_info.new_frame_info[frame], sizeof (itv->yuv_info.old_frame_info_args));
+		itv->yuv_info.new_frame_info[frame].update = 1;
+/*		IVTV_DEBUG_YUV ("Requesting register update for frame %d\n",frame); */
+	}
+
+	/* DMA the frame */
+	mutex_lock(&itv->udma.lock);
+
+	if ((rc = ivtv_yuv_prep_user_dma(itv, &itv->udma, args)) != 0) {
+		mutex_unlock(&itv->udma.lock);
+		return rc;
+	}
+
+	ivtv_udma_prepare(itv);
+	prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
+	/* if no UDMA is pending and no UDMA is in progress, then the DMA
+	is finished */
+	while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) {
+		/* don't interrupt if the DMA is in progress but break off
+		a still pending DMA. */
+		got_sig = signal_pending(current);
+		if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))
+			break;
+		got_sig = 0;
+		schedule();
+	}
+	finish_wait(&itv->dma_waitq, &wait);
+
+	/* Unmap Last DMA Xfer */
+	ivtv_udma_unmap(itv);
+
+	if (got_sig) {
+		IVTV_DEBUG_INFO("User stopped YUV UDMA\n");
+		mutex_unlock(&itv->udma.lock);
+		return -EINTR;
+	}
+
+	atomic_set(&itv->yuv_info.next_fill_frame, next_fill_frame);
+
+	mutex_unlock(&itv->udma.lock);
+	return rc;
+}
+
+void ivtv_yuv_close(struct ivtv *itv)
+{
+	int h_filter, v_filter_1, v_filter_2;
+
+	IVTV_DEBUG_YUV("ivtv_yuv_close\n");
+	ivtv_waitq(&itv->vsync_waitq);
+
+	atomic_set(&itv->yuv_info.next_dma_frame, -1);
+	atomic_set(&itv->yuv_info.next_fill_frame, 0);
+
+	/* Reset registers we have changed so mpeg playback works */
+
+	/* If we fully restore this register, the display may remain active.
+	   Restore, but set one bit to blank the video. Firmware will always
+	   clear this bit when needed, so not a problem. */
+	write_reg(itv->yuv_info.reg_2898 | 0x01000000, 0x2898);
+
+	write_reg(itv->yuv_info.reg_2834, 0x02834);
+	write_reg(itv->yuv_info.reg_2838, 0x02838);
+	write_reg(itv->yuv_info.reg_283c, 0x0283c);
+	write_reg(itv->yuv_info.reg_2840, 0x02840);
+	write_reg(itv->yuv_info.reg_2844, 0x02844);
+	write_reg(itv->yuv_info.reg_2848, 0x02848);
+	write_reg(itv->yuv_info.reg_2854, 0x02854);
+	write_reg(itv->yuv_info.reg_285c, 0x0285c);
+	write_reg(itv->yuv_info.reg_2864, 0x02864);
+	write_reg(itv->yuv_info.reg_2870, 0x02870);
+	write_reg(itv->yuv_info.reg_2874, 0x02874);
+	write_reg(itv->yuv_info.reg_2890, 0x02890);
+	write_reg(itv->yuv_info.reg_289c, 0x0289c);
+
+	write_reg(itv->yuv_info.reg_2918, 0x02918);
+	write_reg(itv->yuv_info.reg_291c, 0x0291c);
+	write_reg(itv->yuv_info.reg_2920, 0x02920);
+	write_reg(itv->yuv_info.reg_2924, 0x02924);
+	write_reg(itv->yuv_info.reg_2928, 0x02928);
+	write_reg(itv->yuv_info.reg_292c, 0x0292c);
+	write_reg(itv->yuv_info.reg_2930, 0x02930);
+	write_reg(itv->yuv_info.reg_2934, 0x02934);
+	write_reg(itv->yuv_info.reg_2938, 0x02938);
+	write_reg(itv->yuv_info.reg_293c, 0x0293c);
+	write_reg(itv->yuv_info.reg_2940, 0x02940);
+	write_reg(itv->yuv_info.reg_2944, 0x02944);
+	write_reg(itv->yuv_info.reg_2948, 0x02948);
+	write_reg(itv->yuv_info.reg_294c, 0x0294c);
+	write_reg(itv->yuv_info.reg_2950, 0x02950);
+	write_reg(itv->yuv_info.reg_2954, 0x02954);
+	write_reg(itv->yuv_info.reg_2958, 0x02958);
+	write_reg(itv->yuv_info.reg_295c, 0x0295c);
+	write_reg(itv->yuv_info.reg_2960, 0x02960);
+	write_reg(itv->yuv_info.reg_2964, 0x02964);
+	write_reg(itv->yuv_info.reg_2968, 0x02968);
+	write_reg(itv->yuv_info.reg_296c, 0x0296c);
+	write_reg(itv->yuv_info.reg_2970, 0x02970);
+
+	/* Prepare to restore filters */
+
+	/* First the horizontal filter */
+	if ((itv->yuv_info.reg_2834 & 0x0000FFFF) == (itv->yuv_info.reg_2834 >> 16)) {
+		/* An exact size match uses filter 0 */
+		h_filter = 0;
+	}
+	else {
+		/* Figure out which filter to use */
+		h_filter = ((itv->yuv_info.reg_2834 << 16) / (itv->yuv_info.reg_2834 >> 16)) >> 15;
+		h_filter = (h_filter >> 1) + (h_filter & 1);
+		/* Only an exact size match can use filter 0. */
+		if (h_filter < 1) h_filter = 1;
+	}
+
+	/* Now the vertical filter */
+	if ((itv->yuv_info.reg_2918 & 0x0000FFFF) == (itv->yuv_info.reg_2918 >> 16)) {
+		/* An exact size match uses filter 0/1 */
+		v_filter_1 = 0;
+		v_filter_2 = 1;
+	}
+	else {
+		/* Figure out which filter to use */
+		v_filter_1 = ((itv->yuv_info.reg_2918 << 16) / (itv->yuv_info.reg_2918 >> 16)) >> 15;
+		v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1);
+		/* Only an exact size match can use filter 0 */
+		if (v_filter_1 == 0) v_filter_1 = 1;
+		v_filter_2 = v_filter_1;
+	}
+
+	/* Now restore the filters */
+	ivtv_yuv_filter (itv,h_filter,v_filter_1,v_filter_2);
+
+	/* and clear a few registers */
+	write_reg(0, 0x02814);
+	write_reg(0, 0x0282c);
+	write_reg(0, 0x02904);
+	write_reg(0, 0x02910);
+
+	/* Release the blanking buffer */
+	if (itv->yuv_info.blanking_ptr) {
+		kfree (itv->yuv_info.blanking_ptr);
+		itv->yuv_info.blanking_ptr = NULL;
+		pci_unmap_single(itv->dev, itv->yuv_info.blanking_dmaptr, 720*16, PCI_DMA_TODEVICE);
+	}
+
+	/* Invalidate the old dimension information */
+	itv->yuv_info.old_frame_info.src_w = 0;
+	itv->yuv_info.old_frame_info.src_h = 0;
+	itv->yuv_info.old_frame_info_args.src_w = 0;
+	itv->yuv_info.old_frame_info_args.src_h = 0;
+
+	/* All done. */
+	clear_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags);
+}
+
diff --git a/drivers/media/video/ivtv/ivtv-yuv.h b/drivers/media/video/ivtv/ivtv-yuv.h
new file mode 100644
index 0000000..88972d3
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-yuv.h
@@ -0,0 +1,24 @@
+/*
+    yuv support
+
+    Copyright (C) 2007  Ian Armstrong <ian@iarmst.demon.co.uk>
+
+    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
+ */
+
+int ivtv_yuv_filter_check(struct ivtv *itv);
+int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args);
+void ivtv_yuv_close(struct ivtv *itv);
+void ivtv_yuv_work_handler (struct ivtv *itv);
diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c
index 98681da..664aba8 100644
--- a/drivers/media/video/meye.c
+++ b/drivers/media/video/meye.c
@@ -925,13 +925,13 @@
 		if (p->palette != VIDEO_PALETTE_YUV422 && p->palette != VIDEO_PALETTE_YUYV)
 			return -EINVAL;
 		mutex_lock(&meye.lock);
-		sonypi_camera_command(SONYPI_COMMAND_SETCAMERABRIGHTNESS,
+		sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERABRIGHTNESS,
 				      p->brightness >> 10);
-		sonypi_camera_command(SONYPI_COMMAND_SETCAMERAHUE,
+		sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAHUE,
 				      p->hue >> 10);
-		sonypi_camera_command(SONYPI_COMMAND_SETCAMERACOLOR,
+		sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACOLOR,
 				      p->colour >> 10);
-		sonypi_camera_command(SONYPI_COMMAND_SETCAMERACONTRAST,
+		sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACONTRAST,
 				      p->contrast >> 10);
 		meye.picture = *p;
 		mutex_unlock(&meye.lock);
@@ -1043,11 +1043,11 @@
 		    meye.params.quality != jp->quality)
 			mchip_hic_stop();	/* need restart */
 		meye.params = *jp;
-		sonypi_camera_command(SONYPI_COMMAND_SETCAMERASHARPNESS,
+		sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS,
 				      meye.params.sharpness);
-		sonypi_camera_command(SONYPI_COMMAND_SETCAMERAAGC,
+		sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC,
 				      meye.params.agc);
-		sonypi_camera_command(SONYPI_COMMAND_SETCAMERAPICTURE,
+		sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE,
 				      meye.params.picture);
 		mutex_unlock(&meye.lock);
 		break;
@@ -1287,38 +1287,38 @@
 		mutex_lock(&meye.lock);
 		switch (c->id) {
 		case V4L2_CID_BRIGHTNESS:
-			sonypi_camera_command(
-				SONYPI_COMMAND_SETCAMERABRIGHTNESS, c->value);
+			sony_pic_camera_command(
+				SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, c->value);
 			meye.picture.brightness = c->value << 10;
 			break;
 		case V4L2_CID_HUE:
-			sonypi_camera_command(
-				SONYPI_COMMAND_SETCAMERAHUE, c->value);
+			sony_pic_camera_command(
+				SONY_PIC_COMMAND_SETCAMERAHUE, c->value);
 			meye.picture.hue = c->value << 10;
 			break;
 		case V4L2_CID_CONTRAST:
-			sonypi_camera_command(
-				SONYPI_COMMAND_SETCAMERACONTRAST, c->value);
+			sony_pic_camera_command(
+				SONY_PIC_COMMAND_SETCAMERACONTRAST, c->value);
 			meye.picture.contrast = c->value << 10;
 			break;
 		case V4L2_CID_SATURATION:
-			sonypi_camera_command(
-				SONYPI_COMMAND_SETCAMERACOLOR, c->value);
+			sony_pic_camera_command(
+				SONY_PIC_COMMAND_SETCAMERACOLOR, c->value);
 			meye.picture.colour = c->value << 10;
 			break;
 		case V4L2_CID_AGC:
-			sonypi_camera_command(
-				SONYPI_COMMAND_SETCAMERAAGC, c->value);
+			sony_pic_camera_command(
+				SONY_PIC_COMMAND_SETCAMERAAGC, c->value);
 			meye.params.agc = c->value;
 			break;
 		case V4L2_CID_SHARPNESS:
-			sonypi_camera_command(
-				SONYPI_COMMAND_SETCAMERASHARPNESS, c->value);
+			sony_pic_camera_command(
+				SONY_PIC_COMMAND_SETCAMERASHARPNESS, c->value);
 			meye.params.sharpness = c->value;
 			break;
 		case V4L2_CID_PICTURE:
-			sonypi_camera_command(
-				SONYPI_COMMAND_SETCAMERAPICTURE, c->value);
+			sony_pic_camera_command(
+				SONY_PIC_COMMAND_SETCAMERAPICTURE, c->value);
 			meye.params.picture = c->value;
 			break;
 		case V4L2_CID_JPEGQUAL:
@@ -1848,7 +1848,7 @@
 	memcpy(meye.video_dev, &meye_template, sizeof(meye_template));
 	meye.video_dev->dev = &meye.mchip_dev->dev;
 
-	if ((ret = sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 1))) {
+	if ((ret = sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 1))) {
 		printk(KERN_ERR "meye: unable to power on the camera\n");
 		printk(KERN_ERR "meye: did you enable the camera in "
 				"sonypi using the module options ?\n");
@@ -1928,13 +1928,13 @@
 	meye.params.picture = 0;
 	meye.params.framerate = 0;
 
-	sonypi_camera_command(SONYPI_COMMAND_SETCAMERABRIGHTNESS, 32);
-	sonypi_camera_command(SONYPI_COMMAND_SETCAMERAHUE, 32);
-	sonypi_camera_command(SONYPI_COMMAND_SETCAMERACOLOR, 32);
-	sonypi_camera_command(SONYPI_COMMAND_SETCAMERACONTRAST, 32);
-	sonypi_camera_command(SONYPI_COMMAND_SETCAMERASHARPNESS, 32);
-	sonypi_camera_command(SONYPI_COMMAND_SETCAMERAPICTURE, 0);
-	sonypi_camera_command(SONYPI_COMMAND_SETCAMERAAGC, 48);
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, 32);
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAHUE, 32);
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACOLOR, 32);
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACONTRAST, 32);
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, 32);
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, 0);
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, 48);
 
 	printk(KERN_INFO "meye: Motion Eye Camera Driver v%s.\n",
 	       MEYE_DRIVER_VERSION);
@@ -1953,7 +1953,7 @@
 outregions:
 	pci_disable_device(meye.mchip_dev);
 outenabledev:
-	sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 0);
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0);
 outsonypienable:
 	kfifo_free(meye.doneq);
 outkfifoalloc2:
@@ -1986,7 +1986,7 @@
 
 	pci_disable_device(meye.mchip_dev);
 
-	sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 0);
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0);
 
 	kfifo_free(meye.doneq);
 	kfifo_free(meye.grabq);
diff --git a/drivers/media/video/meye.h b/drivers/media/video/meye.h
index ea107cb..323d007 100644
--- a/drivers/media/video/meye.h
+++ b/drivers/media/video/meye.h
@@ -255,7 +255,7 @@
 /****************************************************************************/
 
 /* Sony Programmable I/O Controller for accessing the camera commands */
-#include <linux/sonypi.h>
+#include <linux/sony-laptop.h>
 
 /* private API definitions */
 #include <linux/meye.h>
diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c
index ba1af3c..3bb7d66 100644
--- a/drivers/media/video/msp3400-driver.c
+++ b/drivers/media/video/msp3400-driver.c
@@ -773,6 +773,9 @@
 		break;
 	}
 
+	case VIDIOC_G_CHIP_IDENT:
+		return v4l2_chip_ident_i2c_client(client, arg, state->ident, (state->rev1 << 16) | state->rev2);
+
 	default:
 		/* unknown */
 		return -EINVAL;
@@ -872,6 +875,8 @@
 	snprintf(client->name, sizeof(client->name), "MSP%d4%02d%c-%c%d",
 			msp_family, msp_product,
 			msp_revision, msp_hard, msp_rom);
+	/* Rev B=2, C=3, D=4, G=7 */
+	state->ident = msp_family * 10000 + 4000 + msp_product * 10 + msp_revision - '@';
 
 	/* Has NICAM support: all mspx41x and mspx45x products have NICAM */
 	state->has_nicam = msp_prod_hi == 1 || msp_prod_hi == 5;
diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/video/msp3400-driver.h
index 7531efa..ab69a29 100644
--- a/drivers/media/video/msp3400-driver.h
+++ b/drivers/media/video/msp3400-driver.h
@@ -50,6 +50,7 @@
 
 struct msp_state {
 	int rev1, rev2;
+	int ident;
 	u8 has_nicam;
 	u8 has_radio;
 	u8 has_headphones;
diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c
index 5ed0adc..03bc369 100644
--- a/drivers/media/video/ov7670.c
+++ b/drivers/media/video/ov7670.c
@@ -5,6 +5,8 @@
  * by Jonathan Corbet with substantial inspiration from Mark
  * McClelland's ovcamchip code.
  *
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ *
  * This file may be distributed under the terms of the GNU General
  * Public License, version 2.
  */
@@ -15,6 +17,7 @@
 #include <linux/delay.h>
 #include <linux/videodev.h>
 #include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
 #include <linux/i2c.h>
 
 
@@ -162,6 +165,10 @@
 
 #define REG_GFIX	0x69	/* Fix gain control */
 
+#define REG_REG76	0x76	/* OV's name */
+#define   R76_BLKPCOR	  0x80	  /* Black pixel correction enable */
+#define   R76_WHTPCOR	  0x40	  /* White pixel correction enable */
+
 #define REG_RGB444	0x8c	/* RGB 444 control */
 #define   R444_ENABLE	  0x02	  /* Turn on RGB444, overrides 5x5 */
 #define   R444_RGBX	  0x01	  /* Empty nibble at end */
@@ -255,7 +262,7 @@
 
 	/* Almost all of these are magic "reserved" values.  */
 	{ REG_COM5, 0x61 },	{ REG_COM6, 0x4b },
-	{ 0x16, 0x02 },		{ REG_MVFP, 0x07|MVFP_MIRROR },
+	{ 0x16, 0x02 },		{ REG_MVFP, 0x07 },
 	{ 0x21, 0x02 },		{ 0x22, 0x91 },
 	{ 0x29, 0x07 },		{ 0x33, 0x0b },
 	{ 0x35, 0x0b },		{ 0x37, 0x1d },
@@ -380,6 +387,13 @@
 	{ 0xff, 0xff },
 };
 
+static struct regval_list ov7670_fmt_raw[] = {
+	{ REG_COM7, COM7_BAYER },
+	{ REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */
+	{ REG_COM16, 0x3d }, /* Edge enhancement, denoise */
+	{ REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */
+	{ 0xff, 0xff },
+};
 
 
 
@@ -483,32 +497,39 @@
 	__u32 pixelformat;
 	struct regval_list *regs;
 	int cmatrix[CMATRIX_LEN];
+	int bpp;   /* Bytes per pixel */
 } ov7670_formats[] = {
 	{
 		.desc		= "YUYV 4:2:2",
 		.pixelformat	= V4L2_PIX_FMT_YUYV,
 		.regs 		= ov7670_fmt_yuv422,
 		.cmatrix	= { 128, -128, 0, -34, -94, 128 },
+		.bpp		= 2,
 	},
 	{
 		.desc		= "RGB 444",
 		.pixelformat	= V4L2_PIX_FMT_RGB444,
 		.regs		= ov7670_fmt_rgb444,
 		.cmatrix	= { 179, -179, 0, -61, -176, 228 },
+		.bpp		= 2,
 	},
 	{
 		.desc		= "RGB 565",
 		.pixelformat	= V4L2_PIX_FMT_RGB565,
 		.regs		= ov7670_fmt_rgb565,
 		.cmatrix	= { 179, -179, 0, -61, -176, 228 },
+		.bpp		= 2,
+	},
+	{
+		.desc		= "Raw RGB Bayer",
+		.pixelformat	= V4L2_PIX_FMT_SBGGR8,
+		.regs 		= ov7670_fmt_raw,
+		.cmatrix	= { 0, 0, 0, 0, 0, 0 },
+		.bpp		= 1
 	},
 };
-#define N_OV7670_FMTS (sizeof(ov7670_formats)/sizeof(ov7670_formats[0]))
+#define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats)
 
-/*
- * All formats we support are 2 bytes/pixel.
- */
-#define BYTES_PER_PIXEL 2
 
 /*
  * Then there is the issue of window sizes.  Try to capture the info here.
@@ -685,7 +706,7 @@
 	 */
 	pix->width = wsize->width;
 	pix->height = wsize->height;
-	pix->bytesperline = pix->width*BYTES_PER_PIXEL;
+	pix->bytesperline = pix->width*ov7670_formats[index].bpp;
 	pix->sizeimage = pix->height*pix->bytesperline;
 	return 0;
 }
@@ -1270,9 +1291,8 @@
 		void *arg)
 {
 	switch (cmd) {
-	case VIDIOC_INT_G_CHIP_IDENT:
-		* (enum v4l2_chip_ident *) arg = V4L2_IDENT_OV7670;
-		return 0;
+	case VIDIOC_G_CHIP_IDENT:
+		return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_OV7670, 0);
 
 	case VIDIOC_INT_RESET:
 		ov7670_reset(client);
diff --git a/drivers/media/video/planb.c b/drivers/media/video/planb.c
index 86d2884..1455a8f 100644
--- a/drivers/media/video/planb.c
+++ b/drivers/media/video/planb.c
@@ -2160,7 +2160,7 @@
 	if (!machine_is(powermac))
 		return 0;
 
-	planb_devices = find_devices("planb");
+	planb_devices = of_find_node_by_name(NULL, "planb");
 	if (planb_devices == 0) {
 		planb_num=0;
 		printk(KERN_WARNING "PlanB: no device found!\n");
@@ -2175,12 +2175,14 @@
 	if (planb_devices->n_addrs != 1) {
 		printk (KERN_WARNING "PlanB: expecting 1 address for planb "
 			"(got %d)", planb_devices->n_addrs);
+		of_node_put(planb_devices);
 		return 0;
 	}
 
 	if (planb_devices->n_intrs == 0) {
 		printk(KERN_WARNING "PlanB: no intrs for device %s\n",
 		       planb_devices->full_name);
+		of_node_put(planb_devices);
 		return 0;
 	} else {
 		irq = planb_devices->intrs[0].line;
@@ -2202,12 +2204,13 @@
 	confreg = planb_devices->addrs[0].space & 0xff;
 	old_base = planb_devices->addrs[0].address;
 	new_base = 0xf1000000;
+	of_node_put(planb_devices);
 
 	DEBUG("PlanB: Found on bus %d, dev %d, func %d, "
 		"membase 0x%x (base reg. 0x%x)\n",
 		bus, PCI_SLOT(dev_fn), PCI_FUNC(dev_fn), old_base, confreg);
 
-	pdev = pci_find_slot (bus, dev_fn);
+	pdev = pci_get_bus_and_slot(bus, dev_fn);
 	if (!pdev) {
 		printk(KERN_ERR "planb: cannot find slot\n");
 		goto err_out;
@@ -2237,6 +2240,7 @@
 	pb->planb_base = planb_regs;
 	pb->planb_base_phys = (struct planb_registers *)new_base;
 	pb->irq	= irq;
+	pb->dev = pdev;
 
 	return planb_num;
 
@@ -2244,6 +2248,7 @@
 	pci_disable_device(pdev);
 err_out:
 	/* FIXME handle error */   /* comment moved from pci_find_slot, above */
+	pci_dev_put(pdev);
 	return 0;
 }
 
@@ -2271,6 +2276,8 @@
 		printk(KERN_INFO "PlanB: unregistering with v4l\n");
 		video_unregister_device(&pb->video_dev);
 
+		pci_dev_put(pb->dev);
+
 		/* note that iounmap() does nothing on the PPC right now */
 		iounmap ((void *)pb->planb_base);
 	}
diff --git a/drivers/media/video/planb.h b/drivers/media/video/planb.h
index 9282321..e21b573 100644
--- a/drivers/media/video/planb.h
+++ b/drivers/media/video/planb.h
@@ -177,6 +177,7 @@
 	struct mutex lock;
 	unsigned int	irq;			/* interrupt number */
 	volatile unsigned int intr_mask;
+	struct pci_dev *dev;			/* Our PCI device */
 
 	int	overlay;			/* overlay running? */
 	struct	planb_window win;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
index 5786faf..5669c8c 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
@@ -324,7 +324,7 @@
 
 /* This implements some extra setup for the encoder that seems to be
    specific to the PVR USB2 hardware. */
-int pvr2_encoder_prep_config(struct pvr2_hdw *hdw)
+static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw)
 {
 	int ret = 0;
 	int encMisc3Arg = 0;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
index 16bd741..ce66ab8 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
@@ -283,6 +283,8 @@
 	int unit_number;             /* ID for driver instance */
 	unsigned long serial_number; /* ID for hardware itself */
 
+	char bus_info[32]; /* Bus location info */
+
 	/* Minor numbers used by v4l logic (yes, this is a hack, as there
 	   should be no v4l junk here).  Probably a better way to do this. */
 	int v4l_minor_number_video;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index 9916cf3..acf651e 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -1008,6 +1008,13 @@
 	return hdw->serial_number;
 }
 
+
+const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *hdw)
+{
+	return hdw->bus_info;
+}
+
+
 unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw)
 {
 	return hdw->freqSelector ? hdw->freqValTelevision : hdw->freqValRadio;
@@ -2105,6 +2112,11 @@
 	hdw->usb_intf = intf;
 	hdw->usb_dev = interface_to_usbdev(intf);
 
+	scnprintf(hdw->bus_info,sizeof(hdw->bus_info),
+		  "usb %s address %d",
+		  hdw->usb_dev->dev.bus_id,
+		  hdw->usb_dev->devnum);
+
 	ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber;
 	usb_set_interface(hdw->usb_dev,ifnum,0);
 
@@ -3275,7 +3287,9 @@
 	mutex_lock(&hdw->i2c_list_lock); do {
 		list_for_each(item,&hdw->i2c_clients) {
 			cp = list_entry(item,struct pvr2_i2c_client,list);
-			if (!v4l2_chip_match_i2c_client(cp->client, req.match_type, req.match_chip)) {
+			if (!v4l2_chip_match_i2c_client(
+				    cp->client,
+				    req.match_type, req.match_chip)) {
 				continue;
 			}
 			stat = pvr2_i2c_client_cmd(
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
index 0c9cca4..4dba8d0 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
@@ -124,6 +124,9 @@
 /* Retrieve serial number of device */
 unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *);
 
+/* Retrieve bus location info of device */
+const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *);
+
 /* Called when hardware has been unplugged */
 void pvr2_hdw_disconnect(struct pvr2_hdw *);
 
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
index 91396fd..a741c556 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
@@ -42,9 +42,11 @@
 	struct class_device_attribute attr_v4l_minor_number;
 	struct class_device_attribute attr_v4l_radio_minor_number;
 	struct class_device_attribute attr_unit_number;
+	struct class_device_attribute attr_bus_info;
 	int v4l_minor_number_created_ok;
 	int v4l_radio_minor_number_created_ok;
 	int unit_number_created_ok;
+	int bus_info_created_ok;
 };
 
 #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
@@ -705,6 +707,10 @@
 	pvr2_sysfs_tear_down_debugifc(sfp);
 #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
 	pvr2_sysfs_tear_down_controls(sfp);
+	if (sfp->bus_info_created_ok) {
+		class_device_remove_file(sfp->class_dev,
+					 &sfp->attr_bus_info);
+	}
 	if (sfp->v4l_minor_number_created_ok) {
 		class_device_remove_file(sfp->class_dev,
 					 &sfp->attr_v4l_minor_number);
@@ -735,6 +741,16 @@
 }
 
 
+static ssize_t bus_info_show(struct class_device *class_dev,char *buf)
+{
+	struct pvr2_sysfs *sfp;
+	sfp = (struct pvr2_sysfs *)class_dev->class_data;
+	if (!sfp) return -EINVAL;
+	return scnprintf(buf,PAGE_SIZE,"%s\n",
+			 pvr2_hdw_get_bus_info(sfp->channel.hdw));
+}
+
+
 static ssize_t v4l_radio_minor_number_show(struct class_device *class_dev,
 					   char *buf)
 {
@@ -836,6 +852,20 @@
 		sfp->unit_number_created_ok = !0;
 	}
 
+	sfp->attr_bus_info.attr.owner = THIS_MODULE;
+	sfp->attr_bus_info.attr.name = "bus_info_str";
+	sfp->attr_bus_info.attr.mode = S_IRUGO;
+	sfp->attr_bus_info.show = bus_info_show;
+	sfp->attr_bus_info.store = NULL;
+	ret = class_device_create_file(sfp->class_dev,
+				       &sfp->attr_bus_info);
+	if (ret < 0) {
+		printk(KERN_WARNING "%s: class_device_create_file error: %d\n",
+		       __FUNCTION__, ret);
+	} else {
+		sfp->bus_info_created_ok = !0;
+	}
+
 	pvr2_sysfs_add_controls(sfp);
 #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
 	pvr2_sysfs_add_debugifc(sfp);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
index 25d3830..4563b3d 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -203,6 +203,8 @@
 		struct v4l2_capability *cap = arg;
 
 		memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability));
+		strlcpy(cap->bus_info,pvr2_hdw_get_bus_info(hdw),
+			sizeof(cap->bus_info));
 
 		ret = 0;
 		break;
diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c
index 0bd1155..338ced7 100644
--- a/drivers/media/video/pwc/pwc-ctrl.c
+++ b/drivers/media/video/pwc/pwc-ctrl.c
@@ -140,6 +140,8 @@
    An alternate value of 0 means this mode is not available at all.
  */
 
+#define PWC_FPS_MAX_NALA 8
+
 struct Nala_table_entry {
 	char alternate;			/* USB alternate setting */
 	int compressed;			/* Compressed yes/no */
@@ -147,7 +149,9 @@
 	unsigned char mode[3];		/* precomputed mode table */
 };
 
-static struct Nala_table_entry Nala_table[PSZ_MAX][8] =
+static unsigned int Nala_fps_vector[PWC_FPS_MAX_NALA] = { 4, 5, 7, 10, 12, 15, 20, 24 };
+
+static struct Nala_table_entry Nala_table[PSZ_MAX][PWC_FPS_MAX_NALA] =
 {
 #include "pwc-nala.h"
 };
@@ -423,6 +427,59 @@
 	return 0;
 }
 
+static unsigned int pwc_get_fps_Nala(struct pwc_device *pdev, unsigned int index, unsigned int size)
+{
+	unsigned int i;
+
+	for (i = 0; i < PWC_FPS_MAX_NALA; i++) {
+		if (Nala_table[size][i].alternate) {
+			if (index--==0) return Nala_fps_vector[i];
+		}
+	}
+	return 0;
+}
+
+static unsigned int pwc_get_fps_Kiara(struct pwc_device *pdev, unsigned int index, unsigned int size)
+{
+	unsigned int i;
+
+	for (i = 0; i < PWC_FPS_MAX_KIARA; i++) {
+		if (Kiara_table[size][i][3].alternate) {
+			if (index--==0) return Kiara_fps_vector[i];
+		}
+	}
+	return 0;
+}
+
+static unsigned int pwc_get_fps_Timon(struct pwc_device *pdev, unsigned int index, unsigned int size)
+{
+	unsigned int i;
+
+	for (i=0; i < PWC_FPS_MAX_TIMON; i++) {
+		if (Timon_table[size][i][3].alternate) {
+			if (index--==0) return Timon_fps_vector[i];
+		}
+	}
+	return 0;
+}
+
+unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size)
+{
+	unsigned int ret;
+
+	if (DEVICE_USE_CODEC1(pdev->type)) {
+		ret = pwc_get_fps_Nala(pdev, index, size);
+
+	} else if (DEVICE_USE_CODEC3(pdev->type)) {
+		ret = pwc_get_fps_Kiara(pdev, index, size);
+
+	} else {
+		ret = pwc_get_fps_Timon(pdev, index, size);
+	}
+
+	return ret;
+}
+
 #define BLACK_Y 0
 #define BLACK_U 128
 #define BLACK_V 128
@@ -1343,7 +1400,7 @@
 				ret = pwc_read_red_gain(pdev, &ARGR(wb).read_red);
 				if (ret < 0)
 					break;
-				ret =pwc_read_blue_gain(pdev, &ARGR(wb).read_blue);
+				ret = pwc_read_blue_gain(pdev, &ARGR(wb).read_blue);
 				if (ret < 0)
 					break;
 			}
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
index 27ed769..085332a 100644
--- a/drivers/media/video/pwc/pwc-if.c
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -95,8 +95,8 @@
 	{ USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */
 	{ USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */
 	{ USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */
-	{ USB_DEVICE(0x046D, 0x08B6) }, /* Logitech (reserved) */
-	{ USB_DEVICE(0x046D, 0x08B7) }, /* Logitech (reserved) */
+	{ USB_DEVICE(0x046D, 0x08B6) }, /* Cisco VT Camera */
+	{ USB_DEVICE(0x046D, 0x08B7) }, /* Logitech ViewPort AV 100 */
 	{ USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */
 	{ USB_DEVICE(0x055D, 0x9000) }, /* Samsung MPC-C10 */
 	{ USB_DEVICE(0x055D, 0x9001) }, /* Samsung MPC-C30 */
@@ -1493,7 +1493,7 @@
 		case 0x0329:
 			PWC_INFO("Philips SPC 900NC USB webcam detected.\n");
 			name = "Philips SPC 900NC webcam";
-			type_id = 720;
+			type_id = 740;
 			break;
 		default:
 			return -ENODEV;
@@ -1547,8 +1547,16 @@
 			features |= FEATURE_MOTOR_PANTILT;
 			break;
 		case 0x08b6:
+			PWC_INFO("Logitech/Cisco VT Camera webcam detected.\n");
+			name = "Cisco VT Camera";
+			type_id = 740; /* CCD sensor */
+			break;
 		case 0x08b7:
-		case 0x08b8:
+			PWC_INFO("Logitech ViewPort AV 100 webcam detected.\n");
+			name = "Logitech ViewPort AV 100";
+			type_id = 740; /* CCD sensor */
+			break;
+		case 0x08b8: /* Where this released? */
 			PWC_INFO("Logitech QuickCam detected (reserved ID).\n");
 			name = "Logitech QuickCam (res.)";
 			type_id = 730; /* Assuming CMOS */
diff --git a/drivers/media/video/pwc/pwc-ioctl.h b/drivers/media/video/pwc/pwc-ioctl.h
index 784bc72..cec6602 100644
--- a/drivers/media/video/pwc/pwc-ioctl.h
+++ b/drivers/media/video/pwc/pwc-ioctl.h
@@ -2,7 +2,7 @@
 #define PWC_IOCTL_H
 
 /* (C) 2001-2004 Nemosoft Unv.
-   (C) 2004      Luc Saillard (luc@saillard.org)
+   (C) 2004-2006 Luc Saillard (luc@saillard.org)
 
    NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
    driver and thus may have bugs that are not present in the original version.
@@ -25,7 +25,7 @@
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
-/* This is pwc-ioctl.h belonging to PWC 8.12.1
+/* This is pwc-ioctl.h belonging to PWC 10.0.10
    It contains structures and defines to communicate from user space
    directly to the driver.
  */
@@ -51,6 +51,9 @@
 	     ... 	the function
  */
 
+#include <linux/types.h>
+#include <linux/version.h>
+
 
  /* Enumeration of image sizes */
 #define PSZ_SQCIF	0x00
@@ -65,6 +68,8 @@
 /* The frame rate is encoded in the video_window.flags parameter using
    the upper 16 bits, since some flags are defined nowadays. The following
    defines provide a mask and shift to filter out this value.
+   This value can also be passing using the private flag when using v4l2 and
+   VIDIOC_S_FMT ioctl.
 
    In 'Snapshot' mode the camera freezes its automatic exposure and colour
    balance controls.
@@ -73,6 +78,8 @@
 #define PWC_FPS_MASK		0x00FF0000
 #define PWC_FPS_FRMASK		0x003F0000
 #define PWC_FPS_SNAPSHOT	0x00400000
+#define PWC_QLT_MASK		0x03000000
+#define PWC_QLT_SHIFT		24
 
 
 /* structure for transferring x & y coordinates */
@@ -289,4 +296,29 @@
 };
 #define VIDIOCPWCGVIDTABLE	_IOR('v', 216, struct pwc_table_init_buffer)
 
+/*
+ * This is private command used when communicating with v4l2.
+ * In the future all private ioctl will be remove/replace to
+ * use interface offer by v4l2.
+ */
+
+#define V4L2_CID_PRIVATE_SAVE_USER       (V4L2_CID_PRIVATE_BASE + 0)
+#define V4L2_CID_PRIVATE_RESTORE_USER    (V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_PRIVATE_RESTORE_FACTORY (V4L2_CID_PRIVATE_BASE + 2)
+#define V4L2_CID_PRIVATE_COLOUR_MODE     (V4L2_CID_PRIVATE_BASE + 3)
+#define V4L2_CID_PRIVATE_AUTOCONTOUR     (V4L2_CID_PRIVATE_BASE + 4)
+#define V4L2_CID_PRIVATE_CONTOUR         (V4L2_CID_PRIVATE_BASE + 5)
+#define V4L2_CID_PRIVATE_BACKLIGHT       (V4L2_CID_PRIVATE_BASE + 6)
+#define V4L2_CID_PRIVATE_FLICKERLESS     (V4L2_CID_PRIVATE_BASE + 7)
+#define V4L2_CID_PRIVATE_NOISE_REDUCTION (V4L2_CID_PRIVATE_BASE + 8)
+
+struct pwc_raw_frame {
+   __le16 type;		/* type of the webcam */
+   __le16 vbandlength;	/* Size of 4lines compressed (used by the decompressor) */
+   __u8   cmd[4];	/* the four byte of the command (in case of nala,
+			   only the first 3 bytes is filled) */
+   __u8   rawframe[0];	/* frame_size = H/4*vbandlength */
+} __attribute__ ((packed));
+
+
 #endif
diff --git a/drivers/media/video/pwc/pwc-kiara.c b/drivers/media/video/pwc/pwc-kiara.c
index fec39cc..f4ae83c 100644
--- a/drivers/media/video/pwc/pwc-kiara.c
+++ b/drivers/media/video/pwc/pwc-kiara.c
@@ -42,6 +42,8 @@
 #include "pwc-kiara.h"
 #include "pwc-uncompress.h"
 
+const unsigned int Kiara_fps_vector[PWC_FPS_MAX_KIARA] = { 5, 10, 15, 20, 25, 30 };
+
 const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4] =
 {
    /* SQCIF */
diff --git a/drivers/media/video/pwc/pwc-kiara.h b/drivers/media/video/pwc/pwc-kiara.h
index 0bdb225..047dad8 100644
--- a/drivers/media/video/pwc/pwc-kiara.h
+++ b/drivers/media/video/pwc/pwc-kiara.h
@@ -29,6 +29,8 @@
 
 #include <media/pwc-ioctl.h>
 
+#define PWC_FPS_MAX_KIARA 6
+
 struct Kiara_table_entry
 {
 	char alternate;			/* USB alternate interface */
@@ -37,8 +39,9 @@
 	unsigned char mode[12];		/* precomputed mode settings for cam */
 };
 
-extern const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4];
+extern const struct Kiara_table_entry Kiara_table[PSZ_MAX][PWC_FPS_MAX_KIARA][4];
 extern const unsigned int KiaraRomTable[8][2][16][8];
+extern const unsigned int Kiara_fps_vector[PWC_FPS_MAX_KIARA];
 
 #endif
 
diff --git a/drivers/media/video/pwc/pwc-timon.c b/drivers/media/video/pwc/pwc-timon.c
index be65bdc..c56c174 100644
--- a/drivers/media/video/pwc/pwc-timon.c
+++ b/drivers/media/video/pwc/pwc-timon.c
@@ -40,7 +40,9 @@
 
 #include "pwc-timon.h"
 
-const struct Timon_table_entry Timon_table[PSZ_MAX][6][4] =
+const unsigned int Timon_fps_vector[PWC_FPS_MAX_TIMON] = { 5, 10, 15, 20, 25, 30 };
+
+const struct Timon_table_entry Timon_table[PSZ_MAX][PWC_FPS_MAX_TIMON][4] =
 {
    /* SQCIF */
    {
diff --git a/drivers/media/video/pwc/pwc-timon.h b/drivers/media/video/pwc/pwc-timon.h
index eef9e2c..a6e22224 100644
--- a/drivers/media/video/pwc/pwc-timon.h
+++ b/drivers/media/video/pwc/pwc-timon.h
@@ -44,6 +44,8 @@
 
 #include <media/pwc-ioctl.h>
 
+#define PWC_FPS_MAX_TIMON 6
+
 struct Timon_table_entry
 {
 	char alternate;			/* USB alternate interface */
@@ -52,9 +54,9 @@
 	unsigned char mode[13];		/* precomputed mode settings for cam */
 };
 
-extern const struct Timon_table_entry Timon_table[PSZ_MAX][6][4];
+extern const struct Timon_table_entry Timon_table[PSZ_MAX][PWC_FPS_MAX_TIMON][4];
 extern const unsigned int TimonRomTable [16][2][16][8];
-
+extern const unsigned int Timon_fps_vector[PWC_FPS_MAX_TIMON];
 
 #endif
 
diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c
index d5e6bc8..32fbe1a 100644
--- a/drivers/media/video/pwc/pwc-v4l.c
+++ b/drivers/media/video/pwc/pwc-v4l.c
@@ -1168,7 +1168,7 @@
 			buf->sequence = 0;
 			buf->memory = V4L2_MEMORY_MMAP;
 			buf->m.offset = pdev->fill_image * pdev->len_per_image;
-			buf->length = buf->bytesused;
+			buf->length = pdev->len_per_image;
 			pwc_next_image(pdev);
 
 			PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->index=%d\n",buf->index);
@@ -1193,6 +1193,64 @@
 			return 0;
 		}
 
+		case VIDIOC_ENUM_FRAMESIZES:
+		{
+			struct v4l2_frmsizeenum *fsize = arg;
+			unsigned int i = 0, index = fsize->index;
+
+			if (fsize->pixel_format == V4L2_PIX_FMT_YUV420) {
+				for (i = 0; i < PSZ_MAX; i++) {
+					if (pdev->image_mask & (1UL << i)) {
+						if (!index--) {
+							fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+							fsize->discrete.width = pwc_image_sizes[i].x;
+							fsize->discrete.height = pwc_image_sizes[i].y;
+							return 0;
+						}
+					}
+				}
+			} else if (fsize->index == 0 &&
+				   ((fsize->pixel_format == V4L2_PIX_FMT_PWC1 && DEVICE_USE_CODEC1(pdev->type)) ||
+				    (fsize->pixel_format == V4L2_PIX_FMT_PWC2 && DEVICE_USE_CODEC23(pdev->type)))) {
+
+				fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+				fsize->discrete.width = pdev->abs_max.x;
+				fsize->discrete.height = pdev->abs_max.y;
+				return 0;
+			}
+			return -EINVAL;
+		}
+
+		case VIDIOC_ENUM_FRAMEINTERVALS:
+		{
+			struct v4l2_frmivalenum *fival = arg;
+			int size = -1;
+			unsigned int i;
+
+			for (i = 0; i < PSZ_MAX; i++) {
+				if (pwc_image_sizes[i].x == fival->width &&
+				    pwc_image_sizes[i].y == fival->height) {
+					size = i;
+					break;
+				}
+			}
+
+			/* TODO: Support raw format */
+			if (size < 0 || fival->pixel_format != V4L2_PIX_FMT_YUV420) {
+				return -EINVAL;
+			}
+
+			i = pwc_get_fps(pdev, fival->index, size);
+			if (!i)
+				return -EINVAL;
+
+			fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+			fival->discrete.numerator = 1;
+			fival->discrete.denominator = i;
+
+			return 0;
+		}
+
 		default:
 			return pwc_ioctl(pdev, cmd, arg);
 	} /* ..switch */
diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h
index e778a2b..acbb931 100644
--- a/drivers/media/video/pwc/pwc.h
+++ b/drivers/media/video/pwc/pwc.h
@@ -44,7 +44,7 @@
 #define PWC_MINOR	0
 #define PWC_EXTRAMINOR	12
 #define PWC_VERSION_CODE KERNEL_VERSION(PWC_MAJOR,PWC_MINOR,PWC_EXTRAMINOR)
-#define PWC_VERSION 	"10.0.12"
+#define PWC_VERSION 	"10.0.13"
 #define PWC_NAME 	"pwc"
 #define PFX		PWC_NAME ": "
 
@@ -85,7 +85,7 @@
 #define PWC_INFO(fmt, args...) printk(KERN_INFO PFX fmt, ##args)
 #define PWC_TRACE(fmt, args...) PWC_DEBUG(TRACE, fmt, ##args)
 
-#else /* if ! CONFIG_PWC_DEBUG */
+#else /* if ! CONFIG_USB_PWC_DEBUG */
 
 #define PWC_ERROR(fmt, args...) printk(KERN_ERR PFX fmt, ##args)
 #define PWC_WARNING(fmt, args...) printk(KERN_WARNING PFX fmt, ##args)
@@ -287,6 +287,7 @@
 /** Functions in pwc-ctrl.c */
 /* Request a certain video mode. Returns < 0 if not possible */
 extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot);
+extern unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size);
 /* Calculate the number of bytes per image (not frame) */
 extern int pwc_mpt_reset(struct pwc_device *pdev, int flags);
 extern int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt);
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
index 4d5bbd85..2d18f00 100644
--- a/drivers/media/video/saa7115.c
+++ b/drivers/media/video/saa7115.c
@@ -45,6 +45,7 @@
 #include <linux/i2c.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
 #include <media/saa7115.h>
 #include <asm/div64.h>
 
@@ -80,7 +81,7 @@
 	int sat;
 	int width;
 	int height;
-	enum v4l2_chip_ident ident;
+	u32 ident;
 	u32 audclk_freq;
 	u32 crystal_freq;
 	u8 ucgc;
@@ -1232,7 +1233,6 @@
 static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *arg)
 {
 	struct saa711x_state *state = i2c_get_clientdata(client);
-	int *iarg = arg;
 
 	/* ioctls to allow direct access to the saa7115 registers for testing */
 	switch (cmd) {
@@ -1437,9 +1437,8 @@
 	}
 #endif
 
-	case VIDIOC_INT_G_CHIP_IDENT:
-		*iarg = state->ident;
-		break;
+	case VIDIOC_G_CHIP_IDENT:
+		return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0);
 
 	default:
 		return -EINVAL;
@@ -1487,6 +1486,7 @@
 	if (memcmp(name, "1f711", 5)) {
 		v4l_dbg(1, debug, client, "chip found @ 0x%x (ID %s) does not match a known saa711x chip.\n",
 			address << 1, name);
+		kfree(client);
 		return 0;
 	}
 
diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c
index 654863d..9f98693 100644
--- a/drivers/media/video/saa7127.c
+++ b/drivers/media/video/saa7127.c
@@ -54,6 +54,7 @@
 #include <linux/i2c.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
 #include <media/saa7127.h>
 
 static int debug = 0;
@@ -234,7 +235,7 @@
 
 struct saa7127_state {
 	v4l2_std_id std;
-	enum v4l2_chip_ident ident;
+	u32 ident;
 	enum saa7127_input_type input_type;
 	enum saa7127_output_type output_type;
 	int video_enable;
@@ -550,12 +551,12 @@
 	struct v4l2_routing *route = arg;
 
 	switch (cmd) {
-	case VIDIOC_S_STD:
+	case VIDIOC_INT_S_STD_OUTPUT:
 		if (state->std == *(v4l2_std_id *)arg)
 			break;
 		return saa7127_set_std(client, *(v4l2_std_id *)arg);
 
-	case VIDIOC_G_STD:
+	case VIDIOC_INT_G_STD_OUTPUT:
 		*(v4l2_std_id *)arg = state->std;
 		break;
 
@@ -650,9 +651,8 @@
 		break;
 	}
 
-	case VIDIOC_INT_G_CHIP_IDENT:
-		*(enum v4l2_chip_ident *)arg = state->ident;
-		break;
+	case VIDIOC_G_CHIP_IDENT:
+		return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0);
 
 	default:
 		return -EINVAL;
diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig
index 59da79c..309dca3 100644
--- a/drivers/media/video/saa7134/Kconfig
+++ b/drivers/media/video/saa7134/Kconfig
@@ -46,6 +46,7 @@
 	select DVB_NXT200X if !DVB_FE_CUSTOMISE
 	select DVB_TDA10086 if !DVB_FE_CUSTOMISE
 	select DVB_TDA826X if !DVB_FE_CUSTOMISE
+	select DVB_TDA827X if !DVB_FE_CUSTOMISE
 	select DVB_ISL6421 if !DVB_FE_CUSTOMISE
 	---help---
 	  This adds support for DVB cards based on the
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index 89f3210..4ea479b 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -1543,12 +1543,12 @@
 		},{
 			.name = name_comp1,
 			.vmux = 0,
-			.amux = LINE2,
+			.amux = LINE1,
 			.gpio = 0x02,
 		},{
 			.name = name_svideo,
 			.vmux = 6,
-			.amux = LINE2,
+			.amux = LINE1,
 			.gpio = 0x02,
 		}},
 		.radio = {
@@ -1778,17 +1778,19 @@
 	[SAA7134_BOARD_FLYDVBTDUO] = {
 		/* LifeView FlyDVB-T DUO */
 		/* "Nico Sabbi <nsabbi@tiscali.it>  Hartmut Hackmann hartmut.hackmann@t-online.de*/
-		.name           = "LifeView FlyDVB-T DUO",
+		.name           = "LifeView FlyDVB-T DUO / MSI TV@nywhere Duo",
 		.audio_clock    = 0x00200000,
 		.tuner_type     = TUNER_PHILIPS_TDA8290,
 		.radio_type     = UNSET,
 		.tuner_addr	= ADDR_UNSET,
 		.radio_addr	= ADDR_UNSET,
+		.gpiomask	= 0x00200000,
 		.mpeg           = SAA7134_MPEG_DVB,
 		.inputs         = {{
 			.name = name_tv,
 			.vmux = 1,
 			.amux = TV,
+			.gpio = 0x200000,	/* GPIO21=High for TV input */
 			.tv   = 1,
 		},{
 			.name = name_comp1,	/* Composite signal on S-Video input */
@@ -1803,6 +1805,11 @@
 			.vmux = 8,
 			.amux = LINE2,
 		}},
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x000000,	/* GPIO21=Low for FM radio antenna */
+		},
 	},
 	[SAA7134_BOARD_PHILIPS_TOUGH] = {
 		.name           = "Philips TOUGH DVB-T reference design",
@@ -2546,8 +2553,9 @@
 		.radio_type     = UNSET,
 		.tuner_addr	= ADDR_UNSET,
 		.radio_addr	= ADDR_UNSET,
+		.tuner_config   = 0,
 		.mpeg           = SAA7134_MPEG_DVB,
-		.gpiomask       = 1 << 21,
+		.gpiomask       = 0x0200000,
 		.inputs = {{
 			.name   = name_tv,
 			.vmux   = 1,
@@ -2624,7 +2632,7 @@
 		}},
 		.radio = {
 			.name   = name_radio,
-			.amux   = LINE1,
+			.amux   = TV,
 			.gpio   = 0x0200000,
 		},
 	},
@@ -3043,6 +3051,7 @@
 		.radio_type     = UNSET,
 		.tuner_addr     = ADDR_UNSET,
 		.radio_addr     = ADDR_UNSET,
+		.tuner_config   = 1,
 		.mpeg           = SAA7134_MPEG_DVB,
 		.gpiomask       = 0x000200000,
 		.inputs         = {{
@@ -3289,6 +3298,115 @@
 			.amux   = LINE1,
 		}},
 	},
+	[SAA7134_BOARD_PHILIPS_TIGER_S] = {
+		.name           = "Philips Tiger - S Reference design",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tuner_config   = 2,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.gpiomask       = 0x0200000,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE1,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+		}},
+		.radio = {
+			.name   = name_radio,
+			.amux   = TV,
+			.gpio   = 0x0200000,
+		},
+	},
+	[SAA7134_BOARD_AVERMEDIA_M102] = {
+		.name           = "Avermedia M102",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask       = 1<<21,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 6,
+			.amux = LINE2,
+		}},
+	},
+	[SAA7134_BOARD_ASUS_P7131_4871] = {
+		.name           = "ASUS P7131 4871",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tuner_config   = 2,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.gpiomask       = 0x0200000,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+			.gpio   = 0x0200000,
+		}},
+	},
+	[SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA] = {
+		.name           = "ASUSTeK P7131 Hybrid",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tuner_config   = 2,
+		.gpiomask	= 1 << 21,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x0000000,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE2,
+			.gpio = 0x0200000,
+		},{
+			.name = name_comp2,
+			.vmux = 0,
+			.amux = LINE2,
+			.gpio = 0x0200000,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+			.gpio = 0x0200000,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x0200000,
+		},
+	},
 };
 
 const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
@@ -3914,7 +4032,7 @@
 		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
 		.subvendor    = 0x1043,
 		.subdevice    = 0x4876,
-		.driver_data  = SAA7134_BOARD_ASUSTeK_P7131_DUAL,
+		.driver_data  = SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA,
 	},{
 		.vendor       = PCI_VENDOR_ID_PHILIPS,
 		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
@@ -3958,6 +4076,30 @@
 		.subdevice    = 0x1175,
 		.driver_data  = SAA7134_BOARD_CINERGY_HT_PCI,
 	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0xf31e,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_M102,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x4E42,         /* MSI */
+		.subdevice    = 0x0306,         /* TV@nywhere DUO */
+		.driver_data  = SAA7134_BOARD_FLYDVBTDUO,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1043,
+		.subdevice    = 0x4871,
+		.driver_data  = SAA7134_BOARD_ASUS_P7131_4871,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1043,
+		.subdevice    = 0x4857,
+		.driver_data  = SAA7134_BOARD_ASUSTeK_P7131_DUAL,
+	},{
 		/* --- boards without eeprom + subsystem ID --- */
 		.vendor       = PCI_VENDOR_ID_PHILIPS,
 		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
@@ -3971,7 +4113,6 @@
 		.subdevice    = 0,
 		.driver_data  = SAA7134_BOARD_NOAUTO,
 	},{
-
 		/* --- default catch --- */
 		.vendor       = PCI_VENDOR_ID_PHILIPS,
 		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
@@ -4063,6 +4204,7 @@
 	case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS:
 	case SAA7134_BOARD_FLYDVBT_LR301:
 	case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
+	case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
 	case SAA7134_BOARD_FLYDVBTDUO:
 	case SAA7134_BOARD_PROTEUS_2309:
 	case SAA7134_BOARD_AVERMEDIA_A16AR:
@@ -4103,8 +4245,8 @@
 		break;
 	case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
 	case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS:
-		saa_writeb(SAA7134_GPIO_GPMODE3, 0x08);
-		saa_writeb(SAA7134_GPIO_GPSTATUS3, 0x00);
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x08000000, 0x08000000);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08000000, 0x00000000);
 		break;
 	case SAA7134_BOARD_AVERMEDIA_CARDBUS:
 		/* power-up tuner chip */
@@ -4137,6 +4279,11 @@
 		       "%s: Dual decoder functionality is disabled for now, use the other chip.\n",
 		       dev->name,card(dev).name,dev->name,dev->name);
 		break;
+	case SAA7134_BOARD_AVERMEDIA_M102:
+		/* enable tuner */
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2,   0x8c040007, 0x8c040007);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0c0007cd, 0x0c0007cd);
+		break;
 	}
 	return 0;
 }
@@ -4146,6 +4293,9 @@
 {
 	unsigned char buf;
 	int board;
+	struct tuner_setup tun_setup;
+	tun_setup.config = 0;
+	tun_setup.tuner_callback = saa7134_tuner_callback;
 
 	switch (dev->board) {
 	case SAA7134_BOARD_BMK_MPEX_NOTUNER:
@@ -4162,8 +4312,6 @@
 		dev->tuner_type = saa7134_boards[dev->board].tuner_type;
 
 		if (TUNER_ABSENT != dev->tuner_type) {
-				struct tuner_setup tun_setup;
-
 				tun_setup.mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
 				tun_setup.type = dev->tuner_type;
 				tun_setup.addr = ADDR_UNSET;
@@ -4173,7 +4321,6 @@
 		break;
 	case SAA7134_BOARD_MD7134:
 		{
-		struct tuner_setup tun_setup;
 		u8 subaddr;
 		u8 data[3];
 		int ret, tuner_t;
@@ -4245,7 +4392,6 @@
 		 * the channel decoder. We have to make it transparent to find it
 		 */
 		{
-		struct tuner_setup tun_setup;
 		u8 data[] = { 0x07, 0x02};
 		struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
 		i2c_transfer(&dev->i2c_adap, &msg, 1);
@@ -4258,16 +4404,38 @@
 		}
 		break;
 	case SAA7134_BOARD_PHILIPS_TIGER:
+	case SAA7134_BOARD_PHILIPS_TIGER_S:
+		{
+		u8 data[] = { 0x3c, 0x33, 0x60};
+		struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+		if(dev->autodetected && (dev->eedata[0x49] == 0x50)) {
+			dev->board = SAA7134_BOARD_PHILIPS_TIGER_S;
+			printk(KERN_INFO "%s: Reconfigured board as %s\n",
+				dev->name, saa7134_boards[dev->board].name);
+		}
+		if(dev->board == SAA7134_BOARD_PHILIPS_TIGER_S) {
+			tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV;
+			tun_setup.type = TUNER_PHILIPS_TDA8290;
+			tun_setup.addr = 0x4b;
+			tun_setup.config = 2;
+
+			saa7134_i2c_call_clients (dev, TUNER_SET_TYPE_ADDR,&tun_setup);
+			data[2] = 0x68;
+		}
+		i2c_transfer(&dev->i2c_adap, &msg, 1);
+		}
+		break;
 	case SAA7134_BOARD_PINNACLE_PCTV_310i:
 	case SAA7134_BOARD_TEVION_DVBT_220RF:
 	case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
+	case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
 	case SAA7134_BOARD_MEDION_MD8800_QUADRO:
 	case SAA7134_BOARD_HAUPPAUGE_HVR1110:
 		/* this is a hybrid board, initialize to analog mode
 		 * and configure firmware eeprom address
 		 */
 		{
-		u8 data[] = { 0x3c, 0x33, 0x68};
+		u8 data[] = { 0x3c, 0x33, 0x60};
 		struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
 		i2c_transfer(&dev->i2c_adap, &msg, 1);
 		}
@@ -4281,18 +4449,18 @@
 		break;
 	case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
 	case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS:
-		/* make the tda10046 find its eeprom */
+		/* initialize analog mode  */
 		{
-		u8 data[] = { 0x3c, 0x33, 0x62};
+		u8 data[] = { 0x3c, 0x33, 0x6a};
 		struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
 		i2c_transfer(&dev->i2c_adap, &msg, 1);
 		}
 		break;
 	case SAA7134_BOARD_CINERGY_HT_PCMCIA:
 	case SAA7134_BOARD_CINERGY_HT_PCI:
-		/* make the tda10046 find its eeprom */
+		/* initialize analog mode */
 		{
-		u8 data[] = { 0x3c, 0x33, 0x60};
+		u8 data[] = { 0x3c, 0x33, 0x68};
 		struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
 		i2c_transfer(&dev->i2c_adap, &msg, 1);
 		}
diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c
index ed038ff..25f8470 100644
--- a/drivers/media/video/saa7134/saa7134-core.c
+++ b/drivers/media/video/saa7134/saa7134-core.c
@@ -117,6 +117,64 @@
 	       dev->name, mode, (~mode) & status, mode & status, msg);
 }
 
+void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value)
+{
+	u32 index, bitval;
+
+	index = 1 << bit_no;
+	switch (value) {
+	case 0: /* static value */
+	case 1:	dprintk("setting GPIO%d to static %d\n", bit_no, value);
+		/* turn sync mode off if necessary */
+		if (index & 0x00c00000)
+			saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x00);
+		if (value)
+			bitval = index;
+		else
+			bitval = 0;
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, index);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, index, bitval);
+		break;
+	case 3:	/* tristate */
+		dprintk("setting GPIO%d to tristate\n", bit_no);
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, 0);
+		break;
+	}
+}
+
+int saa7134_tuner_callback(void *ptr, int command, int arg)
+{
+	u8 sync_control;
+	struct saa7134_dev *dev = ptr;
+
+	switch (dev->tuner_type) {
+	case TUNER_PHILIPS_TDA8290:
+		switch (command) {
+		case 0: /* switch LNA gain through GPIO 22*/
+			saa7134_set_gpio(dev, 22, arg) ;
+			break;
+		case 1: /* vsync output at GPIO22. 50 / 60Hz */
+			dprintk("setting GPIO22 to vsync %d\n", arg);
+			saa_andorb(SAA7134_VIDEO_PORT_CTRL3, 0x80, 0x80);
+			saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x03);
+			if (arg == 1)
+				sync_control = 11;
+			else
+				sync_control = 17;
+			saa_writeb(SAA7134_VGATE_START, sync_control);
+			saa_writeb(SAA7134_VGATE_STOP, sync_control + 1);
+			saa_andorb(SAA7134_MISC_VGATE_MSB, 0x03, 0x00);
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -ENODEV;
+	}
+	return 0;
+}
+
 /* ------------------------------------------------------------------ */
 
 
@@ -124,55 +182,28 @@
 /* delayed request_module                                      */
 
 #if defined(CONFIG_MODULES) && defined(MODULE)
-static int need_empress;
-static int need_dvb;
-static int need_alsa;
-static int need_oss;
 
-static int pending_call(struct notifier_block *self, unsigned long state,
-			void *module)
-{
-	if (module != THIS_MODULE || state != MODULE_STATE_LIVE)
-		return NOTIFY_DONE;
 
-	if (need_empress)
+static void request_module_async(struct work_struct *work){
+	struct saa7134_dev* dev = container_of(work, struct saa7134_dev, request_module_wk);
+	if (card_is_empress(dev))
 		request_module("saa7134-empress");
-	if (need_dvb)
+	if (card_is_dvb(dev))
 		request_module("saa7134-dvb");
-	if (need_alsa)
+	if (alsa)
 		request_module("saa7134-alsa");
-	if (need_oss)
+	if (oss)
 		request_module("saa7134-oss");
-	return NOTIFY_DONE;
 }
 
-static int pending_registered;
-static struct notifier_block pending_notifier = {
-	.notifier_call = pending_call,
-};
-
-static void request_module_depend(char *name, int *flag)
+static void request_submodules(struct saa7134_dev *dev)
 {
-	int err;
-	switch (THIS_MODULE->state) {
-	case MODULE_STATE_COMING:
-		if (!pending_registered) {
-			err = register_module_notifier(&pending_notifier);
-			pending_registered = 1;
-		}
-		*flag = 1;
-		break;
-	case MODULE_STATE_LIVE:
-		request_module(name);
-		break;
-	default:
-		/* nothing */;
-		break;
-	}
+	INIT_WORK(&dev->request_module_wk, request_module_async);
+	schedule_work(&dev->request_module_wk);
 }
 
 #else
-#define request_module_depend(name,flag)
+#define request_submodules(dev)
 #endif /* CONFIG_MODULES */
 
 /* ------------------------------------------------------------------ */
@@ -703,7 +734,6 @@
 		saa7134_ts_fini(dev);
 	saa7134_input_fini(dev);
 	saa7134_vbi_fini(dev);
-	saa7134_video_fini(dev);
 	saa7134_tvaudio_fini(dev);
 	return 0;
 }
@@ -944,18 +974,9 @@
 		request_module("tuner");
 	if (card_is_empress(dev)) {
 		request_module("saa6752hs");
-		request_module_depend("saa7134-empress",&need_empress);
 	}
 
-	if (card_is_dvb(dev))
-		request_module_depend("saa7134-dvb",&need_dvb);
-
-
-	if (alsa)
-		request_module_depend("saa7134-alsa",&need_alsa);
-
-	if (oss)
-		request_module_depend("saa7134-oss",&need_oss);
+	request_submodules(dev);
 
 	v4l2_prio_init(&dev->prio);
 
@@ -1013,6 +1034,9 @@
 		saa7134_dmasound_init(dev);
 	}
 
+	if (TUNER_ABSENT != dev->tuner_type)
+		saa7134_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL);
+
 	return 0;
 
  fail4:
@@ -1152,10 +1176,6 @@
 
 static void saa7134_fini(void)
 {
-#if defined(CONFIG_MODULES) && defined(MODULE)
-	if (pending_registered)
-		unregister_module_notifier(&pending_notifier);
-#endif /* CONFIG_MODULES */
 	pci_unregister_driver(&saa7134_pci_driver);
 }
 
@@ -1164,6 +1184,7 @@
 
 /* ----------------------------------------------------------- */
 
+EXPORT_SYMBOL(saa7134_set_gpio);
 EXPORT_SYMBOL(saa7134_i2c_call_clients);
 EXPORT_SYMBOL(saa7134_devlist);
 EXPORT_SYMBOL(saa7134_boards);
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
index e3059fd..65aec88 100644
--- a/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/drivers/media/video/saa7134/saa7134-dvb.c
@@ -41,7 +41,9 @@
 
 #include "tda10086.h"
 #include "tda826x.h"
+#include "tda827x.h"
 #include "isl6421.h"
+
 MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
 MODULE_LICENSE("GPL");
 
@@ -54,7 +56,21 @@
 module_param(use_frontend, int, 0644);
 MODULE_PARM_DESC(use_frontend,"for cards with multiple frontends (0: terrestrial, 1: satellite)");
 
-/* ------------------------------------------------------------------ */
+static int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off module debugging (default:off).");
+
+#define dprintk(fmt, arg...)	do { if (debug) \
+	printk(KERN_DEBUG "%s/dvb: " fmt, dev->name , ## arg); } while(0)
+
+/* Print a warning */
+#define wprintk(fmt, arg...) \
+	printk(KERN_WARNING "%s/dvb: " fmt, dev->name, ## arg)
+
+/* ------------------------------------------------------------------
+ * mt352 based DVB-T cards
+ */
+
 static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on)
 {
 	u32 ok;
@@ -75,8 +91,7 @@
 	saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2,   (1 << 28));
 	udelay(10);
 	ok = saa_readl(SAA7134_GPIO_GPSTATUS0) & (1 << 27);
-	printk("%s: %s %s\n", dev->name, __FUNCTION__,
-	       ok ? "on" : "off");
+	dprintk("%s %s\n", __FUNCTION__, ok ? "on" : "off");
 
 	if (!ok)
 		saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2,   (1 << 26));
@@ -96,7 +111,7 @@
 	static u8 irq_cfg []       = { INTERRUPT_EN_0, 0x00, 0x00, 0x00, 0x00 };
 	struct saa7134_dev *dev= fe->dvb->priv;
 
-	printk("%s: %s called\n",dev->name,__FUNCTION__);
+	dprintk("%s called\n", __FUNCTION__);
 
 	mt352_write(fe, clock_config,   sizeof(clock_config));
 	udelay(200);
@@ -185,10 +200,26 @@
 	.demod_init    = mt352_aver777_init,
 };
 
-/* ------------------------------------------------------------------ */
-static int philips_tda6651_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+/* ==================================================================
+ * tda1004x based DVB-T cards, helper functions
+ */
+
+static int philips_tda1004x_request_firmware(struct dvb_frontend *fe,
+					   const struct firmware **fw, char *name)
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
+	return request_firmware(fw, name, &dev->pci->dev);
+}
+
+/* ------------------------------------------------------------------
+ * these tuners are tu1216, td1316(a)
+ */
+
+static int philips_tda6651_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+	struct tda1004x_state *state = fe->demodulator_priv;
+	u8 addr = state->config->tuner_address;
 	u8 tuner_buf[4];
 	struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tuner_buf,.len =
 			sizeof(tuner_buf) };
@@ -263,15 +294,20 @@
 
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 1);
-	if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1)
+	if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) {
+		wprintk("could not write to tuner at addr: 0x%02x\n",
+			addr << 1);
 		return -EIO;
+	}
 	msleep(1);
 	return 0;
 }
 
-static int philips_tda6651_pll_init(u8 addr, struct dvb_frontend *fe)
+static int philips_tu1216_init(struct dvb_frontend *fe)
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
+	struct tda1004x_state *state = fe->demodulator_priv;
+	u8 addr = state->config->tuner_address;
 	static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab };
 	struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) };
 
@@ -287,46 +323,17 @@
 
 /* ------------------------------------------------------------------ */
 
-static int philips_tu1216_tuner_60_init(struct dvb_frontend *fe)
-{
-	return philips_tda6651_pll_init(0x60, fe);
-}
-
-static int philips_tu1216_tuner_60_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
-{
-	return philips_tda6651_pll_set(0x60, fe, params);
-}
-
-static int philips_tda1004x_request_firmware(struct dvb_frontend *fe,
-					   const struct firmware **fw, char *name)
-{
-	struct saa7134_dev *dev = fe->dvb->priv;
-	return request_firmware(fw, name, &dev->pci->dev);
-}
-
 static struct tda1004x_config philips_tu1216_60_config = {
-
 	.demod_address = 0x8,
 	.invert        = 1,
 	.invert_oclk   = 0,
 	.xtal_freq     = TDA10046_XTAL_4M,
 	.agc_config    = TDA10046_AGC_DEFAULT,
 	.if_freq       = TDA10046_FREQ_3617,
-	.request_firmware = philips_tda1004x_request_firmware,
+	.tuner_address = 0x60,
+	.request_firmware = philips_tda1004x_request_firmware
 };
 
-/* ------------------------------------------------------------------ */
-
-static int philips_tu1216_tuner_61_init(struct dvb_frontend *fe)
-{
-	return philips_tda6651_pll_init(0x61, fe);
-}
-
-static int philips_tu1216_tuner_61_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
-{
-	return philips_tda6651_pll_set(0x61, fe, params);
-}
-
 static struct tda1004x_config philips_tu1216_61_config = {
 
 	.demod_address = 0x8,
@@ -335,7 +342,8 @@
 	.xtal_freq     = TDA10046_XTAL_4M,
 	.agc_config    = TDA10046_AGC_DEFAULT,
 	.if_freq       = TDA10046_FREQ_3617,
-	.request_firmware = philips_tda1004x_request_firmware,
+	.tuner_address = 0x61,
+	.request_firmware = philips_tda1004x_request_firmware
 };
 
 /* ------------------------------------------------------------------ */
@@ -343,24 +351,42 @@
 static int philips_td1316_tuner_init(struct dvb_frontend *fe)
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
+	struct tda1004x_state *state = fe->demodulator_priv;
+	u8 addr = state->config->tuner_address;
 	static u8 msg[] = { 0x0b, 0xf5, 0x86, 0xab };
-	struct i2c_msg init_msg = {.addr = 0x61,.flags = 0,.buf = msg,.len = sizeof(msg) };
+	struct i2c_msg init_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) };
 
 	/* setup PLL configuration */
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 1);
 	if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1)
 		return -EIO;
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 0);
 	return 0;
 }
 
 static int philips_td1316_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
 {
-	return philips_tda6651_pll_set(0x61, fe, params);
+	return philips_tda6651_pll_set(fe, params);
 }
 
+static int philips_td1316_tuner_sleep(struct dvb_frontend *fe)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+	struct tda1004x_state *state = fe->demodulator_priv;
+	u8 addr = state->config->tuner_address;
+	static u8 msg[] = { 0x0b, 0xdc, 0x86, 0xa4 };
+	struct i2c_msg analog_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) };
+
+	/* switch the tuner to analog mode */
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&dev->i2c_adap, &analog_msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
 static int philips_europa_tuner_init(struct dvb_frontend *fe)
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
@@ -380,18 +406,14 @@
 static int philips_europa_tuner_sleep(struct dvb_frontend *fe)
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
-	/* this message actually turns the tuner back to analog mode */
-	static u8 msg[] = { 0x0b, 0xdc, 0x86, 0xa4 };
-	struct i2c_msg analog_msg = {.addr = 0x61,.flags = 0,.buf = msg,.len = sizeof(msg) };
 
-	i2c_transfer(&dev->i2c_adap, &analog_msg, 1);
-	msleep(1);
+	static u8 msg[] = { 0x00, 0x14 };
+	struct i2c_msg analog_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) };
+
+	if (philips_td1316_tuner_sleep(fe))
+		return -EIO;
 
 	/* switch the board to analog mode */
-	analog_msg.addr = 0x43;
-	analog_msg.len  = 0x02;
-	msg[0] = 0x00;
-	msg[1] = 0x14;
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 1);
 	i2c_transfer(&dev->i2c_adap, &analog_msg, 1);
@@ -416,7 +438,8 @@
 	.xtal_freq     = TDA10046_XTAL_4M,
 	.agc_config    = TDA10046_AGC_IFO_AUTO_POS,
 	.if_freq       = TDA10046_FREQ_052,
-	.request_firmware = NULL,
+	.tuner_address = 0x61,
+	.request_firmware = philips_tda1004x_request_firmware
 };
 
 /* ------------------------------------------------------------------ */
@@ -424,9 +447,11 @@
 static int philips_fmd1216_tuner_init(struct dvb_frontend *fe)
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
+	struct tda1004x_state *state = fe->demodulator_priv;
+	u8 addr = state->config->tuner_address;
 	/* this message is to set up ATC and ALC */
 	static u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0xa0 };
-	struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) };
+	struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) };
 
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 1);
@@ -440,9 +465,11 @@
 static int philips_fmd1216_tuner_sleep(struct dvb_frontend *fe)
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
+	struct tda1004x_state *state = fe->demodulator_priv;
+	u8 addr = state->config->tuner_address;
 	/* this message actually turns the tuner back to analog mode */
-	static u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0x60 };
-	struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) };
+	u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0x60 };
+	struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) };
 
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 1);
@@ -460,8 +487,10 @@
 static int philips_fmd1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
+	struct tda1004x_state *state = fe->demodulator_priv;
+	u8 addr = state->config->tuner_address;
 	u8 tuner_buf[4];
-	struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = tuner_buf,.len =
+	struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tuner_buf,.len =
 			sizeof(tuner_buf) };
 	int tuner_frequency = 0;
 	int divider = 0;
@@ -536,8 +565,11 @@
 
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 1);
-	if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1)
+	if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) {
+		wprintk("could not write to tuner at addr: 0x%02x\n",
+			addr << 1);
 		return -EIO;
+	}
 	return 0;
 }
 
@@ -548,484 +580,323 @@
 	.xtal_freq     = TDA10046_XTAL_16M,
 	.agc_config    = TDA10046_AGC_IFO_AUTO_NEG,
 	.if_freq       = TDA10046_FREQ_3613,
-	.request_firmware = NULL,
+	.tuner_address = 0x61,
+	.request_firmware = philips_tda1004x_request_firmware
 };
 
-/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------
+ * tda 1004x based cards with philips silicon tuner
+ */
 
-struct tda827x_data {
-	u32 lomax;
-	u8  spd;
-	u8  bs;
-	u8  bp;
-	u8  cp;
-	u8  gc3;
-	u8 div1p5;
-};
-
-static struct tda827x_data tda827x_dvbt[] = {
-	{ .lomax =  62000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1},
-	{ .lomax =  66000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1},
-	{ .lomax =  76000000, .spd = 3, .bs = 1, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0},
-	{ .lomax =  84000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0},
-	{ .lomax =  93000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0},
-	{ .lomax =  98000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0},
-	{ .lomax = 109000000, .spd = 3, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0},
-	{ .lomax = 123000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1},
-	{ .lomax = 133000000, .spd = 2, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1},
-	{ .lomax = 151000000, .spd = 2, .bs = 1, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0},
-	{ .lomax = 154000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0},
-	{ .lomax = 181000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 0, .div1p5 = 0},
-	{ .lomax = 185000000, .spd = 2, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0},
-	{ .lomax = 217000000, .spd = 2, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0},
-	{ .lomax = 244000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1},
-	{ .lomax = 265000000, .spd = 1, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1},
-	{ .lomax = 302000000, .spd = 1, .bs = 1, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0},
-	{ .lomax = 324000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0},
-	{ .lomax = 370000000, .spd = 1, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0},
-	{ .lomax = 454000000, .spd = 1, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0},
-	{ .lomax = 493000000, .spd = 0, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1},
-	{ .lomax = 530000000, .spd = 0, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1},
-	{ .lomax = 554000000, .spd = 0, .bs = 1, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0},
-	{ .lomax = 604000000, .spd = 0, .bs = 1, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0},
-	{ .lomax = 696000000, .spd = 0, .bs = 2, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0},
-	{ .lomax = 740000000, .spd = 0, .bs = 2, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0},
-	{ .lomax = 820000000, .spd = 0, .bs = 3, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0},
-	{ .lomax = 865000000, .spd = 0, .bs = 3, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0},
-	{ .lomax =         0, .spd = 0, .bs = 0, .bp = 0, .cp = 0, .gc3 = 0, .div1p5 = 0}
-};
-
-static int philips_tda827x_tuner_init(struct dvb_frontend *fe)
-{
-	return 0;
-}
-
-static int philips_tda827x_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+static void philips_tda827x_lna_gain(struct dvb_frontend *fe, int high)
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
-	u8 tuner_buf[14];
+	struct tda1004x_state *state = fe->demodulator_priv;
+	u8 addr = state->config->i2c_gate;
+	u8 config = state->config->tuner_config;
+	u8 GP00_CF[] = {0x20, 0x01};
+	u8 GP00_LEV[] = {0x22, 0x00};
 
-	struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf,
-					.len = sizeof(tuner_buf) };
-	int i, tuner_freq, if_freq;
-	u32 N;
-	switch (params->u.ofdm.bandwidth) {
-	case BANDWIDTH_6_MHZ:
-		if_freq = 4000000;
+	struct i2c_msg msg = {.addr = addr,.flags = 0,.buf = GP00_CF, .len = 2};
+	if (config) {
+		if (high) {
+			dprintk("setting LNA to high gain\n");
+		} else {
+			dprintk("setting LNA to low gain\n");
+		}
+	}
+	switch (config) {
+	case 0: /* no LNA */
 		break;
-	case BANDWIDTH_7_MHZ:
-		if_freq = 4500000;
+	case 1: /* switch is GPIO 0 of tda8290 */
+	case 2:
+		/* turn Vsync off */
+		saa7134_set_gpio(dev, 22, 0);
+		GP00_LEV[1] = high ? 0 : 1;
+		if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) {
+			wprintk("could not access tda8290 at addr: 0x%02x\n",
+				addr << 1);
+			return;
+		}
+		msg.buf = GP00_LEV;
+		if (config == 2)
+			GP00_LEV[1] = high ? 1 : 0;
+		i2c_transfer(&dev->i2c_adap, &msg, 1);
 		break;
-	default:		   /* 8 MHz or Auto */
-		if_freq = 5000000;
+	case 3: /* switch with GPIO of saa713x */
+		saa7134_set_gpio(dev, 22, high);
 		break;
 	}
-	tuner_freq = params->frequency + if_freq;
-
-	i = 0;
-	while (tda827x_dvbt[i].lomax < tuner_freq) {
-		if(tda827x_dvbt[i + 1].lomax == 0)
-			break;
-		i++;
-	}
-
-	N = ((tuner_freq + 125000) / 250000) << (tda827x_dvbt[i].spd + 2);
-	tuner_buf[0] = 0;
-	tuner_buf[1] = (N>>8) | 0x40;
-	tuner_buf[2] = N & 0xff;
-	tuner_buf[3] = 0;
-	tuner_buf[4] = 0x52;
-	tuner_buf[5] = (tda827x_dvbt[i].spd << 6) + (tda827x_dvbt[i].div1p5 << 5) +
-				   (tda827x_dvbt[i].bs << 3) + tda827x_dvbt[i].bp;
-	tuner_buf[6] = (tda827x_dvbt[i].gc3 << 4) + 0x8f;
-	tuner_buf[7] = 0xbf;
-	tuner_buf[8] = 0x2a;
-	tuner_buf[9] = 0x05;
-	tuner_buf[10] = 0xff;
-	tuner_buf[11] = 0x00;
-	tuner_buf[12] = 0x00;
-	tuner_buf[13] = 0x40;
-
-	tuner_msg.len = 14;
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1)
-		return -EIO;
-
-	msleep(500);
-	/* correct CP value */
-	tuner_buf[0] = 0x30;
-	tuner_buf[1] = 0x50 + tda827x_dvbt[i].cp;
-	tuner_msg.len = 2;
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	i2c_transfer(&dev->i2c_adap, &tuner_msg, 1);
-
-	return 0;
 }
 
-static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe)
+static int tda8290_i2c_gate_ctrl( struct dvb_frontend* fe, int enable)
 {
-	struct saa7134_dev *dev = fe->dvb->priv;
-	static u8 tda827x_sleep[] = { 0x30, 0xd0};
-	struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tda827x_sleep,
-				    .len = sizeof(tda827x_sleep) };
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	i2c_transfer(&dev->i2c_adap, &tuner_msg, 1);
-	return 0;
-}
+	struct tda1004x_state *state = fe->demodulator_priv;
 
-static struct tda1004x_config tda827x_lifeview_config = {
-	.demod_address = 0x08,
-	.invert        = 1,
-	.invert_oclk   = 0,
-	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X_GP11,
-	.if_freq       = TDA10046_FREQ_045,
-	.request_firmware = NULL,
-};
-
-/* ------------------------------------------------------------------ */
-
-struct tda827xa_data {
-	u32 lomax;
-	u8  svco;
-	u8  spd;
-	u8  scr;
-	u8  sbs;
-	u8  gc3;
-};
-
-static struct tda827xa_data tda827xa_dvbt[] = {
-	{ .lomax =  56875000, .svco = 3, .spd = 4, .scr = 0, .sbs = 0, .gc3 = 1},
-	{ .lomax =  67250000, .svco = 0, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1},
-	{ .lomax =  81250000, .svco = 1, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1},
-	{ .lomax =  97500000, .svco = 2, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1},
-	{ .lomax = 113750000, .svco = 3, .spd = 3, .scr = 0, .sbs = 1, .gc3 = 1},
-	{ .lomax = 134500000, .svco = 0, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
-	{ .lomax = 154000000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
-	{ .lomax = 162500000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
-	{ .lomax = 183000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
-	{ .lomax = 195000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1},
-	{ .lomax = 227500000, .svco = 3, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1},
-	{ .lomax = 269000000, .svco = 0, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1},
-	{ .lomax = 290000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1},
-	{ .lomax = 325000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1},
-	{ .lomax = 390000000, .svco = 2, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1},
-	{ .lomax = 455000000, .svco = 3, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1},
-	{ .lomax = 520000000, .svco = 0, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1},
-	{ .lomax = 538000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1},
-	{ .lomax = 550000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1},
-	{ .lomax = 620000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
-	{ .lomax = 650000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
-	{ .lomax = 700000000, .svco = 2, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
-	{ .lomax = 780000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
-	{ .lomax = 820000000, .svco = 3, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
-	{ .lomax = 870000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
-	{ .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 0},
-	{ .lomax =         0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0}};
-
-
-static int philips_tda827xa_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
-{
-	struct saa7134_dev *dev = fe->dvb->priv;
-	u8 tuner_buf[14];
-	unsigned char reg2[2];
-
-	struct i2c_msg msg = {.addr = addr,.flags = 0,.buf = tuner_buf};
-	int i, tuner_freq, if_freq;
-	u32 N;
-
-	switch (params->u.ofdm.bandwidth) {
-	case BANDWIDTH_6_MHZ:
-		if_freq = 4000000;
-		break;
-	case BANDWIDTH_7_MHZ:
-		if_freq = 4500000;
-		break;
-	default:		   /* 8 MHz or Auto */
-		if_freq = 5000000;
-		break;
-	}
-	tuner_freq = params->frequency + if_freq;
-
-	i = 0;
-	while (tda827xa_dvbt[i].lomax < tuner_freq) {
-		if(tda827xa_dvbt[i + 1].lomax == 0)
-			break;
-		i++;
-	}
-
-	N = ((tuner_freq + 31250) / 62500) << tda827xa_dvbt[i].spd;
-	tuner_buf[0] = 0;            // subaddress
-	tuner_buf[1] = N >> 8;
-	tuner_buf[2] = N & 0xff;
-	tuner_buf[3] = 0;
-	tuner_buf[4] = 0x16;
-	tuner_buf[5] = (tda827xa_dvbt[i].spd << 5) + (tda827xa_dvbt[i].svco << 3) +
-			tda827xa_dvbt[i].sbs;
-	tuner_buf[6] = 0x4b + (tda827xa_dvbt[i].gc3 << 4);
-	tuner_buf[7] = 0x0c;
-	tuner_buf[8] = 0x06;
-	tuner_buf[9] = 0x24;
-	tuner_buf[10] = 0xff;
-	tuner_buf[11] = 0x60;
-	tuner_buf[12] = 0x00;
-	tuner_buf[13] = 0x39;  // lpsel
-	msg.len = 14;
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1)
-		return -EIO;
-
-	msg.buf= reg2;
-	msg.len = 2;
-	reg2[0] = 0x60;
-	reg2[1] = 0x3c;
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	i2c_transfer(&dev->i2c_adap, &msg, 1);
-
-	reg2[0] = 0xa0;
-	reg2[1] = 0x40;
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	i2c_transfer(&dev->i2c_adap, &msg, 1);
-
-	msleep(2);
-	/* correct CP value */
-	reg2[0] = 0x30;
-	reg2[1] = 0x10 + tda827xa_dvbt[i].scr;
-	msg.len = 2;
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	i2c_transfer(&dev->i2c_adap, &msg, 1);
-
-	msleep(550);
-	reg2[0] = 0x50;
-	reg2[1] = 0x4f + (tda827xa_dvbt[i].gc3 << 4);
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	i2c_transfer(&dev->i2c_adap, &msg, 1);
-
-	return 0;
-
-}
-
-static int philips_tda827xa_tuner_sleep(u8 addr, struct dvb_frontend *fe)
-{
-	struct saa7134_dev *dev = fe->dvb->priv;
-	static u8 tda827xa_sleep[] = { 0x30, 0x90};
-	struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tda827xa_sleep,
-				    .len = sizeof(tda827xa_sleep) };
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	i2c_transfer(&dev->i2c_adap, &tuner_msg, 1);
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 0);
-	return 0;
-}
-
-/* ------------------------------------------------------------------ */
-
-static int tda8290_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
-{
-	struct saa7134_dev *dev = fe->dvb->priv;
+	u8 addr = state->config->i2c_gate;
 	static u8 tda8290_close[] = { 0x21, 0xc0};
 	static u8 tda8290_open[]  = { 0x21, 0x80};
-	struct i2c_msg tda8290_msg = {.addr = 0x4b,.flags = 0, .len = 2};
+	struct i2c_msg tda8290_msg = {.addr = addr,.flags = 0, .len = 2};
 	if (enable) {
 		tda8290_msg.buf = tda8290_close;
 	} else {
 		tda8290_msg.buf = tda8290_open;
 	}
-	if (i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1) != 1)
+	if (i2c_transfer(state->i2c, &tda8290_msg, 1) != 1) {
+		struct saa7134_dev *dev = fe->dvb->priv;
+		wprintk("could not access tda8290 I2C gate\n");
 		return -EIO;
+	}
 	msleep(20);
 	return 0;
 }
 
 /* ------------------------------------------------------------------ */
 
-static int philips_tiger_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
-{
-	int ret;
-
-	ret = philips_tda827xa_pll_set(0x61, fe, params);
-	if (ret != 0)
-		return ret;
-	return 0;
-}
-
-static int philips_tiger_tuner_init(struct dvb_frontend *fe)
+static int philips_tda827x_tuner_init(struct dvb_frontend *fe)
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
-	static u8 data[] = { 0x3c, 0x33, 0x6a};
-	struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+	struct tda1004x_state *state = fe->demodulator_priv;
 
-	if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1)
-		return -EIO;
+	switch (state->config->antenna_switch) {
+	case 0: break;
+	case 1:	dprintk("setting GPIO21 to 0 (TV antenna?)\n");
+		saa7134_set_gpio(dev, 21, 0);
+		break;
+	case 2: dprintk("setting GPIO21 to 1 (Radio antenna?)\n");
+		saa7134_set_gpio(dev, 21, 1);
+		break;
+	}
 	return 0;
 }
 
-static int philips_tiger_tuner_sleep(struct dvb_frontend *fe)
+static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe)
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
-	static u8 data[] = { 0x3c, 0x33, 0x68};
-	struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+	struct tda1004x_state *state = fe->demodulator_priv;
 
-	i2c_transfer(&dev->i2c_adap, &msg, 1);
-	philips_tda827xa_tuner_sleep( 0x61, fe);
+	switch (state->config->antenna_switch) {
+	case 0: break;
+	case 1: dprintk("setting GPIO21 to 1 (Radio antenna?)\n");
+		saa7134_set_gpio(dev, 21, 1);
+		break;
+	case 2:	dprintk("setting GPIO21 to 0 (TV antenna?)\n");
+		saa7134_set_gpio(dev, 21, 0);
+		break;
+	}
 	return 0;
 }
 
+static struct tda827x_config tda827x_cfg = {
+	.lna_gain = philips_tda827x_lna_gain,
+	.init = philips_tda827x_tuner_init,
+	.sleep = philips_tda827x_tuner_sleep
+};
+
+static void configure_tda827x_fe(struct saa7134_dev *dev, struct tda1004x_config *tda_conf)
+{
+	dev->dvb.frontend = dvb_attach(tda10046_attach, tda_conf, &dev->i2c_adap);
+	if (dev->dvb.frontend) {
+		if (tda_conf->i2c_gate)
+			dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
+		if (dvb_attach(tda827x_attach, dev->dvb.frontend, tda_conf->tuner_address,
+						&dev->i2c_adap,&tda827x_cfg) == NULL) {
+			wprintk("no tda827x tuner found at addr: %02x\n",
+				tda_conf->tuner_address);
+		}
+	}
+}
+
+/* ------------------------------------------------------------------ */
+static struct tda1004x_config tda827x_lifeview_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP11_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.tuner_address = 0x60,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
 static struct tda1004x_config philips_tiger_config = {
 	.demod_address = 0x08,
 	.invert        = 1,
 	.invert_oclk   = 0,
 	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X_GP11,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP11_I,
 	.if_freq       = TDA10046_FREQ_045,
-	.request_firmware = NULL,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x61,
+	.tuner_config  = 0,
+	.antenna_switch= 1,
+	.request_firmware = philips_tda1004x_request_firmware
 };
-/* ------------------------------------------------------------------ */
-
-static int cinergy_ht_tuner_init(struct dvb_frontend *fe)
-{
-	struct saa7134_dev *dev = fe->dvb->priv;
-	static u8 data[] = { 0x3c, 0x33, 0x62};
-	struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
-
-	if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1)
-		return -EIO;
-	return 0;
-}
-
-static int cinergy_ht_tuner_sleep(struct dvb_frontend *fe)
-{
-	struct saa7134_dev *dev = fe->dvb->priv;
-	static u8 data[] = { 0x3c, 0x33, 0x60};
-	struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
-
-	i2c_transfer(&dev->i2c_adap, &msg, 1);
-	philips_tda827xa_tuner_sleep( 0x61, fe);
-	return 0;
-}
 
 static struct tda1004x_config cinergy_ht_config = {
 	.demod_address = 0x08,
 	.invert        = 1,
 	.invert_oclk   = 0,
 	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X_GP01,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP01_I,
 	.if_freq       = TDA10046_FREQ_045,
-	.request_firmware = NULL,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x61,
+	.tuner_config  = 0,
+	.request_firmware = philips_tda1004x_request_firmware
 };
 
-/* ------------------------------------------------------------------ */
+static struct tda1004x_config cinergy_ht_pci_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP01_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x60,
+	.tuner_config  = 0,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config philips_tiger_s_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP01_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x61,
+	.tuner_config  = 2,
+	.antenna_switch= 1,
+	.request_firmware = philips_tda1004x_request_firmware
+};
 
 static struct tda1004x_config pinnacle_pctv_310i_config = {
 	.demod_address = 0x08,
 	.invert        = 1,
 	.invert_oclk   = 0,
 	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X_GP11,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP11_I,
 	.if_freq       = TDA10046_FREQ_045,
-	.request_firmware = philips_tda1004x_request_firmware,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x61,
+	.tuner_config  = 1,
+	.request_firmware = philips_tda1004x_request_firmware
 };
 
-/* ------------------------------------------------------------------ */
-
 static struct tda1004x_config hauppauge_hvr_1110_config = {
 	.demod_address = 0x08,
 	.invert        = 1,
 	.invert_oclk   = 0,
 	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X_GP11,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP11_I,
 	.if_freq       = TDA10046_FREQ_045,
-	.request_firmware = philips_tda1004x_request_firmware,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x61,
+	.request_firmware = philips_tda1004x_request_firmware
 };
 
-/* ------------------------------------------------------------------ */
-
 static struct tda1004x_config asus_p7131_dual_config = {
 	.demod_address = 0x08,
 	.invert        = 1,
 	.invert_oclk   = 0,
 	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X_GP11,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP11_I,
 	.if_freq       = TDA10046_FREQ_045,
-	.request_firmware = philips_tda1004x_request_firmware,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x61,
+	.tuner_config  = 0,
+	.antenna_switch= 2,
+	.request_firmware = philips_tda1004x_request_firmware
 };
 
-static int asus_p7131_dual_tuner_init(struct dvb_frontend *fe)
-{
-	struct saa7134_dev *dev = fe->dvb->priv;
-	static u8 data[] = { 0x3c, 0x33, 0x6a};
-	struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
-
-	if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1)
-		return -EIO;
-	/* make sure the DVB-T antenna input is set */
-	saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0200000);
-	return 0;
-}
-
-static int asus_p7131_dual_tuner_sleep(struct dvb_frontend *fe)
-{
-	struct saa7134_dev *dev = fe->dvb->priv;
-	static u8 data[] = { 0x3c, 0x33, 0x68};
-	struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
-
-	i2c_transfer(&dev->i2c_adap, &msg, 1);
-	philips_tda827xa_tuner_sleep( 0x61, fe);
-	/* reset antenna inputs for analog usage */
-	saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0200000);
-	return 0;
-}
-
-/* ------------------------------------------------------------------ */
-
-static int lifeview_trio_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
-{
-	int ret;
-
-	ret = philips_tda827xa_pll_set(0x60, fe, params);
-	return ret;
-}
-
-static int lifeview_trio_tuner_sleep(struct dvb_frontend *fe)
-{
-	philips_tda827xa_tuner_sleep(0x60, fe);
-	return 0;
-}
-
 static struct tda1004x_config lifeview_trio_config = {
 	.demod_address = 0x09,
 	.invert        = 1,
 	.invert_oclk   = 0,
 	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X_GP00,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP00_I,
 	.if_freq       = TDA10046_FREQ_045,
-	.request_firmware = NULL,
+	.tuner_address = 0x60,
+	.request_firmware = philips_tda1004x_request_firmware
 };
 
-/* ------------------------------------------------------------------ */
+static struct tda1004x_config tevion_dvbt220rf_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP11_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.tuner_address = 0x60,
+	.request_firmware = philips_tda1004x_request_firmware
+};
 
-static int ads_duo_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
-{
-	int ret;
+static struct tda1004x_config md8800_dvbt_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP01_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x60,
+	.tuner_config  = 0,
+	.request_firmware = philips_tda1004x_request_firmware
+};
 
-	ret = philips_tda827xa_pll_set(0x61, fe, params);
-	return ret;
-}
+static struct tda1004x_config asus_p7131_4871_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP01_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x61,
+	.tuner_config  = 2,
+	.antenna_switch= 2,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config asus_p7131_hybrid_lna_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP11_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x61,
+	.tuner_config  = 2,
+	.antenna_switch= 2,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+/* ------------------------------------------------------------------
+ * special case: this card uses saa713x GPIO22 for the mode switch
+ */
 
 static int ads_duo_tuner_init(struct dvb_frontend *fe)
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
+	philips_tda827x_tuner_init(fe);
 	/* route TDA8275a AGC input to the channel decoder */
-	saa_writeb(SAA7134_GPIO_GPSTATUS2, 0x60);
+	saa7134_set_gpio(dev, 22, 1);
 	return 0;
 }
 
@@ -1033,97 +904,41 @@
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
 	/* route TDA8275a AGC input to the analog IF chip*/
-	saa_writeb(SAA7134_GPIO_GPSTATUS2, 0x20);
-	philips_tda827xa_tuner_sleep( 0x61, fe);
+	saa7134_set_gpio(dev, 22, 0);
+	philips_tda827x_tuner_sleep(fe);
 	return 0;
 }
 
+static struct tda827x_config ads_duo_cfg = {
+	.lna_gain = philips_tda827x_lna_gain,
+	.init = ads_duo_tuner_init,
+	.sleep = ads_duo_tuner_sleep
+};
+
 static struct tda1004x_config ads_tech_duo_config = {
 	.demod_address = 0x08,
 	.invert        = 1,
 	.invert_oclk   = 0,
 	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X_GP00,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP00_I,
 	.if_freq       = TDA10046_FREQ_045,
-	.request_firmware = NULL,
+	.tuner_address = 0x61,
+	.request_firmware = philips_tda1004x_request_firmware
 };
 
-/* ------------------------------------------------------------------ */
-
-static int tevion_dvb220rf_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
-{
-	int ret;
-	ret = philips_tda827xa_pll_set(0x60, fe, params);
-	return ret;
-}
-
-static int tevion_dvb220rf_tuner_sleep(struct dvb_frontend *fe)
-{
-	philips_tda827xa_tuner_sleep( 0x61, fe);
-	return 0;
-}
-
-static struct tda1004x_config tevion_dvbt220rf_config = {
-	.demod_address = 0x08,
-	.invert        = 1,
-	.invert_oclk   = 0,
-	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X_GP11,
-	.if_freq       = TDA10046_FREQ_045,
-	.request_firmware = NULL,
-};
-
-/* ------------------------------------------------------------------ */
-
-static int md8800_dvbt_analog_mode(struct dvb_frontend *fe)
-{
-	struct saa7134_dev *dev = fe->dvb->priv;
-	static u8 data[] = { 0x3c, 0x33, 0x68};
-	struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
-
-	i2c_transfer(&dev->i2c_adap, &msg, 1);
-	philips_tda827xa_tuner_sleep( 0x61, fe);
-	return 0;
-}
-
-static int md8800_dvbt_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
-{
-	int ret;
-	struct saa7134_dev *dev = fe->dvb->priv;
-	static u8 tda8290_close[] = { 0x21, 0xc0};
-	static u8 tda8290_open[]  = { 0x21, 0x80};
-	struct i2c_msg tda8290_msg = {.addr = 0x4b,.flags = 0, .len = 2};
-	/* close tda8290 i2c bridge */
-	tda8290_msg.buf = tda8290_close;
-	ret = i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1);
-	if (ret != 1)
-		return -EIO;
-	msleep(20);
-	ret = philips_tda827xa_pll_set(0x60, fe, params);
-	if (ret != 0)
-		return ret;
-	/* open tda8290 i2c bridge */
-	tda8290_msg.buf = tda8290_open;
-	i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1);
-	return ret;
-}
-
-static struct tda1004x_config md8800_dvbt_config = {
-	.demod_address = 0x08,
-	.invert        = 1,
-	.invert_oclk   = 0,
-	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X_GP11,
-	.if_freq       = TDA10046_FREQ_045,
-	.request_firmware = NULL,
-};
+/* ==================================================================
+ * tda10086 based DVB-S cards, helper functions
+ */
 
 static struct tda10086_config flydvbs = {
 	.demod_address = 0x0e,
 	.invert = 0,
 };
 
-/* ------------------------------------------------------------------ */
+/* ==================================================================
+ * nxt200x based ATSC cards, helper functions
+ */
 
 static struct nxt200x_config avertvhda180 = {
 	.demod_address    = 0x0a,
@@ -1143,10 +958,13 @@
 	.set_pll_input    = nxt200x_set_pll_input,
 };
 
-/* ------------------------------------------------------------------ */
+/* ==================================================================
+ * Core code
+ */
 
 static int dvb_init(struct saa7134_dev *dev)
 {
+	int ret;
 	/* init struct videobuf_dvb */
 	dev->ts.nr_bufs    = 32;
 	dev->ts.nr_packets = 32*4;
@@ -1160,7 +978,7 @@
 
 	switch (dev->board) {
 	case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL:
-		printk("%s: pinnacle 300i dvb setup\n",dev->name);
+		dprintk("pinnacle 300i dvb setup\n");
 		dev->dvb.frontend = dvb_attach(mt352_attach, &pinnacle_300i,
 					       &dev->i2c_adap);
 		if (dev->dvb.frontend) {
@@ -1169,7 +987,7 @@
 		break;
 	case SAA7134_BOARD_AVERMEDIA_777:
 	case SAA7134_BOARD_AVERMEDIA_A16AR:
-		printk("%s: avertv 777 dvb setup\n",dev->name);
+		dprintk("avertv 777 dvb setup\n");
 		dev->dvb.frontend = dvb_attach(mt352_attach, &avermedia_777,
 					       &dev->i2c_adap);
 		if (dev->dvb.frontend) {
@@ -1191,42 +1009,15 @@
 					       &philips_tu1216_60_config,
 					       &dev->i2c_adap);
 		if (dev->dvb.frontend) {
-			dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_tuner_60_init;
-			dev->dvb.frontend->ops.tuner_ops.set_params = philips_tu1216_tuner_60_set_params;
+			dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init;
+			dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set;
 		}
 		break;
 	case SAA7134_BOARD_FLYDVBTDUO:
-		dev->dvb.frontend = dvb_attach(tda10046_attach,
-					       &tda827x_lifeview_config,
-					       &dev->i2c_adap);
-		if (dev->dvb.frontend) {
-			dev->dvb.frontend->ops.tuner_ops.init = philips_tda827x_tuner_init;
-			dev->dvb.frontend->ops.tuner_ops.sleep = philips_tda827x_tuner_sleep;
-			dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda827x_tuner_set_params;
-		}
-		break;
 	case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS:
-		dev->dvb.frontend = dvb_attach(tda10046_attach,
-					       &tda827x_lifeview_config,
-					       &dev->i2c_adap);
-		if (dev->dvb.frontend) {
-			dev->dvb.frontend->ops.tuner_ops.init = philips_tda827x_tuner_init;
-			dev->dvb.frontend->ops.tuner_ops.sleep = philips_tda827x_tuner_sleep;
-			dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda827x_tuner_set_params;
-		}
+		configure_tda827x_fe(dev, &tda827x_lifeview_config);
 		break;
 	case SAA7134_BOARD_PHILIPS_EUROPA:
-		dev->dvb.frontend = dvb_attach(tda10046_attach,
-					       &philips_europa_config,
-					       &dev->i2c_adap);
-		if (dev->dvb.frontend) {
-			dev->original_demod_sleep = dev->dvb.frontend->ops.sleep;
-			dev->dvb.frontend->ops.sleep = philips_europa_demod_sleep;
-			dev->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init;
-			dev->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep;
-			dev->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params;
-		}
-		break;
 	case SAA7134_BOARD_VIDEOMATE_DVBT_300:
 		dev->dvb.frontend = dvb_attach(tda10046_attach,
 					       &philips_europa_config,
@@ -1244,125 +1035,61 @@
 					       &philips_tu1216_61_config,
 					       &dev->i2c_adap);
 		if (dev->dvb.frontend) {
-			dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_tuner_61_init;
-			dev->dvb.frontend->ops.tuner_ops.set_params = philips_tu1216_tuner_61_set_params;
+			dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init;
+			dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set;
 		}
 		break;
 	case SAA7134_BOARD_PHILIPS_TIGER:
-		dev->dvb.frontend = dvb_attach(tda10046_attach,
-					       &philips_tiger_config,
-					       &dev->i2c_adap);
-		if (dev->dvb.frontend) {
-			dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
-			dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init;
-			dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep;
-			dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
-		}
+		configure_tda827x_fe(dev, &philips_tiger_config);
 		break;
 	case SAA7134_BOARD_PINNACLE_PCTV_310i:
-		dev->dvb.frontend = dvb_attach(tda10046_attach,
-					       &pinnacle_pctv_310i_config,
-					       &dev->i2c_adap);
-		if (dev->dvb.frontend) {
-			dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
-			dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init;
-			dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep;
-			dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
-		}
+		configure_tda827x_fe(dev, &pinnacle_pctv_310i_config);
 		break;
 	case SAA7134_BOARD_HAUPPAUGE_HVR1110:
-		dev->dvb.frontend = dvb_attach(tda10046_attach,
-					       &hauppauge_hvr_1110_config,
-					       &dev->i2c_adap);
-		if (dev->dvb.frontend) {
-			dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
-			dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init;
-			dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep;
-			dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
-		}
+		configure_tda827x_fe(dev, &hauppauge_hvr_1110_config);
 		break;
 	case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
-		dev->dvb.frontend = dvb_attach(tda10046_attach,
-					       &asus_p7131_dual_config,
-					       &dev->i2c_adap);
-		if (dev->dvb.frontend) {
-			dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
-			dev->dvb.frontend->ops.tuner_ops.init = asus_p7131_dual_tuner_init;
-			dev->dvb.frontend->ops.tuner_ops.sleep = asus_p7131_dual_tuner_sleep;
-			dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
-		}
+		configure_tda827x_fe(dev, &asus_p7131_dual_config);
 		break;
 	case SAA7134_BOARD_FLYDVBT_LR301:
-		dev->dvb.frontend = dvb_attach(tda10046_attach,
-					       &tda827x_lifeview_config,
-					       &dev->i2c_adap);
-		if (dev->dvb.frontend) {
-			dev->dvb.frontend->ops.tuner_ops.init = philips_tda827x_tuner_init;
-			dev->dvb.frontend->ops.tuner_ops.sleep = philips_tda827x_tuner_sleep;
-			dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda827x_tuner_set_params;
-		}
+		configure_tda827x_fe(dev, &tda827x_lifeview_config);
 		break;
 	case SAA7134_BOARD_FLYDVB_TRIO:
 		if(! use_frontend) {	//terrestrial
-			dev->dvb.frontend = dvb_attach(tda10046_attach,
-						       &lifeview_trio_config,
-						       &dev->i2c_adap);
-			if (dev->dvb.frontend) {
-				dev->dvb.frontend->ops.tuner_ops.sleep = lifeview_trio_tuner_sleep;
-				dev->dvb.frontend->ops.tuner_ops.set_params =
-								lifeview_trio_tuner_set_params;
-			}
+			configure_tda827x_fe(dev, &lifeview_trio_config);
 		} else {  	      //satellite
 			dev->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap);
 			if (dev->dvb.frontend) {
 				if (dvb_attach(tda826x_attach, dev->dvb.frontend, 0x63,
 									&dev->i2c_adap, 0) == NULL) {
-					printk("%s: Lifeview Trio, No tda826x found!\n", __FUNCTION__);
+					wprintk("%s: Lifeview Trio, No tda826x found!\n", __FUNCTION__);
 				}
 				if (dvb_attach(isl6421_attach, dev->dvb.frontend, &dev->i2c_adap,
 										0x08, 0, 0) == NULL) {
-					printk("%s: Lifeview Trio, No ISL6421 found!\n", __FUNCTION__);
+					wprintk("%s: Lifeview Trio, No ISL6421 found!\n", __FUNCTION__);
 				}
 			}
 		}
 		break;
 	case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
-		dev->dvb.frontend = dvb_attach(tda10046_attach,
-					       &ads_tech_duo_config,
-					       &dev->i2c_adap);
-		if (dev->dvb.frontend) {
-			dev->dvb.frontend->ops.tuner_ops.init = ads_duo_tuner_init;
-			dev->dvb.frontend->ops.tuner_ops.sleep = ads_duo_tuner_sleep;
-			dev->dvb.frontend->ops.tuner_ops.set_params = ads_duo_tuner_set_params;
-		}
-		break;
-	case SAA7134_BOARD_TEVION_DVBT_220RF:
-		dev->dvb.frontend = dvb_attach(tda10046_attach,
-					       &tevion_dvbt220rf_config,
-					       &dev->i2c_adap);
-		if (dev->dvb.frontend) {
-			dev->dvb.frontend->ops.tuner_ops.sleep = tevion_dvb220rf_tuner_sleep;
-			dev->dvb.frontend->ops.tuner_ops.set_params = tevion_dvb220rf_tuner_set_params;
-		}
-		break;
 	case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS:
 		dev->dvb.frontend = dvb_attach(tda10046_attach,
 					       &ads_tech_duo_config,
 					       &dev->i2c_adap);
 		if (dev->dvb.frontend) {
-			dev->dvb.frontend->ops.tuner_ops.init = ads_duo_tuner_init;
-			dev->dvb.frontend->ops.tuner_ops.sleep = ads_duo_tuner_sleep;
-			dev->dvb.frontend->ops.tuner_ops.set_params = ads_duo_tuner_set_params;
+			if (dvb_attach(tda827x_attach,dev->dvb.frontend,
+				   ads_tech_duo_config.tuner_address,
+				   &dev->i2c_adap,&ads_duo_cfg) == NULL) {
+				wprintk("no tda827x tuner found at addr: %02x\n",
+					ads_tech_duo_config.tuner_address);
+			}
 		}
 		break;
+	case SAA7134_BOARD_TEVION_DVBT_220RF:
+		configure_tda827x_fe(dev, &tevion_dvbt220rf_config);
+		break;
 	case SAA7134_BOARD_MEDION_MD8800_QUADRO:
-		dev->dvb.frontend = tda10046_attach(&md8800_dvbt_config,
-						    &dev->i2c_adap);
-		if (dev->dvb.frontend) {
-			dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init;
-			dev->dvb.frontend->ops.tuner_ops.sleep = md8800_dvbt_analog_mode;
-			dev->dvb.frontend->ops.tuner_ops.set_params = md8800_dvbt_pll_set;
-		}
+		configure_tda827x_fe(dev, &md8800_dvbt_config);
 		break;
 	case SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180:
 		dev->dvb.frontend = dvb_attach(nxt200x_attach, &avertvhda180,
@@ -1386,11 +1113,11 @@
 		if (dev->dvb.frontend) {
 			if (dvb_attach(tda826x_attach, dev->dvb.frontend, 0x60,
 				       &dev->i2c_adap, 0) == NULL) {
-				printk("%s: No tda826x found!\n", __FUNCTION__);
+				wprintk("%s: No tda826x found!\n", __FUNCTION__);
 			}
 			if (dvb_attach(isl6421_attach, dev->dvb.frontend,
 				       &dev->i2c_adap, 0x08, 0, 0) == NULL) {
-				printk("%s: No ISL6421 found!\n", __FUNCTION__);
+				wprintk("%s: No ISL6421 found!\n", __FUNCTION__);
 			}
 		}
 		break;
@@ -1415,41 +1142,45 @@
 		}
 		break;
 	case SAA7134_BOARD_CINERGY_HT_PCMCIA:
-		dev->dvb.frontend = dvb_attach(tda10046_attach,
-					       &cinergy_ht_config,
-					       &dev->i2c_adap);
-		if (dev->dvb.frontend) {
-			dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
-			dev->dvb.frontend->ops.tuner_ops.init = cinergy_ht_tuner_init;
-			dev->dvb.frontend->ops.tuner_ops.sleep = cinergy_ht_tuner_sleep;
-			dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
-
-		}
+		configure_tda827x_fe(dev, &cinergy_ht_config);
 		break;
 	case SAA7134_BOARD_CINERGY_HT_PCI:
-		dev->dvb.frontend = dvb_attach(tda10046_attach,
-					       &cinergy_ht_config,
-					       &dev->i2c_adap);
-		if (dev->dvb.frontend) {
-			dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
-			dev->dvb.frontend->ops.tuner_ops.init = cinergy_ht_tuner_init;
-			dev->dvb.frontend->ops.tuner_ops.sleep = cinergy_ht_tuner_sleep;
-			dev->dvb.frontend->ops.tuner_ops.set_params = md8800_dvbt_pll_set;
-
-		}
+		configure_tda827x_fe(dev, &cinergy_ht_pci_config);
+		break;
+	case SAA7134_BOARD_PHILIPS_TIGER_S:
+		configure_tda827x_fe(dev, &philips_tiger_s_config);
+		break;
+	case SAA7134_BOARD_ASUS_P7131_4871:
+		configure_tda827x_fe(dev, &asus_p7131_4871_config);
+		break;
+	case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
+		configure_tda827x_fe(dev, &asus_p7131_hybrid_lna_config);
 		break;
 	default:
-		printk("%s: Huh? unknown DVB card?\n",dev->name);
+		wprintk("Huh? unknown DVB card?\n");
 		break;
 	}
 
 	if (NULL == dev->dvb.frontend) {
-		printk("%s: frontend initialization failed\n",dev->name);
+		printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name);
 		return -1;
 	}
 
 	/* register everything else */
-	return videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev, &dev->pci->dev);
+	ret = videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev, &dev->pci->dev);
+
+	/* this sequence is necessary to make the tda1004x load its firmware
+	 * and to enter analog mode of hybrid boards
+	 */
+	if (!ret) {
+		if (dev->dvb.frontend->ops.init)
+			dev->dvb.frontend->ops.init(dev->dvb.frontend);
+		if (dev->dvb.frontend->ops.sleep)
+			dev->dvb.frontend->ops.sleep(dev->dvb.frontend);
+		if (dev->dvb.frontend->ops.tuner_ops.sleep)
+			dev->dvb.frontend->ops.tuner_ops.sleep(dev->dvb.frontend);
+	}
+	return ret;
 }
 
 static int dvb_fini(struct saa7134_dev *dev)
diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c
index cce8da6..1cb8c70 100644
--- a/drivers/media/video/saa7134/saa7134-i2c.c
+++ b/drivers/media/video/saa7134/saa7134-i2c.c
@@ -370,6 +370,8 @@
 
 		tun_setup.type = tuner;
 		tun_setup.addr = saa7134_boards[dev->board].tuner_addr;
+		tun_setup.config = saa7134_boards[dev->board].tuner_config;
+		tun_setup.tuner_callback = saa7134_tuner_callback;
 
 		if ((tun_setup.addr == ADDR_UNSET)||(tun_setup.addr == client->addr)) {
 
@@ -445,7 +447,7 @@
 	unsigned char buf;
 	int i,rc;
 
-	for (i = 0; i < 128; i++) {
+	for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
 		c->addr = i;
 		rc = i2c_master_recv(c,&buf,0);
 		if (rc < 0)
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index 46c583f..c0de37e 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -321,6 +321,7 @@
 		mask_keydown = 0x0040000;
 		break;
 	case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
+	case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
 		ir_codes     = ir_codes_asus_pc39;
 		mask_keydown = 0x0040000;
 		rc5_gpio = 1;
diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c
index f2cb630..9985ded 100644
--- a/drivers/media/video/saa7134/saa7134-video.c
+++ b/drivers/media/video/saa7134/saa7134-video.c
@@ -26,6 +26,7 @@
 #include <linux/moduleparam.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
+#include <linux/sort.h>
 
 #include "saa7134-reg.h"
 #include "saa7134.h"
@@ -516,14 +517,12 @@
 	return 1;
 }
 
-static
-int res_check(struct saa7134_fh *fh, unsigned int bit)
+static int res_check(struct saa7134_fh *fh, unsigned int bit)
 {
 	return (fh->resources & bit);
 }
 
-static
-int res_locked(struct saa7134_dev *dev, unsigned int bit)
+static int res_locked(struct saa7134_dev *dev, unsigned int bit)
 {
 	return (dev->resources & bit);
 }
@@ -603,7 +602,14 @@
 	saa_writeb(SAA7134_RAW_DATA_GAIN,         0x40);
 	saa_writeb(SAA7134_RAW_DATA_OFFSET,       0x80);
 
-	saa7134_i2c_call_clients(dev,VIDIOC_S_STD,&norm->id);
+	/* only tell the tuner if this is a tv input */
+	if (card_in(dev,dev->ctl_input).tv) {
+		if ((card(dev).tuner_type == TUNER_PHILIPS_TDA8290)
+				&& ((card(dev).tuner_config == 1)
+				||  (card(dev).tuner_config == 2)))
+			saa7134_set_gpio(dev, 22, 5);
+		saa7134_i2c_call_clients(dev,VIDIOC_S_STD,&norm->id);
+	}
 }
 
 static void video_mux(struct saa7134_dev *dev, int input)
@@ -732,25 +738,6 @@
 	__u8  disable;
 };
 
-static void sort_cliplist(struct cliplist *cl, int entries)
-{
-	struct cliplist swap;
-	int i,j,n;
-
-	for (i = entries-2; i >= 0; i--) {
-		for (n = 0, j = 0; j <= i; j++) {
-			if (cl[j].position > cl[j+1].position) {
-				swap = cl[j];
-				cl[j] = cl[j+1];
-				cl[j+1] = swap;
-				n++;
-			}
-		}
-		if (0 == n)
-			break;
-	}
-}
-
 static void set_cliplist(struct saa7134_dev *dev, int reg,
 			struct cliplist *cl, int entries, char *name)
 {
@@ -784,15 +771,27 @@
 	return val;
 }
 
+/* Sort into smallest position first order */
+static int cliplist_cmp(const void *a, const void *b)
+{
+	const struct cliplist *cla = a;
+	const struct cliplist *clb = b;
+	if (cla->position < clb->position)
+		return -1;
+	if (cla->position > clb->position)
+		return 1;
+	return 0;
+}
+
 static int setup_clipping(struct saa7134_dev *dev, struct v4l2_clip *clips,
 			  int nclips, int interlace)
 {
 	struct cliplist col[16], row[16];
-	int cols, rows, i;
+	int cols = 0, rows = 0, i;
 	int div = interlace ? 2 : 1;
 
-	memset(col,0,sizeof(col)); cols = 0;
-	memset(row,0,sizeof(row)); rows = 0;
+	memset(col, 0, sizeof(col));
+	memset(row, 0, sizeof(row));
 	for (i = 0; i < nclips && i < 8; i++) {
 		col[cols].position = clip_range(clips[i].c.left);
 		col[cols].enable   = (1 << i);
@@ -808,8 +807,8 @@
 		row[rows].disable  = (1 << i);
 		rows++;
 	}
-	sort_cliplist(col,cols);
-	sort_cliplist(row,rows);
+	sort(col, cols, sizeof col[0], cliplist_cmp, NULL);
+	sort(row, rows, sizeof row[0], cliplist_cmp, NULL);
 	set_cliplist(dev,0x380,col,cols,"cols");
 	set_cliplist(dev,0x384,row,rows,"rows");
 	return 0;
@@ -1261,19 +1260,14 @@
 
 static int saa7134_resource(struct saa7134_fh *fh)
 {
-	int res = 0;
+	if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return RESOURCE_VIDEO;
 
-	switch (fh->type) {
-	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-		res = RESOURCE_VIDEO;
-		break;
-	case V4L2_BUF_TYPE_VBI_CAPTURE:
-		res = RESOURCE_VBI;
-		break;
-	default:
-		BUG();
-	}
-	return res;
+	if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+		return RESOURCE_VBI;
+
+	BUG();
+	return 0;
 }
 
 static int video_open(struct inode *inode, struct file *file)
@@ -1461,8 +1455,7 @@
 	return 0;
 }
 
-static int
-video_mmap(struct file *file, struct vm_area_struct * vma)
+static int video_mmap(struct file *file, struct vm_area_struct * vma)
 {
 	struct saa7134_fh *fh = file->private_data;
 
@@ -2461,12 +2454,6 @@
 	return 0;
 }
 
-int saa7134_video_fini(struct saa7134_dev *dev)
-{
-	/* nothing */
-	return 0;
-}
-
 void saa7134_irq_video_intl(struct saa7134_dev *dev)
 {
 	static const char *st[] = {
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index b3e3957..62224cc 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -231,6 +231,10 @@
 #define SAA7134_BOARD_ENCORE_ENLTV         106
 #define SAA7134_BOARD_ENCORE_ENLTV_FM      107
 #define SAA7134_BOARD_CINERGY_HT_PCI       108
+#define SAA7134_BOARD_PHILIPS_TIGER_S      109
+#define SAA7134_BOARD_AVERMEDIA_M102	   110
+#define SAA7134_BOARD_ASUS_P7131_4871	   111
+#define SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA 112
 
 #define SAA7134_MAXBOARDS 8
 #define SAA7134_INPUT_MAX 8
@@ -280,6 +284,7 @@
 	unsigned char		radio_addr;
 
 	unsigned int            tda9887_conf;
+	unsigned int            tuner_config;
 
 	/* peripheral I/O */
 	enum saa7134_video_out  video_out;
@@ -435,6 +440,8 @@
 #ifdef VIDIOC_G_PRIORITY
 	struct v4l2_prio_state     prio;
 #endif
+	/* workstruct for loading modules */
+	struct work_struct request_module_wk;
 
 	/* insmod option/autodetected */
 	int                        autodetected;
@@ -562,6 +569,8 @@
 extern int saa7134_no_overlay;
 
 void saa7134_track_gpio(struct saa7134_dev *dev, char *msg);
+void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value);
+int saa7134_tuner_callback(void *ptr, int command, int arg);
 
 #define SAA7134_PGTABLE_SIZE 4096
 
@@ -620,7 +629,6 @@
 
 int saa7134_video_init1(struct saa7134_dev *dev);
 int saa7134_video_init2(struct saa7134_dev *dev);
-int saa7134_video_fini(struct saa7134_dev *dev);
 void saa7134_irq_video_intl(struct saa7134_dev *dev);
 void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status);
 
diff --git a/drivers/media/video/se401.c b/drivers/media/video/se401.c
index 038448f..93fb04e 100644
--- a/drivers/media/video/se401.c
+++ b/drivers/media/video/se401.c
@@ -450,6 +450,13 @@
 	}
 	for (i=0; i<SE401_NUMSBUF; i++) {
 		se401->sbuf[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL);
+		if (!se401->sbuf[i].data) {
+			for(i = i - 1; i >= 0; i--) {
+				kfree(se401->sbuf[i].data);
+				se401->sbuf[i].data = NULL;
+			}
+			return -ENOMEM;
+		}
 	}
 
 	se401->bayeroffset=0;
@@ -458,13 +465,26 @@
 	se401->scratch_overflow=0;
 	for (i=0; i<SE401_NUMSCRATCH; i++) {
 		se401->scratch[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL);
+		if (!se401->scratch[i].data) {
+			for(i = i - 1; i >= 0; i--) {
+				kfree(se401->scratch[i].data);
+				se401->scratch[i].data = NULL;
+			}
+			goto nomem_sbuf;
+		}
 		se401->scratch[i].state=BUFFER_UNUSED;
 	}
 
 	for (i=0; i<SE401_NUMSBUF; i++) {
 		urb=usb_alloc_urb(0, GFP_KERNEL);
-		if(!urb)
-			return -ENOMEM;
+		if(!urb) {
+			for(i = i - 1; i >= 0; i--) {
+				usb_kill_urb(se401->urb[i]);
+				usb_free_urb(se401->urb[i]);
+				se401->urb[i] = NULL;
+			}
+			goto nomem_scratch;
+		}
 
 		usb_fill_bulk_urb(urb, se401->dev,
 			usb_rcvbulkpipe(se401->dev, SE401_VIDEO_ENDPOINT),
@@ -482,6 +502,18 @@
 	se401->framecount=0;
 
 	return 0;
+
+ nomem_scratch:
+	for (i=0; i<SE401_NUMSCRATCH; i++) {
+		kfree(se401->scratch[i].data);
+		se401->scratch[i].data = NULL;
+	}
+ nomem_sbuf:
+	for (i=0; i<SE401_NUMSBUF; i++) {
+		kfree(se401->sbuf[i].data);
+		se401->sbuf[i].data = NULL;
+	}
+	return -ENOMEM;
 }
 
 static int se401_stop_stream(struct usb_se401 *se401)
diff --git a/drivers/media/video/sn9c102/Kconfig b/drivers/media/video/sn9c102/Kconfig
index 1a7ccb66..19204f5 100644
--- a/drivers/media/video/sn9c102/Kconfig
+++ b/drivers/media/video/sn9c102/Kconfig
@@ -1,6 +1,6 @@
 config USB_SN9C102
 	tristate "USB SN9C1xx PC Camera Controller support"
-	depends on USB && VIDEO_V4L1
+	depends on USB && VIDEO_V4L2
 	---help---
 	  Say Y here if you want support for cameras based on SONiX SN9C101,
 	  SN9C102, SN9C103, SN9C105 and SN9C120 PC Camera Controllers.
diff --git a/drivers/media/video/sn9c102/Makefile b/drivers/media/video/sn9c102/Makefile
index 30e3dfe..a56d16f 100644
--- a/drivers/media/video/sn9c102/Makefile
+++ b/drivers/media/video/sn9c102/Makefile
@@ -1,7 +1,14 @@
-sn9c102-objs    := sn9c102_core.o sn9c102_hv7131d.o sn9c102_mi0343.o \
-		   sn9c102_ov7630.o sn9c102_ov7660.o sn9c102_pas106b.o \
-		   sn9c102_pas202bcb.o sn9c102_tas5110c1b.o \
-		   sn9c102_tas5130d1b.o
+sn9c102-objs := sn9c102_core.o \
+		sn9c102_hv7131d.o \
+		sn9c102_hv7131r.o \
+		sn9c102_mi0343.o \
+		sn9c102_mi0360.o \
+		sn9c102_ov7630.o \
+		sn9c102_ov7660.o \
+		sn9c102_pas106b.o \
+		sn9c102_pas202bcb.o \
+		sn9c102_tas5110c1b.o \
+		sn9c102_tas5110d.o \
+		sn9c102_tas5130d1b.o
 
 obj-$(CONFIG_USB_SN9C102)       += sn9c102.o
-
diff --git a/drivers/media/video/sn9c102/sn9c102.h b/drivers/media/video/sn9c102/sn9c102.h
index 5428f34..680e746 100644
--- a/drivers/media/video/sn9c102/sn9c102.h
+++ b/drivers/media/video/sn9c102/sn9c102.h
@@ -78,8 +78,13 @@
 
 typedef char sn9c102_sof_header_t[62];
 
+struct sn9c102_sof_t {
+	sn9c102_sof_header_t header;
+	u16 bytesread;
+};
+
 struct sn9c102_sysfs_attr {
-	u8 reg, i2c_reg;
+	u16 reg, i2c_reg;
 	sn9c102_sof_header_t frame_header;
 };
 
@@ -112,7 +117,7 @@
 	struct v4l2_jpegcompression compression;
 
 	struct sn9c102_sysfs_attr sysfs;
-	sn9c102_sof_header_t sof_header;
+	struct sn9c102_sof_t sof;
 	u16 reg[384];
 
 	struct sn9c102_module_param module_param;
@@ -182,8 +187,8 @@
 		if ((level) == 1 || (level) == 2)                             \
 			pr_info("sn9c102: " fmt "\n", ## args);               \
 		else if ((level) == 3)                                        \
-			pr_debug("sn9c102: [%s:%d] " fmt "\n", __FUNCTION__,  \
-				 __LINE__ , ## args);                         \
+			pr_debug("sn9c102: [%s:%d] " fmt "\n",                \
+				 __FUNCTION__, __LINE__ , ## args);           \
 	}                                                                     \
 } while (0)
 #else
@@ -194,8 +199,8 @@
 
 #undef PDBG
 #define PDBG(fmt, args...)                                                    \
-dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n",                              \
-	 __FUNCTION__, __LINE__ , ## args)
+dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __FUNCTION__,   \
+	 __LINE__ , ## args)
 
 #undef PDBGG
 #define PDBGG(fmt, args...) do {;} while(0) /* placeholder */
diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c
index d0e2b40..89f8335 100644
--- a/drivers/media/video/sn9c102/sn9c102_core.c
+++ b/drivers/media/video/sn9c102/sn9c102_core.c
@@ -44,11 +44,12 @@
 /*****************************************************************************/
 
 #define SN9C102_MODULE_NAME     "V4L2 driver for SN9C1xx PC Camera Controllers"
-#define SN9C102_MODULE_AUTHOR   "(C) 2004-2006 Luca Risolia"
+#define SN9C102_MODULE_ALIAS    "sn9c1xx"
+#define SN9C102_MODULE_AUTHOR   "(C) 2004-2007 Luca Risolia"
 #define SN9C102_AUTHOR_EMAIL    "<luca.risolia@studio.unibo.it>"
 #define SN9C102_MODULE_LICENSE  "GPL"
-#define SN9C102_MODULE_VERSION  "1:1.34"
-#define SN9C102_MODULE_VERSION_CODE  KERNEL_VERSION(1, 1, 34)
+#define SN9C102_MODULE_VERSION  "1:1.39"
+#define SN9C102_MODULE_VERSION_CODE  KERNEL_VERSION(1, 1, 39)
 
 /*****************************************************************************/
 
@@ -56,6 +57,7 @@
 
 MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL);
 MODULE_DESCRIPTION(SN9C102_MODULE_NAME);
+MODULE_ALIAS(SN9C102_MODULE_ALIAS);
 MODULE_VERSION(SN9C102_MODULE_VERSION);
 MODULE_LICENSE(SN9C102_MODULE_LICENSE);
 
@@ -106,8 +108,7 @@
 		 "\n1 = critical errors"
 		 "\n2 = significant informations"
 		 "\n3 = more verbose messages"
-		 "\nLevel 3 is useful for testing only, when only "
-		 "one device is used."
+		 "\nLevel 3 is useful for testing only."
 		 "\nDefault value is "__MODULE_STRING(SN9C102_DEBUG_LEVEL)"."
 		 "\n");
 #endif
@@ -121,8 +122,8 @@
 	struct v4l2_pix_format* p = &(cam->sensor.pix_format);
 	struct v4l2_rect* r = &(cam->sensor.cropcap.bounds);
 	size_t imagesize = cam->module_param.force_munmap || io == IO_READ ?
-				 (p->width * p->height * p->priv) / 8 :
-				 (r->width * r->height * p->priv) / 8;
+			   (p->width * p->height * p->priv) / 8 :
+			   (r->width * r->height * p->priv) / 8;
 	void* buff = NULL;
 	u32 i;
 
@@ -208,27 +209,40 @@
 }
 
 /*****************************************************************************/
-
-int sn9c102_write_regs(struct sn9c102_device* cam, u8* buff, u16 index)
+/*
+ * Write a sequence of count value/register pairs.  Returns -1 after the
+ * first failed write, or 0 for no errors.
+ */
+int sn9c102_write_regs(struct sn9c102_device* cam, const u8 valreg[][2],
+		       int count)
 {
 	struct usb_device* udev = cam->usbdev;
+	u8* value = cam->control_buffer;  /* Needed for DMA'able memory */
 	int i, res;
 
-	if (index + sizeof(buff) >= ARRAY_SIZE(cam->reg))
-		return -1;
+	for (i = 0; i < count; i++) {
+		u8 index = valreg[i][1];
 
-	res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
-			      index, 0, buff, sizeof(buff),
-			      SN9C102_CTRL_TIMEOUT*sizeof(buff));
-	if (res < 0) {
-		DBG(3, "Failed to write registers (index 0x%02X, error %d)",
-		    index, res);
-		return -1;
+		/*
+		 * index is a u8, so it must be <256 and can't be out of range.
+		 * If we put in a check anyway, gcc annoys us with a warning
+		 * that our check is useless.  People get all uppity when they
+		 * see warnings in the kernel compile.
+		 */
+
+		*value = valreg[i][0];
+		res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+				      0x08, 0x41, index, 0,
+				      value, 1, SN9C102_CTRL_TIMEOUT);
+		if (res < 0) {
+			DBG(3, "Failed to write a register (value 0x%02X, "
+			       "index 0x%02X, error %d)", *value, index, res);
+			return -1;
+		}
+
+		cam->reg[index] = *value;
 	}
 
-	for (i = 0; i < sizeof(buff); i++)
-		cam->reg[index+i] = buff[i];
-
 	return 0;
 }
 
@@ -485,18 +499,43 @@
 static void*
 sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len)
 {
-	char sof_header[6] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96};
-	size_t soflen = 0, i;
+	static const char marker[6] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96};
+	const char *m = mem;
+	size_t soflen = 0, i, j;
 
 	soflen = sn9c102_sof_length(cam);
 
-	for (i = 0; (len >= soflen) && (i <= len - soflen); i++)
-		if (!memcmp(mem + i, sof_header, sizeof(sof_header))) {
-			memcpy(cam->sof_header, mem + i,
-			       sizeof(sn9c102_sof_header_t));
-				/* Skip the header */
-				return mem + i + soflen;
+	for (i = 0; i < len; i++) {
+		size_t b;
+
+		/* Read the variable part of the header */
+		if (unlikely(cam->sof.bytesread >= sizeof(marker))) {
+			cam->sof.header[cam->sof.bytesread] = *(m+i);
+			if (++cam->sof.bytesread == soflen) {
+				cam->sof.bytesread = 0;
+				return mem + i;
 			}
+			continue;
+		}
+
+		/* Search for the SOF marker (fixed part) in the header */
+		for (j = 0, b=cam->sof.bytesread; j+b < sizeof(marker); j++) {
+			if (unlikely(i+j) == len)
+				return NULL;
+			if (*(m+i+j) == marker[cam->sof.bytesread]) {
+				cam->sof.header[cam->sof.bytesread] = *(m+i+j);
+				if (++cam->sof.bytesread == sizeof(marker)) {
+					PDBGG("Bytes to analyze: %zd. SOF "
+					      "starts at byte #%zd", len, i);
+					i += j+1;
+					break;
+				}
+			} else {
+				cam->sof.bytesread = 0;
+				break;
+			}
+		}
+	}
 
 	return NULL;
 }
@@ -505,7 +544,7 @@
 static void*
 sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len)
 {
-	char eof_header[4][4] = {
+	static const u8 eof_header[4][4] = {
 		{0x00, 0x00, 0x00, 0x00},
 		{0x40, 0x00, 0x00, 0x00},
 		{0x80, 0x00, 0x00, 0x00},
@@ -513,10 +552,16 @@
 	};
 	size_t i, j;
 
+	/* The EOF header does not exist in compressed data */
 	if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X ||
 	    cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG)
-		return NULL; /* EOF header does not exist in compressed data */
+		return NULL;
 
+	/*
+	   The EOF header might cross the packet boundary, but this is not a
+	   problem, since the end of a frame is determined by checking its size
+	   in the first place.
+	*/
 	for (i = 0; (len >= 4) && (i <= len - 4); i++)
 		for (j = 0; j < ARRAY_SIZE(eof_header); j++)
 			if (!memcmp(mem + i, eof_header[j], 4))
@@ -529,7 +574,7 @@
 static void
 sn9c102_write_jpegheader(struct sn9c102_device* cam, struct sn9c102_frame_t* f)
 {
-	static u8 jpeg_header[589] = {
+	static const u8 jpeg_header[589] = {
 		0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x06, 0x04, 0x05,
 		0x06, 0x05, 0x04, 0x06, 0x06, 0x05, 0x06, 0x07, 0x07, 0x06,
 		0x08, 0x0a, 0x10, 0x0a, 0x0a, 0x09, 0x09, 0x0a, 0x14, 0x0e,
@@ -639,6 +684,7 @@
 		cam->stream = STREAM_OFF;
 		if ((*f))
 			(*f)->state = F_QUEUED;
+		cam->sof.bytesread = 0;
 		DBG(3, "Stream interrupted by application");
 		wake_up(&cam->wait_stream);
 	}
@@ -676,6 +722,7 @@
 		if (status) {
 			DBG(3, "Error in isochronous frame");
 			(*f)->state = F_ERROR;
+			cam->sof.bytesread = 0;
 			continue;
 		}
 
@@ -692,13 +739,13 @@
 				if (eof)
 					img = (eof > pos) ? eof - pos - 1 : 0;
 
-				if ((*f)->buf.bytesused+img > imagesize) {
+				if ((*f)->buf.bytesused + img > imagesize) {
 					u32 b;
 					b = (*f)->buf.bytesused + img -
 					    imagesize;
 					img = imagesize - (*f)->buf.bytesused;
-					DBG(3, "Expected EOF not found: "
-					       "video frame cut");
+					PDBGG("Expected EOF not found: video "
+					      "frame cut");
 					if (eof)
 						DBG(3, "Exceeded limit: +%u "
 						       "bytes", (unsigned)(b));
@@ -719,11 +766,6 @@
 				      V4L2_PIX_FMT_JPEG) && eof)) {
 					u32 b;
 
-					if (cam->sensor.pix_format.pixelformat
-					    == V4L2_PIX_FMT_JPEG)
-						sn9c102_write_eoimarker(cam,
-									(*f));
-
 					b = (*f)->buf.bytesused;
 					(*f)->state = F_DONE;
 					(*f)->buf.sequence= ++cam->frame_count;
@@ -741,7 +783,7 @@
 					spin_unlock(&cam->queue_lock);
 
 					memcpy(cam->sysfs.frame_header,
-					       cam->sof_header, soflen);
+					       cam->sof.header, soflen);
 
 					DBG(3, "Video frame captured: %lu "
 					       "bytes", (unsigned long)(b));
@@ -791,7 +833,13 @@
 				    V4L2_PIX_FMT_SN9C10X ||
 				    cam->sensor.pix_format.pixelformat ==
 				    V4L2_PIX_FMT_JPEG) {
-					eof = sof - soflen;
+					if (sof - pos >= soflen) {
+						eof = sof - soflen;
+					} else { /* remove header */
+						eof = pos;
+						(*f)->buf.bytesused -=
+							(soflen - (sof - pos));
+					}
 					goto end_of_frame;
 				} else {
 					DBG(3, "SOF before expected EOF after "
@@ -878,6 +926,7 @@
 	}
 
 	cam->frame_current = NULL;
+	cam->sof.bytesread = 0;
 
 	for (i = 0; i < SN9C102_URBS; i++) {
 		err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
@@ -959,9 +1008,9 @@
 
 	if (len < 6) {
 		strncpy(str, buff, len);
-		str[len+1] = '\0';
+		str[len] = '\0';
 	} else {
-		strncpy(str, buff, 4);
+		strncpy(str, buff, 6);
 		str[6] = '\0';
 	}
 
@@ -1062,7 +1111,7 @@
 
 	count = sprintf(buf, "%d\n", val);
 
-	DBG(3, "Read bytes: %zd", count);
+	DBG(3, "Read bytes: %zd, value: %d", count, val);
 
 	mutex_unlock(&sn9c102_sysfs_lock);
 
@@ -1197,7 +1246,7 @@
 
 	count = sprintf(buf, "%d\n", val);
 
-	DBG(3, "Read bytes: %zd", count);
+	DBG(3, "Read bytes: %zd, value: %d", count, val);
 
 	mutex_unlock(&sn9c102_sysfs_lock);
 
@@ -1371,35 +1420,35 @@
 
 static int sn9c102_create_sysfs(struct sn9c102_device* cam)
 {
-	struct video_device *v4ldev = cam->v4ldev;
+	struct class_device *classdev = &(cam->v4ldev->class_dev);
 	int err = 0;
 
-	if ((err = video_device_create_file(v4ldev, &class_device_attr_reg)))
+	if ((err = class_device_create_file(classdev, &class_device_attr_reg)))
 		goto err_out;
-	if ((err = video_device_create_file(v4ldev, &class_device_attr_val)))
+	if ((err = class_device_create_file(classdev, &class_device_attr_val)))
 		goto err_reg;
-	if ((err = video_device_create_file(v4ldev,
+	if ((err = class_device_create_file(classdev,
 					    &class_device_attr_frame_header)))
 		goto err_val;
 
 	if (cam->sensor.sysfs_ops) {
-		if ((err = video_device_create_file(v4ldev,
+		if ((err = class_device_create_file(classdev,
 						  &class_device_attr_i2c_reg)))
 			goto err_frame_header;
-		if ((err = video_device_create_file(v4ldev,
+		if ((err = class_device_create_file(classdev,
 						  &class_device_attr_i2c_val)))
 			goto err_i2c_reg;
 	}
 
 	if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) {
-		if ((err = video_device_create_file(v4ldev,
+		if ((err = class_device_create_file(classdev,
 						    &class_device_attr_green)))
 			goto err_i2c_val;
 	} else {
-		if ((err = video_device_create_file(v4ldev,
+		if ((err = class_device_create_file(classdev,
 						    &class_device_attr_blue)))
 			goto err_i2c_val;
-		if ((err = video_device_create_file(v4ldev,
+		if ((err = class_device_create_file(classdev,
 						    &class_device_attr_red)))
 			goto err_blue;
 	}
@@ -1407,19 +1456,19 @@
 	return 0;
 
 err_blue:
-	video_device_remove_file(v4ldev, &class_device_attr_blue);
+	class_device_remove_file(classdev, &class_device_attr_blue);
 err_i2c_val:
 	if (cam->sensor.sysfs_ops)
-		video_device_remove_file(v4ldev, &class_device_attr_i2c_val);
+		class_device_remove_file(classdev, &class_device_attr_i2c_val);
 err_i2c_reg:
 	if (cam->sensor.sysfs_ops)
-		video_device_remove_file(v4ldev, &class_device_attr_i2c_reg);
+		class_device_remove_file(classdev, &class_device_attr_i2c_reg);
 err_frame_header:
-	video_device_remove_file(v4ldev, &class_device_attr_frame_header);
+	class_device_remove_file(classdev, &class_device_attr_frame_header);
 err_val:
-	video_device_remove_file(v4ldev, &class_device_attr_val);
+	class_device_remove_file(classdev, &class_device_attr_val);
 err_reg:
-	video_device_remove_file(v4ldev, &class_device_attr_reg);
+	class_device_remove_file(classdev, &class_device_attr_reg);
 err_out:
 	return err;
 }
@@ -1477,10 +1526,10 @@
 	case BRIDGE_SN9C101:
 	case BRIDGE_SN9C102:
 	case BRIDGE_SN9C103:
-	if (compression->quality == 0)
+		if (compression->quality == 0)
 			err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01,
 						 0x17);
-	else if (compression->quality == 1)
+		else if (compression->quality == 1)
 			err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe,
 						 0x17);
 		break;
@@ -1489,10 +1538,10 @@
 		if (compression->quality == 0) {
 			for (i = 0; i <= 63; i++) {
 				err += sn9c102_write_reg(cam,
-							 SN9C102_Y_QTABLE0[i],
+							 SN9C102_Y_QTABLE1[i],
 							 0x100 + i);
 				err += sn9c102_write_reg(cam,
-							 SN9C102_UV_QTABLE0[i],
+							 SN9C102_UV_QTABLE1[i],
 							 0x140 + i);
 			}
 			err += sn9c102_write_reg(cam, cam->reg[0x18] & 0xbf,
@@ -1597,9 +1646,13 @@
 		if (cam->bridge == BRIDGE_SN9C101 ||
 		    cam->bridge == BRIDGE_SN9C102 ||
 		    cam->bridge == BRIDGE_SN9C103) {
+			if (s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG)
+				s->pix_format.pixelformat= V4L2_PIX_FMT_SBGGR8;
 			cam->compression.quality =  cam->reg[0x17] & 0x01 ?
 						    0 : 1;
 		} else {
+			if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X)
+				s->pix_format.pixelformat = V4L2_PIX_FMT_JPEG;
 			cam->compression.quality =  cam->reg[0x18] & 0x40 ?
 						    0 : 1;
 			err += sn9c102_set_compression(cam, &cam->compression);
@@ -1805,7 +1858,7 @@
 		DBG(3, "Close and open the device again to choose "
 		       "the read method");
 		mutex_unlock(&cam->fileop_mutex);
-		return -EINVAL;
+		return -EBUSY;
 	}
 
 	if (cam->io == IO_NONE) {
@@ -1845,16 +1898,16 @@
 				return err;
 			}
 		} else {
-		timeout = wait_event_interruptible_timeout
-			  ( cam->wait_frame,
-			    (!list_empty(&cam->outqueue)) ||
-			    (cam->state & DEV_DISCONNECTED) ||
-			    (cam->state & DEV_MISCONFIGURED),
-			    cam->module_param.frame_timeout *
-			    1000 * msecs_to_jiffies(1) );
-		if (timeout < 0) {
-			mutex_unlock(&cam->fileop_mutex);
-			return timeout;
+			timeout = wait_event_interruptible_timeout
+				  ( cam->wait_frame,
+				    (!list_empty(&cam->outqueue)) ||
+				    (cam->state & DEV_DISCONNECTED) ||
+				    (cam->state & DEV_MISCONFIGURED),
+				    cam->module_param.frame_timeout *
+				    1000 * msecs_to_jiffies(1) );
+			if (timeout < 0) {
+				mutex_unlock(&cam->fileop_mutex);
+				return timeout;
 			} else if (timeout == 0 &&
 				   !(cam->state & DEV_DISCONNECTED)) {
 				DBG(1, "Video frame timeout elapsed");
@@ -2001,7 +2054,12 @@
 		return -EIO;
 	}
 
-	if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) ||
+	if (!(vma->vm_flags & (VM_WRITE | VM_READ))) {
+		mutex_unlock(&cam->fileop_mutex);
+		return -EACCES;
+	}
+
+	if (cam->io != IO_MMAP ||
 	    size != PAGE_ALIGN(cam->frame[0].buf.length)) {
 		mutex_unlock(&cam->fileop_mutex);
 		return -EINVAL;
@@ -2267,7 +2325,7 @@
 			if (cam->frame[i].vma_use_count) {
 				DBG(3, "VIDIOC_S_CROP failed. "
 				       "Unmap the buffers first.");
-				return -EINVAL;
+				return -EBUSY;
 			}
 
 	/* Preserve R,G or B origin */
@@ -2410,8 +2468,8 @@
 		case BRIDGE_SN9C101:
 		case BRIDGE_SN9C102:
 		case BRIDGE_SN9C103:
-		strcpy(fmtd.description, "compressed");
-		fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X;
+			strcpy(fmtd.description, "compressed");
+			fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X;
 			break;
 		case BRIDGE_SN9C105:
 		case BRIDGE_SN9C120:
@@ -2445,8 +2503,10 @@
 	if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
 		return -EINVAL;
 
-	pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_SN9C10X ||
-			      pfmt->pixelformat==V4L2_PIX_FMT_JPEG)
+	pfmt->colorspace = (pfmt->pixelformat == V4L2_PIX_FMT_JPEG) ?
+			   V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB;
+	pfmt->bytesperline = (pfmt->pixelformat == V4L2_PIX_FMT_SN9C10X ||
+			      pfmt->pixelformat == V4L2_PIX_FMT_JPEG)
 			     ? 0 : (pfmt->width * pfmt->priv) / 8;
 	pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8);
 	pfmt->field = V4L2_FIELD_NONE;
@@ -2521,9 +2581,9 @@
 	case BRIDGE_SN9C101:
 	case BRIDGE_SN9C102:
 	case BRIDGE_SN9C103:
-	if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X &&
-	    pix->pixelformat != V4L2_PIX_FMT_SBGGR8)
-		pix->pixelformat = pfmt->pixelformat;
+		if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X &&
+		    pix->pixelformat != V4L2_PIX_FMT_SBGGR8)
+			pix->pixelformat = pfmt->pixelformat;
 		break;
 	case BRIDGE_SN9C105:
 	case BRIDGE_SN9C120:
@@ -2533,7 +2593,8 @@
 		break;
 	}
 	pix->priv = pfmt->priv; /* bpp */
-	pix->colorspace = pfmt->colorspace;
+	pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_JPEG) ?
+			  V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB;
 	pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X ||
 			     pix->pixelformat == V4L2_PIX_FMT_JPEG)
 			    ? 0 : (pix->width * pix->priv) / 8;
@@ -2551,7 +2612,7 @@
 			if (cam->frame[i].vma_use_count) {
 				DBG(3, "VIDIOC_S_FMT failed. Unmap the "
 				       "buffers first.");
-				return -EINVAL;
+				return -EBUSY;
 			}
 
 	if (cam->stream == STREAM_ON)
@@ -2666,14 +2727,14 @@
 	if (cam->io == IO_READ) {
 		DBG(3, "Close and open the device again to choose the mmap "
 		       "I/O method");
-		return -EINVAL;
+		return -EBUSY;
 	}
 
 	for (i = 0; i < cam->nbuffers; i++)
 		if (cam->frame[i].vma_use_count) {
 			DBG(3, "VIDIOC_REQBUFS failed. Previous buffers are "
 			       "still mapped.");
-			return -EINVAL;
+			return -EBUSY;
 		}
 
 	if (cam->stream == STREAM_ON)
@@ -2785,15 +2846,15 @@
 			if (err)
 				return err;
 		} else {
-		timeout = wait_event_interruptible_timeout
-			  ( cam->wait_frame,
-			    (!list_empty(&cam->outqueue)) ||
-			    (cam->state & DEV_DISCONNECTED) ||
-			    (cam->state & DEV_MISCONFIGURED),
-			    cam->module_param.frame_timeout *
-			    1000 * msecs_to_jiffies(1) );
-		if (timeout < 0)
-			return timeout;
+			timeout = wait_event_interruptible_timeout
+				  ( cam->wait_frame,
+				    (!list_empty(&cam->outqueue)) ||
+				    (cam->state & DEV_DISCONNECTED) ||
+				    (cam->state & DEV_MISCONFIGURED),
+				    cam->module_param.frame_timeout *
+				    1000 * msecs_to_jiffies(1) );
+			if (timeout < 0)
+				return timeout;
 			else if (timeout == 0 &&
 				 !(cam->state & DEV_DISCONNECTED)) {
 				DBG(1, "Video frame timeout elapsed");
@@ -2837,9 +2898,6 @@
 	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
 		return -EINVAL;
 
-	if (list_empty(&cam->inqueue))
-		return -EINVAL;
-
 	cam->stream = STREAM_ON;
 
 	DBG(3, "Stream on");
@@ -3166,8 +3224,8 @@
 
 	r = sn9c102_read_reg(cam, 0x00);
 	if (r < 0 || (r != 0x10 && r != 0x11 && r != 0x12)) {
-		DBG(1, "Sorry, this is not a SN9C1xx based camera "
-		       "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+		DBG(1, "Sorry, this is not a SN9C1xx-based camera "
+		       "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
 		err = -ENODEV;
 		goto fail;
 	}
@@ -3177,19 +3235,19 @@
 	case BRIDGE_SN9C101:
 	case BRIDGE_SN9C102:
 		DBG(2, "SN9C10[12] PC Camera Controller detected "
-		       "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+		       "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
 		break;
 	case BRIDGE_SN9C103:
 		DBG(2, "SN9C103 PC Camera Controller detected "
-		       "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+		       "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
 		break;
 	case BRIDGE_SN9C105:
 		DBG(2, "SN9C105 PC Camera Controller detected "
-		       "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+		       "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
 		break;
 	case BRIDGE_SN9C120:
 		DBG(2, "SN9C120 PC Camera Controller detected "
-		       "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+		       "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
 		break;
 	}
 
@@ -3260,6 +3318,8 @@
 		       "device controlling. Error #%d", err);
 #else
 	DBG(2, "Optional device control through 'sysfs' interface disabled");
+	DBG(3, "Compile the kernel with the 'CONFIG_VIDEO_ADV_DEBUG' "
+	       "configuration option to enable it.");
 #endif
 
 	usb_set_intfdata(intf, cam);
diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h
index 3a682ec..f49bd8c 100644
--- a/drivers/media/video/sn9c102/sn9c102_devtable.h
+++ b/drivers/media/video/sn9c102/sn9c102_devtable.h
@@ -89,16 +89,22 @@
 	{ SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), },
 	{ SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), },
 	{ SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), },
+	{ SN9C102_USB_DEVICE(0x0c45, 0x60c2, BRIDGE_SN9C105), },
 	{ SN9C102_USB_DEVICE(0x0c45, 0x60c8, BRIDGE_SN9C105), },
 	{ SN9C102_USB_DEVICE(0x0c45, 0x60cc, BRIDGE_SN9C105), },
 	{ SN9C102_USB_DEVICE(0x0c45, 0x60ea, BRIDGE_SN9C105), },
 	{ SN9C102_USB_DEVICE(0x0c45, 0x60ec, BRIDGE_SN9C105), },
+	{ SN9C102_USB_DEVICE(0x0c45, 0x60ef, BRIDGE_SN9C105), },
 	{ SN9C102_USB_DEVICE(0x0c45, 0x60fa, BRIDGE_SN9C105), },
 	{ SN9C102_USB_DEVICE(0x0c45, 0x60fb, BRIDGE_SN9C105), },
 	{ SN9C102_USB_DEVICE(0x0c45, 0x60fc, BRIDGE_SN9C105), },
 	{ SN9C102_USB_DEVICE(0x0c45, 0x60fe, BRIDGE_SN9C105), },
 	/* SN9C120 */
+	{ SN9C102_USB_DEVICE(0x0c45, 0x6102, BRIDGE_SN9C120), },
+	{ SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), },
+	{ SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), },
 	{ SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), },
+	{ SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), },
 	{ SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), },
 	{ SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), },
 	{ SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), },
@@ -114,12 +120,15 @@
    Functions must return 0 on success, the appropriate error otherwise.
 */
 extern int sn9c102_probe_hv7131d(struct sn9c102_device* cam);
+extern int sn9c102_probe_hv7131r(struct sn9c102_device* cam);
 extern int sn9c102_probe_mi0343(struct sn9c102_device* cam);
+extern int sn9c102_probe_mi0360(struct sn9c102_device* cam);
 extern int sn9c102_probe_ov7630(struct sn9c102_device* cam);
 extern int sn9c102_probe_ov7660(struct sn9c102_device* cam);
 extern int sn9c102_probe_pas106b(struct sn9c102_device* cam);
 extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam);
 extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam);
+extern int sn9c102_probe_tas5110d(struct sn9c102_device* cam);
 extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam);
 
 /*
@@ -128,13 +137,16 @@
    the order of the list below, from top to bottom.
 */
 static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = {
+	&sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */
+	&sn9c102_probe_hv7131r, /* strong detection based on SENSOR ids */
 	&sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */
+	&sn9c102_probe_mi0360, /* strong detection based on SENSOR ids */
 	&sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */
 	&sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */
-	&sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */
 	&sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */
 	&sn9c102_probe_ov7660, /* strong detection based on SENSOR ids */
 	&sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */
+	&sn9c102_probe_tas5110d, /* detection based on USB pid/vid */
 	&sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */
 	NULL,
 };
diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131d.c b/drivers/media/video/sn9c102/sn9c102_hv7131d.c
index 7ae368f..28a861a 100644
--- a/drivers/media/video/sn9c102/sn9c102_hv7131d.c
+++ b/drivers/media/video/sn9c102/sn9c102_hv7131d.c
@@ -22,19 +22,13 @@
 #include "sn9c102_sensor.h"
 
 
-static struct sn9c102_sensor hv7131d;
-
-
 static int hv7131d_init(struct sn9c102_device* cam)
 {
-	int err = 0;
+	int err;
 
-	err += sn9c102_write_reg(cam, 0x00, 0x10);
-	err += sn9c102_write_reg(cam, 0x00, 0x11);
-	err += sn9c102_write_reg(cam, 0x00, 0x14);
-	err += sn9c102_write_reg(cam, 0x60, 0x17);
-	err += sn9c102_write_reg(cam, 0x0e, 0x18);
-	err += sn9c102_write_reg(cam, 0xf2, 0x19);
+	err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11},
+				       {0x00, 0x14}, {0x60, 0x17},
+				       {0x0e, 0x18}, {0xf2, 0x19});
 
 	err += sn9c102_i2c_write(cam, 0x01, 0x04);
 	err += sn9c102_i2c_write(cam, 0x02, 0x00);
@@ -153,7 +147,7 @@
 static struct sn9c102_sensor hv7131d = {
 	.name = "HV7131D",
 	.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
-	.supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103,
+	.supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102,
 	.sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
 	.frequency = SN9C102_I2C_100KHZ,
 	.interface = SN9C102_I2C_2WIRES,
@@ -250,11 +244,10 @@
 
 int sn9c102_probe_hv7131d(struct sn9c102_device* cam)
 {
-	int r0 = 0, r1 = 0, err = 0;
+	int r0 = 0, r1 = 0, err;
 
-	err += sn9c102_write_reg(cam, 0x01, 0x01);
-	err += sn9c102_write_reg(cam, 0x00, 0x01);
-	err += sn9c102_write_reg(cam, 0x28, 0x17);
+	err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01},
+				       {0x28, 0x17});
 	if (err)
 		return -EIO;
 
@@ -263,7 +256,7 @@
 	if (r0 < 0 || r1 < 0)
 		return -EIO;
 
-	if (r0 != 0x00 && r1 != 0x04)
+	if (r0 != 0x00 || r1 != 0x04)
 		return -ENODEV;
 
 	sn9c102_attach_sensor(cam, &hv7131d);
diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131r.c b/drivers/media/video/sn9c102/sn9c102_hv7131r.c
new file mode 100644
index 0000000..5a495ba
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_hv7131r.c
@@ -0,0 +1,366 @@
+/***************************************************************************
+ * Plug-in for HV7131R image sensor connected to the SN9C1xx PC Camera     *
+ * Controllers                                                             *
+ *                                                                         *
+ * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it>       *
+ *                                                                         *
+ * 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 "sn9c102_sensor.h"
+
+
+static int hv7131r_init(struct sn9c102_device* cam)
+{
+	int err = 0;
+
+	switch (sn9c102_get_bridge(cam)) {
+	case BRIDGE_SN9C103:
+		err = sn9c102_write_const_regs(cam, {0x00, 0x03}, {0x1a, 0x04},
+					       {0x20, 0x05}, {0x20, 0x06},
+					       {0x03, 0x10}, {0x00, 0x14},
+					       {0x60, 0x17}, {0x0a, 0x18},
+					       {0xf0, 0x19}, {0x1d, 0x1a},
+					       {0x10, 0x1b}, {0x02, 0x1c},
+					       {0x03, 0x1d}, {0x0f, 0x1e},
+					       {0x0c, 0x1f}, {0x00, 0x20},
+					       {0x10, 0x21}, {0x20, 0x22},
+					       {0x30, 0x23}, {0x40, 0x24},
+					       {0x50, 0x25}, {0x60, 0x26},
+					       {0x70, 0x27}, {0x80, 0x28},
+					       {0x90, 0x29}, {0xa0, 0x2a},
+					       {0xb0, 0x2b}, {0xc0, 0x2c},
+					       {0xd0, 0x2d}, {0xe0, 0x2e},
+					       {0xf0, 0x2f}, {0xff, 0x30});
+
+		break;
+	case BRIDGE_SN9C105:
+	case BRIDGE_SN9C120:
+		err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02},
+					       {0x00, 0x03}, {0x1a, 0x04},
+					       {0x44, 0x05}, {0x3e, 0x06},
+					       {0x1a, 0x07}, {0x03, 0x10},
+					       {0x08, 0x14}, {0xa3, 0x17},
+					       {0x4b, 0x18}, {0x00, 0x19},
+					       {0x1d, 0x1a}, {0x10, 0x1b},
+					       {0x02, 0x1c}, {0x03, 0x1d},
+					       {0x0f, 0x1e}, {0x0c, 0x1f},
+					       {0x00, 0x20}, {0x29, 0x21},
+					       {0x40, 0x22}, {0x54, 0x23},
+					       {0x66, 0x24}, {0x76, 0x25},
+					       {0x85, 0x26}, {0x94, 0x27},
+					       {0xa1, 0x28}, {0xae, 0x29},
+					       {0xbb, 0x2a}, {0xc7, 0x2b},
+					       {0xd3, 0x2c}, {0xde, 0x2d},
+					       {0xea, 0x2e}, {0xf4, 0x2f},
+					       {0xff, 0x30}, {0x00, 0x3F},
+					       {0xC7, 0x40}, {0x01, 0x41},
+					       {0x44, 0x42}, {0x00, 0x43},
+					       {0x44, 0x44}, {0x00, 0x45},
+					       {0x44, 0x46}, {0x00, 0x47},
+					       {0xC7, 0x48}, {0x01, 0x49},
+					       {0xC7, 0x4A}, {0x01, 0x4B},
+					       {0xC7, 0x4C}, {0x01, 0x4D},
+					       {0x44, 0x4E}, {0x00, 0x4F},
+					       {0x44, 0x50}, {0x00, 0x51},
+					       {0x44, 0x52}, {0x00, 0x53},
+					       {0xC7, 0x54}, {0x01, 0x55},
+					       {0xC7, 0x56}, {0x01, 0x57},
+					       {0xC7, 0x58}, {0x01, 0x59},
+					       {0x44, 0x5A}, {0x00, 0x5B},
+					       {0x44, 0x5C}, {0x00, 0x5D},
+					       {0x44, 0x5E}, {0x00, 0x5F},
+					       {0xC7, 0x60}, {0x01, 0x61},
+					       {0xC7, 0x62}, {0x01, 0x63},
+					       {0xC7, 0x64}, {0x01, 0x65},
+					       {0x44, 0x66}, {0x00, 0x67},
+					       {0x44, 0x68}, {0x00, 0x69},
+					       {0x44, 0x6A}, {0x00, 0x6B},
+					       {0xC7, 0x6C}, {0x01, 0x6D},
+					       {0xC7, 0x6E}, {0x01, 0x6F},
+					       {0xC7, 0x70}, {0x01, 0x71},
+					       {0x44, 0x72}, {0x00, 0x73},
+					       {0x44, 0x74}, {0x00, 0x75},
+					       {0x44, 0x76}, {0x00, 0x77},
+					       {0xC7, 0x78}, {0x01, 0x79},
+					       {0xC7, 0x7A}, {0x01, 0x7B},
+					       {0xC7, 0x7C}, {0x01, 0x7D},
+					       {0x44, 0x7E}, {0x00, 0x7F},
+					       {0x14, 0x84}, {0x00, 0x85},
+					       {0x27, 0x86}, {0x00, 0x87},
+					       {0x07, 0x88}, {0x00, 0x89},
+					       {0xEC, 0x8A}, {0x0f, 0x8B},
+					       {0xD8, 0x8C}, {0x0f, 0x8D},
+					       {0x3D, 0x8E}, {0x00, 0x8F},
+					       {0x3D, 0x90}, {0x00, 0x91},
+					       {0xCD, 0x92}, {0x0f, 0x93},
+					       {0xf7, 0x94}, {0x0f, 0x95},
+					       {0x0C, 0x96}, {0x00, 0x97},
+					       {0x00, 0x98}, {0x66, 0x99},
+					       {0x05, 0x9A}, {0x00, 0x9B},
+					       {0x04, 0x9C}, {0x00, 0x9D},
+					       {0x08, 0x9E}, {0x00, 0x9F},
+					       {0x2D, 0xC0}, {0x2D, 0xC1},
+					       {0x3A, 0xC2}, {0x05, 0xC3},
+					       {0x04, 0xC4}, {0x3F, 0xC5},
+					       {0x00, 0xC6}, {0x00, 0xC7},
+					       {0x50, 0xC8}, {0x3C, 0xC9},
+					       {0x28, 0xCA}, {0xD8, 0xCB},
+					       {0x14, 0xCC}, {0xEC, 0xCD},
+					       {0x32, 0xCE}, {0xDD, 0xCF},
+					       {0x32, 0xD0}, {0xDD, 0xD1},
+					       {0x6A, 0xD2}, {0x50, 0xD3},
+					       {0x00, 0xD4}, {0x00, 0xD5},
+					       {0x00, 0xD6});
+		break;
+	default:
+		break;
+	}
+
+	err += sn9c102_i2c_write(cam, 0x20, 0x00);
+	err += sn9c102_i2c_write(cam, 0x21, 0xd6);
+	err += sn9c102_i2c_write(cam, 0x25, 0x06);
+
+	return err;
+}
+
+
+static int hv7131r_get_ctrl(struct sn9c102_device* cam,
+			    struct v4l2_control* ctrl)
+{
+	switch (ctrl->id) {
+	case V4L2_CID_GAIN:
+		if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0)
+			return -EIO;
+		return 0;
+	case V4L2_CID_RED_BALANCE:
+		if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0)
+			return -EIO;
+		ctrl->value = ctrl->value & 0x3f;
+		return 0;
+	case V4L2_CID_BLUE_BALANCE:
+		if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0)
+			return -EIO;
+		ctrl->value = ctrl->value & 0x3f;
+		return 0;
+	case SN9C102_V4L2_CID_GREEN_BALANCE:
+		if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0)
+			return -EIO;
+		ctrl->value = ctrl->value & 0x3f;
+		return 0;
+	case V4L2_CID_BLACK_LEVEL:
+		if ((ctrl->value = sn9c102_i2c_read(cam, 0x01)) < 0)
+			return -EIO;
+		ctrl->value = (ctrl->value & 0x08) ? 1 : 0;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+
+static int hv7131r_set_ctrl(struct sn9c102_device* cam,
+			    const struct v4l2_control* ctrl)
+{
+	int err = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_GAIN:
+		err += sn9c102_i2c_write(cam, 0x30, ctrl->value);
+		break;
+	case V4L2_CID_RED_BALANCE:
+		err += sn9c102_i2c_write(cam, 0x31, ctrl->value);
+		break;
+	case V4L2_CID_BLUE_BALANCE:
+		err += sn9c102_i2c_write(cam, 0x33, ctrl->value);
+		break;
+	case SN9C102_V4L2_CID_GREEN_BALANCE:
+		err += sn9c102_i2c_write(cam, 0x32, ctrl->value);
+		break;
+	case V4L2_CID_BLACK_LEVEL:
+		{
+			int r = sn9c102_i2c_read(cam, 0x01);
+			if (r < 0)
+				return -EIO;
+			err += sn9c102_i2c_write(cam, 0x01,
+						 (ctrl->value<<3) | (r&0xf7));
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return err ? -EIO : 0;
+}
+
+
+static int hv7131r_set_crop(struct sn9c102_device* cam,
+			    const struct v4l2_rect* rect)
+{
+	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+	int err = 0;
+	u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1,
+	   v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1;
+
+	err += sn9c102_write_reg(cam, h_start, 0x12);
+	err += sn9c102_write_reg(cam, v_start, 0x13);
+
+	return err;
+}
+
+
+static int hv7131r_set_pix_format(struct sn9c102_device* cam,
+				  const struct v4l2_pix_format* pix)
+{
+	int err = 0;
+
+	switch (sn9c102_get_bridge(cam)) {
+	case BRIDGE_SN9C103:
+		if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) {
+			err += sn9c102_write_reg(cam, 0xa0, 0x19);
+			err += sn9c102_i2c_write(cam, 0x01, 0x04);
+		} else {
+			err += sn9c102_write_reg(cam, 0x30, 0x19);
+			err += sn9c102_i2c_write(cam, 0x01, 0x04);
+		}
+		break;
+	case BRIDGE_SN9C105:
+	case BRIDGE_SN9C120:
+		if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) {
+			err += sn9c102_write_reg(cam, 0xa5, 0x17);
+			err += sn9c102_i2c_write(cam, 0x01, 0x24);
+		} else {
+			err += sn9c102_write_reg(cam, 0xa3, 0x17);
+			err += sn9c102_i2c_write(cam, 0x01, 0x04);
+		}
+		break;
+	default:
+		break;
+	}
+
+	return err;
+}
+
+
+static struct sn9c102_sensor hv7131r = {
+	.name = "HV7131R",
+	.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+	.supported_bridge = BRIDGE_SN9C103 | BRIDGE_SN9C105 | BRIDGE_SN9C120,
+	.sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
+	.frequency = SN9C102_I2C_100KHZ,
+	.interface = SN9C102_I2C_2WIRES,
+	.i2c_slave_id = 0x11,
+	.init = &hv7131r_init,
+	.qctrl = {
+		{
+			.id = V4L2_CID_GAIN,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "global gain",
+			.minimum = 0x00,
+			.maximum = 0xff,
+			.step = 0x01,
+			.default_value = 0x40,
+			.flags = 0,
+		},
+		{
+			.id = V4L2_CID_RED_BALANCE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "red balance",
+			.minimum = 0x00,
+			.maximum = 0x3f,
+			.step = 0x01,
+			.default_value = 0x08,
+			.flags = 0,
+		},
+		{
+			.id = V4L2_CID_BLUE_BALANCE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "blue balance",
+			.minimum = 0x00,
+			.maximum = 0x3f,
+			.step = 0x01,
+			.default_value = 0x1a,
+			.flags = 0,
+		},
+		{
+			.id = SN9C102_V4L2_CID_GREEN_BALANCE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "green balance",
+			.minimum = 0x00,
+			.maximum = 0x3f,
+			.step = 0x01,
+			.default_value = 0x2f,
+			.flags = 0,
+		},
+		{
+			.id = V4L2_CID_BLACK_LEVEL,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "auto black level compensation",
+			.minimum = 0x00,
+			.maximum = 0x01,
+			.step = 0x01,
+			.default_value = 0x00,
+			.flags = 0,
+		},
+	},
+	.get_ctrl = &hv7131r_get_ctrl,
+	.set_ctrl = &hv7131r_set_ctrl,
+	.cropcap = {
+		.bounds = {
+			.left = 0,
+			.top = 0,
+			.width = 640,
+			.height = 480,
+		},
+		.defrect = {
+			.left = 0,
+			.top = 0,
+			.width = 640,
+			.height = 480,
+		},
+	},
+	.set_crop = &hv7131r_set_crop,
+	.pix_format = {
+		.width = 640,
+		.height = 480,
+		.pixelformat = V4L2_PIX_FMT_SBGGR8,
+		.priv = 8,
+	},
+	.set_pix_format = &hv7131r_set_pix_format
+};
+
+
+int sn9c102_probe_hv7131r(struct sn9c102_device* cam)
+{
+	int devid, err;
+
+	err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x44, 0x02},
+				       {0x34, 0x01}, {0x20, 0x17},
+				       {0x34, 0x01}, {0x46, 0x01});
+
+	if (err)
+		return -EIO;
+
+	devid = sn9c102_i2c_try_read(cam, &hv7131r, 0x00);
+	if (devid < 0)
+		return -EIO;
+
+	if (devid != 0x02)
+		return -ENODEV;
+
+	sn9c102_attach_sensor(cam, &hv7131r);
+
+	return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_mi0343.c b/drivers/media/video/sn9c102/sn9c102_mi0343.c
index a33d1bc..9200845 100644
--- a/drivers/media/video/sn9c102/sn9c102_mi0343.c
+++ b/drivers/media/video/sn9c102/sn9c102_mi0343.c
@@ -22,36 +22,30 @@
 #include "sn9c102_sensor.h"
 
 
-static struct sn9c102_sensor mi0343;
-static u8 mi0343_i2c_data[5+1];
-
-
 static int mi0343_init(struct sn9c102_device* cam)
 {
+	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
 	int err = 0;
 
-	err += sn9c102_write_reg(cam, 0x00, 0x10);
-	err += sn9c102_write_reg(cam, 0x00, 0x11);
-	err += sn9c102_write_reg(cam, 0x0a, 0x14);
-	err += sn9c102_write_reg(cam, 0x40, 0x01);
-	err += sn9c102_write_reg(cam, 0x20, 0x17);
-	err += sn9c102_write_reg(cam, 0x07, 0x18);
-	err += sn9c102_write_reg(cam, 0xa0, 0x19);
+	err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11},
+				       {0x0a, 0x14}, {0x40, 0x01},
+				       {0x20, 0x17}, {0x07, 0x18},
+				       {0xa0, 0x19});
 
-	err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
-					 0x0d, 0x00, 0x01, 0, 0);
-	err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
-					 0x0d, 0x00, 0x00, 0, 0);
-	err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
-					 0x03, 0x01, 0xe1, 0, 0);
-	err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
-					 0x04, 0x02, 0x81, 0, 0);
-	err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
-					 0x05, 0x00, 0x17, 0, 0);
-	err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
-					 0x06, 0x00, 0x11, 0, 0);
-	err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
-					 0x62, 0x04, 0x9a, 0, 0);
+	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
+					 0x00, 0x01, 0, 0);
+	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
+					 0x00, 0x00, 0, 0);
+	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03,
+					 0x01, 0xe1, 0, 0);
+	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04,
+					 0x02, 0x81, 0, 0);
+	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05,
+					 0x00, 0x17, 0, 0);
+	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06,
+					 0x00, 0x11, 0, 0);
+	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62,
+					 0x04, 0x9a, 0, 0);
 
 	return err;
 }
@@ -60,43 +54,46 @@
 static int mi0343_get_ctrl(struct sn9c102_device* cam,
 			   struct v4l2_control* ctrl)
 {
+	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+	u8 data[5+1];
+
 	switch (ctrl->id) {
 	case V4L2_CID_EXPOSURE:
-		if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
-					     0x09, 2+1, mi0343_i2c_data) < 0)
+		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09,
+					     2+1, data) < 0)
 			return -EIO;
-		ctrl->value = mi0343_i2c_data[2];
+		ctrl->value = data[2];
 		return 0;
 	case V4L2_CID_GAIN:
-		if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
-					     0x35, 2+1, mi0343_i2c_data) < 0)
+		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35,
+					     2+1, data) < 0)
 			return -EIO;
 		break;
 	case V4L2_CID_HFLIP:
-		if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
-					     0x20, 2+1, mi0343_i2c_data) < 0)
+		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20,
+					     2+1, data) < 0)
 			return -EIO;
-		ctrl->value = mi0343_i2c_data[3] & 0x20 ? 1 : 0;
+		ctrl->value = data[3] & 0x20 ? 1 : 0;
 		return 0;
 	case V4L2_CID_VFLIP:
-		if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
-					     0x20, 2+1, mi0343_i2c_data) < 0)
+		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20,
+					     2+1, data) < 0)
 			return -EIO;
-		ctrl->value = mi0343_i2c_data[3] & 0x80 ? 1 : 0;
+		ctrl->value = data[3] & 0x80 ? 1 : 0;
 		return 0;
 	case V4L2_CID_RED_BALANCE:
-		if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
-					     0x2d, 2+1, mi0343_i2c_data) < 0)
+		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d,
+					     2+1, data) < 0)
 			return -EIO;
 		break;
 	case V4L2_CID_BLUE_BALANCE:
-		if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
-					     0x2c, 2+1, mi0343_i2c_data) < 0)
+		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c,
+					     2+1, data) < 0)
 			return -EIO;
 		break;
 	case SN9C102_V4L2_CID_GREEN_BALANCE:
-		if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
-					     0x2e, 2+1, mi0343_i2c_data) < 0)
+		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e,
+					     2+1, data) < 0)
 			return -EIO;
 		break;
 	default:
@@ -108,7 +105,7 @@
 	case V4L2_CID_RED_BALANCE:
 	case V4L2_CID_BLUE_BALANCE:
 	case SN9C102_V4L2_CID_GREEN_BALANCE:
-		ctrl->value = mi0343_i2c_data[3] | (mi0343_i2c_data[2] << 8);
+		ctrl->value = data[3] | (data[2] << 8);
 		if (ctrl->value >= 0x10 && ctrl->value <= 0x3f)
 			ctrl->value -= 0x10;
 		else if (ctrl->value >= 0x60 && ctrl->value <= 0x7f)
@@ -124,6 +121,7 @@
 static int mi0343_set_ctrl(struct sn9c102_device* cam,
 			   const struct v4l2_control* ctrl)
 {
+	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
 	u16 reg = 0;
 	int err = 0;
 
@@ -143,50 +141,42 @@
 
 	switch (ctrl->id) {
 	case V4L2_CID_EXPOSURE:
-		err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
-						 mi0343.i2c_slave_id,
+		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
 						 0x09, ctrl->value, 0x00,
 						 0, 0);
 		break;
 	case V4L2_CID_GAIN:
-		err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
-						 mi0343.i2c_slave_id,
+		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
 						 0x35, reg >> 8, reg & 0xff,
 						 0, 0);
 		break;
 	case V4L2_CID_HFLIP:
-		err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
-						 mi0343.i2c_slave_id,
+		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
 						 0x20, ctrl->value ? 0x40:0x00,
 						 ctrl->value ? 0x20:0x00,
 						 0, 0);
 		break;
 	case V4L2_CID_VFLIP:
-		err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
-						 mi0343.i2c_slave_id,
+		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
 						 0x20, ctrl->value ? 0x80:0x00,
 						 ctrl->value ? 0x80:0x00,
 						 0, 0);
 		break;
 	case V4L2_CID_RED_BALANCE:
-		err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
-						 mi0343.i2c_slave_id,
+		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
 						 0x2d, reg >> 8, reg & 0xff,
 						 0, 0);
 		break;
 	case V4L2_CID_BLUE_BALANCE:
-		err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
-						 mi0343.i2c_slave_id,
+		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
 						 0x2c, reg >> 8, reg & 0xff,
 						 0, 0);
 		break;
 	case SN9C102_V4L2_CID_GREEN_BALANCE:
-		err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
-						 mi0343.i2c_slave_id,
+		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
 						 0x2b, reg >> 8, reg & 0xff,
 						 0, 0);
-		err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
-						 mi0343.i2c_slave_id,
+		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
 						 0x2e, reg >> 8, reg & 0xff,
 						 0, 0);
 		break;
@@ -216,16 +206,15 @@
 static int mi0343_set_pix_format(struct sn9c102_device* cam,
 				 const struct v4l2_pix_format* pix)
 {
+	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
 	int err = 0;
 
 	if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) {
-		err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
-						 mi0343.i2c_slave_id,
+		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
 						 0x0a, 0x00, 0x03, 0, 0);
 		err += sn9c102_write_reg(cam, 0x20, 0x19);
 	} else {
-		err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
-						 mi0343.i2c_slave_id,
+		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
 						 0x0a, 0x00, 0x05, 0, 0);
 		err += sn9c102_write_reg(cam, 0xa0, 0x19);
 	}
@@ -237,7 +226,7 @@
 static struct sn9c102_sensor mi0343 = {
 	.name = "MI-0343",
 	.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
-	.supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103,
+	.supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102,
 	.frequency = SN9C102_I2C_100KHZ,
 	.interface = SN9C102_I2C_2WIRES,
 	.i2c_slave_id = 0x5d,
@@ -343,19 +332,20 @@
 
 int sn9c102_probe_mi0343(struct sn9c102_device* cam)
 {
+	u8 data[5+1];
 	int err = 0;
 
-	err += sn9c102_write_reg(cam, 0x01, 0x01);
-	err += sn9c102_write_reg(cam, 0x00, 0x01);
-	err += sn9c102_write_reg(cam, 0x28, 0x17);
+	err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01},
+				       {0x28, 0x17});
+
 	if (err)
 		return -EIO;
 
 	if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, 0x00,
-				     2, mi0343_i2c_data) < 0)
+				     2, data) < 0)
 		return -EIO;
 
-	if (mi0343_i2c_data[4] != 0x32 && mi0343_i2c_data[3] != 0xe3)
+	if (data[4] != 0x32 || data[3] != 0xe3)
 		return -ENODEV;
 
 	sn9c102_attach_sensor(cam, &mi0343);
diff --git a/drivers/media/video/sn9c102/sn9c102_mi0360.c b/drivers/media/video/sn9c102/sn9c102_mi0360.c
new file mode 100644
index 0000000..64698ac
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_mi0360.c
@@ -0,0 +1,338 @@
+/***************************************************************************
+ * Plug-in for MI-0360 image sensor connected to the SN9C1xx PC Camera     *
+ * Controllers                                                             *
+ *                                                                         *
+ * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it>       *
+ *                                                                         *
+ * 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 "sn9c102_sensor.h"
+
+
+static int mi0360_init(struct sn9c102_device* cam)
+{
+	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+	int err = 0;
+
+	err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11},
+				       {0x0a, 0x14}, {0x40, 0x01},
+				       {0x20, 0x17}, {0x07, 0x18},
+				       {0xa0, 0x19}, {0x02, 0x1c},
+				       {0x03, 0x1d}, {0x0f, 0x1e},
+				       {0x0c, 0x1f}, {0x00, 0x20},
+				       {0x10, 0x21}, {0x20, 0x22},
+				       {0x30, 0x23}, {0x40, 0x24},
+				       {0x50, 0x25}, {0x60, 0x26},
+				       {0x70, 0x27}, {0x80, 0x28},
+				       {0x90, 0x29}, {0xa0, 0x2a},
+				       {0xb0, 0x2b}, {0xc0, 0x2c},
+				       {0xd0, 0x2d}, {0xe0, 0x2e},
+				       {0xf0, 0x2f}, {0xff, 0x30});
+
+	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
+					 0x00, 0x01, 0, 0);
+	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
+					 0x00, 0x00, 0, 0);
+	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03,
+					 0x01, 0xe1, 0, 0);
+	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04,
+					 0x02, 0x81, 0, 0);
+	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05,
+					 0x00, 0x17, 0, 0);
+	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06,
+					 0x00, 0x11, 0, 0);
+	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62,
+					 0x04, 0x9a, 0, 0);
+
+	return err;
+}
+
+
+static int mi0360_get_ctrl(struct sn9c102_device* cam,
+			   struct v4l2_control* ctrl)
+{
+	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+	u8 data[5+1];
+
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE:
+		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09,
+					     2+1, data) < 0)
+			return -EIO;
+		ctrl->value = data[2];
+		return 0;
+	case V4L2_CID_GAIN:
+		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35,
+					     2+1, data) < 0)
+			return -EIO;
+		ctrl->value = data[3];
+		return 0;
+	case V4L2_CID_RED_BALANCE:
+		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c,
+					     2+1, data) < 0)
+			return -EIO;
+		ctrl->value = data[3];
+		return 0;
+	case V4L2_CID_BLUE_BALANCE:
+		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d,
+					     2+1, data) < 0)
+			return -EIO;
+		ctrl->value = data[3];
+		return 0;
+	case SN9C102_V4L2_CID_GREEN_BALANCE:
+		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e,
+					     2+1, data) < 0)
+			return -EIO;
+		ctrl->value = data[3];
+		return 0;
+	case V4L2_CID_HFLIP:
+		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20,
+					     2+1, data) < 0)
+			return -EIO;
+		ctrl->value = data[3] & 0x20 ? 1 : 0;
+		return 0;
+	case V4L2_CID_VFLIP:
+		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20,
+					     2+1, data) < 0)
+			return -EIO;
+		ctrl->value = data[3] & 0x80 ? 1 : 0;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+static int mi0360_set_ctrl(struct sn9c102_device* cam,
+			   const struct v4l2_control* ctrl)
+{
+	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+	int err = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE:
+		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+						 0x09, ctrl->value, 0x00,
+						 0, 0);
+		break;
+	case V4L2_CID_GAIN:
+		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+						 0x35, 0x03, ctrl->value,
+						 0, 0);
+		break;
+	case V4L2_CID_RED_BALANCE:
+		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+						 0x2c, 0x03, ctrl->value,
+						 0, 0);
+		break;
+	case V4L2_CID_BLUE_BALANCE:
+		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+						 0x2d, 0x03, ctrl->value,
+						 0, 0);
+		break;
+	case SN9C102_V4L2_CID_GREEN_BALANCE:
+		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+						 0x2b, 0x03, ctrl->value,
+						 0, 0);
+		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+						 0x2e, 0x03, ctrl->value,
+						 0, 0);
+		break;
+	case V4L2_CID_HFLIP:
+		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+						 0x20, ctrl->value ? 0x40:0x00,
+						 ctrl->value ? 0x20:0x00,
+						 0, 0);
+		break;
+	case V4L2_CID_VFLIP:
+		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+						 0x20, ctrl->value ? 0x80:0x00,
+						 ctrl->value ? 0x80:0x00,
+						 0, 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return err ? -EIO : 0;
+}
+
+
+static int mi0360_set_crop(struct sn9c102_device* cam,
+			    const struct v4l2_rect* rect)
+{
+	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+	int err = 0;
+	u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0,
+	   v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1;
+
+	err += sn9c102_write_reg(cam, h_start, 0x12);
+	err += sn9c102_write_reg(cam, v_start, 0x13);
+
+	return err;
+}
+
+
+static int mi0360_set_pix_format(struct sn9c102_device* cam,
+				 const struct v4l2_pix_format* pix)
+{
+	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+	int err = 0;
+
+	if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) {
+		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+						 0x0a, 0x00, 0x02, 0, 0);
+		err += sn9c102_write_reg(cam, 0x20, 0x19);
+	} else {
+		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+						 0x0a, 0x00, 0x05, 0, 0);
+		err += sn9c102_write_reg(cam, 0x60, 0x19);
+	}
+
+	return err;
+}
+
+
+static struct sn9c102_sensor mi0360 = {
+	.name = "MI-0360",
+	.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+	.supported_bridge = BRIDGE_SN9C103,
+	.frequency = SN9C102_I2C_100KHZ,
+	.interface = SN9C102_I2C_2WIRES,
+	.i2c_slave_id = 0x5d,
+	.init = &mi0360_init,
+	.qctrl = {
+		{
+			.id = V4L2_CID_EXPOSURE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "exposure",
+			.minimum = 0x00,
+			.maximum = 0x0f,
+			.step = 0x01,
+			.default_value = 0x05,
+			.flags = 0,
+		},
+		{
+			.id = V4L2_CID_GAIN,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "global gain",
+			.minimum = 0x00,
+			.maximum = 0x7f,
+			.step = 0x01,
+			.default_value = 0x25,
+			.flags = 0,
+		},
+		{
+			.id = V4L2_CID_HFLIP,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "horizontal mirror",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 1,
+			.default_value = 0,
+			.flags = 0,
+		},
+		{
+			.id = V4L2_CID_VFLIP,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "vertical mirror",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 1,
+			.default_value = 0,
+			.flags = 0,
+		},
+		{
+			.id = V4L2_CID_BLUE_BALANCE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "blue balance",
+			.minimum = 0x00,
+			.maximum = 0x7f,
+			.step = 0x01,
+			.default_value = 0x0f,
+			.flags = 0,
+		},
+		{
+			.id = V4L2_CID_RED_BALANCE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "red balance",
+			.minimum = 0x00,
+			.maximum = 0x7f,
+			.step = 0x01,
+			.default_value = 0x32,
+			.flags = 0,
+		},
+		{
+			.id = SN9C102_V4L2_CID_GREEN_BALANCE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "green balance",
+			.minimum = 0x00,
+			.maximum = 0x7f,
+			.step = 0x01,
+			.default_value = 0x25,
+			.flags = 0,
+		},
+	},
+	.get_ctrl = &mi0360_get_ctrl,
+	.set_ctrl = &mi0360_set_ctrl,
+	.cropcap = {
+		.bounds = {
+			.left = 0,
+			.top = 0,
+			.width = 640,
+			.height = 480,
+		},
+		.defrect = {
+			.left = 0,
+			.top = 0,
+			.width = 640,
+			.height = 480,
+		},
+	},
+	.set_crop = &mi0360_set_crop,
+	.pix_format = {
+		.width = 640,
+		.height = 480,
+		.pixelformat = V4L2_PIX_FMT_SBGGR8,
+		.priv = 8,
+	},
+	.set_pix_format = &mi0360_set_pix_format
+};
+
+
+int sn9c102_probe_mi0360(struct sn9c102_device* cam)
+{
+	u8 data[5+1];
+	int err;
+
+	err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01},
+				       {0x28, 0x17});
+	if (err)
+		return -EIO;
+
+	if (sn9c102_i2c_try_raw_read(cam, &mi0360, mi0360.i2c_slave_id, 0x00,
+				     2+1, data) < 0)
+		return -EIO;
+
+	if (data[2] != 0x82 || data[3] != 0x43)
+		return -ENODEV;
+
+	sn9c102_attach_sensor(cam, &mi0360);
+
+	return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_ov7630.c b/drivers/media/video/sn9c102/sn9c102_ov7630.c
index 7df09ff..31b6080 100644
--- a/drivers/media/video/sn9c102/sn9c102_ov7630.c
+++ b/drivers/media/video/sn9c102/sn9c102_ov7630.c
@@ -22,9 +22,6 @@
 #include "sn9c102_sensor.h"
 
 
-static struct sn9c102_sensor ov7630;
-
-
 static int ov7630_init(struct sn9c102_device* cam)
 {
 	int err = 0;
@@ -32,21 +29,20 @@
 	switch (sn9c102_get_bridge(cam)) {
 	case BRIDGE_SN9C101:
 	case BRIDGE_SN9C102:
-	err += sn9c102_write_reg(cam, 0x00, 0x14);
-	err += sn9c102_write_reg(cam, 0x60, 0x17);
-	err += sn9c102_write_reg(cam, 0x0f, 0x18);
-	err += sn9c102_write_reg(cam, 0x50, 0x19);
+		err = sn9c102_write_const_regs(cam, {0x00, 0x14},
+					       {0x60, 0x17}, {0x0f, 0x18},
+					       {0x50, 0x19});
 
 		err += sn9c102_i2c_write(cam, 0x12, 0x8d);
 		err += sn9c102_i2c_write(cam, 0x12, 0x0d);
 		err += sn9c102_i2c_write(cam, 0x11, 0x00);
-	err += sn9c102_i2c_write(cam, 0x15, 0x34);
-	err += sn9c102_i2c_write(cam, 0x16, 0x03);
-	err += sn9c102_i2c_write(cam, 0x17, 0x1c);
-	err += sn9c102_i2c_write(cam, 0x18, 0xbd);
-	err += sn9c102_i2c_write(cam, 0x19, 0x06);
-	err += sn9c102_i2c_write(cam, 0x1a, 0xf6);
-	err += sn9c102_i2c_write(cam, 0x1b, 0x04);
+		err += sn9c102_i2c_write(cam, 0x15, 0x35);
+		err += sn9c102_i2c_write(cam, 0x16, 0x03);
+		err += sn9c102_i2c_write(cam, 0x17, 0x1c);
+		err += sn9c102_i2c_write(cam, 0x18, 0xbd);
+		err += sn9c102_i2c_write(cam, 0x19, 0x06);
+		err += sn9c102_i2c_write(cam, 0x1a, 0xf6);
+		err += sn9c102_i2c_write(cam, 0x1b, 0x04);
 		err += sn9c102_i2c_write(cam, 0x20, 0x44);
 		err += sn9c102_i2c_write(cam, 0x23, 0xee);
 		err += sn9c102_i2c_write(cam, 0x26, 0xa0);
@@ -65,42 +61,26 @@
 		err += sn9c102_i2c_write(cam, 0x71, 0x00);
 		err += sn9c102_i2c_write(cam, 0x74, 0x21);
 		err += sn9c102_i2c_write(cam, 0x7d, 0xf7);
+
 		break;
 	case BRIDGE_SN9C103:
-		err += sn9c102_write_reg(cam, 0x00, 0x02);
-		err += sn9c102_write_reg(cam, 0x00, 0x03);
-		err += sn9c102_write_reg(cam, 0x1a, 0x04);
-		err += sn9c102_write_reg(cam, 0x20, 0x05);
-		err += sn9c102_write_reg(cam, 0x20, 0x06);
-		err += sn9c102_write_reg(cam, 0x20, 0x07);
-		err += sn9c102_write_reg(cam, 0x03, 0x10);
-		err += sn9c102_write_reg(cam, 0x0a, 0x14);
-		err += sn9c102_write_reg(cam, 0x60, 0x17);
-		err += sn9c102_write_reg(cam, 0x0f, 0x18);
-		err += sn9c102_write_reg(cam, 0x50, 0x19);
-		err += sn9c102_write_reg(cam, 0x1d, 0x1a);
-		err += sn9c102_write_reg(cam, 0x10, 0x1b);
-		err += sn9c102_write_reg(cam, 0x02, 0x1c);
-		err += sn9c102_write_reg(cam, 0x03, 0x1d);
-		err += sn9c102_write_reg(cam, 0x0f, 0x1e);
-		err += sn9c102_write_reg(cam, 0x0c, 0x1f);
-		err += sn9c102_write_reg(cam, 0x00, 0x20);
-		err += sn9c102_write_reg(cam, 0x10, 0x21);
-		err += sn9c102_write_reg(cam, 0x20, 0x22);
-		err += sn9c102_write_reg(cam, 0x30, 0x23);
-		err += sn9c102_write_reg(cam, 0x40, 0x24);
-		err += sn9c102_write_reg(cam, 0x50, 0x25);
-		err += sn9c102_write_reg(cam, 0x60, 0x26);
-		err += sn9c102_write_reg(cam, 0x70, 0x27);
-		err += sn9c102_write_reg(cam, 0x80, 0x28);
-		err += sn9c102_write_reg(cam, 0x90, 0x29);
-		err += sn9c102_write_reg(cam, 0xa0, 0x2a);
-		err += sn9c102_write_reg(cam, 0xb0, 0x2b);
-		err += sn9c102_write_reg(cam, 0xc0, 0x2c);
-		err += sn9c102_write_reg(cam, 0xd0, 0x2d);
-		err += sn9c102_write_reg(cam, 0xe0, 0x2e);
-		err += sn9c102_write_reg(cam, 0xf0, 0x2f);
-		err += sn9c102_write_reg(cam, 0xff, 0x30);
+		err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03},
+					       {0x1a, 0x04}, {0x20, 0x05},
+					       {0x20, 0x06}, {0x20, 0x07},
+					       {0x03, 0x10}, {0x0a, 0x14},
+					       {0x60, 0x17}, {0x0f, 0x18},
+					       {0x50, 0x19}, {0x1d, 0x1a},
+					       {0x10, 0x1b}, {0x02, 0x1c},
+					       {0x03, 0x1d}, {0x0f, 0x1e},
+					       {0x0c, 0x1f}, {0x00, 0x20},
+					       {0x10, 0x21}, {0x20, 0x22},
+					       {0x30, 0x23}, {0x40, 0x24},
+					       {0x50, 0x25}, {0x60, 0x26},
+					       {0x70, 0x27}, {0x80, 0x28},
+					       {0x90, 0x29}, {0xa0, 0x2a},
+					       {0xb0, 0x2b}, {0xc0, 0x2c},
+					       {0xd0, 0x2d}, {0xe0, 0x2e},
+					       {0xf0, 0x2f}, {0xff, 0x30});
 
 		err += sn9c102_i2c_write(cam, 0x12, 0x8d);
 		err += sn9c102_i2c_write(cam, 0x12, 0x0d);
@@ -108,23 +88,23 @@
 		err += sn9c102_i2c_write(cam, 0x11, 0x01);
 		err += sn9c102_i2c_write(cam, 0x1b, 0x04);
 		err += sn9c102_i2c_write(cam, 0x20, 0x44);
-	err += sn9c102_i2c_write(cam, 0x23, 0xee);
-	err += sn9c102_i2c_write(cam, 0x26, 0xa0);
-	err += sn9c102_i2c_write(cam, 0x27, 0x9a);
+		err += sn9c102_i2c_write(cam, 0x23, 0xee);
+		err += sn9c102_i2c_write(cam, 0x26, 0xa0);
+		err += sn9c102_i2c_write(cam, 0x27, 0x9a);
 		err += sn9c102_i2c_write(cam, 0x28, 0x20);
-	err += sn9c102_i2c_write(cam, 0x29, 0x30);
-	err += sn9c102_i2c_write(cam, 0x2f, 0x3d);
-	err += sn9c102_i2c_write(cam, 0x30, 0x24);
-	err += sn9c102_i2c_write(cam, 0x32, 0x86);
-	err += sn9c102_i2c_write(cam, 0x60, 0xa9);
-	err += sn9c102_i2c_write(cam, 0x61, 0x42);
-	err += sn9c102_i2c_write(cam, 0x65, 0x00);
-	err += sn9c102_i2c_write(cam, 0x69, 0x38);
-	err += sn9c102_i2c_write(cam, 0x6f, 0x88);
-	err += sn9c102_i2c_write(cam, 0x70, 0x0b);
-	err += sn9c102_i2c_write(cam, 0x71, 0x00);
-	err += sn9c102_i2c_write(cam, 0x74, 0x21);
-	err += sn9c102_i2c_write(cam, 0x7d, 0xf7);
+		err += sn9c102_i2c_write(cam, 0x29, 0x30);
+		err += sn9c102_i2c_write(cam, 0x2f, 0x3d);
+		err += sn9c102_i2c_write(cam, 0x30, 0x24);
+		err += sn9c102_i2c_write(cam, 0x32, 0x86);
+		err += sn9c102_i2c_write(cam, 0x60, 0xa9);
+		err += sn9c102_i2c_write(cam, 0x61, 0x42);
+		err += sn9c102_i2c_write(cam, 0x65, 0x00);
+		err += sn9c102_i2c_write(cam, 0x69, 0x38);
+		err += sn9c102_i2c_write(cam, 0x6f, 0x88);
+		err += sn9c102_i2c_write(cam, 0x70, 0x0b);
+		err += sn9c102_i2c_write(cam, 0x71, 0x00);
+		err += sn9c102_i2c_write(cam, 0x74, 0x21);
+		err += sn9c102_i2c_write(cam, 0x7d, 0xf7);
 		break;
 	default:
 		break;
@@ -428,15 +408,14 @@
 	switch (sn9c102_get_bridge(cam)) {
 	case BRIDGE_SN9C101:
 	case BRIDGE_SN9C102:
-	err += sn9c102_write_reg(cam, 0x01, 0x01);
-	err += sn9c102_write_reg(cam, 0x00, 0x01);
-	err += sn9c102_write_reg(cam, 0x28, 0x17);
+		err = sn9c102_write_const_regs(cam, {0x01, 0x01},
+					       {0x00, 0x01}, {0x28, 0x17});
+
 		break;
 	case BRIDGE_SN9C103: /* do _not_ change anything! */
-		err += sn9c102_write_reg(cam, 0x09, 0x01);
-		err += sn9c102_write_reg(cam, 0x42, 0x01);
-		err += sn9c102_write_reg(cam, 0x28, 0x17);
-		err += sn9c102_write_reg(cam, 0x44, 0x02);
+		err = sn9c102_write_const_regs(cam, {0x09, 0x01},
+					       {0x42, 0x01}, {0x28, 0x17},
+					       {0x44, 0x02});
 		pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a);
 		if (err || pid < 0) { /* try a different initialization */
 			err = sn9c102_write_reg(cam, 0x01, 0x01);
diff --git a/drivers/media/video/sn9c102/sn9c102_ov7660.c b/drivers/media/video/sn9c102/sn9c102_ov7660.c
index d670c24..c898e948 100644
--- a/drivers/media/video/sn9c102/sn9c102_ov7660.c
+++ b/drivers/media/video/sn9c102/sn9c102_ov7660.c
@@ -22,160 +22,84 @@
 #include "sn9c102_sensor.h"
 
 
-static struct sn9c102_sensor ov7660;
-
-
 static int ov7660_init(struct sn9c102_device* cam)
 {
 	int err = 0;
 
-	err += sn9c102_write_reg(cam, 0x40, 0x02);
-	err += sn9c102_write_reg(cam, 0x00, 0x03);
-	err += sn9c102_write_reg(cam, 0x1a, 0x04);
-	err += sn9c102_write_reg(cam, 0x03, 0x10);
-	err += sn9c102_write_reg(cam, 0x08, 0x14);
-	err += sn9c102_write_reg(cam, 0x20, 0x17);
-	err += sn9c102_write_reg(cam, 0x8b, 0x18);
-	err += sn9c102_write_reg(cam, 0x00, 0x19);
-	err += sn9c102_write_reg(cam, 0x1d, 0x1a);
-	err += sn9c102_write_reg(cam, 0x10, 0x1b);
-	err += sn9c102_write_reg(cam, 0x02, 0x1c);
-	err += sn9c102_write_reg(cam, 0x03, 0x1d);
-	err += sn9c102_write_reg(cam, 0x0f, 0x1e);
-	err += sn9c102_write_reg(cam, 0x0c, 0x1f);
-	err += sn9c102_write_reg(cam, 0x00, 0x20);
-	err += sn9c102_write_reg(cam, 0x29, 0x21);
-	err += sn9c102_write_reg(cam, 0x40, 0x22);
-	err += sn9c102_write_reg(cam, 0x54, 0x23);
-	err += sn9c102_write_reg(cam, 0x66, 0x24);
-	err += sn9c102_write_reg(cam, 0x76, 0x25);
-	err += sn9c102_write_reg(cam, 0x85, 0x26);
-	err += sn9c102_write_reg(cam, 0x94, 0x27);
-	err += sn9c102_write_reg(cam, 0xa1, 0x28);
-	err += sn9c102_write_reg(cam, 0xae, 0x29);
-	err += sn9c102_write_reg(cam, 0xbb, 0x2a);
-	err += sn9c102_write_reg(cam, 0xc7, 0x2b);
-	err += sn9c102_write_reg(cam, 0xd3, 0x2c);
-	err += sn9c102_write_reg(cam, 0xde, 0x2d);
-	err += sn9c102_write_reg(cam, 0xea, 0x2e);
-	err += sn9c102_write_reg(cam, 0xf4, 0x2f);
-	err += sn9c102_write_reg(cam, 0xff, 0x30);
-	err += sn9c102_write_reg(cam, 0x00, 0x3F);
-	err += sn9c102_write_reg(cam, 0xC7, 0x40);
-	err += sn9c102_write_reg(cam, 0x01, 0x41);
-	err += sn9c102_write_reg(cam, 0x44, 0x42);
-	err += sn9c102_write_reg(cam, 0x00, 0x43);
-	err += sn9c102_write_reg(cam, 0x44, 0x44);
-	err += sn9c102_write_reg(cam, 0x00, 0x45);
-	err += sn9c102_write_reg(cam, 0x44, 0x46);
-	err += sn9c102_write_reg(cam, 0x00, 0x47);
-	err += sn9c102_write_reg(cam, 0xC7, 0x48);
-	err += sn9c102_write_reg(cam, 0x01, 0x49);
-	err += sn9c102_write_reg(cam, 0xC7, 0x4A);
-	err += sn9c102_write_reg(cam, 0x01, 0x4B);
-	err += sn9c102_write_reg(cam, 0xC7, 0x4C);
-	err += sn9c102_write_reg(cam, 0x01, 0x4D);
-	err += sn9c102_write_reg(cam, 0x44, 0x4E);
-	err += sn9c102_write_reg(cam, 0x00, 0x4F);
-	err += sn9c102_write_reg(cam, 0x44, 0x50);
-	err += sn9c102_write_reg(cam, 0x00, 0x51);
-	err += sn9c102_write_reg(cam, 0x44, 0x52);
-	err += sn9c102_write_reg(cam, 0x00, 0x53);
-	err += sn9c102_write_reg(cam, 0xC7, 0x54);
-	err += sn9c102_write_reg(cam, 0x01, 0x55);
-	err += sn9c102_write_reg(cam, 0xC7, 0x56);
-	err += sn9c102_write_reg(cam, 0x01, 0x57);
-	err += sn9c102_write_reg(cam, 0xC7, 0x58);
-	err += sn9c102_write_reg(cam, 0x01, 0x59);
-	err += sn9c102_write_reg(cam, 0x44, 0x5A);
-	err += sn9c102_write_reg(cam, 0x00, 0x5B);
-	err += sn9c102_write_reg(cam, 0x44, 0x5C);
-	err += sn9c102_write_reg(cam, 0x00, 0x5D);
-	err += sn9c102_write_reg(cam, 0x44, 0x5E);
-	err += sn9c102_write_reg(cam, 0x00, 0x5F);
-	err += sn9c102_write_reg(cam, 0xC7, 0x60);
-	err += sn9c102_write_reg(cam, 0x01, 0x61);
-	err += sn9c102_write_reg(cam, 0xC7, 0x62);
-	err += sn9c102_write_reg(cam, 0x01, 0x63);
-	err += sn9c102_write_reg(cam, 0xC7, 0x64);
-	err += sn9c102_write_reg(cam, 0x01, 0x65);
-	err += sn9c102_write_reg(cam, 0x44, 0x66);
-	err += sn9c102_write_reg(cam, 0x00, 0x67);
-	err += sn9c102_write_reg(cam, 0x44, 0x68);
-	err += sn9c102_write_reg(cam, 0x00, 0x69);
-	err += sn9c102_write_reg(cam, 0x44, 0x6A);
-	err += sn9c102_write_reg(cam, 0x00, 0x6B);
-	err += sn9c102_write_reg(cam, 0xC7, 0x6C);
-	err += sn9c102_write_reg(cam, 0x01, 0x6D);
-	err += sn9c102_write_reg(cam, 0xC7, 0x6E);
-	err += sn9c102_write_reg(cam, 0x01, 0x6F);
-	err += sn9c102_write_reg(cam, 0xC7, 0x70);
-	err += sn9c102_write_reg(cam, 0x01, 0x71);
-	err += sn9c102_write_reg(cam, 0x44, 0x72);
-	err += sn9c102_write_reg(cam, 0x00, 0x73);
-	err += sn9c102_write_reg(cam, 0x44, 0x74);
-	err += sn9c102_write_reg(cam, 0x00, 0x75);
-	err += sn9c102_write_reg(cam, 0x44, 0x76);
-	err += sn9c102_write_reg(cam, 0x00, 0x77);
-	err += sn9c102_write_reg(cam, 0xC7, 0x78);
-	err += sn9c102_write_reg(cam, 0x01, 0x79);
-	err += sn9c102_write_reg(cam, 0xC7, 0x7A);
-	err += sn9c102_write_reg(cam, 0x01, 0x7B);
-	err += sn9c102_write_reg(cam, 0xC7, 0x7C);
-	err += sn9c102_write_reg(cam, 0x01, 0x7D);
-	err += sn9c102_write_reg(cam, 0x44, 0x7E);
-	err += sn9c102_write_reg(cam, 0x00, 0x7F);
-	err += sn9c102_write_reg(cam, 0x14, 0x84);
-	err += sn9c102_write_reg(cam, 0x00, 0x85);
-	err += sn9c102_write_reg(cam, 0x27, 0x86);
-	err += sn9c102_write_reg(cam, 0x00, 0x87);
-	err += sn9c102_write_reg(cam, 0x07, 0x88);
-	err += sn9c102_write_reg(cam, 0x00, 0x89);
-	err += sn9c102_write_reg(cam, 0xEC, 0x8A);
-	err += sn9c102_write_reg(cam, 0x0f, 0x8B);
-	err += sn9c102_write_reg(cam, 0xD8, 0x8C);
-	err += sn9c102_write_reg(cam, 0x0f, 0x8D);
-	err += sn9c102_write_reg(cam, 0x3D, 0x8E);
-	err += sn9c102_write_reg(cam, 0x00, 0x8F);
-	err += sn9c102_write_reg(cam, 0x3D, 0x90);
-	err += sn9c102_write_reg(cam, 0x00, 0x91);
-	err += sn9c102_write_reg(cam, 0xCD, 0x92);
-	err += sn9c102_write_reg(cam, 0x0f, 0x93);
-	err += sn9c102_write_reg(cam, 0xf7, 0x94);
-	err += sn9c102_write_reg(cam, 0x0f, 0x95);
-	err += sn9c102_write_reg(cam, 0x0C, 0x96);
-	err += sn9c102_write_reg(cam, 0x00, 0x97);
-	err += sn9c102_write_reg(cam, 0x00, 0x98);
-	err += sn9c102_write_reg(cam, 0x66, 0x99);
-	err += sn9c102_write_reg(cam, 0x05, 0x9A);
-	err += sn9c102_write_reg(cam, 0x00, 0x9B);
-	err += sn9c102_write_reg(cam, 0x04, 0x9C);
-	err += sn9c102_write_reg(cam, 0x00, 0x9D);
-	err += sn9c102_write_reg(cam, 0x08, 0x9E);
-	err += sn9c102_write_reg(cam, 0x00, 0x9F);
-	err += sn9c102_write_reg(cam, 0x2D, 0xC0);
-	err += sn9c102_write_reg(cam, 0x2D, 0xC1);
-	err += sn9c102_write_reg(cam, 0x3A, 0xC2);
-	err += sn9c102_write_reg(cam, 0x05, 0xC3);
-	err += sn9c102_write_reg(cam, 0x04, 0xC4);
-	err += sn9c102_write_reg(cam, 0x3F, 0xC5);
-	err += sn9c102_write_reg(cam, 0x00, 0xC6);
-	err += sn9c102_write_reg(cam, 0x00, 0xC7);
-	err += sn9c102_write_reg(cam, 0x50, 0xC8);
-	err += sn9c102_write_reg(cam, 0x3C, 0xC9);
-	err += sn9c102_write_reg(cam, 0x28, 0xCA);
-	err += sn9c102_write_reg(cam, 0xD8, 0xCB);
-	err += sn9c102_write_reg(cam, 0x14, 0xCC);
-	err += sn9c102_write_reg(cam, 0xEC, 0xCD);
-	err += sn9c102_write_reg(cam, 0x32, 0xCE);
-	err += sn9c102_write_reg(cam, 0xDD, 0xCF);
-	err += sn9c102_write_reg(cam, 0x32, 0xD0);
-	err += sn9c102_write_reg(cam, 0xDD, 0xD1);
-	err += sn9c102_write_reg(cam, 0x6A, 0xD2);
-	err += sn9c102_write_reg(cam, 0x50, 0xD3);
-	err += sn9c102_write_reg(cam, 0x00, 0xD4);
-	err += sn9c102_write_reg(cam, 0x00, 0xD5);
-	err += sn9c102_write_reg(cam, 0x00, 0xD6);
+	err = sn9c102_write_const_regs(cam, {0x40, 0x02}, {0x00, 0x03},
+				       {0x1a, 0x04}, {0x03, 0x10},
+				       {0x08, 0x14}, {0x20, 0x17},
+				       {0x8b, 0x18}, {0x00, 0x19},
+				       {0x1d, 0x1a}, {0x10, 0x1b},
+				       {0x02, 0x1c}, {0x03, 0x1d},
+				       {0x0f, 0x1e}, {0x0c, 0x1f},
+				       {0x00, 0x20}, {0x29, 0x21},
+				       {0x40, 0x22}, {0x54, 0x23},
+				       {0x66, 0x24}, {0x76, 0x25},
+				       {0x85, 0x26}, {0x94, 0x27},
+				       {0xa1, 0x28}, {0xae, 0x29},
+				       {0xbb, 0x2a}, {0xc7, 0x2b},
+				       {0xd3, 0x2c}, {0xde, 0x2d},
+				       {0xea, 0x2e}, {0xf4, 0x2f},
+				       {0xff, 0x30}, {0x00, 0x3F},
+				       {0xC7, 0x40}, {0x01, 0x41},
+				       {0x44, 0x42}, {0x00, 0x43},
+				       {0x44, 0x44}, {0x00, 0x45},
+				       {0x44, 0x46}, {0x00, 0x47},
+				       {0xC7, 0x48}, {0x01, 0x49},
+				       {0xC7, 0x4A}, {0x01, 0x4B},
+				       {0xC7, 0x4C}, {0x01, 0x4D},
+				       {0x44, 0x4E}, {0x00, 0x4F},
+				       {0x44, 0x50}, {0x00, 0x51},
+				       {0x44, 0x52}, {0x00, 0x53},
+				       {0xC7, 0x54}, {0x01, 0x55},
+				       {0xC7, 0x56}, {0x01, 0x57},
+				       {0xC7, 0x58}, {0x01, 0x59},
+				       {0x44, 0x5A}, {0x00, 0x5B},
+				       {0x44, 0x5C}, {0x00, 0x5D},
+				       {0x44, 0x5E}, {0x00, 0x5F},
+				       {0xC7, 0x60}, {0x01, 0x61},
+				       {0xC7, 0x62}, {0x01, 0x63},
+				       {0xC7, 0x64}, {0x01, 0x65},
+				       {0x44, 0x66}, {0x00, 0x67},
+				       {0x44, 0x68}, {0x00, 0x69},
+				       {0x44, 0x6A}, {0x00, 0x6B},
+				       {0xC7, 0x6C}, {0x01, 0x6D},
+				       {0xC7, 0x6E}, {0x01, 0x6F},
+				       {0xC7, 0x70}, {0x01, 0x71},
+				       {0x44, 0x72}, {0x00, 0x73},
+				       {0x44, 0x74}, {0x00, 0x75},
+				       {0x44, 0x76}, {0x00, 0x77},
+				       {0xC7, 0x78}, {0x01, 0x79},
+				       {0xC7, 0x7A}, {0x01, 0x7B},
+				       {0xC7, 0x7C}, {0x01, 0x7D},
+				       {0x44, 0x7E}, {0x00, 0x7F},
+				       {0x14, 0x84}, {0x00, 0x85},
+				       {0x27, 0x86}, {0x00, 0x87},
+				       {0x07, 0x88}, {0x00, 0x89},
+				       {0xEC, 0x8A}, {0x0f, 0x8B},
+				       {0xD8, 0x8C}, {0x0f, 0x8D},
+				       {0x3D, 0x8E}, {0x00, 0x8F},
+				       {0x3D, 0x90}, {0x00, 0x91},
+				       {0xCD, 0x92}, {0x0f, 0x93},
+				       {0xf7, 0x94}, {0x0f, 0x95},
+				       {0x0C, 0x96}, {0x00, 0x97},
+				       {0x00, 0x98}, {0x66, 0x99},
+				       {0x05, 0x9A}, {0x00, 0x9B},
+				       {0x04, 0x9C}, {0x00, 0x9D},
+				       {0x08, 0x9E}, {0x00, 0x9F},
+				       {0x2D, 0xC0}, {0x2D, 0xC1},
+				       {0x3A, 0xC2}, {0x05, 0xC3},
+				       {0x04, 0xC4}, {0x3F, 0xC5},
+				       {0x00, 0xC6}, {0x00, 0xC7},
+				       {0x50, 0xC8}, {0x3C, 0xC9},
+				       {0x28, 0xCA}, {0xD8, 0xCB},
+				       {0x14, 0xCC}, {0xEC, 0xCD},
+				       {0x32, 0xCE}, {0xDD, 0xCF},
+				       {0x32, 0xD0}, {0xDD, 0xD1},
+				       {0x6A, 0xD2}, {0x50, 0xD3},
+				       {0x00, 0xD4}, {0x00, 0xD5},
+				       {0x00, 0xD6});
 
 	err += sn9c102_i2c_write(cam, 0x12, 0x80);
 	err += sn9c102_i2c_write(cam, 0x11, 0x09);
@@ -572,13 +496,11 @@
 
 int sn9c102_probe_ov7660(struct sn9c102_device* cam)
 {
-	int pid, ver, err = 0;
+	int pid, ver, err;
 
-	err += sn9c102_write_reg(cam, 0x01, 0xf1);
-	err += sn9c102_write_reg(cam, 0x00, 0xf1);
-	err += sn9c102_write_reg(cam, 0x01, 0x01);
-	err += sn9c102_write_reg(cam, 0x00, 0x01);
-	err += sn9c102_write_reg(cam, 0x28, 0x17);
+	err = sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1},
+				       {0x01, 0x01}, {0x00, 0x01},
+				       {0x28, 0x17});
 
 	pid = sn9c102_i2c_try_read(cam, &ov7660, 0x0a);
 	ver = sn9c102_i2c_try_read(cam, &ov7660, 0x0b);
diff --git a/drivers/media/video/sn9c102/sn9c102_pas106b.c b/drivers/media/video/sn9c102/sn9c102_pas106b.c
index 8d79a5f..6715196 100644
--- a/drivers/media/video/sn9c102/sn9c102_pas106b.c
+++ b/drivers/media/video/sn9c102/sn9c102_pas106b.c
@@ -23,19 +23,13 @@
 #include "sn9c102_sensor.h"
 
 
-static struct sn9c102_sensor pas106b;
-
-
 static int pas106b_init(struct sn9c102_device* cam)
 {
 	int err = 0;
 
-	err += sn9c102_write_reg(cam, 0x00, 0x10);
-	err += sn9c102_write_reg(cam, 0x00, 0x11);
-	err += sn9c102_write_reg(cam, 0x00, 0x14);
-	err += sn9c102_write_reg(cam, 0x20, 0x17);
-	err += sn9c102_write_reg(cam, 0x20, 0x19);
-	err += sn9c102_write_reg(cam, 0x09, 0x18);
+	err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11},
+				       {0x00, 0x14}, {0x20, 0x17},
+				       {0x20, 0x19}, {0x09, 0x18});
 
 	err += sn9c102_i2c_write(cam, 0x02, 0x0c);
 	err += sn9c102_i2c_write(cam, 0x05, 0x5a);
@@ -172,7 +166,7 @@
 static struct sn9c102_sensor pas106b = {
 	.name = "PAS106B",
 	.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
-	.supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103,
+	.supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102,
 	.sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
 	.frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ,
 	.interface = SN9C102_I2C_2WIRES,
@@ -279,16 +273,17 @@
 
 int sn9c102_probe_pas106b(struct sn9c102_device* cam)
 {
-	int r0 = 0, r1 = 0, err = 0;
+	int r0 = 0, r1 = 0, err;
 	unsigned int pid = 0;
 
 	/*
 	   Minimal initialization to enable the I2C communication
 	   NOTE: do NOT change the values!
 	*/
-	err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */
-	err += sn9c102_write_reg(cam, 0x00, 0x01); /* sensor power on */
-	err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */
+	err = sn9c102_write_const_regs(cam,
+				       {0x01, 0x01}, /* sensor power down */
+				       {0x00, 0x01}, /* sensor power on */
+				       {0x28, 0x17});/* sensor clock 24 MHz */
 	if (err)
 		return -EIO;
 
diff --git a/drivers/media/video/sn9c102/sn9c102_pas202bcb.c b/drivers/media/video/sn9c102/sn9c102_pas202bcb.c
index 7894f01..c1b8d6b 100644
--- a/drivers/media/video/sn9c102/sn9c102_pas202bcb.c
+++ b/drivers/media/video/sn9c102/sn9c102_pas202bcb.c
@@ -28,9 +28,6 @@
 #include "sn9c102_sensor.h"
 
 
-static struct sn9c102_sensor pas202bcb;
-
-
 static int pas202bcb_init(struct sn9c102_device* cam)
 {
 	int err = 0;
@@ -38,47 +35,29 @@
 	switch (sn9c102_get_bridge(cam)) {
 	case BRIDGE_SN9C101:
 	case BRIDGE_SN9C102:
-	err += sn9c102_write_reg(cam, 0x00, 0x10);
-	err += sn9c102_write_reg(cam, 0x00, 0x11);
-	err += sn9c102_write_reg(cam, 0x00, 0x14);
-	err += sn9c102_write_reg(cam, 0x20, 0x17);
-	err += sn9c102_write_reg(cam, 0x30, 0x19);
-	err += sn9c102_write_reg(cam, 0x09, 0x18);
+		err = sn9c102_write_const_regs(cam, {0x00, 0x10},
+					       {0x00, 0x11}, {0x00, 0x14},
+					       {0x20, 0x17}, {0x30, 0x19},
+					       {0x09, 0x18});
 		break;
 	case BRIDGE_SN9C103:
-		err += sn9c102_write_reg(cam, 0x00, 0x02);
-		err += sn9c102_write_reg(cam, 0x00, 0x03);
-		err += sn9c102_write_reg(cam, 0x1a, 0x04);
-		err += sn9c102_write_reg(cam, 0x20, 0x05);
-		err += sn9c102_write_reg(cam, 0x20, 0x06);
-		err += sn9c102_write_reg(cam, 0x20, 0x07);
-		err += sn9c102_write_reg(cam, 0x00, 0x10);
-		err += sn9c102_write_reg(cam, 0x00, 0x11);
-		err += sn9c102_write_reg(cam, 0x00, 0x14);
-		err += sn9c102_write_reg(cam, 0x20, 0x17);
-		err += sn9c102_write_reg(cam, 0x30, 0x19);
-		err += sn9c102_write_reg(cam, 0x09, 0x18);
-		err += sn9c102_write_reg(cam, 0x02, 0x1c);
-		err += sn9c102_write_reg(cam, 0x03, 0x1d);
-		err += sn9c102_write_reg(cam, 0x0f, 0x1e);
-		err += sn9c102_write_reg(cam, 0x0c, 0x1f);
-		err += sn9c102_write_reg(cam, 0x00, 0x20);
-		err += sn9c102_write_reg(cam, 0x10, 0x21);
-		err += sn9c102_write_reg(cam, 0x20, 0x22);
-		err += sn9c102_write_reg(cam, 0x30, 0x23);
-		err += sn9c102_write_reg(cam, 0x40, 0x24);
-		err += sn9c102_write_reg(cam, 0x50, 0x25);
-		err += sn9c102_write_reg(cam, 0x60, 0x26);
-		err += sn9c102_write_reg(cam, 0x70, 0x27);
-		err += sn9c102_write_reg(cam, 0x80, 0x28);
-		err += sn9c102_write_reg(cam, 0x90, 0x29);
-		err += sn9c102_write_reg(cam, 0xa0, 0x2a);
-		err += sn9c102_write_reg(cam, 0xb0, 0x2b);
-		err += sn9c102_write_reg(cam, 0xc0, 0x2c);
-		err += sn9c102_write_reg(cam, 0xd0, 0x2d);
-		err += sn9c102_write_reg(cam, 0xe0, 0x2e);
-		err += sn9c102_write_reg(cam, 0xf0, 0x2f);
-		err += sn9c102_write_reg(cam, 0xff, 0x30);
+		err = sn9c102_write_const_regs(cam, {0x00, 0x02},
+					       {0x00, 0x03}, {0x1a, 0x04},
+					       {0x20, 0x05}, {0x20, 0x06},
+					       {0x20, 0x07}, {0x00, 0x10},
+					       {0x00, 0x11}, {0x00, 0x14},
+					       {0x20, 0x17}, {0x30, 0x19},
+					       {0x09, 0x18}, {0x02, 0x1c},
+					       {0x03, 0x1d}, {0x0f, 0x1e},
+					       {0x0c, 0x1f}, {0x00, 0x20},
+					       {0x10, 0x21}, {0x20, 0x22},
+					       {0x30, 0x23}, {0x40, 0x24},
+					       {0x50, 0x25}, {0x60, 0x26},
+					       {0x70, 0x27}, {0x80, 0x28},
+					       {0x90, 0x29}, {0xa0, 0x2a},
+					       {0xb0, 0x2b}, {0xc0, 0x2c},
+					       {0xd0, 0x2d}, {0xe0, 0x2e},
+					       {0xf0, 0x2f}, {0xff, 0x30});
 		break;
 	default:
 		break;
@@ -328,15 +307,15 @@
 	switch (sn9c102_get_bridge(cam)) {
 	case BRIDGE_SN9C101:
 	case BRIDGE_SN9C102:
-		err += sn9c102_write_reg(cam, 0x01, 0x01); /* power down */
-		err += sn9c102_write_reg(cam, 0x40, 0x01); /* power on */
-		err += sn9c102_write_reg(cam, 0x28, 0x17); /* clock 24 MHz */
+		err = sn9c102_write_const_regs(cam,
+					       {0x01, 0x01}, /* power down */
+					       {0x40, 0x01}, /* power on */
+					       {0x28, 0x17});/* clock 24 MHz */
 		break;
 	case BRIDGE_SN9C103: /* do _not_ change anything! */
-		err += sn9c102_write_reg(cam, 0x09, 0x01);
-		err += sn9c102_write_reg(cam, 0x44, 0x01);
-		err += sn9c102_write_reg(cam, 0x44, 0x02);
-		err += sn9c102_write_reg(cam, 0x29, 0x17);
+		err = sn9c102_write_const_regs(cam, {0x09, 0x01},
+					       {0x44, 0x01}, {0x44, 0x02},
+					       {0x29, 0x17});
 		break;
 	default:
 		break;
diff --git a/drivers/media/video/sn9c102/sn9c102_sensor.h b/drivers/media/video/sn9c102/sn9c102_sensor.h
index 05f2942..1bbf64c 100644
--- a/drivers/media/video/sn9c102/sn9c102_sensor.h
+++ b/drivers/media/video/sn9c102/sn9c102_sensor.h
@@ -114,9 +114,17 @@
 extern int sn9c102_i2c_read(struct sn9c102_device*, u8 address);
 
 /* I/O on registers in the bridge. Could be used by the sensor methods too */
-extern int sn9c102_write_regs(struct sn9c102_device*, u8* buff, u16 index);
-extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index);
 extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index);
+extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index);
+extern int sn9c102_write_regs(struct sn9c102_device*, const u8 valreg[][2],
+			      int count);
+/*
+ * Write multiple registers with constant values.  For example:
+ * sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17}, {0x0f, 0x18});
+ */
+#define sn9c102_write_const_regs(device, data...) \
+	({ const static u8 _data[][2] = {data}; \
+	sn9c102_write_regs(device, _data, ARRAY_SIZE(_data)); })
 
 /*****************************************************************************/
 
diff --git a/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c b/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c
index 90023ad..0e7ec86 100644
--- a/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c
+++ b/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c
@@ -22,21 +22,14 @@
 #include "sn9c102_sensor.h"
 
 
-static struct sn9c102_sensor tas5110c1b;
-
-
 static int tas5110c1b_init(struct sn9c102_device* cam)
 {
 	int err = 0;
 
-	err += sn9c102_write_reg(cam, 0x01, 0x01);
-	err += sn9c102_write_reg(cam, 0x44, 0x01);
-	err += sn9c102_write_reg(cam, 0x00, 0x10);
-	err += sn9c102_write_reg(cam, 0x00, 0x11);
-	err += sn9c102_write_reg(cam, 0x0a, 0x14);
-	err += sn9c102_write_reg(cam, 0x60, 0x17);
-	err += sn9c102_write_reg(cam, 0x06, 0x18);
-	err += sn9c102_write_reg(cam, 0xfb, 0x19);
+	err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x44, 0x01},
+				       {0x00, 0x10}, {0x00, 0x11},
+				       {0x0a, 0x14}, {0x60, 0x17},
+				       {0x06, 0x18}, {0xfb, 0x19});
 
 	err += sn9c102_i2c_write(cam, 0xc0, 0x80);
 
@@ -98,7 +91,7 @@
 static struct sn9c102_sensor tas5110c1b = {
 	.name = "TAS5110C1B",
 	.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
-	.supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103,
+	.supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102,
 	.sysfs_ops = SN9C102_I2C_WRITE,
 	.frequency = SN9C102_I2C_100KHZ,
 	.interface = SN9C102_I2C_3WIRES,
@@ -146,7 +139,6 @@
 	const struct usb_device_id tas5110c1b_id_table[] = {
 		{ USB_DEVICE(0x0c45, 0x6001), },
 		{ USB_DEVICE(0x0c45, 0x6005), },
-		{ USB_DEVICE(0x0c45, 0x6007), },
 		{ USB_DEVICE(0x0c45, 0x60ab), },
 		{ }
 	};
diff --git a/drivers/media/video/sn9c102/sn9c102_tas5110d.c b/drivers/media/video/sn9c102/sn9c102_tas5110d.c
new file mode 100644
index 0000000..83a39e8
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_tas5110d.c
@@ -0,0 +1,118 @@
+/***************************************************************************
+ * Plug-in for TAS5110D image sensor connected to the SN9C1xx PC Camera    *
+ * Controllers                                                             *
+ *                                                                         *
+ * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it>       *
+ *                                                                         *
+ * 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 "sn9c102_sensor.h"
+
+
+static int tas5110d_init(struct sn9c102_device* cam)
+{
+	int err;
+
+	err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x04, 0x01},
+				       {0x0a, 0x14}, {0x60, 0x17},
+				       {0x06, 0x18}, {0xfb, 0x19});
+
+	err += sn9c102_i2c_write(cam, 0x9a, 0xca);
+
+	return err;
+}
+
+
+static int tas5110d_set_crop(struct sn9c102_device* cam,
+			     const struct v4l2_rect* rect)
+{
+	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+	int err = 0;
+	u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69,
+	   v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9;
+
+	err += sn9c102_write_reg(cam, h_start, 0x12);
+	err += sn9c102_write_reg(cam, v_start, 0x13);
+
+	err += sn9c102_write_reg(cam, 0x14, 0x1a);
+	err += sn9c102_write_reg(cam, 0x0a, 0x1b);
+
+	return err;
+}
+
+
+static int tas5110d_set_pix_format(struct sn9c102_device* cam,
+				     const struct v4l2_pix_format* pix)
+{
+	int err = 0;
+
+	if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+		err += sn9c102_write_reg(cam, 0x3b, 0x19);
+	else
+		err += sn9c102_write_reg(cam, 0xfb, 0x19);
+
+	return err;
+}
+
+
+static struct sn9c102_sensor tas5110d = {
+	.name = "TAS5110D",
+	.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+	.supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102,
+	.sysfs_ops = SN9C102_I2C_WRITE,
+	.frequency = SN9C102_I2C_100KHZ,
+	.interface = SN9C102_I2C_2WIRES,
+	.i2c_slave_id = 0x61,
+	.init = &tas5110d_init,
+	.cropcap = {
+		.bounds = {
+			.left = 0,
+			.top = 0,
+			.width = 352,
+			.height = 288,
+		},
+		.defrect = {
+			.left = 0,
+			.top = 0,
+			.width = 352,
+			.height = 288,
+		},
+	},
+	.set_crop = &tas5110d_set_crop,
+	.pix_format = {
+		.width = 352,
+		.height = 288,
+		.pixelformat = V4L2_PIX_FMT_SBGGR8,
+		.priv = 8,
+	},
+	.set_pix_format = &tas5110d_set_pix_format
+};
+
+
+int sn9c102_probe_tas5110d(struct sn9c102_device* cam)
+{
+	const struct usb_device_id tas5110d_id_table[] = {
+		{ USB_DEVICE(0x0c45, 0x6007), },
+		{ }
+	};
+
+	if (!sn9c102_match_id(cam, tas5110d_id_table))
+		return -ENODEV;
+
+	sn9c102_attach_sensor(cam, &tas5110d);
+
+	return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c b/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c
index cb1b318..5040650 100644
--- a/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c
+++ b/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c
@@ -22,21 +22,14 @@
 #include "sn9c102_sensor.h"
 
 
-static struct sn9c102_sensor tas5130d1b;
-
-
 static int tas5130d1b_init(struct sn9c102_device* cam)
 {
-	int err = 0;
+	int err;
 
-	err += sn9c102_write_reg(cam, 0x01, 0x01);
-	err += sn9c102_write_reg(cam, 0x20, 0x17);
-	err += sn9c102_write_reg(cam, 0x04, 0x01);
-	err += sn9c102_write_reg(cam, 0x01, 0x10);
-	err += sn9c102_write_reg(cam, 0x00, 0x11);
-	err += sn9c102_write_reg(cam, 0x00, 0x14);
-	err += sn9c102_write_reg(cam, 0x60, 0x17);
-	err += sn9c102_write_reg(cam, 0x07, 0x18);
+	err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x20, 0x17},
+				       {0x04, 0x01}, {0x01, 0x10},
+				       {0x00, 0x11}, {0x00, 0x14},
+				       {0x60, 0x17}, {0x07, 0x18});
 
 	return err;
 }
@@ -99,7 +92,7 @@
 static struct sn9c102_sensor tas5130d1b = {
 	.name = "TAS5130D1B",
 	.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
-	.supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103,
+	.supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102,
 	.sysfs_ops = SN9C102_I2C_WRITE,
 	.frequency = SN9C102_I2C_100KHZ,
 	.interface = SN9C102_I2C_3WIRES,
diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c
index d1ccc06..43225802 100644
--- a/drivers/media/video/tda7432.c
+++ b/drivers/media/video/tda7432.c
@@ -45,7 +45,6 @@
 #include <linux/slab.h>
 #include <linux/videodev.h>
 #include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
 
 #include <media/v4l2-common.h>
 #include <media/i2c-addr.h>
diff --git a/drivers/media/video/tda8290.c b/drivers/media/video/tda8290.c
index 027c8a0..1a1bef0 100644
--- a/drivers/media/video/tda8290.c
+++ b/drivers/media/video/tda8290.c
@@ -192,14 +192,52 @@
 	{ .lomax =     0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0}   /* End */
 };
 
+static void tda827xa_lna_gain(struct i2c_client *c, int high)
+{
+	struct tuner *t = i2c_get_clientdata(c);
+	unsigned char buf[] = {0x22, 0x01};
+	int arg;
+	struct i2c_msg msg = {.addr = c->addr, .flags = 0, .buf = buf, .len = sizeof(buf)};
+	if (t->config) {
+		if (high)
+			tuner_dbg("setting LNA to high gain\n");
+		else
+			tuner_dbg("setting LNA to low gain\n");
+	}
+	switch (t->config) {
+	case 0: /* no LNA */
+		break;
+	case 1: /* switch is GPIO 0 of tda8290 */
+	case 2:
+		/* turn Vsync on */
+		if (t->std & V4L2_STD_MN)
+			arg = 1;
+		else
+			arg = 0;
+		if (t->tuner_callback)
+			t->tuner_callback(c->adapter->algo_data, 1, arg);
+		buf[1] = high ? 0 : 1;
+		if (t->config == 2)
+			buf[1] = high ? 1 : 0;
+		i2c_transfer(c->adapter, &msg, 1);
+		break;
+	case 3: /* switch with GPIO of saa713x */
+		if (t->tuner_callback)
+			t->tuner_callback(c->adapter->algo_data, 0, high);
+		break;
+	}
+}
+
 static void tda827xa_tune(struct i2c_client *c, u16 ifc, unsigned int freq)
 {
-	unsigned char tuner_reg[14];
-	unsigned char reg2[2];
+	unsigned char tuner_reg[11];
 	u32 N;
 	int i;
 	struct tuner *t = i2c_get_clientdata(c);
-	struct i2c_msg msg = {.addr = t->tda827x_addr, .flags = 0};
+	struct i2c_msg msg = {.addr = t->tda827x_addr, .flags = 0, .buf = tuner_reg};
+
+	tda827xa_lna_gain( c, 1);
+	msleep(10);
 
 	if (t->mode == V4L2_TUNER_RADIO)
 		freq = freq / 1000;
@@ -222,48 +260,58 @@
 	tuner_reg[5] = (tda827xa_analog[i].spd << 5) + (tda827xa_analog[i].svco << 3) +
 			tda827xa_analog[i].sbs;
 	tuner_reg[6] = 0x8b + (tda827xa_analog[i].gc3 << 4);
-	tuner_reg[7] = 0x0c;
+	tuner_reg[7] = 0x1c;
 	tuner_reg[8] = 4;
 	tuner_reg[9] = 0x20;
-	tuner_reg[10] = 0xff;
-	tuner_reg[11] = 0xe0;
-	tuner_reg[12] = 0;
-	tuner_reg[13] = 0x39 + (t->tda827x_lpsel << 1);
-
-	msg.buf = tuner_reg;
-	msg.len = 14;
+	tuner_reg[10] = 0x00;
+	msg.len = 11;
 	i2c_transfer(c->adapter, &msg, 1);
 
-	msg.buf= reg2;
+	tuner_reg[0] = 0x90;
+	tuner_reg[1] = 0xff;
+	tuner_reg[2] = 0xe0;
+	tuner_reg[3] = 0;
+	tuner_reg[4] = 0x99 + (t->tda827x_lpsel << 1);
+	msg.len = 5;
+	i2c_transfer(c->adapter, &msg, 1);
+
+	tuner_reg[0] = 0xa0;
+	tuner_reg[1] = 0xc0;
 	msg.len = 2;
-	reg2[0] = 0x60;
-	reg2[1] = 0x3c;
 	i2c_transfer(c->adapter, &msg, 1);
 
-	reg2[0] = 0xa0;
-	reg2[1] = 0xc0;
+	tuner_reg[0] = 0x30;
+	tuner_reg[1] = 0x10 + tda827xa_analog[i].scr;
 	i2c_transfer(c->adapter, &msg, 1);
 
-	msleep(2);
-	reg2[0] = 0x30;
-	reg2[1] = 0x10 + tda827xa_analog[i].scr;
+	msg.flags = I2C_M_RD;
+	i2c_transfer(c->adapter, &msg, 1);
+	msg.flags = 0;
+	tuner_reg[1] >>= 4;
+	tuner_dbg("AGC2 gain is: %d\n", tuner_reg[1]);
+	if (tuner_reg[1] < 1)
+		tda827xa_lna_gain( c, 0);
+
+	msleep(100);
+	tuner_reg[0] = 0x60;
+	tuner_reg[1] = 0x3c;
 	i2c_transfer(c->adapter, &msg, 1);
 
-	msleep(550);
-	reg2[0] = 0x50;
-	reg2[1] = 0x8f + (tda827xa_analog[i].gc3 << 4);
+	msleep(163);
+	tuner_reg[0] = 0x50;
+	tuner_reg[1] = 0x8f + (tda827xa_analog[i].gc3 << 4);
 	i2c_transfer(c->adapter, &msg, 1);
 
-	reg2[0] = 0x80;
-	reg2[1] = 0x28;
+	tuner_reg[0] = 0x80;
+	tuner_reg[1] = 0x28;
 	i2c_transfer(c->adapter, &msg, 1);
 
-	reg2[0] = 0xb0;
-	reg2[1] = 0x01;
+	tuner_reg[0] = 0xb0;
+	tuner_reg[1] = 0x01;
 	i2c_transfer(c->adapter, &msg, 1);
 
-	reg2[0] = 0xc0;
-	reg2[1] = 0x19 + (t->tda827x_lpsel << 1);
+	tuner_reg[0] = 0xc0;
+	tuner_reg[1] = 0x19 + (t->tda827x_lpsel << 1);
 	i2c_transfer(c->adapter, &msg, 1);
 }
 
@@ -319,7 +367,9 @@
 	unsigned char addr_pll_stat = 0x1b;
 	unsigned char adc_sat, agc_stat,
 		      pll_stat;
+	int i;
 
+	tuner_dbg("tda827xa config is 0x%02x\n", t->config);
 	i2c_master_send(c, easy_mode, 2);
 	i2c_master_send(c, agc_out_on, 2);
 	i2c_master_send(c, soft_reset, 2);
@@ -340,17 +390,22 @@
 		tda827xa_tune(c, ifc, freq);
 	else
 		tda827x_tune(c, ifc, freq);
+	for (i = 0; i < 3; i++) {
+		i2c_master_send(c, &addr_pll_stat, 1);
+		i2c_master_recv(c, &pll_stat, 1);
+		if (pll_stat & 0x80) {
+			i2c_master_send(c, &addr_adc_sat, 1);
+			i2c_master_recv(c, &adc_sat, 1);
+			i2c_master_send(c, &addr_agc_stat, 1);
+			i2c_master_recv(c, &agc_stat, 1);
+			tuner_dbg("tda8290 is locked, AGC: %d\n", agc_stat);
+			break;
+		} else {
+			tuner_dbg("tda8290 not locked, no signal?\n");
+			msleep(100);
+		}
+	}
 	/* adjust headroom resp. gain */
-	i2c_master_send(c, &addr_adc_sat, 1);
-	i2c_master_recv(c, &adc_sat, 1);
-	i2c_master_send(c, &addr_agc_stat, 1);
-	i2c_master_recv(c, &agc_stat, 1);
-	i2c_master_send(c, &addr_pll_stat, 1);
-	i2c_master_recv(c, &pll_stat, 1);
-	if (pll_stat & 0x80)
-		tuner_dbg("tda8290 is locked, AGC: %d\n", agc_stat);
-	else
-		tuner_dbg("tda8290 not locked, no signal?\n");
 	if ((agc_stat > 115) || (!(pll_stat & 0x80) && (adc_sat < 20))) {
 		tuner_dbg("adjust gain, step 1. Agc: %d, ADC stat: %d, lock: %d\n",
 			   agc_stat, adc_sat, pll_stat & 0x80);
@@ -407,7 +462,6 @@
 	char* mode;
 
 	t->tda827x_lpsel = 0;
-	mode = "xx";
 	if (t->std & V4L2_STD_MN) {
 		t->sgIF = 92;
 		t->tda8290_easy_mode = 0x01;
@@ -437,8 +491,12 @@
 		t->sgIF = 20;
 		t->tda8290_easy_mode = 0x40;
 		mode = "LC";
+	} else {
+		t->sgIF = 124;
+		t->tda8290_easy_mode = 0x10;
+		mode = "xx";
 	}
-    tuner_dbg("setting tda8290 to system %s\n", mode);
+	tuner_dbg("setting tda8290 to system %s\n", mode);
 }
 
 static void set_tv_freq(struct i2c_client *c, unsigned int freq)
@@ -487,11 +545,16 @@
 
 static void tda8290_init_if(struct i2c_client *c)
 {
+	struct tuner *t = i2c_get_clientdata(c);
 	unsigned char set_VS[] = { 0x30, 0x6F };
+	unsigned char set_GP00_CF[] = { 0x20, 0x01 };
 	unsigned char set_GP01_CF[] = { 0x20, 0x0B };
 
+	if ((t->config == 1) || (t->config == 2))
+		i2c_master_send(c, set_GP00_CF, 2);
+	else
+		i2c_master_send(c, set_GP01_CF, 2);
 	i2c_master_send(c, set_VS, 2);
-	i2c_master_send(c, set_GP01_CF, 2);
 }
 
 static void tda8290_init_tuner(struct i2c_client *c)
@@ -576,6 +639,7 @@
 	t->has_signal = has_signal;
 	t->standby = standby;
 	t->tda827x_lpsel = 0;
+	t->mode = V4L2_TUNER_ANALOG_TV;
 
 	tda8290_init_tuner(c);
 	tda8290_init_if(c);
diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c
index 00f0e8b..d110441 100644
--- a/drivers/media/video/tda9875.c
+++ b/drivers/media/video/tda9875.c
@@ -27,7 +27,6 @@
 #include <linux/videodev.h>
 #include <media/v4l2-common.h>
 #include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
 #include <linux/init.h>
 
 
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index 15dbc6b..505591a 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -144,7 +144,8 @@
 }
 
 static void set_type(struct i2c_client *c, unsigned int type,
-		     unsigned int new_mode_mask)
+		     unsigned int new_mode_mask, unsigned int new_config,
+		     int (*tuner_callback) (void *dev, int command,int arg))
 {
 	struct tuner *t = i2c_get_clientdata(c);
 	unsigned char buffer[4];
@@ -159,15 +160,20 @@
 		return;
 	}
 
+	t->type = type;
+	t->config = new_config;
+	if (tuner_callback != NULL) {
+		tuner_dbg("defining GPIO callback\n");
+		t->tuner_callback = tuner_callback;
+	}
+
 	/* This code detects calls by card attach_inform */
 	if (NULL == t->i2c.dev.driver) {
 		tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
 
-		t->type=type;
 		return;
 	}
 
-	t->type = type;
 	switch (t->type) {
 	case TUNER_MT2032:
 		microtune_init(c);
@@ -234,10 +240,11 @@
 
 	tuner_dbg("set addr for type %i\n", t->type);
 
-	if ( t->type == UNSET && ((tun_setup->addr == ADDR_UNSET &&
-		(t->mode_mask & tun_setup->mode_mask)) ||
-		tun_setup->addr == c->addr)) {
-			set_type(c, tun_setup->type, tun_setup->mode_mask);
+	if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
+		(t->mode_mask & tun_setup->mode_mask))) ||
+		(tun_setup->addr == c->addr)) {
+			set_type(c, tun_setup->type, tun_setup->mode_mask,
+				 tun_setup->config, tun_setup->tuner_callback);
 	}
 }
 
@@ -496,7 +503,7 @@
 register_client:
 	tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
 	i2c_attach_client (&t->i2c);
-	set_type (&t->i2c,t->type, t->mode_mask);
+	set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
 	return 0;
 }
 
@@ -576,10 +583,11 @@
 	switch (cmd) {
 	/* --- configuration --- */
 	case TUNER_SET_TYPE_ADDR:
-		tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x\n",
+		tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n",
 				((struct tuner_setup *)arg)->type,
 				((struct tuner_setup *)arg)->addr,
-				((struct tuner_setup *)arg)->mode_mask);
+				((struct tuner_setup *)arg)->mode_mask,
+				((struct tuner_setup *)arg)->config);
 
 		set_addr(client, (struct tuner_setup *)arg);
 		break;
diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c
index d506dfa..a2da5d2 100644
--- a/drivers/media/video/tvaudio.c
+++ b/drivers/media/video/tvaudio.c
@@ -25,7 +25,6 @@
 #include <linux/slab.h>
 #include <linux/videodev.h>
 #include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
 #include <linux/init.h>
 #include <linux/smp_lock.h>
 #include <linux/kthread.h>
@@ -33,6 +32,7 @@
 
 #include <media/tvaudio.h>
 #include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
 
 #include <media/i2c-addr.h>
 
@@ -1775,6 +1775,9 @@
 			/* the thread will call checkmode() later */
 		}
 		break;
+
+	case VIDIOC_G_CHIP_IDENT:
+		return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_TVAUDIO, 0);
 	}
 	return 0;
 }
diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c
index 4e7c1fa..a1136da 100644
--- a/drivers/media/video/tveeprom.c
+++ b/drivers/media/video/tveeprom.c
@@ -163,7 +163,7 @@
 	/* 60-69 */
 	{ TUNER_PHILIPS_FM1216ME_MK3, "LG S001D MK3"},
 	{ TUNER_ABSENT,        "LG M001D MK3"},
-	{ TUNER_ABSENT,        "LG S701D MK3"},
+	{ TUNER_PHILIPS_FM1216ME_MK3, "LG S701D MK3"},
 	{ TUNER_ABSENT,        "LG M701D MK3"},
 	{ TUNER_ABSENT,        "Temic 4146FM5"},
 	{ TUNER_ABSENT,        "Temic 4136FY5"},
@@ -229,6 +229,36 @@
 	/* 120-129 */
 	{ TUNER_ABSENT,        "Xceive XC3028"},
 	{ TUNER_ABSENT,        "Philips FQ1216LME MK5"},
+	{ TUNER_ABSENT,        "Philips FQD1216LME"},
+	{ TUNER_ABSENT,        "Conexant CX24118A"},
+	{ TUNER_ABSENT,        "TCL DMF11WIP"},
+	{ TUNER_ABSENT,        "TCL MFNM05_4H_E"},
+	{ TUNER_ABSENT,        "TCL MNM05_4H_E"},
+	{ TUNER_ABSENT,        "TCL MPE05_2H_E"},
+	{ TUNER_ABSENT,        "TCL MQNM05_4_U"},
+	{ TUNER_ABSENT,        "TCL M2523_5NH_E"},
+	/* 130-139 */
+	{ TUNER_ABSENT,        "TCL M2523_3DBH_E"},
+	{ TUNER_ABSENT,        "TCL M2523_3DIH_E"},
+	{ TUNER_ABSENT,        "TCL MFPE05_2_U"},
+	{ TUNER_ABSENT,        "Philips FMD1216MEX"},
+	{ TUNER_ABSENT,        "Philips FRH2036B"},
+	{ TUNER_ABSENT,        "Panasonic ENGF75_01GF"},
+	{ TUNER_ABSENT,        "MaxLinear MXL5005"},
+	{ TUNER_ABSENT,        "MaxLinear MXL5003"},
+	{ TUNER_ABSENT,        "Xceive XC2028"},
+	{ TUNER_ABSENT,        "Microtune MT2131"},
+	/* 140-149 */
+	{ TUNER_ABSENT,        "Philips 8275A_8295"},
+	{ TUNER_ABSENT,        "TCL MF02GIP_5N_E"},
+	{ TUNER_ABSENT,        "TCL MF02GIP_3DB_E"},
+	{ TUNER_ABSENT,        "TCL MF02GIP_3DI_E"},
+	{ TUNER_ABSENT,        "Microtune MT2266"},
+	{ TUNER_ABSENT,        "TCL MF10WPP_4N_E"},
+	{ TUNER_ABSENT,        "LG TAPQ_H702F"},
+	{ TUNER_ABSENT,        "TCL M09WPP_4N_E"},
+	{ TUNER_ABSENT,        "MaxLinear MXL5005_v2"},
+	{ TUNER_ABSENT,        "Philips 18271_8295"},
 };
 
 static struct HAUPPAUGE_AUDIOIC
@@ -280,11 +310,16 @@
 	{AUDIO_CHIP_INTERNAL, "CX883"},
 	{AUDIO_CHIP_INTERNAL, "CX882"},
 	{AUDIO_CHIP_INTERNAL, "CX25840"},
-	/* 35-38 */
+	/* 35-39 */
 	{AUDIO_CHIP_INTERNAL, "CX25841"},
 	{AUDIO_CHIP_INTERNAL, "CX25842"},
 	{AUDIO_CHIP_INTERNAL, "CX25843"},
 	{AUDIO_CHIP_INTERNAL, "CX23418"},
+	{AUDIO_CHIP_INTERNAL, "CX23885"},
+	/* 40-42 */
+	{AUDIO_CHIP_INTERNAL, "CX23888"},
+	{AUDIO_CHIP_INTERNAL, "SAA7131"},
+	{AUDIO_CHIP_INTERNAL, "CX23887"},
 };
 
 /* This list is supplied by Hauppauge. Thanks! */
@@ -301,8 +336,10 @@
 	"CX880", "CX881", "CX883", "SAA7111", "SAA7113",
 	/* 25-29 */
 	"CX882", "TVP5150A", "CX25840", "CX25841", "CX25842",
-	/* 30-31 */
-	"CX25843", "CX23418",
+	/* 30-34 */
+	"CX25843", "CX23418", "NEC61153", "CX23885", "CX23888",
+	/* 35-37 */
+	"SAA7131", "CX25837", "CX23887"
 };
 
 static int hasRadioTuner(int tunerType)
diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c
index 28d1133..0b2a961 100644
--- a/drivers/media/video/upd64031a.c
+++ b/drivers/media/video/upd64031a.c
@@ -27,6 +27,7 @@
 #include <linux/i2c.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
 #include <media/upd64031a.h>
 
 // --------------------- read registers functions define -----------------------
@@ -179,6 +180,9 @@
 	}
 #endif
 
+	case VIDIOC_G_CHIP_IDENT:
+		return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_UPD64031A, 0);
+
 	default:
 		break;
 	}
diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c
index fe38224..401bd21 100644
--- a/drivers/media/video/upd64083.c
+++ b/drivers/media/video/upd64083.c
@@ -26,6 +26,7 @@
 #include <linux/i2c.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
 #include <media/upd64083.h>
 
 MODULE_DESCRIPTION("uPD64083 driver");
@@ -155,6 +156,10 @@
 		break;
 	}
 #endif
+
+	case VIDIOC_G_CHIP_IDENT:
+		return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_UPD64083, 0);
+
 	default:
 		break;
 	}
diff --git a/drivers/media/video/usbvideo/usbvideo.c b/drivers/media/video/usbvideo/usbvideo.c
index d34d8c8..687f026 100644
--- a/drivers/media/video/usbvideo/usbvideo.c
+++ b/drivers/media/video/usbvideo/usbvideo.c
@@ -628,24 +628,21 @@
 /* ******************************************************************** */
 
 /* XXX: this piece of crap really wants some error handling.. */
-static void usbvideo_ClientIncModCount(struct uvd *uvd)
+static int usbvideo_ClientIncModCount(struct uvd *uvd)
 {
 	if (uvd == NULL) {
 		err("%s: uvd == NULL", __FUNCTION__);
-		return;
+		return -EINVAL;
 	}
 	if (uvd->handle == NULL) {
 		err("%s: uvd->handle == NULL", __FUNCTION__);
-		return;
-	}
-	if (uvd->handle->md_module == NULL) {
-		err("%s: uvd->handle->md_module == NULL", __FUNCTION__);
-		return;
+		return -EINVAL;
 	}
 	if (!try_module_get(uvd->handle->md_module)) {
 		err("%s: try_module_get() == 0", __FUNCTION__);
-		return;
+		return -ENODEV;
 	}
+	return 0;
 }
 
 static void usbvideo_ClientDecModCount(struct uvd *uvd)
@@ -712,8 +709,6 @@
 	cams->num_cameras = num_cams;
 	cams->cam = (struct uvd *) &cams[1];
 	cams->md_module = md;
-	if (cams->md_module == NULL)
-		warn("%s: module == NULL!", __FUNCTION__);
 	mutex_init(&cams->lock);	/* to 1 == available */
 
 	for (i = 0; i < num_cams; i++) {
@@ -1119,7 +1114,8 @@
 	if (uvd->debug > 1)
 		info("%s($%p)", __FUNCTION__, dev);
 
-	usbvideo_ClientIncModCount(uvd);
+	if (0 < usbvideo_ClientIncModCount(uvd))
+		return -ENODEV;
 	mutex_lock(&uvd->lock);
 
 	if (uvd->user) {
diff --git a/drivers/media/video/usbvision/usbvision-cards.c b/drivers/media/video/usbvision/usbvision-cards.c
index a40e583..13f69fe 100644
--- a/drivers/media/video/usbvision/usbvision-cards.c
+++ b/drivers/media/video/usbvision/usbvision-cards.c
@@ -1,6 +1,6 @@
 /*
- * USBVISION.H
- *  usbvision header file
+ *  usbvision-cards.c
+ *  usbvision cards definition file
  *
  * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
  *
@@ -28,129 +28,1060 @@
 #include <media/v4l2-dev.h>
 #include <media/tuner.h>
 #include "usbvision.h"
+#include "usbvision-cards.h"
 
 /* Supported Devices: A table for usbvision.c*/
 struct usbvision_device_data_st  usbvision_device_data[] = {
-	{0xFFF0, 0xFFF0, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC,  1, 1, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1, -1, -1, -1, "Custom Dummy USBVision Device"},
-	{0x0A6F, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC,  1, 0, 1, 0, 0,                          -1, -1, -1, -1, -1, "Xanboo"},
-	{0x050D, 0x0208, -1, CODEC_SAA7113, 2, V4L2_STD_PAL,   1, 0, 1, 0, 0,                          -1, -1,  0,  3,  7, "Belkin USBView II"},
-	{0x0571, 0x0002,  0, CODEC_SAA7111, 2, V4L2_STD_PAL,   0, 0, 1, 0, 0,                          -1, -1, -1, -1,  7, "echoFX InterView Lite"},
-	{0x0573, 0x0003, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC,  1, 0, 1, 0, 0,                          -1, -1, -1, -1, -1, "USBGear USBG-V1 resp. HAMA USB"},
-	{0x0573, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC,  0, 0, 1, 0, 0,                          -1, -1,  0,  3,  7, "D-Link V100"},
-	{0x0573, 0x2000, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC,  1, 0, 1, 0, 0,                          -1, -1, -1, -1, -1, "X10 USB Camera"},
-	{0x0573, 0x2d00, -1, CODEC_SAA7111, 2, V4L2_STD_PAL,   1, 0, 1, 0, 0,                          -1, -1, -1,  3,  7, "Osprey 50"},
-	{0x0573, 0x2d01, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC,  0, 0, 1, 0, 0,			       -1, -1,  0,  3,  7, "Hauppauge USB-Live Model 600"},
-	{0x0573, 0x2101, -1, CODEC_SAA7113, 2, V4L2_STD_PAL,   2, 0, 1, 0, 0,                          -1, -1,  0,  3,  7, "Zoran Co. PMD (Nogatech) AV-grabber Manhattan"},
-	{0x0573, 0x4100, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC,  1, 1, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1, -1, 20, -1, "Nogatech USB-TV (NTSC) FM"},
-	{0x0573, 0x4110, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC,  1, 1, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1, -1, 20, -1, "PNY USB-TV (NTSC) FM"},
-	{0x0573, 0x4450,  0, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_PHILIPS_PAL,          -1, -1,  0,  3,  7, "PixelView PlayTv-USB PRO (PAL) FM"},
-	{0x0573, 0x4550,  0, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_PHILIPS_PAL,          -1, -1,  0,  3,  7, "ZTV ZT-721 2.4GHz USB A/V Receiver"},
-	{0x0573, 0x4d00, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC,  1, 0, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1, -1, 20, -1, "Hauppauge WinTv-USB USA"},
-	{0x0573, 0x4d01, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC,  1, 0, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1, -1, -1, -1, "Hauppauge WinTv-USB"},
-	{0x0573, 0x4d02, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC,  1, 0, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (NTSC)"},
-	{0x0573, 0x4d03, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM,        -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (SECAM) "},
-	{0x0573, 0x4d10, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC,  1, 1, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (NTSC) FM"},
-	{0x0573, 0x4d11, -1, CODEC_SAA7111, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_PHILIPS_PAL,          -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (PAL) FM"},
-	{0x0573, 0x4d12, -1, CODEC_SAA7111, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_PHILIPS_PAL,          -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (PAL) FM"},
-	{0x0573, 0x4d2a,  0, CODEC_SAA7113, 3, V4L2_STD_NTSC,  1, 1, 1, 1, TUNER_MICROTUNE_4049FM5,    -1, -1,  0,  3,  7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B285"},
-	{0x0573, 0x4d2b,  0, CODEC_SAA7113, 3, V4L2_STD_NTSC,  1, 1, 1, 1, TUNER_MICROTUNE_4049FM5,    -1, -1,  0,  3,  7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B282"},
-	{0x0573, 0x4d2c,  0, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 0, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1,  0,  3,  7, "Hauppauge WinTv USB (PAL/SECAM) 40209 Rev E1A5"},
-	{0x0573, 0x4d20,  0, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_PHILIPS_PAL,          -1, -1,  0,  3,  7, "Hauppauge WinTv-USB II (PAL) FM Model 40201 Rev B226"},
-	{0x0573, 0x4d21,  0, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 0, 1, 1, TUNER_PHILIPS_PAL,          -1, -1,  0,  3,  7, "Hauppauge WinTv-USB II (PAL)"},
-	{0x0573, 0x4d22,  0, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 0, 1, 1, TUNER_PHILIPS_PAL,          -1, -1,  0,  3,  7, "Hauppauge WinTv-USB II (PAL) MODEL 566"},
-	{0x0573, 0x4d23, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM,        -1, -1,  0,  3,  7, "Hauppauge WinTv-USB (SECAM) 4D23"},
-	{0x0573, 0x4d25, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM,        -1, -1,  0,  3,  7, "Hauppauge WinTv-USB (SECAM) Model 40209 Rev B234"},
-	{0x0573, 0x4d26, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM,        -1, -1,  0,  3,  7, "Hauppauge WinTv-USB (SECAM) Model 40209 Rev B243"},
-	{0x0573, 0x4d27, -1, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL,       -1, -1,  0,  3,  7, "Hauppauge WinTv-USB Model 40204 Rev B281"},
-	{0x0573, 0x4d28, -1, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL,       -1, -1,  0,  3,  7, "Hauppauge WinTv-USB Model 40204 Rev B283"},
-	{0x0573, 0x4d29, -1, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 0, 1, 1, TUNER_PHILIPS_PAL,          -1, -1,  0,  3,  7, "Hauppauge WinTv-USB Model 40205 Rev B298"},
-	{0x0573, 0x4d30, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC,  1, 1, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1,  0,  3,  7, "Hauppauge WinTv-USB FM Model 40211 Rev B123"},
-	{0x0573, 0x4d31,  0, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_PHILIPS_PAL,          -1, -1,  0,  3,  7, "Hauppauge WinTv-USB III (PAL) FM Model 568"},
-	{0x0573, 0x4d32,  0, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_PHILIPS_PAL,          -1, -1,  0,  3,  7, "Hauppauge WinTv-USB III (PAL) FM Model 573"},
-	{0x0573, 0x4d35,  0, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_MICROTUNE_4049FM5,    -1, -1,  0,  3,  7, "Hauppauge WinTv-USB III (PAL) FM Model 40219 Rev B252"},
-	{0x0573, 0x4d37,  0, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1,  0,  3,  7, "Hauppauge WinTV USB device Model 40219 Rev E189"},
-	{0x0768, 0x0006, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC,  1, 1, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1,  5,  5, -1, "Camtel Technology USB TV Genie Pro FM Model TVB330"},
-	{0x07d0, 0x0001, -1, CODEC_SAA7113, 2, V4L2_STD_PAL,   0, 0, 1, 0, 0,                          -1, -1,  0,  3,  7, "Digital Video Creator I"},
-	{0x07d0, 0x0002, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC,  0, 0, 1, 0, 0,   		       -1, -1, 82, 20,  7, "Global Village GV-007 (NTSC)"},
-	{0x07d0, 0x0003,  0, CODEC_SAA7113, 2, V4L2_STD_NTSC,  0, 0, 1, 0, 0,                          -1, -1,  0,  3,  7, "Dazzle Fusion Model DVC-50 Rev 1 (NTSC)"},
-	{0x07d0, 0x0004,  0, CODEC_SAA7113, 2, V4L2_STD_PAL,   0, 0, 1, 0, 0,                          -1, -1,  0,  3,  7, "Dazzle Fusion Model DVC-80 Rev 1 (PAL)"},
-	{0x07d0, 0x0005,  0, CODEC_SAA7113, 2, V4L2_STD_SECAM, 0, 0, 1, 0, 0,			       -1, -1,  0,  3,  7, "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)"},
-	{0x2304, 0x010d, -1, CODEC_SAA7111, 3, V4L2_STD_PAL,   1, 0, 0, 1, TUNER_TEMIC_4066FY5_PAL_I,  -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (PAL)"},
-	{0x2304, 0x0109, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM,        -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (SECAM)"},
-	{0x2304, 0x0110, -1, CODEC_SAA7111, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_PHILIPS_PAL,          -1, -1,128, 23, -1, "Pinnacle Studio PCTV USB (PAL) FM"},
-	{0x2304, 0x0111, -1, CODEC_SAA7111, 3, V4L2_STD_PAL,   1, 0, 1, 1, TUNER_PHILIPS_PAL,          -1, -1, -1, -1, -1, "Miro PCTV USB"},
-	{0x2304, 0x0112, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC,  1, 1, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (NTSC) FM"},
-	{0x2304, 0x0210, -1, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL,    -1, -1,  0,  3,  7, "Pinnacle Studio PCTV USB (PAL) FM"},
-	{0x2304, 0x0212, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC,  1, 1, 1, 1, TUNER_TEMIC_4039FR5_NTSC,   -1, -1,  0,  3,  7, "Pinnacle Studio PCTV USB (NTSC) FM"},
-	{0x2304, 0x0214, -1, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL,    -1, -1,  0,  3,  7, "Pinnacle Studio PCTV USB (PAL) FM"},
-	{0x2304, 0x0300, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC,  1, 0, 1, 0, 0,                          -1, -1,  0,  3,  7, "Pinnacle Studio Linx Video input cable (NTSC)"},
-	{0x2304, 0x0301, -1, CODEC_SAA7113, 2, V4L2_STD_PAL,   1, 0, 1, 0, 0,                          -1, -1,  0,  3,  7, "Pinnacle Studio Linx Video input cable (PAL)"},
-	{0x2304, 0x0419, -1, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL,    -1, -1,  0,  3,  7, "Pinnacle PCTV Bungee USB (PAL) FM"},
-	{0x2400, 0x4200, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC,  1, 0, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1, -1, -1, -1, "Hauppauge WinTv-USB"},
-	{}  /* Terminating entry */
+	[XANBOO] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 4,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 0,
+		.TunerType     = 0,
+		.X_Offset      = -1,
+		.Y_Offset      = -1,
+		.ModelString   = "Xanboo",
+	},
+	[BELKIN_VIDEOBUS_II] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 2,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 0,
+		.TunerType     = 0,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Belkin USB VideoBus II Adapter",
+	},
+	[BELKIN_VIDEOBUS] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 2,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 0,
+		.TunerType     = 0,
+		.X_Offset      = -1,
+		.Y_Offset      = -1,
+		.ModelString   = "Belkin Components USB VideoBus",
+	},
+	[BELKIN_USB_VIDEOBUS_II] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 2,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 0,
+		.TunerType     = 0,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Belkin USB VideoBus II",
+	},
+	[ECHOFX_INTERVIEW_LITE] = {
+		.Interface     = 0,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 2,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 0,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 0,
+		.TunerType     = 0,
+		.X_Offset      = -1,
+		.Y_Offset      = -1,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "echoFX InterView Lite",
+	},
+	[USBGEAR_USBG_V1] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 2,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 0,
+		.TunerType     = 0,
+		.X_Offset      = -1,
+		.Y_Offset      = -1,
+		.ModelString   = "USBGear USBG-V1 resp. HAMA USB",
+	},
+	[D_LINK_V100] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 4,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 0,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 0,
+		.TunerType     = 0,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "D-Link V100",
+	},
+	[X10_USB_CAMERA] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 2,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 0,
+		.TunerType     = 0,
+		.X_Offset      = -1,
+		.Y_Offset      = -1,
+		.ModelString   = "X10 USB Camera",
+	},
+	[HPG_WINTV_LIVE_PAL_BG] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 2,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 0,
+		.TunerType     = 0,
+		.X_Offset      = -1,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Live (PAL B/G)",
+	},
+	[HPG_WINTV_LIVE_PRO_NTSC_MN] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 2,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 0,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 0,
+		.TunerType     = 0,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Live Pro (NTSC M/N)",
+	},
+	[ZORAN_PMD_NOGATECH] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 2,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 2,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 0,
+		.TunerType     = 0,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Zoran Co. PMD (Nogatech) AV-grabber Manhattan",
+	},
+	[NOGATECH_USB_TV_NTSC_FM] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_NTSC_M,
+		.X_Offset      = -1,
+		.Y_Offset      = 20,
+		.ModelString   = "Nogatech USB-TV (NTSC) FM",
+	},
+	[PNY_USB_TV_NTSC_FM] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_NTSC_M,
+		.X_Offset      = -1,
+		.Y_Offset      = 20,
+		.ModelString   = "PNY USB-TV (NTSC) FM",
+	},
+	[PV_PLAYTV_USB_PRO_PAL_FM] = {
+		.Interface     = 0,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_PAL,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "PixelView PlayTv-USB PRO (PAL) FM",
+	},
+	[ZT_721] = {
+		.Interface     = 0,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_PAL,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "ZTV ZT-721 2.4GHz USB A/V Receiver",
+	},
+	[HPG_WINTV_NTSC_MN] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_NTSC_M,
+		.X_Offset      = -1,
+		.Y_Offset      = 20,
+		.ModelString   = "Hauppauge WinTV USB (NTSC M/N)",
+	},
+	[HPG_WINTV_PAL_BG] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_PAL,
+		.X_Offset      = -1,
+		.Y_Offset      = -1,
+		.ModelString   = "Hauppauge WinTV USB (PAL B/G)",
+	},
+	[HPG_WINTV_PAL_I] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_PAL,
+		.X_Offset      = -1,
+		.Y_Offset      = -1,
+		.ModelString   = "Hauppauge WinTV USB (PAL I)",
+	},
+	[HPG_WINTV_PAL_SECAM_L] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_SECAM,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_SECAM,
+		.X_Offset      = -1,
+		.Y_Offset      = -1,
+		.ModelString   = "Hauppauge WinTV USB (PAL/SECAM L)",
+	},
+	[HPG_WINTV_PAL_D_K] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_PAL,
+		.X_Offset      = -1,
+		.Y_Offset      = -1,
+		.ModelString   = "Hauppauge WinTV USB (PAL D/K)",
+	},
+	[HPG_WINTV_NTSC_FM] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_NTSC_M,
+		.X_Offset      = -1,
+		.Y_Offset      = -1,
+		.ModelString   = "Hauppauge WinTV USB (NTSC FM)",
+	},
+	[HPG_WINTV_PAL_BG_FM] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_PAL,
+		.X_Offset      = -1,
+		.Y_Offset      = -1,
+		.ModelString   = "Hauppauge WinTV USB (PAL B/G FM)",
+	},
+	[HPG_WINTV_PAL_I_FM] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_PAL,
+		.X_Offset      = -1,
+		.Y_Offset      = -1,
+		.ModelString   = "Hauppauge WinTV USB (PAL I FM)",
+	},
+	[HPG_WINTV_PAL_D_K_FM] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_PAL,
+		.X_Offset      = -1,
+		.Y_Offset      = -1,
+		.ModelString   = "Hauppauge WinTV USB (PAL D/K FM)",
+	},
+	[HPG_WINTV_PRO_NTSC_MN] = {
+		.Interface     = 0,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_MICROTUNE_4049FM5,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (NTSC M/N)",
+	},
+	[HPG_WINTV_PRO_NTSC_MN_V2] = {
+		.Interface     = 0,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_MICROTUNE_4049FM5,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (NTSC M/N) V2",
+	},
+	[HPG_WINTV_PRO_PAL] = {
+		.Interface     = 0,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_FM1216ME_MK3,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L)",
+	},
+	[HPG_WINTV_PRO_NTSC_MN_V3] = {
+		.Interface     = 0,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_NTSC_M,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (NTSC M/N) V3",
+	},
+	[HPG_WINTV_PRO_PAL_BG] = {
+		.Interface     = 0,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_PAL,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (PAL B/G)",
+	},
+	[HPG_WINTV_PRO_PAL_I] = {
+		.Interface     = 0,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_PAL,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (PAL I)",
+	},
+	[HPG_WINTV_PRO_PAL_SECAM_L] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_SECAM,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_SECAM,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (PAL/SECAM L)",
+	},
+	[HPG_WINTV_PRO_PAL_D_K] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_PAL,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (PAL D/K)",
+	},
+	[HPG_WINTV_PRO_PAL_SECAM] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_SECAM,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_SECAM,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L)",
+	},
+	[HPG_WINTV_PRO_PAL_SECAM_V2] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_SECAM,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_SECAM,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) V2",
+	},
+	[HPG_WINTV_PRO_PAL_BG_V2] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_ALPS_TSBE1_PAL,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (PAL B/G) V2",
+	},
+	[HPG_WINTV_PRO_PAL_BG_D_K] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_ALPS_TSBE1_PAL,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (PAL B/G,D/K)",
+	},
+	[HPG_WINTV_PRO_PAL_I_D_K] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_PAL,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (PAL I,D/K)",
+	},
+	[HPG_WINTV_PRO_NTSC_MN_FM] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_NTSC_M,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (NTSC M/N FM)",
+	},
+	[HPG_WINTV_PRO_PAL_BG_FM] = {
+		.Interface     = 0,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_PAL,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (PAL B/G FM)",
+	},
+	[HPG_WINTV_PRO_PAL_I_FM] = {
+		.Interface     = 0,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_PAL,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (PAL I FM)",
+	},
+	[HPG_WINTV_PRO_PAL_D_K_FM] = {
+		.Interface     = 0,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_PAL,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (PAL D/K FM)",
+	},
+	[HPG_WINTV_PRO_TEMIC_PAL_FM] = {
+		.Interface     = 0,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_MICROTUNE_4049FM5,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (Temic PAL/SECAM B/G/I/D/K/L FM)",
+	},
+	[HPG_WINTV_PRO_TEMIC_PAL_BG_FM] = {
+		.Interface     = 0,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_MICROTUNE_4049FM5,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (Temic PAL B/G FM)",
+	},
+	[HPG_WINTV_PRO_PAL_FM] = {
+		.Interface     = 0,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_FM1216ME_MK3,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L FM)",
+	},
+	[HPG_WINTV_PRO_NTSC_MN_FM_V2] = {
+		.Interface     = 0,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_NTSC_M,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Hauppauge WinTV USB Pro (NTSC M/N FM) V2",
+	},
+	[CAMTEL_TVB330] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_NTSC_M,
+		.X_Offset      = 5,
+		.Y_Offset      = 5,
+		.ModelString   = "Camtel Technology USB TV Genie Pro FM Model TVB330",
+	},
+	[DIGITAL_VIDEO_CREATOR_I] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 2,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 0,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 0,
+		.TunerType     = 0,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Digital Video Creator I",
+	},
+	[GLOBAL_VILLAGE_GV_007_NTSC] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 2,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 0,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 0,
+		.TunerType     = 0,
+		.X_Offset      = 82,
+		.Y_Offset      = 20,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Global Village GV-007 (NTSC)",
+	},
+	[DAZZLE_DVC_50_REV_1_NTSC] = {
+		.Interface     = 0,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 2,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 0,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 0,
+		.TunerType     = 0,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Dazzle Fusion Model DVC-50 Rev 1 (NTSC)",
+	},
+	[DAZZLE_DVC_80_REV_1_PAL] = {
+		.Interface     = 0,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 2,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 0,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 0,
+		.TunerType     = 0,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Dazzle Fusion Model DVC-80 Rev 1 (PAL)",
+	},
+	[DAZZLE_DVC_90_REV_1_SECAM] = {
+		.Interface     = 0,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 2,
+		.VideoNorm     = V4L2_STD_SECAM,
+		.AudioChannels = 0,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 0,
+		.TunerType     = 0,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)",
+	},
+	[ESKAPE_LABS_MYTV2GO] = {
+		.Interface     = 0,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 2,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_FM1216ME_MK3,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Eskape Labs MyTV2Go",
+	},
+	[PINNA_PCTV_USB_PAL] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 0,
+		.Tuner         = 1,
+		.TunerType     = TUNER_TEMIC_4066FY5_PAL_I,
+		.X_Offset      = -1,
+		.Y_Offset      = -1,
+		.ModelString   = "Pinnacle Studio PCTV USB (PAL)",
+	},
+	[PINNA_PCTV_USB_SECAM] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_SECAM,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_SECAM,
+		.X_Offset      = -1,
+		.Y_Offset      = -1,
+		.ModelString   = "Pinnacle Studio PCTV USB (SECAM)",
+	},
+	[PINNA_PCTV_USB_PAL_FM] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_PAL,
+		.X_Offset      = 128,
+		.Y_Offset      = 23,
+		.ModelString   = "Pinnacle Studio PCTV USB (PAL) FM",
+	},
+	[MIRO_PCTV_USB] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_PAL,
+		.X_Offset      = -1,
+		.Y_Offset      = -1,
+		.ModelString   = "Miro PCTV USB",
+	},
+	[PINNA_PCTV_USB_NTSC_FM] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_NTSC_M,
+		.X_Offset      = -1,
+		.Y_Offset      = -1,
+		.ModelString   = "Pinnacle Studio PCTV USB (NTSC) FM",
+	},
+	[PINNA_PCTV_USB_PAL_FM_V2] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_TEMIC_4009FR5_PAL,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Pinnacle Studio PCTV USB (PAL) FM V2",
+	},
+	[PINNA_PCTV_USB_NTSC_FM_V2] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_TEMIC_4039FR5_NTSC,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Pinnacle Studio PCTV USB (NTSC) FM V2",
+	},
+	[PINNA_PCTV_USB_PAL_FM_V3] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_TEMIC_4009FR5_PAL,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Pinnacle Studio PCTV USB (PAL) FM V3",
+	},
+	[PINNA_LINX_VD_IN_CAB_NTSC] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 2,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 0,
+		.TunerType     = 0,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Pinnacle Studio Linx Video input cable (NTSC)",
+	},
+	[PINNA_LINX_VD_IN_CAB_PAL] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 2,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 0,
+		.TunerType     = 0,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Pinnacle Studio Linx Video input cable (PAL)",
+	},
+	[PINNA_PCTV_BUNGEE_PAL_FM] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7113,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_PAL,
+		.AudioChannels = 1,
+		.Radio         = 1,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_TEMIC_4009FR5_PAL,
+		.X_Offset      = 0,
+		.Y_Offset      = 3,
+		.Dvi_yuv_override = 1,
+		.Dvi_yuv       = 7,
+		.ModelString   = "Pinnacle PCTV Bungee USB (PAL) FM",
+	},
+	[HPG_WINTV] = {
+		.Interface     = -1,
+		.Codec         = CODEC_SAA7111,
+		.VideoChannels = 3,
+		.VideoNorm     = V4L2_STD_NTSC,
+		.AudioChannels = 1,
+		.Radio         = 0,
+		.vbi           = 1,
+		.Tuner         = 1,
+		.TunerType     = TUNER_PHILIPS_NTSC_M,
+		.X_Offset      = -1,
+		.Y_Offset      = -1,
+		.ModelString   = "Hauppauge WinTv-USB",
+	},
 };
+const int usbvision_device_data_size=ARRAY_SIZE(usbvision_device_data);
 
 /* Supported Devices */
 
 struct usb_device_id usbvision_table [] = {
-	{ USB_DEVICE(0xFFF0, 0xFFF0) },  /* Custom Dummy USBVision Device */
-	{ USB_DEVICE(0x0A6F, 0x0400) },  /* Xanboo */
-	{ USB_DEVICE(0x050d, 0x0208) },  /* Belkin USBView II */
-	{ USB_DEVICE(0x0571, 0x0002) },  /* echoFX InterView Lite */
-	{ USB_DEVICE(0x0573, 0x0003) },  /* USBGear USBG-V1 */
-	{ USB_DEVICE(0x0573, 0x0400) },  /* D-Link V100 */
-	{ USB_DEVICE(0x0573, 0x2000) },  /* X10 USB Camera */
-	{ USB_DEVICE(0x0573, 0x2d00) },  /* Osprey 50 */
-	{ USB_DEVICE(0x0573, 0x2d01) },  /* Hauppauge USB-Live Model 600 */
-	{ USB_DEVICE(0x0573, 0x2101) },  /* Zoran Co. PMD (Nogatech) AV-grabber Manhattan */
-	{ USB_DEVICE(0x0573, 0x4100) },  /* Nogatech USB-TV FM (NTSC) */
-	{ USB_DEVICE(0x0573, 0x4110) },  /* PNY USB-TV (NTSC) FM */
-	{ USB_DEVICE(0x0573, 0x4450) },  /* PixelView PlayTv-USB PRO (PAL) FM */
-	{ USB_DEVICE(0x0573, 0x4550) },  /* ZTV ZT-721 2.4GHz USB A/V Receiver */
-	{ USB_DEVICE(0x0573, 0x4d00) },  /* Hauppauge WinTv-USB USA */
-	{ USB_DEVICE(0x0573, 0x4d01) },  /* Hauppauge WinTv-USB */
-	{ USB_DEVICE(0x0573, 0x4d02) },  /* Hauppauge WinTv-USB UK */
-	{ USB_DEVICE(0x0573, 0x4d03) },  /* Hauppauge WinTv-USB France */
-	{ USB_DEVICE(0x0573, 0x4d10) },  /* Hauppauge WinTv-USB with FM USA radio */
-	{ USB_DEVICE(0x0573, 0x4d11) },  /* Hauppauge WinTv-USB (PAL) with FM radio */
-	{ USB_DEVICE(0x0573, 0x4d12) },  /* Hauppauge WinTv-USB UK with FM Radio */
-	{ USB_DEVICE(0x0573, 0x4d2a) },  /* Hauppague WinTv USB Model 602 40201 Rev B285 */
-	{ USB_DEVICE(0x0573, 0x4d2b) },  /* Hauppague WinTv USB Model 602 40201 Rev B282 */
-	{ USB_DEVICE(0x0573, 0x4d2c) },  /* Hauppague WinTv USB Model 40209 Rev. E1A5 PAL*/
-	{ USB_DEVICE(0x0573, 0x4d20) },  /* Hauppauge WinTv-USB II (PAL) FM Model 40201 Rev B226 */
-	{ USB_DEVICE(0x0573, 0x4d21) },  /* Hauppauge WinTv-USB II (PAL) with FM radio*/
-	{ USB_DEVICE(0x0573, 0x4d22) },  /* Hauppauge WinTv-USB II (PAL) Model 566 */
-	{ USB_DEVICE(0x0573, 0x4d23) },  /* Hauppauge WinTv-USB France 4D23*/
-	{ USB_DEVICE(0x0573, 0x4d25) },  /* Hauppauge WinTv-USB Model 40209 rev B234 */
-	{ USB_DEVICE(0x0573, 0x4d26) },  /* Hauppauge WinTv-USB Model 40209 Rev B243 */
-	{ USB_DEVICE(0x0573, 0x4d27) },  /* Hauppauge WinTv-USB Model 40204 Rev B281 */
-	{ USB_DEVICE(0x0573, 0x4d28) },  /* Hauppauge WinTv-USB Model 40204 Rev B283 */
-	{ USB_DEVICE(0x0573, 0x4d29) },  /* Hauppauge WinTv-USB Model 40205 Rev B298 */
-	{ USB_DEVICE(0x0573, 0x4d30) },  /* Hauppauge WinTv-USB FM Model 40211 Rev B123 */
-	{ USB_DEVICE(0x0573, 0x4d31) },  /* Hauppauge WinTv-USB III (PAL) with FM radio Model 568 */
-	{ USB_DEVICE(0x0573, 0x4d32) },  /* Hauppauge WinTv-USB III (PAL) FM Model 573 */
-	{ USB_DEVICE(0x0573, 0x4d35) },  /* Hauppauge WinTv-USB III (SECAM) FM Model 40219 Rev B252 */
-	{ USB_DEVICE(0x0573, 0x4d37) },  /* Hauppauge WinTv-USB Model 40219 Rev E189 */
-	{ USB_DEVICE(0x0768, 0x0006) },  /* Camtel Technology USB TV Genie Pro FM Model TVB330 */
-	{ USB_DEVICE(0x07d0, 0x0001) },  /* Digital Video Creator I */
-	{ USB_DEVICE(0x07d0, 0x0002) },  /* Global Village GV-007 (NTSC) */
-	{ USB_DEVICE(0x07d0, 0x0003) },  /* Dazzle Fusion Model DVC-50 Rev 1 (NTSC) */
-	{ USB_DEVICE(0x07d0, 0x0004) },  /* Dazzle Fusion Model DVC-80 Rev 1 (PAL) */
-	{ USB_DEVICE(0x07d0, 0x0005) },  /* Dazzle Fusion Model DVC-90 Rev 1 (SECAM) */
-	{ USB_DEVICE(0x2304, 0x010d) },  /* Pinnacle Studio PCTV USB (PAL) */
-	{ USB_DEVICE(0x2304, 0x0109) },  /* Pinnacle Studio PCTV USB (SECAM) */
-	{ USB_DEVICE(0x2304, 0x0110) },  /* Pinnacle Studio PCTV USB (PAL) */
-	{ USB_DEVICE(0x2304, 0x0111) },  /* Miro PCTV USB */
-	{ USB_DEVICE(0x2304, 0x0112) },  /* Pinnacle Studio PCTV USB (NTSC) with FM radio */
-	{ USB_DEVICE(0x2304, 0x0210) },  /* Pinnacle Studio PCTV USB (PAL) with FM radio */
-	{ USB_DEVICE(0x2304, 0x0212) },  /* Pinnacle Studio PCTV USB (NTSC) with FM radio */
-	{ USB_DEVICE(0x2304, 0x0214) },  /* Pinnacle Studio PCTV USB (PAL) with FM radio */
-	{ USB_DEVICE(0x2304, 0x0300) },  /* Pinnacle Studio Linx Video input cable (NTSC) */
-	{ USB_DEVICE(0x2304, 0x0301) },  /* Pinnacle Studio Linx Video input cable (PAL) */
-	{ USB_DEVICE(0x2304, 0x0419) },  /* Pinnacle PCTV Bungee USB (PAL) FM */
-	{ USB_DEVICE(0x2400, 0x4200) },  /* Hauppauge WinTv-USB2 Model 42012 */
-
-	{ }  /* Terminating entry */
+	{ USB_DEVICE(0x0a6f, 0x0400), .driver_info=XANBOO },
+	{ USB_DEVICE(0x050d, 0x0106), .driver_info=BELKIN_VIDEOBUS_II },
+	{ USB_DEVICE(0x050d, 0x0207), .driver_info=BELKIN_VIDEOBUS },
+	{ USB_DEVICE(0x050d, 0x0208), .driver_info=BELKIN_USB_VIDEOBUS_II },
+	{ USB_DEVICE(0x0571, 0x0002), .driver_info=ECHOFX_INTERVIEW_LITE },
+	{ USB_DEVICE(0x0573, 0x0003), .driver_info=USBGEAR_USBG_V1 },
+	{ USB_DEVICE(0x0573, 0x0400), .driver_info=D_LINK_V100 },
+	{ USB_DEVICE(0x0573, 0x2000), .driver_info=X10_USB_CAMERA },
+	{ USB_DEVICE(0x0573, 0x2d00), .driver_info=HPG_WINTV_LIVE_PAL_BG },
+	{ USB_DEVICE(0x0573, 0x2d01), .driver_info=HPG_WINTV_LIVE_PRO_NTSC_MN },
+	{ USB_DEVICE(0x0573, 0x2101), .driver_info=ZORAN_PMD_NOGATECH },
+	{ USB_DEVICE(0x0573, 0x4100), .driver_info=NOGATECH_USB_TV_NTSC_FM },
+	{ USB_DEVICE(0x0573, 0x4110), .driver_info=PNY_USB_TV_NTSC_FM },
+	{ USB_DEVICE(0x0573, 0x4450), .driver_info=PV_PLAYTV_USB_PRO_PAL_FM },
+	{ USB_DEVICE(0x0573, 0x4550), .driver_info=ZT_721 },
+	{ USB_DEVICE(0x0573, 0x4d00), .driver_info=HPG_WINTV_NTSC_MN },
+	{ USB_DEVICE(0x0573, 0x4d01), .driver_info=HPG_WINTV_PAL_BG },
+	{ USB_DEVICE(0x0573, 0x4d02), .driver_info=HPG_WINTV_PAL_I },
+	{ USB_DEVICE(0x0573, 0x4d03), .driver_info=HPG_WINTV_PAL_SECAM_L },
+	{ USB_DEVICE(0x0573, 0x4d04), .driver_info=HPG_WINTV_PAL_D_K },
+	{ USB_DEVICE(0x0573, 0x4d10), .driver_info=HPG_WINTV_NTSC_FM },
+	{ USB_DEVICE(0x0573, 0x4d11), .driver_info=HPG_WINTV_PAL_BG_FM },
+	{ USB_DEVICE(0x0573, 0x4d12), .driver_info=HPG_WINTV_PAL_I_FM },
+	{ USB_DEVICE(0x0573, 0x4d14), .driver_info=HPG_WINTV_PAL_D_K_FM },
+	{ USB_DEVICE(0x0573, 0x4d2a), .driver_info=HPG_WINTV_PRO_NTSC_MN },
+	{ USB_DEVICE(0x0573, 0x4d2b), .driver_info=HPG_WINTV_PRO_NTSC_MN_V2 },
+	{ USB_DEVICE(0x0573, 0x4d2c), .driver_info=HPG_WINTV_PRO_PAL },
+	{ USB_DEVICE(0x0573, 0x4d20), .driver_info=HPG_WINTV_PRO_NTSC_MN_V3 },
+	{ USB_DEVICE(0x0573, 0x4d21), .driver_info=HPG_WINTV_PRO_PAL_BG },
+	{ USB_DEVICE(0x0573, 0x4d22), .driver_info=HPG_WINTV_PRO_PAL_I },
+	{ USB_DEVICE(0x0573, 0x4d23), .driver_info=HPG_WINTV_PRO_PAL_SECAM_L },
+	{ USB_DEVICE(0x0573, 0x4d24), .driver_info=HPG_WINTV_PRO_PAL_D_K },
+	{ USB_DEVICE(0x0573, 0x4d25), .driver_info=HPG_WINTV_PRO_PAL_SECAM },
+	{ USB_DEVICE(0x0573, 0x4d26), .driver_info=HPG_WINTV_PRO_PAL_SECAM_V2 },
+	{ USB_DEVICE(0x0573, 0x4d27), .driver_info=HPG_WINTV_PRO_PAL_BG_V2 },
+	{ USB_DEVICE(0x0573, 0x4d28), .driver_info=HPG_WINTV_PRO_PAL_BG_D_K },
+	{ USB_DEVICE(0x0573, 0x4d29), .driver_info=HPG_WINTV_PRO_PAL_I_D_K },
+	{ USB_DEVICE(0x0573, 0x4d30), .driver_info=HPG_WINTV_PRO_NTSC_MN_FM },
+	{ USB_DEVICE(0x0573, 0x4d31), .driver_info=HPG_WINTV_PRO_PAL_BG_FM },
+	{ USB_DEVICE(0x0573, 0x4d32), .driver_info=HPG_WINTV_PRO_PAL_I_FM },
+	{ USB_DEVICE(0x0573, 0x4d34), .driver_info=HPG_WINTV_PRO_PAL_D_K_FM },
+	{ USB_DEVICE(0x0573, 0x4d35), .driver_info=HPG_WINTV_PRO_TEMIC_PAL_FM },
+	{ USB_DEVICE(0x0573, 0x4d36), .driver_info=HPG_WINTV_PRO_TEMIC_PAL_BG_FM },
+	{ USB_DEVICE(0x0573, 0x4d37), .driver_info=HPG_WINTV_PRO_PAL_FM },
+	{ USB_DEVICE(0x0573, 0x4d38), .driver_info=HPG_WINTV_PRO_NTSC_MN_FM_V2 },
+	{ USB_DEVICE(0x0768, 0x0006), .driver_info=CAMTEL_TVB330 },
+	{ USB_DEVICE(0x07d0, 0x0001), .driver_info=DIGITAL_VIDEO_CREATOR_I },
+	{ USB_DEVICE(0x07d0, 0x0002), .driver_info=GLOBAL_VILLAGE_GV_007_NTSC },
+	{ USB_DEVICE(0x07d0, 0x0003), .driver_info=DAZZLE_DVC_50_REV_1_NTSC },
+	{ USB_DEVICE(0x07d0, 0x0004), .driver_info=DAZZLE_DVC_80_REV_1_PAL },
+	{ USB_DEVICE(0x07d0, 0x0005), .driver_info=DAZZLE_DVC_90_REV_1_SECAM },
+	{ USB_DEVICE(0x07f8, 0x9104), .driver_info=ESKAPE_LABS_MYTV2GO },
+	{ USB_DEVICE(0x2304, 0x010d), .driver_info=PINNA_PCTV_USB_PAL },
+	{ USB_DEVICE(0x2304, 0x0109), .driver_info=PINNA_PCTV_USB_SECAM },
+	{ USB_DEVICE(0x2304, 0x0110), .driver_info=PINNA_PCTV_USB_PAL_FM },
+	{ USB_DEVICE(0x2304, 0x0111), .driver_info=MIRO_PCTV_USB },
+	{ USB_DEVICE(0x2304, 0x0112), .driver_info=PINNA_PCTV_USB_NTSC_FM },
+	{ USB_DEVICE(0x2304, 0x0210), .driver_info=PINNA_PCTV_USB_PAL_FM_V2 },
+	{ USB_DEVICE(0x2304, 0x0212), .driver_info=PINNA_PCTV_USB_NTSC_FM_V2 },
+	{ USB_DEVICE(0x2304, 0x0214), .driver_info=PINNA_PCTV_USB_PAL_FM_V3 },
+	{ USB_DEVICE(0x2304, 0x0300), .driver_info=PINNA_LINX_VD_IN_CAB_NTSC },
+	{ USB_DEVICE(0x2304, 0x0301), .driver_info=PINNA_LINX_VD_IN_CAB_PAL },
+	{ USB_DEVICE(0x2304, 0x0419), .driver_info=PINNA_PCTV_BUNGEE_PAL_FM },
+	{ USB_DEVICE(0x2400, 0x4200), .driver_info=HPG_WINTV },
 };
 
 MODULE_DEVICE_TABLE (usb, usbvision_table);
diff --git a/drivers/media/video/usbvision/usbvision-cards.h b/drivers/media/video/usbvision/usbvision-cards.h
new file mode 100644
index 0000000..512c5ce
--- /dev/null
+++ b/drivers/media/video/usbvision/usbvision-cards.h
@@ -0,0 +1,66 @@
+#define XANBOO                                   0
+#define BELKIN_VIDEOBUS_II                       1
+#define BELKIN_VIDEOBUS                          2
+#define BELKIN_USB_VIDEOBUS_II                   3
+#define ECHOFX_INTERVIEW_LITE                    4
+#define USBGEAR_USBG_V1                          5
+#define D_LINK_V100                              6
+#define X10_USB_CAMERA                           7
+#define HPG_WINTV_LIVE_PAL_BG                    8
+#define HPG_WINTV_LIVE_PRO_NTSC_MN               9
+#define ZORAN_PMD_NOGATECH                       10
+#define NOGATECH_USB_TV_NTSC_FM                  11
+#define PNY_USB_TV_NTSC_FM                       12
+#define PV_PLAYTV_USB_PRO_PAL_FM                 13
+#define ZT_721                                   14
+#define HPG_WINTV_NTSC_MN                        15
+#define HPG_WINTV_PAL_BG                         16
+#define HPG_WINTV_PAL_I                          17
+#define HPG_WINTV_PAL_SECAM_L                    18
+#define HPG_WINTV_PAL_D_K                        19
+#define HPG_WINTV_NTSC_FM                        20
+#define HPG_WINTV_PAL_BG_FM                      21
+#define HPG_WINTV_PAL_I_FM                       22
+#define HPG_WINTV_PAL_D_K_FM                     23
+#define HPG_WINTV_PRO_NTSC_MN                    24
+#define HPG_WINTV_PRO_NTSC_MN_V2                 25
+#define HPG_WINTV_PRO_PAL                        26
+#define HPG_WINTV_PRO_NTSC_MN_V3                 27
+#define HPG_WINTV_PRO_PAL_BG                     28
+#define HPG_WINTV_PRO_PAL_I                      29
+#define HPG_WINTV_PRO_PAL_SECAM_L                30
+#define HPG_WINTV_PRO_PAL_D_K                    31
+#define HPG_WINTV_PRO_PAL_SECAM                  32
+#define HPG_WINTV_PRO_PAL_SECAM_V2               33
+#define HPG_WINTV_PRO_PAL_BG_V2                  34
+#define HPG_WINTV_PRO_PAL_BG_D_K                 35
+#define HPG_WINTV_PRO_PAL_I_D_K                  36
+#define HPG_WINTV_PRO_NTSC_MN_FM                 37
+#define HPG_WINTV_PRO_PAL_BG_FM                  38
+#define HPG_WINTV_PRO_PAL_I_FM                   39
+#define HPG_WINTV_PRO_PAL_D_K_FM                 40
+#define HPG_WINTV_PRO_TEMIC_PAL_FM               41
+#define HPG_WINTV_PRO_TEMIC_PAL_BG_FM            42
+#define HPG_WINTV_PRO_PAL_FM                     43
+#define HPG_WINTV_PRO_NTSC_MN_FM_V2              44
+#define CAMTEL_TVB330                            45
+#define DIGITAL_VIDEO_CREATOR_I                  46
+#define GLOBAL_VILLAGE_GV_007_NTSC               47
+#define DAZZLE_DVC_50_REV_1_NTSC                 48
+#define DAZZLE_DVC_80_REV_1_PAL                  49
+#define DAZZLE_DVC_90_REV_1_SECAM                50
+#define ESKAPE_LABS_MYTV2GO                      51
+#define PINNA_PCTV_USB_PAL                       52
+#define PINNA_PCTV_USB_SECAM                     53
+#define PINNA_PCTV_USB_PAL_FM                    54
+#define MIRO_PCTV_USB                            55
+#define PINNA_PCTV_USB_NTSC_FM                   56
+#define PINNA_PCTV_USB_PAL_FM_V2                 57
+#define PINNA_PCTV_USB_NTSC_FM_V2                58
+#define PINNA_PCTV_USB_PAL_FM_V3                 59
+#define PINNA_LINX_VD_IN_CAB_NTSC                60
+#define PINNA_LINX_VD_IN_CAB_PAL                 61
+#define PINNA_PCTV_BUNGEE_PAL_FM                 62
+#define HPG_WINTV                                63
+
+extern const int usbvision_device_data_size;
diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c
index f2154dc..bcb551a 100644
--- a/drivers/media/video/usbvision/usbvision-core.c
+++ b/drivers/media/video/usbvision/usbvision-core.c
@@ -2040,8 +2040,8 @@
 		return 0;
 
 	/* Set input format expected from decoder*/
-	if (usbvision_device_data[usbvision->DevModel].Vin_Reg1 >= 0) {
-		value[0] = usbvision_device_data[usbvision->DevModel].Vin_Reg1 & 0xff;
+	if (usbvision_device_data[usbvision->DevModel].Vin_Reg1_override) {
+		value[0] = usbvision_device_data[usbvision->DevModel].Vin_Reg1;
 	} else if(usbvision_device_data[usbvision->DevModel].Codec == CODEC_SAA7113) {
 		/* SAA7113 uses 8 bit output */
 		value[0] = USBVISION_8_422_SYNC;
@@ -2112,8 +2112,8 @@
 
 	dvi_yuv_value = 0x00;	/* U comes after V, Ya comes after U/V, Yb comes after Yb */
 
-	if(usbvision_device_data[usbvision->DevModel].Dvi_yuv >= 0){
-		dvi_yuv_value = usbvision_device_data[usbvision->DevModel].Dvi_yuv & 0xff;
+	if(usbvision_device_data[usbvision->DevModel].Dvi_yuv_override){
+		dvi_yuv_value = usbvision_device_data[usbvision->DevModel].Dvi_yuv;
 	}
 	else if(usbvision_device_data[usbvision->DevModel].Codec == CODEC_SAA7113) {
 	/* This changes as the fine sync control changes. Further investigation necessary */
@@ -2238,7 +2238,7 @@
 	PDEBUG(DBG_FUNC, "");
 	down_interruptible(&usbvision->lock);
 	if(usbvision->user == 0) {
-		usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
+		usbvision_i2c_unregister(usbvision);
 
 		usbvision_power_off(usbvision);
 		usbvision->initialized = 0;
diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c
index 609e1fd..025be55 100644
--- a/drivers/media/video/usbvision/usbvision-i2c.c
+++ b/drivers/media/video/usbvision/usbvision-i2c.c
@@ -1,8 +1,8 @@
 /*
- * I2C_ALGO_USB.C
+ * usbvision_i2c.c
  *  i2c algorithm for USB-I2C Bridges
  *
- * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
+ * Copyright (c) 1999-2007 Joerg Heckenbach <joerg@heckenbach-aw.de>
  *                         Dwaine Garden <dwainegarden@rogers.com>
  *
  * This module is part of usbvision driver project.
@@ -39,7 +39,6 @@
 #include "usbvision.h"
 
 #define DBG_I2C		1<<0
-#define DBG_ALGO	1<<1
 
 static int i2c_debug = 0;
 
@@ -49,22 +48,22 @@
 #define PDEBUG(level, fmt, args...) \
 		if (i2c_debug & (level)) info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ , ## args)
 
-static int usbvision_i2c_write(void *data, unsigned char addr, char *buf,
+static int usbvision_i2c_write(struct usb_usbvision *usbvision, unsigned char addr, char *buf,
 			    short len);
-static int usbvision_i2c_read(void *data, unsigned char addr, char *buf,
+static int usbvision_i2c_read(struct usb_usbvision *usbvision, unsigned char addr, char *buf,
 			   short len);
 
 static inline int try_write_address(struct i2c_adapter *i2c_adap,
 				    unsigned char addr, int retries)
 {
-	void *data;
+	struct usb_usbvision *usbvision;
 	int i, ret = -1;
 	char buf[4];
 
-	data = i2c_get_adapdata(i2c_adap);
+	usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap);
 	buf[0] = 0x00;
 	for (i = 0; i <= retries; i++) {
-		ret = (usbvision_i2c_write(data, addr, buf, 1));
+		ret = (usbvision_i2c_write(usbvision, addr, buf, 1));
 		if (ret == 1)
 			break;	/* success! */
 		udelay(5);
@@ -73,8 +72,8 @@
 		udelay(10);
 	}
 	if (i) {
-		PDEBUG(DBG_ALGO,"Needed %d retries for address %#2x", i, addr);
-		PDEBUG(DBG_ALGO,"Maybe there's no device at this address");
+		PDEBUG(DBG_I2C,"Needed %d retries for address %#2x", i, addr);
+		PDEBUG(DBG_I2C,"Maybe there's no device at this address");
 	}
 	return ret;
 }
@@ -82,13 +81,13 @@
 static inline int try_read_address(struct i2c_adapter *i2c_adap,
 				   unsigned char addr, int retries)
 {
-	void *data;
+	struct usb_usbvision *usbvision;
 	int i, ret = -1;
 	char buf[4];
 
-	data = i2c_get_adapdata(i2c_adap);
+	usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap);
 	for (i = 0; i <= retries; i++) {
-		ret = (usbvision_i2c_read(data, addr, buf, 1));
+		ret = (usbvision_i2c_read(usbvision, addr, buf, 1));
 		if (ret == 1)
 			break;	/* success! */
 		udelay(5);
@@ -97,8 +96,8 @@
 		udelay(10);
 	}
 	if (i) {
-		PDEBUG(DBG_ALGO,"Needed %d retries for address %#2x", i, addr);
-		PDEBUG(DBG_ALGO,"Maybe there's no device at this address");
+		PDEBUG(DBG_I2C,"Needed %d retries for address %#2x", i, addr);
+		PDEBUG(DBG_I2C,"Maybe there's no device at this address");
 	}
 	return ret;
 }
@@ -152,32 +151,32 @@
 }
 
 static int
-usb_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
+usbvision_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
 {
 	struct i2c_msg *pmsg;
-	void *data;
+	struct usb_usbvision *usbvision;
 	int i, ret;
 	unsigned char addr;
 
-	data = i2c_get_adapdata(i2c_adap);
+	usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap);
 
 	for (i = 0; i < num; i++) {
 		pmsg = &msgs[i];
 		ret = usb_find_address(i2c_adap, pmsg, i2c_adap->retries, &addr);
 		if (ret != 0) {
-			PDEBUG(DBG_ALGO,"got NAK from device, message #%d", i);
+			PDEBUG(DBG_I2C,"got NAK from device, message #%d", i);
 			return (ret < 0) ? ret : -EREMOTEIO;
 		}
 
 		if (pmsg->flags & I2C_M_RD) {
 			/* read bytes into buffer */
-			ret = (usbvision_i2c_read(data, addr, pmsg->buf, pmsg->len));
+			ret = (usbvision_i2c_read(usbvision, addr, pmsg->buf, pmsg->len));
 			if (ret < pmsg->len) {
 				return (ret < 0) ? ret : -EREMOTEIO;
 			}
 		} else {
 			/* write bytes from buffer */
-			ret = (usbvision_i2c_write(data, addr, pmsg->buf, pmsg->len));
+			ret = (usbvision_i2c_write(usbvision, addr, pmsg->buf, pmsg->len));
 			if (ret < pmsg->len) {
 				return (ret < 0) ? ret : -EREMOTEIO;
 			}
@@ -191,7 +190,7 @@
 	return 0;
 }
 
-static u32 usb_func(struct i2c_adapter *adap)
+static u32 functionality(struct i2c_adapter *adap)
 {
 	return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
 }
@@ -199,11 +198,11 @@
 
 /* -----exported algorithm data: -------------------------------------	*/
 
-static struct i2c_algorithm i2c_usb_algo = {
-	.master_xfer   = usb_xfer,
+static struct i2c_algorithm usbvision_algo = {
+	.master_xfer   = usbvision_i2c_xfer,
 	.smbus_xfer    = NULL,
 	.algo_control  = algo_control,
-	.functionality = usb_func,
+	.functionality = functionality,
 };
 
 
@@ -213,41 +212,29 @@
 static int usbvision_i2c_usb_add_bus(struct i2c_adapter *adap)
 {
 	PDEBUG(DBG_I2C, "I2C   debugging is enabled [i2c]");
-	PDEBUG(DBG_ALGO, "ALGO   debugging is enabled [i2c]");
+	PDEBUG(DBG_I2C, "ALGO   debugging is enabled [i2c]");
 
 	/* register new adapter to i2c module... */
 
-	adap->algo = &i2c_usb_algo;
+	adap->algo = &usbvision_algo;
 
 	adap->timeout = 100;	/* default values, should       */
 	adap->retries = 3;	/* be replaced by defines       */
 
 	i2c_add_adapter(adap);
 
-	PDEBUG(DBG_ALGO,"i2c bus for %s registered", adap->name);
+	PDEBUG(DBG_I2C,"i2c bus for %s registered", adap->name);
 
 	return 0;
 }
 
-
-int usbvision_i2c_usb_del_bus(struct i2c_adapter *adap)
-{
-
-	i2c_del_adapter(adap);
-
-	PDEBUG(DBG_ALGO,"i2c bus for %s unregistered", adap->name);
-
-	return 0;
-}
-
-
 /* ----------------------------------------------------------------------- */
 /* usbvision specific I2C functions                                        */
 /* ----------------------------------------------------------------------- */
 static struct i2c_adapter i2c_adap_template;
 static struct i2c_client i2c_client_template;
 
-int usbvision_init_i2c(struct usb_usbvision *usbvision)
+int usbvision_i2c_register(struct usb_usbvision *usbvision)
 {
 	memcpy(&usbvision->i2c_adap, &i2c_adap_template,
 	       sizeof(struct i2c_adapter));
@@ -265,7 +252,7 @@
 	usbvision->i2c_client.adapter = &usbvision->i2c_adap;
 
 	if (usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_IIC_LRNACK) < 0) {
-		printk(KERN_ERR "usbvision_init_i2c: can't write reg\n");
+		printk(KERN_ERR "usbvision_register: can't write reg\n");
 		return -EBUSY;
 	}
 
@@ -287,6 +274,16 @@
 	return usbvision_i2c_usb_add_bus(&usbvision->i2c_adap);
 }
 
+int usbvision_i2c_unregister(struct usb_usbvision *usbvision)
+{
+
+	i2c_del_adapter(&(usbvision->i2c_adap));
+
+	PDEBUG(DBG_I2C,"i2c bus for %s unregistered", usbvision->i2c_adap.name);
+
+	return 0;
+}
+
 void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd,
 		      void *arg)
 {
@@ -300,19 +297,12 @@
 	usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter);
 
 	switch (client->addr << 1) {
-		case 0x43:
-		case 0x4b:
-		{
-			struct tuner_setup tun_setup;
-
-			tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
-			tun_setup.type = TUNER_TDA9887;
-			tun_setup.addr = client->addr;
-
-			call_i2c_clients(usbvision, TUNER_SET_TYPE_ADDR, &tun_setup);
-
+		case 0x42 << 1:
+		case 0x43 << 1:
+		case 0x4a << 1:
+		case 0x4b << 1:
+			PDEBUG(DBG_I2C,"attach_inform: tda9887 detected.");
 			break;
-		}
 		case 0x42:
 			PDEBUG(DBG_I2C,"attach_inform: saa7114 detected.");
 			break;
@@ -480,7 +470,7 @@
 	return len;
 }
 
-static int usbvision_i2c_write(void *data, unsigned char addr, char *buf,
+static int usbvision_i2c_write(struct usb_usbvision *usbvision, unsigned char addr, char *buf,
 			    short len)
 {
 	char *bufPtr = buf;
@@ -488,7 +478,6 @@
 	int wrcount = 0;
 	int count;
 	int maxLen = 4;
-	struct usb_usbvision *usbvision = (struct usb_usbvision *) data;
 
 	while (len > 0) {
 		count = (len > maxLen) ? maxLen : len;
@@ -503,14 +492,13 @@
 	return wrcount;
 }
 
-static int usbvision_i2c_read(void *data, unsigned char addr, char *buf,
+static int usbvision_i2c_read(struct usb_usbvision *usbvision, unsigned char addr, char *buf,
 			   short len)
 {
 	char temp[4];
 	int retval, i;
 	int rdcount = 0;
 	int count;
-	struct usb_usbvision *usbvision = (struct usb_usbvision *) data;
 
 	while (len > 0) {
 		count = (len > 3) ? 4 : len;
diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c
index 6fc1455..2167041 100644
--- a/drivers/media/video/usbvision/usbvision-video.c
+++ b/drivers/media/video/usbvision/usbvision-video.c
@@ -76,6 +76,7 @@
 #endif
 
 #include "usbvision.h"
+#include "usbvision-cards.h"
 
 #define DRIVER_AUTHOR "Joerg Heckenbach <joerg@heckenbach-aw.de>, Dwaine Garden <DwaineGarden@rogers.com>"
 #define DRIVER_NAME "usbvision"
@@ -150,7 +151,6 @@
 static int video_nr = -1;				// Sequential Number of Video Device
 static int radio_nr = -1;				// Sequential Number of Radio Device
 static int vbi_nr = -1;					// Sequential Number of VBI Device
-static char *CustomDevice=NULL;				// Set as nothing....
 
 // Grab parameters for the device driver
 
@@ -161,7 +161,6 @@
 module_param(video_nr, int, 0444);
 module_param(radio_nr, int, 0444);
 module_param(vbi_nr, int, 0444);
-module_param(CustomDevice, charp, 0444);
 #else							// Old Style
 MODULE_PARAM(isocMode, "i");
 MODULE_PARM(video_debug, "i");				// Grab the Debug Mode of the device driver
@@ -171,7 +170,6 @@
 MODULE_PARM(video_nr, "i");				// video_nr option allows to specify a certain /dev/videoX device (like /dev/video0 or /dev/video1 ...)
 MODULE_PARM(radio_nr, "i");				// radio_nr option allows to specify a certain /dev/radioX device (like /dev/radio0 or /dev/radio1 ...)
 MODULE_PARM(vbi_nr, "i");				// vbi_nr option allows to specify a certain /dev/vbiX device (like /dev/vbi0 or /dev/vbi1 ...)
-MODULE_PARM(CustomDevice, "s");				// .... CustomDevice
 #endif
 
 MODULE_PARM_DESC(isocMode, " Set the default format for ISOC endpoint.  Default: 0x60 (Compression On)");
@@ -180,7 +178,6 @@
 MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX).  Default: -1 (autodetect)");
 MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX).  Default: -1 (autodetect)");
 MODULE_PARM_DESC(vbi_nr, "Set vbi device number (/dev/vbiX).  Default: -1 (autodetect)");
-MODULE_PARM_DESC(CustomDevice, " Define the fine tuning parameters for the device.  Default: null");
 
 
 // Misc stuff
@@ -409,7 +406,7 @@
 		down(&usbvision->lock);
 		if (usbvision->power == 0) {
 			usbvision_power_on(usbvision);
-			usbvision_init_i2c(usbvision);
+			usbvision_i2c_register(usbvision);
 		}
 
 		/* Send init sequence only once, it's large! */
@@ -431,7 +428,7 @@
 		}
 		else {
 			if (PowerOnAtOpen) {
-				usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
+				usbvision_i2c_unregister(usbvision);
 				usbvision_power_off(usbvision);
 				usbvision->initialized = 0;
 			}
@@ -1239,7 +1236,7 @@
 			usbvision_reset_powerOffTimer(usbvision);
 			if (usbvision->power == 0) {
 				usbvision_power_on(usbvision);
-				usbvision_init_i2c(usbvision);
+				usbvision_i2c_register(usbvision);
 			}
 		}
 
@@ -1261,7 +1258,7 @@
 
 	if (errCode) {
 		if (PowerOnAtOpen) {
-			usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
+			usbvision_i2c_unregister(usbvision);
 			usbvision_power_off(usbvision);
 			usbvision->initialized = 0;
 		}
@@ -1744,8 +1741,8 @@
 	model = usbvision->DevModel;
 	usbvision->palette = usbvision_v4l2_format[2]; // V4L2_PIX_FMT_RGB24;
 
-	if (usbvision_device_data[usbvision->DevModel].Vin_Reg2 >= 0) {
-		usbvision->Vin_Reg2_Preset = usbvision_device_data[usbvision->DevModel].Vin_Reg2 & 0xff;
+	if (usbvision_device_data[usbvision->DevModel].Vin_Reg2_override) {
+		usbvision->Vin_Reg2_Preset = usbvision_device_data[usbvision->DevModel].Vin_Reg2;
 	} else {
 		usbvision->Vin_Reg2_Preset = 0;
 	}
@@ -1764,7 +1761,7 @@
 	usbvision_audio_off(usbvision);	//first switch off audio
 	if (!PowerOnAtOpen) {
 		usbvision_power_on(usbvision);	//and then power up the noisy tuner
-		usbvision_init_i2c(usbvision);
+		usbvision_i2c_register(usbvision);
 	}
 }
 
@@ -1775,7 +1772,8 @@
  * if it looks like USBVISION video device
  *
  */
-static int __devinit usbvision_probe(struct usb_interface *intf, const struct usb_device_id *devid)
+static int __devinit usbvision_probe(struct usb_interface *intf,
+				     const struct usb_device_id *devid)
 {
 	struct usb_device *dev = usb_get_dev(interface_to_usbdev(intf));
 	struct usb_interface *uif;
@@ -1786,25 +1784,17 @@
 	int model,i;
 
 	PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u",
-					dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum);
+				dev->descriptor.idVendor,
+				dev->descriptor.idProduct, ifnum);
 
-	/* Is it an USBVISION video dev? */
-	model = 0;
-	for(model = 0; usbvision_device_data[model].idVendor; model++) {
-		if (le16_to_cpu(dev->descriptor.idVendor) != usbvision_device_data[model].idVendor) {
-			continue;
-		}
-		if (le16_to_cpu(dev->descriptor.idProduct) != usbvision_device_data[model].idProduct) {
-			continue;
-		}
-
-		printk(KERN_INFO "%s: %s found\n", __FUNCTION__, usbvision_device_data[model].ModelString);
-		break;
+	model = devid->driver_info;
+	if ( (model<0) || (model>=usbvision_device_data_size) ) {
+		PDEBUG(DBG_PROBE, "model out of bounds %d",model);
+		return -ENODEV;
 	}
+	printk(KERN_INFO "%s: %s found\n", __FUNCTION__,
+				usbvision_device_data[model].ModelString);
 
-	if (usbvision_device_data[model].idVendor == 0) {
-		return -ENODEV; //no matching device
-	}
 	if (usbvision_device_data[model].Interface >= 0) {
 		interface = &dev->actconfig->interface[usbvision_device_data[model].Interface]->altsetting[0];
 	}
@@ -1822,16 +1812,15 @@
 		return -ENODEV;
 	}
 
-	usb_get_dev(dev);
-
 	if ((usbvision = usbvision_alloc(dev)) == NULL) {
 		err("%s: couldn't allocate USBVision struct", __FUNCTION__);
 		return -ENOMEM;
 	}
+
 	if (dev->descriptor.bNumConfigurations > 1) {
 		usbvision->bridgeType = BRIDGE_NT1004;
 	}
-	else if (usbvision_device_data[model].ModelString == "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)") {
+	else if (model == DAZZLE_DVC_90_REV_1_SECAM) {
 		usbvision->bridgeType = BRIDGE_NT1005;
 	}
 	else {
@@ -1920,7 +1909,7 @@
 	usbvision_stop_isoc(usbvision);
 
 	if (usbvision->power) {
-		usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
+		usbvision_i2c_unregister(usbvision);
 		usbvision_power_off(usbvision);
 	}
 	usbvision->remove_pending = 1;	// Now all ISO data will be ignored
@@ -1951,124 +1940,6 @@
 };
 
 /*
- * customdevice_process()
- *
- * This procedure preprocesses CustomDevice parameter if any
- *
- */
-static void customdevice_process(void)
-{
-	usbvision_device_data[0]=usbvision_device_data[1];
-	usbvision_table[0]=usbvision_table[1];
-
-	if(CustomDevice)
-	{
-		char *parse=CustomDevice;
-
-		PDEBUG(DBG_PROBE, "CustomDevide=%s", CustomDevice);
-
-		/*format is CustomDevice="0x0573 0x4D31 0 7113 3 PAL 1 1 1 5 -1 -1 -1 -1 -1"
-		usbvision_device_data[0].idVendor;
-		usbvision_device_data[0].idProduct;
-		usbvision_device_data[0].Interface;
-		usbvision_device_data[0].Codec;
-		usbvision_device_data[0].VideoChannels;
-		usbvision_device_data[0].VideoNorm;
-		usbvision_device_data[0].AudioChannels;
-		usbvision_device_data[0].Radio;
-		usbvision_device_data[0].Tuner;
-		usbvision_device_data[0].TunerType;
-		usbvision_device_data[0].Vin_Reg1;
-		usbvision_device_data[0].Vin_Reg2;
-		usbvision_device_data[0].X_Offset;
-		usbvision_device_data[0].Y_Offset;
-		usbvision_device_data[0].Dvi_yuv;
-		usbvision_device_data[0].ModelString;
-		*/
-
-		rmspace(parse);
-		usbvision_device_data[0].ModelString="USBVISION Custom Device";
-
-		parse+=2;
-		sscanf(parse,"%x",&usbvision_device_data[0].idVendor);
-		goto2next(parse);
-		PDEBUG(DBG_PROBE, "idVendor=0x%.4X", usbvision_device_data[0].idVendor);
-		parse+=2;
-		sscanf(parse,"%x",&usbvision_device_data[0].idProduct);
-		goto2next(parse);
-		PDEBUG(DBG_PROBE, "idProduct=0x%.4X", usbvision_device_data[0].idProduct);
-		sscanf(parse,"%d",&usbvision_device_data[0].Interface);
-		goto2next(parse);
-		PDEBUG(DBG_PROBE, "Interface=%d", usbvision_device_data[0].Interface);
-		sscanf(parse,"%d",&usbvision_device_data[0].Codec);
-		goto2next(parse);
-		PDEBUG(DBG_PROBE, "Codec=%d", usbvision_device_data[0].Codec);
-		sscanf(parse,"%d",&usbvision_device_data[0].VideoChannels);
-		goto2next(parse);
-		PDEBUG(DBG_PROBE, "VideoChannels=%d", usbvision_device_data[0].VideoChannels);
-
-		switch(*parse)
-		{
-			case 'P':
-				PDEBUG(DBG_PROBE, "VideoNorm=PAL");
-				usbvision_device_data[0].VideoNorm=V4L2_STD_PAL;
-				break;
-
-			case 'S':
-				PDEBUG(DBG_PROBE, "VideoNorm=SECAM");
-				usbvision_device_data[0].VideoNorm=V4L2_STD_SECAM;
-				break;
-
-			case 'N':
-				PDEBUG(DBG_PROBE, "VideoNorm=NTSC");
-				usbvision_device_data[0].VideoNorm=V4L2_STD_NTSC;
-				break;
-
-			default:
-				PDEBUG(DBG_PROBE, "VideoNorm=PAL (by default)");
-				usbvision_device_data[0].VideoNorm=V4L2_STD_PAL;
-				break;
-		}
-		goto2next(parse);
-
-		sscanf(parse,"%d",&usbvision_device_data[0].AudioChannels);
-		goto2next(parse);
-		PDEBUG(DBG_PROBE, "AudioChannels=%d", usbvision_device_data[0].AudioChannels);
-		sscanf(parse,"%d",&usbvision_device_data[0].Radio);
-		goto2next(parse);
-		PDEBUG(DBG_PROBE, "Radio=%d", usbvision_device_data[0].Radio);
-		sscanf(parse,"%d",&usbvision_device_data[0].Tuner);
-		goto2next(parse);
-		PDEBUG(DBG_PROBE, "Tuner=%d", usbvision_device_data[0].Tuner);
-		sscanf(parse,"%d",&usbvision_device_data[0].TunerType);
-		goto2next(parse);
-		PDEBUG(DBG_PROBE, "TunerType=%d", usbvision_device_data[0].TunerType);
-		sscanf(parse,"%d",&usbvision_device_data[0].Vin_Reg1);
-		goto2next(parse);
-		PDEBUG(DBG_PROBE, "Vin_Reg1=%d", usbvision_device_data[0].Vin_Reg1);
-		sscanf(parse,"%d",&usbvision_device_data[0].Vin_Reg2);
-		goto2next(parse);
-		PDEBUG(DBG_PROBE, "Vin_Reg2=%d", usbvision_device_data[0].Vin_Reg2);
-		sscanf(parse,"%d",&usbvision_device_data[0].X_Offset);
-		goto2next(parse);
-		PDEBUG(DBG_PROBE, "X_Offset=%d", usbvision_device_data[0].X_Offset);
-		sscanf(parse,"%d",&usbvision_device_data[0].Y_Offset);
-		goto2next(parse);
-		PDEBUG(DBG_PROBE, "Y_Offset=%d", usbvision_device_data[0].Y_Offset);
-		sscanf(parse,"%d",&usbvision_device_data[0].Dvi_yuv);
-		PDEBUG(DBG_PROBE, "Dvi_yuv=%d", usbvision_device_data[0].Dvi_yuv);
-
-		//add to usbvision_table also
-		usbvision_table[0].match_flags=USB_DEVICE_ID_MATCH_DEVICE;
-		usbvision_table[0].idVendor=usbvision_device_data[0].idVendor;
-		usbvision_table[0].idProduct=usbvision_device_data[0].idProduct;
-
-	}
-}
-
-
-
-/*
  * usbvision_init()
  *
  * This code is run to initialize the driver.
@@ -2092,8 +1963,6 @@
 		usbvision_v4l2_format[7].supported = 0; // V4L2_PIX_FMT_YUV422P
 	}
 
-	customdevice_process();
-
 	errCode = usb_register(&usbvision_driver);
 
 	if (errCode == 0) {
diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h
index ad6afd3..bd6f642 100644
--- a/drivers/media/video/usbvision/usbvision.h
+++ b/drivers/media/video/usbvision/usbvision.h
@@ -342,23 +342,24 @@
 #define BRIDGE_NT1005   1005
 
 struct usbvision_device_data_st {
-	int idVendor;
-	int idProduct;
-	int Interface; /* to handle special interface number like BELKIN and Hauppauge WinTV-USB II */
-	int Codec;
-	int VideoChannels;
 	__u64 VideoNorm;
-	int AudioChannels;
-	int Radio;
-	int vbi;
-	int Tuner;
-	int TunerType;
-	int Vin_Reg1;
-	int Vin_Reg2;
-	int X_Offset;
-	int Y_Offset;
-	int Dvi_yuv;
-	char *ModelString;
+	const char *ModelString;
+	int Interface; /* to handle special interface number like BELKIN and Hauppauge WinTV-USB II */
+	__u16 Codec;
+	unsigned VideoChannels:3;
+	unsigned AudioChannels:2;
+	unsigned Radio:1;
+	unsigned vbi:1;
+	unsigned Tuner:1;
+	unsigned Vin_Reg1_override:1;	/* Override default value with */
+	unsigned Vin_Reg2_override:1;   /* Vin_Reg1, Vin_Reg2, etc. */
+	unsigned Dvi_yuv_override:1;
+	__u8 Vin_Reg1;
+	__u8 Vin_Reg2;
+	__u8 Dvi_yuv;
+	__u8 TunerType;
+	__s16 X_Offset;
+	__s16 Y_Offset;
 };
 
 /* Declared on usbvision-cards.c */
@@ -481,13 +482,11 @@
 /* i2c-algo-usb declaration                                        */
 /* --------------------------------------------------------------- */
 
-int usbvision_i2c_usb_del_bus(struct i2c_adapter *);
-
-
 /* ----------------------------------------------------------------------- */
 /* usbvision specific I2C functions                                        */
 /* ----------------------------------------------------------------------- */
-int usbvision_init_i2c(struct usb_usbvision *usbvision);
+int usbvision_i2c_register(struct usb_usbvision *usbvision);
+int usbvision_i2c_unregister(struct usb_usbvision *usbvision);
 void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd,void *arg);
 
 /* defined in usbvision-core.c                                      */
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index 5474760..49f1df7 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -60,6 +60,7 @@
 #include <linux/video_decoder.h>
 #define __OLD_VIDIOC_ /* To allow fixing old calls*/
 #include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
 
 #ifdef CONFIG_KMOD
 #include <linux/kmod.h>
@@ -260,6 +261,8 @@
 	[V4L2_FIELD_SEQ_TB]     = "seq-tb",
 	[V4L2_FIELD_SEQ_BT]     = "seq-bt",
 	[V4L2_FIELD_ALTERNATE]  = "alternate",
+	[V4L2_FIELD_INTERLACED_TB] = "interlaced-tb",
+	[V4L2_FIELD_INTERLACED_BT] = "interlaced-bt",
 };
 
 char *v4l2_type_names[] = {
@@ -269,7 +272,8 @@
 	[V4L2_BUF_TYPE_VBI_CAPTURE]        = "vbi-cap",
 	[V4L2_BUF_TYPE_VBI_OUTPUT]         = "vbi-out",
 	[V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-cap",
-	[V4L2_BUF_TYPE_SLICED_VBI_OUTPUT]  = "slicec-vbi-out",
+	[V4L2_BUF_TYPE_SLICED_VBI_OUTPUT]  = "sliced-vbi-out",
+	[V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "video-out-over",
 };
 
 
@@ -380,6 +384,8 @@
 
 	[_IOC_NR(VIDIOC_DBG_S_REGISTER)]   = "VIDIOC_DBG_S_REGISTER",
 	[_IOC_NR(VIDIOC_DBG_G_REGISTER)]   = "VIDIOC_DBG_G_REGISTER",
+
+	[_IOC_NR(VIDIOC_G_CHIP_IDENT)]     = "VIDIOC_G_CHIP_IDENT",
 #endif
 };
 #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
@@ -410,14 +416,16 @@
 	[_IOC_NR(VIDIOC_INT_DECODE_VBI_LINE)]  = "VIDIOC_INT_DECODE_VBI_LINE",
 	[_IOC_NR(VIDIOC_INT_S_VBI_DATA)]       = "VIDIOC_INT_S_VBI_DATA",
 	[_IOC_NR(VIDIOC_INT_G_VBI_DATA)]       = "VIDIOC_INT_G_VBI_DATA",
-	[_IOC_NR(VIDIOC_INT_G_CHIP_IDENT)]     = "VIDIOC_INT_G_CHIP_IDENT",
 	[_IOC_NR(VIDIOC_INT_I2S_CLOCK_FREQ)]   = "VIDIOC_INT_I2S_CLOCK_FREQ",
 	[_IOC_NR(VIDIOC_INT_S_STANDBY)]        = "VIDIOC_INT_S_STANDBY",
 	[_IOC_NR(VIDIOC_INT_S_AUDIO_ROUTING)]  = "VIDIOC_INT_S_AUDIO_ROUTING",
 	[_IOC_NR(VIDIOC_INT_G_AUDIO_ROUTING)]  = "VIDIOC_INT_G_AUDIO_ROUTING",
 	[_IOC_NR(VIDIOC_INT_S_VIDEO_ROUTING)]  = "VIDIOC_INT_S_VIDEO_ROUTING",
 	[_IOC_NR(VIDIOC_INT_G_VIDEO_ROUTING)]  = "VIDIOC_INT_G_VIDEO_ROUTING",
-	[_IOC_NR(VIDIOC_INT_S_CRYSTAL_FREQ)]   = "VIDIOC_INT_S_CRYSTAL_FREQ"
+	[_IOC_NR(VIDIOC_INT_S_CRYSTAL_FREQ)]   = "VIDIOC_INT_S_CRYSTAL_FREQ",
+	[_IOC_NR(VIDIOC_INT_INIT)]   	       = "VIDIOC_INT_INIT",
+	[_IOC_NR(VIDIOC_INT_G_STD_OUTPUT)]     = "VIDIOC_INT_G_STD_OUTPUT",
+	[_IOC_NR(VIDIOC_INT_S_STD_OUTPUT)]     = "VIDIOC_INT_S_STD_OUTPUT",
 };
 #define V4L2_INT_IOCTLS ARRAY_SIZE(v4l2_int_ioctls)
 
@@ -680,6 +688,7 @@
 	case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: name = "Audio Stereo Mode Extension"; break;
 	case V4L2_CID_MPEG_AUDIO_EMPHASIS: 	name = "Audio Emphasis"; break;
 	case V4L2_CID_MPEG_AUDIO_CRC: 		name = "Audio CRC"; break;
+	case V4L2_CID_MPEG_AUDIO_MUTE: 		name = "Audio Mute"; break;
 	case V4L2_CID_MPEG_VIDEO_ENCODING: 	name = "Video Encoding"; break;
 	case V4L2_CID_MPEG_VIDEO_ASPECT: 	name = "Video Aspect"; break;
 	case V4L2_CID_MPEG_VIDEO_B_FRAMES: 	name = "Video B Frames"; break;
@@ -690,6 +699,8 @@
 	case V4L2_CID_MPEG_VIDEO_BITRATE: 	name = "Video Bitrate"; break;
 	case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: 	name = "Video Peak Bitrate"; break;
 	case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: name = "Video Temporal Decimation"; break;
+	case V4L2_CID_MPEG_VIDEO_MUTE: 		name = "Video Mute"; break;
+	case V4L2_CID_MPEG_VIDEO_MUTE_YUV:	name = "Video Mute YUV"; break;
 	case V4L2_CID_MPEG_STREAM_TYPE: 	name = "Stream Type"; break;
 	case V4L2_CID_MPEG_STREAM_PID_PMT: 	name = "Stream PMT Program ID"; break;
 	case V4L2_CID_MPEG_STREAM_PID_AUDIO: 	name = "Stream Audio Program ID"; break;
@@ -705,6 +716,7 @@
 	switch (qctrl->id) {
 	case V4L2_CID_AUDIO_MUTE:
 	case V4L2_CID_AUDIO_LOUDNESS:
+	case V4L2_CID_MPEG_AUDIO_MUTE:
 	case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
 	case V4L2_CID_MPEG_VIDEO_PULLDOWN:
 		qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
@@ -838,6 +850,8 @@
 				V4L2_MPEG_AUDIO_CRC_NONE,
 				V4L2_MPEG_AUDIO_CRC_CRC16, 1,
 				V4L2_MPEG_AUDIO_CRC_NONE);
+	case V4L2_CID_MPEG_AUDIO_MUTE:
+		return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0);
 	case V4L2_CID_MPEG_VIDEO_ENCODING:
 		return v4l2_ctrl_query_fill(qctrl,
 				V4L2_MPEG_VIDEO_ENCODING_MPEG_1,
@@ -867,6 +881,10 @@
 		return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000);
 	case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
 		return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0);
+	case V4L2_CID_MPEG_VIDEO_MUTE:
+		return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0);
+	case V4L2_CID_MPEG_VIDEO_MUTE_YUV:  /* Init YUV (really YCbCr) to black */
+		return v4l2_ctrl_query_fill(qctrl, 0, 0xffffff, 1, 0x008080);
 	case V4L2_CID_MPEG_STREAM_TYPE:
 		return v4l2_ctrl_query_fill(qctrl,
 				V4L2_MPEG_STREAM_TYPE_MPEG2_PS,
@@ -965,6 +983,22 @@
 	}
 }
 
+int v4l2_chip_ident_i2c_client(struct i2c_client *c, struct v4l2_chip_ident *chip,
+		u32 ident, u32 revision)
+{
+	if (!v4l2_chip_match_i2c_client(c, chip->match_type, chip->match_chip))
+		return 0;
+	if (chip->ident == V4L2_IDENT_NONE) {
+		chip->ident = ident;
+		chip->revision = revision;
+	}
+	else {
+		chip->ident = V4L2_IDENT_AMBIGUOUS;
+		chip->revision = 0;
+	}
+	return 0;
+}
+
 int v4l2_chip_match_host(u32 match_type, u32 match_chip)
 {
 	switch (match_type) {
@@ -999,6 +1033,7 @@
 EXPORT_SYMBOL(v4l2_ctrl_query_fill_std);
 
 EXPORT_SYMBOL(v4l2_chip_match_i2c_client);
+EXPORT_SYMBOL(v4l2_chip_ident_i2c_client);
 EXPORT_SYMBOL(v4l2_chip_match_host);
 
 /*
diff --git a/drivers/media/video/videocodec.c b/drivers/media/video/videocodec.c
index 290e641..f2bbd7a 100644
--- a/drivers/media/video/videocodec.c
+++ b/drivers/media/video/videocodec.c
@@ -348,6 +348,9 @@
 	kfree(videocodec_buf);
 	videocodec_buf = kmalloc(size, GFP_KERNEL);
 
+	if (!videocodec_buf)
+		return 0;
+
 	i = 0;
 	i += scnprintf(videocodec_buf + i, size - 1,
 		      "<S>lave or attached <M>aster name  type flags    magic    ");
diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c
index 011938f..80ac5f8 100644
--- a/drivers/media/video/videodev.c
+++ b/drivers/media/video/videodev.c
@@ -318,6 +318,7 @@
 	[V4L2_BUF_TYPE_VBI_OUTPUT]         = "vbi-out",
 	[V4L2_BUF_TYPE_SLICED_VBI_OUTPUT]  = "sliced-vbi-out",
 	[V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-capture",
+	[V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "video-out-over",
 	[V4L2_BUF_TYPE_PRIVATE]            = "private",
 };
 
@@ -330,6 +331,8 @@
 	[V4L2_FIELD_SEQ_TB]     = "seq-tb",
 	[V4L2_FIELD_SEQ_BT]     = "seq-bt",
 	[V4L2_FIELD_ALTERNATE]  = "alternate",
+	[V4L2_FIELD_INTERLACED_TB] = "interlaced-tb",
+	[V4L2_FIELD_INTERLACED_BT] = "interlaced-bt",
 };
 
 #define prt_names(a,arr) (((a)>=0)&&((a)<ARRAY_SIZE(arr)))?arr[a]:"unknown"
@@ -411,6 +414,10 @@
 		if (vfd->vidioc_try_fmt_vbi_output)
 			return (0);
 		break;
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+		if (vfd->vidioc_try_fmt_output_overlay)
+			return (0);
+		break;
 	case V4L2_BUF_TYPE_PRIVATE:
 		if (vfd->vidioc_try_fmt_type_private)
 			return (0);
@@ -525,6 +532,10 @@
 				ret=vfd->vidioc_enum_fmt_vbi_output(file,
 								fh, f);
 			break;
+		case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+			if (vfd->vidioc_enum_fmt_output_overlay)
+				ret=vfd->vidioc_enum_fmt_output_overlay(file, fh, f);
+			break;
 		case V4L2_BUF_TYPE_PRIVATE:
 			if (vfd->vidioc_enum_fmt_type_private)
 				ret=vfd->vidioc_enum_fmt_type_private(file,
@@ -582,6 +593,10 @@
 				ret=vfd->vidioc_g_fmt_video_output(file,
 								fh, f);
 			break;
+		case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+			if (vfd->vidioc_g_fmt_output_overlay)
+				ret=vfd->vidioc_g_fmt_output_overlay(file, fh, f);
+			break;
 		case V4L2_BUF_TYPE_VBI_OUTPUT:
 			if (vfd->vidioc_g_fmt_vbi_output)
 				ret=vfd->vidioc_g_fmt_vbi_output(file, fh, f);
@@ -630,6 +645,10 @@
 				ret=vfd->vidioc_s_fmt_video_output(file,
 								fh, f);
 			break;
+		case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+			if (vfd->vidioc_s_fmt_output_overlay)
+				ret=vfd->vidioc_s_fmt_output_overlay(file, fh, f);
+			break;
 		case V4L2_BUF_TYPE_VBI_OUTPUT:
 			if (vfd->vidioc_s_fmt_vbi_output)
 				ret=vfd->vidioc_s_fmt_vbi_output(file,
@@ -680,6 +699,10 @@
 				ret=vfd->vidioc_try_fmt_video_output(file,
 								fh, f);
 			break;
+		case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+			if (vfd->vidioc_try_fmt_output_overlay)
+				ret=vfd->vidioc_try_fmt_output_overlay(file, fh, f);
+			break;
 		case V4L2_BUF_TYPE_VBI_OUTPUT:
 			if (vfd->vidioc_try_fmt_vbi_output)
 				ret=vfd->vidioc_try_fmt_vbi_output(file,
@@ -1381,6 +1404,11 @@
 	case VIDIOC_G_PARM:
 	{
 		struct v4l2_streamparm *p=arg;
+		__u32 type=p->type;
+
+		memset(p,0,sizeof(*p));
+		p->type=type;
+
 		if (vfd->vidioc_g_parm) {
 			ret=vfd->vidioc_g_parm(file, fh, p);
 		} else {
@@ -1392,8 +1420,6 @@
 			v4l2_video_std_construct(&s, vfd->current_norm,
 						 v4l2_norm_to_name(vfd->current_norm));
 
-			memset(p,0,sizeof(*p));
-
 			p->parm.capture.timeperframe = s.frameperiod;
 			ret=0;
 		}
@@ -1509,6 +1535,16 @@
 		break;
 	}
 #endif
+	case VIDIOC_G_CHIP_IDENT:
+	{
+		struct v4l2_chip_ident *p=arg;
+		if (!vfd->vidioc_g_chip_ident)
+			break;
+		ret=vfd->vidioc_g_chip_ident(file, fh, p);
+		if (!ret)
+			dbgarg (cmd, "chip_ident=%u, revision=0x%x\n", p->ident, p->revision);
+		break;
+	}
 	} /* switch */
 
 	if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) {
diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c
index a9b59c3..8f6741a 100644
--- a/drivers/media/video/wm8739.c
+++ b/drivers/media/video/wm8739.c
@@ -29,6 +29,7 @@
 #include <linux/i2c-id.h>
 #include <linux/videodev.h>
 #include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
 
 MODULE_DESCRIPTION("wm8739 driver");
 MODULE_AUTHOR("T. Adachi, Hans Verkuil");
@@ -236,6 +237,9 @@
 		return -EINVAL;
 	}
 
+	case VIDIOC_G_CHIP_IDENT:
+		return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_WM8739, 0);
+
 	case VIDIOC_LOG_STATUS:
 		v4l_info(client, "Frequency: %u Hz\n", state->clock_freq);
 		v4l_info(client, "Volume L:  %02x%s\n", state->vol_l & 0x1f,
diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c
index d81a88b..4df5d30 100644
--- a/drivers/media/video/wm8775.c
+++ b/drivers/media/video/wm8775.c
@@ -33,6 +33,7 @@
 #include <linux/i2c-id.h>
 #include <linux/videodev.h>
 #include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
 
 MODULE_DESCRIPTION("wm8775 driver");
 MODULE_AUTHOR("Ulf Eklund, Hans Verkuil");
@@ -124,6 +125,9 @@
 			wm8775_write(client, R21, 0x100 + state->input);
 		break;
 
+	case VIDIOC_G_CHIP_IDENT:
+		return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_WM8775, 0);
+
 	case VIDIOC_LOG_STATUS:
 		v4l_info(client, "Input: %d%s\n", state->input,
 			    state->muted ? " (muted)" : "");
diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c
new file mode 100644
index 0000000..b5d3364
--- /dev/null
+++ b/drivers/media/video/zr364xx.c
@@ -0,0 +1,929 @@
+/*
+ * Zoran 364xx based USB webcam module version 0.72
+ *
+ * Allows you to use your USB webcam with V4L2 applications
+ * This is still in heavy developpement !
+ *
+ * Copyright (C) 2004  Antoine Jacquet <royale@zerezo.com>
+ * http://royale.zerezo.com/zr364xx/
+ *
+ * Heavily inspired by usb-skeleton.c, vicam.c, cpia.c and spca50x.c drivers
+ * V4L2 version inspired by meye.c driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/highmem.h>
+#include <media/v4l2-common.h>
+
+
+/* Version Information */
+#define DRIVER_VERSION "v0.72"
+#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/"
+#define DRIVER_DESC "Zoran 364xx"
+
+
+/* Camera */
+#define FRAMES 2
+#define MAX_FRAME_SIZE 100000
+#define BUFFER_SIZE 0x1000
+#define CTRL_TIMEOUT 500
+
+
+/* Debug macro */
+#define DBG(x...) if (debug) info(x)
+
+
+/* Init methods, need to find nicer names for these
+ * the exact names of the chipsets would be the best if someone finds it */
+#define METHOD0 0
+#define METHOD1 1
+#define METHOD2 2
+
+
+/* Module parameters */
+static int debug = 0;
+static int mode = 0;
+
+
+/* Module parameters interface */
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level");
+module_param(mode, int, 0644);
+MODULE_PARM_DESC(mode, "0 = 320x240, 1 = 160x120, 2 = 640x480");
+
+
+/* Devices supported by this driver
+ * .driver_info contains the init method used by the camera */
+static struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x08ca, 0x0109), .driver_info = METHOD0 },
+	{USB_DEVICE(0x041e, 0x4024), .driver_info = METHOD0 },
+	{USB_DEVICE(0x0d64, 0x0108), .driver_info = METHOD0 },
+	{USB_DEVICE(0x0546, 0x3187), .driver_info = METHOD0 },
+	{USB_DEVICE(0x0d64, 0x3108), .driver_info = METHOD0 },
+	{USB_DEVICE(0x0595, 0x4343), .driver_info = METHOD0 },
+	{USB_DEVICE(0x0bb0, 0x500d), .driver_info = METHOD0 },
+	{USB_DEVICE(0x0feb, 0x2004), .driver_info = METHOD0 },
+	{USB_DEVICE(0x055f, 0xb500), .driver_info = METHOD0 },
+	{USB_DEVICE(0x08ca, 0x2062), .driver_info = METHOD2 },
+	{USB_DEVICE(0x052b, 0x1a18), .driver_info = METHOD1 },
+	{USB_DEVICE(0x04c8, 0x0729), .driver_info = METHOD0 },
+	{USB_DEVICE(0x04f2, 0xa208), .driver_info = METHOD0 },
+	{USB_DEVICE(0x0784, 0x0040), .driver_info = METHOD1 },
+	{USB_DEVICE(0x06d6, 0x0034), .driver_info = METHOD0 },
+	{USB_DEVICE(0x0a17, 0x0062), .driver_info = METHOD2 },
+	{}			/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+
+/* Camera stuff */
+struct zr364xx_camera {
+	struct usb_device *udev;	/* save off the usb device pointer */
+	struct usb_interface *interface;/* the interface for this device */
+	struct video_device *vdev;	/* v4l video device */
+	u8 *framebuf;
+	int nb;
+	unsigned char *buffer;
+	int skip;
+	int brightness;
+	int width;
+	int height;
+	int method;
+	struct mutex lock;
+};
+
+
+/* function used to send initialisation commands to the camera */
+static int send_control_msg(struct usb_device *udev, u8 request, u16 value,
+			    u16 index, unsigned char *cp, u16 size)
+{
+	int status;
+
+	unsigned char *transfer_buffer = kmalloc(size, GFP_KERNEL);
+	if (!transfer_buffer) {
+		info("kmalloc(%d) failed", size);
+		return -ENOMEM;
+	}
+
+	memcpy(transfer_buffer, cp, size);
+
+	status = usb_control_msg(udev,
+				 usb_sndctrlpipe(udev, 0),
+				 request,
+				 USB_DIR_OUT | USB_TYPE_VENDOR |
+				 USB_RECIP_DEVICE, value, index,
+				 transfer_buffer, size, CTRL_TIMEOUT);
+
+	kfree(transfer_buffer);
+
+	if (status < 0)
+		info("Failed sending control message, error %d.", status);
+
+	return status;
+}
+
+
+/* Control messages sent to the camera to initialize it
+ * and launch the capture */
+typedef struct {
+	unsigned int value;
+	unsigned int size;
+	unsigned char *bytes;
+} message;
+
+/* method 0 */
+static unsigned char m0d1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char m0d2[] = { 0, 0, 0, 0, 0, 0 };
+static unsigned char m0d3[] = { 0, 0 };
+static message m0[] = {
+	{0x1f30, 0, NULL},
+	{0xd000, 0, NULL},
+	{0x3370, sizeof(m0d1), m0d1},
+	{0x2000, 0, NULL},
+	{0x2f0f, 0, NULL},
+	{0x2610, sizeof(m0d2), m0d2},
+	{0xe107, 0, NULL},
+	{0x2502, 0, NULL},
+	{0x1f70, 0, NULL},
+	{0xd000, 0, NULL},
+	{0x9a01, sizeof(m0d3), m0d3},
+	{-1, -1, NULL}
+};
+
+/* method 1 */
+static unsigned char m1d1[] = { 0xff, 0xff };
+static unsigned char m1d2[] = { 0x00, 0x00 };
+static message m1[] = {
+	{0x1f30, 0, NULL},
+	{0xd000, 0, NULL},
+	{0xf000, 0, NULL},
+	{0x2000, 0, NULL},
+	{0x2f0f, 0, NULL},
+	{0x2650, 0, NULL},
+	{0xe107, 0, NULL},
+	{0x2502, sizeof(m1d1), m1d1},
+	{0x1f70, 0, NULL},
+	{0xd000, 0, NULL},
+	{0xd000, 0, NULL},
+	{0xd000, 0, NULL},
+	{0x9a01, sizeof(m1d2), m1d2},
+	{-1, -1, NULL}
+};
+
+/* method 2 */
+static unsigned char m2d1[] = { 0xff, 0xff };
+static message m2[] = {
+	{0x1f30, 0, NULL},
+	{0xf000, 0, NULL},
+	{0x2000, 0, NULL},
+	{0x2f0f, 0, NULL},
+	{0x2650, 0, NULL},
+	{0xe107, 0, NULL},
+	{0x2502, sizeof(m2d1), m2d1},
+	{0x1f70, 0, NULL},
+	{-1, -1, NULL}
+};
+
+/* init table */
+static message *init[3] = { m0, m1, m2 };
+
+
+/* JPEG static data in header (Huffman table, etc) */
+static unsigned char header1[] = {
+	0xFF, 0xD8,
+	/*
+	0xFF, 0xE0, 0x00, 0x10, 'J', 'F', 'I', 'F',
+	0x00, 0x01, 0x01, 0x00, 0x33, 0x8A, 0x00, 0x00, 0x33, 0x88,
+	*/
+	0xFF, 0xDB, 0x00, 0x84
+};
+static unsigned char header2[] = {
+	0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01,
+	0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
+	0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02,
+	0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01,
+	0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
+	0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1,
+	0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33,
+	0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25,
+	0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+	0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54,
+	0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67,
+	0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A,
+	0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94,
+	0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6,
+	0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8,
+	0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA,
+	0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2,
+	0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3,
+	0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, 0x1F,
+	0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
+	0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5,
+	0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05,
+	0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11,
+	0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
+	0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1,
+	0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16,
+	0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27,
+	0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
+	0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57,
+	0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A,
+	0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84,
+	0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96,
+	0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
+	0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA,
+	0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3,
+	0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5,
+	0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+	0xF8, 0xF9, 0xFA, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01,
+	0x40, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01,
+	0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11,
+	0x00, 0x3F, 0x00
+};
+static unsigned char header3;
+
+
+
+/********************/
+/* V4L2 integration */
+/********************/
+
+/* this function reads a full JPEG picture synchronously
+ * TODO: do it asynchronously... */
+static int read_frame(struct zr364xx_camera *cam, int framenum)
+{
+	int i, n, temp, head, size, actual_length;
+	unsigned char *ptr = NULL, *jpeg;
+
+      redo:
+	/* hardware brightness */
+	n = send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0);
+	temp = (0x60 << 8) + 127 - cam->brightness;
+	n = send_control_msg(cam->udev, 1, temp, 0, NULL, 0);
+
+	/* during the first loop we are going to insert JPEG header */
+	head = 0;
+	/* this is the place in memory where we are going to build
+	 * the JPEG image */
+	jpeg = cam->framebuf + framenum * MAX_FRAME_SIZE;
+	/* read data... */
+	do {
+		n = usb_bulk_msg(cam->udev,
+				 usb_rcvbulkpipe(cam->udev, 0x81),
+				 cam->buffer, BUFFER_SIZE, &actual_length,
+				 CTRL_TIMEOUT);
+		DBG("buffer : %d %d", cam->buffer[0], cam->buffer[1]);
+		DBG("bulk : n=%d size=%d", n, actual_length);
+		if (n < 0) {
+			info("error reading bulk msg");
+			return 0;
+		}
+		if (actual_length < 0 || actual_length > BUFFER_SIZE) {
+			info("wrong number of bytes");
+			return 0;
+		}
+
+		/* swap bytes if camera needs it */
+		if (cam->method == METHOD0) {
+			u16 *buf = (u16*)cam->buffer;
+			for (i = 0; i < BUFFER_SIZE/2; i++)
+				swab16s(buf + i);
+		}
+
+		/* write the JPEG header */
+		if (!head) {
+			DBG("jpeg header");
+			ptr = jpeg;
+			memcpy(ptr, header1, sizeof(header1));
+			ptr += sizeof(header1);
+			header3 = 0;
+			memcpy(ptr, &header3, 1);
+			ptr++;
+			memcpy(ptr, cam->buffer, 64);
+			ptr += 64;
+			header3 = 1;
+			memcpy(ptr, &header3, 1);
+			ptr++;
+			memcpy(ptr, cam->buffer + 64, 64);
+			ptr += 64;
+			memcpy(ptr, header2, sizeof(header2));
+			ptr += sizeof(header2);
+			memcpy(ptr, cam->buffer + 128,
+			       actual_length - 128);
+			ptr += actual_length - 128;
+			head = 1;
+			DBG("header : %d %d %d %d %d %d %d %d %d",
+			    cam->buffer[0], cam->buffer[1], cam->buffer[2],
+			    cam->buffer[3], cam->buffer[4], cam->buffer[5],
+			    cam->buffer[6], cam->buffer[7], cam->buffer[8]);
+		} else {
+			memcpy(ptr, cam->buffer, actual_length);
+			ptr += actual_length;
+		}
+	}
+	/* ... until there is no more */
+	while (actual_length == BUFFER_SIZE);
+
+	/* we skip the 2 first frames which are usually buggy */
+	if (cam->skip) {
+		cam->skip--;
+		goto redo;
+	}
+
+	/* go back to find the JPEG EOI marker */
+	size = ptr - jpeg;
+	ptr -= 2;
+	while (ptr > jpeg) {
+		if (*ptr == 0xFF && *(ptr + 1) == 0xD9
+		    && *(ptr + 2) == 0xFF)
+			break;
+		ptr--;
+	}
+	if (ptr == jpeg)
+		DBG("No EOI marker");
+
+	/* Sometimes there is junk data in the middle of the picture,
+	 * we want to skip this bogus frames */
+	while (ptr > jpeg) {
+		if (*ptr == 0xFF && *(ptr + 1) == 0xFF
+		    && *(ptr + 2) == 0xFF)
+			break;
+		ptr--;
+	}
+	if (ptr != jpeg) {
+		DBG("Bogus frame ? %d", cam->nb);
+		goto redo;
+	}
+
+	DBG("jpeg : %d %d %d %d %d %d %d %d",
+	    jpeg[0], jpeg[1], jpeg[2], jpeg[3],
+	    jpeg[4], jpeg[5], jpeg[6], jpeg[7]);
+
+	return size;
+}
+
+
+static ssize_t zr364xx_read(struct file *file, char *buf, size_t cnt,
+			    loff_t * ppos)
+{
+	unsigned long count = cnt;
+	struct video_device *vdev = video_devdata(file);
+	struct zr364xx_camera *cam;
+
+	DBG("zr364xx_read: read %d bytes.", (int) count);
+
+	if (vdev == NULL)
+		return -ENODEV;
+	cam = video_get_drvdata(vdev);
+
+	if (!buf)
+		return -EINVAL;
+
+	if (!count)
+		return -EINVAL;
+
+	/* NoMan Sux ! */
+	count = read_frame(cam, 0);
+
+	if (copy_to_user(buf, cam->framebuf, count))
+		return -EFAULT;
+
+	return count;
+}
+
+
+static int zr364xx_vidioc_querycap(struct file *file, void *priv,
+				   struct v4l2_capability *cap)
+{
+	memset(cap, 0, sizeof(*cap));
+	strcpy(cap->driver, DRIVER_DESC);
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+	return 0;
+}
+
+static int zr364xx_vidioc_enum_input(struct file *file, void *priv,
+				     struct v4l2_input *i)
+{
+	if (i->index != 0)
+		return -EINVAL;
+	memset(i, 0, sizeof(*i));
+	i->index = 0;
+	strcpy(i->name, DRIVER_DESC " Camera");
+	i->type = V4L2_INPUT_TYPE_CAMERA;
+	return 0;
+}
+
+static int zr364xx_vidioc_g_input(struct file *file, void *priv,
+				  unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int zr364xx_vidioc_s_input(struct file *file, void *priv,
+				  unsigned int i)
+{
+	if (i != 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int zr364xx_vidioc_queryctrl(struct file *file, void *priv,
+				    struct v4l2_queryctrl *c)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct zr364xx_camera *cam;
+
+	if (vdev == NULL)
+		return -ENODEV;
+	cam = video_get_drvdata(vdev);
+
+	switch (c->id) {
+	case V4L2_CID_BRIGHTNESS:
+		c->type = V4L2_CTRL_TYPE_INTEGER;
+		strcpy(c->name, "Brightness");
+		c->minimum = 0;
+		c->maximum = 127;
+		c->step = 1;
+		c->default_value = cam->brightness;
+		c->flags = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int zr364xx_vidioc_s_ctrl(struct file *file, void *priv,
+				 struct v4l2_control *c)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct zr364xx_camera *cam;
+
+	if (vdev == NULL)
+		return -ENODEV;
+	cam = video_get_drvdata(vdev);
+
+	switch (c->id) {
+	case V4L2_CID_BRIGHTNESS:
+		cam->brightness = c->value;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int zr364xx_vidioc_g_ctrl(struct file *file, void *priv,
+				 struct v4l2_control *c)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct zr364xx_camera *cam;
+
+	if (vdev == NULL)
+		return -ENODEV;
+	cam = video_get_drvdata(vdev);
+
+	switch (c->id) {
+	case V4L2_CID_BRIGHTNESS:
+		c->value = cam->brightness;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int zr364xx_vidioc_enum_fmt_cap(struct file *file,
+				       void *priv, struct v4l2_fmtdesc *f)
+{
+	if (f->index > 0)
+		return -EINVAL;
+	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	memset(f, 0, sizeof(*f));
+	f->index = 0;
+	f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	f->flags = V4L2_FMT_FLAG_COMPRESSED;
+	strcpy(f->description, "JPEG");
+	f->pixelformat = V4L2_PIX_FMT_JPEG;
+	return 0;
+}
+
+static int zr364xx_vidioc_try_fmt_cap(struct file *file, void *priv,
+				      struct v4l2_format *f)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct zr364xx_camera *cam;
+
+	if (vdev == NULL)
+		return -ENODEV;
+	cam = video_get_drvdata(vdev);
+
+	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG)
+		return -EINVAL;
+	if (f->fmt.pix.field != V4L2_FIELD_ANY &&
+	    f->fmt.pix.field != V4L2_FIELD_NONE)
+		return -EINVAL;
+	f->fmt.pix.field = V4L2_FIELD_NONE;
+	f->fmt.pix.width = cam->width;
+	f->fmt.pix.height = cam->height;
+	f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+	f->fmt.pix.colorspace = 0;
+	f->fmt.pix.priv = 0;
+	return 0;
+}
+
+static int zr364xx_vidioc_g_fmt_cap(struct file *file, void *priv,
+				    struct v4l2_format *f)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct zr364xx_camera *cam;
+
+	if (vdev == NULL)
+		return -ENODEV;
+	cam = video_get_drvdata(vdev);
+
+	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format));
+	f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	f->fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;
+	f->fmt.pix.field = V4L2_FIELD_NONE;
+	f->fmt.pix.width = cam->width;
+	f->fmt.pix.height = cam->height;
+	f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+	f->fmt.pix.colorspace = 0;
+	f->fmt.pix.priv = 0;
+	return 0;
+}
+
+static int zr364xx_vidioc_s_fmt_cap(struct file *file, void *priv,
+				    struct v4l2_format *f)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct zr364xx_camera *cam;
+
+	if (vdev == NULL)
+		return -ENODEV;
+	cam = video_get_drvdata(vdev);
+
+	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG)
+		return -EINVAL;
+	if (f->fmt.pix.field != V4L2_FIELD_ANY &&
+	    f->fmt.pix.field != V4L2_FIELD_NONE)
+		return -EINVAL;
+	f->fmt.pix.field = V4L2_FIELD_NONE;
+	f->fmt.pix.width = cam->width;
+	f->fmt.pix.height = cam->height;
+	f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+	f->fmt.pix.colorspace = 0;
+	f->fmt.pix.priv = 0;
+	DBG("ok!");
+	return 0;
+}
+
+static int zr364xx_vidioc_streamon(struct file *file, void *priv,
+				   enum v4l2_buf_type type)
+{
+	return 0;
+}
+
+static int zr364xx_vidioc_streamoff(struct file *file, void *priv,
+				    enum v4l2_buf_type type)
+{
+	return 0;
+}
+
+
+/* open the camera */
+static int zr364xx_open(struct inode *inode, struct file *file)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct zr364xx_camera *cam = video_get_drvdata(vdev);
+	struct usb_device *udev = cam->udev;
+	int i, err;
+
+	DBG("zr364xx_open");
+
+	cam->skip = 2;
+
+	err = video_exclusive_open(inode, file);
+	if (err < 0)
+		return err;
+
+	if (!cam->framebuf) {
+		cam->framebuf = vmalloc_32(MAX_FRAME_SIZE * FRAMES);
+		if (!cam->framebuf) {
+			info("vmalloc_32 failed!");
+			return -ENOMEM;
+		}
+	}
+
+	mutex_lock(&cam->lock);
+	for (i = 0; init[cam->method][i].size != -1; i++) {
+		err =
+		    send_control_msg(udev, 1, init[cam->method][i].value,
+				     0, init[cam->method][i].bytes,
+				     init[cam->method][i].size);
+		if (err < 0) {
+			info("error during open sequence: %d", i);
+			mutex_unlock(&cam->lock);
+			return err;
+		}
+	}
+
+	file->private_data = vdev;
+
+	/* Added some delay here, since opening/closing the camera quickly,
+	 * like Ekiga does during its startup, can crash the webcam
+	 */
+	mdelay(100);
+
+	mutex_unlock(&cam->lock);
+	return 0;
+}
+
+
+/* release the camera */
+static int zr364xx_release(struct inode *inode, struct file *file)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct zr364xx_camera *cam;
+	struct usb_device *udev;
+	int i, err;
+
+	DBG("zr364xx_release");
+
+	if (vdev == NULL)
+		return -ENODEV;
+	cam = video_get_drvdata(vdev);
+
+	udev = cam->udev;
+
+	mutex_lock(&cam->lock);
+	for (i = 0; i < 2; i++) {
+		err =
+		    send_control_msg(udev, 1, init[cam->method][i].value,
+				     0, init[i][cam->method].bytes,
+				     init[cam->method][i].size);
+		if (err < 0) {
+			info("error during release sequence");
+			mutex_unlock(&cam->lock);
+			return err;
+		}
+	}
+
+	file->private_data = NULL;
+	video_exclusive_release(inode, file);
+
+	/* Added some delay here, since opening/closing the camera quickly,
+	 * like Ekiga does during its startup, can crash the webcam
+	 */
+	mdelay(100);
+
+	mutex_unlock(&cam->lock);
+	return 0;
+}
+
+
+static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	void *pos;
+	unsigned long start = vma->vm_start;
+	unsigned long size = vma->vm_end - vma->vm_start;
+	struct video_device *vdev = video_devdata(file);
+	struct zr364xx_camera *cam;
+
+	DBG("zr364xx_mmap: %ld\n", size);
+
+	if (vdev == NULL)
+		return -ENODEV;
+	cam = video_get_drvdata(vdev);
+
+	pos = cam->framebuf;
+	while (size > 0) {
+		if (vm_insert_page(vma, start, vmalloc_to_page(pos)))
+			return -EAGAIN;
+		start += PAGE_SIZE;
+		pos += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
+	}
+
+	return 0;
+}
+
+
+static struct file_operations zr364xx_fops = {
+	.owner = THIS_MODULE,
+	.open = zr364xx_open,
+	.release = zr364xx_release,
+	.read = zr364xx_read,
+	.mmap = zr364xx_mmap,
+	.ioctl = video_ioctl2,
+	.llseek = no_llseek,
+};
+
+static struct video_device zr364xx_template = {
+	.owner = THIS_MODULE,
+	.name = DRIVER_DESC,
+	.type = VID_TYPE_CAPTURE,
+	.fops = &zr364xx_fops,
+	.release = video_device_release,
+	.minor = -1,
+
+	.vidioc_querycap	= zr364xx_vidioc_querycap,
+	.vidioc_enum_fmt_cap	= zr364xx_vidioc_enum_fmt_cap,
+	.vidioc_try_fmt_cap	= zr364xx_vidioc_try_fmt_cap,
+	.vidioc_s_fmt_cap	= zr364xx_vidioc_s_fmt_cap,
+	.vidioc_g_fmt_cap	= zr364xx_vidioc_g_fmt_cap,
+	.vidioc_enum_input	= zr364xx_vidioc_enum_input,
+	.vidioc_g_input		= zr364xx_vidioc_g_input,
+	.vidioc_s_input		= zr364xx_vidioc_s_input,
+	.vidioc_streamon	= zr364xx_vidioc_streamon,
+	.vidioc_streamoff	= zr364xx_vidioc_streamoff,
+	.vidioc_queryctrl	= zr364xx_vidioc_queryctrl,
+	.vidioc_g_ctrl		= zr364xx_vidioc_g_ctrl,
+	.vidioc_s_ctrl		= zr364xx_vidioc_s_ctrl,
+};
+
+
+
+/*******************/
+/* USB integration */
+/*******************/
+
+static int zr364xx_probe(struct usb_interface *intf,
+			 const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct zr364xx_camera *cam = NULL;
+
+	DBG("probing...");
+
+	info(DRIVER_DESC " compatible webcam plugged");
+	info("model %04x:%04x detected", udev->descriptor.idVendor,
+	     udev->descriptor.idProduct);
+
+	if ((cam =
+	     kmalloc(sizeof(struct zr364xx_camera), GFP_KERNEL)) == NULL) {
+		info("cam: out of memory !");
+		return -ENODEV;
+	}
+	memset(cam, 0x00, sizeof(struct zr364xx_camera));
+	/* save the init method used by this camera */
+	cam->method = id->driver_info;
+
+	cam->vdev = video_device_alloc();
+	if (cam->vdev == NULL) {
+		info("cam->vdev: out of memory !");
+		kfree(cam);
+		return -ENODEV;
+	}
+	memcpy(cam->vdev, &zr364xx_template, sizeof(zr364xx_template));
+	video_set_drvdata(cam->vdev, cam);
+	if (debug)
+		cam->vdev->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
+
+	cam->udev = udev;
+
+	if ((cam->buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL)) == NULL) {
+		info("cam->buffer: out of memory !");
+		video_device_release(cam->vdev);
+		kfree(cam);
+		return -ENODEV;
+	}
+
+	switch (mode) {
+	case 1:
+		info("160x120 mode selected");
+		cam->width = 160;
+		cam->height = 120;
+		break;
+	case 2:
+		info("640x480 mode selected");
+		cam->width = 640;
+		cam->height = 480;
+		break;
+	default:
+		info("320x240 mode selected");
+		cam->width = 320;
+		cam->height = 240;
+		break;
+	}
+
+	m0d1[0] = mode;
+	m1[2].value = 0xf000 + mode;
+	m2[1].value = 0xf000 + mode;
+	header2[437] = cam->height / 256;
+	header2[438] = cam->height % 256;
+	header2[439] = cam->width / 256;
+	header2[440] = cam->width % 256;
+
+	cam->nb = 0;
+	cam->brightness = 64;
+	mutex_init(&cam->lock);
+
+	if (video_register_device(cam->vdev, VFL_TYPE_GRABBER, -1) == -1) {
+		info("video_register_device failed");
+		video_device_release(cam->vdev);
+		kfree(cam->buffer);
+		kfree(cam);
+		return -ENODEV;
+	}
+
+	usb_set_intfdata(intf, cam);
+
+	info(DRIVER_DESC " controlling video device %d", cam->vdev->minor);
+	return 0;
+}
+
+
+static void zr364xx_disconnect(struct usb_interface *intf)
+{
+	struct zr364xx_camera *cam = usb_get_intfdata(intf);
+	usb_set_intfdata(intf, NULL);
+	dev_set_drvdata(&intf->dev, NULL);
+	info(DRIVER_DESC " webcam unplugged");
+	if (cam->vdev)
+		video_unregister_device(cam->vdev);
+	cam->vdev = NULL;
+	kfree(cam->buffer);
+	if (cam->framebuf)
+		vfree(cam->framebuf);
+	kfree(cam);
+}
+
+
+
+/**********************/
+/* Module integration */
+/**********************/
+
+static struct usb_driver zr364xx_driver = {
+	.name = "zr364xx",
+	.probe = zr364xx_probe,
+	.disconnect = zr364xx_disconnect,
+	.id_table = device_table
+};
+
+
+static int __init zr364xx_init(void)
+{
+	int retval;
+	retval = usb_register(&zr364xx_driver) < 0;
+	if (retval)
+		info("usb_register failed!");
+	else
+		info(DRIVER_DESC " module loaded");
+	return retval;
+}
+
+
+static void __exit zr364xx_exit(void)
+{
+	info(DRIVER_DESC " module unloaded");
+	usb_deregister(&zr364xx_driver);
+}
+
+
+module_init(zr364xx_init);
+module_exit(zr364xx_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/message/fusion/mptlan.c b/drivers/message/fusion/mptlan.c
index b691292..7dd34bd 100644
--- a/drivers/message/fusion/mptlan.c
+++ b/drivers/message/fusion/mptlan.c
@@ -714,6 +714,7 @@
 	LANSendRequest_t *pSendReq;
 	SGETransaction32_t *pTrans;
 	SGESimple64_t *pSimple;
+	const unsigned char *mac;
 	dma_addr_t dma;
 	unsigned long flags;
 	int ctx;
@@ -753,7 +754,7 @@
 	/* Set the mac.raw pointer, since this apparently isn't getting
 	 * done before we get the skb. Pull the data pointer past the mac data.
 	 */
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	skb_pull(skb, 12);
 
         dma = pci_map_single(mpt_dev->pcidev, skb->data, skb->len,
@@ -784,6 +785,7 @@
 //			IOC_AND_NETDEV_NAMES_s_s(dev),
 //			ctx, skb, skb->data));
 
+	mac = skb_mac_header(skb);
 #ifdef QLOGIC_NAA_WORKAROUND
 {
 	struct NAA_Hosed *nh;
@@ -793,12 +795,12 @@
 	   drops. */
 	read_lock_irq(&bad_naa_lock);
 	for (nh = mpt_bad_naa; nh != NULL; nh=nh->next) {
-		if ((nh->ieee[0] == skb->mac.raw[0]) &&
-		    (nh->ieee[1] == skb->mac.raw[1]) &&
-		    (nh->ieee[2] == skb->mac.raw[2]) &&
-		    (nh->ieee[3] == skb->mac.raw[3]) &&
-		    (nh->ieee[4] == skb->mac.raw[4]) &&
-		    (nh->ieee[5] == skb->mac.raw[5])) {
+		if ((nh->ieee[0] == mac[0]) &&
+		    (nh->ieee[1] == mac[1]) &&
+		    (nh->ieee[2] == mac[2]) &&
+		    (nh->ieee[3] == mac[3]) &&
+		    (nh->ieee[4] == mac[4]) &&
+		    (nh->ieee[5] == mac[5])) {
 			cur_naa = nh->NAA;
 			dlprintk ((KERN_INFO "mptlan/sdu_send: using NAA value "
 				  "= %04x.\n", cur_naa));
@@ -810,12 +812,12 @@
 #endif
 
 	pTrans->TransactionDetails[0] = cpu_to_le32((cur_naa         << 16) |
-						    (skb->mac.raw[0] <<  8) |
-						    (skb->mac.raw[1] <<  0));
-	pTrans->TransactionDetails[1] = cpu_to_le32((skb->mac.raw[2] << 24) |
-						    (skb->mac.raw[3] << 16) |
-						    (skb->mac.raw[4] <<  8) |
-						    (skb->mac.raw[5] <<  0));
+						    (mac[0] <<  8) |
+						    (mac[1] <<  0));
+	pTrans->TransactionDetails[1] = cpu_to_le32((mac[2] << 24) |
+						    (mac[3] << 16) |
+						    (mac[4] <<  8) |
+						    (mac[5] <<  0));
 
 	pSimple = (SGESimple64_t *) &pTrans->TransactionDetails[2];
 
@@ -930,7 +932,7 @@
 		pci_dma_sync_single_for_cpu(mpt_dev->pcidev, priv->RcvCtl[ctx].dma,
 					    priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE);
 
-		memcpy(skb_put(skb, len), old_skb->data, len);
+		skb_copy_from_linear_data(old_skb, skb_put(skb, len), len);
 
 		pci_dma_sync_single_for_device(mpt_dev->pcidev, priv->RcvCtl[ctx].dma,
 					       priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE);
@@ -1091,7 +1093,7 @@
 						    priv->RcvCtl[ctx].dma,
 						    priv->RcvCtl[ctx].len,
 						    PCI_DMA_FROMDEVICE);
-			memcpy(skb_put(skb, l), old_skb->data, l);
+			skb_copy_from_linear_data(old_skb, skb_put(skb, l), l);
 
 			pci_dma_sync_single_for_device(mpt_dev->pcidev,
 						       priv->RcvCtl[ctx].dma,
@@ -1120,7 +1122,7 @@
 					    priv->RcvCtl[ctx].len,
 					    PCI_DMA_FROMDEVICE);
 
-		memcpy(skb_put(skb, len), old_skb->data, len);
+		skb_copy_from_linear_data(old_skb, skb_put(skb, len), len);
 
 		pci_dma_sync_single_for_device(mpt_dev->pcidev,
 					       priv->RcvCtl[ctx].dma,
@@ -1549,7 +1551,7 @@
 	struct mpt_lan_ohdr *fch = (struct mpt_lan_ohdr *)skb->data;
 	struct fcllc *fcllc;
 
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	skb_pull(skb, sizeof(struct mpt_lan_ohdr));
 
 	if (fch->dtype == htons(0xffff)) {
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 80b199f..a3c525b 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -112,14 +112,70 @@
 	depends on X86 && ACPI
 	select BACKLIGHT_CLASS_DEVICE
 	  ---help---
-	  This mini-driver drives the SNC device present in the ACPI BIOS of
-	  the Sony Vaio laptops.
+	  This mini-driver drives the SNC and SPIC devices present in the ACPI
+	  BIOS of the Sony Vaio laptops.
 
-	  It gives access to some extra laptop functionalities. In its current
-	  form, this driver let the user set or query the screen brightness
-	  through the backlight subsystem and remove/apply power to some
+	  It gives access to some extra laptop functionalities like Bluetooth,
+	  screen brightness control, Fn keys and allows powering on/off some
 	  devices.
 
 	  Read <file:Documentation/sony-laptop.txt> for more information.
 
+config SONY_LAPTOP_OLD
+	bool "Sonypi compatibility"
+	depends on SONY_LAPTOP
+	  ---help---
+	  Build the sonypi driver compatibility code into the sony-laptop driver.
+
+config THINKPAD_ACPI
+	tristate "ThinkPad ACPI Laptop Extras"
+	depends on X86 && ACPI
+	select BACKLIGHT_CLASS_DEVICE
+	select HWMON
+	---help---
+	  This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
+	  support for Fn-Fx key combinations, Bluetooth control, video
+	  output switching, ThinkLight control, UltraBay eject and more.
+	  For more information about this driver see 
+	  <file:Documentation/thinkpad-acpi.txt> and <http://ibm-acpi.sf.net/> .
+
+	  This driver was formely known as ibm-acpi.
+
+	  If you have an IBM or Lenovo ThinkPad laptop, say Y or M here.
+
+config THINKPAD_ACPI_DEBUG
+	bool "Verbose debug mode"
+	depends on THINKPAD_ACPI
+	default n
+	---help---
+	  Enables extra debugging information, at the expense of a slightly
+	  increase in driver size.
+
+	  If you are not sure, say N here.
+
+config THINKPAD_ACPI_DOCK
+	bool "Legacy Docking Station Support"
+	depends on THINKPAD_ACPI
+	depends on ACPI_DOCK=n
+	default n
+	---help---
+	  Allows the thinkpad_acpi driver to handle docking station events.
+	  This support was made obsolete by the generic ACPI docking station
+	  support (CONFIG_ACPI_DOCK).  It will allow locking and removing the
+	  laptop from the docking station, but will not properly connect PCI
+	  devices.
+
+	  If you are not sure, say N here.
+
+config THINKPAD_ACPI_BAY
+	bool "Legacy Removable Bay Support"
+	depends on THINKPAD_ACPI
+	default y
+	---help---
+	  Allows the thinkpad_acpi driver to handle removable bays.  It will
+	  eletrically disable the device in the bay, and also generate
+	  notifications when the bay lever is ejected or inserted.
+
+	  If you are not sure, say Y here.
+
 endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7793ccd..e325164 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -12,3 +12,4 @@
 obj-$(CONFIG_TIFM_7XX1)       	+= tifm_7xx1.o
 obj-$(CONFIG_SGI_IOC4)		+= ioc4.o
 obj-$(CONFIG_SONY_LAPTOP)	+= sony-laptop.o
+obj-$(CONFIG_THINKPAD_ACPI)	+= thinkpad_acpi.o
diff --git a/drivers/misc/asus-laptop.c b/drivers/misc/asus-laptop.c
index 4b23212..65c32a9 100644
--- a/drivers/misc/asus-laptop.c
+++ b/drivers/misc/asus-laptop.c
@@ -3,7 +3,7 @@
  *
  *
  *  Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
- *  Copyright (C) 2006 Corentin Chary
+ *  Copyright (C) 2006-2007 Corentin Chary
  *
  *  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
@@ -48,7 +48,7 @@
 #include <acpi/acpi_bus.h>
 #include <asm/uaccess.h>
 
-#define ASUS_LAPTOP_VERSION "0.40"
+#define ASUS_LAPTOP_VERSION "0.41"
 
 #define ASUS_HOTK_NAME          "Asus Laptop Support"
 #define ASUS_HOTK_CLASS         "hotkey"
@@ -81,7 +81,8 @@
 #define TLED_ON     0x08	//touchpad LED
 #define RLED_ON     0x10	//Record LED
 #define PLED_ON     0x20	//Phone LED
-#define LCD_ON      0x40	//LCD backlight
+#define GLED_ON     0x40	//Gaming LED
+#define LCD_ON      0x80	//LCD backlight
 
 #define ASUS_LOG    ASUS_HOTK_FILE ": "
 #define ASUS_ERR    KERN_ERR    ASUS_LOG
@@ -94,6 +95,19 @@
 MODULE_DESCRIPTION(ASUS_HOTK_NAME);
 MODULE_LICENSE("GPL");
 
+/* WAPF defines the behavior of the Fn+Fx wlan key
+ * The significance of values is yet to be found, but
+ * most of the time:
+ * 0x0 will do nothing
+ * 0x1 will allow to control the device with Fn+Fx key.
+ * 0x4 will send an ACPI event (0x88) while pressing the Fn+Fx key
+ * 0x5 like 0x1 or 0x4
+ * So, if something doesn't work as you want, just try other values =)
+ */
+static uint wapf = 1;
+module_param(wapf, uint, 0644);
+MODULE_PARM_DESC(wapf, "WAPF value");
+
 #define ASUS_HANDLE(object, paths...)					\
 	static acpi_handle  object##_handle = NULL;			\
 	static char *object##_paths[] = { paths }
@@ -103,6 +117,7 @@
 ASUS_HANDLE(tled_set, ASUS_HOTK_PREFIX "TLED");
 ASUS_HANDLE(rled_set, ASUS_HOTK_PREFIX "RLED");	/* W1JC */
 ASUS_HANDLE(pled_set, ASUS_HOTK_PREFIX "PLED");	/* A7J */
+ASUS_HANDLE(gled_set, ASUS_HOTK_PREFIX "GLED");	/* G1, G2 (probably) */
 
 /* LEDD */
 ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM");
@@ -221,6 +236,7 @@
 ASUS_LED(tled, "touchpad");
 ASUS_LED(rled, "record");
 ASUS_LED(pled, "phone");
+ASUS_LED(gled, "gaming");
 
 /*
  * This function evaluates an ACPI method, given an int as parameter, the
@@ -245,32 +261,19 @@
 	return (status == AE_OK);
 }
 
-static int read_acpi_int(acpi_handle handle, const char *method, int *val,
-			 struct acpi_object_list *params)
-{
-	struct acpi_buffer output;
-	union acpi_object out_obj;
-	acpi_status status;
-
-	output.length = sizeof(out_obj);
-	output.pointer = &out_obj;
-
-	status = acpi_evaluate_object(handle, (char *)method, params, &output);
-	*val = out_obj.integer.value;
-	return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER);
-}
-
 static int read_wireless_status(int mask)
 {
-	int status;
+	ulong status;
+	acpi_status rv = AE_OK;
 
 	if (!wireless_status_handle)
 		return (hotk->status & mask) ? 1 : 0;
 
-	if (read_acpi_int(wireless_status_handle, NULL, &status, NULL)) {
-		return (status & mask) ? 1 : 0;
-	} else
+	rv = acpi_evaluate_integer(wireless_status_handle, NULL, NULL, &status);
+	if (ACPI_FAILURE(rv))
 		printk(ASUS_WARNING "Error reading Wireless status\n");
+	else
+		return (status & mask) ? 1 : 0;
 
 	return (hotk->status & mask) ? 1 : 0;
 }
@@ -285,19 +288,28 @@
 	return (hotk->status & mask) ? 1 : 0;
 }
 
-static void write_status(acpi_handle handle, int out, int mask, int invert)
+static void write_status(acpi_handle handle, int out, int mask)
 {
 	hotk->status = (out) ? (hotk->status | mask) : (hotk->status & ~mask);
 
-	if (invert)		/* invert target value */
+	switch (mask) {
+	case MLED_ON:
 		out = !out & 0x1;
+		break;
+	case GLED_ON:
+		out = (out & 0x1) + 1;
+		break;
+	default:
+		out &= 0x1;
+		break;
+	}
 
 	if (handle && !write_acpi_int(handle, NULL, out, NULL))
-		printk(ASUS_WARNING " write failed\n");
+		printk(ASUS_WARNING " write failed %x\n", mask);
 }
 
 /* /sys/class/led handlers */
-#define ASUS_LED_HANDLER(object, mask, invert)				\
+#define ASUS_LED_HANDLER(object, mask)					\
 	static void object##_led_set(struct led_classdev *led_cdev,	\
 				     enum led_brightness value)		\
 	{								\
@@ -307,13 +319,14 @@
 	static void object##_led_update(struct work_struct *ignored)	\
 	{								\
 		int value = object##_led_wk;				\
-		write_status(object##_set_handle, value, (mask), (invert)); \
+		write_status(object##_set_handle, value, (mask));	\
 	}
 
-ASUS_LED_HANDLER(mled, MLED_ON, 1);
-ASUS_LED_HANDLER(pled, PLED_ON, 0);
-ASUS_LED_HANDLER(rled, RLED_ON, 0);
-ASUS_LED_HANDLER(tled, TLED_ON, 0);
+ASUS_LED_HANDLER(mled, MLED_ON);
+ASUS_LED_HANDLER(pled, PLED_ON);
+ASUS_LED_HANDLER(rled, RLED_ON);
+ASUS_LED_HANDLER(tled, TLED_ON);
+ASUS_LED_HANDLER(gled, GLED_ON);
 
 static int get_lcd_state(void)
 {
@@ -338,7 +351,7 @@
 			printk(ASUS_WARNING "Error switching LCD\n");
 	}
 
-	write_status(NULL, lcd, LCD_ON, 0);
+	write_status(NULL, lcd, LCD_ON);
 	return 0;
 }
 
@@ -354,9 +367,11 @@
 
 static int read_brightness(struct backlight_device *bd)
 {
-	int value;
+	ulong value;
+	acpi_status rv = AE_OK;
 
-	if (!read_acpi_int(brightness_get_handle, NULL, &value, NULL))
+	rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value);
+	if (ACPI_FAILURE(rv))
 		printk(ASUS_WARNING "Error reading brightness\n");
 
 	return value;
@@ -403,8 +418,10 @@
 			  struct device_attribute *attr, char *page)
 {
 	int len = 0;
-	int temp;
+	ulong temp;
 	char buf[16];		//enough for all info
+	acpi_status rv = AE_OK;
+
 	/*
 	 * We use the easy way, we don't care of off and count, so we don't set eof
 	 * to 1
@@ -418,9 +435,10 @@
 	 * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
 	 * The significance of others is yet to be found.
 	 */
-	if (read_acpi_int(hotk->handle, "SFUN", &temp, NULL))
-		len +=
-		    sprintf(page + len, "SFUN value         : 0x%04x\n", temp);
+	rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp);
+	if (!ACPI_FAILURE(rv))
+		len += sprintf(page + len, "SFUN value         : 0x%04x\n",
+			       (uint) temp);
 	/*
 	 * Another value for userspace: the ASYM method returns 0x02 for
 	 * battery low and 0x04 for battery critical, its readings tend to be
@@ -428,9 +446,10 @@
 	 * Note: since not all the laptops provide this method, errors are
 	 * silently ignored.
 	 */
-	if (read_acpi_int(hotk->handle, "ASYM", &temp, NULL))
-		len +=
-		    sprintf(page + len, "ASYM value         : 0x%04x\n", temp);
+	rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp);
+	if (!ACPI_FAILURE(rv))
+		len += sprintf(page + len, "ASYM value         : 0x%04x\n",
+			       (uint) temp);
 	if (asus_info) {
 		snprintf(buf, 16, "%d", asus_info->length);
 		len += sprintf(page + len, "DSDT length        : %s\n", buf);
@@ -465,7 +484,7 @@
 }
 
 static ssize_t store_status(const char *buf, size_t count,
-			    acpi_handle handle, int mask, int invert)
+			    acpi_handle handle, int mask)
 {
 	int rv, value;
 	int out = 0;
@@ -474,7 +493,7 @@
 	if (rv > 0)
 		out = value ? 1 : 0;
 
-	write_status(handle, out, mask, invert);
+	write_status(handle, out, mask);
 
 	return rv;
 }
@@ -515,7 +534,7 @@
 static ssize_t store_wlan(struct device *dev, struct device_attribute *attr,
 			  const char *buf, size_t count)
 {
-	return store_status(buf, count, wl_switch_handle, WL_ON, 0);
+	return store_status(buf, count, wl_switch_handle, WL_ON);
 }
 
 /*
@@ -531,7 +550,7 @@
 			       struct device_attribute *attr, const char *buf,
 			       size_t count)
 {
-	return store_status(buf, count, bt_switch_handle, BT_ON, 0);
+	return store_status(buf, count, bt_switch_handle, BT_ON);
 }
 
 /*
@@ -547,12 +566,15 @@
 
 static int read_display(void)
 {
-	int value = 0;
+	ulong value = 0;
+	acpi_status rv = AE_OK;
 
 	/* In most of the case, we know how to set the display, but sometime
 	   we can't read it */
 	if (display_get_handle) {
-		if (!read_acpi_int(display_get_handle, NULL, &value, NULL))
+		rv = acpi_evaluate_integer(display_get_handle, NULL,
+					   NULL, &value);
+		if (ACPI_FAILURE(rv))
 			printk(ASUS_WARNING "Error reading display status\n");
 	}
 
@@ -656,10 +678,10 @@
 	 * switched
 	 */
 	if (event == ATKD_LCD_ON) {
-		write_status(NULL, 1, LCD_ON, 0);
+		write_status(NULL, 1, LCD_ON);
 		lcd_blank(FB_BLANK_UNBLANK);
 	} else if (event == ATKD_LCD_OFF) {
-		write_status(NULL, 0, LCD_ON, 0);
+		write_status(NULL, 0, LCD_ON);
 		lcd_blank(FB_BLANK_POWERDOWN);
 	}
 
@@ -771,7 +793,7 @@
 {
 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 	union acpi_object *model = NULL;
-	int bsts_result, hwrs_result;
+	ulong bsts_result, hwrs_result;
 	char *string = NULL;
 	acpi_status status;
 
@@ -794,11 +816,16 @@
 	}
 
 	/* This needs to be called for some laptops to init properly */
-	if (!read_acpi_int(hotk->handle, "BSTS", &bsts_result, NULL))
+	status =
+	    acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result);
+	if (ACPI_FAILURE(status))
 		printk(ASUS_WARNING "Error calling BSTS\n");
 	else if (bsts_result)
 		printk(ASUS_NOTICE "BSTS called, 0x%02x returned\n",
-		       bsts_result);
+		       (uint) bsts_result);
+
+	/* This too ... */
+	write_acpi_int(hotk->handle, "CWAP", wapf, NULL);
 
 	/*
 	 * Try to match the object returned by INIT to the specific model.
@@ -831,6 +858,7 @@
 	ASUS_HANDLE_INIT(tled_set);
 	ASUS_HANDLE_INIT(rled_set);
 	ASUS_HANDLE_INIT(pled_set);
+	ASUS_HANDLE_INIT(gled_set);
 
 	ASUS_HANDLE_INIT(ledd_set);
 
@@ -840,7 +868,9 @@
 	 * The significance of others is yet to be found.
 	 * If we don't find the method, we assume the device are present.
 	 */
-	if (!read_acpi_int(hotk->handle, "HRWS", &hwrs_result, NULL))
+	status =
+	    acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &hwrs_result);
+	if (ACPI_FAILURE(status))
 		hwrs_result = WL_HWRS | BT_HWRS;
 
 	if (hwrs_result & WL_HWRS)
@@ -928,11 +958,15 @@
 	asus_hotk_found = 1;
 
 	/* WLED and BLED are on by default */
-	write_status(bt_switch_handle, 1, BT_ON, 0);
-	write_status(wl_switch_handle, 1, WL_ON, 0);
+	write_status(bt_switch_handle, 1, BT_ON);
+	write_status(wl_switch_handle, 1, WL_ON);
+
+	/* If the h/w switch is off, we need to check the real status */
+	write_status(NULL, read_status(BT_ON), BT_ON);
+	write_status(NULL, read_status(WL_ON), WL_ON);
 
 	/* LCD Backlight is on by default */
-	write_status(NULL, 1, LCD_ON, 0);
+	write_status(NULL, 1, LCD_ON);
 
 	/* LED display is off by default */
 	hotk->ledd_status = 0xFFF;
@@ -991,6 +1025,7 @@
 	ASUS_LED_UNREGISTER(tled);
 	ASUS_LED_UNREGISTER(pled);
 	ASUS_LED_UNREGISTER(rled);
+	ASUS_LED_UNREGISTER(gled);
 
 	destroy_workqueue(led_workqueue);
 }
@@ -1062,6 +1097,10 @@
 	if (rv)
 		return rv;
 
+	rv = ASUS_LED_REGISTER(gled, dev);
+	if (rv)
+		return rv;
+
 	led_workqueue = create_singlethread_workqueue("led_workqueue");
 	if (!led_workqueue)
 		return -ENOMEM;
diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c
index ac708bc..c15c1f6 100644
--- a/drivers/misc/sony-laptop.c
+++ b/drivers/misc/sony-laptop.c
@@ -1,5 +1,5 @@
 /*
- * ACPI Sony Notebook Control Driver (SNC)
+ * ACPI Sony Notebook Control Driver (SNC and SPIC)
  *
  * Copyright (C) 2004-2005 Stelian Pop <stelian@popies.net>
  * Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
@@ -7,6 +7,25 @@
  * Parts of this driver inspired from asus_acpi.c and ibm_acpi.c
  * which are copyrighted by their respective authors.
  *
+ * The SNY6001 driver part is based on the sonypi driver which includes
+ * material from:
+ *
+ * Copyright (C) 2001-2005 Stelian Pop <stelian@popies.net>
+ *
+ * Copyright (C) 2005 Narayanan R S <nars@kadamba.org>
+ *
+ * Copyright (C) 2001-2002 Alcôve <www.alcove.com>
+ *
+ * Copyright (C) 2001 Michael Ashley <m.ashley@unsw.edu.au>
+ *
+ * Copyright (C) 2001 Junichi Morita <jun1m@mars.dti.ne.jp>
+ *
+ * Copyright (C) 2000 Takaya Kinjo <t-kinjo@tc4.so-net.ne.jp>
+ *
+ * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com>
+ *
+ * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.
+ *
  * 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,40 +50,404 @@
 #include <linux/backlight.h>
 #include <linux/platform_device.h>
 #include <linux/err.h>
+#include <linux/dmi.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/kfifo.h>
+#include <linux/workqueue.h>
+#include <linux/acpi.h>
 #include <acpi/acpi_drivers.h>
 #include <acpi/acpi_bus.h>
 #include <asm/uaccess.h>
+#include <linux/sonypi.h>
+#include <linux/sony-laptop.h>
+#ifdef CONFIG_SONY_LAPTOP_OLD
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#endif
 
-#define ACPI_SNC_CLASS		"sony"
-#define ACPI_SNC_HID		"SNY5001"
-#define ACPI_SNC_DRIVER_NAME	"ACPI Sony Notebook Control Driver v0.4"
+#define DRV_PFX			"sony-laptop: "
+#define dprintk(msg...)		do {			\
+	if (debug) printk(KERN_WARNING DRV_PFX  msg);	\
+} while (0)
 
-/* the device uses 1-based values, while the backlight subsystem uses
-   0-based values */
-#define SONY_MAX_BRIGHTNESS	8
+#define SONY_LAPTOP_DRIVER_VERSION	"0.5"
 
-#define LOG_PFX			KERN_WARNING "sony-laptop: "
+#define SONY_NC_CLASS		"sony-nc"
+#define SONY_NC_HID		"SNY5001"
+#define SONY_NC_DRIVER_NAME	"Sony Notebook Control Driver"
+
+#define SONY_PIC_CLASS		"sony-pic"
+#define SONY_PIC_HID		"SNY6001"
+#define SONY_PIC_DRIVER_NAME	"Sony Programmable IO Control Driver"
 
 MODULE_AUTHOR("Stelian Pop, Mattia Dongili");
-MODULE_DESCRIPTION(ACPI_SNC_DRIVER_NAME);
+MODULE_DESCRIPTION("Sony laptop extras driver (SPIC and SNC ACPI device)");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(SONY_LAPTOP_DRIVER_VERSION);
 
 static int debug;
 module_param(debug, int, 0);
 MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help "
 		 "the development of this driver");
 
-static ssize_t sony_acpi_show(struct device *, struct device_attribute *,
-			      char *);
-static ssize_t sony_acpi_store(struct device *, struct device_attribute *,
-			       const char *, size_t);
-static int boolean_validate(const int, const int);
-static int brightness_default_validate(const int, const int);
+static int no_spic;		/* = 0 */
+module_param(no_spic, int, 0444);
+MODULE_PARM_DESC(no_spic,
+		 "set this if you don't want to enable the SPIC device");
+
+static int compat;		/* = 0 */
+module_param(compat, int, 0444);
+MODULE_PARM_DESC(compat,
+		 "set this if you want to enable backward compatibility mode");
+
+static unsigned long mask = 0xffffffff;
+module_param(mask, ulong, 0644);
+MODULE_PARM_DESC(mask,
+		 "set this to the mask of event you want to enable (see doc)");
+
+static int camera;		/* = 0 */
+module_param(camera, int, 0444);
+MODULE_PARM_DESC(camera,
+		 "set this to 1 to enable Motion Eye camera controls "
+		 "(only use it if you have a C1VE or C1VN model)");
+
+#ifdef CONFIG_SONY_LAPTOP_OLD
+static int minor = -1;
+module_param(minor, int, 0);
+MODULE_PARM_DESC(minor,
+		 "minor number of the misc device for the SPIC compatibility code, "
+		 "default is -1 (automatic)");
+#endif
+
+/*********** Input Devices ***********/
+
+#define SONY_LAPTOP_BUF_SIZE	128
+struct sony_laptop_input_s {
+	atomic_t		users;
+	struct input_dev	*jog_dev;
+	struct input_dev	*key_dev;
+	struct kfifo		*fifo;
+	spinlock_t		fifo_lock;
+	struct workqueue_struct	*wq;
+};
+static struct sony_laptop_input_s sony_laptop_input = {
+	.users = ATOMIC_INIT(0),
+};
+
+struct sony_laptop_keypress {
+	struct input_dev *dev;
+	int key;
+};
+
+/* Correspondance table between sonypi events and input layer events */
+static struct {
+	int sonypiev;
+	int inputev;
+} sony_laptop_inputkeys[] = {
+	{ SONYPI_EVENT_CAPTURE_PRESSED,	 	KEY_CAMERA },
+	{ SONYPI_EVENT_FNKEY_ONLY, 		KEY_FN },
+	{ SONYPI_EVENT_FNKEY_ESC, 		KEY_FN_ESC },
+	{ SONYPI_EVENT_FNKEY_F1, 		KEY_FN_F1 },
+	{ SONYPI_EVENT_FNKEY_F2, 		KEY_FN_F2 },
+	{ SONYPI_EVENT_FNKEY_F3, 		KEY_FN_F3 },
+	{ SONYPI_EVENT_FNKEY_F4, 		KEY_FN_F4 },
+	{ SONYPI_EVENT_FNKEY_F5, 		KEY_FN_F5 },
+	{ SONYPI_EVENT_FNKEY_F6, 		KEY_FN_F6 },
+	{ SONYPI_EVENT_FNKEY_F7, 		KEY_FN_F7 },
+	{ SONYPI_EVENT_FNKEY_F8, 		KEY_FN_F8 },
+	{ SONYPI_EVENT_FNKEY_F9,		KEY_FN_F9 },
+	{ SONYPI_EVENT_FNKEY_F10,		KEY_FN_F10 },
+	{ SONYPI_EVENT_FNKEY_F11, 		KEY_FN_F11 },
+	{ SONYPI_EVENT_FNKEY_F12,		KEY_FN_F12 },
+	{ SONYPI_EVENT_FNKEY_1, 		KEY_FN_1 },
+	{ SONYPI_EVENT_FNKEY_2, 		KEY_FN_2 },
+	{ SONYPI_EVENT_FNKEY_D,			KEY_FN_D },
+	{ SONYPI_EVENT_FNKEY_E,			KEY_FN_E },
+	{ SONYPI_EVENT_FNKEY_F,			KEY_FN_F },
+	{ SONYPI_EVENT_FNKEY_S,			KEY_FN_S },
+	{ SONYPI_EVENT_FNKEY_B,			KEY_FN_B },
+	{ SONYPI_EVENT_BLUETOOTH_PRESSED, 	KEY_BLUE },
+	{ SONYPI_EVENT_BLUETOOTH_ON, 		KEY_BLUE },
+	{ SONYPI_EVENT_PKEY_P1, 		KEY_PROG1 },
+	{ SONYPI_EVENT_PKEY_P2, 		KEY_PROG2 },
+	{ SONYPI_EVENT_PKEY_P3, 		KEY_PROG3 },
+	{ SONYPI_EVENT_BACK_PRESSED, 		KEY_BACK },
+	{ SONYPI_EVENT_HELP_PRESSED, 		KEY_HELP },
+	{ SONYPI_EVENT_ZOOM_PRESSED, 		KEY_ZOOM },
+	{ SONYPI_EVENT_THUMBPHRASE_PRESSED, 	BTN_THUMB },
+	{ 0, 0 },
+};
+
+/* release buttons after a short delay if pressed */
+static void do_sony_laptop_release_key(struct work_struct *work)
+{
+	struct sony_laptop_keypress kp;
+
+	while (kfifo_get(sony_laptop_input.fifo, (unsigned char *)&kp,
+			 sizeof(kp)) == sizeof(kp)) {
+		msleep(10);
+		input_report_key(kp.dev, kp.key, 0);
+		input_sync(kp.dev);
+	}
+}
+static DECLARE_WORK(sony_laptop_release_key_work,
+		do_sony_laptop_release_key);
+
+/* forward event to the input subsytem */
+static void sony_laptop_report_input_event(u8 event)
+{
+	struct input_dev *jog_dev = sony_laptop_input.jog_dev;
+	struct input_dev *key_dev = sony_laptop_input.key_dev;
+	struct sony_laptop_keypress kp = { NULL };
+	int i;
+
+	if (event == SONYPI_EVENT_FNKEY_RELEASED) {
+		/* Nothing, not all VAIOs generate this event */
+		return;
+	}
+
+	/* report events */
+	switch (event) {
+	/* jog_dev events */
+	case SONYPI_EVENT_JOGDIAL_UP:
+	case SONYPI_EVENT_JOGDIAL_UP_PRESSED:
+		input_report_rel(jog_dev, REL_WHEEL, 1);
+		input_sync(jog_dev);
+		return;
+
+	case SONYPI_EVENT_JOGDIAL_DOWN:
+	case SONYPI_EVENT_JOGDIAL_DOWN_PRESSED:
+		input_report_rel(jog_dev, REL_WHEEL, -1);
+		input_sync(jog_dev);
+		return;
+
+	/* key_dev events */
+	case SONYPI_EVENT_JOGDIAL_PRESSED:
+		kp.key = BTN_MIDDLE;
+		kp.dev = jog_dev;
+		break;
+
+	default:
+		for (i = 0; sony_laptop_inputkeys[i].sonypiev; i++)
+			if (event == sony_laptop_inputkeys[i].sonypiev) {
+				kp.dev = key_dev;
+				kp.key = sony_laptop_inputkeys[i].inputev;
+				break;
+			}
+		break;
+	}
+
+	if (kp.dev) {
+		input_report_key(kp.dev, kp.key, 1);
+		input_sync(kp.dev);
+		kfifo_put(sony_laptop_input.fifo,
+			  (unsigned char *)&kp, sizeof(kp));
+
+		if (!work_pending(&sony_laptop_release_key_work))
+			queue_work(sony_laptop_input.wq,
+					&sony_laptop_release_key_work);
+	} else
+		dprintk("unknown input event %.2x\n", event);
+}
+
+static int sony_laptop_setup_input(void)
+{
+	struct input_dev *jog_dev;
+	struct input_dev *key_dev;
+	int i;
+	int error;
+
+	/* don't run again if already initialized */
+	if (atomic_add_return(1, &sony_laptop_input.users) > 1)
+		return 0;
+
+	/* kfifo */
+	spin_lock_init(&sony_laptop_input.fifo_lock);
+	sony_laptop_input.fifo =
+		kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL,
+			    &sony_laptop_input.fifo_lock);
+	if (IS_ERR(sony_laptop_input.fifo)) {
+		printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
+		error = PTR_ERR(sony_laptop_input.fifo);
+		goto err_dec_users;
+	}
+
+	/* init workqueue */
+	sony_laptop_input.wq = create_singlethread_workqueue("sony-laptop");
+	if (!sony_laptop_input.wq) {
+		printk(KERN_ERR DRV_PFX
+				"Unabe to create workqueue.\n");
+		error = -ENXIO;
+		goto err_free_kfifo;
+	}
+
+	/* input keys */
+	key_dev = input_allocate_device();
+	if (!key_dev) {
+		error = -ENOMEM;
+		goto err_destroy_wq;
+	}
+
+	key_dev->name = "Sony Vaio Keys";
+	key_dev->id.bustype = BUS_ISA;
+	key_dev->id.vendor = PCI_VENDOR_ID_SONY;
+
+	/* Initialize the Input Drivers: special keys */
+	key_dev->evbit[0] = BIT(EV_KEY);
+	for (i = 0; sony_laptop_inputkeys[i].sonypiev; i++)
+		if (sony_laptop_inputkeys[i].inputev)
+			set_bit(sony_laptop_inputkeys[i].inputev,
+					key_dev->keybit);
+
+	error = input_register_device(key_dev);
+	if (error)
+		goto err_free_keydev;
+
+	sony_laptop_input.key_dev = key_dev;
+
+	/* jogdial */
+	jog_dev = input_allocate_device();
+	if (!jog_dev) {
+		error = -ENOMEM;
+		goto err_unregister_keydev;
+	}
+
+	jog_dev->name = "Sony Vaio Jogdial";
+	jog_dev->id.bustype = BUS_ISA;
+	jog_dev->id.vendor = PCI_VENDOR_ID_SONY;
+
+	jog_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+	jog_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_MIDDLE);
+	jog_dev->relbit[0] = BIT(REL_WHEEL);
+
+	error = input_register_device(jog_dev);
+	if (error)
+		goto err_free_jogdev;
+
+	sony_laptop_input.jog_dev = jog_dev;
+
+	return 0;
+
+err_free_jogdev:
+	input_free_device(jog_dev);
+
+err_unregister_keydev:
+	input_unregister_device(key_dev);
+	/* to avoid kref underflow below at input_free_device */
+	key_dev = NULL;
+
+err_free_keydev:
+	input_free_device(key_dev);
+
+err_destroy_wq:
+	destroy_workqueue(sony_laptop_input.wq);
+
+err_free_kfifo:
+	kfifo_free(sony_laptop_input.fifo);
+
+err_dec_users:
+	atomic_dec(&sony_laptop_input.users);
+	return error;
+}
+
+static void sony_laptop_remove_input(void)
+{
+	/* cleanup only after the last user has gone */
+	if (!atomic_dec_and_test(&sony_laptop_input.users))
+		return;
+
+	/* flush workqueue first */
+	flush_workqueue(sony_laptop_input.wq);
+
+	/* destroy input devs */
+	input_unregister_device(sony_laptop_input.key_dev);
+	sony_laptop_input.key_dev = NULL;
+
+	if (sony_laptop_input.jog_dev) {
+		input_unregister_device(sony_laptop_input.jog_dev);
+		sony_laptop_input.jog_dev = NULL;
+	}
+
+	destroy_workqueue(sony_laptop_input.wq);
+	kfifo_free(sony_laptop_input.fifo);
+}
+
+/*********** Platform Device ***********/
+
+static atomic_t sony_pf_users = ATOMIC_INIT(0);
+static struct platform_driver sony_pf_driver = {
+	.driver = {
+		   .name = "sony-laptop",
+		   .owner = THIS_MODULE,
+		   }
+};
+static struct platform_device *sony_pf_device;
+
+static int sony_pf_add(void)
+{
+	int ret = 0;
+
+	/* don't run again if already initialized */
+	if (atomic_add_return(1, &sony_pf_users) > 1)
+		return 0;
+
+	ret = platform_driver_register(&sony_pf_driver);
+	if (ret)
+		goto out;
+
+	sony_pf_device = platform_device_alloc("sony-laptop", -1);
+	if (!sony_pf_device) {
+		ret = -ENOMEM;
+		goto out_platform_registered;
+	}
+
+	ret = platform_device_add(sony_pf_device);
+	if (ret)
+		goto out_platform_alloced;
+
+	return 0;
+
+      out_platform_alloced:
+	platform_device_put(sony_pf_device);
+	sony_pf_device = NULL;
+      out_platform_registered:
+	platform_driver_unregister(&sony_pf_driver);
+      out:
+	atomic_dec(&sony_pf_users);
+	return ret;
+}
+
+static void sony_pf_remove(void)
+{
+	/* deregister only after the last user has gone */
+	if (!atomic_dec_and_test(&sony_pf_users))
+		return;
+
+	platform_device_del(sony_pf_device);
+	platform_device_put(sony_pf_device);
+	platform_driver_unregister(&sony_pf_driver);
+}
+
+/*********** SNC (SNY5001) Device ***********/
+
+/* the device uses 1-based values, while the backlight subsystem uses
+   0-based values */
+#define SONY_MAX_BRIGHTNESS	8
 
 #define SNC_VALIDATE_IN		0
 #define SNC_VALIDATE_OUT	1
 
-struct sony_acpi_value {
+static ssize_t sony_nc_sysfs_show(struct device *, struct device_attribute *,
+			      char *);
+static ssize_t sony_nc_sysfs_store(struct device *, struct device_attribute *,
+			       const char *, size_t);
+static int boolean_validate(const int, const int);
+static int brightness_default_validate(const int, const int);
+
+struct sony_nc_value {
 	char *name;		/* name of the entry */
 	char **acpiget;		/* names of the ACPI get function */
 	char **acpiset;		/* names of the ACPI set function */
@@ -75,65 +458,65 @@
 	struct device_attribute devattr;	/* sysfs atribute */
 };
 
-#define HANDLE_NAMES(_name, _values...) \
+#define SNC_HANDLE_NAMES(_name, _values...) \
 	static char *snc_##_name[] = { _values, NULL }
 
-#define SONY_ACPI_VALUE(_name, _getters, _setters, _validate, _debug) \
+#define SNC_HANDLE(_name, _getters, _setters, _validate, _debug) \
 	{ \
 		.name		= __stringify(_name), \
 		.acpiget	= _getters, \
 		.acpiset	= _setters, \
 		.validate	= _validate, \
 		.debug		= _debug, \
-		.devattr	= __ATTR(_name, 0, sony_acpi_show, sony_acpi_store), \
+		.devattr	= __ATTR(_name, 0, sony_nc_sysfs_show, sony_nc_sysfs_store), \
 	}
 
-#define SONY_ACPI_VALUE_NULL	{ .name = NULL }
+#define SNC_HANDLE_NULL	{ .name = NULL }
 
-HANDLE_NAMES(fnkey_get, "GHKE");
+SNC_HANDLE_NAMES(fnkey_get, "GHKE");
 
-HANDLE_NAMES(brightness_def_get, "GPBR");
-HANDLE_NAMES(brightness_def_set, "SPBR");
+SNC_HANDLE_NAMES(brightness_def_get, "GPBR");
+SNC_HANDLE_NAMES(brightness_def_set, "SPBR");
 
-HANDLE_NAMES(cdpower_get, "GCDP");
-HANDLE_NAMES(cdpower_set, "SCDP", "CDPW");
+SNC_HANDLE_NAMES(cdpower_get, "GCDP");
+SNC_HANDLE_NAMES(cdpower_set, "SCDP", "CDPW");
 
-HANDLE_NAMES(audiopower_get, "GAZP");
-HANDLE_NAMES(audiopower_set, "AZPW");
+SNC_HANDLE_NAMES(audiopower_get, "GAZP");
+SNC_HANDLE_NAMES(audiopower_set, "AZPW");
 
-HANDLE_NAMES(lanpower_get, "GLNP");
-HANDLE_NAMES(lanpower_set, "LNPW");
+SNC_HANDLE_NAMES(lanpower_get, "GLNP");
+SNC_HANDLE_NAMES(lanpower_set, "LNPW");
 
-HANDLE_NAMES(PID_get, "GPID");
+SNC_HANDLE_NAMES(PID_get, "GPID");
 
-HANDLE_NAMES(CTR_get, "GCTR");
-HANDLE_NAMES(CTR_set, "SCTR");
+SNC_HANDLE_NAMES(CTR_get, "GCTR");
+SNC_HANDLE_NAMES(CTR_set, "SCTR");
 
-HANDLE_NAMES(PCR_get, "GPCR");
-HANDLE_NAMES(PCR_set, "SPCR");
+SNC_HANDLE_NAMES(PCR_get, "GPCR");
+SNC_HANDLE_NAMES(PCR_set, "SPCR");
 
-HANDLE_NAMES(CMI_get, "GCMI");
-HANDLE_NAMES(CMI_set, "SCMI");
+SNC_HANDLE_NAMES(CMI_get, "GCMI");
+SNC_HANDLE_NAMES(CMI_set, "SCMI");
 
-static struct sony_acpi_value sony_acpi_values[] = {
-	SONY_ACPI_VALUE(brightness_default, snc_brightness_def_get,
+static struct sony_nc_value sony_nc_values[] = {
+	SNC_HANDLE(brightness_default, snc_brightness_def_get,
 			snc_brightness_def_set, brightness_default_validate, 0),
-	SONY_ACPI_VALUE(fnkey, snc_fnkey_get, NULL, NULL, 0),
-	SONY_ACPI_VALUE(cdpower, snc_cdpower_get, snc_cdpower_set, boolean_validate, 0),
-	SONY_ACPI_VALUE(audiopower, snc_audiopower_get, snc_audiopower_set,
+	SNC_HANDLE(fnkey, snc_fnkey_get, NULL, NULL, 0),
+	SNC_HANDLE(cdpower, snc_cdpower_get, snc_cdpower_set, boolean_validate, 0),
+	SNC_HANDLE(audiopower, snc_audiopower_get, snc_audiopower_set,
 			boolean_validate, 0),
-	SONY_ACPI_VALUE(lanpower, snc_lanpower_get, snc_lanpower_set,
+	SNC_HANDLE(lanpower, snc_lanpower_get, snc_lanpower_set,
 			boolean_validate, 1),
 	/* unknown methods */
-	SONY_ACPI_VALUE(PID, snc_PID_get, NULL, NULL, 1),
-	SONY_ACPI_VALUE(CTR, snc_CTR_get, snc_CTR_set, NULL, 1),
-	SONY_ACPI_VALUE(PCR, snc_PCR_get, snc_PCR_set, NULL, 1),
-	SONY_ACPI_VALUE(CMI, snc_CMI_get, snc_CMI_set, NULL, 1),
-	SONY_ACPI_VALUE_NULL
+	SNC_HANDLE(PID, snc_PID_get, NULL, NULL, 1),
+	SNC_HANDLE(CTR, snc_CTR_get, snc_CTR_set, NULL, 1),
+	SNC_HANDLE(PCR, snc_PCR_get, snc_PCR_set, NULL, 1),
+	SNC_HANDLE(CMI, snc_CMI_get, snc_CMI_set, NULL, 1),
+	SNC_HANDLE_NULL
 };
 
-static acpi_handle sony_acpi_handle;
-static struct acpi_device *sony_acpi_acpi_device = NULL;
+static acpi_handle sony_nc_acpi_handle;
+static struct acpi_device *sony_nc_acpi_device = NULL;
 
 /*
  * acpi_evaluate_object wrappers
@@ -153,7 +536,7 @@
 		return 0;
 	}
 
-	printk(LOG_PFX "acpi_callreadfunc failed\n");
+	printk(KERN_WARNING DRV_PFX "acpi_callreadfunc failed\n");
 
 	return -1;
 }
@@ -179,7 +562,7 @@
 	if (status == AE_OK) {
 		if (result != NULL) {
 			if (out_obj.type != ACPI_TYPE_INTEGER) {
-				printk(LOG_PFX "acpi_evaluate_object bad "
+				printk(KERN_WARNING DRV_PFX "acpi_evaluate_object bad "
 				       "return type\n");
 				return -1;
 			}
@@ -188,13 +571,13 @@
 		return 0;
 	}
 
-	printk(LOG_PFX "acpi_evaluate_object failed\n");
+	printk(KERN_WARNING DRV_PFX "acpi_evaluate_object failed\n");
 
 	return -1;
 }
 
 /*
- * sony_acpi_values input/output validate functions
+ * sony_nc_values input/output validate functions
  */
 
 /* brightness_default_validate:
@@ -229,19 +612,19 @@
 }
 
 /*
- * Sysfs show/store common to all sony_acpi_values
+ * Sysfs show/store common to all sony_nc_values
  */
-static ssize_t sony_acpi_show(struct device *dev, struct device_attribute *attr,
+static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *attr,
 			      char *buffer)
 {
 	int value;
-	struct sony_acpi_value *item =
-	    container_of(attr, struct sony_acpi_value, devattr);
+	struct sony_nc_value *item =
+	    container_of(attr, struct sony_nc_value, devattr);
 
 	if (!*item->acpiget)
 		return -EIO;
 
-	if (acpi_callgetfunc(sony_acpi_handle, *item->acpiget, &value) < 0)
+	if (acpi_callgetfunc(sony_nc_acpi_handle, *item->acpiget, &value) < 0)
 		return -EIO;
 
 	if (item->validate)
@@ -250,13 +633,13 @@
 	return snprintf(buffer, PAGE_SIZE, "%d\n", value);
 }
 
-static ssize_t sony_acpi_store(struct device *dev,
+static ssize_t sony_nc_sysfs_store(struct device *dev,
 			       struct device_attribute *attr,
 			       const char *buffer, size_t count)
 {
 	int value;
-	struct sony_acpi_value *item =
-	    container_of(attr, struct sony_acpi_value, devattr);
+	struct sony_nc_value *item =
+	    container_of(attr, struct sony_nc_value, devattr);
 
 	if (!item->acpiset)
 		return -EIO;
@@ -272,118 +655,20 @@
 	if (value < 0)
 		return value;
 
-	if (acpi_callsetfunc(sony_acpi_handle, *item->acpiset, value, NULL) < 0)
+	if (acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, value, NULL) < 0)
 		return -EIO;
 	item->value = value;
 	item->valid = 1;
 	return count;
 }
 
-/*
- * Platform device
- */
-static struct platform_driver sncpf_driver = {
-	.driver = {
-		   .name = "sony-laptop",
-		   .owner = THIS_MODULE,
-		   }
-};
-static struct platform_device *sncpf_device;
-
-static int sony_snc_pf_add(void)
-{
-	acpi_handle handle;
-	struct sony_acpi_value *item;
-	int ret = 0;
-
-	ret = platform_driver_register(&sncpf_driver);
-	if (ret)
-		goto out;
-
-	sncpf_device = platform_device_alloc("sony-laptop", -1);
-	if (!sncpf_device) {
-		ret = -ENOMEM;
-		goto out_platform_registered;
-	}
-
-	ret = platform_device_add(sncpf_device);
-	if (ret)
-		goto out_platform_alloced;
-
-	for (item = sony_acpi_values; item->name; ++item) {
-
-		if (!debug && item->debug)
-			continue;
-
-		/* find the available acpiget as described in the DSDT */
-		for (; item->acpiget && *item->acpiget; ++item->acpiget) {
-			if (ACPI_SUCCESS(acpi_get_handle(sony_acpi_handle,
-							 *item->acpiget,
-							 &handle))) {
-				if (debug)
-					printk(LOG_PFX "Found %s getter: %s\n",
-					       item->name, *item->acpiget);
-				item->devattr.attr.mode |= S_IRUGO;
-				break;
-			}
-		}
-
-		/* find the available acpiset as described in the DSDT */
-		for (; item->acpiset && *item->acpiset; ++item->acpiset) {
-			if (ACPI_SUCCESS(acpi_get_handle(sony_acpi_handle,
-							 *item->acpiset,
-							 &handle))) {
-				if (debug)
-					printk(LOG_PFX "Found %s setter: %s\n",
-					       item->name, *item->acpiset);
-				item->devattr.attr.mode |= S_IWUSR;
-				break;
-			}
-		}
-
-		if (item->devattr.attr.mode != 0) {
-			ret =
-			    device_create_file(&sncpf_device->dev,
-					       &item->devattr);
-			if (ret)
-				goto out_sysfs;
-		}
-	}
-
-	return 0;
-
-      out_sysfs:
-	for (item = sony_acpi_values; item->name; ++item) {
-		device_remove_file(&sncpf_device->dev, &item->devattr);
-	}
-	platform_device_del(sncpf_device);
-      out_platform_alloced:
-	platform_device_put(sncpf_device);
-      out_platform_registered:
-	platform_driver_unregister(&sncpf_driver);
-      out:
-	return ret;
-}
-
-static void sony_snc_pf_remove(void)
-{
-	struct sony_acpi_value *item;
-
-	for (item = sony_acpi_values; item->name; ++item) {
-		device_remove_file(&sncpf_device->dev, &item->devattr);
-	}
-
-	platform_device_del(sncpf_device);
-	platform_device_put(sncpf_device);
-	platform_driver_unregister(&sncpf_driver);
-}
 
 /*
  * Backlight device
  */
 static int sony_backlight_update_status(struct backlight_device *bd)
 {
-	return acpi_callsetfunc(sony_acpi_handle, "SBRT",
+	return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT",
 				bd->props.brightness + 1, NULL);
 }
 
@@ -391,7 +676,7 @@
 {
 	int value;
 
-	if (acpi_callgetfunc(sony_acpi_handle, "GBRT", &value))
+	if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value))
 		return 0;
 	/* brightness levels are 1-based, while backlight ones are 0-based */
 	return value - 1;
@@ -408,9 +693,9 @@
  */
 static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
 {
-	if (debug)
-		printk(LOG_PFX "sony_acpi_notify, event: %d\n", event);
-	acpi_bus_generate_event(sony_acpi_acpi_device, 1, event);
+	dprintk("sony_acpi_notify, event: %d\n", event);
+	sony_laptop_report_input_event(event);
+	acpi_bus_generate_event(sony_nc_acpi_device, 1, event);
 }
 
 static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
@@ -422,7 +707,7 @@
 	node = (struct acpi_namespace_node *)handle;
 	operand = (union acpi_operand_object *)node->object;
 
-	printk(LOG_PFX "method: name: %4.4s, args %X\n", node->name.ascii,
+	printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n", node->name.ascii,
 	       (u32) operand->method.param_count);
 
 	return AE_OK;
@@ -431,16 +716,16 @@
 /*
  * ACPI device
  */
-static int sony_acpi_resume(struct acpi_device *device)
+static int sony_nc_resume(struct acpi_device *device)
 {
-	struct sony_acpi_value *item;
+	struct sony_nc_value *item;
 
-	for (item = sony_acpi_values; item->name; item++) {
+	for (item = sony_nc_values; item->name; item++) {
 		int ret;
 
 		if (!item->valid)
 			continue;
-		ret = acpi_callsetfunc(sony_acpi_handle, *item->acpiset,
+		ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset,
 				       item->value, NULL);
 		if (ret < 0) {
 			printk("%s: %d\n", __FUNCTION__, ret);
@@ -450,42 +735,55 @@
 	return 0;
 }
 
-static int sony_acpi_add(struct acpi_device *device)
+static int sony_nc_add(struct acpi_device *device)
 {
 	acpi_status status;
 	int result = 0;
 	acpi_handle handle;
+	struct sony_nc_value *item;
 
-	sony_acpi_acpi_device = device;
+	printk(KERN_INFO DRV_PFX "%s v%s.\n",
+		SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
 
-	sony_acpi_handle = device->handle;
+	sony_nc_acpi_device = device;
+	strcpy(acpi_device_class(device), "sony/hotkey");
+
+	sony_nc_acpi_handle = device->handle;
 
 	if (debug) {
-		status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_acpi_handle,
+		status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle,
 					     1, sony_walk_callback, NULL, NULL);
 		if (ACPI_FAILURE(status)) {
-			printk(LOG_PFX "unable to walk acpi resources\n");
+			printk(KERN_WARNING DRV_PFX "unable to walk acpi resources\n");
 			result = -ENODEV;
 			goto outwalk;
 		}
 	}
 
-	status = acpi_install_notify_handler(sony_acpi_handle,
-					     ACPI_DEVICE_NOTIFY,
-					     sony_acpi_notify, NULL);
-	if (ACPI_FAILURE(status)) {
-		printk(LOG_PFX "unable to install notify handler\n");
-		result = -ENODEV;
+	/* setup input devices and helper fifo */
+	result = sony_laptop_setup_input();
+	if (result) {
+		printk(KERN_ERR DRV_PFX
+				"Unabe to create input devices.\n");
 		goto outwalk;
 	}
 
-	if (ACPI_SUCCESS(acpi_get_handle(sony_acpi_handle, "GBRT", &handle))) {
+	status = acpi_install_notify_handler(sony_nc_acpi_handle,
+					     ACPI_DEVICE_NOTIFY,
+					     sony_acpi_notify, NULL);
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_WARNING DRV_PFX "unable to install notify handler\n");
+		result = -ENODEV;
+		goto outinput;
+	}
+
+	if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", &handle))) {
 		sony_backlight_device = backlight_device_register("sony", NULL,
 								  NULL,
 								  &sony_backlight_ops);
 
 		if (IS_ERR(sony_backlight_device)) {
-			printk(LOG_PFX "unable to register backlight device\n");
+			printk(KERN_WARNING DRV_PFX "unable to register backlight device\n");
 			sony_backlight_device = NULL;
 		} else {
 			sony_backlight_device->props.brightness =
@@ -497,68 +795,1497 @@
 
 	}
 
-	if (sony_snc_pf_add())
+	result = sony_pf_add();
+	if (result)
 		goto outbacklight;
 
-	printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully installed\n");
+	/* create sony_pf sysfs attributes related to the SNC device */
+	for (item = sony_nc_values; item->name; ++item) {
+
+		if (!debug && item->debug)
+			continue;
+
+		/* find the available acpiget as described in the DSDT */
+		for (; item->acpiget && *item->acpiget; ++item->acpiget) {
+			if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle,
+							 *item->acpiget,
+							 &handle))) {
+				dprintk("Found %s getter: %s\n",
+						item->name, *item->acpiget);
+				item->devattr.attr.mode |= S_IRUGO;
+				break;
+			}
+		}
+
+		/* find the available acpiset as described in the DSDT */
+		for (; item->acpiset && *item->acpiset; ++item->acpiset) {
+			if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle,
+							 *item->acpiset,
+							 &handle))) {
+				dprintk("Found %s setter: %s\n",
+						item->name, *item->acpiset);
+				item->devattr.attr.mode |= S_IWUSR;
+				break;
+			}
+		}
+
+		if (item->devattr.attr.mode != 0) {
+			result =
+			    device_create_file(&sony_pf_device->dev,
+					       &item->devattr);
+			if (result)
+				goto out_sysfs;
+		}
+	}
 
 	return 0;
 
+      out_sysfs:
+	for (item = sony_nc_values; item->name; ++item) {
+		device_remove_file(&sony_pf_device->dev, &item->devattr);
+	}
+	sony_pf_remove();
+
       outbacklight:
 	if (sony_backlight_device)
 		backlight_device_unregister(sony_backlight_device);
 
-	status = acpi_remove_notify_handler(sony_acpi_handle,
+	status = acpi_remove_notify_handler(sony_nc_acpi_handle,
 					    ACPI_DEVICE_NOTIFY,
 					    sony_acpi_notify);
 	if (ACPI_FAILURE(status))
-		printk(LOG_PFX "unable to remove notify handler\n");
+		printk(KERN_WARNING DRV_PFX "unable to remove notify handler\n");
+
+      outinput:
+	sony_laptop_remove_input();
+
       outwalk:
 	return result;
 }
 
-static int sony_acpi_remove(struct acpi_device *device, int type)
+static int sony_nc_remove(struct acpi_device *device, int type)
 {
 	acpi_status status;
+	struct sony_nc_value *item;
 
 	if (sony_backlight_device)
 		backlight_device_unregister(sony_backlight_device);
 
-	sony_acpi_acpi_device = NULL;
+	sony_nc_acpi_device = NULL;
 
-	status = acpi_remove_notify_handler(sony_acpi_handle,
+	status = acpi_remove_notify_handler(sony_nc_acpi_handle,
 					    ACPI_DEVICE_NOTIFY,
 					    sony_acpi_notify);
 	if (ACPI_FAILURE(status))
-		printk(LOG_PFX "unable to remove notify handler\n");
+		printk(KERN_WARNING DRV_PFX "unable to remove notify handler\n");
 
-	sony_snc_pf_remove();
+	for (item = sony_nc_values; item->name; ++item) {
+		device_remove_file(&sony_pf_device->dev, &item->devattr);
+	}
 
-	printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully removed\n");
+	sony_pf_remove();
+	sony_laptop_remove_input();
+	dprintk(SONY_NC_DRIVER_NAME " removed.\n");
 
 	return 0;
 }
 
-static struct acpi_driver sony_acpi_driver = {
-	.name = ACPI_SNC_DRIVER_NAME,
-	.class = ACPI_SNC_CLASS,
-	.ids = ACPI_SNC_HID,
+static struct acpi_driver sony_nc_driver = {
+	.name = SONY_NC_DRIVER_NAME,
+	.class = SONY_NC_CLASS,
+	.ids = SONY_NC_HID,
+	.owner = THIS_MODULE,
 	.ops = {
-		.add = sony_acpi_add,
-		.remove = sony_acpi_remove,
-		.resume = sony_acpi_resume,
+		.add = sony_nc_add,
+		.remove = sony_nc_remove,
+		.resume = sony_nc_resume,
 		},
 };
 
-static int __init sony_acpi_init(void)
+/*********** SPIC (SNY6001) Device ***********/
+
+#define SONYPI_DEVICE_TYPE1	0x00000001
+#define SONYPI_DEVICE_TYPE2	0x00000002
+#define SONYPI_DEVICE_TYPE3	0x00000004
+
+#define SONY_PIC_EV_MASK	0xff
+
+struct sony_pic_ioport {
+	struct acpi_resource_io	io;
+	struct list_head	list;
+};
+
+struct sony_pic_irq {
+	struct acpi_resource_irq	irq;
+	struct list_head		list;
+};
+
+struct sony_pic_dev {
+	int			model;
+	u8			camera_power;
+	u8			bluetooth_power;
+	u8			wwan_power;
+	struct acpi_device	*acpi_dev;
+	struct sony_pic_irq	*cur_irq;
+	struct sony_pic_ioport	*cur_ioport;
+	struct list_head	interrupts;
+	struct list_head	ioports;
+	struct mutex		lock;
+};
+
+static struct sony_pic_dev spic_dev = {
+	.interrupts	= LIST_HEAD_INIT(spic_dev.interrupts),
+	.ioports	= LIST_HEAD_INIT(spic_dev.ioports),
+};
+
+/* Event masks */
+#define SONYPI_JOGGER_MASK			0x00000001
+#define SONYPI_CAPTURE_MASK			0x00000002
+#define SONYPI_FNKEY_MASK			0x00000004
+#define SONYPI_BLUETOOTH_MASK			0x00000008
+#define SONYPI_PKEY_MASK			0x00000010
+#define SONYPI_BACK_MASK			0x00000020
+#define SONYPI_HELP_MASK			0x00000040
+#define SONYPI_LID_MASK				0x00000080
+#define SONYPI_ZOOM_MASK			0x00000100
+#define SONYPI_THUMBPHRASE_MASK			0x00000200
+#define SONYPI_MEYE_MASK			0x00000400
+#define SONYPI_MEMORYSTICK_MASK			0x00000800
+#define SONYPI_BATTERY_MASK			0x00001000
+#define SONYPI_WIRELESS_MASK			0x00002000
+
+struct sonypi_event {
+	u8	data;
+	u8	event;
+};
+
+/* The set of possible button release events */
+static struct sonypi_event sonypi_releaseev[] = {
+	{ 0x00, SONYPI_EVENT_ANYBUTTON_RELEASED },
+	{ 0, 0 }
+};
+
+/* The set of possible jogger events  */
+static struct sonypi_event sonypi_joggerev[] = {
+	{ 0x1f, SONYPI_EVENT_JOGDIAL_UP },
+	{ 0x01, SONYPI_EVENT_JOGDIAL_DOWN },
+	{ 0x5f, SONYPI_EVENT_JOGDIAL_UP_PRESSED },
+	{ 0x41, SONYPI_EVENT_JOGDIAL_DOWN_PRESSED },
+	{ 0x1e, SONYPI_EVENT_JOGDIAL_FAST_UP },
+	{ 0x02, SONYPI_EVENT_JOGDIAL_FAST_DOWN },
+	{ 0x5e, SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED },
+	{ 0x42, SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED },
+	{ 0x1d, SONYPI_EVENT_JOGDIAL_VFAST_UP },
+	{ 0x03, SONYPI_EVENT_JOGDIAL_VFAST_DOWN },
+	{ 0x5d, SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED },
+	{ 0x43, SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED },
+	{ 0x40, SONYPI_EVENT_JOGDIAL_PRESSED },
+	{ 0, 0 }
+};
+
+/* The set of possible capture button events */
+static struct sonypi_event sonypi_captureev[] = {
+	{ 0x05, SONYPI_EVENT_CAPTURE_PARTIALPRESSED },
+	{ 0x07, SONYPI_EVENT_CAPTURE_PRESSED },
+	{ 0x01, SONYPI_EVENT_CAPTURE_PARTIALRELEASED },
+	{ 0, 0 }
+};
+
+/* The set of possible fnkeys events */
+static struct sonypi_event sonypi_fnkeyev[] = {
+	{ 0x10, SONYPI_EVENT_FNKEY_ESC },
+	{ 0x11, SONYPI_EVENT_FNKEY_F1 },
+	{ 0x12, SONYPI_EVENT_FNKEY_F2 },
+	{ 0x13, SONYPI_EVENT_FNKEY_F3 },
+	{ 0x14, SONYPI_EVENT_FNKEY_F4 },
+	{ 0x15, SONYPI_EVENT_FNKEY_F5 },
+	{ 0x16, SONYPI_EVENT_FNKEY_F6 },
+	{ 0x17, SONYPI_EVENT_FNKEY_F7 },
+	{ 0x18, SONYPI_EVENT_FNKEY_F8 },
+	{ 0x19, SONYPI_EVENT_FNKEY_F9 },
+	{ 0x1a, SONYPI_EVENT_FNKEY_F10 },
+	{ 0x1b, SONYPI_EVENT_FNKEY_F11 },
+	{ 0x1c, SONYPI_EVENT_FNKEY_F12 },
+	{ 0x1f, SONYPI_EVENT_FNKEY_RELEASED },
+	{ 0x21, SONYPI_EVENT_FNKEY_1 },
+	{ 0x22, SONYPI_EVENT_FNKEY_2 },
+	{ 0x31, SONYPI_EVENT_FNKEY_D },
+	{ 0x32, SONYPI_EVENT_FNKEY_E },
+	{ 0x33, SONYPI_EVENT_FNKEY_F },
+	{ 0x34, SONYPI_EVENT_FNKEY_S },
+	{ 0x35, SONYPI_EVENT_FNKEY_B },
+	{ 0x36, SONYPI_EVENT_FNKEY_ONLY },
+	{ 0, 0 }
+};
+
+/* The set of possible program key events */
+static struct sonypi_event sonypi_pkeyev[] = {
+	{ 0x01, SONYPI_EVENT_PKEY_P1 },
+	{ 0x02, SONYPI_EVENT_PKEY_P2 },
+	{ 0x04, SONYPI_EVENT_PKEY_P3 },
+	{ 0x5c, SONYPI_EVENT_PKEY_P1 },
+	{ 0, 0 }
+};
+
+/* The set of possible bluetooth events */
+static struct sonypi_event sonypi_blueev[] = {
+	{ 0x55, SONYPI_EVENT_BLUETOOTH_PRESSED },
+	{ 0x59, SONYPI_EVENT_BLUETOOTH_ON },
+	{ 0x5a, SONYPI_EVENT_BLUETOOTH_OFF },
+	{ 0, 0 }
+};
+
+/* The set of possible wireless events */
+static struct sonypi_event sonypi_wlessev[] = {
+	{ 0x59, SONYPI_EVENT_WIRELESS_ON },
+	{ 0x5a, SONYPI_EVENT_WIRELESS_OFF },
+	{ 0, 0 }
+};
+
+/* The set of possible back button events */
+static struct sonypi_event sonypi_backev[] = {
+	{ 0x20, SONYPI_EVENT_BACK_PRESSED },
+	{ 0, 0 }
+};
+
+/* The set of possible help button events */
+static struct sonypi_event sonypi_helpev[] = {
+	{ 0x3b, SONYPI_EVENT_HELP_PRESSED },
+	{ 0, 0 }
+};
+
+
+/* The set of possible lid events */
+static struct sonypi_event sonypi_lidev[] = {
+	{ 0x51, SONYPI_EVENT_LID_CLOSED },
+	{ 0x50, SONYPI_EVENT_LID_OPENED },
+	{ 0, 0 }
+};
+
+/* The set of possible zoom events */
+static struct sonypi_event sonypi_zoomev[] = {
+	{ 0x39, SONYPI_EVENT_ZOOM_PRESSED },
+	{ 0, 0 }
+};
+
+/* The set of possible thumbphrase events */
+static struct sonypi_event sonypi_thumbphraseev[] = {
+	{ 0x3a, SONYPI_EVENT_THUMBPHRASE_PRESSED },
+	{ 0, 0 }
+};
+
+/* The set of possible motioneye camera events */
+static struct sonypi_event sonypi_meyeev[] = {
+	{ 0x00, SONYPI_EVENT_MEYE_FACE },
+	{ 0x01, SONYPI_EVENT_MEYE_OPPOSITE },
+	{ 0, 0 }
+};
+
+/* The set of possible memorystick events */
+static struct sonypi_event sonypi_memorystickev[] = {
+	{ 0x53, SONYPI_EVENT_MEMORYSTICK_INSERT },
+	{ 0x54, SONYPI_EVENT_MEMORYSTICK_EJECT },
+	{ 0, 0 }
+};
+
+/* The set of possible battery events */
+static struct sonypi_event sonypi_batteryev[] = {
+	{ 0x20, SONYPI_EVENT_BATTERY_INSERT },
+	{ 0x30, SONYPI_EVENT_BATTERY_REMOVE },
+	{ 0, 0 }
+};
+
+static struct sonypi_eventtypes {
+	int			model;
+	u8			data;
+	unsigned long		mask;
+	struct sonypi_event *	events;
+} sony_pic_eventtypes[] = {
+	{ SONYPI_DEVICE_TYPE1, 0, 0xffffffff, sonypi_releaseev },
+	{ SONYPI_DEVICE_TYPE1, 0x70, SONYPI_MEYE_MASK, sonypi_meyeev },
+	{ SONYPI_DEVICE_TYPE1, 0x30, SONYPI_LID_MASK, sonypi_lidev },
+	{ SONYPI_DEVICE_TYPE1, 0x60, SONYPI_CAPTURE_MASK, sonypi_captureev },
+	{ SONYPI_DEVICE_TYPE1, 0x10, SONYPI_JOGGER_MASK, sonypi_joggerev },
+	{ SONYPI_DEVICE_TYPE1, 0x20, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
+	{ SONYPI_DEVICE_TYPE1, 0x30, SONYPI_BLUETOOTH_MASK, sonypi_blueev },
+	{ SONYPI_DEVICE_TYPE1, 0x40, SONYPI_PKEY_MASK, sonypi_pkeyev },
+	{ SONYPI_DEVICE_TYPE1, 0x30, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
+	{ SONYPI_DEVICE_TYPE1, 0x40, SONYPI_BATTERY_MASK, sonypi_batteryev },
+
+	{ SONYPI_DEVICE_TYPE2, 0, 0xffffffff, sonypi_releaseev },
+	{ SONYPI_DEVICE_TYPE2, 0x38, SONYPI_LID_MASK, sonypi_lidev },
+	{ SONYPI_DEVICE_TYPE2, 0x11, SONYPI_JOGGER_MASK, sonypi_joggerev },
+	{ SONYPI_DEVICE_TYPE2, 0x61, SONYPI_CAPTURE_MASK, sonypi_captureev },
+	{ SONYPI_DEVICE_TYPE2, 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
+	{ SONYPI_DEVICE_TYPE2, 0x31, SONYPI_BLUETOOTH_MASK, sonypi_blueev },
+	{ SONYPI_DEVICE_TYPE2, 0x08, SONYPI_PKEY_MASK, sonypi_pkeyev },
+	{ SONYPI_DEVICE_TYPE2, 0x11, SONYPI_BACK_MASK, sonypi_backev },
+	{ SONYPI_DEVICE_TYPE2, 0x21, SONYPI_HELP_MASK, sonypi_helpev },
+	{ SONYPI_DEVICE_TYPE2, 0x21, SONYPI_ZOOM_MASK, sonypi_zoomev },
+	{ SONYPI_DEVICE_TYPE2, 0x20, SONYPI_THUMBPHRASE_MASK, sonypi_thumbphraseev },
+	{ SONYPI_DEVICE_TYPE2, 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
+	{ SONYPI_DEVICE_TYPE2, 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
+	{ SONYPI_DEVICE_TYPE2, 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
+
+	{ SONYPI_DEVICE_TYPE3, 0, 0xffffffff, sonypi_releaseev },
+	{ SONYPI_DEVICE_TYPE3, 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
+	{ SONYPI_DEVICE_TYPE3, 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev },
+	{ SONYPI_DEVICE_TYPE3, 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
+	{ SONYPI_DEVICE_TYPE3, 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
+	{ SONYPI_DEVICE_TYPE3, 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
+	{ 0 }
+};
+
+static int sony_pic_detect_device_type(void)
 {
-	return acpi_bus_register_driver(&sony_acpi_driver);
+	struct pci_dev *pcidev;
+	int model = 0;
+
+	if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
+				     PCI_DEVICE_ID_INTEL_82371AB_3, NULL)))
+		model = SONYPI_DEVICE_TYPE1;
+
+	else if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
+					  PCI_DEVICE_ID_INTEL_ICH6_1, NULL)))
+		model = SONYPI_DEVICE_TYPE3;
+
+	else if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
+					  PCI_DEVICE_ID_INTEL_ICH7_1, NULL)))
+		model = SONYPI_DEVICE_TYPE3;
+
+	else
+		model = SONYPI_DEVICE_TYPE2;
+
+	if (pcidev)
+		pci_dev_put(pcidev);
+
+	printk(KERN_INFO DRV_PFX "detected Type%d model\n",
+			model == SONYPI_DEVICE_TYPE1 ? 1 :
+			model == SONYPI_DEVICE_TYPE2 ? 2 : 3);
+	return model;
 }
 
-static void __exit sony_acpi_exit(void)
-{
-	acpi_bus_unregister_driver(&sony_acpi_driver);
+#define ITERATIONS_LONG		10000
+#define ITERATIONS_SHORT	10
+#define wait_on_command(command, iterations) {				\
+	unsigned int n = iterations;					\
+	while (--n && (command))					\
+		udelay(1);						\
+	if (!n)								\
+		dprintk("command failed at %s : %s (line %d)\n",	\
+				__FILE__, __FUNCTION__, __LINE__);	\
 }
 
-module_init(sony_acpi_init);
-module_exit(sony_acpi_exit);
+static u8 sony_pic_call1(u8 dev)
+{
+	u8 v1, v2;
+
+	wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2,
+			ITERATIONS_LONG);
+	outb(dev, spic_dev.cur_ioport->io.minimum + 4);
+	v1 = inb_p(spic_dev.cur_ioport->io.minimum + 4);
+	v2 = inb_p(spic_dev.cur_ioport->io.minimum);
+	dprintk("sony_pic_call1: 0x%.4x\n", (v2 << 8) | v1);
+	return v2;
+}
+
+static u8 sony_pic_call2(u8 dev, u8 fn)
+{
+	u8 v1;
+
+	wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2,
+			ITERATIONS_LONG);
+	outb(dev, spic_dev.cur_ioport->io.minimum + 4);
+	wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2,
+			ITERATIONS_LONG);
+	outb(fn, spic_dev.cur_ioport->io.minimum);
+	v1 = inb_p(spic_dev.cur_ioport->io.minimum);
+	dprintk("sony_pic_call2: 0x%.4x\n", v1);
+	return v1;
+}
+
+static u8 sony_pic_call3(u8 dev, u8 fn, u8 v)
+{
+	u8 v1;
+
+	wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG);
+	outb(dev, spic_dev.cur_ioport->io.minimum + 4);
+	wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG);
+	outb(fn, spic_dev.cur_ioport->io.minimum);
+	wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG);
+	outb(v, spic_dev.cur_ioport->io.minimum);
+	v1 = inb_p(spic_dev.cur_ioport->io.minimum);
+	dprintk("sony_pic_call3: 0x%.4x\n", v1);
+	return v1;
+}
+
+/* camera tests and poweron/poweroff */
+#define SONYPI_CAMERA_PICTURE		5
+#define SONYPI_CAMERA_CONTROL		0x10
+
+#define SONYPI_CAMERA_BRIGHTNESS		0
+#define SONYPI_CAMERA_CONTRAST			1
+#define SONYPI_CAMERA_HUE			2
+#define SONYPI_CAMERA_COLOR			3
+#define SONYPI_CAMERA_SHARPNESS			4
+
+#define SONYPI_CAMERA_EXPOSURE_MASK		0xC
+#define SONYPI_CAMERA_WHITE_BALANCE_MASK	0x3
+#define SONYPI_CAMERA_PICTURE_MODE_MASK		0x30
+#define SONYPI_CAMERA_MUTE_MASK			0x40
+
+/* the rest don't need a loop until not 0xff */
+#define SONYPI_CAMERA_AGC			6
+#define SONYPI_CAMERA_AGC_MASK			0x30
+#define SONYPI_CAMERA_SHUTTER_MASK 		0x7
+
+#define SONYPI_CAMERA_SHUTDOWN_REQUEST		7
+#define SONYPI_CAMERA_CONTROL			0x10
+
+#define SONYPI_CAMERA_STATUS 			7
+#define SONYPI_CAMERA_STATUS_READY 		0x2
+#define SONYPI_CAMERA_STATUS_POSITION		0x4
+
+#define SONYPI_DIRECTION_BACKWARDS 		0x4
+
+#define SONYPI_CAMERA_REVISION 			8
+#define SONYPI_CAMERA_ROMVERSION 		9
+
+static int __sony_pic_camera_ready(void)
+{
+	u8 v;
+
+	v = sony_pic_call2(0x8f, SONYPI_CAMERA_STATUS);
+	return (v != 0xff && (v & SONYPI_CAMERA_STATUS_READY));
+}
+
+static int __sony_pic_camera_off(void)
+{
+	if (!camera) {
+		printk(KERN_WARNING DRV_PFX "camera control not enabled\n");
+		return -ENODEV;
+	}
+
+	wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE,
+				SONYPI_CAMERA_MUTE_MASK),
+			ITERATIONS_SHORT);
+
+	if (spic_dev.camera_power) {
+		sony_pic_call2(0x91, 0);
+		spic_dev.camera_power = 0;
+	}
+	return 0;
+}
+
+static int __sony_pic_camera_on(void)
+{
+	int i, j, x;
+
+	if (!camera) {
+		printk(KERN_WARNING DRV_PFX "camera control not enabled\n");
+		return -ENODEV;
+	}
+
+	if (spic_dev.camera_power)
+		return 0;
+
+	for (j = 5; j > 0; j--) {
+
+		for (x = 0; x < 100 && sony_pic_call2(0x91, 0x1); x++)
+			msleep(10);
+		sony_pic_call1(0x93);
+
+		for (i = 400; i > 0; i--) {
+			if (__sony_pic_camera_ready())
+				break;
+			msleep(10);
+		}
+		if (i)
+			break;
+	}
+
+	if (j == 0) {
+		printk(KERN_WARNING DRV_PFX "failed to power on camera\n");
+		return -ENODEV;
+	}
+
+	wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTROL,
+				0x5a),
+			ITERATIONS_SHORT);
+
+	spic_dev.camera_power = 1;
+	return 0;
+}
+
+/* External camera command (exported to the motion eye v4l driver) */
+int sony_pic_camera_command(int command, u8 value)
+{
+	if (!camera)
+		return -EIO;
+
+	mutex_lock(&spic_dev.lock);
+
+	switch (command) {
+	case SONY_PIC_COMMAND_SETCAMERA:
+		if (value)
+			__sony_pic_camera_on();
+		else
+			__sony_pic_camera_off();
+		break;
+	case SONY_PIC_COMMAND_SETCAMERABRIGHTNESS:
+		wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_BRIGHTNESS, value),
+				ITERATIONS_SHORT);
+		break;
+	case SONY_PIC_COMMAND_SETCAMERACONTRAST:
+		wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTRAST, value),
+				ITERATIONS_SHORT);
+		break;
+	case SONY_PIC_COMMAND_SETCAMERAHUE:
+		wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_HUE, value),
+				ITERATIONS_SHORT);
+		break;
+	case SONY_PIC_COMMAND_SETCAMERACOLOR:
+		wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_COLOR, value),
+				ITERATIONS_SHORT);
+		break;
+	case SONY_PIC_COMMAND_SETCAMERASHARPNESS:
+		wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_SHARPNESS, value),
+				ITERATIONS_SHORT);
+		break;
+	case SONY_PIC_COMMAND_SETCAMERAPICTURE:
+		wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE, value),
+				ITERATIONS_SHORT);
+		break;
+	case SONY_PIC_COMMAND_SETCAMERAAGC:
+		wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_AGC, value),
+				ITERATIONS_SHORT);
+		break;
+	default:
+		printk(KERN_ERR DRV_PFX "sony_pic_camera_command invalid: %d\n",
+		       command);
+		break;
+	}
+	mutex_unlock(&spic_dev.lock);
+	return 0;
+}
+EXPORT_SYMBOL(sony_pic_camera_command);
+
+/* gprs/edge modem (SZ460N and SZ210P), thanks to Joshua Wise */
+static void sony_pic_set_wwanpower(u8 state)
+{
+	state = !!state;
+	mutex_lock(&spic_dev.lock);
+	if (spic_dev.wwan_power == state) {
+		mutex_unlock(&spic_dev.lock);
+		return;
+	}
+	sony_pic_call2(0xB0, state);
+	spic_dev.wwan_power = state;
+	mutex_unlock(&spic_dev.lock);
+}
+
+static ssize_t sony_pic_wwanpower_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buffer, size_t count)
+{
+	unsigned long value;
+	if (count > 31)
+		return -EINVAL;
+
+	value = simple_strtoul(buffer, NULL, 10);
+	sony_pic_set_wwanpower(value);
+
+	return count;
+}
+
+static ssize_t sony_pic_wwanpower_show(struct device *dev,
+		struct device_attribute *attr, char *buffer)
+{
+	ssize_t count;
+	mutex_lock(&spic_dev.lock);
+	count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.wwan_power);
+	mutex_unlock(&spic_dev.lock);
+	return count;
+}
+
+/* bluetooth subsystem power state */
+static void __sony_pic_set_bluetoothpower(u8 state)
+{
+	state = !!state;
+	if (spic_dev.bluetooth_power == state)
+		return;
+	sony_pic_call2(0x96, state);
+	sony_pic_call1(0x82);
+	spic_dev.bluetooth_power = state;
+}
+
+static ssize_t sony_pic_bluetoothpower_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buffer, size_t count)
+{
+	unsigned long value;
+	if (count > 31)
+		return -EINVAL;
+
+	value = simple_strtoul(buffer, NULL, 10);
+	mutex_lock(&spic_dev.lock);
+	__sony_pic_set_bluetoothpower(value);
+	mutex_unlock(&spic_dev.lock);
+
+	return count;
+}
+
+static ssize_t sony_pic_bluetoothpower_show(struct device *dev,
+		struct device_attribute *attr, char *buffer)
+{
+	ssize_t count = 0;
+	mutex_lock(&spic_dev.lock);
+	count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.bluetooth_power);
+	mutex_unlock(&spic_dev.lock);
+	return count;
+}
+
+/* fan speed */
+/* FAN0 information (reverse engineered from ACPI tables) */
+#define SONY_PIC_FAN0_STATUS	0x93
+static int sony_pic_set_fanspeed(unsigned long value)
+{
+	return ec_write(SONY_PIC_FAN0_STATUS, value);
+}
+
+static int sony_pic_get_fanspeed(u8 *value)
+{
+	return ec_read(SONY_PIC_FAN0_STATUS, value);
+}
+
+static ssize_t sony_pic_fanspeed_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buffer, size_t count)
+{
+	unsigned long value;
+	if (count > 31)
+		return -EINVAL;
+
+	value = simple_strtoul(buffer, NULL, 10);
+	if (sony_pic_set_fanspeed(value))
+		return -EIO;
+
+	return count;
+}
+
+static ssize_t sony_pic_fanspeed_show(struct device *dev,
+		struct device_attribute *attr, char *buffer)
+{
+	u8 value = 0;
+	if (sony_pic_get_fanspeed(&value))
+		return -EIO;
+
+	return snprintf(buffer, PAGE_SIZE, "%d\n", value);
+}
+
+#define SPIC_ATTR(_name, _mode)					\
+struct device_attribute spic_attr_##_name = __ATTR(_name,	\
+		_mode, sony_pic_## _name ##_show,		\
+		sony_pic_## _name ##_store)
+
+static SPIC_ATTR(bluetoothpower, 0644);
+static SPIC_ATTR(wwanpower, 0644);
+static SPIC_ATTR(fanspeed, 0644);
+
+static struct attribute *spic_attributes[] = {
+	&spic_attr_bluetoothpower.attr,
+	&spic_attr_wwanpower.attr,
+	&spic_attr_fanspeed.attr,
+	NULL
+};
+
+static struct attribute_group spic_attribute_group = {
+	.attrs = spic_attributes
+};
+
+/******** SONYPI compatibility **********/
+#ifdef CONFIG_SONY_LAPTOP_OLD
+
+/* battery / brightness / temperature  addresses */
+#define SONYPI_BAT_FLAGS	0x81
+#define SONYPI_LCD_LIGHT	0x96
+#define SONYPI_BAT1_PCTRM	0xa0
+#define SONYPI_BAT1_LEFT	0xa2
+#define SONYPI_BAT1_MAXRT	0xa4
+#define SONYPI_BAT2_PCTRM	0xa8
+#define SONYPI_BAT2_LEFT	0xaa
+#define SONYPI_BAT2_MAXRT	0xac
+#define SONYPI_BAT1_MAXTK	0xb0
+#define SONYPI_BAT1_FULL	0xb2
+#define SONYPI_BAT2_MAXTK	0xb8
+#define SONYPI_BAT2_FULL	0xba
+#define SONYPI_TEMP_STATUS	0xC1
+
+struct sonypi_compat_s {
+	struct fasync_struct	*fifo_async;
+	struct kfifo		*fifo;
+	spinlock_t		fifo_lock;
+	wait_queue_head_t	fifo_proc_list;
+	atomic_t		open_count;
+};
+static struct sonypi_compat_s sonypi_compat = {
+	.open_count = ATOMIC_INIT(0),
+};
+
+static int sonypi_misc_fasync(int fd, struct file *filp, int on)
+{
+	int retval;
+
+	retval = fasync_helper(fd, filp, on, &sonypi_compat.fifo_async);
+	if (retval < 0)
+		return retval;
+	return 0;
+}
+
+static int sonypi_misc_release(struct inode *inode, struct file *file)
+{
+	sonypi_misc_fasync(-1, file, 0);
+	atomic_dec(&sonypi_compat.open_count);
+	return 0;
+}
+
+static int sonypi_misc_open(struct inode *inode, struct file *file)
+{
+	/* Flush input queue on first open */
+	if (atomic_inc_return(&sonypi_compat.open_count) == 1)
+		kfifo_reset(sonypi_compat.fifo);
+	return 0;
+}
+
+static ssize_t sonypi_misc_read(struct file *file, char __user *buf,
+				size_t count, loff_t *pos)
+{
+	ssize_t ret;
+	unsigned char c;
+
+	if ((kfifo_len(sonypi_compat.fifo) == 0) &&
+	    (file->f_flags & O_NONBLOCK))
+		return -EAGAIN;
+
+	ret = wait_event_interruptible(sonypi_compat.fifo_proc_list,
+				       kfifo_len(sonypi_compat.fifo) != 0);
+	if (ret)
+		return ret;
+
+	while (ret < count &&
+	       (kfifo_get(sonypi_compat.fifo, &c, sizeof(c)) == sizeof(c))) {
+		if (put_user(c, buf++))
+			return -EFAULT;
+		ret++;
+	}
+
+	if (ret > 0) {
+		struct inode *inode = file->f_path.dentry->d_inode;
+		inode->i_atime = current_fs_time(inode->i_sb);
+	}
+
+	return ret;
+}
+
+static unsigned int sonypi_misc_poll(struct file *file, poll_table *wait)
+{
+	poll_wait(file, &sonypi_compat.fifo_proc_list, wait);
+	if (kfifo_len(sonypi_compat.fifo))
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static int ec_read16(u8 addr, u16 *value)
+{
+	u8 val_lb, val_hb;
+	if (ec_read(addr, &val_lb))
+		return -1;
+	if (ec_read(addr + 1, &val_hb))
+		return -1;
+	*value = val_lb | (val_hb << 8);
+	return 0;
+}
+
+static int sonypi_misc_ioctl(struct inode *ip, struct file *fp,
+			     unsigned int cmd, unsigned long arg)
+{
+	int ret = 0;
+	void __user *argp = (void __user *)arg;
+	u8 val8;
+	u16 val16;
+	int value;
+
+	mutex_lock(&spic_dev.lock);
+	switch (cmd) {
+	case SONYPI_IOCGBRT:
+		if (sony_backlight_device == NULL) {
+			ret = -EIO;
+			break;
+		}
+		if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) {
+			ret = -EIO;
+			break;
+		}
+		val8 = ((value & 0xff) - 1) << 5;
+		if (copy_to_user(argp, &val8, sizeof(val8)))
+				ret = -EFAULT;
+		break;
+	case SONYPI_IOCSBRT:
+		if (sony_backlight_device == NULL) {
+			ret = -EIO;
+			break;
+		}
+		if (copy_from_user(&val8, argp, sizeof(val8))) {
+			ret = -EFAULT;
+			break;
+		}
+		if (acpi_callsetfunc(sony_nc_acpi_handle, "SBRT",
+				(val8 >> 5) + 1, NULL)) {
+			ret = -EIO;
+			break;
+		}
+		/* sync the backlight device status */
+		sony_backlight_device->props.brightness =
+		    sony_backlight_get_brightness(sony_backlight_device);
+		break;
+	case SONYPI_IOCGBAT1CAP:
+		if (ec_read16(SONYPI_BAT1_FULL, &val16)) {
+			ret = -EIO;
+			break;
+		}
+		if (copy_to_user(argp, &val16, sizeof(val16)))
+			ret = -EFAULT;
+		break;
+	case SONYPI_IOCGBAT1REM:
+		if (ec_read16(SONYPI_BAT1_LEFT, &val16)) {
+			ret = -EIO;
+			break;
+		}
+		if (copy_to_user(argp, &val16, sizeof(val16)))
+			ret = -EFAULT;
+		break;
+	case SONYPI_IOCGBAT2CAP:
+		if (ec_read16(SONYPI_BAT2_FULL, &val16)) {
+			ret = -EIO;
+			break;
+		}
+		if (copy_to_user(argp, &val16, sizeof(val16)))
+			ret = -EFAULT;
+		break;
+	case SONYPI_IOCGBAT2REM:
+		if (ec_read16(SONYPI_BAT2_LEFT, &val16)) {
+			ret = -EIO;
+			break;
+		}
+		if (copy_to_user(argp, &val16, sizeof(val16)))
+			ret = -EFAULT;
+		break;
+	case SONYPI_IOCGBATFLAGS:
+		if (ec_read(SONYPI_BAT_FLAGS, &val8)) {
+			ret = -EIO;
+			break;
+		}
+		val8 &= 0x07;
+		if (copy_to_user(argp, &val8, sizeof(val8)))
+			ret = -EFAULT;
+		break;
+	case SONYPI_IOCGBLUE:
+		val8 = spic_dev.bluetooth_power;
+		if (copy_to_user(argp, &val8, sizeof(val8)))
+			ret = -EFAULT;
+		break;
+	case SONYPI_IOCSBLUE:
+		if (copy_from_user(&val8, argp, sizeof(val8))) {
+			ret = -EFAULT;
+			break;
+		}
+		__sony_pic_set_bluetoothpower(val8);
+		break;
+	/* FAN Controls */
+	case SONYPI_IOCGFAN:
+		if (sony_pic_get_fanspeed(&val8)) {
+			ret = -EIO;
+			break;
+		}
+		if (copy_to_user(argp, &val8, sizeof(val8)))
+			ret = -EFAULT;
+		break;
+	case SONYPI_IOCSFAN:
+		if (copy_from_user(&val8, argp, sizeof(val8))) {
+			ret = -EFAULT;
+			break;
+		}
+		if (sony_pic_set_fanspeed(val8))
+			ret = -EIO;
+		break;
+	/* GET Temperature (useful under APM) */
+	case SONYPI_IOCGTEMP:
+		if (ec_read(SONYPI_TEMP_STATUS, &val8)) {
+			ret = -EIO;
+			break;
+		}
+		if (copy_to_user(argp, &val8, sizeof(val8)))
+			ret = -EFAULT;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	mutex_unlock(&spic_dev.lock);
+	return ret;
+}
+
+static const struct file_operations sonypi_misc_fops = {
+	.owner		= THIS_MODULE,
+	.read		= sonypi_misc_read,
+	.poll		= sonypi_misc_poll,
+	.open		= sonypi_misc_open,
+	.release	= sonypi_misc_release,
+	.fasync		= sonypi_misc_fasync,
+	.ioctl		= sonypi_misc_ioctl,
+};
+
+static struct miscdevice sonypi_misc_device = {
+	.minor		= MISC_DYNAMIC_MINOR,
+	.name		= "sonypi",
+	.fops		= &sonypi_misc_fops,
+};
+
+static void sonypi_compat_report_event(u8 event)
+{
+	kfifo_put(sonypi_compat.fifo, (unsigned char *)&event, sizeof(event));
+	kill_fasync(&sonypi_compat.fifo_async, SIGIO, POLL_IN);
+	wake_up_interruptible(&sonypi_compat.fifo_proc_list);
+}
+
+static int sonypi_compat_init(void)
+{
+	int error;
+
+	spin_lock_init(&sonypi_compat.fifo_lock);
+	sonypi_compat.fifo = kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL,
+					 &sonypi_compat.fifo_lock);
+	if (IS_ERR(sonypi_compat.fifo)) {
+		printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
+		return PTR_ERR(sonypi_compat.fifo);
+	}
+
+	init_waitqueue_head(&sonypi_compat.fifo_proc_list);
+
+	if (minor != -1)
+		sonypi_misc_device.minor = minor;
+	error = misc_register(&sonypi_misc_device);
+	if (error) {
+		printk(KERN_ERR DRV_PFX "misc_register failed\n");
+		goto err_free_kfifo;
+	}
+	if (minor == -1)
+		printk(KERN_INFO DRV_PFX "device allocated minor is %d\n",
+		       sonypi_misc_device.minor);
+
+	return 0;
+
+err_free_kfifo:
+	kfifo_free(sonypi_compat.fifo);
+	return error;
+}
+
+static void sonypi_compat_exit(void)
+{
+	misc_deregister(&sonypi_misc_device);
+	kfifo_free(sonypi_compat.fifo);
+}
+#else
+static int sonypi_compat_init(void) { return 0; }
+static void sonypi_compat_exit(void) { }
+static void sonypi_compat_report_event(u8 event) { }
+#endif /* CONFIG_SONY_LAPTOP_OLD */
+
+/*
+ * ACPI callbacks
+ */
+static acpi_status
+sony_pic_read_possible_resource(struct acpi_resource *resource, void *context)
+{
+	u32 i;
+	struct sony_pic_dev *dev = (struct sony_pic_dev *)context;
+
+	switch (resource->type) {
+	case ACPI_RESOURCE_TYPE_START_DEPENDENT:
+	case ACPI_RESOURCE_TYPE_END_DEPENDENT:
+		return AE_OK;
+
+	case ACPI_RESOURCE_TYPE_IRQ:
+		{
+			struct acpi_resource_irq *p = &resource->data.irq;
+			struct sony_pic_irq *interrupt = NULL;
+			if (!p || !p->interrupt_count) {
+				/*
+				 * IRQ descriptors may have no IRQ# bits set,
+				 * particularly those those w/ _STA disabled
+				 */
+				dprintk("Blank IRQ resource\n");
+				return AE_OK;
+			}
+			for (i = 0; i < p->interrupt_count; i++) {
+				if (!p->interrupts[i]) {
+					printk(KERN_WARNING DRV_PFX
+							"Invalid IRQ %d\n",
+							p->interrupts[i]);
+					continue;
+				}
+				interrupt = kzalloc(sizeof(*interrupt),
+						GFP_KERNEL);
+				if (!interrupt)
+					return AE_ERROR;
+
+				list_add_tail(&interrupt->list, &dev->interrupts);
+				interrupt->irq.triggering = p->triggering;
+				interrupt->irq.polarity = p->polarity;
+				interrupt->irq.sharable = p->sharable;
+				interrupt->irq.interrupt_count = 1;
+				interrupt->irq.interrupts[0] = p->interrupts[i];
+			}
+			return AE_OK;
+		}
+	case ACPI_RESOURCE_TYPE_IO:
+		{
+			struct acpi_resource_io *io = &resource->data.io;
+			struct sony_pic_ioport *ioport = NULL;
+			if (!io) {
+				dprintk("Blank IO resource\n");
+				return AE_OK;
+			}
+
+			ioport = kzalloc(sizeof(*ioport), GFP_KERNEL);
+			if (!ioport)
+				return AE_ERROR;
+
+			list_add_tail(&ioport->list, &dev->ioports);
+			memcpy(&ioport->io, io, sizeof(*io));
+			return AE_OK;
+		}
+	default:
+		dprintk("Resource %d isn't an IRQ nor an IO port\n",
+				resource->type);
+
+	case ACPI_RESOURCE_TYPE_END_TAG:
+		return AE_OK;
+	}
+	return AE_CTRL_TERMINATE;
+}
+
+static int sony_pic_possible_resources(struct acpi_device *device)
+{
+	int result = 0;
+	acpi_status status = AE_OK;
+
+	if (!device)
+		return -EINVAL;
+
+	/* get device status */
+	/* see acpi_pci_link_get_current acpi_pci_link_get_possible */
+	dprintk("Evaluating _STA\n");
+	result = acpi_bus_get_status(device);
+	if (result) {
+		printk(KERN_WARNING DRV_PFX "Unable to read status\n");
+		goto end;
+	}
+
+	if (!device->status.enabled)
+		dprintk("Device disabled\n");
+	else
+		dprintk("Device enabled\n");
+
+	/*
+	 * Query and parse 'method'
+	 */
+	dprintk("Evaluating %s\n", METHOD_NAME__PRS);
+	status = acpi_walk_resources(device->handle, METHOD_NAME__PRS,
+			sony_pic_read_possible_resource, &spic_dev);
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_WARNING DRV_PFX
+				"Failure evaluating %s\n",
+				METHOD_NAME__PRS);
+		result = -ENODEV;
+	}
+end:
+	return result;
+}
+
+/*
+ *  Disable the spic device by calling its _DIS method
+ */
+static int sony_pic_disable(struct acpi_device *device)
+{
+	if (ACPI_FAILURE(acpi_evaluate_object(device->handle, "_DIS", 0, NULL)))
+		return -ENXIO;
+
+	dprintk("Device disabled\n");
+	return 0;
+}
+
+
+/*
+ *  Based on drivers/acpi/pci_link.c:acpi_pci_link_set
+ *
+ *  Call _SRS to set current resources
+ */
+static int sony_pic_enable(struct acpi_device *device,
+		struct sony_pic_ioport *ioport, struct sony_pic_irq *irq)
+{
+	acpi_status status;
+	int result = 0;
+	struct {
+		struct acpi_resource io_res;
+		struct acpi_resource irq_res;
+		struct acpi_resource end;
+	} *resource;
+	struct acpi_buffer buffer = { 0, NULL };
+
+	if (!ioport || !irq)
+		return -EINVAL;
+
+	/* init acpi_buffer */
+	resource = kzalloc(sizeof(*resource) + 1, GFP_KERNEL);
+	if (!resource)
+		return -ENOMEM;
+
+	buffer.length = sizeof(*resource) + 1;
+	buffer.pointer = resource;
+
+	/* setup io resource */
+	resource->io_res.type = ACPI_RESOURCE_TYPE_IO;
+	resource->io_res.length = sizeof(struct acpi_resource);
+	memcpy(&resource->io_res.data.io, &ioport->io,
+			sizeof(struct acpi_resource_io));
+
+	/* setup irq resource */
+	resource->irq_res.type = ACPI_RESOURCE_TYPE_IRQ;
+	resource->irq_res.length = sizeof(struct acpi_resource);
+	memcpy(&resource->irq_res.data.irq, &irq->irq,
+			sizeof(struct acpi_resource_irq));
+	/* we requested a shared irq */
+	resource->irq_res.data.irq.sharable = ACPI_SHARED;
+
+	resource->end.type = ACPI_RESOURCE_TYPE_END_TAG;
+
+	/* Attempt to set the resource */
+	dprintk("Evaluating _SRS\n");
+	status = acpi_set_current_resources(device->handle, &buffer);
+
+	/* check for total failure */
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_ERR DRV_PFX "Error evaluating _SRS");
+		result = -ENODEV;
+		goto end;
+	}
+
+	/* Necessary device initializations calls (from sonypi) */
+	sony_pic_call1(0x82);
+	sony_pic_call2(0x81, 0xff);
+	sony_pic_call1(compat ? 0x92 : 0x82);
+
+end:
+	kfree(resource);
+	return result;
+}
+
+/*****************
+ *
+ * ISR: some event is available
+ *
+ *****************/
+static irqreturn_t sony_pic_irq(int irq, void *dev_id)
+{
+	int i, j;
+	u32 port_val = 0;
+	u8 ev = 0;
+	u8 data_mask = 0;
+	u8 device_event = 0;
+
+	struct sony_pic_dev *dev = (struct sony_pic_dev *) dev_id;
+
+	acpi_os_read_port(dev->cur_ioport->io.minimum, &port_val,
+			dev->cur_ioport->io.address_length);
+	ev = port_val & SONY_PIC_EV_MASK;
+	data_mask = 0xff & (port_val >> (dev->cur_ioport->io.address_length - 8));
+
+	dprintk("event (0x%.8x [%.2x] [%.2x]) at port 0x%.4x\n",
+			port_val, ev, data_mask, dev->cur_ioport->io.minimum);
+
+	if (ev == 0x00 || ev == 0xff)
+		return IRQ_HANDLED;
+
+	for (i = 0; sony_pic_eventtypes[i].model; i++) {
+
+		if (spic_dev.model != sony_pic_eventtypes[i].model)
+			continue;
+
+		if ((data_mask & sony_pic_eventtypes[i].data) !=
+		    sony_pic_eventtypes[i].data)
+			continue;
+
+		if (!(mask & sony_pic_eventtypes[i].mask))
+			continue;
+
+		for (j = 0; sony_pic_eventtypes[i].events[j].event; j++) {
+			if (ev == sony_pic_eventtypes[i].events[j].data) {
+				device_event =
+					sony_pic_eventtypes[i].events[j].event;
+				goto found;
+			}
+		}
+	}
+	return IRQ_HANDLED;
+
+found:
+	sony_laptop_report_input_event(device_event);
+	acpi_bus_generate_event(spic_dev.acpi_dev, 1, device_event);
+	sonypi_compat_report_event(device_event);
+
+	return IRQ_HANDLED;
+}
+
+/*****************
+ *
+ *  ACPI driver
+ *
+ *****************/
+static int sony_pic_remove(struct acpi_device *device, int type)
+{
+	struct sony_pic_ioport *io, *tmp_io;
+	struct sony_pic_irq *irq, *tmp_irq;
+
+	sonypi_compat_exit();
+
+	if (sony_pic_disable(device)) {
+		printk(KERN_ERR DRV_PFX "Couldn't disable device.\n");
+		return -ENXIO;
+	}
+
+	free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);
+	release_region(spic_dev.cur_ioport->io.minimum,
+			spic_dev.cur_ioport->io.address_length);
+
+	sony_laptop_remove_input();
+
+	/* pf attrs */
+	sysfs_remove_group(&sony_pf_device->dev.kobj, &spic_attribute_group);
+	sony_pf_remove();
+
+	list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) {
+		list_del(&io->list);
+		kfree(io);
+	}
+	list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) {
+		list_del(&irq->list);
+		kfree(irq);
+	}
+	spic_dev.cur_ioport = NULL;
+	spic_dev.cur_irq = NULL;
+
+	dprintk(SONY_PIC_DRIVER_NAME " removed.\n");
+	return 0;
+}
+
+static int sony_pic_add(struct acpi_device *device)
+{
+	int result;
+	struct sony_pic_ioport *io, *tmp_io;
+	struct sony_pic_irq *irq, *tmp_irq;
+
+	printk(KERN_INFO DRV_PFX "%s v%s.\n",
+		SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
+
+	spic_dev.acpi_dev = device;
+	strcpy(acpi_device_class(device), "sony/hotkey");
+	spic_dev.model = sony_pic_detect_device_type();
+	mutex_init(&spic_dev.lock);
+
+	/* read _PRS resources */
+	result = sony_pic_possible_resources(device);
+	if (result) {
+		printk(KERN_ERR DRV_PFX
+				"Unabe to read possible resources.\n");
+		goto err_free_resources;
+	}
+
+	/* setup input devices and helper fifo */
+	result = sony_laptop_setup_input();
+	if (result) {
+		printk(KERN_ERR DRV_PFX
+				"Unabe to create input devices.\n");
+		goto err_free_resources;
+	}
+
+	/* request io port */
+	list_for_each_entry(io, &spic_dev.ioports, list) {
+		if (request_region(io->io.minimum, io->io.address_length,
+					"Sony Programable I/O Device")) {
+			dprintk("I/O port: 0x%.4x (0x%.4x) + 0x%.2x\n",
+					io->io.minimum, io->io.maximum,
+					io->io.address_length);
+			spic_dev.cur_ioport = io;
+			break;
+		}
+	}
+	if (!spic_dev.cur_ioport) {
+		printk(KERN_ERR DRV_PFX "Failed to request_region.\n");
+		result = -ENODEV;
+		goto err_remove_input;
+	}
+
+	/* request IRQ */
+	list_for_each_entry(irq, &spic_dev.interrupts, list) {
+		if (!request_irq(irq->irq.interrupts[0], sony_pic_irq,
+					IRQF_SHARED, "sony-laptop", &spic_dev)) {
+			dprintk("IRQ: %d - triggering: %d - "
+					"polarity: %d - shr: %d\n",
+					irq->irq.interrupts[0],
+					irq->irq.triggering,
+					irq->irq.polarity,
+					irq->irq.sharable);
+			spic_dev.cur_irq = irq;
+			break;
+		}
+	}
+	if (!spic_dev.cur_irq) {
+		printk(KERN_ERR DRV_PFX "Failed to request_irq.\n");
+		result = -ENODEV;
+		goto err_release_region;
+	}
+
+	/* set resource status _SRS */
+	result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);
+	if (result) {
+		printk(KERN_ERR DRV_PFX "Couldn't enable device.\n");
+		goto err_free_irq;
+	}
+
+	spic_dev.bluetooth_power = -1;
+	/* create device attributes */
+	result = sony_pf_add();
+	if (result)
+		goto err_disable_device;
+
+	result = sysfs_create_group(&sony_pf_device->dev.kobj, &spic_attribute_group);
+	if (result)
+		goto err_remove_pf;
+
+	if (sonypi_compat_init())
+		goto err_remove_pf;
+
+	return 0;
+
+err_remove_pf:
+	sony_pf_remove();
+
+err_disable_device:
+	sony_pic_disable(device);
+
+err_free_irq:
+	free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);
+
+err_release_region:
+	release_region(spic_dev.cur_ioport->io.minimum,
+			spic_dev.cur_ioport->io.address_length);
+
+err_remove_input:
+	sony_laptop_remove_input();
+
+err_free_resources:
+	list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) {
+		list_del(&io->list);
+		kfree(io);
+	}
+	list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) {
+		list_del(&irq->list);
+		kfree(irq);
+	}
+	spic_dev.cur_ioport = NULL;
+	spic_dev.cur_irq = NULL;
+
+	return result;
+}
+
+static int sony_pic_suspend(struct acpi_device *device, pm_message_t state)
+{
+	if (sony_pic_disable(device))
+		return -ENXIO;
+	return 0;
+}
+
+static int sony_pic_resume(struct acpi_device *device)
+{
+	sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);
+	return 0;
+}
+
+static struct acpi_driver sony_pic_driver = {
+	.name = SONY_PIC_DRIVER_NAME,
+	.class = SONY_PIC_CLASS,
+	.ids = SONY_PIC_HID,
+	.owner = THIS_MODULE,
+	.ops = {
+		.add = sony_pic_add,
+		.remove = sony_pic_remove,
+		.suspend = sony_pic_suspend,
+		.resume = sony_pic_resume,
+		},
+};
+
+static struct dmi_system_id __initdata sonypi_dmi_table[] = {
+	{
+		.ident = "Sony Vaio",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "PCG-"),
+		},
+	},
+	{
+		.ident = "Sony Vaio",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "VGN-"),
+		},
+	},
+	{ }
+};
+
+static int __init sony_laptop_init(void)
+{
+	int result;
+
+	if (!no_spic && dmi_check_system(sonypi_dmi_table)) {
+		result = acpi_bus_register_driver(&sony_pic_driver);
+		if (result) {
+			printk(KERN_ERR DRV_PFX
+					"Unable to register SPIC driver.");
+			goto out;
+		}
+	}
+
+	result = acpi_bus_register_driver(&sony_nc_driver);
+	if (result) {
+		printk(KERN_ERR DRV_PFX "Unable to register SNC driver.");
+		goto out_unregister_pic;
+	}
+
+	return 0;
+
+out_unregister_pic:
+	if (!no_spic)
+		acpi_bus_unregister_driver(&sony_pic_driver);
+out:
+	return result;
+}
+
+static void __exit sony_laptop_exit(void)
+{
+	acpi_bus_unregister_driver(&sony_nc_driver);
+	if (!no_spic)
+		acpi_bus_unregister_driver(&sony_pic_driver);
+}
+
+module_init(sony_laptop_init);
+module_exit(sony_laptop_exit);
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
new file mode 100644
index 0000000..6c36a55
--- /dev/null
+++ b/drivers/misc/thinkpad_acpi.c
@@ -0,0 +1,4312 @@
+/*
+ *  thinkpad_acpi.c - ThinkPad ACPI Extras
+ *
+ *
+ *  Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>
+ *  Copyright (C) 2006-2007 Henrique de Moraes Holschuh <hmh@hmh.eng.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; 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#define IBM_VERSION "0.14"
+#define TPACPI_SYSFS_VERSION 0x000100
+
+/*
+ *  Changelog:
+ *  2007-03-27  0.14	renamed to thinkpad_acpi and moved to
+ *  			drivers/misc.
+ *
+ *  2006-11-22	0.13	new maintainer
+ *  			changelog now lives in git commit history, and will
+ *  			not be updated further in-file.
+ *
+ *  2005-08-17  0.12	fix compilation on 2.6.13-rc kernels
+ *  2005-03-17	0.11	support for 600e, 770x
+ *			    thanks to Jamie Lentin <lentinj@dial.pipex.com>
+ *			support for 770e, G41
+ *			G40 and G41 don't have a thinklight
+ *			temperatures no longer experimental
+ *			experimental brightness control
+ *			experimental volume control
+ *			experimental fan enable/disable
+ *  2005-01-16	0.10	fix module loading on R30, R31
+ *  2005-01-16	0.9	support for 570, R30, R31
+ *			ultrabay support on A22p, A3x
+ *			limit arg for cmos, led, beep, drop experimental status
+ *			more capable led control on A21e, A22p, T20-22, X20
+ *			experimental temperatures and fan speed
+ *			experimental embedded controller register dump
+ *			mark more functions as __init, drop incorrect __exit
+ *			use MODULE_VERSION
+ *			    thanks to Henrik Brix Andersen <brix@gentoo.org>
+ *			fix parameter passing on module loading
+ *			    thanks to Rusty Russell <rusty@rustcorp.com.au>
+ *			    thanks to Jim Radford <radford@blackbean.org>
+ *  2004-11-08	0.8	fix init error case, don't return from a macro
+ *			    thanks to Chris Wright <chrisw@osdl.org>
+ *  2004-10-23	0.7	fix module loading on A21e, A22p, T20, T21, X20
+ *			fix led control on A21e
+ *  2004-10-19	0.6	use acpi_bus_register_driver() to claim HKEY device
+ *  2004-10-18	0.5	thinklight support on A21e, G40, R32, T20, T21, X20
+ *			proc file format changed
+ *			video_switch command
+ *			experimental cmos control
+ *			experimental led control
+ *			experimental acpi sounds
+ *  2004-09-16	0.4	support for module parameters
+ *			hotkey mask can be prefixed by 0x
+ *			video output switching
+ *			video expansion control
+ *			ultrabay eject support
+ *			removed lcd brightness/on/off control, didn't work
+ *  2004-08-17	0.3	support for R40
+ *			lcd off, brightness control
+ *			thinklight on/off
+ *  2004-08-14	0.2	support for T series, X20
+ *			bluetooth enable/disable
+ *			hotkey events disabled by default
+ *			removed fan control, currently useless
+ *  2004-08-09	0.1	initial release, support for X series
+ */
+
+#include "thinkpad_acpi.h"
+
+MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh");
+MODULE_DESCRIPTION(IBM_DESC);
+MODULE_VERSION(IBM_VERSION);
+MODULE_LICENSE("GPL");
+
+/* Please remove this in year 2009 */
+MODULE_ALIAS("ibm_acpi");
+
+#define __unused __attribute__ ((unused))
+
+/****************************************************************************
+ ****************************************************************************
+ *
+ * ACPI Helpers and device model
+ *
+ ****************************************************************************
+ ****************************************************************************/
+
+/*************************************************************************
+ * ACPI basic handles
+ */
+
+static acpi_handle root_handle = NULL;
+
+#define IBM_HANDLE(object, parent, paths...)			\
+	static acpi_handle  object##_handle;			\
+	static acpi_handle *object##_parent = &parent##_handle;	\
+	static char        *object##_path;			\
+	static char        *object##_paths[] = { paths }
+
+IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0",	/* 240, 240x */
+	   "\\_SB.PCI.ISA.EC",	/* 570 */
+	   "\\_SB.PCI0.ISA0.EC0",	/* 600e/x, 770e, 770x */
+	   "\\_SB.PCI0.ISA.EC",	/* A21e, A2xm/p, T20-22, X20-21 */
+	   "\\_SB.PCI0.AD4S.EC0",	/* i1400, R30 */
+	   "\\_SB.PCI0.ICH3.EC0",	/* R31 */
+	   "\\_SB.PCI0.LPC.EC",	/* all others */
+	   );
+
+IBM_HANDLE(ecrd, ec, "ECRD");	/* 570 */
+IBM_HANDLE(ecwr, ec, "ECWR");	/* 570 */
+
+
+/*************************************************************************
+ * Misc ACPI handles
+ */
+
+IBM_HANDLE(cmos, root, "\\UCMS",	/* R50, R50e, R50p, R51, T4x, X31, X40 */
+	   "\\CMOS",		/* A3x, G4x, R32, T23, T30, X22-24, X30 */
+	   "\\CMS",		/* R40, R40e */
+	   );			/* all others */
+
+IBM_HANDLE(hkey, ec, "\\_SB.HKEY",	/* 600e/x, 770e, 770x */
+	   "^HKEY",		/* R30, R31 */
+	   "HKEY",		/* all others */
+	   );			/* 570 */
+
+
+/*************************************************************************
+ * ACPI helpers
+ */
+
+static int acpi_evalf(acpi_handle handle,
+		      void *res, char *method, char *fmt, ...)
+{
+	char *fmt0 = fmt;
+	struct acpi_object_list params;
+	union acpi_object in_objs[IBM_MAX_ACPI_ARGS];
+	struct acpi_buffer result, *resultp;
+	union acpi_object out_obj;
+	acpi_status status;
+	va_list ap;
+	char res_type;
+	int success;
+	int quiet;
+
+	if (!*fmt) {
+		printk(IBM_ERR "acpi_evalf() called with empty format\n");
+		return 0;
+	}
+
+	if (*fmt == 'q') {
+		quiet = 1;
+		fmt++;
+	} else
+		quiet = 0;
+
+	res_type = *(fmt++);
+
+	params.count = 0;
+	params.pointer = &in_objs[0];
+
+	va_start(ap, fmt);
+	while (*fmt) {
+		char c = *(fmt++);
+		switch (c) {
+		case 'd':	/* int */
+			in_objs[params.count].integer.value = va_arg(ap, int);
+			in_objs[params.count++].type = ACPI_TYPE_INTEGER;
+			break;
+			/* add more types as needed */
+		default:
+			printk(IBM_ERR "acpi_evalf() called "
+			       "with invalid format character '%c'\n", c);
+			return 0;
+		}
+	}
+	va_end(ap);
+
+	if (res_type != 'v') {
+		result.length = sizeof(out_obj);
+		result.pointer = &out_obj;
+		resultp = &result;
+	} else
+		resultp = NULL;
+
+	status = acpi_evaluate_object(handle, method, &params, resultp);
+
+	switch (res_type) {
+	case 'd':		/* int */
+		if (res)
+			*(int *)res = out_obj.integer.value;
+		success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER;
+		break;
+	case 'v':		/* void */
+		success = status == AE_OK;
+		break;
+		/* add more types as needed */
+	default:
+		printk(IBM_ERR "acpi_evalf() called "
+		       "with invalid format character '%c'\n", res_type);
+		return 0;
+	}
+
+	if (!success && !quiet)
+		printk(IBM_ERR "acpi_evalf(%s, %s, ...) failed: %d\n",
+		       method, fmt0, status);
+
+	return success;
+}
+
+static void __unused acpi_print_int(acpi_handle handle, char *method)
+{
+	int i;
+
+	if (acpi_evalf(handle, &i, method, "d"))
+		printk(IBM_INFO "%s = 0x%x\n", method, i);
+	else
+		printk(IBM_ERR "error calling %s\n", method);
+}
+
+static int acpi_ec_read(int i, u8 * p)
+{
+	int v;
+
+	if (ecrd_handle) {
+		if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i))
+			return 0;
+		*p = v;
+	} else {
+		if (ec_read(i, p) < 0)
+			return 0;
+	}
+
+	return 1;
+}
+
+static int acpi_ec_write(int i, u8 v)
+{
+	if (ecwr_handle) {
+		if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v))
+			return 0;
+	} else {
+		if (ec_write(i, v) < 0)
+			return 0;
+	}
+
+	return 1;
+}
+
+static int _sta(acpi_handle handle)
+{
+	int status;
+
+	if (!handle || !acpi_evalf(handle, &status, "_STA", "d"))
+		status = 0;
+
+	return status;
+}
+
+static int issue_thinkpad_cmos_command(int cmos_cmd)
+{
+	if (!cmos_handle)
+		return -ENXIO;
+
+	if (!acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd))
+		return -EIO;
+
+	return 0;
+}
+
+/*************************************************************************
+ * ACPI device model
+ */
+
+static void drv_acpi_handle_init(char *name,
+			   acpi_handle *handle, acpi_handle parent,
+			   char **paths, int num_paths, char **path)
+{
+	int i;
+	acpi_status status;
+
+	vdbg_printk(TPACPI_DBG_INIT, "trying to locate ACPI handle for %s\n",
+		name);
+
+	for (i = 0; i < num_paths; i++) {
+		status = acpi_get_handle(parent, paths[i], handle);
+		if (ACPI_SUCCESS(status)) {
+			*path = paths[i];
+			dbg_printk(TPACPI_DBG_INIT,
+				   "Found ACPI handle %s for %s\n",
+				   *path, name);
+			return;
+		}
+	}
+
+	vdbg_printk(TPACPI_DBG_INIT, "ACPI handle for %s not found\n",
+		    name);
+	*handle = NULL;
+}
+
+static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct ibm_struct *ibm = data;
+
+	if (!ibm || !ibm->acpi || !ibm->acpi->notify)
+		return;
+
+	ibm->acpi->notify(ibm, event);
+}
+
+static int __init setup_acpi_notify(struct ibm_struct *ibm)
+{
+	acpi_status status;
+	int rc;
+
+	BUG_ON(!ibm->acpi);
+
+	if (!*ibm->acpi->handle)
+		return 0;
+
+	vdbg_printk(TPACPI_DBG_INIT,
+		"setting up ACPI notify for %s\n", ibm->name);
+
+	rc = acpi_bus_get_device(*ibm->acpi->handle, &ibm->acpi->device);
+	if (rc < 0) {
+		printk(IBM_ERR "acpi_bus_get_device(%s) failed: %d\n",
+			ibm->name, rc);
+		return -ENODEV;
+	}
+
+	acpi_driver_data(ibm->acpi->device) = ibm;
+	sprintf(acpi_device_class(ibm->acpi->device), "%s/%s",
+		IBM_ACPI_EVENT_PREFIX,
+		ibm->name);
+
+	status = acpi_install_notify_handler(*ibm->acpi->handle,
+			ibm->acpi->type, dispatch_acpi_notify, ibm);
+	if (ACPI_FAILURE(status)) {
+		if (status == AE_ALREADY_EXISTS) {
+			printk(IBM_NOTICE "another device driver is already handling %s events\n",
+				ibm->name);
+		} else {
+			printk(IBM_ERR "acpi_install_notify_handler(%s) failed: %d\n",
+				ibm->name, status);
+		}
+		return -ENODEV;
+	}
+	ibm->flags.acpi_notify_installed = 1;
+	return 0;
+}
+
+static int __init tpacpi_device_add(struct acpi_device *device)
+{
+	return 0;
+}
+
+static int __init register_tpacpi_subdriver(struct ibm_struct *ibm)
+{
+	int rc;
+
+	dbg_printk(TPACPI_DBG_INIT,
+		"registering %s as an ACPI driver\n", ibm->name);
+
+	BUG_ON(!ibm->acpi);
+
+	ibm->acpi->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL);
+	if (!ibm->acpi->driver) {
+		printk(IBM_ERR "kzalloc(ibm->driver) failed\n");
+		return -ENOMEM;
+	}
+
+	sprintf(ibm->acpi->driver->name, "%s_%s", IBM_NAME, ibm->name);
+	ibm->acpi->driver->ids = ibm->acpi->hid;
+	ibm->acpi->driver->ops.add = &tpacpi_device_add;
+
+	rc = acpi_bus_register_driver(ibm->acpi->driver);
+	if (rc < 0) {
+		printk(IBM_ERR "acpi_bus_register_driver(%s) failed: %d\n",
+		       ibm->acpi->hid, rc);
+		kfree(ibm->acpi->driver);
+		ibm->acpi->driver = NULL;
+	} else if (!rc)
+		ibm->flags.acpi_driver_registered = 1;
+
+	return rc;
+}
+
+
+/****************************************************************************
+ ****************************************************************************
+ *
+ * Procfs Helpers
+ *
+ ****************************************************************************
+ ****************************************************************************/
+
+static int dispatch_procfs_read(char *page, char **start, off_t off,
+			int count, int *eof, void *data)
+{
+	struct ibm_struct *ibm = data;
+	int len;
+
+	if (!ibm || !ibm->read)
+		return -EINVAL;
+
+	len = ibm->read(page);
+	if (len < 0)
+		return len;
+
+	if (len <= off + count)
+		*eof = 1;
+	*start = page + off;
+	len -= off;
+	if (len > count)
+		len = count;
+	if (len < 0)
+		len = 0;
+
+	return len;
+}
+
+static int dispatch_procfs_write(struct file *file,
+			const char __user * userbuf,
+			unsigned long count, void *data)
+{
+	struct ibm_struct *ibm = data;
+	char *kernbuf;
+	int ret;
+
+	if (!ibm || !ibm->write)
+		return -EINVAL;
+
+	kernbuf = kmalloc(count + 2, GFP_KERNEL);
+	if (!kernbuf)
+		return -ENOMEM;
+
+	if (copy_from_user(kernbuf, userbuf, count)) {
+		kfree(kernbuf);
+		return -EFAULT;
+	}
+
+	kernbuf[count] = 0;
+	strcat(kernbuf, ",");
+	ret = ibm->write(kernbuf);
+	if (ret == 0)
+		ret = count;
+
+	kfree(kernbuf);
+
+	return ret;
+}
+
+static char *next_cmd(char **cmds)
+{
+	char *start = *cmds;
+	char *end;
+
+	while ((end = strchr(start, ',')) && end == start)
+		start = end + 1;
+
+	if (!end)
+		return NULL;
+
+	*end = 0;
+	*cmds = end + 1;
+	return start;
+}
+
+
+/****************************************************************************
+ ****************************************************************************
+ *
+ * Device model: hwmon and platform
+ *
+ ****************************************************************************
+ ****************************************************************************/
+
+static struct platform_device *tpacpi_pdev = NULL;
+static struct class_device *tpacpi_hwmon = NULL;
+
+static struct platform_driver tpacpi_pdriver = {
+	.driver = {
+		.name = IBM_DRVR_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+
+/*************************************************************************
+ * thinkpad-acpi driver attributes
+ */
+
+/* interface_version --------------------------------------------------- */
+static ssize_t tpacpi_driver_interface_version_show(
+				struct device_driver *drv,
+				char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%08x\n", TPACPI_SYSFS_VERSION);
+}
+
+static DRIVER_ATTR(interface_version, S_IRUGO,
+		tpacpi_driver_interface_version_show, NULL);
+
+/* debug_level --------------------------------------------------------- */
+static ssize_t tpacpi_driver_debug_show(struct device_driver *drv,
+						char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%04x\n", dbg_level);
+}
+
+static ssize_t tpacpi_driver_debug_store(struct device_driver *drv,
+						const char *buf, size_t count)
+{
+	unsigned long t;
+
+	if (parse_strtoul(buf, 0xffff, &t))
+		return -EINVAL;
+
+	dbg_level = t;
+
+	return count;
+}
+
+static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
+		tpacpi_driver_debug_show, tpacpi_driver_debug_store);
+
+/* version ------------------------------------------------------------- */
+static ssize_t tpacpi_driver_version_show(struct device_driver *drv,
+						char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s v%s\n", IBM_DESC, IBM_VERSION);
+}
+
+static DRIVER_ATTR(version, S_IRUGO,
+		tpacpi_driver_version_show, NULL);
+
+/* --------------------------------------------------------------------- */
+
+static struct driver_attribute* tpacpi_driver_attributes[] = {
+	&driver_attr_debug_level, &driver_attr_version,
+	&driver_attr_interface_version,
+};
+
+static int __init tpacpi_create_driver_attributes(struct device_driver *drv)
+{
+	int i, res;
+
+	i = 0;
+	res = 0;
+	while (!res && i < ARRAY_SIZE(tpacpi_driver_attributes)) {
+		res = driver_create_file(drv, tpacpi_driver_attributes[i]);
+		i++;
+	}
+
+	return res;
+}
+
+static void tpacpi_remove_driver_attributes(struct device_driver *drv)
+{
+	int i;
+
+	for(i = 0; i < ARRAY_SIZE(tpacpi_driver_attributes); i++)
+		driver_remove_file(drv, tpacpi_driver_attributes[i]);
+}
+
+/*************************************************************************
+ * sysfs support helpers
+ */
+
+struct attribute_set_obj {
+	struct attribute_set s;
+	struct attribute *a;
+} __attribute__((packed));
+
+static struct attribute_set *create_attr_set(unsigned int max_members,
+						const char* name)
+{
+	struct attribute_set_obj *sobj;
+
+	if (max_members == 0)
+		return NULL;
+
+	/* Allocates space for implicit NULL at the end too */
+	sobj = kzalloc(sizeof(struct attribute_set_obj) +
+		    max_members * sizeof(struct attribute *),
+		    GFP_KERNEL);
+	if (!sobj)
+		return NULL;
+	sobj->s.max_members = max_members;
+	sobj->s.group.attrs = &sobj->a;
+	sobj->s.group.name = name;
+
+	return &sobj->s;
+}
+
+/* not multi-threaded safe, use it in a single thread per set */
+static int add_to_attr_set(struct attribute_set* s, struct attribute *attr)
+{
+	if (!s || !attr)
+		return -EINVAL;
+
+	if (s->members >= s->max_members)
+		return -ENOMEM;
+
+	s->group.attrs[s->members] = attr;
+	s->members++;
+
+	return 0;
+}
+
+static int add_many_to_attr_set(struct attribute_set* s,
+			struct attribute **attr,
+			unsigned int count)
+{
+	int i, res;
+
+	for (i = 0; i < count; i++) {
+		res = add_to_attr_set(s, attr[i]);
+		if (res)
+			return res;
+	}
+
+	return 0;
+}
+
+static void delete_attr_set(struct attribute_set* s, struct kobject *kobj)
+{
+	sysfs_remove_group(kobj, &s->group);
+	destroy_attr_set(s);
+}
+
+static int parse_strtoul(const char *buf,
+		unsigned long max, unsigned long *value)
+{
+	char *endp;
+
+	*value = simple_strtoul(buf, &endp, 0);
+	while (*endp && isspace(*endp))
+		endp++;
+	if (*endp || *value > max)
+		return -EINVAL;
+
+	return 0;
+}
+
+/****************************************************************************
+ ****************************************************************************
+ *
+ * Subdrivers
+ *
+ ****************************************************************************
+ ****************************************************************************/
+
+/*************************************************************************
+ * thinkpad-acpi init subdriver
+ */
+
+static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm)
+{
+	printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION);
+	printk(IBM_INFO "%s\n", IBM_URL);
+
+	if (ibm_thinkpad_ec_found)
+		printk(IBM_INFO "ThinkPad EC firmware %s\n",
+		       ibm_thinkpad_ec_found);
+
+	return 0;
+}
+
+static int thinkpad_acpi_driver_read(char *p)
+{
+	int len = 0;
+
+	len += sprintf(p + len, "driver:\t\t%s\n", IBM_DESC);
+	len += sprintf(p + len, "version:\t%s\n", IBM_VERSION);
+
+	return len;
+}
+
+static struct ibm_struct thinkpad_acpi_driver_data = {
+	.name = "driver",
+	.read = thinkpad_acpi_driver_read,
+};
+
+/*************************************************************************
+ * Hotkey subdriver
+ */
+
+static int hotkey_orig_status;
+static int hotkey_orig_mask;
+
+static struct attribute_set *hotkey_dev_attributes = NULL;
+
+/* sysfs hotkey enable ------------------------------------------------- */
+static ssize_t hotkey_enable_show(struct device *dev,
+			   struct device_attribute *attr,
+			   char *buf)
+{
+	int res, status, mask;
+
+	res = hotkey_get(&status, &mask);
+	if (res)
+		return res;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", status);
+}
+
+static ssize_t hotkey_enable_store(struct device *dev,
+			    struct device_attribute *attr,
+			    const char *buf, size_t count)
+{
+	unsigned long t;
+	int res, status, mask;
+
+	if (parse_strtoul(buf, 1, &t))
+		return -EINVAL;
+
+	res = hotkey_get(&status, &mask);
+	if (!res)
+		res = hotkey_set(t, mask);
+
+	return (res) ? res : count;
+}
+
+static struct device_attribute dev_attr_hotkey_enable =
+	__ATTR(enable, S_IWUSR | S_IRUGO,
+		hotkey_enable_show, hotkey_enable_store);
+
+/* sysfs hotkey mask --------------------------------------------------- */
+static ssize_t hotkey_mask_show(struct device *dev,
+			   struct device_attribute *attr,
+			   char *buf)
+{
+	int res, status, mask;
+
+	res = hotkey_get(&status, &mask);
+	if (res)
+		return res;
+
+	return snprintf(buf, PAGE_SIZE, "0x%04x\n", mask);
+}
+
+static ssize_t hotkey_mask_store(struct device *dev,
+			    struct device_attribute *attr,
+			    const char *buf, size_t count)
+{
+	unsigned long t;
+	int res, status, mask;
+
+	if (parse_strtoul(buf, 0xffff, &t))
+		return -EINVAL;
+
+	res = hotkey_get(&status, &mask);
+	if (!res)
+		hotkey_set(status, t);
+
+	return (res) ? res : count;
+}
+
+static struct device_attribute dev_attr_hotkey_mask =
+	__ATTR(mask, S_IWUSR | S_IRUGO,
+		hotkey_mask_show, hotkey_mask_store);
+
+/* sysfs hotkey bios_enabled ------------------------------------------- */
+static ssize_t hotkey_bios_enabled_show(struct device *dev,
+			   struct device_attribute *attr,
+			   char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_orig_status);
+}
+
+static struct device_attribute dev_attr_hotkey_bios_enabled =
+	__ATTR(bios_enabled, S_IRUGO, hotkey_bios_enabled_show, NULL);
+
+/* sysfs hotkey bios_mask ---------------------------------------------- */
+static ssize_t hotkey_bios_mask_show(struct device *dev,
+			   struct device_attribute *attr,
+			   char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%04x\n", hotkey_orig_mask);
+}
+
+static struct device_attribute dev_attr_hotkey_bios_mask =
+	__ATTR(bios_mask, S_IRUGO, hotkey_bios_mask_show, NULL);
+
+/* --------------------------------------------------------------------- */
+
+static struct attribute *hotkey_mask_attributes[] = {
+	&dev_attr_hotkey_mask.attr,
+	&dev_attr_hotkey_bios_enabled.attr,
+	&dev_attr_hotkey_bios_mask.attr,
+};
+
+static int __init hotkey_init(struct ibm_init_struct *iibm)
+{
+	int res;
+
+	vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n");
+
+	IBM_ACPIHANDLE_INIT(hkey);
+	mutex_init(&hotkey_mutex);
+
+	/* hotkey not supported on 570 */
+	tp_features.hotkey = hkey_handle != NULL;
+
+	vdbg_printk(TPACPI_DBG_INIT, "hotkeys are %s\n",
+		str_supported(tp_features.hotkey));
+
+	if (tp_features.hotkey) {
+		hotkey_dev_attributes = create_attr_set(4,
+						TPACPI_HOTKEY_SYSFS_GROUP);
+		if (!hotkey_dev_attributes)
+			return -ENOMEM;
+		res = add_to_attr_set(hotkey_dev_attributes,
+				&dev_attr_hotkey_enable.attr);
+		if (res)
+			return res;
+
+		/* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
+		   A30, R30, R31, T20-22, X20-21, X22-24 */
+		tp_features.hotkey_mask =
+			acpi_evalf(hkey_handle, NULL, "DHKN", "qv");
+
+		vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n",
+			str_supported(tp_features.hotkey_mask));
+
+		res = hotkey_get(&hotkey_orig_status, &hotkey_orig_mask);
+		if (!res && tp_features.hotkey_mask) {
+			res = add_many_to_attr_set(hotkey_dev_attributes,
+				hotkey_mask_attributes,
+				ARRAY_SIZE(hotkey_mask_attributes));
+		}
+		if (!res)
+			res = register_attr_set_with_sysfs(
+					hotkey_dev_attributes,
+					&tpacpi_pdev->dev.kobj);
+
+		if (res)
+			return res;
+	}
+
+	return (tp_features.hotkey)? 0 : 1;
+}
+
+static void hotkey_exit(void)
+{
+	int res;
+
+	if (tp_features.hotkey) {
+		dbg_printk(TPACPI_DBG_EXIT, "restoring original hotkey mask\n");
+		res = hotkey_set(hotkey_orig_status, hotkey_orig_mask);
+		if (res)
+			printk(IBM_ERR "failed to restore hotkey to BIOS defaults\n");
+	}
+
+	if (hotkey_dev_attributes) {
+		delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
+		hotkey_dev_attributes = NULL;
+	}
+}
+
+static void hotkey_notify(struct ibm_struct *ibm, u32 event)
+{
+	int hkey;
+
+	if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d"))
+		acpi_bus_generate_event(ibm->acpi->device, event, hkey);
+	else {
+		printk(IBM_ERR "unknown hotkey event %d\n", event);
+		acpi_bus_generate_event(ibm->acpi->device, event, 0);
+	}
+}
+
+/*
+ * Call with hotkey_mutex held
+ */
+static int hotkey_get(int *status, int *mask)
+{
+	if (!acpi_evalf(hkey_handle, status, "DHKC", "d"))
+		return -EIO;
+
+	if (tp_features.hotkey_mask)
+		if (!acpi_evalf(hkey_handle, mask, "DHKN", "d"))
+			return -EIO;
+
+	return 0;
+}
+
+/*
+ * Call with hotkey_mutex held
+ */
+static int hotkey_set(int status, int mask)
+{
+	int i;
+
+	if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status))
+		return -EIO;
+
+	if (tp_features.hotkey_mask)
+		for (i = 0; i < 32; i++) {
+			int bit = ((1 << i) & mask) != 0;
+			if (!acpi_evalf(hkey_handle,
+					NULL, "MHKM", "vdd", i + 1, bit))
+				return -EIO;
+		}
+
+	return 0;
+}
+
+/* procfs -------------------------------------------------------------- */
+static int hotkey_read(char *p)
+{
+	int res, status, mask;
+	int len = 0;
+
+	if (!tp_features.hotkey) {
+		len += sprintf(p + len, "status:\t\tnot supported\n");
+		return len;
+	}
+
+	res = mutex_lock_interruptible(&hotkey_mutex);
+	if (res < 0)
+		return res;
+	res = hotkey_get(&status, &mask);
+	mutex_unlock(&hotkey_mutex);
+	if (res)
+		return res;
+
+	len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0));
+	if (tp_features.hotkey_mask) {
+		len += sprintf(p + len, "mask:\t\t0x%04x\n", mask);
+		len += sprintf(p + len,
+			       "commands:\tenable, disable, reset, <mask>\n");
+	} else {
+		len += sprintf(p + len, "mask:\t\tnot supported\n");
+		len += sprintf(p + len, "commands:\tenable, disable, reset\n");
+	}
+
+	return len;
+}
+
+static int hotkey_write(char *buf)
+{
+	int res, status, mask;
+	char *cmd;
+	int do_cmd = 0;
+
+	if (!tp_features.hotkey)
+		return -ENODEV;
+
+	res = mutex_lock_interruptible(&hotkey_mutex);
+	if (res < 0)
+		return res;
+
+	res = hotkey_get(&status, &mask);
+	if (res)
+		goto errexit;
+
+	res = 0;
+	while ((cmd = next_cmd(&buf))) {
+		if (strlencmp(cmd, "enable") == 0) {
+			status = 1;
+		} else if (strlencmp(cmd, "disable") == 0) {
+			status = 0;
+		} else if (strlencmp(cmd, "reset") == 0) {
+			status = hotkey_orig_status;
+			mask = hotkey_orig_mask;
+		} else if (sscanf(cmd, "0x%x", &mask) == 1) {
+			/* mask set */
+		} else if (sscanf(cmd, "%x", &mask) == 1) {
+			/* mask set */
+		} else {
+			res = -EINVAL;
+			goto errexit;
+		}
+		do_cmd = 1;
+	}
+
+	if (do_cmd)
+		res = hotkey_set(status, mask);
+
+errexit:
+	mutex_unlock(&hotkey_mutex);
+	return res;
+}
+
+static struct tp_acpi_drv_struct ibm_hotkey_acpidriver = {
+	.hid = IBM_HKEY_HID,
+	.notify = hotkey_notify,
+	.handle = &hkey_handle,
+	.type = ACPI_DEVICE_NOTIFY,
+};
+
+static struct ibm_struct hotkey_driver_data = {
+	.name = "hotkey",
+	.read = hotkey_read,
+	.write = hotkey_write,
+	.exit = hotkey_exit,
+	.acpi = &ibm_hotkey_acpidriver,
+};
+
+/*************************************************************************
+ * Bluetooth subdriver
+ */
+
+/* sysfs bluetooth enable ---------------------------------------------- */
+static ssize_t bluetooth_enable_show(struct device *dev,
+			   struct device_attribute *attr,
+			   char *buf)
+{
+	int status;
+
+	status = bluetooth_get_radiosw();
+	if (status < 0)
+		return status;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0);
+}
+
+static ssize_t bluetooth_enable_store(struct device *dev,
+			    struct device_attribute *attr,
+			    const char *buf, size_t count)
+{
+	unsigned long t;
+	int res;
+
+	if (parse_strtoul(buf, 1, &t))
+		return -EINVAL;
+
+	res = bluetooth_set_radiosw(t);
+
+	return (res) ? res : count;
+}
+
+static struct device_attribute dev_attr_bluetooth_enable =
+	__ATTR(enable, S_IWUSR | S_IRUGO,
+		bluetooth_enable_show, bluetooth_enable_store);
+
+/* --------------------------------------------------------------------- */
+
+static struct attribute *bluetooth_attributes[] = {
+	&dev_attr_bluetooth_enable.attr,
+	NULL
+};
+
+static const struct attribute_group bluetooth_attr_group = {
+	.name = TPACPI_BLUETH_SYSFS_GROUP,
+	.attrs = bluetooth_attributes,
+};
+
+static int __init bluetooth_init(struct ibm_init_struct *iibm)
+{
+	int res;
+	int status = 0;
+
+	vdbg_printk(TPACPI_DBG_INIT, "initializing bluetooth subdriver\n");
+
+	IBM_ACPIHANDLE_INIT(hkey);
+
+	/* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
+	   G4x, R30, R31, R40e, R50e, T20-22, X20-21 */
+	tp_features.bluetooth = hkey_handle &&
+	    acpi_evalf(hkey_handle, &status, "GBDC", "qd");
+
+	vdbg_printk(TPACPI_DBG_INIT, "bluetooth is %s, status 0x%02x\n",
+		str_supported(tp_features.bluetooth),
+		status);
+
+	if (tp_features.bluetooth) {
+		if (!(status & TP_ACPI_BLUETOOTH_HWPRESENT)) {
+			/* no bluetooth hardware present in system */
+			tp_features.bluetooth = 0;
+			dbg_printk(TPACPI_DBG_INIT,
+				   "bluetooth hardware not installed\n");
+		} else {
+			res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+					&bluetooth_attr_group);
+			if (res)
+				return res;
+		}
+	}
+
+	return (tp_features.bluetooth)? 0 : 1;
+}
+
+static void bluetooth_exit(void)
+{
+	sysfs_remove_group(&tpacpi_pdev->dev.kobj,
+			&bluetooth_attr_group);
+}
+
+static int bluetooth_get_radiosw(void)
+{
+	int status;
+
+	if (!tp_features.bluetooth)
+		return -ENODEV;
+
+	if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
+		return -EIO;
+
+	return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0);
+}
+
+static int bluetooth_set_radiosw(int radio_on)
+{
+	int status;
+
+	if (!tp_features.bluetooth)
+		return -ENODEV;
+
+	if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
+		return -EIO;
+	if (radio_on)
+		status |= TP_ACPI_BLUETOOTH_RADIOSSW;
+	else
+		status &= ~TP_ACPI_BLUETOOTH_RADIOSSW;
+	if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
+		return -EIO;
+
+	return 0;
+}
+
+/* procfs -------------------------------------------------------------- */
+static int bluetooth_read(char *p)
+{
+	int len = 0;
+	int status = bluetooth_get_radiosw();
+
+	if (!tp_features.bluetooth)
+		len += sprintf(p + len, "status:\t\tnot supported\n");
+	else {
+		len += sprintf(p + len, "status:\t\t%s\n",
+				(status)? "enabled" : "disabled");
+		len += sprintf(p + len, "commands:\tenable, disable\n");
+	}
+
+	return len;
+}
+
+static int bluetooth_write(char *buf)
+{
+	char *cmd;
+
+	if (!tp_features.bluetooth)
+		return -ENODEV;
+
+	while ((cmd = next_cmd(&buf))) {
+		if (strlencmp(cmd, "enable") == 0) {
+			bluetooth_set_radiosw(1);
+		} else if (strlencmp(cmd, "disable") == 0) {
+			bluetooth_set_radiosw(0);
+		} else
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct ibm_struct bluetooth_driver_data = {
+	.name = "bluetooth",
+	.read = bluetooth_read,
+	.write = bluetooth_write,
+	.exit = bluetooth_exit,
+};
+
+/*************************************************************************
+ * Wan subdriver
+ */
+
+/* sysfs wan enable ---------------------------------------------------- */
+static ssize_t wan_enable_show(struct device *dev,
+			   struct device_attribute *attr,
+			   char *buf)
+{
+	int status;
+
+	status = wan_get_radiosw();
+	if (status < 0)
+		return status;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0);
+}
+
+static ssize_t wan_enable_store(struct device *dev,
+			    struct device_attribute *attr,
+			    const char *buf, size_t count)
+{
+	unsigned long t;
+	int res;
+
+	if (parse_strtoul(buf, 1, &t))
+		return -EINVAL;
+
+	res = wan_set_radiosw(t);
+
+	return (res) ? res : count;
+}
+
+static struct device_attribute dev_attr_wan_enable =
+	__ATTR(enable, S_IWUSR | S_IRUGO,
+		wan_enable_show, wan_enable_store);
+
+/* --------------------------------------------------------------------- */
+
+static struct attribute *wan_attributes[] = {
+	&dev_attr_wan_enable.attr,
+	NULL
+};
+
+static const struct attribute_group wan_attr_group = {
+	.name = TPACPI_WAN_SYSFS_GROUP,
+	.attrs = wan_attributes,
+};
+
+static int __init wan_init(struct ibm_init_struct *iibm)
+{
+	int res;
+	int status = 0;
+
+	vdbg_printk(TPACPI_DBG_INIT, "initializing wan subdriver\n");
+
+	IBM_ACPIHANDLE_INIT(hkey);
+
+	tp_features.wan = hkey_handle &&
+	    acpi_evalf(hkey_handle, &status, "GWAN", "qd");
+
+	vdbg_printk(TPACPI_DBG_INIT, "wan is %s, status 0x%02x\n",
+		str_supported(tp_features.wan),
+		status);
+
+	if (tp_features.wan) {
+		if (!(status & TP_ACPI_WANCARD_HWPRESENT)) {
+			/* no wan hardware present in system */
+			tp_features.wan = 0;
+			dbg_printk(TPACPI_DBG_INIT,
+				   "wan hardware not installed\n");
+		} else {
+			res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+					&wan_attr_group);
+			if (res)
+				return res;
+		}
+	}
+
+	return (tp_features.wan)? 0 : 1;
+}
+
+static void wan_exit(void)
+{
+	sysfs_remove_group(&tpacpi_pdev->dev.kobj,
+		&wan_attr_group);
+}
+
+static int wan_get_radiosw(void)
+{
+	int status;
+
+	if (!tp_features.wan)
+		return -ENODEV;
+
+	if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
+		return -EIO;
+
+	return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0);
+}
+
+static int wan_set_radiosw(int radio_on)
+{
+	int status;
+
+	if (!tp_features.wan)
+		return -ENODEV;
+
+	if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
+		return -EIO;
+	if (radio_on)
+		status |= TP_ACPI_WANCARD_RADIOSSW;
+	else
+		status &= ~TP_ACPI_WANCARD_RADIOSSW;
+	if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
+		return -EIO;
+
+	return 0;
+}
+
+/* procfs -------------------------------------------------------------- */
+static int wan_read(char *p)
+{
+	int len = 0;
+	int status = wan_get_radiosw();
+
+	if (!tp_features.wan)
+		len += sprintf(p + len, "status:\t\tnot supported\n");
+	else {
+		len += sprintf(p + len, "status:\t\t%s\n",
+				(status)? "enabled" : "disabled");
+		len += sprintf(p + len, "commands:\tenable, disable\n");
+	}
+
+	return len;
+}
+
+static int wan_write(char *buf)
+{
+	char *cmd;
+
+	if (!tp_features.wan)
+		return -ENODEV;
+
+	while ((cmd = next_cmd(&buf))) {
+		if (strlencmp(cmd, "enable") == 0) {
+			wan_set_radiosw(1);
+		} else if (strlencmp(cmd, "disable") == 0) {
+			wan_set_radiosw(0);
+		} else
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct ibm_struct wan_driver_data = {
+	.name = "wan",
+	.read = wan_read,
+	.write = wan_write,
+	.exit = wan_exit,
+	.flags.experimental = 1,
+};
+
+/*************************************************************************
+ * Video subdriver
+ */
+
+static enum video_access_mode video_supported;
+static int video_orig_autosw;
+
+IBM_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA",	/* 570 */
+	   "\\_SB.PCI0.AGP0.VID0",	/* 600e/x, 770x */
+	   "\\_SB.PCI0.VID0",	/* 770e */
+	   "\\_SB.PCI0.VID",	/* A21e, G4x, R50e, X30, X40 */
+	   "\\_SB.PCI0.AGP.VID",	/* all others */
+	   );				/* R30, R31 */
+
+IBM_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID");	/* G41 */
+
+static int __init video_init(struct ibm_init_struct *iibm)
+{
+	int ivga;
+
+	vdbg_printk(TPACPI_DBG_INIT, "initializing video subdriver\n");
+
+	IBM_ACPIHANDLE_INIT(vid);
+	IBM_ACPIHANDLE_INIT(vid2);
+
+	if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga)
+		/* G41, assume IVGA doesn't change */
+		vid_handle = vid2_handle;
+
+	if (!vid_handle)
+		/* video switching not supported on R30, R31 */
+		video_supported = TPACPI_VIDEO_NONE;
+	else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd"))
+		/* 570 */
+		video_supported = TPACPI_VIDEO_570;
+	else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd"))
+		/* 600e/x, 770e, 770x */
+		video_supported = TPACPI_VIDEO_770;
+	else
+		/* all others */
+		video_supported = TPACPI_VIDEO_NEW;
+
+	vdbg_printk(TPACPI_DBG_INIT, "video is %s, mode %d\n",
+		str_supported(video_supported != TPACPI_VIDEO_NONE),
+		video_supported);
+
+	return (video_supported != TPACPI_VIDEO_NONE)? 0 : 1;
+}
+
+static void video_exit(void)
+{
+	dbg_printk(TPACPI_DBG_EXIT,
+		   "restoring original video autoswitch mode\n");
+	if (video_autosw_set(video_orig_autosw))
+		printk(IBM_ERR "error while trying to restore original "
+			"video autoswitch mode\n");
+}
+
+static int video_outputsw_get(void)
+{
+	int status = 0;
+	int i;
+
+	switch (video_supported) {
+	case TPACPI_VIDEO_570:
+		if (!acpi_evalf(NULL, &i, "\\_SB.PHS", "dd",
+				 TP_ACPI_VIDEO_570_PHSCMD))
+			return -EIO;
+		status = i & TP_ACPI_VIDEO_570_PHSMASK;
+		break;
+	case TPACPI_VIDEO_770:
+		if (!acpi_evalf(NULL, &i, "\\VCDL", "d"))
+			return -EIO;
+		if (i)
+			status |= TP_ACPI_VIDEO_S_LCD;
+		if (!acpi_evalf(NULL, &i, "\\VCDC", "d"))
+			return -EIO;
+		if (i)
+			status |= TP_ACPI_VIDEO_S_CRT;
+		break;
+	case TPACPI_VIDEO_NEW:
+		if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1) ||
+		    !acpi_evalf(NULL, &i, "\\VCDC", "d"))
+			return -EIO;
+		if (i)
+			status |= TP_ACPI_VIDEO_S_CRT;
+
+		if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0) ||
+		    !acpi_evalf(NULL, &i, "\\VCDL", "d"))
+			return -EIO;
+		if (i)
+			status |= TP_ACPI_VIDEO_S_LCD;
+		if (!acpi_evalf(NULL, &i, "\\VCDD", "d"))
+			return -EIO;
+		if (i)
+			status |= TP_ACPI_VIDEO_S_DVI;
+		break;
+	default:
+		return -ENOSYS;
+	}
+
+	return status;
+}
+
+static int video_outputsw_set(int status)
+{
+	int autosw;
+	int res = 0;
+
+	switch (video_supported) {
+	case TPACPI_VIDEO_570:
+		res = acpi_evalf(NULL, NULL,
+				 "\\_SB.PHS2", "vdd",
+				 TP_ACPI_VIDEO_570_PHS2CMD,
+				 status | TP_ACPI_VIDEO_570_PHS2SET);
+		break;
+	case TPACPI_VIDEO_770:
+		autosw = video_autosw_get();
+		if (autosw < 0)
+			return autosw;
+
+		res = video_autosw_set(1);
+		if (res)
+			return res;
+		res = acpi_evalf(vid_handle, NULL,
+				 "ASWT", "vdd", status * 0x100, 0);
+		if (!autosw && video_autosw_set(autosw)) {
+			printk(IBM_ERR "video auto-switch left enabled due to error\n");
+			return -EIO;
+		}
+		break;
+	case TPACPI_VIDEO_NEW:
+		res = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) &&
+			acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1);
+		break;
+	default:
+		return -ENOSYS;
+	}
+
+	return (res)? 0 : -EIO;
+}
+
+static int video_autosw_get(void)
+{
+	int autosw = 0;
+
+	switch (video_supported) {
+	case TPACPI_VIDEO_570:
+		if (!acpi_evalf(vid_handle, &autosw, "SWIT", "d"))
+			return -EIO;
+		break;
+	case TPACPI_VIDEO_770:
+	case TPACPI_VIDEO_NEW:
+		if (!acpi_evalf(vid_handle, &autosw, "^VDEE", "d"))
+			return -EIO;
+		break;
+	default:
+		return -ENOSYS;
+	}
+
+	return autosw & 1;
+}
+
+static int video_autosw_set(int enable)
+{
+	if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", (enable)? 1 : 0))
+		return -EIO;
+	return 0;
+}
+
+static int video_outputsw_cycle(void)
+{
+	int autosw = video_autosw_get();
+	int res;
+
+	if (autosw < 0)
+		return autosw;
+
+	switch (video_supported) {
+	case TPACPI_VIDEO_570:
+		res = video_autosw_set(1);
+		if (res)
+			return res;
+		res = acpi_evalf(ec_handle, NULL, "_Q16", "v");
+		break;
+	case TPACPI_VIDEO_770:
+	case TPACPI_VIDEO_NEW:
+		res = video_autosw_set(1);
+		if (res)
+			return res;
+		res = acpi_evalf(vid_handle, NULL, "VSWT", "v");
+		break;
+	default:
+		return -ENOSYS;
+	}
+	if (!autosw && video_autosw_set(autosw)) {
+		printk(IBM_ERR "video auto-switch left enabled due to error\n");
+		return -EIO;
+	}
+
+	return (res)? 0 : -EIO;
+}
+
+static int video_expand_toggle(void)
+{
+	switch (video_supported) {
+	case TPACPI_VIDEO_570:
+		return acpi_evalf(ec_handle, NULL, "_Q17", "v")?
+			0 : -EIO;
+	case TPACPI_VIDEO_770:
+		return acpi_evalf(vid_handle, NULL, "VEXP", "v")?
+			0 : -EIO;
+	case TPACPI_VIDEO_NEW:
+		return acpi_evalf(NULL, NULL, "\\VEXP", "v")?
+			0 : -EIO;
+	default:
+		return -ENOSYS;
+	}
+	/* not reached */
+}
+
+static int video_read(char *p)
+{
+	int status, autosw;
+	int len = 0;
+
+	if (video_supported == TPACPI_VIDEO_NONE) {
+		len += sprintf(p + len, "status:\t\tnot supported\n");
+		return len;
+	}
+
+	status = video_outputsw_get();
+	if (status < 0)
+		return status;
+
+	autosw = video_autosw_get();
+	if (autosw < 0)
+		return autosw;
+
+	len += sprintf(p + len, "status:\t\tsupported\n");
+	len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0));
+	len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1));
+	if (video_supported == TPACPI_VIDEO_NEW)
+		len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3));
+	len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0));
+	len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n");
+	len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n");
+	if (video_supported == TPACPI_VIDEO_NEW)
+		len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n");
+	len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n");
+	len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n");
+
+	return len;
+}
+
+static int video_write(char *buf)
+{
+	char *cmd;
+	int enable, disable, status;
+	int res;
+
+	if (video_supported == TPACPI_VIDEO_NONE)
+		return -ENODEV;
+
+	enable = 0;
+	disable = 0;
+
+	while ((cmd = next_cmd(&buf))) {
+		if (strlencmp(cmd, "lcd_enable") == 0) {
+			enable |= TP_ACPI_VIDEO_S_LCD;
+		} else if (strlencmp(cmd, "lcd_disable") == 0) {
+			disable |= TP_ACPI_VIDEO_S_LCD;
+		} else if (strlencmp(cmd, "crt_enable") == 0) {
+			enable |= TP_ACPI_VIDEO_S_CRT;
+		} else if (strlencmp(cmd, "crt_disable") == 0) {
+			disable |= TP_ACPI_VIDEO_S_CRT;
+		} else if (video_supported == TPACPI_VIDEO_NEW &&
+			   strlencmp(cmd, "dvi_enable") == 0) {
+			enable |= TP_ACPI_VIDEO_S_DVI;
+		} else if (video_supported == TPACPI_VIDEO_NEW &&
+			   strlencmp(cmd, "dvi_disable") == 0) {
+			disable |= TP_ACPI_VIDEO_S_DVI;
+		} else if (strlencmp(cmd, "auto_enable") == 0) {
+			res = video_autosw_set(1);
+			if (res)
+				return res;
+		} else if (strlencmp(cmd, "auto_disable") == 0) {
+			res = video_autosw_set(0);
+			if (res)
+				return res;
+		} else if (strlencmp(cmd, "video_switch") == 0) {
+			res = video_outputsw_cycle();
+			if (res)
+				return res;
+		} else if (strlencmp(cmd, "expand_toggle") == 0) {
+			res = video_expand_toggle();
+			if (res)
+				return res;
+		} else
+			return -EINVAL;
+	}
+
+	if (enable || disable) {
+		status = video_outputsw_get();
+		if (status < 0)
+			return status;
+		res = video_outputsw_set((status & ~disable) | enable);
+		if (res)
+			return res;
+	}
+
+	return 0;
+}
+
+static struct ibm_struct video_driver_data = {
+	.name = "video",
+	.read = video_read,
+	.write = video_write,
+	.exit = video_exit,
+};
+
+/*************************************************************************
+ * Light (thinklight) subdriver
+ */
+
+IBM_HANDLE(lght, root, "\\LGHT");	/* A21e, A2xm/p, T20-22, X20-21 */
+IBM_HANDLE(ledb, ec, "LEDB");		/* G4x */
+
+static int __init light_init(struct ibm_init_struct *iibm)
+{
+	vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n");
+
+	IBM_ACPIHANDLE_INIT(ledb);
+	IBM_ACPIHANDLE_INIT(lght);
+	IBM_ACPIHANDLE_INIT(cmos);
+
+	/* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */
+	tp_features.light = (cmos_handle || lght_handle) && !ledb_handle;
+
+	if (tp_features.light)
+		/* light status not supported on
+		   570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */
+		tp_features.light_status =
+			acpi_evalf(ec_handle, NULL, "KBLT", "qv");
+
+	vdbg_printk(TPACPI_DBG_INIT, "light is %s\n",
+		str_supported(tp_features.light));
+
+	return (tp_features.light)? 0 : 1;
+}
+
+static int light_read(char *p)
+{
+	int len = 0;
+	int status = 0;
+
+	if (!tp_features.light) {
+		len += sprintf(p + len, "status:\t\tnot supported\n");
+	} else if (!tp_features.light_status) {
+		len += sprintf(p + len, "status:\t\tunknown\n");
+		len += sprintf(p + len, "commands:\ton, off\n");
+	} else {
+		if (!acpi_evalf(ec_handle, &status, "KBLT", "d"))
+			return -EIO;
+		len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0));
+		len += sprintf(p + len, "commands:\ton, off\n");
+	}
+
+	return len;
+}
+
+static int light_write(char *buf)
+{
+	int cmos_cmd, lght_cmd;
+	char *cmd;
+	int success;
+
+	if (!tp_features.light)
+		return -ENODEV;
+
+	while ((cmd = next_cmd(&buf))) {
+		if (strlencmp(cmd, "on") == 0) {
+			cmos_cmd = 0x0c;
+			lght_cmd = 1;
+		} else if (strlencmp(cmd, "off") == 0) {
+			cmos_cmd = 0x0d;
+			lght_cmd = 0;
+		} else
+			return -EINVAL;
+
+		success = cmos_handle ?
+		    acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) :
+		    acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd);
+		if (!success)
+			return -EIO;
+	}
+
+	return 0;
+}
+
+static struct ibm_struct light_driver_data = {
+	.name = "light",
+	.read = light_read,
+	.write = light_write,
+};
+
+/*************************************************************************
+ * Dock subdriver
+ */
+
+#ifdef CONFIG_THINKPAD_ACPI_DOCK
+
+IBM_HANDLE(dock, root, "\\_SB.GDCK",	/* X30, X31, X40 */
+	   "\\_SB.PCI0.DOCK",	/* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */
+	   "\\_SB.PCI0.PCI1.DOCK",	/* all others */
+	   "\\_SB.PCI.ISA.SLCE",	/* 570 */
+    );				/* A21e,G4x,R30,R31,R32,R40,R40e,R50e */
+
+/* don't list other alternatives as we install a notify handler on the 570 */
+IBM_HANDLE(pci, root, "\\_SB.PCI");	/* 570 */
+
+static struct tp_acpi_drv_struct ibm_dock_acpidriver[2] = {
+	{
+	 .notify = dock_notify,
+	 .handle = &dock_handle,
+	 .type = ACPI_SYSTEM_NOTIFY,
+	},
+	{
+	 .hid = IBM_PCI_HID,
+	 .notify = dock_notify,
+	 .handle = &pci_handle,
+	 .type = ACPI_SYSTEM_NOTIFY,
+	},
+};
+
+static struct ibm_struct dock_driver_data[2] = {
+	{
+	 .name = "dock",
+	 .read = dock_read,
+	 .write = dock_write,
+	 .acpi = &ibm_dock_acpidriver[0],
+	},
+	{
+	 .name = "dock",
+	 .acpi = &ibm_dock_acpidriver[1],
+	},
+};
+
+#define dock_docked() (_sta(dock_handle) & 1)
+
+static int __init dock_init(struct ibm_init_struct *iibm)
+{
+	vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver\n");
+
+	IBM_ACPIHANDLE_INIT(dock);
+
+	vdbg_printk(TPACPI_DBG_INIT, "dock is %s\n",
+		str_supported(dock_handle != NULL));
+
+	return (dock_handle)? 0 : 1;
+}
+
+static int __init dock_init2(struct ibm_init_struct *iibm)
+{
+	int dock2_needed;
+
+	vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver part 2\n");
+
+	if (dock_driver_data[0].flags.acpi_driver_registered &&
+	    dock_driver_data[0].flags.acpi_notify_installed) {
+		IBM_ACPIHANDLE_INIT(pci);
+		dock2_needed = (pci_handle != NULL);
+		vdbg_printk(TPACPI_DBG_INIT,
+			    "dock PCI handler for the TP 570 is %s\n",
+			    str_supported(dock2_needed));
+	} else {
+		vdbg_printk(TPACPI_DBG_INIT,
+		"dock subdriver part 2 not required\n");
+		dock2_needed = 0;
+	}
+
+	return (dock2_needed)? 0 : 1;
+}
+
+static void dock_notify(struct ibm_struct *ibm, u32 event)
+{
+	int docked = dock_docked();
+	int pci = ibm->acpi->hid && strstr(ibm->acpi->hid, IBM_PCI_HID);
+
+	if (event == 1 && !pci)	/* 570 */
+		acpi_bus_generate_event(ibm->acpi->device, event, 1);	/* button */
+	else if (event == 1 && pci)	/* 570 */
+		acpi_bus_generate_event(ibm->acpi->device, event, 3);	/* dock */
+	else if (event == 3 && docked)
+		acpi_bus_generate_event(ibm->acpi->device, event, 1);	/* button */
+	else if (event == 3 && !docked)
+		acpi_bus_generate_event(ibm->acpi->device, event, 2);	/* undock */
+	else if (event == 0 && docked)
+		acpi_bus_generate_event(ibm->acpi->device, event, 3);	/* dock */
+	else {
+		printk(IBM_ERR "unknown dock event %d, status %d\n",
+		       event, _sta(dock_handle));
+		acpi_bus_generate_event(ibm->acpi->device, event, 0);	/* unknown */
+	}
+}
+
+static int dock_read(char *p)
+{
+	int len = 0;
+	int docked = dock_docked();
+
+	if (!dock_handle)
+		len += sprintf(p + len, "status:\t\tnot supported\n");
+	else if (!docked)
+		len += sprintf(p + len, "status:\t\tundocked\n");
+	else {
+		len += sprintf(p + len, "status:\t\tdocked\n");
+		len += sprintf(p + len, "commands:\tdock, undock\n");
+	}
+
+	return len;
+}
+
+static int dock_write(char *buf)
+{
+	char *cmd;
+
+	if (!dock_docked())
+		return -ENODEV;
+
+	while ((cmd = next_cmd(&buf))) {
+		if (strlencmp(cmd, "undock") == 0) {
+			if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) ||
+			    !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1))
+				return -EIO;
+		} else if (strlencmp(cmd, "dock") == 0) {
+			if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1))
+				return -EIO;
+		} else
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_THINKPAD_ACPI_DOCK */
+
+/*************************************************************************
+ * Bay subdriver
+ */
+
+#ifdef CONFIG_THINKPAD_ACPI_BAY
+IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST",	/* 570 */
+	   "\\_SB.PCI0.IDE0.IDES.IDSM",	/* 600e/x, 770e, 770x */
+	   "\\_SB.PCI0.SATA.SCND.MSTR",	/* T60, X60, Z60 */
+	   "\\_SB.PCI0.IDE0.SCND.MSTR",	/* all others */
+	   );				/* A21e, R30, R31 */
+IBM_HANDLE(bay_ej, bay, "_EJ3",	/* 600e/x, A2xm/p, A3x */
+	   "_EJ0",		/* all others */
+	   );			/* 570,A21e,G4x,R30,R31,R32,R40e,R50e */
+IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV",	/* A3x, R32 */
+	   "\\_SB.PCI0.IDE0.IDEP.IDPS",	/* 600e/x, 770e, 770x */
+	   );				/* all others */
+IBM_HANDLE(bay2_ej, bay2, "_EJ3",	/* 600e/x, 770e, A3x */
+	   "_EJ0",			/* 770x */
+	   );				/* all others */
+
+static int __init bay_init(struct ibm_init_struct *iibm)
+{
+	vdbg_printk(TPACPI_DBG_INIT, "initializing bay subdriver\n");
+
+	IBM_ACPIHANDLE_INIT(bay);
+	if (bay_handle)
+		IBM_ACPIHANDLE_INIT(bay_ej);
+	IBM_ACPIHANDLE_INIT(bay2);
+	if (bay2_handle)
+		IBM_ACPIHANDLE_INIT(bay2_ej);
+
+	tp_features.bay_status = bay_handle &&
+		acpi_evalf(bay_handle, NULL, "_STA", "qv");
+	tp_features.bay_status2 = bay2_handle &&
+		acpi_evalf(bay2_handle, NULL, "_STA", "qv");
+
+	tp_features.bay_eject = bay_handle && bay_ej_handle &&
+		(strlencmp(bay_ej_path, "_EJ0") == 0 || experimental);
+	tp_features.bay_eject2 = bay2_handle && bay2_ej_handle &&
+		(strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental);
+
+	vdbg_printk(TPACPI_DBG_INIT,
+		"bay 1: status %s, eject %s; bay 2: status %s, eject %s\n",
+		str_supported(tp_features.bay_status),
+		str_supported(tp_features.bay_eject),
+		str_supported(tp_features.bay_status2),
+		str_supported(tp_features.bay_eject2));
+
+	return (tp_features.bay_status || tp_features.bay_eject ||
+		tp_features.bay_status2 || tp_features.bay_eject2)? 0 : 1;
+}
+
+static void bay_notify(struct ibm_struct *ibm, u32 event)
+{
+	acpi_bus_generate_event(ibm->acpi->device, event, 0);
+}
+
+#define bay_occupied(b) (_sta(b##_handle) & 1)
+
+static int bay_read(char *p)
+{
+	int len = 0;
+	int occupied = bay_occupied(bay);
+	int occupied2 = bay_occupied(bay2);
+	int eject, eject2;
+
+	len += sprintf(p + len, "status:\t\t%s\n",
+		tp_features.bay_status ?
+			(occupied ? "occupied" : "unoccupied") :
+				"not supported");
+	if (tp_features.bay_status2)
+		len += sprintf(p + len, "status2:\t%s\n", occupied2 ?
+			       "occupied" : "unoccupied");
+
+	eject = tp_features.bay_eject && occupied;
+	eject2 = tp_features.bay_eject2 && occupied2;
+
+	if (eject && eject2)
+		len += sprintf(p + len, "commands:\teject, eject2\n");
+	else if (eject)
+		len += sprintf(p + len, "commands:\teject\n");
+	else if (eject2)
+		len += sprintf(p + len, "commands:\teject2\n");
+
+	return len;
+}
+
+static int bay_write(char *buf)
+{
+	char *cmd;
+
+	if (!tp_features.bay_eject && !tp_features.bay_eject2)
+		return -ENODEV;
+
+	while ((cmd = next_cmd(&buf))) {
+		if (tp_features.bay_eject && strlencmp(cmd, "eject") == 0) {
+			if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1))
+				return -EIO;
+		} else if (tp_features.bay_eject2 &&
+			   strlencmp(cmd, "eject2") == 0) {
+			if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1))
+				return -EIO;
+		} else
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct tp_acpi_drv_struct ibm_bay_acpidriver = {
+	.notify = bay_notify,
+	.handle = &bay_handle,
+	.type = ACPI_SYSTEM_NOTIFY,
+};
+
+static struct ibm_struct bay_driver_data = {
+	.name = "bay",
+	.read = bay_read,
+	.write = bay_write,
+	.acpi = &ibm_bay_acpidriver,
+};
+
+#endif /* CONFIG_THINKPAD_ACPI_BAY */
+
+/*************************************************************************
+ * CMOS subdriver
+ */
+
+/* sysfs cmos_command -------------------------------------------------- */
+static ssize_t cmos_command_store(struct device *dev,
+			    struct device_attribute *attr,
+			    const char *buf, size_t count)
+{
+	unsigned long cmos_cmd;
+	int res;
+
+	if (parse_strtoul(buf, 21, &cmos_cmd))
+		return -EINVAL;
+
+	res = issue_thinkpad_cmos_command(cmos_cmd);
+	return (res)? res : count;
+}
+
+static struct device_attribute dev_attr_cmos_command =
+	__ATTR(cmos_command, S_IWUSR, NULL, cmos_command_store);
+
+/* --------------------------------------------------------------------- */
+
+static int __init cmos_init(struct ibm_init_struct *iibm)
+{
+	int res;
+
+	vdbg_printk(TPACPI_DBG_INIT,
+		"initializing cmos commands subdriver\n");
+
+	IBM_ACPIHANDLE_INIT(cmos);
+
+	vdbg_printk(TPACPI_DBG_INIT, "cmos commands are %s\n",
+		str_supported(cmos_handle != NULL));
+
+	res = device_create_file(&tpacpi_pdev->dev, &dev_attr_cmos_command);
+	if (res)
+		return res;
+
+	return (cmos_handle)? 0 : 1;
+}
+
+static void cmos_exit(void)
+{
+	device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command);
+}
+
+static int cmos_read(char *p)
+{
+	int len = 0;
+
+	/* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
+	   R30, R31, T20-22, X20-21 */
+	if (!cmos_handle)
+		len += sprintf(p + len, "status:\t\tnot supported\n");
+	else {
+		len += sprintf(p + len, "status:\t\tsupported\n");
+		len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n");
+	}
+
+	return len;
+}
+
+static int cmos_write(char *buf)
+{
+	char *cmd;
+	int cmos_cmd, res;
+
+	while ((cmd = next_cmd(&buf))) {
+		if (sscanf(cmd, "%u", &cmos_cmd) == 1 &&
+		    cmos_cmd >= 0 && cmos_cmd <= 21) {
+			/* cmos_cmd set */
+		} else
+			return -EINVAL;
+
+		res = issue_thinkpad_cmos_command(cmos_cmd);
+		if (res)
+			return res;
+	}
+
+	return 0;
+}
+
+static struct ibm_struct cmos_driver_data = {
+	.name = "cmos",
+	.read = cmos_read,
+	.write = cmos_write,
+	.exit = cmos_exit,
+};
+
+/*************************************************************************
+ * LED subdriver
+ */
+
+static enum led_access_mode led_supported;
+
+IBM_HANDLE(led, ec, "SLED",	/* 570 */
+	   "SYSL",		/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
+	   "LED",		/* all others */
+	   );			/* R30, R31 */
+
+static int __init led_init(struct ibm_init_struct *iibm)
+{
+	vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n");
+
+	IBM_ACPIHANDLE_INIT(led);
+
+	if (!led_handle)
+		/* led not supported on R30, R31 */
+		led_supported = TPACPI_LED_NONE;
+	else if (strlencmp(led_path, "SLED") == 0)
+		/* 570 */
+		led_supported = TPACPI_LED_570;
+	else if (strlencmp(led_path, "SYSL") == 0)
+		/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
+		led_supported = TPACPI_LED_OLD;
+	else
+		/* all others */
+		led_supported = TPACPI_LED_NEW;
+
+	vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n",
+		str_supported(led_supported), led_supported);
+
+	return (led_supported != TPACPI_LED_NONE)? 0 : 1;
+}
+
+#define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking"))
+
+static int led_read(char *p)
+{
+	int len = 0;
+
+	if (!led_supported) {
+		len += sprintf(p + len, "status:\t\tnot supported\n");
+		return len;
+	}
+	len += sprintf(p + len, "status:\t\tsupported\n");
+
+	if (led_supported == TPACPI_LED_570) {
+		/* 570 */
+		int i, status;
+		for (i = 0; i < 8; i++) {
+			if (!acpi_evalf(ec_handle,
+					&status, "GLED", "dd", 1 << i))
+				return -EIO;
+			len += sprintf(p + len, "%d:\t\t%s\n",
+				       i, led_status(status));
+		}
+	}
+
+	len += sprintf(p + len, "commands:\t"
+		       "<led> on, <led> off, <led> blink (<led> is 0-7)\n");
+
+	return len;
+}
+
+/* off, on, blink */
+static const int led_sled_arg1[] = { 0, 1, 3 };
+static const int led_exp_hlbl[] = { 0, 0, 1 };	/* led# * */
+static const int led_exp_hlcl[] = { 0, 1, 1 };	/* led# * */
+static const int led_led_arg1[] = { 0, 0x80, 0xc0 };
+
+static int led_write(char *buf)
+{
+	char *cmd;
+	int led, ind, ret;
+
+	if (!led_supported)
+		return -ENODEV;
+
+	while ((cmd = next_cmd(&buf))) {
+		if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7)
+			return -EINVAL;
+
+		if (strstr(cmd, "off")) {
+			ind = 0;
+		} else if (strstr(cmd, "on")) {
+			ind = 1;
+		} else if (strstr(cmd, "blink")) {
+			ind = 2;
+		} else
+			return -EINVAL;
+
+		if (led_supported == TPACPI_LED_570) {
+			/* 570 */
+			led = 1 << led;
+			if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
+					led, led_sled_arg1[ind]))
+				return -EIO;
+		} else if (led_supported == TPACPI_LED_OLD) {
+			/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
+			led = 1 << led;
+			ret = ec_write(TPACPI_LED_EC_HLMS, led);
+			if (ret >= 0)
+				ret =
+				    ec_write(TPACPI_LED_EC_HLBL,
+				    	     led * led_exp_hlbl[ind]);
+			if (ret >= 0)
+				ret =
+				    ec_write(TPACPI_LED_EC_HLCL,
+				    	     led * led_exp_hlcl[ind]);
+			if (ret < 0)
+				return ret;
+		} else {
+			/* all others */
+			if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
+					led, led_led_arg1[ind]))
+				return -EIO;
+		}
+	}
+
+	return 0;
+}
+
+static struct ibm_struct led_driver_data = {
+	.name = "led",
+	.read = led_read,
+	.write = led_write,
+};
+
+/*************************************************************************
+ * Beep subdriver
+ */
+
+IBM_HANDLE(beep, ec, "BEEP");	/* all except R30, R31 */
+
+static int __init beep_init(struct ibm_init_struct *iibm)
+{
+	vdbg_printk(TPACPI_DBG_INIT, "initializing beep subdriver\n");
+
+	IBM_ACPIHANDLE_INIT(beep);
+
+	vdbg_printk(TPACPI_DBG_INIT, "beep is %s\n",
+		str_supported(beep_handle != NULL));
+
+	return (beep_handle)? 0 : 1;
+}
+
+static int beep_read(char *p)
+{
+	int len = 0;
+
+	if (!beep_handle)
+		len += sprintf(p + len, "status:\t\tnot supported\n");
+	else {
+		len += sprintf(p + len, "status:\t\tsupported\n");
+		len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n");
+	}
+
+	return len;
+}
+
+static int beep_write(char *buf)
+{
+	char *cmd;
+	int beep_cmd;
+
+	if (!beep_handle)
+		return -ENODEV;
+
+	while ((cmd = next_cmd(&buf))) {
+		if (sscanf(cmd, "%u", &beep_cmd) == 1 &&
+		    beep_cmd >= 0 && beep_cmd <= 17) {
+			/* beep_cmd set */
+		} else
+			return -EINVAL;
+		if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0))
+			return -EIO;
+	}
+
+	return 0;
+}
+
+static struct ibm_struct beep_driver_data = {
+	.name = "beep",
+	.read = beep_read,
+	.write = beep_write,
+};
+
+/*************************************************************************
+ * Thermal subdriver
+ */
+
+static enum thermal_access_mode thermal_read_mode;
+
+/* sysfs temp##_input -------------------------------------------------- */
+
+static ssize_t thermal_temp_input_show(struct device *dev,
+			   struct device_attribute *attr,
+			   char *buf)
+{
+	struct sensor_device_attribute *sensor_attr =
+					to_sensor_dev_attr(attr);
+	int idx = sensor_attr->index;
+	s32 value;
+	int res;
+
+	res = thermal_get_sensor(idx, &value);
+	if (res)
+		return res;
+	if (value == TP_EC_THERMAL_TMP_NA * 1000)
+		return -ENXIO;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", value);
+}
+
+#define THERMAL_SENSOR_ATTR_TEMP(_idxA, _idxB) \
+	 SENSOR_ATTR(temp##_idxA##_input, S_IRUGO, thermal_temp_input_show, NULL, _idxB)
+
+static struct sensor_device_attribute sensor_dev_attr_thermal_temp_input[] = {
+	THERMAL_SENSOR_ATTR_TEMP(1, 0),
+	THERMAL_SENSOR_ATTR_TEMP(2, 1),
+	THERMAL_SENSOR_ATTR_TEMP(3, 2),
+	THERMAL_SENSOR_ATTR_TEMP(4, 3),
+	THERMAL_SENSOR_ATTR_TEMP(5, 4),
+	THERMAL_SENSOR_ATTR_TEMP(6, 5),
+	THERMAL_SENSOR_ATTR_TEMP(7, 6),
+	THERMAL_SENSOR_ATTR_TEMP(8, 7),
+	THERMAL_SENSOR_ATTR_TEMP(9, 8),
+	THERMAL_SENSOR_ATTR_TEMP(10, 9),
+	THERMAL_SENSOR_ATTR_TEMP(11, 10),
+	THERMAL_SENSOR_ATTR_TEMP(12, 11),
+	THERMAL_SENSOR_ATTR_TEMP(13, 12),
+	THERMAL_SENSOR_ATTR_TEMP(14, 13),
+	THERMAL_SENSOR_ATTR_TEMP(15, 14),
+	THERMAL_SENSOR_ATTR_TEMP(16, 15),
+};
+
+#define THERMAL_ATTRS(X) \
+	&sensor_dev_attr_thermal_temp_input[X].dev_attr.attr
+
+static struct attribute *thermal_temp_input_attr[] = {
+	THERMAL_ATTRS(8),
+	THERMAL_ATTRS(9),
+	THERMAL_ATTRS(10),
+	THERMAL_ATTRS(11),
+	THERMAL_ATTRS(12),
+	THERMAL_ATTRS(13),
+	THERMAL_ATTRS(14),
+	THERMAL_ATTRS(15),
+	THERMAL_ATTRS(0),
+	THERMAL_ATTRS(1),
+	THERMAL_ATTRS(2),
+	THERMAL_ATTRS(3),
+	THERMAL_ATTRS(4),
+	THERMAL_ATTRS(5),
+	THERMAL_ATTRS(6),
+	THERMAL_ATTRS(7),
+	NULL
+};
+
+static const struct attribute_group thermal_temp_input16_group = {
+	.attrs = thermal_temp_input_attr
+};
+
+static const struct attribute_group thermal_temp_input8_group = {
+	.attrs = &thermal_temp_input_attr[8]
+};
+
+#undef THERMAL_SENSOR_ATTR_TEMP
+#undef THERMAL_ATTRS
+
+/* --------------------------------------------------------------------- */
+
+static int __init thermal_init(struct ibm_init_struct *iibm)
+{
+	u8 t, ta1, ta2;
+	int i;
+	int acpi_tmp7;
+	int res;
+
+	vdbg_printk(TPACPI_DBG_INIT, "initializing thermal subdriver\n");
+
+	acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
+
+	if (ibm_thinkpad_ec_found && experimental) {
+		/*
+		 * Direct EC access mode: sensors at registers
+		 * 0x78-0x7F, 0xC0-0xC7.  Registers return 0x00 for
+		 * non-implemented, thermal sensors return 0x80 when
+		 * not available
+		 */
+
+		ta1 = ta2 = 0;
+		for (i = 0; i < 8; i++) {
+			if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) {
+				ta1 |= t;
+			} else {
+				ta1 = 0;
+				break;
+			}
+			if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) {
+				ta2 |= t;
+			} else {
+				ta1 = 0;
+				break;
+			}
+		}
+		if (ta1 == 0) {
+			/* This is sheer paranoia, but we handle it anyway */
+			if (acpi_tmp7) {
+				printk(IBM_ERR
+				       "ThinkPad ACPI EC access misbehaving, "
+				       "falling back to ACPI TMPx access mode\n");
+				thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
+			} else {
+				printk(IBM_ERR
+				       "ThinkPad ACPI EC access misbehaving, "
+				       "disabling thermal sensors access\n");
+				thermal_read_mode = TPACPI_THERMAL_NONE;
+			}
+		} else {
+			thermal_read_mode =
+			    (ta2 != 0) ?
+			    TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8;
+		}
+	} else if (acpi_tmp7) {
+		if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
+			/* 600e/x, 770e, 770x */
+			thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT;
+		} else {
+			/* Standard ACPI TMPx access, max 8 sensors */
+			thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
+		}
+	} else {
+		/* temperatures not supported on 570, G4x, R30, R31, R32 */
+		thermal_read_mode = TPACPI_THERMAL_NONE;
+	}
+
+	vdbg_printk(TPACPI_DBG_INIT, "thermal is %s, mode %d\n",
+		str_supported(thermal_read_mode != TPACPI_THERMAL_NONE),
+		thermal_read_mode);
+
+	switch(thermal_read_mode) {
+	case TPACPI_THERMAL_TPEC_16:
+		res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+				&thermal_temp_input16_group);
+		if (res)
+			return res;
+		break;
+	case TPACPI_THERMAL_TPEC_8:
+	case TPACPI_THERMAL_ACPI_TMP07:
+	case TPACPI_THERMAL_ACPI_UPDT:
+		res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+				&thermal_temp_input8_group);
+		if (res)
+			return res;
+		break;
+	case TPACPI_THERMAL_NONE:
+	default:
+		return 1;
+	}
+
+	return 0;
+}
+
+static void thermal_exit(void)
+{
+	switch(thermal_read_mode) {
+	case TPACPI_THERMAL_TPEC_16:
+		sysfs_remove_group(&tpacpi_pdev->dev.kobj,
+				   &thermal_temp_input16_group);
+		break;
+	case TPACPI_THERMAL_TPEC_8:
+	case TPACPI_THERMAL_ACPI_TMP07:
+	case TPACPI_THERMAL_ACPI_UPDT:
+		sysfs_remove_group(&tpacpi_pdev->dev.kobj,
+				   &thermal_temp_input16_group);
+		break;
+	case TPACPI_THERMAL_NONE:
+	default:
+		break;
+	}
+}
+
+/* idx is zero-based */
+static int thermal_get_sensor(int idx, s32 *value)
+{
+	int t;
+	s8 tmp;
+	char tmpi[5];
+
+	t = TP_EC_THERMAL_TMP0;
+
+	switch (thermal_read_mode) {
+#if TPACPI_MAX_THERMAL_SENSORS >= 16
+	case TPACPI_THERMAL_TPEC_16:
+		if (idx >= 8 && idx <= 15) {
+			t = TP_EC_THERMAL_TMP8;
+			idx -= 8;
+		}
+		/* fallthrough */
+#endif
+	case TPACPI_THERMAL_TPEC_8:
+		if (idx <= 7) {
+			if (!acpi_ec_read(t + idx, &tmp))
+				return -EIO;
+			*value = tmp * 1000;
+			return 0;
+		}
+		break;
+
+	case TPACPI_THERMAL_ACPI_UPDT:
+		if (idx <= 7) {
+			snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
+			if (!acpi_evalf(ec_handle, NULL, "UPDT", "v"))
+				return -EIO;
+			if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
+				return -EIO;
+			*value = (t - 2732) * 100;
+			return 0;
+		}
+		break;
+
+	case TPACPI_THERMAL_ACPI_TMP07:
+		if (idx <= 7) {
+			snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
+			if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
+				return -EIO;
+			*value = t * 1000;
+			return 0;
+		}
+		break;
+
+	case TPACPI_THERMAL_NONE:
+	default:
+		return -ENOSYS;
+	}
+
+	return -EINVAL;
+}
+
+static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s)
+{
+	int res, i;
+	int n;
+
+	n = 8;
+	i = 0;
+
+	if (!s)
+		return -EINVAL;
+
+	if (thermal_read_mode == TPACPI_THERMAL_TPEC_16)
+		n = 16;
+
+	for(i = 0 ; i < n; i++) {
+		res = thermal_get_sensor(i, &s->temp[i]);
+		if (res)
+			return res;
+	}
+
+	return n;
+}
+
+static int thermal_read(char *p)
+{
+	int len = 0;
+	int n, i;
+	struct ibm_thermal_sensors_struct t;
+
+	n = thermal_get_sensors(&t);
+	if (unlikely(n < 0))
+		return n;
+
+	len += sprintf(p + len, "temperatures:\t");
+
+	if (n > 0) {
+		for (i = 0; i < (n - 1); i++)
+			len += sprintf(p + len, "%d ", t.temp[i] / 1000);
+		len += sprintf(p + len, "%d\n", t.temp[i] / 1000);
+	} else
+		len += sprintf(p + len, "not supported\n");
+
+	return len;
+}
+
+static struct ibm_struct thermal_driver_data = {
+	.name = "thermal",
+	.read = thermal_read,
+	.exit = thermal_exit,
+};
+
+/*************************************************************************
+ * EC Dump subdriver
+ */
+
+static u8 ecdump_regs[256];
+
+static int ecdump_read(char *p)
+{
+	int len = 0;
+	int i, j;
+	u8 v;
+
+	len += sprintf(p + len, "EC      "
+		       " +00 +01 +02 +03 +04 +05 +06 +07"
+		       " +08 +09 +0a +0b +0c +0d +0e +0f\n");
+	for (i = 0; i < 256; i += 16) {
+		len += sprintf(p + len, "EC 0x%02x:", i);
+		for (j = 0; j < 16; j++) {
+			if (!acpi_ec_read(i + j, &v))
+				break;
+			if (v != ecdump_regs[i + j])
+				len += sprintf(p + len, " *%02x", v);
+			else
+				len += sprintf(p + len, "  %02x", v);
+			ecdump_regs[i + j] = v;
+		}
+		len += sprintf(p + len, "\n");
+		if (j != 16)
+			break;
+	}
+
+	/* These are way too dangerous to advertise openly... */
+#if 0
+	len += sprintf(p + len, "commands:\t0x<offset> 0x<value>"
+		       " (<offset> is 00-ff, <value> is 00-ff)\n");
+	len += sprintf(p + len, "commands:\t0x<offset> <value>  "
+		       " (<offset> is 00-ff, <value> is 0-255)\n");
+#endif
+	return len;
+}
+
+static int ecdump_write(char *buf)
+{
+	char *cmd;
+	int i, v;
+
+	while ((cmd = next_cmd(&buf))) {
+		if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) {
+			/* i and v set */
+		} else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) {
+			/* i and v set */
+		} else
+			return -EINVAL;
+		if (i >= 0 && i < 256 && v >= 0 && v < 256) {
+			if (!acpi_ec_write(i, v))
+				return -EIO;
+		} else
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct ibm_struct ecdump_driver_data = {
+	.name = "ecdump",
+	.read = ecdump_read,
+	.write = ecdump_write,
+	.flags.experimental = 1,
+};
+
+/*************************************************************************
+ * Backlight/brightness subdriver
+ */
+
+static struct backlight_device *ibm_backlight_device = NULL;
+
+static struct backlight_ops ibm_backlight_data = {
+        .get_brightness = brightness_get,
+        .update_status  = brightness_update_status,
+};
+
+static int __init brightness_init(struct ibm_init_struct *iibm)
+{
+	int b;
+
+	vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n");
+
+	b = brightness_get(NULL);
+	if (b < 0)
+		return b;
+
+	ibm_backlight_device = backlight_device_register(
+					TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL,
+					&ibm_backlight_data);
+	if (IS_ERR(ibm_backlight_device)) {
+		printk(IBM_ERR "Could not register backlight device\n");
+		return PTR_ERR(ibm_backlight_device);
+	}
+	vdbg_printk(TPACPI_DBG_INIT, "brightness is supported\n");
+
+	ibm_backlight_device->props.max_brightness = 7;
+	ibm_backlight_device->props.brightness = b;
+	backlight_update_status(ibm_backlight_device);
+
+	return 0;
+}
+
+static void brightness_exit(void)
+{
+	if (ibm_backlight_device) {
+		vdbg_printk(TPACPI_DBG_EXIT,
+			    "calling backlight_device_unregister()\n");
+		backlight_device_unregister(ibm_backlight_device);
+		ibm_backlight_device = NULL;
+	}
+}
+
+static int brightness_update_status(struct backlight_device *bd)
+{
+	return brightness_set(
+		(bd->props.fb_blank == FB_BLANK_UNBLANK &&
+		 bd->props.power == FB_BLANK_UNBLANK) ?
+				bd->props.brightness : 0);
+}
+
+static int brightness_get(struct backlight_device *bd)
+{
+	u8 level;
+	if (!acpi_ec_read(brightness_offset, &level))
+		return -EIO;
+
+	level &= 0x7;
+
+	return level;
+}
+
+static int brightness_set(int value)
+{
+	int cmos_cmd, inc, i;
+	int current_value = brightness_get(NULL);
+
+	value &= 7;
+
+	cmos_cmd = value > current_value ? TP_CMOS_BRIGHTNESS_UP : TP_CMOS_BRIGHTNESS_DOWN;
+	inc = value > current_value ? 1 : -1;
+	for (i = current_value; i != value; i += inc) {
+		if (issue_thinkpad_cmos_command(cmos_cmd))
+			return -EIO;
+		if (!acpi_ec_write(brightness_offset, i + inc))
+			return -EIO;
+	}
+
+	return 0;
+}
+
+static int brightness_read(char *p)
+{
+	int len = 0;
+	int level;
+
+	if ((level = brightness_get(NULL)) < 0) {
+		len += sprintf(p + len, "level:\t\tunreadable\n");
+	} else {
+		len += sprintf(p + len, "level:\t\t%d\n", level & 0x7);
+		len += sprintf(p + len, "commands:\tup, down\n");
+		len += sprintf(p + len, "commands:\tlevel <level>"
+			       " (<level> is 0-7)\n");
+	}
+
+	return len;
+}
+
+static int brightness_write(char *buf)
+{
+	int level;
+	int new_level;
+	char *cmd;
+
+	while ((cmd = next_cmd(&buf))) {
+		if ((level = brightness_get(NULL)) < 0)
+			return level;
+		level &= 7;
+
+		if (strlencmp(cmd, "up") == 0) {
+			new_level = level == 7 ? 7 : level + 1;
+		} else if (strlencmp(cmd, "down") == 0) {
+			new_level = level == 0 ? 0 : level - 1;
+		} else if (sscanf(cmd, "level %d", &new_level) == 1 &&
+			   new_level >= 0 && new_level <= 7) {
+			/* new_level set */
+		} else
+			return -EINVAL;
+
+		brightness_set(new_level);
+	}
+
+	return 0;
+}
+
+static struct ibm_struct brightness_driver_data = {
+	.name = "brightness",
+	.read = brightness_read,
+	.write = brightness_write,
+	.exit = brightness_exit,
+};
+
+/*************************************************************************
+ * Volume subdriver
+ */
+
+static int volume_read(char *p)
+{
+	int len = 0;
+	u8 level;
+
+	if (!acpi_ec_read(volume_offset, &level)) {
+		len += sprintf(p + len, "level:\t\tunreadable\n");
+	} else {
+		len += sprintf(p + len, "level:\t\t%d\n", level & 0xf);
+		len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6));
+		len += sprintf(p + len, "commands:\tup, down, mute\n");
+		len += sprintf(p + len, "commands:\tlevel <level>"
+			       " (<level> is 0-15)\n");
+	}
+
+	return len;
+}
+
+static int volume_write(char *buf)
+{
+	int cmos_cmd, inc, i;
+	u8 level, mute;
+	int new_level, new_mute;
+	char *cmd;
+
+	while ((cmd = next_cmd(&buf))) {
+		if (!acpi_ec_read(volume_offset, &level))
+			return -EIO;
+		new_mute = mute = level & 0x40;
+		new_level = level = level & 0xf;
+
+		if (strlencmp(cmd, "up") == 0) {
+			if (mute)
+				new_mute = 0;
+			else
+				new_level = level == 15 ? 15 : level + 1;
+		} else if (strlencmp(cmd, "down") == 0) {
+			if (mute)
+				new_mute = 0;
+			else
+				new_level = level == 0 ? 0 : level - 1;
+		} else if (sscanf(cmd, "level %d", &new_level) == 1 &&
+			   new_level >= 0 && new_level <= 15) {
+			/* new_level set */
+		} else if (strlencmp(cmd, "mute") == 0) {
+			new_mute = 0x40;
+		} else
+			return -EINVAL;
+
+		if (new_level != level) {	/* mute doesn't change */
+			cmos_cmd = new_level > level ? TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN;
+			inc = new_level > level ? 1 : -1;
+
+			if (mute && (issue_thinkpad_cmos_command(cmos_cmd) ||
+				     !acpi_ec_write(volume_offset, level)))
+				return -EIO;
+
+			for (i = level; i != new_level; i += inc)
+				if (issue_thinkpad_cmos_command(cmos_cmd) ||
+				    !acpi_ec_write(volume_offset, i + inc))
+					return -EIO;
+
+			if (mute && (issue_thinkpad_cmos_command(TP_CMOS_VOLUME_MUTE) ||
+				     !acpi_ec_write(volume_offset,
+						    new_level + mute)))
+				return -EIO;
+		}
+
+		if (new_mute != mute) {	/* level doesn't change */
+			cmos_cmd = new_mute ? TP_CMOS_VOLUME_MUTE : TP_CMOS_VOLUME_UP;
+
+			if (issue_thinkpad_cmos_command(cmos_cmd) ||
+			    !acpi_ec_write(volume_offset, level + new_mute))
+				return -EIO;
+		}
+	}
+
+	return 0;
+}
+
+static struct ibm_struct volume_driver_data = {
+	.name = "volume",
+	.read = volume_read,
+	.write = volume_write,
+};
+
+/*************************************************************************
+ * Fan subdriver
+ */
+
+/*
+ * FAN ACCESS MODES
+ *
+ * TPACPI_FAN_RD_ACPI_GFAN:
+ * 	ACPI GFAN method: returns fan level
+ *
+ * 	see TPACPI_FAN_WR_ACPI_SFAN
+ * 	EC 0x2f (HFSP) not available if GFAN exists
+ *
+ * TPACPI_FAN_WR_ACPI_SFAN:
+ * 	ACPI SFAN method: sets fan level, 0 (stop) to 7 (max)
+ *
+ * 	EC 0x2f (HFSP) might be available *for reading*, but do not use
+ * 	it for writing.
+ *
+ * TPACPI_FAN_WR_TPEC:
+ * 	ThinkPad EC register 0x2f (HFSP): fan control loop mode
+ * 	Supported on almost all ThinkPads
+ *
+ * 	Fan speed changes of any sort (including those caused by the
+ * 	disengaged mode) are usually done slowly by the firmware as the
+ * 	maximum ammount of fan duty cycle change per second seems to be
+ * 	limited.
+ *
+ * 	Reading is not available if GFAN exists.
+ * 	Writing is not available if SFAN exists.
+ *
+ * 	Bits
+ *	 7	automatic mode engaged;
+ *  		(default operation mode of the ThinkPad)
+ * 		fan level is ignored in this mode.
+ *	 6	full speed mode (takes precedence over bit 7);
+ *		not available on all thinkpads.  May disable
+ *		the tachometer while the fan controller ramps up
+ *		the speed (which can take up to a few *minutes*).
+ *		Speeds up fan to 100% duty-cycle, which is far above
+ *		the standard RPM levels.  It is not impossible that
+ *		it could cause hardware damage.
+ *	5-3	unused in some models.  Extra bits for fan level
+ *		in others, but still useless as all values above
+ *		7 map to the same speed as level 7 in these models.
+ *	2-0	fan level (0..7 usually)
+ *			0x00 = stop
+ * 			0x07 = max (set when temperatures critical)
+ * 		Some ThinkPads may have other levels, see
+ * 		TPACPI_FAN_WR_ACPI_FANS (X31/X40/X41)
+ *
+ *	FIRMWARE BUG: on some models, EC 0x2f might not be initialized at
+ *	boot. Apparently the EC does not intialize it, so unless ACPI DSDT
+ *	does so, its initial value is meaningless (0x07).
+ *
+ *	For firmware bugs, refer to:
+ *	http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
+ *
+ * 	----
+ *
+ *	ThinkPad EC register 0x84 (LSB), 0x85 (MSB):
+ *	Main fan tachometer reading (in RPM)
+ *
+ *	This register is present on all ThinkPads with a new-style EC, and
+ *	it is known not to be present on the A21m/e, and T22, as there is
+ *	something else in offset 0x84 according to the ACPI DSDT.  Other
+ *	ThinkPads from this same time period (and earlier) probably lack the
+ *	tachometer as well.
+ *
+ *	Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare
+ *	was never fixed by IBM to report the EC firmware version string
+ *	probably support the tachometer (like the early X models), so
+ *	detecting it is quite hard.  We need more data to know for sure.
+ *
+ *	FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings
+ *	might result.
+ *
+ *	FIRMWARE BUG: may go stale while the EC is switching to full speed
+ *	mode.
+ *
+ *	For firmware bugs, refer to:
+ *	http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
+ *
+ * TPACPI_FAN_WR_ACPI_FANS:
+ *	ThinkPad X31, X40, X41.  Not available in the X60.
+ *
+ *	FANS ACPI handle: takes three arguments: low speed, medium speed,
+ *	high speed.  ACPI DSDT seems to map these three speeds to levels
+ *	as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH
+ *	(this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3")
+ *
+ * 	The speeds are stored on handles
+ * 	(FANA:FAN9), (FANC:FANB), (FANE:FAND).
+ *
+ * 	There are three default speed sets, acessible as handles:
+ * 	FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H
+ *
+ * 	ACPI DSDT switches which set is in use depending on various
+ * 	factors.
+ *
+ * 	TPACPI_FAN_WR_TPEC is also available and should be used to
+ * 	command the fan.  The X31/X40/X41 seems to have 8 fan levels,
+ * 	but the ACPI tables just mention level 7.
+ */
+
+static enum fan_status_access_mode fan_status_access_mode;
+static enum fan_control_access_mode fan_control_access_mode;
+static enum fan_control_commands fan_control_commands;
+
+static u8 fan_control_initial_status;
+static u8 fan_control_desired_level;
+
+static void fan_watchdog_fire(struct work_struct *ignored);
+static int fan_watchdog_maxinterval;
+static DECLARE_DELAYED_WORK(fan_watchdog_task, fan_watchdog_fire);
+
+IBM_HANDLE(fans, ec, "FANS");	/* X31, X40, X41 */
+IBM_HANDLE(gfan, ec, "GFAN",	/* 570 */
+	   "\\FSPD",		/* 600e/x, 770e, 770x */
+	   );			/* all others */
+IBM_HANDLE(sfan, ec, "SFAN",	/* 570 */
+	   "JFNS",		/* 770x-JL */
+	   );			/* all others */
+
+/*
+ * SYSFS fan layout: hwmon compatible (device)
+ *
+ * pwm*_enable:
+ * 	0: "disengaged" mode
+ * 	1: manual mode
+ * 	2: native EC "auto" mode (recommended, hardware default)
+ *
+ * pwm*: set speed in manual mode, ignored otherwise.
+ * 	0 is level 0; 255 is level 7. Intermediate points done with linear
+ * 	interpolation.
+ *
+ * fan*_input: tachometer reading, RPM
+ *
+ *
+ * SYSFS fan layout: extensions
+ *
+ * fan_watchdog (driver):
+ * 	fan watchdog interval in seconds, 0 disables (default), max 120
+ */
+
+/* sysfs fan pwm1_enable ----------------------------------------------- */
+static ssize_t fan_pwm1_enable_show(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	int res, mode;
+	u8 status;
+
+	res = fan_get_status_safe(&status);
+	if (res)
+		return res;
+
+	if (unlikely(tp_features.fan_ctrl_status_undef)) {
+		if (status != fan_control_initial_status) {
+			tp_features.fan_ctrl_status_undef = 0;
+		} else {
+			/* Return most likely status. In fact, it
+			 * might be the only possible status */
+			status = TP_EC_FAN_AUTO;
+		}
+	}
+
+	if (status & TP_EC_FAN_FULLSPEED) {
+		mode = 0;
+	} else if (status & TP_EC_FAN_AUTO) {
+		mode = 2;
+	} else
+		mode = 1;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", mode);
+}
+
+static ssize_t fan_pwm1_enable_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	unsigned long t;
+	int res, level;
+
+	if (parse_strtoul(buf, 2, &t))
+		return -EINVAL;
+
+	switch (t) {
+	case 0:
+		level = TP_EC_FAN_FULLSPEED;
+		break;
+	case 1:
+		level = TPACPI_FAN_LAST_LEVEL;
+		break;
+	case 2:
+		level = TP_EC_FAN_AUTO;
+		break;
+	case 3:
+		/* reserved for software-controlled auto mode */
+		return -ENOSYS;
+	default:
+		return -EINVAL;
+	}
+
+	res = fan_set_level_safe(level);
+	if (res == -ENXIO)
+		return -EINVAL;
+	else if (res < 0)
+		return res;
+
+	fan_watchdog_reset();
+
+	return count;
+}
+
+static struct device_attribute dev_attr_fan_pwm1_enable =
+	__ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
+		fan_pwm1_enable_show, fan_pwm1_enable_store);
+
+/* sysfs fan pwm1 ------------------------------------------------------ */
+static ssize_t fan_pwm1_show(struct device *dev,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	int res;
+	u8 status;
+
+	res = fan_get_status_safe(&status);
+	if (res)
+		return res;
+
+	if (unlikely(tp_features.fan_ctrl_status_undef)) {
+		if (status != fan_control_initial_status) {
+			tp_features.fan_ctrl_status_undef = 0;
+		} else {
+			status = TP_EC_FAN_AUTO;
+		}
+	}
+
+	if ((status &
+	     (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) != 0)
+		status = fan_control_desired_level;
+
+	if (status > 7)
+		status = 7;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", (status * 255) / 7);
+}
+
+static ssize_t fan_pwm1_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	unsigned long s;
+	int rc;
+	u8 status, newlevel;
+
+	if (parse_strtoul(buf, 255, &s))
+		return -EINVAL;
+
+	/* scale down from 0-255 to 0-7 */
+	newlevel = (s >> 5) & 0x07;
+
+	rc = mutex_lock_interruptible(&fan_mutex);
+	if (rc < 0)
+		return rc;
+
+	rc = fan_get_status(&status);
+	if (!rc && (status &
+		    (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) {
+		rc = fan_set_level(newlevel);
+		if (rc == -ENXIO)
+			rc = -EINVAL;
+		else if (!rc) {
+			fan_update_desired_level(newlevel);
+			fan_watchdog_reset();
+		}
+	}
+
+	mutex_unlock(&fan_mutex);
+	return (rc)? rc : count;
+}
+
+static struct device_attribute dev_attr_fan_pwm1 =
+	__ATTR(pwm1, S_IWUSR | S_IRUGO,
+		fan_pwm1_show, fan_pwm1_store);
+
+/* sysfs fan fan1_input ------------------------------------------------ */
+static ssize_t fan_fan1_input_show(struct device *dev,
+			   struct device_attribute *attr,
+			   char *buf)
+{
+	int res;
+	unsigned int speed;
+
+	res = fan_get_speed(&speed);
+	if (res < 0)
+		return res;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", speed);
+}
+
+static struct device_attribute dev_attr_fan_fan1_input =
+	__ATTR(fan1_input, S_IRUGO,
+		fan_fan1_input_show, NULL);
+
+/* sysfs fan fan_watchdog (driver) ------------------------------------- */
+static ssize_t fan_fan_watchdog_show(struct device_driver *drv,
+				     char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%u\n", fan_watchdog_maxinterval);
+}
+
+static ssize_t fan_fan_watchdog_store(struct device_driver *drv,
+				      const char *buf, size_t count)
+{
+	unsigned long t;
+
+	if (parse_strtoul(buf, 120, &t))
+		return -EINVAL;
+
+	if (!fan_control_allowed)
+		return -EPERM;
+
+	fan_watchdog_maxinterval = t;
+	fan_watchdog_reset();
+
+	return count;
+}
+
+static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO,
+		fan_fan_watchdog_show, fan_fan_watchdog_store);
+
+/* --------------------------------------------------------------------- */
+static struct attribute *fan_attributes[] = {
+	&dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr,
+	&dev_attr_fan_fan1_input.attr,
+	NULL
+};
+
+static const struct attribute_group fan_attr_group = {
+	.attrs = fan_attributes,
+};
+
+static int __init fan_init(struct ibm_init_struct *iibm)
+{
+	int rc;
+
+	vdbg_printk(TPACPI_DBG_INIT, "initializing fan subdriver\n");
+
+	mutex_init(&fan_mutex);
+	fan_status_access_mode = TPACPI_FAN_NONE;
+	fan_control_access_mode = TPACPI_FAN_WR_NONE;
+	fan_control_commands = 0;
+	fan_watchdog_maxinterval = 0;
+	tp_features.fan_ctrl_status_undef = 0;
+	fan_control_desired_level = 7;
+
+	IBM_ACPIHANDLE_INIT(fans);
+	IBM_ACPIHANDLE_INIT(gfan);
+	IBM_ACPIHANDLE_INIT(sfan);
+
+	if (gfan_handle) {
+		/* 570, 600e/x, 770e, 770x */
+		fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN;
+	} else {
+		/* all other ThinkPads: note that even old-style
+		 * ThinkPad ECs supports the fan control register */
+		if (likely(acpi_ec_read(fan_status_offset,
+					&fan_control_initial_status))) {
+			fan_status_access_mode = TPACPI_FAN_RD_TPEC;
+
+			/* In some ThinkPads, neither the EC nor the ACPI
+			 * DSDT initialize the fan status, and it ends up
+			 * being set to 0x07 when it *could* be either
+			 * 0x07 or 0x80.
+			 *
+			 * Enable for TP-1Y (T43), TP-78 (R51e),
+			 * TP-76 (R52), TP-70 (T43, R52), which are known
+			 * to be buggy. */
+			if (fan_control_initial_status == 0x07 &&
+			    ibm_thinkpad_ec_found &&
+			    ((ibm_thinkpad_ec_found[0] == '1' &&
+			      ibm_thinkpad_ec_found[1] == 'Y') ||
+			     (ibm_thinkpad_ec_found[0] == '7' &&
+			      (ibm_thinkpad_ec_found[1] == '6' ||
+			       ibm_thinkpad_ec_found[1] == '8' ||
+			       ibm_thinkpad_ec_found[1] == '0'))
+			    )) {
+				printk(IBM_NOTICE
+				       "fan_init: initial fan status is "
+				       "unknown, assuming it is in auto "
+				       "mode\n");
+				tp_features.fan_ctrl_status_undef = 1;
+			}
+		} else {
+			printk(IBM_ERR
+			       "ThinkPad ACPI EC access misbehaving, "
+			       "fan status and control unavailable\n");
+			return 1;
+		}
+	}
+
+	if (sfan_handle) {
+		/* 570, 770x-JL */
+		fan_control_access_mode = TPACPI_FAN_WR_ACPI_SFAN;
+		fan_control_commands |=
+		    TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_ENABLE;
+	} else {
+		if (!gfan_handle) {
+			/* gfan without sfan means no fan control */
+			/* all other models implement TP EC 0x2f control */
+
+			if (fans_handle) {
+				/* X31, X40, X41 */
+				fan_control_access_mode =
+				    TPACPI_FAN_WR_ACPI_FANS;
+				fan_control_commands |=
+				    TPACPI_FAN_CMD_SPEED |
+				    TPACPI_FAN_CMD_LEVEL |
+				    TPACPI_FAN_CMD_ENABLE;
+			} else {
+				fan_control_access_mode = TPACPI_FAN_WR_TPEC;
+				fan_control_commands |=
+				    TPACPI_FAN_CMD_LEVEL |
+				    TPACPI_FAN_CMD_ENABLE;
+			}
+		}
+	}
+
+	vdbg_printk(TPACPI_DBG_INIT, "fan is %s, modes %d, %d\n",
+		str_supported(fan_status_access_mode != TPACPI_FAN_NONE ||
+		  fan_control_access_mode != TPACPI_FAN_WR_NONE),
+		fan_status_access_mode, fan_control_access_mode);
+
+	/* fan control master switch */
+	if (!fan_control_allowed) {
+		fan_control_access_mode = TPACPI_FAN_WR_NONE;
+		fan_control_commands = 0;
+		dbg_printk(TPACPI_DBG_INIT,
+			   "fan control features disabled by parameter\n");
+	}
+
+	/* update fan_control_desired_level */
+	if (fan_status_access_mode != TPACPI_FAN_NONE)
+		fan_get_status_safe(NULL);
+
+	if (fan_status_access_mode != TPACPI_FAN_NONE ||
+	    fan_control_access_mode != TPACPI_FAN_WR_NONE) {
+		rc = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+					 &fan_attr_group);
+		if (!(rc < 0))
+			rc = driver_create_file(&tpacpi_pdriver.driver,
+					&driver_attr_fan_watchdog);
+		if (rc < 0)
+			return rc;
+		return 0;
+	} else
+		return 1;
+}
+
+/*
+ * Call with fan_mutex held
+ */
+static void fan_update_desired_level(u8 status)
+{
+	if ((status &
+	     (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) {
+		if (status > 7)
+			fan_control_desired_level = 7;
+		else
+			fan_control_desired_level = status;
+	}
+}
+
+static int fan_get_status(u8 *status)
+{
+	u8 s;
+
+	/* TODO:
+	 * Add TPACPI_FAN_RD_ACPI_FANS ? */
+
+	switch (fan_status_access_mode) {
+	case TPACPI_FAN_RD_ACPI_GFAN:
+		/* 570, 600e/x, 770e, 770x */
+
+		if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d")))
+			return -EIO;
+
+		if (likely(status))
+			*status = s & 0x07;
+
+		break;
+
+	case TPACPI_FAN_RD_TPEC:
+		/* all except 570, 600e/x, 770e, 770x */
+		if (unlikely(!acpi_ec_read(fan_status_offset, &s)))
+			return -EIO;
+
+		if (likely(status))
+			*status = s;
+
+		break;
+
+	default:
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static int fan_get_status_safe(u8 *status)
+{
+	int rc;
+	u8 s;
+
+	rc = mutex_lock_interruptible(&fan_mutex);
+	if (rc < 0)
+		return rc;
+	rc = fan_get_status(&s);
+	if (!rc)
+		fan_update_desired_level(s);
+	mutex_unlock(&fan_mutex);
+
+	if (status)
+		*status = s;
+
+	return rc;
+}
+
+static void fan_exit(void)
+{
+	vdbg_printk(TPACPI_DBG_EXIT, "cancelling any pending fan watchdog tasks\n");
+
+	/* FIXME: can we really do this unconditionally? */
+	sysfs_remove_group(&tpacpi_pdev->dev.kobj, &fan_attr_group);
+	driver_remove_file(&tpacpi_pdriver.driver, &driver_attr_fan_watchdog);
+
+	cancel_delayed_work(&fan_watchdog_task);
+	flush_scheduled_work();
+}
+
+static int fan_get_speed(unsigned int *speed)
+{
+	u8 hi, lo;
+
+	switch (fan_status_access_mode) {
+	case TPACPI_FAN_RD_TPEC:
+		/* all except 570, 600e/x, 770e, 770x */
+		if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) ||
+			     !acpi_ec_read(fan_rpm_offset + 1, &hi)))
+			return -EIO;
+
+		if (likely(speed))
+			*speed = (hi << 8) | lo;
+
+		break;
+
+	default:
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static void fan_watchdog_fire(struct work_struct *ignored)
+{
+	int rc;
+
+	printk(IBM_NOTICE "fan watchdog: enabling fan\n");
+	rc = fan_set_enable();
+	if (rc < 0) {
+		printk(IBM_ERR "fan watchdog: error %d while enabling fan, "
+			"will try again later...\n", -rc);
+		/* reschedule for later */
+		fan_watchdog_reset();
+	}
+}
+
+static void fan_watchdog_reset(void)
+{
+	static int fan_watchdog_active = 0;
+
+	if (fan_control_access_mode == TPACPI_FAN_WR_NONE)
+		return;
+
+	if (fan_watchdog_active)
+		cancel_delayed_work(&fan_watchdog_task);
+
+	if (fan_watchdog_maxinterval > 0) {
+		fan_watchdog_active = 1;
+		if (!schedule_delayed_work(&fan_watchdog_task,
+				msecs_to_jiffies(fan_watchdog_maxinterval
+						 * 1000))) {
+			printk(IBM_ERR "failed to schedule the fan watchdog, "
+			       "watchdog will not trigger\n");
+		}
+	} else
+		fan_watchdog_active = 0;
+}
+
+static int fan_set_level(int level)
+{
+	if (!fan_control_allowed)
+		return -EPERM;
+
+	switch (fan_control_access_mode) {
+	case TPACPI_FAN_WR_ACPI_SFAN:
+		if (level >= 0 && level <= 7) {
+			if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
+				return -EIO;
+		} else
+			return -EINVAL;
+		break;
+
+	case TPACPI_FAN_WR_ACPI_FANS:
+	case TPACPI_FAN_WR_TPEC:
+		if ((level != TP_EC_FAN_AUTO) &&
+		    (level != TP_EC_FAN_FULLSPEED) &&
+		    ((level < 0) || (level > 7)))
+			return -EINVAL;
+
+		/* safety net should the EC not support AUTO
+		 * or FULLSPEED mode bits and just ignore them */
+		if (level & TP_EC_FAN_FULLSPEED)
+			level |= 7;	/* safety min speed 7 */
+		else if (level & TP_EC_FAN_FULLSPEED)
+			level |= 4;	/* safety min speed 4 */
+
+		if (!acpi_ec_write(fan_status_offset, level))
+			return -EIO;
+		else
+			tp_features.fan_ctrl_status_undef = 0;
+		break;
+
+	default:
+		return -ENXIO;
+	}
+	return 0;
+}
+
+static int fan_set_level_safe(int level)
+{
+	int rc;
+
+	if (!fan_control_allowed)
+		return -EPERM;
+
+	rc = mutex_lock_interruptible(&fan_mutex);
+	if (rc < 0)
+		return rc;
+
+	if (level == TPACPI_FAN_LAST_LEVEL)
+		level = fan_control_desired_level;
+
+	rc = fan_set_level(level);
+	if (!rc)
+		fan_update_desired_level(level);
+
+	mutex_unlock(&fan_mutex);
+	return rc;
+}
+
+static int fan_set_enable(void)
+{
+	u8 s;
+	int rc;
+
+	if (!fan_control_allowed)
+		return -EPERM;
+
+	rc = mutex_lock_interruptible(&fan_mutex);
+	if (rc < 0)
+		return rc;
+
+	switch (fan_control_access_mode) {
+	case TPACPI_FAN_WR_ACPI_FANS:
+	case TPACPI_FAN_WR_TPEC:
+		rc = fan_get_status(&s);
+		if (rc < 0)
+			break;
+
+		/* Don't go out of emergency fan mode */
+		if (s != 7) {
+			s &= 0x07;
+			s |= TP_EC_FAN_AUTO | 4; /* min fan speed 4 */
+		}
+
+		if (!acpi_ec_write(fan_status_offset, s))
+			rc = -EIO;
+		else {
+			tp_features.fan_ctrl_status_undef = 0;
+			rc = 0;
+		}
+		break;
+
+	case TPACPI_FAN_WR_ACPI_SFAN:
+		rc = fan_get_status(&s);
+		if (rc < 0)
+			break;
+
+		s &= 0x07;
+
+		/* Set fan to at least level 4 */
+		s |= 4;
+
+		if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s))
+			rc= -EIO;
+		else
+			rc = 0;
+		break;
+
+	default:
+		rc = -ENXIO;
+	}
+
+	mutex_unlock(&fan_mutex);
+	return rc;
+}
+
+static int fan_set_disable(void)
+{
+	int rc;
+
+	if (!fan_control_allowed)
+		return -EPERM;
+
+	rc = mutex_lock_interruptible(&fan_mutex);
+	if (rc < 0)
+		return rc;
+
+	rc = 0;
+	switch (fan_control_access_mode) {
+	case TPACPI_FAN_WR_ACPI_FANS:
+	case TPACPI_FAN_WR_TPEC:
+		if (!acpi_ec_write(fan_status_offset, 0x00))
+			rc = -EIO;
+		else {
+			fan_control_desired_level = 0;
+			tp_features.fan_ctrl_status_undef = 0;
+		}
+		break;
+
+	case TPACPI_FAN_WR_ACPI_SFAN:
+		if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00))
+			rc = -EIO;
+		else
+			fan_control_desired_level = 0;
+		break;
+
+	default:
+		rc = -ENXIO;
+	}
+
+
+	mutex_unlock(&fan_mutex);
+	return rc;
+}
+
+static int fan_set_speed(int speed)
+{
+	int rc;
+
+	if (!fan_control_allowed)
+		return -EPERM;
+
+	rc = mutex_lock_interruptible(&fan_mutex);
+	if (rc < 0)
+		return rc;
+
+	rc = 0;
+	switch (fan_control_access_mode) {
+	case TPACPI_FAN_WR_ACPI_FANS:
+		if (speed >= 0 && speed <= 65535) {
+			if (!acpi_evalf(fans_handle, NULL, NULL, "vddd",
+					speed, speed, speed))
+				rc = -EIO;
+		} else
+			rc = -EINVAL;
+		break;
+
+	default:
+		rc = -ENXIO;
+	}
+
+	mutex_unlock(&fan_mutex);
+	return rc;
+}
+
+static int fan_read(char *p)
+{
+	int len = 0;
+	int rc;
+	u8 status;
+	unsigned int speed = 0;
+
+	switch (fan_status_access_mode) {
+	case TPACPI_FAN_RD_ACPI_GFAN:
+		/* 570, 600e/x, 770e, 770x */
+		if ((rc = fan_get_status_safe(&status)) < 0)
+			return rc;
+
+		len += sprintf(p + len, "status:\t\t%s\n"
+			       "level:\t\t%d\n",
+			       (status != 0) ? "enabled" : "disabled", status);
+		break;
+
+	case TPACPI_FAN_RD_TPEC:
+		/* all except 570, 600e/x, 770e, 770x */
+		if ((rc = fan_get_status_safe(&status)) < 0)
+			return rc;
+
+		if (unlikely(tp_features.fan_ctrl_status_undef)) {
+			if (status != fan_control_initial_status)
+				tp_features.fan_ctrl_status_undef = 0;
+			else
+				/* Return most likely status. In fact, it
+				 * might be the only possible status */
+				status = TP_EC_FAN_AUTO;
+		}
+
+		len += sprintf(p + len, "status:\t\t%s\n",
+			       (status != 0) ? "enabled" : "disabled");
+
+		if ((rc = fan_get_speed(&speed)) < 0)
+			return rc;
+
+		len += sprintf(p + len, "speed:\t\t%d\n", speed);
+
+		if (status & TP_EC_FAN_FULLSPEED)
+			/* Disengaged mode takes precedence */
+			len += sprintf(p + len, "level:\t\tdisengaged\n");
+		else if (status & TP_EC_FAN_AUTO)
+			len += sprintf(p + len, "level:\t\tauto\n");
+		else
+			len += sprintf(p + len, "level:\t\t%d\n", status);
+		break;
+
+	case TPACPI_FAN_NONE:
+	default:
+		len += sprintf(p + len, "status:\t\tnot supported\n");
+	}
+
+	if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) {
+		len += sprintf(p + len, "commands:\tlevel <level>");
+
+		switch (fan_control_access_mode) {
+		case TPACPI_FAN_WR_ACPI_SFAN:
+			len += sprintf(p + len, " (<level> is 0-7)\n");
+			break;
+
+		default:
+			len += sprintf(p + len, " (<level> is 0-7, "
+				       "auto, disengaged, full-speed)\n");
+			break;
+		}
+	}
+
+	if (fan_control_commands & TPACPI_FAN_CMD_ENABLE)
+		len += sprintf(p + len, "commands:\tenable, disable\n"
+			       "commands:\twatchdog <timeout> (<timeout> is 0 (off), "
+			       "1-120 (seconds))\n");
+
+	if (fan_control_commands & TPACPI_FAN_CMD_SPEED)
+		len += sprintf(p + len, "commands:\tspeed <speed>"
+			       " (<speed> is 0-65535)\n");
+
+	return len;
+}
+
+static int fan_write_cmd_level(const char *cmd, int *rc)
+{
+	int level;
+
+	if (strlencmp(cmd, "level auto") == 0)
+		level = TP_EC_FAN_AUTO;
+	else if ((strlencmp(cmd, "level disengaged") == 0) |
+	         (strlencmp(cmd, "level full-speed") == 0))
+		level = TP_EC_FAN_FULLSPEED;
+	else if (sscanf(cmd, "level %d", &level) != 1)
+		return 0;
+
+	if ((*rc = fan_set_level_safe(level)) == -ENXIO)
+		printk(IBM_ERR "level command accepted for unsupported "
+		       "access mode %d", fan_control_access_mode);
+
+	return 1;
+}
+
+static int fan_write_cmd_enable(const char *cmd, int *rc)
+{
+	if (strlencmp(cmd, "enable") != 0)
+		return 0;
+
+	if ((*rc = fan_set_enable()) == -ENXIO)
+		printk(IBM_ERR "enable command accepted for unsupported "
+		       "access mode %d", fan_control_access_mode);
+
+	return 1;
+}
+
+static int fan_write_cmd_disable(const char *cmd, int *rc)
+{
+	if (strlencmp(cmd, "disable") != 0)
+		return 0;
+
+	if ((*rc = fan_set_disable()) == -ENXIO)
+		printk(IBM_ERR "disable command accepted for unsupported "
+		       "access mode %d", fan_control_access_mode);
+
+	return 1;
+}
+
+static int fan_write_cmd_speed(const char *cmd, int *rc)
+{
+	int speed;
+
+	/* TODO:
+	 * Support speed <low> <medium> <high> ? */
+
+	if (sscanf(cmd, "speed %d", &speed) != 1)
+		return 0;
+
+	if ((*rc = fan_set_speed(speed)) == -ENXIO)
+		printk(IBM_ERR "speed command accepted for unsupported "
+		       "access mode %d", fan_control_access_mode);
+
+	return 1;
+}
+
+static int fan_write_cmd_watchdog(const char *cmd, int *rc)
+{
+	int interval;
+
+	if (sscanf(cmd, "watchdog %d", &interval) != 1)
+		return 0;
+
+	if (interval < 0 || interval > 120)
+		*rc = -EINVAL;
+	else
+		fan_watchdog_maxinterval = interval;
+
+	return 1;
+}
+
+static int fan_write(char *buf)
+{
+	char *cmd;
+	int rc = 0;
+
+	while (!rc && (cmd = next_cmd(&buf))) {
+		if (!((fan_control_commands & TPACPI_FAN_CMD_LEVEL) &&
+		      fan_write_cmd_level(cmd, &rc)) &&
+		    !((fan_control_commands & TPACPI_FAN_CMD_ENABLE) &&
+		      (fan_write_cmd_enable(cmd, &rc) ||
+		       fan_write_cmd_disable(cmd, &rc) ||
+		       fan_write_cmd_watchdog(cmd, &rc))) &&
+		    !((fan_control_commands & TPACPI_FAN_CMD_SPEED) &&
+		      fan_write_cmd_speed(cmd, &rc))
+		    )
+			rc = -EINVAL;
+		else if (!rc)
+			fan_watchdog_reset();
+	}
+
+	return rc;
+}
+
+static struct ibm_struct fan_driver_data = {
+	.name = "fan",
+	.read = fan_read,
+	.write = fan_write,
+	.exit = fan_exit,
+};
+
+/****************************************************************************
+ ****************************************************************************
+ *
+ * Infrastructure
+ *
+ ****************************************************************************
+ ****************************************************************************/
+
+/* /proc support */
+static struct proc_dir_entry *proc_dir = NULL;
+
+/* Subdriver registry */
+static LIST_HEAD(tpacpi_all_drivers);
+
+
+/*
+ * Module and infrastructure proble, init and exit handling
+ */
+
+#ifdef CONFIG_THINKPAD_ACPI_DEBUG
+static const char * __init str_supported(int is_supported)
+{
+	static char text_unsupported[] __initdata = "not supported";
+
+	return (is_supported)? &text_unsupported[4] : &text_unsupported[0];
+}
+#endif /* CONFIG_THINKPAD_ACPI_DEBUG */
+
+static int __init ibm_init(struct ibm_init_struct *iibm)
+{
+	int ret;
+	struct ibm_struct *ibm = iibm->data;
+	struct proc_dir_entry *entry;
+
+	BUG_ON(ibm == NULL);
+
+	INIT_LIST_HEAD(&ibm->all_drivers);
+
+	if (ibm->flags.experimental && !experimental)
+		return 0;
+
+	dbg_printk(TPACPI_DBG_INIT,
+		"probing for %s\n", ibm->name);
+
+	if (iibm->init) {
+		ret = iibm->init(iibm);
+		if (ret > 0)
+			return 0;	/* probe failed */
+		if (ret)
+			return ret;
+
+		ibm->flags.init_called = 1;
+	}
+
+	if (ibm->acpi) {
+		if (ibm->acpi->hid) {
+			ret = register_tpacpi_subdriver(ibm);
+			if (ret)
+				goto err_out;
+		}
+
+		if (ibm->acpi->notify) {
+			ret = setup_acpi_notify(ibm);
+			if (ret == -ENODEV) {
+				printk(IBM_NOTICE "disabling subdriver %s\n",
+					ibm->name);
+				ret = 0;
+				goto err_out;
+			}
+			if (ret < 0)
+				goto err_out;
+		}
+	}
+
+	dbg_printk(TPACPI_DBG_INIT,
+		"%s installed\n", ibm->name);
+
+	if (ibm->read) {
+		entry = create_proc_entry(ibm->name,
+					  S_IFREG | S_IRUGO | S_IWUSR,
+					  proc_dir);
+		if (!entry) {
+			printk(IBM_ERR "unable to create proc entry %s\n",
+			       ibm->name);
+			ret = -ENODEV;
+			goto err_out;
+		}
+		entry->owner = THIS_MODULE;
+		entry->data = ibm;
+		entry->read_proc = &dispatch_procfs_read;
+		if (ibm->write)
+			entry->write_proc = &dispatch_procfs_write;
+		ibm->flags.proc_created = 1;
+	}
+
+	list_add_tail(&ibm->all_drivers, &tpacpi_all_drivers);
+
+	return 0;
+
+err_out:
+	dbg_printk(TPACPI_DBG_INIT,
+		"%s: at error exit path with result %d\n",
+		ibm->name, ret);
+
+	ibm_exit(ibm);
+	return (ret < 0)? ret : 0;
+}
+
+static void ibm_exit(struct ibm_struct *ibm)
+{
+	dbg_printk(TPACPI_DBG_EXIT, "removing %s\n", ibm->name);
+
+	list_del_init(&ibm->all_drivers);
+
+	if (ibm->flags.acpi_notify_installed) {
+		dbg_printk(TPACPI_DBG_EXIT,
+			"%s: acpi_remove_notify_handler\n", ibm->name);
+		BUG_ON(!ibm->acpi);
+		acpi_remove_notify_handler(*ibm->acpi->handle,
+					   ibm->acpi->type,
+					   dispatch_acpi_notify);
+		ibm->flags.acpi_notify_installed = 0;
+		ibm->flags.acpi_notify_installed = 0;
+	}
+
+	if (ibm->flags.proc_created) {
+		dbg_printk(TPACPI_DBG_EXIT,
+			"%s: remove_proc_entry\n", ibm->name);
+		remove_proc_entry(ibm->name, proc_dir);
+		ibm->flags.proc_created = 0;
+	}
+
+	if (ibm->flags.acpi_driver_registered) {
+		dbg_printk(TPACPI_DBG_EXIT,
+			"%s: acpi_bus_unregister_driver\n", ibm->name);
+		BUG_ON(!ibm->acpi);
+		acpi_bus_unregister_driver(ibm->acpi->driver);
+		kfree(ibm->acpi->driver);
+		ibm->acpi->driver = NULL;
+		ibm->flags.acpi_driver_registered = 0;
+	}
+
+	if (ibm->flags.init_called && ibm->exit) {
+		ibm->exit();
+		ibm->flags.init_called = 0;
+	}
+
+	dbg_printk(TPACPI_DBG_INIT, "finished removing %s\n", ibm->name);
+}
+
+/* Probing */
+
+static char *ibm_thinkpad_ec_found = NULL;
+
+static char* __init check_dmi_for_ec(void)
+{
+	struct dmi_device *dev = NULL;
+	char ec_fw_string[18];
+
+	/*
+	 * ThinkPad T23 or newer, A31 or newer, R50e or newer,
+	 * X32 or newer, all Z series;  Some models must have an
+	 * up-to-date BIOS or they will not be detected.
+	 *
+	 * See http://thinkwiki.org/wiki/List_of_DMI_IDs
+	 */
+	while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
+		if (sscanf(dev->name,
+			   "IBM ThinkPad Embedded Controller -[%17c",
+			   ec_fw_string) == 1) {
+			ec_fw_string[sizeof(ec_fw_string) - 1] = 0;
+			ec_fw_string[strcspn(ec_fw_string, " ]")] = 0;
+			return kstrdup(ec_fw_string, GFP_KERNEL);
+		}
+	}
+	return NULL;
+}
+
+static int __init probe_for_thinkpad(void)
+{
+	int is_thinkpad;
+
+	if (acpi_disabled)
+		return -ENODEV;
+
+	/*
+	 * Non-ancient models have better DMI tagging, but very old models
+	 * don't.
+	 */
+	is_thinkpad = dmi_name_in_vendors("ThinkPad");
+
+	/* ec is required because many other handles are relative to it */
+	IBM_ACPIHANDLE_INIT(ec);
+	if (!ec_handle) {
+		if (is_thinkpad)
+			printk(IBM_ERR
+				"Not yet supported ThinkPad detected!\n");
+		return -ENODEV;
+	}
+
+	/*
+	 * Risks a regression on very old machines, but reduces potential
+	 * false positives a damn great deal
+	 */
+	if (!is_thinkpad)
+		is_thinkpad = dmi_name_in_vendors("IBM");
+
+	if (!is_thinkpad && !force_load)
+		return -ENODEV;
+
+	return 0;
+}
+
+
+/* Module init, exit, parameters */
+
+static struct ibm_init_struct ibms_init[] __initdata = {
+	{
+		.init = thinkpad_acpi_driver_init,
+		.data = &thinkpad_acpi_driver_data,
+	},
+	{
+		.init = hotkey_init,
+		.data = &hotkey_driver_data,
+	},
+	{
+		.init = bluetooth_init,
+		.data = &bluetooth_driver_data,
+	},
+	{
+		.init = wan_init,
+		.data = &wan_driver_data,
+	},
+	{
+		.init = video_init,
+		.data = &video_driver_data,
+	},
+	{
+		.init = light_init,
+		.data = &light_driver_data,
+	},
+#ifdef CONFIG_THINKPAD_ACPI_DOCK
+	{
+		.init = dock_init,
+		.data = &dock_driver_data[0],
+	},
+	{
+		.init = dock_init2,
+		.data = &dock_driver_data[1],
+	},
+#endif
+#ifdef CONFIG_THINKPAD_ACPI_BAY
+	{
+		.init = bay_init,
+		.data = &bay_driver_data,
+	},
+#endif
+	{
+		.init = cmos_init,
+		.data = &cmos_driver_data,
+	},
+	{
+		.init = led_init,
+		.data = &led_driver_data,
+	},
+	{
+		.init = beep_init,
+		.data = &beep_driver_data,
+	},
+	{
+		.init = thermal_init,
+		.data = &thermal_driver_data,
+	},
+	{
+		.data = &ecdump_driver_data,
+	},
+	{
+		.init = brightness_init,
+		.data = &brightness_driver_data,
+	},
+	{
+		.data = &volume_driver_data,
+	},
+	{
+		.init = fan_init,
+		.data = &fan_driver_data,
+	},
+};
+
+static int __init set_ibm_param(const char *val, struct kernel_param *kp)
+{
+	unsigned int i;
+	struct ibm_struct *ibm;
+
+	for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
+		ibm = ibms_init[i].data;
+		BUG_ON(ibm == NULL);
+
+		if (strcmp(ibm->name, kp->name) == 0 && ibm->write) {
+			if (strlen(val) > sizeof(ibms_init[i].param) - 2)
+				return -ENOSPC;
+			strcpy(ibms_init[i].param, val);
+			strcat(ibms_init[i].param, ",");
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int experimental;
+module_param(experimental, int, 0);
+
+static u32 dbg_level;
+module_param_named(debug, dbg_level, uint, 0);
+
+static int force_load;
+module_param(force_load, int, 0);
+
+static int fan_control_allowed;
+module_param_named(fan_control, fan_control_allowed, int, 0);
+
+#define IBM_PARAM(feature) \
+	module_param_call(feature, set_ibm_param, NULL, NULL, 0)
+
+IBM_PARAM(hotkey);
+IBM_PARAM(bluetooth);
+IBM_PARAM(video);
+IBM_PARAM(light);
+#ifdef CONFIG_THINKPAD_ACPI_DOCK
+IBM_PARAM(dock);
+#endif
+#ifdef CONFIG_THINKPAD_ACPI_BAY
+IBM_PARAM(bay);
+#endif /* CONFIG_THINKPAD_ACPI_BAY */
+IBM_PARAM(cmos);
+IBM_PARAM(led);
+IBM_PARAM(beep);
+IBM_PARAM(ecdump);
+IBM_PARAM(brightness);
+IBM_PARAM(volume);
+IBM_PARAM(fan);
+
+static int __init thinkpad_acpi_module_init(void)
+{
+	int ret, i;
+
+	/* Driver-level probe */
+	ret = probe_for_thinkpad();
+	if (ret)
+		return ret;
+
+	/* Driver initialization */
+	ibm_thinkpad_ec_found = check_dmi_for_ec();
+	IBM_ACPIHANDLE_INIT(ecrd);
+	IBM_ACPIHANDLE_INIT(ecwr);
+
+	proc_dir = proc_mkdir(IBM_PROC_DIR, acpi_root_dir);
+	if (!proc_dir) {
+		printk(IBM_ERR "unable to create proc dir " IBM_PROC_DIR);
+		thinkpad_acpi_module_exit();
+		return -ENODEV;
+	}
+	proc_dir->owner = THIS_MODULE;
+
+	ret = platform_driver_register(&tpacpi_pdriver);
+	if (ret) {
+		printk(IBM_ERR "unable to register platform driver\n");
+		thinkpad_acpi_module_exit();
+		return ret;
+	}
+	ret = tpacpi_create_driver_attributes(&tpacpi_pdriver.driver);
+	if (ret) {
+		printk(IBM_ERR "unable to create sysfs driver attributes\n");
+		thinkpad_acpi_module_exit();
+		return ret;
+	}
+
+
+	/* Device initialization */
+	tpacpi_pdev = platform_device_register_simple(IBM_DRVR_NAME, -1,
+							NULL, 0);
+	if (IS_ERR(tpacpi_pdev)) {
+		ret = PTR_ERR(tpacpi_pdev);
+		tpacpi_pdev = NULL;
+		printk(IBM_ERR "unable to register platform device\n");
+		thinkpad_acpi_module_exit();
+		return ret;
+	}
+	tpacpi_hwmon = hwmon_device_register(&tpacpi_pdev->dev);
+	if (IS_ERR(tpacpi_hwmon)) {
+		ret = PTR_ERR(tpacpi_hwmon);
+		tpacpi_hwmon = NULL;
+		printk(IBM_ERR "unable to register hwmon device\n");
+		thinkpad_acpi_module_exit();
+		return ret;
+	}
+	for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
+		ret = ibm_init(&ibms_init[i]);
+		if (ret >= 0 && *ibms_init[i].param)
+			ret = ibms_init[i].data->write(ibms_init[i].param);
+		if (ret < 0) {
+			thinkpad_acpi_module_exit();
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void thinkpad_acpi_module_exit(void)
+{
+	struct ibm_struct *ibm, *itmp;
+
+	list_for_each_entry_safe_reverse(ibm, itmp,
+					 &tpacpi_all_drivers,
+					 all_drivers) {
+		ibm_exit(ibm);
+	}
+
+	dbg_printk(TPACPI_DBG_INIT, "finished subdriver exit path...\n");
+
+	if (tpacpi_hwmon)
+		hwmon_device_unregister(tpacpi_hwmon);
+
+	if (tpacpi_pdev)
+		platform_device_unregister(tpacpi_pdev);
+
+	tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver);
+	platform_driver_unregister(&tpacpi_pdriver);
+
+	if (proc_dir)
+		remove_proc_entry(IBM_PROC_DIR, acpi_root_dir);
+
+	kfree(ibm_thinkpad_ec_found);
+}
+
+module_init(thinkpad_acpi_module_init);
+module_exit(thinkpad_acpi_module_exit);
diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h
new file mode 100644
index 0000000..440145a
--- /dev/null
+++ b/drivers/misc/thinkpad_acpi.h
@@ -0,0 +1,572 @@
+/*
+ *  thinkpad_acpi.h - ThinkPad ACPI Extras
+ *
+ *
+ *  Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>
+ *  Copyright (C) 2006-2007 Henrique de Moraes Holschuh <hmh@hmh.eng.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; 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#ifndef __THINKPAD_ACPI_H__
+#define __THINKPAD_ACPI_H__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+
+#include <linux/proc_fs.h>
+#include <linux/sysfs.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <asm/uaccess.h>
+
+#include <linux/dmi.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+
+#include <acpi/acpi_drivers.h>
+#include <acpi/acnamesp.h>
+
+
+/****************************************************************************
+ * Main driver
+ */
+
+#define IBM_NAME "thinkpad"
+#define IBM_DESC "ThinkPad ACPI Extras"
+#define IBM_FILE "thinkpad_acpi"
+#define IBM_URL "http://ibm-acpi.sf.net/"
+#define IBM_MAIL "ibm-acpi-devel@lists.sourceforge.net"
+
+#define IBM_PROC_DIR "ibm"
+#define IBM_ACPI_EVENT_PREFIX "ibm"
+#define IBM_DRVR_NAME IBM_FILE
+
+#define IBM_LOG IBM_FILE ": "
+#define IBM_ERR	   KERN_ERR    IBM_LOG
+#define IBM_NOTICE KERN_NOTICE IBM_LOG
+#define IBM_INFO   KERN_INFO   IBM_LOG
+#define IBM_DEBUG  KERN_DEBUG  IBM_LOG
+
+#define IBM_MAX_ACPI_ARGS 3
+
+/* ThinkPad CMOS commands */
+#define TP_CMOS_VOLUME_DOWN	0
+#define TP_CMOS_VOLUME_UP	1
+#define TP_CMOS_VOLUME_MUTE	2
+#define TP_CMOS_BRIGHTNESS_UP	4
+#define TP_CMOS_BRIGHTNESS_DOWN	5
+
+#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off")
+#define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
+#define strlencmp(a,b) (strncmp((a), (b), strlen(b)))
+
+/* Debugging */
+#define TPACPI_DBG_ALL		0xffff
+#define TPACPI_DBG_ALL		0xffff
+#define TPACPI_DBG_INIT		0x0001
+#define TPACPI_DBG_EXIT		0x0002
+#define dbg_printk(a_dbg_level, format, arg...) \
+	do { if (dbg_level & a_dbg_level) \
+		printk(IBM_DEBUG "%s: " format, __func__ , ## arg); } while (0)
+#ifdef CONFIG_THINKPAD_ACPI_DEBUG
+#define vdbg_printk(a_dbg_level, format, arg...) \
+	dbg_printk(a_dbg_level, format, ## arg)
+static const char *str_supported(int is_supported);
+#else
+#define vdbg_printk(a_dbg_level, format, arg...)
+#endif
+
+/* ACPI HIDs */
+#define IBM_HKEY_HID    "IBM0068"
+#define IBM_PCI_HID     "PNP0A03"
+
+/* ACPI helpers */
+static int __must_check acpi_evalf(acpi_handle handle,
+		      void *res, char *method, char *fmt, ...);
+static int __must_check acpi_ec_read(int i, u8 * p);
+static int __must_check acpi_ec_write(int i, u8 v);
+static int __must_check _sta(acpi_handle handle);
+
+/* ACPI handles */
+static acpi_handle root_handle;			/* root namespace */
+static acpi_handle ec_handle;			/* EC */
+static acpi_handle ecrd_handle, ecwr_handle;	/* 570 EC access */
+static acpi_handle cmos_handle, hkey_handle;	/* basic thinkpad handles */
+
+static void drv_acpi_handle_init(char *name,
+		   acpi_handle *handle, acpi_handle parent,
+		   char **paths, int num_paths, char **path);
+#define IBM_ACPIHANDLE_INIT(object)						\
+	drv_acpi_handle_init(#object, &object##_handle, *object##_parent,	\
+		object##_paths, ARRAY_SIZE(object##_paths), &object##_path)
+
+/* ThinkPad ACPI helpers */
+static int issue_thinkpad_cmos_command(int cmos_cmd);
+
+/* procfs support */
+static struct proc_dir_entry *proc_dir;
+
+/* procfs helpers */
+static int dispatch_procfs_read(char *page, char **start, off_t off,
+		int count, int *eof, void *data);
+static int dispatch_procfs_write(struct file *file,
+		const char __user * userbuf,
+		unsigned long count, void *data);
+static char *next_cmd(char **cmds);
+
+/* sysfs support */
+struct attribute_set {
+	unsigned int members, max_members;
+	struct attribute_group group;
+};
+
+static struct attribute_set *create_attr_set(unsigned int max_members,
+						const char* name);
+#define destroy_attr_set(_set) \
+	kfree(_set);
+static int add_to_attr_set(struct attribute_set* s, struct attribute *attr);
+static int add_many_to_attr_set(struct attribute_set* s,
+			struct attribute **attr,
+			unsigned int count);
+#define register_attr_set_with_sysfs(_attr_set, _kobj) \
+	sysfs_create_group(_kobj, &_attr_set->group)
+static void delete_attr_set(struct attribute_set* s, struct kobject *kobj);
+
+static int parse_strtoul(const char *buf, unsigned long max,
+			unsigned long *value);
+
+/* Device model */
+static struct platform_device *tpacpi_pdev;
+static struct class_device *tpacpi_hwmon;
+static struct platform_driver tpacpi_pdriver;
+static int tpacpi_create_driver_attributes(struct device_driver *drv);
+static void tpacpi_remove_driver_attributes(struct device_driver *drv);
+
+/* Module */
+static int experimental;
+static u32 dbg_level;
+static int force_load;
+static char *ibm_thinkpad_ec_found;
+
+static char* check_dmi_for_ec(void);
+static int thinkpad_acpi_module_init(void);
+static void thinkpad_acpi_module_exit(void);
+
+
+/****************************************************************************
+ * Subdrivers
+ */
+
+struct ibm_struct;
+
+struct tp_acpi_drv_struct {
+	char *hid;
+	struct acpi_driver *driver;
+
+	void (*notify) (struct ibm_struct *, u32);
+	acpi_handle *handle;
+	u32 type;
+	struct acpi_device *device;
+};
+
+struct ibm_struct {
+	char *name;
+
+	int (*read) (char *);
+	int (*write) (char *);
+	void (*exit) (void);
+
+	struct list_head all_drivers;
+
+	struct tp_acpi_drv_struct *acpi;
+
+	struct {
+		u8 acpi_driver_registered:1;
+		u8 acpi_notify_installed:1;
+		u8 proc_created:1;
+		u8 init_called:1;
+		u8 experimental:1;
+	} flags;
+};
+
+struct ibm_init_struct {
+	char param[32];
+
+	int (*init) (struct ibm_init_struct *);
+	struct ibm_struct *data;
+};
+
+static struct {
+#ifdef CONFIG_THINKPAD_ACPI_BAY
+	u16 bay_status:1;
+	u16 bay_eject:1;
+	u16 bay_status2:1;
+	u16 bay_eject2:1;
+#endif
+	u16 bluetooth:1;
+	u16 hotkey:1;
+	u16 hotkey_mask:1;
+	u16 light:1;
+	u16 light_status:1;
+	u16 wan:1;
+	u16 fan_ctrl_status_undef:1;
+} tp_features;
+
+static struct list_head tpacpi_all_drivers;
+
+static struct ibm_init_struct ibms_init[];
+static int set_ibm_param(const char *val, struct kernel_param *kp);
+static int ibm_init(struct ibm_init_struct *iibm);
+static void ibm_exit(struct ibm_struct *ibm);
+
+
+/*
+ * procfs master subdriver
+ */
+static int thinkpad_acpi_driver_init(struct ibm_init_struct *iibm);
+static int thinkpad_acpi_driver_read(char *p);
+
+
+/*
+ * Bay subdriver
+ */
+
+#ifdef CONFIG_THINKPAD_ACPI_BAY
+static acpi_handle bay_handle, bay_ej_handle;
+static acpi_handle bay2_handle, bay2_ej_handle;
+
+static int bay_init(struct ibm_init_struct *iibm);
+static void bay_notify(struct ibm_struct *ibm, u32 event);
+static int bay_read(char *p);
+static int bay_write(char *buf);
+#endif /* CONFIG_THINKPAD_ACPI_BAY */
+
+
+/*
+ * Beep subdriver
+ */
+
+static acpi_handle beep_handle;
+
+static int beep_read(char *p);
+static int beep_write(char *buf);
+
+
+/*
+ * Bluetooth subdriver
+ */
+
+#define TPACPI_BLUETH_SYSFS_GROUP "bluetooth"
+
+enum {
+	/* ACPI GBDC/SBDC bits */
+	TP_ACPI_BLUETOOTH_HWPRESENT	= 0x01,	/* Bluetooth hw available */
+	TP_ACPI_BLUETOOTH_RADIOSSW	= 0x02,	/* Bluetooth radio enabled */
+	TP_ACPI_BLUETOOTH_UNK		= 0x04,	/* unknown function */
+};
+
+static int bluetooth_init(struct ibm_init_struct *iibm);
+static int bluetooth_get_radiosw(void);
+static int bluetooth_set_radiosw(int radio_on);
+static int bluetooth_read(char *p);
+static int bluetooth_write(char *buf);
+
+
+/*
+ * Brightness (backlight) subdriver
+ */
+
+#define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen"
+
+static struct backlight_device *ibm_backlight_device;
+static int brightness_offset = 0x31;
+
+static int brightness_init(struct ibm_init_struct *iibm);
+static void brightness_exit(void);
+static int brightness_get(struct backlight_device *bd);
+static int brightness_set(int value);
+static int brightness_update_status(struct backlight_device *bd);
+static int brightness_read(char *p);
+static int brightness_write(char *buf);
+
+
+/*
+ * CMOS subdriver
+ */
+
+static int cmos_read(char *p);
+static int cmos_write(char *buf);
+
+
+/*
+ * Dock subdriver
+ */
+
+#ifdef CONFIG_THINKPAD_ACPI_DOCK
+static acpi_handle pci_handle;
+static acpi_handle dock_handle;
+
+static void dock_notify(struct ibm_struct *ibm, u32 event);
+static int dock_read(char *p);
+static int dock_write(char *buf);
+#endif /* CONFIG_THINKPAD_ACPI_DOCK */
+
+
+/*
+ * EC dump subdriver
+ */
+
+static int ecdump_read(char *p) ;
+static int ecdump_write(char *buf);
+
+
+/*
+ * Fan subdriver
+ */
+
+enum {					/* Fan control constants */
+	fan_status_offset = 0x2f,	/* EC register 0x2f */
+	fan_rpm_offset = 0x84,		/* EC register 0x84: LSB, 0x85 MSB (RPM)
+					 * 0x84 must be read before 0x85 */
+
+	TP_EC_FAN_FULLSPEED = 0x40,	/* EC fan mode: full speed */
+	TP_EC_FAN_AUTO	    = 0x80,	/* EC fan mode: auto fan control */
+
+	TPACPI_FAN_LAST_LEVEL = 0x100,	/* Use cached last-seen fan level */
+};
+
+enum fan_status_access_mode {
+	TPACPI_FAN_NONE = 0,		/* No fan status or control */
+	TPACPI_FAN_RD_ACPI_GFAN,	/* Use ACPI GFAN */
+	TPACPI_FAN_RD_TPEC,		/* Use ACPI EC regs 0x2f, 0x84-0x85 */
+};
+
+enum fan_control_access_mode {
+	TPACPI_FAN_WR_NONE = 0,		/* No fan control */
+	TPACPI_FAN_WR_ACPI_SFAN,	/* Use ACPI SFAN */
+	TPACPI_FAN_WR_TPEC,		/* Use ACPI EC reg 0x2f */
+	TPACPI_FAN_WR_ACPI_FANS,	/* Use ACPI FANS and EC reg 0x2f */
+};
+
+enum fan_control_commands {
+	TPACPI_FAN_CMD_SPEED 	= 0x0001,	/* speed command */
+	TPACPI_FAN_CMD_LEVEL 	= 0x0002,	/* level command  */
+	TPACPI_FAN_CMD_ENABLE	= 0x0004,	/* enable/disable cmd,
+						 * and also watchdog cmd */
+};
+
+static int fan_control_allowed;
+
+static enum fan_status_access_mode fan_status_access_mode;
+static enum fan_control_access_mode fan_control_access_mode;
+static enum fan_control_commands fan_control_commands;
+static u8 fan_control_initial_status;
+static u8 fan_control_desired_level;
+static int fan_watchdog_maxinterval;
+
+static struct mutex fan_mutex;
+
+static acpi_handle fans_handle, gfan_handle, sfan_handle;
+
+static int fan_init(struct ibm_init_struct *iibm);
+static void fan_exit(void);
+static int fan_get_status(u8 *status);
+static int fan_get_status_safe(u8 *status);
+static int fan_get_speed(unsigned int *speed);
+static void fan_update_desired_level(u8 status);
+static void fan_watchdog_fire(struct work_struct *ignored);
+static void fan_watchdog_reset(void);
+static int fan_set_level(int level);
+static int fan_set_level_safe(int level);
+static int fan_set_enable(void);
+static int fan_set_disable(void);
+static int fan_set_speed(int speed);
+static int fan_read(char *p);
+static int fan_write(char *buf);
+static int fan_write_cmd_level(const char *cmd, int *rc);
+static int fan_write_cmd_enable(const char *cmd, int *rc);
+static int fan_write_cmd_disable(const char *cmd, int *rc);
+static int fan_write_cmd_speed(const char *cmd, int *rc);
+static int fan_write_cmd_watchdog(const char *cmd, int *rc);
+
+
+/*
+ * Hotkey subdriver
+ */
+
+#define TPACPI_HOTKEY_SYSFS_GROUP "hotkey"
+
+static int hotkey_orig_status;
+static int hotkey_orig_mask;
+
+static struct mutex hotkey_mutex;
+
+static int hotkey_init(struct ibm_init_struct *iibm);
+static void hotkey_exit(void);
+static int hotkey_get(int *status, int *mask);
+static int hotkey_set(int status, int mask);
+static void hotkey_notify(struct ibm_struct *ibm, u32 event);
+static int hotkey_read(char *p);
+static int hotkey_write(char *buf);
+
+
+/*
+ * LED subdriver
+ */
+
+enum led_access_mode {
+	TPACPI_LED_NONE = 0,
+	TPACPI_LED_570,	/* 570 */
+	TPACPI_LED_OLD,	/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
+	TPACPI_LED_NEW,	/* all others */
+};
+
+enum {	/* For TPACPI_LED_OLD */
+	TPACPI_LED_EC_HLCL = 0x0c,	/* EC reg to get led to power on */
+	TPACPI_LED_EC_HLBL = 0x0d,	/* EC reg to blink a lit led */
+	TPACPI_LED_EC_HLMS = 0x0e,	/* EC reg to select led to command */
+};
+
+static enum led_access_mode led_supported;
+static acpi_handle led_handle;
+
+static int led_init(struct ibm_init_struct *iibm);
+static int led_read(char *p);
+static int led_write(char *buf);
+
+/*
+ * Light (thinklight) subdriver
+ */
+
+static acpi_handle lght_handle, ledb_handle;
+
+static int light_init(struct ibm_init_struct *iibm);
+static int light_read(char *p);
+static int light_write(char *buf);
+
+
+/*
+ * Thermal subdriver
+ */
+
+enum thermal_access_mode {
+	TPACPI_THERMAL_NONE = 0,	/* No thermal support */
+	TPACPI_THERMAL_ACPI_TMP07,	/* Use ACPI TMP0-7 */
+	TPACPI_THERMAL_ACPI_UPDT,	/* Use ACPI TMP0-7 with UPDT */
+	TPACPI_THERMAL_TPEC_8,		/* Use ACPI EC regs, 8 sensors */
+	TPACPI_THERMAL_TPEC_16,		/* Use ACPI EC regs, 16 sensors */
+};
+
+enum { /* TPACPI_THERMAL_TPEC_* */
+	TP_EC_THERMAL_TMP0 = 0x78,	/* ACPI EC regs TMP 0..7 */
+	TP_EC_THERMAL_TMP8 = 0xC0,	/* ACPI EC regs TMP 8..15 */
+	TP_EC_THERMAL_TMP_NA = -128,	/* ACPI EC sensor not available */
+};
+
+#define TPACPI_MAX_THERMAL_SENSORS 16	/* Max thermal sensors supported */
+struct ibm_thermal_sensors_struct {
+	s32 temp[TPACPI_MAX_THERMAL_SENSORS];
+};
+
+static enum thermal_access_mode thermal_read_mode;
+
+static int thermal_init(struct ibm_init_struct *iibm);
+static int thermal_get_sensor(int idx, s32 *value);
+static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s);
+static int thermal_read(char *p);
+
+
+/*
+ * Video subdriver
+ */
+
+enum video_access_mode {
+	TPACPI_VIDEO_NONE = 0,
+	TPACPI_VIDEO_570,	/* 570 */
+	TPACPI_VIDEO_770,	/* 600e/x, 770e, 770x */
+	TPACPI_VIDEO_NEW,	/* all others */
+};
+
+enum {	/* video status flags, based on VIDEO_570 */
+	TP_ACPI_VIDEO_S_LCD = 0x01,	/* LCD output enabled */
+	TP_ACPI_VIDEO_S_CRT = 0x02,	/* CRT output enabled */
+	TP_ACPI_VIDEO_S_DVI = 0x08,	/* DVI output enabled */
+};
+
+enum {  /* TPACPI_VIDEO_570 constants */
+	TP_ACPI_VIDEO_570_PHSCMD = 0x87,	/* unknown magic constant :( */
+	TP_ACPI_VIDEO_570_PHSMASK = 0x03,	/* PHS bits that map to
+						 * video_status_flags */
+	TP_ACPI_VIDEO_570_PHS2CMD = 0x8b,	/* unknown magic constant :( */
+	TP_ACPI_VIDEO_570_PHS2SET = 0x80,	/* unknown magic constant :( */
+};
+
+static enum video_access_mode video_supported;
+static int video_orig_autosw;
+static acpi_handle vid_handle, vid2_handle;
+
+static int video_init(struct ibm_init_struct *iibm);
+static void video_exit(void);
+static int video_outputsw_get(void);
+static int video_outputsw_set(int status);
+static int video_autosw_get(void);
+static int video_autosw_set(int enable);
+static int video_outputsw_cycle(void);
+static int video_expand_toggle(void);
+static int video_read(char *p);
+static int video_write(char *buf);
+
+
+/*
+ * Volume subdriver
+ */
+
+static int volume_offset = 0x30;
+
+static int volume_read(char *p);
+static int volume_write(char *buf);
+
+
+/*
+ * Wan subdriver
+ */
+
+#define TPACPI_WAN_SYSFS_GROUP "wwan"
+
+enum {
+	/* ACPI GWAN/SWAN bits */
+	TP_ACPI_WANCARD_HWPRESENT	= 0x01,	/* Wan hw available */
+	TP_ACPI_WANCARD_RADIOSSW	= 0x02,	/* Wan radio enabled */
+	TP_ACPI_WANCARD_UNK		= 0x04,	/* unknown function */
+};
+
+static int wan_init(struct ibm_init_struct *iibm);
+static int wan_get_radiosw(void);
+static int wan_set_radiosw(int radio_on);
+static int wan_read(char *p);
+static int wan_write(char *buf);
+
+
+#endif /* __THINKPAD_ACPI_H */
diff --git a/drivers/mmc/mmc_sysfs.c b/drivers/mmc/mmc_sysfs.c
index d32698b..e0e82d8 100644
--- a/drivers/mmc/mmc_sysfs.c
+++ b/drivers/mmc/mmc_sysfs.c
@@ -86,31 +86,26 @@
 {
 	struct mmc_card *card = dev_to_mmc_card(dev);
 	char ccc[13];
-	int i = 0;
+	int retval = 0, i = 0, length = 0;
 
-#define add_env(fmt,val)						\
-	({								\
-		int len, ret = -ENOMEM;					\
-		if (i < num_envp) {					\
-			envp[i++] = buf;				\
-			len = snprintf(buf, buf_size, fmt, val) + 1;	\
-			buf_size -= len;				\
-			buf += len;					\
-			if (buf_size >= 0)				\
-				ret = 0;				\
-		}							\
-		ret;							\
-	})
+#define add_env(fmt,val) do {					\
+	retval = add_uevent_var(envp, num_envp, &i,		\
+				buf, buf_size, &length,		\
+				fmt, val);			\
+	if (retval)						\
+		return retval;					\
+} while (0);
 
 	for (i = 0; i < 12; i++)
 		ccc[i] = card->csd.cmdclass & (1 << i) ? '1' : '0';
 	ccc[12] = '\0';
 
-	i = 0;
 	add_env("MMC_CCC=%s", ccc);
 	add_env("MMC_MANFID=%06x", card->cid.manfid);
 	add_env("MMC_NAME=%s", mmc_card_name(card));
 	add_env("MMC_OEMID=%04x", card->cid.oemid);
+#undef add_env
+	envp[i] = NULL;
 
 	return 0;
 }
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 26f75c2..c1b47db 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -1,8 +1,6 @@
 # $Id: Kconfig,v 1.11 2005/11/07 11:14:19 gleixner Exp $
 
-menu "Memory Technology Devices (MTD)"
-
-config MTD
+menuconfig MTD
 	tristate "Memory Technology Device (MTD) support"
 	help
 	  Memory Technology Devices are flash, RAM and similar chips, often
@@ -13,9 +11,10 @@
 	  them. It will also allow you to select individual drivers for
 	  particular hardware and users of MTD devices. If unsure, say N.
 
+if MTD
+
 config MTD_DEBUG
 	bool "Debugging"
-	depends on MTD
 	help
 	  This turns on low-level debugging for the entire MTD sub-system.
 	  Normally, you should say 'N'.
@@ -29,7 +28,6 @@
 
 config MTD_CONCAT
 	tristate "MTD concatenating support"
-	depends on MTD
 	help
 	  Support for concatenating several MTD devices into a single
 	  (virtual) one. This allows you to have -for example- a JFFS(2)
@@ -38,7 +36,6 @@
 
 config MTD_PARTITIONS
 	bool "MTD partitioning support"
-	depends on MTD
 	help
 	  If you have a device which needs to divide its flash chip(s) up
 	  into multiple 'partitions', each of which appears to the user as
@@ -153,11 +150,9 @@
 	  'armflash' map driver (CONFIG_MTD_ARMFLASH) does this, for example.
 
 comment "User Modules And Translation Layers"
-	depends on MTD
 
 config MTD_CHAR
 	tristate "Direct char device access to MTD devices"
-	depends on MTD
 	help
 	  This provides a character device for each MTD device present in
 	  the system, allowing the user to read and write directly to the
@@ -166,12 +161,12 @@
 
 config MTD_BLKDEVS
 	tristate "Common interface to block layer for MTD 'translation layers'"
-	depends on MTD && BLOCK
+	depends on BLOCK
 	default n
 
 config MTD_BLOCK
 	tristate "Caching block device access to MTD devices"
-	depends on MTD && BLOCK
+	depends on BLOCK
 	select MTD_BLKDEVS
 	---help---
 	  Although most flash chips have an erase size too large to be useful
@@ -194,7 +189,7 @@
 
 config MTD_BLOCK_RO
 	tristate "Readonly block device access to MTD devices"
-	depends on MTD_BLOCK!=y && MTD && BLOCK
+	depends on MTD_BLOCK!=y && BLOCK
 	select MTD_BLKDEVS
 	help
 	  This allows you to mount read-only file systems (such as cramfs)
@@ -206,7 +201,7 @@
 
 config FTL
 	tristate "FTL (Flash Translation Layer) support"
-	depends on MTD && BLOCK
+	depends on BLOCK
 	select MTD_BLKDEVS
 	---help---
 	  This provides support for the original Flash Translation Layer which
@@ -223,7 +218,7 @@
 
 config NFTL
 	tristate "NFTL (NAND Flash Translation Layer) support"
-	depends on MTD && BLOCK
+	depends on BLOCK
 	select MTD_BLKDEVS
 	---help---
 	  This provides support for the NAND Flash Translation Layer which is
@@ -247,7 +242,7 @@
 
 config INFTL
 	tristate "INFTL (Inverse NAND Flash Translation Layer) support"
-	depends on MTD && BLOCK
+	depends on BLOCK
 	select MTD_BLKDEVS
 	---help---
 	  This provides support for the Inverse NAND Flash Translation
@@ -265,7 +260,7 @@
 
 config RFD_FTL
         tristate "Resident Flash Disk (Flash Translation Layer) support"
-	depends on MTD && BLOCK
+	depends on BLOCK
 	select MTD_BLKDEVS
 	---help---
 	  This provides support for the flash translation layer known
@@ -276,7 +271,7 @@
 
 config SSFDC
 	tristate "NAND SSFDC (SmartMedia) read only translation layer"
-	depends on MTD && BLOCK
+	depends on BLOCK
 	select MTD_BLKDEVS
 	help
 	  This enables read only access to SmartMedia formatted NAND
@@ -292,5 +287,6 @@
 
 source "drivers/mtd/onenand/Kconfig"
 
-endmenu
+source "drivers/mtd/ubi/Kconfig"
 
+endif # MTD
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index c130e62..9205540 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -28,3 +28,5 @@
 inftl-objs		:= inftlcore.o inftlmount.o
 
 obj-y		+= chips/ maps/ devices/ nand/ onenand/
+
+obj-$(CONFIG_MTD_UBI)		+= ubi/
diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig
index 72e6d73..d28e0fc 100644
--- a/drivers/mtd/chips/Kconfig
+++ b/drivers/mtd/chips/Kconfig
@@ -6,7 +6,6 @@
 
 config MTD_CFI
 	tristate "Detect flash chips by Common Flash Interface (CFI) probe"
-	depends on MTD
 	select MTD_GEN_PROBE
 	help
 	  The Common Flash Interface specification was developed by Intel,
@@ -18,7 +17,6 @@
 
 config MTD_JEDECPROBE
 	tristate "Detect non-CFI AMD/JEDEC-compatible flash chips"
-	depends on MTD
 	select MTD_GEN_PROBE
 	help
 	  This option enables JEDEC-style probing of flash chips which are not
@@ -213,21 +211,18 @@
 
 config MTD_RAM
 	tristate "Support for RAM chips in bus mapping"
-	depends on MTD
 	help
 	  This option enables basic support for RAM chips accessed through
 	  a bus mapping driver.
 
 config MTD_ROM
 	tristate "Support for ROM chips in bus mapping"
-	depends on MTD
 	help
 	  This option enables basic support for ROM chips accessed through
 	  a bus mapping driver.
 
 config MTD_ABSENT
 	tristate "Support for absent chips in bus mapping"
-	depends on MTD
 	help
 	  This option enables support for a dummy probing driver used to
 	  allocated placeholder MTD devices on systems that have socketed
@@ -237,7 +232,6 @@
 	  with this driver will return -ENODEV upon access.
 
 config MTD_OBSOLETE_CHIPS
-	depends on MTD
 	bool "Older (theoretically obsoleted now) drivers for non-CFI chips"
 	help
 	  This option does not enable any code directly, but will allow you to
@@ -250,7 +244,7 @@
 
 config MTD_AMDSTD
 	tristate "AMD compatible flash chip support (non-CFI)"
-	depends on MTD && MTD_OBSOLETE_CHIPS && BROKEN
+	depends on MTD_OBSOLETE_CHIPS && BROKEN
 	help
 	  This option enables support for flash chips using AMD-compatible
 	  commands, including some which are not CFI-compatible and hence
@@ -260,7 +254,7 @@
 
 config MTD_SHARP
 	tristate "pre-CFI Sharp chip support"
-	depends on MTD && MTD_OBSOLETE_CHIPS
+	depends on MTD_OBSOLETE_CHIPS
 	help
 	  This option enables support for flash chips using Sharp-compatible
 	  commands, including some which are not CFI-compatible and hence
@@ -268,7 +262,7 @@
 
 config MTD_JEDEC
 	tristate "JEDEC device support"
-	depends on MTD && MTD_OBSOLETE_CHIPS && BROKEN
+	depends on MTD_OBSOLETE_CHIPS && BROKEN
 	help
 	  Enable older JEDEC flash interface devices for self
 	  programming flash.  It is commonly used in older AMD chips.  It is
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c
index f334959..2f19fa7 100644
--- a/drivers/mtd/chips/cfi_cmdset_0001.c
+++ b/drivers/mtd/chips/cfi_cmdset_0001.c
@@ -15,6 +15,8 @@
  *	- optimized write buffer method
  * 02/05/2002	Christopher Hoover <ch@hpl.hp.com>/<ch@murgatroid.com>
  *	- reworked lock/unlock/erase support for var size flash
+ * 21/03/2007   Rodolfo Giometti <giometti@linux.it>
+ * 	- auto unlock sectors on resume for auto locking flash on power up
  */
 
 #include <linux/module.h>
@@ -30,6 +32,7 @@
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/reboot.h>
+#include <linux/bitmap.h>
 #include <linux/mtd/xip.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/mtd.h>
@@ -220,6 +223,15 @@
 	}
 }
 
+/*
+ * Some chips power-up with all sectors locked by default.
+ */
+static void fixup_use_powerup_lock(struct mtd_info *mtd, void *param)
+{
+	printk(KERN_INFO "Using auto-unlock on power-up/resume\n" );
+	mtd->flags |= MTD_STUPID_LOCK;
+}
+
 static struct cfi_fixup cfi_fixup_table[] = {
 #ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
 	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL },
@@ -232,6 +244,7 @@
 #endif
 	{ CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct, NULL },
 	{ CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb, NULL },
+	{ MANUFACTURER_INTEL, 0x891c,	      fixup_use_powerup_lock, NULL, },
 	{ 0, 0, NULL, NULL }
 };
 
@@ -460,6 +473,7 @@
 			mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset;
 			mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize;
 			mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum;
+			mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].lockmap = kmalloc(ernum / 8 + 1, GFP_KERNEL);
 		}
 		offset += (ersize * ernum);
 	}
@@ -1825,8 +1839,7 @@
 	}
 }
 
-#ifdef DEBUG_LOCK_BITS
-static int __xipram do_printlockstatus_oneblock(struct map_info *map,
+static int __xipram do_getlockstatus_oneblock(struct map_info *map,
 						struct flchip *chip,
 						unsigned long adr,
 						int len, void *thunk)
@@ -1840,8 +1853,17 @@
 	chip->state = FL_JEDEC_QUERY;
 	status = cfi_read_query(map, adr+(2*ofs_factor));
 	xip_enable(map, chip, 0);
+	return status;
+}
+
+#ifdef DEBUG_LOCK_BITS
+static int __xipram do_printlockstatus_oneblock(struct map_info *map,
+						struct flchip *chip,
+						unsigned long adr,
+						int len, void *thunk)
+{
 	printk(KERN_DEBUG "block status register for 0x%08lx is %x\n",
-	       adr, status);
+	       adr, do_getlockstatus_oneblock(map, chip, adr, len, thunk));
 	return 0;
 }
 #endif
@@ -2216,14 +2238,45 @@
 
 #endif
 
+static void cfi_intelext_save_locks(struct mtd_info *mtd)
+{
+	struct mtd_erase_region_info *region;
+	int block, status, i;
+	unsigned long adr;
+	size_t len;
+
+	for (i = 0; i < mtd->numeraseregions; i++) {
+		region = &mtd->eraseregions[i];
+		if (!region->lockmap)
+			continue;
+
+		for (block = 0; block < region->numblocks; block++){
+			len = region->erasesize;
+			adr = region->offset + block * len;
+
+			status = cfi_varsize_frob(mtd,
+					do_getlockstatus_oneblock, adr, len, 0);
+			if (status)
+				set_bit(block, region->lockmap);
+			else
+				clear_bit(block, region->lockmap);
+		}
+	}
+}
+
 static int cfi_intelext_suspend(struct mtd_info *mtd)
 {
 	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
+	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
 	int i;
 	struct flchip *chip;
 	int ret = 0;
 
+	if ((mtd->flags & MTD_STUPID_LOCK)
+	    && extp && (extp->FeatureSupport & (1 << 5)))
+		cfi_intelext_save_locks(mtd);
+
 	for (i=0; !ret && i<cfi->numchips; i++) {
 		chip = &cfi->chips[i];
 
@@ -2285,10 +2338,33 @@
 	return ret;
 }
 
+static void cfi_intelext_restore_locks(struct mtd_info *mtd)
+{
+	struct mtd_erase_region_info *region;
+	int block, i;
+	unsigned long adr;
+	size_t len;
+
+	for (i = 0; i < mtd->numeraseregions; i++) {
+		region = &mtd->eraseregions[i];
+		if (!region->lockmap)
+			continue;
+
+		for (block = 0; block < region->numblocks; block++) {
+			len = region->erasesize;
+			adr = region->offset + block * len;
+
+			if (!test_bit(block, region->lockmap))
+				cfi_intelext_unlock(mtd, adr, len);
+		}
+	}
+}
+
 static void cfi_intelext_resume(struct mtd_info *mtd)
 {
 	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
+	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
 	int i;
 	struct flchip *chip;
 
@@ -2307,6 +2383,10 @@
 
 		spin_unlock(chip->mutex);
 	}
+
+	if ((mtd->flags & MTD_STUPID_LOCK)
+	    && extp && (extp->FeatureSupport & (1 << 5)))
+		cfi_intelext_restore_locks(mtd);
 }
 
 static int cfi_intelext_reset(struct mtd_info *mtd)
@@ -2347,12 +2427,19 @@
 {
 	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
+	struct mtd_erase_region_info *region;
+	int i;
 	cfi_intelext_reset(mtd);
 	unregister_reboot_notifier(&mtd->reboot_notifier);
 	kfree(cfi->cmdset_priv);
 	kfree(cfi->cfiq);
 	kfree(cfi->chips[0].priv);
 	kfree(cfi);
+	for (i = 0; i < mtd->numeraseregions; i++) {
+		region = &mtd->eraseregions[i];
+		if (region->lockmap)
+			kfree(region->lockmap);
+	}
 	kfree(mtd->eraseregions);
 }
 
diff --git a/drivers/mtd/chips/fwh_lock.h b/drivers/mtd/chips/fwh_lock.h
index 77303ce..ab44f2b 100644
--- a/drivers/mtd/chips/fwh_lock.h
+++ b/drivers/mtd/chips/fwh_lock.h
@@ -65,11 +65,12 @@
 		return ret;
 	}
 
+	chip->oldstate = chip->state;
 	chip->state = xxlt->state;
 	map_write(map, CMD(xxlt->val), adr);
 
 	/* Done and happy. */
-	chip->state = FL_READY;
+	chip->state = chip->oldstate;
 	put_chip(map, chip, adr);
 	spin_unlock(chip->mutex);
 	return 0;
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 440f685..690c942 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -6,7 +6,7 @@
 
 config MTD_PMC551
 	tristate "Ramix PMC551 PCI Mezzanine RAM card support"
-	depends on MTD && PCI
+	depends on PCI
 	---help---
 	  This provides a MTD device driver for the Ramix PMC551 RAM PCI card
 	  from Ramix Inc. <http://www.ramix.com/products/memory/pmc551.html>.
@@ -40,7 +40,7 @@
 
 config MTD_MS02NV
 	tristate "DEC MS02-NV NVRAM module support"
-	depends on MTD && MACH_DECSTATION
+	depends on MACH_DECSTATION
 	help
 	  This is an MTD driver for the DEC's MS02-NV (54-20948-01) battery
 	  backed-up NVRAM module.  The module was originally meant as an NFS
@@ -54,15 +54,23 @@
 
 config MTD_DATAFLASH
 	tristate "Support for AT45xxx DataFlash"
-	depends on MTD && SPI_MASTER && EXPERIMENTAL
+	depends on SPI_MASTER && EXPERIMENTAL
 	help
 	  This enables access to AT45xxx DataFlash chips, using SPI.
 	  Sometimes DataFlash chips are packaged inside MMC-format
 	  cards; at this writing, the MMC stack won't handle those.
 
+config MTD_DATAFLASH26
+	tristate "AT91RM9200 DataFlash AT26xxx"
+	depends on MTD && ARCH_AT91RM9200 && AT91_SPI
+	help
+	  This enables access to the DataFlash chip (AT26xxx) on an
+	  AT91RM9200-based board.
+	  If you have such a board and such a DataFlash, say 'Y'.
+
 config MTD_M25P80
 	tristate "Support for M25 SPI Flash"
-	depends on MTD && SPI_MASTER && EXPERIMENTAL
+	depends on SPI_MASTER && EXPERIMENTAL
 	help
 	  This enables access to ST M25P80 and similar SPI flash chips,
 	  used for program and data storage.  Set up your spi devices
@@ -70,7 +78,6 @@
 
 config MTD_SLRAM
 	tristate "Uncached system RAM"
-	depends on MTD
 	help
 	  If your CPU cannot cache all of the physical memory in your machine,
 	  you can still use it for storage or swap by using this driver to
@@ -78,7 +85,6 @@
 
 config MTD_PHRAM
 	tristate "Physical system RAM"
-	depends on MTD
 	help
 	  This is a re-implementation of the slram driver above.
 
@@ -88,7 +94,7 @@
 
 config MTD_LART
 	tristate "28F160xx flash driver for LART"
-	depends on SA1100_LART && MTD
+	depends on SA1100_LART
 	help
 	  This enables the flash driver for LART. Please note that you do
 	  not need any mapping/chip driver for LART. This one does it all
@@ -96,7 +102,6 @@
 
 config MTD_MTDRAM
 	tristate "Test driver using RAM"
-	depends on MTD
 	help
 	  This enables a test MTD device driver which uses vmalloc() to
 	  provide storage.  You probably want to say 'N' unless you're
@@ -136,7 +141,7 @@
 
 config MTD_BLOCK2MTD
 	tristate "MTD using block device"
-	depends on MTD && BLOCK
+	depends on BLOCK
 	help
 	  This driver allows a block device to appear as an MTD. It would
 	  generally be used in the following cases:
@@ -150,7 +155,6 @@
 
 config MTD_DOC2000
 	tristate "M-Systems Disk-On-Chip 2000 and Millennium (DEPRECATED)"
-	depends on MTD
 	select MTD_DOCPROBE
 	select MTD_NAND_IDS
 	---help---
@@ -173,7 +177,6 @@
 
 config MTD_DOC2001
 	tristate "M-Systems Disk-On-Chip Millennium-only alternative driver (DEPRECATED)"
-	depends on MTD
 	select MTD_DOCPROBE
 	select MTD_NAND_IDS
 	---help---
@@ -195,7 +198,6 @@
 
 config MTD_DOC2001PLUS
 	tristate "M-Systems Disk-On-Chip Millennium Plus"
-	depends on MTD
 	select MTD_DOCPROBE
 	select MTD_NAND_IDS
 	---help---
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index 0f788d5..8ab568b 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -16,4 +16,5 @@
 obj-$(CONFIG_MTD_LART)		+= lart.o
 obj-$(CONFIG_MTD_BLOCK2MTD)	+= block2mtd.o
 obj-$(CONFIG_MTD_DATAFLASH)	+= mtd_dataflash.o
+obj-$(CONFIG_MTD_DATAFLASH26)	+= at91_dataflash26.o
 obj-$(CONFIG_MTD_M25P80)	+= m25p80.o
diff --git a/drivers/mtd/devices/at91_dataflash26.c b/drivers/mtd/devices/at91_dataflash26.c
new file mode 100644
index 0000000..64ce37f
--- /dev/null
+++ b/drivers/mtd/devices/at91_dataflash26.c
@@ -0,0 +1,485 @@
+/*
+ * Atmel DataFlash driver for Atmel AT91RM9200 (Thunder)
+ * This is a largely modified version of at91_dataflash.c that
+ * supports AT26xxx dataflash chips. The original driver supports
+ * AT45xxx chips.
+ *
+ * Note: This driver was only tested with an AT26F004. It should be
+ * easy to make it work with other AT26xxx dataflash devices, though.
+ *
+ * Copyright (C) 2007 Hans J. Koch <hjk@linutronix.de>
+ * original Copyright (C) SAN People (Pty) Ltd
+ *
+ * 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.
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+
+#include <asm/arch/at91_spi.h>
+
+#define DATAFLASH_MAX_DEVICES	4	/* max number of dataflash devices */
+
+#define MANUFACTURER_ID_ATMEL		0x1F
+
+/* command codes */
+
+#define AT26_OP_READ_STATUS		0x05
+#define AT26_OP_READ_DEV_ID		0x9F
+#define AT26_OP_ERASE_PAGE_4K		0x20
+#define AT26_OP_READ_ARRAY_FAST		0x0B
+#define AT26_OP_SEQUENTIAL_WRITE	0xAF
+#define AT26_OP_WRITE_ENABLE		0x06
+#define AT26_OP_WRITE_DISABLE		0x04
+#define AT26_OP_SECTOR_PROTECT		0x36
+#define AT26_OP_SECTOR_UNPROTECT	0x39
+
+/* status register bits */
+
+#define AT26_STATUS_BUSY		0x01
+#define AT26_STATUS_WRITE_ENABLE	0x02
+
+struct dataflash_local
+{
+	int spi;			/* SPI chip-select number */
+	unsigned int page_size;		/* number of bytes per page */
+};
+
+
+/* Detected DataFlash devices */
+static struct mtd_info* mtd_devices[DATAFLASH_MAX_DEVICES];
+static int nr_devices = 0;
+
+/* Allocate a single SPI transfer descriptor.  We're assuming that if multiple
+   SPI transfers occur at the same time, spi_access_bus() will serialize them.
+   If this is not valid, then either (i) each dataflash 'priv' structure
+   needs it's own transfer descriptor, (ii) we lock this one, or (iii) use
+   another mechanism.   */
+static struct spi_transfer_list* spi_transfer_desc;
+
+/*
+ * Perform a SPI transfer to access the DataFlash device.
+ */
+static int do_spi_transfer(int nr, char* tx, int tx_len, char* rx, int rx_len,
+		char* txnext, int txnext_len, char* rxnext, int rxnext_len)
+{
+	struct spi_transfer_list* list = spi_transfer_desc;
+
+	list->tx[0] = tx;	list->txlen[0] = tx_len;
+	list->rx[0] = rx;	list->rxlen[0] = rx_len;
+
+	list->tx[1] = txnext; 	list->txlen[1] = txnext_len;
+	list->rx[1] = rxnext;	list->rxlen[1] = rxnext_len;
+
+	list->nr_transfers = nr;
+	/* Note: spi_transfer() always returns 0, there are no error checks */
+	return spi_transfer(list);
+}
+
+/*
+ * Return the status of the DataFlash device.
+ */
+static unsigned char at91_dataflash26_status(void)
+{
+	unsigned char command[2];
+
+	command[0] = AT26_OP_READ_STATUS;
+	command[1] = 0;
+
+	do_spi_transfer(1, command, 2, command, 2, NULL, 0, NULL, 0);
+
+	return command[1];
+}
+
+/*
+ * Poll the DataFlash device until it is READY.
+ */
+static unsigned char at91_dataflash26_waitready(void)
+{
+	unsigned char status;
+
+	while (1) {
+		status = at91_dataflash26_status();
+		if (!(status & AT26_STATUS_BUSY))
+			return status;
+	}
+}
+
+/*
+ * Enable/disable write access
+ */
+ static void at91_dataflash26_write_enable(int enable)
+{
+	unsigned char cmd[2];
+
+	DEBUG(MTD_DEBUG_LEVEL3, "write_enable: enable=%i\n", enable);
+
+	if (enable)
+		cmd[0] = AT26_OP_WRITE_ENABLE;
+	else
+		cmd[0] = AT26_OP_WRITE_DISABLE;
+	cmd[1] = 0;
+
+	do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0);
+}
+
+/*
+ * Protect/unprotect sector
+ */
+ static void at91_dataflash26_sector_protect(loff_t addr, int protect)
+{
+	unsigned char cmd[4];
+
+	DEBUG(MTD_DEBUG_LEVEL3, "sector_protect: addr=0x%06x prot=%d\n",
+	       addr, protect);
+
+	if (protect)
+		cmd[0] = AT26_OP_SECTOR_PROTECT;
+	else
+		cmd[0] = AT26_OP_SECTOR_UNPROTECT;
+	cmd[1] = (addr & 0x00FF0000) >> 16;
+	cmd[2] = (addr & 0x0000FF00) >> 8;
+	cmd[3] = (addr & 0x000000FF);
+
+	do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0);
+}
+
+/*
+ * Erase blocks of flash.
+ */
+static int at91_dataflash26_erase(struct mtd_info *mtd,
+				  struct erase_info *instr)
+{
+	struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
+	unsigned char cmd[4];
+
+	DEBUG(MTD_DEBUG_LEVEL1, "dataflash_erase: addr=0x%06x len=%i\n",
+	       instr->addr, instr->len);
+
+	/* Sanity checks */
+	if (priv->page_size != 4096)
+		return -EINVAL; /* Can't handle other sizes at the moment */
+
+	if (   ((instr->len % mtd->erasesize) != 0)
+	    || ((instr->len % priv->page_size) != 0)
+	    || ((instr->addr % priv->page_size) != 0)
+	    || ((instr->addr + instr->len) > mtd->size))
+		return -EINVAL;
+
+	spi_access_bus(priv->spi);
+
+	while (instr->len > 0) {
+		at91_dataflash26_write_enable(1);
+		at91_dataflash26_sector_protect(instr->addr, 0);
+		at91_dataflash26_write_enable(1);
+		cmd[0] = AT26_OP_ERASE_PAGE_4K;
+		cmd[1] = (instr->addr & 0x00FF0000) >> 16;
+		cmd[2] = (instr->addr & 0x0000FF00) >> 8;
+		cmd[3] = (instr->addr & 0x000000FF);
+
+		DEBUG(MTD_DEBUG_LEVEL3, "ERASE: (0x%02x) 0x%02x 0x%02x"
+					"0x%02x\n",
+			cmd[0], cmd[1], cmd[2], cmd[3]);
+
+		do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0);
+		at91_dataflash26_waitready();
+
+		instr->addr += priv->page_size;	 /* next page */
+		instr->len -= priv->page_size;
+	}
+
+	at91_dataflash26_write_enable(0);
+	spi_release_bus(priv->spi);
+
+	/* Inform MTD subsystem that erase is complete */
+	instr->state = MTD_ERASE_DONE;
+	if (instr->callback)
+		instr->callback(instr);
+
+	return 0;
+}
+
+/*
+ * Read from the DataFlash device.
+ *   from   : Start offset in flash device
+ *   len    : Number of bytes to read
+ *   retlen : Number of bytes actually read
+ *   buf    : Buffer that will receive data
+ */
+static int at91_dataflash26_read(struct mtd_info *mtd, loff_t from, size_t len,
+				 size_t *retlen, u_char *buf)
+{
+	struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
+	unsigned char cmd[5];
+
+	DEBUG(MTD_DEBUG_LEVEL1, "dataflash_read: %lli .. %lli\n",
+	      from, from+len);
+
+	*retlen = 0;
+
+	/* Sanity checks */
+	if (!len)
+		return 0;
+	if (from + len > mtd->size)
+		return -EINVAL;
+
+	cmd[0] = AT26_OP_READ_ARRAY_FAST;
+	cmd[1] = (from & 0x00FF0000) >> 16;
+	cmd[2] = (from & 0x0000FF00) >> 8;
+	cmd[3] = (from & 0x000000FF);
+	/* cmd[4] is a "Don't care" byte  */
+
+	DEBUG(MTD_DEBUG_LEVEL3, "READ: (0x%02x) 0x%02x 0x%02x 0x%02x\n",
+	       cmd[0], cmd[1], cmd[2], cmd[3]);
+
+	spi_access_bus(priv->spi);
+	do_spi_transfer(2, cmd, 5, cmd, 5, buf, len, buf, len);
+	spi_release_bus(priv->spi);
+
+	*retlen = len;
+	return 0;
+}
+
+/*
+ * Write to the DataFlash device.
+ *   to     : Start offset in flash device
+ *   len    : Number of bytes to write
+ *   retlen : Number of bytes actually written
+ *   buf    : Buffer containing the data
+ */
+static int at91_dataflash26_write(struct mtd_info *mtd, loff_t to, size_t len,
+				  size_t *retlen, const u_char *buf)
+{
+	struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
+	unsigned int addr, buf_index = 0;
+	int ret = -EIO, sector, last_sector;
+	unsigned char status, cmd[5];
+
+	DEBUG(MTD_DEBUG_LEVEL1, "dataflash_write: %lli .. %lli\n", to, to+len);
+
+	*retlen = 0;
+
+	/* Sanity checks */
+	if (!len)
+		return 0;
+	if (to + len > mtd->size)
+		return -EINVAL;
+
+	spi_access_bus(priv->spi);
+
+	addr = to;
+	last_sector = -1;
+
+	while (buf_index < len) {
+		sector = addr / priv->page_size;
+		/* Write first byte if a new sector begins */
+		if (sector != last_sector) {
+			at91_dataflash26_write_enable(1);
+			at91_dataflash26_sector_protect(addr, 0);
+			at91_dataflash26_write_enable(1);
+
+			/* Program first byte of a new sector */
+			cmd[0] = AT26_OP_SEQUENTIAL_WRITE;
+			cmd[1] = (addr & 0x00FF0000) >> 16;
+			cmd[2] = (addr & 0x0000FF00) >> 8;
+			cmd[3] = (addr & 0x000000FF);
+			cmd[4] = buf[buf_index++];
+			do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0);
+			status = at91_dataflash26_waitready();
+			addr++;
+			/* On write errors, the chip resets the write enable
+			   flag. This also happens after the last byte of a
+			   sector is successfully programmed. */
+			if (   ( !(status & AT26_STATUS_WRITE_ENABLE))
+			    && ((addr % priv->page_size) != 0) ) {
+				DEBUG(MTD_DEBUG_LEVEL1,
+					"write error1: addr=0x%06x, "
+					"status=0x%02x\n", addr, status);
+				goto write_err;
+			}
+			(*retlen)++;
+			last_sector = sector;
+		}
+
+		/* Write subsequent bytes in the same sector */
+		cmd[0] = AT26_OP_SEQUENTIAL_WRITE;
+		cmd[1] = buf[buf_index++];
+		do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0);
+		status = at91_dataflash26_waitready();
+		addr++;
+
+		if (   ( !(status & AT26_STATUS_WRITE_ENABLE))
+		    && ((addr % priv->page_size) != 0) ) {
+			DEBUG(MTD_DEBUG_LEVEL1, "write error2: addr=0x%06x, "
+				"status=0x%02x\n", addr, status);
+			goto write_err;
+		}
+
+		(*retlen)++;
+	}
+
+	ret = 0;
+	at91_dataflash26_write_enable(0);
+write_err:
+	spi_release_bus(priv->spi);
+	return ret;
+}
+
+/*
+ * Initialize and register DataFlash device with MTD subsystem.
+ */
+static int __init add_dataflash(int channel, char *name, int nr_pages,
+				int pagesize)
+{
+	struct mtd_info *device;
+	struct dataflash_local *priv;
+
+	if (nr_devices >= DATAFLASH_MAX_DEVICES) {
+		printk(KERN_ERR "at91_dataflash26: Too many devices "
+				"detected\n");
+		return 0;
+	}
+
+	device = kzalloc(sizeof(struct mtd_info) + strlen(name) + 8,
+			 GFP_KERNEL);
+	if (!device)
+		return -ENOMEM;
+
+	device->name = (char *)&device[1];
+	sprintf(device->name, "%s.spi%d", name, channel);
+	device->size = nr_pages * pagesize;
+	device->erasesize = pagesize;
+	device->owner = THIS_MODULE;
+	device->type = MTD_DATAFLASH;
+	device->flags = MTD_CAP_NORFLASH;
+	device->erase = at91_dataflash26_erase;
+	device->read = at91_dataflash26_read;
+	device->write = at91_dataflash26_write;
+
+	priv = (struct dataflash_local *)kzalloc(sizeof(struct dataflash_local),
+		GFP_KERNEL);
+	if (!priv) {
+		kfree(device);
+		return -ENOMEM;
+	}
+
+	priv->spi = channel;
+	priv->page_size = pagesize;
+	device->priv = priv;
+
+	mtd_devices[nr_devices] = device;
+	nr_devices++;
+	printk(KERN_INFO "at91_dataflash26: %s detected [spi%i] (%i bytes)\n",
+	       name, channel, device->size);
+
+	return add_mtd_device(device);
+}
+
+/*
+ * Detect and initialize DataFlash device connected to specified SPI channel.
+ *
+ */
+
+struct dataflash26_types {
+	unsigned char id0;
+	unsigned char id1;
+	char *name;
+	int pagesize;
+	int nr_pages;
+};
+
+struct dataflash26_types df26_types[] = {
+	{
+		.id0 = 0x04,
+		.id1 = 0x00,
+		.name = "AT26F004",
+		.pagesize = 4096,
+		.nr_pages = 128,
+	},
+	{
+		.id0 = 0x45,
+		.id1 = 0x01,
+		.name = "AT26DF081A", /* Not tested ! */
+		.pagesize = 4096,
+		.nr_pages = 256,
+	},
+};
+
+static int __init at91_dataflash26_detect(int channel)
+{
+	unsigned char status, cmd[5];
+	int i;
+
+	spi_access_bus(channel);
+	status = at91_dataflash26_status();
+
+	if (status == 0 || status == 0xff) {
+		printk(KERN_ERR "at91_dataflash26_detect: status error %d\n",
+			status);
+		spi_release_bus(channel);
+		return -ENODEV;
+	}
+
+	cmd[0] = AT26_OP_READ_DEV_ID;
+	do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0);
+	spi_release_bus(channel);
+
+	if (cmd[1] != MANUFACTURER_ID_ATMEL)
+		return -ENODEV;
+
+	for (i = 0; i < ARRAY_SIZE(df26_types); i++) {
+		if (   cmd[2] == df26_types[i].id0
+		    && cmd[3] == df26_types[i].id1)
+			return add_dataflash(channel,
+						df26_types[i].name,
+						df26_types[i].nr_pages,
+						df26_types[i].pagesize);
+	}
+
+	printk(KERN_ERR "at91_dataflash26_detect: Unsupported device "
+			"(0x%02x/0x%02x)\n", cmd[2], cmd[3]);
+	return -ENODEV;
+}
+
+static int __init at91_dataflash26_init(void)
+{
+	spi_transfer_desc = kmalloc(sizeof(struct spi_transfer_list),
+				    GFP_KERNEL);
+	if (!spi_transfer_desc)
+		return -ENOMEM;
+
+	/* DataFlash (SPI chip select 0) */
+	at91_dataflash26_detect(0);
+
+#ifdef CONFIG_MTD_AT91_DATAFLASH_CARD
+	/* DataFlash card (SPI chip select 3) */
+	at91_dataflash26_detect(3);
+#endif
+	return 0;
+}
+
+static void __exit at91_dataflash26_exit(void)
+{
+	int i;
+
+	for (i = 0; i < DATAFLASH_MAX_DEVICES; i++) {
+		if (mtd_devices[i]) {
+			del_mtd_device(mtd_devices[i]);
+			kfree(mtd_devices[i]->priv);
+			kfree(mtd_devices[i]);
+		}
+	}
+	nr_devices = 0;
+	kfree(spi_transfer_desc);
+}
+
+module_init(at91_dataflash26_init);
+module_exit(at91_dataflash26_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Hans J. Koch");
+MODULE_DESCRIPTION("DataFlash AT26xxx driver for Atmel AT91RM9200");
diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c
index f9f2ce7..ce47544 100644
--- a/drivers/mtd/devices/block2mtd.c
+++ b/drivers/mtd/devices/block2mtd.c
@@ -40,56 +40,9 @@
 static LIST_HEAD(blkmtd_device_list);
 
 
-#define PAGE_READAHEAD 64
-static void cache_readahead(struct address_space *mapping, int index)
+static struct page* page_read(struct address_space *mapping, int index)
 {
 	filler_t *filler = (filler_t*)mapping->a_ops->readpage;
-	int i, pagei;
-	unsigned ret = 0;
-	unsigned long end_index;
-	struct page *page;
-	LIST_HEAD(page_pool);
-	struct inode *inode = mapping->host;
-	loff_t isize = i_size_read(inode);
-
-	if (!isize) {
-		INFO("iSize=0 in cache_readahead\n");
-		return;
-	}
-
-	end_index = ((isize - 1) >> PAGE_CACHE_SHIFT);
-
-	read_lock_irq(&mapping->tree_lock);
-	for (i = 0; i < PAGE_READAHEAD; i++) {
-		pagei = index + i;
-		if (pagei > end_index) {
-			INFO("Overrun end of disk in cache readahead\n");
-			break;
-		}
-		page = radix_tree_lookup(&mapping->page_tree, pagei);
-		if (page && (!i))
-			break;
-		if (page)
-			continue;
-		read_unlock_irq(&mapping->tree_lock);
-		page = page_cache_alloc_cold(mapping);
-		read_lock_irq(&mapping->tree_lock);
-		if (!page)
-			break;
-		page->index = pagei;
-		list_add(&page->lru, &page_pool);
-		ret++;
-	}
-	read_unlock_irq(&mapping->tree_lock);
-	if (ret)
-		read_cache_pages(mapping, &page_pool, filler, NULL);
-}
-
-
-static struct page* page_readahead(struct address_space *mapping, int index)
-{
-	filler_t *filler = (filler_t*)mapping->a_ops->readpage;
-	cache_readahead(mapping, index);
 	return read_cache_page(mapping, index, filler, NULL);
 }
 
@@ -105,14 +58,14 @@
 	u_long *max;
 
 	while (pages) {
-		page = page_readahead(mapping, index);
+		page = page_read(mapping, index);
 		if (!page)
 			return -ENOMEM;
 		if (IS_ERR(page))
 			return PTR_ERR(page);
 
-		max = (u_long*)page_address(page) + PAGE_SIZE;
-		for (p=(u_long*)page_address(page); p<max; p++)
+		max = page_address(page) + PAGE_SIZE;
+		for (p=page_address(page); p<max; p++)
 			if (*p != -1UL) {
 				lock_page(page);
 				memset(page_address(page), 0xff, PAGE_SIZE);
@@ -174,8 +127,7 @@
 			cpylen = len;	// this page
 		len = len - cpylen;
 
-		//      Get page
-		page = page_readahead(dev->blkdev->bd_inode->i_mapping, index);
+		page = page_read(dev->blkdev->bd_inode->i_mapping, index);
 		if (!page)
 			return -ENOMEM;
 		if (IS_ERR(page))
@@ -213,8 +165,7 @@
 			cpylen = len;			// this page
 		len = len - cpylen;
 
-		//	Get page
-		page = page_readahead(mapping, index);
+		page = page_read(mapping, index);
 		if (!page)
 			return -ENOMEM;
 		if (IS_ERR(page))
@@ -308,9 +259,9 @@
 		/* We might not have rootfs mounted at this point. Try
 		   to resolve the device name by other means. */
 
-		dev_t dev = name_to_dev_t(devname);
-		if (dev != 0) {
-			bdev = open_by_devnum(dev, FMODE_WRITE | FMODE_READ);
+		dev_t devt = name_to_dev_t(devname);
+		if (devt) {
+			bdev = open_by_devnum(devt, FMODE_WRITE | FMODE_READ);
 		}
 	}
 #endif
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index bbf0553..d990d81 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -6,7 +6,6 @@
 
 config MTD_COMPLEX_MAPPINGS
 	bool "Support non-linear mappings of flash chips"
-	depends on MTD
 	help
 	  This causes the chip drivers to allow for complicated
 	  paged mappings of flash chips.
@@ -69,6 +68,39 @@
 	  physically into the CPU's memory. The mapping description here is
 	  taken from OF device tree.
 
+config MTD_PMC_MSP_EVM
+	tristate "CFI Flash device mapped on PMC-Sierra MSP"
+	depends on PMC_MSP && MTD_CFI
+	select MTD_PARTITIONS
+	help
+	  This provides a 'mapping' driver which support the way
+          in which user-programmable flash chips are connected on the
+          PMC-Sierra MSP eval/demo boards
+
+choice
+	prompt "Maximum mappable memory avialable for flash IO"
+	depends on MTD_PMC_MSP_EVM
+	default MSP_FLASH_MAP_LIMIT_32M
+
+config MSP_FLASH_MAP_LIMIT_32M
+	bool "32M"
+
+endchoice
+
+config MSP_FLASH_MAP_LIMIT
+	hex
+	default "0x02000000"
+	depends on MSP_FLASH_MAP_LIMIT_32M
+
+config MTD_PMC_MSP_RAMROOT
+	tristate "Embedded RAM block device for root on PMC-Sierra MSP"
+	depends on PMC_MSP_EMBEDDED_ROOTFS && \
+			(MTD_BLOCK || MTD_BLOCK_RO) && \
+			MTD_RAM
+	help
+	  This provides support for the embedded root file system
+          on PMC MSP devices.  This memory is mapped as a MTD block device.
+
 config MTD_SUN_UFLASH
 	tristate "Sun Microsystems userflash support"
 	depends on SPARC && MTD_CFI
@@ -240,13 +272,13 @@
 
 config MTD_ALCHEMY
 	tristate "AMD Alchemy Pb1xxx/Db1xxx/RDK MTD support"
-	depends on SOC_AU1X00
+	depends on SOC_AU1X00 && MTD_PARTITIONS && MTD_CFI
 	help
 	  Flash memory access on AMD Alchemy Pb/Db/RDK Reference Boards
 
 config MTD_MTX1
 	tristate "4G Systems MTX-1 Flash device"
-	depends on MIPS && MIPS_MTX1
+	depends on MIPS_MTX1 && MTD_CFI
 	help
 	  Flash memory access on 4G Systems MTX-1 Board. If you have one of
 	  these boards and would like to use the flash chips on it, say 'Y'.
@@ -384,7 +416,7 @@
 
 config MTD_OCELOT
 	tristate "Momenco Ocelot boot flash device"
-	depends on MIPS && MOMENCO_OCELOT
+	depends on MOMENCO_OCELOT
 	help
 	  This enables access routines for the boot flash device and for the
 	  NVRAM on the Momenco Ocelot board. If you have one of these boards
@@ -517,7 +549,7 @@
 # This needs CFI or JEDEC, depending on the cards found.
 config MTD_PCI
 	tristate "PCI MTD driver"
-	depends on MTD && PCI && MTD_COMPLEX_MAPPINGS
+	depends on PCI && MTD_COMPLEX_MAPPINGS
 	help
 	  Mapping for accessing flash devices on add-in cards like the Intel XScale
 	  IQ80310 card, and the Intel EBSA285 card in blank ROM programming mode
@@ -527,7 +559,7 @@
 
 config MTD_PCMCIA
 	tristate "PCMCIA MTD driver"
-	depends on MTD && PCMCIA && MTD_COMPLEX_MAPPINGS && BROKEN
+	depends on PCMCIA && MTD_COMPLEX_MAPPINGS && BROKEN
 	help
 	  Map driver for accessing PCMCIA linear flash memory cards. These
 	  cards are usually around 4-16MiB in size. This does not include
@@ -591,13 +623,12 @@
 
 config MTD_SHARP_SL
 	bool "ROM mapped on Sharp SL Series"
-	depends on MTD && ARCH_PXA
+	depends on ARCH_PXA
 	help
 	  This enables access to the flash chip on the Sharp SL Series of PDAs.
 
 config MTD_PLATRAM
 	tristate "Map driver for platform device RAM (mtd-ram)"
-	depends on MTD
 	select MTD_RAM
 	help
 	  Map driver for RAM areas described via the platform device
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index 071d0bf..de036c5 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -27,6 +27,8 @@
 obj-$(CONFIG_MTD_OCTAGON)	+= octagon-5066.o
 obj-$(CONFIG_MTD_PHYSMAP)	+= physmap.o
 obj-$(CONFIG_MTD_PHYSMAP_OF)	+= physmap_of.o
+obj-$(CONFIG_MTD_PMC_MSP_EVM)   += pmcmsp-flash.o
+obj-$(CONFIG_MTD_PMC_MSP_RAMROOT)+= pmcmsp-ramroot.o
 obj-$(CONFIG_MTD_PNC2000)	+= pnc2000.o
 obj-$(CONFIG_MTD_PCMCIA)	+= pcmciamtd.o
 obj-$(CONFIG_MTD_RPXLITE)	+= rpxlite.o
diff --git a/drivers/mtd/maps/alchemy-flash.c b/drivers/mtd/maps/alchemy-flash.c
index 7fc8097..84fbe0e 100644
--- a/drivers/mtd/maps/alchemy-flash.c
+++ b/drivers/mtd/maps/alchemy-flash.c
@@ -1,10 +1,7 @@
 /*
  * Flash memory access on AMD Alchemy evaluation boards
  *
- * $Id: alchemy-flash.c,v 1.2 2005/11/07 11:14:26 gleixner Exp $
- *
  * (C) 2003, 2004 Pete Popov <ppopov@embeddedalley.com>
- *
  */
 
 #include <linux/init.h>
@@ -18,12 +15,6 @@
 
 #include <asm/io.h>
 
-#ifdef 	DEBUG_RW
-#define	DBG(x...)	printk(x)
-#else
-#define	DBG(x...)
-#endif
-
 #ifdef CONFIG_MIPS_PB1000
 #define BOARD_MAP_NAME "Pb1000 Flash"
 #define BOARD_FLASH_SIZE 0x00800000 /* 8MB */
diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c
index 3d4a4d8..688ef49 100644
--- a/drivers/mtd/maps/ck804xrom.c
+++ b/drivers/mtd/maps/ck804xrom.c
@@ -338,7 +338,7 @@
 	}
 	return -ENXIO;
 #if 0
-	return pci_module_init(&ck804xrom_driver);
+	return pci_register_driver(&ck804xrom_driver);
 #endif
 }
 
diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c
index 2b6504e..894c0b2 100644
--- a/drivers/mtd/maps/plat-ram.c
+++ b/drivers/mtd/maps/plat-ram.c
@@ -169,7 +169,8 @@
 		goto exit_free;
 	}
 
-	dev_dbg(&pdev->dev, "got platform resource %p (0x%lx)\n", res, res->start);
+	dev_dbg(&pdev->dev, "got platform resource %p (0x%llx)\n", res,
+		(unsigned long long)res->start);
 
 	/* setup map parameters */
 
diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c
new file mode 100644
index 0000000..7e0377e
--- /dev/null
+++ b/drivers/mtd/maps/pmcmsp-flash.c
@@ -0,0 +1,184 @@
+/*
+ * Mapping of a custom board with both AMD CFI and JEDEC flash in partitions.
+ * Config with both CFI and JEDEC device support.
+ *
+ * Basically physmap.c with the addition of partitions and
+ * an array of mapping info to accomodate more than one flash type per board.
+ *
+ * Copyright 2005-2007 PMC-Sierra, Inc.
+ *
+ *  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  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
+ *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+ *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
+ *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  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/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+
+#include <msp_prom.h>
+#include <msp_regs.h>
+
+
+static struct mtd_info **msp_flash;
+static struct mtd_partition **msp_parts;
+static struct map_info *msp_maps;
+static int fcnt;
+
+#define DEBUG_MARKER printk(KERN_NOTICE "%s[%d]\n",__FUNCTION__,__LINE__)
+
+int __init init_msp_flash(void)
+{
+	int i, j;
+	int offset, coff;
+	char *env;
+	int pcnt;
+	char flash_name[] = "flash0";
+	char part_name[] = "flash0_0";
+	unsigned addr, size;
+
+	/* If ELB is disabled by "ful-mux" mode, we can't get at flash */
+	if ((*DEV_ID_REG & DEV_ID_SINGLE_PC) &&
+	    (*ELB_1PC_EN_REG & SINGLE_PCCARD)) {
+		printk(KERN_NOTICE "Single PC Card mode: no flash access\n");
+		return -ENXIO;
+	}
+
+	/* examine the prom environment for flash devices */
+	for (fcnt = 0; (env = prom_getenv(flash_name)); fcnt++)
+		flash_name[5] = '0' + fcnt + 1;
+
+	if (fcnt < 1)
+		return -ENXIO;
+
+	printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt);
+	msp_flash = (struct mtd_info **)kmalloc(
+			fcnt * sizeof(struct map_info *), GFP_KERNEL);
+	msp_parts = (struct mtd_partition **)kmalloc(
+			fcnt * sizeof(struct mtd_partition *), GFP_KERNEL);
+	msp_maps = (struct map_info *)kmalloc(
+			fcnt * sizeof(struct mtd_info), GFP_KERNEL);
+	memset(msp_maps, 0, fcnt * sizeof(struct mtd_info));
+
+	/* loop over the flash devices, initializing each */
+	for (i = 0; i < fcnt; i++) {
+		/* examine the prom environment for flash partititions */
+		part_name[5] = '0' + i;
+		part_name[7] = '0';
+		for (pcnt = 0; (env = prom_getenv(part_name)); pcnt++)
+			part_name[7] = '0' + pcnt + 1;
+
+		if (pcnt == 0) {
+			printk(KERN_NOTICE "Skipping flash device %d "
+				"(no partitions defined)\n", i);
+			continue;
+		}
+
+		msp_parts[i] = (struct mtd_partition *)kmalloc(
+			pcnt * sizeof(struct mtd_partition), GFP_KERNEL);
+		memset(msp_parts[i], 0, pcnt * sizeof(struct mtd_partition));
+
+		/* now initialize the devices proper */
+		flash_name[5] = '0' + i;
+		env = prom_getenv(flash_name);
+
+		if (sscanf(env, "%x:%x", &addr, &size) < 2)
+			return -ENXIO;
+		addr = CPHYSADDR(addr);
+
+		printk(KERN_NOTICE
+			"MSP flash device \"%s\": 0x%08x at 0x%08x\n",
+			flash_name, size, addr);
+		/* This must matchs the actual size of the flash chip */
+		msp_maps[i].size = size;
+		msp_maps[i].phys = addr;
+
+		/*
+		 * Platforms have a specific limit of the size of memory
+		 * which may be mapped for flash:
+		 */
+		if (size > CONFIG_MSP_FLASH_MAP_LIMIT)
+			size = CONFIG_MSP_FLASH_MAP_LIMIT;
+		msp_maps[i].virt = ioremap(addr, size);
+		msp_maps[i].bankwidth = 1;
+		msp_maps[i].name = strncpy(kmalloc(7, GFP_KERNEL),
+					flash_name, 7);
+
+		if (msp_maps[i].virt == NULL)
+			return -ENXIO;
+
+		for (j = 0; j < pcnt; j++) {
+			part_name[5] = '0' + i;
+			part_name[7] = '0' + j;
+
+			env = prom_getenv(part_name);
+
+			if (sscanf(env, "%x:%x:%n", &offset, &size, &coff) < 2)
+				return -ENXIO;
+
+			msp_parts[i][j].size = size;
+			msp_parts[i][j].offset = offset;
+			msp_parts[i][j].name = env + coff;
+		}
+
+		/* now probe and add the device */
+		simple_map_init(&msp_maps[i]);
+		msp_flash[i] = do_map_probe("cfi_probe", &msp_maps[i]);
+		if (msp_flash[i]) {
+			msp_flash[i]->owner = THIS_MODULE;
+			add_mtd_partitions(msp_flash[i], msp_parts[i], pcnt);
+		} else {
+			printk(KERN_ERR "map probe failed for flash\n");
+			return -ENXIO;
+		}
+	}
+
+	return 0;
+}
+
+static void __exit cleanup_msp_flash(void)
+{
+	int i;
+
+	for (i = 0; i < sizeof(msp_flash) / sizeof(struct mtd_info **); i++) {
+		del_mtd_partitions(msp_flash[i]);
+		map_destroy(msp_flash[i]);
+		iounmap((void *)msp_maps[i].virt);
+
+		/* free the memory */
+		kfree(msp_maps[i].name);
+		kfree(msp_parts[i]);
+	}
+
+	kfree(msp_flash);
+	kfree(msp_parts);
+	kfree(msp_maps);
+}
+
+MODULE_AUTHOR("PMC-Sierra, Inc");
+MODULE_DESCRIPTION("MTD map driver for PMC-Sierra MSP boards");
+MODULE_LICENSE("GPL");
+
+module_init(init_msp_flash);
+module_exit(cleanup_msp_flash);
diff --git a/drivers/mtd/maps/pmcmsp-ramroot.c b/drivers/mtd/maps/pmcmsp-ramroot.c
new file mode 100644
index 0000000..18049bc
--- /dev/null
+++ b/drivers/mtd/maps/pmcmsp-ramroot.c
@@ -0,0 +1,105 @@
+/*
+ * Mapping of the rootfs in a physical region of memory
+ *
+ * Copyright (C) 2005-2007 PMC-Sierra Inc.
+ * Author: Andrew Hughes, Andrew_Hughes@pmc-sierra.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  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
+ *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+ *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
+ *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  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/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/root_dev.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+
+#include <asm/io.h>
+
+#include <msp_prom.h>
+
+static struct mtd_info *rr_mtd;
+
+struct map_info rr_map = {
+	.name = "ramroot",
+	.bankwidth = 4,
+};
+
+static int __init init_rrmap(void)
+{
+	void *ramroot_start;
+	unsigned long ramroot_size;
+
+	/* Check for supported rootfs types */
+	if (get_ramroot(&ramroot_start, &ramroot_size)) {
+		rr_map.phys = CPHYSADDR(ramroot_start);
+		rr_map.size = ramroot_size;
+
+		printk(KERN_NOTICE
+			"PMC embedded root device: 0x%08lx @ 0x%08lx\n",
+			rr_map.size, (unsigned long)rr_map.phys);
+	} else {
+		printk(KERN_ERR
+			"init_rrmap: no supported embedded rootfs detected!\n");
+		return -ENXIO;
+	}
+
+	/* Map rootfs to I/O space for block device driver */
+	rr_map.virt = ioremap(rr_map.phys, rr_map.size);
+	if (!rr_map.virt) {
+		printk(KERN_ERR "Failed to ioremap\n");
+		return -EIO;
+	}
+
+	simple_map_init(&rr_map);
+
+	rr_mtd = do_map_probe("map_ram", &rr_map);
+	if (rr_mtd) {
+		rr_mtd->owner = THIS_MODULE;
+
+		add_mtd_device(rr_mtd);
+		ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, rr_mtd->index);
+
+		return 0;
+	}
+
+	iounmap(rr_map.virt);
+	return -ENXIO;
+}
+
+static void __exit cleanup_rrmap(void)
+{
+	del_mtd_device(rr_mtd);
+	map_destroy(rr_mtd);
+
+	iounmap(rr_map.virt);
+	rr_map.virt = NULL;
+}
+
+MODULE_AUTHOR("PMC-Sierra, Inc");
+MODULE_DESCRIPTION("MTD map driver for embedded PMC-Sierra MSP filesystem");
+MODULE_LICENSE("GPL");
+
+module_init(init_rrmap);
+module_exit(cleanup_rrmap);
diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c
index 4db2055..001af7f 100644
--- a/drivers/mtd/maps/sun_uflash.c
+++ b/drivers/mtd/maps/sun_uflash.c
@@ -39,7 +39,7 @@
 
 static LIST_HEAD(device_list);
 struct uflash_dev {
-	char			*name;	/* device name */
+	const char		*name;	/* device name */
 	struct map_info 	map;	/* mtd map info */
 	struct mtd_info		*mtd;	/* mtd info */
 };
@@ -80,7 +80,7 @@
 
 	up->name = of_get_property(dp, "model", NULL);
 	if (up->name && 0 < strlen(up->name))
-		up->map.name = up->name;
+		up->map.name = (char *)up->name;
 
 	up->map.phys = res->start;
 
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index b879a66..524b83b 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -20,6 +20,7 @@
 #include <linux/hdreg.h>
 #include <linux/init.h>
 #include <linux/mutex.h>
+#include <linux/kthread.h>
 #include <asm/uaccess.h>
 
 static LIST_HEAD(blktrans_majors);
@@ -28,9 +29,7 @@
 extern struct mtd_info *mtd_table[];
 
 struct mtd_blkcore_priv {
-	struct completion thread_dead;
-	int exiting;
-	wait_queue_head_t thread_wq;
+	struct task_struct *thread;
 	struct request_queue *rq;
 	spinlock_t queue_lock;
 };
@@ -83,38 +82,19 @@
 	/* we might get involved when memory gets low, so use PF_MEMALLOC */
 	current->flags |= PF_MEMALLOC | PF_NOFREEZE;
 
-	daemonize("%sd", tr->name);
-
-	/* daemonize() doesn't do this for us since some kernel threads
-	   actually want to deal with signals. We can't just call
-	   exit_sighand() since that'll cause an oops when we finally
-	   do exit. */
-	spin_lock_irq(&current->sighand->siglock);
-	sigfillset(&current->blocked);
-	recalc_sigpending();
-	spin_unlock_irq(&current->sighand->siglock);
-
 	spin_lock_irq(rq->queue_lock);
-
-	while (!tr->blkcore_priv->exiting) {
+	while (!kthread_should_stop()) {
 		struct request *req;
 		struct mtd_blktrans_dev *dev;
 		int res = 0;
-		DECLARE_WAITQUEUE(wait, current);
 
 		req = elv_next_request(rq);
 
 		if (!req) {
-			add_wait_queue(&tr->blkcore_priv->thread_wq, &wait);
 			set_current_state(TASK_INTERRUPTIBLE);
-
 			spin_unlock_irq(rq->queue_lock);
-
 			schedule();
-			remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait);
-
 			spin_lock_irq(rq->queue_lock);
-
 			continue;
 		}
 
@@ -133,13 +113,13 @@
 	}
 	spin_unlock_irq(rq->queue_lock);
 
-	complete_and_exit(&tr->blkcore_priv->thread_dead, 0);
+	return 0;
 }
 
 static void mtd_blktrans_request(struct request_queue *rq)
 {
 	struct mtd_blktrans_ops *tr = rq->queuedata;
-	wake_up(&tr->blkcore_priv->thread_wq);
+	wake_up_process(tr->blkcore_priv->thread);
 }
 
 
@@ -388,8 +368,6 @@
 		return ret;
 	}
 	spin_lock_init(&tr->blkcore_priv->queue_lock);
-	init_completion(&tr->blkcore_priv->thread_dead);
-	init_waitqueue_head(&tr->blkcore_priv->thread_wq);
 
 	tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
 	if (!tr->blkcore_priv->rq) {
@@ -403,13 +381,14 @@
 	blk_queue_hardsect_size(tr->blkcore_priv->rq, tr->blksize);
 	tr->blkshift = ffs(tr->blksize) - 1;
 
-	ret = kernel_thread(mtd_blktrans_thread, tr, CLONE_KERNEL);
-	if (ret < 0) {
+	tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
+			"%sd", tr->name);
+	if (IS_ERR(tr->blkcore_priv->thread)) {
 		blk_cleanup_queue(tr->blkcore_priv->rq);
 		unregister_blkdev(tr->major, tr->name);
 		kfree(tr->blkcore_priv);
 		mutex_unlock(&mtd_table_mutex);
-		return ret;
+		return PTR_ERR(tr->blkcore_priv->thread);
 	}
 
 	INIT_LIST_HEAD(&tr->devs);
@@ -432,9 +411,7 @@
 	mutex_lock(&mtd_table_mutex);
 
 	/* Clean up the kernel thread */
-	tr->blkcore_priv->exiting = 1;
-	wake_up(&tr->blkcore_priv->thread_wq);
-	wait_for_completion(&tr->blkcore_priv->thread_dead);
+	kthread_stop(tr->blkcore_priv->thread);
 
 	/* Remove it from the list of active majors */
 	list_del(&tr->list);
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 1592eac..8c86b80 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -553,7 +553,7 @@
 		ops.datbuf = NULL;
 		ops.mode = MTD_OOB_PLACE;
 
-		if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs))
+		if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
 			return -EINVAL;
 
 		ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 2d12dcd..d05873b 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -1,10 +1,7 @@
 # drivers/mtd/nand/Kconfig
 # $Id: Kconfig,v 1.35 2005/11/07 11:14:30 gleixner Exp $
 
-menu "NAND Flash Device Drivers"
-	depends on MTD!=n
-
-config MTD_NAND
+menuconfig MTD_NAND
 	tristate "NAND Device Support"
 	depends on MTD
 	select MTD_NAND_IDS
@@ -13,9 +10,10 @@
 	  devices. For further information see
 	  <http://www.linux-mtd.infradead.org/doc/nand.html>.
 
+if MTD_NAND
+
 config MTD_NAND_VERIFY_WRITE
 	bool "Verify NAND page writes"
-	depends on MTD_NAND
 	help
 	  This adds an extra check when data is written to the flash. The
 	  NAND flash device internally checks only bits transitioning
@@ -25,53 +23,61 @@
 
 config MTD_NAND_ECC_SMC
 	bool "NAND ECC Smart Media byte order"
-	depends on MTD_NAND
 	default n
 	help
 	  Software ECC according to the Smart Media Specification.
 	  The original Linux implementation had byte 0 and 1 swapped.
 
+config MTD_NAND_MUSEUM_IDS
+	bool "Enable chip ids for obsolete ancient NAND devices"
+	depends on MTD_NAND
+	default n
+	help
+	  Enable this option only when your board has first generation
+	  NAND chips (page size 256 byte, erase size 4-8KiB). The IDs
+	  of these chips were reused by later, larger chips.
+
 config MTD_NAND_AUTCPU12
 	tristate "SmartMediaCard on autronix autcpu12 board"
-	depends on MTD_NAND && ARCH_AUTCPU12
+	depends on ARCH_AUTCPU12
 	help
 	  This enables the driver for the autronix autcpu12 board to
 	  access the SmartMediaCard.
 
 config MTD_NAND_EDB7312
 	tristate "Support for Cirrus Logic EBD7312 evaluation board"
-	depends on MTD_NAND && ARCH_EDB7312
+	depends on ARCH_EDB7312
 	help
 	  This enables the driver for the Cirrus Logic EBD7312 evaluation
 	  board to access the onboard NAND Flash.
 
 config MTD_NAND_H1900
 	tristate "iPAQ H1900 flash"
-	depends on MTD_NAND && ARCH_PXA && MTD_PARTITIONS
+	depends on ARCH_PXA && MTD_PARTITIONS
 	help
 	  This enables the driver for the iPAQ h1900 flash.
 
 config MTD_NAND_SPIA
 	tristate "NAND Flash device on SPIA board"
-	depends on ARCH_P720T && MTD_NAND
+	depends on ARCH_P720T
 	help
 	  If you had to ask, you don't have one. Say 'N'.
 
 config MTD_NAND_AMS_DELTA
 	tristate "NAND Flash device on Amstrad E3"
-	depends on MACH_AMS_DELTA && MTD_NAND
+	depends on MACH_AMS_DELTA
 	help
 	  Support for NAND flash on Amstrad E3 (Delta).
 
 config MTD_NAND_TOTO
 	tristate "NAND Flash device on TOTO board"
-	depends on ARCH_OMAP && MTD_NAND && BROKEN
+	depends on ARCH_OMAP && BROKEN
 	help
 	  Support for NAND flash on Texas Instruments Toto platform.
 
 config MTD_NAND_TS7250
 	tristate "NAND Flash device on TS-7250 board"
-	depends on MACH_TS72XX && MTD_NAND
+	depends on MACH_TS72XX
 	help
 	  Support for NAND flash on Technologic Systems TS-7250 platform.
 
@@ -80,14 +86,14 @@
 
 config MTD_NAND_AU1550
 	tristate "Au1550/1200 NAND support"
-	depends on (SOC_AU1200 || SOC_AU1550) && MTD_NAND
+	depends on SOC_AU1200 || SOC_AU1550
 	help
 	  This enables the driver for the NAND flash controller on the
 	  AMD/Alchemy 1550 SOC.
 
 config MTD_NAND_RTC_FROM4
 	tristate "Renesas Flash ROM 4-slot interface board (FROM_BOARD4)"
-	depends on MTD_NAND && SH_SOLUTION_ENGINE
+	depends on SH_SOLUTION_ENGINE
 	select REED_SOLOMON
 	select REED_SOLOMON_DEC8
 	select BITREVERSE
@@ -97,13 +103,13 @@
 
 config MTD_NAND_PPCHAMELEONEVB
 	tristate "NAND Flash device on PPChameleonEVB board"
-	depends on PPCHAMELEONEVB && MTD_NAND && BROKEN
+	depends on PPCHAMELEONEVB && BROKEN
 	help
 	  This enables the NAND flash driver on the PPChameleon EVB Board.
 
 config MTD_NAND_S3C2410
 	tristate "NAND Flash support for S3C2410/S3C2440 SoC"
-	depends on ARCH_S3C2410 && MTD_NAND
+	depends on ARCH_S3C2410
 	help
 	  This enables the NAND flash controller on the S3C2410 and S3C2440
 	  SoCs
@@ -128,7 +134,7 @@
 
 config MTD_NAND_NDFC
 	tristate "NDFC NanD Flash Controller"
-	depends on MTD_NAND && 44x
+	depends on 44x
 	select MTD_NAND_ECC_SMC
 	help
 	 NDFC Nand Flash Controllers are integrated in EP44x SoCs
@@ -145,7 +151,7 @@
 
 config MTD_NAND_DISKONCHIP
 	tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)"
-	depends on MTD_NAND && EXPERIMENTAL
+	depends on EXPERIMENTAL
 	select REED_SOLOMON
 	select REED_SOLOMON_DEC16
 	help
@@ -215,11 +221,11 @@
 
 config MTD_NAND_SHARPSL
 	tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
-	depends on MTD_NAND && ARCH_PXA
+	depends on ARCH_PXA
 
 config MTD_NAND_BASLER_EXCITE
 	tristate  "Support for NAND Flash on Basler eXcite"
-	depends on MTD_NAND && BASLER_EXCITE
+	depends on BASLER_EXCITE
 	help
           This enables the driver for the NAND flash device found on the
           Basler eXcite Smart Camera. If built as a module, the driver
@@ -227,14 +233,14 @@
 
 config MTD_NAND_CAFE
        tristate "NAND support for OLPC CAFÉ chip"
-       depends on MTD_NAND && PCI
+       depends on PCI
        help
 	 Use NAND flash attached to the CAFÉ chip designed for the $100
 	 laptop.
 
 config MTD_NAND_CS553X
 	tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
-	depends on MTD_NAND && X86_32 && (X86_PC || X86_GENERICARCH)
+	depends on X86_32 && (X86_PC || X86_GENERICARCH)
 	help
 	  The CS553x companion chips for the AMD Geode processor
 	  include NAND flash controllers with built-in hardware ECC
@@ -247,16 +253,21 @@
 
 config MTD_NAND_AT91
 	bool "Support for NAND Flash / SmartMedia on AT91"
-	depends on MTD_NAND && ARCH_AT91
+	depends on ARCH_AT91
 	help
 	  Enables support for NAND Flash / Smart Media Card interface
 	  on Atmel AT91 processors.
 
+config MTD_NAND_CM_X270
+	tristate "Support for NAND Flash on CM-X270 modules"
+	depends on MTD_NAND && MACH_ARMCORE
+
+
 config MTD_NAND_NANDSIM
 	tristate "Support for NAND Flash Simulator"
-	depends on MTD_NAND && MTD_PARTITIONS
+	depends on MTD_PARTITIONS
 	help
 	  The simulator may simulate various NAND flash chips for the
 	  MTD nand layer.
 
-endmenu
+endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 80f1dfc..6872031 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -24,6 +24,7 @@
 obj-$(CONFIG_MTD_NAND_CS553X)		+= cs553x_nand.o
 obj-$(CONFIG_MTD_NAND_NDFC)		+= ndfc.o
 obj-$(CONFIG_MTD_NAND_AT91)		+= at91_nand.o
+obj-$(CONFIG_MTD_NAND_CM_X270)		+= cmx270_nand.o
 obj-$(CONFIG_MTD_NAND_BASLER_EXCITE)	+= excite_nandflash.o
 
 nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c
index fd6bb3e..c328a75 100644
--- a/drivers/mtd/nand/cafe.c
+++ b/drivers/mtd/nand/cafe.c
@@ -530,7 +530,6 @@
 {
 	struct mtd_info *mtd;
 	struct cafe_priv *cafe;
-	uint32_t timing1, timing2, timing3;
 	uint32_t ctrl;
 	int err = 0;
 
@@ -587,21 +586,19 @@
 	}
 
 	if (numtimings == 3) {
-		timing1 = timing[0];
-		timing2 = timing[1];
-		timing3 = timing[2];
 		cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n",
-			     timing1, timing2, timing3);
+			     timing[0], timing[1], timing[2]);
 	} else {
-		timing1 = cafe_readl(cafe, NAND_TIMING1);
-		timing2 = cafe_readl(cafe, NAND_TIMING2);
-		timing3 = cafe_readl(cafe, NAND_TIMING3);
+		timing[0] = cafe_readl(cafe, NAND_TIMING1);
+		timing[1] = cafe_readl(cafe, NAND_TIMING2);
+		timing[2] = cafe_readl(cafe, NAND_TIMING3);
 
-		if (timing1 | timing2 | timing3) {
-			cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", timing1, timing2, timing3);
+		if (timing[0] | timing[1] | timing[2]) {
+			cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n",
+				     timing[0], timing[1], timing[2]);
 		} else {
 			dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n");
-			timing1 = timing2 = timing3 = 0xffffffff;
+			timing[0] = timing[1] = timing[2] = 0xffffffff;
 		}
 	}
 
@@ -609,9 +606,9 @@
 	cafe_writel(cafe, 1, NAND_RESET);
 	cafe_writel(cafe, 0, NAND_RESET);
 
-	cafe_writel(cafe, timing1, NAND_TIMING1);
-	cafe_writel(cafe, timing2, NAND_TIMING2);
-	cafe_writel(cafe, timing3, NAND_TIMING3);
+	cafe_writel(cafe, timing[0], NAND_TIMING1);
+	cafe_writel(cafe, timing[1], NAND_TIMING2);
+	cafe_writel(cafe, timing[2], NAND_TIMING3);
 
 	cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK);
 	err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED,
diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c
new file mode 100644
index 0000000..cb663ef
--- /dev/null
+++ b/drivers/mtd/nand/cmx270_nand.c
@@ -0,0 +1,267 @@
+/*
+ *  linux/drivers/mtd/nand/cmx270-nand.c
+ *
+ *  Copyright (C) 2006 Compulab, Ltd.
+ *  Mike Rapoport <mike@compulab.co.il>
+ *
+ *  Derived from drivers/mtd/nand/h1910.c
+ *       Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
+ *       Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Overview:
+ *   This is a device driver for the NAND flash device found on the
+ *   CM-X270 board.
+ */
+
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+
+#define GPIO_NAND_CS	(11)
+#define GPIO_NAND_RB	(89)
+
+/* This macro needed to ensure in-order operation of GPIO and local
+ * bus. Without both asm command and dummy uncached read there're
+ * states when NAND access is broken. I've looked for such macro(s) in
+ * include/asm-arm but found nothing approptiate.
+ * dmac_clean_range is close, but is makes cache invalidation
+ * unnecessary here and it cannot be used in module
+ */
+#define DRAIN_WB() \
+	do { \
+		unsigned char dummy; \
+		asm volatile ("mcr p15, 0, r0, c7, c10, 4":::"r0"); \
+		dummy=*((unsigned char*)UNCACHED_ADDR); \
+	} while(0)
+
+/* MTD structure for CM-X270 board */
+static struct mtd_info *cmx270_nand_mtd;
+
+/* remaped IO address of the device */
+static void __iomem *cmx270_nand_io;
+
+/*
+ * Define static partitions for flash device
+ */
+static struct mtd_partition partition_info[] = {
+	[0] = {
+		.name	= "cmx270-0",
+		.offset	= 0,
+		.size	= MTDPART_SIZ_FULL
+	}
+};
+#define NUM_PARTITIONS (ARRAY_SIZE(partition_info))
+
+const char *part_probes[] = { "cmdlinepart", NULL };
+
+static u_char cmx270_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+
+	return (readl(this->IO_ADDR_R) >> 16);
+}
+
+static void cmx270_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+
+	for (i=0; i<len; i++)
+		writel((*buf++ << 16), this->IO_ADDR_W);
+}
+
+static void cmx270_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+
+	for (i=0; i<len; i++)
+		*buf++ = readl(this->IO_ADDR_R) >> 16;
+}
+
+static int cmx270_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+
+	for (i=0; i<len; i++)
+		if (buf[i] != (u_char)(readl(this->IO_ADDR_R) >> 16))
+			return -EFAULT;
+
+	return 0;
+}
+
+static inline void nand_cs_on(void)
+{
+	GPCR(GPIO_NAND_CS) = GPIO_bit(GPIO_NAND_CS);
+}
+
+static void nand_cs_off(void)
+{
+	DRAIN_WB();
+
+	GPSR(GPIO_NAND_CS) = GPIO_bit(GPIO_NAND_CS);
+}
+
+/*
+ *	hardware specific access to control-lines
+ */
+static void cmx270_hwcontrol(struct mtd_info *mtd, int dat,
+			     unsigned int ctrl)
+{
+	struct nand_chip* this = mtd->priv;
+	unsigned int nandaddr = (unsigned int)this->IO_ADDR_W;
+
+	DRAIN_WB();
+
+	if (ctrl & NAND_CTRL_CHANGE) {
+		if ( ctrl & NAND_ALE )
+			nandaddr |=  (1 << 3);
+		else
+			nandaddr &= ~(1 << 3);
+		if ( ctrl & NAND_CLE )
+			nandaddr |=  (1 << 2);
+		else
+			nandaddr &= ~(1 << 2);
+		if ( ctrl & NAND_NCE )
+			nand_cs_on();
+		else
+			nand_cs_off();
+	}
+
+	DRAIN_WB();
+	this->IO_ADDR_W = (void __iomem*)nandaddr;
+	if (dat != NAND_CMD_NONE)
+		writel((dat << 16), this->IO_ADDR_W);
+
+	DRAIN_WB();
+}
+
+/*
+ *	read device ready pin
+ */
+static int cmx270_device_ready(struct mtd_info *mtd)
+{
+	DRAIN_WB();
+
+	return (GPLR(GPIO_NAND_RB) & GPIO_bit(GPIO_NAND_RB));
+}
+
+/*
+ * Main initialization routine
+ */
+static int cmx270_init(void)
+{
+	struct nand_chip *this;
+	const char *part_type;
+	struct mtd_partition *mtd_parts;
+	int mtd_parts_nb = 0;
+	int ret;
+
+	/* Allocate memory for MTD device structure and private data */
+	cmx270_nand_mtd = kzalloc(sizeof(struct mtd_info) +
+				  sizeof(struct nand_chip),
+				  GFP_KERNEL);
+	if (!cmx270_nand_mtd) {
+		printk("Unable to allocate CM-X270 NAND MTD device structure.\n");
+		return -ENOMEM;
+	}
+
+	cmx270_nand_io = ioremap(PXA_CS1_PHYS, 12);
+	if (!cmx270_nand_io) {
+		printk("Unable to ioremap NAND device\n");
+		ret = -EINVAL;
+		goto err1;
+	}
+
+	/* Get pointer to private data */
+	this = (struct nand_chip *)(&cmx270_nand_mtd[1]);
+
+	/* Link the private data with the MTD structure */
+	cmx270_nand_mtd->owner = THIS_MODULE;
+	cmx270_nand_mtd->priv = this;
+
+	/* insert callbacks */
+	this->IO_ADDR_R = cmx270_nand_io;
+	this->IO_ADDR_W = cmx270_nand_io;
+	this->cmd_ctrl = cmx270_hwcontrol;
+	this->dev_ready = cmx270_device_ready;
+
+	/* 15 us command delay time */
+	this->chip_delay = 20;
+	this->ecc.mode = NAND_ECC_SOFT;
+
+	/* read/write functions */
+	this->read_byte = cmx270_read_byte;
+	this->read_buf = cmx270_read_buf;
+	this->write_buf = cmx270_write_buf;
+	this->verify_buf = cmx270_verify_buf;
+
+	/* Scan to find existence of the device */
+	if (nand_scan (cmx270_nand_mtd, 1)) {
+		printk(KERN_NOTICE "No NAND device\n");
+		ret = -ENXIO;
+		goto err2;
+	}
+
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+	mtd_parts_nb = parse_mtd_partitions(cmx270_nand_mtd, part_probes,
+					    &mtd_parts, 0);
+	if (mtd_parts_nb > 0)
+		part_type = "command line";
+	else
+		mtd_parts_nb = 0;
+#endif
+	if (!mtd_parts_nb) {
+		mtd_parts = partition_info;
+		mtd_parts_nb = NUM_PARTITIONS;
+		part_type = "static";
+	}
+
+	/* Register the partitions */
+	printk(KERN_NOTICE "Using %s partition definition\n", part_type);
+	ret = add_mtd_partitions(cmx270_nand_mtd, mtd_parts, mtd_parts_nb);
+	if (ret)
+		goto err2;
+
+	/* Return happy */
+	return 0;
+
+err2:
+	iounmap(cmx270_nand_io);
+err1:
+	kfree(cmx270_nand_mtd);
+
+	return ret;
+
+}
+module_init(cmx270_init);
+
+/*
+ * Clean up routine
+ */
+static void cmx270_cleanup(void)
+{
+	/* Release resources, unregister device */
+	nand_release(cmx270_nand_mtd);
+
+	iounmap(cmx270_nand_io);
+
+	/* Free the MTD device structure */
+	kfree (cmx270_nand_mtd);
+}
+module_exit(cmx270_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
+MODULE_DESCRIPTION("NAND flash driver for Compulab CM-X270 Module");
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 6af37b8..04de315 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -312,7 +312,7 @@
 		/* Select the NAND device */
 		chip->select_chip(mtd, chipnr);
 	} else
-		page = (int)ofs;
+		page = (int)(ofs >> chip->page_shift);
 
 	if (chip->options & NAND_BUSWIDTH_16) {
 		chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE,
@@ -350,7 +350,7 @@
 	int block, ret;
 
 	/* Get block number */
-	block = ((int)ofs) >> chip->bbt_erase_shift;
+	block = (int)(ofs >> chip->bbt_erase_shift);
 	if (chip->bbt)
 		chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
 
@@ -771,7 +771,7 @@
 	uint8_t *ecc_code = chip->buffers->ecccode;
 	int *eccpos = chip->ecc.layout->eccpos;
 
-	nand_read_page_raw(mtd, chip, buf);
+	chip->ecc.read_page_raw(mtd, chip, buf);
 
 	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
 		chip->ecc.calculate(mtd, p, &ecc_calc[i]);
@@ -1426,7 +1426,7 @@
 	for (i = 0; i < chip->ecc.total; i++)
 		chip->oob_poi[eccpos[i]] = ecc_calc[i];
 
-	nand_write_page_raw(mtd, chip, buf);
+	chip->ecc.write_page_raw(mtd, chip, buf);
 }
 
 /**
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index 2e2cdf2..2fc674a 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -24,6 +24,8 @@
 *	512	512 Byte page size
 */
 struct nand_flash_dev nand_flash_ids[] = {
+
+#ifdef CONFIG_MTD_NAND_MUSEUM_IDS
 	{"NAND 1MiB 5V 8-bit",		0x6e, 256, 1, 0x1000, 0},
 	{"NAND 2MiB 5V 8-bit",		0x64, 256, 2, 0x1000, 0},
 	{"NAND 4MiB 5V 8-bit",		0x6b, 512, 4, 0x2000, 0},
@@ -39,6 +41,7 @@
 	{"NAND 8MiB 3,3V 8-bit",	0xe6, 512, 8, 0x2000, 0},
 	{"NAND 8MiB 1,8V 16-bit",	0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16},
 	{"NAND 8MiB 3,3V 16-bit",	0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16},
+#endif
 
 	{"NAND 16MiB 1,8V 8-bit",	0x33, 512, 16, 0x4000, 0},
 	{"NAND 16MiB 3,3V 8-bit",	0x73, 512, 16, 0x4000, 0},
@@ -137,6 +140,7 @@
 	{NAND_MFR_RENESAS, "Renesas"},
 	{NAND_MFR_STMICRO, "ST Micro"},
 	{NAND_MFR_HYNIX, "Hynix"},
+	{NAND_MFR_MICRON, "Micron"},
 	{0x0, "Unknown"}
 };
 
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
index c3bca95..205df0f 100644
--- a/drivers/mtd/nand/nandsim.c
+++ b/drivers/mtd/nand/nandsim.c
@@ -37,6 +37,8 @@
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
 #include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/random.h>
 
 /* Default simulator parameters values */
 #if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE)  || \
@@ -90,6 +92,15 @@
 static uint do_delays      = CONFIG_NANDSIM_DO_DELAYS;
 static uint log            = CONFIG_NANDSIM_LOG;
 static uint dbg            = CONFIG_NANDSIM_DBG;
+static unsigned long parts[MAX_MTD_DEVICES];
+static unsigned int parts_num;
+static char *badblocks = NULL;
+static char *weakblocks = NULL;
+static char *weakpages = NULL;
+static unsigned int bitflips = 0;
+static char *gravepages = NULL;
+static unsigned int rptwear = 0;
+static unsigned int overridesize = 0;
 
 module_param(first_id_byte,  uint, 0400);
 module_param(second_id_byte, uint, 0400);
@@ -104,8 +115,16 @@
 module_param(do_delays,      uint, 0400);
 module_param(log,            uint, 0400);
 module_param(dbg,            uint, 0400);
+module_param_array(parts, ulong, &parts_num, 0400);
+module_param(badblocks,      charp, 0400);
+module_param(weakblocks,     charp, 0400);
+module_param(weakpages,      charp, 0400);
+module_param(bitflips,       uint, 0400);
+module_param(gravepages,     charp, 0400);
+module_param(rptwear,        uint, 0400);
+module_param(overridesize,   uint, 0400);
 
-MODULE_PARM_DESC(first_id_byte,  "The fist byte returned by NAND Flash 'read ID' command (manufaturer ID)");
+MODULE_PARM_DESC(first_id_byte,  "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)");
 MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)");
 MODULE_PARM_DESC(third_id_byte,  "The third byte returned by NAND Flash 'read ID' command");
 MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command");
@@ -118,6 +137,23 @@
 MODULE_PARM_DESC(do_delays,      "Simulate NAND delays using busy-waits if not zero");
 MODULE_PARM_DESC(log,            "Perform logging if not zero");
 MODULE_PARM_DESC(dbg,            "Output debug information if not zero");
+MODULE_PARM_DESC(parts,          "Partition sizes (in erase blocks) separated by commas");
+/* Page and erase block positions for the following parameters are independent of any partitions */
+MODULE_PARM_DESC(badblocks,      "Erase blocks that are initially marked bad, separated by commas");
+MODULE_PARM_DESC(weakblocks,     "Weak erase blocks [: remaining erase cycles (defaults to 3)]"
+				 " separated by commas e.g. 113:2 means eb 113"
+				 " can be erased only twice before failing");
+MODULE_PARM_DESC(weakpages,      "Weak pages [: maximum writes (defaults to 3)]"
+				 " separated by commas e.g. 1401:2 means page 1401"
+				 " can be written only twice before failing");
+MODULE_PARM_DESC(bitflips,       "Maximum number of random bit flips per page (zero by default)");
+MODULE_PARM_DESC(gravepages,     "Pages that lose data [: maximum reads (defaults to 3)]"
+				 " separated by commas e.g. 1401:2 means page 1401"
+				 " can be read only twice before failing");
+MODULE_PARM_DESC(rptwear,        "Number of erases inbetween reporting wear, if not zero");
+MODULE_PARM_DESC(overridesize,   "Specifies the NAND Flash size overriding the ID bytes. "
+				 "The size is specified in erase blocks and as the exponent of a power of two"
+				 " e.g. 5 means a size of 32 erase blocks");
 
 /* The largest possible page size */
 #define NS_LARGEST_PAGE_SIZE	2048
@@ -131,9 +167,11 @@
 #define NS_DBG(args...) \
 	do { if (dbg) printk(KERN_DEBUG NS_OUTPUT_PREFIX " debug: " args); } while(0)
 #define NS_WARN(args...) \
-	do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warnig: " args); } while(0)
+	do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warning: " args); } while(0)
 #define NS_ERR(args...) \
-	do { printk(KERN_ERR NS_OUTPUT_PREFIX " errorr: " args); } while(0)
+	do { printk(KERN_ERR NS_OUTPUT_PREFIX " error: " args); } while(0)
+#define NS_INFO(args...) \
+	do { printk(KERN_INFO NS_OUTPUT_PREFIX " " args); } while(0)
 
 /* Busy-wait delay macros (microseconds, milliseconds) */
 #define NS_UDELAY(us) \
@@ -238,7 +276,8 @@
  * The structure which describes all the internal simulator data.
  */
 struct nandsim {
-	struct mtd_partition part;
+	struct mtd_partition partitions[MAX_MTD_DEVICES];
+	unsigned int nbparts;
 
 	uint busw;              /* flash chip bus width (8 or 16) */
 	u_char ids[4];          /* chip's ID bytes */
@@ -338,6 +377,38 @@
 			       STATE_DATAOUT, STATE_READY}}
 };
 
+struct weak_block {
+	struct list_head list;
+	unsigned int erase_block_no;
+	unsigned int max_erases;
+	unsigned int erases_done;
+};
+
+static LIST_HEAD(weak_blocks);
+
+struct weak_page {
+	struct list_head list;
+	unsigned int page_no;
+	unsigned int max_writes;
+	unsigned int writes_done;
+};
+
+static LIST_HEAD(weak_pages);
+
+struct grave_page {
+	struct list_head list;
+	unsigned int page_no;
+	unsigned int max_reads;
+	unsigned int reads_done;
+};
+
+static LIST_HEAD(grave_pages);
+
+static unsigned long *erase_block_wear = NULL;
+static unsigned int wear_eb_count = 0;
+static unsigned long total_wear = 0;
+static unsigned int rptwear_cnt = 0;
+
 /* MTD structure for NAND controller */
 static struct mtd_info *nsmtd;
 
@@ -381,6 +452,13 @@
 	}
 }
 
+static char *get_partition_name(int i)
+{
+	char buf[64];
+	sprintf(buf, "NAND simulator partition %d", i);
+	return kstrdup(buf, GFP_KERNEL);
+}
+
 /*
  * Initialize the nandsim structure.
  *
@@ -390,7 +468,9 @@
 {
 	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
 	struct nandsim   *ns   = (struct nandsim *)(chip->priv);
-	int i;
+	int i, ret = 0;
+	u_int32_t remains;
+	u_int32_t next_offset;
 
 	if (NS_IS_INITIALIZED(ns)) {
 		NS_ERR("init_nandsim: nandsim is already initialized\n");
@@ -448,6 +528,40 @@
 		}
 	}
 
+	/* Fill the partition_info structure */
+	if (parts_num > ARRAY_SIZE(ns->partitions)) {
+		NS_ERR("too many partitions.\n");
+		ret = -EINVAL;
+		goto error;
+	}
+	remains = ns->geom.totsz;
+	next_offset = 0;
+	for (i = 0; i < parts_num; ++i) {
+		unsigned long part = parts[i];
+		if (!part || part > remains / ns->geom.secsz) {
+			NS_ERR("bad partition size.\n");
+			ret = -EINVAL;
+			goto error;
+		}
+		ns->partitions[i].name   = get_partition_name(i);
+		ns->partitions[i].offset = next_offset;
+		ns->partitions[i].size   = part * ns->geom.secsz;
+		next_offset += ns->partitions[i].size;
+		remains -= ns->partitions[i].size;
+	}
+	ns->nbparts = parts_num;
+	if (remains) {
+		if (parts_num + 1 > ARRAY_SIZE(ns->partitions)) {
+			NS_ERR("too many partitions.\n");
+			ret = -EINVAL;
+			goto error;
+		}
+		ns->partitions[i].name   = get_partition_name(i);
+		ns->partitions[i].offset = next_offset;
+		ns->partitions[i].size   = remains;
+		ns->nbparts += 1;
+	}
+
 	/* Detect how many ID bytes the NAND chip outputs */
         for (i = 0; nand_flash_ids[i].name != NULL; i++) {
                 if (second_id_byte != nand_flash_ids[i].id)
@@ -474,7 +588,7 @@
 	printk("sector address bytes: %u\n",    ns->geom.secaddrbytes);
 	printk("options: %#x\n",                ns->options);
 
-	if (alloc_device(ns) != 0)
+	if ((ret = alloc_device(ns)) != 0)
 		goto error;
 
 	/* Allocate / initialize the internal buffer */
@@ -482,21 +596,17 @@
 	if (!ns->buf.byte) {
 		NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n",
 			ns->geom.pgszoob);
+		ret = -ENOMEM;
 		goto error;
 	}
 	memset(ns->buf.byte, 0xFF, ns->geom.pgszoob);
 
-	/* Fill the partition_info structure */
-	ns->part.name   = "NAND simulator partition";
-	ns->part.offset = 0;
-	ns->part.size   = ns->geom.totsz;
-
 	return 0;
 
 error:
 	free_device(ns);
 
-	return -ENOMEM;
+	return ret;
 }
 
 /*
@@ -510,6 +620,287 @@
 	return;
 }
 
+static int parse_badblocks(struct nandsim *ns, struct mtd_info *mtd)
+{
+	char *w;
+	int zero_ok;
+	unsigned int erase_block_no;
+	loff_t offset;
+
+	if (!badblocks)
+		return 0;
+	w = badblocks;
+	do {
+		zero_ok = (*w == '0' ? 1 : 0);
+		erase_block_no = simple_strtoul(w, &w, 0);
+		if (!zero_ok && !erase_block_no) {
+			NS_ERR("invalid badblocks.\n");
+			return -EINVAL;
+		}
+		offset = erase_block_no * ns->geom.secsz;
+		if (mtd->block_markbad(mtd, offset)) {
+			NS_ERR("invalid badblocks.\n");
+			return -EINVAL;
+		}
+		if (*w == ',')
+			w += 1;
+	} while (*w);
+	return 0;
+}
+
+static int parse_weakblocks(void)
+{
+	char *w;
+	int zero_ok;
+	unsigned int erase_block_no;
+	unsigned int max_erases;
+	struct weak_block *wb;
+
+	if (!weakblocks)
+		return 0;
+	w = weakblocks;
+	do {
+		zero_ok = (*w == '0' ? 1 : 0);
+		erase_block_no = simple_strtoul(w, &w, 0);
+		if (!zero_ok && !erase_block_no) {
+			NS_ERR("invalid weakblocks.\n");
+			return -EINVAL;
+		}
+		max_erases = 3;
+		if (*w == ':') {
+			w += 1;
+			max_erases = simple_strtoul(w, &w, 0);
+		}
+		if (*w == ',')
+			w += 1;
+		wb = kzalloc(sizeof(*wb), GFP_KERNEL);
+		if (!wb) {
+			NS_ERR("unable to allocate memory.\n");
+			return -ENOMEM;
+		}
+		wb->erase_block_no = erase_block_no;
+		wb->max_erases = max_erases;
+		list_add(&wb->list, &weak_blocks);
+	} while (*w);
+	return 0;
+}
+
+static int erase_error(unsigned int erase_block_no)
+{
+	struct weak_block *wb;
+
+	list_for_each_entry(wb, &weak_blocks, list)
+		if (wb->erase_block_no == erase_block_no) {
+			if (wb->erases_done >= wb->max_erases)
+				return 1;
+			wb->erases_done += 1;
+			return 0;
+		}
+	return 0;
+}
+
+static int parse_weakpages(void)
+{
+	char *w;
+	int zero_ok;
+	unsigned int page_no;
+	unsigned int max_writes;
+	struct weak_page *wp;
+
+	if (!weakpages)
+		return 0;
+	w = weakpages;
+	do {
+		zero_ok = (*w == '0' ? 1 : 0);
+		page_no = simple_strtoul(w, &w, 0);
+		if (!zero_ok && !page_no) {
+			NS_ERR("invalid weakpagess.\n");
+			return -EINVAL;
+		}
+		max_writes = 3;
+		if (*w == ':') {
+			w += 1;
+			max_writes = simple_strtoul(w, &w, 0);
+		}
+		if (*w == ',')
+			w += 1;
+		wp = kzalloc(sizeof(*wp), GFP_KERNEL);
+		if (!wp) {
+			NS_ERR("unable to allocate memory.\n");
+			return -ENOMEM;
+		}
+		wp->page_no = page_no;
+		wp->max_writes = max_writes;
+		list_add(&wp->list, &weak_pages);
+	} while (*w);
+	return 0;
+}
+
+static int write_error(unsigned int page_no)
+{
+	struct weak_page *wp;
+
+	list_for_each_entry(wp, &weak_pages, list)
+		if (wp->page_no == page_no) {
+			if (wp->writes_done >= wp->max_writes)
+				return 1;
+			wp->writes_done += 1;
+			return 0;
+		}
+	return 0;
+}
+
+static int parse_gravepages(void)
+{
+	char *g;
+	int zero_ok;
+	unsigned int page_no;
+	unsigned int max_reads;
+	struct grave_page *gp;
+
+	if (!gravepages)
+		return 0;
+	g = gravepages;
+	do {
+		zero_ok = (*g == '0' ? 1 : 0);
+		page_no = simple_strtoul(g, &g, 0);
+		if (!zero_ok && !page_no) {
+			NS_ERR("invalid gravepagess.\n");
+			return -EINVAL;
+		}
+		max_reads = 3;
+		if (*g == ':') {
+			g += 1;
+			max_reads = simple_strtoul(g, &g, 0);
+		}
+		if (*g == ',')
+			g += 1;
+		gp = kzalloc(sizeof(*gp), GFP_KERNEL);
+		if (!gp) {
+			NS_ERR("unable to allocate memory.\n");
+			return -ENOMEM;
+		}
+		gp->page_no = page_no;
+		gp->max_reads = max_reads;
+		list_add(&gp->list, &grave_pages);
+	} while (*g);
+	return 0;
+}
+
+static int read_error(unsigned int page_no)
+{
+	struct grave_page *gp;
+
+	list_for_each_entry(gp, &grave_pages, list)
+		if (gp->page_no == page_no) {
+			if (gp->reads_done >= gp->max_reads)
+				return 1;
+			gp->reads_done += 1;
+			return 0;
+		}
+	return 0;
+}
+
+static void free_lists(void)
+{
+	struct list_head *pos, *n;
+	list_for_each_safe(pos, n, &weak_blocks) {
+		list_del(pos);
+		kfree(list_entry(pos, struct weak_block, list));
+	}
+	list_for_each_safe(pos, n, &weak_pages) {
+		list_del(pos);
+		kfree(list_entry(pos, struct weak_page, list));
+	}
+	list_for_each_safe(pos, n, &grave_pages) {
+		list_del(pos);
+		kfree(list_entry(pos, struct grave_page, list));
+	}
+	kfree(erase_block_wear);
+}
+
+static int setup_wear_reporting(struct mtd_info *mtd)
+{
+	size_t mem;
+
+	if (!rptwear)
+		return 0;
+	wear_eb_count = mtd->size / mtd->erasesize;
+	mem = wear_eb_count * sizeof(unsigned long);
+	if (mem / sizeof(unsigned long) != wear_eb_count) {
+		NS_ERR("Too many erase blocks for wear reporting\n");
+		return -ENOMEM;
+	}
+	erase_block_wear = kzalloc(mem, GFP_KERNEL);
+	if (!erase_block_wear) {
+		NS_ERR("Too many erase blocks for wear reporting\n");
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static void update_wear(unsigned int erase_block_no)
+{
+	unsigned long wmin = -1, wmax = 0, avg;
+	unsigned long deciles[10], decile_max[10], tot = 0;
+	unsigned int i;
+
+	if (!erase_block_wear)
+		return;
+	total_wear += 1;
+	if (total_wear == 0)
+		NS_ERR("Erase counter total overflow\n");
+	erase_block_wear[erase_block_no] += 1;
+	if (erase_block_wear[erase_block_no] == 0)
+		NS_ERR("Erase counter overflow for erase block %u\n", erase_block_no);
+	rptwear_cnt += 1;
+	if (rptwear_cnt < rptwear)
+		return;
+	rptwear_cnt = 0;
+	/* Calc wear stats */
+	for (i = 0; i < wear_eb_count; ++i) {
+		unsigned long wear = erase_block_wear[i];
+		if (wear < wmin)
+			wmin = wear;
+		if (wear > wmax)
+			wmax = wear;
+		tot += wear;
+	}
+	for (i = 0; i < 9; ++i) {
+		deciles[i] = 0;
+		decile_max[i] = (wmax * (i + 1) + 5) / 10;
+	}
+	deciles[9] = 0;
+	decile_max[9] = wmax;
+	for (i = 0; i < wear_eb_count; ++i) {
+		int d;
+		unsigned long wear = erase_block_wear[i];
+		for (d = 0; d < 10; ++d)
+			if (wear <= decile_max[d]) {
+				deciles[d] += 1;
+				break;
+			}
+	}
+	avg = tot / wear_eb_count;
+	/* Output wear report */
+	NS_INFO("*** Wear Report ***\n");
+	NS_INFO("Total numbers of erases:  %lu\n", tot);
+	NS_INFO("Number of erase blocks:   %u\n", wear_eb_count);
+	NS_INFO("Average number of erases: %lu\n", avg);
+	NS_INFO("Maximum number of erases: %lu\n", wmax);
+	NS_INFO("Minimum number of erases: %lu\n", wmin);
+	for (i = 0; i < 10; ++i) {
+		unsigned long from = (i ? decile_max[i - 1] + 1 : 0);
+		if (from > decile_max[i])
+			continue;
+		NS_INFO("Number of ebs with erase counts from %lu to %lu : %lu\n",
+			from,
+			decile_max[i],
+			deciles[i]);
+	}
+	NS_INFO("*** End of Wear Report ***\n");
+}
+
 /*
  * Returns the string representation of 'state' state.
  */
@@ -822,9 +1213,31 @@
 		NS_DBG("read_page: page %d not allocated\n", ns->regs.row);
 		memset(ns->buf.byte, 0xFF, num);
 	} else {
+		unsigned int page_no = ns->regs.row;
 		NS_DBG("read_page: page %d allocated, reading from %d\n",
 			ns->regs.row, ns->regs.column + ns->regs.off);
+		if (read_error(page_no)) {
+			int i;
+			memset(ns->buf.byte, 0xFF, num);
+			for (i = 0; i < num; ++i)
+				ns->buf.byte[i] = random32();
+			NS_WARN("simulating read error in page %u\n", page_no);
+			return;
+		}
 		memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num);
+		if (bitflips && random32() < (1 << 22)) {
+			int flips = 1;
+			if (bitflips > 1)
+				flips = (random32() % (int) bitflips) + 1;
+			while (flips--) {
+				int pos = random32() % (num * 8);
+				ns->buf.byte[pos / 8] ^= (1 << (pos % 8));
+				NS_WARN("read_page: flipping bit %d in page %d "
+					"reading from %d ecc: corrected=%u failed=%u\n",
+					pos, ns->regs.row, ns->regs.column + ns->regs.off,
+					nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed);
+			}
+		}
 	}
 }
 
@@ -883,6 +1296,7 @@
 {
 	int num;
 	int busdiv = ns->busw == 8 ? 1 : 2;
+	unsigned int erase_block_no, page_no;
 
 	action &= ACTION_MASK;
 
@@ -942,14 +1356,24 @@
 				8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column;
 		ns->regs.column = 0;
 
+		erase_block_no = ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift);
+
 		NS_DBG("do_state_action: erase sector at address %#x, off = %d\n",
 				ns->regs.row, NS_RAW_OFFSET(ns));
-		NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift));
+		NS_LOG("erase sector %u\n", erase_block_no);
 
 		erase_sector(ns);
 
 		NS_MDELAY(erase_delay);
 
+		if (erase_block_wear)
+			update_wear(erase_block_no);
+
+		if (erase_error(erase_block_no)) {
+			NS_WARN("simulating erase failure in erase block %u\n", erase_block_no);
+			return -1;
+		}
+
 		break;
 
 	case ACTION_PRGPAGE:
@@ -972,6 +1396,8 @@
 		if (prog_page(ns, num) == -1)
 			return -1;
 
+		page_no = ns->regs.row;
+
 		NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n",
 			num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off);
 		NS_LOG("programm page %d\n", ns->regs.row);
@@ -979,6 +1405,11 @@
 		NS_UDELAY(programm_delay);
 		NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv);
 
+		if (write_error(page_no)) {
+			NS_WARN("simulating write failure in page %u\n", page_no);
+			return -1;
+		}
+
 		break;
 
 	case ACTION_ZEROOFF:
@@ -1503,7 +1934,7 @@
 {
 	struct nand_chip *chip;
 	struct nandsim *nand;
-	int retval = -ENOMEM;
+	int retval = -ENOMEM, i;
 
 	if (bus_width != 8 && bus_width != 16) {
 		NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width);
@@ -1533,6 +1964,8 @@
 	chip->verify_buf = ns_nand_verify_buf;
 	chip->read_word  = ns_nand_read_word;
 	chip->ecc.mode   = NAND_ECC_SOFT;
+	/* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */
+	/* and 'badblocks' parameters to work */
 	chip->options   |= NAND_SKIP_BBTSCAN;
 
 	/*
@@ -1557,6 +1990,15 @@
 
 	nsmtd->owner = THIS_MODULE;
 
+	if ((retval = parse_weakblocks()) != 0)
+		goto error;
+
+	if ((retval = parse_weakpages()) != 0)
+		goto error;
+
+	if ((retval = parse_gravepages()) != 0)
+		goto error;
+
 	if ((retval = nand_scan(nsmtd, 1)) != 0) {
 		NS_ERR("can't register NAND Simulator\n");
 		if (retval > 0)
@@ -1564,23 +2006,44 @@
 		goto error;
 	}
 
-	if ((retval = init_nandsim(nsmtd)) != 0) {
-		NS_ERR("scan_bbt: can't initialize the nandsim structure\n");
-		goto error;
+	if (overridesize) {
+		u_int32_t new_size = nsmtd->erasesize << overridesize;
+		if (new_size >> overridesize != nsmtd->erasesize) {
+			NS_ERR("overridesize is too big\n");
+			goto err_exit;
+		}
+		/* N.B. This relies on nand_scan not doing anything with the size before we change it */
+		nsmtd->size = new_size;
+		chip->chipsize = new_size;
+		chip->chip_shift = ffs(new_size) - 1;
 	}
 
-	if ((retval = nand_default_bbt(nsmtd)) != 0) {
-		free_nandsim(nand);
-		goto error;
-	}
+	if ((retval = setup_wear_reporting(nsmtd)) != 0)
+		goto err_exit;
 
-	/* Register NAND as one big partition */
-	add_mtd_partitions(nsmtd, &nand->part, 1);
+	if ((retval = init_nandsim(nsmtd)) != 0)
+		goto err_exit;
+
+	if ((retval = parse_badblocks(nand, nsmtd)) != 0)
+		goto err_exit;
+
+	if ((retval = nand_default_bbt(nsmtd)) != 0)
+		goto err_exit;
+
+	/* Register NAND partitions */
+	if ((retval = add_mtd_partitions(nsmtd, &nand->partitions[0], nand->nbparts)) != 0)
+		goto err_exit;
 
         return 0;
 
+err_exit:
+	free_nandsim(nand);
+	nand_release(nsmtd);
+	for (i = 0;i < ARRAY_SIZE(nand->partitions); ++i)
+		kfree(nand->partitions[i].name);
 error:
 	kfree(nsmtd);
+	free_lists();
 
 	return retval;
 }
@@ -1593,10 +2056,14 @@
 static void __exit ns_cleanup_module(void)
 {
 	struct nandsim *ns = (struct nandsim *)(((struct nand_chip *)nsmtd->priv)->priv);
+	int i;
 
 	free_nandsim(ns);    /* Free nandsim private resources */
-	nand_release(nsmtd); /* Unregisterd drived */
+	nand_release(nsmtd); /* Unregister driver */
+	for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i)
+		kfree(ns->partitions[i].name);
 	kfree(nsmtd);        /* Free other structures */
+	free_lists();
 }
 
 module_exit(ns_cleanup_module);
@@ -1604,4 +2071,3 @@
 MODULE_LICENSE ("GPL");
 MODULE_AUTHOR ("Artem B. Bityuckiy");
 MODULE_DESCRIPTION ("The NAND flash simulator");
-
diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig
index 373bddc..c257d39 100644
--- a/drivers/mtd/onenand/Kconfig
+++ b/drivers/mtd/onenand/Kconfig
@@ -2,20 +2,18 @@
 # linux/drivers/mtd/onenand/Kconfig
 #
 
-menu "OneNAND Flash Device Drivers"
-	depends on MTD != n
-
-config MTD_ONENAND
+menuconfig MTD_ONENAND
 	tristate "OneNAND Device Support"
 	depends on MTD
 	help
 	  This enables support for accessing all type of OneNAND flash
 	  devices. For further information see
-	  <http://www.samsung.com/Products/Semiconductor/Flash/OneNAND_TM/index.htm>.
+	  <http://www.samsung.com/Products/Semiconductor/OneNAND/index.htm>
+
+if MTD_ONENAND
 
 config MTD_ONENAND_VERIFY_WRITE
 	bool "Verify OneNAND page writes"
-	depends on MTD_ONENAND
 	help
 	  This adds an extra check when data is written to the flash. The
 	  OneNAND flash device internally checks only bits transitioning
@@ -25,13 +23,12 @@
 
 config MTD_ONENAND_GENERIC
 	tristate "OneNAND Flash device via platform device driver"
-	depends on MTD_ONENAND && ARM
+	depends on ARM
 	help
 	  Support for OneNAND flash via platform device driver.
 
 config MTD_ONENAND_OTP
 	bool "OneNAND OTP Support"
-	depends on MTD_ONENAND
 	help
 	  One Block of the NAND Flash Array memory is reserved as
 	  a One-Time Programmable Block memory area.
@@ -43,4 +40,4 @@
 
 	  OTP block is fully-guaranteed to be a valid block.
 
-endmenu
+endif # MTD_ONENAND
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 9e14a26..000794c 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -836,9 +836,11 @@
 	int readcol = column;
 	int readend = column + thislen;
 	int lastgap = 0;
+	unsigned int i;
 	uint8_t *oob_buf = this->oob_buf;
 
-	for (free = this->ecclayout->oobfree; free->length; ++free) {
+	free = this->ecclayout->oobfree;
+	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
 		if (readcol >= lastgap)
 			readcol += free->offset - lastgap;
 		if (readend >= lastgap)
@@ -846,7 +848,8 @@
 		lastgap = free->offset + free->length;
 	}
 	this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
-	for (free = this->ecclayout->oobfree; free->length; ++free) {
+	free = this->ecclayout->oobfree;
+	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
 		int free_end = free->offset + free->length;
 		if (free->offset < readend && free_end > readcol) {
 			int st = max_t(int,free->offset,readcol);
@@ -854,7 +857,7 @@
 			int n = ed - st;
 			memcpy(buf, oob_buf + st, n);
 			buf += n;
-		} else
+		} else if (column == 0)
 			break;
 	}
 	return 0;
@@ -1280,15 +1283,18 @@
 	int writecol = column;
 	int writeend = column + thislen;
 	int lastgap = 0;
+	unsigned int i;
 
-	for (free = this->ecclayout->oobfree; free->length; ++free) {
+	free = this->ecclayout->oobfree;
+	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
 		if (writecol >= lastgap)
 			writecol += free->offset - lastgap;
 		if (writeend >= lastgap)
 			writeend += free->offset - lastgap;
 		lastgap = free->offset + free->length;
 	}
-	for (free = this->ecclayout->oobfree; free->length; ++free) {
+	free = this->ecclayout->oobfree;
+	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
 		int free_end = free->offset + free->length;
 		if (free->offset < writeend && free_end > writecol) {
 			int st = max_t(int,free->offset,writecol);
@@ -1296,7 +1302,7 @@
 			int n = ed - st;
 			memcpy(oob_buf + st, buf, n);
 			buf += n;
-		} else
+		} else if (column == 0)
 			break;
 	}
 	return 0;
@@ -2386,7 +2392,8 @@
 	 * the out of band area
 	 */
 	this->ecclayout->oobavail = 0;
-	for (i = 0; this->ecclayout->oobfree[i].length; i++)
+	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES &&
+	    this->ecclayout->oobfree[i].length; i++)
 		this->ecclayout->oobavail +=
 			this->ecclayout->oobfree[i].length;
 	mtd->oobavail = this->ecclayout->oobavail;
diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
new file mode 100644
index 0000000..b9daf15
--- /dev/null
+++ b/drivers/mtd/ubi/Kconfig
@@ -0,0 +1,58 @@
+# drivers/mtd/ubi/Kconfig
+
+menu "UBI - Unsorted block images"
+	depends on MTD
+
+config MTD_UBI
+	tristate "Enable UBI"
+	depends on MTD
+	select CRC32
+	help
+	  UBI is a software layer above MTD layer which admits of LVM-like
+	  logical volumes on top of MTD devices, hides some complexities of
+	  flash chips like wear and bad blocks and provides some other useful
+	  capabilities. Please, consult the MTD web site for more details
+	  (www.linux-mtd.infradead.org).
+
+config MTD_UBI_WL_THRESHOLD
+	int "UBI wear-leveling threshold"
+	default 4096
+	range 2 65536
+	depends on MTD_UBI
+	help
+	  This parameter defines the maximum difference between the highest
+	  erase counter value and the lowest erase counter value of eraseblocks
+	  of UBI devices. When this threshold is exceeded, UBI starts performing
+	  wear leveling by means of moving data from eraseblock with low erase
+	  counter to eraseblocks with high erase counter. Leave the default
+	  value if unsure.
+
+config MTD_UBI_BEB_RESERVE
+	int "Percentage of reserved eraseblocks for bad eraseblocks handling"
+	default 1
+	range 0 25
+	depends on MTD_UBI
+	help
+	  If the MTD device admits of bad eraseblocks (e.g. NAND flash), UBI
+	  reserves some amount of physical eraseblocks to handle new bad
+	  eraseblocks. For example, if a flash physical eraseblock becomes bad,
+	  UBI uses these reserved physical eraseblocks to relocate the bad one.
+	  This option specifies how many physical eraseblocks will be reserved
+	  for bad eraseblock handling (percents of total number of good flash
+	  eraseblocks). If the underlying flash does not admit of bad
+	  eraseblocks (e.g. NOR flash), this value is ignored and nothing is
+	  reserved. Leave the default value if unsure.
+
+config MTD_UBI_GLUEBI
+	bool "Emulate MTD devices"
+	default n
+	depends on MTD_UBI
+	help
+	   This option enables MTD devices emulation on top of UBI volumes: for
+	   each UBI volumes an MTD device is created, and all I/O to this MTD
+	   device is redirected to the UBI volume. This is handy to make
+	   MTD-oriented software (like JFFS2) work on top of UBI. Do not enable
+	   this if no legacy software will be used.
+
+source "drivers/mtd/ubi/Kconfig.debug"
+endmenu
diff --git a/drivers/mtd/ubi/Kconfig.debug b/drivers/mtd/ubi/Kconfig.debug
new file mode 100644
index 0000000..1e2ee22
--- /dev/null
+++ b/drivers/mtd/ubi/Kconfig.debug
@@ -0,0 +1,104 @@
+comment "UBI debugging options"
+	depends on MTD_UBI
+
+config MTD_UBI_DEBUG
+	bool "UBI debugging"
+	depends on SYSFS
+	depends on MTD_UBI
+	select DEBUG_FS
+	select KALLSYMS_ALL
+	help
+	  This option enables UBI debugging.
+
+config MTD_UBI_DEBUG_MSG
+	bool "UBI debugging messages"
+	depends on MTD_UBI_DEBUG
+	default n
+	help
+	  This option enables UBI debugging messages.
+
+config MTD_UBI_DEBUG_PARANOID
+	bool "Extra self-checks"
+	default n
+	depends on MTD_UBI_DEBUG
+	help
+	  This option enables extra checks in UBI code. Note this slows UBI down
+	  significantly.
+
+config MTD_UBI_DEBUG_DISABLE_BGT
+	bool "Do not enable the UBI background thread"
+	depends on MTD_UBI_DEBUG
+	default n
+	help
+	  This option switches the background thread off by default. The thread
+	  may be also be enabled/disabled via UBI sysfs.
+
+config MTD_UBI_DEBUG_USERSPACE_IO
+	bool "Direct user-space write/erase support"
+	default n
+	depends on MTD_UBI_DEBUG
+	help
+	  By default, users cannot directly write and erase individual
+	  eraseblocks of dynamic volumes, and have to use update operation
+	  instead. This option enables this capability - it is very useful for
+	  debugging and testing.
+
+config MTD_UBI_DEBUG_EMULATE_BITFLIPS
+	bool "Emulate flash bit-flips"
+	depends on MTD_UBI_DEBUG
+	default n
+	help
+	  This option emulates bit-flips with probability 1/50, which in turn
+	  causes scrubbing. Useful for debugging and stressing UBI.
+
+config MTD_UBI_DEBUG_EMULATE_WRITE_FAILURES
+	bool "Emulate flash write failures"
+	depends on MTD_UBI_DEBUG
+	default n
+	help
+	  This option emulates write failures with probability 1/100. Useful for
+	  debugging and testing how UBI handlines errors.
+
+config MTD_UBI_DEBUG_EMULATE_ERASE_FAILURES
+	bool "Emulate flash erase failures"
+	depends on MTD_UBI_DEBUG
+	default n
+	help
+	  This option emulates erase failures with probability 1/100. Useful for
+	  debugging and testing how UBI handlines errors.
+
+menu "Additional UBI debugging messages"
+	depends on MTD_UBI_DEBUG
+
+config MTD_UBI_DEBUG_MSG_BLD
+	bool "Additional UBI initialization and build messages"
+	default n
+	depends on MTD_UBI_DEBUG
+	help
+	  This option enables detailed UBI initialization and device build
+	  debugging messages.
+
+config MTD_UBI_DEBUG_MSG_EBA
+	bool "Eraseblock association unit messages"
+	default n
+	depends on MTD_UBI_DEBUG
+	help
+	  This option enables debugging messages from the UBI eraseblock
+	  association unit.
+
+config MTD_UBI_DEBUG_MSG_WL
+	bool "Wear-leveling unit messages"
+	default n
+	depends on MTD_UBI_DEBUG
+	help
+	  This option enables debugging messages from the UBI wear-leveling
+	  unit.
+
+config MTD_UBI_DEBUG_MSG_IO
+	bool "Input/output unit messages"
+	default n
+	depends on MTD_UBI_DEBUG
+	help
+	  This option enables debugging messages from the UBI input/output unit.
+
+endmenu # UBI debugging messages
diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile
new file mode 100644
index 0000000..dd834e0
--- /dev/null
+++ b/drivers/mtd/ubi/Makefile
@@ -0,0 +1,7 @@
+obj-$(CONFIG_MTD_UBI) += ubi.o
+
+ubi-y += vtbl.o vmt.o upd.o build.o cdev.o kapi.o eba.o io.o wl.o scan.o
+ubi-y += misc.o
+
+ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o
+ubi-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
new file mode 100644
index 0000000..555d594
--- /dev/null
+++ b/drivers/mtd/ubi/build.c
@@ -0,0 +1,848 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright (c) Nokia Corporation, 2007
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём),
+ *         Frank Haverkamp
+ */
+
+/*
+ * This file includes UBI initialization and building of UBI devices. At the
+ * moment UBI devices may only be added while UBI is initialized, but dynamic
+ * device add/remove functionality is planned. Also, at the moment we only
+ * attach UBI devices by scanning, which will become a bottleneck when flashes
+ * reach certain large size. Then one may improve UBI and add other methods.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/stringify.h>
+#include <linux/stat.h>
+#include "ubi.h"
+
+/* Maximum length of the 'mtd=' parameter */
+#define MTD_PARAM_LEN_MAX 64
+
+/**
+ * struct mtd_dev_param - MTD device parameter description data structure.
+ * @name: MTD device name or number string
+ * @vid_hdr_offs: VID header offset
+ * @data_offs: data offset
+ */
+struct mtd_dev_param
+{
+	char name[MTD_PARAM_LEN_MAX];
+	int vid_hdr_offs;
+	int data_offs;
+};
+
+/* Numbers of elements set in the @mtd_dev_param array */
+static int mtd_devs = 0;
+
+/* MTD devices specification parameters */
+static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES];
+
+/* Number of UBI devices in system */
+int ubi_devices_cnt;
+
+/* All UBI devices in system */
+struct ubi_device *ubi_devices[UBI_MAX_DEVICES];
+
+/* Root UBI "class" object (corresponds to '/<sysfs>/class/ubi/') */
+struct class *ubi_class;
+
+/* "Show" method for files in '/<sysfs>/class/ubi/' */
+static ssize_t ubi_version_show(struct class *class, char *buf)
+{
+	return sprintf(buf, "%d\n", UBI_VERSION);
+}
+
+/* UBI version attribute ('/<sysfs>/class/ubi/version') */
+static struct class_attribute ubi_version =
+	__ATTR(version, S_IRUGO, ubi_version_show, NULL);
+
+static ssize_t dev_attribute_show(struct device *dev,
+				  struct device_attribute *attr, char *buf);
+
+/* UBI device attributes (correspond to files in '/<sysfs>/class/ubi/ubiX') */
+static struct device_attribute dev_eraseblock_size =
+	__ATTR(eraseblock_size, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_avail_eraseblocks =
+	__ATTR(avail_eraseblocks, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_total_eraseblocks =
+	__ATTR(total_eraseblocks, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_volumes_count =
+	__ATTR(volumes_count, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_max_ec =
+	__ATTR(max_ec, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_reserved_for_bad =
+	__ATTR(reserved_for_bad, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_bad_peb_count =
+	__ATTR(bad_peb_count, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_max_vol_count =
+	__ATTR(max_vol_count, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_min_io_size =
+	__ATTR(min_io_size, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_bgt_enabled =
+	__ATTR(bgt_enabled, S_IRUGO, dev_attribute_show, NULL);
+
+/* "Show" method for files in '/<sysfs>/class/ubi/ubiX/' */
+static ssize_t dev_attribute_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	const struct ubi_device *ubi;
+
+	ubi = container_of(dev, struct ubi_device, dev);
+	if (attr == &dev_eraseblock_size)
+		return sprintf(buf, "%d\n", ubi->leb_size);
+	else if (attr == &dev_avail_eraseblocks)
+		return sprintf(buf, "%d\n", ubi->avail_pebs);
+	else if (attr == &dev_total_eraseblocks)
+		return sprintf(buf, "%d\n", ubi->good_peb_count);
+	else if (attr == &dev_volumes_count)
+		return sprintf(buf, "%d\n", ubi->vol_count);
+	else if (attr == &dev_max_ec)
+		return sprintf(buf, "%d\n", ubi->max_ec);
+	else if (attr == &dev_reserved_for_bad)
+		return sprintf(buf, "%d\n", ubi->beb_rsvd_pebs);
+	else if (attr == &dev_bad_peb_count)
+		return sprintf(buf, "%d\n", ubi->bad_peb_count);
+	else if (attr == &dev_max_vol_count)
+		return sprintf(buf, "%d\n", ubi->vtbl_slots);
+	else if (attr == &dev_min_io_size)
+		return sprintf(buf, "%d\n", ubi->min_io_size);
+	else if (attr == &dev_bgt_enabled)
+		return sprintf(buf, "%d\n", ubi->thread_enabled);
+	else
+		BUG();
+
+	return 0;
+}
+
+/* Fake "release" method for UBI devices */
+static void dev_release(struct device *dev) { }
+
+/**
+ * ubi_sysfs_init - initialize sysfs for an UBI device.
+ * @ubi: UBI device description object
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int ubi_sysfs_init(struct ubi_device *ubi)
+{
+	int err;
+
+	ubi->dev.release = dev_release;
+	ubi->dev.devt = MKDEV(ubi->major, 0);
+	ubi->dev.class = ubi_class;
+	sprintf(&ubi->dev.bus_id[0], UBI_NAME_STR"%d", ubi->ubi_num);
+	err = device_register(&ubi->dev);
+	if (err)
+		goto out;
+
+	err = device_create_file(&ubi->dev, &dev_eraseblock_size);
+	if (err)
+		goto out_unregister;
+	err = device_create_file(&ubi->dev, &dev_avail_eraseblocks);
+	if (err)
+		goto out_eraseblock_size;
+	err = device_create_file(&ubi->dev, &dev_total_eraseblocks);
+	if (err)
+		goto out_avail_eraseblocks;
+	err = device_create_file(&ubi->dev, &dev_volumes_count);
+	if (err)
+		goto out_total_eraseblocks;
+	err = device_create_file(&ubi->dev, &dev_max_ec);
+	if (err)
+		goto out_volumes_count;
+	err = device_create_file(&ubi->dev, &dev_reserved_for_bad);
+	if (err)
+		goto out_volumes_max_ec;
+	err = device_create_file(&ubi->dev, &dev_bad_peb_count);
+	if (err)
+		goto out_reserved_for_bad;
+	err = device_create_file(&ubi->dev, &dev_max_vol_count);
+	if (err)
+		goto out_bad_peb_count;
+	err = device_create_file(&ubi->dev, &dev_min_io_size);
+	if (err)
+		goto out_max_vol_count;
+	err = device_create_file(&ubi->dev, &dev_bgt_enabled);
+	if (err)
+		goto out_min_io_size;
+
+	return 0;
+
+out_min_io_size:
+	device_remove_file(&ubi->dev, &dev_min_io_size);
+out_max_vol_count:
+	device_remove_file(&ubi->dev, &dev_max_vol_count);
+out_bad_peb_count:
+	device_remove_file(&ubi->dev, &dev_bad_peb_count);
+out_reserved_for_bad:
+	device_remove_file(&ubi->dev, &dev_reserved_for_bad);
+out_volumes_max_ec:
+	device_remove_file(&ubi->dev, &dev_max_ec);
+out_volumes_count:
+	device_remove_file(&ubi->dev, &dev_volumes_count);
+out_total_eraseblocks:
+	device_remove_file(&ubi->dev, &dev_total_eraseblocks);
+out_avail_eraseblocks:
+	device_remove_file(&ubi->dev, &dev_avail_eraseblocks);
+out_eraseblock_size:
+	device_remove_file(&ubi->dev, &dev_eraseblock_size);
+out_unregister:
+	device_unregister(&ubi->dev);
+out:
+	ubi_err("failed to initialize sysfs for %s", ubi->ubi_name);
+	return err;
+}
+
+/**
+ * ubi_sysfs_close - close sysfs for an UBI device.
+ * @ubi: UBI device description object
+ */
+static void ubi_sysfs_close(struct ubi_device *ubi)
+{
+	device_remove_file(&ubi->dev, &dev_bgt_enabled);
+	device_remove_file(&ubi->dev, &dev_min_io_size);
+	device_remove_file(&ubi->dev, &dev_max_vol_count);
+	device_remove_file(&ubi->dev, &dev_bad_peb_count);
+	device_remove_file(&ubi->dev, &dev_reserved_for_bad);
+	device_remove_file(&ubi->dev, &dev_max_ec);
+	device_remove_file(&ubi->dev, &dev_volumes_count);
+	device_remove_file(&ubi->dev, &dev_total_eraseblocks);
+	device_remove_file(&ubi->dev, &dev_avail_eraseblocks);
+	device_remove_file(&ubi->dev, &dev_eraseblock_size);
+	device_unregister(&ubi->dev);
+}
+
+/**
+ * kill_volumes - destroy all volumes.
+ * @ubi: UBI device description object
+ */
+static void kill_volumes(struct ubi_device *ubi)
+{
+	int i;
+
+	for (i = 0; i < ubi->vtbl_slots; i++)
+		if (ubi->volumes[i])
+			ubi_free_volume(ubi, i);
+}
+
+/**
+ * uif_init - initialize user interfaces for an UBI device.
+ * @ubi: UBI device description object
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int uif_init(struct ubi_device *ubi)
+{
+	int i, err;
+	dev_t dev;
+
+	mutex_init(&ubi->vtbl_mutex);
+	spin_lock_init(&ubi->volumes_lock);
+
+	sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num);
+
+	/*
+	 * Major numbers for the UBI character devices are allocated
+	 * dynamically. Major numbers of volume character devices are
+	 * equivalent to ones of the corresponding UBI character device. Minor
+	 * numbers of UBI character devices are 0, while minor numbers of
+	 * volume character devices start from 1. Thus, we allocate one major
+	 * number and ubi->vtbl_slots + 1 minor numbers.
+	 */
+	err = alloc_chrdev_region(&dev, 0, ubi->vtbl_slots + 1, ubi->ubi_name);
+	if (err) {
+		ubi_err("cannot register UBI character devices");
+		return err;
+	}
+
+	cdev_init(&ubi->cdev, &ubi_cdev_operations);
+	ubi->major = MAJOR(dev);
+	dbg_msg("%s major is %u", ubi->ubi_name, ubi->major);
+	ubi->cdev.owner = THIS_MODULE;
+
+	dev = MKDEV(ubi->major, 0);
+	err = cdev_add(&ubi->cdev, dev, 1);
+	if (err) {
+		ubi_err("cannot add character device %s", ubi->ubi_name);
+		goto out_unreg;
+	}
+
+	err = ubi_sysfs_init(ubi);
+	if (err)
+		goto out_cdev;
+
+	for (i = 0; i < ubi->vtbl_slots; i++)
+		if (ubi->volumes[i]) {
+			err = ubi_add_volume(ubi, i);
+			if (err)
+				goto out_volumes;
+		}
+
+	return 0;
+
+out_volumes:
+	kill_volumes(ubi);
+	ubi_sysfs_close(ubi);
+out_cdev:
+	cdev_del(&ubi->cdev);
+out_unreg:
+	unregister_chrdev_region(MKDEV(ubi->major, 0),
+				 ubi->vtbl_slots + 1);
+	return err;
+}
+
+/**
+ * uif_close - close user interfaces for an UBI device.
+ * @ubi: UBI device description object
+ */
+static void uif_close(struct ubi_device *ubi)
+{
+	kill_volumes(ubi);
+	ubi_sysfs_close(ubi);
+	cdev_del(&ubi->cdev);
+	unregister_chrdev_region(MKDEV(ubi->major, 0), ubi->vtbl_slots + 1);
+}
+
+/**
+ * attach_by_scanning - attach an MTD device using scanning method.
+ * @ubi: UBI device descriptor
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ *
+ * Note, currently this is the only method to attach UBI devices. Hopefully in
+ * the future we'll have more scalable attaching methods and avoid full media
+ * scanning. But even in this case scanning will be needed as a fall-back
+ * attaching method if there are some on-flash table corruptions.
+ */
+static int attach_by_scanning(struct ubi_device *ubi)
+{
+	int err;
+	struct ubi_scan_info *si;
+
+	si = ubi_scan(ubi);
+	if (IS_ERR(si))
+		return PTR_ERR(si);
+
+	ubi->bad_peb_count = si->bad_peb_count;
+	ubi->good_peb_count = ubi->peb_count - ubi->bad_peb_count;
+	ubi->max_ec = si->max_ec;
+	ubi->mean_ec = si->mean_ec;
+
+	err = ubi_read_volume_table(ubi, si);
+	if (err)
+		goto out_si;
+
+	err = ubi_wl_init_scan(ubi, si);
+	if (err)
+		goto out_vtbl;
+
+	err = ubi_eba_init_scan(ubi, si);
+	if (err)
+		goto out_wl;
+
+	ubi_scan_destroy_si(si);
+	return 0;
+
+out_wl:
+	ubi_wl_close(ubi);
+out_vtbl:
+	kfree(ubi->vtbl);
+out_si:
+	ubi_scan_destroy_si(si);
+	return err;
+}
+
+/**
+ * io_init - initialize I/O unit for a given UBI device.
+ * @ubi: UBI device description object
+ *
+ * If @ubi->vid_hdr_offset or @ubi->leb_start is zero, default offsets are
+ * assumed:
+ *   o EC header is always at offset zero - this cannot be changed;
+ *   o VID header starts just after the EC header at the closest address
+ *   aligned to @io->@hdrs_min_io_size;
+ *   o data starts just after the VID header at the closest address aligned to
+ *     @io->@min_io_size
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int io_init(struct ubi_device *ubi)
+{
+	if (ubi->mtd->numeraseregions != 0) {
+		/*
+		 * Some flashes have several erase regions. Different regions
+		 * may have different eraseblock size and other
+		 * characteristics. It looks like mostly multi-region flashes
+		 * have one "main" region and one or more small regions to
+		 * store boot loader code or boot parameters or whatever. I
+		 * guess we should just pick the largest region. But this is
+		 * not implemented.
+		 */
+		ubi_err("multiple regions, not implemented");
+		return -EINVAL;
+	}
+
+	/*
+	 * Note, in this implementation we support MTD devices with 0x7FFFFFFF
+	 * physical eraseblocks maximum.
+	 */
+
+	ubi->peb_size   = ubi->mtd->erasesize;
+	ubi->peb_count  = ubi->mtd->size / ubi->mtd->erasesize;
+	ubi->flash_size = ubi->mtd->size;
+
+	if (ubi->mtd->block_isbad && ubi->mtd->block_markbad)
+		ubi->bad_allowed = 1;
+
+	ubi->min_io_size = ubi->mtd->writesize;
+	ubi->hdrs_min_io_size = ubi->mtd->writesize >> ubi->mtd->subpage_sft;
+
+	/* Make sure minimal I/O unit is power of 2 */
+	if (ubi->min_io_size == 0 ||
+	    (ubi->min_io_size & (ubi->min_io_size - 1))) {
+		ubi_err("bad min. I/O unit");
+		return -EINVAL;
+	}
+
+	ubi_assert(ubi->hdrs_min_io_size > 0);
+	ubi_assert(ubi->hdrs_min_io_size <= ubi->min_io_size);
+	ubi_assert(ubi->min_io_size % ubi->hdrs_min_io_size == 0);
+
+	/* Calculate default aligned sizes of EC and VID headers */
+	ubi->ec_hdr_alsize = ALIGN(UBI_EC_HDR_SIZE, ubi->hdrs_min_io_size);
+	ubi->vid_hdr_alsize = ALIGN(UBI_VID_HDR_SIZE, ubi->hdrs_min_io_size);
+
+	dbg_msg("min_io_size      %d", ubi->min_io_size);
+	dbg_msg("hdrs_min_io_size %d", ubi->hdrs_min_io_size);
+	dbg_msg("ec_hdr_alsize    %d", ubi->ec_hdr_alsize);
+	dbg_msg("vid_hdr_alsize   %d", ubi->vid_hdr_alsize);
+
+	if (ubi->vid_hdr_offset == 0)
+		/* Default offset */
+		ubi->vid_hdr_offset = ubi->vid_hdr_aloffset =
+				      ubi->ec_hdr_alsize;
+	else {
+		ubi->vid_hdr_aloffset = ubi->vid_hdr_offset &
+						~(ubi->hdrs_min_io_size - 1);
+		ubi->vid_hdr_shift = ubi->vid_hdr_offset -
+						ubi->vid_hdr_aloffset;
+	}
+
+	/* Similar for the data offset */
+	if (ubi->leb_start == 0) {
+		ubi->leb_start = ubi->vid_hdr_offset + ubi->vid_hdr_alsize;
+		ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size);
+	}
+
+	dbg_msg("vid_hdr_offset   %d", ubi->vid_hdr_offset);
+	dbg_msg("vid_hdr_aloffset %d", ubi->vid_hdr_aloffset);
+	dbg_msg("vid_hdr_shift    %d", ubi->vid_hdr_shift);
+	dbg_msg("leb_start        %d", ubi->leb_start);
+
+	/* The shift must be aligned to 32-bit boundary */
+	if (ubi->vid_hdr_shift % 4) {
+		ubi_err("unaligned VID header shift %d",
+			ubi->vid_hdr_shift);
+		return -EINVAL;
+	}
+
+	/* Check sanity */
+	if (ubi->vid_hdr_offset < UBI_EC_HDR_SIZE ||
+	    ubi->leb_start < ubi->vid_hdr_offset + UBI_VID_HDR_SIZE ||
+	    ubi->leb_start > ubi->peb_size - UBI_VID_HDR_SIZE ||
+	    ubi->leb_start % ubi->min_io_size) {
+		ubi_err("bad VID header (%d) or data offsets (%d)",
+			ubi->vid_hdr_offset, ubi->leb_start);
+		return -EINVAL;
+	}
+
+	/*
+	 * It may happen that EC and VID headers are situated in one minimal
+	 * I/O unit. In this case we can only accept this UBI image in
+	 * read-only mode.
+	 */
+	if (ubi->vid_hdr_offset + UBI_VID_HDR_SIZE <= ubi->hdrs_min_io_size) {
+		ubi_warn("EC and VID headers are in the same minimal I/O unit, "
+			 "switch to read-only mode");
+		ubi->ro_mode = 1;
+	}
+
+	ubi->leb_size = ubi->peb_size - ubi->leb_start;
+
+	if (!(ubi->mtd->flags & MTD_WRITEABLE)) {
+		ubi_msg("MTD device %d is write-protected, attach in "
+			"read-only mode", ubi->mtd->index);
+		ubi->ro_mode = 1;
+	}
+
+	dbg_msg("leb_size         %d", ubi->leb_size);
+	dbg_msg("ro_mode          %d", ubi->ro_mode);
+
+	/*
+	 * Note, ideally, we have to initialize ubi->bad_peb_count here. But
+	 * unfortunately, MTD does not provide this information. We should loop
+	 * over all physical eraseblocks and invoke mtd->block_is_bad() for
+	 * each physical eraseblock. So, we skip ubi->bad_peb_count
+	 * uninitialized and initialize it after scanning.
+	 */
+
+	return 0;
+}
+
+/**
+ * attach_mtd_dev - attach an MTD device.
+ * @mtd_dev: MTD device name or number string
+ * @vid_hdr_offset: VID header offset
+ * @data_offset: data offset
+ *
+ * This function attaches an MTD device to UBI. It first treats @mtd_dev as the
+ * MTD device name, and tries to open it by this name. If it is unable to open,
+ * it tries to convert @mtd_dev to an integer and open the MTD device by its
+ * number. Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+static int attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset,
+			  int data_offset)
+{
+	struct ubi_device *ubi;
+	struct mtd_info *mtd;
+	int i, err;
+
+	mtd = get_mtd_device_nm(mtd_dev);
+	if (IS_ERR(mtd)) {
+		int mtd_num;
+		char *endp;
+
+		if (PTR_ERR(mtd) != -ENODEV)
+			return PTR_ERR(mtd);
+
+		/*
+		 * Probably this is not MTD device name but MTD device number -
+		 * check this out.
+		 */
+		mtd_num = simple_strtoul(mtd_dev, &endp, 0);
+		if (*endp != '\0' || mtd_dev == endp) {
+			ubi_err("incorrect MTD device: \"%s\"", mtd_dev);
+			return -ENODEV;
+		}
+
+		mtd = get_mtd_device(NULL, mtd_num);
+		if (IS_ERR(mtd))
+			return PTR_ERR(mtd);
+	}
+
+	/* Check if we already have the same MTD device attached */
+	for (i = 0; i < ubi_devices_cnt; i++)
+		if (ubi_devices[i]->mtd->index == mtd->index) {
+			ubi_err("mtd%d is already attached to ubi%d",
+				mtd->index, i);
+			err = -EINVAL;
+			goto out_mtd;
+		}
+
+	ubi = ubi_devices[ubi_devices_cnt] = kzalloc(sizeof(struct ubi_device),
+						      GFP_KERNEL);
+	if (!ubi) {
+		err = -ENOMEM;
+		goto out_mtd;
+	}
+
+	ubi->ubi_num = ubi_devices_cnt;
+	ubi->mtd = mtd;
+
+	dbg_msg("attaching mtd%d to ubi%d: VID header offset %d data offset %d",
+		ubi->mtd->index, ubi_devices_cnt, vid_hdr_offset, data_offset);
+
+	ubi->vid_hdr_offset = vid_hdr_offset;
+	ubi->leb_start = data_offset;
+	err = io_init(ubi);
+	if (err)
+		goto out_free;
+
+	err = attach_by_scanning(ubi);
+	if (err) {
+		dbg_err("failed to attach by scanning, error %d", err);
+		goto out_free;
+	}
+
+	err = uif_init(ubi);
+	if (err)
+		goto out_detach;
+
+	ubi_devices_cnt += 1;
+
+	ubi_msg("attached mtd%d to ubi%d", ubi->mtd->index, ubi_devices_cnt);
+	ubi_msg("MTD device name:            \"%s\"", ubi->mtd->name);
+	ubi_msg("MTD device size:            %llu MiB", ubi->flash_size >> 20);
+	ubi_msg("physical eraseblock size:   %d bytes (%d KiB)",
+		ubi->peb_size, ubi->peb_size >> 10);
+	ubi_msg("logical eraseblock size:    %d bytes", ubi->leb_size);
+	ubi_msg("number of good PEBs:        %d", ubi->good_peb_count);
+	ubi_msg("number of bad PEBs:         %d", ubi->bad_peb_count);
+	ubi_msg("smallest flash I/O unit:    %d", ubi->min_io_size);
+	ubi_msg("VID header offset:          %d (aligned %d)",
+		ubi->vid_hdr_offset, ubi->vid_hdr_aloffset);
+	ubi_msg("data offset:                %d", ubi->leb_start);
+	ubi_msg("max. allowed volumes:       %d", ubi->vtbl_slots);
+	ubi_msg("wear-leveling threshold:    %d", CONFIG_MTD_UBI_WL_THRESHOLD);
+	ubi_msg("number of internal volumes: %d", UBI_INT_VOL_COUNT);
+	ubi_msg("number of user volumes:     %d",
+		ubi->vol_count - UBI_INT_VOL_COUNT);
+	ubi_msg("available PEBs:             %d", ubi->avail_pebs);
+	ubi_msg("total number of reserved PEBs: %d", ubi->rsvd_pebs);
+	ubi_msg("number of PEBs reserved for bad PEB handling: %d",
+		ubi->beb_rsvd_pebs);
+	ubi_msg("max/mean erase counter: %d/%d", ubi->max_ec, ubi->mean_ec);
+
+	/* Enable the background thread */
+	if (!DBG_DISABLE_BGT) {
+		ubi->thread_enabled = 1;
+		wake_up_process(ubi->bgt_thread);
+	}
+
+	return 0;
+
+out_detach:
+	ubi_eba_close(ubi);
+	ubi_wl_close(ubi);
+	kfree(ubi->vtbl);
+out_free:
+	kfree(ubi);
+out_mtd:
+	put_mtd_device(mtd);
+	ubi_devices[ubi_devices_cnt] = NULL;
+	return err;
+}
+
+/**
+ * detach_mtd_dev - detach an MTD device.
+ * @ubi: UBI device description object
+ */
+static void detach_mtd_dev(struct ubi_device *ubi)
+{
+	int ubi_num = ubi->ubi_num, mtd_num = ubi->mtd->index;
+
+	dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num);
+	uif_close(ubi);
+	ubi_eba_close(ubi);
+	ubi_wl_close(ubi);
+	kfree(ubi->vtbl);
+	put_mtd_device(ubi->mtd);
+	kfree(ubi_devices[ubi_num]);
+	ubi_devices[ubi_num] = NULL;
+	ubi_devices_cnt -= 1;
+	ubi_assert(ubi_devices_cnt >= 0);
+	ubi_msg("mtd%d is detached from ubi%d", mtd_num, ubi_num);
+}
+
+static int __init ubi_init(void)
+{
+	int err, i, k;
+
+	/* Ensure that EC and VID headers have correct size */
+	BUILD_BUG_ON(sizeof(struct ubi_ec_hdr) != 64);
+	BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64);
+
+	if (mtd_devs > UBI_MAX_DEVICES) {
+		printk("UBI error: too many MTD devices, maximum is %d\n",
+		       UBI_MAX_DEVICES);
+		return -EINVAL;
+	}
+
+	ubi_class = class_create(THIS_MODULE, UBI_NAME_STR);
+	if (IS_ERR(ubi_class))
+		return PTR_ERR(ubi_class);
+
+	err = class_create_file(ubi_class, &ubi_version);
+	if (err)
+		goto out_class;
+
+	/* Attach MTD devices */
+	for (i = 0; i < mtd_devs; i++) {
+		struct mtd_dev_param *p = &mtd_dev_param[i];
+
+		cond_resched();
+
+		if (!p->name) {
+			dbg_err("empty name");
+			err = -EINVAL;
+			goto out_detach;
+		}
+
+		err = attach_mtd_dev(p->name, p->vid_hdr_offs, p->data_offs);
+		if (err)
+			goto out_detach;
+	}
+
+	return 0;
+
+out_detach:
+	for (k = 0; k < i; k++)
+		detach_mtd_dev(ubi_devices[k]);
+	class_remove_file(ubi_class, &ubi_version);
+out_class:
+	class_destroy(ubi_class);
+	return err;
+}
+module_init(ubi_init);
+
+static void __exit ubi_exit(void)
+{
+	int i, n = ubi_devices_cnt;
+
+	for (i = 0; i < n; i++)
+		detach_mtd_dev(ubi_devices[i]);
+	class_remove_file(ubi_class, &ubi_version);
+	class_destroy(ubi_class);
+}
+module_exit(ubi_exit);
+
+/**
+ * bytes_str_to_int - convert a string representing number of bytes to an
+ * integer.
+ * @str: the string to convert
+ *
+ * This function returns positive resulting integer in case of success and a
+ * negative error code in case of failure.
+ */
+static int __init bytes_str_to_int(const char *str)
+{
+	char *endp;
+	unsigned long result;
+
+	result = simple_strtoul(str, &endp, 0);
+	if (str == endp || result < 0) {
+		printk("UBI error: incorrect bytes count: \"%s\"\n", str);
+		return -EINVAL;
+	}
+
+	switch (*endp) {
+	case 'G':
+		result *= 1024;
+	case 'M':
+		result *= 1024;
+	case 'K':
+	case 'k':
+		result *= 1024;
+		if (endp[1] == 'i' && (endp[2] == '\0' ||
+			  endp[2] == 'B'  || endp[2] == 'b'))
+			endp += 2;
+	case '\0':
+		break;
+	default:
+		printk("UBI error: incorrect bytes count: \"%s\"\n", str);
+		return -EINVAL;
+	}
+
+	return result;
+}
+
+/**
+ * ubi_mtd_param_parse - parse the 'mtd=' UBI parameter.
+ * @val: the parameter value to parse
+ * @kp: not used
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of error.
+ */
+static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp)
+{
+	int i, len;
+	struct mtd_dev_param *p;
+	char buf[MTD_PARAM_LEN_MAX];
+	char *pbuf = &buf[0];
+	char *tokens[3] = {NULL, NULL, NULL};
+
+	if (mtd_devs == UBI_MAX_DEVICES) {
+		printk("UBI error: too many parameters, max. is %d\n",
+		       UBI_MAX_DEVICES);
+		return -EINVAL;
+	}
+
+	len = strnlen(val, MTD_PARAM_LEN_MAX);
+	if (len == MTD_PARAM_LEN_MAX) {
+		printk("UBI error: parameter \"%s\" is too long, max. is %d\n",
+		       val, MTD_PARAM_LEN_MAX);
+		return -EINVAL;
+	}
+
+	if (len == 0) {
+		printk("UBI warning: empty 'mtd=' parameter - ignored\n");
+		return 0;
+	}
+
+	strcpy(buf, val);
+
+	/* Get rid of the final newline */
+	if (buf[len - 1] == '\n')
+		buf[len - 1] = 0;
+
+	for (i = 0; i < 3; i++)
+		tokens[i] = strsep(&pbuf, ",");
+
+	if (pbuf) {
+		printk("UBI error: too many arguments at \"%s\"\n", val);
+		return -EINVAL;
+	}
+
+	if (tokens[0] == '\0')
+		return -EINVAL;
+
+	p = &mtd_dev_param[mtd_devs];
+	strcpy(&p->name[0], tokens[0]);
+
+	if (tokens[1])
+		p->vid_hdr_offs = bytes_str_to_int(tokens[1]);
+	if (tokens[2])
+		p->data_offs = bytes_str_to_int(tokens[2]);
+
+	if (p->vid_hdr_offs < 0)
+		return p->vid_hdr_offs;
+	if (p->data_offs < 0)
+		return p->data_offs;
+
+	mtd_devs += 1;
+	return 0;
+}
+
+module_param_call(mtd, ubi_mtd_param_parse, NULL, NULL, 000);
+MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: "
+		      "mtd=<name|num>[,<vid_hdr_offs>,<data_offs>]. "
+		      "Multiple \"mtd\" parameters may be specified.\n"
+		      "MTD devices may be specified by their number or name. "
+		      "Optional \"vid_hdr_offs\" and \"data_offs\" parameters "
+		      "specify UBI VID header position and data starting "
+		      "position to be used by UBI.\n"
+		      "Example: mtd=content,1984,2048 mtd=4 - attach MTD device"
+		      "with name content using VID header offset 1984 and data "
+		      "start 2048, and MTD device number 4 using default "
+		      "offsets");
+
+MODULE_VERSION(__stringify(UBI_VERSION));
+MODULE_DESCRIPTION("UBI - Unsorted Block Images");
+MODULE_AUTHOR("Artem Bityutskiy");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c
new file mode 100644
index 0000000..6612eb7
--- /dev/null
+++ b/drivers/mtd/ubi/cdev.c
@@ -0,0 +1,722 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file includes implementation of UBI character device operations.
+ *
+ * There are two kinds of character devices in UBI: UBI character devices and
+ * UBI volume character devices. UBI character devices allow users to
+ * manipulate whole volumes: create, remove, and re-size them. Volume character
+ * devices provide volume I/O capabilities.
+ *
+ * Major and minor numbers are assigned dynamically to both UBI and volume
+ * character devices.
+ */
+
+#include <linux/module.h>
+#include <linux/stat.h>
+#include <linux/ioctl.h>
+#include <linux/capability.h>
+#include <mtd/ubi-user.h>
+#include <asm/uaccess.h>
+#include <asm/div64.h>
+#include "ubi.h"
+
+/*
+ * Maximum sequence numbers of UBI and volume character device IOCTLs (direct
+ * logical eraseblock erase is a debug-only feature).
+ */
+#define UBI_CDEV_IOC_MAX_SEQ 2
+#ifndef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO
+#define VOL_CDEV_IOC_MAX_SEQ 1
+#else
+#define VOL_CDEV_IOC_MAX_SEQ 2
+#endif
+
+/**
+ * major_to_device - get UBI device object by character device major number.
+ * @major: major number
+ *
+ * This function returns a pointer to the UBI device object.
+ */
+static struct ubi_device *major_to_device(int major)
+{
+	int i;
+
+	for (i = 0; i < ubi_devices_cnt; i++)
+		if (ubi_devices[i] && ubi_devices[i]->major == major)
+			return ubi_devices[i];
+	BUG();
+}
+
+/**
+ * get_exclusive - get exclusive access to an UBI volume.
+ * @desc: volume descriptor
+ *
+ * This function changes UBI volume open mode to "exclusive". Returns previous
+ * mode value (positive integer) in case of success and a negative error code
+ * in case of failure.
+ */
+static int get_exclusive(struct ubi_volume_desc *desc)
+{
+	int users, err;
+	struct ubi_volume *vol = desc->vol;
+
+	spin_lock(&vol->ubi->volumes_lock);
+	users = vol->readers + vol->writers + vol->exclusive;
+	ubi_assert(users > 0);
+	if (users > 1) {
+		dbg_err("%d users for volume %d", users, vol->vol_id);
+		err = -EBUSY;
+	} else {
+		vol->readers = vol->writers = 0;
+		vol->exclusive = 1;
+		err = desc->mode;
+		desc->mode = UBI_EXCLUSIVE;
+	}
+	spin_unlock(&vol->ubi->volumes_lock);
+
+	return err;
+}
+
+/**
+ * revoke_exclusive - revoke exclusive mode.
+ * @desc: volume descriptor
+ * @mode: new mode to switch to
+ */
+static void revoke_exclusive(struct ubi_volume_desc *desc, int mode)
+{
+	struct ubi_volume *vol = desc->vol;
+
+	spin_lock(&vol->ubi->volumes_lock);
+	ubi_assert(vol->readers == 0 && vol->writers == 0);
+	ubi_assert(vol->exclusive == 1 && desc->mode == UBI_EXCLUSIVE);
+	vol->exclusive = 0;
+	if (mode == UBI_READONLY)
+		vol->readers = 1;
+	else if (mode == UBI_READWRITE)
+		vol->writers = 1;
+	else
+		vol->exclusive = 1;
+	spin_unlock(&vol->ubi->volumes_lock);
+
+	desc->mode = mode;
+}
+
+static int vol_cdev_open(struct inode *inode, struct file *file)
+{
+	struct ubi_volume_desc *desc;
+	const struct ubi_device *ubi = major_to_device(imajor(inode));
+	int vol_id = iminor(inode) - 1;
+	int mode;
+
+	if (file->f_mode & FMODE_WRITE)
+		mode = UBI_READWRITE;
+	else
+		mode = UBI_READONLY;
+
+	dbg_msg("open volume %d, mode %d", vol_id, mode);
+
+	desc = ubi_open_volume(ubi->ubi_num, vol_id, mode);
+	if (IS_ERR(desc))
+		return PTR_ERR(desc);
+
+	file->private_data = desc;
+	return 0;
+}
+
+static int vol_cdev_release(struct inode *inode, struct file *file)
+{
+	struct ubi_volume_desc *desc = file->private_data;
+	struct ubi_volume *vol = desc->vol;
+
+	dbg_msg("release volume %d, mode %d", vol->vol_id, desc->mode);
+
+	if (vol->updating) {
+		ubi_warn("update of volume %d not finished, volume is damaged",
+			 vol->vol_id);
+		vol->updating = 0;
+		kfree(vol->upd_buf);
+	}
+
+	ubi_close_volume(desc);
+	return 0;
+}
+
+static loff_t vol_cdev_llseek(struct file *file, loff_t offset, int origin)
+{
+	struct ubi_volume_desc *desc = file->private_data;
+	struct ubi_volume *vol = desc->vol;
+	loff_t new_offset;
+
+	if (vol->updating) {
+		 /* Update is in progress, seeking is prohibited */
+		dbg_err("updating");
+		return -EBUSY;
+	}
+
+	switch (origin) {
+	case 0: /* SEEK_SET */
+		new_offset = offset;
+		break;
+	case 1: /* SEEK_CUR */
+		new_offset = file->f_pos + offset;
+		break;
+	case 2: /* SEEK_END */
+		new_offset = vol->used_bytes + offset;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (new_offset < 0 || new_offset > vol->used_bytes) {
+		dbg_err("bad seek %lld", new_offset);
+		return -EINVAL;
+	}
+
+	dbg_msg("seek volume %d, offset %lld, origin %d, new offset %lld",
+		vol->vol_id, offset, origin, new_offset);
+
+	file->f_pos = new_offset;
+	return new_offset;
+}
+
+static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count,
+			     loff_t *offp)
+{
+	struct ubi_volume_desc *desc = file->private_data;
+	struct ubi_volume *vol = desc->vol;
+	struct ubi_device *ubi = vol->ubi;
+	int err, lnum, off, len,  vol_id = desc->vol->vol_id, tbuf_size;
+	size_t count_save = count;
+	void *tbuf;
+	uint64_t tmp;
+
+	dbg_msg("read %zd bytes from offset %lld of volume %d",
+		count, *offp, vol_id);
+
+	if (vol->updating) {
+		dbg_err("updating");
+		return -EBUSY;
+	}
+	if (vol->upd_marker) {
+		dbg_err("damaged volume, update marker is set");
+		return -EBADF;
+	}
+	if (*offp == vol->used_bytes || count == 0)
+		return 0;
+
+	if (vol->corrupted)
+		dbg_msg("read from corrupted volume %d", vol_id);
+
+	if (*offp + count > vol->used_bytes)
+		count_save = count = vol->used_bytes - *offp;
+
+	tbuf_size = vol->usable_leb_size;
+	if (count < tbuf_size)
+		tbuf_size = ALIGN(count, ubi->min_io_size);
+	tbuf = kmalloc(tbuf_size, GFP_KERNEL);
+	if (!tbuf)
+		return -ENOMEM;
+
+	len = count > tbuf_size ? tbuf_size : count;
+
+	tmp = *offp;
+	off = do_div(tmp, vol->usable_leb_size);
+	lnum = tmp;
+
+	do {
+		cond_resched();
+
+		if (off + len >= vol->usable_leb_size)
+			len = vol->usable_leb_size - off;
+
+		err = ubi_eba_read_leb(ubi, vol_id, lnum, tbuf, off, len, 0);
+		if (err)
+			break;
+
+		off += len;
+		if (off == vol->usable_leb_size) {
+			lnum += 1;
+			off -= vol->usable_leb_size;
+		}
+
+		count -= len;
+		*offp += len;
+
+		err = copy_to_user(buf, tbuf, len);
+		if (err) {
+			err = -EFAULT;
+			break;
+		}
+
+		buf += len;
+		len = count > tbuf_size ? tbuf_size : count;
+	} while (count);
+
+	kfree(tbuf);
+	return err ? err : count_save - count;
+}
+
+#ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO
+
+/*
+ * This function allows to directly write to dynamic UBI volumes, without
+ * issuing the volume update operation. Available only as a debugging feature.
+ * Very useful for testing UBI.
+ */
+static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf,
+				     size_t count, loff_t *offp)
+{
+	struct ubi_volume_desc *desc = file->private_data;
+	struct ubi_volume *vol = desc->vol;
+	struct ubi_device *ubi = vol->ubi;
+	int lnum, off, len, tbuf_size, vol_id = vol->vol_id, err = 0;
+	size_t count_save = count;
+	char *tbuf;
+	uint64_t tmp;
+
+	dbg_msg("requested: write %zd bytes to offset %lld of volume %u",
+		count, *offp, desc->vol->vol_id);
+
+	if (vol->vol_type == UBI_STATIC_VOLUME)
+		return -EROFS;
+
+	tmp = *offp;
+	off = do_div(tmp, vol->usable_leb_size);
+	lnum = tmp;
+
+	if (off % ubi->min_io_size) {
+		dbg_err("unaligned position");
+		return -EINVAL;
+	}
+
+	if (*offp + count > vol->used_bytes)
+		count_save = count = vol->used_bytes - *offp;
+
+	/* We can write only in fractions of the minimum I/O unit */
+	if (count % ubi->min_io_size) {
+		dbg_err("unaligned write length");
+		return -EINVAL;
+	}
+
+	tbuf_size = vol->usable_leb_size;
+	if (count < tbuf_size)
+		tbuf_size = ALIGN(count, ubi->min_io_size);
+	tbuf = kmalloc(tbuf_size, GFP_KERNEL);
+	if (!tbuf)
+		return -ENOMEM;
+
+	len = count > tbuf_size ? tbuf_size : count;
+
+	while (count) {
+		cond_resched();
+
+		if (off + len >= vol->usable_leb_size)
+			len = vol->usable_leb_size - off;
+
+		err = copy_from_user(tbuf, buf, len);
+		if (err) {
+			err = -EFAULT;
+			break;
+		}
+
+		err = ubi_eba_write_leb(ubi, vol_id, lnum, tbuf, off, len,
+					UBI_UNKNOWN);
+		if (err)
+			break;
+
+		off += len;
+		if (off == vol->usable_leb_size) {
+			lnum += 1;
+			off -= vol->usable_leb_size;
+		}
+
+		count -= len;
+		*offp += len;
+		buf += len;
+		len = count > tbuf_size ? tbuf_size : count;
+	}
+
+	kfree(tbuf);
+	return err ? err : count_save - count;
+}
+
+#else
+#define vol_cdev_direct_write(file, buf, count, offp) -EPERM
+#endif /* CONFIG_MTD_UBI_DEBUG_USERSPACE_IO */
+
+static ssize_t vol_cdev_write(struct file *file, const char __user *buf,
+			      size_t count, loff_t *offp)
+{
+	int err = 0;
+	struct ubi_volume_desc *desc = file->private_data;
+	struct ubi_volume *vol = desc->vol;
+	struct ubi_device *ubi = vol->ubi;
+
+	if (!vol->updating)
+		return vol_cdev_direct_write(file, buf, count, offp);
+
+	err = ubi_more_update_data(ubi, vol->vol_id, buf, count);
+	if (err < 0) {
+		ubi_err("cannot write %zd bytes of update data", count);
+		return err;
+	}
+
+	if (err) {
+		/*
+		 * Update is finished, @err contains number of actually written
+		 * bytes now.
+		 */
+		count = err;
+
+		err = ubi_check_volume(ubi, vol->vol_id);
+		if (err < 0)
+			return err;
+
+		if (err) {
+			ubi_warn("volume %d on UBI device %d is corrupted",
+				 vol->vol_id, ubi->ubi_num);
+			vol->corrupted = 1;
+		}
+		vol->checked = 1;
+		revoke_exclusive(desc, UBI_READWRITE);
+	}
+
+	*offp += count;
+	return count;
+}
+
+static int vol_cdev_ioctl(struct inode *inode, struct file *file,
+			  unsigned int cmd, unsigned long arg)
+{
+	int err = 0;
+	struct ubi_volume_desc *desc = file->private_data;
+	struct ubi_volume *vol = desc->vol;
+	struct ubi_device *ubi = vol->ubi;
+	void __user *argp = (void __user *)arg;
+
+	if (_IOC_NR(cmd) > VOL_CDEV_IOC_MAX_SEQ ||
+	    _IOC_TYPE(cmd) != UBI_VOL_IOC_MAGIC)
+		return -ENOTTY;
+
+	if (_IOC_DIR(cmd) && _IOC_READ)
+		err = !access_ok(VERIFY_WRITE, argp, _IOC_SIZE(cmd));
+	else if (_IOC_DIR(cmd) && _IOC_WRITE)
+		err = !access_ok(VERIFY_READ, argp, _IOC_SIZE(cmd));
+	if (err)
+		return -EFAULT;
+
+	switch (cmd) {
+
+	/* Volume update command */
+	case UBI_IOCVOLUP:
+	{
+		int64_t bytes, rsvd_bytes;
+
+		if (!capable(CAP_SYS_RESOURCE)) {
+			err = -EPERM;
+			break;
+		}
+
+		err = copy_from_user(&bytes, argp, sizeof(int64_t));
+		if (err) {
+			err = -EFAULT;
+			break;
+		}
+
+		if (desc->mode == UBI_READONLY) {
+			err = -EROFS;
+			break;
+		}
+
+		rsvd_bytes = vol->reserved_pebs * (ubi->leb_size-vol->data_pad);
+		if (bytes < 0 || bytes > rsvd_bytes) {
+			err = -EINVAL;
+			break;
+		}
+
+		err = get_exclusive(desc);
+		if (err < 0)
+			break;
+
+		err = ubi_start_update(ubi, vol->vol_id, bytes);
+		if (bytes == 0)
+			revoke_exclusive(desc, UBI_READWRITE);
+
+		file->f_pos = 0;
+		break;
+	}
+
+#ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO
+	/* Logical eraseblock erasure command */
+	case UBI_IOCEBER:
+	{
+		int32_t lnum;
+
+		err = __get_user(lnum, (__user int32_t *)argp);
+		if (err) {
+			err = -EFAULT;
+			break;
+		}
+
+		if (desc->mode == UBI_READONLY) {
+			err = -EROFS;
+			break;
+		}
+
+		if (lnum < 0 || lnum >= vol->reserved_pebs) {
+			err = -EINVAL;
+			break;
+		}
+
+		if (vol->vol_type != UBI_DYNAMIC_VOLUME) {
+			err = -EROFS;
+			break;
+		}
+
+		dbg_msg("erase LEB %d:%d", vol->vol_id, lnum);
+		err = ubi_eba_unmap_leb(ubi, vol->vol_id, lnum);
+		if (err)
+			break;
+
+		err = ubi_wl_flush(ubi);
+		break;
+	}
+#endif
+
+	default:
+		err = -ENOTTY;
+		break;
+	}
+
+	return err;
+}
+
+/**
+ * verify_mkvol_req - verify volume creation request.
+ * @ubi: UBI device description object
+ * @req: the request to check
+ *
+ * This function zero if the request is correct, and %-EINVAL if not.
+ */
+static int verify_mkvol_req(const struct ubi_device *ubi,
+			    const struct ubi_mkvol_req *req)
+{
+	int n, err = -EINVAL;
+
+	if (req->bytes < 0 || req->alignment < 0 || req->vol_type < 0 ||
+	    req->name_len < 0)
+		goto bad;
+
+	if ((req->vol_id < 0 || req->vol_id >= ubi->vtbl_slots) &&
+	    req->vol_id != UBI_VOL_NUM_AUTO)
+		goto bad;
+
+	if (req->alignment == 0)
+		goto bad;
+
+	if (req->bytes == 0)
+		goto bad;
+
+	if (req->vol_type != UBI_DYNAMIC_VOLUME &&
+	    req->vol_type != UBI_STATIC_VOLUME)
+		goto bad;
+
+	if (req->alignment > ubi->leb_size)
+		goto bad;
+
+	n = req->alignment % ubi->min_io_size;
+	if (req->alignment != 1 && n)
+		goto bad;
+
+	if (req->name_len > UBI_VOL_NAME_MAX) {
+		err = -ENAMETOOLONG;
+		goto bad;
+	}
+
+	return 0;
+
+bad:
+	dbg_err("bad volume creation request");
+	ubi_dbg_dump_mkvol_req(req);
+	return err;
+}
+
+/**
+ * verify_rsvol_req - verify volume re-size request.
+ * @ubi: UBI device description object
+ * @req: the request to check
+ *
+ * This function returns zero if the request is correct, and %-EINVAL if not.
+ */
+static int verify_rsvol_req(const struct ubi_device *ubi,
+			    const struct ubi_rsvol_req *req)
+{
+	if (req->bytes <= 0)
+		return -EINVAL;
+
+	if (req->vol_id < 0 || req->vol_id >= ubi->vtbl_slots)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
+			  unsigned int cmd, unsigned long arg)
+{
+	int err = 0;
+	struct ubi_device *ubi;
+	struct ubi_volume_desc *desc;
+	void __user *argp = (void __user *)arg;
+
+	if (_IOC_NR(cmd) > UBI_CDEV_IOC_MAX_SEQ ||
+	    _IOC_TYPE(cmd) != UBI_IOC_MAGIC)
+		return -ENOTTY;
+
+	if (_IOC_DIR(cmd) && _IOC_READ)
+		err = !access_ok(VERIFY_WRITE, argp, _IOC_SIZE(cmd));
+	else if (_IOC_DIR(cmd) && _IOC_WRITE)
+		err = !access_ok(VERIFY_READ, argp, _IOC_SIZE(cmd));
+	if (err)
+		return -EFAULT;
+
+	if (!capable(CAP_SYS_RESOURCE))
+		return -EPERM;
+
+	ubi = major_to_device(imajor(inode));
+	if (IS_ERR(ubi))
+		return PTR_ERR(ubi);
+
+	switch (cmd) {
+	/* Create volume command */
+	case UBI_IOCMKVOL:
+	{
+		struct ubi_mkvol_req req;
+
+		dbg_msg("create volume");
+		err = __copy_from_user(&req, argp,
+				       sizeof(struct ubi_mkvol_req));
+		if (err) {
+			err = -EFAULT;
+			break;
+		}
+
+		err = verify_mkvol_req(ubi, &req);
+		if (err)
+			break;
+
+		req.name[req.name_len] = '\0';
+
+		err = ubi_create_volume(ubi, &req);
+		if (err)
+			break;
+
+		err = __put_user(req.vol_id, (__user int32_t *)argp);
+		if (err)
+			err = -EFAULT;
+
+		break;
+	}
+
+	/* Remove volume command */
+	case UBI_IOCRMVOL:
+	{
+		int vol_id;
+
+		dbg_msg("remove volume");
+		err = __get_user(vol_id, (__user int32_t *)argp);
+		if (err) {
+			err = -EFAULT;
+			break;
+		}
+
+		desc = ubi_open_volume(ubi->ubi_num, vol_id, UBI_EXCLUSIVE);
+		if (IS_ERR(desc)) {
+			err = PTR_ERR(desc);
+			break;
+		}
+
+		err = ubi_remove_volume(desc);
+		if (err)
+			ubi_close_volume(desc);
+
+		break;
+	}
+
+	/* Re-size volume command */
+	case UBI_IOCRSVOL:
+	{
+		int pebs;
+		uint64_t tmp;
+		struct ubi_rsvol_req req;
+
+		dbg_msg("re-size volume");
+		err = __copy_from_user(&req, argp,
+				       sizeof(struct ubi_rsvol_req));
+		if (err) {
+			err = -EFAULT;
+			break;
+		}
+
+		err = verify_rsvol_req(ubi, &req);
+		if (err)
+			break;
+
+		desc = ubi_open_volume(ubi->ubi_num, req.vol_id, UBI_EXCLUSIVE);
+		if (IS_ERR(desc)) {
+			err = PTR_ERR(desc);
+			break;
+		}
+
+		tmp = req.bytes;
+		pebs = !!do_div(tmp, desc->vol->usable_leb_size);
+		pebs += tmp;
+
+		err = ubi_resize_volume(desc, pebs);
+		ubi_close_volume(desc);
+		break;
+	}
+
+	default:
+		err = -ENOTTY;
+		break;
+	}
+
+	return err;
+}
+
+/* UBI character device operations */
+struct file_operations ubi_cdev_operations = {
+	.owner = THIS_MODULE,
+	.ioctl = ubi_cdev_ioctl,
+	.llseek = no_llseek
+};
+
+/* UBI volume character device operations */
+struct file_operations ubi_vol_cdev_operations = {
+	.owner   = THIS_MODULE,
+	.open    = vol_cdev_open,
+	.release = vol_cdev_release,
+	.llseek  = vol_cdev_llseek,
+	.read    = vol_cdev_read,
+	.write   = vol_cdev_write,
+	.ioctl   = vol_cdev_ioctl
+};
diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c
new file mode 100644
index 0000000..8636422
--- /dev/null
+++ b/drivers/mtd/ubi/debug.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * Here we keep all the UBI debugging stuff which should normally be disabled
+ * and compiled-out, but it is extremely helpful when hunting bugs or doing big
+ * changes.
+ */
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG
+
+#include "ubi.h"
+
+/**
+ * ubi_dbg_dump_ec_hdr - dump an erase counter header.
+ * @ec_hdr: the erase counter header to dump
+ */
+void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr)
+{
+	dbg_msg("erase counter header dump:");
+	dbg_msg("magic          %#08x", ubi32_to_cpu(ec_hdr->magic));
+	dbg_msg("version        %d",    (int)ec_hdr->version);
+	dbg_msg("ec             %llu",  (long long)ubi64_to_cpu(ec_hdr->ec));
+	dbg_msg("vid_hdr_offset %d",    ubi32_to_cpu(ec_hdr->vid_hdr_offset));
+	dbg_msg("data_offset    %d",    ubi32_to_cpu(ec_hdr->data_offset));
+	dbg_msg("hdr_crc        %#08x", ubi32_to_cpu(ec_hdr->hdr_crc));
+	dbg_msg("erase counter header hexdump:");
+	ubi_dbg_hexdump(ec_hdr, UBI_EC_HDR_SIZE);
+}
+
+/**
+ * ubi_dbg_dump_vid_hdr - dump a volume identifier header.
+ * @vid_hdr: the volume identifier header to dump
+ */
+void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr)
+{
+	dbg_msg("volume identifier header dump:");
+	dbg_msg("magic     %08x", ubi32_to_cpu(vid_hdr->magic));
+	dbg_msg("version   %d",   (int)vid_hdr->version);
+	dbg_msg("vol_type  %d",   (int)vid_hdr->vol_type);
+	dbg_msg("copy_flag %d",   (int)vid_hdr->copy_flag);
+	dbg_msg("compat    %d",   (int)vid_hdr->compat);
+	dbg_msg("vol_id    %d",   ubi32_to_cpu(vid_hdr->vol_id));
+	dbg_msg("lnum      %d",   ubi32_to_cpu(vid_hdr->lnum));
+	dbg_msg("leb_ver   %u",   ubi32_to_cpu(vid_hdr->leb_ver));
+	dbg_msg("data_size %d",   ubi32_to_cpu(vid_hdr->data_size));
+	dbg_msg("used_ebs  %d",   ubi32_to_cpu(vid_hdr->used_ebs));
+	dbg_msg("data_pad  %d",   ubi32_to_cpu(vid_hdr->data_pad));
+	dbg_msg("sqnum     %llu",
+		(unsigned long long)ubi64_to_cpu(vid_hdr->sqnum));
+	dbg_msg("hdr_crc   %08x", ubi32_to_cpu(vid_hdr->hdr_crc));
+	dbg_msg("volume identifier header hexdump:");
+}
+
+/**
+ * ubi_dbg_dump_vol_info- dump volume information.
+ * @vol: UBI volume description object
+ */
+void ubi_dbg_dump_vol_info(const struct ubi_volume *vol)
+{
+	dbg_msg("volume information dump:");
+	dbg_msg("vol_id          %d", vol->vol_id);
+	dbg_msg("reserved_pebs   %d", vol->reserved_pebs);
+	dbg_msg("alignment       %d", vol->alignment);
+	dbg_msg("data_pad        %d", vol->data_pad);
+	dbg_msg("vol_type        %d", vol->vol_type);
+	dbg_msg("name_len        %d", vol->name_len);
+	dbg_msg("usable_leb_size %d", vol->usable_leb_size);
+	dbg_msg("used_ebs        %d", vol->used_ebs);
+	dbg_msg("used_bytes      %lld", vol->used_bytes);
+	dbg_msg("last_eb_bytes   %d", vol->last_eb_bytes);
+	dbg_msg("corrupted       %d", vol->corrupted);
+	dbg_msg("upd_marker      %d", vol->upd_marker);
+
+	if (vol->name_len <= UBI_VOL_NAME_MAX &&
+	    strnlen(vol->name, vol->name_len + 1) == vol->name_len) {
+		dbg_msg("name          %s", vol->name);
+	} else {
+		dbg_msg("the 1st 5 characters of the name: %c%c%c%c%c",
+			vol->name[0], vol->name[1], vol->name[2],
+			vol->name[3], vol->name[4]);
+	}
+}
+
+/**
+ * ubi_dbg_dump_vtbl_record - dump a &struct ubi_vtbl_record object.
+ * @r: the object to dump
+ * @idx: volume table index
+ */
+void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx)
+{
+	int name_len = ubi16_to_cpu(r->name_len);
+
+	dbg_msg("volume table record %d dump:", idx);
+	dbg_msg("reserved_pebs   %d", ubi32_to_cpu(r->reserved_pebs));
+	dbg_msg("alignment       %d", ubi32_to_cpu(r->alignment));
+	dbg_msg("data_pad        %d", ubi32_to_cpu(r->data_pad));
+	dbg_msg("vol_type        %d", (int)r->vol_type);
+	dbg_msg("upd_marker      %d", (int)r->upd_marker);
+	dbg_msg("name_len        %d", name_len);
+
+	if (r->name[0] == '\0') {
+		dbg_msg("name          NULL");
+		return;
+	}
+
+	if (name_len <= UBI_VOL_NAME_MAX &&
+	    strnlen(&r->name[0], name_len + 1) == name_len) {
+		dbg_msg("name          %s", &r->name[0]);
+	} else {
+		dbg_msg("1st 5 characters of the name: %c%c%c%c%c",
+			r->name[0], r->name[1], r->name[2], r->name[3],
+			r->name[4]);
+	}
+	dbg_msg("crc             %#08x", ubi32_to_cpu(r->crc));
+}
+
+/**
+ * ubi_dbg_dump_sv - dump a &struct ubi_scan_volume object.
+ * @sv: the object to dump
+ */
+void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv)
+{
+	dbg_msg("volume scanning information dump:");
+	dbg_msg("vol_id         %d", sv->vol_id);
+	dbg_msg("highest_lnum   %d", sv->highest_lnum);
+	dbg_msg("leb_count      %d", sv->leb_count);
+	dbg_msg("compat         %d", sv->compat);
+	dbg_msg("vol_type       %d", sv->vol_type);
+	dbg_msg("used_ebs       %d", sv->used_ebs);
+	dbg_msg("last_data_size %d", sv->last_data_size);
+	dbg_msg("data_pad       %d", sv->data_pad);
+}
+
+/**
+ * ubi_dbg_dump_seb - dump a &struct ubi_scan_leb object.
+ * @seb: the object to dump
+ * @type: object type: 0 - not corrupted, 1 - corrupted
+ */
+void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type)
+{
+	dbg_msg("eraseblock scanning information dump:");
+	dbg_msg("ec       %d", seb->ec);
+	dbg_msg("pnum     %d", seb->pnum);
+	if (type == 0) {
+		dbg_msg("lnum     %d", seb->lnum);
+		dbg_msg("scrub    %d", seb->scrub);
+		dbg_msg("sqnum    %llu", seb->sqnum);
+		dbg_msg("leb_ver  %u", seb->leb_ver);
+	}
+}
+
+/**
+ * ubi_dbg_dump_mkvol_req - dump a &struct ubi_mkvol_req object.
+ * @req: the object to dump
+ */
+void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req)
+{
+	char nm[17];
+
+	dbg_msg("volume creation request dump:");
+	dbg_msg("vol_id    %d",   req->vol_id);
+	dbg_msg("alignment %d",   req->alignment);
+	dbg_msg("bytes     %lld", (long long)req->bytes);
+	dbg_msg("vol_type  %d",   req->vol_type);
+	dbg_msg("name_len  %d",   req->name_len);
+
+	memcpy(nm, req->name, 16);
+	nm[16] = 0;
+	dbg_msg("the 1st 16 characters of the name: %s", nm);
+}
+
+#define BYTES_PER_LINE 32
+
+/**
+ * ubi_dbg_hexdump - dump a buffer.
+ * @ptr: the buffer to dump
+ * @size: buffer size which must be multiple of 4 bytes
+ */
+void ubi_dbg_hexdump(const void *ptr, int size)
+{
+	int i, k = 0, rows, columns;
+	const uint8_t *p = ptr;
+
+	size = ALIGN(size, 4);
+	rows = size/BYTES_PER_LINE + size % BYTES_PER_LINE;
+	for (i = 0; i < rows; i++) {
+		int j;
+
+		cond_resched();
+		columns = min(size - k, BYTES_PER_LINE) / 4;
+		if (columns == 0)
+			break;
+		printk(KERN_DEBUG "%5d:  ", i * BYTES_PER_LINE);
+		for (j = 0; j < columns; j++) {
+			int n, N;
+
+			N = size - k > 4 ? 4 : size - k;
+			for (n = 0; n < N; n++)
+				printk("%02x", p[k++]);
+			printk(" ");
+		}
+		printk("\n");
+	}
+}
+
+#endif /* CONFIG_MTD_UBI_DEBUG_MSG */
diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h
new file mode 100644
index 0000000..f816ad9
--- /dev/null
+++ b/drivers/mtd/ubi/debug.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+#ifndef __UBI_DEBUG_H__
+#define __UBI_DEBUG_H__
+
+#ifdef CONFIG_MTD_UBI_DEBUG
+#include <linux/random.h>
+
+#define ubi_assert(expr)  BUG_ON(!(expr))
+#define dbg_err(fmt, ...) ubi_err(fmt, ##__VA_ARGS__)
+#else
+#define ubi_assert(expr)  ({})
+#define dbg_err(fmt, ...) ({})
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_DISABLE_BGT
+#define DBG_DISABLE_BGT 1
+#else
+#define DBG_DISABLE_BGT 0
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG
+/* Generic debugging message */
+#define dbg_msg(fmt, ...) \
+	printk(KERN_DEBUG "UBI DBG: %s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__)
+
+#define ubi_dbg_dump_stack() dump_stack()
+
+struct ubi_ec_hdr;
+struct ubi_vid_hdr;
+struct ubi_volume;
+struct ubi_vtbl_record;
+struct ubi_scan_volume;
+struct ubi_scan_leb;
+struct ubi_mkvol_req;
+
+void ubi_dbg_print(int type, const char *func, const char *fmt, ...);
+void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr);
+void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr);
+void ubi_dbg_dump_vol_info(const struct ubi_volume *vol);
+void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx);
+void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv);
+void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type);
+void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req);
+void ubi_dbg_hexdump(const void *buf, int size);
+
+#else
+
+#define dbg_msg(fmt, ...)    ({})
+#define ubi_dbg_dump_stack() ({})
+#define ubi_dbg_print(func, fmt, ...)    ({})
+#define ubi_dbg_dump_ec_hdr(ec_hdr)      ({})
+#define ubi_dbg_dump_vid_hdr(vid_hdr)    ({})
+#define ubi_dbg_dump_vol_info(vol)       ({})
+#define ubi_dbg_dump_vtbl_record(r, idx) ({})
+#define ubi_dbg_dump_sv(sv)              ({})
+#define ubi_dbg_dump_seb(seb, type)      ({})
+#define ubi_dbg_dump_mkvol_req(req)      ({})
+#define ubi_dbg_hexdump(buf, size)       ({})
+
+#endif /* CONFIG_MTD_UBI_DEBUG_MSG */
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG_EBA
+/* Messages from the eraseblock association unit */
+#define dbg_eba(fmt, ...) \
+	printk(KERN_DEBUG "UBI DBG eba: %s: " fmt "\n", __FUNCTION__, \
+	       ##__VA_ARGS__)
+#else
+#define dbg_eba(fmt, ...) ({})
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG_WL
+/* Messages from the wear-leveling unit */
+#define dbg_wl(fmt, ...) \
+	printk(KERN_DEBUG "UBI DBG wl: %s: " fmt "\n", __FUNCTION__, \
+	       ##__VA_ARGS__)
+#else
+#define dbg_wl(fmt, ...) ({})
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG_IO
+/* Messages from the input/output unit */
+#define dbg_io(fmt, ...) \
+	printk(KERN_DEBUG "UBI DBG io: %s: " fmt "\n", __FUNCTION__, \
+	       ##__VA_ARGS__)
+#else
+#define dbg_io(fmt, ...) ({})
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG_BLD
+/* Initialization and build messages */
+#define dbg_bld(fmt, ...) \
+	printk(KERN_DEBUG "UBI DBG bld: %s: " fmt "\n", __FUNCTION__, \
+	       ##__VA_ARGS__)
+#else
+#define dbg_bld(fmt, ...) ({})
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS
+/**
+ * ubi_dbg_is_bitflip - if it is time to emulate a bit-flip.
+ *
+ * Returns non-zero if a bit-flip should be emulated, otherwise returns zero.
+ */
+static inline int ubi_dbg_is_bitflip(void)
+{
+	return !(random32() % 200);
+}
+#else
+#define ubi_dbg_is_bitflip() 0
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_WRITE_FAILURES
+/**
+ * ubi_dbg_is_write_failure - if it is time to emulate a write failure.
+ *
+ * Returns non-zero if a write failure should be emulated, otherwise returns
+ * zero.
+ */
+static inline int ubi_dbg_is_write_failure(void)
+{
+	return !(random32() % 500);
+}
+#else
+#define ubi_dbg_is_write_failure() 0
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_ERASE_FAILURES
+/**
+ * ubi_dbg_is_erase_failure - if its time to emulate an erase failure.
+ *
+ * Returns non-zero if an erase failure should be emulated, otherwise returns
+ * zero.
+ */
+static inline int ubi_dbg_is_erase_failure(void)
+{
+		return !(random32() % 400);
+}
+#else
+#define ubi_dbg_is_erase_failure() 0
+#endif
+
+#endif /* !__UBI_DEBUG_H__ */
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c
new file mode 100644
index 0000000..d847ee1
--- /dev/null
+++ b/drivers/mtd/ubi/eba.c
@@ -0,0 +1,1241 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * The UBI Eraseblock Association (EBA) unit.
+ *
+ * This unit is responsible for I/O to/from logical eraseblock.
+ *
+ * Although in this implementation the EBA table is fully kept and managed in
+ * RAM, which assumes poor scalability, it might be (partially) maintained on
+ * flash in future implementations.
+ *
+ * The EBA unit implements per-logical eraseblock locking. Before accessing a
+ * logical eraseblock it is locked for reading or writing. The per-logical
+ * eraseblock locking is implemented by means of the lock tree. The lock tree
+ * is an RB-tree which refers all the currently locked logical eraseblocks. The
+ * lock tree elements are &struct ltree_entry objects. They are indexed by
+ * (@vol_id, @lnum) pairs.
+ *
+ * EBA also maintains the global sequence counter which is incremented each
+ * time a logical eraseblock is mapped to a physical eraseblock and it is
+ * stored in the volume identifier header. This means that each VID header has
+ * a unique sequence number. The sequence number is only increased an we assume
+ * 64 bits is enough to never overflow.
+ */
+
+#include <linux/slab.h>
+#include <linux/crc32.h>
+#include <linux/err.h>
+#include "ubi.h"
+
+/**
+ * struct ltree_entry - an entry in the lock tree.
+ * @rb: links RB-tree nodes
+ * @vol_id: volume ID of the locked logical eraseblock
+ * @lnum: locked logical eraseblock number
+ * @users: how many tasks are using this logical eraseblock or wait for it
+ * @mutex: read/write mutex to implement read/write access serialization to
+ * the (@vol_id, @lnum) logical eraseblock
+ *
+ * When a logical eraseblock is being locked - corresponding &struct ltree_entry
+ * object is inserted to the lock tree (@ubi->ltree).
+ */
+struct ltree_entry {
+	struct rb_node rb;
+	int vol_id;
+	int lnum;
+	int users;
+	struct rw_semaphore mutex;
+};
+
+/* Slab cache for lock-tree entries */
+static struct kmem_cache *ltree_slab;
+
+/**
+ * next_sqnum - get next sequence number.
+ * @ubi: UBI device description object
+ *
+ * This function returns next sequence number to use, which is just the current
+ * global sequence counter value. It also increases the global sequence
+ * counter.
+ */
+static unsigned long long next_sqnum(struct ubi_device *ubi)
+{
+	unsigned long long sqnum;
+
+	spin_lock(&ubi->ltree_lock);
+	sqnum = ubi->global_sqnum++;
+	spin_unlock(&ubi->ltree_lock);
+
+	return sqnum;
+}
+
+/**
+ * ubi_get_compat - get compatibility flags of a volume.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ *
+ * This function returns compatibility flags for an internal volume. User
+ * volumes have no compatibility flags, so %0 is returned.
+ */
+static int ubi_get_compat(const struct ubi_device *ubi, int vol_id)
+{
+	if (vol_id == UBI_LAYOUT_VOL_ID)
+		return UBI_LAYOUT_VOLUME_COMPAT;
+	return 0;
+}
+
+/**
+ * ltree_lookup - look up the lock tree.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ *
+ * This function returns a pointer to the corresponding &struct ltree_entry
+ * object if the logical eraseblock is locked and %NULL if it is not.
+ * @ubi->ltree_lock has to be locked.
+ */
+static struct ltree_entry *ltree_lookup(struct ubi_device *ubi, int vol_id,
+					int lnum)
+{
+	struct rb_node *p;
+
+	p = ubi->ltree.rb_node;
+	while (p) {
+		struct ltree_entry *le;
+
+		le = rb_entry(p, struct ltree_entry, rb);
+
+		if (vol_id < le->vol_id)
+			p = p->rb_left;
+		else if (vol_id > le->vol_id)
+			p = p->rb_right;
+		else {
+			if (lnum < le->lnum)
+				p = p->rb_left;
+			else if (lnum > le->lnum)
+				p = p->rb_right;
+			else
+				return le;
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * ltree_add_entry - add new entry to the lock tree.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ *
+ * This function adds new entry for logical eraseblock (@vol_id, @lnum) to the
+ * lock tree. If such entry is already there, its usage counter is increased.
+ * Returns pointer to the lock tree entry or %-ENOMEM if memory allocation
+ * failed.
+ */
+static struct ltree_entry *ltree_add_entry(struct ubi_device *ubi, int vol_id,
+					   int lnum)
+{
+	struct ltree_entry *le, *le1, *le_free;
+
+	le = kmem_cache_alloc(ltree_slab, GFP_KERNEL);
+	if (!le)
+		return ERR_PTR(-ENOMEM);
+
+	le->vol_id = vol_id;
+	le->lnum = lnum;
+
+	spin_lock(&ubi->ltree_lock);
+	le1 = ltree_lookup(ubi, vol_id, lnum);
+
+	if (le1) {
+		/*
+		 * This logical eraseblock is already locked. The newly
+		 * allocated lock entry is not needed.
+		 */
+		le_free = le;
+		le = le1;
+	} else {
+		struct rb_node **p, *parent = NULL;
+
+		/*
+		 * No lock entry, add the newly allocated one to the
+		 * @ubi->ltree RB-tree.
+		 */
+		le_free = NULL;
+
+		p = &ubi->ltree.rb_node;
+		while (*p) {
+			parent = *p;
+			le1 = rb_entry(parent, struct ltree_entry, rb);
+
+			if (vol_id < le1->vol_id)
+				p = &(*p)->rb_left;
+			else if (vol_id > le1->vol_id)
+				p = &(*p)->rb_right;
+			else {
+				ubi_assert(lnum != le1->lnum);
+				if (lnum < le1->lnum)
+					p = &(*p)->rb_left;
+				else
+					p = &(*p)->rb_right;
+			}
+		}
+
+		rb_link_node(&le->rb, parent, p);
+		rb_insert_color(&le->rb, &ubi->ltree);
+	}
+	le->users += 1;
+	spin_unlock(&ubi->ltree_lock);
+
+	if (le_free)
+		kmem_cache_free(ltree_slab, le_free);
+
+	return le;
+}
+
+/**
+ * leb_read_lock - lock logical eraseblock for reading.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ *
+ * This function locks a logical eraseblock for reading. Returns zero in case
+ * of success and a negative error code in case of failure.
+ */
+static int leb_read_lock(struct ubi_device *ubi, int vol_id, int lnum)
+{
+	struct ltree_entry *le;
+
+	le = ltree_add_entry(ubi, vol_id, lnum);
+	if (IS_ERR(le))
+		return PTR_ERR(le);
+	down_read(&le->mutex);
+	return 0;
+}
+
+/**
+ * leb_read_unlock - unlock logical eraseblock.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ */
+static void leb_read_unlock(struct ubi_device *ubi, int vol_id, int lnum)
+{
+	int free = 0;
+	struct ltree_entry *le;
+
+	spin_lock(&ubi->ltree_lock);
+	le = ltree_lookup(ubi, vol_id, lnum);
+	le->users -= 1;
+	ubi_assert(le->users >= 0);
+	if (le->users == 0) {
+		rb_erase(&le->rb, &ubi->ltree);
+		free = 1;
+	}
+	spin_unlock(&ubi->ltree_lock);
+
+	up_read(&le->mutex);
+	if (free)
+		kmem_cache_free(ltree_slab, le);
+}
+
+/**
+ * leb_write_lock - lock logical eraseblock for writing.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ *
+ * This function locks a logical eraseblock for writing. Returns zero in case
+ * of success and a negative error code in case of failure.
+ */
+static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum)
+{
+	struct ltree_entry *le;
+
+	le = ltree_add_entry(ubi, vol_id, lnum);
+	if (IS_ERR(le))
+		return PTR_ERR(le);
+	down_write(&le->mutex);
+	return 0;
+}
+
+/**
+ * leb_write_unlock - unlock logical eraseblock.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ */
+static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum)
+{
+	int free;
+	struct ltree_entry *le;
+
+	spin_lock(&ubi->ltree_lock);
+	le = ltree_lookup(ubi, vol_id, lnum);
+	le->users -= 1;
+	ubi_assert(le->users >= 0);
+	if (le->users == 0) {
+		rb_erase(&le->rb, &ubi->ltree);
+		free = 1;
+	} else
+		free = 0;
+	spin_unlock(&ubi->ltree_lock);
+
+	up_write(&le->mutex);
+	if (free)
+		kmem_cache_free(ltree_slab, le);
+}
+
+/**
+ * ubi_eba_unmap_leb - un-map logical eraseblock.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ *
+ * This function un-maps logical eraseblock @lnum and schedules corresponding
+ * physical eraseblock for erasure. Returns zero in case of success and a
+ * negative error code in case of failure.
+ */
+int ubi_eba_unmap_leb(struct ubi_device *ubi, int vol_id, int lnum)
+{
+	int idx = vol_id2idx(ubi, vol_id), err, pnum;
+	struct ubi_volume *vol = ubi->volumes[idx];
+
+	if (ubi->ro_mode)
+		return -EROFS;
+
+	err = leb_write_lock(ubi, vol_id, lnum);
+	if (err)
+		return err;
+
+	pnum = vol->eba_tbl[lnum];
+	if (pnum < 0)
+		/* This logical eraseblock is already unmapped */
+		goto out_unlock;
+
+	dbg_eba("erase LEB %d:%d, PEB %d", vol_id, lnum, pnum);
+
+	vol->eba_tbl[lnum] = UBI_LEB_UNMAPPED;
+	err = ubi_wl_put_peb(ubi, pnum, 0);
+
+out_unlock:
+	leb_write_unlock(ubi, vol_id, lnum);
+	return err;
+}
+
+/**
+ * ubi_eba_read_leb - read data.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ * @buf: buffer to store the read data
+ * @offset: offset from where to read
+ * @len: how many bytes to read
+ * @check: data CRC check flag
+ *
+ * If the logical eraseblock @lnum is unmapped, @buf is filled with 0xFF
+ * bytes. The @check flag only makes sense for static volumes and forces
+ * eraseblock data CRC checking.
+ *
+ * In case of success this function returns zero. In case of a static volume,
+ * if data CRC mismatches - %-EBADMSG is returned. %-EBADMSG may also be
+ * returned for any volume type if an ECC error was detected by the MTD device
+ * driver. Other negative error cored may be returned in case of other errors.
+ */
+int ubi_eba_read_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf,
+		     int offset, int len, int check)
+{
+	int err, pnum, scrub = 0, idx = vol_id2idx(ubi, vol_id);
+	struct ubi_vid_hdr *vid_hdr;
+	struct ubi_volume *vol = ubi->volumes[idx];
+	uint32_t crc, crc1;
+
+	err = leb_read_lock(ubi, vol_id, lnum);
+	if (err)
+		return err;
+
+	pnum = vol->eba_tbl[lnum];
+	if (pnum < 0) {
+		/*
+		 * The logical eraseblock is not mapped, fill the whole buffer
+		 * with 0xFF bytes. The exception is static volumes for which
+		 * it is an error to read unmapped logical eraseblocks.
+		 */
+		dbg_eba("read %d bytes from offset %d of LEB %d:%d (unmapped)",
+			len, offset, vol_id, lnum);
+		leb_read_unlock(ubi, vol_id, lnum);
+		ubi_assert(vol->vol_type != UBI_STATIC_VOLUME);
+		memset(buf, 0xFF, len);
+		return 0;
+	}
+
+	dbg_eba("read %d bytes from offset %d of LEB %d:%d, PEB %d",
+		len, offset, vol_id, lnum, pnum);
+
+	if (vol->vol_type == UBI_DYNAMIC_VOLUME)
+		check = 0;
+
+retry:
+	if (check) {
+		vid_hdr = ubi_zalloc_vid_hdr(ubi);
+		if (!vid_hdr) {
+			err = -ENOMEM;
+			goto out_unlock;
+		}
+
+		err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1);
+		if (err && err != UBI_IO_BITFLIPS) {
+			if (err > 0) {
+				/*
+				 * The header is either absent or corrupted.
+				 * The former case means there is a bug -
+				 * switch to read-only mode just in case.
+				 * The latter case means a real corruption - we
+				 * may try to recover data. FIXME: but this is
+				 * not implemented.
+				 */
+				if (err == UBI_IO_BAD_VID_HDR) {
+					ubi_warn("bad VID header at PEB %d, LEB"
+						 "%d:%d", pnum, vol_id, lnum);
+					err = -EBADMSG;
+				} else
+					ubi_ro_mode(ubi);
+			}
+			goto out_free;
+		} else if (err == UBI_IO_BITFLIPS)
+			scrub = 1;
+
+		ubi_assert(lnum < ubi32_to_cpu(vid_hdr->used_ebs));
+		ubi_assert(len == ubi32_to_cpu(vid_hdr->data_size));
+
+		crc = ubi32_to_cpu(vid_hdr->data_crc);
+		ubi_free_vid_hdr(ubi, vid_hdr);
+	}
+
+	err = ubi_io_read_data(ubi, buf, pnum, offset, len);
+	if (err) {
+		if (err == UBI_IO_BITFLIPS) {
+			scrub = 1;
+			err = 0;
+		} else if (err == -EBADMSG) {
+			if (vol->vol_type == UBI_DYNAMIC_VOLUME)
+				goto out_unlock;
+			scrub = 1;
+			if (!check) {
+				ubi_msg("force data checking");
+				check = 1;
+				goto retry;
+			}
+		} else
+			goto out_unlock;
+	}
+
+	if (check) {
+		crc1 = crc32(UBI_CRC32_INIT, buf, len);
+		if (crc1 != crc) {
+			ubi_warn("CRC error: calculated %#08x, must be %#08x",
+				 crc1, crc);
+			err = -EBADMSG;
+			goto out_unlock;
+		}
+	}
+
+	if (scrub)
+		err = ubi_wl_scrub_peb(ubi, pnum);
+
+	leb_read_unlock(ubi, vol_id, lnum);
+	return err;
+
+out_free:
+	ubi_free_vid_hdr(ubi, vid_hdr);
+out_unlock:
+	leb_read_unlock(ubi, vol_id, lnum);
+	return err;
+}
+
+/**
+ * recover_peb - recover from write failure.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock to recover
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ * @buf: data which was not written because of the write failure
+ * @offset: offset of the failed write
+ * @len: how many bytes should have been written
+ *
+ * This function is called in case of a write failure and moves all good data
+ * from the potentially bad physical eraseblock to a good physical eraseblock.
+ * This function also writes the data which was not written due to the failure.
+ * Returns new physical eraseblock number in case of success, and a negative
+ * error code in case of failure.
+ */
+static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum,
+		       const void *buf, int offset, int len)
+{
+	int err, idx = vol_id2idx(ubi, vol_id), new_pnum, data_size, tries = 0;
+	struct ubi_volume *vol = ubi->volumes[idx];
+	struct ubi_vid_hdr *vid_hdr;
+	unsigned char *new_buf;
+
+	vid_hdr = ubi_zalloc_vid_hdr(ubi);
+	if (!vid_hdr) {
+		return -ENOMEM;
+	}
+
+retry:
+	new_pnum = ubi_wl_get_peb(ubi, UBI_UNKNOWN);
+	if (new_pnum < 0) {
+		ubi_free_vid_hdr(ubi, vid_hdr);
+		return new_pnum;
+	}
+
+	ubi_msg("recover PEB %d, move data to PEB %d", pnum, new_pnum);
+
+	err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1);
+	if (err && err != UBI_IO_BITFLIPS) {
+		if (err > 0)
+			err = -EIO;
+		goto out_put;
+	}
+
+	vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi));
+	err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr);
+	if (err)
+		goto write_error;
+
+	data_size = offset + len;
+	new_buf = kmalloc(data_size, GFP_KERNEL);
+	if (!new_buf) {
+		err = -ENOMEM;
+		goto out_put;
+	}
+	memset(new_buf + offset, 0xFF, len);
+
+	/* Read everything before the area where the write failure happened */
+	if (offset > 0) {
+		err = ubi_io_read_data(ubi, new_buf, pnum, 0, offset);
+		if (err && err != UBI_IO_BITFLIPS) {
+			kfree(new_buf);
+			goto out_put;
+		}
+	}
+
+	memcpy(new_buf + offset, buf, len);
+
+	err = ubi_io_write_data(ubi, new_buf, new_pnum, 0, data_size);
+	if (err) {
+		kfree(new_buf);
+		goto write_error;
+	}
+
+	kfree(new_buf);
+	ubi_free_vid_hdr(ubi, vid_hdr);
+
+	vol->eba_tbl[lnum] = new_pnum;
+	ubi_wl_put_peb(ubi, pnum, 1);
+
+	ubi_msg("data was successfully recovered");
+	return 0;
+
+out_put:
+	ubi_wl_put_peb(ubi, new_pnum, 1);
+	ubi_free_vid_hdr(ubi, vid_hdr);
+	return err;
+
+write_error:
+	/*
+	 * Bad luck? This physical eraseblock is bad too? Crud. Let's try to
+	 * get another one.
+	 */
+	ubi_warn("failed to write to PEB %d", new_pnum);
+	ubi_wl_put_peb(ubi, new_pnum, 1);
+	if (++tries > UBI_IO_RETRIES) {
+		ubi_free_vid_hdr(ubi, vid_hdr);
+		return err;
+	}
+	ubi_msg("try again");
+	goto retry;
+}
+
+/**
+ * ubi_eba_write_leb - write data to dynamic volume.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ * @buf: the data to write
+ * @offset: offset within the logical eraseblock where to write
+ * @len: how many bytes to write
+ * @dtype: data type
+ *
+ * This function writes data to logical eraseblock @lnum of a dynamic volume
+ * @vol_id. Returns zero in case of success and a negative error code in case
+ * of failure. In case of error, it is possible that something was still
+ * written to the flash media, but may be some garbage.
+ */
+int ubi_eba_write_leb(struct ubi_device *ubi, int vol_id, int lnum,
+		      const void *buf, int offset, int len, int dtype)
+{
+	int idx = vol_id2idx(ubi, vol_id), err, pnum, tries = 0;
+	struct ubi_volume *vol = ubi->volumes[idx];
+	struct ubi_vid_hdr *vid_hdr;
+
+	if (ubi->ro_mode)
+		return -EROFS;
+
+	err = leb_write_lock(ubi, vol_id, lnum);
+	if (err)
+		return err;
+
+	pnum = vol->eba_tbl[lnum];
+	if (pnum >= 0) {
+		dbg_eba("write %d bytes at offset %d of LEB %d:%d, PEB %d",
+			len, offset, vol_id, lnum, pnum);
+
+		err = ubi_io_write_data(ubi, buf, pnum, offset, len);
+		if (err) {
+			ubi_warn("failed to write data to PEB %d", pnum);
+			if (err == -EIO && ubi->bad_allowed)
+				err = recover_peb(ubi, pnum, vol_id, lnum, buf, offset, len);
+			if (err)
+				ubi_ro_mode(ubi);
+		}
+		leb_write_unlock(ubi, vol_id, lnum);
+		return err;
+	}
+
+	/*
+	 * The logical eraseblock is not mapped. We have to get a free physical
+	 * eraseblock and write the volume identifier header there first.
+	 */
+	vid_hdr = ubi_zalloc_vid_hdr(ubi);
+	if (!vid_hdr) {
+		leb_write_unlock(ubi, vol_id, lnum);
+		return -ENOMEM;
+	}
+
+	vid_hdr->vol_type = UBI_VID_DYNAMIC;
+	vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi));
+	vid_hdr->vol_id = cpu_to_ubi32(vol_id);
+	vid_hdr->lnum = cpu_to_ubi32(lnum);
+	vid_hdr->compat = ubi_get_compat(ubi, vol_id);
+	vid_hdr->data_pad = cpu_to_ubi32(vol->data_pad);
+
+retry:
+	pnum = ubi_wl_get_peb(ubi, dtype);
+	if (pnum < 0) {
+		ubi_free_vid_hdr(ubi, vid_hdr);
+		leb_write_unlock(ubi, vol_id, lnum);
+		return pnum;
+	}
+
+	dbg_eba("write VID hdr and %d bytes at offset %d of LEB %d:%d, PEB %d",
+		len, offset, vol_id, lnum, pnum);
+
+	err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr);
+	if (err) {
+		ubi_warn("failed to write VID header to LEB %d:%d, PEB %d",
+			 vol_id, lnum, pnum);
+		goto write_error;
+	}
+
+	err = ubi_io_write_data(ubi, buf, pnum, offset, len);
+	if (err) {
+		ubi_warn("failed to write %d bytes at offset %d of LEB %d:%d, "
+			 "PEB %d", len, offset, vol_id, lnum, pnum);
+		goto write_error;
+	}
+
+	vol->eba_tbl[lnum] = pnum;
+
+	leb_write_unlock(ubi, vol_id, lnum);
+	ubi_free_vid_hdr(ubi, vid_hdr);
+	return 0;
+
+write_error:
+	if (err != -EIO || !ubi->bad_allowed) {
+		ubi_ro_mode(ubi);
+		leb_write_unlock(ubi, vol_id, lnum);
+		ubi_free_vid_hdr(ubi, vid_hdr);
+		return err;
+	}
+
+	/*
+	 * Fortunately, this is the first write operation to this physical
+	 * eraseblock, so just put it and request a new one. We assume that if
+	 * this physical eraseblock went bad, the erase code will handle that.
+	 */
+	err = ubi_wl_put_peb(ubi, pnum, 1);
+	if (err || ++tries > UBI_IO_RETRIES) {
+		ubi_ro_mode(ubi);
+		leb_write_unlock(ubi, vol_id, lnum);
+		ubi_free_vid_hdr(ubi, vid_hdr);
+		return err;
+	}
+
+	vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi));
+	ubi_msg("try another PEB");
+	goto retry;
+}
+
+/**
+ * ubi_eba_write_leb_st - write data to static volume.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ * @buf: data to write
+ * @len: how many bytes to write
+ * @dtype: data type
+ * @used_ebs: how many logical eraseblocks will this volume contain
+ *
+ * This function writes data to logical eraseblock @lnum of static volume
+ * @vol_id. The @used_ebs argument should contain total number of logical
+ * eraseblock in this static volume.
+ *
+ * When writing to the last logical eraseblock, the @len argument doesn't have
+ * to be aligned to the minimal I/O unit size. Instead, it has to be equivalent
+ * to the real data size, although the @buf buffer has to contain the
+ * alignment. In all other cases, @len has to be aligned.
+ *
+ * It is prohibited to write more then once to logical eraseblocks of static
+ * volumes. This function returns zero in case of success and a negative error
+ * code in case of failure.
+ */
+int ubi_eba_write_leb_st(struct ubi_device *ubi, int vol_id, int lnum,
+			 const void *buf, int len, int dtype, int used_ebs)
+{
+	int err, pnum, tries = 0, data_size = len;
+	int idx = vol_id2idx(ubi, vol_id);
+	struct ubi_volume *vol = ubi->volumes[idx];
+	struct ubi_vid_hdr *vid_hdr;
+	uint32_t crc;
+
+	if (ubi->ro_mode)
+		return -EROFS;
+
+	if (lnum == used_ebs - 1)
+		/* If this is the last LEB @len may be unaligned */
+		len = ALIGN(data_size, ubi->min_io_size);
+	else
+		ubi_assert(len % ubi->min_io_size == 0);
+
+	vid_hdr = ubi_zalloc_vid_hdr(ubi);
+	if (!vid_hdr)
+		return -ENOMEM;
+
+	err = leb_write_lock(ubi, vol_id, lnum);
+	if (err) {
+		ubi_free_vid_hdr(ubi, vid_hdr);
+		return err;
+	}
+
+	vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi));
+	vid_hdr->vol_id = cpu_to_ubi32(vol_id);
+	vid_hdr->lnum = cpu_to_ubi32(lnum);
+	vid_hdr->compat = ubi_get_compat(ubi, vol_id);
+	vid_hdr->data_pad = cpu_to_ubi32(vol->data_pad);
+
+	crc = crc32(UBI_CRC32_INIT, buf, data_size);
+	vid_hdr->vol_type = UBI_VID_STATIC;
+	vid_hdr->data_size = cpu_to_ubi32(data_size);
+	vid_hdr->used_ebs = cpu_to_ubi32(used_ebs);
+	vid_hdr->data_crc = cpu_to_ubi32(crc);
+
+retry:
+	pnum = ubi_wl_get_peb(ubi, dtype);
+	if (pnum < 0) {
+		ubi_free_vid_hdr(ubi, vid_hdr);
+		leb_write_unlock(ubi, vol_id, lnum);
+		return pnum;
+	}
+
+	dbg_eba("write VID hdr and %d bytes at LEB %d:%d, PEB %d, used_ebs %d",
+		len, vol_id, lnum, pnum, used_ebs);
+
+	err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr);
+	if (err) {
+		ubi_warn("failed to write VID header to LEB %d:%d, PEB %d",
+			 vol_id, lnum, pnum);
+		goto write_error;
+	}
+
+	err = ubi_io_write_data(ubi, buf, pnum, 0, len);
+	if (err) {
+		ubi_warn("failed to write %d bytes of data to PEB %d",
+			 len, pnum);
+		goto write_error;
+	}
+
+	ubi_assert(vol->eba_tbl[lnum] < 0);
+	vol->eba_tbl[lnum] = pnum;
+
+	leb_write_unlock(ubi, vol_id, lnum);
+	ubi_free_vid_hdr(ubi, vid_hdr);
+	return 0;
+
+write_error:
+	if (err != -EIO || !ubi->bad_allowed) {
+		/*
+		 * This flash device does not admit of bad eraseblocks or
+		 * something nasty and unexpected happened. Switch to read-only
+		 * mode just in case.
+		 */
+		ubi_ro_mode(ubi);
+		leb_write_unlock(ubi, vol_id, lnum);
+		ubi_free_vid_hdr(ubi, vid_hdr);
+		return err;
+	}
+
+	err = ubi_wl_put_peb(ubi, pnum, 1);
+	if (err || ++tries > UBI_IO_RETRIES) {
+		ubi_ro_mode(ubi);
+		leb_write_unlock(ubi, vol_id, lnum);
+		ubi_free_vid_hdr(ubi, vid_hdr);
+		return err;
+	}
+
+	vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi));
+	ubi_msg("try another PEB");
+	goto retry;
+}
+
+/*
+ * ubi_eba_atomic_leb_change - change logical eraseblock atomically.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ * @buf: data to write
+ * @len: how many bytes to write
+ * @dtype: data type
+ *
+ * This function changes the contents of a logical eraseblock atomically. @buf
+ * has to contain new logical eraseblock data, and @len - the length of the
+ * data, which has to be aligned. This function guarantees that in case of an
+ * unclean reboot the old contents is preserved. Returns zero in case of
+ * success and a negative error code in case of failure.
+ */
+int ubi_eba_atomic_leb_change(struct ubi_device *ubi, int vol_id, int lnum,
+			      const void *buf, int len, int dtype)
+{
+	int err, pnum, tries = 0, idx = vol_id2idx(ubi, vol_id);
+	struct ubi_volume *vol = ubi->volumes[idx];
+	struct ubi_vid_hdr *vid_hdr;
+	uint32_t crc;
+
+	if (ubi->ro_mode)
+		return -EROFS;
+
+	vid_hdr = ubi_zalloc_vid_hdr(ubi);
+	if (!vid_hdr)
+		return -ENOMEM;
+
+	err = leb_write_lock(ubi, vol_id, lnum);
+	if (err) {
+		ubi_free_vid_hdr(ubi, vid_hdr);
+		return err;
+	}
+
+	vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi));
+	vid_hdr->vol_id = cpu_to_ubi32(vol_id);
+	vid_hdr->lnum = cpu_to_ubi32(lnum);
+	vid_hdr->compat = ubi_get_compat(ubi, vol_id);
+	vid_hdr->data_pad = cpu_to_ubi32(vol->data_pad);
+
+	crc = crc32(UBI_CRC32_INIT, buf, len);
+	vid_hdr->vol_type = UBI_VID_STATIC;
+	vid_hdr->data_size = cpu_to_ubi32(len);
+	vid_hdr->copy_flag = 1;
+	vid_hdr->data_crc = cpu_to_ubi32(crc);
+
+retry:
+	pnum = ubi_wl_get_peb(ubi, dtype);
+	if (pnum < 0) {
+		ubi_free_vid_hdr(ubi, vid_hdr);
+		leb_write_unlock(ubi, vol_id, lnum);
+		return pnum;
+	}
+
+	dbg_eba("change LEB %d:%d, PEB %d, write VID hdr to PEB %d",
+		vol_id, lnum, vol->eba_tbl[lnum], pnum);
+
+	err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr);
+	if (err) {
+		ubi_warn("failed to write VID header to LEB %d:%d, PEB %d",
+			 vol_id, lnum, pnum);
+		goto write_error;
+	}
+
+	err = ubi_io_write_data(ubi, buf, pnum, 0, len);
+	if (err) {
+		ubi_warn("failed to write %d bytes of data to PEB %d",
+			 len, pnum);
+		goto write_error;
+	}
+
+	err = ubi_wl_put_peb(ubi, vol->eba_tbl[lnum], 1);
+	if (err) {
+		ubi_free_vid_hdr(ubi, vid_hdr);
+		leb_write_unlock(ubi, vol_id, lnum);
+		return err;
+	}
+
+	vol->eba_tbl[lnum] = pnum;
+	leb_write_unlock(ubi, vol_id, lnum);
+	ubi_free_vid_hdr(ubi, vid_hdr);
+	return 0;
+
+write_error:
+	if (err != -EIO || !ubi->bad_allowed) {
+		/*
+		 * This flash device does not admit of bad eraseblocks or
+		 * something nasty and unexpected happened. Switch to read-only
+		 * mode just in case.
+		 */
+		ubi_ro_mode(ubi);
+		leb_write_unlock(ubi, vol_id, lnum);
+		ubi_free_vid_hdr(ubi, vid_hdr);
+		return err;
+	}
+
+	err = ubi_wl_put_peb(ubi, pnum, 1);
+	if (err || ++tries > UBI_IO_RETRIES) {
+		ubi_ro_mode(ubi);
+		leb_write_unlock(ubi, vol_id, lnum);
+		ubi_free_vid_hdr(ubi, vid_hdr);
+		return err;
+	}
+
+	vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi));
+	ubi_msg("try another PEB");
+	goto retry;
+}
+
+/**
+ * ltree_entry_ctor - lock tree entries slab cache constructor.
+ * @obj: the lock-tree entry to construct
+ * @cache: the lock tree entry slab cache
+ * @flags: constructor flags
+ */
+static void ltree_entry_ctor(void *obj, struct kmem_cache *cache,
+			     unsigned long flags)
+{
+	struct ltree_entry *le = obj;
+
+	if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) !=
+	    SLAB_CTOR_CONSTRUCTOR)
+		return;
+
+	le->users = 0;
+	init_rwsem(&le->mutex);
+}
+
+/**
+ * ubi_eba_copy_leb - copy logical eraseblock.
+ * @ubi: UBI device description object
+ * @from: physical eraseblock number from where to copy
+ * @to: physical eraseblock number where to copy
+ * @vid_hdr: VID header of the @from physical eraseblock
+ *
+ * This function copies logical eraseblock from physical eraseblock @from to
+ * physical eraseblock @to. The @vid_hdr buffer may be changed by this
+ * function. Returns zero in case of success, %UBI_IO_BITFLIPS if the operation
+ * was canceled because bit-flips were detected at the target PEB, and a
+ * negative error code in case of failure.
+ */
+int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
+		     struct ubi_vid_hdr *vid_hdr)
+{
+	int err, vol_id, lnum, data_size, aldata_size, pnum, idx;
+	struct ubi_volume *vol;
+	uint32_t crc;
+	void *buf, *buf1 = NULL;
+
+	vol_id = ubi32_to_cpu(vid_hdr->vol_id);
+	lnum = ubi32_to_cpu(vid_hdr->lnum);
+
+	dbg_eba("copy LEB %d:%d, PEB %d to PEB %d", vol_id, lnum, from, to);
+
+	if (vid_hdr->vol_type == UBI_VID_STATIC) {
+		data_size = ubi32_to_cpu(vid_hdr->data_size);
+		aldata_size = ALIGN(data_size, ubi->min_io_size);
+	} else
+		data_size = aldata_size =
+			    ubi->leb_size - ubi32_to_cpu(vid_hdr->data_pad);
+
+	buf = kmalloc(aldata_size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	/*
+	 * We do not want anybody to write to this logical eraseblock while we
+	 * are moving it, so we lock it.
+	 */
+	err = leb_write_lock(ubi, vol_id, lnum);
+	if (err) {
+		kfree(buf);
+		return err;
+	}
+
+	/*
+	 * But the logical eraseblock might have been put by this time.
+	 * Cancel if it is true.
+	 */
+	idx = vol_id2idx(ubi, vol_id);
+
+	/*
+	 * We may race with volume deletion/re-size, so we have to hold
+	 * @ubi->volumes_lock.
+	 */
+	spin_lock(&ubi->volumes_lock);
+	vol = ubi->volumes[idx];
+	if (!vol) {
+		dbg_eba("volume %d was removed meanwhile", vol_id);
+		spin_unlock(&ubi->volumes_lock);
+		goto out_unlock;
+	}
+
+	pnum = vol->eba_tbl[lnum];
+	if (pnum != from) {
+		dbg_eba("LEB %d:%d is no longer mapped to PEB %d, mapped to "
+			"PEB %d, cancel", vol_id, lnum, from, pnum);
+		spin_unlock(&ubi->volumes_lock);
+		goto out_unlock;
+	}
+	spin_unlock(&ubi->volumes_lock);
+
+	/* OK, now the LEB is locked and we can safely start moving it */
+
+	dbg_eba("read %d bytes of data", aldata_size);
+	err = ubi_io_read_data(ubi, buf, from, 0, aldata_size);
+	if (err && err != UBI_IO_BITFLIPS) {
+		ubi_warn("error %d while reading data from PEB %d",
+			 err, from);
+		goto out_unlock;
+	}
+
+	/*
+	 * Now we have got to calculate how much data we have to to copy. In
+	 * case of a static volume it is fairly easy - the VID header contains
+	 * the data size. In case of a dynamic volume it is more difficult - we
+	 * have to read the contents, cut 0xFF bytes from the end and copy only
+	 * the first part. We must do this to avoid writing 0xFF bytes as it
+	 * may have some side-effects. And not only this. It is important not
+	 * to include those 0xFFs to CRC because later the they may be filled
+	 * by data.
+	 */
+	if (vid_hdr->vol_type == UBI_VID_DYNAMIC)
+		aldata_size = data_size =
+				ubi_calc_data_len(ubi, buf, data_size);
+
+	cond_resched();
+	crc = crc32(UBI_CRC32_INIT, buf, data_size);
+	cond_resched();
+
+	/*
+	 * It may turn out to me that the whole @from physical eraseblock
+	 * contains only 0xFF bytes. Then we have to only write the VID header
+	 * and do not write any data. This also means we should not set
+	 * @vid_hdr->copy_flag, @vid_hdr->data_size, and @vid_hdr->data_crc.
+	 */
+	if (data_size > 0) {
+		vid_hdr->copy_flag = 1;
+		vid_hdr->data_size = cpu_to_ubi32(data_size);
+		vid_hdr->data_crc = cpu_to_ubi32(crc);
+	}
+	vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi));
+
+	err = ubi_io_write_vid_hdr(ubi, to, vid_hdr);
+	if (err)
+		goto out_unlock;
+
+	cond_resched();
+
+	/* Read the VID header back and check if it was written correctly */
+	err = ubi_io_read_vid_hdr(ubi, to, vid_hdr, 1);
+	if (err) {
+		if (err != UBI_IO_BITFLIPS)
+			ubi_warn("cannot read VID header back from PEB %d", to);
+		goto out_unlock;
+	}
+
+	if (data_size > 0) {
+		err = ubi_io_write_data(ubi, buf, to, 0, aldata_size);
+		if (err)
+			goto out_unlock;
+
+		/*
+		 * We've written the data and are going to read it back to make
+		 * sure it was written correctly.
+		 */
+		buf1 = kmalloc(aldata_size, GFP_KERNEL);
+		if (!buf1) {
+			err = -ENOMEM;
+			goto out_unlock;
+		}
+
+		cond_resched();
+
+		err = ubi_io_read_data(ubi, buf1, to, 0, aldata_size);
+		if (err) {
+			if (err != UBI_IO_BITFLIPS)
+				ubi_warn("cannot read data back from PEB %d",
+					 to);
+			goto out_unlock;
+		}
+
+		cond_resched();
+
+		if (memcmp(buf, buf1, aldata_size)) {
+			ubi_warn("read data back from PEB %d - it is different",
+				 to);
+			goto out_unlock;
+		}
+	}
+
+	ubi_assert(vol->eba_tbl[lnum] == from);
+	vol->eba_tbl[lnum] = to;
+
+	leb_write_unlock(ubi, vol_id, lnum);
+	kfree(buf);
+	kfree(buf1);
+
+	return 0;
+
+out_unlock:
+	leb_write_unlock(ubi, vol_id, lnum);
+	kfree(buf);
+	kfree(buf1);
+	return err;
+}
+
+/**
+ * ubi_eba_init_scan - initialize the EBA unit using scanning information.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
+{
+	int i, j, err, num_volumes;
+	struct ubi_scan_volume *sv;
+	struct ubi_volume *vol;
+	struct ubi_scan_leb *seb;
+	struct rb_node *rb;
+
+	dbg_eba("initialize EBA unit");
+
+	spin_lock_init(&ubi->ltree_lock);
+	ubi->ltree = RB_ROOT;
+
+	if (ubi_devices_cnt == 0) {
+		ltree_slab = kmem_cache_create("ubi_ltree_slab",
+					       sizeof(struct ltree_entry), 0,
+					       0, &ltree_entry_ctor, NULL);
+		if (!ltree_slab)
+			return -ENOMEM;
+	}
+
+	ubi->global_sqnum = si->max_sqnum + 1;
+	num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT;
+
+	for (i = 0; i < num_volumes; i++) {
+		vol = ubi->volumes[i];
+		if (!vol)
+			continue;
+
+		cond_resched();
+
+		vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int),
+				       GFP_KERNEL);
+		if (!vol->eba_tbl) {
+			err = -ENOMEM;
+			goto out_free;
+		}
+
+		for (j = 0; j < vol->reserved_pebs; j++)
+			vol->eba_tbl[j] = UBI_LEB_UNMAPPED;
+
+		sv = ubi_scan_find_sv(si, idx2vol_id(ubi, i));
+		if (!sv)
+			continue;
+
+		ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) {
+			if (seb->lnum >= vol->reserved_pebs)
+				/*
+				 * This may happen in case of an unclean reboot
+				 * during re-size.
+				 */
+				ubi_scan_move_to_list(sv, seb, &si->erase);
+			vol->eba_tbl[seb->lnum] = seb->pnum;
+		}
+	}
+
+	if (ubi->bad_allowed) {
+		ubi_calculate_reserved(ubi);
+
+		if (ubi->avail_pebs < ubi->beb_rsvd_level) {
+			/* No enough free physical eraseblocks */
+			ubi->beb_rsvd_pebs = ubi->avail_pebs;
+			ubi_warn("cannot reserve enough PEBs for bad PEB "
+				 "handling, reserved %d, need %d",
+				 ubi->beb_rsvd_pebs, ubi->beb_rsvd_level);
+		} else
+			ubi->beb_rsvd_pebs = ubi->beb_rsvd_level;
+
+		ubi->avail_pebs -= ubi->beb_rsvd_pebs;
+		ubi->rsvd_pebs  += ubi->beb_rsvd_pebs;
+	}
+
+	dbg_eba("EBA unit is initialized");
+	return 0;
+
+out_free:
+	for (i = 0; i < num_volumes; i++) {
+		if (!ubi->volumes[i])
+			continue;
+		kfree(ubi->volumes[i]->eba_tbl);
+	}
+	if (ubi_devices_cnt == 0)
+		kmem_cache_destroy(ltree_slab);
+	return err;
+}
+
+/**
+ * ubi_eba_close - close EBA unit.
+ * @ubi: UBI device description object
+ */
+void ubi_eba_close(const struct ubi_device *ubi)
+{
+	int i, num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT;
+
+	dbg_eba("close EBA unit");
+
+	for (i = 0; i < num_volumes; i++) {
+		if (!ubi->volumes[i])
+			continue;
+		kfree(ubi->volumes[i]->eba_tbl);
+	}
+	if (ubi_devices_cnt == 1)
+		kmem_cache_destroy(ltree_slab);
+}
diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c
new file mode 100644
index 0000000..fc9478d
--- /dev/null
+++ b/drivers/mtd/ubi/gluebi.c
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём), Joern Engel
+ */
+
+/*
+ * This file includes implementation of fake MTD devices for each UBI volume.
+ * This sounds strange, but it is in fact quite useful to make MTD-oriented
+ * software (including all the legacy software) to work on top of UBI.
+ *
+ * Gluebi emulates MTD devices of "MTD_UBIVOLUME" type. Their minimal I/O unit
+ * size (mtd->writesize) is equivalent to the UBI minimal I/O unit. The
+ * eraseblock size is equivalent to the logical eraseblock size of the volume.
+ */
+
+#include <asm/div64.h>
+#include "ubi.h"
+
+/**
+ * gluebi_get_device - get MTD device reference.
+ * @mtd: the MTD device description object
+ *
+ * This function is called every time the MTD device is being opened and
+ * implements the MTD get_device() operation. Returns zero in case of success
+ * and a negative error code in case of failure.
+ */
+static int gluebi_get_device(struct mtd_info *mtd)
+{
+	struct ubi_volume *vol;
+
+	vol = container_of(mtd, struct ubi_volume, gluebi_mtd);
+
+	/*
+	 * We do not introduce locks for gluebi reference count because the
+	 * get_device()/put_device() calls are already serialized at MTD.
+	 */
+	if (vol->gluebi_refcount > 0) {
+		/*
+		 * The MTD device is already referenced and this is just one
+		 * more reference. MTD allows many users to open the same
+		 * volume simultaneously and do not distinguish between
+		 * readers/writers/exclusive openers as UBI does. So we do not
+		 * open the UBI volume again - just increase the reference
+		 * counter and return.
+		 */
+		vol->gluebi_refcount += 1;
+		return 0;
+	}
+
+	/*
+	 * This is the first reference to this UBI volume via the MTD device
+	 * interface. Open the corresponding volume in read-write mode.
+	 */
+	vol->gluebi_desc = ubi_open_volume(vol->ubi->ubi_num, vol->vol_id,
+					   UBI_READWRITE);
+	if (IS_ERR(vol->gluebi_desc))
+		return PTR_ERR(vol->gluebi_desc);
+	vol->gluebi_refcount += 1;
+	return 0;
+}
+
+/**
+ * gluebi_put_device - put MTD device reference.
+ * @mtd: the MTD device description object
+ *
+ * This function is called every time the MTD device is being put. Returns
+ * zero in case of success and a negative error code in case of failure.
+ */
+static void gluebi_put_device(struct mtd_info *mtd)
+{
+	struct ubi_volume *vol;
+
+	vol = container_of(mtd, struct ubi_volume, gluebi_mtd);
+	vol->gluebi_refcount -= 1;
+	ubi_assert(vol->gluebi_refcount >= 0);
+	if (vol->gluebi_refcount == 0)
+		ubi_close_volume(vol->gluebi_desc);
+}
+
+/**
+ * gluebi_read - read operation of emulated MTD devices.
+ * @mtd: MTD device description object
+ * @from: absolute offset from where to read
+ * @len: how many bytes to read
+ * @retlen: count of read bytes is returned here
+ * @buf: buffer to store the read data
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len,
+		       size_t *retlen, unsigned char *buf)
+{
+	int err = 0, lnum, offs, total_read;
+	struct ubi_volume *vol;
+	struct ubi_device *ubi;
+	uint64_t tmp = from;
+
+	dbg_msg("read %zd bytes from offset %lld", len, from);
+
+	if (len < 0 || from < 0 || from + len > mtd->size)
+		return -EINVAL;
+
+	vol = container_of(mtd, struct ubi_volume, gluebi_mtd);
+	ubi = vol->ubi;
+
+	offs = do_div(tmp, mtd->erasesize);
+	lnum = tmp;
+
+	total_read = len;
+	while (total_read) {
+		size_t to_read = mtd->erasesize - offs;
+
+		if (to_read > total_read)
+			to_read = total_read;
+
+		err = ubi_eba_read_leb(ubi, vol->vol_id, lnum, buf, offs,
+				       to_read, 0);
+		if (err)
+			break;
+
+		lnum += 1;
+		offs = 0;
+		total_read -= to_read;
+		buf += to_read;
+	}
+
+	*retlen = len - total_read;
+	return err;
+}
+
+/**
+ * gluebi_write - write operation of emulated MTD devices.
+ * @mtd: MTD device description object
+ * @to: absolute offset where to write
+ * @len: how many bytes to write
+ * @retlen: count of written bytes is returned here
+ * @buf: buffer with data to write
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len,
+		       size_t *retlen, const u_char *buf)
+{
+	int err = 0, lnum, offs, total_written;
+	struct ubi_volume *vol;
+	struct ubi_device *ubi;
+	uint64_t tmp = to;
+
+	dbg_msg("write %zd bytes to offset %lld", len, to);
+
+	if (len < 0 || to < 0 || len + to > mtd->size)
+		return -EINVAL;
+
+	vol = container_of(mtd, struct ubi_volume, gluebi_mtd);
+	ubi = vol->ubi;
+
+	if (ubi->ro_mode)
+		return -EROFS;
+
+	offs = do_div(tmp, mtd->erasesize);
+	lnum = tmp;
+
+	if (len % mtd->writesize || offs % mtd->writesize)
+		return -EINVAL;
+
+	total_written = len;
+	while (total_written) {
+		size_t to_write = mtd->erasesize - offs;
+
+		if (to_write > total_written)
+			to_write = total_written;
+
+		err = ubi_eba_write_leb(ubi, vol->vol_id, lnum, buf, offs,
+					to_write, UBI_UNKNOWN);
+		if (err)
+			break;
+
+		lnum += 1;
+		offs = 0;
+		total_written -= to_write;
+		buf += to_write;
+	}
+
+	*retlen = len - total_written;
+	return err;
+}
+
+/**
+ * gluebi_erase - erase operation of emulated MTD devices.
+ * @mtd: the MTD device description object
+ * @instr: the erase operation description
+ *
+ * This function calls the erase callback when finishes. Returns zero in case
+ * of success and a negative error code in case of failure.
+ */
+static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+	int err, i, lnum, count;
+	struct ubi_volume *vol;
+	struct ubi_device *ubi;
+
+	dbg_msg("erase %u bytes at offset %u", instr->len, instr->addr);
+
+	if (instr->addr < 0 || instr->addr > mtd->size - mtd->erasesize)
+		return -EINVAL;
+
+	if (instr->len < 0 || instr->addr + instr->len > mtd->size)
+		return -EINVAL;
+
+	if (instr->addr % mtd->writesize || instr->len % mtd->writesize)
+		return -EINVAL;
+
+	lnum = instr->addr / mtd->erasesize;
+	count = instr->len / mtd->erasesize;
+
+	vol = container_of(mtd, struct ubi_volume, gluebi_mtd);
+	ubi = vol->ubi;
+
+	if (ubi->ro_mode)
+		return -EROFS;
+
+	for (i = 0; i < count; i++) {
+		err = ubi_eba_unmap_leb(ubi, vol->vol_id, lnum + i);
+		if (err)
+			goto out_err;
+	}
+
+	/*
+	 * MTD erase operations are synchronous, so we have to make sure the
+	 * physical eraseblock is wiped out.
+	 */
+	err = ubi_wl_flush(ubi);
+	if (err)
+		goto out_err;
+
+        instr->state = MTD_ERASE_DONE;
+        mtd_erase_callback(instr);
+	return 0;
+
+out_err:
+	instr->state = MTD_ERASE_FAILED;
+	instr->fail_addr = lnum * mtd->erasesize;
+	return err;
+}
+
+/**
+ * ubi_create_gluebi - initialize gluebi for an UBI volume.
+ * @ubi: UBI device description object
+ * @vol: volume description object
+ *
+ * This function is called when an UBI volume is created in order to create
+ * corresponding fake MTD device. Returns zero in case of success and a
+ * negative error code in case of failure.
+ */
+int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol)
+{
+	struct mtd_info *mtd = &vol->gluebi_mtd;
+
+	mtd->name = kmemdup(vol->name, vol->name_len + 1, GFP_KERNEL);
+	if (!mtd->name)
+		return -ENOMEM;
+
+	mtd->type = MTD_UBIVOLUME;
+	if (!ubi->ro_mode)
+		mtd->flags = MTD_WRITEABLE;
+	mtd->writesize  = ubi->min_io_size;
+	mtd->owner      = THIS_MODULE;
+	mtd->size       = vol->usable_leb_size * vol->reserved_pebs;
+	mtd->erasesize  = vol->usable_leb_size;
+	mtd->read       = gluebi_read;
+	mtd->write      = gluebi_write;
+	mtd->erase      = gluebi_erase;
+	mtd->get_device = gluebi_get_device;
+	mtd->put_device = gluebi_put_device;
+
+	if (add_mtd_device(mtd)) {
+		ubi_err("cannot not add MTD device\n");
+		kfree(mtd->name);
+		return -ENFILE;
+	}
+
+	dbg_msg("added mtd%d (\"%s\"), size %u, EB size %u",
+		mtd->index, mtd->name, mtd->size, mtd->erasesize);
+	return 0;
+}
+
+/**
+ * ubi_destroy_gluebi - close gluebi for an UBI volume.
+ * @vol: volume description object
+ *
+ * This function is called when an UBI volume is removed in order to remove
+ * corresponding fake MTD device. Returns zero in case of success and a
+ * negative error code in case of failure.
+ */
+int ubi_destroy_gluebi(struct ubi_volume *vol)
+{
+	int err;
+	struct mtd_info *mtd = &vol->gluebi_mtd;
+
+	dbg_msg("remove mtd%d", mtd->index);
+	err = del_mtd_device(mtd);
+	if (err)
+		return err;
+	kfree(mtd->name);
+	return 0;
+}
diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c
new file mode 100644
index 0000000..438914d
--- /dev/null
+++ b/drivers/mtd/ubi/io.c
@@ -0,0 +1,1259 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright (c) Nokia Corporation, 2006, 2007
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * UBI input/output unit.
+ *
+ * This unit provides a uniform way to work with all kinds of the underlying
+ * MTD devices. It also implements handy functions for reading and writing UBI
+ * headers.
+ *
+ * We are trying to have a paranoid mindset and not to trust to what we read
+ * from the flash media in order to be more secure and robust. So this unit
+ * validates every single header it reads from the flash media.
+ *
+ * Some words about how the eraseblock headers are stored.
+ *
+ * The erase counter header is always stored at offset zero. By default, the
+ * VID header is stored after the EC header at the closest aligned offset
+ * (i.e. aligned to the minimum I/O unit size). Data starts next to the VID
+ * header at the closest aligned offset. But this default layout may be
+ * changed. For example, for different reasons (e.g., optimization) UBI may be
+ * asked to put the VID header at further offset, and even at an unaligned
+ * offset. Of course, if the offset of the VID header is unaligned, UBI adds
+ * proper padding in front of it. Data offset may also be changed but it has to
+ * be aligned.
+ *
+ * About minimal I/O units. In general, UBI assumes flash device model where
+ * there is only one minimal I/O unit size. E.g., in case of NOR flash it is 1,
+ * in case of NAND flash it is a NAND page, etc. This is reported by MTD in the
+ * @ubi->mtd->writesize field. But as an exception, UBI admits of using another
+ * (smaller) minimal I/O unit size for EC and VID headers to make it possible
+ * to do different optimizations.
+ *
+ * This is extremely useful in case of NAND flashes which admit of several
+ * write operations to one NAND page. In this case UBI can fit EC and VID
+ * headers at one NAND page. Thus, UBI may use "sub-page" size as the minimal
+ * I/O unit for the headers (the @ubi->hdrs_min_io_size field). But it still
+ * reports NAND page size (@ubi->min_io_size) as a minimal I/O unit for the UBI
+ * users.
+ *
+ * Example: some Samsung NANDs with 2KiB pages allow 4x 512-byte writes, so
+ * although the minimal I/O unit is 2K, UBI uses 512 bytes for EC and VID
+ * headers.
+ *
+ * Q: why not just to treat sub-page as a minimal I/O unit of this flash
+ * device, e.g., make @ubi->min_io_size = 512 in the example above?
+ *
+ * A: because when writing a sub-page, MTD still writes a full 2K page but the
+ * bytes which are no relevant to the sub-page are 0xFF. So, basically, writing
+ * 4x512 sub-pages is 4 times slower then writing one 2KiB NAND page. Thus, we
+ * prefer to use sub-pages only for EV and VID headers.
+ *
+ * As it was noted above, the VID header may start at a non-aligned offset.
+ * For example, in case of a 2KiB page NAND flash with a 512 bytes sub-page,
+ * the VID header may reside at offset 1984 which is the last 64 bytes of the
+ * last sub-page (EC header is always at offset zero). This causes some
+ * difficulties when reading and writing VID headers.
+ *
+ * Suppose we have a 64-byte buffer and we read a VID header at it. We change
+ * the data and want to write this VID header out. As we can only write in
+ * 512-byte chunks, we have to allocate one more buffer and copy our VID header
+ * to offset 448 of this buffer.
+ *
+ * The I/O unit does the following trick in order to avoid this extra copy.
+ * It always allocates a @ubi->vid_hdr_alsize bytes buffer for the VID header
+ * and returns a pointer to offset @ubi->vid_hdr_shift of this buffer. When the
+ * VID header is being written out, it shifts the VID header pointer back and
+ * writes the whole sub-page.
+ */
+
+#include <linux/crc32.h>
+#include <linux/err.h>
+#include "ubi.h"
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum);
+static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum);
+static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum,
+				 const struct ubi_ec_hdr *ec_hdr);
+static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum);
+static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum,
+				  const struct ubi_vid_hdr *vid_hdr);
+static int paranoid_check_all_ff(const struct ubi_device *ubi, int pnum,
+				 int offset, int len);
+#else
+#define paranoid_check_not_bad(ubi, pnum) 0
+#define paranoid_check_peb_ec_hdr(ubi, pnum)  0
+#define paranoid_check_ec_hdr(ubi, pnum, ec_hdr)  0
+#define paranoid_check_peb_vid_hdr(ubi, pnum) 0
+#define paranoid_check_vid_hdr(ubi, pnum, vid_hdr) 0
+#define paranoid_check_all_ff(ubi, pnum, offset, len) 0
+#endif
+
+/**
+ * ubi_io_read - read data from a physical eraseblock.
+ * @ubi: UBI device description object
+ * @buf: buffer where to store the read data
+ * @pnum: physical eraseblock number to read from
+ * @offset: offset within the physical eraseblock from where to read
+ * @len: how many bytes to read
+ *
+ * This function reads data from offset @offset of physical eraseblock @pnum
+ * and stores the read data in the @buf buffer. The following return codes are
+ * possible:
+ *
+ * o %0 if all the requested data were successfully read;
+ * o %UBI_IO_BITFLIPS if all the requested data were successfully read, but
+ *   correctable bit-flips were detected; this is harmless but may indicate
+ *   that this eraseblock may become bad soon (but do not have to);
+ * o %-EBADMSG if the MTD subsystem reported about data data integrity
+ *   problems, for example it can me an ECC error in case of NAND; this most
+ *   probably means that the data is corrupted;
+ * o %-EIO if some I/O error occurred;
+ * o other negative error codes in case of other errors.
+ */
+int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset,
+		int len)
+{
+	int err, retries = 0;
+	size_t read;
+	loff_t addr;
+
+	dbg_io("read %d bytes from PEB %d:%d", len, pnum, offset);
+
+	ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+	ubi_assert(offset >= 0 && offset + len <= ubi->peb_size);
+	ubi_assert(len > 0);
+
+	err = paranoid_check_not_bad(ubi, pnum);
+	if (err)
+		return err > 0 ? -EINVAL : err;
+
+	addr = (loff_t)pnum * ubi->peb_size + offset;
+retry:
+	err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf);
+	if (err) {
+		if (err == -EUCLEAN) {
+			/*
+			 * -EUCLEAN is reported if there was a bit-flip which
+			 * was corrected, so this is harmless.
+			 */
+			ubi_msg("fixable bit-flip detected at PEB %d", pnum);
+			ubi_assert(len == read);
+			return UBI_IO_BITFLIPS;
+		}
+
+		if (read != len && retries++ < UBI_IO_RETRIES) {
+			dbg_io("error %d while reading %d bytes from PEB %d:%d, "
+			       "read only %zd bytes, retry",
+			       err, len, pnum, offset, read);
+			yield();
+			goto retry;
+		}
+
+		ubi_err("error %d while reading %d bytes from PEB %d:%d, "
+			"read %zd bytes", err, len, pnum, offset, read);
+		ubi_dbg_dump_stack();
+	} else {
+		ubi_assert(len == read);
+
+		if (ubi_dbg_is_bitflip()) {
+			dbg_msg("bit-flip (emulated)");
+			err = UBI_IO_BITFLIPS;
+		}
+	}
+
+	return err;
+}
+
+/**
+ * ubi_io_write - write data to a physical eraseblock.
+ * @ubi: UBI device description object
+ * @buf: buffer with the data to write
+ * @pnum: physical eraseblock number to write to
+ * @offset: offset within the physical eraseblock where to write
+ * @len: how many bytes to write
+ *
+ * This function writes @len bytes of data from buffer @buf to offset @offset
+ * of physical eraseblock @pnum. If all the data were successfully written,
+ * zero is returned. If an error occurred, this function returns a negative
+ * error code. If %-EIO is returned, the physical eraseblock most probably went
+ * bad.
+ *
+ * Note, in case of an error, it is possible that something was still written
+ * to the flash media, but may be some garbage.
+ */
+int ubi_io_write(const struct ubi_device *ubi, const void *buf, int pnum,
+		 int offset, int len)
+{
+	int err;
+	size_t written;
+	loff_t addr;
+
+	dbg_io("write %d bytes to PEB %d:%d", len, pnum, offset);
+
+	ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+	ubi_assert(offset >= 0 && offset + len <= ubi->peb_size);
+	ubi_assert(offset % ubi->hdrs_min_io_size == 0);
+	ubi_assert(len > 0 && len % ubi->hdrs_min_io_size == 0);
+
+	if (ubi->ro_mode) {
+		ubi_err("read-only mode");
+		return -EROFS;
+	}
+
+	/* The below has to be compiled out if paranoid checks are disabled */
+
+	err = paranoid_check_not_bad(ubi, pnum);
+	if (err)
+		return err > 0 ? -EINVAL : err;
+
+	/* The area we are writing to has to contain all 0xFF bytes */
+	err = paranoid_check_all_ff(ubi, pnum, offset, len);
+	if (err)
+		return err > 0 ? -EINVAL : err;
+
+	if (offset >= ubi->leb_start) {
+		/*
+		 * We write to the data area of the physical eraseblock. Make
+		 * sure it has valid EC and VID headers.
+		 */
+		err = paranoid_check_peb_ec_hdr(ubi, pnum);
+		if (err)
+			return err > 0 ? -EINVAL : err;
+		err = paranoid_check_peb_vid_hdr(ubi, pnum);
+		if (err)
+			return err > 0 ? -EINVAL : err;
+	}
+
+	if (ubi_dbg_is_write_failure()) {
+		dbg_err("cannot write %d bytes to PEB %d:%d "
+			"(emulated)", len, pnum, offset);
+		ubi_dbg_dump_stack();
+		return -EIO;
+	}
+
+	addr = (loff_t)pnum * ubi->peb_size + offset;
+	err = ubi->mtd->write(ubi->mtd, addr, len, &written, buf);
+	if (err) {
+		ubi_err("error %d while writing %d bytes to PEB %d:%d, written"
+			" %zd bytes", err, len, pnum, offset, written);
+		ubi_dbg_dump_stack();
+	} else
+		ubi_assert(written == len);
+
+	return err;
+}
+
+/**
+ * erase_callback - MTD erasure call-back.
+ * @ei: MTD erase information object.
+ *
+ * Note, even though MTD erase interface is asynchronous, all the current
+ * implementations are synchronous anyway.
+ */
+static void erase_callback(struct erase_info *ei)
+{
+	wake_up_interruptible((wait_queue_head_t *)ei->priv);
+}
+
+/**
+ * do_sync_erase - synchronously erase a physical eraseblock.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to erase
+ *
+ * This function synchronously erases physical eraseblock @pnum and returns
+ * zero in case of success and a negative error code in case of failure. If
+ * %-EIO is returned, the physical eraseblock most probably went bad.
+ */
+static int do_sync_erase(const struct ubi_device *ubi, int pnum)
+{
+	int err, retries = 0;
+	struct erase_info ei;
+	wait_queue_head_t wq;
+
+	dbg_io("erase PEB %d", pnum);
+
+retry:
+	init_waitqueue_head(&wq);
+	memset(&ei, 0, sizeof(struct erase_info));
+
+	ei.mtd      = ubi->mtd;
+	ei.addr     = pnum * ubi->peb_size;
+	ei.len      = ubi->peb_size;
+	ei.callback = erase_callback;
+	ei.priv     = (unsigned long)&wq;
+
+	err = ubi->mtd->erase(ubi->mtd, &ei);
+	if (err) {
+		if (retries++ < UBI_IO_RETRIES) {
+			dbg_io("error %d while erasing PEB %d, retry",
+			       err, pnum);
+			yield();
+			goto retry;
+		}
+		ubi_err("cannot erase PEB %d, error %d", pnum, err);
+		ubi_dbg_dump_stack();
+		return err;
+	}
+
+	err = wait_event_interruptible(wq, ei.state == MTD_ERASE_DONE ||
+					   ei.state == MTD_ERASE_FAILED);
+	if (err) {
+		ubi_err("interrupted PEB %d erasure", pnum);
+		return -EINTR;
+	}
+
+	if (ei.state == MTD_ERASE_FAILED) {
+		if (retries++ < UBI_IO_RETRIES) {
+			dbg_io("error while erasing PEB %d, retry", pnum);
+			yield();
+			goto retry;
+		}
+		ubi_err("cannot erase PEB %d", pnum);
+		ubi_dbg_dump_stack();
+		return -EIO;
+	}
+
+	err = paranoid_check_all_ff(ubi, pnum, 0, ubi->peb_size);
+	if (err)
+		return err > 0 ? -EINVAL : err;
+
+	if (ubi_dbg_is_erase_failure() && !err) {
+		dbg_err("cannot erase PEB %d (emulated)", pnum);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/**
+ * check_pattern - check if buffer contains only a certain byte pattern.
+ * @buf: buffer to check
+ * @patt: the pattern to check
+ * @size: buffer size in bytes
+ *
+ * This function returns %1 in there are only @patt bytes in @buf, and %0 if
+ * something else was also found.
+ */
+static int check_pattern(const void *buf, uint8_t patt, int size)
+{
+	int i;
+
+	for (i = 0; i < size; i++)
+		if (((const uint8_t *)buf)[i] != patt)
+			return 0;
+	return 1;
+}
+
+/* Patterns to write to a physical eraseblock when torturing it */
+static uint8_t patterns[] = {0xa5, 0x5a, 0x0};
+
+/**
+ * torture_peb - test a supposedly bad physical eraseblock.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to test
+ *
+ * This function returns %-EIO if the physical eraseblock did not pass the
+ * test, a positive number of erase operations done if the test was
+ * successfully passed, and other negative error codes in case of other errors.
+ */
+static int torture_peb(const struct ubi_device *ubi, int pnum)
+{
+	void *buf;
+	int err, i, patt_count;
+
+	buf = kmalloc(ubi->peb_size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	patt_count = ARRAY_SIZE(patterns);
+	ubi_assert(patt_count > 0);
+
+	for (i = 0; i < patt_count; i++) {
+		err = do_sync_erase(ubi, pnum);
+		if (err)
+			goto out;
+
+		/* Make sure the PEB contains only 0xFF bytes */
+		err = ubi_io_read(ubi, buf, pnum, 0, ubi->peb_size);
+		if (err)
+			goto out;
+
+		err = check_pattern(buf, 0xFF, ubi->peb_size);
+		if (err == 0) {
+			ubi_err("erased PEB %d, but a non-0xFF byte found",
+				pnum);
+			err = -EIO;
+			goto out;
+		}
+
+		/* Write a pattern and check it */
+		memset(buf, patterns[i], ubi->peb_size);
+		err = ubi_io_write(ubi, buf, pnum, 0, ubi->peb_size);
+		if (err)
+			goto out;
+
+		memset(buf, ~patterns[i], ubi->peb_size);
+		err = ubi_io_read(ubi, buf, pnum, 0, ubi->peb_size);
+		if (err)
+			goto out;
+
+		err = check_pattern(buf, patterns[i], ubi->peb_size);
+		if (err == 0) {
+			ubi_err("pattern %x checking failed for PEB %d",
+				patterns[i], pnum);
+			err = -EIO;
+			goto out;
+		}
+	}
+
+	err = patt_count;
+
+out:
+	if (err == UBI_IO_BITFLIPS || err == -EBADMSG)
+		/*
+		 * If a bit-flip or data integrity error was detected, the test
+		 * has not passed because it happened on a freshly erased
+		 * physical eraseblock which means something is wrong with it.
+		 */
+		err = -EIO;
+	kfree(buf);
+	return err;
+}
+
+/**
+ * ubi_io_sync_erase - synchronously erase a physical eraseblock.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock number to erase
+ * @torture: if this physical eraseblock has to be tortured
+ *
+ * This function synchronously erases physical eraseblock @pnum. If @torture
+ * flag is not zero, the physical eraseblock is checked by means of writing
+ * different patterns to it and reading them back. If the torturing is enabled,
+ * the physical eraseblock is erased more then once.
+ *
+ * This function returns the number of erasures made in case of success, %-EIO
+ * if the erasure failed or the torturing test failed, and other negative error
+ * codes in case of other errors. Note, %-EIO means that the physical
+ * eraseblock is bad.
+ */
+int ubi_io_sync_erase(const struct ubi_device *ubi, int pnum, int torture)
+{
+	int err, ret = 0;
+
+	ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+
+	err = paranoid_check_not_bad(ubi, pnum);
+	if (err != 0)
+		return err > 0 ? -EINVAL : err;
+
+	if (ubi->ro_mode) {
+		ubi_err("read-only mode");
+		return -EROFS;
+	}
+
+	if (torture) {
+		ret = torture_peb(ubi, pnum);
+		if (ret < 0)
+			return ret;
+	}
+
+	err = do_sync_erase(ubi, pnum);
+	if (err)
+		return err;
+
+	return ret + 1;
+}
+
+/**
+ * ubi_io_is_bad - check if a physical eraseblock is bad.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to check
+ *
+ * This function returns a positive number if the physical eraseblock is bad,
+ * zero if not, and a negative error code if an error occurred.
+ */
+int ubi_io_is_bad(const struct ubi_device *ubi, int pnum)
+{
+	struct mtd_info *mtd = ubi->mtd;
+
+	ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+
+	if (ubi->bad_allowed) {
+		int ret;
+
+		ret = mtd->block_isbad(mtd, (loff_t)pnum * ubi->peb_size);
+		if (ret < 0)
+			ubi_err("error %d while checking if PEB %d is bad",
+				ret, pnum);
+		else if (ret)
+			dbg_io("PEB %d is bad", pnum);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * ubi_io_mark_bad - mark a physical eraseblock as bad.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to mark
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+int ubi_io_mark_bad(const struct ubi_device *ubi, int pnum)
+{
+	int err;
+	struct mtd_info *mtd = ubi->mtd;
+
+	ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+
+	if (ubi->ro_mode) {
+		ubi_err("read-only mode");
+		return -EROFS;
+	}
+
+	if (!ubi->bad_allowed)
+		return 0;
+
+	err = mtd->block_markbad(mtd, (loff_t)pnum * ubi->peb_size);
+	if (err)
+		ubi_err("cannot mark PEB %d bad, error %d", pnum, err);
+	return err;
+}
+
+/**
+ * validate_ec_hdr - validate an erase counter header.
+ * @ubi: UBI device description object
+ * @ec_hdr: the erase counter header to check
+ *
+ * This function returns zero if the erase counter header is OK, and %1 if
+ * not.
+ */
+static int validate_ec_hdr(const struct ubi_device *ubi,
+			   const struct ubi_ec_hdr *ec_hdr)
+{
+	long long ec;
+	int vid_hdr_offset, leb_start;
+
+	ec = ubi64_to_cpu(ec_hdr->ec);
+	vid_hdr_offset = ubi32_to_cpu(ec_hdr->vid_hdr_offset);
+	leb_start = ubi32_to_cpu(ec_hdr->data_offset);
+
+	if (ec_hdr->version != UBI_VERSION) {
+		ubi_err("node with incompatible UBI version found: "
+			"this UBI version is %d, image version is %d",
+			UBI_VERSION, (int)ec_hdr->version);
+		goto bad;
+	}
+
+	if (vid_hdr_offset != ubi->vid_hdr_offset) {
+		ubi_err("bad VID header offset %d, expected %d",
+			vid_hdr_offset, ubi->vid_hdr_offset);
+		goto bad;
+	}
+
+	if (leb_start != ubi->leb_start) {
+		ubi_err("bad data offset %d, expected %d",
+			leb_start, ubi->leb_start);
+		goto bad;
+	}
+
+	if (ec < 0 || ec > UBI_MAX_ERASECOUNTER) {
+		ubi_err("bad erase counter %lld", ec);
+		goto bad;
+	}
+
+	return 0;
+
+bad:
+	ubi_err("bad EC header");
+	ubi_dbg_dump_ec_hdr(ec_hdr);
+	ubi_dbg_dump_stack();
+	return 1;
+}
+
+/**
+ * ubi_io_read_ec_hdr - read and check an erase counter header.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock to read from
+ * @ec_hdr: a &struct ubi_ec_hdr object where to store the read erase counter
+ * header
+ * @verbose: be verbose if the header is corrupted or was not found
+ *
+ * This function reads erase counter header from physical eraseblock @pnum and
+ * stores it in @ec_hdr. This function also checks CRC checksum of the read
+ * erase counter header. The following codes may be returned:
+ *
+ * o %0 if the CRC checksum is correct and the header was successfully read;
+ * o %UBI_IO_BITFLIPS if the CRC is correct, but bit-flips were detected
+ *   and corrected by the flash driver; this is harmless but may indicate that
+ *   this eraseblock may become bad soon (but may be not);
+ * o %UBI_IO_BAD_EC_HDR if the erase counter header is corrupted (a CRC error);
+ * o %UBI_IO_PEB_EMPTY if the physical eraseblock is empty;
+ * o a negative error code in case of failure.
+ */
+int ubi_io_read_ec_hdr(const struct ubi_device *ubi, int pnum,
+		       struct ubi_ec_hdr *ec_hdr, int verbose)
+{
+	int err, read_err = 0;
+	uint32_t crc, magic, hdr_crc;
+
+	dbg_io("read EC header from PEB %d", pnum);
+	ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+
+	err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE);
+	if (err) {
+		if (err != UBI_IO_BITFLIPS && err != -EBADMSG)
+			return err;
+
+		/*
+		 * We read all the data, but either a correctable bit-flip
+		 * occurred, or MTD reported about some data integrity error,
+		 * like an ECC error in case of NAND. The former is harmless,
+		 * the later may mean that the read data is corrupted. But we
+		 * have a CRC check-sum and we will detect this. If the EC
+		 * header is still OK, we just report this as there was a
+		 * bit-flip.
+		 */
+		read_err = err;
+	}
+
+	magic = ubi32_to_cpu(ec_hdr->magic);
+	if (magic != UBI_EC_HDR_MAGIC) {
+		/*
+		 * The magic field is wrong. Let's check if we have read all
+		 * 0xFF. If yes, this physical eraseblock is assumed to be
+		 * empty.
+		 *
+		 * But if there was a read error, we do not test it for all
+		 * 0xFFs. Even if it does contain all 0xFFs, this error
+		 * indicates that something is still wrong with this physical
+		 * eraseblock and we anyway cannot treat it as empty.
+		 */
+		if (read_err != -EBADMSG &&
+		    check_pattern(ec_hdr, 0xFF, UBI_EC_HDR_SIZE)) {
+			/* The physical eraseblock is supposedly empty */
+
+			/*
+			 * The below is just a paranoid check, it has to be
+			 * compiled out if paranoid checks are disabled.
+			 */
+			err = paranoid_check_all_ff(ubi, pnum, 0,
+						    ubi->peb_size);
+			if (err)
+				return err > 0 ? UBI_IO_BAD_EC_HDR : err;
+
+			if (verbose)
+				ubi_warn("no EC header found at PEB %d, "
+					 "only 0xFF bytes", pnum);
+			return UBI_IO_PEB_EMPTY;
+		}
+
+		/*
+		 * This is not a valid erase counter header, and these are not
+		 * 0xFF bytes. Report that the header is corrupted.
+		 */
+		if (verbose) {
+			ubi_warn("bad magic number at PEB %d: %08x instead of "
+				 "%08x", pnum, magic, UBI_EC_HDR_MAGIC);
+			ubi_dbg_dump_ec_hdr(ec_hdr);
+		}
+		return UBI_IO_BAD_EC_HDR;
+	}
+
+	crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC);
+	hdr_crc = ubi32_to_cpu(ec_hdr->hdr_crc);
+
+	if (hdr_crc != crc) {
+		if (verbose) {
+			ubi_warn("bad EC header CRC at PEB %d, calculated %#08x,"
+				 " read %#08x", pnum, crc, hdr_crc);
+			ubi_dbg_dump_ec_hdr(ec_hdr);
+		}
+		return UBI_IO_BAD_EC_HDR;
+	}
+
+	/* And of course validate what has just been read from the media */
+	err = validate_ec_hdr(ubi, ec_hdr);
+	if (err) {
+		ubi_err("validation failed for PEB %d", pnum);
+		return -EINVAL;
+	}
+
+	return read_err ? UBI_IO_BITFLIPS : 0;
+}
+
+/**
+ * ubi_io_write_ec_hdr - write an erase counter header.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock to write to
+ * @ec_hdr: the erase counter header to write
+ *
+ * This function writes erase counter header described by @ec_hdr to physical
+ * eraseblock @pnum. It also fills most fields of @ec_hdr before writing, so
+ * the caller do not have to fill them. Callers must only fill the @ec_hdr->ec
+ * field.
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure. If %-EIO is returned, the physical eraseblock most probably
+ * went bad.
+ */
+int ubi_io_write_ec_hdr(const struct ubi_device *ubi, int pnum,
+			struct ubi_ec_hdr *ec_hdr)
+{
+	int err;
+	uint32_t crc;
+
+	dbg_io("write EC header to PEB %d", pnum);
+	ubi_assert(pnum >= 0 &&  pnum < ubi->peb_count);
+
+	ec_hdr->magic = cpu_to_ubi32(UBI_EC_HDR_MAGIC);
+	ec_hdr->version = UBI_VERSION;
+	ec_hdr->vid_hdr_offset = cpu_to_ubi32(ubi->vid_hdr_offset);
+	ec_hdr->data_offset = cpu_to_ubi32(ubi->leb_start);
+	crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC);
+	ec_hdr->hdr_crc = cpu_to_ubi32(crc);
+
+	err = paranoid_check_ec_hdr(ubi, pnum, ec_hdr);
+	if (err)
+		return -EINVAL;
+
+	err = ubi_io_write(ubi, ec_hdr, pnum, 0, ubi->ec_hdr_alsize);
+	return err;
+}
+
+/**
+ * validate_vid_hdr - validate a volume identifier header.
+ * @ubi: UBI device description object
+ * @vid_hdr: the volume identifier header to check
+ *
+ * This function checks that data stored in the volume identifier header
+ * @vid_hdr. Returns zero if the VID header is OK and %1 if not.
+ */
+static int validate_vid_hdr(const struct ubi_device *ubi,
+			    const struct ubi_vid_hdr *vid_hdr)
+{
+	int vol_type = vid_hdr->vol_type;
+	int copy_flag = vid_hdr->copy_flag;
+	int vol_id = ubi32_to_cpu(vid_hdr->vol_id);
+	int lnum = ubi32_to_cpu(vid_hdr->lnum);
+	int compat = vid_hdr->compat;
+	int data_size = ubi32_to_cpu(vid_hdr->data_size);
+	int used_ebs = ubi32_to_cpu(vid_hdr->used_ebs);
+	int data_pad = ubi32_to_cpu(vid_hdr->data_pad);
+	int data_crc = ubi32_to_cpu(vid_hdr->data_crc);
+	int usable_leb_size = ubi->leb_size - data_pad;
+
+	if (copy_flag != 0 && copy_flag != 1) {
+		dbg_err("bad copy_flag");
+		goto bad;
+	}
+
+	if (vol_id < 0 || lnum < 0 || data_size < 0 || used_ebs < 0 ||
+	    data_pad < 0) {
+		dbg_err("negative values");
+		goto bad;
+	}
+
+	if (vol_id >= UBI_MAX_VOLUMES && vol_id < UBI_INTERNAL_VOL_START) {
+		dbg_err("bad vol_id");
+		goto bad;
+	}
+
+	if (vol_id < UBI_INTERNAL_VOL_START && compat != 0) {
+		dbg_err("bad compat");
+		goto bad;
+	}
+
+	if (vol_id >= UBI_INTERNAL_VOL_START && compat != UBI_COMPAT_DELETE &&
+	    compat != UBI_COMPAT_RO && compat != UBI_COMPAT_PRESERVE &&
+	    compat != UBI_COMPAT_REJECT) {
+		dbg_err("bad compat");
+		goto bad;
+	}
+
+	if (vol_type != UBI_VID_DYNAMIC && vol_type != UBI_VID_STATIC) {
+		dbg_err("bad vol_type");
+		goto bad;
+	}
+
+	if (data_pad >= ubi->leb_size / 2) {
+		dbg_err("bad data_pad");
+		goto bad;
+	}
+
+	if (vol_type == UBI_VID_STATIC) {
+		/*
+		 * Although from high-level point of view static volumes may
+		 * contain zero bytes of data, but no VID headers can contain
+		 * zero at these fields, because they empty volumes do not have
+		 * mapped logical eraseblocks.
+		 */
+		if (used_ebs == 0) {
+			dbg_err("zero used_ebs");
+			goto bad;
+		}
+		if (data_size == 0) {
+			dbg_err("zero data_size");
+			goto bad;
+		}
+		if (lnum < used_ebs - 1) {
+			if (data_size != usable_leb_size) {
+				dbg_err("bad data_size");
+				goto bad;
+			}
+		} else if (lnum == used_ebs - 1) {
+			if (data_size == 0) {
+				dbg_err("bad data_size at last LEB");
+				goto bad;
+			}
+		} else {
+			dbg_err("too high lnum");
+			goto bad;
+		}
+	} else {
+		if (copy_flag == 0) {
+			if (data_crc != 0) {
+				dbg_err("non-zero data CRC");
+				goto bad;
+			}
+			if (data_size != 0) {
+				dbg_err("non-zero data_size");
+				goto bad;
+			}
+		} else {
+			if (data_size == 0) {
+				dbg_err("zero data_size of copy");
+				goto bad;
+			}
+		}
+		if (used_ebs != 0) {
+			dbg_err("bad used_ebs");
+			goto bad;
+		}
+	}
+
+	return 0;
+
+bad:
+	ubi_err("bad VID header");
+	ubi_dbg_dump_vid_hdr(vid_hdr);
+	ubi_dbg_dump_stack();
+	return 1;
+}
+
+/**
+ * ubi_io_read_vid_hdr - read and check a volume identifier header.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock number to read from
+ * @vid_hdr: &struct ubi_vid_hdr object where to store the read volume
+ * identifier header
+ * @verbose: be verbose if the header is corrupted or wasn't found
+ *
+ * This function reads the volume identifier header from physical eraseblock
+ * @pnum and stores it in @vid_hdr. It also checks CRC checksum of the read
+ * volume identifier header. The following codes may be returned:
+ *
+ * o %0 if the CRC checksum is correct and the header was successfully read;
+ * o %UBI_IO_BITFLIPS if the CRC is correct, but bit-flips were detected
+ *   and corrected by the flash driver; this is harmless but may indicate that
+ *   this eraseblock may become bad soon;
+ * o %UBI_IO_BAD_VID_HRD if the volume identifier header is corrupted (a CRC
+ *   error detected);
+ * o %UBI_IO_PEB_FREE if the physical eraseblock is free (i.e., there is no VID
+ *   header there);
+ * o a negative error code in case of failure.
+ */
+int ubi_io_read_vid_hdr(const struct ubi_device *ubi, int pnum,
+			struct ubi_vid_hdr *vid_hdr, int verbose)
+{
+	int err, read_err = 0;
+	uint32_t crc, magic, hdr_crc;
+	void *p;
+
+	dbg_io("read VID header from PEB %d", pnum);
+	ubi_assert(pnum >= 0 &&  pnum < ubi->peb_count);
+
+	p = (char *)vid_hdr - ubi->vid_hdr_shift;
+	err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset,
+			  ubi->vid_hdr_alsize);
+	if (err) {
+		if (err != UBI_IO_BITFLIPS && err != -EBADMSG)
+			return err;
+
+		/*
+		 * We read all the data, but either a correctable bit-flip
+		 * occurred, or MTD reported about some data integrity error,
+		 * like an ECC error in case of NAND. The former is harmless,
+		 * the later may mean the read data is corrupted. But we have a
+		 * CRC check-sum and we will identify this. If the VID header is
+		 * still OK, we just report this as there was a bit-flip.
+		 */
+		read_err = err;
+	}
+
+	magic = ubi32_to_cpu(vid_hdr->magic);
+	if (magic != UBI_VID_HDR_MAGIC) {
+		/*
+		 * If we have read all 0xFF bytes, the VID header probably does
+		 * not exist and the physical eraseblock is assumed to be free.
+		 *
+		 * But if there was a read error, we do not test the data for
+		 * 0xFFs. Even if it does contain all 0xFFs, this error
+		 * indicates that something is still wrong with this physical
+		 * eraseblock and it cannot be regarded as free.
+		 */
+		if (read_err != -EBADMSG &&
+		    check_pattern(vid_hdr, 0xFF, UBI_VID_HDR_SIZE)) {
+			/* The physical eraseblock is supposedly free */
+
+			/*
+			 * The below is just a paranoid check, it has to be
+			 * compiled out if paranoid checks are disabled.
+			 */
+			err = paranoid_check_all_ff(ubi, pnum, ubi->leb_start,
+						    ubi->leb_size);
+			if (err)
+				return err > 0 ? UBI_IO_BAD_VID_HDR : err;
+
+			if (verbose)
+				ubi_warn("no VID header found at PEB %d, "
+					 "only 0xFF bytes", pnum);
+			return UBI_IO_PEB_FREE;
+		}
+
+		/*
+		 * This is not a valid VID header, and these are not 0xFF
+		 * bytes. Report that the header is corrupted.
+		 */
+		if (verbose) {
+			ubi_warn("bad magic number at PEB %d: %08x instead of "
+				 "%08x", pnum, magic, UBI_VID_HDR_MAGIC);
+			ubi_dbg_dump_vid_hdr(vid_hdr);
+		}
+		return UBI_IO_BAD_VID_HDR;
+	}
+
+	crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_VID_HDR_SIZE_CRC);
+	hdr_crc = ubi32_to_cpu(vid_hdr->hdr_crc);
+
+	if (hdr_crc != crc) {
+		if (verbose) {
+			ubi_warn("bad CRC at PEB %d, calculated %#08x, "
+				 "read %#08x", pnum, crc, hdr_crc);
+			ubi_dbg_dump_vid_hdr(vid_hdr);
+		}
+		return UBI_IO_BAD_VID_HDR;
+	}
+
+	/* Validate the VID header that we have just read */
+	err = validate_vid_hdr(ubi, vid_hdr);
+	if (err) {
+		ubi_err("validation failed for PEB %d", pnum);
+		return -EINVAL;
+	}
+
+	return read_err ? UBI_IO_BITFLIPS : 0;
+}
+
+/**
+ * ubi_io_write_vid_hdr - write a volume identifier header.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to write to
+ * @vid_hdr: the volume identifier header to write
+ *
+ * This function writes the volume identifier header described by @vid_hdr to
+ * physical eraseblock @pnum. This function automatically fills the
+ * @vid_hdr->magic and the @vid_hdr->version fields, as well as calculates
+ * header CRC checksum and stores it at vid_hdr->hdr_crc.
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure. If %-EIO is returned, the physical eraseblock probably went
+ * bad.
+ */
+int ubi_io_write_vid_hdr(const struct ubi_device *ubi, int pnum,
+			 struct ubi_vid_hdr *vid_hdr)
+{
+	int err;
+	uint32_t crc;
+	void *p;
+
+	dbg_io("write VID header to PEB %d", pnum);
+	ubi_assert(pnum >= 0 &&  pnum < ubi->peb_count);
+
+	err = paranoid_check_peb_ec_hdr(ubi, pnum);
+	if (err)
+		return err > 0 ? -EINVAL: err;
+
+	vid_hdr->magic = cpu_to_ubi32(UBI_VID_HDR_MAGIC);
+	vid_hdr->version = UBI_VERSION;
+	crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_VID_HDR_SIZE_CRC);
+	vid_hdr->hdr_crc = cpu_to_ubi32(crc);
+
+	err = paranoid_check_vid_hdr(ubi, pnum, vid_hdr);
+	if (err)
+		return -EINVAL;
+
+	p = (char *)vid_hdr - ubi->vid_hdr_shift;
+	err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset,
+			   ubi->vid_hdr_alsize);
+	return err;
+}
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+
+/**
+ * paranoid_check_not_bad - ensure that a physical eraseblock is not bad.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock number to check
+ *
+ * This function returns zero if the physical eraseblock is good, a positive
+ * number if it is bad and a negative error code if an error occurred.
+ */
+static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum)
+{
+	int err;
+
+	err = ubi_io_is_bad(ubi, pnum);
+	if (!err)
+		return err;
+
+	ubi_err("paranoid check failed for PEB %d", pnum);
+	ubi_dbg_dump_stack();
+	return err;
+}
+
+/**
+ * paranoid_check_ec_hdr - check if an erase counter header is all right.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock number the erase counter header belongs to
+ * @ec_hdr: the erase counter header to check
+ *
+ * This function returns zero if the erase counter header contains valid
+ * values, and %1 if not.
+ */
+static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum,
+				 const struct ubi_ec_hdr *ec_hdr)
+{
+	int err;
+	uint32_t magic;
+
+	magic = ubi32_to_cpu(ec_hdr->magic);
+	if (magic != UBI_EC_HDR_MAGIC) {
+		ubi_err("bad magic %#08x, must be %#08x",
+			magic, UBI_EC_HDR_MAGIC);
+		goto fail;
+	}
+
+	err = validate_ec_hdr(ubi, ec_hdr);
+	if (err) {
+		ubi_err("paranoid check failed for PEB %d", pnum);
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	ubi_dbg_dump_ec_hdr(ec_hdr);
+	ubi_dbg_dump_stack();
+	return 1;
+}
+
+/**
+ * paranoid_check_peb_ec_hdr - check that the erase counter header of a
+ * physical eraseblock is in-place and is all right.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to check
+ *
+ * This function returns zero if the erase counter header is all right, %1 if
+ * not, and a negative error code if an error occurred.
+ */
+static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum)
+{
+	int err;
+	uint32_t crc, hdr_crc;
+	struct ubi_ec_hdr *ec_hdr;
+
+	ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
+	if (!ec_hdr)
+		return -ENOMEM;
+
+	err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE);
+	if (err && err != UBI_IO_BITFLIPS && err != -EBADMSG)
+		goto exit;
+
+	crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC);
+	hdr_crc = ubi32_to_cpu(ec_hdr->hdr_crc);
+	if (hdr_crc != crc) {
+		ubi_err("bad CRC, calculated %#08x, read %#08x", crc, hdr_crc);
+		ubi_err("paranoid check failed for PEB %d", pnum);
+		ubi_dbg_dump_ec_hdr(ec_hdr);
+		ubi_dbg_dump_stack();
+		err = 1;
+		goto exit;
+	}
+
+	err = paranoid_check_ec_hdr(ubi, pnum, ec_hdr);
+
+exit:
+	kfree(ec_hdr);
+	return err;
+}
+
+/**
+ * paranoid_check_vid_hdr - check that a volume identifier header is all right.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock number the volume identifier header belongs to
+ * @vid_hdr: the volume identifier header to check
+ *
+ * This function returns zero if the volume identifier header is all right, and
+ * %1 if not.
+ */
+static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum,
+				  const struct ubi_vid_hdr *vid_hdr)
+{
+	int err;
+	uint32_t magic;
+
+	magic = ubi32_to_cpu(vid_hdr->magic);
+	if (magic != UBI_VID_HDR_MAGIC) {
+		ubi_err("bad VID header magic %#08x at PEB %d, must be %#08x",
+			magic, pnum, UBI_VID_HDR_MAGIC);
+		goto fail;
+	}
+
+	err = validate_vid_hdr(ubi, vid_hdr);
+	if (err) {
+		ubi_err("paranoid check failed for PEB %d", pnum);
+		goto fail;
+	}
+
+	return err;
+
+fail:
+	ubi_err("paranoid check failed for PEB %d", pnum);
+	ubi_dbg_dump_vid_hdr(vid_hdr);
+	ubi_dbg_dump_stack();
+	return 1;
+
+}
+
+/**
+ * paranoid_check_peb_vid_hdr - check that the volume identifier header of a
+ * physical eraseblock is in-place and is all right.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to check
+ *
+ * This function returns zero if the volume identifier header is all right,
+ * %1 if not, and a negative error code if an error occurred.
+ */
+static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum)
+{
+	int err;
+	uint32_t crc, hdr_crc;
+	struct ubi_vid_hdr *vid_hdr;
+	void *p;
+
+	vid_hdr = ubi_zalloc_vid_hdr(ubi);
+	if (!vid_hdr)
+		return -ENOMEM;
+
+	p = (char *)vid_hdr - ubi->vid_hdr_shift;
+	err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset,
+			  ubi->vid_hdr_alsize);
+	if (err && err != UBI_IO_BITFLIPS && err != -EBADMSG)
+		goto exit;
+
+	crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_EC_HDR_SIZE_CRC);
+	hdr_crc = ubi32_to_cpu(vid_hdr->hdr_crc);
+	if (hdr_crc != crc) {
+		ubi_err("bad VID header CRC at PEB %d, calculated %#08x, "
+			"read %#08x", pnum, crc, hdr_crc);
+		ubi_err("paranoid check failed for PEB %d", pnum);
+		ubi_dbg_dump_vid_hdr(vid_hdr);
+		ubi_dbg_dump_stack();
+		err = 1;
+		goto exit;
+	}
+
+	err = paranoid_check_vid_hdr(ubi, pnum, vid_hdr);
+
+exit:
+	ubi_free_vid_hdr(ubi, vid_hdr);
+	return err;
+}
+
+/**
+ * paranoid_check_all_ff - check that a region of flash is empty.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to check
+ * @offset: the starting offset within the physical eraseblock to check
+ * @len: the length of the region to check
+ *
+ * This function returns zero if only 0xFF bytes are present at offset
+ * @offset of the physical eraseblock @pnum, %1 if not, and a negative error
+ * code if an error occurred.
+ */
+static int paranoid_check_all_ff(const struct ubi_device *ubi, int pnum,
+				 int offset, int len)
+{
+	size_t read;
+	int err;
+	void *buf;
+	loff_t addr = (loff_t)pnum * ubi->peb_size + offset;
+
+	buf = kzalloc(len, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf);
+	if (err && err != -EUCLEAN) {
+		ubi_err("error %d while reading %d bytes from PEB %d:%d, "
+			"read %zd bytes", err, len, pnum, offset, read);
+		goto error;
+	}
+
+	err = check_pattern(buf, 0xFF, len);
+	if (err == 0) {
+		ubi_err("flash region at PEB %d:%d, length %d does not "
+			"contain all 0xFF bytes", pnum, offset, len);
+		goto fail;
+	}
+
+	kfree(buf);
+	return 0;
+
+fail:
+	ubi_err("paranoid check failed for PEB %d", pnum);
+	dbg_msg("hex dump of the %d-%d region", offset, offset + len);
+	ubi_dbg_hexdump(buf, len);
+	err = 1;
+error:
+	ubi_dbg_dump_stack();
+	kfree(buf);
+	return err;
+}
+
+#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */
diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c
new file mode 100644
index 0000000..d352c45
--- /dev/null
+++ b/drivers/mtd/ubi/kapi.c
@@ -0,0 +1,575 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/* This file mostly implements UBI kernel API functions */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <asm/div64.h>
+#include "ubi.h"
+
+/**
+ * ubi_get_device_info - get information about UBI device.
+ * @ubi_num: UBI device number
+ * @di: the information is stored here
+ *
+ * This function returns %0 in case of success and a %-ENODEV if there is no
+ * such UBI device.
+ */
+int ubi_get_device_info(int ubi_num, struct ubi_device_info *di)
+{
+	const struct ubi_device *ubi;
+
+	if (!try_module_get(THIS_MODULE))
+		return -ENODEV;
+
+	if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES ||
+	    !ubi_devices[ubi_num]) {
+		module_put(THIS_MODULE);
+		return -ENODEV;
+	}
+
+	ubi = ubi_devices[ubi_num];
+	di->ubi_num = ubi->ubi_num;
+	di->leb_size = ubi->leb_size;
+	di->min_io_size = ubi->min_io_size;
+	di->ro_mode = ubi->ro_mode;
+	di->cdev = MKDEV(ubi->major, 0);
+	module_put(THIS_MODULE);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ubi_get_device_info);
+
+/**
+ * ubi_get_volume_info - get information about UBI volume.
+ * @desc: volume descriptor
+ * @vi: the information is stored here
+ */
+void ubi_get_volume_info(struct ubi_volume_desc *desc,
+			 struct ubi_volume_info *vi)
+{
+	const struct ubi_volume *vol = desc->vol;
+	const struct ubi_device *ubi = vol->ubi;
+
+	vi->vol_id = vol->vol_id;
+	vi->ubi_num = ubi->ubi_num;
+	vi->size = vol->reserved_pebs;
+	vi->used_bytes = vol->used_bytes;
+	vi->vol_type = vol->vol_type;
+	vi->corrupted = vol->corrupted;
+	vi->upd_marker = vol->upd_marker;
+	vi->alignment = vol->alignment;
+	vi->usable_leb_size = vol->usable_leb_size;
+	vi->name_len = vol->name_len;
+	vi->name = vol->name;
+	vi->cdev = MKDEV(ubi->major, vi->vol_id + 1);
+}
+EXPORT_SYMBOL_GPL(ubi_get_volume_info);
+
+/**
+ * ubi_open_volume - open UBI volume.
+ * @ubi_num: UBI device number
+ * @vol_id: volume ID
+ * @mode: open mode
+ *
+ * The @mode parameter specifies if the volume should be opened in read-only
+ * mode, read-write mode, or exclusive mode. The exclusive mode guarantees that
+ * nobody else will be able to open this volume. UBI allows to have many volume
+ * readers and one writer at a time.
+ *
+ * If a static volume is being opened for the first time since boot, it will be
+ * checked by this function, which means it will be fully read and the CRC
+ * checksum of each logical eraseblock will be checked.
+ *
+ * This function returns volume descriptor in case of success and a negative
+ * error code in case of failure.
+ */
+struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode)
+{
+	int err;
+	struct ubi_volume_desc *desc;
+	struct ubi_device *ubi = ubi_devices[ubi_num];
+	struct ubi_volume *vol;
+
+	dbg_msg("open device %d volume %d, mode %d", ubi_num, vol_id, mode);
+
+	err = -ENODEV;
+	if (!try_module_get(THIS_MODULE))
+		return ERR_PTR(err);
+
+	if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES || !ubi)
+		goto out_put;
+
+	err = -EINVAL;
+	if (vol_id < 0 || vol_id >= ubi->vtbl_slots)
+		goto out_put;
+	if (mode != UBI_READONLY && mode != UBI_READWRITE &&
+	    mode != UBI_EXCLUSIVE)
+		goto out_put;
+
+	desc = kmalloc(sizeof(struct ubi_volume_desc), GFP_KERNEL);
+	if (!desc) {
+		err = -ENOMEM;
+		goto out_put;
+	}
+
+	spin_lock(&ubi->volumes_lock);
+	vol = ubi->volumes[vol_id];
+	if (!vol) {
+		err = -ENODEV;
+		goto out_unlock;
+	}
+
+	err = -EBUSY;
+	switch (mode) {
+	case UBI_READONLY:
+		if (vol->exclusive)
+			goto out_unlock;
+		vol->readers += 1;
+		break;
+
+	case UBI_READWRITE:
+		if (vol->exclusive || vol->writers > 0)
+			goto out_unlock;
+		vol->writers += 1;
+		break;
+
+	case UBI_EXCLUSIVE:
+		if (vol->exclusive || vol->writers || vol->readers)
+			goto out_unlock;
+		vol->exclusive = 1;
+		break;
+	}
+	spin_unlock(&ubi->volumes_lock);
+
+	desc->vol = vol;
+	desc->mode = mode;
+
+	/*
+	 * To prevent simultaneous checks of the same volume we use @vtbl_mutex,
+	 * although it is not the purpose it was introduced for.
+	 */
+	mutex_lock(&ubi->vtbl_mutex);
+	if (!vol->checked) {
+		/* This is the first open - check the volume */
+		err = ubi_check_volume(ubi, vol_id);
+		if (err < 0) {
+			mutex_unlock(&ubi->vtbl_mutex);
+			ubi_close_volume(desc);
+			return ERR_PTR(err);
+		}
+		if (err == 1) {
+			ubi_warn("volume %d on UBI device %d is corrupted",
+				 vol_id, ubi->ubi_num);
+			vol->corrupted = 1;
+		}
+		vol->checked = 1;
+	}
+	mutex_unlock(&ubi->vtbl_mutex);
+	return desc;
+
+out_unlock:
+	spin_unlock(&ubi->volumes_lock);
+	kfree(desc);
+out_put:
+	module_put(THIS_MODULE);
+	return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(ubi_open_volume);
+
+/**
+ * ubi_open_volume_nm - open UBI volume by name.
+ * @ubi_num: UBI device number
+ * @name: volume name
+ * @mode: open mode
+ *
+ * This function is similar to 'ubi_open_volume()', but opens a volume by name.
+ */
+struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
+					   int mode)
+{
+	int i, vol_id = -1, len;
+	struct ubi_volume_desc *ret;
+	struct ubi_device *ubi;
+
+	dbg_msg("open volume %s, mode %d", name, mode);
+
+	if (!name)
+		return ERR_PTR(-EINVAL);
+
+	len = strnlen(name, UBI_VOL_NAME_MAX + 1);
+	if (len > UBI_VOL_NAME_MAX)
+		return ERR_PTR(-EINVAL);
+
+	ret = ERR_PTR(-ENODEV);
+	if (!try_module_get(THIS_MODULE))
+		return ret;
+
+	if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES || !ubi_devices[ubi_num])
+		goto out_put;
+
+	ubi = ubi_devices[ubi_num];
+
+	spin_lock(&ubi->volumes_lock);
+	/* Walk all volumes of this UBI device */
+	for (i = 0; i < ubi->vtbl_slots; i++) {
+		struct ubi_volume *vol = ubi->volumes[i];
+
+		if (vol && len == vol->name_len && !strcmp(name, vol->name)) {
+			vol_id = i;
+			break;
+		}
+	}
+	spin_unlock(&ubi->volumes_lock);
+
+	if (vol_id < 0)
+		goto out_put;
+
+	ret = ubi_open_volume(ubi_num, vol_id, mode);
+
+out_put:
+	module_put(THIS_MODULE);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ubi_open_volume_nm);
+
+/**
+ * ubi_close_volume - close UBI volume.
+ * @desc: volume descriptor
+ */
+void ubi_close_volume(struct ubi_volume_desc *desc)
+{
+	struct ubi_volume *vol = desc->vol;
+
+	dbg_msg("close volume %d, mode %d", vol->vol_id, desc->mode);
+
+	spin_lock(&vol->ubi->volumes_lock);
+	switch (desc->mode) {
+	case UBI_READONLY:
+		vol->readers -= 1;
+		break;
+	case UBI_READWRITE:
+		vol->writers -= 1;
+		break;
+	case UBI_EXCLUSIVE:
+		vol->exclusive = 0;
+	}
+	spin_unlock(&vol->ubi->volumes_lock);
+
+	kfree(desc);
+	module_put(THIS_MODULE);
+}
+EXPORT_SYMBOL_GPL(ubi_close_volume);
+
+/**
+ * ubi_leb_read - read data.
+ * @desc: volume descriptor
+ * @lnum: logical eraseblock number to read from
+ * @buf: buffer where to store the read data
+ * @offset: offset within the logical eraseblock to read from
+ * @len: how many bytes to read
+ * @check: whether UBI has to check the read data's CRC or not.
+ *
+ * This function reads data from offset @offset of logical eraseblock @lnum and
+ * stores the data at @buf. When reading from static volumes, @check specifies
+ * whether the data has to be checked or not. If yes, the whole logical
+ * eraseblock will be read and its CRC checksum will be checked (i.e., the CRC
+ * checksum is per-eraseblock). So checking may substantially slow down the
+ * read speed. The @check argument is ignored for dynamic volumes.
+ *
+ * In case of success, this function returns zero. In case of failure, this
+ * function returns a negative error code.
+ *
+ * %-EBADMSG error code is returned:
+ * o for both static and dynamic volumes if MTD driver has detected a data
+ *   integrity problem (unrecoverable ECC checksum mismatch in case of NAND);
+ * o for static volumes in case of data CRC mismatch.
+ *
+ * If the volume is damaged because of an interrupted update this function just
+ * returns immediately with %-EBADF error code.
+ */
+int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
+		 int len, int check)
+{
+	struct ubi_volume *vol = desc->vol;
+	struct ubi_device *ubi = vol->ubi;
+	int err, vol_id = vol->vol_id;
+
+	dbg_msg("read %d bytes from LEB %d:%d:%d", len, vol_id, lnum, offset);
+
+	if (vol_id < 0 || vol_id >= ubi->vtbl_slots || lnum < 0 ||
+	    lnum >= vol->used_ebs || offset < 0 || len < 0 ||
+	    offset + len > vol->usable_leb_size)
+		return -EINVAL;
+
+	if (vol->vol_type == UBI_STATIC_VOLUME && lnum == vol->used_ebs - 1 &&
+	    offset + len > vol->last_eb_bytes)
+		return -EINVAL;
+
+	if (vol->upd_marker)
+		return -EBADF;
+	if (len == 0)
+		return 0;
+
+	err = ubi_eba_read_leb(ubi, vol_id, lnum, buf, offset, len, check);
+	if (err && err == -EBADMSG && vol->vol_type == UBI_STATIC_VOLUME) {
+		ubi_warn("mark volume %d as corrupted", vol_id);
+		vol->corrupted = 1;
+	}
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(ubi_leb_read);
+
+/**
+ * ubi_leb_write - write data.
+ * @desc: volume descriptor
+ * @lnum: logical eraseblock number to write to
+ * @buf: data to write
+ * @offset: offset within the logical eraseblock where to write
+ * @len: how many bytes to write
+ * @dtype: expected data type
+ *
+ * This function writes @len bytes of data from @buf to offset @offset of
+ * logical eraseblock @lnum. The @dtype argument describes expected lifetime of
+ * the data.
+ *
+ * This function takes care of physical eraseblock write failures. If write to
+ * the physical eraseblock write operation fails, the logical eraseblock is
+ * re-mapped to another physical eraseblock, the data is recovered, and the
+ * write finishes. UBI has a pool of reserved physical eraseblocks for this.
+ *
+ * If all the data were successfully written, zero is returned. If an error
+ * occurred and UBI has not been able to recover from it, this function returns
+ * a negative error code. Note, in case of an error, it is possible that
+ * something was still written to the flash media, but that may be some
+ * garbage.
+ *
+ * If the volume is damaged because of an interrupted update this function just
+ * returns immediately with %-EBADF code.
+ */
+int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
+		  int offset, int len, int dtype)
+{
+	struct ubi_volume *vol = desc->vol;
+	struct ubi_device *ubi = vol->ubi;
+	int vol_id = vol->vol_id;
+
+	dbg_msg("write %d bytes to LEB %d:%d:%d", len, vol_id, lnum, offset);
+
+	if (vol_id < 0 || vol_id >= ubi->vtbl_slots)
+		return -EINVAL;
+
+	if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
+		return -EROFS;
+
+	if (lnum < 0 || lnum >= vol->reserved_pebs || offset < 0 || len < 0 ||
+	    offset + len > vol->usable_leb_size || offset % ubi->min_io_size ||
+	    len % ubi->min_io_size)
+		return -EINVAL;
+
+	if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM &&
+	    dtype != UBI_UNKNOWN)
+		return -EINVAL;
+
+	if (vol->upd_marker)
+		return -EBADF;
+
+	if (len == 0)
+		return 0;
+
+	return ubi_eba_write_leb(ubi, vol_id, lnum, buf, offset, len, dtype);
+}
+EXPORT_SYMBOL_GPL(ubi_leb_write);
+
+/*
+ * ubi_leb_change - change logical eraseblock atomically.
+ * @desc: volume descriptor
+ * @lnum: logical eraseblock number to change
+ * @buf: data to write
+ * @len: how many bytes to write
+ * @dtype: expected data type
+ *
+ * This function changes the contents of a logical eraseblock atomically. @buf
+ * has to contain new logical eraseblock data, and @len - the length of the
+ * data, which has to be aligned. The length may be shorter then the logical
+ * eraseblock size, ant the logical eraseblock may be appended to more times
+ * later on. This function guarantees that in case of an unclean reboot the old
+ * contents is preserved. Returns zero in case of success and a negative error
+ * code in case of failure.
+ */
+int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
+		   int len, int dtype)
+{
+	struct ubi_volume *vol = desc->vol;
+	struct ubi_device *ubi = vol->ubi;
+	int vol_id = vol->vol_id;
+
+	dbg_msg("atomically write %d bytes to LEB %d:%d", len, vol_id, lnum);
+
+	if (vol_id < 0 || vol_id >= ubi->vtbl_slots)
+		return -EINVAL;
+
+	if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
+		return -EROFS;
+
+	if (lnum < 0 || lnum >= vol->reserved_pebs || len < 0 ||
+	    len > vol->usable_leb_size || len % ubi->min_io_size)
+		return -EINVAL;
+
+	if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM &&
+	    dtype != UBI_UNKNOWN)
+		return -EINVAL;
+
+	if (vol->upd_marker)
+		return -EBADF;
+
+	if (len == 0)
+		return 0;
+
+	return ubi_eba_atomic_leb_change(ubi, vol_id, lnum, buf, len, dtype);
+}
+EXPORT_SYMBOL_GPL(ubi_leb_change);
+
+/**
+ * ubi_leb_erase - erase logical eraseblock.
+ * @desc: volume descriptor
+ * @lnum: logical eraseblock number
+ *
+ * This function un-maps logical eraseblock @lnum and synchronously erases the
+ * correspondent physical eraseblock. Returns zero in case of success and a
+ * negative error code in case of failure.
+ *
+ * If the volume is damaged because of an interrupted update this function just
+ * returns immediately with %-EBADF code.
+ */
+int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum)
+{
+	struct ubi_volume *vol = desc->vol;
+	struct ubi_device *ubi = vol->ubi;
+	int err, vol_id = vol->vol_id;
+
+	dbg_msg("erase LEB %d:%d", vol_id, lnum);
+
+	if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
+		return -EROFS;
+
+	if (lnum < 0 || lnum >= vol->reserved_pebs)
+		return -EINVAL;
+
+	if (vol->upd_marker)
+		return -EBADF;
+
+	err = ubi_eba_unmap_leb(ubi, vol_id, lnum);
+	if (err)
+		return err;
+
+	return ubi_wl_flush(ubi);
+}
+EXPORT_SYMBOL_GPL(ubi_leb_erase);
+
+/**
+ * ubi_leb_unmap - un-map logical eraseblock.
+ * @desc: volume descriptor
+ * @lnum: logical eraseblock number
+ *
+ * This function un-maps logical eraseblock @lnum and schedules the
+ * corresponding physical eraseblock for erasure, so that it will eventually be
+ * physically erased in background. This operation is much faster then the
+ * erase operation.
+ *
+ * Unlike erase, the un-map operation does not guarantee that the logical
+ * eraseblock will contain all 0xFF bytes when UBI is initialized again. For
+ * example, if several logical eraseblocks are un-mapped, and an unclean reboot
+ * happens after this, the logical eraseblocks will not necessarily be
+ * un-mapped again when this MTD device is attached. They may actually be
+ * mapped to the same physical eraseblocks again. So, this function has to be
+ * used with care.
+ *
+ * In other words, when un-mapping a logical eraseblock, UBI does not store
+ * any information about this on the flash media, it just marks the logical
+ * eraseblock as "un-mapped" in RAM. If UBI is detached before the physical
+ * eraseblock is physically erased, it will be mapped again to the same logical
+ * eraseblock when the MTD device is attached again.
+ *
+ * The main and obvious use-case of this function is when the contents of a
+ * logical eraseblock has to be re-written. Then it is much more efficient to
+ * first un-map it, then write new data, rather then first erase it, then write
+ * new data. Note, once new data has been written to the logical eraseblock,
+ * UBI guarantees that the old contents has gone forever. In other words, if an
+ * unclean reboot happens after the logical eraseblock has been un-mapped and
+ * then written to, it will contain the last written data.
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure. If the volume is damaged because of an interrupted update
+ * this function just returns immediately with %-EBADF code.
+ */
+int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum)
+{
+	struct ubi_volume *vol = desc->vol;
+	struct ubi_device *ubi = vol->ubi;
+	int vol_id = vol->vol_id;
+
+	dbg_msg("unmap LEB %d:%d", vol_id, lnum);
+
+	if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
+		return -EROFS;
+
+	if (lnum < 0 || lnum >= vol->reserved_pebs)
+		return -EINVAL;
+
+	if (vol->upd_marker)
+		return -EBADF;
+
+	return ubi_eba_unmap_leb(ubi, vol_id, lnum);
+}
+EXPORT_SYMBOL_GPL(ubi_leb_unmap);
+
+/**
+ * ubi_is_mapped - check if logical eraseblock is mapped.
+ * @desc: volume descriptor
+ * @lnum: logical eraseblock number
+ *
+ * This function checks if logical eraseblock @lnum is mapped to a physical
+ * eraseblock. If a logical eraseblock is un-mapped, this does not necessarily
+ * mean it will still be un-mapped after the UBI device is re-attached. The
+ * logical eraseblock may become mapped to the physical eraseblock it was last
+ * mapped to.
+ *
+ * This function returns %1 if the LEB is mapped, %0 if not, and a negative
+ * error code in case of failure. If the volume is damaged because of an
+ * interrupted update this function just returns immediately with %-EBADF error
+ * code.
+ */
+int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum)
+{
+	struct ubi_volume *vol = desc->vol;
+
+	dbg_msg("test LEB %d:%d", vol->vol_id, lnum);
+
+	if (lnum < 0 || lnum >= vol->reserved_pebs)
+		return -EINVAL;
+
+	if (vol->upd_marker)
+		return -EBADF;
+
+	return vol->eba_tbl[lnum] >= 0;
+}
+EXPORT_SYMBOL_GPL(ubi_is_mapped);
diff --git a/drivers/mtd/ubi/misc.c b/drivers/mtd/ubi/misc.c
new file mode 100644
index 0000000..38d4e67
--- /dev/null
+++ b/drivers/mtd/ubi/misc.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/* Here we keep miscellaneous functions which are used all over the UBI code */
+
+#include "ubi.h"
+
+/**
+ * calc_data_len - calculate how much real data is stored in a buffer.
+ * @ubi: UBI device description object
+ * @buf: a buffer with the contents of the physical eraseblock
+ * @length: the buffer length
+ *
+ * This function calculates how much "real data" is stored in @buf and returnes
+ * the length. Continuous 0xFF bytes at the end of the buffer are not
+ * considered as "real data".
+ */
+int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf,
+		      int length)
+{
+	int i;
+
+	ubi_assert(length % ubi->min_io_size == 0);
+
+	for (i = length - 1; i >= 0; i--)
+		if (((const uint8_t *)buf)[i] != 0xFF)
+			break;
+
+	/* The resulting length must be aligned to the minimum flash I/O size */
+	length = ALIGN(i + 1, ubi->min_io_size);
+	return length;
+}
+
+/**
+ * ubi_check_volume - check the contents of a static volume.
+ * @ubi: UBI device description object
+ * @vol_id: ID of the volume to check
+ *
+ * This function checks if static volume @vol_id is corrupted by fully reading
+ * it and checking data CRC. This function returns %0 if the volume is not
+ * corrupted, %1 if it is corrupted and a negative error code in case of
+ * failure. Dynamic volumes are not checked and zero is returned immediately.
+ */
+int ubi_check_volume(struct ubi_device *ubi, int vol_id)
+{
+	void *buf;
+	int err = 0, i;
+	struct ubi_volume *vol = ubi->volumes[vol_id];
+
+	if (vol->vol_type != UBI_STATIC_VOLUME)
+		return 0;
+
+	buf = kmalloc(vol->usable_leb_size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < vol->used_ebs; i++) {
+		int size;
+
+		if (i == vol->used_ebs - 1)
+			size = vol->last_eb_bytes;
+		else
+			size = vol->usable_leb_size;
+
+		err = ubi_eba_read_leb(ubi, vol_id, i, buf, 0, size, 1);
+		if (err) {
+			if (err == -EBADMSG)
+				err = 1;
+			break;
+		}
+	}
+
+	kfree(buf);
+	return err;
+}
+
+/**
+ * ubi_calculate_rsvd_pool - calculate how many PEBs must be reserved for bad
+ * eraseblock handling.
+ * @ubi: UBI device description object
+ */
+void ubi_calculate_reserved(struct ubi_device *ubi)
+{
+	ubi->beb_rsvd_level = ubi->good_peb_count/100;
+	ubi->beb_rsvd_level *= CONFIG_MTD_UBI_BEB_RESERVE;
+	if (ubi->beb_rsvd_level < MIN_RESEVED_PEBS)
+		ubi->beb_rsvd_level = MIN_RESEVED_PEBS;
+}
diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c
new file mode 100644
index 0000000..473f320
--- /dev/null
+++ b/drivers/mtd/ubi/scan.c
@@ -0,0 +1,1368 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * UBI scanning unit.
+ *
+ * This unit is responsible for scanning the flash media, checking UBI
+ * headers and providing complete information about the UBI flash image.
+ *
+ * The scanning information is reoresented by a &struct ubi_scan_info' object.
+ * Information about found volumes is represented by &struct ubi_scan_volume
+ * objects which are kept in volume RB-tree with root at the @volumes field.
+ * The RB-tree is indexed by the volume ID.
+ *
+ * Found logical eraseblocks are represented by &struct ubi_scan_leb objects.
+ * These objects are kept in per-volume RB-trees with the root at the
+ * corresponding &struct ubi_scan_volume object. To put it differently, we keep
+ * an RB-tree of per-volume objects and each of these objects is the root of
+ * RB-tree of per-eraseblock objects.
+ *
+ * Corrupted physical eraseblocks are put to the @corr list, free physical
+ * eraseblocks are put to the @free list and the physical eraseblock to be
+ * erased are put to the @erase list.
+ */
+
+#include <linux/err.h>
+#include <linux/crc32.h>
+#include "ubi.h"
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+static int paranoid_check_si(const struct ubi_device *ubi,
+			     struct ubi_scan_info *si);
+#else
+#define paranoid_check_si(ubi, si) 0
+#endif
+
+/* Temporary variables used during scanning */
+static struct ubi_ec_hdr *ech;
+static struct ubi_vid_hdr *vidh;
+
+int ubi_scan_add_to_list(struct ubi_scan_info *si, int pnum, int ec,
+			 struct list_head *list)
+{
+	struct ubi_scan_leb *seb;
+
+	if (list == &si->free)
+		dbg_bld("add to free: PEB %d, EC %d", pnum, ec);
+	else if (list == &si->erase)
+		dbg_bld("add to erase: PEB %d, EC %d", pnum, ec);
+	else if (list == &si->corr)
+		dbg_bld("add to corrupted: PEB %d, EC %d", pnum, ec);
+	else if (list == &si->alien)
+		dbg_bld("add to alien: PEB %d, EC %d", pnum, ec);
+	else
+		BUG();
+
+	seb = kmalloc(sizeof(struct ubi_scan_leb), GFP_KERNEL);
+	if (!seb)
+		return -ENOMEM;
+
+	seb->pnum = pnum;
+	seb->ec = ec;
+	list_add_tail(&seb->u.list, list);
+	return 0;
+}
+
+/**
+ * commit_to_mean_value - commit intermediate results to the final mean erase
+ * counter value.
+ * @si: scanning information
+ *
+ * This is a helper function which calculates partial mean erase counter mean
+ * value and adds it to the resulting mean value. As we can work only in
+ * integer arithmetic and we want to calculate the mean value of erase counter
+ * accurately, we first sum erase counter values in @si->ec_sum variable and
+ * count these components in @si->ec_count. If this temporary @si->ec_sum is
+ * going to overflow, we calculate the partial mean value
+ * (@si->ec_sum/@si->ec_count) and add it to @si->mean_ec.
+ */
+static void commit_to_mean_value(struct ubi_scan_info *si)
+{
+	si->ec_sum /= si->ec_count;
+	if (si->ec_sum % si->ec_count >= si->ec_count / 2)
+		si->mean_ec += 1;
+	si->mean_ec += si->ec_sum;
+}
+
+/**
+ * validate_vid_hdr - check that volume identifier header is correct and
+ * consistent.
+ * @vid_hdr: the volume identifier header to check
+ * @sv: information about the volume this logical eraseblock belongs to
+ * @pnum: physical eraseblock number the VID header came from
+ *
+ * This function checks that data stored in @vid_hdr is consistent. Returns
+ * non-zero if an inconsistency was found and zero if not.
+ *
+ * Note, UBI does sanity check of everything it reads from the flash media.
+ * Most of the checks are done in the I/O unit. Here we check that the
+ * information in the VID header is consistent to the information in other VID
+ * headers of the same volume.
+ */
+static int validate_vid_hdr(const struct ubi_vid_hdr *vid_hdr,
+			    const struct ubi_scan_volume *sv, int pnum)
+{
+	int vol_type = vid_hdr->vol_type;
+	int vol_id = ubi32_to_cpu(vid_hdr->vol_id);
+	int used_ebs = ubi32_to_cpu(vid_hdr->used_ebs);
+	int data_pad = ubi32_to_cpu(vid_hdr->data_pad);
+
+	if (sv->leb_count != 0) {
+		int sv_vol_type;
+
+		/*
+		 * This is not the first logical eraseblock belonging to this
+		 * volume. Ensure that the data in its VID header is consistent
+		 * to the data in previous logical eraseblock headers.
+		 */
+
+		if (vol_id != sv->vol_id) {
+			dbg_err("inconsistent vol_id");
+			goto bad;
+		}
+
+		if (sv->vol_type == UBI_STATIC_VOLUME)
+			sv_vol_type = UBI_VID_STATIC;
+		else
+			sv_vol_type = UBI_VID_DYNAMIC;
+
+		if (vol_type != sv_vol_type) {
+			dbg_err("inconsistent vol_type");
+			goto bad;
+		}
+
+		if (used_ebs != sv->used_ebs) {
+			dbg_err("inconsistent used_ebs");
+			goto bad;
+		}
+
+		if (data_pad != sv->data_pad) {
+			dbg_err("inconsistent data_pad");
+			goto bad;
+		}
+	}
+
+	return 0;
+
+bad:
+	ubi_err("inconsistent VID header at PEB %d", pnum);
+	ubi_dbg_dump_vid_hdr(vid_hdr);
+	ubi_dbg_dump_sv(sv);
+	return -EINVAL;
+}
+
+/**
+ * add_volume - add volume to the scanning information.
+ * @si: scanning information
+ * @vol_id: ID of the volume to add
+ * @pnum: physical eraseblock number
+ * @vid_hdr: volume identifier header
+ *
+ * If the volume corresponding to the @vid_hdr logical eraseblock is already
+ * present in the scanning information, this function does nothing. Otherwise
+ * it adds corresponding volume to the scanning information. Returns a pointer
+ * to the scanning volume object in case of success and a negative error code
+ * in case of failure.
+ */
+static struct ubi_scan_volume *add_volume(struct ubi_scan_info *si, int vol_id,
+					  int pnum,
+					  const struct ubi_vid_hdr *vid_hdr)
+{
+	struct ubi_scan_volume *sv;
+	struct rb_node **p = &si->volumes.rb_node, *parent = NULL;
+
+	ubi_assert(vol_id == ubi32_to_cpu(vid_hdr->vol_id));
+
+	/* Walk the volume RB-tree to look if this volume is already present */
+	while (*p) {
+		parent = *p;
+		sv = rb_entry(parent, struct ubi_scan_volume, rb);
+
+		if (vol_id == sv->vol_id)
+			return sv;
+
+		if (vol_id > sv->vol_id)
+			p = &(*p)->rb_left;
+		else
+			p = &(*p)->rb_right;
+	}
+
+	/* The volume is absent - add it */
+	sv = kmalloc(sizeof(struct ubi_scan_volume), GFP_KERNEL);
+	if (!sv)
+		return ERR_PTR(-ENOMEM);
+
+	sv->highest_lnum = sv->leb_count = 0;
+	si->max_sqnum = 0;
+	sv->vol_id = vol_id;
+	sv->root = RB_ROOT;
+	sv->used_ebs = ubi32_to_cpu(vid_hdr->used_ebs);
+	sv->data_pad = ubi32_to_cpu(vid_hdr->data_pad);
+	sv->compat = vid_hdr->compat;
+	sv->vol_type = vid_hdr->vol_type == UBI_VID_DYNAMIC ? UBI_DYNAMIC_VOLUME
+							    : UBI_STATIC_VOLUME;
+	if (vol_id > si->highest_vol_id)
+		si->highest_vol_id = vol_id;
+
+	rb_link_node(&sv->rb, parent, p);
+	rb_insert_color(&sv->rb, &si->volumes);
+	si->vols_found += 1;
+	dbg_bld("added volume %d", vol_id);
+	return sv;
+}
+
+/**
+ * compare_lebs - find out which logical eraseblock is newer.
+ * @ubi: UBI device description object
+ * @seb: first logical eraseblock to compare
+ * @pnum: physical eraseblock number of the second logical eraseblock to
+ * compare
+ * @vid_hdr: volume identifier header of the second logical eraseblock
+ *
+ * This function compares 2 copies of a LEB and informs which one is newer. In
+ * case of success this function returns a positive value, in case of failure, a
+ * negative error code is returned. The success return codes use the following
+ * bits:
+ *     o bit 0 is cleared: the first PEB (described by @seb) is newer then the
+ *       second PEB (described by @pnum and @vid_hdr);
+ *     o bit 0 is set: the second PEB is newer;
+ *     o bit 1 is cleared: no bit-flips were detected in the newer LEB;
+ *     o bit 1 is set: bit-flips were detected in the newer LEB;
+ *     o bit 2 is cleared: the older LEB is not corrupted;
+ *     o bit 2 is set: the older LEB is corrupted.
+ */
+static int compare_lebs(const struct ubi_device *ubi,
+			const struct ubi_scan_leb *seb, int pnum,
+			const struct ubi_vid_hdr *vid_hdr)
+{
+	void *buf;
+	int len, err, second_is_newer, bitflips = 0, corrupted = 0;
+	uint32_t data_crc, crc;
+	struct ubi_vid_hdr *vidh = NULL;
+	unsigned long long sqnum2 = ubi64_to_cpu(vid_hdr->sqnum);
+
+	if (seb->sqnum == 0 && sqnum2 == 0) {
+		long long abs, v1 = seb->leb_ver, v2 = ubi32_to_cpu(vid_hdr->leb_ver);
+
+		/*
+		 * UBI constantly increases the logical eraseblock version
+		 * number and it can overflow. Thus, we have to bear in mind
+		 * that versions that are close to %0xFFFFFFFF are less then
+		 * versions that are close to %0.
+		 *
+		 * The UBI WL unit guarantees that the number of pending tasks
+		 * is not greater then %0x7FFFFFFF. So, if the difference
+		 * between any two versions is greater or equivalent to
+		 * %0x7FFFFFFF, there was an overflow and the logical
+		 * eraseblock with lower version is actually newer then the one
+		 * with higher version.
+		 *
+		 * FIXME: but this is anyway obsolete and will be removed at
+		 * some point.
+		 */
+
+		dbg_bld("using old crappy leb_ver stuff");
+
+		abs = v1 - v2;
+		if (abs < 0)
+			abs = -abs;
+
+		if (abs < 0x7FFFFFFF)
+			/* Non-overflow situation */
+			second_is_newer = (v2 > v1);
+		else
+			second_is_newer = (v2 < v1);
+	} else
+		/* Obviously the LEB with lower sequence counter is older */
+		second_is_newer = sqnum2 > seb->sqnum;
+
+	/*
+	 * Now we know which copy is newer. If the copy flag of the PEB with
+	 * newer version is not set, then we just return, otherwise we have to
+	 * check data CRC. For the second PEB we already have the VID header,
+	 * for the first one - we'll need to re-read it from flash.
+	 *
+	 * FIXME: this may be optimized so that we wouldn't read twice.
+	 */
+
+	if (second_is_newer) {
+		if (!vid_hdr->copy_flag) {
+			/* It is not a copy, so it is newer */
+			dbg_bld("second PEB %d is newer, copy_flag is unset",
+				pnum);
+			return 1;
+		}
+	} else {
+		pnum = seb->pnum;
+
+		vidh = ubi_zalloc_vid_hdr(ubi);
+		if (!vidh)
+			return -ENOMEM;
+
+		err = ubi_io_read_vid_hdr(ubi, pnum, vidh, 0);
+		if (err) {
+			if (err == UBI_IO_BITFLIPS)
+				bitflips = 1;
+			else {
+				dbg_err("VID of PEB %d header is bad, but it "
+					"was OK earlier", pnum);
+				if (err > 0)
+					err = -EIO;
+
+				goto out_free_vidh;
+			}
+		}
+
+		if (!vidh->copy_flag) {
+			/* It is not a copy, so it is newer */
+			dbg_bld("first PEB %d is newer, copy_flag is unset",
+				pnum);
+			err = bitflips << 1;
+			goto out_free_vidh;
+		}
+
+		vid_hdr = vidh;
+	}
+
+	/* Read the data of the copy and check the CRC */
+
+	len = ubi32_to_cpu(vid_hdr->data_size);
+	buf = kmalloc(len, GFP_KERNEL);
+	if (!buf) {
+		err = -ENOMEM;
+		goto out_free_vidh;
+	}
+
+	err = ubi_io_read_data(ubi, buf, pnum, 0, len);
+	if (err && err != UBI_IO_BITFLIPS)
+		goto out_free_buf;
+
+	data_crc = ubi32_to_cpu(vid_hdr->data_crc);
+	crc = crc32(UBI_CRC32_INIT, buf, len);
+	if (crc != data_crc) {
+		dbg_bld("PEB %d CRC error: calculated %#08x, must be %#08x",
+			pnum, crc, data_crc);
+		corrupted = 1;
+		bitflips = 0;
+		second_is_newer = !second_is_newer;
+	} else {
+		dbg_bld("PEB %d CRC is OK", pnum);
+		bitflips = !!err;
+	}
+
+	kfree(buf);
+	ubi_free_vid_hdr(ubi, vidh);
+
+	if (second_is_newer)
+		dbg_bld("second PEB %d is newer, copy_flag is set", pnum);
+	else
+		dbg_bld("first PEB %d is newer, copy_flag is set", pnum);
+
+	return second_is_newer | (bitflips << 1) | (corrupted << 2);
+
+out_free_buf:
+	kfree(buf);
+out_free_vidh:
+	ubi_free_vid_hdr(ubi, vidh);
+	ubi_assert(err < 0);
+	return err;
+}
+
+/**
+ * ubi_scan_add_used - add information about a physical eraseblock to the
+ * scanning information.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ * @pnum: the physical eraseblock number
+ * @ec: erase counter
+ * @vid_hdr: the volume identifier header
+ * @bitflips: if bit-flips were detected when this physical eraseblock was read
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+int ubi_scan_add_used(const struct ubi_device *ubi, struct ubi_scan_info *si,
+		      int pnum, int ec, const struct ubi_vid_hdr *vid_hdr,
+		      int bitflips)
+{
+	int err, vol_id, lnum;
+	uint32_t leb_ver;
+	unsigned long long sqnum;
+	struct ubi_scan_volume *sv;
+	struct ubi_scan_leb *seb;
+	struct rb_node **p, *parent = NULL;
+
+	vol_id = ubi32_to_cpu(vid_hdr->vol_id);
+	lnum = ubi32_to_cpu(vid_hdr->lnum);
+	sqnum = ubi64_to_cpu(vid_hdr->sqnum);
+	leb_ver = ubi32_to_cpu(vid_hdr->leb_ver);
+
+	dbg_bld("PEB %d, LEB %d:%d, EC %d, sqnum %llu, ver %u, bitflips %d",
+		pnum, vol_id, lnum, ec, sqnum, leb_ver, bitflips);
+
+	sv = add_volume(si, vol_id, pnum, vid_hdr);
+	if (IS_ERR(sv) < 0)
+		return PTR_ERR(sv);
+
+	/*
+	 * Walk the RB-tree of logical eraseblocks of volume @vol_id to look
+	 * if this is the first instance of this logical eraseblock or not.
+	 */
+	p = &sv->root.rb_node;
+	while (*p) {
+		int cmp_res;
+
+		parent = *p;
+		seb = rb_entry(parent, struct ubi_scan_leb, u.rb);
+		if (lnum != seb->lnum) {
+			if (lnum < seb->lnum)
+				p = &(*p)->rb_left;
+			else
+				p = &(*p)->rb_right;
+			continue;
+		}
+
+		/*
+		 * There is already a physical eraseblock describing the same
+		 * logical eraseblock present.
+		 */
+
+		dbg_bld("this LEB already exists: PEB %d, sqnum %llu, "
+			"LEB ver %u, EC %d", seb->pnum, seb->sqnum,
+			seb->leb_ver, seb->ec);
+
+		/*
+		 * Make sure that the logical eraseblocks have different
+		 * versions. Otherwise the image is bad.
+		 */
+		if (seb->leb_ver == leb_ver && leb_ver != 0) {
+			ubi_err("two LEBs with same version %u", leb_ver);
+			ubi_dbg_dump_seb(seb, 0);
+			ubi_dbg_dump_vid_hdr(vid_hdr);
+			return -EINVAL;
+		}
+
+		/*
+		 * Make sure that the logical eraseblocks have different
+		 * sequence numbers. Otherwise the image is bad.
+		 *
+		 * FIXME: remove 'sqnum != 0' check when leb_ver is removed.
+		 */
+		if (seb->sqnum == sqnum && sqnum != 0) {
+			ubi_err("two LEBs with same sequence number %llu",
+				sqnum);
+			ubi_dbg_dump_seb(seb, 0);
+			ubi_dbg_dump_vid_hdr(vid_hdr);
+			return -EINVAL;
+		}
+
+		/*
+		 * Now we have to drop the older one and preserve the newer
+		 * one.
+		 */
+		cmp_res = compare_lebs(ubi, seb, pnum, vid_hdr);
+		if (cmp_res < 0)
+			return cmp_res;
+
+		if (cmp_res & 1) {
+			/*
+			 * This logical eraseblock is newer then the one
+			 * found earlier.
+			 */
+			err = validate_vid_hdr(vid_hdr, sv, pnum);
+			if (err)
+				return err;
+
+			if (cmp_res & 4)
+				err = ubi_scan_add_to_list(si, seb->pnum,
+							   seb->ec, &si->corr);
+			else
+				err = ubi_scan_add_to_list(si, seb->pnum,
+							   seb->ec, &si->erase);
+			if (err)
+				return err;
+
+			seb->ec = ec;
+			seb->pnum = pnum;
+			seb->scrub = ((cmp_res & 2) || bitflips);
+			seb->sqnum = sqnum;
+			seb->leb_ver = leb_ver;
+
+			if (sv->highest_lnum == lnum)
+				sv->last_data_size =
+					ubi32_to_cpu(vid_hdr->data_size);
+
+			return 0;
+		} else {
+			/*
+			 * This logical eraseblock is older then the one found
+			 * previously.
+			 */
+			if (cmp_res & 4)
+				return ubi_scan_add_to_list(si, pnum, ec,
+							    &si->corr);
+			else
+				return ubi_scan_add_to_list(si, pnum, ec,
+							    &si->erase);
+		}
+	}
+
+	/*
+	 * We've met this logical eraseblock for the first time, add it to the
+	 * scanning information.
+	 */
+
+	err = validate_vid_hdr(vid_hdr, sv, pnum);
+	if (err)
+		return err;
+
+	seb = kmalloc(sizeof(struct ubi_scan_leb), GFP_KERNEL);
+	if (!seb)
+		return -ENOMEM;
+
+	seb->ec = ec;
+	seb->pnum = pnum;
+	seb->lnum = lnum;
+	seb->sqnum = sqnum;
+	seb->scrub = bitflips;
+	seb->leb_ver = leb_ver;
+
+	if (sv->highest_lnum <= lnum) {
+		sv->highest_lnum = lnum;
+		sv->last_data_size = ubi32_to_cpu(vid_hdr->data_size);
+	}
+
+	if (si->max_sqnum < sqnum)
+		si->max_sqnum = sqnum;
+
+	sv->leb_count += 1;
+	rb_link_node(&seb->u.rb, parent, p);
+	rb_insert_color(&seb->u.rb, &sv->root);
+	return 0;
+}
+
+/**
+ * ubi_scan_find_sv - find information about a particular volume in the
+ * scanning information.
+ * @si: scanning information
+ * @vol_id: the requested volume ID
+ *
+ * This function returns a pointer to the volume description or %NULL if there
+ * are no data about this volume in the scanning information.
+ */
+struct ubi_scan_volume *ubi_scan_find_sv(const struct ubi_scan_info *si,
+					 int vol_id)
+{
+	struct ubi_scan_volume *sv;
+	struct rb_node *p = si->volumes.rb_node;
+
+	while (p) {
+		sv = rb_entry(p, struct ubi_scan_volume, rb);
+
+		if (vol_id == sv->vol_id)
+			return sv;
+
+		if (vol_id > sv->vol_id)
+			p = p->rb_left;
+		else
+			p = p->rb_right;
+	}
+
+	return NULL;
+}
+
+/**
+ * ubi_scan_find_seb - find information about a particular logical
+ * eraseblock in the volume scanning information.
+ * @sv: a pointer to the volume scanning information
+ * @lnum: the requested logical eraseblock
+ *
+ * This function returns a pointer to the scanning logical eraseblock or %NULL
+ * if there are no data about it in the scanning volume information.
+ */
+struct ubi_scan_leb *ubi_scan_find_seb(const struct ubi_scan_volume *sv,
+				       int lnum)
+{
+	struct ubi_scan_leb *seb;
+	struct rb_node *p = sv->root.rb_node;
+
+	while (p) {
+		seb = rb_entry(p, struct ubi_scan_leb, u.rb);
+
+		if (lnum == seb->lnum)
+			return seb;
+
+		if (lnum > seb->lnum)
+			p = p->rb_left;
+		else
+			p = p->rb_right;
+	}
+
+	return NULL;
+}
+
+/**
+ * ubi_scan_rm_volume - delete scanning information about a volume.
+ * @si: scanning information
+ * @sv: the volume scanning information to delete
+ */
+void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv)
+{
+	struct rb_node *rb;
+	struct ubi_scan_leb *seb;
+
+	dbg_bld("remove scanning information about volume %d", sv->vol_id);
+
+	while ((rb = rb_first(&sv->root))) {
+		seb = rb_entry(rb, struct ubi_scan_leb, u.rb);
+		rb_erase(&seb->u.rb, &sv->root);
+		list_add_tail(&seb->u.list, &si->erase);
+	}
+
+	rb_erase(&sv->rb, &si->volumes);
+	kfree(sv);
+	si->vols_found -= 1;
+}
+
+/**
+ * ubi_scan_erase_peb - erase a physical eraseblock.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ * @pnum: physical eraseblock number to erase;
+ * @ec: erase counter value to write (%UBI_SCAN_UNKNOWN_EC if it is unknown)
+ *
+ * This function erases physical eraseblock 'pnum', and writes the erase
+ * counter header to it. This function should only be used on UBI device
+ * initialization stages, when the EBA unit had not been yet initialized. This
+ * function returns zero in case of success and a negative error code in case
+ * of failure.
+ */
+int ubi_scan_erase_peb(const struct ubi_device *ubi,
+		       const struct ubi_scan_info *si, int pnum, int ec)
+{
+	int err;
+	struct ubi_ec_hdr *ec_hdr;
+
+	ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
+	if (!ec_hdr)
+		return -ENOMEM;
+
+	if ((long long)ec >= UBI_MAX_ERASECOUNTER) {
+		/*
+		 * Erase counter overflow. Upgrade UBI and use 64-bit
+		 * erase counters internally.
+		 */
+		ubi_err("erase counter overflow at PEB %d, EC %d", pnum, ec);
+		return -EINVAL;
+	}
+
+	ec_hdr->ec = cpu_to_ubi64(ec);
+
+	err = ubi_io_sync_erase(ubi, pnum, 0);
+	if (err < 0)
+		goto out_free;
+
+	err = ubi_io_write_ec_hdr(ubi, pnum, ec_hdr);
+
+out_free:
+	kfree(ec_hdr);
+	return err;
+}
+
+/**
+ * ubi_scan_get_free_peb - get a free physical eraseblock.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ *
+ * This function returns a free physical eraseblock. It is supposed to be
+ * called on the UBI initialization stages when the wear-leveling unit is not
+ * initialized yet. This function picks a physical eraseblocks from one of the
+ * lists, writes the EC header if it is needed, and removes it from the list.
+ *
+ * This function returns scanning physical eraseblock information in case of
+ * success and an error code in case of failure.
+ */
+struct ubi_scan_leb *ubi_scan_get_free_peb(const struct ubi_device *ubi,
+					   struct ubi_scan_info *si)
+{
+	int err = 0, i;
+	struct ubi_scan_leb *seb;
+
+	if (!list_empty(&si->free)) {
+		seb = list_entry(si->free.next, struct ubi_scan_leb, u.list);
+		list_del(&seb->u.list);
+		dbg_bld("return free PEB %d, EC %d", seb->pnum, seb->ec);
+		return seb;
+	}
+
+	for (i = 0; i < 2; i++) {
+		struct list_head *head;
+		struct ubi_scan_leb *tmp_seb;
+
+		if (i == 0)
+			head = &si->erase;
+		else
+			head = &si->corr;
+
+		/*
+		 * We try to erase the first physical eraseblock from the @head
+		 * list and pick it if we succeed, or try to erase the
+		 * next one if not. And so forth. We don't want to take care
+		 * about bad eraseblocks here - they'll be handled later.
+		 */
+		list_for_each_entry_safe(seb, tmp_seb, head, u.list) {
+			if (seb->ec == UBI_SCAN_UNKNOWN_EC)
+				seb->ec = si->mean_ec;
+
+			err = ubi_scan_erase_peb(ubi, si, seb->pnum, seb->ec+1);
+			if (err)
+				continue;
+
+			seb->ec += 1;
+			list_del(&seb->u.list);
+			dbg_bld("return PEB %d, EC %d", seb->pnum, seb->ec);
+			return seb;
+		}
+	}
+
+	ubi_err("no eraseblocks found");
+	return ERR_PTR(-ENOSPC);
+}
+
+/**
+ * process_eb - read UBI headers, check them and add corresponding data
+ * to the scanning information.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ * @pnum: the physical eraseblock number
+ *
+ * This function returns a zero if the physical eraseblock was succesfully
+ * handled and a negative error code in case of failure.
+ */
+static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum)
+{
+	long long ec;
+	int err, bitflips = 0, vol_id, ec_corr = 0;
+
+	dbg_bld("scan PEB %d", pnum);
+
+	/* Skip bad physical eraseblocks */
+	err = ubi_io_is_bad(ubi, pnum);
+	if (err < 0)
+		return err;
+	else if (err) {
+		/*
+		 * FIXME: this is actually duty of the I/O unit to initialize
+		 * this, but MTD does not provide enough information.
+		 */
+		si->bad_peb_count += 1;
+		return 0;
+	}
+
+	err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0);
+	if (err < 0)
+		return err;
+	else if (err == UBI_IO_BITFLIPS)
+		bitflips = 1;
+	else if (err == UBI_IO_PEB_EMPTY)
+		return ubi_scan_add_to_list(si, pnum, UBI_SCAN_UNKNOWN_EC,
+					    &si->erase);
+	else if (err == UBI_IO_BAD_EC_HDR) {
+		/*
+		 * We have to also look at the VID header, possibly it is not
+		 * corrupted. Set %bitflips flag in order to make this PEB be
+		 * moved and EC be re-created.
+		 */
+		ec_corr = 1;
+		ec = UBI_SCAN_UNKNOWN_EC;
+		bitflips = 1;
+	}
+
+	si->is_empty = 0;
+
+	if (!ec_corr) {
+		/* Make sure UBI version is OK */
+		if (ech->version != UBI_VERSION) {
+			ubi_err("this UBI version is %d, image version is %d",
+				UBI_VERSION, (int)ech->version);
+			return -EINVAL;
+		}
+
+		ec = ubi64_to_cpu(ech->ec);
+		if (ec > UBI_MAX_ERASECOUNTER) {
+			/*
+			 * Erase counter overflow. The EC headers have 64 bits
+			 * reserved, but we anyway make use of only 31 bit
+			 * values, as this seems to be enough for any existing
+			 * flash. Upgrade UBI and use 64-bit erase counters
+			 * internally.
+			 */
+			ubi_err("erase counter overflow, max is %d",
+				UBI_MAX_ERASECOUNTER);
+			ubi_dbg_dump_ec_hdr(ech);
+			return -EINVAL;
+		}
+	}
+
+	/* OK, we've done with the EC header, let's look at the VID header */
+
+	err = ubi_io_read_vid_hdr(ubi, pnum, vidh, 0);
+	if (err < 0)
+		return err;
+	else if (err == UBI_IO_BITFLIPS)
+		bitflips = 1;
+	else if (err == UBI_IO_BAD_VID_HDR ||
+		 (err == UBI_IO_PEB_FREE && ec_corr)) {
+		/* VID header is corrupted */
+		err = ubi_scan_add_to_list(si, pnum, ec, &si->corr);
+		if (err)
+			return err;
+		goto adjust_mean_ec;
+	} else if (err == UBI_IO_PEB_FREE) {
+		/* No VID header - the physical eraseblock is free */
+		err = ubi_scan_add_to_list(si, pnum, ec, &si->free);
+		if (err)
+			return err;
+		goto adjust_mean_ec;
+	}
+
+	vol_id = ubi32_to_cpu(vidh->vol_id);
+	if (vol_id > UBI_MAX_VOLUMES && vol_id != UBI_LAYOUT_VOL_ID) {
+		int lnum = ubi32_to_cpu(vidh->lnum);
+
+		/* Unsupported internal volume */
+		switch (vidh->compat) {
+		case UBI_COMPAT_DELETE:
+			ubi_msg("\"delete\" compatible internal volume %d:%d"
+				" found, remove it", vol_id, lnum);
+			err = ubi_scan_add_to_list(si, pnum, ec, &si->corr);
+			if (err)
+				return err;
+			break;
+
+		case UBI_COMPAT_RO:
+			ubi_msg("read-only compatible internal volume %d:%d"
+				" found, switch to read-only mode",
+				vol_id, lnum);
+			ubi->ro_mode = 1;
+			break;
+
+		case UBI_COMPAT_PRESERVE:
+			ubi_msg("\"preserve\" compatible internal volume %d:%d"
+				" found", vol_id, lnum);
+			err = ubi_scan_add_to_list(si, pnum, ec, &si->alien);
+			if (err)
+				return err;
+			si->alien_peb_count += 1;
+			return 0;
+
+		case UBI_COMPAT_REJECT:
+			ubi_err("incompatible internal volume %d:%d found",
+				vol_id, lnum);
+			return -EINVAL;
+		}
+	}
+
+	/* Both UBI headers seem to be fine */
+	err = ubi_scan_add_used(ubi, si, pnum, ec, vidh, bitflips);
+	if (err)
+		return err;
+
+adjust_mean_ec:
+	if (!ec_corr) {
+		if (si->ec_sum + ec < ec) {
+			commit_to_mean_value(si);
+			si->ec_sum = 0;
+			si->ec_count = 0;
+		} else {
+			si->ec_sum += ec;
+			si->ec_count += 1;
+		}
+
+		if (ec > si->max_ec)
+			si->max_ec = ec;
+		if (ec < si->min_ec)
+			si->min_ec = ec;
+	}
+
+	return 0;
+}
+
+/**
+ * ubi_scan - scan an MTD device.
+ * @ubi: UBI device description object
+ *
+ * This function does full scanning of an MTD device and returns complete
+ * information about it. In case of failure, an error code is returned.
+ */
+struct ubi_scan_info *ubi_scan(struct ubi_device *ubi)
+{
+	int err, pnum;
+	struct rb_node *rb1, *rb2;
+	struct ubi_scan_volume *sv;
+	struct ubi_scan_leb *seb;
+	struct ubi_scan_info *si;
+
+	si = kzalloc(sizeof(struct ubi_scan_info), GFP_KERNEL);
+	if (!si)
+		return ERR_PTR(-ENOMEM);
+
+	INIT_LIST_HEAD(&si->corr);
+	INIT_LIST_HEAD(&si->free);
+	INIT_LIST_HEAD(&si->erase);
+	INIT_LIST_HEAD(&si->alien);
+	si->volumes = RB_ROOT;
+	si->is_empty = 1;
+
+	err = -ENOMEM;
+	ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
+	if (!ech)
+		goto out_si;
+
+	vidh = ubi_zalloc_vid_hdr(ubi);
+	if (!vidh)
+		goto out_ech;
+
+	for (pnum = 0; pnum < ubi->peb_count; pnum++) {
+		cond_resched();
+
+		dbg_msg("process PEB %d", pnum);
+		err = process_eb(ubi, si, pnum);
+		if (err < 0)
+			goto out_vidh;
+	}
+
+	dbg_msg("scanning is finished");
+
+	/* Finish mean erase counter calculations */
+	if (si->ec_count)
+		commit_to_mean_value(si);
+
+	if (si->is_empty)
+		ubi_msg("empty MTD device detected");
+
+	/*
+	 * In case of unknown erase counter we use the mean erase counter
+	 * value.
+	 */
+	ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) {
+		ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb)
+			if (seb->ec == UBI_SCAN_UNKNOWN_EC)
+				seb->ec = si->mean_ec;
+	}
+
+	list_for_each_entry(seb, &si->free, u.list) {
+		if (seb->ec == UBI_SCAN_UNKNOWN_EC)
+			seb->ec = si->mean_ec;
+	}
+
+	list_for_each_entry(seb, &si->corr, u.list)
+		if (seb->ec == UBI_SCAN_UNKNOWN_EC)
+			seb->ec = si->mean_ec;
+
+	list_for_each_entry(seb, &si->erase, u.list)
+		if (seb->ec == UBI_SCAN_UNKNOWN_EC)
+			seb->ec = si->mean_ec;
+
+	err = paranoid_check_si(ubi, si);
+	if (err) {
+		if (err > 0)
+			err = -EINVAL;
+		goto out_vidh;
+	}
+
+	ubi_free_vid_hdr(ubi, vidh);
+	kfree(ech);
+
+	return si;
+
+out_vidh:
+	ubi_free_vid_hdr(ubi, vidh);
+out_ech:
+	kfree(ech);
+out_si:
+	ubi_scan_destroy_si(si);
+	return ERR_PTR(err);
+}
+
+/**
+ * destroy_sv - free the scanning volume information
+ * @sv: scanning volume information
+ *
+ * This function destroys the volume RB-tree (@sv->root) and the scanning
+ * volume information.
+ */
+static void destroy_sv(struct ubi_scan_volume *sv)
+{
+	struct ubi_scan_leb *seb;
+	struct rb_node *this = sv->root.rb_node;
+
+	while (this) {
+		if (this->rb_left)
+			this = this->rb_left;
+		else if (this->rb_right)
+			this = this->rb_right;
+		else {
+			seb = rb_entry(this, struct ubi_scan_leb, u.rb);
+			this = rb_parent(this);
+			if (this) {
+				if (this->rb_left == &seb->u.rb)
+					this->rb_left = NULL;
+				else
+					this->rb_right = NULL;
+			}
+
+			kfree(seb);
+		}
+	}
+	kfree(sv);
+}
+
+/**
+ * ubi_scan_destroy_si - destroy scanning information.
+ * @si: scanning information
+ */
+void ubi_scan_destroy_si(struct ubi_scan_info *si)
+{
+	struct ubi_scan_leb *seb, *seb_tmp;
+	struct ubi_scan_volume *sv;
+	struct rb_node *rb;
+
+	list_for_each_entry_safe(seb, seb_tmp, &si->alien, u.list) {
+		list_del(&seb->u.list);
+		kfree(seb);
+	}
+	list_for_each_entry_safe(seb, seb_tmp, &si->erase, u.list) {
+		list_del(&seb->u.list);
+		kfree(seb);
+	}
+	list_for_each_entry_safe(seb, seb_tmp, &si->corr, u.list) {
+		list_del(&seb->u.list);
+		kfree(seb);
+	}
+	list_for_each_entry_safe(seb, seb_tmp, &si->free, u.list) {
+		list_del(&seb->u.list);
+		kfree(seb);
+	}
+
+	/* Destroy the volume RB-tree */
+	rb = si->volumes.rb_node;
+	while (rb) {
+		if (rb->rb_left)
+			rb = rb->rb_left;
+		else if (rb->rb_right)
+			rb = rb->rb_right;
+		else {
+			sv = rb_entry(rb, struct ubi_scan_volume, rb);
+
+			rb = rb_parent(rb);
+			if (rb) {
+				if (rb->rb_left == &sv->rb)
+					rb->rb_left = NULL;
+				else
+					rb->rb_right = NULL;
+			}
+
+			destroy_sv(sv);
+		}
+	}
+
+	kfree(si);
+}
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+
+/**
+ * paranoid_check_si - check if the scanning information is correct and
+ * consistent.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ *
+ * This function returns zero if the scanning information is all right, %1 if
+ * not and a negative error code if an error occurred.
+ */
+static int paranoid_check_si(const struct ubi_device *ubi,
+			     struct ubi_scan_info *si)
+{
+	int pnum, err, vols_found = 0;
+	struct rb_node *rb1, *rb2;
+	struct ubi_scan_volume *sv;
+	struct ubi_scan_leb *seb, *last_seb;
+	uint8_t *buf;
+
+	/*
+	 * At first, check that scanning information is ok.
+	 */
+	ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) {
+		int leb_count = 0;
+
+		cond_resched();
+
+		vols_found += 1;
+
+		if (si->is_empty) {
+			ubi_err("bad is_empty flag");
+			goto bad_sv;
+		}
+
+		if (sv->vol_id < 0 || sv->highest_lnum < 0 ||
+		    sv->leb_count < 0 || sv->vol_type < 0 || sv->used_ebs < 0 ||
+		    sv->data_pad < 0 || sv->last_data_size < 0) {
+			ubi_err("negative values");
+			goto bad_sv;
+		}
+
+		if (sv->vol_id >= UBI_MAX_VOLUMES &&
+		    sv->vol_id < UBI_INTERNAL_VOL_START) {
+			ubi_err("bad vol_id");
+			goto bad_sv;
+		}
+
+		if (sv->vol_id > si->highest_vol_id) {
+			ubi_err("highest_vol_id is %d, but vol_id %d is there",
+				si->highest_vol_id, sv->vol_id);
+			goto out;
+		}
+
+		if (sv->vol_type != UBI_DYNAMIC_VOLUME &&
+		    sv->vol_type != UBI_STATIC_VOLUME) {
+			ubi_err("bad vol_type");
+			goto bad_sv;
+		}
+
+		if (sv->data_pad > ubi->leb_size / 2) {
+			ubi_err("bad data_pad");
+			goto bad_sv;
+		}
+
+		last_seb = NULL;
+		ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) {
+			cond_resched();
+
+			last_seb = seb;
+			leb_count += 1;
+
+			if (seb->pnum < 0 || seb->ec < 0) {
+				ubi_err("negative values");
+				goto bad_seb;
+			}
+
+			if (seb->ec < si->min_ec) {
+				ubi_err("bad si->min_ec (%d), %d found",
+					si->min_ec, seb->ec);
+				goto bad_seb;
+			}
+
+			if (seb->ec > si->max_ec) {
+				ubi_err("bad si->max_ec (%d), %d found",
+					si->max_ec, seb->ec);
+				goto bad_seb;
+			}
+
+			if (seb->pnum >= ubi->peb_count) {
+				ubi_err("too high PEB number %d, total PEBs %d",
+					seb->pnum, ubi->peb_count);
+				goto bad_seb;
+			}
+
+			if (sv->vol_type == UBI_STATIC_VOLUME) {
+				if (seb->lnum >= sv->used_ebs) {
+					ubi_err("bad lnum or used_ebs");
+					goto bad_seb;
+				}
+			} else {
+				if (sv->used_ebs != 0) {
+					ubi_err("non-zero used_ebs");
+					goto bad_seb;
+				}
+			}
+
+			if (seb->lnum > sv->highest_lnum) {
+				ubi_err("incorrect highest_lnum or lnum");
+				goto bad_seb;
+			}
+		}
+
+		if (sv->leb_count != leb_count) {
+			ubi_err("bad leb_count, %d objects in the tree",
+				leb_count);
+			goto bad_sv;
+		}
+
+		if (!last_seb)
+			continue;
+
+		seb = last_seb;
+
+		if (seb->lnum != sv->highest_lnum) {
+			ubi_err("bad highest_lnum");
+			goto bad_seb;
+		}
+	}
+
+	if (vols_found != si->vols_found) {
+		ubi_err("bad si->vols_found %d, should be %d",
+			si->vols_found, vols_found);
+		goto out;
+	}
+
+	/* Check that scanning information is correct */
+	ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) {
+		last_seb = NULL;
+		ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) {
+			int vol_type;
+
+			cond_resched();
+
+			last_seb = seb;
+
+			err = ubi_io_read_vid_hdr(ubi, seb->pnum, vidh, 1);
+			if (err && err != UBI_IO_BITFLIPS) {
+				ubi_err("VID header is not OK (%d)", err);
+				if (err > 0)
+					err = -EIO;
+				return err;
+			}
+
+			vol_type = vidh->vol_type == UBI_VID_DYNAMIC ?
+				   UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME;
+			if (sv->vol_type != vol_type) {
+				ubi_err("bad vol_type");
+				goto bad_vid_hdr;
+			}
+
+			if (seb->sqnum != ubi64_to_cpu(vidh->sqnum)) {
+				ubi_err("bad sqnum %llu", seb->sqnum);
+				goto bad_vid_hdr;
+			}
+
+			if (sv->vol_id != ubi32_to_cpu(vidh->vol_id)) {
+				ubi_err("bad vol_id %d", sv->vol_id);
+				goto bad_vid_hdr;
+			}
+
+			if (sv->compat != vidh->compat) {
+				ubi_err("bad compat %d", vidh->compat);
+				goto bad_vid_hdr;
+			}
+
+			if (seb->lnum != ubi32_to_cpu(vidh->lnum)) {
+				ubi_err("bad lnum %d", seb->lnum);
+				goto bad_vid_hdr;
+			}
+
+			if (sv->used_ebs != ubi32_to_cpu(vidh->used_ebs)) {
+				ubi_err("bad used_ebs %d", sv->used_ebs);
+				goto bad_vid_hdr;
+			}
+
+			if (sv->data_pad != ubi32_to_cpu(vidh->data_pad)) {
+				ubi_err("bad data_pad %d", sv->data_pad);
+				goto bad_vid_hdr;
+			}
+
+			if (seb->leb_ver != ubi32_to_cpu(vidh->leb_ver)) {
+				ubi_err("bad leb_ver %u", seb->leb_ver);
+				goto bad_vid_hdr;
+			}
+		}
+
+		if (!last_seb)
+			continue;
+
+		if (sv->highest_lnum != ubi32_to_cpu(vidh->lnum)) {
+			ubi_err("bad highest_lnum %d", sv->highest_lnum);
+			goto bad_vid_hdr;
+		}
+
+		if (sv->last_data_size != ubi32_to_cpu(vidh->data_size)) {
+			ubi_err("bad last_data_size %d", sv->last_data_size);
+			goto bad_vid_hdr;
+		}
+	}
+
+	/*
+	 * Make sure that all the physical eraseblocks are in one of the lists
+	 * or trees.
+	 */
+	buf = kmalloc(ubi->peb_count, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	memset(buf, 1, ubi->peb_count);
+	for (pnum = 0; pnum < ubi->peb_count; pnum++) {
+		err = ubi_io_is_bad(ubi, pnum);
+		if (err < 0)
+			return err;
+		else if (err)
+			buf[pnum] = 0;
+	}
+
+	ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb)
+		ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb)
+			buf[seb->pnum] = 0;
+
+	list_for_each_entry(seb, &si->free, u.list)
+		buf[seb->pnum] = 0;
+
+	list_for_each_entry(seb, &si->corr, u.list)
+		buf[seb->pnum] = 0;
+
+	list_for_each_entry(seb, &si->erase, u.list)
+		buf[seb->pnum] = 0;
+
+	list_for_each_entry(seb, &si->alien, u.list)
+		buf[seb->pnum] = 0;
+
+	err = 0;
+	for (pnum = 0; pnum < ubi->peb_count; pnum++)
+		if (buf[pnum]) {
+			ubi_err("PEB %d is not referred", pnum);
+			err = 1;
+		}
+
+	kfree(buf);
+	if (err)
+		goto out;
+	return 0;
+
+bad_seb:
+	ubi_err("bad scanning information about LEB %d", seb->lnum);
+	ubi_dbg_dump_seb(seb, 0);
+	ubi_dbg_dump_sv(sv);
+	goto out;
+
+bad_sv:
+	ubi_err("bad scanning information about volume %d", sv->vol_id);
+	ubi_dbg_dump_sv(sv);
+	goto out;
+
+bad_vid_hdr:
+	ubi_err("bad scanning information about volume %d", sv->vol_id);
+	ubi_dbg_dump_sv(sv);
+	ubi_dbg_dump_vid_hdr(vidh);
+
+out:
+	ubi_dbg_dump_stack();
+	return 1;
+}
+
+#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */
diff --git a/drivers/mtd/ubi/scan.h b/drivers/mtd/ubi/scan.h
new file mode 100644
index 0000000..3949f61
--- /dev/null
+++ b/drivers/mtd/ubi/scan.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+#ifndef __UBI_SCAN_H__
+#define __UBI_SCAN_H__
+
+/* The erase counter value for this physical eraseblock is unknown */
+#define UBI_SCAN_UNKNOWN_EC (-1)
+
+/**
+ * struct ubi_scan_leb - scanning information about a physical eraseblock.
+ * @ec: erase counter (%UBI_SCAN_UNKNOWN_EC if it is unknown)
+ * @pnum: physical eraseblock number
+ * @lnum: logical eraseblock number
+ * @scrub: if this physical eraseblock needs scrubbing
+ * @sqnum: sequence number
+ * @u: unions RB-tree or @list links
+ * @u.rb: link in the per-volume RB-tree of &struct ubi_scan_leb objects
+ * @u.list: link in one of the eraseblock lists
+ * @leb_ver: logical eraseblock version (obsolete)
+ *
+ * One object of this type is allocated for each physical eraseblock during
+ * scanning.
+ */
+struct ubi_scan_leb {
+	int ec;
+	int pnum;
+	int lnum;
+	int scrub;
+	unsigned long long sqnum;
+	union {
+		struct rb_node rb;
+		struct list_head list;
+	} u;
+	uint32_t leb_ver;
+};
+
+/**
+ * struct ubi_scan_volume - scanning information about a volume.
+ * @vol_id: volume ID
+ * @highest_lnum: highest logical eraseblock number in this volume
+ * @leb_count: number of logical eraseblocks in this volume
+ * @vol_type: volume type
+ * @used_ebs: number of used logical eraseblocks in this volume (only for
+ * static volumes)
+ * @last_data_size: amount of data in the last logical eraseblock of this
+ * volume (always equivalent to the usable logical eraseblock size in case of
+ * dynamic volumes)
+ * @data_pad: how many bytes at the end of logical eraseblocks of this volume
+ * are not used (due to volume alignment)
+ * @compat: compatibility flags of this volume
+ * @rb: link in the volume RB-tree
+ * @root: root of the RB-tree containing all the eraseblock belonging to this
+ * volume (&struct ubi_scan_leb objects)
+ *
+ * One object of this type is allocated for each volume during scanning.
+ */
+struct ubi_scan_volume {
+	int vol_id;
+	int highest_lnum;
+	int leb_count;
+	int vol_type;
+	int used_ebs;
+	int last_data_size;
+	int data_pad;
+	int compat;
+	struct rb_node rb;
+	struct rb_root root;
+};
+
+/**
+ * struct ubi_scan_info - UBI scanning information.
+ * @volumes: root of the volume RB-tree
+ * @corr: list of corrupted physical eraseblocks
+ * @free: list of free physical eraseblocks
+ * @erase: list of physical eraseblocks which have to be erased
+ * @alien: list of physical eraseblocks which should not be used by UBI (e.g.,
+ * @bad_peb_count: count of bad physical eraseblocks
+ * those belonging to "preserve"-compatible internal volumes)
+ * @vols_found: number of volumes found during scanning
+ * @highest_vol_id: highest volume ID
+ * @alien_peb_count: count of physical eraseblocks in the @alien list
+ * @is_empty: flag indicating whether the MTD device is empty or not
+ * @min_ec: lowest erase counter value
+ * @max_ec: highest erase counter value
+ * @max_sqnum: highest sequence number value
+ * @mean_ec: mean erase counter value
+ * @ec_sum: a temporary variable used when calculating @mean_ec
+ * @ec_count: a temporary variable used when calculating @mean_ec
+ *
+ * This data structure contains the result of scanning and may be used by other
+ * UBI units to build final UBI data structures, further error-recovery and so
+ * on.
+ */
+struct ubi_scan_info {
+	struct rb_root volumes;
+	struct list_head corr;
+	struct list_head free;
+	struct list_head erase;
+	struct list_head alien;
+	int bad_peb_count;
+	int vols_found;
+	int highest_vol_id;
+	int alien_peb_count;
+	int is_empty;
+	int min_ec;
+	int max_ec;
+	unsigned long long max_sqnum;
+	int mean_ec;
+	int ec_sum;
+	int ec_count;
+};
+
+struct ubi_device;
+struct ubi_vid_hdr;
+
+/*
+ * ubi_scan_move_to_list - move a physical eraseblock from the volume tree to a
+ * list.
+ *
+ * @sv: volume scanning information
+ * @seb: scanning eraseblock infprmation
+ * @list: the list to move to
+ */
+static inline void ubi_scan_move_to_list(struct ubi_scan_volume *sv,
+					 struct ubi_scan_leb *seb,
+					 struct list_head *list)
+{
+		rb_erase(&seb->u.rb, &sv->root);
+		list_add_tail(&seb->u.list, list);
+}
+
+int ubi_scan_add_to_list(struct ubi_scan_info *si, int pnum, int ec,
+			 struct list_head *list);
+int ubi_scan_add_used(const struct ubi_device *ubi, struct ubi_scan_info *si,
+		      int pnum, int ec, const struct ubi_vid_hdr *vid_hdr,
+		      int bitflips);
+struct ubi_scan_volume *ubi_scan_find_sv(const struct ubi_scan_info *si,
+					 int vol_id);
+struct ubi_scan_leb *ubi_scan_find_seb(const struct ubi_scan_volume *sv,
+				       int lnum);
+void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv);
+struct ubi_scan_leb *ubi_scan_get_free_peb(const struct ubi_device *ubi,
+					   struct ubi_scan_info *si);
+int ubi_scan_erase_peb(const struct ubi_device *ubi,
+		       const struct ubi_scan_info *si, int pnum, int ec);
+struct ubi_scan_info *ubi_scan(struct ubi_device *ubi);
+void ubi_scan_destroy_si(struct ubi_scan_info *si);
+
+#endif /* !__UBI_SCAN_H__ */
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
new file mode 100644
index 0000000..feb647f
--- /dev/null
+++ b/drivers/mtd/ubi/ubi.h
@@ -0,0 +1,535 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright (c) Nokia Corporation, 2006, 2007
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+#ifndef __UBI_UBI_H__
+#define __UBI_UBI_H__
+
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <linux/rwsem.h>
+#include <linux/spinlock.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/mtd/mtd.h>
+
+#include <mtd/ubi-header.h>
+#include <linux/mtd/ubi.h>
+
+#include "scan.h"
+#include "debug.h"
+
+/* Maximum number of supported UBI devices */
+#define UBI_MAX_DEVICES 32
+
+/* UBI name used for character devices, sysfs, etc */
+#define UBI_NAME_STR "ubi"
+
+/* Normal UBI messages */
+#define ubi_msg(fmt, ...) printk(KERN_NOTICE "UBI: " fmt "\n", ##__VA_ARGS__)
+/* UBI warning messages */
+#define ubi_warn(fmt, ...) printk(KERN_WARNING "UBI warning: %s: " fmt "\n", \
+				  __FUNCTION__, ##__VA_ARGS__)
+/* UBI error messages */
+#define ubi_err(fmt, ...) printk(KERN_ERR "UBI error: %s: " fmt "\n", \
+				 __FUNCTION__, ##__VA_ARGS__)
+
+/* Lowest number PEBs reserved for bad PEB handling */
+#define MIN_RESEVED_PEBS 2
+
+/* Background thread name pattern */
+#define UBI_BGT_NAME_PATTERN "ubi_bgt%dd"
+
+/* This marker in the EBA table means that the LEB is um-mapped */
+#define UBI_LEB_UNMAPPED -1
+
+/*
+ * In case of errors, UBI tries to repeat the operation several times before
+ * returning error. The below constant defines how many times UBI re-tries.
+ */
+#define UBI_IO_RETRIES 3
+
+/*
+ * Error codes returned by the I/O unit.
+ *
+ * UBI_IO_PEB_EMPTY: the physical eraseblock is empty, i.e. it contains only
+ * 0xFF bytes
+ * UBI_IO_PEB_FREE: the physical eraseblock is free, i.e. it contains only a
+ * valid erase counter header, and the rest are %0xFF bytes
+ * UBI_IO_BAD_EC_HDR: the erase counter header is corrupted (bad magic or CRC)
+ * UBI_IO_BAD_VID_HDR: the volume identifier header is corrupted (bad magic or
+ * CRC)
+ * UBI_IO_BITFLIPS: bit-flips were detected and corrected
+ */
+enum {
+	UBI_IO_PEB_EMPTY = 1,
+	UBI_IO_PEB_FREE,
+	UBI_IO_BAD_EC_HDR,
+	UBI_IO_BAD_VID_HDR,
+	UBI_IO_BITFLIPS
+};
+
+extern int ubi_devices_cnt;
+extern struct ubi_device *ubi_devices[];
+
+struct ubi_volume_desc;
+
+/**
+ * struct ubi_volume - UBI volume description data structure.
+ * @dev: device object to make use of the the Linux device model
+ * @cdev: character device object to create character device
+ * @ubi: reference to the UBI device description object
+ * @vol_id: volume ID
+ * @readers: number of users holding this volume in read-only mode
+ * @writers: number of users holding this volume in read-write mode
+ * @exclusive: whether somebody holds this volume in exclusive mode
+ * @removed: if the volume was removed
+ * @checked: if this static volume was checked
+ *
+ * @reserved_pebs: how many physical eraseblocks are reserved for this volume
+ * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ * @usable_leb_size: logical eraseblock size without padding
+ * @used_ebs: how many logical eraseblocks in this volume contain data
+ * @last_eb_bytes: how many bytes are stored in the last logical eraseblock
+ * @used_bytes: how many bytes of data this volume contains
+ * @upd_marker: non-zero if the update marker is set for this volume
+ * @corrupted: non-zero if the volume is corrupted (static volumes only)
+ * @alignment: volume alignment
+ * @data_pad: how many bytes are not used at the end of physical eraseblocks to
+ * satisfy the requested alignment
+ * @name_len: volume name length
+ * @name: volume name
+ *
+ * @updating: whether the volume is being updated
+ * @upd_ebs: how many eraseblocks are expected to be updated
+ * @upd_bytes: how many bytes are expected to be received
+ * @upd_received: how many update bytes were already received
+ * @upd_buf: update buffer which is used to collect update data
+ *
+ * @eba_tbl: EBA table of this volume (LEB->PEB mapping)
+ *
+ * @gluebi_desc: gluebi UBI volume descriptor
+ * @gluebi_refcount: reference count of the gluebi MTD device
+ * @gluebi_mtd: MTD device description object of the gluebi MTD device
+ *
+ * The @corrupted field indicates that the volume's contents is corrupted.
+ * Since UBI protects only static volumes, this field is not relevant to
+ * dynamic volumes - it is user's responsibility to assure their data
+ * integrity.
+ *
+ * The @upd_marker flag indicates that this volume is either being updated at
+ * the moment or is damaged because of an unclean reboot.
+ */
+struct ubi_volume {
+	struct device dev;
+	struct cdev cdev;
+	struct ubi_device *ubi;
+	int vol_id;
+	int readers;
+	int writers;
+	int exclusive;
+	int removed;
+	int checked;
+
+	int reserved_pebs;
+	int vol_type;
+	int usable_leb_size;
+	int used_ebs;
+	int last_eb_bytes;
+	long long used_bytes;
+	int upd_marker;
+	int corrupted;
+	int alignment;
+	int data_pad;
+	int name_len;
+	char name[UBI_VOL_NAME_MAX+1];
+
+	int updating;
+	int upd_ebs;
+	long long upd_bytes;
+	long long upd_received;
+	void *upd_buf;
+
+	int *eba_tbl;
+
+#ifdef CONFIG_MTD_UBI_GLUEBI
+	/* Gluebi-related stuff may be compiled out */
+	struct ubi_volume_desc *gluebi_desc;
+	int gluebi_refcount;
+	struct mtd_info gluebi_mtd;
+#endif
+};
+
+/**
+ * struct ubi_volume_desc - descriptor of the UBI volume returned when it is
+ * opened.
+ * @vol: reference to the corresponding volume description object
+ * @mode: open mode (%UBI_READONLY, %UBI_READWRITE, or %UBI_EXCLUSIVE)
+ */
+struct ubi_volume_desc {
+	struct ubi_volume *vol;
+	int mode;
+};
+
+struct ubi_wl_entry;
+
+/**
+ * struct ubi_device - UBI device description structure
+ * @dev: class device object to use the the Linux device model
+ * @cdev: character device object to create character device
+ * @ubi_num: UBI device number
+ * @ubi_name: UBI device name
+ * @major: character device major number
+ * @vol_count: number of volumes in this UBI device
+ * @volumes: volumes of this UBI device
+ * @volumes_lock: protects @volumes, @rsvd_pebs, @avail_pebs, beb_rsvd_pebs,
+ * @beb_rsvd_level, @bad_peb_count, @good_peb_count, @vol_count, @vol->readers,
+ * @vol->writers, @vol->exclusive, @vol->removed, @vol->mapping and
+ * @vol->eba_tbl.
+ *
+ * @rsvd_pebs: count of reserved physical eraseblocks
+ * @avail_pebs: count of available physical eraseblocks
+ * @beb_rsvd_pebs: how many physical eraseblocks are reserved for bad PEB
+ * handling
+ * @beb_rsvd_level: normal level of PEBs reserved for bad PEB handling
+ *
+ * @vtbl_slots: how many slots are available in the volume table
+ * @vtbl_size: size of the volume table in bytes
+ * @vtbl: in-RAM volume table copy
+ *
+ * @max_ec: current highest erase counter value
+ * @mean_ec: current mean erase counter value
+ *
+ * global_sqnum: global sequence number
+ * @ltree_lock: protects the lock tree and @global_sqnum
+ * @ltree: the lock tree
+ * @vtbl_mutex: protects on-flash volume table
+ *
+ * @used: RB-tree of used physical eraseblocks
+ * @free: RB-tree of free physical eraseblocks
+ * @scrub: RB-tree of physical eraseblocks which need scrubbing
+ * @prot: protection trees
+ * @prot.pnum: protection tree indexed by physical eraseblock numbers
+ * @prot.aec: protection tree indexed by absolute erase counter value
+ * @wl_lock: protects the @used, @free, @prot, @lookuptbl, @abs_ec, @move_from,
+ * @move_to, @move_to_put @erase_pending, @wl_scheduled, and @works
+ * fields
+ * @wl_scheduled: non-zero if the wear-leveling was scheduled
+ * @lookuptbl: a table to quickly find a &struct ubi_wl_entry object for any
+ * physical eraseblock
+ * @abs_ec: absolute erase counter
+ * @move_from: physical eraseblock from where the data is being moved
+ * @move_to: physical eraseblock where the data is being moved to
+ * @move_from_put: if the "from" PEB was put
+ * @move_to_put: if the "to" PEB was put
+ * @works: list of pending works
+ * @works_count: count of pending works
+ * @bgt_thread: background thread description object
+ * @thread_enabled: if the background thread is enabled
+ * @bgt_name: background thread name
+ *
+ * @flash_size: underlying MTD device size (in bytes)
+ * @peb_count: count of physical eraseblocks on the MTD device
+ * @peb_size: physical eraseblock size
+ * @bad_peb_count: count of bad physical eraseblocks
+ * @good_peb_count: count of good physical eraseblocks
+ * @min_io_size: minimal input/output unit size of the underlying MTD device
+ * @hdrs_min_io_size: minimal I/O unit size used for VID and EC headers
+ * @ro_mode: if the UBI device is in read-only mode
+ * @leb_size: logical eraseblock size
+ * @leb_start: starting offset of logical eraseblocks within physical
+ * eraseblocks
+ * @ec_hdr_alsize: size of the EC header aligned to @hdrs_min_io_size
+ * @vid_hdr_alsize: size of the VID header aligned to @hdrs_min_io_size
+ * @vid_hdr_offset: starting offset of the volume identifier header (might be
+ * unaligned)
+ * @vid_hdr_aloffset: starting offset of the VID header aligned to
+ * @hdrs_min_io_size
+ * @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset
+ * @bad_allowed: whether the MTD device admits of bad physical eraseblocks or
+ * not
+ * @mtd: MTD device descriptor
+ */
+struct ubi_device {
+	struct cdev cdev;
+	struct device dev;
+	int ubi_num;
+	char ubi_name[sizeof(UBI_NAME_STR)+5];
+	int major;
+	int vol_count;
+	struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT];
+	spinlock_t volumes_lock;
+
+	int rsvd_pebs;
+	int avail_pebs;
+	int beb_rsvd_pebs;
+	int beb_rsvd_level;
+
+	int vtbl_slots;
+	int vtbl_size;
+	struct ubi_vtbl_record *vtbl;
+	struct mutex vtbl_mutex;
+
+	int max_ec;
+	int mean_ec;
+
+	/* EBA unit's stuff */
+	unsigned long long global_sqnum;
+	spinlock_t ltree_lock;
+	struct rb_root ltree;
+
+	/* Wear-leveling unit's stuff */
+	struct rb_root used;
+	struct rb_root free;
+	struct rb_root scrub;
+	struct {
+		struct rb_root pnum;
+		struct rb_root aec;
+	} prot;
+	spinlock_t wl_lock;
+	int wl_scheduled;
+	struct ubi_wl_entry **lookuptbl;
+	unsigned long long abs_ec;
+	struct ubi_wl_entry *move_from;
+	struct ubi_wl_entry *move_to;
+	int move_from_put;
+	int move_to_put;
+	struct list_head works;
+	int works_count;
+	struct task_struct *bgt_thread;
+	int thread_enabled;
+	char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2];
+
+	/* I/O unit's stuff */
+	long long flash_size;
+	int peb_count;
+	int peb_size;
+	int bad_peb_count;
+	int good_peb_count;
+	int min_io_size;
+	int hdrs_min_io_size;
+	int ro_mode;
+	int leb_size;
+	int leb_start;
+	int ec_hdr_alsize;
+	int vid_hdr_alsize;
+	int vid_hdr_offset;
+	int vid_hdr_aloffset;
+	int vid_hdr_shift;
+	int bad_allowed;
+	struct mtd_info *mtd;
+};
+
+extern struct file_operations ubi_cdev_operations;
+extern struct file_operations ubi_vol_cdev_operations;
+extern struct class *ubi_class;
+
+/* vtbl.c */
+int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
+			   struct ubi_vtbl_record *vtbl_rec);
+int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si);
+
+/* vmt.c */
+int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req);
+int ubi_remove_volume(struct ubi_volume_desc *desc);
+int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs);
+int ubi_add_volume(struct ubi_device *ubi, int vol_id);
+void ubi_free_volume(struct ubi_device *ubi, int vol_id);
+
+/* upd.c */
+int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes);
+int ubi_more_update_data(struct ubi_device *ubi, int vol_id,
+			 const void __user *buf, int count);
+
+/* misc.c */
+int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, int length);
+int ubi_check_volume(struct ubi_device *ubi, int vol_id);
+void ubi_calculate_reserved(struct ubi_device *ubi);
+
+/* gluebi.c */
+#ifdef CONFIG_MTD_UBI_GLUEBI
+int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol);
+int ubi_destroy_gluebi(struct ubi_volume *vol);
+#else
+#define ubi_create_gluebi(ubi, vol) 0
+#define ubi_destroy_gluebi(vol) 0
+#endif
+
+/* eba.c */
+int ubi_eba_unmap_leb(struct ubi_device *ubi, int vol_id, int lnum);
+int ubi_eba_read_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf,
+		     int offset, int len, int check);
+int ubi_eba_write_leb(struct ubi_device *ubi, int vol_id, int lnum,
+		      const void *buf, int offset, int len, int dtype);
+int ubi_eba_write_leb_st(struct ubi_device *ubi, int vol_id, int lnum,
+			 const void *buf, int len, int dtype,
+			 int used_ebs);
+int ubi_eba_atomic_leb_change(struct ubi_device *ubi, int vol_id, int lnum,
+			      const void *buf, int len, int dtype);
+int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
+		     struct ubi_vid_hdr *vid_hdr);
+int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si);
+void ubi_eba_close(const struct ubi_device *ubi);
+
+/* wl.c */
+int ubi_wl_get_peb(struct ubi_device *ubi, int dtype);
+int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture);
+int ubi_wl_flush(struct ubi_device *ubi);
+int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum);
+int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si);
+void ubi_wl_close(struct ubi_device *ubi);
+
+/* io.c */
+int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset,
+		int len);
+int ubi_io_write(const struct ubi_device *ubi, const void *buf, int pnum,
+		 int offset, int len);
+int ubi_io_sync_erase(const struct ubi_device *ubi, int pnum, int torture);
+int ubi_io_is_bad(const struct ubi_device *ubi, int pnum);
+int ubi_io_mark_bad(const struct ubi_device *ubi, int pnum);
+int ubi_io_read_ec_hdr(const struct ubi_device *ubi, int pnum,
+		       struct ubi_ec_hdr *ec_hdr, int verbose);
+int ubi_io_write_ec_hdr(const struct ubi_device *ubi, int pnum,
+			struct ubi_ec_hdr *ec_hdr);
+int ubi_io_read_vid_hdr(const struct ubi_device *ubi, int pnum,
+			struct ubi_vid_hdr *vid_hdr, int verbose);
+int ubi_io_write_vid_hdr(const struct ubi_device *ubi, int pnum,
+			 struct ubi_vid_hdr *vid_hdr);
+
+/*
+ * ubi_rb_for_each_entry - walk an RB-tree.
+ * @rb: a pointer to type 'struct rb_node' to to use as a loop counter
+ * @pos: a pointer to RB-tree entry type to use as a loop counter
+ * @root: RB-tree's root
+ * @member: the name of the 'struct rb_node' within the RB-tree entry
+ */
+#define ubi_rb_for_each_entry(rb, pos, root, member)                         \
+	for (rb = rb_first(root),                                            \
+	     pos = (rb ? container_of(rb, typeof(*pos), member) : NULL);     \
+	     rb;                                                             \
+	     rb = rb_next(rb), pos = container_of(rb, typeof(*pos), member))
+
+/**
+ * ubi_zalloc_vid_hdr - allocate a volume identifier header object.
+ * @ubi: UBI device description object
+ *
+ * This function returns a pointer to the newly allocated and zero-filled
+ * volume identifier header object in case of success and %NULL in case of
+ * failure.
+ */
+static inline struct ubi_vid_hdr *ubi_zalloc_vid_hdr(const struct ubi_device *ubi)
+{
+	void *vid_hdr;
+
+	vid_hdr = kzalloc(ubi->vid_hdr_alsize, GFP_KERNEL);
+	if (!vid_hdr)
+		return NULL;
+
+	/*
+	 * VID headers may be stored at un-aligned flash offsets, so we shift
+	 * the pointer.
+	 */
+	return vid_hdr + ubi->vid_hdr_shift;
+}
+
+/**
+ * ubi_free_vid_hdr - free a volume identifier header object.
+ * @ubi: UBI device description object
+ * @vid_hdr: the object to free
+ */
+static inline void ubi_free_vid_hdr(const struct ubi_device *ubi,
+				    struct ubi_vid_hdr *vid_hdr)
+{
+	void *p = vid_hdr;
+
+	if (!p)
+		return;
+
+	kfree(p - ubi->vid_hdr_shift);
+}
+
+/*
+ * This function is equivalent to 'ubi_io_read()', but @offset is relative to
+ * the beginning of the logical eraseblock, not to the beginning of the
+ * physical eraseblock.
+ */
+static inline int ubi_io_read_data(const struct ubi_device *ubi, void *buf,
+				   int pnum, int offset, int len)
+{
+	ubi_assert(offset >= 0);
+	return ubi_io_read(ubi, buf, pnum, offset + ubi->leb_start, len);
+}
+
+/*
+ * This function is equivalent to 'ubi_io_write()', but @offset is relative to
+ * the beginning of the logical eraseblock, not to the beginning of the
+ * physical eraseblock.
+ */
+static inline int ubi_io_write_data(const struct ubi_device *ubi, const void *buf,
+				    int pnum, int offset, int len)
+{
+	ubi_assert(offset >= 0);
+	return ubi_io_write(ubi, buf, pnum, offset + ubi->leb_start, len);
+}
+
+/**
+ * ubi_ro_mode - switch to read-only mode.
+ * @ubi: UBI device description object
+ */
+static inline void ubi_ro_mode(struct ubi_device *ubi)
+{
+	ubi->ro_mode = 1;
+	ubi_warn("switch to read-only mode");
+}
+
+/**
+ * vol_id2idx - get table index by volume ID.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ */
+static inline int vol_id2idx(const struct ubi_device *ubi, int vol_id)
+{
+	if (vol_id >= UBI_INTERNAL_VOL_START)
+		return vol_id - UBI_INTERNAL_VOL_START + ubi->vtbl_slots;
+	else
+		return vol_id;
+}
+
+/**
+ * idx2vol_id - get volume ID by table index.
+ * @ubi: UBI device description object
+ * @idx: table index
+ */
+static inline int idx2vol_id(const struct ubi_device *ubi, int idx)
+{
+	if (idx >= ubi->vtbl_slots)
+		return idx - ubi->vtbl_slots + UBI_INTERNAL_VOL_START;
+	else
+		return idx;
+}
+
+#endif /* !__UBI_UBI_H__ */
diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c
new file mode 100644
index 0000000..8925b97
--- /dev/null
+++ b/drivers/mtd/ubi/upd.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright (c) Nokia Corporation, 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ *
+ * Jan 2007: Alexander Schmidt, hacked per-volume update.
+ */
+
+/*
+ * This file contains implementation of the volume update functionality.
+ *
+ * The update operation is based on the per-volume update marker which is
+ * stored in the volume table. The update marker is set before the update
+ * starts, and removed after the update has been finished. So if the update was
+ * interrupted by an unclean re-boot or due to some other reasons, the update
+ * marker stays on the flash media and UBI finds it when it attaches the MTD
+ * device next time. If the update marker is set for a volume, the volume is
+ * treated as damaged and most I/O operations are prohibited. Only a new update
+ * operation is allowed.
+ *
+ * Note, in general it is possible to implement the update operation as a
+ * transaction with a roll-back capability.
+ */
+
+#include <linux/err.h>
+#include <asm/uaccess.h>
+#include <asm/div64.h>
+#include "ubi.h"
+
+/**
+ * set_update_marker - set update marker.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ *
+ * This function sets the update marker flag for volume @vol_id. Returns zero
+ * in case of success and a negative error code in case of failure.
+ */
+static int set_update_marker(struct ubi_device *ubi, int vol_id)
+{
+	int err;
+	struct ubi_vtbl_record vtbl_rec;
+	struct ubi_volume *vol = ubi->volumes[vol_id];
+
+	dbg_msg("set update marker for volume %d", vol_id);
+
+	if (vol->upd_marker) {
+		ubi_assert(ubi->vtbl[vol_id].upd_marker);
+		dbg_msg("already set");
+		return 0;
+	}
+
+	memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record));
+	vtbl_rec.upd_marker = 1;
+
+	err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
+	vol->upd_marker = 1;
+	return err;
+}
+
+/**
+ * clear_update_marker - clear update marker.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @bytes: new data size in bytes
+ *
+ * This function clears the update marker for volume @vol_id, sets new volume
+ * data size and clears the "corrupted" flag (static volumes only). Returns
+ * zero in case of success and a negative error code in case of failure.
+ */
+static int clear_update_marker(struct ubi_device *ubi, int vol_id, long long bytes)
+{
+	int err;
+	uint64_t tmp;
+	struct ubi_vtbl_record vtbl_rec;
+	struct ubi_volume *vol = ubi->volumes[vol_id];
+
+	dbg_msg("clear update marker for volume %d", vol_id);
+
+	memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record));
+	ubi_assert(vol->upd_marker && vtbl_rec.upd_marker);
+	vtbl_rec.upd_marker = 0;
+
+	if (vol->vol_type == UBI_STATIC_VOLUME) {
+		vol->corrupted = 0;
+		vol->used_bytes = tmp = bytes;
+		vol->last_eb_bytes = do_div(tmp, vol->usable_leb_size);
+		vol->used_ebs = tmp;
+		if (vol->last_eb_bytes)
+			vol->used_ebs += 1;
+		else
+			vol->last_eb_bytes = vol->usable_leb_size;
+	}
+
+	err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
+	vol->upd_marker = 0;
+	return err;
+}
+
+/**
+ * ubi_start_update - start volume update.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @bytes: update bytes
+ *
+ * This function starts volume update operation. If @bytes is zero, the volume
+ * is just wiped out. Returns zero in case of success and a negative error code
+ * in case of failure.
+ */
+int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes)
+{
+	int i, err;
+	uint64_t tmp;
+	struct ubi_volume *vol = ubi->volumes[vol_id];
+
+	dbg_msg("start update of volume %d, %llu bytes", vol_id, bytes);
+	vol->updating = 1;
+
+	err = set_update_marker(ubi, vol_id);
+	if (err)
+		return err;
+
+	/* Before updating - wipe out the volume */
+	for (i = 0; i < vol->reserved_pebs; i++) {
+		err = ubi_eba_unmap_leb(ubi, vol_id, i);
+		if (err)
+			return err;
+	}
+
+	if (bytes == 0) {
+		err = clear_update_marker(ubi, vol_id, 0);
+		if (err)
+			return err;
+		err = ubi_wl_flush(ubi);
+		if (!err)
+			vol->updating = 0;
+	}
+
+	vol->upd_buf = kmalloc(ubi->leb_size, GFP_KERNEL);
+	if (!vol->upd_buf)
+		return -ENOMEM;
+
+	tmp = bytes;
+	vol->upd_ebs = !!do_div(tmp, vol->usable_leb_size);
+	vol->upd_ebs += tmp;
+	vol->upd_bytes = bytes;
+	vol->upd_received = 0;
+	return 0;
+}
+
+/**
+ * write_leb - write update data.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ * @buf: data to write
+ * @len: data size
+ * @used_ebs: how many logical eraseblocks will this volume contain (static
+ * volumes only)
+ *
+ * This function writes update data to corresponding logical eraseblock. In
+ * case of dynamic volume, this function checks if the data contains 0xFF bytes
+ * at the end. If yes, the 0xFF bytes are cut and not written. So if the whole
+ * buffer contains only 0xFF bytes, the LEB is left unmapped.
+ *
+ * The reason why we skip the trailing 0xFF bytes in case of dynamic volume is
+ * that we want to make sure that more data may be appended to the logical
+ * eraseblock in future. Indeed, writing 0xFF bytes may have side effects and
+ * this PEB won't be writable anymore. So if one writes the file-system image
+ * to the UBI volume where 0xFFs mean free space - UBI makes sure this free
+ * space is writable after the update.
+ *
+ * We do not do this for static volumes because they are read-only. But this
+ * also cannot be done because we have to store per-LEB CRC and the correct
+ * data length.
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int write_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf,
+		     int len, int used_ebs)
+{
+	int err, l;
+	struct ubi_volume *vol = ubi->volumes[vol_id];
+
+	if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
+		l = ALIGN(len, ubi->min_io_size);
+		memset(buf + len, 0xFF, l - len);
+
+		l = ubi_calc_data_len(ubi, buf, l);
+		if (l == 0) {
+			dbg_msg("all %d bytes contain 0xFF - skip", len);
+			return 0;
+		}
+		if (len != l)
+			dbg_msg("skip last %d bytes (0xFF)", len - l);
+
+		err = ubi_eba_write_leb(ubi, vol_id, lnum, buf, 0, l,
+					UBI_UNKNOWN);
+	} else {
+		/*
+		 * When writing static volume, and this is the last logical
+		 * eraseblock, the length (@len) does not have to be aligned to
+		 * the minimal flash I/O unit. The 'ubi_eba_write_leb_st()'
+		 * function accepts exact (unaligned) length and stores it in
+		 * the VID header. And it takes care of proper alignment by
+		 * padding the buffer. Here we just make sure the padding will
+		 * contain zeros, not random trash.
+		 */
+		memset(buf + len, 0, vol->usable_leb_size - len);
+		err = ubi_eba_write_leb_st(ubi, vol_id, lnum, buf, len,
+					   UBI_UNKNOWN, used_ebs);
+	}
+
+	return err;
+}
+
+/**
+ * ubi_more_update_data - write more update data.
+ * @vol: volume description object
+ * @buf: write data (user-space memory buffer)
+ * @count: how much bytes to write
+ *
+ * This function writes more data to the volume which is being updated. It may
+ * be called arbitrary number of times until all of the update data arrive.
+ * This function returns %0 in case of success, number of bytes written during
+ * the last call if the whole volume update was successfully finished, and a
+ * negative error code in case of failure.
+ */
+int ubi_more_update_data(struct ubi_device *ubi, int vol_id,
+			 const void __user *buf, int count)
+{
+	uint64_t tmp;
+	struct ubi_volume *vol = ubi->volumes[vol_id];
+	int lnum, offs, err = 0, len, to_write = count;
+
+	dbg_msg("write %d of %lld bytes, %lld already passed",
+		count, vol->upd_bytes, vol->upd_received);
+
+	if (ubi->ro_mode)
+		return -EROFS;
+
+	tmp = vol->upd_received;
+	offs = do_div(tmp, vol->usable_leb_size);
+	lnum = tmp;
+
+	if (vol->upd_received + count > vol->upd_bytes)
+		to_write = count = vol->upd_bytes - vol->upd_received;
+
+	/*
+	 * When updating volumes, we accumulate whole logical eraseblock of
+	 * data and write it at once.
+	 */
+	if (offs != 0) {
+		/*
+		 * This is a write to the middle of the logical eraseblock. We
+		 * copy the data to our update buffer and wait for more data or
+		 * flush it if the whole eraseblock is written or the update
+		 * is finished.
+		 */
+
+		len = vol->usable_leb_size - offs;
+		if (len > count)
+			len = count;
+
+		err = copy_from_user(vol->upd_buf + offs, buf, len);
+		if (err)
+			return -EFAULT;
+
+		if (offs + len == vol->usable_leb_size ||
+		    vol->upd_received + len == vol->upd_bytes) {
+			int flush_len = offs + len;
+
+			/*
+			 * OK, we gathered either the whole eraseblock or this
+			 * is the last chunk, it's time to flush the buffer.
+			 */
+			ubi_assert(flush_len <= vol->usable_leb_size);
+			err = write_leb(ubi, vol_id, lnum, vol->upd_buf,
+					flush_len, vol->upd_ebs);
+			if (err)
+				return err;
+		}
+
+		vol->upd_received += len;
+		count -= len;
+		buf += len;
+		lnum += 1;
+	}
+
+	/*
+	 * If we've got more to write, let's continue. At this point we know we
+	 * are starting from the beginning of an eraseblock.
+	 */
+	while (count) {
+		if (count > vol->usable_leb_size)
+			len = vol->usable_leb_size;
+		else
+			len = count;
+
+		err = copy_from_user(vol->upd_buf, buf, len);
+		if (err)
+			return -EFAULT;
+
+		if (len == vol->usable_leb_size ||
+		    vol->upd_received + len == vol->upd_bytes) {
+			err = write_leb(ubi, vol_id, lnum, vol->upd_buf, len,
+					vol->upd_ebs);
+			if (err)
+				break;
+		}
+
+		vol->upd_received += len;
+		count -= len;
+		lnum += 1;
+		buf += len;
+	}
+
+	ubi_assert(vol->upd_received <= vol->upd_bytes);
+	if (vol->upd_received == vol->upd_bytes) {
+		/* The update is finished, clear the update marker */
+		err = clear_update_marker(ubi, vol_id, vol->upd_bytes);
+		if (err)
+			return err;
+		err = ubi_wl_flush(ubi);
+		if (err == 0) {
+			err = to_write;
+			kfree(vol->upd_buf);
+			vol->updating = 0;
+		}
+	}
+
+	return err;
+}
diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c
new file mode 100644
index 0000000..622d0d1
--- /dev/null
+++ b/drivers/mtd/ubi/vmt.c
@@ -0,0 +1,809 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file contains implementation of volume creation, deletion, updating and
+ * resizing.
+ */
+
+#include <linux/err.h>
+#include <asm/div64.h>
+#include "ubi.h"
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+static void paranoid_check_volumes(struct ubi_device *ubi);
+#else
+#define paranoid_check_volumes(ubi)
+#endif
+
+static ssize_t vol_attribute_show(struct device *dev,
+				  struct device_attribute *attr, char *buf);
+
+/* Device attributes corresponding to files in '/<sysfs>/class/ubi/ubiX_Y' */
+static struct device_attribute vol_reserved_ebs =
+	__ATTR(reserved_ebs, S_IRUGO, vol_attribute_show, NULL);
+static struct device_attribute vol_type =
+	__ATTR(type, S_IRUGO, vol_attribute_show, NULL);
+static struct device_attribute vol_name =
+	__ATTR(name, S_IRUGO, vol_attribute_show, NULL);
+static struct device_attribute vol_corrupted =
+	__ATTR(corrupted, S_IRUGO, vol_attribute_show, NULL);
+static struct device_attribute vol_alignment =
+	__ATTR(alignment, S_IRUGO, vol_attribute_show, NULL);
+static struct device_attribute vol_usable_eb_size =
+	__ATTR(usable_eb_size, S_IRUGO, vol_attribute_show, NULL);
+static struct device_attribute vol_data_bytes =
+	__ATTR(data_bytes, S_IRUGO, vol_attribute_show, NULL);
+static struct device_attribute vol_upd_marker =
+	__ATTR(upd_marker, S_IRUGO, vol_attribute_show, NULL);
+
+/*
+ * "Show" method for files in '/<sysfs>/class/ubi/ubiX_Y/'.
+ *
+ * Consider a situation:
+ * A. process 1 opens a sysfs file related to volume Y, say
+ *    /<sysfs>/class/ubi/ubiX_Y/reserved_ebs;
+ * B. process 2 removes volume Y;
+ * C. process 1 starts reading the /<sysfs>/class/ubi/ubiX_Y/reserved_ebs file;
+ *
+ * What we want to do in a situation like that is to return error when the file
+ * is read. This is done by means of the 'removed' flag and the 'vol_lock' of
+ * the UBI volume description object.
+ */
+static ssize_t vol_attribute_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	int ret;
+	struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev);
+
+	spin_lock(&vol->ubi->volumes_lock);
+	if (vol->removed) {
+		spin_unlock(&vol->ubi->volumes_lock);
+		return -ENODEV;
+	}
+	if (attr == &vol_reserved_ebs)
+		ret = sprintf(buf, "%d\n", vol->reserved_pebs);
+	else if (attr == &vol_type) {
+		const char *tp;
+		tp = vol->vol_type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static";
+		ret = sprintf(buf, "%s\n", tp);
+	} else if (attr == &vol_name)
+		ret = sprintf(buf, "%s\n", vol->name);
+	else if (attr == &vol_corrupted)
+		ret = sprintf(buf, "%d\n", vol->corrupted);
+	else if (attr == &vol_alignment)
+		ret = sprintf(buf, "%d\n", vol->alignment);
+	else if (attr == &vol_usable_eb_size) {
+		ret = sprintf(buf, "%d\n", vol->usable_leb_size);
+	} else if (attr == &vol_data_bytes)
+		ret = sprintf(buf, "%lld\n", vol->used_bytes);
+	else if (attr == &vol_upd_marker)
+		ret = sprintf(buf, "%d\n", vol->upd_marker);
+	else
+		BUG();
+	spin_unlock(&vol->ubi->volumes_lock);
+	return ret;
+}
+
+/* Release method for volume devices */
+static void vol_release(struct device *dev)
+{
+	struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev);
+	ubi_assert(vol->removed);
+	kfree(vol);
+}
+
+/**
+ * volume_sysfs_init - initialize sysfs for new volume.
+ * @ubi: UBI device description object
+ * @vol: volume description object
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ *
+ * Note, this function does not free allocated resources in case of failure -
+ * the caller does it. This is because this would cause release() here and the
+ * caller would oops.
+ */
+static int volume_sysfs_init(struct ubi_device *ubi, struct ubi_volume *vol)
+{
+	int err;
+
+	err = device_create_file(&vol->dev, &vol_reserved_ebs);
+	if (err)
+		return err;
+	err = device_create_file(&vol->dev, &vol_type);
+	if (err)
+		return err;
+	err = device_create_file(&vol->dev, &vol_name);
+	if (err)
+		return err;
+	err = device_create_file(&vol->dev, &vol_corrupted);
+	if (err)
+		return err;
+	err = device_create_file(&vol->dev, &vol_alignment);
+	if (err)
+		return err;
+	err = device_create_file(&vol->dev, &vol_usable_eb_size);
+	if (err)
+		return err;
+	err = device_create_file(&vol->dev, &vol_data_bytes);
+	if (err)
+		return err;
+	err = device_create_file(&vol->dev, &vol_upd_marker);
+	if (err)
+		return err;
+	return 0;
+}
+
+/**
+ * volume_sysfs_close - close sysfs for a volume.
+ * @vol: volume description object
+ */
+static void volume_sysfs_close(struct ubi_volume *vol)
+{
+	device_remove_file(&vol->dev, &vol_upd_marker);
+	device_remove_file(&vol->dev, &vol_data_bytes);
+	device_remove_file(&vol->dev, &vol_usable_eb_size);
+	device_remove_file(&vol->dev, &vol_alignment);
+	device_remove_file(&vol->dev, &vol_corrupted);
+	device_remove_file(&vol->dev, &vol_name);
+	device_remove_file(&vol->dev, &vol_type);
+	device_remove_file(&vol->dev, &vol_reserved_ebs);
+	device_unregister(&vol->dev);
+}
+
+/**
+ * ubi_create_volume - create volume.
+ * @ubi: UBI device description object
+ * @req: volume creation request
+ *
+ * This function creates volume described by @req. If @req->vol_id id
+ * %UBI_VOL_NUM_AUTO, this function automatically assigne ID to the new volume
+ * and saves it in @req->vol_id. Returns zero in case of success and a negative
+ * error code in case of failure.
+ */
+int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
+{
+	int i, err, vol_id = req->vol_id;
+	struct ubi_volume *vol;
+	struct ubi_vtbl_record vtbl_rec;
+	uint64_t bytes;
+
+	if (ubi->ro_mode)
+		return -EROFS;
+
+	vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL);
+	if (!vol)
+		return -ENOMEM;
+
+	spin_lock(&ubi->volumes_lock);
+
+	if (vol_id == UBI_VOL_NUM_AUTO) {
+		/* Find unused volume ID */
+		dbg_msg("search for vacant volume ID");
+		for (i = 0; i < ubi->vtbl_slots; i++)
+			if (!ubi->volumes[i]) {
+				vol_id = i;
+				break;
+			}
+
+		if (vol_id == UBI_VOL_NUM_AUTO) {
+			dbg_err("out of volume IDs");
+			err = -ENFILE;
+			goto out_unlock;
+		}
+		req->vol_id = vol_id;
+	}
+
+	dbg_msg("volume ID %d, %llu bytes, type %d, name %s",
+		vol_id, (unsigned long long)req->bytes,
+		(int)req->vol_type, req->name);
+
+	/* Ensure that this volume does not exist */
+	err = -EEXIST;
+	if (ubi->volumes[vol_id]) {
+		dbg_err("volume %d already exists", vol_id);
+		goto out_unlock;
+	}
+
+	/* Ensure that the name is unique */
+	for (i = 0; i < ubi->vtbl_slots; i++)
+		if (ubi->volumes[i] &&
+		    ubi->volumes[i]->name_len == req->name_len &&
+		    strcmp(ubi->volumes[i]->name, req->name) == 0) {
+			dbg_err("volume \"%s\" exists (ID %d)", req->name, i);
+			goto out_unlock;
+		}
+
+        /* Calculate how many eraseblocks are requested */
+	vol->usable_leb_size = ubi->leb_size - ubi->leb_size % req->alignment;
+	bytes = req->bytes;
+	if (do_div(bytes, vol->usable_leb_size))
+		vol->reserved_pebs = 1;
+	vol->reserved_pebs += bytes;
+
+	/* Reserve physical eraseblocks */
+	if (vol->reserved_pebs > ubi->avail_pebs) {
+		dbg_err("not enough PEBs, only %d available", ubi->avail_pebs);
+		spin_unlock(&ubi->volumes_lock);
+		err = -ENOSPC;
+		goto out_unlock;
+	}
+	ubi->avail_pebs -= vol->reserved_pebs;
+	ubi->rsvd_pebs += vol->reserved_pebs;
+
+	vol->vol_id    = vol_id;
+	vol->alignment = req->alignment;
+	vol->data_pad  = ubi->leb_size % vol->alignment;
+	vol->vol_type  = req->vol_type;
+	vol->name_len  = req->name_len;
+	memcpy(vol->name, req->name, vol->name_len + 1);
+	vol->exclusive = 1;
+	vol->ubi = ubi;
+	ubi->volumes[vol_id] = vol;
+	spin_unlock(&ubi->volumes_lock);
+
+	/*
+	 * Finish all pending erases because there may be some LEBs belonging
+	 * to the same volume ID.
+	 */
+	err = ubi_wl_flush(ubi);
+	if (err)
+		goto out_acc;
+
+	vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int), GFP_KERNEL);
+	if (!vol->eba_tbl) {
+		err = -ENOMEM;
+		goto out_acc;
+	}
+
+	for (i = 0; i < vol->reserved_pebs; i++)
+		vol->eba_tbl[i] = UBI_LEB_UNMAPPED;
+
+	if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
+		vol->used_ebs = vol->reserved_pebs;
+		vol->last_eb_bytes = vol->usable_leb_size;
+		vol->used_bytes = vol->used_ebs * vol->usable_leb_size;
+	} else {
+		bytes = vol->used_bytes;
+		vol->last_eb_bytes = do_div(bytes, vol->usable_leb_size);
+		vol->used_ebs = bytes;
+		if (vol->last_eb_bytes)
+			vol->used_ebs += 1;
+		else
+			vol->last_eb_bytes = vol->usable_leb_size;
+	}
+
+	/* Register character device for the volume */
+	cdev_init(&vol->cdev, &ubi_vol_cdev_operations);
+	vol->cdev.owner = THIS_MODULE;
+	err = cdev_add(&vol->cdev, MKDEV(ubi->major, vol_id + 1), 1);
+	if (err) {
+		ubi_err("cannot add character device for volume %d", vol_id);
+		goto out_mapping;
+	}
+
+	err = ubi_create_gluebi(ubi, vol);
+	if (err)
+		goto out_cdev;
+
+	vol->dev.release = vol_release;
+	vol->dev.parent = &ubi->dev;
+	vol->dev.devt = MKDEV(ubi->major, vol->vol_id + 1);
+	vol->dev.class = ubi_class;
+	sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id);
+	err = device_register(&vol->dev);
+	if (err)
+		goto out_gluebi;
+
+	err = volume_sysfs_init(ubi, vol);
+	if (err)
+		goto out_sysfs;
+
+	/* Fill volume table record */
+	memset(&vtbl_rec, 0, sizeof(struct ubi_vtbl_record));
+	vtbl_rec.reserved_pebs = cpu_to_ubi32(vol->reserved_pebs);
+	vtbl_rec.alignment     = cpu_to_ubi32(vol->alignment);
+	vtbl_rec.data_pad      = cpu_to_ubi32(vol->data_pad);
+	vtbl_rec.name_len      = cpu_to_ubi16(vol->name_len);
+	if (vol->vol_type == UBI_DYNAMIC_VOLUME)
+		vtbl_rec.vol_type = UBI_VID_DYNAMIC;
+	else
+		vtbl_rec.vol_type = UBI_VID_STATIC;
+	memcpy(vtbl_rec.name, vol->name, vol->name_len + 1);
+
+	err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
+	if (err)
+		goto out_sysfs;
+
+	spin_lock(&ubi->volumes_lock);
+	ubi->vol_count += 1;
+	vol->exclusive = 0;
+	spin_unlock(&ubi->volumes_lock);
+
+	paranoid_check_volumes(ubi);
+	return 0;
+
+out_gluebi:
+	err = ubi_destroy_gluebi(vol);
+out_cdev:
+	cdev_del(&vol->cdev);
+out_mapping:
+	kfree(vol->eba_tbl);
+out_acc:
+	spin_lock(&ubi->volumes_lock);
+	ubi->rsvd_pebs -= vol->reserved_pebs;
+	ubi->avail_pebs += vol->reserved_pebs;
+out_unlock:
+	spin_unlock(&ubi->volumes_lock);
+	kfree(vol);
+	return err;
+
+	/*
+	 * We are registered, so @vol is destroyed in the release function and
+	 * we have to de-initialize differently.
+	 */
+out_sysfs:
+	err = ubi_destroy_gluebi(vol);
+	cdev_del(&vol->cdev);
+	kfree(vol->eba_tbl);
+	spin_lock(&ubi->volumes_lock);
+	ubi->rsvd_pebs -= vol->reserved_pebs;
+	ubi->avail_pebs += vol->reserved_pebs;
+	spin_unlock(&ubi->volumes_lock);
+	volume_sysfs_close(vol);
+	return err;
+}
+
+/**
+ * ubi_remove_volume - remove volume.
+ * @desc: volume descriptor
+ *
+ * This function removes volume described by @desc. The volume has to be opened
+ * in "exclusive" mode. Returns zero in case of success and a negative error
+ * code in case of failure.
+ */
+int ubi_remove_volume(struct ubi_volume_desc *desc)
+{
+	struct ubi_volume *vol = desc->vol;
+	struct ubi_device *ubi = vol->ubi;
+	int i, err, vol_id = vol->vol_id, reserved_pebs = vol->reserved_pebs;
+
+	dbg_msg("remove UBI volume %d", vol_id);
+	ubi_assert(desc->mode == UBI_EXCLUSIVE);
+	ubi_assert(vol == ubi->volumes[vol_id]);
+
+	if (ubi->ro_mode)
+		return -EROFS;
+
+	err = ubi_destroy_gluebi(vol);
+	if (err)
+		return err;
+
+	err = ubi_change_vtbl_record(ubi, vol_id, NULL);
+	if (err)
+		return err;
+
+	for (i = 0; i < vol->reserved_pebs; i++) {
+		err = ubi_eba_unmap_leb(ubi, vol_id, i);
+		if (err)
+			return err;
+	}
+
+	spin_lock(&ubi->volumes_lock);
+	vol->removed = 1;
+	ubi->volumes[vol_id] = NULL;
+	spin_unlock(&ubi->volumes_lock);
+
+	kfree(vol->eba_tbl);
+	vol->eba_tbl = NULL;
+	cdev_del(&vol->cdev);
+	volume_sysfs_close(vol);
+	kfree(desc);
+
+	spin_lock(&ubi->volumes_lock);
+	ubi->rsvd_pebs -= reserved_pebs;
+	ubi->avail_pebs += reserved_pebs;
+	i = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs;
+	if (i > 0) {
+		i = ubi->avail_pebs >= i ? i : ubi->avail_pebs;
+		ubi->avail_pebs -= i;
+		ubi->rsvd_pebs += i;
+		ubi->beb_rsvd_pebs += i;
+		if (i > 0)
+			ubi_msg("reserve more %d PEBs", i);
+	}
+	ubi->vol_count -= 1;
+	spin_unlock(&ubi->volumes_lock);
+
+	paranoid_check_volumes(ubi);
+	module_put(THIS_MODULE);
+	return 0;
+}
+
+/**
+ * ubi_resize_volume - re-size volume.
+ * @desc: volume descriptor
+ * @reserved_pebs: new size in physical eraseblocks
+ *
+ * This function returns zero in case of success, and a negative error code in
+ * case of failure.
+ */
+int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
+{
+	int i, err, pebs, *new_mapping;
+	struct ubi_volume *vol = desc->vol;
+	struct ubi_device *ubi = vol->ubi;
+	struct ubi_vtbl_record vtbl_rec;
+	int vol_id = vol->vol_id;
+
+	if (ubi->ro_mode)
+		return -EROFS;
+
+	dbg_msg("re-size volume %d to from %d to %d PEBs",
+		vol_id, vol->reserved_pebs, reserved_pebs);
+	ubi_assert(desc->mode == UBI_EXCLUSIVE);
+	ubi_assert(vol == ubi->volumes[vol_id]);
+
+	if (vol->vol_type == UBI_STATIC_VOLUME &&
+	    reserved_pebs < vol->used_ebs) {
+		dbg_err("too small size %d, %d LEBs contain data",
+			reserved_pebs, vol->used_ebs);
+		return -EINVAL;
+	}
+
+	/* If the size is the same, we have nothing to do */
+	if (reserved_pebs == vol->reserved_pebs)
+		return 0;
+
+	new_mapping = kmalloc(reserved_pebs * sizeof(int), GFP_KERNEL);
+	if (!new_mapping)
+		return -ENOMEM;
+
+	for (i = 0; i < reserved_pebs; i++)
+		new_mapping[i] = UBI_LEB_UNMAPPED;
+
+	/* Reserve physical eraseblocks */
+	pebs = reserved_pebs - vol->reserved_pebs;
+	if (pebs > 0) {
+		spin_lock(&ubi->volumes_lock);
+		if (pebs > ubi->avail_pebs) {
+			dbg_err("not enough PEBs: requested %d, available %d",
+				pebs, ubi->avail_pebs);
+			spin_unlock(&ubi->volumes_lock);
+			err = -ENOSPC;
+			goto out_free;
+		}
+		ubi->avail_pebs -= pebs;
+		ubi->rsvd_pebs += pebs;
+		for (i = 0; i < vol->reserved_pebs; i++)
+			new_mapping[i] = vol->eba_tbl[i];
+		kfree(vol->eba_tbl);
+		vol->eba_tbl = new_mapping;
+		spin_unlock(&ubi->volumes_lock);
+	}
+
+	/* Change volume table record */
+	memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record));
+	vtbl_rec.reserved_pebs = cpu_to_ubi32(reserved_pebs);
+	err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
+	if (err)
+		goto out_acc;
+
+	if (pebs < 0) {
+		for (i = 0; i < -pebs; i++) {
+			err = ubi_eba_unmap_leb(ubi, vol_id, reserved_pebs + i);
+			if (err)
+				goto out_acc;
+		}
+		spin_lock(&ubi->volumes_lock);
+		ubi->rsvd_pebs += pebs;
+		ubi->avail_pebs -= pebs;
+		pebs = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs;
+		if (pebs > 0) {
+			pebs = ubi->avail_pebs >= pebs ? pebs : ubi->avail_pebs;
+			ubi->avail_pebs -= pebs;
+			ubi->rsvd_pebs += pebs;
+			ubi->beb_rsvd_pebs += pebs;
+			if (pebs > 0)
+				ubi_msg("reserve more %d PEBs", pebs);
+		}
+		for (i = 0; i < reserved_pebs; i++)
+			new_mapping[i] = vol->eba_tbl[i];
+		kfree(vol->eba_tbl);
+		vol->eba_tbl = new_mapping;
+		spin_unlock(&ubi->volumes_lock);
+	}
+
+	vol->reserved_pebs = reserved_pebs;
+	if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
+		vol->used_ebs = reserved_pebs;
+		vol->last_eb_bytes = vol->usable_leb_size;
+		vol->used_bytes = vol->used_ebs * vol->usable_leb_size;
+	}
+
+	paranoid_check_volumes(ubi);
+	return 0;
+
+out_acc:
+	if (pebs > 0) {
+		spin_lock(&ubi->volumes_lock);
+		ubi->rsvd_pebs -= pebs;
+		ubi->avail_pebs += pebs;
+		spin_unlock(&ubi->volumes_lock);
+	}
+out_free:
+	kfree(new_mapping);
+	return err;
+}
+
+/**
+ * ubi_add_volume - add volume.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ *
+ * This function adds an existin volume and initializes all its data
+ * structures. Returnes zero in case of success and a negative error code in
+ * case of failure.
+ */
+int ubi_add_volume(struct ubi_device *ubi, int vol_id)
+{
+	int err;
+	struct ubi_volume *vol = ubi->volumes[vol_id];
+
+	dbg_msg("add volume %d", vol_id);
+	ubi_dbg_dump_vol_info(vol);
+	ubi_assert(vol);
+
+	/* Register character device for the volume */
+	cdev_init(&vol->cdev, &ubi_vol_cdev_operations);
+	vol->cdev.owner = THIS_MODULE;
+	err = cdev_add(&vol->cdev, MKDEV(ubi->major, vol->vol_id + 1), 1);
+	if (err) {
+		ubi_err("cannot add character device for volume %d", vol_id);
+		return err;
+	}
+
+	err = ubi_create_gluebi(ubi, vol);
+	if (err)
+		goto out_cdev;
+
+	vol->dev.release = vol_release;
+	vol->dev.parent = &ubi->dev;
+	vol->dev.devt = MKDEV(ubi->major, vol->vol_id + 1);
+	vol->dev.class = ubi_class;
+	sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id);
+	err = device_register(&vol->dev);
+	if (err)
+		goto out_gluebi;
+
+	err = volume_sysfs_init(ubi, vol);
+	if (err) {
+		cdev_del(&vol->cdev);
+		err = ubi_destroy_gluebi(vol);
+		volume_sysfs_close(vol);
+		return err;
+	}
+
+	paranoid_check_volumes(ubi);
+	return 0;
+
+out_gluebi:
+	err = ubi_destroy_gluebi(vol);
+out_cdev:
+	cdev_del(&vol->cdev);
+	return err;
+}
+
+/**
+ * ubi_free_volume - free volume.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ *
+ * This function frees all resources for volume @vol_id but does not remove it.
+ * Used only when the UBI device is detached.
+ */
+void ubi_free_volume(struct ubi_device *ubi, int vol_id)
+{
+	int err;
+	struct ubi_volume *vol = ubi->volumes[vol_id];
+
+	dbg_msg("free volume %d", vol_id);
+	ubi_assert(vol);
+
+	vol->removed = 1;
+	err = ubi_destroy_gluebi(vol);
+	ubi->volumes[vol_id] = NULL;
+	cdev_del(&vol->cdev);
+	volume_sysfs_close(vol);
+}
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+
+/**
+ * paranoid_check_volume - check volume information.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ */
+static void paranoid_check_volume(const struct ubi_device *ubi, int vol_id)
+{
+	int idx = vol_id2idx(ubi, vol_id);
+	int reserved_pebs, alignment, data_pad, vol_type, name_len, upd_marker;
+	const struct ubi_volume *vol = ubi->volumes[idx];
+	long long n;
+	const char *name;
+
+	reserved_pebs = ubi32_to_cpu(ubi->vtbl[vol_id].reserved_pebs);
+
+	if (!vol) {
+		if (reserved_pebs) {
+			ubi_err("no volume info, but volume exists");
+			goto fail;
+		}
+		return;
+	}
+
+	if (vol->reserved_pebs < 0 || vol->alignment < 0 || vol->data_pad < 0 ||
+	    vol->name_len < 0) {
+		ubi_err("negative values");
+		goto fail;
+	}
+	if (vol->alignment > ubi->leb_size || vol->alignment == 0) {
+		ubi_err("bad alignment");
+		goto fail;
+	}
+
+	n = vol->alignment % ubi->min_io_size;
+	if (vol->alignment != 1 && n) {
+		ubi_err("alignment is not multiple of min I/O unit");
+		goto fail;
+	}
+
+	n = ubi->leb_size % vol->alignment;
+	if (vol->data_pad != n) {
+		ubi_err("bad data_pad, has to be %lld", n);
+		goto fail;
+	}
+
+	if (vol->vol_type != UBI_DYNAMIC_VOLUME &&
+	    vol->vol_type != UBI_STATIC_VOLUME) {
+		ubi_err("bad vol_type");
+		goto fail;
+	}
+
+	if (vol->upd_marker != 0 && vol->upd_marker != 1) {
+		ubi_err("bad upd_marker");
+		goto fail;
+	}
+
+	if (vol->upd_marker && vol->corrupted) {
+		dbg_err("update marker and corrupted simultaneously");
+		goto fail;
+	}
+
+	if (vol->reserved_pebs > ubi->good_peb_count) {
+		ubi_err("too large reserved_pebs");
+		goto fail;
+	}
+
+	n = ubi->leb_size - vol->data_pad;
+	if (vol->usable_leb_size != ubi->leb_size - vol->data_pad) {
+		ubi_err("bad usable_leb_size, has to be %lld", n);
+		goto fail;
+	}
+
+	if (vol->name_len > UBI_VOL_NAME_MAX) {
+		ubi_err("too long volume name, max is %d", UBI_VOL_NAME_MAX);
+		goto fail;
+	}
+
+	if (!vol->name) {
+		ubi_err("NULL volume name");
+		goto fail;
+	}
+
+	n = strnlen(vol->name, vol->name_len + 1);
+	if (n != vol->name_len) {
+		ubi_err("bad name_len %lld", n);
+		goto fail;
+	}
+
+	n = vol->used_ebs * vol->usable_leb_size;
+	if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
+		if (vol->corrupted != 0) {
+			ubi_err("corrupted dynamic volume");
+			goto fail;
+		}
+		if (vol->used_ebs != vol->reserved_pebs) {
+			ubi_err("bad used_ebs");
+			goto fail;
+		}
+		if (vol->last_eb_bytes != vol->usable_leb_size) {
+			ubi_err("bad last_eb_bytes");
+			goto fail;
+		}
+		if (vol->used_bytes != n) {
+			ubi_err("bad used_bytes");
+			goto fail;
+		}
+	} else {
+		if (vol->corrupted != 0 && vol->corrupted != 1) {
+			ubi_err("bad corrupted");
+			goto fail;
+		}
+		if (vol->used_ebs < 0 || vol->used_ebs > vol->reserved_pebs) {
+			ubi_err("bad used_ebs");
+			goto fail;
+		}
+		if (vol->last_eb_bytes < 0 ||
+		    vol->last_eb_bytes > vol->usable_leb_size) {
+			ubi_err("bad last_eb_bytes");
+			goto fail;
+		}
+		if (vol->used_bytes < 0 || vol->used_bytes > n ||
+		    vol->used_bytes < n - vol->usable_leb_size) {
+			ubi_err("bad used_bytes");
+			goto fail;
+		}
+	}
+
+	alignment  = ubi32_to_cpu(ubi->vtbl[vol_id].alignment);
+	data_pad   = ubi32_to_cpu(ubi->vtbl[vol_id].data_pad);
+	name_len   = ubi16_to_cpu(ubi->vtbl[vol_id].name_len);
+	upd_marker = ubi->vtbl[vol_id].upd_marker;
+	name       = &ubi->vtbl[vol_id].name[0];
+	if (ubi->vtbl[vol_id].vol_type == UBI_VID_DYNAMIC)
+		vol_type = UBI_DYNAMIC_VOLUME;
+	else
+		vol_type = UBI_STATIC_VOLUME;
+
+	if (alignment != vol->alignment || data_pad != vol->data_pad ||
+	    upd_marker != vol->upd_marker || vol_type != vol->vol_type ||
+	    name_len!= vol->name_len || strncmp(name, vol->name, name_len)) {
+		ubi_err("volume info is different");
+		goto fail;
+	}
+
+	return;
+
+fail:
+	ubi_err("paranoid check failed");
+	ubi_dbg_dump_vol_info(vol);
+	ubi_dbg_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id);
+	BUG();
+}
+
+/**
+ * paranoid_check_volumes - check information about all volumes.
+ * @ubi: UBI device description object
+ */
+static void paranoid_check_volumes(struct ubi_device *ubi)
+{
+	int i;
+
+	mutex_lock(&ubi->vtbl_mutex);
+	spin_lock(&ubi->volumes_lock);
+	for (i = 0; i < ubi->vtbl_slots; i++)
+		paranoid_check_volume(ubi, i);
+	spin_unlock(&ubi->volumes_lock);
+	mutex_unlock(&ubi->vtbl_mutex);
+}
+#endif
diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c
new file mode 100644
index 0000000..b6fd6bb
--- /dev/null
+++ b/drivers/mtd/ubi/vtbl.c
@@ -0,0 +1,809 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright (c) Nokia Corporation, 2006, 2007
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file includes volume table manipulation code. The volume table is an
+ * on-flash table containing volume meta-data like name, number of reserved
+ * physical eraseblocks, type, etc. The volume table is stored in the so-called
+ * "layout volume".
+ *
+ * The layout volume is an internal volume which is organized as follows. It
+ * consists of two logical eraseblocks - LEB 0 and LEB 1. Each logical
+ * eraseblock stores one volume table copy, i.e. LEB 0 and LEB 1 duplicate each
+ * other. This redundancy guarantees robustness to unclean reboots. The volume
+ * table is basically an array of volume table records. Each record contains
+ * full information about the volume and protected by a CRC checksum.
+ *
+ * The volume table is changed, it is first changed in RAM. Then LEB 0 is
+ * erased, and the updated volume table is written back to LEB 0. Then same for
+ * LEB 1. This scheme guarantees recoverability from unclean reboots.
+ *
+ * In this UBI implementation the on-flash volume table does not contain any
+ * information about how many data static volumes contain. This information may
+ * be found from the scanning data.
+ *
+ * But it would still be beneficial to store this information in the volume
+ * table. For example, suppose we have a static volume X, and all its physical
+ * eraseblocks became bad for some reasons. Suppose we are attaching the
+ * corresponding MTD device, the scanning has found no logical eraseblocks
+ * corresponding to the volume X. According to the volume table volume X does
+ * exist. So we don't know whether it is just empty or all its physical
+ * eraseblocks went bad. So we cannot alarm the user about this corruption.
+ *
+ * The volume table also stores so-called "update marker", which is used for
+ * volume updates. Before updating the volume, the update marker is set, and
+ * after the update operation is finished, the update marker is cleared. So if
+ * the update operation was interrupted (e.g. by an unclean reboot) - the
+ * update marker is still there and we know that the volume's contents is
+ * damaged.
+ */
+
+#include <linux/crc32.h>
+#include <linux/err.h>
+#include <asm/div64.h>
+#include "ubi.h"
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+static void paranoid_vtbl_check(const struct ubi_device *ubi);
+#else
+#define paranoid_vtbl_check(ubi)
+#endif
+
+/* Empty volume table record */
+static struct ubi_vtbl_record empty_vtbl_record;
+
+/**
+ * ubi_change_vtbl_record - change volume table record.
+ * @ubi: UBI device description object
+ * @idx: table index to change
+ * @vtbl_rec: new volume table record
+ *
+ * This function changes volume table record @idx. If @vtbl_rec is %NULL, empty
+ * volume table record is written. The caller does not have to calculate CRC of
+ * the record as it is done by this function. Returns zero in case of success
+ * and a negative error code in case of failure.
+ */
+int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
+			   struct ubi_vtbl_record *vtbl_rec)
+{
+	int i, err;
+	uint32_t crc;
+
+	ubi_assert(idx >= 0 && idx < ubi->vtbl_slots);
+
+	if (!vtbl_rec)
+		vtbl_rec = &empty_vtbl_record;
+	else {
+		crc = crc32(UBI_CRC32_INIT, vtbl_rec, UBI_VTBL_RECORD_SIZE_CRC);
+		vtbl_rec->crc = cpu_to_ubi32(crc);
+	}
+
+	dbg_msg("change record %d", idx);
+	ubi_dbg_dump_vtbl_record(vtbl_rec, idx);
+
+	mutex_lock(&ubi->vtbl_mutex);
+	memcpy(&ubi->vtbl[idx], vtbl_rec, sizeof(struct ubi_vtbl_record));
+	for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) {
+		err = ubi_eba_unmap_leb(ubi, UBI_LAYOUT_VOL_ID, i);
+		if (err) {
+			mutex_unlock(&ubi->vtbl_mutex);
+			return err;
+		}
+		err = ubi_eba_write_leb(ubi, UBI_LAYOUT_VOL_ID, i, ubi->vtbl, 0,
+					ubi->vtbl_size, UBI_LONGTERM);
+		if (err) {
+			mutex_unlock(&ubi->vtbl_mutex);
+			return err;
+		}
+	}
+
+	paranoid_vtbl_check(ubi);
+	mutex_unlock(&ubi->vtbl_mutex);
+	return ubi_wl_flush(ubi);
+}
+
+/**
+ * vol_til_check - check if volume table is not corrupted and contains sensible
+ * data.
+ *
+ * @ubi: UBI device description object
+ * @vtbl: volume table
+ *
+ * This function returns zero if @vtbl is all right, %1 if CRC is incorrect,
+ * and %-EINVAL if it contains inconsistent data.
+ */
+static int vtbl_check(const struct ubi_device *ubi,
+		      const struct ubi_vtbl_record *vtbl)
+{
+	int i, n, reserved_pebs, alignment, data_pad, vol_type, name_len;
+	int upd_marker;
+	uint32_t crc;
+	const char *name;
+
+	for (i = 0; i < ubi->vtbl_slots; i++) {
+		cond_resched();
+
+		reserved_pebs = ubi32_to_cpu(vtbl[i].reserved_pebs);
+		alignment = ubi32_to_cpu(vtbl[i].alignment);
+		data_pad = ubi32_to_cpu(vtbl[i].data_pad);
+		upd_marker = vtbl[i].upd_marker;
+		vol_type = vtbl[i].vol_type;
+		name_len = ubi16_to_cpu(vtbl[i].name_len);
+		name = &vtbl[i].name[0];
+
+		crc = crc32(UBI_CRC32_INIT, &vtbl[i], UBI_VTBL_RECORD_SIZE_CRC);
+		if (ubi32_to_cpu(vtbl[i].crc) != crc) {
+			ubi_err("bad CRC at record %u: %#08x, not %#08x",
+				 i, crc, ubi32_to_cpu(vtbl[i].crc));
+			ubi_dbg_dump_vtbl_record(&vtbl[i], i);
+			return 1;
+		}
+
+		if (reserved_pebs == 0) {
+			if (memcmp(&vtbl[i], &empty_vtbl_record,
+						UBI_VTBL_RECORD_SIZE)) {
+				dbg_err("bad empty record");
+				goto bad;
+			}
+			continue;
+		}
+
+		if (reserved_pebs < 0 || alignment < 0 || data_pad < 0 ||
+		    name_len < 0) {
+			dbg_err("negative values");
+			goto bad;
+		}
+
+		if (alignment > ubi->leb_size || alignment == 0) {
+			dbg_err("bad alignment");
+			goto bad;
+		}
+
+		n = alignment % ubi->min_io_size;
+		if (alignment != 1 && n) {
+			dbg_err("alignment is not multiple of min I/O unit");
+			goto bad;
+		}
+
+		n = ubi->leb_size % alignment;
+		if (data_pad != n) {
+			dbg_err("bad data_pad, has to be %d", n);
+			goto bad;
+		}
+
+		if (vol_type != UBI_VID_DYNAMIC && vol_type != UBI_VID_STATIC) {
+			dbg_err("bad vol_type");
+			goto bad;
+		}
+
+		if (upd_marker != 0 && upd_marker != 1) {
+			dbg_err("bad upd_marker");
+			goto bad;
+		}
+
+		if (reserved_pebs > ubi->good_peb_count) {
+			dbg_err("too large reserved_pebs, good PEBs %d",
+				ubi->good_peb_count);
+			goto bad;
+		}
+
+		if (name_len > UBI_VOL_NAME_MAX) {
+			dbg_err("too long volume name, max %d",
+				UBI_VOL_NAME_MAX);
+			goto bad;
+		}
+
+		if (name[0] == '\0') {
+			dbg_err("NULL volume name");
+			goto bad;
+		}
+
+		if (name_len != strnlen(name, name_len + 1)) {
+			dbg_err("bad name_len");
+			goto bad;
+		}
+	}
+
+	/* Checks that all names are unique */
+	for (i = 0; i < ubi->vtbl_slots - 1; i++) {
+		for (n = i + 1; n < ubi->vtbl_slots; n++) {
+			int len1 = ubi16_to_cpu(vtbl[i].name_len);
+			int len2 = ubi16_to_cpu(vtbl[n].name_len);
+
+			if (len1 > 0 && len1 == len2 &&
+			    !strncmp(vtbl[i].name, vtbl[n].name, len1)) {
+				ubi_err("volumes %d and %d have the same name"
+					" \"%s\"", i, n, vtbl[i].name);
+				ubi_dbg_dump_vtbl_record(&vtbl[i], i);
+				ubi_dbg_dump_vtbl_record(&vtbl[n], n);
+				return -EINVAL;
+			}
+		}
+	}
+
+	return 0;
+
+bad:
+	ubi_err("volume table check failed, record %d", i);
+	ubi_dbg_dump_vtbl_record(&vtbl[i], i);
+	return -EINVAL;
+}
+
+/**
+ * create_vtbl - create a copy of volume table.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ * @copy: number of the volume table copy
+ * @vtbl: contents of the volume table
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int create_vtbl(const struct ubi_device *ubi, struct ubi_scan_info *si,
+		       int copy, void *vtbl)
+{
+	int err, tries = 0;
+	static struct ubi_vid_hdr *vid_hdr;
+	struct ubi_scan_volume *sv;
+	struct ubi_scan_leb *new_seb, *old_seb = NULL;
+
+	ubi_msg("create volume table (copy #%d)", copy + 1);
+
+	vid_hdr = ubi_zalloc_vid_hdr(ubi);
+	if (!vid_hdr)
+		return -ENOMEM;
+
+	/*
+	 * Check if there is a logical eraseblock which would have to contain
+	 * this volume table copy was found during scanning. It has to be wiped
+	 * out.
+	 */
+	sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOL_ID);
+	if (sv)
+		old_seb = ubi_scan_find_seb(sv, copy);
+
+retry:
+	new_seb = ubi_scan_get_free_peb(ubi, si);
+	if (IS_ERR(new_seb)) {
+		err = PTR_ERR(new_seb);
+		goto out_free;
+	}
+
+	vid_hdr->vol_type = UBI_VID_DYNAMIC;
+	vid_hdr->vol_id = cpu_to_ubi32(UBI_LAYOUT_VOL_ID);
+	vid_hdr->compat = UBI_LAYOUT_VOLUME_COMPAT;
+	vid_hdr->data_size = vid_hdr->used_ebs =
+			     vid_hdr->data_pad = cpu_to_ubi32(0);
+	vid_hdr->lnum = cpu_to_ubi32(copy);
+	vid_hdr->sqnum = cpu_to_ubi64(++si->max_sqnum);
+	vid_hdr->leb_ver = cpu_to_ubi32(old_seb ? old_seb->leb_ver + 1: 0);
+
+	/* The EC header is already there, write the VID header */
+	err = ubi_io_write_vid_hdr(ubi, new_seb->pnum, vid_hdr);
+	if (err)
+		goto write_error;
+
+	/* Write the layout volume contents */
+	err = ubi_io_write_data(ubi, vtbl, new_seb->pnum, 0, ubi->vtbl_size);
+	if (err)
+		goto write_error;
+
+	/*
+	 * And add it to the scanning information. Don't delete the old
+	 * @old_seb as it will be deleted and freed in 'ubi_scan_add_used()'.
+	 */
+	err = ubi_scan_add_used(ubi, si, new_seb->pnum, new_seb->ec,
+				vid_hdr, 0);
+	kfree(new_seb);
+	ubi_free_vid_hdr(ubi, vid_hdr);
+	return err;
+
+write_error:
+	kfree(new_seb);
+	/* May be this physical eraseblock went bad, try to pick another one */
+	if (++tries <= 5) {
+		err = ubi_scan_add_to_list(si, new_seb->pnum, new_seb->ec,
+					   &si->corr);
+		if (!err)
+			goto retry;
+	}
+out_free:
+	ubi_free_vid_hdr(ubi, vid_hdr);
+	return err;
+
+}
+
+/**
+ * process_lvol - process the layout volume.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ * @sv: layout volume scanning information
+ *
+ * This function is responsible for reading the layout volume, ensuring it is
+ * not corrupted, and recovering from corruptions if needed. Returns volume
+ * table in case of success and a negative error code in case of failure.
+ */
+static struct ubi_vtbl_record *process_lvol(const struct ubi_device *ubi,
+					    struct ubi_scan_info *si,
+					    struct ubi_scan_volume *sv)
+{
+	int err;
+	struct rb_node *rb;
+	struct ubi_scan_leb *seb;
+	struct ubi_vtbl_record *leb[UBI_LAYOUT_VOLUME_EBS] = { NULL, NULL };
+	int leb_corrupted[UBI_LAYOUT_VOLUME_EBS] = {1, 1};
+
+	/*
+	 * UBI goes through the following steps when it changes the layout
+	 * volume:
+	 * a. erase LEB 0;
+	 * b. write new data to LEB 0;
+	 * c. erase LEB 1;
+	 * d. write new data to LEB 1.
+	 *
+	 * Before the change, both LEBs contain the same data.
+	 *
+	 * Due to unclean reboots, the contents of LEB 0 may be lost, but there
+	 * should LEB 1. So it is OK if LEB 0 is corrupted while LEB 1 is not.
+	 * Similarly, LEB 1 may be lost, but there should be LEB 0. And
+	 * finally, unclean reboots may result in a situation when neither LEB
+	 * 0 nor LEB 1 are corrupted, but they are different. In this case, LEB
+	 * 0 contains more recent information.
+	 *
+	 * So the plan is to first check LEB 0. Then
+	 * a. if LEB 0 is OK, it must be containing the most resent data; then
+	 *    we compare it with LEB 1, and if they are different, we copy LEB
+	 *    0 to LEB 1;
+	 * b. if LEB 0 is corrupted, but LEB 1 has to be OK, and we copy LEB 1
+	 *    to LEB 0.
+	 */
+
+	dbg_msg("check layout volume");
+
+	/* Read both LEB 0 and LEB 1 into memory */
+	ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) {
+		leb[seb->lnum] = kzalloc(ubi->vtbl_size, GFP_KERNEL);
+		if (!leb[seb->lnum]) {
+			err = -ENOMEM;
+			goto out_free;
+		}
+
+		err = ubi_io_read_data(ubi, leb[seb->lnum], seb->pnum, 0,
+				       ubi->vtbl_size);
+		if (err == UBI_IO_BITFLIPS || err == -EBADMSG)
+			/* Scrub the PEB later */
+			seb->scrub = 1;
+		else if (err)
+			goto out_free;
+	}
+
+	err = -EINVAL;
+	if (leb[0]) {
+		leb_corrupted[0] = vtbl_check(ubi, leb[0]);
+		if (leb_corrupted[0] < 0)
+			goto out_free;
+	}
+
+	if (!leb_corrupted[0]) {
+		/* LEB 0 is OK */
+		if (leb[1])
+			leb_corrupted[1] = memcmp(leb[0], leb[1], ubi->vtbl_size);
+		if (leb_corrupted[1]) {
+			ubi_warn("volume table copy #2 is corrupted");
+			err = create_vtbl(ubi, si, 1, leb[0]);
+			if (err)
+				goto out_free;
+			ubi_msg("volume table was restored");
+		}
+
+		/* Both LEB 1 and LEB 2 are OK and consistent */
+		kfree(leb[1]);
+		return leb[0];
+	} else {
+		/* LEB 0 is corrupted or does not exist */
+		if (leb[1]) {
+			leb_corrupted[1] = vtbl_check(ubi, leb[1]);
+			if (leb_corrupted[1] < 0)
+				goto out_free;
+		}
+		if (leb_corrupted[1]) {
+			/* Both LEB 0 and LEB 1 are corrupted */
+			ubi_err("both volume tables are corrupted");
+			goto out_free;
+		}
+
+		ubi_warn("volume table copy #1 is corrupted");
+		err = create_vtbl(ubi, si, 0, leb[1]);
+		if (err)
+			goto out_free;
+		ubi_msg("volume table was restored");
+
+		kfree(leb[0]);
+		return leb[1];
+	}
+
+out_free:
+	kfree(leb[0]);
+	kfree(leb[1]);
+	return ERR_PTR(err);
+}
+
+/**
+ * create_empty_lvol - create empty layout volume.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ *
+ * This function returns volume table contents in case of success and a
+ * negative error code in case of failure.
+ */
+static struct ubi_vtbl_record *create_empty_lvol(const struct ubi_device *ubi,
+						 struct ubi_scan_info *si)
+{
+	int i;
+	struct ubi_vtbl_record *vtbl;
+
+	vtbl = kzalloc(ubi->vtbl_size, GFP_KERNEL);
+	if (!vtbl)
+		return ERR_PTR(-ENOMEM);
+
+	for (i = 0; i < ubi->vtbl_slots; i++)
+		memcpy(&vtbl[i], &empty_vtbl_record, UBI_VTBL_RECORD_SIZE);
+
+	for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) {
+		int err;
+
+		err = create_vtbl(ubi, si, i, vtbl);
+		if (err) {
+			kfree(vtbl);
+			return ERR_PTR(err);
+		}
+	}
+
+	return vtbl;
+}
+
+/**
+ * init_volumes - initialize volume information for existing volumes.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ * @vtbl: volume table
+ *
+ * This function allocates volume description objects for existing volumes.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
+			const struct ubi_vtbl_record *vtbl)
+{
+	int i, reserved_pebs = 0;
+	struct ubi_scan_volume *sv;
+	struct ubi_volume *vol;
+
+	for (i = 0; i < ubi->vtbl_slots; i++) {
+		cond_resched();
+
+		if (ubi32_to_cpu(vtbl[i].reserved_pebs) == 0)
+			continue; /* Empty record */
+
+		vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL);
+		if (!vol)
+			return -ENOMEM;
+
+		vol->reserved_pebs = ubi32_to_cpu(vtbl[i].reserved_pebs);
+		vol->alignment = ubi32_to_cpu(vtbl[i].alignment);
+		vol->data_pad = ubi32_to_cpu(vtbl[i].data_pad);
+		vol->vol_type = vtbl[i].vol_type == UBI_VID_DYNAMIC ?
+					UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME;
+		vol->name_len = ubi16_to_cpu(vtbl[i].name_len);
+		vol->usable_leb_size = ubi->leb_size - vol->data_pad;
+		memcpy(vol->name, vtbl[i].name, vol->name_len);
+		vol->name[vol->name_len] = '\0';
+		vol->vol_id = i;
+
+		ubi_assert(!ubi->volumes[i]);
+		ubi->volumes[i] = vol;
+		ubi->vol_count += 1;
+		vol->ubi = ubi;
+		reserved_pebs += vol->reserved_pebs;
+
+		/*
+		 * In case of dynamic volume UBI knows nothing about how many
+		 * data is stored there. So assume the whole volume is used.
+		 */
+		if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
+			vol->used_ebs = vol->reserved_pebs;
+			vol->last_eb_bytes = vol->usable_leb_size;
+			vol->used_bytes = vol->used_ebs * vol->usable_leb_size;
+			continue;
+		}
+
+		/* Static volumes only */
+		sv = ubi_scan_find_sv(si, i);
+		if (!sv) {
+			/*
+			 * No eraseblocks belonging to this volume found. We
+			 * don't actually know whether this static volume is
+			 * completely corrupted or just contains no data. And
+			 * we cannot know this as long as data size is not
+			 * stored on flash. So we just assume the volume is
+			 * empty. FIXME: this should be handled.
+			 */
+			continue;
+		}
+
+		if (sv->leb_count != sv->used_ebs) {
+			/*
+			 * We found a static volume which misses several
+			 * eraseblocks. Treat it as corrupted.
+			 */
+			ubi_warn("static volume %d misses %d LEBs - corrupted",
+				 sv->vol_id, sv->used_ebs - sv->leb_count);
+			vol->corrupted = 1;
+			continue;
+		}
+
+		vol->used_ebs = sv->used_ebs;
+		vol->used_bytes = (vol->used_ebs - 1) * vol->usable_leb_size;
+		vol->used_bytes += sv->last_data_size;
+		vol->last_eb_bytes = sv->last_data_size;
+	}
+
+	vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL);
+	if (!vol)
+		return -ENOMEM;
+
+	vol->reserved_pebs = UBI_LAYOUT_VOLUME_EBS;
+	vol->alignment = 1;
+	vol->vol_type = UBI_DYNAMIC_VOLUME;
+	vol->name_len = sizeof(UBI_LAYOUT_VOLUME_NAME) - 1;
+	memcpy(vol->name, UBI_LAYOUT_VOLUME_NAME, vol->name_len + 1);
+	vol->usable_leb_size = ubi->leb_size;
+	vol->used_ebs = vol->reserved_pebs;
+	vol->last_eb_bytes = vol->reserved_pebs;
+	vol->used_bytes = vol->used_ebs * (ubi->leb_size - vol->data_pad);
+	vol->vol_id = UBI_LAYOUT_VOL_ID;
+
+	ubi_assert(!ubi->volumes[i]);
+	ubi->volumes[vol_id2idx(ubi, vol->vol_id)] = vol;
+	reserved_pebs += vol->reserved_pebs;
+	ubi->vol_count += 1;
+	vol->ubi = ubi;
+
+	if (reserved_pebs > ubi->avail_pebs)
+		ubi_err("not enough PEBs, required %d, available %d",
+			reserved_pebs, ubi->avail_pebs);
+	ubi->rsvd_pebs += reserved_pebs;
+	ubi->avail_pebs -= reserved_pebs;
+
+	return 0;
+}
+
+/**
+ * check_sv - check volume scanning information.
+ * @vol: UBI volume description object
+ * @sv: volume scanning information
+ *
+ * This function returns zero if the volume scanning information is consistent
+ * to the data read from the volume tabla, and %-EINVAL if not.
+ */
+static int check_sv(const struct ubi_volume *vol,
+		    const struct ubi_scan_volume *sv)
+{
+	if (sv->highest_lnum >= vol->reserved_pebs) {
+		dbg_err("bad highest_lnum");
+		goto bad;
+	}
+	if (sv->leb_count > vol->reserved_pebs) {
+		dbg_err("bad leb_count");
+		goto bad;
+	}
+	if (sv->vol_type != vol->vol_type) {
+		dbg_err("bad vol_type");
+		goto bad;
+	}
+	if (sv->used_ebs > vol->reserved_pebs) {
+		dbg_err("bad used_ebs");
+		goto bad;
+	}
+	if (sv->data_pad != vol->data_pad) {
+		dbg_err("bad data_pad");
+		goto bad;
+	}
+	return 0;
+
+bad:
+	ubi_err("bad scanning information");
+	ubi_dbg_dump_sv(sv);
+	ubi_dbg_dump_vol_info(vol);
+	return -EINVAL;
+}
+
+/**
+ * check_scanning_info - check that scanning information.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ *
+ * Even though we protect on-flash data by CRC checksums, we still don't trust
+ * the media. This function ensures that scanning information is consistent to
+ * the information read from the volume table. Returns zero if the scanning
+ * information is OK and %-EINVAL if it is not.
+ */
+static int check_scanning_info(const struct ubi_device *ubi,
+			       struct ubi_scan_info *si)
+{
+	int err, i;
+	struct ubi_scan_volume *sv;
+	struct ubi_volume *vol;
+
+	if (si->vols_found > UBI_INT_VOL_COUNT + ubi->vtbl_slots) {
+		ubi_err("scanning found %d volumes, maximum is %d + %d",
+			si->vols_found, UBI_INT_VOL_COUNT, ubi->vtbl_slots);
+		return -EINVAL;
+	}
+
+	if (si->highest_vol_id >= ubi->vtbl_slots + UBI_INT_VOL_COUNT&&
+	    si->highest_vol_id < UBI_INTERNAL_VOL_START) {
+		ubi_err("too large volume ID %d found by scanning",
+			si->highest_vol_id);
+		return -EINVAL;
+	}
+
+
+	for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) {
+		cond_resched();
+
+		sv = ubi_scan_find_sv(si, i);
+		vol = ubi->volumes[i];
+		if (!vol) {
+			if (sv)
+				ubi_scan_rm_volume(si, sv);
+			continue;
+		}
+
+		if (vol->reserved_pebs == 0) {
+			ubi_assert(i < ubi->vtbl_slots);
+
+			if (!sv)
+				continue;
+
+			/*
+			 * During scanning we found a volume which does not
+			 * exist according to the information in the volume
+			 * table. This must have happened due to an unclean
+			 * reboot while the volume was being removed. Discard
+			 * these eraseblocks.
+			 */
+			ubi_msg("finish volume %d removal", sv->vol_id);
+			ubi_scan_rm_volume(si, sv);
+		} else if (sv) {
+			err = check_sv(vol, sv);
+			if (err)
+				return err;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * ubi_read_volume_table - read volume table.
+ * information.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ *
+ * This function reads volume table, checks it, recover from errors if needed,
+ * or creates it if needed. Returns zero in case of success and a negative
+ * error code in case of failure.
+ */
+int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si)
+{
+	int i, err;
+	struct ubi_scan_volume *sv;
+
+	empty_vtbl_record.crc = cpu_to_ubi32(0xf116c36b);
+
+	/*
+	 * The number of supported volumes is limited by the eraseblock size
+	 * and by the UBI_MAX_VOLUMES constant.
+	 */
+	ubi->vtbl_slots = ubi->leb_size / UBI_VTBL_RECORD_SIZE;
+	if (ubi->vtbl_slots > UBI_MAX_VOLUMES)
+		ubi->vtbl_slots = UBI_MAX_VOLUMES;
+
+	ubi->vtbl_size = ubi->vtbl_slots * UBI_VTBL_RECORD_SIZE;
+	ubi->vtbl_size = ALIGN(ubi->vtbl_size, ubi->min_io_size);
+
+	sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOL_ID);
+	if (!sv) {
+		/*
+		 * No logical eraseblocks belonging to the layout volume were
+		 * found. This could mean that the flash is just empty. In
+		 * this case we create empty layout volume.
+		 *
+		 * But if flash is not empty this must be a corruption or the
+		 * MTD device just contains garbage.
+		 */
+		if (si->is_empty) {
+			ubi->vtbl = create_empty_lvol(ubi, si);
+			if (IS_ERR(ubi->vtbl))
+				return PTR_ERR(ubi->vtbl);
+		} else {
+			ubi_err("the layout volume was not found");
+			return -EINVAL;
+		}
+	} else {
+		if (sv->leb_count > UBI_LAYOUT_VOLUME_EBS) {
+			/* This must not happen with proper UBI images */
+			dbg_err("too many LEBs (%d) in layout volume",
+				sv->leb_count);
+			return -EINVAL;
+		}
+
+		ubi->vtbl = process_lvol(ubi, si, sv);
+		if (IS_ERR(ubi->vtbl))
+			return PTR_ERR(ubi->vtbl);
+	}
+
+	ubi->avail_pebs = ubi->good_peb_count;
+
+	/*
+	 * The layout volume is OK, initialize the corresponding in-RAM data
+	 * structures.
+	 */
+	err = init_volumes(ubi, si, ubi->vtbl);
+	if (err)
+		goto out_free;
+
+	/*
+	 * Get sure that the scanning information is consistent to the
+	 * information stored in the volume table.
+	 */
+	err = check_scanning_info(ubi, si);
+	if (err)
+		goto out_free;
+
+	return 0;
+
+out_free:
+	kfree(ubi->vtbl);
+	for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++)
+		if (ubi->volumes[i]) {
+			kfree(ubi->volumes[i]);
+			ubi->volumes[i] = NULL;
+		}
+	return err;
+}
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+
+/**
+ * paranoid_vtbl_check - check volume table.
+ * @ubi: UBI device description object
+ */
+static void paranoid_vtbl_check(const struct ubi_device *ubi)
+{
+	if (vtbl_check(ubi, ubi->vtbl)) {
+		ubi_err("paranoid check failed");
+		BUG();
+	}
+}
+
+#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
new file mode 100644
index 0000000..9ecaf77
--- /dev/null
+++ b/drivers/mtd/ubi/wl.c
@@ -0,0 +1,1671 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём), Thomas Gleixner
+ */
+
+/*
+ * UBI wear-leveling unit.
+ *
+ * This unit is responsible for wear-leveling. It works in terms of physical
+ * eraseblocks and erase counters and knows nothing about logical eraseblocks,
+ * volumes, etc. From this unit's perspective all physical eraseblocks are of
+ * two types - used and free. Used physical eraseblocks are those that were
+ * "get" by the 'ubi_wl_get_peb()' function, and free physical eraseblocks are
+ * those that were put by the 'ubi_wl_put_peb()' function.
+ *
+ * Physical eraseblocks returned by 'ubi_wl_get_peb()' have only erase counter
+ * header. The rest of the physical eraseblock contains only 0xFF bytes.
+ *
+ * When physical eraseblocks are returned to the WL unit by means of the
+ * 'ubi_wl_put_peb()' function, they are scheduled for erasure. The erasure is
+ * done asynchronously in context of the per-UBI device background thread,
+ * which is also managed by the WL unit.
+ *
+ * The wear-leveling is ensured by means of moving the contents of used
+ * physical eraseblocks with low erase counter to free physical eraseblocks
+ * with high erase counter.
+ *
+ * The 'ubi_wl_get_peb()' function accepts data type hints which help to pick
+ * an "optimal" physical eraseblock. For example, when it is known that the
+ * physical eraseblock will be "put" soon because it contains short-term data,
+ * the WL unit may pick a free physical eraseblock with low erase counter, and
+ * so forth.
+ *
+ * If the WL unit fails to erase a physical eraseblock, it marks it as bad.
+ *
+ * This unit is also responsible for scrubbing. If a bit-flip is detected in a
+ * physical eraseblock, it has to be moved. Technically this is the same as
+ * moving it for wear-leveling reasons.
+ *
+ * As it was said, for the UBI unit all physical eraseblocks are either "free"
+ * or "used". Free eraseblock are kept in the @wl->free RB-tree, while used
+ * eraseblocks are kept in a set of different RB-trees: @wl->used,
+ * @wl->prot.pnum, @wl->prot.aec, and @wl->scrub.
+ *
+ * Note, in this implementation, we keep a small in-RAM object for each physical
+ * eraseblock. This is surely not a scalable solution. But it appears to be good
+ * enough for moderately large flashes and it is simple. In future, one may
+ * re-work this unit and make it more scalable.
+ *
+ * At the moment this unit does not utilize the sequence number, which was
+ * introduced relatively recently. But it would be wise to do this because the
+ * sequence number of a logical eraseblock characterizes how old is it. For
+ * example, when we move a PEB with low erase counter, and we need to pick the
+ * target PEB, we pick a PEB with the highest EC if our PEB is "old" and we
+ * pick target PEB with an average EC if our PEB is not very "old". This is a
+ * room for future re-works of the WL unit.
+ *
+ * FIXME: looks too complex, should be simplified (later).
+ */
+
+#include <linux/slab.h>
+#include <linux/crc32.h>
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#include "ubi.h"
+
+/* Number of physical eraseblocks reserved for wear-leveling purposes */
+#define WL_RESERVED_PEBS 1
+
+/*
+ * How many erase cycles are short term, unknown, and long term physical
+ * eraseblocks protected.
+ */
+#define ST_PROTECTION 16
+#define U_PROTECTION  10
+#define LT_PROTECTION 4
+
+/*
+ * Maximum difference between two erase counters. If this threshold is
+ * exceeded, the WL unit starts moving data from used physical eraseblocks with
+ * low erase counter to free physical eraseblocks with high erase counter.
+ */
+#define UBI_WL_THRESHOLD CONFIG_MTD_UBI_WL_THRESHOLD
+
+/*
+ * When a physical eraseblock is moved, the WL unit has to pick the target
+ * physical eraseblock to move to. The simplest way would be just to pick the
+ * one with the highest erase counter. But in certain workloads this could lead
+ * to an unlimited wear of one or few physical eraseblock. Indeed, imagine a
+ * situation when the picked physical eraseblock is constantly erased after the
+ * data is written to it. So, we have a constant which limits the highest erase
+ * counter of the free physical eraseblock to pick. Namely, the WL unit does
+ * not pick eraseblocks with erase counter greater then the lowest erase
+ * counter plus %WL_FREE_MAX_DIFF.
+ */
+#define WL_FREE_MAX_DIFF (2*UBI_WL_THRESHOLD)
+
+/*
+ * Maximum number of consecutive background thread failures which is enough to
+ * switch to read-only mode.
+ */
+#define WL_MAX_FAILURES 32
+
+/**
+ * struct ubi_wl_entry - wear-leveling entry.
+ * @rb: link in the corresponding RB-tree
+ * @ec: erase counter
+ * @pnum: physical eraseblock number
+ *
+ * Each physical eraseblock has a corresponding &struct wl_entry object which
+ * may be kept in different RB-trees.
+ */
+struct ubi_wl_entry {
+	struct rb_node rb;
+	int ec;
+	int pnum;
+};
+
+/**
+ * struct ubi_wl_prot_entry - PEB protection entry.
+ * @rb_pnum: link in the @wl->prot.pnum RB-tree
+ * @rb_aec: link in the @wl->prot.aec RB-tree
+ * @abs_ec: the absolute erase counter value when the protection ends
+ * @e: the wear-leveling entry of the physical eraseblock under protection
+ *
+ * When the WL unit returns a physical eraseblock, the physical eraseblock is
+ * protected from being moved for some "time". For this reason, the physical
+ * eraseblock is not directly moved from the @wl->free tree to the @wl->used
+ * tree. There is one more tree in between where this physical eraseblock is
+ * temporarily stored (@wl->prot).
+ *
+ * All this protection stuff is needed because:
+ *  o we don't want to move physical eraseblocks just after we have given them
+ *    to the user; instead, we first want to let users fill them up with data;
+ *
+ *  o there is a chance that the user will put the physical eraseblock very
+ *    soon, so it makes sense not to move it for some time, but wait; this is
+ *    especially important in case of "short term" physical eraseblocks.
+ *
+ * Physical eraseblocks stay protected only for limited time. But the "time" is
+ * measured in erase cycles in this case. This is implemented with help of the
+ * absolute erase counter (@wl->abs_ec). When it reaches certain value, the
+ * physical eraseblocks are moved from the protection trees (@wl->prot.*) to
+ * the @wl->used tree.
+ *
+ * Protected physical eraseblocks are searched by physical eraseblock number
+ * (when they are put) and by the absolute erase counter (to check if it is
+ * time to move them to the @wl->used tree). So there are actually 2 RB-trees
+ * storing the protected physical eraseblocks: @wl->prot.pnum and
+ * @wl->prot.aec. They are referred to as the "protection" trees. The
+ * first one is indexed by the physical eraseblock number. The second one is
+ * indexed by the absolute erase counter. Both trees store
+ * &struct ubi_wl_prot_entry objects.
+ *
+ * Each physical eraseblock has 2 main states: free and used. The former state
+ * corresponds to the @wl->free tree. The latter state is split up on several
+ * sub-states:
+ * o the WL movement is allowed (@wl->used tree);
+ * o the WL movement is temporarily prohibited (@wl->prot.pnum and
+ * @wl->prot.aec trees);
+ * o scrubbing is needed (@wl->scrub tree).
+ *
+ * Depending on the sub-state, wear-leveling entries of the used physical
+ * eraseblocks may be kept in one of those trees.
+ */
+struct ubi_wl_prot_entry {
+	struct rb_node rb_pnum;
+	struct rb_node rb_aec;
+	unsigned long long abs_ec;
+	struct ubi_wl_entry *e;
+};
+
+/**
+ * struct ubi_work - UBI work description data structure.
+ * @list: a link in the list of pending works
+ * @func: worker function
+ * @priv: private data of the worker function
+ *
+ * @e: physical eraseblock to erase
+ * @torture: if the physical eraseblock has to be tortured
+ *
+ * The @func pointer points to the worker function. If the @cancel argument is
+ * not zero, the worker has to free the resources and exit immediately. The
+ * worker has to return zero in case of success and a negative error code in
+ * case of failure.
+ */
+struct ubi_work {
+	struct list_head list;
+	int (*func)(struct ubi_device *ubi, struct ubi_work *wrk, int cancel);
+	/* The below fields are only relevant to erasure works */
+	struct ubi_wl_entry *e;
+	int torture;
+};
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+static int paranoid_check_ec(const struct ubi_device *ubi, int pnum, int ec);
+static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e,
+				     struct rb_root *root);
+#else
+#define paranoid_check_ec(ubi, pnum, ec) 0
+#define paranoid_check_in_wl_tree(e, root)
+#endif
+
+/* Slab cache for wear-leveling entries */
+static struct kmem_cache *wl_entries_slab;
+
+/**
+ * tree_empty - a helper function to check if an RB-tree is empty.
+ * @root: the root of the tree
+ *
+ * This function returns non-zero if the RB-tree is empty and zero if not.
+ */
+static inline int tree_empty(struct rb_root *root)
+{
+	return root->rb_node == NULL;
+}
+
+/**
+ * wl_tree_add - add a wear-leveling entry to a WL RB-tree.
+ * @e: the wear-leveling entry to add
+ * @root: the root of the tree
+ *
+ * Note, we use (erase counter, physical eraseblock number) pairs as keys in
+ * the @ubi->used and @ubi->free RB-trees.
+ */
+static void wl_tree_add(struct ubi_wl_entry *e, struct rb_root *root)
+{
+	struct rb_node **p, *parent = NULL;
+
+	p = &root->rb_node;
+	while (*p) {
+		struct ubi_wl_entry *e1;
+
+		parent = *p;
+		e1 = rb_entry(parent, struct ubi_wl_entry, rb);
+
+		if (e->ec < e1->ec)
+			p = &(*p)->rb_left;
+		else if (e->ec > e1->ec)
+			p = &(*p)->rb_right;
+		else {
+			ubi_assert(e->pnum != e1->pnum);
+			if (e->pnum < e1->pnum)
+				p = &(*p)->rb_left;
+			else
+				p = &(*p)->rb_right;
+		}
+	}
+
+	rb_link_node(&e->rb, parent, p);
+	rb_insert_color(&e->rb, root);
+}
+
+
+/*
+ * Helper functions to add and delete wear-leveling entries from different
+ * trees.
+ */
+
+static void free_tree_add(struct ubi_device *ubi, struct ubi_wl_entry *e)
+{
+	wl_tree_add(e, &ubi->free);
+}
+static inline void used_tree_add(struct ubi_device *ubi,
+				 struct ubi_wl_entry *e)
+{
+	wl_tree_add(e, &ubi->used);
+}
+static inline void scrub_tree_add(struct ubi_device *ubi,
+				  struct ubi_wl_entry *e)
+{
+	wl_tree_add(e, &ubi->scrub);
+}
+static inline void free_tree_del(struct ubi_device *ubi,
+				 struct ubi_wl_entry *e)
+{
+	paranoid_check_in_wl_tree(e, &ubi->free);
+	rb_erase(&e->rb, &ubi->free);
+}
+static inline void used_tree_del(struct ubi_device *ubi,
+				 struct ubi_wl_entry *e)
+{
+	paranoid_check_in_wl_tree(e, &ubi->used);
+	rb_erase(&e->rb, &ubi->used);
+}
+static inline void scrub_tree_del(struct ubi_device *ubi,
+				  struct ubi_wl_entry *e)
+{
+	paranoid_check_in_wl_tree(e, &ubi->scrub);
+	rb_erase(&e->rb, &ubi->scrub);
+}
+
+/**
+ * do_work - do one pending work.
+ * @ubi: UBI device description object
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int do_work(struct ubi_device *ubi)
+{
+	int err;
+	struct ubi_work *wrk;
+
+	spin_lock(&ubi->wl_lock);
+
+	if (list_empty(&ubi->works)) {
+		spin_unlock(&ubi->wl_lock);
+		return 0;
+	}
+
+	wrk = list_entry(ubi->works.next, struct ubi_work, list);
+	list_del(&wrk->list);
+	spin_unlock(&ubi->wl_lock);
+
+	/*
+	 * Call the worker function. Do not touch the work structure
+	 * after this call as it will have been freed or reused by that
+	 * time by the worker function.
+	 */
+	err = wrk->func(ubi, wrk, 0);
+	if (err)
+		ubi_err("work failed with error code %d", err);
+
+	spin_lock(&ubi->wl_lock);
+	ubi->works_count -= 1;
+	ubi_assert(ubi->works_count >= 0);
+	spin_unlock(&ubi->wl_lock);
+	return err;
+}
+
+/**
+ * produce_free_peb - produce a free physical eraseblock.
+ * @ubi: UBI device description object
+ *
+ * This function tries to make a free PEB by means of synchronous execution of
+ * pending works. This may be needed if, for example the background thread is
+ * disabled. Returns zero in case of success and a negative error code in case
+ * of failure.
+ */
+static int produce_free_peb(struct ubi_device *ubi)
+{
+	int err;
+
+	spin_lock(&ubi->wl_lock);
+	while (tree_empty(&ubi->free)) {
+		spin_unlock(&ubi->wl_lock);
+
+		dbg_wl("do one work synchronously");
+		err = do_work(ubi);
+		if (err)
+			return err;
+
+		spin_lock(&ubi->wl_lock);
+	}
+	spin_unlock(&ubi->wl_lock);
+
+	return 0;
+}
+
+/**
+ * in_wl_tree - check if wear-leveling entry is present in a WL RB-tree.
+ * @e: the wear-leveling entry to check
+ * @root: the root of the tree
+ *
+ * This function returns non-zero if @e is in the @root RB-tree and zero if it
+ * is not.
+ */
+static int in_wl_tree(struct ubi_wl_entry *e, struct rb_root *root)
+{
+	struct rb_node *p;
+
+	p = root->rb_node;
+	while (p) {
+		struct ubi_wl_entry *e1;
+
+		e1 = rb_entry(p, struct ubi_wl_entry, rb);
+
+		if (e->pnum == e1->pnum) {
+			ubi_assert(e == e1);
+			return 1;
+		}
+
+		if (e->ec < e1->ec)
+			p = p->rb_left;
+		else if (e->ec > e1->ec)
+			p = p->rb_right;
+		else {
+			ubi_assert(e->pnum != e1->pnum);
+			if (e->pnum < e1->pnum)
+				p = p->rb_left;
+			else
+				p = p->rb_right;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * prot_tree_add - add physical eraseblock to protection trees.
+ * @ubi: UBI device description object
+ * @e: the physical eraseblock to add
+ * @pe: protection entry object to use
+ * @abs_ec: absolute erase counter value when this physical eraseblock has
+ * to be removed from the protection trees.
+ *
+ * @wl->lock has to be locked.
+ */
+static void prot_tree_add(struct ubi_device *ubi, struct ubi_wl_entry *e,
+			  struct ubi_wl_prot_entry *pe, int abs_ec)
+{
+	struct rb_node **p, *parent = NULL;
+	struct ubi_wl_prot_entry *pe1;
+
+	pe->e = e;
+	pe->abs_ec = ubi->abs_ec + abs_ec;
+
+	p = &ubi->prot.pnum.rb_node;
+	while (*p) {
+		parent = *p;
+		pe1 = rb_entry(parent, struct ubi_wl_prot_entry, rb_pnum);
+
+		if (e->pnum < pe1->e->pnum)
+			p = &(*p)->rb_left;
+		else
+			p = &(*p)->rb_right;
+	}
+	rb_link_node(&pe->rb_pnum, parent, p);
+	rb_insert_color(&pe->rb_pnum, &ubi->prot.pnum);
+
+	p = &ubi->prot.aec.rb_node;
+	parent = NULL;
+	while (*p) {
+		parent = *p;
+		pe1 = rb_entry(parent, struct ubi_wl_prot_entry, rb_aec);
+
+		if (pe->abs_ec < pe1->abs_ec)
+			p = &(*p)->rb_left;
+		else
+			p = &(*p)->rb_right;
+	}
+	rb_link_node(&pe->rb_aec, parent, p);
+	rb_insert_color(&pe->rb_aec, &ubi->prot.aec);
+}
+
+/**
+ * find_wl_entry - find wear-leveling entry closest to certain erase counter.
+ * @root: the RB-tree where to look for
+ * @max: highest possible erase counter
+ *
+ * This function looks for a wear leveling entry with erase counter closest to
+ * @max and less then @max.
+ */
+static struct ubi_wl_entry *find_wl_entry(struct rb_root *root, int max)
+{
+	struct rb_node *p;
+	struct ubi_wl_entry *e;
+
+	e = rb_entry(rb_first(root), struct ubi_wl_entry, rb);
+	max += e->ec;
+
+	p = root->rb_node;
+	while (p) {
+		struct ubi_wl_entry *e1;
+
+		e1 = rb_entry(p, struct ubi_wl_entry, rb);
+		if (e1->ec >= max)
+			p = p->rb_left;
+		else {
+			p = p->rb_right;
+			e = e1;
+		}
+	}
+
+	return e;
+}
+
+/**
+ * ubi_wl_get_peb - get a physical eraseblock.
+ * @ubi: UBI device description object
+ * @dtype: type of data which will be stored in this physical eraseblock
+ *
+ * This function returns a physical eraseblock in case of success and a
+ * negative error code in case of failure. Might sleep.
+ */
+int ubi_wl_get_peb(struct ubi_device *ubi, int dtype)
+{
+	int err, protect, medium_ec;
+	struct ubi_wl_entry *e, *first, *last;
+	struct ubi_wl_prot_entry *pe;
+
+	ubi_assert(dtype == UBI_LONGTERM || dtype == UBI_SHORTTERM ||
+		   dtype == UBI_UNKNOWN);
+
+	pe = kmalloc(sizeof(struct ubi_wl_prot_entry), GFP_KERNEL);
+	if (!pe)
+		return -ENOMEM;
+
+retry:
+	spin_lock(&ubi->wl_lock);
+	if (tree_empty(&ubi->free)) {
+		if (ubi->works_count == 0) {
+			ubi_assert(list_empty(&ubi->works));
+			ubi_err("no free eraseblocks");
+			spin_unlock(&ubi->wl_lock);
+			kfree(pe);
+			return -ENOSPC;
+		}
+		spin_unlock(&ubi->wl_lock);
+
+		err = produce_free_peb(ubi);
+		if (err < 0) {
+			kfree(pe);
+			return err;
+		}
+		goto retry;
+	}
+
+	switch (dtype) {
+		case UBI_LONGTERM:
+			/*
+			 * For long term data we pick a physical eraseblock
+			 * with high erase counter. But the highest erase
+			 * counter we can pick is bounded by the the lowest
+			 * erase counter plus %WL_FREE_MAX_DIFF.
+			 */
+			e = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF);
+			protect = LT_PROTECTION;
+			break;
+		case UBI_UNKNOWN:
+			/*
+			 * For unknown data we pick a physical eraseblock with
+			 * medium erase counter. But we by no means can pick a
+			 * physical eraseblock with erase counter greater or
+			 * equivalent than the lowest erase counter plus
+			 * %WL_FREE_MAX_DIFF.
+			 */
+			first = rb_entry(rb_first(&ubi->free),
+					 struct ubi_wl_entry, rb);
+			last = rb_entry(rb_last(&ubi->free),
+					struct ubi_wl_entry, rb);
+
+			if (last->ec - first->ec < WL_FREE_MAX_DIFF)
+				e = rb_entry(ubi->free.rb_node,
+						struct ubi_wl_entry, rb);
+			else {
+				medium_ec = (first->ec + WL_FREE_MAX_DIFF)/2;
+				e = find_wl_entry(&ubi->free, medium_ec);
+			}
+			protect = U_PROTECTION;
+			break;
+		case UBI_SHORTTERM:
+			/*
+			 * For short term data we pick a physical eraseblock
+			 * with the lowest erase counter as we expect it will
+			 * be erased soon.
+			 */
+			e = rb_entry(rb_first(&ubi->free),
+				     struct ubi_wl_entry, rb);
+			protect = ST_PROTECTION;
+			break;
+		default:
+			protect = 0;
+			e = NULL;
+			BUG();
+	}
+
+	/*
+	 * Move the physical eraseblock to the protection trees where it will
+	 * be protected from being moved for some time.
+	 */
+	free_tree_del(ubi, e);
+	prot_tree_add(ubi, e, pe, protect);
+
+	dbg_wl("PEB %d EC %d, protection %d", e->pnum, e->ec, protect);
+	spin_unlock(&ubi->wl_lock);
+
+	return e->pnum;
+}
+
+/**
+ * prot_tree_del - remove a physical eraseblock from the protection trees
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock to remove
+ */
+static void prot_tree_del(struct ubi_device *ubi, int pnum)
+{
+	struct rb_node *p;
+	struct ubi_wl_prot_entry *pe = NULL;
+
+	p = ubi->prot.pnum.rb_node;
+	while (p) {
+
+		pe = rb_entry(p, struct ubi_wl_prot_entry, rb_pnum);
+
+		if (pnum == pe->e->pnum)
+			break;
+
+		if (pnum < pe->e->pnum)
+			p = p->rb_left;
+		else
+			p = p->rb_right;
+	}
+
+	ubi_assert(pe->e->pnum == pnum);
+	rb_erase(&pe->rb_aec, &ubi->prot.aec);
+	rb_erase(&pe->rb_pnum, &ubi->prot.pnum);
+	kfree(pe);
+}
+
+/**
+ * sync_erase - synchronously erase a physical eraseblock.
+ * @ubi: UBI device description object
+ * @e: the the physical eraseblock to erase
+ * @torture: if the physical eraseblock has to be tortured
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, int torture)
+{
+	int err;
+	struct ubi_ec_hdr *ec_hdr;
+	unsigned long long ec = e->ec;
+
+	dbg_wl("erase PEB %d, old EC %llu", e->pnum, ec);
+
+	err = paranoid_check_ec(ubi, e->pnum, e->ec);
+	if (err > 0)
+		return -EINVAL;
+
+	ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
+	if (!ec_hdr)
+		return -ENOMEM;
+
+	err = ubi_io_sync_erase(ubi, e->pnum, torture);
+	if (err < 0)
+		goto out_free;
+
+	ec += err;
+	if (ec > UBI_MAX_ERASECOUNTER) {
+		/*
+		 * Erase counter overflow. Upgrade UBI and use 64-bit
+		 * erase counters internally.
+		 */
+		ubi_err("erase counter overflow at PEB %d, EC %llu",
+			e->pnum, ec);
+		err = -EINVAL;
+		goto out_free;
+	}
+
+	dbg_wl("erased PEB %d, new EC %llu", e->pnum, ec);
+
+	ec_hdr->ec = cpu_to_ubi64(ec);
+
+	err = ubi_io_write_ec_hdr(ubi, e->pnum, ec_hdr);
+	if (err)
+		goto out_free;
+
+	e->ec = ec;
+	spin_lock(&ubi->wl_lock);
+	if (e->ec > ubi->max_ec)
+		ubi->max_ec = e->ec;
+	spin_unlock(&ubi->wl_lock);
+
+out_free:
+	kfree(ec_hdr);
+	return err;
+}
+
+/**
+ * check_protection_over - check if it is time to stop protecting some
+ * physical eraseblocks.
+ * @ubi: UBI device description object
+ *
+ * This function is called after each erase operation, when the absolute erase
+ * counter is incremented, to check if some physical eraseblock  have not to be
+ * protected any longer. These physical eraseblocks are moved from the
+ * protection trees to the used tree.
+ */
+static void check_protection_over(struct ubi_device *ubi)
+{
+	struct ubi_wl_prot_entry *pe;
+
+	/*
+	 * There may be several protected physical eraseblock to remove,
+	 * process them all.
+	 */
+	while (1) {
+		spin_lock(&ubi->wl_lock);
+		if (tree_empty(&ubi->prot.aec)) {
+			spin_unlock(&ubi->wl_lock);
+			break;
+		}
+
+		pe = rb_entry(rb_first(&ubi->prot.aec),
+			      struct ubi_wl_prot_entry, rb_aec);
+
+		if (pe->abs_ec > ubi->abs_ec) {
+			spin_unlock(&ubi->wl_lock);
+			break;
+		}
+
+		dbg_wl("PEB %d protection over, abs_ec %llu, PEB abs_ec %llu",
+		       pe->e->pnum, ubi->abs_ec, pe->abs_ec);
+		rb_erase(&pe->rb_aec, &ubi->prot.aec);
+		rb_erase(&pe->rb_pnum, &ubi->prot.pnum);
+		used_tree_add(ubi, pe->e);
+		spin_unlock(&ubi->wl_lock);
+
+		kfree(pe);
+		cond_resched();
+	}
+}
+
+/**
+ * schedule_ubi_work - schedule a work.
+ * @ubi: UBI device description object
+ * @wrk: the work to schedule
+ *
+ * This function enqueues a work defined by @wrk to the tail of the pending
+ * works list.
+ */
+static void schedule_ubi_work(struct ubi_device *ubi, struct ubi_work *wrk)
+{
+	spin_lock(&ubi->wl_lock);
+	list_add_tail(&wrk->list, &ubi->works);
+	ubi_assert(ubi->works_count >= 0);
+	ubi->works_count += 1;
+	if (ubi->thread_enabled)
+		wake_up_process(ubi->bgt_thread);
+	spin_unlock(&ubi->wl_lock);
+}
+
+static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
+			int cancel);
+
+/**
+ * schedule_erase - schedule an erase work.
+ * @ubi: UBI device description object
+ * @e: the WL entry of the physical eraseblock to erase
+ * @torture: if the physical eraseblock has to be tortured
+ *
+ * This function returns zero in case of success and a %-ENOMEM in case of
+ * failure.
+ */
+static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
+			  int torture)
+{
+	struct ubi_work *wl_wrk;
+
+	dbg_wl("schedule erasure of PEB %d, EC %d, torture %d",
+	       e->pnum, e->ec, torture);
+
+	wl_wrk = kmalloc(sizeof(struct ubi_work), GFP_KERNEL);
+	if (!wl_wrk)
+		return -ENOMEM;
+
+	wl_wrk->func = &erase_worker;
+	wl_wrk->e = e;
+	wl_wrk->torture = torture;
+
+	schedule_ubi_work(ubi, wl_wrk);
+	return 0;
+}
+
+/**
+ * wear_leveling_worker - wear-leveling worker function.
+ * @ubi: UBI device description object
+ * @wrk: the work object
+ * @cancel: non-zero if the worker has to free memory and exit
+ *
+ * This function copies a more worn out physical eraseblock to a less worn out
+ * one. Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
+				int cancel)
+{
+	int err, put = 0;
+	struct ubi_wl_entry *e1, *e2;
+	struct ubi_vid_hdr *vid_hdr;
+
+	kfree(wrk);
+
+	if (cancel)
+		return 0;
+
+	vid_hdr = ubi_zalloc_vid_hdr(ubi);
+	if (!vid_hdr)
+		return -ENOMEM;
+
+	spin_lock(&ubi->wl_lock);
+
+	/*
+	 * Only one WL worker at a time is supported at this implementation, so
+	 * make sure a PEB is not being moved already.
+	 */
+	if (ubi->move_to || tree_empty(&ubi->free) ||
+	    (tree_empty(&ubi->used) && tree_empty(&ubi->scrub))) {
+		/*
+		 * Only one WL worker at a time is supported at this
+		 * implementation, so if a LEB is already being moved, cancel.
+		 *
+		 * No free physical eraseblocks? Well, we cancel wear-leveling
+		 * then. It will be triggered again when a free physical
+		 * eraseblock appears.
+		 *
+		 * No used physical eraseblocks? They must be temporarily
+		 * protected from being moved. They will be moved to the
+		 * @ubi->used tree later and the wear-leveling will be
+		 * triggered again.
+		 */
+		dbg_wl("cancel WL, a list is empty: free %d, used %d",
+		       tree_empty(&ubi->free), tree_empty(&ubi->used));
+		ubi->wl_scheduled = 0;
+		spin_unlock(&ubi->wl_lock);
+		ubi_free_vid_hdr(ubi, vid_hdr);
+		return 0;
+	}
+
+	if (tree_empty(&ubi->scrub)) {
+		/*
+		 * Now pick the least worn-out used physical eraseblock and a
+		 * highly worn-out free physical eraseblock. If the erase
+		 * counters differ much enough, start wear-leveling.
+		 */
+		e1 = rb_entry(rb_first(&ubi->used), struct ubi_wl_entry, rb);
+		e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF);
+
+		if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) {
+			dbg_wl("no WL needed: min used EC %d, max free EC %d",
+			       e1->ec, e2->ec);
+			ubi->wl_scheduled = 0;
+			spin_unlock(&ubi->wl_lock);
+			ubi_free_vid_hdr(ubi, vid_hdr);
+			return 0;
+		}
+		used_tree_del(ubi, e1);
+		dbg_wl("move PEB %d EC %d to PEB %d EC %d",
+		       e1->pnum, e1->ec, e2->pnum, e2->ec);
+	} else {
+		e1 = rb_entry(rb_first(&ubi->scrub), struct ubi_wl_entry, rb);
+		e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF);
+		scrub_tree_del(ubi, e1);
+		dbg_wl("scrub PEB %d to PEB %d", e1->pnum, e2->pnum);
+	}
+
+	free_tree_del(ubi, e2);
+	ubi_assert(!ubi->move_from && !ubi->move_to);
+	ubi_assert(!ubi->move_to_put && !ubi->move_from_put);
+	ubi->move_from = e1;
+	ubi->move_to = e2;
+	spin_unlock(&ubi->wl_lock);
+
+	/*
+	 * Now we are going to copy physical eraseblock @e1->pnum to @e2->pnum.
+	 * We so far do not know which logical eraseblock our physical
+	 * eraseblock (@e1) belongs to. We have to read the volume identifier
+	 * header first.
+	 */
+
+	err = ubi_io_read_vid_hdr(ubi, e1->pnum, vid_hdr, 0);
+	if (err && err != UBI_IO_BITFLIPS) {
+		if (err == UBI_IO_PEB_FREE) {
+			/*
+			 * We are trying to move PEB without a VID header. UBI
+			 * always write VID headers shortly after the PEB was
+			 * given, so we have a situation when it did not have
+			 * chance to write it down because it was preempted.
+			 * Just re-schedule the work, so that next time it will
+			 * likely have the VID header in place.
+			 */
+			dbg_wl("PEB %d has no VID header", e1->pnum);
+			err = 0;
+		} else {
+			ubi_err("error %d while reading VID header from PEB %d",
+				err, e1->pnum);
+			if (err > 0)
+				err = -EIO;
+		}
+		goto error;
+	}
+
+	err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr);
+	if (err) {
+		if (err == UBI_IO_BITFLIPS)
+			err = 0;
+		goto error;
+	}
+
+	ubi_free_vid_hdr(ubi, vid_hdr);
+	spin_lock(&ubi->wl_lock);
+	if (!ubi->move_to_put)
+		used_tree_add(ubi, e2);
+	else
+		put = 1;
+	ubi->move_from = ubi->move_to = NULL;
+	ubi->move_from_put = ubi->move_to_put = 0;
+	ubi->wl_scheduled = 0;
+	spin_unlock(&ubi->wl_lock);
+
+	if (put) {
+		/*
+		 * Well, the target PEB was put meanwhile, schedule it for
+		 * erasure.
+		 */
+		dbg_wl("PEB %d was put meanwhile, erase", e2->pnum);
+		err = schedule_erase(ubi, e2, 0);
+		if (err) {
+			kmem_cache_free(wl_entries_slab, e2);
+			ubi_ro_mode(ubi);
+		}
+	}
+
+	err = schedule_erase(ubi, e1, 0);
+	if (err) {
+		kmem_cache_free(wl_entries_slab, e1);
+		ubi_ro_mode(ubi);
+	}
+
+	dbg_wl("done");
+	return err;
+
+	/*
+	 * Some error occurred. @e1 was not changed, so return it back. @e2
+	 * might be changed, schedule it for erasure.
+	 */
+error:
+	if (err)
+		dbg_wl("error %d occurred, cancel operation", err);
+	ubi_assert(err <= 0);
+
+	ubi_free_vid_hdr(ubi, vid_hdr);
+	spin_lock(&ubi->wl_lock);
+	ubi->wl_scheduled = 0;
+	if (ubi->move_from_put)
+		put = 1;
+	else
+		used_tree_add(ubi, e1);
+	ubi->move_from = ubi->move_to = NULL;
+	ubi->move_from_put = ubi->move_to_put = 0;
+	spin_unlock(&ubi->wl_lock);
+
+	if (put) {
+		/*
+		 * Well, the target PEB was put meanwhile, schedule it for
+		 * erasure.
+		 */
+		dbg_wl("PEB %d was put meanwhile, erase", e1->pnum);
+		err = schedule_erase(ubi, e1, 0);
+		if (err) {
+			kmem_cache_free(wl_entries_slab, e1);
+			ubi_ro_mode(ubi);
+		}
+	}
+
+	err = schedule_erase(ubi, e2, 0);
+	if (err) {
+		kmem_cache_free(wl_entries_slab, e2);
+		ubi_ro_mode(ubi);
+	}
+
+	yield();
+	return err;
+}
+
+/**
+ * ensure_wear_leveling - schedule wear-leveling if it is needed.
+ * @ubi: UBI device description object
+ *
+ * This function checks if it is time to start wear-leveling and schedules it
+ * if yes. This function returns zero in case of success and a negative error
+ * code in case of failure.
+ */
+static int ensure_wear_leveling(struct ubi_device *ubi)
+{
+	int err = 0;
+	struct ubi_wl_entry *e1;
+	struct ubi_wl_entry *e2;
+	struct ubi_work *wrk;
+
+	spin_lock(&ubi->wl_lock);
+	if (ubi->wl_scheduled)
+		/* Wear-leveling is already in the work queue */
+		goto out_unlock;
+
+	/*
+	 * If the ubi->scrub tree is not empty, scrubbing is needed, and the
+	 * the WL worker has to be scheduled anyway.
+	 */
+	if (tree_empty(&ubi->scrub)) {
+		if (tree_empty(&ubi->used) || tree_empty(&ubi->free))
+			/* No physical eraseblocks - no deal */
+			goto out_unlock;
+
+		/*
+		 * We schedule wear-leveling only if the difference between the
+		 * lowest erase counter of used physical eraseblocks and a high
+		 * erase counter of free physical eraseblocks is greater then
+		 * %UBI_WL_THRESHOLD.
+		 */
+		e1 = rb_entry(rb_first(&ubi->used), struct ubi_wl_entry, rb);
+		e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF);
+
+		if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD))
+			goto out_unlock;
+		dbg_wl("schedule wear-leveling");
+	} else
+		dbg_wl("schedule scrubbing");
+
+	ubi->wl_scheduled = 1;
+	spin_unlock(&ubi->wl_lock);
+
+	wrk = kmalloc(sizeof(struct ubi_work), GFP_KERNEL);
+	if (!wrk) {
+		err = -ENOMEM;
+		goto out_cancel;
+	}
+
+	wrk->func = &wear_leveling_worker;
+	schedule_ubi_work(ubi, wrk);
+	return err;
+
+out_cancel:
+	spin_lock(&ubi->wl_lock);
+	ubi->wl_scheduled = 0;
+out_unlock:
+	spin_unlock(&ubi->wl_lock);
+	return err;
+}
+
+/**
+ * erase_worker - physical eraseblock erase worker function.
+ * @ubi: UBI device description object
+ * @wl_wrk: the work object
+ * @cancel: non-zero if the worker has to free memory and exit
+ *
+ * This function erases a physical eraseblock and perform torture testing if
+ * needed. It also takes care about marking the physical eraseblock bad if
+ * needed. Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
+			int cancel)
+{
+	int err;
+	struct ubi_wl_entry *e = wl_wrk->e;
+	int pnum = e->pnum;
+
+	if (cancel) {
+		dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec);
+		kfree(wl_wrk);
+		kmem_cache_free(wl_entries_slab, e);
+		return 0;
+	}
+
+	dbg_wl("erase PEB %d EC %d", pnum, e->ec);
+
+	err = sync_erase(ubi, e, wl_wrk->torture);
+	if (!err) {
+		/* Fine, we've erased it successfully */
+		kfree(wl_wrk);
+
+		spin_lock(&ubi->wl_lock);
+		ubi->abs_ec += 1;
+		free_tree_add(ubi, e);
+		spin_unlock(&ubi->wl_lock);
+
+		/*
+		 * One more erase operation has happened, take care about protected
+		 * physical eraseblocks.
+		 */
+		check_protection_over(ubi);
+
+		/* And take care about wear-leveling */
+		err = ensure_wear_leveling(ubi);
+		return err;
+	}
+
+	kfree(wl_wrk);
+	kmem_cache_free(wl_entries_slab, e);
+
+	if (err != -EIO) {
+		/*
+		 * If this is not %-EIO, we have no idea what to do. Scheduling
+		 * this physical eraseblock for erasure again would cause
+		 * errors again and again. Well, lets switch to RO mode.
+		 */
+		ubi_ro_mode(ubi);
+		return err;
+	}
+
+	/* It is %-EIO, the PEB went bad */
+
+	if (!ubi->bad_allowed) {
+		ubi_err("bad physical eraseblock %d detected", pnum);
+		ubi_ro_mode(ubi);
+		err = -EIO;
+	} else {
+		int need;
+
+		spin_lock(&ubi->volumes_lock);
+		need = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs + 1;
+		if (need > 0) {
+			need = ubi->avail_pebs >= need ? need : ubi->avail_pebs;
+			ubi->avail_pebs -= need;
+			ubi->rsvd_pebs += need;
+			ubi->beb_rsvd_pebs += need;
+			if (need > 0)
+				ubi_msg("reserve more %d PEBs", need);
+		}
+
+		if (ubi->beb_rsvd_pebs == 0) {
+			spin_unlock(&ubi->volumes_lock);
+			ubi_err("no reserved physical eraseblocks");
+			ubi_ro_mode(ubi);
+			return -EIO;
+		}
+
+		spin_unlock(&ubi->volumes_lock);
+		ubi_msg("mark PEB %d as bad", pnum);
+
+		err = ubi_io_mark_bad(ubi, pnum);
+		if (err) {
+			ubi_ro_mode(ubi);
+			return err;
+		}
+
+		spin_lock(&ubi->volumes_lock);
+		ubi->beb_rsvd_pebs -= 1;
+		ubi->bad_peb_count += 1;
+		ubi->good_peb_count -= 1;
+		ubi_calculate_reserved(ubi);
+		if (ubi->beb_rsvd_pebs == 0)
+			ubi_warn("last PEB from the reserved pool was used");
+		spin_unlock(&ubi->volumes_lock);
+	}
+
+	return err;
+}
+
+/**
+ * ubi_wl_put_peb - return a physical eraseblock to the wear-leveling
+ * unit.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock to return
+ * @torture: if this physical eraseblock has to be tortured
+ *
+ * This function is called to return physical eraseblock @pnum to the pool of
+ * free physical eraseblocks. The @torture flag has to be set if an I/O error
+ * occurred to this @pnum and it has to be tested. This function returns zero
+ * in case of success and a negative error code in case of failure.
+ */
+int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture)
+{
+	int err;
+	struct ubi_wl_entry *e;
+
+	dbg_wl("PEB %d", pnum);
+	ubi_assert(pnum >= 0);
+	ubi_assert(pnum < ubi->peb_count);
+
+	spin_lock(&ubi->wl_lock);
+
+	e = ubi->lookuptbl[pnum];
+	if (e == ubi->move_from) {
+		/*
+		 * User is putting the physical eraseblock which was selected to
+		 * be moved. It will be scheduled for erasure in the
+		 * wear-leveling worker.
+		 */
+		dbg_wl("PEB %d is being moved", pnum);
+		ubi_assert(!ubi->move_from_put);
+		ubi->move_from_put = 1;
+		spin_unlock(&ubi->wl_lock);
+		return 0;
+	} else if (e == ubi->move_to) {
+		/*
+		 * User is putting the physical eraseblock which was selected
+		 * as the target the data is moved to. It may happen if the EBA
+		 * unit already re-mapped the LEB but the WL unit did has not
+		 * put the PEB to the "used" tree.
+		 */
+		dbg_wl("PEB %d is the target of data moving", pnum);
+		ubi_assert(!ubi->move_to_put);
+		ubi->move_to_put = 1;
+		spin_unlock(&ubi->wl_lock);
+		return 0;
+	} else {
+		if (in_wl_tree(e, &ubi->used))
+			used_tree_del(ubi, e);
+		else if (in_wl_tree(e, &ubi->scrub))
+			scrub_tree_del(ubi, e);
+		else
+			prot_tree_del(ubi, e->pnum);
+	}
+	spin_unlock(&ubi->wl_lock);
+
+	err = schedule_erase(ubi, e, torture);
+	if (err) {
+		spin_lock(&ubi->wl_lock);
+		used_tree_add(ubi, e);
+		spin_unlock(&ubi->wl_lock);
+	}
+
+	return err;
+}
+
+/**
+ * ubi_wl_scrub_peb - schedule a physical eraseblock for scrubbing.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock to schedule
+ *
+ * If a bit-flip in a physical eraseblock is detected, this physical eraseblock
+ * needs scrubbing. This function schedules a physical eraseblock for
+ * scrubbing which is done in background. This function returns zero in case of
+ * success and a negative error code in case of failure.
+ */
+int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum)
+{
+	struct ubi_wl_entry *e;
+
+	ubi_msg("schedule PEB %d for scrubbing", pnum);
+
+retry:
+	spin_lock(&ubi->wl_lock);
+	e = ubi->lookuptbl[pnum];
+	if (e == ubi->move_from || in_wl_tree(e, &ubi->scrub)) {
+		spin_unlock(&ubi->wl_lock);
+		return 0;
+	}
+
+	if (e == ubi->move_to) {
+		/*
+		 * This physical eraseblock was used to move data to. The data
+		 * was moved but the PEB was not yet inserted to the proper
+		 * tree. We should just wait a little and let the WL worker
+		 * proceed.
+		 */
+		spin_unlock(&ubi->wl_lock);
+		dbg_wl("the PEB %d is not in proper tree, retry", pnum);
+		yield();
+		goto retry;
+	}
+
+	if (in_wl_tree(e, &ubi->used))
+		used_tree_del(ubi, e);
+	else
+		prot_tree_del(ubi, pnum);
+
+	scrub_tree_add(ubi, e);
+	spin_unlock(&ubi->wl_lock);
+
+	/*
+	 * Technically scrubbing is the same as wear-leveling, so it is done
+	 * by the WL worker.
+	 */
+	return ensure_wear_leveling(ubi);
+}
+
+/**
+ * ubi_wl_flush - flush all pending works.
+ * @ubi: UBI device description object
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+int ubi_wl_flush(struct ubi_device *ubi)
+{
+	int err, pending_count;
+
+	pending_count = ubi->works_count;
+
+	dbg_wl("flush (%d pending works)", pending_count);
+
+	/*
+	 * Erase while the pending works queue is not empty, but not more then
+	 * the number of currently pending works.
+	 */
+	while (pending_count-- > 0) {
+		err = do_work(ubi);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+/**
+ * tree_destroy - destroy an RB-tree.
+ * @root: the root of the tree to destroy
+ */
+static void tree_destroy(struct rb_root *root)
+{
+	struct rb_node *rb;
+	struct ubi_wl_entry *e;
+
+	rb = root->rb_node;
+	while (rb) {
+		if (rb->rb_left)
+			rb = rb->rb_left;
+		else if (rb->rb_right)
+			rb = rb->rb_right;
+		else {
+			e = rb_entry(rb, struct ubi_wl_entry, rb);
+
+			rb = rb_parent(rb);
+			if (rb) {
+				if (rb->rb_left == &e->rb)
+					rb->rb_left = NULL;
+				else
+					rb->rb_right = NULL;
+			}
+
+			kmem_cache_free(wl_entries_slab, e);
+		}
+	}
+}
+
+/**
+ * ubi_thread - UBI background thread.
+ * @u: the UBI device description object pointer
+ */
+static int ubi_thread(void *u)
+{
+	int failures = 0;
+	struct ubi_device *ubi = u;
+
+	ubi_msg("background thread \"%s\" started, PID %d",
+		ubi->bgt_name, current->pid);
+
+	for (;;) {
+		int err;
+
+		if (kthread_should_stop())
+			goto out;
+
+		if (try_to_freeze())
+			continue;
+
+		spin_lock(&ubi->wl_lock);
+		if (list_empty(&ubi->works) || ubi->ro_mode ||
+			       !ubi->thread_enabled) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			spin_unlock(&ubi->wl_lock);
+			schedule();
+			continue;
+		}
+		spin_unlock(&ubi->wl_lock);
+
+		err = do_work(ubi);
+		if (err) {
+			ubi_err("%s: work failed with error code %d",
+				ubi->bgt_name, err);
+			if (failures++ > WL_MAX_FAILURES) {
+				/*
+				 * Too many failures, disable the thread and
+				 * switch to read-only mode.
+				 */
+				ubi_msg("%s: %d consecutive failures",
+					ubi->bgt_name, WL_MAX_FAILURES);
+				ubi_ro_mode(ubi);
+				break;
+			}
+		} else
+			failures = 0;
+
+		cond_resched();
+	}
+
+out:
+	dbg_wl("background thread \"%s\" is killed", ubi->bgt_name);
+	return 0;
+}
+
+/**
+ * cancel_pending - cancel all pending works.
+ * @ubi: UBI device description object
+ */
+static void cancel_pending(struct ubi_device *ubi)
+{
+	while (!list_empty(&ubi->works)) {
+		struct ubi_work *wrk;
+
+		wrk = list_entry(ubi->works.next, struct ubi_work, list);
+		list_del(&wrk->list);
+		wrk->func(ubi, wrk, 1);
+		ubi->works_count -= 1;
+		ubi_assert(ubi->works_count >= 0);
+	}
+}
+
+/**
+ * ubi_wl_init_scan - initialize the wear-leveling unit using scanning
+ * information.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ *
+ * This function returns zero in case of success, and a negative error code in
+ * case of failure.
+ */
+int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
+{
+	int err;
+	struct rb_node *rb1, *rb2;
+	struct ubi_scan_volume *sv;
+	struct ubi_scan_leb *seb, *tmp;
+	struct ubi_wl_entry *e;
+
+
+	ubi->used = ubi->free = ubi->scrub = RB_ROOT;
+	ubi->prot.pnum = ubi->prot.aec = RB_ROOT;
+	spin_lock_init(&ubi->wl_lock);
+	ubi->max_ec = si->max_ec;
+	INIT_LIST_HEAD(&ubi->works);
+
+	sprintf(ubi->bgt_name, UBI_BGT_NAME_PATTERN, ubi->ubi_num);
+
+	ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name);
+	if (IS_ERR(ubi->bgt_thread)) {
+		err = PTR_ERR(ubi->bgt_thread);
+		ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name,
+			err);
+		return err;
+	}
+
+	if (ubi_devices_cnt == 0) {
+		wl_entries_slab = kmem_cache_create("ubi_wl_entry_slab",
+						    sizeof(struct ubi_wl_entry),
+						    0, 0, NULL, NULL);
+		if (!wl_entries_slab)
+			return -ENOMEM;
+	}
+
+	err = -ENOMEM;
+	ubi->lookuptbl = kzalloc(ubi->peb_count * sizeof(void *), GFP_KERNEL);
+	if (!ubi->lookuptbl)
+		goto out_free;
+
+	list_for_each_entry_safe(seb, tmp, &si->erase, u.list) {
+		cond_resched();
+
+		e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL);
+		if (!e)
+			goto out_free;
+
+		e->pnum = seb->pnum;
+		e->ec = seb->ec;
+		ubi->lookuptbl[e->pnum] = e;
+		if (schedule_erase(ubi, e, 0)) {
+			kmem_cache_free(wl_entries_slab, e);
+			goto out_free;
+		}
+	}
+
+	list_for_each_entry(seb, &si->free, u.list) {
+		cond_resched();
+
+		e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL);
+		if (!e)
+			goto out_free;
+
+		e->pnum = seb->pnum;
+		e->ec = seb->ec;
+		ubi_assert(e->ec >= 0);
+		free_tree_add(ubi, e);
+		ubi->lookuptbl[e->pnum] = e;
+	}
+
+	list_for_each_entry(seb, &si->corr, u.list) {
+		cond_resched();
+
+		e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL);
+		if (!e)
+			goto out_free;
+
+		e->pnum = seb->pnum;
+		e->ec = seb->ec;
+		ubi->lookuptbl[e->pnum] = e;
+		if (schedule_erase(ubi, e, 0)) {
+			kmem_cache_free(wl_entries_slab, e);
+			goto out_free;
+		}
+	}
+
+	ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) {
+		ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) {
+			cond_resched();
+
+			e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL);
+			if (!e)
+				goto out_free;
+
+			e->pnum = seb->pnum;
+			e->ec = seb->ec;
+			ubi->lookuptbl[e->pnum] = e;
+			if (!seb->scrub) {
+				dbg_wl("add PEB %d EC %d to the used tree",
+				       e->pnum, e->ec);
+				used_tree_add(ubi, e);
+			} else {
+				dbg_wl("add PEB %d EC %d to the scrub tree",
+				       e->pnum, e->ec);
+				scrub_tree_add(ubi, e);
+			}
+		}
+	}
+
+	if (WL_RESERVED_PEBS > ubi->avail_pebs) {
+		ubi_err("no enough physical eraseblocks (%d, need %d)",
+			ubi->avail_pebs, WL_RESERVED_PEBS);
+		goto out_free;
+	}
+	ubi->avail_pebs -= WL_RESERVED_PEBS;
+	ubi->rsvd_pebs += WL_RESERVED_PEBS;
+
+	/* Schedule wear-leveling if needed */
+	err = ensure_wear_leveling(ubi);
+	if (err)
+		goto out_free;
+
+	return 0;
+
+out_free:
+	cancel_pending(ubi);
+	tree_destroy(&ubi->used);
+	tree_destroy(&ubi->free);
+	tree_destroy(&ubi->scrub);
+	kfree(ubi->lookuptbl);
+	if (ubi_devices_cnt == 0)
+		kmem_cache_destroy(wl_entries_slab);
+	return err;
+}
+
+/**
+ * protection_trees_destroy - destroy the protection RB-trees.
+ * @ubi: UBI device description object
+ */
+static void protection_trees_destroy(struct ubi_device *ubi)
+{
+	struct rb_node *rb;
+	struct ubi_wl_prot_entry *pe;
+
+	rb = ubi->prot.aec.rb_node;
+	while (rb) {
+		if (rb->rb_left)
+			rb = rb->rb_left;
+		else if (rb->rb_right)
+			rb = rb->rb_right;
+		else {
+			pe = rb_entry(rb, struct ubi_wl_prot_entry, rb_aec);
+
+			rb = rb_parent(rb);
+			if (rb) {
+				if (rb->rb_left == &pe->rb_aec)
+					rb->rb_left = NULL;
+				else
+					rb->rb_right = NULL;
+			}
+
+			kmem_cache_free(wl_entries_slab, pe->e);
+			kfree(pe);
+		}
+	}
+}
+
+/**
+ * ubi_wl_close - close the wear-leveling unit.
+ * @ubi: UBI device description object
+ */
+void ubi_wl_close(struct ubi_device *ubi)
+{
+	dbg_wl("disable \"%s\"", ubi->bgt_name);
+	if (ubi->bgt_thread)
+		kthread_stop(ubi->bgt_thread);
+
+	dbg_wl("close the UBI wear-leveling unit");
+
+	cancel_pending(ubi);
+	protection_trees_destroy(ubi);
+	tree_destroy(&ubi->used);
+	tree_destroy(&ubi->free);
+	tree_destroy(&ubi->scrub);
+	kfree(ubi->lookuptbl);
+	if (ubi_devices_cnt == 1)
+		kmem_cache_destroy(wl_entries_slab);
+}
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+
+/**
+ * paranoid_check_ec - make sure that the erase counter of a physical eraseblock
+ * is correct.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to check
+ * @ec: the erase counter to check
+ *
+ * This function returns zero if the erase counter of physical eraseblock @pnum
+ * is equivalent to @ec, %1 if not, and a negative error code if an error
+ * occurred.
+ */
+static int paranoid_check_ec(const struct ubi_device *ubi, int pnum, int ec)
+{
+	int err;
+	long long read_ec;
+	struct ubi_ec_hdr *ec_hdr;
+
+	ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
+	if (!ec_hdr)
+		return -ENOMEM;
+
+	err = ubi_io_read_ec_hdr(ubi, pnum, ec_hdr, 0);
+	if (err && err != UBI_IO_BITFLIPS) {
+		/* The header does not have to exist */
+		err = 0;
+		goto out_free;
+	}
+
+	read_ec = ubi64_to_cpu(ec_hdr->ec);
+	if (ec != read_ec) {
+		ubi_err("paranoid check failed for PEB %d", pnum);
+		ubi_err("read EC is %lld, should be %d", read_ec, ec);
+		ubi_dbg_dump_stack();
+		err = 1;
+	} else
+		err = 0;
+
+out_free:
+	kfree(ec_hdr);
+	return err;
+}
+
+/**
+ * paranoid_check_in_wl_tree - make sure that a wear-leveling entry is present
+ * in a WL RB-tree.
+ * @e: the wear-leveling entry to check
+ * @root: the root of the tree
+ *
+ * This function returns zero if @e is in the @root RB-tree and %1 if it
+ * is not.
+ */
+static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e,
+				     struct rb_root *root)
+{
+	if (in_wl_tree(e, root))
+		return 0;
+
+	ubi_err("paranoid check failed for PEB %d, EC %d, RB-tree %p ",
+		e->pnum, e->ec, root);
+	ubi_dbg_dump_stack();
+	return 1;
+}
+
+#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */
diff --git a/drivers/net/3c501.c b/drivers/net/3c501.c
index 06e3378..4bee99b 100644
--- a/drivers/net/3c501.c
+++ b/drivers/net/3c501.c
@@ -735,7 +735,6 @@
 	else
 	{
     		skb_reserve(skb,2);	/* Force 16 byte alignment */
-		skb->dev = dev;
 		/*
 		 *	The read increments through the bytes. The interrupt
 		 *	handler will fix the pointer when it returns to
diff --git a/drivers/net/3c505.c b/drivers/net/3c505.c
index 702bfb2..e985a85 100644
--- a/drivers/net/3c505.c
+++ b/drivers/net/3c505.c
@@ -615,7 +615,6 @@
 	if (test_and_set_bit(0, (void *) &adapter->dmaing))
 		printk(KERN_ERR "%s: rx blocked, DMA in progress, dir %d\n", dev->name, adapter->current_dma.direction);
 
-	skb->dev = dev;
 	adapter->current_dma.direction = 0;
 	adapter->current_dma.length = rlen;
 	adapter->current_dma.skb = skb;
@@ -1026,7 +1025,7 @@
 	adapter->current_dma.start_time = jiffies;
 
 	if ((unsigned long)(skb->data + nlen) >= MAX_DMA_ADDRESS || nlen != skb->len) {
-		memcpy(adapter->dma_buffer, skb->data, nlen);
+		skb_copy_from_linear_data(skb, adapter->dma_buffer, nlen);
 		memset(adapter->dma_buffer+skb->len, 0, nlen-skb->len);
 		target = isa_virt_to_bus(adapter->dma_buffer);
 	}
diff --git a/drivers/net/3c507.c b/drivers/net/3c507.c
index 54e1d5a..eed4299 100644
--- a/drivers/net/3c507.c
+++ b/drivers/net/3c507.c
@@ -873,7 +873,6 @@
 			}
 
 			skb_reserve(skb,2);
-			skb->dev = dev;
 
 			/* 'skb->data' points to the start of sk_buff data area. */
 			memcpy_fromio(skb_put(skb,pkt_len), data_frame + 10, pkt_len);
diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c
index f791bf0..9588da3 100644
--- a/drivers/net/3c509.c
+++ b/drivers/net/3c509.c
@@ -83,7 +83,6 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/pm.h>
-#include <linux/pm_legacy.h>
 #include <linux/skbuff.h>
 #include <linux/delay.h>	/* for udelay() */
 #include <linux/spinlock.h>
@@ -1091,7 +1090,6 @@
 				printk("Receiving packet size %d status %4.4x.\n",
 					   pkt_len, rx_status);
 			if (skb != NULL) {
-				skb->dev = dev;
 				skb_reserve(skb, 2);     /* Align IP on 16 byte */
 
 				/* 'skb->data' points to the start of sk_buff data area. */
diff --git a/drivers/net/3c515.c b/drivers/net/3c515.c
index c307ce6..290166d 100644
--- a/drivers/net/3c515.c
+++ b/drivers/net/3c515.c
@@ -1292,7 +1292,6 @@
 				printk("Receiving packet size %d status %4.4x.\n",
 				     pkt_len, rx_status);
 			if (skb != NULL) {
-				skb->dev = dev;
 				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
 				/* 'skb_put()' points to the start of sk_buff data area. */
 				insl(ioaddr + RX_FIFO,
@@ -1363,7 +1362,6 @@
 			   copying to a properly sized skbuff. */
 			if (pkt_len < rx_copybreak
 			    && (skb = dev_alloc_skb(pkt_len + 4)) != 0) {
-				skb->dev = dev;
 				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
 				/* 'skb_put()' points to the start of sk_buff data area. */
 				memcpy(skb_put(skb, pkt_len),
diff --git a/drivers/net/3c523.c b/drivers/net/3c523.c
index 17d61eb..da1a22c 100644
--- a/drivers/net/3c523.c
+++ b/drivers/net/3c523.c
@@ -988,7 +988,6 @@
 				rbd->status = 0;
 				skb = (struct sk_buff *) dev_alloc_skb(totlen + 2);
 				if (skb != NULL) {
-					skb->dev = dev;
 					skb_reserve(skb, 2);	/* 16 byte alignment */
 					skb_put(skb,totlen);
 					eth_copy_and_sum(skb, (char *) p->base+(unsigned long) rbd->buffer,totlen,0);
@@ -1146,7 +1145,7 @@
 
 	if (len != skb->len)
 		memset((char *) p->xmit_cbuffs[p->xmit_count], 0, ETH_ZLEN);
-	memcpy((char *) p->xmit_cbuffs[p->xmit_count], (char *) (skb->data), skb->len);
+	skb_copy_from_linear_data(skb, (char *) p->xmit_cbuffs[p->xmit_count], skb->len);
 
 #if (NUM_XMIT_BUFFS == 1)
 #ifdef NO_NOPCOMMANDS
diff --git a/drivers/net/3c527.c b/drivers/net/3c527.c
index 6c7437e..c7b571b 100644
--- a/drivers/net/3c527.c
+++ b/drivers/net/3c527.c
@@ -1189,7 +1189,6 @@
 			}
 
 			skb->protocol=eth_type_trans(skb,dev);
-			skb->dev=dev;
 			dev->last_rx = jiffies;
  			lp->net_stats.rx_packets++;
  			lp->net_stats.rx_bytes += length;
diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c
index b406ecf..80924f7 100644
--- a/drivers/net/3c59x.c
+++ b/drivers/net/3c59x.c
@@ -2414,7 +2414,6 @@
 				printk(KERN_DEBUG "Receiving packet size %d status %4.4x.\n",
 					   pkt_len, rx_status);
 			if (skb != NULL) {
-				skb->dev = dev;
 				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
 				/* 'skb_put()' points to the start of sk_buff data area. */
 				if (vp->bus_master &&
@@ -2491,7 +2490,6 @@
 			/* Check if the packet is long enough to just accept without
 			   copying to a properly sized skbuff. */
 			if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != 0) {
-				skb->dev = dev;
 				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
 				pci_dma_sync_single_for_cpu(VORTEX_PCI(vp), dma, PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
 				/* 'skb_put()' points to the start of sk_buff data area. */
diff --git a/drivers/net/7990.c b/drivers/net/7990.c
index 1b3d11e..d396f99 100644
--- a/drivers/net/7990.c
+++ b/drivers/net/7990.c
@@ -331,7 +331,6 @@
                                 return 0;
                         }
 
-                        skb->dev = dev;
                         skb_reserve (skb, 2);           /* 16 byte align */
                         skb_put (skb, len);             /* make room */
                         eth_copy_and_sum(skb,
@@ -568,7 +567,7 @@
 
     	if (skb->len < ETH_ZLEN)
     		memset((char *)&ib->tx_buf[entry][0], 0, ETH_ZLEN);
-        memcpy ((char *)&ib->tx_buf [entry][0], skb->data, skblen);
+        skb_copy_from_linear_data(skb, &ib->tx_buf[entry][0], skblen);
 
         /* Now, give the packet to the lance */
         ib->btx_ring [entry].tmd1_bits = (LE_T1_POK|LE_T1_OWN);
diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c
index 12c8453..e8c9f278 100644
--- a/drivers/net/8139cp.c
+++ b/drivers/net/8139cp.c
@@ -573,7 +573,6 @@
 		}
 
 		skb_reserve(new_skb, RX_OFFSET);
-		new_skb->dev = dev;
 
 		pci_unmap_single(cp->pdev, mapping,
 				 buflen, PCI_DMA_FROMDEVICE);
@@ -807,7 +806,7 @@
 		if (mss)
 			flags |= LargeSend | ((mss & MSSMask) << MSSShift);
 		else if (skb->ip_summed == CHECKSUM_PARTIAL) {
-			const struct iphdr *ip = skb->nh.iph;
+			const struct iphdr *ip = ip_hdr(skb);
 			if (ip->protocol == IPPROTO_TCP)
 				flags |= IPCS | TCPCS;
 			else if (ip->protocol == IPPROTO_UDP)
@@ -826,7 +825,7 @@
 		u32 first_len, first_eor;
 		dma_addr_t first_mapping;
 		int frag, first_entry = entry;
-		const struct iphdr *ip = skb->nh.iph;
+		const struct iphdr *ip = ip_hdr(skb);
 
 		/* We must give this initial chunk to the device last.
 		 * Otherwise we could race with the device.
@@ -1082,7 +1081,6 @@
 		if (!skb)
 			goto err_out;
 
-		skb->dev = cp->dev;
 		skb_reserve(skb, RX_OFFSET);
 
 		mapping = pci_map_single(cp->pdev, skb->data, cp->rx_buf_sz,
diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c
index 99304b2..a844b1f 100644
--- a/drivers/net/8139too.c
+++ b/drivers/net/8139too.c
@@ -1904,10 +1904,10 @@
 	u32 left = RX_BUF_LEN - offset;
 
 	if (size > left) {
-		memcpy(skb->data, ring + offset, left);
-		memcpy(skb->data+left, ring, size - left);
+		skb_copy_to_linear_data(skb, ring + offset, left);
+		skb_copy_to_linear_data_offset(skb, left, ring, size - left);
 	} else
-		memcpy(skb->data, ring + offset, size);
+		skb_copy_to_linear_data(skb, ring + offset, size);
 }
 #endif
 
@@ -2013,7 +2013,6 @@
 
 		skb = dev_alloc_skb (pkt_size + 2);
 		if (likely(skb)) {
-			skb->dev = dev;
 			skb_reserve (skb, 2);	/* 16 byte align the IP fields. */
 #if RX_BUF_IDX == 3
 			wrap_copy(skb, rx_ring, ring_offset+4, pkt_size);
diff --git a/drivers/net/82596.c b/drivers/net/82596.c
index 640d7ca2e..3ff1155 100644
--- a/drivers/net/82596.c
+++ b/drivers/net/82596.c
@@ -830,7 +830,6 @@
 				lp->stats.rx_dropped++;
 			}
 			else {
-				skb->dev = dev;
 				if (!rx_in_place) {
 					/* 16 byte align the data fields */
 					skb_reserve(skb, 2);
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index a3d46ea..dcdad21 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -486,8 +486,8 @@
 	  enables offloading for checksums on transmit.  If unsure, say Y.
 
 config MIPS_SIM_NET
-	tristate "MIPS simulator Network device (EXPERIMENTAL)"
-	depends on MIPS_SIM && EXPERIMENTAL
+	tristate "MIPS simulator Network device"
+	depends on NET_ETHERNET && MIPS_SIM
 	help
 	  The MIPSNET device is a simple Ethernet network device which is
 	  emulated by the MIPS Simulator.
@@ -1444,7 +1444,8 @@
 
 config TC35815
 	tristate "TOSHIBA TC35815 Ethernet support"
-	depends on NET_PCI && PCI && TOSHIBA_JMR3927
+	depends on NET_PCI && PCI && MIPS
+	select MII
 
 config DGRS
 	tristate "Digi Intl. RightSwitch SE-X support"
@@ -2273,11 +2274,12 @@
 	depends on GIANFAR
 
 config UCC_GETH
-	tristate "Freescale QE UCC GETH"
-	depends on QUICC_ENGINE && UCC_FAST
+	tristate "Freescale QE Gigabit Ethernet"
+	depends on QUICC_ENGINE
+	select UCC_FAST
 	help
-	  This driver supports the Gigabit Ethernet mode of QE UCC.
-	  QE can be found on MPC836x CPUs.
+	  This driver supports the Gigabit Ethernet mode of the QUICC Engine,
+	  which is available on some Freescale SOCs.
 
 config UGETH_NAPI
 	bool "NAPI Support"
@@ -2291,14 +2293,10 @@
 	bool "Mac address filtering support"
 	depends on UCC_GETH
 
-config UGETH_TX_ON_DEMOND
-	bool "Transmit on Demond support"
+config UGETH_TX_ON_DEMAND
+	bool "Transmit on Demand support"
 	depends on UCC_GETH
 
-config UGETH_HAS_GIGA
-	bool
-	depends on UCC_GETH && PPC_MPC836x
-
 config MV643XX_ETH
 	tristate "MV-643XX Ethernet support"
 	depends on MOMENCO_OCELOT_C || MOMENCO_JAGUAR_ATX || MV64360 || MOMENCO_OCELOT_3 || (PPC_MULTIPLATFORM && PPC32)
@@ -2929,11 +2927,6 @@
 config NETPOLL
 	def_bool NETCONSOLE
 
-config NETPOLL_RX
-	bool "Netpoll support for trapping incoming packets"
-	default n
-	depends on NETPOLL
-
 config NETPOLL_TRAP
 	bool "Netpoll traffic trapping"
 	default n
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 33af833..59c0459 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -18,7 +18,7 @@
 		gianfar_sysfs.o
 
 obj-$(CONFIG_UCC_GETH) += ucc_geth_driver.o
-ucc_geth_driver-objs := ucc_geth.o ucc_geth_phy.o
+ucc_geth_driver-objs := ucc_geth.o ucc_geth_mii.o
 
 #
 # link order important here
@@ -206,7 +206,7 @@
 obj-$(CONFIG_WAN) += wan/
 obj-$(CONFIG_ARCNET) += arcnet/
 obj-$(CONFIG_NET_PCMCIA) += pcmcia/
-obj-$(CONFIG_NET_RADIO) += wireless/
+obj-y += wireless/
 obj-$(CONFIG_NET_TULIP) += tulip/
 obj-$(CONFIG_HAMRADIO) += hamradio/
 obj-$(CONFIG_IRDA) += irda/
diff --git a/drivers/net/a2065.c b/drivers/net/a2065.c
index d76548e..1226cbb 100644
--- a/drivers/net/a2065.c
+++ b/drivers/net/a2065.c
@@ -320,7 +320,6 @@
 				return 0;
 			}
 
-			skb->dev = dev;
 			skb_reserve (skb, 2);		/* 16 byte align */
 			skb_put (skb, len);		/* make room */
 			eth_copy_and_sum(skb,
@@ -599,7 +598,7 @@
 	ib->btx_ring [entry].length = (-len) | 0xf000;
 	ib->btx_ring [entry].misc = 0;
 
-	memcpy ((char *)&ib->tx_buf [entry][0], skb->data, skblen);
+	skb_copy_from_linear_data(skb, &ib->tx_buf [entry][0], skblen);
 
 	/* Clear the slack of the packet, do I need this? */
 	if (len != skblen)
diff --git a/drivers/net/acenic.c b/drivers/net/acenic.c
index 7138e0e..7122b7b 100644
--- a/drivers/net/acenic.c
+++ b/drivers/net/acenic.c
@@ -2027,7 +2027,6 @@
 		 */
 		csum = retdesc->tcp_udp_csum;
 
-		skb->dev = dev;
 		skb->protocol = eth_type_trans(skb, dev);
 
 		/*
diff --git a/drivers/net/amd8111e.c b/drivers/net/amd8111e.c
index 962c954..675fe91 100644
--- a/drivers/net/amd8111e.c
+++ b/drivers/net/amd8111e.c
@@ -798,9 +798,7 @@
 			pci_unmap_single(lp->pci_dev,lp->rx_dma_addr[rx_index],
 					 lp->rx_buff_len-2, PCI_DMA_FROMDEVICE);
 			skb_put(skb, pkt_len);
-			skb->dev = dev;
 			lp->rx_skbuff[rx_index] = new_skb;
-			new_skb->dev = dev;
 			lp->rx_dma_addr[rx_index] = pci_map_single(lp->pci_dev,
 								   new_skb->data,
 								   lp->rx_buff_len-2,
@@ -926,9 +924,7 @@
 		pci_unmap_single(lp->pci_dev,lp->rx_dma_addr[rx_index],
 			lp->rx_buff_len-2, PCI_DMA_FROMDEVICE);
 		skb_put(skb, pkt_len);
-		skb->dev = dev;
 		lp->rx_skbuff[rx_index] = new_skb;
-		new_skb->dev = dev;
 		lp->rx_dma_addr[rx_index] = pci_map_single(lp->pci_dev,
 			new_skb->data, lp->rx_buff_len-2,PCI_DMA_FROMDEVICE);
 
diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c
index dba5e51..da6ffa8 100644
--- a/drivers/net/appletalk/cops.c
+++ b/drivers/net/appletalk/cops.c
@@ -853,9 +853,9 @@
                 return;
         }
 
-        skb->mac.raw    = skb->data;    /* Point to entire packet. */
+        skb_reset_mac_header(skb);    /* Point to entire packet. */
         skb_pull(skb,3);
-        skb->h.raw      = skb->data;    /* Point to data (Skip header). */
+        skb_reset_transport_header(skb);    /* Point to data (Skip header). */
 
         /* Update the counters. */
         lp->stats.rx_packets++;
diff --git a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c
index 2ea44ce..6a6cbd3 100644
--- a/drivers/net/appletalk/ltpc.c
+++ b/drivers/net/appletalk/ltpc.c
@@ -770,13 +770,13 @@
 	skb->data[0] = dnode;
 	skb->data[1] = snode;
 	skb->data[2] = llaptype;
-	skb->mac.raw = skb->data;	/* save pointer to llap header */
+	skb_reset_mac_header(skb);	/* save pointer to llap header */
 	skb_pull(skb,3);
 
 	/* copy ddp(s,e)hdr + contents */
-	memcpy(skb->data,(void*)ltdmabuf,len);
+	skb_copy_to_linear_data(skb, ltdmabuf, len);
 
-	skb->h.raw = skb->data;
+	skb_reset_transport_header(skb);
 
 	stats->rx_packets++;
 	stats->rx_bytes+=skb->len;
@@ -917,13 +917,14 @@
 
 	int i;
 	struct lt_sendlap cbuf;
+	unsigned char *hdr;
 
 	cbuf.command = LT_SENDLAP;
 	cbuf.dnode = skb->data[0];
 	cbuf.laptype = skb->data[2];
 	skb_pull(skb,3);	/* skip past LLAP header */
 	cbuf.length = skb->len;	/* this is host order */
-	skb->h.raw=skb->data;
+	skb_reset_transport_header(skb);
 
 	if(debug & DEBUG_UPPER) {
 		printk("command ");
@@ -932,11 +933,13 @@
 		printk("\n");
 	}
 
-	do_write(dev,&cbuf,sizeof(cbuf),skb->h.raw,skb->len);
+	hdr = skb_transport_header(skb);
+	do_write(dev, &cbuf, sizeof(cbuf), hdr, skb->len);
 
 	if(debug & DEBUG_UPPER) {
 		printk("sent %d ddp bytes\n",skb->len);
-		for(i=0;i<skb->len;i++) printk("%02x ",skb->h.raw[i]);
+		for (i = 0; i < skb->len; i++)
+			printk("%02x ", hdr[i]);
 		printk("\n");
 	}
 
diff --git a/drivers/net/arcnet/arc-rawmode.c b/drivers/net/arcnet/arc-rawmode.c
index 6318814..e0a18e7 100644
--- a/drivers/net/arcnet/arc-rawmode.c
+++ b/drivers/net/arcnet/arc-rawmode.c
@@ -110,7 +110,7 @@
 
 	pkt = (struct archdr *) skb->data;
 
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	skb_pull(skb, ARC_HDR_SIZE);
 
 	/* up to sizeof(pkt->soft) has already been copied from the card */
diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c
index 83004fd..681e20b 100644
--- a/drivers/net/arcnet/arcnet.c
+++ b/drivers/net/arcnet/arcnet.c
@@ -519,9 +519,12 @@
 		 * real header when we do rebuild_header.
 		 */
 		*(uint16_t *) skb_push(skb, 2) = type;
-		if (skb->nh.raw - skb->mac.raw != 2)
+		/*
+		 * XXX: Why not use skb->mac_len?
+		 */
+		if (skb->network_header - skb->mac_header != 2)
 			BUGMSG(D_NORMAL, "arcnet_header: Yikes!  diff (%d) is not 2!\n",
-			       (int)(skb->nh.raw - skb->mac.raw));
+			       (int)(skb->network_header - skb->mac_header));
 		return -2;	/* return error -- can't transmit yet! */
 	}
 	else {
@@ -554,11 +557,13 @@
 	unsigned short type;
 	uint8_t daddr=0;
 	struct ArcProto *proto;
-
-	if (skb->nh.raw - skb->mac.raw != 2) {
+	/*
+	 * XXX: Why not use skb->mac_len?
+	 */
+	if (skb->network_header - skb->mac_header != 2) {
 		BUGMSG(D_NORMAL,
-		     "rebuild_header: shouldn't be here! (hdrsize=%d)\n",
-		     (int)(skb->nh.raw - skb->mac.raw));
+		       "rebuild_header: shouldn't be here! (hdrsize=%d)\n",
+		       (int)(skb->network_header - skb->mac_header));
 		return 0;
 	}
 	type = *(uint16_t *) skb_pull(skb, 2);
diff --git a/drivers/net/arcnet/capmode.c b/drivers/net/arcnet/capmode.c
index 6648558..cc4610d 100644
--- a/drivers/net/arcnet/capmode.c
+++ b/drivers/net/arcnet/capmode.c
@@ -122,10 +122,8 @@
 	}
 	skb_put(skb, length + ARC_HDR_SIZE + sizeof(int));
 	skb->dev = dev;
-
-	pkt = (struct archdr *) skb->data;
-
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
+	pkt = (struct archdr *)skb_mac_header(skb);
 	skb_pull(skb, ARC_HDR_SIZE);
 
 	/* up to sizeof(pkt->soft) has already been copied from the card */
@@ -270,13 +268,13 @@
   skb_put(ackskb, length + ARC_HDR_SIZE );
   ackskb->dev = dev;
 
-  ackpkt = (struct archdr *) ackskb->data;
-
-  ackskb->mac.raw = ackskb->data;
+  skb_reset_mac_header(ackskb);
+  ackpkt = (struct archdr *)skb_mac_header(ackskb);
   /* skb_pull(ackskb, ARC_HDR_SIZE); */
 
 
-  memcpy(ackpkt, lp->outgoing.skb->data, ARC_HDR_SIZE+sizeof(struct arc_cap));
+  skb_copy_from_linear_data(lp->outgoing.skb, ackpkt,
+		ARC_HDR_SIZE + sizeof(struct arc_cap));
   ackpkt->soft.cap.proto=0; /* using protocol 0 for acknowledge */
   ackpkt->soft.cap.mes.ack=acked;
 
diff --git a/drivers/net/arcnet/rfc1051.c b/drivers/net/arcnet/rfc1051.c
index 6d6c69f..2de8877 100644
--- a/drivers/net/arcnet/rfc1051.c
+++ b/drivers/net/arcnet/rfc1051.c
@@ -94,7 +94,7 @@
 	int hdr_size = ARC_HDR_SIZE + RFC1051_HDR_SIZE;
 
 	/* Pull off the arcnet header. */
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	skb_pull(skb, hdr_size);
 
 	if (pkt->hard.dest == 0)
diff --git a/drivers/net/arcnet/rfc1201.c b/drivers/net/arcnet/rfc1201.c
index bee3422..460a095 100644
--- a/drivers/net/arcnet/rfc1201.c
+++ b/drivers/net/arcnet/rfc1201.c
@@ -96,7 +96,7 @@
 	int hdr_size = ARC_HDR_SIZE + RFC1201_HDR_SIZE;
 
 	/* Pull off the arcnet header. */
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	skb_pull(skb, hdr_size);
 
 	if (pkt->hard.dest == 0)
diff --git a/drivers/net/ariadne.c b/drivers/net/ariadne.c
index 9dfc09b..a0e68e7 100644
--- a/drivers/net/ariadne.c
+++ b/drivers/net/ariadne.c
@@ -743,7 +743,6 @@
 	    }
 
 
-	    skb->dev = dev;
 	    skb_reserve(skb,2);		/* 16 byte align */
 	    skb_put(skb,pkt_len);	/* Make room */
 	    eth_copy_and_sum(skb, (char *)priv->rx_buff[entry], pkt_len,0);
diff --git a/drivers/net/arm/am79c961a.c b/drivers/net/arm/am79c961a.c
index ddd12d4..8f0d7ce 100644
--- a/drivers/net/arm/am79c961a.c
+++ b/drivers/net/arm/am79c961a.c
@@ -526,7 +526,6 @@
 		skb = dev_alloc_skb(len + 2);
 
 		if (skb) {
-			skb->dev = dev;
 			skb_reserve(skb, 2);
 
 			am_readbuffer(dev, pktaddr, skb_put(skb, len), len);
diff --git a/drivers/net/arm/at91_ether.c b/drivers/net/arm/at91_ether.c
index 1621b8f..152fa7a 100644
--- a/drivers/net/arm/at91_ether.c
+++ b/drivers/net/arm/at91_ether.c
@@ -858,7 +858,6 @@
 			skb_reserve(skb, 2);
 			memcpy(skb_put(skb, pktlen), p_recv, pktlen);
 
-			skb->dev = dev;
 			skb->protocol = eth_type_trans(skb, dev);
 			dev->last_rx = jiffies;
 			lp->stats.rx_bytes += pktlen;
diff --git a/drivers/net/arm/ep93xx_eth.c b/drivers/net/arm/ep93xx_eth.c
index dd698b0..2438c5b 100644
--- a/drivers/net/arm/ep93xx_eth.c
+++ b/drivers/net/arm/ep93xx_eth.c
@@ -255,7 +255,6 @@
 
 		skb = dev_alloc_skb(length + 2);
 		if (likely(skb != NULL)) {
-			skb->dev = dev;
 			skb_reserve(skb, 2);
 			dma_sync_single(NULL, ep->descs->rdesc[entry].buf_addr,
 						length, DMA_FROM_DEVICE);
diff --git a/drivers/net/arm/ether1.c b/drivers/net/arm/ether1.c
index a292188..f075ceb 100644
--- a/drivers/net/arm/ether1.c
+++ b/drivers/net/arm/ether1.c
@@ -875,7 +875,6 @@
 			skb = dev_alloc_skb (length + 2);
 
 			if (skb) {
-				skb->dev = dev;
 				skb_reserve (skb, 2);
 
 				ether1_readbuffer (dev, skb_put (skb, length), rbd.rbd_bufl, length);
diff --git a/drivers/net/arm/ether3.c b/drivers/net/arm/ether3.c
index 8411783..32da2eb 100644
--- a/drivers/net/arm/ether3.c
+++ b/drivers/net/arm/ether3.c
@@ -661,7 +661,6 @@
 			if (skb) {
 				unsigned char *buf;
 
-				skb->dev = dev;
 				skb_reserve(skb, 2);
 				buf = skb_put(skb, length);
 				ether3_readbuffer(dev, buf + 12, length - 12);
diff --git a/drivers/net/at1700.c b/drivers/net/at1700.c
index 56ae8ba..bed8e0eb 100644
--- a/drivers/net/at1700.c
+++ b/drivers/net/at1700.c
@@ -768,7 +768,6 @@
 				lp->stats.rx_dropped++;
 				break;
 			}
-			skb->dev = dev;
 			skb_reserve(skb,2);
 
 			insw(ioaddr + DATAPORT, skb_put(skb,pkt_len), (pkt_len + 1) >> 1);
diff --git a/drivers/net/atari_bionet.c b/drivers/net/atari_bionet.c
index 4e3bf6a..3d87bd2 100644
--- a/drivers/net/atari_bionet.c
+++ b/drivers/net/atari_bionet.c
@@ -453,7 +453,8 @@
 		stdma_lock(bionet_intr, NULL);
 		local_irq_restore(flags);
 		if( !STRAM_ADDR(buf+length-1) ) {
-			memcpy(nic_packet->buffer, skb->data, length);
+			skb_copy_from_linear_data(skb, nic_packet->buffer,
+						  length);
 			buf = (unsigned long)&((struct nic_pkt_s *)phys_nic_packet)->buffer;
 		}
 
@@ -544,13 +545,13 @@
 				break;
 			}
 
-			skb->dev = dev;
 			skb_reserve( skb, 2 );		/* 16 Byte align  */
 			skb_put( skb, pkt_len );	/* make room */
 
 			/* 'skb->data' points to the start of sk_buff data area.
 			 */
-			memcpy(skb->data, nic_packet->buffer, pkt_len);
+			skb_copy_to_linear_data(skb, nic_packet->buffer,
+						pkt_len);
 			skb->protocol = eth_type_trans( skb, dev );
 			netif_rx(skb);
 			dev->last_rx = jiffies;
diff --git a/drivers/net/atari_pamsnet.c b/drivers/net/atari_pamsnet.c
index 3b54361..5471440 100644
--- a/drivers/net/atari_pamsnet.c
+++ b/drivers/net/atari_pamsnet.c
@@ -717,7 +717,8 @@
 
 		local_irq_restore(flags);
 		if( !STRAM_ADDR(buf+length-1) ) {
-			memcpy(nic_packet->buffer, skb->data, length);
+			skb_copy_from_linear_data(skb, nic_packet->buffer,
+						  length);
 			buf = (unsigned long)phys_nic_packet;
 		}
 
@@ -792,7 +793,8 @@
 
 			/* 'skb->data' points to the start of sk_buff data area.
 			 */
-			memcpy(skb->data, nic_packet->buffer, pkt_len);
+			skb_copy_to_linear_data(skb, nic_packet->buffer,
+						pkt_len);
 			netif_rx(skb);
 			dev->last_rx = jiffies;
 			lp->stats.rx_packets++;
diff --git a/drivers/net/atarilance.c b/drivers/net/atarilance.c
index 7e37ac8..dfa8b9b 100644
--- a/drivers/net/atarilance.c
+++ b/drivers/net/atarilance.c
@@ -1047,7 +1047,6 @@
 						   pkt_len );
 				}
 
-				skb->dev = dev;
 				skb_reserve( skb, 2 );	/* 16 byte align */
 				skb_put( skb, pkt_len );	/* Make room */
 				lp->memcpy_f( skb->data, PKTBUF_ADDR(head), pkt_len );
diff --git a/drivers/net/atl1/atl1_main.c b/drivers/net/atl1/atl1_main.c
index 8606eac..4b1d4d1 100644
--- a/drivers/net/atl1/atl1_main.c
+++ b/drivers/net/atl1/atl1_main.c
@@ -408,7 +408,6 @@
 static u16 atl1_alloc_rx_buffers(struct atl1_adapter *adapter)
 {
 	struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring;
-	struct net_device *netdev = adapter->netdev;
 	struct pci_dev *pdev = adapter->pdev;
 	struct page *page;
 	unsigned long offset;
@@ -444,7 +443,6 @@
 		 * the 14 byte MAC header is removed
 		 */
 		skb_reserve(skb, NET_IP_ALIGN);
-		skb->dev = netdev;
 
 		buffer_info->alloced = 1;
 		buffer_info->skb = skb;
@@ -1296,19 +1294,21 @@
 		}
 
 		if (skb->protocol == ntohs(ETH_P_IP)) {
-			skb->nh.iph->tot_len = 0;
-			skb->nh.iph->check = 0;
-			skb->h.th->check =
-			    ~csum_tcpudp_magic(skb->nh.iph->saddr,
-					       skb->nh.iph->daddr, 0,
-					       IPPROTO_TCP, 0);
-			ipofst = skb->nh.raw - skb->data;
+			struct iphdr *iph = ip_hdr(skb);
+
+			iph->tot_len = 0;
+			iph->check = 0;
+			tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
+								 iph->daddr, 0,
+								 IPPROTO_TCP,
+								 0);
+			ipofst = skb_network_offset(skb);
 			if (ipofst != ENET_HEADER_SIZE) /* 802.3 frame */
 				tso->tsopl |= 1 << TSO_PARAM_ETHTYPE_SHIFT;
 
-			tso->tsopl |= (skb->nh.iph->ihl &
+			tso->tsopl |= (iph->ihl &
 				CSUM_PARAM_IPHL_MASK) << CSUM_PARAM_IPHL_SHIFT;
-			tso->tsopl |= ((skb->h.th->doff << 2) &
+			tso->tsopl |= (tcp_hdrlen(skb) &
 				TSO_PARAM_TCPHDRLEN_MASK) << TSO_PARAM_TCPHDRLEN_SHIFT;
 			tso->tsopl |= (skb_shinfo(skb)->gso_size &
 				TSO_PARAM_MSS_MASK) << TSO_PARAM_MSS_SHIFT;
@@ -1327,8 +1327,8 @@
 	u8 css, cso;
 
 	if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) {
-		cso = skb->h.raw - skb->data;
-		css = (skb->h.raw + skb->csum_offset) - skb->data;
+		cso = skb_transport_offset(skb);
+		css = cso + skb->csum_offset;
 		if (unlikely(cso & 0x1)) {
 			printk(KERN_DEBUG "%s: payload offset != even number\n",
 				atl1_driver_name);
@@ -1370,8 +1370,7 @@
 
 	if (tcp_seg) {
 		/* TSO/GSO */
-		proto_hdr_len =
-		    ((skb->h.raw - skb->data) + (skb->h.th->doff << 2));
+		proto_hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
 		buffer_info->length = proto_hdr_len;
 		page = virt_to_page(skb->data);
 		offset = (unsigned long)skb->data & ~PAGE_MASK;
@@ -1563,8 +1562,8 @@
 	mss = skb_shinfo(skb)->gso_size;
 	if (mss) {
 		if (skb->protocol == htons(ETH_P_IP)) {
-			proto_hdr_len = ((skb->h.raw - skb->data) +
-					 (skb->h.th->doff << 2));
+			proto_hdr_len = (skb_transport_offset(skb) +
+					 tcp_hdrlen(skb));
 			if (unlikely(proto_hdr_len > len)) {
 				dev_kfree_skb_any(skb);
 				return NETDEV_TX_OK;
diff --git a/drivers/net/atp.c b/drivers/net/atp.c
index 2d306fc..18aba83 100644
--- a/drivers/net/atp.c
+++ b/drivers/net/atp.c
@@ -793,7 +793,6 @@
 			lp->stats.rx_dropped++;
 			goto done;
 		}
-		skb->dev = dev;
 
 		skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
 		read_block(ioaddr, pkt_len, skb_put(skb,pkt_len), dev->if_port);
diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c
index 69ae229..d10fb80 100644
--- a/drivers/net/au1000_eth.c
+++ b/drivers/net/au1000_eth.c
@@ -1125,7 +1125,7 @@
 	}
 
 	pDB = aup->tx_db_inuse[aup->tx_head];
-	memcpy((void *)pDB->vaddr, skb->data, skb->len);
+	skb_copy_from_linear_data(skb, pDB->vaddr, skb->len);
 	if (skb->len < ETH_ZLEN) {
 		for (i=skb->len; i<ETH_ZLEN; i++) {
 			((char *)pDB->vaddr)[i] = 0;
@@ -1205,7 +1205,6 @@
 				aup->stats.rx_dropped++;
 				continue;
 			}
-			skb->dev = dev;
 			skb_reserve(skb, 2);	/* 16 byte IP header align */
 			eth_copy_and_sum(skb,
 				(unsigned char *)pDB->vaddr, frmlen, 0);
diff --git a/drivers/net/b44.c b/drivers/net/b44.c
index d742bfe..879a2ff 100644
--- a/drivers/net/b44.c
+++ b/drivers/net/b44.c
@@ -825,12 +825,11 @@
 			if (copy_skb == NULL)
 				goto drop_it_no_recycle;
 
-			copy_skb->dev = bp->dev;
 			skb_reserve(copy_skb, 2);
 			skb_put(copy_skb, len);
 			/* DMA sync done above, copy just the actual packet */
-			memcpy(copy_skb->data, skb->data+bp->rx_offset, len);
-
+			skb_copy_from_linear_data_offset(skb, bp->rx_offset,
+							 copy_skb->data, len);
 			skb = copy_skb;
 		}
 		skb->ip_summed = CHECKSUM_NONE;
@@ -1007,7 +1006,8 @@
 			goto err_out;
 		}
 
-		memcpy(skb_put(bounce_skb, len), skb->data, skb->len);
+		skb_copy_from_linear_data(skb, skb_put(bounce_skb, len),
+					  skb->len);
 		dev_kfree_skb_any(skb);
 		skb = bounce_skb;
 	}
diff --git a/drivers/net/bmac.c b/drivers/net/bmac.c
index c143304d..4612725 100644
--- a/drivers/net/bmac.c
+++ b/drivers/net/bmac.c
@@ -715,7 +715,6 @@
 		if (skb != NULL) {
 			nb -= ETHERCRC;
 			skb_put(skb, nb);
-			skb->dev = dev;
 			skb->protocol = eth_type_trans(skb, dev);
 			netif_rx(skb);
 			dev->last_rx = jiffies;
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c
index e85f5ec..f98a220 100644
--- a/drivers/net/bnx2.c
+++ b/drivers/net/bnx2.c
@@ -1884,10 +1884,8 @@
 				goto reuse_rx;
 
 			/* aligned copy */
-			memcpy(new_skb->data,
-				skb->data + bp->rx_offset - 2,
-				len + 2);
-
+			skb_copy_from_linear_data_offset(skb, bp->rx_offset - 2,
+				      new_skb->data, len + 2);
 			skb_reserve(new_skb, 2);
 			skb_put(new_skb, len);
 
@@ -4513,6 +4511,7 @@
 	if ((mss = skb_shinfo(skb)->gso_size) &&
 		(skb->len > (bp->dev->mtu + ETH_HLEN))) {
 		u32 tcp_opt_len, ip_tcp_len;
+		struct iphdr *iph;
 
 		if (skb_header_cloned(skb) &&
 		    pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
@@ -4520,25 +4519,23 @@
 			return NETDEV_TX_OK;
 		}
 
-		tcp_opt_len = ((skb->h.th->doff - 5) * 4);
 		vlan_tag_flags |= TX_BD_FLAGS_SW_LSO;
 
 		tcp_opt_len = 0;
-		if (skb->h.th->doff > 5) {
-			tcp_opt_len = (skb->h.th->doff - 5) << 2;
-		}
-		ip_tcp_len = (skb->nh.iph->ihl << 2) + sizeof(struct tcphdr);
+		if (tcp_hdr(skb)->doff > 5)
+			tcp_opt_len = tcp_optlen(skb);
 
-		skb->nh.iph->check = 0;
-		skb->nh.iph->tot_len = htons(mss + ip_tcp_len + tcp_opt_len);
-		skb->h.th->check =
-			~csum_tcpudp_magic(skb->nh.iph->saddr,
-					    skb->nh.iph->daddr,
-					    0, IPPROTO_TCP, 0);
+		ip_tcp_len = ip_hdrlen(skb) + sizeof(struct tcphdr);
 
-		if (tcp_opt_len || (skb->nh.iph->ihl > 5)) {
-			vlan_tag_flags |= ((skb->nh.iph->ihl - 5) +
-				(tcp_opt_len >> 2)) << 8;
+		iph = ip_hdr(skb);
+		iph->check = 0;
+		iph->tot_len = htons(mss + ip_tcp_len + tcp_opt_len);
+		tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
+							 iph->daddr, 0,
+							 IPPROTO_TCP, 0);
+		if (tcp_opt_len || (iph->ihl > 5)) {
+			vlan_tag_flags |= ((iph->ihl - 5) +
+					   (tcp_opt_len >> 2)) << 8;
 		}
 	}
 	else
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index 3fb354d..7e03f41 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -884,8 +884,8 @@
 	}
 
 	skb->dev = slave->dev;
-	skb->mac.raw = skb->data;
-	skb->nh.raw = skb->data + ETH_HLEN;
+	skb_reset_mac_header(skb);
+	skb->network_header = skb->mac_header + ETH_HLEN;
 	skb->protocol = PKT_TYPE_LACPDU;
 	skb->priority = TC_PRIO_CONTROL;
 
@@ -928,8 +928,8 @@
 	skb_reserve(skb, 16);
 
 	skb->dev = slave->dev;
-	skb->mac.raw = skb->data;
-	skb->nh.raw = skb->data + ETH_HLEN;
+	skb_reset_mac_header(skb);
+	skb->network_header = skb->mac_header + ETH_HLEN;
 	skb->protocol = PKT_TYPE_LACPDU;
 
 	marker_header = (struct marker_header *)skb_put(skb, length);
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index 217a2ee..92c3b6f 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -104,10 +104,15 @@
 };
 #pragma pack()
 
+static inline struct arp_pkt *arp_pkt(const struct sk_buff *skb)
+{
+	return (struct arp_pkt *)skb_network_header(skb);
+}
+
 /* Forward declaration */
 static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[]);
 
-static inline u8 _simple_hash(u8 *hash_start, int hash_size)
+static inline u8 _simple_hash(const u8 *hash_start, int hash_size)
 {
 	int i;
 	u8 hash = 0;
@@ -613,7 +618,7 @@
 static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bond)
 {
 	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
-	struct arp_pkt *arp = (struct arp_pkt *)skb->nh.raw;
+	struct arp_pkt *arp = arp_pkt(skb);
 	struct slave *assigned_slave;
 	struct rlb_client_info *client_info;
 	u32 hash_index = 0;
@@ -701,7 +706,7 @@
  */
 static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond)
 {
-	struct arp_pkt *arp = (struct arp_pkt *)skb->nh.raw;
+	struct arp_pkt *arp = arp_pkt(skb);
 	struct slave *tx_slave = NULL;
 
 	if (arp->op_code == __constant_htons(ARPOP_REPLY)) {
@@ -890,8 +895,8 @@
 		data = skb_put(skb, size);
 		memcpy(data, &pkt, size);
 
-		skb->mac.raw = data;
-		skb->nh.raw = data + ETH_HLEN;
+		skb_reset_mac_header(skb);
+		skb->network_header = skb->mac_header + ETH_HLEN;
 		skb->protocol = pkt.type;
 		skb->priority = TC_PRIO_CONTROL;
 		skb->dev = slave->dev;
@@ -1263,10 +1268,10 @@
 	int hash_size = 0;
 	int do_tx_balance = 1;
 	u32 hash_index = 0;
-	u8 *hash_start = NULL;
+	const u8 *hash_start = NULL;
 	int res = 1;
 
-	skb->mac.raw = (unsigned char *)skb->data;
+	skb_reset_mac_header(skb);
 	eth_data = eth_hdr(skb);
 
 	/* make sure that the curr_active_slave and the slaves list do
@@ -1280,15 +1285,18 @@
 	}
 
 	switch (ntohs(skb->protocol)) {
-	case ETH_P_IP:
+	case ETH_P_IP: {
+		const struct iphdr *iph = ip_hdr(skb);
+
 		if ((memcmp(eth_data->h_dest, mac_bcast, ETH_ALEN) == 0) ||
-		    (skb->nh.iph->daddr == ip_bcast) ||
-		    (skb->nh.iph->protocol == IPPROTO_IGMP)) {
+		    (iph->daddr == ip_bcast) ||
+		    (iph->protocol == IPPROTO_IGMP)) {
 			do_tx_balance = 0;
 			break;
 		}
-		hash_start = (char*)&(skb->nh.iph->daddr);
-		hash_size = sizeof(skb->nh.iph->daddr);
+		hash_start = (char *)&(iph->daddr);
+		hash_size = sizeof(iph->daddr);
+	}
 		break;
 	case ETH_P_IPV6:
 		if (memcmp(eth_data->h_dest, mac_bcast, ETH_ALEN) == 0) {
@@ -1296,8 +1304,8 @@
 			break;
 		}
 
-		hash_start = (char*)&(skb->nh.ipv6h->daddr);
-		hash_size = sizeof(skb->nh.ipv6h->daddr);
+		hash_start = (char *)&(ipv6_hdr(skb)->daddr);
+		hash_size = sizeof(ipv6_hdr(skb)->daddr);
 		break;
 	case ETH_P_IPX:
 		if (ipx_hdr(skb)->ipx_checksum !=
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index e4724d8..724bce5 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1360,13 +1360,6 @@
 		goto err_undo_flags;
 	}
 
-	if (slave_dev->get_stats == NULL) {
-		printk(KERN_NOTICE DRV_NAME
-			": %s: the driver for slave device %s does not provide "
-			"get_stats function, network statistics will be "
-			"inaccurate.\n", bond_dev->name, slave_dev->name);
-	}
-
 	new_slave = kzalloc(sizeof(struct slave), GFP_KERNEL);
 	if (!new_slave) {
 		res = -ENOMEM;
@@ -2524,7 +2517,7 @@
 				 (2 * sizeof(u32)))))
 		goto out_unlock;
 
-	arp = skb->nh.arph;
+	arp = arp_hdr(skb);
 	if (arp->ar_hln != dev->addr_len ||
 	    skb->pkt_type == PACKET_OTHERHOST ||
 	    skb->pkt_type == PACKET_LOOPBACK ||
@@ -3476,7 +3469,7 @@
 				    struct net_device *bond_dev, int count)
 {
 	struct ethhdr *data = (struct ethhdr *)skb->data;
-	struct iphdr *iph = skb->nh.iph;
+	struct iphdr *iph = ip_hdr(skb);
 	u16 *layer4hdr = (u16 *)((u32 *)iph + iph->ihl);
 	int layer4_xor = 0;
 
@@ -3640,35 +3633,32 @@
 	read_lock_bh(&bond->lock);
 
 	bond_for_each_slave(bond, slave, i) {
-		if (slave->dev->get_stats) {
-			sstats = slave->dev->get_stats(slave->dev);
+		sstats = slave->dev->get_stats(slave->dev);
+		stats->rx_packets += sstats->rx_packets;
+		stats->rx_bytes += sstats->rx_bytes;
+		stats->rx_errors += sstats->rx_errors;
+		stats->rx_dropped += sstats->rx_dropped;
 
-			stats->rx_packets += sstats->rx_packets;
-			stats->rx_bytes += sstats->rx_bytes;
-			stats->rx_errors += sstats->rx_errors;
-			stats->rx_dropped += sstats->rx_dropped;
+		stats->tx_packets += sstats->tx_packets;
+		stats->tx_bytes += sstats->tx_bytes;
+		stats->tx_errors += sstats->tx_errors;
+		stats->tx_dropped += sstats->tx_dropped;
 
-			stats->tx_packets += sstats->tx_packets;
-			stats->tx_bytes += sstats->tx_bytes;
-			stats->tx_errors += sstats->tx_errors;
-			stats->tx_dropped += sstats->tx_dropped;
+		stats->multicast += sstats->multicast;
+		stats->collisions += sstats->collisions;
 
-			stats->multicast += sstats->multicast;
-			stats->collisions += sstats->collisions;
+		stats->rx_length_errors += sstats->rx_length_errors;
+		stats->rx_over_errors += sstats->rx_over_errors;
+		stats->rx_crc_errors += sstats->rx_crc_errors;
+		stats->rx_frame_errors += sstats->rx_frame_errors;
+		stats->rx_fifo_errors += sstats->rx_fifo_errors;
+		stats->rx_missed_errors += sstats->rx_missed_errors;
 
-			stats->rx_length_errors += sstats->rx_length_errors;
-			stats->rx_over_errors += sstats->rx_over_errors;
-			stats->rx_crc_errors += sstats->rx_crc_errors;
-			stats->rx_frame_errors += sstats->rx_frame_errors;
-			stats->rx_fifo_errors += sstats->rx_fifo_errors;
-			stats->rx_missed_errors += sstats->rx_missed_errors;
-
-			stats->tx_aborted_errors += sstats->tx_aborted_errors;
-			stats->tx_carrier_errors += sstats->tx_carrier_errors;
-			stats->tx_fifo_errors += sstats->tx_fifo_errors;
-			stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors;
-			stats->tx_window_errors += sstats->tx_window_errors;
-		}
+		stats->tx_aborted_errors += sstats->tx_aborted_errors;
+		stats->tx_carrier_errors += sstats->tx_carrier_errors;
+		stats->tx_fifo_errors += sstats->tx_fifo_errors;
+		stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors;
+		stats->tx_window_errors += sstats->tx_window_errors;
 	}
 
 	read_unlock_bh(&bond->lock);
diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c
index c812648..4aec747 100644
--- a/drivers/net/cassini.c
+++ b/drivers/net/cassini.c
@@ -1995,7 +1995,6 @@
 		return -1;
 
 	*skbref = skb;
-	skb->dev = cp->dev;
 	skb_reserve(skb, swivel);
 
 	p = skb->data;
@@ -2822,10 +2821,8 @@
 
 	ctrl = 0;
 	if (skb->ip_summed == CHECKSUM_PARTIAL) {
-		u64 csum_start_off, csum_stuff_off;
-
-		csum_start_off = (u64) (skb->h.raw - skb->data);
-		csum_stuff_off = csum_start_off + skb->csum_offset;
+		const u64 csum_start_off = skb_transport_offset(skb);
+		const u64 csum_stuff_off = csum_start_off + skb->csum_offset;
 
 		ctrl =  TX_DESC_CSUM_EN |
 			CAS_BASE(TX_DESC_CSUM_START, csum_start_off) |
@@ -2849,8 +2846,8 @@
 			      ctrl | TX_DESC_SOF, 0);
 		entry = TX_DESC_NEXT(ring, entry);
 
-		memcpy(tx_tiny_buf(cp, ring, entry), skb->data +
-		       len - tabort, tabort);
+		skb_copy_from_linear_data_offset(skb, len - tabort,
+			      tx_tiny_buf(cp, ring, entry), tabort);
 		mapping = tx_tiny_map(cp, ring, entry, tentry);
 		cas_write_txd(cp, ring, entry, mapping, tabort, ctrl,
 			      (nr_frags == 0));
diff --git a/drivers/net/chelsio/Makefile b/drivers/net/chelsio/Makefile
index 382d23f..743ad8b 100644
--- a/drivers/net/chelsio/Makefile
+++ b/drivers/net/chelsio/Makefile
@@ -4,8 +4,6 @@
 
 obj-$(CONFIG_CHELSIO_T1) += cxgb.o
 
-cxgb-$(CONFIG_CHELSIO_T1_1G) += ixf1010.o mac.o mv88e1xxx.o vsc7326.o vsc8244.o
+cxgb-$(CONFIG_CHELSIO_T1_1G) += mac.o mv88e1xxx.o vsc7326.o
 cxgb-objs := cxgb2.o espi.o tp.o pm3393.o sge.o subr.o \
 	     mv88x201x.o my3126.o $(cxgb-y)
-
-
diff --git a/drivers/net/chelsio/common.h b/drivers/net/chelsio/common.h
index 787f2f2..8ba702c 100644
--- a/drivers/net/chelsio/common.h
+++ b/drivers/net/chelsio/common.h
@@ -322,9 +322,9 @@
 	unsigned char           mdio_mdiinv;
 	unsigned char           mdio_mdc;
 	unsigned char           mdio_phybaseaddr;
-	struct gmac            *gmac;
-	struct gphy            *gphy;
-	struct mdio_ops        *mdio_ops;
+	const struct gmac      *gmac;
+	const struct gphy      *gphy;
+	const struct mdio_ops  *mdio_ops;
 	const char             *desc;
 };
 
diff --git a/drivers/net/chelsio/cphy.h b/drivers/net/chelsio/cphy.h
index cf91434..79d855e 100644
--- a/drivers/net/chelsio/cphy.h
+++ b/drivers/net/chelsio/cphy.h
@@ -100,7 +100,7 @@
 
 	u32 elmer_gpo;
 
-	struct cphy_ops *ops;                /* PHY operations */
+	const struct cphy_ops *ops;            /* PHY operations */
 	int (*mdio_read)(adapter_t *adapter, int phy_addr, int mmd_addr,
 			 int reg_addr, unsigned int *val);
 	int (*mdio_write)(adapter_t *adapter, int phy_addr, int mmd_addr,
@@ -136,7 +136,7 @@
 /* Convenience initializer */
 static inline void cphy_init(struct cphy *phy, adapter_t *adapter,
 			     int phy_addr, struct cphy_ops *phy_ops,
-			     struct mdio_ops *mdio_ops)
+			     const struct mdio_ops *mdio_ops)
 {
 	phy->adapter = adapter;
 	phy->addr    = phy_addr;
@@ -151,7 +151,7 @@
 struct gphy {
 	/* Construct a PHY instance with the given PHY address */
 	struct cphy *(*create)(adapter_t *adapter, int phy_addr,
-			       struct mdio_ops *mdio_ops);
+			       const struct mdio_ops *mdio_ops);
 
 	/*
 	 * Reset the PHY chip.  This resets the whole PHY chip, not individual
@@ -160,11 +160,9 @@
 	int (*reset)(adapter_t *adapter);
 };
 
-extern struct gphy t1_my3126_ops;
-extern struct gphy t1_mv88e1xxx_ops;
-extern struct gphy t1_vsc8244_ops;
-extern struct gphy t1_xpak_ops;
-extern struct gphy t1_mv88x201x_ops;
-extern struct gphy t1_dummy_phy_ops;
+extern const struct gphy t1_my3126_ops;
+extern const struct gphy t1_mv88e1xxx_ops;
+extern const struct gphy t1_vsc8244_ops;
+extern const struct gphy t1_mv88x201x_ops;
 
 #endif /* _CXGB_CPHY_H_ */
diff --git a/drivers/net/chelsio/gmac.h b/drivers/net/chelsio/gmac.h
index 006a2eb..d423374 100644
--- a/drivers/net/chelsio/gmac.h
+++ b/drivers/net/chelsio/gmac.h
@@ -126,7 +126,7 @@
 struct cmac {
 	struct cmac_statistics stats;
 	adapter_t *adapter;
-	struct cmac_ops *ops;
+	const struct cmac_ops *ops;
 	cmac_instance *instance;
 };
 
@@ -136,11 +136,7 @@
 	int (*reset)(adapter_t *);
 };
 
-extern struct gmac t1_pm3393_ops;
-extern struct gmac t1_chelsio_mac_ops;
-extern struct gmac t1_vsc7321_ops;
-extern struct gmac t1_vsc7326_ops;
-extern struct gmac t1_ixf1010_ops;
-extern struct gmac t1_dummy_mac_ops;
+extern const struct gmac t1_pm3393_ops;
+extern const struct gmac t1_vsc7326_ops;
 
 #endif /* _CXGB_GMAC_H_ */
diff --git a/drivers/net/chelsio/ixf1010.c b/drivers/net/chelsio/ixf1010.c
deleted file mode 100644
index 10b2a9a..0000000
--- a/drivers/net/chelsio/ixf1010.c
+++ /dev/null
@@ -1,505 +0,0 @@
-/* $Date: 2005/11/12 02:13:49 $ $RCSfile: ixf1010.c,v $ $Revision: 1.36 $ */
-#include "gmac.h"
-#include "elmer0.h"
-
-/* Update fast changing statistics every 15 seconds */
-#define STATS_TICK_SECS 15
-/* 30 minutes for full statistics update */
-#define MAJOR_UPDATE_TICKS (1800 / STATS_TICK_SECS)
-
-/*
- * The IXF1010 can handle frames up to 16383 bytes but it's optimized for
- * frames up to 9831 (0x2667) bytes, so we limit jumbo frame size to this.
- * This length includes ethernet header and FCS.
- */
-#define MAX_FRAME_SIZE 0x2667
-
-/* MAC registers */
-enum {
-	/* Per-port registers */
-	REG_MACADDR_LOW = 0,
-	REG_MACADDR_HIGH = 0x4,
-	REG_FDFC_TYPE = 0xC,
-	REG_FC_TX_TIMER_VALUE = 0x1c,
-	REG_IPG_RX_TIME1 = 0x28,
-	REG_IPG_RX_TIME2 = 0x2c,
-	REG_IPG_TX_TIME = 0x30,
-	REG_PAUSE_THRES = 0x38,
-	REG_MAX_FRAME_SIZE = 0x3c,
-	REG_RGMII_SPEED = 0x40,
-	REG_FC_ENABLE = 0x48,
-	REG_DISCARD_CTRL_FRAMES = 0x54,
-	REG_DIVERSE_CONFIG = 0x60,
-	REG_RX_FILTER = 0x64,
-	REG_MC_ADDR_LOW = 0x68,
-	REG_MC_ADDR_HIGH = 0x6c,
-
-	REG_RX_OCTETS_OK = 0x80,
-	REG_RX_OCTETS_BAD = 0x84,
-	REG_RX_UC_PKTS = 0x88,
-	REG_RX_MC_PKTS = 0x8c,
-	REG_RX_BC_PKTS = 0x90,
-	REG_RX_FCS_ERR = 0xb0,
-	REG_RX_TAGGED = 0xb4,
-	REG_RX_DATA_ERR = 0xb8,
-	REG_RX_ALIGN_ERR = 0xbc,
-	REG_RX_LONG_ERR = 0xc0,
-	REG_RX_JABBER_ERR = 0xc4,
-	REG_RX_PAUSE_FRAMES = 0xc8,
-	REG_RX_UNKNOWN_CTRL_FRAMES = 0xcc,
-	REG_RX_VERY_LONG_ERR = 0xd0,
-	REG_RX_RUNT_ERR = 0xd4,
-	REG_RX_SHORT_ERR = 0xd8,
-	REG_RX_SYMBOL_ERR = 0xe4,
-
-	REG_TX_OCTETS_OK = 0x100,
-	REG_TX_OCTETS_BAD = 0x104,
-	REG_TX_UC_PKTS = 0x108,
-	REG_TX_MC_PKTS = 0x10c,
-	REG_TX_BC_PKTS = 0x110,
-	REG_TX_EXCESSIVE_LEN_DROP = 0x14c,
-	REG_TX_UNDERRUN = 0x150,
-	REG_TX_TAGGED = 0x154,
-	REG_TX_PAUSE_FRAMES = 0x15C,
-
-	/* Global registers */
-	REG_PORT_ENABLE = 0x1400,
-
-	REG_JTAG_ID = 0x1430,
-
-	RX_FIFO_HIGH_WATERMARK_BASE = 0x1600,
-	RX_FIFO_LOW_WATERMARK_BASE = 0x1628,
-	RX_FIFO_FRAMES_REMOVED_BASE = 0x1650,
-
-	REG_RX_ERR_DROP = 0x167c,
-	REG_RX_FIFO_OVERFLOW_EVENT = 0x1680,
-
-	TX_FIFO_HIGH_WATERMARK_BASE = 0x1800,
-	TX_FIFO_LOW_WATERMARK_BASE = 0x1828,
-	TX_FIFO_XFER_THRES_BASE = 0x1850,
-
-	REG_TX_FIFO_OVERFLOW_EVENT = 0x1878,
-	REG_TX_FIFO_OOS_EVENT = 0x1884,
-
-	TX_FIFO_FRAMES_REMOVED_BASE = 0x1888,
-
-	REG_SPI_RX_BURST = 0x1c00,
-	REG_SPI_RX_TRAINING = 0x1c04,
-	REG_SPI_RX_CALENDAR = 0x1c08,
-	REG_SPI_TX_SYNC = 0x1c0c
-};
-
-enum {                     /* RMON registers */
-	REG_RxOctetsTotalOK = 0x80,
-	REG_RxOctetsBad = 0x84,
-	REG_RxUCPkts = 0x88,
-	REG_RxMCPkts = 0x8c,
-	REG_RxBCPkts = 0x90,
-	REG_RxJumboPkts = 0xac,
-	REG_RxFCSErrors = 0xb0,
-	REG_RxDataErrors = 0xb8,
-	REG_RxAlignErrors = 0xbc,
-	REG_RxLongErrors = 0xc0,
-	REG_RxJabberErrors = 0xc4,
-	REG_RxPauseMacControlCounter = 0xc8,
-	REG_RxVeryLongErrors = 0xd0,
-	REG_RxRuntErrors = 0xd4,
-	REG_RxShortErrors = 0xd8,
-	REG_RxSequenceErrors = 0xe0,
-	REG_RxSymbolErrors = 0xe4,
-
-	REG_TxOctetsTotalOK = 0x100,
-	REG_TxOctetsBad = 0x104,
-	REG_TxUCPkts = 0x108,
-	REG_TxMCPkts = 0x10c,
-	REG_TxBCPkts = 0x110,
-	REG_TxJumboPkts = 0x12C,
-	REG_TxTotalCollisions = 0x134,
-	REG_TxExcessiveLengthDrop = 0x14c,
-	REG_TxUnderrun = 0x150,
-	REG_TxCRCErrors = 0x158,
-	REG_TxPauseFrames = 0x15c
-};
-
-enum {
-	DIVERSE_CONFIG_PAD_ENABLE = 0x80,
-	DIVERSE_CONFIG_CRC_ADD = 0x40
-};
-
-#define MACREG_BASE            0
-#define MACREG(mac, mac_reg)   ((mac)->instance->mac_base + (mac_reg))
-
-struct _cmac_instance {
-	u32 mac_base;
-	u32 index;
-	u32 version;
-	u32 ticks;
-};
-
-static void disable_port(struct cmac *mac)
-{
-	u32 val;
-
-	t1_tpi_read(mac->adapter, REG_PORT_ENABLE, &val);
-	val &= ~(1 << mac->instance->index);
-	t1_tpi_write(mac->adapter, REG_PORT_ENABLE, val);
-}
-
-/*
- * Read the current values of the RMON counters and add them to the cumulative
- * port statistics.  The HW RMON counters are cleared by this operation.
- */
-static void port_stats_update(struct cmac *mac)
-{
-	static struct {
-		unsigned int reg;
-		unsigned int offset;
-	} hw_stats[] = {
-
-#define HW_STAT(name, stat_name) \
-	{ REG_##name, \
-	  (&((struct cmac_statistics *)NULL)->stat_name) - (u64 *)NULL }
-
-		/* Rx stats */
-		HW_STAT(RxOctetsTotalOK, RxOctetsOK),
-		HW_STAT(RxOctetsBad, RxOctetsBad),
-		HW_STAT(RxUCPkts, RxUnicastFramesOK),
-		HW_STAT(RxMCPkts, RxMulticastFramesOK),
-		HW_STAT(RxBCPkts, RxBroadcastFramesOK),
-		HW_STAT(RxJumboPkts, RxJumboFramesOK),
-		HW_STAT(RxFCSErrors, RxFCSErrors),
-		HW_STAT(RxAlignErrors, RxAlignErrors),
-		HW_STAT(RxLongErrors, RxFrameTooLongErrors),
-		HW_STAT(RxVeryLongErrors, RxFrameTooLongErrors),
-		HW_STAT(RxPauseMacControlCounter, RxPauseFrames),
-		HW_STAT(RxDataErrors, RxDataErrors),
-		HW_STAT(RxJabberErrors, RxJabberErrors),
-		HW_STAT(RxRuntErrors, RxRuntErrors),
-		HW_STAT(RxShortErrors, RxRuntErrors),
-		HW_STAT(RxSequenceErrors, RxSequenceErrors),
-		HW_STAT(RxSymbolErrors, RxSymbolErrors),
-
-		/* Tx stats (skip collision stats as we are full-duplex only) */
-		HW_STAT(TxOctetsTotalOK, TxOctetsOK),
-		HW_STAT(TxOctetsBad, TxOctetsBad),
-		HW_STAT(TxUCPkts, TxUnicastFramesOK),
-		HW_STAT(TxMCPkts, TxMulticastFramesOK),
-		HW_STAT(TxBCPkts, TxBroadcastFramesOK),
-		HW_STAT(TxJumboPkts, TxJumboFramesOK),
-		HW_STAT(TxPauseFrames, TxPauseFrames),
-		HW_STAT(TxExcessiveLengthDrop, TxLengthErrors),
-		HW_STAT(TxUnderrun, TxUnderrun),
-		HW_STAT(TxCRCErrors, TxFCSErrors)
-	}, *p = hw_stats;
-	u64 *stats = (u64 *) &mac->stats;
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(hw_stats); i++) {
-		u32 val;
-
-		t1_tpi_read(mac->adapter, MACREG(mac, p->reg), &val);
-		stats[p->offset] += val;
-	}
-}
-
-/* No-op interrupt operation as this MAC does not support interrupts */
-static int mac_intr_op(struct cmac *mac)
-{
-	return 0;
-}
-
-/* Expect MAC address to be in network byte order. */
-static int mac_set_address(struct cmac *mac, u8 addr[6])
-{
-	u32 addr_lo, addr_hi;
-
-	addr_lo = addr[2];
-	addr_lo = (addr_lo << 8) | addr[3];
-	addr_lo = (addr_lo << 8) | addr[4];
-	addr_lo = (addr_lo << 8) | addr[5];
-
-	addr_hi = addr[0];
-	addr_hi = (addr_hi << 8) | addr[1];
-
-	t1_tpi_write(mac->adapter, MACREG(mac, REG_MACADDR_LOW), addr_lo);
-	t1_tpi_write(mac->adapter, MACREG(mac, REG_MACADDR_HIGH), addr_hi);
-	return 0;
-}
-
-static int mac_get_address(struct cmac *mac, u8 addr[6])
-{
-	u32 addr_lo, addr_hi;
-
-	t1_tpi_read(mac->adapter, MACREG(mac, REG_MACADDR_LOW), &addr_lo);
-	t1_tpi_read(mac->adapter, MACREG(mac, REG_MACADDR_HIGH), &addr_hi);
-
-	addr[0] = (u8) (addr_hi >> 8);
-	addr[1] = (u8) addr_hi;
-	addr[2] = (u8) (addr_lo >> 24);
-	addr[3] = (u8) (addr_lo >> 16);
-	addr[4] = (u8) (addr_lo >> 8);
-	addr[5] = (u8) addr_lo;
-	return 0;
-}
-
-/* This is intended to reset a port, not the whole MAC */
-static int mac_reset(struct cmac *mac)
-{
-	return 0;
-}
-
-static int mac_set_rx_mode(struct cmac *mac, struct t1_rx_mode *rm)
-{
-	u32 val, new_mode;
-	adapter_t *adapter = mac->adapter;
-	u32 addr_lo, addr_hi;
-	u8 *addr;
-
-	t1_tpi_read(adapter, MACREG(mac, REG_RX_FILTER), &val);
-	new_mode = val & ~7;
-	if (!t1_rx_mode_promisc(rm) && mac->instance->version > 0)
-		new_mode |= 1;     /* only set if version > 0 due to erratum */
-	if (!t1_rx_mode_promisc(rm) && !t1_rx_mode_allmulti(rm)
-	     && t1_rx_mode_mc_cnt(rm) <= 1)
-		new_mode |= 2;
-	if (new_mode != val)
-		t1_tpi_write(adapter, MACREG(mac, REG_RX_FILTER), new_mode);
-	switch (t1_rx_mode_mc_cnt(rm)) {
-	case 0:
-		t1_tpi_write(adapter, MACREG(mac, REG_MC_ADDR_LOW), 0);
-		t1_tpi_write(adapter, MACREG(mac, REG_MC_ADDR_HIGH), 0);
-		break;
-	case 1:
-		addr = t1_get_next_mcaddr(rm);
-		addr_lo = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) |
-			addr[5];
-		addr_hi = (addr[0] << 8) | addr[1];
-		t1_tpi_write(adapter, MACREG(mac, REG_MC_ADDR_LOW), addr_lo);
-		t1_tpi_write(adapter, MACREG(mac, REG_MC_ADDR_HIGH), addr_hi);
-		break;
-	default:
-		break;
-	}
-	return 0;
-}
-
-static int mac_set_mtu(struct cmac *mac, int mtu)
-{
-	/* MAX_FRAME_SIZE inludes header + FCS, mtu doesn't */
-	if (mtu > (MAX_FRAME_SIZE - 14 - 4))
-		return -EINVAL;
-	t1_tpi_write(mac->adapter, MACREG(mac, REG_MAX_FRAME_SIZE),
-		     mtu + 14 + 4);
-	return 0;
-}
-
-static int mac_set_speed_duplex_fc(struct cmac *mac, int speed, int duplex,
-				   int fc)
-{
-	u32 val;
-
-	if (speed >= 0 && speed != SPEED_100 && speed != SPEED_1000)
-		return -1;
-	if (duplex >= 0 && duplex != DUPLEX_FULL)
-		return -1;
-
-	if (speed >= 0) {
-		val = speed == SPEED_100 ? 1 : 2;
-		t1_tpi_write(mac->adapter, MACREG(mac, REG_RGMII_SPEED), val);
-	}
-
-	t1_tpi_read(mac->adapter, MACREG(mac, REG_FC_ENABLE), &val);
-	val &= ~3;
-	if (fc & PAUSE_RX)
-		val |= 1;
-	if (fc & PAUSE_TX)
-		val |= 2;
-	t1_tpi_write(mac->adapter, MACREG(mac, REG_FC_ENABLE), val);
-	return 0;
-}
-
-static int mac_get_speed_duplex_fc(struct cmac *mac, int *speed, int *duplex,
-				   int *fc)
-{
-	u32 val;
-
-	if (duplex)
-		*duplex = DUPLEX_FULL;
-	if (speed) {
-		t1_tpi_read(mac->adapter, MACREG(mac, REG_RGMII_SPEED),
-			 &val);
-		*speed = (val & 2) ? SPEED_1000 : SPEED_100;
-	}
-	if (fc) {
-		t1_tpi_read(mac->adapter, MACREG(mac, REG_FC_ENABLE), &val);
-		*fc = 0;
-		if (val & 1)
-			*fc |= PAUSE_RX;
-		if (val & 2)
-			*fc |= PAUSE_TX;
-	}
-	return 0;
-}
-
-static void enable_port(struct cmac *mac)
-{
-	u32 val;
-	u32 index = mac->instance->index;
-	adapter_t *adapter = mac->adapter;
-
-	t1_tpi_read(adapter, MACREG(mac, REG_DIVERSE_CONFIG), &val);
-	val |= DIVERSE_CONFIG_CRC_ADD | DIVERSE_CONFIG_PAD_ENABLE;
-	t1_tpi_write(adapter, MACREG(mac, REG_DIVERSE_CONFIG), val);
-	if (mac->instance->version > 0)
-		t1_tpi_write(adapter, MACREG(mac, REG_RX_FILTER), 3);
-	else /* Don't enable unicast address filtering due to IXF1010 bug */
-		t1_tpi_write(adapter, MACREG(mac, REG_RX_FILTER), 2);
-
-	t1_tpi_read(adapter, REG_RX_ERR_DROP, &val);
-	val |= (1 << index);
-	t1_tpi_write(adapter, REG_RX_ERR_DROP, val);
-
-	/*
-	 * Clear the port RMON registers by adding their current values to the
-	 * cumulatice port stats and then clearing the stats.  Really.
-	 */
-	port_stats_update(mac);
-	memset(&mac->stats, 0, sizeof(struct cmac_statistics));
-	mac->instance->ticks = 0;
-
-	t1_tpi_read(adapter, REG_PORT_ENABLE, &val);
-	val |= (1 << index);
-	t1_tpi_write(adapter, REG_PORT_ENABLE, val);
-
-	index <<= 2;
-	if (is_T2(adapter)) {
-		/* T204: set the Fifo water level & threshold */
-		t1_tpi_write(adapter, RX_FIFO_HIGH_WATERMARK_BASE + index, 0x740);
-		t1_tpi_write(adapter, RX_FIFO_LOW_WATERMARK_BASE + index, 0x730);
-		t1_tpi_write(adapter, TX_FIFO_HIGH_WATERMARK_BASE + index, 0x600);
-		t1_tpi_write(adapter, TX_FIFO_LOW_WATERMARK_BASE + index, 0x1d0);
-		t1_tpi_write(adapter, TX_FIFO_XFER_THRES_BASE + index, 0x1100);
-	} else {
-	/*
-	 * Set the TX Fifo Threshold to 0x400 instead of 0x100 to work around
-	 * Underrun problem. Intel has blessed this solution.
-	 */
-		t1_tpi_write(adapter, TX_FIFO_XFER_THRES_BASE + index, 0x400);
-	}
-}
-
-/* IXF1010 ports do not have separate enables for TX and RX */
-static int mac_enable(struct cmac *mac, int which)
-{
-	if (which & (MAC_DIRECTION_RX | MAC_DIRECTION_TX))
-		enable_port(mac);
-	return 0;
-}
-
-static int mac_disable(struct cmac *mac, int which)
-{
-	if (which & (MAC_DIRECTION_RX | MAC_DIRECTION_TX))
-		disable_port(mac);
-	return 0;
-}
-
-#define RMON_UPDATE(mac, name, stat_name) \
-	t1_tpi_read((mac)->adapter, MACREG(mac, REG_##name), &val); \
-	(mac)->stats.stat_name += val;
-
-/*
- * This function is called periodically to accumulate the current values of the
- * RMON counters into the port statistics.  Since the counters are only 32 bits
- * some of them can overflow in less than a minute at GigE speeds, so this
- * function should be called every 30 seconds or so.
- *
- * To cut down on reading costs we update only the octet counters at each tick
- * and do a full update at major ticks, which can be every 30 minutes or more.
- */
-static const struct cmac_statistics *mac_update_statistics(struct cmac *mac,
-							   int flag)
-{
-	if (flag == MAC_STATS_UPDATE_FULL ||
-	    MAJOR_UPDATE_TICKS <= mac->instance->ticks) {
-		port_stats_update(mac);
-		mac->instance->ticks = 0;
-	} else {
-		u32 val;
-
-		RMON_UPDATE(mac, RxOctetsTotalOK, RxOctetsOK);
-		RMON_UPDATE(mac, TxOctetsTotalOK, TxOctetsOK);
-		mac->instance->ticks++;
-	}
-	return &mac->stats;
-}
-
-static void mac_destroy(struct cmac *mac)
-{
-	kfree(mac);
-}
-
-static struct cmac_ops ixf1010_ops = {
-	.destroy                  = mac_destroy,
-	.reset                    = mac_reset,
-	.interrupt_enable         = mac_intr_op,
-	.interrupt_disable        = mac_intr_op,
-	.interrupt_clear          = mac_intr_op,
-	.enable                   = mac_enable,
-	.disable                  = mac_disable,
-	.set_mtu                  = mac_set_mtu,
-	.set_rx_mode              = mac_set_rx_mode,
-	.set_speed_duplex_fc      = mac_set_speed_duplex_fc,
-	.get_speed_duplex_fc      = mac_get_speed_duplex_fc,
-	.statistics_update        = mac_update_statistics,
-	.macaddress_get           = mac_get_address,
-	.macaddress_set           = mac_set_address,
-};
-
-static int ixf1010_mac_reset(adapter_t *adapter)
-{
-	u32 val;
-
-	t1_tpi_read(adapter, A_ELMER0_GPO, &val);
-	if ((val & 1) != 0) {
-		val &= ~1;
-		t1_tpi_write(adapter, A_ELMER0_GPO, val);
-		udelay(2);
-	}
-	val |= 1;
-	t1_tpi_write(adapter, A_ELMER0_GPO, val);
-	udelay(2);
-
-	t1_tpi_write(adapter, REG_PORT_ENABLE, 0);
-	return 0;
-}
-
-static struct cmac *ixf1010_mac_create(adapter_t *adapter, int index)
-{
-	struct cmac *mac;
-	u32 val;
-
-	if (index > 9)
-		return NULL;
-
-	mac = kzalloc(sizeof(*mac) + sizeof(cmac_instance), GFP_KERNEL);
-	if (!mac)
-		return NULL;
-
-	mac->ops = &ixf1010_ops;
-	mac->instance = (cmac_instance *)(mac + 1);
-
-	mac->instance->mac_base = MACREG_BASE + (index * 0x200);
-	mac->instance->index    = index;
-	mac->adapter  = adapter;
-	mac->instance->ticks    = 0;
-
-	t1_tpi_read(adapter, REG_JTAG_ID, &val);
-	mac->instance->version = val >> 28;
-	return mac;
-}
-
-struct gmac t1_ixf1010_ops = {
-	STATS_TICK_SECS,
-	ixf1010_mac_create,
-	ixf1010_mac_reset
-};
diff --git a/drivers/net/chelsio/mac.c b/drivers/net/chelsio/mac.c
index 6af39dc..1d97282 100644
--- a/drivers/net/chelsio/mac.c
+++ b/drivers/net/chelsio/mac.c
@@ -363,6 +363,6 @@
 	return mac;
 }
 
-struct gmac t1_chelsio_mac_ops = {
+const struct gmac t1_chelsio_mac_ops = {
 	.create = mac_create
 };
diff --git a/drivers/net/chelsio/mv88e1xxx.c b/drivers/net/chelsio/mv88e1xxx.c
index 5867e3b..0632be0 100644
--- a/drivers/net/chelsio/mv88e1xxx.c
+++ b/drivers/net/chelsio/mv88e1xxx.c
@@ -354,7 +354,7 @@
 };
 
 static struct cphy *mv88e1xxx_phy_create(adapter_t *adapter, int phy_addr,
-					 struct mdio_ops *mdio_ops)
+					 const struct mdio_ops *mdio_ops)
 {
 	struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL);
 
@@ -390,7 +390,7 @@
 	return 0;
 }
 
-struct gphy t1_mv88e1xxx_ops = {
-	mv88e1xxx_phy_create,
-	mv88e1xxx_phy_reset
+const struct gphy t1_mv88e1xxx_ops = {
+	.create = mv88e1xxx_phy_create,
+	.reset =  mv88e1xxx_phy_reset
 };
diff --git a/drivers/net/chelsio/mv88x201x.c b/drivers/net/chelsio/mv88x201x.c
index c8e8948..cd85604 100644
--- a/drivers/net/chelsio/mv88x201x.c
+++ b/drivers/net/chelsio/mv88x201x.c
@@ -208,7 +208,7 @@
 };
 
 static struct cphy *mv88x201x_phy_create(adapter_t *adapter, int phy_addr,
-					 struct mdio_ops *mdio_ops)
+					 const struct mdio_ops *mdio_ops)
 {
 	u32 val;
 	struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL);
@@ -252,7 +252,7 @@
 	return 0;
 }
 
-struct gphy t1_mv88x201x_ops = {
-	mv88x201x_phy_create,
-	mv88x201x_phy_reset
+const struct gphy t1_mv88x201x_ops = {
+	.create = mv88x201x_phy_create,
+	.reset = mv88x201x_phy_reset
 };
diff --git a/drivers/net/chelsio/my3126.c b/drivers/net/chelsio/my3126.c
index 87dde3e..040acd2 100644
--- a/drivers/net/chelsio/my3126.c
+++ b/drivers/net/chelsio/my3126.c
@@ -166,7 +166,7 @@
 };
 
 static struct cphy *my3126_phy_create(adapter_t *adapter,
-			int phy_addr, struct mdio_ops *mdio_ops)
+			int phy_addr, const struct mdio_ops *mdio_ops)
 {
 	struct cphy *cphy = kzalloc(sizeof (*cphy), GFP_KERNEL);
 
@@ -201,7 +201,7 @@
 	return 0;
 }
 
-struct gphy t1_my3126_ops = {
-	my3126_phy_create,
-	my3126_phy_reset
+const struct gphy t1_my3126_ops = {
+	.create = my3126_phy_create,
+	.reset = my3126_phy_reset
 };
diff --git a/drivers/net/chelsio/pm3393.c b/drivers/net/chelsio/pm3393.c
index 69129ed..678778a 100644
--- a/drivers/net/chelsio/pm3393.c
+++ b/drivers/net/chelsio/pm3393.c
@@ -807,8 +807,8 @@
 	return successful_reset ? 0 : 1;
 }
 
-struct gmac t1_pm3393_ops = {
-	STATS_TICK_SECS,
-	pm3393_mac_create,
-	pm3393_mac_reset
+const struct gmac t1_pm3393_ops = {
+	.stats_update_period = STATS_TICK_SECS,
+	.create              = pm3393_mac_create,
+	.reset               = pm3393_mac_reset,
 };
diff --git a/drivers/net/chelsio/sge.c b/drivers/net/chelsio/sge.c
index 326d4a6..e4f874a 100644
--- a/drivers/net/chelsio/sge.c
+++ b/drivers/net/chelsio/sge.c
@@ -1062,7 +1062,7 @@
 					    pci_unmap_addr(ce, dma_addr),
 					    pci_unmap_len(ce, dma_len),
 					    PCI_DMA_FROMDEVICE);
-		memcpy(skb->data, ce->skb->data, len);
+		skb_copy_from_linear_data(ce->skb, skb->data, len);
 		pci_dma_sync_single_for_device(pdev,
 					       pci_unmap_addr(ce, dma_addr),
 					       pci_unmap_len(ce, dma_len),
@@ -1379,12 +1379,11 @@
 	}
 	__skb_pull(skb, sizeof(*p));
 
-	skb->dev = adapter->port[p->iff].dev;
 	skb->dev->last_rx = jiffies;
 	st = per_cpu_ptr(sge->port_stats[p->iff], smp_processor_id());
 	st->rx_packets++;
 
-	skb->protocol = eth_type_trans(skb, skb->dev);
+	skb->protocol = eth_type_trans(skb, adapter->port[p->iff].dev);
 	if ((adapter->flags & RX_CSUM_ENABLED) && p->csum == 0xffff &&
 	    skb->protocol == htons(ETH_P_IP) &&
 	    (skb->data[9] == IPPROTO_TCP || skb->data[9] == IPPROTO_UDP)) {
@@ -1866,14 +1865,14 @@
 
 		++st->tx_tso;
 
-		eth_type = skb->nh.raw - skb->data == ETH_HLEN ?
+		eth_type = skb_network_offset(skb) == ETH_HLEN ?
 			CPL_ETH_II : CPL_ETH_II_VLAN;
 
 		hdr = (struct cpl_tx_pkt_lso *)skb_push(skb, sizeof(*hdr));
 		hdr->opcode = CPL_TX_PKT_LSO;
 		hdr->ip_csum_dis = hdr->l4_csum_dis = 0;
-		hdr->ip_hdr_words = skb->nh.iph->ihl;
-		hdr->tcp_hdr_words = skb->h.th->doff;
+		hdr->ip_hdr_words = ip_hdr(skb)->ihl;
+		hdr->tcp_hdr_words = tcp_hdr(skb)->doff;
 		hdr->eth_type_mss = htons(MK_ETH_TYPE_MSS(eth_type,
 							  skb_shinfo(skb)->gso_size));
 		hdr->len = htonl(skb->len - sizeof(*hdr));
@@ -1913,7 +1912,7 @@
 
 		if (!(adapter->flags & UDP_CSUM_CAPABLE) &&
 		    skb->ip_summed == CHECKSUM_PARTIAL &&
-		    skb->nh.iph->protocol == IPPROTO_UDP) {
+		    ip_hdr(skb)->protocol == IPPROTO_UDP) {
 			if (unlikely(skb_checksum_help(skb))) {
 				pr_debug("%s: unable to do udp checksum\n", dev->name);
 				dev_kfree_skb_any(skb);
@@ -1926,7 +1925,7 @@
 		 */
 		if ((unlikely(!adapter->sge->espibug_skb[dev->if_port]))) {
 			if (skb->protocol == htons(ETH_P_ARP) &&
-			    skb->nh.arph->ar_op == htons(ARPOP_REQUEST)) {
+			    arp_hdr(skb)->ar_op == htons(ARPOP_REQUEST)) {
 				adapter->sge->espibug_skb[dev->if_port] = skb;
 				/* We want to re-use this skb later. We
 				 * simply bump the reference count and it
@@ -2096,10 +2095,14 @@
 					0x0, 0x7, 0x43, 0x0, 0x0, 0x0
 				};
 
-				memcpy(skb->data + sizeof(struct cpl_tx_pkt),
-					ch_mac_addr, ETH_ALEN);
-				memcpy(skb->data + skb->len - 10,
-					ch_mac_addr, ETH_ALEN);
+				skb_copy_to_linear_data_offset(skb,
+						    sizeof(struct cpl_tx_pkt),
+							       ch_mac_addr,
+							       ETH_ALEN);
+				skb_copy_to_linear_data_offset(skb,
+							       skb->len - 10,
+							       ch_mac_addr,
+							       ETH_ALEN);
 				skb->cb[0] = 0xff;
 			}
 
@@ -2126,10 +2129,14 @@
 	                if (!skb->cb[0]) {
 	                        u8 ch_mac_addr[ETH_ALEN] =
 	                            {0x0, 0x7, 0x43, 0x0, 0x0, 0x0};
-	                        memcpy(skb->data + sizeof(struct cpl_tx_pkt),
-	                               ch_mac_addr, ETH_ALEN);
-	                        memcpy(skb->data + skb->len - 10, ch_mac_addr,
-	                               ETH_ALEN);
+	                        skb_copy_to_linear_data_offset(skb,
+						     sizeof(struct cpl_tx_pkt),
+							       ch_mac_addr,
+							       ETH_ALEN);
+	                        skb_copy_to_linear_data_offset(skb,
+							       skb->len - 10,
+							       ch_mac_addr,
+							       ETH_ALEN);
 	                        skb->cb[0] = 0xff;
 	                }
 
diff --git a/drivers/net/chelsio/subr.c b/drivers/net/chelsio/subr.c
index c2522cd..7de9a61 100644
--- a/drivers/net/chelsio/subr.c
+++ b/drivers/net/chelsio/subr.c
@@ -321,10 +321,10 @@
 }
 
 #if defined(CONFIG_CHELSIO_T1_1G) || defined(CONFIG_CHELSIO_T1_COUGAR)
-static struct mdio_ops mi1_mdio_ops = {
-	mi1_mdio_init,
-	mi1_mdio_read,
-	mi1_mdio_write
+static const struct mdio_ops mi1_mdio_ops = {
+	.init = mi1_mdio_init,
+	.read = mi1_mdio_read,
+	.write = mi1_mdio_write
 };
 #endif
 
@@ -377,10 +377,10 @@
 	return 0;
 }
 
-static struct mdio_ops mi1_mdio_ext_ops = {
-	mi1_mdio_init,
-	mi1_mdio_ext_read,
-	mi1_mdio_ext_write
+static const struct mdio_ops mi1_mdio_ext_ops = {
+	.init = mi1_mdio_init,
+	.read = mi1_mdio_ext_read,
+	.write = mi1_mdio_ext_write
 };
 
 enum {
@@ -392,63 +392,136 @@
 	CH_BRD_N204_4CU,
 };
 
-static struct board_info t1_board[] = {
+static const struct board_info t1_board[] = {
+	{
+		.board		= CHBT_BOARD_CHT110,
+		.port_number	= 1,
+		.caps		= SUPPORTED_10000baseT_Full,
+		.chip_term	= CHBT_TERM_T1,
+		.chip_mac	= CHBT_MAC_PM3393,
+		.chip_phy	= CHBT_PHY_MY3126,
+		.clock_core	= 125000000,
+		.clock_mc3	= 150000000,
+		.clock_mc4	= 125000000,
+		.espi_nports	= 1,
+		.clock_elmer0	= 44,
+		.mdio_mdien	= 1,
+		.mdio_mdiinv	= 1,
+		.mdio_mdc	= 1,
+		.mdio_phybaseaddr = 1,
+		.gmac		= &t1_pm3393_ops,
+		.gphy		= &t1_my3126_ops,
+		.mdio_ops	= &mi1_mdio_ext_ops,
+		.desc		= "Chelsio T110 1x10GBase-CX4 TOE",
+	},
 
-{ CHBT_BOARD_CHT110, 1/*ports#*/,
-  SUPPORTED_10000baseT_Full /*caps*/, CHBT_TERM_T1,
-  CHBT_MAC_PM3393, CHBT_PHY_MY3126,
-  125000000/*clk-core*/, 150000000/*clk-mc3*/, 125000000/*clk-mc4*/,
-  1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 1/*mdien*/,
-  1/*mdiinv*/, 1/*mdc*/, 1/*phybaseaddr*/, &t1_pm3393_ops,
-  &t1_my3126_ops, &mi1_mdio_ext_ops,
-  "Chelsio T110 1x10GBase-CX4 TOE" },
+	{
+		.board		= CHBT_BOARD_N110,
+		.port_number	= 1,
+		.caps		= SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE,
+		.chip_term	= CHBT_TERM_T1,
+		.chip_mac	= CHBT_MAC_PM3393,
+		.chip_phy	= CHBT_PHY_88X2010,
+		.clock_core	= 125000000,
+		.espi_nports	= 1,
+		.clock_elmer0	= 44,
+		.mdio_mdien	= 0,
+		.mdio_mdiinv	= 0,
+		.mdio_mdc	= 1,
+		.mdio_phybaseaddr = 0,
+		.gmac		= &t1_pm3393_ops,
+		.gphy		= &t1_mv88x201x_ops,
+		.mdio_ops	= &mi1_mdio_ext_ops,
+		.desc		= "Chelsio N110 1x10GBaseX NIC",
+	},
 
-{ CHBT_BOARD_N110, 1/*ports#*/,
-  SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE /*caps*/, CHBT_TERM_T1,
-  CHBT_MAC_PM3393, CHBT_PHY_88X2010,
-  125000000/*clk-core*/, 0/*clk-mc3*/, 0/*clk-mc4*/,
-  1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/,
-  0/*mdiinv*/, 1/*mdc*/, 0/*phybaseaddr*/, &t1_pm3393_ops,
-  &t1_mv88x201x_ops, &mi1_mdio_ext_ops,
-  "Chelsio N110 1x10GBaseX NIC" },
+	{
+		.board		= CHBT_BOARD_N210,
+		.port_number	= 1,
+		.caps		= SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE,
+		.chip_term	= CHBT_TERM_T2,
+		.chip_mac	= CHBT_MAC_PM3393,
+		.chip_phy	= CHBT_PHY_88X2010,
+		.clock_core	= 125000000,
+		.espi_nports	= 1,
+		.clock_elmer0	= 44,
+		.mdio_mdien	= 0,
+		.mdio_mdiinv	= 0,
+		.mdio_mdc	= 1,
+		.mdio_phybaseaddr = 0,
+		.gmac		= &t1_pm3393_ops,
+		.gphy		= &t1_mv88x201x_ops,
+		.mdio_ops	= &mi1_mdio_ext_ops,
+		.desc		= "Chelsio N210 1x10GBaseX NIC",
+	},
 
-{ CHBT_BOARD_N210, 1/*ports#*/,
-  SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE /*caps*/, CHBT_TERM_T2,
-  CHBT_MAC_PM3393, CHBT_PHY_88X2010,
-  125000000/*clk-core*/, 0/*clk-mc3*/, 0/*clk-mc4*/,
-  1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/,
-  0/*mdiinv*/, 1/*mdc*/, 0/*phybaseaddr*/, &t1_pm3393_ops,
-  &t1_mv88x201x_ops, &mi1_mdio_ext_ops,
-  "Chelsio N210 1x10GBaseX NIC" },
+	{
+		.board		= CHBT_BOARD_CHT210,
+		.port_number	= 1,
+		.caps		= SUPPORTED_10000baseT_Full,
+		.chip_term	= CHBT_TERM_T2,
+		.chip_mac	= CHBT_MAC_PM3393,
+		.chip_phy	= CHBT_PHY_88X2010,
+		.clock_core	= 125000000,
+		.clock_mc3	= 133000000,
+		.clock_mc4	= 125000000,
+		.espi_nports	= 1,
+		.clock_elmer0	= 44,
+		.mdio_mdien	= 0,
+		.mdio_mdiinv	= 0,
+		.mdio_mdc	= 1,
+		.mdio_phybaseaddr = 0,
+		.gmac		= &t1_pm3393_ops,
+		.gphy		= &t1_mv88x201x_ops,
+		.mdio_ops	= &mi1_mdio_ext_ops,
+		.desc		= "Chelsio T210 1x10GBaseX TOE",
+	},
 
-{ CHBT_BOARD_CHT210, 1/*ports#*/,
-  SUPPORTED_10000baseT_Full /*caps*/, CHBT_TERM_T2,
-  CHBT_MAC_PM3393, CHBT_PHY_88X2010,
-  125000000/*clk-core*/, 133000000/*clk-mc3*/, 125000000/*clk-mc4*/,
-  1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/,
-  0/*mdiinv*/, 1/*mdc*/, 0/*phybaseaddr*/, &t1_pm3393_ops,
-  &t1_mv88x201x_ops, &mi1_mdio_ext_ops,
-  "Chelsio T210 1x10GBaseX TOE" },
-
-{ CHBT_BOARD_CHT210, 1/*ports#*/,
-  SUPPORTED_10000baseT_Full /*caps*/, CHBT_TERM_T2,
-  CHBT_MAC_PM3393, CHBT_PHY_MY3126,
-  125000000/*clk-core*/, 133000000/*clk-mc3*/, 125000000/*clk-mc4*/,
-  1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 1/*mdien*/,
-  1/*mdiinv*/, 1/*mdc*/, 1/*phybaseaddr*/, &t1_pm3393_ops,
-  &t1_my3126_ops, &mi1_mdio_ext_ops,
-  "Chelsio T210 1x10GBase-CX4 TOE" },
+	{
+		.board		= CHBT_BOARD_CHT210,
+		.port_number	= 1,
+		.caps		= SUPPORTED_10000baseT_Full,
+		.chip_term	= CHBT_TERM_T2,
+		.chip_mac	= CHBT_MAC_PM3393,
+		.chip_phy	= CHBT_PHY_MY3126,
+		.clock_core	= 125000000,
+		.clock_mc3	= 133000000,
+		.clock_mc4	= 125000000,
+		.espi_nports	= 1,
+		.clock_elmer0	= 44,
+		.mdio_mdien	= 1,
+		.mdio_mdiinv	= 1,
+		.mdio_mdc	= 1,
+		.mdio_phybaseaddr = 1,
+		.gmac		= &t1_pm3393_ops,
+		.gphy		= &t1_my3126_ops,
+		.mdio_ops	= &mi1_mdio_ext_ops,
+		.desc		= "Chelsio T210 1x10GBase-CX4 TOE",
+	},
 
 #ifdef CONFIG_CHELSIO_T1_1G
-{ CHBT_BOARD_CHN204, 4/*ports#*/,
-  SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half |
-  SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
-  SUPPORTED_PAUSE | SUPPORTED_TP /*caps*/, CHBT_TERM_T2, CHBT_MAC_VSC7321, CHBT_PHY_88E1111,
-  100000000/*clk-core*/, 0/*clk-mc3*/, 0/*clk-mc4*/,
-  4/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/,
-  0/*mdiinv*/, 1/*mdc*/, 4/*phybaseaddr*/, &t1_vsc7326_ops,
-  &t1_mv88e1xxx_ops, &mi1_mdio_ops,
-  "Chelsio N204 4x100/1000BaseT NIC" },
+	{
+		.board		= CHBT_BOARD_CHN204,
+		.port_number	= 4,
+		.caps		= SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full
+				| SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full
+				| SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
+				  SUPPORTED_PAUSE | SUPPORTED_TP,
+		.chip_term	= CHBT_TERM_T2,
+		.chip_mac	= CHBT_MAC_VSC7321,
+		.chip_phy	= CHBT_PHY_88E1111,
+		.clock_core	= 100000000,
+		.espi_nports	= 4,
+		.clock_elmer0	= 44,
+		.mdio_mdien	= 0,
+		.mdio_mdiinv	= 0,
+		.mdio_mdc	= 0,
+		.mdio_phybaseaddr = 4,
+		.gmac		= &t1_vsc7326_ops,
+		.gphy		= &t1_mv88e1xxx_ops,
+		.mdio_ops	= &mi1_mdio_ops,
+		.desc		= "Chelsio N204 4x100/1000BaseT NIC",
+	},
 #endif
 
 };
diff --git a/drivers/net/chelsio/vsc7326.c b/drivers/net/chelsio/vsc7326.c
index 534ffa0..99b51f6 100644
--- a/drivers/net/chelsio/vsc7326.c
+++ b/drivers/net/chelsio/vsc7326.c
@@ -723,7 +723,7 @@
 	return 0;
 }
 
-struct gmac t1_vsc7326_ops = {
+const struct gmac t1_vsc7326_ops = {
 	.stats_update_period = STATS_TICK_SECS,
 	.create              = vsc7326_mac_create,
 	.reset               = vsc7326_mac_reset,
diff --git a/drivers/net/chelsio/vsc8244.c b/drivers/net/chelsio/vsc8244.c
deleted file mode 100644
index 251d485..0000000
--- a/drivers/net/chelsio/vsc8244.c
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- * This file is part of the Chelsio T2 Ethernet driver.
- *
- * Copyright (C) 2005 Chelsio Communications.  All rights reserved.
- *
- * 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 LICENSE file included in this
- * release for licensing terms and conditions.
- */
-
-#include "common.h"
-#include "cphy.h"
-#include "elmer0.h"
-
-#ifndef ADVERTISE_PAUSE_CAP
-# define ADVERTISE_PAUSE_CAP 0x400
-#endif
-#ifndef ADVERTISE_PAUSE_ASYM
-# define ADVERTISE_PAUSE_ASYM 0x800
-#endif
-
-/* Gigabit MII registers */
-#ifndef MII_CTRL1000
-# define MII_CTRL1000 9
-#endif
-
-#ifndef ADVERTISE_1000FULL
-# define ADVERTISE_1000FULL 0x200
-# define ADVERTISE_1000HALF 0x100
-#endif
-
-/* VSC8244 PHY specific registers. */
-enum {
-	VSC8244_INTR_ENABLE   = 25,
-	VSC8244_INTR_STATUS   = 26,
-	VSC8244_AUX_CTRL_STAT = 28,
-};
-
-enum {
-	VSC_INTR_RX_ERR     = 1 << 0,
-	VSC_INTR_MS_ERR     = 1 << 1,  /* master/slave resolution error */
-	VSC_INTR_CABLE      = 1 << 2,  /* cable impairment */
-	VSC_INTR_FALSE_CARR = 1 << 3,  /* false carrier */
-	VSC_INTR_MEDIA_CHG  = 1 << 4,  /* AMS media change */
-	VSC_INTR_RX_FIFO    = 1 << 5,  /* Rx FIFO over/underflow */
-	VSC_INTR_TX_FIFO    = 1 << 6,  /* Tx FIFO over/underflow */
-	VSC_INTR_DESCRAMBL  = 1 << 7,  /* descrambler lock-lost */
-	VSC_INTR_SYMBOL_ERR = 1 << 8,  /* symbol error */
-	VSC_INTR_NEG_DONE   = 1 << 10, /* autoneg done */
-	VSC_INTR_NEG_ERR    = 1 << 11, /* autoneg error */
-	VSC_INTR_LINK_CHG   = 1 << 13, /* link change */
-	VSC_INTR_ENABLE     = 1 << 15, /* interrupt enable */
-};
-
-#define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \
-			   VSC_INTR_NEG_DONE)
-#define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \
-		   VSC_INTR_ENABLE)
-
-/* PHY specific auxiliary control & status register fields */
-#define S_ACSR_ACTIPHY_TMR    0
-#define M_ACSR_ACTIPHY_TMR    0x3
-#define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR)
-
-#define S_ACSR_SPEED    3
-#define M_ACSR_SPEED    0x3
-#define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED)
-
-#define S_ACSR_DUPLEX 5
-#define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX)
-
-#define S_ACSR_ACTIPHY 6
-#define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY)
-
-/*
- * Reset the PHY.  This PHY completes reset immediately so we never wait.
- */
-static int vsc8244_reset(struct cphy *cphy, int wait)
-{
-	int err;
-	unsigned int ctl;
-
-	err = simple_mdio_read(cphy, MII_BMCR, &ctl);
-	if (err)
-		return err;
-
-	ctl &= ~BMCR_PDOWN;
-	ctl |= BMCR_RESET;
-	return simple_mdio_write(cphy, MII_BMCR, ctl);
-}
-
-static int vsc8244_intr_enable(struct cphy *cphy)
-{
-	simple_mdio_write(cphy, VSC8244_INTR_ENABLE, INTR_MASK);
-
-	/* Enable interrupts through Elmer */
-	if (t1_is_asic(cphy->adapter)) {
-		u32 elmer;
-
-		t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
-		elmer |= ELMER0_GP_BIT1;
-		if (is_T2(cphy->adapter))
-		    elmer |= ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4;
-		t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
-	}
-
-	return 0;
-}
-
-static int vsc8244_intr_disable(struct cphy *cphy)
-{
-	simple_mdio_write(cphy, VSC8244_INTR_ENABLE, 0);
-
-	if (t1_is_asic(cphy->adapter)) {
-		u32 elmer;
-
-		t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
-		elmer &= ~ELMER0_GP_BIT1;
-		if (is_T2(cphy->adapter))
-		    elmer &= ~(ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4);
-		t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
-	}
-
-	return 0;
-}
-
-static int vsc8244_intr_clear(struct cphy *cphy)
-{
-	u32 val;
-	u32 elmer;
-
-	/* Clear PHY interrupts by reading the register. */
-	simple_mdio_read(cphy, VSC8244_INTR_ENABLE, &val);
-
-	if (t1_is_asic(cphy->adapter)) {
-		t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer);
-		elmer |= ELMER0_GP_BIT1;
-		if (is_T2(cphy->adapter))
-		    elmer |= ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4;
-		t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer);
-	}
-
-	return 0;
-}
-
-/*
- * Force the PHY speed and duplex.  This also disables auto-negotiation, except
- * for 1Gb/s, where auto-negotiation is mandatory.
- */
-static int vsc8244_set_speed_duplex(struct cphy *phy, int speed, int duplex)
-{
-	int err;
-	unsigned int ctl;
-
-	err = simple_mdio_read(phy, MII_BMCR, &ctl);
-	if (err)
-		return err;
-
-	if (speed >= 0) {
-		ctl &= ~(BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE);
-		if (speed == SPEED_100)
-			ctl |= BMCR_SPEED100;
-		else if (speed == SPEED_1000)
-			ctl |= BMCR_SPEED1000;
-	}
-	if (duplex >= 0) {
-		ctl &= ~(BMCR_FULLDPLX | BMCR_ANENABLE);
-		if (duplex == DUPLEX_FULL)
-			ctl |= BMCR_FULLDPLX;
-	}
-	if (ctl & BMCR_SPEED1000)  /* auto-negotiation required for 1Gb/s */
-		ctl |= BMCR_ANENABLE;
-	return simple_mdio_write(phy, MII_BMCR, ctl);
-}
-
-int t1_mdio_set_bits(struct cphy *phy, int mmd, int reg, unsigned int bits)
-{
-	int ret;
-	unsigned int val;
-
-	ret = mdio_read(phy, mmd, reg, &val);
-	if (!ret)
-		ret = mdio_write(phy, mmd, reg, val | bits);
-	return ret;
-}
-
-static int vsc8244_autoneg_enable(struct cphy *cphy)
-{
-	return t1_mdio_set_bits(cphy, 0, MII_BMCR,
-				BMCR_ANENABLE | BMCR_ANRESTART);
-}
-
-static int vsc8244_autoneg_restart(struct cphy *cphy)
-{
-	return t1_mdio_set_bits(cphy, 0, MII_BMCR, BMCR_ANRESTART);
-}
-
-static int vsc8244_advertise(struct cphy *phy, unsigned int advertise_map)
-{
-	int err;
-	unsigned int val = 0;
-
-	err = simple_mdio_read(phy, MII_CTRL1000, &val);
-	if (err)
-		return err;
-
-	val &= ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
-	if (advertise_map & ADVERTISED_1000baseT_Half)
-		val |= ADVERTISE_1000HALF;
-	if (advertise_map & ADVERTISED_1000baseT_Full)
-		val |= ADVERTISE_1000FULL;
-
-	err = simple_mdio_write(phy, MII_CTRL1000, val);
-	if (err)
-		return err;
-
-	val = 1;
-	if (advertise_map & ADVERTISED_10baseT_Half)
-		val |= ADVERTISE_10HALF;
-	if (advertise_map & ADVERTISED_10baseT_Full)
-		val |= ADVERTISE_10FULL;
-	if (advertise_map & ADVERTISED_100baseT_Half)
-		val |= ADVERTISE_100HALF;
-	if (advertise_map & ADVERTISED_100baseT_Full)
-		val |= ADVERTISE_100FULL;
-	if (advertise_map & ADVERTISED_PAUSE)
-		val |= ADVERTISE_PAUSE_CAP;
-	if (advertise_map & ADVERTISED_ASYM_PAUSE)
-		val |= ADVERTISE_PAUSE_ASYM;
-	return simple_mdio_write(phy, MII_ADVERTISE, val);
-}
-
-static int vsc8244_get_link_status(struct cphy *cphy, int *link_ok,
-				   int *speed, int *duplex, int *fc)
-{
-	unsigned int bmcr, status, lpa, adv;
-	int err, sp = -1, dplx = -1, pause = 0;
-
-	err = simple_mdio_read(cphy, MII_BMCR, &bmcr);
-	if (!err)
-		err = simple_mdio_read(cphy, MII_BMSR, &status);
-	if (err)
-		return err;
-
-	if (link_ok) {
-		/*
-		 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
-		 * once more to get the current link state.
-		 */
-		if (!(status & BMSR_LSTATUS))
-			err = simple_mdio_read(cphy, MII_BMSR, &status);
-		if (err)
-			return err;
-		*link_ok = (status & BMSR_LSTATUS) != 0;
-	}
-	if (!(bmcr & BMCR_ANENABLE)) {
-		dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
-		if (bmcr & BMCR_SPEED1000)
-			sp = SPEED_1000;
-		else if (bmcr & BMCR_SPEED100)
-			sp = SPEED_100;
-		else
-			sp = SPEED_10;
-	} else if (status & BMSR_ANEGCOMPLETE) {
-		err = simple_mdio_read(cphy, VSC8244_AUX_CTRL_STAT, &status);
-		if (err)
-			return err;
-
-		dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
-		sp = G_ACSR_SPEED(status);
-		if (sp == 0)
-			sp = SPEED_10;
-		else if (sp == 1)
-			sp = SPEED_100;
-		else
-			sp = SPEED_1000;
-
-		if (fc && dplx == DUPLEX_FULL) {
-			err = simple_mdio_read(cphy, MII_LPA, &lpa);
-			if (!err)
-				err = simple_mdio_read(cphy, MII_ADVERTISE,
-						       &adv);
-			if (err)
-				return err;
-
-			if (lpa & adv & ADVERTISE_PAUSE_CAP)
-				pause = PAUSE_RX | PAUSE_TX;
-			else if ((lpa & ADVERTISE_PAUSE_CAP) &&
-				 (lpa & ADVERTISE_PAUSE_ASYM) &&
-				 (adv & ADVERTISE_PAUSE_ASYM))
-				pause = PAUSE_TX;
-			else if ((lpa & ADVERTISE_PAUSE_ASYM) &&
-				 (adv & ADVERTISE_PAUSE_CAP))
-				pause = PAUSE_RX;
-		}
-	}
-	if (speed)
-		*speed = sp;
-	if (duplex)
-		*duplex = dplx;
-	if (fc)
-		*fc = pause;
-	return 0;
-}
-
-static int vsc8244_intr_handler(struct cphy *cphy)
-{
-	unsigned int cause;
-	int err, cphy_cause = 0;
-
-	err = simple_mdio_read(cphy, VSC8244_INTR_STATUS, &cause);
-	if (err)
-		return err;
-
-	cause &= INTR_MASK;
-	if (cause & CFG_CHG_INTR_MASK)
-		cphy_cause |= cphy_cause_link_change;
-	if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO))
-		cphy_cause |= cphy_cause_fifo_error;
-	return cphy_cause;
-}
-
-static void vsc8244_destroy(struct cphy *cphy)
-{
-	kfree(cphy);
-}
-
-static struct cphy_ops vsc8244_ops = {
-	.destroy              = vsc8244_destroy,
-	.reset                = vsc8244_reset,
-	.interrupt_enable     = vsc8244_intr_enable,
-	.interrupt_disable    = vsc8244_intr_disable,
-	.interrupt_clear      = vsc8244_intr_clear,
-	.interrupt_handler    = vsc8244_intr_handler,
-	.autoneg_enable       = vsc8244_autoneg_enable,
-	.autoneg_restart      = vsc8244_autoneg_restart,
-	.advertise            = vsc8244_advertise,
-	.set_speed_duplex     = vsc8244_set_speed_duplex,
-	.get_link_status      = vsc8244_get_link_status
-};
-
-static struct cphy* vsc8244_phy_create(adapter_t *adapter, int phy_addr,
-				       struct mdio_ops *mdio_ops)
-{
-	struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL);
-
-	if (!cphy)
-		return NULL;
-
-	cphy_init(cphy, adapter, phy_addr, &vsc8244_ops, mdio_ops);
-
-	return cphy;
-}
-
-
-static int vsc8244_phy_reset(adapter_t* adapter)
-{
-	return 0;
-}
-
-struct gphy t1_vsc8244_ops = {
-	vsc8244_phy_create,
-	vsc8244_phy_reset
-};
-
-
diff --git a/drivers/net/chelsio/vsc8244_reg.h b/drivers/net/chelsio/vsc8244_reg.h
deleted file mode 100644
index d3c1829..0000000
--- a/drivers/net/chelsio/vsc8244_reg.h
+++ /dev/null
@@ -1,172 +0,0 @@
-/* $Date: 2005/11/23 16:28:53 $ $RCSfile: vsc8244_reg.h,v $ $Revision: 1.1 $ */
-#ifndef CHELSIO_MV8E1XXX_H
-#define CHELSIO_MV8E1XXX_H
-
-#ifndef BMCR_SPEED1000
-# define BMCR_SPEED1000 0x40
-#endif
-
-#ifndef ADVERTISE_PAUSE
-# define ADVERTISE_PAUSE 0x400
-#endif
-#ifndef ADVERTISE_PAUSE_ASYM
-# define ADVERTISE_PAUSE_ASYM 0x800
-#endif
-
-/* Gigabit MII registers */
-#define MII_GBMR 1       /* 1000Base-T mode register */
-#define MII_GBCR 9       /* 1000Base-T control register */
-#define MII_GBSR 10      /* 1000Base-T status register */
-
-/* 1000Base-T control register fields */
-#define GBCR_ADV_1000HALF         0x100
-#define GBCR_ADV_1000FULL         0x200
-#define GBCR_PREFER_MASTER        0x400
-#define GBCR_MANUAL_AS_MASTER     0x800
-#define GBCR_MANUAL_CONFIG_ENABLE 0x1000
-
-/* 1000Base-T status register fields */
-#define GBSR_LP_1000HALF  0x400
-#define GBSR_LP_1000FULL  0x800
-#define GBSR_REMOTE_OK    0x1000
-#define GBSR_LOCAL_OK     0x2000
-#define GBSR_LOCAL_MASTER 0x4000
-#define GBSR_MASTER_FAULT 0x8000
-
-/* Vitesse PHY interrupt status bits. */
-#if 0
-#define VSC8244_INTR_JABBER          0x0001
-#define VSC8244_INTR_POLARITY_CHNG   0x0002
-#define VSC8244_INTR_ENG_DETECT_CHNG 0x0010
-#define VSC8244_INTR_DOWNSHIFT       0x0020
-#define VSC8244_INTR_MDI_XOVER_CHNG  0x0040
-#define VSC8244_INTR_FIFO_OVER_UNDER 0x0080
-#define VSC8244_INTR_FALSE_CARRIER   0x0100
-#define VSC8244_INTR_SYMBOL_ERROR    0x0200
-#define VSC8244_INTR_LINK_CHNG       0x0400
-#define VSC8244_INTR_AUTONEG_DONE    0x0800
-#define VSC8244_INTR_PAGE_RECV       0x1000
-#define VSC8244_INTR_DUPLEX_CHNG     0x2000
-#define VSC8244_INTR_SPEED_CHNG      0x4000
-#define VSC8244_INTR_AUTONEG_ERR     0x8000
-#else
-//#define VSC8244_INTR_JABBER          0x0001
-//#define VSC8244_INTR_POLARITY_CHNG   0x0002
-//#define VSC8244_INTR_BIT2            0x0004
-//#define VSC8244_INTR_BIT3            0x0008
-#define VSC8244_INTR_RX_ERR          0x0001
-#define VSC8244_INTR_MASTER_SLAVE    0x0002
-#define VSC8244_INTR_CABLE_IMPAIRED  0x0004
-#define VSC8244_INTR_FALSE_CARRIER   0x0008
-//#define VSC8244_INTR_ENG_DETECT_CHNG 0x0010
-//#define VSC8244_INTR_DOWNSHIFT       0x0020
-//#define VSC8244_INTR_MDI_XOVER_CHNG  0x0040
-//#define VSC8244_INTR_FIFO_OVER_UNDER 0x0080
-#define VSC8244_INTR_BIT4            0x0010
-#define VSC8244_INTR_FIFO_RX         0x0020
-#define VSC8244_INTR_FIFO_OVER_UNDER 0x0040
-#define VSC8244_INTR_LOCK_LOST       0x0080
-//#define VSC8244_INTR_FALSE_CARRIER   0x0100
-//#define VSC8244_INTR_SYMBOL_ERROR    0x0200
-//#define VSC8244_INTR_LINK_CHNG       0x0400
-//#define VSC8244_INTR_AUTONEG_DONE    0x0800
-#define VSC8244_INTR_SYMBOL_ERROR    0x0100
-#define VSC8244_INTR_ENG_DETECT_CHNG 0x0200
-#define VSC8244_INTR_AUTONEG_DONE    0x0400
-#define VSC8244_INTR_AUTONEG_ERR     0x0800
-//#define VSC8244_INTR_PAGE_RECV       0x1000
-//#define VSC8244_INTR_DUPLEX_CHNG     0x2000
-//#define VSC8244_INTR_SPEED_CHNG      0x4000
-//#define VSC8244_INTR_AUTONEG_ERR     0x8000
-#define VSC8244_INTR_DUPLEX_CHNG     0x1000
-#define VSC8244_INTR_LINK_CHNG       0x2000
-#define VSC8244_INTR_SPEED_CHNG      0x4000
-#define VSC8244_INTR_STATUS          0x8000
-#endif
-
-
-/* Vitesse PHY specific registers. */
-#define VSC8244_SPECIFIC_CNTRL_REGISTER               16
-#define VSC8244_SPECIFIC_STATUS_REGISTER              0x1c
-#define VSC8244_INTERRUPT_ENABLE_REGISTER             0x19
-#define VSC8244_INTERRUPT_STATUS_REGISTER             0x1a
-#define VSC8244_EXT_PHY_SPECIFIC_CNTRL_REGISTER       20
-#define VSC8244_RECV_ERR_CNTR_REGISTER                21
-#define VSC8244_RES_REGISTER                          22
-#define VSC8244_GLOBAL_STATUS_REGISTER                23
-#define VSC8244_LED_CONTROL_REGISTER                  24
-#define VSC8244_MANUAL_LED_OVERRIDE_REGISTER          25
-#define VSC8244_EXT_PHY_SPECIFIC_CNTRL_2_REGISTER     26
-#define VSC8244_EXT_PHY_SPECIFIC_STATUS_REGISTER      27
-#define VSC8244_VIRTUAL_CABLE_TESTER_REGISTER         28
-#define VSC8244_EXTENDED_ADDR_REGISTER                29
-#define VSC8244_EXTENDED_REGISTER                     30
-
-/* PHY specific control register fields */
-#define S_PSCR_MDI_XOVER_MODE    5
-#define M_PSCR_MDI_XOVER_MODE    0x3
-#define V_PSCR_MDI_XOVER_MODE(x) ((x) << S_PSCR_MDI_XOVER_MODE)
-#define G_PSCR_MDI_XOVER_MODE(x) (((x) >> S_PSCR_MDI_XOVER_MODE) & M_PSCR_MDI_XOVER_MODE)
-
-/* Extended PHY specific control register fields */
-#define S_DOWNSHIFT_ENABLE 8
-#define V_DOWNSHIFT_ENABLE (1 << S_DOWNSHIFT_ENABLE)
-
-#define S_DOWNSHIFT_CNT    9
-#define M_DOWNSHIFT_CNT    0x7
-#define V_DOWNSHIFT_CNT(x) ((x) << S_DOWNSHIFT_CNT)
-#define G_DOWNSHIFT_CNT(x) (((x) >> S_DOWNSHIFT_CNT) & M_DOWNSHIFT_CNT)
-
-/* PHY specific status register fields */
-#define S_PSSR_JABBER 0
-#define V_PSSR_JABBER (1 << S_PSSR_JABBER)
-
-#define S_PSSR_POLARITY 1
-#define V_PSSR_POLARITY (1 << S_PSSR_POLARITY)
-
-#define S_PSSR_RX_PAUSE 2
-#define V_PSSR_RX_PAUSE (1 << S_PSSR_RX_PAUSE)
-
-#define S_PSSR_TX_PAUSE 3
-#define V_PSSR_TX_PAUSE (1 << S_PSSR_TX_PAUSE)
-
-#define S_PSSR_ENERGY_DETECT 4
-#define V_PSSR_ENERGY_DETECT (1 << S_PSSR_ENERGY_DETECT)
-
-#define S_PSSR_DOWNSHIFT_STATUS 5
-#define V_PSSR_DOWNSHIFT_STATUS (1 << S_PSSR_DOWNSHIFT_STATUS)
-
-#define S_PSSR_MDI 6
-#define V_PSSR_MDI (1 << S_PSSR_MDI)
-
-#define S_PSSR_CABLE_LEN    7
-#define M_PSSR_CABLE_LEN    0x7
-#define V_PSSR_CABLE_LEN(x) ((x) << S_PSSR_CABLE_LEN)
-#define G_PSSR_CABLE_LEN(x) (((x) >> S_PSSR_CABLE_LEN) & M_PSSR_CABLE_LEN)
-
-//#define S_PSSR_LINK 10
-//#define S_PSSR_LINK 13
-#define S_PSSR_LINK 2
-#define V_PSSR_LINK (1 << S_PSSR_LINK)
-
-//#define S_PSSR_STATUS_RESOLVED 11
-//#define S_PSSR_STATUS_RESOLVED 10
-#define S_PSSR_STATUS_RESOLVED 15
-#define V_PSSR_STATUS_RESOLVED (1 << S_PSSR_STATUS_RESOLVED)
-
-#define S_PSSR_PAGE_RECEIVED 12
-#define V_PSSR_PAGE_RECEIVED (1 << S_PSSR_PAGE_RECEIVED)
-
-//#define S_PSSR_DUPLEX 13
-//#define S_PSSR_DUPLEX 12
-#define S_PSSR_DUPLEX 5
-#define V_PSSR_DUPLEX (1 << S_PSSR_DUPLEX)
-
-//#define S_PSSR_SPEED    14
-//#define S_PSSR_SPEED    14
-#define S_PSSR_SPEED    3
-#define M_PSSR_SPEED    0x3
-#define V_PSSR_SPEED(x) ((x) << S_PSSR_SPEED)
-#define G_PSSR_SPEED(x) (((x) >> S_PSSR_SPEED) & M_PSSR_SPEED)
-
-#endif
diff --git a/drivers/net/cris/eth_v10.c b/drivers/net/cris/eth_v10.c
index 8eb5712..5bdf5ca 100644
--- a/drivers/net/cris/eth_v10.c
+++ b/drivers/net/cris/eth_v10.c
@@ -1348,7 +1348,8 @@
 
 #ifdef ETHDEBUG
 		printk("head = 0x%x, data = 0x%x, tail = 0x%x, end = 0x%x\n",
-		  skb->head, skb->data, skb->tail, skb->end);
+		       skb->head, skb->data, skb_tail_pointer(skb),
+		       skb_end_pointer(skb));
 		printk("copying packet to 0x%x.\n", skb_data_ptr);
 #endif
 
@@ -1375,7 +1376,6 @@
 		myNextRxDesc->descr.buf = L1_CACHE_ALIGN(virt_to_phys(myNextRxDesc->skb->data));
 	}
 
-	skb->dev = dev;
 	skb->protocol = eth_type_trans(skb, dev);
 
 	/* Send the packet to the upper layers */
diff --git a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c
index 4612f71..9774bb1 100644
--- a/drivers/net/cs89x0.c
+++ b/drivers/net/cs89x0.c
@@ -1004,7 +1004,6 @@
 		return;
 	}
 	skb_reserve(skb, 2);	/* longword align L3 header */
-	skb->dev = dev;
 
 	if (bp + length > lp->end_dma_buff) {
 		int semi_cnt = lp->end_dma_buff - bp;
@@ -1702,7 +1701,6 @@
 		return;
 	}
 	skb_reserve(skb, 2);	/* longword align L3 header */
-	skb->dev = dev;
 
 	readwords(ioaddr, RX_FRAME_PORT, skb_put(skb, length), length >> 1);
 	if (length & 1)
diff --git a/drivers/net/cxgb3/cxgb3_offload.c b/drivers/net/cxgb3/cxgb3_offload.c
index 199e506..ebcf35e 100644
--- a/drivers/net/cxgb3/cxgb3_offload.c
+++ b/drivers/net/cxgb3/cxgb3_offload.c
@@ -783,7 +783,7 @@
 	skb->protocol = htons(0xffff);
 	skb->dev = dev->lldev;
 	skb_pull(skb, sizeof(*p));
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	netif_receive_skb(skb);
 	return 0;
 }
diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/cxgb3/sge.c
index 027ab2c..3666586 100644
--- a/drivers/net/cxgb3/sge.c
+++ b/drivers/net/cxgb3/sge.c
@@ -661,7 +661,7 @@
 
 	if (skb) {
 		__skb_put(skb, IMMED_PKT_SIZE);
-		memcpy(skb->data, resp->imm_data, IMMED_PKT_SIZE);
+		skb_copy_to_linear_data(skb, resp->imm_data, IMMED_PKT_SIZE);
 	}
 	return skb;
 }
@@ -897,11 +897,11 @@
 		d->flit[2] = 0;
 		cntrl |= V_TXPKT_OPCODE(CPL_TX_PKT_LSO);
 		hdr->cntrl = htonl(cntrl);
-		eth_type = skb->nh.raw - skb->data == ETH_HLEN ?
+		eth_type = skb_network_offset(skb) == ETH_HLEN ?
 		    CPL_ETH_II : CPL_ETH_II_VLAN;
 		tso_info |= V_LSO_ETH_TYPE(eth_type) |
-		    V_LSO_IPHDR_WORDS(skb->nh.iph->ihl) |
-		    V_LSO_TCPHDR_WORDS(skb->h.th->doff);
+		    V_LSO_IPHDR_WORDS(ip_hdr(skb)->ihl) |
+		    V_LSO_TCPHDR_WORDS(tcp_hdr(skb)->doff);
 		hdr->lso_info = htonl(tso_info);
 		flits = 3;
 	} else {
@@ -913,7 +913,8 @@
 		if (skb->len <= WR_LEN - sizeof(*cpl)) {
 			q->sdesc[pidx].skb = NULL;
 			if (!skb->data_len)
-				memcpy(&d->flit[2], skb->data, skb->len);
+				skb_copy_from_linear_data(skb, &d->flit[2],
+							  skb->len);
 			else
 				skb_copy_bits(skb, 0, &d->flit[2], skb->len);
 
@@ -1319,16 +1320,19 @@
 	/* Only TX_DATA builds SGLs */
 
 	from = (struct work_request_hdr *)skb->data;
-	memcpy(&d->flit[1], &from[1], skb->h.raw - skb->data - sizeof(*from));
+	memcpy(&d->flit[1], &from[1],
+	       skb_transport_offset(skb) - sizeof(*from));
 
-	flits = (skb->h.raw - skb->data) / 8;
+	flits = skb_transport_offset(skb) / 8;
 	sgp = ndesc == 1 ? (struct sg_ent *)&d->flit[flits] : sgl;
-	sgl_flits = make_sgl(skb, sgp, skb->h.raw, skb->tail - skb->h.raw,
+	sgl_flits = make_sgl(skb, sgp, skb_transport_header(skb),
+			     skb->tail - skb->transport_header,
 			     adap->pdev);
 	if (need_skb_unmap()) {
 		setup_deferred_unmapping(skb, adap->pdev, sgp, sgl_flits);
 		skb->destructor = deferred_unmap_destructor;
-		((struct unmap_info *)skb->cb)->len = skb->tail - skb->h.raw;
+		((struct unmap_info *)skb->cb)->len = (skb->tail -
+						       skb->transport_header);
 	}
 
 	write_wr_hdr_sgl(ndesc, skb, d, pidx, q, sgl, flits, sgl_flits,
@@ -1349,8 +1353,8 @@
 	if (skb->len <= WR_LEN && cnt == 0)
 		return 1;	/* packet fits as immediate data */
 
-	flits = (skb->h.raw - skb->data) / 8;	/* headers */
-	if (skb->tail != skb->h.raw)
+	flits = skb_transport_offset(skb) / 8;	/* headers */
+	if (skb->tail != skb->transport_header)
 		cnt++;
 	return flits_to_desc(flits + sgl_len(cnt));
 }
@@ -1620,7 +1624,9 @@
 			     unsigned int gather_idx)
 {
 	rq->offload_pkts++;
-	skb->mac.raw = skb->nh.raw = skb->h.raw = skb->data;
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
+	skb_reset_transport_header(skb);
 
 	if (rq->polling) {
 		rx_gather[gather_idx++] = skb;
@@ -1684,9 +1690,8 @@
 	struct port_info *pi;
 
 	skb_pull(skb, sizeof(*p) + pad);
-	skb->dev = adap->port[p->iff];
 	skb->dev->last_rx = jiffies;
-	skb->protocol = eth_type_trans(skb, skb->dev);
+	skb->protocol = eth_type_trans(skb, adap->port[p->iff]);
 	pi = netdev_priv(skb->dev);
 	if (pi->rx_csum_offload && p->csum_valid && p->csum == 0xffff &&
 	    !p->fragment) {
@@ -1717,11 +1722,11 @@
 {
 	skb->len = len;
 	if (len <= SKB_DATA_SIZE) {
-		memcpy(skb->data, p->va, len);
+		skb_copy_to_linear_data(skb, p->va, len);
 		skb->tail += len;
 		put_page(p->frag.page);
 	} else {
-		memcpy(skb->data, p->va, SKB_DATA_SIZE);
+		skb_copy_to_linear_data(skb, p->va, SKB_DATA_SIZE);
 		skb_shinfo(skb)->frags[0].page = p->frag.page;
 		skb_shinfo(skb)->frags[0].page_offset =
 		    p->frag.page_offset + SKB_DATA_SIZE;
@@ -1767,7 +1772,7 @@
 			__skb_put(skb, len);
 			pci_dma_sync_single_for_cpu(adap->pdev, mapping, len,
 						    PCI_DMA_FROMDEVICE);
-			memcpy(skb->data, sd->t.skb->data, len);
+			skb_copy_from_linear_data(sd->t.skb, skb->data, len);
 			pci_dma_sync_single_for_device(adap->pdev, mapping, len,
 						       PCI_DMA_FROMDEVICE);
 		} else if (!drop_thres)
diff --git a/drivers/net/de600.c b/drivers/net/de600.c
index e547ce1..dae97b8 100644
--- a/drivers/net/de600.c
+++ b/drivers/net/de600.c
@@ -359,7 +359,6 @@
 	}
 	/* else */
 
-	skb->dev = dev;
 	skb_reserve(skb,2);	/* Align */
 
 	/* 'skb->data' points to the start of sk_buff data area. */
diff --git a/drivers/net/de620.c b/drivers/net/de620.c
index b6ad0cb..dc48924 100644
--- a/drivers/net/de620.c
+++ b/drivers/net/de620.c
@@ -697,7 +697,6 @@
 		}
 		else { /* Yep! Go get it! */
 			skb_reserve(skb,2);	/* Align */
-			skb->dev = dev;
 			/* skb->data points to the start of sk_buff data area */
 			buffer = skb_put(skb,size);
 			/* copy the packet into the buffer */
diff --git a/drivers/net/declance.c b/drivers/net/declance.c
index 9f7e1db..95d854e 100644
--- a/drivers/net/declance.c
+++ b/drivers/net/declance.c
@@ -616,7 +616,6 @@
 			}
 			lp->stats.rx_bytes += len;
 
-			skb->dev = dev;
 			skb_reserve(skb, 2);	/* 16 byte align */
 			skb_put(skb, len);	/* make room */
 
diff --git a/drivers/net/defxx.c b/drivers/net/defxx.c
index 07d2731..571d82f 100644
--- a/drivers/net/defxx.c
+++ b/drivers/net/defxx.c
@@ -3091,13 +3091,13 @@
 					{
 						/* Receive buffer allocated, pass receive packet up */
 
-						memcpy(skb->data, p_buff + RCV_BUFF_K_PADDING, pkt_len+3);
+						skb_copy_to_linear_data(skb,
+							       p_buff + RCV_BUFF_K_PADDING,
+							       pkt_len + 3);
 					}
 
 					skb_reserve(skb,3);		/* adjust data field so that it points to FC byte */
 					skb_put(skb, pkt_len);		/* pass up packet length, NOT including CRC */
-					skb->dev = bp->dev;		/* pass up device pointer */
-
 					skb->protocol = fddi_type_trans(skb, bp->dev);
 					bp->rcv_total_bytes += skb->len;
 					netif_rx(skb);
diff --git a/drivers/net/depca.c b/drivers/net/depca.c
index f3807aa..1834970 100644
--- a/drivers/net/depca.c
+++ b/drivers/net/depca.c
@@ -1044,7 +1044,6 @@
 					unsigned char *buf;
 					skb_reserve(skb, 2);	/* 16 byte align the IP header */
 					buf = skb_put(skb, pkt_len);
-					skb->dev = dev;
 					if (entry < lp->rx_old) {	/* Wrapped buffer */
 						len = (lp->rxRingMask - lp->rx_old + 1) * RX_BUFF_SZ;
 						memcpy_fromio(buf, lp->rx_buff[lp->rx_old], len);
diff --git a/drivers/net/dgrs.c b/drivers/net/dgrs.c
index a795202..df62c02 100644
--- a/drivers/net/dgrs.c
+++ b/drivers/net/dgrs.c
@@ -503,7 +503,6 @@
 		/* discarding the frame */
 		goto out;
 	}
-	skb->dev = devN;
 	skb_reserve(skb, 2);	/* Align IP header */
 
 again:
@@ -742,7 +741,7 @@
 		}
 
 		amt = min_t(unsigned int, len, rbdp->size - count);
-		memcpy( (char *) S2H(rbdp->buf) + count, skb->data + i, amt);
+		skb_copy_from_linear_data_offset(skb, i, S2H(rbdp->buf) + count, amt);
 		i += amt;
 		count += amt;
 		len -= amt;
diff --git a/drivers/net/dl2k.c b/drivers/net/dl2k.c
index 9d446a0..74ec64a 100644
--- a/drivers/net/dl2k.c
+++ b/drivers/net/dl2k.c
@@ -504,7 +504,6 @@
 					break;
 				}
 				np->rx_skbuff[entry] = skb;
-				skb->dev = dev;
 				/* 16 byte align the IP header */
 				skb_reserve (skb, 2);
 				np->rx_ring[entry].fraginfo =
@@ -575,7 +574,6 @@
 				dev->name);
 			break;
 		}
-		skb->dev = dev;	/* Mark as being used by this device. */
 		skb_reserve (skb, 2);	/* 16 byte align the IP header. */
 		/* Rubicon now supports 40 bits of addressing space. */
 		np->rx_ring[i].fraginfo =
@@ -866,7 +864,6 @@
 							    	DMA_48BIT_MASK,
 							    np->rx_buf_sz,
 							    PCI_DMA_FROMDEVICE);
-				skb->dev = dev;
 				/* 16 byte align the IP header */
 				skb_reserve (skb, 2);
 				eth_copy_and_sum (skb,
@@ -910,7 +907,6 @@
 				break;
 			}
 			np->rx_skbuff[entry] = skb;
-			skb->dev = dev;
 			/* 16 byte align the IP header */
 			skb_reserve (skb, 2);
 			np->rx_ring[entry].fraginfo =
diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c
index 615d2b1..8cc1174 100644
--- a/drivers/net/dm9000.c
+++ b/drivers/net/dm9000.c
@@ -954,7 +954,6 @@
 		/* Move data from DM9000 */
 		if (GoodPacket
 		    && ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) {
-			skb->dev = dev;
 			skb_reserve(skb, 2);
 			rdptr = (u8 *) skb_put(skb, RxLen - 4);
 
diff --git a/drivers/net/e100.c b/drivers/net/e100.c
index 0cefef5..6169663 100644
--- a/drivers/net/e100.c
+++ b/drivers/net/e100.c
@@ -159,7 +159,7 @@
 
 #define DRV_NAME		"e100"
 #define DRV_EXT			"-NAPI"
-#define DRV_VERSION		"3.5.17-k2"DRV_EXT
+#define DRV_VERSION		"3.5.17-k4"DRV_EXT
 #define DRV_DESCRIPTION		"Intel(R) PRO/100 Network Driver"
 #define DRV_COPYRIGHT		"Copyright(c) 1999-2006 Intel Corporation"
 #define PFX			DRV_NAME ": "
@@ -174,10 +174,13 @@
 
 static int debug = 3;
 static int eeprom_bad_csum_allow = 0;
+static int use_io = 0;
 module_param(debug, int, 0);
 module_param(eeprom_bad_csum_allow, int, 0);
+module_param(use_io, int, 0);
 MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
 MODULE_PARM_DESC(eeprom_bad_csum_allow, "Allow bad eeprom checksums");
+MODULE_PARM_DESC(use_io, "Force use of i/o access mode");
 #define DPRINTK(nlevel, klevel, fmt, args...) \
 	(void)((NETIF_MSG_##nlevel & nic->msg_enable) && \
 	printk(KERN_##klevel PFX "%s: %s: " fmt, nic->netdev->name, \
@@ -282,12 +285,6 @@
 	rus_mask         = 0x3C,
 };
 
-enum ru_state  {
-	RU_SUSPENDED = 0,
-	RU_RUNNING	 = 1,
-	RU_UNINITIALIZED = -1,
-};
-
 enum scb_stat_ack {
 	stat_ack_not_ours    = 0x00,
 	stat_ack_sw_gen      = 0x04,
@@ -529,7 +526,6 @@
 	struct rx *rx_to_use;
 	struct rx *rx_to_clean;
 	struct rfd blank_rfd;
-	enum ru_state ru_running;
 
 	spinlock_t cb_lock			____cacheline_aligned;
 	spinlock_t cmd_lock;
@@ -591,7 +587,7 @@
 {
 	/* Flush previous PCI writes through intermediate bridges
 	 * by doing a benign read */
-	(void)readb(&nic->csr->scb.status);
+	(void)ioread8(&nic->csr->scb.status);
 }
 
 static void e100_enable_irq(struct nic *nic)
@@ -599,7 +595,7 @@
 	unsigned long flags;
 
 	spin_lock_irqsave(&nic->cmd_lock, flags);
-	writeb(irq_mask_none, &nic->csr->scb.cmd_hi);
+	iowrite8(irq_mask_none, &nic->csr->scb.cmd_hi);
 	e100_write_flush(nic);
 	spin_unlock_irqrestore(&nic->cmd_lock, flags);
 }
@@ -609,7 +605,7 @@
 	unsigned long flags;
 
 	spin_lock_irqsave(&nic->cmd_lock, flags);
-	writeb(irq_mask_all, &nic->csr->scb.cmd_hi);
+	iowrite8(irq_mask_all, &nic->csr->scb.cmd_hi);
 	e100_write_flush(nic);
 	spin_unlock_irqrestore(&nic->cmd_lock, flags);
 }
@@ -618,11 +614,11 @@
 {
 	/* Put CU and RU into idle with a selective reset to get
 	 * device off of PCI bus */
-	writel(selective_reset, &nic->csr->port);
+	iowrite32(selective_reset, &nic->csr->port);
 	e100_write_flush(nic); udelay(20);
 
 	/* Now fully reset device */
-	writel(software_reset, &nic->csr->port);
+	iowrite32(software_reset, &nic->csr->port);
 	e100_write_flush(nic); udelay(20);
 
 	/* Mask off our interrupt line - it's unmasked after reset */
@@ -639,7 +635,7 @@
 	nic->mem->selftest.signature = 0;
 	nic->mem->selftest.result = 0xFFFFFFFF;
 
-	writel(selftest | dma_addr, &nic->csr->port);
+	iowrite32(selftest | dma_addr, &nic->csr->port);
 	e100_write_flush(nic);
 	/* Wait 10 msec for self-test to complete */
 	msleep(10);
@@ -677,23 +673,23 @@
 	for(j = 0; j < 3; j++) {
 
 		/* Chip select */
-		writeb(eecs | eesk, &nic->csr->eeprom_ctrl_lo);
+		iowrite8(eecs | eesk, &nic->csr->eeprom_ctrl_lo);
 		e100_write_flush(nic); udelay(4);
 
 		for(i = 31; i >= 0; i--) {
 			ctrl = (cmd_addr_data[j] & (1 << i)) ?
 				eecs | eedi : eecs;
-			writeb(ctrl, &nic->csr->eeprom_ctrl_lo);
+			iowrite8(ctrl, &nic->csr->eeprom_ctrl_lo);
 			e100_write_flush(nic); udelay(4);
 
-			writeb(ctrl | eesk, &nic->csr->eeprom_ctrl_lo);
+			iowrite8(ctrl | eesk, &nic->csr->eeprom_ctrl_lo);
 			e100_write_flush(nic); udelay(4);
 		}
 		/* Wait 10 msec for cmd to complete */
 		msleep(10);
 
 		/* Chip deselect */
-		writeb(0, &nic->csr->eeprom_ctrl_lo);
+		iowrite8(0, &nic->csr->eeprom_ctrl_lo);
 		e100_write_flush(nic); udelay(4);
 	}
 };
@@ -709,21 +705,21 @@
 	cmd_addr_data = ((op_read << *addr_len) | addr) << 16;
 
 	/* Chip select */
-	writeb(eecs | eesk, &nic->csr->eeprom_ctrl_lo);
+	iowrite8(eecs | eesk, &nic->csr->eeprom_ctrl_lo);
 	e100_write_flush(nic); udelay(4);
 
 	/* Bit-bang to read word from eeprom */
 	for(i = 31; i >= 0; i--) {
 		ctrl = (cmd_addr_data & (1 << i)) ? eecs | eedi : eecs;
-		writeb(ctrl, &nic->csr->eeprom_ctrl_lo);
+		iowrite8(ctrl, &nic->csr->eeprom_ctrl_lo);
 		e100_write_flush(nic); udelay(4);
 
-		writeb(ctrl | eesk, &nic->csr->eeprom_ctrl_lo);
+		iowrite8(ctrl | eesk, &nic->csr->eeprom_ctrl_lo);
 		e100_write_flush(nic); udelay(4);
 
 		/* Eeprom drives a dummy zero to EEDO after receiving
 		 * complete address.  Use this to adjust addr_len. */
-		ctrl = readb(&nic->csr->eeprom_ctrl_lo);
+		ctrl = ioread8(&nic->csr->eeprom_ctrl_lo);
 		if(!(ctrl & eedo) && i > 16) {
 			*addr_len -= (i - 16);
 			i = 17;
@@ -733,7 +729,7 @@
 	}
 
 	/* Chip deselect */
-	writeb(0, &nic->csr->eeprom_ctrl_lo);
+	iowrite8(0, &nic->csr->eeprom_ctrl_lo);
 	e100_write_flush(nic); udelay(4);
 
 	return le16_to_cpu(data);
@@ -804,7 +800,7 @@
 
 	/* Previous command is accepted when SCB clears */
 	for(i = 0; i < E100_WAIT_SCB_TIMEOUT; i++) {
-		if(likely(!readb(&nic->csr->scb.cmd_lo)))
+		if(likely(!ioread8(&nic->csr->scb.cmd_lo)))
 			break;
 		cpu_relax();
 		if(unlikely(i > E100_WAIT_SCB_FAST))
@@ -816,8 +812,8 @@
 	}
 
 	if(unlikely(cmd != cuc_resume))
-		writel(dma_addr, &nic->csr->scb.gen_ptr);
-	writeb(cmd, &nic->csr->scb.cmd_lo);
+		iowrite32(dma_addr, &nic->csr->scb.gen_ptr);
+	iowrite8(cmd, &nic->csr->scb.cmd_lo);
 
 err_unlock:
 	spin_unlock_irqrestore(&nic->cmd_lock, flags);
@@ -895,7 +891,7 @@
 	 */
 	spin_lock_irqsave(&nic->mdio_lock, flags);
 	for (i = 100; i; --i) {
-		if (readl(&nic->csr->mdi_ctrl) & mdi_ready)
+		if (ioread32(&nic->csr->mdi_ctrl) & mdi_ready)
 			break;
 		udelay(20);
 	}
@@ -905,11 +901,11 @@
 		spin_unlock_irqrestore(&nic->mdio_lock, flags);
 		return 0;		/* No way to indicate timeout error */
 	}
-	writel((reg << 16) | (addr << 21) | dir | data, &nic->csr->mdi_ctrl);
+	iowrite32((reg << 16) | (addr << 21) | dir | data, &nic->csr->mdi_ctrl);
 
 	for (i = 0; i < 100; i++) {
 		udelay(20);
-		if ((data_out = readl(&nic->csr->mdi_ctrl)) & mdi_ready)
+		if ((data_out = ioread32(&nic->csr->mdi_ctrl)) & mdi_ready)
 			break;
 	}
 	spin_unlock_irqrestore(&nic->mdio_lock, flags);
@@ -951,7 +947,7 @@
 		((nic->mac >= mac_82558_D101_A4) ? cb_cid : cb_i));
 
 	/* Template for a freshly allocated RFD */
-	nic->blank_rfd.command = cpu_to_le16(cb_el);
+	nic->blank_rfd.command = cpu_to_le16(cb_el & cb_s);
 	nic->blank_rfd.rbd = 0xFFFFFFFF;
 	nic->blank_rfd.size = cpu_to_le16(VLAN_ETH_FRAME_LEN);
 
@@ -1318,7 +1314,7 @@
 	}
 
 	/* ack any interupts, something could have been set */
-	writeb(~0, &nic->csr->scb.stat_ack);
+	iowrite8(~0, &nic->csr->scb.stat_ack);
 
 	/* if the command failed, or is not OK, notify and return */
 	if (!counter || !(cb->status & cpu_to_le16(cb_ok))) {
@@ -1580,7 +1576,7 @@
 	 * accidentally, due to hardware that shares a register between the
 	 * interrupt mask bit and the SW Interrupt generation bit */
 	spin_lock_irq(&nic->cmd_lock);
-	writeb(readb(&nic->csr->scb.cmd_hi) | irq_sw_gen,&nic->csr->scb.cmd_hi);
+	iowrite8(ioread8(&nic->csr->scb.cmd_hi) | irq_sw_gen,&nic->csr->scb.cmd_hi);
 	e100_write_flush(nic);
 	spin_unlock_irq(&nic->cmd_lock);
 
@@ -1746,19 +1742,11 @@
 	return 0;
 }
 
-static inline void e100_start_receiver(struct nic *nic, struct rx *rx)
+static inline void e100_start_receiver(struct nic *nic)
 {
-	if(!nic->rxs) return;
-	if(RU_SUSPENDED != nic->ru_running) return;
-
-	/* handle init time starts */
-	if(!rx) rx = nic->rxs;
-
-	/* (Re)start RU if suspended or idle and RFA is non-NULL */
-	if(rx->skb) {
-		e100_exec_cmd(nic, ruc_start, rx->dma_addr);
-		nic->ru_running = RU_RUNNING;
-	}
+	/* Start if RFA is non-NULL */
+	if(nic->rx_to_clean->skb)
+		e100_exec_cmd(nic, ruc_start, nic->rx_to_clean->dma_addr);
 }
 
 #define RFD_BUF_LEN (sizeof(struct rfd) + VLAN_ETH_FRAME_LEN)
@@ -1769,7 +1757,7 @@
 
 	/* Align, init, and map the RFD. */
 	skb_reserve(rx->skb, NET_IP_ALIGN);
-	memcpy(rx->skb->data, &nic->blank_rfd, sizeof(struct rfd));
+	skb_copy_to_linear_data(rx->skb, &nic->blank_rfd, sizeof(struct rfd));
 	rx->dma_addr = pci_map_single(nic->pdev, rx->skb->data,
 		RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL);
 
@@ -1787,7 +1775,7 @@
 		put_unaligned(cpu_to_le32(rx->dma_addr),
 			(u32 *)&prev_rfd->link);
 		wmb();
-		prev_rfd->command &= ~cpu_to_le16(cb_el);
+		prev_rfd->command &= ~cpu_to_le16(cb_el & cb_s);
 		pci_dma_sync_single_for_device(nic->pdev, rx->prev->dma_addr,
 			sizeof(struct rfd), PCI_DMA_TODEVICE);
 	}
@@ -1825,10 +1813,6 @@
 	pci_unmap_single(nic->pdev, rx->dma_addr,
 		RFD_BUF_LEN, PCI_DMA_FROMDEVICE);
 
-	/* this allows for a fast restart without re-enabling interrupts */
-	if(le16_to_cpu(rfd->command) & cb_el)
-		nic->ru_running = RU_SUSPENDED;
-
 	/* Pull off the RFD and put the actual data (minus eth hdr) */
 	skb_reserve(skb, sizeof(struct rfd));
 	skb_put(skb, actual_size);
@@ -1859,45 +1843,18 @@
 	unsigned int work_to_do)
 {
 	struct rx *rx;
-	int restart_required = 0;
-	struct rx *rx_to_start = NULL;
-
-	/* are we already rnr? then pay attention!!! this ensures that
-	 * the state machine progression never allows a start with a
-	 * partially cleaned list, avoiding a race between hardware
-	 * and rx_to_clean when in NAPI mode */
-	if(RU_SUSPENDED == nic->ru_running)
-		restart_required = 1;
 
 	/* Indicate newly arrived packets */
 	for(rx = nic->rx_to_clean; rx->skb; rx = nic->rx_to_clean = rx->next) {
-		int err = e100_rx_indicate(nic, rx, work_done, work_to_do);
-		if(-EAGAIN == err) {
-			/* hit quota so have more work to do, restart once
-			 * cleanup is complete */
-			restart_required = 0;
-			break;
-		} else if(-ENODATA == err)
+		if(e100_rx_indicate(nic, rx, work_done, work_to_do))
 			break; /* No more to clean */
 	}
 
-	/* save our starting point as the place we'll restart the receiver */
-	if(restart_required)
-		rx_to_start = nic->rx_to_clean;
-
 	/* Alloc new skbs to refill list */
 	for(rx = nic->rx_to_use; !rx->skb; rx = nic->rx_to_use = rx->next) {
 		if(unlikely(e100_rx_alloc_skb(nic, rx)))
 			break; /* Better luck next time (see watchdog) */
 	}
-
-	if(restart_required) {
-		// ack the rnr?
-		writeb(stat_ack_rnr, &nic->csr->scb.stat_ack);
-		e100_start_receiver(nic, rx_to_start);
-		if(work_done)
-			(*work_done)++;
-	}
 }
 
 static void e100_rx_clean_list(struct nic *nic)
@@ -1905,8 +1862,6 @@
 	struct rx *rx;
 	unsigned int i, count = nic->params.rfds.count;
 
-	nic->ru_running = RU_UNINITIALIZED;
-
 	if(nic->rxs) {
 		for(rx = nic->rxs, i = 0; i < count; rx++, i++) {
 			if(rx->skb) {
@@ -1928,7 +1883,6 @@
 	unsigned int i, count = nic->params.rfds.count;
 
 	nic->rx_to_use = nic->rx_to_clean = NULL;
-	nic->ru_running = RU_UNINITIALIZED;
 
 	if(!(nic->rxs = kcalloc(count, sizeof(struct rx), GFP_ATOMIC)))
 		return -ENOMEM;
@@ -1943,7 +1897,6 @@
 	}
 
 	nic->rx_to_use = nic->rx_to_clean = nic->rxs;
-	nic->ru_running = RU_SUSPENDED;
 
 	return 0;
 }
@@ -1952,7 +1905,7 @@
 {
 	struct net_device *netdev = dev_id;
 	struct nic *nic = netdev_priv(netdev);
-	u8 stat_ack = readb(&nic->csr->scb.stat_ack);
+	u8 stat_ack = ioread8(&nic->csr->scb.stat_ack);
 
 	DPRINTK(INTR, DEBUG, "stat_ack = 0x%02X\n", stat_ack);
 
@@ -1961,11 +1914,7 @@
 		return IRQ_NONE;
 
 	/* Ack interrupt(s) */
-	writeb(stat_ack, &nic->csr->scb.stat_ack);
-
-	/* We hit Receive No Resource (RNR); restart RU after cleaning */
-	if(stat_ack & stat_ack_rnr)
-		nic->ru_running = RU_SUSPENDED;
+	iowrite8(stat_ack, &nic->csr->scb.stat_ack);
 
 	if(likely(netif_rx_schedule_prep(netdev))) {
 		e100_disable_irq(nic);
@@ -2058,7 +2007,7 @@
 	if((err = e100_hw_init(nic)))
 		goto err_clean_cbs;
 	e100_set_multicast_list(nic->netdev);
-	e100_start_receiver(nic, NULL);
+	e100_start_receiver(nic);
 	mod_timer(&nic->watchdog, jiffies);
 	if((err = request_irq(nic->pdev->irq, e100_intr, IRQF_SHARED,
 		nic->netdev->name, nic->netdev)))
@@ -2107,7 +2056,7 @@
 	struct net_device *netdev = nic->netdev;
 
 	DPRINTK(TX_ERR, DEBUG, "scb.status=0x%02X\n",
-		readb(&nic->csr->scb.status));
+		ioread8(&nic->csr->scb.status));
 	e100_down(netdev_priv(netdev));
 	e100_up(netdev_priv(netdev));
 }
@@ -2139,7 +2088,7 @@
 		mdio_write(nic->netdev, nic->mii.phy_id, MII_BMCR,
 			BMCR_LOOPBACK);
 
-	e100_start_receiver(nic, NULL);
+	e100_start_receiver(nic);
 
 	if(!(skb = netdev_alloc_skb(nic->netdev, ETH_DATA_LEN))) {
 		err = -ENOMEM;
@@ -2230,9 +2179,9 @@
 	int i;
 
 	regs->version = (1 << 24) | nic->rev_id;
-	buff[0] = readb(&nic->csr->scb.cmd_hi) << 24 |
-		readb(&nic->csr->scb.cmd_lo) << 16 |
-		readw(&nic->csr->scb.status);
+	buff[0] = ioread8(&nic->csr->scb.cmd_hi) << 24 |
+		ioread8(&nic->csr->scb.cmd_lo) << 16 |
+		ioread16(&nic->csr->scb.status);
 	for(i = E100_PHY_REGS; i >= 0; i--)
 		buff[1 + E100_PHY_REGS - i] =
 			mdio_read(netdev, nic->mii.phy_id, i);
@@ -2604,7 +2553,10 @@
 	SET_MODULE_OWNER(netdev);
 	SET_NETDEV_DEV(netdev, &pdev->dev);
 
-	nic->csr = ioremap(pci_resource_start(pdev, 0), sizeof(struct csr));
+	if (use_io)
+		DPRINTK(PROBE, INFO, "using i/o access mode\n");
+
+	nic->csr = pci_iomap(pdev, (use_io ? 1 : 0), sizeof(struct csr));
 	if(!nic->csr) {
 		DPRINTK(PROBE, ERR, "Cannot map device registers, aborting.\n");
 		err = -ENOMEM;
@@ -2651,11 +2603,16 @@
 
 	memcpy(netdev->dev_addr, nic->eeprom, ETH_ALEN);
 	memcpy(netdev->perm_addr, nic->eeprom, ETH_ALEN);
-	if(!is_valid_ether_addr(netdev->perm_addr)) {
-		DPRINTK(PROBE, ERR, "Invalid MAC address from "
-			"EEPROM, aborting.\n");
-		err = -EAGAIN;
-		goto err_out_free;
+	if (!is_valid_ether_addr(netdev->perm_addr)) {
+		if (!eeprom_bad_csum_allow) {
+			DPRINTK(PROBE, ERR, "Invalid MAC address from "
+			        "EEPROM, aborting.\n");
+			err = -EAGAIN;
+			goto err_out_free;
+		} else {
+			DPRINTK(PROBE, ERR, "Invalid MAC address from EEPROM, "
+			        "you MUST configure one.\n");
+		}
 	}
 
 	/* Wol magic packet can be enabled from eeprom */
@@ -2676,7 +2633,7 @@
 
 	DPRINTK(PROBE, INFO, "addr 0x%llx, irq %d, "
 		"MAC addr %02X:%02X:%02X:%02X:%02X:%02X\n",
-		(unsigned long long)pci_resource_start(pdev, 0), pdev->irq,
+		(unsigned long long)pci_resource_start(pdev, use_io ? 1 : 0), pdev->irq,
 		netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2],
 		netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]);
 
@@ -2685,7 +2642,7 @@
 err_out_free:
 	e100_free(nic);
 err_out_iounmap:
-	iounmap(nic->csr);
+	pci_iounmap(pdev, nic->csr);
 err_out_free_res:
 	pci_release_regions(pdev);
 err_out_disable_pdev:
diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h
index dd4b728..a9ea67e 100644
--- a/drivers/net/e1000/e1000.h
+++ b/drivers/net/e1000/e1000.h
@@ -155,9 +155,6 @@
 /* Number of packet split data buffers (not including the header buffer) */
 #define PS_PAGE_BUFFERS MAX_PS_BUFFERS-1
 
-/* only works for sizes that are powers of 2 */
-#define E1000_ROUNDUP(i, size) ((i) = (((i) + (size) - 1) & ~((size) - 1)))
-
 /* wrapper around a pointer to a socket buffer,
  * so a DMA handle can be stored along with the buffer */
 struct e1000_buffer {
diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c
index 6777887..bb08375 100644
--- a/drivers/net/e1000/e1000_ethtool.c
+++ b/drivers/net/e1000/e1000_ethtool.c
@@ -654,14 +654,11 @@
 	e1000_mac_type mac_type = adapter->hw.mac_type;
 	struct e1000_tx_ring *txdr, *tx_old;
 	struct e1000_rx_ring *rxdr, *rx_old;
-	int i, err, tx_ring_size, rx_ring_size;
+	int i, err;
 
 	if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
 		return -EINVAL;
 
-	tx_ring_size = sizeof(struct e1000_tx_ring) * adapter->num_tx_queues;
-	rx_ring_size = sizeof(struct e1000_rx_ring) * adapter->num_rx_queues;
-
 	while (test_and_set_bit(__E1000_RESETTING, &adapter->flags))
 		msleep(1);
 
@@ -672,11 +669,11 @@
 	rx_old = adapter->rx_ring;
 
 	err = -ENOMEM;
-	txdr = kzalloc(tx_ring_size, GFP_KERNEL);
+	txdr = kcalloc(adapter->num_tx_queues, sizeof(struct e1000_tx_ring), GFP_KERNEL);
 	if (!txdr)
 		goto err_alloc_tx;
 
-	rxdr = kzalloc(rx_ring_size, GFP_KERNEL);
+	rxdr = kcalloc(adapter->num_rx_queues, sizeof(struct e1000_rx_ring), GFP_KERNEL);
 	if (!rxdr)
 		goto err_alloc_rx;
 
@@ -686,12 +683,12 @@
 	rxdr->count = max(ring->rx_pending,(uint32_t)E1000_MIN_RXD);
 	rxdr->count = min(rxdr->count,(uint32_t)(mac_type < e1000_82544 ?
 		E1000_MAX_RXD : E1000_MAX_82544_RXD));
-	E1000_ROUNDUP(rxdr->count, REQ_RX_DESCRIPTOR_MULTIPLE);
+	rxdr->count = ALIGN(rxdr->count, REQ_RX_DESCRIPTOR_MULTIPLE);
 
 	txdr->count = max(ring->tx_pending,(uint32_t)E1000_MIN_TXD);
 	txdr->count = min(txdr->count,(uint32_t)(mac_type < e1000_82544 ?
 		E1000_MAX_TXD : E1000_MAX_82544_TXD));
-	E1000_ROUNDUP(txdr->count, REQ_TX_DESCRIPTOR_MULTIPLE);
+	txdr->count = ALIGN(txdr->count, REQ_TX_DESCRIPTOR_MULTIPLE);
 
 	for (i = 0; i < adapter->num_tx_queues; i++)
 		txdr[i].count = txdr->count;
@@ -742,7 +739,7 @@
 	uint32_t pat, value;                                                   \
 	uint32_t test[] =                                                      \
 		{0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF};              \
-	for (pat = 0; pat < sizeof(test)/sizeof(test[0]); pat++) {              \
+	for (pat = 0; pat < ARRAY_SIZE(test); pat++) {              \
 		E1000_WRITE_REG(&adapter->hw, R, (test[pat] & W));             \
 		value = E1000_READ_REG(&adapter->hw, R);                       \
 		if (value != (test[pat] & W & M)) {                             \
@@ -1053,23 +1050,24 @@
 	struct e1000_rx_ring *rxdr = &adapter->test_rx_ring;
 	struct pci_dev *pdev = adapter->pdev;
 	uint32_t rctl;
-	int size, i, ret_val;
+	int i, ret_val;
 
 	/* Setup Tx descriptor ring and Tx buffers */
 
 	if (!txdr->count)
 		txdr->count = E1000_DEFAULT_TXD;
 
-	size = txdr->count * sizeof(struct e1000_buffer);
-	if (!(txdr->buffer_info = kmalloc(size, GFP_KERNEL))) {
+	if (!(txdr->buffer_info = kcalloc(txdr->count,
+	                                  sizeof(struct e1000_buffer),
+		                          GFP_KERNEL))) {
 		ret_val = 1;
 		goto err_nomem;
 	}
-	memset(txdr->buffer_info, 0, size);
 
 	txdr->size = txdr->count * sizeof(struct e1000_tx_desc);
-	E1000_ROUNDUP(txdr->size, 4096);
-	if (!(txdr->desc = pci_alloc_consistent(pdev, txdr->size, &txdr->dma))) {
+	txdr->size = ALIGN(txdr->size, 4096);
+	if (!(txdr->desc = pci_alloc_consistent(pdev, txdr->size,
+	                                        &txdr->dma))) {
 		ret_val = 2;
 		goto err_nomem;
 	}
@@ -1116,12 +1114,12 @@
 	if (!rxdr->count)
 		rxdr->count = E1000_DEFAULT_RXD;
 
-	size = rxdr->count * sizeof(struct e1000_buffer);
-	if (!(rxdr->buffer_info = kmalloc(size, GFP_KERNEL))) {
+	if (!(rxdr->buffer_info = kcalloc(rxdr->count,
+	                                  sizeof(struct e1000_buffer),
+	                                  GFP_KERNEL))) {
 		ret_val = 4;
 		goto err_nomem;
 	}
-	memset(rxdr->buffer_info, 0, size);
 
 	rxdr->size = rxdr->count * sizeof(struct e1000_rx_desc);
 	if (!(rxdr->desc = pci_alloc_consistent(pdev, rxdr->size, &rxdr->dma))) {
diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c
index b28a915..3a03a74 100644
--- a/drivers/net/e1000/e1000_main.c
+++ b/drivers/net/e1000/e1000_main.c
@@ -409,25 +409,21 @@
 {
 	uint32_t ctrl_ext;
 	uint32_t swsm;
-	uint32_t extcnf;
 
 	/* Let firmware taken over control of h/w */
 	switch (adapter->hw.mac_type) {
-	case e1000_82571:
-	case e1000_82572:
-	case e1000_80003es2lan:
-		ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT);
-		E1000_WRITE_REG(&adapter->hw, CTRL_EXT,
-				ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD);
-		break;
 	case e1000_82573:
 		swsm = E1000_READ_REG(&adapter->hw, SWSM);
 		E1000_WRITE_REG(&adapter->hw, SWSM,
 				swsm & ~E1000_SWSM_DRV_LOAD);
+		break;
+	case e1000_82571:
+	case e1000_82572:
+	case e1000_80003es2lan:
 	case e1000_ich8lan:
-		extcnf = E1000_READ_REG(&adapter->hw, CTRL_EXT);
+		ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT);
 		E1000_WRITE_REG(&adapter->hw, CTRL_EXT,
-				extcnf & ~E1000_CTRL_EXT_DRV_LOAD);
+				ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD);
 		break;
 	default:
 		break;
@@ -450,26 +446,21 @@
 {
 	uint32_t ctrl_ext;
 	uint32_t swsm;
-	uint32_t extcnf;
 
 	/* Let firmware know the driver has taken over */
 	switch (adapter->hw.mac_type) {
-	case e1000_82571:
-	case e1000_82572:
-	case e1000_80003es2lan:
-		ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT);
-		E1000_WRITE_REG(&adapter->hw, CTRL_EXT,
-				ctrl_ext | E1000_CTRL_EXT_DRV_LOAD);
-		break;
 	case e1000_82573:
 		swsm = E1000_READ_REG(&adapter->hw, SWSM);
 		E1000_WRITE_REG(&adapter->hw, SWSM,
 				swsm | E1000_SWSM_DRV_LOAD);
 		break;
+	case e1000_82571:
+	case e1000_82572:
+	case e1000_80003es2lan:
 	case e1000_ich8lan:
-		extcnf = E1000_READ_REG(&adapter->hw, EXTCNF_CTRL);
-		E1000_WRITE_REG(&adapter->hw, EXTCNF_CTRL,
-				extcnf | E1000_EXTCNF_CTRL_SWFLAG);
+		ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT);
+		E1000_WRITE_REG(&adapter->hw, CTRL_EXT,
+				ctrl_ext | E1000_CTRL_EXT_DRV_LOAD);
 		break;
 	default:
 		break;
@@ -522,14 +513,15 @@
 	}
 }
 
-int
-e1000_up(struct e1000_adapter *adapter)
+/**
+ * e1000_configure - configure the hardware for RX and TX
+ * @adapter = private board structure
+ **/
+static void e1000_configure(struct e1000_adapter *adapter)
 {
 	struct net_device *netdev = adapter->netdev;
 	int i;
 
-	/* hardware has been reset, we need to reload some things */
-
 	e1000_set_multi(netdev);
 
 	e1000_restore_vlan(adapter);
@@ -548,14 +540,20 @@
 	}
 
 	adapter->tx_queue_len = netdev->tx_queue_len;
+}
 
-#ifdef CONFIG_E1000_NAPI
-	netif_poll_enable(netdev);
-#endif
-	e1000_irq_enable(adapter);
+int e1000_up(struct e1000_adapter *adapter)
+{
+	/* hardware has been reset, we need to reload some things */
+	e1000_configure(adapter);
 
 	clear_bit(__E1000_DOWN, &adapter->flags);
 
+#ifdef CONFIG_E1000_NAPI
+	netif_poll_enable(adapter->netdev);
+#endif
+	e1000_irq_enable(adapter);
+
 	/* fire a link change interrupt to start the watchdog */
 	E1000_WRITE_REG(&adapter->hw, ICS, E1000_ICS_LSC);
 	return 0;
@@ -640,15 +638,15 @@
 	 * reschedule our watchdog timer */
 	set_bit(__E1000_DOWN, &adapter->flags);
 
+#ifdef CONFIG_E1000_NAPI
+	netif_poll_disable(netdev);
+#endif
 	e1000_irq_disable(adapter);
 
 	del_timer_sync(&adapter->tx_fifo_stall_timer);
 	del_timer_sync(&adapter->watchdog_timer);
 	del_timer_sync(&adapter->phy_info_timer);
 
-#ifdef CONFIG_E1000_NAPI
-	netif_poll_disable(netdev);
-#endif
 	netdev->tx_queue_len = adapter->tx_queue_len;
 	adapter->link_speed = 0;
 	adapter->link_duplex = 0;
@@ -750,9 +748,9 @@
 		               VLAN_TAG_SIZE;
 		min_tx_space = min_rx_space;
 		min_tx_space *= 2;
-		E1000_ROUNDUP(min_tx_space, 1024);
+		min_tx_space = ALIGN(min_tx_space, 1024);
 		min_tx_space >>= 10;
-		E1000_ROUNDUP(min_rx_space, 1024);
+		min_rx_space = ALIGN(min_rx_space, 1024);
 		min_rx_space >>= 10;
 
 		/* If current Tx allocation is less than the min Tx FIFO size,
@@ -1356,31 +1354,27 @@
 static int __devinit
 e1000_alloc_queues(struct e1000_adapter *adapter)
 {
-	int size;
-
-	size = sizeof(struct e1000_tx_ring) * adapter->num_tx_queues;
-	adapter->tx_ring = kmalloc(size, GFP_KERNEL);
+	adapter->tx_ring = kcalloc(adapter->num_tx_queues,
+	                           sizeof(struct e1000_tx_ring), GFP_KERNEL);
 	if (!adapter->tx_ring)
 		return -ENOMEM;
-	memset(adapter->tx_ring, 0, size);
 
-	size = sizeof(struct e1000_rx_ring) * adapter->num_rx_queues;
-	adapter->rx_ring = kmalloc(size, GFP_KERNEL);
+	adapter->rx_ring = kcalloc(adapter->num_rx_queues,
+	                           sizeof(struct e1000_rx_ring), GFP_KERNEL);
 	if (!adapter->rx_ring) {
 		kfree(adapter->tx_ring);
 		return -ENOMEM;
 	}
-	memset(adapter->rx_ring, 0, size);
 
 #ifdef CONFIG_E1000_NAPI
-	size = sizeof(struct net_device) * adapter->num_rx_queues;
-	adapter->polling_netdev = kmalloc(size, GFP_KERNEL);
+	adapter->polling_netdev = kcalloc(adapter->num_rx_queues,
+	                                  sizeof(struct net_device),
+	                                  GFP_KERNEL);
 	if (!adapter->polling_netdev) {
 		kfree(adapter->tx_ring);
 		kfree(adapter->rx_ring);
 		return -ENOMEM;
 	}
-	memset(adapter->polling_netdev, 0, size);
 #endif
 
 	return E1000_SUCCESS;
@@ -1410,21 +1404,17 @@
 		return -EBUSY;
 
 	/* allocate transmit descriptors */
-	if ((err = e1000_setup_all_tx_resources(adapter)))
+	err = e1000_setup_all_tx_resources(adapter);
+	if (err)
 		goto err_setup_tx;
 
 	/* allocate receive descriptors */
-	if ((err = e1000_setup_all_rx_resources(adapter)))
-		goto err_setup_rx;
-
-	err = e1000_request_irq(adapter);
+	err = e1000_setup_all_rx_resources(adapter);
 	if (err)
-		goto err_req_irq;
+		goto err_setup_rx;
 
 	e1000_power_up_phy(adapter);
 
-	if ((err = e1000_up(adapter)))
-		goto err_up;
 	adapter->mng_vlan_id = E1000_MNG_VLAN_NONE;
 	if ((adapter->hw.mng_cookie.status &
 			  E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT)) {
@@ -1437,12 +1427,33 @@
 	    e1000_check_mng_mode(&adapter->hw))
 		e1000_get_hw_control(adapter);
 
+	/* before we allocate an interrupt, we must be ready to handle it.
+	 * Setting DEBUG_SHIRQ in the kernel makes it fire an interrupt
+	 * as soon as we call pci_request_irq, so we have to setup our
+	 * clean_rx handler before we do so.  */
+	e1000_configure(adapter);
+
+	err = e1000_request_irq(adapter);
+	if (err)
+		goto err_req_irq;
+
+	/* From here on the code is the same as e1000_up() */
+	clear_bit(__E1000_DOWN, &adapter->flags);
+
+#ifdef CONFIG_E1000_NAPI
+	netif_poll_enable(netdev);
+#endif
+
+	e1000_irq_enable(adapter);
+
+	/* fire a link status change interrupt to start the watchdog */
+	E1000_WRITE_REG(&adapter->hw, ICS, E1000_ICS_LSC);
+
 	return E1000_SUCCESS;
 
-err_up:
-	e1000_power_down_phy(adapter);
-	e1000_free_irq(adapter);
 err_req_irq:
+	e1000_release_hw_control(adapter);
+	e1000_power_down_phy(adapter);
 	e1000_free_all_rx_resources(adapter);
 err_setup_rx:
 	e1000_free_all_tx_resources(adapter);
@@ -1545,7 +1556,7 @@
 	/* round up to nearest 4K */
 
 	txdr->size = txdr->count * sizeof(struct e1000_tx_desc);
-	E1000_ROUNDUP(txdr->size, 4096);
+	txdr->size = ALIGN(txdr->size, 4096);
 
 	txdr->desc = pci_alloc_consistent(pdev, txdr->size, &txdr->dma);
 	if (!txdr->desc) {
@@ -1759,18 +1770,18 @@
 	}
 	memset(rxdr->buffer_info, 0, size);
 
-	size = sizeof(struct e1000_ps_page) * rxdr->count;
-	rxdr->ps_page = kmalloc(size, GFP_KERNEL);
+	rxdr->ps_page = kcalloc(rxdr->count, sizeof(struct e1000_ps_page),
+	                        GFP_KERNEL);
 	if (!rxdr->ps_page) {
 		vfree(rxdr->buffer_info);
 		DPRINTK(PROBE, ERR,
 		"Unable to allocate memory for the receive descriptor ring\n");
 		return -ENOMEM;
 	}
-	memset(rxdr->ps_page, 0, size);
 
-	size = sizeof(struct e1000_ps_page_dma) * rxdr->count;
-	rxdr->ps_page_dma = kmalloc(size, GFP_KERNEL);
+	rxdr->ps_page_dma = kcalloc(rxdr->count,
+	                            sizeof(struct e1000_ps_page_dma),
+	                            GFP_KERNEL);
 	if (!rxdr->ps_page_dma) {
 		vfree(rxdr->buffer_info);
 		kfree(rxdr->ps_page);
@@ -1778,7 +1789,6 @@
 		"Unable to allocate memory for the receive descriptor ring\n");
 		return -ENOMEM;
 	}
-	memset(rxdr->ps_page_dma, 0, size);
 
 	if (adapter->hw.mac_type <= e1000_82547_rev_2)
 		desc_len = sizeof(struct e1000_rx_desc);
@@ -1788,7 +1798,7 @@
 	/* Round up to nearest 4K */
 
 	rxdr->size = rxdr->count * desc_len;
-	E1000_ROUNDUP(rxdr->size, 4096);
+	rxdr->size = ALIGN(rxdr->size, 4096);
 
 	rxdr->desc = pci_alloc_consistent(pdev, rxdr->size, &rxdr->dma);
 
@@ -2652,7 +2662,7 @@
 
 			netif_carrier_on(netdev);
 			netif_wake_queue(netdev);
-			mod_timer(&adapter->phy_info_timer, jiffies + 2 * HZ);
+			mod_timer(&adapter->phy_info_timer, round_jiffies(jiffies + 2 * HZ));
 			adapter->smartspeed = 0;
 		} else {
 			/* make sure the receive unit is started */
@@ -2669,7 +2679,7 @@
 			DPRINTK(LINK, INFO, "NIC Link is Down\n");
 			netif_carrier_off(netdev);
 			netif_stop_queue(netdev);
-			mod_timer(&adapter->phy_info_timer, jiffies + 2 * HZ);
+			mod_timer(&adapter->phy_info_timer, round_jiffies(jiffies + 2 * HZ));
 
 			/* 80003ES2LAN workaround--
 			 * For packet buffer work-around on link down event;
@@ -2721,7 +2731,7 @@
 		e1000_rar_set(&adapter->hw, adapter->hw.mac_addr, 0);
 
 	/* Reset the timer */
-	mod_timer(&adapter->watchdog_timer, jiffies + 2 * HZ);
+	mod_timer(&adapter->watchdog_timer, round_jiffies(jiffies + 2 * HZ));
 }
 
 enum latency_range {
@@ -2887,33 +2897,30 @@
 				return err;
 		}
 
-		hdr_len = ((skb->h.raw - skb->data) + (skb->h.th->doff << 2));
+		hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
 		mss = skb_shinfo(skb)->gso_size;
 		if (skb->protocol == htons(ETH_P_IP)) {
-			skb->nh.iph->tot_len = 0;
-			skb->nh.iph->check = 0;
-			skb->h.th->check =
-				~csum_tcpudp_magic(skb->nh.iph->saddr,
-						   skb->nh.iph->daddr,
-						   0,
-						   IPPROTO_TCP,
-						   0);
+			struct iphdr *iph = ip_hdr(skb);
+			iph->tot_len = 0;
+			iph->check = 0;
+			tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
+								 iph->daddr, 0,
+								 IPPROTO_TCP,
+								 0);
 			cmd_length = E1000_TXD_CMD_IP;
-			ipcse = skb->h.raw - skb->data - 1;
+			ipcse = skb_transport_offset(skb) - 1;
 		} else if (skb->protocol == htons(ETH_P_IPV6)) {
-			skb->nh.ipv6h->payload_len = 0;
-			skb->h.th->check =
-				~csum_ipv6_magic(&skb->nh.ipv6h->saddr,
-						 &skb->nh.ipv6h->daddr,
-						 0,
-						 IPPROTO_TCP,
-						 0);
+			ipv6_hdr(skb)->payload_len = 0;
+			tcp_hdr(skb)->check =
+				~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+						 &ipv6_hdr(skb)->daddr,
+						 0, IPPROTO_TCP, 0);
 			ipcse = 0;
 		}
-		ipcss = skb->nh.raw - skb->data;
-		ipcso = (void *)&(skb->nh.iph->check) - (void *)skb->data;
-		tucss = skb->h.raw - skb->data;
-		tucso = (void *)&(skb->h.th->check) - (void *)skb->data;
+		ipcss = skb_network_offset(skb);
+		ipcso = (void *)&(ip_hdr(skb)->check) - (void *)skb->data;
+		tucss = skb_transport_offset(skb);
+		tucso = (void *)&(tcp_hdr(skb)->check) - (void *)skb->data;
 		tucse = 0;
 
 		cmd_length |= (E1000_TXD_CMD_DEXT | E1000_TXD_CMD_TSE |
@@ -2954,7 +2961,7 @@
 	uint8_t css;
 
 	if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) {
-		css = skb->h.raw - skb->data;
+		css = skb_transport_offset(skb);
 
 		i = tx_ring->next_to_use;
 		buffer_info = &tx_ring->buffer_info[i];
@@ -2962,7 +2969,8 @@
 
 		context_desc->lower_setup.ip_config = 0;
 		context_desc->upper_setup.tcp_fields.tucss = css;
-		context_desc->upper_setup.tcp_fields.tucso = css + skb->csum;
+		context_desc->upper_setup.tcp_fields.tucso =
+			css + skb->csum_offset;
 		context_desc->upper_setup.tcp_fields.tucse = 0;
 		context_desc->tcp_seg_setup.data = 0;
 		context_desc->cmd_and_length = cpu_to_le32(E1000_TXD_CMD_DEXT);
@@ -3162,7 +3170,7 @@
 	uint32_t fifo_space = adapter->tx_fifo_size - adapter->tx_fifo_head;
 	uint32_t skb_fifo_len = skb->len + E1000_FIFO_HDR;
 
-	E1000_ROUNDUP(skb_fifo_len, E1000_FIFO_HDR);
+	skb_fifo_len = ALIGN(skb_fifo_len, E1000_FIFO_HDR);
 
 	if (adapter->link_duplex != HALF_DUPLEX)
 		goto no_fifo_stall_required;
@@ -3296,7 +3304,7 @@
 		/* TSO Workaround for 82571/2/3 Controllers -- if skb->data
 		* points to just header, pull a few bytes of payload from
 		* frags into skb->data */
-		hdr_len = ((skb->h.raw - skb->data) + (skb->h.th->doff << 2));
+		hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
 		if (skb->data_len && (hdr_len == (skb->len - skb->data_len))) {
 			switch (adapter->hw.mac_type) {
 				unsigned int pull_size;
@@ -3307,7 +3315,7 @@
 				 * NOTE: this is a TSO only workaround
 				 * if end byte alignment not correct move us
 				 * into the next dword */
-				if ((unsigned long)(skb->tail - 1) & 4)
+				if ((unsigned long)(skb_tail_pointer(skb) - 1) & 4)
 					break;
 				/* fall through */
 			case e1000_82571:
@@ -3363,12 +3371,9 @@
 	    (adapter->hw.mac_type == e1000_82573))
 		e1000_transfer_dhcp_info(adapter, skb);
 
-	local_irq_save(flags);
-	if (!spin_trylock(&tx_ring->tx_lock)) {
+	if (!spin_trylock_irqsave(&tx_ring->tx_lock, flags))
 		/* Collision - tell upper layer to requeue */
-		local_irq_restore(flags);
 		return NETDEV_TX_LOCKED;
-	}
 
 	/* need: count + 2 desc gap to keep tail from touching
 	 * head, otherwise try next time */
@@ -4227,9 +4232,12 @@
 			    netdev_alloc_skb(netdev, length + NET_IP_ALIGN);
 			if (new_skb) {
 				skb_reserve(new_skb, NET_IP_ALIGN);
-				memcpy(new_skb->data - NET_IP_ALIGN,
-				       skb->data - NET_IP_ALIGN,
-				       length + NET_IP_ALIGN);
+				skb_copy_to_linear_data_offset(new_skb,
+							       -NET_IP_ALIGN,
+							       (skb->data -
+							        NET_IP_ALIGN),
+							       (length +
+							        NET_IP_ALIGN));
 				/* save the skb in buffer_info as good */
 				buffer_info->skb = skb;
 				skb = new_skb;
@@ -4391,7 +4399,7 @@
 				PCI_DMA_FROMDEVICE);
 			vaddr = kmap_atomic(ps_page->ps_page[0],
 			                    KM_SKB_DATA_SOFTIRQ);
-			memcpy(skb->tail, vaddr, l1);
+			memcpy(skb_tail_pointer(skb), vaddr, l1);
 			kunmap_atomic(vaddr, KM_SKB_DATA_SOFTIRQ);
 			pci_dma_sync_single_for_device(pdev,
 				ps_page_dma->ps_page_dma[0],
diff --git a/drivers/net/e1000/e1000_param.c b/drivers/net/e1000/e1000_param.c
index f8862e2..f485874 100644
--- a/drivers/net/e1000/e1000_param.c
+++ b/drivers/net/e1000/e1000_param.c
@@ -305,7 +305,7 @@
 		if (num_TxDescriptors > bd) {
 			tx_ring->count = TxDescriptors[bd];
 			e1000_validate_option(&tx_ring->count, &opt, adapter);
-			E1000_ROUNDUP(tx_ring->count,
+			tx_ring->count = ALIGN(tx_ring->count,
 						REQ_TX_DESCRIPTOR_MULTIPLE);
 		} else {
 			tx_ring->count = opt.def;
@@ -331,7 +331,7 @@
 		if (num_RxDescriptors > bd) {
 			rx_ring->count = RxDescriptors[bd];
 			e1000_validate_option(&rx_ring->count, &opt, adapter);
-			E1000_ROUNDUP(rx_ring->count,
+			rx_ring->count = ALIGN(rx_ring->count,
 						REQ_RX_DESCRIPTOR_MULTIPLE);
 		} else {
 			rx_ring->count = opt.def;
diff --git a/drivers/net/eepro.c b/drivers/net/eepro.c
index b446309..39654e1 100644
--- a/drivers/net/eepro.c
+++ b/drivers/net/eepro.c
@@ -1591,7 +1591,6 @@
 
 				break;
 			}
-			skb->dev = dev;
 			skb_reserve(skb,2);
 
 			if (lp->version == LAN595)
diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c
index e28bb1e..6c267c3 100644
--- a/drivers/net/eepro100.c
+++ b/drivers/net/eepro100.c
@@ -1793,7 +1793,6 @@
 			   copying to a properly sized skbuff. */
 			if (pkt_len < rx_copybreak
 				&& (skb = dev_alloc_skb(pkt_len + 2)) != 0) {
-				skb->dev = dev;
 				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
 				/* 'skb_put()' points to the start of sk_buff data area. */
 				pci_dma_sync_single_for_cpu(sp->pdev, sp->rx_ring_dma[entry],
@@ -1805,8 +1804,9 @@
 				eth_copy_and_sum(skb, sp->rx_skbuff[entry]->data, pkt_len, 0);
 				skb_put(skb, pkt_len);
 #else
-				memcpy(skb_put(skb, pkt_len), sp->rx_skbuff[entry]->data,
-					   pkt_len);
+				skb_copy_from_linear_data(sp->rx_skbuff[entry],
+							  skb_put(skb, pkt_len),
+							  pkt_len);
 #endif
 				pci_dma_sync_single_for_device(sp->pdev, sp->rx_ring_dma[entry],
 											   sizeof(struct RxFD) + pkt_len,
diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c
index 3868b80..7934ea3 100644
--- a/drivers/net/eexpress.c
+++ b/drivers/net/eexpress.c
@@ -115,6 +115,7 @@
 #include <linux/mca-legacy.h>
 #include <linux/spinlock.h>
 #include <linux/bitops.h>
+#include <linux/jiffies.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -556,7 +557,7 @@
 
 	if (lp->started)
 	{
-		if ((jiffies - dev->trans_start)>50)
+		if (time_after(jiffies, dev->trans_start + 50))
 		{
 			if (lp->tx_link==lp->last_tx_restart)
 			{
@@ -612,7 +613,7 @@
 	}
 	else
 	{
-		if ((jiffies-lp->init_time)>10)
+		if (time_after(jiffies, lp->init_time + 10))
 		{
 			unsigned short status = scb_status(dev);
 			printk(KERN_WARNING "%s: i82586 startup timed out, status %04x, resetting...\n",
@@ -776,7 +777,7 @@
 static void eexp_cmd_clear(struct net_device *dev)
 {
 	unsigned long int oldtime = jiffies;
-	while (scb_rdcmd(dev) && ((jiffies-oldtime)<10));
+	while (scb_rdcmd(dev) && (time_before(jiffies, oldtime + 10)));
 	if (scb_rdcmd(dev)) {
 		printk("%s: command didn't clear\n", dev->name);
 	}
@@ -976,7 +977,6 @@
 					lp->stats.rx_dropped++;
 					break;
 				}
-				skb->dev = dev;
 				skb_reserve(skb, 2);
 				outw(pbuf+10, ioaddr+READ_PTR);
 			        insw(ioaddr+DATAPORT, skb_put(skb,pkt_len),(pkt_len+1)>>1);
@@ -1650,7 +1650,7 @@
 #endif
                 oj = jiffies;
                 while ((SCB_CUstat(scb_status(dev)) == 2) &&
-                       ((jiffies-oj) < 2000));
+                       (time_before(jiffies, oj + 2000)));
 		if (SCB_CUstat(scb_status(dev)) == 2)
 			printk("%s: warning, CU didn't stop\n", dev->name);
                 lp->started &= ~(STARTED_CU);
diff --git a/drivers/net/ehea/ehea.h b/drivers/net/ehea/ehea.h
index 42295d6..602872d 100644
--- a/drivers/net/ehea/ehea.h
+++ b/drivers/net/ehea/ehea.h
@@ -39,7 +39,7 @@
 #include <asm/io.h>
 
 #define DRV_NAME	"ehea"
-#define DRV_VERSION	"EHEA_0046"
+#define DRV_VERSION	"EHEA_0058"
 
 #define EHEA_MSG_DEFAULT (NETIF_MSG_LINK | NETIF_MSG_TIMER \
 	| NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR)
@@ -78,10 +78,7 @@
 #define EHEA_RQ2_PKT_SIZE       1522
 #define EHEA_L_PKT_SIZE         256	/* low latency */
 
-#define EHEA_POLL_MAX_RWQE      1000
-
 /* Send completion signaling */
-#define EHEA_SIG_IV_LONG           1
 
 /* Protection Domain Identifier */
 #define EHEA_PD_ID        0xaabcdeff
@@ -108,11 +105,7 @@
 #define EHEA_CACHE_LINE          128
 
 /* Memory Regions */
-#define EHEA_MR_MAX_TX_PAGES   20
-#define EHEA_MR_TX_DATA_PN      3
 #define EHEA_MR_ACC_CTRL       0x00800000
-#define EHEA_RWQES_PER_MR_RQ2  10
-#define EHEA_RWQES_PER_MR_RQ3  10
 
 #define EHEA_WATCH_DOG_TIMEOUT 10*HZ
 
@@ -311,6 +304,7 @@
  * Memory Region
  */
 struct ehea_mr {
+	struct ehea_adapter *adapter;
 	u64 handle;
 	u64 vaddr;
 	u32 lkey;
@@ -319,17 +313,12 @@
 /*
  * Port state information
  */
-struct port_state {
-	int poll_max_processed;
+struct port_stats {
 	int poll_receive_errors;
-	int ehea_poll;
 	int queue_stopped;
-	int min_swqe_avail;
-	u64 sqc_stop_sum;
-	int pkt_send;
-	int pkt_xmit;
-	int send_tasklet;
-	int nwqe;
+	int err_tcp_cksum;
+	int err_ip_cksum;
+	int err_frame_crc;
 };
 
 #define EHEA_IRQ_NAME_SIZE 20
@@ -348,6 +337,7 @@
  * Port resources
  */
 struct ehea_port_res {
+	struct port_stats p_stats;
 	struct ehea_mr send_mr;       	/* send memory region */
 	struct ehea_mr recv_mr;       	/* receive memory region */
 	spinlock_t xmit_lock;
@@ -357,9 +347,8 @@
 	struct ehea_qp *qp;
 	struct ehea_cq *send_cq;
 	struct ehea_cq *recv_cq;
-	struct ehea_eq *send_eq;
-	struct ehea_eq *recv_eq;
-	spinlock_t send_lock;
+	struct ehea_eq *eq;
+	struct net_device *d_netdev;
 	struct ehea_q_skb_arr rq1_skba;
 	struct ehea_q_skb_arr rq2_skba;
 	struct ehea_q_skb_arr rq3_skba;
@@ -369,21 +358,18 @@
 	int swqe_refill_th;
 	atomic_t swqe_avail;
 	int swqe_ll_count;
-	int swqe_count;
 	u32 swqe_id_counter;
 	u64 tx_packets;
-	struct tasklet_struct send_comp_task;
-	spinlock_t recv_lock;
-	struct port_state p_state;
 	u64 rx_packets;
 	u32 poll_counter;
 };
 
 
+#define EHEA_MAX_PORTS 16
 struct ehea_adapter {
 	u64 handle;
-	u8 num_ports;
-	struct ehea_port *port[16];
+	struct ibmebus_dev *ebus_dev;
+	struct ehea_port *port[EHEA_MAX_PORTS];
 	struct ehea_eq *neq;       /* notification event queue */
 	struct workqueue_struct *ehea_wq;
 	struct tasklet_struct neq_tasklet;
@@ -406,7 +392,7 @@
 	struct net_device *netdev;
 	struct net_device_stats stats;
 	struct ehea_port_res port_res[EHEA_MAX_PORT_RES];
-	struct device_node *of_dev_node; /* Open Firmware Device Node */
+	struct of_device  ofdev; /* Open Firmware Device */
 	struct ehea_mc_list *mc_list;	 /* Multicast MAC addresses */
 	struct vlan_group *vgrp;
 	struct ehea_eq *qp_eq;
@@ -415,7 +401,9 @@
 	char int_aff_name[EHEA_IRQ_NAME_SIZE];
 	int allmulti;			 /* Indicates IFF_ALLMULTI state */
 	int promisc;		 	 /* Indicates IFF_PROMISC state */
+	int num_tx_qps;
 	int num_add_tx_qps;
+	int num_mcs;
 	int resets;
 	u64 mac_addr;
 	u32 logical_port_id;
diff --git a/drivers/net/ehea/ehea_ethtool.c b/drivers/net/ehea/ehea_ethtool.c
index 9f57c2e..decec8c 100644
--- a/drivers/net/ehea/ehea_ethtool.c
+++ b/drivers/net/ehea/ehea_ethtool.c
@@ -144,8 +144,8 @@
 static void ehea_get_drvinfo(struct net_device *dev,
 			       struct ethtool_drvinfo *info)
 {
-	strlcpy(info->driver, DRV_NAME, sizeof(info->driver) - 1);
-	strlcpy(info->version, DRV_VERSION, sizeof(info->version) - 1);
+	strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+	strlcpy(info->version, DRV_VERSION, sizeof(info->version));
 }
 
 static u32 ehea_get_msglevel(struct net_device *dev)
@@ -166,33 +166,23 @@
 }
 
 static char ehea_ethtool_stats_keys[][ETH_GSTRING_LEN] = {
-	{"poll_max_processed"},
-	{"queue_stopped"},
-	{"min_swqe_avail"},
-	{"poll_receive_err"},
-	{"pkt_send"},
-	{"pkt_xmit"},
-	{"send_tasklet"},
-	{"ehea_poll"},
-	{"nwqe"},
-	{"swqe_available_0"},
 	{"sig_comp_iv"},
 	{"swqe_refill_th"},
 	{"port resets"},
-	{"rxo"},
-	{"rx64"},
-	{"rx65"},
-	{"rx128"},
-	{"rx256"},
-	{"rx512"},
-	{"rx1024"},
-	{"txo"},
-	{"tx64"},
-	{"tx65"},
-	{"tx128"},
-	{"tx256"},
-	{"tx512"},
-	{"tx1024"},
+	{"Receive errors"},
+	{"TCP cksum errors"},
+	{"IP cksum errors"},
+	{"Frame cksum errors"},
+	{"num SQ stopped"},
+	{"SQ stopped"},
+	{"PR0 free_swqes"},
+	{"PR1 free_swqes"},
+	{"PR2 free_swqes"},
+	{"PR3 free_swqes"},
+	{"PR4 free_swqes"},
+	{"PR5 free_swqes"},
+	{"PR6 free_swqes"},
+	{"PR7 free_swqes"},
 };
 
 static void ehea_get_strings(struct net_device *dev, u32 stringset, u8 *data)
@@ -211,63 +201,44 @@
 static void ehea_get_ethtool_stats(struct net_device *dev,
 				     struct ethtool_stats *stats, u64 *data)
 {
-	u64 hret;
-	int i;
+	int i, k, tmp;
 	struct ehea_port *port = netdev_priv(dev);
-	struct ehea_adapter *adapter = port->adapter;
-	struct ehea_port_res *pr = &port->port_res[0];
-	struct port_state *p_state = &pr->p_state;
-	struct hcp_ehea_port_cb6 *cb6;
 
 	for (i = 0; i < ehea_get_stats_count(dev); i++)
 		data[i] = 0;
-
 	i = 0;
 
-	data[i++] = p_state->poll_max_processed;
-	data[i++] = p_state->queue_stopped;
-	data[i++] = p_state->min_swqe_avail;
-	data[i++] = p_state->poll_receive_errors;
-	data[i++] = p_state->pkt_send;
-	data[i++] = p_state->pkt_xmit;
-	data[i++] = p_state->send_tasklet;
-	data[i++] = p_state->ehea_poll;
-	data[i++] = p_state->nwqe;
-	data[i++] = atomic_read(&port->port_res[0].swqe_avail);
 	data[i++] = port->sig_comp_iv;
 	data[i++] = port->port_res[0].swqe_refill_th;
 	data[i++] = port->resets;
 
-	cb6 = kzalloc(PAGE_SIZE, GFP_KERNEL);
-	if (!cb6) {
-		ehea_error("no mem for cb6");
-		return;
-	}
+	for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
+		tmp += port->port_res[k].p_stats.poll_receive_errors;
+	data[i++] = tmp;
 
-	hret = ehea_h_query_ehea_port(adapter->handle, port->logical_port_id,
-				      H_PORT_CB6, H_PORT_CB6_ALL, cb6);
-	if (netif_msg_hw(port))
-		ehea_dump(cb6, sizeof(*cb6), "ehea_get_ethtool_stats");
+	for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
+		tmp += port->port_res[k].p_stats.err_tcp_cksum;
+	data[i++] = tmp;
 
-	if (hret == H_SUCCESS) {
-		data[i++] = cb6->rxo;
-		data[i++] = cb6->rx64;
-		data[i++] = cb6->rx65;
-		data[i++] = cb6->rx128;
-		data[i++] = cb6->rx256;
-		data[i++] = cb6->rx512;
-		data[i++] = cb6->rx1024;
-		data[i++] = cb6->txo;
-		data[i++] = cb6->tx64;
-		data[i++] = cb6->tx65;
-		data[i++] = cb6->tx128;
-		data[i++] = cb6->tx256;
-		data[i++] = cb6->tx512;
-		data[i++] = cb6->tx1024;
-	} else
-		ehea_error("query_ehea_port failed");
+	for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
+		tmp += port->port_res[k].p_stats.err_ip_cksum;
+	data[i++] = tmp;
 
-	kfree(cb6);
+	for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
+		tmp += port->port_res[k].p_stats.err_frame_crc;
+	data[i++] = tmp;
+
+	for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
+		tmp += port->port_res[k].p_stats.queue_stopped;
+	data[i++] = tmp;
+
+	for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
+		tmp |= port->port_res[k].queue_stopped;
+	data[i++] = tmp;
+
+	for (k = 0; k < 8; k++)
+		data[i++] = atomic_read(&port->port_res[k].swqe_avail);
+
 }
 
 const struct ethtool_ops ehea_ethtool_ops = {
diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c
index 0e4042b..c7a5614 100644
--- a/drivers/net/ehea/ehea_main.c
+++ b/drivers/net/ehea/ehea_main.c
@@ -51,13 +51,18 @@
 static int rq2_entries = EHEA_DEF_ENTRIES_RQ2;
 static int rq3_entries = EHEA_DEF_ENTRIES_RQ3;
 static int sq_entries = EHEA_DEF_ENTRIES_SQ;
+static int use_mcs = 0;
+static int num_tx_qps = EHEA_NUM_TX_QP;
 
 module_param(msg_level, int, 0);
 module_param(rq1_entries, int, 0);
 module_param(rq2_entries, int, 0);
 module_param(rq3_entries, int, 0);
 module_param(sq_entries, int, 0);
+module_param(use_mcs, int, 0);
+module_param(num_tx_qps, int, 0);
 
+MODULE_PARM_DESC(num_tx_qps, "Number of TX-QPS");
 MODULE_PARM_DESC(msg_level, "msg_level");
 MODULE_PARM_DESC(rq3_entries, "Number of entries for Receive Queue 3 "
 		 "[2^x - 1], x = [6..14]. Default = "
@@ -71,6 +76,29 @@
 MODULE_PARM_DESC(sq_entries, " Number of entries for the Send Queue  "
 		 "[2^x - 1], x = [6..14]. Default = "
 		 __MODULE_STRING(EHEA_DEF_ENTRIES_SQ) ")");
+MODULE_PARM_DESC(use_mcs, " 0:NAPI, 1:Multiple receive queues, Default = 1 ");
+
+static int port_name_cnt = 0;
+
+static int __devinit ehea_probe_adapter(struct ibmebus_dev *dev,
+                                        const struct of_device_id *id);
+
+static int __devexit ehea_remove(struct ibmebus_dev *dev);
+
+static struct of_device_id ehea_device_table[] = {
+	{
+		.name = "lhea",
+		.compatible = "IBM,lhea",
+	},
+	{},
+};
+
+static struct ibmebus_driver ehea_driver = {
+	.name = "ehea",
+	.id_table = ehea_device_table,
+	.probe = ehea_probe_adapter,
+	.remove = ehea_remove,
+};
 
 void ehea_dump(void *adr, int len, char *msg) {
 	int x;
@@ -197,7 +225,7 @@
 		struct sk_buff *skb = netdev_alloc_skb(dev, packet_size);
 		if (!skb) {
 			ehea_error("%s: no mem for skb/%d wqes filled",
-				   dev->name, i);
+				   pr->port->netdev->name, i);
 			q_skba->os_skbs = fill_wqes - i;
 			ret = -ENOMEM;
 			break;
@@ -321,6 +349,13 @@
 {
 	struct sk_buff *skb;
 
+	if (cqe->status & EHEA_CQE_STAT_ERR_TCP)
+		pr->p_stats.err_tcp_cksum++;
+	if (cqe->status & EHEA_CQE_STAT_ERR_IP)
+		pr->p_stats.err_ip_cksum++;
+	if (cqe->status & EHEA_CQE_STAT_ERR_CRC)
+		pr->p_stats.err_frame_crc++;
+
 	if (netif_msg_rx_err(pr->port)) {
 		ehea_error("CQE Error for QP %d", pr->qp->init_attr.qp_nr);
 		ehea_dump(cqe, sizeof(*cqe), "CQE");
@@ -345,10 +380,11 @@
 	return 0;
 }
 
-static int ehea_poll(struct net_device *dev, int *budget)
+static struct ehea_cqe *ehea_proc_rwqes(struct net_device *dev,
+					struct ehea_port_res *pr,
+					int *budget)
 {
-	struct ehea_port *port = netdev_priv(dev);
-	struct ehea_port_res *pr = &port->port_res[0];
+	struct ehea_port *port = pr->port;
 	struct ehea_qp *qp = pr->qp;
 	struct ehea_cqe *cqe;
 	struct sk_buff *skb;
@@ -359,14 +395,12 @@
 	int skb_arr_rq2_len = pr->rq2_skba.len;
 	int skb_arr_rq3_len = pr->rq3_skba.len;
 	int processed, processed_rq1, processed_rq2, processed_rq3;
-	int wqe_index, last_wqe_index, rq, intreq, my_quota, port_reset;
+	int wqe_index, last_wqe_index, rq, my_quota, port_reset;
 
 	processed = processed_rq1 = processed_rq2 = processed_rq3 = 0;
 	last_wqe_index = 0;
 	my_quota = min(*budget, dev->quota);
-	my_quota = min(my_quota, EHEA_POLL_MAX_RWQE);
 
-	/* rq0 is low latency RQ */
 	cqe = ehea_poll_rq1(qp, &wqe_index);
 	while ((my_quota > 0) && cqe) {
 		ehea_inc_rq1(qp);
@@ -386,13 +420,14 @@
 				if (unlikely(!skb)) {
 					if (netif_msg_rx_err(port))
 						ehea_error("LL rq1: skb=NULL");
-					skb = netdev_alloc_skb(dev,
+
+					skb = netdev_alloc_skb(port->netdev,
 							       EHEA_L_PKT_SIZE);
 					if (!skb)
 						break;
 				}
-				memcpy(skb->data, ((char*)cqe) + 64,
-				       cqe->num_bytes_transfered - 4);
+				skb_copy_to_linear_data(skb, ((char*)cqe) + 64,
+					       cqe->num_bytes_transfered - 4);
 				ehea_fill_skb(dev, skb, cqe);
 			} else if (rq == 2) {  /* RQ2 */
 				skb = get_skb_by_index(skb_arr_rq2,
@@ -402,7 +437,7 @@
 						ehea_error("rq2: skb=NULL");
 					break;
 				}
-				ehea_fill_skb(dev, skb, cqe);
+				ehea_fill_skb(port->netdev, skb, cqe);
 				processed_rq2++;
 			} else {  /* RQ3 */
 				skb = get_skb_by_index(skb_arr_rq3,
@@ -412,7 +447,7 @@
 						ehea_error("rq3: skb=NULL");
 					break;
 				}
-				ehea_fill_skb(dev, skb, cqe);
+				ehea_fill_skb(port->netdev, skb, cqe);
 				processed_rq3++;
 			}
 
@@ -421,9 +456,8 @@
 							 cqe->vlan_tag);
 			else
 				netif_receive_skb(skb);
-
-		} else { /* Error occured */
-			pr->p_state.poll_receive_errors++;
+		} else {
+			pr->p_stats.poll_receive_errors++;
 			port_reset = ehea_treat_poll_error(pr, rq, cqe,
 							   &processed_rq2,
 							   &processed_rq3);
@@ -433,72 +467,32 @@
 		cqe = ehea_poll_rq1(qp, &wqe_index);
 	}
 
-	dev->quota -= processed;
-	*budget -= processed;
-
-	pr->p_state.ehea_poll += 1;
 	pr->rx_packets += processed;
+	*budget -= processed;
 
 	ehea_refill_rq1(pr, last_wqe_index, processed_rq1);
 	ehea_refill_rq2(pr, processed_rq2);
 	ehea_refill_rq3(pr, processed_rq3);
 
-	intreq = ((pr->p_state.ehea_poll & 0xF) == 0xF);
-
-	if (!cqe || intreq) {
-		netif_rx_complete(dev);
-		ehea_reset_cq_ep(pr->recv_cq);
-		ehea_reset_cq_n1(pr->recv_cq);
-		cqe = hw_qeit_get_valid(&qp->hw_rqueue1);
-		if (!cqe || intreq)
-			return 0;
-		if (!netif_rx_reschedule(dev, my_quota))
-			return 0;
-	}
-	return 1;
+	cqe = ehea_poll_rq1(qp, &wqe_index);
+	return cqe;
 }
 
-void free_sent_skbs(struct ehea_cqe *cqe, struct ehea_port_res *pr)
+static struct ehea_cqe *ehea_proc_cqes(struct ehea_port_res *pr, int my_quota)
 {
 	struct sk_buff *skb;
-	int index, max_index_mask, i;
-
-	index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, cqe->wr_id);
-	max_index_mask = pr->sq_skba.len - 1;
-	for (i = 0; i < EHEA_BMASK_GET(EHEA_WR_ID_REFILL, cqe->wr_id); i++) {
-		skb = pr->sq_skba.arr[index];
-		if (likely(skb)) {
-			dev_kfree_skb(skb);
-			pr->sq_skba.arr[index] = NULL;
-		} else {
-			ehea_error("skb=NULL, wr_id=%lX, loop=%d, index=%d",
-				   cqe->wr_id, i, index);
-		}
-		index--;
-		index &= max_index_mask;
-	}
-}
-
-#define MAX_SENDCOMP_QUOTA 400
-void ehea_send_irq_tasklet(unsigned long data)
-{
-	struct ehea_port_res *pr = (struct ehea_port_res*)data;
 	struct ehea_cq *send_cq = pr->send_cq;
 	struct ehea_cqe *cqe;
-	int quota = MAX_SENDCOMP_QUOTA;
+	int quota = my_quota;
 	int cqe_counter = 0;
 	int swqe_av = 0;
+	int index;
 	unsigned long flags;
 
-	do {
-		cqe = ehea_poll_cq(send_cq);
-		if (!cqe) {
-			ehea_reset_cq_ep(send_cq);
-			ehea_reset_cq_n1(send_cq);
-			cqe = ehea_poll_cq(send_cq);
-			if (!cqe)
-				break;
-		}
+	cqe = ehea_poll_cq(send_cq);
+	while(cqe && (quota > 0)) {
+		ehea_inc_cq(send_cq);
+
 		cqe_counter++;
 		rmb();
 		if (cqe->status & EHEA_CQE_STAT_ERR_MASK) {
@@ -514,17 +508,25 @@
 			ehea_dump(cqe, sizeof(*cqe), "CQE");
 
 		if (likely(EHEA_BMASK_GET(EHEA_WR_ID_TYPE, cqe->wr_id)
-			   == EHEA_SWQE2_TYPE))
-			free_sent_skbs(cqe, pr);
+			   == EHEA_SWQE2_TYPE)) {
+
+			index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, cqe->wr_id);
+			skb = pr->sq_skba.arr[index];
+			dev_kfree_skb(skb);
+			pr->sq_skba.arr[index] = NULL;
+		}
 
 		swqe_av += EHEA_BMASK_GET(EHEA_WR_ID_REFILL, cqe->wr_id);
 		quota--;
-	} while (quota > 0);
+
+		cqe = ehea_poll_cq(send_cq);
+	};
 
 	ehea_update_feca(send_cq, cqe_counter);
 	atomic_add(swqe_av, &pr->swqe_avail);
 
 	spin_lock_irqsave(&pr->netif_queue, flags);
+
 	if (pr->queue_stopped && (atomic_read(&pr->swqe_avail)
 				  >= pr->swqe_refill_th)) {
 		netif_wake_queue(pr->port->netdev);
@@ -532,22 +534,55 @@
 	}
 	spin_unlock_irqrestore(&pr->netif_queue, flags);
 
-	if (unlikely(cqe))
-		tasklet_hi_schedule(&pr->send_comp_task);
+	return cqe;
 }
 
-static irqreturn_t ehea_send_irq_handler(int irq, void *param)
+#define EHEA_NAPI_POLL_NUM_BEFORE_IRQ 16
+
+static int ehea_poll(struct net_device *dev, int *budget)
 {
-	struct ehea_port_res *pr = param;
-	tasklet_hi_schedule(&pr->send_comp_task);
-	return IRQ_HANDLED;
+	struct ehea_port_res *pr = dev->priv;
+	struct ehea_cqe *cqe;
+	struct ehea_cqe *cqe_skb = NULL;
+	int force_irq, wqe_index;
+
+	cqe = ehea_poll_rq1(pr->qp, &wqe_index);
+	cqe_skb = ehea_poll_cq(pr->send_cq);
+
+	force_irq = (pr->poll_counter > EHEA_NAPI_POLL_NUM_BEFORE_IRQ);
+
+	if ((!cqe && !cqe_skb) || force_irq) {
+		pr->poll_counter = 0;
+		netif_rx_complete(dev);
+		ehea_reset_cq_ep(pr->recv_cq);
+		ehea_reset_cq_ep(pr->send_cq);
+		ehea_reset_cq_n1(pr->recv_cq);
+		ehea_reset_cq_n1(pr->send_cq);
+		cqe = ehea_poll_rq1(pr->qp, &wqe_index);
+		cqe_skb = ehea_poll_cq(pr->send_cq);
+
+		if (!cqe && !cqe_skb)
+			return 0;
+
+		if (!netif_rx_reschedule(dev, dev->quota))
+			return 0;
+	}
+
+	cqe = ehea_proc_rwqes(dev, pr, budget);
+	cqe_skb = ehea_proc_cqes(pr, 300);
+
+	if (cqe || cqe_skb)
+		pr->poll_counter++;
+
+	return 1;
 }
 
 static irqreturn_t ehea_recv_irq_handler(int irq, void *param)
 {
 	struct ehea_port_res *pr = param;
-	struct ehea_port *port = pr->port;
-	netif_rx_schedule(port->netdev);
+
+	netif_rx_schedule(pr->d_netdev);
+
 	return IRQ_HANDLED;
 }
 
@@ -580,7 +615,7 @@
 {
 	int i;
 
-	for (i = 0; i < adapter->num_ports; i++)
+	for (i = 0; i < EHEA_MAX_PORTS; i++)
 		if (adapter->port[i])
 	                if (adapter->port[i]->logical_port_id == logical_port)
 				return adapter->port[i];
@@ -650,19 +685,25 @@
 	}
 
 	port->autoneg = 1;
+	port->num_mcs = cb0->num_default_qps;
 
 	/* Number of default QPs */
-	port->num_def_qps = cb0->num_default_qps;
+	if (use_mcs)
+		port->num_def_qps = cb0->num_default_qps;
+	else
+		port->num_def_qps = 1;
 
 	if (!port->num_def_qps) {
 		ret = -EINVAL;
 		goto out_free;
 	}
 
-	if (port->num_def_qps >= EHEA_NUM_TX_QP)
+	port->num_tx_qps = num_tx_qps;
+
+	if (port->num_def_qps >= port->num_tx_qps)
 		port->num_add_tx_qps = 0;
 	else
-		port->num_add_tx_qps = EHEA_NUM_TX_QP - port->num_def_qps;
+		port->num_add_tx_qps = port->num_tx_qps - port->num_def_qps;
 
 	ret = 0;
 out_free:
@@ -882,23 +923,6 @@
 	struct ehea_port_res *pr;
 	int i, ret;
 
-	for (i = 0; i < port->num_def_qps; i++) {
-		pr = &port->port_res[i];
-		snprintf(pr->int_recv_name, EHEA_IRQ_NAME_SIZE - 1
-			 , "%s-recv%d", dev->name, i);
-		ret = ibmebus_request_irq(NULL, pr->recv_eq->attr.ist1,
-					  ehea_recv_irq_handler,
-					  IRQF_DISABLED, pr->int_recv_name, pr);
-		if (ret) {
-			ehea_error("failed registering irq for ehea_recv_int:"
-				   "port_res_nr:%d, ist=%X", i,
-				   pr->recv_eq->attr.ist1);
-			goto out_free_seq;
-		}
-		if (netif_msg_ifup(port))
-			ehea_info("irq_handle 0x%X for funct ehea_recv_int %d "
-				  "registered", pr->recv_eq->attr.ist1, i);
-	}
 
 	snprintf(port->int_aff_name, EHEA_IRQ_NAME_SIZE - 1, "%s-aff",
 		 dev->name);
@@ -916,41 +940,41 @@
 		ehea_info("irq_handle 0x%X for function qp_aff_irq_handler "
 			  "registered", port->qp_eq->attr.ist1);
 
+
 	for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) {
 		pr = &port->port_res[i];
 		snprintf(pr->int_send_name, EHEA_IRQ_NAME_SIZE - 1,
-			 "%s-send%d", dev->name, i);
-		ret = ibmebus_request_irq(NULL, pr->send_eq->attr.ist1,
-					  ehea_send_irq_handler,
+			 "%s-queue%d", dev->name, i);
+		ret = ibmebus_request_irq(NULL, pr->eq->attr.ist1,
+					  ehea_recv_irq_handler,
 					  IRQF_DISABLED, pr->int_send_name,
 					  pr);
 		if (ret) {
-			ehea_error("failed registering irq for ehea_send "
+			ehea_error("failed registering irq for ehea_queue "
 				   "port_res_nr:%d, ist=%X", i,
-				   pr->send_eq->attr.ist1);
+				   pr->eq->attr.ist1);
 			goto out_free_req;
 		}
 		if (netif_msg_ifup(port))
-			ehea_info("irq_handle 0x%X for function ehea_send_int "
-				  "%d registered", pr->send_eq->attr.ist1, i);
+			ehea_info("irq_handle 0x%X for function ehea_queue_int "
+				  "%d registered", pr->eq->attr.ist1, i);
 	}
 out:
 	return ret;
 
+
 out_free_req:
 	while (--i >= 0) {
-		u32 ist = port->port_res[i].send_eq->attr.ist1;
+		u32 ist = port->port_res[i].eq->attr.ist1;
 		ibmebus_free_irq(NULL, ist, &port->port_res[i]);
 	}
+
 out_free_qpeq:
 	ibmebus_free_irq(NULL, port->qp_eq->attr.ist1, port);
 	i = port->num_def_qps;
-out_free_seq:
-	while (--i >= 0) {
-		u32 ist = port->port_res[i].recv_eq->attr.ist1;
-		ibmebus_free_irq(NULL, ist, &port->port_res[i]);
-	}
+
 	goto out;
+
 }
 
 static void ehea_free_interrupts(struct net_device *dev)
@@ -960,21 +984,13 @@
 	int i;
 
 	/* send */
+
 	for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) {
 		pr = &port->port_res[i];
-		ibmebus_free_irq(NULL, pr->send_eq->attr.ist1, pr);
+		ibmebus_free_irq(NULL, pr->eq->attr.ist1, pr);
 		if (netif_msg_intr(port))
 			ehea_info("free send irq for res %d with handle 0x%X",
-				  i, pr->send_eq->attr.ist1);
-	}
-
-	/* receive */
-	for (i = 0; i < port->num_def_qps; i++) {
-		pr = &port->port_res[i];
-		ibmebus_free_irq(NULL, pr->recv_eq->attr.ist1, pr);
-		if (netif_msg_intr(port))
-			ehea_info("free recv irq for res %d with handle 0x%X",
-				  i, pr->recv_eq->attr.ist1);
+				  i, pr->eq->attr.ist1);
 	}
 
 	/* associated events */
@@ -1003,8 +1019,13 @@
 				      PXLY_RC_VLAN_FILTER)
 		     | EHEA_BMASK_SET(PXLY_RC_JUMBO_FRAME, 1);
 
-	for (i = 0; i < port->num_def_qps; i++)
-		cb0->default_qpn_arr[i] = port->port_res[0].qp->init_attr.qp_nr;
+	for (i = 0; i < port->num_mcs; i++)
+		if (use_mcs)
+			cb0->default_qpn_arr[i] =
+				port->port_res[i].qp->init_attr.qp_nr;
+		else
+			cb0->default_qpn_arr[i] =
+				port->port_res[0].qp->init_attr.qp_nr;
 
 	if (netif_msg_ifup(port))
 		ehea_dump(cb0, sizeof(*cb0), "ehea_configure_port");
@@ -1027,52 +1048,35 @@
 	return ret;
 }
 
-static int ehea_gen_smrs(struct ehea_port_res *pr)
+int ehea_gen_smrs(struct ehea_port_res *pr)
 {
-	u64 hret;
+	int ret;
 	struct ehea_adapter *adapter = pr->port->adapter;
 
-	hret = ehea_h_register_smr(adapter->handle, adapter->mr.handle,
-				   adapter->mr.vaddr, EHEA_MR_ACC_CTRL,
-				   adapter->pd, &pr->send_mr);
-	if (hret != H_SUCCESS)
+	ret = ehea_gen_smr(adapter, &adapter->mr, &pr->send_mr);
+	if (ret)
 		goto out;
 
-	hret = ehea_h_register_smr(adapter->handle, adapter->mr.handle,
-				   adapter->mr.vaddr, EHEA_MR_ACC_CTRL,
-				   adapter->pd, &pr->recv_mr);
-	if (hret != H_SUCCESS)
-		goto out_freeres;
+	ret = ehea_gen_smr(adapter, &adapter->mr, &pr->recv_mr);
+	if (ret)
+		goto out_free;
 
 	return 0;
 
-out_freeres:
-	hret = ehea_h_free_resource(adapter->handle, pr->send_mr.handle);
-	if (hret != H_SUCCESS)
-		ehea_error("failed freeing SMR");
+out_free:
+	ehea_rem_mr(&pr->send_mr);
 out:
+	ehea_error("Generating SMRS failed\n");
 	return -EIO;
 }
 
-static int ehea_rem_smrs(struct ehea_port_res *pr)
+int ehea_rem_smrs(struct ehea_port_res *pr)
 {
-	struct ehea_adapter *adapter = pr->port->adapter;
-	int ret = 0;
-	u64 hret;
-
-	hret = ehea_h_free_resource(adapter->handle, pr->send_mr.handle);
-	if (hret != H_SUCCESS) {
-		ret = -EIO;
-		ehea_error("failed freeing send SMR for pr=%p", pr);
-	}
-
-	hret = ehea_h_free_resource(adapter->handle, pr->recv_mr.handle);
-	if (hret != H_SUCCESS) {
-		ret = -EIO;
-		ehea_error("failed freeing recv SMR for pr=%p", pr);
-	}
-
-	return ret;
+	if ((ehea_rem_mr(&pr->send_mr))
+	    || (ehea_rem_mr(&pr->recv_mr)))
+		return -EIO;
+	else
+		return 0;
 }
 
 static int ehea_init_q_skba(struct ehea_q_skb_arr *q_skba, int max_q_entries)
@@ -1103,25 +1107,17 @@
 	memset(pr, 0, sizeof(struct ehea_port_res));
 
 	pr->port = port;
-	spin_lock_init(&pr->send_lock);
-	spin_lock_init(&pr->recv_lock);
 	spin_lock_init(&pr->xmit_lock);
 	spin_lock_init(&pr->netif_queue);
 
-	pr->recv_eq = ehea_create_eq(adapter, eq_type, EHEA_MAX_ENTRIES_EQ, 0);
-	if (!pr->recv_eq) {
-		ehea_error("create_eq failed (recv_eq)");
-		goto out_free;
-	}
-
-	pr->send_eq = ehea_create_eq(adapter, eq_type, EHEA_MAX_ENTRIES_EQ, 0);
-	if (!pr->send_eq) {
-		ehea_error("create_eq failed (send_eq)");
+	pr->eq = ehea_create_eq(adapter, eq_type, EHEA_MAX_ENTRIES_EQ, 0);
+	if (!pr->eq) {
+		ehea_error("create_eq failed (eq)");
 		goto out_free;
 	}
 
 	pr->recv_cq = ehea_create_cq(adapter, pr_cfg->max_entries_rcq,
-				     pr->recv_eq->fw_handle,
+				     pr->eq->fw_handle,
 				     port->logical_port_id);
 	if (!pr->recv_cq) {
 		ehea_error("create_cq failed (cq_recv)");
@@ -1129,7 +1125,7 @@
 	}
 
 	pr->send_cq = ehea_create_cq(adapter, pr_cfg->max_entries_scq,
-				     pr->send_eq->fw_handle,
+				     pr->eq->fw_handle,
 				     port->logical_port_id);
 	if (!pr->send_cq) {
 		ehea_error("create_cq failed (cq_send)");
@@ -1194,11 +1190,20 @@
 		ret = -EIO;
 		goto out_free;
 	}
-	tasklet_init(&pr->send_comp_task, ehea_send_irq_tasklet,
-		     (unsigned long)pr);
+
 	atomic_set(&pr->swqe_avail, init_attr->act_nr_send_wqes - 1);
 
 	kfree(init_attr);
+
+	pr->d_netdev = alloc_netdev(0, "", ether_setup);
+	if (!pr->d_netdev)
+		goto out_free;
+	pr->d_netdev->priv = pr;
+	pr->d_netdev->weight = 64;
+	pr->d_netdev->poll = ehea_poll;
+	set_bit(__LINK_STATE_START, &pr->d_netdev->state);
+	strcpy(pr->d_netdev->name, port->netdev->name);
+
 	ret = 0;
 	goto out;
 
@@ -1211,8 +1216,7 @@
 	ehea_destroy_qp(pr->qp);
 	ehea_destroy_cq(pr->send_cq);
 	ehea_destroy_cq(pr->recv_cq);
-	ehea_destroy_eq(pr->send_eq);
-	ehea_destroy_eq(pr->recv_eq);
+	ehea_destroy_eq(pr->eq);
 out:
 	return ret;
 }
@@ -1221,13 +1225,14 @@
 {
 	int ret, i;
 
+	free_netdev(pr->d_netdev);
+
 	ret = ehea_destroy_qp(pr->qp);
 
 	if (!ret) {
 		ehea_destroy_cq(pr->send_cq);
 		ehea_destroy_cq(pr->recv_cq);
-		ehea_destroy_eq(pr->send_eq);
-		ehea_destroy_eq(pr->recv_eq);
+		ehea_destroy_eq(pr->eq);
 
 		for (i = 0; i < pr->rq1_skba.len; i++)
 			if (pr->rq1_skba.arr[i])
@@ -1262,8 +1267,8 @@
 static inline void write_ip_start_end(struct ehea_swqe *swqe,
 				      const struct sk_buff *skb)
 {
-	swqe->ip_start = (u8)(((u64)skb->nh.iph) - ((u64)skb->data));
-	swqe->ip_end = (u8)(swqe->ip_start + skb->nh.iph->ihl * 4 - 1);
+	swqe->ip_start = skb_network_offset(skb);
+	swqe->ip_end = (u8)(swqe->ip_start + ip_hdrlen(skb) - 1);
 }
 
 static inline void write_tcp_offset_end(struct ehea_swqe *swqe,
@@ -1300,13 +1305,13 @@
 	/* copy only eth/ip/tcp headers to immediate data and
 	 * the rest of skb->data to sg1entry
 	 */
-	headersize = ETH_HLEN + (skb->nh.iph->ihl * 4) + (skb->h.th->doff * 4);
+	headersize = ETH_HLEN + ip_hdrlen(skb) + tcp_hdrlen(skb);
 
 	skb_data_size = skb->len - skb->data_len;
 
 	if (skb_data_size >= headersize) {
 		/* copy immediate data */
-		memcpy(imm_data, skb->data, headersize);
+		skb_copy_from_linear_data(skb, imm_data, headersize);
 		swqe->immediate_data_length = headersize;
 
 		if (skb_data_size > headersize) {
@@ -1337,7 +1342,7 @@
 	 */
 	if (skb_data_size >= SWQE2_MAX_IMM) {
 		/* copy immediate data */
-		memcpy(imm_data, skb->data, SWQE2_MAX_IMM);
+		skb_copy_from_linear_data(skb, imm_data, SWQE2_MAX_IMM);
 
 		swqe->immediate_data_length = SWQE2_MAX_IMM;
 
@@ -1350,7 +1355,7 @@
 			swqe->descriptors++;
 		}
 	} else {
-		memcpy(imm_data, skb->data, skb_data_size);
+		skb_copy_from_linear_data(skb, imm_data, skb_data_size);
 		swqe->immediate_data_length = skb_data_size;
 	}
 }
@@ -1688,6 +1693,7 @@
 		       struct ehea_swqe *swqe, u32 lkey)
 {
 	if (skb->protocol == htons(ETH_P_IP)) {
+		const struct iphdr *iph = ip_hdr(skb);
 		/* IPv4 */
 		swqe->tx_control |= EHEA_SWQE_CRC
 				 | EHEA_SWQE_IP_CHECKSUM
@@ -1697,15 +1703,15 @@
 
 		write_ip_start_end(swqe, skb);
 
-		if (skb->nh.iph->protocol == IPPROTO_UDP) {
-			if ((skb->nh.iph->frag_off & IP_MF) ||
-			    (skb->nh.iph->frag_off & IP_OFFSET))
+		if (iph->protocol == IPPROTO_UDP) {
+			if ((iph->frag_off & IP_MF) ||
+			    (iph->frag_off & IP_OFFSET))
 				/* IP fragment, so don't change cs */
 				swqe->tx_control &= ~EHEA_SWQE_TCP_CHECKSUM;
 			else
 				write_udp_offset_end(swqe, skb);
 
-		} else if (skb->nh.iph->protocol == IPPROTO_TCP) {
+		} else if (iph->protocol == IPPROTO_TCP) {
 			write_tcp_offset_end(swqe, skb);
 		}
 
@@ -1731,10 +1737,11 @@
 	int i;
 
 	if (skb->protocol == htons(ETH_P_IP)) {
+		const struct iphdr *iph = ip_hdr(skb);
 		/* IPv4 */
 		write_ip_start_end(swqe, skb);
 
-		if (skb->nh.iph->protocol == IPPROTO_TCP) {
+		if (iph->protocol == IPPROTO_TCP) {
 			swqe->tx_control |= EHEA_SWQE_CRC
 					 | EHEA_SWQE_IP_CHECKSUM
 					 | EHEA_SWQE_TCP_CHECKSUM
@@ -1742,9 +1749,9 @@
 
 			write_tcp_offset_end(swqe, skb);
 
-		} else if (skb->nh.iph->protocol == IPPROTO_UDP) {
-			if ((skb->nh.iph->frag_off & IP_MF) ||
-			    (skb->nh.iph->frag_off & IP_OFFSET))
+		} else if (iph->protocol == IPPROTO_UDP) {
+			if ((iph->frag_off & IP_MF) ||
+			    (iph->frag_off & IP_OFFSET))
 				/* IP fragment, so don't change cs */
 				swqe->tx_control |= EHEA_SWQE_CRC
 						 | EHEA_SWQE_IMM_DATA_PRESENT;
@@ -1770,10 +1777,11 @@
 	/* copy (immediate) data */
 	if (nfrags == 0) {
 		/* data is in a single piece */
-		memcpy(imm_data, skb->data, skb->len);
+		skb_copy_from_linear_data(skb, imm_data, skb->len);
 	} else {
 		/* first copy data from the skb->data buffer ... */
-		memcpy(imm_data, skb->data, skb->len - skb->data_len);
+		skb_copy_from_linear_data(skb, imm_data,
+					  skb->len - skb->data_len);
 		imm_data += skb->len - skb->data_len;
 
 		/* ... then copy data from the fragments */
@@ -1789,6 +1797,22 @@
 	dev_kfree_skb(skb);
 }
 
+static inline int ehea_hash_skb(struct sk_buff *skb, int num_qps)
+{
+	struct tcphdr *tcp;
+	u32 tmp;
+
+	if ((skb->protocol == htons(ETH_P_IP)) &&
+	    (skb->nh.iph->protocol == IPPROTO_TCP)) {
+		tcp = (struct tcphdr*)(skb->nh.raw + (skb->nh.iph->ihl * 4));
+		tmp = (tcp->source + (tcp->dest << 16)) % 31;
+		tmp += skb->nh.iph->daddr % 31;
+		return tmp % num_qps;
+	}
+	else
+		return 0;
+}
+
 static int ehea_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct ehea_port *port = netdev_priv(dev);
@@ -1796,9 +1820,17 @@
 	unsigned long flags;
 	u32 lkey;
 	int swqe_index;
-	struct ehea_port_res *pr = &port->port_res[0];
+	struct ehea_port_res *pr;
 
-	spin_lock(&pr->xmit_lock);
+	pr = &port->port_res[ehea_hash_skb(skb, port->num_tx_qps)];
+
+	if (!spin_trylock(&pr->xmit_lock))
+		return NETDEV_TX_BUSY;
+
+	if (pr->queue_stopped) {
+		spin_unlock(&pr->xmit_lock);
+		return NETDEV_TX_BUSY;
+	}
 
 	swqe = ehea_get_swqe(pr->qp, &swqe_index);
 	memset(swqe, 0, SWQE_HEADER_SIZE);
@@ -1821,6 +1853,7 @@
 		swqe->wr_id =
 			EHEA_BMASK_SET(EHEA_WR_ID_TYPE, EHEA_SWQE2_TYPE)
 		      | EHEA_BMASK_SET(EHEA_WR_ID_COUNT, pr->swqe_id_counter)
+		      | EHEA_BMASK_SET(EHEA_WR_ID_REFILL, 1)
 		      | EHEA_BMASK_SET(EHEA_WR_ID_INDEX, pr->sq_skba.index);
 		pr->sq_skba.arr[pr->sq_skba.index] = skb;
 
@@ -1829,14 +1862,7 @@
 
 		lkey = pr->send_mr.lkey;
 		ehea_xmit2(skb, dev, swqe, lkey);
-
-		if (pr->swqe_count >= (EHEA_SIG_IV_LONG - 1)) {
-			swqe->wr_id |= EHEA_BMASK_SET(EHEA_WR_ID_REFILL,
-						      EHEA_SIG_IV_LONG);
-			swqe->tx_control |= EHEA_SWQE_SIGNALLED_COMPLETION;
-			pr->swqe_count = 0;
-		} else
-			pr->swqe_count += 1;
+		swqe->tx_control |= EHEA_SWQE_SIGNALLED_COMPLETION;
 	}
 	pr->swqe_id_counter += 1;
 
@@ -1856,6 +1882,7 @@
 	if (unlikely(atomic_read(&pr->swqe_avail) <= 1)) {
 		spin_lock_irqsave(&pr->netif_queue, flags);
 		if (unlikely(atomic_read(&pr->swqe_avail) <= 1)) {
+			pr->p_stats.queue_stopped++;
 			netif_stop_queue(dev);
 			pr->queue_stopped = 1;
 		}
@@ -2057,7 +2084,7 @@
 	}
 
 	pr_cfg.max_entries_rcq = rq1_entries + rq2_entries + rq3_entries;
-	pr_cfg.max_entries_scq = sq_entries;
+	pr_cfg.max_entries_scq = sq_entries * 2;
 	pr_cfg.max_entries_sq = sq_entries;
 	pr_cfg.max_entries_rq1 = rq1_entries;
 	pr_cfg.max_entries_rq2 = rq2_entries;
@@ -2106,6 +2133,28 @@
 	return ret;
 }
 
+static void ehea_remove_adapter_mr (struct ehea_adapter *adapter)
+{
+	int i;
+
+	for (i=0; i < EHEA_MAX_PORTS; i++)
+		if (adapter->port[i])
+			return;
+
+	ehea_rem_mr(&adapter->mr);
+}
+
+static int ehea_add_adapter_mr (struct ehea_adapter *adapter)
+{
+	int i;
+
+	for (i=0; i < EHEA_MAX_PORTS; i++)
+		if (adapter->port[i])
+			return 0;
+
+	return ehea_reg_kernel_mr(adapter, &adapter->mr);
+}
+
 static int ehea_up(struct net_device *dev)
 {
 	int ret, i;
@@ -2205,8 +2254,10 @@
 	ehea_drop_multicast_list(dev);
 	ehea_free_interrupts(dev);
 
-	for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++)
-		tasklet_kill(&port->port_res[i].send_comp_task);
+	for (i = 0; i < port->num_def_qps; i++)
+		while (test_bit(__LINK_STATE_RX_SCHED,
+				&port->port_res[i].d_netdev->state))
+			msleep(1);
 
 	ehea_broadcast_reg_helper(port, H_DEREG_BCMC);
 	ret = ehea_clean_all_portres(port);
@@ -2273,8 +2324,6 @@
 int ehea_sense_adapter_attr(struct ehea_adapter *adapter)
 {
 	struct hcp_query_ehea *cb;
-	struct device_node *lhea_dn = NULL;
-	struct device_node *eth_dn = NULL;
 	u64 hret;
 	int ret;
 
@@ -2291,18 +2340,6 @@
 		goto out_herr;
 	}
 
-	/* Determine the number of available logical ports
-	 * by counting the child nodes of the lhea OFDT entry
-	 */
-	adapter->num_ports = 0;
-	lhea_dn = of_find_node_by_name(lhea_dn, "lhea");
-	do {
-		eth_dn = of_get_next_child(lhea_dn, eth_dn);
-		if (eth_dn)
-			adapter->num_ports++;
-	} while ( eth_dn );
-	of_node_put(lhea_dn);
-
 	adapter->max_mc_mac = cb->max_mc_mac - 1;
 	ret = 0;
 
@@ -2312,79 +2349,188 @@
 	return ret;
 }
 
-static int ehea_setup_single_port(struct ehea_port *port,
-				  struct device_node *dn)
+int ehea_get_jumboframe_status(struct ehea_port *port, int *jumbo)
+{
+	struct hcp_ehea_port_cb4 *cb4;
+	u64 hret;
+	int ret = 0;
+
+	*jumbo = 0;
+
+	/* (Try to) enable *jumbo frames */
+	cb4 = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!cb4) {
+		ehea_error("no mem for cb4");
+		ret = -ENOMEM;
+		goto out;
+	} else {
+		hret = ehea_h_query_ehea_port(port->adapter->handle,
+					      port->logical_port_id,
+					      H_PORT_CB4,
+					      H_PORT_CB4_JUMBO, cb4);
+		if (hret == H_SUCCESS) {
+			if (cb4->jumbo_frame)
+				*jumbo = 1;
+			else {
+				cb4->jumbo_frame = 1;
+				hret = ehea_h_modify_ehea_port(port->adapter->
+							       handle,
+							       port->
+							       logical_port_id,
+							       H_PORT_CB4,
+							       H_PORT_CB4_JUMBO,
+							       cb4);
+				if (hret == H_SUCCESS)
+					*jumbo = 1;
+			}
+		} else
+			ret = -EINVAL;
+
+		kfree(cb4);
+	}
+out:
+	return ret;
+}
+
+static ssize_t ehea_show_port_id(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct ehea_port *port = container_of(dev, struct ehea_port, ofdev.dev);
+	return sprintf(buf, "0x%X", port->logical_port_id);
+}
+
+static DEVICE_ATTR(log_port_id, S_IRUSR | S_IRGRP | S_IROTH, ehea_show_port_id,
+		   NULL);
+
+static void __devinit logical_port_release(struct device *dev)
+{
+	struct ehea_port *port = container_of(dev, struct ehea_port, ofdev.dev);
+	of_node_put(port->ofdev.node);
+}
+
+static int ehea_driver_sysfs_add(struct device *dev,
+                                 struct device_driver *driver)
 {
 	int ret;
-	u64 hret;
-	struct net_device *dev = port->netdev;
-	struct ehea_adapter *adapter = port->adapter;
-	struct hcp_ehea_port_cb4 *cb4;
-	u32 *dn_log_port_id;
-	int jumbo = 0;
+
+	ret = sysfs_create_link(&driver->kobj, &dev->kobj,
+				kobject_name(&dev->kobj));
+	if (ret == 0) {
+		ret = sysfs_create_link(&dev->kobj, &driver->kobj,
+					"driver");
+		if (ret)
+			sysfs_remove_link(&driver->kobj,
+					  kobject_name(&dev->kobj));
+	}
+	return ret;
+}
+
+static void ehea_driver_sysfs_remove(struct device *dev,
+                                     struct device_driver *driver)
+{
+	struct device_driver *drv = driver;
+
+	if (drv) {
+		sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj));
+		sysfs_remove_link(&dev->kobj, "driver");
+	}
+}
+
+static struct device *ehea_register_port(struct ehea_port *port,
+					 struct device_node *dn)
+{
+	int ret;
+
+	port->ofdev.node = of_node_get(dn);
+	port->ofdev.dev.parent = &port->adapter->ebus_dev->ofdev.dev;
+	port->ofdev.dev.bus = &ibmebus_bus_type;
+
+	sprintf(port->ofdev.dev.bus_id, "port%d", port_name_cnt++);
+	port->ofdev.dev.release = logical_port_release;
+
+	ret = of_device_register(&port->ofdev);
+	if (ret) {
+		ehea_error("failed to register device. ret=%d", ret);
+		goto out;
+	}
+
+	ret = device_create_file(&port->ofdev.dev, &dev_attr_log_port_id);
+        if (ret) {
+		ehea_error("failed to register attributes, ret=%d", ret);
+		goto out_unreg_of_dev;
+	}
+
+	ret = ehea_driver_sysfs_add(&port->ofdev.dev, &ehea_driver.driver);
+	if (ret) {
+		ehea_error("failed to register sysfs driver link");
+		goto out_rem_dev_file;
+	}
+
+	return &port->ofdev.dev;
+
+out_rem_dev_file:
+	device_remove_file(&port->ofdev.dev, &dev_attr_log_port_id);
+out_unreg_of_dev:
+	of_device_unregister(&port->ofdev);
+out:
+	return NULL;
+}
+
+static void ehea_unregister_port(struct ehea_port *port)
+{
+	ehea_driver_sysfs_remove(&port->ofdev.dev, &ehea_driver.driver);
+	device_remove_file(&port->ofdev.dev, &dev_attr_log_port_id);
+	of_device_unregister(&port->ofdev);
+}
+
+struct ehea_port *ehea_setup_single_port(struct ehea_adapter *adapter,
+					 u32 logical_port_id,
+					 struct device_node *dn)
+{
+	int ret;
+	struct net_device *dev;
+	struct ehea_port *port;
+	struct device *port_dev;
+	int jumbo;
+
+	/* allocate memory for the port structures */
+	dev = alloc_etherdev(sizeof(struct ehea_port));
+
+	if (!dev) {
+		ehea_error("no mem for net_device");
+		ret = -ENOMEM;
+		goto out_err;
+	}
+
+	port = netdev_priv(dev);
 
 	sema_init(&port->port_lock, 1);
 	port->state = EHEA_PORT_DOWN;
 	port->sig_comp_iv = sq_entries / 10;
 
-	if (!dn) {
-		ehea_error("bad device node: dn=%p", dn);
-		ret = -EINVAL;
-		goto out;
-	}
+	port->adapter = adapter;
+	port->netdev = dev;
+	port->logical_port_id = logical_port_id;
 
-	port->of_dev_node = dn;
-
-	/* Determine logical port id */
-	dn_log_port_id = (u32*)get_property(dn, "ibm,hea-port-no", NULL);
-
-	if (!dn_log_port_id) {
-		ehea_error("bad device node: dn_log_port_id=%p",
-			   dn_log_port_id);
-		ret = -EINVAL;
-		goto out;
-	}
-	port->logical_port_id = *dn_log_port_id;
+	port->msg_enable = netif_msg_init(msg_level, EHEA_MSG_DEFAULT);
 
 	port->mc_list = kzalloc(sizeof(struct ehea_mc_list), GFP_KERNEL);
 	if (!port->mc_list) {
 		ret = -ENOMEM;
-		goto out;
+		goto out_free_ethdev;
 	}
 
 	INIT_LIST_HEAD(&port->mc_list->list);
 
 	ret = ehea_sense_port_attr(port);
 	if (ret)
-		goto out;
+		goto out_free_mc_list;
 
-	/* Enable Jumbo frames */
-	cb4 = kzalloc(PAGE_SIZE, GFP_KERNEL);
-	if (!cb4) {
-		ehea_error("no mem for cb4");
-	} else {
-		hret = ehea_h_query_ehea_port(adapter->handle,
-					      port->logical_port_id,
-					      H_PORT_CB4,
-					      H_PORT_CB4_JUMBO, cb4);
+	port_dev = ehea_register_port(port, dn);
+	if (!port_dev)
+		goto out_free_mc_list;
 
-		if (hret == H_SUCCESS) {
-			if (cb4->jumbo_frame)
-				jumbo = 1;
-			else {
-				cb4->jumbo_frame = 1;
-				hret = ehea_h_modify_ehea_port(adapter->handle,
-							       port->
-							        logical_port_id,
-							       H_PORT_CB4,
-							       H_PORT_CB4_JUMBO,
-							       cb4);
-				if (hret == H_SUCCESS)
-					jumbo = 1;
-			}
-		}
-		kfree(cb4);
-	}
+	SET_NETDEV_DEV(dev, port_dev);
 
 	/* initialize net_device structure */
 	SET_MODULE_OWNER(dev);
@@ -2417,84 +2563,225 @@
 	ret = register_netdev(dev);
 	if (ret) {
 		ehea_error("register_netdev failed. ret=%d", ret);
-		goto out_free;
+		goto out_unreg_port;
 	}
 
+	ret = ehea_get_jumboframe_status(port, &jumbo);
+	if (ret)
+		ehea_error("failed determining jumbo frame status for %s",
+			   port->netdev->name);
+
 	ehea_info("%s: Jumbo frames are %sabled", dev->name,
 		  jumbo == 1 ? "en" : "dis");
 
-	port->netdev = dev;
-	ret = 0;
-	goto out;
+	return port;
 
-out_free:
+out_unreg_port:
+	ehea_unregister_port(port);
+
+out_free_mc_list:
 	kfree(port->mc_list);
-out:
-	return ret;
+
+out_free_ethdev:
+	free_netdev(dev);
+
+out_err:
+	ehea_error("setting up logical port with id=%d failed, ret=%d",
+		   logical_port_id, ret);
+	return NULL;
+}
+
+static void ehea_shutdown_single_port(struct ehea_port *port)
+{
+	unregister_netdev(port->netdev);
+	ehea_unregister_port(port);
+	kfree(port->mc_list);
+	free_netdev(port->netdev);
 }
 
 static int ehea_setup_ports(struct ehea_adapter *adapter)
 {
-	int ret;
-	int port_setup_ok = 0;
+	struct device_node *lhea_dn;
+	struct device_node *eth_dn = NULL;
+
+	u32 *dn_log_port_id;
+	int i = 0;
+
+	lhea_dn = adapter->ebus_dev->ofdev.node;
+	while ((eth_dn = of_get_next_child(lhea_dn, eth_dn))) {
+
+		dn_log_port_id = (u32*)get_property(eth_dn, "ibm,hea-port-no",
+						    NULL);
+		if (!dn_log_port_id) {
+			ehea_error("bad device node: eth_dn name=%s",
+				   eth_dn->full_name);
+			continue;
+		}
+
+		if (ehea_add_adapter_mr(adapter)) {
+			ehea_error("creating MR failed");
+			of_node_put(eth_dn);
+			return -EIO;
+		}
+
+		adapter->port[i] = ehea_setup_single_port(adapter,
+							  *dn_log_port_id,
+							  eth_dn);
+		if (adapter->port[i])
+			ehea_info("%s -> logical port id #%d",
+				  adapter->port[i]->netdev->name,
+				  *dn_log_port_id);
+		else
+			ehea_remove_adapter_mr(adapter);
+
+		i++;
+	};
+
+	return 0;
+}
+
+static struct device_node *ehea_get_eth_dn(struct ehea_adapter *adapter,
+					   u32 logical_port_id)
+{
+	struct device_node *lhea_dn;
+	struct device_node *eth_dn = NULL;
+	u32 *dn_log_port_id;
+
+	lhea_dn = adapter->ebus_dev->ofdev.node;
+	while ((eth_dn = of_get_next_child(lhea_dn, eth_dn))) {
+
+		dn_log_port_id = (u32*)get_property(eth_dn, "ibm,hea-port-no",
+						    NULL);
+		if (dn_log_port_id)
+			if (*dn_log_port_id == logical_port_id)
+				return eth_dn;
+	};
+
+	return NULL;
+}
+
+static ssize_t ehea_probe_port(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct ehea_adapter *adapter = dev->driver_data;
 	struct ehea_port *port;
-	struct device_node *dn = NULL;
-	struct net_device *dev;
+	struct device_node *eth_dn = NULL;
 	int i;
 
-	/* get port properties for all ports */
-	for (i = 0; i < adapter->num_ports; i++) {
+	u32 logical_port_id;
 
-		if (adapter->port[i])
-			continue;	/* port already up and running */
+	sscanf(buf, "%X", &logical_port_id);
 
-		/* allocate memory for the port structures */
-		dev = alloc_etherdev(sizeof(struct ehea_port));
+	port = ehea_get_port(adapter, logical_port_id);
 
-		if (!dev) {
-			ehea_error("no mem for net_device");
-			break;
-		}
-
-		port = netdev_priv(dev);
-		port->adapter = adapter;
-		port->netdev = dev;
-		adapter->port[i] = port;
-		port->msg_enable = netif_msg_init(msg_level, EHEA_MSG_DEFAULT);
-
-		dn = of_find_node_by_name(dn, "ethernet");
-		ret = ehea_setup_single_port(port, dn);
-		if (ret) {
-			/* Free mem for this port struct. The others will be
-			   processed on rollback */
-			free_netdev(dev);
-			adapter->port[i] = NULL;
-			ehea_error("eHEA port %d setup failed, ret=%d", i, ret);
-		}
+	if (port) {
+		ehea_info("adding port with logical port id=%d failed. port "
+			  "already configured as %s.", logical_port_id,
+			  port->netdev->name);
+		return -EINVAL;
 	}
 
-	of_node_put(dn);
+	eth_dn = ehea_get_eth_dn(adapter, logical_port_id);
 
-	/* Check for succesfully set up ports */
-	for (i = 0; i < adapter->num_ports; i++)
-		if (adapter->port[i])
-			port_setup_ok++;
+	if (!eth_dn) {
+		ehea_info("no logical port with id %d found", logical_port_id);
+		return -EINVAL;
+	}
 
-	if (port_setup_ok)
-		ret = 0;	/* At least some ports are setup correctly */
-	else
-		ret = -EINVAL;
+	if (ehea_add_adapter_mr(adapter)) {
+		ehea_error("creating MR failed");
+		return -EIO;
+	}
 
+	port = ehea_setup_single_port(adapter, logical_port_id, eth_dn);
+
+	of_node_put(eth_dn);
+
+	if (port) {
+		for (i=0; i < EHEA_MAX_PORTS; i++)
+			if (!adapter->port[i]) {
+				adapter->port[i] = port;
+				break;
+			}
+
+		ehea_info("added %s (logical port id=%d)", port->netdev->name,
+			  logical_port_id);
+	} else {
+		ehea_remove_adapter_mr(adapter);
+		return -EIO;
+	}
+
+	return (ssize_t) count;
+}
+
+static ssize_t ehea_remove_port(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct ehea_adapter *adapter = dev->driver_data;
+	struct ehea_port *port;
+	int i;
+	u32 logical_port_id;
+
+	sscanf(buf, "%X", &logical_port_id);
+
+	port = ehea_get_port(adapter, logical_port_id);
+
+	if (port) {
+		ehea_info("removed %s (logical port id=%d)", port->netdev->name,
+			  logical_port_id);
+
+		ehea_shutdown_single_port(port);
+
+		for (i=0; i < EHEA_MAX_PORTS; i++)
+			if (adapter->port[i] == port) {
+				adapter->port[i] = NULL;
+				break;
+			}
+	} else {
+		ehea_error("removing port with logical port id=%d failed. port "
+			   "not configured.", logical_port_id);
+		return -EINVAL;
+	}
+
+	ehea_remove_adapter_mr(adapter);
+
+	return (ssize_t) count;
+}
+
+static DEVICE_ATTR(probe_port, S_IWUSR, NULL, ehea_probe_port);
+static DEVICE_ATTR(remove_port, S_IWUSR, NULL, ehea_remove_port);
+
+int ehea_create_device_sysfs(struct ibmebus_dev *dev)
+{
+	int ret = device_create_file(&dev->ofdev.dev, &dev_attr_probe_port);
+	if (ret)
+		goto out;
+
+	ret = device_create_file(&dev->ofdev.dev, &dev_attr_remove_port);
+out:
 	return ret;
 }
 
-static int __devinit ehea_probe(struct ibmebus_dev *dev,
-				const struct of_device_id *id)
+void ehea_remove_device_sysfs(struct ibmebus_dev *dev)
+{
+	device_remove_file(&dev->ofdev.dev, &dev_attr_probe_port);
+	device_remove_file(&dev->ofdev.dev, &dev_attr_remove_port);
+}
+
+static int __devinit ehea_probe_adapter(struct ibmebus_dev *dev,
+					const struct of_device_id *id)
 {
 	struct ehea_adapter *adapter;
 	u64 *adapter_handle;
 	int ret;
 
+	if (!dev || !dev->ofdev.node) {
+		ehea_error("Invalid ibmebus device probed");
+		return -EINVAL;
+	}
+
 	adapter = kzalloc(sizeof(*adapter), GFP_KERNEL);
 	if (!adapter) {
 		ret = -ENOMEM;
@@ -2502,6 +2789,8 @@
 		goto out;
 	}
 
+	adapter->ebus_dev = dev;
+
 	adapter_handle = (u64*)get_property(dev->ofdev.node, "ibm,hea-handle",
 					    NULL);
 	if (adapter_handle)
@@ -2518,26 +2807,21 @@
 
 	dev->ofdev.dev.driver_data = adapter;
 
-	ret = ehea_reg_mr_adapter(adapter);
-	if (ret) {
-		dev_err(&dev->ofdev.dev, "reg_mr_adapter failed\n");
-		goto out_free_ad;
-	}
 
 	/* initialize adapter and ports */
 	/* get adapter properties */
 	ret = ehea_sense_adapter_attr(adapter);
 	if (ret) {
 		dev_err(&dev->ofdev.dev, "sense_adapter_attr failed: %d", ret);
-		goto out_free_res;
+		goto out_free_ad;
 	}
-	dev_info(&dev->ofdev.dev, "%d eHEA ports found\n", adapter->num_ports);
 
 	adapter->neq = ehea_create_eq(adapter,
 				      EHEA_NEQ, EHEA_MAX_ENTRIES_EQ, 1);
 	if (!adapter->neq) {
+		ret = -EIO;
 		dev_err(&dev->ofdev.dev, "NEQ creation failed");
-		goto out_free_res;
+		goto out_free_ad;
 	}
 
 	tasklet_init(&adapter->neq_tasklet, ehea_neq_tasklet,
@@ -2552,18 +2836,27 @@
 	}
 
 	adapter->ehea_wq = create_workqueue("ehea_wq");
-	if (!adapter->ehea_wq)
+	if (!adapter->ehea_wq) {
+		ret = -EIO;
 		goto out_free_irq;
+	}
+
+	ret = ehea_create_device_sysfs(dev);
+	if (ret)
+		goto out_kill_wq;
 
 	ret = ehea_setup_ports(adapter);
 	if (ret) {
 		dev_err(&dev->ofdev.dev, "setup_ports failed");
-		goto out_kill_wq;
+		goto out_rem_dev_sysfs;
 	}
 
 	ret = 0;
 	goto out;
 
+out_rem_dev_sysfs:
+	ehea_remove_device_sysfs(dev);
+
 out_kill_wq:
 	destroy_workqueue(adapter->ehea_wq);
 
@@ -2573,45 +2866,32 @@
 out_kill_eq:
 	ehea_destroy_eq(adapter->neq);
 
-out_free_res:
-	ehea_h_free_resource(adapter->handle, adapter->mr.handle);
-
 out_free_ad:
 	kfree(adapter);
 out:
 	return ret;
 }
 
-static void ehea_shutdown_single_port(struct ehea_port *port)
-{
-	unregister_netdev(port->netdev);
-	kfree(port->mc_list);
-	free_netdev(port->netdev);
-}
-
 static int __devexit ehea_remove(struct ibmebus_dev *dev)
 {
 	struct ehea_adapter *adapter = dev->ofdev.dev.driver_data;
-	u64 hret;
 	int i;
 
-	for (i = 0; i < adapter->num_ports; i++)
+	for (i = 0; i < EHEA_MAX_PORTS; i++)
 		if (adapter->port[i]) {
 			ehea_shutdown_single_port(adapter->port[i]);
 			adapter->port[i] = NULL;
 		}
+
+	ehea_remove_device_sysfs(dev);
+
 	destroy_workqueue(adapter->ehea_wq);
 
 	ibmebus_free_irq(NULL, adapter->neq->attr.ist1, adapter);
 	tasklet_kill(&adapter->neq_tasklet);
 
 	ehea_destroy_eq(adapter->neq);
-
-	hret = ehea_h_free_resource(adapter->handle, adapter->mr.handle);
-	if (hret) {
-		dev_err(&dev->ofdev.dev, "free_resource_mr failed");
-		return -EIO;
-	}
+	ehea_remove_adapter_mr(adapter);
 	kfree(adapter);
 	return 0;
 }
@@ -2644,21 +2924,6 @@
 	return ret;
 }
 
-static struct of_device_id ehea_device_table[] = {
-	{
-		.name = "lhea",
-		.compatible = "IBM,lhea",
-	},
-	{},
-};
-
-static struct ibmebus_driver ehea_driver = {
-	.name = "ehea",
-	.id_table = ehea_device_table,
-	.probe = ehea_probe,
-	.remove = ehea_remove,
-};
-
 int __init ehea_module_init(void)
 {
 	int ret;
diff --git a/drivers/net/ehea/ehea_phyp.c b/drivers/net/ehea/ehea_phyp.c
index bc3c005..95c4a7f 100644
--- a/drivers/net/ehea/ehea_phyp.c
+++ b/drivers/net/ehea/ehea_phyp.c
@@ -478,12 +478,14 @@
 				 0, 0, 0, 0, 0, 0);             /* R7-R12 */
 }
 
-u64 ehea_h_free_resource(const u64 adapter_handle, const u64 res_handle)
+u64 ehea_h_free_resource(const u64 adapter_handle, const u64 res_handle,
+			 u64 force_bit)
 {
 	return ehea_plpar_hcall_norets(H_FREE_RESOURCE,
 				       adapter_handle,	   /* R4 */
 				       res_handle,         /* R5 */
-				       0, 0, 0, 0, 0);     /* R6-R10 */
+				       force_bit,
+				       0, 0, 0, 0);        /* R7-R10 */
 }
 
 u64 ehea_h_alloc_resource_mr(const u64 adapter_handle, const u64 vaddr,
diff --git a/drivers/net/ehea/ehea_phyp.h b/drivers/net/ehea/ehea_phyp.h
index 90acddb0..d17a45a 100644
--- a/drivers/net/ehea/ehea_phyp.h
+++ b/drivers/net/ehea/ehea_phyp.h
@@ -414,7 +414,11 @@
 
 u64 ehea_h_disable_and_get_hea(const u64 adapter_handle, const u64 qp_handle);
 
-u64 ehea_h_free_resource(const u64 adapter_handle, const u64 res_handle);
+#define FORCE_FREE 1
+#define NORMAL_FREE 0
+
+u64 ehea_h_free_resource(const u64 adapter_handle, const u64 res_handle,
+			 u64 force_bit);
 
 u64 ehea_h_alloc_resource_mr(const u64 adapter_handle, const u64 vaddr,
 			     const u64 length, const u32 access_ctrl,
diff --git a/drivers/net/ehea/ehea_qmr.c b/drivers/net/ehea/ehea_qmr.c
index 96ff3b6..f24a886 100644
--- a/drivers/net/ehea/ehea_qmr.c
+++ b/drivers/net/ehea/ehea_qmr.c
@@ -197,7 +197,7 @@
 	hw_queue_dtor(&cq->hw_queue);
 
 out_freeres:
-	ehea_h_free_resource(adapter->handle, cq->fw_handle);
+	ehea_h_free_resource(adapter->handle, cq->fw_handle, FORCE_FREE);
 
 out_freemem:
 	kfree(cq);
@@ -206,25 +206,38 @@
 	return NULL;
 }
 
+u64 ehea_destroy_cq_res(struct ehea_cq *cq, u64 force)
+{
+	u64 hret;
+	u64 adapter_handle = cq->adapter->handle;
+
+        /* deregister all previous registered pages */
+	hret = ehea_h_free_resource(adapter_handle, cq->fw_handle, force);
+	if (hret != H_SUCCESS)
+		return hret;
+
+	hw_queue_dtor(&cq->hw_queue);
+	kfree(cq);
+
+	return hret;
+}
+
 int ehea_destroy_cq(struct ehea_cq *cq)
 {
-	u64 adapter_handle, hret;
-
+	u64 hret;
 	if (!cq)
 		return 0;
 
-	adapter_handle = cq->adapter->handle;
+	if ((hret = ehea_destroy_cq_res(cq, NORMAL_FREE)) == H_R_STATE) {
+		ehea_error_data(cq->adapter, cq->fw_handle);
+		hret = ehea_destroy_cq_res(cq, FORCE_FREE);
+	}
 
-	/* deregister all previous registered pages */
-	hret = ehea_h_free_resource(adapter_handle, cq->fw_handle);
 	if (hret != H_SUCCESS) {
 		ehea_error("destroy CQ failed");
 		return -EIO;
 	}
 
-	hw_queue_dtor(&cq->hw_queue);
-	kfree(cq);
-
 	return 0;
 }
 
@@ -297,7 +310,7 @@
 	hw_queue_dtor(&eq->hw_queue);
 
 out_freeres:
-	ehea_h_free_resource(adapter->handle, eq->fw_handle);
+	ehea_h_free_resource(adapter->handle, eq->fw_handle, FORCE_FREE);
 
 out_freemem:
 	kfree(eq);
@@ -316,27 +329,41 @@
 	return eqe;
 }
 
-int ehea_destroy_eq(struct ehea_eq *eq)
+u64 ehea_destroy_eq_res(struct ehea_eq *eq, u64 force)
 {
 	u64 hret;
 	unsigned long flags;
 
-	if (!eq)
-		return 0;
-
 	spin_lock_irqsave(&eq->spinlock, flags);
 
-	hret = ehea_h_free_resource(eq->adapter->handle, eq->fw_handle);
+	hret = ehea_h_free_resource(eq->adapter->handle, eq->fw_handle, force);
 	spin_unlock_irqrestore(&eq->spinlock, flags);
 
-	if (hret != H_SUCCESS) {
-		ehea_error("destroy_eq failed");
-		return -EIO;
-	}
+	if (hret != H_SUCCESS)
+		return hret;
 
 	hw_queue_dtor(&eq->hw_queue);
 	kfree(eq);
 
+	return hret;
+}
+
+int ehea_destroy_eq(struct ehea_eq *eq)
+{
+	u64 hret;
+	if (!eq)
+		return 0;
+
+	if ((hret = ehea_destroy_eq_res(eq, NORMAL_FREE)) == H_R_STATE) {
+		ehea_error_data(eq->adapter, eq->fw_handle);
+		hret = ehea_destroy_eq_res(eq, FORCE_FREE);
+	}
+
+	if (hret != H_SUCCESS) {
+		ehea_error("destroy EQ failed");
+		return -EIO;
+        }
+
 	return 0;
 }
 
@@ -471,41 +498,56 @@
 
 out_freeres:
 	ehea_h_disable_and_get_hea(adapter->handle, qp->fw_handle);
-	ehea_h_free_resource(adapter->handle, qp->fw_handle);
+	ehea_h_free_resource(adapter->handle, qp->fw_handle, FORCE_FREE);
 
 out_freemem:
 	kfree(qp);
 	return NULL;
 }
 
-int ehea_destroy_qp(struct ehea_qp *qp)
+u64 ehea_destroy_qp_res(struct ehea_qp *qp, u64 force)
 {
-	u64 hret;
-	struct ehea_qp_init_attr *qp_attr = &qp->init_attr;
+        u64 hret;
+        struct ehea_qp_init_attr *qp_attr = &qp->init_attr;
 
-	if (!qp)
-		return 0;
 
-	ehea_h_disable_and_get_hea(qp->adapter->handle, qp->fw_handle);
-	hret = ehea_h_free_resource(qp->adapter->handle, qp->fw_handle);
-	if (hret != H_SUCCESS) {
-		ehea_error("destroy_qp failed");
-		return -EIO;
-	}
+        ehea_h_disable_and_get_hea(qp->adapter->handle, qp->fw_handle);
+        hret = ehea_h_free_resource(qp->adapter->handle, qp->fw_handle, force);
+        if (hret != H_SUCCESS)
+                return hret;
 
-	hw_queue_dtor(&qp->hw_squeue);
-	hw_queue_dtor(&qp->hw_rqueue1);
+        hw_queue_dtor(&qp->hw_squeue);
+        hw_queue_dtor(&qp->hw_rqueue1);
 
-   	if (qp_attr->rq_count > 1)
-		hw_queue_dtor(&qp->hw_rqueue2);
-   	if (qp_attr->rq_count > 2)
-		hw_queue_dtor(&qp->hw_rqueue3);
-	kfree(qp);
+        if (qp_attr->rq_count > 1)
+                hw_queue_dtor(&qp->hw_rqueue2);
+        if (qp_attr->rq_count > 2)
+                hw_queue_dtor(&qp->hw_rqueue3);
+        kfree(qp);
 
-	return 0;
+        return hret;
 }
 
-int ehea_reg_mr_adapter(struct ehea_adapter *adapter)
+int ehea_destroy_qp(struct ehea_qp *qp)
+{
+        u64 hret;
+        if (!qp)
+                return 0;
+
+        if ((hret = ehea_destroy_qp_res(qp, NORMAL_FREE)) == H_R_STATE) {
+                ehea_error_data(qp->adapter, qp->fw_handle);
+                hret = ehea_destroy_qp_res(qp, FORCE_FREE);
+        }
+
+        if (hret != H_SUCCESS) {
+                ehea_error("destroy QP failed");
+                return -EIO;
+        }
+
+        return 0;
+}
+
+int ehea_reg_kernel_mr(struct ehea_adapter *adapter, struct ehea_mr *mr)
 {
 	int i, k, ret;
 	u64 hret, pt_abs, start, end, nr_pages;
@@ -526,14 +568,14 @@
 
 	hret = ehea_h_alloc_resource_mr(adapter->handle, start, end - start,
 					acc_ctrl, adapter->pd,
-					&adapter->mr.handle, &adapter->mr.lkey);
+					&mr->handle, &mr->lkey);
 	if (hret != H_SUCCESS) {
 		ehea_error("alloc_resource_mr failed");
 		ret = -EIO;
 		goto out;
 	}
 
-	adapter->mr.vaddr = KERNELBASE;
+	mr->vaddr = KERNELBASE;
 	k = 0;
 
 	while (nr_pages > 0) {
@@ -545,7 +587,7 @@
 							     EHEA_PAGESIZE)));
 
 			hret = ehea_h_register_rpage_mr(adapter->handle,
-							adapter->mr.handle, 0,
+							mr->handle, 0,
 							0, (u64)pt_abs,
 							num_pages);
 			nr_pages -= num_pages;
@@ -554,34 +596,68 @@
 							  (k * EHEA_PAGESIZE)));
 
 			hret = ehea_h_register_rpage_mr(adapter->handle,
-							adapter->mr.handle, 0,
+							mr->handle, 0,
 							0, abs_adr,1);
 			nr_pages--;
 		}
 
 		if ((hret != H_SUCCESS) && (hret != H_PAGE_REGISTERED)) {
 			ehea_h_free_resource(adapter->handle,
-						adapter->mr.handle);
-			ehea_error("register_rpage_mr failed: hret = %lX",
-				   hret);
+					     mr->handle, FORCE_FREE);
+			ehea_error("register_rpage_mr failed");
 			ret = -EIO;
 			goto out;
 		}
 	}
 
 	if (hret != H_SUCCESS) {
-		ehea_h_free_resource(adapter->handle, adapter->mr.handle);
-		ehea_error("register_rpage failed for last page: hret = %lX",
-			   hret);
+		ehea_h_free_resource(adapter->handle, mr->handle,
+				     FORCE_FREE);
+		ehea_error("register_rpage failed for last page");
 		ret = -EIO;
 		goto out;
 	}
+
+	mr->adapter = adapter;
 	ret = 0;
 out:
 	kfree(pt);
 	return ret;
 }
 
+int ehea_rem_mr(struct ehea_mr *mr)
+{
+	u64 hret;
+
+	if (!mr || !mr->adapter)
+		return -EINVAL;
+
+	hret = ehea_h_free_resource(mr->adapter->handle, mr->handle,
+				    FORCE_FREE);
+	if (hret != H_SUCCESS) {
+		ehea_error("destroy MR failed");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+int ehea_gen_smr(struct ehea_adapter *adapter, struct ehea_mr *old_mr,
+		 struct ehea_mr *shared_mr)
+{
+	u64 hret;
+
+	hret = ehea_h_register_smr(adapter->handle, old_mr->handle,
+				   old_mr->vaddr, EHEA_MR_ACC_CTRL,
+				   adapter->pd, shared_mr);
+	if (hret != H_SUCCESS)
+		return -EIO;
+
+	shared_mr->adapter = adapter;
+
+	return 0;
+}
+
 void print_error_data(u64 *data)
 {
 	int length;
@@ -597,6 +673,14 @@
 		ehea_error("QP (resource=%lX) state: AER=0x%lX, AERR=0x%lX, "
 			   "port=%lX", resource, data[6], data[12], data[22]);
 
+	if (type == 0x4) /* Completion Queue */
+		ehea_error("CQ (resource=%lX) state: AER=0x%lX", resource,
+			   data[6]);
+
+	if (type == 0x3) /* Event Queue */
+		ehea_error("EQ (resource=%lX) state: AER=0x%lX", resource,
+			   data[6]);
+
 	ehea_dump(data, length, "error data");
 }
 
diff --git a/drivers/net/ehea/ehea_qmr.h b/drivers/net/ehea/ehea_qmr.h
index 1ff6098..c0eb3e0 100644
--- a/drivers/net/ehea/ehea_qmr.h
+++ b/drivers/net/ehea/ehea_qmr.h
@@ -142,6 +142,8 @@
 #define EHEA_CQE_STAT_ERR_MASK     0x721F
 #define EHEA_CQE_STAT_FAT_ERR_MASK 0x1F
 #define EHEA_CQE_STAT_ERR_TCP      0x4000
+#define EHEA_CQE_STAT_ERR_IP       0x2000
+#define EHEA_CQE_STAT_ERR_CRC      0x1000
 
 struct ehea_cqe {
 	u64 wr_id;		/* work request ID from WQE */
@@ -320,6 +322,11 @@
 	return hw_qeit_get_valid(queue);
 }
 
+static inline void ehea_inc_cq(struct ehea_cq *cq)
+{
+	hw_qeit_inc(&cq->hw_queue);
+}
+
 static inline void ehea_inc_rq1(struct ehea_qp *qp)
 {
 	hw_qeit_inc(&qp->hw_rqueue1);
@@ -327,7 +334,7 @@
 
 static inline struct ehea_cqe *ehea_poll_cq(struct ehea_cq *my_cq)
 {
-	return hw_qeit_get_inc_valid(&my_cq->hw_queue);
+	return hw_qeit_get_valid(&my_cq->hw_queue);
 }
 
 #define EHEA_CQ_REGISTER_ORIG 0
@@ -356,7 +363,12 @@
 
 int ehea_destroy_qp(struct ehea_qp *qp);
 
-int ehea_reg_mr_adapter(struct ehea_adapter *adapter);
+int ehea_reg_kernel_mr(struct ehea_adapter *adapter, struct ehea_mr *mr);
+
+int ehea_gen_smr(struct ehea_adapter *adapter, struct ehea_mr *old_mr,
+		 struct ehea_mr *shared_mr);
+
+int ehea_rem_mr(struct ehea_mr *mr);
 
 void ehea_error_data(struct ehea_adapter *adapter, u64 res_handle);
 
diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c
index 3a6a83d..4e3f14c 100644
--- a/drivers/net/epic100.c
+++ b/drivers/net/epic100.c
@@ -934,7 +934,6 @@
 		ep->rx_skbuff[i] = skb;
 		if (skb == NULL)
 			break;
-		skb->dev = dev;			/* Mark as being used by this device. */
 		skb_reserve(skb, 2);	/* 16 byte align the IP header. */
 		ep->rx_ring[i].bufaddr = pci_map_single(ep->pci_dev,
 			skb->data, ep->rx_buf_sz, PCI_DMA_FROMDEVICE);
@@ -1199,7 +1198,6 @@
 			   to a minimally-sized skbuff. */
 			if (pkt_len < rx_copybreak
 				&& (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_for_cpu(ep->pci_dev,
 							    ep->rx_ring[entry].bufaddr,
@@ -1236,7 +1234,6 @@
 			skb = ep->rx_skbuff[entry] = dev_alloc_skb(ep->rx_buf_sz);
 			if (skb == NULL)
 				break;
-			skb->dev = dev;			/* Mark as being used by this device. */
 			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
 			ep->rx_ring[entry].bufaddr = pci_map_single(ep->pci_dev,
 				skb->data, ep->rx_buf_sz, PCI_DMA_FROMDEVICE);
diff --git a/drivers/net/eth16i.c b/drivers/net/eth16i.c
index 93283e3..04abf59 100644
--- a/drivers/net/eth16i.c
+++ b/drivers/net/eth16i.c
@@ -1175,7 +1175,6 @@
 				break;
 			}
 
-			skb->dev = dev;
 			skb_reserve(skb,2);
 
 			/*
diff --git a/drivers/net/ewrk3.c b/drivers/net/ewrk3.c
index 714ea11..cb0792c 100644
--- a/drivers/net/ewrk3.c
+++ b/drivers/net/ewrk3.c
@@ -993,7 +993,6 @@
 
 					if ((skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
 						unsigned char *p;
-						skb->dev = dev;
 						skb_reserve(skb, 2);	/* Align to 16 bytes */
 						p = skb_put(skb, pkt_len);
 
diff --git a/drivers/net/fealnx.c b/drivers/net/fealnx.c
index 38a13f4..abe9b08 100644
--- a/drivers/net/fealnx.c
+++ b/drivers/net/fealnx.c
@@ -1719,7 +1719,6 @@
 			   to a minimally-sized skbuff. */
 			if (pkt_len < rx_copybreak &&
 			    (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_for_cpu(np->pci_dev,
 							    np->cur_rx->buffer,
diff --git a/drivers/net/fec.c b/drivers/net/fec.c
index 6764281..255b091 100644
--- a/drivers/net/fec.c
+++ b/drivers/net/fec.c
@@ -647,7 +647,6 @@
 		printk("%s: Memory squeeze, dropping packet.\n", dev->name);
 		fep->stats.rx_dropped++;
 	} else {
-		skb->dev = dev;
 		skb_put(skb,pkt_len-4);	/* Make room */
 		eth_copy_and_sum(skb, data, pkt_len-4, 0);
 		skb->protocol=eth_type_trans(skb,dev);
diff --git a/drivers/net/fec_8xx/fec_main.c b/drivers/net/fec_8xx/fec_main.c
index 77f747a..e824d5d 100644
--- a/drivers/net/fec_8xx/fec_main.c
+++ b/drivers/net/fec_8xx/fec_main.c
@@ -551,7 +551,9 @@
 				skbn = dev_alloc_skb(pkt_len + 2);
 				if (skbn != NULL) {
 					skb_reserve(skbn, 2);	/* align IP header */
-					memcpy(skbn->data, skb->data, pkt_len);
+					skb_copy_from_linear_data(skb
+								  skbn->data,
+								  pkt_len);
 					/* swap */
 					skbt = skb;
 					skb = skbn;
@@ -561,7 +563,6 @@
 				skbn = dev_alloc_skb(ENET_RX_FRSIZE);
 
 			if (skbn != NULL) {
-				skb->dev = dev;
 				skb_put(skb, pkt_len);	/* Make room */
 				skb->protocol = eth_type_trans(skb, dev);
 				received++;
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index d04214e..7a01802 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -1385,11 +1385,12 @@
 	while (np->put_rx.orig != less_rx) {
 		struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz + NV_RX_ALLOC_PAD);
 		if (skb) {
-			skb->dev = dev;
 			np->put_rx_ctx->skb = skb;
-			np->put_rx_ctx->dma = pci_map_single(np->pci_dev, skb->data,
-							     skb->end-skb->data, PCI_DMA_FROMDEVICE);
-			np->put_rx_ctx->dma_len = skb->end-skb->data;
+			np->put_rx_ctx->dma = pci_map_single(np->pci_dev,
+							     skb->data,
+							     skb_tailroom(skb),
+							     PCI_DMA_FROMDEVICE);
+			np->put_rx_ctx->dma_len = skb_tailroom(skb);
 			np->put_rx.orig->buf = cpu_to_le32(np->put_rx_ctx->dma);
 			wmb();
 			np->put_rx.orig->flaglen = cpu_to_le32(np->rx_buf_sz | NV_RX_AVAIL);
@@ -1416,11 +1417,12 @@
 	while (np->put_rx.ex != less_rx) {
 		struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz + NV_RX_ALLOC_PAD);
 		if (skb) {
-			skb->dev = dev;
 			np->put_rx_ctx->skb = skb;
-			np->put_rx_ctx->dma = pci_map_single(np->pci_dev, skb->data,
-							     skb->end-skb->data, PCI_DMA_FROMDEVICE);
-			np->put_rx_ctx->dma_len = skb->end-skb->data;
+			np->put_rx_ctx->dma = pci_map_single(np->pci_dev,
+							     skb->data,
+							     skb_tailroom(skb),
+							     PCI_DMA_FROMDEVICE);
+			np->put_rx_ctx->dma_len = skb_tailroom(skb);
 			np->put_rx.ex->bufhigh = cpu_to_le64(np->put_rx_ctx->dma) >> 32;
 			np->put_rx.ex->buflow = cpu_to_le64(np->put_rx_ctx->dma) & 0x0FFFFFFFF;
 			wmb();
@@ -1604,8 +1606,9 @@
 		wmb();
 		if (np->rx_skb[i].skb) {
 			pci_unmap_single(np->pci_dev, np->rx_skb[i].dma,
-						np->rx_skb[i].skb->end-np->rx_skb[i].skb->data,
-						PCI_DMA_FROMDEVICE);
+					 (skb_end_pointer(np->rx_skb[i].skb) -
+					  np->rx_skb[i].skb->data),
+					 PCI_DMA_FROMDEVICE);
 			dev_kfree_skb(np->rx_skb[i].skb);
 			np->rx_skb[i].skb = NULL;
 		}
@@ -4376,11 +4379,12 @@
 		ret = 0;
 		goto out;
 	}
+	test_dma_addr = pci_map_single(np->pci_dev, tx_skb->data,
+				       skb_tailroom(tx_skb),
+				       PCI_DMA_FROMDEVICE);
 	pkt_data = skb_put(tx_skb, pkt_len);
 	for (i = 0; i < pkt_len; i++)
 		pkt_data[i] = (u8)(i & 0xff);
-	test_dma_addr = pci_map_single(np->pci_dev, tx_skb->data,
-				       tx_skb->end-tx_skb->data, PCI_DMA_FROMDEVICE);
 
 	if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
 		np->tx_ring.orig[0].buf = cpu_to_le32(test_dma_addr);
@@ -4437,7 +4441,7 @@
 	}
 
 	pci_unmap_page(np->pci_dev, test_dma_addr,
-		       tx_skb->end-tx_skb->data,
+		       (skb_end_pointer(tx_skb) - tx_skb->data),
 		       PCI_DMA_TODEVICE);
 	dev_kfree_skb_any(tx_skb);
  out:
diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c
index 4a05c14..e2ddd61 100644
--- a/drivers/net/fs_enet/fs_enet-main.c
+++ b/drivers/net/fs_enet/fs_enet-main.c
@@ -160,7 +160,8 @@
 				skbn = dev_alloc_skb(pkt_len + 2);
 				if (skbn != NULL) {
 					skb_reserve(skbn, 2);	/* align IP header */
-					memcpy(skbn->data, skb->data, pkt_len);
+					skb_copy_from_linear_data(skb,
+						      skbn->data, pkt_len);
 					/* swap */
 					skbt = skb;
 					skb = skbn;
@@ -170,7 +171,6 @@
 				skbn = dev_alloc_skb(ENET_RX_FRSIZE);
 
 			if (skbn != NULL) {
-				skb->dev = dev;
 				skb_put(skb, pkt_len);	/* Make room */
 				skb->protocol = eth_type_trans(skb, dev);
 				received++;
@@ -294,7 +294,8 @@
 				skbn = dev_alloc_skb(pkt_len + 2);
 				if (skbn != NULL) {
 					skb_reserve(skbn, 2);	/* align IP header */
-					memcpy(skbn->data, skb->data, pkt_len);
+					skb_copy_from_linear_data(skb,
+						      skbn->data, pkt_len);
 					/* swap */
 					skbt = skb;
 					skb = skbn;
@@ -304,7 +305,6 @@
 				skbn = dev_alloc_skb(ENET_RX_FRSIZE);
 
 			if (skbn != NULL) {
-				skb->dev = dev;
 				skb_put(skb, pkt_len);	/* Make room */
 				skb->protocol = eth_type_trans(skb, dev);
 				received++;
@@ -516,7 +516,6 @@
 			break;
 		}
 		fep->rx_skbuff[i] = skb;
-		skb->dev = dev;
 		CBDW_BUFADDR(bdp,
 			dma_map_single(fep->dev, skb->data,
 				L1_CACHE_ALIGN(PKT_MAXBUF_SIZE),
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index d981d4c..b666a0c 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -942,18 +942,18 @@
 
 	/* Tell the controller what the protocol is */
 	/* And provide the already calculated phcs */
-	if (skb->nh.iph->protocol == IPPROTO_UDP) {
+	if (ip_hdr(skb)->protocol == IPPROTO_UDP) {
 		flags |= TXFCB_UDP;
-		fcb->phcs = skb->h.uh->check;
+		fcb->phcs = udp_hdr(skb)->check;
 	} else
-		fcb->phcs = skb->h.th->check;
+		fcb->phcs = udp_hdr(skb)->check;
 
 	/* l3os is the distance between the start of the
 	 * frame (skb->data) and the start of the IP hdr.
 	 * l4os is the distance between the start of the
 	 * l3 hdr and the l4 hdr */
-	fcb->l3os = (u16)(skb->nh.raw - skb->data - GMAC_FCB_LEN);
-	fcb->l4os = (u16)(skb->h.raw - skb->nh.raw);
+	fcb->l3os = (u16)(skb_network_offset(skb) - GMAC_FCB_LEN);
+	fcb->l4os = skb_network_header_len(skb);
 
 	fcb->flags = flags;
 }
@@ -1295,8 +1295,6 @@
 	 */
 	skb_reserve(skb, alignamount);
 
-	skb->dev = dev;
-
 	bdp->bufPtr = dma_map_single(NULL, skb->data,
 			priv->rx_buffer_size, DMA_FROM_DEVICE);
 
diff --git a/drivers/net/hamachi.c b/drivers/net/hamachi.c
index c3c0d67..2521b11 100644
--- a/drivers/net/hamachi.c
+++ b/drivers/net/hamachi.c
@@ -1568,7 +1568,6 @@
 				printk(KERN_ERR "%s: rx_copybreak non-zero "
 				  "not good with RX_CHECKSUM\n", dev->name);
 #endif
-				skb->dev = dev;
 				skb_reserve(skb, 2);	/* 16 byte align the IP header */
 				pci_dma_sync_single_for_cpu(hmp->pci_dev,
 							    hmp->rx_ring[entry].addr,
diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c
index 30baf6e..17ac697 100644
--- a/drivers/net/hamradio/baycom_ser_fdx.c
+++ b/drivers/net/hamradio/baycom_ser_fdx.c
@@ -415,11 +415,18 @@
 
 	if (!dev || !bc)
 		return -ENXIO;
-	if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT ||
-	    dev->irq < 2 || dev->irq > 15)
+	if (!dev->base_addr || dev->base_addr > 0xffff-SER12_EXTENT ||
+	    dev->irq < 2 || dev->irq > NR_IRQS) {
+		printk(KERN_INFO "baycom_ser_fdx: invalid portnumber (max %u) "
+				"or irq (2 <= irq <= %d)\n",
+				0xffff-SER12_EXTENT, NR_IRQS);
 		return -ENXIO;
-	if (bc->baud < 300 || bc->baud > 4800)
+	}
+	if (bc->baud < 300 || bc->baud > 4800) {
+		printk(KERN_INFO "baycom_ser_fdx: invalid baudrate "
+				"(300...4800)\n");
 		return -EINVAL;
+	}
 	if (!request_region(dev->base_addr, SER12_EXTENT, "baycom_ser_fdx")) {
 		printk(KERN_WARNING "BAYCOM_SER_FSX: I/O port 0x%04lx busy \n", 
 		       dev->base_addr);
diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c
index d254269..656f278 100644
--- a/drivers/net/hamradio/bpqether.c
+++ b/drivers/net/hamradio/bpqether.c
@@ -282,7 +282,7 @@
 	}
 
 	skb->protocol = ax25_type_trans(skb, dev);
-	skb->nh.raw = skb->data;
+	skb_reset_network_header(skb);
 	dev->hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0);
 	bpq->stats.tx_packets++;
 	bpq->stats.tx_bytes+=skb->len;
diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c
index 0fbb414..3be8c50 100644
--- a/drivers/net/hamradio/dmascc.c
+++ b/drivers/net/hamradio/dmascc.c
@@ -930,7 +930,7 @@
 
 	/* Transfer data to DMA buffer */
 	i = priv->tx_head;
-	memcpy(priv->tx_buf[i], skb->data + 1, skb->len - 1);
+	skb_copy_from_linear_data_offset(skb, 1, priv->tx_buf[i], skb->len - 1);
 	priv->tx_len[i] = skb->len - 1;
 
 	/* Clear interrupts while we touch our circular buffers */
diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c
index f5a17ad..b33adc6 100644
--- a/drivers/net/hamradio/hdlcdrv.c
+++ b/drivers/net/hamradio/hdlcdrv.c
@@ -317,7 +317,9 @@
 				dev_kfree_skb_irq(skb);
 				break;
 			}
-			memcpy(s->hdlctx.buffer, skb->data+1, pkt_len);
+			skb_copy_from_linear_data_offset(skb, 1,
+							 s->hdlctx.buffer,
+							 pkt_len);
 			dev_kfree_skb_irq(skb);
 			s->hdlctx.bp = s->hdlctx.buffer;
 			append_crc_ccitt(s->hdlctx.buffer, pkt_len);
diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c
index ee3ea4f..467559d 100644
--- a/drivers/net/hamradio/yam.c
+++ b/drivers/net/hamradio/yam.c
@@ -638,7 +638,9 @@
         			dev_kfree_skb_any(skb);
 				break;
 			}
-			memcpy(yp->tx_buf, skb->data + 1, yp->tx_len);
+			skb_copy_from_linear_data_offset(skb, 1,
+							 yp->tx_buf,
+							 yp->tx_len);
 			dev_kfree_skb_any(skb);
 			yp->tx_count = 0;
 			yp->tx_crcl = 0x21;
diff --git a/drivers/net/hp100.c b/drivers/net/hp100.c
index 7dc5185..8118a67 100644
--- a/drivers/net/hp100.c
+++ b/drivers/net/hp100.c
@@ -1816,7 +1816,6 @@
 			u_char *ptr;
 
 			skb_reserve(skb,2);
-			skb->dev = dev;
 
 			/* ptr to start of the sk_buff data area */
 			skb_put(skb, pkt_len);
diff --git a/drivers/net/ibm_emac/ibm_emac_core.c b/drivers/net/ibm_emac/ibm_emac_core.c
index dd8ad87..3d82d46 100644
--- a/drivers/net/ibm_emac/ibm_emac_core.c
+++ b/drivers/net/ibm_emac/ibm_emac_core.c
@@ -1338,7 +1338,7 @@
 			dev_kfree_skb(dev->rx_sg_skb);
 			dev->rx_sg_skb = NULL;
 		} else {
-			cacheable_memcpy(dev->rx_sg_skb->tail,
+			cacheable_memcpy(skb_tail_pointer(dev->rx_sg_skb),
 					 dev->rx_skb[slot]->data, len);
 			skb_put(dev->rx_sg_skb, len);
 			emac_recycle_rx_skb(dev, slot, len);
@@ -1398,7 +1398,6 @@
 
 		skb_put(skb, len);
 	      push_packet:
-		skb->dev = dev->ndev;
 		skb->protocol = eth_type_trans(skb, dev->ndev);
 		emac_rx_csum(dev, skb, ctrl);
 
diff --git a/drivers/net/ibmlana.c b/drivers/net/ibmlana.c
index 3f946c8..fe85d6f 100644
--- a/drivers/net/ibmlana.c
+++ b/drivers/net/ibmlana.c
@@ -601,7 +601,6 @@
 
 				/* set up skb fields */
 
-				skb->dev = dev;
 				skb->protocol = eth_type_trans(skb, dev);
 				skb->ip_summed = CHECKSUM_NONE;
 
diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c
index 458db05..3bec0f7 100644
--- a/drivers/net/ibmveth.c
+++ b/drivers/net/ibmveth.c
@@ -93,7 +93,7 @@
 static void ibmveth_proc_register_adapter(struct ibmveth_adapter *adapter);
 static void ibmveth_proc_unregister_adapter(struct ibmveth_adapter *adapter);
 static irqreturn_t ibmveth_interrupt(int irq, void *dev_instance);
-static inline void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter);
+static void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter);
 static struct kobj_type ktype_veth_pool;
 
 #ifdef CONFIG_PROC_FS
@@ -389,7 +389,7 @@
 	}
 }
 
-static inline void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter)
+static void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter)
 {
 	ibmveth_remove_buffer_from_pool(adapter, adapter->rx_queue.queue_addr[adapter->rx_queue.index].correlator);
 
@@ -798,7 +798,6 @@
 
 				skb_reserve(skb, offset);
 				skb_put(skb, length);
-				skb->dev = netdev;
 				skb->protocol = eth_type_trans(skb, netdev);
 
 				netif_receive_skb(skb);	/* send it up */
@@ -954,14 +953,16 @@
 	ibmveth_debug_printk_no_adapter("entering ibmveth_probe for UA 0x%x\n",
 					dev->unit_address);
 
-	mac_addr_p = (unsigned char *) vio_get_attribute(dev, VETH_MAC_ADDR, 0);
+	mac_addr_p = (unsigned char *) vio_get_attribute(dev,
+						VETH_MAC_ADDR, NULL);
 	if(!mac_addr_p) {
 		printk(KERN_ERR "(%s:%3.3d) ERROR: Can't find VETH_MAC_ADDR "
 				"attribute\n", __FILE__, __LINE__);
 		return 0;
 	}
 
-	mcastFilterSize_p= (unsigned int *) vio_get_attribute(dev, VETH_MCAST_FILTER_SIZE, 0);
+	mcastFilterSize_p = (unsigned int *) vio_get_attribute(dev,
+						VETH_MCAST_FILTER_SIZE, NULL);
 	if(!mcastFilterSize_p) {
 		printk(KERN_ERR "(%s:%3.3d) ERROR: Can't find "
 				"VETH_MCAST_FILTER_SIZE attribute\n",
diff --git a/drivers/net/ioc3-eth.c b/drivers/net/ioc3-eth.c
index 4ad7807..f749e07 100644
--- a/drivers/net/ioc3-eth.c
+++ b/drivers/net/ioc3-eth.c
@@ -633,8 +633,6 @@
 
 			ip->rx_skbs[rx_entry] = NULL;	/* Poison  */
 
-			new_skb->dev = priv_netdev(ip);
-
 			/* Because we reserve afterwards. */
 			skb_put(new_skb, (1664 + RX_OFFSET));
 			rxb = (struct ioc3_erxbuf *) new_skb->data;
@@ -940,7 +938,6 @@
 			}
 
 			ip->rx_skbs[i] = skb;
-			skb->dev = dev;
 
 			/* Because we reserve afterwards. */
 			skb_put(skb, (1664 + RX_OFFSET));
@@ -1396,9 +1393,9 @@
 	 * manually.
 	 */
 	if (skb->ip_summed == CHECKSUM_PARTIAL) {
-		int proto = ntohs(skb->nh.iph->protocol);
+		const struct iphdr *ih = ip_hdr(skb);
+		const int proto = ntohs(ih->protocol);
 		unsigned int csoff;
-		struct iphdr *ih = skb->nh.iph;
 		uint32_t csum, ehsum;
 		uint16_t *eh;
 
@@ -1425,11 +1422,11 @@
 		csoff = ETH_HLEN + (ih->ihl << 2);
 		if (proto == IPPROTO_UDP) {
 			csoff += offsetof(struct udphdr, check);
-			skb->h.uh->check = csum;
+			udp_hdr(skb)->check = csum;
 		}
 		if (proto == IPPROTO_TCP) {
 			csoff += offsetof(struct tcphdr, check);
-			skb->h.th->check = csum;
+			tcp_hdr(skb)->check = csum;
 		}
 
 		w0 = ETXD_DOCHECKSUM | (csoff << ETXD_CHKOFF_SHIFT);
@@ -1446,7 +1443,7 @@
 
 	if (len <= 104) {
 		/* Short packet, let's copy it directly into the ring.  */
-		memcpy(desc->data, skb->data, skb->len);
+		skb_copy_from_linear_data(skb, desc->data, skb->len);
 		if (len < ETH_ZLEN) {
 			/* Very short packet, pad with zeros at the end. */
 			memset(desc->data + len, 0, ETH_ZLEN - len);
diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c
index cebf8c3..f9c889c 100644
--- a/drivers/net/irda/ali-ircc.c
+++ b/drivers/net/irda/ali-ircc.c
@@ -1472,9 +1472,8 @@
 
 	self->stats.tx_bytes += skb->len;
 
-	memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data, 
-	       skb->len);
-	
+	skb_copy_from_linear_data(skb, self->tx_fifo.queue[self->tx_fifo.free].start,
+		      skb->len);
 	self->tx_fifo.len++;
 	self->tx_fifo.free++;
 
@@ -1924,7 +1923,7 @@
 			
 			/* Copy frame without CRC, CRC is removed by hardware*/
 			skb_put(skb, len);
-			memcpy(skb->data, self->rx_buff.data, len);
+			skb_copy_to_linear_data(skb, self->rx_buff.data, len);
 
 			/* Move to next frame */
 			self->rx_buff.data += len;
@@ -1932,7 +1931,7 @@
 			self->stats.rx_packets++;
 
 			skb->dev = self->netdev;
-			skb->mac.raw  = skb->data;
+			skb_reset_mac_header(skb);
 			skb->protocol = htons(ETH_P_IRDA);
 			netif_rx(skb);
 			self->netdev->last_rx = jiffies;
diff --git a/drivers/net/irda/au1k_ir.c b/drivers/net/irda/au1k_ir.c
index 37914dc..4dbdfaa 100644
--- a/drivers/net/irda/au1k_ir.c
+++ b/drivers/net/irda/au1k_ir.c
@@ -526,7 +526,7 @@
 	
 	if (aup->speed == 4000000) {
 		/* FIR */
-		memcpy((void *)pDB->vaddr, skb->data, skb->len);
+		skb_copy_from_linear_data(skb, pDB->vaddr, skb->len);
 		ptxd->count_0 = skb->len & 0xff;
 		ptxd->count_1 = (skb->len >> 8) & 0xff;
 
@@ -604,9 +604,9 @@
 				skb_put(skb, count);
 			else
 				skb_put(skb, count-2);
-			memcpy(skb->data, (void *)pDB->vaddr, count-2);
+			skb_copy_to_linear_data(skb, pDB->vaddr, count - 2);
 			skb->dev = dev;
-			skb->mac.raw = skb->data;
+			skb_reset_mac_header(skb);
 			skb->protocol = htons(ETH_P_IRDA);
 			netif_rx(skb);
 			prxd->count_0 = 0;
diff --git a/drivers/net/irda/donauboe.c b/drivers/net/irda/donauboe.c
index 11af0ae..3ca47bf 100644
--- a/drivers/net/irda/donauboe.c
+++ b/drivers/net/irda/donauboe.c
@@ -1119,7 +1119,7 @@
   else
     {
       len = skb->len;
-      memcpy (self->tx_bufs[self->txs], skb->data, len);
+      skb_copy_from_linear_data(skb, self->tx_bufs[self->txs], len);
     }
   self->ring->tx[self->txs].len = len & 0x0fff;
 
@@ -1282,11 +1282,11 @@
                       skb_reserve (skb, 1);
 
                       skb_put (skb, len);
-                      memcpy (skb->data, self->rx_bufs[self->rxs], len);
-
+                      skb_copy_to_linear_data(skb, self->rx_bufs[self->rxs],
+					      len);
                       self->stats.rx_packets++;
                       skb->dev = self->netdev;
-                      skb->mac.raw = skb->data;
+                      skb_reset_mac_header(skb);
                       skb->protocol = htons (ETH_P_IRDA);
                     }
                   else
diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c
index 1d510bd..0ac240c 100644
--- a/drivers/net/irda/irda-usb.c
+++ b/drivers/net/irda/irda-usb.c
@@ -441,7 +441,7 @@
 		goto drop;
 	}
 
-	memcpy(self->tx_buff + self->header_length, skb->data, skb->len);
+	skb_copy_from_linear_data(skb, self->tx_buff + self->header_length, skb->len);
 
 	/* Change setting for next frame */
 	if (self->capability & IUC_STIR421X) {
@@ -902,7 +902,7 @@
 
 	if(docopy) {
 		/* Copy packet, so we can recycle the original */
-		memcpy(newskb->data, skb->data, urb->actual_length);
+		skb_copy_from_linear_data(skb, newskb->data, urb->actual_length);
 		/* Deliver this new skb */
 		dataskb = newskb;
 		/* And hook the old skb to the URB
@@ -921,7 +921,7 @@
 
 	/* Ask the networking layer to queue the packet for the IrDA stack */
 	dataskb->dev = self->netdev;
-	dataskb->mac.raw  = dataskb->data;
+	skb_reset_mac_header(dataskb);
 	dataskb->protocol = htons(ETH_P_IRDA);
 	len = dataskb->len;
 	netif_rx(dataskb);
diff --git a/drivers/net/irda/mcs7780.c b/drivers/net/irda/mcs7780.c
index f0c61f3..0de8672 100644
--- a/drivers/net/irda/mcs7780.c
+++ b/drivers/net/irda/mcs7780.c
@@ -200,14 +200,14 @@
 /* Setup a communication between mcs7780 and agilent chip. */
 static inline int mcs_setup_transceiver_agilent(struct mcs_cb *mcs)
 {
-	IRDA_WARNING("This transceiver type is not supported yet.");
+	IRDA_WARNING("This transceiver type is not supported yet.\n");
 	return 1;
 }
 
 /* Setup a communication between mcs7780 and sharp chip. */
 static inline int mcs_setup_transceiver_sharp(struct mcs_cb *mcs)
 {
-	IRDA_WARNING("This transceiver type is not supported yet.");
+	IRDA_WARNING("This transceiver type is not supported yet.\n");
 	return 1;
 }
 
@@ -279,7 +279,7 @@
 		break;
 
 	default:
-		IRDA_WARNING("Unknown transceiver type: %d",
+		IRDA_WARNING("Unknown transceiver type: %d\n",
 			     mcs->transceiver_type);
 		ret = 1;
 	}
@@ -318,7 +318,7 @@
 		return ret;
 
 error:
-	IRDA_ERROR("%s", msg);
+	IRDA_ERROR("%s\n", msg);
 	return ret;
 }
 
@@ -353,7 +353,7 @@
 	buf[0] = len & 0xff;
 	buf[1] = (len >> 8) & 0xff;
 	/* copy the data into the tx buffer. */
-	memcpy(buf+2, skb->data, skb->len);
+	skb_copy_from_linear_data(skb, buf + 2, skb->len);
 	/* put the fcs in the last four bytes in little endian order. */
 	buf[len - 4] = fcs & 0xff;
 	buf[len - 3] = (fcs >> 8) & 0xff;
@@ -377,7 +377,7 @@
 	buf[0] = len & 0xff;
 	buf[1] = (len >> 8) & 0xff;
 	/* copy the data */
-	memcpy(buf+2, skb->data, skb->len);
+	skb_copy_from_linear_data(skb, buf + 2, skb->len);
 	/* put the fcs in last two bytes in little endian order. */
 	buf[len - 2] = fcs & 0xff;
 	buf[len - 1] = (fcs >> 8) & 0xff;
@@ -426,9 +426,9 @@
 	}
 
 	skb_reserve(skb, 1);
-	memcpy(skb->data, buf, new_len);
+	skb_copy_to_linear_data(skb, buf, new_len);
 	skb_put(skb, new_len);
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	skb->protocol = htons(ETH_P_IRDA);
 	skb->dev = mcs->netdev;
 
@@ -479,9 +479,9 @@
 	}
 
 	skb_reserve(skb, 1);
-	memcpy(skb->data, buf, new_len);
+	skb_copy_to_linear_data(skb, buf, new_len);
 	skb_put(skb, new_len);
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	skb->protocol = htons(ETH_P_IRDA);
 	skb->dev = mcs->netdev;
 
@@ -587,7 +587,7 @@
 	} while(cnt++ < 100 && (rval & MCS_IRINTX));
 
 	if(cnt >= 100) {
-		IRDA_ERROR("unable to change speed");
+		IRDA_ERROR("unable to change speed\n");
 		ret = -EIO;
 		goto error;
 	}
@@ -638,7 +638,7 @@
 
 		default:
 			ret = 1;
-			IRDA_WARNING("Unknown transceiver type: %d",
+			IRDA_WARNING("Unknown transceiver type: %d\n",
 				     mcs->transceiver_type);
 		}
 	if (unlikely(ret))
@@ -733,7 +733,7 @@
 	sprintf(hwname, "usb#%d", mcs->usbdev->devnum);
 	mcs->irlap = irlap_open(netdev, &mcs->qos, hwname);
 	if (!mcs->irlap) {
-		IRDA_ERROR("mcs7780: irlap_open failed");
+		IRDA_ERROR("mcs7780: irlap_open failed\n");
 		goto error2;
 	}
 
@@ -862,7 +862,7 @@
 			  mcs->out_buf, wraplen, mcs_send_irq, mcs);
 
 	if ((ret = usb_submit_urb(mcs->tx_urb, GFP_ATOMIC))) {
-		IRDA_ERROR("failed tx_urb: %d", ret);
+		IRDA_ERROR("failed tx_urb: %d\n", ret);
 		switch (ret) {
 		case -ENODEV:
 		case -EPIPE:
@@ -897,7 +897,7 @@
 	if (!ndev)
 		goto error1;
 
-	IRDA_DEBUG(1, "MCS7780 USB-IrDA bridge found at %d.", udev->devnum);
+	IRDA_DEBUG(1, "MCS7780 USB-IrDA bridge found at %d.\n", udev->devnum);
 
 	/* what is it realy for? */
 	SET_MODULE_OWNER(ndev);
@@ -905,7 +905,7 @@
 
 	ret = usb_reset_configuration(udev);
 	if (ret != 0) {
-		IRDA_ERROR("mcs7780: usb reset configuration failed");
+		IRDA_ERROR("mcs7780: usb reset configuration failed\n");
 		goto error2;
 	}
 
@@ -950,7 +950,7 @@
 	if (ret != 0)
 		goto error2;
 
-	IRDA_DEBUG(1, "IrDA: Registered MosChip MCS7780 device as %s",
+	IRDA_DEBUG(1, "IrDA: Registered MosChip MCS7780 device as %s\n",
 		   ndev->name);
 
 	mcs->transceiver_type = transceiver_type;
@@ -981,7 +981,7 @@
 	free_netdev(mcs->netdev);
 
 	usb_set_intfdata(intf, NULL);
-	IRDA_DEBUG(0, "MCS7780 now disconnected.");
+	IRDA_DEBUG(0, "MCS7780 now disconnected.\n");
 }
 
 /* Module insertion */
@@ -992,7 +992,7 @@
 	/* register this driver with the USB subsystem */
 	result = usb_register(&mcs_driver);
 	if (result)
-		IRDA_ERROR("usb_register failed. Error number %d", result);
+		IRDA_ERROR("usb_register failed. Error number %d\n", result);
 
 	return result;
 }
diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c
index 29b5ccd..d96c897 100644
--- a/drivers/net/irda/nsc-ircc.c
+++ b/drivers/net/irda/nsc-ircc.c
@@ -1466,9 +1466,8 @@
 
 	self->stats.tx_bytes += skb->len;
 
-	memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data, 
-	       skb->len);
-	
+	skb_copy_from_linear_data(skb, self->tx_fifo.queue[self->tx_fifo.free].start,
+		      skb->len);
 	self->tx_fifo.len++;
 	self->tx_fifo.free++;
 
@@ -1869,10 +1868,14 @@
 			/* Copy frame without CRC */
 			if (self->io.speed < 4000000) {
 				skb_put(skb, len-2);
-				memcpy(skb->data, self->rx_buff.data, len-2);
+				skb_copy_to_linear_data(skb,
+							self->rx_buff.data,
+							len - 2);
 			} else {
 				skb_put(skb, len-4);
-				memcpy(skb->data, self->rx_buff.data, len-4);
+				skb_copy_to_linear_data(skb,
+							self->rx_buff.data,
+							len - 4);
 			}
 
 			/* Move to next frame */
@@ -1881,7 +1884,7 @@
 			self->stats.rx_packets++;
 
 			skb->dev = self->netdev;
-			skb->mac.raw  = skb->data;
+			skb_reset_mac_header(skb);
 			skb->protocol = htons(ETH_P_IRDA);
 			netif_rx(skb);
 			self->netdev->last_rx = jiffies;
diff --git a/drivers/net/irda/pxaficp_ir.c b/drivers/net/irda/pxaficp_ir.c
index 2272156..fb196fd 100644
--- a/drivers/net/irda/pxaficp_ir.c
+++ b/drivers/net/irda/pxaficp_ir.c
@@ -386,12 +386,12 @@
 
 		/* Align IP header to 20 bytes  */
 		skb_reserve(skb, 1);
-		memcpy(skb->data, si->dma_rx_buff, len);
+		skb_copy_to_linear_data(skb, si->dma_rx_buff, len);
 		skb_put(skb, len);
 
 		/* Feed it to IrLAP  */
 		skb->dev = dev;
-		skb->mac.raw  = skb->data;
+		skb_reset_mac_header(skb);
 		skb->protocol = htons(ETH_P_IRDA);
 		netif_rx(skb);
 
@@ -484,7 +484,7 @@
 		unsigned long mtt = irda_get_mtt(skb);
 
 		si->dma_tx_buff_len = skb->len;
-		memcpy(si->dma_tx_buff, skb->data, skb->len);
+		skb_copy_from_linear_data(skb, si->dma_tx_buff, skb->len);
 
 		if (mtt)
 			while ((unsigned)(OSCR - si->last_oscr)/4 < mtt)
diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c
index 937372d..056639f 100644
--- a/drivers/net/irda/sa1100_ir.c
+++ b/drivers/net/irda/sa1100_ir.c
@@ -504,7 +504,7 @@
 
 		skb_put(skb, len);
 		skb->dev = dev;
-		skb->mac.raw = skb->data;
+		skb_reset_mac_header(skb);
 		skb->protocol = htons(ETH_P_IRDA);
 		si->stats.rx_packets++;
 		si->stats.rx_bytes += len;
diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c
index 31c6233..198bf3b 100644
--- a/drivers/net/irda/smsc-ircc2.c
+++ b/drivers/net/irda/smsc-ircc2.c
@@ -315,6 +315,7 @@
 {
 	/* Base address 0x2E or 0x4E */
 	{ "47N227",	KEY55_1|FIR|SERx4,	0x5a, 0x00 },
+	{ "47N227",	KEY55_1|FIR|SERx4,	0x7a, 0x00 },
 	{ "47N267",	KEY55_1|FIR|SERx4,	0x5e, 0x00 },
 	{ NULL }
 };
@@ -1161,7 +1162,7 @@
 		self->new_speed = speed;
 	}
 
-	memcpy(self->tx_buff.head, skb->data, skb->len);
+	skb_copy_from_linear_data(skb, self->tx_buff.head, skb->len);
 
 	self->tx_buff.len = skb->len;
 	self->tx_buff.data = self->tx_buff.head;
@@ -1412,7 +1413,7 @@
 	self->stats.rx_bytes += len;
 
 	skb->dev = self->netdev;
-	skb->mac.raw  = skb->data;
+	skb_reset_mac_header(skb);
 	skb->protocol = htons(ETH_P_IRDA);
 	netif_rx(skb);
 }
diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c
index 20d306f..755aa44 100644
--- a/drivers/net/irda/stir4200.c
+++ b/drivers/net/irda/stir4200.c
@@ -52,7 +52,6 @@
 #include <linux/kthread.h>
 #include <linux/freezer.h>
 #include <net/irda/irda.h>
-#include <net/irda/irlap.h>
 #include <net/irda/irda_device.h>
 #include <net/irda/wrapper.h>
 #include <net/irda/crc.h>
@@ -349,7 +348,7 @@
 		}
 		skb_reserve(nskb, 1);
 		skb = nskb;
-		memcpy(nskb->data, rx_buff->data, len);
+		skb_copy_to_linear_data(nskb, rx_buff->data, len);
 	} else {
 		nskb = dev_alloc_skb(rx_buff->truesize);
 		if (unlikely(!nskb)) {
@@ -364,7 +363,7 @@
 
 	skb_put(skb, len);
 
-	skb->mac.raw  = skb->data;
+	skb_reset_mac_header(skb);
 	skb->protocol = htons(ETH_P_IRDA);
 	skb->dev = stir->netdev;
 
diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c
index c3ed9b3..ff53585 100644
--- a/drivers/net/irda/via-ircc.c
+++ b/drivers/net/irda/via-ircc.c
@@ -925,8 +925,8 @@
 
 	self->tx_fifo.tail += skb->len;
 	self->stats.tx_bytes += skb->len;
-	memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data,
-	       skb->len);
+	skb_copy_from_linear_data(skb,
+		      self->tx_fifo.queue[self->tx_fifo.free].start, skb->len);
 	self->tx_fifo.len++;
 	self->tx_fifo.free++;
 //F01   if (self->tx_fifo.len == 1) {
@@ -1125,7 +1125,7 @@
 		self->stats.rx_bytes += len;
 		self->stats.rx_packets++;
 		skb->dev = self->netdev;
-		skb->mac.raw = skb->data;
+		skb_reset_mac_header(skb);
 		skb->protocol = htons(ETH_P_IRDA);
 		netif_rx(skb);
 		return TRUE;
@@ -1189,7 +1189,7 @@
 		skb_reserve(skb, 1);
 		skb_put(skb, len - 4);
 
-		memcpy(skb->data, self->rx_buff.data, len - 4);
+		skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4);
 		IRDA_DEBUG(2, "%s(): len=%x.rx_buff=%p\n", __FUNCTION__,
 			   len - 4, self->rx_buff.data);
 
@@ -1198,7 +1198,7 @@
 		self->stats.rx_bytes += len;
 		self->stats.rx_packets++;
 		skb->dev = self->netdev;
-		skb->mac.raw = skb->data;
+		skb_reset_mac_header(skb);
 		skb->protocol = htons(ETH_P_IRDA);
 		netif_rx(skb);
 
@@ -1234,7 +1234,7 @@
 	}
 	skb_reserve(skb, 1);
 	skb_put(skb, len - 4 + 1);
-	memcpy(skb->data, self->rx_buff.data, len - 4 + 1);
+	skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4 + 1);
 	st_fifo->tail++;
 	st_fifo->len++;
 	if (st_fifo->tail > MAX_RX_WINDOW)
@@ -1244,7 +1244,7 @@
 	self->stats.rx_bytes += len;
 	self->stats.rx_packets++;
 	skb->dev = self->netdev;
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	skb->protocol = htons(ETH_P_IRDA);
 	netif_rx(skb);
 	if (st_fifo->len < (MAX_RX_WINDOW + 2)) {
@@ -1303,7 +1303,7 @@
 			}
 			skb_reserve(skb, 1);
 			skb_put(skb, len - 4);
-			memcpy(skb->data, self->rx_buff.data, len - 4);
+			skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4);
 
 			IRDA_DEBUG(2, "%s(): len=%x.head=%x\n", __FUNCTION__,
 				   len - 4, st_fifo->head);
@@ -1313,7 +1313,7 @@
 			self->stats.rx_bytes += len;
 			self->stats.rx_packets++;
 			skb->dev = self->netdev;
-			skb->mac.raw = skb->data;
+			skb_reset_mac_header(skb);
 			skb->protocol = htons(ETH_P_IRDA);
 			netif_rx(skb);
 		}		//while
diff --git a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c
index 3457e9d..c4be973 100644
--- a/drivers/net/irda/vlsi_ir.c
+++ b/drivers/net/irda/vlsi_ir.c
@@ -595,7 +595,7 @@
 	rd->skb = NULL;
 	skb->dev = ndev;
 	memcpy(skb_put(skb,len), rd->buf, len);
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	if (in_interrupt())
 		netif_rx(skb);
 	else
@@ -993,7 +993,7 @@
 			goto drop;
 		}
 		else
-			memcpy(rd->buf, skb->data, len);
+			skb_copy_from_linear_data(skb, rd->buf, len);
 	}
 
 	rd->skb = skb;			/* remember skb for tx-complete stats */
diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c
index 4212657..5182e80 100644
--- a/drivers/net/irda/w83977af_ir.c
+++ b/drivers/net/irda/w83977af_ir.c
@@ -529,7 +529,7 @@
 	/* Decide if we should use PIO or DMA transfer */
 	if (self->io.speed > PIO_MAX_SPEED) {
 		self->tx_buff.data = self->tx_buff.head;
-		memcpy(self->tx_buff.data, skb->data, skb->len);
+		skb_copy_from_linear_data(skb, self->tx_buff.data, skb->len);
 		self->tx_buff.len = skb->len;
 		
 		mtt = irda_get_mtt(skb);
@@ -908,10 +908,14 @@
 			/* Copy frame without CRC */
 			if (self->io.speed < 4000000) {
 				skb_put(skb, len-2);
-				memcpy(skb->data, self->rx_buff.data, len-2);
+				skb_copy_to_linear_data(skb,
+							self->rx_buff.data,
+							len - 2);
 			} else {
 				skb_put(skb, len-4);
-				memcpy(skb->data, self->rx_buff.data, len-4);
+				skb_copy_to_linear_data(skb,
+							self->rx_buff.data,
+							len - 4);
 			}
 
 			/* Move to next frame */
@@ -919,7 +923,7 @@
 			self->stats.rx_packets++;
 			
 			skb->dev = self->netdev;
-			skb->mac.raw  = skb->data;
+			skb_reset_mac_header(skb);
 			skb->protocol = htons(ETH_P_IRDA);
 			netif_rx(skb);
 			self->netdev->last_rx = jiffies;
diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c
index 0e9ba3c..347d50c 100644
--- a/drivers/net/iseries_veth.c
+++ b/drivers/net/iseries_veth.c
@@ -1540,7 +1540,6 @@
 		}
 
 		skb_put(skb, length);
-		skb->dev = dev;
 		skb->protocol = eth_type_trans(skb, dev);
 		skb->ip_summed = CHECKSUM_NONE;
 		netif_rx(skb);	/* send it up */
diff --git a/drivers/net/ixgb/ixgb.h b/drivers/net/ixgb/ixgb.h
index cf30a10..c8e9086 100644
--- a/drivers/net/ixgb/ixgb.h
+++ b/drivers/net/ixgb/ixgb.h
@@ -111,9 +111,6 @@
 /* How many Rx Buffers do we bundle into one write to the hardware ? */
 #define IXGB_RX_BUFFER_WRITE	8	/* Must be power of 2 */
 
-/* only works for sizes that are powers of 2 */
-#define IXGB_ROUNDUP(i, size) ((i) = (((i) + (size) - 1) & ~((size) - 1)))
-
 /* wrapper around a pointer to a socket buffer,
  * so a DMA handle can be stored along with the buffer */
 struct ixgb_buffer {
diff --git a/drivers/net/ixgb/ixgb_ethtool.c b/drivers/net/ixgb/ixgb_ethtool.c
index d6628bd..afde848 100644
--- a/drivers/net/ixgb/ixgb_ethtool.c
+++ b/drivers/net/ixgb/ixgb_ethtool.c
@@ -577,11 +577,11 @@
 
 	rxdr->count = max(ring->rx_pending,(uint32_t)MIN_RXD);
 	rxdr->count = min(rxdr->count,(uint32_t)MAX_RXD);
-	IXGB_ROUNDUP(rxdr->count, IXGB_REQ_RX_DESCRIPTOR_MULTIPLE); 
+	rxdr->count = ALIGN(rxdr->count, IXGB_REQ_RX_DESCRIPTOR_MULTIPLE);
 
 	txdr->count = max(ring->tx_pending,(uint32_t)MIN_TXD);
 	txdr->count = min(txdr->count,(uint32_t)MAX_TXD);
-	IXGB_ROUNDUP(txdr->count, IXGB_REQ_TX_DESCRIPTOR_MULTIPLE); 
+	txdr->count = ALIGN(txdr->count, IXGB_REQ_TX_DESCRIPTOR_MULTIPLE);
 
 	if(netif_running(adapter->netdev)) {
 		/* Try to get new resources before deleting old */
diff --git a/drivers/net/ixgb/ixgb_main.c b/drivers/net/ixgb/ixgb_main.c
index afc2ec7..6d2b059 100644
--- a/drivers/net/ixgb/ixgb_main.c
+++ b/drivers/net/ixgb/ixgb_main.c
@@ -685,7 +685,7 @@
 	/* round up to nearest 4K */
 
 	txdr->size = txdr->count * sizeof(struct ixgb_tx_desc);
-	IXGB_ROUNDUP(txdr->size, 4096);
+	txdr->size = ALIGN(txdr->size, 4096);
 
 	txdr->desc = pci_alloc_consistent(pdev, txdr->size, &txdr->dma);
 	if(!txdr->desc) {
@@ -774,7 +774,7 @@
 	/* Round up to nearest 4K */
 
 	rxdr->size = rxdr->count * sizeof(struct ixgb_rx_desc);
-	IXGB_ROUNDUP(rxdr->size, 4096);
+	rxdr->size = ALIGN(rxdr->size, 4096);
 
 	rxdr->desc = pci_alloc_consistent(pdev, rxdr->size, &rxdr->dma);
 
@@ -1182,24 +1182,27 @@
 
 	if (likely(skb_is_gso(skb))) {
 		struct ixgb_buffer *buffer_info;
+		struct iphdr *iph;
+
 		if (skb_header_cloned(skb)) {
 			err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
 			if (err)
 				return err;
 		}
 
-		hdr_len = ((skb->h.raw - skb->data) + (skb->h.th->doff << 2));
+		hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
 		mss = skb_shinfo(skb)->gso_size;
-		skb->nh.iph->tot_len = 0;
-		skb->nh.iph->check = 0;
-		skb->h.th->check = ~csum_tcpudp_magic(skb->nh.iph->saddr,
-						      skb->nh.iph->daddr,
-						      0, IPPROTO_TCP, 0);
-		ipcss = skb->nh.raw - skb->data;
-		ipcso = (void *)&(skb->nh.iph->check) - (void *)skb->data;
-		ipcse = skb->h.raw - skb->data - 1;
-		tucss = skb->h.raw - skb->data;
-		tucso = (void *)&(skb->h.th->check) - (void *)skb->data;
+		iph = ip_hdr(skb);
+		iph->tot_len = 0;
+		iph->check = 0;
+		tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
+							 iph->daddr, 0,
+							 IPPROTO_TCP, 0);
+		ipcss = skb_network_offset(skb);
+		ipcso = (void *)&(iph->check) - (void *)skb->data;
+		ipcse = skb_transport_offset(skb) - 1;
+		tucss = skb_transport_offset(skb);
+		tucso = (void *)&(tcp_hdr(skb)->check) - (void *)skb->data;
 		tucse = 0;
 
 		i = adapter->tx_ring.next_to_use;
@@ -1243,7 +1246,7 @@
 
 	if(likely(skb->ip_summed == CHECKSUM_PARTIAL)) {
 		struct ixgb_buffer *buffer_info;
-		css = skb->h.raw - skb->data;
+		css = skb_transport_offset(skb);
 		cso = css + skb->csum_offset;
 
 		i = adapter->tx_ring.next_to_use;
@@ -2014,9 +2017,12 @@
 			    netdev_alloc_skb(netdev, length + NET_IP_ALIGN);
 			if (new_skb) {
 				skb_reserve(new_skb, NET_IP_ALIGN);
-				memcpy(new_skb->data - NET_IP_ALIGN,
-				       skb->data - NET_IP_ALIGN,
-				       length + NET_IP_ALIGN);
+				skb_copy_to_linear_data_offset(new_skb,
+							       -NET_IP_ALIGN,
+							       (skb->data -
+							        NET_IP_ALIGN),
+							       (length +
+							        NET_IP_ALIGN));
 				/* save the skb in buffer_info as good */
 				buffer_info->skb = skb;
 				skb = new_skb;
diff --git a/drivers/net/ixgb/ixgb_param.c b/drivers/net/ixgb/ixgb_param.c
index b27442a..5d5ddab 100644
--- a/drivers/net/ixgb/ixgb_param.c
+++ b/drivers/net/ixgb/ixgb_param.c
@@ -245,8 +245,6 @@
 	return -1;
 }
 
-#define LIST_LEN(l) (sizeof(l) / sizeof(l[0]))
-
 /**
  * ixgb_check_options - Range Checking for Command Line Parameters
  * @adapter: board private structure
@@ -284,7 +282,7 @@
 		} else {
 			tx_ring->count = opt.def;
 		}
-		IXGB_ROUNDUP(tx_ring->count, IXGB_REQ_TX_DESCRIPTOR_MULTIPLE);
+		tx_ring->count = ALIGN(tx_ring->count, IXGB_REQ_TX_DESCRIPTOR_MULTIPLE);
 	}
 	{ /* Receive Descriptor Count */
 		struct ixgb_option opt = {
@@ -303,7 +301,7 @@
 		} else {
 			rx_ring->count = opt.def;
 		}
-		IXGB_ROUNDUP(rx_ring->count, IXGB_REQ_RX_DESCRIPTOR_MULTIPLE);
+		rx_ring->count = ALIGN(rx_ring->count, IXGB_REQ_RX_DESCRIPTOR_MULTIPLE);
 	}
 	{ /* Receive Checksum Offload Enable */
 		struct ixgb_option opt = {
@@ -335,7 +333,7 @@
 			.name = "Flow Control",
 			.err  = "reading default settings from EEPROM",
 			.def  = ixgb_fc_tx_pause,
-			.arg  = { .l = { .nr = LIST_LEN(fc_list),
+			.arg  = { .l = { .nr = ARRAY_SIZE(fc_list),
 					 .p = fc_list }}
 		};
 
diff --git a/drivers/net/ixp2000/ixpdev.c b/drivers/net/ixp2000/ixpdev.c
index a4eccb1..6683afc 100644
--- a/drivers/net/ixp2000/ixpdev.c
+++ b/drivers/net/ixp2000/ixpdev.c
@@ -110,11 +110,10 @@
 
 		skb = dev_alloc_skb(desc->pkt_length + 2);
 		if (likely(skb != NULL)) {
-			skb->dev = nds[desc->channel];
 			skb_reserve(skb, 2);
 			eth_copy_and_sum(skb, buf, desc->pkt_length, 0);
 			skb_put(skb, desc->pkt_length);
-			skb->protocol = eth_type_trans(skb, skb->dev);
+			skb->protocol = eth_type_trans(skb, nds[desc->channel]);
 
 			skb->dev->last_rx = jiffies;
 
diff --git a/drivers/net/lance.c b/drivers/net/lance.c
index a384332..0fe96c8 100644
--- a/drivers/net/lance.c
+++ b/drivers/net/lance.c
@@ -988,7 +988,7 @@
 		if (lance_debug > 5)
 			printk("%s: bouncing a high-memory packet (%#x).\n",
 				   dev->name, (u32)isa_virt_to_bus(skb->data));
-		memcpy(&lp->tx_bounce_buffs[entry], skb->data, skb->len);
+		skb_copy_from_linear_data(skb, &lp->tx_bounce_buffs[entry], skb->len);
 		lp->tx_ring[entry].base =
 			((u32)isa_virt_to_bus((lp->tx_bounce_buffs + entry)) & 0xffffff) | 0x83000000;
 		dev_kfree_skb(skb);
@@ -1184,7 +1184,6 @@
 					}
 					break;
 				}
-				skb->dev = dev;
 				skb_reserve(skb,2);	/* 16 byte align */
 				skb_put(skb,pkt_len);	/* Make room */
 				eth_copy_and_sum(skb,
diff --git a/drivers/net/lasi_82596.c b/drivers/net/lasi_82596.c
index 452863d..0edcd12 100644
--- a/drivers/net/lasi_82596.c
+++ b/drivers/net/lasi_82596.c
@@ -801,7 +801,6 @@
 				lp->stats.rx_dropped++;
 			}
 			else {
-				skb->dev = dev;
 				if (!rx_in_place) {
 					/* 16 byte align the data fields */
 					dma_sync_single_for_cpu(lp->dev, (dma_addr_t)WSWAPchar(rbd->b_data), PKT_BUF_SZ, DMA_FROM_DEVICE);
diff --git a/drivers/net/lib8390.c b/drivers/net/lib8390.c
index e726c06..5c86e73 100644
--- a/drivers/net/lib8390.c
+++ b/drivers/net/lib8390.c
@@ -722,7 +722,6 @@
 			else
 			{
 				skb_reserve(skb,2);	/* IP headers on 16 byte boundaries */
-				skb->dev = dev;
 				skb_put(skb, pkt_len);	/* Make room */
 				ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame));
 				skb->protocol=eth_type_trans(skb,dev);
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
index 2b739fd..6ba6ed2 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -75,8 +75,9 @@
 #ifdef LOOPBACK_TSO
 static void emulate_large_send_offload(struct sk_buff *skb)
 {
-	struct iphdr *iph = skb->nh.iph;
-	struct tcphdr *th = (struct tcphdr*)(skb->nh.raw + (iph->ihl * 4));
+	struct iphdr *iph = ip_hdr(skb);
+	struct tcphdr *th = (struct tcphdr *)(skb_network_header(skb) +
+					      (iph->ihl * 4));
 	unsigned int doffset = (iph->ihl + th->doff) * 4;
 	unsigned int mtu = skb_shinfo(skb)->gso_size + doffset;
 	unsigned int offset = 0;
@@ -90,10 +91,11 @@
 		if (!nskb)
 			break;
 		skb_reserve(nskb, 32);
-		nskb->mac.raw = nskb->data - 14;
-		nskb->nh.raw = nskb->data;
-		iph = nskb->nh.iph;
-		memcpy(nskb->data, skb->nh.raw, doffset);
+		skb_set_mac_header(nskb, -ETH_HLEN);
+		skb_reset_network_header(nskb);
+		iph = ip_hdr(nskb);
+		skb_copy_to_linear_data(nskb, skb_network_header(skb),
+					doffset);
 		if (skb_copy_bits(skb,
 				  doffset + offset,
 				  nskb->data + doffset,
@@ -108,7 +110,7 @@
 		memcpy(nskb->cb, skb->cb, sizeof(skb->cb));
 		nskb->pkt_type = skb->pkt_type;
 
-		th = (struct tcphdr*)(nskb->nh.raw + iph->ihl*4);
+		th = (struct tcphdr *)(skb_network_header(nskb) + iph->ihl * 4);
 		iph->tot_len = htons(frag_size + doffset);
 		iph->id = htons(id);
 		iph->check = 0;
@@ -137,7 +139,6 @@
 	skb_orphan(skb);
 
 	skb->protocol = eth_type_trans(skb,dev);
-	skb->dev = dev;
 #ifndef LOOPBACK_MUST_CHECKSUM
 	skb->ip_summed = CHECKSUM_UNNECESSARY;
 #endif
@@ -145,7 +146,7 @@
 #ifdef LOOPBACK_TSO
 	if (skb_is_gso(skb)) {
 		BUG_ON(skb->protocol != htons(ETH_P_IP));
-		BUG_ON(skb->nh.iph->protocol != IPPROTO_TCP);
+		BUG_ON(ip_hdr(skb)->protocol != IPPROTO_TCP);
 
 		emulate_large_send_offload(skb);
 		return 0;
@@ -163,11 +164,9 @@
 	return 0;
 }
 
-static struct net_device_stats loopback_stats;
-
 static struct net_device_stats *get_stats(struct net_device *dev)
 {
-	struct net_device_stats *stats = &loopback_stats;
+	struct net_device_stats *stats = &dev->stats;
 	unsigned long bytes = 0;
 	unsigned long packets = 0;
 	int i;
@@ -207,7 +206,6 @@
 struct net_device loopback_dev = {
 	.name	 		= "lo",
 	.get_stats		= &get_stats,
-	.priv			= &loopback_stats,
 	.mtu			= (16 * 1024) + 20 + 20 + 12,
 	.hard_start_xmit	= loopback_xmit,
 	.hard_header		= eth_header,
diff --git a/drivers/net/lp486e.c b/drivers/net/lp486e.c
index 177c502..5fc18da 100644
--- a/drivers/net/lp486e.c
+++ b/drivers/net/lp486e.c
@@ -676,7 +676,6 @@
 			return 1;
 		}
 
-		skb->dev = dev;
 		memcpy(skb_put(skb,pkt_len), rfd->data, pkt_len);
 
 		skb->protocol = eth_type_trans(skb,dev);
diff --git a/drivers/net/mac89x0.c b/drivers/net/mac89x0.c
index e960138..90e695d 100644
--- a/drivers/net/mac89x0.c
+++ b/drivers/net/mac89x0.c
@@ -530,7 +530,6 @@
 		return;
 	}
 	skb_put(skb, length);
-	skb->dev = dev;
 
 	memcpy_fromio(skb->data, dev->mem_start + PP_RxFrame, length);
 
diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index 2e9571b..0e04f7a 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -357,7 +357,6 @@
 	}
 
 	skb_reserve(skb, RX_OFFSET);
-	skb->dev = bp->dev;
 	skb->ip_summed = CHECKSUM_NONE;
 	skb_put(skb, len);
 
@@ -368,9 +367,10 @@
 			BUG_ON(frag != last_frag);
 			frag_len = len - offset;
 		}
-		memcpy(skb->data + offset,
-		       bp->rx_buffers + (RX_BUFFER_SIZE * frag),
-		       frag_len);
+		skb_copy_to_linear_data_offset(skb, offset,
+					       (bp->rx_buffers +
+					        (RX_BUFFER_SIZE * frag)),
+					       frag_len);
 		offset += RX_BUFFER_SIZE;
 		bp->rx_ring[frag].addr &= ~MACB_BIT(RX_USED);
 		wmb();
@@ -576,7 +576,8 @@
 	int i;
 	dev_dbg(&bp->pdev->dev,
 		"start_xmit: len %u head %p data %p tail %p end %p\n",
-		skb->len, skb->head, skb->data, skb->tail, skb->end);
+		skb->len, skb->head, skb->data,
+		skb_tail_pointer(skb), skb_end_pointer(skb));
 	dev_dbg(&bp->pdev->dev,
 		"data:");
 	for (i = 0; i < 16; i++)
diff --git a/drivers/net/mace.c b/drivers/net/mace.c
index 9ec24f0..b3bd623 100644
--- a/drivers/net/mace.c
+++ b/drivers/net/mace.c
@@ -939,7 +939,6 @@
 		else	/* Ethernet header; mace includes FCS */
 		    nb -= 8;
 		skb_put(skb, nb);
-		skb->dev = dev;
 		skb->protocol = eth_type_trans(skb, dev);
 		mp->stats.rx_bytes += skb->len;
 		netif_rx(skb);
diff --git a/drivers/net/macmace.c b/drivers/net/macmace.c
index 5d541e8..27911c0 100644
--- a/drivers/net/macmace.c
+++ b/drivers/net/macmace.c
@@ -420,8 +420,7 @@
 	mp->stats.tx_bytes += skb->len;
 
 	/* We need to copy into our xmit buffer to take care of alignment and caching issues */
-
-	memcpy((void *) mp->tx_ring, skb->data, skb->len);
+	skb_copy_from_linear_data(skb, mp->tx_ring, skb->len);
 
 	/* load the Tx DMA and fire it off */
 
@@ -621,7 +620,6 @@
 	skb_reserve(skb,2);
 	memcpy(skb_put(skb, mf->len), mf->data, mf->len);
 
-	skb->dev = dev;
 	skb->protocol = eth_type_trans(skb, dev);
 	netif_rx(skb);
 	dev->last_rx = jiffies;
diff --git a/drivers/net/meth.c b/drivers/net/meth.c
index 7e69ca6..0343ea1 100644
--- a/drivers/net/meth.c
+++ b/drivers/net/meth.c
@@ -421,7 +421,6 @@
 					/* Write metadata, and then pass to the receive level */
 					skb_put(skb_c, len);
 					priv->rx_skbs[priv->rx_write] = skb;
-					skb_c->dev = dev;
 					skb_c->protocol = eth_type_trans(skb_c, dev);
 					dev->last_rx = jiffies;
 					priv->stats.rx_packets++;
@@ -609,7 +608,7 @@
 
 	desc->header.raw = METH_TX_CMD_INT_EN | (len-1) | ((128-len) << 16);
 	/* maybe I should set whole thing to 0 first... */
-	memcpy(desc->data.dt + (120 - len), skb->data, skb->len);
+	skb_copy_from_linear_data(skb, desc->data.dt + (120 - len), skb->len);
 	if (skb->len < len)
 		memset(desc->data.dt + 120 - len + skb->len, 0, len-skb->len);
 }
@@ -627,8 +626,8 @@
 
 	/* unaligned part */
 	if (unaligned_len) {
-		memcpy(desc->data.dt + (120 - unaligned_len),
-		       skb->data, unaligned_len);
+		skb_copy_from_linear_data(skb, desc->data.dt + (120 - unaligned_len),
+			      unaligned_len);
 		desc->header.raw |= (128 - unaligned_len) << 16;
 	}
 
@@ -653,8 +652,8 @@
 	desc->header.raw = METH_TX_CMD_INT_EN | TX_CATBUF1 | TX_CATBUF2| (skb->len - 1);
 	/* unaligned part */
 	if (unaligned_len){
-		memcpy(desc->data.dt + (120 - unaligned_len),
-		       skb->data, unaligned_len);
+		skb_copy_from_linear_data(skb, desc->data.dt + (120 - unaligned_len),
+			      unaligned_len);
 		desc->header.raw |= (128 - unaligned_len) << 16;
 	}
 
diff --git a/drivers/net/mii.c b/drivers/net/mii.c
index 2912a34..9205605 100644
--- a/drivers/net/mii.c
+++ b/drivers/net/mii.c
@@ -33,6 +33,13 @@
 #include <linux/ethtool.h>
 #include <linux/mii.h>
 
+/**
+ * mii_ethtool_gset - get settings that are specified in @ecmd
+ * @mii: MII interface
+ * @ecmd: requested ethtool_cmd
+ *
+ * Returns 0 for success, negative on error.
+ */
 int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
 {
 	struct net_device *dev = mii->dev;
@@ -114,6 +121,13 @@
 	return 0;
 }
 
+/**
+ * mii_ethtool_sset - set settings that are specified in @ecmd
+ * @mii: MII interface
+ * @ecmd: requested ethtool_cmd
+ *
+ * Returns 0 for success, negative on error.
+ */
 int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
 {
 	struct net_device *dev = mii->dev;
@@ -207,6 +221,10 @@
 	return 0;
 }
 
+/**
+ * mii_check_gmii_support - check if the MII supports Gb interfaces
+ * @mii: the MII interface
+ */
 int mii_check_gmii_support(struct mii_if_info *mii)
 {
 	int reg;
@@ -221,6 +239,12 @@
 	return 0;
 }
 
+/**
+ * mii_link_ok - is link status up/ok
+ * @mii: the MII interface
+ *
+ * Returns 1 if the MII reports link status up/ok, 0 otherwise.
+ */
 int mii_link_ok (struct mii_if_info *mii)
 {
 	/* first, a dummy read, needed to latch some MII phys */
@@ -230,6 +254,12 @@
 	return 0;
 }
 
+/**
+ * mii_nway_restart - restart NWay (autonegotiation) for this interface
+ * @mii: the MII interface
+ *
+ * Returns 0 on success, negative on error.
+ */
 int mii_nway_restart (struct mii_if_info *mii)
 {
 	int bmcr;
@@ -247,6 +277,14 @@
 	return r;
 }
 
+/**
+ * mii_check_link - check MII link status
+ * @mii: MII interface
+ *
+ * If the link status changed (previous != current), call
+ * netif_carrier_on() if current link status is Up or call
+ * netif_carrier_off() if current link status is Down.
+ */
 void mii_check_link (struct mii_if_info *mii)
 {
 	int cur_link = mii_link_ok(mii);
@@ -258,6 +296,15 @@
 		netif_carrier_off(mii->dev);
 }
 
+/**
+ * mii_check_media - check the MII interface for a duplex change
+ * @mii: the MII interface
+ * @ok_to_print: OK to print link up/down messages
+ * @init_media: OK to save duplex mode in @mii
+ *
+ * Returns 1 if the duplex mode changed, 0 if not.
+ * If the media type is forced, always returns 0.
+ */
 unsigned int mii_check_media (struct mii_if_info *mii,
 			      unsigned int ok_to_print,
 			      unsigned int init_media)
@@ -326,6 +373,16 @@
 	return 0; /* duplex did not change */
 }
 
+/**
+ * generic_mii_ioctl - main MII ioctl interface
+ * @mii_if: the MII interface
+ * @mii_data: MII ioctl data structure
+ * @cmd: MII ioctl command
+ * @duplex_chg_out: pointer to @duplex_changed status if there was no
+ *	ioctl error
+ *
+ * Returns 0 on success, negative on error.
+ */
 int generic_mii_ioctl(struct mii_if_info *mii_if,
 		      struct mii_ioctl_data *mii_data, int cmd,
 		      unsigned int *duplex_chg_out)
diff --git a/drivers/net/mipsnet.c b/drivers/net/mipsnet.c
index f42b9e2..638a279 100644
--- a/drivers/net/mipsnet.c
+++ b/drivers/net/mipsnet.c
@@ -26,8 +26,6 @@
 	struct net_device_stats stats;
 };
 
-static struct platform_device *mips_plat_dev;
-
 static char mipsnet_string[] = "mipsnet";
 
 /*
@@ -101,7 +99,6 @@
 	if (ioiocpy_frommipsnet(dev, skb_put(skb, len), len))
 		return -EFAULT;
 
-	skb->dev = dev;
 	skb->protocol = eth_type_trans(skb, dev);
 	skb->ip_summed = CHECKSUM_UNNECESSARY;
 
@@ -298,64 +295,17 @@
 	.remove	= __devexit_p(mipsnet_device_remove),
 };
 
-static void mipsnet_platform_release(struct device *device)
-{
-	struct platform_device *pldev;
-
-	/* free device */
-	pldev = to_platform_device(device);
-	kfree(pldev);
-}
-
 static int __init mipsnet_init_module(void)
 {
-	struct platform_device *pldev;
 	int err;
 
 	printk(KERN_INFO "MIPSNet Ethernet driver. Version: %s. "
 	       "(c)2005 MIPS Technologies, Inc.\n", MIPSNET_VERSION);
 
-	if (driver_register(&mipsnet_driver)) {
+	err = driver_register(&mipsnet_driver);
+	if (err)
 		printk(KERN_ERR "Driver registration failed\n");
-		err = -ENODEV;
-		goto out;
-	}
 
-        if (!(pldev = kmalloc (sizeof (*pldev), GFP_KERNEL))) {
-		err = -ENOMEM;
-		goto out_unregister_driver;
-	}
-
-	memset (pldev, 0, sizeof (*pldev));
-	pldev->name		= mipsnet_string;
-	pldev->id		= 0;
-	pldev->dev.release	= mipsnet_platform_release;
-
-	if (platform_device_register(pldev)) {
-		err = -ENODEV;
-		goto out_free_pldev;
-	}
-
-        if (!pldev->dev.driver) {
-		/*
-		 * The driver was not bound to this device, there was
-                 * no hardware at this address. Unregister it, as the
-		 * release fuction will take care of freeing the
-		 * allocated structure
-		 */
-		platform_device_unregister (pldev);
-	}
-
-	mips_plat_dev		= pldev;
-
-	return 0;
-
-out_free_pldev:
-	kfree(pldev);
-
-out_unregister_driver:
-	driver_unregister(&mipsnet_driver);
-out:
 	return err;
 }
 
diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c
index 8015a7c..1799eee 100644
--- a/drivers/net/mv643xx_eth.c
+++ b/drivers/net/mv643xx_eth.c
@@ -51,8 +51,8 @@
 #include "mv643xx_eth.h"
 
 /* Static function declarations */
-static void eth_port_uc_addr_get(struct net_device *dev,
-						unsigned char *MacAddr);
+static void eth_port_uc_addr_get(unsigned int port_num, unsigned char *p_addr);
+static void eth_port_uc_addr_set(unsigned int port_num, unsigned char *p_addr);
 static void eth_port_set_multicast_list(struct net_device *);
 static void mv643xx_eth_port_enable_tx(unsigned int port_num,
 						unsigned int queues);
@@ -434,7 +434,6 @@
 			 * received packet
 			 */
 			skb_put(skb, pkt_info.byte_cnt - 4);
-			skb->dev = dev;
 
 			if (pkt_info.cmd_sts & ETH_LAYER_4_CHECKSUM_OK) {
 				skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -1162,15 +1161,15 @@
 
 		cmd_sts |= ETH_GEN_TCP_UDP_CHECKSUM |
 			   ETH_GEN_IP_V_4_CHECKSUM  |
-			   skb->nh.iph->ihl << ETH_TX_IHL_SHIFT;
+			   ip_hdr(skb)->ihl << ETH_TX_IHL_SHIFT;
 
-		switch (skb->nh.iph->protocol) {
+		switch (ip_hdr(skb)->protocol) {
 		case IPPROTO_UDP:
 			cmd_sts |= ETH_UDP_FRAME;
-			desc->l4i_chk = skb->h.uh->check;
+			desc->l4i_chk = udp_hdr(skb)->check;
 			break;
 		case IPPROTO_TCP:
-			desc->l4i_chk = skb->h.th->check;
+			desc->l4i_chk = tcp_hdr(skb)->check;
 			break;
 		default:
 			BUG();
@@ -1382,7 +1381,7 @@
 	port_num = mp->port_num = pd->port_number;
 
 	/* set default config values */
-	eth_port_uc_addr_get(dev, dev->dev_addr);
+	eth_port_uc_addr_get(port_num, dev->dev_addr);
 	mp->rx_ring_size = MV643XX_ETH_PORT_DEFAULT_RECEIVE_QUEUE_SIZE;
 	mp->tx_ring_size = MV643XX_ETH_PORT_DEFAULT_TRANSMIT_QUEUE_SIZE;
 
@@ -1840,26 +1839,9 @@
 }
 
 /*
- * eth_port_uc_addr_set - This function Set the port Unicast address.
- *
- * DESCRIPTION:
- *		This function Set the port Ethernet MAC address.
- *
- * INPUT:
- *	unsigned int	eth_port_num	Port number.
- *	char *		p_addr		Address to be set
- *
- * OUTPUT:
- *	Set MAC address low and high registers. also calls
- *	eth_port_set_filter_table_entry() to set the unicast
- *	table with the proper information.
- *
- * RETURN:
- *	N/A.
- *
+ * eth_port_uc_addr_set - Write a MAC address into the port's hw registers
  */
-static void eth_port_uc_addr_set(unsigned int eth_port_num,
-							unsigned char *p_addr)
+static void eth_port_uc_addr_set(unsigned int port_num, unsigned char *p_addr)
 {
 	unsigned int mac_h;
 	unsigned int mac_l;
@@ -1869,40 +1851,24 @@
 	mac_h = (p_addr[0] << 24) | (p_addr[1] << 16) | (p_addr[2] << 8) |
 							(p_addr[3] << 0);
 
-	mv_write(MV643XX_ETH_MAC_ADDR_LOW(eth_port_num), mac_l);
-	mv_write(MV643XX_ETH_MAC_ADDR_HIGH(eth_port_num), mac_h);
+	mv_write(MV643XX_ETH_MAC_ADDR_LOW(port_num), mac_l);
+	mv_write(MV643XX_ETH_MAC_ADDR_HIGH(port_num), mac_h);
 
-	/* Accept frames of this address */
-	table = MV643XX_ETH_DA_FILTER_UNICAST_TABLE_BASE(eth_port_num);
+	/* Accept frames with this address */
+	table = MV643XX_ETH_DA_FILTER_UNICAST_TABLE_BASE(port_num);
 	eth_port_set_filter_table_entry(table, p_addr[5] & 0x0f);
 }
 
 /*
- * eth_port_uc_addr_get - This function retrieves the port Unicast address
- * (MAC address) from the ethernet hw registers.
- *
- * DESCRIPTION:
- *		This function retrieves the port Ethernet MAC address.
- *
- * INPUT:
- *	unsigned int	eth_port_num	Port number.
- *	char		*MacAddr	pointer where the MAC address is stored
- *
- * OUTPUT:
- *	Copy the MAC address to the location pointed to by MacAddr
- *
- * RETURN:
- *	N/A.
- *
+ * eth_port_uc_addr_get - Read the MAC address from the port's hw registers
  */
-static void eth_port_uc_addr_get(struct net_device *dev, unsigned char *p_addr)
+static void eth_port_uc_addr_get(unsigned int port_num, unsigned char *p_addr)
 {
-	struct mv643xx_private *mp = netdev_priv(dev);
 	unsigned int mac_h;
 	unsigned int mac_l;
 
-	mac_h = mv_read(MV643XX_ETH_MAC_ADDR_HIGH(mp->port_num));
-	mac_l = mv_read(MV643XX_ETH_MAC_ADDR_LOW(mp->port_num));
+	mac_h = mv_read(MV643XX_ETH_MAC_ADDR_HIGH(port_num));
+	mac_l = mv_read(MV643XX_ETH_MAC_ADDR_LOW(port_num));
 
 	p_addr[0] = (mac_h >> 24) & 0xff;
 	p_addr[1] = (mac_h >> 16) & 0xff;
diff --git a/drivers/net/mv643xx_eth.h b/drivers/net/mv643xx_eth.h
index 7d4e90c..82f8c0c 100644
--- a/drivers/net/mv643xx_eth.h
+++ b/drivers/net/mv643xx_eth.h
@@ -346,10 +346,6 @@
 static void eth_port_reset(unsigned int eth_port_num);
 static void eth_port_start(struct net_device *dev);
 
-/* Port MAC address routines */
-static void eth_port_uc_addr_set(unsigned int eth_port_num,
-				 unsigned char *p_addr);
-
 /* PHY and MIB routines */
 static void ethernet_phy_reset(unsigned int eth_port_num);
 
diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c
index f8efe0e..16e3c43 100644
--- a/drivers/net/myri10ge/myri10ge.c
+++ b/drivers/net/myri10ge/myri10ge.c
@@ -879,7 +879,7 @@
 	 * skb_pull() (for ether_pad and eth_type_trans()) requires
 	 * the beginning of the packet in skb_headlen(), move it
 	 * manually */
-	memcpy(skb->data, va, hlen);
+	skb_copy_to_linear_data(skb, va, hlen);
 	skb_shinfo(skb)->frags[0].page_offset += hlen;
 	skb_shinfo(skb)->frags[0].size -= hlen;
 	skb->data_len -= hlen;
@@ -1020,7 +1020,6 @@
 		skb_shinfo(skb)->nr_frags = 0;
 	}
 	skb->protocol = eth_type_trans(skb, dev);
-	skb->dev = dev;
 
 	if (mgp->csum_flag) {
 		if ((skb->protocol == htons(ETH_P_IP)) ||
@@ -2030,7 +2029,7 @@
 	odd_flag = 0;
 	flags = (MXGEFW_FLAGS_NO_TSO | MXGEFW_FLAGS_FIRST);
 	if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) {
-		cksum_offset = (skb->h.raw - skb->data);
+		cksum_offset = skb_transport_offset(skb);
 		pseudo_hdr_offset = cksum_offset + skb->csum_offset;
 		/* If the headers are excessively large, then we must
 		 * fall back to a software checksum */
@@ -2055,7 +2054,7 @@
 		 * send loop that we are still in the
 		 * header portion of the TSO packet.
 		 * TSO header must be at most 134 bytes long */
-		cum_len = -((skb->h.raw - skb->data) + (skb->h.th->doff << 2));
+		cum_len = -(skb_transport_offset(skb) + tcp_hdrlen(skb));
 
 		/* for TSO, pseudo_hdr_offset holds mss.
 		 * The firmware figures out where to put
diff --git a/drivers/net/myri_sbus.c b/drivers/net/myri_sbus.c
index ee26ef5..13444da 100644
--- a/drivers/net/myri_sbus.c
+++ b/drivers/net/myri_sbus.c
@@ -368,7 +368,7 @@
 	struct ethhdr *eth;
 	unsigned char *rawp;
 
-	skb->mac.raw = (((unsigned char *)skb->data) + MYRI_PAD_LEN);
+	skb_set_mac_header(skb, MYRI_PAD_LEN);
 	skb_pull(skb, dev->hard_header_len);
 	eth = eth_hdr(skb);
 
@@ -502,7 +502,7 @@
 			copy_skb->dev = dev;
 			DRX(("resv_and_put "));
 			skb_put(copy_skb, len);
-			memcpy(copy_skb->data, skb->data, len);
+			skb_copy_from_linear_data(skb, copy_skb->data, len);
 
 			/* Reuse original ring buffer. */
 			DRX(("reuse "));
diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c
index 349b96a..a8d7ff2 100644
--- a/drivers/net/natsemi.c
+++ b/drivers/net/natsemi.c
@@ -2289,7 +2289,6 @@
 			 * without copying to a minimally-sized skbuff. */
 			if (pkt_len < rx_copybreak
 			    && (skb = dev_alloc_skb(pkt_len + RX_OFFSET)) != NULL) {
-				skb->dev = dev;
 				/* 16 byte align the IP header */
 				skb_reserve(skb, RX_OFFSET);
 				pci_dma_sync_single_for_cpu(np->pci_dev,
diff --git a/drivers/net/netx-eth.c b/drivers/net/netx-eth.c
index a53644f..2b8da0a 100644
--- a/drivers/net/netx-eth.c
+++ b/drivers/net/netx-eth.c
@@ -168,7 +168,6 @@
 		FIFO_PTR_SEGMENT(seg) | FIFO_PTR_FRAMENO(frameno));
 
 	ndev->last_rx = jiffies;
-	skb->dev = ndev;
 	skb->protocol = eth_type_trans(skb, ndev);
 	netif_rx(skb);
 	priv->stats.rx_packets++;
diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h
index dd8ce35..ad6688e 100644
--- a/drivers/net/netxen/netxen_nic.h
+++ b/drivers/net/netxen/netxen_nic.h
@@ -64,9 +64,9 @@
 #include "netxen_nic_hw.h"
 
 #define _NETXEN_NIC_LINUX_MAJOR 3
-#define _NETXEN_NIC_LINUX_MINOR 3
-#define _NETXEN_NIC_LINUX_SUBVERSION 3
-#define NETXEN_NIC_LINUX_VERSIONID  "3.3.3"
+#define _NETXEN_NIC_LINUX_MINOR 4
+#define _NETXEN_NIC_LINUX_SUBVERSION 2
+#define NETXEN_NIC_LINUX_VERSIONID  "3.4.2"
 
 #define NUM_FLASH_SECTORS (64)
 #define FLASH_SECTOR_SIZE (64 * 1024)
@@ -131,8 +131,8 @@
 #define FIRST_PAGE_GROUP_START	0
 #define FIRST_PAGE_GROUP_END	0x100000
 
-#define SECOND_PAGE_GROUP_START	0x4000000
-#define SECOND_PAGE_GROUP_END	0x66BC000
+#define SECOND_PAGE_GROUP_START	0x6000000
+#define SECOND_PAGE_GROUP_END	0x68BC000
 
 #define THIRD_PAGE_GROUP_START	0x70E4000
 #define THIRD_PAGE_GROUP_END	0x8000000
@@ -205,6 +205,8 @@
 
 #define MAX_CMD_DESCRIPTORS		1024
 #define MAX_RCV_DESCRIPTORS		16384
+#define MAX_CMD_DESCRIPTORS_HOST	(MAX_CMD_DESCRIPTORS / 4)
+#define MAX_RCV_DESCRIPTORS_1G		(MAX_RCV_DESCRIPTORS / 4)
 #define MAX_JUMBO_RCV_DESCRIPTORS	1024
 #define MAX_LRO_RCV_DESCRIPTORS		64
 #define MAX_RCVSTATUS_DESCRIPTORS	MAX_RCV_DESCRIPTORS
@@ -230,7 +232,9 @@
 	(((index) + (count)) & ((length) - 1))
 
 #define MPORT_SINGLE_FUNCTION_MODE 0x1111
+#define MPORT_MULTI_FUNCTION_MODE 0x2222
 
+#include "netxen_nic_phan_reg.h"
 extern unsigned long long netxen_dma_mask;
 extern unsigned long last_schedule_time;
 
@@ -300,6 +304,8 @@
 
 #define netxen_set_cmd_desc_port(cmd_desc, var)	\
 	((cmd_desc)->port_ctxid |= ((var) & 0x0F))
+#define netxen_set_cmd_desc_ctxid(cmd_desc, var)	\
+	((cmd_desc)->port_ctxid |= ((var) & 0xF0))
 
 #define netxen_set_cmd_desc_flags(cmd_desc, val)	\
 	((cmd_desc)->flags_opcode &= ~cpu_to_le16(0x7f), \
@@ -442,7 +448,7 @@
 	/* Bit pattern: 0-6 lro_count indicates frag sequence,
 	   7 last_frag indicates last frag */
 	u8 lro;
-} __attribute__ ((aligned(8)));
+} __attribute__ ((aligned(16)));
 
 enum {
 	NETXEN_RCV_PEG_0 = 0,
@@ -703,10 +709,8 @@
 #else
 #define DPRINTK(klevel, fmt, args...)	do { \
 	printk(KERN_##klevel PFX "%s: %s: " fmt, __FUNCTION__,\
-		(adapter != NULL && \
-		adapter->port[0] != NULL && \
-		adapter->port[0]->netdev != NULL) ? \
-		adapter->port[0]->netdev->name : NULL, \
+		(adapter != NULL && adapter->netdev != NULL) ? \
+		adapter->netdev->name : NULL, \
 		## args); } while(0)
 #endif
 
@@ -722,6 +726,18 @@
 	u32 length;
 };
 
+#define _netxen_set_bits(config_word, start, bits, val)	{\
+	unsigned long long __tmask = (((1ULL << (bits)) - 1) << (start));\
+	unsigned long long __tvalue = (val);    \
+	(config_word) &= ~__tmask;      \
+	(config_word) |= (((__tvalue) << (start)) & __tmask); \
+}
+	
+#define _netxen_clear_bits(config_word, start, bits) {\
+	unsigned long long __tmask = (((1ULL << (bits)) - 1) << (start));  \
+	(config_word) &= ~__tmask; \
+}		
+
 /*    Following defines are for the state of the buffers    */
 #define	NETXEN_BUFFER_FREE	0
 #define	NETXEN_BUFFER_BUSY	1
@@ -766,6 +782,8 @@
 	void __iomem *pci_base0;
 	void __iomem *pci_base1;
 	void __iomem *pci_base2;
+	unsigned long first_page_group_end;
+	unsigned long first_page_group_start;
 	void __iomem *db_base;
 	unsigned long db_len;
 
@@ -780,6 +798,7 @@
 	struct pci_dev *cmd_desc_pdev;
 	dma_addr_t cmd_desc_phys_addr;
 	struct netxen_adapter *adapter;
+	int pci_func;
 };
 
 #define RCV_RING_LRO	RCV_DESC_LRO
@@ -788,17 +807,27 @@
 #define ETHERNET_FCS_SIZE		4
 
 struct netxen_adapter_stats {
-	u64 ints;
-	u64 hostints;
-	u64 otherints;
-	u64 process_rcv;
-	u64 process_xmit;
-	u64 noxmitdone;
-	u64 xmitcsummed;
-	u64 post_called;
-	u64 posted;
-	u64 lastposted;
-	u64 goodskbposts;
+	u64  rcvdbadskb;
+	u64  xmitcalled;
+	u64  xmitedframes;
+	u64  xmitfinished;
+	u64  badskblen;
+	u64  nocmddescriptor;
+	u64  polled;
+	u64  uphappy;
+	u64  updropped;
+	u64  uplcong;
+	u64  uphcong;
+	u64  upmcong;
+	u64  updunno;
+	u64  skbfreed;
+	u64  txdropped;
+	u64  txnullskb;
+	u64  csummed;
+	u64  no_rcv;
+	u64  rxbytes;
+	u64  txbytes;
+	u64  ints;
 };
 
 /*
@@ -846,13 +875,20 @@
 
 struct netxen_adapter {
 	struct netxen_hardware_context ahw;
-	int port_count;		/* Number of configured ports  */
-	int active_ports;	/* Number of open ports */
-	struct netxen_port *port[NETXEN_MAX_PORTS];	/* ptr to each port  */
+	
+	struct netxen_adapter *master;
+	struct net_device *netdev;
+	struct pci_dev *pdev;
+	struct net_device_stats net_stats;
+	unsigned char mac_addr[ETH_ALEN];
+	int mtu;
+	int portnum;
+
 	spinlock_t tx_lock;
 	spinlock_t lock;
 	struct work_struct watchdog_task;
 	struct timer_list watchdog_timer;
+	struct work_struct  tx_timeout_task;
 
 	u32 curr_window;
 
@@ -875,6 +911,15 @@
 	u32 temp;
 
 	struct netxen_adapter_stats stats;
+	
+	u16 portno;
+	u16 link_speed;
+	u16 link_duplex;
+	u16 state;
+	u16 link_autoneg;
+	int rcsum;
+	int status;
+	spinlock_t stats_lock;
 
 	struct netxen_cmd_buffer *cmd_buf_arr;	/* Command buffers for xmit */
 
@@ -891,65 +936,23 @@
 	struct netxen_ring_ctx *ctx_desc;
 	struct pci_dev *ctx_desc_pdev;
 	dma_addr_t ctx_desc_phys_addr;
-	int (*enable_phy_interrupts) (struct netxen_adapter *, int);
-	int (*disable_phy_interrupts) (struct netxen_adapter *, int);
+	int (*enable_phy_interrupts) (struct netxen_adapter *);
+	int (*disable_phy_interrupts) (struct netxen_adapter *);
 	void (*handle_phy_intr) (struct netxen_adapter *);
-	int (*macaddr_set) (struct netxen_port *, netxen_ethernet_macaddr_t);
-	int (*set_mtu) (struct netxen_port *, int);
-	int (*set_promisc) (struct netxen_adapter *, int,
-			    netxen_niu_prom_mode_t);
-	int (*unset_promisc) (struct netxen_adapter *, int,
-			      netxen_niu_prom_mode_t);
-	int (*phy_read) (struct netxen_adapter *, long phy, long reg, u32 *);
-	int (*phy_write) (struct netxen_adapter *, long phy, long reg, u32 val);
+	int (*macaddr_set) (struct netxen_adapter *, netxen_ethernet_macaddr_t);
+	int (*set_mtu) (struct netxen_adapter *, int);
+	int (*set_promisc) (struct netxen_adapter *, netxen_niu_prom_mode_t);
+	int (*unset_promisc) (struct netxen_adapter *, netxen_niu_prom_mode_t);
+	int (*phy_read) (struct netxen_adapter *, long reg, u32 *);
+	int (*phy_write) (struct netxen_adapter *, long reg, u32 val);
 	int (*init_port) (struct netxen_adapter *, int);
 	void (*init_niu) (struct netxen_adapter *);
-	int (*stop_port) (struct netxen_adapter *, int);
+	int (*stop_port) (struct netxen_adapter *);
 };				/* netxen_adapter structure */
 
 /* Max number of xmit producer threads that can run simultaneously */
 #define	MAX_XMIT_PRODUCERS		16
 
-struct netxen_port_stats {
-	u64 rcvdbadskb;
-	u64 xmitcalled;
-	u64 xmitedframes;
-	u64 xmitfinished;
-	u64 badskblen;
-	u64 nocmddescriptor;
-	u64 polled;
-	u64 uphappy;
-	u64 updropped;
-	u64 uplcong;
-	u64 uphcong;
-	u64 upmcong;
-	u64 updunno;
-	u64 skbfreed;
-	u64 txdropped;
-	u64 txnullskb;
-	u64 csummed;
-	u64 no_rcv;
-	u64 rxbytes;
-	u64 txbytes;
-};
-
-struct netxen_port {
-	struct netxen_adapter *adapter;
-
-	u16 portnum;		/* GBE port number */
-	u16 link_speed;
-	u16 link_duplex;
-	u16 link_autoneg;
-
-	int flags;
-
-	struct net_device *netdev;
-	struct pci_dev *pdev;
-	struct net_device_stats net_stats;
-	struct netxen_port_stats stats;
-	struct work_struct tx_timeout_task;
-};
-
 #define PCI_OFFSET_FIRST_RANGE(adapter, off)    \
 	((adapter)->ahw.pci_base0 + (off))
 #define PCI_OFFSET_SECOND_RANGE(adapter, off)   \
@@ -987,32 +990,26 @@
 	return NULL;
 }
 
-int netxen_niu_xgbe_enable_phy_interrupts(struct netxen_adapter *adapter,
-					  int port);
-int netxen_niu_gbe_enable_phy_interrupts(struct netxen_adapter *adapter,
-					 int port);
-int netxen_niu_xgbe_disable_phy_interrupts(struct netxen_adapter *adapter,
-					   int port);
-int netxen_niu_gbe_disable_phy_interrupts(struct netxen_adapter *adapter,
-					  int port);
-int netxen_niu_xgbe_clear_phy_interrupts(struct netxen_adapter *adapter,
-					 int port);
-int netxen_niu_gbe_clear_phy_interrupts(struct netxen_adapter *adapter,
-					int port);
+int netxen_niu_xgbe_enable_phy_interrupts(struct netxen_adapter *adapter);
+int netxen_niu_gbe_enable_phy_interrupts(struct netxen_adapter *adapter);
+int netxen_niu_xgbe_disable_phy_interrupts(struct netxen_adapter *adapter);
+int netxen_niu_gbe_disable_phy_interrupts(struct netxen_adapter *adapter);
+int netxen_niu_xgbe_clear_phy_interrupts(struct netxen_adapter *adapter);
+int netxen_niu_gbe_clear_phy_interrupts(struct netxen_adapter *adapter);
 void netxen_nic_xgbe_handle_phy_intr(struct netxen_adapter *adapter);
 void netxen_nic_gbe_handle_phy_intr(struct netxen_adapter *adapter);
 void netxen_niu_gbe_set_mii_mode(struct netxen_adapter *adapter, int port,
 				 long enable);
 void netxen_niu_gbe_set_gmii_mode(struct netxen_adapter *adapter, int port,
 				  long enable);
-int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long phy, long reg,
+int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long reg,
 			    __u32 * readval);
-int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter, long phy,
+int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter,
 			     long reg, __u32 val);
 
 /* Functions available from netxen_nic_hw.c */
-int netxen_nic_set_mtu_xgb(struct netxen_port *port, int new_mtu);
-int netxen_nic_set_mtu_gb(struct netxen_port *port, int new_mtu);
+int netxen_nic_set_mtu_xgb(struct netxen_adapter *adapter, int new_mtu);
+int netxen_nic_set_mtu_gb(struct netxen_adapter *adapter, int new_mtu);
 void netxen_nic_init_niu_gb(struct netxen_adapter *adapter);
 void netxen_nic_pci_change_crbwindow(struct netxen_adapter *adapter, u32 wndw);
 void netxen_nic_reg_write(struct netxen_adapter *adapter, u64 off, u32 val);
@@ -1027,6 +1024,7 @@
 			   int len);
 void netxen_crb_writelit_adapter(struct netxen_adapter *adapter,
 				 unsigned long off, int data);
+int netxen_nic_erase_pxe(struct netxen_adapter *adapter);
 
 /* Functions from netxen_nic_init.c */
 void netxen_free_adapter_offload(struct netxen_adapter *adapter);
@@ -1051,11 +1049,8 @@
 
 /* Functions from netxen_nic_isr.c */
 void netxen_nic_isr_other(struct netxen_adapter *adapter);
-void netxen_indicate_link_status(struct netxen_adapter *adapter, u32 port,
-				 u32 link);
-void netxen_handle_port_int(struct netxen_adapter *adapter, u32 port,
-			    u32 enable);
-void netxen_nic_stop_all_ports(struct netxen_adapter *adapter);
+void netxen_indicate_link_status(struct netxen_adapter *adapter, u32 link);
+void netxen_handle_port_int(struct netxen_adapter *adapter, u32 enable);
 void netxen_initialize_adapter_sw(struct netxen_adapter *adapter);
 void netxen_initialize_adapter_hw(struct netxen_adapter *adapter);
 void *netxen_alloc(struct pci_dev *pdev, size_t sz, dma_addr_t * ptr,
@@ -1110,6 +1105,7 @@
 
 	if (!(adapter->flags & NETXEN_NIC_MSI_ENABLED)) {
 		mask = 0xbff;
+		writel(0X0, NETXEN_CRB_NORMALIZE(adapter, CRB_INT_VECTOR));
 		writel(mask, PCI_OFFSET_SECOND_RANGE(adapter,
 						     ISR_INT_TARGET_MASK));
 	}
@@ -1174,4 +1170,5 @@
 
 extern struct ethtool_ops netxen_nic_ethtool_ops;
 
+extern int physical_port[];	/* physical port # from virtual port.*/
 #endif				/* __NETXEN_NIC_H_ */
diff --git a/drivers/net/netxen/netxen_nic_ethtool.c b/drivers/net/netxen/netxen_nic_ethtool.c
index ee1b5a2..16fabb3 100644
--- a/drivers/net/netxen/netxen_nic_ethtool.c
+++ b/drivers/net/netxen/netxen_nic_ethtool.c
@@ -40,8 +40,8 @@
 #include <linux/ethtool.h>
 #include <linux/version.h>
 
-#include "netxen_nic_hw.h"
 #include "netxen_nic.h"
+#include "netxen_nic_hw.h"
 #include "netxen_nic_phan_reg.h"
 
 struct netxen_nic_stats {
@@ -50,8 +50,8 @@
 	int stat_offset;
 };
 
-#define NETXEN_NIC_STAT(m) sizeof(((struct netxen_port *)0)->m), \
-			offsetof(struct netxen_port, m)
+#define NETXEN_NIC_STAT(m) sizeof(((struct netxen_adapter *)0)->m), \
+			offsetof(struct netxen_adapter, m)
 
 #define NETXEN_NIC_PORT_WINDOW 0x10000
 #define NETXEN_NIC_INVALID_DATA 0xDEADBEEF
@@ -100,8 +100,7 @@
 static void
 netxen_nic_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo)
 {
-	struct netxen_port *port = netdev_priv(dev);
-	struct netxen_adapter *adapter = port->adapter;
+	struct netxen_adapter *adapter = netdev_priv(dev);
 	u32 fw_major = 0;
 	u32 fw_minor = 0;
 	u32 fw_build = 0;
@@ -115,7 +114,7 @@
 	fw_build = readl(NETXEN_CRB_NORMALIZE(adapter, NETXEN_FW_VERSION_SUB));
 	sprintf(drvinfo->fw_version, "%d.%d.%d", fw_major, fw_minor, fw_build);
 
-	strncpy(drvinfo->bus_info, pci_name(port->pdev), 32);
+	strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32);
 	drvinfo->n_stats = NETXEN_NIC_STATS_LEN;
 	drvinfo->testinfo_len = NETXEN_NIC_TEST_LEN;
 	drvinfo->regdump_len = NETXEN_NIC_REGS_LEN;
@@ -125,8 +124,7 @@
 static int
 netxen_nic_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
 {
-	struct netxen_port *port = netdev_priv(dev);
-	struct netxen_adapter *adapter = port->adapter;
+	struct netxen_adapter *adapter = netdev_priv(dev);
 	struct netxen_board_info *boardinfo = &adapter->ahw.boardcfg;
 
 	/* read which mode */
@@ -146,8 +144,8 @@
 		ecmd->port = PORT_TP;
 
 		if (netif_running(dev)) {
-			ecmd->speed = port->link_speed;
-			ecmd->duplex = port->link_duplex;
+			ecmd->speed = adapter->link_speed;
+			ecmd->duplex = adapter->link_duplex;
 		} else
 			return -EIO;	/* link absent */
 	} else if (adapter->ahw.board_type == NETXEN_NIC_XGBE) {
@@ -165,7 +163,7 @@
 	} else
 		return -EIO;
 
-	ecmd->phy_address = port->portnum;
+	ecmd->phy_address = adapter->portnum;
 	ecmd->transceiver = XCVR_EXTERNAL;
 
 	switch ((netxen_brdtype_t) boardinfo->board_type) {
@@ -179,7 +177,7 @@
 		ecmd->port = PORT_TP;
 		ecmd->autoneg = (boardinfo->board_type ==
 				 NETXEN_BRDTYPE_P2_SB31_10G_CX4) ?
-		    (AUTONEG_DISABLE) : (port->link_autoneg);
+		    (AUTONEG_DISABLE) : (adapter->link_autoneg);
 		break;
 	case NETXEN_BRDTYPE_P2_SB31_10G_HMEZ:
 	case NETXEN_BRDTYPE_P2_SB31_10G_IMEZ:
@@ -206,23 +204,22 @@
 static int
 netxen_nic_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
 {
-	struct netxen_port *port = netdev_priv(dev);
-	struct netxen_adapter *adapter = port->adapter;
+	struct netxen_adapter *adapter = netdev_priv(dev);
 	__u32 status;
 
 	/* read which mode */
 	if (adapter->ahw.board_type == NETXEN_NIC_GBE) {
 		/* autonegotiation */
 		if (adapter->phy_write
-		    && adapter->phy_write(adapter, port->portnum,
+		    && adapter->phy_write(adapter,
 					  NETXEN_NIU_GB_MII_MGMT_ADDR_AUTONEG,
 					  ecmd->autoneg) != 0)
 			return -EIO;
 		else
-			port->link_autoneg = ecmd->autoneg;
+			adapter->link_autoneg = ecmd->autoneg;
 
 		if (adapter->phy_read
-		    && adapter->phy_read(adapter, port->portnum,
+		    && adapter->phy_read(adapter,
 					 NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS,
 					 &status) != 0)
 			return -EIO;
@@ -245,13 +242,13 @@
 		if (ecmd->duplex == DUPLEX_FULL)
 			netxen_set_phy_duplex(status);
 		if (adapter->phy_write
-		    && adapter->phy_write(adapter, port->portnum,
+		    && adapter->phy_write(adapter,
 					  NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS,
 					  *((int *)&status)) != 0)
 			return -EIO;
 		else {
-			port->link_speed = ecmd->speed;
-			port->link_duplex = ecmd->duplex;
+			adapter->link_speed = ecmd->speed;
+			adapter->link_duplex = ecmd->duplex;
 		}
 	} else
 		return -EOPNOTSUPP;
@@ -360,15 +357,14 @@
 static void
 netxen_nic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p)
 {
-	struct netxen_port *port = netdev_priv(dev);
-	struct netxen_adapter *adapter = port->adapter;
+	struct netxen_adapter *adapter = netdev_priv(dev);
 	__u32 mode, *regs_buff = p;
 	void __iomem *addr;
 	int i, window;
 
 	memset(p, 0, NETXEN_NIC_REGS_LEN);
 	regs->version = (1 << 24) | (adapter->ahw.revision_id << 16) |
-	    (port->pdev)->device;
+	    (adapter->pdev)->device;
 	/* which mode */
 	NETXEN_NIC_LOCKED_READ_REG(NETXEN_NIU_MODE, &regs_buff[0]);
 	mode = regs_buff[0];
@@ -383,7 +379,8 @@
 		for (i = 3; niu_registers[mode].reg[i - 3] != -1; i++) {
 			/* GB: port specific registers */
 			if (mode == 0 && i >= 19)
-				window = port->portnum * NETXEN_NIC_PORT_WINDOW;
+				window = physical_port[adapter->portnum] *
+					NETXEN_NIC_PORT_WINDOW;
 
 			NETXEN_NIC_LOCKED_READ_REG(niu_registers[mode].
 						   reg[i - 3] + window,
@@ -395,15 +392,14 @@
 
 static u32 netxen_nic_test_link(struct net_device *dev)
 {
-	struct netxen_port *port = netdev_priv(dev);
-	struct netxen_adapter *adapter = port->adapter;
+	struct netxen_adapter *adapter = netdev_priv(dev);
 	__u32 status;
 	int val;
 
 	/* read which mode */
 	if (adapter->ahw.board_type == NETXEN_NIC_GBE) {
 		if (adapter->phy_read
-		    && adapter->phy_read(adapter, port->portnum,
+		    && adapter->phy_read(adapter,
 					 NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS,
 					 &status) != 0)
 			return -EIO;
@@ -422,15 +418,15 @@
 netxen_nic_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
 		      u8 * bytes)
 {
-	struct netxen_port *port = netdev_priv(dev);
-	struct netxen_adapter *adapter = port->adapter;
+	struct netxen_adapter *adapter = netdev_priv(dev);
 	int offset;
 	int ret;
 
 	if (eeprom->len == 0)
 		return -EINVAL;
 
-	eeprom->magic = (port->pdev)->vendor | ((port->pdev)->device << 16);
+	eeprom->magic = (adapter->pdev)->vendor | 
+			((adapter->pdev)->device << 16);
 	offset = eeprom->offset;
 
 	ret = netxen_rom_fast_read_words(adapter, offset, bytes, 
@@ -445,8 +441,7 @@
 netxen_nic_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
 			u8 * bytes)
 {
-	struct netxen_port *port = netdev_priv(dev);
-	struct netxen_adapter *adapter = port->adapter;
+	struct netxen_adapter *adapter = netdev_priv(dev);
 	int offset = eeprom->offset;
 	static int flash_start;
 	static int ready_to_flash;
@@ -516,8 +511,7 @@
 static void
 netxen_nic_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ring)
 {
-	struct netxen_port *port = netdev_priv(dev);
-	struct netxen_adapter *adapter = port->adapter;
+	struct netxen_adapter *adapter = netdev_priv(dev);
 	int i;
 
 	ring->rx_pending = 0;
@@ -541,19 +535,45 @@
 netxen_nic_get_pauseparam(struct net_device *dev,
 			  struct ethtool_pauseparam *pause)
 {
-	struct netxen_port *port = netdev_priv(dev);
-	struct netxen_adapter *adapter = port->adapter;
+	struct netxen_adapter *adapter = netdev_priv(dev);
 	__u32 val;
+	int port = physical_port[adapter->portnum];
 
 	if (adapter->ahw.board_type == NETXEN_NIC_GBE) {
+		if ((port < 0) || (port > NETXEN_NIU_MAX_GBE_PORTS))
+			return;
 		/* get flow control settings */
-		netxen_nic_read_w0(adapter,
-				   NETXEN_NIU_GB_MAC_CONFIG_0(port->portnum),
-				   &val);
+		netxen_nic_read_w0(adapter,NETXEN_NIU_GB_MAC_CONFIG_0(port),
+				&val);
 		pause->rx_pause = netxen_gb_get_rx_flowctl(val);
-		pause->tx_pause = netxen_gb_get_tx_flowctl(val);
-		/* get autoneg settings */
-		pause->autoneg = port->link_autoneg;
+		netxen_nic_read_w0(adapter, NETXEN_NIU_GB_PAUSE_CTL, &val);
+		switch (port) {
+			case 0:
+				pause->tx_pause = !(netxen_gb_get_gb0_mask(val));
+				break;
+			case 1:
+				pause->tx_pause = !(netxen_gb_get_gb1_mask(val));
+				break;
+			case 2:
+				pause->tx_pause = !(netxen_gb_get_gb2_mask(val));
+				break;
+			case 3:
+			default:
+				pause->tx_pause = !(netxen_gb_get_gb3_mask(val));
+				break;
+		}
+	} else if (adapter->ahw.board_type == NETXEN_NIC_XGBE) {
+		if ((port < 0) || (port > NETXEN_NIU_MAX_XG_PORTS))
+			return;
+		pause->rx_pause = 1;
+		netxen_nic_read_w0(adapter, NETXEN_NIU_XG_PAUSE_CTL, &val);
+		if (port == 0)
+			pause->tx_pause = !(netxen_xg_get_xg0_mask(val));
+		else
+			pause->tx_pause = !(netxen_xg_get_xg1_mask(val));
+	} else {
+		printk(KERN_ERR"%s: Unknown board type: %x\n", 
+				netxen_nic_driver_name, adapter->ahw.board_type);
 	}
 }
 
@@ -561,42 +581,76 @@
 netxen_nic_set_pauseparam(struct net_device *dev,
 			  struct ethtool_pauseparam *pause)
 {
-	struct netxen_port *port = netdev_priv(dev);
-	struct netxen_adapter *adapter = port->adapter;
+	struct netxen_adapter *adapter = netdev_priv(dev);
 	__u32 val;
-	unsigned int autoneg;
-
+	int port = physical_port[adapter->portnum];
 	/* read mode */
 	if (adapter->ahw.board_type == NETXEN_NIC_GBE) {
+		if ((port < 0) || (port > NETXEN_NIU_MAX_GBE_PORTS))
+			return -EIO;
 		/* set flow control */
 		netxen_nic_read_w0(adapter,
-				   NETXEN_NIU_GB_MAC_CONFIG_0(port->portnum),
-				   (u32 *) & val);
-		if (pause->tx_pause)
-			netxen_gb_tx_flowctl(val);
-		else
-			netxen_gb_unset_tx_flowctl(val);
+					NETXEN_NIU_GB_MAC_CONFIG_0(port), &val);
+		
 		if (pause->rx_pause)
 			netxen_gb_rx_flowctl(val);
 		else
 			netxen_gb_unset_rx_flowctl(val);
 
-		netxen_nic_write_w0(adapter,
-				    NETXEN_NIU_GB_MAC_CONFIG_0(port->portnum),
-				    *&val);
+		netxen_nic_write_w0(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(port),
+				val);
 		/* set autoneg */
-		autoneg = pause->autoneg;
-		if (adapter->phy_write
-		    && adapter->phy_write(adapter, port->portnum,
-					  NETXEN_NIU_GB_MII_MGMT_ADDR_AUTONEG,
-					  autoneg) != 0)
-			return -EIO;
-		else {
-			port->link_autoneg = pause->autoneg;
-			return 0;
+		netxen_nic_read_w0(adapter, NETXEN_NIU_GB_PAUSE_CTL, &val);
+		switch (port) {
+			case 0:
+				if (pause->tx_pause)
+					netxen_gb_unset_gb0_mask(val);
+				else
+					netxen_gb_set_gb0_mask(val);
+				break;
+			case 1:
+				if (pause->tx_pause)
+					netxen_gb_unset_gb1_mask(val);
+				else
+					netxen_gb_set_gb1_mask(val);
+				break;
+			case 2:
+				if (pause->tx_pause)
+					netxen_gb_unset_gb2_mask(val);
+				else
+					netxen_gb_set_gb2_mask(val);
+				break;
+			case 3:
+			default:
+				if (pause->tx_pause)
+					netxen_gb_unset_gb3_mask(val);
+				else
+					netxen_gb_set_gb3_mask(val);
+				break;
 		}
-	} else
-		return -EOPNOTSUPP;
+		netxen_nic_write_w0(adapter, NETXEN_NIU_GB_PAUSE_CTL, val);
+	} else if (adapter->ahw.board_type == NETXEN_NIC_XGBE) {
+		if ((port < 0) || (port > NETXEN_NIU_MAX_XG_PORTS))
+			return -EIO;
+		netxen_nic_read_w0(adapter, NETXEN_NIU_XG_PAUSE_CTL, &val);
+		if (port == 0) {
+			if (pause->tx_pause)
+				netxen_xg_unset_xg0_mask(val);
+			else
+				netxen_xg_set_xg0_mask(val);
+		} else {
+			if (pause->tx_pause)
+				netxen_xg_unset_xg1_mask(val);
+			else
+				netxen_xg_set_xg1_mask(val);
+		}
+		netxen_nic_write_w0(adapter, NETXEN_NIU_XG_PAUSE_CTL, val);			
+	} else {
+		printk(KERN_ERR "%s: Unknown board type: %x\n",
+				netxen_nic_driver_name, 
+				adapter->ahw.board_type);
+	}
+	return 0;
 }
 
 static int netxen_nic_reg_test(struct net_device *dev)
@@ -627,23 +681,12 @@
 netxen_nic_diag_test(struct net_device *dev, struct ethtool_test *eth_test,
 		     u64 * data)
 {
-	if (eth_test->flags == ETH_TEST_FL_OFFLINE) {	/* offline tests */
-		/* link test */
-		if ((data[1] = (u64) netxen_nic_test_link(dev)))
-			eth_test->flags |= ETH_TEST_FL_FAILED;
-
-		/* register tests */
-		if ((data[0] = netxen_nic_reg_test(dev)))
-			eth_test->flags |= ETH_TEST_FL_FAILED;
-	} else {		/* online tests */
-		/* register tests */
-		if((data[0] = netxen_nic_reg_test(dev)))
-			eth_test->flags |= ETH_TEST_FL_FAILED;
-
-		/* link test */
-		if ((data[1] = (u64) netxen_nic_test_link(dev)))
-			eth_test->flags |= ETH_TEST_FL_FAILED;
-	}
+	memset(data, 0, sizeof(uint64_t) * NETXEN_NIC_TEST_LEN);
+	if ((data[0] = netxen_nic_reg_test(dev)))
+		eth_test->flags |= ETH_TEST_FL_FAILED;
+	/* link test */
+	if ((data[1] = (u64) netxen_nic_test_link(dev)))
+		eth_test->flags |= ETH_TEST_FL_FAILED;
 }
 
 static void
@@ -675,12 +718,13 @@
 netxen_nic_get_ethtool_stats(struct net_device *dev,
 			     struct ethtool_stats *stats, u64 * data)
 {
-	struct netxen_port *port = netdev_priv(dev);
+	struct netxen_adapter *adapter = netdev_priv(dev);
 	int index;
 
 	for (index = 0; index < NETXEN_NIC_STATS_LEN; index++) {
 		char *p =
-		    (char *)port + netxen_nic_gstrings_stats[index].stat_offset;
+		    (char *)adapter +
+		    netxen_nic_gstrings_stats[index].stat_offset;
 		data[index] =
 		    (netxen_nic_gstrings_stats[index].sizeof_stat ==
 		     sizeof(u64)) ? *(u64 *) p : *(u32 *) p;
diff --git a/drivers/net/netxen/netxen_nic_hdr.h b/drivers/net/netxen/netxen_nic_hdr.h
index fe8b675..608e37b 100644
--- a/drivers/net/netxen/netxen_nic_hdr.h
+++ b/drivers/net/netxen/netxen_nic_hdr.h
@@ -467,6 +467,8 @@
 #define NETXEN_PCI_OCM1		(0x05100000UL)
 #define NETXEN_PCI_OCM1_MAX	(0x051fffffUL)
 #define NETXEN_PCI_CRBSPACE	(0x06000000UL)
+#define NETXEN_PCI_128MB_SIZE	(0x08000000UL)
+#define NETXEN_PCI_32MB_SIZE	(0x02000000UL)
 
 #define NETXEN_CRB_CAM	NETXEN_PCI_CRB_WINDOW(NETXEN_HW_PX_MAP_CRB_CAM)
 
@@ -484,6 +486,7 @@
 	/* 10 seconds before we give up */
 #define	NETXEN_NIU_PHY_WAITMAX		50
 #define	NETXEN_NIU_MAX_GBE_PORTS	4
+#define	NETXEN_NIU_MAX_XG_PORTS		2
 
 #define	NETXEN_NIU_MODE			(NETXEN_CRB_NIU + 0x00000)
 
@@ -527,6 +530,7 @@
 #define	NETXEN_NIU_XG_PAUSE_CTL		(NETXEN_CRB_NIU + 0x00098)
 #define	NETXEN_NIU_XG_PAUSE_LEVEL	(NETXEN_CRB_NIU + 0x000dc)
 #define	NETXEN_NIU_XG_SEL		(NETXEN_CRB_NIU + 0x00128)
+#define NETXEN_NIU_GB_PAUSE_CTL		(NETXEN_CRB_NIU + 0x0030c)
 
 #define NETXEN_NIU_FULL_LEVEL_XG	(NETXEN_CRB_NIU + 0x00450)
 
@@ -649,11 +653,19 @@
 #define PCIX_MS_WINDOW		(0x10204)
 #define PCIX_SN_WINDOW		(0x10208)
 #define PCIX_CRB_WINDOW		(0x10210)
+#define PCIX_CRB_WINDOW_F0	(0x10210)
+#define PCIX_CRB_WINDOW_F1	(0x10230)
+#define PCIX_CRB_WINDOW_F2	(0x10250)
+#define PCIX_CRB_WINDOW_F3	(0x10270)
 
 #define PCIX_TARGET_STATUS	(0x10118)
 #define PCIX_TARGET_MASK	(0x10128)
 
 #define PCIX_MSI_F0		(0x13000)
+#define PCIX_MSI_F1		(0x13004)
+#define PCIX_MSI_F2		(0x13008)
+#define PCIX_MSI_F3		(0x1300c)
+#define PCIX_MSI_F(i)		(0x13000+((i)*4))
 
 #define PCIX_PS_MEM_SPACE	(0x90000)
 
diff --git a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/netxen/netxen_nic_hw.c
index 6537574..baff17a 100644
--- a/drivers/net/netxen/netxen_nic_hw.c
+++ b/drivers/net/netxen/netxen_nic_hw.c
@@ -33,8 +33,225 @@
 
 #include "netxen_nic.h"
 #include "netxen_nic_hw.h"
+#define DEFINE_GLOBAL_RECV_CRB
 #include "netxen_nic_phan_reg.h"
 
+
+#include <net/ip.h>
+
+struct netxen_recv_crb recv_crb_registers[] = {
+	/*
+	 * Instance 0.
+	 */
+	{
+	 /* rcv_desc_crb: */
+	 {
+	  {
+	   /* crb_rcv_producer_offset: */
+	   NETXEN_NIC_REG(0x100),
+	   /* crb_rcv_consumer_offset: */
+	   NETXEN_NIC_REG(0x104),
+	   /* crb_gloablrcv_ring: */
+	   NETXEN_NIC_REG(0x108),
+	   /* crb_rcv_ring_size */
+	   NETXEN_NIC_REG(0x10c),
+
+	   },
+	  /* Jumbo frames */
+	  {
+	   /* crb_rcv_producer_offset: */
+	   NETXEN_NIC_REG(0x110),
+	   /* crb_rcv_consumer_offset: */
+	   NETXEN_NIC_REG(0x114),
+	   /* crb_gloablrcv_ring: */
+	   NETXEN_NIC_REG(0x118),
+	   /* crb_rcv_ring_size */
+	   NETXEN_NIC_REG(0x11c),
+	   },
+	  /* LRO */
+	  {
+	   /* crb_rcv_producer_offset: */
+	   NETXEN_NIC_REG(0x120),
+	   /* crb_rcv_consumer_offset: */
+	   NETXEN_NIC_REG(0x124),
+	   /* crb_gloablrcv_ring: */
+	   NETXEN_NIC_REG(0x128),
+	   /* crb_rcv_ring_size */
+	   NETXEN_NIC_REG(0x12c),
+	   }
+	  },
+	 /* crb_rcvstatus_ring: */
+	 NETXEN_NIC_REG(0x130),
+	 /* crb_rcv_status_producer: */
+	 NETXEN_NIC_REG(0x134),
+	 /* crb_rcv_status_consumer: */
+	 NETXEN_NIC_REG(0x138),
+	 /* crb_rcvpeg_state: */
+	 NETXEN_NIC_REG(0x13c),
+	 /* crb_status_ring_size */
+	 NETXEN_NIC_REG(0x140),
+
+	 },
+	/*
+	 * Instance 1,
+	 */
+	{
+	 /* rcv_desc_crb: */
+	 {
+	  {
+	   /* crb_rcv_producer_offset: */
+	   NETXEN_NIC_REG(0x144),
+	   /* crb_rcv_consumer_offset: */
+	   NETXEN_NIC_REG(0x148),
+	   /* crb_globalrcv_ring: */
+	   NETXEN_NIC_REG(0x14c),
+	   /* crb_rcv_ring_size */
+	   NETXEN_NIC_REG(0x150),
+
+	   },
+	  /* Jumbo frames */
+	  {
+	   /* crb_rcv_producer_offset: */
+	   NETXEN_NIC_REG(0x154),
+	   /* crb_rcv_consumer_offset: */
+	   NETXEN_NIC_REG(0x158),
+	   /* crb_globalrcv_ring: */
+	   NETXEN_NIC_REG(0x15c),
+	   /* crb_rcv_ring_size */
+	   NETXEN_NIC_REG(0x160),
+	   },
+	  /* LRO */
+	  {
+	   /* crb_rcv_producer_offset: */
+	   NETXEN_NIC_REG(0x164),
+	   /* crb_rcv_consumer_offset: */
+	   NETXEN_NIC_REG(0x168),
+	   /* crb_globalrcv_ring: */
+	   NETXEN_NIC_REG(0x16c),
+	   /* crb_rcv_ring_size */
+	   NETXEN_NIC_REG(0x170),
+	   }
+
+	  },
+	 /* crb_rcvstatus_ring: */
+	 NETXEN_NIC_REG(0x174),
+	 /* crb_rcv_status_producer: */
+	 NETXEN_NIC_REG(0x178),
+	 /* crb_rcv_status_consumer: */
+	 NETXEN_NIC_REG(0x17c),
+	 /* crb_rcvpeg_state: */
+	 NETXEN_NIC_REG(0x180),
+	 /* crb_status_ring_size */
+	 NETXEN_NIC_REG(0x184),
+	 },
+	/*
+	 * Instance 2,
+	 */
+	{
+	  {
+	    {
+	    /* crb_rcv_producer_offset: */
+	    NETXEN_NIC_REG(0x1d8),
+	    /* crb_rcv_consumer_offset: */
+	    NETXEN_NIC_REG(0x1dc),
+	    /* crb_gloablrcv_ring: */
+	    NETXEN_NIC_REG(0x1f0),
+	    /* crb_rcv_ring_size */
+	    NETXEN_NIC_REG(0x1f4),
+	    },
+	    /* Jumbo frames */
+	    {
+	    /* crb_rcv_producer_offset: */		    
+	    NETXEN_NIC_REG(0x1f8),
+	    /* crb_rcv_consumer_offset: */
+	    NETXEN_NIC_REG(0x1fc),
+	    /* crb_gloablrcv_ring: */
+	    NETXEN_NIC_REG(0x200),
+	    /* crb_rcv_ring_size */
+	    NETXEN_NIC_REG(0x204),
+	    },
+	    /* LRO */
+	    {
+	    /* crb_rcv_producer_offset: */
+	    NETXEN_NIC_REG(0x208),
+	    /* crb_rcv_consumer_offset: */
+	    NETXEN_NIC_REG(0x20c),
+	    /* crb_gloablrcv_ring: */
+	    NETXEN_NIC_REG(0x210),
+	    /* crb_rcv_ring_size */
+	    NETXEN_NIC_REG(0x214),
+	    }
+	  },
+	  /* crb_rcvstatus_ring: */
+	  NETXEN_NIC_REG(0x218),
+	  /* crb_rcv_status_producer: */
+	  NETXEN_NIC_REG(0x21c),
+	  /* crb_rcv_status_consumer: */
+	  NETXEN_NIC_REG(0x220),
+	  /* crb_rcvpeg_state: */
+	  NETXEN_NIC_REG(0x224),
+	  /* crb_status_ring_size */
+	  NETXEN_NIC_REG(0x228),
+	},
+	/*
+	 * Instance 3,
+	 */
+	{
+	  {
+	    {
+	    /* crb_rcv_producer_offset: */
+	    NETXEN_NIC_REG(0x22c),
+	    /* crb_rcv_consumer_offset: */
+	    NETXEN_NIC_REG(0x230),
+	    /* crb_gloablrcv_ring: */
+	    NETXEN_NIC_REG(0x234),
+	    /* crb_rcv_ring_size */
+	    NETXEN_NIC_REG(0x238),
+	    },
+	    /* Jumbo frames */
+	    {
+	    /* crb_rcv_producer_offset: */ 
+	    NETXEN_NIC_REG(0x23c),
+	    /* crb_rcv_consumer_offset: */
+	    NETXEN_NIC_REG(0x240),
+	    /* crb_gloablrcv_ring: */
+	    NETXEN_NIC_REG(0x244),
+	    /* crb_rcv_ring_size */
+	    NETXEN_NIC_REG(0x248),
+	    },
+	    /* LRO */
+	    {
+	    /* crb_rcv_producer_offset: */
+	    NETXEN_NIC_REG(0x24c),
+	    /* crb_rcv_consumer_offset: */
+	    NETXEN_NIC_REG(0x250),
+	    /* crb_gloablrcv_ring: */
+	    NETXEN_NIC_REG(0x254),
+	    /* crb_rcv_ring_size */
+	    NETXEN_NIC_REG(0x258),
+	    }
+	  },
+	  /* crb_rcvstatus_ring: */
+	  NETXEN_NIC_REG(0x25c),
+	  /* crb_rcv_status_producer: */
+	  NETXEN_NIC_REG(0x260),
+	  /* crb_rcv_status_consumer: */
+	  NETXEN_NIC_REG(0x264),
+	  /* crb_rcvpeg_state: */
+	  NETXEN_NIC_REG(0x268),
+	  /* crb_status_ring_size */
+	  NETXEN_NIC_REG(0x26c),
+	},
+};
+
+u64 ctx_addr_sig_regs[][3] = {
+	{NETXEN_NIC_REG(0x188), NETXEN_NIC_REG(0x18c), NETXEN_NIC_REG(0x1c0)},
+	{NETXEN_NIC_REG(0x190), NETXEN_NIC_REG(0x194), NETXEN_NIC_REG(0x1c4)},
+	{NETXEN_NIC_REG(0x198), NETXEN_NIC_REG(0x19c), NETXEN_NIC_REG(0x1c8)},
+	{NETXEN_NIC_REG(0x1a0), NETXEN_NIC_REG(0x1a4), NETXEN_NIC_REG(0x1cc)}
+};
+
+
 /*  PCI Windowing for DDR regions.  */
 
 #define ADDR_IN_RANGE(addr, low, high)	\
@@ -68,8 +285,7 @@
 
 int netxen_nic_set_mac(struct net_device *netdev, void *p)
 {
-	struct netxen_port *port = netdev_priv(netdev);
-	struct netxen_adapter *adapter = port->adapter;
+	struct netxen_adapter *adapter = netdev_priv(netdev);
 	struct sockaddr *addr = p;
 
 	if (netif_running(netdev))
@@ -82,7 +298,7 @@
 	memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
 
 	if (adapter->macaddr_set)
-		adapter->macaddr_set(port, addr->sa_data);
+		adapter->macaddr_set(adapter, addr->sa_data);
 
 	return 0;
 }
@@ -92,56 +308,19 @@
  */
 void netxen_nic_set_multi(struct net_device *netdev)
 {
-	struct netxen_port *port = netdev_priv(netdev);
-	struct netxen_adapter *adapter = port->adapter;
+	struct netxen_adapter *adapter = netdev_priv(netdev);
 	struct dev_mc_list *mc_ptr;
-	__u32 netxen_mac_addr_cntl_data = 0;
 
 	mc_ptr = netdev->mc_list;
 	if (netdev->flags & IFF_PROMISC) {
 		if (adapter->set_promisc)
 			adapter->set_promisc(adapter,
-					     port->portnum,
 					     NETXEN_NIU_PROMISC_MODE);
 	} else {
-		if (adapter->unset_promisc &&
-		    adapter->ahw.boardcfg.board_type
-		    != NETXEN_BRDTYPE_P2_SB31_10G_IMEZ)
+		if (adapter->unset_promisc)
 			adapter->unset_promisc(adapter,
-					       port->portnum,
 					       NETXEN_NIU_NON_PROMISC_MODE);
 	}
-	if (adapter->ahw.board_type == NETXEN_NIC_XGBE) {
-		netxen_nic_mcr_set_mode_select(netxen_mac_addr_cntl_data, 0x03);
-		netxen_nic_mcr_set_id_pool0(netxen_mac_addr_cntl_data, 0x00);
-		netxen_nic_mcr_set_id_pool1(netxen_mac_addr_cntl_data, 0x00);
-		netxen_nic_mcr_set_id_pool2(netxen_mac_addr_cntl_data, 0x00);
-		netxen_nic_mcr_set_id_pool3(netxen_mac_addr_cntl_data, 0x00);
-		netxen_nic_mcr_set_enable_xtnd0(netxen_mac_addr_cntl_data);
-		netxen_nic_mcr_set_enable_xtnd1(netxen_mac_addr_cntl_data);
-		netxen_nic_mcr_set_enable_xtnd2(netxen_mac_addr_cntl_data);
-		netxen_nic_mcr_set_enable_xtnd3(netxen_mac_addr_cntl_data);
-	} else {
-		netxen_nic_mcr_set_mode_select(netxen_mac_addr_cntl_data, 0x00);
-		netxen_nic_mcr_set_id_pool0(netxen_mac_addr_cntl_data, 0x00);
-		netxen_nic_mcr_set_id_pool1(netxen_mac_addr_cntl_data, 0x01);
-		netxen_nic_mcr_set_id_pool2(netxen_mac_addr_cntl_data, 0x02);
-		netxen_nic_mcr_set_id_pool3(netxen_mac_addr_cntl_data, 0x03);
-	}
-	writel(netxen_mac_addr_cntl_data,
-	       NETXEN_CRB_NORMALIZE(adapter, NETXEN_MAC_ADDR_CNTL_REG));
-	if (adapter->ahw.board_type == NETXEN_NIC_XGBE) {
-		writel(netxen_mac_addr_cntl_data,
-		       NETXEN_CRB_NORMALIZE(adapter,
-					    NETXEN_MULTICAST_ADDR_HI_0));
-	} else {
-		writel(netxen_mac_addr_cntl_data,
-		       NETXEN_CRB_NORMALIZE(adapter,
-					    NETXEN_MULTICAST_ADDR_HI_1));
-	}
-	netxen_mac_addr_cntl_data = 0;
-	writel(netxen_mac_addr_cntl_data,
-	       NETXEN_CRB_NORMALIZE(adapter, NETXEN_NIU_GB_DROP_WRONGADDR));
 }
 
 /*
@@ -150,8 +329,7 @@
  */
 int netxen_nic_change_mtu(struct net_device *netdev, int mtu)
 {
-	struct netxen_port *port = netdev_priv(netdev);
-	struct netxen_adapter *adapter = port->adapter;
+	struct netxen_adapter *adapter = netdev_priv(netdev);
 	int eff_mtu = mtu + NETXEN_ENET_HEADER_SIZE + NETXEN_ETH_FCS_SIZE;
 
 	if ((eff_mtu > NETXEN_MAX_MTU) || (eff_mtu < NETXEN_MIN_MTU)) {
@@ -161,7 +339,7 @@
 	}
 
 	if (adapter->set_mtu)
-		adapter->set_mtu(port, mtu);
+		adapter->set_mtu(adapter, mtu);
 	netdev->mtu = mtu;
 
 	return 0;
@@ -178,9 +356,9 @@
 	void *addr;
 	int loops = 0, err = 0;
 	int ctx, ring;
-	u32 card_cmdring = 0;
 	struct netxen_recv_context *recv_ctx;
 	struct netxen_rcv_desc_ctx *rcv_desc;
+	int func_id = adapter->portnum;
 
 	DPRINTK(INFO, "crb_base: %lx %x", NETXEN_PCI_CRBSPACE,
 		PCI_OFFSET_SECOND_RANGE(adapter, NETXEN_PCI_CRBSPACE));
@@ -189,11 +367,6 @@
 	DPRINTK(INFO, "cam RAM: %lx %x", NETXEN_CAM_RAM_BASE,
 		pci_base_offset(adapter, NETXEN_CAM_RAM_BASE));
 
-	/* Window 1 call */
-	card_cmdring = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_CMDRING));
-
-	DPRINTK(INFO, "Command Peg sends 0x%x for cmdring base\n",
-		card_cmdring);
 
 	for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) {
 		DPRINTK(INFO, "Command Peg ready..waiting for rcv peg\n");
@@ -227,7 +400,7 @@
 			    (dma_addr_t *) & adapter->ctx_desc_phys_addr,
 			    &adapter->ctx_desc_pdev);
 
-	printk("ctx_desc_phys_addr: 0x%llx\n",
+	printk(KERN_INFO "ctx_desc_phys_addr: 0x%llx\n",
 	       (unsigned long long) adapter->ctx_desc_phys_addr);
 	if (addr == NULL) {
 		DPRINTK(ERR, "bad return from pci_alloc_consistent\n");
@@ -236,6 +409,7 @@
 	}
 	memset(addr, 0, sizeof(struct netxen_ring_ctx));
 	adapter->ctx_desc = (struct netxen_ring_ctx *)addr;
+	adapter->ctx_desc->ctx_id = cpu_to_le32(adapter->portnum);
 	adapter->ctx_desc->cmd_consumer_offset =
 	    cpu_to_le64(adapter->ctx_desc_phys_addr +
 			sizeof(struct netxen_ring_ctx));
@@ -247,7 +421,7 @@
 			    adapter->max_tx_desc_count,
 			    (dma_addr_t *) & hw->cmd_desc_phys_addr,
 			    &adapter->ahw.cmd_desc_pdev);
-	printk("cmd_desc_phys_addr: 0x%llx\n",
+	printk(KERN_INFO "cmd_desc_phys_addr: 0x%llx\n",
 	       (unsigned long long) hw->cmd_desc_phys_addr);
 
 	if (addr == NULL) {
@@ -306,11 +480,11 @@
 	/* Window = 1 */
 
 	writel(lower32(adapter->ctx_desc_phys_addr),
-	       NETXEN_CRB_NORMALIZE(adapter, CRB_CTX_ADDR_REG_LO));
+	       NETXEN_CRB_NORMALIZE(adapter, CRB_CTX_ADDR_REG_LO(func_id)));
 	writel(upper32(adapter->ctx_desc_phys_addr),
-	       NETXEN_CRB_NORMALIZE(adapter, CRB_CTX_ADDR_REG_HI));
-	writel(NETXEN_CTX_SIGNATURE,
-	       NETXEN_CRB_NORMALIZE(adapter, CRB_CTX_SIGNATURE_REG));
+	       NETXEN_CRB_NORMALIZE(adapter, CRB_CTX_ADDR_REG_HI(func_id)));
+	writel(NETXEN_CTX_SIGNATURE | func_id,
+	       NETXEN_CRB_NORMALIZE(adapter, CRB_CTX_SIGNATURE_REG(func_id)));
 	return err;
 }
 
@@ -337,10 +511,6 @@
 				    adapter->ahw.cmd_desc_phys_addr);
 		adapter->ahw.cmd_desc_head = NULL;
 	}
-	/* Special handling: there are 2 ports on this board */
-	if (adapter->ahw.boardcfg.board_type == NETXEN_BRDTYPE_P2_SB31_10G_IMEZ) {
-		adapter->ahw.max_ports = 2;
-	}
 
 	for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) {
 		recv_ctx = &adapter->recv_ctx[ctx];
@@ -371,22 +541,20 @@
 		      struct cmd_desc_type0 *desc, struct sk_buff *skb)
 {
 	if (desc->mss) {
-		desc->total_hdr_length = sizeof(struct ethhdr) +
-		    ((skb->nh.iph)->ihl * sizeof(u32)) +
-		    ((skb->h.th)->doff * sizeof(u32));
+		desc->total_hdr_length = (sizeof(struct ethhdr) +
+					  ip_hdrlen(skb) + tcp_hdrlen(skb));
 		netxen_set_cmd_desc_opcode(desc, TX_TCP_LSO);
 	} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
-		if (skb->nh.iph->protocol == IPPROTO_TCP) {
+		if (ip_hdr(skb)->protocol == IPPROTO_TCP) {
 			netxen_set_cmd_desc_opcode(desc, TX_TCP_PKT);
-		} else if (skb->nh.iph->protocol == IPPROTO_UDP) {
+		} else if (ip_hdr(skb)->protocol == IPPROTO_UDP) {
 			netxen_set_cmd_desc_opcode(desc, TX_UDP_PKT);
 		} else {
 			return;
 		}
 	}
-	adapter->stats.xmitcsummed++;
-	desc->tcp_hdr_offset = skb->h.raw - skb->data;
-	desc->ip_hdr_offset = skb->nh.raw - skb->data;
+	desc->tcp_hdr_offset = skb_transport_offset(skb);
+	desc->ip_hdr_offset = skb_network_offset(skb);
 }
 
 int netxen_is_flash_supported(struct netxen_adapter *adapter)
@@ -474,7 +642,30 @@
 
 	if (adapter->curr_window == wndw)
 		return;
-
+	switch(adapter->ahw.pci_func) {
+		case 0:
+			offset = PCI_OFFSET_SECOND_RANGE(adapter,
+					NETXEN_PCIX_PH_REG(PCIX_CRB_WINDOW));
+			break;
+		case 1:
+			offset = PCI_OFFSET_SECOND_RANGE(adapter,
+					NETXEN_PCIX_PH_REG(PCIX_CRB_WINDOW_F1));
+			break;
+		case 2:
+			offset = PCI_OFFSET_SECOND_RANGE(adapter,
+					NETXEN_PCIX_PH_REG(PCIX_CRB_WINDOW_F2));
+			break;
+		case 3:
+			offset = PCI_OFFSET_SECOND_RANGE(adapter,
+					NETXEN_PCIX_PH_REG(PCIX_CRB_WINDOW_F3));
+			break;
+		default:
+			printk(KERN_INFO "Changing the window for PCI function"
+					"%d\n",	adapter->ahw.pci_func);
+			offset = PCI_OFFSET_SECOND_RANGE(adapter,
+					NETXEN_PCIX_PH_REG(PCIX_CRB_WINDOW));
+			break;
+	}
 	/*
 	 * Move the CRB window.
 	 * We need to write to the "direct access" region of PCI
@@ -483,9 +674,6 @@
 	 * register address is received by PCI. The direct region bypasses
 	 * the CRB bus.
 	 */
-	offset =
-	    PCI_OFFSET_SECOND_RANGE(adapter,
-				    NETXEN_PCIX_PH_REG(PCIX_CRB_WINDOW));
 
 	if (wndw & 0x1)
 		wndw = NETXEN_WINDOW_ONE;
@@ -503,7 +691,10 @@
 		count++;
 	}
 
-	adapter->curr_window = wndw;
+	if (wndw == NETXEN_WINDOW_ONE)
+		adapter->curr_window = 1;
+	else
+		adapter->curr_window = 0;
 }
 
 void netxen_load_firmware(struct netxen_adapter *adapter)
@@ -748,6 +939,17 @@
 	return addr;
 }
 
+int
+netxen_nic_erase_pxe(struct netxen_adapter *adapter)
+{
+	if (netxen_rom_fast_write(adapter, PXE_START, 0) == -1) {
+		printk(KERN_ERR "%s: erase pxe failed\n", 
+			netxen_nic_driver_name);
+		return -1;
+	}
+	return 0;
+}
+
 int netxen_nic_get_board_info(struct netxen_adapter *adapter)
 {
 	int rv = 0;
@@ -809,43 +1011,29 @@
 
 /* NIU access sections */
 
-int netxen_nic_set_mtu_gb(struct netxen_port *port, int new_mtu)
+int netxen_nic_set_mtu_gb(struct netxen_adapter *adapter, int new_mtu)
 {
-	struct netxen_adapter *adapter = port->adapter;
 	netxen_nic_write_w0(adapter,
-			    NETXEN_NIU_GB_MAX_FRAME_SIZE(port->portnum),
-			    new_mtu);
+			NETXEN_NIU_GB_MAX_FRAME_SIZE(
+				physical_port[adapter->portnum]), new_mtu);
 	return 0;
 }
 
-int netxen_nic_set_mtu_xgb(struct netxen_port *port, int new_mtu)
+int netxen_nic_set_mtu_xgb(struct netxen_adapter *adapter, int new_mtu)
 {
-	struct netxen_adapter *adapter = port->adapter;
 	new_mtu += NETXEN_NIU_HDRSIZE + NETXEN_NIU_TLRSIZE;
-	if (port->portnum == 0)
-	    netxen_nic_write_w0(adapter, NETXEN_NIU_XGE_MAX_FRAME_SIZE, new_mtu);
-	else if (port->portnum == 1)
-	    netxen_nic_write_w0(adapter, NETXEN_NIU_XG1_MAX_FRAME_SIZE, new_mtu);
+	if (physical_port[adapter->portnum] == 0)
+		netxen_nic_write_w0(adapter, NETXEN_NIU_XGE_MAX_FRAME_SIZE, 
+				new_mtu);
+	else 
+		netxen_nic_write_w0(adapter, NETXEN_NIU_XG1_MAX_FRAME_SIZE,
+				new_mtu);
 	return 0;
 }
 
 void netxen_nic_init_niu_gb(struct netxen_adapter *adapter)
 {
-	int portno;
-	for (portno = 0; portno < NETXEN_NIU_MAX_GBE_PORTS; portno++)
-		netxen_niu_gbe_init_port(adapter, portno);
-}
-
-void netxen_nic_stop_all_ports(struct netxen_adapter *adapter)
-{
-	int port_nr;
-	struct netxen_port *port;
-
-	for (port_nr = 0; port_nr < adapter->ahw.max_ports; port_nr++) {
-		port = adapter->port[port_nr];
-		if (adapter->stop_port)
-			adapter->stop_port(adapter, port->portnum);
-	}
+	netxen_niu_gbe_init_port(adapter, physical_port[adapter->portnum]);
 }
 
 void
@@ -864,9 +1052,8 @@
 	}
 }
 
-void netxen_nic_set_link_parameters(struct netxen_port *port)
+void netxen_nic_set_link_parameters(struct netxen_adapter *adapter)
 {
-	struct netxen_adapter *adapter = port->adapter;
 	__u32 status;
 	__u32 autoneg;
 	__u32 mode;
@@ -875,47 +1062,47 @@
 	if (netxen_get_niu_enable_ge(mode)) {	/* Gb 10/100/1000 Mbps mode */
 		if (adapter->phy_read
 		    && adapter->
-		    phy_read(adapter, port->portnum,
+		    phy_read(adapter,
 			     NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS,
 			     &status) == 0) {
 			if (netxen_get_phy_link(status)) {
 				switch (netxen_get_phy_speed(status)) {
 				case 0:
-					port->link_speed = SPEED_10;
+					adapter->link_speed = SPEED_10;
 					break;
 				case 1:
-					port->link_speed = SPEED_100;
+					adapter->link_speed = SPEED_100;
 					break;
 				case 2:
-					port->link_speed = SPEED_1000;
+					adapter->link_speed = SPEED_1000;
 					break;
 				default:
-					port->link_speed = -1;
+					adapter->link_speed = -1;
 					break;
 				}
 				switch (netxen_get_phy_duplex(status)) {
 				case 0:
-					port->link_duplex = DUPLEX_HALF;
+					adapter->link_duplex = DUPLEX_HALF;
 					break;
 				case 1:
-					port->link_duplex = DUPLEX_FULL;
+					adapter->link_duplex = DUPLEX_FULL;
 					break;
 				default:
-					port->link_duplex = -1;
+					adapter->link_duplex = -1;
 					break;
 				}
 				if (adapter->phy_read
 				    && adapter->
-				    phy_read(adapter, port->portnum,
+				    phy_read(adapter,
 					     NETXEN_NIU_GB_MII_MGMT_ADDR_AUTONEG,
 					     &autoneg) != 0)
-					port->link_autoneg = autoneg;
+					adapter->link_autoneg = autoneg;
 			} else
 				goto link_down;
 		} else {
 		      link_down:
-			port->link_speed = -1;
-			port->link_duplex = -1;
+			adapter->link_speed = -1;
+			adapter->link_duplex = -1;
 		}
 	}
 }
@@ -929,7 +1116,7 @@
 	char brd_name[NETXEN_MAX_SHORT_NAME];
 	struct netxen_new_user_info user_info;
 	int i, addr = USER_START;
-	u32 *ptr32;
+	__le32 *ptr32;
 
 	struct netxen_board_info *board_info = &(adapter->ahw.boardcfg);
 	if (board_info->magic != NETXEN_BDINFO_MAGIC) {
@@ -955,7 +1142,6 @@
 				       netxen_nic_driver_name);
 				return;
 			}
-			*ptr32 = le32_to_cpu(*ptr32);
 			ptr32++;
 			addr += sizeof(u32);
 		}
diff --git a/drivers/net/netxen/netxen_nic_hw.h b/drivers/net/netxen/netxen_nic_hw.h
index ab1112eb..245bf13 100644
--- a/drivers/net/netxen/netxen_nic_hw.h
+++ b/drivers/net/netxen/netxen_nic_hw.h
@@ -6,12 +6,12 @@
  * 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,
@@ -87,7 +87,7 @@
 	*(u32 *)Y = readl((void __iomem*) addr);
 
 struct netxen_port;
-void netxen_nic_set_link_parameters(struct netxen_port *port);
+void netxen_nic_set_link_parameters(struct netxen_adapter *adapter);
 void netxen_nic_flash_print(struct netxen_adapter *adapter);
 int netxen_nic_hw_write_wx(struct netxen_adapter *adapter, u64 off,
 			   void *data, int len);
@@ -220,6 +220,69 @@
 		_netxen_crb_get_bit(config_word, 1)
 #define netxen_get_gb_mii_mgmt_notvalid(config_word)	\
 		_netxen_crb_get_bit(config_word, 2)
+/*
+ * NIU XG Pause Ctl Register
+ *
+ *      Bit 0       : xg0_mask => 1:disable tx pause frames
+ *      Bit 1       : xg0_request => 1:request single pause frame
+ *      Bit 2       : xg0_on_off => 1:request is pause on, 0:off
+ *      Bit 3       : xg1_mask => 1:disable tx pause frames
+ *      Bit 4       : xg1_request => 1:request single pause frame
+ *      Bit 5       : xg1_on_off => 1:request is pause on, 0:off
+ */
+
+#define netxen_xg_set_xg0_mask(config_word)    \
+	((config_word) |= 1 << 0)
+#define netxen_xg_set_xg1_mask(config_word)    \
+	((config_word) |= 1 << 3)
+		
+#define netxen_xg_get_xg0_mask(config_word)    \
+	_netxen_crb_get_bit((config_word), 0)
+#define netxen_xg_get_xg1_mask(config_word)    \
+	_netxen_crb_get_bit((config_word), 3)
+
+#define netxen_xg_unset_xg0_mask(config_word)  \
+	((config_word) &= ~(1 << 0))
+#define netxen_xg_unset_xg1_mask(config_word)  \
+	((config_word) &= ~(1 << 3))
+
+/*
+ * NIU XG Pause Ctl Register
+ *
+ *      Bit 0       : xg0_mask => 1:disable tx pause frames
+ *      Bit 1       : xg0_request => 1:request single pause frame
+ *      Bit 2       : xg0_on_off => 1:request is pause on, 0:off
+ *      Bit 3       : xg1_mask => 1:disable tx pause frames
+ *      Bit 4       : xg1_request => 1:request single pause frame
+ *      Bit 5       : xg1_on_off => 1:request is pause on, 0:off
+ */
+#define netxen_gb_set_gb0_mask(config_word)    \
+	((config_word) |= 1 << 0)
+#define netxen_gb_set_gb1_mask(config_word)    \
+	((config_word) |= 1 << 2)
+#define netxen_gb_set_gb2_mask(config_word)    \
+	((config_word) |= 1 << 4)
+#define netxen_gb_set_gb3_mask(config_word)    \
+	((config_word) |= 1 << 6)
+
+#define netxen_gb_get_gb0_mask(config_word)    \
+	_netxen_crb_get_bit((config_word), 0)
+#define netxen_gb_get_gb1_mask(config_word)    \
+	_netxen_crb_get_bit((config_word), 2)
+#define netxen_gb_get_gb2_mask(config_word)    \
+	_netxen_crb_get_bit((config_word), 4)
+#define netxen_gb_get_gb3_mask(config_word)    \
+	_netxen_crb_get_bit((config_word), 6)
+	
+#define netxen_gb_unset_gb0_mask(config_word)  \
+	((config_word) &= ~(1 << 0))
+#define netxen_gb_unset_gb1_mask(config_word)  \
+	((config_word) &= ~(1 << 2))
+#define netxen_gb_unset_gb2_mask(config_word)  \
+	((config_word) &= ~(1 << 4))
+#define netxen_gb_unset_gb3_mask(config_word)  \
+	((config_word) &= ~(1 << 6))
+
 
 /*
  * PHY-Specific MII control/status registers.
@@ -452,21 +515,21 @@
 		((config) |= (((val) & 0x0f) << 28))
 
 /* Set promiscuous mode for a GbE interface */
-int netxen_niu_set_promiscuous_mode(struct netxen_adapter *adapter, int port,
+int netxen_niu_set_promiscuous_mode(struct netxen_adapter *adapter, 
 				    netxen_niu_prom_mode_t mode);
 int netxen_niu_xg_set_promiscuous_mode(struct netxen_adapter *adapter,
-				       int port, netxen_niu_prom_mode_t mode);
+				       netxen_niu_prom_mode_t mode);
 
 /* get/set the MAC address for a given MAC */
-int netxen_niu_macaddr_get(struct netxen_adapter *adapter, int port,
+int netxen_niu_macaddr_get(struct netxen_adapter *adapter,
 			   netxen_ethernet_macaddr_t * addr);
-int netxen_niu_macaddr_set(struct netxen_port *port,
+int netxen_niu_macaddr_set(struct netxen_adapter *adapter,
 			   netxen_ethernet_macaddr_t addr);
 
 /* XG versons */
-int netxen_niu_xg_macaddr_get(struct netxen_adapter *adapter, int port,
+int netxen_niu_xg_macaddr_get(struct netxen_adapter *adapter,
 			      netxen_ethernet_macaddr_t * addr);
-int netxen_niu_xg_macaddr_set(struct netxen_port *port,
+int netxen_niu_xg_macaddr_set(struct netxen_adapter *adapter,
 			      netxen_ethernet_macaddr_t addr);
 
 /* Generic enable for GbE ports. Will detect the speed of the link. */
@@ -475,8 +538,8 @@
 int netxen_niu_xg_init_port(struct netxen_adapter *adapter, int port);
 
 /* Disable a GbE interface */
-int netxen_niu_disable_gbe_port(struct netxen_adapter *adapter, int port);
+int netxen_niu_disable_gbe_port(struct netxen_adapter *adapter);
 
-int netxen_niu_disable_xg_port(struct netxen_adapter *adapter, int port);
+int netxen_niu_disable_xg_port(struct netxen_adapter *adapter);
 
 #endif				/* __NETXEN_NIC_HW_H_ */
diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c
index eff965d..cf0e96a 100644
--- a/drivers/net/netxen/netxen_nic_init.c
+++ b/drivers/net/netxen/netxen_nic_init.c
@@ -139,7 +139,7 @@
 		return err;
 	}
 	/* Window 1 call */
-	writel(MPORT_SINGLE_FUNCTION_MODE,
+	writel(MPORT_MULTI_FUNCTION_MODE,
 	       NETXEN_CRB_NORMALIZE(adapter, CRB_MPORT_MODE));
 	writel(PHAN_INITIALIZE_ACK,
 	       NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE));
@@ -226,7 +226,6 @@
 		adapter->unset_promisc = netxen_niu_set_promiscuous_mode;
 		adapter->phy_read = netxen_niu_gbe_phy_read;
 		adapter->phy_write = netxen_niu_gbe_phy_write;
-		adapter->init_port = netxen_niu_gbe_init_port;
 		adapter->init_niu = netxen_nic_init_niu_gb;
 		adapter->stop_port = netxen_niu_disable_gbe_port;
 		break;
@@ -277,8 +276,8 @@
 		return (pci_base + offset);
 }
 
-static long rom_max_timeout = 10000;
-static long rom_lock_timeout = 1000000;
+static long rom_max_timeout = 100;
+static long rom_lock_timeout = 10000;
 static long rom_write_timeout = 700;
 
 static inline int rom_lock(struct netxen_adapter *adapter)
@@ -438,9 +437,9 @@
 
 	for (addridx = addr; addridx < (addr + size); addridx += 4) {
 		ret = do_rom_fast_read(adapter, addridx, (int *)bytes);
-		*(int *)bytes = cpu_to_le32(*(int *)bytes);
 		if (ret != 0)
 			break;
+		*(int *)bytes = cpu_to_le32(*(int *)bytes);
 		bytes += 4;
 	}
 
@@ -499,7 +498,6 @@
 		int data;
 
 		data = le32_to_cpu((*(u32*)bytes));
-
 		ret = do_rom_fast_write(adapter, addridx, data);
 		if (ret < 0)
 			return ret;
@@ -953,7 +951,8 @@
 
 	if (!pegtune_val) {
 		val = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE));
-		while (val != PHAN_INITIALIZE_COMPLETE && loops < 200000) {
+		while (val != PHAN_INITIALIZE_COMPLETE && 
+			val != PHAN_INITIALIZE_ACK && loops < 200000) {
 			udelay(100);
 			schedule();
 			val =
@@ -990,9 +989,7 @@
 
 static inline int netxen_nic_check_temp(struct netxen_adapter *adapter)
 {
-	int port_num;
-	struct netxen_port *port;
-	struct net_device *netdev;
+	struct net_device *netdev = adapter->netdev;
 	uint32_t temp, temp_state, temp_val;
 	int rv = 0;
 
@@ -1006,14 +1003,9 @@
 		       "%s: Device temperature %d degrees C exceeds"
 		       " maximum allowed. Hardware has been shut down.\n",
 		       netxen_nic_driver_name, temp_val);
-		for (port_num = 0; port_num < adapter->ahw.max_ports;
-		     port_num++) {
-			port = adapter->port[port_num];
-			netdev = port->netdev;
 
-			netif_carrier_off(netdev);
-			netif_stop_queue(netdev);
-		}
+		netif_carrier_off(netdev);
+		netif_stop_queue(netdev);
 		rv = 1;
 	} else if (temp_state == NX_TEMP_WARN) {
 		if (adapter->temp == NX_TEMP_NORMAL) {
@@ -1037,29 +1029,23 @@
 
 void netxen_watchdog_task(struct work_struct *work)
 {
-	int port_num;
-	struct netxen_port *port;
 	struct net_device *netdev;
 	struct netxen_adapter *adapter =
 		container_of(work, struct netxen_adapter, watchdog_task);
 
-	if (netxen_nic_check_temp(adapter))
+	if ((adapter->portnum  == 0) && netxen_nic_check_temp(adapter))
 		return;
 
-	for (port_num = 0; port_num < adapter->ahw.max_ports; port_num++) {
-		port = adapter->port[port_num];
-		netdev = port->netdev;
-
-		if ((netif_running(netdev)) && !netif_carrier_ok(netdev)) {
-			printk(KERN_INFO "%s port %d, %s carrier is now ok\n",
-			       netxen_nic_driver_name, port_num, netdev->name);
-			netif_carrier_on(netdev);
-		}
-
-		if (netif_queue_stopped(netdev))
-			netif_wake_queue(netdev);
+	netdev = adapter->netdev;
+	if ((netif_running(netdev)) && !netif_carrier_ok(netdev)) {
+		printk(KERN_INFO "%s port %d, %s carrier is now ok\n",
+		       netxen_nic_driver_name, adapter->portnum, netdev->name);
+		netif_carrier_on(netdev);
 	}
 
+	if (netif_queue_stopped(netdev))
+		netif_wake_queue(netdev);
+
 	if (adapter->handle_phy_intr)
 		adapter->handle_phy_intr(adapter);
 	mod_timer(&adapter->watchdog_timer, jiffies + 2 * HZ);
@@ -1074,9 +1060,8 @@
 netxen_process_rcv(struct netxen_adapter *adapter, int ctxid,
 		   struct status_desc *desc)
 {
-	struct netxen_port *port = adapter->port[netxen_get_sts_port(desc)];
-	struct pci_dev *pdev = port->pdev;
-	struct net_device *netdev = port->netdev;
+	struct pci_dev *pdev = adapter->pdev;
+	struct net_device *netdev = adapter->netdev;
 	int index = netxen_get_sts_refhandle(desc);
 	struct netxen_recv_context *recv_ctx = &(adapter->recv_ctx[ctxid]);
 	struct netxen_rx_buffer *buffer;
@@ -1126,10 +1111,9 @@
 	skb = (struct sk_buff *)buffer->skb;
 
 	if (likely(netxen_get_sts_status(desc) == STATUS_CKSUM_OK)) {
-		port->stats.csummed++;
+		adapter->stats.csummed++;
 		skb->ip_summed = CHECKSUM_UNNECESSARY;
 	}
-	skb->dev = netdev;
 	if (desc_ctx == RCV_DESC_LRO_CTXID) {
 		/* True length was only available on the last pkt */
 		skb_put(skb, buffer->lro_length);
@@ -1147,27 +1131,27 @@
 	 */
 	switch (ret) {
 	case NET_RX_SUCCESS:
-		port->stats.uphappy++;
+		adapter->stats.uphappy++;
 		break;
 
 	case NET_RX_CN_LOW:
-		port->stats.uplcong++;
+		adapter->stats.uplcong++;
 		break;
 
 	case NET_RX_CN_MOD:
-		port->stats.upmcong++;
+		adapter->stats.upmcong++;
 		break;
 
 	case NET_RX_CN_HIGH:
-		port->stats.uphcong++;
+		adapter->stats.uphcong++;
 		break;
 
 	case NET_RX_DROP:
-		port->stats.updropped++;
+		adapter->stats.updropped++;
 		break;
 
 	default:
-		port->stats.updunno++;
+		adapter->stats.updunno++;
 		break;
 	}
 
@@ -1179,14 +1163,13 @@
 	/*
 	 * We just consumed one buffer so post a buffer.
 	 */
-	adapter->stats.post_called++;
 	buffer->skb = NULL;
 	buffer->state = NETXEN_BUFFER_FREE;
 	buffer->lro_current_frags = 0;
 	buffer->lro_expected_frags = 0;
 
-	port->stats.no_rcv++;
-	port->stats.rxbytes += length;
+	adapter->stats.no_rcv++;
+	adapter->stats.rxbytes += length;
 }
 
 /* Process Receive status ring */
@@ -1227,7 +1210,6 @@
 
 	/* update the consumer index in phantom */
 	if (count) {
-		adapter->stats.process_rcv++;
 		recv_ctx->status_rx_consumer = consumer;
 		recv_ctx->status_rx_producer = producer;
 
@@ -1250,13 +1232,10 @@
 	int count1 = 0;
 	int count2 = 0;
 	struct netxen_cmd_buffer *buffer;
-	struct netxen_port *port;	/* port #1 */
-	struct netxen_port *nport;
 	struct pci_dev *pdev;
 	struct netxen_skb_frag *frag;
 	u32 i;
 	struct sk_buff *skb = NULL;
-	int p;
 	int done;
 
 	spin_lock(&adapter->tx_lock);
@@ -1277,7 +1256,6 @@
 	}
 
 	adapter->proc_cmd_buf_counter++;
-	adapter->stats.process_xmit++;
 	/*
 	 * Not needed - does not seem to be used anywhere.
 	 * adapter->cmd_consumer = consumer;
@@ -1286,8 +1264,7 @@
 
 	while ((last_consumer != consumer) && (count1 < MAX_STATUS_HANDLE)) {
 		buffer = &adapter->cmd_buf_arr[last_consumer];
-		port = adapter->port[buffer->port];
-		pdev = port->pdev;
+		pdev = adapter->pdev;
 		frag = &buffer->frag_array[0];
 		skb = buffer->skb;
 		if (skb && (cmpxchg(&buffer->skb, skb, 0) == skb)) {
@@ -1300,24 +1277,23 @@
 					       PCI_DMA_TODEVICE);
 			}
 
-			port->stats.skbfreed++;
+			adapter->stats.skbfreed++;
 			dev_kfree_skb_any(skb);
 			skb = NULL;
 		} else if (adapter->proc_cmd_buf_counter == 1) {
-			port->stats.txnullskb++;
+			adapter->stats.txnullskb++;
 		}
-		if (unlikely(netif_queue_stopped(port->netdev)
-			     && netif_carrier_ok(port->netdev))
-		    && ((jiffies - port->netdev->trans_start) >
-			port->netdev->watchdog_timeo)) {
-			SCHEDULE_WORK(&port->tx_timeout_task);
+		if (unlikely(netif_queue_stopped(adapter->netdev)
+			     && netif_carrier_ok(adapter->netdev))
+		    && ((jiffies - adapter->netdev->trans_start) >
+			adapter->netdev->watchdog_timeo)) {
+			SCHEDULE_WORK(&adapter->tx_timeout_task);
 		}
 
 		last_consumer = get_next_index(last_consumer,
 					       adapter->max_tx_desc_count);
 		count1++;
 	}
-	adapter->stats.noxmitdone += count1;
 
 	count2 = 0;
 	spin_lock(&adapter->tx_lock);
@@ -1337,13 +1313,10 @@
 		}
 	}
 	if (count1 || count2) {
-		for (p = 0; p < adapter->ahw.max_ports; p++) {
-			nport = adapter->port[p];
-			if (netif_queue_stopped(nport->netdev)
-			    && (nport->flags & NETXEN_NETDEV_STATUS)) {
-				netif_wake_queue(nport->netdev);
-				nport->flags &= ~NETXEN_NETDEV_STATUS;
-			}
+		if (netif_queue_stopped(adapter->netdev)
+		    && (adapter->flags & NETXEN_NETDEV_STATUS)) {
+			netif_wake_queue(adapter->netdev);
+			adapter->flags &= ~NETXEN_NETDEV_STATUS;
 		}
 	}
 	/*
@@ -1389,7 +1362,6 @@
 	netxen_ctx_msg msg = 0;
 	dma_addr_t dma;
 
-	adapter->stats.post_called++;
 	rcv_desc = &recv_ctx->rcv_desc[ringid];
 
 	producer = rcv_desc->producer;
@@ -1442,8 +1414,6 @@
 	if (count) {
 		rcv_desc->begin_alloc = index;
 		rcv_desc->rcv_pending += count;
-		adapter->stats.lastposted = count;
-		adapter->stats.posted += count;
 		rcv_desc->producer = producer;
 		if (rcv_desc->rcv_free >= 32) {
 			rcv_desc->rcv_free = 0;
@@ -1451,7 +1421,8 @@
 			writel((producer - 1) &
 			       (rcv_desc->max_rx_desc_count - 1),
 			       NETXEN_CRB_NORMALIZE(adapter,
-						    recv_crb_registers[0].
+						    recv_crb_registers[
+						    adapter->portnum].
 						    rcv_desc_crb[ringid].
 						    crb_rcv_producer_offset));
 			/*
@@ -1464,7 +1435,7 @@
 					     ((producer -
 					       1) & (rcv_desc->
 						     max_rx_desc_count - 1)));
-			netxen_set_msg_ctxid(msg, 0);
+			netxen_set_msg_ctxid(msg, adapter->portnum);
 			netxen_set_msg_opcode(msg, NETXEN_RCV_PRODUCER(ringid));
 			writel(msg,
 			       DB_NORMALIZE(adapter,
@@ -1486,7 +1457,6 @@
 	int count = 0;
 	int index = 0;
 
-	adapter->stats.post_called++;
 	rcv_desc = &recv_ctx->rcv_desc[ringid];
 
 	producer = rcv_desc->producer;
@@ -1533,8 +1503,6 @@
 	if (count) {
 		rcv_desc->begin_alloc = index;
 		rcv_desc->rcv_pending += count;
-		adapter->stats.lastposted = count;
-		adapter->stats.posted += count;
 		rcv_desc->producer = producer;
 		if (rcv_desc->rcv_free >= 32) {
 			rcv_desc->rcv_free = 0;
@@ -1542,7 +1510,8 @@
 			writel((producer - 1) &
 			       (rcv_desc->max_rx_desc_count - 1),
 			       NETXEN_CRB_NORMALIZE(adapter,
-						    recv_crb_registers[0].
+						    recv_crb_registers[
+						    adapter->portnum].
 						    rcv_desc_crb[ringid].
 						    crb_rcv_producer_offset));
 			wmb();
@@ -1563,13 +1532,7 @@
 
 void netxen_nic_clear_stats(struct netxen_adapter *adapter)
 {
-	struct netxen_port *port;
-	int port_num;
-
 	memset(&adapter->stats, 0, sizeof(adapter->stats));
-	for (port_num = 0; port_num < adapter->ahw.max_ports; port_num++) {
-		port = adapter->port[port_num];
-		memset(&port->stats, 0, sizeof(port->stats));
-	}
+	return;
 }
 
diff --git a/drivers/net/netxen/netxen_nic_isr.c b/drivers/net/netxen/netxen_nic_isr.c
index be366e4..b213b06 100644
--- a/drivers/net/netxen/netxen_nic_isr.c
+++ b/drivers/net/netxen/netxen_nic_isr.c
@@ -6,12 +6,12 @@
  * 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,
@@ -40,35 +40,35 @@
  */
 struct net_device_stats *netxen_nic_get_stats(struct net_device *netdev)
 {
-	struct netxen_port *port = netdev_priv(netdev);
-	struct net_device_stats *stats = &port->net_stats;
+	struct netxen_adapter *adapter = netdev_priv(netdev);
+	struct net_device_stats *stats = &adapter->net_stats;
 
 	memset(stats, 0, sizeof(*stats));
 
 	/* total packets received   */
-	stats->rx_packets = port->stats.no_rcv;
+	stats->rx_packets = adapter->stats.no_rcv;
 	/* total packets transmitted    */
-	stats->tx_packets = port->stats.xmitedframes + port->stats.xmitfinished;
+	stats->tx_packets = adapter->stats.xmitedframes + 
+		adapter->stats.xmitfinished;
 	/* total bytes received     */
-	stats->rx_bytes = port->stats.rxbytes;
+	stats->rx_bytes = adapter->stats.rxbytes;
 	/* total bytes transmitted  */
-	stats->tx_bytes = port->stats.txbytes;
+	stats->tx_bytes = adapter->stats.txbytes;
 	/* bad packets received     */
-	stats->rx_errors = port->stats.rcvdbadskb;
+	stats->rx_errors = adapter->stats.rcvdbadskb;
 	/* packet transmit problems */
-	stats->tx_errors = port->stats.nocmddescriptor;
+	stats->tx_errors = adapter->stats.nocmddescriptor;
 	/* no space in linux buffers    */
-	stats->rx_dropped = port->stats.updropped;
+	stats->rx_dropped = adapter->stats.updropped;
 	/* no space available in linux  */
-	stats->tx_dropped = port->stats.txdropped;
+	stats->tx_dropped = adapter->stats.txdropped;
 
 	return stats;
 }
 
-void netxen_indicate_link_status(struct netxen_adapter *adapter, u32 portno,
-				 u32 link)
+void netxen_indicate_link_status(struct netxen_adapter *adapter, u32 link)
 {
-	struct net_device *netdev = (adapter->port[portno])->netdev;
+	struct net_device *netdev = adapter->netdev;
 
 	if (link)
 		netif_carrier_on(netdev);
@@ -76,15 +76,13 @@
 		netif_carrier_off(netdev);
 }
 
-void netxen_handle_port_int(struct netxen_adapter *adapter, u32 portno,
-			    u32 enable)
+void netxen_handle_port_int(struct netxen_adapter *adapter, u32 enable)
 {
 	__u32 int_src;
-	struct netxen_port *port;
 
 	/*  This should clear the interrupt source */
 	if (adapter->phy_read)
-		adapter->phy_read(adapter, portno,
+		adapter->phy_read(adapter, 
 				  NETXEN_NIU_GB_MII_MGMT_ADDR_INT_STATUS,
 				  &int_src);
 	if (int_src == 0) {
@@ -92,9 +90,7 @@
 		return;
 	}
 	if (adapter->disable_phy_interrupts)
-		adapter->disable_phy_interrupts(adapter, portno);
-
-	port = adapter->port[portno];
+		adapter->disable_phy_interrupts(adapter);
 
 	if (netxen_get_phy_int_jabber(int_src))
 		DPRINTK(INFO, "Jabber interrupt \n");
@@ -115,64 +111,57 @@
 		DPRINTK(INFO, "SPEED CHANGED OR LINK STATUS CHANGED \n");
 
 		if (adapter->phy_read
-		    && adapter->phy_read(adapter, portno,
+		    && adapter->phy_read(adapter, 
 					 NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS,
 					 &status) == 0) {
 			if (netxen_get_phy_int_link_status_changed(int_src)) {
 				if (netxen_get_phy_link(status)) {
-					netxen_niu_gbe_init_port(adapter,
-								 portno);
-					printk("%s: %s Link UP\n",
+					printk(KERN_INFO "%s: %s Link UP\n",
 					       netxen_nic_driver_name,
-					       port->netdev->name);
+					       adapter->netdev->name);
 
 				} else {
-					printk("%s: %s Link DOWN\n",
+					printk(KERN_INFO "%s: %s Link DOWN\n",
 					       netxen_nic_driver_name,
-					       port->netdev->name);
+					       adapter->netdev->name);
 				}
-				netxen_indicate_link_status(adapter, portno,
+				netxen_indicate_link_status(adapter, 
 							    netxen_get_phy_link
 							    (status));
 			}
 		}
 	}
 	if (adapter->enable_phy_interrupts)
-		adapter->enable_phy_interrupts(adapter, portno);
+		adapter->enable_phy_interrupts(adapter);
 }
 
 void netxen_nic_isr_other(struct netxen_adapter *adapter)
 {
-	u32 portno;
+	int portno = adapter->portnum;
 	u32 val, linkup, qg_linksup;
 
 	/* verify the offset */
 	val = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_XG_STATE));
+	val = val >> physical_port[adapter->portnum];
 	if (val == adapter->ahw.qg_linksup)
 		return;
 
 	qg_linksup = adapter->ahw.qg_linksup;
 	adapter->ahw.qg_linksup = val;
 	DPRINTK(INFO, "link update 0x%08x\n", val);
-	for (portno = 0; portno < NETXEN_NIU_MAX_GBE_PORTS; portno++) {
-		linkup = val & 1;
-		if (linkup != (qg_linksup & 1)) {
-			printk(KERN_INFO "%s: %s PORT %d link %s\n",
-			       adapter->port[portno]->netdev->name,
-			       netxen_nic_driver_name, portno,
-			       ((linkup == 0) ? "down" : "up"));
-			netxen_indicate_link_status(adapter, portno, linkup);
-			if (linkup)
-				netxen_nic_set_link_parameters(adapter->
-							       port[portno]);
 
-		}
-		val = val >> 1;
-		qg_linksup = qg_linksup >> 1;
+	linkup = val & 1;
+
+	if (linkup != (qg_linksup & 1)) {
+		printk(KERN_INFO "%s: %s PORT %d link %s\n",
+		       adapter->netdev->name,
+		       netxen_nic_driver_name, portno,
+		       ((linkup == 0) ? "down" : "up"));
+		netxen_indicate_link_status(adapter, linkup);
+		if (linkup)
+			netxen_nic_set_link_parameters(adapter);
+
 	}
-
-	adapter->stats.otherints++;
-
 }
 
 void netxen_nic_gbe_handle_phy_intr(struct netxen_adapter *adapter)
@@ -182,26 +171,28 @@
 
 void netxen_nic_xgbe_handle_phy_intr(struct netxen_adapter *adapter)
 {
-	struct net_device *netdev = adapter->port[0]->netdev;
-	u32 val;
+	struct net_device *netdev = adapter->netdev;
+	u32 val, val1;
 
 	/* WINDOW = 1 */
 	val = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_XG_STATE));
+	val >>= (physical_port[adapter->portnum] * 8);
+	val1 = val & 0xff;
 
-	if (adapter->ahw.xg_linkup == 1 && val != XG_LINK_UP) {
+	if (adapter->ahw.xg_linkup == 1 && val1 != XG_LINK_UP) {
 		printk(KERN_INFO "%s: %s NIC Link is down\n",
 		       netxen_nic_driver_name, netdev->name);
 		adapter->ahw.xg_linkup = 0;
 		/* read twice to clear sticky bits */
 		/* WINDOW = 0 */
-		netxen_nic_read_w0(adapter, NETXEN_NIU_XG_STATUS, &val);
-		netxen_nic_read_w0(adapter, NETXEN_NIU_XG_STATUS, &val);
+		netxen_nic_read_w0(adapter, NETXEN_NIU_XG_STATUS, &val1);
+		netxen_nic_read_w0(adapter, NETXEN_NIU_XG_STATUS, &val1);
 
 		if ((val & 0xffb) != 0xffb) {
 			printk(KERN_INFO "%s ISR: Sync/Align BAD: 0x%08x\n",
-			       netxen_nic_driver_name, val);
+			       netxen_nic_driver_name, val1);
 		}
-	} else if (adapter->ahw.xg_linkup == 0 && val == XG_LINK_UP) {
+	} else if (adapter->ahw.xg_linkup == 0 && val1 == XG_LINK_UP) {
 		printk(KERN_INFO "%s: %s NIC Link is up\n",
 		       netxen_nic_driver_name, netdev->name);
 		adapter->ahw.xg_linkup = 1;
diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c
index 7d2525e..4e32bb6 100644
--- a/drivers/net/netxen/netxen_nic_main.c
+++ b/drivers/net/netxen/netxen_nic_main.c
@@ -36,11 +36,11 @@
 #include "netxen_nic_hw.h"
 
 #include "netxen_nic.h"
-#define DEFINE_GLOBAL_RECV_CRB
 #include "netxen_nic_phan_reg.h"
 
 #include <linux/dma-mapping.h>
 #include <linux/vmalloc.h>
+#include <net/ip.h>
 
 MODULE_DESCRIPTION("NetXen Multi port (1/10) Gigabit Network Driver");
 MODULE_LICENSE("GPL");
@@ -76,6 +76,8 @@
 #endif
 static irqreturn_t netxen_intr(int irq, void *data);
 
+int physical_port[] = {0, 1, 2, 3};
+
 /*  PCI Device ID Table  */
 static struct pci_device_id netxen_pci_tbl[] __devinitdata = {
 	{PCI_DEVICE(0x4040, 0x0001)},
@@ -93,6 +95,67 @@
 struct workqueue_struct *netxen_workq;
 static void netxen_watchdog(unsigned long);
 
+static inline void netxen_nic_update_cmd_producer(struct netxen_adapter *adapter,
+							uint32_t crb_producer)
+{
+	switch (adapter->portnum) {
+		case 0:
+			writel(crb_producer, NETXEN_CRB_NORMALIZE
+					(adapter, CRB_CMD_PRODUCER_OFFSET));
+			return;
+		case 1:
+			writel(crb_producer, NETXEN_CRB_NORMALIZE
+					(adapter, CRB_CMD_PRODUCER_OFFSET_1));
+			return;
+		case 2:
+			writel(crb_producer, NETXEN_CRB_NORMALIZE
+					(adapter, CRB_CMD_PRODUCER_OFFSET_2));
+			return;
+		case 3:
+			writel(crb_producer, NETXEN_CRB_NORMALIZE
+					(adapter, CRB_CMD_PRODUCER_OFFSET_3));
+			return;
+		default:
+			printk(KERN_WARNING "We tried to update "
+					"CRB_CMD_PRODUCER_OFFSET for invalid "
+					"PCI function id %d\n",
+					adapter->portnum);
+			return;
+	}
+}
+
+static inline void netxen_nic_update_cmd_consumer(struct netxen_adapter *adapter,
+							u32 crb_consumer)
+{
+	switch (adapter->portnum) {
+		case 0:
+			writel(crb_consumer, NETXEN_CRB_NORMALIZE
+				(adapter, CRB_CMD_CONSUMER_OFFSET));
+			return;
+		case 1:
+			writel(crb_consumer, NETXEN_CRB_NORMALIZE
+				(adapter, CRB_CMD_CONSUMER_OFFSET_1));
+			return;
+		case 2:
+			writel(crb_consumer, NETXEN_CRB_NORMALIZE
+				(adapter, CRB_CMD_CONSUMER_OFFSET_2));
+			return;
+		case 3:
+			writel(crb_consumer, NETXEN_CRB_NORMALIZE
+				(adapter, CRB_CMD_CONSUMER_OFFSET_3));
+			return;
+		default:
+			printk(KERN_WARNING "We tried to update "
+					"CRB_CMD_PRODUCER_OFFSET for invalid "
+					"PCI function id %d\n",
+					adapter->portnum);
+			return;
+	}
+}
+
+#define	ADAPTER_LIST_SIZE 12
+int netxen_cards_found;
+
 /*
  * netxen_nic_probe()
  *
@@ -110,26 +173,30 @@
 {
 	struct net_device *netdev = NULL;
 	struct netxen_adapter *adapter = NULL;
-	struct netxen_port *port = NULL;
 	void __iomem *mem_ptr0 = NULL;
 	void __iomem *mem_ptr1 = NULL;
 	void __iomem *mem_ptr2 = NULL;
+	unsigned long first_page_group_end;
+	unsigned long first_page_group_start;
+
 
 	u8 __iomem *db_ptr = NULL;
 	unsigned long mem_base, mem_len, db_base, db_len;
-	int pci_using_dac, i, err;
+	int pci_using_dac, i = 0, err;
 	int ring;
 	struct netxen_recv_context *recv_ctx = NULL;
 	struct netxen_rcv_desc_ctx *rcv_desc = NULL;
 	struct netxen_cmd_buffer *cmd_buf_arr = NULL;
 	u64 mac_addr[FLASH_NUM_PORTS + 1];
 	int valid_mac = 0;
+	u32 val;
+	int pci_func_id = PCI_FUNC(pdev->devfn);
 
 	printk(KERN_INFO "%s \n", netxen_nic_driver_string);
-	/* In current scheme, we use only PCI function 0 */
-	if (PCI_FUNC(pdev->devfn) != 0) {
-		DPRINTK(ERR, "NetXen function %d will not be enabled.\n",
-			PCI_FUNC(pdev->devfn));
+
+	if (pdev->class != 0x020000) {
+		printk(KERN_ERR"NetXen function %d, class %x will not"
+				"be enabled.\n",pci_func_id, pdev->class);
 		return -ENODEV;
 	}
 	if ((err = pci_enable_device(pdev)))
@@ -156,18 +223,52 @@
 		pci_using_dac = 0;
 	}
 
+
+	netdev = alloc_etherdev(sizeof(struct netxen_adapter));
+	if(!netdev) {
+		printk(KERN_ERR"%s: Failed to allocate memory for the "
+				"device block.Check system memory resource"
+				" usage.\n", netxen_nic_driver_name);
+		goto err_out_free_res;
+	}
+
+	SET_MODULE_OWNER(netdev);
+	SET_NETDEV_DEV(netdev, &pdev->dev);
+
+	adapter = netdev->priv;
+	memset(adapter, 0 , sizeof(struct netxen_adapter));
+
+	adapter->ahw.pdev = pdev;
+	adapter->ahw.pci_func  = pci_func_id;
+	spin_lock_init(&adapter->tx_lock);
+	spin_lock_init(&adapter->lock);
+
 	/* remap phys address */
 	mem_base = pci_resource_start(pdev, 0);	/* 0 is for BAR 0 */
 	mem_len = pci_resource_len(pdev, 0);
 
 	/* 128 Meg of memory */
-	mem_ptr0 = ioremap(mem_base, FIRST_PAGE_GROUP_SIZE);
-	mem_ptr1 =
-	    ioremap(mem_base + SECOND_PAGE_GROUP_START, SECOND_PAGE_GROUP_SIZE);
-	mem_ptr2 =
-	    ioremap(mem_base + THIRD_PAGE_GROUP_START, THIRD_PAGE_GROUP_SIZE);
+	if (mem_len == NETXEN_PCI_128MB_SIZE) {
+		mem_ptr0 = ioremap(mem_base, FIRST_PAGE_GROUP_SIZE);
+		mem_ptr1 = ioremap(mem_base + SECOND_PAGE_GROUP_START,
+				SECOND_PAGE_GROUP_SIZE);
+		mem_ptr2 = ioremap(mem_base + THIRD_PAGE_GROUP_START,
+				THIRD_PAGE_GROUP_SIZE);
+		first_page_group_start = FIRST_PAGE_GROUP_START;
+		first_page_group_end   = FIRST_PAGE_GROUP_END;
+	} else if (mem_len == NETXEN_PCI_32MB_SIZE) {
+		mem_ptr1 = ioremap(mem_base, SECOND_PAGE_GROUP_SIZE);
+		mem_ptr2 = ioremap(mem_base + THIRD_PAGE_GROUP_START -
+			SECOND_PAGE_GROUP_START, THIRD_PAGE_GROUP_SIZE);
+		first_page_group_start = 0;
+		first_page_group_end   = 0;
+	} else {
+		err = -EIO; 
+		goto err_out_free_netdev;
+	}
 
-	if ((mem_ptr0 == 0UL) || (mem_ptr1 == 0UL) || (mem_ptr2 == 0UL)) {
+	if (((mem_ptr0 == 0UL) && (mem_len == NETXEN_PCI_128MB_SIZE)) ||
+			(mem_ptr1 == 0UL) || (mem_ptr2 == 0UL)) {
 		DPRINTK(ERR,
 			"Cannot remap adapter memory aborting.:"
 			"0 -> %p, 1 -> %p, 2 -> %p\n",
@@ -197,30 +298,87 @@
 	}
 	DPRINTK(INFO, "doorbell ioremaped at %p\n", db_ptr);
 
-/*
- *      Allocate a adapter structure which will manage all the initialization
- *      as well as the common resources for all ports...
- *      all the ports will have pointer to this adapter as well as Adapter
- *      will have pointers of all the ports structures.
- */
+	adapter->ahw.pci_base0 = mem_ptr0;
+	adapter->ahw.first_page_group_start = first_page_group_start;
+	adapter->ahw.first_page_group_end   = first_page_group_end;
+	adapter->ahw.pci_base1 = mem_ptr1;
+	adapter->ahw.pci_base2 = mem_ptr2;
+	adapter->ahw.db_base = db_ptr;
+	adapter->ahw.db_len = db_len;
 
-	/* One adapter structure for all 4 ports....   */
-	adapter = kzalloc(sizeof(struct netxen_adapter), GFP_KERNEL);
-	if (adapter == NULL) {
-		printk(KERN_ERR "%s: Could not allocate adapter memory:%d\n",
-		       netxen_nic_driver_name,
-		       (int)sizeof(struct netxen_adapter));
-		err = -ENOMEM;
-		goto err_out_dbunmap;
-	}
+	adapter->netdev  = netdev;
+	adapter->pdev    = pdev;
+	adapter->portnum = pci_func_id;
 
-	adapter->max_tx_desc_count = MAX_CMD_DESCRIPTORS;
-	adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS;
+	netdev->open		   = netxen_nic_open;
+	netdev->stop		   = netxen_nic_close;
+	netdev->hard_start_xmit    = netxen_nic_xmit_frame;
+	netdev->get_stats	   = netxen_nic_get_stats;	
+	netdev->set_multicast_list = netxen_nic_set_multi;
+	netdev->set_mac_address    = netxen_nic_set_mac;
+	netdev->change_mtu	   = netxen_nic_change_mtu;
+	netdev->tx_timeout	   = netxen_tx_timeout;
+	netdev->watchdog_timeo     = HZ;
+
+	netxen_nic_change_mtu(netdev, netdev->mtu);
+
+	SET_ETHTOOL_OPS(netdev, &netxen_nic_ethtool_ops);
+	netdev->poll = netxen_nic_poll;
+	netdev->weight = NETXEN_NETDEV_WEIGHT;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	netdev->poll_controller = netxen_nic_poll_controller;
+#endif
+	/* ScatterGather support */
+	netdev->features = NETIF_F_SG;
+	netdev->features |= NETIF_F_IP_CSUM;
+	netdev->features |= NETIF_F_TSO;
+
+	if (pci_using_dac)
+		netdev->features |= NETIF_F_HIGHDMA;
+
+	if (pci_enable_msi(pdev)) {
+		adapter->flags &= ~NETXEN_NIC_MSI_ENABLED;
+		printk(KERN_WARNING "%s: unable to allocate MSI interrupt"
+		       " error\n", netxen_nic_driver_name);
+	} else
+		adapter->flags |= NETXEN_NIC_MSI_ENABLED;
+
+	netdev->irq = pdev->irq;
+	INIT_WORK(&adapter->tx_timeout_task, netxen_tx_timeout_task);
+
+	/*
+	 * Set the CRB window to invalid. If any register in window 0 is
+	 * accessed it should set the window to 0 and then reset it to 1.
+	 */
+	adapter->curr_window = 255;
+
+	/* initialize the adapter */
+	netxen_initialize_adapter_hw(adapter);
+
+#ifdef CONFIG_PPC
+	if ((adapter->ahw.boardcfg.board_type ==
+		NETXEN_BRDTYPE_P2_SB31_10G_IMEZ) &&
+			(pci_func_id == 2))
+		    goto err_out_free_adapter;
+#endif /* CONFIG_PPC */
+
+	/*
+	 *  Adapter in our case is quad port so initialize it before
+	 *  initializing the ports
+	 */
+
+	netxen_initialize_adapter_ops(adapter);
+
+	adapter->max_tx_desc_count = MAX_CMD_DESCRIPTORS_HOST;
+	if ((adapter->ahw.boardcfg.board_type == NETXEN_BRDTYPE_P2_SB35_4G) ||
+			(adapter->ahw.boardcfg.board_type == 
+			 NETXEN_BRDTYPE_P2_SB31_2G)) 
+		adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS_1G;
+	else
+		adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS;
 	adapter->max_jumbo_rx_desc_count = MAX_JUMBO_RCV_DESCRIPTORS;
 	adapter->max_lro_rx_desc_count = MAX_LRO_RCV_DESCRIPTORS;
 
-	pci_set_drvdata(pdev, adapter);
-
 	cmd_buf_arr = (struct netxen_cmd_buffer *)vmalloc(TX_RINGSIZE);
 	if (cmd_buf_arr == NULL) {
 		printk(KERN_ERR
@@ -230,6 +388,7 @@
 		goto err_out_free_adapter;
 	}
 	memset(cmd_buf_arr, 0, TX_RINGSIZE);
+	adapter->cmd_buf_arr = cmd_buf_arr;
 
 	for (i = 0; i < MAX_RCV_CTX; ++i) {
 		recv_ctx = &adapter->recv_ctx[i];
@@ -277,34 +436,21 @@
 
 	}
 
-	adapter->cmd_buf_arr = cmd_buf_arr;
-	adapter->ahw.pci_base0 = mem_ptr0;
-	adapter->ahw.pci_base1 = mem_ptr1;
-	adapter->ahw.pci_base2 = mem_ptr2;
-	adapter->ahw.db_base = db_ptr;
-	adapter->ahw.db_len = db_len;
-	spin_lock_init(&adapter->tx_lock);
-	spin_lock_init(&adapter->lock);
 	netxen_initialize_adapter_sw(adapter);	/* initialize the buffers in adapter */
+
+	/* Mezz cards have PCI function 0,2,3 enabled */
+	if ((adapter->ahw.boardcfg.board_type == NETXEN_BRDTYPE_P2_SB31_10G_IMEZ)
+		&& (pci_func_id >= 2))
+			adapter->portnum = pci_func_id - 2;
+
 #ifdef CONFIG_IA64
-	netxen_pinit_from_rom(adapter, 0);
-	udelay(500);
-	netxen_load_firmware(adapter);
+	if(adapter->portnum == 0) {
+		netxen_pinit_from_rom(adapter, 0);
+		udelay(500);
+		netxen_load_firmware(adapter);
+	}
 #endif
 
-	/*
-	 * Set the CRB window to invalid. If any register in window 0 is
-	 * accessed it should set the window to 0 and then reset it to 1.
-	 */
-	adapter->curr_window = 255;
-	/*
-	 *  Adapter in our case is quad port so initialize it before
-	 *  initializing the ports
-	 */
-	netxen_initialize_adapter_hw(adapter);	/* initialize the adapter */
-
-	netxen_initialize_adapter_ops(adapter);
-
 	init_timer(&adapter->watchdog_timer);
 	adapter->ahw.xg_linkup = 0;
 	adapter->watchdog_timer.function = &netxen_watchdog;
@@ -314,12 +460,12 @@
 	adapter->proc_cmd_buf_counter = 0;
 	adapter->ahw.revision_id = nx_p2_id;
 
-	if (pci_enable_msi(pdev)) {
-		adapter->flags &= ~NETXEN_NIC_MSI_ENABLED;
-		printk(KERN_WARNING "%s: unable to allocate MSI interrupt"
-		       " error\n", netxen_nic_driver_name);
-	} else
-		adapter->flags |= NETXEN_NIC_MSI_ENABLED;
+	/* make sure Window == 1 */
+	netxen_nic_pci_change_crbwindow(adapter, 1);
+
+	netxen_nic_update_cmd_producer(adapter, 0);
+	netxen_nic_update_cmd_consumer(adapter, 0);
+	writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_HOST_CMD_ADDR_LO));
 
 	if (netxen_is_flash_supported(adapter) == 0 &&
 	    netxen_get_flash_mac_addr(adapter, mac_addr) == 0)
@@ -327,153 +473,118 @@
 	else
 		valid_mac = 0;
 
-	/*
-	 * Initialize all the CRB registers here.
-	 */
-	writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_CMD_PRODUCER_OFFSET));
-	writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_CMD_CONSUMER_OFFSET));
-	writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_HOST_CMD_ADDR_LO));
+	if (valid_mac) {
+		unsigned char *p = (unsigned char *)&mac_addr[adapter->portnum];
+		netdev->dev_addr[0] = *(p + 5);
+		netdev->dev_addr[1] = *(p + 4);
+		netdev->dev_addr[2] = *(p + 3);
+		netdev->dev_addr[3] = *(p + 2);
+		netdev->dev_addr[4] = *(p + 1);
+		netdev->dev_addr[5] = *(p + 0);
 
-	/* do this before waking up pegs so that we have valid dummy dma addr */
-	err = netxen_initialize_adapter_offload(adapter);
-	if (err) {
+		memcpy(netdev->perm_addr, netdev->dev_addr,
+			netdev->addr_len);
+		if (!is_valid_ether_addr(netdev->perm_addr)) {
+			printk(KERN_ERR "%s: Bad MAC address "
+				"%02x:%02x:%02x:%02x:%02x:%02x.\n",
+				netxen_nic_driver_name,
+				netdev->dev_addr[0],
+				netdev->dev_addr[1],
+				netdev->dev_addr[2],
+				netdev->dev_addr[3],
+				netdev->dev_addr[4],
+				netdev->dev_addr[5]);
+		} else {
+			if (adapter->macaddr_set)
+				adapter->macaddr_set(adapter,
+							netdev->dev_addr);
+		}
+	}
+
+	if (adapter->portnum == 0) {
+		err = netxen_initialize_adapter_offload(adapter);
+		if (err) 
+			goto err_out_free_rx_buffer;
+		val = readl(NETXEN_CRB_NORMALIZE(adapter, 
+					NETXEN_CAM_RAM(0x1fc)));
+		if (val == 0x55555555) {
+		    /* This is the first boot after power up */
+		    val = readl(NETXEN_CRB_NORMALIZE(adapter,
+					NETXEN_ROMUSB_GLB_SW_RESET));
+		    printk(KERN_INFO"NetXen: read 0x%08x for reset reg.\n",val);
+		    if (val != 0x80000f) {
+			/* clear the register for future unloads/loads */
+			writel(0, NETXEN_CRB_NORMALIZE(adapter,
+						NETXEN_CAM_RAM(0x1fc)));
+			printk(KERN_ERR "ERROR in NetXen HW init sequence.\n");
+			err = -ENODEV;
+			goto err_out_free_dev;
+		    }
+
+		    /* clear the register for future unloads/loads */
+		    writel(0, NETXEN_CRB_NORMALIZE(adapter, 
+					    NETXEN_CAM_RAM(0x1fc)));
+		}
+		printk(KERN_INFO "State: 0x%0x\n",
+			readl(NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE)));
+
+		/*
+		 * Tell the hardware our version number.
+		 */
+		i = (_NETXEN_NIC_LINUX_MAJOR << 16) 
+			| ((_NETXEN_NIC_LINUX_MINOR << 8))
+			| (_NETXEN_NIC_LINUX_SUBVERSION);
+		writel(i, NETXEN_CRB_NORMALIZE(adapter, CRB_DRIVER_VERSION));
+
+		/* Unlock the HW, prompting the boot sequence */
+		writel(1,
+			NETXEN_CRB_NORMALIZE(adapter,
+				NETXEN_ROMUSB_GLB_PEGTUNE_DONE));
+		/* Handshake with the card before we register the devices. */
+		netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE);
+	}
+
+	/*
+	 * See if the firmware gave us a virtual-physical port mapping.
+	 */
+	i = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_V2P(adapter->portnum)));
+	if (i != 0x55555555)
+		physical_port[adapter->portnum] = i;
+
+	netif_carrier_off(netdev);
+	netif_stop_queue(netdev);
+
+	if ((err = register_netdev(netdev))) {
+		printk(KERN_ERR "%s: register_netdev failed port #%d"
+			       " aborting\n", netxen_nic_driver_name,
+			       adapter->portnum);
+		err = -EIO;
 		goto err_out_free_dev;
 	}
 
-	/* Unlock the HW, prompting the boot sequence */
-	writel(1,
-	       NETXEN_CRB_NORMALIZE(adapter, NETXEN_ROMUSB_GLB_PEGTUNE_DONE));
-
-	/* Handshake with the card before we register the devices. */
-	netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE);
-
-	/* initialize the all the ports */
-	adapter->active_ports = 0;
-
-	for (i = 0; i < adapter->ahw.max_ports; i++) {
-		netdev = alloc_etherdev(sizeof(struct netxen_port));
-		if (!netdev) {
-			printk(KERN_ERR "%s: could not allocate netdev for port"
-			       " %d\n", netxen_nic_driver_name, i + 1);
-			goto err_out_free_dev;
-		}
-
-		SET_MODULE_OWNER(netdev);
-		SET_NETDEV_DEV(netdev, &pdev->dev);
-
-		port = netdev_priv(netdev);
-		port->netdev = netdev;
-		port->pdev = pdev;
-		port->adapter = adapter;
-		port->portnum = i;	/* Gigabit port number from 0-3 */
-
-		netdev->open = netxen_nic_open;
-		netdev->stop = netxen_nic_close;
-		netdev->hard_start_xmit = netxen_nic_xmit_frame;
-		netdev->get_stats = netxen_nic_get_stats;
-		netdev->set_multicast_list = netxen_nic_set_multi;
-		netdev->set_mac_address = netxen_nic_set_mac;
-		netdev->change_mtu = netxen_nic_change_mtu;
-		netdev->tx_timeout = netxen_tx_timeout;
-		netdev->watchdog_timeo = HZ;
-
-		netxen_nic_change_mtu(netdev, netdev->mtu);
-
-		SET_ETHTOOL_OPS(netdev, &netxen_nic_ethtool_ops);
-		netdev->poll = netxen_nic_poll;
-		netdev->weight = NETXEN_NETDEV_WEIGHT;
-#ifdef CONFIG_NET_POLL_CONTROLLER
-		netdev->poll_controller = netxen_nic_poll_controller;
-#endif
-		/* ScatterGather support */
-		netdev->features = NETIF_F_SG;
-		netdev->features |= NETIF_F_IP_CSUM;
-		netdev->features |= NETIF_F_TSO;
-
-		if (pci_using_dac)
-			netdev->features |= NETIF_F_HIGHDMA;
-
-		if (valid_mac) {
-			unsigned char *p = (unsigned char *)&mac_addr[i];
-			netdev->dev_addr[0] = *(p + 5);
-			netdev->dev_addr[1] = *(p + 4);
-			netdev->dev_addr[2] = *(p + 3);
-			netdev->dev_addr[3] = *(p + 2);
-			netdev->dev_addr[4] = *(p + 1);
-			netdev->dev_addr[5] = *(p + 0);
-
-			memcpy(netdev->perm_addr, netdev->dev_addr,
-			       netdev->addr_len);
-			if (!is_valid_ether_addr(netdev->perm_addr)) {
-				printk(KERN_ERR "%s: Bad MAC address "
-				       "%02x:%02x:%02x:%02x:%02x:%02x.\n",
-				       netxen_nic_driver_name,
-				       netdev->dev_addr[0],
-				       netdev->dev_addr[1],
-				       netdev->dev_addr[2],
-				       netdev->dev_addr[3],
-				       netdev->dev_addr[4],
-				       netdev->dev_addr[5]);
-			} else {
-				if (adapter->macaddr_set)
-					adapter->macaddr_set(port,
-							     netdev->dev_addr);
-			}
-		}
-		INIT_WORK(&port->tx_timeout_task, netxen_tx_timeout_task);
-		netif_carrier_off(netdev);
-		netif_stop_queue(netdev);
-
-		if ((err = register_netdev(netdev))) {
-			printk(KERN_ERR "%s: register_netdev failed port #%d"
-			       " aborting\n", netxen_nic_driver_name, i + 1);
-			err = -EIO;
-			free_netdev(netdev);
-			goto err_out_free_dev;
-		}
-		adapter->port_count++;
-		adapter->port[i] = port;
-	}
-	writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE));
-	netxen_pinit_from_rom(adapter, 0);
-	udelay(500);
-	netxen_load_firmware(adapter);
-	netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE);
-	/*
-	 * delay a while to ensure that the Pegs are up & running.
-	 * Otherwise, we might see some flaky behaviour.
-	 */
-	udelay(100);
+	pci_set_drvdata(pdev, adapter);
 
 	switch (adapter->ahw.board_type) {
-	case NETXEN_NIC_GBE:
-		printk("%s: QUAD GbE board initialized\n",
-		       netxen_nic_driver_name);
-		break;
+		case NETXEN_NIC_GBE:
+			printk(KERN_INFO "%s: QUAD GbE board initialized\n",
+			       netxen_nic_driver_name);
+			break;
 
-	case NETXEN_NIC_XGBE:
-		printk("%s: XGbE board initialized\n", netxen_nic_driver_name);
-		break;
+		case NETXEN_NIC_XGBE:
+			printk(KERN_INFO "%s: XGbE board initialized\n", 
+					netxen_nic_driver_name);
+			break;
 	}
 
 	adapter->driver_mismatch = 0;
 
 	return 0;
 
-      err_out_free_dev:
-	if (adapter->flags & NETXEN_NIC_MSI_ENABLED)
-		pci_disable_msi(pdev);
-	for (i = 0; i < adapter->port_count; i++) {
-		port = adapter->port[i];
-		if ((port) && (port->netdev)) {
-			unregister_netdev(port->netdev);
-			free_netdev(port->netdev);
-		}
-	}
+err_out_free_dev:
+	if (adapter->portnum == 0)
+		netxen_free_adapter_offload(adapter);
 
-	netxen_free_adapter_offload(adapter);
-
-      err_out_free_rx_buffer:
+err_out_free_rx_buffer:
 	for (i = 0; i < MAX_RCV_CTX; ++i) {
 		recv_ctx = &adapter->recv_ctx[i];
 		for (ring = 0; ring < NUM_RCV_DESC_RINGS; ring++) {
@@ -486,15 +597,16 @@
 	}
 	vfree(cmd_buf_arr);
 
-      err_out_free_adapter:
-	pci_set_drvdata(pdev, NULL);
-	kfree(adapter);
+err_out_free_adapter:
+	if (adapter->flags & NETXEN_NIC_MSI_ENABLED)
+		pci_disable_msi(pdev);
 
-      err_out_dbunmap:
+	pci_set_drvdata(pdev, NULL);
+
 	if (db_ptr)
 		iounmap(db_ptr);
 
-      err_out_iounmap:
+err_out_iounmap:
 	if (mem_ptr0)
 		iounmap(mem_ptr0);
 	if (mem_ptr1)
@@ -502,9 +614,13 @@
 	if (mem_ptr2)
 		iounmap(mem_ptr2);
 
-      err_out_free_res:
+err_out_free_netdev:
+	free_netdev(netdev);
+
+err_out_free_res:
 	pci_release_regions(pdev);
-      err_out_disable_pdev:
+
+err_out_disable_pdev:
 	pci_disable_device(pdev);
 	return err;
 }
@@ -512,7 +628,7 @@
 static void __devexit netxen_nic_remove(struct pci_dev *pdev)
 {
 	struct netxen_adapter *adapter;
-	struct netxen_port *port;
+	struct net_device *netdev;
 	struct netxen_rx_buffer *buffer;
 	struct netxen_recv_context *recv_ctx;
 	struct netxen_rcv_desc_ctx *rcv_desc;
@@ -523,39 +639,35 @@
 	if (adapter == NULL)
 		return;
 
+	netdev = adapter->netdev;
+
+	netxen_nic_disable_int(adapter);
 	if (adapter->irq)
 		free_irq(adapter->irq, adapter);
-	netxen_nic_stop_all_ports(adapter);
-	/* leave the hw in the same state as reboot */
-	writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE));
-	netxen_pinit_from_rom(adapter, 0);
-	udelay(500);
-	netxen_load_firmware(adapter);
-	netxen_free_adapter_offload(adapter);
-
-	mdelay(1000);		/* Delay for a while to drain the DMA engines */
-	for (i = 0; i < adapter->port_count; i++) {
-		port = adapter->port[i];
-		if ((port) && (port->netdev)) {
-			unregister_netdev(port->netdev);
-			free_netdev(port->netdev);
-		}
-	}
+	
+	if (adapter->stop_port)
+		adapter->stop_port(adapter);
 
 	if ((adapter->flags & NETXEN_NIC_MSI_ENABLED))
 		pci_disable_msi(pdev);
+
+	if (adapter->portnum == 0)
+		netxen_free_adapter_offload(adapter);
+
+	if (adapter->irq)
+		free_irq(adapter->irq, adapter);
+	if(adapter->portnum == 0) {
+		/* leave the hw in the same state as reboot */
+		writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE));
+		netxen_pinit_from_rom(adapter, 0);
+		udelay(500);
+		netxen_load_firmware(adapter);
+		netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE);
+	}
+
 	if (adapter->is_up == NETXEN_ADAPTER_UP_MAGIC)
 		netxen_free_hw_resources(adapter);
 
-	iounmap(adapter->ahw.db_base);
-	iounmap(adapter->ahw.pci_base0);
-	iounmap(adapter->ahw.pci_base1);
-	iounmap(adapter->ahw.pci_base2);
-
-	pci_release_regions(pdev);
-	pci_disable_device(pdev);
-	pci_set_drvdata(pdev, NULL);
-
 	for (ctxid = 0; ctxid < MAX_RCV_CTX; ++ctxid) {
 		recv_ctx = &adapter->recv_ctx[ctxid];
 		for (ring = 0; ring < NUM_RCV_DESC_RINGS; ring++) {
@@ -574,8 +686,20 @@
 		}
 	}
 
+	unregister_netdev(netdev);
+
 	vfree(adapter->cmd_buf_arr);
-	kfree(adapter);
+
+	iounmap(adapter->ahw.db_base);
+	iounmap(adapter->ahw.pci_base0);
+	iounmap(adapter->ahw.pci_base1);
+	iounmap(adapter->ahw.pci_base2);
+
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
+
+	free_netdev(netdev);
 }
 
 /*
@@ -584,8 +708,7 @@
  */
 static int netxen_nic_open(struct net_device *netdev)
 {
-	struct netxen_port *port = netdev_priv(netdev);
-	struct netxen_adapter *adapter = port->adapter;
+	struct netxen_adapter *adapter = (struct netxen_adapter *)netdev->priv;
 	int err = 0;
 	int ctx, ring;
 
@@ -596,8 +719,6 @@
 			return -EIO;
 		}
 		netxen_nic_flash_print(adapter);
-		if (adapter->init_niu)
-			adapter->init_niu(adapter);
 
 		/* setup all the resources for the Phantom... */
 		/* this include the descriptors for rcv, tx, and status */
@@ -608,21 +729,14 @@
 			       err);
 			return err;
 		}
-		if (adapter->init_port
-		    && adapter->init_port(adapter, port->portnum) != 0) {
-			printk(KERN_ERR "%s: Failed to initialize port %d\n",
-			       netxen_nic_driver_name, port->portnum);
-			netxen_free_hw_resources(adapter);
-			return -EIO;
-		}
 		for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) {
 			for (ring = 0; ring < NUM_RCV_DESC_RINGS; ring++)
 				netxen_post_rx_buffers(adapter, ctx, ring);
 		}
 		adapter->irq = adapter->ahw.pdev->irq;
-		err = request_irq(adapter->ahw.pdev->irq, &netxen_intr,
-				  IRQF_SHARED | IRQF_SAMPLE_RANDOM,
-				  netdev->name, adapter);
+		err = request_irq(adapter->ahw.pdev->irq, netxen_intr,
+				  SA_SHIRQ | SA_SAMPLE_RANDOM, netdev->name,
+				  adapter);
 		if (err) {
 			printk(KERN_ERR "request_irq failed with: %d\n", err);
 			netxen_free_hw_resources(adapter);
@@ -631,23 +745,28 @@
 
 		adapter->is_up = NETXEN_ADAPTER_UP_MAGIC;
 	}
-	adapter->active_ports++;
-	if (adapter->active_ports == 1) {
-		if (!adapter->driver_mismatch)
-			mod_timer(&adapter->watchdog_timer, jiffies);
+	if (!adapter->driver_mismatch)
+		mod_timer(&adapter->watchdog_timer, jiffies);
 
-		netxen_nic_enable_int(adapter);
-	}
+	netxen_nic_enable_int(adapter);
 
 	/* Done here again so that even if phantom sw overwrote it,
 	 * we set it */
 	if (adapter->macaddr_set)
-		adapter->macaddr_set(port, netdev->dev_addr);
-	netxen_nic_set_link_parameters(port);
+		adapter->macaddr_set(adapter, netdev->dev_addr);
+	if (adapter->init_port
+	    && adapter->init_port(adapter, adapter->portnum) != 0) {
+	    del_timer_sync(&adapter->watchdog_timer);
+		printk(KERN_ERR "%s: Failed to initialize port %d\n",
+				netxen_nic_driver_name, adapter->portnum);
+		return -EIO;
+	}
+
+	netxen_nic_set_link_parameters(adapter);
 
 	netxen_nic_set_multi(netdev);
 	if (adapter->set_mtu)
-		adapter->set_mtu(port, netdev->mtu);
+		adapter->set_mtu(adapter, netdev->mtu);
 
 	if (!adapter->driver_mismatch)
 		netif_start_queue(netdev);
@@ -660,8 +779,7 @@
  */
 static int netxen_nic_close(struct net_device *netdev)
 {
-	struct netxen_port *port = netdev_priv(netdev);
-	struct netxen_adapter *adapter = port->adapter;
+	struct netxen_adapter *adapter = netdev_priv(netdev);
 	int i, j;
 	struct netxen_cmd_buffer *cmd_buff;
 	struct netxen_skb_frag *buffrag;
@@ -669,47 +787,39 @@
 	netif_carrier_off(netdev);
 	netif_stop_queue(netdev);
 
-	adapter->active_ports--;
-
-	if (!adapter->active_ports) {
-		netxen_nic_disable_int(adapter);
-		cmd_buff = adapter->cmd_buf_arr;
-		for (i = 0; i < adapter->max_tx_desc_count; i++) {
-			buffrag = cmd_buff->frag_array;
+	cmd_buff = adapter->cmd_buf_arr;
+	for (i = 0; i < adapter->max_tx_desc_count; i++) {
+		buffrag = cmd_buff->frag_array;
+		if (buffrag->dma) {
+			pci_unmap_single(adapter->pdev, buffrag->dma,
+					 buffrag->length, PCI_DMA_TODEVICE);
+			buffrag->dma = (u64) NULL;
+		}
+		for (j = 0; j < cmd_buff->frag_count; j++) {
+			buffrag++;
 			if (buffrag->dma) {
-				pci_unmap_single(port->pdev, buffrag->dma,
-						 buffrag->length,
-						 PCI_DMA_TODEVICE);
+				pci_unmap_page(adapter->pdev, buffrag->dma,
+					       buffrag->length, 
+					       PCI_DMA_TODEVICE);
 				buffrag->dma = (u64) NULL;
 			}
-			for (j = 0; j < cmd_buff->frag_count; j++) {
-				buffrag++;
-				if (buffrag->dma) {
-					pci_unmap_page(port->pdev,
-						       buffrag->dma,
-						       buffrag->length,
-						       PCI_DMA_TODEVICE);
-					buffrag->dma = (u64) NULL;
-				}
-			}
-			/* Free the skb we received in netxen_nic_xmit_frame */
-			if (cmd_buff->skb) {
-				dev_kfree_skb_any(cmd_buff->skb);
-				cmd_buff->skb = NULL;
-			}
-			cmd_buff++;
 		}
-		FLUSH_SCHEDULED_WORK();
-		del_timer_sync(&adapter->watchdog_timer);
+		/* Free the skb we received in netxen_nic_xmit_frame */
+		if (cmd_buff->skb) {
+			dev_kfree_skb_any(cmd_buff->skb);
+			cmd_buff->skb = NULL;
+		}
+		cmd_buff++;
 	}
+	FLUSH_SCHEDULED_WORK();
+	del_timer_sync(&adapter->watchdog_timer);
 
 	return 0;
 }
 
 static int netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
 {
-	struct netxen_port *port = netdev_priv(netdev);
-	struct netxen_adapter *adapter = port->adapter;
+	struct netxen_adapter *adapter = netdev_priv(netdev);
 	struct netxen_hardware_context *hw = &adapter->ahw;
 	unsigned int first_seg_len = skb->len - skb->data_len;
 	struct netxen_skb_frag *buffrag;
@@ -727,12 +837,12 @@
 	u32 last_cmd_consumer = 0;
 	int no_of_desc;
 
-	port->stats.xmitcalled++;
+	adapter->stats.xmitcalled++;
 	frag_count = skb_shinfo(skb)->nr_frags + 1;
 
 	if (unlikely(skb->len <= 0)) {
 		dev_kfree_skb_any(skb);
-		port->stats.badskblen++;
+		adapter->stats.badskblen++;
 		return NETDEV_TX_OK;
 	}
 
@@ -741,7 +851,7 @@
 		       "too large, can handle only %d frags\n",
 		       netxen_nic_driver_name, netdev->name,
 		       frag_count, MAX_BUFFERS_PER_CMD);
-		port->stats.txdropped++;
+		adapter->stats.txdropped++;
 		if ((++dropped_packet & 0xff) == 0xff)
 			printk("%s: %s droppped packets = %d\n",
 			       netxen_nic_driver_name, netdev->name,
@@ -758,7 +868,7 @@
 	 */
       retry_getting_window:
 	spin_lock_bh(&adapter->tx_lock);
-	if (adapter->total_threads == MAX_XMIT_PRODUCERS) {
+	if (adapter->total_threads >= MAX_XMIT_PRODUCERS) {
 		spin_unlock_bh(&adapter->tx_lock);
 		/*
 		 * Yield CPU
@@ -778,9 +888,8 @@
 		if (skb_shinfo(skb)->gso_size > 0) {
 
 			no_of_desc++;
-			if (((skb->nh.iph)->ihl * sizeof(u32)) +
-			    ((skb->h.th)->doff * sizeof(u32)) +
-			    sizeof(struct ethhdr) >
+			if ((ip_hdrlen(skb) + tcp_hdrlen(skb) +
+			     sizeof(struct ethhdr)) >
 			    (sizeof(struct cmd_desc_type0) - 2)) {
 				no_of_desc++;
 			}
@@ -792,15 +901,8 @@
 	if ((k + no_of_desc) >=
 	    ((last_cmd_consumer <= k) ? last_cmd_consumer + max_tx_desc_count :
 	     last_cmd_consumer)) {
-		port->stats.nocmddescriptor++;
-		DPRINTK(ERR, "No command descriptors available,"
-			" producer = %d, consumer = %d count=%llu,"
-			" dropping packet\n", producer,
-			adapter->last_cmd_consumer,
-			port->stats.nocmddescriptor);
-
 		netif_stop_queue(netdev);
-		port->flags |= NETXEN_NETDEV_STATUS;
+		adapter->flags |= NETXEN_NETDEV_STATUS;
 		spin_unlock_bh(&adapter->tx_lock);
 		return NETDEV_TX_BUSY;
 	}
@@ -828,16 +930,17 @@
 	pbuf->skb = skb;
 	pbuf->cmd = TX_ETHER_PKT;
 	pbuf->frag_count = frag_count;
-	pbuf->port = port->portnum;
+	pbuf->port = adapter->portnum;
 	buffrag = &pbuf->frag_array[0];
-	buffrag->dma = pci_map_single(port->pdev, skb->data, first_seg_len,
+	buffrag->dma = pci_map_single(adapter->pdev, skb->data, first_seg_len,
 				      PCI_DMA_TODEVICE);
 	buffrag->length = first_seg_len;
 	netxen_set_cmd_desc_totallength(hwdesc, skb->len);
 	netxen_set_cmd_desc_num_of_buff(hwdesc, frag_count);
 	netxen_set_cmd_desc_opcode(hwdesc, TX_ETHER_PKT);
 
-	netxen_set_cmd_desc_port(hwdesc, port->portnum);
+	netxen_set_cmd_desc_port(hwdesc, adapter->portnum);
+	netxen_set_cmd_desc_ctxid(hwdesc, adapter->portnum);
 	hwdesc->buffer1_length = cpu_to_le16(first_seg_len);
 	hwdesc->addr_buffer1 = cpu_to_le64(buffrag->dma);
 
@@ -860,7 +963,7 @@
 		offset = frag->page_offset;
 
 		temp_len = len;
-		temp_dma = pci_map_page(port->pdev, frag->page, offset,
+		temp_dma = pci_map_page(adapter->pdev, frag->page, offset,
 					len, PCI_DMA_TODEVICE);
 
 		buffrag++;
@@ -920,26 +1023,36 @@
 			/* copy the next 64 bytes - should be enough except
 			 * for pathological case
 			 */
-			memcpy((void *)hwdesc, (void *)(skb->data) +
-			       first_hdr_len, hdr_len - first_hdr_len);
+			skb_copy_from_linear_data_offset(skb, first_hdr_len,
+							 hwdesc,
+							 (hdr_len -
+							  first_hdr_len));
 			producer = get_next_index(producer, max_tx_desc_count);
 		}
 	}
+
+	i = netxen_get_cmd_desc_totallength(&hw->cmd_desc_head[saved_producer]);
+
+	hw->cmd_desc_head[saved_producer].flags_opcode =
+		cpu_to_le16(hw->cmd_desc_head[saved_producer].flags_opcode);
+	hw->cmd_desc_head[saved_producer].num_of_buffers_total_length =
+	  cpu_to_le32(hw->cmd_desc_head[saved_producer].
+			  num_of_buffers_total_length);
+
 	spin_lock_bh(&adapter->tx_lock);
-	port->stats.txbytes +=
-	    netxen_get_cmd_desc_totallength(&hw->cmd_desc_head[saved_producer]);
+	adapter->stats.txbytes += i;
+
 	/* Code to update the adapter considering how many producer threads
 	   are currently working */
 	if ((--adapter->num_threads) == 0) {
 		/* This is the last thread */
 		u32 crb_producer = adapter->cmd_producer;
-		writel(crb_producer,
-		       NETXEN_CRB_NORMALIZE(adapter, CRB_CMD_PRODUCER_OFFSET));
+		netxen_nic_update_cmd_producer(adapter, crb_producer);
 		wmb();
 		adapter->total_threads = 0;
 	}
 
-	port->stats.xmitfinished++;
+	adapter->stats.xmitfinished++;
 	spin_unlock_bh(&adapter->tx_lock);
 
 	netdev->trans_start = jiffies;
@@ -959,27 +1072,26 @@
 
 static void netxen_tx_timeout(struct net_device *netdev)
 {
-	struct netxen_port *port = (struct netxen_port *)netdev_priv(netdev);
-
-	SCHEDULE_WORK(&port->tx_timeout_task);
+	struct netxen_adapter *adapter = (struct netxen_adapter *)
+						netdev_priv(netdev);
+	SCHEDULE_WORK(&adapter->tx_timeout_task);
 }
 
 static void netxen_tx_timeout_task(struct work_struct *work)
 {
-	struct netxen_port *port =
-		container_of(work, struct netxen_port, tx_timeout_task);
-	struct net_device *netdev = port->netdev;
+	struct netxen_adapter *adapter = 
+		container_of(work, struct netxen_adapter, tx_timeout_task);
 	unsigned long flags;
 
 	printk(KERN_ERR "%s %s: transmit timeout, resetting.\n",
-	       netxen_nic_driver_name, netdev->name);
+	       netxen_nic_driver_name, adapter->netdev->name);
 
-	spin_lock_irqsave(&port->adapter->lock, flags);
-	netxen_nic_close(netdev);
-	netxen_nic_open(netdev);
-	spin_unlock_irqrestore(&port->adapter->lock, flags);
-	netdev->trans_start = jiffies;
-	netif_wake_queue(netdev);
+	spin_lock_irqsave(&adapter->lock, flags);
+	netxen_nic_close(adapter->netdev);
+	netxen_nic_open(adapter->netdev);
+	spin_unlock_irqrestore(&adapter->lock, flags);
+	adapter->netdev->trans_start = jiffies;
+	netif_wake_queue(adapter->netdev);
 }
 
 static int
@@ -988,17 +1100,16 @@
 	u32 ret = 0;
 
 	DPRINTK(INFO, "Entered handle ISR\n");
-
 	adapter->stats.ints++;
 
 	if (!(adapter->flags & NETXEN_NIC_MSI_ENABLED)) {
 		int count = 0;
 		u32 mask;
-		mask = readl(pci_base_offset(adapter, ISR_INT_VECTOR));
-		if ((mask & 0x80) == 0) {
-			/* not our interrupt */
+		u32 our_int = 0;
+		our_int = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_INT_VECTOR));
+		/* not our interrupt */
+		if ((our_int & (0x80 << adapter->portnum)) == 0)
 			return ret;
-		}
 		netxen_nic_disable_int(adapter);
 		/* Window = 0 or 1 */
 		do {
@@ -1010,7 +1121,6 @@
 			printk("Could not disable interrupt completely\n");
 
 	}
-	adapter->stats.hostints++;
 
 	if (netxen_nic_rx_has_work(adapter) || netxen_nic_tx_has_work(adapter)) {
 		if (netif_rx_schedule_prep(netdev)) {
@@ -1044,33 +1154,24 @@
 irqreturn_t netxen_intr(int irq, void *data)
 {
 	struct netxen_adapter *adapter;
-	struct netxen_port *port;
 	struct net_device *netdev;
-	int i;
 
 	if (unlikely(!irq)) {
 		return IRQ_NONE;	/* Not our interrupt */
 	}
 
 	adapter = (struct netxen_adapter *)data;
-	for (i = 0; i < adapter->ahw.max_ports; i++) {
-		port = adapter->port[i];
-		netdev = port->netdev;
-
-		/* process our status queue (for all 4 ports) */
-		if (netif_running(netdev)) {
-			netxen_handle_int(adapter, netdev);
-			break;
-		}
-	}
+	netdev  = adapter->netdev;
+	/* process our status queue (for all 4 ports) */
+	if (netif_running(netdev))
+		netxen_handle_int(adapter, netdev);
 
 	return IRQ_HANDLED;
 }
 
 static int netxen_nic_poll(struct net_device *netdev, int *budget)
 {
-	struct netxen_port *port = (struct netxen_port *)netdev_priv(netdev);
-	struct netxen_adapter *adapter = port->adapter;
+	struct netxen_adapter *adapter = netdev_priv(netdev);
 	int work_to_do = min(*budget, netdev->quota);
 	int done = 1;
 	int ctx;
@@ -1078,7 +1179,6 @@
 	int work_done = 0;
 
 	DPRINTK(INFO, "polling for %d descriptors\n", *budget);
-	port->stats.polled++;
 
 	work_done = 0;
 	for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) {
@@ -1122,8 +1222,7 @@
 #ifdef CONFIG_NET_POLL_CONTROLLER
 static void netxen_nic_poll_controller(struct net_device *netdev)
 {
-	struct netxen_port *port = netdev_priv(netdev);
-	struct netxen_adapter *adapter = port->adapter;
+	struct netxen_adapter *adapter = netdev_priv(netdev);
 	disable_irq(adapter->irq);
 	netxen_intr(adapter->irq, adapter);
 	enable_irq(adapter->irq);
diff --git a/drivers/net/netxen/netxen_nic_niu.c b/drivers/net/netxen/netxen_nic_niu.c
index d5d9507..cef90a7 100644
--- a/drivers/net/netxen/netxen_nic_niu.c
+++ b/drivers/net/netxen/netxen_nic_niu.c
@@ -88,12 +88,13 @@
  *	  -1 on error
  *
  */
-int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long phy,
-			    long reg, __u32 * readval)
+int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long reg, 
+				__u32 * readval)
 {
 	long timeout = 0;
 	long result = 0;
 	long restore = 0;
+	long phy = physical_port[adapter->portnum];
 	__u32 address;
 	__u32 command;
 	__u32 status;
@@ -183,12 +184,13 @@
  *	  -1 on error
  *
  */
-int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter,
-			     long phy, long reg, __u32 val)
+int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter, long reg, 
+				__u32 val)
 {
 	long timeout = 0;
 	long result = 0;
 	long restore = 0;
+	long phy = physical_port[adapter->portnum];
 	__u32 address;
 	__u32 command;
 	__u32 status;
@@ -258,15 +260,13 @@
 	return result;
 }
 
-int netxen_niu_xgbe_enable_phy_interrupts(struct netxen_adapter *adapter,
-					  int port)
+int netxen_niu_xgbe_enable_phy_interrupts(struct netxen_adapter *adapter)
 {
 	netxen_crb_writelit_adapter(adapter, NETXEN_NIU_INT_MASK, 0x3f);
 	return 0;
 }
 
-int netxen_niu_gbe_enable_phy_interrupts(struct netxen_adapter *adapter,
-					 int port)
+int netxen_niu_gbe_enable_phy_interrupts(struct netxen_adapter *adapter)
 {
 	int result = 0;
 	__u32 enable = 0;
@@ -275,7 +275,7 @@
 	netxen_set_phy_int_speed_changed(enable);
 
 	if (0 !=
-	    netxen_niu_gbe_phy_write(adapter, port,
+	    netxen_niu_gbe_phy_write(adapter, 
 				     NETXEN_NIU_GB_MII_MGMT_ADDR_INT_ENABLE,
 				     enable))
 		result = -EIO;
@@ -283,38 +283,34 @@
 	return result;
 }
 
-int netxen_niu_xgbe_disable_phy_interrupts(struct netxen_adapter *adapter,
-					   int port)
+int netxen_niu_xgbe_disable_phy_interrupts(struct netxen_adapter *adapter)
 {
 	netxen_crb_writelit_adapter(adapter, NETXEN_NIU_INT_MASK, 0x7f);
 	return 0;
 }
 
-int netxen_niu_gbe_disable_phy_interrupts(struct netxen_adapter *adapter,
-					  int port)
+int netxen_niu_gbe_disable_phy_interrupts(struct netxen_adapter *adapter)
 {
 	int result = 0;
 	if (0 !=
-	    netxen_niu_gbe_phy_write(adapter, port,
+	    netxen_niu_gbe_phy_write(adapter,
 				     NETXEN_NIU_GB_MII_MGMT_ADDR_INT_ENABLE, 0))
 		result = -EIO;
 
 	return result;
 }
 
-int netxen_niu_xgbe_clear_phy_interrupts(struct netxen_adapter *adapter,
-					 int port)
+int netxen_niu_xgbe_clear_phy_interrupts(struct netxen_adapter *adapter)
 {
 	netxen_crb_writelit_adapter(adapter, NETXEN_NIU_ACTIVE_INT, -1);
 	return 0;
 }
 
-int netxen_niu_gbe_clear_phy_interrupts(struct netxen_adapter *adapter,
-					int port)
+int netxen_niu_gbe_clear_phy_interrupts(struct netxen_adapter *adapter)
 {
 	int result = 0;
 	if (0 !=
-	    netxen_niu_gbe_phy_write(adapter, port,
+	    netxen_niu_gbe_phy_write(adapter, 
 				     NETXEN_NIU_GB_MII_MGMT_ADDR_INT_STATUS,
 				     -EIO))
 		result = -EIO;
@@ -355,9 +351,9 @@
 					    0x5);
 	}
 
-	if (netxen_niu_gbe_enable_phy_interrupts(adapter, port))
+	if (netxen_niu_gbe_enable_phy_interrupts(adapter))
 		printk(KERN_ERR PFX "ERROR enabling PHY interrupts\n");
-	if (netxen_niu_gbe_clear_phy_interrupts(adapter, port))
+	if (netxen_niu_gbe_clear_phy_interrupts(adapter))
 		printk(KERN_ERR PFX "ERROR clearing PHY interrupts\n");
 }
 
@@ -393,9 +389,9 @@
 					    0x5);
 	}
 
-	if (netxen_niu_gbe_enable_phy_interrupts(adapter, port))
+	if (netxen_niu_gbe_enable_phy_interrupts(adapter))
 		printk(KERN_ERR PFX "ERROR enabling PHY interrupts\n");
-	if (netxen_niu_gbe_clear_phy_interrupts(adapter, port))
+	if (netxen_niu_gbe_clear_phy_interrupts(adapter))
 		printk(KERN_ERR PFX "ERROR clearing PHY interrupts\n");
 }
 
@@ -404,11 +400,11 @@
 	int result = 0;
 	__u32 status;
 	if (adapter->disable_phy_interrupts)
-		adapter->disable_phy_interrupts(adapter, port);
+		adapter->disable_phy_interrupts(adapter);
 	mdelay(2);
 
 	if (0 ==
-	    netxen_niu_gbe_phy_read(adapter, port,
+	    netxen_niu_gbe_phy_read(adapter,
 				    NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS,
 				    &status)) {
 		if (netxen_get_phy_link(status)) {
@@ -439,13 +435,13 @@
 						    | NETXEN_GB_MAC_ENABLE_TX_RX
 						    |
 						    NETXEN_GB_MAC_PAUSED_FRMS);
-			if (netxen_niu_gbe_clear_phy_interrupts(adapter, port))
+			if (netxen_niu_gbe_clear_phy_interrupts(adapter))
 				printk(KERN_ERR PFX
 				       "ERROR clearing PHY interrupts\n");
-			if (netxen_niu_gbe_enable_phy_interrupts(adapter, port))
+			if (netxen_niu_gbe_enable_phy_interrupts(adapter))
 				printk(KERN_ERR PFX
 				       "ERROR enabling PHY interrupts\n");
-			if (netxen_niu_gbe_clear_phy_interrupts(adapter, port))
+			if (netxen_niu_gbe_clear_phy_interrupts(adapter))
 				printk(KERN_ERR PFX
 				       "ERROR clearing PHY interrupts\n");
 			result = -1;
@@ -458,26 +454,18 @@
 
 int netxen_niu_xg_init_port(struct netxen_adapter *adapter, int port)
 {
-	u32 reg = 0, ret = 0;
+	u32 reg;
+	u32 portnum = physical_port[adapter->portnum];
 
-	if (adapter->ahw.boardcfg.board_type == NETXEN_BRDTYPE_P2_SB31_10G_IMEZ) {
-		netxen_crb_writelit_adapter(adapter,
-					    NETXEN_NIU_XG1_CONFIG_0, 0x5);
-		/* XXX hack for Mez cards: both ports in promisc mode */
-		netxen_nic_hw_read_wx(adapter,
-				      NETXEN_NIU_XGE_CONFIG_1, &reg, 4);
-		reg = (reg | 0x2000UL);
-		netxen_crb_writelit_adapter(adapter,
-					    NETXEN_NIU_XGE_CONFIG_1, reg);
-		reg = 0;
-		netxen_nic_hw_read_wx(adapter,
-				      NETXEN_NIU_XG1_CONFIG_1, &reg, 4);
-		reg = (reg | 0x2000UL);
-		netxen_crb_writelit_adapter(adapter,
-					    NETXEN_NIU_XG1_CONFIG_1, reg);
-	}
+	netxen_crb_writelit_adapter(adapter,
+		NETXEN_NIU_XGE_CONFIG_0+(0x10000*portnum), 0x5);
+	netxen_nic_hw_read_wx(adapter,
+		NETXEN_NIU_XGE_CONFIG_1+(0x10000*portnum), &reg, 4);
+	reg = (reg & ~0x2000UL);
+	netxen_crb_writelit_adapter(adapter,
+		NETXEN_NIU_XGE_CONFIG_1+(0x10000*portnum), reg);
 
-	return ret;
+	return 0;
 }
 
 /* 
@@ -498,7 +486,7 @@
 	 * The read of the PHY INT status will clear the pending
 	 * interrupt status
 	 */
-	if (netxen_niu_gbe_phy_read(adapter, port,
+	if (netxen_niu_gbe_phy_read(adapter,
 				    NETXEN_NIU_GB_MII_MGMT_ADDR_INT_STATUS,
 				    &int_src) != 0)
 		result = -EINVAL;
@@ -535,7 +523,7 @@
 			printk(KERN_INFO PFX
 			       "speed_changed or link status changed");
 			if (netxen_niu_gbe_phy_read
-			    (adapter, port,
+			    (adapter,
 			     NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS,
 			     &status) == 0) {
 				if (netxen_get_phy_speed(status) == 2) {
@@ -581,10 +569,11 @@
  * Note that the passed-in value must already be in network byte order.
  */
 int netxen_niu_macaddr_get(struct netxen_adapter *adapter,
-			   int phy, netxen_ethernet_macaddr_t * addr)
+			   netxen_ethernet_macaddr_t * addr)
 {
 	u32 stationhigh;
 	u32 stationlow;
+	int phy = physical_port[adapter->portnum];
 	u8 val[8];
 
 	if (addr == NULL)
@@ -610,13 +599,12 @@
  * Set the station MAC address.
  * Note that the passed-in value must already be in network byte order.
  */
-int netxen_niu_macaddr_set(struct netxen_port *port,
+int netxen_niu_macaddr_set(struct netxen_adapter *adapter,
 			   netxen_ethernet_macaddr_t addr)
 {
 	u8 temp[4];
 	u32 val;
-	struct netxen_adapter *adapter = port->adapter;
-	int phy = port->portnum;
+	int phy = physical_port[adapter->portnum];
 	unsigned char mac_addr[6];
 	int i;
 
@@ -634,7 +622,7 @@
 		    (adapter, NETXEN_NIU_GB_STATION_ADDR_0(phy), &val, 4))
 			return -2;
 
-		netxen_niu_macaddr_get(adapter, phy,
+		netxen_niu_macaddr_get(adapter, 
 				       (netxen_ethernet_macaddr_t *) mac_addr);
 		if (memcmp(mac_addr, addr, 6) == 0)
 			break;
@@ -642,7 +630,7 @@
 
 	if (i == 10) {
 		printk(KERN_ERR "%s: cannot set Mac addr for %s\n",
-		       netxen_nic_driver_name, port->netdev->name);
+		       netxen_nic_driver_name, adapter->netdev->name);
 		printk(KERN_ERR "MAC address set: "
 		       "%02x:%02x:%02x:%02x:%02x:%02x.\n",
 		       addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
@@ -735,13 +723,13 @@
 }
 
 /* Disable a GbE interface */
-int netxen_niu_disable_gbe_port(struct netxen_adapter *adapter, int port)
+int netxen_niu_disable_gbe_port(struct netxen_adapter *adapter)
 {
 	__u32 mac_cfg0;
+	u32 port = physical_port[adapter->portnum];
 
 	if ((port < 0) || (port > NETXEN_NIU_MAX_GBE_PORTS))
 		return -EINVAL;
-
 	mac_cfg0 = 0;
 	netxen_gb_soft_reset(mac_cfg0);
 	if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(port),
@@ -751,13 +739,13 @@
 }
 
 /* Disable an XG interface */
-int netxen_niu_disable_xg_port(struct netxen_adapter *adapter, int port)
+int netxen_niu_disable_xg_port(struct netxen_adapter *adapter)
 {
 	__u32 mac_cfg;
+	u32 port = physical_port[adapter->portnum];
 
 	if (port != 0)
 		return -EINVAL;
-
 	mac_cfg = 0;
 	netxen_xg_soft_reset(mac_cfg);
 	if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XGE_CONFIG_0,
@@ -767,10 +755,11 @@
 }
 
 /* Set promiscuous mode for a GbE interface */
-int netxen_niu_set_promiscuous_mode(struct netxen_adapter *adapter, int port,
+int netxen_niu_set_promiscuous_mode(struct netxen_adapter *adapter, 
 				    netxen_niu_prom_mode_t mode)
 {
 	__u32 reg;
+	u32 port = physical_port[adapter->portnum];
 
 	if ((port < 0) || (port > NETXEN_NIU_MAX_GBE_PORTS))
 		return -EINVAL;
@@ -824,25 +813,50 @@
  * Set the MAC address for an XG port
  * Note that the passed-in value must already be in network byte order.
  */
-int netxen_niu_xg_macaddr_set(struct netxen_port *port,
+int netxen_niu_xg_macaddr_set(struct netxen_adapter *adapter,
 			      netxen_ethernet_macaddr_t addr)
 {
+	int phy = physical_port[adapter->portnum];
 	u8 temp[4];
 	u32 val;
-	struct netxen_adapter *adapter = port->adapter;
+
+	if ((phy < 0) || (phy > NETXEN_NIU_MAX_XG_PORTS))
+		return -EIO;
 
 	temp[0] = temp[1] = 0;
-	memcpy(temp + 2, addr, 2);
-	val = le32_to_cpu(*(__le32 *)temp);
-	if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XGE_STATION_ADDR_0_1,
-				   &val, 4))
+	switch (phy) {
+	case 0:
+	    memcpy(temp + 2, addr, 2);
+	    val = le32_to_cpu(*(__le32 *)temp);
+	    if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XGE_STATION_ADDR_0_1,
+				&val, 4))
 		return -EIO;
 
-	memcpy(&temp, ((u8 *) addr) + 2, sizeof(__le32));
-	val = le32_to_cpu(*(__le32 *)temp);
-	if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XGE_STATION_ADDR_0_HI,
-				   &val, 4))
+	    memcpy(&temp, ((u8 *) addr) + 2, sizeof(__le32));
+	    val = le32_to_cpu(*(__le32 *)temp);
+	    if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XGE_STATION_ADDR_0_HI,
+				&val, 4))
 		return -EIO;
+	    break;
+
+	case 1:
+	    memcpy(temp + 2, addr, 2);
+	    val = le32_to_cpu(*(__le32 *)temp);
+	    if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XG1_STATION_ADDR_0_1,
+				&val, 4))
+		return -EIO;
+
+	    memcpy(&temp, ((u8 *) addr) + 2, sizeof(__le32));
+	    val = le32_to_cpu(*(__le32 *)temp);
+	    if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XG1_STATION_ADDR_0_HI,
+				&val, 4))
+		return -EIO;
+	    break;
+
+	default:
+	    printk(KERN_ERR "Unknown port %d\n", phy);
+	    break;
+	}
 
 	return 0;
 }
@@ -851,9 +865,10 @@
  * Return the current station MAC address.
  * Note that the passed-in value must already be in network byte order.
  */
-int netxen_niu_xg_macaddr_get(struct netxen_adapter *adapter, int phy,
+int netxen_niu_xg_macaddr_get(struct netxen_adapter *adapter,
 			      netxen_ethernet_macaddr_t * addr)
 {
+	int phy = physical_port[adapter->portnum];
 	u32 stationhigh;
 	u32 stationlow;
 	u8 val[8];
@@ -878,21 +893,24 @@
 }
 
 int netxen_niu_xg_set_promiscuous_mode(struct netxen_adapter *adapter,
-				       int port, netxen_niu_prom_mode_t mode)
+				       netxen_niu_prom_mode_t mode)
 {
 	__u32 reg;
+	u32 port = physical_port[adapter->portnum];
 
-	if ((port < 0) || (port > NETXEN_NIU_MAX_GBE_PORTS))
+	if ((port < 0) || (port > NETXEN_NIU_MAX_XG_PORTS))
 		return -EINVAL;
 
-	if (netxen_nic_hw_read_wx(adapter, NETXEN_NIU_XGE_CONFIG_1, &reg, 4))
-		return -EIO;
+	if (netxen_nic_hw_read_wx(adapter,
+		NETXEN_NIU_XGE_CONFIG_1 + (0x10000 * port), &reg, 4))
+			return -EIO;
 	if (mode == NETXEN_NIU_PROMISC_MODE)
 		reg = (reg | 0x2000UL);
 	else
 		reg = (reg & ~0x2000UL);
 
-	netxen_crb_writelit_adapter(adapter, NETXEN_NIU_XGE_CONFIG_1, reg);
+	netxen_crb_writelit_adapter(adapter,
+		NETXEN_NIU_XGE_CONFIG_1 + (0x10000 * port), reg);
 
 	return 0;
 }
diff --git a/drivers/net/netxen/netxen_nic_phan_reg.h b/drivers/net/netxen/netxen_nic_phan_reg.h
index 0c7c943..9457fc7 100644
--- a/drivers/net/netxen/netxen_nic_phan_reg.h
+++ b/drivers/net/netxen/netxen_nic_phan_reg.h
@@ -100,8 +100,21 @@
 
 #define CRB_CMD_PRODUCER_OFFSET_1   NETXEN_NIC_REG(0x1ac)
 #define CRB_CMD_CONSUMER_OFFSET_1   NETXEN_NIC_REG(0x1b0)
+#define CRB_CMD_PRODUCER_OFFSET_2   NETXEN_NIC_REG(0x1b8)
+#define CRB_CMD_CONSUMER_OFFSET_2   NETXEN_NIC_REG(0x1bc)
+
+// 1c0 to 1cc used for signature reg
+#define CRB_CMD_PRODUCER_OFFSET_3   NETXEN_NIC_REG(0x1d0)
+#define CRB_CMD_CONSUMER_OFFSET_3   NETXEN_NIC_REG(0x1d4)
 #define CRB_TEMP_STATE              NETXEN_NIC_REG(0x1b4)
 
+#define CRB_V2P_0		    NETXEN_NIC_REG(0x290)
+#define CRB_V2P_1		    NETXEN_NIC_REG(0x294)
+#define CRB_V2P_2		    NETXEN_NIC_REG(0x298)
+#define CRB_V2P_3		    NETXEN_NIC_REG(0x29c)
+#define CRB_V2P(port)		    (CRB_V2P_0+((port)*4))
+#define CRB_DRIVER_VERSION	    NETXEN_NIC_REG(0x2a0)
+
 /* used for ethtool tests */
 #define CRB_SCRATCHPAD_TEST	    NETXEN_NIC_REG(0x280)
 
@@ -139,128 +152,13 @@
 };
 
 #if defined(DEFINE_GLOBAL_RECV_CRB)
-struct netxen_recv_crb recv_crb_registers[] = {
-	/*
-	 * Instance 0.
-	 */
-	{
-	 /* rcv_desc_crb: */
-	 {
-	  {
-	   /* crb_rcv_producer_offset: */
-	   NETXEN_NIC_REG(0x100),
-	   /* crb_rcv_consumer_offset: */
-	   NETXEN_NIC_REG(0x104),
-	   /* crb_gloablrcv_ring: */
-	   NETXEN_NIC_REG(0x108),
-	   /* crb_rcv_ring_size */
-	   NETXEN_NIC_REG(0x10c),
-
-	   },
-	  /* Jumbo frames */
-	  {
-	   /* crb_rcv_producer_offset: */
-	   NETXEN_NIC_REG(0x110),
-	   /* crb_rcv_consumer_offset: */
-	   NETXEN_NIC_REG(0x114),
-	   /* crb_gloablrcv_ring: */
-	   NETXEN_NIC_REG(0x118),
-	   /* crb_rcv_ring_size */
-	   NETXEN_NIC_REG(0x11c),
-	   },
-	  /* LRO */
-	  {
-	   /* crb_rcv_producer_offset: */
-	   NETXEN_NIC_REG(0x120),
-	   /* crb_rcv_consumer_offset: */
-	   NETXEN_NIC_REG(0x124),
-	   /* crb_gloablrcv_ring: */
-	   NETXEN_NIC_REG(0x128),
-	   /* crb_rcv_ring_size */
-	   NETXEN_NIC_REG(0x12c),
-	   }
-	  },
-	 /* crb_rcvstatus_ring: */
-	 NETXEN_NIC_REG(0x130),
-	 /* crb_rcv_status_producer: */
-	 NETXEN_NIC_REG(0x134),
-	 /* crb_rcv_status_consumer: */
-	 NETXEN_NIC_REG(0x138),
-	 /* crb_rcvpeg_state: */
-	 NETXEN_NIC_REG(0x13c),
-	 /* crb_status_ring_size */
-	 NETXEN_NIC_REG(0x140),
-
-	 },
-	/*
-	 * Instance 1,
-	 */
-	{
-	 /* rcv_desc_crb: */
-	 {
-	  {
-	   /* crb_rcv_producer_offset: */
-	   NETXEN_NIC_REG(0x144),
-	   /* crb_rcv_consumer_offset: */
-	   NETXEN_NIC_REG(0x148),
-	   /* crb_globalrcv_ring: */
-	   NETXEN_NIC_REG(0x14c),
-	   /* crb_rcv_ring_size */
-	   NETXEN_NIC_REG(0x150),
-
-	   },
-	  /* Jumbo frames */
-	  {
-	   /* crb_rcv_producer_offset: */
-	   NETXEN_NIC_REG(0x154),
-	   /* crb_rcv_consumer_offset: */
-	   NETXEN_NIC_REG(0x158),
-	   /* crb_globalrcv_ring: */
-	   NETXEN_NIC_REG(0x15c),
-	   /* crb_rcv_ring_size */
-	   NETXEN_NIC_REG(0x160),
-	   },
-	  /* LRO */
-	  {
-	   /* crb_rcv_producer_offset: */
-	   NETXEN_NIC_REG(0x164),
-	   /* crb_rcv_consumer_offset: */
-	   NETXEN_NIC_REG(0x168),
-	   /* crb_globalrcv_ring: */
-	   NETXEN_NIC_REG(0x16c),
-	   /* crb_rcv_ring_size */
-	   NETXEN_NIC_REG(0x170),
-	   }
-
-	  },
-	 /* crb_rcvstatus_ring: */
-	 NETXEN_NIC_REG(0x174),
-	 /* crb_rcv_status_producer: */
-	 NETXEN_NIC_REG(0x178),
-	 /* crb_rcv_status_consumer: */
-	 NETXEN_NIC_REG(0x17c),
-	 /* crb_rcvpeg_state: */
-	 NETXEN_NIC_REG(0x180),
-	 /* crb_status_ring_size */
-	 NETXEN_NIC_REG(0x184),
-
-	 },
-};
-
-u64 ctx_addr_sig_regs[][3] = {
-	{NETXEN_NIC_REG(0x188), NETXEN_NIC_REG(0x18c), NETXEN_NIC_REG(0x1c0)},
-	{NETXEN_NIC_REG(0x190), NETXEN_NIC_REG(0x194), NETXEN_NIC_REG(0x1c4)},
-	{NETXEN_NIC_REG(0x198), NETXEN_NIC_REG(0x19c), NETXEN_NIC_REG(0x1c8)},
-	{NETXEN_NIC_REG(0x1a0), NETXEN_NIC_REG(0x1a4), NETXEN_NIC_REG(0x1cc)}
-};
-
 #else
 extern struct netxen_recv_crb recv_crb_registers[];
 extern u64 ctx_addr_sig_regs[][3];
-#define CRB_CTX_ADDR_REG_LO            (ctx_addr_sig_regs[0][0])
-#define CRB_CTX_ADDR_REG_HI            (ctx_addr_sig_regs[0][2])
-#define CRB_CTX_SIGNATURE_REG       (ctx_addr_sig_regs[0][1])
 #endif				/* DEFINE_GLOBAL_RECEIVE_CRB */
+#define CRB_CTX_ADDR_REG_LO(FUNC_ID)		(ctx_addr_sig_regs[FUNC_ID][0])
+#define CRB_CTX_ADDR_REG_HI(FUNC_ID)		(ctx_addr_sig_regs[FUNC_ID][2])
+#define CRB_CTX_SIGNATURE_REG(FUNC_ID)		(ctx_addr_sig_regs[FUNC_ID][1])
 
 /*
  * Temperature control.
diff --git a/drivers/net/ni5010.c b/drivers/net/ni5010.c
index 8be0d03..3d5b423 100644
--- a/drivers/net/ni5010.c
+++ b/drivers/net/ni5010.c
@@ -562,7 +562,6 @@
 		return;
 	}
 
-	skb->dev = dev;
 	skb_reserve(skb, 2);
 
 	/* Read packet into buffer */
diff --git a/drivers/net/ni52.c b/drivers/net/ni52.c
index a6f4b24..8dbd6d1 100644
--- a/drivers/net/ni52.c
+++ b/drivers/net/ni52.c
@@ -934,7 +934,6 @@
 					skb = (struct sk_buff *) dev_alloc_skb(totlen+2);
 					if(skb != NULL)
 					{
-						skb->dev = dev;
 						skb_reserve(skb,2);
 						skb_put(skb,totlen);
 						eth_copy_and_sum(skb,(char *) p->base+(unsigned long) rbd->buffer,totlen,0);
@@ -1183,7 +1182,7 @@
 	else
 #endif
 	{
-		memcpy((char *)p->xmit_cbuffs[p->xmit_count],(char *)(skb->data),skb->len);
+		skb_copy_from_linear_data(skb, (char *) p->xmit_cbuffs[p->xmit_count], skb->len);
 		len = skb->len;
 		if (len < ETH_ZLEN) {
 			len = ETH_ZLEN;
diff --git a/drivers/net/ni65.c b/drivers/net/ni65.c
index 1578f4d..3818edf 100644
--- a/drivers/net/ni65.c
+++ b/drivers/net/ni65.c
@@ -610,7 +610,6 @@
 			printk(KERN_WARNING "%s: unable to allocate %s memory.\n",dev->name,what);
 			return NULL;
 		}
-		skb->dev = dev;
 		skb_reserve(skb,2+16);
 		skb_put(skb,R_BUF_SIZE);	 /* grab the whole space .. (not necessary) */
 		ptr = skb->data;
@@ -1094,7 +1093,6 @@
 			if(skb)
 			{
 				skb_reserve(skb,2);
-	skb->dev = dev;
 #ifdef RCV_VIA_SKB
 				if( (unsigned long) (skb->data + R_BUF_SIZE) > 0x1000000) {
 					skb_put(skb,len);
@@ -1178,8 +1176,9 @@
 		if( (unsigned long) (skb->data + skb->len) > 0x1000000) {
 #endif
 
-			memcpy((char *) p->tmdbounce[p->tmdbouncenum] ,(char *)skb->data,
-							 (skb->len > T_BUF_SIZE) ? T_BUF_SIZE : skb->len);
+			skb_copy_from_linear_data(skb, p->tmdbounce[p->tmdbouncenum],
+				      skb->len > T_BUF_SIZE ? T_BUF_SIZE :
+							      skb->len);
 			if (len > skb->len)
 				memset((char *)p->tmdbounce[p->tmdbouncenum]+skb->len, 0, len-skb->len);
 			dev_kfree_skb (skb);
diff --git a/drivers/net/ns83820.c b/drivers/net/ns83820.c
index 9ec6e9e..6a32338 100644
--- a/drivers/net/ns83820.c
+++ b/drivers/net/ns83820.c
@@ -607,7 +607,6 @@
 		res &= 0xf;
 		skb_reserve(skb, res);
 
-		skb->dev = ndev;
 		if (gfp != GFP_ATOMIC)
 			spin_lock_irqsave(&dev->rx_info.lock, flags);
 		res = ns83820_add_rx_skb(dev, skb);
@@ -1157,9 +1156,9 @@
 	extsts = 0;
 	if (skb->ip_summed == CHECKSUM_PARTIAL) {
 		extsts |= EXTSTS_IPPKT;
-		if (IPPROTO_TCP == skb->nh.iph->protocol)
+		if (IPPROTO_TCP == ip_hdr(skb)->protocol)
 			extsts |= EXTSTS_TCPPKT;
-		else if (IPPROTO_UDP == skb->nh.iph->protocol)
+		else if (IPPROTO_UDP == ip_hdr(skb)->protocol)
 			extsts |= EXTSTS_UDPPKT;
 	}
 
diff --git a/drivers/net/pasemi_mac.c b/drivers/net/pasemi_mac.c
index d670ac7..76fe9dd 100644
--- a/drivers/net/pasemi_mac.c
+++ b/drivers/net/pasemi_mac.c
@@ -334,8 +334,6 @@
 			break;
 		}
 
-		skb->dev = dev;
-
 		dma = pci_map_single(mac->dma_pdev, skb->data, skb->len,
 				     PCI_DMA_FROMDEVICE);
 
@@ -731,16 +729,18 @@
 	dflags = XCT_MACTX_O | XCT_MACTX_ST | XCT_MACTX_SS | XCT_MACTX_CRC_PAD;
 
 	if (skb->ip_summed == CHECKSUM_PARTIAL) {
-		switch (skb->nh.iph->protocol) {
+		const unsigned char *nh = skb_network_header(skb);
+
+		switch (ip_hdr(skb)->protocol) {
 		case IPPROTO_TCP:
 			dflags |= XCT_MACTX_CSUM_TCP;
-			dflags |= XCT_MACTX_IPH((skb->h.raw - skb->nh.raw) >> 2);
-			dflags |= XCT_MACTX_IPO(skb->nh.raw - skb->data);
+			dflags |= XCT_MACTX_IPH(skb_network_header_len(skb) >> 2);
+			dflags |= XCT_MACTX_IPO(nh - skb->data);
 			break;
 		case IPPROTO_UDP:
 			dflags |= XCT_MACTX_CSUM_UDP;
-			dflags |= XCT_MACTX_IPH((skb->h.raw - skb->nh.raw) >> 2);
-			dflags |= XCT_MACTX_IPO(skb->nh.raw - skb->data);
+			dflags |= XCT_MACTX_IPH(skb_network_header_len(skb) >> 2);
+			dflags |= XCT_MACTX_IPO(nh - skb->data);
 			break;
 		}
 	}
diff --git a/drivers/net/pci-skeleton.c b/drivers/net/pci-skeleton.c
index 6ca4e4f..df8998b 100644
--- a/drivers/net/pci-skeleton.c
+++ b/drivers/net/pci-skeleton.c
@@ -1344,7 +1344,7 @@
 
 	tp->tx_info[entry].skb = skb;
 	/* tp->tx_info[entry].mapping = 0; */
-	memcpy (tp->tx_buf[entry], skb->data, skb->len);
+	skb_copy_from_linear_data(skb, tp->tx_buf[entry], skb->len);
 
 	/* Note: the chip doesn't have auto-pad! */
 	NETDRV_W32 (TxStatus0 + (entry * sizeof(u32)),
@@ -1565,7 +1565,6 @@
 
 		skb = dev_alloc_skb (pkt_size + 2);
 		if (skb) {
-			skb->dev = dev;
 			skb_reserve (skb, 2);	/* 16 byte align the IP fields. */
 
 			eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0);
diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c
index c7bd9c1..2b395ee 100644
--- a/drivers/net/pcmcia/3c574_cs.c
+++ b/drivers/net/pcmcia/3c574_cs.c
@@ -1056,7 +1056,6 @@
 			DEBUG(3, "  Receiving packet size %d status %4.4x.\n",
 				  pkt_len, rx_status);
 			if (skb != NULL) {
-				skb->dev = dev;
 				skb_reserve(skb, 2);
 				insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
 						((pkt_len+3)>>2));
diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c
index 461e827..143ae2f 100644
--- a/drivers/net/pcmcia/3c589_cs.c
+++ b/drivers/net/pcmcia/3c589_cs.c
@@ -883,7 +883,6 @@
 	    DEBUG(3, "    Receiving packet size %d status %4.4x.\n",
 		  pkt_len, rx_status);
 	    if (skb != NULL) {
-		skb->dev = dev;
 		skb_reserve(skb, 2);
 		insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
 			(pkt_len+3)>>2);
diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c
index 6139048..808fae1 100644
--- a/drivers/net/pcmcia/axnet_cs.c
+++ b/drivers/net/pcmcia/axnet_cs.c
@@ -1136,7 +1136,7 @@
 		ei_block_output(dev, length, skb->data, output_page);
 	else {
 		memset(packet, 0, ETH_ZLEN);
-		memcpy(packet, skb->data, skb->len);
+		skb_copy_from_linear_data(skb, packet, skb->len);
 		ei_block_output(dev, length, packet, output_page);
 	}
 	
@@ -1496,7 +1496,6 @@
 			else
 			{
 				skb_reserve(skb,2);	/* IP headers on 16 byte boundaries */
-				skb->dev = dev;
 				skb_put(skb, pkt_len);	/* Make room */
 				ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame));
 				skb->protocol=eth_type_trans(skb,dev);
diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c
index 0d7de61..3f93d49 100644
--- a/drivers/net/pcmcia/fmvj18x_cs.c
+++ b/drivers/net/pcmcia/fmvj18x_cs.c
@@ -999,7 +999,6 @@
 		lp->stats.rx_dropped++;
 		break;
 	    }
-	    skb->dev = dev;
 
 	    skb_reserve(skb, 2);
 	    insw(ioaddr + DATAPORT, skb_put(skb, pkt_len),
diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c
index 3b70774..73da611 100644
--- a/drivers/net/pcmcia/nmclan_cs.c
+++ b/drivers/net/pcmcia/nmclan_cs.c
@@ -1182,12 +1182,10 @@
       skb = dev_alloc_skb(pkt_len+2);
 
       if (skb != NULL) {
-	skb->dev = dev;
-
 	skb_reserve(skb, 2);
 	insw(ioaddr + AM2150_RCV, skb_put(skb, pkt_len), pkt_len>>1);
 	if (pkt_len & 1)
-	    *(skb->tail-1) = inb(ioaddr + AM2150_RCV);
+	    *(skb_tail_pointer(skb) - 1) = inb(ioaddr + AM2150_RCV);
 	skb->protocol = eth_type_trans(skb, dev);
 	
 	netif_rx(skb); /* Send the packet to the upper (protocol) layers. */
diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c
index 2561f76..7912dbd 100644
--- a/drivers/net/pcmcia/smc91c92_cs.c
+++ b/drivers/net/pcmcia/smc91c92_cs.c
@@ -1669,7 +1669,6 @@
 	     (packet_length+1)>>1);
 	skb->protocol = eth_type_trans(skb, dev);
 	
-	skb->dev = dev;
 	netif_rx(skb);
 	dev->last_rx = jiffies;
 	smc->stats.rx_packets++;
diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c
index 5879e7c..809ec440 100644
--- a/drivers/net/pcmcia/xirc2ps_cs.c
+++ b/drivers/net/pcmcia/xirc2ps_cs.c
@@ -1226,7 +1226,6 @@
 			    (pktlen+1)>>1);
 		}
 		skb->protocol = eth_type_trans(skb, dev);
-		skb->dev = dev;
 		netif_rx(skb);
 		dev->last_rx = jiffies;
 		lp->stats.rx_packets++;
diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c
index 4d94ba7..9c171a7 100644
--- a/drivers/net/pcnet32.c
+++ b/drivers/net/pcnet32.c
@@ -253,12 +253,12 @@
  * so the structure should be allocated using pci_alloc_consistent().
  */
 struct pcnet32_private {
-	struct pcnet32_init_block init_block;
+	struct pcnet32_init_block *init_block;
 	/* The Tx and Rx ring entries must be aligned on 16-byte boundaries in 32bit mode. */
 	struct pcnet32_rx_head	*rx_ring;
 	struct pcnet32_tx_head	*tx_ring;
-	dma_addr_t		dma_addr;/* DMA address of beginning of this
-				   object, returned by pci_alloc_consistent */
+	dma_addr_t		init_dma_addr;/* DMA address of beginning of the init block,
+				   returned by pci_alloc_consistent */
 	struct pci_dev		*pci_dev;
 	const char		*name;
 	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
@@ -653,7 +653,7 @@
 
 static void pcnet32_purge_rx_ring(struct net_device *dev)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	int i;
 
 	/* free all allocated skbuffs */
@@ -681,7 +681,7 @@
 
 static int pcnet32_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	unsigned long flags;
 	int r = -EOPNOTSUPP;
 
@@ -696,7 +696,7 @@
 
 static int pcnet32_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	unsigned long flags;
 	int r = -EOPNOTSUPP;
 
@@ -711,7 +711,7 @@
 static void pcnet32_get_drvinfo(struct net_device *dev,
 				struct ethtool_drvinfo *info)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 
 	strcpy(info->driver, DRV_NAME);
 	strcpy(info->version, DRV_VERSION);
@@ -723,7 +723,7 @@
 
 static u32 pcnet32_get_link(struct net_device *dev)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	unsigned long flags;
 	int r;
 
@@ -743,19 +743,19 @@
 
 static u32 pcnet32_get_msglevel(struct net_device *dev)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	return lp->msg_enable;
 }
 
 static void pcnet32_set_msglevel(struct net_device *dev, u32 value)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	lp->msg_enable = value;
 }
 
 static int pcnet32_nway_reset(struct net_device *dev)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	unsigned long flags;
 	int r = -EOPNOTSUPP;
 
@@ -770,7 +770,7 @@
 static void pcnet32_get_ringparam(struct net_device *dev,
 				  struct ethtool_ringparam *ering)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 
 	ering->tx_max_pending = TX_MAX_RING_SIZE;
 	ering->tx_pending = lp->tx_ring_size;
@@ -781,7 +781,7 @@
 static int pcnet32_set_ringparam(struct net_device *dev,
 				 struct ethtool_ringparam *ering)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	unsigned long flags;
 	unsigned int size;
 	ulong ioaddr = dev->base_addr;
@@ -847,7 +847,7 @@
 static void pcnet32_ethtool_test(struct net_device *dev,
 				 struct ethtool_test *test, u64 * data)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	int rc;
 
 	if (test->flags == ETH_TEST_FL_OFFLINE) {
@@ -868,7 +868,7 @@
 
 static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	struct pcnet32_access *a = &lp->a;	/* access to registers */
 	ulong ioaddr = dev->base_addr;	/* card base I/O address */
 	struct sk_buff *skb;	/* sk buff */
@@ -1047,7 +1047,7 @@
 
 static void pcnet32_led_blink_callback(struct net_device *dev)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	struct pcnet32_access *a = &lp->a;
 	ulong ioaddr = dev->base_addr;
 	unsigned long flags;
@@ -1064,7 +1064,7 @@
 
 static int pcnet32_phys_id(struct net_device *dev, u32 data)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	struct pcnet32_access *a = &lp->a;
 	ulong ioaddr = dev->base_addr;
 	unsigned long flags;
@@ -1109,7 +1109,7 @@
 		int can_sleep)
 {
 	int csr5;
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	struct pcnet32_access *a = &lp->a;
 	ulong ioaddr = dev->base_addr;
 	int ticks;
@@ -1206,7 +1206,6 @@
 					 PCI_DMA_FROMDEVICE);
 			skb_put(skb, pkt_len);
 			lp->rx_skbuff[entry] = newskb;
-			newskb->dev = dev;
 			lp->rx_dma_addr[entry] =
 					    pci_map_single(lp->pci_dev,
 							   newskb->data,
@@ -1258,7 +1257,7 @@
 
 static int pcnet32_rx(struct net_device *dev, int quota)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	int entry = lp->cur_rx & lp->rx_mod_mask;
 	struct pcnet32_rx_head *rxp = &lp->rx_ring[entry];
 	int npackets = 0;
@@ -1283,7 +1282,7 @@
 
 static int pcnet32_tx(struct net_device *dev)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	unsigned int dirty_tx = lp->dirty_tx;
 	int delta;
 	int must_restart = 0;
@@ -1382,7 +1381,7 @@
 #ifdef CONFIG_PCNET32_NAPI
 static int pcnet32_poll(struct net_device *dev, int *budget)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	int quota = min(dev->quota, *budget);
 	unsigned long ioaddr = dev->base_addr;
 	unsigned long flags;
@@ -1429,7 +1428,7 @@
 #define PCNET32_MAX_PHYS	32
 static int pcnet32_get_regs_len(struct net_device *dev)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	int j = lp->phycount * PCNET32_REGS_PER_PHY;
 
 	return ((PCNET32_NUM_REGS + j) * sizeof(u16));
@@ -1440,7 +1439,7 @@
 {
 	int i, csr0;
 	u16 *buff = ptr;
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	struct pcnet32_access *a = &lp->a;
 	ulong ioaddr = dev->base_addr;
 	unsigned long flags;
@@ -1593,7 +1592,6 @@
 pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
 {
 	struct pcnet32_private *lp;
-	dma_addr_t lp_dma_addr;
 	int i, media;
 	int fdx, mii, fset, dxsuflo;
 	int chip_version;
@@ -1715,7 +1713,7 @@
 		dxsuflo = 1;
 	}
 
-	dev = alloc_etherdev(0);
+	dev = alloc_etherdev(sizeof(*lp));
 	if (!dev) {
 		if (pcnet32_debug & NETIF_MSG_PROBE)
 			printk(KERN_ERR PFX "Memory allocation failed.\n");
@@ -1806,25 +1804,22 @@
 	}
 
 	dev->base_addr = ioaddr;
+	lp = netdev_priv(dev);
 	/* pci_alloc_consistent returns page-aligned memory, so we do not have to check the alignment */
-	if ((lp =
-	     pci_alloc_consistent(pdev, sizeof(*lp), &lp_dma_addr)) == NULL) {
+	if ((lp->init_block =
+	     pci_alloc_consistent(pdev, sizeof(*lp->init_block), &lp->init_dma_addr)) == NULL) {
 		if (pcnet32_debug & NETIF_MSG_PROBE)
 			printk(KERN_ERR PFX
 			       "Consistent memory allocation failed.\n");
 		ret = -ENOMEM;
 		goto err_free_netdev;
 	}
-
-	memset(lp, 0, sizeof(*lp));
-	lp->dma_addr = lp_dma_addr;
 	lp->pci_dev = pdev;
 
 	spin_lock_init(&lp->lock);
 
 	SET_MODULE_OWNER(dev);
 	SET_NETDEV_DEV(dev, &pdev->dev);
-	dev->priv = lp;
 	lp->name = chipname;
 	lp->shared_irq = shared;
 	lp->tx_ring_size = TX_RING_SIZE;	/* default tx ring size */
@@ -1871,23 +1866,21 @@
 	    && dev->dev_addr[2] == 0x75)
 		lp->options = PCNET32_PORT_FD | PCNET32_PORT_GPSI;
 
-	lp->init_block.mode = le16_to_cpu(0x0003);	/* Disable Rx and Tx. */
-	lp->init_block.tlen_rlen =
+	lp->init_block->mode = le16_to_cpu(0x0003);	/* Disable Rx and Tx. */
+	lp->init_block->tlen_rlen =
 	    le16_to_cpu(lp->tx_len_bits | lp->rx_len_bits);
 	for (i = 0; i < 6; i++)
-		lp->init_block.phys_addr[i] = dev->dev_addr[i];
-	lp->init_block.filter[0] = 0x00000000;
-	lp->init_block.filter[1] = 0x00000000;
-	lp->init_block.rx_ring = (u32) le32_to_cpu(lp->rx_ring_dma_addr);
-	lp->init_block.tx_ring = (u32) le32_to_cpu(lp->tx_ring_dma_addr);
+		lp->init_block->phys_addr[i] = dev->dev_addr[i];
+	lp->init_block->filter[0] = 0x00000000;
+	lp->init_block->filter[1] = 0x00000000;
+	lp->init_block->rx_ring = (u32) le32_to_cpu(lp->rx_ring_dma_addr);
+	lp->init_block->tx_ring = (u32) le32_to_cpu(lp->tx_ring_dma_addr);
 
 	/* switch pcnet32 to 32bit mode */
 	a->write_bcr(ioaddr, 20, 2);
 
-	a->write_csr(ioaddr, 1, (lp->dma_addr + offsetof(struct pcnet32_private,
-							 init_block)) & 0xffff);
-	a->write_csr(ioaddr, 2, (lp->dma_addr + offsetof(struct pcnet32_private,
-							 init_block)) >> 16);
+	a->write_csr(ioaddr, 1, (lp->init_dma_addr & 0xffff));
+	a->write_csr(ioaddr, 2, (lp->init_dma_addr >> 16));
 
 	if (pdev) {		/* use the IRQ provided by PCI */
 		dev->irq = pdev->irq;
@@ -1993,7 +1986,8 @@
       err_free_ring:
 	pcnet32_free_ring(dev);
       err_free_consistent:
-	pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr);
+	pci_free_consistent(lp->pci_dev, sizeof(*lp->init_block), 
+			    lp->init_block, lp->init_dma_addr);
       err_free_netdev:
 	free_netdev(dev);
       err_release_region:
@@ -2004,7 +1998,7 @@
 /* if any allocation fails, caller must also call pcnet32_free_ring */
 static int pcnet32_alloc_ring(struct net_device *dev, char *name)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 
 	lp->tx_ring = pci_alloc_consistent(lp->pci_dev,
 					   sizeof(struct pcnet32_tx_head) *
@@ -2071,7 +2065,7 @@
 
 static void pcnet32_free_ring(struct net_device *dev)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 
 	kfree(lp->tx_skbuff);
 	lp->tx_skbuff = NULL;
@@ -2104,7 +2098,7 @@
 
 static int pcnet32_open(struct net_device *dev)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	unsigned long ioaddr = dev->base_addr;
 	u16 val;
 	int i;
@@ -2135,8 +2129,7 @@
 		       "%s: pcnet32_open() irq %d tx/rx rings %#x/%#x init %#x.\n",
 		       dev->name, dev->irq, (u32) (lp->tx_ring_dma_addr),
 		       (u32) (lp->rx_ring_dma_addr),
-		       (u32) (lp->dma_addr +
-			      offsetof(struct pcnet32_private, init_block)));
+		       (u32) (lp->init_dma_addr));
 
 	/* set/reset autoselect bit */
 	val = lp->a.read_bcr(ioaddr, 2) & ~2;
@@ -2275,7 +2268,7 @@
 	}
 #endif
 
-	lp->init_block.mode =
+	lp->init_block->mode =
 	    le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7);
 	pcnet32_load_multicast(dev);
 
@@ -2285,12 +2278,8 @@
 	}
 
 	/* Re-initialize the PCNET32, and start it when done. */
-	lp->a.write_csr(ioaddr, 1, (lp->dma_addr +
-				    offsetof(struct pcnet32_private,
-					     init_block)) & 0xffff);
-	lp->a.write_csr(ioaddr, 2,
-			(lp->dma_addr +
-			 offsetof(struct pcnet32_private, init_block)) >> 16);
+	lp->a.write_csr(ioaddr, 1, (lp->init_dma_addr & 0xffff));
+	lp->a.write_csr(ioaddr, 2, (lp->init_dma_addr >> 16));
 
 	lp->a.write_csr(ioaddr, CSR4, 0x0915);	/* auto tx pad */
 	lp->a.write_csr(ioaddr, CSR0, CSR0_INIT);
@@ -2317,8 +2306,7 @@
 		printk(KERN_DEBUG
 		       "%s: pcnet32 open after %d ticks, init block %#x csr0 %4.4x.\n",
 		       dev->name, i,
-		       (u32) (lp->dma_addr +
-			      offsetof(struct pcnet32_private, init_block)),
+		       (u32) (lp->init_dma_addr),
 		       lp->a.read_csr(ioaddr, CSR0));
 
 	spin_unlock_irqrestore(&lp->lock, flags);
@@ -2356,7 +2344,7 @@
 
 static void pcnet32_purge_tx_ring(struct net_device *dev)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	int i;
 
 	for (i = 0; i < lp->tx_ring_size; i++) {
@@ -2376,7 +2364,7 @@
 /* Initialize the PCNET32 Rx and Tx rings. */
 static int pcnet32_init_ring(struct net_device *dev)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	int i;
 
 	lp->tx_full = 0;
@@ -2418,12 +2406,12 @@
 		lp->tx_dma_addr[i] = 0;
 	}
 
-	lp->init_block.tlen_rlen =
+	lp->init_block->tlen_rlen =
 	    le16_to_cpu(lp->tx_len_bits | lp->rx_len_bits);
 	for (i = 0; i < 6; i++)
-		lp->init_block.phys_addr[i] = dev->dev_addr[i];
-	lp->init_block.rx_ring = (u32) le32_to_cpu(lp->rx_ring_dma_addr);
-	lp->init_block.tx_ring = (u32) le32_to_cpu(lp->tx_ring_dma_addr);
+		lp->init_block->phys_addr[i] = dev->dev_addr[i];
+	lp->init_block->rx_ring = (u32) le32_to_cpu(lp->rx_ring_dma_addr);
+	lp->init_block->tx_ring = (u32) le32_to_cpu(lp->tx_ring_dma_addr);
 	wmb();			/* Make sure all changes are visible */
 	return 0;
 }
@@ -2434,7 +2422,7 @@
  */
 static void pcnet32_restart(struct net_device *dev, unsigned int csr0_bits)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	unsigned long ioaddr = dev->base_addr;
 	int i;
 
@@ -2464,7 +2452,7 @@
 
 static void pcnet32_tx_timeout(struct net_device *dev)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	unsigned long ioaddr = dev->base_addr, flags;
 
 	spin_lock_irqsave(&lp->lock, flags);
@@ -2505,7 +2493,7 @@
 
 static int pcnet32_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	unsigned long ioaddr = dev->base_addr;
 	u16 status;
 	int entry;
@@ -2570,7 +2558,7 @@
 	int boguscnt = max_interrupt_work;
 
 	ioaddr = dev->base_addr;
-	lp = dev->priv;
+	lp = netdev_priv(dev);
 
 	spin_lock(&lp->lock);
 
@@ -2652,7 +2640,7 @@
 static int pcnet32_close(struct net_device *dev)
 {
 	unsigned long ioaddr = dev->base_addr;
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	unsigned long flags;
 
 	del_timer_sync(&lp->watchdog_timer);
@@ -2693,7 +2681,7 @@
 
 static struct net_device_stats *pcnet32_get_stats(struct net_device *dev)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	unsigned long ioaddr = dev->base_addr;
 	unsigned long flags;
 
@@ -2707,8 +2695,8 @@
 /* taken from the sunlance driver, which it took from the depca driver */
 static void pcnet32_load_multicast(struct net_device *dev)
 {
-	struct pcnet32_private *lp = dev->priv;
-	volatile struct pcnet32_init_block *ib = &lp->init_block;
+	struct pcnet32_private *lp = netdev_priv(dev);
+	volatile struct pcnet32_init_block *ib = lp->init_block;
 	volatile u16 *mcast_table = (u16 *) & ib->filter;
 	struct dev_mc_list *dmi = dev->mc_list;
 	unsigned long ioaddr = dev->base_addr;
@@ -2757,7 +2745,7 @@
 static void pcnet32_set_multicast_list(struct net_device *dev)
 {
 	unsigned long ioaddr = dev->base_addr, flags;
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	int csr15, suspended;
 
 	spin_lock_irqsave(&lp->lock, flags);
@@ -2768,12 +2756,12 @@
 		if (netif_msg_hw(lp))
 			printk(KERN_INFO "%s: Promiscuous mode enabled.\n",
 			       dev->name);
-		lp->init_block.mode =
+		lp->init_block->mode =
 		    le16_to_cpu(0x8000 | (lp->options & PCNET32_PORT_PORTSEL) <<
 				7);
 		lp->a.write_csr(ioaddr, CSR15, csr15 | 0x8000);
 	} else {
-		lp->init_block.mode =
+		lp->init_block->mode =
 		    le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7);
 		lp->a.write_csr(ioaddr, CSR15, csr15 & 0x7fff);
 		pcnet32_load_multicast(dev);
@@ -2796,7 +2784,7 @@
 /* This routine assumes that the lp->lock is held */
 static int mdio_read(struct net_device *dev, int phy_id, int reg_num)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	unsigned long ioaddr = dev->base_addr;
 	u16 val_out;
 
@@ -2812,7 +2800,7 @@
 /* This routine assumes that the lp->lock is held */
 static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	unsigned long ioaddr = dev->base_addr;
 
 	if (!lp->mii)
@@ -2824,7 +2812,7 @@
 
 static int pcnet32_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	int rc;
 	unsigned long flags;
 
@@ -2842,7 +2830,7 @@
 
 static int pcnet32_check_otherphy(struct net_device *dev)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	struct mii_if_info mii = lp->mii_if;
 	u16 bmcr;
 	int i;
@@ -2889,7 +2877,7 @@
 
 static void pcnet32_check_media(struct net_device *dev, int verbose)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	int curr_link;
 	int prev_link = netif_carrier_ok(dev) ? 1 : 0;
 	u32 bcr9;
@@ -2945,7 +2933,7 @@
 
 static void pcnet32_watchdog(struct net_device *dev)
 {
-	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_private *lp = netdev_priv(dev);
 	unsigned long flags;
 
 	/* Print the link status if it has changed */
@@ -2961,12 +2949,13 @@
 	struct net_device *dev = pci_get_drvdata(pdev);
 
 	if (dev) {
-		struct pcnet32_private *lp = dev->priv;
+		struct pcnet32_private *lp = netdev_priv(dev);
 
 		unregister_netdev(dev);
 		pcnet32_free_ring(dev);
 		release_region(dev->base_addr, PCNET32_TOTAL_SIZE);
-		pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr);
+		pci_free_consistent(lp->pci_dev, sizeof(*lp->init_block), 
+				    lp->init_block, lp->init_dma_addr);
 		free_netdev(dev);
 		pci_disable_device(pdev);
 		pci_set_drvdata(pdev, NULL);
@@ -3041,12 +3030,13 @@
 	struct net_device *next_dev;
 
 	while (pcnet32_dev) {
-		struct pcnet32_private *lp = pcnet32_dev->priv;
+		struct pcnet32_private *lp = netdev_priv(pcnet32_dev);
 		next_dev = lp->next;
 		unregister_netdev(pcnet32_dev);
 		pcnet32_free_ring(pcnet32_dev);
 		release_region(pcnet32_dev->base_addr, PCNET32_TOTAL_SIZE);
-		pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr);
+		pci_free_consistent(lp->pci_dev, sizeof(*lp->init_block), 
+				    lp->init_block, lp->init_dma_addr);
 		free_netdev(pcnet32_dev);
 		pcnet32_dev = next_dev;
 	}
diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c
index 66da91b..68c99b4c 100644
--- a/drivers/net/phy/fixed.c
+++ b/drivers/net/phy/fixed.c
@@ -276,21 +276,15 @@
 	   artificially, we are binding the driver here by hand;
 	   it will be the same for all the fixed phys anyway.
 	 */
-	down_write(&phydev->dev.bus->subsys.rwsem);
-
 	phydev->dev.driver = &fixed_mdio_driver.driver;
 
 	err = phydev->dev.driver->probe(&phydev->dev);
 	if(err < 0) {
 		printk(KERN_ERR "Phy %s: problems with fixed driver\n",phydev->dev.bus_id);
-		up_write(&phydev->dev.bus->subsys.rwsem);
 		goto probe_fail;
 	}
 
 	err = device_bind_driver(&phydev->dev);
-
-	up_write(&phydev->dev.bus->subsys.rwsem);
-
 	if (err)
 		goto probe_fail;
 
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index b31ce27..fc4aee9 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -35,10 +35,14 @@
 #include <asm/irq.h>
 #include <asm/uaccess.h>
 
-/* mdiobus_register 
+/**
+ * mdiobus_register - bring up all the PHYs on a given bus and attach them to bus
+ * @bus: target mii_bus
  *
- * description: Called by a bus driver to bring up all the PHYs
- *   on a given bus, and attach them to the bus
+ * Description: Called by a bus driver to bring up all the PHYs
+ *   on a given bus, and attach them to the bus.
+ *
+ * Returns 0 on success or < 0 on error.
  */
 int mdiobus_register(struct mii_bus *bus)
 {
@@ -114,10 +118,13 @@
 }
 EXPORT_SYMBOL(mdiobus_unregister);
 
-/* mdio_bus_match
+/**
+ * mdio_bus_match - determine if given PHY driver supports the given PHY device
+ * @dev: target PHY device
+ * @drv: given PHY driver
  *
- * description: Given a PHY device, and a PHY driver, return 1 if
- *   the driver supports the device.  Otherwise, return 0
+ * Description: Given a PHY device, and a PHY driver, return 1 if
+ *   the driver supports the device.  Otherwise, return 0.
  */
 static int mdio_bus_match(struct device *dev, struct device_driver *drv)
 {
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index c94a1fb..eed433d 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -39,7 +39,9 @@
 #include <asm/irq.h>
 #include <asm/uaccess.h>
 
-/* Convenience function to print out the current phy status
+/**
+ * phy_print_status - Convenience function to print out the current phy status
+ * @phydev: the phy_device struct
  */
 void phy_print_status(struct phy_device *phydev)
 {
@@ -55,10 +57,15 @@
 EXPORT_SYMBOL(phy_print_status);
 
 
-/* Convenience functions for reading/writing a given PHY
- * register. They MUST NOT be called from interrupt context,
+/**
+ * phy_read - Convenience function for reading a given PHY register
+ * @phydev: the phy_device struct
+ * @regnum: register number to read
+ *
+ * NOTE: MUST NOT be called from interrupt context,
  * because the bus read/write functions may wait for an interrupt
- * to conclude the operation. */
+ * to conclude the operation.
+ */
 int phy_read(struct phy_device *phydev, u16 regnum)
 {
 	int retval;
@@ -72,6 +79,16 @@
 }
 EXPORT_SYMBOL(phy_read);
 
+/**
+ * phy_write - Convenience function for writing a given PHY register
+ * @phydev: the phy_device struct
+ * @regnum: register number to write
+ * @val: value to write to @regnum
+ *
+ * NOTE: MUST NOT be called from interrupt context,
+ * because the bus read/write functions may wait for an interrupt
+ * to conclude the operation.
+ */
 int phy_write(struct phy_device *phydev, u16 regnum, u16 val)
 {
 	int err;
@@ -85,7 +102,15 @@
 }
 EXPORT_SYMBOL(phy_write);
 
-
+/**
+ * phy_clear_interrupt - Ack the phy device's interrupt
+ * @phydev: the phy_device struct
+ *
+ * If the @phydev driver has an ack_interrupt function, call it to
+ * ack and clear the phy device's interrupt.
+ *
+ * Returns 0 on success on < 0 on error.
+ */
 int phy_clear_interrupt(struct phy_device *phydev)
 {
 	int err = 0;
@@ -96,7 +121,13 @@
 	return err;
 }
 
-
+/**
+ * phy_config_interrupt - configure the PHY device for the requested interrupts
+ * @phydev: the phy_device struct
+ * @interrupts: interrupt flags to configure for this @phydev
+ *
+ * Returns 0 on success on < 0 on error.
+ */
 int phy_config_interrupt(struct phy_device *phydev, u32 interrupts)
 {
 	int err = 0;
@@ -109,9 +140,11 @@
 }
 
 
-/* phy_aneg_done
+/**
+ * phy_aneg_done - return auto-negotiation status
+ * @phydev: target phy_device struct
  *
- * description: Reads the status register and returns 0 either if
+ * Description: Reads the status register and returns 0 either if
  *   auto-negotiation is incomplete, or if there was an error.
  *   Returns BMSR_ANEGCOMPLETE if auto-negotiation is done.
  */
@@ -173,9 +206,12 @@
 
 #define MAX_NUM_SETTINGS (sizeof(settings)/sizeof(struct phy_setting))
 
-/* phy_find_setting
+/**
+ * phy_find_setting - find a PHY settings array entry that matches speed & duplex
+ * @speed: speed to match
+ * @duplex: duplex to match
  *
- * description: Searches the settings array for the setting which
+ * Description: Searches the settings array for the setting which
  *   matches the desired speed and duplex, and returns the index
  *   of that setting.  Returns the index of the last setting if
  *   none of the others match.
@@ -192,11 +228,12 @@
 	return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1;
 }
 
-/* phy_find_valid
- * idx: The first index in settings[] to search
- * features: A mask of the valid settings
+/**
+ * phy_find_valid - find a PHY setting that matches the requested features mask
+ * @idx: The first index in settings[] to search
+ * @features: A mask of the valid settings
  *
- * description: Returns the index of the first valid setting less
+ * Description: Returns the index of the first valid setting less
  *   than or equal to the one pointed to by idx, as determined by
  *   the mask in features.  Returns the index of the last setting
  *   if nothing else matches.
@@ -209,11 +246,13 @@
 	return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1;
 }
 
-/* phy_sanitize_settings
+/**
+ * phy_sanitize_settings - make sure the PHY is set to supported speed and duplex
+ * @phydev: the target phy_device struct
  *
- * description: Make sure the PHY is set to supported speeds and
+ * Description: Make sure the PHY is set to supported speeds and
  *   duplexes.  Drop down by one in this order:  1000/FULL,
- *   1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF
+ *   1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF.
  */
 void phy_sanitize_settings(struct phy_device *phydev)
 {
@@ -232,16 +271,17 @@
 }
 EXPORT_SYMBOL(phy_sanitize_settings);
 
-/* phy_ethtool_sset:
- * A generic ethtool sset function.  Handles all the details
+/**
+ * phy_ethtool_sset - generic ethtool sset function, handles all the details
+ * @phydev: target phy_device struct
+ * @cmd: ethtool_cmd
  *
  * A few notes about parameter checking:
  * - We don't set port or transceiver, so we don't care what they
  *   were set to.
  * - phy_start_aneg() will make sure forced settings are sane, and
  *   choose the next best ones from the ones selected, so we don't
- *   care if ethtool tries to give us bad values
- *
+ *   care if ethtool tries to give us bad values.
  */
 int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd)
 {
@@ -304,9 +344,15 @@
 }
 EXPORT_SYMBOL(phy_ethtool_gset);
 
-/* Note that this function is currently incompatible with the
+/**
+ * phy_mii_ioctl - generic PHY MII ioctl interface
+ * @phydev: the phy_device struct
+ * @mii_data: MII ioctl data
+ * @cmd: ioctl cmd to execute
+ *
+ * Note that this function is currently incompatible with the
  * PHYCONTROL layer.  It changes registers without regard to
- * current state.  Use at own risk
+ * current state.  Use at own risk.
  */
 int phy_mii_ioctl(struct phy_device *phydev,
 		struct mii_ioctl_data *mii_data, int cmd)
@@ -336,6 +382,12 @@
 					phydev->duplex = DUPLEX_FULL;
 				else
 					phydev->duplex = DUPLEX_HALF;
+				if ((!phydev->autoneg) &&
+						(val & BMCR_SPEED1000))
+					phydev->speed = SPEED_1000;
+				else if ((!phydev->autoneg) &&
+						(val & BMCR_SPEED100))
+					phydev->speed = SPEED_100;
 				break;
 			case MII_ADVERTISE:
 				phydev->advertising = val;
@@ -358,13 +410,14 @@
 	return 0;
 }
 
-/* phy_start_aneg
+/**
+ * phy_start_aneg - start auto-negotiation for this PHY device
+ * @phydev: the phy_device struct
  *
- * description: Sanitizes the settings (if we're not
- *   autonegotiating them), and then calls the driver's
- *   config_aneg function.  If the PHYCONTROL Layer is operating,
- *   we change the state to reflect the beginning of
- *   Auto-negotiation or forcing.
+ * Description: Sanitizes the settings (if we're not autonegotiating
+ *   them), and then calls the driver's config_aneg function.
+ *   If the PHYCONTROL Layer is operating, we change the state to
+ *   reflect the beginning of Auto-negotiation or forcing.
  */
 int phy_start_aneg(struct phy_device *phydev)
 {
@@ -400,15 +453,19 @@
 static void phy_change(struct work_struct *work);
 static void phy_timer(unsigned long data);
 
-/* phy_start_machine:
+/**
+ * phy_start_machine - start PHY state machine tracking
+ * @phydev: the phy_device struct
+ * @handler: callback function for state change notifications
  *
- * description: The PHY infrastructure can run a state machine
+ * Description: The PHY infrastructure can run a state machine
  *   which tracks whether the PHY is starting up, negotiating,
  *   etc.  This function starts the timer which tracks the state
- *   of the PHY.  If you want to be notified when the state
- *   changes, pass in the callback, otherwise, pass NULL.  If you
+ *   of the PHY.  If you want to be notified when the state changes,
+ *   pass in the callback @handler, otherwise, pass NULL.  If you
  *   want to maintain your own state machine, do not call this
- *   function. */
+ *   function.
+ */
 void phy_start_machine(struct phy_device *phydev,
 		void (*handler)(struct net_device *))
 {
@@ -420,9 +477,11 @@
 	mod_timer(&phydev->phy_timer, jiffies + HZ);
 }
 
-/* phy_stop_machine
+/**
+ * phy_stop_machine - stop the PHY state machine tracking
+ * @phydev: target phy_device struct
  *
- * description: Stops the state machine timer, sets the state to UP
+ * Description: Stops the state machine timer, sets the state to UP
  *   (unless it wasn't up yet). This function must be called BEFORE
  *   phy_detach.
  */
@@ -438,12 +497,14 @@
 	phydev->adjust_state = NULL;
 }
 
-/* phy_force_reduction
+/**
+ * phy_force_reduction - reduce PHY speed/duplex settings by one step
+ * @phydev: target phy_device struct
  *
- * description: Reduces the speed/duplex settings by
- *   one notch.  The order is so:
- *   1000/FULL, 1000/HALF, 100/FULL, 100/HALF,
- *   10/FULL, 10/HALF.  The function bottoms out at 10/HALF.
+ * Description: Reduces the speed/duplex settings by one notch,
+ *   in this order--
+ *   1000/FULL, 1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF.
+ *   The function bottoms out at 10/HALF.
  */
 static void phy_force_reduction(struct phy_device *phydev)
 {
@@ -464,7 +525,9 @@
 }
 
 
-/* phy_error:
+/**
+ * phy_error - enter HALTED state for this PHY device
+ * @phydev: target phy_device struct
  *
  * Moves the PHY to the HALTED state in response to a read
  * or write error, and tells the controller the link is down.
@@ -478,9 +541,12 @@
 	spin_unlock(&phydev->lock);
 }
 
-/* phy_interrupt
+/**
+ * phy_interrupt - PHY interrupt handler
+ * @irq: interrupt line
+ * @phy_dat: phy_device pointer
  *
- * description: When a PHY interrupt occurs, the handler disables
+ * Description: When a PHY interrupt occurs, the handler disables
  * interrupts, and schedules a work task to clear the interrupt.
  */
 static irqreturn_t phy_interrupt(int irq, void *phy_dat)
@@ -501,7 +567,10 @@
 	return IRQ_HANDLED;
 }
 
-/* Enable the interrupts from the PHY side */
+/**
+ * phy_enable_interrupts - Enable the interrupts from the PHY side
+ * @phydev: target phy_device struct
+ */
 int phy_enable_interrupts(struct phy_device *phydev)
 {
 	int err;
@@ -517,7 +586,10 @@
 }
 EXPORT_SYMBOL(phy_enable_interrupts);
 
-/* Disable the PHY interrupts from the PHY side */
+/**
+ * phy_disable_interrupts - Disable the PHY interrupts from the PHY side
+ * @phydev: target phy_device struct
+ */
 int phy_disable_interrupts(struct phy_device *phydev)
 {
 	int err;
@@ -543,13 +615,15 @@
 }
 EXPORT_SYMBOL(phy_disable_interrupts);
 
-/* phy_start_interrupts
+/**
+ * phy_start_interrupts - request and enable interrupts for a PHY device
+ * @phydev: target phy_device struct
  *
- * description: Request the interrupt for the given PHY.  If
- *   this fails, then we set irq to PHY_POLL.
+ * Description: Request the interrupt for the given PHY.
+ *   If this fails, then we set irq to PHY_POLL.
  *   Otherwise, we enable the interrupts in the PHY.
- *   Returns 0 on success.
  *   This should only be called with a valid IRQ number.
+ *   Returns 0 on success or < 0 on error.
  */
 int phy_start_interrupts(struct phy_device *phydev)
 {
@@ -574,6 +648,10 @@
 }
 EXPORT_SYMBOL(phy_start_interrupts);
 
+/**
+ * phy_stop_interrupts - disable interrupts from a PHY device
+ * @phydev: target phy_device struct
+ */
 int phy_stop_interrupts(struct phy_device *phydev)
 {
 	int err;
@@ -596,7 +674,10 @@
 EXPORT_SYMBOL(phy_stop_interrupts);
 
 
-/* Scheduled by the phy_interrupt/timer to handle PHY changes */
+/**
+ * phy_change - Scheduled by the phy_interrupt/timer to handle PHY changes
+ * @work: work_struct that describes the work to be done
+ */
 static void phy_change(struct work_struct *work)
 {
 	int err;
@@ -630,7 +711,10 @@
 	phy_error(phydev);
 }
 
-/* Bring down the PHY link, and stop checking the status. */
+/**
+ * phy_stop - Bring down the PHY link, and stop checking the status
+ * @phydev: target phy_device struct
+ */
 void phy_stop(struct phy_device *phydev)
 {
 	spin_lock(&phydev->lock);
@@ -659,9 +743,11 @@
 }
 
 
-/* phy_start
+/**
+ * phy_start - start or restart a PHY device
+ * @phydev: target phy_device struct
  *
- * description: Indicates the attached device's readiness to
+ * Description: Indicates the attached device's readiness to
  *   handle PHY-related work.  Used during startup to start the
  *   PHY, and after a call to phy_stop() to resume operation.
  *   Also used to indicate the MDIO bus has cleared an error
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 7d5b6d1..a8b74cd 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -74,11 +74,13 @@
 }
 EXPORT_SYMBOL(phy_device_create);
 
-/* get_phy_device
+/**
+ * get_phy_device - reads the specified PHY device and returns its @phy_device struct
+ * @bus: the target MII bus
+ * @addr: PHY address on the MII bus
  *
- * description: Reads the ID registers of the PHY at addr on the
- *   bus, then allocates and returns the phy_device to
- *   represent it.
+ * Description: Reads the ID registers of the PHY at @addr on the
+ *   @bus, then allocates and returns the phy_device to represent it.
  */
 struct phy_device * get_phy_device(struct mii_bus *bus, int addr)
 {
@@ -112,23 +114,33 @@
 	return dev;
 }
 
-/* phy_prepare_link:
+/**
+ * phy_prepare_link - prepares the PHY layer to monitor link status
+ * @phydev: target phy_device struct
+ * @handler: callback function for link status change notifications
  *
- * description: Tells the PHY infrastructure to handle the
+ * Description: Tells the PHY infrastructure to handle the
  *   gory details on monitoring link status (whether through
  *   polling or an interrupt), and to call back to the
  *   connected device driver when the link status changes.
  *   If you want to monitor your own link state, don't call
- *   this function */
+ *   this function.
+ */
 void phy_prepare_link(struct phy_device *phydev,
 		void (*handler)(struct net_device *))
 {
 	phydev->adjust_link = handler;
 }
 
-/* phy_connect:
+/**
+ * phy_connect - connect an ethernet device to a PHY device
+ * @dev: the network device to connect
+ * @phy_id: the PHY device to connect
+ * @handler: callback function for state change notifications
+ * @flags: PHY device's dev_flags
+ * @interface: PHY device's interface
  *
- * description: Convenience function for connecting ethernet
+ * Description: Convenience function for connecting ethernet
  *   devices to PHY devices.  The default behavior is for
  *   the PHY infrastructure to handle everything, and only notify
  *   the connected driver when the link status changes.  If you
@@ -158,6 +170,10 @@
 }
 EXPORT_SYMBOL(phy_connect);
 
+/**
+ * phy_disconnect - disable interrupts, stop state machine, and detach a PHY device
+ * @phydev: target phy_device struct
+ */
 void phy_disconnect(struct phy_device *phydev)
 {
 	if (phydev->irq > 0)
@@ -171,21 +187,25 @@
 }
 EXPORT_SYMBOL(phy_disconnect);
 
-/* phy_attach:
- *
- *   description: Called by drivers to attach to a particular PHY
- *     device. The phy_device is found, and properly hooked up
- *     to the phy_driver.  If no driver is attached, then the
- *     genphy_driver is used.  The phy_device is given a ptr to
- *     the attaching device, and given a callback for link status
- *     change.  The phy_device is returned to the attaching
- *     driver.
- */
 static int phy_compare_id(struct device *dev, void *data)
 {
 	return strcmp((char *)data, dev->bus_id) ? 0 : 1;
 }
 
+/**
+ * phy_attach - attach a network device to a particular PHY device
+ * @dev: network device to attach
+ * @phy_id: PHY device to attach
+ * @flags: PHY device's dev_flags
+ * @interface: PHY device's interface
+ *
+ * Description: Called by drivers to attach to a particular PHY
+ *     device. The phy_device is found, and properly hooked up
+ *     to the phy_driver.  If no driver is attached, then the
+ *     genphy_driver is used.  The phy_device is given a ptr to
+ *     the attaching device, and given a callback for link status
+ *     change.  The phy_device is returned to the attaching driver.
+ */
 struct phy_device *phy_attach(struct net_device *dev,
 		const char *phy_id, u32 flags, phy_interface_t interface)
 {
@@ -208,16 +228,12 @@
 	 * exist, and we should use the genphy driver. */
 	if (NULL == d->driver) {
 		int err;
-		down_write(&d->bus->subsys.rwsem);
 		d->driver = &genphy_driver.driver;
 
 		err = d->driver->probe(d);
-
 		if (err >= 0)
 			err = device_bind_driver(d);
 
-		up_write(&d->bus->subsys.rwsem);
-
 		if (err)
 			return ERR_PTR(err);
 	}
@@ -250,6 +266,10 @@
 }
 EXPORT_SYMBOL(phy_attach);
 
+/**
+ * phy_detach - detach a PHY device from its network device
+ * @phydev: target phy_device struct
+ */
 void phy_detach(struct phy_device *phydev)
 {
 	phydev->attached_dev = NULL;
@@ -258,22 +278,21 @@
 	 * was using the generic driver), we unbind the device
 	 * from the generic driver so that there's a chance a
 	 * real driver could be loaded */
-	if (phydev->dev.driver == &genphy_driver.driver) {
-		down_write(&phydev->dev.bus->subsys.rwsem);
+	if (phydev->dev.driver == &genphy_driver.driver)
 		device_release_driver(&phydev->dev);
-		up_write(&phydev->dev.bus->subsys.rwsem);
-	}
 }
 EXPORT_SYMBOL(phy_detach);
 
 
 /* Generic PHY support and helper functions */
 
-/* genphy_config_advert
+/**
+ * genphy_config_advert - sanitize and advertise auto-negotation parameters
+ * @phydev: target phy_device struct
  *
- * description: Writes MII_ADVERTISE with the appropriate values,
+ * Description: Writes MII_ADVERTISE with the appropriate values,
  *   after sanitizing the values to make sure we only advertise
- *   what is supported
+ *   what is supported.
  */
 int genphy_config_advert(struct phy_device *phydev)
 {
@@ -335,11 +354,14 @@
 }
 EXPORT_SYMBOL(genphy_config_advert);
 
-/* genphy_setup_forced
+/**
+ * genphy_setup_forced - configures/forces speed/duplex from @phydev
+ * @phydev: target phy_device struct
  *
- * description: Configures MII_BMCR to force speed/duplex
+ * Description: Configures MII_BMCR to force speed/duplex
  *   to the values in phydev. Assumes that the values are valid.
- *   Please see phy_sanitize_settings() */
+ *   Please see phy_sanitize_settings().
+ */
 int genphy_setup_forced(struct phy_device *phydev)
 {
 	int ctl = BMCR_RESET;
@@ -368,7 +390,10 @@
 }
 
 
-/* Enable and Restart Autonegotiation */
+/**
+ * genphy_restart_aneg - Enable and Restart Autonegotiation
+ * @phydev: target phy_device struct
+ */
 int genphy_restart_aneg(struct phy_device *phydev)
 {
 	int ctl;
@@ -389,11 +414,13 @@
 }
 
 
-/* genphy_config_aneg
+/**
+ * genphy_config_aneg - restart auto-negotiation or write BMCR
+ * @phydev: target phy_device struct
  *
- * description: If auto-negotiation is enabled, we configure the
+ * Description: If auto-negotiation is enabled, we configure the
  *   advertising, and then restart auto-negotiation.  If it is not
- *   enabled, then we write the BMCR
+ *   enabled, then we write the BMCR.
  */
 int genphy_config_aneg(struct phy_device *phydev)
 {
@@ -413,11 +440,13 @@
 }
 EXPORT_SYMBOL(genphy_config_aneg);
 
-/* genphy_update_link
+/**
+ * genphy_update_link - update link status in @phydev
+ * @phydev: target phy_device struct
  *
- * description: Update the value in phydev->link to reflect the
+ * Description: Update the value in phydev->link to reflect the
  *   current link value.  In order to do this, we need to read
- *   the status register twice, keeping the second value
+ *   the status register twice, keeping the second value.
  */
 int genphy_update_link(struct phy_device *phydev)
 {
@@ -444,9 +473,11 @@
 }
 EXPORT_SYMBOL(genphy_update_link);
 
-/* genphy_read_status
+/**
+ * genphy_read_status - check the link status and update current link state
+ * @phydev: target phy_device struct
  *
- * description: Check the link, then figure out the current state
+ * Description: Check the link, then figure out the current state
  *   by comparing what we advertise with what the link partner
  *   advertises.  Start by checking the gigabit possibilities,
  *   then move on to 10/100.
@@ -586,9 +617,11 @@
 }
 
 
-/* phy_probe
+/**
+ * phy_probe - probe and init a PHY device
+ * @dev: device to probe and init
  *
- * description: Take care of setting up the phy_device structure,
+ * Description: Take care of setting up the phy_device structure,
  *   set the state to READY (the driver's init function should
  *   set it to STARTING if needed).
  */
@@ -650,6 +683,10 @@
 	return 0;
 }
 
+/**
+ * phy_driver_register - register a phy_driver with the PHY layer
+ * @new_driver: new phy_driver to register
+ */
 int phy_driver_register(struct phy_driver *new_driver)
 {
 	int retval;
diff --git a/drivers/net/plip.c b/drivers/net/plip.c
index 6bb085f..8754cf3 100644
--- a/drivers/net/plip.c
+++ b/drivers/net/plip.c
@@ -546,7 +546,7 @@
 	struct ethhdr *eth;
 	unsigned char *rawp;
 
-	skb->mac.raw=skb->data;
+	skb_reset_mac_header(skb);
 	skb_pull(skb,dev->hard_header_len);
 	eth = eth_hdr(skb);
 
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c
index ef58e41..6d596ca 100644
--- a/drivers/net/ppp_generic.c
+++ b/drivers/net/ppp_generic.c
@@ -88,8 +88,6 @@
 #define PF_TO_PPP(pf)		PF_TO_X(pf, struct ppp)
 #define PF_TO_CHANNEL(pf)	PF_TO_X(pf, struct channel)
 
-#define ROUNDUP(n, x)		(((n) + (x) - 1) / (x))
-
 /*
  * Data structure describing one ppp unit.
  * A ppp unit corresponds to a ppp network interface device
@@ -1297,7 +1295,7 @@
 	 */
 	fragsize = len;
 	if (nfree > 1)
-		fragsize = ROUNDUP(fragsize, nfree);
+		fragsize = DIV_ROUND_UP(fragsize, nfree);
 	/* nbigger channels get fragsize bytes, the rest get fragsize-1,
 	   except if nbigger==0, then they all get fragsize. */
 	nbigger = len % nfree;
@@ -1685,7 +1683,7 @@
 			skb_pull_rcsum(skb, 2);
 			skb->dev = ppp->dev;
 			skb->protocol = htons(npindex_to_ethertype[npi]);
-			skb->mac.raw = skb->data;
+			skb_reset_mac_header(skb);
 			netif_rx(skb);
 			ppp->dev->last_rx = jiffies;
 		}
diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c
index b6f0e9a..5918fab 100644
--- a/drivers/net/ppp_synctty.c
+++ b/drivers/net/ppp_synctty.c
@@ -594,7 +594,8 @@
 				return NULL;
 			}
 			skb_reserve(npkt,2);
-			memcpy(skb_put(npkt,skb->len), skb->data, skb->len);
+			skb_copy_from_linear_data(skb,
+				      skb_put(npkt, skb->len), skb->len);
 			kfree_skb(skb);
 			skb = npkt;
 		}
diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c
index ebfa296..6f98834 100644
--- a/drivers/net/pppoe.c
+++ b/drivers/net/pppoe.c
@@ -207,7 +207,7 @@
 
 static inline struct pppox_sock *get_item_by_addr(struct sockaddr_pppox *sp)
 {
-	struct net_device *dev = NULL;
+	struct net_device *dev;
 	int ifindex;
 
 	dev = dev_get_by_name(sp->sa_addr.pppoe.dev);
@@ -218,20 +218,6 @@
 	return get_item(sp->sa_addr.pppoe.sid, sp->sa_addr.pppoe.remote, ifindex);
 }
 
-static inline int set_item(struct pppox_sock *po)
-{
-	int i;
-
-	if (!po)
-		return -EINVAL;
-
-	write_lock_bh(&pppoe_hash_lock);
-	i = __set_item(po);
-	write_unlock_bh(&pppoe_hash_lock);
-
-	return i;
-}
-
 static inline struct pppox_sock *delete_item(unsigned long sid, char *addr, int ifindex)
 {
 	struct pppox_sock *ret;
@@ -255,54 +241,53 @@
 static void pppoe_flush_dev(struct net_device *dev)
 {
 	int hash;
-
 	BUG_ON(dev == NULL);
 
-	read_lock_bh(&pppoe_hash_lock);
+	write_lock_bh(&pppoe_hash_lock);
 	for (hash = 0; hash < PPPOE_HASH_SIZE; hash++) {
 		struct pppox_sock *po = item_hash_table[hash];
 
 		while (po != NULL) {
-			if (po->pppoe_dev == dev) {
-				struct sock *sk = sk_pppox(po);
-
-				sock_hold(sk);
-				po->pppoe_dev = NULL;
-
-				/* We hold a reference to SK, now drop the
-				 * hash table lock so that we may attempt
-				 * to lock the socket (which can sleep).
-				 */
-				read_unlock_bh(&pppoe_hash_lock);
-
-				lock_sock(sk);
-
-				if (sk->sk_state &
-				    (PPPOX_CONNECTED | PPPOX_BOUND)) {
-					pppox_unbind_sock(sk);
-					dev_put(dev);
-					sk->sk_state = PPPOX_ZOMBIE;
-					sk->sk_state_change(sk);
-				}
-
-				release_sock(sk);
-
-				sock_put(sk);
-
-				read_lock_bh(&pppoe_hash_lock);
-
-				/* Now restart from the beginning of this
-				 * hash chain.  We always NULL out pppoe_dev
-				 * so we are guaranteed to make forward
-				 * progress.
-				 */
-				po = item_hash_table[hash];
+			struct sock *sk = sk_pppox(po);
+			if (po->pppoe_dev != dev) {
+				po = po->next;
 				continue;
 			}
-			po = po->next;
+			po->pppoe_dev = NULL;
+			dev_put(dev);
+
+
+			/* We always grab the socket lock, followed by the
+			 * pppoe_hash_lock, in that order.  Since we should
+			 * hold the sock lock while doing any unbinding,
+			 * we need to release the lock we're holding.
+			 * Hold a reference to the sock so it doesn't disappear
+			 * as we're jumping between locks.
+			 */
+
+			sock_hold(sk);
+
+			write_unlock_bh(&pppoe_hash_lock);
+			lock_sock(sk);
+
+			if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) {
+				pppox_unbind_sock(sk);
+				sk->sk_state = PPPOX_ZOMBIE;
+				sk->sk_state_change(sk);
+			}
+
+			release_sock(sk);
+			sock_put(sk);
+
+			/* Restart scan at the beginning of this hash chain.
+			 * While the lock was dropped the chain contents may
+			 * have changed.
+			 */
+			write_lock_bh(&pppoe_hash_lock);
+			po = item_hash_table[hash];
 		}
 	}
-	read_unlock_bh(&pppoe_hash_lock);
+	write_unlock_bh(&pppoe_hash_lock);
 }
 
 static int pppoe_device_event(struct notifier_block *this,
@@ -344,10 +329,10 @@
 static int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb)
 {
 	struct pppox_sock *po = pppox_sk(sk);
-	struct pppox_sock *relay_po = NULL;
+	struct pppox_sock *relay_po;
 
 	if (sk->sk_state & PPPOX_BOUND) {
-		struct pppoe_hdr *ph = (struct pppoe_hdr *) skb->nh.raw;
+		struct pppoe_hdr *ph = pppoe_hdr(skb);
 		int len = ntohs(ph->length);
 		skb_pull_rcsum(skb, sizeof(struct pppoe_hdr));
 		if (pskb_trim_rcsum(skb, len))
@@ -401,7 +386,7 @@
 	if (!(skb = skb_share_check(skb, GFP_ATOMIC)))
 		goto out;
 
-	ph = (struct pppoe_hdr *) skb->nh.raw;
+	ph = pppoe_hdr(skb);
 
 	po = get_item((unsigned long) ph->sid, eth_hdr(skb)->h_source, dev->ifindex);
 	if (po != NULL)
@@ -433,7 +418,7 @@
 	if (!(skb = skb_share_check(skb, GFP_ATOMIC)))
 		goto out;
 
-	ph = (struct pppoe_hdr *) skb->nh.raw;
+	ph = pppoe_hdr(skb);
 	if (ph->code != PADT_CODE)
 		goto abort;
 
@@ -514,36 +499,49 @@
 {
 	struct sock *sk = sock->sk;
 	struct pppox_sock *po;
-	int error = 0;
 
 	if (!sk)
 		return 0;
 
-	if (sock_flag(sk, SOCK_DEAD))
+	lock_sock(sk);
+	if (sock_flag(sk, SOCK_DEAD)){
+		release_sock(sk);
 		return -EBADF;
+	}
 
 	pppox_unbind_sock(sk);
 
 	/* Signal the death of the socket. */
 	sk->sk_state = PPPOX_DEAD;
 
+
+	/* Write lock on hash lock protects the entire "po" struct from
+	 * concurrent updates via pppoe_flush_dev. The "po" struct should
+	 * be considered part of the hash table contents, thus protected
+	 * by the hash table lock */
+	write_lock_bh(&pppoe_hash_lock);
+
 	po = pppox_sk(sk);
 	if (po->pppoe_pa.sid) {
-		delete_item(po->pppoe_pa.sid, po->pppoe_pa.remote, po->pppoe_ifindex);
+		__delete_item(po->pppoe_pa.sid,
+			      po->pppoe_pa.remote, po->pppoe_ifindex);
 	}
 
-	if (po->pppoe_dev)
+	if (po->pppoe_dev) {
 		dev_put(po->pppoe_dev);
+		po->pppoe_dev = NULL;
+	}
 
-	po->pppoe_dev = NULL;
+	write_unlock_bh(&pppoe_hash_lock);
 
 	sock_orphan(sk);
 	sock->sk = NULL;
 
 	skb_queue_purge(&sk->sk_receive_queue);
+	release_sock(sk);
 	sock_put(sk);
 
-	return error;
+	return 0;
 }
 
 
@@ -599,14 +597,18 @@
 		po->pppoe_dev = dev;
 		po->pppoe_ifindex = dev->ifindex;
 
-		if (!(dev->flags & IFF_UP))
+		write_lock_bh(&pppoe_hash_lock);
+		if (!(dev->flags & IFF_UP)){
+			write_unlock_bh(&pppoe_hash_lock);
 			goto err_put;
+		}
 
 		memcpy(&po->pppoe_pa,
 		       &sp->sa_addr.pppoe,
 		       sizeof(struct pppoe_addr));
 
-		error = set_item(po);
+		error = __set_item(po);
+		write_unlock_bh(&pppoe_hash_lock);
 		if (error < 0)
 			goto err_put;
 
@@ -762,10 +764,10 @@
 static int pppoe_sendmsg(struct kiocb *iocb, struct socket *sock,
 		  struct msghdr *m, size_t total_len)
 {
-	struct sk_buff *skb = NULL;
+	struct sk_buff *skb;
 	struct sock *sk = sock->sk;
 	struct pppox_sock *po = pppox_sk(sk);
-	int error = 0;
+	int error;
 	struct pppoe_hdr hdr;
 	struct pppoe_hdr *ph;
 	struct net_device *dev;
@@ -799,7 +801,7 @@
 
 	/* Reserve space for headers. */
 	skb_reserve(skb, dev->hard_header_len);
-	skb->nh.raw = skb->data;
+	skb_reset_network_header(skb);
 
 	skb->dev = dev;
 
@@ -869,7 +871,8 @@
 			goto abort;
 
 		skb_reserve(skb2, dev->hard_header_len + sizeof(struct pppoe_hdr));
-		memcpy(skb_put(skb2, skb->len), skb->data, skb->len);
+		skb_copy_from_linear_data(skb, skb_put(skb2, skb->len),
+					  skb->len);
 	} else {
 		/* Make a clone so as to not disturb the original skb,
 		 * give dev_queue_xmit something it can free.
@@ -884,7 +887,7 @@
 	memcpy(ph, &hdr, sizeof(struct pppoe_hdr));
 	skb2->protocol = __constant_htons(ETH_P_PPP_SES);
 
-	skb2->nh.raw = skb2->data;
+	skb_reset_network_header(skb2);
 
 	skb2->dev = dev;
 
@@ -929,10 +932,8 @@
 		  struct msghdr *m, size_t total_len, int flags)
 {
 	struct sock *sk = sock->sk;
-	struct sk_buff *skb = NULL;
+	struct sk_buff *skb;
 	int error = 0;
-	int len;
-	struct pppoe_hdr *ph = NULL;
 
 	if (sk->sk_state & PPPOX_BOUND) {
 		error = -EIO;
@@ -942,26 +943,21 @@
 	skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
 				flags & MSG_DONTWAIT, &error);
 
-	if (error < 0) {
+	if (error < 0)
 		goto end;
-	}
 
 	m->msg_namelen = 0;
 
 	if (skb) {
-		error = 0;
-		ph = (struct pppoe_hdr *) skb->nh.raw;
-		len = ntohs(ph->length);
+		struct pppoe_hdr *ph = pppoe_hdr(skb);
+		const int len = ntohs(ph->length);
 
 		error = memcpy_toiovec(m->msg_iov, (unsigned char *) &ph->tag[0], len);
-		if (error < 0)
-			goto do_skb_free;
-		error = len;
+		if (error == 0)
+			error = len;
 	}
 
-do_skb_free:
-	if (skb)
-		kfree_skb(skb);
+	kfree_skb(skb);
 end:
 	return error;
 }
@@ -991,7 +987,7 @@
 
 static __inline__ struct pppox_sock *pppoe_get_idx(loff_t pos)
 {
-	struct pppox_sock *po = NULL;
+	struct pppox_sock *po;
 	int i = 0;
 
 	for (; i < PPPOE_HASH_SIZE; i++) {
diff --git a/drivers/net/pppox.c b/drivers/net/pppox.c
index 9315046..f3e47d0 100644
--- a/drivers/net/pppox.c
+++ b/drivers/net/pppox.c
@@ -31,6 +31,7 @@
 #include <linux/ppp_defs.h>
 #include <linux/if_ppp.h>
 #include <linux/ppp_channel.h>
+#include <linux/kmod.h>
 
 #include <net/sock.h>
 
@@ -58,7 +59,7 @@
 {
 	/* Clear connection to ppp device, if attached. */
 
-	if (sk->sk_state & (PPPOX_BOUND | PPPOX_ZOMBIE)) {
+	if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED | PPPOX_ZOMBIE)) {
 		ppp_unregister_channel(&pppox_sk(sk)->chan);
 		sk->sk_state = PPPOX_DEAD;
 	}
@@ -114,6 +115,13 @@
 		goto out;
 
 	rc = -EPROTONOSUPPORT;
+#ifdef CONFIG_KMOD
+	if (!pppox_protos[protocol]) {
+		char buffer[32];
+		sprintf(buffer, "pppox-proto-%d", protocol);
+		request_module(buffer);
+	}
+#endif
 	if (!pppox_protos[protocol] ||
 	    !try_module_get(pppox_protos[protocol]->owner))
 		goto out;
diff --git a/drivers/net/qla3xxx.c b/drivers/net/qla3xxx.c
index a8246eb..d8766c0 100755
--- a/drivers/net/qla3xxx.c
+++ b/drivers/net/qla3xxx.c
@@ -39,7 +39,7 @@
 
 #define DRV_NAME  	"qla3xxx"
 #define DRV_STRING 	"QLogic ISP3XXX Network Driver"
-#define DRV_VERSION	"v2.03.00-k3"
+#define DRV_VERSION	"v2.03.00-k4"
 #define PFX		DRV_NAME " "
 
 static const char ql3xxx_driver_name[] = DRV_NAME;
@@ -72,6 +72,30 @@
 MODULE_DEVICE_TABLE(pci, ql3xxx_pci_tbl);
 
 /*
+ *  These are the known PHY's which are used
+ */
+typedef enum {
+   PHY_TYPE_UNKNOWN   = 0,
+   PHY_VITESSE_VSC8211,
+   PHY_AGERE_ET1011C,
+   MAX_PHY_DEV_TYPES
+} PHY_DEVICE_et;
+
+typedef struct {
+	PHY_DEVICE_et phyDevice; 
+	u32		phyIdOUI;
+	u16		phyIdModel;
+	char 		*name;
+} PHY_DEVICE_INFO_t;
+
+static const PHY_DEVICE_INFO_t PHY_DEVICES[] =
+	{{PHY_TYPE_UNKNOWN,    0x000000, 0x0, "PHY_TYPE_UNKNOWN"},
+	 {PHY_VITESSE_VSC8211, 0x0003f1, 0xb, "PHY_VITESSE_VSC8211"},
+	 {PHY_AGERE_ET1011C,   0x00a0bc, 0x1, "PHY_AGERE_ET1011C"},
+};
+
+
+/*
  * Caller must take hw_lock.
  */
 static int ql_sem_spinlock(struct ql3_adapter *qdev,
@@ -662,7 +686,7 @@
 }
 
 static int ql_mii_write_reg_ex(struct ql3_adapter *qdev,
-			       u16 regAddr, u16 value, u32 mac_index)
+			       u16 regAddr, u16 value, u32 phyAddr)
 {
 	struct ql3xxx_port_registers __iomem *port_regs =
 	    		qdev->mem_map_registers;
@@ -680,7 +704,7 @@
 	}
 
 	ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg,
-			   PHYAddr[mac_index] | regAddr);
+			   phyAddr | regAddr);
 
 	ql_write_page0_reg(qdev, &port_regs->macMIIMgmtDataReg, value);
 
@@ -701,7 +725,7 @@
 }
 
 static int ql_mii_read_reg_ex(struct ql3_adapter *qdev, u16 regAddr,
-			      u16 * value, u32 mac_index)
+			      u16 * value, u32 phyAddr)
 {
 	struct ql3xxx_port_registers __iomem *port_regs =
 	    		qdev->mem_map_registers;
@@ -720,7 +744,7 @@
 	}
 
 	ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg,
-			   PHYAddr[mac_index] | regAddr);
+			   phyAddr | regAddr);
 
 	ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg,
 			   (MAC_MII_CONTROL_RC << 16));
@@ -850,28 +874,31 @@
 
 }
 
-static void ql_petbi_reset_ex(struct ql3_adapter *qdev, u32 mac_index)
+static void ql_petbi_reset_ex(struct ql3_adapter *qdev)
 {
 	ql_mii_write_reg_ex(qdev, PETBI_CONTROL_REG, PETBI_CTRL_SOFT_RESET,
-			    mac_index);
+			    PHYAddr[qdev->mac_index]);
 }
 
-static void ql_petbi_start_neg_ex(struct ql3_adapter *qdev, u32 mac_index)
+static void ql_petbi_start_neg_ex(struct ql3_adapter *qdev)
 {
 	u16 reg;
 
 	/* Enable Auto-negotiation sense */
-	ql_mii_read_reg_ex(qdev, PETBI_TBI_CTRL, &reg, mac_index);
+	ql_mii_read_reg_ex(qdev, PETBI_TBI_CTRL, &reg, 
+			   PHYAddr[qdev->mac_index]);
 	reg |= PETBI_TBI_AUTO_SENSE;
-	ql_mii_write_reg_ex(qdev, PETBI_TBI_CTRL, reg, mac_index);
+	ql_mii_write_reg_ex(qdev, PETBI_TBI_CTRL, reg, 
+			    PHYAddr[qdev->mac_index]);
 
 	ql_mii_write_reg_ex(qdev, PETBI_NEG_ADVER,
-			    PETBI_NEG_PAUSE | PETBI_NEG_DUPLEX, mac_index);
+			    PETBI_NEG_PAUSE | PETBI_NEG_DUPLEX, 
+			    PHYAddr[qdev->mac_index]);
 
 	ql_mii_write_reg_ex(qdev, PETBI_CONTROL_REG,
 			    PETBI_CTRL_AUTO_NEG | PETBI_CTRL_RESTART_NEG |
 			    PETBI_CTRL_FULL_DUPLEX | PETBI_CTRL_SPEED_1000,
-			    mac_index);
+			    PHYAddr[qdev->mac_index]);
 }
 
 static void ql_petbi_init(struct ql3_adapter *qdev)
@@ -880,10 +907,10 @@
 	ql_petbi_start_neg(qdev);
 }
 
-static void ql_petbi_init_ex(struct ql3_adapter *qdev, u32 mac_index)
+static void ql_petbi_init_ex(struct ql3_adapter *qdev)
 {
-	ql_petbi_reset_ex(qdev, mac_index);
-	ql_petbi_start_neg_ex(qdev, mac_index);
+	ql_petbi_reset_ex(qdev);
+	ql_petbi_start_neg_ex(qdev);
 }
 
 static int ql_is_petbi_neg_pause(struct ql3_adapter *qdev)
@@ -896,33 +923,128 @@
 	return (reg & PETBI_NEG_PAUSE_MASK) == PETBI_NEG_PAUSE;
 }
 
+static void phyAgereSpecificInit(struct ql3_adapter *qdev, u32 miiAddr)
+{
+	printk(KERN_INFO "%s: enabling Agere specific PHY\n", qdev->ndev->name);
+	/* power down device bit 11 = 1 */
+	ql_mii_write_reg_ex(qdev, 0x00, 0x1940, miiAddr);
+	/* enable diagnostic mode bit 2 = 1 */
+	ql_mii_write_reg_ex(qdev, 0x12, 0x840e, miiAddr);
+	/* 1000MB amplitude adjust (see Agere errata) */
+	ql_mii_write_reg_ex(qdev, 0x10, 0x8805, miiAddr);
+	/* 1000MB amplitude adjust (see Agere errata) */
+	ql_mii_write_reg_ex(qdev, 0x11, 0xf03e, miiAddr);
+	/* 100MB amplitude adjust (see Agere errata) */
+	ql_mii_write_reg_ex(qdev, 0x10, 0x8806, miiAddr);
+	/* 100MB amplitude adjust (see Agere errata) */
+	ql_mii_write_reg_ex(qdev, 0x11, 0x003e, miiAddr);
+	/* 10MB amplitude adjust (see Agere errata) */
+	ql_mii_write_reg_ex(qdev, 0x10, 0x8807, miiAddr);
+	/* 10MB amplitude adjust (see Agere errata) */
+	ql_mii_write_reg_ex(qdev, 0x11, 0x1f00, miiAddr);
+	/* point to hidden reg 0x2806 */
+	ql_mii_write_reg_ex(qdev, 0x10, 0x2806, miiAddr);
+	/* Write new PHYAD w/bit 5 set */
+	ql_mii_write_reg_ex(qdev, 0x11, 0x0020 | (PHYAddr[qdev->mac_index] >> 8), miiAddr);
+	/* 
+	 * Disable diagnostic mode bit 2 = 0
+	 * Power up device bit 11 = 0
+	 * Link up (on) and activity (blink)
+	 */
+	ql_mii_write_reg(qdev, 0x12, 0x840a);
+	ql_mii_write_reg(qdev, 0x00, 0x1140);
+	ql_mii_write_reg(qdev, 0x1c, 0xfaf0);
+}
+
+static PHY_DEVICE_et getPhyType (struct ql3_adapter *qdev, 
+				 u16 phyIdReg0, u16 phyIdReg1)
+{
+	PHY_DEVICE_et result = PHY_TYPE_UNKNOWN;
+	u32   oui;     
+	u16   model;
+	int i;   
+
+	if (phyIdReg0 == 0xffff) {
+		return result;
+	}
+   
+	if (phyIdReg1 == 0xffff) {
+		return result;
+	}
+
+	/* oui is split between two registers */
+	oui = (phyIdReg0 << 6) | ((phyIdReg1 & PHY_OUI_1_MASK) >> 10);
+
+	model = (phyIdReg1 & PHY_MODEL_MASK) >> 4;
+
+	/* Scan table for this PHY */
+	for(i = 0; i < MAX_PHY_DEV_TYPES; i++) {
+		if ((oui == PHY_DEVICES[i].phyIdOUI) && (model == PHY_DEVICES[i].phyIdModel))
+		{
+			result = PHY_DEVICES[i].phyDevice;
+
+			printk(KERN_INFO "%s: Phy: %s\n",
+				qdev->ndev->name, PHY_DEVICES[i].name);
+			
+		        break;
+		}
+	}
+
+	return result;
+}
+
 static int ql_phy_get_speed(struct ql3_adapter *qdev)
 {
 	u16 reg;
 
+	switch(qdev->phyType) {
+	case PHY_AGERE_ET1011C:
+	{
+		if (ql_mii_read_reg(qdev, 0x1A, &reg) < 0)
+			return 0;
+
+		reg = (reg >> 8) & 3;
+		break;
+	}
+	default:
 	if (ql_mii_read_reg(qdev, AUX_CONTROL_STATUS, &reg) < 0)
 		return 0;
 
 	reg = (((reg & 0x18) >> 3) & 3);
+	}
 
-	if (reg == 2)
+	switch(reg) {
+		case 2:
 		return SPEED_1000;
-	else if (reg == 1)
+		case 1:
 		return SPEED_100;
-	else if (reg == 0)
+		case 0:
 		return SPEED_10;
-	else
+		default:
 		return -1;
+	}
 }
 
 static int ql_is_full_dup(struct ql3_adapter *qdev)
 {
 	u16 reg;
 
-	if (ql_mii_read_reg(qdev, AUX_CONTROL_STATUS, &reg) < 0)
-		return 0;
-
-	return (reg & PHY_AUX_DUPLEX_STAT) != 0;
+	switch(qdev->phyType) {
+	case PHY_AGERE_ET1011C:
+	{
+		if (ql_mii_read_reg(qdev, 0x1A, &reg))
+			return 0;
+			
+		return ((reg & 0x0080) && (reg & 0x1000)) != 0;
+	}
+	case PHY_VITESSE_VSC8211:
+	default:
+	{
+		if (ql_mii_read_reg(qdev, AUX_CONTROL_STATUS, &reg) < 0)
+			return 0;
+		return (reg & PHY_AUX_DUPLEX_STAT) != 0;
+	}
+	}
 }
 
 static int ql_is_phy_neg_pause(struct ql3_adapter *qdev)
@@ -935,6 +1057,73 @@
 	return (reg & PHY_NEG_PAUSE) != 0;
 }
 
+static int PHY_Setup(struct ql3_adapter *qdev)
+{
+	u16   reg1;
+	u16   reg2;
+	bool  agereAddrChangeNeeded = false;
+	u32 miiAddr = 0;
+	int err;
+
+	/*  Determine the PHY we are using by reading the ID's */
+	err = ql_mii_read_reg(qdev, PHY_ID_0_REG, &reg1);
+	if(err != 0) {
+		printk(KERN_ERR "%s: Could not read from reg PHY_ID_0_REG\n",
+		       qdev->ndev->name);
+                return err;
+	}
+
+	err = ql_mii_read_reg(qdev, PHY_ID_1_REG, &reg2);
+	if(err != 0) {
+		printk(KERN_ERR "%s: Could not read from reg PHY_ID_0_REG\n",
+		       qdev->ndev->name);
+                return err;
+	}
+
+	/*  Check if we have a Agere PHY */
+	if ((reg1 == 0xffff) || (reg2 == 0xffff)) {
+
+		/* Determine which MII address we should be using 
+		   determined by the index of the card */
+		if (qdev->mac_index == 0) {
+			miiAddr = MII_AGERE_ADDR_1;
+		} else {
+			miiAddr = MII_AGERE_ADDR_2;
+		}
+      
+		err =ql_mii_read_reg_ex(qdev, PHY_ID_0_REG, &reg1, miiAddr);
+		if(err != 0) {
+			printk(KERN_ERR "%s: Could not read from reg PHY_ID_0_REG after Agere detected\n",
+		       	       qdev->ndev->name);
+                	return err; 
+		}
+
+		err = ql_mii_read_reg_ex(qdev, PHY_ID_1_REG, &reg2, miiAddr);
+		if(err != 0) {
+			printk(KERN_ERR "%s: Could not read from reg PHY_ID_0_REG after Agere detected\n",
+			       qdev->ndev->name);
+        	        return err;
+		}
+   
+		/*  We need to remember to initialize the Agere PHY */
+         	agereAddrChangeNeeded = true; 
+	}
+
+	/*  Determine the particular PHY we have on board to apply
+	    PHY specific initializations */
+	qdev->phyType = getPhyType(qdev, reg1, reg2);
+
+	if ((qdev->phyType == PHY_AGERE_ET1011C) && agereAddrChangeNeeded) {
+		/* need this here so address gets changed */
+		phyAgereSpecificInit(qdev, miiAddr);  
+	} else if (qdev->phyType == PHY_TYPE_UNKNOWN) {
+		printk(KERN_ERR "%s: PHY is unknown\n", qdev->ndev->name);
+		return -EIO;
+	}
+
+	return 0;
+}
+
 /*
  * Caller holds hw_lock.
  */
@@ -1205,15 +1394,14 @@
 /*
  * Caller holds hw_lock.
  */
-static int ql_this_adapter_controls_port(struct ql3_adapter *qdev,
-					 u32 mac_index)
+static int ql_this_adapter_controls_port(struct ql3_adapter *qdev)
 {
 	struct ql3xxx_port_registers __iomem *port_regs =
 	    		qdev->mem_map_registers;
 	u32 bitToCheck = 0;
 	u32 temp;
 
-	switch (mac_index) {
+	switch (qdev->mac_index) {
 	case 0:
 		bitToCheck = PORT_STATUS_F1_ENABLED;
 		break;
@@ -1238,27 +1426,96 @@
 	}
 }
 
-static void ql_phy_reset_ex(struct ql3_adapter *qdev, u32 mac_index)
+static void ql_phy_reset_ex(struct ql3_adapter *qdev)
 {
-	ql_mii_write_reg_ex(qdev, CONTROL_REG, PHY_CTRL_SOFT_RESET, mac_index);
+	ql_mii_write_reg_ex(qdev, CONTROL_REG, PHY_CTRL_SOFT_RESET, 
+			    PHYAddr[qdev->mac_index]);
 }
 
-static void ql_phy_start_neg_ex(struct ql3_adapter *qdev, u32 mac_index)
+static void ql_phy_start_neg_ex(struct ql3_adapter *qdev)
 {
 	u16 reg;
+	u16 portConfiguration;
 
-	ql_mii_write_reg_ex(qdev, PHY_NEG_ADVER,
-			    PHY_NEG_PAUSE | PHY_NEG_ADV_SPEED | 1, mac_index);
+	if(qdev->phyType == PHY_AGERE_ET1011C) {
+		/* turn off external loopback */
+		ql_mii_write_reg(qdev, 0x13, 0x0000); 
+	}
 
-	ql_mii_read_reg_ex(qdev, CONTROL_REG, &reg, mac_index);
-	ql_mii_write_reg_ex(qdev, CONTROL_REG, reg | PHY_CTRL_RESTART_NEG,
-			    mac_index);
+	if(qdev->mac_index == 0)
+		portConfiguration = qdev->nvram_data.macCfg_port0.portConfiguration;
+	else
+		portConfiguration = qdev->nvram_data.macCfg_port1.portConfiguration;
+
+	/*  Some HBA's in the field are set to 0 and they need to
+	    be reinterpreted with a default value */
+	if(portConfiguration == 0)
+		portConfiguration = PORT_CONFIG_DEFAULT;
+
+	/* Set the 1000 advertisements */
+	ql_mii_read_reg_ex(qdev, PHY_GIG_CONTROL, &reg, 
+			   PHYAddr[qdev->mac_index]);
+	reg &= ~PHY_GIG_ALL_PARAMS;
+
+	if(portConfiguration & 
+	   PORT_CONFIG_FULL_DUPLEX_ENABLED &
+	   PORT_CONFIG_1000MB_SPEED) {
+		reg |= PHY_GIG_ADV_1000F;
+	}
+	 
+	if(portConfiguration & 
+	   PORT_CONFIG_HALF_DUPLEX_ENABLED &
+	   PORT_CONFIG_1000MB_SPEED) {
+		reg |= PHY_GIG_ADV_1000H;
+	}
+
+	ql_mii_write_reg_ex(qdev, PHY_GIG_CONTROL, reg, 
+			    PHYAddr[qdev->mac_index]);
+
+	/* Set the 10/100 & pause negotiation advertisements */
+	ql_mii_read_reg_ex(qdev, PHY_NEG_ADVER, &reg,
+			   PHYAddr[qdev->mac_index]);
+	reg &= ~PHY_NEG_ALL_PARAMS;
+
+	if(portConfiguration & PORT_CONFIG_SYM_PAUSE_ENABLED)
+		reg |= PHY_NEG_ASY_PAUSE | PHY_NEG_SYM_PAUSE;
+
+	if(portConfiguration & PORT_CONFIG_FULL_DUPLEX_ENABLED) {
+		if(portConfiguration & PORT_CONFIG_100MB_SPEED)
+			reg |= PHY_NEG_ADV_100F;
+		
+		if(portConfiguration & PORT_CONFIG_10MB_SPEED)
+			reg |= PHY_NEG_ADV_10F;
+	}
+
+	if(portConfiguration & PORT_CONFIG_HALF_DUPLEX_ENABLED) {
+		if(portConfiguration & PORT_CONFIG_100MB_SPEED)
+			reg |= PHY_NEG_ADV_100H;
+		
+		if(portConfiguration & PORT_CONFIG_10MB_SPEED)
+			reg |= PHY_NEG_ADV_10H;
+	}
+
+	if(portConfiguration &
+	   PORT_CONFIG_1000MB_SPEED) {
+		reg |= 1;	
+	}
+
+	ql_mii_write_reg_ex(qdev, PHY_NEG_ADVER, reg, 
+			    PHYAddr[qdev->mac_index]);
+
+	ql_mii_read_reg_ex(qdev, CONTROL_REG, &reg, PHYAddr[qdev->mac_index]);
+	
+	ql_mii_write_reg_ex(qdev, CONTROL_REG, 
+			    reg | PHY_CTRL_RESTART_NEG | PHY_CTRL_AUTO_NEG,
+			    PHYAddr[qdev->mac_index]);
 }
 
-static void ql_phy_init_ex(struct ql3_adapter *qdev, u32 mac_index)
+static void ql_phy_init_ex(struct ql3_adapter *qdev)
 {
-	ql_phy_reset_ex(qdev, mac_index);
-	ql_phy_start_neg_ex(qdev, mac_index);
+	ql_phy_reset_ex(qdev);
+	PHY_Setup(qdev);
+	ql_phy_start_neg_ex(qdev);
 }
 
 /*
@@ -1295,14 +1552,17 @@
 {
 	if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK,
 		(QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) *
-			 2) << 7))
+			 2) << 7)) {
+		printk(KERN_ERR "%s: Could not get hw lock for GIO\n",
+		       qdev->ndev->name);
 		return -1;
+	}
 
 	if (ql_is_fiber(qdev)) {
 		ql_petbi_init(qdev);
 	} else {
 		/* Copper port */
-		ql_phy_init_ex(qdev, qdev->mac_index);
+		ql_phy_init_ex(qdev);
 	}
 
 	ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK);
@@ -1453,7 +1713,7 @@
  */
 static void ql_get_phy_owner(struct ql3_adapter *qdev)
 {
-	if (ql_this_adapter_controls_port(qdev, qdev->mac_index))
+	if (ql_this_adapter_controls_port(qdev))
 		set_bit(QL_LINK_MASTER,&qdev->flags);
 	else
 		clear_bit(QL_LINK_MASTER,&qdev->flags);
@@ -1467,11 +1727,11 @@
 	ql_mii_enable_scan_mode(qdev);
 
 	if (test_bit(QL_LINK_OPTICAL,&qdev->flags)) {
-		if (ql_this_adapter_controls_port(qdev, qdev->mac_index))
-			ql_petbi_init_ex(qdev, qdev->mac_index);
+		if (ql_this_adapter_controls_port(qdev))
+			ql_petbi_init_ex(qdev);
 	} else {
-		if (ql_this_adapter_controls_port(qdev, qdev->mac_index))
-			ql_phy_init_ex(qdev, qdev->mac_index);
+		if (ql_this_adapter_controls_port(qdev))
+			ql_phy_init_ex(qdev);
 	}
 }
 
@@ -1624,6 +1884,23 @@
 	qdev->msg_enable = value;
 }
 
+static void ql_get_pauseparam(struct net_device *ndev,
+			      struct ethtool_pauseparam *pause)
+{
+	struct ql3_adapter *qdev = netdev_priv(ndev);
+	struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers;
+
+	u32 reg;
+	if(qdev->mac_index == 0)
+		reg = ql_read_page0_reg(qdev, &port_regs->mac0ConfigReg);
+	else
+		reg = ql_read_page0_reg(qdev, &port_regs->mac1ConfigReg);
+
+	pause->autoneg  = ql_get_auto_cfg_status(qdev);
+	pause->rx_pause = (reg & MAC_CONFIG_REG_RF) >> 2;
+	pause->tx_pause = (reg & MAC_CONFIG_REG_TF) >> 1;
+}
+
 static const struct ethtool_ops ql3xxx_ethtool_ops = {
 	.get_settings = ql_get_settings,
 	.get_drvinfo = ql_get_drvinfo,
@@ -1631,6 +1908,7 @@
 	.get_link = ethtool_op_get_link,
 	.get_msglevel = ql_get_msglevel,
 	.set_msglevel = ql_set_msglevel,
+	.get_pauseparam = ql_get_pauseparam,
 };
 
 static int ql_populate_free_queue(struct ql3_adapter *qdev)
@@ -1815,14 +2093,14 @@
 	atomic_inc(&qdev->tx_count);
 }
 
-void ql_get_sbuf(struct ql3_adapter *qdev)
+static void ql_get_sbuf(struct ql3_adapter *qdev)
 {
 	if (++qdev->small_buf_index == NUM_SMALL_BUFFERS)
 		qdev->small_buf_index = 0;
 	qdev->small_buf_release_cnt++;
 }
 
-struct ql_rcv_buf_cb *ql_get_lbuf(struct ql3_adapter *qdev)
+static struct ql_rcv_buf_cb *ql_get_lbuf(struct ql3_adapter *qdev)
 {
 	struct ql_rcv_buf_cb *lrg_buf_cb = NULL;
 	lrg_buf_cb = &qdev->lrg_buf[qdev->lrg_buf_index];
@@ -1873,7 +2151,6 @@
 			 pci_unmap_len(lrg_buf_cb2, maplen),
 			 PCI_DMA_FROMDEVICE);
 	prefetch(skb->data);
-	skb->dev = qdev->ndev;
 	skb->ip_summed = CHECKSUM_NONE;
 	skb->protocol = eth_type_trans(skb, qdev->ndev);
 
@@ -1928,7 +2205,8 @@
 		 * Copy the ethhdr from first buffer to second. This
 		 * is necessary for 3022 IP completions.
 		 */
-		memcpy(skb_push(skb2, size), skb1->data + VLAN_ID_LEN, size);
+		skb_copy_from_linear_data_offset(skb1, VLAN_ID_LEN,
+						 skb_push(skb2, size), size);
 	} else {
 		u16 checksum = le16_to_cpu(ib_ip_rsp_ptr->checksum);
 		if (checksum & 
@@ -1946,7 +2224,6 @@
 			skb2->ip_summed = CHECKSUM_UNNECESSARY;
 		}
 	}
-	skb2->dev = qdev->ndev;
 	skb2->protocol = eth_type_trans(skb2, qdev->ndev);
 
 	netif_receive_skb(skb2);
@@ -3075,6 +3352,7 @@
 		goto out;
 	}
 
+	PHY_Setup(qdev);
 	ql_init_scan_mode(qdev);
 	ql_get_phy_owner(qdev);
 
diff --git a/drivers/net/qla3xxx.h b/drivers/net/qla3xxx.h
index 0203f88..4a832c4 100755
--- a/drivers/net/qla3xxx.h
+++ b/drivers/net/qla3xxx.h
@@ -293,6 +293,16 @@
 
 #define MII_SCAN_REGISTER 0x00000001
 
+#define PHY_ID_0_REG    2
+#define PHY_ID_1_REG    3
+
+#define PHY_OUI_1_MASK       0xfc00
+#define PHY_MODEL_MASK       0x03f0
+
+/*  Address for the Agere Phy */
+#define MII_AGERE_ADDR_1  0x00001000
+#define MII_AGERE_ADDR_2  0x00001100
+
 /* 32-bit ispControlStatus */
 enum {
 	ISP_CONTROL_NP_MASK = 0x0003,
@@ -789,6 +799,7 @@
 	PHY_CTRL_LOOPBACK = 0x4000,
 
 	PETBI_CONTROL_REG = 0x00,
+	PETBI_CTRL_ALL_PARAMS = 0x7140,
 	PETBI_CTRL_SOFT_RESET = 0x8000,
 	PETBI_CTRL_AUTO_NEG = 0x1000,
 	PETBI_CTRL_RESTART_NEG = 0x0200,
@@ -811,6 +822,23 @@
 	PETBI_EXPANSION_REG = 0x06,
 	PETBI_EXP_PAGE_RX = 0x0002,
 
+	PHY_GIG_CONTROL = 9,
+	PHY_GIG_ENABLE_MAN = 0x1000,  /* Enable Master/Slave Manual Config*/
+	PHY_GIG_SET_MASTER = 0x0800,  /* Set Master (slave if clear)*/
+	PHY_GIG_ALL_PARAMS = 0x0300,
+	PHY_GIG_ADV_1000F = 0x0200,
+	PHY_GIG_ADV_1000H = 0x0100,
+
+	PHY_NEG_ADVER = 4,
+	PHY_NEG_ALL_PARAMS = 0x0fe0,
+	PHY_NEG_ASY_PAUSE =  0x0800,
+	PHY_NEG_SYM_PAUSE =  0x0400,
+	PHY_NEG_ADV_SPEED =  0x01e0,
+	PHY_NEG_ADV_100F =   0x0100,
+	PHY_NEG_ADV_100H =   0x0080,
+	PHY_NEG_ADV_10F =    0x0040,
+	PHY_NEG_ADV_10H =    0x0020,
+
 	PETBI_TBI_CTRL = 0x11,
 	PETBI_TBI_RESET = 0x8000,
 	PETBI_TBI_AUTO_SENSE = 0x0100,
@@ -826,8 +854,7 @@
 	PHY_AUX_RESET_STICK = 0x0002,
 	PHY_NEG_PAUSE = 0x0400,
 	PHY_CTRL_SOFT_RESET = 0x8000,
-	PHY_NEG_ADVER = 4,
-	PHY_NEG_ADV_SPEED = 0x01e0,
+	PHY_CTRL_AUTO_NEG = 0x1000,
 	PHY_CTRL_RESTART_NEG = 0x0200,
 };
 enum {
@@ -892,6 +919,7 @@
 	u16 pauseThreshold_mac;
 	u16 resumeThreshold_mac;
 	u16 portConfiguration;
+#define PORT_CONFIG_DEFAULT                 0xf700
 #define PORT_CONFIG_AUTO_NEG_ENABLED        0x8000
 #define PORT_CONFIG_SYM_PAUSE_ENABLED       0x4000
 #define PORT_CONFIG_FULL_DUPLEX_ENABLED     0x2000
@@ -1259,6 +1287,7 @@
 	struct delayed_work tx_timeout_work;
 	u32 max_frame_size;
 	u32 device_id;
+	u16 phyType;
 };
 
 #endif				/* _QLA3XXX_H_ */
diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c
index 6a77b8a..45876a8 100644
--- a/drivers/net/r8169.c
+++ b/drivers/net/r8169.c
@@ -2284,7 +2284,7 @@
 			return LargeSend | ((mss & MSSMask) << MSSShift);
 	}
 	if (skb->ip_summed == CHECKSUM_PARTIAL) {
-		const struct iphdr *ip = skb->nh.iph;
+		const struct iphdr *ip = ip_hdr(skb);
 
 		if (ip->protocol == IPPROTO_TCP)
 			return IPCS | TCPCS;
@@ -2586,7 +2586,6 @@
 			pci_action(tp->pci_dev, le64_to_cpu(desc->addr),
 				   tp->rx_buf_sz, PCI_DMA_FROMDEVICE);
 
-			skb->dev = dev;
 			skb_put(skb, pkt_size);
 			skb->protocol = eth_type_trans(skb, dev);
 
diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c
index b7ff484..df6b738 100644
--- a/drivers/net/rionet.c
+++ b/drivers/net/rionet.c
@@ -115,7 +115,6 @@
 
 		rnet->rx_skb[i]->data = data;
 		skb_put(rnet->rx_skb[i], RIO_MAX_MSG_SIZE);
-		rnet->rx_skb[i]->dev = ndev;
 		rnet->rx_skb[i]->protocol =
 		    eth_type_trans(rnet->rx_skb[i], ndev);
 		error = netif_rx(rnet->rx_skb[i]);
diff --git a/drivers/net/rrunner.c b/drivers/net/rrunner.c
index d81536f..25c73d4 100644
--- a/drivers/net/rrunner.c
+++ b/drivers/net/rrunner.c
@@ -1029,7 +1029,6 @@
 					goto defer;
 				}
 			}
-			skb->dev = dev;
 			skb->protocol = hippi_type_trans(skb, dev);
 
 			netif_rx(skb);		/* send it up */
@@ -1452,7 +1451,7 @@
 		}
 		skb_reserve(new_skb, 8);
 		skb_put(new_skb, len);
-		memcpy(new_skb->data, skb->data, len);
+		skb_copy_from_linear_data(skb, new_skb->data, len);
 		dev_kfree_skb(skb);
 		skb = new_skb;
 	}
diff --git a/drivers/net/s2io-regs.h b/drivers/net/s2io-regs.h
index 33fb7f3..4cb710b 100644
--- a/drivers/net/s2io-regs.h
+++ b/drivers/net/s2io-regs.h
@@ -1,6 +1,6 @@
 /************************************************************************
  * regs.h: A Linux PCI-X Ethernet driver for Neterion 10GbE Server NIC
- * Copyright(c) 2002-2005 Neterion Inc.
+ * Copyright(c) 2002-2007 Neterion Inc.
 
  * This software may be used and distributed according to the terms of
  * the GNU General Public License (GPL), incorporated herein by reference.
diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c
index 46ebf141..290e1c1 100644
--- a/drivers/net/s2io.c
+++ b/drivers/net/s2io.c
@@ -1,6 +1,6 @@
 /************************************************************************
  * s2io.c: A Linux PCI-X Ethernet driver for Neterion 10GbE Server NIC
- * Copyright(c) 2002-2005 Neterion Inc.
+ * Copyright(c) 2002-2007 Neterion Inc.
 
  * This software may be used and distributed according to the terms of
  * the GNU General Public License (GPL), incorporated herein by reference.
@@ -84,7 +84,7 @@
 #include "s2io.h"
 #include "s2io-regs.h"
 
-#define DRV_VERSION "2.0.17.1"
+#define DRV_VERSION "2.0.22.1"
 
 /* S2io Driver name & version. */
 static char s2io_driver_name[] = "Neterion";
@@ -316,7 +316,7 @@
 }
 
 /* A flag indicating whether 'RX_PA_CFG_STRIP_VLAN_TAG' bit is set or not */
-int vlan_strip_flag;
+static int vlan_strip_flag;
 
 /* Unregister the vlan */
 static void s2io_vlan_rx_kill_vid(struct net_device *dev, unsigned long vid)
@@ -394,7 +394,6 @@
 	END_SIGN
 };
 
-MODULE_AUTHOR("Raghavendra Koushik <raghavendra.koushik@neterion.com>");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(DRV_VERSION);
 
@@ -516,7 +515,7 @@
 		mac_control->fifos[i].list_info = kmalloc(list_holder_size,
 							  GFP_KERNEL);
 		if (!mac_control->fifos[i].list_info) {
-			DBG_PRINT(ERR_DBG,
+			DBG_PRINT(INFO_DBG,
 				  "Malloc failed for list_info\n");
 			return -ENOMEM;
 		}
@@ -542,9 +541,9 @@
 			tmp_v = pci_alloc_consistent(nic->pdev,
 						     PAGE_SIZE, &tmp_p);
 			if (!tmp_v) {
-				DBG_PRINT(ERR_DBG,
+				DBG_PRINT(INFO_DBG,
 					  "pci_alloc_consistent ");
-				DBG_PRINT(ERR_DBG, "failed for TxDL\n");
+				DBG_PRINT(INFO_DBG, "failed for TxDL\n");
 				return -ENOMEM;
 			}
 			/* If we got a zero DMA address(can happen on
@@ -561,9 +560,9 @@
 				tmp_v = pci_alloc_consistent(nic->pdev,
 						     PAGE_SIZE, &tmp_p);
 				if (!tmp_v) {
-					DBG_PRINT(ERR_DBG,
+					DBG_PRINT(INFO_DBG,
 					  "pci_alloc_consistent ");
-					DBG_PRINT(ERR_DBG, "failed for TxDL\n");
+					DBG_PRINT(INFO_DBG, "failed for TxDL\n");
 					return -ENOMEM;
 				}
 			}
@@ -2187,7 +2186,7 @@
 	/* skb_shinfo(skb)->frag_list will have L4 data payload */
 	skb_shinfo(skb)->frag_list = dev_alloc_skb(dev->mtu + ALIGN_SIZE);
 	if (skb_shinfo(skb)->frag_list == NULL) {
-		DBG_PRINT(ERR_DBG, "%s: dev_alloc_skb failed\n ", dev->name);
+		DBG_PRINT(INFO_DBG, "%s: dev_alloc_skb failed\n ", dev->name);
 		return -ENOMEM ;
 	}
 	frag_list = skb_shinfo(skb)->frag_list;
@@ -2195,7 +2194,7 @@
 	frag_list->next = NULL;
 	tmp = (void *)ALIGN((long)frag_list->data, ALIGN_SIZE + 1);
 	frag_list->data = tmp;
-	frag_list->tail = tmp;
+	skb_reset_tail_pointer(frag_list);
 
 	/* Buffer-2 receives L4 data payload */
 	((struct RxD3*)rxdp)->Buffer2_ptr = pci_map_single(nic->pdev,
@@ -2242,6 +2241,7 @@
 	struct buffAdd *ba;
 	unsigned long flags;
 	struct RxD_t *first_rxdp = NULL;
+	u64 Buffer0_ptr = 0, Buffer1_ptr = 0;
 
 	mac_control = &nic->mac_control;
 	config = &nic->config;
@@ -2313,8 +2313,8 @@
 		/* allocate skb */
 		skb = dev_alloc_skb(size);
 		if(!skb) {
-			DBG_PRINT(ERR_DBG, "%s: Out of ", dev->name);
-			DBG_PRINT(ERR_DBG, "memory to allocate SKBs\n");
+			DBG_PRINT(INFO_DBG, "%s: Out of ", dev->name);
+			DBG_PRINT(INFO_DBG, "memory to allocate SKBs\n");
 			if (first_rxdp) {
 				wmb();
 				first_rxdp->Control_1 |= RXD_OWN_XENA;
@@ -2342,14 +2342,21 @@
 			 * payload
 			 */
 
+			/* save the buffer pointers to avoid frequent dma mapping */
+			Buffer0_ptr = ((struct RxD3*)rxdp)->Buffer0_ptr;
+			Buffer1_ptr = ((struct RxD3*)rxdp)->Buffer1_ptr;
 			memset(rxdp, 0, sizeof(struct RxD3));
+			/* restore the buffer pointers for dma sync*/
+			((struct RxD3*)rxdp)->Buffer0_ptr = Buffer0_ptr;
+			((struct RxD3*)rxdp)->Buffer1_ptr = Buffer1_ptr;
+
 			ba = &mac_control->rings[ring_no].ba[block_no][off];
 			skb_reserve(skb, BUF0_LEN);
 			tmp = (u64)(unsigned long) skb->data;
 			tmp += ALIGN_SIZE;
 			tmp &= ~ALIGN_SIZE;
 			skb->data = (void *) (unsigned long)tmp;
-			skb->tail = (void *) (unsigned long)tmp;
+			skb_reset_tail_pointer(skb);
 
 			if (!(((struct RxD3*)rxdp)->Buffer0_ptr))
 				((struct RxD3*)rxdp)->Buffer0_ptr =
@@ -2573,8 +2580,8 @@
 
 	for (i = 0; i < config->rx_ring_num; i++) {
 		if (fill_rx_buffers(nic, i) == -ENOMEM) {
-			DBG_PRINT(ERR_DBG, "%s:Out of memory", dev->name);
-			DBG_PRINT(ERR_DBG, " in Rx Poll!!\n");
+			DBG_PRINT(INFO_DBG, "%s:Out of memory", dev->name);
+			DBG_PRINT(INFO_DBG, " in Rx Poll!!\n");
 			break;
 		}
 	}
@@ -2590,8 +2597,8 @@
 
 	for (i = 0; i < config->rx_ring_num; i++) {
 		if (fill_rx_buffers(nic, i) == -ENOMEM) {
-			DBG_PRINT(ERR_DBG, "%s:Out of memory", dev->name);
-			DBG_PRINT(ERR_DBG, " in Rx Poll!!\n");
+			DBG_PRINT(INFO_DBG, "%s:Out of memory", dev->name);
+			DBG_PRINT(INFO_DBG, " in Rx Poll!!\n");
 			break;
 		}
 	}
@@ -2640,8 +2647,8 @@
 
 	for (i = 0; i < config->rx_ring_num; i++) {
 		if (fill_rx_buffers(nic, i) == -ENOMEM) {
-			DBG_PRINT(ERR_DBG, "%s:Out of memory", dev->name);
-			DBG_PRINT(ERR_DBG, " in Rx Netpoll!!\n");
+			DBG_PRINT(INFO_DBG, "%s:Out of memory", dev->name);
+			DBG_PRINT(INFO_DBG, " in Rx Netpoll!!\n");
 			break;
 		}
 	}
@@ -3307,6 +3314,7 @@
 	u16 subid, pci_cmd;
 	int i;
 	u16 val16;
+	unsigned long long reset_cnt = 0;
 	DBG_PRINT(INIT_DBG,"%s - Resetting XFrame card %s\n",
 			__FUNCTION__, sp->dev->name);
 
@@ -3372,6 +3380,11 @@
 
 	/* Reset device statistics maintained by OS */
 	memset(&sp->stats, 0, sizeof (struct net_device_stats));
+	/* save reset count */
+	reset_cnt = sp->mac_control.stats_info->sw_stat.soft_reset_cnt;
+	memset(sp->mac_control.stats_info, 0, sizeof(struct stat_block));
+	/* restore reset count */
+	sp->mac_control.stats_info->sw_stat.soft_reset_cnt = reset_cnt;
 
 	/* SXE-002: Configure link and activity LED to turn it off */
 	subid = sp->pdev->subsystem_device;
@@ -3659,7 +3672,7 @@
 	nic->entries = kmalloc(MAX_REQUESTED_MSI_X * sizeof(struct msix_entry),
 			       GFP_KERNEL);
 	if (nic->entries == NULL) {
-		DBG_PRINT(ERR_DBG, "%s: Memory allocation failed\n", __FUNCTION__);
+		DBG_PRINT(INFO_DBG, "%s: Memory allocation failed\n", __FUNCTION__);
 		return -ENOMEM;
 	}
 	memset(nic->entries, 0, MAX_REQUESTED_MSI_X * sizeof(struct msix_entry));
@@ -3668,7 +3681,7 @@
 		kmalloc(MAX_REQUESTED_MSI_X * sizeof(struct s2io_msix_entry),
 				   GFP_KERNEL);
 	if (nic->s2io_entries == NULL) {
-		DBG_PRINT(ERR_DBG, "%s: Memory allocation failed\n", __FUNCTION__);
+		DBG_PRINT(INFO_DBG, "%s: Memory allocation failed\n", __FUNCTION__);
 		kfree(nic->entries);
 		return -ENOMEM;
 	}
@@ -4019,7 +4032,7 @@
 			DBG_PRINT(INTR_DBG, "%s: Rx BD hit ", __FUNCTION__);
 			DBG_PRINT(INTR_DBG, "PANIC levels\n");
 			if ((ret = fill_rx_buffers(sp, rng_n)) == -ENOMEM) {
-				DBG_PRINT(ERR_DBG, "Out of memory in %s",
+				DBG_PRINT(INFO_DBG, "Out of memory in %s",
 					  __FUNCTION__);
 				clear_bit(0, (&sp->tasklet_status));
 				return -1;
@@ -4029,8 +4042,8 @@
 			tasklet_schedule(&sp->task);
 
 	} else if (fill_rx_buffers(sp, rng_n) == -ENOMEM) {
-			DBG_PRINT(ERR_DBG, "%s:Out of memory", sp->dev->name);
-			DBG_PRINT(ERR_DBG, " in Rx Intr!!\n");
+			DBG_PRINT(INFO_DBG, "%s:Out of memory", sp->dev->name);
+			DBG_PRINT(INFO_DBG, " in Rx Intr!!\n");
 	}
 	return 0;
 }
@@ -4279,9 +4292,7 @@
 			if (cnt == 5)
 				break; /* Updt failed */
 		} while(1);
-	} else {
-		memset(sp->mac_control.stats_info, 0, sizeof(struct stat_block));
-	}
+	} 
 }
 
 /**
@@ -5949,12 +5960,12 @@
 		for (i = 0; i < config->rx_ring_num; i++) {
 			ret = fill_rx_buffers(sp, i);
 			if (ret == -ENOMEM) {
-				DBG_PRINT(ERR_DBG, "%s: Out of ",
+				DBG_PRINT(INFO_DBG, "%s: Out of ",
 					  dev->name);
 				DBG_PRINT(ERR_DBG, "memory in tasklet\n");
 				break;
 			} else if (ret == -EFILL) {
-				DBG_PRINT(ERR_DBG,
+				DBG_PRINT(INFO_DBG,
 					  "%s: Rx Ring %d is full\n",
 					  dev->name, i);
 				break;
@@ -6065,8 +6076,8 @@
 		} else {
 			*skb = dev_alloc_skb(size);
 			if (!(*skb)) {
-				DBG_PRINT(ERR_DBG, "%s: Out of ", dev->name);
-				DBG_PRINT(ERR_DBG, "memory to allocate SKBs\n");
+				DBG_PRINT(INFO_DBG, "%s: Out of ", dev->name);
+				DBG_PRINT(INFO_DBG, "memory to allocate SKBs\n");
 				return -ENOMEM ;
 			}
 			/* storing the mapped addr in a temp variable
@@ -6088,7 +6099,7 @@
 		} else {
 			*skb = dev_alloc_skb(size);
 			if (!(*skb)) {
-				DBG_PRINT(ERR_DBG, "%s: dev_alloc_skb failed\n",
+				DBG_PRINT(INFO_DBG, "%s: dev_alloc_skb failed\n",
 					dev->name);
 				return -ENOMEM;
 			}
@@ -6115,7 +6126,7 @@
 		} else {
 			*skb = dev_alloc_skb(size);
 			if (!(*skb)) {
-				DBG_PRINT(ERR_DBG, "%s: dev_alloc_skb failed\n",
+				DBG_PRINT(INFO_DBG, "%s: dev_alloc_skb failed\n",
 					  dev->name);
 				return -ENOMEM;
 			}
@@ -6616,7 +6627,6 @@
 
 	/* Updating statistics */
 	rxdp->Host_Control = 0;
-	sp->rx_pkt_count++;
 	sp->stats.rx_packets++;
 	if (sp->rxd_mode == RXD_MODE_1) {
 		int len = RXD_GET_BUFFER0_SIZE_1(rxdp->Control_2);
@@ -7252,7 +7262,7 @@
 		goto register_failed;
 	}
 	s2io_vpd_read(sp);
-	DBG_PRINT(ERR_DBG, "Copyright(c) 2002-2005 Neterion Inc.\n");
+	DBG_PRINT(ERR_DBG, "Copyright(c) 2002-2007 Neterion Inc.\n");
 	DBG_PRINT(ERR_DBG, "%s: Neterion %s (rev %d)\n",dev->name,
 		  sp->product_name, get_xena_rev_id(sp->pdev));
 	DBG_PRINT(ERR_DBG, "%s: Driver version %s\n", dev->name,
diff --git a/drivers/net/s2io.h b/drivers/net/s2io.h
index 803137c..a656d18 100644
--- a/drivers/net/s2io.h
+++ b/drivers/net/s2io.h
@@ -1,6 +1,6 @@
 /************************************************************************
  * s2io.h: A Linux PCI-X Ethernet driver for Neterion 10GbE Server NIC
- * Copyright(c) 2002-2005 Neterion Inc.
+ * Copyright(c) 2002-2007 Neterion Inc.
 
  * This software may be used and distributed according to the terms of
  * the GNU General Public License (GPL), incorporated herein by reference.
@@ -760,7 +760,6 @@
 #define MAX_SUPPORTED_MULTICASTS MAX_MAC_SUPPORTED
 
 	struct mac_addr def_mac_addr[MAX_MAC_SUPPORTED];
-	struct mac_addr pre_mac_addr[MAX_MAC_SUPPORTED];
 
 	struct net_device_stats stats;
 	int high_dma_flag;
@@ -794,11 +793,6 @@
 	u16 all_multi_pos;
 	u16 promisc_flg;
 
-	u16 tx_pkt_count;
-	u16 rx_pkt_count;
-	u16 tx_err_count;
-	u16 rx_err_count;
-
 	/*  Id timer, used to blink NIC to physically identify NIC. */
 	struct timer_list id_timer;
 
diff --git a/drivers/net/saa9730.c b/drivers/net/saa9730.c
index 143958f..ad94358 100644
--- a/drivers/net/saa9730.c
+++ b/drivers/net/saa9730.c
@@ -688,7 +688,6 @@
 			} else {
 				lp->stats.rx_bytes += len;
 				lp->stats.rx_packets++;
-				skb->dev = dev;
 				skb_reserve(skb, 2);	/* 16 byte align */
 				skb_put(skb, len);	/* make room */
 				eth_copy_and_sum(skb,
diff --git a/drivers/net/sb1000.c b/drivers/net/sb1000.c
index b9fa4fb..1de3eec 100644
--- a/drivers/net/sb1000.c
+++ b/drivers/net/sb1000.c
@@ -834,7 +834,7 @@
 			goto dropped_frame;
 		}
 		skb->dev = dev;
-		skb->mac.raw = skb->data;
+		skb_reset_mac_header(skb);
 		skb->protocol = (unsigned short) buffer[NewDatagramHeaderSkip + 16];
 		insw(ioaddr, skb_put(skb, NewDatagramDataSize),
 			NewDatagramDataSize / 2);
diff --git a/drivers/net/sb1250-mac.c b/drivers/net/sb1250-mac.c
index 103c317..132e214 100644
--- a/drivers/net/sb1250-mac.c
+++ b/drivers/net/sb1250-mac.c
@@ -95,19 +95,28 @@
 #endif
 
 #ifdef CONFIG_SBMAC_COALESCE
-static int int_pktcnt = 0;
-module_param(int_pktcnt, int, S_IRUGO);
-MODULE_PARM_DESC(int_pktcnt, "Packet count");
+static int int_pktcnt_tx = 255;
+module_param(int_pktcnt_tx, int, S_IRUGO);
+MODULE_PARM_DESC(int_pktcnt_tx, "TX packet count");
 
-static int int_timeout = 0;
-module_param(int_timeout, int, S_IRUGO);
-MODULE_PARM_DESC(int_timeout, "Timeout value");
+static int int_timeout_tx = 255;
+module_param(int_timeout_tx, int, S_IRUGO);
+MODULE_PARM_DESC(int_timeout_tx, "TX timeout value");
+
+static int int_pktcnt_rx = 64;
+module_param(int_pktcnt_rx, int, S_IRUGO);
+MODULE_PARM_DESC(int_pktcnt_rx, "RX packet count");
+
+static int int_timeout_rx = 64;
+module_param(int_timeout_rx, int, S_IRUGO);
+MODULE_PARM_DESC(int_timeout_rx, "RX timeout value");
 #endif
 
 #include <asm/sibyte/sb1250.h>
 #if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
 #include <asm/sibyte/bcm1480_regs.h>
 #include <asm/sibyte/bcm1480_int.h>
+#define R_MAC_DMA_OODPKTLOST_RX	R_MAC_DMA_OODPKTLOST
 #elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X)
 #include <asm/sibyte/sb1250_regs.h>
 #include <asm/sibyte/sb1250_int.h>
@@ -155,8 +164,8 @@
 
 #define NUMCACHEBLKS(x) (((x)+SMP_CACHE_BYTES-1)/SMP_CACHE_BYTES)
 
-#define SBMAC_MAX_TXDESCR	32
-#define SBMAC_MAX_RXDESCR	32
+#define SBMAC_MAX_TXDESCR	256
+#define SBMAC_MAX_RXDESCR	256
 
 #define ETHER_ALIGN	2
 #define ETHER_ADDR_LEN	6
@@ -185,10 +194,10 @@
 	 * associated with it.
 	 */
 
-	struct sbmac_softc *sbdma_eth;	        /* back pointer to associated MAC */
-	int              sbdma_channel;	/* channel number */
+	struct sbmac_softc *sbdma_eth;	    /* back pointer to associated MAC */
+	int              sbdma_channel;	    /* channel number */
 	int		 sbdma_txdir;       /* direction (1=transmit) */
-	int		 sbdma_maxdescr;	/* total # of descriptors in ring */
+	int		 sbdma_maxdescr;    /* total # of descriptors in ring */
 #ifdef CONFIG_SBMAC_COALESCE
 	int		 sbdma_int_pktcnt;  /* # descriptors rx/tx before interrupt*/
 	int		 sbdma_int_timeout; /* # usec rx/tx interrupt */
@@ -197,13 +206,16 @@
 	volatile void __iomem *sbdma_config0;	/* DMA config register 0 */
 	volatile void __iomem *sbdma_config1;	/* DMA config register 1 */
 	volatile void __iomem *sbdma_dscrbase;	/* Descriptor base address */
-	volatile void __iomem *sbdma_dscrcnt;     /* Descriptor count register */
+	volatile void __iomem *sbdma_dscrcnt;   /* Descriptor count register */
 	volatile void __iomem *sbdma_curdscr;	/* current descriptor address */
+	volatile void __iomem *sbdma_oodpktlost;/* pkt drop (rx only) */
+
 
 	/*
 	 * This stuff is for maintenance of the ring
 	 */
 
+	sbdmadscr_t     *sbdma_dscrtable_unaligned;
 	sbdmadscr_t     *sbdma_dscrtable;	/* base of descriptor table */
 	sbdmadscr_t     *sbdma_dscrtable_end; /* end of descriptor table */
 
@@ -286,8 +298,8 @@
 static int sbdma_add_txbuffer(sbmacdma_t *d,struct sk_buff *m);
 static void sbdma_emptyring(sbmacdma_t *d);
 static void sbdma_fillring(sbmacdma_t *d);
-static void sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d);
-static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d);
+static int sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d, int work_to_do, int poll);
+static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d, int poll);
 static int sbmac_initctx(struct sbmac_softc *s);
 static void sbmac_channel_start(struct sbmac_softc *s);
 static void sbmac_channel_stop(struct sbmac_softc *s);
@@ -308,6 +320,8 @@
 static void sbmac_set_rx_mode(struct net_device *dev);
 static int sbmac_mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static int sbmac_close(struct net_device *dev);
+static int sbmac_poll(struct net_device *poll_dev, int *budget);
+
 static int sbmac_mii_poll(struct sbmac_softc *s,int noisy);
 static int sbmac_mii_probe(struct net_device *dev);
 
@@ -679,6 +693,10 @@
 			  int txrx,
 			  int maxdescr)
 {
+#ifdef CONFIG_SBMAC_COALESCE
+	int int_pktcnt, int_timeout;
+#endif
+
 	/*
 	 * Save away interesting stuff in the structure
 	 */
@@ -728,6 +746,11 @@
 		s->sbm_base + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_DSCR_CNT);
 	d->sbdma_curdscr =
 		s->sbm_base + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_CUR_DSCRADDR);
+	if (d->sbdma_txdir)
+		d->sbdma_oodpktlost = NULL;
+	else
+		d->sbdma_oodpktlost =
+			s->sbm_base + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_OODPKTLOST_RX);
 
 	/*
 	 * Allocate memory for the ring
@@ -735,6 +758,7 @@
 
 	d->sbdma_maxdescr = maxdescr;
 
+	d->sbdma_dscrtable_unaligned =
 	d->sbdma_dscrtable = (sbdmadscr_t *)
 		kmalloc((d->sbdma_maxdescr+1)*sizeof(sbdmadscr_t), GFP_KERNEL);
 
@@ -765,12 +789,14 @@
 	 * Setup Rx/Tx DMA coalescing defaults
 	 */
 
+	int_pktcnt = (txrx == DMA_TX) ? int_pktcnt_tx : int_pktcnt_rx;
 	if ( int_pktcnt ) {
 		d->sbdma_int_pktcnt = int_pktcnt;
 	} else {
 		d->sbdma_int_pktcnt = 1;
 	}
 
+	int_timeout = (txrx == DMA_TX) ? int_timeout_tx : int_timeout_rx;
 	if ( int_timeout ) {
 		d->sbdma_int_timeout = int_timeout;
 	} else {
@@ -933,9 +959,6 @@
 		}
 
 		sbdma_align_skb(sb_new, SMP_CACHE_BYTES, ETHER_ALIGN);
-
-		/* mark skbuff owned by our device */
-		sb_new->dev = d->sbdma_eth->sbm_dev;
 	}
 	else {
 		sb_new = sb;
@@ -1128,32 +1151,63 @@
 	}
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void sbmac_netpoll(struct net_device *netdev)
+{
+	struct sbmac_softc *sc = netdev_priv(netdev);
+	int irq = sc->sbm_dev->irq;
+
+	__raw_writeq(0, sc->sbm_imr);
+
+	sbmac_intr(irq, netdev, NULL);
+
+#ifdef CONFIG_SBMAC_COALESCE
+	__raw_writeq(((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_TX_CH0) |
+	((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_RX_CH0),
+	sc->sbm_imr);
+#else
+	__raw_writeq((M_MAC_INT_CHANNEL << S_MAC_TX_CH0) | 
+	(M_MAC_INT_CHANNEL << S_MAC_RX_CH0), sc->sbm_imr);
+#endif
+}
+#endif
 
 /**********************************************************************
- *  SBDMA_RX_PROCESS(sc,d)
+ *  SBDMA_RX_PROCESS(sc,d,work_to_do,poll)
  *
  *  Process "completed" receive buffers on the specified DMA channel.
- *  Note that this isn't really ideal for priority channels, since
- *  it processes all of the packets on a given channel before
- *  returning.
  *
  *  Input parameters:
- *	   sc - softc structure
- *  	   d - DMA channel context
+ *            sc - softc structure
+ *  	       d - DMA channel context
+ *    work_to_do - no. of packets to process before enabling interrupt
+ *                 again (for NAPI)
+ *          poll - 1: using polling (for NAPI)
  *
  *  Return value:
  *  	   nothing
  ********************************************************************* */
 
-static void sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d)
+static int sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d,
+                             int work_to_do, int poll)
 {
 	int curidx;
 	int hwidx;
 	sbdmadscr_t *dsc;
 	struct sk_buff *sb;
 	int len;
+	int work_done = 0;
+	int dropped = 0;
 
-	for (;;) {
+	prefetch(d);
+
+again:
+	/* Check if the HW dropped any frames */
+	sc->sbm_stats.rx_fifo_errors
+	    += __raw_readq(sc->sbm_rxdma.sbdma_oodpktlost) & 0xffff;
+	__raw_writeq(0, sc->sbm_rxdma.sbdma_oodpktlost);
+
+	while (work_to_do-- > 0) {
 		/*
 		 * figure out where we are (as an index) and where
 		 * the hardware is (also as an index)
@@ -1165,7 +1219,12 @@
 		 * (sbdma_remptr) and the physical address (sbdma_curdscr CSR)
 		 */
 
-		curidx = d->sbdma_remptr - d->sbdma_dscrtable;
+		dsc = d->sbdma_remptr;
+		curidx = dsc - d->sbdma_dscrtable;
+
+		prefetch(dsc);
+		prefetch(&d->sbdma_ctxtable[curidx]);
+
 		hwidx = (int) (((__raw_readq(d->sbdma_curdscr) & M_DMA_CURDSCR_ADDR) -
 				d->sbdma_dscrtable_phys) / sizeof(sbdmadscr_t));
 
@@ -1176,13 +1235,12 @@
 		 */
 
 		if (curidx == hwidx)
-			break;
+			goto done;
 
 		/*
 		 * Otherwise, get the packet's sk_buff ptr back
 		 */
 
-		dsc = &(d->sbdma_dscrtable[curidx]);
 		sb = d->sbdma_ctxtable[curidx];
 		d->sbdma_ctxtable[curidx] = NULL;
 
@@ -1194,7 +1252,7 @@
 		 * receive ring.
 		 */
 
-		if (!(dsc->dscr_a & M_DMA_ETHRX_BAD)) {
+		if (likely (!(dsc->dscr_a & M_DMA_ETHRX_BAD))) {
 
 			/*
 			 * Add a new buffer to replace the old one.  If we fail
@@ -1202,9 +1260,14 @@
 			 * packet and put it right back on the receive ring.
 			 */
 
-			if (sbdma_add_rcvbuffer(d,NULL) == -ENOBUFS) {
-				sc->sbm_stats.rx_dropped++;
+			if (unlikely (sbdma_add_rcvbuffer(d,NULL) ==
+				      -ENOBUFS)) {
+ 				sc->sbm_stats.rx_dropped++;
 				sbdma_add_rcvbuffer(d,sb); /* re-add old buffer */
+				/* No point in continuing at the moment */
+				printk(KERN_ERR "dropped packet (1)\n");
+				d->sbdma_remptr = SBDMA_NEXTBUF(d,sbdma_remptr);
+				goto done;
 			} else {
 				/*
 				 * Set length into the packet
@@ -1216,8 +1279,6 @@
 				 * receive ring.  Pass the buffer to
 				 * the kernel
 				 */
-				sc->sbm_stats.rx_bytes += len;
-				sc->sbm_stats.rx_packets++;
 				sb->protocol = eth_type_trans(sb,d->sbdma_eth->sbm_dev);
 				/* Check hw IPv4/TCP checksum if supported */
 				if (sc->rx_hw_checksum == ENABLE) {
@@ -1229,8 +1290,22 @@
 						sb->ip_summed = CHECKSUM_NONE;
 					}
 				}
+				prefetch(sb->data);
+				prefetch((const void *)(((char *)sb->data)+32));
+				if (poll)
+					dropped = netif_receive_skb(sb);
+				else
+					dropped = netif_rx(sb);
 
-				netif_rx(sb);
+				if (dropped == NET_RX_DROP) {
+					sc->sbm_stats.rx_dropped++;
+					d->sbdma_remptr = SBDMA_NEXTBUF(d,sbdma_remptr);
+					goto done;
+				}
+				else {
+					sc->sbm_stats.rx_bytes += len;
+					sc->sbm_stats.rx_packets++;
+				}
 			}
 		} else {
 			/*
@@ -1247,12 +1322,16 @@
 		 */
 
 		d->sbdma_remptr = SBDMA_NEXTBUF(d,sbdma_remptr);
-
+		work_done++;
 	}
+	if (!poll) {
+		work_to_do = 32;
+		goto again; /* collect fifo drop statistics again */
+	}
+done:
+	return work_done;
 }
 
-
-
 /**********************************************************************
  *  SBDMA_TX_PROCESS(sc,d)
  *
@@ -1264,22 +1343,30 @@
  *
  *  Input parameters:
  *      sc - softc structure
- *  	   d - DMA channel context
+ *  	 d - DMA channel context
+ *    poll - 1: using polling (for NAPI)
  *
  *  Return value:
  *  	   nothing
  ********************************************************************* */
 
-static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d)
+static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d, int poll)
 {
 	int curidx;
 	int hwidx;
 	sbdmadscr_t *dsc;
 	struct sk_buff *sb;
 	unsigned long flags;
+	int packets_handled = 0;
 
 	spin_lock_irqsave(&(sc->sbm_lock), flags);
 
+	if (d->sbdma_remptr == d->sbdma_addptr)
+	  goto end_unlock;
+
+	hwidx = (int) (((__raw_readq(d->sbdma_curdscr) & M_DMA_CURDSCR_ADDR) -
+			d->sbdma_dscrtable_phys) / sizeof(sbdmadscr_t));
+
 	for (;;) {
 		/*
 		 * figure out where we are (as an index) and where
@@ -1293,8 +1380,6 @@
 		 */
 
 		curidx = d->sbdma_remptr - d->sbdma_dscrtable;
-		hwidx = (int) (((__raw_readq(d->sbdma_curdscr) & M_DMA_CURDSCR_ADDR) -
-				d->sbdma_dscrtable_phys) / sizeof(sbdmadscr_t));
 
 		/*
 		 * If they're the same, that means we've processed all
@@ -1332,6 +1417,8 @@
 
 		d->sbdma_remptr = SBDMA_NEXTBUF(d,sbdma_remptr);
 
+		packets_handled++;
+
 	}
 
 	/*
@@ -1340,8 +1427,10 @@
 	 * watermark on the transmit queue.
 	 */
 
-	netif_wake_queue(d->sbdma_eth->sbm_dev);
+	if (packets_handled)
+		netif_wake_queue(d->sbdma_eth->sbm_dev);
 
+end_unlock:
 	spin_unlock_irqrestore(&(sc->sbm_lock), flags);
 
 }
@@ -1415,9 +1504,9 @@
 
 static void sbdma_uninitctx(struct sbmacdma_s *d)
 {
-	if (d->sbdma_dscrtable) {
-		kfree(d->sbdma_dscrtable);
-		d->sbdma_dscrtable = NULL;
+	if (d->sbdma_dscrtable_unaligned) {
+		kfree(d->sbdma_dscrtable_unaligned);
+		d->sbdma_dscrtable_unaligned = d->sbdma_dscrtable = NULL;
 	}
 
 	if (d->sbdma_ctxtable) {
@@ -1615,15 +1704,9 @@
 #endif
 
 #ifdef CONFIG_SBMAC_COALESCE
-	/*
-	 * Accept any TX interrupt and EOP count/timer RX interrupts on ch 0
-	 */
 	__raw_writeq(((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_TX_CH0) |
 		       ((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_RX_CH0), s->sbm_imr);
 #else
-	/*
-	 * Accept any kind of interrupt on TX and RX DMA channel 0
-	 */
 	__raw_writeq((M_MAC_INT_CHANNEL << S_MAC_TX_CH0) |
 		       (M_MAC_INT_CHANNEL << S_MAC_RX_CH0), s->sbm_imr);
 #endif
@@ -2056,57 +2139,46 @@
 	uint64_t isr;
 	int handled = 0;
 
-	for (;;) {
+	/*
+	 * Read the ISR (this clears the bits in the real
+	 * register, except for counter addr)
+	 */
 
-		/*
-		 * Read the ISR (this clears the bits in the real
-		 * register, except for counter addr)
-		 */
+	isr = __raw_readq(sc->sbm_isr) & ~M_MAC_COUNTER_ADDR;
 
-		isr = __raw_readq(sc->sbm_isr) & ~M_MAC_COUNTER_ADDR;
+	if (isr == 0)
+		return IRQ_RETVAL(0);
+	handled = 1;
 
-		if (isr == 0)
-			break;
+	/*
+	 * Transmits on channel 0
+	 */
 
-		handled = 1;
-
-		/*
-		 * Transmits on channel 0
-		 */
-
-		if (isr & (M_MAC_INT_CHANNEL << S_MAC_TX_CH0)) {
-			sbdma_tx_process(sc,&(sc->sbm_txdma));
+	if (isr & (M_MAC_INT_CHANNEL << S_MAC_TX_CH0)) {
+		sbdma_tx_process(sc,&(sc->sbm_txdma), 0);
+#ifdef CONFIG_NETPOLL_TRAP
+		if (netpoll_trap()) {
+			if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->state))
+				__netif_schedule(dev);
 		}
+#endif
+	}
 
-		/*
-		 * Receives on channel 0
-		 */
-
-		/*
-		 * It's important to test all the bits (or at least the
-		 * EOP_SEEN bit) when deciding to do the RX process
-		 * particularly when coalescing, to make sure we
-		 * take care of the following:
-		 *
-		 * If you have some packets waiting (have been received
-		 * but no interrupt) and get a TX interrupt before
-		 * the RX timer or counter expires, reading the ISR
-		 * above will clear the timer and counter, and you
-		 * won't get another interrupt until a packet shows
-		 * up to start the timer again.  Testing
-		 * EOP_SEEN here takes care of this case.
-		 * (EOP_SEEN is part of M_MAC_INT_CHANNEL << S_MAC_RX_CH0)
-		 */
-
-
-		if (isr & (M_MAC_INT_CHANNEL << S_MAC_RX_CH0)) {
-			sbdma_rx_process(sc,&(sc->sbm_rxdma));
+	if (isr & (M_MAC_INT_CHANNEL << S_MAC_RX_CH0)) {
+		if (netif_rx_schedule_prep(dev)) {
+			__raw_writeq(0, sc->sbm_imr);
+			__netif_rx_schedule(dev);
+			/* Depend on the exit from poll to reenable intr */
+		}
+		else {
+			/* may leave some packets behind */
+			sbdma_rx_process(sc,&(sc->sbm_rxdma),
+					 SBMAC_MAX_RXDESCR * 2, 0);
 		}
 	}
 	return IRQ_RETVAL(handled);
 }
 
-
 /**********************************************************************
  *  SBMAC_START_TX(skb,dev)
  *
@@ -2236,8 +2308,6 @@
 	}
 }
 
-
-
 #if defined(SBMAC_ETH0_HWADDR) || defined(SBMAC_ETH1_HWADDR) || defined(SBMAC_ETH2_HWADDR) || defined(SBMAC_ETH3_HWADDR)
 /**********************************************************************
  *  SBMAC_PARSE_XDIGIT(str)
@@ -2400,8 +2470,13 @@
 	dev->do_ioctl           = sbmac_mii_ioctl;
 	dev->tx_timeout         = sbmac_tx_timeout;
 	dev->watchdog_timeo     = TX_TIMEOUT;
+	dev->poll               = sbmac_poll;
+	dev->weight             = 16;
 
 	dev->change_mtu         = sb1250_change_mtu;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = sbmac_netpoll;
+#endif
 
 	/* This is needed for PASS2 for Rx H/W checksum feature */
 	sbmac_set_iphdr_offset(sc);
@@ -2799,7 +2874,39 @@
 	return 0;
 }
 
+static int sbmac_poll(struct net_device *dev, int *budget)
+{
+	int work_to_do;
+	int work_done;
+	struct sbmac_softc *sc = netdev_priv(dev);
 
+	work_to_do = min(*budget, dev->quota);
+	work_done = sbdma_rx_process(sc, &(sc->sbm_rxdma), work_to_do, 1);
+
+	if (work_done > work_to_do)
+		printk(KERN_ERR "%s exceeded work_to_do budget=%d quota=%d work-done=%d\n",
+		       sc->sbm_dev->name, *budget, dev->quota, work_done);
+
+	sbdma_tx_process(sc, &(sc->sbm_txdma), 1);
+
+	*budget -= work_done;
+	dev->quota -= work_done;
+
+	if (work_done < work_to_do) {
+		netif_rx_complete(dev);
+
+#ifdef CONFIG_SBMAC_COALESCE
+		__raw_writeq(((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_TX_CH0) |
+			     ((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_RX_CH0),
+			     sc->sbm_imr);
+#else
+		__raw_writeq((M_MAC_INT_CHANNEL << S_MAC_TX_CH0) |
+			     (M_MAC_INT_CHANNEL << S_MAC_RX_CH0), sc->sbm_imr);
+#endif
+	}
+
+	return (work_done >= work_to_do);
+}
 
 #if defined(SBMAC_ETH0_HWADDR) || defined(SBMAC_ETH1_HWADDR) || defined(SBMAC_ETH2_HWADDR) || defined(SBMAC_ETH3_HWADDR)
 static void
@@ -2886,7 +2993,7 @@
 
 		/*
 		 * The R_MAC_ETHERNET_ADDR register will be set to some nonzero
-		 * value for us by the firmware if we're going to use this MAC.
+		 * value for us by the firmware if we are going to use this MAC.
 		 * If we find a zero, skip this MAC.
 		 */
 
diff --git a/drivers/net/sc92031.c b/drivers/net/sc92031.c
index c32c21a..5b7284c 100644
--- a/drivers/net/sc92031.c
+++ b/drivers/net/sc92031.c
@@ -814,7 +814,6 @@
 			memcpy(skb_put(skb, pkt_size), rx_ring + rx_ring_offset, pkt_size);
 		}
 
-		skb->dev = dev;
 		skb->protocol = eth_type_trans(skb, dev);
 		dev->last_rx = jiffies;
 		netif_rx(skb);
diff --git a/drivers/net/seeq8005.c b/drivers/net/seeq8005.c
index 0d6c95c..4bce7c4 100644
--- a/drivers/net/seeq8005.c
+++ b/drivers/net/seeq8005.c
@@ -550,7 +550,6 @@
 				lp->stats.rx_dropped++;
 				break;
 			}
-			skb->dev = dev;
 			skb_reserve(skb, 2);	/* align data on 16 byte */
 			buf = skb_put(skb,pkt_len);
 
diff --git a/drivers/net/sgiseeq.c b/drivers/net/sgiseeq.c
index 52ed522..1fc7730 100644
--- a/drivers/net/sgiseeq.c
+++ b/drivers/net/sgiseeq.c
@@ -318,7 +318,6 @@
 			skb = dev_alloc_skb(len + 2);
 
 			if (skb) {
-				skb->dev = dev;
 				skb_reserve(skb, 2);
 				skb_put(skb, len);
 
@@ -535,7 +534,7 @@
 	 *    entry and the HPC got to the end of the chain before we
 	 *    added this new entry and restarted it.
 	 */
-	memcpy((char *)(long)td->buf_vaddr, skb->data, skblen);
+	skb_copy_from_linear_data(skb, (char *)(long)td->buf_vaddr, skblen);
 	if (len != skblen)
 		memset((char *)(long)td->buf_vaddr + skb->len, 0, len-skblen);
 	td->tdma.cntinfo = (len & HPCDMA_BCNT) |
@@ -625,7 +624,7 @@
 
 #define ALIGNED(x)  ((((unsigned long)(x)) + 0xf) & ~(0xf))
 
-static int sgiseeq_init(struct hpc3_regs* hpcregs, int irq)
+static int sgiseeq_init(struct hpc3_regs* hpcregs, int irq, int has_eeprom)
 {
 	struct sgiseeq_init_block *sr;
 	struct sgiseeq_private *sp;
@@ -651,7 +650,9 @@
 
 #define EADDR_NVOFS     250
 	for (i = 0; i < 3; i++) {
-		unsigned short tmp = ip22_nvram_read(EADDR_NVOFS / 2 + i);
+		unsigned short tmp = has_eeprom ?
+			ip22_eeprom_read(&hpcregs->eeprom, EADDR_NVOFS / 2+i) :
+			ip22_nvram_read(EADDR_NVOFS / 2+i);
 
 		dev->dev_addr[2 * i]     = tmp >> 8;
 		dev->dev_addr[2 * i + 1] = tmp & 0xff;
@@ -684,6 +685,11 @@
 	sp->hregs->dconfig = HPC3_EDCFG_FIRQ | HPC3_EDCFG_FEOP |
 			     HPC3_EDCFG_FRXDC | HPC3_EDCFG_PTO | 0x026;
 
+	/* Setup PIO and DMA transfer timing */
+	sp->hregs->pconfig = 0x161;
+	sp->hregs->dconfig = HPC3_EDCFG_FIRQ | HPC3_EDCFG_FEOP |
+			     HPC3_EDCFG_FRXDC | HPC3_EDCFG_PTO | 0x026;
+
 	/* Reset the chip. */
 	hpc3_eth_reset(sp->hregs);
 
@@ -730,8 +736,23 @@
 
 static int __init sgiseeq_probe(void)
 {
+	unsigned int tmp, ret1, ret2 = 0;
+
 	/* On board adapter on 1st HPC is always present */
-	return sgiseeq_init(hpc3c0, SGI_ENET_IRQ);
+	ret1 = sgiseeq_init(hpc3c0, SGI_ENET_IRQ, 0);
+	/* Let's see if second HPC is there */
+	if (!(ip22_is_fullhouse()) &&
+	    get_dbe(tmp, (unsigned int *)&hpc3c1->pbdma[1]) == 0) {
+		sgimc->giopar |= SGIMC_GIOPAR_MASTEREXP1 |
+				 SGIMC_GIOPAR_EXP164 |
+				 SGIMC_GIOPAR_HPC264;
+		hpc3c1->pbus_piocfg[0][0] = 0x3ffff;
+		/* interrupt/config register on Challenge S Mezz board */
+		hpc3c1->pbus_extregs[0][0] = 0x30;
+		ret2 = sgiseeq_init(hpc3c1, SGI_GIO_0_IRQ, 1);
+	}
+
+	return (ret1 & ret2) ? ret1 : 0;
 }
 
 static void __exit sgiseeq_exit(void)
diff --git a/drivers/net/sis190.c b/drivers/net/sis190.c
index 34463ce..bc8de48 100644
--- a/drivers/net/sis190.c
+++ b/drivers/net/sis190.c
@@ -632,7 +632,6 @@
 			pci_action(tp->pci_dev, le32_to_cpu(desc->addr),
 				   tp->rx_buf_sz, PCI_DMA_FROMDEVICE);
 
-			skb->dev = dev;
 			skb_put(skb, pkt_size);
 			skb->protocol = eth_type_trans(skb, dev);
 
diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c
index b2a3b19d..2cb2e15 100644
--- a/drivers/net/sis900.c
+++ b/drivers/net/sis900.c
@@ -1160,7 +1160,6 @@
 			   buffer */
 			break;
 		}
-		skb->dev = net_dev;
 		sis_priv->rx_skbuff[i] = skb;
 		sis_priv->rx_ring[i].cmdsts = RX_BUF_SIZE;
                 sis_priv->rx_ring[i].bufptr = pci_map_single(sis_priv->pci_dev,
@@ -1754,6 +1753,7 @@
 			sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE;
 		} else {
 			struct sk_buff * skb;
+			struct sk_buff * rx_skb;
 
 			pci_unmap_single(sis_priv->pci_dev,
 				sis_priv->rx_ring[entry].bufptr, RX_BUF_SIZE,
@@ -1787,10 +1787,10 @@
 			}
 
 			/* give the socket buffer to upper layers */
-			skb = sis_priv->rx_skbuff[entry];
-			skb_put(skb, rx_size);
-			skb->protocol = eth_type_trans(skb, net_dev);
-			netif_rx(skb);
+			rx_skb = sis_priv->rx_skbuff[entry];
+			skb_put(rx_skb, rx_size);
+			rx_skb->protocol = eth_type_trans(rx_skb, net_dev);
+			netif_rx(rx_skb);
 
 			/* some network statistics */
 			if ((rx_status & BCAST) == MCAST)
@@ -1800,7 +1800,6 @@
 			sis_priv->stats.rx_packets++;
 			sis_priv->dirty_rx++;
 refill_rx_ring:
-			skb->dev = net_dev;
 			sis_priv->rx_skbuff[entry] = skb;
 			sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE;
                 	sis_priv->rx_ring[entry].bufptr =
@@ -1832,7 +1831,6 @@
 				sis_priv->stats.rx_dropped++;
 				break;
 			}
-			skb->dev = net_dev;
 			sis_priv->rx_skbuff[entry] = skb;
 			sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE;
                 	sis_priv->rx_ring[entry].bufptr =
diff --git a/drivers/net/sk98lin/skge.c b/drivers/net/sk98lin/skge.c
index e94ab25..bf21862 100644
--- a/drivers/net/sk98lin/skge.c
+++ b/drivers/net/sk98lin/skge.c
@@ -1562,10 +1562,10 @@
 	pTxd->pMBuf     = pMessage;
 
 	if (pMessage->ip_summed == CHECKSUM_PARTIAL) {
-		u16 hdrlen = pMessage->h.raw - pMessage->data;
+		u16 hdrlen = skb_transport_offset(pMessage);
 		u16 offset = hdrlen + pMessage->csum_offset;
 
-		if ((pMessage->h.ipiph->protocol == IPPROTO_UDP ) &&
+		if ((ipip_hdr(pMessage)->protocol == IPPROTO_UDP) &&
 			(pAC->GIni.GIChipRev == 0) &&
 			(pAC->GIni.GIChipId == CHIP_ID_YUKON)) {
 			pTxd->TBControl = BMU_TCP_CHECK;
@@ -1681,7 +1681,7 @@
 	** Does the HW need to evaluate checksum for TCP or UDP packets? 
 	*/
 	if (pMessage->ip_summed == CHECKSUM_PARTIAL) {
-		u16 hdrlen = pMessage->h.raw - pMessage->data;
+		u16 hdrlen = skb_transport_offset(pMessage);
 		u16 offset = hdrlen + pMessage->csum_offset;
 
 		Control = BMU_STFWD;
@@ -1691,7 +1691,7 @@
 		** opcode for udp is not working in the hardware yet 
 		** (Revision 2.0)
 		*/
-		if ((pMessage->h.ipiph->protocol == IPPROTO_UDP ) &&
+		if ((ipip_hdr(pMessage)->protocol == IPPROTO_UDP) &&
 			(pAC->GIni.GIChipRev == 0) &&
 			(pAC->GIni.GIChipId == CHIP_ID_YUKON)) {
 			Control |= BMU_TCP_CHECK;
@@ -2127,7 +2127,7 @@
 						    (dma_addr_t) PhysAddr,
 						    FrameLength,
 						    PCI_DMA_FROMDEVICE);
-			memcpy(pNewMsg->data, pMsg, FrameLength);
+			skb_copy_to_linear_data(pNewMsg, pMsg, FrameLength);
 
 			pci_dma_sync_single_for_device(pAC->PciDev,
 						       (dma_addr_t) PhysAddr,
@@ -2193,7 +2193,6 @@
 				SK_PNMI_CNT_RX_OCTETS_DELIVERED(pAC,
 					FrameLength, pRxPort->PortIndex);
 
-				pMsg->dev = pAC->dev[pRxPort->PortIndex];
 				pMsg->protocol = eth_type_trans(pMsg,
 					pAC->dev[pRxPort->PortIndex]);
 				netif_rx(pMsg);
@@ -2246,7 +2245,6 @@
 				(IFF_PROMISC | IFF_ALLMULTI)) != 0 ||
 				(ForRlmt & SK_RLMT_RX_PROTOCOL) ==
 				SK_RLMT_RX_PROTOCOL) {
-				pMsg->dev = pAC->dev[pRxPort->PortIndex];
 				pMsg->protocol = eth_type_trans(pMsg,
 					pAC->dev[pRxPort->PortIndex]);
 				netif_rx(pMsg);
@@ -5125,7 +5123,12 @@
 
 	pci_set_power_state(pdev, PCI_D0);
 	pci_restore_state(pdev);
-	pci_enable_device(pdev);
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		printk(KERN_WARNING "sk98lin: unable to enable device %s "
+				"in resume\n", dev->name);
+		goto err_out;
+	}
 	pci_set_master(pdev);
 	if (pAC->GIni.GIMacsFound == 2)
 		ret = request_irq(dev->irq, SkGeIsr, IRQF_SHARED, "sk98lin", dev);
@@ -5133,10 +5136,8 @@
 		ret = request_irq(dev->irq, SkGeIsrOnePort, IRQF_SHARED, "sk98lin", dev);
 	if (ret) {
 		printk(KERN_WARNING "sk98lin: unable to acquire IRQ %d\n", dev->irq);
-		pAC->AllocFlag &= ~SK_ALLOC_IRQ;
-		dev->irq = 0;
-		pci_disable_device(pdev);
-		return -EBUSY;
+		ret = -EBUSY;
+		goto err_out_disable_pdev;
 	}
 
 	netif_device_attach(dev);
@@ -5153,6 +5154,13 @@
 	}
 
 	return 0;
+
+err_out_disable_pdev:
+	pci_disable_device(pdev);
+err_out:
+	pAC->AllocFlag &= ~SK_ALLOC_IRQ;
+	dev->irq = 0;
+	return ret;
 }
 #else
 #define skge_suspend NULL
diff --git a/drivers/net/skfp/h/lnkstat.h b/drivers/net/skfp/h/lnkstat.h
deleted file mode 100644
index c73dcd9..0000000
--- a/drivers/net/skfp/h/lnkstat.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/******************************************************************************
- *
- *	(C)Copyright 1998,1999 SysKonnect,
- *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
- *
- *	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 information in this file is provided "AS IS" without warranty.
- *
- ******************************************************************************/
-
-/*
- * Definition of the Error Log Structure
- * This structure will be copied into the Error Log buffer
- * during the NDIS General Request ReadErrorLog by the MAC Driver
- */
-
-struct	s_error_log {
-
-	/*
-	 * place holder for token ring adapter error log (zeros)
-	 */
-	u_char	reserved_0 ;			/* byte 0 inside Error Log */
-	u_char	reserved_1 ;			/* byte 1 */
-	u_char	reserved_2 ;			/* byte 2 */	
-	u_char	reserved_3 ;			/* byte 3 */
-	u_char	reserved_4 ;			/* byte 4 */
-	u_char	reserved_5 ;			/* byte 5 */
-	u_char	reserved_6 ;			/* byte 6 */
-	u_char	reserved_7 ;			/* byte 7 */
-	u_char	reserved_8 ;			/* byte 8 */
-	u_char	reserved_9 ;			/* byte 9 */
-	u_char	reserved_10 ;			/* byte 10 */
-	u_char	reserved_11 ;			/* byte 11 */
-	u_char	reserved_12 ;			/* byte 12 */
-	u_char	reserved_13 ;			/* byte 13 */
-
-	/*
-	 * FDDI link statistics 
-	 */
-/*
- * smt error low
- */
-#define SMT_ERL_AEB	(1<<15)			/* A elast. buffer */
-#define SMT_ERL_BLC	(1<<14)			/* B link error condition */
-#define SMT_ERL_ALC	(1<<13)			/* A link error condition */
-#define SMT_ERL_NCC	(1<<12)			/* not copied condition */
-#define SMT_ERL_FEC	(1<<11)			/* frame error condition */
-
-/*
- * smt event low
- */
-#define SMT_EVL_NCE	(1<<5)
-
-	u_short	smt_error_low ;			/* byte 14/15 */
-	u_short	smt_error_high ;		/* byte 16/17 */
-	u_short	smt_event_low ;			/* byte 18/19 */
-	u_short	smt_event_high ;		/* byte 20/21 */
-	u_short	connection_policy_violation ;	/* byte 22/23 */
-	u_short port_event ;			/* byte 24/25 */
-	u_short set_count_low ;			/* byte 26/27 */
-	u_short set_count_high ;		/* byte 28/29 */
-	u_short	aci_id_code ;			/* byte 30/31 */
-	u_short	purge_frame_counter ;		/* byte 32/33 */
-
-	/*
-	 * CMT and RMT state machines
-	 */
-	u_short	ecm_state ;			/* byte 34/35 */
-	u_short	pcm_a_state ;			/* byte 36/37 */
-	u_short pcm_b_state ;			/* byte 38/39 */
-	u_short	cfm_state ;			/* byte 40/41 */
-	u_short	rmt_state ;			/* byte 42/43 */
-
-	u_short	not_used[30] ;			/* byte 44-103 */
-
-	u_short	ucode_version_level ;		/* byte 104/105 */
-
-	u_short	not_used_1 ;			/* byte 106/107 */
-	u_short not_used_2 ;			/* byte 108/109 */
-} ;
diff --git a/drivers/net/skfp/skfddi.c b/drivers/net/skfp/skfddi.c
index 9733a11..a7ef6c8 100644
--- a/drivers/net/skfp/skfddi.c
+++ b/drivers/net/skfp/skfddi.c
@@ -1680,7 +1680,6 @@
 	rxd->rxd_os.skb = NULL;
 	skb_trim(skb, len);
 	skb->protocol = fddi_type_trans(skb, bp->dev);
-	skb->dev = bp->dev;	/* pass up device pointer */
 
 	netif_rx(skb);
 	bp->dev->last_rx = jiffies;
@@ -1938,7 +1937,7 @@
 	}
 	skb_reserve(skb, 3);
 	skb_put(skb, len);
-	memcpy(skb->data, look_ahead, len);
+	skb_copy_to_linear_data(skb, look_ahead, len);
 
 	// deliver frame to system
 	skb->protocol = fddi_type_trans(skb, smc->os.dev);
diff --git a/drivers/net/skge.c b/drivers/net/skge.c
index d476a3c..21afe10 100644
--- a/drivers/net/skge.c
+++ b/drivers/net/skge.c
@@ -42,7 +42,7 @@
 #include "skge.h"
 
 #define DRV_NAME		"skge"
-#define DRV_VERSION		"1.10"
+#define DRV_VERSION		"1.11"
 #define PFX			DRV_NAME " "
 
 #define DEFAULT_TX_RING_SIZE	128
@@ -2621,6 +2621,7 @@
 
 static inline int skge_avail(const struct skge_ring *ring)
 {
+	smp_mb();
 	return ((ring->to_clean > ring->to_use) ? 0 : ring->count)
 		+ (ring->to_clean - ring->to_use) - 1;
 }
@@ -2654,12 +2655,12 @@
 	td->dma_hi = map >> 32;
 
 	if (skb->ip_summed == CHECKSUM_PARTIAL) {
-		int offset = skb->h.raw - skb->data;
+		const int offset = skb_transport_offset(skb);
 
 		/* This seems backwards, but it is what the sk98lin
 		 * does.  Looks like hardware is wrong?
 		 */
-		if (skb->h.ipiph->protocol == IPPROTO_UDP
+		if (ipip_hdr(skb)->protocol == IPPROTO_UDP
 	            && hw->chip_rev == 0 && hw->chip_id == CHIP_ID_YUKON)
 			control = BMU_TCP_CHECK;
 		else
@@ -2709,6 +2710,8 @@
 		       dev->name, e - skge->tx_ring.start, skb->len);
 
 	skge->tx_ring.to_use = e->next;
+	smp_wmb();
+
 	if (skge_avail(&skge->tx_ring) <= TX_LOW_WATER) {
 		pr_debug("%s: transmit queue full\n", dev->name);
 		netif_stop_queue(dev);
@@ -2726,8 +2729,6 @@
 {
 	struct pci_dev *pdev = skge->hw->pdev;
 
-	BUG_ON(!e->skb);
-
 	/* skb header vs. fragment */
 	if (control & BMU_STF)
 		pci_unmap_single(pdev, pci_unmap_addr(e, mapaddr),
@@ -2745,7 +2746,6 @@
 
 		dev_kfree_skb(e->skb);
 	}
-	e->skb = NULL;
 }
 
 /* Free all buffers in transmit ring */
@@ -2950,7 +2950,7 @@
 		pci_dma_sync_single_for_cpu(skge->hw->pdev,
 					    pci_unmap_addr(e, mapaddr),
 					    len, PCI_DMA_FROMDEVICE);
-		memcpy(skb->data, e->skb->data, len);
+		skb_copy_from_linear_data(e->skb, skb->data, len);
 		pci_dma_sync_single_for_device(skge->hw->pdev,
 					       pci_unmap_addr(e, mapaddr),
 					       len, PCI_DMA_FROMDEVICE);
@@ -3017,21 +3017,29 @@
 
 	skge_write8(skge->hw, Q_ADDR(txqaddr[skge->port], Q_CSR), CSR_IRQ_CL_F);
 
-	netif_tx_lock(dev);
 	for (e = ring->to_clean; e != ring->to_use; e = e->next) {
-		struct skge_tx_desc *td = e->desc;
+		u32 control = ((const struct skge_tx_desc *) e->desc)->control;
 
-		if (td->control & BMU_OWN)
+		if (control & BMU_OWN)
 			break;
 
-		skge_tx_free(skge, e, td->control);
+		skge_tx_free(skge, e, control);
 	}
 	skge->tx_ring.to_clean = e;
 
-	if (skge_avail(&skge->tx_ring) > TX_LOW_WATER)
-		netif_wake_queue(dev);
+	/* Can run lockless until we need to synchronize to restart queue. */
+	smp_mb();
 
-	netif_tx_unlock(dev);
+	if (unlikely(netif_queue_stopped(dev) &&
+		     skge_avail(&skge->tx_ring) > TX_LOW_WATER)) {
+		netif_tx_lock(dev);
+		if (unlikely(netif_queue_stopped(dev) &&
+			     skge_avail(&skge->tx_ring) > TX_LOW_WATER)) {
+			netif_wake_queue(dev);
+
+		}
+		netif_tx_unlock(dev);
+	}
 }
 
 static int skge_poll(struct net_device *dev, int *budget)
diff --git a/drivers/net/skge.h b/drivers/net/skge.h
index 86467ae..edd7146 100644
--- a/drivers/net/skge.h
+++ b/drivers/net/skge.h
@@ -232,7 +232,6 @@
 	IS_R2_PAR_ERR	= 1<<0,	/* Queue R2 Parity Error */
 
 	IS_ERR_MSK	= IS_IRQ_MST_ERR | IS_IRQ_STAT
-			| IS_NO_STAT_M1 | IS_NO_STAT_M2
 			| IS_RAM_RD_PAR | IS_RAM_WR_PAR
 			| IS_M1_PAR_ERR | IS_M2_PAR_ERR
 			| IS_R1_PAR_ERR | IS_R2_PAR_ERR,
@@ -2447,15 +2446,15 @@
 
 
 struct skge_port {
-	u32		     msg_enable;
 	struct skge_hw	     *hw;
 	struct net_device    *netdev;
 	int		     port;
+	u32		     msg_enable;
 
 	struct skge_ring     tx_ring;
-	struct skge_ring     rx_ring;
 
-	struct net_device_stats net_stats;
+	struct skge_ring     rx_ring ____cacheline_aligned_in_smp;
+	unsigned int	     rx_buf_size;
 
 	struct timer_list    link_timer;
 	enum pause_control   flow_control;
@@ -2471,7 +2470,8 @@
 	void		     *mem;	/* PCI memory for rings */
 	dma_addr_t	     dma;
 	unsigned long	     mem_size;
-	unsigned int	     rx_buf_size;
+
+	struct net_device_stats net_stats;
 };
 
 
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
index ac36152..238c2ca 100644
--- a/drivers/net/sky2.c
+++ b/drivers/net/sky2.c
@@ -32,6 +32,7 @@
 #include <linux/ethtool.h>
 #include <linux/pci.h>
 #include <linux/ip.h>
+#include <net/ip.h>
 #include <linux/tcp.h>
 #include <linux/in.h>
 #include <linux/delay.h>
@@ -1391,8 +1392,8 @@
 	/* Check for TCP Segmentation Offload */
 	mss = skb_shinfo(skb)->gso_size;
 	if (mss != 0) {
-		mss += ((skb->h.th->doff - 5) * 4);	/* TCP options */
-		mss += (skb->nh.iph->ihl * 4) + sizeof(struct tcphdr);
+		mss += tcp_optlen(skb); /* TCP options */
+		mss += ip_hdrlen(skb) + sizeof(struct tcphdr);
 		mss += ETH_HLEN;
 
 		if (mss != sky2->tx_last_mss) {
@@ -1420,14 +1421,14 @@
 
 	/* Handle TCP checksum offload */
 	if (skb->ip_summed == CHECKSUM_PARTIAL) {
-		unsigned offset = skb->h.raw - skb->data;
+		const unsigned offset = skb_transport_offset(skb);
 		u32 tcpsum;
 
 		tcpsum = offset << 16;		/* sum start */
 		tcpsum |= offset + skb->csum_offset;	/* sum write */
 
 		ctrl = CALSUM | WR_SUM | INIT_SUM | LOCK_SUM;
-		if (skb->nh.iph->protocol == IPPROTO_UDP)
+		if (ip_hdr(skb)->protocol == IPPROTO_UDP)
 			ctrl |= UDPTCP;
 
 		if (tcpsum != sky2->tx_tcpsum) {
@@ -1970,7 +1971,7 @@
 		skb_reserve(skb, 2);
 		pci_dma_sync_single_for_cpu(sky2->hw->pdev, re->data_addr,
 					    length, PCI_DMA_FROMDEVICE);
-		memcpy(skb->data, re->skb->data, length);
+		skb_copy_from_linear_data(re->skb, skb->data, length);
 		skb->ip_summed = re->skb->ip_summed;
 		skb->csum = re->skb->csum;
 		pci_dma_sync_single_for_device(sky2->hw->pdev, re->data_addr,
diff --git a/drivers/net/slip.c b/drivers/net/slip.c
index 2f4b1de..65bd20f 100644
--- a/drivers/net/slip.c
+++ b/drivers/net/slip.c
@@ -363,7 +363,7 @@
 	}
 	skb->dev = sl->dev;
 	memcpy(skb_put(skb,count), sl->rbuff, count);
-	skb->mac.raw=skb->data;
+	skb_reset_mac_header(skb);
 	skb->protocol=htons(ETH_P_IP);
 	netif_rx(skb);
 	sl->dev->last_rx = jiffies;
diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c
index c956141..81f2484 100644
--- a/drivers/net/smc911x.c
+++ b/drivers/net/smc911x.c
@@ -499,10 +499,9 @@
 		SMC_SET_RX_CFG(RX_CFG_RX_END_ALGN4_ | ((2<<8) & RX_CFG_RXDOFF_));
 		SMC_PULL_DATA(data, pkt_len+2+3);
 
-		DBG(SMC_DEBUG_PKTS, "%s: Received packet\n", dev->name,);
+		DBG(SMC_DEBUG_PKTS, "%s: Received packet\n", dev->name);
 		PRINT_PKT(data, ((pkt_len - 4) <= 64) ? pkt_len - 4 : 64);
 		dev->last_rx = jiffies;
-		skb->dev = dev;
 		skb->protocol = eth_type_trans(skb, dev);
 		netif_rx(skb);
 		lp->stats.rx_packets++;
@@ -1307,7 +1306,6 @@
 	lp->current_rx_skb = NULL;
 	PRINT_PKT(skb->data, skb->len);
 	dev->last_rx = jiffies;
-	skb->dev = dev;
 	skb->protocol = eth_type_trans(skb, dev);
 	netif_rx(skb);
 	lp->stats.rx_packets++;
diff --git a/drivers/net/smc9194.c b/drivers/net/smc9194.c
index bd6e845..36c1eba 100644
--- a/drivers/net/smc9194.c
+++ b/drivers/net/smc9194.c
@@ -1262,7 +1262,6 @@
 
 		skb_reserve( skb, 2 );   /* 16 bit alignment */
 
-		skb->dev = dev;
 		data = skb_put( skb, packet_length);
 
 #ifdef USE_32_BIT
diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c
index 49f4b77..01cc3c7 100644
--- a/drivers/net/smc91x.c
+++ b/drivers/net/smc91x.c
@@ -568,7 +568,6 @@
 		PRINT_PKT(data, packet_len - 4);
 
 		dev->last_rx = jiffies;
-		skb->dev = dev;
 		skb->protocol = eth_type_trans(skb, dev);
 		netif_rx(skb);
 		lp->stats.rx_packets++;
diff --git a/drivers/net/sonic.c b/drivers/net/sonic.c
index ed7aa0a..c6320c7 100644
--- a/drivers/net/sonic.c
+++ b/drivers/net/sonic.c
@@ -85,7 +85,6 @@
 			       dev->name);
 			return -ENOMEM;
 		}
-		skb->dev = dev;
 		/* align IP header unless DMA requires otherwise */
 		if (SONIC_BUS_SCALE(lp->dma_bitmode) == 2)
 			skb_reserve(skb, 2);
@@ -451,7 +450,6 @@
 				lp->stats.rx_dropped++;
 				break;
 			}
-			new_skb->dev = dev;
 			/* provide 16 byte IP header alignment unless DMA requires otherwise */
 			if(SONIC_BUS_SCALE(lp->dma_bitmode) == 2)
 				skb_reserve(new_skb, 2);
diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c
index e3019d5..230da14 100644
--- a/drivers/net/spider_net.c
+++ b/drivers/net/spider_net.c
@@ -720,7 +720,7 @@
 	spin_unlock_irqrestore(&chain->lock, flags);
 
 	if (skb->protocol == htons(ETH_P_IP) && skb->ip_summed == CHECKSUM_PARTIAL)
-		switch (skb->nh.iph->protocol) {
+		switch (ip_hdr(skb)->protocol) {
 		case IPPROTO_TCP:
 			hwdescr->dmac_cmd_status |= SPIDER_NET_DMAC_TCP;
 			break;
@@ -990,7 +990,6 @@
 	netdev = card->netdev;
 
 	skb = descr->skb;
-	skb->dev = netdev;
 	skb_put(skb, hwdescr->valid_size);
 
 	/* the card seems to add 2 bytes of junk in front
diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c
index 8bba2e3..9d6e454 100644
--- a/drivers/net/starfire.c
+++ b/drivers/net/starfire.c
@@ -1452,7 +1452,6 @@
 		   to a minimally-sized skbuff. */
 		if (pkt_len < rx_copybreak
 		    && (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_for_cpu(np->pci_dev,
 						    np->rx_info[entry].mapping,
diff --git a/drivers/net/sun3_82586.c b/drivers/net/sun3_82586.c
index 4757aa6..396c3d9 100644
--- a/drivers/net/sun3_82586.c
+++ b/drivers/net/sun3_82586.c
@@ -775,7 +775,6 @@
 					skb = (struct sk_buff *) dev_alloc_skb(totlen+2);
 					if(skb != NULL)
 					{
-						skb->dev = dev;
 						skb_reserve(skb,2);
 						skb_put(skb,totlen);
 						eth_copy_and_sum(skb,(char *) p->base+swab32((unsigned long) rbd->buffer),totlen,0);
@@ -1027,7 +1026,7 @@
 			memset((char *)p->xmit_cbuffs[p->xmit_count], 0, ETH_ZLEN);
 			len = ETH_ZLEN;
 		}
-		memcpy((char *)p->xmit_cbuffs[p->xmit_count],(char *)(skb->data),skb->len);
+		skb_copy_from_linear_data(skb, p->xmit_cbuffs[p->xmit_count], skb->len);
 
 #if (NUM_XMIT_BUFFS == 1)
 #	ifdef NO_NOPCOMMANDS
diff --git a/drivers/net/sun3lance.c b/drivers/net/sun3lance.c
index 7bee45b..791e081 100644
--- a/drivers/net/sun3lance.c
+++ b/drivers/net/sun3lance.c
@@ -629,7 +629,7 @@
 	head->length = (-len) | 0xf000;
 	head->misc = 0;
 
-	memcpy( PKTBUF_ADDR(head), (void *)skb->data, skb->len );
+	skb_copy_from_linear_data(skb, PKTBUF_ADDR(head), skb->len);
 	if (len != skb->len)
 		memset(PKTBUF_ADDR(head) + skb->len, 0, len-skb->len);
 
@@ -851,10 +851,9 @@
 				}
 
 
-				skb->dev = dev;
 				skb_reserve( skb, 2 );	/* 16 byte align */
 				skb_put( skb, pkt_len );	/* Make room */
-//			        memcpy( skb->data, PKTBUF_ADDR(head), pkt_len );
+//			        skb_copy_to_linear_data(skb, PKTBUF_ADDR(head), pkt_len);
 				eth_copy_and_sum(skb,
 						 PKTBUF_ADDR(head),
 						 pkt_len, 0);
diff --git a/drivers/net/sunbmac.c b/drivers/net/sunbmac.c
index 18f8885..2ad8d58 100644
--- a/drivers/net/sunbmac.c
+++ b/drivers/net/sunbmac.c
@@ -855,7 +855,6 @@
 				drops++;
 				goto drop_it;
 			}
-			copy_skb->dev = bp->dev;
 			skb_reserve(copy_skb, 2);
 			skb_put(copy_skb, len);
 			sbus_dma_sync_single_for_cpu(bp->bigmac_sdev,
diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c
index c06ecc8..f51ba31 100644
--- a/drivers/net/sundance.c
+++ b/drivers/net/sundance.c
@@ -1308,7 +1308,6 @@
 			   to a minimally-sized skbuff. */
 			if (pkt_len < rx_copybreak
 				&& (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_for_cpu(np->pci_dev,
 							    desc->frag[0].addr,
diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c
index 08ea61d..5da7321 100644
--- a/drivers/net/sungem.c
+++ b/drivers/net/sungem.c
@@ -64,11 +64,9 @@
 #include <asm/uaccess.h>
 #include <asm/irq.h>
 
-#ifdef __sparc__
+#ifdef CONFIG_SPARC
 #include <asm/idprom.h>
-#include <asm/openprom.h>
-#include <asm/oplib.h>
-#include <asm/pbm.h>
+#include <asm/prom.h>
 #endif
 
 #ifdef CONFIG_PPC_PMAC
@@ -845,11 +843,10 @@
 				goto drop_it;
 			}
 
-			copy_skb->dev = gp->dev;
 			skb_reserve(copy_skb, 2);
 			skb_put(copy_skb, len);
 			pci_dma_sync_single_for_cpu(gp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE);
-			memcpy(copy_skb->data, skb->data, len);
+			skb_copy_from_linear_data(skb, copy_skb->data, len);
 			pci_dma_sync_single_for_device(gp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE);
 
 			/* We'll reuse the original ring buffer. */
@@ -1029,10 +1026,8 @@
 
 	ctrl = 0;
 	if (skb->ip_summed == CHECKSUM_PARTIAL) {
-		u64 csum_start_off, csum_stuff_off;
-
-		csum_start_off = (u64) (skb->h.raw - skb->data);
-		csum_stuff_off = csum_start_off + skb->csum_offset;
+		const u64 csum_start_off = skb_transport_offset(skb);
+		const u64 csum_stuff_off = csum_start_off + skb->csum_offset;
 
 		ctrl = (TXDCTRL_CENAB |
 			(csum_start_off << 15) |
@@ -2849,7 +2844,7 @@
 	return rc;
 }
 
-#if (!defined(__sparc__) && !defined(CONFIG_PPC_PMAC))
+#if (!defined(CONFIG_SPARC) && !defined(CONFIG_PPC_PMAC))
 /* Fetch MAC address from vital product data of PCI ROM. */
 static int find_eth_addr_in_vpd(void __iomem *rom_base, int len, unsigned char *dev_addr)
 {
@@ -2904,36 +2899,19 @@
 
 static int __devinit gem_get_device_address(struct gem *gp)
 {
-#if defined(__sparc__) || defined(CONFIG_PPC_PMAC)
+#if defined(CONFIG_SPARC) || defined(CONFIG_PPC_PMAC)
 	struct net_device *dev = gp->dev;
-#endif
-
-#if defined(__sparc__)
-	struct pci_dev *pdev = gp->pdev;
-	struct pcidev_cookie *pcp = pdev->sysdata;
-	int use_idprom = 1;
-
-	if (pcp != NULL) {
-		unsigned char *addr;
-		int len;
-
-		addr = of_get_property(pcp->prom_node, "local-mac-address",
-				       &len);
-		if (addr && len == 6) {
-			use_idprom = 0;
-			memcpy(dev->dev_addr, addr, 6);
-		}
-	}
-	if (use_idprom)
-		memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
-#elif defined(CONFIG_PPC_PMAC)
 	const unsigned char *addr;
 
 	addr = get_property(gp->of_node, "local-mac-address", NULL);
 	if (addr == NULL) {
+#ifdef CONFIG_SPARC
+		addr = idprom->id_ethaddr;
+#else
 		printk("\n");
 		printk(KERN_ERR "%s: can't get mac-address\n", dev->name);
 		return -1;
+#endif
 	}
 	memcpy(dev->dev_addr, addr, 6);
 #else
@@ -3091,7 +3069,7 @@
 	/* On Apple, we want a reference to the Open Firmware device-tree
 	 * node. We use it for clock control.
 	 */
-#ifdef CONFIG_PPC_PMAC
+#if defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC)
 	gp->of_node = pci_device_to_OF_node(pdev);
 #endif
 
diff --git a/drivers/net/sungem.h b/drivers/net/sungem.h
index a70067c..58cf87c 100644
--- a/drivers/net/sungem.h
+++ b/drivers/net/sungem.h
@@ -1025,7 +1025,7 @@
 
 	struct pci_dev		*pdev;
 	struct net_device	*dev;
-#ifdef CONFIG_PPC_PMAC
+#if defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC)
 	struct device_node	*of_node;
 #endif
 };
diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c
index 192bbc9..51c3fe2 100644
--- a/drivers/net/sunhme.c
+++ b/drivers/net/sunhme.c
@@ -55,9 +55,6 @@
 
 #ifdef CONFIG_PCI
 #include <linux/pci.h>
-#ifdef CONFIG_SPARC
-#include <asm/pbm.h>
-#endif
 #endif
 
 #include "sunhme.h"
@@ -2058,11 +2055,10 @@
 				goto drop_it;
 			}
 
-			copy_skb->dev = dev;
 			skb_reserve(copy_skb, 2);
 			skb_put(copy_skb, len);
 			hme_dma_sync_for_cpu(hp, dma_addr, len, DMA_FROMDEVICE);
-			memcpy(copy_skb->data, skb->data, len);
+			skb_copy_from_linear_data(skb, copy_skb->data, len);
 			hme_dma_sync_for_device(hp, dma_addr, len, DMA_FROMDEVICE);
 
 			/* Reuse original ring buffer. */
@@ -2270,10 +2266,8 @@
 
 	tx_flags = TXFLAG_OWN;
 	if (skb->ip_summed == CHECKSUM_PARTIAL) {
-		u32 csum_start_off, csum_stuff_off;
-
-		csum_start_off = (u32) (skb->h.raw - skb->data);
-		csum_stuff_off = csum_start_off + skb->csum_offset;
+		const u32 csum_start_off = skb_transport_offset(skb);
+		const u32 csum_stuff_off = csum_start_off + skb->csum_offset;
 
 		tx_flags = (TXFLAG_OWN | TXFLAG_CSENABLE |
 			    ((csum_start_off << 14) & TXFLAG_CSBUFBEGIN) |
@@ -2704,7 +2698,7 @@
 			dev->dev_addr[i] = macaddr[i];
 		macaddr[5]++;
 	} else {
-		unsigned char *addr;
+		const unsigned char *addr;
 		int len;
 
 		addr = of_get_property(dp, "local-mac-address", &len);
@@ -2986,7 +2980,7 @@
 {
 	struct quattro *qp = NULL;
 #ifdef CONFIG_SPARC
-	struct pcidev_cookie *pcp;
+	struct device_node *dp;
 #endif
 	struct happy_meal *hp;
 	struct net_device *dev;
@@ -2998,13 +2992,8 @@
 
 	/* Now make sure pci_dev cookie is there. */
 #ifdef CONFIG_SPARC
-	pcp = pdev->sysdata;
-	if (pcp == NULL) {
-		printk(KERN_ERR "happymeal(PCI): Some PCI device info missing\n");
-		return -ENODEV;
-	}
-
-	strcpy(prom_name, pcp->prom_node->name);
+	dp = pci_device_to_OF_node(pdev);
+	strcpy(prom_name, dp->name);
 #else
 	if (is_quattro_p(pdev))
 		strcpy(prom_name, "SUNW,qfe");
@@ -3081,11 +3070,11 @@
 		macaddr[5]++;
 	} else {
 #ifdef CONFIG_SPARC
-		unsigned char *addr;
+		const unsigned char *addr;
 		int len;
 
 		if (qfe_slot != -1 &&
-		    (addr = of_get_property(pcp->prom_node,
+		    (addr = of_get_property(dp,
 					    "local-mac-address", &len)) != NULL
 		    && len == 6) {
 			memcpy(dev->dev_addr, addr, 6);
@@ -3105,7 +3094,7 @@
 	hp->tcvregs    = (hpreg_base + 0x7000UL);
 
 #ifdef CONFIG_SPARC
-	hp->hm_revision = of_getintprop_default(pcp->prom_node, "hm-rev", 0xff);
+	hp->hm_revision = of_getintprop_default(dp, "hm-rev", 0xff);
 	if (hp->hm_revision == 0xff) {
 		unsigned char prev;
 
@@ -3300,7 +3289,7 @@
 {
 	struct sbus_dev *sdev = to_sbus_device(&dev->dev);
 	struct device_node *dp = dev->node;
-	char *model = of_get_property(dp, "model", NULL);
+	const char *model = of_get_property(dp, "model", NULL);
 	int is_qfe = (match->data != NULL);
 
 	if (!is_qfe && model && !strcmp(model, "SUNW,sbus-qfe"))
diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c
index b0929a4..4272253 100644
--- a/drivers/net/sunlance.c
+++ b/drivers/net/sunlance.c
@@ -547,7 +547,6 @@
 
 			lp->stats.rx_bytes += len;
 
-			skb->dev = dev;
 			skb_reserve(skb, 2);		/* 16 byte align */
 			skb_put(skb, len);		/* make room */
 			eth_copy_and_sum(skb,
@@ -721,7 +720,6 @@
 
 			lp->stats.rx_bytes += len;
 
-			skb->dev = dev;
 			skb_reserve (skb, 2);		/* 16 byte align */
 			skb_put(skb, len);		/* make room */
 			lance_piocopy_to_skb(skb, &(ib->rx_buf[entry][0]), len);
@@ -1145,7 +1143,7 @@
 		struct lance_init_block *ib = lp->init_block_mem;
 		ib->btx_ring [entry].length = (-len) | 0xf000;
 		ib->btx_ring [entry].misc = 0;
-		memcpy((char *)&ib->tx_buf [entry][0], skb->data, skblen);
+		skb_copy_from_linear_data(skb, &ib->tx_buf [entry][0], skblen);
 		if (len != skblen)
 			memset((char *) &ib->tx_buf [entry][skblen], 0, len - skblen);
 		ib->btx_ring [entry].tmd1_bits = (LE_T1_POK | LE_T1_OWN);
diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c
index f3bad56..fa70e0b 100644
--- a/drivers/net/sunqe.c
+++ b/drivers/net/sunqe.c
@@ -437,7 +437,6 @@
 				drops++;
 				qep->net_stats.rx_dropped++;
 			} else {
-				skb->dev = qep->dev;
 				skb_reserve(skb, 2);
 				skb_put(skb, len);
 				eth_copy_and_sum(skb, (unsigned char *) this_qbuf,
@@ -593,7 +592,7 @@
 	/* Avoid a race... */
 	qep->qe_block->qe_txd[entry].tx_flags = TXD_UPDATE;
 
-	memcpy(txbuf, skb->data, len);
+	skb_copy_from_linear_data(skb, txbuf, len);
 
 	qep->qe_block->qe_txd[entry].tx_addr = txbuf_dvma;
 	qep->qe_block->qe_txd[entry].tx_flags =
diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c
index e3a7e3c..f1e2dfc 100644
--- a/drivers/net/tc35815.c
+++ b/drivers/net/tc35815.c
@@ -1,35 +1,34 @@
-/* tc35815.c: A TOSHIBA TC35815CF PCI 10/100Mbps ethernet driver for linux.
- *
- * Copyright 2001 MontaVista Software Inc.
- * Author: MontaVista Software, Inc.
- *                ahennessy@mvista.com
+/*
+ * tc35815.c: A TOSHIBA TC35815CF PCI 10/100Mbps ethernet driver for linux.
  *
  * Based on skelton.c by Donald Becker.
- * Copyright (C) 2000-2001 Toshiba Corporation
  *
- * 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 driver is a replacement of older and less maintained version.
+ * This is a header of the older version:
+ *	-----<snip>-----
+ *	Copyright 2001 MontaVista Software Inc.
+ *	Author: MontaVista Software, Inc.
+ *		ahennessy@mvista.com
+ *	Copyright (C) 2000-2001 Toshiba Corporation
+ *	static const char *version =
+ *		"tc35815.c:v0.00 26/07/2000 by Toshiba Corporation\n";
+ *	-----<snip>-----
  *
- * THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
- * WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
- * NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
- * USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
  *
- * 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.
+ * (C) Copyright TOSHIBA CORPORATION 2004-2005
+ * All Rights Reserved.
  */
 
-static const char *version =
-	"tc35815.c:v0.00 26/07/2000 by Toshiba Corporation\n";
+#ifdef TC35815_NAPI
+#define DRV_VERSION	"1.35-NAPI"
+#else
+#define DRV_VERSION	"1.35"
+#endif
+static const char *version = "tc35815.c:v" DRV_VERSION "\n";
+#define MODNAME			"tc35815"
 
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -40,6 +39,7 @@
 #include <linux/in.h>
 #include <linux/slab.h>
 #include <linux/string.h>
+#include <linux/spinlock.h>
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/netdevice.h>
@@ -47,36 +47,47 @@
 #include <linux/skbuff.h>
 #include <linux/delay.h>
 #include <linux/pci.h>
-#include <linux/proc_fs.h>
-#include <linux/spinlock.h>
-#include <linux/bitops.h>
-
-#include <asm/system.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
 #include <asm/io.h>
-#include <asm/dma.h>
 #include <asm/byteorder.h>
 
-/*
- * The name of the card. Is used for messages and in the requests for
- * io regions, irqs and dma channels
- */
-static const char* cardname = "TC35815CF";
-#define TC35815_PROC_ENTRY "net/tc35815"
-
-#define TC35815_MODULE_NAME "TC35815CF"
-#define TX_TIMEOUT (4*HZ)
-
 /* First, a few definitions that the brave might change. */
 
-/* use 0 for production, 1 for verification, >2 for debug */
-#ifndef TC35815_DEBUG
-#define TC35815_DEBUG 1
-#endif
-static unsigned int tc35815_debug = TC35815_DEBUG;
-
 #define GATHER_TXINT	/* On-Demand Tx Interrupt */
+#define WORKAROUND_LOSTCAR
+#define WORKAROUND_100HALF_PROMISC
+/* #define TC35815_USE_PACKEDBUFFER */
 
-#define vtonocache(p)	KSEG1ADDR(virt_to_phys(p))
+typedef enum {
+	TC35815CF = 0,
+	TC35815_NWU,
+	TC35815_TX4939,
+} board_t;
+
+/* indexed by board_t, above */
+static const struct {
+	const char *name;
+} board_info[] __devinitdata = {
+	{ "TOSHIBA TC35815CF 10/100BaseTX" },
+	{ "TOSHIBA TC35815 with Wake on LAN" },
+	{ "TOSHIBA TC35815/TX4939" },
+};
+
+static const struct pci_device_id tc35815_pci_tbl[] = {
+	{PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC35815CF), .driver_data = TC35815CF },
+	{PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC35815_NWU), .driver_data = TC35815_NWU },
+	{PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC35815_TX4939), .driver_data = TC35815_TX4939 },
+	{0,}
+};
+MODULE_DEVICE_TABLE (pci, tc35815_pci_tbl);
+
+/* see MODULE_PARM_DESC */
+static struct tc35815_options {
+	int speed;
+	int duplex;
+	int doforce;
+} options;
 
 /*
  * Registers
@@ -119,6 +130,11 @@
  * Bit assignments
  */
 /* DMA_Ctl bit asign ------------------------------------------------------- */
+#define DMA_RxAlign            0x00c00000 /* 1:Reception Alignment           */
+#define DMA_RxAlign_1          0x00400000
+#define DMA_RxAlign_2          0x00800000
+#define DMA_RxAlign_3          0x00c00000
+#define DMA_M66EnStat          0x00080000 /* 1:66MHz Enable State            */
 #define DMA_IntMask            0x00040000 /* 1:Interupt mask                 */
 #define DMA_SWIntReq           0x00020000 /* 1:Software Interrupt request    */
 #define DMA_TxWakeUp           0x00010000 /* 1:Transmit Wake Up              */
@@ -269,42 +285,6 @@
 #define MD_CA_Wr               0x00000400 /* 1:Write 0:Read                  */
 
 
-/* MII register offsets */
-#define MII_CONTROL             0x0000
-#define MII_STATUS              0x0001
-#define MII_PHY_ID0             0x0002
-#define MII_PHY_ID1             0x0003
-#define MII_ANAR                0x0004
-#define MII_ANLPAR              0x0005
-#define MII_ANER                0x0006
-/* MII Control register bit definitions. */
-#define MIICNTL_FDX             0x0100
-#define MIICNTL_RST_AUTO        0x0200
-#define MIICNTL_ISOLATE         0x0400
-#define MIICNTL_PWRDWN          0x0800
-#define MIICNTL_AUTO            0x1000
-#define MIICNTL_SPEED           0x2000
-#define MIICNTL_LPBK            0x4000
-#define MIICNTL_RESET           0x8000
-/* MII Status register bit significance. */
-#define MIISTAT_EXT             0x0001
-#define MIISTAT_JAB             0x0002
-#define MIISTAT_LINK            0x0004
-#define MIISTAT_CAN_AUTO        0x0008
-#define MIISTAT_FAULT           0x0010
-#define MIISTAT_AUTO_DONE       0x0020
-#define MIISTAT_CAN_T           0x0800
-#define MIISTAT_CAN_T_FDX       0x1000
-#define MIISTAT_CAN_TX          0x2000
-#define MIISTAT_CAN_TX_FDX      0x4000
-#define MIISTAT_CAN_T4          0x8000
-/* MII Auto-Negotiation Expansion/RemoteEnd Register Bits */
-#define MII_AN_TX_FDX           0x0100
-#define MII_AN_TX_HDX           0x0080
-#define MII_AN_10_FDX           0x0040
-#define MII_AN_10_HDX           0x0020
-
-
 /*
  * Descriptors
  */
@@ -352,32 +332,51 @@
 
 #ifdef NO_CHECK_CARRIER
 #define TX_CTL_CMD	(Tx_EnComp | Tx_EnTxPar | Tx_EnLateColl | \
-	Tx_EnExColl | Tx_EnLCarr | Tx_EnExDefer | Tx_EnUnder | \
-	Tx_En)	/* maybe  0x7d01 */
+	Tx_EnExColl | Tx_EnExDefer | Tx_EnUnder | \
+	Tx_En)	/* maybe  0x7b01 */
 #else
 #define TX_CTL_CMD	(Tx_EnComp | Tx_EnTxPar | Tx_EnLateColl | \
-	Tx_EnExColl | Tx_EnExDefer | Tx_EnUnder | \
-	Tx_En)	/* maybe  0x7f01 */
+	Tx_EnExColl | Tx_EnLCarr | Tx_EnExDefer | Tx_EnUnder | \
+	Tx_En)	/* maybe  0x7b01 */
 #endif
 #define RX_CTL_CMD	(Rx_EnGood | Rx_EnRxPar | Rx_EnLongErr | Rx_EnOver \
 	| Rx_EnCRCErr | Rx_EnAlign | Rx_RxEn)	/* maybe 0x6f01 */
-
 #define INT_EN_CMD  (Int_NRAbtEn | \
-	 Int_DParDEn | Int_DParErrEn | \
+	Int_DmParErrEn | Int_DParDEn | Int_DParErrEn | \
 	Int_SSysErrEn  | Int_RMasAbtEn | Int_RTargAbtEn | \
 	Int_STargAbtEn | \
 	Int_BLExEn  | Int_FDAExEn) /* maybe 0xb7f*/
+#define DMA_CTL_CMD	DMA_BURST_SIZE
+#define HAVE_DMA_RXALIGN(lp)	likely((lp)->boardtype != TC35815CF)
 
 /* Tuning parameters */
 #define DMA_BURST_SIZE	32
 #define TX_THRESHOLD	1024
+#define TX_THRESHOLD_MAX 1536       /* used threshold with packet max byte for low pci transfer ability.*/
+#define TX_THRESHOLD_KEEP_LIMIT 10  /* setting threshold max value when overrun error occured this count. */
 
+/* 16 + RX_BUF_NUM * 8 + RX_FD_NUM * 16 + TX_FD_NUM * 32 <= PAGE_SIZE*FD_PAGE_NUM */
+#ifdef TC35815_USE_PACKEDBUFFER
 #define FD_PAGE_NUM 2
-#define FD_PAGE_ORDER 1
-/* 16 + RX_BUF_PAGES * 8 + RX_FD_NUM * 16 + TX_FD_NUM * 32 <= PAGE_SIZE*2 */
-#define RX_BUF_PAGES	8	/* >= 2 */
+#define RX_BUF_NUM	8	/* >= 2 */
 #define RX_FD_NUM	250	/* >= 32 */
 #define TX_FD_NUM	128
+#define RX_BUF_SIZE	PAGE_SIZE
+#else /* TC35815_USE_PACKEDBUFFER */
+#define FD_PAGE_NUM 4
+#define RX_BUF_NUM	128	/* < 256 */
+#define RX_FD_NUM	256	/* >= 32 */
+#define TX_FD_NUM	128
+#if RX_CTL_CMD & Rx_LongEn
+#define RX_BUF_SIZE	PAGE_SIZE
+#elif RX_CTL_CMD & Rx_StripCRC
+#define RX_BUF_SIZE	ALIGN(ETH_FRAME_LEN + 4 + 2, 32) /* +2: reserve */
+#else
+#define RX_BUF_SIZE	ALIGN(ETH_FRAME_LEN + 2, 32) /* +2: reserve */
+#endif
+#endif /* TC35815_USE_PACKEDBUFFER */
+#define RX_FD_RESERVE	(2 / 2)	/* max 2 BD per RxFD */
+#define NAPI_WEIGHT	16
 
 struct TxFD {
 	struct FDesc fd;
@@ -392,18 +391,27 @@
 
 struct FrFD {
 	struct FDesc fd;
-	struct BDesc bd[RX_BUF_PAGES];
+	struct BDesc bd[RX_BUF_NUM];
 };
 
 
-extern unsigned long tc_readl(volatile __u32 *addr);
-extern void tc_writel(unsigned long data, volatile __u32 *addr);
+#define tc_readl(addr)	readl(addr)
+#define tc_writel(d, addr)	writel(d, addr)
 
-dma_addr_t priv_dma_handle;
+#define TC35815_TX_TIMEOUT  msecs_to_jiffies(400)
+
+/* Timer state engine. */
+enum tc35815_timer_state {
+	arbwait  = 0,	/* Waiting for auto negotiation to complete.          */
+	lupwait  = 1,	/* Auto-neg complete, awaiting link-up status.        */
+	ltrywait = 2,	/* Forcing try of all modes, from fastest to slowest. */
+	asleep   = 3,	/* Time inactive.                                     */
+	lcheck   = 4,	/* Check link status.                                 */
+};
 
 /* Information that need to be kept for each board. */
 struct tc35815_local {
-	struct net_device *next_module;
+	struct pci_dev *pci_dev;
 
 	/* statistics */
 	struct net_device_stats stats;
@@ -411,151 +419,192 @@
 		int max_tx_qlen;
 		int tx_ints;
 		int rx_ints;
+	        int tx_underrun;
 	} lstats;
 
-	int tbusy;
-	int option;
-#define TC35815_OPT_AUTO	0x00
-#define TC35815_OPT_10M	0x01
-#define TC35815_OPT_100M	0x02
-#define TC35815_OPT_FULLDUP	0x04
-	int linkspeed;	/* 10 or 100 */
+	/* Tx control lock.  This protects the transmit buffer ring
+	 * state along with the "tx full" state of the driver.  This
+	 * means all netif_queue flow control actions are protected
+	 * by this lock as well.
+	 */
+	spinlock_t lock;
+
+	int phy_addr;
 	int fullduplex;
+	unsigned short saved_lpa;
+	struct timer_list timer;
+	enum tc35815_timer_state timer_state; /* State of auto-neg timer. */
+	unsigned int timer_ticks;	/* Number of clicks at each state  */
 
 	/*
 	 * Transmitting: Batch Mode.
 	 *	1 BD in 1 TxFD.
-	 * Receiving: Packing Mode.
+	 * Receiving: Packing Mode. (TC35815_USE_PACKEDBUFFER)
 	 *	1 circular FD for Free Buffer List.
-	 *	RX_BUG_PAGES BD in Free Buffer FD.
+	 *	RX_BUF_NUM BD in Free Buffer FD.
 	 *	One Free Buffer BD has PAGE_SIZE data buffer.
+	 * Or Non-Packing Mode.
+	 *	1 circular FD for Free Buffer List.
+	 *	RX_BUF_NUM BD in Free Buffer FD.
+	 *	One Free Buffer BD has ETH_FRAME_LEN data buffer.
 	 */
-        struct pci_dev *pdev;
-	dma_addr_t fd_buf_dma_handle;
-	void * fd_buf;	/* for TxFD, TxFD, FrFD */
+	void * fd_buf;	/* for TxFD, RxFD, FrFD */
+	dma_addr_t fd_buf_dma;
 	struct TxFD *tfd_base;
-	int tfd_start;
-	int tfd_end;
+	unsigned int tfd_start;
+	unsigned int tfd_end;
 	struct RxFD *rfd_base;
 	struct RxFD *rfd_limit;
 	struct RxFD *rfd_cur;
 	struct FrFD *fbl_ptr;
+#ifdef TC35815_USE_PACKEDBUFFER
 	unsigned char fbl_curid;
-	dma_addr_t data_buf_dma_handle[RX_BUF_PAGES];
-	void * data_buf[RX_BUF_PAGES];		/* packing */
-	spinlock_t lock;
+	void * data_buf[RX_BUF_NUM];		/* packing */
+	dma_addr_t data_buf_dma[RX_BUF_NUM];
+	struct {
+		struct sk_buff *skb;
+		dma_addr_t skb_dma;
+	} tx_skbs[TX_FD_NUM];
+#else
+	unsigned int fbl_count;
+	struct {
+		struct sk_buff *skb;
+		dma_addr_t skb_dma;
+	} tx_skbs[TX_FD_NUM], rx_skbs[RX_BUF_NUM];
+#endif
+	struct mii_if_info mii;
+	unsigned short mii_id[2];
+	u32 msg_enable;
+	board_t boardtype;
 };
 
+static inline dma_addr_t fd_virt_to_bus(struct tc35815_local *lp, void *virt)
+{
+	return lp->fd_buf_dma + ((u8 *)virt - (u8 *)lp->fd_buf);
+}
+#ifdef DEBUG
+static inline void *fd_bus_to_virt(struct tc35815_local *lp, dma_addr_t bus)
+{
+	return (void *)((u8 *)lp->fd_buf + (bus - lp->fd_buf_dma));
+}
+#endif
+#ifdef TC35815_USE_PACKEDBUFFER
+static inline void *rxbuf_bus_to_virt(struct tc35815_local *lp, dma_addr_t bus)
+{
+	int i;
+	for (i = 0; i < RX_BUF_NUM; i++) {
+		if (bus >= lp->data_buf_dma[i] &&
+		    bus < lp->data_buf_dma[i] + PAGE_SIZE)
+			return (void *)((u8 *)lp->data_buf[i] +
+					(bus - lp->data_buf_dma[i]));
+	}
+	return NULL;
+}
+
+#define TC35815_DMA_SYNC_ONDEMAND
+static void* alloc_rxbuf_page(struct pci_dev *hwdev, dma_addr_t *dma_handle)
+{
+#ifdef TC35815_DMA_SYNC_ONDEMAND
+	void *buf;
+	/* pci_map + pci_dma_sync will be more effective than
+	 * pci_alloc_consistent on some archs. */
+	if ((buf = (void *)__get_free_page(GFP_ATOMIC)) == NULL)
+		return NULL;
+	*dma_handle = pci_map_single(hwdev, buf, PAGE_SIZE,
+				     PCI_DMA_FROMDEVICE);
+	if (pci_dma_mapping_error(*dma_handle)) {
+		free_page((unsigned long)buf);
+		return NULL;
+	}
+	return buf;
+#else
+	return pci_alloc_consistent(hwdev, PAGE_SIZE, dma_handle);
+#endif
+}
+
+static void free_rxbuf_page(struct pci_dev *hwdev, void *buf, dma_addr_t dma_handle)
+{
+#ifdef TC35815_DMA_SYNC_ONDEMAND
+	pci_unmap_single(hwdev, dma_handle, PAGE_SIZE, PCI_DMA_FROMDEVICE);
+	free_page((unsigned long)buf);
+#else
+	pci_free_consistent(hwdev, PAGE_SIZE, buf, dma_handle);
+#endif
+}
+#else /* TC35815_USE_PACKEDBUFFER */
+static struct sk_buff *alloc_rxbuf_skb(struct net_device *dev,
+				       struct pci_dev *hwdev,
+				       dma_addr_t *dma_handle)
+{
+	struct sk_buff *skb;
+	skb = dev_alloc_skb(RX_BUF_SIZE);
+	if (!skb)
+		return NULL;
+	skb->dev = dev;
+	*dma_handle = pci_map_single(hwdev, skb->data, RX_BUF_SIZE,
+				     PCI_DMA_FROMDEVICE);
+	if (pci_dma_mapping_error(*dma_handle)) {
+		dev_kfree_skb_any(skb);
+		return NULL;
+	}
+	skb_reserve(skb, 2);	/* make IP header 4byte aligned */
+	return skb;
+}
+
+static void free_rxbuf_skb(struct pci_dev *hwdev, struct sk_buff *skb, dma_addr_t dma_handle)
+{
+	pci_unmap_single(hwdev, dma_handle, RX_BUF_SIZE,
+			 PCI_DMA_FROMDEVICE);
+	dev_kfree_skb_any(skb);
+}
+#endif /* TC35815_USE_PACKEDBUFFER */
+
 /* Index to functions, as function prototypes. */
 
-static int __devinit tc35815_probe1(struct pci_dev *pdev, unsigned int base_addr, unsigned int irq);
-
 static int	tc35815_open(struct net_device *dev);
 static int	tc35815_send_packet(struct sk_buff *skb, struct net_device *dev);
-static void     tc35815_tx_timeout(struct net_device *dev);
-static irqreturn_t tc35815_interrupt(int irq, void *dev_id);
+static irqreturn_t	tc35815_interrupt(int irq, void *dev_id);
+#ifdef TC35815_NAPI
+static int	tc35815_rx(struct net_device *dev, int limit);
+static int	tc35815_poll(struct net_device *dev, int *budget);
+#else
 static void	tc35815_rx(struct net_device *dev);
+#endif
 static void	tc35815_txdone(struct net_device *dev);
 static int	tc35815_close(struct net_device *dev);
 static struct	net_device_stats *tc35815_get_stats(struct net_device *dev);
 static void	tc35815_set_multicast_list(struct net_device *dev);
+static void     tc35815_tx_timeout(struct net_device *dev);
+static int	tc35815_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void	tc35815_poll_controller(struct net_device *dev);
+#endif
+static const struct ethtool_ops tc35815_ethtool_ops;
 
+/* Example routines you must write ;->. */
 static void 	tc35815_chip_reset(struct net_device *dev);
 static void 	tc35815_chip_init(struct net_device *dev);
+static void	tc35815_find_phy(struct net_device *dev);
 static void 	tc35815_phy_chip_init(struct net_device *dev);
 
-/* A list of all installed tc35815 devices. */
-static struct net_device *root_tc35815_dev = NULL;
+#ifdef DEBUG
+static void	panic_queues(struct net_device *dev);
+#endif
 
-/*
- * PCI device identifiers for "new style" Linux PCI Device Drivers
- */
-static struct pci_device_id tc35815_pci_tbl[] = {
-    { PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC35815CF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
-    { 0, }
-};
+static void tc35815_timer(unsigned long data);
+static void tc35815_start_auto_negotiation(struct net_device *dev,
+					   struct ethtool_cmd *ep);
+static int tc_mdio_read(struct net_device *dev, int phy_id, int location);
+static void tc_mdio_write(struct net_device *dev, int phy_id, int location,
+			  int val);
 
-MODULE_DEVICE_TABLE (pci, tc35815_pci_tbl);
-
-int
-tc35815_probe(struct pci_dev *pdev,
-		const struct pci_device_id *ent)
+static void __devinit tc35815_init_dev_addr (struct net_device *dev)
 {
-	int err = 0;
-	int ret;
-	unsigned long pci_memaddr;
-	unsigned int pci_irq_line;
+	struct tc35815_regs __iomem *tr =
+		(struct tc35815_regs __iomem *)dev->base_addr;
+	int i;
 
-	printk(KERN_INFO "tc35815_probe: found device %#08x.%#08x\n", ent->vendor, ent->device);
-
-	err = pci_enable_device(pdev);
-	if (err)
-		return err;
-
-        pci_memaddr = pci_resource_start (pdev, 1);
-
-        printk(KERN_INFO "    pci_memaddr=%#08lx  resource_flags=%#08lx\n", pci_memaddr, pci_resource_flags (pdev, 0));
-
-	if (!pci_memaddr) {
-		printk(KERN_WARNING "no PCI MEM resources, aborting\n");
-		ret = -ENODEV;
-		goto err_out;
-	}
-	pci_irq_line = pdev->irq;
-	/* irq disabled. */
-	if (pci_irq_line == 0) {
-		printk(KERN_WARNING "no PCI irq, aborting\n");
-		ret = -ENODEV;
-		goto err_out;
-	}
-
-	ret =  tc35815_probe1(pdev, pci_memaddr, pci_irq_line);
-	if (ret)
-		goto err_out;
-
-	pci_set_master(pdev);
-
-	return 0;
-
-err_out:
-	pci_disable_device(pdev);
-	return ret;
-}
-
-static int __devinit tc35815_probe1(struct pci_dev *pdev, unsigned int base_addr, unsigned int irq)
-{
-	static unsigned version_printed = 0;
-	int i, ret;
-	struct tc35815_local *lp;
-	struct tc35815_regs *tr;
-	struct net_device *dev;
-
-	/* Allocate a new 'dev' if needed. */
-	dev = alloc_etherdev(sizeof(struct tc35815_local));
-	if (dev == NULL)
-		return -ENOMEM;
-
-	/*
-	 * alloc_etherdev allocs and zeros dev->priv
-	 */
-	lp = dev->priv;
-
-	if (tc35815_debug  &&  version_printed++ == 0)
-		printk(KERN_DEBUG "%s", version);
-
-	/* Fill in the 'dev' fields. */
-	dev->irq = irq;
-	dev->base_addr = (unsigned long)ioremap(base_addr,
-						sizeof(struct tc35815_regs));
-	if (!dev->base_addr) {
-		ret = -ENOMEM;
-		goto err_out;
-	}
-	tr = (struct tc35815_regs*)dev->base_addr;
-
-	tc35815_chip_reset(dev);
-
-	/* Retrieve and print the ethernet address. */
+	/* dev_addr will be overwritten on NETDEV_REGISTER event */
 	while (tc_readl(&tr->PROM_Ctl) & PROM_Busy)
 		;
 	for (i = 0; i < 6; i += 2) {
@@ -567,60 +616,175 @@
 		dev->dev_addr[i] = data & 0xff;
 		dev->dev_addr[i+1] = data >> 8;
 	}
+}
 
-	/* Initialize the device structure. */
-	lp->pdev = pdev;
-	lp->next_module = root_tc35815_dev;
-	root_tc35815_dev = dev;
+static int __devinit tc35815_init_one (struct pci_dev *pdev,
+				       const struct pci_device_id *ent)
+{
+	void __iomem *ioaddr = NULL;
+	struct net_device *dev;
+	struct tc35815_local *lp;
+	int rc;
+	unsigned long mmio_start, mmio_end, mmio_flags, mmio_len;
 
-	spin_lock_init(&lp->lock);
-
-	if (dev->mem_start > 0) {
-		lp->option = dev->mem_start;
-		if ((lp->option & TC35815_OPT_10M) &&
-		    (lp->option & TC35815_OPT_100M)) {
-			/* if both speed speficied, auto select. */
-			lp->option &= ~(TC35815_OPT_10M | TC35815_OPT_100M);
-		}
+	static int printed_version;
+	if (!printed_version++) {
+		printk(version);
+		dev_printk(KERN_DEBUG, &pdev->dev,
+			   "speed:%d duplex:%d doforce:%d\n",
+			   options.speed, options.duplex, options.doforce);
 	}
-	//XXX fixme
-        lp->option |= TC35815_OPT_10M;
 
-	/* do auto negotiation */
-	tc35815_phy_chip_init(dev);
+	if (!pdev->irq) {
+		dev_warn(&pdev->dev, "no IRQ assigned.\n");
+		return -ENODEV;
+	}
 
-	dev->open		= tc35815_open;
-	dev->stop		= tc35815_close;
-	dev->tx_timeout         = tc35815_tx_timeout;
-	dev->watchdog_timeo     = TX_TIMEOUT;
-	dev->hard_start_xmit	= tc35815_send_packet;
-	dev->get_stats		= tc35815_get_stats;
-	dev->set_multicast_list = tc35815_set_multicast_list;
+	/* dev zeroed in alloc_etherdev */
+	dev = alloc_etherdev (sizeof (*lp));
+	if (dev == NULL) {
+		dev_err(&pdev->dev, "unable to alloc new ethernet\n");
+		return -ENOMEM;
+	}
 	SET_MODULE_OWNER(dev);
 	SET_NETDEV_DEV(dev, &pdev->dev);
+	lp = dev->priv;
 
-	ret = register_netdev(dev);
-	if (ret)
-		goto err_out_iounmap;
+	/* enable device (incl. PCI PM wakeup), and bus-mastering */
+	rc = pci_enable_device (pdev);
+	if (rc)
+		goto err_out;
 
-	printk(KERN_INFO "%s: %s found at %#x, irq %d, MAC",
-	       dev->name, cardname, base_addr, irq);
-	for (i = 0; i < 6; i++)
-		printk(" %2.2x", dev->dev_addr[i]);
-	printk("\n");
-	printk(KERN_INFO "%s: linkspeed %dMbps, %s Duplex\n",
-	       dev->name, lp->linkspeed, lp->fullduplex ? "Full" : "Half");
+	mmio_start = pci_resource_start (pdev, 1);
+	mmio_end = pci_resource_end (pdev, 1);
+	mmio_flags = pci_resource_flags (pdev, 1);
+	mmio_len = pci_resource_len (pdev, 1);
+
+	/* set this immediately, we need to know before
+	 * we talk to the chip directly */
+
+	/* make sure PCI base addr 1 is MMIO */
+	if (!(mmio_flags & IORESOURCE_MEM)) {
+		dev_err(&pdev->dev, "region #1 not an MMIO resource, aborting\n");
+		rc = -ENODEV;
+		goto err_out;
+	}
+
+	/* check for weird/broken PCI region reporting */
+	if ((mmio_len < sizeof(struct tc35815_regs))) {
+		dev_err(&pdev->dev, "Invalid PCI region size(s), aborting\n");
+		rc = -ENODEV;
+		goto err_out;
+	}
+
+	rc = pci_request_regions (pdev, MODNAME);
+	if (rc)
+		goto err_out;
+
+	pci_set_master (pdev);
+
+	/* ioremap MMIO region */
+	ioaddr = ioremap (mmio_start, mmio_len);
+	if (ioaddr == NULL) {
+		dev_err(&pdev->dev, "cannot remap MMIO, aborting\n");
+		rc = -EIO;
+		goto err_out_free_res;
+	}
+
+	/* Initialize the device structure. */
+	dev->open = tc35815_open;
+	dev->hard_start_xmit = tc35815_send_packet;
+	dev->stop = tc35815_close;
+	dev->get_stats = tc35815_get_stats;
+	dev->set_multicast_list = tc35815_set_multicast_list;
+	dev->do_ioctl = tc35815_ioctl;
+	dev->ethtool_ops = &tc35815_ethtool_ops;
+	dev->tx_timeout = tc35815_tx_timeout;
+	dev->watchdog_timeo = TC35815_TX_TIMEOUT;
+#ifdef TC35815_NAPI
+	dev->poll = tc35815_poll;
+	dev->weight = NAPI_WEIGHT;
+#endif
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = tc35815_poll_controller;
+#endif
+
+	dev->irq = pdev->irq;
+	dev->base_addr = (unsigned long) ioaddr;
+
+	/* dev->priv/lp zeroed and aligned in alloc_etherdev */
+	lp = dev->priv;
+	spin_lock_init(&lp->lock);
+	lp->pci_dev = pdev;
+	lp->boardtype = ent->driver_data;
+
+	lp->msg_enable = NETIF_MSG_TX_ERR | NETIF_MSG_HW | NETIF_MSG_DRV | NETIF_MSG_LINK;
+	pci_set_drvdata(pdev, dev);
+
+	/* Soft reset the chip. */
+	tc35815_chip_reset(dev);
+
+	/* Retrieve the ethernet address. */
+	tc35815_init_dev_addr(dev);
+
+	rc = register_netdev (dev);
+	if (rc)
+		goto err_out_unmap;
+
+	memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
+	printk(KERN_INFO "%s: %s at 0x%lx, "
+		"%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
+		"IRQ %d\n",
+		dev->name,
+		board_info[ent->driver_data].name,
+		dev->base_addr,
+		dev->dev_addr[0], dev->dev_addr[1],
+		dev->dev_addr[2], dev->dev_addr[3],
+		dev->dev_addr[4], dev->dev_addr[5],
+		dev->irq);
+
+	setup_timer(&lp->timer, tc35815_timer, (unsigned long) dev);
+	lp->mii.dev = dev;
+	lp->mii.mdio_read = tc_mdio_read;
+	lp->mii.mdio_write = tc_mdio_write;
+	lp->mii.phy_id_mask = 0x1f;
+	lp->mii.reg_num_mask = 0x1f;
+	tc35815_find_phy(dev);
+	lp->mii.phy_id = lp->phy_addr;
+	lp->mii.full_duplex = 0;
+	lp->mii.force_media = 0;
 
 	return 0;
 
-err_out_iounmap:
-	iounmap((void *) dev->base_addr);
+err_out_unmap:
+	iounmap(ioaddr);
+err_out_free_res:
+	pci_release_regions (pdev);
 err_out:
-	free_netdev(dev);
-	return ret;
+	free_netdev (dev);
+	return rc;
 }
 
 
+static void __devexit tc35815_remove_one (struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata (pdev);
+	unsigned long mmio_addr;
+
+	mmio_addr = dev->base_addr;
+
+	unregister_netdev (dev);
+
+	if (mmio_addr) {
+		iounmap ((void __iomem *)mmio_addr);
+		pci_release_regions (pdev);
+	}
+
+	free_netdev (dev);
+
+	pci_set_drvdata (pdev, NULL);
+}
+
 static int
 tc35815_init_queues(struct net_device *dev)
 {
@@ -629,44 +793,64 @@
 	unsigned long fd_addr;
 
 	if (!lp->fd_buf) {
-		if (sizeof(struct FDesc) +
-		    sizeof(struct BDesc) * RX_BUF_PAGES +
-		    sizeof(struct FDesc) * RX_FD_NUM +
-		    sizeof(struct TxFD) * TX_FD_NUM > PAGE_SIZE * FD_PAGE_NUM) {
-			printk(KERN_WARNING "%s: Invalid Queue Size.\n", dev->name);
-			return -ENOMEM;
-		}
+		BUG_ON(sizeof(struct FDesc) +
+		       sizeof(struct BDesc) * RX_BUF_NUM +
+		       sizeof(struct FDesc) * RX_FD_NUM +
+		       sizeof(struct TxFD) * TX_FD_NUM >
+		       PAGE_SIZE * FD_PAGE_NUM);
 
-		if ((lp->fd_buf = (void *)__get_free_pages(GFP_KERNEL, FD_PAGE_ORDER)) == 0)
+		if ((lp->fd_buf = pci_alloc_consistent(lp->pci_dev, PAGE_SIZE * FD_PAGE_NUM, &lp->fd_buf_dma)) == 0)
 			return -ENOMEM;
-		for (i = 0; i < RX_BUF_PAGES; i++) {
-			if ((lp->data_buf[i] = (void *)get_zeroed_page(GFP_KERNEL)) == 0) {
+		for (i = 0; i < RX_BUF_NUM; i++) {
+#ifdef TC35815_USE_PACKEDBUFFER
+			if ((lp->data_buf[i] = alloc_rxbuf_page(lp->pci_dev, &lp->data_buf_dma[i])) == NULL) {
 				while (--i >= 0) {
-					free_page((unsigned long)lp->data_buf[i]);
-					lp->data_buf[i] = 0;
+					free_rxbuf_page(lp->pci_dev,
+							lp->data_buf[i],
+							lp->data_buf_dma[i]);
+					lp->data_buf[i] = NULL;
 				}
-				free_page((unsigned long)lp->fd_buf);
-				lp->fd_buf = 0;
+				pci_free_consistent(lp->pci_dev,
+						    PAGE_SIZE * FD_PAGE_NUM,
+						    lp->fd_buf,
+						    lp->fd_buf_dma);
+				lp->fd_buf = NULL;
 				return -ENOMEM;
 			}
-#ifdef __mips__
-			dma_cache_wback_inv((unsigned long)lp->data_buf[i], PAGE_SIZE * FD_PAGE_NUM);
+#else
+			lp->rx_skbs[i].skb =
+				alloc_rxbuf_skb(dev, lp->pci_dev,
+						&lp->rx_skbs[i].skb_dma);
+			if (!lp->rx_skbs[i].skb) {
+				while (--i >= 0) {
+					free_rxbuf_skb(lp->pci_dev,
+						       lp->rx_skbs[i].skb,
+						       lp->rx_skbs[i].skb_dma);
+					lp->rx_skbs[i].skb = NULL;
+				}
+				pci_free_consistent(lp->pci_dev,
+						    PAGE_SIZE * FD_PAGE_NUM,
+						    lp->fd_buf,
+						    lp->fd_buf_dma);
+				lp->fd_buf = NULL;
+				return -ENOMEM;
+			}
 #endif
 		}
-#ifdef __mips__
-		dma_cache_wback_inv((unsigned long)lp->fd_buf, PAGE_SIZE * FD_PAGE_NUM);
+		printk(KERN_DEBUG "%s: FD buf %p DataBuf",
+		       dev->name, lp->fd_buf);
+#ifdef TC35815_USE_PACKEDBUFFER
+		printk(" DataBuf");
+		for (i = 0; i < RX_BUF_NUM; i++)
+			printk(" %p", lp->data_buf[i]);
 #endif
+		printk("\n");
 	} else {
-		memset(lp->fd_buf, 0, PAGE_SIZE * FD_PAGE_NUM);
-#ifdef __mips__
-		dma_cache_wback_inv((unsigned long)lp->fd_buf, PAGE_SIZE * FD_PAGE_NUM);
-#endif
+		for (i = 0; i < FD_PAGE_NUM; i++) {
+			clear_page((void *)((unsigned long)lp->fd_buf + i * PAGE_SIZE));
+		}
 	}
-#ifdef __mips__
-	fd_addr = (unsigned long)vtonocache(lp->fd_buf);
-#else
 	fd_addr = (unsigned long)lp->fd_buf;
-#endif
 
 	/* Free Descriptors (for Receive) */
 	lp->rfd_base = (struct RxFD *)fd_addr;
@@ -675,34 +859,66 @@
 		lp->rfd_base[i].fd.FDCtl = cpu_to_le32(FD_CownsFD);
 	}
 	lp->rfd_cur = lp->rfd_base;
-	lp->rfd_limit = (struct RxFD *)(fd_addr -
-					sizeof(struct FDesc) -
-					sizeof(struct BDesc) * 30);
+	lp->rfd_limit = (struct RxFD *)fd_addr - (RX_FD_RESERVE + 1);
 
 	/* Transmit Descriptors */
 	lp->tfd_base = (struct TxFD *)fd_addr;
 	fd_addr += sizeof(struct TxFD) * TX_FD_NUM;
 	for (i = 0; i < TX_FD_NUM; i++) {
-		lp->tfd_base[i].fd.FDNext = cpu_to_le32(virt_to_bus(&lp->tfd_base[i+1]));
-		lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0);
+		lp->tfd_base[i].fd.FDNext = cpu_to_le32(fd_virt_to_bus(lp, &lp->tfd_base[i+1]));
+		lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0xffffffff);
 		lp->tfd_base[i].fd.FDCtl = cpu_to_le32(0);
 	}
-	lp->tfd_base[TX_FD_NUM-1].fd.FDNext = cpu_to_le32(virt_to_bus(&lp->tfd_base[0]));
+	lp->tfd_base[TX_FD_NUM-1].fd.FDNext = cpu_to_le32(fd_virt_to_bus(lp, &lp->tfd_base[0]));
 	lp->tfd_start = 0;
 	lp->tfd_end = 0;
 
 	/* Buffer List (for Receive) */
 	lp->fbl_ptr = (struct FrFD *)fd_addr;
-	lp->fbl_ptr->fd.FDNext = cpu_to_le32(virt_to_bus(lp->fbl_ptr));
-	lp->fbl_ptr->fd.FDCtl = cpu_to_le32(RX_BUF_PAGES | FD_CownsFD);
-	for (i = 0; i < RX_BUF_PAGES; i++) {
-		lp->fbl_ptr->bd[i].BuffData = cpu_to_le32(virt_to_bus(lp->data_buf[i]));
+	lp->fbl_ptr->fd.FDNext = cpu_to_le32(fd_virt_to_bus(lp, lp->fbl_ptr));
+	lp->fbl_ptr->fd.FDCtl = cpu_to_le32(RX_BUF_NUM | FD_CownsFD);
+#ifndef TC35815_USE_PACKEDBUFFER
+	/*
+	 * move all allocated skbs to head of rx_skbs[] array.
+	 * fbl_count mighe not be RX_BUF_NUM if alloc_rxbuf_skb() in
+	 * tc35815_rx() had failed.
+	 */
+	lp->fbl_count = 0;
+	for (i = 0; i < RX_BUF_NUM; i++) {
+		if (lp->rx_skbs[i].skb) {
+			if (i != lp->fbl_count) {
+				lp->rx_skbs[lp->fbl_count].skb =
+					lp->rx_skbs[i].skb;
+				lp->rx_skbs[lp->fbl_count].skb_dma =
+					lp->rx_skbs[i].skb_dma;
+			}
+			lp->fbl_count++;
+		}
+	}
+#endif
+	for (i = 0; i < RX_BUF_NUM; i++) {
+#ifdef TC35815_USE_PACKEDBUFFER
+		lp->fbl_ptr->bd[i].BuffData = cpu_to_le32(lp->data_buf_dma[i]);
+#else
+		if (i >= lp->fbl_count) {
+			lp->fbl_ptr->bd[i].BuffData = 0;
+			lp->fbl_ptr->bd[i].BDCtl = 0;
+			continue;
+		}
+		lp->fbl_ptr->bd[i].BuffData =
+			cpu_to_le32(lp->rx_skbs[i].skb_dma);
+#endif
 		/* BDID is index of FrFD.bd[] */
 		lp->fbl_ptr->bd[i].BDCtl =
-			cpu_to_le32(BD_CownsBD | (i << BD_RxBDID_SHIFT) | PAGE_SIZE);
+			cpu_to_le32(BD_CownsBD | (i << BD_RxBDID_SHIFT) |
+				    RX_BUF_SIZE);
 	}
+#ifdef TC35815_USE_PACKEDBUFFER
 	lp->fbl_curid = 0;
+#endif
 
+	printk(KERN_DEBUG "%s: TxFD %p RxFD %p FrFD %p\n",
+	       dev->name, lp->tfd_base, lp->rfd_base, lp->fbl_ptr);
 	return 0;
 }
 
@@ -713,11 +929,25 @@
 	int i;
 
 	for (i = 0; i < TX_FD_NUM; i++) {
-		struct sk_buff *skb = (struct sk_buff *)
-			le32_to_cpu(lp->tfd_base[i].fd.FDSystem);
-		if (skb)
+		u32 fdsystem = le32_to_cpu(lp->tfd_base[i].fd.FDSystem);
+		struct sk_buff *skb =
+			fdsystem != 0xffffffff ?
+			lp->tx_skbs[fdsystem].skb : NULL;
+#ifdef DEBUG
+		if (lp->tx_skbs[i].skb != skb) {
+			printk("%s: tx_skbs mismatch(%d).\n", dev->name, i);
+			panic_queues(dev);
+		}
+#else
+		BUG_ON(lp->tx_skbs[i].skb != skb);
+#endif
+		if (skb) {
+			pci_unmap_single(lp->pci_dev, lp->tx_skbs[i].skb_dma, skb->len, PCI_DMA_TODEVICE);
+			lp->tx_skbs[i].skb = NULL;
+			lp->tx_skbs[i].skb_dma = 0;
 			dev_kfree_skb_any(skb);
-		lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0);
+		}
+		lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0xffffffff);
 	}
 
 	tc35815_init_queues(dev);
@@ -731,28 +961,53 @@
 
 	if (lp->tfd_base) {
 		for (i = 0; i < TX_FD_NUM; i++) {
-			struct sk_buff *skb = (struct sk_buff *)
-				le32_to_cpu(lp->tfd_base[i].fd.FDSystem);
-			if (skb)
-				dev_kfree_skb_any(skb);
-			lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0);
+			u32 fdsystem = le32_to_cpu(lp->tfd_base[i].fd.FDSystem);
+			struct sk_buff *skb =
+				fdsystem != 0xffffffff ?
+				lp->tx_skbs[fdsystem].skb : NULL;
+#ifdef DEBUG
+			if (lp->tx_skbs[i].skb != skb) {
+				printk("%s: tx_skbs mismatch(%d).\n", dev->name, i);
+				panic_queues(dev);
+			}
+#else
+			BUG_ON(lp->tx_skbs[i].skb != skb);
+#endif
+			if (skb) {
+				dev_kfree_skb(skb);
+				pci_unmap_single(lp->pci_dev, lp->tx_skbs[i].skb_dma, skb->len, PCI_DMA_TODEVICE);
+				lp->tx_skbs[i].skb = NULL;
+				lp->tx_skbs[i].skb_dma = 0;
+			}
+			lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0xffffffff);
 		}
 	}
 
 	lp->rfd_base = NULL;
-	lp->rfd_base = NULL;
 	lp->rfd_limit = NULL;
 	lp->rfd_cur = NULL;
 	lp->fbl_ptr = NULL;
 
-	for (i = 0; i < RX_BUF_PAGES; i++) {
-		if (lp->data_buf[i])
-			free_page((unsigned long)lp->data_buf[i]);
-		lp->data_buf[i] = 0;
+	for (i = 0; i < RX_BUF_NUM; i++) {
+#ifdef TC35815_USE_PACKEDBUFFER
+		if (lp->data_buf[i]) {
+			free_rxbuf_page(lp->pci_dev,
+					lp->data_buf[i], lp->data_buf_dma[i]);
+			lp->data_buf[i] = NULL;
+		}
+#else
+		if (lp->rx_skbs[i].skb) {
+			free_rxbuf_skb(lp->pci_dev, lp->rx_skbs[i].skb,
+				       lp->rx_skbs[i].skb_dma);
+			lp->rx_skbs[i].skb = NULL;
+		}
+#endif
 	}
-	if (lp->fd_buf)
-		__free_pages(lp->fd_buf, FD_PAGE_ORDER);
-	lp->fd_buf = NULL;
+	if (lp->fd_buf) {
+		pci_free_consistent(lp->pci_dev, PAGE_SIZE * FD_PAGE_NUM,
+				    lp->fd_buf, lp->fd_buf_dma);
+		lp->fd_buf = NULL;
+	}
 }
 
 static void
@@ -792,6 +1047,7 @@
 	return bd_count;
 }
 
+#if defined(DEBUG) || defined(TC35815_USE_PACKEDBUFFER)
 static void
 dump_frfd(struct FrFD *fd)
 {
@@ -802,20 +1058,22 @@
 	       le32_to_cpu(fd->fd.FDStat),
 	       le32_to_cpu(fd->fd.FDCtl));
 	printk("BD: ");
-	for (i = 0; i < RX_BUF_PAGES; i++)
+	for (i = 0; i < RX_BUF_NUM; i++)
 		printk(" %08x %08x",
 		       le32_to_cpu(fd->bd[i].BuffData),
 		       le32_to_cpu(fd->bd[i].BDCtl));
 	printk("\n");
 }
+#endif
 
+#ifdef DEBUG
 static void
 panic_queues(struct net_device *dev)
 {
 	struct tc35815_local *lp = dev->priv;
 	int i;
 
-	printk("TxFD base %p, start %d, end %d\n",
+	printk("TxFD base %p, start %u, end %u\n",
 	       lp->tfd_base, lp->tfd_start, lp->tfd_end);
 	printk("RxFD base %p limit %p cur %p\n",
 	       lp->rfd_base, lp->rfd_limit, lp->rfd_cur);
@@ -829,31 +1087,13 @@
 	dump_frfd(lp->fbl_ptr);
 	panic("%s: Illegal queue state.", dev->name);
 }
-
-#if 0
-static void print_buf(char *add, int length)
-{
-	int i;
-	int len = length;
-
-	printk("print_buf(%08x)(%x)\n", (unsigned int) add,length);
-
-	if (len > 100)
-		len = 100;
-	for (i = 0; i < len; i++) {
-		printk(" %2.2X", (unsigned char) add[i]);
-		if (!(i % 16))
-			printk("\n");
-	}
-	printk("\n");
-}
 #endif
 
 static void print_eth(char *add)
 {
 	int i;
 
-	printk("print_eth(%08x)\n", (unsigned int) add);
+	printk("print_eth(%p)\n", add);
 	for (i = 0; i < 6; i++)
 		printk(" %2.2X", (unsigned char) add[i + 6]);
 	printk(" =>");
@@ -862,6 +1102,73 @@
 	printk(" : %2.2X%2.2X\n", (unsigned char) add[12], (unsigned char) add[13]);
 }
 
+static int tc35815_tx_full(struct net_device *dev)
+{
+	struct tc35815_local *lp = dev->priv;
+	return ((lp->tfd_start + 1) % TX_FD_NUM == lp->tfd_end);
+}
+
+static void tc35815_restart(struct net_device *dev)
+{
+	struct tc35815_local *lp = dev->priv;
+	int pid = lp->phy_addr;
+	int do_phy_reset = 1;
+	del_timer(&lp->timer);		/* Kill if running	*/
+
+	if (lp->mii_id[0] == 0x0016 && (lp->mii_id[1] & 0xfc00) == 0xf800) {
+		/* Resetting PHY cause problem on some chip... (SEEQ 80221) */
+		do_phy_reset = 0;
+	}
+	if (do_phy_reset) {
+		int timeout;
+		tc_mdio_write(dev, pid, MII_BMCR, BMCR_RESET);
+		timeout = 100;
+		while (--timeout) {
+			if (!(tc_mdio_read(dev, pid, MII_BMCR) & BMCR_RESET))
+				break;
+			udelay(1);
+		}
+		if (!timeout)
+			printk(KERN_ERR "%s: BMCR reset failed.\n", dev->name);
+	}
+
+	tc35815_chip_reset(dev);
+	tc35815_clear_queues(dev);
+	tc35815_chip_init(dev);
+	/* Reconfigure CAM again since tc35815_chip_init() initialize it. */
+	tc35815_set_multicast_list(dev);
+}
+
+static void tc35815_tx_timeout(struct net_device *dev)
+{
+	struct tc35815_local *lp = dev->priv;
+	struct tc35815_regs __iomem *tr =
+		(struct tc35815_regs __iomem *)dev->base_addr;
+
+	printk(KERN_WARNING "%s: transmit timed out, status %#x\n",
+	       dev->name, tc_readl(&tr->Tx_Stat));
+
+	/* Try to restart the adaptor. */
+	spin_lock_irq(&lp->lock);
+	tc35815_restart(dev);
+	spin_unlock_irq(&lp->lock);
+
+	lp->stats.tx_errors++;
+
+	/* If we have space available to accept new transmit
+	 * requests, wake up the queueing layer.  This would
+	 * be the case if the chipset_init() call above just
+	 * flushes out the tx queue and empties it.
+	 *
+	 * If instead, the tx queue is retained then the
+	 * netif_wake_queue() call should be placed in the
+	 * TX completion interrupt handler of the driver instead
+	 * of here.
+	 */
+	if (!tc35815_tx_full(dev))
+		netif_wake_queue(dev);
+}
+
 /*
  * Open/initialize the board. This is called (in the current kernel)
  * sometime after booting when the 'ifconfig' program is run.
@@ -874,16 +1181,16 @@
 tc35815_open(struct net_device *dev)
 {
 	struct tc35815_local *lp = dev->priv;
+
 	/*
 	 * This is used if the interrupt line can turned off (shared).
 	 * See 3c503.c for an example of selecting the IRQ at config-time.
 	 */
-
-	if (dev->irq == 0  ||
-	    request_irq(dev->irq, &tc35815_interrupt, IRQF_SHARED, cardname, dev)) {
+	if (request_irq(dev->irq, &tc35815_interrupt, IRQF_SHARED, dev->name, dev)) {
 		return -EAGAIN;
 	}
 
+	del_timer(&lp->timer);		/* Kill if running	*/
 	tc35815_chip_reset(dev);
 
 	if (tc35815_init_queues(dev) != 0) {
@@ -892,138 +1199,119 @@
 	}
 
 	/* Reset the hardware here. Don't forget to set the station address. */
+	spin_lock_irq(&lp->lock);
 	tc35815_chip_init(dev);
+	spin_unlock_irq(&lp->lock);
 
-	lp->tbusy = 0;
+	/* We are now ready to accept transmit requeusts from
+	 * the queueing layer of the networking.
+	 */
 	netif_start_queue(dev);
 
 	return 0;
 }
 
-static void tc35815_tx_timeout(struct net_device *dev)
-{
-	struct tc35815_local *lp = dev->priv;
-	struct tc35815_regs *tr = (struct tc35815_regs *)dev->base_addr;
-	unsigned long flags;
-
-	spin_lock_irqsave(&lp->lock, flags);
-	printk(KERN_WARNING "%s: transmit timed out, status %#lx\n",
-	       dev->name, tc_readl(&tr->Tx_Stat));
-	/* Try to restart the adaptor. */
-	tc35815_chip_reset(dev);
-	tc35815_clear_queues(dev);
-	tc35815_chip_init(dev);
-	lp->tbusy=0;
-	spin_unlock_irqrestore(&lp->lock, flags);
-	dev->trans_start = jiffies;
-	netif_wake_queue(dev);
-}
-
+/* This will only be invoked if your driver is _not_ in XOFF state.
+ * What this means is that you need not check it, and that this
+ * invariant will hold if you make sure that the netif_*_queue()
+ * calls are done at the proper times.
+ */
 static int tc35815_send_packet(struct sk_buff *skb, struct net_device *dev)
 {
 	struct tc35815_local *lp = dev->priv;
-	struct tc35815_regs *tr = (struct tc35815_regs *)dev->base_addr;
+	struct TxFD *txfd;
+	unsigned long flags;
 
-	if (netif_queue_stopped(dev)) {
-		/*
-		 * If we get here, some higher level has decided we are broken.
-		 * There should really be a "kick me" function call instead.
-		 */
-		int tickssofar = jiffies - dev->trans_start;
-		if (tickssofar < 5)
-			return 1;
-		printk(KERN_WARNING "%s: transmit timed out, status %#lx\n",
-		       dev->name, tc_readl(&tr->Tx_Stat));
-		/* Try to restart the adaptor. */
-		tc35815_chip_reset(dev);
-		tc35815_clear_queues(dev);
-		tc35815_chip_init(dev);
-		lp->tbusy=0;
-		dev->trans_start = jiffies;
-		netif_wake_queue(dev);
-	}
-
-	/*
-	 * Block a timer-based transmit from overlapping. This could better be
-	 * done with atomic_swap(1, lp->tbusy), but set_bit() works as well.
+	/* If some error occurs while trying to transmit this
+	 * packet, you should return '1' from this function.
+	 * In such a case you _may not_ do anything to the
+	 * SKB, it is still owned by the network queueing
+	 * layer when an error is returned.  This means you
+	 * may not modify any SKB fields, you may not free
+	 * the SKB, etc.
 	 */
-	if (test_and_set_bit(0, (void*)&lp->tbusy) != 0) {
-		printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name);
-		dev_kfree_skb_any(skb);
-	} else {
-		short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
-		unsigned char *buf = skb->data;
-		struct TxFD *txfd = &lp->tfd_base[lp->tfd_start];
-		unsigned long flags;
-		lp->stats.tx_bytes += skb->len;
 
+	/* This is the most common case for modern hardware.
+	 * The spinlock protects this code from the TX complete
+	 * hardware interrupt handler.  Queue flow control is
+	 * thus managed under this lock as well.
+	 */
+	spin_lock_irqsave(&lp->lock, flags);
 
-#ifdef __mips__
-		dma_cache_wback_inv((unsigned long)buf, length);
+	/* failsafe... (handle txdone now if half of FDs are used) */
+	if ((lp->tfd_start + TX_FD_NUM - lp->tfd_end) % TX_FD_NUM >
+	    TX_FD_NUM / 2)
+		tc35815_txdone(dev);
+
+	if (netif_msg_pktdata(lp))
+		print_eth(skb->data);
+#ifdef DEBUG
+	if (lp->tx_skbs[lp->tfd_start].skb) {
+		printk("%s: tx_skbs conflict.\n", dev->name);
+		panic_queues(dev);
+	}
+#else
+	BUG_ON(lp->tx_skbs[lp->tfd_start].skb);
 #endif
+	lp->tx_skbs[lp->tfd_start].skb = skb;
+	lp->tx_skbs[lp->tfd_start].skb_dma = pci_map_single(lp->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE);
 
-		spin_lock_irqsave(&lp->lock, flags);
+	/*add to ring */
+	txfd = &lp->tfd_base[lp->tfd_start];
+	txfd->bd.BuffData = cpu_to_le32(lp->tx_skbs[lp->tfd_start].skb_dma);
+	txfd->bd.BDCtl = cpu_to_le32(skb->len);
+	txfd->fd.FDSystem = cpu_to_le32(lp->tfd_start);
+	txfd->fd.FDCtl = cpu_to_le32(FD_CownsFD | (1 << FD_BDCnt_SHIFT));
 
-		/* failsafe... */
-		if (lp->tfd_start != lp->tfd_end)
-			tc35815_txdone(dev);
-
-
-		txfd->bd.BuffData = cpu_to_le32(virt_to_bus(buf));
-
-		txfd->bd.BDCtl = cpu_to_le32(length);
-		txfd->fd.FDSystem = cpu_to_le32((__u32)skb);
-		txfd->fd.FDCtl = cpu_to_le32(FD_CownsFD | (1 << FD_BDCnt_SHIFT));
-
-		if (lp->tfd_start == lp->tfd_end) {
-			/* Start DMA Transmitter. */
-			txfd->fd.FDNext |= cpu_to_le32(FD_Next_EOL);
+	if (lp->tfd_start == lp->tfd_end) {
+		struct tc35815_regs __iomem *tr =
+			(struct tc35815_regs __iomem *)dev->base_addr;
+		/* Start DMA Transmitter. */
+		txfd->fd.FDNext |= cpu_to_le32(FD_Next_EOL);
 #ifdef GATHER_TXINT
-			txfd->fd.FDCtl |= cpu_to_le32(FD_FrmOpt_IntTx);
+		txfd->fd.FDCtl |= cpu_to_le32(FD_FrmOpt_IntTx);
 #endif
-			if (tc35815_debug > 2) {
-				printk("%s: starting TxFD.\n", dev->name);
-				dump_txfd(txfd);
-				if (tc35815_debug > 3)
-					print_eth(buf);
-			}
-			tc_writel(virt_to_bus(txfd), &tr->TxFrmPtr);
-		} else {
-			txfd->fd.FDNext &= cpu_to_le32(~FD_Next_EOL);
-			if (tc35815_debug > 2) {
-				printk("%s: queueing TxFD.\n", dev->name);
-				dump_txfd(txfd);
-				if (tc35815_debug > 3)
-					print_eth(buf);
-			}
+		if (netif_msg_tx_queued(lp)) {
+			printk("%s: starting TxFD.\n", dev->name);
+			dump_txfd(txfd);
 		}
-		lp->tfd_start = (lp->tfd_start + 1) % TX_FD_NUM;
-
-		dev->trans_start = jiffies;
-
-		if ((lp->tfd_start + 1) % TX_FD_NUM != lp->tfd_end) {
-			/* we can send another packet */
-			lp->tbusy = 0;
-			netif_start_queue(dev);
-		} else {
-			netif_stop_queue(dev);
-			if (tc35815_debug > 1)
-				printk(KERN_WARNING "%s: TxFD Exhausted.\n", dev->name);
+		tc_writel(fd_virt_to_bus(lp, txfd), &tr->TxFrmPtr);
+	} else {
+		txfd->fd.FDNext &= cpu_to_le32(~FD_Next_EOL);
+		if (netif_msg_tx_queued(lp)) {
+			printk("%s: queueing TxFD.\n", dev->name);
+			dump_txfd(txfd);
 		}
-		spin_unlock_irqrestore(&lp->lock, flags);
+	}
+	lp->tfd_start = (lp->tfd_start + 1) % TX_FD_NUM;
+
+	dev->trans_start = jiffies;
+
+	/* If we just used up the very last entry in the
+	 * TX ring on this device, tell the queueing
+	 * layer to send no more.
+	 */
+	if (tc35815_tx_full(dev)) {
+		if (netif_msg_tx_queued(lp))
+			printk(KERN_WARNING "%s: TxFD Exhausted.\n", dev->name);
+		netif_stop_queue(dev);
 	}
 
+	/* When the TX completion hw interrupt arrives, this
+	 * is when the transmit statistics are updated.
+	 */
+
+	spin_unlock_irqrestore(&lp->lock, flags);
 	return 0;
 }
 
 #define FATAL_ERROR_INT \
 	(Int_IntPCI | Int_DmParErr | Int_IntNRAbt)
-static void tc35815_fatal_error_interrupt(struct net_device *dev, int status)
+static void tc35815_fatal_error_interrupt(struct net_device *dev, u32 status)
 {
 	static int count;
 	printk(KERN_WARNING "%s: Fatal Error Intterrupt (%#x):",
 	       dev->name, status);
-
 	if (status & Int_IntPCI)
 		printk(" IntPCI");
 	if (status & Int_DmParErr)
@@ -1033,110 +1321,170 @@
 	printk("\n");
 	if (count++ > 100)
 		panic("%s: Too many fatal errors.", dev->name);
-	printk(KERN_WARNING "%s: Resetting %s...\n", dev->name, cardname);
+	printk(KERN_WARNING "%s: Resetting ...\n", dev->name);
 	/* Try to restart the adaptor. */
-	tc35815_chip_reset(dev);
-	tc35815_clear_queues(dev);
-	tc35815_chip_init(dev);
+	tc35815_restart(dev);
+}
+
+#ifdef TC35815_NAPI
+static int tc35815_do_interrupt(struct net_device *dev, u32 status, int limit)
+#else
+static int tc35815_do_interrupt(struct net_device *dev, u32 status)
+#endif
+{
+	struct tc35815_local *lp = dev->priv;
+	struct tc35815_regs __iomem *tr =
+		(struct tc35815_regs __iomem *)dev->base_addr;
+	int ret = -1;
+
+	/* Fatal errors... */
+	if (status & FATAL_ERROR_INT) {
+		tc35815_fatal_error_interrupt(dev, status);
+		return 0;
+	}
+	/* recoverable errors */
+	if (status & Int_IntFDAEx) {
+		/* disable FDAEx int. (until we make rooms...) */
+		tc_writel(tc_readl(&tr->Int_En) & ~Int_FDAExEn, &tr->Int_En);
+		printk(KERN_WARNING
+		       "%s: Free Descriptor Area Exhausted (%#x).\n",
+		       dev->name, status);
+		lp->stats.rx_dropped++;
+		ret = 0;
+	}
+	if (status & Int_IntBLEx) {
+		/* disable BLEx int. (until we make rooms...) */
+		tc_writel(tc_readl(&tr->Int_En) & ~Int_BLExEn, &tr->Int_En);
+		printk(KERN_WARNING
+		       "%s: Buffer List Exhausted (%#x).\n",
+		       dev->name, status);
+		lp->stats.rx_dropped++;
+		ret = 0;
+	}
+	if (status & Int_IntExBD) {
+		printk(KERN_WARNING
+		       "%s: Excessive Buffer Descriptiors (%#x).\n",
+		       dev->name, status);
+		lp->stats.rx_length_errors++;
+		ret = 0;
+	}
+
+	/* normal notification */
+	if (status & Int_IntMacRx) {
+		/* Got a packet(s). */
+#ifdef TC35815_NAPI
+		ret = tc35815_rx(dev, limit);
+#else
+		tc35815_rx(dev);
+		ret = 0;
+#endif
+		lp->lstats.rx_ints++;
+	}
+	if (status & Int_IntMacTx) {
+		/* Transmit complete. */
+		lp->lstats.tx_ints++;
+		tc35815_txdone(dev);
+		netif_wake_queue(dev);
+		ret = 0;
+	}
+	return ret;
 }
 
 /*
  * The typical workload of the driver:
- *   Handle the network interface interrupts.
+ * Handle the network interface interrupts.
  */
 static irqreturn_t tc35815_interrupt(int irq, void *dev_id)
 {
 	struct net_device *dev = dev_id;
-	struct tc35815_regs *tr;
-	struct tc35815_local *lp;
-	int status, boguscount = 0;
-	int handled = 0;
+	struct tc35815_regs __iomem *tr =
+		(struct tc35815_regs __iomem *)dev->base_addr;
+#ifdef TC35815_NAPI
+	u32 dmactl = tc_readl(&tr->DMA_Ctl);
 
-	if (dev == NULL) {
-		printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq);
-		return IRQ_NONE;
+	if (!(dmactl & DMA_IntMask)) {
+		/* disable interrupts */
+		tc_writel(dmactl | DMA_IntMask, &tr->DMA_Ctl);
+		if (netif_rx_schedule_prep(dev))
+			__netif_rx_schedule(dev);
+		else {
+			printk(KERN_ERR "%s: interrupt taken in poll\n",
+			       dev->name);
+			BUG();
+		}
+		(void)tc_readl(&tr->Int_Src);	/* flush */
+		return IRQ_HANDLED;
 	}
+	return IRQ_NONE;
+#else
+	struct tc35815_local *lp = dev->priv;
+	int handled;
+	u32 status;
 
-	tr = (struct tc35815_regs*)dev->base_addr;
-	lp = dev->priv;
-
-	do {
-		status = tc_readl(&tr->Int_Src);
-		if (status == 0)
-			break;
-		handled = 1;
-		tc_writel(status, &tr->Int_Src);	/* write to clear */
-
-		/* Fatal errors... */
-		if (status & FATAL_ERROR_INT) {
-			tc35815_fatal_error_interrupt(dev, status);
-			break;
-		}
-		/* recoverable errors */
-		if (status & Int_IntFDAEx) {
-			/* disable FDAEx int. (until we make rooms...) */
-			tc_writel(tc_readl(&tr->Int_En) & ~Int_FDAExEn, &tr->Int_En);
-			printk(KERN_WARNING
-			       "%s: Free Descriptor Area Exhausted (%#x).\n",
-			       dev->name, status);
-			lp->stats.rx_dropped++;
-		}
-		if (status & Int_IntBLEx) {
-			/* disable BLEx int. (until we make rooms...) */
-			tc_writel(tc_readl(&tr->Int_En) & ~Int_BLExEn, &tr->Int_En);
-			printk(KERN_WARNING
-			       "%s: Buffer List Exhausted (%#x).\n",
-			       dev->name, status);
-			lp->stats.rx_dropped++;
-		}
-		if (status & Int_IntExBD) {
-			printk(KERN_WARNING
-			       "%s: Excessive Buffer Descriptiors (%#x).\n",
-			       dev->name, status);
-			lp->stats.rx_length_errors++;
-		}
-		/* normal notification */
-		if (status & Int_IntMacRx) {
-			/* Got a packet(s). */
-			lp->lstats.rx_ints++;
-			tc35815_rx(dev);
-		}
-		if (status & Int_IntMacTx) {
-			lp->lstats.tx_ints++;
-			tc35815_txdone(dev);
-		}
-	} while (++boguscount < 20) ;
-
-	return IRQ_RETVAL(handled);
+	spin_lock(&lp->lock);
+	status = tc_readl(&tr->Int_Src);
+	tc_writel(status, &tr->Int_Src);	/* write to clear */
+	handled = tc35815_do_interrupt(dev, status);
+	(void)tc_readl(&tr->Int_Src);	/* flush */
+	spin_unlock(&lp->lock);
+	return IRQ_RETVAL(handled >= 0);
+#endif /* TC35815_NAPI */
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void tc35815_poll_controller(struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	tc35815_interrupt(dev->irq, dev);
+	enable_irq(dev->irq);
+}
+#endif
+
 /* We have a good packet(s), get it/them out of the buffers. */
+#ifdef TC35815_NAPI
+static int
+tc35815_rx(struct net_device *dev, int limit)
+#else
 static void
 tc35815_rx(struct net_device *dev)
+#endif
 {
 	struct tc35815_local *lp = dev->priv;
-	struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr;
 	unsigned int fdctl;
 	int i;
 	int buf_free_count = 0;
 	int fd_free_count = 0;
+#ifdef TC35815_NAPI
+	int received = 0;
+#endif
 
 	while (!((fdctl = le32_to_cpu(lp->rfd_cur->fd.FDCtl)) & FD_CownsFD)) {
 		int status = le32_to_cpu(lp->rfd_cur->fd.FDStat);
 		int pkt_len = fdctl & FD_FDLength_MASK;
-		struct RxFD *next_rfd;
 		int bd_count = (fdctl & FD_BDCnt_MASK) >> FD_BDCnt_SHIFT;
+#ifdef DEBUG
+		struct RxFD *next_rfd;
+#endif
+#if (RX_CTL_CMD & Rx_StripCRC) == 0
+		pkt_len -= 4;
+#endif
 
-		if (tc35815_debug > 2)
+		if (netif_msg_rx_status(lp))
 			dump_rxfd(lp->rfd_cur);
 		if (status & Rx_Good) {
-			/* Malloc up new buffer. */
 			struct sk_buff *skb;
 			unsigned char *data;
-			int cur_bd, offset;
+			int cur_bd;
+#ifdef TC35815_USE_PACKEDBUFFER
+			int offset;
+#endif
 
-			lp->stats.rx_bytes += pkt_len;
-
+#ifdef TC35815_NAPI
+			if (--limit < 0)
+				break;
+#endif
+#ifdef TC35815_USE_PACKEDBUFFER
+			BUG_ON(bd_count > 2);
 			skb = dev_alloc_skb(pkt_len + 2); /* +2: for reserve */
 			if (skb == NULL) {
 				printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
@@ -1145,7 +1493,6 @@
 				break;
 			}
 			skb_reserve(skb, 2);   /* 16 bit alignment */
-			skb->dev = dev;
 
 			data = skb_put(skb, pkt_len);
 
@@ -1155,25 +1502,69 @@
 			while (offset < pkt_len && cur_bd < bd_count) {
 				int len = le32_to_cpu(lp->rfd_cur->bd[cur_bd].BDCtl) &
 					BD_BuffLength_MASK;
-				void *rxbuf =
-					bus_to_virt(le32_to_cpu(lp->rfd_cur->bd[cur_bd].BuffData));
-#ifdef __mips__
-				dma_cache_inv((unsigned long)rxbuf, len);
+				dma_addr_t dma = le32_to_cpu(lp->rfd_cur->bd[cur_bd].BuffData);
+				void *rxbuf = rxbuf_bus_to_virt(lp, dma);
+				if (offset + len > pkt_len)
+					len = pkt_len - offset;
+#ifdef TC35815_DMA_SYNC_ONDEMAND
+				pci_dma_sync_single_for_cpu(lp->pci_dev,
+							    dma, len,
+							    PCI_DMA_FROMDEVICE);
 #endif
 				memcpy(data + offset, rxbuf, len);
+#ifdef TC35815_DMA_SYNC_ONDEMAND
+				pci_dma_sync_single_for_device(lp->pci_dev,
+							       dma, len,
+							       PCI_DMA_FROMDEVICE);
+#endif
 				offset += len;
 				cur_bd++;
 			}
-#if 0
-			print_buf(data,pkt_len);
+#else /* TC35815_USE_PACKEDBUFFER */
+			BUG_ON(bd_count > 1);
+			cur_bd = (le32_to_cpu(lp->rfd_cur->bd[0].BDCtl)
+				  & BD_RxBDID_MASK) >> BD_RxBDID_SHIFT;
+#ifdef DEBUG
+			if (cur_bd >= RX_BUF_NUM) {
+				printk("%s: invalid BDID.\n", dev->name);
+				panic_queues(dev);
+			}
+			BUG_ON(lp->rx_skbs[cur_bd].skb_dma !=
+			       (le32_to_cpu(lp->rfd_cur->bd[0].BuffData) & ~3));
+			if (!lp->rx_skbs[cur_bd].skb) {
+				printk("%s: NULL skb.\n", dev->name);
+				panic_queues(dev);
+			}
+#else
+			BUG_ON(cur_bd >= RX_BUF_NUM);
 #endif
-			if (tc35815_debug > 3)
+			skb = lp->rx_skbs[cur_bd].skb;
+			prefetch(skb->data);
+			lp->rx_skbs[cur_bd].skb = NULL;
+			lp->fbl_count--;
+			pci_unmap_single(lp->pci_dev,
+					 lp->rx_skbs[cur_bd].skb_dma,
+					 RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+			if (!HAVE_DMA_RXALIGN(lp))
+				memmove(skb->data, skb->data - 2, pkt_len);
+			data = skb_put(skb, pkt_len);
+#endif /* TC35815_USE_PACKEDBUFFER */
+			if (netif_msg_pktdata(lp))
 				print_eth(data);
 			skb->protocol = eth_type_trans(skb, dev);
+#ifdef TC35815_NAPI
+			netif_receive_skb(skb);
+			received++;
+#else
 			netif_rx(skb);
+#endif
+			dev->last_rx = jiffies;
 			lp->stats.rx_packets++;
+			lp->stats.rx_bytes += pkt_len;
 		} else {
 			lp->stats.rx_errors++;
+			printk(KERN_DEBUG "%s: Rx error (status %x)\n",
+			       dev->name, status & Rx_Stat_Mask);
 			/* WORKAROUND: LongErr and CRCErr means Overflow. */
 			if ((status & Rx_LongErr) && (status & Rx_CRCErr)) {
 				status &= ~(Rx_LongErr|Rx_CRCErr);
@@ -1190,63 +1581,150 @@
 			int bdctl = le32_to_cpu(lp->rfd_cur->bd[bd_count - 1].BDCtl);
 			unsigned char id =
 				(bdctl & BD_RxBDID_MASK) >> BD_RxBDID_SHIFT;
-			if (id >= RX_BUF_PAGES) {
+#ifdef DEBUG
+			if (id >= RX_BUF_NUM) {
 				printk("%s: invalid BDID.\n", dev->name);
 				panic_queues(dev);
 			}
+#else
+			BUG_ON(id >= RX_BUF_NUM);
+#endif
 			/* free old buffers */
-			while (lp->fbl_curid != id) {
-				bdctl = le32_to_cpu(lp->fbl_ptr->bd[lp->fbl_curid].BDCtl);
+#ifdef TC35815_USE_PACKEDBUFFER
+			while (lp->fbl_curid != id)
+#else
+			while (lp->fbl_count < RX_BUF_NUM)
+#endif
+			{
+#ifdef TC35815_USE_PACKEDBUFFER
+				unsigned char curid = lp->fbl_curid;
+#else
+				unsigned char curid =
+					(id + 1 + lp->fbl_count) % RX_BUF_NUM;
+#endif
+				struct BDesc *bd = &lp->fbl_ptr->bd[curid];
+#ifdef DEBUG
+				bdctl = le32_to_cpu(bd->BDCtl);
 				if (bdctl & BD_CownsBD) {
 					printk("%s: Freeing invalid BD.\n",
 					       dev->name);
 					panic_queues(dev);
 				}
+#endif
 				/* pass BD to controler */
+#ifndef TC35815_USE_PACKEDBUFFER
+				if (!lp->rx_skbs[curid].skb) {
+					lp->rx_skbs[curid].skb =
+						alloc_rxbuf_skb(dev,
+								lp->pci_dev,
+								&lp->rx_skbs[curid].skb_dma);
+					if (!lp->rx_skbs[curid].skb)
+						break; /* try on next reception */
+					bd->BuffData = cpu_to_le32(lp->rx_skbs[curid].skb_dma);
+				}
+#endif /* TC35815_USE_PACKEDBUFFER */
 				/* Note: BDLength was modified by chip. */
-				lp->fbl_ptr->bd[lp->fbl_curid].BDCtl =
-					cpu_to_le32(BD_CownsBD |
-						    (lp->fbl_curid << BD_RxBDID_SHIFT) |
-						    PAGE_SIZE);
-				lp->fbl_curid =
-					(lp->fbl_curid + 1) % RX_BUF_PAGES;
-				if (tc35815_debug > 2) {
+				bd->BDCtl = cpu_to_le32(BD_CownsBD |
+							(curid << BD_RxBDID_SHIFT) |
+							RX_BUF_SIZE);
+#ifdef TC35815_USE_PACKEDBUFFER
+				lp->fbl_curid = (curid + 1) % RX_BUF_NUM;
+				if (netif_msg_rx_status(lp)) {
 					printk("%s: Entering new FBD %d\n",
 					       dev->name, lp->fbl_curid);
 					dump_frfd(lp->fbl_ptr);
 				}
+#else
+				lp->fbl_count++;
+#endif
 				buf_free_count++;
 			}
 		}
 
 		/* put RxFD back to controller */
-		next_rfd = bus_to_virt(le32_to_cpu(lp->rfd_cur->fd.FDNext));
-#ifdef __mips__
-		next_rfd = (struct RxFD *)vtonocache(next_rfd);
-#endif
+#ifdef DEBUG
+		next_rfd = fd_bus_to_virt(lp,
+					  le32_to_cpu(lp->rfd_cur->fd.FDNext));
 		if (next_rfd < lp->rfd_base || next_rfd > lp->rfd_limit) {
 			printk("%s: RxFD FDNext invalid.\n", dev->name);
 			panic_queues(dev);
 		}
+#endif
 		for (i = 0; i < (bd_count + 1) / 2 + 1; i++) {
 			/* pass FD to controler */
-			lp->rfd_cur->fd.FDNext = cpu_to_le32(0xdeaddead);	/* for debug */
+#ifdef DEBUG
+			lp->rfd_cur->fd.FDNext = cpu_to_le32(0xdeaddead);
+#else
+			lp->rfd_cur->fd.FDNext = cpu_to_le32(FD_Next_EOL);
+#endif
 			lp->rfd_cur->fd.FDCtl = cpu_to_le32(FD_CownsFD);
 			lp->rfd_cur++;
 			fd_free_count++;
 		}
-
-		lp->rfd_cur = next_rfd;
+		if (lp->rfd_cur > lp->rfd_limit)
+			lp->rfd_cur = lp->rfd_base;
+#ifdef DEBUG
+		if (lp->rfd_cur != next_rfd)
+			printk("rfd_cur = %p, next_rfd %p\n",
+			       lp->rfd_cur, next_rfd);
+#endif
 	}
 
 	/* re-enable BL/FDA Exhaust interrupts. */
 	if (fd_free_count) {
-		tc_writel(tc_readl(&tr->Int_En) | Int_FDAExEn, &tr->Int_En);
+		struct tc35815_regs __iomem *tr =
+			(struct tc35815_regs __iomem *)dev->base_addr;
+		u32 en, en_old = tc_readl(&tr->Int_En);
+		en = en_old | Int_FDAExEn;
 		if (buf_free_count)
-			tc_writel(tc_readl(&tr->Int_En) | Int_BLExEn, &tr->Int_En);
+			en |= Int_BLExEn;
+		if (en != en_old)
+			tc_writel(en, &tr->Int_En);
 	}
+#ifdef TC35815_NAPI
+	return received;
+#endif
 }
 
+#ifdef TC35815_NAPI
+static int
+tc35815_poll(struct net_device *dev, int *budget)
+{
+	struct tc35815_local *lp = dev->priv;
+	struct tc35815_regs __iomem *tr =
+		(struct tc35815_regs __iomem *)dev->base_addr;
+	int limit = min(*budget, dev->quota);
+	int received = 0, handled;
+	u32 status;
+
+	spin_lock(&lp->lock);
+	status = tc_readl(&tr->Int_Src);
+	do {
+		tc_writel(status, &tr->Int_Src);	/* write to clear */
+
+		handled = tc35815_do_interrupt(dev, status, limit);
+		if (handled >= 0) {
+			received += handled;
+			limit -= handled;
+			if (limit <= 0)
+				break;
+		}
+		status = tc_readl(&tr->Int_Src);
+	} while (status);
+	spin_unlock(&lp->lock);
+
+	dev->quota -= received;
+	*budget -= received;
+	if (limit <= 0)
+		return 1;
+
+	netif_rx_complete(dev);
+	/* enable interrupts */
+	tc_writel(tc_readl(&tr->DMA_Ctl) & ~DMA_IntMask, &tr->DMA_Ctl);
+	return 0;
+}
+#endif
+
 #ifdef NO_CHECK_CARRIER
 #define TX_STA_ERR	(Tx_ExColl|Tx_Under|Tx_Defer|Tx_LateColl|Tx_TxPar|Tx_SQErr)
 #else
@@ -1265,9 +1743,17 @@
 	if (status & Tx_TxColl_MASK)
 		lp->stats.collisions += status & Tx_TxColl_MASK;
 
-	/* WORKAROUND: ignore LostCrS in full duplex operation */
-	if (lp->fullduplex)
+#ifndef NO_CHECK_CARRIER
+	/* TX4939 does not have NCarr */
+	if (lp->boardtype == TC35815_TX4939)
 		status &= ~Tx_NCarr;
+#ifdef WORKAROUND_LOSTCAR
+	/* WORKAROUND: ignore LostCrS in full duplex operation */
+	if ((lp->timer_state != asleep && lp->timer_state != lcheck)
+	    || lp->fullduplex)
+		status &= ~Tx_NCarr;
+#endif
+#endif
 
 	if (!(status & TX_STA_ERR)) {
 		/* no error. */
@@ -1283,6 +1769,15 @@
 	if (status & Tx_Under) {
 		lp->stats.tx_fifo_errors++;
 		msg = "Tx FIFO Underrun.";
+		if (lp->lstats.tx_underrun < TX_THRESHOLD_KEEP_LIMIT) {
+			lp->lstats.tx_underrun++;
+			if (lp->lstats.tx_underrun >= TX_THRESHOLD_KEEP_LIMIT) {
+				struct tc35815_regs __iomem *tr =
+					(struct tc35815_regs __iomem *)dev->base_addr;
+				tc_writel(TX_THRESHOLD_MAX, &tr->TxThrsh);
+				msg = "Tx FIFO Underrun.Change Tx threshold to max.";
+			}
+		}
 	}
 	if (status & Tx_Defer) {
 		lp->stats.tx_fifo_errors++;
@@ -1306,18 +1801,19 @@
 		lp->stats.tx_heartbeat_errors++;
 		msg = "Signal Quality Error.";
 	}
-	if (msg)
+	if (msg && netif_msg_tx_err(lp))
 		printk(KERN_WARNING "%s: %s (%#x)\n", dev->name, msg, status);
 }
 
+/* This handles TX complete events posted by the device
+ * via interrupts.
+ */
 static void
 tc35815_txdone(struct net_device *dev)
 {
 	struct tc35815_local *lp = dev->priv;
-	struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr;
 	struct TxFD *txfd;
 	unsigned int fdctl;
-	int num_done = 0;
 
 	txfd = &lp->tfd_base[lp->tfd_end];
 	while (lp->tfd_start != lp->tfd_end &&
@@ -1325,38 +1821,61 @@
 		int status = le32_to_cpu(txfd->fd.FDStat);
 		struct sk_buff *skb;
 		unsigned long fdnext = le32_to_cpu(txfd->fd.FDNext);
+		u32 fdsystem = le32_to_cpu(txfd->fd.FDSystem);
 
-		if (tc35815_debug > 2) {
+		if (netif_msg_tx_done(lp)) {
 			printk("%s: complete TxFD.\n", dev->name);
 			dump_txfd(txfd);
 		}
 		tc35815_check_tx_stat(dev, status);
 
-		skb = (struct sk_buff *)le32_to_cpu(txfd->fd.FDSystem);
-		if (skb) {
-			dev_kfree_skb_any(skb);
+		skb = fdsystem != 0xffffffff ?
+			lp->tx_skbs[fdsystem].skb : NULL;
+#ifdef DEBUG
+		if (lp->tx_skbs[lp->tfd_end].skb != skb) {
+			printk("%s: tx_skbs mismatch.\n", dev->name);
+			panic_queues(dev);
 		}
-		txfd->fd.FDSystem = cpu_to_le32(0);
+#else
+		BUG_ON(lp->tx_skbs[lp->tfd_end].skb != skb);
+#endif
+		if (skb) {
+			lp->stats.tx_bytes += skb->len;
+			pci_unmap_single(lp->pci_dev, lp->tx_skbs[lp->tfd_end].skb_dma, skb->len, PCI_DMA_TODEVICE);
+			lp->tx_skbs[lp->tfd_end].skb = NULL;
+			lp->tx_skbs[lp->tfd_end].skb_dma = 0;
+#ifdef TC35815_NAPI
+			dev_kfree_skb_any(skb);
+#else
+			dev_kfree_skb_irq(skb);
+#endif
+		}
+		txfd->fd.FDSystem = cpu_to_le32(0xffffffff);
 
-		num_done++;
 		lp->tfd_end = (lp->tfd_end + 1) % TX_FD_NUM;
 		txfd = &lp->tfd_base[lp->tfd_end];
-		if ((fdnext & ~FD_Next_EOL) != virt_to_bus(txfd)) {
+#ifdef DEBUG
+		if ((fdnext & ~FD_Next_EOL) != fd_virt_to_bus(lp, txfd)) {
 			printk("%s: TxFD FDNext invalid.\n", dev->name);
 			panic_queues(dev);
 		}
+#endif
 		if (fdnext & FD_Next_EOL) {
 			/* DMA Transmitter has been stopping... */
 			if (lp->tfd_end != lp->tfd_start) {
+				struct tc35815_regs __iomem *tr =
+					(struct tc35815_regs __iomem *)dev->base_addr;
 				int head = (lp->tfd_start + TX_FD_NUM - 1) % TX_FD_NUM;
 				struct TxFD* txhead = &lp->tfd_base[head];
 				int qlen = (lp->tfd_start + TX_FD_NUM
 					    - lp->tfd_end) % TX_FD_NUM;
 
+#ifdef DEBUG
 				if (!(le32_to_cpu(txfd->fd.FDCtl) & FD_CownsFD)) {
 					printk("%s: TxFD FDCtl invalid.\n", dev->name);
 					panic_queues(dev);
 				}
+#endif
 				/* log max queue length */
 				if (lp->lstats.max_tx_qlen < qlen)
 					lp->lstats.max_tx_qlen = qlen;
@@ -1367,21 +1886,23 @@
 #ifdef GATHER_TXINT
 				txhead->fd.FDCtl |= cpu_to_le32(FD_FrmOpt_IntTx);
 #endif
-				if (tc35815_debug > 2) {
+				if (netif_msg_tx_queued(lp)) {
 					printk("%s: start TxFD on queue.\n",
 					       dev->name);
 					dump_txfd(txfd);
 				}
-				tc_writel(virt_to_bus(txfd), &tr->TxFrmPtr);
+				tc_writel(fd_virt_to_bus(lp, txfd), &tr->TxFrmPtr);
 			}
 			break;
 		}
 	}
 
-	if (num_done > 0 && lp->tbusy) {
-		lp->tbusy = 0;
-		netif_start_queue(dev);
-	}
+	/* If we had stopped the queue due to a "tx full"
+	 * condition, and space has now been made available,
+	 * wake up the queue.
+	 */
+	if (netif_queue_stopped(dev) && ! tc35815_tx_full(dev))
+		netif_wake_queue(dev);
 }
 
 /* The inverse routine to tc35815_open(). */
@@ -1389,18 +1910,18 @@
 tc35815_close(struct net_device *dev)
 {
 	struct tc35815_local *lp = dev->priv;
-
-	lp->tbusy = 1;
 	netif_stop_queue(dev);
 
 	/* Flush the Tx and disable Rx here. */
 
+	del_timer(&lp->timer);		/* Kill if running	*/
 	tc35815_chip_reset(dev);
 	free_irq(dev->irq, dev);
 
 	tc35815_free_queues(dev);
 
 	return 0;
+
 }
 
 /*
@@ -1410,29 +1931,29 @@
 static struct net_device_stats *tc35815_get_stats(struct net_device *dev)
 {
 	struct tc35815_local *lp = dev->priv;
-	struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr;
-	unsigned long flags;
-
+	struct tc35815_regs __iomem *tr =
+		(struct tc35815_regs __iomem *)dev->base_addr;
 	if (netif_running(dev)) {
-		spin_lock_irqsave(&lp->lock, flags);
 		/* Update the statistics from the device registers. */
 		lp->stats.rx_missed_errors = tc_readl(&tr->Miss_Cnt);
-		spin_unlock_irqrestore(&lp->lock, flags);
 	}
 
 	return &lp->stats;
 }
 
-static void tc35815_set_cam_entry(struct tc35815_regs *tr, int index, unsigned char *addr)
+static void tc35815_set_cam_entry(struct net_device *dev, int index, unsigned char *addr)
 {
+	struct tc35815_local *lp = dev->priv;
+	struct tc35815_regs __iomem *tr =
+		(struct tc35815_regs __iomem *)dev->base_addr;
 	int cam_index = index * 6;
-	unsigned long cam_data;
-	unsigned long saved_addr;
+	u32 cam_data;
+	u32 saved_addr;
 	saved_addr = tc_readl(&tr->CAM_Adr);
 
-	if (tc35815_debug > 1) {
+	if (netif_msg_hw(lp)) {
 		int i;
-		printk(KERN_DEBUG "%s: CAM %d:", cardname, index);
+		printk(KERN_DEBUG "%s: CAM %d:", dev->name, index);
 		for (i = 0; i < 6; i++)
 			printk(" %02x", addr[i]);
 		printk("\n");
@@ -1459,14 +1980,6 @@
 		tc_writel(cam_data, &tr->CAM_Data);
 	}
 
-	if (tc35815_debug > 2) {
-		int i;
-		for (i = cam_index / 4; i < cam_index / 4 + 2; i++) {
-			tc_writel(i * 4, &tr->CAM_Adr);
-			printk("CAM 0x%x: %08lx",
-			       i * 4, tc_readl(&tr->CAM_Data));
-		}
-	}
 	tc_writel(saved_addr, &tr->CAM_Adr);
 }
 
@@ -1481,10 +1994,19 @@
 static void
 tc35815_set_multicast_list(struct net_device *dev)
 {
-	struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr;
+	struct tc35815_regs __iomem *tr =
+		(struct tc35815_regs __iomem *)dev->base_addr;
 
 	if (dev->flags&IFF_PROMISC)
 	{
+#ifdef WORKAROUND_100HALF_PROMISC
+		/* With some (all?) 100MHalf HUB, controller will hang
+		 * if we enabled promiscuous mode before linkup... */
+		struct tc35815_local *lp = dev->priv;
+		int pid = lp->phy_addr;
+		if (!(tc_mdio_read(dev, pid, MII_BMSR) & BMSR_LSTATUS))
+			return;
+#endif
 		/* Enable promiscuous mode */
 		tc_writel(CAM_CompEn | CAM_BroadAcc | CAM_GroupAcc | CAM_StationAcc, &tr->CAM_Ctl);
 	}
@@ -1506,7 +2028,7 @@
 			if (!cur_addr)
 				break;
 			/* entry 0,1 is reserved. */
-			tc35815_set_cam_entry(tr, i + 2, cur_addr->dmi_addr);
+			tc35815_set_cam_entry(dev, i + 2, cur_addr->dmi_addr);
 			ena_bits |= CAM_Ena_Bit(i + 2);
 		}
 		tc_writel(ena_bits, &tr->CAM_Ena);
@@ -1518,122 +2040,753 @@
 	}
 }
 
-static unsigned long tc_phy_read(struct net_device *dev, struct tc35815_regs *tr, int phy, int phy_reg)
+static void tc35815_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
 {
 	struct tc35815_local *lp = dev->priv;
-	unsigned long data;
-	unsigned long flags;
+	strcpy(info->driver, MODNAME);
+	strcpy(info->version, DRV_VERSION);
+	strcpy(info->bus_info, pci_name(lp->pci_dev));
+}
 
-	spin_lock_irqsave(&lp->lock, flags);
+static int tc35815_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct tc35815_local *lp = dev->priv;
+	spin_lock_irq(&lp->lock);
+	mii_ethtool_gset(&lp->mii, cmd);
+	spin_unlock_irq(&lp->lock);
+	return 0;
+}
 
-	tc_writel(MD_CA_Busy | (phy << 5) | phy_reg, &tr->MD_CA);
+static int tc35815_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct tc35815_local *lp = dev->priv;
+	int rc;
+#if 1	/* use our negotiation method... */
+	/* Verify the settings we care about. */
+	if (cmd->autoneg != AUTONEG_ENABLE &&
+	    cmd->autoneg != AUTONEG_DISABLE)
+		return -EINVAL;
+	if (cmd->autoneg == AUTONEG_DISABLE &&
+	    ((cmd->speed != SPEED_100 &&
+	      cmd->speed != SPEED_10) ||
+	     (cmd->duplex != DUPLEX_HALF &&
+	      cmd->duplex != DUPLEX_FULL)))
+		return -EINVAL;
+
+	/* Ok, do it to it. */
+	spin_lock_irq(&lp->lock);
+	del_timer(&lp->timer);
+	tc35815_start_auto_negotiation(dev, cmd);
+	spin_unlock_irq(&lp->lock);
+	rc = 0;
+#else
+	spin_lock_irq(&lp->lock);
+	rc = mii_ethtool_sset(&lp->mii, cmd);
+	spin_unlock_irq(&lp->lock);
+#endif
+	return rc;
+}
+
+static int tc35815_nway_reset(struct net_device *dev)
+{
+	struct tc35815_local *lp = dev->priv;
+	int rc;
+	spin_lock_irq(&lp->lock);
+	rc = mii_nway_restart(&lp->mii);
+	spin_unlock_irq(&lp->lock);
+	return rc;
+}
+
+static u32 tc35815_get_link(struct net_device *dev)
+{
+	struct tc35815_local *lp = dev->priv;
+	int rc;
+	spin_lock_irq(&lp->lock);
+	rc = mii_link_ok(&lp->mii);
+	spin_unlock_irq(&lp->lock);
+	return rc;
+}
+
+static u32 tc35815_get_msglevel(struct net_device *dev)
+{
+	struct tc35815_local *lp = dev->priv;
+	return lp->msg_enable;
+}
+
+static void tc35815_set_msglevel(struct net_device *dev, u32 datum)
+{
+	struct tc35815_local *lp = dev->priv;
+	lp->msg_enable = datum;
+}
+
+static int tc35815_get_stats_count(struct net_device *dev)
+{
+	struct tc35815_local *lp = dev->priv;
+	return sizeof(lp->lstats) / sizeof(int);
+}
+
+static void tc35815_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data)
+{
+	struct tc35815_local *lp = dev->priv;
+	data[0] = lp->lstats.max_tx_qlen;
+	data[1] = lp->lstats.tx_ints;
+	data[2] = lp->lstats.rx_ints;
+	data[3] = lp->lstats.tx_underrun;
+}
+
+static struct {
+	const char str[ETH_GSTRING_LEN];
+} ethtool_stats_keys[] = {
+	{ "max_tx_qlen" },
+	{ "tx_ints" },
+	{ "rx_ints" },
+	{ "tx_underrun" },
+};
+
+static void tc35815_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+	memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys));
+}
+
+static const struct ethtool_ops tc35815_ethtool_ops = {
+	.get_drvinfo		= tc35815_get_drvinfo,
+	.get_settings		= tc35815_get_settings,
+	.set_settings		= tc35815_set_settings,
+	.nway_reset		= tc35815_nway_reset,
+	.get_link		= tc35815_get_link,
+	.get_msglevel		= tc35815_get_msglevel,
+	.set_msglevel		= tc35815_set_msglevel,
+	.get_strings		= tc35815_get_strings,
+	.get_stats_count	= tc35815_get_stats_count,
+	.get_ethtool_stats	= tc35815_get_ethtool_stats,
+	.get_perm_addr		= ethtool_op_get_perm_addr,
+};
+
+static int tc35815_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct tc35815_local *lp = dev->priv;
+	int rc;
+
+	if (!netif_running(dev))
+		return -EINVAL;
+
+	spin_lock_irq(&lp->lock);
+	rc = generic_mii_ioctl(&lp->mii, if_mii(rq), cmd, NULL);
+	spin_unlock_irq(&lp->lock);
+
+	return rc;
+}
+
+static int tc_mdio_read(struct net_device *dev, int phy_id, int location)
+{
+	struct tc35815_regs __iomem *tr =
+		(struct tc35815_regs __iomem *)dev->base_addr;
+	u32 data;
+	tc_writel(MD_CA_Busy | (phy_id << 5) | location, &tr->MD_CA);
 	while (tc_readl(&tr->MD_CA) & MD_CA_Busy)
 		;
 	data = tc_readl(&tr->MD_Data);
-	spin_unlock_irqrestore(&lp->lock, flags);
-	return data;
+	return data & 0xffff;
 }
 
-static void tc_phy_write(struct net_device *dev, unsigned long d, struct tc35815_regs *tr, int phy, int phy_reg)
+static void tc_mdio_write(struct net_device *dev, int phy_id, int location,
+			  int val)
 {
-	struct tc35815_local *lp = dev->priv;
-	unsigned long flags;
-
-	spin_lock_irqsave(&lp->lock, flags);
-
-	tc_writel(d, &tr->MD_Data);
-	tc_writel(MD_CA_Busy | MD_CA_Wr | (phy << 5) | phy_reg, &tr->MD_CA);
+	struct tc35815_regs __iomem *tr =
+		(struct tc35815_regs __iomem *)dev->base_addr;
+	tc_writel(val, &tr->MD_Data);
+	tc_writel(MD_CA_Busy | MD_CA_Wr | (phy_id << 5) | location, &tr->MD_CA);
 	while (tc_readl(&tr->MD_CA) & MD_CA_Busy)
 		;
-	spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+/* Auto negotiation.  The scheme is very simple.  We have a timer routine
+ * that keeps watching the auto negotiation process as it progresses.
+ * The DP83840 is first told to start doing it's thing, we set up the time
+ * and place the timer state machine in it's initial state.
+ *
+ * Here the timer peeks at the DP83840 status registers at each click to see
+ * if the auto negotiation has completed, we assume here that the DP83840 PHY
+ * will time out at some point and just tell us what (didn't) happen.  For
+ * complete coverage we only allow so many of the ticks at this level to run,
+ * when this has expired we print a warning message and try another strategy.
+ * This "other" strategy is to force the interface into various speed/duplex
+ * configurations and we stop when we see a link-up condition before the
+ * maximum number of "peek" ticks have occurred.
+ *
+ * Once a valid link status has been detected we configure the BigMAC and
+ * the rest of the Happy Meal to speak the most efficient protocol we could
+ * get a clean link for.  The priority for link configurations, highest first
+ * is:
+ *                 100 Base-T Full Duplex
+ *                 100 Base-T Half Duplex
+ *                 10 Base-T Full Duplex
+ *                 10 Base-T Half Duplex
+ *
+ * We start a new timer now, after a successful auto negotiation status has
+ * been detected.  This timer just waits for the link-up bit to get set in
+ * the BMCR of the DP83840.  When this occurs we print a kernel log message
+ * describing the link type in use and the fact that it is up.
+ *
+ * If a fatal error of some sort is signalled and detected in the interrupt
+ * service routine, and the chip is reset, or the link is ifconfig'd down
+ * and then back up, this entire process repeats itself all over again.
+ */
+/* Note: Above comments are come from sunhme driver. */
+
+static int tc35815_try_next_permutation(struct net_device *dev)
+{
+	struct tc35815_local *lp = dev->priv;
+	int pid = lp->phy_addr;
+	unsigned short bmcr;
+
+	bmcr = tc_mdio_read(dev, pid, MII_BMCR);
+
+	/* Downgrade from full to half duplex.  Only possible via ethtool.  */
+	if (bmcr & BMCR_FULLDPLX) {
+		bmcr &= ~BMCR_FULLDPLX;
+		printk(KERN_DEBUG "%s: try next permutation (BMCR %x)\n", dev->name, bmcr);
+		tc_mdio_write(dev, pid, MII_BMCR, bmcr);
+		return 0;
+	}
+
+	/* Downgrade from 100 to 10. */
+	if (bmcr & BMCR_SPEED100) {
+		bmcr &= ~BMCR_SPEED100;
+		printk(KERN_DEBUG "%s: try next permutation (BMCR %x)\n", dev->name, bmcr);
+		tc_mdio_write(dev, pid, MII_BMCR, bmcr);
+		return 0;
+	}
+
+	/* We've tried everything. */
+	return -1;
+}
+
+static void
+tc35815_display_link_mode(struct net_device *dev)
+{
+	struct tc35815_local *lp = dev->priv;
+	int pid = lp->phy_addr;
+	unsigned short lpa, bmcr;
+	char *speed = "", *duplex = "";
+
+	lpa = tc_mdio_read(dev, pid, MII_LPA);
+	bmcr = tc_mdio_read(dev, pid, MII_BMCR);
+	if (options.speed ? (bmcr & BMCR_SPEED100) : (lpa & (LPA_100HALF | LPA_100FULL)))
+		speed = "100Mb/s";
+	else
+		speed = "10Mb/s";
+	if (options.duplex ? (bmcr & BMCR_FULLDPLX) : (lpa & (LPA_100FULL | LPA_10FULL)))
+		duplex = "Full Duplex";
+	else
+		duplex = "Half Duplex";
+
+	if (netif_msg_link(lp))
+		printk(KERN_INFO "%s: Link is up at %s, %s.\n",
+		       dev->name, speed, duplex);
+	printk(KERN_DEBUG "%s: MII BMCR %04x BMSR %04x LPA %04x\n",
+	       dev->name,
+	       bmcr, tc_mdio_read(dev, pid, MII_BMSR), lpa);
+}
+
+static void tc35815_display_forced_link_mode(struct net_device *dev)
+{
+	struct tc35815_local *lp = dev->priv;
+	int pid = lp->phy_addr;
+	unsigned short bmcr;
+	char *speed = "", *duplex = "";
+
+	bmcr = tc_mdio_read(dev, pid, MII_BMCR);
+	if (bmcr & BMCR_SPEED100)
+		speed = "100Mb/s";
+	else
+		speed = "10Mb/s";
+	if (bmcr & BMCR_FULLDPLX)
+		duplex = "Full Duplex.\n";
+	else
+		duplex = "Half Duplex.\n";
+
+	if (netif_msg_link(lp))
+		printk(KERN_INFO "%s: Link has been forced up at %s, %s",
+		       dev->name, speed, duplex);
+}
+
+static void tc35815_set_link_modes(struct net_device *dev)
+{
+	struct tc35815_local *lp = dev->priv;
+	struct tc35815_regs __iomem *tr =
+		(struct tc35815_regs __iomem *)dev->base_addr;
+	int pid = lp->phy_addr;
+	unsigned short bmcr, lpa;
+	int speed;
+
+	if (lp->timer_state == arbwait) {
+		lpa = tc_mdio_read(dev, pid, MII_LPA);
+		bmcr = tc_mdio_read(dev, pid, MII_BMCR);
+		printk(KERN_DEBUG "%s: MII BMCR %04x BMSR %04x LPA %04x\n",
+		       dev->name,
+		       bmcr, tc_mdio_read(dev, pid, MII_BMSR), lpa);
+		if (!(lpa & (LPA_10HALF | LPA_10FULL |
+			     LPA_100HALF | LPA_100FULL))) {
+			/* fall back to 10HALF */
+			printk(KERN_INFO "%s: bad ability %04x - falling back to 10HD.\n",
+			       dev->name, lpa);
+			lpa = LPA_10HALF;
+		}
+		if (options.duplex ? (bmcr & BMCR_FULLDPLX) : (lpa & (LPA_100FULL | LPA_10FULL)))
+			lp->fullduplex = 1;
+		else
+			lp->fullduplex = 0;
+		if (options.speed ? (bmcr & BMCR_SPEED100) : (lpa & (LPA_100HALF | LPA_100FULL)))
+			speed = 100;
+		else
+			speed = 10;
+	} else {
+		/* Forcing a link mode. */
+		bmcr = tc_mdio_read(dev, pid, MII_BMCR);
+		if (bmcr & BMCR_FULLDPLX)
+			lp->fullduplex = 1;
+		else
+			lp->fullduplex = 0;
+		if (bmcr & BMCR_SPEED100)
+			speed = 100;
+		else
+			speed = 10;
+	}
+
+	tc_writel(tc_readl(&tr->MAC_Ctl) | MAC_HaltReq, &tr->MAC_Ctl);
+	if (lp->fullduplex) {
+		tc_writel(tc_readl(&tr->MAC_Ctl) | MAC_FullDup, &tr->MAC_Ctl);
+	} else {
+		tc_writel(tc_readl(&tr->MAC_Ctl) & ~MAC_FullDup, &tr->MAC_Ctl);
+	}
+	tc_writel(tc_readl(&tr->MAC_Ctl) & ~MAC_HaltReq, &tr->MAC_Ctl);
+
+	/* TX4939 PCFG.SPEEDn bit will be changed on NETDEV_CHANGE event. */
+
+#ifndef NO_CHECK_CARRIER
+	/* TX4939 does not have EnLCarr */
+	if (lp->boardtype != TC35815_TX4939) {
+#ifdef WORKAROUND_LOSTCAR
+		/* WORKAROUND: enable LostCrS only if half duplex operation */
+		if (!lp->fullduplex && lp->boardtype != TC35815_TX4939)
+			tc_writel(tc_readl(&tr->Tx_Ctl) | Tx_EnLCarr, &tr->Tx_Ctl);
+#endif
+	}
+#endif
+	lp->mii.full_duplex = lp->fullduplex;
+}
+
+static void tc35815_timer(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct tc35815_local *lp = dev->priv;
+	int pid = lp->phy_addr;
+	unsigned short bmsr, bmcr, lpa;
+	int restart_timer = 0;
+
+	spin_lock_irq(&lp->lock);
+
+	lp->timer_ticks++;
+	switch (lp->timer_state) {
+	case arbwait:
+		/*
+		 * Only allow for 5 ticks, thats 10 seconds and much too
+		 * long to wait for arbitration to complete.
+		 */
+		/* TC35815 need more times... */
+		if (lp->timer_ticks >= 10) {
+			/* Enter force mode. */
+			if (!options.doforce) {
+				printk(KERN_NOTICE "%s: Auto-Negotiation unsuccessful,"
+				       " cable probblem?\n", dev->name);
+				/* Try to restart the adaptor. */
+				tc35815_restart(dev);
+				goto out;
+			}
+			printk(KERN_NOTICE "%s: Auto-Negotiation unsuccessful,"
+			       " trying force link mode\n", dev->name);
+			printk(KERN_DEBUG "%s: BMCR %x BMSR %x\n", dev->name,
+			       tc_mdio_read(dev, pid, MII_BMCR),
+			       tc_mdio_read(dev, pid, MII_BMSR));
+			bmcr = BMCR_SPEED100;
+			tc_mdio_write(dev, pid, MII_BMCR, bmcr);
+
+			/*
+			 * OK, seems we need do disable the transceiver
+			 * for the first tick to make sure we get an
+			 * accurate link state at the second tick.
+			 */
+
+			lp->timer_state = ltrywait;
+			lp->timer_ticks = 0;
+			restart_timer = 1;
+		} else {
+			/* Anything interesting happen? */
+			bmsr = tc_mdio_read(dev, pid, MII_BMSR);
+			if (bmsr & BMSR_ANEGCOMPLETE) {
+				/* Just what we've been waiting for... */
+				tc35815_set_link_modes(dev);
+
+				/*
+				 * Success, at least so far, advance our state
+				 * engine.
+				 */
+				lp->timer_state = lupwait;
+				restart_timer = 1;
+			} else {
+				restart_timer = 1;
+			}
+		}
+		break;
+
+	case lupwait:
+		/*
+		 * Auto negotiation was successful and we are awaiting a
+		 * link up status.  I have decided to let this timer run
+		 * forever until some sort of error is signalled, reporting
+		 * a message to the user at 10 second intervals.
+		 */
+		bmsr = tc_mdio_read(dev, pid, MII_BMSR);
+		if (bmsr & BMSR_LSTATUS) {
+			/*
+			 * Wheee, it's up, display the link mode in use and put
+			 * the timer to sleep.
+			 */
+			tc35815_display_link_mode(dev);
+			netif_carrier_on(dev);
+#ifdef WORKAROUND_100HALF_PROMISC
+			/* delayed promiscuous enabling */
+			if (dev->flags & IFF_PROMISC)
+				tc35815_set_multicast_list(dev);
+#endif
+#if 1
+			lp->saved_lpa = tc_mdio_read(dev, pid, MII_LPA);
+			lp->timer_state = lcheck;
+			restart_timer = 1;
+#else
+			lp->timer_state = asleep;
+			restart_timer = 0;
+#endif
+		} else {
+			if (lp->timer_ticks >= 10) {
+				printk(KERN_NOTICE "%s: Auto negotiation successful, link still "
+				       "not completely up.\n", dev->name);
+				lp->timer_ticks = 0;
+				restart_timer = 1;
+			} else {
+				restart_timer = 1;
+			}
+		}
+		break;
+
+	case ltrywait:
+		/*
+		 * Making the timeout here too long can make it take
+		 * annoyingly long to attempt all of the link mode
+		 * permutations, but then again this is essentially
+		 * error recovery code for the most part.
+		 */
+		bmsr = tc_mdio_read(dev, pid, MII_BMSR);
+		bmcr = tc_mdio_read(dev, pid, MII_BMCR);
+		if (lp->timer_ticks == 1) {
+			/*
+			 * Re-enable transceiver, we'll re-enable the
+			 * transceiver next tick, then check link state
+			 * on the following tick.
+			 */
+			restart_timer = 1;
+			break;
+		}
+		if (lp->timer_ticks == 2) {
+			restart_timer = 1;
+			break;
+		}
+		if (bmsr & BMSR_LSTATUS) {
+			/* Force mode selection success. */
+			tc35815_display_forced_link_mode(dev);
+			netif_carrier_on(dev);
+			tc35815_set_link_modes(dev);
+#ifdef WORKAROUND_100HALF_PROMISC
+			/* delayed promiscuous enabling */
+			if (dev->flags & IFF_PROMISC)
+				tc35815_set_multicast_list(dev);
+#endif
+#if 1
+			lp->saved_lpa = tc_mdio_read(dev, pid, MII_LPA);
+			lp->timer_state = lcheck;
+			restart_timer = 1;
+#else
+			lp->timer_state = asleep;
+			restart_timer = 0;
+#endif
+		} else {
+			if (lp->timer_ticks >= 4) { /* 6 seconds or so... */
+				int ret;
+
+				ret = tc35815_try_next_permutation(dev);
+				if (ret == -1) {
+					/*
+					 * Aieee, tried them all, reset the
+					 * chip and try all over again.
+					 */
+					printk(KERN_NOTICE "%s: Link down, "
+					       "cable problem?\n",
+					       dev->name);
+
+					/* Try to restart the adaptor. */
+					tc35815_restart(dev);
+					goto out;
+				}
+				lp->timer_ticks = 0;
+				restart_timer = 1;
+			} else {
+				restart_timer = 1;
+			}
+		}
+		break;
+
+	case lcheck:
+		bmcr = tc_mdio_read(dev, pid, MII_BMCR);
+		lpa = tc_mdio_read(dev, pid, MII_LPA);
+		if (bmcr & (BMCR_PDOWN | BMCR_ISOLATE | BMCR_RESET)) {
+			printk(KERN_ERR "%s: PHY down? (BMCR %x)\n", dev->name,
+			       bmcr);
+		} else if ((lp->saved_lpa ^ lpa) &
+			   (LPA_100FULL|LPA_100HALF|LPA_10FULL|LPA_10HALF)) {
+			printk(KERN_NOTICE "%s: link status changed"
+			       " (BMCR %x LPA %x->%x)\n", dev->name,
+			       bmcr, lp->saved_lpa, lpa);
+		} else {
+			/* go on */
+			restart_timer = 1;
+			break;
+		}
+		/* Try to restart the adaptor. */
+		tc35815_restart(dev);
+		goto out;
+
+	case asleep:
+	default:
+		/* Can't happens.... */
+		printk(KERN_ERR "%s: Aieee, link timer is asleep but we got "
+		       "one anyways!\n", dev->name);
+		restart_timer = 0;
+		lp->timer_ticks = 0;
+		lp->timer_state = asleep; /* foo on you */
+		break;
+	}
+
+	if (restart_timer) {
+		lp->timer.expires = jiffies + msecs_to_jiffies(1200);
+		add_timer(&lp->timer);
+	}
+out:
+	spin_unlock_irq(&lp->lock);
+}
+
+static void tc35815_start_auto_negotiation(struct net_device *dev,
+					   struct ethtool_cmd *ep)
+{
+	struct tc35815_local *lp = dev->priv;
+	int pid = lp->phy_addr;
+	unsigned short bmsr, bmcr, advertize;
+	int timeout;
+
+	netif_carrier_off(dev);
+	bmsr = tc_mdio_read(dev, pid, MII_BMSR);
+	bmcr = tc_mdio_read(dev, pid, MII_BMCR);
+	advertize = tc_mdio_read(dev, pid, MII_ADVERTISE);
+
+	if (ep == NULL || ep->autoneg == AUTONEG_ENABLE) {
+		if (options.speed || options.duplex) {
+			/* Advertise only specified configuration. */
+			advertize &= ~(ADVERTISE_10HALF |
+				       ADVERTISE_10FULL |
+				       ADVERTISE_100HALF |
+				       ADVERTISE_100FULL);
+			if (options.speed != 10) {
+				if (options.duplex != 1)
+					advertize |= ADVERTISE_100FULL;
+				if (options.duplex != 2)
+					advertize |= ADVERTISE_100HALF;
+			}
+			if (options.speed != 100) {
+				if (options.duplex != 1)
+					advertize |= ADVERTISE_10FULL;
+				if (options.duplex != 2)
+					advertize |= ADVERTISE_10HALF;
+			}
+			if (options.speed == 100)
+				bmcr |= BMCR_SPEED100;
+			else if (options.speed == 10)
+				bmcr &= ~BMCR_SPEED100;
+			if (options.duplex == 2)
+				bmcr |= BMCR_FULLDPLX;
+			else if (options.duplex == 1)
+				bmcr &= ~BMCR_FULLDPLX;
+		} else {
+			/* Advertise everything we can support. */
+			if (bmsr & BMSR_10HALF)
+				advertize |= ADVERTISE_10HALF;
+			else
+				advertize &= ~ADVERTISE_10HALF;
+			if (bmsr & BMSR_10FULL)
+				advertize |= ADVERTISE_10FULL;
+			else
+				advertize &= ~ADVERTISE_10FULL;
+			if (bmsr & BMSR_100HALF)
+				advertize |= ADVERTISE_100HALF;
+			else
+				advertize &= ~ADVERTISE_100HALF;
+			if (bmsr & BMSR_100FULL)
+				advertize |= ADVERTISE_100FULL;
+			else
+				advertize &= ~ADVERTISE_100FULL;
+		}
+
+		tc_mdio_write(dev, pid, MII_ADVERTISE, advertize);
+
+		/* Enable Auto-Negotiation, this is usually on already... */
+		bmcr |= BMCR_ANENABLE;
+		tc_mdio_write(dev, pid, MII_BMCR, bmcr);
+
+		/* Restart it to make sure it is going. */
+		bmcr |= BMCR_ANRESTART;
+		tc_mdio_write(dev, pid, MII_BMCR, bmcr);
+		printk(KERN_DEBUG "%s: ADVERTISE %x BMCR %x\n", dev->name, advertize, bmcr);
+
+		/* BMCR_ANRESTART self clears when the process has begun. */
+		timeout = 64;  /* More than enough. */
+		while (--timeout) {
+			bmcr = tc_mdio_read(dev, pid, MII_BMCR);
+			if (!(bmcr & BMCR_ANRESTART))
+				break; /* got it. */
+			udelay(10);
+		}
+		if (!timeout) {
+			printk(KERN_ERR "%s: TC35815 would not start auto "
+			       "negotiation BMCR=0x%04x\n",
+			       dev->name, bmcr);
+			printk(KERN_NOTICE "%s: Performing force link "
+			       "detection.\n", dev->name);
+			goto force_link;
+		} else {
+			printk(KERN_DEBUG "%s: auto negotiation started.\n", dev->name);
+			lp->timer_state = arbwait;
+		}
+	} else {
+force_link:
+		/* Force the link up, trying first a particular mode.
+		 * Either we are here at the request of ethtool or
+		 * because the Happy Meal would not start to autoneg.
+		 */
+
+		/* Disable auto-negotiation in BMCR, enable the duplex and
+		 * speed setting, init the timer state machine, and fire it off.
+		 */
+		if (ep == NULL || ep->autoneg == AUTONEG_ENABLE) {
+			bmcr = BMCR_SPEED100;
+		} else {
+			if (ep->speed == SPEED_100)
+				bmcr = BMCR_SPEED100;
+			else
+				bmcr = 0;
+			if (ep->duplex == DUPLEX_FULL)
+				bmcr |= BMCR_FULLDPLX;
+		}
+		tc_mdio_write(dev, pid, MII_BMCR, bmcr);
+
+		/* OK, seems we need do disable the transceiver for the first
+		 * tick to make sure we get an accurate link state at the
+		 * second tick.
+		 */
+		lp->timer_state = ltrywait;
+	}
+
+	del_timer(&lp->timer);
+	lp->timer_ticks = 0;
+	lp->timer.expires = jiffies + msecs_to_jiffies(1200);
+	add_timer(&lp->timer);
+}
+
+static void tc35815_find_phy(struct net_device *dev)
+{
+	struct tc35815_local *lp = dev->priv;
+	int pid = lp->phy_addr;
+	unsigned short id0;
+
+	/* find MII phy */
+	for (pid = 31; pid >= 0; pid--) {
+		id0 = tc_mdio_read(dev, pid, MII_BMSR);
+		if (id0 != 0xffff && id0 != 0x0000 &&
+		    (id0 & BMSR_RESV) != (0xffff & BMSR_RESV) /* paranoia? */
+			) {
+			lp->phy_addr = pid;
+			break;
+		}
+	}
+	if (pid < 0) {
+		printk(KERN_ERR "%s: No MII Phy found.\n",
+		       dev->name);
+		lp->phy_addr = pid = 0;
+	}
+
+	lp->mii_id[0] = tc_mdio_read(dev, pid, MII_PHYSID1);
+	lp->mii_id[1] = tc_mdio_read(dev, pid, MII_PHYSID2);
+	if (netif_msg_hw(lp))
+		printk(KERN_INFO "%s: PHY(%02x) ID %04x %04x\n", dev->name,
+		       pid, lp->mii_id[0], lp->mii_id[1]);
 }
 
 static void tc35815_phy_chip_init(struct net_device *dev)
 {
 	struct tc35815_local *lp = dev->priv;
-	struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr;
-	static int first = 1;
-	unsigned short ctl;
+	int pid = lp->phy_addr;
+	unsigned short bmcr;
+	struct ethtool_cmd ecmd, *ep;
 
-	if (first) {
-		unsigned short id0, id1;
-		int count;
-		first = 0;
-
-		/* first data written to the PHY will be an ID number */
-		tc_phy_write(dev, 0, tr, 0, MII_CONTROL);	/* ID:0 */
-#if 0
-		tc_phy_write(dev, MIICNTL_RESET, tr, 0, MII_CONTROL);
-		printk(KERN_INFO "%s: Resetting PHY...", dev->name);
-		while (tc_phy_read(dev, tr, 0, MII_CONTROL) & MIICNTL_RESET)
-			;
-		printk("\n");
-		tc_phy_write(dev, MIICNTL_AUTO|MIICNTL_SPEED|MIICNTL_FDX, tr, 0,
-			     MII_CONTROL);
-#endif
-		id0 = tc_phy_read(dev, tr, 0, MII_PHY_ID0);
-		id1 = tc_phy_read(dev, tr, 0, MII_PHY_ID1);
-		printk(KERN_DEBUG "%s: PHY ID %04x %04x\n", dev->name,
-		       id0, id1);
-		if (lp->option & TC35815_OPT_10M) {
-			lp->linkspeed = 10;
-			lp->fullduplex = (lp->option & TC35815_OPT_FULLDUP) != 0;
-		} else if (lp->option & TC35815_OPT_100M) {
-			lp->linkspeed = 100;
-			lp->fullduplex = (lp->option & TC35815_OPT_FULLDUP) != 0;
-		} else {
-			/* auto negotiation */
-			unsigned long neg_result;
-			tc_phy_write(dev, MIICNTL_AUTO | MIICNTL_RST_AUTO, tr, 0, MII_CONTROL);
-			printk(KERN_INFO "%s: Auto Negotiation...", dev->name);
-			count = 0;
-			while (!(tc_phy_read(dev, tr, 0, MII_STATUS) & MIISTAT_AUTO_DONE)) {
-				if (count++ > 5000) {
-					printk(" failed. Assume 10Mbps\n");
-					lp->linkspeed = 10;
-					lp->fullduplex = 0;
-					goto done;
-				}
-				if (count % 512 == 0)
-					printk(".");
-				mdelay(1);
-			}
-			printk(" done.\n");
-			neg_result = tc_phy_read(dev, tr, 0, MII_ANLPAR);
-			if (neg_result & (MII_AN_TX_FDX | MII_AN_TX_HDX))
-				lp->linkspeed = 100;
-			else
-				lp->linkspeed = 10;
-			if (neg_result & (MII_AN_TX_FDX | MII_AN_10_FDX))
-				lp->fullduplex = 1;
-			else
-				lp->fullduplex = 0;
-		done:
-			;
+	/* dis-isolate if needed. */
+	bmcr = tc_mdio_read(dev, pid, MII_BMCR);
+	if (bmcr & BMCR_ISOLATE) {
+		int count = 32;
+		printk(KERN_DEBUG "%s: unisolating...", dev->name);
+		tc_mdio_write(dev, pid, MII_BMCR, bmcr & ~BMCR_ISOLATE);
+		while (--count) {
+			if (!(tc_mdio_read(dev, pid, MII_BMCR) & BMCR_ISOLATE))
+				break;
+			udelay(20);
 		}
+		printk(" %s.\n", count ? "done" : "failed");
 	}
 
-	ctl = 0;
-	if (lp->linkspeed == 100)
-		ctl |= MIICNTL_SPEED;
-	if (lp->fullduplex)
-		ctl |= MIICNTL_FDX;
-	tc_phy_write(dev, ctl, tr, 0, MII_CONTROL);
-
-	if (lp->fullduplex) {
-		tc_writel(tc_readl(&tr->MAC_Ctl) | MAC_FullDup, &tr->MAC_Ctl);
+	if (options.speed && options.duplex) {
+		ecmd.autoneg = AUTONEG_DISABLE;
+		ecmd.speed = options.speed == 10 ? SPEED_10 : SPEED_100;
+		ecmd.duplex = options.duplex == 1 ? DUPLEX_HALF : DUPLEX_FULL;
+		ep = &ecmd;
+	} else {
+		ep = NULL;
 	}
+	tc35815_start_auto_negotiation(dev, ep);
 }
 
 static void tc35815_chip_reset(struct net_device *dev)
 {
-	struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr;
-
+	struct tc35815_regs __iomem *tr =
+		(struct tc35815_regs __iomem *)dev->base_addr;
+	int i;
 	/* reset the controller */
 	tc_writel(MAC_Reset, &tr->MAC_Ctl);
-	while (tc_readl(&tr->MAC_Ctl) & MAC_Reset)
-		;
-
+	udelay(4); /* 3200ns */
+	i = 0;
+	while (tc_readl(&tr->MAC_Ctl) & MAC_Reset) {
+		if (i++ > 100) {
+			printk(KERN_ERR "%s: MAC reset failed.\n", dev->name);
+			break;
+		}
+		mdelay(1);
+	}
 	tc_writel(0, &tr->MAC_Ctl);
 
 	/* initialize registers to default value */
@@ -1651,90 +2804,142 @@
 	tc_writel(0, &tr->CAM_Ena);
 	(void)tc_readl(&tr->Miss_Cnt);	/* Read to clear */
 
+	/* initialize internal SRAM */
+	tc_writel(DMA_TestMode, &tr->DMA_Ctl);
+	for (i = 0; i < 0x1000; i += 4) {
+		tc_writel(i, &tr->CAM_Adr);
+		tc_writel(0, &tr->CAM_Data);
+	}
+	tc_writel(0, &tr->DMA_Ctl);
 }
 
 static void tc35815_chip_init(struct net_device *dev)
 {
 	struct tc35815_local *lp = dev->priv;
-	struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr;
-	unsigned long flags;
+	struct tc35815_regs __iomem *tr =
+		(struct tc35815_regs __iomem *)dev->base_addr;
 	unsigned long txctl = TX_CTL_CMD;
 
 	tc35815_phy_chip_init(dev);
 
 	/* load station address to CAM */
-	tc35815_set_cam_entry(tr, CAM_ENTRY_SOURCE, dev->dev_addr);
+	tc35815_set_cam_entry(dev, CAM_ENTRY_SOURCE, dev->dev_addr);
 
 	/* Enable CAM (broadcast and unicast) */
 	tc_writel(CAM_Ena_Bit(CAM_ENTRY_SOURCE), &tr->CAM_Ena);
 	tc_writel(CAM_CompEn | CAM_BroadAcc, &tr->CAM_Ctl);
 
-	spin_lock_irqsave(&lp->lock, flags);
-
-	tc_writel(DMA_BURST_SIZE, &tr->DMA_Ctl);
-
+	/* Use DMA_RxAlign_2 to make IP header 4-byte aligned. */
+	if (HAVE_DMA_RXALIGN(lp))
+		tc_writel(DMA_BURST_SIZE | DMA_RxAlign_2, &tr->DMA_Ctl);
+	else
+		tc_writel(DMA_BURST_SIZE, &tr->DMA_Ctl);
+#ifdef TC35815_USE_PACKEDBUFFER
 	tc_writel(RxFrag_EnPack | ETH_ZLEN, &tr->RxFragSize);	/* Packing */
+#else
+	tc_writel(ETH_ZLEN, &tr->RxFragSize);
+#endif
 	tc_writel(0, &tr->TxPollCtr);	/* Batch mode */
 	tc_writel(TX_THRESHOLD, &tr->TxThrsh);
 	tc_writel(INT_EN_CMD, &tr->Int_En);
 
 	/* set queues */
-	tc_writel(virt_to_bus(lp->rfd_base), &tr->FDA_Bas);
+	tc_writel(fd_virt_to_bus(lp, lp->rfd_base), &tr->FDA_Bas);
 	tc_writel((unsigned long)lp->rfd_limit - (unsigned long)lp->rfd_base,
 		  &tr->FDA_Lim);
 	/*
 	 * Activation method:
-	 * First, enable eht MAC Transmitter and the DMA Receive circuits.
+	 * First, enable the MAC Transmitter and the DMA Receive circuits.
 	 * Then enable the DMA Transmitter and the MAC Receive circuits.
 	 */
-	tc_writel(virt_to_bus(lp->fbl_ptr), &tr->BLFrmPtr);	/* start DMA receiver */
+	tc_writel(fd_virt_to_bus(lp, lp->fbl_ptr), &tr->BLFrmPtr);	/* start DMA receiver */
 	tc_writel(RX_CTL_CMD, &tr->Rx_Ctl);	/* start MAC receiver */
+
 	/* start MAC transmitter */
+#ifndef NO_CHECK_CARRIER
+	/* TX4939 does not have EnLCarr */
+	if (lp->boardtype == TC35815_TX4939)
+		txctl &= ~Tx_EnLCarr;
+#ifdef WORKAROUND_LOSTCAR
 	/* WORKAROUND: ignore LostCrS in full duplex operation */
-	if (lp->fullduplex)
-		txctl = TX_CTL_CMD & ~Tx_EnLCarr;
+	if ((lp->timer_state != asleep && lp->timer_state != lcheck) ||
+	    lp->fullduplex)
+		txctl &= ~Tx_EnLCarr;
+#endif
+#endif /* !NO_CHECK_CARRIER */
 #ifdef GATHER_TXINT
 	txctl &= ~Tx_EnComp;	/* disable global tx completion int. */
 #endif
 	tc_writel(txctl, &tr->Tx_Ctl);
-#if 0	/* No need to polling */
-	tc_writel(virt_to_bus(lp->tfd_base), &tr->TxFrmPtr);	/* start DMA transmitter */
-#endif
-	spin_unlock_irqrestore(&lp->lock, flags);
 }
 
-static struct pci_driver tc35815_driver = {
-	.name = TC35815_MODULE_NAME,
-	.probe = tc35815_probe,
-	.remove = NULL,
-	.id_table = tc35815_pci_tbl,
+#ifdef CONFIG_PM
+static int tc35815_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct tc35815_local *lp = dev->priv;
+	unsigned long flags;
+
+	pci_save_state(pdev);
+	if (!netif_running(dev))
+		return 0;
+	netif_device_detach(dev);
+	spin_lock_irqsave(&lp->lock, flags);
+	del_timer(&lp->timer);		/* Kill if running	*/
+	tc35815_chip_reset(dev);
+	spin_unlock_irqrestore(&lp->lock, flags);
+	pci_set_power_state(pdev, PCI_D3hot);
+	return 0;
+}
+
+static int tc35815_resume(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct tc35815_local *lp = dev->priv;
+	unsigned long flags;
+
+	pci_restore_state(pdev);
+	if (!netif_running(dev))
+		return 0;
+	pci_set_power_state(pdev, PCI_D0);
+	spin_lock_irqsave(&lp->lock, flags);
+	tc35815_restart(dev);
+	spin_unlock_irqrestore(&lp->lock, flags);
+	netif_device_attach(dev);
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct pci_driver tc35815_pci_driver = {
+	.name		= MODNAME,
+	.id_table	= tc35815_pci_tbl,
+	.probe		= tc35815_init_one,
+	.remove		= __devexit_p(tc35815_remove_one),
+#ifdef CONFIG_PM
+	.suspend	= tc35815_suspend,
+	.resume		= tc35815_resume,
+#endif
 };
 
+module_param_named(speed, options.speed, int, 0);
+MODULE_PARM_DESC(speed, "0:auto, 10:10Mbps, 100:100Mbps");
+module_param_named(duplex, options.duplex, int, 0);
+MODULE_PARM_DESC(duplex, "0:auto, 1:half, 2:full");
+module_param_named(doforce, options.doforce, int, 0);
+MODULE_PARM_DESC(doforce, "try force link mode if auto-negotiation failed");
+
 static int __init tc35815_init_module(void)
 {
-	return pci_register_driver(&tc35815_driver);
+	return pci_register_driver(&tc35815_pci_driver);
 }
 
 static void __exit tc35815_cleanup_module(void)
 {
-	struct net_device *next_dev;
-
-	/*
-	 * TODO: implement a tc35815_driver.remove hook, and
-	 * move this code into that function.  Then, delete
-	 * all root_tc35815_dev list handling code.
-	 */
-	while (root_tc35815_dev) {
-		struct net_device *dev = root_tc35815_dev;
-		next_dev = ((struct tc35815_local *)dev->priv)->next_module;
-		iounmap((void *)(dev->base_addr));
-		unregister_netdev(dev);
-		free_netdev(dev);
-		root_tc35815_dev = next_dev;
-	}
-
-	pci_unregister_driver(&tc35815_driver);
+	pci_unregister_driver(&tc35815_pci_driver);
 }
 
 module_init(tc35815_init_module);
 module_exit(tc35815_cleanup_module);
+
+MODULE_DESCRIPTION("TOSHIBA TC35815 PCI 10M/100M Ethernet driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index 256969e..9488f49 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -40,16 +40,16 @@
 #include <linux/dma-mapping.h>
 
 #include <net/checksum.h>
+#include <net/ip.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/byteorder.h>
 #include <asm/uaccess.h>
 
-#ifdef CONFIG_SPARC64
+#ifdef CONFIG_SPARC
 #include <asm/idprom.h>
-#include <asm/oplib.h>
-#include <asm/pbm.h>
+#include <asm/prom.h>
 #endif
 
 #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
@@ -3349,7 +3349,7 @@
 			skb_reserve(copy_skb, 2);
 			skb_put(copy_skb, len);
 			pci_dma_sync_single_for_cpu(tp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE);
-			memcpy(copy_skb->data, skb->data, len);
+			skb_copy_from_linear_data(skb, copy_skb->data, len);
 			pci_dma_sync_single_for_device(tp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE);
 
 			/* We'll reuse the original ring buffer. */
@@ -3908,20 +3908,20 @@
 		if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6)
 			mss |= (skb_headlen(skb) - ETH_HLEN) << 9;
 		else {
-			tcp_opt_len = ((skb->h.th->doff - 5) * 4);
-			ip_tcp_len = (skb->nh.iph->ihl * 4) +
-				     sizeof(struct tcphdr);
+			struct iphdr *iph = ip_hdr(skb);
 
-			skb->nh.iph->check = 0;
-			skb->nh.iph->tot_len = htons(mss + ip_tcp_len +
-						     tcp_opt_len);
+			tcp_opt_len = tcp_optlen(skb);
+			ip_tcp_len = ip_hdrlen(skb) + sizeof(struct tcphdr);
+
+			iph->check = 0;
+			iph->tot_len = htons(mss + ip_tcp_len + tcp_opt_len);
 			mss |= (ip_tcp_len + tcp_opt_len) << 9;
 		}
 
 		base_flags |= (TXD_FLAG_CPU_PRE_DMA |
 			       TXD_FLAG_CPU_POST_DMA);
 
-		skb->h.th->check = 0;
+		tcp_hdr(skb)->check = 0;
 
 	}
 	else if (skb->ip_summed == CHECKSUM_PARTIAL)
@@ -4055,6 +4055,7 @@
 	mss = 0;
 	if (skb->len > (tp->dev->mtu + ETH_HLEN) &&
 	    (mss = skb_shinfo(skb)->gso_size) != 0) {
+		struct iphdr *iph;
 		int tcp_opt_len, ip_tcp_len, hdr_len;
 
 		if (skb_header_cloned(skb) &&
@@ -4063,8 +4064,8 @@
 			goto out_unlock;
 		}
 
-		tcp_opt_len = ((skb->h.th->doff - 5) * 4);
-		ip_tcp_len = (skb->nh.iph->ihl * 4) + sizeof(struct tcphdr);
+		tcp_opt_len = tcp_optlen(skb);
+		ip_tcp_len = ip_hdrlen(skb) + sizeof(struct tcphdr);
 
 		hdr_len = ip_tcp_len + tcp_opt_len;
 		if (unlikely((ETH_HLEN + hdr_len) > 80) &&
@@ -4074,34 +4075,31 @@
 		base_flags |= (TXD_FLAG_CPU_PRE_DMA |
 			       TXD_FLAG_CPU_POST_DMA);
 
-		skb->nh.iph->check = 0;
-		skb->nh.iph->tot_len = htons(mss + hdr_len);
+		iph = ip_hdr(skb);
+		iph->check = 0;
+		iph->tot_len = htons(mss + hdr_len);
 		if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) {
-			skb->h.th->check = 0;
+			tcp_hdr(skb)->check = 0;
 			base_flags &= ~TXD_FLAG_TCPUDP_CSUM;
-		}
-		else {
-			skb->h.th->check =
-				~csum_tcpudp_magic(skb->nh.iph->saddr,
-						   skb->nh.iph->daddr,
-						   0, IPPROTO_TCP, 0);
-		}
+		} else
+			tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
+								 iph->daddr, 0,
+								 IPPROTO_TCP,
+								 0);
 
 		if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO) ||
 		    (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)) {
-			if (tcp_opt_len || skb->nh.iph->ihl > 5) {
+			if (tcp_opt_len || iph->ihl > 5) {
 				int tsflags;
 
-				tsflags = ((skb->nh.iph->ihl - 5) +
-					   (tcp_opt_len >> 2));
+				tsflags = (iph->ihl - 5) + (tcp_opt_len >> 2);
 				mss |= (tsflags << 11);
 			}
 		} else {
-			if (tcp_opt_len || skb->nh.iph->ihl > 5) {
+			if (tcp_opt_len || iph->ihl > 5) {
 				int tsflags;
 
-				tsflags = ((skb->nh.iph->ihl - 5) +
-					   (tcp_opt_len >> 2));
+				tsflags = (iph->ihl - 5) + (tcp_opt_len >> 2);
 				base_flags |= tsflags << 12;
 			}
 		}
@@ -10988,24 +10986,20 @@
 	return err;
 }
 
-#ifdef CONFIG_SPARC64
+#ifdef CONFIG_SPARC
 static int __devinit tg3_get_macaddr_sparc(struct tg3 *tp)
 {
 	struct net_device *dev = tp->dev;
 	struct pci_dev *pdev = tp->pdev;
-	struct pcidev_cookie *pcp = pdev->sysdata;
+	struct device_node *dp = pci_device_to_OF_node(pdev);
+	const unsigned char *addr;
+	int len;
 
-	if (pcp != NULL) {
-		unsigned char *addr;
-		int len;
-
-		addr = of_get_property(pcp->prom_node, "local-mac-address",
-					&len);
-		if (addr && len == 6) {
-			memcpy(dev->dev_addr, addr, 6);
-			memcpy(dev->perm_addr, dev->dev_addr, 6);
-			return 0;
-		}
+	addr = of_get_property(dp, "local-mac-address", &len);
+	if (addr && len == 6) {
+		memcpy(dev->dev_addr, addr, 6);
+		memcpy(dev->perm_addr, dev->dev_addr, 6);
+		return 0;
 	}
 	return -ENODEV;
 }
@@ -11026,7 +11020,7 @@
 	u32 hi, lo, mac_offset;
 	int addr_ok = 0;
 
-#ifdef CONFIG_SPARC64
+#ifdef CONFIG_SPARC
 	if (!tg3_get_macaddr_sparc(tp))
 		return 0;
 #endif
diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c
index f85f002..106dc1e 100644
--- a/drivers/net/tlan.c
+++ b/drivers/net/tlan.c
@@ -1112,7 +1112,7 @@
 
 	if ( bbuf ) {
 		tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE );
-		memcpy( tail_buffer, skb->data, skb->len );
+		skb_copy_from_linear_data(skb, tail_buffer, skb->len);
 	} else {
 		tail_list->buffer[0].address = pci_map_single(priv->pciDev, skb->data, skb->len, PCI_DMA_TODEVICE);
 		TLan_StoreSKB(tail_list, skb);
@@ -1577,7 +1577,6 @@
 				printk(KERN_INFO "TLAN: Couldn't allocate memory for received data.\n");
 			else {
 				head_buffer = priv->rxBuffer + (priv->rxHead * TLAN_MAX_FRAME_SIZE);
-				skb->dev = dev;
 				skb_reserve(skb, 2);
 				t = (void *) skb_put(skb, frameSize);
 
@@ -1608,7 +1607,6 @@
 				skb->protocol = eth_type_trans( skb, dev );
 				netif_rx( skb );
 
-				new_skb->dev = dev;
 				skb_reserve( new_skb, 2 );
 				t = (void *) skb_put( new_skb, TLAN_MAX_FRAME_SIZE );
 				head_list->buffer[0].address = pci_map_single(priv->pciDev, new_skb->data, TLAN_MAX_FRAME_SIZE, PCI_DMA_FROMDEVICE);
diff --git a/drivers/net/tokenring/3c359.c b/drivers/net/tokenring/3c359.c
index 7580bde..e22a3f5 100644
--- a/drivers/net/tokenring/3c359.c
+++ b/drivers/net/tokenring/3c359.c
@@ -933,20 +933,21 @@
 				return ; 				
 			}
 	
-			skb->dev = dev ; 
-
 			while (xl_priv->rx_ring_tail != temp_ring_loc) { 
 				copy_len = xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfraglen & 0x7FFF ; 
 				frame_length -= copy_len ;  
 				pci_dma_sync_single_for_cpu(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ;
-				memcpy(skb_put(skb,copy_len), xl_priv->rx_ring_skb[xl_priv->rx_ring_tail]->data, copy_len) ; 
+				skb_copy_from_linear_data(xl_priv->rx_ring_skb[xl_priv->rx_ring_tail],
+							  skb_put(skb, copy_len),
+							  copy_len);
 				pci_dma_sync_single_for_device(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ;
 				adv_rx_ring(dev) ; 
 			} 
 
 			/* Now we have found the last fragment */
 			pci_dma_sync_single_for_cpu(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ;
-			memcpy(skb_put(skb,copy_len), xl_priv->rx_ring_skb[xl_priv->rx_ring_tail]->data, frame_length) ; 
+			skb_copy_from_linear_data(xl_priv->rx_ring_skb[xl_priv->rx_ring_tail],
+				      skb_put(skb,copy_len), frame_length);
 /*			memcpy(skb_put(skb,frame_length), bus_to_virt(xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr), frame_length) ; */
 			pci_dma_sync_single_for_device(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ;
 			adv_rx_ring(dev) ; 
@@ -967,8 +968,6 @@
 				return ; 
 			}
 
-			skb->dev = dev ; 
-
 			skb2 = xl_priv->rx_ring_skb[xl_priv->rx_ring_tail] ; 
 			pci_unmap_single(xl_priv->pdev, xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr, xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; 
 			skb_put(skb2, frame_length) ; 
diff --git a/drivers/net/tokenring/ibmtr.c b/drivers/net/tokenring/ibmtr.c
index 01d5531..1e8958e 100644
--- a/drivers/net/tokenring/ibmtr.c
+++ b/drivers/net/tokenring/ibmtr.c
@@ -1771,7 +1771,6 @@
 	/*BMS again, if she comes in with few but leaves with many */
 	skb_reserve(skb, sizeof(struct trh_hdr) - lan_hdr_len);
 	skb_put(skb, length);
-	skb->dev = dev;
 	data = skb->data;
 	rbuffer_len = ntohs(readw(rbuf + offsetof(struct rec_buf, buf_len)));
 	rbufdata = rbuf + offsetof(struct rec_buf, data);
diff --git a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c
index e999feb..5d849c0 100644
--- a/drivers/net/tokenring/lanstreamer.c
+++ b/drivers/net/tokenring/lanstreamer.c
@@ -944,8 +944,6 @@
 				printk(KERN_WARNING "%s: Not enough memory to copy packet to upper layers. \n",	dev->name);
 				streamer_priv->streamer_stats.rx_dropped++;
 			} else {	/* we allocated an skb OK */
-				skb->dev = dev;
-
 				if (buffer_cnt == 1) {
 					/* release the DMA mapping */
 					pci_unmap_single(streamer_priv->pci_dev, 
@@ -1607,10 +1605,11 @@
 				      frame_data, buffer_len);
 		} while (next_ptr && (buff_off = next_ptr));
 
+		mac_frame->protocol = tr_type_trans(mac_frame, dev);
 #if STREAMER_NETWORK_MONITOR
 		printk(KERN_WARNING "%s: Received MAC Frame, details: \n",
 		       dev->name);
-		mac_hdr = (struct trh_hdr *) mac_frame->data;
+		mac_hdr = tr_hdr(mac_frame);
 		printk(KERN_WARNING
 		       "%s: MAC Frame Dest. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n",
 		       dev->name, mac_hdr->daddr[0], mac_hdr->daddr[1],
@@ -1622,8 +1621,6 @@
 		       mac_hdr->saddr[2], mac_hdr->saddr[3],
 		       mac_hdr->saddr[4], mac_hdr->saddr[5]);
 #endif
-		mac_frame->dev = dev;
-		mac_frame->protocol = tr_type_trans(mac_frame, dev);
 		netif_rx(mac_frame);
 
 		/* Now tell the card we have dealt with the received frame */
diff --git a/drivers/net/tokenring/olympic.c b/drivers/net/tokenring/olympic.c
index 8f4ecc1..09b3cfb 100644
--- a/drivers/net/tokenring/olympic.c
+++ b/drivers/net/tokenring/olympic.c
@@ -814,8 +814,6 @@
 					olympic_priv->rx_ring_last_received += i ; 
 					olympic_priv->rx_ring_last_received &= (OLYMPIC_RX_RING_SIZE -1) ;  
 				} else  {
-					skb->dev = dev ; 
-
 					/* Optimise based upon number of buffers used. 
 			   	   	   If only one buffer is used we can simply swap the buffers around.
 			   	   	   If more than one then we must use the new buffer and copy the information
@@ -847,7 +845,9 @@
 							pci_dma_sync_single_for_cpu(olympic_priv->pdev,
 								le32_to_cpu(olympic_priv->olympic_rx_ring[rx_ring_last_received].buffer),
 								olympic_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; 
-							memcpy(skb_put(skb,length-4),olympic_priv->rx_ring_skb[rx_ring_last_received]->data,length-4) ; 
+							skb_copy_from_linear_data(olympic_priv->rx_ring_skb[rx_ring_last_received],
+								      skb_put(skb,length - 4),
+								      length - 4);
 							pci_dma_sync_single_for_device(olympic_priv->pdev,
 								le32_to_cpu(olympic_priv->olympic_rx_ring[rx_ring_last_received].buffer),
 								olympic_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ;
@@ -864,7 +864,9 @@
 								olympic_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; 
 							rx_desc = &(olympic_priv->olympic_rx_ring[rx_ring_last_received]);
 							cpy_length = (i == 1 ? frag_len : le32_to_cpu(rx_desc->res_length)); 
-							memcpy(skb_put(skb, cpy_length), olympic_priv->rx_ring_skb[rx_ring_last_received]->data, cpy_length) ;
+							skb_copy_from_linear_data(olympic_priv->rx_ring_skb[rx_ring_last_received],
+								      skb_put(skb, cpy_length),
+								      cpy_length);
 							pci_dma_sync_single_for_device(olympic_priv->pdev,
 								le32_to_cpu(olympic_priv->olympic_rx_ring[rx_ring_last_received].buffer),
 								olympic_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ;
@@ -1440,16 +1442,16 @@
 			next_ptr=readw(buf_ptr+offsetof(struct mac_receive_buffer,next)); 
 		} while (next_ptr && (buf_ptr=olympic_priv->olympic_lap + ntohs(next_ptr)));
 
+		mac_frame->protocol = tr_type_trans(mac_frame, dev);
+
 		if (olympic_priv->olympic_network_monitor) { 
 			struct trh_hdr *mac_hdr ; 
 			printk(KERN_WARNING "%s: Received MAC Frame, details: \n",dev->name) ;
-			mac_hdr = (struct trh_hdr *)mac_frame->data ; 
+			mac_hdr = tr_hdr(mac_frame);
 			printk(KERN_WARNING "%s: MAC Frame Dest. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n", dev->name , mac_hdr->daddr[0], mac_hdr->daddr[1], mac_hdr->daddr[2], mac_hdr->daddr[3], mac_hdr->daddr[4], mac_hdr->daddr[5]) ; 
 			printk(KERN_WARNING "%s: MAC Frame Srce. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n", dev->name , mac_hdr->saddr[0], mac_hdr->saddr[1], mac_hdr->saddr[2], mac_hdr->saddr[3], mac_hdr->saddr[4], mac_hdr->saddr[5]) ; 
 		}
-		mac_frame->dev = dev ; 
-		mac_frame->protocol = tr_type_trans(mac_frame,dev);
-		netif_rx(mac_frame) ; 	
+		netif_rx(mac_frame);
 		dev->last_rx = jiffies;
 
 drop_frame:
diff --git a/drivers/net/tokenring/smctr.c b/drivers/net/tokenring/smctr.c
index cec282a..9bbea5c 100644
--- a/drivers/net/tokenring/smctr.c
+++ b/drivers/net/tokenring/smctr.c
@@ -3889,14 +3889,13 @@
 
                 /* Slide data into a sleek skb. */
                 skb_put(skb, skb->len);
-                memcpy(skb->data, rmf, skb->len);
+                skb_copy_to_linear_data(skb, rmf, skb->len);
 
                 /* Update Counters */
                 tp->MacStat.rx_packets++;
                 tp->MacStat.rx_bytes += skb->len;
 
                 /* Kick the packet on up. */
-                skb->dev = dev;
                 skb->protocol = tr_type_trans(skb, dev);
                 netif_rx(skb);
 		dev->last_rx = jiffies;
@@ -4476,14 +4475,13 @@
 				if (skb) {
                                 	skb_put(skb, rx_size);
 
-                                	memcpy(skb->data, pbuff, rx_size);
+					skb_copy_to_linear_data(skb, pbuff, rx_size);
 
                                 	/* Update Counters */
                                 	tp->MacStat.rx_packets++;
                                 	tp->MacStat.rx_bytes += skb->len;
 
                                 	/* Kick the packet on up. */
-                                	skb->dev = dev;
                                 	skb->protocol = tr_type_trans(skb, dev);
                                 	netif_rx(skb);
 					dev->last_rx = jiffies;
diff --git a/drivers/net/tokenring/tms380tr.c b/drivers/net/tokenring/tms380tr.c
index ea797ca..12bd2940 100644
--- a/drivers/net/tokenring/tms380tr.c
+++ b/drivers/net/tokenring/tms380tr.c
@@ -644,7 +644,7 @@
 		dmabuf  = 0;
 		i 	= tp->TplFree->TPLIndex;
 		buf 	= tp->LocalTxBuffers[i];
-		memcpy(buf, skb->data, length);
+		skb_copy_from_linear_data(skb, buf, length);
 		newbuf 	= ((char *)buf - (char *)tp) + tp->dmabuffer;
 	}
 	else {
@@ -2168,7 +2168,6 @@
 				}
 				else
 				{
-					skb->dev	= dev;
 					skb_put(skb, tp->MaxPacketSize);
 					rpl->SkbStat 	= SKB_DATA_COPY;
 					ReceiveDataPtr 	= rpl->MData;
@@ -2179,7 +2178,8 @@
 				|| rpl->SkbStat == SKB_DMA_DIRECT))
 			{
 				if(rpl->SkbStat == SKB_DATA_COPY)
-					memcpy(skb->data, ReceiveDataPtr, Length);
+					skb_copy_to_linear_data(skb, ReceiveDataPtr,
+						       Length);
 
 				/* Deliver frame to system */
 				rpl->Skb = NULL;
diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c
index d92c5c5..0bfc2c9 100644
--- a/drivers/net/tsi108_eth.c
+++ b/drivers/net/tsi108_eth.c
@@ -788,7 +788,6 @@
 			printk(".\n");
 		}
 
-		skb->dev = dev;
 		skb_put(skb, data->rxring[rx].len);
 		skb->protocol = eth_type_trans(skb, dev);
 		netif_receive_skb(skb);
diff --git a/drivers/net/tulip/de2104x.c b/drivers/net/tulip/de2104x.c
index c82befa..8617298 100644
--- a/drivers/net/tulip/de2104x.c
+++ b/drivers/net/tulip/de2104x.c
@@ -63,7 +63,7 @@
 
 /* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
 #if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \
-        || defined(__sparc__) || defined(__ia64__) \
+        || defined(CONFIG_SPARC) || defined(__ia64__) \
         || defined(__sh__) || defined(__mips__)
 static int rx_copybreak = 1518;
 #else
@@ -435,7 +435,6 @@
 			rx_work = 100;
 			goto rx_next;
 		}
-		copy_skb->dev = de->dev;
 
 		if (!copying_skb) {
 			pci_unmap_single(de->pdev, mapping,
@@ -450,8 +449,8 @@
 		} else {
 			pci_dma_sync_single_for_cpu(de->pdev, mapping, len, PCI_DMA_FROMDEVICE);
 			skb_reserve(copy_skb, RX_OFFSET);
-			memcpy(skb_put(copy_skb, len), skb->data, len);
-
+			skb_copy_from_linear_data(skb, skb_put(copy_skb, len),
+						  len);
 			pci_dma_sync_single_for_device(de->pdev, mapping, len, PCI_DMA_FROMDEVICE);
 
 			/* We'll reuse the original ring buffer. */
diff --git a/drivers/net/tulip/de4x5.c b/drivers/net/tulip/de4x5.c
index 4b3cd3d..62143f9 100644
--- a/drivers/net/tulip/de4x5.c
+++ b/drivers/net/tulip/de4x5.c
@@ -1160,7 +1160,7 @@
 	sprintf(lp->adapter_name,"%s (%s)", name, gendev->bus_id);
 
 	lp->dma_size = (NUM_RX_DESC + NUM_TX_DESC) * sizeof(struct de4x5_desc);
-#if defined(__alpha__) || defined(__powerpc__) || defined(__sparc_v9__) || defined(DE4X5_DO_MEMCPY)
+#if defined(__alpha__) || defined(__powerpc__) || defined(CONFIG_SPARC) || defined(DE4X5_DO_MEMCPY)
 	lp->dma_size += RX_BUFF_SZ * NUM_RX_DESC + DE4X5_ALIGN;
 #endif
 	lp->rx_ring = dma_alloc_coherent(gendev, lp->dma_size,
@@ -1175,7 +1175,7 @@
 	** Set up the RX descriptor ring (Intels)
 	** Allocate contiguous receive buffers, long word aligned (Alphas)
 	*/
-#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY)
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(CONFIG_SPARC) && !defined(DE4X5_DO_MEMCPY)
 	for (i=0; i<NUM_RX_DESC; i++) {
 	    lp->rx_ring[i].status = 0;
 	    lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ);
@@ -1252,11 +1252,7 @@
 	    mii_get_phy(dev);
 	}
 
-#ifndef __sparc_v9__
 	printk("      and requires IRQ%d (provided by %s).\n", dev->irq,
-#else
-	printk("      and requires IRQ%x (provided by %s).\n", dev->irq,
-#endif
 	       ((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG"));
     }
 
@@ -3627,14 +3623,13 @@
     struct de4x5_private *lp = netdev_priv(dev);
     struct sk_buff *p;
 
-#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY)
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(CONFIG_SPARC) && !defined(DE4X5_DO_MEMCPY)
     struct sk_buff *ret;
     u_long i=0, tmp;
 
     p = dev_alloc_skb(IEEE802_3_SZ + DE4X5_ALIGN + 2);
     if (!p) return NULL;
 
-    p->dev = dev;
     tmp = virt_to_bus(p->data);
     i = ((tmp + DE4X5_ALIGN) & ~DE4X5_ALIGN) - tmp;
     skb_reserve(p, i);
@@ -3655,7 +3650,6 @@
     p = dev_alloc_skb(len + 2);
     if (!p) return NULL;
 
-    p->dev = dev;
     skb_reserve(p, 2);	                               /* Align */
     if (index < lp->rx_old) {                          /* Wrapped buffer */
 	short tlen = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ;
diff --git a/drivers/net/tulip/dmfe.c b/drivers/net/tulip/dmfe.c
index 9aeac76..4ed67ff 100644
--- a/drivers/net/tulip/dmfe.c
+++ b/drivers/net/tulip/dmfe.c
@@ -55,9 +55,6 @@
 
     TODO
 
-    Implement pci_driver::suspend() and pci_driver::resume()
-    power management methods.
-
     Check on 64 bit boxes.
     Check and fix on big endian boxes.
 
@@ -125,6 +122,11 @@
 #define DM9801_NOISE_FLOOR 8
 #define DM9802_NOISE_FLOOR 5
 
+#define DMFE_WOL_LINKCHANGE	0x20000000
+#define DMFE_WOL_SAMPLEPACKET	0x10000000
+#define DMFE_WOL_MAGICPACKET	0x08000000
+
+
 #define DMFE_10MHF      0
 #define DMFE_100MHF     1
 #define DMFE_10MFD      4
@@ -251,6 +253,7 @@
 	u8 wait_reset;			/* Hardware failed, need to reset */
 	u8 dm910x_chk_mode;		/* Operating mode check */
 	u8 first_in_callback;		/* Flag to record state */
+	u8 wol_mode;			/* user WOL settings */
 	struct timer_list timer;
 
 	/* System defined statistic counter */
@@ -431,6 +434,7 @@
 	db->chip_id = ent->driver_data;
 	db->ioaddr = pci_resource_start(pdev, 0);
 	db->chip_revision = dev_rev;
+	db->wol_mode = 0;
 
 	db->pdev = pdev;
 
@@ -682,7 +686,7 @@
 
 	/* transmit this packet */
 	txptr = db->tx_insert_ptr;
-	memcpy(txptr->tx_buf_ptr, skb->data, skb->len);
+	skb_copy_from_linear_data(skb, txptr->tx_buf_ptr, skb->len);
 	txptr->tdes1 = cpu_to_le32(0xe1000000 | skb->len);
 
 	/* Point to next transmit free descriptor */
@@ -988,14 +992,14 @@
 
 						skb = newskb;
 						/* size less than COPY_SIZE, allocate a rxlen SKB */
-						skb->dev = dev;
 						skb_reserve(skb, 2); /* 16byte align */
-						memcpy(skb_put(skb, rxlen), rxptr->rx_skb_ptr->data, rxlen);
+						skb_copy_from_linear_data(rxptr->rx_skb_ptr,
+							  skb_put(skb, rxlen),
+									  rxlen);
 						dmfe_reuse_skb(db, rxptr->rx_skb_ptr);
-					} else {
-						skb->dev = dev;
+					} else
 						skb_put(skb, rxlen);
-					}
+
 					skb->protocol = eth_type_trans(skb, dev);
 					netif_rx(skb);
 					dev->last_rx = jiffies;
@@ -1065,7 +1069,11 @@
 	spin_unlock_irqrestore(&db->lock, flags);
 }
 
-static void netdev_get_drvinfo(struct net_device *dev,
+/*
+ * 	Ethtool interace
+ */
+
+static void dmfe_ethtool_get_drvinfo(struct net_device *dev,
 			       struct ethtool_drvinfo *info)
 {
 	struct dmfe_board_info *np = netdev_priv(dev);
@@ -1079,9 +1087,35 @@
 			dev->base_addr, dev->irq);
 }
 
+static int dmfe_ethtool_set_wol(struct net_device *dev,
+				struct ethtool_wolinfo *wolinfo)
+{
+	struct dmfe_board_info *db = netdev_priv(dev);
+
+	if (wolinfo->wolopts & (WAKE_UCAST | WAKE_MCAST | WAKE_BCAST |
+		   		WAKE_ARP | WAKE_MAGICSECURE))
+		   return -EOPNOTSUPP;
+
+	db->wol_mode = wolinfo->wolopts;
+	return 0;
+}
+
+static void dmfe_ethtool_get_wol(struct net_device *dev,
+				 struct ethtool_wolinfo *wolinfo)
+{
+	struct dmfe_board_info *db = netdev_priv(dev);
+
+	wolinfo->supported = WAKE_PHY | WAKE_MAGIC;
+	wolinfo->wolopts = db->wol_mode;
+	return;
+}
+
+
 static const struct ethtool_ops netdev_ethtool_ops = {
-	.get_drvinfo		= netdev_get_drvinfo,
+	.get_drvinfo		= dmfe_ethtool_get_drvinfo,
 	.get_link               = ethtool_op_get_link,
+	.set_wol		= dmfe_ethtool_set_wol,
+	.get_wol		= dmfe_ethtool_get_wol,
 };
 
 /*
@@ -2050,11 +2084,85 @@
 MODULE_DEVICE_TABLE(pci, dmfe_pci_tbl);
 
 
+#ifdef CONFIG_PM
+static int dmfe_suspend(struct pci_dev *pci_dev, pm_message_t state)
+{
+	struct net_device *dev = pci_get_drvdata(pci_dev);
+	struct dmfe_board_info *db = netdev_priv(dev);
+	u32 tmp;
+
+	/* Disable upper layer interface */
+	netif_device_detach(dev);
+
+	/* Disable Tx/Rx */
+	db->cr6_data &= ~(CR6_RXSC | CR6_TXSC);
+	update_cr6(db->cr6_data, dev->base_addr);
+
+	/* Disable Interrupt */
+	outl(0, dev->base_addr + DCR7);
+	outl(inl (dev->base_addr + DCR5), dev->base_addr + DCR5);
+
+	/* Fre RX buffers */
+	dmfe_free_rxbuffer(db);
+
+	/* Enable WOL */
+	pci_read_config_dword(pci_dev, 0x40, &tmp);
+	tmp &= ~(DMFE_WOL_LINKCHANGE|DMFE_WOL_MAGICPACKET);
+
+	if (db->wol_mode & WAKE_PHY)
+		tmp |= DMFE_WOL_LINKCHANGE;
+	if (db->wol_mode & WAKE_MAGIC)
+		tmp |= DMFE_WOL_MAGICPACKET;
+
+	pci_write_config_dword(pci_dev, 0x40, tmp);
+
+	pci_enable_wake(pci_dev, PCI_D3hot, 1);
+	pci_enable_wake(pci_dev, PCI_D3cold, 1);
+
+	/* Power down device*/
+	pci_set_power_state(pci_dev, pci_choose_state (pci_dev,state));
+	pci_save_state(pci_dev);
+
+	return 0;
+}
+
+static int dmfe_resume(struct pci_dev *pci_dev)
+{
+	struct net_device *dev = pci_get_drvdata(pci_dev);
+	u32 tmp;
+
+	pci_restore_state(pci_dev);
+	pci_set_power_state(pci_dev, PCI_D0);
+
+	/* Re-initilize DM910X board */
+	dmfe_init_dm910x(dev);
+
+	/* Disable WOL */
+	pci_read_config_dword(pci_dev, 0x40, &tmp);
+
+	tmp &= ~(DMFE_WOL_LINKCHANGE | DMFE_WOL_MAGICPACKET);
+	pci_write_config_dword(pci_dev, 0x40, tmp);
+
+	pci_enable_wake(pci_dev, PCI_D3hot, 0);
+	pci_enable_wake(pci_dev, PCI_D3cold, 0);
+
+	/* Restart upper layer interface */
+	netif_device_attach(dev);
+
+	return 0;
+}
+#else
+#define dmfe_suspend NULL
+#define dmfe_resume NULL
+#endif
+
 static struct pci_driver dmfe_driver = {
 	.name		= "dmfe",
 	.id_table	= dmfe_pci_tbl,
 	.probe		= dmfe_init_one,
 	.remove		= __devexit_p(dmfe_remove_one),
+	.suspend        = dmfe_suspend,
+	.resume         = dmfe_resume
 };
 
 MODULE_AUTHOR("Sten Wang, sten_wang@davicom.com.tw");
diff --git a/drivers/net/tulip/interrupt.c b/drivers/net/tulip/interrupt.c
index e3488d7..9b08afb 100644
--- a/drivers/net/tulip/interrupt.c
+++ b/drivers/net/tulip/interrupt.c
@@ -192,7 +192,6 @@
                                   to a minimally-sized skbuff. */
                                if (pkt_len < tulip_rx_copybreak
                                    && (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_for_cpu(tp->pdev,
 								   tp->rx_buffers[entry].mapping,
@@ -416,7 +415,6 @@
 			   to a minimally-sized skbuff. */
 			if (pkt_len < tulip_rx_copybreak
 				&& (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_for_cpu(tp->pdev,
 							    tp->rx_buffers[entry].mapping,
@@ -675,7 +673,7 @@
 				if (tp->link_change)
 					(tp->link_change)(dev, csr5);
 			}
-			if (csr5 & SytemError) {
+			if (csr5 & SystemError) {
 				int error = (csr5 >> 23) & 7;
 				/* oops, we hit a PCI error.  The code produced corresponds
 				 * to the reason:
@@ -745,7 +743,7 @@
 			  TxFIFOUnderflow |
 			  TxJabber |
 			  TPLnkFail |
-			  SytemError )) != 0);
+			  SystemError )) != 0);
 #else
 	} while ((csr5 & (NormalIntr|AbnormalIntr)) != 0);
 
diff --git a/drivers/net/tulip/media.c b/drivers/net/tulip/media.c
index 20bd52b..b562566 100644
--- a/drivers/net/tulip/media.c
+++ b/drivers/net/tulip/media.c
@@ -44,8 +44,10 @@
 
 /* MII transceiver control section.
    Read and write the MII registers using software-generated serial
-   MDIO protocol.  See the MII specifications or DP83840A data sheet
-   for details. */
+   MDIO protocol.
+   See IEEE 802.3-2002.pdf (Section 2, Chapter "22.2.4 Management functions")
+   or DP83840A data sheet for more details.
+   */
 
 int tulip_mdio_read(struct net_device *dev, int phy_id, int location)
 {
@@ -261,24 +263,56 @@
 				u16 *reset_sequence = &((u16*)(p+3))[init_length];
 				int reset_length = p[2 + init_length*2];
 				misc_info = reset_sequence + reset_length;
-				if (startup)
+				if (startup) {
+					int timeout = 10;	/* max 1 ms */
 					for (i = 0; i < reset_length; i++)
 						iowrite32(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15);
+
+					/* flush posted writes */
+					ioread32(ioaddr + CSR15);
+
+					/* Sect 3.10.3 in DP83840A.pdf (p39) */
+					udelay(500);
+
+					/* Section 4.2 in DP83840A.pdf (p43) */
+					/* and IEEE 802.3 "22.2.4.1.1 Reset" */
+					while (timeout-- &&
+						(tulip_mdio_read (dev, phy_num, MII_BMCR) & BMCR_RESET))
+						udelay(100);
+				}
 				for (i = 0; i < init_length; i++)
 					iowrite32(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15);
+
+				ioread32(ioaddr + CSR15);	/* flush posted writes */
 			} else {
 				u8 *init_sequence = p + 2;
 				u8 *reset_sequence = p + 3 + init_length;
 				int reset_length = p[2 + init_length];
 				misc_info = (u16*)(reset_sequence + reset_length);
 				if (startup) {
+					int timeout = 10;	/* max 1 ms */
 					iowrite32(mtable->csr12dir | 0x100, ioaddr + CSR12);
 					for (i = 0; i < reset_length; i++)
 						iowrite32(reset_sequence[i], ioaddr + CSR12);
+
+					/* flush posted writes */
+					ioread32(ioaddr + CSR12);
+
+					/* Sect 3.10.3 in DP83840A.pdf (p39) */
+					udelay(500);
+
+					/* Section 4.2 in DP83840A.pdf (p43) */
+					/* and IEEE 802.3 "22.2.4.1.1 Reset" */
+					while (timeout-- &&
+						(tulip_mdio_read (dev, phy_num, MII_BMCR) & BMCR_RESET))
+						udelay(100);
 				}
 				for (i = 0; i < init_length; i++)
 					iowrite32(init_sequence[i], ioaddr + CSR12);
+
+				ioread32(ioaddr + CSR12);	/* flush posted writes */
 			}
+
 			tmp_info = get_u16(&misc_info[1]);
 			if (tmp_info)
 				tp->advertising[phy_num] = tmp_info | 1;
diff --git a/drivers/net/tulip/tulip.h b/drivers/net/tulip/tulip.h
index 25f25da..c840d2e 100644
--- a/drivers/net/tulip/tulip.h
+++ b/drivers/net/tulip/tulip.h
@@ -132,7 +132,7 @@
 /* The bits in the CSR5 status registers, mostly interrupt sources. */
 enum status_bits {
 	TimerInt = 0x800,
-	SytemError = 0x2000,
+	SystemError = 0x2000,
 	TPLnkFail = 0x1000,
 	TPLnkPass = 0x10,
 	NormalIntr = 0x10000,
@@ -482,8 +482,11 @@
 			udelay(10);
 
 		if (!i)
-			printk(KERN_DEBUG "%s: tulip_stop_rxtx() failed\n",
-					pci_name(tp->pdev));
+			printk(KERN_DEBUG "%s: tulip_stop_rxtx() failed"
+					" (CSR5 0x%x CSR6 0x%x)\n",
+					pci_name(tp->pdev),
+					ioread32(ioaddr + CSR5),
+					ioread32(ioaddr + CSR6));
 	}
 }
 
diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c
index e3774a5..041af63 100644
--- a/drivers/net/tulip/tulip_core.c
+++ b/drivers/net/tulip/tulip_core.c
@@ -17,11 +17,11 @@
 
 #define DRV_NAME	"tulip"
 #ifdef CONFIG_TULIP_NAPI
-#define DRV_VERSION    "1.1.14-NAPI" /* Keep at least for test */
+#define DRV_VERSION    "1.1.15-NAPI" /* Keep at least for test */
 #else
-#define DRV_VERSION	"1.1.14"
+#define DRV_VERSION	"1.1.15"
 #endif
-#define DRV_RELDATE	"May 11, 2002"
+#define DRV_RELDATE	"Feb 27, 2007"
 
 
 #include <linux/module.h>
@@ -36,8 +36,8 @@
 #include <asm/unaligned.h>
 #include <asm/uaccess.h>
 
-#ifdef __sparc__
-#include <asm/pbm.h>
+#ifdef CONFIG_SPARC
+#include <asm/prom.h>
 #endif
 
 static char version[] __devinitdata =
@@ -67,7 +67,7 @@
 
 /* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
 #if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \
-	|| defined(__sparc__) || defined(__ia64__) \
+	|| defined(CONFIG_SPARC) || defined(__ia64__) \
 	|| defined(__sh__) || defined(__mips__)
 static int rx_copybreak = 1518;
 #else
@@ -91,7 +91,7 @@
 static int csr0 = 0x01A00000 | 0xE000;
 #elif defined(__i386__) || defined(__powerpc__) || defined(__x86_64__)
 static int csr0 = 0x01A00000 | 0x8000;
-#elif defined(__sparc__) || defined(__hppa__)
+#elif defined(CONFIG_SPARC) || defined(__hppa__)
 /* The UltraSparc PCI controllers will disconnect at every 64-byte
  * crossing anyways so it makes no sense to tell Tulip to burst
  * any more than that.
@@ -1315,7 +1315,7 @@
 	/* DM9102A has troubles with MRM & clear reserved bits 24:22, 20, 16, 7:1 */
 	if (tulip_uli_dm_quirk(pdev)) {
 		csr0 &= ~0x01f100ff;
-#if defined(__sparc__)
+#if defined(CONFIG_SPARC)
                 csr0 = (csr0 & ~0xff00) | 0xe000;
 #endif
 	}
@@ -1535,23 +1535,19 @@
 	   Many PCI BIOSes also incorrectly report the IRQ line, so we correct
 	   that here as well. */
 	if (sum == 0  || sum == 6*0xff) {
-#if defined(__sparc__)
-		struct pcidev_cookie *pcp = pdev->sysdata;
+#if defined(CONFIG_SPARC)
+		struct device_node *dp = pci_device_to_OF_node(pdev);
+		const unsigned char *addr;
+		int len;
 #endif
 		eeprom_missing = 1;
 		for (i = 0; i < 5; i++)
 			dev->dev_addr[i] = last_phys_addr[i];
 		dev->dev_addr[i] = last_phys_addr[i] + 1;
-#if defined(__sparc__)
-		if (pcp) {
-			unsigned char *addr;
-			int len;
-
-			addr = of_get_property(pcp->prom_node,
-					       "local-mac-address", &len);
-			if (addr && len == 6)
-				memcpy(dev->dev_addr, addr, 6);
-		}
+#if defined(CONFIG_SPARC)
+		addr = of_get_property(dp, "local-mac-address", &len);
+		if (addr && len == 6)
+			memcpy(dev->dev_addr, addr, 6);
 #endif
 #if defined(__i386__) || defined(__x86_64__)	/* Patch up x86 BIOS bug. */
 		if (last_irq)
diff --git a/drivers/net/tulip/uli526x.c b/drivers/net/tulip/uli526x.c
index 229158e..ca2548e 100644
--- a/drivers/net/tulip/uli526x.c
+++ b/drivers/net/tulip/uli526x.c
@@ -583,7 +583,7 @@
 
 	/* transmit this packet */
 	txptr = db->tx_insert_ptr;
-	memcpy(txptr->tx_buf_ptr, skb->data, skb->len);
+	skb_copy_from_linear_data(skb, txptr->tx_buf_ptr, skb->len);
 	txptr->tdes1 = cpu_to_le32(0xe1000000 | skb->len);
 
 	/* Point to next transmit free descriptor */
@@ -828,14 +828,14 @@
 					( (skb = dev_alloc_skb(rxlen + 2) )
 					!= NULL) ) {
 					/* size less than COPY_SIZE, allocate a rxlen SKB */
-					skb->dev = dev;
 					skb_reserve(skb, 2); /* 16byte align */
-					memcpy(skb_put(skb, rxlen), rxptr->rx_skb_ptr->tail, rxlen);
+					memcpy(skb_put(skb, rxlen),
+					       skb_tail_pointer(rxptr->rx_skb_ptr),
+					       rxlen);
 					uli526x_reuse_skb(db, rxptr->rx_skb_ptr);
-				} else {
-					skb->dev = dev;
+				} else
 					skb_put(skb, rxlen);
-				}
+
 				skb->protocol = eth_type_trans(skb, dev);
 				netif_rx(skb);
 				dev->last_rx = jiffies;
@@ -1177,7 +1177,10 @@
 
 	if (!(rxptr->rdes0 & cpu_to_le32(0x80000000))) {
 		rxptr->rx_skb_ptr = skb;
-		rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->tail, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) );
+		rxptr->rdes2 = cpu_to_le32(pci_map_single(db->pdev,
+							  skb_tail_pointer(skb),
+							  RX_ALLOC_SIZE,
+							  PCI_DMA_FROMDEVICE));
 		wmb();
 		rxptr->rdes0 = cpu_to_le32(0x80000000);
 		db->rx_avail_cnt++;
@@ -1341,7 +1344,10 @@
 		if ( ( skb = dev_alloc_skb(RX_ALLOC_SIZE) ) == NULL )
 			break;
 		rxptr->rx_skb_ptr = skb; /* FIXME (?) */
-		rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->tail, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) );
+		rxptr->rdes2 = cpu_to_le32(pci_map_single(db->pdev,
+							  skb_tail_pointer(skb),
+							  RX_ALLOC_SIZE,
+							  PCI_DMA_FROMDEVICE));
 		wmb();
 		rxptr->rdes0 = cpu_to_le32(0x80000000);
 		rxptr = rxptr->next_rx_desc;
diff --git a/drivers/net/tulip/winbond-840.c b/drivers/net/tulip/winbond-840.c
index 002a05e..fa44070 100644
--- a/drivers/net/tulip/winbond-840.c
+++ b/drivers/net/tulip/winbond-840.c
@@ -813,7 +813,6 @@
 		np->rx_skbuff[i] = skb;
 		if (skb == NULL)
 			break;
-		skb->dev = dev;			/* Mark as being used by this device. */
 		np->rx_addr[i] = pci_map_single(np->pci_dev,skb->data,
 					np->rx_buf_sz,PCI_DMA_FROMDEVICE);
 
@@ -903,7 +902,7 @@
 	}
 #elif defined(__powerpc__) || defined(__i386__) || defined(__alpha__) || defined(__ia64__) || defined(__x86_64__)
 	i |= 0xE000;
-#elif defined(__sparc__) || defined (CONFIG_PARISC)
+#elif defined(CONFIG_SPARC) || defined (CONFIG_PARISC)
 	i |= 0x4800;
 #else
 #warning Processor architecture undefined
@@ -1148,7 +1147,7 @@
 		}
 
 		/* Abnormal error summary/uncommon events handlers. */
-		if (intr_status & (AbnormalIntr | TxFIFOUnderflow | SytemError |
+		if (intr_status & (AbnormalIntr | TxFIFOUnderflow | SystemError |
 						   TimerInt | TxDied))
 			netdev_error(dev, intr_status);
 
@@ -1229,7 +1228,6 @@
 			   to a minimally-sized skbuff. */
 			if (pkt_len < rx_copybreak
 				&& (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_for_cpu(np->pci_dev,np->rx_addr[entry],
 							    np->rx_skbuff[entry]->len,
@@ -1278,7 +1276,6 @@
 			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_addr[entry] = pci_map_single(np->pci_dev,
 							skb->data,
 							np->rx_buf_sz, PCI_DMA_FROMDEVICE);
diff --git a/drivers/net/tulip/xircom_cb.c b/drivers/net/tulip/xircom_cb.c
index 61d3130..985a181 100644
--- a/drivers/net/tulip/xircom_cb.c
+++ b/drivers/net/tulip/xircom_cb.c
@@ -411,9 +411,9 @@
 			   sometimes sends more than you ask it to. */
 
 			memset(&card->tx_buffer[bufferoffsets[desc]/4],0,1536);
-			memcpy(&(card->tx_buffer[bufferoffsets[desc]/4]),skb->data,skb->len);
-
-
+			skb_copy_from_linear_data(skb,
+				  &(card->tx_buffer[bufferoffsets[desc] / 4]),
+						  skb->len);
 			/* FIXME: The specification tells us that the length we send HAS to be a multiple of
 			   4 bytes. */
 
@@ -1207,7 +1207,6 @@
 				card->stats.rx_dropped++;
 				goto out;
 			}
-			skb->dev = dev;
 			skb_reserve(skb, 2);
 			eth_copy_and_sum(skb, (unsigned char*)&card->rx_buffer[bufferoffset / 4], pkt_len, 0);
 			skb_put(skb, pkt_len);
diff --git a/drivers/net/tulip/xircom_tulip_cb.c b/drivers/net/tulip/xircom_tulip_cb.c
index a998c5d..f641729 100644
--- a/drivers/net/tulip/xircom_tulip_cb.c
+++ b/drivers/net/tulip/xircom_tulip_cb.c
@@ -65,7 +65,7 @@
 static int csr0 = 0x01A00000 | 0xE000;
 #elif defined(__powerpc__)
 static int csr0 = 0x01B00000 | 0x8000;
-#elif defined(__sparc__)
+#elif defined(CONFIG_SPARC)
 static int csr0 = 0x01B00080 | 0x8000;
 #elif defined(__i386__)
 static int csr0 = 0x01A00000 | 0x8000;
@@ -915,7 +915,9 @@
 
 	tp->tx_skbuff[entry] = skb;
 	if (tp->chip_id == X3201_3) {
-		memcpy(tp->tx_aligned_skbuff[entry]->data,skb->data,skb->len);
+		skb_copy_from_linear_data(skb,
+					  tp->tx_aligned_skbuff[entry]->data,
+					  skb->len);
 		tp->tx_ring[entry].buffer1 = virt_to_bus(tp->tx_aligned_skbuff[entry]->data);
 	} else
 		tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data);
@@ -1238,7 +1240,6 @@
 			   to a minimally-sized skbuff. */
 			if (pkt_len < rx_copybreak
 				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
-				skb->dev = dev;
 				skb_reserve(skb, 2);	/* 16 byte align the IP header */
 #if ! defined(__alpha__)
 				eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1),
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 5643d1e..a2c6caa 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -18,6 +18,10 @@
 /*
  *  Changes:
  *
+ *  Brian Braunstein <linuxkernel@bristyle.com> 2007/03/23
+ *    Fixed hw address handling.  Now net_device.dev_addr is kept consistent
+ *    with tun.dev_addr when the address is set by this module.
+ *
  *  Mike Kershaw <dragorn@kismetwireless.net> 2005/08/14
  *    Add TUNSETLINK ioctl to set the link encapsulation
  *
@@ -196,7 +200,10 @@
 		dev->set_multicast_list = tun_net_mclist;
 
 		ether_setup(dev);
-		random_ether_addr(dev->dev_addr);
+
+		/* random address already created for us by tun_set_iff, use it */
+		memcpy(dev->dev_addr, tun->dev_addr, min(sizeof(tun->dev_addr), sizeof(dev->dev_addr)) );
+
 		dev->tx_queue_len = TUN_READQ_SIZE;  /* We prefer our own queue length */
 		break;
 	}
@@ -254,11 +261,11 @@
 		return -EFAULT;
 	}
 
-	skb->dev = tun->dev;
 	switch (tun->flags & TUN_TYPE_MASK) {
 	case TUN_TUN_DEV:
-		skb->mac.raw = skb->data;
+		skb_reset_mac_header(skb);
 		skb->protocol = pi.proto;
+		skb->dev = tun->dev;
 		break;
 	case TUN_TAP_DEV:
 		skb->protocol = eth_type_trans(skb, tun->dev);
@@ -386,8 +393,8 @@
 		 *   - we are multicast promiscous.
 		 *   - we belong to the multicast group.
 		 */
-		memcpy(addr, skb->data,
-		       min_t(size_t, sizeof addr, skb->len));
+		skb_copy_from_linear_data(skb, addr, min_t(size_t, sizeof addr,
+								   skb->len));
 		bit_nr = ether_crc(sizeof addr, addr) >> 26;
 		if ((tun->if_flags & IFF_PROMISC) ||
 				memcmp(addr, tun->dev_addr, sizeof addr) == 0 ||
@@ -636,6 +643,7 @@
 		return 0;
 
 	case SIOCGIFHWADDR:
+		/* Note: the actual net device's address may be different */
 		memcpy(ifr.ifr_hwaddr.sa_data, tun->dev_addr,
 				min(sizeof ifr.ifr_hwaddr.sa_data, sizeof tun->dev_addr));
 		if (copy_to_user( argp, &ifr, sizeof ifr))
@@ -643,16 +651,24 @@
 		return 0;
 
 	case SIOCSIFHWADDR:
-		/** Set the character device's hardware address. This is used when
-		 * filtering packets being sent from the network device to the character
-		 * device. */
-		memcpy(tun->dev_addr, ifr.ifr_hwaddr.sa_data,
-				min(sizeof ifr.ifr_hwaddr.sa_data, sizeof tun->dev_addr));
-		DBG(KERN_DEBUG "%s: set hardware address: %x:%x:%x:%x:%x:%x\n",
-				tun->dev->name,
-				tun->dev_addr[0], tun->dev_addr[1], tun->dev_addr[2],
-				tun->dev_addr[3], tun->dev_addr[4], tun->dev_addr[5]);
-		return 0;
+	{
+		/* try to set the actual net device's hw address */
+		int ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr);
+
+		if (ret == 0) {
+			/** Set the character device's hardware address. This is used when
+			 * filtering packets being sent from the network device to the character
+			 * device. */
+			memcpy(tun->dev_addr, ifr.ifr_hwaddr.sa_data,
+					min(sizeof ifr.ifr_hwaddr.sa_data, sizeof tun->dev_addr));
+			DBG(KERN_DEBUG "%s: set hardware address: %x:%x:%x:%x:%x:%x\n",
+					tun->dev->name,
+					tun->dev_addr[0], tun->dev_addr[1], tun->dev_addr[2],
+					tun->dev_addr[3], tun->dev_addr[4], tun->dev_addr[5]);
+		}
+
+		return  ret;
+	}
 
 	case SIOCADDMULTI:
 		/** Add the specified group to the character device's multicast filter
diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c
index 0d91d09..f2dd776 100644
--- a/drivers/net/typhoon.c
+++ b/drivers/net/typhoon.c
@@ -1708,7 +1708,6 @@
 
 		if(pkt_len < rx_copybreak &&
 		   (new_skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
-			new_skb->dev = tp->dev;
 			skb_reserve(new_skb, 2);
 			pci_dma_sync_single_for_cpu(tp->pdev, dma_addr,
 						    PKT_BUF_SZ,
diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c
index 639e1e6..16b9acd 100644
--- a/drivers/net/ucc_geth.c
+++ b/drivers/net/ucc_geth.c
@@ -29,6 +29,7 @@
 #include <linux/fsl_devices.h>
 #include <linux/ethtool.h>
 #include <linux/mii.h>
+#include <linux/phy.h>
 #include <linux/workqueue.h>
 
 #include <asm/of_platform.h>
@@ -41,12 +42,13 @@
 #include <asm/ucc_fast.h>
 
 #include "ucc_geth.h"
-#include "ucc_geth_phy.h"
+#include "ucc_geth_mii.h"
 
 #undef DEBUG
 
-#define DRV_DESC "QE UCC Gigabit Ethernet Controller version:Sept 11, 2006"
+#define DRV_DESC "QE UCC Gigabit Ethernet Controller"
 #define DRV_NAME "ucc_geth"
+#define DRV_VERSION "1.1"
 
 #define ugeth_printk(level, format, arg...)  \
         printk(level format "\n", ## arg)
@@ -73,22 +75,13 @@
 		    .bd_mem_part = MEM_PART_SYSTEM,
 		    .rtsm = UCC_FAST_SEND_IDLES_BETWEEN_FRAMES,
 		    .max_rx_buf_length = 1536,
-/* FIXME: should be changed in run time for 1G and 100M */
-#ifdef CONFIG_UGETH_HAS_GIGA
-		    .urfs = UCC_GETH_URFS_GIGA_INIT,
-		    .urfet = UCC_GETH_URFET_GIGA_INIT,
-		    .urfset = UCC_GETH_URFSET_GIGA_INIT,
-		    .utfs = UCC_GETH_UTFS_GIGA_INIT,
-		    .utfet = UCC_GETH_UTFET_GIGA_INIT,
-		    .utftt = UCC_GETH_UTFTT_GIGA_INIT,
-#else
+		    /* adjusted at startup if max-speed 1000 */
 		    .urfs = UCC_GETH_URFS_INIT,
 		    .urfet = UCC_GETH_URFET_INIT,
 		    .urfset = UCC_GETH_URFSET_INIT,
 		    .utfs = UCC_GETH_UTFS_INIT,
 		    .utfet = UCC_GETH_UTFET_INIT,
 		    .utftt = UCC_GETH_UTFTT_INIT,
-#endif
 		    .ufpt = 256,
 		    .mode = UCC_FAST_PROTOCOL_MODE_ETHERNET,
 		    .ttx_trx = UCC_FAST_GUMR_TRANSPARENT_TTX_TRX_NORMAL,
@@ -217,70 +210,6 @@
 	}
 }
 
-static int get_interface_details(enum enet_interface enet_interface,
-				 enum enet_speed *speed,
-				 int *r10m,
-				 int *rmm,
-				 int *rpm,
-				 int *tbi, int *limited_to_full_duplex)
-{
-	/* Analyze enet_interface according to Interface Mode
-	Configuration table */
-	switch (enet_interface) {
-	case ENET_10_MII:
-		*speed = ENET_SPEED_10BT;
-		break;
-	case ENET_10_RMII:
-		*speed = ENET_SPEED_10BT;
-		*r10m = 1;
-		*rmm = 1;
-		break;
-	case ENET_10_RGMII:
-		*speed = ENET_SPEED_10BT;
-		*rpm = 1;
-		*r10m = 1;
-		*limited_to_full_duplex = 1;
-		break;
-	case ENET_100_MII:
-		*speed = ENET_SPEED_100BT;
-		break;
-	case ENET_100_RMII:
-		*speed = ENET_SPEED_100BT;
-		*rmm = 1;
-		break;
-	case ENET_100_RGMII:
-		*speed = ENET_SPEED_100BT;
-		*rpm = 1;
-		*limited_to_full_duplex = 1;
-		break;
-	case ENET_1000_GMII:
-		*speed = ENET_SPEED_1000BT;
-		*limited_to_full_duplex = 1;
-		break;
-	case ENET_1000_RGMII:
-		*speed = ENET_SPEED_1000BT;
-		*rpm = 1;
-		*limited_to_full_duplex = 1;
-		break;
-	case ENET_1000_TBI:
-		*speed = ENET_SPEED_1000BT;
-		*tbi = 1;
-		*limited_to_full_duplex = 1;
-		break;
-	case ENET_1000_RTBI:
-		*speed = ENET_SPEED_1000BT;
-		*rpm = 1;
-		*tbi = 1;
-		*limited_to_full_duplex = 1;
-		break;
-	default:
-		return -EINVAL;
-		break;
-	}
-
-	return 0;
-}
-
 static struct sk_buff *get_new_skb(struct ucc_geth_private *ugeth, u8 *bd)
 {
 	struct sk_buff *skb = NULL;
@@ -758,24 +687,6 @@
 	ugeth_info("hafdup     : addr - 0x%08x, val - 0x%08x",
 		   (u32) & ugeth->ug_regs->hafdup,
 		   in_be32(&ugeth->ug_regs->hafdup));
-	ugeth_info("miimcfg    : addr - 0x%08x, val - 0x%08x",
-		   (u32) & ugeth->ug_regs->miimng.miimcfg,
-		   in_be32(&ugeth->ug_regs->miimng.miimcfg));
-	ugeth_info("miimcom    : addr - 0x%08x, val - 0x%08x",
-		   (u32) & ugeth->ug_regs->miimng.miimcom,
-		   in_be32(&ugeth->ug_regs->miimng.miimcom));
-	ugeth_info("miimadd    : addr - 0x%08x, val - 0x%08x",
-		   (u32) & ugeth->ug_regs->miimng.miimadd,
-		   in_be32(&ugeth->ug_regs->miimng.miimadd));
-	ugeth_info("miimcon    : addr - 0x%08x, val - 0x%08x",
-		   (u32) & ugeth->ug_regs->miimng.miimcon,
-		   in_be32(&ugeth->ug_regs->miimng.miimcon));
-	ugeth_info("miimstat   : addr - 0x%08x, val - 0x%08x",
-		   (u32) & ugeth->ug_regs->miimng.miimstat,
-		   in_be32(&ugeth->ug_regs->miimng.miimstat));
-	ugeth_info("miimmind   : addr - 0x%08x, val - 0x%08x",
-		   (u32) & ugeth->ug_regs->miimng.miimind,
-		   in_be32(&ugeth->ug_regs->miimng.miimind));
 	ugeth_info("ifctl      : addr - 0x%08x, val - 0x%08x",
 		   (u32) & ugeth->ug_regs->ifctl,
 		   in_be32(&ugeth->ug_regs->ifctl));
@@ -1425,27 +1336,6 @@
 	return 0;
 }
 
-static int init_mac_duplex_mode(int full_duplex,
-				int limited_to_full_duplex,
-				volatile u32 *maccfg2_register)
-{
-	u32 value = 0;
-
-	/* some interfaces must work in full duplex mode */
-	if ((full_duplex == 0) && (limited_to_full_duplex == 1))
-		return -EINVAL;
-
-	value = in_be32(maccfg2_register);
-
-	if (full_duplex)
-		value |= MACCFG2_FDX;
-	else
-		value &= ~MACCFG2_FDX;
-
-	out_be32(maccfg2_register, value);
-	return 0;
-}
-
 static int init_check_frame_length_mode(int length_check,
 					volatile u32 *maccfg2_register)
 {
@@ -1477,40 +1367,6 @@
 	return 0;
 }
 
-static int init_mii_management_configuration(int reset_mgmt,
-					     int preamble_supress,
-					     volatile u32 *miimcfg_register,
-					     volatile u32 *miimind_register)
-{
-	unsigned int timeout = PHY_INIT_TIMEOUT;
-	u32 value = 0;
-
-	value = in_be32(miimcfg_register);
-	if (reset_mgmt) {
-		value |= MIIMCFG_RESET_MANAGEMENT;
-		out_be32(miimcfg_register, value);
-	}
-
-	value = 0;
-
-	if (preamble_supress)
-		value |= MIIMCFG_NO_PREAMBLE;
-
-	value |= UCC_GETH_MIIMCFG_MNGMNT_CLC_DIV_INIT;
-	out_be32(miimcfg_register, value);
-
-	/* Wait until the bus is free */
-	while ((in_be32(miimind_register) & MIIMIND_BUSY) && timeout--)
-		cpu_relax();
-
-	if (timeout <= 0) {
-		ugeth_err("%s: The MII Bus is stuck!", __FUNCTION__);
-		return -ETIMEDOUT;
-	}
-
-	return 0;
-}
-
 static int init_rx_parameters(int reject_broadcast,
 			      int receive_short_frames,
 			      int promiscuous, volatile u32 *upsmr_register)
@@ -1570,10 +1426,8 @@
 	struct ucc_geth_info *ug_info;
 	struct ucc_geth *ug_regs;
 	struct ucc_fast *uf_regs;
-	enum enet_speed speed;
-	int ret_val, rpm = 0, tbi = 0, r10m = 0, rmm =
-	    0, limited_to_full_duplex = 0;
-	u32 upsmr, maccfg2, utbipar, tbiBaseAddress;
+	int ret_val;
+	u32 upsmr, maccfg2, tbiBaseAddress;
 	u16 value;
 
 	ugeth_vdbg("%s: IN", __FUNCTION__);
@@ -1582,24 +1436,13 @@
 	ug_regs = ugeth->ug_regs;
 	uf_regs = ugeth->uccf->uf_regs;
 
-	/* Analyze enet_interface according to Interface Mode Configuration
-	table */
-	ret_val =
-	    get_interface_details(ug_info->enet_interface, &speed, &r10m, &rmm,
-				  &rpm, &tbi, &limited_to_full_duplex);
-	if (ret_val != 0) {
-		ugeth_err
-		  ("%s: half duplex not supported in requested configuration.",
-		     __FUNCTION__);
-		return ret_val;
-	}
-
 	/*                    Set MACCFG2                    */
 	maccfg2 = in_be32(&ug_regs->maccfg2);
 	maccfg2 &= ~MACCFG2_INTERFACE_MODE_MASK;
-	if ((speed == ENET_SPEED_10BT) || (speed == ENET_SPEED_100BT))
+	if ((ugeth->max_speed == SPEED_10) ||
+	    (ugeth->max_speed == SPEED_100))
 		maccfg2 |= MACCFG2_INTERFACE_MODE_NIBBLE;
-	else if (speed == ENET_SPEED_1000BT)
+	else if (ugeth->max_speed == SPEED_1000)
 		maccfg2 |= MACCFG2_INTERFACE_MODE_BYTE;
 	maccfg2 |= ug_info->padAndCrc;
 	out_be32(&ug_regs->maccfg2, maccfg2);
@@ -1607,54 +1450,39 @@
 	/*                    Set UPSMR                      */
 	upsmr = in_be32(&uf_regs->upsmr);
 	upsmr &= ~(UPSMR_RPM | UPSMR_R10M | UPSMR_TBIM | UPSMR_RMM);
-	if (rpm)
+	if ((ugeth->phy_interface == PHY_INTERFACE_MODE_RMII) ||
+	    (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII) ||
+	    (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII_ID) ||
+	    (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) {
 		upsmr |= UPSMR_RPM;
-	if (r10m)
-		upsmr |= UPSMR_R10M;
-	if (tbi)
+		switch (ugeth->max_speed) {
+		case SPEED_10:
+			upsmr |= UPSMR_R10M;
+			/* FALLTHROUGH */
+		case SPEED_100:
+			if (ugeth->phy_interface != PHY_INTERFACE_MODE_RTBI)
+				upsmr |= UPSMR_RMM;
+		}
+	}
+	if ((ugeth->phy_interface == PHY_INTERFACE_MODE_TBI) ||
+	    (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) {
 		upsmr |= UPSMR_TBIM;
-	if (rmm)
-		upsmr |= UPSMR_RMM;
+	}
 	out_be32(&uf_regs->upsmr, upsmr);
 
-	/*                    Set UTBIPAR                    */
-	utbipar = in_be32(&ug_regs->utbipar);
-	utbipar &= ~UTBIPAR_PHY_ADDRESS_MASK;
-	if (tbi)
-		utbipar |=
-		    (ug_info->phy_address +
-		     ugeth->ug_info->uf_info.
-		     ucc_num) << UTBIPAR_PHY_ADDRESS_SHIFT;
-	else
-		utbipar |=
-		    (0x10 +
-		     ugeth->ug_info->uf_info.
-		     ucc_num) << UTBIPAR_PHY_ADDRESS_SHIFT;
-	out_be32(&ug_regs->utbipar, utbipar);
-
 	/* Disable autonegotiation in tbi mode, because by default it
 	comes up in autonegotiation mode. */
 	/* Note that this depends on proper setting in utbipar register. */
-	if (tbi) {
+	if ((ugeth->phy_interface == PHY_INTERFACE_MODE_TBI) ||
+	    (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) {
 		tbiBaseAddress = in_be32(&ug_regs->utbipar);
 		tbiBaseAddress &= UTBIPAR_PHY_ADDRESS_MASK;
 		tbiBaseAddress >>= UTBIPAR_PHY_ADDRESS_SHIFT;
-		value =
-		    ugeth->mii_info->mdio_read(ugeth->dev, (u8) tbiBaseAddress,
-					       ENET_TBI_MII_CR);
+		value = ugeth->phydev->bus->read(ugeth->phydev->bus,
+				(u8) tbiBaseAddress, ENET_TBI_MII_CR);
 		value &= ~0x1000;	/* Turn off autonegotiation */
-		ugeth->mii_info->mdio_write(ugeth->dev, (u8) tbiBaseAddress,
-					    ENET_TBI_MII_CR, value);
-	}
-
-	ret_val = init_mac_duplex_mode(1,
-				       limited_to_full_duplex,
-				       &ug_regs->maccfg2);
-	if (ret_val != 0) {
-		ugeth_err
-		("%s: half duplex not supported in requested configuration.",
-		     __FUNCTION__);
-		return ret_val;
+		ugeth->phydev->bus->write(ugeth->phydev->bus,
+				(u8) tbiBaseAddress, ENET_TBI_MII_CR, value);
 	}
 
 	init_check_frame_length_mode(ug_info->lengthCheckRx, &ug_regs->maccfg2);
@@ -1676,76 +1504,88 @@
  * function converts those variables into the appropriate
  * register values, and can bring down the device if needed.
  */
+
 static void adjust_link(struct net_device *dev)
 {
 	struct ucc_geth_private *ugeth = netdev_priv(dev);
 	struct ucc_geth *ug_regs;
-	u32 tempval;
-	struct ugeth_mii_info *mii_info = ugeth->mii_info;
+	struct ucc_fast *uf_regs;
+	struct phy_device *phydev = ugeth->phydev;
+	unsigned long flags;
+	int new_state = 0;
 
 	ug_regs = ugeth->ug_regs;
+	uf_regs = ugeth->uccf->uf_regs;
 
-	if (mii_info->link) {
+	spin_lock_irqsave(&ugeth->lock, flags);
+
+	if (phydev->link) {
+		u32 tempval = in_be32(&ug_regs->maccfg2);
+		u32 upsmr = in_be32(&uf_regs->upsmr);
 		/* Now we make sure that we can be in full duplex mode.
 		 * If not, we operate in half-duplex mode. */
-		if (mii_info->duplex != ugeth->oldduplex) {
-			if (!(mii_info->duplex)) {
-				tempval = in_be32(&ug_regs->maccfg2);
+		if (phydev->duplex != ugeth->oldduplex) {
+			new_state = 1;
+			if (!(phydev->duplex))
 				tempval &= ~(MACCFG2_FDX);
-				out_be32(&ug_regs->maccfg2, tempval);
-
-				ugeth_info("%s: Half Duplex", dev->name);
-			} else {
-				tempval = in_be32(&ug_regs->maccfg2);
+			else
 				tempval |= MACCFG2_FDX;
-				out_be32(&ug_regs->maccfg2, tempval);
-
-				ugeth_info("%s: Full Duplex", dev->name);
-			}
-
-			ugeth->oldduplex = mii_info->duplex;
+			ugeth->oldduplex = phydev->duplex;
 		}
 
-		if (mii_info->speed != ugeth->oldspeed) {
-			switch (mii_info->speed) {
-			case 1000:
-				ugeth->ug_info->enet_interface = ENET_1000_RGMII;
+		if (phydev->speed != ugeth->oldspeed) {
+			new_state = 1;
+			switch (phydev->speed) {
+			case SPEED_1000:
+				tempval = ((tempval &
+					    ~(MACCFG2_INTERFACE_MODE_MASK)) |
+					    MACCFG2_INTERFACE_MODE_BYTE);
 				break;
-			case 100:
-				ugeth->ug_info->enet_interface = ENET_100_RGMII;
-				break;
-			case 10:
-				ugeth->ug_info->enet_interface = ENET_10_RGMII;
+			case SPEED_100:
+			case SPEED_10:
+				tempval = ((tempval &
+					    ~(MACCFG2_INTERFACE_MODE_MASK)) |
+					    MACCFG2_INTERFACE_MODE_NIBBLE);
+				/* if reduced mode, re-set UPSMR.R10M */
+				if ((ugeth->phy_interface == PHY_INTERFACE_MODE_RMII) ||
+				    (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII) ||
+				    (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII_ID) ||
+				    (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) {
+					if (phydev->speed == SPEED_10)
+						upsmr |= UPSMR_R10M;
+					else
+						upsmr &= ~(UPSMR_R10M);
+				}
 				break;
 			default:
-				ugeth_warn
-				    ("%s: Ack!  Speed (%d) is not 10/100/1000!",
-				     dev->name, mii_info->speed);
+				if (netif_msg_link(ugeth))
+					ugeth_warn(
+						"%s: Ack!  Speed (%d) is not 10/100/1000!",
+						dev->name, phydev->speed);
 				break;
 			}
-			adjust_enet_interface(ugeth);
-
-			ugeth_info("%s: Speed %dBT", dev->name,
-				   mii_info->speed);
-
-			ugeth->oldspeed = mii_info->speed;
+			ugeth->oldspeed = phydev->speed;
 		}
 
+		out_be32(&ug_regs->maccfg2, tempval);
+		out_be32(&uf_regs->upsmr, upsmr);
+
 		if (!ugeth->oldlink) {
-			ugeth_info("%s: Link is up", dev->name);
+			new_state = 1;
 			ugeth->oldlink = 1;
-			netif_carrier_on(dev);
 			netif_schedule(dev);
 		}
-	} else {
-		if (ugeth->oldlink) {
-			ugeth_info("%s: Link is down", dev->name);
+	} else if (ugeth->oldlink) {
+			new_state = 1;
 			ugeth->oldlink = 0;
 			ugeth->oldspeed = 0;
 			ugeth->oldduplex = -1;
-			netif_carrier_off(dev);
-		}
 	}
+
+	if (new_state && netif_msg_link(ugeth))
+		phy_print_status(phydev);
+
+	spin_unlock_irqrestore(&ugeth->lock, flags);
 }
 
 /* Configure the PHY for dev.
@@ -1753,102 +1593,40 @@
  */
 static int init_phy(struct net_device *dev)
 {
-	struct ucc_geth_private *ugeth = netdev_priv(dev);
-	struct phy_info *curphy;
-	struct ucc_mii_mng *mii_regs;
-	struct ugeth_mii_info *mii_info;
-	int err;
+	struct ucc_geth_private *priv = netdev_priv(dev);
+	struct phy_device *phydev;
+	char phy_id[BUS_ID_SIZE];
 
-	mii_regs = &ugeth->ug_regs->miimng;
+	priv->oldlink = 0;
+	priv->oldspeed = 0;
+	priv->oldduplex = -1;
 
-	ugeth->oldlink = 0;
-	ugeth->oldspeed = 0;
-	ugeth->oldduplex = -1;
+	snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT, priv->ug_info->mdio_bus,
+			priv->ug_info->phy_address);
 
-	mii_info = kmalloc(sizeof(struct ugeth_mii_info), GFP_KERNEL);
+	phydev = phy_connect(dev, phy_id, &adjust_link, 0, priv->phy_interface);
 
-	if (NULL == mii_info) {
-		ugeth_err("%s: Could not allocate mii_info", dev->name);
-		return -ENOMEM;
+	if (IS_ERR(phydev)) {
+		printk("%s: Could not attach to PHY\n", dev->name);
+		return PTR_ERR(phydev);
 	}
 
-	mii_info->mii_regs = mii_regs;
-	mii_info->speed = SPEED_1000;
-	mii_info->duplex = DUPLEX_FULL;
-	mii_info->pause = 0;
-	mii_info->link = 0;
-
-	mii_info->advertising = (ADVERTISED_10baseT_Half |
+	phydev->supported &= (ADVERTISED_10baseT_Half |
 				 ADVERTISED_10baseT_Full |
 				 ADVERTISED_100baseT_Half |
-				 ADVERTISED_100baseT_Full |
-				 ADVERTISED_1000baseT_Full);
-	mii_info->autoneg = 1;
+				 ADVERTISED_100baseT_Full);
 
-	mii_info->mii_id = ugeth->ug_info->phy_address;
+	if (priv->max_speed == SPEED_1000)
+		phydev->supported |= ADVERTISED_1000baseT_Full;
 
-	mii_info->dev = dev;
+	phydev->advertising = phydev->supported;
 
-	mii_info->mdio_read = &read_phy_reg;
-	mii_info->mdio_write = &write_phy_reg;
-
-	spin_lock_init(&mii_info->mdio_lock);
-
-	ugeth->mii_info = mii_info;
-
-	spin_lock_irq(&ugeth->lock);
-
-	/* Set this UCC to be the master of the MII managment */
-	ucc_set_qe_mux_mii_mng(ugeth->ug_info->uf_info.ucc_num);
-
-	if (init_mii_management_configuration(1,
-					      ugeth->ug_info->
-					      miiPreambleSupress,
-					      &mii_regs->miimcfg,
-					      &mii_regs->miimind)) {
-		ugeth_err("%s: The MII Bus is stuck!", dev->name);
-		err = -1;
-		goto bus_fail;
-	}
-
-	spin_unlock_irq(&ugeth->lock);
-
-	/* get info for this PHY */
-	curphy = get_phy_info(ugeth->mii_info);
-
-	if (curphy == NULL) {
-		ugeth_err("%s: No PHY found", dev->name);
-		err = -1;
-		goto no_phy;
-	}
-
-	mii_info->phyinfo = curphy;
-
-	/* Run the commands which initialize the PHY */
-	if (curphy->init) {
-		err = curphy->init(ugeth->mii_info);
-		if (err)
-			goto phy_init_fail;
-	}
-
-	return 0;
-
-      phy_init_fail:
-      no_phy:
-      bus_fail:
-	kfree(mii_info);
-
-	return err;
-}
-
-#ifdef CONFIG_UGETH_TX_ON_DEMOND
-static int ugeth_transmit_on_demand(struct ucc_geth_private *ugeth)
-{
-	struct ucc_fastransmit_on_demand(ugeth->uccf);
+	priv->phydev = phydev;
 
 	return 0;
 }
-#endif
+
+
 
 static int ugeth_graceful_stop_tx(struct ucc_geth_private *ugeth)
 {
@@ -2356,6 +2134,8 @@
 	}
 	for (i = 0; i < ugeth->ug_info->numQueuesTx; i++) {
 		bd = ugeth->p_tx_bd_ring[i];
+		if (!bd)
+			continue;
 		for (j = 0; j < ugeth->ug_info->bdRingLenTx[i]; j++) {
 			if (ugeth->tx_skbuff[i][j]) {
 				dma_unmap_single(NULL,
@@ -2487,6 +2267,7 @@
 static void ucc_geth_stop(struct ucc_geth_private *ugeth)
 {
 	struct ucc_geth *ug_regs = ugeth->ug_regs;
+	struct phy_device *phydev = ugeth->phydev;
 	u32 tempval;
 
 	ugeth_vdbg("%s: IN", __FUNCTION__);
@@ -2495,8 +2276,7 @@
 	ugeth_disable(ugeth, COMM_DIR_RX_AND_TX);
 
 	/* Tell the kernel the link is down */
-	ugeth->mii_info->link = 0;
-	adjust_link(ugeth->dev);
+	phy_stop(phydev);
 
 	/* Mask all interrupts */
 	out_be32(ugeth->uccf->p_ucce, 0x00000000);
@@ -2509,50 +2289,24 @@
 	tempval &= ~(MACCFG1_ENABLE_RX | MACCFG1_ENABLE_TX);
 	out_be32(&ug_regs->maccfg1, tempval);
 
-	if (ugeth->ug_info->board_flags & FSL_UGETH_BRD_HAS_PHY_INTR) {
-		/* Clear any pending interrupts */
-		mii_clear_phy_interrupt(ugeth->mii_info);
-
-		/* Disable PHY Interrupts */
-		mii_configure_phy_interrupt(ugeth->mii_info,
-					    MII_INTERRUPT_DISABLED);
-	}
-
 	free_irq(ugeth->ug_info->uf_info.irq, ugeth->dev);
 
-	if (ugeth->ug_info->board_flags & FSL_UGETH_BRD_HAS_PHY_INTR) {
-		free_irq(ugeth->ug_info->phy_interrupt, ugeth->dev);
-	} else {
-		del_timer_sync(&ugeth->phy_info_timer);
-	}
-
 	ucc_geth_memclean(ugeth);
 }
 
-static int ucc_geth_startup(struct ucc_geth_private *ugeth)
+static int ucc_struct_init(struct ucc_geth_private *ugeth)
 {
-	struct ucc_geth_82xx_address_filtering_pram *p_82xx_addr_filt;
-	struct ucc_geth_init_pram *p_init_enet_pram;
-	struct ucc_fast_private *uccf;
 	struct ucc_geth_info *ug_info;
 	struct ucc_fast_info *uf_info;
-	struct ucc_fast *uf_regs;
-	struct ucc_geth *ug_regs;
-	int ret_val = -EINVAL;
-	u32 remoder = UCC_GETH_REMODER_INIT;
-	u32 init_enet_pram_offset, cecr_subblock, command, maccfg1;
-	u32 ifstat, i, j, size, l2qt, l3qt, length;
-	u16 temoder = UCC_GETH_TEMODER_INIT;
-	u16 test;
-	u8 function_code = 0;
-	u8 *bd, *endOfRing;
-	u8 numThreadsRxNumerical, numThreadsTxNumerical;
-
-	ugeth_vdbg("%s: IN", __FUNCTION__);
+	int i;
 
 	ug_info = ugeth->ug_info;
 	uf_info = &ug_info->uf_info;
 
+	/* Create CQs for hash tables */
+	INIT_LIST_HEAD(&ugeth->group_hash_q);
+	INIT_LIST_HEAD(&ugeth->ind_hash_q);
+
 	if (!((uf_info->bd_mem_part == MEM_PART_SYSTEM) ||
 	      (uf_info->bd_mem_part == MEM_PART_MURAM))) {
 		ugeth_err("%s: Bad memory partition value.", __FUNCTION__);
@@ -2647,12 +2401,42 @@
 	for (i = 0; i < ug_info->numQueuesTx; i++)
 		uf_info->uccm_mask |= (UCCE_TXBF_SINGLE_MASK << i);
 	/* Initialize the general fast UCC block. */
-	if (ucc_fast_init(uf_info, &uccf)) {
+	if (ucc_fast_init(uf_info, &ugeth->uccf)) {
 		ugeth_err("%s: Failed to init uccf.", __FUNCTION__);
 		ucc_geth_memclean(ugeth);
 		return -ENOMEM;
 	}
-	ugeth->uccf = uccf;
+
+	ugeth->ug_regs = (struct ucc_geth *) ioremap(uf_info->regs, sizeof(struct ucc_geth));
+
+	return 0;
+}
+
+static int ucc_geth_startup(struct ucc_geth_private *ugeth)
+{
+	struct ucc_geth_82xx_address_filtering_pram *p_82xx_addr_filt;
+	struct ucc_geth_init_pram *p_init_enet_pram;
+	struct ucc_fast_private *uccf;
+	struct ucc_geth_info *ug_info;
+	struct ucc_fast_info *uf_info;
+	struct ucc_fast *uf_regs;
+	struct ucc_geth *ug_regs;
+	int ret_val = -EINVAL;
+	u32 remoder = UCC_GETH_REMODER_INIT;
+	u32 init_enet_pram_offset, cecr_subblock, command, maccfg1;
+	u32 ifstat, i, j, size, l2qt, l3qt, length;
+	u16 temoder = UCC_GETH_TEMODER_INIT;
+	u16 test;
+	u8 function_code = 0;
+	u8 *bd, *endOfRing;
+	u8 numThreadsRxNumerical, numThreadsTxNumerical;
+
+	ugeth_vdbg("%s: IN", __FUNCTION__);
+	uccf = ugeth->uccf;
+	ug_info = ugeth->ug_info;
+	uf_info = &ug_info->uf_info;
+	uf_regs = uccf->uf_regs;
+	ug_regs = ugeth->ug_regs;
 
 	switch (ug_info->numThreadsRx) {
 	case UCC_GETH_NUM_OF_THREADS_1:
@@ -2711,10 +2495,6 @@
 	    || (ug_info->vlanOperationNonTagged !=
 		UCC_GETH_VLAN_OPERATION_NON_TAGGED_NOP);
 
-	uf_regs = uccf->uf_regs;
-	ug_regs = (struct ucc_geth *) (uccf->uf_regs);
-	ugeth->ug_regs = ug_regs;
-
 	init_default_reg_vals(&uf_regs->upsmr,
 			      &ug_regs->maccfg1, &ug_regs->maccfg2);
 
@@ -3177,8 +2957,8 @@
 	/* Size varies with number of Rx queues */
 	ugeth->rx_irq_coalescing_tbl_offset =
 	    qe_muram_alloc(ug_info->numQueuesRx *
-			   sizeof(struct ucc_geth_rx_interrupt_coalescing_entry),
-			   UCC_GETH_RX_INTERRUPT_COALESCING_ALIGNMENT);
+			   sizeof(struct ucc_geth_rx_interrupt_coalescing_entry)
+			   + 4, UCC_GETH_RX_INTERRUPT_COALESCING_ALIGNMENT);
 	if (IS_MURAM_ERR(ugeth->rx_irq_coalescing_tbl_offset)) {
 		ugeth_err
 		    ("%s: Can not allocate DPRAM memory for"
@@ -3359,13 +3139,6 @@
 		for (j = 0; j < NUM_OF_PADDRS; j++)
 			ugeth_82xx_filtering_clear_addr_in_paddr(ugeth, (u8) j);
 
-		/* Create CQs for hash tables */
-		if (ug_info->maxGroupAddrInHash > 0) {
-			INIT_LIST_HEAD(&ugeth->group_hash_q);
-		}
-		if (ug_info->maxIndAddrInHash > 0) {
-			INIT_LIST_HEAD(&ugeth->ind_hash_q);
-		}
 		p_82xx_addr_filt =
 		    (struct ucc_geth_82xx_address_filtering_pram *) ugeth->
 		    p_rx_glbl_pram->addressfiltering;
@@ -3562,6 +3335,9 @@
 static int ucc_geth_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct ucc_geth_private *ugeth = netdev_priv(dev);
+#ifdef CONFIG_UGETH_TX_ON_DEMAND
+	struct ucc_fast_private *uccf;
+#endif
 	u8 *bd;			/* BD pointer */
 	u32 bd_status;
 	u8 txQ = 0;
@@ -3620,6 +3396,10 @@
 		out_be16(ugeth->p_cpucount[txQ], ugeth->cpucount[txQ]);
 	}
 
+#ifdef CONFIG_UGETH_TX_ON_DEMAND
+	uccf = ugeth->uccf;
+	out_be16(uccf->p_utodr, UCC_FAST_TOD);
+#endif
 	spin_unlock_irq(&ugeth->lock);
 
 	return 0;
@@ -3635,7 +3415,6 @@
 
 	ugeth_vdbg("%s: IN", __FUNCTION__);
 
-	spin_lock(&ugeth->lock);
 	/* collect received buffers */
 	bd = ugeth->rxBd[rxQ];
 
@@ -3683,7 +3462,6 @@
 		skb = get_new_skb(ugeth, bd);
 		if (!skb) {
 			ugeth_warn("%s: No Rx Data Buffer", __FUNCTION__);
-			spin_unlock(&ugeth->lock);
 			ugeth->stats.rx_dropped++;
 			break;
 		}
@@ -3704,7 +3482,6 @@
 	}
 
 	ugeth->rxBd[rxQ] = bd;
-	spin_unlock(&ugeth->lock);
 	return howmany;
 }
 
@@ -3756,23 +3533,38 @@
 static int ucc_geth_poll(struct net_device *dev, int *budget)
 {
 	struct ucc_geth_private *ugeth = netdev_priv(dev);
+	struct ucc_geth_info *ug_info;
+	struct ucc_fast_private *uccf;
 	int howmany;
-	int rx_work_limit = *budget;
-	u8 rxQ = 0;
+	u8 i;
+	int rx_work_limit;
+	register u32 uccm;
 
+	ug_info = ugeth->ug_info;
+
+	rx_work_limit = *budget;
 	if (rx_work_limit > dev->quota)
 		rx_work_limit = dev->quota;
 
-	howmany = ucc_geth_rx(ugeth, rxQ, rx_work_limit);
+	howmany = 0;
+
+	for (i = 0; i < ug_info->numQueuesRx; i++) {
+		howmany += ucc_geth_rx(ugeth, i, rx_work_limit);
+	}
 
 	dev->quota -= howmany;
 	rx_work_limit -= howmany;
 	*budget -= howmany;
 
-	if (rx_work_limit >= 0)
+	if (rx_work_limit > 0) {
 		netif_rx_complete(dev);
+		uccf = ugeth->uccf;
+		uccm = in_be32(uccf->p_uccm);
+		uccm |= UCCE_RX_EVENTS;
+		out_be32(uccf->p_uccm, uccm);
+	}
 
-	return (rx_work_limit < 0) ? 1 : 0;
+	return (rx_work_limit > 0) ? 0 : 1;
 }
 #endif				/* CONFIG_UGETH_NAPI */
 
@@ -3782,10 +3574,13 @@
 	struct ucc_geth_private *ugeth = netdev_priv(dev);
 	struct ucc_fast_private *uccf;
 	struct ucc_geth_info *ug_info;
-	register u32 ucce = 0;
-	register u32 bit_mask = UCCE_RXBF_SINGLE_MASK;
-	register u32 tx_mask = UCCE_TXBF_SINGLE_MASK;
-	register u8 i;
+	register u32 ucce;
+	register u32 uccm;
+#ifndef CONFIG_UGETH_NAPI
+	register u32 rx_mask;
+#endif
+	register u32 tx_mask;
+	u8 i;
 
 	ugeth_vdbg("%s: IN", __FUNCTION__);
 
@@ -3795,174 +3590,57 @@
 	uccf = ugeth->uccf;
 	ug_info = ugeth->ug_info;
 
-	do {
-		ucce |= (u32) (in_be32(uccf->p_ucce) & in_be32(uccf->p_uccm));
+	/* read and clear events */
+	ucce = (u32) in_be32(uccf->p_ucce);
+	uccm = (u32) in_be32(uccf->p_uccm);
+	ucce &= uccm;
+	out_be32(uccf->p_ucce, ucce);
 
-		/* clear event bits for next time */
-		/* Side effect here is to mask ucce variable
-		for future processing below. */
-		out_be32(uccf->p_ucce, ucce);	/* Clear with ones,
-						but only bits in UCCM */
-
-		/* We ignore Tx interrupts because Tx confirmation is
-		done inside Tx routine */
-
-		for (i = 0; i < ug_info->numQueuesRx; i++) {
-			if (ucce & bit_mask)
-				ucc_geth_rx(ugeth, i,
-					    (int)ugeth->ug_info->
-					    bdRingLenRx[i]);
-			ucce &= ~bit_mask;
-			bit_mask <<= 1;
+	/* check for receive events that require processing */
+	if (ucce & UCCE_RX_EVENTS) {
+#ifdef CONFIG_UGETH_NAPI
+		if (netif_rx_schedule_prep(dev)) {
+		uccm &= ~UCCE_RX_EVENTS;
+			out_be32(uccf->p_uccm, uccm);
+			__netif_rx_schedule(dev);
 		}
+#else
+		rx_mask = UCCE_RXBF_SINGLE_MASK;
+		for (i = 0; i < ug_info->numQueuesRx; i++) {
+			if (ucce & rx_mask)
+				ucc_geth_rx(ugeth, i, (int)ugeth->ug_info->bdRingLenRx[i]);
+			ucce &= ~rx_mask;
+			rx_mask <<= 1;
+		}
+#endif /* CONFIG_UGETH_NAPI */
+	}
 
+	/* Tx event processing */
+	if (ucce & UCCE_TX_EVENTS) {
+		spin_lock(&ugeth->lock);
+		tx_mask = UCCE_TXBF_SINGLE_MASK;
 		for (i = 0; i < ug_info->numQueuesTx; i++) {
 			if (ucce & tx_mask)
 				ucc_geth_tx(dev, i);
 			ucce &= ~tx_mask;
 			tx_mask <<= 1;
 		}
+		spin_unlock(&ugeth->lock);
+	}
 
-		/* Exceptions */
+	/* Errors and other events */
+	if (ucce & UCCE_OTHER) {
 		if (ucce & UCCE_BSY) {
-			ugeth_vdbg("Got BUSY irq!!!!");
 			ugeth->stats.rx_errors++;
-			ucce &= ~UCCE_BSY;
 		}
-		if (ucce & UCCE_OTHER) {
-			ugeth_vdbg("Got frame with error (ucce - 0x%08x)!!!!",
-				   ucce);
-			ugeth->stats.rx_errors++;
-			ucce &= ~ucce;
+		if (ucce & UCCE_TXE) {
+			ugeth->stats.tx_errors++;
 		}
 	}
-	while (ucce);
 
 	return IRQ_HANDLED;
 }
 
-static irqreturn_t phy_interrupt(int irq, void *dev_id)
-{
-	struct net_device *dev = (struct net_device *)dev_id;
-	struct ucc_geth_private *ugeth = netdev_priv(dev);
-
-	ugeth_vdbg("%s: IN", __FUNCTION__);
-
-	/* Clear the interrupt */
-	mii_clear_phy_interrupt(ugeth->mii_info);
-
-	/* Disable PHY interrupts */
-	mii_configure_phy_interrupt(ugeth->mii_info, MII_INTERRUPT_DISABLED);
-
-	/* Schedule the phy change */
-	schedule_work(&ugeth->tq);
-
-	return IRQ_HANDLED;
-}
-
-/* Scheduled by the phy_interrupt/timer to handle PHY changes */
-static void ugeth_phy_change(struct work_struct *work)
-{
-	struct ucc_geth_private *ugeth =
-		container_of(work, struct ucc_geth_private, tq);
-	struct net_device *dev = ugeth->dev;
-	struct ucc_geth *ug_regs;
-	int result = 0;
-
-	ugeth_vdbg("%s: IN", __FUNCTION__);
-
-	ug_regs = ugeth->ug_regs;
-
-	/* Delay to give the PHY a chance to change the
-	 * register state */
-	msleep(1);
-
-	/* Update the link, speed, duplex */
-	result = ugeth->mii_info->phyinfo->read_status(ugeth->mii_info);
-
-	/* Adjust the known status as long as the link
-	 * isn't still coming up */
-	if ((0 == result) || (ugeth->mii_info->link == 0))
-		adjust_link(dev);
-
-	/* Reenable interrupts, if needed */
-	if (ugeth->ug_info->board_flags & FSL_UGETH_BRD_HAS_PHY_INTR)
-		mii_configure_phy_interrupt(ugeth->mii_info,
-					    MII_INTERRUPT_ENABLED);
-}
-
-/* Called every so often on systems that don't interrupt
- * the core for PHY changes */
-static void ugeth_phy_timer(unsigned long data)
-{
-	struct net_device *dev = (struct net_device *)data;
-	struct ucc_geth_private *ugeth = netdev_priv(dev);
-
-	schedule_work(&ugeth->tq);
-
-	mod_timer(&ugeth->phy_info_timer, jiffies + PHY_CHANGE_TIME * HZ);
-}
-
-/* Keep trying aneg for some time
- * If, after GFAR_AN_TIMEOUT seconds, it has not
- * finished, we switch to forced.
- * Either way, once the process has completed, we either
- * request the interrupt, or switch the timer over to
- * using ugeth_phy_timer to check status */
-static void ugeth_phy_startup_timer(unsigned long data)
-{
-	struct ugeth_mii_info *mii_info = (struct ugeth_mii_info *)data;
-	struct ucc_geth_private *ugeth = netdev_priv(mii_info->dev);
-	static int secondary = UGETH_AN_TIMEOUT;
-	int result;
-
-	/* Configure the Auto-negotiation */
-	result = mii_info->phyinfo->config_aneg(mii_info);
-
-	/* If autonegotiation failed to start, and
-	 * we haven't timed out, reset the timer, and return */
-	if (result && secondary--) {
-		mod_timer(&ugeth->phy_info_timer, jiffies + HZ);
-		return;
-	} else if (result) {
-		/* Couldn't start autonegotiation.
-		 * Try switching to forced */
-		mii_info->autoneg = 0;
-		result = mii_info->phyinfo->config_aneg(mii_info);
-
-		/* Forcing failed!  Give up */
-		if (result) {
-			ugeth_err("%s: Forcing failed!", mii_info->dev->name);
-			return;
-		}
-	}
-
-	/* Kill the timer so it can be restarted */
-	del_timer_sync(&ugeth->phy_info_timer);
-
-	/* Grab the PHY interrupt, if necessary/possible */
-	if (ugeth->ug_info->board_flags & FSL_UGETH_BRD_HAS_PHY_INTR) {
-		if (request_irq(ugeth->ug_info->phy_interrupt,
-				phy_interrupt, IRQF_SHARED,
-				"phy_interrupt", mii_info->dev) < 0) {
-			ugeth_err("%s: Can't get IRQ %d (PHY)",
-				  mii_info->dev->name,
-				  ugeth->ug_info->phy_interrupt);
-		} else {
-			mii_configure_phy_interrupt(ugeth->mii_info,
-						    MII_INTERRUPT_ENABLED);
-			return;
-		}
-	}
-
-	/* Start the timer again, this time in order to
-	 * handle a change in status */
-	init_timer(&ugeth->phy_info_timer);
-	ugeth->phy_info_timer.function = &ugeth_phy_timer;
-	ugeth->phy_info_timer.data = (unsigned long)mii_info->dev;
-	mod_timer(&ugeth->phy_info_timer, jiffies + PHY_CHANGE_TIME * HZ);
-}
-
 /* Called when something needs to use the ethernet device */
 /* Returns 0 for success. */
 static int ucc_geth_open(struct net_device *dev)
@@ -3979,6 +3657,12 @@
 		return -EINVAL;
 	}
 
+	err = ucc_struct_init(ugeth);
+	if (err) {
+		ugeth_err("%s: Cannot configure internal struct, aborting.", dev->name);
+		return err;
+	}
+
 	err = ucc_geth_startup(ugeth);
 	if (err) {
 		ugeth_err("%s: Cannot configure net device, aborting.",
@@ -4006,10 +3690,12 @@
 
 	err = init_phy(dev);
 	if (err) {
-		ugeth_err("%s: Cannot initialzie PHY, aborting.", dev->name);
+		ugeth_err("%s: Cannot initialize PHY, aborting.", dev->name);
 		return err;
 	}
-#ifndef CONFIG_UGETH_NAPI
+
+	phy_start(ugeth->phydev);
+
 	err =
 	    request_irq(ugeth->ug_info->uf_info.irq, ucc_geth_irq_handler, 0,
 			"UCC Geth", dev);
@@ -4019,15 +3705,6 @@
 		ucc_geth_stop(ugeth);
 		return err;
 	}
-#endif				/* CONFIG_UGETH_NAPI */
-
-	/* Set up the PHY change work queue */
-	INIT_WORK(&ugeth->tq, ugeth_phy_change);
-
-	init_timer(&ugeth->phy_info_timer);
-	ugeth->phy_info_timer.function = &ugeth_phy_startup_timer;
-	ugeth->phy_info_timer.data = (unsigned long)ugeth->mii_info;
-	mod_timer(&ugeth->phy_info_timer, jiffies + HZ);
 
 	err = ugeth_enable(ugeth, COMM_DIR_RX_AND_TX);
 	if (err) {
@@ -4050,11 +3727,8 @@
 
 	ucc_geth_stop(ugeth);
 
-	/* Shutdown the PHY */
-	if (ugeth->mii_info->phyinfo->close)
-		ugeth->mii_info->phyinfo->close(ugeth->mii_info);
-
-	kfree(ugeth->mii_info);
+	phy_disconnect(ugeth->phydev);
+	ugeth->phydev = NULL;
 
 	netif_stop_queue(dev);
 
@@ -4063,20 +3737,53 @@
 
 const struct ethtool_ops ucc_geth_ethtool_ops = { };
 
+static phy_interface_t to_phy_interface(const char *interface_type)
+{
+	if (strcasecmp(interface_type, "mii") == 0)
+		return PHY_INTERFACE_MODE_MII;
+	if (strcasecmp(interface_type, "gmii") == 0)
+		return PHY_INTERFACE_MODE_GMII;
+	if (strcasecmp(interface_type, "tbi") == 0)
+		return PHY_INTERFACE_MODE_TBI;
+	if (strcasecmp(interface_type, "rmii") == 0)
+		return PHY_INTERFACE_MODE_RMII;
+	if (strcasecmp(interface_type, "rgmii") == 0)
+		return PHY_INTERFACE_MODE_RGMII;
+	if (strcasecmp(interface_type, "rgmii-id") == 0)
+		return PHY_INTERFACE_MODE_RGMII_ID;
+	if (strcasecmp(interface_type, "rtbi") == 0)
+		return PHY_INTERFACE_MODE_RTBI;
+
+	return PHY_INTERFACE_MODE_MII;
+}
+
 static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *match)
 {
 	struct device *device = &ofdev->dev;
 	struct device_node *np = ofdev->node;
+	struct device_node *mdio;
 	struct net_device *dev = NULL;
 	struct ucc_geth_private *ugeth = NULL;
 	struct ucc_geth_info *ug_info;
 	struct resource res;
 	struct device_node *phy;
-	int err, ucc_num, phy_interface;
-	static int mii_mng_configured = 0;
+	int err, ucc_num, max_speed = 0;
 	const phandle *ph;
 	const unsigned int *prop;
 	const void *mac_addr;
+	phy_interface_t phy_interface;
+	static const int enet_to_speed[] = {
+		SPEED_10, SPEED_10, SPEED_10,
+		SPEED_100, SPEED_100, SPEED_100,
+		SPEED_1000, SPEED_1000, SPEED_1000, SPEED_1000,
+	};
+	static const phy_interface_t enet_to_phy_interface[] = {
+		PHY_INTERFACE_MODE_MII, PHY_INTERFACE_MODE_RMII,
+		PHY_INTERFACE_MODE_RGMII, PHY_INTERFACE_MODE_MII,
+		PHY_INTERFACE_MODE_RMII, PHY_INTERFACE_MODE_RGMII,
+		PHY_INTERFACE_MODE_GMII, PHY_INTERFACE_MODE_RGMII,
+		PHY_INTERFACE_MODE_TBI, PHY_INTERFACE_MODE_RTBI,
+	};
 
 	ugeth_vdbg("%s: IN", __FUNCTION__);
 
@@ -4087,6 +3794,7 @@
 
 	ug_info = &ugeth_info[ucc_num];
 	ug_info->uf_info.ucc_num = ucc_num;
+
 	prop = get_property(np, "rx-clock", NULL);
 	ug_info->uf_info.rx_clock = *prop;
 	prop = get_property(np, "tx-clock", NULL);
@@ -4104,13 +3812,72 @@
 	if (phy == NULL)
 		return -ENODEV;
 
+	/* set the PHY address */
 	prop = get_property(phy, "reg", NULL);
+	if (prop == NULL)
+		return -1;
 	ug_info->phy_address = *prop;
-	prop = get_property(phy, "interface", NULL);
-	ug_info->enet_interface = *prop;
-	ug_info->phy_interrupt = irq_of_parse_and_map(phy, 0);
-	ug_info->board_flags = (ug_info->phy_interrupt == NO_IRQ)?
-			0:FSL_UGETH_BRD_HAS_PHY_INTR;
+
+	/* get the phy interface type, or default to MII */
+	prop = get_property(np, "interface-type", NULL);
+	if (!prop) {
+		/* handle interface property present in old trees */
+		prop = get_property(phy, "interface", NULL);
+		if (prop != NULL)
+			phy_interface = enet_to_phy_interface[*prop];
+		else
+			phy_interface = PHY_INTERFACE_MODE_MII;
+	} else {
+		phy_interface = to_phy_interface((const char *)prop);
+	}
+
+	/* get speed, or derive from interface */
+	prop = get_property(np, "max-speed", NULL);
+	if (!prop) {
+		/* handle interface property present in old trees */
+		prop = get_property(phy, "interface", NULL);
+		if (prop != NULL)
+			max_speed = enet_to_speed[*prop];
+	} else {
+		max_speed = *prop;
+	}
+	if (!max_speed) {
+		switch (phy_interface) {
+		case PHY_INTERFACE_MODE_GMII:
+		case PHY_INTERFACE_MODE_RGMII:
+		case PHY_INTERFACE_MODE_RGMII_ID:
+		case PHY_INTERFACE_MODE_TBI:
+		case PHY_INTERFACE_MODE_RTBI:
+			max_speed = SPEED_1000;
+			break;
+		default:
+			max_speed = SPEED_100;
+			break;
+		}
+	}
+
+	if (max_speed == SPEED_1000) {
+		ug_info->uf_info.urfs = UCC_GETH_URFS_GIGA_INIT;
+		ug_info->uf_info.urfet = UCC_GETH_URFET_GIGA_INIT;
+		ug_info->uf_info.urfset = UCC_GETH_URFSET_GIGA_INIT;
+		ug_info->uf_info.utfs = UCC_GETH_UTFS_GIGA_INIT;
+		ug_info->uf_info.utfet = UCC_GETH_UTFET_GIGA_INIT;
+		ug_info->uf_info.utftt = UCC_GETH_UTFTT_GIGA_INIT;
+	}
+
+	/* Set the bus id */
+	mdio = of_get_parent(phy);
+
+	if (mdio == NULL)
+		return -1;
+
+	err = of_address_to_resource(mdio, 0, &res);
+	of_node_put(mdio);
+
+	if (err)
+		return -1;
+
+	ug_info->mdio_bus = res.start;
 
 	printk(KERN_INFO "ucc_geth: UCC%1d at 0x%8x (irq = %d) \n",
 		ug_info->uf_info.ucc_num + 1, ug_info->uf_info.regs,
@@ -4122,43 +3889,6 @@
 		return -ENODEV;
 	}
 
-	/* FIXME: Work around for early chip rev.               */
-	/* There's a bug in initial chip rev(s) in the RGMII ac */
-	/* timing.						*/
-	/* The following compensates by writing to the reserved */
-	/* QE Port Output Hold Registers (CPOH1?).              */
-	prop = get_property(phy, "interface", NULL);
-	phy_interface = *prop;
-	if ((phy_interface == ENET_1000_RGMII) ||
-			(phy_interface == ENET_100_RGMII) ||
-			(phy_interface == ENET_10_RGMII)) {
-		struct device_node *soc;
-		phys_addr_t immrbase = -1;
-		u32 *tmp_reg;
-		u32 tmp_val;
-
-		soc = of_find_node_by_type(NULL, "soc");
-		if (soc) {
-			unsigned int size;
-			const void *prop = get_property(soc, "reg", &size);
-			immrbase = of_translate_address(soc, prop);
-			of_node_put(soc);
-		};
-
-		tmp_reg = (u32 *) ioremap(immrbase + 0x14A8, 0x4);
-		tmp_val = in_be32(tmp_reg);
-		if (ucc_num == 1)
-			out_be32(tmp_reg, tmp_val | 0x00003000);
-		else if (ucc_num == 2)
-			out_be32(tmp_reg, tmp_val | 0x0c000000);
-		iounmap(tmp_reg);
-	}
-
-	if (!mii_mng_configured) {
-		ucc_set_qe_mux_mii_mng(ucc_num);
-		mii_mng_configured = 1;
-	}
-
 	/* Create an ethernet device instance */
 	dev = alloc_etherdev(sizeof(*ugeth));
 
@@ -4192,6 +3922,10 @@
 	dev->set_multicast_list = ucc_geth_set_multi;
 	dev->ethtool_ops = &ucc_geth_ethtool_ops;
 
+	ugeth->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1;
+	ugeth->phy_interface = phy_interface;
+	ugeth->max_speed = max_speed;
+
 	err = register_netdev(dev);
 	if (err) {
 		ugeth_err("%s: Cannot register net device, aborting.",
@@ -4200,13 +3934,13 @@
 		return err;
 	}
 
-	ugeth->ug_info = ug_info;
-	ugeth->dev = dev;
-
 	mac_addr = of_get_mac_address(np);
 	if (mac_addr)
 		memcpy(dev->dev_addr, mac_addr, 6);
 
+	ugeth->ug_info = ug_info;
+	ugeth->dev = dev;
+
 	return 0;
 }
 
@@ -4242,19 +3976,30 @@
 
 static int __init ucc_geth_init(void)
 {
-	int i;
+	int i, ret;
+
+	ret = uec_mdio_init();
+
+	if (ret)
+		return ret;
 
 	printk(KERN_INFO "ucc_geth: " DRV_DESC "\n");
 	for (i = 0; i < 8; i++)
 		memcpy(&(ugeth_info[i]), &ugeth_primary_info,
 		       sizeof(ugeth_primary_info));
 
-	return of_register_platform_driver(&ucc_geth_driver);
+	ret = of_register_platform_driver(&ucc_geth_driver);
+
+	if (ret)
+		uec_mdio_exit();
+
+	return ret;
 }
 
 static void __exit ucc_geth_exit(void)
 {
 	of_unregister_platform_driver(&ucc_geth_driver);
+	uec_mdio_exit();
 }
 
 module_init(ucc_geth_init);
@@ -4262,4 +4007,5 @@
 
 MODULE_AUTHOR("Freescale Semiconductor, Inc");
 MODULE_DESCRIPTION(DRV_DESC);
+MODULE_VERSION(DRV_VERSION);
 MODULE_LICENSE("GPL");
diff --git a/drivers/net/ucc_geth.h b/drivers/net/ucc_geth.h
index a665612..a29e1c3 100644
--- a/drivers/net/ucc_geth.h
+++ b/drivers/net/ucc_geth.h
@@ -28,6 +28,8 @@
 #include <asm/ucc.h>
 #include <asm/ucc_fast.h>
 
+#include "ucc_geth_mii.h"
+
 #define NUM_TX_QUEUES                   8
 #define NUM_RX_QUEUES                   8
 #define NUM_BDS_IN_PREFETCHED_BDS       4
@@ -36,15 +38,6 @@
 #define ENET_INIT_PARAM_MAX_ENTRIES_RX  9
 #define ENET_INIT_PARAM_MAX_ENTRIES_TX  8
 
-struct ucc_mii_mng {
-	u32 miimcfg;		/* MII management configuration reg */
-	u32 miimcom;		/* MII management command reg */
-	u32 miimadd;		/* MII management address reg */
-	u32 miimcon;		/* MII management control reg */
-	u32 miimstat;		/* MII management status reg */
-	u32 miimind;		/* MII management indication reg */
-} __attribute__ ((packed));
-
 struct ucc_geth {
 	struct ucc_fast uccf;
 
@@ -53,7 +46,7 @@
 	u32 ipgifg;		/* interframe gap reg.  */
 	u32 hafdup;		/* half-duplex reg.  */
 	u8 res1[0x10];
-	struct ucc_mii_mng miimng;	/* MII management structure */
+	u8 miimng[0x18];	/* MII management structure moved to _mii.h */
 	u32 ifctl;		/* interface control reg */
 	u32 ifstat;		/* interface statux reg */
 	u32 macstnaddr1;	/* mac station address part 1 reg */
@@ -212,6 +205,9 @@
 #define UCCE_OTHER       (UCCE_SCAR | UCCE_GRA  | UCCE_CBPR | UCCE_BSY  |\
 			UCCE_RXC  | UCCE_TXC  | UCCE_TXE)
 
+#define UCCE_RX_EVENTS							(UCCE_RXF | UCCE_BSY)
+#define UCCE_TX_EVENTS							(UCCE_TXB | UCCE_TXE)
+
 /* UCC GETH UPSMR (Protocol Specific Mode Register) */
 #define UPSMR_ECM                               0x04000000	/* Enable CAM
 								   Miss or
@@ -381,66 +377,6 @@
 #define UCCS_MPD                                0x01	/* Magic Packet
 							   Detected */
 
-/* UCC GETH MIIMCFG (MII Management Configuration Register) */
-#define MIIMCFG_RESET_MANAGEMENT                0x80000000	/* Reset
-								   management */
-#define MIIMCFG_NO_PREAMBLE                     0x00000010	/* Preamble
-								   suppress */
-#define MIIMCFG_CLOCK_DIVIDE_SHIFT              (31 - 31)	/* clock divide
-								   << shift */
-#define MIIMCFG_CLOCK_DIVIDE_MAX                0xf	/* clock divide max val
-							 */
-#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_2    0x00000000	/* divide by 2 */
-#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_4    0x00000001	/* divide by 4 */
-#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_6    0x00000002	/* divide by 6 */
-#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_8    0x00000003	/* divide by 8 */
-#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_10   0x00000004	/* divide by 10
-								 */
-#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_14   0x00000005	/* divide by 14
-								 */
-#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_16   0x00000008	/* divide by 16
-								 */
-#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_20   0x00000006	/* divide by 20
-								 */
-#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_28   0x00000007	/* divide by 28
-								 */
-#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_32   0x00000009	/* divide by 32
-								 */
-#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_48   0x0000000a	/* divide by 48
-								 */
-#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_64   0x0000000b	/* divide by 64
-								 */
-#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_80   0x0000000c	/* divide by 80
-								 */
-#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_112  0x0000000d	/* divide by
-								   112 */
-#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_160  0x0000000e	/* divide by
-								   160 */
-#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_224  0x0000000f	/* divide by
-								   224 */
-
-/* UCC GETH MIIMCOM (MII Management Command Register) */
-#define MIIMCOM_SCAN_CYCLE                      0x00000002	/* Scan cycle */
-#define MIIMCOM_READ_CYCLE                      0x00000001	/* Read cycle */
-
-/* UCC GETH MIIMADD (MII Management Address Register) */
-#define MIIMADD_PHY_ADDRESS_SHIFT               (31 - 23)	/* PHY Address
-								   << shift */
-#define MIIMADD_PHY_REGISTER_SHIFT              (31 - 31)	/* PHY Register
-								   << shift */
-
-/* UCC GETH MIIMCON (MII Management Control Register) */
-#define MIIMCON_PHY_CONTROL_SHIFT               (31 - 31)	/* PHY Control
-								   << shift */
-#define MIIMCON_PHY_STATUS_SHIFT                (31 - 31)	/* PHY Status
-								   << shift */
-
-/* UCC GETH MIIMIND (MII Management Indicator Register) */
-#define MIIMIND_NOT_VALID                       0x00000004	/* Not valid */
-#define MIIMIND_SCAN                            0x00000002	/* Scan in
-								   progress */
-#define MIIMIND_BUSY                            0x00000001
-
 /* UCC GETH IFSTAT (Interface Status Register) */
 #define IFSTAT_EXCESS_DEFER                     0x00000200	/* Excessive
 								   transmission
@@ -931,8 +867,7 @@
 #define UCC_GETH_SCHEDULER_ALIGNMENT		4	/* This is a guess */
 #define UCC_GETH_TX_STATISTICS_ALIGNMENT	4	/* This is a guess */
 #define UCC_GETH_RX_STATISTICS_ALIGNMENT	4	/* This is a guess */
-#define UCC_GETH_RX_INTERRUPT_COALESCING_ALIGNMENT	4	/* This is a
-								   guess */
+#define UCC_GETH_RX_INTERRUPT_COALESCING_ALIGNMENT	64
 #define UCC_GETH_RX_BD_QUEUES_ALIGNMENT		8	/* This is a guess */
 #define UCC_GETH_RX_PREFETCHED_BDS_ALIGNMENT	128	/* This is a guess */
 #define UCC_GETH_RX_EXTENDED_FILTERING_GLOBAL_PARAMETERS_ALIGNMENT 4	/* This
@@ -1009,15 +944,6 @@
 								   register */
 #define UCC_GETH_MACCFG1_INIT                   0
 #define UCC_GETH_MACCFG2_INIT                   (MACCFG2_RESERVED_1)
-#define UCC_GETH_MIIMCFG_MNGMNT_CLC_DIV_INIT    \
-				(MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_112)
-
-/* Ethernet speed */
-enum enet_speed {
-	ENET_SPEED_10BT,	/* 10 Base T */
-	ENET_SPEED_100BT,	/* 100 Base T */
-	ENET_SPEED_1000BT	/* 1000 Base T */
-};
 
 /* Ethernet Address Type. */
 enum enet_addr_type {
@@ -1026,22 +952,6 @@
 	ENET_ADDR_TYPE_BROADCAST
 };
 
-/* TBI / MII Set Register */
-enum enet_tbi_mii_reg {
-	ENET_TBI_MII_CR = 0x00,	/* Control (CR ) */
-	ENET_TBI_MII_SR = 0x01,	/* Status (SR ) */
-	ENET_TBI_MII_ANA = 0x04,	/* AN advertisement (ANA ) */
-	ENET_TBI_MII_ANLPBPA = 0x05,	/* AN link partner base page ability
-					   (ANLPBPA) */
-	ENET_TBI_MII_ANEX = 0x06,	/* AN expansion (ANEX ) */
-	ENET_TBI_MII_ANNPT = 0x07,	/* AN next page transmit (ANNPT ) */
-	ENET_TBI_MII_ANLPANP = 0x08,	/* AN link partner ability next page
-					   (ANLPANP) */
-	ENET_TBI_MII_EXST = 0x0F,	/* Extended status (EXST ) */
-	ENET_TBI_MII_JD = 0x10,	/* Jitter diagnostics (JD ) */
-	ENET_TBI_MII_TBICON = 0x11	/* TBI control (TBICON ) */
-};
-
 /* UCC GETH 82xx Ethernet Address Recognition Location */
 enum ucc_geth_enet_address_recognition_location {
 	UCC_GETH_ENET_ADDRESS_RECOGNITION_LOCATION_STATION_ADDRESS,/* station
@@ -1239,8 +1149,7 @@
 	u16 pausePeriod;
 	u16 extensionField;
 	u8 phy_address;
-	u32 board_flags;
-	u32 phy_interrupt;
+	u32 mdio_bus;
 	u8 weightfactor[NUM_TX_QUEUES];
 	u8 interruptcoalescingmaxvalue[NUM_RX_QUEUES];
 	u8 l2qt[UCC_GETH_VLAN_PRIORITY_MAX];
@@ -1249,7 +1158,6 @@
 	u8 iphoffset[TX_IP_OFFSET_ENTRY_MAX];
 	u16 bdRingLenTx[NUM_TX_QUEUES];
 	u16 bdRingLenRx[NUM_RX_QUEUES];
-	enum enet_interface enet_interface;
 	enum ucc_geth_num_of_station_addresses numStationAddresses;
 	enum qe_fltr_largest_external_tbl_lookup_key_size
 	    largestexternallookupkeysize;
@@ -1326,9 +1234,11 @@
 	/* index of the first skb which hasn't been transmitted yet. */
 	u16 skb_dirtytx[NUM_TX_QUEUES];
 
-	struct work_struct tq;
-	struct timer_list phy_info_timer;
 	struct ugeth_mii_info *mii_info;
+	struct phy_device *phydev;
+	phy_interface_t phy_interface;
+	int max_speed;
+	uint32_t msg_enable;
 	int oldspeed;
 	int oldduplex;
 	int oldlink;
diff --git a/drivers/net/ucc_geth_mii.c b/drivers/net/ucc_geth_mii.c
new file mode 100644
index 0000000..73b5a53
--- /dev/null
+++ b/drivers/net/ucc_geth_mii.c
@@ -0,0 +1,279 @@
+/*
+ * drivers/net/ucc_geth_mii.c
+ *
+ * Gianfar Ethernet Driver -- MIIM bus implementation
+ * Provides Bus interface for MIIM regs
+ *
+ * Author: Li Yang
+ *
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <asm/ocp.h>
+#include <linux/crc32.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/fsl_devices.h>
+
+#include <asm/of_platform.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/ucc.h>
+
+#include "ucc_geth_mii.h"
+#include "ucc_geth.h"
+
+#define DEBUG
+#ifdef DEBUG
+#define vdbg(format, arg...) printk(KERN_DEBUG , format "\n" , ## arg)
+#else
+#define vdbg(format, arg...) do {} while(0)
+#endif
+
+#define DRV_DESC "QE UCC Ethernet Controller MII Bus"
+#define DRV_NAME "fsl-uec_mdio"
+
+/* Write value to the PHY for this device to the register at regnum, */
+/* waiting until the write is done before it returns.  All PHY */
+/* configuration has to be done through the master UEC MIIM regs */
+int uec_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value)
+{
+	struct ucc_mii_mng __iomem *regs = (void __iomem *)bus->priv;
+
+	/* Setting up the MII Mangement Address Register */
+	out_be32(&regs->miimadd,
+		 (mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | regnum);
+
+	/* Setting up the MII Mangement Control Register with the value */
+	out_be32(&regs->miimcon, value);
+
+	/* Wait till MII management write is complete */
+	while ((in_be32(&regs->miimind)) & MIIMIND_BUSY)
+		cpu_relax();
+
+	return 0;
+}
+
+/* Reads from register regnum in the PHY for device dev, */
+/* returning the value.  Clears miimcom first.  All PHY */
+/* configuration has to be done through the TSEC1 MIIM regs */
+int uec_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+	struct ucc_mii_mng __iomem *regs = (void __iomem *)bus->priv;
+	u16 value;
+
+	/* Setting up the MII Mangement Address Register */
+	out_be32(&regs->miimadd,
+		 (mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | regnum);
+
+	/* Clear miimcom, perform an MII management read cycle */
+	out_be32(&regs->miimcom, 0);
+	out_be32(&regs->miimcom, MIIMCOM_READ_CYCLE);
+
+	/* Wait till MII management write is complete */
+	while ((in_be32(&regs->miimind)) & (MIIMIND_BUSY | MIIMIND_NOT_VALID))
+		cpu_relax();
+
+	/* Read MII management status  */
+	value = in_be32(&regs->miimstat);
+
+	return value;
+}
+
+/* Reset the MIIM registers, and wait for the bus to free */
+int uec_mdio_reset(struct mii_bus *bus)
+{
+	struct ucc_mii_mng __iomem *regs = (void __iomem *)bus->priv;
+	unsigned int timeout = PHY_INIT_TIMEOUT;
+
+	spin_lock_bh(&bus->mdio_lock);
+
+	/* Reset the management interface */
+	out_be32(&regs->miimcfg, MIIMCFG_RESET_MANAGEMENT);
+
+	/* Setup the MII Mgmt clock speed */
+	out_be32(&regs->miimcfg, MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_112);
+
+	/* Wait until the bus is free */
+	while ((in_be32(&regs->miimind) & MIIMIND_BUSY) && timeout--)
+		cpu_relax();
+
+	spin_unlock_bh(&bus->mdio_lock);
+
+	if (timeout <= 0) {
+		printk(KERN_ERR "%s: The MII Bus is stuck!\n", bus->name);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static int uec_mdio_probe(struct of_device *ofdev, const struct of_device_id *match)
+{
+	struct device *device = &ofdev->dev;
+	struct device_node *np = ofdev->node, *tempnp = NULL;
+	struct device_node *child = NULL;
+	struct ucc_mii_mng __iomem *regs;
+	struct mii_bus *new_bus;
+	struct resource res;
+	int k, err = 0;
+
+	new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL);
+
+	if (NULL == new_bus)
+		return -ENOMEM;
+
+	new_bus->name = "UCC Ethernet Controller MII Bus";
+	new_bus->read = &uec_mdio_read;
+	new_bus->write = &uec_mdio_write;
+	new_bus->reset = &uec_mdio_reset;
+
+	memset(&res, 0, sizeof(res));
+
+	err = of_address_to_resource(np, 0, &res);
+	if (err)
+		goto reg_map_fail;
+
+	new_bus->id = res.start;
+
+	new_bus->irq = kmalloc(32 * sizeof(int), GFP_KERNEL);
+
+	if (NULL == new_bus->irq) {
+		err = -ENOMEM;
+		goto reg_map_fail;
+	}
+
+	for (k = 0; k < 32; k++)
+		new_bus->irq[k] = PHY_POLL;
+
+	while ((child = of_get_next_child(np, child)) != NULL) {
+		int irq = irq_of_parse_and_map(child, 0);
+		if (irq != NO_IRQ) {
+			const u32 *id = get_property(child, "reg", NULL);
+			new_bus->irq[*id] = irq;
+		}
+	}
+
+	/* Set the base address */
+	regs = ioremap(res.start, sizeof(struct ucc_mii_mng));
+
+	if (NULL == regs) {
+		err = -ENOMEM;
+		goto ioremap_fail;
+	}
+
+	new_bus->priv = (void __force *)regs;
+
+	new_bus->dev = device;
+	dev_set_drvdata(device, new_bus);
+
+	/* Read MII management master from device tree */
+	while ((tempnp = of_find_compatible_node(tempnp, "network", "ucc_geth"))
+	       != NULL) {
+		struct resource tempres;
+
+		err = of_address_to_resource(tempnp, 0, &tempres);
+		if (err)
+			goto bus_register_fail;
+
+		/* if our mdio regs fall within this UCC regs range */
+		if ((res.start >= tempres.start) &&
+		    (res.end <= tempres.end)) {
+			/* set this UCC to be the MII master */
+			const u32 *id = get_property(tempnp, "device-id", NULL);
+			if (id == NULL)
+				goto bus_register_fail;
+
+			ucc_set_qe_mux_mii_mng(*id - 1);
+
+			/* assign the TBI an address which won't
+			 * conflict with the PHYs */
+			out_be32(&regs->utbipar, UTBIPAR_INIT_TBIPA);
+			break;
+		}
+	}
+
+	err = mdiobus_register(new_bus);
+	if (0 != err) {
+		printk(KERN_ERR "%s: Cannot register as MDIO bus\n",
+		       new_bus->name);
+		goto bus_register_fail;
+	}
+
+	return 0;
+
+bus_register_fail:
+	iounmap(regs);
+ioremap_fail:
+	kfree(new_bus->irq);
+reg_map_fail:
+	kfree(new_bus);
+
+	return err;
+}
+
+int uec_mdio_remove(struct of_device *ofdev)
+{
+	struct device *device = &ofdev->dev;
+	struct mii_bus *bus = dev_get_drvdata(device);
+
+	mdiobus_unregister(bus);
+
+	dev_set_drvdata(device, NULL);
+
+	iounmap((void __iomem *)bus->priv);
+	bus->priv = NULL;
+	kfree(bus);
+
+	return 0;
+}
+
+static struct of_device_id uec_mdio_match[] = {
+	{
+		.type = "mdio",
+		.compatible = "ucc_geth_phy",
+	},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, uec_mdio_match);
+
+static struct of_platform_driver uec_mdio_driver = {
+	.name	= DRV_NAME,
+	.probe	= uec_mdio_probe,
+	.remove	= uec_mdio_remove,
+	.match_table	= uec_mdio_match,
+};
+
+int __init uec_mdio_init(void)
+{
+	return of_register_platform_driver(&uec_mdio_driver);
+}
+
+void __exit uec_mdio_exit(void)
+{
+	of_unregister_platform_driver(&uec_mdio_driver);
+}
diff --git a/drivers/net/ucc_geth_mii.h b/drivers/net/ucc_geth_mii.h
new file mode 100644
index 0000000..98430fe
--- /dev/null
+++ b/drivers/net/ucc_geth_mii.h
@@ -0,0 +1,100 @@
+/*
+ * drivers/net/ucc_geth_mii.h
+ *
+ * Gianfar Ethernet Driver -- MII Management Bus Implementation
+ * Driver for the MDIO bus controller in the Gianfar register space
+ *
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala
+ *
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ *
+ */
+#ifndef __UEC_MII_H
+#define __UEC_MII_H
+
+/* UCC GETH MIIMCFG (MII Management Configuration Register) */
+#define MIIMCFG_RESET_MANAGEMENT                0x80000000	/* Reset
+								   management */
+#define MIIMCFG_NO_PREAMBLE                     0x00000010	/* Preamble
+								   suppress */
+#define MIIMCFG_CLOCK_DIVIDE_SHIFT              (31 - 31)	/* clock divide
+								   << shift */
+#define MIIMCFG_CLOCK_DIVIDE_MAX                0xf	/* max clock divide */
+#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_2    0x00000000
+#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_4    0x00000001
+#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_6    0x00000002
+#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_8    0x00000003
+#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_10   0x00000004
+#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_14   0x00000005
+#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_16   0x00000008
+#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_20   0x00000006
+#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_28   0x00000007
+#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_32   0x00000009
+#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_48   0x0000000a
+#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_64   0x0000000b
+#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_80   0x0000000c
+#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_112  0x0000000d
+#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_160  0x0000000e
+#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_224  0x0000000f
+
+/* UCC GETH MIIMCOM (MII Management Command Register) */
+#define MIIMCOM_SCAN_CYCLE                      0x00000002	/* Scan cycle */
+#define MIIMCOM_READ_CYCLE                      0x00000001	/* Read cycle */
+
+/* UCC GETH MIIMADD (MII Management Address Register) */
+#define MIIMADD_PHY_ADDRESS_SHIFT               (31 - 23)	/* PHY Address
+								   << shift */
+#define MIIMADD_PHY_REGISTER_SHIFT              (31 - 31)	/* PHY Register
+								   << shift */
+
+/* UCC GETH MIIMCON (MII Management Control Register) */
+#define MIIMCON_PHY_CONTROL_SHIFT               (31 - 31)	/* PHY Control
+								   << shift */
+#define MIIMCON_PHY_STATUS_SHIFT                (31 - 31)	/* PHY Status
+								   << shift */
+
+/* UCC GETH MIIMIND (MII Management Indicator Register) */
+#define MIIMIND_NOT_VALID                       0x00000004	/* Not valid */
+#define MIIMIND_SCAN                            0x00000002	/* Scan in
+								   progress */
+#define MIIMIND_BUSY                            0x00000001
+
+/* Initial TBI Physical Address */
+#define UTBIPAR_INIT_TBIPA			0x1f
+
+struct ucc_mii_mng {
+	u32 miimcfg;		/* MII management configuration reg */
+	u32 miimcom;		/* MII management command reg */
+	u32 miimadd;		/* MII management address reg */
+	u32 miimcon;		/* MII management control reg */
+	u32 miimstat;		/* MII management status reg */
+	u32 miimind;		/* MII management indication reg */
+	u8 notcare[28];		/* Space holder */
+	u32 utbipar;		/* TBI phy address reg */
+} __attribute__ ((packed));
+
+/* TBI / MII Set Register */
+enum enet_tbi_mii_reg {
+	ENET_TBI_MII_CR = 0x00,	/* Control */
+	ENET_TBI_MII_SR = 0x01,	/* Status */
+	ENET_TBI_MII_ANA = 0x04,	/* AN advertisement */
+	ENET_TBI_MII_ANLPBPA = 0x05,	/* AN link partner base page ability */
+	ENET_TBI_MII_ANEX = 0x06,	/* AN expansion */
+	ENET_TBI_MII_ANNPT = 0x07,	/* AN next page transmit */
+	ENET_TBI_MII_ANLPANP = 0x08,	/* AN link partner ability next page */
+	ENET_TBI_MII_EXST = 0x0F,	/* Extended status */
+	ENET_TBI_MII_JD = 0x10,	/* Jitter diagnostics */
+	ENET_TBI_MII_TBICON = 0x11	/* TBI control */
+};
+
+int uec_mdio_read(struct mii_bus *bus, int mii_id, int regnum);
+int uec_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value);
+int __init uec_mdio_init(void);
+void __exit uec_mdio_exit(void);
+#endif				/* __UEC_MII_H */
diff --git a/drivers/net/ucc_geth_phy.c b/drivers/net/ucc_geth_phy.c
deleted file mode 100644
index 9373d895..0000000
--- a/drivers/net/ucc_geth_phy.c
+++ /dev/null
@@ -1,785 +0,0 @@
-/*
- * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved.
- *
- * Author: Shlomi Gridish <gridish@freescale.com>
- *
- * Description:
- * UCC GETH Driver -- PHY handling
- *
- * Changelog:
- * Jun 28, 2006 Li Yang <LeoLi@freescale.com>
- * - Rearrange code and style fixes
- *
- * 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.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/spinlock.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/crc32.h>
-#include <linux/mii.h>
-#include <linux/ethtool.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-
-#include "ucc_geth.h"
-#include "ucc_geth_phy.h"
-
-#define ugphy_printk(level, format, arg...)  \
-        printk(level format "\n", ## arg)
-
-#define ugphy_dbg(format, arg...)            \
-        ugphy_printk(KERN_DEBUG, format , ## arg)
-#define ugphy_err(format, arg...)            \
-        ugphy_printk(KERN_ERR, format , ## arg)
-#define ugphy_info(format, arg...)           \
-        ugphy_printk(KERN_INFO, format , ## arg)
-#define ugphy_warn(format, arg...)           \
-        ugphy_printk(KERN_WARNING, format , ## arg)
-
-#ifdef UGETH_VERBOSE_DEBUG
-#define ugphy_vdbg ugphy_dbg
-#else
-#define ugphy_vdbg(fmt, args...) do { } while (0)
-#endif				/* UGETH_VERBOSE_DEBUG */
-
-static void config_genmii_advert(struct ugeth_mii_info *mii_info);
-static void genmii_setup_forced(struct ugeth_mii_info *mii_info);
-static void genmii_restart_aneg(struct ugeth_mii_info *mii_info);
-static int gbit_config_aneg(struct ugeth_mii_info *mii_info);
-static int genmii_config_aneg(struct ugeth_mii_info *mii_info);
-static int genmii_update_link(struct ugeth_mii_info *mii_info);
-static int genmii_read_status(struct ugeth_mii_info *mii_info);
-
-static u16 ucc_geth_phy_read(struct ugeth_mii_info *mii_info, u16 regnum)
-{
-	u16 retval;
-	unsigned long flags;
-
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	spin_lock_irqsave(&mii_info->mdio_lock, flags);
-	retval = mii_info->mdio_read(mii_info->dev, mii_info->mii_id, regnum);
-	spin_unlock_irqrestore(&mii_info->mdio_lock, flags);
-
-	return retval;
-}
-
-static void ucc_geth_phy_write(struct ugeth_mii_info *mii_info, u16 regnum, u16 val)
-{
-	unsigned long flags;
-
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	spin_lock_irqsave(&mii_info->mdio_lock, flags);
-	mii_info->mdio_write(mii_info->dev, mii_info->mii_id, regnum, val);
-	spin_unlock_irqrestore(&mii_info->mdio_lock, flags);
-}
-
-/* Write value to the PHY for this device to the register at regnum, */
-/* waiting until the write is done before it returns.  All PHY */
-/* configuration has to be done through the TSEC1 MIIM regs */
-void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value)
-{
-	struct ucc_geth_private *ugeth = netdev_priv(dev);
-	struct ucc_mii_mng *mii_regs;
-	enum enet_tbi_mii_reg mii_reg = (enum enet_tbi_mii_reg) regnum;
-	u32 tmp_reg;
-
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	spin_lock_irq(&ugeth->lock);
-
-	mii_regs = ugeth->mii_info->mii_regs;
-
-	/* Set this UCC to be the master of the MII managment */
-	ucc_set_qe_mux_mii_mng(ugeth->ug_info->uf_info.ucc_num);
-
-	/* Stop the MII management read cycle */
-	out_be32(&mii_regs->miimcom, 0);
-	/* Setting up the MII Mangement Address Register */
-	tmp_reg = ((u32) mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | mii_reg;
-	out_be32(&mii_regs->miimadd, tmp_reg);
-
-	/* Setting up the MII Mangement Control Register with the value */
-	out_be32(&mii_regs->miimcon, (u32) value);
-
-	/* Wait till MII management write is complete */
-	while ((in_be32(&mii_regs->miimind)) & MIIMIND_BUSY)
-		cpu_relax();
-
-	spin_unlock_irq(&ugeth->lock);
-
-	udelay(10000);
-}
-
-/* Reads from register regnum in the PHY for device dev, */
-/* returning the value.  Clears miimcom first.  All PHY */
-/* configuration has to be done through the TSEC1 MIIM regs */
-int read_phy_reg(struct net_device *dev, int mii_id, int regnum)
-{
-	struct ucc_geth_private *ugeth = netdev_priv(dev);
-	struct ucc_mii_mng *mii_regs;
-	enum enet_tbi_mii_reg mii_reg = (enum enet_tbi_mii_reg) regnum;
-	u32 tmp_reg;
-	u16 value;
-
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	spin_lock_irq(&ugeth->lock);
-
-	mii_regs = ugeth->mii_info->mii_regs;
-
-	/* Setting up the MII Mangement Address Register */
-	tmp_reg = ((u32) mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | mii_reg;
-	out_be32(&mii_regs->miimadd, tmp_reg);
-
-	/* Perform an MII management read cycle */
-	out_be32(&mii_regs->miimcom, MIIMCOM_READ_CYCLE);
-
-	/* Wait till MII management write is complete */
-	while ((in_be32(&mii_regs->miimind)) & MIIMIND_BUSY)
-		cpu_relax();
-
-	udelay(10000);
-
-	/* Read MII management status  */
-	value = (u16) in_be32(&mii_regs->miimstat);
-	out_be32(&mii_regs->miimcom, 0);
-	if (value == 0xffff)
-		ugphy_warn("read wrong value : mii_id %d,mii_reg %d, base %08x",
-			   mii_id, mii_reg, (u32) & (mii_regs->miimcfg));
-
-	spin_unlock_irq(&ugeth->lock);
-
-	return (value);
-}
-
-void mii_clear_phy_interrupt(struct ugeth_mii_info *mii_info)
-{
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	if (mii_info->phyinfo->ack_interrupt)
-		mii_info->phyinfo->ack_interrupt(mii_info);
-}
-
-void mii_configure_phy_interrupt(struct ugeth_mii_info *mii_info,
-				 u32 interrupts)
-{
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	mii_info->interrupts = interrupts;
-	if (mii_info->phyinfo->config_intr)
-		mii_info->phyinfo->config_intr(mii_info);
-}
-
-/* Writes MII_ADVERTISE with the appropriate values, after
- * sanitizing advertise to make sure only supported features
- * are advertised
- */
-static void config_genmii_advert(struct ugeth_mii_info *mii_info)
-{
-	u32 advertise;
-	u16 adv;
-
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	/* Only allow advertising what this PHY supports */
-	mii_info->advertising &= mii_info->phyinfo->features;
-	advertise = mii_info->advertising;
-
-	/* Setup standard advertisement */
-	adv = ucc_geth_phy_read(mii_info, MII_ADVERTISE);
-	adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
-	if (advertise & ADVERTISED_10baseT_Half)
-		adv |= ADVERTISE_10HALF;
-	if (advertise & ADVERTISED_10baseT_Full)
-		adv |= ADVERTISE_10FULL;
-	if (advertise & ADVERTISED_100baseT_Half)
-		adv |= ADVERTISE_100HALF;
-	if (advertise & ADVERTISED_100baseT_Full)
-		adv |= ADVERTISE_100FULL;
-	ucc_geth_phy_write(mii_info, MII_ADVERTISE, adv);
-}
-
-static void genmii_setup_forced(struct ugeth_mii_info *mii_info)
-{
-	u16 ctrl;
-	u32 features = mii_info->phyinfo->features;
-
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	ctrl = ucc_geth_phy_read(mii_info, MII_BMCR);
-
-	ctrl &=
-	    ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE);
-	ctrl |= BMCR_RESET;
-
-	switch (mii_info->speed) {
-	case SPEED_1000:
-		if (features & (SUPPORTED_1000baseT_Half
-				| SUPPORTED_1000baseT_Full)) {
-			ctrl |= BMCR_SPEED1000;
-			break;
-		}
-		mii_info->speed = SPEED_100;
-	case SPEED_100:
-		if (features & (SUPPORTED_100baseT_Half
-				| SUPPORTED_100baseT_Full)) {
-			ctrl |= BMCR_SPEED100;
-			break;
-		}
-		mii_info->speed = SPEED_10;
-	case SPEED_10:
-		if (features & (SUPPORTED_10baseT_Half
-				| SUPPORTED_10baseT_Full))
-			break;
-	default:		/* Unsupported speed! */
-		ugphy_err("%s: Bad speed!", mii_info->dev->name);
-		break;
-	}
-
-	ucc_geth_phy_write(mii_info, MII_BMCR, ctrl);
-}
-
-/* Enable and Restart Autonegotiation */
-static void genmii_restart_aneg(struct ugeth_mii_info *mii_info)
-{
-	u16 ctl;
-
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	ctl = ucc_geth_phy_read(mii_info, MII_BMCR);
-	ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
-	ucc_geth_phy_write(mii_info, MII_BMCR, ctl);
-}
-
-static int gbit_config_aneg(struct ugeth_mii_info *mii_info)
-{
-	u16 adv;
-	u32 advertise;
-
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	if (mii_info->autoneg) {
-		/* Configure the ADVERTISE register */
-		config_genmii_advert(mii_info);
-		advertise = mii_info->advertising;
-
-		adv = ucc_geth_phy_read(mii_info, MII_1000BASETCONTROL);
-		adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP |
-			 MII_1000BASETCONTROL_HALFDUPLEXCAP);
-		if (advertise & SUPPORTED_1000baseT_Half)
-			adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;
-		if (advertise & SUPPORTED_1000baseT_Full)
-			adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;
-		ucc_geth_phy_write(mii_info, MII_1000BASETCONTROL, adv);
-
-		/* Start/Restart aneg */
-		genmii_restart_aneg(mii_info);
-	} else
-		genmii_setup_forced(mii_info);
-
-	return 0;
-}
-
-static int genmii_config_aneg(struct ugeth_mii_info *mii_info)
-{
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	if (mii_info->autoneg) {
-		config_genmii_advert(mii_info);
-		genmii_restart_aneg(mii_info);
-	} else
-		genmii_setup_forced(mii_info);
-
-	return 0;
-}
-
-static int genmii_update_link(struct ugeth_mii_info *mii_info)
-{
-	u16 status;
-
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	/* Do a fake read */
-	ucc_geth_phy_read(mii_info, MII_BMSR);
-
-	/* Read link and autonegotiation status */
-	status = ucc_geth_phy_read(mii_info, MII_BMSR);
-	if ((status & BMSR_LSTATUS) == 0)
-		mii_info->link = 0;
-	else
-		mii_info->link = 1;
-
-	/* If we are autonegotiating, and not done,
-	 * return an error */
-	if (mii_info->autoneg && !(status & BMSR_ANEGCOMPLETE))
-		return -EAGAIN;
-
-	return 0;
-}
-
-static int genmii_read_status(struct ugeth_mii_info *mii_info)
-{
-	u16 status;
-	int err;
-
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	/* Update the link, but return if there
-	 * was an error */
-	err = genmii_update_link(mii_info);
-	if (err)
-		return err;
-
-	if (mii_info->autoneg) {
-		status = ucc_geth_phy_read(mii_info, MII_LPA);
-
-		if (status & (LPA_10FULL | LPA_100FULL))
-			mii_info->duplex = DUPLEX_FULL;
-		else
-			mii_info->duplex = DUPLEX_HALF;
-		if (status & (LPA_100FULL | LPA_100HALF))
-			mii_info->speed = SPEED_100;
-		else
-			mii_info->speed = SPEED_10;
-		mii_info->pause = 0;
-	}
-	/* On non-aneg, we assume what we put in BMCR is the speed,
-	 * though magic-aneg shouldn't prevent this case from occurring
-	 */
-
-	return 0;
-}
-
-static int marvell_init(struct ugeth_mii_info *mii_info)
-{
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	ucc_geth_phy_write(mii_info, 0x14, 0x0cd2);
-	ucc_geth_phy_write(mii_info, 0x1b,
-		(ucc_geth_phy_read(mii_info, 0x1b) & ~0x000f) | 0x000b);
-	ucc_geth_phy_write(mii_info, MII_BMCR,
-		  ucc_geth_phy_read(mii_info, MII_BMCR) | BMCR_RESET);
-	msleep(4000);
-
-	return 0;
-}
-
-static int marvell_config_aneg(struct ugeth_mii_info *mii_info)
-{
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	/* The Marvell PHY has an errata which requires
-	 * that certain registers get written in order
-	 * to restart autonegotiation */
-	ucc_geth_phy_write(mii_info, MII_BMCR, BMCR_RESET);
-
-	ucc_geth_phy_write(mii_info, 0x1d, 0x1f);
-	ucc_geth_phy_write(mii_info, 0x1e, 0x200c);
-	ucc_geth_phy_write(mii_info, 0x1d, 0x5);
-	ucc_geth_phy_write(mii_info, 0x1e, 0);
-	ucc_geth_phy_write(mii_info, 0x1e, 0x100);
-
-	gbit_config_aneg(mii_info);
-
-	return 0;
-}
-
-static int marvell_read_status(struct ugeth_mii_info *mii_info)
-{
-	u16 status;
-	int err;
-
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	/* Update the link, but return if there
-	 * was an error */
-	err = genmii_update_link(mii_info);
-	if (err)
-		return err;
-
-	/* If the link is up, read the speed and duplex */
-	/* If we aren't autonegotiating, assume speeds
-	 * are as set */
-	if (mii_info->autoneg && mii_info->link) {
-		int speed;
-		status = ucc_geth_phy_read(mii_info, MII_M1011_PHY_SPEC_STATUS);
-
-		/* Get the duplexity */
-		if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX)
-			mii_info->duplex = DUPLEX_FULL;
-		else
-			mii_info->duplex = DUPLEX_HALF;
-
-		/* Get the speed */
-		speed = status & MII_M1011_PHY_SPEC_STATUS_SPD_MASK;
-		switch (speed) {
-		case MII_M1011_PHY_SPEC_STATUS_1000:
-			mii_info->speed = SPEED_1000;
-			break;
-		case MII_M1011_PHY_SPEC_STATUS_100:
-			mii_info->speed = SPEED_100;
-			break;
-		default:
-			mii_info->speed = SPEED_10;
-			break;
-		}
-		mii_info->pause = 0;
-	}
-
-	return 0;
-}
-
-static int marvell_ack_interrupt(struct ugeth_mii_info *mii_info)
-{
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	/* Clear the interrupts by reading the reg */
-	ucc_geth_phy_read(mii_info, MII_M1011_IEVENT);
-
-	return 0;
-}
-
-static int marvell_config_intr(struct ugeth_mii_info *mii_info)
-{
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	if (mii_info->interrupts == MII_INTERRUPT_ENABLED)
-		ucc_geth_phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
-	else
-		ucc_geth_phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);
-
-	return 0;
-}
-
-static int cis820x_init(struct ugeth_mii_info *mii_info)
-{
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	ucc_geth_phy_write(mii_info, MII_CIS8201_AUX_CONSTAT,
-		  MII_CIS8201_AUXCONSTAT_INIT);
-	ucc_geth_phy_write(mii_info, MII_CIS8201_EXT_CON1, MII_CIS8201_EXTCON1_INIT);
-
-	return 0;
-}
-
-static int cis820x_read_status(struct ugeth_mii_info *mii_info)
-{
-	u16 status;
-	int err;
-
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	/* Update the link, but return if there
-	 * was an error */
-	err = genmii_update_link(mii_info);
-	if (err)
-		return err;
-
-	/* If the link is up, read the speed and duplex */
-	/* If we aren't autonegotiating, assume speeds
-	 * are as set */
-	if (mii_info->autoneg && mii_info->link) {
-		int speed;
-
-		status = ucc_geth_phy_read(mii_info, MII_CIS8201_AUX_CONSTAT);
-		if (status & MII_CIS8201_AUXCONSTAT_DUPLEX)
-			mii_info->duplex = DUPLEX_FULL;
-		else
-			mii_info->duplex = DUPLEX_HALF;
-
-		speed = status & MII_CIS8201_AUXCONSTAT_SPEED;
-
-		switch (speed) {
-		case MII_CIS8201_AUXCONSTAT_GBIT:
-			mii_info->speed = SPEED_1000;
-			break;
-		case MII_CIS8201_AUXCONSTAT_100:
-			mii_info->speed = SPEED_100;
-			break;
-		default:
-			mii_info->speed = SPEED_10;
-			break;
-		}
-	}
-
-	return 0;
-}
-
-static int cis820x_ack_interrupt(struct ugeth_mii_info *mii_info)
-{
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	ucc_geth_phy_read(mii_info, MII_CIS8201_ISTAT);
-
-	return 0;
-}
-
-static int cis820x_config_intr(struct ugeth_mii_info *mii_info)
-{
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	if (mii_info->interrupts == MII_INTERRUPT_ENABLED)
-		ucc_geth_phy_write(mii_info, MII_CIS8201_IMASK, MII_CIS8201_IMASK_MASK);
-	else
-		ucc_geth_phy_write(mii_info, MII_CIS8201_IMASK, 0);
-
-	return 0;
-}
-
-#define DM9161_DELAY 10
-
-static int dm9161_read_status(struct ugeth_mii_info *mii_info)
-{
-	u16 status;
-	int err;
-
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	/* Update the link, but return if there
-	 * was an error */
-	err = genmii_update_link(mii_info);
-	if (err)
-		return err;
-
-	/* If the link is up, read the speed and duplex */
-	/* If we aren't autonegotiating, assume speeds
-	 * are as set */
-	if (mii_info->autoneg && mii_info->link) {
-		status = ucc_geth_phy_read(mii_info, MII_DM9161_SCSR);
-		if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_100H))
-			mii_info->speed = SPEED_100;
-		else
-			mii_info->speed = SPEED_10;
-
-		if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_10F))
-			mii_info->duplex = DUPLEX_FULL;
-		else
-			mii_info->duplex = DUPLEX_HALF;
-	}
-
-	return 0;
-}
-
-static int dm9161_config_aneg(struct ugeth_mii_info *mii_info)
-{
-	struct dm9161_private *priv = mii_info->priv;
-
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	if (0 == priv->resetdone)
-		return -EAGAIN;
-
-	return 0;
-}
-
-static void dm9161_timer(unsigned long data)
-{
-	struct ugeth_mii_info *mii_info = (struct ugeth_mii_info *)data;
-	struct dm9161_private *priv = mii_info->priv;
-	u16 status = ucc_geth_phy_read(mii_info, MII_BMSR);
-
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	if (status & BMSR_ANEGCOMPLETE) {
-		priv->resetdone = 1;
-	} else
-		mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
-}
-
-static int dm9161_init(struct ugeth_mii_info *mii_info)
-{
-	struct dm9161_private *priv;
-
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	/* Allocate the private data structure */
-	priv = kmalloc(sizeof(struct dm9161_private), GFP_KERNEL);
-
-	if (NULL == priv)
-		return -ENOMEM;
-
-	mii_info->priv = priv;
-
-	/* Reset is not done yet */
-	priv->resetdone = 0;
-
-	ucc_geth_phy_write(mii_info, MII_BMCR,
-		  ucc_geth_phy_read(mii_info, MII_BMCR) | BMCR_RESET);
-
-	ucc_geth_phy_write(mii_info, MII_BMCR,
-		  ucc_geth_phy_read(mii_info, MII_BMCR) & ~BMCR_ISOLATE);
-
-	config_genmii_advert(mii_info);
-	/* Start/Restart aneg */
-	genmii_config_aneg(mii_info);
-
-	/* Start a timer for DM9161_DELAY seconds to wait
-	 * for the PHY to be ready */
-	init_timer(&priv->timer);
-	priv->timer.function = &dm9161_timer;
-	priv->timer.data = (unsigned long)mii_info;
-	mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
-
-	return 0;
-}
-
-static void dm9161_close(struct ugeth_mii_info *mii_info)
-{
-	struct dm9161_private *priv = mii_info->priv;
-
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	del_timer_sync(&priv->timer);
-	kfree(priv);
-}
-
-static int dm9161_ack_interrupt(struct ugeth_mii_info *mii_info)
-{
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	/* Clear the interrupts by reading the reg */
-	ucc_geth_phy_read(mii_info, MII_DM9161_INTR);
-
-
-	return 0;
-}
-
-static int dm9161_config_intr(struct ugeth_mii_info *mii_info)
-{
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	if (mii_info->interrupts == MII_INTERRUPT_ENABLED)
-		ucc_geth_phy_write(mii_info, MII_DM9161_INTR, MII_DM9161_INTR_INIT);
-	else
-		ucc_geth_phy_write(mii_info, MII_DM9161_INTR, MII_DM9161_INTR_STOP);
-
-	return 0;
-}
-
-/* Cicada 820x */
-static struct phy_info phy_info_cis820x = {
-	.phy_id = 0x000fc440,
-	.name = "Cicada Cis8204",
-	.phy_id_mask = 0x000fffc0,
-	.features = MII_GBIT_FEATURES,
-	.init = &cis820x_init,
-	.config_aneg = &gbit_config_aneg,
-	.read_status = &cis820x_read_status,
-	.ack_interrupt = &cis820x_ack_interrupt,
-	.config_intr = &cis820x_config_intr,
-};
-
-static struct phy_info phy_info_dm9161 = {
-	.phy_id = 0x0181b880,
-	.phy_id_mask = 0x0ffffff0,
-	.name = "Davicom DM9161E",
-	.init = dm9161_init,
-	.config_aneg = dm9161_config_aneg,
-	.read_status = dm9161_read_status,
-	.close = dm9161_close,
-};
-
-static struct phy_info phy_info_dm9161a = {
-	.phy_id = 0x0181b8a0,
-	.phy_id_mask = 0x0ffffff0,
-	.name = "Davicom DM9161A",
-	.features = MII_BASIC_FEATURES,
-	.init = dm9161_init,
-	.config_aneg = dm9161_config_aneg,
-	.read_status = dm9161_read_status,
-	.ack_interrupt = dm9161_ack_interrupt,
-	.config_intr = dm9161_config_intr,
-	.close = dm9161_close,
-};
-
-static struct phy_info phy_info_marvell = {
-	.phy_id = 0x01410c00,
-	.phy_id_mask = 0xffffff00,
-	.name = "Marvell 88E11x1",
-	.features = MII_GBIT_FEATURES,
-	.init = &marvell_init,
-	.config_aneg = &marvell_config_aneg,
-	.read_status = &marvell_read_status,
-	.ack_interrupt = &marvell_ack_interrupt,
-	.config_intr = &marvell_config_intr,
-};
-
-static struct phy_info phy_info_genmii = {
-	.phy_id = 0x00000000,
-	.phy_id_mask = 0x00000000,
-	.name = "Generic MII",
-	.features = MII_BASIC_FEATURES,
-	.config_aneg = genmii_config_aneg,
-	.read_status = genmii_read_status,
-};
-
-static struct phy_info *phy_info[] = {
-	&phy_info_cis820x,
-	&phy_info_marvell,
-	&phy_info_dm9161,
-	&phy_info_dm9161a,
-	&phy_info_genmii,
-	NULL
-};
-
-/* Use the PHY ID registers to determine what type of PHY is attached
- * to device dev.  return a struct phy_info structure describing that PHY
- */
-struct phy_info *get_phy_info(struct ugeth_mii_info *mii_info)
-{
-	u16 phy_reg;
-	u32 phy_ID;
-	int i;
-	struct phy_info *theInfo = NULL;
-	struct net_device *dev = mii_info->dev;
-
-	ugphy_vdbg("%s: IN", __FUNCTION__);
-
-	/* Grab the bits from PHYIR1, and put them in the upper half */
-	phy_reg = ucc_geth_phy_read(mii_info, MII_PHYSID1);
-	phy_ID = (phy_reg & 0xffff) << 16;
-
-	/* Grab the bits from PHYIR2, and put them in the lower half */
-	phy_reg = ucc_geth_phy_read(mii_info, MII_PHYSID2);
-	phy_ID |= (phy_reg & 0xffff);
-
-	/* loop through all the known PHY types, and find one that */
-	/* matches the ID we read from the PHY. */
-	for (i = 0; phy_info[i]; i++)
-		if (phy_info[i]->phy_id == (phy_ID & phy_info[i]->phy_id_mask)){
-			theInfo = phy_info[i];
-			break;
-		}
-
-	/* This shouldn't happen, as we have generic PHY support */
-	if (theInfo == NULL) {
-		ugphy_info("%s: PHY id %x is not supported!", dev->name,
-			   phy_ID);
-		return NULL;
-	} else {
-		ugphy_info("%s: PHY is %s (%x)", dev->name, theInfo->name,
-			   phy_ID);
-	}
-
-	return theInfo;
-}
diff --git a/drivers/net/ucc_geth_phy.h b/drivers/net/ucc_geth_phy.h
deleted file mode 100644
index f574078..0000000
--- a/drivers/net/ucc_geth_phy.h
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved.
- *
- * Author: Shlomi Gridish <gridish@freescale.com>
- *
- * Description:
- * UCC GETH Driver -- PHY handling
- *
- * Changelog:
- * Jun 28, 2006 Li Yang <LeoLi@freescale.com>
- * - Rearrange code and style fixes
- *
- * 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.
- *
- */
-#ifndef __UCC_GETH_PHY_H__
-#define __UCC_GETH_PHY_H__
-
-#define MII_end ((u32)-2)
-#define MII_read ((u32)-1)
-
-#define MIIMIND_BUSY            0x00000001
-#define MIIMIND_NOTVALID        0x00000004
-
-#define UGETH_AN_TIMEOUT        2000
-
-/* 1000BT control (Marvell & BCM54xx at least) */
-#define MII_1000BASETCONTROL                  0x09
-#define MII_1000BASETCONTROL_FULLDUPLEXCAP    0x0200
-#define MII_1000BASETCONTROL_HALFDUPLEXCAP    0x0100
-
-/* Cicada Extended Control Register 1 */
-#define MII_CIS8201_EXT_CON1        0x17
-#define MII_CIS8201_EXTCON1_INIT    0x0000
-
-/* Cicada Interrupt Mask Register */
-#define MII_CIS8201_IMASK           0x19
-#define MII_CIS8201_IMASK_IEN       0x8000
-#define MII_CIS8201_IMASK_SPEED     0x4000
-#define MII_CIS8201_IMASK_LINK      0x2000
-#define MII_CIS8201_IMASK_DUPLEX    0x1000
-#define MII_CIS8201_IMASK_MASK      0xf000
-
-/* Cicada Interrupt Status Register */
-#define MII_CIS8201_ISTAT           0x1a
-#define MII_CIS8201_ISTAT_STATUS    0x8000
-#define MII_CIS8201_ISTAT_SPEED     0x4000
-#define MII_CIS8201_ISTAT_LINK      0x2000
-#define MII_CIS8201_ISTAT_DUPLEX    0x1000
-
-/* Cicada Auxiliary Control/Status Register */
-#define MII_CIS8201_AUX_CONSTAT        0x1c
-#define MII_CIS8201_AUXCONSTAT_INIT    0x0004
-#define MII_CIS8201_AUXCONSTAT_DUPLEX  0x0020
-#define MII_CIS8201_AUXCONSTAT_SPEED   0x0018
-#define MII_CIS8201_AUXCONSTAT_GBIT    0x0010
-#define MII_CIS8201_AUXCONSTAT_100     0x0008
-
-/* 88E1011 PHY Status Register */
-#define MII_M1011_PHY_SPEC_STATUS               0x11
-#define MII_M1011_PHY_SPEC_STATUS_1000          0x8000
-#define MII_M1011_PHY_SPEC_STATUS_100           0x4000
-#define MII_M1011_PHY_SPEC_STATUS_SPD_MASK      0xc000
-#define MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX    0x2000
-#define MII_M1011_PHY_SPEC_STATUS_RESOLVED      0x0800
-#define MII_M1011_PHY_SPEC_STATUS_LINK          0x0400
-
-#define MII_M1011_IEVENT                0x13
-#define MII_M1011_IEVENT_CLEAR          0x0000
-
-#define MII_M1011_IMASK                 0x12
-#define MII_M1011_IMASK_INIT            0x6400
-#define MII_M1011_IMASK_CLEAR           0x0000
-
-#define MII_DM9161_SCR                  0x10
-#define MII_DM9161_SCR_INIT             0x0610
-
-/* DM9161 Specified Configuration and Status Register */
-#define MII_DM9161_SCSR                 0x11
-#define MII_DM9161_SCSR_100F            0x8000
-#define MII_DM9161_SCSR_100H            0x4000
-#define MII_DM9161_SCSR_10F             0x2000
-#define MII_DM9161_SCSR_10H             0x1000
-
-/* DM9161 Interrupt Register */
-#define MII_DM9161_INTR                 0x15
-#define MII_DM9161_INTR_PEND            0x8000
-#define MII_DM9161_INTR_DPLX_MASK       0x0800
-#define MII_DM9161_INTR_SPD_MASK        0x0400
-#define MII_DM9161_INTR_LINK_MASK       0x0200
-#define MII_DM9161_INTR_MASK            0x0100
-#define MII_DM9161_INTR_DPLX_CHANGE     0x0010
-#define MII_DM9161_INTR_SPD_CHANGE      0x0008
-#define MII_DM9161_INTR_LINK_CHANGE     0x0004
-#define MII_DM9161_INTR_INIT            0x0000
-#define MII_DM9161_INTR_STOP    \
-(MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK \
- | MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK)
-
-/* DM9161 10BT Configuration/Status */
-#define MII_DM9161_10BTCSR              0x12
-#define MII_DM9161_10BTCSR_INIT         0x7800
-
-#define MII_BASIC_FEATURES    (SUPPORTED_10baseT_Half | \
-                 SUPPORTED_10baseT_Full | \
-                 SUPPORTED_100baseT_Half | \
-                 SUPPORTED_100baseT_Full | \
-                 SUPPORTED_Autoneg | \
-                 SUPPORTED_TP | \
-                 SUPPORTED_MII)
-
-#define MII_GBIT_FEATURES    (MII_BASIC_FEATURES | \
-                 SUPPORTED_1000baseT_Half | \
-                 SUPPORTED_1000baseT_Full)
-
-#define MII_READ_COMMAND                0x00000001
-
-#define MII_INTERRUPT_DISABLED          0x0
-#define MII_INTERRUPT_ENABLED           0x1
-/* Taken from mii_if_info and sungem_phy.h */
-struct ugeth_mii_info {
-	/* Information about the PHY type */
-	/* And management functions */
-	struct phy_info *phyinfo;
-
-	struct ucc_mii_mng *mii_regs;
-
-	/* forced speed & duplex (no autoneg)
-	 * partner speed & duplex & pause (autoneg)
-	 */
-	int speed;
-	int duplex;
-	int pause;
-
-	/* The most recently read link state */
-	int link;
-
-	/* Enabled Interrupts */
-	u32 interrupts;
-
-	u32 advertising;
-	int autoneg;
-	int mii_id;
-
-	/* private data pointer */
-	/* For use by PHYs to maintain extra state */
-	void *priv;
-
-	/* Provided by host chip */
-	struct net_device *dev;
-
-	/* A lock to ensure that only one thing can read/write
-	 * the MDIO bus at a time */
-	spinlock_t mdio_lock;
-
-	/* Provided by ethernet driver */
-	int (*mdio_read) (struct net_device * dev, int mii_id, int reg);
-	void (*mdio_write) (struct net_device * dev, int mii_id, int reg,
-			    int val);
-};
-
-/* struct phy_info: a structure which defines attributes for a PHY
- *
- * id will contain a number which represents the PHY.  During
- * startup, the driver will poll the PHY to find out what its
- * UID--as defined by registers 2 and 3--is.  The 32-bit result
- * gotten from the PHY will be ANDed with phy_id_mask to
- * discard any bits which may change based on revision numbers
- * unimportant to functionality
- *
- * There are 6 commands which take a ugeth_mii_info structure.
- * Each PHY must declare config_aneg, and read_status.
- */
-struct phy_info {
-	u32 phy_id;
-	char *name;
-	unsigned int phy_id_mask;
-	u32 features;
-
-	/* Called to initialize the PHY */
-	int (*init) (struct ugeth_mii_info * mii_info);
-
-	/* Called to suspend the PHY for power */
-	int (*suspend) (struct ugeth_mii_info * mii_info);
-
-	/* Reconfigures autonegotiation (or disables it) */
-	int (*config_aneg) (struct ugeth_mii_info * mii_info);
-
-	/* Determines the negotiated speed and duplex */
-	int (*read_status) (struct ugeth_mii_info * mii_info);
-
-	/* Clears any pending interrupts */
-	int (*ack_interrupt) (struct ugeth_mii_info * mii_info);
-
-	/* Enables or disables interrupts */
-	int (*config_intr) (struct ugeth_mii_info * mii_info);
-
-	/* Clears up any memory if needed */
-	void (*close) (struct ugeth_mii_info * mii_info);
-};
-
-struct phy_info *get_phy_info(struct ugeth_mii_info *mii_info);
-void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value);
-int read_phy_reg(struct net_device *dev, int mii_id, int regnum);
-void mii_clear_phy_interrupt(struct ugeth_mii_info *mii_info);
-void mii_configure_phy_interrupt(struct ugeth_mii_info *mii_info,
-				 u32 interrupts);
-
-struct dm9161_private {
-	struct timer_list timer;
-	int resetdone;
-};
-
-#endif				/* __UCC_GETH_PHY_H__ */
diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c
index f3a972e..adea290a9 100644
--- a/drivers/net/via-rhine.c
+++ b/drivers/net/via-rhine.c
@@ -1486,7 +1486,6 @@
 			   copying to a minimally-sized skbuff. */
 			if (pkt_len < rx_copybreak &&
 				(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_for_cpu(rp->pdev,
 							    rp->rx_skbuff_dma[entry],
diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c
index 8e5d820..25b75b6 100644
--- a/drivers/net/via-velocity.c
+++ b/drivers/net/via-velocity.c
@@ -1339,7 +1339,8 @@
 			if (vptr->flags & VELOCITY_FLAGS_IP_ALIGN)
 				skb_reserve(new_skb, 2);
 
-			memcpy(new_skb->data, rx_skb[0]->data, pkt_size);
+			skb_copy_from_linear_data(rx_skb[0], new_skb->data,
+						  pkt_size);
 			*rx_skb = new_skb;
 			ret = 0;
 		}
@@ -1398,7 +1399,6 @@
 		vptr->stats.multicast++;
 
 	skb = rd_info->skb;
-	skb->dev = vptr->dev;
 
 	pci_dma_sync_single_for_cpu(vptr->pdev, rd_info->skb_dma,
 				    vptr->rx_buf_sz, PCI_DMA_FROMDEVICE);
@@ -1428,7 +1428,7 @@
 		   PCI_DMA_FROMDEVICE);
 
 	skb_put(skb, pkt_len - 4);
-	skb->protocol = eth_type_trans(skb, skb->dev);
+	skb->protocol = eth_type_trans(skb, vptr->dev);
 
 	stats->rx_bytes += pkt_len;
 	netif_rx(skb);
@@ -1928,7 +1928,7 @@
 	if (pktlen < ETH_ZLEN) {
 		/* Cannot occur until ZC support */
 		pktlen = ETH_ZLEN;
-		memcpy(tdinfo->buf, skb->data, skb->len);
+		skb_copy_from_linear_data(skb, tdinfo->buf, skb->len);
 		memset(tdinfo->buf + skb->len, 0, ETH_ZLEN - skb->len);
 		tdinfo->skb = skb;
 		tdinfo->skb_dma[0] = tdinfo->buf_dma;
@@ -1944,7 +1944,7 @@
 		int nfrags = skb_shinfo(skb)->nr_frags;
 		tdinfo->skb = skb;
 		if (nfrags > 6) {
-			memcpy(tdinfo->buf, skb->data, skb->len);
+			skb_copy_from_linear_data(skb, tdinfo->buf, skb->len);
 			tdinfo->skb_dma[0] = tdinfo->buf_dma;
 			td_ptr->tdesc0.pktsize =
 			td_ptr->td_buf[0].pa_low = cpu_to_le32(tdinfo->skb_dma[0]);
@@ -2007,7 +2007,7 @@
 	 */
 	if ((vptr->flags & VELOCITY_FLAGS_TX_CSUM)
 				 && (skb->ip_summed == CHECKSUM_PARTIAL)) {
-		struct iphdr *ip = skb->nh.iph;
+		const struct iphdr *ip = ip_hdr(skb);
 		if (ip->protocol == IPPROTO_TCP)
 			td_ptr->tdesc1.TCR |= TCR0_TCPCK;
 		else if (ip->protocol == IPPROTO_UDP)
diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c
index 5b82e4f..2346473 100644
--- a/drivers/net/wan/cosa.c
+++ b/drivers/net/wan/cosa.c
@@ -773,7 +773,7 @@
 	}
 	chan->rx_skb->protocol = htons(ETH_P_WAN_PPP);
 	chan->rx_skb->dev = chan->pppdev.dev;
-	chan->rx_skb->mac.raw = chan->rx_skb->data;
+	skb_reset_mac_header(chan->rx_skb);
 	chan->stats.rx_packets++;
 	chan->stats.rx_bytes += chan->cosa->rxsize;
 	netif_rx(chan->rx_skb);
diff --git a/drivers/net/wan/cycx_x25.c b/drivers/net/wan/cycx_x25.c
index a631d1c..016b3ff 100644
--- a/drivers/net/wan/cycx_x25.c
+++ b/drivers/net/wan/cycx_x25.c
@@ -834,7 +834,7 @@
 	++chan->ifstats.rx_packets;
 	chan->ifstats.rx_bytes += pktlen;
 
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	netif_rx(skb);
 	dev->last_rx = jiffies;		/* timestamp */
 }
diff --git a/drivers/net/wan/dlci.c b/drivers/net/wan/dlci.c
index 7369875..66be20c 100644
--- a/drivers/net/wan/dlci.c
+++ b/drivers/net/wan/dlci.c
@@ -176,7 +176,7 @@
 	if (process)
 	{
 		/* we've set up the protocol, so discard the header */
-		skb->mac.raw = skb->data; 
+		skb_reset_mac_header(skb);
 		skb_pull(skb, header);
 		dlp->stats.rx_bytes += skb->len;
 		netif_rx(skb);
diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c
index 25021a7..dca0244 100644
--- a/drivers/net/wan/dscc4.c
+++ b/drivers/net/wan/dscc4.c
@@ -1904,7 +1904,8 @@
 		struct TxFD *tx_fd = dpriv->tx_fd + last;
 
 		skb->len = DUMMY_SKB_SIZE;
-		memcpy(skb->data, version, strlen(version)%DUMMY_SKB_SIZE);
+		skb_copy_to_linear_data(skb, version,
+					strlen(version) % DUMMY_SKB_SIZE);
 		tx_fd->state = FrameEnd | TO_STATE_TX(DUMMY_SKB_SIZE);
 		tx_fd->data = pci_map_single(dpriv->pci_priv->pdev, skb->data,
 					     DUMMY_SKB_SIZE, PCI_DMA_TODEVICE);
diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c
index c45d6a8..58a53b6 100644
--- a/drivers/net/wan/farsync.c
+++ b/drivers/net/wan/farsync.c
@@ -864,7 +864,7 @@
 static __be16 farsync_type_trans(struct sk_buff *skb, struct net_device *dev)
 {
 	skb->dev = dev;
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	skb->pkt_type = PACKET_HOST;
 	return htons(ETH_P_CUST);
 }
diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c
index c9664fd..9ec6cf2 100644
--- a/drivers/net/wan/hdlc_cisco.c
+++ b/drivers/net/wan/hdlc_cisco.c
@@ -37,16 +37,16 @@
 struct hdlc_header {
 	u8 address;
 	u8 control;
-	u16 protocol;
+	__be16 protocol;
 }__attribute__ ((packed));
 
 
 struct cisco_packet {
-	u32 type;		/* code */
-	u32 par1;
-	u32 par2;
-	u16 rel;		/* reliability */
-	u32 time;
+	__be32 type;		/* code */
+	__be32 par1;
+	__be32 par2;
+	__be16 rel;		/* reliability */
+	__be32 time;
 }__attribute__ ((packed));
 #define	CISCO_PACKET_LEN	18
 #define	CISCO_BIG_PACKET_LEN	20
@@ -97,7 +97,7 @@
 
 
 static void cisco_keepalive_send(struct net_device *dev, u32 type,
-				 u32 par1, u32 par2)
+				 __be32 par1, __be32 par2)
 {
 	struct sk_buff *skb;
 	struct cisco_packet *data;
@@ -115,16 +115,16 @@
 	data = (struct cisco_packet*)(skb->data + 4);
 
 	data->type = htonl(type);
-	data->par1 = htonl(par1);
-	data->par2 = htonl(par2);
-	data->rel = 0xFFFF;
+	data->par1 = par1;
+	data->par2 = par2;
+	data->rel = __constant_htons(0xFFFF);
 	/* we will need do_div here if 1000 % HZ != 0 */
 	data->time = htonl((jiffies - INITIAL_JIFFIES) * (1000 / HZ));
 
 	skb_put(skb, sizeof(struct cisco_packet));
 	skb->priority = TC_PRIO_CONTROL;
 	skb->dev = dev;
-	skb->nh.raw = skb->data;
+	skb_reset_network_header(skb);
 
 	dev_queue_xmit(skb);
 }
@@ -193,7 +193,7 @@
 		case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */
 			in_dev = dev->ip_ptr;
 			addr = 0;
-			mask = ~0; /* is the mask correct? */
+			mask = __constant_htonl(~0); /* is the mask correct? */
 
 			if (in_dev != NULL) {
 				struct in_ifaddr **ifap = &in_dev->ifa_list;
@@ -245,7 +245,7 @@
 	} /* switch(protocol) */
 
 	printk(KERN_INFO "%s: Unsupported protocol %x\n", dev->name,
-	       data->protocol);
+	       ntohs(data->protocol));
 	dev_kfree_skb_any(skb);
 	return NET_RX_DROP;
 
@@ -270,8 +270,9 @@
 		netif_dormant_on(dev);
 	}
 
-	cisco_keepalive_send(dev, CISCO_KEEPALIVE_REQ, ++state(hdlc)->txseq,
-			     state(hdlc)->rxseq);
+	cisco_keepalive_send(dev, CISCO_KEEPALIVE_REQ,
+			     htonl(++state(hdlc)->txseq),
+			     htonl(state(hdlc)->rxseq));
 	state(hdlc)->request_sent = 1;
 	state(hdlc)->timer.expires = jiffies +
 		state(hdlc)->settings.interval * HZ;
diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c
index c6c3c75..15b6e07 100644
--- a/drivers/net/wan/hdlc_fr.c
+++ b/drivers/net/wan/hdlc_fr.c
@@ -288,31 +288,31 @@
 	struct sk_buff *skb = *skb_p;
 
 	switch (skb->protocol) {
-	case __constant_ntohs(NLPID_CCITT_ANSI_LMI):
+	case __constant_htons(NLPID_CCITT_ANSI_LMI):
 		head_len = 4;
 		skb_push(skb, head_len);
 		skb->data[3] = NLPID_CCITT_ANSI_LMI;
 		break;
 
-	case __constant_ntohs(NLPID_CISCO_LMI):
+	case __constant_htons(NLPID_CISCO_LMI):
 		head_len = 4;
 		skb_push(skb, head_len);
 		skb->data[3] = NLPID_CISCO_LMI;
 		break;
 
-	case __constant_ntohs(ETH_P_IP):
+	case __constant_htons(ETH_P_IP):
 		head_len = 4;
 		skb_push(skb, head_len);
 		skb->data[3] = NLPID_IP;
 		break;
 
-	case __constant_ntohs(ETH_P_IPV6):
+	case __constant_htons(ETH_P_IPV6):
 		head_len = 4;
 		skb_push(skb, head_len);
 		skb->data[3] = NLPID_IPV6;
 		break;
 
-	case __constant_ntohs(ETH_P_802_3):
+	case __constant_htons(ETH_P_802_3):
 		head_len = 10;
 		if (skb_headroom(skb) < head_len) {
 			struct sk_buff *skb2 = skb_realloc_headroom(skb,
@@ -340,7 +340,7 @@
 		skb->data[5] = FR_PAD;
 		skb->data[6] = FR_PAD;
 		skb->data[7] = FR_PAD;
-		*(u16*)(skb->data + 8) = skb->protocol;
+		*(__be16*)(skb->data + 8) = skb->protocol;
 	}
 
 	dlci_to_q922(skb->data, dlci);
@@ -533,7 +533,7 @@
 		skb->protocol = __constant_htons(NLPID_CCITT_ANSI_LMI);
 		fr_hard_header(&skb, LMI_CCITT_ANSI_DLCI);
 	}
-	data = skb->tail;
+	data = skb_tail_pointer(skb);
 	data[i++] = LMI_CALLREF;
 	data[i++] = dce ? LMI_STATUS : LMI_STATUS_ENQUIRY;
 	if (lmi == LMI_ANSI)
@@ -590,7 +590,7 @@
 	skb_put(skb, i);
 	skb->priority = TC_PRIO_CONTROL;
 	skb->dev = dev;
-	skb->nh.raw = skb->data;
+	skb_reset_network_header(skb);
 
 	dev_queue_xmit(skb);
 }
@@ -974,8 +974,8 @@
 
 	} else if (skb->len > 10 && data[3] == FR_PAD &&
 		   data[4] == NLPID_SNAP && data[5] == FR_PAD) {
-		u16 oui = ntohs(*(u16*)(data + 6));
-		u16 pid = ntohs(*(u16*)(data + 8));
+		u16 oui = ntohs(*(__be16*)(data + 6));
+		u16 pid = ntohs(*(__be16*)(data + 8));
 		skb_pull(skb, 10);
 
 		switch ((((u32)oui) << 16) | pid) {
@@ -1011,7 +1011,6 @@
 		stats->rx_bytes += skb->len;
 		if (pvc->state.becn)
 			stats->rx_compressed++;
-		skb->dev = dev;
 		netif_rx(skb);
 		return NET_RX_SUCCESS;
 	} else {
@@ -1128,7 +1127,7 @@
 		memcpy(dev->dev_addr, "\x00\x01", 2);
                 get_random_bytes(dev->dev_addr + 2, ETH_ALEN - 2);
 	} else {
-		*(u16*)dev->dev_addr = htons(dlci);
+		*(__be16*)dev->dev_addr = htons(dlci);
 		dlci_to_q922(dev->broadcast, dlci);
 	}
 	dev->hard_start_xmit = pvc_xmit;
diff --git a/drivers/net/wan/hostess_sv11.c b/drivers/net/wan/hostess_sv11.c
index a02c5fb..9ba3e4e 100644
--- a/drivers/net/wan/hostess_sv11.c
+++ b/drivers/net/wan/hostess_sv11.c
@@ -59,7 +59,7 @@
 	/* Drop the CRC - it's not a good idea to try and negotiate it ;) */
 	skb_trim(skb, skb->len-2);
 	skb->protocol=__constant_htons(ETH_P_WAN_PPP);
-	skb->mac.raw=skb->data;
+	skb_reset_mac_header(skb);
 	skb->dev=c->netdevice;
 	/*
 	 *	Send it to the PPP layer. We don't have time to process
diff --git a/drivers/net/wan/lmc/lmc_main.c b/drivers/net/wan/lmc/lmc_main.c
index 2b54f1b..ae132c1 100644
--- a/drivers/net/wan/lmc/lmc_main.c
+++ b/drivers/net/wan/lmc/lmc_main.c
@@ -1636,7 +1636,7 @@
             if (nsb) {
                 sc->lmc_rxq[i] = nsb;
                 nsb->dev = dev;
-                sc->lmc_rxring[i].buffer1 = virt_to_bus (nsb->tail);
+                sc->lmc_rxring[i].buffer1 = virt_to_bus(skb_tail_pointer(nsb));
             }
             sc->failed_recv_alloc = 1;
             goto skip_packet;
@@ -1667,8 +1667,8 @@
             skb_put (skb, len);
             skb->protocol = lmc_proto_type(sc, skb);
             skb->protocol = htons(ETH_P_WAN_PPP);
-            skb->mac.raw = skb->data;
-//            skb->nh.raw = skb->data;
+            skb_reset_mac_header(skb);
+            /* skb_reset_network_header(skb); */
             skb->dev = dev;
             lmc_proto_netif(sc, skb);
 
@@ -1679,7 +1679,7 @@
             if (nsb) {
                 sc->lmc_rxq[i] = nsb;
                 nsb->dev = dev;
-                sc->lmc_rxring[i].buffer1 = virt_to_bus (nsb->tail);
+                sc->lmc_rxring[i].buffer1 = virt_to_bus(skb_tail_pointer(nsb));
                 /* Transferred to 21140 below */
             }
             else {
@@ -1702,11 +1702,11 @@
             if(!nsb) {
                 goto give_it_anyways;
             }
-            memcpy(skb_put(nsb, len), skb->data, len);
+            skb_copy_from_linear_data(skb, skb_put(nsb, len), len);
             
             nsb->protocol = lmc_proto_type(sc, skb);
-            nsb->mac.raw = nsb->data;
-//            nsb->nh.raw = nsb->data;
+            skb_reset_mac_header(nsb);
+            /* skb_reset_network_header(nsb); */
             nsb->dev = dev;
             lmc_proto_netif(sc, nsb);
         }
@@ -1932,7 +1932,7 @@
         sc->lmc_rxring[i].status = 0x80000000;
 
         /* used to be PKT_BUF_SZ now uses skb since we lose some to head room */
-        sc->lmc_rxring[i].length = skb->end - skb->data;
+        sc->lmc_rxring[i].length = skb_tailroom(skb);
 
         /* use to be tail which is dumb since you're thinking why write
          * to the end of the packj,et but since there's nothing there tail == data
diff --git a/drivers/net/wan/pc300_drv.c b/drivers/net/wan/pc300_drv.c
index 62184de..999bf71 100644
--- a/drivers/net/wan/pc300_drv.c
+++ b/drivers/net/wan/pc300_drv.c
@@ -1755,17 +1755,17 @@
 
 	skb->dev = dev;
 	skb->protocol = htons(ETH_P_CUST);
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	skb->pkt_type = PACKET_HOST;
 	skb->len = 10 + skb_main->len;
 
-	memcpy(skb->data, dev->name, 5);
+	skb_copy_to_linear_data(skb, dev->name, 5);
 	skb->data[5] = '[';
 	skb->data[6] = rx_tx;
 	skb->data[7] = ']';
 	skb->data[8] = ':';
 	skb->data[9] = ' ';
-	memcpy(&skb->data[10], skb_main->data, skb_main->len);
+	skb_copy_from_linear_data(skb_main, &skb->data[10], skb_main->len);
 
 	netif_rx(skb);
 }
diff --git a/drivers/net/wan/pc300_tty.c b/drivers/net/wan/pc300_tty.c
index 5873c34..07dbdfb 100644
--- a/drivers/net/wan/pc300_tty.c
+++ b/drivers/net/wan/pc300_tty.c
@@ -1003,17 +1003,17 @@
 	skb_put (skb, 10 + len); 
 	skb->dev = dev->dev; 
 	skb->protocol = htons(ETH_P_CUST); 
-	skb->mac.raw = skb->data; 
+	skb_reset_mac_header(skb);
 	skb->pkt_type = PACKET_HOST; 
 	skb->len = 10 + len; 
 
-	memcpy(skb->data,dev->dev->name,5);
+	skb_copy_to_linear_data(skb, dev->dev->name, 5);
 	skb->data[5] = '['; 
 	skb->data[6] = rxtx; 
 	skb->data[7] = ']'; 
 	skb->data[8] = ':'; 
 	skb->data[9] = ' '; 
-	memcpy(&skb->data[10], buf, len); 
+	skb_copy_to_linear_data_offset(skb, 10, buf, len);
 	netif_rx(skb); 
 } 	
 
diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c
index fc5c0c6..35eded7 100644
--- a/drivers/net/wan/sbni.c
+++ b/drivers/net/wan/sbni.c
@@ -999,11 +999,6 @@
 	if( !skb )
 		return  NULL;
 
-#ifdef CONFIG_SBNI_MULTILINE
-	skb->dev = ((struct net_local *) dev->priv)->master;
-#else
-	skb->dev = dev;
-#endif
 	skb_reserve( skb, 2 );		/* Align IP on longword boundaries */
 	return  skb;
 }
diff --git a/drivers/net/wan/sealevel.c b/drivers/net/wan/sealevel.c
index 70fb1b9..1313581 100644
--- a/drivers/net/wan/sealevel.c
+++ b/drivers/net/wan/sealevel.c
@@ -61,7 +61,7 @@
 	/* Drop the CRC - it's not a good idea to try and negotiate it ;) */
 	skb_trim(skb, skb->len-2);
 	skb->protocol=htons(ETH_P_WAN_PPP);
-	skb->mac.raw=skb->data;
+	skb_reset_mac_header(skb);
 	skb->dev=c->netdevice;
 	/*
 	 *	Send it to the PPP layer. We don't have time to process
diff --git a/drivers/net/wan/syncppp.c b/drivers/net/wan/syncppp.c
index 218f7b5..67fc67c 100644
--- a/drivers/net/wan/syncppp.c
+++ b/drivers/net/wan/syncppp.c
@@ -227,7 +227,7 @@
 	unsigned long flags;
 
 	skb->dev=dev;
-	skb->mac.raw=skb->data;
+	skb_reset_mac_header(skb);
 
 	if (dev->flags & IFF_RUNNING)
 	{
diff --git a/drivers/net/wan/z85230.c b/drivers/net/wan/z85230.c
index 8b4540b..98ef400 100644
--- a/drivers/net/wan/z85230.c
+++ b/drivers/net/wan/z85230.c
@@ -1656,7 +1656,7 @@
 		else
 		{
 			skb_put(skb, ct);
-			memcpy(skb->data, rxb, ct);
+			skb_copy_to_linear_data(skb, rxb, ct);
 			c->stats.rx_packets++;
 			c->stats.rx_bytes+=ct;
 		}
@@ -1782,7 +1782,7 @@
 		 */
 		c->tx_next_ptr=c->tx_dma_buf[c->tx_dma_used];
 		c->tx_dma_used^=1;	/* Flip temp buffer */
-		memcpy(c->tx_next_ptr, skb->data, skb->len);
+		skb_copy_from_linear_data(skb, c->tx_next_ptr, skb->len);
 	}
 	else
 		c->tx_next_ptr=skb->data;	
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index ece3d9c..c4b3dc2 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -2,47 +2,21 @@
 # Wireless LAN device configuration
 #
 
-menu "Wireless LAN (non-hamradio)"
+menu "Wireless LAN"
+
+config WLAN_PRE80211
+	bool "Wireless LAN (pre-802.11)"
 	depends on NETDEVICES
-
-config NET_RADIO
-	bool "Wireless LAN drivers (non-hamradio) & Wireless Extensions"
-	select WIRELESS_EXT
 	---help---
-	  Support for wireless LANs and everything having to do with radio,
-	  but not with amateur radio or FM broadcasting.
+	  Say Y if you have any pre-802.11 wireless LAN hardware.
 
-	  Saying Y here also enables the Wireless Extensions (creates
-	  /proc/net/wireless and enables iwconfig access). The Wireless
-	  Extension is a generic API allowing a driver to expose to the user
-	  space configuration and statistics specific to common Wireless LANs.
-	  The beauty of it is that a single set of tool can support all the
-	  variations of Wireless LANs, regardless of their type (as long as
-	  the driver supports Wireless Extension). Another advantage is that
-	  these parameters may be changed on the fly without restarting the
-	  driver (or Linux). If you wish to use Wireless Extensions with
-	  wireless PCMCIA (PC-) cards, you need to say Y here; you can fetch
-	  the tools from
-	  <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
-
-config NET_WIRELESS_RTNETLINK
-	bool "Wireless Extension API over RtNetlink"
-	depends on NET_RADIO
-	---help---
-	  Support the Wireless Extension API over the RtNetlink socket
-	  in addition to the traditional ioctl interface (selected above).
-
-	  For now, few tools use this facility, but it might grow in the
-	  future. The only downside is that it adds 4.5 kB to your kernel.
-
-# Note : the cards are obsolete (can't buy them anymore), but the drivers
-# are not, as people are still using them...
-comment "Obsolete Wireless cards support (pre-802.11)"
-	depends on NET_RADIO && (INET || ISA || PCMCIA)
+	  This option does not affect the kernel build, it only
+	  lets you choose drivers.
 
 config STRIP
 	tristate "STRIP (Metricom starmode radio IP)"
-	depends on NET_RADIO && INET
+	depends on INET && WLAN_PRE80211
+	select WIRELESS_EXT
 	---help---
 	  Say Y if you have a Metricom radio and intend to use Starmode Radio
 	  IP. STRIP is a radio protocol developed for the MosquitoNet project
@@ -65,7 +39,8 @@
 
 config ARLAN
 	tristate "Aironet Arlan 655 & IC2200 DS support"
-	depends on NET_RADIO && ISA && !64BIT
+	depends on ISA && !64BIT && WLAN_PRE80211
+	select WIRELESS_EXT
 	---help---
 	  Aironet makes Arlan, a class of wireless LAN adapters. These use the
 	  www.Telxon.com chip, which is also used on several similar cards.
@@ -80,7 +55,8 @@
 
 config WAVELAN
 	tristate "AT&T/Lucent old WaveLAN & DEC RoamAbout DS ISA support"
-	depends on NET_RADIO && ISA
+	depends on ISA && WLAN_PRE80211
+	select WIRELESS_EXT
 	---help---
 	  The Lucent WaveLAN (formerly NCR and AT&T; or DEC RoamAbout DS) is
 	  a Radio LAN (wireless Ethernet-like Local Area Network) using the
@@ -107,7 +83,8 @@
 
 config PCMCIA_WAVELAN
 	tristate "AT&T/Lucent old WaveLAN Pcmcia wireless support"
-	depends on NET_RADIO && PCMCIA
+	depends on PCMCIA && WLAN_PRE80211
+	select WIRELESS_EXT
 	help
 	  Say Y here if you intend to attach an AT&T/Lucent Wavelan PCMCIA
 	  (PC-card) wireless Ethernet networking card to your computer.  This
@@ -118,7 +95,8 @@
 
 config PCMCIA_NETWAVE
 	tristate "Xircom Netwave AirSurfer Pcmcia wireless support"
-	depends on NET_RADIO && PCMCIA
+	depends on PCMCIA && WLAN_PRE80211
+	select WIRELESS_EXT
 	help
 	  Say Y here if you intend to attach this type of PCMCIA (PC-card)
 	  wireless Ethernet networking card to your computer.
@@ -126,12 +104,20 @@
 	  To compile this driver as a module, choose M here: the module will be
 	  called netwave_cs.  If unsure, say N.
 
-comment "Wireless 802.11 Frequency Hopping cards support"
-	depends on NET_RADIO && PCMCIA
+
+config WLAN_80211
+	bool "Wireless LAN (IEEE 802.11)"
+	depends on NETDEVICES
+	---help---
+	  Say Y if you have any 802.11 wireless LAN hardware.
+
+	  This option does not affect the kernel build, it only
+	  lets you choose drivers.
 
 config PCMCIA_RAYCS
 	tristate "Aviator/Raytheon 2.4MHz wireless support"
-	depends on NET_RADIO && PCMCIA
+	depends on PCMCIA && WLAN_80211
+	select WIRELESS_EXT
 	---help---
 	  Say Y here if you intend to attach an Aviator/Raytheon PCMCIA
 	  (PC-card) wireless Ethernet networking card to your computer.
@@ -141,12 +127,10 @@
 	  To compile this driver as a module, choose M here: the module will be
 	  called ray_cs.  If unsure, say N.
 
-comment "Wireless 802.11b ISA/PCI cards support"
-	depends on NET_RADIO && (ISA || PCI || PPC_PMAC || PCMCIA)
-
 config IPW2100
 	tristate "Intel PRO/Wireless 2100 Network Connection"
-	depends on NET_RADIO && PCI
+	depends on PCI && WLAN_80211
+	select WIRELESS_EXT
 	select FW_LOADER
 	select IEEE80211
 	---help---
@@ -200,7 +184,8 @@
 
 config IPW2200
 	tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection"
-	depends on NET_RADIO && PCI
+	depends on PCI && WLAN_80211
+	select WIRELESS_EXT
 	select FW_LOADER
 	select IEEE80211
 	---help---
@@ -280,9 +265,23 @@
 
 	  If you are not sure, say N here.
 
+config LIBERTAS_USB
+	tristate "Marvell Libertas 8388 802.11a/b/g cards"
+	depends on NET_RADIO && USB
+	select FW_LOADER
+	---help---
+	  A driver for Marvell Libertas 8388 USB devices.
+
+config LIBERTAS_USB_DEBUG
+	bool "Enable full debugging output in the Libertas USB module."
+	depends on LIBERTAS_USB
+	---help---
+	  Debugging support.
+
 config AIRO
 	tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards"
- 	depends on NET_RADIO && ISA_DMA_API && (PCI || BROKEN)
+	depends on ISA_DMA_API && WLAN_80211 && (PCI || BROKEN)
+	select WIRELESS_EXT
 	select CRYPTO
 	---help---
 	  This is the standard Linux driver to support Cisco/Aironet ISA and
@@ -299,7 +298,8 @@
 
 config HERMES
 	tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)"
-	depends on NET_RADIO && (PPC_PMAC || PCI || PCMCIA)
+	depends on (PPC_PMAC || PCI || PCMCIA) && WLAN_80211
+	select WIRELESS_EXT
 	---help---
 	  A driver for 802.11b wireless cards based on the "Hermes" or
 	  Intersil HFA384x (Prism 2) MAC controller.  This includes the vast
@@ -373,7 +373,8 @@
 
 config ATMEL
       tristate "Atmel at76c50x chipset  802.11b support"
-      depends on NET_RADIO && (PCI || PCMCIA)
+      depends on (PCI || PCMCIA) && WLAN_80211
+      select WIRELESS_EXT
       select FW_LOADER
       select CRC32
        ---help---
@@ -394,13 +395,9 @@
         Enable support for PCI and mini-PCI cards containing the
         Atmel at76c506 chip.
 
-# If Pcmcia is compiled in, offer Pcmcia cards...
-comment "Wireless 802.11b Pcmcia/Cardbus cards support"
-	depends on NET_RADIO && PCMCIA
-
 config PCMCIA_HERMES
 	tristate "Hermes PCMCIA card support"
-	depends on NET_RADIO && PCMCIA && HERMES
+	depends on PCMCIA && HERMES
 	---help---
 	  A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
 	  as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/
@@ -420,7 +417,7 @@
 
 config PCMCIA_SPECTRUM
 	tristate "Symbol Spectrum24 Trilogy PCMCIA card support"
-	depends on NET_RADIO && PCMCIA && HERMES
+	depends on PCMCIA && HERMES
 	select FW_LOADER
 	---help---
 
@@ -434,7 +431,8 @@
 
 config AIRO_CS
 	tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards"
-	depends on NET_RADIO && PCMCIA && (BROKEN || !M32R)
+	depends on PCMCIA && (BROKEN || !M32R) && WLAN_80211
+	select WIRELESS_EXT
 	select CRYPTO
 	select CRYPTO_AES
 	---help---
@@ -458,7 +456,8 @@
 
 config PCMCIA_ATMEL
 	tristate "Atmel at76c502/at76c504 PCMCIA cards"
-	depends on NET_RADIO && ATMEL && PCMCIA
+	depends on ATMEL && PCMCIA
+	select WIRELESS_EXT
 	select FW_LOADER
 	select CRC32
 	---help---
@@ -467,17 +466,17 @@
 
 config PCMCIA_WL3501
       tristate "Planet WL3501 PCMCIA cards"
-      depends on NET_RADIO && EXPERIMENTAL && PCMCIA
+      depends on EXPERIMENTAL && PCMCIA && WLAN_80211
+      select WIRELESS_EXT
        ---help---
          A driver for WL3501 PCMCIA 802.11 wireless cards made by Planet.
 	 It has basic support for Linux wireless extensions and initial
 	 micro support for ethtool.
 
-comment "Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support"
-	depends on NET_RADIO && PCI
 config PRISM54
 	tristate 'Intersil Prism GT/Duette/Indigo PCI/Cardbus' 
-	depends on PCI && NET_RADIO && EXPERIMENTAL
+	depends on PCI && EXPERIMENTAL && WLAN_80211
+	select WIRELESS_EXT
 	select FW_LOADER
 	---help---
 	  Enable PCI and Cardbus support for the following chipset based cards:
@@ -523,7 +522,8 @@
 
 config USB_ZD1201
 	tristate "USB ZD1201 based Wireless device support"
-	depends on USB && NET_RADIO
+	depends on USB && WLAN_80211
+	select WIRELESS_EXT
 	select FW_LOADER
 	---help---
 	  Say Y if you want to use wireless LAN adapters based on the ZyDAS
@@ -542,11 +542,4 @@
 source "drivers/net/wireless/bcm43xx/Kconfig"
 source "drivers/net/wireless/zd1211rw/Kconfig"
 
-# yes, this works even when no drivers are selected
-config NET_WIRELESS
-	bool
-	depends on NET_RADIO && (ISA || PCI || PPC_PMAC || PCMCIA)
-	default y
-
 endmenu
-
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index c613af1..d212460 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -43,3 +43,4 @@
 obj-$(CONFIG_PCMCIA_WL3501)	+= wl3501_cs.o
 
 obj-$(CONFIG_USB_ZD1201)	+= zd1201.o
+obj-$(CONFIG_LIBERTAS_USB)     += libertas/
diff --git a/drivers/net/wireless/README b/drivers/net/wireless/README
deleted file mode 100644
index 0c274bf..0000000
--- a/drivers/net/wireless/README
+++ /dev/null
@@ -1,25 +0,0 @@
-	README
-	------
-
-	This directory is mostly for Wireless LAN drivers, in their
-various incarnations (ISA, PCI, Pcmcia...).
-	This separate directory is needed because a lot of driver work
-on different bus (typically PCI + Pcmcia) and share 95% of the
-code. This allow the code and the config options to be in one single
-place instead of scattered all over the driver tree, which is never
-100% satisfactory.
-
-	Note : if you want more info on the topic of Wireless LANs,
-you are kindly invited to have a look at the Wireless Howto :
-		http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/
-	Some Wireless LAN drivers, like orinoco_cs, require the use of
-Wireless Tools to be configured :
-		http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html
-
-	Special notes for distribution maintainers :
-	1) wvlan_cs will be discontinued soon in favor of orinoco_cs
-	2) Please add Wireless Tools support in your scripts
-
-	Have fun...
-
-	Jean
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
index 2ada76a..f21bbaf 100644
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/airo.c
@@ -1145,6 +1145,7 @@
 struct airo_info {
 	struct net_device_stats	stats;
 	struct net_device             *dev;
+	struct list_head              dev_list;
 	/* Note, we can have MAX_FIDS outstanding.  FIDs are 16-bits, so we
 	   use the high bit to mark whether it is in use. */
 #define MAX_FIDS 6
@@ -2360,6 +2361,21 @@
 	return 0;
 }
 
+static LIST_HEAD(airo_devices);
+
+static void add_airo_dev(struct airo_info *ai)
+{
+	/* Upper layers already keep track of PCI devices,
+	 * so we only need to remember our non-PCI cards. */
+	if (!ai->pci)
+		list_add_tail(&ai->dev_list, &airo_devices);
+}
+
+static void del_airo_dev(struct airo_info *ai)
+{
+	if (!ai->pci)
+		list_del(&ai->dev_list);
+}
 
 static int airo_close(struct net_device *dev) {
 	struct airo_info *ai = dev->priv;
@@ -2381,8 +2397,6 @@
 	return 0;
 }
 
-static void del_airo_dev( struct net_device *dev );
-
 void stop_airo_card( struct net_device *dev, int freeres )
 {
 	struct airo_info *ai = dev->priv;
@@ -2434,17 +2448,15 @@
 		}
         }
 	crypto_free_cipher(ai->tfm);
-	del_airo_dev( dev );
+	del_airo_dev(ai);
 	free_netdev( dev );
 }
 
 EXPORT_SYMBOL(stop_airo_card);
 
-static int add_airo_dev( struct net_device *dev );
-
 static int wll_header_parse(struct sk_buff *skb, unsigned char *haddr)
 {
-	memcpy(haddr, skb->mac.raw + 10, ETH_ALEN);
+	memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN);
 	return ETH_ALEN;
 }
 
@@ -2740,8 +2752,6 @@
 
 static void airo_networks_free(struct airo_info *ai)
 {
-	if (!ai->networks)
-		return;
 	kfree(ai->networks);
 	ai->networks = NULL;
 }
@@ -2816,12 +2826,10 @@
 	if (IS_ERR(ai->airo_thread_task))
 		goto err_out_free;
 	ai->tfm = NULL;
-	rc = add_airo_dev( dev );
-	if (rc)
-		goto err_out_thr;
+	add_airo_dev(ai);
 
 	if (airo_networks_allocate (ai))
-		goto err_out_unlink;
+		goto err_out_thr;
 	airo_networks_initialize (ai);
 
 	/* The Airo-specific entries in the device structure. */
@@ -2937,9 +2945,8 @@
 	free_irq(dev->irq, dev);
 err_out_nets:
 	airo_networks_free(ai);
-err_out_unlink:
-	del_airo_dev(dev);
 err_out_thr:
+	del_airo_dev(ai);
 	set_bit(JOB_DIE, &ai->jobs);
 	kthread_stop(ai->airo_thread_task);
 err_out_free:
@@ -3411,14 +3418,12 @@
 			OUT4500( apriv, EVACK, EV_RX);
 
 			if (test_bit(FLAG_802_11, &apriv->flags)) {
-				skb->mac.raw = skb->data;
+				skb_reset_mac_header(skb);
 				skb->pkt_type = PACKET_OTHERHOST;
 				skb->dev = apriv->wifidev;
 				skb->protocol = htons(ETH_P_802_2);
-			} else {
-				skb->dev = dev;
+			} else
 				skb->protocol = eth_type_trans(skb,dev);
-			}
 			skb->dev->last_rx = jiffies;
 			skb->ip_summed = CHECKSUM_NONE;
 
@@ -3641,7 +3646,6 @@
 		}
 #endif /* WIRELESS_SPY */
 
-		skb->dev = ai->dev;
 		skb->ip_summed = CHECKSUM_NONE;
 		skb->protocol = eth_type_trans(skb, ai->dev);
 		skb->dev->last_rx = jiffies;
@@ -3749,7 +3753,7 @@
 		wireless_spy_update(ai->dev, sa, &wstats);
 	}
 #endif /* IW_WIRELESS_SPY */
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	skb->pkt_type = PACKET_OTHERHOST;
 	skb->dev = ai->wifidev;
 	skb->protocol = htons(ETH_P_802_2);
@@ -5538,11 +5542,6 @@
 	return 0;
 }
 
-static struct net_device_list {
-	struct net_device *dev;
-	struct net_device_list *next;
-} *airo_devices;
-
 /* Since the card doesn't automatically switch to the right WEP mode,
    we will make it do it.  If the card isn't associated, every secs we
    will switch WEP modes to see if that will help.  If the card is
@@ -5585,26 +5584,6 @@
 	apriv->expires = RUN_AT(HZ*3);
 }
 
-static int add_airo_dev( struct net_device *dev ) {
-	struct net_device_list *node = kmalloc( sizeof( *node ), GFP_KERNEL );
-	if ( !node )
-		return -ENOMEM;
-
-	node->dev = dev;
-	node->next = airo_devices;
-	airo_devices = node;
-
-	return 0;
-}
-
-static void del_airo_dev( struct net_device *dev ) {
-	struct net_device_list **p = &airo_devices;
-	while( *p && ( (*p)->dev != dev ) )
-		p = &(*p)->next;
-	if ( *p && (*p)->dev == dev )
-		*p = (*p)->next;
-}
-
 #ifdef CONFIG_PCI
 static int __devinit airo_pci_probe(struct pci_dev *pdev,
 				    const struct pci_device_id *pent)
@@ -5628,6 +5607,10 @@
 
 static void __devexit airo_pci_remove(struct pci_dev *pdev)
 {
+	struct net_device *dev = pci_get_drvdata(pdev);
+
+	airo_print_info(dev->name, "Unregistering...");
+	stop_airo_card(dev, 1);
 }
 
 static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state)
@@ -5753,9 +5736,11 @@
 
 static void __exit airo_cleanup_module( void )
 {
-	while( airo_devices ) {
-		airo_print_info(airo_devices->dev->name, "Unregistering...\n");
-		stop_airo_card( airo_devices->dev, 1 );
+	struct airo_info *ai;
+	while(!list_empty(&airo_devices)) {
+		ai = list_entry(airo_devices.next, struct airo_info, dev_list);
+		airo_print_info(ai->dev->name, "Unregistering...");
+		stop_airo_card(ai->dev, 1);
 	}
 #ifdef CONFIG_PCI
 	pci_unregister_driver(&airo_driver);
diff --git a/drivers/net/wireless/arlan-main.c b/drivers/net/wireless/arlan-main.c
index 4688e56..498e848 100644
--- a/drivers/net/wireless/arlan-main.c
+++ b/drivers/net/wireless/arlan-main.c
@@ -1500,7 +1500,6 @@
 				break;
 			}
 			skb_reserve(skb, 2);
-			skb->dev = dev;
 			skbtmp = skb_put(skb, pkt_len);
 
 			memcpy_fromio(skbtmp + ARLAN_FAKE_HDR_LEN, ((char __iomem *) arlan) + rxOffset, pkt_len - ARLAN_FAKE_HDR_LEN);
diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c
index 23eba69..51a7db5 100644
--- a/drivers/net/wireless/atmel.c
+++ b/drivers/net/wireless/atmel.c
@@ -827,14 +827,14 @@
 	if (priv->wep_is_on)
 		frame_ctl |= IEEE80211_FCTL_PROTECTED;
 	if (priv->operating_mode == IW_MODE_ADHOC) {
-		memcpy(&header.addr1, skb->data, 6);
+		skb_copy_from_linear_data(skb, &header.addr1, 6);
 		memcpy(&header.addr2, dev->dev_addr, 6);
 		memcpy(&header.addr3, priv->BSSID, 6);
 	} else {
 		frame_ctl |= IEEE80211_FCTL_TODS;
 		memcpy(&header.addr1, priv->CurrentBSSID, 6);
 		memcpy(&header.addr2, dev->dev_addr, 6);
-		memcpy(&header.addr3, skb->data, 6);
+		skb_copy_from_linear_data(skb, &header.addr3, 6);
 	}
 
 	if (priv->use_wpa)
@@ -920,7 +920,6 @@
 		memcpy(&skbp[6], header->addr2, 6); /* source address */
 
 	priv->dev->last_rx = jiffies;
-	skb->dev = priv->dev;
 	skb->protocol = eth_type_trans(skb, priv->dev);
 	skb->ip_summed = CHECKSUM_NONE;
 	netif_rx(skb);
@@ -1028,7 +1027,6 @@
 				       priv->rx_buf,
 				       priv->frag_len + 12);
 				priv->dev->last_rx = jiffies;
-				skb->dev = priv->dev;
 				skb->protocol = eth_type_trans(skb, priv->dev);
 				skb->ip_summed = CHECKSUM_NONE;
 				netif_rx(skb);
diff --git a/drivers/net/wireless/bcm43xx/Kconfig b/drivers/net/wireless/bcm43xx/Kconfig
index 533993f..ce397e4 100644
--- a/drivers/net/wireless/bcm43xx/Kconfig
+++ b/drivers/net/wireless/bcm43xx/Kconfig
@@ -1,6 +1,7 @@
 config BCM43XX
 	tristate "Broadcom BCM43xx wireless support"
-	depends on PCI && IEEE80211 && IEEE80211_SOFTMAC && NET_RADIO && EXPERIMENTAL
+	depends on PCI && IEEE80211 && IEEE80211_SOFTMAC && WLAN_80211 && EXPERIMENTAL
+	select WIRELESS_EXT
 	select FW_LOADER
 	select HW_RANDOM
 	---help---
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx.h b/drivers/net/wireless/bcm43xx/bcm43xx.h
index 95ff175d..f8483c1 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx.h
+++ b/drivers/net/wireless/bcm43xx/bcm43xx.h
@@ -277,11 +277,14 @@
 #define BCM43xx_SBTMSTATELOW_REJECT		0x02
 #define BCM43xx_SBTMSTATELOW_CLOCK		0x10000
 #define BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK	0x20000
+#define BCM43xx_SBTMSTATELOW_G_MODE_ENABLE	0x20000000
 
 /* sbtmstatehigh state flags */
 #define BCM43xx_SBTMSTATEHIGH_SERROR		0x00000001
 #define BCM43xx_SBTMSTATEHIGH_BUSY		0x00000004
 #define BCM43xx_SBTMSTATEHIGH_TIMEOUT		0x00000020
+#define BCM43xx_SBTMSTATEHIGH_G_PHY_AVAIL	0x00010000
+#define BCM43xx_SBTMSTATEHIGH_A_PHY_AVAIL	0x00020000
 #define BCM43xx_SBTMSTATEHIGH_COREFLAGS		0x1FFF0000
 #define BCM43xx_SBTMSTATEHIGH_DMA64BIT		0x10000000
 #define BCM43xx_SBTMSTATEHIGH_GATEDCLK		0x20000000
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_dma.c b/drivers/net/wireless/bcm43xx/bcm43xx_dma.c
index 6e0dc76..e3d2e61 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_dma.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_dma.c
@@ -998,7 +998,8 @@
 			assert(0);
 			return;
 		}
-		memcpy(skb_put(bounce_skb, skb->len), skb->data, skb->len);
+		skb_copy_from_linear_data(skb, skb_put(bounce_skb, skb->len),
+					  skb->len);
 		dev_kfree_skb_any(skb);
 		skb = bounce_skb;
 	}
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c b/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c
index c947025..d2df6a0 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c
@@ -32,7 +32,7 @@
 #include <linux/netdevice.h>
 #include <linux/pci.h>
 #include <linux/string.h>
-#include <linux/utsrelease.h>
+#include <linux/utsname.h>
 
 
 static void bcm43xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
@@ -40,7 +40,7 @@
 	struct bcm43xx_private *bcm = bcm43xx_priv(dev);
 
 	strncpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
-	strncpy(info->version, UTS_RELEASE, sizeof(info->version));
+	strncpy(info->version, utsname()->release, sizeof(info->version));
 	strncpy(info->bus_info, pci_name(bcm->pci_dev), ETHTOOL_BUSINFO_LEN);
 }
 
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
index a38e7ee..5e96bca 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
@@ -1407,7 +1407,7 @@
 				& ~(BCM43xx_SBF_MAC_ENABLED | 0x00000002));
 	} else {
 		if (connect_phy)
-			flags |= 0x20000000;
+			flags |= BCM43xx_SBTMSTATELOW_G_MODE_ENABLE;
 		bcm43xx_phy_connect(bcm, connect_phy);
 		bcm43xx_core_enable(bcm, flags);
 		bcm43xx_write16(bcm, 0x03E6, 0x0000);
@@ -3604,7 +3604,7 @@
 		u32 sbtmstatelow;
 
 		sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
-		sbtmstatelow |= 0x20000000;
+		sbtmstatelow |= BCM43xx_SBTMSTATELOW_G_MODE_ENABLE;
 		bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);
 	}
 	err = wireless_core_up(bcm, 1);
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c
index 72529a4..b37f1e3 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c
@@ -168,16 +168,16 @@
 
 	flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH);
 	if (connect) {
-		if (!(flags & 0x00010000))
+		if (!(flags & BCM43xx_SBTMSTATEHIGH_G_PHY_AVAIL))
 			return -ENODEV;
 		flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
-		flags |= (0x800 << 18);
+		flags |= BCM43xx_SBTMSTATELOW_G_MODE_ENABLE;
 		bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, flags);
 	} else {
-		if (!(flags & 0x00020000))
+		if (!(flags & BCM43xx_SBTMSTATEHIGH_A_PHY_AVAIL))
 			return -ENODEV;
 		flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
-		flags &= ~(0x800 << 18);
+		flags &= ~BCM43xx_SBTMSTATELOW_G_MODE_ENABLE;
 		bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, flags);
 	}
 out:
@@ -300,16 +300,20 @@
 
 	if (phy->rev > 2) {
 		bcm43xx_phy_write(bcm, 0x0422, 0x287A);
-		bcm43xx_phy_write(bcm, 0x0420, (bcm43xx_phy_read(bcm, 0x0420) & 0x0FFF) | 0x3000); 
+		bcm43xx_phy_write(bcm, 0x0420, (bcm43xx_phy_read(bcm, 0x0420)
+				  & 0x0FFF) | 0x3000);
 	}
 		
-	bcm43xx_phy_write(bcm, 0x04A8, (bcm43xx_phy_read(bcm, 0x04A8) & 0x8080) | 0x7874);
+	bcm43xx_phy_write(bcm, 0x04A8, (bcm43xx_phy_read(bcm, 0x04A8) & 0x8080)
+					| 0x7874);
 	bcm43xx_phy_write(bcm, 0x048E, 0x1C00);
 
 	if (phy->rev == 1) {
-		bcm43xx_phy_write(bcm, 0x04AB, (bcm43xx_phy_read(bcm, 0x04AB) & 0xF0FF) | 0x0600);
+		bcm43xx_phy_write(bcm, 0x04AB, (bcm43xx_phy_read(bcm, 0x04AB)
+				  & 0xF0FF) | 0x0600);
 		bcm43xx_phy_write(bcm, 0x048B, 0x005E);
-		bcm43xx_phy_write(bcm, 0x048C, (bcm43xx_phy_read(bcm, 0x048C) & 0xFF00) | 0x001E);
+		bcm43xx_phy_write(bcm, 0x048C, (bcm43xx_phy_read(bcm, 0x048C)
+				  & 0xFF00) | 0x001E);
 		bcm43xx_phy_write(bcm, 0x048D, 0x0002);
 	}
 
@@ -335,7 +339,8 @@
 	if (phy->rev == 1) {
 		bcm43xx_phy_write(bcm, 0x0406, 0x4F19);
 		bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
-				  (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0xFC3F) | 0x0340);
+				  (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS)
+				  & 0xFC3F) | 0x0340);
 		bcm43xx_phy_write(bcm, 0x042C, 0x005A);
 		bcm43xx_phy_write(bcm, 0x0427, 0x001A);
 
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_phy.h b/drivers/net/wireless/bcm43xx/bcm43xx_phy.h
index 1f321ef..7311836 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_phy.h
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_phy.h
@@ -48,6 +48,10 @@
 		local_irq_restore(flags);	\
 	} while (0)
 
+/* Card uses the loopback gain stuff */
+#define has_loopback_gain(phy) \
+        (((phy)->rev > 1) || ((phy)->connected))
+
 u16 bcm43xx_phy_read(struct bcm43xx_private *bcm, u16 offset);
 void bcm43xx_phy_write(struct bcm43xx_private *bcm, u16 offset, u16 val);
 
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_radio.c b/drivers/net/wireless/bcm43xx/bcm43xx_radio.c
index 4025dd0..6a109f4 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_radio.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_radio.c
@@ -1343,11 +1343,110 @@
 	return ret;
 }
 
+#define LPD(L, P, D)    (((L) << 2) | ((P) << 1) | ((D) << 0))
+static u16 bcm43xx_get_812_value(struct bcm43xx_private *bcm, u8 lpd)
+{
+	struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+	struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+	u16 loop_or = 0;
+	u16 adj_loopback_gain = phy->loopback_gain[0];
+	u8 loop;
+	u16 extern_lna_control;
+
+	if (!phy->connected)
+		return 0;
+	if (!has_loopback_gain(phy)) {
+		if (phy->rev < 7 || !(bcm->sprom.boardflags
+		    & BCM43xx_BFL_EXTLNA)) {
+			switch (lpd) {
+			case LPD(0, 1, 1):
+				return 0x0FB2;
+			case LPD(0, 0, 1):
+				return 0x00B2;
+			case LPD(1, 0, 1):
+				return 0x30B2;
+			case LPD(1, 0, 0):
+				return 0x30B3;
+			default:
+				assert(0);
+			}
+		} else {
+			switch (lpd) {
+			case LPD(0, 1, 1):
+				return 0x8FB2;
+			case LPD(0, 0, 1):
+				return 0x80B2;
+			case LPD(1, 0, 1):
+				return 0x20B2;
+			case LPD(1, 0, 0):
+				return 0x20B3;
+			default:
+				assert(0);
+			}
+		}
+	} else {
+		if (radio->revision == 8)
+			adj_loopback_gain += 0x003E;
+		else
+			adj_loopback_gain += 0x0026;
+		if (adj_loopback_gain >= 0x46) {
+			adj_loopback_gain -= 0x46;
+			extern_lna_control = 0x3000;
+		} else if (adj_loopback_gain >= 0x3A) {
+			adj_loopback_gain -= 0x3A;
+			extern_lna_control = 0x2000;
+		} else if (adj_loopback_gain >= 0x2E) {
+			adj_loopback_gain -= 0x2E;
+			extern_lna_control = 0x1000;
+		} else {
+			adj_loopback_gain -= 0x10;
+			extern_lna_control = 0x0000;
+		}
+		for (loop = 0; loop < 16; loop++) {
+			u16 tmp = adj_loopback_gain - 6 * loop;
+			if (tmp < 6)
+				break;
+		}
+
+		loop_or = (loop << 8) | extern_lna_control;
+		if (phy->rev >= 7 && bcm->sprom.boardflags
+		    & BCM43xx_BFL_EXTLNA) {
+			if (extern_lna_control)
+				loop_or |= 0x8000;
+			switch (lpd) {
+			case LPD(0, 1, 1):
+				return 0x8F92;
+			case LPD(0, 0, 1):
+				return (0x8092 | loop_or);
+			case LPD(1, 0, 1):
+				return (0x2092 | loop_or);
+			case LPD(1, 0, 0):
+				return (0x2093 | loop_or);
+			default:
+				assert(0);
+			}
+		} else {
+			switch (lpd) {
+			case LPD(0, 1, 1):
+				return 0x0F92;
+			case LPD(0, 0, 1):
+			case LPD(1, 0, 1):
+				return (0x0092 | loop_or);
+			case LPD(1, 0, 0):
+				return (0x0093 | loop_or);
+			default:
+				assert(0);
+			}
+		}
+	}
+	return 0;
+}
+
 u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm)
 {
 	struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
 	struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
-	u16 backup[19] = { 0 };
+	u16 backup[21] = { 0 };
 	u16 ret;
 	u16 i, j;
 	u32 tmp1 = 0, tmp2 = 0;
@@ -1373,19 +1472,36 @@
 			backup[8] = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS);
 			backup[9] = bcm43xx_phy_read(bcm, 0x0802);
 			bcm43xx_phy_write(bcm, 0x0814,
-			                  (bcm43xx_phy_read(bcm, 0x0814) | 0x0003));
+			                  (bcm43xx_phy_read(bcm, 0x0814)
+					  | 0x0003));
 			bcm43xx_phy_write(bcm, 0x0815,
-			                  (bcm43xx_phy_read(bcm, 0x0815) & 0xFFFC));	
+			                  (bcm43xx_phy_read(bcm, 0x0815)
+					  & 0xFFFC));
 			bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
-			                  (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0x7FFF));
+			                  (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS)
+					  & 0x7FFF));
 			bcm43xx_phy_write(bcm, 0x0802,
 			                  (bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC));
-			bcm43xx_phy_write(bcm, 0x0811, 0x01B3);
-			bcm43xx_phy_write(bcm, 0x0812, 0x0FB2);
+			if (phy->rev > 1) { /* loopback gain enabled */
+				backup[19] = bcm43xx_phy_read(bcm, 0x080F);
+				backup[20] = bcm43xx_phy_read(bcm, 0x0810);
+				if (phy->rev >= 3)
+					bcm43xx_phy_write(bcm, 0x080F, 0xC020);
+				else
+					bcm43xx_phy_write(bcm, 0x080F, 0x8020);
+				bcm43xx_phy_write(bcm, 0x0810, 0x0000);
+			}
+			bcm43xx_phy_write(bcm, 0x0812,
+					  bcm43xx_get_812_value(bcm, LPD(0, 1, 1)));
+			if (phy->rev < 7 || !(bcm->sprom.boardflags
+			    & BCM43xx_BFL_EXTLNA))
+				bcm43xx_phy_write(bcm, 0x0811, 0x01B3);
+			else
+				bcm43xx_phy_write(bcm, 0x0811, 0x09B3);
 		}
-		bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO,
-		                (bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_RADIO) | 0x8000));
 	}
+	bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO,
+	                (bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_RADIO) | 0x8000));
 	backup[10] = bcm43xx_phy_read(bcm, 0x0035);
 	bcm43xx_phy_write(bcm, 0x0035,
 	                  (bcm43xx_phy_read(bcm, 0x0035) & 0xFF7F));
@@ -1397,10 +1513,12 @@
 		bcm43xx_write16(bcm, 0x03E6, 0x0122);
 	} else {
 		if (phy->analog >= 2)
-			bcm43xx_phy_write(bcm, 0x0003, (bcm43xx_phy_read(bcm, 0x0003)
-					& 0xFFBF) | 0x0040);
+			bcm43xx_phy_write(bcm, 0x0003,
+					  (bcm43xx_phy_read(bcm, 0x0003)
+					  & 0xFFBF) | 0x0040);
 		bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT,
-		                (bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT) | 0x2000));
+		                (bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT)
+				| 0x2000));
 	}
 
 	ret = bcm43xx_radio_calibrationvalue(bcm);
@@ -1408,16 +1526,25 @@
 	if (phy->type == BCM43xx_PHYTYPE_B)
 		bcm43xx_radio_write16(bcm, 0x0078, 0x0026);
 
+	if (phy->connected)
+		bcm43xx_phy_write(bcm, 0x0812,
+				  bcm43xx_get_812_value(bcm, LPD(0, 1, 1)));
 	bcm43xx_phy_write(bcm, 0x0015, 0xBFAF);
 	bcm43xx_phy_write(bcm, 0x002B, 0x1403);
 	if (phy->connected)
-		bcm43xx_phy_write(bcm, 0x0812, 0x00B2);
+		bcm43xx_phy_write(bcm, 0x0812,
+				  bcm43xx_get_812_value(bcm, LPD(0, 0, 1)));
 	bcm43xx_phy_write(bcm, 0x0015, 0xBFA0);
 	bcm43xx_radio_write16(bcm, 0x0051,
 	                      (bcm43xx_radio_read16(bcm, 0x0051) | 0x0004));
-	bcm43xx_radio_write16(bcm, 0x0052, 0x0000);
-	bcm43xx_radio_write16(bcm, 0x0043,
-			      (bcm43xx_radio_read16(bcm, 0x0043) & 0xFFF0) | 0x0009);
+	if (radio->revision == 8)
+		bcm43xx_radio_write16(bcm, 0x0043, 0x001F);
+	else {
+		bcm43xx_radio_write16(bcm, 0x0052, 0x0000);
+		bcm43xx_radio_write16(bcm, 0x0043,
+				      (bcm43xx_radio_read16(bcm, 0x0043) & 0xFFF0)
+				      | 0x0009);
+	}
 	bcm43xx_phy_write(bcm, 0x0058, 0x0000);
 
 	for (i = 0; i < 16; i++) {
@@ -1425,21 +1552,25 @@
 		bcm43xx_phy_write(bcm, 0x0059, 0xC810);
 		bcm43xx_phy_write(bcm, 0x0058, 0x000D);
 		if (phy->connected)
-			bcm43xx_phy_write(bcm, 0x0812, 0x30B2);
+			bcm43xx_phy_write(bcm, 0x0812,
+					  bcm43xx_get_812_value(bcm, LPD(1, 0, 1)));
 		bcm43xx_phy_write(bcm, 0x0015, 0xAFB0);
 		udelay(10);
 		if (phy->connected)
-			bcm43xx_phy_write(bcm, 0x0812, 0x30B2);
+			bcm43xx_phy_write(bcm, 0x0812,
+					  bcm43xx_get_812_value(bcm, LPD(1, 0, 1)));
 		bcm43xx_phy_write(bcm, 0x0015, 0xEFB0);
 		udelay(10);
 		if (phy->connected)
-			bcm43xx_phy_write(bcm, 0x0812, 0x30B2);
+			bcm43xx_phy_write(bcm, 0x0812,
+					  bcm43xx_get_812_value(bcm, LPD(1, 0, 0)));
 		bcm43xx_phy_write(bcm, 0x0015, 0xFFF0);
-		udelay(10);
+		udelay(20);
 		tmp1 += bcm43xx_phy_read(bcm, 0x002D);
 		bcm43xx_phy_write(bcm, 0x0058, 0x0000);
 		if (phy->connected)
-			bcm43xx_phy_write(bcm, 0x0812, 0x30B2);
+			bcm43xx_phy_write(bcm, 0x0812,
+					  bcm43xx_get_812_value(bcm, LPD(1, 0, 1)));
 		bcm43xx_phy_write(bcm, 0x0015, 0xAFB0);
 	}
 
@@ -1457,21 +1588,29 @@
 			bcm43xx_phy_write(bcm, 0x0059, 0xC810);
 			bcm43xx_phy_write(bcm, 0x0058, 0x000D);
 			if (phy->connected)
-				bcm43xx_phy_write(bcm, 0x0812, 0x30B2);
+				bcm43xx_phy_write(bcm, 0x0812,
+						  bcm43xx_get_812_value(bcm,
+						  LPD(1, 0, 1)));
 			bcm43xx_phy_write(bcm, 0x0015, 0xAFB0);
 			udelay(10);
 			if (phy->connected)
-				bcm43xx_phy_write(bcm, 0x0812, 0x30B2);
+				bcm43xx_phy_write(bcm, 0x0812,
+						  bcm43xx_get_812_value(bcm,
+						  LPD(1, 0, 1)));
 			bcm43xx_phy_write(bcm, 0x0015, 0xEFB0);
 			udelay(10);
 			if (phy->connected)
-				bcm43xx_phy_write(bcm, 0x0812, 0x30B3); /* 0x30B3 is not a typo */
+				bcm43xx_phy_write(bcm, 0x0812,
+						  bcm43xx_get_812_value(bcm,
+						  LPD(1, 0, 0)));
 			bcm43xx_phy_write(bcm, 0x0015, 0xFFF0);
 			udelay(10);
 			tmp2 += bcm43xx_phy_read(bcm, 0x002D);
 			bcm43xx_phy_write(bcm, 0x0058, 0x0000);
 			if (phy->connected)
-				bcm43xx_phy_write(bcm, 0x0812, 0x30B2);
+				bcm43xx_phy_write(bcm, 0x0812,
+						  bcm43xx_get_812_value(bcm,
+						  LPD(1, 0, 1)));
 			bcm43xx_phy_write(bcm, 0x0015, 0xAFB0);
 		}
 		tmp2++;
@@ -1497,15 +1636,20 @@
 		bcm43xx_phy_write(bcm, 0x0030, backup[2]);
 		bcm43xx_write16(bcm, 0x03EC, backup[3]);
 	} else {
-		bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO,
-				(bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_RADIO) & 0x7FFF));
 		if (phy->connected) {
+			bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO,
+					(bcm43xx_read16(bcm,
+					BCM43xx_MMIO_PHY_RADIO) & 0x7FFF));
 			bcm43xx_phy_write(bcm, 0x0811, backup[4]);
 			bcm43xx_phy_write(bcm, 0x0812, backup[5]);
 			bcm43xx_phy_write(bcm, 0x0814, backup[6]);
 			bcm43xx_phy_write(bcm, 0x0815, backup[7]);
 			bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, backup[8]);
 			bcm43xx_phy_write(bcm, 0x0802, backup[9]);
+			if (phy->rev > 1) {
+				bcm43xx_phy_write(bcm, 0x080F, backup[19]);
+				bcm43xx_phy_write(bcm, 0x0810, backup[20]);
+			}
 		}
 	}
 	if (i >= 15)
diff --git a/drivers/net/wireless/hostap/Kconfig b/drivers/net/wireless/hostap/Kconfig
index 308f773..1fef331 100644
--- a/drivers/net/wireless/hostap/Kconfig
+++ b/drivers/net/wireless/hostap/Kconfig
@@ -1,6 +1,7 @@
 config HOSTAP
 	tristate "IEEE 802.11 for Host AP (Prism2/2.5/3 and WEP/TKIP/CCMP)"
-	depends on NET_RADIO
+	depends on WLAN_80211
+	select WIRELESS_EXT
 	select IEEE80211
 	select IEEE80211_CRYPT_WEP
 	---help---
diff --git a/drivers/net/wireless/hostap/hostap_80211_rx.c b/drivers/net/wireless/hostap/hostap_80211_rx.c
index 7e04dc9..cbedc9e 100644
--- a/drivers/net/wireless/hostap/hostap_80211_rx.c
+++ b/drivers/net/wireless/hostap/hostap_80211_rx.c
@@ -167,7 +167,7 @@
 
 	ret = skb->len - phdrlen;
 	skb->dev = dev;
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	skb_pull(skb, hdrlen);
 	if (prism_header)
 		skb_pull(skb, phdrlen);
@@ -933,12 +933,14 @@
 		if (frag == 0) {
 			/* copy first fragment (including full headers) into
 			 * beginning of the fragment cache skb */
-			memcpy(skb_put(frag_skb, flen), skb->data, flen);
+			skb_copy_from_linear_data(skb, skb_put(frag_skb, flen),
+						  flen);
 		} else {
 			/* append frame payload to the end of the fragment
 			 * cache skb */
-			memcpy(skb_put(frag_skb, flen), skb->data + hdrlen,
-			       flen);
+			skb_copy_from_linear_data_offset(skb, hdrlen,
+							 skb_put(frag_skb,
+								 flen), flen);
 		}
 		dev_kfree_skb(skb);
 		skb = NULL;
@@ -1044,8 +1046,9 @@
 	    skb->len >= ETH_HLEN + ETH_ALEN) {
 		/* Non-standard frame: get addr4 from its bogus location after
 		 * the payload */
-		memcpy(skb->data + ETH_ALEN,
-		       skb->data + skb->len - ETH_ALEN, ETH_ALEN);
+		skb_copy_from_linear_data_offset(skb, skb->len - ETH_ALEN,
+						 skb->data + ETH_ALEN,
+						 ETH_ALEN);
 		skb_trim(skb, skb->len - ETH_ALEN);
 	}
 
@@ -1073,17 +1076,17 @@
 
 	if (skb2 != NULL) {
 		/* send to wireless media */
-		skb2->protocol = __constant_htons(ETH_P_802_3);
-		skb2->mac.raw = skb2->nh.raw = skb2->data;
-		/* skb2->nh.raw = skb2->data + ETH_HLEN; */
 		skb2->dev = dev;
+		skb2->protocol = __constant_htons(ETH_P_802_3);
+		skb_reset_mac_header(skb2);
+		skb_reset_network_header(skb2);
+		/* skb2->network_header += ETH_HLEN; */
 		dev_queue_xmit(skb2);
 	}
 
 	if (skb) {
 		skb->protocol = eth_type_trans(skb, dev);
 		memset(skb->cb, 0, sizeof(skb->cb));
-		skb->dev = dev;
 		netif_rx(skb);
 	}
 
diff --git a/drivers/net/wireless/hostap/hostap_80211_tx.c b/drivers/net/wireless/hostap/hostap_80211_tx.c
index 4a5be70..246fac0 100644
--- a/drivers/net/wireless/hostap/hostap_80211_tx.c
+++ b/drivers/net/wireless/hostap/hostap_80211_tx.c
@@ -146,7 +146,8 @@
 			fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS;
 			/* From&To DS: Addr1 = RA, Addr2 = TA, Addr3 = DA,
 			 * Addr4 = SA */
-			memcpy(&hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
+			skb_copy_from_linear_data_offset(skb, ETH_ALEN,
+							 &hdr.addr4, ETH_ALEN);
 			hdr_len += ETH_ALEN;
 		} else {
 			/* bogus 4-addr format to workaround Prism2 station
@@ -159,7 +160,8 @@
 			/* SA from skb->data + ETH_ALEN will be added after
 			 * frame payload; use hdr.addr4 as a temporary buffer
 			 */
-			memcpy(&hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
+			skb_copy_from_linear_data_offset(skb, ETH_ALEN,
+							 &hdr.addr4, ETH_ALEN);
 			need_tailroom += ETH_ALEN;
 		}
 
@@ -174,24 +176,27 @@
 		else
 			memcpy(&hdr.addr1, local->bssid, ETH_ALEN);
 		memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN);
-		memcpy(&hdr.addr3, skb->data, ETH_ALEN);
+		skb_copy_from_linear_data(skb, &hdr.addr3, ETH_ALEN);
 	} else if (local->iw_mode == IW_MODE_MASTER && !to_assoc_ap) {
 		fc |= IEEE80211_FCTL_FROMDS;
 		/* From DS: Addr1 = DA, Addr2 = BSSID, Addr3 = SA */
-		memcpy(&hdr.addr1, skb->data, ETH_ALEN);
+		skb_copy_from_linear_data(skb, &hdr.addr1, ETH_ALEN);
 		memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN);
-		memcpy(&hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
+		skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr3,
+						 ETH_ALEN);
 	} else if (local->iw_mode == IW_MODE_INFRA || to_assoc_ap) {
 		fc |= IEEE80211_FCTL_TODS;
 		/* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */
 		memcpy(&hdr.addr1, to_assoc_ap ?
 		       local->assoc_ap_addr : local->bssid, ETH_ALEN);
-		memcpy(&hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
-		memcpy(&hdr.addr3, skb->data, ETH_ALEN);
+		skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr2,
+						 ETH_ALEN);
+		skb_copy_from_linear_data(skb, &hdr.addr3, ETH_ALEN);
 	} else if (local->iw_mode == IW_MODE_ADHOC) {
 		/* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */
-		memcpy(&hdr.addr1, skb->data, ETH_ALEN);
-		memcpy(&hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+		skb_copy_from_linear_data(skb, &hdr.addr1, ETH_ALEN);
+		skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr2,
+						 ETH_ALEN);
 		memcpy(&hdr.addr3, local->bssid, ETH_ALEN);
 	}
 
@@ -237,7 +242,7 @@
 	iface->stats.tx_packets++;
 	iface->stats.tx_bytes += skb->len;
 
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	meta = (struct hostap_skb_tx_data *) skb->cb;
 	memset(meta, 0, sizeof(*meta));
 	meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c
index efb8cf3..5b3abd5 100644
--- a/drivers/net/wireless/hostap/hostap_ap.c
+++ b/drivers/net/wireless/hostap/hostap_ap.c
@@ -1,8 +1,8 @@
 /*
  * Intersil Prism2 driver with Host AP (software access point) support
  * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
- * <jkmaline@cc.hut.fi>
- * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ * <j@w1.fi>
+ * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
  *
  * This file is to be included into hostap.c when S/W AP functionality is
  * compiled.
@@ -982,7 +982,8 @@
 	meta->tx_cb_idx = tx_cb_idx;
 
 	skb->dev = dev;
-	skb->mac.raw = skb->nh.raw = skb->data;
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
 	dev_queue_xmit(skb);
 }
 #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
@@ -1276,8 +1277,8 @@
 		return NULL;
 	}
 
-	memcpy(tmpbuf, skb->data + ap->crypt->extra_mpdu_prefix_len,
-	       WLAN_AUTH_CHALLENGE_LEN);
+	skb_copy_from_linear_data_offset(skb, ap->crypt->extra_mpdu_prefix_len,
+					 tmpbuf, WLAN_AUTH_CHALLENGE_LEN);
 	dev_kfree_skb(skb);
 
 	return tmpbuf;
diff --git a/drivers/net/wireless/hostap/hostap_common.h b/drivers/net/wireless/hostap/hostap_common.h
index 0162400..b31e6a0 100644
--- a/drivers/net/wireless/hostap/hostap_common.h
+++ b/drivers/net/wireless/hostap/hostap_common.h
@@ -368,9 +368,9 @@
 
 #define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024
 #define PRISM2_HOSTAPD_RID_HDR_LEN \
-((int) (&((struct prism2_hostapd_param *) 0)->u.rid.data))
+offsetof(struct prism2_hostapd_param, u.rid.data)
 #define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \
-((int) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data))
+offsetof(struct prism2_hostapd_param, u.generic_elem.data)
 
 /* Maximum length for algorithm names (-1 for nul termination) used in ioctl()
  */
diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c
index 8d8f4b9..ee1532b 100644
--- a/drivers/net/wireless/hostap/hostap_cs.c
+++ b/drivers/net/wireless/hostap/hostap_cs.c
@@ -22,7 +22,7 @@
 #include "hostap_wlan.h"
 
 
-static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)";
+static char *version = PRISM2_VERSION " (Jouni Malinen <j@w1.fi>)";
 static dev_info_t dev_info = "hostap_cs";
 
 MODULE_AUTHOR("Jouni Malinen");
@@ -838,6 +838,8 @@
 	PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005),
 	PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0010),
 	PCMCIA_DEVICE_MANF_CARD(0x0126, 0x0002),
+	PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0xd601, 0x0005, "ADLINK 345 CF",
+					 0x2d858104),
 	PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "INTERSIL",
 					 0x74c5e40d),
 	PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "Intersil",
@@ -848,6 +850,11 @@
 		"Intersil", "PRISM 2_5 PCMCIA ADAPTER",	"ISL37300P",
 		"Eval-RevA",
 		0x4b801a17, 0x6345a0bf, 0xc9049a39, 0xc23adc0e),
+	/* D-Link DWL-650 Rev. P1; manfid 0x000b, 0x7110 */
+	PCMCIA_DEVICE_PROD_ID1234(
+		"D-Link", "DWL-650 Wireless PC Card RevP", "ISL37101P-10",
+		"A3",
+		0x1a424a1c, 0x6ea57632, 0xdd97a26b, 0x56b21f52),
 	PCMCIA_DEVICE_PROD_ID123(
 		"Addtron", "AWP-100 Wireless PCMCIA", "Version 01.02",
 		0xe6ec52ce, 0x08649af2, 0x4b74baa0),
diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c
index 3079378..959887b 100644
--- a/drivers/net/wireless/hostap/hostap_hw.c
+++ b/drivers/net/wireless/hostap/hostap_hw.c
@@ -3,8 +3,8 @@
  * Intersil Prism2/2.5/3.
  *
  * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
- * <jkmaline@cc.hut.fi>
- * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ * <j@w1.fi>
+ * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
  *
  * 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
@@ -1838,13 +1838,14 @@
 
 	/* skb->data starts with txdesc->frame_control */
 	hdr_len = 24;
-	memcpy(&txdesc.frame_control, skb->data, hdr_len);
+	skb_copy_from_linear_data(skb, &txdesc.frame_control, hdr_len);
  	fc = le16_to_cpu(txdesc.frame_control);
 	if (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA &&
 	    (fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS) &&
 	    skb->len >= 30) {
 		/* Addr4 */
-		memcpy(txdesc.addr4, skb->data + hdr_len, ETH_ALEN);
+		skb_copy_from_linear_data_offset(skb, hdr_len, txdesc.addr4,
+						 ETH_ALEN);
 		hdr_len += ETH_ALEN;
 	}
 
@@ -2217,7 +2218,7 @@
 		memcpy(skb_put(skb, len), payload, len);
 
 	skb->dev = local->dev;
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 
 	cb->func(skb, ok, cb->data);
 }
diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/hostap/hostap_main.c
index 9077e6e..4743426 100644
--- a/drivers/net/wireless/hostap/hostap_main.c
+++ b/drivers/net/wireless/hostap/hostap_main.c
@@ -3,8 +3,8 @@
  * Intersil Prism2/2.5/3 - hostap.o module, common routines
  *
  * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
- * <jkmaline@cc.hut.fi>
- * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ * <j@w1.fi>
+ * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
  *
  * 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
@@ -590,20 +590,20 @@
 
 int hostap_80211_header_parse(struct sk_buff *skb, unsigned char *haddr)
 {
-	memcpy(haddr, skb->mac.raw + 10, ETH_ALEN); /* addr2 */
+	memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */
 	return ETH_ALEN;
 }
 
 
 int hostap_80211_prism_header_parse(struct sk_buff *skb, unsigned char *haddr)
 {
-	if (*(u32 *)skb->mac.raw == LWNG_CAP_DID_BASE) {
-		memcpy(haddr, skb->mac.raw +
-		       sizeof(struct linux_wlan_ng_prism_hdr) + 10,
+	const unsigned char *mac = skb_mac_header(skb);
+
+	if (*(u32 *)mac == LWNG_CAP_DID_BASE) {
+		memcpy(haddr, mac + sizeof(struct linux_wlan_ng_prism_hdr) + 10,
 		       ETH_ALEN); /* addr2 */
-	} else { /* (*(u32 *)skb->mac.raw == htonl(LWNG_CAPHDR_VERSION)) */
-		memcpy(haddr, skb->mac.raw +
-		       sizeof(struct linux_wlan_ng_cap_hdr) + 10,
+	} else { /* (*(u32 *)mac == htonl(LWNG_CAPHDR_VERSION)) */
+		memcpy(haddr, mac + sizeof(struct linux_wlan_ng_cap_hdr) + 10,
 		       ETH_ALEN); /* addr2 */
 	}
 	return ETH_ALEN;
@@ -1063,7 +1063,8 @@
 	meta->iface = netdev_priv(dev);
 
 	skb->dev = dev;
-	skb->mac.raw = skb->nh.raw = skb->data;
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
 	dev_queue_xmit(skb);
 
 	return 0;
diff --git a/drivers/net/wireless/hostap/hostap_pci.c b/drivers/net/wireless/hostap/hostap_pci.c
index c4f6020..db4899e 100644
--- a/drivers/net/wireless/hostap/hostap_pci.c
+++ b/drivers/net/wireless/hostap/hostap_pci.c
@@ -20,7 +20,7 @@
 #include "hostap_wlan.h"
 
 
-static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)";
+static char *version = PRISM2_VERSION " (Jouni Malinen <j@w1.fi>)";
 static char *dev_info = "hostap_pci";
 
 
diff --git a/drivers/net/wireless/hostap/hostap_plx.c b/drivers/net/wireless/hostap/hostap_plx.c
index e235e06..f0fd5ec 100644
--- a/drivers/net/wireless/hostap/hostap_plx.c
+++ b/drivers/net/wireless/hostap/hostap_plx.c
@@ -23,7 +23,7 @@
 #include "hostap_wlan.h"
 
 
-static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)";
+static char *version = PRISM2_VERSION " (Jouni Malinen <j@w1.fi>)";
 static char *dev_info = "hostap_plx";
 
 
diff --git a/drivers/net/wireless/ipw2100.c b/drivers/net/wireless/ipw2100.c
index ad6e4a4..d51daf8 100644
--- a/drivers/net/wireless/ipw2100.c
+++ b/drivers/net/wireless/ipw2100.c
@@ -28,8 +28,8 @@
 
   Portions of this file are based on the Host AP project,
   Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
-    <jkmaline@cc.hut.fi>
-  Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+    <j@w1.fi>
+  Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
 
   Portions of ipw2100_mod_firmware_load, ipw2100_do_mod_firmware_load, and
   ipw2100_fw_load are loosely based on drivers/sound/sound_firmware.c
@@ -2416,8 +2416,9 @@
 #ifdef IPW2100_RX_DEBUG
 	/* Make a copy of the frame so we can dump it to the logs if
 	 * ieee80211_rx fails */
-	memcpy(packet_data, packet->skb->data,
-	       min_t(u32, status->frame_size, IPW_RX_NIC_BUFFER_LENGTH));
+	skb_copy_from_linear_data(packet->skb, packet_data,
+				  min_t(u32, status->frame_size,
+					     IPW_RX_NIC_BUFFER_LENGTH));
 #endif
 
 	if (!ieee80211_rx(priv->ieee, packet->skb, stats)) {
diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c
index c878a2f..7cb2052 100644
--- a/drivers/net/wireless/ipw2200.c
+++ b/drivers/net/wireless/ipw2200.c
@@ -1847,6 +1847,52 @@
 static DEVICE_ATTR(net_stats, S_IWUSR | S_IRUGO,
 		   show_net_stats, store_net_stats);
 
+static ssize_t show_channels(struct device *d,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	struct ipw_priv *priv = dev_get_drvdata(d);
+	const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
+	int len = 0, i;
+
+	len = sprintf(&buf[len],
+		      "Displaying %d channels in 2.4Ghz band "
+		      "(802.11bg):\n", geo->bg_channels);
+
+	for (i = 0; i < geo->bg_channels; i++) {
+		len += sprintf(&buf[len], "%d: BSS%s%s, %s, Band %s.\n",
+			       geo->bg[i].channel,
+			       geo->bg[i].flags & IEEE80211_CH_RADAR_DETECT ?
+			       " (radar spectrum)" : "",
+			       ((geo->bg[i].flags & IEEE80211_CH_NO_IBSS) ||
+				(geo->bg[i].flags & IEEE80211_CH_RADAR_DETECT))
+			       ? "" : ", IBSS",
+			       geo->bg[i].flags & IEEE80211_CH_PASSIVE_ONLY ?
+			       "passive only" : "active/passive",
+			       geo->bg[i].flags & IEEE80211_CH_B_ONLY ?
+			       "B" : "B/G");
+	}
+
+	len += sprintf(&buf[len],
+		       "Displaying %d channels in 5.2Ghz band "
+		       "(802.11a):\n", geo->a_channels);
+	for (i = 0; i < geo->a_channels; i++) {
+		len += sprintf(&buf[len], "%d: BSS%s%s, %s.\n",
+			       geo->a[i].channel,
+			       geo->a[i].flags & IEEE80211_CH_RADAR_DETECT ?
+			       " (radar spectrum)" : "",
+			       ((geo->a[i].flags & IEEE80211_CH_NO_IBSS) ||
+				(geo->a[i].flags & IEEE80211_CH_RADAR_DETECT))
+			       ? "" : ", IBSS",
+			       geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY ?
+			       "passive only" : "active/passive");
+	}
+
+	return len;
+}
+
+static DEVICE_ATTR(channels, S_IRUSR, show_channels, NULL);
+
 static void notify_wx_assoc_event(struct ipw_priv *priv)
 {
 	union iwreq_data wrqu;
@@ -8133,7 +8179,7 @@
 		skb->dev = priv->ieee->dev;
 
 		/* Point raw at the ieee80211_stats */
-		skb->mac.raw = skb->data;
+		skb_reset_mac_header(skb);
 
 		skb->pkt_type = PACKET_OTHERHOST;
 		skb->protocol = __constant_htons(ETH_P_80211_STATS);
@@ -10355,7 +10401,7 @@
 
 		rt_hdr->it_len = dst->len;
 
-		memcpy(skb_put(dst, len), src->data, len);
+		skb_copy_from_linear_data(src, skb_put(dst, len), len);
 
 		if (!ieee80211_rx(priv->prom_priv->ieee, dst, &dummystats))
 			dev_kfree_skb_any(dst);
@@ -11383,6 +11429,7 @@
 	&dev_attr_led.attr,
 	&dev_attr_speed_scan.attr,
 	&dev_attr_net_stats.attr,
+	&dev_attr_channels.attr,
 #ifdef CONFIG_IPW2200_PROMISCUOUS
 	&dev_attr_rtap_iface.attr,
 	&dev_attr_rtap_filter.attr,
diff --git a/drivers/net/wireless/libertas/11d.c b/drivers/net/wireless/libertas/11d.c
new file mode 100644
index 0000000..e0ecc4d
--- /dev/null
+++ b/drivers/net/wireless/libertas/11d.c
@@ -0,0 +1,754 @@
+/**
+  * This file contains functions for 802.11D.
+  */
+#include <linux/ctype.h>
+#include <linux/kernel.h>
+#include <linux/wireless.h>
+
+#include "host.h"
+#include "decl.h"
+#include "11d.h"
+#include "dev.h"
+#include "wext.h"
+
+#define TX_PWR_DEFAULT	10
+
+static struct region_code_mapping region_code_mapping[] = {
+	{"US ", 0x10},		/* US FCC      */
+	{"CA ", 0x10},		/* IC Canada   */
+	{"SG ", 0x10},		/* Singapore   */
+	{"EU ", 0x30},		/* ETSI        */
+	{"AU ", 0x30},		/* Australia   */
+	{"KR ", 0x30},		/* Republic Of Korea */
+	{"ES ", 0x31},		/* Spain       */
+	{"FR ", 0x32},		/* France      */
+	{"JP ", 0x40},		/* Japan       */
+};
+
+/* Following 2 structure defines the supported channels */
+static struct chan_freq_power channel_freq_power_UN_BG[] = {
+	{1, 2412, TX_PWR_DEFAULT},
+	{2, 2417, TX_PWR_DEFAULT},
+	{3, 2422, TX_PWR_DEFAULT},
+	{4, 2427, TX_PWR_DEFAULT},
+	{5, 2432, TX_PWR_DEFAULT},
+	{6, 2437, TX_PWR_DEFAULT},
+	{7, 2442, TX_PWR_DEFAULT},
+	{8, 2447, TX_PWR_DEFAULT},
+	{9, 2452, TX_PWR_DEFAULT},
+	{10, 2457, TX_PWR_DEFAULT},
+	{11, 2462, TX_PWR_DEFAULT},
+	{12, 2467, TX_PWR_DEFAULT},
+	{13, 2472, TX_PWR_DEFAULT},
+	{14, 2484, TX_PWR_DEFAULT}
+};
+
+static u8 wlan_region_2_code(u8 * region)
+{
+	u8 i;
+	u8 size = sizeof(region_code_mapping)/
+		  sizeof(struct region_code_mapping);
+
+	for (i = 0; region[i] && i < COUNTRY_CODE_LEN; i++)
+		region[i] = toupper(region[i]);
+
+	for (i = 0; i < size; i++) {
+		if (!memcmp(region, region_code_mapping[i].region,
+			    COUNTRY_CODE_LEN))
+			return (region_code_mapping[i].code);
+	}
+
+	/* default is US */
+	return (region_code_mapping[0].code);
+}
+
+static u8 *wlan_code_2_region(u8 code)
+{
+	u8 i;
+	u8 size = sizeof(region_code_mapping)
+		  / sizeof(struct region_code_mapping);
+	for (i = 0; i < size; i++) {
+		if (region_code_mapping[i].code == code)
+			return (region_code_mapping[i].region);
+	}
+	/* default is US */
+	return (region_code_mapping[0].region);
+}
+
+/**
+ *  @brief This function finds the nrchan-th chan after the firstchan
+ *  @param band       band
+ *  @param firstchan  first channel number
+ *  @param nrchan   number of channels
+ *  @return 	      the nrchan-th chan number
+*/
+static u8 wlan_get_chan_11d(u8 band, u8 firstchan, u8 nrchan, u8 * chan)
+/*find the nrchan-th chan after the firstchan*/
+{
+	u8 i;
+	struct chan_freq_power *cfp;
+	u8 cfp_no;
+
+	cfp = channel_freq_power_UN_BG;
+	cfp_no = sizeof(channel_freq_power_UN_BG) /
+	    sizeof(struct chan_freq_power);
+
+	for (i = 0; i < cfp_no; i++) {
+		if ((cfp + i)->channel == firstchan) {
+			lbs_pr_debug(1, "firstchan found\n");
+			break;
+		}
+	}
+
+	if (i < cfp_no) {
+		/*if beyond the boundary */
+		if (i + nrchan < cfp_no) {
+			*chan = (cfp + i + nrchan)->channel;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ *  @brief This function Checks if chan txpwr is learned from AP/IBSS
+ *  @param chan                 chan number
+ *  @param parsed_region_chan   pointer to parsed_region_chan_11d
+ *  @return 	                TRUE; FALSE
+*/
+static u8 wlan_channel_known_11d(u8 chan,
+			  struct parsed_region_chan_11d * parsed_region_chan)
+{
+	struct chan_power_11d *chanpwr = parsed_region_chan->chanpwr;
+	u8 nr_chan = parsed_region_chan->nr_chan;
+	u8 i = 0;
+
+	lbs_dbg_hex("11D:parsed_region_chan:", (char *)chanpwr,
+		sizeof(struct chan_power_11d) * nr_chan);
+
+	for (i = 0; i < nr_chan; i++) {
+		if (chan == chanpwr[i].chan) {
+			lbs_pr_debug(1, "11D: Found Chan:%d\n", chan);
+			return 1;
+		}
+	}
+
+	lbs_pr_debug(1, "11D: Not Find Chan:%d\n", chan);
+	return 0;
+}
+
+u32 libertas_chan_2_freq(u8 chan, u8 band)
+{
+	struct chan_freq_power *cf;
+	u16 cnt;
+	u16 i;
+	u32 freq = 0;
+
+	cf = channel_freq_power_UN_BG;
+	cnt =
+	    sizeof(channel_freq_power_UN_BG) /
+	    sizeof(struct chan_freq_power);
+
+	for (i = 0; i < cnt; i++) {
+		if (chan == cf[i].channel)
+			freq = cf[i].freq;
+	}
+
+	return freq;
+}
+
+static int generate_domain_info_11d(struct parsed_region_chan_11d
+				  *parsed_region_chan,
+				  struct wlan_802_11d_domain_reg * domaininfo)
+{
+	u8 nr_subband = 0;
+
+	u8 nr_chan = parsed_region_chan->nr_chan;
+	u8 nr_parsedchan = 0;
+
+	u8 firstchan = 0, nextchan = 0, maxpwr = 0;
+
+	u8 i, flag = 0;
+
+	memcpy(domaininfo->countrycode, parsed_region_chan->countrycode,
+	       COUNTRY_CODE_LEN);
+
+	lbs_pr_debug(1, "11D:nrchan=%d\n", nr_chan);
+	lbs_dbg_hex("11D:parsed_region_chan:", (char *)parsed_region_chan,
+		sizeof(struct parsed_region_chan_11d));
+
+	for (i = 0; i < nr_chan; i++) {
+		if (!flag) {
+			flag = 1;
+			nextchan = firstchan =
+			    parsed_region_chan->chanpwr[i].chan;
+			maxpwr = parsed_region_chan->chanpwr[i].pwr;
+			nr_parsedchan = 1;
+			continue;
+		}
+
+		if (parsed_region_chan->chanpwr[i].chan == nextchan + 1 &&
+		    parsed_region_chan->chanpwr[i].pwr == maxpwr) {
+			nextchan++;
+			nr_parsedchan++;
+		} else {
+			domaininfo->subband[nr_subband].firstchan = firstchan;
+			domaininfo->subband[nr_subband].nrchan =
+			    nr_parsedchan;
+			domaininfo->subband[nr_subband].maxtxpwr = maxpwr;
+			nr_subband++;
+			nextchan = firstchan =
+			    parsed_region_chan->chanpwr[i].chan;
+			maxpwr = parsed_region_chan->chanpwr[i].pwr;
+		}
+	}
+
+	if (flag) {
+		domaininfo->subband[nr_subband].firstchan = firstchan;
+		domaininfo->subband[nr_subband].nrchan = nr_parsedchan;
+		domaininfo->subband[nr_subband].maxtxpwr = maxpwr;
+		nr_subband++;
+	}
+	domaininfo->nr_subband = nr_subband;
+
+	lbs_pr_debug(1, "nr_subband=%x\n", domaininfo->nr_subband);
+	lbs_dbg_hex("11D:domaininfo:", (char *)domaininfo,
+		COUNTRY_CODE_LEN + 1 +
+		sizeof(struct ieeetypes_subbandset) * nr_subband);
+	return 0;
+}
+
+/**
+ *  @brief This function generates parsed_region_chan from Domain Info learned from AP/IBSS
+ *  @param region_chan          pointer to struct region_channel
+ *  @param *parsed_region_chan  pointer to parsed_region_chan_11d
+ *  @return 	                N/A
+*/
+static void wlan_generate_parsed_region_chan_11d(struct region_channel * region_chan,
+					  struct parsed_region_chan_11d *
+					  parsed_region_chan)
+{
+	u8 i;
+	struct chan_freq_power *cfp;
+
+	if (region_chan == NULL) {
+		lbs_pr_debug(1, "11D: region_chan is NULL\n");
+		return;
+	}
+
+	cfp = region_chan->CFP;
+	if (cfp == NULL) {
+		lbs_pr_debug(1, "11D: cfp equal NULL \n");
+		return;
+	}
+
+	parsed_region_chan->band = region_chan->band;
+	parsed_region_chan->region = region_chan->region;
+	memcpy(parsed_region_chan->countrycode,
+	       wlan_code_2_region(region_chan->region), COUNTRY_CODE_LEN);
+
+	lbs_pr_debug(1, "11D: region[0x%x] band[%d]\n", parsed_region_chan->region,
+	       parsed_region_chan->band);
+
+	for (i = 0; i < region_chan->nrcfp; i++, cfp++) {
+		parsed_region_chan->chanpwr[i].chan = cfp->channel;
+		parsed_region_chan->chanpwr[i].pwr = cfp->maxtxpower;
+		lbs_pr_debug(1, "11D: Chan[%d] Pwr[%d]\n",
+		       parsed_region_chan->chanpwr[i].chan,
+		       parsed_region_chan->chanpwr[i].pwr);
+	}
+	parsed_region_chan->nr_chan = region_chan->nrcfp;
+
+	lbs_pr_debug(1, "11D: nrchan[%d]\n", parsed_region_chan->nr_chan);
+
+	return;
+}
+
+/**
+ *  @brief generate parsed_region_chan from Domain Info learned from AP/IBSS
+ *  @param region               region ID
+ *  @param band                 band
+ *  @param chan                 chan
+ *  @return 	                TRUE;FALSE
+*/
+static u8 wlan_region_chan_supported_11d(u8 region, u8 band, u8 chan)
+{
+	struct chan_freq_power *cfp;
+	int cfp_no;
+	u8 idx;
+
+	ENTER();
+
+	cfp = libertas_get_region_cfp_table(region, band, &cfp_no);
+	if (cfp == NULL)
+		return 0;
+
+	for (idx = 0; idx < cfp_no; idx++) {
+		if (chan == (cfp + idx)->channel) {
+			/* If Mrvl Chip Supported? */
+			if ((cfp + idx)->unsupported) {
+				return 0;
+			} else {
+				return 1;
+			}
+		}
+	}
+
+	/*chan is not in the region table */
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief This function checks if chan txpwr is learned from AP/IBSS
+ *  @param chan                 chan number
+ *  @param parsed_region_chan   pointer to parsed_region_chan_11d
+ *  @return 	                0
+*/
+static int parse_domain_info_11d(struct ieeetypes_countryinfofullset*
+				 countryinfo,
+				 u8 band,
+				 struct parsed_region_chan_11d *
+				 parsed_region_chan)
+{
+	u8 nr_subband, nrchan;
+	u8 lastchan, firstchan;
+	u8 region;
+	u8 curchan = 0;
+
+	u8 idx = 0;		/*chan index in parsed_region_chan */
+
+	u8 j, i;
+
+	ENTER();
+
+	/*validation Rules:
+	   1. valid region Code
+	   2. First Chan increment
+	   3. channel range no overlap
+	   4. channel is valid?
+	   5. channel is supported by region?
+	   6. Others
+	 */
+
+	lbs_dbg_hex("CountryInfo:", (u8 *) countryinfo, 30);
+
+	if ((*(countryinfo->countrycode)) == 0
+	    || (countryinfo->len <= COUNTRY_CODE_LEN)) {
+		/* No region Info or Wrong region info: treat as No 11D info */
+		LEAVE();
+		return 0;
+	}
+
+	/*Step1: check region_code */
+	parsed_region_chan->region = region =
+	    wlan_region_2_code(countryinfo->countrycode);
+
+	lbs_pr_debug(1, "regioncode=%x\n", (u8) parsed_region_chan->region);
+	lbs_dbg_hex("CountryCode:", (char *)countryinfo->countrycode,
+		COUNTRY_CODE_LEN);
+
+	parsed_region_chan->band = band;
+
+	memcpy(parsed_region_chan->countrycode, countryinfo->countrycode,
+	       COUNTRY_CODE_LEN);
+
+	nr_subband = (countryinfo->len - COUNTRY_CODE_LEN) /
+	    sizeof(struct ieeetypes_subbandset);
+
+	for (j = 0, lastchan = 0; j < nr_subband; j++) {
+
+		if (countryinfo->subband[j].firstchan <= lastchan) {
+			/*Step2&3. Check First Chan Num increment and no overlap */
+			lbs_pr_debug(1, "11D: Chan[%d>%d] Overlap\n",
+			       countryinfo->subband[j].firstchan, lastchan);
+			continue;
+		}
+
+		firstchan = countryinfo->subband[j].firstchan;
+		nrchan = countryinfo->subband[j].nrchan;
+
+		for (i = 0; idx < MAX_NO_OF_CHAN && i < nrchan; i++) {
+			/*step4: channel is supported? */
+
+			if (!wlan_get_chan_11d(band, firstchan, i, &curchan)) {
+				/* Chan is not found in UN table */
+				lbs_pr_debug(1, "chan is not supported: %d \n", i);
+				break;
+			}
+
+			lastchan = curchan;
+
+			if (wlan_region_chan_supported_11d
+			    (region, band, curchan)) {
+				/*step5: Check if curchan is supported by mrvl in region */
+				parsed_region_chan->chanpwr[idx].chan = curchan;
+				parsed_region_chan->chanpwr[idx].pwr =
+				    countryinfo->subband[j].maxtxpwr;
+				idx++;
+			} else {
+				/*not supported and ignore the chan */
+				lbs_pr_debug(1,
+				       "11D:i[%d] chan[%d] unsupported in region[%x] band[%d]\n",
+				       i, curchan, region, band);
+			}
+		}
+
+		/*Step6: Add other checking if any */
+
+	}
+
+	parsed_region_chan->nr_chan = idx;
+
+	lbs_pr_debug(1, "nrchan=%x\n", parsed_region_chan->nr_chan);
+	lbs_dbg_hex("11D:parsed_region_chan:", (u8 *) parsed_region_chan,
+		2 + COUNTRY_CODE_LEN + sizeof(struct parsed_region_chan_11d) * idx);
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief This function calculates the scan type for channels
+ *  @param chan                 chan number
+ *  @param parsed_region_chan   pointer to parsed_region_chan_11d
+ *  @return 	                PASSIVE if chan is unknown; ACTIVE if chan is known
+*/
+u8 libertas_get_scan_type_11d(u8 chan,
+			  struct parsed_region_chan_11d * parsed_region_chan)
+{
+	u8 scan_type = cmd_scan_type_passive;
+
+	ENTER();
+
+	if (wlan_channel_known_11d(chan, parsed_region_chan)) {
+		lbs_pr_debug(1, "11D: Found and do Active Scan\n");
+		scan_type = cmd_scan_type_active;
+	} else {
+		lbs_pr_debug(1, "11D: Not Find and do Passive Scan\n");
+	}
+
+	LEAVE();
+	return scan_type;
+
+}
+
+void libertas_init_11d(wlan_private * priv)
+{
+	priv->adapter->enable11d = 0;
+	memset(&(priv->adapter->parsed_region_chan), 0,
+	       sizeof(struct parsed_region_chan_11d));
+	return;
+}
+
+static int wlan_enable_11d(wlan_private * priv, u8 flag)
+{
+	int ret;
+
+	priv->adapter->enable11d = flag;
+
+	/* send cmd to FW to enable/disable 11D function in FW */
+	ret = libertas_prepare_and_send_command(priv,
+				    cmd_802_11_snmp_mib,
+				    cmd_act_set,
+				    cmd_option_waitforrsp,
+				    OID_802_11D_ENABLE,
+				    &priv->adapter->enable11d);
+	if (ret)
+		lbs_pr_debug(1, "11D: Fail to enable 11D \n");
+
+	return 0;
+}
+
+/**
+ *  @brief This function sets DOMAIN INFO to FW
+ *  @param priv       pointer to wlan_private
+ *  @return 	      0; -1
+*/
+static int set_domain_info_11d(wlan_private * priv)
+{
+	int ret;
+
+	if (!priv->adapter->enable11d) {
+		lbs_pr_debug(1, "11D: dnld domain Info with 11d disabled\n");
+		return 0;
+	}
+
+	ret = libertas_prepare_and_send_command(priv, cmd_802_11d_domain_info,
+				    cmd_act_set,
+				    cmd_option_waitforrsp, 0, NULL);
+	if (ret)
+		lbs_pr_debug(1, "11D: Fail to dnld domain Info\n");
+
+	return ret;
+}
+
+/**
+ *  @brief This function setups scan channels
+ *  @param priv       pointer to wlan_private
+ *  @param band       band
+ *  @return 	      0
+*/
+int libertas_set_universaltable(wlan_private * priv, u8 band)
+{
+	wlan_adapter *adapter = priv->adapter;
+	u16 size = sizeof(struct chan_freq_power);
+	u16 i = 0;
+
+	memset(adapter->universal_channel, 0,
+	       sizeof(adapter->universal_channel));
+
+	adapter->universal_channel[i].nrcfp =
+	    sizeof(channel_freq_power_UN_BG) / size;
+	lbs_pr_debug(1, "11D: BG-band nrcfp=%d\n",
+	       adapter->universal_channel[i].nrcfp);
+
+	adapter->universal_channel[i].CFP = channel_freq_power_UN_BG;
+	adapter->universal_channel[i].valid = 1;
+	adapter->universal_channel[i].region = UNIVERSAL_REGION_CODE;
+	adapter->universal_channel[i].band = band;
+	i++;
+
+	return 0;
+}
+
+/**
+ *  @brief This function implements command CMD_802_11D_DOMAIN_INFO
+ *  @param priv       pointer to wlan_private
+ *  @param cmd        pointer to cmd buffer
+ *  @param cmdno      cmd ID
+ *  @param cmdOption  cmd action
+ *  @return 	      0
+*/
+int libertas_cmd_802_11d_domain_info(wlan_private * priv,
+				 struct cmd_ds_command *cmd, u16 cmdno,
+				 u16 cmdoption)
+{
+	struct cmd_ds_802_11d_domain_info *pdomaininfo =
+	    &cmd->params.domaininfo;
+	struct mrvlietypes_domainparamset *domain = &pdomaininfo->domain;
+	wlan_adapter *adapter = priv->adapter;
+	u8 nr_subband = adapter->domainreg.nr_subband;
+
+	ENTER();
+
+	lbs_pr_debug(1, "nr_subband=%x\n", nr_subband);
+
+	cmd->command = cpu_to_le16(cmdno);
+	pdomaininfo->action = cpu_to_le16(cmdoption);
+	if (cmdoption == cmd_act_get) {
+		cmd->size =
+		    cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN);
+		lbs_dbg_hex("11D: 802_11D_DOMAIN_INFO:", (u8 *) cmd,
+			(int)(cmd->size));
+		LEAVE();
+		return 0;
+	}
+
+	domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN);
+	memcpy(domain->countrycode, adapter->domainreg.countrycode,
+	       sizeof(domain->countrycode));
+
+	domain->header.len =
+	    cpu_to_le16(nr_subband * sizeof(struct ieeetypes_subbandset) +
+			     sizeof(domain->countrycode));
+
+	if (nr_subband) {
+		memcpy(domain->subband, adapter->domainreg.subband,
+		       nr_subband * sizeof(struct ieeetypes_subbandset));
+
+		cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) +
+					     domain->header.len +
+					     sizeof(struct mrvlietypesheader) +
+					     S_DS_GEN);
+	} else {
+		cmd->size =
+		    cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN);
+	}
+
+	lbs_dbg_hex("11D:802_11D_DOMAIN_INFO:", (u8 *) cmd, (int)(cmd->size));
+
+	LEAVE();
+
+	return 0;
+}
+
+/**
+ *  @brief This function implements private cmd: enable/disable 11D
+ *  @param priv    pointer to wlan_private
+ *  @param wrq     pointer to user data
+ *  @return 	   0 or -1
+ */
+int libertas_cmd_enable_11d(wlan_private * priv, struct iwreq *wrq)
+{
+	int data = 0;
+	int *val;
+
+	ENTER();
+	data = SUBCMD_DATA(wrq);
+
+	lbs_pr_debug(1, "enable 11D: %s\n",
+	       (data == 1) ? "enable" : "Disable");
+
+	wlan_enable_11d(priv, data);
+	val = (int *)wrq->u.name;
+	*val = priv->adapter->enable11d;
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief This function parses countryinfo from AP and download country info to FW
+ *  @param priv    pointer to wlan_private
+ *  @param resp    pointer to command response buffer
+ *  @return 	   0; -1
+ */
+int libertas_ret_802_11d_domain_info(wlan_private * priv,
+				 struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11d_domain_info
+	*domaininfo = &resp->params.domaininforesp;
+	struct mrvlietypes_domainparamset *domain = &domaininfo->domain;
+	u16 action = le16_to_cpu(domaininfo->action);
+	s16 ret = 0;
+	u8 nr_subband = 0;
+
+	ENTER();
+
+	lbs_dbg_hex("11D DOMAIN Info Rsp Data:", (u8 *) resp,
+		(int)le16_to_cpu(resp->size));
+
+	nr_subband = (domain->header.len - 3) / sizeof(struct ieeetypes_subbandset);
+	/* countrycode 3 bytes */
+
+	lbs_pr_debug(1, "11D Domain Info Resp: nr_subband=%d\n", nr_subband);
+
+	if (nr_subband > MRVDRV_MAX_SUBBAND_802_11D) {
+		lbs_pr_debug(1, "Invalid Numrer of Subband returned!!\n");
+		return -1;
+	}
+
+	switch (action) {
+	case cmd_act_set:	/*Proc Set action */
+		break;
+
+	case cmd_act_get:
+		break;
+	default:
+		lbs_pr_debug(1, "Invalid action:%d\n", domaininfo->action);
+		ret = -1;
+		break;
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function parses countryinfo from AP and download country info to FW
+ *  @param priv    pointer to wlan_private
+ *  @return 	   0; -1
+ */
+int libertas_parse_dnld_countryinfo_11d(wlan_private * priv)
+{
+	int ret;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+	if (priv->adapter->enable11d) {
+		memset(&adapter->parsed_region_chan, 0,
+		       sizeof(struct parsed_region_chan_11d));
+		ret = parse_domain_info_11d(&adapter->pattemptedbssdesc->
+					       countryinfo, 0,
+					       &adapter->parsed_region_chan);
+
+		if (ret == -1) {
+			lbs_pr_debug(1, "11D: Err Parse domain_info from AP..\n");
+			LEAVE();
+			return ret;
+		}
+
+		memset(&adapter->domainreg, 0,
+		       sizeof(struct wlan_802_11d_domain_reg));
+		generate_domain_info_11d(&adapter->parsed_region_chan,
+				      &adapter->domainreg);
+
+		ret = set_domain_info_11d(priv);
+
+		if (ret) {
+			lbs_pr_debug(1, "11D: Err set domainInfo to FW\n");
+			LEAVE();
+			return ret;
+		}
+	}
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief This function generates 11D info from user specified regioncode and download to FW
+ *  @param priv    pointer to wlan_private
+ *  @return 	   0; -1
+ */
+int libertas_create_dnld_countryinfo_11d(wlan_private * priv)
+{
+	int ret;
+	wlan_adapter *adapter = priv->adapter;
+	struct region_channel *region_chan;
+	u8 j;
+
+	ENTER();
+	lbs_pr_debug(1, "11D:curbssparams.band[%d]\n", adapter->curbssparams.band);
+
+	if (priv->adapter->enable11d) {
+		/* update parsed_region_chan_11; dnld domaininf to FW */
+
+		for (j = 0; j < sizeof(adapter->region_channel) /
+		     sizeof(adapter->region_channel[0]); j++) {
+			region_chan = &adapter->region_channel[j];
+
+			lbs_pr_debug(1, "11D:[%d] region_chan->band[%d]\n", j,
+			       region_chan->band);
+
+			if (!region_chan || !region_chan->valid
+			    || !region_chan->CFP)
+				continue;
+			if (region_chan->band != adapter->curbssparams.band)
+				continue;
+			break;
+		}
+
+		if (j >= sizeof(adapter->region_channel) /
+		    sizeof(adapter->region_channel[0])) {
+			lbs_pr_debug(1, "11D:region_chan not found. band[%d]\n",
+			       adapter->curbssparams.band);
+			LEAVE();
+			return -1;
+		}
+
+		memset(&adapter->parsed_region_chan, 0,
+		       sizeof(struct parsed_region_chan_11d));
+		wlan_generate_parsed_region_chan_11d(region_chan,
+						     &adapter->
+						     parsed_region_chan);
+
+		memset(&adapter->domainreg, 0,
+		       sizeof(struct wlan_802_11d_domain_reg));
+		generate_domain_info_11d(&adapter->parsed_region_chan,
+					 &adapter->domainreg);
+
+		ret = set_domain_info_11d(priv);
+
+		if (ret) {
+			lbs_pr_debug(1, "11D: Err set domainInfo to FW\n");
+			LEAVE();
+			return ret;
+		}
+
+	}
+
+	LEAVE();
+	return 0;
+}
diff --git a/drivers/net/wireless/libertas/11d.h b/drivers/net/wireless/libertas/11d.h
new file mode 100644
index 0000000..db2ebea9
--- /dev/null
+++ b/drivers/net/wireless/libertas/11d.h
@@ -0,0 +1,105 @@
+/**
+  * This header file contains data structures and
+  * function declarations of 802.11d
+  */
+#ifndef _WLAN_11D_
+#define _WLAN_11D_
+
+#include "types.h"
+#include "defs.h"
+
+#define UNIVERSAL_REGION_CODE			0xff
+
+/** (Beaconsize(256)-5(IEId,len,contrystr(3))/3(FirstChan,NoOfChan,MaxPwr)
+ */
+#define MRVDRV_MAX_SUBBAND_802_11D		83
+
+#define COUNTRY_CODE_LEN			3
+#define MAX_NO_OF_CHAN 				40
+
+struct cmd_ds_command;
+
+/** Data structure for Country IE*/
+struct ieeetypes_subbandset {
+	u8 firstchan;
+	u8 nrchan;
+	u8 maxtxpwr;
+} __attribute__ ((packed));
+
+struct ieeetypes_countryinfoset {
+	u8 element_id;
+	u8 len;
+	u8 countrycode[COUNTRY_CODE_LEN];
+	struct ieeetypes_subbandset subband[1];
+};
+
+struct ieeetypes_countryinfofullset {
+	u8 element_id;
+	u8 len;
+	u8 countrycode[COUNTRY_CODE_LEN];
+	struct ieeetypes_subbandset subband[MRVDRV_MAX_SUBBAND_802_11D];
+} __attribute__ ((packed));
+
+struct mrvlietypes_domainparamset {
+	struct mrvlietypesheader header;
+	u8 countrycode[COUNTRY_CODE_LEN];
+	struct ieeetypes_subbandset subband[1];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11d_domain_info {
+	u16 action;
+	struct mrvlietypes_domainparamset domain;
+} __attribute__ ((packed));
+
+/** domain regulatory information */
+struct wlan_802_11d_domain_reg {
+	/** country Code*/
+	u8 countrycode[COUNTRY_CODE_LEN];
+	/** No. of subband*/
+	u8 nr_subband;
+	struct ieeetypes_subbandset subband[MRVDRV_MAX_SUBBAND_802_11D];
+};
+
+struct chan_power_11d {
+	u8 chan;
+	u8 pwr;
+} __attribute__ ((packed));
+
+struct parsed_region_chan_11d {
+	u8 band;
+	u8 region;
+	s8 countrycode[COUNTRY_CODE_LEN];
+	struct chan_power_11d chanpwr[MAX_NO_OF_CHAN];
+	u8 nr_chan;
+} __attribute__ ((packed));
+
+struct region_code_mapping {
+	u8 region[COUNTRY_CODE_LEN];
+	u8 code;
+};
+
+u8 libertas_get_scan_type_11d(u8 chan,
+			  struct parsed_region_chan_11d *parsed_region_chan);
+
+u32 libertas_chan_2_freq(u8 chan, u8 band);
+
+enum state_11d libertas_get_state_11d(wlan_private * priv);
+
+void libertas_init_11d(wlan_private * priv);
+
+int libertas_set_universaltable(wlan_private * priv, u8 band);
+
+int libertas_cmd_802_11d_domain_info(wlan_private * priv,
+				 struct cmd_ds_command *cmd, u16 cmdno,
+				 u16 cmdOption);
+
+int libertas_cmd_enable_11d(wlan_private * priv, struct iwreq *wrq);
+
+int libertas_ret_802_11d_domain_info(wlan_private * priv,
+				 struct cmd_ds_command *resp);
+
+int libertas_parse_dnld_countryinfo_11d(wlan_private * priv);
+
+int libertas_create_dnld_countryinfo_11d(wlan_private * priv);
+
+#endif				/* _WLAN_11D_ */
diff --git a/drivers/net/wireless/libertas/LICENSE b/drivers/net/wireless/libertas/LICENSE
new file mode 100644
index 0000000..8862742
--- /dev/null
+++ b/drivers/net/wireless/libertas/LICENSE
@@ -0,0 +1,16 @@
+  Copyright (c) 2003-2006, Marvell International Ltd.
+  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 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.
+
diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile
new file mode 100644
index 0000000..19c9350
--- /dev/null
+++ b/drivers/net/wireless/libertas/Makefile
@@ -0,0 +1,21 @@
+# EXTRA_CFLAGS += -Wpacked
+
+usb8xxx-objs := main.o fw.o wext.o \
+		rx.o tx.o cmd.o 	  \
+		cmdresp.o scan.o	  \
+		join.o 11d.o 		  \
+		ioctl.o debugfs.o	  \
+		ethtool.o assoc.o
+
+ifeq ($(CONFIG_LIBERTAS_USB_DEBUG), y)
+EXTRA_CFLAGS += -DDEBUG -DPROC_DEBUG
+endif
+
+
+# This is needed to support the newer boot2 bootloader (v >= 3104)
+EXTRA_CFLAGS += -DSUPPORT_BOOT_COMMAND
+usb8xxx-objs += if_bootcmd.o
+usb8xxx-objs += if_usb.o
+
+obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o
+
diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README
new file mode 100644
index 0000000..688da4c
--- /dev/null
+++ b/drivers/net/wireless/libertas/README
@@ -0,0 +1,1044 @@
+================================================================================
+			README for USB8388
+
+ (c) Copyright © 2003-2006, Marvell International Ltd.
+ All Rights Reserved
+
+ This software file (the "File") is distributed by Marvell International
+ Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ (the "License").  You may use, redistribute and/or modify this File in
+ accordance with the terms and conditions of the License, a copy of which
+ is available along with the File in the license.txt file or by writing to
+ the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt.
+
+ THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ this warranty disclaimer.
+================================================================================
+
+=====================
+DRIVER LOADING
+=====================
+
+	o. Copy the firmware image (e.g. usb8388.bin) to /lib/firmware/
+
+	o. Load driver by using the following command:
+
+		insmod usb8388.ko [fw_name=usb8388.bin]
+
+=====================
+IWPRIV COMMAND
+=====================
+
+NAME
+	This manual describes the usage of private commands used in Marvell WLAN
+	Linux Driver. All the commands available in Wlanconfig will not be available
+	in the iwpriv.
+
+SYNOPSIS
+	iwpriv <ethX> <command> [sub-command] ...
+
+	iwpriv ethX version
+	iwpriv ethX scantype [sub-command]
+	iwpriv ethX getSNR <n>
+	iwpriv ethX getNF <n>
+	iwpriv ethX getRSSI <n>
+	iwpriv ethX setrxant <n>
+	iwpriv ethX getrxant
+	iwpriv ethX settxant <n>
+	iwpriv ethX gettxant
+	iwpriv ethX authalgs <n>
+	iwpriv ethX pre-TBTT <n>
+	iwpriv ethX 8021xauthalgs <n>
+	iwpriv ethX encryptionmode <n>
+	iwpriv ethX setregioncode <n>
+	iwpriv ethX getregioncode
+	iwpriv ethX setbcnavg <n>
+	iwpriv ethX getbcnavg
+	iwpriv ethX setdataavg <n>
+	iwpriv ethX setlisteninter <n>
+	iwpriv ethX getlisteninter
+	iwpriv ethX setmultipledtim <n>
+	iwpriv ethX getmultipledtim
+	iwpriv ethX atimwindow <n>
+	iwpriv ethX deauth
+	iwpriv ethX adhocstop
+	iwpriv ethX radioon
+	iwpriv ethX radiooff
+	iwpriv ethX reasso-on
+	iwpriv ethX reasso-off
+	iwpriv ethX scanmode  [sub-command]
+	iwpriv ethX setwpaie <n>
+	iwpriv ethX wlanidle-off
+	iwpriv ethX wlanidle-on
+	iwpriv ethX getcis
+	iwpriv ethX getlog
+	iwpriv ethX getadhocstatus
+	iwpriv ethX adhocgrate <n>
+
+Version 4 Command:
+	iwpriv ethX inactvityto <n>
+	iwpriv ethX sleeppd <n>
+	iwpriv ethX enable11d <n>
+	iwpriv ethX tpccfg <n>
+	iwpriv ethX powercfg <n>
+	iwpriv ethX setafc <n>
+	iwpriv ethX getafc
+
+Version 5 Command:
+	iwpriv ethX ledgpio <n>
+	iwpriv ethX scanprobes <n>
+	iwpriv ethX lolisteninter <n>
+	iwpriv ethX rateadapt <n> <m>
+	iwpriv ethX txcontrol <n>
+	iwpriv ethX psnullinterval <n>
+	iwpriv ethX prescan <n>
+	iwpriv ethX getrxinfo
+	iwpriv ethX gettxrate
+	iwpriv ethX beaconinterval
+
+BT Commands:
+	The blinding table (BT) contains a list of mac addresses that should be
+	ignored by the firmware.  It is primarily used for debugging and
+	testing networks.  It can be edited and inspected with the following
+	commands:
+
+	iwpriv ethX bt_reset
+	iwpriv ethX bt_add <mac_address>
+	iwpriv ethX bt_del <mac_address>
+	iwpriv ethX bt_list <id>
+
+FWT Commands:
+	The forwarding table (FWT) is a feature used to manage mesh network
+	routing in the firmware.  The FWT is essentially a routing table that
+	associates a destination mac address (da) with a next hop receiver
+	address (ra).  The FWT can be inspected and edited with the following
+	iwpriv commands, which are described in greater detail below.
+	Eventually, the table will be automatically maintained by a custom
+	routing protocol.
+
+	NOTE: FWT commands replace the previous DFT commands.  What were the DFT
+	commands?, you might ask.  They were an earlier API to the firmware that
+	implemented a simple MAC-layer forwarding mechanism.  In the unlikely
+	event that you were using these commands, you must migrate to the new
+	FWT commands which can be used to achieve the same functionality.
+
+	iwpriv ethX fwt_add [parameters]
+	iwpriv ethX fwt_del [parameters]
+	iwpriv ethX fwt_lookup [parameters]
+	iwpriv ethX fwt_list [parameters]
+	iwpriv ethX fwt_list_route [parameters]
+	iwpriv ethX fwt_list_neigh [parameters]
+	iwpriv ethX fwt_reset [parameters]
+	iwpriv ethX fwt_cleanup
+	iwpriv ethX fwt_time
+
+MESH Commands:
+
+	The MESH commands are used to configure various features of the mesh
+	routing protocol.  The following commands are supported:
+
+	iwpriv ethX mesh_get_ttl
+	iwpriv ethX mesh_set_ttl ttl
+
+DESCRIPTION
+	Those commands are used to send additional commands to the Marvell WLAN
+	card via the Linux device driver.
+
+	The ethX parameter specifies the network device that is to be used to
+		perform this command on. it could be eth0, eth1 etc.
+
+version
+	This is used to get the current version of the driver and the firmware.
+
+scantype
+	This command is used to set the scan type to be used by the driver in
+	the scan command. This setting will not be used while performing a scan
+	for a specific SSID, as it is always done with scan type being active.
+
+	where the sub-commands are: -
+			active 	-- to set the scan type to active
+			passive -- to set the scan type to passive
+			get 	-- to get the scan type set in the driver
+
+getSNR
+	This command gets the average and non average value of Signal to Noise
+	Ratio of Beacon and Data.
+
+	where value is:-
+			0 	-- Beacon non-average.
+			1 	-- Beacon average.
+			2 	-- Data non-average.
+			3 	-- Data average.
+
+	If no value is given, all four values are returned in the order mentioned
+	above.
+
+	Note: This command is available only when STA is connected.
+
+getRSSI
+	This command gets the average and non average value os Receive Signal
+	Strength of Beacon and Data.
+
+	where value is:-
+			0 	-- Beacon non-average.
+			1 	-- Beacon average.
+			2 	-- Data non-average.
+			3 	-- Data average.
+
+	Note: This command is available only when STA is connected.
+
+getNF
+	This command gets the average and non average value of Noise Floor of
+	Beacon and Data.
+
+	where value is:-
+			0 	-- Beacon non-average.
+			1 	-- Beacon average.
+			2 	-- Data non-average.
+			3 	-- Data average.
+
+	Note: This command is available only when STA is connected.
+
+setrxant
+	This command is used to set the mode for Rx antenna.
+
+	The options that can be sent are:-
+			1 	-- Antenna 1.
+			2 	-- Antenna 2.
+			0xFFFF 	-- Diversity.
+
+	Usage:
+		iwpriv ethX setrxant 0x01: select Antenna 1.
+
+getrxant
+	This command is used to get the mode for Rx antenna.
+
+
+settxant
+	This command is used to set the mode for Tx antenna.
+		The options that can be sent are:-
+			1 	-- Antenna 1.
+			2 	-- Antenna 2.
+			0xFFFF 	-- Diversity.
+	Usage:
+		iwpriv ethX settxant 0x01: select Antenna 1.
+
+gettxant
+	This command is used to get the mode for Tx antenna.
+
+authalgs
+	This command is used by the WPA supplicant to set the authentication
+	algorithms in the station.
+
+8021xauthalgs
+	This command is used by the WPA supplicant to set the 8021.x authentication algorithm type
+	station.
+
+	where values can be:-
+			1 	-- None
+			2 	-- LEAP
+			4 	-- TLS
+			8 	-- TTLs
+			16	-- MD5
+
+
+encryptionmode
+	This command is used by the WPA supplicant to set the encryption algorithm.
+
+	where values can be:-
+			0 	-- NONE
+			1 	-- WEP40
+			2 	-- TKIP
+			3 	-- CCMP
+			4 	-- WEP104
+
+pre-TBTT
+	This command is used to set pre-TBTT time period where value is in microseconds.
+
+setregioncode
+	This command is used to set the region code in the station.
+	where value is 'region code' for various regions like
+	USA FCC, Canada IC, Spain, France, Europe ETSI,	Japan ...
+
+	Usage:
+		iwpriv ethX setregioncode 0x10: set region code to USA (0x10).
+
+getregioncode
+	This command is used to get the region code information set in the
+	station.
+
+setbcnavg
+	Set the weighting factor for calculating RSSI.
+
+getbcnavg
+	Get weighting factor for calculating RSSI.
+
+setdataavg
+	Set the weighting factor for calculating SNR.
+
+setlisteninter
+	This command is used to set the listen interval in the
+	station.
+
+	where the value ranges between 1 - 255
+
+getlisteninter
+	This command is used to get the listen interval value set in the
+	station.
+
+setmultipledtim
+	This command is used to set the multiple dtim value in the
+	station.
+		where the value is 1,2,3,4,5,0xfffe
+		0xfffe means the firmware will use listen interval in association
+		command for waking up
+
+getmultipledtim
+	This command is used to get the multiple dtim value set in the station.
+
+atimwindow
+	This command is used to set the atim value in the
+	station.
+
+	where the value ranges between 0 - 50
+
+deauth
+	This command is used to send the de-authentication to the AP with which
+	the station is associated. This command is valid only when
+	station is in Infrastructure mode.
+
+	Note: This command is available only when STA is connected.
+
+adhocstop
+	This command is used to stop beacon transmission from the station and
+	go into idle state in ad-hoc mode.
+
+	Note: This command is available only when STA is connected.
+
+radioon
+	This command is used to turn on the RF antenna.
+
+radiooff
+	This command is sued to turn off the RF antenna.
+
+scanmode
+	This command is used to set the station to scan for either IBSS
+	networks or BSS networks or both BSS and IBSS networks. This
+	command can be used with sub commands,
+
+	where the value for
+			bss 	-- Scan All the BSS networks.
+			ibss 	-- Scan All the IBSS networks.
+			any 	-- Scan both BSS and IBSS networks.
+
+
+
+setwpaie
+	This command is used by WPA supplicant to send the WPA-IE to the driver.
+
+wlanidle-off
+	This command is used to get into idle state.
+
+	Note: This command is available only when STA is connected.
+
+wlanidle-on
+	This command is used to get off the idle state.
+
+	Note: This command is available only when STA is connected.
+
+
+getlog
+	This command is used to get the 802.11 statistics available in the
+		station.
+
+	Note: This command is available only when STA is connected.
+
+getadhocstatus
+	This command is used to get the ad-hoc Network Status.
+
+	The various status codes are:
+		AdhocStarted
+		AdhocJoined
+		AdhocIdle
+		InfraMode
+		AutoUnknownMode
+
+	Note: This command is available only when STA is connected.
+
+adhocgrate
+	This command is used to enable(1) g_rate, Disable(0) g_rate
+	and request(2) the status which g_rate is disabled/enabled,
+	for Ad-hoc creator.
+
+	where value is:-
+		0	-- Disabled
+		1	-- Enabled
+		2	-- Get
+
+ledgpio
+	This command is used to set/get LEDs.
+
+	iwpriv ethX ledgpio <LEDs>
+		will set the corresponding LED for the GPIO Line.
+
+	iwpriv ethX ledgpio
+		will give u which LEDs are Enabled.
+
+	Usage:
+		iwpriv eth1 ledgpio 1 0 2 1 3 4
+			will enable
+			LED 1 -> GPIO 0
+			LED 2 -> GPIO 1
+			LED 3 -> GPIO 4
+
+		iwpriv eth1 ledgpio
+			shows LED information in the format as mentioned above.
+
+	Note: LED0 is invalid
+	Note: Maximum Number of LEDs are 16.
+
+inactivityto
+	This command is used by the host to set/get the inactivity timeout value,
+	which specifies when WLAN device is put to sleep.
+
+	Usage:
+		iwpriv ethX inactivityto [<timeout>]
+
+	where the parameter are:
+		timeout: timeout value in milliseconds.
+
+	Example:
+		iwpriv eth1 inactivityto
+			"get the timeout value"
+
+		iwpriv eth1 inactivityto X
+			"set timeout value to X ms"
+
+
+sleeppd
+	This command is used to configure the sleep period of the WLAN device.
+
+	Usage:
+		iwpriv ethX sleeppd [<sleep period>]
+
+	where the parameter are:
+		Period: sleep period in milliseconds. Range 10~60.
+
+	Example:
+		iwpriv eth1 sleeppd 10
+			"set period as 10 ms"
+		iwpriv eth1 sleeppd
+			"get the sleep period configuration"
+
+enable11d
+	This command is used to control 11d
+	where value is:-
+		1	-- Enabled
+		0	-- Disabled
+		2	-- Get
+
+
+
+
+tpccfg
+	Enables or disables automatic transmit power control.
+
+	The first parameter turns this feature on (1) or off (0).  When turning
+	on, the user must also supply four more parameters in the following
+	order:
+		-UseSNR (Use SNR (in addition to PER) for TPC algorithm),
+		-P0 (P0 power level for TPC),
+		-P1 (P1 power level for TPC),
+		-P2 (P2 power level for TPC).
+
+	Usage:
+		iwpriv ethX tpccfg: Get current configuration
+		iwpriv ethX tpccfg 0: disable auto TPC
+		iwpriv ethX tpccfg 0x01 0x00 0x05 0x0a 0x0d: enable auto TPC; do not use SNR;
+							     P0=0x05; P1=0x0a; P2=0x0d;
+		iwpriv ethX tpccfg 0x01 0x01 0x05 0x0a 0x0d: enable auto TPC; use SNR;
+							     P0=0x05; P1=0x0a; P2=0x0d.
+
+powercfg
+	Enables or disables power adaptation.
+
+	The first parameter turns this feature on (1) or off (0).  When turning
+	on, the user must also supply three more parameters in the following
+	order:
+		-P0 (P0 power level for Power Adaptation),
+		-P1 (P1 power level for Power Adaptation),
+		-P2 (P2 power level for Power Adaptation).
+
+	Usage:
+		iwpriv ethX powercfg: Get current configuration
+		iwpriv ethX powercfg 0: disable power adaptation
+		iwpriv ethX powercfg 1 0x0d 0x0f 0x12: enable power adaptation;
+						       P0=0x0d; P1=0x0f; P2=0x12.
+
+getafc
+	This command returns automatic frequency control parameters.  It returns
+	three integers:
+		-P0: automatic is on (1), or off (0),
+		-P1: current timing offset in PPM (part per million), and
+		-P2: current frequency offset in PPM.
+
+setafc
+	Set automatic frequency control options.
+
+	The first parameter turns automatic on (1) or off (0).
+	The user must supply two more parameters in either case, in the following
+  order:
+
+  When auto is on:
+
+		-P0 (automatic adjustment frequency threshold in PPM),
+		-P1 (automatic adjustment period in beacon period),
+
+  When auto is off:
+
+		-P0 (manual adjustment timing offset in PPM), and
+		-P1 (manual adjustment frequency offset in PPM).
+
+	Usage:
+		iwpriv ethX setafc 0 10 10: manual adjustment, both timing and frequcncy
+    offset are 10 PPM.
+
+		iwpriv ethX setafc 1 10 10 enable afc, automatic adjustment,
+    frequency threshold 10 PPM, for every 10 beacon periods.
+
+
+
+scanprobes
+	This command sets number of probe requests per channel.
+
+	Usage:
+		iwpriv ethX scanprobes 3 (set scan probes to 3)
+		iwpriv ethX scanprobes   (get scan probes)
+
+lolisteninter
+	This command sets the value of listen interval.
+
+	Usage:
+	iwpriv ethX lolisteninter 234 (set the lolisteninter to 234)
+	iwpriv ethX lolisteninter     (get the lolisteninter value)
+
+rateadapt
+	This command sets the data rates bitmap.
+	Where <n>
+		0: Disable auto rate adapt
+		1: Enable auto rate adapt
+
+	      <m>
+		 data rate bitmap
+			Bit	Data rate
+			0	1 Mbps
+			1	2 Mbps
+			2	5.5 Mbps
+			3	11 Mbps
+			4	Reserved
+			5	6 Mbps
+			6	9 Mbps
+			7	12 Mbps
+			8	18 Mbps
+			9	24 Mbps
+			10	36 Mbps
+			11	48 Mbps
+			12	54 Mbps
+			12-15	Reserved
+
+	Usage:
+	iwpriv ethX rateadapt
+			read the currect data rate setting
+	iwpriv ethX rateadapt 1 0x07
+			enable auto data rate adapt and
+			data rates are 1Mbps, 2Mbsp and 5.5Mbps
+
+
+txcontrol
+	This command is used to set the Tx rate, ack policy, and retry limit on a per packet basis.
+
+	Where value <n> is:
+	    if bit[4] == 1:
+		bit[3:0]        -- 0   1   2   3   4   5   6   7   8   9   10   11   12   13-16
+		Data Rate(Mbps) -- 1   2   5.5 11  Rsv 6   9   12  18  24  36   48   54   Rsv
+
+	    bit[12:8]
+		if bit[12] == 1, bit[11:8] specifies the Tx retry limit.
+
+	    bit[14:13] specifies per packet ack policy:
+		bit[14:13]
+		     1  0	use immediate ack policy for this packet
+		     1  1       use no ack policy for this packet
+		     0  x	use the per-packet ack policy setting
+
+	Usage:
+	iwpriv ethX txcontrol 0x7513
+			Use no-ack policy, 5 retires for Tx, 11Mbps rate
+
+
+
+psnullinterval
+	This command is used to set/request NULL package interval for Power Save
+	under infrastructure mode.
+
+	where value is:-
+		-1	-- Disabled
+		n>0	-- Set interval as n (seconds)
+
+prescan
+	This command is used to enable (1)/disable(0) auto prescan before assoicate to the ap
+
+	where value is:-
+		0	-- Disabled
+		1	-- Enabled
+		2       -- Get
+
+getrxinfo
+	This command gets non average value of Signal to Noise Ratio of Data and rate index.
+
+	The following table shows RateIndex and Rate
+
+		     RateIndex	Data rate
+			0	1 Mbps
+			1	2 Mbps
+			2	5.5 Mbps
+			3	11 Mbps
+			4	Reserved
+			5	6 Mbps
+			6	9 Mbps
+			7	12 Mbps
+			8	18 Mbps
+			9	24 Mbps
+			10	36 Mbps
+			11	48 Mbps
+			12	54 Mbps
+			13-15	Reserved
+
+gettxrate
+	This command gets current Tx rate index of the first packet associated with Rate Adaptation.
+
+	The following table shows RateIndex and Rate
+
+		     RateIndex	Data rate
+			0	1 Mbps
+			1	2 Mbps
+			2	5.5 Mbps
+			3	11 Mbps
+			4	Reserved
+			5	6 Mbps
+			6	9 Mbps
+			7	12 Mbps
+			8	18 Mbps
+			9	24 Mbps
+			10	36 Mbps
+			11	48 Mbps
+			12	54 Mbps
+			13-15	Reserved
+
+bcninterval
+	This command is used to sets beacon interval in adhoc mode when an argument is given, and gets current adhoc
+	beacon interval when no argument is given. The valid beacon interval is between 20 - 1000,
+	default beacon interval is 100.
+
+	Usage:
+		iwpriv ethX bcninterval 100  (set adhoc beacon interval to 100)
+		iwpriv ethX bcninterval      (get adhoc beacon interval)
+
+fwt_add
+	This command is used to insert an entry into the FWT table. The list of
+	parameters must follow the following structure:
+
+	iwpriv ethX fwt_add da ra [metric dir ssn dsn hopcount ttl expiration sleepmode snr]
+
+	The parameters between brackets are optional, but they must appear in
+	the order specified.  For example, if you want to specify the metric,
+	you must also specify the dir, ssn, and dsn but you need not specify the
+	hopcount, expiration, sleepmode, or snr.  Any unspecified parameters
+	will be assigned the defaults specified below.
+
+	The different parameters are:-
+		da		-- DA MAC address in the form 00:11:22:33:44:55
+		ra		-- RA MAC address in the form 00:11:22:33:44:55
+		metric		-- route metric (cost: smaller-metric routes are
+				   preferred, default is 0)
+		dir		-- direction (1 for direct, 0 for reverse,
+				   default is 1)
+		ssn		-- Source Sequence Number (time at the RA for
+				   reverse routes.  Default is 0)
+		dsn		-- Destination Sequence Number (time at the DA
+				   for direct routes.  Default is 0)
+		hopcount	-- hop count (currently unused, default is 0)
+		ttl		-- TTL (Only used in reverse entries)
+		expiration	-- entry expiration (in ticks, where a tick is
+				   1024us, or ~ 1ms. Use 0 for an indefinite
+				   entry, default is 0)
+		sleepmode	-- RA's sleep mode (currently unused, default is
+				   0)
+		snr		-- SNR in the link to RA (currently unused,
+				   default is 0)
+
+	The command does not return anything.
+
+fwt_del
+	This command is used to remove an entry to the FWT table. The list of
+	parameters must follow the following structure:
+
+		iwpriv ethX fwt_del da ra [dir]
+
+	where the different parameters are:-
+		da		-- DA MAC address (in the form "00:11:22:33:44:55")
+		ra		-- RA MAC address (in the form "00:11:22:33:44:55")
+		dir		-- direction (1 for direct, 0 for reverse,
+				   default is 1)
+
+	The command does not return anything.
+
+fwt_lookup
+	This command is used to get the best route in the FWT table to a given
+	host. The only parameter is the MAC address of the host that is being
+	looked for.
+
+		iwpriv ethX fwt_lookup da
+
+	where:-
+		da		-- DA MAC address (in the form "00:11:22:33:44:55")
+
+	The command returns an output string identical to the one returned by
+	fwt_list described below.
+
+
+fwt_list
+	This command is used to list a route from the FWT table. The only
+	parameter is the index into the table. If you want to list all the
+	routes in a table, start with index=0, and keep listing until you get a
+	"(null)" string.  Note that the indicies may change as the fwt is
+	updated.  It is expected that most users will not use fwt_list directly,
+	but that a utility similar to the traditional route command will be used
+	to invoke fwt_list over and over.
+
+		iwpriv ethX fwt_list index
+
+	The output is a string of the following form:
+
+		da ra metric dir ssn dsn hopcount ttl expiration sleepmode snr
+
+	where the different fields are:-
+		da		-- DA MAC address (in the form "00:11:22:33:44:55")
+		ra		-- RA MAC address (in the form "00:11:22:33:44:55")
+		metric		-- route metric (cost: smaller-metric routes are preferred)
+		dir		-- direction (1 for direct, 0 for reverse)
+		ssn		-- Source Sequence Number (time at the RA for reverse routes)
+		dsn		-- Destination Sequence Number (time at the DA for direct routes)
+		hopcount	-- hop count (currently unused)
+		ttl		-- TTL (only used in reverse entries)
+		expiration	-- entry expiration (in ticks, where a tick is 1024us, or ~ 1ms. Use 0 for an indefinite entry)
+		sleepmode	-- RA's sleep mode (currently unused)
+		snr		-- SNR in the link to RA (currently unused)
+
+fwt_list_route
+	This command is used to list a route from the FWT table. The only
+	parameter is the route ID. If you want to list all the routes in a
+	table, start with rid=0, and keep incrementing rid until you get a
+	"(null)" string. This function is similar to fwt_list. The only
+	difference is the output format.  Also note that this command is meant
+	for debugging.  It is expected that users will use fwt_lookup and
+	fwt_list.  One important reason for this is that the route id may change
+	as the route table is altered.
+
+		iwpriv ethX fwt_list_route rid
+
+	The output is a string of the following form:
+
+		da metric dir nid ssn dsn hopcount ttl expiration
+
+	where the different fields are:-
+		da		-- DA MAC address (in the form "00:11:22:33:44:55")
+		metric		-- route metric (cost: smaller-metric routes are preferred)
+		dir		-- direction (1 for direct, 0 for reverse)
+		nid		-- Next-hop (neighbor) host ID (nid)
+		ssn		-- Source Sequence Number (time at the RA for reverse routes)
+		dsn		-- Destination Sequence Number (time at the DA for direct routes)
+		hopcount	-- hop count (currently unused)
+		ttl		-- TTL count (only used in reverse entries)
+		expiration	-- entry expiration (in ticks, where a tick is 1024us, or ~ 1ms. Use 0 for an indefinite entry)
+
+fwt_list_neigh
+	This command is used to list a neighbor from the FWT table. The only
+	parameter is the neighbor ID. If you want to list all the neighbors in a
+	table, start with nid=0, and keep incrementing nid until you get a
+	"(null)" string.  Note that the nid from a fwt_list_route command can be
+	used as an input to this command.  Also note that this command is meant
+	mostly for debugging.  It is expected that users will use fwt_lookup.
+	One important reason for this is that the neighbor id may change as the
+	neighbor table is altered.
+
+		iwpriv ethX fwt_list_neigh nid
+
+	The output is a string of the following form:
+
+		ra sleepmode snr references
+
+	where the different fields are:-
+		ra		-- RA MAC address (in the form "00:11:22:33:44:55")
+		sleepmode	-- RA's sleep mode (currently unused)
+		snr		-- SNR in the link to RA (currently unused)
+		references	-- RA's reference counter
+
+fwt_reset
+	This command is used to reset the FWT table, getting rid of all the
+	entries. There are no input parameters.
+
+		iwpriv ethX fwt_reset
+
+	The command does not return anything.
+
+fwt_cleanup
+	This command is used to perform user-based garbage recollection. The
+	FWT table is checked, and all the entries that are expired or invalid
+	are cleaned. Note that this is exported to the driver for debugging
+	purposes, as garbage collection is also fired by the firmware when in
+	space problems. There are no input parameters.
+
+		iwpriv ethX fwt_cleanup
+
+	The command does returns the number of invalid/expired routes deleted.
+
+fwt_time
+	This command returns a card's internal time representation.  It is this
+	time that is used to represent the expiration times of FWT entries.  The
+	number is not consistent from card to card; it is simply a timer count.
+	The fwt_time command is used to inspect the timer so that expiration
+	times reported by fwt_list can be properly interpreted.
+
+		iwpriv ethX fwt_time
+
+mesh_get_ttl
+
+	The mesh ttl is the number of hops a mesh packet can traverse before it
+	is dropped.  This parameter is used to prevent infinite loops in the
+	mesh network.  The value returned by this function is the ttl assigned
+	to all mesh packets.  Currently there is no way to control the ttl on a
+	per packet or per socket basis.
+
+	iwpriv ethX mesh_get_ttl
+
+mesh_set_ttl ttl
+
+	Set the ttl.  The argument must be between 0 and 255.
+
+	iwpriv ethX mesh_set_ttl <ttl>
+
+=========================
+ETHTOOL
+=========================
+
+
+Use the -i option to retrieve version information from the driver.
+
+# ethtool -i eth0
+driver: libertas
+version: COMM-USB8388-318.p4
+firmware-version: 5.110.7
+bus-info:
+
+Use the -e option to read the EEPROM contents of the card.
+
+	Usage:
+	ethtool -e ethX [raw on|off] [offset N] [length N]
+
+       -e     retrieves and prints an EEPROM dump for the  specified  ethernet
+              device.   When raw is enabled, then it dumps the raw EEPROM data
+              to stdout. The length and offset parameters allow  dumping  cer-
+              tain portions of the EEPROM.  Default is to dump the entire EEP-
+              ROM.
+
+# ethtool -e eth0 offset 0 length 16
+Offset          Values
+------          ------
+0x0000          38 33 30 58 00 00 34 f4 00 00 10 00 00 c4 17 00
+
+========================
+DEBUGFS COMMANDS
+========================
+
+those commands are used via debugfs interface
+
+===========
+rdmac
+rdbbp
+rdrf
+	These commands are used to read the MAC, BBP and RF registers from the
+	card.  These commands take one parameter that specifies the offset
+	location that is to be read.  This parameter must be specified in
+	hexadecimal (its possible to preceed preceding the number with a "0x").
+
+	Path: /debugfs/libertas_wireless/ethX/registers/
+
+	Usage:
+		echo "0xa123" > rdmac ; cat rdmac
+		echo "0xa123" > rdbbp ; cat rdbbp
+		echo "0xa123" > rdrf ; cat rdrf
+wrmac
+wrbbp
+wrrf
+	These commands are used to write the MAC, BBP and RF registers in the
+	card.  These commands take two parameters that specify the offset
+	location and the value that is to be written. This parameters must
+	be specified in hexadecimal (its possible to preceed the number
+	with a "0x").
+
+	Usage:
+		echo "0xa123 0xaa" > wrmac
+		echo "0xa123 0xaa" > wrbbp
+		echo "0xa123 0xaa" > wrrf
+
+sleepparams
+	This command is used to set the sleepclock configurations
+
+	Path: /debugfs/libertas_wireless/ethX/
+
+	Usage:
+		cat sleepparams: reads the current sleepclock configuration
+
+		echo "p1 p2 p3 p4 p5 p6" > sleepparams: writes the sleepclock configuration.
+
+		where:
+			p1 is Sleep clock error in ppm (0-65535)
+			p2 is Wakeup offset in usec (0-65535)
+			p3 is Clock stabilization time in usec (0-65535)
+			p4 is Control periodic calibration (0-2)
+			p5 is Control the use of external sleep clock (0-2)
+			p6 is reserved for debug (0-65535)
+
+subscribed_events
+
+	The subscribed_events directory contains the interface for the
+	subscribed events API.
+
+	Path: /debugfs/libertas_wireless/ethX/subscribed_events/
+
+	Each event is represented by a filename. Each filename consists of the
+	following three fields:
+	Value Frequency Subscribed
+
+	To read the current values for a given event, do:
+		cat event
+	To set the current values, do:
+		echo "60 2 1" > event
+
+	Frequency field specifies the reporting frequency for this event.
+	If it is set to 0, then the event is reported only once, and then
+	automatically unsubscribed. If it is set to 1, then the event is
+	reported every time it occurs. If it is set to N, then the event is
+	reported every Nth time it occurs.
+
+	beacon_missed
+	Value field specifies the number of consecutive missing beacons which
+	triggers the LINK_LOSS event. This event is generated only once after
+	which the firmware resets its state. At initialization, the LINK_LOSS
+	event is subscribed by default. The default value of MissedBeacons is
+	60.
+
+	failure_count
+	Value field specifies the consecutive failure count threshold which
+	triggers the generation of the MAX_FAIL event. Once this event is
+	generated, the consecutive failure count is reset to 0.
+	At initialization, the MAX_FAIL event is NOT subscribed by
+	default.
+
+	high_rssi
+	This event is generated when the average received RSSI in beacons goes
+	above a threshold, specified by Value.
+
+	low_rssi
+	This event is generated when the average received RSSI in beacons goes
+	below a threshold, specified by Value.
+
+	high_snr
+	This event is generated when the average received SNR in beacons goes
+	above a threshold, specified by Value.
+
+	low_snr
+	This event is generated when the average received SNR in beacons goes
+	below a threshold, specified by Value.
+
+extscan
+	This command is used to do a specific scan.
+
+	Path: /debugfs/libertas_wireless/ethX/
+
+	Usage: echo "SSID" > extscan
+
+	Example:
+		echo "LINKSYS-AP" > extscan
+
+	To see the results of use getscantable command.
+
+getscantable
+
+	Display the current contents of the driver scan table (ie. get the
+	scan results).
+
+	Path: /debugfs/libertas_wireless/ethX/
+
+	Usage:
+		cat getscantable
+
+setuserscan
+	Initiate a customized scan and retrieve the results
+
+
+	Path: /debugfs/libertas_wireless/ethX/
+
+    Usage:
+       echo "[ARGS]" > setuserscan
+
+         where [ARGS]:
+
+      chan=[chan#][band][mode] where band is [a,b,g] and mode is
+                               blank for active or 'p' for passive
+      bssid=xx:xx:xx:xx:xx:xx  specify a BSSID filter for the scan
+      ssid="[SSID]"            specify a SSID filter for the scan
+      keep=[0 or 1]            keep the previous scan results (1), discard (0)
+      dur=[scan time]          time to scan for each channel in milliseconds
+      probes=[#]               number of probe requests to send on each chan
+      type=[1,2,3]             BSS type: 1 (Infra), 2(Adhoc), 3(Any)
+
+    Any combination of the above arguments can be supplied on the command line.
+      If the chan token is absent, a full channel scan will be completed by
+      the driver.  If the dur or probes tokens are absent, the driver default
+      setting will be used.  The bssid and ssid fields, if blank,
+      will produce an unfiltered scan. The type field will default to 3 (Any)
+      and the keep field will default to 0 (Discard).
+
+    Examples:
+    1) Perform an active scan on channels 1, 6, and 11 in the 'g' band:
+            echo "chan=1g,6g,11g" > setuserscan
+
+    2) Perform a passive scan on channel 11 for 20 ms:
+            echo "chan=11gp dur=20" > setuserscan
+
+    3) Perform an active scan on channels 1, 6, and 11; and a passive scan on
+       channel 36 in the 'a' band:
+
+            echo "chan=1g,6g,11g,36ap" > setuserscan
+
+    4) Perform an active scan on channel 6 and 36 for a specific SSID:
+            echo "chan=6g,36a ssid="TestAP"" > setuserscan
+
+    5) Scan all available channels (B/G, A bands) for a specific BSSID, keep
+       the current scan table intact, update existing or append new scan data:
+            echo "bssid=00:50:43:20:12:82 keep=1" > setuserscan
+
+    6) Scan channel 6, for all infrastructure networks, sending two probe
+       requests.  Keep the previous scan table intact. Update any duplicate
+       BSSID/SSID matches with the new scan data:
+            echo "chan=6g type=1 probes=2 keep=1" > setuserscan
+
+    All entries in the scan table (not just the new scan data when keep=1)
+    will be displayed upon completion by use of the getscantable ioctl.
+
+==============================================================================
diff --git a/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c
new file mode 100644
index 0000000..b55c7f5
--- /dev/null
+++ b/drivers/net/wireless/libertas/assoc.c
@@ -0,0 +1,588 @@
+/* Copyright (C) 2006, Red Hat, Inc. */
+
+#include <linux/bitops.h>
+#include <net/ieee80211.h>
+
+#include "assoc.h"
+#include "join.h"
+#include "decl.h"
+#include "hostcmd.h"
+#include "host.h"
+
+
+static const u8 bssid_any[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+static const u8 bssid_off[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static int assoc_helper_essid(wlan_private *priv,
+                              struct assoc_request * assoc_req)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+	int i;
+
+	ENTER();
+
+	lbs_pr_debug(1, "New SSID requested: %s\n", assoc_req->ssid.ssid);
+	if (assoc_req->mode == wlan802_11infrastructure) {
+		if (adapter->prescan) {
+			libertas_send_specific_SSID_scan(priv, &assoc_req->ssid, 1);
+		}
+
+		i = libertas_find_SSID_in_list(adapter, &assoc_req->ssid,
+				NULL, wlan802_11infrastructure);
+		if (i >= 0) {
+			lbs_pr_debug(1,
+			       "SSID found in scan list ... associating...\n");
+
+			ret = wlan_associate(priv, &adapter->scantable[i]);
+			if (ret == 0) {
+				memcpy(&assoc_req->bssid,
+				       &adapter->scantable[i].macaddress,
+				       ETH_ALEN);
+			}
+		} else {
+			lbs_pr_debug(1, "SSID '%s' not found; cannot associate\n",
+				assoc_req->ssid.ssid);
+		}
+	} else if (assoc_req->mode == wlan802_11ibss) {
+		/* Scan for the network, do not save previous results.  Stale
+		 *   scan data will cause us to join a non-existant adhoc network
+		 */
+		libertas_send_specific_SSID_scan(priv, &assoc_req->ssid, 0);
+
+		/* Search for the requested SSID in the scan table */
+		i = libertas_find_SSID_in_list(adapter, &assoc_req->ssid, NULL,
+				wlan802_11ibss);
+		if (i >= 0) {
+			lbs_pr_debug(1, "SSID found at %d in List, so join\n", ret);
+			libertas_join_adhoc_network(priv, &adapter->scantable[i]);
+		} else {
+			/* else send START command */
+			lbs_pr_debug(1, "SSID not found in list, so creating adhoc"
+				" with SSID '%s'\n", assoc_req->ssid.ssid);
+			libertas_start_adhoc_network(priv, &assoc_req->ssid);
+		}
+		memcpy(&assoc_req->bssid, &adapter->current_addr, ETH_ALEN);
+	}
+
+	LEAVE();
+	return ret;
+}
+
+
+static int assoc_helper_bssid(wlan_private *priv,
+                              struct assoc_request * assoc_req)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int i, ret = 0;
+
+	ENTER();
+
+	lbs_pr_debug(1, "ASSOC: WAP: BSSID = " MAC_FMT "\n",
+		MAC_ARG(assoc_req->bssid));
+
+	/* Search for index position in list for requested MAC */
+	i = libertas_find_BSSID_in_list(adapter, assoc_req->bssid,
+			    assoc_req->mode);
+	if (i < 0) {
+		lbs_pr_debug(1, "ASSOC: WAP: BSSID " MAC_FMT " not found, "
+			"cannot associate.\n", MAC_ARG(assoc_req->bssid));
+		goto out;
+	}
+
+	if (assoc_req->mode == wlan802_11infrastructure) {
+		ret = wlan_associate(priv, &adapter->scantable[i]);
+		lbs_pr_debug(1, "ASSOC: return from wlan_associate(bssd) was %d\n", ret);
+	} else if (assoc_req->mode == wlan802_11ibss) {
+		libertas_join_adhoc_network(priv, &adapter->scantable[i]);
+	}
+	memcpy(&assoc_req->ssid, &adapter->scantable[i].ssid,
+		sizeof(struct WLAN_802_11_SSID));
+
+out:
+	LEAVE();
+	return ret;
+}
+
+
+static int assoc_helper_associate(wlan_private *priv,
+                                  struct assoc_request * assoc_req)
+{
+	int ret = 0, done = 0;
+
+	/* If we're given and 'any' BSSID, try associating based on SSID */
+
+	if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
+		if (memcmp(bssid_any, assoc_req->bssid, ETH_ALEN)
+		    && memcmp(bssid_off, assoc_req->bssid, ETH_ALEN)) {
+			ret = assoc_helper_bssid(priv, assoc_req);
+			done = 1;
+			if (ret) {
+				lbs_pr_debug(1, "ASSOC: bssid: ret = %d\n", ret);
+			}
+		}
+	}
+
+	if (!done && test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
+		ret = assoc_helper_essid(priv, assoc_req);
+		if (ret) {
+			lbs_pr_debug(1, "ASSOC: bssid: ret = %d\n", ret);
+		}
+	}
+
+	return ret;
+}
+
+
+static int assoc_helper_mode(wlan_private *priv,
+                             struct assoc_request * assoc_req)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+
+	ENTER();
+
+	if (assoc_req->mode == adapter->inframode) {
+		LEAVE();
+		return 0;
+	}
+
+	if (assoc_req->mode == wlan802_11infrastructure) {
+		if (adapter->psstate != PS_STATE_FULL_POWER)
+			libertas_ps_wakeup(priv, cmd_option_waitforrsp);
+		adapter->psmode = wlan802_11powermodecam;
+	}
+
+	adapter->inframode = assoc_req->mode;
+	ret = libertas_prepare_and_send_command(priv,
+				    cmd_802_11_snmp_mib,
+				    0, cmd_option_waitforrsp,
+				    OID_802_11_INFRASTRUCTURE_MODE,
+				    (void *) assoc_req->mode);
+
+	LEAVE();
+	return ret;
+}
+
+
+static int assoc_helper_wep_keys(wlan_private *priv,
+                                 struct assoc_request * assoc_req)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int i;
+	int ret = 0;
+
+	ENTER();
+
+	/* Set or remove WEP keys */
+	if (   assoc_req->wep_keys[0].len
+	    || assoc_req->wep_keys[1].len
+	    || assoc_req->wep_keys[2].len
+	    || assoc_req->wep_keys[3].len) {
+		ret = libertas_prepare_and_send_command(priv,
+					    cmd_802_11_set_wep,
+					    cmd_act_add,
+					    cmd_option_waitforrsp,
+					    0, assoc_req);
+	} else {
+		ret = libertas_prepare_and_send_command(priv,
+					    cmd_802_11_set_wep,
+					    cmd_act_remove,
+					    cmd_option_waitforrsp,
+					    0, NULL);
+	}
+
+	if (ret)
+		goto out;
+
+	/* enable/disable the MAC's WEP packet filter */
+	if (assoc_req->secinfo.WEPstatus == wlan802_11WEPenabled)
+		adapter->currentpacketfilter |= cmd_act_mac_wep_enable;
+	else
+		adapter->currentpacketfilter &= ~cmd_act_mac_wep_enable;
+	ret = libertas_set_mac_packet_filter(priv);
+	if (ret)
+		goto out;
+
+	mutex_lock(&adapter->lock);
+
+	/* Copy WEP keys into adapter wep key fields */
+	for (i = 0; i < 4; i++) {
+		memcpy(&adapter->wep_keys[i], &assoc_req->wep_keys[i],
+			sizeof(struct WLAN_802_11_KEY));
+	}
+	adapter->wep_tx_keyidx = assoc_req->wep_tx_keyidx;
+
+	mutex_unlock(&adapter->lock);
+
+out:
+	LEAVE();
+	return ret;
+}
+
+static int assoc_helper_secinfo(wlan_private *priv,
+                                struct assoc_request * assoc_req)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+
+	ENTER();
+
+	memcpy(&adapter->secinfo, &assoc_req->secinfo,
+		sizeof(struct wlan_802_11_security));
+
+	ret = libertas_set_mac_packet_filter(priv);
+
+	LEAVE();
+	return ret;
+}
+
+
+static int assoc_helper_wpa_keys(wlan_private *priv,
+                                 struct assoc_request * assoc_req)
+{
+	int ret = 0;
+
+	ENTER();
+
+	/* enable/Disable RSN */
+	ret = libertas_prepare_and_send_command(priv,
+				    cmd_802_11_enable_rsn,
+				    cmd_act_set,
+				    cmd_option_waitforrsp,
+				    0, assoc_req);
+	if (ret)
+		goto out;
+
+	ret = libertas_prepare_and_send_command(priv,
+				    cmd_802_11_key_material,
+				    cmd_act_set,
+				    cmd_option_waitforrsp,
+				    0, assoc_req);
+
+out:
+	LEAVE();
+	return ret;
+}
+
+
+static int assoc_helper_wpa_ie(wlan_private *priv,
+                               struct assoc_request * assoc_req)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+
+	ENTER();
+
+	if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) {
+		memcpy(&adapter->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len);
+		adapter->wpa_ie_len = assoc_req->wpa_ie_len;
+	} else {
+		memset(&adapter->wpa_ie, 0, MAX_WPA_IE_LEN);
+		adapter->wpa_ie_len = 0;
+	}
+
+	LEAVE();
+	return ret;
+}
+
+
+static int should_deauth_infrastructure(wlan_adapter *adapter,
+                                        struct assoc_request * assoc_req)
+{
+	if (adapter->connect_status != libertas_connected)
+		return 0;
+
+	if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
+		lbs_pr_debug(1, "Deauthenticating due to new SSID in "
+			" configuration request.\n");
+		return 1;
+	}
+
+	if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
+		if (adapter->secinfo.authmode !=
+		    assoc_req->secinfo.authmode) {
+			lbs_pr_debug(1, "Deauthenticating due to updated security "
+				"info in configuration request.\n");
+			return 1;
+		}
+	}
+
+	if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
+		lbs_pr_debug(1, "Deauthenticating due to new BSSID in "
+			" configuration request.\n");
+		return 1;
+	}
+
+	/* FIXME: deal with 'auto' mode somehow */
+	if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
+		if (assoc_req->mode != wlan802_11infrastructure)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int should_stop_adhoc(wlan_adapter *adapter,
+                             struct assoc_request * assoc_req)
+{
+	if (adapter->connect_status != libertas_connected)
+		return 0;
+
+	if (adapter->curbssparams.ssid.ssidlength != assoc_req->ssid.ssidlength)
+		return 1;
+	if (memcmp(adapter->curbssparams.ssid.ssid, assoc_req->ssid.ssid,
+			sizeof(struct WLAN_802_11_SSID)))
+		return 1;
+
+	/* FIXME: deal with 'auto' mode somehow */
+	if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
+		if (assoc_req->mode != wlan802_11ibss)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+void wlan_association_worker(struct work_struct *work)
+{
+	wlan_private *priv = container_of(work, wlan_private, assoc_work.work);
+	wlan_adapter *adapter = priv->adapter;
+	struct assoc_request * assoc_req = NULL;
+	int ret = 0;
+	int find_any_ssid = 0;
+
+	ENTER();
+
+	mutex_lock(&adapter->lock);
+	assoc_req = adapter->assoc_req;
+	adapter->assoc_req = NULL;
+	mutex_unlock(&adapter->lock);
+
+	if (!assoc_req) {
+		LEAVE();
+		return;
+	}
+
+	lbs_pr_debug(1, "ASSOC: starting new association request: flags = 0x%lX\n",
+		assoc_req->flags);
+
+	/* If 'any' SSID was specified, find an SSID to associate with */
+	if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)
+	    && !assoc_req->ssid.ssidlength)
+		find_any_ssid = 1;
+
+	/* But don't use 'any' SSID if there's a valid locked BSSID to use */
+	if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
+		if (memcmp(&assoc_req->bssid, bssid_any, ETH_ALEN)
+		    && memcmp(&assoc_req->bssid, bssid_off, ETH_ALEN))
+			find_any_ssid = 0;
+	}
+
+	if (find_any_ssid) {
+		enum WLAN_802_11_NETWORK_INFRASTRUCTURE new_mode;
+
+		ret = libertas_find_best_network_SSID(priv, &assoc_req->ssid,
+				assoc_req->mode, &new_mode);
+		if (ret) {
+			lbs_pr_debug(1, "Could not find best network\n");
+			ret = -ENETUNREACH;
+			goto out;
+		}
+
+		/* Ensure we switch to the mode of the AP */
+		if (assoc_req->mode == wlan802_11autounknown) {
+			set_bit(ASSOC_FLAG_MODE, &assoc_req->flags);
+			assoc_req->mode = new_mode;
+		}
+	}
+
+	/*
+	 * Check if the attributes being changing require deauthentication
+	 * from the currently associated infrastructure access point.
+	 */
+	if (adapter->inframode == wlan802_11infrastructure) {
+		if (should_deauth_infrastructure(adapter, assoc_req)) {
+			ret = libertas_send_deauthentication(priv);
+			if (ret) {
+				lbs_pr_debug(1, "Deauthentication due to new "
+					"configuration request failed: %d\n",
+					ret);
+			}
+		}
+	} else if (adapter->inframode == wlan802_11ibss) {
+		if (should_stop_adhoc(adapter, assoc_req)) {
+			ret = libertas_stop_adhoc_network(priv);
+			if (ret) {
+				lbs_pr_debug(1, "Teardown of AdHoc network due to "
+					"new configuration request failed: %d\n",
+					ret);
+			}
+
+		}
+	}
+
+	/* Send the various configuration bits to the firmware */
+	if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
+		ret = assoc_helper_mode(priv, assoc_req);
+		if (ret) {
+lbs_pr_debug(1, "ASSOC(:%d) mode: ret = %d\n", __LINE__, ret);
+			goto out;
+		}
+	}
+
+	if (   test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)
+	    || test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) {
+		ret = assoc_helper_wep_keys(priv, assoc_req);
+		if (ret) {
+lbs_pr_debug(1, "ASSOC(:%d) wep_keys: ret = %d\n", __LINE__, ret);
+			goto out;
+		}
+	}
+
+	if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
+		ret = assoc_helper_secinfo(priv, assoc_req);
+		if (ret) {
+lbs_pr_debug(1, "ASSOC(:%d) secinfo: ret = %d\n", __LINE__, ret);
+			goto out;
+		}
+	}
+
+	if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
+		ret = assoc_helper_wpa_ie(priv, assoc_req);
+		if (ret) {
+lbs_pr_debug(1, "ASSOC(:%d) wpa_ie: ret = %d\n", __LINE__, ret);
+			goto out;
+		}
+	}
+
+	if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)
+	    || test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
+		ret = assoc_helper_wpa_keys(priv, assoc_req);
+		if (ret) {
+lbs_pr_debug(1, "ASSOC(:%d) wpa_keys: ret = %d\n", __LINE__, ret);
+			goto out;
+		}
+	}
+
+	/* SSID/BSSID should be the _last_ config option set, because they
+	 * trigger the association attempt.
+	 */
+	if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)
+	    || test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
+		int success = 1;
+
+		ret = assoc_helper_associate(priv, assoc_req);
+		if (ret) {
+			lbs_pr_debug(1, "ASSOC: association attempt unsuccessful: %d\n",
+				ret);
+			success = 0;
+		}
+
+		if (adapter->connect_status != libertas_connected) {
+			lbs_pr_debug(1, "ASSOC: assoication attempt unsuccessful, "
+				"not connected.\n");
+			success = 0;
+		}
+
+		if (success) {
+			lbs_pr_debug(1, "ASSOC: association attempt successful. "
+				"Associated to '%s' (" MAC_FMT ")\n",
+				assoc_req->ssid.ssid, MAC_ARG(assoc_req->bssid));
+			libertas_prepare_and_send_command(priv,
+				cmd_802_11_rssi,
+				0, cmd_option_waitforrsp, 0, NULL);
+
+			libertas_prepare_and_send_command(priv,
+				cmd_802_11_get_log,
+				0, cmd_option_waitforrsp, 0, NULL);
+		} else {
+
+			ret = -1;
+		}
+	}
+
+out:
+	if (ret) {
+		lbs_pr_debug(1, "ASSOC: reconfiguration attempt unsuccessful: %d\n",
+			ret);
+	}
+	kfree(assoc_req);
+	LEAVE();
+}
+
+
+/*
+ * Caller MUST hold any necessary locks
+ */
+struct assoc_request * wlan_get_association_request(wlan_adapter *adapter)
+{
+	struct assoc_request * assoc_req;
+
+	if (!adapter->assoc_req) {
+		adapter->assoc_req = kzalloc(sizeof(struct assoc_request), GFP_KERNEL);
+		if (!adapter->assoc_req) {
+			lbs_pr_info("Not enough memory to allocate association"
+				" request!\n");
+			return NULL;
+		}
+	}
+
+	/* Copy current configuration attributes to the association request,
+	 * but don't overwrite any that are already set.
+	 */
+	assoc_req = adapter->assoc_req;
+	if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
+		memcpy(&assoc_req->ssid, adapter->curbssparams.ssid.ssid,
+			adapter->curbssparams.ssid.ssidlength);
+	}
+
+	if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
+		assoc_req->channel = adapter->curbssparams.channel;
+
+	if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags))
+		assoc_req->mode = adapter->inframode;
+
+	if (!test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
+		memcpy(&assoc_req->bssid, adapter->curbssparams.bssid,
+			ETH_ALEN);
+	}
+
+	if (!test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)) {
+		int i;
+		for (i = 0; i < 4; i++) {
+			memcpy(&assoc_req->wep_keys[i], &adapter->wep_keys[i],
+				sizeof(struct WLAN_802_11_KEY));
+		}
+	}
+
+	if (!test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags))
+		assoc_req->wep_tx_keyidx = adapter->wep_tx_keyidx;
+
+	if (!test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
+		memcpy(&assoc_req->wpa_mcast_key, &adapter->wpa_mcast_key,
+			sizeof(struct WLAN_802_11_KEY));
+	}
+
+	if (!test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
+		memcpy(&assoc_req->wpa_unicast_key, &adapter->wpa_unicast_key,
+			sizeof(struct WLAN_802_11_KEY));
+	}
+
+	if (!test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
+		memcpy(&assoc_req->secinfo, &adapter->secinfo,
+			sizeof(struct wlan_802_11_security));
+	}
+
+	if (!test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
+		memcpy(&assoc_req->wpa_ie, &adapter->wpa_ie,
+			MAX_WPA_IE_LEN);
+		assoc_req->wpa_ie_len = adapter->wpa_ie_len;
+	}
+
+	return assoc_req;
+}
+
+
diff --git a/drivers/net/wireless/libertas/assoc.h b/drivers/net/wireless/libertas/assoc.h
new file mode 100644
index 0000000..2ffd82d
--- /dev/null
+++ b/drivers/net/wireless/libertas/assoc.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2006, Red Hat, Inc. */
+
+#ifndef _WLAN_ASSOC_H_
+#define _WLAN_ASSOC_H_
+
+#include "dev.h"
+
+void wlan_association_worker(struct work_struct *work);
+
+struct assoc_request * wlan_get_association_request(wlan_adapter *adapter);
+
+#define ASSOC_DELAY (HZ / 2)
+static inline void wlan_postpone_association_work(wlan_private *priv)
+{
+	if (priv->adapter->surpriseremoved)
+		return;
+	cancel_delayed_work(&priv->assoc_work);
+	queue_delayed_work(priv->assoc_thread, &priv->assoc_work, ASSOC_DELAY);
+}
+
+static inline void wlan_cancel_association_work(wlan_private *priv)
+{
+	cancel_delayed_work(&priv->assoc_work);
+	if (priv->adapter->assoc_req) {
+		kfree(priv->adapter->assoc_req);
+		priv->adapter->assoc_req = NULL;
+	}
+}
+
+#endif /* _WLAN_ASSOC_H */
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c
new file mode 100644
index 0000000..bfdac58
--- /dev/null
+++ b/drivers/net/wireless/libertas/cmd.c
@@ -0,0 +1,1958 @@
+/**
+  * This file contains the handling of command.
+  * It prepares command and sends it to firmware when it is ready.
+  */
+
+#include <net/iw_handler.h>
+#include "host.h"
+#include "hostcmd.h"
+#include "sbi.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "join.h"
+#include "wext.h"
+
+static void cleanup_cmdnode(struct cmd_ctrl_node *ptempnode);
+
+static u16 commands_allowed_in_ps[] = {
+	cmd_802_11_rssi,
+};
+
+/**
+ *  @brief This function checks if the commans is allowed
+ *  in PS mode not.
+ *
+ *  @param command the command ID
+ *  @return 	   TRUE or FALSE
+ */
+static u8 is_command_allowed_in_ps(u16 command)
+{
+	int count = sizeof(commands_allowed_in_ps)
+	    / sizeof(commands_allowed_in_ps[0]);
+	int i;
+
+	for (i = 0; i < count; i++) {
+		if (command == cpu_to_le16(commands_allowed_in_ps[i]))
+			return 1;
+	}
+
+	return 0;
+}
+
+static int wlan_cmd_hw_spec(wlan_private * priv, struct cmd_ds_command *cmd)
+{
+	struct cmd_ds_get_hw_spec *hwspec = &cmd->params.hwspec;
+
+	ENTER();
+
+	cmd->command = cpu_to_le16(cmd_get_hw_spec);
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_get_hw_spec) + S_DS_GEN);
+	memcpy(hwspec->permanentaddr, priv->adapter->current_addr, ETH_ALEN);
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_cmd_802_11_ps_mode(wlan_private * priv,
+				   struct cmd_ds_command *cmd,
+				   u16 cmd_action)
+{
+	struct cmd_ds_802_11_ps_mode *psm = &cmd->params.psmode;
+	u16 action = cmd_action;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	cmd->command = cpu_to_le16(cmd_802_11_ps_mode);
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_802_11_ps_mode) +
+			     S_DS_GEN);
+	psm->action = cpu_to_le16(cmd_action);
+	psm->multipledtim = 0;
+	switch (action) {
+	case cmd_subcmd_enter_ps:
+		lbs_pr_debug(1, "PS command:" "SubCode- Enter PS\n");
+		lbs_pr_debug(1, "locallisteninterval = %d\n",
+		       adapter->locallisteninterval);
+
+		psm->locallisteninterval =
+		    cpu_to_le16(adapter->locallisteninterval);
+		psm->nullpktinterval =
+		    cpu_to_le16(adapter->nullpktinterval);
+		psm->multipledtim =
+		    cpu_to_le16(priv->adapter->multipledtim);
+		break;
+
+	case cmd_subcmd_exit_ps:
+		lbs_pr_debug(1, "PS command:" "SubCode- Exit PS\n");
+		break;
+
+	case cmd_subcmd_sleep_confirmed:
+		lbs_pr_debug(1, "PS command: SubCode- sleep confirm\n");
+		break;
+
+	default:
+		break;
+	}
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_cmd_802_11_inactivity_timeout(wlan_private * priv,
+					      struct cmd_ds_command *cmd,
+					      u16 cmd_action, void *pdata_buf)
+{
+	u16 *timeout = pdata_buf;
+
+	cmd->command = cpu_to_le16(cmd_802_11_inactivity_timeout);
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_802_11_inactivity_timeout)
+			     + S_DS_GEN);
+
+	cmd->params.inactivity_timeout.action = cpu_to_le16(cmd_action);
+
+	if (cmd_action)
+		cmd->params.inactivity_timeout.timeout =
+		    cpu_to_le16(*timeout);
+	else
+		cmd->params.inactivity_timeout.timeout = 0;
+
+	return 0;
+}
+
+static int wlan_cmd_802_11_sleep_params(wlan_private * priv,
+					struct cmd_ds_command *cmd,
+					u16 cmd_action)
+{
+	wlan_adapter *adapter = priv->adapter;
+	struct cmd_ds_802_11_sleep_params *sp = &cmd->params.sleep_params;
+
+	ENTER();
+
+	cmd->size =
+	    cpu_to_le16((sizeof(struct cmd_ds_802_11_sleep_params)) +
+			     S_DS_GEN);
+	cmd->command = cpu_to_le16(cmd_802_11_sleep_params);
+
+	if (cmd_action == cmd_act_get) {
+		memset(&adapter->sp, 0, sizeof(struct sleep_params));
+		memset(sp, 0, sizeof(struct cmd_ds_802_11_sleep_params));
+		sp->action = cpu_to_le16(cmd_action);
+	} else if (cmd_action == cmd_act_set) {
+		sp->action = cpu_to_le16(cmd_action);
+		sp->error = cpu_to_le16(adapter->sp.sp_error);
+		sp->offset = cpu_to_le16(adapter->sp.sp_offset);
+		sp->stabletime = cpu_to_le16(adapter->sp.sp_stabletime);
+		sp->calcontrol = (u8) adapter->sp.sp_calcontrol;
+		sp->externalsleepclk = (u8) adapter->sp.sp_extsleepclk;
+		sp->reserved = cpu_to_le16(adapter->sp.sp_reserved);
+	}
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_cmd_802_11_set_wep(wlan_private * priv,
+                                   struct cmd_ds_command *cmd,
+                                   u32 cmd_act,
+                                   void * pdata_buf)
+{
+	struct cmd_ds_802_11_set_wep *wep = &cmd->params.wep;
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+	struct assoc_request * assoc_req = pdata_buf;
+
+	ENTER();
+
+	cmd->command = cpu_to_le16(cmd_802_11_set_wep);
+	cmd->size = cpu_to_le16((sizeof(struct cmd_ds_802_11_set_wep))
+	                             + S_DS_GEN);
+
+	if (cmd_act == cmd_act_add) {
+		int i;
+
+		if (!assoc_req) {
+			lbs_pr_debug(1, "Invalid association request!");
+			ret = -1;
+			goto done;
+		}
+
+		wep->action = cpu_to_le16(cmd_act_add);
+
+		/* default tx key index */
+		wep->keyindex = cpu_to_le16((u16)
+				                 (assoc_req->wep_tx_keyidx &
+				                 (u32)cmd_WEP_KEY_INDEX_MASK));
+
+		lbs_pr_debug(1, "Tx key Index: %u\n", wep->keyindex);
+
+		/* Copy key types and material to host command structure */
+		for (i = 0; i < 4; i++) {
+			struct WLAN_802_11_KEY * pkey = &assoc_req->wep_keys[i];
+
+			switch (pkey->len) {
+			case KEY_LEN_WEP_40:
+				wep->keytype[i] = cmd_type_wep_40_bit;
+				memmove(&wep->keymaterial[i], pkey->key,
+				        pkey->len);
+				break;
+			case KEY_LEN_WEP_104:
+				wep->keytype[i] = cmd_type_wep_104_bit;
+				memmove(&wep->keymaterial[i], pkey->key,
+				        pkey->len);
+				break;
+			case 0:
+				break;
+			default:
+				lbs_pr_debug(1, "Invalid WEP key %d length of %d\n",
+				       i, pkey->len);
+				ret = -1;
+				goto done;
+				break;
+			}
+		}
+	} else if (cmd_act == cmd_act_remove) {
+		/* ACT_REMOVE clears _all_ WEP keys */
+		wep->action = cpu_to_le16(cmd_act_remove);
+
+		/* default tx key index */
+		wep->keyindex = cpu_to_le16((u16)
+				                 (adapter->wep_tx_keyidx &
+				                 (u32)cmd_WEP_KEY_INDEX_MASK));
+	}
+
+	ret = 0;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+static int wlan_cmd_802_11_enable_rsn(wlan_private * priv,
+				      struct cmd_ds_command *cmd,
+				      u16 cmd_action)
+{
+	struct cmd_ds_802_11_enable_rsn *penableRSN = &cmd->params.enbrsn;
+	wlan_adapter *adapter = priv->adapter;
+
+	cmd->command = cpu_to_le16(cmd_802_11_enable_rsn);
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_802_11_enable_rsn) +
+			     S_DS_GEN);
+	penableRSN->action = cpu_to_le16(cmd_action);
+	if (adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) {
+		penableRSN->enable = cpu_to_le16(cmd_enable_rsn);
+	} else {
+		penableRSN->enable = cpu_to_le16(cmd_disable_rsn);
+	}
+
+	return 0;
+}
+
+
+static void set_one_wpa_key(struct MrvlIEtype_keyParamSet * pkeyparamset,
+                            struct WLAN_802_11_KEY * pkey)
+{
+	pkeyparamset->keytypeid = cpu_to_le16(pkey->type);
+
+	if (pkey->flags & KEY_INFO_WPA_ENABLED) {
+		pkeyparamset->keyinfo = cpu_to_le16(KEY_INFO_WPA_ENABLED);
+	} else {
+		pkeyparamset->keyinfo = cpu_to_le16(!KEY_INFO_WPA_ENABLED);
+	}
+
+	if (pkey->flags & KEY_INFO_WPA_UNICAST) {
+		pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_UNICAST);
+	} else if (pkey->flags & KEY_INFO_WPA_MCAST) {
+		pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_MCAST);
+	}
+
+	pkeyparamset->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL);
+	pkeyparamset->keylen = cpu_to_le16(pkey->len);
+	memcpy(pkeyparamset->key, pkey->key, pkey->len);
+	pkeyparamset->length = cpu_to_le16(  sizeof(pkeyparamset->keytypeid)
+	                                        + sizeof(pkeyparamset->keyinfo)
+	                                        + sizeof(pkeyparamset->keylen)
+	                                        + sizeof(pkeyparamset->key));
+}
+
+static int wlan_cmd_802_11_key_material(wlan_private * priv,
+					struct cmd_ds_command *cmd,
+					u16 cmd_action,
+					u32 cmd_oid, void *pdata_buf)
+{
+	wlan_adapter *adapter = priv->adapter;
+	struct cmd_ds_802_11_key_material *pkeymaterial =
+	    &cmd->params.keymaterial;
+	int ret = 0;
+	int index = 0;
+
+	ENTER();
+
+	cmd->command = cpu_to_le16(cmd_802_11_key_material);
+	pkeymaterial->action = cpu_to_le16(cmd_action);
+
+	if (cmd_action == cmd_act_get) {
+		cmd->size = cpu_to_le16(  S_DS_GEN
+		                             + sizeof (pkeymaterial->action));
+		ret = 0;
+		goto done;
+	}
+
+	memset(&pkeymaterial->keyParamSet, 0, sizeof(pkeymaterial->keyParamSet));
+
+	if (adapter->wpa_unicast_key.len) {
+		set_one_wpa_key(&pkeymaterial->keyParamSet[index],
+		                &adapter->wpa_unicast_key);
+		index++;
+	}
+
+	if (adapter->wpa_mcast_key.len) {
+		set_one_wpa_key(&pkeymaterial->keyParamSet[index],
+		                &adapter->wpa_mcast_key);
+		index++;
+	}
+
+	cmd->size = cpu_to_le16(  S_DS_GEN
+	                             + sizeof (pkeymaterial->action)
+	                             + index * sizeof(struct MrvlIEtype_keyParamSet));
+
+	ret = 0;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+static int wlan_cmd_802_11_reset(wlan_private * priv,
+				 struct cmd_ds_command *cmd, int cmd_action)
+{
+	struct cmd_ds_802_11_reset *reset = &cmd->params.reset;
+
+	cmd->command = cpu_to_le16(cmd_802_11_reset);
+	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_reset) + S_DS_GEN);
+	reset->action = cpu_to_le16(cmd_action);
+
+	return 0;
+}
+
+static int wlan_cmd_802_11_get_log(wlan_private * priv,
+				   struct cmd_ds_command *cmd)
+{
+	cmd->command = cpu_to_le16(cmd_802_11_get_log);
+	cmd->size =
+		cpu_to_le16(sizeof(struct cmd_ds_802_11_get_log) + S_DS_GEN);
+
+	return 0;
+}
+
+static int wlan_cmd_802_11_get_stat(wlan_private * priv,
+				    struct cmd_ds_command *cmd)
+{
+	cmd->command = cpu_to_le16(cmd_802_11_get_stat);
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_802_11_get_stat) +
+			     S_DS_GEN);
+
+	return 0;
+}
+
+static int wlan_cmd_802_11_snmp_mib(wlan_private * priv,
+				    struct cmd_ds_command *cmd,
+				    int cmd_action,
+				    int cmd_oid, void *pdata_buf)
+{
+	struct cmd_ds_802_11_snmp_mib *pSNMPMIB = &cmd->params.smib;
+	wlan_adapter *adapter = priv->adapter;
+	u8 ucTemp;
+
+	ENTER();
+
+	lbs_pr_debug(1, "SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid);
+
+	cmd->command = cpu_to_le16(cmd_802_11_snmp_mib);
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_802_11_snmp_mib) +
+			     S_DS_GEN);
+
+	switch (cmd_oid) {
+	case OID_802_11_INFRASTRUCTURE_MODE:
+	{
+		enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode =
+			(enum WLAN_802_11_NETWORK_INFRASTRUCTURE) pdata_buf;
+		pSNMPMIB->querytype = cpu_to_le16(cmd_act_set);
+		pSNMPMIB->oid = cpu_to_le16((u16) desired_bsstype_i);
+		pSNMPMIB->bufsize = sizeof(u8);
+		if (mode == wlan802_11infrastructure)
+			ucTemp = SNMP_MIB_VALUE_INFRA;
+		else
+			ucTemp = SNMP_MIB_VALUE_ADHOC;
+
+		memmove(pSNMPMIB->value, &ucTemp, sizeof(u8));
+
+		break;
+	}
+
+	case OID_802_11D_ENABLE:
+		{
+			u32 ulTemp;
+
+			pSNMPMIB->oid = cpu_to_le16((u16) dot11d_i);
+
+			if (cmd_action == cmd_act_set) {
+				pSNMPMIB->querytype = cmd_act_set;
+				pSNMPMIB->bufsize = sizeof(u16);
+				ulTemp = *(u32 *)pdata_buf;
+				*((unsigned short *)(pSNMPMIB->value)) =
+				    cpu_to_le16((u16) ulTemp);
+			}
+			break;
+		}
+
+	case OID_802_11_FRAGMENTATION_THRESHOLD:
+		{
+			u32 ulTemp;
+
+			pSNMPMIB->oid = cpu_to_le16((u16) fragthresh_i);
+
+			if (cmd_action == cmd_act_get) {
+				pSNMPMIB->querytype =
+				    cpu_to_le16(cmd_act_get);
+			} else if (cmd_action == cmd_act_set) {
+				pSNMPMIB->querytype =
+				    cpu_to_le16(cmd_act_set);
+				pSNMPMIB->bufsize =
+				    cpu_to_le16(sizeof(u16));
+				ulTemp = *((u32 *) pdata_buf);
+				*((unsigned short *)(pSNMPMIB->value)) =
+				    cpu_to_le16((u16) ulTemp);
+
+			}
+
+			break;
+		}
+
+	case OID_802_11_RTS_THRESHOLD:
+		{
+
+			u32 ulTemp;
+			pSNMPMIB->oid = le16_to_cpu((u16) rtsthresh_i);
+
+			if (cmd_action == cmd_act_get) {
+				pSNMPMIB->querytype =
+				    cpu_to_le16(cmd_act_get);
+			} else if (cmd_action == cmd_act_set) {
+				pSNMPMIB->querytype =
+				    cpu_to_le16(cmd_act_set);
+				pSNMPMIB->bufsize =
+				    cpu_to_le16(sizeof(u16));
+				ulTemp = *((u32 *)
+					   pdata_buf);
+				*(unsigned short *)(pSNMPMIB->value) =
+				    cpu_to_le16((u16) ulTemp);
+
+			}
+			break;
+		}
+	case OID_802_11_TX_RETRYCOUNT:
+		pSNMPMIB->oid = cpu_to_le16((u16) short_retrylim_i);
+
+		if (cmd_action == cmd_act_get) {
+			pSNMPMIB->querytype =
+			    cpu_to_le16(cmd_act_get);
+		} else if (cmd_action == cmd_act_set) {
+			pSNMPMIB->querytype =
+			    cpu_to_le16(cmd_act_set);
+			pSNMPMIB->bufsize = cpu_to_le16(sizeof(u16));
+			*((unsigned short *)(pSNMPMIB->value)) =
+			    cpu_to_le16((u16) adapter->txretrycount);
+		}
+
+		break;
+	default:
+		break;
+	}
+
+	lbs_pr_debug(1,
+	       "SNMP_CMD: command=0x%x, size=0x%x, seqnum=0x%x, result=0x%x\n",
+	       cmd->command, cmd->size, cmd->seqnum, cmd->result);
+
+	lbs_pr_debug(1,
+	       "SNMP_CMD: action=0x%x, oid=0x%x, oidsize=0x%x, value=0x%x\n",
+	       pSNMPMIB->querytype, pSNMPMIB->oid, pSNMPMIB->bufsize,
+	       *(u16 *) pSNMPMIB->value);
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_cmd_802_11_radio_control(wlan_private * priv,
+					 struct cmd_ds_command *cmd,
+					 int cmd_action)
+{
+	wlan_adapter *adapter = priv->adapter;
+	struct cmd_ds_802_11_radio_control *pradiocontrol =
+	    &cmd->params.radio;
+
+	ENTER();
+
+	cmd->size =
+	    cpu_to_le16((sizeof(struct cmd_ds_802_11_radio_control)) +
+			     S_DS_GEN);
+	cmd->command = cpu_to_le16(cmd_802_11_radio_control);
+
+	pradiocontrol->action = cpu_to_le16(cmd_action);
+
+	switch (adapter->preamble) {
+	case cmd_type_short_preamble:
+		pradiocontrol->control = cpu_to_le16(SET_SHORT_PREAMBLE);
+		break;
+
+	case cmd_type_long_preamble:
+		pradiocontrol->control = cpu_to_le16(SET_LONG_PREAMBLE);
+		break;
+
+	case cmd_type_auto_preamble:
+	default:
+		pradiocontrol->control = cpu_to_le16(SET_AUTO_PREAMBLE);
+		break;
+	}
+
+	if (adapter->radioon)
+		pradiocontrol->control |= cpu_to_le16(TURN_ON_RF);
+	else
+		pradiocontrol->control &= cpu_to_le16(~TURN_ON_RF);
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_cmd_802_11_rf_tx_power(wlan_private * priv,
+				       struct cmd_ds_command *cmd,
+				       u16 cmd_action, void *pdata_buf)
+{
+
+	struct cmd_ds_802_11_rf_tx_power *prtp = &cmd->params.txp;
+
+	ENTER();
+
+	cmd->size =
+	    cpu_to_le16((sizeof(struct cmd_ds_802_11_rf_tx_power)) +
+			     S_DS_GEN);
+	cmd->command = cpu_to_le16(cmd_802_11_rf_tx_power);
+	prtp->action = cmd_action;
+
+	lbs_pr_debug(1, "RF_TX_POWER_CMD: size:%d cmd:0x%x Act:%d\n", cmd->size,
+	       cmd->command, prtp->action);
+
+	switch (cmd_action) {
+	case cmd_act_tx_power_opt_get:
+		prtp->action = cpu_to_le16(cmd_act_get);
+		prtp->currentlevel = 0;
+		break;
+
+	case cmd_act_tx_power_opt_set_high:
+		prtp->action = cpu_to_le16(cmd_act_set);
+		prtp->currentlevel =
+		    cpu_to_le16(cmd_act_tx_power_index_high);
+		break;
+
+	case cmd_act_tx_power_opt_set_mid:
+		prtp->action = cpu_to_le16(cmd_act_set);
+		prtp->currentlevel =
+		    cpu_to_le16(cmd_act_tx_power_index_mid);
+		break;
+
+	case cmd_act_tx_power_opt_set_low:
+		prtp->action = cpu_to_le16(cmd_act_set);
+		prtp->currentlevel = cpu_to_le16(*((u16 *) pdata_buf));
+		break;
+	}
+	LEAVE();
+	return 0;
+}
+
+static int wlan_cmd_802_11_rf_antenna(wlan_private * priv,
+				      struct cmd_ds_command *cmd,
+				      u16 cmd_action, void *pdata_buf)
+{
+	struct cmd_ds_802_11_rf_antenna *rant = &cmd->params.rant;
+
+	cmd->command = cpu_to_le16(cmd_802_11_rf_antenna);
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_802_11_rf_antenna) +
+			     S_DS_GEN);
+
+	rant->action = cpu_to_le16(cmd_action);
+	if ((cmd_action == cmd_act_set_rx) ||
+	    (cmd_action == cmd_act_set_tx)) {
+		rant->antennamode =
+		    cpu_to_le16((u16) (*(u32 *) pdata_buf));
+	}
+
+	return 0;
+}
+
+static int wlan_cmd_802_11_rate_adapt_rateset(wlan_private * priv,
+					      struct cmd_ds_command *cmd,
+					      u16 cmd_action)
+{
+	struct cmd_ds_802_11_rate_adapt_rateset
+	*rateadapt = &cmd->params.rateset;
+	wlan_adapter *adapter = priv->adapter;
+
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_802_11_rate_adapt_rateset)
+			     + S_DS_GEN);
+	cmd->command = cpu_to_le16(cmd_802_11_rate_adapt_rateset);
+
+	ENTER();
+
+	rateadapt->action = cmd_action;
+	rateadapt->enablehwauto = adapter->enablehwauto;
+	rateadapt->bitmap = adapter->ratebitmap;
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_cmd_802_11_data_rate(wlan_private * priv,
+				     struct cmd_ds_command *cmd,
+				     u16 cmd_action)
+{
+	struct cmd_ds_802_11_data_rate *pdatarate = &cmd->params.drate;
+	wlan_adapter *adapter = priv->adapter;
+	u16 action = cmd_action;
+
+	ENTER();
+
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_802_11_data_rate) +
+			     S_DS_GEN);
+
+	cmd->command = cpu_to_le16(cmd_802_11_data_rate);
+
+	memset(pdatarate, 0, sizeof(struct cmd_ds_802_11_data_rate));
+
+	pdatarate->action = cpu_to_le16(cmd_action);
+
+	if (action == cmd_act_set_tx_fix_rate) {
+		pdatarate->datarate[0] = libertas_data_rate_to_index(adapter->datarate);
+		lbs_pr_debug(1, "Setting FW for fixed rate 0x%02X\n",
+		       adapter->datarate);
+	} else if (action == cmd_act_set_tx_auto) {
+		lbs_pr_debug(1, "Setting FW for AUTO rate\n");
+	}
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_cmd_mac_multicast_adr(wlan_private * priv,
+				      struct cmd_ds_command *cmd,
+				      u16 cmd_action)
+{
+	struct cmd_ds_mac_multicast_adr *pMCastAdr = &cmd->params.madr;
+	wlan_adapter *adapter = priv->adapter;
+
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_mac_multicast_adr) +
+			     S_DS_GEN);
+	cmd->command = cpu_to_le16(cmd_mac_multicast_adr);
+
+	pMCastAdr->action = cpu_to_le16(cmd_action);
+	pMCastAdr->nr_of_adrs =
+	    cpu_to_le16((u16) adapter->nr_of_multicastmacaddr);
+	memcpy(pMCastAdr->maclist, adapter->multicastlist,
+	       adapter->nr_of_multicastmacaddr * ETH_ALEN);
+
+	return 0;
+}
+
+static int wlan_cmd_802_11_rf_channel(wlan_private * priv,
+				      struct cmd_ds_command *cmd,
+				      int option, void *pdata_buf)
+{
+	struct cmd_ds_802_11_rf_channel *rfchan = &cmd->params.rfchannel;
+
+	cmd->command = cpu_to_le16(cmd_802_11_rf_channel);
+	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_rf_channel)
+				     + S_DS_GEN);
+
+	if (option == cmd_opt_802_11_rf_channel_set) {
+		rfchan->currentchannel = cpu_to_le16(*((u16 *) pdata_buf));
+	}
+
+	rfchan->action = cpu_to_le16(option);
+
+	return 0;
+}
+
+static int wlan_cmd_802_11_rssi(wlan_private * priv,
+				struct cmd_ds_command *cmd)
+{
+	wlan_adapter *adapter = priv->adapter;
+
+	cmd->command = cpu_to_le16(cmd_802_11_rssi);
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_802_11_rssi) + S_DS_GEN);
+	cmd->params.rssi.N = priv->adapter->bcn_avg_factor;
+
+	/* reset Beacon SNR/NF/RSSI values */
+	adapter->SNR[TYPE_BEACON][TYPE_NOAVG] = 0;
+	adapter->SNR[TYPE_BEACON][TYPE_AVG] = 0;
+	adapter->NF[TYPE_BEACON][TYPE_NOAVG] = 0;
+	adapter->NF[TYPE_BEACON][TYPE_AVG] = 0;
+	adapter->RSSI[TYPE_BEACON][TYPE_NOAVG] = 0;
+	adapter->RSSI[TYPE_BEACON][TYPE_AVG] = 0;
+
+	return 0;
+}
+
+static int wlan_cmd_reg_access(wlan_private * priv,
+			       struct cmd_ds_command *cmdptr,
+			       u8 cmd_action, void *pdata_buf)
+{
+	struct wlan_offset_value *offval;
+
+	ENTER();
+
+	offval = (struct wlan_offset_value *)pdata_buf;
+
+	switch (cmdptr->command) {
+	case cmd_mac_reg_access:
+		{
+			struct cmd_ds_mac_reg_access *macreg;
+
+			cmdptr->size =
+			    cpu_to_le16(sizeof
+					     (struct cmd_ds_mac_reg_access)
+					     + S_DS_GEN);
+			macreg =
+			    (struct cmd_ds_mac_reg_access *)&cmdptr->params.
+			    macreg;
+
+			macreg->action = cpu_to_le16(cmd_action);
+			macreg->offset = cpu_to_le16((u16) offval->offset);
+			macreg->value = cpu_to_le32(offval->value);
+
+			break;
+		}
+
+	case cmd_bbp_reg_access:
+		{
+			struct cmd_ds_bbp_reg_access *bbpreg;
+
+			cmdptr->size =
+			    cpu_to_le16(sizeof
+					     (struct cmd_ds_bbp_reg_access)
+					     + S_DS_GEN);
+			bbpreg =
+			    (struct cmd_ds_bbp_reg_access *)&cmdptr->params.
+			    bbpreg;
+
+			bbpreg->action = cpu_to_le16(cmd_action);
+			bbpreg->offset = cpu_to_le16((u16) offval->offset);
+			bbpreg->value = (u8) offval->value;
+
+			break;
+		}
+
+	case cmd_rf_reg_access:
+		{
+			struct cmd_ds_rf_reg_access *rfreg;
+
+			cmdptr->size =
+			    cpu_to_le16(sizeof
+					     (struct cmd_ds_rf_reg_access) +
+					     S_DS_GEN);
+			rfreg =
+			    (struct cmd_ds_rf_reg_access *)&cmdptr->params.
+			    rfreg;
+
+			rfreg->action = cpu_to_le16(cmd_action);
+			rfreg->offset = cpu_to_le16((u16) offval->offset);
+			rfreg->value = (u8) offval->value;
+
+			break;
+		}
+
+	default:
+		break;
+	}
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_cmd_802_11_mac_address(wlan_private * priv,
+				       struct cmd_ds_command *cmd,
+				       u16 cmd_action)
+{
+	wlan_adapter *adapter = priv->adapter;
+
+	cmd->command = cpu_to_le16(cmd_802_11_mac_address);
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_802_11_mac_address) +
+			     S_DS_GEN);
+	cmd->result = 0;
+
+	cmd->params.macadd.action = cpu_to_le16(cmd_action);
+
+	if (cmd_action == cmd_act_set) {
+		memcpy(cmd->params.macadd.macadd,
+		       adapter->current_addr, ETH_ALEN);
+		lbs_dbg_hex("SET_CMD: MAC ADDRESS-", adapter->current_addr, 6);
+	}
+
+	return 0;
+}
+
+static int wlan_cmd_802_11_eeprom_access(wlan_private * priv,
+					 struct cmd_ds_command *cmd,
+					 int cmd_action, void *pdata_buf)
+{
+	struct wlan_ioctl_regrdwr *ea = pdata_buf;
+
+	ENTER();
+
+	cmd->command = cpu_to_le16(cmd_802_11_eeprom_access);
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_802_11_eeprom_access) +
+			     S_DS_GEN);
+	cmd->result = 0;
+
+	cmd->params.rdeeprom.action = cpu_to_le16(ea->action);
+	cmd->params.rdeeprom.offset = cpu_to_le16(ea->offset);
+	cmd->params.rdeeprom.bytecount = cpu_to_le16(ea->NOB);
+	cmd->params.rdeeprom.value = 0;
+
+	return 0;
+}
+
+static int wlan_cmd_bt_access(wlan_private * priv,
+			       struct cmd_ds_command *cmd,
+			       u16 cmd_action, void *pdata_buf)
+{
+	struct cmd_ds_bt_access *bt_access = &cmd->params.bt;
+	lbs_pr_debug(1, "BT CMD(%d)\n", cmd_action);
+
+	cmd->command = cpu_to_le16(cmd_bt_access);
+	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_bt_access)
+				     + S_DS_GEN);
+	cmd->result = 0;
+	bt_access->action = cpu_to_le16(cmd_action);
+
+	switch (cmd_action) {
+	case cmd_act_bt_access_add:
+		memcpy(bt_access->addr1, pdata_buf, 2 * ETH_ALEN);
+		lbs_dbg_hex("BT_ADD: blinded mac address-", bt_access->addr1, 6);
+		break;
+	case cmd_act_bt_access_del:
+		memcpy(bt_access->addr1, pdata_buf, 1 * ETH_ALEN);
+		lbs_dbg_hex("BT_DEL: blinded mac address-", bt_access->addr1, 6);
+		break;
+	case cmd_act_bt_access_list:
+		bt_access->id = cpu_to_le32(*(u32 *) pdata_buf);
+		break;
+	case cmd_act_bt_access_reset:
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int wlan_cmd_fwt_access(wlan_private * priv,
+			       struct cmd_ds_command *cmd,
+			       u16 cmd_action, void *pdata_buf)
+{
+	struct cmd_ds_fwt_access *fwt_access = &cmd->params.fwt;
+	lbs_pr_debug(1, "FWT CMD(%d)\n", cmd_action);
+
+	cmd->command = cpu_to_le16(cmd_fwt_access);
+	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_fwt_access)
+				     + S_DS_GEN);
+	cmd->result = 0;
+
+	if (pdata_buf)
+		memcpy(fwt_access, pdata_buf, sizeof(*fwt_access));
+	else
+		memset(fwt_access, 0, sizeof(*fwt_access));
+
+	fwt_access->action = cpu_to_le16(cmd_action);
+
+	return 0;
+}
+
+static int wlan_cmd_mesh_access(wlan_private * priv,
+				struct cmd_ds_command *cmd,
+				u16 cmd_action, void *pdata_buf)
+{
+	struct cmd_ds_mesh_access *mesh_access = &cmd->params.mesh;
+	lbs_pr_debug(1, "FWT CMD(%d)\n", cmd_action);
+
+	cmd->command = cpu_to_le16(cmd_mesh_access);
+	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_mesh_access)
+				     + S_DS_GEN);
+	cmd->result = 0;
+
+	if (pdata_buf)
+		memcpy(mesh_access, pdata_buf, sizeof(*mesh_access));
+	else
+		memset(mesh_access, 0, sizeof(*mesh_access));
+
+	mesh_access->action = cpu_to_le16(cmd_action);
+
+	return 0;
+}
+
+void libertas_queue_cmd(wlan_adapter * adapter, struct cmd_ctrl_node *cmdnode, u8 addtail)
+{
+	unsigned long flags;
+	struct cmd_ds_command *cmdptr;
+
+	ENTER();
+
+	if (!cmdnode) {
+		lbs_pr_debug(1, "QUEUE_CMD: cmdnode is NULL\n");
+		goto done;
+	}
+
+	cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr;
+	if (!cmdptr) {
+		lbs_pr_debug(1, "QUEUE_CMD: cmdptr is NULL\n");
+		goto done;
+	}
+
+	/* Exit_PS command needs to be queued in the header always. */
+	if (cmdptr->command == cmd_802_11_ps_mode) {
+		struct cmd_ds_802_11_ps_mode *psm = &cmdptr->params.psmode;
+		if (psm->action == cmd_subcmd_exit_ps) {
+			if (adapter->psstate != PS_STATE_FULL_POWER)
+				addtail = 0;
+		}
+	}
+
+	spin_lock_irqsave(&adapter->driver_lock, flags);
+
+	if (addtail)
+		list_add_tail((struct list_head *)cmdnode,
+			      &adapter->cmdpendingq);
+	else
+		list_add((struct list_head *)cmdnode, &adapter->cmdpendingq);
+
+	spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+	lbs_pr_debug(1, "QUEUE_CMD: Inserted node=0x%x, cmd=0x%x in cmdpendingq\n",
+	       (u32) cmdnode,
+	       ((struct cmd_ds_gen*)cmdnode->bufvirtualaddr)->command);
+
+done:
+	LEAVE();
+	return;
+}
+
+/*
+ * TODO: Fix the issue when DownloadcommandToStation is being called the
+ * second time when the command timesout. All the cmdptr->xxx are in little
+ * endian and therefore all the comparissions will fail.
+ * For now - we are not performing the endian conversion the second time - but
+ * for PS and DEEP_SLEEP we need to worry
+ */
+static int DownloadcommandToStation(wlan_private * priv,
+				    struct cmd_ctrl_node *cmdnode)
+{
+	unsigned long flags;
+	struct cmd_ds_command *cmdptr;
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+	u16 cmdsize;
+	u16 command;
+
+	ENTER();
+
+	if (!adapter || !cmdnode) {
+		lbs_pr_debug(1, "DNLD_CMD: adapter = %#x, cmdnode = %#x\n",
+		       (int)adapter, (int)cmdnode);
+		if (cmdnode) {
+			spin_lock_irqsave(&adapter->driver_lock, flags);
+			__libertas_cleanup_and_insert_cmd(priv, cmdnode);
+			spin_unlock_irqrestore(&adapter->driver_lock, flags);
+		}
+		ret = -1;
+		goto done;
+	}
+
+	cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr;
+
+
+	spin_lock_irqsave(&adapter->driver_lock, flags);
+	if (!cmdptr || !cmdptr->size) {
+		lbs_pr_debug(1, "DNLD_CMD: cmdptr is Null or cmd size is Zero, "
+		       "Not sending\n");
+		__libertas_cleanup_and_insert_cmd(priv, cmdnode);
+		spin_unlock_irqrestore(&adapter->driver_lock, flags);
+		ret = -1;
+		goto done;
+	}
+
+	adapter->cur_cmd = cmdnode;
+	adapter->cur_cmd_retcode = 0;
+	spin_unlock_irqrestore(&adapter->driver_lock, flags);
+	lbs_pr_debug(1, "DNLD_CMD:: Before download, size of cmd = %d\n",
+	       cmdptr->size);
+
+	cmdsize = cmdptr->size;
+
+	command = cpu_to_le16(cmdptr->command);
+
+	cmdnode->cmdwaitqwoken = 0;
+	cmdsize = cpu_to_le16(cmdsize);
+
+	ret = libertas_sbi_host_to_card(priv, MVMS_CMD, (u8 *) cmdptr, cmdsize);
+
+	if (ret != 0) {
+		lbs_pr_debug(1, "DNLD_CMD: Host to Card failed\n");
+		spin_lock_irqsave(&adapter->driver_lock, flags);
+		__libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd);
+		adapter->cur_cmd = NULL;
+		spin_unlock_irqrestore(&adapter->driver_lock, flags);
+		ret = -1;
+		goto done;
+	}
+
+	lbs_pr_debug(1, "DNLD_CMD: Sent command 0x%x @ %lu\n", command, jiffies);
+	lbs_dbg_hex("DNLD_CMD: command", cmdnode->bufvirtualaddr, cmdsize);
+
+	/* Setup the timer after transmit command */
+	if (command == cmd_802_11_scan
+	    || command == cmd_802_11_authenticate
+	    || command == cmd_802_11_associate)
+		mod_timer(&adapter->command_timer, jiffies + (10*HZ));
+	else
+		mod_timer(&adapter->command_timer, jiffies + (5*HZ));
+
+	ret = 0;
+
+      done:
+	LEAVE();
+	return ret;
+}
+
+static int wlan_cmd_mac_control(wlan_private * priv,
+				struct cmd_ds_command *cmd)
+{
+	struct cmd_ds_mac_control *mac = &cmd->params.macctrl;
+
+	ENTER();
+
+	cmd->command = cpu_to_le16(cmd_mac_control);
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_mac_control) + S_DS_GEN);
+	mac->action = cpu_to_le16(priv->adapter->currentpacketfilter);
+
+	lbs_pr_debug(1, "wlan_cmd_mac_control(): action=0x%X size=%d\n",
+	       mac->action, cmd->size);
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  This function inserts command node to cmdfreeq
+ *  after cleans it. Requires adapter->driver_lock held.
+ */
+void __libertas_cleanup_and_insert_cmd(wlan_private * priv, struct cmd_ctrl_node *ptempcmd)
+{
+	wlan_adapter *adapter = priv->adapter;
+
+	if (!ptempcmd)
+		goto done;
+
+	cleanup_cmdnode(ptempcmd);
+	list_add_tail((struct list_head *)ptempcmd, &adapter->cmdfreeq);
+done:
+	return;
+}
+
+void libertas_cleanup_and_insert_cmd(wlan_private * priv, struct cmd_ctrl_node *ptempcmd)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->adapter->driver_lock, flags);
+	__libertas_cleanup_and_insert_cmd(priv, ptempcmd);
+	spin_unlock_irqrestore(&priv->adapter->driver_lock, flags);
+}
+
+int libertas_set_radio_control(wlan_private * priv)
+{
+	int ret = 0;
+
+	ENTER();
+
+	ret = libertas_prepare_and_send_command(priv,
+				    cmd_802_11_radio_control,
+				    cmd_act_set,
+				    cmd_option_waitforrsp, 0, NULL);
+
+	lbs_pr_debug(1, "RADIO_SET: on or off: 0x%X, preamble = 0x%X\n",
+	       priv->adapter->radioon, priv->adapter->preamble);
+
+	LEAVE();
+	return ret;
+}
+
+int libertas_set_mac_packet_filter(wlan_private * priv)
+{
+	int ret = 0;
+
+	ENTER();
+
+	lbs_pr_debug(1, "libertas_set_mac_packet_filter value = %x\n",
+	       priv->adapter->currentpacketfilter);
+
+	/* Send MAC control command to station */
+	ret = libertas_prepare_and_send_command(priv,
+				    cmd_mac_control, 0, 0, 0, NULL);
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function prepare the command before send to firmware.
+ *
+ *  @param priv		A pointer to wlan_private structure
+ *  @param cmd_no	command number
+ *  @param cmd_action	command action: GET or SET
+ *  @param wait_option	wait option: wait response or not
+ *  @param cmd_oid	cmd oid: treated as sub command
+ *  @param pdata_buf	A pointer to informaion buffer
+ *  @return 		0 or -1
+ */
+int libertas_prepare_and_send_command(wlan_private * priv,
+			  u16 cmd_no,
+			  u16 cmd_action,
+			  u16 wait_option, u32 cmd_oid, void *pdata_buf)
+{
+	int ret = 0;
+	wlan_adapter *adapter = priv->adapter;
+	struct cmd_ctrl_node *cmdnode;
+	struct cmd_ds_command *cmdptr;
+	unsigned long flags;
+
+	ENTER();
+
+	if (!adapter) {
+		lbs_pr_debug(1, "PREP_CMD: adapter is Null\n");
+		ret = -1;
+		goto done;
+	}
+
+	if (adapter->surpriseremoved) {
+		lbs_pr_debug(1, "PREP_CMD: Card is Removed\n");
+		ret = -1;
+		goto done;
+	}
+
+	cmdnode = libertas_get_free_cmd_ctrl_node(priv);
+
+	if (cmdnode == NULL) {
+		lbs_pr_debug(1, "PREP_CMD: No free cmdnode\n");
+
+		/* Wake up main thread to execute next command */
+		wake_up_interruptible(&priv->mainthread.waitq);
+		ret = -1;
+		goto done;
+	}
+
+	libertas_set_cmd_ctrl_node(priv, cmdnode, cmd_oid, wait_option, pdata_buf);
+
+	cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr;
+
+	lbs_pr_debug(1, "PREP_CMD: Val of cmd ptr =0x%x, command=0x%X\n",
+	       (u32) cmdptr, cmd_no);
+
+	if (!cmdptr) {
+		lbs_pr_debug(1, "PREP_CMD: bufvirtualaddr of cmdnode is NULL\n");
+		libertas_cleanup_and_insert_cmd(priv, cmdnode);
+		ret = -1;
+		goto done;
+	}
+
+	/* Set sequence number, command and INT option */
+	adapter->seqnum++;
+	cmdptr->seqnum = cpu_to_le16(adapter->seqnum);
+
+	cmdptr->command = cmd_no;
+	cmdptr->result = 0;
+
+	switch (cmd_no) {
+	case cmd_get_hw_spec:
+		ret = wlan_cmd_hw_spec(priv, cmdptr);
+		break;
+	case cmd_802_11_ps_mode:
+		ret = wlan_cmd_802_11_ps_mode(priv, cmdptr, cmd_action);
+		break;
+
+	case cmd_802_11_scan:
+		ret = libertas_cmd_80211_scan(priv, cmdptr, pdata_buf);
+		break;
+
+	case cmd_mac_control:
+		ret = wlan_cmd_mac_control(priv, cmdptr);
+		break;
+
+	case cmd_802_11_associate:
+	case cmd_802_11_reassociate:
+		ret = libertas_cmd_80211_associate(priv, cmdptr, pdata_buf);
+		break;
+
+	case cmd_802_11_deauthenticate:
+		ret = libertas_cmd_80211_deauthenticate(priv, cmdptr);
+		break;
+
+	case cmd_802_11_set_wep:
+		ret = wlan_cmd_802_11_set_wep(priv, cmdptr, cmd_action, pdata_buf);
+		break;
+
+	case cmd_802_11_ad_hoc_start:
+		ret = libertas_cmd_80211_ad_hoc_start(priv, cmdptr, pdata_buf);
+		break;
+	case cmd_code_dnld:
+		break;
+
+	case cmd_802_11_reset:
+		ret = wlan_cmd_802_11_reset(priv, cmdptr, cmd_action);
+		break;
+
+	case cmd_802_11_get_log:
+		ret = wlan_cmd_802_11_get_log(priv, cmdptr);
+		break;
+
+	case cmd_802_11_authenticate:
+		ret = libertas_cmd_80211_authenticate(priv, cmdptr, pdata_buf);
+		break;
+
+	case cmd_802_11_get_stat:
+		ret = wlan_cmd_802_11_get_stat(priv, cmdptr);
+		break;
+
+	case cmd_802_11_snmp_mib:
+		ret = wlan_cmd_802_11_snmp_mib(priv, cmdptr,
+					       cmd_action, cmd_oid, pdata_buf);
+		break;
+
+	case cmd_mac_reg_access:
+	case cmd_bbp_reg_access:
+	case cmd_rf_reg_access:
+		ret = wlan_cmd_reg_access(priv, cmdptr, cmd_action, pdata_buf);
+		break;
+
+	case cmd_802_11_rf_channel:
+		ret = wlan_cmd_802_11_rf_channel(priv, cmdptr,
+						 cmd_action, pdata_buf);
+		break;
+
+	case cmd_802_11_rf_tx_power:
+		ret = wlan_cmd_802_11_rf_tx_power(priv, cmdptr,
+						  cmd_action, pdata_buf);
+		break;
+
+	case cmd_802_11_radio_control:
+		ret = wlan_cmd_802_11_radio_control(priv, cmdptr, cmd_action);
+		break;
+
+	case cmd_802_11_rf_antenna:
+		ret = wlan_cmd_802_11_rf_antenna(priv, cmdptr,
+						 cmd_action, pdata_buf);
+		break;
+
+	case cmd_802_11_data_rate:
+		ret = wlan_cmd_802_11_data_rate(priv, cmdptr, cmd_action);
+		break;
+	case cmd_802_11_rate_adapt_rateset:
+		ret = wlan_cmd_802_11_rate_adapt_rateset(priv,
+							 cmdptr, cmd_action);
+		break;
+
+	case cmd_mac_multicast_adr:
+		ret = wlan_cmd_mac_multicast_adr(priv, cmdptr, cmd_action);
+		break;
+
+	case cmd_802_11_ad_hoc_join:
+		ret = libertas_cmd_80211_ad_hoc_join(priv, cmdptr, pdata_buf);
+		break;
+
+	case cmd_802_11_rssi:
+		ret = wlan_cmd_802_11_rssi(priv, cmdptr);
+		break;
+
+	case cmd_802_11_ad_hoc_stop:
+		ret = libertas_cmd_80211_ad_hoc_stop(priv, cmdptr);
+		break;
+
+	case cmd_802_11_enable_rsn:
+		ret = wlan_cmd_802_11_enable_rsn(priv, cmdptr, cmd_action);
+		break;
+
+	case cmd_802_11_key_material:
+		ret = wlan_cmd_802_11_key_material(priv, cmdptr,
+						   cmd_action, cmd_oid,
+						   pdata_buf);
+		break;
+
+	case cmd_802_11_pairwise_tsc:
+		break;
+	case cmd_802_11_group_tsc:
+		break;
+
+	case cmd_802_11_mac_address:
+		ret = wlan_cmd_802_11_mac_address(priv, cmdptr, cmd_action);
+		break;
+
+	case cmd_802_11_eeprom_access:
+		ret = wlan_cmd_802_11_eeprom_access(priv, cmdptr,
+						    cmd_action, pdata_buf);
+		break;
+
+	case cmd_802_11_set_afc:
+	case cmd_802_11_get_afc:
+
+		cmdptr->command = cpu_to_le16(cmd_no);
+		cmdptr->size =
+		    cpu_to_le16(sizeof(struct cmd_ds_802_11_afc) +
+				     S_DS_GEN);
+
+		memmove(&cmdptr->params.afc,
+			pdata_buf, sizeof(struct cmd_ds_802_11_afc));
+
+		ret = 0;
+		goto done;
+
+	case cmd_802_11d_domain_info:
+		ret = libertas_cmd_802_11d_domain_info(priv, cmdptr,
+						   cmd_no, cmd_action);
+		break;
+
+	case cmd_802_11_sleep_params:
+		ret = wlan_cmd_802_11_sleep_params(priv, cmdptr, cmd_action);
+		break;
+	case cmd_802_11_inactivity_timeout:
+		ret = wlan_cmd_802_11_inactivity_timeout(priv, cmdptr,
+							 cmd_action, pdata_buf);
+		libertas_set_cmd_ctrl_node(priv, cmdnode, 0, 0, pdata_buf);
+		break;
+
+	case cmd_802_11_tpc_cfg:
+		cmdptr->command = cpu_to_le16(cmd_802_11_tpc_cfg);
+		cmdptr->size =
+		    cpu_to_le16(sizeof(struct cmd_ds_802_11_tpc_cfg) +
+				     S_DS_GEN);
+
+		memmove(&cmdptr->params.tpccfg,
+			pdata_buf, sizeof(struct cmd_ds_802_11_tpc_cfg));
+
+		ret = 0;
+		break;
+	case cmd_802_11_led_gpio_ctrl:
+		{
+			struct mrvlietypes_ledgpio *gpio =
+			    (struct mrvlietypes_ledgpio*)
+			    cmdptr->params.ledgpio.data;
+
+			memmove(&cmdptr->params.ledgpio,
+				pdata_buf,
+				sizeof(struct cmd_ds_802_11_led_ctrl));
+
+			cmdptr->command =
+			    cpu_to_le16(cmd_802_11_led_gpio_ctrl);
+
+#define ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN 8
+			cmdptr->size =
+			    cpu_to_le16(gpio->header.len + S_DS_GEN +
+					     ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN);
+			gpio->header.len = cpu_to_le16(gpio->header.len);
+
+			ret = 0;
+			break;
+		}
+	case cmd_802_11_pwr_cfg:
+		cmdptr->command = cpu_to_le16(cmd_802_11_pwr_cfg);
+		cmdptr->size =
+		    cpu_to_le16(sizeof(struct cmd_ds_802_11_pwr_cfg) +
+				     S_DS_GEN);
+		memmove(&cmdptr->params.pwrcfg, pdata_buf,
+			sizeof(struct cmd_ds_802_11_pwr_cfg));
+
+		ret = 0;
+		break;
+	case cmd_bt_access:
+		ret = wlan_cmd_bt_access(priv, cmdptr, cmd_action, pdata_buf);
+		break;
+
+	case cmd_fwt_access:
+		ret = wlan_cmd_fwt_access(priv, cmdptr, cmd_action, pdata_buf);
+		break;
+
+	case cmd_mesh_access:
+		ret = wlan_cmd_mesh_access(priv, cmdptr, cmd_action, pdata_buf);
+		break;
+
+	case cmd_get_tsf:
+		cmdptr->command = cpu_to_le16(cmd_get_tsf);
+		cmdptr->size =
+		    cpu_to_le16(sizeof(struct cmd_ds_get_tsf)
+				     + S_DS_GEN);
+		ret = 0;
+		break;
+	case cmd_802_11_tx_rate_query:
+		cmdptr->command =
+		    cpu_to_le16(cmd_802_11_tx_rate_query);
+		cmdptr->size =
+		    cpu_to_le16(sizeof(struct cmd_tx_rate_query) +
+				     S_DS_GEN);
+		adapter->txrate = 0;
+		ret = 0;
+		break;
+	default:
+		lbs_pr_debug(1, "PREP_CMD: unknown command- %#x\n", cmd_no);
+		ret = -1;
+		break;
+	}
+
+	/* return error, since the command preparation failed */
+	if (ret != 0) {
+		lbs_pr_debug(1, "PREP_CMD: command preparation failed\n");
+		libertas_cleanup_and_insert_cmd(priv, cmdnode);
+		ret = -1;
+		goto done;
+	}
+
+	cmdnode->cmdwaitqwoken = 0;
+
+	libertas_queue_cmd(adapter, cmdnode, 1);
+	adapter->nr_cmd_pending++;
+	wake_up_interruptible(&priv->mainthread.waitq);
+
+	if (wait_option & cmd_option_waitforrsp) {
+		lbs_pr_debug(1, "PREP_CMD: Wait for CMD response\n");
+		might_sleep();
+		wait_event_interruptible(cmdnode->cmdwait_q,
+					 cmdnode->cmdwaitqwoken);
+	}
+
+	spin_lock_irqsave(&adapter->driver_lock, flags);
+	if (adapter->cur_cmd_retcode) {
+		lbs_pr_debug(1, "PREP_CMD: command failed with return code=%d\n",
+		       adapter->cur_cmd_retcode);
+		adapter->cur_cmd_retcode = 0;
+		ret = -1;
+	}
+	spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function allocates the command buffer and link
+ *  it to command free queue.
+ *
+ *  @param priv		A pointer to wlan_private structure
+ *  @return 		0 or -1
+ */
+int libertas_allocate_cmd_buffer(wlan_private * priv)
+{
+	int ret = 0;
+	u32 ulbufsize;
+	u32 i;
+	struct cmd_ctrl_node *tempcmd_array;
+	u8 *ptempvirtualaddr;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	/* Allocate and initialize cmdCtrlNode */
+	ulbufsize = sizeof(struct cmd_ctrl_node) * MRVDRV_NUM_OF_CMD_BUFFER;
+
+	if (!(tempcmd_array = kmalloc(ulbufsize, GFP_KERNEL))) {
+		lbs_pr_debug(1,
+		       "ALLOC_CMD_BUF: failed to allocate tempcmd_array\n");
+		ret = -1;
+		goto done;
+	}
+
+	adapter->cmd_array = tempcmd_array;
+	memset(adapter->cmd_array, 0, ulbufsize);
+
+	/* Allocate and initialize command buffers */
+	ulbufsize = MRVDRV_SIZE_OF_CMD_BUFFER;
+	for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) {
+		if (!(ptempvirtualaddr = kmalloc(ulbufsize, GFP_KERNEL))) {
+			lbs_pr_debug(1,
+			       "ALLOC_CMD_BUF: ptempvirtualaddr: out of memory\n");
+			ret = -1;
+			goto done;
+		}
+
+		memset(ptempvirtualaddr, 0, ulbufsize);
+
+		/* Update command buffer virtual */
+		tempcmd_array[i].bufvirtualaddr = ptempvirtualaddr;
+	}
+
+	for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) {
+		init_waitqueue_head(&tempcmd_array[i].cmdwait_q);
+		libertas_cleanup_and_insert_cmd(priv, &tempcmd_array[i]);
+	}
+
+	ret = 0;
+      done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function frees the command buffer.
+ *
+ *  @param priv		A pointer to wlan_private structure
+ *  @return 		0 or -1
+ */
+int libertas_free_cmd_buffer(wlan_private * priv)
+{
+	u32 ulbufsize;
+	unsigned int i;
+	struct cmd_ctrl_node *tempcmd_array;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	/* need to check if cmd array is allocated or not */
+	if (adapter->cmd_array == NULL) {
+		lbs_pr_debug(1, "FREE_CMD_BUF: cmd_array is Null\n");
+		goto done;
+	}
+
+	tempcmd_array = adapter->cmd_array;
+
+	/* Release shared memory buffers */
+	ulbufsize = MRVDRV_SIZE_OF_CMD_BUFFER;
+	for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) {
+		if (tempcmd_array[i].bufvirtualaddr) {
+			lbs_pr_debug(1, "Free all the array\n");
+			kfree(tempcmd_array[i].bufvirtualaddr);
+			tempcmd_array[i].bufvirtualaddr = NULL;
+		}
+	}
+
+	/* Release cmd_ctrl_node */
+	if (adapter->cmd_array) {
+		lbs_pr_debug(1, "Free cmd_array\n");
+		kfree(adapter->cmd_array);
+		adapter->cmd_array = NULL;
+	}
+
+done:
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief This function gets a free command node if available in
+ *  command free queue.
+ *
+ *  @param priv		A pointer to wlan_private structure
+ *  @return cmd_ctrl_node A pointer to cmd_ctrl_node structure or NULL
+ */
+struct cmd_ctrl_node *libertas_get_free_cmd_ctrl_node(wlan_private * priv)
+{
+	struct cmd_ctrl_node *tempnode;
+	wlan_adapter *adapter = priv->adapter;
+	unsigned long flags;
+
+	if (!adapter)
+		return NULL;
+
+	spin_lock_irqsave(&adapter->driver_lock, flags);
+
+	if (!list_empty(&adapter->cmdfreeq)) {
+		tempnode = (struct cmd_ctrl_node *)adapter->cmdfreeq.next;
+		list_del((struct list_head *)tempnode);
+	} else {
+		lbs_pr_debug(1, "GET_CMD_NODE: cmd_ctrl_node is not available\n");
+		tempnode = NULL;
+	}
+
+	spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+	if (tempnode) {
+		lbs_pr_debug(3, "GET_CMD_NODE: cmdCtrlNode available\n");
+		lbs_pr_debug(3, "GET_CMD_NODE: cmdCtrlNode Address = %p\n",
+		       tempnode);
+		cleanup_cmdnode(tempnode);
+	}
+
+	return tempnode;
+}
+
+/**
+ *  @brief This function cleans command node.
+ *
+ *  @param ptempnode	A pointer to cmdCtrlNode structure
+ *  @return 		n/a
+ */
+static void cleanup_cmdnode(struct cmd_ctrl_node *ptempnode)
+{
+	if (!ptempnode)
+		return;
+	ptempnode->cmdwaitqwoken = 1;
+	wake_up_interruptible(&ptempnode->cmdwait_q);
+	ptempnode->status = 0;
+	ptempnode->cmd_oid = (u32) 0;
+	ptempnode->wait_option = 0;
+	ptempnode->pdata_buf = NULL;
+
+	if (ptempnode->bufvirtualaddr != NULL)
+		memset(ptempnode->bufvirtualaddr, 0, MRVDRV_SIZE_OF_CMD_BUFFER);
+	return;
+}
+
+/**
+ *  @brief This function initializes the command node.
+ *
+ *  @param priv		A pointer to wlan_private structure
+ *  @param ptempnode	A pointer to cmd_ctrl_node structure
+ *  @param cmd_oid	cmd oid: treated as sub command
+ *  @param wait_option	wait option: wait response or not
+ *  @param pdata_buf	A pointer to informaion buffer
+ *  @return 		0 or -1
+ */
+void libertas_set_cmd_ctrl_node(wlan_private * priv,
+		    struct cmd_ctrl_node *ptempnode,
+		    u32 cmd_oid, u16 wait_option, void *pdata_buf)
+{
+	ENTER();
+
+	if (!ptempnode)
+		return;
+
+	ptempnode->cmd_oid = cmd_oid;
+	ptempnode->wait_option = wait_option;
+	ptempnode->pdata_buf = pdata_buf;
+
+	LEAVE();
+}
+
+/**
+ *  @brief This function executes next command in command
+ *  pending queue. It will put fimware back to PS mode
+ *  if applicable.
+ *
+ *  @param priv     A pointer to wlan_private structure
+ *  @return 	   0 or -1
+ */
+int libertas_execute_next_command(wlan_private * priv)
+{
+	wlan_adapter *adapter = priv->adapter;
+	struct cmd_ctrl_node *cmdnode = NULL;
+	struct cmd_ds_command *cmdptr;
+	unsigned long flags;
+	int ret = 0;
+
+	lbs_pr_debug(1, "libertas_execute_next_command\n");
+
+	spin_lock_irqsave(&adapter->driver_lock, flags);
+
+	if (adapter->cur_cmd) {
+		lbs_pr_alert( "EXEC_NEXT_CMD: there is command in processing!\n");
+		spin_unlock_irqrestore(&adapter->driver_lock, flags);
+		ret = -1;
+		goto done;
+	}
+
+	if (!list_empty(&adapter->cmdpendingq)) {
+		cmdnode = (struct cmd_ctrl_node *)
+		    adapter->cmdpendingq.next;
+	}
+
+	spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+	if (cmdnode) {
+		lbs_pr_debug(1,
+		       "EXEC_NEXT_CMD: Got next command from cmdpendingq\n");
+		cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr;
+
+		if (is_command_allowed_in_ps(cmdptr->command)) {
+			if ((adapter->psstate == PS_STATE_SLEEP)
+			    || (adapter->psstate == PS_STATE_PRE_SLEEP)
+			    ) {
+				lbs_pr_debug(1,
+				       "EXEC_NEXT_CMD: Cannot send cmd 0x%x in psstate %d\n",
+				       cmdptr->command, adapter->psstate);
+				ret = -1;
+				goto done;
+			}
+			lbs_pr_debug(1, "EXEC_NEXT_CMD: OK to send command "
+			       "0x%x in psstate %d\n",
+			       cmdptr->command, adapter->psstate);
+		} else if (adapter->psstate != PS_STATE_FULL_POWER) {
+			/*
+			 * 1. Non-PS command:
+			 * Queue it. set needtowakeup to TRUE if current state
+			 * is SLEEP, otherwise call libertas_ps_wakeup to send Exit_PS.
+			 * 2. PS command but not Exit_PS:
+			 * Ignore it.
+			 * 3. PS command Exit_PS:
+			 * Set needtowakeup to TRUE if current state is SLEEP,
+			 * otherwise send this command down to firmware
+			 * immediately.
+			 */
+			if (cmdptr->command !=
+			    cpu_to_le16(cmd_802_11_ps_mode)) {
+				/*  Prepare to send Exit PS,
+				 *  this non PS command will be sent later */
+				if ((adapter->psstate == PS_STATE_SLEEP)
+				    || (adapter->psstate == PS_STATE_PRE_SLEEP)
+				    ) {
+					/* w/ new scheme, it will not reach here.
+					   since it is blocked in main_thread. */
+					adapter->needtowakeup = 1;
+				} else
+					libertas_ps_wakeup(priv, 0);
+
+				ret = 0;
+				goto done;
+			} else {
+				/*
+				 * PS command. Ignore it if it is not Exit_PS.
+				 * otherwise send it down immediately.
+				 */
+				struct cmd_ds_802_11_ps_mode *psm =
+				    &cmdptr->params.psmode;
+
+				lbs_pr_debug(1,
+				       "EXEC_NEXT_CMD: PS cmd- action=0x%x\n",
+				       psm->action);
+				if (psm->action !=
+				    cpu_to_le16(cmd_subcmd_exit_ps)) {
+					lbs_pr_debug(1,
+					       "EXEC_NEXT_CMD: Ignore Enter PS cmd\n");
+					list_del((struct list_head *)cmdnode);
+					libertas_cleanup_and_insert_cmd(priv, cmdnode);
+
+					ret = 0;
+					goto done;
+				}
+
+				if ((adapter->psstate == PS_STATE_SLEEP)
+				    || (adapter->psstate == PS_STATE_PRE_SLEEP)
+				    ) {
+					lbs_pr_debug(1,
+					       "EXEC_NEXT_CMD: Ignore ExitPS cmd in sleep\n");
+					list_del((struct list_head *)cmdnode);
+					libertas_cleanup_and_insert_cmd(priv, cmdnode);
+					adapter->needtowakeup = 1;
+
+					ret = 0;
+					goto done;
+				}
+
+				lbs_pr_debug(1,
+				       "EXEC_NEXT_CMD: Sending Exit_PS down...\n");
+			}
+		}
+		list_del((struct list_head *)cmdnode);
+		lbs_pr_debug(1, "EXEC_NEXT_CMD: Sending 0x%04X command\n",
+		       cmdptr->command);
+		DownloadcommandToStation(priv, cmdnode);
+	} else {
+		/*
+		 * check if in power save mode, if yes, put the device back
+		 * to PS mode
+		 */
+		if ((adapter->psmode != wlan802_11powermodecam) &&
+		    (adapter->psstate == PS_STATE_FULL_POWER) &&
+		    (adapter->connect_status == libertas_connected)) {
+			if (adapter->secinfo.WPAenabled
+			    || adapter->secinfo.WPA2enabled) {
+				/* check for valid WPA group keys */
+				if (adapter->wpa_mcast_key.len
+				    || adapter->wpa_unicast_key.len) {
+					lbs_pr_debug(1,
+					       "EXEC_NEXT_CMD: WPA enabled and GTK_SET"
+					       " go back to PS_SLEEP");
+					libertas_ps_sleep(priv, 0);
+				}
+			} else {
+				lbs_pr_debug(1,
+				       "EXEC_NEXT_CMD: command PendQ is empty,"
+				       " go back to PS_SLEEP");
+				libertas_ps_sleep(priv, 0);
+			}
+		}
+	}
+
+	ret = 0;
+done:
+	return ret;
+}
+
+void libertas_send_iwevcustom_event(wlan_private * priv, s8 * str)
+{
+	union iwreq_data iwrq;
+	u8 buf[50];
+
+	ENTER();
+
+	memset(&iwrq, 0, sizeof(union iwreq_data));
+	memset(buf, 0, sizeof(buf));
+
+	snprintf(buf, sizeof(buf) - 1, "%s", str);
+
+	iwrq.data.length = strlen(buf) + 1 + IW_EV_LCP_LEN;
+
+	/* Send Event to upper layer */
+	lbs_pr_debug(1, "Event Indication string = %s\n",
+	       (char *)buf);
+	lbs_pr_debug(1, "Event Indication String length = %d\n", iwrq.data.length);
+
+	lbs_pr_debug(1, "Sending wireless event IWEVCUSTOM for %s\n", str);
+	wireless_send_event(priv->wlan_dev.netdev, IWEVCUSTOM, &iwrq, buf);
+
+	LEAVE();
+	return;
+}
+
+static int sendconfirmsleep(wlan_private * priv, u8 * cmdptr, u16 size)
+{
+	unsigned long flags;
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+
+	ENTER();
+
+	lbs_pr_debug(1, "SEND_SLEEPC_CMD: Before download, size of cmd = %d\n",
+	       size);
+
+	lbs_dbg_hex("SEND_SLEEPC_CMD: Sleep confirm command", cmdptr, size);
+
+	ret = libertas_sbi_host_to_card(priv, MVMS_CMD, cmdptr, size);
+	priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED;
+
+	spin_lock_irqsave(&adapter->driver_lock, flags);
+	if (adapter->intcounter || adapter->currenttxskb)
+		lbs_pr_debug(1, "SEND_SLEEPC_CMD: intcounter=%d currenttxskb=%p\n",
+		       adapter->intcounter, adapter->currenttxskb);
+	spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+	if (ret) {
+		lbs_pr_alert(
+		       "SEND_SLEEPC_CMD: Host to Card failed for Confirm Sleep\n");
+	} else {
+		spin_lock_irqsave(&adapter->driver_lock, flags);
+		if (!adapter->intcounter) {
+			adapter->psstate = PS_STATE_SLEEP;
+		} else {
+			lbs_pr_debug(1, "SEND_SLEEPC_CMD: After sent,IntC=%d\n",
+			       adapter->intcounter);
+		}
+		spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+		lbs_pr_debug(1, "SEND_SLEEPC_CMD: Sent Confirm Sleep command\n");
+		lbs_pr_debug(1, "+");
+	}
+
+	LEAVE();
+	return ret;
+}
+
+void libertas_ps_sleep(wlan_private * priv, int wait_option)
+{
+
+	ENTER();
+
+	/*
+	 * PS is currently supported only in Infrastructure mode
+	 * Remove this check if it is to be supported in IBSS mode also
+	 */
+
+	libertas_prepare_and_send_command(priv, cmd_802_11_ps_mode,
+			      cmd_subcmd_enter_ps, wait_option, 0, NULL);
+
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function sends Eixt_PS command to firmware.
+ *
+ *  @param priv    	A pointer to wlan_private structure
+ *  @param wait_option	wait response or not
+ *  @return 	   	n/a
+ */
+void libertas_ps_wakeup(wlan_private * priv, int wait_option)
+{
+	enum WLAN_802_11_POWER_MODE Localpsmode;
+
+	ENTER();
+
+	Localpsmode = wlan802_11powermodecam;
+
+	lbs_pr_debug(1, "Exit_PS: Localpsmode = %d\n", Localpsmode);
+
+	libertas_prepare_and_send_command(priv, cmd_802_11_ps_mode,
+			      cmd_subcmd_exit_ps,
+			      wait_option, 0, &Localpsmode);
+
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function checks condition and prepares to
+ *  send sleep confirm command to firmware if ok.
+ *
+ *  @param priv    	A pointer to wlan_private structure
+ *  @param psmode  	Power Saving mode
+ *  @return 	   	n/a
+ */
+void libertas_ps_confirm_sleep(wlan_private * priv, u16 psmode)
+{
+	unsigned long flags =0;
+	wlan_adapter *adapter = priv->adapter;
+	u8 allowed = 1;
+
+	ENTER();
+
+	if (priv->wlan_dev.dnld_sent) {
+		allowed = 0;
+		lbs_pr_debug(1, "D");
+	}
+
+	spin_lock_irqsave(&adapter->driver_lock, flags);
+	if (adapter->cur_cmd) {
+		allowed = 0;
+		lbs_pr_debug(1, "C");
+	}
+	if (adapter->intcounter > 0) {
+		allowed = 0;
+		lbs_pr_debug(1, "I%d", adapter->intcounter);
+	}
+	spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+	if (allowed) {
+		lbs_pr_debug(1, "Sending libertas_ps_confirm_sleep\n");
+		sendconfirmsleep(priv, (u8 *) & adapter->libertas_ps_confirm_sleep,
+				 sizeof(struct PS_CMD_ConfirmSleep));
+	} else {
+		lbs_pr_debug(1, "Sleep Confirm has been delayed\n");
+	}
+
+	LEAVE();
+}
diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c
new file mode 100644
index 0000000..cdb012c
--- /dev/null
+++ b/drivers/net/wireless/libertas/cmdresp.c
@@ -0,0 +1,1031 @@
+/**
+  * This file contains the handling of command
+  * responses as well as events generated by firmware.
+  */
+#include <linux/delay.h>
+#include <linux/if_arp.h>
+#include <linux/netdevice.h>
+
+#include <net/iw_handler.h>
+
+#include "host.h"
+#include "sbi.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "join.h"
+#include "wext.h"
+
+/**
+ *  @brief This function handles disconnect event. it
+ *  reports disconnect to upper layer, clean tx/rx packets,
+ *  reset link state etc.
+ *
+ *  @param priv    A pointer to wlan_private structure
+ *  @return 	   n/a
+ */
+void libertas_mac_event_disconnected(wlan_private * priv)
+{
+	wlan_adapter *adapter = priv->adapter;
+	union iwreq_data wrqu;
+
+	if (adapter->connect_status != libertas_connected)
+		return;
+
+	lbs_pr_debug(1, "Handles disconnect event.\n");
+
+	memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN);
+	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+
+	/*
+	 * Cisco AP sends EAP failure and de-auth in less than 0.5 ms.
+	 * It causes problem in the Supplicant
+	 */
+
+	msleep_interruptible(1000);
+	wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL);
+
+	/* Free Tx and Rx packets */
+	kfree_skb(priv->adapter->currenttxskb);
+	priv->adapter->currenttxskb = NULL;
+
+	/* report disconnect to upper layer */
+	netif_stop_queue(priv->wlan_dev.netdev);
+	netif_carrier_off(priv->wlan_dev.netdev);
+
+	/* reset SNR/NF/RSSI values */
+	memset(adapter->SNR, 0x00, sizeof(adapter->SNR));
+	memset(adapter->NF, 0x00, sizeof(adapter->NF));
+	memset(adapter->RSSI, 0x00, sizeof(adapter->RSSI));
+	memset(adapter->rawSNR, 0x00, sizeof(adapter->rawSNR));
+	memset(adapter->rawNF, 0x00, sizeof(adapter->rawNF));
+	adapter->nextSNRNF = 0;
+	adapter->numSNRNF = 0;
+	adapter->rxpd_rate = 0;
+	lbs_pr_debug(1, "Current SSID=%s, ssid length=%u\n",
+	       adapter->curbssparams.ssid.ssid,
+	       adapter->curbssparams.ssid.ssidlength);
+	lbs_pr_debug(1, "Previous SSID=%s, ssid length=%u\n",
+	       adapter->previousssid.ssid, adapter->previousssid.ssidlength);
+
+	/* reset internal flags */
+	adapter->secinfo.WPAenabled = 0;
+	adapter->secinfo.WPA2enabled = 0;
+	adapter->wpa_ie_len = 0;
+	adapter->secinfo.auth1xalg = WLAN_1X_AUTH_ALG_NONE;
+	adapter->secinfo.Encryptionmode = CIPHER_NONE;
+
+	adapter->connect_status = libertas_disconnected;
+
+	/*
+	 * memorize the previous SSID and BSSID
+	 * it could be used for re-assoc
+	 */
+	memcpy(&adapter->previousssid,
+	       &adapter->curbssparams.ssid, sizeof(struct WLAN_802_11_SSID));
+	memcpy(adapter->previousbssid,
+	       adapter->curbssparams.bssid, ETH_ALEN);
+
+	/* need to erase the current SSID and BSSID info */
+	adapter->pattemptedbssdesc = NULL;
+	memset(&adapter->curbssparams, 0, sizeof(adapter->curbssparams));
+
+	if (adapter->psstate != PS_STATE_FULL_POWER) {
+		/* make firmware to exit PS mode */
+		lbs_pr_debug(1, "Disconnected, so exit PS mode.\n");
+		libertas_ps_wakeup(priv, 0);
+	}
+}
+
+/**
+ *  @brief This function handles MIC failure event.
+ *
+ *  @param priv    A pointer to wlan_private structure
+ *  @para  event   the event id
+ *  @return 	   n/a
+ */
+static void handle_mic_failureevent(wlan_private * priv, u32 event)
+{
+	char buf[50];
+
+	memset(buf, 0, sizeof(buf));
+
+	sprintf(buf, "%s", "MLME-MICHAELMICFAILURE.indication ");
+
+	if (event == MACREG_INT_CODE_MIC_ERR_UNICAST) {
+		strcat(buf, "unicast ");
+	} else {
+		strcat(buf, "multicast ");
+	}
+
+	libertas_send_iwevcustom_event(priv, buf);
+}
+
+static int wlan_ret_reg_access(wlan_private * priv,
+			       u16 type, struct cmd_ds_command *resp)
+{
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	switch (type) {
+	case cmd_ret_mac_reg_access:
+		{
+			struct cmd_ds_mac_reg_access *reg;
+
+			reg =
+			    (struct cmd_ds_mac_reg_access *)&resp->params.
+			    macreg;
+
+			adapter->offsetvalue.offset = reg->offset;
+			adapter->offsetvalue.value = reg->value;
+			break;
+		}
+
+	case cmd_ret_bbp_reg_access:
+		{
+			struct cmd_ds_bbp_reg_access *reg;
+			reg =
+			    (struct cmd_ds_bbp_reg_access *)&resp->params.
+			    bbpreg;
+
+			adapter->offsetvalue.offset = reg->offset;
+			adapter->offsetvalue.value = reg->value;
+			break;
+		}
+
+	case cmd_ret_rf_reg_access:
+		{
+			struct cmd_ds_rf_reg_access *reg;
+			reg =
+			    (struct cmd_ds_rf_reg_access *)&resp->params.
+			    rfreg;
+
+			adapter->offsetvalue.offset = reg->offset;
+			adapter->offsetvalue.value = reg->value;
+			break;
+		}
+
+	default:
+		LEAVE();
+		return -1;
+	}
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_ret_get_hw_spec(wlan_private * priv,
+				struct cmd_ds_command *resp)
+{
+	u32 i;
+	struct cmd_ds_get_hw_spec *hwspec = &resp->params.hwspec;
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+
+	ENTER();
+
+	adapter->fwcapinfo = le32_to_cpu(hwspec->fwcapinfo);
+
+	adapter->fwreleasenumber = hwspec->fwreleasenumber;
+
+	lbs_pr_debug(1, "GET_HW_SPEC: FWReleaseVersion- 0x%X\n",
+	       adapter->fwreleasenumber);
+	lbs_pr_debug(1, "GET_HW_SPEC: Permanent addr- %2x:%2x:%2x:%2x:%2x:%2x\n",
+	       hwspec->permanentaddr[0], hwspec->permanentaddr[1],
+	       hwspec->permanentaddr[2], hwspec->permanentaddr[3],
+	       hwspec->permanentaddr[4], hwspec->permanentaddr[5]);
+	lbs_pr_debug(1, "GET_HW_SPEC: hwifversion=0x%X  version=0x%X\n",
+	       hwspec->hwifversion, hwspec->version);
+
+	adapter->regioncode = le16_to_cpu(hwspec->regioncode);
+
+	for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) {
+		/* use the region code to search for the index */
+		if (adapter->regioncode == libertas_region_code_to_index[i]) {
+			adapter->regiontableindex = (u16) i;
+			break;
+		}
+	}
+
+	/* if it's unidentified region code, use the default (USA) */
+	if (i >= MRVDRV_MAX_REGION_CODE) {
+		adapter->regioncode = 0x10;
+		adapter->regiontableindex = 0;
+		lbs_pr_info(
+		       "unidentified region code, use the default (USA)\n");
+	}
+
+	if (adapter->current_addr[0] == 0xff) {
+		memmove(adapter->current_addr, hwspec->permanentaddr,
+			ETH_ALEN);
+	}
+
+	memcpy(priv->wlan_dev.netdev->dev_addr, adapter->current_addr, ETH_ALEN);
+	memcpy(priv->mesh_dev->dev_addr, adapter->current_addr, ETH_ALEN);
+
+	if (libertas_set_regiontable(priv, adapter->regioncode, 0)) {
+		ret = -1;
+		goto done;
+	}
+
+	if (libertas_set_universaltable(priv, 0)) {
+		ret = -1;
+		goto done;
+	}
+
+      done:
+	LEAVE();
+	return ret;
+}
+
+static int wlan_ret_802_11_sleep_params(wlan_private * priv,
+					struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_sleep_params *sp = &resp->params.sleep_params;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	lbs_pr_debug(1, "error=%x offset=%x stabletime=%x calcontrol=%x\n"
+	       " extsleepclk=%x\n", sp->error, sp->offset,
+	       sp->stabletime, sp->calcontrol, sp->externalsleepclk);
+	adapter->sp.sp_error = le16_to_cpu(sp->error);
+	adapter->sp.sp_offset = le16_to_cpu(sp->offset);
+	adapter->sp.sp_stabletime = le16_to_cpu(sp->stabletime);
+	adapter->sp.sp_calcontrol = le16_to_cpu(sp->calcontrol);
+	adapter->sp.sp_extsleepclk = le16_to_cpu(sp->externalsleepclk);
+	adapter->sp.sp_reserved = le16_to_cpu(sp->reserved);
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_ret_802_11_stat(wlan_private * priv,
+				struct cmd_ds_command *resp)
+{
+/*	currently adapter->wlan802_11Stat is unused
+
+	struct cmd_ds_802_11_get_stat *p11Stat = &resp->params.gstat;
+	wlan_adapter *adapter = priv->adapter;
+
+	// TODO Convert it to Big endian befor copy
+	memcpy(&adapter->wlan802_11Stat,
+	       p11Stat, sizeof(struct cmd_ds_802_11_get_stat));
+*/
+	return 0;
+}
+
+static int wlan_ret_802_11_snmp_mib(wlan_private * priv,
+				    struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_snmp_mib *smib = &resp->params.smib;
+	u16 oid = le16_to_cpu(smib->oid);
+	u16 querytype = le16_to_cpu(smib->querytype);
+
+	ENTER();
+
+	lbs_pr_debug(1, "SNMP_RESP: value of the oid = %x, querytype=%x\n", oid,
+	       querytype);
+	lbs_pr_debug(1, "SNMP_RESP: Buf size  = %x\n",
+	       le16_to_cpu(smib->bufsize));
+
+	if (querytype == cmd_act_get) {
+		switch (oid) {
+		case fragthresh_i:
+			priv->adapter->fragthsd =
+			    le16_to_cpu(*
+					     ((unsigned short *)(smib->value)));
+			lbs_pr_debug(1, "SNMP_RESP: fragthsd =%u\n",
+			       priv->adapter->fragthsd);
+			break;
+		case rtsthresh_i:
+			priv->adapter->rtsthsd =
+			    le16_to_cpu(*
+					     ((unsigned short *)(smib->value)));
+			lbs_pr_debug(1, "SNMP_RESP: rtsthsd =%u\n",
+			       priv->adapter->rtsthsd);
+			break;
+		case short_retrylim_i:
+			priv->adapter->txretrycount =
+			    le16_to_cpu(*
+					     ((unsigned short *)(smib->value)));
+			lbs_pr_debug(1, "SNMP_RESP: txretrycount =%u\n",
+			       priv->adapter->rtsthsd);
+			break;
+		default:
+			break;
+		}
+	}
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_ret_802_11_key_material(wlan_private * priv,
+					struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_key_material *pkeymaterial =
+	    &resp->params.keymaterial;
+	wlan_adapter *adapter = priv->adapter;
+	u16 action = le16_to_cpu(pkeymaterial->action);
+
+	ENTER();
+
+	/* Copy the returned key to driver private data */
+	if (action == cmd_act_get) {
+		u8 * buf_ptr = (u8 *) &pkeymaterial->keyParamSet;
+		u8 * resp_end = (u8 *) (resp + le16_to_cpu(resp->size));
+
+		while (buf_ptr < resp_end) {
+			struct MrvlIEtype_keyParamSet * pkeyparamset =
+			    (struct MrvlIEtype_keyParamSet *) buf_ptr;
+			struct WLAN_802_11_KEY * pkey;
+			u16 key_info = le16_to_cpu(pkeyparamset->keyinfo);
+			u16 param_set_len = le16_to_cpu(pkeyparamset->length);
+			u8 * end;
+			u16 key_len = le16_to_cpu(pkeyparamset->keylen);
+
+			end = (u8 *) pkeyparamset + sizeof (pkeyparamset->type)
+			                          + sizeof (pkeyparamset->length)
+			                          + param_set_len;
+			/* Make sure we don't access past the end of the IEs */
+			if (end > resp_end)
+				break;
+
+			if (key_info & KEY_INFO_WPA_UNICAST)
+				pkey = &adapter->wpa_unicast_key;
+			else if (key_info & KEY_INFO_WPA_MCAST)
+				pkey = &adapter->wpa_mcast_key;
+			else
+				break;
+
+			/* Copy returned key into driver */
+			memset(pkey, 0, sizeof(struct WLAN_802_11_KEY));
+			if (key_len > sizeof(pkey->key))
+				break;
+			pkey->type = le16_to_cpu(pkeyparamset->keytypeid);
+			pkey->flags = le16_to_cpu(pkeyparamset->keyinfo);
+			pkey->len = le16_to_cpu(pkeyparamset->keylen);
+			memcpy(pkey->key, pkeyparamset->key, pkey->len);
+
+			buf_ptr = end + 1;
+		}
+	}
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_ret_802_11_mac_address(wlan_private * priv,
+				       struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_mac_address *macadd = &resp->params.macadd;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	memcpy(adapter->current_addr, macadd->macadd, ETH_ALEN);
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_ret_802_11_rf_tx_power(wlan_private * priv,
+				       struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_rf_tx_power *rtp = &resp->params.txp;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	adapter->txpowerlevel = le16_to_cpu(rtp->currentlevel);
+
+	lbs_pr_debug(1, "Current TxPower Level = %d\n", adapter->txpowerlevel);
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_ret_802_11_rf_antenna(wlan_private * priv,
+				      struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_rf_antenna *pAntenna = &resp->params.rant;
+	wlan_adapter *adapter = priv->adapter;
+	u16 action = le16_to_cpu(pAntenna->action);
+
+	if (action == cmd_act_get_rx)
+		adapter->rxantennamode =
+		    le16_to_cpu(pAntenna->antennamode);
+
+	if (action == cmd_act_get_tx)
+		adapter->txantennamode =
+		    le16_to_cpu(pAntenna->antennamode);
+
+	lbs_pr_debug(1, "RF_ANT_RESP: action = 0x%x, mode = 0x%04x\n",
+	       action, le16_to_cpu(pAntenna->antennamode));
+
+	return 0;
+}
+
+static int wlan_ret_802_11_rate_adapt_rateset(wlan_private * priv,
+					      struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_rate_adapt_rateset *rates =
+	    &resp->params.rateset;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	if (rates->action == cmd_act_get) {
+		adapter->enablehwauto = rates->enablehwauto;
+		adapter->ratebitmap = rates->bitmap;
+	}
+
+	LEAVE();
+
+	return 0;
+}
+
+static int wlan_ret_802_11_data_rate(wlan_private * priv,
+				     struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_data_rate *pdatarate = &resp->params.drate;
+	wlan_adapter *adapter = priv->adapter;
+	u8 dot11datarate;
+
+	ENTER();
+
+	lbs_dbg_hex("DATA_RATE_RESP: data_rate- ",
+		(u8 *) pdatarate, sizeof(struct cmd_ds_802_11_data_rate));
+
+	dot11datarate = pdatarate->datarate[0];
+	if (pdatarate->action == cmd_act_get_tx_rate) {
+		memcpy(adapter->libertas_supported_rates, pdatarate->datarate,
+		       sizeof(adapter->libertas_supported_rates));
+	}
+	adapter->datarate = libertas_index_to_data_rate(dot11datarate);
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_ret_802_11_rf_channel(wlan_private * priv,
+				      struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_rf_channel *rfchannel =
+	    &resp->params.rfchannel;
+	wlan_adapter *adapter = priv->adapter;
+	u16 action = le16_to_cpu(rfchannel->action);
+	u16 newchannel = le16_to_cpu(rfchannel->currentchannel);
+
+	ENTER();
+
+	if (action == cmd_opt_802_11_rf_channel_get
+	    && adapter->curbssparams.channel != newchannel) {
+		lbs_pr_debug(1, "channel Switch: %d to %d\n",
+		       adapter->curbssparams.channel, newchannel);
+
+		/* Update the channel again */
+		adapter->curbssparams.channel = newchannel;
+	}
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_ret_802_11_rssi(wlan_private * priv,
+				struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_rssi_rsp *rssirsp = &resp->params.rssirsp;
+	wlan_adapter *adapter = priv->adapter;
+
+	/* store the non average value */
+	adapter->SNR[TYPE_BEACON][TYPE_NOAVG] = le16_to_cpu(rssirsp->SNR);
+	adapter->NF[TYPE_BEACON][TYPE_NOAVG] =
+	    le16_to_cpu(rssirsp->noisefloor);
+
+	adapter->SNR[TYPE_BEACON][TYPE_AVG] = le16_to_cpu(rssirsp->avgSNR);
+	adapter->NF[TYPE_BEACON][TYPE_AVG] =
+	    le16_to_cpu(rssirsp->avgnoisefloor);
+
+	adapter->RSSI[TYPE_BEACON][TYPE_NOAVG] =
+	    CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG],
+		     adapter->NF[TYPE_BEACON][TYPE_NOAVG]);
+
+	adapter->RSSI[TYPE_BEACON][TYPE_AVG] =
+	    CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_AVG] / AVG_SCALE,
+		     adapter->NF[TYPE_BEACON][TYPE_AVG] / AVG_SCALE);
+
+	lbs_pr_debug(1, "Beacon RSSI value = 0x%x\n",
+	       adapter->RSSI[TYPE_BEACON][TYPE_AVG]);
+
+	return 0;
+}
+
+static int wlan_ret_802_11_eeprom_access(wlan_private * priv,
+				  struct cmd_ds_command *resp)
+{
+	wlan_adapter *adapter = priv->adapter;
+	struct wlan_ioctl_regrdwr *pbuf;
+	pbuf = (struct wlan_ioctl_regrdwr *) adapter->prdeeprom;
+
+	lbs_pr_debug(1, "eeprom read len=%x\n",
+	       le16_to_cpu(resp->params.rdeeprom.bytecount));
+	if (pbuf->NOB < le16_to_cpu(resp->params.rdeeprom.bytecount)) {
+		pbuf->NOB = 0;
+		lbs_pr_debug(1, "eeprom read return length is too big\n");
+		return -1;
+	}
+	pbuf->NOB = le16_to_cpu(resp->params.rdeeprom.bytecount);
+	if (pbuf->NOB > 0) {
+
+		memcpy(&pbuf->value, (u8 *) & resp->params.rdeeprom.value,
+		       le16_to_cpu(resp->params.rdeeprom.bytecount));
+		lbs_dbg_hex("adapter", (char *)&pbuf->value,
+			le16_to_cpu(resp->params.rdeeprom.bytecount));
+	}
+	return 0;
+}
+
+static int wlan_ret_get_log(wlan_private * priv,
+			    struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_get_log *logmessage =
+	    (struct cmd_ds_802_11_get_log *)&resp->params.glog;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	/* TODO Convert it to Big Endian before copy */
+	memcpy(&adapter->logmsg, logmessage,
+	       sizeof(struct cmd_ds_802_11_get_log));
+
+	LEAVE();
+	return 0;
+}
+
+static inline int handle_cmd_response(u16 respcmd,
+				      struct cmd_ds_command *resp,
+				      wlan_private *priv)
+{
+	int ret = 0;
+	unsigned long flags;
+	wlan_adapter *adapter = priv->adapter;
+
+	switch (respcmd) {
+	case cmd_ret_mac_reg_access:
+	case cmd_ret_bbp_reg_access:
+	case cmd_ret_rf_reg_access:
+		ret = wlan_ret_reg_access(priv, respcmd, resp);
+		break;
+
+	case cmd_ret_hw_spec_info:
+		ret = wlan_ret_get_hw_spec(priv, resp);
+		break;
+
+	case cmd_ret_802_11_scan:
+		ret = libertas_ret_80211_scan(priv, resp);
+		break;
+
+	case cmd_ret_802_11_get_log:
+		ret = wlan_ret_get_log(priv, resp);
+		break;
+
+	case cmd_ret_802_11_associate:
+	case cmd_ret_802_11_reassociate:
+		ret = libertas_ret_80211_associate(priv, resp);
+		break;
+
+	case cmd_ret_802_11_disassociate:
+	case cmd_ret_802_11_deauthenticate:
+		ret = libertas_ret_80211_disassociate(priv, resp);
+		break;
+
+	case cmd_ret_802_11_ad_hoc_start:
+	case cmd_ret_802_11_ad_hoc_join:
+		ret = libertas_ret_80211_ad_hoc_start(priv, resp);
+		break;
+
+	case cmd_ret_802_11_stat:
+		ret = wlan_ret_802_11_stat(priv, resp);
+		break;
+
+	case cmd_ret_802_11_snmp_mib:
+		ret = wlan_ret_802_11_snmp_mib(priv, resp);
+		break;
+
+	case cmd_ret_802_11_rf_tx_power:
+		ret = wlan_ret_802_11_rf_tx_power(priv, resp);
+		break;
+
+	case cmd_ret_802_11_set_afc:
+	case cmd_ret_802_11_get_afc:
+		spin_lock_irqsave(&adapter->driver_lock, flags);
+		memmove(adapter->cur_cmd->pdata_buf,
+			&resp->params.afc,
+			sizeof(struct cmd_ds_802_11_afc));
+		spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+		break;
+	case cmd_ret_802_11_rf_antenna:
+		ret = wlan_ret_802_11_rf_antenna(priv, resp);
+		break;
+
+	case cmd_ret_mac_multicast_adr:
+	case cmd_ret_mac_control:
+	case cmd_ret_802_11_set_wep:
+	case cmd_ret_802_11_reset:
+	case cmd_ret_802_11_authenticate:
+	case cmd_ret_802_11_radio_control:
+	case cmd_ret_802_11_beacon_stop:
+	case cmd_ret_802_11_enable_rsn:
+		break;
+
+	case cmd_ret_802_11_data_rate:
+		ret = wlan_ret_802_11_data_rate(priv, resp);
+		break;
+	case cmd_ret_802_11_rate_adapt_rateset:
+		ret = wlan_ret_802_11_rate_adapt_rateset(priv, resp);
+		break;
+	case cmd_ret_802_11_rf_channel:
+		ret = wlan_ret_802_11_rf_channel(priv, resp);
+		break;
+
+	case cmd_ret_802_11_rssi:
+		ret = wlan_ret_802_11_rssi(priv, resp);
+		break;
+
+	case cmd_ret_802_11_mac_address:
+		ret = wlan_ret_802_11_mac_address(priv, resp);
+		break;
+
+	case cmd_ret_802_11_ad_hoc_stop:
+		ret = libertas_ret_80211_ad_hoc_stop(priv, resp);
+		break;
+
+	case cmd_ret_802_11_key_material:
+		lbs_pr_debug(1, "CMD_RESP: KEY_MATERIAL command response\n");
+		ret = wlan_ret_802_11_key_material(priv, resp);
+		break;
+
+	case cmd_ret_802_11_eeprom_access:
+		ret = wlan_ret_802_11_eeprom_access(priv, resp);
+		break;
+
+	case cmd_ret_802_11d_domain_info:
+		ret = libertas_ret_802_11d_domain_info(priv, resp);
+		break;
+
+	case cmd_ret_802_11_sleep_params:
+		ret = wlan_ret_802_11_sleep_params(priv, resp);
+		break;
+	case cmd_ret_802_11_inactivity_timeout:
+		spin_lock_irqsave(&adapter->driver_lock, flags);
+		*((u16 *) adapter->cur_cmd->pdata_buf) =
+		    le16_to_cpu(resp->params.inactivity_timeout.timeout);
+		spin_unlock_irqrestore(&adapter->driver_lock, flags);
+		break;
+
+	case cmd_ret_802_11_tpc_cfg:
+		spin_lock_irqsave(&adapter->driver_lock, flags);
+		memmove(adapter->cur_cmd->pdata_buf,
+			&resp->params.tpccfg,
+			sizeof(struct cmd_ds_802_11_tpc_cfg));
+		spin_unlock_irqrestore(&adapter->driver_lock, flags);
+		break;
+	case cmd_ret_802_11_led_gpio_ctrl:
+		spin_lock_irqsave(&adapter->driver_lock, flags);
+		memmove(adapter->cur_cmd->pdata_buf,
+			&resp->params.ledgpio,
+			sizeof(struct cmd_ds_802_11_led_ctrl));
+		spin_unlock_irqrestore(&adapter->driver_lock, flags);
+		break;
+	case cmd_ret_802_11_pwr_cfg:
+		spin_lock_irqsave(&adapter->driver_lock, flags);
+		memmove(adapter->cur_cmd->pdata_buf,
+			&resp->params.pwrcfg,
+			sizeof(struct cmd_ds_802_11_pwr_cfg));
+		spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+		break;
+
+	case cmd_ret_get_tsf:
+		spin_lock_irqsave(&adapter->driver_lock, flags);
+		memcpy(priv->adapter->cur_cmd->pdata_buf,
+		       &resp->params.gettsf.tsfvalue, sizeof(u64));
+		spin_unlock_irqrestore(&adapter->driver_lock, flags);
+		break;
+	case cmd_ret_bt_access:
+		spin_lock_irqsave(&adapter->driver_lock, flags);
+		if (adapter->cur_cmd->pdata_buf)
+			memcpy(adapter->cur_cmd->pdata_buf,
+			       &resp->params.bt.addr1, 2 * ETH_ALEN);
+		spin_unlock_irqrestore(&adapter->driver_lock, flags);
+		break;
+	case cmd_ret_fwt_access:
+		spin_lock_irqsave(&adapter->driver_lock, flags);
+		if (adapter->cur_cmd->pdata_buf)
+			memcpy(adapter->cur_cmd->pdata_buf,
+			       &resp->params.fwt,
+				sizeof(resp->params.fwt));
+		spin_unlock_irqrestore(&adapter->driver_lock, flags);
+		break;
+	case cmd_ret_mesh_access:
+		if (adapter->cur_cmd->pdata_buf)
+			memcpy(adapter->cur_cmd->pdata_buf,
+			       &resp->params.mesh,
+			       sizeof(resp->params.mesh));
+		break;
+	case cmd_rte_802_11_tx_rate_query:
+		priv->adapter->txrate = resp->params.txrate.txrate;
+		break;
+	default:
+		lbs_pr_debug(1, "CMD_RESP: Unknown command response %#x\n",
+		       resp->command);
+		break;
+	}
+	return ret;
+}
+
+int libertas_process_rx_command(wlan_private * priv)
+{
+	u16 respcmd;
+	struct cmd_ds_command *resp;
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+	ulong flags;
+	u16 result;
+
+	ENTER();
+
+	lbs_pr_debug(1, "CMD_RESP: @ %lu\n", jiffies);
+
+	/* Now we got response from FW, cancel the command timer */
+	del_timer(&adapter->command_timer);
+
+	mutex_lock(&adapter->lock);
+	spin_lock_irqsave(&adapter->driver_lock, flags);
+
+	if (!adapter->cur_cmd) {
+		lbs_pr_debug(1, "CMD_RESP: NULL cur_cmd=%p\n", adapter->cur_cmd);
+		ret = -1;
+		spin_unlock_irqrestore(&adapter->driver_lock, flags);
+		goto done;
+	}
+	resp = (struct cmd_ds_command *)(adapter->cur_cmd->bufvirtualaddr);
+
+	lbs_dbg_hex("CMD_RESP:", adapter->cur_cmd->bufvirtualaddr,
+		priv->wlan_dev.upld_len);
+
+	respcmd = le16_to_cpu(resp->command);
+
+	result = le16_to_cpu(resp->result);
+
+	lbs_pr_debug(1, "CMD_RESP: %x result: %d length: %d\n", respcmd,
+	       result, priv->wlan_dev.upld_len);
+
+	if (!(respcmd & 0x8000)) {
+		lbs_pr_debug(1, "Invalid response to command!");
+		adapter->cur_cmd_retcode = -1;
+		__libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd);
+		adapter->nr_cmd_pending--;
+		adapter->cur_cmd = NULL;
+		spin_unlock_irqrestore(&adapter->driver_lock, flags);
+		ret = -1;
+		goto done;
+	}
+
+	/* Store the response code to cur_cmd_retcode. */
+	adapter->cur_cmd_retcode = le16_to_cpu(resp->result);
+
+	if (respcmd == cmd_ret_802_11_ps_mode) {
+		struct cmd_ds_802_11_ps_mode *psmode;
+
+		psmode = &resp->params.psmode;
+		lbs_pr_debug(1,
+		       "CMD_RESP: PS_MODE cmd reply result=%#x action=0x%X\n",
+		       resp->result, psmode->action);
+		psmode->action = cpu_to_le16(psmode->action);
+
+		if (result) {
+			lbs_pr_debug(1, "CMD_RESP: PS command failed- %#x \n",
+			       resp->result);
+			if (adapter->inframode == wlan802_11ibss) {
+				/*
+				 * We should not re-try enter-ps command in
+				 * ad-hoc mode. It takes place in
+				 * libertas_execute_next_command().
+				 */
+				if (psmode->action == cmd_subcmd_enter_ps)
+					adapter->psmode =
+					    wlan802_11powermodecam;
+			}
+		} else if (psmode->action == cmd_subcmd_enter_ps) {
+			adapter->needtowakeup = 0;
+			adapter->psstate = PS_STATE_AWAKE;
+
+			lbs_pr_debug(1, "CMD_RESP: Enter_PS command response\n");
+			if (adapter->connect_status != libertas_connected) {
+				/*
+				 * When Deauth Event received before Enter_PS command
+				 * response, We need to wake up the firmware.
+				 */
+				lbs_pr_debug(1,
+				       "Disconnected, Going to invoke libertas_ps_wakeup\n");
+
+				mutex_unlock(&adapter->lock);
+				spin_unlock_irqrestore(&adapter->driver_lock, flags);
+				libertas_ps_wakeup(priv, 0);
+				mutex_lock(&adapter->lock);
+				spin_lock_irqsave(&adapter->driver_lock, flags);
+			}
+		} else if (psmode->action == cmd_subcmd_exit_ps) {
+			adapter->needtowakeup = 0;
+			adapter->psstate = PS_STATE_FULL_POWER;
+			lbs_pr_debug(1, "CMD_RESP: Exit_PS command response\n");
+		} else {
+			lbs_pr_debug(1, "CMD_RESP: PS- action=0x%X\n",
+			       psmode->action);
+		}
+
+		__libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd);
+		adapter->nr_cmd_pending--;
+		adapter->cur_cmd = NULL;
+		spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+		ret = 0;
+		goto done;
+	}
+
+	if (adapter->cur_cmd->cmdflags & CMD_F_HOSTCMD) {
+		/* Copy the response back to response buffer */
+		memcpy(adapter->cur_cmd->pdata_buf, resp, resp->size);
+
+		adapter->cur_cmd->cmdflags &= ~CMD_F_HOSTCMD;
+	}
+
+	/* If the command is not successful, cleanup and return failure */
+	if ((result != 0 || !(respcmd & 0x8000))) {
+		lbs_pr_debug(1, "CMD_RESP: command reply %#x result=%#x\n",
+		       resp->command, resp->result);
+		/*
+		 * Handling errors here
+		 */
+		switch (respcmd) {
+		case cmd_ret_hw_spec_info:
+		case cmd_ret_802_11_reset:
+			lbs_pr_debug(1, "CMD_RESP: Reset command failed\n");
+			break;
+
+		}
+
+		__libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd);
+		adapter->nr_cmd_pending--;
+		adapter->cur_cmd = NULL;
+		spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+		ret = -1;
+		goto done;
+	}
+
+	spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+	ret = handle_cmd_response(respcmd, resp, priv);
+
+	spin_lock_irqsave(&adapter->driver_lock, flags);
+	if (adapter->cur_cmd) {
+		/* Clean up and Put current command back to cmdfreeq */
+		__libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd);
+		adapter->nr_cmd_pending--;
+		WARN_ON(adapter->nr_cmd_pending > 128);
+		adapter->cur_cmd = NULL;
+	}
+	spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+done:
+	mutex_unlock(&adapter->lock);
+	LEAVE();
+	return ret;
+}
+
+int libertas_process_event(wlan_private * priv)
+{
+	int ret = 0;
+	wlan_adapter *adapter = priv->adapter;
+	u32 eventcause;
+
+	spin_lock_irq(&adapter->driver_lock);
+	eventcause = adapter->eventcause;
+	spin_unlock_irq(&adapter->driver_lock);
+
+	ENTER();
+
+	lbs_pr_debug(1, "EVENT Cause %x\n", eventcause);
+
+	switch (eventcause >> SBI_EVENT_CAUSE_SHIFT) {
+	case MACREG_INT_CODE_LINK_SENSED:
+		lbs_pr_debug(1, "EVENT: MACREG_INT_CODE_LINK_SENSED\n");
+		break;
+
+	case MACREG_INT_CODE_DEAUTHENTICATED:
+		lbs_pr_debug(1, "EVENT: Deauthenticated\n");
+		libertas_mac_event_disconnected(priv);
+		break;
+
+	case MACREG_INT_CODE_DISASSOCIATED:
+		lbs_pr_debug(1, "EVENT: Disassociated\n");
+		libertas_mac_event_disconnected(priv);
+		break;
+
+	case MACREG_INT_CODE_LINK_LOSE_NO_SCAN:
+		lbs_pr_debug(1, "EVENT: Link lost\n");
+		libertas_mac_event_disconnected(priv);
+		break;
+
+	case MACREG_INT_CODE_PS_SLEEP:
+		lbs_pr_debug(1, "EVENT: SLEEP\n");
+		lbs_pr_debug(1, "_");
+
+		/* handle unexpected PS SLEEP event */
+		if (adapter->psstate == PS_STATE_FULL_POWER) {
+			lbs_pr_debug(1,
+			       "EVENT: In FULL POWER mode - ignore PS SLEEP\n");
+			break;
+		}
+		adapter->psstate = PS_STATE_PRE_SLEEP;
+
+		libertas_ps_confirm_sleep(priv, (u16) adapter->psmode);
+
+		break;
+
+	case MACREG_INT_CODE_PS_AWAKE:
+		lbs_pr_debug(1, "EVENT: AWAKE \n");
+		lbs_pr_debug(1, "|");
+
+		/* handle unexpected PS AWAKE event */
+		if (adapter->psstate == PS_STATE_FULL_POWER) {
+			lbs_pr_debug(1,
+			       "EVENT: In FULL POWER mode - ignore PS AWAKE\n");
+			break;
+		}
+
+		adapter->psstate = PS_STATE_AWAKE;
+
+		if (adapter->needtowakeup) {
+			/*
+			 * wait for the command processing to finish
+			 * before resuming sending
+			 * adapter->needtowakeup will be set to FALSE
+			 * in libertas_ps_wakeup()
+			 */
+			lbs_pr_debug(1, "Waking up...\n");
+			libertas_ps_wakeup(priv, 0);
+		}
+		break;
+
+	case MACREG_INT_CODE_MIC_ERR_UNICAST:
+		lbs_pr_debug(1, "EVENT: UNICAST MIC ERROR\n");
+		handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_UNICAST);
+		break;
+
+	case MACREG_INT_CODE_MIC_ERR_MULTICAST:
+		lbs_pr_debug(1, "EVENT: MULTICAST MIC ERROR\n");
+		handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_MULTICAST);
+		break;
+	case MACREG_INT_CODE_MIB_CHANGED:
+	case MACREG_INT_CODE_INIT_DONE:
+		break;
+
+	case MACREG_INT_CODE_ADHOC_BCN_LOST:
+		lbs_pr_debug(1, "EVENT: HWAC - ADHOC BCN LOST\n");
+		break;
+
+	case MACREG_INT_CODE_RSSI_LOW:
+		lbs_pr_alert( "EVENT: RSSI_LOW\n");
+		break;
+	case MACREG_INT_CODE_SNR_LOW:
+		lbs_pr_alert( "EVENT: SNR_LOW\n");
+		break;
+	case MACREG_INT_CODE_MAX_FAIL:
+		lbs_pr_alert( "EVENT: MAX_FAIL\n");
+		break;
+	case MACREG_INT_CODE_RSSI_HIGH:
+		lbs_pr_alert( "EVENT: RSSI_HIGH\n");
+		break;
+	case MACREG_INT_CODE_SNR_HIGH:
+		lbs_pr_alert( "EVENT: SNR_HIGH\n");
+		break;
+
+	default:
+		lbs_pr_alert( "EVENT: unknown event id: %#x\n",
+		       eventcause >> SBI_EVENT_CAUSE_SHIFT);
+		break;
+	}
+
+	spin_lock_irq(&adapter->driver_lock);
+	adapter->eventcause = 0;
+	spin_unlock_irq(&adapter->driver_lock);
+	LEAVE();
+	return ret;
+}
diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c
new file mode 100644
index 0000000..51dfd20
--- /dev/null
+++ b/drivers/net/wireless/libertas/debugfs.c
@@ -0,0 +1,1935 @@
+#include <linux/module.h>
+#include <linux/dcache.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <net/iw_handler.h>
+#include "dev.h"
+#include "decl.h"
+#include "host.h"
+
+static struct dentry *libertas_dir = NULL;
+static char *szStates[] = {
+	"Connected",
+	"Disconnected"
+};
+
+void libertas_debug_init(wlan_private * priv, struct net_device *dev);
+
+static int open_file_generic(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t write_file_dummy(struct file *file, const char __user *buf,
+                                size_t count, loff_t *ppos)
+{
+        return -EINVAL;
+}
+
+static const size_t len = PAGE_SIZE;
+
+static ssize_t libertas_dev_info(struct file *file, char __user *userbuf,
+				  size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	size_t pos = 0;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+	ssize_t res;
+
+	pos += snprintf(buf+pos, len-pos, "state = %s\n",
+				szStates[priv->adapter->connect_status]);
+	pos += snprintf(buf+pos, len-pos, "region_code = %02x\n",
+				(u32) priv->adapter->regioncode);
+
+	res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+
+	free_page(addr);
+	return res;
+}
+
+
+static ssize_t libertas_getscantable(struct file *file, char __user *userbuf,
+				  size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	size_t pos = 0;
+	int numscansdone = 0, res;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	pos += snprintf(buf+pos, len-pos,
+			"---------------------------------------");
+	pos += snprintf(buf+pos, len-pos,
+			"---------------------------------------\n");
+	pos += snprintf(buf+pos, len-pos,
+		"# | ch  | ss  |       bssid       |   cap    |    TSF   | Qual | SSID \n");
+	pos += snprintf(buf+pos, len-pos,
+		"---------------------------------------");
+	pos += snprintf(buf+pos, len-pos,
+		"---------------------------------------\n");
+
+	while (numscansdone < priv->adapter->numinscantable) {
+		struct bss_descriptor *pbssinfo;
+		u16 cap;
+
+		pbssinfo = &priv->adapter->scantable[numscansdone];
+		memcpy(&cap, &pbssinfo->cap, sizeof(cap));
+		pos += snprintf(buf+pos, len-pos,
+			"%02u| %03d | %03ld | %02x:%02x:%02x:%02x:%02x:%02x |",
+			numscansdone, pbssinfo->channel, pbssinfo->rssi,
+			pbssinfo->macaddress[0], pbssinfo->macaddress[1],
+			pbssinfo->macaddress[2], pbssinfo->macaddress[3],
+			pbssinfo->macaddress[4], pbssinfo->macaddress[5]);
+		pos += snprintf(buf+pos, len-pos, " %04x-", cap);
+		pos += snprintf(buf+pos, len-pos, "%c%c%c |",
+				pbssinfo->cap.ibss ? 'A' : 'I',
+				pbssinfo->cap.privacy ? 'P' : ' ',
+				pbssinfo->cap.spectrummgmt ? 'S' : ' ');
+		pos += snprintf(buf+pos, len-pos, " %08llx |", pbssinfo->networktsf);
+		pos += snprintf(buf+pos, len-pos, " %d |",
+			SCAN_RSSI(priv->adapter->scantable[numscansdone].rssi));
+
+		pos += snprintf(buf+pos, len-pos, " %s\n", pbssinfo->ssid.ssid);
+
+		numscansdone++;
+	}
+
+	res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+
+	free_page(addr);
+	return res;
+}
+
+static ssize_t libertas_sleepparams_write(struct file *file,
+				const char __user *user_buf, size_t count,
+				loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	ssize_t buf_size, res;
+	int p1, p2, p3, p4, p5, p6;
+	struct sleep_params sp;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, user_buf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	res = sscanf(buf, "%d %d %d %d %d %d", &p1, &p2, &p3, &p4, &p5, &p6);
+	if (res != 6) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	sp.sp_error = p1;
+	sp.sp_offset = p2;
+	sp.sp_stabletime = p3;
+	sp.sp_calcontrol = p4;
+	sp.sp_extsleepclk = p5;
+	sp.sp_reserved = p6;
+
+	memcpy(&priv->adapter->sp, &sp, sizeof(struct sleep_params));
+
+        res = libertas_prepare_and_send_command(priv,
+				cmd_802_11_sleep_params,
+				cmd_act_set,
+				cmd_option_waitforrsp, 0, NULL);
+
+	if (!res)
+		res = count;
+	else
+		res = -EINVAL;
+
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+static ssize_t libertas_sleepparams_read(struct file *file, char __user *userbuf,
+				  size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	wlan_adapter *adapter = priv->adapter;
+	ssize_t res;
+	size_t pos = 0;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+        res = libertas_prepare_and_send_command(priv,
+				cmd_802_11_sleep_params,
+				cmd_act_get,
+				cmd_option_waitforrsp, 0, NULL);
+	if (res) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+
+	pos += snprintf(buf, len, "%d %d %d %d %d %d\n", adapter->sp.sp_error,
+			adapter->sp.sp_offset, adapter->sp.sp_stabletime,
+			adapter->sp.sp_calcontrol, adapter->sp.sp_extsleepclk,
+			adapter->sp.sp_reserved);
+
+	res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+static ssize_t libertas_extscan(struct file *file, const char __user *userbuf,
+				  size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	ssize_t res, buf_size;
+	struct WLAN_802_11_SSID extscan_ssid;
+	union iwreq_data wrqu;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+
+	memcpy(&extscan_ssid.ssid, buf, strlen(buf)-1);
+	extscan_ssid.ssidlength = strlen(buf)-1;
+
+	libertas_send_specific_SSID_scan(priv, &extscan_ssid, 1);
+
+	memset(&wrqu, 0, sizeof(union iwreq_data));
+	wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, NULL);
+
+out_unlock:
+	free_page(addr);
+	return count;
+}
+
+static int libertas_parse_chan(char *buf, size_t count,
+			struct wlan_ioctl_user_scan_cfg *scan_cfg, int dur)
+{
+	char *start, *end, *hold, *str;
+	int i = 0;
+
+	start = strstr(buf, "chan=");
+	if (!start)
+		return -EINVAL;
+	start += 5;
+	end = strstr(start, " ");
+	if (!end)
+		end = buf + count;
+	hold = kzalloc((end - start)+1, GFP_KERNEL);
+	if (!hold)
+		return -ENOMEM;
+	strncpy(hold, start, end - start);
+	hold[(end-start)+1] = '\0';
+	while(hold && (str = strsep(&hold, ","))) {
+		int chan;
+		char band, passive = 0;
+		sscanf(str, "%d%c%c", &chan, &band, &passive);
+		scan_cfg->chanlist[i].channumber = chan;
+		scan_cfg->chanlist[i].scantype = passive ? 1 : 0;
+		if (band == 'b' || band == 'g')
+			scan_cfg->chanlist[i].radiotype = 0;
+		else if (band == 'a')
+			scan_cfg->chanlist[i].radiotype = 1;
+
+		scan_cfg->chanlist[i].scantime = dur;
+		i++;
+	}
+
+	kfree(hold);
+	return i;
+}
+
+static void libertas_parse_bssid(char *buf, size_t count,
+                        struct wlan_ioctl_user_scan_cfg *scan_cfg)
+{
+	char *hold;
+	unsigned int mac[ETH_ALEN];
+	int i;
+
+	hold = strstr(buf, "bssid=");
+	if (!hold)
+		return;
+	hold += 6;
+	sscanf(hold, "%2x:%2x:%2x:%2x:%2x:%2x", mac, mac+1, mac+2, mac+3,
+			mac+4, mac+5);
+	for(i=0;i<ETH_ALEN;i++)
+		scan_cfg->specificBSSID[i] = mac[i];
+}
+
+static void libertas_parse_ssid(char *buf, size_t count,
+                        struct wlan_ioctl_user_scan_cfg *scan_cfg)
+{
+	char *hold, *end;
+	ssize_t size;
+
+	hold = strstr(buf, "ssid=");
+	if (!hold)
+		return;
+	hold += 5;
+	end = strstr(hold, " ");
+	if (!end)
+		end = buf + count - 1;
+
+	size = min(IW_ESSID_MAX_SIZE, end - hold);
+	strncpy(scan_cfg->specificSSID, hold, size);
+
+	return;
+}
+
+static void libertas_parse_keep(char *buf, size_t count,
+                        struct wlan_ioctl_user_scan_cfg *scan_cfg)
+{
+	char *hold;
+	int val;
+
+	hold = strstr(buf, "keep=");
+	if (!hold)
+		return;
+	hold += 5;
+	sscanf(hold, "%d", &val);
+
+	if (val != 0)
+		val = 1;
+
+	scan_cfg->keeppreviousscan = val;
+	return;
+}
+
+static int libertas_parse_dur(char *buf, size_t count,
+                        struct wlan_ioctl_user_scan_cfg *scan_cfg)
+{
+	char *hold;
+	int val;
+
+	hold = strstr(buf, "dur=");
+	if (!hold)
+		return 0;
+	hold += 4;
+	sscanf(hold, "%d", &val);
+
+	return val;
+}
+
+static void libertas_parse_probes(char *buf, size_t count,
+                        struct wlan_ioctl_user_scan_cfg *scan_cfg)
+{
+	char *hold;
+	int val;
+
+	hold = strstr(buf, "probes=");
+	if (!hold)
+		return;
+	hold += 7;
+	sscanf(hold, "%d", &val);
+
+	scan_cfg->numprobes = val;
+
+	return;
+}
+
+static void libertas_parse_type(char *buf, size_t count,
+                        struct wlan_ioctl_user_scan_cfg *scan_cfg)
+{
+	char *hold;
+	int val;
+
+	hold = strstr(buf, "type=");
+	if (!hold)
+		return;
+	hold += 5;
+	sscanf(hold, "%d", &val);
+
+	/* type=1,2 or 3 */
+	if (val < 1 || val > 3)
+		return;
+
+	scan_cfg->bsstype = val;
+
+	return;
+}
+
+static ssize_t libertas_setuserscan(struct file *file,
+				    const char __user *userbuf,
+				    size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	ssize_t res, buf_size;
+	struct wlan_ioctl_user_scan_cfg *scan_cfg;
+	union iwreq_data wrqu;
+	int dur;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	scan_cfg = kzalloc(sizeof(struct wlan_ioctl_user_scan_cfg), GFP_KERNEL);
+	if (!scan_cfg)
+		return -ENOMEM;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+
+	scan_cfg->bsstype = WLAN_SCAN_BSS_TYPE_ANY;
+
+	dur = libertas_parse_dur(buf, count, scan_cfg);
+	libertas_parse_chan(buf, count, scan_cfg, dur);
+	libertas_parse_bssid(buf, count, scan_cfg);
+	libertas_parse_ssid(buf, count, scan_cfg);
+	libertas_parse_keep(buf, count, scan_cfg);
+	libertas_parse_probes(buf, count, scan_cfg);
+	libertas_parse_type(buf, count, scan_cfg);
+
+	wlan_scan_networks(priv, scan_cfg);
+	wait_event_interruptible(priv->adapter->cmd_pending,
+				 !priv->adapter->nr_cmd_pending);
+
+	memset(&wrqu, 0x00, sizeof(union iwreq_data));
+	wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, NULL);
+
+out_unlock:
+	free_page(addr);
+	kfree(scan_cfg);
+	return count;
+}
+
+static int libertas_event_initcmd(wlan_private *priv, void **response_buf,
+			struct cmd_ctrl_node **cmdnode,
+			struct cmd_ds_command **cmd)
+{
+	u16 wait_option = cmd_option_waitforrsp;
+
+	if (!(*cmdnode = libertas_get_free_cmd_ctrl_node(priv))) {
+		lbs_pr_debug(1, "failed libertas_get_free_cmd_ctrl_node\n");
+		return -ENOMEM;
+	}
+	if (!(*response_buf = kmalloc(3000, GFP_KERNEL))) {
+		lbs_pr_debug(1, "failed to allocate response buffer!\n");
+		return -ENOMEM;
+	}
+	libertas_set_cmd_ctrl_node(priv, *cmdnode, 0, wait_option, NULL);
+	init_waitqueue_head(&(*cmdnode)->cmdwait_q);
+	(*cmdnode)->pdata_buf = *response_buf;
+	(*cmdnode)->cmdflags |= CMD_F_HOSTCMD;
+	(*cmdnode)->cmdwaitqwoken = 0;
+	*cmd = (struct cmd_ds_command *)(*cmdnode)->bufvirtualaddr;
+	(*cmd)->command = cmd_802_11_subscribe_event;
+	(*cmd)->seqnum = ++priv->adapter->seqnum;
+	(*cmd)->result = 0;
+	return 0;
+}
+
+static ssize_t libertas_lowrssi_read(struct file *file, char __user *userbuf,
+				  size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	wlan_adapter *adapter = priv->adapter;
+	struct cmd_ctrl_node *pcmdnode;
+	struct cmd_ds_command *pcmdptr;
+	struct cmd_ds_802_11_subscribe_event *event;
+	void *response_buf;
+	int res, cmd_len;
+	ssize_t pos = 0;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+	if (res < 0) {
+		free_page(addr);
+		return res;
+	}
+
+	event = &pcmdptr->params.subscribe_event;
+	event->action = cmd_act_get;
+	pcmdptr->size =
+	cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN);
+	libertas_queue_cmd(adapter, pcmdnode, 1);
+	wake_up_interruptible(&priv->mainthread.waitq);
+
+	/* Sleep until response is generated by FW */
+	wait_event_interruptible(pcmdnode->cmdwait_q,
+				pcmdnode->cmdwaitqwoken);
+
+	pcmdptr = response_buf;
+	if (pcmdptr->result) {
+		lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+			pcmdptr->result);
+		kfree(response_buf);
+		free_page(addr);
+		return 0;
+	}
+
+	if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+		lbs_pr_err("command response incorrect!\n");
+		kfree(response_buf);
+		free_page(addr);
+		return 0;
+	}
+
+	cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+	event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN);
+	while (cmd_len < pcmdptr->size) {
+		struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len);
+		switch(header->type) {
+		struct mrvlietypes_rssithreshold  *Lowrssi;
+		case TLV_TYPE_RSSI_LOW:
+		Lowrssi = (struct mrvlietypes_rssithreshold *)(response_buf + cmd_len);
+		pos += snprintf(buf+pos, len-pos, "%d %d %d\n",
+				Lowrssi->rssivalue,
+				Lowrssi->rssifreq,
+				(event->events & 0x0001)?1:0);
+		default:
+			cmd_len += sizeof(struct mrvlietypes_snrthreshold);
+			break;
+		}
+	}
+
+	kfree(response_buf);
+	res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+	free_page(addr);
+	return res;
+}
+
+static u16 libertas_get_events_bitmap(wlan_private *priv)
+{
+	wlan_adapter *adapter = priv->adapter;
+	struct cmd_ctrl_node *pcmdnode;
+	struct cmd_ds_command *pcmdptr;
+	struct cmd_ds_802_11_subscribe_event *event;
+	void *response_buf;
+	int res;
+	u16 event_bitmap;
+
+	res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+	if (res < 0)
+		return res;
+
+	event = &pcmdptr->params.subscribe_event;
+	event->action = cmd_act_get;
+	pcmdptr->size =
+	cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN);
+	libertas_queue_cmd(adapter, pcmdnode, 1);
+	wake_up_interruptible(&priv->mainthread.waitq);
+
+	/* Sleep until response is generated by FW */
+	wait_event_interruptible(pcmdnode->cmdwait_q,
+				pcmdnode->cmdwaitqwoken);
+
+	pcmdptr = response_buf;
+
+	if (pcmdptr->result) {
+		lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+			pcmdptr->result);
+		kfree(response_buf);
+		return 0;
+	}
+
+	if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+		lbs_pr_err("command response incorrect!\n");
+		kfree(response_buf);
+		return 0;
+	}
+
+	event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN);
+	event_bitmap = event->events;
+	kfree(response_buf);
+	return event_bitmap;
+}
+
+static ssize_t libertas_lowrssi_write(struct file *file,
+				    const char __user *userbuf,
+				    size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	wlan_adapter *adapter = priv->adapter;
+	ssize_t res, buf_size;
+	int value, freq, subscribed, cmd_len;
+	struct cmd_ctrl_node *pcmdnode;
+	struct cmd_ds_command *pcmdptr;
+	struct cmd_ds_802_11_subscribe_event *event;
+	struct mrvlietypes_rssithreshold *rssi_threshold;
+	void *response_buf;
+	u16 event_bitmap;
+	u8 *ptr;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed);
+	if (res != 3) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+
+	event_bitmap = libertas_get_events_bitmap(priv);
+
+	res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+	if (res < 0)
+		goto out_unlock;
+
+	event = &pcmdptr->params.subscribe_event;
+	event->action = cmd_act_set;
+	pcmdptr->size = cpu_to_le16(S_DS_GEN +
+		sizeof(struct cmd_ds_802_11_subscribe_event) +
+		sizeof(struct mrvlietypes_rssithreshold));
+
+	cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+	ptr = (u8*) pcmdptr+cmd_len;
+	rssi_threshold = (struct mrvlietypes_rssithreshold *)(ptr);
+	rssi_threshold->header.type = cpu_to_le16(0x0104);
+	rssi_threshold->header.len = 2;
+	rssi_threshold->rssivalue = cpu_to_le16(value);
+	rssi_threshold->rssifreq = cpu_to_le16(freq);
+	event_bitmap |= subscribed ? 0x0001 : 0x0;
+	event->events = event_bitmap;
+
+	libertas_queue_cmd(adapter, pcmdnode, 1);
+	wake_up_interruptible(&priv->mainthread.waitq);
+
+	/* Sleep until response is generated by FW */
+	wait_event_interruptible(pcmdnode->cmdwait_q,
+				pcmdnode->cmdwaitqwoken);
+
+	pcmdptr = response_buf;
+
+	if (pcmdptr->result) {
+		lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+			pcmdptr->result);
+		kfree(response_buf);
+		free_page(addr);
+		return 0;
+	}
+
+	if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+		lbs_pr_err("command response incorrect!\n");
+		kfree(response_buf);
+		free_page(addr);
+		return 0;
+	}
+
+	res = count;
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+static ssize_t libertas_lowsnr_read(struct file *file, char __user *userbuf,
+				  size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	wlan_adapter *adapter = priv->adapter;
+	struct cmd_ctrl_node *pcmdnode;
+	struct cmd_ds_command *pcmdptr;
+	struct cmd_ds_802_11_subscribe_event *event;
+	void *response_buf;
+	int res, cmd_len;
+	ssize_t pos = 0;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+	if (res < 0) {
+		free_page(addr);
+		return res;
+	}
+
+	event = &pcmdptr->params.subscribe_event;
+	event->action = cmd_act_get;
+	pcmdptr->size =
+	cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN);
+	libertas_queue_cmd(adapter, pcmdnode, 1);
+	wake_up_interruptible(&priv->mainthread.waitq);
+
+	/* Sleep until response is generated by FW */
+	wait_event_interruptible(pcmdnode->cmdwait_q,
+				pcmdnode->cmdwaitqwoken);
+
+	pcmdptr = response_buf;
+
+	if (pcmdptr->result) {
+		lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+			pcmdptr->result);
+		kfree(response_buf);
+		free_page(addr);
+		return 0;
+	}
+
+	if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+		lbs_pr_err("command response incorrect!\n");
+		kfree(response_buf);
+		free_page(addr);
+		return 0;
+	}
+
+	cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+	event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN);
+	while (cmd_len < pcmdptr->size) {
+		struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len);
+		switch(header->type) {
+		struct mrvlietypes_snrthreshold *LowSnr;
+		case TLV_TYPE_SNR_LOW:
+		LowSnr = (struct mrvlietypes_snrthreshold *)(response_buf + cmd_len);
+		pos += snprintf(buf+pos, len-pos, "%d %d %d\n",
+				LowSnr->snrvalue,
+				LowSnr->snrfreq,
+				(event->events & 0x0002)?1:0);
+		default:
+			cmd_len += sizeof(struct mrvlietypes_snrthreshold);
+			break;
+		}
+	}
+
+	kfree(response_buf);
+
+	res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+	free_page(addr);
+	return res;
+}
+
+static ssize_t libertas_lowsnr_write(struct file *file,
+				    const char __user *userbuf,
+				    size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	wlan_adapter *adapter = priv->adapter;
+	ssize_t res, buf_size;
+	int value, freq, subscribed, cmd_len;
+	struct cmd_ctrl_node *pcmdnode;
+	struct cmd_ds_command *pcmdptr;
+	struct cmd_ds_802_11_subscribe_event *event;
+	struct mrvlietypes_snrthreshold *snr_threshold;
+	void *response_buf;
+	u16 event_bitmap;
+	u8 *ptr;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed);
+	if (res != 3) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+
+	event_bitmap = libertas_get_events_bitmap(priv);
+
+	res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+	if (res < 0)
+		goto out_unlock;
+
+	event = &pcmdptr->params.subscribe_event;
+	event->action = cmd_act_set;
+	pcmdptr->size = cpu_to_le16(S_DS_GEN +
+		sizeof(struct cmd_ds_802_11_subscribe_event) +
+		sizeof(struct mrvlietypes_snrthreshold));
+	cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+	ptr = (u8*) pcmdptr+cmd_len;
+	snr_threshold = (struct mrvlietypes_snrthreshold *)(ptr);
+	snr_threshold->header.type = cpu_to_le16(TLV_TYPE_SNR_LOW);
+	snr_threshold->header.len = 2;
+	snr_threshold->snrvalue = cpu_to_le16(value);
+	snr_threshold->snrfreq = cpu_to_le16(freq);
+	event_bitmap |= subscribed ? 0x0002 : 0x0;
+	event->events = event_bitmap;
+
+	libertas_queue_cmd(adapter, pcmdnode, 1);
+	wake_up_interruptible(&priv->mainthread.waitq);
+
+	/* Sleep until response is generated by FW */
+	wait_event_interruptible(pcmdnode->cmdwait_q,
+				pcmdnode->cmdwaitqwoken);
+
+	pcmdptr = response_buf;
+
+	if (pcmdptr->result) {
+		lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+			pcmdptr->result);
+		kfree(response_buf);
+		free_page(addr);
+		return 0;
+	}
+
+	if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+		lbs_pr_err("command response incorrect!\n");
+		kfree(response_buf);
+		free_page(addr);
+		return 0;
+	}
+
+	res = count;
+
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+static ssize_t libertas_failcount_read(struct file *file, char __user *userbuf,
+				  size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	wlan_adapter *adapter = priv->adapter;
+	struct cmd_ctrl_node *pcmdnode;
+	struct cmd_ds_command *pcmdptr;
+	struct cmd_ds_802_11_subscribe_event *event;
+	void *response_buf;
+	int res, cmd_len;
+	ssize_t pos = 0;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+	if (res < 0) {
+		free_page(addr);
+		return res;
+	}
+
+	event = &pcmdptr->params.subscribe_event;
+	event->action = cmd_act_get;
+	pcmdptr->size =
+	cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN);
+	libertas_queue_cmd(adapter, pcmdnode, 1);
+	wake_up_interruptible(&priv->mainthread.waitq);
+
+	/* Sleep until response is generated by FW */
+	wait_event_interruptible(pcmdnode->cmdwait_q,
+				pcmdnode->cmdwaitqwoken);
+
+	pcmdptr = response_buf;
+
+	if (pcmdptr->result) {
+		lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+			pcmdptr->result);
+		kfree(response_buf);
+		free_page(addr);
+		return 0;
+	}
+
+	if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+		lbs_pr_err("command response incorrect!\n");
+		kfree(response_buf);
+		free_page(addr);
+		return 0;
+	}
+
+	cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+	event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN);
+	while (cmd_len < pcmdptr->size) {
+		struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len);
+		switch(header->type) {
+		struct mrvlietypes_failurecount *failcount;
+		case TLV_TYPE_FAILCOUNT:
+		failcount = (struct mrvlietypes_failurecount *)(response_buf + cmd_len);
+		pos += snprintf(buf+pos, len-pos, "%d %d %d\n",
+				failcount->failvalue,
+				failcount->Failfreq,
+				(event->events & 0x0004)?1:0);
+		default:
+			cmd_len += sizeof(struct mrvlietypes_failurecount);
+			break;
+		}
+	}
+
+	kfree(response_buf);
+	res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+	free_page(addr);
+	return res;
+}
+
+static ssize_t libertas_failcount_write(struct file *file,
+				    const char __user *userbuf,
+				    size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	wlan_adapter *adapter = priv->adapter;
+	ssize_t res, buf_size;
+	int value, freq, subscribed, cmd_len;
+	struct cmd_ctrl_node *pcmdnode;
+	struct cmd_ds_command *pcmdptr;
+	struct cmd_ds_802_11_subscribe_event *event;
+	struct mrvlietypes_failurecount *failcount;
+	void *response_buf;
+	u16 event_bitmap;
+	u8 *ptr;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed);
+	if (res != 3) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+
+	event_bitmap = libertas_get_events_bitmap(priv);
+
+	res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+	if (res < 0)
+		goto out_unlock;
+
+	event = &pcmdptr->params.subscribe_event;
+	event->action = cmd_act_set;
+	pcmdptr->size = cpu_to_le16(S_DS_GEN +
+		sizeof(struct cmd_ds_802_11_subscribe_event) +
+		sizeof(struct mrvlietypes_failurecount));
+	cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+	ptr = (u8*) pcmdptr+cmd_len;
+	failcount = (struct mrvlietypes_failurecount *)(ptr);
+	failcount->header.type = cpu_to_le16(TLV_TYPE_FAILCOUNT);
+	failcount->header.len = 2;
+	failcount->failvalue = cpu_to_le16(value);
+	failcount->Failfreq = cpu_to_le16(freq);
+	event_bitmap |= subscribed ? 0x0004 : 0x0;
+	event->events = event_bitmap;
+
+	libertas_queue_cmd(adapter, pcmdnode, 1);
+	wake_up_interruptible(&priv->mainthread.waitq);
+
+	/* Sleep until response is generated by FW */
+	wait_event_interruptible(pcmdnode->cmdwait_q,
+				pcmdnode->cmdwaitqwoken);
+
+	pcmdptr = (struct cmd_ds_command *)response_buf;
+
+	if (pcmdptr->result) {
+		lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+			pcmdptr->result);
+		kfree(response_buf);
+		free_page(addr);
+		return 0;
+	}
+
+	if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+		lbs_pr_err("command response incorrect!\n");
+		kfree(response_buf);
+		free_page(addr);
+		return 0;
+	}
+
+	res = count;
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+static ssize_t libertas_bcnmiss_read(struct file *file, char __user *userbuf,
+				  size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	wlan_adapter *adapter = priv->adapter;
+	struct cmd_ctrl_node *pcmdnode;
+	struct cmd_ds_command *pcmdptr;
+	struct cmd_ds_802_11_subscribe_event *event;
+	void *response_buf;
+	int res, cmd_len;
+	ssize_t pos = 0;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+	if (res < 0) {
+		free_page(addr);
+		return res;
+	}
+
+	event = &pcmdptr->params.subscribe_event;
+	event->action = cmd_act_get;
+	pcmdptr->size =
+	cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN);
+	libertas_queue_cmd(adapter, pcmdnode, 1);
+	wake_up_interruptible(&priv->mainthread.waitq);
+
+	/* Sleep until response is generated by FW */
+	wait_event_interruptible(pcmdnode->cmdwait_q,
+				pcmdnode->cmdwaitqwoken);
+
+	pcmdptr = response_buf;
+
+	if (pcmdptr->result) {
+		lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+			pcmdptr->result);
+		free_page(addr);
+		kfree(response_buf);
+		return 0;
+	}
+
+	if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+		lbs_pr_err("command response incorrect!\n");
+		free_page(addr);
+		kfree(response_buf);
+		return 0;
+	}
+
+	cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+	event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN);
+	while (cmd_len < pcmdptr->size) {
+		struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len);
+		switch(header->type) {
+		struct mrvlietypes_beaconsmissed *bcnmiss;
+		case TLV_TYPE_BCNMISS:
+		bcnmiss = (struct mrvlietypes_beaconsmissed *)(response_buf + cmd_len);
+		pos += snprintf(buf+pos, len-pos, "%d N/A %d\n",
+				bcnmiss->beaconmissed,
+				(event->events & 0x0008)?1:0);
+		default:
+			cmd_len += sizeof(struct mrvlietypes_beaconsmissed);
+			break;
+		}
+	}
+
+	kfree(response_buf);
+
+	res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+	free_page(addr);
+	return res;
+}
+
+static ssize_t libertas_bcnmiss_write(struct file *file,
+				    const char __user *userbuf,
+				    size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	wlan_adapter *adapter = priv->adapter;
+	ssize_t res, buf_size;
+	int value, freq, subscribed, cmd_len;
+	struct cmd_ctrl_node *pcmdnode;
+	struct cmd_ds_command *pcmdptr;
+	struct cmd_ds_802_11_subscribe_event *event;
+	struct mrvlietypes_beaconsmissed *bcnmiss;
+	void *response_buf;
+	u16 event_bitmap;
+	u8 *ptr;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed);
+	if (res != 3) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+
+	event_bitmap = libertas_get_events_bitmap(priv);
+
+	res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+	if (res < 0)
+		goto out_unlock;
+
+	event = &pcmdptr->params.subscribe_event;
+	event->action = cmd_act_set;
+	pcmdptr->size = cpu_to_le16(S_DS_GEN +
+		sizeof(struct cmd_ds_802_11_subscribe_event) +
+		sizeof(struct mrvlietypes_beaconsmissed));
+	cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+	ptr = (u8*) pcmdptr+cmd_len;
+	bcnmiss = (struct mrvlietypes_beaconsmissed *)(ptr);
+	bcnmiss->header.type = cpu_to_le16(TLV_TYPE_BCNMISS);
+	bcnmiss->header.len = 2;
+	bcnmiss->beaconmissed = cpu_to_le16(value);
+	event_bitmap |= subscribed ? 0x0008 : 0x0;
+	event->events = event_bitmap;
+
+	libertas_queue_cmd(adapter, pcmdnode, 1);
+	wake_up_interruptible(&priv->mainthread.waitq);
+
+	/* Sleep until response is generated by FW */
+	wait_event_interruptible(pcmdnode->cmdwait_q,
+				pcmdnode->cmdwaitqwoken);
+
+	pcmdptr = response_buf;
+
+	if (pcmdptr->result) {
+		lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+			pcmdptr->result);
+		kfree(response_buf);
+		free_page(addr);
+		return 0;
+	}
+
+	if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+		lbs_pr_err("command response incorrect!\n");
+		free_page(addr);
+		kfree(response_buf);
+		return 0;
+	}
+
+	res = count;
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+static ssize_t libertas_highrssi_read(struct file *file, char __user *userbuf,
+				  size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	wlan_adapter *adapter = priv->adapter;
+	struct cmd_ctrl_node *pcmdnode;
+	struct cmd_ds_command *pcmdptr;
+	struct cmd_ds_802_11_subscribe_event *event;
+	void *response_buf;
+	int res, cmd_len;
+	ssize_t pos = 0;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+	if (res < 0) {
+		free_page(addr);
+		return res;
+	}
+
+	event = &pcmdptr->params.subscribe_event;
+	event->action = cmd_act_get;
+	pcmdptr->size =
+	cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN);
+	libertas_queue_cmd(adapter, pcmdnode, 1);
+	wake_up_interruptible(&priv->mainthread.waitq);
+
+	/* Sleep until response is generated by FW */
+	wait_event_interruptible(pcmdnode->cmdwait_q,
+				pcmdnode->cmdwaitqwoken);
+
+	pcmdptr = response_buf;
+
+	if (pcmdptr->result) {
+		lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+			pcmdptr->result);
+		kfree(response_buf);
+		free_page(addr);
+		return 0;
+	}
+
+	if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+		lbs_pr_err("command response incorrect!\n");
+		kfree(response_buf);
+		free_page(addr);
+		return 0;
+	}
+
+	cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+	event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN);
+	while (cmd_len < pcmdptr->size) {
+		struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len);
+		switch(header->type) {
+		struct mrvlietypes_rssithreshold  *Highrssi;
+		case TLV_TYPE_RSSI_HIGH:
+		Highrssi = (struct mrvlietypes_rssithreshold *)(response_buf + cmd_len);
+		pos += snprintf(buf+pos, len-pos, "%d %d %d\n",
+				Highrssi->rssivalue,
+				Highrssi->rssifreq,
+				(event->events & 0x0010)?1:0);
+		default:
+			cmd_len += sizeof(struct mrvlietypes_snrthreshold);
+			break;
+		}
+	}
+
+	kfree(response_buf);
+
+	res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+	free_page(addr);
+	return res;
+}
+
+static ssize_t libertas_highrssi_write(struct file *file,
+				    const char __user *userbuf,
+				    size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	wlan_adapter *adapter = priv->adapter;
+	ssize_t res, buf_size;
+	int value, freq, subscribed, cmd_len;
+	struct cmd_ctrl_node *pcmdnode;
+	struct cmd_ds_command *pcmdptr;
+	struct cmd_ds_802_11_subscribe_event *event;
+	struct mrvlietypes_rssithreshold *rssi_threshold;
+	void *response_buf;
+	u16 event_bitmap;
+	u8 *ptr;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed);
+	if (res != 3) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+
+	event_bitmap = libertas_get_events_bitmap(priv);
+
+	res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+	if (res < 0)
+		goto out_unlock;
+
+	event = &pcmdptr->params.subscribe_event;
+	event->action = cmd_act_set;
+	pcmdptr->size = cpu_to_le16(S_DS_GEN +
+		sizeof(struct cmd_ds_802_11_subscribe_event) +
+		sizeof(struct mrvlietypes_rssithreshold));
+	cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+	ptr = (u8*) pcmdptr+cmd_len;
+	rssi_threshold = (struct mrvlietypes_rssithreshold *)(ptr);
+	rssi_threshold->header.type = cpu_to_le16(TLV_TYPE_RSSI_HIGH);
+	rssi_threshold->header.len = 2;
+	rssi_threshold->rssivalue = cpu_to_le16(value);
+	rssi_threshold->rssifreq = cpu_to_le16(freq);
+	event_bitmap |= subscribed ? 0x0010 : 0x0;
+	event->events = event_bitmap;
+
+	libertas_queue_cmd(adapter, pcmdnode, 1);
+	wake_up_interruptible(&priv->mainthread.waitq);
+
+	/* Sleep until response is generated by FW */
+	wait_event_interruptible(pcmdnode->cmdwait_q,
+				pcmdnode->cmdwaitqwoken);
+
+	pcmdptr = response_buf;
+
+	if (pcmdptr->result) {
+		lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+			pcmdptr->result);
+		kfree(response_buf);
+		return 0;
+	}
+
+	if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+		lbs_pr_err("command response incorrect!\n");
+		kfree(response_buf);
+		return 0;
+	}
+
+	res = count;
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+static ssize_t libertas_highsnr_read(struct file *file, char __user *userbuf,
+				  size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	wlan_adapter *adapter = priv->adapter;
+	struct cmd_ctrl_node *pcmdnode;
+	struct cmd_ds_command *pcmdptr;
+	struct cmd_ds_802_11_subscribe_event *event;
+	void *response_buf;
+	int res, cmd_len;
+	ssize_t pos = 0;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+	if (res < 0) {
+		free_page(addr);
+		return res;
+	}
+
+	event = &pcmdptr->params.subscribe_event;
+	event->action = cmd_act_get;
+	pcmdptr->size =
+	cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN);
+	libertas_queue_cmd(adapter, pcmdnode, 1);
+	wake_up_interruptible(&priv->mainthread.waitq);
+
+	/* Sleep until response is generated by FW */
+	wait_event_interruptible(pcmdnode->cmdwait_q,
+				pcmdnode->cmdwaitqwoken);
+
+	pcmdptr = response_buf;
+
+	if (pcmdptr->result) {
+		lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+			pcmdptr->result);
+		kfree(response_buf);
+		free_page(addr);
+		return 0;
+	}
+
+	if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+		lbs_pr_err("command response incorrect!\n");
+		kfree(response_buf);
+		free_page(addr);
+		return 0;
+	}
+
+	cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+	event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN);
+	while (cmd_len < pcmdptr->size) {
+		struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len);
+		switch(header->type) {
+		struct mrvlietypes_snrthreshold *HighSnr;
+		case TLV_TYPE_SNR_HIGH:
+		HighSnr = (struct mrvlietypes_snrthreshold *)(response_buf + cmd_len);
+		pos += snprintf(buf+pos, len-pos, "%d %d %d\n",
+				HighSnr->snrvalue,
+				HighSnr->snrfreq,
+				(event->events & 0x0020)?1:0);
+		default:
+			cmd_len += sizeof(struct mrvlietypes_snrthreshold);
+			break;
+		}
+	}
+
+	kfree(response_buf);
+
+	res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+	free_page(addr);
+	return res;
+}
+
+static ssize_t libertas_highsnr_write(struct file *file,
+				    const char __user *userbuf,
+				    size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	wlan_adapter *adapter = priv->adapter;
+	ssize_t res, buf_size;
+	int value, freq, subscribed, cmd_len;
+	struct cmd_ctrl_node *pcmdnode;
+	struct cmd_ds_command *pcmdptr;
+	struct cmd_ds_802_11_subscribe_event *event;
+	struct mrvlietypes_snrthreshold *snr_threshold;
+	void *response_buf;
+	u16 event_bitmap;
+	u8 *ptr;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed);
+	if (res != 3) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+
+	event_bitmap = libertas_get_events_bitmap(priv);
+
+	res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+	if (res < 0)
+		goto out_unlock;
+
+	event = &pcmdptr->params.subscribe_event;
+	event->action = cmd_act_set;
+	pcmdptr->size = cpu_to_le16(S_DS_GEN +
+		sizeof(struct cmd_ds_802_11_subscribe_event) +
+		sizeof(struct mrvlietypes_snrthreshold));
+	cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+	ptr = (u8*) pcmdptr+cmd_len;
+	snr_threshold = (struct mrvlietypes_snrthreshold *)(ptr);
+	snr_threshold->header.type = cpu_to_le16(TLV_TYPE_SNR_HIGH);
+	snr_threshold->header.len = 2;
+	snr_threshold->snrvalue = cpu_to_le16(value);
+	snr_threshold->snrfreq = cpu_to_le16(freq);
+	event_bitmap |= subscribed ? 0x0020 : 0x0;
+	event->events = event_bitmap;
+
+	libertas_queue_cmd(adapter, pcmdnode, 1);
+	wake_up_interruptible(&priv->mainthread.waitq);
+
+	/* Sleep until response is generated by FW */
+	wait_event_interruptible(pcmdnode->cmdwait_q,
+				pcmdnode->cmdwaitqwoken);
+
+	pcmdptr = response_buf;
+
+	if (pcmdptr->result) {
+		lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+			pcmdptr->result);
+		kfree(response_buf);
+		free_page(addr);
+		return 0;
+	}
+
+	if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+		lbs_pr_err("command response incorrect!\n");
+		kfree(response_buf);
+		free_page(addr);
+		return 0;
+	}
+
+	res = count;
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+static ssize_t libertas_rdmac_read(struct file *file, char __user *userbuf,
+				  size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	wlan_adapter *adapter = priv->adapter;
+	struct wlan_offset_value offval;
+	ssize_t pos = 0;
+	int ret;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	offval.offset = priv->mac_offset;
+	offval.value = 0;
+
+	ret = libertas_prepare_and_send_command(priv,
+				cmd_mac_reg_access, 0,
+				cmd_option_waitforrsp, 0, &offval);
+	mdelay(10);
+	pos += snprintf(buf+pos, len-pos, "MAC[0x%x] = 0x%08x\n",
+				priv->mac_offset, adapter->offsetvalue.value);
+
+	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+	free_page(addr);
+	return ret;
+}
+
+static ssize_t libertas_rdmac_write(struct file *file,
+				    const char __user *userbuf,
+				    size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	ssize_t res, buf_size;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	priv->mac_offset = simple_strtoul((char *)buf, NULL, 16);
+	res = count;
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+static ssize_t libertas_wrmac_write(struct file *file,
+				    const char __user *userbuf,
+				    size_t count, loff_t *ppos)
+{
+
+	wlan_private *priv = file->private_data;
+	ssize_t res, buf_size;
+	u32 offset, value;
+	struct wlan_offset_value offval;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	res = sscanf(buf, "%x %x", &offset, &value);
+	if (res != 2) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+
+	offval.offset = offset;
+	offval.value = value;
+	res = libertas_prepare_and_send_command(priv,
+				cmd_mac_reg_access, 1,
+				cmd_option_waitforrsp, 0, &offval);
+	mdelay(10);
+
+	res = count;
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+static ssize_t libertas_rdbbp_read(struct file *file, char __user *userbuf,
+				  size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	wlan_adapter *adapter = priv->adapter;
+	struct wlan_offset_value offval;
+	ssize_t pos = 0;
+	int ret;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	offval.offset = priv->bbp_offset;
+	offval.value = 0;
+
+	ret = libertas_prepare_and_send_command(priv,
+				cmd_bbp_reg_access, 0,
+				cmd_option_waitforrsp, 0, &offval);
+	mdelay(10);
+	pos += snprintf(buf+pos, len-pos, "BBP[0x%x] = 0x%08x\n",
+				priv->bbp_offset, adapter->offsetvalue.value);
+
+	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+	free_page(addr);
+
+	return ret;
+}
+
+static ssize_t libertas_rdbbp_write(struct file *file,
+				    const char __user *userbuf,
+				    size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	ssize_t res, buf_size;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	priv->bbp_offset = simple_strtoul((char *)buf, NULL, 16);
+	res = count;
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+static ssize_t libertas_wrbbp_write(struct file *file,
+				    const char __user *userbuf,
+				    size_t count, loff_t *ppos)
+{
+
+	wlan_private *priv = file->private_data;
+	ssize_t res, buf_size;
+	u32 offset, value;
+	struct wlan_offset_value offval;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	res = sscanf(buf, "%x %x", &offset, &value);
+	if (res != 2) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+
+	offval.offset = offset;
+	offval.value = value;
+	res = libertas_prepare_and_send_command(priv,
+				cmd_bbp_reg_access, 1,
+				cmd_option_waitforrsp, 0, &offval);
+	mdelay(10);
+
+	res = count;
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+static ssize_t libertas_rdrf_read(struct file *file, char __user *userbuf,
+				  size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	wlan_adapter *adapter = priv->adapter;
+	struct wlan_offset_value offval;
+	ssize_t pos = 0;
+	int ret;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	offval.offset = priv->rf_offset;
+	offval.value = 0;
+
+	ret = libertas_prepare_and_send_command(priv,
+				cmd_rf_reg_access, 0,
+				cmd_option_waitforrsp, 0, &offval);
+	mdelay(10);
+	pos += snprintf(buf+pos, len-pos, "RF[0x%x] = 0x%08x\n",
+				priv->rf_offset, adapter->offsetvalue.value);
+
+	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+	free_page(addr);
+
+	return ret;
+}
+
+static ssize_t libertas_rdrf_write(struct file *file,
+				    const char __user *userbuf,
+				    size_t count, loff_t *ppos)
+{
+	wlan_private *priv = file->private_data;
+	ssize_t res, buf_size;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	priv->rf_offset = simple_strtoul((char *)buf, NULL, 16);
+	res = count;
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+static ssize_t libertas_wrrf_write(struct file *file,
+				    const char __user *userbuf,
+				    size_t count, loff_t *ppos)
+{
+
+	wlan_private *priv = file->private_data;
+	ssize_t res, buf_size;
+	u32 offset, value;
+	struct wlan_offset_value offval;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	res = sscanf(buf, "%x %x", &offset, &value);
+	if (res != 2) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+
+	offval.offset = offset;
+	offval.value = value;
+	res = libertas_prepare_and_send_command(priv,
+				cmd_rf_reg_access, 1,
+				cmd_option_waitforrsp, 0, &offval);
+	mdelay(10);
+
+	res = count;
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+#define FOPS(fread, fwrite) { \
+	.owner = THIS_MODULE, \
+	.open = open_file_generic, \
+	.read = (fread), \
+	.write = (fwrite), \
+}
+
+struct libertas_debugfs_files {
+	char *name;
+	int perm;
+	struct file_operations fops;
+};
+
+struct libertas_debugfs_files debugfs_files[] = {
+	{ "info", 0444, FOPS(libertas_dev_info, write_file_dummy), },
+	{ "getscantable", 0444, FOPS(libertas_getscantable,
+					write_file_dummy), },
+	{ "sleepparams", 0644, FOPS(libertas_sleepparams_read,
+				libertas_sleepparams_write), },
+	{ "extscan", 0600, FOPS(NULL, libertas_extscan), },
+	{ "setuserscan", 0600, FOPS(NULL, libertas_setuserscan), },
+};
+
+struct libertas_debugfs_files debugfs_events_files[] = {
+	{"low_rssi", 0644, FOPS(libertas_lowrssi_read,
+				libertas_lowrssi_write), },
+	{"low_snr", 0644, FOPS(libertas_lowsnr_read,
+				libertas_lowsnr_write), },
+	{"failure_count", 0644, FOPS(libertas_failcount_read,
+				libertas_failcount_write), },
+	{"beacon_missed", 0644, FOPS(libertas_bcnmiss_read,
+				libertas_bcnmiss_write), },
+	{"high_rssi", 0644, FOPS(libertas_highrssi_read,
+				libertas_highrssi_write), },
+	{"high_snr", 0644, FOPS(libertas_highsnr_read,
+				libertas_highsnr_write), },
+};
+
+struct libertas_debugfs_files debugfs_regs_files[] = {
+	{"rdmac", 0644, FOPS(libertas_rdmac_read, libertas_rdmac_write), },
+	{"wrmac", 0600, FOPS(NULL, libertas_wrmac_write), },
+	{"rdbbp", 0644, FOPS(libertas_rdbbp_read, libertas_rdbbp_write), },
+	{"wrbbp", 0600, FOPS(NULL, libertas_wrbbp_write), },
+	{"rdrf", 0644, FOPS(libertas_rdrf_read, libertas_rdrf_write), },
+	{"wrrf", 0600, FOPS(NULL, libertas_wrrf_write), },
+};
+
+void libertas_debugfs_init(void)
+{
+	if (!libertas_dir)
+		libertas_dir = debugfs_create_dir("libertas_wireless", NULL);
+
+	return;
+}
+
+void libertas_debugfs_remove(void)
+{
+	if (libertas_dir)
+		 debugfs_remove(libertas_dir);
+	return;
+}
+
+void libertas_debugfs_init_one(wlan_private *priv, struct net_device *dev)
+{
+	int i;
+	struct libertas_debugfs_files *files;
+	if (!libertas_dir)
+		goto exit;
+
+	priv->debugfs_dir = debugfs_create_dir(dev->name, libertas_dir);
+	if (!priv->debugfs_dir)
+		goto exit;
+
+	for (i=0; i<ARRAY_SIZE(debugfs_files); i++) {
+		files = &debugfs_files[i];
+		priv->debugfs_files[i] = debugfs_create_file(files->name,
+							     files->perm,
+							     priv->debugfs_dir,
+							     priv,
+							     &files->fops);
+	}
+
+	priv->events_dir = debugfs_create_dir("subscribed_events", priv->debugfs_dir);
+	if (!priv->events_dir)
+		goto exit;
+
+	for (i=0; i<ARRAY_SIZE(debugfs_events_files); i++) {
+		files = &debugfs_events_files[i];
+		priv->debugfs_events_files[i] = debugfs_create_file(files->name,
+							     files->perm,
+							     priv->events_dir,
+							     priv,
+							     &files->fops);
+	}
+
+	priv->regs_dir = debugfs_create_dir("registers", priv->debugfs_dir);
+	if (!priv->regs_dir)
+		goto exit;
+
+	for (i=0; i<ARRAY_SIZE(debugfs_regs_files); i++) {
+		files = &debugfs_regs_files[i];
+		priv->debugfs_regs_files[i] = debugfs_create_file(files->name,
+							     files->perm,
+							     priv->regs_dir,
+							     priv,
+							     &files->fops);
+	}
+
+#ifdef PROC_DEBUG
+	libertas_debug_init(priv, dev);
+#endif
+exit:
+	return;
+}
+
+void libertas_debugfs_remove_one(wlan_private *priv)
+{
+	int i;
+
+	for(i=0; i<ARRAY_SIZE(debugfs_regs_files); i++)
+		debugfs_remove(priv->debugfs_regs_files[i]);
+
+	debugfs_remove(priv->regs_dir);
+
+	for(i=0; i<ARRAY_SIZE(debugfs_files); i++)
+		debugfs_remove(priv->debugfs_events_files[i]);
+
+	debugfs_remove(priv->events_dir);
+#ifdef PROC_DEBUG
+	debugfs_remove(priv->debugfs_debug);
+#endif
+	for(i=0; i<ARRAY_SIZE(debugfs_files); i++)
+		debugfs_remove(priv->debugfs_files[i]);
+}
+
+/* debug entry */
+
+#define item_size(n)	(FIELD_SIZEOF(wlan_adapter, n))
+#define item_addr(n)	(offsetof(wlan_adapter, n))
+
+struct debug_data {
+	char name[32];
+	u32 size;
+	u32 addr;
+};
+
+/* To debug any member of wlan_adapter, simply add one line here.
+ */
+static struct debug_data items[] = {
+	{"intcounter", item_size(intcounter), item_addr(intcounter)},
+	{"psmode", item_size(psmode), item_addr(psmode)},
+	{"psstate", item_size(psstate), item_addr(psstate)},
+};
+
+static int num_of_items = ARRAY_SIZE(items);
+
+/**
+ *  @brief proc read function
+ *
+ *  @param page	   pointer to buffer
+ *  @param s       read data starting position
+ *  @param off     offset
+ *  @param cnt     counter
+ *  @param eof     end of file flag
+ *  @param data    data to output
+ *  @return 	   number of output data
+ */
+static ssize_t wlan_debugfs_read(struct file *file, char __user *userbuf,
+			size_t count, loff_t *ppos)
+{
+	int val = 0;
+	size_t pos = 0;
+	ssize_t res;
+	char *p;
+	int i;
+	struct debug_data *d;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	p = buf;
+
+	d = (struct debug_data *)file->private_data;
+
+	for (i = 0; i < num_of_items; i++) {
+		if (d[i].size == 1)
+			val = *((u8 *) d[i].addr);
+		else if (d[i].size == 2)
+			val = *((u16 *) d[i].addr);
+		else if (d[i].size == 4)
+			val = *((u32 *) d[i].addr);
+
+		pos += sprintf(p + pos, "%s=%d\n", d[i].name, val);
+	}
+
+	res = simple_read_from_buffer(userbuf, count, ppos, p, pos);
+
+	free_page(addr);
+	return res;
+}
+
+/**
+ *  @brief proc write function
+ *
+ *  @param f	   file pointer
+ *  @param buf     pointer to data buffer
+ *  @param cnt     data number to write
+ *  @param data    data to write
+ *  @return 	   number of data
+ */
+static int wlan_debugfs_write(struct file *f, const char __user *buf,
+			    size_t cnt, loff_t *ppos)
+{
+	int r, i;
+	char *pdata;
+	char *p;
+	char *p0;
+	char *p1;
+	char *p2;
+	struct debug_data *d = (struct debug_data *)f->private_data;
+
+	pdata = (char *)kmalloc(cnt, GFP_KERNEL);
+	if (pdata == NULL)
+		return 0;
+
+	if (copy_from_user(pdata, buf, cnt)) {
+		lbs_pr_debug(1, "Copy from user failed\n");
+		kfree(pdata);
+		return 0;
+	}
+
+	p0 = pdata;
+	for (i = 0; i < num_of_items; i++) {
+		do {
+			p = strstr(p0, d[i].name);
+			if (p == NULL)
+				break;
+			p1 = strchr(p, '\n');
+			if (p1 == NULL)
+				break;
+			p0 = p1++;
+			p2 = strchr(p, '=');
+			if (!p2)
+				break;
+			p2++;
+			r = simple_strtoul(p2, NULL, 0);
+			if (d[i].size == 1)
+				*((u8 *) d[i].addr) = (u8) r;
+			else if (d[i].size == 2)
+				*((u16 *) d[i].addr) = (u16) r;
+			else if (d[i].size == 4)
+				*((u32 *) d[i].addr) = (u32) r;
+			break;
+		} while (1);
+	}
+	kfree(pdata);
+
+	return cnt;
+}
+
+static struct file_operations libertas_debug_fops = {
+	.owner = THIS_MODULE,
+	.open = open_file_generic,
+	.write = wlan_debugfs_write,
+	.read = wlan_debugfs_read,
+};
+
+/**
+ *  @brief create debug proc file
+ *
+ *  @param priv	   pointer wlan_private
+ *  @param dev     pointer net_device
+ *  @return 	   N/A
+ */
+void libertas_debug_init(wlan_private * priv, struct net_device *dev)
+{
+	int i;
+
+	if (!priv->debugfs_dir)
+		return;
+
+	for (i = 0; i < num_of_items; i++)
+		items[i].addr += (u32) priv->adapter;
+
+	priv->debugfs_debug = debugfs_create_file("debug", 0644,
+						  priv->debugfs_dir, &items[0],
+						  &libertas_debug_fops);
+}
+
+/**
+ *  @brief remove proc file
+ *
+ *  @param priv	   pointer wlan_private
+ *  @return 	   N/A
+ */
+void libertas_debug_remove(wlan_private * priv)
+{
+	debugfs_remove(priv->debugfs_debug);
+}
diff --git a/drivers/net/wireless/libertas/debugfs.h b/drivers/net/wireless/libertas/debugfs.h
new file mode 100644
index 0000000..880a11b
--- /dev/null
+++ b/drivers/net/wireless/libertas/debugfs.h
@@ -0,0 +1,6 @@
+void libertas_debugfs_init(void);
+void libertas_debugfs_remove(void);
+
+void libertas_debugfs_init_one(wlan_private *priv, struct net_device *dev);
+void libertas_debugfs_remove_one(wlan_private *priv);
+
diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h
new file mode 100644
index 0000000..606bdd0
--- /dev/null
+++ b/drivers/net/wireless/libertas/decl.h
@@ -0,0 +1,83 @@
+/**
+  *  This file contains declaration referring to
+  *  functions defined in other source files
+  */
+
+#ifndef _WLAN_DECL_H_
+#define _WLAN_DECL_H_
+
+#include "defs.h"
+
+/** Function Prototype Declaration */
+struct wlan_private;
+struct sk_buff;
+struct net_device;
+
+extern char *libertas_fw_name;
+
+void libertas_free_adapter(wlan_private * priv);
+int libertas_set_mac_packet_filter(wlan_private * priv);
+
+int libertas_send_null_packet(wlan_private * priv, u8 pwr_mgmt);
+void libertas_send_tx_feedback(wlan_private * priv);
+u8 libertas_check_last_packet_indication(wlan_private * priv);
+
+int libertas_free_cmd_buffer(wlan_private * priv);
+struct cmd_ctrl_node;
+struct cmd_ctrl_node *libertas_get_free_cmd_ctrl_node(wlan_private * priv);
+
+void libertas_set_cmd_ctrl_node(wlan_private * priv,
+		    struct cmd_ctrl_node *ptempnode,
+		    u32 cmd_oid, u16 wait_option, void *pdata_buf);
+
+int libertas_prepare_and_send_command(wlan_private * priv,
+			  u16 cmd_no,
+			  u16 cmd_action,
+			  u16 wait_option, u32 cmd_oid, void *pdata_buf);
+
+void libertas_queue_cmd(wlan_adapter * adapter, struct cmd_ctrl_node *cmdnode, u8 addtail);
+
+int libertas_allocate_cmd_buffer(wlan_private * priv);
+int libertas_execute_next_command(wlan_private * priv);
+int libertas_process_event(wlan_private * priv);
+void libertas_interrupt(struct net_device *);
+int libertas_set_radio_control(wlan_private * priv);
+u32 libertas_index_to_data_rate(u8 index);
+u8 libertas_data_rate_to_index(u32 rate);
+void libertas_get_fwversion(wlan_adapter * adapter, char *fwversion, int maxlen);
+
+int libertas_upload_rx_packet(wlan_private * priv, struct sk_buff *skb);
+
+/** The proc fs interface */
+int libertas_process_rx_command(wlan_private * priv);
+int libertas_process_tx(wlan_private * priv, struct sk_buff *skb);
+void libertas_cleanup_and_insert_cmd(wlan_private * priv,
+					struct cmd_ctrl_node *ptempcmd);
+void __libertas_cleanup_and_insert_cmd(wlan_private * priv,
+					struct cmd_ctrl_node *ptempcmd);
+
+int libertas_set_regiontable(wlan_private * priv, u8 region, u8 band);
+
+int libertas_process_rxed_packet(wlan_private * priv, struct sk_buff *);
+
+void libertas_ps_sleep(wlan_private * priv, int wait_option);
+void libertas_ps_confirm_sleep(wlan_private * priv, u16 psmode);
+void libertas_ps_wakeup(wlan_private * priv, int wait_option);
+
+void libertas_tx_runqueue(wlan_private *priv);
+
+extern struct chan_freq_power *libertas_find_cfp_by_band_and_channel(
+				wlan_adapter * adapter, u8 band, u16 channel);
+
+extern void libertas_mac_event_disconnected(wlan_private * priv);
+
+void libertas_send_iwevcustom_event(wlan_private * priv, s8 * str);
+
+int reset_device(wlan_private *priv);
+/* main.c */
+extern struct chan_freq_power *libertas_get_region_cfp_table(u8 region, u8 band,
+						             int *cfp_no);
+wlan_private *wlan_add_card(void *card);
+int wlan_remove_card(void *card);
+
+#endif				/* _WLAN_DECL_H_ */
diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h
new file mode 100644
index 0000000..fb1478c
--- /dev/null
+++ b/drivers/net/wireless/libertas/defs.h
@@ -0,0 +1,369 @@
+/**
+  * This header file contains global constant/enum definitions,
+  * global variable declaration.
+  */
+#ifndef _WLAN_DEFS_H_
+#define _WLAN_DEFS_H_
+
+#include <linux/spinlock.h>
+
+extern unsigned int libertas_debug;
+
+#define DRV_NAME		"usb8xxx"
+
+#define lbs_pr_info(format, args...) \
+	printk(KERN_INFO DRV_NAME": " format, ## args)
+#define lbs_pr_err(format, args...) \
+	printk(KERN_ERR DRV_NAME": " format, ## args)
+#define lbs_pr_alert(format, args...) \
+	printk(KERN_ALERT DRV_NAME": " format, ## args)
+
+#ifdef DEBUG
+#define lbs_pr_debug(level, format, args...) \
+	do { if (libertas_debug >= level) \
+	printk(KERN_INFO DRV_NAME": " format, ##args); } while (0)
+#define lbs_dev_dbg(level, device, format, args...) \
+        lbs_pr_debug(level, "%s: " format, \
+        (device)->bus_id , ## args)
+
+static inline void lbs_dbg_hex(char *prompt, u8 * buf, int len)
+{
+	int i = 0;
+
+	if (!libertas_debug)
+		return;
+
+	printk(KERN_DEBUG "%s: ", prompt);
+	for (i = 1; i <= len; i++) {
+		printk(KERN_DEBUG "%02x ", (u8) * buf);
+		buf++;
+	}
+	printk("\n");
+}
+#else
+#define lbs_pr_debug(level, format, args...)		do {} while (0)
+#define lbs_dev_dbg(level, device, format, args...)	do {} while (0)
+#define lbs_dbg_hex(x,y,z)				do {} while (0)
+#endif
+
+#define	ENTER()			lbs_pr_debug(1, "Enter: %s, %s:%i\n", \
+					__FUNCTION__, __FILE__, __LINE__)
+#define	LEAVE()			lbs_pr_debug(1, "Leave: %s, %s:%i\n", \
+					__FUNCTION__, __FILE__, __LINE__)
+
+/** Buffer Constants */
+
+/*	The size of SQ memory PPA, DPA are 8 DWORDs, that keep the physical
+*	addresses of TxPD buffers. Station has only 8 TxPD available, Whereas
+*	driver has more local TxPDs. Each TxPD on the host memory is associated
+*	with a Tx control node. The driver maintains 8 RxPD descriptors for
+*	station firmware to store Rx packet information.
+*
+*	Current version of MAC has a 32x6 multicast address buffer.
+*
+*	802.11b can have up to  14 channels, the driver keeps the
+*	BSSID(MAC address) of each APs or Ad hoc stations it has sensed.
+*/
+
+#define MRVDRV_MAX_MULTICAST_LIST_SIZE	32
+#define MRVDRV_NUM_OF_CMD_BUFFER        10
+#define MRVDRV_SIZE_OF_CMD_BUFFER       (2 * 1024)
+#define MRVDRV_MAX_CHANNEL_SIZE		14
+#define MRVDRV_MAX_BSSID_LIST		64
+#define MRVDRV_ASSOCIATION_TIME_OUT	255
+#define MRVDRV_SNAP_HEADER_LEN          8
+
+#define	WLAN_UPLD_SIZE			2312
+#define DEV_NAME_LEN			32
+
+/** Misc constants */
+/* This section defines 802.11 specific contants */
+
+#define MRVDRV_MAX_BSS_DESCRIPTS		16
+#define MRVDRV_MAX_REGION_CODE			6
+
+#define MRVDRV_IGNORE_MULTIPLE_DTIM		0xfffe
+#define MRVDRV_MIN_MULTIPLE_DTIM		1
+#define MRVDRV_MAX_MULTIPLE_DTIM		5
+#define MRVDRV_DEFAULT_MULTIPLE_DTIM		1
+
+#define MRVDRV_DEFAULT_LISTEN_INTERVAL		10
+
+#define	MRVDRV_CHANNELS_PER_SCAN		4
+#define	MRVDRV_MAX_CHANNELS_PER_SCAN		14
+
+#define MRVDRV_DEBUG_RX_PATH		0x00000001
+#define MRVDRV_DEBUG_TX_PATH		0x00000002
+
+#define MRVDRV_MIN_BEACON_INTERVAL		20
+#define MRVDRV_MAX_BEACON_INTERVAL		1000
+#define MRVDRV_BEACON_INTERVAL			100
+
+/** TxPD status */
+
+/*	Station firmware use TxPD status field to report final Tx transmit
+*	result, Bit masks are used to present combined situations.
+*/
+
+#define MRVDRV_TxPD_POWER_MGMT_NULL_PACKET 0x01
+#define MRVDRV_TxPD_POWER_MGMT_LAST_PACKET 0x08
+
+/** Tx mesh flag */
+/* Currently we are using normal WDS flag as mesh flag.
+ * TODO: change to proper mesh flag when MAC understands it.
+ */
+#define TxPD_CONTROL_WDS_FRAME (1<<17)
+#define TxPD_MESH_FRAME TxPD_CONTROL_WDS_FRAME
+
+/** RxPD status */
+
+#define MRVDRV_RXPD_STATUS_OK                0x0001
+
+/** RxPD status - Received packet types */
+/** Rx mesh flag */
+/* Currently we are using normal WDS flag as mesh flag.
+ * TODO: change to proper mesh flag when MAC understands it.
+ */
+#define RxPD_CONTROL_WDS_FRAME (0x40)
+#define RxPD_MESH_FRAME RxPD_CONTROL_WDS_FRAME
+
+/** RSSI-related defines */
+/*	RSSI constants are used to implement 802.11 RSSI threshold
+*	indication. if the Rx packet signal got too weak for 5 consecutive
+*	times, miniport driver (driver) will report this event to wrapper
+*/
+
+#define MRVDRV_NF_DEFAULT_SCAN_VALUE		(-96)
+
+/** RTS/FRAG related defines */
+#define MRVDRV_RTS_MIN_VALUE		0
+#define MRVDRV_RTS_MAX_VALUE		2347
+#define MRVDRV_FRAG_MIN_VALUE		256
+#define MRVDRV_FRAG_MAX_VALUE		2346
+
+/* This is for firmware specific length */
+#define EXTRA_LEN	36
+
+#define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \
+	(ETH_FRAME_LEN + sizeof(struct txpd) + EXTRA_LEN)
+
+#define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \
+	(ETH_FRAME_LEN + sizeof(struct rxpd) \
+	 + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN)
+
+#define	CMD_F_HOSTCMD		(1 << 0)
+#define FW_CAPINFO_WPA  	(1 << 0)
+
+/** WPA key LENGTH*/
+#define MRVL_MAX_KEY_WPA_KEY_LENGTH     32
+
+#define KEY_LEN_WPA_AES			16
+#define KEY_LEN_WPA_TKIP		32
+#define KEY_LEN_WEP_104			13
+#define KEY_LEN_WEP_40			5
+
+#define RF_ANTENNA_1		0x1
+#define RF_ANTENNA_2		0x2
+#define RF_ANTENNA_AUTO		0xFFFF
+
+#define	BAND_B			(0x01)
+#define	BAND_G			(0x02)
+#define ALL_802_11_BANDS	(BAND_B | BAND_G)
+
+/** MACRO DEFINITIONS */
+#define CAL_NF(NF)			((s32)(-(s32)(NF)))
+#define CAL_RSSI(SNR, NF) 		((s32)((s32)(SNR) + CAL_NF(NF)))
+#define SCAN_RSSI(RSSI)			(0x100 - ((u8)(RSSI)))
+
+#define DEFAULT_BCN_AVG_FACTOR		8
+#define DEFAULT_DATA_AVG_FACTOR		8
+#define AVG_SCALE			100
+#define CAL_AVG_SNR_NF(AVG, SNRNF, N)         \
+                        (((AVG) == 0) ? ((u16)(SNRNF) * AVG_SCALE) : \
+                        ((((int)(AVG) * (N -1)) + ((u16)(SNRNF) * \
+                        AVG_SCALE))  / N))
+
+#define B_SUPPORTED_RATES		8
+#define G_SUPPORTED_RATES		14
+
+#define	WLAN_SUPPORTED_RATES		14
+
+#define	MAX_LEDS			8
+
+#define IS_MESH_FRAME(x) (x->cb[6])
+#define SET_MESH_FRAME(x) (x->cb[6]=1)
+#define UNSET_MESH_FRAME(x) (x->cb[6]=0)
+
+/** Global Variable Declaration */
+typedef struct _wlan_private wlan_private;
+typedef struct _wlan_adapter wlan_adapter;
+extern const char libertas_driver_version[];
+extern u16 libertas_region_code_to_index[MRVDRV_MAX_REGION_CODE];
+
+extern u8 libertas_wlan_data_rates[WLAN_SUPPORTED_RATES];
+
+extern u8 libertas_supported_rates[G_SUPPORTED_RATES];
+
+extern u8 libertas_adhoc_rates_g[G_SUPPORTED_RATES];
+
+extern u8 libertas_adhoc_rates_b[4];
+
+/** ENUM definition*/
+/** SNRNF_TYPE */
+enum SNRNF_TYPE {
+	TYPE_BEACON = 0,
+	TYPE_RXPD,
+	MAX_TYPE_B
+};
+
+/** SNRNF_DATA*/
+enum SNRNF_DATA {
+	TYPE_NOAVG = 0,
+	TYPE_AVG,
+	MAX_TYPE_AVG
+};
+
+/** WLAN_802_11_AUTH_ALG*/
+enum WLAN_802_11_AUTH_ALG {
+	AUTH_ALG_OPEN_SYSTEM = 1,
+	AUTH_ALG_SHARED_KEY = 2,
+	AUTH_ALG_NETWORK_EAP = 8,
+};
+
+/** WLAN_802_1X_AUTH_ALG */
+enum WLAN_802_1X_AUTH_ALG {
+	WLAN_1X_AUTH_ALG_NONE = 1,
+	WLAN_1X_AUTH_ALG_LEAP = 2,
+	WLAN_1X_AUTH_ALG_TLS = 4,
+	WLAN_1X_AUTH_ALG_TTLS = 8,
+	WLAN_1X_AUTH_ALG_MD5 = 16,
+};
+
+/** WLAN_802_11_ENCRYPTION_MODE */
+enum WLAN_802_11_ENCRYPTION_MODE {
+	CIPHER_NONE,
+	CIPHER_WEP40,
+	CIPHER_TKIP,
+	CIPHER_CCMP,
+	CIPHER_WEP104,
+};
+
+/** WLAN_802_11_POWER_MODE */
+enum WLAN_802_11_POWER_MODE {
+	wlan802_11powermodecam,
+	wlan802_11powermodemax_psp,
+	wlan802_11Powermodefast_psp,
+	/*not a real mode, defined as an upper bound */
+	wlan802_11powemodemax
+};
+
+/** PS_STATE */
+enum PS_STATE {
+	PS_STATE_FULL_POWER,
+	PS_STATE_AWAKE,
+	PS_STATE_PRE_SLEEP,
+	PS_STATE_SLEEP
+};
+
+/** DNLD_STATE */
+enum DNLD_STATE {
+	DNLD_RES_RECEIVED,
+	DNLD_DATA_SENT,
+	DNLD_CMD_SENT
+};
+
+/** WLAN_MEDIA_STATE */
+enum WLAN_MEDIA_STATE {
+	libertas_connected,
+	libertas_disconnected
+};
+
+/** WLAN_802_11_PRIVACY_FILTER */
+enum WLAN_802_11_PRIVACY_FILTER {
+	wlan802_11privfilteracceptall,
+	wlan802_11privfilter8021xWEP
+};
+
+/** mv_ms_type */
+enum mv_ms_type {
+	MVMS_DAT = 0,
+	MVMS_CMD = 1,
+	MVMS_TXDONE = 2,
+	MVMS_EVENT
+};
+
+/** WLAN_802_11_NETWORK_INFRASTRUCTURE */
+enum WLAN_802_11_NETWORK_INFRASTRUCTURE {
+	wlan802_11ibss,
+	wlan802_11infrastructure,
+	wlan802_11autounknown,
+	/*defined as upper bound */
+	wlan802_11infrastructuremax
+};
+
+/** WLAN_802_11_AUTHENTICATION_MODE */
+enum WLAN_802_11_AUTHENTICATION_MODE {
+	wlan802_11authmodeopen = 0x00,
+	wlan802_11authmodeshared = 0x01,
+	wlan802_11authmodenetworkEAP = 0x80,
+};
+
+/** WLAN_802_11_WEP_STATUS */
+enum WLAN_802_11_WEP_STATUS {
+	wlan802_11WEPenabled,
+	wlan802_11WEPdisabled,
+};
+
+/** SNMP_MIB_INDEX_e */
+enum SNMP_MIB_INDEX_e {
+	desired_bsstype_i = 0,
+	op_rateset_i,
+	bcnperiod_i,
+	dtimperiod_i,
+	assocrsp_timeout_i,
+	rtsthresh_i,
+	short_retrylim_i,
+	long_retrylim_i,
+	fragthresh_i,
+	dot11d_i,
+	dot11h_i,
+	manufid_i,
+	prodID_i,
+	manuf_oui_i,
+	manuf_name_i,
+	manuf_prodname_i,
+	manuf_prodver_i,
+};
+
+/** KEY_TYPE_ID */
+enum KEY_TYPE_ID {
+	KEY_TYPE_ID_WEP = 0,
+	KEY_TYPE_ID_TKIP,
+	KEY_TYPE_ID_AES
+};
+
+/** KEY_INFO_WPA (applies to both TKIP and AES/CCMP) */
+enum KEY_INFO_WPA {
+	KEY_INFO_WPA_MCAST = 0x01,
+	KEY_INFO_WPA_UNICAST = 0x02,
+	KEY_INFO_WPA_ENABLED = 0x04
+};
+
+/** SNMP_MIB_VALUE_e */
+enum SNMP_MIB_VALUE_e {
+	SNMP_MIB_VALUE_INFRA = 1,
+	SNMP_MIB_VALUE_ADHOC
+};
+
+/* Default values for fwt commands. */
+#define FWT_DEFAULT_METRIC 0
+#define FWT_DEFAULT_DIR 1
+#define FWT_DEFAULT_SSN 0xffffffff
+#define FWT_DEFAULT_DSN 0
+#define FWT_DEFAULT_HOPCOUNT 0
+#define FWT_DEFAULT_TTL 0
+#define FWT_DEFAULT_EXPIRATION 0
+#define FWT_DEFAULT_SLEEPMODE 0
+#define FWT_DEFAULT_SNR 0
+
+#endif				/* _WLAN_DEFS_H_ */
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
new file mode 100644
index 0000000..b1f876f
--- /dev/null
+++ b/drivers/net/wireless/libertas/dev.h
@@ -0,0 +1,403 @@
+/**
+  * This file contains definitions and data structures specific
+  * to Marvell 802.11 NIC. It contains the Device Information
+  * structure wlan_adapter.
+  */
+#ifndef _WLAN_DEV_H_
+#define _WLAN_DEV_H_
+
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <linux/ethtool.h>
+#include <linux/debugfs.h>
+
+#include "defs.h"
+#include "scan.h"
+#include "thread.h"
+
+extern struct ethtool_ops libertas_ethtool_ops;
+
+#define	MAX_BSSID_PER_CHANNEL		16
+
+#define NR_TX_QUEUE			3
+
+/* For the extended Scan */
+#define MAX_EXTENDED_SCAN_BSSID_LIST    MAX_BSSID_PER_CHANNEL * \
+						MRVDRV_MAX_CHANNEL_SIZE + 1
+
+#define	MAX_REGION_CHANNEL_NUM	2
+
+/** Chan-freq-TxPower mapping table*/
+struct chan_freq_power {
+	/** channel Number		*/
+	u16 channel;
+	/** frequency of this channel	*/
+	u32 freq;
+	/** Max allowed Tx power level	*/
+	u16 maxtxpower;
+	/** TRUE:channel unsupported;  FLASE:supported*/
+	u8 unsupported;
+};
+
+/** region-band mapping table*/
+struct region_channel {
+	/** TRUE if this entry is valid		     */
+	u8 valid;
+	/** region code for US, Japan ...	     */
+	u8 region;
+	/** band B/G/A, used for BAND_CONFIG cmd	     */
+	u8 band;
+	/** Actual No. of elements in the array below */
+	u8 nrcfp;
+	/** chan-freq-txpower mapping table*/
+	struct chan_freq_power *CFP;
+};
+
+struct wlan_802_11_security {
+	u8 WPAenabled;
+	u8 WPA2enabled;
+	enum WLAN_802_11_WEP_STATUS WEPstatus;
+	enum WLAN_802_11_AUTHENTICATION_MODE authmode;
+	enum WLAN_802_1X_AUTH_ALG auth1xalg;
+	enum WLAN_802_11_ENCRYPTION_MODE Encryptionmode;
+};
+
+/** Current Basic Service Set State Structure */
+struct current_bss_params {
+	struct bss_descriptor bssdescriptor;
+	/** bssid */
+	u8 bssid[ETH_ALEN];
+	/** ssid */
+	struct WLAN_802_11_SSID ssid;
+
+	/** band */
+	u8 band;
+	/** channel */
+	u8 channel;
+	/** number of rates supported */
+	int numofrates;
+	/** supported rates*/
+	u8 datarates[WLAN_SUPPORTED_RATES];
+};
+
+/** sleep_params */
+struct sleep_params {
+	u16 sp_error;
+	u16 sp_offset;
+	u16 sp_stabletime;
+	u8 sp_calcontrol;
+	u8 sp_extsleepclk;
+	u16 sp_reserved;
+};
+
+/** Data structure for the Marvell WLAN device */
+typedef struct _wlan_dev {
+	/** device name */
+	char name[DEV_NAME_LEN];
+	/** card pointer */
+	void *card;
+	/** IO port */
+	u32 ioport;
+	/** Upload received */
+	u32 upld_rcv;
+	/** Upload type */
+	u32 upld_typ;
+	/** Upload length */
+	u32 upld_len;
+	/** netdev pointer */
+	struct net_device *netdev;
+	/* Upload buffer */
+	u8 upld_buf[WLAN_UPLD_SIZE];
+	/* Download sent:
+	   bit0 1/0=data_sent/data_tx_done,
+	   bit1 1/0=cmd_sent/cmd_tx_done,
+	   all other bits reserved 0 */
+	u8 dnld_sent;
+} wlan_dev_t, *pwlan_dev_t;
+
+/* Mesh statistics */
+struct wlan_mesh_stats {
+	u32	fwd_bcast_cnt;		/* Fwd: Broadcast counter */
+	u32	fwd_unicast_cnt;	/* Fwd: Unicast counter */
+	u32	fwd_drop_ttl;		/* Fwd: TTL zero */
+	u32	fwd_drop_rbt;		/* Fwd: Recently Broadcasted */
+	u32	fwd_drop_noroute; 	/* Fwd: No route to Destination */
+	u32	fwd_drop_nobuf;		/* Fwd: Run out of internal buffers */
+	u32	drop_blind;		/* Rx:  Dropped by blinding table */
+};
+
+/** Private structure for the MV device */
+struct _wlan_private {
+	int open;
+	int mesh_open;
+	int infra_open;
+
+	wlan_adapter *adapter;
+	wlan_dev_t wlan_dev;
+
+	struct net_device_stats stats;
+	struct net_device *mesh_dev ; /* Virtual device */
+
+	struct iw_statistics wstats;
+	struct wlan_mesh_stats mstats;
+	struct dentry *debugfs_dir;
+	struct dentry *debugfs_debug;
+	struct dentry *debugfs_files[6];
+
+	struct dentry *events_dir;
+	struct dentry *debugfs_events_files[6];
+
+	struct dentry *regs_dir;
+	struct dentry *debugfs_regs_files[6];
+
+	u32 mac_offset;
+	u32 bbp_offset;
+	u32 rf_offset;
+
+	const struct firmware *firmware;
+	struct device *hotplug_device;
+
+	/** thread to service interrupts */
+	struct wlan_thread mainthread;
+
+	struct delayed_work assoc_work;
+	struct workqueue_struct *assoc_thread;
+};
+
+/** Association request
+ *
+ * Encapsulates all the options that describe a specific assocation request
+ * or configuration of the wireless card's radio, mode, and security settings.
+ */
+struct assoc_request {
+#define ASSOC_FLAG_SSID			1
+#define ASSOC_FLAG_CHANNEL		2
+#define ASSOC_FLAG_MODE			3
+#define ASSOC_FLAG_BSSID		4
+#define ASSOC_FLAG_WEP_KEYS		5
+#define ASSOC_FLAG_WEP_TX_KEYIDX	6
+#define ASSOC_FLAG_WPA_MCAST_KEY	7
+#define ASSOC_FLAG_WPA_UCAST_KEY	8
+#define ASSOC_FLAG_SECINFO		9
+#define ASSOC_FLAG_WPA_IE		10
+	unsigned long flags;
+
+	struct WLAN_802_11_SSID ssid;
+	u8 channel;
+	enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode;
+	u8 bssid[ETH_ALEN];
+
+	/** WEP keys */
+	struct WLAN_802_11_KEY wep_keys[4];
+	u16 wep_tx_keyidx;
+
+	/** WPA keys */
+	struct WLAN_802_11_KEY wpa_mcast_key;
+	struct WLAN_802_11_KEY wpa_unicast_key;
+
+	struct wlan_802_11_security secinfo;
+
+	/** WPA Information Elements*/
+#define MAX_WPA_IE_LEN 64
+	u8 wpa_ie[MAX_WPA_IE_LEN];
+	u8 wpa_ie_len;
+};
+
+/** Wlan adapter data structure*/
+struct _wlan_adapter {
+	/** STATUS variables */
+	u32 fwreleasenumber;
+	u32 fwcapinfo;
+	/* protected with big lock */
+
+	struct mutex lock;
+
+	u8 tmptxbuf[WLAN_UPLD_SIZE];
+	/* protected by hard_start_xmit serialization */
+
+	/** command-related variables */
+	u16 seqnum;
+	/* protected by big lock */
+
+	struct cmd_ctrl_node *cmd_array;
+	/** Current command */
+	struct cmd_ctrl_node *cur_cmd;
+	int cur_cmd_retcode;
+	/** command Queues */
+	/** Free command buffers */
+	struct list_head cmdfreeq;
+	/** Pending command buffers */
+	struct list_head cmdpendingq;
+
+	wait_queue_head_t cmd_pending;
+	u8 nr_cmd_pending;
+	/* command related variables protected by adapter->driver_lock */
+
+	/** Async and Sync Event variables */
+	u32 intcounter;
+	u32 eventcause;
+	u8 nodename[16];	/* nickname */
+
+	/** spin locks */
+	spinlock_t driver_lock;
+
+	/** Timers */
+	struct timer_list command_timer;
+
+	/* TX queue used in PS mode */
+	spinlock_t txqueue_lock;
+	struct sk_buff *tx_queue_ps[NR_TX_QUEUE];
+	unsigned int tx_queue_idx;
+
+	u8 hisregcpy;
+
+	/** current ssid/bssid related parameters*/
+	struct current_bss_params curbssparams;
+
+	enum WLAN_802_11_NETWORK_INFRASTRUCTURE inframode;
+
+	struct bss_descriptor *pattemptedbssdesc;
+
+	struct WLAN_802_11_SSID previousssid;
+	u8 previousbssid[ETH_ALEN];
+
+	struct bss_descriptor *scantable;
+	u32 numinscantable;
+
+	u8 scantype;
+	u32 scanmode;
+
+	u16 beaconperiod;
+	u8 adhoccreate;
+
+	/** capability Info used in Association, start, join */
+	struct ieeetypes_capinfo capinfo;
+
+	/** MAC address information */
+	u8 current_addr[ETH_ALEN];
+	u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN];
+	u32 nr_of_multicastmacaddr;
+
+	/** 802.11 statistics */
+//	struct cmd_DS_802_11_GET_STAT wlan802_11Stat;
+
+	u16 enablehwauto;
+	u16 ratebitmap;
+	/** control G rates */
+	u8 adhoc_grate_enabled;
+
+	u32 txantenna;
+	u32 rxantenna;
+
+	u8 adhocchannel;
+	u32 fragthsd;
+	u32 rtsthsd;
+
+	u32 datarate;
+	u8 is_datarate_auto;
+
+	u16 listeninterval;
+	u16 prescan;
+	u8 txretrycount;
+
+	/** Tx-related variables (for single packet tx) */
+	struct sk_buff *currenttxskb;
+	u16 TxLockFlag;
+
+	/** NIC Operation characteristics */
+	u16 currentpacketfilter;
+	u32 connect_status;
+	u16 regioncode;
+	u16 regiontableindex;
+	u16 txpowerlevel;
+
+	/** POWER MANAGEMENT AND PnP SUPPORT */
+	u8 surpriseremoved;
+	u16 atimwindow;
+
+	u16 psmode;		/* Wlan802_11PowermodeCAM=disable
+				   Wlan802_11PowermodeMAX_PSP=enable */
+	u16 multipledtim;
+	u32 psstate;
+	u8 needtowakeup;
+
+	struct PS_CMD_ConfirmSleep libertas_ps_confirm_sleep;
+	u16 locallisteninterval;
+	u16 nullpktinterval;
+
+	struct assoc_request * assoc_req;
+
+	/** Encryption parameter */
+	struct wlan_802_11_security secinfo;
+
+	/** WEP keys */
+	struct WLAN_802_11_KEY wep_keys[4];
+	u16 wep_tx_keyidx;
+
+	/** WPA keys */
+	struct WLAN_802_11_KEY wpa_mcast_key;
+	struct WLAN_802_11_KEY wpa_unicast_key;
+
+	/** WPA Information Elements*/
+#define MAX_WPA_IE_LEN 64
+	u8 wpa_ie[MAX_WPA_IE_LEN];
+	u8 wpa_ie_len;
+
+	u16 rxantennamode;
+	u16 txantennamode;
+
+	/** Requested Signal Strength*/
+	u16 bcn_avg_factor;
+	u16 data_avg_factor;
+	u16 SNR[MAX_TYPE_B][MAX_TYPE_AVG];
+	u16 NF[MAX_TYPE_B][MAX_TYPE_AVG];
+	u8 RSSI[MAX_TYPE_B][MAX_TYPE_AVG];
+	u8 rawSNR[DEFAULT_DATA_AVG_FACTOR];
+	u8 rawNF[DEFAULT_DATA_AVG_FACTOR];
+	u16 nextSNRNF;
+	u16 numSNRNF;
+	u16 rxpd_rate;
+
+	u8 radioon;
+	u32 preamble;
+
+	/** Multi bands Parameter*/
+	u8 libertas_supported_rates[G_SUPPORTED_RATES];
+
+	/** Blue Tooth Co-existence Arbitration */
+
+	/** sleep_params */
+	struct sleep_params sp;
+
+	/** RF calibration data */
+
+#define	MAX_REGION_CHANNEL_NUM	2
+	/** region channel data */
+	struct region_channel region_channel[MAX_REGION_CHANNEL_NUM];
+
+	struct region_channel universal_channel[MAX_REGION_CHANNEL_NUM];
+
+	/** 11D and Domain Regulatory Data */
+	struct wlan_802_11d_domain_reg domainreg;
+	struct parsed_region_chan_11d parsed_region_chan;
+
+	/** FSM variable for 11d support */
+	u32 enable11d;
+
+	/**	MISCELLANEOUS */
+	u8 *prdeeprom;
+	struct wlan_offset_value offsetvalue;
+
+	struct cmd_ds_802_11_get_log logmsg;
+	u16 scanprobes;
+
+	u32 pkttxctrl;
+
+	u16 txrate;
+	u32 linkmode;
+	u32 radiomode;
+	u32 debugmode;
+	u8 fw_ready;
+};
+
+#endif				/* _WLAN_DEV_H_ */
diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c
new file mode 100644
index 0000000..0064de5
--- /dev/null
+++ b/drivers/net/wireless/libertas/ethtool.c
@@ -0,0 +1,184 @@
+
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+
+#include "host.h"
+#include "sbi.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "join.h"
+#include "wext.h"
+static const char * mesh_stat_strings[]= {
+			"drop_duplicate_bcast",
+			"drop_ttl_zero",
+			"drop_no_fwd_route",
+			"drop_no_buffers",
+			"fwded_unicast_cnt",
+			"fwded_bcast_cnt",
+			"drop_blind_table"
+};
+
+static void libertas_ethtool_get_drvinfo(struct net_device *dev,
+					 struct ethtool_drvinfo *info)
+{
+	wlan_private *priv = (wlan_private *) dev->priv;
+	char fwver[32];
+
+	libertas_get_fwversion(priv->adapter, fwver, sizeof(fwver) - 1);
+
+	strcpy(info->driver, "libertas");
+	strcpy(info->version, libertas_driver_version);
+	strcpy(info->fw_version, fwver);
+}
+
+/* All 8388 parts have 16KiB EEPROM size at the time of writing.
+ * In case that changes this needs fixing.
+ */
+#define LIBERTAS_EEPROM_LEN 16384
+
+static int libertas_ethtool_get_eeprom_len(struct net_device *dev)
+{
+	return LIBERTAS_EEPROM_LEN;
+}
+
+static int libertas_ethtool_get_eeprom(struct net_device *dev,
+                                  struct ethtool_eeprom *eeprom, u8 * bytes)
+{
+	wlan_private *priv = (wlan_private *) dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	struct wlan_ioctl_regrdwr regctrl;
+	char *ptr;
+	int ret;
+
+	regctrl.action = 0;
+	regctrl.offset = eeprom->offset;
+	regctrl.NOB = eeprom->len;
+
+	if (eeprom->offset + eeprom->len > LIBERTAS_EEPROM_LEN)
+		return -EINVAL;
+
+//      mutex_lock(&priv->mutex);
+
+	adapter->prdeeprom =
+		    (char *)kmalloc(eeprom->len+sizeof(regctrl), GFP_KERNEL);
+	if (!adapter->prdeeprom)
+		return -ENOMEM;
+	memcpy(adapter->prdeeprom, &regctrl, sizeof(regctrl));
+
+	/* +14 is for action, offset, and NOB in
+	 * response */
+	lbs_pr_debug(1, "action:%d offset: %x NOB: %02x\n",
+	       regctrl.action, regctrl.offset, regctrl.NOB);
+
+	ret = libertas_prepare_and_send_command(priv,
+				    cmd_802_11_eeprom_access,
+				    regctrl.action,
+				    cmd_option_waitforrsp, 0,
+				    &regctrl);
+
+	if (ret) {
+		if (adapter->prdeeprom)
+			kfree(adapter->prdeeprom);
+		LEAVE();
+			return ret;
+	}
+
+	mdelay(10);
+
+	ptr = (char *)adapter->prdeeprom;
+
+	/* skip the command header, but include the "value" u32 variable */
+	ptr = ptr + sizeof(struct wlan_ioctl_regrdwr) - 4;
+
+	/*
+	 * Return the result back to the user
+	 */
+	memcpy(bytes, ptr, eeprom->len);
+
+	if (adapter->prdeeprom)
+		kfree(adapter->prdeeprom);
+//	mutex_unlock(&priv->mutex);
+
+        return 0;
+}
+
+static void libertas_ethtool_get_stats(struct net_device * dev,
+				struct ethtool_stats * stats, u64 * data)
+{
+	wlan_private *priv = dev->priv;
+
+	ENTER();
+
+	stats->cmd = ETHTOOL_GSTATS;
+	BUG_ON(stats->n_stats != MESH_STATS_NUM);
+
+        data[0] = priv->mstats.fwd_drop_rbt;
+        data[1] = priv->mstats.fwd_drop_ttl;
+        data[2] = priv->mstats.fwd_drop_noroute;
+        data[3] = priv->mstats.fwd_drop_nobuf;
+        data[4] = priv->mstats.fwd_unicast_cnt;
+        data[5] = priv->mstats.fwd_bcast_cnt;
+        data[6] = priv->mstats.drop_blind;
+
+	LEAVE();
+}
+
+static int libertas_ethtool_get_stats_count(struct net_device * dev)
+{
+	int ret;
+	wlan_private *priv = dev->priv;
+	struct cmd_ds_mesh_access mesh_access;
+
+	ENTER();
+	/* Get Mesh Statistics */
+	ret = libertas_prepare_and_send_command(priv,
+			cmd_mesh_access, cmd_act_mesh_get_stats,
+			cmd_option_waitforrsp, 0, &mesh_access);
+
+	if (ret) {
+		LEAVE();
+		return 0;
+	}
+
+        priv->mstats.fwd_drop_rbt = mesh_access.data[0];
+        priv->mstats.fwd_drop_ttl = mesh_access.data[1];
+        priv->mstats.fwd_drop_noroute = mesh_access.data[2];
+        priv->mstats.fwd_drop_nobuf = mesh_access.data[3];
+        priv->mstats.fwd_unicast_cnt = mesh_access.data[4];
+        priv->mstats.fwd_bcast_cnt = mesh_access.data[5];
+        priv->mstats.drop_blind = mesh_access.data[6];
+
+	LEAVE();
+	return MESH_STATS_NUM;
+}
+
+static void libertas_ethtool_get_strings (struct net_device * dev,
+					  u32 stringset,
+					  u8 * s)
+{
+	int i;
+
+	ENTER();
+	switch (stringset) {
+        case ETH_SS_STATS:
+		for (i=0; i < MESH_STATS_NUM; i++) {
+			memcpy(s + i * ETH_GSTRING_LEN,
+					mesh_stat_strings[i],
+					ETH_GSTRING_LEN);
+		}
+		break;
+        }
+	LEAVE();
+}
+
+struct ethtool_ops libertas_ethtool_ops = {
+	.get_drvinfo = libertas_ethtool_get_drvinfo,
+	.get_eeprom =  libertas_ethtool_get_eeprom,
+	.get_eeprom_len = libertas_ethtool_get_eeprom_len,
+	.get_stats_count = libertas_ethtool_get_stats_count,
+	.get_ethtool_stats = libertas_ethtool_get_stats,
+	.get_strings = libertas_ethtool_get_strings,
+};
+
diff --git a/drivers/net/wireless/libertas/fw.c b/drivers/net/wireless/libertas/fw.c
new file mode 100644
index 0000000..b194a45
--- /dev/null
+++ b/drivers/net/wireless/libertas/fw.c
@@ -0,0 +1,361 @@
+/**
+  * This file contains the initialization for FW and HW
+  */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#include <linux/vmalloc.h>
+#include <linux/firmware.h>
+#include <linux/version.h>
+
+#include "host.h"
+#include "sbi.h"
+#include "defs.h"
+#include "decl.h"
+#include "dev.h"
+#include "fw.h"
+#include "wext.h"
+#include "if_usb.h"
+
+char *libertas_fw_name = NULL;
+module_param_named(fw_name, libertas_fw_name, charp, 0644);
+
+unsigned int libertas_debug = 0;
+module_param(libertas_debug, int, 0);
+
+/**
+ *  @brief This function checks the validity of Boot2/FW image.
+ *
+ *  @param data              pointer to image
+ *         len               image length
+ *  @return     0 or -1
+ */
+static int check_fwfile_format(u8 *data, u32 totlen)
+{
+	u8  bincmd, exit;
+	u32 blksize, offset, len;
+	int ret;
+
+	ret = 1;
+	exit = len = 0;
+
+	do {
+		bincmd = *data;
+		blksize = *(u32*)(data + offsetof(struct fwheader, datalength));
+		switch (bincmd) {
+		case FW_HAS_DATA_TO_RECV:
+			offset = sizeof(struct fwheader) + blksize;
+			data += offset;
+			len += offset;
+			if (len >= totlen)
+				exit = 1;
+			break;
+		case FW_HAS_LAST_BLOCK:
+			exit = 1;
+			ret = 0;
+			break;
+		default:
+			exit = 1;
+			break;
+		}
+	} while (!exit);
+
+	if (ret)
+		lbs_pr_err("bin file format check FAIL...\n");
+	else
+		lbs_pr_debug(1, "bin file format check PASS...\n");
+
+	return ret;
+}
+
+/**
+ *  @brief This function downloads firmware image, gets
+ *  HW spec from firmware and set basic parameters to
+ *  firmware.
+ *
+ *  @param priv    A pointer to wlan_private structure
+ *  @return 	   0 or -1
+ */
+static int wlan_setup_station_hw(wlan_private * priv)
+{
+	int ret = -1;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	if ((ret = request_firmware(&priv->firmware, libertas_fw_name,
+				    priv->hotplug_device)) < 0) {
+		lbs_pr_err("request_firmware() failed, error code = %#x\n",
+		       ret);
+		lbs_pr_err("%s not found in /lib/firmware\n", libertas_fw_name);
+		goto done;
+	}
+
+	if(check_fwfile_format(priv->firmware->data, priv->firmware->size)) {
+		release_firmware(priv->firmware);
+		goto done;
+	}
+
+	ret = libertas_sbi_prog_firmware(priv);
+
+	release_firmware(priv->firmware);
+
+	if (ret) {
+		lbs_pr_debug(1, "Bootloader in invalid state!\n");
+		ret = -1;
+		goto done;
+	}
+
+	/*
+	 * Read MAC address from HW
+	 */
+	memset(adapter->current_addr, 0xff, ETH_ALEN);
+
+	ret = libertas_prepare_and_send_command(priv, cmd_get_hw_spec,
+				    0, cmd_option_waitforrsp, 0, NULL);
+
+	if (ret) {
+		ret = -1;
+		goto done;
+	}
+
+	libertas_set_mac_packet_filter(priv);
+
+	/* Get the supported Data rates */
+	ret = libertas_prepare_and_send_command(priv, cmd_802_11_data_rate,
+				    cmd_act_get_tx_rate,
+				    cmd_option_waitforrsp, 0, NULL);
+
+	if (ret) {
+		ret = -1;
+		goto done;
+	}
+
+	ret = 0;
+done:
+	LEAVE();
+
+	return (ret);
+}
+
+static int wlan_allocate_adapter(wlan_private * priv)
+{
+	u32 ulbufsize;
+	wlan_adapter *adapter = priv->adapter;
+
+	struct bss_descriptor *ptempscantable;
+
+	/* Allocate buffer to store the BSSID list */
+	ulbufsize = sizeof(struct bss_descriptor) * MRVDRV_MAX_BSSID_LIST;
+	if (!(ptempscantable = kmalloc(ulbufsize, GFP_KERNEL))) {
+		libertas_free_adapter(priv);
+		return -1;
+	}
+
+	adapter->scantable = ptempscantable;
+	memset(adapter->scantable, 0, ulbufsize);
+
+	/* Allocate the command buffers */
+	libertas_allocate_cmd_buffer(priv);
+
+	memset(&adapter->libertas_ps_confirm_sleep, 0, sizeof(struct PS_CMD_ConfirmSleep));
+	adapter->libertas_ps_confirm_sleep.seqnum = cpu_to_le16(++adapter->seqnum);
+	adapter->libertas_ps_confirm_sleep.command =
+	    cpu_to_le16(cmd_802_11_ps_mode);
+	adapter->libertas_ps_confirm_sleep.size =
+	    cpu_to_le16(sizeof(struct PS_CMD_ConfirmSleep));
+	adapter->libertas_ps_confirm_sleep.result = 0;
+	adapter->libertas_ps_confirm_sleep.action =
+	    cpu_to_le16(cmd_subcmd_sleep_confirmed);
+
+	return 0;
+}
+
+static void wlan_init_adapter(wlan_private * priv)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int i;
+
+	adapter->scanprobes = 0;
+
+	adapter->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR;
+	adapter->data_avg_factor = DEFAULT_DATA_AVG_FACTOR;
+
+	/* ATIM params */
+	adapter->atimwindow = 0;
+
+	adapter->connect_status = libertas_disconnected;
+	memset(adapter->current_addr, 0xff, ETH_ALEN);
+
+	/* scan type */
+	adapter->scantype = cmd_scan_type_active;
+
+	/* scan mode */
+	adapter->scanmode = cmd_bss_type_any;
+
+	/* 802.11 specific */
+	adapter->secinfo.WEPstatus = wlan802_11WEPdisabled;
+	for (i = 0; i < sizeof(adapter->wep_keys) / sizeof(adapter->wep_keys[0]);
+	     i++)
+		memset(&adapter->wep_keys[i], 0, sizeof(struct WLAN_802_11_KEY));
+	adapter->wep_tx_keyidx = 0;
+	adapter->secinfo.WEPstatus = wlan802_11WEPdisabled;
+	adapter->secinfo.authmode = wlan802_11authmodeopen;
+	adapter->secinfo.auth1xalg = WLAN_1X_AUTH_ALG_NONE;
+	adapter->secinfo.Encryptionmode = CIPHER_NONE;
+	adapter->inframode = wlan802_11infrastructure;
+
+	adapter->assoc_req = NULL;
+
+	adapter->numinscantable = 0;
+	adapter->pattemptedbssdesc = NULL;
+	mutex_init(&adapter->lock);
+
+	adapter->prescan = 1;
+
+	memset(&adapter->curbssparams, 0, sizeof(adapter->curbssparams));
+
+	/* PnP and power profile */
+	adapter->surpriseremoved = 0;
+
+	adapter->currentpacketfilter =
+	    cmd_act_mac_rx_on | cmd_act_mac_tx_on;
+
+	adapter->radioon = RADIO_ON;
+	adapter->txantenna = RF_ANTENNA_2;
+	adapter->rxantenna = RF_ANTENNA_AUTO;
+
+	adapter->is_datarate_auto = 1;
+	adapter->beaconperiod = MRVDRV_BEACON_INTERVAL;
+
+	// set default value of capinfo.
+#define SHORT_PREAMBLE_ALLOWED		1
+	memset(&adapter->capinfo, 0, sizeof(adapter->capinfo));
+	adapter->capinfo.shortpreamble = SHORT_PREAMBLE_ALLOWED;
+
+	adapter->adhocchannel = DEFAULT_AD_HOC_CHANNEL;
+
+	adapter->psmode = wlan802_11powermodecam;
+	adapter->multipledtim = MRVDRV_DEFAULT_MULTIPLE_DTIM;
+
+	adapter->listeninterval = MRVDRV_DEFAULT_LISTEN_INTERVAL;
+
+	adapter->psstate = PS_STATE_FULL_POWER;
+	adapter->needtowakeup = 0;
+	adapter->locallisteninterval = 0;	/* default value in firmware will be used */
+
+	adapter->datarate = 0;	// Initially indicate the rate as auto
+
+	adapter->adhoc_grate_enabled = 0;
+
+	adapter->intcounter = 0;
+
+	adapter->currenttxskb = NULL;
+	adapter->pkttxctrl = 0;
+
+	memset(&adapter->tx_queue_ps, 0, NR_TX_QUEUE*sizeof(struct sk_buff*));
+	adapter->tx_queue_idx = 0;
+	spin_lock_init(&adapter->txqueue_lock);
+
+	return;
+}
+
+static void command_timer_fn(unsigned long data);
+
+int libertas_init_fw(wlan_private * priv)
+{
+	int ret = -1;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	/* Allocate adapter structure */
+	if ((ret = wlan_allocate_adapter(priv)) != 0)
+		goto done;
+
+	/* init adapter structure */
+	wlan_init_adapter(priv);
+
+	/* init timer etc. */
+	setup_timer(&adapter->command_timer, command_timer_fn,
+			(unsigned long)priv);
+
+	/* download fimrware etc. */
+	if ((ret = wlan_setup_station_hw(priv)) != 0) {
+		del_timer_sync(&adapter->command_timer);
+		goto done;
+	}
+
+	/* init 802.11d */
+	libertas_init_11d(priv);
+
+	ret = 0;
+done:
+	LEAVE();
+	return ret;
+}
+
+void libertas_free_adapter(wlan_private * priv)
+{
+	wlan_adapter *adapter = priv->adapter;
+
+	if (!adapter) {
+		lbs_pr_debug(1, "Why double free adapter?:)\n");
+		return;
+	}
+
+	lbs_pr_debug(1, "Free command buffer\n");
+	libertas_free_cmd_buffer(priv);
+
+	lbs_pr_debug(1, "Free commandTimer\n");
+	del_timer(&adapter->command_timer);
+
+	lbs_pr_debug(1, "Free scantable\n");
+	if (adapter->scantable) {
+		kfree(adapter->scantable);
+		adapter->scantable = NULL;
+	}
+
+	lbs_pr_debug(1, "Free adapter\n");
+
+	/* Free the adapter object itself */
+	kfree(adapter);
+	priv->adapter = NULL;
+}
+
+/**
+ *  This function handles the timeout of command sending.
+ *  It will re-send the same command again.
+ */
+static void command_timer_fn(unsigned long data)
+{
+	wlan_private *priv = (wlan_private *)data;
+	wlan_adapter *adapter = priv->adapter;
+	struct cmd_ctrl_node *ptempnode;
+	struct cmd_ds_command *cmd;
+	unsigned long flags;
+
+	ptempnode = adapter->cur_cmd;
+	cmd = (struct cmd_ds_command *)ptempnode->bufvirtualaddr;
+
+	lbs_pr_info("command_timer_fn fired (%x)\n", cmd->command);
+
+	if (!adapter->fw_ready)
+		return;
+
+	if (ptempnode == NULL) {
+		lbs_pr_debug(1, "PTempnode Empty\n");
+		return;
+	}
+
+	spin_lock_irqsave(&adapter->driver_lock, flags);
+	adapter->cur_cmd = NULL;
+	spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+	lbs_pr_debug(1, "Re-sending same command as it timeout...!\n");
+	libertas_queue_cmd(adapter, ptempnode, 0);
+
+	wake_up_interruptible(&priv->mainthread.waitq);
+
+	return;
+}
diff --git a/drivers/net/wireless/libertas/fw.h b/drivers/net/wireless/libertas/fw.h
new file mode 100644
index 0000000..1f9ae26
--- /dev/null
+++ b/drivers/net/wireless/libertas/fw.h
@@ -0,0 +1,13 @@
+/**
+  * This header file contains FW interface related definitions.
+  */
+#ifndef _WLAN_FW_H_
+#define _WLAN_FW_H_
+
+#ifndef DEV_NAME_LEN
+#define DEV_NAME_LEN            32
+#endif
+
+int libertas_init_fw(wlan_private * priv);
+
+#endif				/* _WLAN_FW_H_ */
diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h
new file mode 100644
index 0000000..c0faaec
--- /dev/null
+++ b/drivers/net/wireless/libertas/host.h
@@ -0,0 +1,338 @@
+/**
+  * This file contains definitions of WLAN commands.
+  */
+
+#ifndef _HOST_H_
+#define _HOST_H_
+
+/** PUBLIC DEFINITIONS */
+#define DEFAULT_AD_HOC_CHANNEL       6
+#define DEFAULT_AD_HOC_CHANNEL_A    36
+
+/** IEEE 802.11 oids */
+#define OID_802_11_SSID                       0x00008002
+#define OID_802_11_INFRASTRUCTURE_MODE        0x00008008
+#define OID_802_11_FRAGMENTATION_THRESHOLD    0x00008009
+#define OID_802_11_RTS_THRESHOLD              0x0000800A
+#define OID_802_11_TX_ANTENNA_SELECTED        0x0000800D
+#define OID_802_11_SUPPORTED_RATES            0x0000800E
+#define OID_802_11_STATISTICS                 0x00008012
+#define OID_802_11_TX_RETRYCOUNT              0x0000801D
+#define OID_802_11D_ENABLE                    0x00008020
+
+#define cmd_option_waitforrsp             0x0002
+
+/** Host command ID */
+#define cmd_code_dnld                 0x0002
+#define cmd_get_hw_spec               0x0003
+#define cmd_eeprom_update             0x0004
+#define cmd_802_11_reset              0x0005
+#define cmd_802_11_scan               0x0006
+#define cmd_802_11_get_log            0x000b
+#define cmd_mac_multicast_adr         0x0010
+#define cmd_802_11_authenticate       0x0011
+#define cmd_802_11_eeprom_access      0x0059
+#define cmd_802_11_associate          0x0050
+#define cmd_802_11_set_wep            0x0013
+#define cmd_802_11_get_stat           0x0014
+#define cmd_802_3_get_stat            0x0015
+#define cmd_802_11_snmp_mib           0x0016
+#define cmd_mac_reg_map               0x0017
+#define cmd_bbp_reg_map               0x0018
+#define cmd_mac_reg_access            0x0019
+#define cmd_bbp_reg_access            0x001a
+#define cmd_rf_reg_access             0x001b
+#define cmd_802_11_radio_control      0x001c
+#define cmd_802_11_rf_channel         0x001d
+#define cmd_802_11_rf_tx_power        0x001e
+#define cmd_802_11_rssi               0x001f
+#define cmd_802_11_rf_antenna         0x0020
+
+#define cmd_802_11_ps_mode	      0x0021
+
+#define cmd_802_11_data_rate          0x0022
+#define cmd_rf_reg_map                0x0023
+#define cmd_802_11_deauthenticate     0x0024
+#define cmd_802_11_reassociate        0x0025
+#define cmd_802_11_disassociate       0x0026
+#define cmd_mac_control               0x0028
+#define cmd_802_11_ad_hoc_start       0x002b
+#define cmd_802_11_ad_hoc_join        0x002c
+
+#define cmd_802_11_query_tkip_reply_cntrs  0x002e
+#define cmd_802_11_enable_rsn              0x002f
+#define cmd_802_11_pairwise_tsc       0x0036
+#define cmd_802_11_group_tsc          0x0037
+#define cmd_802_11_key_material       0x005e
+
+#define cmd_802_11_set_afc            0x003c
+#define cmd_802_11_get_afc            0x003d
+
+#define cmd_802_11_ad_hoc_stop        0x0040
+
+#define cmd_802_11_beacon_stop        0x0049
+
+#define cmd_802_11_mac_address        0x004D
+#define cmd_802_11_eeprom_access      0x0059
+
+#define cmd_802_11_band_config        0x0058
+
+#define cmd_802_11d_domain_info       0x005b
+
+#define cmd_802_11_sleep_params          0x0066
+
+#define cmd_802_11_inactivity_timeout    0x0067
+
+#define cmd_802_11_tpc_cfg               0x0072
+#define cmd_802_11_pwr_cfg               0x0073
+
+#define cmd_802_11_led_gpio_ctrl         0x004e
+
+#define cmd_802_11_subscribe_event       0x0075
+
+#define cmd_802_11_rate_adapt_rateset    0x0076
+
+#define cmd_802_11_tx_rate_query	0x007f
+
+#define cmd_get_tsf                      0x0080
+
+#define cmd_bt_access                 0x0087
+#define cmd_ret_bt_access                 0x8087
+
+#define cmd_fwt_access                0x0088
+#define cmd_ret_fwt_access                0x8088
+
+#define cmd_mesh_access               0x0090
+#define cmd_ret_mesh_access               0x8090
+
+/* For the IEEE Power Save */
+#define cmd_subcmd_enter_ps               0x0030
+#define cmd_subcmd_exit_ps                0x0031
+#define cmd_subcmd_sleep_confirmed        0x0034
+#define cmd_subcmd_full_powerdown         0x0035
+#define cmd_subcmd_full_powerup           0x0036
+
+/* command RET code, MSB is set to 1 */
+#define cmd_ret_hw_spec_info              0x8003
+#define cmd_ret_eeprom_update             0x8004
+#define cmd_ret_802_11_reset              0x8005
+#define cmd_ret_802_11_scan               0x8006
+#define cmd_ret_802_11_get_log            0x800b
+#define cmd_ret_mac_control               0x8028
+#define cmd_ret_mac_multicast_adr         0x8010
+#define cmd_ret_802_11_authenticate       0x8011
+#define cmd_ret_802_11_deauthenticate     0x8024
+#define cmd_ret_802_11_associate          0x8012
+#define cmd_ret_802_11_reassociate        0x8025
+#define cmd_ret_802_11_disassociate       0x8026
+#define cmd_ret_802_11_set_wep            0x8013
+#define cmd_ret_802_11_stat               0x8014
+#define cmd_ret_802_3_stat                0x8015
+#define cmd_ret_802_11_snmp_mib           0x8016
+#define cmd_ret_mac_reg_map               0x8017
+#define cmd_ret_bbp_reg_map               0x8018
+#define cmd_ret_rf_reg_map                0x8023
+#define cmd_ret_mac_reg_access            0x8019
+#define cmd_ret_bbp_reg_access            0x801a
+#define cmd_ret_rf_reg_access             0x801b
+#define cmd_ret_802_11_radio_control      0x801c
+#define cmd_ret_802_11_rf_channel         0x801d
+#define cmd_ret_802_11_rssi               0x801f
+#define cmd_ret_802_11_rf_tx_power        0x801e
+#define cmd_ret_802_11_rf_antenna         0x8020
+#define cmd_ret_802_11_ps_mode            0x8021
+#define cmd_ret_802_11_data_rate          0x8022
+
+#define cmd_ret_802_11_ad_hoc_start       0x802B
+#define cmd_ret_802_11_ad_hoc_join        0x802C
+
+#define cmd_ret_802_11_query_tkip_reply_cntrs  0x802e
+#define cmd_ret_802_11_enable_rsn              0x802f
+#define cmd_ret_802_11_pairwise_tsc       0x8036
+#define cmd_ret_802_11_group_tsc          0x8037
+#define cmd_ret_802_11_key_material       0x805e
+
+#define cmd_enable_rsn                    0x0001
+#define cmd_disable_rsn                   0x0000
+
+#define cmd_act_set                       0x0001
+#define cmd_act_get                       0x0000
+
+#define cmd_act_get_AES                   (cmd_act_get + 2)
+#define cmd_act_set_AES                   (cmd_act_set + 2)
+#define cmd_act_remove_aes                (cmd_act_set + 3)
+
+#define cmd_ret_802_11_set_afc            0x803c
+#define cmd_ret_802_11_get_afc            0x803d
+
+#define cmd_ret_802_11_ad_hoc_stop        0x8040
+
+#define cmd_ret_802_11_beacon_stop        0x8049
+
+#define cmd_ret_802_11_mac_address        0x804D
+#define cmd_ret_802_11_eeprom_access      0x8059
+
+#define cmd_ret_802_11_band_config        0x8058
+
+#define cmd_ret_802_11_sleep_params          0x8066
+
+#define cmd_ret_802_11_inactivity_timeout    0x8067
+
+#define cmd_ret_802_11d_domain_info      (0x8000 |                  \
+                                              cmd_802_11d_domain_info)
+
+#define cmd_ret_802_11_tpc_cfg        (cmd_802_11_tpc_cfg | 0x8000)
+#define cmd_ret_802_11_pwr_cfg        (cmd_802_11_pwr_cfg | 0x8000)
+
+#define cmd_ret_802_11_led_gpio_ctrl     0x804e
+
+#define cmd_ret_802_11_subscribe_event	(cmd_802_11_subscribe_event | 0x8000)
+
+#define cmd_ret_802_11_rate_adapt_rateset	(cmd_802_11_rate_adapt_rateset | 0x8000)
+
+#define cmd_rte_802_11_tx_rate_query 	(cmd_802_11_tx_rate_query | 0x8000)
+
+#define cmd_ret_get_tsf             0x8080
+
+/* Define action or option for cmd_802_11_set_wep */
+#define cmd_act_add                         0x0002
+#define cmd_act_remove                      0x0004
+#define cmd_act_use_default                 0x0008
+
+#define cmd_type_wep_40_bit                 0x0001
+#define cmd_type_wep_104_bit                0x0002
+
+#define cmd_NUM_OF_WEP_KEYS                 4
+
+#define cmd_WEP_KEY_INDEX_MASK              0x3fff
+
+/* Define action or option for cmd_802_11_reset */
+#define cmd_act_halt                        0x0003
+
+/* Define action or option for cmd_802_11_scan */
+#define cmd_bss_type_bss                    0x0001
+#define cmd_bss_type_ibss                   0x0002
+#define cmd_bss_type_any                    0x0003
+
+/* Define action or option for cmd_802_11_scan */
+#define cmd_scan_type_active                0x0000
+#define cmd_scan_type_passive               0x0001
+
+#define cmd_scan_radio_type_bg		0
+
+#define cmd_scan_probe_delay_time           0
+
+/* Define action or option for cmd_mac_control */
+#define cmd_act_mac_rx_on                   0x0001
+#define cmd_act_mac_tx_on                   0x0002
+#define cmd_act_mac_loopback_on             0x0004
+#define cmd_act_mac_wep_enable              0x0008
+#define cmd_act_mac_int_enable              0x0010
+#define cmd_act_mac_multicast_enable        0x0020
+#define cmd_act_mac_broadcast_enable        0x0040
+#define cmd_act_mac_promiscuous_enable      0x0080
+#define cmd_act_mac_all_multicast_enable    0x0100
+#define cmd_act_mac_strict_protection_enable  0x0400
+
+/* Define action or option for cmd_802_11_radio_control */
+#define cmd_type_auto_preamble              0x0001
+#define cmd_type_short_preamble             0x0002
+#define cmd_type_long_preamble              0x0003
+
+#define TURN_ON_RF                              0x01
+#define RADIO_ON                                0x01
+#define RADIO_OFF                               0x00
+
+#define SET_AUTO_PREAMBLE                       0x05
+#define SET_SHORT_PREAMBLE                      0x03
+#define SET_LONG_PREAMBLE                       0x01
+
+/* Define action or option for CMD_802_11_RF_CHANNEL */
+#define cmd_opt_802_11_rf_channel_get       0x00
+#define cmd_opt_802_11_rf_channel_set       0x01
+
+/* Define action or option for cmd_802_11_rf_tx_power */
+#define cmd_act_tx_power_opt_get            0x0000
+#define cmd_act_tx_power_opt_set_high       0x8007
+#define cmd_act_tx_power_opt_set_mid        0x8004
+#define cmd_act_tx_power_opt_set_low        0x8000
+
+#define cmd_act_tx_power_index_high         0x0007
+#define cmd_act_tx_power_index_mid          0x0004
+#define cmd_act_tx_power_index_low          0x0000
+
+/* Define action or option for cmd_802_11_data_rate */
+#define cmd_act_set_tx_auto                 0x0000
+#define cmd_act_set_tx_fix_rate             0x0001
+#define cmd_act_get_tx_rate                 0x0002
+
+#define cmd_act_set_rx                      0x0001
+#define cmd_act_set_tx                      0x0002
+#define cmd_act_set_both                    0x0003
+#define cmd_act_get_rx                      0x0004
+#define cmd_act_get_tx                      0x0008
+#define cmd_act_get_both                    0x000c
+
+/* Define action or option for cmd_802_11_ps_mode */
+#define cmd_type_cam                        0x0000
+#define cmd_type_max_psp                    0x0001
+#define cmd_type_fast_psp                   0x0002
+
+/* Define action or option for cmd_bt_access */
+enum cmd_bt_access_opts {
+	/* The bt commands start at 5 instead of 1 because the old dft commands
+	 * are mapped to 1-4.  These old commands are no longer maintained and
+	 * should not be called.
+	 */
+	cmd_act_bt_access_add = 5,
+	cmd_act_bt_access_del,
+	cmd_act_bt_access_list,
+	cmd_act_bt_access_reset
+};
+
+/* Define action or option for cmd_fwt_access */
+enum cmd_fwt_access_opts {
+	cmd_act_fwt_access_add = 1,
+	cmd_act_fwt_access_del,
+	cmd_act_fwt_access_lookup,
+	cmd_act_fwt_access_list,
+	cmd_act_fwt_access_list_route,
+	cmd_act_fwt_access_list_neighbor,
+	cmd_act_fwt_access_reset,
+	cmd_act_fwt_access_cleanup,
+	cmd_act_fwt_access_time,
+};
+
+/* Define action or option for cmd_mesh_access */
+enum cmd_mesh_access_opts {
+	cmd_act_mesh_get_ttl = 1,
+	cmd_act_mesh_set_ttl,
+	cmd_act_mesh_get_stats,
+	cmd_act_mesh_get_mpp,
+	cmd_act_mesh_set_mpp,
+};
+
+/** Card Event definition */
+#define MACREG_INT_CODE_TX_PPA_FREE             0x00000000
+#define MACREG_INT_CODE_TX_DMA_DONE             0x00000001
+#define MACREG_INT_CODE_LINK_LOSE_W_SCAN        0x00000002
+#define MACREG_INT_CODE_LINK_LOSE_NO_SCAN       0x00000003
+#define MACREG_INT_CODE_LINK_SENSED             0x00000004
+#define MACREG_INT_CODE_CMD_FINISHED            0x00000005
+#define MACREG_INT_CODE_MIB_CHANGED             0x00000006
+#define MACREG_INT_CODE_INIT_DONE               0x00000007
+#define MACREG_INT_CODE_DEAUTHENTICATED         0x00000008
+#define MACREG_INT_CODE_DISASSOCIATED           0x00000009
+#define MACREG_INT_CODE_PS_AWAKE                0x0000000a
+#define MACREG_INT_CODE_PS_SLEEP                0x0000000b
+#define MACREG_INT_CODE_MIC_ERR_MULTICAST       0x0000000d
+#define MACREG_INT_CODE_MIC_ERR_UNICAST         0x0000000e
+#define MACREG_INT_CODE_WM_AWAKE                0x0000000f
+#define MACREG_INT_CODE_ADHOC_BCN_LOST          0x00000011
+#define MACREG_INT_CODE_RSSI_LOW		0x00000019
+#define MACREG_INT_CODE_SNR_LOW			0x0000001a
+#define MACREG_INT_CODE_MAX_FAIL		0x0000001b
+#define MACREG_INT_CODE_RSSI_HIGH		0x0000001c
+#define MACREG_INT_CODE_SNR_HIGH		0x0000001d
+
+#endif				/* _HOST_H_ */
diff --git a/drivers/net/wireless/libertas/hostcmd.h b/drivers/net/wireless/libertas/hostcmd.h
new file mode 100644
index 0000000..f239e5d
--- /dev/null
+++ b/drivers/net/wireless/libertas/hostcmd.h
@@ -0,0 +1,693 @@
+/*
+ * This file contains the function prototypes, data structure
+ * and defines for all the host/station commands
+ */
+#ifndef __HOSTCMD__H
+#define __HOSTCMD__H
+
+#include <linux/wireless.h>
+#include "11d.h"
+#include "types.h"
+
+/* 802.11-related definitions */
+
+/* TxPD descriptor */
+struct txpd {
+	/* Current Tx packet status */
+	u32 tx_status;
+	/* Tx control */
+	u32 tx_control;
+	u32 tx_packet_location;
+	/* Tx packet length */
+	u16 tx_packet_length;
+	/* First 2 byte of destination MAC address */
+	u8 tx_dest_addr_high[2];
+	/* Last 4 byte of destination MAC address */
+	u8 tx_dest_addr_low[4];
+	/* Pkt Priority */
+	u8 priority;
+	/* Pkt Trasnit Power control */
+	u8 powermgmt;
+	/* Amount of time the packet has been queued in the driver (units = 2ms) */
+	u8 pktdelay_2ms;
+	/* reserved */
+	u8 reserved1;
+};
+
+/* RxPD Descriptor */
+struct rxpd {
+	/* Current Rx packet status */
+	u16 status;
+
+	/* SNR */
+	u8 snr;
+
+	/* Tx control */
+	u8 rx_control;
+
+	/* Pkt length */
+	u16 pkt_len;
+
+	/* Noise Floor */
+	u8 nf;
+
+	/* Rx Packet Rate */
+	u8 rx_rate;
+
+	/* Pkt addr */
+	u32 pkt_ptr;
+
+	/* Next Rx RxPD addr */
+	u32 next_rxpd_ptr;
+
+	/* Pkt Priority */
+	u8 priority;
+	u8 reserved[3];
+};
+
+struct cmd_ctrl_node {
+	/* CMD link list */
+	struct list_head list;
+	u32 status;
+	/* CMD ID */
+	u32 cmd_oid;
+	/*CMD wait option: wait for finish or no wait */
+	u16 wait_option;
+	/* command parameter */
+	void *pdata_buf;
+	/*command data */
+	u8 *bufvirtualaddr;
+	u16 cmdflags;
+	/* wait queue */
+	u16 cmdwaitqwoken;
+	wait_queue_head_t cmdwait_q;
+};
+
+/* WLAN_802_11_KEY
+ *
+ * Generic structure to hold all key types.  key type (WEP40, WEP104, TKIP, AES)
+ * is determined from the keylength field.
+ */
+struct WLAN_802_11_KEY {
+	u32 len;
+	u32 flags;  /* KEY_INFO_* from wlan_defs.h */
+	u8 key[MRVL_MAX_KEY_WPA_KEY_LENGTH];
+	u16 type; /* KEY_TYPE_* from wlan_defs.h */
+};
+
+struct IE_WPA {
+	u8 elementid;
+	u8 len;
+	u8 oui[4];
+	u16 version;
+};
+
+struct WLAN_802_11_SSID {
+	/* SSID length */
+	u32 ssidlength;
+
+	/* SSID information field */
+	u8 ssid[IW_ESSID_MAX_SIZE];
+};
+
+struct WPA_SUPPLICANT {
+	u8 wpa_ie[256];
+	u8 wpa_ie_len;
+};
+
+/* wlan_offset_value */
+struct wlan_offset_value {
+	u32 offset;
+	u32 value;
+};
+
+struct WLAN_802_11_FIXED_IEs {
+	u8 timestamp[8];
+	u16 beaconinterval;
+	u16 capabilities;
+};
+
+struct WLAN_802_11_VARIABLE_IEs {
+	u8 elementid;
+	u8 length;
+	u8 data[1];
+};
+
+/* Define general data structure */
+/* cmd_DS_GEN */
+struct cmd_ds_gen {
+	u16 command;
+	u16 size;
+	u16 seqnum;
+	u16 result;
+};
+
+#define S_DS_GEN sizeof(struct cmd_ds_gen)
+/*
+ * Define data structure for cmd_get_hw_spec
+ * This structure defines the response for the GET_HW_SPEC command
+ */
+struct cmd_ds_get_hw_spec {
+	/* HW Interface version number */
+	u16 hwifversion;
+	/* HW version number */
+	u16 version;
+	/* Max number of TxPD FW can handle */
+	u16 nr_txpd;
+	/* Max no of Multicast address */
+	u16 nr_mcast_adr;
+	/* MAC address */
+	u8 permanentaddr[6];
+
+	/* region Code */
+	u16 regioncode;
+
+	/* Number of antenna used */
+	u16 nr_antenna;
+
+	/* FW release number, example 0x1234=1.2.3.4 */
+	u32 fwreleasenumber;
+
+	/* Base Address of TxPD queue */
+	u32 wcb_base;
+	/* Read Pointer of RxPd queue */
+	u32 rxpd_rdptr;
+
+	/* Write Pointer of RxPd queue */
+	u32 rxpd_wrptr;
+
+	/*FW/HW capability */
+	u32 fwcapinfo;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_reset {
+	u16 action;
+};
+
+struct cmd_ds_802_11_subscribe_event {
+	u16 action;
+	u16 events;
+};
+
+/*
+ * This scan handle Country Information IE(802.11d compliant)
+ * Define data structure for cmd_802_11_scan
+ */
+struct cmd_ds_802_11_scan {
+	u8 bsstype;
+	u8 BSSID[ETH_ALEN];
+	u8 tlvbuffer[1];
+#if 0
+	mrvlietypes_ssidparamset_t ssidParamSet;
+	mrvlietypes_chanlistparamset_t ChanListParamSet;
+	mrvlietypes_ratesparamset_t OpRateSet;
+#endif
+};
+
+struct cmd_ds_802_11_scan_rsp {
+	u16 bssdescriptsize;
+	u8 nr_sets;
+	u8 bssdesc_and_tlvbuffer[1];
+};
+
+struct cmd_ds_802_11_get_log {
+	u32 mcasttxframe;
+	u32 failed;
+	u32 retry;
+	u32 multiretry;
+	u32 framedup;
+	u32 rtssuccess;
+	u32 rtsfailure;
+	u32 ackfailure;
+	u32 rxfrag;
+	u32 mcastrxframe;
+	u32 fcserror;
+	u32 txframe;
+	u32 wepundecryptable;
+};
+
+struct cmd_ds_mac_control {
+	u16 action;
+	u16 reserved;
+};
+
+struct cmd_ds_mac_multicast_adr {
+	u16 action;
+	u16 nr_of_adrs;
+	u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE];
+};
+
+struct cmd_ds_802_11_authenticate {
+	u8 macaddr[ETH_ALEN];
+	u8 authtype;
+	u8 reserved[10];
+};
+
+struct cmd_ds_802_11_deauthenticate {
+	u8 macaddr[6];
+	u16 reasoncode;
+};
+
+struct cmd_ds_802_11_associate {
+	u8 peerstaaddr[6];
+	struct ieeetypes_capinfo capinfo;
+	u16 listeninterval;
+	u16 bcnperiod;
+	u8 dtimperiod;
+
+#if 0
+	mrvlietypes_ssidparamset_t ssidParamSet;
+	mrvlietypes_phyparamset_t phyparamset;
+	mrvlietypes_ssparamset_t ssparamset;
+	mrvlietypes_ratesparamset_t ratesParamSet;
+#endif
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_disassociate {
+	u8 destmacaddr[6];
+	u16 reasoncode;
+};
+
+struct cmd_ds_802_11_associate_rsp {
+	struct ieeetypes_assocrsp assocRsp;
+};
+
+struct cmd_ds_802_11_ad_hoc_result {
+	u8 PAD[3];
+	u8 BSSID[ETH_ALEN];
+};
+
+struct cmd_ds_802_11_set_wep {
+	/* ACT_ADD, ACT_REMOVE or ACT_ENABLE */
+	u16 action;
+
+	/* key Index selected for Tx */
+	u16 keyindex;
+
+	/* 40, 128bit or TXWEP */
+	u8 keytype[4];
+	u8 keymaterial[4][16];
+};
+
+struct cmd_ds_802_3_get_stat {
+	u32 xmitok;
+	u32 rcvok;
+	u32 xmiterror;
+	u32 rcverror;
+	u32 rcvnobuffer;
+	u32 rcvcrcerror;
+};
+
+struct cmd_ds_802_11_get_stat {
+	u32 txfragmentcnt;
+	u32 mcasttxframecnt;
+	u32 failedcnt;
+	u32 retrycnt;
+	u32 Multipleretrycnt;
+	u32 rtssuccesscnt;
+	u32 rtsfailurecnt;
+	u32 ackfailurecnt;
+	u32 frameduplicatecnt;
+	u32 rxfragmentcnt;
+	u32 mcastrxframecnt;
+	u32 fcserrorcnt;
+	u32 bcasttxframecnt;
+	u32 bcastrxframecnt;
+	u32 txbeacon;
+	u32 rxbeacon;
+	u32 wepundecryptable;
+};
+
+struct cmd_ds_802_11_snmp_mib {
+	u16 querytype;
+	u16 oid;
+	u16 bufsize;
+	u8 value[128];
+};
+
+struct cmd_ds_mac_reg_map {
+	u16 buffersize;
+	u8 regmap[128];
+	u16 reserved;
+};
+
+struct cmd_ds_bbp_reg_map {
+	u16 buffersize;
+	u8 regmap[128];
+	u16 reserved;
+};
+
+struct cmd_ds_rf_reg_map {
+	u16 buffersize;
+	u8 regmap[64];
+	u16 reserved;
+};
+
+struct cmd_ds_mac_reg_access {
+	u16 action;
+	u16 offset;
+	u32 value;
+};
+
+struct cmd_ds_bbp_reg_access {
+	u16 action;
+	u16 offset;
+	u8 value;
+	u8 reserved[3];
+};
+
+struct cmd_ds_rf_reg_access {
+	u16 action;
+	u16 offset;
+	u8 value;
+	u8 reserved[3];
+};
+
+struct cmd_ds_802_11_radio_control {
+	u16 action;
+	u16 control;
+};
+
+struct cmd_ds_802_11_sleep_params {
+	/* ACT_GET/ACT_SET */
+	u16 action;
+
+	/* Sleep clock error in ppm */
+	u16 error;
+
+	/* Wakeup offset in usec */
+	u16 offset;
+
+	/* Clock stabilization time in usec */
+	u16 stabletime;
+
+	/* control periodic calibration */
+	u8 calcontrol;
+
+	/* control the use of external sleep clock */
+	u8 externalsleepclk;
+
+	/* reserved field, should be set to zero */
+	u16 reserved;
+};
+
+struct cmd_ds_802_11_inactivity_timeout {
+	/* ACT_GET/ACT_SET */
+	u16 action;
+
+	/* Inactivity timeout in msec */
+	u16 timeout;
+};
+
+struct cmd_ds_802_11_rf_channel {
+	u16 action;
+	u16 currentchannel;
+	u16 rftype;
+	u16 reserved;
+	u8 channellist[32];
+};
+
+struct cmd_ds_802_11_rssi {
+	/* weighting factor */
+	u16 N;
+
+	u16 reserved_0;
+	u16 reserved_1;
+	u16 reserved_2;
+};
+
+struct cmd_ds_802_11_rssi_rsp {
+	u16 SNR;
+	u16 noisefloor;
+	u16 avgSNR;
+	u16 avgnoisefloor;
+};
+
+struct cmd_ds_802_11_mac_address {
+	u16 action;
+	u8 macadd[ETH_ALEN];
+};
+
+struct cmd_ds_802_11_rf_tx_power {
+	u16 action;
+	u16 currentlevel;
+};
+
+struct cmd_ds_802_11_rf_antenna {
+	u16 action;
+
+	/* Number of antennas or 0xffff(diversity) */
+	u16 antennamode;
+
+};
+
+struct cmd_ds_802_11_ps_mode {
+	u16 action;
+	u16 nullpktinterval;
+	u16 multipledtim;
+	u16 reserved;
+	u16 locallisteninterval;
+};
+
+struct PS_CMD_ConfirmSleep {
+	u16 command;
+	u16 size;
+	u16 seqnum;
+	u16 result;
+
+	u16 action;
+	u16 reserved1;
+	u16 multipledtim;
+	u16 reserved;
+	u16 locallisteninterval;
+};
+
+struct cmd_ds_802_11_data_rate {
+	u16 action;
+	u16 reserverd;
+	u8 datarate[G_SUPPORTED_RATES];
+};
+
+struct cmd_ds_802_11_rate_adapt_rateset {
+	u16 action;
+	u16 enablehwauto;
+	u16 bitmap;
+};
+
+struct cmd_ds_802_11_ad_hoc_start {
+	u8 SSID[IW_ESSID_MAX_SIZE];
+	u8 bsstype;
+	u16 beaconperiod;
+	u8 dtimperiod;
+	union IEEEtypes_ssparamset ssparamset;
+	union ieeetypes_phyparamset phyparamset;
+	u16 probedelay;
+	struct ieeetypes_capinfo cap;
+	u8 datarate[G_SUPPORTED_RATES];
+	u8 tlv_memory_size_pad[100];
+} __attribute__ ((packed));
+
+struct adhoc_bssdesc {
+	u8 BSSID[6];
+	u8 SSID[32];
+	u8 bsstype;
+	u16 beaconperiod;
+	u8 dtimperiod;
+	u8 timestamp[8];
+	u8 localtime[8];
+	union ieeetypes_phyparamset phyparamset;
+	union IEEEtypes_ssparamset ssparamset;
+	struct ieeetypes_capinfo cap;
+	u8 datarates[G_SUPPORTED_RATES];
+
+	/* DO NOT ADD ANY FIELDS TO THIS STRUCTURE. It is used below in the
+	 * Adhoc join command and will cause a binary layout mismatch with
+	 * the firmware
+	 */
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_ad_hoc_join {
+	struct adhoc_bssdesc bssdescriptor;
+	u16 failtimeout;
+	u16 probedelay;
+
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_enable_rsn {
+	u16 action;
+	u16 enable;
+};
+
+struct MrvlIEtype_keyParamSet {
+	/* type ID */
+	u16 type;
+
+	/* length of Payload */
+	u16 length;
+
+	/* type of key: WEP=0, TKIP=1, AES=2 */
+	u16 keytypeid;
+
+	/* key control Info specific to a keytypeid */
+	u16 keyinfo;
+
+	/* length of key */
+	u16 keylen;
+
+	/* key material of size keylen */
+	u8 key[32];
+};
+
+struct cmd_ds_802_11_key_material {
+	u16 action;
+	struct MrvlIEtype_keyParamSet keyParamSet[2];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_eeprom_access {
+	u16 action;
+
+	/* multiple 4 */
+	u16 offset;
+	u16 bytecount;
+	u8 value;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_tpc_cfg {
+	u16 action;
+	u8 enable;
+	s8 P0;
+	s8 P1;
+	s8 P2;
+	u8 usesnr;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_led_ctrl {
+	u16 action;
+	u16 numled;
+	u8 data[256];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_pwr_cfg {
+	u16 action;
+	u8 enable;
+	s8 PA_P0;
+	s8 PA_P1;
+	s8 PA_P2;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_afc {
+	u16 afc_auto;
+	union {
+		struct {
+			u16 threshold;
+			u16 period;
+		};
+		struct {
+			s16 timing_offset;
+			s16 carrier_offset;
+		};
+	};
+} __attribute__ ((packed));
+
+struct cmd_tx_rate_query {
+	u16 txrate;
+} __attribute__ ((packed));
+
+struct cmd_ds_get_tsf {
+	__le64 tsfvalue;
+} __attribute__ ((packed));
+
+struct cmd_ds_bt_access {
+	u16 action;
+	u32 id;
+	u8 addr1[ETH_ALEN];
+	u8 addr2[ETH_ALEN];
+} __attribute__ ((packed));
+
+struct cmd_ds_fwt_access {
+	u16 action;
+	u32 id;
+	u8 da[ETH_ALEN];
+	u8 dir;
+	u8 ra[ETH_ALEN];
+	u32 ssn;
+	u32 dsn;
+	u32 metric;
+	u8 hopcount;
+	u8 ttl;
+	u32 expiration;
+	u8 sleepmode;
+	u32 snr;
+	u32 references;
+} __attribute__ ((packed));
+
+#define MESH_STATS_NUM 7
+struct cmd_ds_mesh_access {
+	u16 action;
+	u32 data[MESH_STATS_NUM + 1];	/* last position reserved */
+} __attribute__ ((packed));
+
+struct cmd_ds_command {
+	/* command header */
+	u16 command;
+	u16 size;
+	u16 seqnum;
+	u16 result;
+
+	/* command Body */
+	union {
+		struct cmd_ds_get_hw_spec hwspec;
+		struct cmd_ds_802_11_ps_mode psmode;
+		struct cmd_ds_802_11_scan scan;
+		struct cmd_ds_802_11_scan_rsp scanresp;
+		struct cmd_ds_mac_control macctrl;
+		struct cmd_ds_802_11_associate associate;
+		struct cmd_ds_802_11_deauthenticate deauth;
+		struct cmd_ds_802_11_set_wep wep;
+		struct cmd_ds_802_11_ad_hoc_start ads;
+		struct cmd_ds_802_11_reset reset;
+		struct cmd_ds_802_11_ad_hoc_result result;
+		struct cmd_ds_802_11_get_log glog;
+		struct cmd_ds_802_11_authenticate auth;
+		struct cmd_ds_802_11_get_stat gstat;
+		struct cmd_ds_802_3_get_stat gstat_8023;
+		struct cmd_ds_802_11_snmp_mib smib;
+		struct cmd_ds_802_11_rf_tx_power txp;
+		struct cmd_ds_802_11_rf_antenna rant;
+		struct cmd_ds_802_11_data_rate drate;
+		struct cmd_ds_802_11_rate_adapt_rateset rateset;
+		struct cmd_ds_mac_multicast_adr madr;
+		struct cmd_ds_802_11_ad_hoc_join adj;
+		struct cmd_ds_802_11_radio_control radio;
+		struct cmd_ds_802_11_rf_channel rfchannel;
+		struct cmd_ds_802_11_rssi rssi;
+		struct cmd_ds_802_11_rssi_rsp rssirsp;
+		struct cmd_ds_802_11_disassociate dassociate;
+		struct cmd_ds_802_11_mac_address macadd;
+		struct cmd_ds_802_11_enable_rsn enbrsn;
+		struct cmd_ds_802_11_key_material keymaterial;
+		struct cmd_ds_mac_reg_access macreg;
+		struct cmd_ds_bbp_reg_access bbpreg;
+		struct cmd_ds_rf_reg_access rfreg;
+		struct cmd_ds_802_11_eeprom_access rdeeprom;
+
+		struct cmd_ds_802_11d_domain_info domaininfo;
+		struct cmd_ds_802_11d_domain_info domaininforesp;
+
+		struct cmd_ds_802_11_sleep_params sleep_params;
+		struct cmd_ds_802_11_inactivity_timeout inactivity_timeout;
+		struct cmd_ds_802_11_tpc_cfg tpccfg;
+		struct cmd_ds_802_11_pwr_cfg pwrcfg;
+		struct cmd_ds_802_11_afc afc;
+		struct cmd_ds_802_11_led_ctrl ledgpio;
+
+		struct cmd_tx_rate_query txrate;
+		struct cmd_ds_bt_access bt;
+		struct cmd_ds_fwt_access fwt;
+		struct cmd_ds_mesh_access mesh;
+		struct cmd_ds_get_tsf gettsf;
+		struct cmd_ds_802_11_subscribe_event subscribe_event;
+	} params;
+} __attribute__ ((packed));
+
+#endif
diff --git a/drivers/net/wireless/libertas/if_bootcmd.c b/drivers/net/wireless/libertas/if_bootcmd.c
new file mode 100644
index 0000000..567000c
--- /dev/null
+++ b/drivers/net/wireless/libertas/if_bootcmd.c
@@ -0,0 +1,38 @@
+/**
+  * This file contains functions used in USB Boot command
+  * and Boot2/FW update
+  */
+
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/netdevice.h>
+#include <linux/usb.h>
+
+#include "defs.h"
+#include "dev.h"
+#include "if_usb.h"
+
+/**
+ *  @brief This function issues Boot command to the Boot2 code
+ *  @param ivalue   1:Boot from FW by USB-Download
+ *                  2:Boot from FW in EEPROM
+ *  @return 	   	0
+ */
+int if_usb_issue_boot_command(wlan_private *priv, int ivalue)
+{
+	struct usb_card_rec	*cardp = priv->wlan_dev.card;
+	struct bootcmdstr	sbootcmd;
+	int i;
+
+	/* Prepare command */
+	sbootcmd.u32magicnumber = BOOT_CMD_MAGIC_NUMBER;
+	sbootcmd.u8cmd_tag = ivalue;
+	for (i=0; i<11; i++)
+		sbootcmd.au8dumy[i]=0x00;
+	memcpy(cardp->bulk_out_buffer, &sbootcmd, sizeof(struct bootcmdstr));
+
+	/* Issue command */
+	usb_tx_block(priv, cardp->bulk_out_buffer, sizeof(struct bootcmdstr));
+
+	return 0;
+}
diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c
new file mode 100644
index 0000000..695fb6a
--- /dev/null
+++ b/drivers/net/wireless/libertas/if_usb.c
@@ -0,0 +1,952 @@
+/**
+  * This file contains functions used in USB interface module.
+  */
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/netdevice.h>
+#include <linux/usb.h>
+
+#include "host.h"
+#include "sbi.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "if_usb.h"
+
+#define MESSAGE_HEADER_LEN	4
+
+static const char usbdriver_name[] = "usb8xxx";
+
+static struct usb_device_id if_usb_table[] = {
+	/* Enter the device signature inside */
+	{
+		USB_DEVICE(USB8388_VID_1, USB8388_PID_1),
+	},
+	{
+		USB_DEVICE(USB8388_VID_2, USB8388_PID_2),
+	},
+	{}	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, if_usb_table);
+
+static void if_usb_receive(struct urb *urb);
+static void if_usb_receive_fwload(struct urb *urb);
+
+/**
+ *  @brief  call back function to handle the status of the URB
+ *  @param urb 		pointer to urb structure
+ *  @return 	   	N/A
+ */
+static void if_usb_write_bulk_callback(struct urb *urb)
+{
+	wlan_private *priv = (wlan_private *) (urb->context);
+	wlan_adapter *adapter = priv->adapter;
+	struct net_device *dev = priv->wlan_dev.netdev;
+
+	/* handle the transmission complete validations */
+
+	if (urb->status != 0) {
+		/* print the failure status number for debug */
+		lbs_pr_info("URB in failure status\n");
+	} else {
+		lbs_dev_dbg(2, &urb->dev->dev, "URB status is successfull\n");
+		lbs_dev_dbg(2, &urb->dev->dev, "Actual length transmitted %d\n",
+		       urb->actual_length);
+		priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED;
+		/* Wake main thread if commands are pending */
+		if (!adapter->cur_cmd)
+			wake_up_interruptible(&priv->mainthread.waitq);
+		if ((adapter->connect_status == libertas_connected))
+			netif_wake_queue(dev);
+	}
+
+	return;
+}
+
+/**
+ *  @brief  free tx/rx urb, skb and rx buffer
+ *  @param cardp	pointer usb_card_rec
+ *  @return 	   	N/A
+ */
+void if_usb_free(struct usb_card_rec *cardp)
+{
+	ENTER();
+
+	/* Unlink tx & rx urb */
+	usb_kill_urb(cardp->tx_urb);
+	usb_kill_urb(cardp->rx_urb);
+
+	usb_free_urb(cardp->tx_urb);
+	cardp->tx_urb = NULL;
+
+	usb_free_urb(cardp->rx_urb);
+	cardp->rx_urb = NULL;
+
+	kfree(cardp->bulk_out_buffer);
+	cardp->bulk_out_buffer = NULL;
+
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief sets the configuration values
+ *  @param ifnum	interface number
+ *  @param id		pointer to usb_device_id
+ *  @return 	   	0 on success, error code on failure
+ */
+static int if_usb_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	struct usb_device *udev;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	wlan_private *pwlanpriv;
+	struct usb_card_rec *usb_cardp;
+	int i;
+
+	udev = interface_to_usbdev(intf);
+
+	usb_cardp = kzalloc(sizeof(struct usb_card_rec), GFP_KERNEL);
+	if (!usb_cardp) {
+		lbs_pr_err("Out of memory allocating private data.\n");
+		goto error;
+	}
+
+	usb_cardp->udev = udev;
+	iface_desc = intf->cur_altsetting;
+
+	lbs_dev_dbg(1, &udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X"
+	       " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n",
+	       udev->descriptor.bcdUSB,
+	       udev->descriptor.bDeviceClass,
+	       udev->descriptor.bDeviceSubClass,
+	       udev->descriptor.bDeviceProtocol);
+
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+		if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+		    && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+			USB_ENDPOINT_XFER_BULK)) {
+			/* we found a bulk in endpoint */
+			lbs_dev_dbg(1, &udev->dev, "Bulk in size is %d\n",
+			       endpoint->wMaxPacketSize);
+			if (!
+			    (usb_cardp->rx_urb =
+			     usb_alloc_urb(0, GFP_KERNEL))) {
+				lbs_dev_dbg(1, &udev->dev,
+				       "Rx URB allocation failed\n");
+				goto dealloc;
+			}
+			usb_cardp->rx_urb_recall = 0;
+
+			usb_cardp->bulk_in_size =
+			    endpoint->wMaxPacketSize;
+			usb_cardp->bulk_in_endpointAddr =
+			    (endpoint->
+			     bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+			lbs_dev_dbg(1, &udev->dev, "in_endpoint = %d\n",
+			       endpoint->bEndpointAddress);
+		}
+
+		if (((endpoint->
+		      bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
+		     USB_DIR_OUT)
+		    && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+			USB_ENDPOINT_XFER_BULK)) {
+			/* We found bulk out endpoint */
+			if (!
+			    (usb_cardp->tx_urb =
+			     usb_alloc_urb(0, GFP_KERNEL))) {
+				lbs_dev_dbg(1,&udev->dev,
+				       "Tx URB allocation failed\n");
+				goto dealloc;
+			}
+
+			usb_cardp->bulk_out_size =
+			    endpoint->wMaxPacketSize;
+			lbs_dev_dbg(1, &udev->dev,
+				    "Bulk out size is %d\n",
+				    endpoint->wMaxPacketSize);
+			usb_cardp->bulk_out_endpointAddr =
+			    endpoint->bEndpointAddress;
+			lbs_dev_dbg(1, &udev->dev, "out_endpoint = %d\n",
+				    endpoint->bEndpointAddress);
+			usb_cardp->bulk_out_buffer =
+			    kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE,
+				    GFP_KERNEL);
+
+			if (!usb_cardp->bulk_out_buffer) {
+				lbs_dev_dbg(1, &udev->dev,
+				       "Could not allocate buffer\n");
+				goto dealloc;
+			}
+		}
+	}
+
+
+	/* At this point wlan_add_card() will be called.  Don't worry
+	 * about keeping pwlanpriv around since it will be set on our
+	 * usb device data in -> add() -> libertas_sbi_register_dev().
+	 */
+	if (!(pwlanpriv = wlan_add_card(usb_cardp)))
+		goto dealloc;
+
+	usb_get_dev(udev);
+	usb_set_intfdata(intf, usb_cardp);
+
+	/*
+	 * return card structure, which can be got back in the
+	 * diconnect function as the ptr
+	 * argument.
+	 */
+	return 0;
+
+dealloc:
+	if_usb_free(usb_cardp);
+
+error:
+	return -ENOMEM;
+}
+
+/**
+ *  @brief free resource and cleanup
+ *  @param udev		pointer to usb_device
+ *  @param ptr		pointer to usb_cardp
+ *  @return 	   	N/A
+ */
+static void if_usb_disconnect(struct usb_interface *intf)
+{
+	struct usb_card_rec *cardp = usb_get_intfdata(intf);
+	wlan_private *priv = (wlan_private *) cardp->priv;
+	wlan_adapter *adapter = NULL;
+
+	adapter = priv->adapter;
+
+	/*
+	 * Update Surprise removed to TRUE
+	 */
+	adapter->surpriseremoved = 1;
+
+	/* card is removed and we can call wlan_remove_card */
+	lbs_dev_dbg(1, &cardp->udev->dev, "call remove card\n");
+	wlan_remove_card(cardp);
+
+	/* Unlink and free urb */
+	if_usb_free(cardp);
+
+	usb_set_intfdata(intf, NULL);
+	usb_put_dev(interface_to_usbdev(intf));
+
+	return;
+}
+
+/**
+ *  @brief  This function download FW
+ *  @param priv		pointer to wlan_private
+ *  @return 	   	0
+ */
+static int if_prog_firmware(wlan_private * priv)
+{
+	struct usb_card_rec *cardp = priv->wlan_dev.card;
+	struct FWData *fwdata;
+	struct fwheader *fwheader;
+	u8 *firmware = priv->firmware->data;
+
+	fwdata = kmalloc(sizeof(struct FWData), GFP_ATOMIC);
+
+	if (!fwdata)
+		return -1;
+
+	fwheader = &fwdata->fwheader;
+
+	if (!cardp->CRC_OK) {
+		cardp->totalbytes = cardp->fwlastblksent;
+		cardp->fwseqnum = cardp->lastseqnum - 1;
+	}
+
+	lbs_dev_dbg(2, &cardp->udev->dev, "totalbytes = %d\n",
+		    cardp->totalbytes);
+
+	memcpy(fwheader, &firmware[cardp->totalbytes],
+	       sizeof(struct fwheader));
+
+	cardp->fwlastblksent = cardp->totalbytes;
+	cardp->totalbytes += sizeof(struct fwheader);
+
+	lbs_dev_dbg(2, &cardp->udev->dev,"Copy Data\n");
+	memcpy(fwdata->data, &firmware[cardp->totalbytes],
+	       fwdata->fwheader.datalength);
+
+	lbs_dev_dbg(2, &cardp->udev->dev,
+		    "Data length = %d\n", fwdata->fwheader.datalength);
+
+	cardp->fwseqnum = cardp->fwseqnum + 1;
+
+	fwdata->seqnum = cardp->fwseqnum;
+	cardp->lastseqnum = fwdata->seqnum;
+	cardp->totalbytes += fwdata->fwheader.datalength;
+
+	if (fwheader->dnldcmd == FW_HAS_DATA_TO_RECV) {
+		lbs_dev_dbg(2, &cardp->udev->dev, "There is data to follow\n");
+		lbs_dev_dbg(2, &cardp->udev->dev,
+			    "seqnum = %d totalbytes = %d\n", cardp->fwseqnum,
+			    cardp->totalbytes);
+		memcpy(cardp->bulk_out_buffer, fwheader, FW_DATA_XMIT_SIZE);
+		usb_tx_block(priv, cardp->bulk_out_buffer, FW_DATA_XMIT_SIZE);
+
+	} else if (fwdata->fwheader.dnldcmd == FW_HAS_LAST_BLOCK) {
+		lbs_dev_dbg(2, &cardp->udev->dev,
+			    "Host has finished FW downloading\n");
+		lbs_dev_dbg(2, &cardp->udev->dev,
+			    "Donwloading FW JUMP BLOCK\n");
+		memcpy(cardp->bulk_out_buffer, fwheader, FW_DATA_XMIT_SIZE);
+		usb_tx_block(priv, cardp->bulk_out_buffer, FW_DATA_XMIT_SIZE);
+		cardp->fwfinalblk = 1;
+	}
+
+	lbs_dev_dbg(2, &cardp->udev->dev,
+		    "The firmware download is done size is %d\n",
+		    cardp->totalbytes);
+
+	kfree(fwdata);
+
+	return 0;
+}
+
+static int libertas_do_reset(wlan_private *priv)
+{
+	int ret;
+	struct usb_card_rec *cardp = priv->wlan_dev.card;
+
+	ret = usb_reset_device(cardp->udev);
+	if (!ret) {
+		msleep(10);
+		reset_device(priv);
+		msleep(10);
+	}
+	return ret;
+}
+
+/**
+ *  @brief This function transfer the data to the device.
+ *  @param priv 	pointer to wlan_private
+ *  @param payload	pointer to payload data
+ *  @param nb		data length
+ *  @return 	   	0 or -1
+ */
+int usb_tx_block(wlan_private * priv, u8 * payload, u16 nb)
+{
+	/* pointer to card structure */
+	struct usb_card_rec *cardp = priv->wlan_dev.card;
+	int ret = -1;
+
+	/* check if device is removed */
+	if (priv->adapter->surpriseremoved) {
+		lbs_dev_dbg(1, &cardp->udev->dev, "Device removed\n");
+		goto tx_ret;
+	}
+
+	usb_fill_bulk_urb(cardp->tx_urb, cardp->udev,
+			  usb_sndbulkpipe(cardp->udev,
+					  cardp->bulk_out_endpointAddr),
+			  payload, nb, if_usb_write_bulk_callback, priv);
+
+	cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET;
+
+	if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) {
+		/*  transfer failed */
+		lbs_dev_dbg(1, &cardp->udev->dev, "usb_submit_urb failed\n");
+		ret = -1;
+	} else {
+		lbs_dev_dbg(2, &cardp->udev->dev, "usb_submit_urb success\n");
+		ret = 0;
+	}
+
+tx_ret:
+	return ret;
+}
+
+static int __if_usb_submit_rx_urb(wlan_private * priv,
+				  void (*callbackfn)
+				  (struct urb *urb))
+{
+	struct usb_card_rec *cardp = priv->wlan_dev.card;
+	struct sk_buff *skb;
+	struct read_cb_info *rinfo = &cardp->rinfo;
+	int ret = -1;
+
+	if (!(skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE))) {
+		lbs_pr_err("No free skb\n");
+		goto rx_ret;
+	}
+
+	rinfo->skb = skb;
+
+	/* Fill the receive configuration URB and initialise the Rx call back */
+	usb_fill_bulk_urb(cardp->rx_urb, cardp->udev,
+			  usb_rcvbulkpipe(cardp->udev,
+					  cardp->bulk_in_endpointAddr),
+			  skb->tail + IPFIELD_ALIGN_OFFSET,
+			  MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn,
+			  rinfo);
+
+	cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET;
+
+	lbs_dev_dbg(2, &cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb);
+	if ((ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC))) {
+		/* handle failure conditions */
+		lbs_dev_dbg(1, &cardp->udev->dev, "Submit Rx URB failed\n");
+		ret = -1;
+	} else {
+		lbs_dev_dbg(2, &cardp->udev->dev, "Submit Rx URB success\n");
+		ret = 0;
+	}
+
+rx_ret:
+	return ret;
+}
+
+static inline int if_usb_submit_rx_urb_fwload(wlan_private * priv)
+{
+	return __if_usb_submit_rx_urb(priv, &if_usb_receive_fwload);
+}
+
+static inline int if_usb_submit_rx_urb(wlan_private * priv)
+{
+	return __if_usb_submit_rx_urb(priv, &if_usb_receive);
+}
+
+static void if_usb_receive_fwload(struct urb *urb)
+{
+	struct read_cb_info *rinfo = (struct read_cb_info *)urb->context;
+	wlan_private *priv = rinfo->priv;
+	struct sk_buff *skb = rinfo->skb;
+	struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card;
+	struct fwsyncheader *syncfwheader;
+	struct bootcmdrespStr bootcmdresp;
+
+	if (urb->status) {
+		lbs_dev_dbg(1, &cardp->udev->dev,
+			    "URB status is failed during fw load\n");
+		kfree_skb(skb);
+		return;
+	}
+
+	if (cardp->bootcmdresp == 0) {
+		memcpy (&bootcmdresp, skb->data + IPFIELD_ALIGN_OFFSET,
+			sizeof(bootcmdresp));
+		if (cardp->udev->descriptor.bcdDevice < 0x3106) {
+			kfree_skb(skb);
+			if_usb_submit_rx_urb_fwload(priv);
+			cardp->bootcmdresp = 1;
+			lbs_dev_dbg(1, &cardp->udev->dev,
+				    "Received valid boot command response\n");
+			return;
+		}
+		if (bootcmdresp.u32magicnumber != BOOT_CMD_MAGIC_NUMBER) {
+			lbs_pr_info(
+				"boot cmd response wrong magic number (0x%x)\n",
+				bootcmdresp.u32magicnumber);
+		} else if (bootcmdresp.u8cmd_tag != BOOT_CMD_FW_BY_USB) {
+			lbs_pr_info(
+				"boot cmd response cmd_tag error (%d)\n",
+				bootcmdresp.u8cmd_tag);
+		} else if (bootcmdresp.u8result != BOOT_CMD_RESP_OK) {
+			lbs_pr_info(
+				"boot cmd response result error (%d)\n",
+				bootcmdresp.u8result);
+		} else {
+			cardp->bootcmdresp = 1;
+			lbs_dev_dbg(1, &cardp->udev->dev,
+				    "Received valid boot command response\n");
+		}
+		kfree_skb(skb);
+		if_usb_submit_rx_urb_fwload(priv);
+		return;
+	}
+
+	syncfwheader = kmalloc(sizeof(struct fwsyncheader), GFP_ATOMIC);
+	if (!syncfwheader) {
+		lbs_dev_dbg(1, &cardp->udev->dev, "Failure to allocate syncfwheader\n");
+		kfree_skb(skb);
+		return;
+	}
+
+	memcpy(syncfwheader, skb->data + IPFIELD_ALIGN_OFFSET,
+			sizeof(struct fwsyncheader));
+
+	if (!syncfwheader->cmd) {
+		lbs_dev_dbg(2, &cardp->udev->dev,
+			    "FW received Blk with correct CRC\n");
+		lbs_dev_dbg(2, &cardp->udev->dev,
+			    "FW received Blk seqnum = %d\n",
+		       syncfwheader->seqnum);
+		cardp->CRC_OK = 1;
+	} else {
+		lbs_dev_dbg(1, &cardp->udev->dev,
+			    "FW received Blk with CRC error\n");
+		cardp->CRC_OK = 0;
+	}
+
+	kfree_skb(skb);
+
+	if (cardp->fwfinalblk) {
+		cardp->fwdnldover = 1;
+		goto exit;
+	}
+
+	if_prog_firmware(priv);
+
+	if_usb_submit_rx_urb_fwload(priv);
+exit:
+	kfree(syncfwheader);
+
+	return;
+
+}
+
+#define MRVDRV_MIN_PKT_LEN	30
+
+static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb,
+				       struct usb_card_rec *cardp,
+				       wlan_private *priv)
+{
+	if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE +
+	    MESSAGE_HEADER_LEN || recvlength < MRVDRV_MIN_PKT_LEN) {
+		lbs_dev_dbg(1, &cardp->udev->dev,
+			    "Packet length is Invalid\n");
+		kfree_skb(skb);
+		return;
+	}
+
+	skb_reserve(skb, IPFIELD_ALIGN_OFFSET);
+	skb_put(skb, recvlength);
+	skb_pull(skb, MESSAGE_HEADER_LEN);
+	libertas_process_rxed_packet(priv, skb);
+	priv->wlan_dev.upld_len = (recvlength - MESSAGE_HEADER_LEN);
+}
+
+static inline void process_cmdrequest(int recvlength, u8 *recvbuff,
+				      struct sk_buff *skb,
+				      struct usb_card_rec *cardp,
+				      wlan_private *priv)
+{
+	u8 *cmdbuf;
+	if (recvlength > MRVDRV_SIZE_OF_CMD_BUFFER) {
+		lbs_dev_dbg(1, &cardp->udev->dev,
+			    "The receive buffer is too large\n");
+		kfree_skb(skb);
+		return;
+	}
+
+	if (!in_interrupt())
+		BUG();
+
+	spin_lock(&priv->adapter->driver_lock);
+	/* take care of cur_cmd = NULL case by reading the
+	 * data to clear the interrupt */
+	if (!priv->adapter->cur_cmd) {
+		cmdbuf = priv->wlan_dev.upld_buf;
+		priv->adapter->hisregcpy &= ~his_cmdupldrdy;
+	} else
+		cmdbuf = priv->adapter->cur_cmd->bufvirtualaddr;
+
+	cardp->usb_int_cause |= his_cmdupldrdy;
+	priv->wlan_dev.upld_len = (recvlength - MESSAGE_HEADER_LEN);
+	memcpy(cmdbuf, recvbuff + MESSAGE_HEADER_LEN,
+	       priv->wlan_dev.upld_len);
+
+	kfree_skb(skb);
+	libertas_interrupt(priv->wlan_dev.netdev);
+	spin_unlock(&priv->adapter->driver_lock);
+
+	lbs_dev_dbg(1, &cardp->udev->dev,
+		    "Wake up main thread to handle cmd response\n");
+
+	return;
+}
+
+/**
+ *  @brief This function reads of the packet into the upload buff,
+ *  wake up the main thread and initialise the Rx callack.
+ *
+ *  @param urb		pointer to struct urb
+ *  @return 	   	N/A
+ */
+static void if_usb_receive(struct urb *urb)
+{
+	struct read_cb_info *rinfo = (struct read_cb_info *)urb->context;
+	wlan_private *priv = rinfo->priv;
+	struct sk_buff *skb = rinfo->skb;
+	struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card;
+
+	int recvlength = urb->actual_length;
+	u8 *recvbuff = NULL;
+	u32 recvtype;
+
+	ENTER();
+
+	if (recvlength) {
+		if (urb->status) {
+			lbs_dev_dbg(1, &cardp->udev->dev,
+				    "URB status is failed\n");
+			kfree_skb(skb);
+			goto setup_for_next;
+		}
+
+		recvbuff = skb->data + IPFIELD_ALIGN_OFFSET;
+		memcpy(&recvtype, recvbuff, sizeof(u32));
+		lbs_dev_dbg(1, &cardp->udev->dev,
+			    "Recv length = 0x%x\n", recvlength);
+		lbs_dev_dbg(1, &cardp->udev->dev,
+			    "Receive type = 0x%X\n", recvtype);
+		recvtype = le32_to_cpu(recvtype);
+		lbs_dev_dbg(1, &cardp->udev->dev,
+			    "Receive type after = 0x%X\n", recvtype);
+	} else if (urb->status)
+		goto rx_exit;
+
+
+	switch (recvtype) {
+	case CMD_TYPE_DATA:
+		process_cmdtypedata(recvlength, skb, cardp, priv);
+		break;
+
+	case CMD_TYPE_REQUEST:
+		process_cmdrequest(recvlength, recvbuff, skb, cardp, priv);
+		break;
+
+	case CMD_TYPE_INDICATION:
+		/* Event cause handling */
+		spin_lock(&priv->adapter->driver_lock);
+		cardp->usb_event_cause = *(u32 *) (recvbuff + MESSAGE_HEADER_LEN);
+		lbs_dev_dbg(1, &cardp->udev->dev,"**EVENT** 0x%X\n",
+			    cardp->usb_event_cause);
+		if (cardp->usb_event_cause & 0xffff0000) {
+			libertas_send_tx_feedback(priv);
+			break;
+		}
+		cardp->usb_event_cause = le32_to_cpu(cardp->usb_event_cause) << 3;
+		cardp->usb_int_cause |= his_cardevent;
+		kfree_skb(skb);
+		libertas_interrupt(priv->wlan_dev.netdev);
+		spin_unlock(&priv->adapter->driver_lock);
+		goto rx_exit;
+	default:
+		kfree_skb(skb);
+		break;
+	}
+
+setup_for_next:
+	if_usb_submit_rx_urb(priv);
+rx_exit:
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function downloads data to FW
+ *  @param priv		pointer to wlan_private structure
+ *  @param type		type of data
+ *  @param buf		pointer to data buffer
+ *  @param len		number of bytes
+ *  @return 	   	0 or -1
+ */
+int libertas_sbi_host_to_card(wlan_private * priv, u8 type, u8 * payload, u16 nb)
+{
+	int ret = -1;
+	u32 tmp;
+	struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card;
+
+	lbs_dev_dbg(1, &cardp->udev->dev,"*** type = %u\n", type);
+	lbs_dev_dbg(1, &cardp->udev->dev,"size after = %d\n", nb);
+
+	if (type == MVMS_CMD) {
+		tmp = cpu_to_le32(CMD_TYPE_REQUEST);
+		priv->wlan_dev.dnld_sent = DNLD_CMD_SENT;
+		memcpy(cardp->bulk_out_buffer, (u8 *) & tmp,
+		       MESSAGE_HEADER_LEN);
+
+	} else {
+		tmp = cpu_to_le32(CMD_TYPE_DATA);
+		priv->wlan_dev.dnld_sent = DNLD_DATA_SENT;
+		memcpy(cardp->bulk_out_buffer, (u8 *) & tmp,
+		       MESSAGE_HEADER_LEN);
+	}
+
+	memcpy((cardp->bulk_out_buffer + MESSAGE_HEADER_LEN), payload, nb);
+
+	ret =
+	    usb_tx_block(priv, cardp->bulk_out_buffer, nb + MESSAGE_HEADER_LEN);
+
+	return ret;
+}
+
+/* called with adapter->driver_lock held */
+int libertas_sbi_get_int_status(wlan_private * priv, u8 * ireg)
+{
+	struct usb_card_rec *cardp = priv->wlan_dev.card;
+
+	*ireg = cardp->usb_int_cause;
+	cardp->usb_int_cause = 0;
+
+	lbs_dev_dbg(1, &cardp->udev->dev,"Int cause is 0x%X\n", *ireg);
+
+	return 0;
+}
+
+int libertas_sbi_read_event_cause(wlan_private * priv)
+{
+	struct usb_card_rec *cardp = priv->wlan_dev.card;
+	priv->adapter->eventcause = cardp->usb_event_cause;
+	/* Re-submit rx urb here to avoid event lost issue */
+	if_usb_submit_rx_urb(priv);
+	return 0;
+}
+
+int reset_device(wlan_private *priv)
+{
+	int ret;
+
+	ret = libertas_prepare_and_send_command(priv, cmd_802_11_reset,
+				    cmd_act_halt, 0, 0, NULL);
+	msleep_interruptible(10);
+
+	return ret;
+}
+
+int libertas_sbi_unregister_dev(wlan_private * priv)
+{
+	int ret = 0;
+
+	/* Need to send a Reset command to device before USB resources freed
+	 * and wlan_remove_card() called, then device can handle FW download
+	 * again.
+	 */
+	if (priv)
+		reset_device(priv);
+
+	return ret;
+}
+
+
+/**
+ *  @brief  This function register usb device and initialize parameter
+ *  @param		priv pointer to wlan_private
+ *  @return		0 or -1
+ */
+int libertas_sbi_register_dev(wlan_private * priv)
+{
+
+	struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card;
+	ENTER();
+
+	cardp->priv = priv;
+	cardp->eth_dev = priv->wlan_dev.netdev;
+	priv->hotplug_device = &(cardp->udev->dev);
+
+	SET_NETDEV_DEV(cardp->eth_dev, &(cardp->udev->dev));
+
+	lbs_dev_dbg(1, &cardp->udev->dev, "udev pointer is at %p\n",
+		    cardp->udev);
+
+	LEAVE();
+	return 0;
+}
+
+
+
+int libertas_sbi_prog_firmware(wlan_private * priv)
+{
+	struct usb_card_rec *cardp = priv->wlan_dev.card;
+	int i = 0;
+	static int reset_count = 10;
+
+	ENTER();
+
+	cardp->rinfo.priv = priv;
+
+restart:
+	if (if_usb_submit_rx_urb_fwload(priv) < 0) {
+		lbs_dev_dbg(1, &cardp->udev->dev, "URB submission is failed\n");
+		LEAVE();
+		return -1;
+	}
+
+#ifdef SUPPORT_BOOT_COMMAND
+	cardp->bootcmdresp = 0;
+	do {
+		int j = 0;
+		i++;
+		/* Issue Boot command = 1, Boot from Download-FW */
+		if_usb_issue_boot_command(priv, BOOT_CMD_FW_BY_USB);
+		/* wait for command response */
+		do {
+			j++;
+			msleep_interruptible(100);
+		} while (cardp->bootcmdresp == 0 && j < 10);
+	} while (cardp->bootcmdresp == 0 && i < 5);
+
+	if (cardp->bootcmdresp == 0) {
+		if (--reset_count >= 0) {
+			libertas_do_reset(priv);
+			goto restart;
+		}
+		return -1;
+	}
+#endif
+
+	i = 0;
+	priv->adapter->fw_ready = 0;
+
+	cardp->totalbytes = 0;
+	cardp->fwlastblksent = 0;
+	cardp->CRC_OK = 1;
+	cardp->fwdnldover = 0;
+	cardp->fwseqnum = -1;
+	cardp->totalbytes = 0;
+	cardp->fwfinalblk = 0;
+
+	if_prog_firmware(priv);
+
+	do {
+		lbs_dev_dbg(1, &cardp->udev->dev,"Wlan sched timeout\n");
+		i++;
+		msleep_interruptible(100);
+		if (priv->adapter->surpriseremoved || i >= 20)
+			break;
+	} while (!cardp->fwdnldover);
+
+	if (!cardp->fwdnldover) {
+		lbs_pr_info("failed to load fw, resetting device!\n");
+		if (--reset_count >= 0) {
+			libertas_do_reset(priv);
+			goto restart;
+		}
+
+		lbs_pr_info("FW download failure, time = %d ms\n", i * 100);
+		LEAVE();
+		return -1;
+	}
+
+	if_usb_submit_rx_urb(priv);
+
+	/* Delay 200 ms to waiting for the FW ready */
+	msleep_interruptible(200);
+
+	priv->adapter->fw_ready = 1;
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief Given a usb_card_rec return its wlan_private
+ *  @param card		pointer to a usb_card_rec
+ *  @return 	   	pointer to wlan_private
+ */
+wlan_private *libertas_sbi_get_priv(void *card)
+{
+	struct usb_card_rec *cardp = card;
+	return cardp->priv;
+}
+
+#ifdef ENABLE_PM
+int libertas_sbi_suspend(wlan_private * priv)
+{
+	return 0;
+}
+
+int libertas_sbi_resume(wlan_private * priv)
+{
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static int if_usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usb_card_rec *cardp = usb_get_intfdata(intf);
+	wlan_private *priv = cardp->priv;
+
+	ENTER();
+
+	if (priv->adapter->psstate != PS_STATE_FULL_POWER)
+		return -1;
+
+	netif_device_detach(cardp->eth_dev);
+
+	/* Unlink tx & rx urb */
+	usb_kill_urb(cardp->tx_urb);
+	usb_kill_urb(cardp->rx_urb);
+
+	cardp->rx_urb_recall = 1;
+
+	LEAVE();
+	return 0;
+}
+
+static int if_usb_resume(struct usb_interface *intf)
+{
+	struct usb_card_rec *cardp = usb_get_intfdata(intf);
+
+	ENTER();
+
+	cardp->rx_urb_recall = 0;
+
+	if_usb_submit_rx_urb(cardp->priv);
+
+	netif_device_attach(cardp->eth_dev);
+
+	LEAVE();
+	return 0;
+}
+#else
+#define if_usb_suspend NULL
+#define if_usb_resume NULL
+#endif
+
+static struct usb_driver if_usb_driver = {
+	/* driver name */
+	.name = usbdriver_name,
+	/* probe function name */
+	.probe = if_usb_probe,
+	/* disconnect function  name */
+	.disconnect = if_usb_disconnect,
+	/* device signature table */
+	.id_table = if_usb_table,
+	.suspend = if_usb_suspend,
+	.resume = if_usb_resume,
+};
+
+/**
+ *  @brief This function registers driver.
+ *  @param add		pointer to add_card callback function
+ *  @param remove	pointer to remove card callback function
+ *  @param arg		pointer to call back function parameter
+ *  @return 	   	dummy success variable
+ */
+int libertas_sbi_register(void)
+{
+	/*
+	 * API registers the Marvell USB driver
+	 * to the USB system
+	 */
+	usb_register(&if_usb_driver);
+
+	/* Return success to wlan layer */
+	return 0;
+}
+
+/**
+ *  @brief This function removes usb driver.
+ *  @return 	   	N/A
+ */
+void libertas_sbi_unregister(void)
+{
+	/* API unregisters the driver from USB subsystem */
+	usb_deregister(&if_usb_driver);
+	return;
+}
diff --git a/drivers/net/wireless/libertas/if_usb.h b/drivers/net/wireless/libertas/if_usb.h
new file mode 100644
index 0000000..7851167
--- /dev/null
+++ b/drivers/net/wireless/libertas/if_usb.h
@@ -0,0 +1,109 @@
+/**
+  * This file contains definition for USB interface.
+  */
+#define CMD_TYPE_REQUEST                0xF00DFACE
+#define CMD_TYPE_DATA                   0xBEADC0DE
+#define CMD_TYPE_INDICATION             0xBEEFFACE
+
+#define IPFIELD_ALIGN_OFFSET	2
+
+#define USB8388_VID_1	0x1286
+#define USB8388_PID_1	0x2001
+#define USB8388_VID_2	0x05a3
+#define USB8388_PID_2	0x8388
+
+#ifdef SUPPORT_BOOT_COMMAND
+#define BOOT_CMD_FW_BY_USB     0x01
+#define BOOT_CMD_FW_IN_EEPROM  0x02
+#define BOOT_CMD_UPDATE_BOOT2  0x03
+#define BOOT_CMD_UPDATE_FW     0x04
+#define BOOT_CMD_MAGIC_NUMBER  0x4C56524D   /* M=>0x4D,R=>0x52,V=>0x56,L=>0x4C */
+
+struct bootcmdstr
+{
+	u32 u32magicnumber;
+	u8  u8cmd_tag;
+	u8  au8dumy[11];
+};
+
+#define BOOT_CMD_RESP_OK     0x0001
+#define BOOT_CMD_RESP_FAIL   0x0000
+
+struct bootcmdrespStr
+{
+	u32 u32magicnumber;
+	u8  u8cmd_tag;
+	u8  u8result;
+	u8  au8dumy[2];
+};
+#endif /* SUPPORT_BOOT_COMMAND */
+
+/* read callback private data */
+struct read_cb_info {
+        wlan_private *priv;
+        struct sk_buff *skb;
+};
+
+/** USB card description structure*/
+struct usb_card_rec {
+	struct net_device *eth_dev;
+	struct usb_device *udev;
+	struct urb *rx_urb, *tx_urb;
+	void *priv;
+	struct read_cb_info rinfo;
+
+	int bulk_in_size;
+	u8 bulk_in_endpointAddr;
+
+	u8 *bulk_out_buffer;
+	int bulk_out_size;
+	u8 bulk_out_endpointAddr;
+
+	u8 CRC_OK;
+	u32 fwseqnum;
+	u32 lastseqnum;
+	u32 totalbytes;
+	u32 fwlastblksent;
+	u8 fwdnldover;
+	u8 fwfinalblk;
+
+	u32 usb_event_cause;
+	u8 usb_int_cause;
+
+	u8 rx_urb_recall;
+
+	u8 bootcmdresp;
+};
+
+/** fwheader */
+struct fwheader {
+	u32 dnldcmd;
+	u32 baseaddr;
+	u32 datalength;
+	u32 CRC;
+};
+
+#define FW_MAX_DATA_BLK_SIZE	600
+/** FWData */
+struct FWData {
+	struct fwheader fwheader;
+	u32 seqnum;
+	u8 data[FW_MAX_DATA_BLK_SIZE];
+};
+
+/** fwsyncheader */
+struct fwsyncheader {
+	u32 cmd;
+	u32 seqnum;
+};
+
+#define FW_HAS_DATA_TO_RECV		0x00000001
+#define FW_HAS_LAST_BLOCK		0x00000004
+
+#define FW_DATA_XMIT_SIZE \
+	sizeof(struct fwheader) + fwdata->fwheader.datalength + sizeof(u32)
+
+int usb_tx_block(wlan_private *priv, u8 *payload, u16 nb);
+void if_usb_free(struct usb_card_rec *cardp);
+int if_usb_issue_boot_command(wlan_private *priv, int ivalue);
+
diff --git a/drivers/net/wireless/libertas/ioctl.c b/drivers/net/wireless/libertas/ioctl.c
new file mode 100644
index 0000000..82b3964
--- /dev/null
+++ b/drivers/net/wireless/libertas/ioctl.c
@@ -0,0 +1,2500 @@
+/**
+  * This file contains ioctl functions
+  */
+
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+
+#include <net/iw_handler.h>
+#include <net/ieee80211.h>
+
+#include "host.h"
+#include "radiotap.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "join.h"
+#include "wext.h"
+
+#define MAX_SCAN_CELL_SIZE      (IW_EV_ADDR_LEN + \
+				IW_ESSID_MAX_SIZE + \
+				IW_EV_UINT_LEN + IW_EV_FREQ_LEN + \
+				IW_EV_QUAL_LEN + IW_ESSID_MAX_SIZE + \
+				IW_EV_PARAM_LEN + 40)	/* 40 for WPAIE */
+
+#define WAIT_FOR_SCAN_RRESULT_MAX_TIME (10 * HZ)
+
+static int setrxantenna(wlan_private * priv, int mode)
+{
+	int ret = 0;
+	wlan_adapter *adapter = priv->adapter;
+
+	if (mode != RF_ANTENNA_1 && mode != RF_ANTENNA_2
+	    && mode != RF_ANTENNA_AUTO) {
+		return -EINVAL;
+	}
+
+	adapter->rxantennamode = mode;
+
+	lbs_pr_debug(1, "SET RX Antenna mode to 0x%04x\n", adapter->rxantennamode);
+
+	ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna,
+				    cmd_act_set_rx,
+				    cmd_option_waitforrsp, 0,
+				    &adapter->rxantennamode);
+	return ret;
+}
+
+static int settxantenna(wlan_private * priv, int mode)
+{
+	int ret = 0;
+	wlan_adapter *adapter = priv->adapter;
+
+	if ((mode != RF_ANTENNA_1) && (mode != RF_ANTENNA_2)
+	    && (mode != RF_ANTENNA_AUTO)) {
+		return -EINVAL;
+	}
+
+	adapter->txantennamode = mode;
+
+	lbs_pr_debug(1, "SET TX Antenna mode to 0x%04x\n", adapter->txantennamode);
+
+	ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna,
+				    cmd_act_set_tx,
+				    cmd_option_waitforrsp, 0,
+				    &adapter->txantennamode);
+
+	return ret;
+}
+
+static int getrxantenna(wlan_private * priv, char *buf)
+{
+	int ret = 0;
+	wlan_adapter *adapter = priv->adapter;
+
+	// clear it, so we will know if the value
+	// returned below is correct or not.
+	adapter->rxantennamode = 0;
+
+	ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna,
+				    cmd_act_get_rx,
+				    cmd_option_waitforrsp, 0, NULL);
+
+	if (ret) {
+		LEAVE();
+		return ret;
+	}
+
+	lbs_pr_debug(1, "Get Rx Antenna mode:0x%04x\n", adapter->rxantennamode);
+
+	return sprintf(buf, "0x%04x", adapter->rxantennamode) + 1;
+}
+
+static int gettxantenna(wlan_private * priv, char *buf)
+{
+	int ret = 0;
+	wlan_adapter *adapter = priv->adapter;
+
+	// clear it, so we will know if the value
+	// returned below is correct or not.
+	adapter->txantennamode = 0;
+
+	ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna,
+				    cmd_act_get_tx,
+				    cmd_option_waitforrsp, 0, NULL);
+
+	if (ret) {
+		LEAVE();
+		return ret;
+	}
+
+	lbs_pr_debug(1, "Get Tx Antenna mode:0x%04x\n", adapter->txantennamode);
+
+	return sprintf(buf, "0x%04x", adapter->txantennamode) + 1;
+}
+
+static int wlan_set_region(wlan_private * priv, u16 region_code)
+{
+	int i;
+
+	for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) {
+		// use the region code to search for the index
+		if (region_code == libertas_region_code_to_index[i]) {
+			priv->adapter->regiontableindex = (u16) i;
+			priv->adapter->regioncode = region_code;
+			break;
+		}
+	}
+
+	// if it's unidentified region code
+	if (i >= MRVDRV_MAX_REGION_CODE) {
+		lbs_pr_debug(1, "region Code not identified\n");
+		LEAVE();
+		return -1;
+	}
+
+	if (libertas_set_regiontable(priv, priv->adapter->regioncode, 0)) {
+		LEAVE();
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ *  @brief Get/Set Firmware wakeup method
+ *
+ *  @param priv		A pointer to wlan_private structure
+ *  @param wrq	   	A pointer to user data
+ *  @return 	   	0--success, otherwise fail
+ */
+static int wlan_txcontrol(wlan_private * priv, struct iwreq *wrq)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int data;
+	ENTER();
+
+	if ((int)wrq->u.data.length == 0) {
+		if (copy_to_user
+		    (wrq->u.data.pointer, &adapter->pkttxctrl, sizeof(u32))) {
+			lbs_pr_alert("copy_to_user failed!\n");
+			return -EFAULT;
+		}
+	} else {
+		if ((int)wrq->u.data.length > 1) {
+			lbs_pr_alert("ioctl too many args!\n");
+			return -EFAULT;
+		}
+		if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
+			lbs_pr_alert("Copy from user failed\n");
+			return -EFAULT;
+		}
+
+		adapter->pkttxctrl = (u32) data;
+	}
+
+	wrq->u.data.length = 1;
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief Get/Set NULL Package generation interval
+ *
+ *  @param priv		A pointer to wlan_private structure
+ *  @param wrq	   	A pointer to user data
+ *  @return 	   	0--success, otherwise fail
+ */
+static int wlan_null_pkt_interval(wlan_private * priv, struct iwreq *wrq)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int data;
+	ENTER();
+
+	if ((int)wrq->u.data.length == 0) {
+		data = adapter->nullpktinterval;
+
+		if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) {
+			lbs_pr_alert( "copy_to_user failed!\n");
+			return -EFAULT;
+		}
+	} else {
+		if ((int)wrq->u.data.length > 1) {
+			lbs_pr_alert( "ioctl too many args!\n");
+			return -EFAULT;
+		}
+		if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
+			lbs_pr_debug(1, "Copy from user failed\n");
+			return -EFAULT;
+		}
+
+		adapter->nullpktinterval = data;
+	}
+
+	wrq->u.data.length = 1;
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_get_rxinfo(wlan_private * priv, struct iwreq *wrq)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int data[2];
+	ENTER();
+	data[0] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG];
+	data[1] = adapter->rxpd_rate;
+	if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 2)) {
+		lbs_pr_debug(1, "Copy to user failed\n");
+		return -EFAULT;
+	}
+	wrq->u.data.length = 2;
+	LEAVE();
+	return 0;
+}
+
+static int wlan_get_snr(wlan_private * priv, struct iwreq *wrq)
+{
+	int ret = 0;
+	wlan_adapter *adapter = priv->adapter;
+	int data[4];
+
+	ENTER();
+	memset(data, 0, sizeof(data));
+	if (wrq->u.data.length) {
+		if (copy_from_user(data, wrq->u.data.pointer,
+		     min_t(size_t, wrq->u.data.length, 4) * sizeof(int)))
+			return -EFAULT;
+	}
+	if ((wrq->u.data.length == 0) || (data[0] == 0) || (data[0] == 1)) {
+		if (adapter->connect_status == libertas_connected) {
+			ret = libertas_prepare_and_send_command(priv,
+						    cmd_802_11_rssi,
+						    0,
+						    cmd_option_waitforrsp,
+						    0, NULL);
+
+			if (ret) {
+				LEAVE();
+				return ret;
+			}
+		}
+	}
+
+	if (wrq->u.data.length == 0) {
+		data[0] = adapter->SNR[TYPE_BEACON][TYPE_NOAVG];
+		data[1] = adapter->SNR[TYPE_BEACON][TYPE_AVG];
+		data[2] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG];
+		data[3] = adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE;
+		if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 4))
+			return -EFAULT;
+		wrq->u.data.length = 4;
+	} else if (data[0] == 0) {
+		data[0] = adapter->SNR[TYPE_BEACON][TYPE_NOAVG];
+		if (copy_to_user(wrq->u.data.pointer, data, sizeof(int)))
+			return -EFAULT;
+		wrq->u.data.length = 1;
+	} else if (data[0] == 1) {
+		data[0] = adapter->SNR[TYPE_BEACON][TYPE_AVG];
+		if (copy_to_user(wrq->u.data.pointer, data, sizeof(int)))
+			return -EFAULT;
+		wrq->u.data.length = 1;
+	} else if (data[0] == 2) {
+		data[0] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG];
+		if (copy_to_user(wrq->u.data.pointer, data, sizeof(int)))
+			return -EFAULT;
+		wrq->u.data.length = 1;
+	} else if (data[0] == 3) {
+		data[0] = adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE;
+		if (copy_to_user(wrq->u.data.pointer, data, sizeof(int)))
+			return -EFAULT;
+		wrq->u.data.length = 1;
+	} else
+		return -ENOTSUPP;
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_beacon_interval(wlan_private * priv, struct iwreq *wrq)
+{
+	int data;
+	wlan_adapter *adapter = priv->adapter;
+
+	if (wrq->u.data.length > 0) {
+		if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int)))
+			return -EFAULT;
+
+		lbs_pr_debug(1, "WLAN SET BEACON INTERVAL: %d\n", data);
+		if ((data > MRVDRV_MAX_BEACON_INTERVAL)
+		    || (data < MRVDRV_MIN_BEACON_INTERVAL))
+			return -ENOTSUPP;
+		adapter->beaconperiod = data;
+	}
+	data = adapter->beaconperiod;
+	if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int)))
+		return -EFAULT;
+
+	wrq->u.data.length = 1;
+
+	return 0;
+}
+
+static int wlan_get_rssi(wlan_private * priv, struct iwreq *wrq)
+{
+	int ret = 0;
+	wlan_adapter *adapter = priv->adapter;
+	int temp;
+	int data = 0;
+	int *val;
+
+	ENTER();
+	data = SUBCMD_DATA(wrq);
+	if ((data == 0) || (data == 1)) {
+		ret = libertas_prepare_and_send_command(priv,
+					    cmd_802_11_rssi,
+					    0, cmd_option_waitforrsp,
+					    0, NULL);
+		if (ret) {
+			LEAVE();
+			return ret;
+		}
+	}
+
+	switch (data) {
+	case 0:
+
+		temp = CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG],
+				adapter->NF[TYPE_BEACON][TYPE_NOAVG]);
+		break;
+	case 1:
+		temp = CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_AVG],
+				adapter->NF[TYPE_BEACON][TYPE_AVG]);
+		break;
+	case 2:
+		temp = CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_NOAVG],
+				adapter->NF[TYPE_RXPD][TYPE_NOAVG]);
+		break;
+	case 3:
+		temp = CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE,
+				adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE);
+		break;
+	default:
+		return -ENOTSUPP;
+	}
+	val = (int *)wrq->u.name;
+	*val = temp;
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_get_nf(wlan_private * priv, struct iwreq *wrq)
+{
+	int ret = 0;
+	wlan_adapter *adapter = priv->adapter;
+	int temp;
+	int data = 0;
+	int *val;
+
+	data = SUBCMD_DATA(wrq);
+	if ((data == 0) || (data == 1)) {
+		ret = libertas_prepare_and_send_command(priv,
+					    cmd_802_11_rssi,
+					    0, cmd_option_waitforrsp,
+					    0, NULL);
+
+		if (ret) {
+			LEAVE();
+			return ret;
+		}
+	}
+
+	switch (data) {
+	case 0:
+		temp = adapter->NF[TYPE_BEACON][TYPE_NOAVG];
+		break;
+	case 1:
+		temp = adapter->NF[TYPE_BEACON][TYPE_AVG];
+		break;
+	case 2:
+		temp = adapter->NF[TYPE_RXPD][TYPE_NOAVG];
+		break;
+	case 3:
+		temp = adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE;
+		break;
+	default:
+		return -ENOTSUPP;
+	}
+
+	temp = CAL_NF(temp);
+
+	lbs_pr_debug(1, "%s: temp = %d\n", __FUNCTION__, temp);
+	val = (int *)wrq->u.name;
+	*val = temp;
+	return 0;
+}
+
+static int wlan_get_txrate_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int *pdata;
+	struct iwreq *wrq = (struct iwreq *)req;
+	int ret = 0;
+	adapter->txrate = 0;
+	lbs_pr_debug(1, "wlan_get_txrate_ioctl\n");
+	ret = libertas_prepare_and_send_command(priv, cmd_802_11_tx_rate_query,
+				    cmd_act_get, cmd_option_waitforrsp,
+				    0, NULL);
+	if (ret)
+		return ret;
+
+	pdata = (int *)wrq->u.name;
+	*pdata = (int)adapter->txrate;
+	return 0;
+}
+
+static int wlan_get_adhoc_status_ioctl(wlan_private * priv, struct iwreq *wrq)
+{
+	char status[64];
+	wlan_adapter *adapter = priv->adapter;
+
+	memset(status, 0, sizeof(status));
+
+	switch (adapter->inframode) {
+	case wlan802_11ibss:
+		if (adapter->connect_status == libertas_connected) {
+			if (adapter->adhoccreate)
+				memcpy(&status, "AdhocStarted", sizeof(status));
+			else
+				memcpy(&status, "AdhocJoined", sizeof(status));
+		} else {
+			memcpy(&status, "AdhocIdle", sizeof(status));
+		}
+		break;
+	case wlan802_11infrastructure:
+		memcpy(&status, "Inframode", sizeof(status));
+		break;
+	default:
+		memcpy(&status, "AutoUnknownmode", sizeof(status));
+		break;
+	}
+
+	lbs_pr_debug(1, "status = %s\n", status);
+	wrq->u.data.length = strlen(status) + 1;
+
+	if (wrq->u.data.pointer) {
+		if (copy_to_user(wrq->u.data.pointer,
+				 &status, wrq->u.data.length))
+			return -EFAULT;
+	}
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief Set/Get WPA IE
+ *  @param priv                 A pointer to wlan_private structure
+ *  @param req			A pointer to ifreq structure
+ *  @return 	   		0 --success, otherwise fail
+ */
+static int wlan_setwpaie_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	struct iwreq *wrq = (struct iwreq *)req;
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+
+	ENTER();
+
+	if (wrq->u.data.length) {
+		if (wrq->u.data.length > sizeof(adapter->wpa_ie)) {
+			lbs_pr_debug(1, "failed to copy WPA IE, too big \n");
+			return -EFAULT;
+		}
+		if (copy_from_user(adapter->wpa_ie, wrq->u.data.pointer,
+				   wrq->u.data.length)) {
+			lbs_pr_debug(1, "failed to copy WPA IE \n");
+			return -EFAULT;
+		}
+		adapter->wpa_ie_len = wrq->u.data.length;
+		lbs_pr_debug(1, "Set wpa_ie_len=%d IE=%#x\n", adapter->wpa_ie_len,
+		       adapter->wpa_ie[0]);
+		lbs_dbg_hex("wpa_ie", adapter->wpa_ie, adapter->wpa_ie_len);
+		if (adapter->wpa_ie[0] == WPA_IE)
+			adapter->secinfo.WPAenabled = 1;
+		else if (adapter->wpa_ie[0] == WPA2_IE)
+			adapter->secinfo.WPA2enabled = 1;
+		else {
+			adapter->secinfo.WPAenabled = 0;
+			adapter->secinfo.WPA2enabled = 0;
+		}
+	} else {
+		memset(adapter->wpa_ie, 0, sizeof(adapter->wpa_ie));
+		adapter->wpa_ie_len = wrq->u.data.length;
+		lbs_pr_debug(1, "Reset wpa_ie_len=%d IE=%#x\n",
+		       adapter->wpa_ie_len, adapter->wpa_ie[0]);
+		adapter->secinfo.WPAenabled = 0;
+		adapter->secinfo.WPA2enabled = 0;
+	}
+
+	// enable/disable RSN in firmware if WPA is enabled/disabled
+	// depending on variable adapter->secinfo.WPAenabled is set or not
+	ret = libertas_prepare_and_send_command(priv, cmd_802_11_enable_rsn,
+				    cmd_act_set, cmd_option_waitforrsp,
+				    0, NULL);
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief Set Auto prescan
+ *  @param priv                 A pointer to wlan_private structure
+ *  @param wrq			A pointer to iwreq structure
+ *  @return 	   		0 --success, otherwise fail
+ */
+static int wlan_subcmd_setprescan_ioctl(wlan_private * priv, struct iwreq *wrq)
+{
+	int data;
+	wlan_adapter *adapter = priv->adapter;
+	int *val;
+
+	data = SUBCMD_DATA(wrq);
+	lbs_pr_debug(1, "WLAN_SUBCMD_SET_PRESCAN %d\n", data);
+	adapter->prescan = data;
+
+	val = (int *)wrq->u.name;
+	*val = data;
+	return 0;
+}
+
+static int wlan_set_multiple_dtim_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	struct iwreq *wrq = (struct iwreq *)req;
+	u32 mdtim;
+	int idata;
+	int ret = -EINVAL;
+
+	ENTER();
+
+	idata = SUBCMD_DATA(wrq);
+	mdtim = (u32) idata;
+	if (((mdtim >= MRVDRV_MIN_MULTIPLE_DTIM)
+	     && (mdtim <= MRVDRV_MAX_MULTIPLE_DTIM))
+	    || (mdtim == MRVDRV_IGNORE_MULTIPLE_DTIM)) {
+		priv->adapter->multipledtim = mdtim;
+		ret = 0;
+	}
+	if (ret)
+		lbs_pr_debug(1, "Invalid parameter, multipledtim not changed.\n");
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief Set authentication mode
+ *  @param priv                 A pointer to wlan_private structure
+ *  @param req			A pointer to ifreq structure
+ *  @return 	   		0 --success, otherwise fail
+ */
+static int wlan_setauthalg_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	int alg;
+	struct iwreq *wrq = (struct iwreq *)req;
+	wlan_adapter *adapter = priv->adapter;
+
+	if (wrq->u.data.flags == 0) {
+		//from iwpriv subcmd
+		alg = SUBCMD_DATA(wrq);
+	} else {
+		//from wpa_supplicant subcmd
+		if (copy_from_user(&alg, wrq->u.data.pointer, sizeof(alg))) {
+			lbs_pr_debug(1, "Copy from user failed\n");
+			return -EFAULT;
+		}
+	}
+
+	lbs_pr_debug(1, "auth alg is %#x\n", alg);
+
+	switch (alg) {
+	case AUTH_ALG_SHARED_KEY:
+		adapter->secinfo.authmode = wlan802_11authmodeshared;
+		break;
+	case AUTH_ALG_NETWORK_EAP:
+		adapter->secinfo.authmode =
+		    wlan802_11authmodenetworkEAP;
+		break;
+	case AUTH_ALG_OPEN_SYSTEM:
+	default:
+		adapter->secinfo.authmode = wlan802_11authmodeopen;
+		break;
+	}
+	return 0;
+}
+
+/**
+ *  @brief Set 802.1x authentication mode
+ *  @param priv                 A pointer to wlan_private structure
+ *  @param req			A pointer to ifreq structure
+ *  @return 	   		0 --success, otherwise fail
+ */
+static int wlan_set8021xauthalg_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	int alg;
+	struct iwreq *wrq = (struct iwreq *)req;
+
+	if (wrq->u.data.flags == 0) {
+		//from iwpriv subcmd
+		alg = SUBCMD_DATA(wrq);
+	} else {
+		//from wpa_supplicant subcmd
+		if (copy_from_user(&alg, wrq->u.data.pointer, sizeof(int))) {
+			lbs_pr_debug(1, "Copy from user failed\n");
+			return -EFAULT;
+		}
+	}
+	lbs_pr_debug(1, "802.1x auth alg is %#x\n", alg);
+	priv->adapter->secinfo.auth1xalg = alg;
+	return 0;
+}
+
+static int wlan_setencryptionmode_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	int mode;
+	struct iwreq *wrq = (struct iwreq *)req;
+
+	ENTER();
+
+	if (wrq->u.data.flags == 0) {
+		//from iwpriv subcmd
+		mode = SUBCMD_DATA(wrq);
+	} else {
+		//from wpa_supplicant subcmd
+		if (copy_from_user(&mode, wrq->u.data.pointer, sizeof(int))) {
+			lbs_pr_debug(1, "Copy from user failed\n");
+			return -EFAULT;
+		}
+	}
+	lbs_pr_debug(1, "encryption mode is %#x\n", mode);
+	priv->adapter->secinfo.Encryptionmode = mode;
+
+	LEAVE();
+	return 0;
+}
+
+static void adjust_mtu(wlan_private * priv)
+{
+	int mtu_increment = 0;
+
+	if (priv->adapter->linkmode == WLAN_LINKMODE_802_11)
+		mtu_increment += sizeof(struct ieee80211_hdr_4addr);
+
+	if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP)
+		mtu_increment += max(sizeof(struct tx_radiotap_hdr),
+				     sizeof(struct rx_radiotap_hdr));
+	priv->wlan_dev.netdev->mtu = ETH_FRAME_LEN
+	    - sizeof(struct ethhdr)
+	    + mtu_increment;
+}
+
+/**
+ *  @brief Set Link-Layer Layer mode
+ *  @param priv                 A pointer to wlan_private structure
+ *  @param req			A pointer to ifreq structure
+ *  @return 	   		0 --success, otherwise fail
+ */
+static int wlan_set_linkmode_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	int mode;
+
+	mode = (int)((struct ifreq *)((u8 *) req + 4))->ifr_data;
+
+	switch (mode) {
+	case WLAN_LINKMODE_802_3:
+		priv->adapter->linkmode = mode;
+		break;
+	case WLAN_LINKMODE_802_11:
+		priv->adapter->linkmode = mode;
+		break;
+	default:
+		lbs_pr_info("usb8388-5: invalid link-layer mode (%#x)\n",
+		       mode);
+		return -EINVAL;
+		break;
+	}
+	lbs_pr_debug(1, "usb8388-5: link-layer mode is %#x\n", mode);
+
+	adjust_mtu(priv);
+
+	return 0;
+}
+
+/**
+ *  @brief Set Radio header mode
+ *  @param priv                 A pointer to wlan_private structure
+ *  @param req			A pointer to ifreq structure
+ *  @return 	   		0 --success, otherwise fail
+ */
+static int wlan_set_radiomode_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	int mode;
+
+	mode = (int)((struct ifreq *)((u8 *) req + 4))->ifr_data;
+
+	switch (mode) {
+	case WLAN_RADIOMODE_NONE:
+		priv->adapter->radiomode = mode;
+		break;
+	case WLAN_RADIOMODE_RADIOTAP:
+		priv->adapter->radiomode = mode;
+		break;
+	default:
+		lbs_pr_debug(1, "usb8388-5: invalid radio header mode (%#x)\n",
+		       mode);
+		return -EINVAL;
+	}
+	lbs_pr_debug(1, "usb8388-5: radio-header mode is %#x\n", mode);
+
+	adjust_mtu(priv);
+	return 0;
+}
+
+/**
+ *  @brief Set Debug header mode
+ *  @param priv                 A pointer to wlan_private structure
+ *  @param req			A pointer to ifreq structure
+ *  @return 	   		0 --success, otherwise fail
+ */
+static int wlan_set_debugmode_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	priv->adapter->debugmode = (int)((struct ifreq *)
+					 ((u8 *) req + 4))->ifr_data;
+	return 0;
+}
+
+static int wlan_subcmd_getrxantenna_ioctl(wlan_private * priv,
+					  struct ifreq *req)
+{
+	int len;
+	char buf[8];
+	struct iwreq *wrq = (struct iwreq *)req;
+
+	lbs_pr_debug(1, "WLAN_SUBCMD_GETRXANTENNA\n");
+	len = getrxantenna(priv, buf);
+
+	wrq->u.data.length = len;
+	if (wrq->u.data.pointer) {
+		if (copy_to_user(wrq->u.data.pointer, &buf, len)) {
+			lbs_pr_debug(1, "CopyToUser failed\n");
+			return -EFAULT;
+		}
+	}
+
+	return 0;
+}
+
+static int wlan_subcmd_gettxantenna_ioctl(wlan_private * priv,
+					  struct ifreq *req)
+{
+	int len;
+	char buf[8];
+	struct iwreq *wrq = (struct iwreq *)req;
+
+	lbs_pr_debug(1, "WLAN_SUBCMD_GETTXANTENNA\n");
+	len = gettxantenna(priv, buf);
+
+	wrq->u.data.length = len;
+	if (wrq->u.data.pointer) {
+		if (copy_to_user(wrq->u.data.pointer, &buf, len)) {
+			lbs_pr_debug(1, "CopyToUser failed\n");
+			return -EFAULT;
+		}
+	}
+	return 0;
+}
+
+/**
+ *  @brief Get the MAC TSF value from the firmware
+ *
+ *  @param priv         A pointer to wlan_private structure
+ *  @param wrq          A pointer to iwreq structure containing buffer
+ *                      space to store a TSF value retrieved from the firmware
+ *
+ *  @return             0 if successful; IOCTL error code otherwise
+ */
+static int wlan_get_tsf_ioctl(wlan_private * priv, struct iwreq *wrq)
+{
+	u64 tsfval;
+	int ret;
+
+	ret = libertas_prepare_and_send_command(priv,
+				    cmd_get_tsf,
+				    0, cmd_option_waitforrsp, 0, &tsfval);
+
+	lbs_pr_debug(1, "IOCTL: Get TSF = 0x%016llx\n", tsfval);
+
+	if (ret != 0) {
+		lbs_pr_debug(1, "IOCTL: Get TSF; command exec failed\n");
+		ret = -EFAULT;
+	} else {
+		if (copy_to_user(wrq->u.data.pointer,
+				 &tsfval,
+				 min_t(size_t, wrq->u.data.length,
+				     sizeof(tsfval))) != 0) {
+
+			lbs_pr_debug(1, "IOCTL: Get TSF; Copy to user failed\n");
+			ret = -EFAULT;
+		} else {
+			ret = 0;
+		}
+	}
+	return ret;
+}
+
+/**
+ *  @brief Get/Set adapt rate
+ *  @param priv                 A pointer to wlan_private structure
+ *  @param wrq			A pointer to iwreq structure
+ *  @return 	   		0 --success, otherwise fail
+ */
+static int wlan_adapt_rateset(wlan_private * priv, struct iwreq *wrq)
+{
+	int ret;
+	wlan_adapter *adapter = priv->adapter;
+	int data[2];
+
+	memset(data, 0, sizeof(data));
+	if (!wrq->u.data.length) {
+		lbs_pr_debug(1, "Get ADAPT RATE SET\n");
+		ret = libertas_prepare_and_send_command(priv,
+					    cmd_802_11_rate_adapt_rateset,
+					    cmd_act_get,
+					    cmd_option_waitforrsp, 0, NULL);
+		data[0] = adapter->enablehwauto;
+		data[1] = adapter->ratebitmap;
+		if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 2)) {
+			lbs_pr_debug(1, "Copy to user failed\n");
+			return -EFAULT;
+		}
+#define GET_TWO_INT	2
+		wrq->u.data.length = GET_TWO_INT;
+	} else {
+		lbs_pr_debug(1, "Set ADAPT RATE SET\n");
+		if (wrq->u.data.length > 2)
+			return -EINVAL;
+		if (copy_from_user
+		    (data, wrq->u.data.pointer,
+		     sizeof(int) * wrq->u.data.length)) {
+			lbs_pr_debug(1, "Copy from user failed\n");
+			return -EFAULT;
+		}
+
+		adapter->enablehwauto = data[0];
+		adapter->ratebitmap = data[1];
+		ret = libertas_prepare_and_send_command(priv,
+					    cmd_802_11_rate_adapt_rateset,
+					    cmd_act_set,
+					    cmd_option_waitforrsp, 0, NULL);
+	}
+	return ret;
+}
+
+/**
+ *  @brief Get/Set inactivity timeout
+ *  @param priv                 A pointer to wlan_private structure
+ *  @param wrq			A pointer to iwreq structure
+ *  @return 	   		0 --success, otherwise fail
+ */
+static int wlan_inactivity_timeout(wlan_private * priv, struct iwreq *wrq)
+{
+	int ret;
+	int data = 0;
+	u16 timeout = 0;
+
+	ENTER();
+	if (wrq->u.data.length > 1)
+		return -ENOTSUPP;
+
+	if (wrq->u.data.length == 0) {
+		/* Get */
+		ret = libertas_prepare_and_send_command(priv,
+					    cmd_802_11_inactivity_timeout,
+					    cmd_act_get,
+					    cmd_option_waitforrsp, 0,
+					    &timeout);
+		data = timeout;
+		if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) {
+			lbs_pr_debug(1, "Copy to user failed\n");
+			return -EFAULT;
+		}
+	} else {
+		/* Set */
+		if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
+			lbs_pr_debug(1, "Copy from user failed\n");
+			return -EFAULT;
+		}
+
+		timeout = data;
+		ret = libertas_prepare_and_send_command(priv,
+					    cmd_802_11_inactivity_timeout,
+					    cmd_act_set,
+					    cmd_option_waitforrsp, 0,
+					    &timeout);
+	}
+
+	wrq->u.data.length = 1;
+
+	LEAVE();
+	return ret;
+}
+
+static int wlan_do_getlog_ioctl(wlan_private * priv, struct iwreq *wrq)
+{
+	int ret;
+	char buf[GETLOG_BUFSIZE - 1];
+	wlan_adapter *adapter = priv->adapter;
+
+	lbs_pr_debug(1, " GET STATS\n");
+
+	ret = libertas_prepare_and_send_command(priv, cmd_802_11_get_log,
+				    0, cmd_option_waitforrsp, 0, NULL);
+
+	if (ret) {
+		return ret;
+	}
+
+	if (wrq->u.data.pointer) {
+		sprintf(buf, "\n  mcasttxframe %u failed %u retry %u "
+			"multiretry %u framedup %u "
+			"rtssuccess %u rtsfailure %u ackfailure %u\n"
+			"rxfrag %u mcastrxframe %u fcserror %u "
+			"txframe %u wepundecryptable %u ",
+			adapter->logmsg.mcasttxframe,
+			adapter->logmsg.failed,
+			adapter->logmsg.retry,
+			adapter->logmsg.multiretry,
+			adapter->logmsg.framedup,
+			adapter->logmsg.rtssuccess,
+			adapter->logmsg.rtsfailure,
+			adapter->logmsg.ackfailure,
+			adapter->logmsg.rxfrag,
+			adapter->logmsg.mcastrxframe,
+			adapter->logmsg.fcserror,
+			adapter->logmsg.txframe,
+			adapter->logmsg.wepundecryptable);
+		wrq->u.data.length = strlen(buf) + 1;
+		if (copy_to_user(wrq->u.data.pointer, buf, wrq->u.data.length)) {
+			lbs_pr_debug(1, "Copy to user failed\n");
+			return -EFAULT;
+		}
+	}
+
+	return 0;
+}
+
+static int wlan_scan_type_ioctl(wlan_private * priv, struct iwreq *wrq)
+{
+	u8 buf[12];
+	u8 *option[] = { "active", "passive", "get", };
+	int i, max_options = (sizeof(option) / sizeof(option[0]));
+	int ret = 0;
+	wlan_adapter *adapter = priv->adapter;
+
+	if (priv->adapter->enable11d) {
+		lbs_pr_debug(1, "11D: Cannot set scantype when 11D enabled\n");
+		return -EFAULT;
+	}
+
+	memset(buf, 0, sizeof(buf));
+
+	if (copy_from_user(buf, wrq->u.data.pointer, min_t(size_t, sizeof(buf),
+							 wrq->u.data.length)))
+		return -EFAULT;
+
+	lbs_pr_debug(1, "Scan type Option = %s\n", buf);
+
+	buf[sizeof(buf) - 1] = '\0';
+
+	for (i = 0; i < max_options; i++) {
+		if (!strcmp(buf, option[i]))
+			break;
+	}
+
+	switch (i) {
+	case 0:
+		adapter->scantype = cmd_scan_type_active;
+		break;
+	case 1:
+		adapter->scantype = cmd_scan_type_passive;
+		break;
+	case 2:
+		wrq->u.data.length = strlen(option[adapter->scantype]) + 1;
+
+		if (copy_to_user(wrq->u.data.pointer,
+				 option[adapter->scantype],
+				 wrq->u.data.length)) {
+			lbs_pr_debug(1, "Copy to user failed\n");
+			ret = -EFAULT;
+		}
+
+		break;
+	default:
+		lbs_pr_debug(1, "Invalid Scan type Ioctl Option\n");
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int wlan_scan_mode_ioctl(wlan_private * priv, struct iwreq *wrq)
+{
+	wlan_adapter *adapter = priv->adapter;
+	u8 buf[12];
+	u8 *option[] = { "bss", "ibss", "any", "get" };
+	int i, max_options = (sizeof(option) / sizeof(option[0]));
+	int ret = 0;
+
+	ENTER();
+
+	memset(buf, 0, sizeof(buf));
+
+	if (copy_from_user(buf, wrq->u.data.pointer, min_t(size_t, sizeof(buf),
+							 wrq->u.data.length))) {
+		lbs_pr_debug(1, "Copy from user failed\n");
+		return -EFAULT;
+	}
+
+	lbs_pr_debug(1, "Scan mode Option = %s\n", buf);
+
+	buf[sizeof(buf) - 1] = '\0';
+
+	for (i = 0; i < max_options; i++) {
+		if (!strcmp(buf, option[i]))
+			break;
+	}
+
+	switch (i) {
+
+	case 0:
+		adapter->scanmode = cmd_bss_type_bss;
+		break;
+	case 1:
+		adapter->scanmode = cmd_bss_type_ibss;
+		break;
+	case 2:
+		adapter->scanmode = cmd_bss_type_any;
+		break;
+	case 3:
+
+		wrq->u.data.length = strlen(option[adapter->scanmode - 1]) + 1;
+
+		lbs_pr_debug(1, "Get Scan mode Option = %s\n",
+		       option[adapter->scanmode - 1]);
+
+		lbs_pr_debug(1, "Scan mode length %d\n", wrq->u.data.length);
+
+		if (copy_to_user(wrq->u.data.pointer,
+				 option[adapter->scanmode - 1],
+				 wrq->u.data.length)) {
+			lbs_pr_debug(1, "Copy to user failed\n");
+			ret = -EFAULT;
+		}
+		lbs_pr_debug(1, "GET Scan type Option after copy = %s\n",
+		       (char *)wrq->u.data.pointer);
+
+		break;
+
+	default:
+		lbs_pr_debug(1, "Invalid Scan mode Ioctl Option\n");
+		ret = -EINVAL;
+		break;
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief Get/Set Adhoc G Rate
+ *
+ *  @param priv		A pointer to wlan_private structure
+ *  @param wrq	   	A pointer to user data
+ *  @return 	   	0--success, otherwise fail
+ */
+static int wlan_do_set_grate_ioctl(wlan_private * priv, struct iwreq *wrq)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int data, data1;
+	int *val;
+
+	ENTER();
+
+	data1 = SUBCMD_DATA(wrq);
+	switch (data1) {
+	case 0:
+		adapter->adhoc_grate_enabled = 0;
+		break;
+	case 1:
+		adapter->adhoc_grate_enabled = 1;
+		break;
+	case 2:
+		break;
+	default:
+		return -EINVAL;
+	}
+	data = adapter->adhoc_grate_enabled;
+	val = (int *)wrq->u.name;
+	*val = data;
+	LEAVE();
+	return 0;
+}
+
+static inline int hex2int(char c)
+{
+	if (c >= '0' && c <= '9')
+		return (c - '0');
+	if (c >= 'a' && c <= 'f')
+		return (c - 'a' + 10);
+	if (c >= 'A' && c <= 'F')
+		return (c - 'A' + 10);
+	return -1;
+}
+
+/* Convert a string representation of a MAC address ("xx:xx:xx:xx:xx:xx")
+   into binary format (6 bytes).
+
+   This function expects that each byte is represented with 2 characters
+   (e.g., 11:2:11:11:11:11 is invalid)
+
+ */
+static char *eth_str2addr(char *ethstr, u8 * addr)
+{
+	int i, val, val2;
+	char *pos = ethstr;
+
+	/* get rid of initial blanks */
+	while (*pos == ' ' || *pos == '\t')
+		++pos;
+
+	for (i = 0; i < 6; i++) {
+		val = hex2int(*pos++);
+		if (val < 0)
+			return NULL;
+		val2 = hex2int(*pos++);
+		if (val2 < 0)
+			return NULL;
+		addr[i] = (val * 16 + val2) & 0xff;
+
+		if (i < 5 && *pos++ != ':')
+			return NULL;
+	}
+	return pos;
+}
+
+/* this writes xx:xx:xx:xx:xx:xx into ethstr
+   (ethstr must have space for 18 chars) */
+static int eth_addr2str(u8 * addr, char *ethstr)
+{
+	int i;
+	char *pos = ethstr;
+
+	for (i = 0; i < 6; i++) {
+		sprintf(pos, "%02x", addr[i] & 0xff);
+		pos += 2;
+		if (i < 5)
+			*pos++ = ':';
+	}
+	return 17;
+}
+
+/**
+ *  @brief          Add an entry to the BT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_bt_add_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	struct iwreq *wrq = (struct iwreq *)req;
+	char ethaddrs_str[18];
+	char *pos;
+	u8 ethaddr[ETH_ALEN];
+
+	ENTER();
+	if (copy_from_user(ethaddrs_str, wrq->u.data.pointer,
+			   sizeof(ethaddrs_str)))
+		return -EFAULT;
+
+	if ((pos = eth_str2addr(ethaddrs_str, ethaddr)) == NULL) {
+		lbs_pr_info("BT_ADD: Invalid MAC address\n");
+		return -EINVAL;
+	}
+
+	lbs_pr_debug(1, "BT: adding %s\n", ethaddrs_str);
+	LEAVE();
+	return (libertas_prepare_and_send_command(priv, cmd_bt_access,
+				      cmd_act_bt_access_add,
+				      cmd_option_waitforrsp, 0, ethaddr));
+}
+
+/**
+ *  @brief          Delete an entry from the BT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_bt_del_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	struct iwreq *wrq = (struct iwreq *)req;
+	char ethaddrs_str[18];
+	u8 ethaddr[ETH_ALEN];
+	char *pos;
+
+	ENTER();
+	if (copy_from_user(ethaddrs_str, wrq->u.data.pointer,
+			   sizeof(ethaddrs_str)))
+		return -EFAULT;
+
+	if ((pos = eth_str2addr(ethaddrs_str, ethaddr)) == NULL) {
+		lbs_pr_info("Invalid MAC address\n");
+		return -EINVAL;
+	}
+
+	lbs_pr_debug(1, "BT: deleting %s\n", ethaddrs_str);
+
+	return (libertas_prepare_and_send_command(priv,
+				      cmd_bt_access,
+				      cmd_act_bt_access_del,
+				      cmd_option_waitforrsp, 0, ethaddr));
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief          Reset all entries from the BT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_bt_reset_ioctl(wlan_private * priv)
+{
+	ENTER();
+
+	lbs_pr_alert( "BT: resetting\n");
+
+	return (libertas_prepare_and_send_command(priv,
+				      cmd_bt_access,
+				      cmd_act_bt_access_reset,
+				      cmd_option_waitforrsp, 0, NULL));
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief          List an entry from the BT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_bt_list_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	int pos;
+	char *addr1;
+	struct iwreq *wrq = (struct iwreq *)req;
+	/* used to pass id and store the bt entry returned by the FW */
+	union {
+		int id;
+		char addr1addr2[2 * ETH_ALEN];
+	} param;
+	static char outstr[64];
+	char *pbuf = outstr;
+	int ret;
+
+	ENTER();
+
+	if (copy_from_user(outstr, wrq->u.data.pointer, sizeof(outstr))) {
+		lbs_pr_debug(1, "Copy from user failed\n");
+		return -1;
+	}
+	param.id = simple_strtoul(outstr, NULL, 10);
+	pos = sprintf(pbuf, "%d: ", param.id);
+	pbuf += pos;
+
+	ret = libertas_prepare_and_send_command(priv, cmd_bt_access,
+				    cmd_act_bt_access_list,
+				    cmd_option_waitforrsp, 0,
+				    (char *)&param);
+
+	if (ret == 0) {
+		addr1 = param.addr1addr2;
+
+		pos = sprintf(pbuf, "ignoring traffic from ");
+		pbuf += pos;
+		pos = eth_addr2str(addr1, pbuf);
+		pbuf += pos;
+	} else {
+		sprintf(pbuf, "(null)");
+		pbuf += pos;
+	}
+
+	wrq->u.data.length = strlen(outstr);
+	if (copy_to_user(wrq->u.data.pointer, (char *)outstr,
+			 wrq->u.data.length)) {
+		lbs_pr_debug(1, "BT_LIST: Copy to user failed!\n");
+		return -EFAULT;
+	}
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief          Find the next parameter in an input string
+ *  @param ptr      A pointer to the input parameter string
+ *  @return         A pointer to the next parameter, or 0 if no parameters left.
+ */
+static char * next_param(char * ptr)
+{
+	if (!ptr) return NULL;
+	while (*ptr == ' ' || *ptr == '\t') ++ptr;
+	return (*ptr == '\0') ? NULL : ptr;
+}
+
+/**
+ *  @brief          Add an entry to the FWT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_fwt_add_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	struct iwreq *wrq = (struct iwreq *)req;
+	char in_str[128];
+	static struct cmd_ds_fwt_access fwt_access;
+	char *ptr;
+
+	ENTER();
+	if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
+		return -EFAULT;
+
+	if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) {
+		lbs_pr_alert( "FWT_ADD: Invalid MAC address 1\n");
+		return -EINVAL;
+	}
+
+	if ((ptr = eth_str2addr(ptr, fwt_access.ra)) == NULL) {
+		lbs_pr_alert( "FWT_ADD: Invalid MAC address 2\n");
+		return -EINVAL;
+	}
+
+	if ((ptr = next_param(ptr)))
+		fwt_access.metric =
+			cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
+	else
+		fwt_access.metric = FWT_DEFAULT_METRIC;
+
+	if ((ptr = next_param(ptr)))
+		fwt_access.dir = (u8)simple_strtoul(ptr, &ptr, 10);
+	else
+		fwt_access.dir = FWT_DEFAULT_DIR;
+
+	if ((ptr = next_param(ptr)))
+		fwt_access.ssn =
+			cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
+	else
+		fwt_access.ssn = FWT_DEFAULT_SSN;
+
+	if ((ptr = next_param(ptr)))
+		fwt_access.dsn =
+			cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
+	else
+		fwt_access.dsn = FWT_DEFAULT_DSN;
+
+	if ((ptr = next_param(ptr)))
+		fwt_access.hopcount = simple_strtoul(ptr, &ptr, 10);
+	else
+		fwt_access.hopcount = FWT_DEFAULT_HOPCOUNT;
+
+	if ((ptr = next_param(ptr)))
+		fwt_access.ttl = simple_strtoul(ptr, &ptr, 10);
+	else
+		fwt_access.ttl = FWT_DEFAULT_TTL;
+
+	if ((ptr = next_param(ptr)))
+		fwt_access.expiration =
+			cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
+	else
+		fwt_access.expiration = FWT_DEFAULT_EXPIRATION;
+
+	if ((ptr = next_param(ptr)))
+		fwt_access.sleepmode = (u8)simple_strtoul(ptr, &ptr, 10);
+	else
+		fwt_access.sleepmode = FWT_DEFAULT_SLEEPMODE;
+
+	if ((ptr = next_param(ptr)))
+		fwt_access.snr =
+			cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
+	else
+		fwt_access.snr = FWT_DEFAULT_SNR;
+
+#ifdef DEBUG
+	{
+		char ethaddr1_str[18], ethaddr2_str[18];
+		eth_addr2str(fwt_access.da, ethaddr1_str);
+		eth_addr2str(fwt_access.ra, ethaddr2_str);
+		lbs_pr_debug(1, "FWT_ADD: adding (da:%s,%i,ra:%s)\n", ethaddr1_str,
+		       fwt_access.dir, ethaddr2_str);
+		lbs_pr_debug(1, "FWT_ADD: ssn:%u dsn:%u met:%u hop:%u ttl:%u exp:%u slp:%u snr:%u\n",
+		       fwt_access.ssn, fwt_access.dsn, fwt_access.metric,
+		       fwt_access.hopcount, fwt_access.ttl, fwt_access.expiration,
+		       fwt_access.sleepmode, fwt_access.snr);
+	}
+#endif
+
+	LEAVE();
+	return (libertas_prepare_and_send_command(priv, cmd_fwt_access,
+						  cmd_act_fwt_access_add,
+						  cmd_option_waitforrsp, 0,
+						  (void *)&fwt_access));
+}
+
+/**
+ *  @brief          Delete an entry from the FWT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_fwt_del_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	struct iwreq *wrq = (struct iwreq *)req;
+	char in_str[64];
+	static struct cmd_ds_fwt_access fwt_access;
+	char *ptr;
+
+	ENTER();
+	if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
+		return -EFAULT;
+
+	if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) {
+		lbs_pr_alert( "FWT_DEL: Invalid MAC address 1\n");
+		return -EINVAL;
+	}
+
+	if ((ptr = eth_str2addr(ptr, fwt_access.ra)) == NULL) {
+		lbs_pr_alert( "FWT_DEL: Invalid MAC address 2\n");
+		return -EINVAL;
+	}
+
+	if ((ptr = next_param(ptr)))
+		fwt_access.dir = (u8)simple_strtoul(ptr, &ptr, 10);
+	else
+		fwt_access.dir = FWT_DEFAULT_DIR;
+
+#ifdef DEBUG
+	{
+		char ethaddr1_str[18], ethaddr2_str[18];
+		lbs_pr_debug(1, "FWT_DEL: line is %s\n", in_str);
+		eth_addr2str(fwt_access.da, ethaddr1_str);
+		eth_addr2str(fwt_access.ra, ethaddr2_str);
+		lbs_pr_debug(1, "FWT_DEL: removing (da:%s,ra:%s,dir:%d)\n", ethaddr1_str,
+		       ethaddr2_str, fwt_access.dir);
+	}
+#endif
+
+	LEAVE();
+	return (libertas_prepare_and_send_command(priv,
+						  cmd_fwt_access,
+						  cmd_act_fwt_access_del,
+						  cmd_option_waitforrsp, 0,
+						  (void *)&fwt_access));
+}
+
+
+/**
+ *  @brief             Print route parameters
+ *  @param fwt_access  struct cmd_ds_fwt_access with route info
+ *  @param buf         destination buffer for route info
+ */
+static void print_route(struct cmd_ds_fwt_access fwt_access, char *buf)
+{
+	buf += sprintf(buf, " ");
+	buf += eth_addr2str(fwt_access.da, buf);
+	buf += sprintf(buf, " ");
+	buf += eth_addr2str(fwt_access.ra, buf);
+	buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.metric));
+	buf += sprintf(buf, " %u", fwt_access.dir);
+	buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.ssn));
+	buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.dsn));
+	buf += sprintf(buf, " %u", fwt_access.hopcount);
+	buf += sprintf(buf, " %u", fwt_access.ttl);
+	buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.expiration));
+	buf += sprintf(buf, " %u", fwt_access.sleepmode);
+	buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.snr));
+}
+
+/**
+ *  @brief          Lookup an entry in the FWT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_fwt_lookup_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	struct iwreq *wrq = (struct iwreq *)req;
+	char in_str[64];
+	char *ptr;
+	static struct cmd_ds_fwt_access fwt_access;
+	static char out_str[128];
+	int ret;
+
+	ENTER();
+	if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
+		return -EFAULT;
+
+	if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) {
+		lbs_pr_alert( "FWT_LOOKUP: Invalid MAC address\n");
+		return -EINVAL;
+	}
+
+#ifdef DEBUG
+	{
+		char ethaddr1_str[18];
+		lbs_pr_debug(1, "FWT_LOOKUP: line is %s\n", in_str);
+		eth_addr2str(fwt_access.da, ethaddr1_str);
+		lbs_pr_debug(1, "FWT_LOOKUP: looking for (da:%s)\n", ethaddr1_str);
+	}
+#endif
+
+	ret = libertas_prepare_and_send_command(priv,
+						cmd_fwt_access,
+						cmd_act_fwt_access_lookup,
+						cmd_option_waitforrsp, 0,
+						(void *)&fwt_access);
+
+	if (ret == 0)
+		print_route(fwt_access, out_str);
+	else
+		sprintf(out_str, "(null)");
+
+	wrq->u.data.length = strlen(out_str);
+	if (copy_to_user(wrq->u.data.pointer, (char *)out_str,
+			 wrq->u.data.length)) {
+		lbs_pr_debug(1, "FWT_LOOKUP: Copy to user failed!\n");
+		return -EFAULT;
+	}
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief          Reset all entries from the FWT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_fwt_reset_ioctl(wlan_private * priv)
+{
+	lbs_pr_debug(1, "FWT: resetting\n");
+
+	return (libertas_prepare_and_send_command(priv,
+				      cmd_fwt_access,
+				      cmd_act_fwt_access_reset,
+				      cmd_option_waitforrsp, 0, NULL));
+}
+
+/**
+ *  @brief          List an entry from the FWT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_fwt_list_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	struct iwreq *wrq = (struct iwreq *)req;
+	char in_str[8];
+	static struct cmd_ds_fwt_access fwt_access;
+	char *ptr = in_str;
+	static char out_str[128];
+	char *pbuf = out_str;
+	int ret;
+
+	ENTER();
+	if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
+		return -EFAULT;
+
+	fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
+
+#ifdef DEBUG
+	{
+		lbs_pr_debug(1, "FWT_LIST: line is %s\n", in_str);
+		lbs_pr_debug(1, "FWT_LIST: listing id:%i\n", le32_to_cpu(fwt_access.id));
+	}
+#endif
+
+	ret = libertas_prepare_and_send_command(priv, cmd_fwt_access,
+				    cmd_act_fwt_access_list,
+				    cmd_option_waitforrsp, 0, (void *)&fwt_access);
+
+	if (ret == 0)
+		print_route(fwt_access, pbuf);
+	else
+		pbuf += sprintf(pbuf, " (null)");
+
+	wrq->u.data.length = strlen(out_str);
+	if (copy_to_user(wrq->u.data.pointer, (char *)out_str,
+			 wrq->u.data.length)) {
+		lbs_pr_debug(1, "FWT_LIST: Copy to user failed!\n");
+		return -EFAULT;
+	}
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief          List an entry from the FRT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_fwt_list_route_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	struct iwreq *wrq = (struct iwreq *)req;
+	char in_str[64];
+	static struct cmd_ds_fwt_access fwt_access;
+	char *ptr = in_str;
+	static char out_str[128];
+	char *pbuf = out_str;
+	int ret;
+
+	ENTER();
+	if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
+		return -EFAULT;
+
+	fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
+
+#ifdef DEBUG
+	{
+		lbs_pr_debug(1, "FWT_LIST_ROUTE: line is %s\n", in_str);
+		lbs_pr_debug(1, "FWT_LIST_ROUTE: listing id:%i\n", le32_to_cpu(fwt_access.id));
+	}
+#endif
+
+	ret = libertas_prepare_and_send_command(priv, cmd_fwt_access,
+				    cmd_act_fwt_access_list_route,
+				    cmd_option_waitforrsp, 0, (void *)&fwt_access);
+
+	if (ret == 0) {
+		pbuf += sprintf(pbuf, " ");
+		pbuf += eth_addr2str(fwt_access.da, pbuf);
+		pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.metric));
+		pbuf += sprintf(pbuf, " %u", fwt_access.dir);
+		/* note that the firmware returns the nid in the id field */
+		pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.id));
+		pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.ssn));
+		pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.dsn));
+		pbuf += sprintf(pbuf, "  hop %u", fwt_access.hopcount);
+		pbuf += sprintf(pbuf, "  ttl %u", fwt_access.ttl);
+		pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.expiration));
+	} else
+		pbuf += sprintf(pbuf, " (null)");
+
+	wrq->u.data.length = strlen(out_str);
+	if (copy_to_user(wrq->u.data.pointer, (char *)out_str,
+			 wrq->u.data.length)) {
+		lbs_pr_debug(1, "FWT_LIST_ROUTE: Copy to user failed!\n");
+		return -EFAULT;
+	}
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief          List an entry from the FNT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_fwt_list_neighbor_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	struct iwreq *wrq = (struct iwreq *)req;
+	char in_str[8];
+	static struct cmd_ds_fwt_access fwt_access;
+	char *ptr = in_str;
+	static char out_str[128];
+	char *pbuf = out_str;
+	int ret;
+
+	ENTER();
+	if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
+		return -EFAULT;
+
+	memset(&fwt_access, 0, sizeof(fwt_access));
+	fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
+
+#ifdef DEBUG
+	{
+		lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: line is %s\n", in_str);
+		lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: listing id:%i\n", le32_to_cpu(fwt_access.id));
+	}
+#endif
+
+	ret = libertas_prepare_and_send_command(priv, cmd_fwt_access,
+				    cmd_act_fwt_access_list_neighbor,
+				    cmd_option_waitforrsp, 0,
+				    (void *)&fwt_access);
+
+	if (ret == 0) {
+		pbuf += sprintf(pbuf, " ra ");
+		pbuf += eth_addr2str(fwt_access.ra, pbuf);
+		pbuf += sprintf(pbuf, "  slp %u", fwt_access.sleepmode);
+		pbuf += sprintf(pbuf, "  snr %u", le32_to_cpu(fwt_access.snr));
+		pbuf += sprintf(pbuf, "  ref %u", le32_to_cpu(fwt_access.references));
+	} else
+		pbuf += sprintf(pbuf, " (null)");
+
+	wrq->u.data.length = strlen(out_str);
+	if (copy_to_user(wrq->u.data.pointer, (char *)out_str,
+			 wrq->u.data.length)) {
+		lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: Copy to user failed!\n");
+		return -EFAULT;
+	}
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief          Cleans up the route (FRT) and neighbor (FNT) tables
+ *                  (Garbage Collection)
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_fwt_cleanup_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	static struct cmd_ds_fwt_access fwt_access;
+	int ret;
+
+	ENTER();
+
+	lbs_pr_debug(1, "FWT: cleaning up\n");
+
+	memset(&fwt_access, 0, sizeof(fwt_access));
+
+	ret = libertas_prepare_and_send_command(priv, cmd_fwt_access,
+				    cmd_act_fwt_access_cleanup,
+				    cmd_option_waitforrsp, 0,
+				    (void *)&fwt_access);
+
+	if (ret == 0)
+		req->ifr_data = (char *)(le32_to_cpu(fwt_access.references));
+	else
+		return -EFAULT;
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief          Gets firmware internal time (debug purposes)
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_fwt_time_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	static struct cmd_ds_fwt_access fwt_access;
+	int ret;
+
+	ENTER();
+
+	lbs_pr_debug(1, "FWT: getting time\n");
+
+	memset(&fwt_access, 0, sizeof(fwt_access));
+
+	ret = libertas_prepare_and_send_command(priv, cmd_fwt_access,
+				    cmd_act_fwt_access_time,
+				    cmd_option_waitforrsp, 0,
+				    (void *)&fwt_access);
+
+	if (ret == 0)
+		req->ifr_data = (char *)(le32_to_cpu(fwt_access.references));
+	else
+		return -EFAULT;
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief          Gets mesh ttl from firmware
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_mesh_get_ttl_ioctl(wlan_private * priv, struct ifreq *req)
+{
+	struct cmd_ds_mesh_access mesh_access;
+	int ret;
+
+	ENTER();
+
+	memset(&mesh_access, 0, sizeof(mesh_access));
+
+	ret = libertas_prepare_and_send_command(priv, cmd_mesh_access,
+				    cmd_act_mesh_get_ttl,
+				    cmd_option_waitforrsp, 0,
+				    (void *)&mesh_access);
+
+	if (ret == 0) {
+		req->ifr_data = (char *)(le32_to_cpu(mesh_access.data[0]));
+	}
+	else
+		return -EFAULT;
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief          Gets mesh ttl from firmware
+ *  @param priv     A pointer to wlan_private structure
+ *  @param ttl      New ttl value
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_mesh_set_ttl_ioctl(wlan_private * priv, int ttl)
+{
+	struct cmd_ds_mesh_access mesh_access;
+	int ret;
+
+	ENTER();
+
+	if( (ttl > 0xff) || (ttl < 0) )
+		return -EINVAL;
+
+	memset(&mesh_access, 0, sizeof(mesh_access));
+	mesh_access.data[0] = ttl;
+
+	ret = libertas_prepare_and_send_command(priv, cmd_mesh_access,
+						cmd_act_mesh_set_ttl,
+						cmd_option_waitforrsp, 0,
+						(void *)&mesh_access);
+
+	if (ret != 0)
+		ret = -EFAULT;
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief ioctl function - entry point
+ *
+ *  @param dev		A pointer to net_device structure
+ *  @param req	   	A pointer to ifreq structure
+ *  @param cmd 		command
+ *  @return 	   	0--success, otherwise fail
+ */
+int libertas_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
+{
+	int subcmd = 0;
+	int idata = 0;
+	int *pdata;
+	int ret = 0;
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	struct iwreq *wrq = (struct iwreq *)req;
+
+	ENTER();
+
+	lbs_pr_debug(1, "libertas_do_ioctl: ioctl cmd = 0x%x\n", cmd);
+	switch (cmd) {
+	case WLANSCAN_TYPE:
+		lbs_pr_debug(1, "Scan type Ioctl\n");
+		ret = wlan_scan_type_ioctl(priv, wrq);
+		break;
+
+	case WLAN_SETNONE_GETNONE:	/* set WPA mode on/off ioctl #20 */
+		switch (wrq->u.data.flags) {
+		case WLANDEAUTH:
+			lbs_pr_debug(1, "Deauth\n");
+			libertas_send_deauth(priv);
+			break;
+
+		case WLANADHOCSTOP:
+			lbs_pr_debug(1, "Adhoc stop\n");
+			ret = libertas_do_adhocstop_ioctl(priv);
+			break;
+
+		case WLANRADIOON:
+			wlan_radio_ioctl(priv, 1);
+			break;
+
+		case WLANRADIOOFF:
+			wlan_radio_ioctl(priv, 0);
+			break;
+		case WLANWLANIDLEON:
+			libertas_idle_on(priv);
+			break;
+		case WLANWLANIDLEOFF:
+			libertas_idle_off(priv);
+			break;
+		case WLAN_SUBCMD_BT_RESET:	/* bt_reset */
+			wlan_bt_reset_ioctl(priv);
+			break;
+		case WLAN_SUBCMD_FWT_RESET:	/* fwt_reset */
+			wlan_fwt_reset_ioctl(priv);
+			break;
+		}		/* End of switch */
+		break;
+
+	case WLANSETWPAIE:
+		ret = wlan_setwpaie_ioctl(priv, req);
+		break;
+	case WLAN_SETINT_GETINT:
+		/* The first 4 bytes of req->ifr_data is sub-ioctl number
+		 * after 4 bytes sits the payload.
+		 */
+		subcmd = (int)req->ifr_data;	//from iwpriv subcmd
+		switch (subcmd) {
+		case WLANNF:
+			ret = wlan_get_nf(priv, wrq);
+			break;
+		case WLANRSSI:
+			ret = wlan_get_rssi(priv, wrq);
+			break;
+		case WLANENABLE11D:
+			ret = libertas_cmd_enable_11d(priv, wrq);
+			break;
+		case WLANADHOCGRATE:
+			ret = wlan_do_set_grate_ioctl(priv, wrq);
+			break;
+		case WLAN_SUBCMD_SET_PRESCAN:
+			ret = wlan_subcmd_setprescan_ioctl(priv, wrq);
+			break;
+		}
+		break;
+
+	case WLAN_SETONEINT_GETONEINT:
+		switch (wrq->u.data.flags) {
+		case WLAN_BEACON_INTERVAL:
+			ret = wlan_beacon_interval(priv, wrq);
+			break;
+
+		case WLAN_LISTENINTRVL:
+			if (!wrq->u.data.length) {
+				int data;
+				lbs_pr_debug(1, "Get locallisteninterval value\n");
+#define GET_ONE_INT	1
+				data = adapter->locallisteninterval;
+				if (copy_to_user(wrq->u.data.pointer,
+						 &data, sizeof(int))) {
+					lbs_pr_debug(1, "Copy to user failed\n");
+					return -EFAULT;
+				}
+
+				wrq->u.data.length = GET_ONE_INT;
+			} else {
+				int data;
+				if (copy_from_user
+				    (&data, wrq->u.data.pointer, sizeof(int))) {
+					lbs_pr_debug(1, "Copy from user failed\n");
+					return -EFAULT;
+				}
+
+				lbs_pr_debug(1, "Set locallisteninterval = %d\n",
+				       data);
+#define MAX_U16_VAL	65535
+				if (data > MAX_U16_VAL) {
+					lbs_pr_debug(1, "Exceeds U16 value\n");
+					return -EINVAL;
+				}
+				adapter->locallisteninterval = data;
+			}
+			break;
+		case WLAN_TXCONTROL:
+			ret = wlan_txcontrol(priv, wrq);	//adds for txcontrol ioctl
+			break;
+
+		case WLAN_NULLPKTINTERVAL:
+			ret = wlan_null_pkt_interval(priv, wrq);
+			break;
+
+		default:
+			ret = -EOPNOTSUPP;
+			break;
+		}
+		break;
+
+	case WLAN_SETONEINT_GETNONE:
+		/* The first 4 bytes of req->ifr_data is sub-ioctl number
+		 * after 4 bytes sits the payload.
+		 */
+		subcmd = wrq->u.data.flags;	//from wpa_supplicant subcmd
+
+		if (!subcmd)
+			subcmd = (int)req->ifr_data;	//from iwpriv subcmd
+
+		switch (subcmd) {
+		case WLAN_SUBCMD_SETRXANTENNA:	/* SETRXANTENNA */
+			idata = SUBCMD_DATA(wrq);
+			ret = setrxantenna(priv, idata);
+			break;
+		case WLAN_SUBCMD_SETTXANTENNA:	/* SETTXANTENNA */
+			idata = SUBCMD_DATA(wrq);
+			ret = settxantenna(priv, idata);
+			break;
+		case WLAN_SET_ATIM_WINDOW:
+			adapter->atimwindow = SUBCMD_DATA(wrq);
+			adapter->atimwindow = min_t(__u16, adapter->atimwindow, 50);
+			break;
+		case WLANSETBCNAVG:
+			adapter->bcn_avg_factor = SUBCMD_DATA(wrq);
+			if (adapter->bcn_avg_factor == 0)
+				adapter->bcn_avg_factor =
+				    DEFAULT_BCN_AVG_FACTOR;
+			if (adapter->bcn_avg_factor > DEFAULT_BCN_AVG_FACTOR)
+				adapter->bcn_avg_factor =
+				    DEFAULT_BCN_AVG_FACTOR;
+			break;
+		case WLANSETDATAAVG:
+			adapter->data_avg_factor = SUBCMD_DATA(wrq);
+			if (adapter->data_avg_factor == 0)
+				adapter->data_avg_factor =
+				    DEFAULT_DATA_AVG_FACTOR;
+			if (adapter->data_avg_factor > DEFAULT_DATA_AVG_FACTOR)
+				adapter->data_avg_factor =
+				    DEFAULT_DATA_AVG_FACTOR;
+			break;
+		case WLANSETREGION:
+			idata = SUBCMD_DATA(wrq);
+			ret = wlan_set_region(priv, (u16) idata);
+			break;
+
+		case WLAN_SET_LISTEN_INTERVAL:
+			idata = SUBCMD_DATA(wrq);
+			adapter->listeninterval = (u16) idata;
+			break;
+
+		case WLAN_SET_MULTIPLE_DTIM:
+			ret = wlan_set_multiple_dtim_ioctl(priv, req);
+			break;
+
+		case WLANSETAUTHALG:
+			ret = wlan_setauthalg_ioctl(priv, req);
+			break;
+
+		case WLANSET8021XAUTHALG:
+			ret = wlan_set8021xauthalg_ioctl(priv, req);
+			break;
+
+		case WLANSETENCRYPTIONMODE:
+			ret = wlan_setencryptionmode_ioctl(priv, req);
+			break;
+
+		case WLAN_SET_LINKMODE:
+			ret = wlan_set_linkmode_ioctl(priv, req);
+			break;
+
+		case WLAN_SET_RADIOMODE:
+			ret = wlan_set_radiomode_ioctl(priv, req);
+			break;
+
+		case WLAN_SET_DEBUGMODE:
+			ret = wlan_set_debugmode_ioctl(priv, req);
+			break;
+
+		case WLAN_SUBCMD_MESH_SET_TTL:
+			idata = SUBCMD_DATA(wrq);
+			ret = wlan_mesh_set_ttl_ioctl(priv, idata);
+			break;
+
+		default:
+			ret = -EOPNOTSUPP;
+			break;
+		}
+
+		break;
+
+	case WLAN_SETNONE_GETTWELVE_CHAR:	/* Get Antenna settings */
+		/*
+		 * We've not used IW_PRIV_TYPE_FIXED so sub-ioctl number is
+		 * in flags of iwreq structure, otherwise it will be in
+		 * mode member of iwreq structure.
+		 */
+		switch ((int)wrq->u.data.flags) {
+		case WLAN_SUBCMD_GETRXANTENNA:	/* Get Rx Antenna */
+			ret = wlan_subcmd_getrxantenna_ioctl(priv, req);
+			break;
+
+		case WLAN_SUBCMD_GETTXANTENNA:	/* Get Tx Antenna */
+			ret = wlan_subcmd_gettxantenna_ioctl(priv, req);
+			break;
+
+		case WLAN_GET_TSF:
+			ret = wlan_get_tsf_ioctl(priv, wrq);
+			break;
+		}
+		break;
+
+	case WLAN_SET128CHAR_GET128CHAR:
+		switch ((int)wrq->u.data.flags) {
+
+		case WLANSCAN_MODE:
+			lbs_pr_debug(1, "Scan mode Ioctl\n");
+			ret = wlan_scan_mode_ioctl(priv, wrq);
+			break;
+
+		case WLAN_GET_ADHOC_STATUS:
+			ret = wlan_get_adhoc_status_ioctl(priv, wrq);
+			break;
+		case WLAN_SUBCMD_BT_ADD:
+			ret = wlan_bt_add_ioctl(priv, req);
+			break;
+		case WLAN_SUBCMD_BT_DEL:
+			ret = wlan_bt_del_ioctl(priv, req);
+			break;
+		case WLAN_SUBCMD_BT_LIST:
+			ret = wlan_bt_list_ioctl(priv, req);
+			break;
+		case WLAN_SUBCMD_FWT_ADD:
+			ret = wlan_fwt_add_ioctl(priv, req);
+			break;
+		case WLAN_SUBCMD_FWT_DEL:
+			ret = wlan_fwt_del_ioctl(priv, req);
+			break;
+		case WLAN_SUBCMD_FWT_LOOKUP:
+			ret = wlan_fwt_lookup_ioctl(priv, req);
+			break;
+		case WLAN_SUBCMD_FWT_LIST_NEIGHBOR:
+			ret = wlan_fwt_list_neighbor_ioctl(priv, req);
+			break;
+		case WLAN_SUBCMD_FWT_LIST:
+			ret = wlan_fwt_list_ioctl(priv, req);
+			break;
+		case WLAN_SUBCMD_FWT_LIST_ROUTE:
+			ret = wlan_fwt_list_route_ioctl(priv, req);
+			break;
+		}
+		break;
+
+	case WLAN_SETNONE_GETONEINT:
+		switch ((int)req->ifr_data) {
+		case WLANGETBCNAVG:
+			pdata = (int *)wrq->u.name;
+			*pdata = (int)adapter->bcn_avg_factor;
+			break;
+
+		case WLANGETREGION:
+			pdata = (int *)wrq->u.name;
+			*pdata = (int)adapter->regioncode;
+			break;
+
+		case WLAN_GET_LISTEN_INTERVAL:
+			pdata = (int *)wrq->u.name;
+			*pdata = (int)adapter->listeninterval;
+			break;
+
+		case WLAN_GET_LINKMODE:
+			req->ifr_data = (char *)((u32) adapter->linkmode);
+			break;
+
+		case WLAN_GET_RADIOMODE:
+			req->ifr_data = (char *)((u32) adapter->radiomode);
+			break;
+
+		case WLAN_GET_DEBUGMODE:
+			req->ifr_data = (char *)((u32) adapter->debugmode);
+			break;
+
+		case WLAN_GET_MULTIPLE_DTIM:
+			pdata = (int *)wrq->u.name;
+			*pdata = (int)adapter->multipledtim;
+			break;
+		case WLAN_GET_TX_RATE:
+			ret = wlan_get_txrate_ioctl(priv, req);
+			break;
+		case WLAN_SUBCMD_FWT_CLEANUP:	/* fwt_cleanup */
+			ret = wlan_fwt_cleanup_ioctl(priv, req);
+			break;
+
+		case WLAN_SUBCMD_FWT_TIME:	/* fwt_time */
+			ret = wlan_fwt_time_ioctl(priv, req);
+			break;
+
+		case WLAN_SUBCMD_MESH_GET_TTL:
+			ret = wlan_mesh_get_ttl_ioctl(priv, req);
+			break;
+
+		default:
+			ret = -EOPNOTSUPP;
+
+		}
+
+		break;
+
+	case WLANGETLOG:
+		ret = wlan_do_getlog_ioctl(priv, wrq);
+		break;
+
+	case WLAN_SET_GET_SIXTEEN_INT:
+		switch ((int)wrq->u.data.flags) {
+		case WLAN_TPCCFG:
+			{
+				int data[5];
+				struct cmd_ds_802_11_tpc_cfg cfg;
+				memset(&cfg, 0, sizeof(cfg));
+				if ((wrq->u.data.length > 1)
+				    && (wrq->u.data.length != 5))
+					return -1;
+
+				if (wrq->u.data.length == 0) {
+					cfg.action =
+					    cpu_to_le16
+					    (cmd_act_get);
+				} else {
+					if (copy_from_user
+					    (data, wrq->u.data.pointer,
+					     sizeof(int) * 5)) {
+						lbs_pr_debug(1,
+						       "Copy from user failed\n");
+						return -EFAULT;
+					}
+
+					cfg.action =
+					    cpu_to_le16
+					    (cmd_act_set);
+					cfg.enable = data[0];
+					cfg.usesnr = data[1];
+					cfg.P0 = data[2];
+					cfg.P1 = data[3];
+					cfg.P2 = data[4];
+				}
+
+				ret =
+				    libertas_prepare_and_send_command(priv,
+							  cmd_802_11_tpc_cfg,
+							  0,
+							  cmd_option_waitforrsp,
+							  0, (void *)&cfg);
+
+				data[0] = cfg.enable;
+				data[1] = cfg.usesnr;
+				data[2] = cfg.P0;
+				data[3] = cfg.P1;
+				data[4] = cfg.P2;
+				if (copy_to_user
+				    (wrq->u.data.pointer, data,
+				     sizeof(int) * 5)) {
+					lbs_pr_debug(1, "Copy to user failed\n");
+					return -EFAULT;
+				}
+
+				wrq->u.data.length = 5;
+			}
+			break;
+
+		case WLAN_POWERCFG:
+			{
+				int data[4];
+				struct cmd_ds_802_11_pwr_cfg cfg;
+				memset(&cfg, 0, sizeof(cfg));
+				if ((wrq->u.data.length > 1)
+				    && (wrq->u.data.length != 4))
+					return -1;
+				if (wrq->u.data.length == 0) {
+					cfg.action =
+					    cpu_to_le16
+					    (cmd_act_get);
+				} else {
+					if (copy_from_user
+					    (data, wrq->u.data.pointer,
+					     sizeof(int) * 4)) {
+						lbs_pr_debug(1,
+						       "Copy from user failed\n");
+						return -EFAULT;
+					}
+
+					cfg.action =
+					    cpu_to_le16
+					    (cmd_act_set);
+					cfg.enable = data[0];
+					cfg.PA_P0 = data[1];
+					cfg.PA_P1 = data[2];
+					cfg.PA_P2 = data[3];
+				}
+				ret =
+				    libertas_prepare_and_send_command(priv,
+							  cmd_802_11_pwr_cfg,
+							  0,
+							  cmd_option_waitforrsp,
+							  0, (void *)&cfg);
+				data[0] = cfg.enable;
+				data[1] = cfg.PA_P0;
+				data[2] = cfg.PA_P1;
+				data[3] = cfg.PA_P2;
+				if (copy_to_user
+				    (wrq->u.data.pointer, data,
+				     sizeof(int) * 4)) {
+					lbs_pr_debug(1, "Copy to user failed\n");
+					return -EFAULT;
+				}
+
+				wrq->u.data.length = 4;
+			}
+			break;
+		case WLAN_AUTO_FREQ_SET:
+			{
+				int data[3];
+				struct cmd_ds_802_11_afc afc;
+				memset(&afc, 0, sizeof(afc));
+				if (wrq->u.data.length != 3)
+					return -1;
+				if (copy_from_user
+				    (data, wrq->u.data.pointer,
+				     sizeof(int) * 3)) {
+					lbs_pr_debug(1, "Copy from user failed\n");
+					return -EFAULT;
+				}
+				afc.afc_auto = data[0];
+
+				if (afc.afc_auto != 0) {
+					afc.threshold = data[1];
+					afc.period = data[2];
+				} else {
+					afc.timing_offset = data[1];
+					afc.carrier_offset = data[2];
+				}
+				ret =
+				    libertas_prepare_and_send_command(priv,
+							  cmd_802_11_set_afc,
+							  0,
+							  cmd_option_waitforrsp,
+							  0, (void *)&afc);
+			}
+			break;
+		case WLAN_AUTO_FREQ_GET:
+			{
+				int data[3];
+				struct cmd_ds_802_11_afc afc;
+				memset(&afc, 0, sizeof(afc));
+				ret =
+				    libertas_prepare_and_send_command(priv,
+							  cmd_802_11_get_afc,
+							  0,
+							  cmd_option_waitforrsp,
+							  0, (void *)&afc);
+				data[0] = afc.afc_auto;
+				data[1] = afc.timing_offset;
+				data[2] = afc.carrier_offset;
+				if (copy_to_user
+				    (wrq->u.data.pointer, data,
+				     sizeof(int) * 3)) {
+					lbs_pr_debug(1, "Copy to user failed\n");
+					return -EFAULT;
+				}
+
+				wrq->u.data.length = 3;
+			}
+			break;
+		case WLAN_SCANPROBES:
+			{
+				int data;
+				if (wrq->u.data.length > 0) {
+					if (copy_from_user
+					    (&data, wrq->u.data.pointer,
+					     sizeof(int))) {
+						lbs_pr_debug(1,
+						       "Copy from user failed\n");
+						return -EFAULT;
+					}
+
+					adapter->scanprobes = data;
+				} else {
+					data = adapter->scanprobes;
+					if (copy_to_user
+					    (wrq->u.data.pointer, &data,
+					     sizeof(int))) {
+						lbs_pr_debug(1,
+						       "Copy to user failed\n");
+						return -EFAULT;
+					}
+				}
+				wrq->u.data.length = 1;
+			}
+			break;
+		case WLAN_LED_GPIO_CTRL:
+			{
+				int i;
+				int data[16];
+
+				struct cmd_ds_802_11_led_ctrl ctrl;
+				struct mrvlietypes_ledgpio *gpio =
+				    (struct mrvlietypes_ledgpio *) ctrl.data;
+
+				memset(&ctrl, 0, sizeof(ctrl));
+				if (wrq->u.data.length > MAX_LEDS * 2)
+					return -ENOTSUPP;
+				if ((wrq->u.data.length % 2) != 0)
+					return -ENOTSUPP;
+				if (wrq->u.data.length == 0) {
+					ctrl.action =
+					    cpu_to_le16
+					    (cmd_act_get);
+				} else {
+					if (copy_from_user
+					    (data, wrq->u.data.pointer,
+					     sizeof(int) *
+					     wrq->u.data.length)) {
+						lbs_pr_debug(1,
+						       "Copy from user failed\n");
+						return -EFAULT;
+					}
+
+					ctrl.action =
+					    cpu_to_le16
+					    (cmd_act_set);
+					ctrl.numled = cpu_to_le16(0);
+					gpio->header.type =
+					    cpu_to_le16(TLV_TYPE_LED_GPIO);
+					gpio->header.len = wrq->u.data.length;
+					for (i = 0; i < wrq->u.data.length;
+					     i += 2) {
+						gpio->ledpin[i / 2].led =
+						    data[i];
+						gpio->ledpin[i / 2].pin =
+						    data[i + 1];
+					}
+				}
+				ret =
+				    libertas_prepare_and_send_command(priv,
+							  cmd_802_11_led_gpio_ctrl,
+							  0,
+							  cmd_option_waitforrsp,
+							  0, (void *)&ctrl);
+				for (i = 0; i < gpio->header.len; i += 2) {
+					data[i] = gpio->ledpin[i / 2].led;
+					data[i + 1] = gpio->ledpin[i / 2].pin;
+				}
+				if (copy_to_user(wrq->u.data.pointer, data,
+						 sizeof(int) *
+						 gpio->header.len)) {
+					lbs_pr_debug(1, "Copy to user failed\n");
+					return -EFAULT;
+				}
+
+				wrq->u.data.length = gpio->header.len;
+			}
+			break;
+		case WLAN_ADAPT_RATESET:
+			ret = wlan_adapt_rateset(priv, wrq);
+			break;
+		case WLAN_INACTIVITY_TIMEOUT:
+			ret = wlan_inactivity_timeout(priv, wrq);
+			break;
+		case WLANSNR:
+			ret = wlan_get_snr(priv, wrq);
+			break;
+		case WLAN_GET_RXINFO:
+			ret = wlan_get_rxinfo(priv, wrq);
+		}
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	LEAVE();
+	return ret;
+}
+
+
diff --git a/drivers/net/wireless/libertas/join.c b/drivers/net/wireless/libertas/join.c
new file mode 100644
index 0000000..11682cb
--- /dev/null
+++ b/drivers/net/wireless/libertas/join.c
@@ -0,0 +1,1055 @@
+/**
+  *  Functions implementing wlan infrastructure and adhoc join routines,
+  *  IOCTL handlers as well as command preperation and response routines
+  *  for sending adhoc start, adhoc join, and association commands
+  *  to the firmware.
+  */
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+
+#include <net/iw_handler.h>
+
+#include "host.h"
+#include "decl.h"
+#include "join.h"
+#include "dev.h"
+
+/**
+ *  @brief This function finds out the common rates between rate1 and rate2.
+ *
+ * It will fill common rates in rate1 as output if found.
+ *
+ * NOTE: Setting the MSB of the basic rates need to be taken
+ *   care, either before or after calling this function
+ *
+ *  @param adapter     A pointer to wlan_adapter structure
+ *  @param rate1       the buffer which keeps input and output
+ *  @param rate1_size  the size of rate1 buffer
+ *  @param rate2       the buffer which keeps rate2
+ *  @param rate2_size  the size of rate2 buffer.
+ *
+ *  @return            0 or -1
+ */
+static int get_common_rates(wlan_adapter * adapter, u8 * rate1,
+			    int rate1_size, u8 * rate2, int rate2_size)
+{
+	u8 *ptr = rate1;
+	int ret = 0;
+	u8 tmp[30];
+	int i;
+
+	memset(&tmp, 0, sizeof(tmp));
+	memcpy(&tmp, rate1, min_t(size_t, rate1_size, sizeof(tmp)));
+	memset(rate1, 0, rate1_size);
+
+	/* Mask the top bit of the original values */
+	for (i = 0; tmp[i] && i < sizeof(tmp); i++)
+		tmp[i] &= 0x7F;
+
+	for (i = 0; rate2[i] && i < rate2_size; i++) {
+		/* Check for Card Rate in tmp, excluding the top bit */
+		if (strchr(tmp, rate2[i] & 0x7F)) {
+			/* values match, so copy the Card Rate to rate1 */
+			*rate1++ = rate2[i];
+		}
+	}
+
+	lbs_dbg_hex("rate1 (AP) rates:", tmp, sizeof(tmp));
+	lbs_dbg_hex("rate2 (Card) rates:", rate2, rate2_size);
+	lbs_dbg_hex("Common rates:", ptr, rate1_size);
+	lbs_pr_debug(1, "Tx datarate is set to 0x%X\n", adapter->datarate);
+
+	if (!adapter->is_datarate_auto) {
+		while (*ptr) {
+			if ((*ptr & 0x7f) == adapter->datarate) {
+				ret = 0;
+				goto done;
+			}
+			ptr++;
+		}
+		lbs_pr_alert( "Previously set fixed data rate %#x isn't "
+		       "compatible with the network.\n", adapter->datarate);
+
+		ret = -1;
+		goto done;
+	}
+
+	ret = 0;
+done:
+	return ret;
+}
+
+int libertas_send_deauth(wlan_private * priv)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+
+	if (adapter->inframode == wlan802_11infrastructure &&
+	    adapter->connect_status == libertas_connected)
+		ret = libertas_send_deauthentication(priv);
+	else
+		ret = -ENOTSUPP;
+
+	return ret;
+}
+
+int libertas_do_adhocstop_ioctl(wlan_private * priv)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+
+	if (adapter->inframode == wlan802_11ibss &&
+	    adapter->connect_status == libertas_connected)
+		ret = libertas_stop_adhoc_network(priv);
+	else
+		ret = -ENOTSUPP;
+
+	return ret;
+}
+
+/**
+ *  @brief Associate to a specific BSS discovered in a scan
+ *
+ *  @param priv      A pointer to wlan_private structure
+ *  @param pbssdesc  Pointer to the BSS descriptor to associate with.
+ *
+ *  @return          0-success, otherwise fail
+ */
+int wlan_associate(wlan_private * priv, struct bss_descriptor * pbssdesc)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret;
+
+	ENTER();
+
+	ret = libertas_prepare_and_send_command(priv, cmd_802_11_authenticate,
+				    0, cmd_option_waitforrsp,
+				    0, pbssdesc->macaddress);
+
+	if (ret) {
+		LEAVE();
+		return ret;
+	}
+
+	/* set preamble to firmware */
+	if (adapter->capinfo.shortpreamble && pbssdesc->cap.shortpreamble)
+		adapter->preamble = cmd_type_short_preamble;
+	else
+		adapter->preamble = cmd_type_long_preamble;
+
+	libertas_set_radio_control(priv);
+
+	ret = libertas_prepare_and_send_command(priv, cmd_802_11_associate,
+				    0, cmd_option_waitforrsp, 0, pbssdesc);
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief Start an Adhoc Network
+ *
+ *  @param priv         A pointer to wlan_private structure
+ *  @param adhocssid    The ssid of the Adhoc Network
+ *  @return             0--success, -1--fail
+ */
+int libertas_start_adhoc_network(wlan_private * priv, struct WLAN_802_11_SSID *adhocssid)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+
+	adapter->adhoccreate = 1;
+
+	if (!adapter->capinfo.shortpreamble) {
+		lbs_pr_debug(1, "AdhocStart: Long preamble\n");
+		adapter->preamble = cmd_type_long_preamble;
+	} else {
+		lbs_pr_debug(1, "AdhocStart: Short preamble\n");
+		adapter->preamble = cmd_type_short_preamble;
+	}
+
+	libertas_set_radio_control(priv);
+
+	lbs_pr_debug(1, "Adhoc channel = %d\n", adapter->adhocchannel);
+	lbs_pr_debug(1, "curbssparams.channel = %d\n",
+	       adapter->curbssparams.channel);
+	lbs_pr_debug(1, "curbssparams.band = %d\n", adapter->curbssparams.band);
+
+	ret = libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_start,
+				    0, cmd_option_waitforrsp, 0, adhocssid);
+
+	return ret;
+}
+
+/**
+ *  @brief Join an adhoc network found in a previous scan
+ *
+ *  @param priv         A pointer to wlan_private structure
+ *  @param pbssdesc     Pointer to a BSS descriptor found in a previous scan
+ *                      to attempt to join
+ *
+ *  @return             0--success, -1--fail
+ */
+int libertas_join_adhoc_network(wlan_private * priv, struct bss_descriptor * pbssdesc)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+
+	lbs_pr_debug(1, "libertas_join_adhoc_network: CurBss.ssid =%s\n",
+	       adapter->curbssparams.ssid.ssid);
+	lbs_pr_debug(1, "libertas_join_adhoc_network: CurBss.ssid_len =%u\n",
+	       adapter->curbssparams.ssid.ssidlength);
+	lbs_pr_debug(1, "libertas_join_adhoc_network: ssid =%s\n", pbssdesc->ssid.ssid);
+	lbs_pr_debug(1, "libertas_join_adhoc_network: ssid len =%u\n",
+	       pbssdesc->ssid.ssidlength);
+
+	/* check if the requested SSID is already joined */
+	if (adapter->curbssparams.ssid.ssidlength
+	    && !libertas_SSID_cmp(&pbssdesc->ssid, &adapter->curbssparams.ssid)
+	    && (adapter->curbssparams.bssdescriptor.inframode ==
+		wlan802_11ibss)) {
+
+        lbs_pr_debug(1,
+		       "ADHOC_J_CMD: New ad-hoc SSID is the same as current, "
+		       "not attempting to re-join");
+
+		return -1;
+	}
+
+	/*Use shortpreamble only when both creator and card supports
+	   short preamble */
+	if (!pbssdesc->cap.shortpreamble || !adapter->capinfo.shortpreamble) {
+		lbs_pr_debug(1, "AdhocJoin: Long preamble\n");
+		adapter->preamble = cmd_type_long_preamble;
+	} else {
+		lbs_pr_debug(1, "AdhocJoin: Short preamble\n");
+		adapter->preamble = cmd_type_short_preamble;
+	}
+
+	libertas_set_radio_control(priv);
+
+	lbs_pr_debug(1, "curbssparams.channel = %d\n",
+	       adapter->curbssparams.channel);
+	lbs_pr_debug(1, "curbssparams.band = %c\n", adapter->curbssparams.band);
+
+	adapter->adhoccreate = 0;
+
+	ret = libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_join,
+				    0, cmd_option_waitforrsp,
+				    OID_802_11_SSID, pbssdesc);
+
+	return ret;
+}
+
+int libertas_stop_adhoc_network(wlan_private * priv)
+{
+	return libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_stop,
+				     0, cmd_option_waitforrsp, 0, NULL);
+}
+
+/**
+ *  @brief Send Deauthentication Request
+ *
+ *  @param priv      A pointer to wlan_private structure
+ *  @return          0--success, -1--fail
+ */
+int libertas_send_deauthentication(wlan_private * priv)
+{
+	return libertas_prepare_and_send_command(priv, cmd_802_11_deauthenticate,
+				     0, cmd_option_waitforrsp, 0, NULL);
+}
+
+/**
+ *  @brief Set Idle Off
+ *
+ *  @param priv         A pointer to wlan_private structure
+ *  @return             0 --success, otherwise fail
+ */
+int libertas_idle_off(wlan_private * priv)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+	const u8 zeromac[] = { 0, 0, 0, 0, 0, 0 };
+	int i;
+
+	ENTER();
+
+	if (adapter->connect_status == libertas_disconnected) {
+		if (adapter->inframode == wlan802_11infrastructure) {
+			if (memcmp(adapter->previousbssid, zeromac,
+				   sizeof(zeromac)) != 0) {
+
+				lbs_pr_debug(1, "Previous SSID = %s\n",
+				       adapter->previousssid.ssid);
+				lbs_pr_debug(1, "Previous BSSID = "
+				       "%02x:%02x:%02x:%02x:%02x:%02x:\n",
+				       adapter->previousbssid[0],
+				       adapter->previousbssid[1],
+				       adapter->previousbssid[2],
+				       adapter->previousbssid[3],
+				       adapter->previousbssid[4],
+				       adapter->previousbssid[5]);
+
+				i = libertas_find_SSID_in_list(adapter,
+						   &adapter->previousssid,
+						   adapter->previousbssid,
+						   adapter->inframode);
+
+				if (i < 0) {
+					libertas_send_specific_BSSID_scan(priv,
+							      adapter->
+							      previousbssid,
+							      1);
+					i = libertas_find_SSID_in_list(adapter,
+							   &adapter->
+							   previousssid,
+							   adapter->
+							   previousbssid,
+							   adapter->
+							   inframode);
+				}
+
+				if (i < 0) {
+					/* If the BSSID could not be found, try just the SSID */
+					i = libertas_find_SSID_in_list(adapter,
+							   &adapter->
+							   previousssid, NULL,
+							   adapter->
+							   inframode);
+				}
+
+				if (i < 0) {
+					libertas_send_specific_SSID_scan(priv,
+							     &adapter->
+							     previousssid,
+							     1);
+					i = libertas_find_SSID_in_list(adapter,
+							   &adapter->
+							   previousssid, NULL,
+							   adapter->
+							   inframode);
+				}
+
+				if (i >= 0) {
+					ret =
+					    wlan_associate(priv,
+							   &adapter->
+							   scantable[i]);
+				}
+			}
+		} else if (adapter->inframode == wlan802_11ibss) {
+			ret = libertas_prepare_and_send_command(priv,
+						    cmd_802_11_ad_hoc_start,
+						    0,
+						    cmd_option_waitforrsp,
+						    0, &adapter->previousssid);
+		}
+	}
+	/* else it is connected */
+
+	lbs_pr_debug(1, "\nwlanidle is off");
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief Set Idle On
+ *
+ *  @param priv         A pointer to wlan_private structure
+ *  @return             0 --success, otherwise fail
+ */
+int libertas_idle_on(wlan_private * priv)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+
+	if (adapter->connect_status == libertas_connected) {
+		if (adapter->inframode == wlan802_11infrastructure) {
+			lbs_pr_debug(1, "Previous SSID = %s\n",
+			       adapter->previousssid.ssid);
+			memmove(&adapter->previousssid,
+				&adapter->curbssparams.ssid,
+				sizeof(struct WLAN_802_11_SSID));
+			libertas_send_deauth(priv);
+
+		} else if (adapter->inframode == wlan802_11ibss) {
+			ret = libertas_stop_adhoc_network(priv);
+		}
+
+	}
+
+	lbs_pr_debug(1, "\nwlanidle is on");
+
+	return ret;
+}
+
+/**
+ *  @brief This function prepares command of authenticate.
+ *
+ *  @param priv      A pointer to wlan_private structure
+ *  @param cmd       A pointer to cmd_ds_command structure
+ *  @param pdata_buf Void cast of pointer to a BSSID to authenticate with
+ *
+ *  @return         0 or -1
+ */
+int libertas_cmd_80211_authenticate(wlan_private * priv,
+				 struct cmd_ds_command *cmd,
+				 void *pdata_buf)
+{
+	wlan_adapter *adapter = priv->adapter;
+	struct cmd_ds_802_11_authenticate *pauthenticate =
+	    &cmd->params.auth;
+	u8 *bssid = pdata_buf;
+
+	cmd->command = cpu_to_le16(cmd_802_11_authenticate);
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_802_11_authenticate)
+			     + S_DS_GEN);
+
+	pauthenticate->authtype = adapter->secinfo.authmode;
+	memcpy(pauthenticate->macaddr, bssid, ETH_ALEN);
+
+	lbs_pr_debug(1, "AUTH_CMD: Bssid is : %x:%x:%x:%x:%x:%x\n",
+	       bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
+
+	return 0;
+}
+
+int libertas_cmd_80211_deauthenticate(wlan_private * priv,
+				   struct cmd_ds_command *cmd)
+{
+	wlan_adapter *adapter = priv->adapter;
+	struct cmd_ds_802_11_deauthenticate *dauth = &cmd->params.deauth;
+
+	ENTER();
+
+	cmd->command = cpu_to_le16(cmd_802_11_deauthenticate);
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_802_11_deauthenticate) +
+			     S_DS_GEN);
+
+	/* set AP MAC address */
+	memmove(dauth->macaddr, adapter->curbssparams.bssid,
+		ETH_ALEN);
+
+	/* Reason code 3 = Station is leaving */
+#define REASON_CODE_STA_LEAVING 3
+	dauth->reasoncode = cpu_to_le16(REASON_CODE_STA_LEAVING);
+
+	LEAVE();
+	return 0;
+}
+
+int libertas_cmd_80211_associate(wlan_private * priv,
+			      struct cmd_ds_command *cmd, void *pdata_buf)
+{
+	wlan_adapter *adapter = priv->adapter;
+	struct cmd_ds_802_11_associate *passo = &cmd->params.associate;
+	int ret = 0;
+	struct bss_descriptor *pbssdesc;
+	u8 *card_rates;
+	u8 *pos;
+	int card_rates_size;
+	u16 tmpcap;
+	struct mrvlietypes_ssidparamset *ssid;
+	struct mrvlietypes_phyparamset *phy;
+	struct mrvlietypes_ssparamset *ss;
+	struct mrvlietypes_ratesparamset *rates;
+	struct mrvlietypes_rsnparamset *rsn;
+
+	ENTER();
+
+	pbssdesc = pdata_buf;
+	pos = (u8 *) passo;
+
+	if (!adapter) {
+		ret = -1;
+		goto done;
+	}
+
+	cmd->command = cpu_to_le16(cmd_802_11_associate);
+
+	/* Save so we know which BSS Desc to use in the response handler */
+	adapter->pattemptedbssdesc = pbssdesc;
+
+	memcpy(passo->peerstaaddr,
+	       pbssdesc->macaddress, sizeof(passo->peerstaaddr));
+	pos += sizeof(passo->peerstaaddr);
+
+	/* set the listen interval */
+	passo->listeninterval = adapter->listeninterval;
+
+	pos += sizeof(passo->capinfo);
+	pos += sizeof(passo->listeninterval);
+	pos += sizeof(passo->bcnperiod);
+	pos += sizeof(passo->dtimperiod);
+
+	ssid = (struct mrvlietypes_ssidparamset *) pos;
+	ssid->header.type = cpu_to_le16(TLV_TYPE_SSID);
+	ssid->header.len = pbssdesc->ssid.ssidlength;
+	memcpy(ssid->ssid, pbssdesc->ssid.ssid, ssid->header.len);
+	pos += sizeof(ssid->header) + ssid->header.len;
+	ssid->header.len = cpu_to_le16(ssid->header.len);
+
+	phy = (struct mrvlietypes_phyparamset *) pos;
+	phy->header.type = cpu_to_le16(TLV_TYPE_PHY_DS);
+	phy->header.len = sizeof(phy->fh_ds.dsparamset);
+	memcpy(&phy->fh_ds.dsparamset,
+	       &pbssdesc->phyparamset.dsparamset.currentchan,
+	       sizeof(phy->fh_ds.dsparamset));
+	pos += sizeof(phy->header) + phy->header.len;
+	phy->header.len = cpu_to_le16(phy->header.len);
+
+	ss = (struct mrvlietypes_ssparamset *) pos;
+	ss->header.type = cpu_to_le16(TLV_TYPE_CF);
+	ss->header.len = sizeof(ss->cf_ibss.cfparamset);
+	pos += sizeof(ss->header) + ss->header.len;
+	ss->header.len = cpu_to_le16(ss->header.len);
+
+	rates = (struct mrvlietypes_ratesparamset *) pos;
+	rates->header.type = cpu_to_le16(TLV_TYPE_RATES);
+
+	memcpy(&rates->rates, &pbssdesc->libertas_supported_rates, WLAN_SUPPORTED_RATES);
+
+	card_rates = libertas_supported_rates;
+	card_rates_size = sizeof(libertas_supported_rates);
+
+	if (get_common_rates(adapter, rates->rates, WLAN_SUPPORTED_RATES,
+			     card_rates, card_rates_size)) {
+		ret = -1;
+		goto done;
+	}
+
+	rates->header.len = min_t(size_t, strlen(rates->rates), WLAN_SUPPORTED_RATES);
+	adapter->curbssparams.numofrates = rates->header.len;
+
+	pos += sizeof(rates->header) + rates->header.len;
+	rates->header.len = cpu_to_le16(rates->header.len);
+
+	if (adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) {
+		rsn = (struct mrvlietypes_rsnparamset *) pos;
+		rsn->header.type = (u16) adapter->wpa_ie[0];	/* WPA_IE or WPA2_IE */
+		rsn->header.type = cpu_to_le16(rsn->header.type);
+		rsn->header.len = (u16) adapter->wpa_ie[1];
+		memcpy(rsn->rsnie, &adapter->wpa_ie[2], rsn->header.len);
+		lbs_dbg_hex("ASSOC_CMD: RSN IE", (u8 *) rsn,
+			sizeof(rsn->header) + rsn->header.len);
+		pos += sizeof(rsn->header) + rsn->header.len;
+		rsn->header.len = cpu_to_le16(rsn->header.len);
+	}
+
+	/* update curbssparams */
+	adapter->curbssparams.channel =
+	    (pbssdesc->phyparamset.dsparamset.currentchan);
+
+	/* Copy the infra. association rates into Current BSS state structure */
+	memcpy(&adapter->curbssparams.datarates, &rates->rates,
+	       min_t(size_t, sizeof(adapter->curbssparams.datarates), rates->header.len));
+
+	lbs_pr_debug(1, "ASSOC_CMD: rates->header.len = %d\n", rates->header.len);
+
+	/* set IBSS field */
+	if (pbssdesc->inframode == wlan802_11infrastructure) {
+#define CAPINFO_ESS_MODE 1
+		passo->capinfo.ess = CAPINFO_ESS_MODE;
+	}
+
+	if (libertas_parse_dnld_countryinfo_11d(priv)) {
+		ret = -1;
+		goto done;
+	}
+
+	cmd->size = cpu_to_le16((u16) (pos - (u8 *) passo) + S_DS_GEN);
+
+	/* set the capability info at last */
+	memcpy(&tmpcap, &pbssdesc->cap, sizeof(passo->capinfo));
+	tmpcap &= CAPINFO_MASK;
+	lbs_pr_debug(1, "ASSOC_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n",
+	       tmpcap, CAPINFO_MASK);
+	tmpcap = cpu_to_le16(tmpcap);
+	memcpy(&passo->capinfo, &tmpcap, sizeof(passo->capinfo));
+
+      done:
+	LEAVE();
+	return ret;
+}
+
+int libertas_cmd_80211_ad_hoc_start(wlan_private * priv,
+				 struct cmd_ds_command *cmd, void *pssid)
+{
+	wlan_adapter *adapter = priv->adapter;
+	struct cmd_ds_802_11_ad_hoc_start *adhs = &cmd->params.ads;
+	int ret = 0;
+	int cmdappendsize = 0;
+	int i;
+	u16 tmpcap;
+	struct bss_descriptor *pbssdesc;
+	struct WLAN_802_11_SSID *ssid = pssid;
+
+	ENTER();
+
+	if (!adapter) {
+		ret = -1;
+		goto done;
+	}
+
+	cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_start);
+
+	pbssdesc = &adapter->curbssparams.bssdescriptor;
+	adapter->pattemptedbssdesc = pbssdesc;
+
+	/*
+	 * Fill in the parameters for 2 data structures:
+	 *   1. cmd_ds_802_11_ad_hoc_start command
+	 *   2. adapter->scantable[i]
+	 *
+	 * Driver will fill up SSID, bsstype,IBSS param, Physical Param,
+	 *   probe delay, and cap info.
+	 *
+	 * Firmware will fill up beacon period, DTIM, Basic rates
+	 *   and operational rates.
+	 */
+
+	memset(adhs->SSID, 0, IW_ESSID_MAX_SIZE);
+
+	memcpy(adhs->SSID, ssid->ssid, ssid->ssidlength);
+
+	lbs_pr_debug(1, "ADHOC_S_CMD: SSID = %s\n", adhs->SSID);
+
+	memset(pbssdesc->ssid.ssid, 0, IW_ESSID_MAX_SIZE);
+	memcpy(pbssdesc->ssid.ssid, ssid->ssid, ssid->ssidlength);
+
+	pbssdesc->ssid.ssidlength = ssid->ssidlength;
+
+	/* set the BSS type */
+	adhs->bsstype = cmd_bss_type_ibss;
+	pbssdesc->inframode = wlan802_11ibss;
+	adhs->beaconperiod = adapter->beaconperiod;
+
+	/* set Physical param set */
+#define DS_PARA_IE_ID   3
+#define DS_PARA_IE_LEN  1
+
+	adhs->phyparamset.dsparamset.elementid = DS_PARA_IE_ID;
+	adhs->phyparamset.dsparamset.len = DS_PARA_IE_LEN;
+
+	WARN_ON(!adapter->adhocchannel);
+
+	lbs_pr_debug(1, "ADHOC_S_CMD: Creating ADHOC on channel %d\n",
+	       adapter->adhocchannel);
+
+	adapter->curbssparams.channel = adapter->adhocchannel;
+
+	pbssdesc->channel = adapter->adhocchannel;
+	adhs->phyparamset.dsparamset.currentchan = adapter->adhocchannel;
+
+	memcpy(&pbssdesc->phyparamset,
+	       &adhs->phyparamset, sizeof(union ieeetypes_phyparamset));
+
+	/* set IBSS param set */
+#define IBSS_PARA_IE_ID   6
+#define IBSS_PARA_IE_LEN  2
+
+	adhs->ssparamset.ibssparamset.elementid = IBSS_PARA_IE_ID;
+	adhs->ssparamset.ibssparamset.len = IBSS_PARA_IE_LEN;
+	adhs->ssparamset.ibssparamset.atimwindow = adapter->atimwindow;
+	memcpy(&pbssdesc->ssparamset,
+	       &adhs->ssparamset, sizeof(union IEEEtypes_ssparamset));
+
+	/* set capability info */
+	adhs->cap.ess = 0;
+	adhs->cap.ibss = 1;
+	pbssdesc->cap.ibss = 1;
+
+	/* probedelay */
+	adhs->probedelay = cpu_to_le16(cmd_scan_probe_delay_time);
+
+	/* set up privacy in adapter->scantable[i] */
+	if (adapter->secinfo.WEPstatus == wlan802_11WEPenabled) {
+
+#define AD_HOC_CAP_PRIVACY_ON 1
+		lbs_pr_debug(1, "ADHOC_S_CMD: WEPstatus set, privacy to WEP\n");
+		pbssdesc->privacy = wlan802_11privfilter8021xWEP;
+		adhs->cap.privacy = AD_HOC_CAP_PRIVACY_ON;
+	} else {
+		lbs_pr_debug(1, "ADHOC_S_CMD: WEPstatus NOT set, Setting "
+		       "privacy to ACCEPT ALL\n");
+		pbssdesc->privacy = wlan802_11privfilteracceptall;
+	}
+
+	memset(adhs->datarate, 0, sizeof(adhs->datarate));
+
+	if (adapter->adhoc_grate_enabled) {
+		memcpy(adhs->datarate, libertas_adhoc_rates_g,
+		       min(sizeof(adhs->datarate), sizeof(libertas_adhoc_rates_g)));
+	} else {
+		memcpy(adhs->datarate, libertas_adhoc_rates_b,
+		       min(sizeof(adhs->datarate), sizeof(libertas_adhoc_rates_b)));
+	}
+
+	/* Find the last non zero */
+	for (i = 0; i < sizeof(adhs->datarate) && adhs->datarate[i]; i++) ;
+
+	adapter->curbssparams.numofrates = i;
+
+	/* Copy the ad-hoc creating rates into Current BSS state structure */
+	memcpy(&adapter->curbssparams.datarates,
+	       &adhs->datarate, adapter->curbssparams.numofrates);
+
+	lbs_pr_debug(1, "ADHOC_S_CMD: rates=%02x %02x %02x %02x \n",
+	       adhs->datarate[0], adhs->datarate[1],
+	       adhs->datarate[2], adhs->datarate[3]);
+
+	lbs_pr_debug(1, "ADHOC_S_CMD: AD HOC Start command is ready\n");
+
+	if (libertas_create_dnld_countryinfo_11d(priv)) {
+		lbs_pr_debug(1, "ADHOC_S_CMD: dnld_countryinfo_11d failed\n");
+		ret = -1;
+		goto done;
+	}
+
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_start)
+			     + S_DS_GEN + cmdappendsize);
+
+	memcpy(&tmpcap, &adhs->cap, sizeof(u16));
+	tmpcap = cpu_to_le16(tmpcap);
+	memcpy(&adhs->cap, &tmpcap, sizeof(u16));
+
+	ret = 0;
+done:
+	LEAVE();
+	return ret;
+}
+
+int libertas_cmd_80211_ad_hoc_stop(wlan_private * priv,
+				struct cmd_ds_command *cmd)
+{
+	cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_stop);
+	cmd->size = cpu_to_le16(S_DS_GEN);
+
+	return 0;
+}
+
+int libertas_cmd_80211_ad_hoc_join(wlan_private * priv,
+				struct cmd_ds_command *cmd, void *pdata_buf)
+{
+	wlan_adapter *adapter = priv->adapter;
+	struct cmd_ds_802_11_ad_hoc_join *padhocjoin = &cmd->params.adj;
+	struct bss_descriptor *pbssdesc = pdata_buf;
+	int cmdappendsize = 0;
+	int ret = 0;
+	u8 *card_rates;
+	int card_rates_size;
+	u16 tmpcap;
+	int i;
+
+	ENTER();
+
+	adapter->pattemptedbssdesc = pbssdesc;
+
+	cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_join);
+
+	padhocjoin->bssdescriptor.bsstype = cmd_bss_type_ibss;
+
+	padhocjoin->bssdescriptor.beaconperiod = pbssdesc->beaconperiod;
+
+	memcpy(&padhocjoin->bssdescriptor.BSSID,
+	       &pbssdesc->macaddress, ETH_ALEN);
+
+	memcpy(&padhocjoin->bssdescriptor.SSID,
+	       &pbssdesc->ssid.ssid, pbssdesc->ssid.ssidlength);
+
+	memcpy(&padhocjoin->bssdescriptor.phyparamset,
+	       &pbssdesc->phyparamset, sizeof(union ieeetypes_phyparamset));
+
+	memcpy(&padhocjoin->bssdescriptor.ssparamset,
+	       &pbssdesc->ssparamset, sizeof(union IEEEtypes_ssparamset));
+
+	memcpy(&tmpcap, &pbssdesc->cap, sizeof(struct ieeetypes_capinfo));
+	tmpcap &= CAPINFO_MASK;
+
+	lbs_pr_debug(1, "ADHOC_J_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n",
+	       tmpcap, CAPINFO_MASK);
+	memcpy(&padhocjoin->bssdescriptor.cap, &tmpcap,
+	       sizeof(struct ieeetypes_capinfo));
+
+	/* information on BSSID descriptor passed to FW */
+    lbs_pr_debug(1,
+	       "ADHOC_J_CMD: BSSID = %2x-%2x-%2x-%2x-%2x-%2x, SSID = %s\n",
+	       padhocjoin->bssdescriptor.BSSID[0],
+	       padhocjoin->bssdescriptor.BSSID[1],
+	       padhocjoin->bssdescriptor.BSSID[2],
+	       padhocjoin->bssdescriptor.BSSID[3],
+	       padhocjoin->bssdescriptor.BSSID[4],
+	       padhocjoin->bssdescriptor.BSSID[5],
+	       padhocjoin->bssdescriptor.SSID);
+
+	lbs_pr_debug(1, "ADHOC_J_CMD: Data Rate = %x\n",
+	       (u32) padhocjoin->bssdescriptor.datarates);
+
+	/* failtimeout */
+	padhocjoin->failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT);
+
+	/* probedelay */
+	padhocjoin->probedelay =
+	    cpu_to_le16(cmd_scan_probe_delay_time);
+
+	/* Copy Data rates from the rates recorded in scan response */
+	memset(padhocjoin->bssdescriptor.datarates, 0,
+	       sizeof(padhocjoin->bssdescriptor.datarates));
+	memcpy(padhocjoin->bssdescriptor.datarates, pbssdesc->datarates,
+	       min(sizeof(padhocjoin->bssdescriptor.datarates),
+		   sizeof(pbssdesc->datarates)));
+
+	card_rates = libertas_supported_rates;
+	card_rates_size = sizeof(libertas_supported_rates);
+
+	adapter->curbssparams.channel = pbssdesc->channel;
+
+	if (get_common_rates(adapter, padhocjoin->bssdescriptor.datarates,
+			     sizeof(padhocjoin->bssdescriptor.datarates),
+			     card_rates, card_rates_size)) {
+		lbs_pr_debug(1, "ADHOC_J_CMD: get_common_rates returns error.\n");
+		ret = -1;
+		goto done;
+	}
+
+	/* Find the last non zero */
+	for (i = 0; i < sizeof(padhocjoin->bssdescriptor.datarates)
+	     && padhocjoin->bssdescriptor.datarates[i]; i++) ;
+
+	adapter->curbssparams.numofrates = i;
+
+	/*
+	 * Copy the adhoc joining rates to Current BSS State structure
+	 */
+	memcpy(adapter->curbssparams.datarates,
+	       padhocjoin->bssdescriptor.datarates,
+	       adapter->curbssparams.numofrates);
+
+	padhocjoin->bssdescriptor.ssparamset.ibssparamset.atimwindow =
+	    cpu_to_le16(pbssdesc->atimwindow);
+
+	if (adapter->secinfo.WEPstatus == wlan802_11WEPenabled) {
+		padhocjoin->bssdescriptor.cap.privacy = AD_HOC_CAP_PRIVACY_ON;
+	}
+
+	if (adapter->psmode == wlan802_11powermodemax_psp) {
+		/* wake up first */
+		enum WLAN_802_11_POWER_MODE Localpsmode;
+
+		Localpsmode = wlan802_11powermodecam;
+		ret = libertas_prepare_and_send_command(priv,
+					    cmd_802_11_ps_mode,
+					    cmd_act_set,
+					    0, 0, &Localpsmode);
+
+		if (ret) {
+			ret = -1;
+			goto done;
+		}
+	}
+
+	if (libertas_parse_dnld_countryinfo_11d(priv)) {
+		ret = -1;
+		goto done;
+	}
+
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_join)
+			     + S_DS_GEN + cmdappendsize);
+
+	memcpy(&tmpcap, &padhocjoin->bssdescriptor.cap,
+	       sizeof(struct ieeetypes_capinfo));
+	tmpcap = cpu_to_le16(tmpcap);
+
+	memcpy(&padhocjoin->bssdescriptor.cap,
+	       &tmpcap, sizeof(struct ieeetypes_capinfo));
+
+      done:
+	LEAVE();
+	return ret;
+}
+
+int libertas_ret_80211_associate(wlan_private * priv,
+			      struct cmd_ds_command *resp)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+	union iwreq_data wrqu;
+	struct ieeetypes_assocrsp *passocrsp;
+	struct bss_descriptor *pbssdesc;
+
+	ENTER();
+
+	passocrsp = (struct ieeetypes_assocrsp *) & resp->params;
+
+	if (passocrsp->statuscode) {
+
+		libertas_mac_event_disconnected(priv);
+
+        lbs_pr_debug(1,
+		       "ASSOC_RESP: Association failed, status code = %d\n",
+		       passocrsp->statuscode);
+
+		ret = -1;
+		goto done;
+	}
+
+	lbs_dbg_hex("ASSOC_RESP:", (void *)&resp->params,
+		le16_to_cpu(resp->size) - S_DS_GEN);
+
+	/* Send a Media Connected event, according to the Spec */
+	adapter->connect_status = libertas_connected;
+
+	/* Set the attempted BSSID Index to current */
+	pbssdesc = adapter->pattemptedbssdesc;
+
+	lbs_pr_debug(1, "ASSOC_RESP: %s\n", pbssdesc->ssid.ssid);
+
+	/* Set the new SSID to current SSID */
+	memcpy(&adapter->curbssparams.ssid,
+	       &pbssdesc->ssid, sizeof(struct WLAN_802_11_SSID));
+
+	/* Set the new BSSID (AP's MAC address) to current BSSID */
+	memcpy(adapter->curbssparams.bssid,
+	       pbssdesc->macaddress, ETH_ALEN);
+
+	/* Make a copy of current BSSID descriptor */
+	memcpy(&adapter->curbssparams.bssdescriptor,
+	       pbssdesc, sizeof(struct bss_descriptor));
+
+	lbs_pr_debug(1, "ASSOC_RESP: currentpacketfilter is %x\n",
+	       adapter->currentpacketfilter);
+
+	adapter->SNR[TYPE_RXPD][TYPE_AVG] = 0;
+	adapter->NF[TYPE_RXPD][TYPE_AVG] = 0;
+
+	memset(adapter->rawSNR, 0x00, sizeof(adapter->rawSNR));
+	memset(adapter->rawNF, 0x00, sizeof(adapter->rawNF));
+	adapter->nextSNRNF = 0;
+	adapter->numSNRNF = 0;
+
+	netif_carrier_on(priv->wlan_dev.netdev);
+	netif_wake_queue(priv->wlan_dev.netdev);
+
+	lbs_pr_debug(1, "ASSOC_RESP: Associated \n");
+
+	memcpy(wrqu.ap_addr.sa_data, adapter->curbssparams.bssid, ETH_ALEN);
+	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+	wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL);
+
+      done:
+	LEAVE();
+	return ret;
+}
+
+int libertas_ret_80211_disassociate(wlan_private * priv,
+				 struct cmd_ds_command *resp)
+{
+	ENTER();
+
+	libertas_mac_event_disconnected(priv);
+
+	LEAVE();
+	return 0;
+}
+
+int libertas_ret_80211_ad_hoc_start(wlan_private * priv,
+				 struct cmd_ds_command *resp)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+	u16 command = le16_to_cpu(resp->command);
+	u16 result = le16_to_cpu(resp->result);
+	struct cmd_ds_802_11_ad_hoc_result *padhocresult;
+	union iwreq_data wrqu;
+	struct bss_descriptor *pbssdesc;
+
+	ENTER();
+
+	padhocresult = &resp->params.result;
+
+	lbs_pr_debug(1, "ADHOC_S_RESP: size = %d\n", le16_to_cpu(resp->size));
+	lbs_pr_debug(1, "ADHOC_S_RESP: command = %x\n", command);
+	lbs_pr_debug(1, "ADHOC_S_RESP: result = %x\n", result);
+
+	pbssdesc = adapter->pattemptedbssdesc;
+
+	/*
+	 * Join result code 0 --> SUCCESS
+	 */
+	if (result) {
+		lbs_pr_debug(1, "ADHOC_RESP failed\n");
+		if (adapter->connect_status == libertas_connected) {
+			libertas_mac_event_disconnected(priv);
+		}
+
+		memset(&adapter->curbssparams.bssdescriptor,
+		       0x00, sizeof(adapter->curbssparams.bssdescriptor));
+
+		LEAVE();
+		return -1;
+	}
+
+	/*
+	 * Now the join cmd should be successful
+	 * If BSSID has changed use SSID to compare instead of BSSID
+	 */
+	lbs_pr_debug(1, "ADHOC_J_RESP  %s\n", pbssdesc->ssid.ssid);
+
+	/* Send a Media Connected event, according to the Spec */
+	adapter->connect_status = libertas_connected;
+
+	if (command == cmd_ret_802_11_ad_hoc_start) {
+		/* Update the created network descriptor with the new BSSID */
+		memcpy(pbssdesc->macaddress,
+		       padhocresult->BSSID, ETH_ALEN);
+	} else {
+
+		/* Make a copy of current BSSID descriptor, only needed for join since
+		 *   the current descriptor is already being used for adhoc start
+		 */
+		memmove(&adapter->curbssparams.bssdescriptor,
+			pbssdesc, sizeof(struct bss_descriptor));
+	}
+
+	/* Set the BSSID from the joined/started descriptor */
+	memcpy(&adapter->curbssparams.bssid,
+	       pbssdesc->macaddress, ETH_ALEN);
+
+	/* Set the new SSID to current SSID */
+	memcpy(&adapter->curbssparams.ssid,
+	       &pbssdesc->ssid, sizeof(struct WLAN_802_11_SSID));
+
+	netif_carrier_on(priv->wlan_dev.netdev);
+	netif_wake_queue(priv->wlan_dev.netdev);
+
+	memset(&wrqu, 0, sizeof(wrqu));
+	memcpy(wrqu.ap_addr.sa_data, adapter->curbssparams.bssid, ETH_ALEN);
+	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+	wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL);
+
+	lbs_pr_debug(1, "ADHOC_RESP: - Joined/Started Ad Hoc\n");
+	lbs_pr_debug(1, "ADHOC_RESP: channel = %d\n", adapter->adhocchannel);
+	lbs_pr_debug(1, "ADHOC_RESP: BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n",
+	       padhocresult->BSSID[0], padhocresult->BSSID[1],
+	       padhocresult->BSSID[2], padhocresult->BSSID[3],
+	       padhocresult->BSSID[4], padhocresult->BSSID[5]);
+
+	LEAVE();
+	return ret;
+}
+
+int libertas_ret_80211_ad_hoc_stop(wlan_private * priv,
+				struct cmd_ds_command *resp)
+{
+	ENTER();
+
+	libertas_mac_event_disconnected(priv);
+
+	LEAVE();
+	return 0;
+}
diff --git a/drivers/net/wireless/libertas/join.h b/drivers/net/wireless/libertas/join.h
new file mode 100644
index 0000000..8efa245
--- /dev/null
+++ b/drivers/net/wireless/libertas/join.h
@@ -0,0 +1,64 @@
+/* -*- mode: C; tab-width: 4; indent-tabs-mode: nil -*- */
+/* vi: set expandtab shiftwidth=4 tabstop=4 textwidth=78: */
+
+/**
+  * Interface for the wlan infrastructure and adhoc join routines
+  *
+  * Driver interface functions and type declarations for the join module
+  *   implemented in wlan_join.c.  Process all start/join requests for
+  *   both adhoc and infrastructure networks
+  */
+#ifndef _WLAN_JOIN_H
+#define _WLAN_JOIN_H
+
+#include "defs.h"
+
+struct cmd_ds_command;
+extern int libertas_cmd_80211_authenticate(wlan_private * priv,
+					struct cmd_ds_command *cmd,
+					void *pdata_buf);
+extern int libertas_cmd_80211_ad_hoc_join(wlan_private * priv,
+				       struct cmd_ds_command *cmd,
+				       void *pdata_buf);
+extern int libertas_cmd_80211_ad_hoc_stop(wlan_private * priv,
+				       struct cmd_ds_command *cmd);
+extern int libertas_cmd_80211_ad_hoc_start(wlan_private * priv,
+					struct cmd_ds_command *cmd,
+					void *pssid);
+extern int libertas_cmd_80211_deauthenticate(wlan_private * priv,
+					  struct cmd_ds_command *cmd);
+extern int libertas_cmd_80211_associate(wlan_private * priv,
+				     struct cmd_ds_command *cmd,
+				     void *pdata_buf);
+
+extern int libertas_ret_80211_ad_hoc_start(wlan_private * priv,
+					struct cmd_ds_command *resp);
+extern int libertas_ret_80211_ad_hoc_stop(wlan_private * priv,
+				       struct cmd_ds_command *resp);
+extern int libertas_ret_80211_disassociate(wlan_private * priv,
+					struct cmd_ds_command *resp);
+extern int libertas_ret_80211_associate(wlan_private * priv,
+				     struct cmd_ds_command *resp);
+
+extern int libertas_idle_on(wlan_private * priv);
+extern int libertas_idle_off(wlan_private * priv);
+
+extern int libertas_do_adhocstop_ioctl(wlan_private * priv);
+extern int libertas_reassociation_thread(void *data);
+
+struct WLAN_802_11_SSID;
+struct bss_descriptor;
+
+extern int libertas_start_adhoc_network(wlan_private * priv,
+			     struct WLAN_802_11_SSID *adhocssid);
+extern int libertas_join_adhoc_network(wlan_private * priv, struct bss_descriptor *pbssdesc);
+extern int libertas_stop_adhoc_network(wlan_private * priv);
+
+extern int libertas_send_deauthentication(wlan_private * priv);
+extern int libertas_send_deauth(wlan_private * priv);
+
+extern int libertas_do_adhocstop_ioctl(wlan_private * priv);
+
+int wlan_associate(wlan_private * priv, struct bss_descriptor * pbssdesc);
+
+#endif
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
new file mode 100644
index 0000000..dcbf102
--- /dev/null
+++ b/drivers/net/wireless/libertas/main.c
@@ -0,0 +1,1258 @@
+/**
+  * This file contains the major functions in WLAN
+  * driver. It includes init, exit, open, close and main
+  * thread etc..
+  */
+
+#include <linux/delay.h>
+#include <linux/freezer.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+
+#include <net/iw_handler.h>
+
+#include "host.h"
+#include "sbi.h"
+#include "decl.h"
+#include "dev.h"
+#include "fw.h"
+#include "wext.h"
+#include "debugfs.h"
+#include "assoc.h"
+
+#ifdef ENABLE_PM
+static struct pm_dev *wlan_pm_dev = NULL;
+#endif
+
+#define WLAN_TX_PWR_DEFAULT		20	/*100mW */
+#define WLAN_TX_PWR_US_DEFAULT		20	/*100mW */
+#define WLAN_TX_PWR_JP_DEFAULT		16	/*50mW */
+#define WLAN_TX_PWR_FR_DEFAULT		20	/*100mW */
+#define WLAN_TX_PWR_EMEA_DEFAULT	20	/*100mW */
+
+/* Format { channel, frequency (MHz), maxtxpower } */
+/* band: 'B/G', region: USA FCC/Canada IC */
+static struct chan_freq_power channel_freq_power_US_BG[] = {
+	{1, 2412, WLAN_TX_PWR_US_DEFAULT},
+	{2, 2417, WLAN_TX_PWR_US_DEFAULT},
+	{3, 2422, WLAN_TX_PWR_US_DEFAULT},
+	{4, 2427, WLAN_TX_PWR_US_DEFAULT},
+	{5, 2432, WLAN_TX_PWR_US_DEFAULT},
+	{6, 2437, WLAN_TX_PWR_US_DEFAULT},
+	{7, 2442, WLAN_TX_PWR_US_DEFAULT},
+	{8, 2447, WLAN_TX_PWR_US_DEFAULT},
+	{9, 2452, WLAN_TX_PWR_US_DEFAULT},
+	{10, 2457, WLAN_TX_PWR_US_DEFAULT},
+	{11, 2462, WLAN_TX_PWR_US_DEFAULT}
+};
+
+/* band: 'B/G', region: Europe ETSI */
+static struct chan_freq_power channel_freq_power_EU_BG[] = {
+	{1, 2412, WLAN_TX_PWR_EMEA_DEFAULT},
+	{2, 2417, WLAN_TX_PWR_EMEA_DEFAULT},
+	{3, 2422, WLAN_TX_PWR_EMEA_DEFAULT},
+	{4, 2427, WLAN_TX_PWR_EMEA_DEFAULT},
+	{5, 2432, WLAN_TX_PWR_EMEA_DEFAULT},
+	{6, 2437, WLAN_TX_PWR_EMEA_DEFAULT},
+	{7, 2442, WLAN_TX_PWR_EMEA_DEFAULT},
+	{8, 2447, WLAN_TX_PWR_EMEA_DEFAULT},
+	{9, 2452, WLAN_TX_PWR_EMEA_DEFAULT},
+	{10, 2457, WLAN_TX_PWR_EMEA_DEFAULT},
+	{11, 2462, WLAN_TX_PWR_EMEA_DEFAULT},
+	{12, 2467, WLAN_TX_PWR_EMEA_DEFAULT},
+	{13, 2472, WLAN_TX_PWR_EMEA_DEFAULT}
+};
+
+/* band: 'B/G', region: Spain */
+static struct chan_freq_power channel_freq_power_SPN_BG[] = {
+	{10, 2457, WLAN_TX_PWR_DEFAULT},
+	{11, 2462, WLAN_TX_PWR_DEFAULT}
+};
+
+/* band: 'B/G', region: France */
+static struct chan_freq_power channel_freq_power_FR_BG[] = {
+	{10, 2457, WLAN_TX_PWR_FR_DEFAULT},
+	{11, 2462, WLAN_TX_PWR_FR_DEFAULT},
+	{12, 2467, WLAN_TX_PWR_FR_DEFAULT},
+	{13, 2472, WLAN_TX_PWR_FR_DEFAULT}
+};
+
+/* band: 'B/G', region: Japan */
+static struct chan_freq_power channel_freq_power_JPN_BG[] = {
+	{1, 2412, WLAN_TX_PWR_JP_DEFAULT},
+	{2, 2417, WLAN_TX_PWR_JP_DEFAULT},
+	{3, 2422, WLAN_TX_PWR_JP_DEFAULT},
+	{4, 2427, WLAN_TX_PWR_JP_DEFAULT},
+	{5, 2432, WLAN_TX_PWR_JP_DEFAULT},
+	{6, 2437, WLAN_TX_PWR_JP_DEFAULT},
+	{7, 2442, WLAN_TX_PWR_JP_DEFAULT},
+	{8, 2447, WLAN_TX_PWR_JP_DEFAULT},
+	{9, 2452, WLAN_TX_PWR_JP_DEFAULT},
+	{10, 2457, WLAN_TX_PWR_JP_DEFAULT},
+	{11, 2462, WLAN_TX_PWR_JP_DEFAULT},
+	{12, 2467, WLAN_TX_PWR_JP_DEFAULT},
+	{13, 2472, WLAN_TX_PWR_JP_DEFAULT},
+	{14, 2484, WLAN_TX_PWR_JP_DEFAULT}
+};
+
+/**
+ * the structure for channel, frequency and power
+ */
+struct region_cfp_table {
+	u8 region;
+	struct chan_freq_power *cfp_BG;
+	int cfp_no_BG;
+};
+
+/**
+ * the structure for the mapping between region and CFP
+ */
+static struct region_cfp_table region_cfp_table[] = {
+	{0x10,			/*US FCC */
+	 channel_freq_power_US_BG,
+	 sizeof(channel_freq_power_US_BG) / sizeof(struct chan_freq_power),
+	 }
+	,
+	{0x20,			/*CANADA IC */
+	 channel_freq_power_US_BG,
+	 sizeof(channel_freq_power_US_BG) / sizeof(struct chan_freq_power),
+	 }
+	,
+	{0x30, /*EU*/ channel_freq_power_EU_BG,
+	 sizeof(channel_freq_power_EU_BG) / sizeof(struct chan_freq_power),
+	 }
+	,
+	{0x31, /*SPAIN*/ channel_freq_power_SPN_BG,
+	 sizeof(channel_freq_power_SPN_BG) / sizeof(struct chan_freq_power),
+	 }
+	,
+	{0x32, /*FRANCE*/ channel_freq_power_FR_BG,
+	 sizeof(channel_freq_power_FR_BG) / sizeof(struct chan_freq_power),
+	 }
+	,
+	{0x40, /*JAPAN*/ channel_freq_power_JPN_BG,
+	 sizeof(channel_freq_power_JPN_BG) / sizeof(struct chan_freq_power),
+	 }
+	,
+/*Add new region here */
+};
+
+/**
+ * the rates supported by the card
+ */
+u8 libertas_wlan_data_rates[WLAN_SUPPORTED_RATES] =
+    { 0x02, 0x04, 0x0B, 0x16, 0x00, 0x0C, 0x12,
+	0x18, 0x24, 0x30, 0x48, 0x60, 0x6C, 0x00
+};
+
+/**
+ * the rates supported
+ */
+u8 libertas_supported_rates[G_SUPPORTED_RATES] =
+    { 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c,
+0 };
+
+/**
+ * the rates supported for ad-hoc G mode
+ */
+u8 libertas_adhoc_rates_g[G_SUPPORTED_RATES] =
+    { 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c,
+0 };
+
+/**
+ * the rates supported for ad-hoc B mode
+ */
+u8 libertas_adhoc_rates_b[4] = { 0x82, 0x84, 0x8b, 0x96 };
+
+/**
+ * the global variable of a pointer to wlan_private
+ * structure variable
+ */
+static wlan_private *wlanpriv = NULL;
+
+#define MAX_DEVS 5
+static struct net_device *libertas_devs[MAX_DEVS];
+static int libertas_found = 0;
+
+/**
+ * the table to keep region code
+ */
+u16 libertas_region_code_to_index[MRVDRV_MAX_REGION_CODE] =
+    { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40 };
+
+static u8 *default_fw_name = "usb8388.bin";
+
+/**
+ * Attributes exported through sysfs
+ */
+
+/**
+ * @brief Get function for sysfs attribute libertas_mpp
+ */
+static ssize_t libertas_mpp_get(struct device * dev,
+		struct device_attribute *attr, char * buf) {
+	struct cmd_ds_mesh_access mesh_access;
+
+	memset(&mesh_access, 0, sizeof(mesh_access));
+	libertas_prepare_and_send_command(to_net_dev(dev)->priv,
+			cmd_mesh_access,
+			cmd_act_mesh_get_mpp,
+			cmd_option_waitforrsp, 0, (void *)&mesh_access);
+
+	return snprintf(buf, 3, "%d\n", mesh_access.data[0]);
+}
+
+/**
+ * @brief Set function for sysfs attribute libertas_mpp
+ */
+static ssize_t libertas_mpp_set(struct device * dev,
+		struct device_attribute *attr, const char * buf, size_t count) {
+	struct cmd_ds_mesh_access mesh_access;
+
+
+	memset(&mesh_access, 0, sizeof(mesh_access));
+	sscanf(buf, "%d", &(mesh_access.data[0]));
+	libertas_prepare_and_send_command((to_net_dev(dev))->priv,
+			cmd_mesh_access,
+			cmd_act_mesh_set_mpp,
+			cmd_option_waitforrsp, 0, (void *)&mesh_access);
+	return strlen(buf);
+}
+
+/**
+ * libertas_mpp attribute to be exported per mshX interface
+ * through sysfs (/sys/class/net/mshX/libertas-mpp)
+ */
+static DEVICE_ATTR(libertas_mpp, 0644, libertas_mpp_get,
+		libertas_mpp_set );
+
+/**
+ *  @brief Check if the device can be open and wait if necessary.
+ *
+ *  @param dev     A pointer to net_device structure
+ *  @return 	   0
+ *
+ * For USB adapter, on some systems the device open handler will be
+ * called before FW ready. Use the following flag check and wait
+ * function to work around the issue.
+ *
+ */
+static int pre_open_check(struct net_device *dev) {
+	wlan_private *priv = (wlan_private *) dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	int i = 0;
+
+	while (!adapter->fw_ready && i < 20) {
+		i++;
+		msleep_interruptible(100);
+	}
+	if (!adapter->fw_ready) {
+		lbs_pr_info("FW not ready, pre_open_check() return failure\n");
+		LEAVE();
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ *  @brief This function opens the device
+ *
+ *  @param dev     A pointer to net_device structure
+ *  @return 	   0
+ */
+static int wlan_dev_open(struct net_device *dev)
+{
+	wlan_private *priv = (wlan_private *) dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+
+	priv->open = 1;
+
+	if (adapter->connect_status == libertas_connected) {
+		netif_carrier_on(priv->wlan_dev.netdev);
+	} else
+		netif_carrier_off(priv->wlan_dev.netdev);
+
+	LEAVE();
+	return 0;
+}
+/**
+ *  @brief This function opens the mshX interface
+ *
+ *  @param dev     A pointer to net_device structure
+ *  @return 	   0
+ */
+static int mesh_open(struct net_device *dev)
+{
+	wlan_private *priv = (wlan_private *) dev->priv ;
+
+	if(pre_open_check(dev) == -1)
+		return -1;
+	priv->mesh_open = 1 ;
+	netif_start_queue(priv->mesh_dev);
+	if (priv->infra_open == 0)
+		return wlan_dev_open(priv->wlan_dev.netdev) ;
+	return 0;
+}
+
+/**
+ *  @brief This function opens the ethX interface
+ *
+ *  @param dev     A pointer to net_device structure
+ *  @return 	   0
+ */
+static int wlan_open(struct net_device *dev)
+{
+	wlan_private *priv = (wlan_private *) dev->priv ;
+
+	if(pre_open_check(dev) == -1)
+		return -1;
+	priv->infra_open = 1 ;
+	netif_wake_queue(priv->wlan_dev.netdev);
+	if (priv->open == 0)
+		return wlan_dev_open(priv->wlan_dev.netdev) ;
+	return 0;
+}
+
+static int wlan_dev_close(struct net_device *dev)
+{
+	wlan_private *priv = dev->priv;
+
+	ENTER();
+
+	netif_carrier_off(priv->wlan_dev.netdev);
+	priv->open = 0;
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief This function closes the mshX interface
+ *
+ *  @param dev     A pointer to net_device structure
+ *  @return 	   0
+ */
+static int mesh_close(struct net_device *dev)
+{
+	wlan_private *priv = (wlan_private *) (dev->priv);
+
+	priv->mesh_open = 0;
+	netif_stop_queue(priv->mesh_dev);
+	if (priv->infra_open == 0)
+		return wlan_dev_close( ((wlan_private *) dev->priv)->wlan_dev.netdev) ;
+	else
+		return 0;
+}
+
+/**
+ *  @brief This function closes the ethX interface
+ *
+ *  @param dev     A pointer to net_device structure
+ *  @return 	   0
+ */
+static int wlan_close(struct net_device *dev) {
+	wlan_private *priv = (wlan_private *) dev->priv;
+
+	netif_stop_queue(priv->wlan_dev.netdev);
+	priv->infra_open = 0;
+	if (priv->mesh_open == 0)
+		return wlan_dev_close( ((wlan_private *) dev->priv)->wlan_dev.netdev) ;
+	else
+		return 0;
+}
+
+
+#ifdef ENABLE_PM
+
+/**
+ *  @brief This function is a callback function. it is called by
+ *  kernel to enter or exit power saving mode.
+ *
+ *  @param pmdev   A pointer to pm_dev
+ *  @param pmreq   pm_request_t
+ *  @param pmdata  A pointer to pmdata
+ *  @return 	   0 or -1
+ */
+static int wlan_pm_callback(struct pm_dev *pmdev, pm_request_t pmreq,
+			    void *pmdata)
+{
+	wlan_private *priv = wlanpriv;
+	wlan_adapter *adapter = priv->adapter;
+	struct net_device *dev = priv->wlan_dev.netdev;
+
+	lbs_pr_debug(1, "WPRM_PM_CALLBACK: pmreq = %d.\n", pmreq);
+
+	switch (pmreq) {
+	case PM_SUSPEND:
+		lbs_pr_debug(1, "WPRM_PM_CALLBACK: enter PM_SUSPEND.\n");
+
+		/* in associated mode */
+		if (adapter->connect_status == libertas_connected) {
+			if ((adapter->psstate != PS_STATE_SLEEP)
+			    ) {
+				lbs_pr_debug(1,
+				       "wlan_pm_callback: can't enter sleep mode\n");
+				return -1;
+			} else {
+
+				/*
+				 * Detach the network interface
+				 * if the network is running
+				 */
+				if (netif_running(dev)) {
+					netif_device_detach(dev);
+					lbs_pr_debug(1,
+					       "netif_device_detach().\n");
+				}
+				libertas_sbi_suspend(priv);
+			}
+			break;
+		}
+
+		/* in non associated mode */
+
+		/*
+		 * Detach the network interface
+		 * if the network is running
+		 */
+		if (netif_running(dev))
+			netif_device_detach(dev);
+
+		/*
+		 * Storing and restoring of the regs be taken care
+		 * at the driver rest will be done at wlan driver
+		 * this makes driver independent of the card
+		 */
+
+		libertas_sbi_suspend(priv);
+
+		break;
+
+	case PM_RESUME:
+		/* in associated mode */
+		if (adapter->connect_status == libertas_connected) {
+			{
+				/*
+				 * Bring the inteface up first
+				 * This case should not happen still ...
+				 */
+				libertas_sbi_resume(priv);
+
+				/*
+				 * Attach the network interface
+				 * if the network is running
+				 */
+				if (netif_running(dev)) {
+					netif_device_attach(dev);
+					lbs_pr_debug(1,
+					       "after netif_device_attach().\n");
+				}
+				lbs_pr_debug(1,
+				       "After netif attach, in associated mode.\n");
+			}
+			break;
+		}
+
+		/* in non associated mode */
+
+		/*
+		 * Bring the inteface up first
+		 * This case should not happen still ...
+		 */
+
+		libertas_sbi_resume(priv);
+
+		if (netif_running(dev))
+			netif_device_attach(dev);
+
+		lbs_pr_debug(1, "after netif attach, in NON associated mode.\n");
+		break;
+	}
+
+	return 0;
+}
+#endif				/* ENABLE_PM */
+
+static int wlan_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	int ret = 0;
+	wlan_private *priv = dev->priv;
+
+	ENTER();
+
+	if (priv->wlan_dev.dnld_sent || priv->adapter->TxLockFlag) {
+		priv->stats.tx_dropped++;
+		goto done;
+	}
+
+	netif_stop_queue(priv->wlan_dev.netdev);
+
+	if (libertas_process_tx(priv, skb) == 0)
+		dev->trans_start = jiffies;
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ * @brief Mark mesh packets and handover them to wlan_hard_start_xmit
+ *
+ */
+static int mesh_pre_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	wlan_private *priv = dev->priv;
+	ENTER();
+	SET_MESH_FRAME(skb);
+	LEAVE();
+
+	return wlan_hard_start_xmit(skb, priv->wlan_dev.netdev);
+}
+
+/**
+ * @brief Mark non-mesh packets and handover them to wlan_hard_start_xmit
+ *
+ */
+static int wlan_pre_start_xmit(struct sk_buff *skb, struct net_device *dev) {
+	ENTER();
+	UNSET_MESH_FRAME(skb);
+	LEAVE();
+	return wlan_hard_start_xmit(skb, dev);
+}
+
+static void wlan_tx_timeout(struct net_device *dev)
+{
+	wlan_private *priv = (wlan_private *) dev->priv;
+
+	ENTER();
+
+	lbs_pr_err("tx watch dog timeout!\n");
+
+	priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED;
+	dev->trans_start = jiffies;
+
+	if (priv->adapter->currenttxskb) {
+		if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) {
+			/* If we are here, we have not received feedback from
+			   the previous packet.  Assume TX_FAIL and move on. */
+			priv->adapter->eventcause = 0x01000000;
+			libertas_send_tx_feedback(priv);
+		} else
+			wake_up_interruptible(&priv->mainthread.waitq);
+	} else if (priv->adapter->connect_status == libertas_connected)
+		netif_wake_queue(priv->wlan_dev.netdev);
+
+	LEAVE();
+}
+
+/**
+ *  @brief This function returns the network statistics
+ *
+ *  @param dev     A pointer to wlan_private structure
+ *  @return 	   A pointer to net_device_stats structure
+ */
+static struct net_device_stats *wlan_get_stats(struct net_device *dev)
+{
+	wlan_private *priv = (wlan_private *) dev->priv;
+
+	return &priv->stats;
+}
+
+static int wlan_set_mac_address(struct net_device *dev, void *addr)
+{
+	int ret = 0;
+	wlan_private *priv = (wlan_private *) dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	struct sockaddr *phwaddr = addr;
+
+	ENTER();
+
+	memset(adapter->current_addr, 0, ETH_ALEN);
+
+	/* dev->dev_addr is 8 bytes */
+	lbs_dbg_hex("dev->dev_addr:", dev->dev_addr, ETH_ALEN);
+
+	lbs_dbg_hex("addr:", phwaddr->sa_data, ETH_ALEN);
+	memcpy(adapter->current_addr, phwaddr->sa_data, ETH_ALEN);
+
+	ret = libertas_prepare_and_send_command(priv, cmd_802_11_mac_address,
+				    cmd_act_set,
+				    cmd_option_waitforrsp, 0, NULL);
+
+	if (ret) {
+		lbs_pr_debug(1, "set mac address failed.\n");
+		ret = -1;
+		goto done;
+	}
+
+	lbs_dbg_hex("adapter->macaddr:", adapter->current_addr, ETH_ALEN);
+	memcpy(dev->dev_addr, adapter->current_addr, ETH_ALEN);
+	memcpy(((wlan_private *) dev->priv)->mesh_dev->dev_addr, adapter->current_addr, ETH_ALEN);
+
+done:
+	LEAVE();
+	return ret;
+}
+
+static int wlan_copy_multicast_address(wlan_adapter * adapter,
+				     struct net_device *dev)
+{
+	int i = 0;
+	struct dev_mc_list *mcptr = dev->mc_list;
+
+	for (i = 0; i < dev->mc_count; i++) {
+		memcpy(&adapter->multicastlist[i], mcptr->dmi_addr, ETH_ALEN);
+		mcptr = mcptr->next;
+	}
+
+	return i;
+
+}
+
+static void wlan_set_multicast_list(struct net_device *dev)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	int oldpacketfilter;
+
+	ENTER();
+
+	oldpacketfilter = adapter->currentpacketfilter;
+
+	if (dev->flags & IFF_PROMISC) {
+		lbs_pr_debug(1, "enable Promiscuous mode\n");
+		adapter->currentpacketfilter |=
+		    cmd_act_mac_promiscuous_enable;
+		adapter->currentpacketfilter &=
+		    ~(cmd_act_mac_all_multicast_enable |
+		      cmd_act_mac_multicast_enable);
+	} else {
+		/* Multicast */
+		adapter->currentpacketfilter &=
+		    ~cmd_act_mac_promiscuous_enable;
+
+		if (dev->flags & IFF_ALLMULTI || dev->mc_count >
+		    MRVDRV_MAX_MULTICAST_LIST_SIZE) {
+			lbs_pr_debug(1, "Enabling All Multicast!\n");
+			adapter->currentpacketfilter |=
+			    cmd_act_mac_all_multicast_enable;
+			adapter->currentpacketfilter &=
+			    ~cmd_act_mac_multicast_enable;
+		} else {
+			adapter->currentpacketfilter &=
+			    ~cmd_act_mac_all_multicast_enable;
+
+			if (!dev->mc_count) {
+				lbs_pr_debug(1, "No multicast addresses - "
+				       "disabling multicast!\n");
+				adapter->currentpacketfilter &=
+				    ~cmd_act_mac_multicast_enable;
+			} else {
+				int i;
+
+				adapter->currentpacketfilter |=
+				    cmd_act_mac_multicast_enable;
+
+				adapter->nr_of_multicastmacaddr =
+				    wlan_copy_multicast_address(adapter, dev);
+
+				lbs_pr_debug(1, "Multicast addresses: %d\n",
+				       dev->mc_count);
+
+				for (i = 0; i < dev->mc_count; i++) {
+					lbs_pr_debug(1, "Multicast address %d:"
+					       "%x %x %x %x %x %x\n", i,
+					       adapter->multicastlist[i][0],
+					       adapter->multicastlist[i][1],
+					       adapter->multicastlist[i][2],
+					       adapter->multicastlist[i][3],
+					       adapter->multicastlist[i][4],
+					       adapter->multicastlist[i][5]);
+				}
+				/* set multicast addresses to firmware */
+				libertas_prepare_and_send_command(priv,
+						      cmd_mac_multicast_adr,
+						      cmd_act_set, 0, 0,
+						      NULL);
+			}
+		}
+	}
+
+	if (adapter->currentpacketfilter != oldpacketfilter) {
+		libertas_set_mac_packet_filter(priv);
+	}
+
+	LEAVE();
+}
+
+/**
+ *  @brief This function hanldes the major job in WLAN driver.
+ *  it handles the event generated by firmware, rx data received
+ *  from firmware and tx data sent from kernel.
+ *
+ *  @param data    A pointer to wlan_thread structure
+ *  @return 	   0
+ */
+static int wlan_service_main_thread(void *data)
+{
+	struct wlan_thread *thread = data;
+	wlan_private *priv = thread->priv;
+	wlan_adapter *adapter = priv->adapter;
+	wait_queue_t wait;
+	u8 ireg = 0;
+
+	ENTER();
+
+	wlan_activate_thread(thread);
+
+	init_waitqueue_entry(&wait, current);
+
+	for (;;) {
+		lbs_pr_debug(1, "main-thread 111: intcounter=%d "
+		       "currenttxskb=%p dnld_sent=%d\n",
+		       adapter->intcounter,
+		       adapter->currenttxskb, priv->wlan_dev.dnld_sent);
+
+		add_wait_queue(&thread->waitq, &wait);
+		set_current_state(TASK_INTERRUPTIBLE);
+		spin_lock_irq(&adapter->driver_lock);
+		if ((adapter->psstate == PS_STATE_SLEEP) ||
+		    (!adapter->intcounter
+		     && (priv->wlan_dev.dnld_sent || adapter->cur_cmd ||
+			 list_empty(&adapter->cmdpendingq)))) {
+			lbs_pr_debug(1,
+			       "main-thread sleeping... Conn=%d IntC=%d PS_mode=%d PS_State=%d\n",
+			       adapter->connect_status, adapter->intcounter,
+			       adapter->psmode, adapter->psstate);
+			spin_unlock_irq(&adapter->driver_lock);
+			schedule();
+		} else
+			spin_unlock_irq(&adapter->driver_lock);
+
+
+		lbs_pr_debug(1,
+		       "main-thread 222 (waking up): intcounter=%d currenttxskb=%p "
+		       "dnld_sent=%d\n", adapter->intcounter,
+		       adapter->currenttxskb, priv->wlan_dev.dnld_sent);
+
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&thread->waitq, &wait);
+		try_to_freeze();
+
+		lbs_pr_debug(1, "main-thread 333: intcounter=%d currenttxskb=%p "
+		       "dnld_sent=%d\n",
+		       adapter->intcounter,
+		       adapter->currenttxskb, priv->wlan_dev.dnld_sent);
+
+		if (kthread_should_stop()
+		    || adapter->surpriseremoved) {
+			lbs_pr_debug(1,
+			       "main-thread: break from main thread: surpriseremoved=0x%x\n",
+			       adapter->surpriseremoved);
+			break;
+		}
+
+
+		spin_lock_irq(&adapter->driver_lock);
+		if (adapter->intcounter) {
+			u8 int_status;
+			adapter->intcounter = 0;
+			int_status = libertas_sbi_get_int_status(priv, &ireg);
+
+			if (int_status) {
+				lbs_pr_debug(1,
+				       "main-thread: reading HOST_INT_STATUS_REG failed\n");
+				spin_unlock_irq(&adapter->driver_lock);
+				continue;
+			}
+			adapter->hisregcpy |= ireg;
+		}
+
+		lbs_pr_debug(1, "main-thread 444: intcounter=%d currenttxskb=%p "
+		       "dnld_sent=%d\n",
+		       adapter->intcounter,
+		       adapter->currenttxskb, priv->wlan_dev.dnld_sent);
+
+		/* command response? */
+		if (adapter->hisregcpy & his_cmdupldrdy) {
+			lbs_pr_debug(1, "main-thread: cmd response ready.\n");
+
+			adapter->hisregcpy &= ~his_cmdupldrdy;
+			spin_unlock_irq(&adapter->driver_lock);
+			libertas_process_rx_command(priv);
+			spin_lock_irq(&adapter->driver_lock);
+		}
+
+		/* Any Card Event */
+		if (adapter->hisregcpy & his_cardevent) {
+			lbs_pr_debug(1, "main-thread: Card Event Activity.\n");
+
+			adapter->hisregcpy &= ~his_cardevent;
+
+			if (libertas_sbi_read_event_cause(priv)) {
+				lbs_pr_alert(
+				       "main-thread: libertas_sbi_read_event_cause failed.\n");
+				spin_unlock_irq(&adapter->driver_lock);
+				continue;
+			}
+			spin_unlock_irq(&adapter->driver_lock);
+			libertas_process_event(priv);
+		} else
+			spin_unlock_irq(&adapter->driver_lock);
+
+		/* Check if we need to confirm Sleep Request received previously */
+		if (adapter->psstate == PS_STATE_PRE_SLEEP) {
+			if (!priv->wlan_dev.dnld_sent && !adapter->cur_cmd) {
+				if (adapter->connect_status ==
+				    libertas_connected) {
+					lbs_pr_debug(1,
+					       "main_thread: PRE_SLEEP--intcounter=%d currenttxskb=%p "
+					       "dnld_sent=%d cur_cmd=%p, confirm now\n",
+					       adapter->intcounter,
+					       adapter->currenttxskb,
+					       priv->wlan_dev.dnld_sent,
+					       adapter->cur_cmd);
+
+					libertas_ps_confirm_sleep(priv,
+						       (u16) adapter->psmode);
+				} else {
+					/* workaround for firmware sending
+					 * deauth/linkloss event immediately
+					 * after sleep request, remove this
+					 * after firmware fixes it
+					 */
+					adapter->psstate = PS_STATE_AWAKE;
+					lbs_pr_alert(
+					       "main-thread: ignore PS_SleepConfirm in non-connected state\n");
+				}
+			}
+		}
+
+		/* The PS state is changed during processing of Sleep Request
+		 * event above
+		 */
+		if ((priv->adapter->psstate == PS_STATE_SLEEP) ||
+		    (priv->adapter->psstate == PS_STATE_PRE_SLEEP))
+			continue;
+
+		/* Execute the next command */
+		if (!priv->wlan_dev.dnld_sent && !priv->adapter->cur_cmd)
+			libertas_execute_next_command(priv);
+
+		/* Wake-up command waiters which can't sleep in
+		 * libertas_prepare_and_send_command
+		 */
+		if (!adapter->nr_cmd_pending)
+			wake_up_all(&adapter->cmd_pending);
+
+		libertas_tx_runqueue(priv);
+	}
+
+	del_timer(&adapter->command_timer);
+	adapter->nr_cmd_pending = 0;
+	wake_up_all(&adapter->cmd_pending);
+	wlan_deactivate_thread(thread);
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ * @brief This function adds the card. it will probe the
+ * card, allocate the wlan_priv and initialize the device.
+ *
+ *  @param card    A pointer to card
+ *  @return 	   A pointer to wlan_private structure
+ */
+wlan_private *wlan_add_card(void *card)
+{
+	struct net_device *dev = NULL;
+	struct net_device *mesh_dev = NULL;
+	wlan_private *priv = NULL;
+
+	ENTER();
+
+	/* Allocate an Ethernet device and register it */
+	if (!(dev = alloc_etherdev(sizeof(wlan_private)))) {
+		lbs_pr_alert( "Init ethernet device failed!\n");
+		return NULL;
+	}
+
+	priv = dev->priv;
+
+	/* allocate buffer for wlan_adapter */
+	if (!(priv->adapter = kmalloc(sizeof(wlan_adapter), GFP_KERNEL))) {
+		lbs_pr_alert( "Allocate buffer for wlan_adapter failed!\n");
+		goto err_kmalloc;
+	}
+
+	/* Allocate a virtual mesh device */
+	if (!(mesh_dev = alloc_netdev(0, "msh%d", ether_setup))) {
+		lbs_pr_debug(1, "Init ethernet device failed!\n");
+		return NULL;
+	}
+
+	/* Both intervaces share the priv structure */
+	mesh_dev->priv = priv;
+
+	/* init wlan_adapter */
+	memset(priv->adapter, 0, sizeof(wlan_adapter));
+
+	priv->wlan_dev.netdev = dev;
+	priv->wlan_dev.card = card;
+	priv->mesh_open = 0;
+	priv->infra_open = 0;
+	priv->mesh_dev = mesh_dev;
+	wlanpriv = priv;
+
+	SET_MODULE_OWNER(dev);
+	SET_MODULE_OWNER(mesh_dev);
+
+	/* Setup the OS Interface to our functions */
+	dev->open = wlan_open;
+	dev->hard_start_xmit = wlan_pre_start_xmit;
+	dev->stop = wlan_close;
+	dev->do_ioctl = libertas_do_ioctl;
+	dev->set_mac_address = wlan_set_mac_address;
+	mesh_dev->open = mesh_open;
+	mesh_dev->hard_start_xmit = mesh_pre_start_xmit;
+	mesh_dev->stop = mesh_close;
+	mesh_dev->do_ioctl = libertas_do_ioctl;
+	memcpy(mesh_dev->dev_addr, wlanpriv->wlan_dev.netdev->dev_addr,
+			sizeof(wlanpriv->wlan_dev.netdev->dev_addr));
+
+#define	WLAN_WATCHDOG_TIMEOUT	(5 * HZ)
+
+	dev->tx_timeout = wlan_tx_timeout;
+	dev->get_stats = wlan_get_stats;
+	dev->watchdog_timeo = WLAN_WATCHDOG_TIMEOUT;
+	dev->ethtool_ops = &libertas_ethtool_ops;
+	mesh_dev->get_stats = wlan_get_stats;
+	mesh_dev->ethtool_ops = &libertas_ethtool_ops;
+
+#ifdef	WIRELESS_EXT
+	dev->wireless_handlers = (struct iw_handler_def *)&libertas_handler_def;
+	mesh_dev->wireless_handlers = (struct iw_handler_def *)&libertas_handler_def;
+#endif
+#define NETIF_F_DYNALLOC 16
+	dev->features |= NETIF_F_DYNALLOC;
+	dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
+	dev->set_multicast_list = wlan_set_multicast_list;
+
+	INIT_LIST_HEAD(&priv->adapter->cmdfreeq);
+	INIT_LIST_HEAD(&priv->adapter->cmdpendingq);
+
+	spin_lock_init(&priv->adapter->driver_lock);
+	init_waitqueue_head(&priv->adapter->cmd_pending);
+	priv->adapter->nr_cmd_pending = 0;
+
+	lbs_pr_debug(1, "Starting kthread...\n");
+	priv->mainthread.priv = priv;
+	wlan_create_thread(wlan_service_main_thread,
+			   &priv->mainthread, "wlan_main_service");
+
+	priv->assoc_thread =
+		create_singlethread_workqueue("libertas_assoc");
+	INIT_DELAYED_WORK(&priv->assoc_work, wlan_association_worker);
+
+	/*
+	 * Register the device. Fillup the private data structure with
+	 * relevant information from the card and request for the required
+	 * IRQ.
+	 */
+	if (libertas_sbi_register_dev(priv) < 0) {
+		lbs_pr_info("failed to register wlan device!\n");
+		goto err_registerdev;
+	}
+
+	/* init FW and HW */
+	if (libertas_init_fw(priv)) {
+		lbs_pr_debug(1, "Firmware Init failed\n");
+		goto err_registerdev;
+	}
+
+	if (register_netdev(dev)) {
+		lbs_pr_err("Cannot register network device!\n");
+		goto err_init_fw;
+	}
+
+	/* Register virtual mesh interface */
+	if (register_netdev(mesh_dev)) {
+		lbs_pr_info("Cannot register mesh virtual interface!\n");
+		goto err_init_fw;
+	}
+
+	lbs_pr_info("%s: Marvell Wlan 802.11 adapter ", dev->name);
+
+	libertas_debugfs_init_one(priv, dev);
+
+	if (libertas_found == MAX_DEVS)
+		goto err_init_fw;
+	libertas_devs[libertas_found] = dev;
+	libertas_found++;
+#ifdef ENABLE_PM
+	if (!(wlan_pm_dev = pm_register(PM_UNKNOWN_DEV, 0, wlan_pm_callback)))
+		lbs_pr_alert( "failed to register PM callback\n");
+#endif
+	if (device_create_file(&(mesh_dev->dev), &dev_attr_libertas_mpp))
+		goto err_create_file;
+
+	LEAVE();
+	return priv;
+
+err_create_file:
+	device_remove_file(&(mesh_dev->dev), &dev_attr_libertas_mpp);
+err_init_fw:
+	libertas_sbi_unregister_dev(priv);
+err_registerdev:
+	destroy_workqueue(priv->assoc_thread);
+	/* Stop the thread servicing the interrupts */
+	wake_up_interruptible(&priv->mainthread.waitq);
+	wlan_terminate_thread(&priv->mainthread);
+	kfree(priv->adapter);
+err_kmalloc:
+	free_netdev(dev);
+	free_netdev(mesh_dev);
+	wlanpriv = NULL;
+
+	LEAVE();
+	return NULL;
+}
+
+static void wake_pending_cmdnodes(wlan_private *priv)
+{
+	struct cmd_ctrl_node *cmdnode;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->adapter->driver_lock, flags);
+	list_for_each_entry(cmdnode, &priv->adapter->cmdpendingq, list) {
+		cmdnode->cmdwaitqwoken = 1;
+		wake_up_interruptible(&cmdnode->cmdwait_q);
+	}
+	spin_unlock_irqrestore(&priv->adapter->driver_lock, flags);
+}
+
+
+int wlan_remove_card(void *card)
+{
+	wlan_private *priv = libertas_sbi_get_priv(card);
+	wlan_adapter *adapter;
+	struct net_device *dev;
+	struct net_device *mesh_dev;
+	union iwreq_data wrqu;
+	int i;
+
+	ENTER();
+
+	if (!priv) {
+		LEAVE();
+		return 0;
+	}
+
+	adapter = priv->adapter;
+
+	if (!adapter) {
+		LEAVE();
+		return 0;
+	}
+
+	dev = priv->wlan_dev.netdev;
+	mesh_dev = priv->mesh_dev;
+
+	netif_stop_queue(mesh_dev);
+	netif_stop_queue(priv->wlan_dev.netdev);
+	netif_carrier_off(priv->wlan_dev.netdev);
+
+	wake_pending_cmdnodes(priv);
+
+	device_remove_file(&(mesh_dev->dev), &dev_attr_libertas_mpp);
+	unregister_netdev(mesh_dev);
+	unregister_netdev(dev);
+
+	cancel_delayed_work(&priv->assoc_work);
+	destroy_workqueue(priv->assoc_thread);
+
+	if (adapter->psmode == wlan802_11powermodemax_psp) {
+		adapter->psmode = wlan802_11powermodecam;
+		libertas_ps_wakeup(priv, cmd_option_waitforrsp);
+	}
+
+	memset(wrqu.ap_addr.sa_data, 0xaa, ETH_ALEN);
+	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+	wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL);
+
+#ifdef ENABLE_PM
+	pm_unregister(wlan_pm_dev);
+#endif
+
+	adapter->surpriseremoved = 1;
+
+	/* Stop the thread servicing the interrupts */
+	wlan_terminate_thread(&priv->mainthread);
+
+	libertas_debugfs_remove_one(priv);
+
+	lbs_pr_debug(1, "Free adapter\n");
+	libertas_free_adapter(priv);
+
+	for (i = 0; i<libertas_found; i++) {
+		if (libertas_devs[i]==priv->wlan_dev.netdev) {
+			libertas_devs[i] = libertas_devs[--libertas_found];
+			libertas_devs[libertas_found] = NULL ;
+			break ;
+		}
+	}
+
+	lbs_pr_debug(1, "Unregister finish\n");
+
+	priv->wlan_dev.netdev = NULL;
+	priv->mesh_dev = NULL ;
+	free_netdev(mesh_dev);
+	free_netdev(dev);
+	wlanpriv = NULL;
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief This function finds the CFP in
+ *  region_cfp_table based on region and band parameter.
+ *
+ *  @param region  The region code
+ *  @param band	   The band
+ *  @param cfp_no  A pointer to CFP number
+ *  @return 	   A pointer to CFP
+ */
+struct chan_freq_power *libertas_get_region_cfp_table(u8 region, u8 band, int *cfp_no)
+{
+	int i, end;
+
+	ENTER();
+
+	end = sizeof(region_cfp_table)/sizeof(struct region_cfp_table);
+
+	for (i = 0; i < end ; i++) {
+		lbs_pr_debug(1, "region_cfp_table[i].region=%d\n",
+			region_cfp_table[i].region);
+		if (region_cfp_table[i].region == region) {
+			*cfp_no = region_cfp_table[i].cfp_no_BG;
+			LEAVE();
+			return region_cfp_table[i].cfp_BG;
+		}
+	}
+
+	LEAVE();
+	return NULL;
+}
+
+int libertas_set_regiontable(wlan_private * priv, u8 region, u8 band)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int i = 0;
+
+	struct chan_freq_power *cfp;
+	int cfp_no;
+
+	ENTER();
+
+	memset(adapter->region_channel, 0, sizeof(adapter->region_channel));
+
+	{
+		cfp = libertas_get_region_cfp_table(region, band, &cfp_no);
+		if (cfp != NULL) {
+			adapter->region_channel[i].nrcfp = cfp_no;
+			adapter->region_channel[i].CFP = cfp;
+		} else {
+			lbs_pr_debug(1, "wrong region code %#x in band B-G\n",
+			       region);
+			return -1;
+		}
+		adapter->region_channel[i].valid = 1;
+		adapter->region_channel[i].region = region;
+		adapter->region_channel[i].band = band;
+		i++;
+	}
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief This function handles the interrupt. it will change PS
+ *  state if applicable. it will wake up main_thread to handle
+ *  the interrupt event as well.
+ *
+ *  @param dev     A pointer to net_device structure
+ *  @return 	   n/a
+ */
+void libertas_interrupt(struct net_device *dev)
+{
+	wlan_private *priv = dev->priv;
+
+	ENTER();
+
+	lbs_pr_debug(1, "libertas_interrupt: intcounter=%d\n",
+	       priv->adapter->intcounter);
+
+	priv->adapter->intcounter++;
+
+	if (priv->adapter->psstate == PS_STATE_SLEEP) {
+		priv->adapter->psstate = PS_STATE_AWAKE;
+		netif_wake_queue(dev);
+	}
+
+	wake_up_interruptible(&priv->mainthread.waitq);
+
+	LEAVE();
+}
+
+static int wlan_init_module(void)
+{
+	int ret = 0;
+
+	ENTER();
+
+	if (libertas_fw_name == NULL) {
+		libertas_fw_name = default_fw_name;
+	}
+
+	libertas_debugfs_init();
+
+	if (libertas_sbi_register()) {
+		ret = -1;
+		libertas_debugfs_remove();
+		goto done;
+	}
+
+done:
+	LEAVE();
+	return ret;
+}
+
+static void wlan_cleanup_module(void)
+{
+	int i;
+
+	ENTER();
+
+	for (i = 0; i<libertas_found; i++) {
+		wlan_private *priv = libertas_devs[i]->priv;
+		reset_device(priv);
+	}
+
+	libertas_sbi_unregister();
+	libertas_debugfs_remove();
+
+	LEAVE();
+}
+
+module_init(wlan_init_module);
+module_exit(wlan_cleanup_module);
+
+MODULE_DESCRIPTION("M-WLAN Driver");
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/libertas/radiotap.h b/drivers/net/wireless/libertas/radiotap.h
new file mode 100644
index 0000000..5d118f4
--- /dev/null
+++ b/drivers/net/wireless/libertas/radiotap.h
@@ -0,0 +1,57 @@
+#include <net/ieee80211_radiotap.h>
+
+struct tx_radiotap_hdr {
+	struct ieee80211_radiotap_header hdr;
+	u8 rate;
+	u8 txpower;
+	u8 rts_retries;
+	u8 data_retries;
+#if 0
+	u8 pad[IEEE80211_RADIOTAP_HDRLEN - 12];
+#endif
+} __attribute__ ((packed));
+
+#define TX_RADIOTAP_PRESENT (				\
+	(1 << IEEE80211_RADIOTAP_RATE) |		\
+	(1 << IEEE80211_RADIOTAP_DBM_TX_POWER) |	\
+	(1 << IEEE80211_RADIOTAP_RTS_RETRIES) |		\
+	(1 << IEEE80211_RADIOTAP_DATA_RETRIES)  |	\
+	0)
+
+#define IEEE80211_FC_VERSION_MASK    0x0003
+#define IEEE80211_FC_TYPE_MASK       0x000c
+#define IEEE80211_FC_TYPE_MGT        0x0000
+#define IEEE80211_FC_TYPE_CTL        0x0004
+#define IEEE80211_FC_TYPE_DATA       0x0008
+#define IEEE80211_FC_SUBTYPE_MASK    0x00f0
+#define IEEE80211_FC_TOFROMDS_MASK   0x0300
+#define IEEE80211_FC_TODS_MASK       0x0100
+#define IEEE80211_FC_FROMDS_MASK     0x0200
+#define IEEE80211_FC_NODS            0x0000
+#define IEEE80211_FC_TODS            0x0100
+#define IEEE80211_FC_FROMDS          0x0200
+#define IEEE80211_FC_DSTODS          0x0300
+
+struct rx_radiotap_hdr {
+	struct ieee80211_radiotap_header hdr;
+	u8 flags;
+	u8 rate;
+	u16 chan_freq;
+	u16 chan_flags;
+	u8 antenna;
+	u8 antsignal;
+	u16 rx_flags;
+#if 0
+	u8 pad[IEEE80211_RADIOTAP_HDRLEN - 18];
+#endif
+} __attribute__ ((packed));
+
+#define RX_RADIOTAP_PRESENT (			\
+	(1 << IEEE80211_RADIOTAP_FLAGS) |	\
+	(1 << IEEE80211_RADIOTAP_RATE) |	\
+	(1 << IEEE80211_RADIOTAP_CHANNEL) |	\
+	(1 << IEEE80211_RADIOTAP_ANTENNA) |	\
+	(1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |\
+	(1 << IEEE80211_RADIOTAP_RX_FLAGS) |	\
+	0)
+
diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c
new file mode 100644
index 0000000..7e3f78f
--- /dev/null
+++ b/drivers/net/wireless/libertas/rx.c
@@ -0,0 +1,459 @@
+/**
+  * This file contains the handling of RX in wlan driver.
+  */
+#include <linux/etherdevice.h>
+#include <linux/types.h>
+
+#include "hostcmd.h"
+#include "radiotap.h"
+#include "decl.h"
+#include "dev.h"
+#include "wext.h"
+
+struct eth803hdr {
+	u8 dest_addr[6];
+	u8 src_addr[6];
+	u16 h803_len;
+} __attribute__ ((packed));
+
+struct rfc1042hdr {
+	u8 llc_dsap;
+	u8 llc_ssap;
+	u8 llc_ctrl;
+	u8 snap_oui[3];
+	u16 snap_type;
+} __attribute__ ((packed));
+
+struct rxpackethdr {
+	struct rxpd rx_pd;
+	struct eth803hdr eth803_hdr;
+	struct rfc1042hdr rfc1042_hdr;
+} __attribute__ ((packed));
+
+struct rx80211packethdr {
+	struct rxpd rx_pd;
+	void *eth80211_hdr;
+} __attribute__ ((packed));
+
+static int process_rxed_802_11_packet(wlan_private * priv, struct sk_buff *skb);
+
+/**
+ *  @brief This function computes the avgSNR .
+ *
+ *  @param priv    A pointer to wlan_private structure
+ *  @return 	   avgSNR
+ */
+static u8 wlan_getavgsnr(wlan_private * priv)
+{
+	u8 i;
+	u16 temp = 0;
+	wlan_adapter *adapter = priv->adapter;
+	if (adapter->numSNRNF == 0)
+		return 0;
+	for (i = 0; i < adapter->numSNRNF; i++)
+		temp += adapter->rawSNR[i];
+	return (u8) (temp / adapter->numSNRNF);
+
+}
+
+/**
+ *  @brief This function computes the AvgNF
+ *
+ *  @param priv    A pointer to wlan_private structure
+ *  @return 	   AvgNF
+ */
+static u8 wlan_getavgnf(wlan_private * priv)
+{
+	u8 i;
+	u16 temp = 0;
+	wlan_adapter *adapter = priv->adapter;
+	if (adapter->numSNRNF == 0)
+		return 0;
+	for (i = 0; i < adapter->numSNRNF; i++)
+		temp += adapter->rawNF[i];
+	return (u8) (temp / adapter->numSNRNF);
+
+}
+
+/**
+ *  @brief This function save the raw SNR/NF to our internel buffer
+ *
+ *  @param priv    A pointer to wlan_private structure
+ *  @param prxpd   A pointer to rxpd structure of received packet
+ *  @return 	   n/a
+ */
+static void wlan_save_rawSNRNF(wlan_private * priv, struct rxpd *p_rx_pd)
+{
+	wlan_adapter *adapter = priv->adapter;
+	if (adapter->numSNRNF < adapter->data_avg_factor)
+		adapter->numSNRNF++;
+	adapter->rawSNR[adapter->nextSNRNF] = p_rx_pd->snr;
+	adapter->rawNF[adapter->nextSNRNF] = p_rx_pd->nf;
+	adapter->nextSNRNF++;
+	if (adapter->nextSNRNF >= adapter->data_avg_factor)
+		adapter->nextSNRNF = 0;
+	return;
+}
+
+/**
+ *  @brief This function computes the RSSI in received packet.
+ *
+ *  @param priv    A pointer to wlan_private structure
+ *  @param prxpd   A pointer to rxpd structure of received packet
+ *  @return 	   n/a
+ */
+static void wlan_compute_rssi(wlan_private * priv, struct rxpd *p_rx_pd)
+{
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	lbs_pr_debug(1, "rxpd: SNR = %d, NF = %d\n", p_rx_pd->snr, p_rx_pd->nf);
+	lbs_pr_debug(1, "Before computing SNR: SNR- avg = %d, NF-avg = %d\n",
+	       adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE,
+	       adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE);
+
+	adapter->SNR[TYPE_RXPD][TYPE_NOAVG] = p_rx_pd->snr;
+	adapter->NF[TYPE_RXPD][TYPE_NOAVG] = p_rx_pd->nf;
+	wlan_save_rawSNRNF(priv, p_rx_pd);
+
+	adapter->rxpd_rate = p_rx_pd->rx_rate;
+
+	adapter->SNR[TYPE_RXPD][TYPE_AVG] = wlan_getavgsnr(priv) * AVG_SCALE;
+	adapter->NF[TYPE_RXPD][TYPE_AVG] = wlan_getavgnf(priv) * AVG_SCALE;
+	lbs_pr_debug(1, "After computing SNR: SNR-avg = %d, NF-avg = %d\n",
+	       adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE,
+	       adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE);
+
+	adapter->RSSI[TYPE_RXPD][TYPE_NOAVG] =
+	    CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_NOAVG],
+		     adapter->NF[TYPE_RXPD][TYPE_NOAVG]);
+
+	adapter->RSSI[TYPE_RXPD][TYPE_AVG] =
+	    CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE,
+		     adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE);
+
+	LEAVE();
+}
+
+int libertas_upload_rx_packet(wlan_private * priv, struct sk_buff *skb)
+{
+	lbs_pr_debug(1, "skb->data=%p\n", skb->data);
+
+	if(IS_MESH_FRAME(skb))
+		skb->dev = priv->mesh_dev;
+	else
+		skb->dev = priv->wlan_dev.netdev;
+	skb->protocol = eth_type_trans(skb, priv->wlan_dev.netdev);
+	skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+	netif_rx(skb);
+
+	return 0;
+}
+
+/**
+ *  @brief This function processes received packet and forwards it
+ *  to kernel/upper layer
+ *
+ *  @param priv    A pointer to wlan_private
+ *  @param skb     A pointer to skb which includes the received packet
+ *  @return 	   0 or -1
+ */
+int libertas_process_rxed_packet(wlan_private * priv, struct sk_buff *skb)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+
+	struct rxpackethdr *p_rx_pkt;
+	struct rxpd *p_rx_pd;
+
+	int hdrchop;
+	struct ethhdr *p_ethhdr;
+
+	const u8 rfc1042_eth_hdr[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+	ENTER();
+
+	if (priv->adapter->debugmode & MRVDRV_DEBUG_RX_PATH)
+		lbs_dbg_hex("RX packet: ", skb->data,
+			 min_t(unsigned int, skb->len, 100));
+
+	if (priv->adapter->linkmode == WLAN_LINKMODE_802_11)
+		return process_rxed_802_11_packet(priv, skb);
+
+	p_rx_pkt = (struct rxpackethdr *) skb->data;
+	p_rx_pd = &p_rx_pkt->rx_pd;
+	if (p_rx_pd->rx_control & RxPD_MESH_FRAME)
+		SET_MESH_FRAME(skb);
+	else
+		UNSET_MESH_FRAME(skb);
+
+	lbs_dbg_hex("RX Data: Before chop rxpd", skb->data,
+		 min_t(unsigned int, skb->len, 100));
+
+	if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) {
+		lbs_pr_debug(1, "RX error: FRAME RECEIVED WITH BAD LENGTH\n");
+		priv->stats.rx_length_errors++;
+		ret = 0;
+		goto done;
+	}
+
+	/*
+	 * Check rxpd status and update 802.3 stat,
+	 */
+	if (!(p_rx_pd->status & MRVDRV_RXPD_STATUS_OK)) {
+		lbs_pr_debug(1, "RX error: frame received with bad status\n");
+		lbs_pr_alert("rxpd Not OK\n");
+		priv->stats.rx_errors++;
+		ret = 0;
+		goto done;
+	}
+
+	lbs_pr_debug(1, "RX Data: skb->len - sizeof(RxPd) = %d - %d = %d\n",
+	       skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd));
+
+	lbs_dbg_hex("RX Data: Dest", p_rx_pkt->eth803_hdr.dest_addr,
+		sizeof(p_rx_pkt->eth803_hdr.dest_addr));
+	lbs_dbg_hex("RX Data: Src", p_rx_pkt->eth803_hdr.src_addr,
+		sizeof(p_rx_pkt->eth803_hdr.src_addr));
+
+	if (memcmp(&p_rx_pkt->rfc1042_hdr,
+		   rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) {
+		/*
+		 *  Replace the 803 header and rfc1042 header (llc/snap) with an
+		 *    EthernetII header, keep the src/dst and snap_type (ethertype)
+		 *
+		 *  The firmware only passes up SNAP frames converting
+		 *    all RX Data from 802.11 to 802.2/LLC/SNAP frames.
+		 *
+		 *  To create the Ethernet II, just move the src, dst address right
+		 *    before the snap_type.
+		 */
+		p_ethhdr = (struct ethhdr *)
+		    ((u8 *) & p_rx_pkt->eth803_hdr
+		     + sizeof(p_rx_pkt->eth803_hdr) + sizeof(p_rx_pkt->rfc1042_hdr)
+		     - sizeof(p_rx_pkt->eth803_hdr.dest_addr)
+		     - sizeof(p_rx_pkt->eth803_hdr.src_addr)
+		     - sizeof(p_rx_pkt->rfc1042_hdr.snap_type));
+
+		memcpy(p_ethhdr->h_source, p_rx_pkt->eth803_hdr.src_addr,
+		       sizeof(p_ethhdr->h_source));
+		memcpy(p_ethhdr->h_dest, p_rx_pkt->eth803_hdr.dest_addr,
+		       sizeof(p_ethhdr->h_dest));
+
+		/* Chop off the rxpd + the excess memory from the 802.2/llc/snap header
+		 *   that was removed
+		 */
+		hdrchop = (u8 *) p_ethhdr - (u8 *) p_rx_pkt;
+	} else {
+		lbs_dbg_hex("RX Data: LLC/SNAP",
+			(u8 *) & p_rx_pkt->rfc1042_hdr,
+			sizeof(p_rx_pkt->rfc1042_hdr));
+
+		/* Chop off the rxpd */
+		hdrchop = (u8 *) & p_rx_pkt->eth803_hdr - (u8 *) p_rx_pkt;
+	}
+
+	/* Chop off the leading header bytes so the skb points to the start of
+	 *   either the reconstructed EthII frame or the 802.2/llc/snap frame
+	 */
+	skb_pull(skb, hdrchop);
+
+	/* Take the data rate from the rxpd structure
+	 * only if the rate is auto
+	 */
+	if (adapter->is_datarate_auto)
+		adapter->datarate = libertas_index_to_data_rate(p_rx_pd->rx_rate);
+
+	wlan_compute_rssi(priv, p_rx_pd);
+
+	lbs_pr_debug(1, "RX Data: size of actual packet = %d\n", skb->len);
+	if (libertas_upload_rx_packet(priv, skb)) {
+		lbs_pr_debug(1, "RX error: libertas_upload_rx_packet"
+		       " returns failure\n");
+		ret = -1;
+		goto done;
+	}
+	priv->stats.rx_bytes += skb->len;
+	priv->stats.rx_packets++;
+
+	ret = 0;
+done:
+	LEAVE();
+
+	return ret;
+}
+
+/**
+ *  @brief This function converts Tx/Rx rates from the Marvell WLAN format
+ *  (see Table 2 in Section 3.1) to IEEE80211_RADIOTAP_RATE units (500 Kb/s)
+ *
+ *  @param rate    Input rate
+ *  @return 	   Output Rate (0 if invalid)
+ */
+static u8 convert_mv_rate_to_radiotap(u8 rate)
+{
+	switch (rate) {
+	case 0:		/*   1 Mbps */
+		return 2;
+	case 1:		/*   2 Mbps */
+		return 4;
+	case 2:		/* 5.5 Mbps */
+		return 11;
+	case 3:		/*  11 Mbps */
+		return 22;
+	case 4:		/*   6 Mbps */
+		return 12;
+	case 5:		/*   9 Mbps */
+		return 18;
+	case 6:		/*  12 Mbps */
+		return 24;
+	case 7:		/*  18 Mbps */
+		return 36;
+	case 8:		/*  24 Mbps */
+		return 48;
+	case 9:		/*  36 Mbps */
+		return 72;
+	case 10:		/*  48 Mbps */
+		return 96;
+	case 11:		/*  54 Mbps */
+		return 108;
+	}
+	lbs_pr_alert( "Invalid Marvell WLAN rate (%i)\n", rate);
+	return 0;
+}
+
+/**
+ *  @brief This function processes a received 802.11 packet and forwards it
+ *  to kernel/upper layer
+ *
+ *  @param priv    A pointer to wlan_private
+ *  @param skb     A pointer to skb which includes the received packet
+ *  @return 	   0 or -1
+ */
+static int process_rxed_802_11_packet(wlan_private * priv, struct sk_buff *skb)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+
+	struct rx80211packethdr *p_rx_pkt;
+	struct rxpd *prxpd;
+	struct rx_radiotap_hdr radiotap_hdr;
+	struct rx_radiotap_hdr *pradiotap_hdr;
+
+	ENTER();
+
+	p_rx_pkt = (struct rx80211packethdr *) skb->data;
+	prxpd = &p_rx_pkt->rx_pd;
+
+	// lbs_dbg_hex("RX Data: Before chop rxpd", skb->data, min(skb->len, 100));
+
+	if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) {
+		lbs_pr_debug(1, "RX error: FRAME RECEIVED WITH BAD LENGTH\n");
+		priv->stats.rx_length_errors++;
+		ret = 0;
+		goto done;
+	}
+
+	/*
+	 * Check rxpd status and update 802.3 stat,
+	 */
+	if (!(prxpd->status & MRVDRV_RXPD_STATUS_OK)) {
+		//lbs_pr_debug(1, "RX error: frame received with bad status\n");
+		priv->stats.rx_errors++;
+	}
+
+	lbs_pr_debug(1, "RX Data: skb->len - sizeof(RxPd) = %d - %d = %d\n",
+	       skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd));
+
+	/* create the exported radio header */
+	switch (priv->adapter->radiomode) {
+	case WLAN_RADIOMODE_NONE:
+		/* no radio header */
+		/* chop the rxpd */
+		skb_pull(skb, sizeof(struct rxpd));
+		break;
+
+	case WLAN_RADIOMODE_RADIOTAP:
+		/* radiotap header */
+		radiotap_hdr.hdr.it_version = 0;
+		/* XXX must check this value for pad */
+		radiotap_hdr.hdr.it_pad = 0;
+		radiotap_hdr.hdr.it_len = sizeof(struct rx_radiotap_hdr);
+		radiotap_hdr.hdr.it_present = RX_RADIOTAP_PRESENT;
+		/* unknown values */
+		radiotap_hdr.flags = 0;
+		radiotap_hdr.chan_freq = 0;
+		radiotap_hdr.chan_flags = 0;
+		radiotap_hdr.antenna = 0;
+		/* known values */
+		radiotap_hdr.rate = convert_mv_rate_to_radiotap(prxpd->rx_rate);
+		/* XXX must check no carryout */
+		radiotap_hdr.antsignal = prxpd->snr + prxpd->nf;
+		radiotap_hdr.rx_flags = 0;
+		if (!(prxpd->status & MRVDRV_RXPD_STATUS_OK))
+			radiotap_hdr.rx_flags |= IEEE80211_RADIOTAP_F_RX_BADFCS;
+		//memset(radiotap_hdr.pad, 0x11, IEEE80211_RADIOTAP_HDRLEN - 18);
+
+		// lbs_dbg_hex1("RX radiomode packet BEF: ", skb->data, min(skb->len, 100));
+
+		/* chop the rxpd */
+		skb_pull(skb, sizeof(struct rxpd));
+
+		/* add space for the new radio header */
+		if ((skb_headroom(skb) < sizeof(struct rx_radiotap_hdr)) &&
+		    pskb_expand_head(skb, sizeof(struct rx_radiotap_hdr), 0,
+				     GFP_ATOMIC)) {
+			lbs_pr_alert( "%s: couldn't pskb_expand_head\n",
+			       __func__);
+		}
+
+		pradiotap_hdr =
+		    (struct rx_radiotap_hdr *)skb_push(skb,
+						     sizeof(struct
+							    rx_radiotap_hdr));
+		memcpy(pradiotap_hdr, &radiotap_hdr,
+		       sizeof(struct rx_radiotap_hdr));
+		//lbs_dbg_hex1("RX radiomode packet AFT: ", skb->data, min(skb->len, 100));
+		break;
+
+	default:
+		/* unknown header */
+		lbs_pr_alert( "Unknown radiomode (%i)\n",
+		       priv->adapter->radiomode);
+		/* don't export any header */
+		/* chop the rxpd */
+		skb_pull(skb, sizeof(struct rxpd));
+		break;
+	}
+
+	/* Take the data rate from the rxpd structure
+	 * only if the rate is auto
+	 */
+	if (adapter->is_datarate_auto) {
+		adapter->datarate = libertas_index_to_data_rate(prxpd->rx_rate);
+	}
+
+	wlan_compute_rssi(priv, prxpd);
+
+	lbs_pr_debug(1, "RX Data: size of actual packet = %d\n", skb->len);
+
+	if (libertas_upload_rx_packet(priv, skb)) {
+		lbs_pr_debug(1, "RX error: libertas_upload_rx_packet "
+			"returns failure\n");
+		ret = -1;
+		goto done;
+	}
+
+	priv->stats.rx_bytes += skb->len;
+	priv->stats.rx_packets++;
+
+	ret = 0;
+done:
+	LEAVE();
+
+	skb->protocol = __constant_htons(0x0019);	/* ETH_P_80211_RAW */
+
+	return (ret);
+}
diff --git a/drivers/net/wireless/libertas/sbi.h b/drivers/net/wireless/libertas/sbi.h
new file mode 100644
index 0000000..59d3a59c
--- /dev/null
+++ b/drivers/net/wireless/libertas/sbi.h
@@ -0,0 +1,40 @@
+/**
+  * This file contains IF layer definitions.
+  */
+
+#ifndef	_SBI_H_
+#define	_SBI_H_
+
+#include <linux/interrupt.h>
+
+#include "defs.h"
+
+/** INT status Bit Definition*/
+#define his_cmddnldrdy			0x01
+#define his_cardevent			0x02
+#define his_cmdupldrdy			0x04
+
+#ifndef DEV_NAME_LEN
+#define DEV_NAME_LEN			32
+#endif
+
+#define SBI_EVENT_CAUSE_SHIFT		3
+
+/* Probe and Check if the card is present*/
+int libertas_sbi_register_dev(wlan_private * priv);
+int libertas_sbi_unregister_dev(wlan_private *);
+int libertas_sbi_get_int_status(wlan_private * priv, u8 *);
+int libertas_sbi_register(void);
+void libertas_sbi_unregister(void);
+int libertas_sbi_prog_firmware(wlan_private *);
+
+int libertas_sbi_read_event_cause(wlan_private *);
+int libertas_sbi_host_to_card(wlan_private * priv, u8 type, u8 * payload, u16 nb);
+wlan_private *libertas_sbi_get_priv(void *card);
+
+#ifdef ENABLE_PM
+int libertas_sbi_suspend(wlan_private *);
+int libertas_sbi_resume(wlan_private *);
+#endif
+
+#endif				/* _SBI_H */
diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c
new file mode 100644
index 0000000..e187062
--- /dev/null
+++ b/drivers/net/wireless/libertas/scan.c
@@ -0,0 +1,2044 @@
+/* -*- mode: C; tab-width: 4; indent-tabs-mode: nil -*- */
+/* vi: set expandtab shiftwidth=4 tabstop=4 textwidth=78: */
+
+/**
+  * Functions implementing wlan scan IOCTL and firmware command APIs
+  *
+  * IOCTL handlers as well as command preperation and response routines
+  *  for sending scan commands to the firmware.
+  */
+#include <linux/ctype.h>
+#include <linux/if.h>
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+
+#include <net/ieee80211.h>
+#include <net/iw_handler.h>
+
+#include "host.h"
+#include "decl.h"
+#include "dev.h"
+#include "scan.h"
+
+//! Approximate amount of data needed to pass a scan result back to iwlist
+#define MAX_SCAN_CELL_SIZE  (IW_EV_ADDR_LEN             \
+                             + IW_ESSID_MAX_SIZE        \
+                             + IW_EV_UINT_LEN           \
+                             + IW_EV_FREQ_LEN           \
+                             + IW_EV_QUAL_LEN           \
+                             + IW_ESSID_MAX_SIZE        \
+                             + IW_EV_PARAM_LEN          \
+                             + 40)	/* 40 for WPAIE */
+
+//! Memory needed to store a max sized channel List TLV for a firmware scan
+#define CHAN_TLV_MAX_SIZE  (sizeof(struct mrvlietypesheader)    \
+                            + (MRVDRV_MAX_CHANNELS_PER_SCAN     \
+                               * sizeof(struct chanscanparamset)))
+
+//! Memory needed to store a max number/size SSID TLV for a firmware scan
+#define SSID_TLV_MAX_SIZE  (1 * sizeof(struct mrvlietypes_ssidparamset))
+
+//! Maximum memory needed for a wlan_scan_cmd_config with all TLVs at max
+#define MAX_SCAN_CFG_ALLOC (sizeof(struct wlan_scan_cmd_config)  \
+                            + sizeof(struct mrvlietypes_numprobes)   \
+                            + CHAN_TLV_MAX_SIZE                 \
+                            + SSID_TLV_MAX_SIZE)
+
+//! The maximum number of channels the firmware can scan per command
+#define MRVDRV_MAX_CHANNELS_PER_SCAN   14
+
+/**
+ * @brief Number of channels to scan per firmware scan command issuance.
+ *
+ *  Number restricted to prevent hitting the limit on the amount of scan data
+ *  returned in a single firmware scan command.
+ */
+#define MRVDRV_CHANNELS_PER_SCAN_CMD   4
+
+//! Scan time specified in the channel TLV for each channel for passive scans
+#define MRVDRV_PASSIVE_SCAN_CHAN_TIME  100
+
+//! Scan time specified in the channel TLV for each channel for active scans
+#define MRVDRV_ACTIVE_SCAN_CHAN_TIME   100
+
+//! Macro to enable/disable SSID checking before storing a scan table
+#ifdef DISCARD_BAD_SSID
+#define CHECK_SSID_IS_VALID(x) ssid_valid(&bssidEntry.ssid)
+#else
+#define CHECK_SSID_IS_VALID(x) 1
+#endif
+
+/**
+ *  @brief Check if a scanned network compatible with the driver settings
+ *
+ *   WEP     WPA     WPA2    ad-hoc  encrypt                      Network
+ * enabled enabled  enabled   AES     mode   privacy  WPA  WPA2  Compatible
+ *    0       0        0       0      NONE      0      0    0   yes No security
+ *    1       0        0       0      NONE      1      0    0   yes Static WEP
+ *    0       1        0       0       x        1x     1    x   yes WPA
+ *    0       0        1       0       x        1x     x    1   yes WPA2
+ *    0       0        0       1      NONE      1      0    0   yes Ad-hoc AES
+ *    0       0        0       0     !=NONE     1      0    0   yes Dynamic WEP
+ *
+ *
+ *  @param adapter A pointer to wlan_adapter
+ *  @param index   Index in scantable to check against current driver settings
+ *  @param mode    Network mode: Infrastructure or IBSS
+ *
+ *  @return        Index in scantable, or error code if negative
+ */
+static int is_network_compatible(wlan_adapter * adapter, int index, int mode)
+{
+	ENTER();
+
+	if (adapter->scantable[index].inframode == mode) {
+		if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled
+		    && !adapter->secinfo.WPAenabled
+		    && !adapter->secinfo.WPA2enabled
+		    && adapter->scantable[index].wpa_supplicant.wpa_ie[0] !=
+		    WPA_IE
+		    && adapter->scantable[index].wpa2_supplicant.wpa_ie[0] !=
+		    WPA2_IE && adapter->secinfo.Encryptionmode == CIPHER_NONE
+		    && !adapter->scantable[index].privacy) {
+			/* no security */
+			LEAVE();
+			return index;
+		} else if (adapter->secinfo.WEPstatus == wlan802_11WEPenabled
+			   && !adapter->secinfo.WPAenabled
+			   && !adapter->secinfo.WPA2enabled
+			   && adapter->scantable[index].privacy) {
+			/* static WEP enabled */
+			LEAVE();
+			return index;
+		} else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled
+			   && adapter->secinfo.WPAenabled
+			   && !adapter->secinfo.WPA2enabled
+			   && (adapter->scantable[index].wpa_supplicant.
+			       wpa_ie[0]
+			       == WPA_IE)
+			   /* privacy bit may NOT be set in some APs like LinkSys WRT54G
+			      && adapter->scantable[index].privacy */
+		    ) {
+			/* WPA enabled */
+            lbs_pr_debug(1,
+			       "is_network_compatible() WPA: index=%d wpa_ie=%#x "
+			       "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x "
+			       "privacy=%#x\n", index,
+			       adapter->scantable[index].wpa_supplicant.
+			       wpa_ie[0],
+			       adapter->scantable[index].wpa2_supplicant.
+			       wpa_ie[0],
+			       (adapter->secinfo.WEPstatus ==
+				wlan802_11WEPenabled) ? "e" : "d",
+			       (adapter->secinfo.WPAenabled) ? "e" : "d",
+			       (adapter->secinfo.WPA2enabled) ? "e" : "d",
+			       adapter->secinfo.Encryptionmode,
+			       adapter->scantable[index].privacy);
+			LEAVE();
+			return index;
+		} else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled
+			   && !adapter->secinfo.WPAenabled
+			   && adapter->secinfo.WPA2enabled
+			   && (adapter->scantable[index].wpa2_supplicant.
+			       wpa_ie[0]
+			       == WPA2_IE)
+			   /* privacy bit may NOT be set in some APs like LinkSys WRT54G
+			      && adapter->scantable[index].privacy */
+		    ) {
+			/* WPA2 enabled */
+            lbs_pr_debug(1,
+			       "is_network_compatible() WPA2: index=%d wpa_ie=%#x "
+			       "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x "
+			       "privacy=%#x\n", index,
+			       adapter->scantable[index].wpa_supplicant.
+			       wpa_ie[0],
+			       adapter->scantable[index].wpa2_supplicant.
+			       wpa_ie[0],
+			       (adapter->secinfo.WEPstatus ==
+				wlan802_11WEPenabled) ? "e" : "d",
+			       (adapter->secinfo.WPAenabled) ? "e" : "d",
+			       (adapter->secinfo.WPA2enabled) ? "e" : "d",
+			       adapter->secinfo.Encryptionmode,
+			       adapter->scantable[index].privacy);
+			LEAVE();
+			return index;
+		} else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled
+			   && !adapter->secinfo.WPAenabled
+			   && !adapter->secinfo.WPA2enabled
+			   && (adapter->scantable[index].wpa_supplicant.
+			       wpa_ie[0]
+			       != WPA_IE)
+			   && (adapter->scantable[index].wpa2_supplicant.
+			       wpa_ie[0]
+			       != WPA2_IE)
+			   && adapter->secinfo.Encryptionmode != CIPHER_NONE
+			   && adapter->scantable[index].privacy) {
+			/* dynamic WEP enabled */
+            lbs_pr_debug(1,
+			       "is_network_compatible() dynamic WEP: index=%d "
+			       "wpa_ie=%#x wpa2_ie=%#x Encmode=%#x privacy=%#x\n",
+			       index,
+			       adapter->scantable[index].wpa_supplicant.
+			       wpa_ie[0],
+			       adapter->scantable[index].wpa2_supplicant.
+			       wpa_ie[0], adapter->secinfo.Encryptionmode,
+			       adapter->scantable[index].privacy);
+			LEAVE();
+			return index;
+		}
+
+		/* security doesn't match */
+        lbs_pr_debug(1,
+		       "is_network_compatible() FAILED: index=%d wpa_ie=%#x "
+		       "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x privacy=%#x\n",
+		       index,
+		       adapter->scantable[index].wpa_supplicant.wpa_ie[0],
+		       adapter->scantable[index].wpa2_supplicant.wpa_ie[0],
+		       (adapter->secinfo.WEPstatus ==
+			wlan802_11WEPenabled) ? "e" : "d",
+		       (adapter->secinfo.WPAenabled) ? "e" : "d",
+		       (adapter->secinfo.WPA2enabled) ? "e" : "d",
+		       adapter->secinfo.Encryptionmode,
+		       adapter->scantable[index].privacy);
+		LEAVE();
+		return -ECONNREFUSED;
+	}
+
+	/* mode doesn't match */
+	LEAVE();
+	return -ENETUNREACH;
+}
+
+/**
+ *  @brief This function validates a SSID as being able to be printed
+ *
+ *  @param pssid   SSID structure to validate
+ *
+ *  @return        TRUE or FALSE
+ */
+static u8 ssid_valid(struct WLAN_802_11_SSID *pssid)
+{
+	int ssididx;
+
+	for (ssididx = 0; ssididx < pssid->ssidlength; ssididx++) {
+		if (!isprint(pssid->ssid[ssididx])) {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+/**
+ *  @brief Post process the scan table after a new scan command has completed
+ *
+ *  Inspect each entry of the scan table and try to find an entry that
+ *    matches our current associated/joined network from the scan.  If
+ *    one is found, update the stored copy of the bssdescriptor for our
+ *    current network.
+ *
+ *  Debug dump the current scan table contents if compiled accordingly.
+ *
+ *  @param priv   A pointer to wlan_private structure
+ *
+ *  @return       void
+ */
+static void wlan_scan_process_results(wlan_private * priv)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int foundcurrent;
+	int i;
+
+	foundcurrent = 0;
+
+	if (adapter->connect_status == libertas_connected) {
+		/* try to find the current BSSID in the new scan list */
+		for (i = 0; i < adapter->numinscantable; i++) {
+			if (!libertas_SSID_cmp(&adapter->scantable[i].ssid,
+				     &adapter->curbssparams.ssid) &&
+			    !memcmp(adapter->curbssparams.bssid,
+				    adapter->scantable[i].macaddress,
+				    ETH_ALEN)) {
+				foundcurrent = 1;
+			}
+		}
+
+		if (foundcurrent) {
+			/* Make a copy of current BSSID descriptor */
+			memcpy(&adapter->curbssparams.bssdescriptor,
+			       &adapter->scantable[i],
+			       sizeof(adapter->curbssparams.bssdescriptor));
+		}
+	}
+
+	for (i = 0; i < adapter->numinscantable; i++) {
+		lbs_pr_debug(1, "Scan:(%02d) %02x:%02x:%02x:%02x:%02x:%02x, "
+		       "RSSI[%03d], SSID[%s]\n",
+		       i,
+		       adapter->scantable[i].macaddress[0],
+		       adapter->scantable[i].macaddress[1],
+		       adapter->scantable[i].macaddress[2],
+		       adapter->scantable[i].macaddress[3],
+		       adapter->scantable[i].macaddress[4],
+		       adapter->scantable[i].macaddress[5],
+		       (s32) adapter->scantable[i].rssi,
+		       adapter->scantable[i].ssid.ssid);
+	}
+}
+
+/**
+ *  @brief Create a channel list for the driver to scan based on region info
+ *
+ *  Use the driver region/band information to construct a comprehensive list
+ *    of channels to scan.  This routine is used for any scan that is not
+ *    provided a specific channel list to scan.
+ *
+ *  @param priv          A pointer to wlan_private structure
+ *  @param scanchanlist  Output parameter: resulting channel list to scan
+ *  @param filteredscan  Flag indicating whether or not a BSSID or SSID filter
+ *                       is being sent in the command to firmware.  Used to
+ *                       increase the number of channels sent in a scan
+ *                       command and to disable the firmware channel scan
+ *                       filter.
+ *
+ *  @return              void
+ */
+static void wlan_scan_create_channel_list(wlan_private * priv,
+					  struct chanscanparamset * scanchanlist,
+					  u8 filteredscan)
+{
+
+	wlan_adapter *adapter = priv->adapter;
+	struct region_channel *scanregion;
+	struct chan_freq_power *cfp;
+	int rgnidx;
+	int chanidx;
+	int nextchan;
+	u8 scantype;
+
+	chanidx = 0;
+
+	/* Set the default scan type to the user specified type, will later
+	 *   be changed to passive on a per channel basis if restricted by
+	 *   regulatory requirements (11d or 11h)
+	 */
+	scantype = adapter->scantype;
+
+	for (rgnidx = 0; rgnidx < ARRAY_SIZE(adapter->region_channel); rgnidx++) {
+		if (priv->adapter->enable11d &&
+		    adapter->connect_status != libertas_connected) {
+			/* Scan all the supported chan for the first scan */
+			if (!adapter->universal_channel[rgnidx].valid)
+				continue;
+			scanregion = &adapter->universal_channel[rgnidx];
+
+			/* clear the parsed_region_chan for the first scan */
+			memset(&adapter->parsed_region_chan, 0x00,
+			       sizeof(adapter->parsed_region_chan));
+		} else {
+			if (!adapter->region_channel[rgnidx].valid)
+				continue;
+			scanregion = &adapter->region_channel[rgnidx];
+		}
+
+		for (nextchan = 0;
+		     nextchan < scanregion->nrcfp; nextchan++, chanidx++) {
+
+			cfp = scanregion->CFP + nextchan;
+
+			if (priv->adapter->enable11d) {
+				scantype =
+				    libertas_get_scan_type_11d(cfp->channel,
+							   &adapter->
+							   parsed_region_chan);
+			}
+
+			switch (scanregion->band) {
+			case BAND_B:
+			case BAND_G:
+			default:
+				scanchanlist[chanidx].radiotype =
+				    cmd_scan_radio_type_bg;
+				break;
+			}
+
+			if (scantype == cmd_scan_type_passive) {
+				scanchanlist[chanidx].maxscantime =
+				    cpu_to_le16
+				    (MRVDRV_PASSIVE_SCAN_CHAN_TIME);
+				scanchanlist[chanidx].chanscanmode.passivescan =
+				    1;
+			} else {
+				scanchanlist[chanidx].maxscantime =
+				    cpu_to_le16
+				    (MRVDRV_ACTIVE_SCAN_CHAN_TIME);
+				scanchanlist[chanidx].chanscanmode.passivescan =
+				    0;
+			}
+
+			scanchanlist[chanidx].channumber = cfp->channel;
+
+			if (filteredscan) {
+				scanchanlist[chanidx].chanscanmode.
+				    disablechanfilt = 1;
+			}
+		}
+	}
+}
+
+/**
+ *  @brief Construct a wlan_scan_cmd_config structure to use in issue scan cmds
+ *
+ *  Application layer or other functions can invoke wlan_scan_networks
+ *    with a scan configuration supplied in a wlan_ioctl_user_scan_cfg struct.
+ *    This structure is used as the basis of one or many wlan_scan_cmd_config
+ *    commands that are sent to the command processing module and sent to
+ *    firmware.
+ *
+ *  Create a wlan_scan_cmd_config based on the following user supplied
+ *    parameters (if present):
+ *             - SSID filter
+ *             - BSSID filter
+ *             - Number of Probes to be sent
+ *             - channel list
+ *
+ *  If the SSID or BSSID filter is not present, disable/clear the filter.
+ *  If the number of probes is not set, use the adapter default setting
+ *  Qualify the channel
+ *
+ *  @param priv             A pointer to wlan_private structure
+ *  @param puserscanin      NULL or pointer to scan configuration parameters
+ *  @param ppchantlvout     Output parameter: Pointer to the start of the
+ *                          channel TLV portion of the output scan config
+ *  @param pscanchanlist    Output parameter: Pointer to the resulting channel
+ *                          list to scan
+ *  @param pmaxchanperscan  Output parameter: Number of channels to scan for
+ *                          each issuance of the firmware scan command
+ *  @param pfilteredscan    Output parameter: Flag indicating whether or not
+ *                          a BSSID or SSID filter is being sent in the
+ *                          command to firmware.  Used to increase the number
+ *                          of channels sent in a scan command and to
+ *                          disable the firmware channel scan filter.
+ *  @param pscancurrentonly Output parameter: Flag indicating whether or not
+ *                          we are only scanning our current active channel
+ *
+ *  @return                 resulting scan configuration
+ */
+static struct wlan_scan_cmd_config *
+wlan_scan_setup_scan_config(wlan_private * priv,
+			    const struct wlan_ioctl_user_scan_cfg * puserscanin,
+			    struct mrvlietypes_chanlistparamset ** ppchantlvout,
+			    struct chanscanparamset * pscanchanlist,
+			    int *pmaxchanperscan,
+			    u8 * pfilteredscan,
+			    u8 * pscancurrentonly)
+{
+	wlan_adapter *adapter = priv->adapter;
+	const u8 zeromac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+	struct mrvlietypes_numprobes *pnumprobestlv;
+	struct mrvlietypes_ssidparamset *pssidtlv;
+	struct wlan_scan_cmd_config * pscancfgout = NULL;
+	u8 *ptlvpos;
+	u16 numprobes;
+	u16 ssidlen;
+	int chanidx;
+	int scantype;
+	int scandur;
+	int channel;
+	int radiotype;
+
+	pscancfgout = kzalloc(MAX_SCAN_CFG_ALLOC, GFP_KERNEL);
+	if (pscancfgout == NULL)
+		goto out;
+
+	/* The tlvbufferlen is calculated for each scan command.  The TLVs added
+	 *   in this routine will be preserved since the routine that sends
+	 *   the command will append channelTLVs at *ppchantlvout.  The difference
+	 *   between the *ppchantlvout and the tlvbuffer start will be used
+	 *   to calculate the size of anything we add in this routine.
+	 */
+	pscancfgout->tlvbufferlen = 0;
+
+	/* Running tlv pointer.  Assigned to ppchantlvout at end of function
+	 *  so later routines know where channels can be added to the command buf
+	 */
+	ptlvpos = pscancfgout->tlvbuffer;
+
+	/*
+	 * Set the initial scan paramters for progressive scanning.  If a specific
+	 *   BSSID or SSID is used, the number of channels in the scan command
+	 *   will be increased to the absolute maximum
+	 */
+	*pmaxchanperscan = MRVDRV_CHANNELS_PER_SCAN_CMD;
+
+	/* Initialize the scan as un-filtered by firmware, set to TRUE below if
+	 *   a SSID or BSSID filter is sent in the command
+	 */
+	*pfilteredscan = 0;
+
+	/* Initialize the scan as not being only on the current channel.  If
+	 *   the channel list is customized, only contains one channel, and
+	 *   is the active channel, this is set true and data flow is not halted.
+	 */
+	*pscancurrentonly = 0;
+
+	if (puserscanin) {
+
+		/* Set the bss type scan filter, use adapter setting if unset */
+		pscancfgout->bsstype =
+		    (puserscanin->bsstype ? puserscanin->bsstype : adapter->
+		     scanmode);
+
+		/* Set the number of probes to send, use adapter setting if unset */
+		numprobes = (puserscanin->numprobes ? puserscanin->numprobes :
+			     adapter->scanprobes);
+
+		/*
+		 * Set the BSSID filter to the incoming configuration,
+		 *   if non-zero.  If not set, it will remain disabled (all zeros).
+		 */
+		memcpy(pscancfgout->specificBSSID,
+		       puserscanin->specificBSSID,
+		       sizeof(pscancfgout->specificBSSID));
+
+		ssidlen = strlen(puserscanin->specificSSID);
+
+		if (ssidlen) {
+			pssidtlv =
+			    (struct mrvlietypes_ssidparamset *) pscancfgout->
+			    tlvbuffer;
+			pssidtlv->header.type = cpu_to_le16(TLV_TYPE_SSID);
+			pssidtlv->header.len = cpu_to_le16(ssidlen);
+			memcpy(pssidtlv->ssid, puserscanin->specificSSID,
+			       ssidlen);
+			ptlvpos += sizeof(pssidtlv->header) + ssidlen;
+		}
+
+		/*
+		 *  The default number of channels sent in the command is low to
+		 *    ensure the response buffer from the firmware does not truncate
+		 *    scan results.  That is not an issue with an SSID or BSSID
+		 *    filter applied to the scan results in the firmware.
+		 */
+		if (ssidlen || (memcmp(pscancfgout->specificBSSID,
+				       &zeromac, sizeof(zeromac)) != 0)) {
+			*pmaxchanperscan = MRVDRV_MAX_CHANNELS_PER_SCAN;
+			*pfilteredscan = 1;
+		}
+	} else {
+		pscancfgout->bsstype = adapter->scanmode;
+		numprobes = adapter->scanprobes;
+	}
+
+	/* If the input config or adapter has the number of Probes set, add tlv */
+	if (numprobes) {
+		pnumprobestlv = (struct mrvlietypes_numprobes *) ptlvpos;
+		pnumprobestlv->header.type =
+		    cpu_to_le16(TLV_TYPE_NUMPROBES);
+		pnumprobestlv->header.len = sizeof(pnumprobestlv->numprobes);
+		pnumprobestlv->numprobes = cpu_to_le16(numprobes);
+
+		ptlvpos +=
+		    sizeof(pnumprobestlv->header) + pnumprobestlv->header.len;
+
+		pnumprobestlv->header.len =
+		    cpu_to_le16(pnumprobestlv->header.len);
+	}
+
+	/*
+	 * Set the output for the channel TLV to the address in the tlv buffer
+	 *   past any TLVs that were added in this fuction (SSID, numprobes).
+	 *   channel TLVs will be added past this for each scan command, preserving
+	 *   the TLVs that were previously added.
+	 */
+	*ppchantlvout = (struct mrvlietypes_chanlistparamset *) ptlvpos;
+
+	if (puserscanin && puserscanin->chanlist[0].channumber) {
+
+		lbs_pr_debug(1, "Scan: Using supplied channel list\n");
+
+		for (chanidx = 0;
+		     chanidx < WLAN_IOCTL_USER_SCAN_CHAN_MAX
+		     && puserscanin->chanlist[chanidx].channumber; chanidx++) {
+
+			channel = puserscanin->chanlist[chanidx].channumber;
+			(pscanchanlist + chanidx)->channumber = channel;
+
+			radiotype = puserscanin->chanlist[chanidx].radiotype;
+			(pscanchanlist + chanidx)->radiotype = radiotype;
+
+			scantype = puserscanin->chanlist[chanidx].scantype;
+
+			if (scantype == cmd_scan_type_passive) {
+				(pscanchanlist +
+				 chanidx)->chanscanmode.passivescan = 1;
+			} else {
+				(pscanchanlist +
+				 chanidx)->chanscanmode.passivescan = 0;
+			}
+
+			if (puserscanin->chanlist[chanidx].scantime) {
+				scandur =
+				    puserscanin->chanlist[chanidx].scantime;
+			} else {
+				if (scantype == cmd_scan_type_passive) {
+					scandur = MRVDRV_PASSIVE_SCAN_CHAN_TIME;
+				} else {
+					scandur = MRVDRV_ACTIVE_SCAN_CHAN_TIME;
+				}
+			}
+
+			(pscanchanlist + chanidx)->minscantime =
+			    cpu_to_le16(scandur);
+			(pscanchanlist + chanidx)->maxscantime =
+			    cpu_to_le16(scandur);
+		}
+
+		/* Check if we are only scanning the current channel */
+		if ((chanidx == 1) && (puserscanin->chanlist[0].channumber
+				       ==
+				       priv->adapter->curbssparams.channel)) {
+			*pscancurrentonly = 1;
+			lbs_pr_debug(1, "Scan: Scanning current channel only");
+		}
+
+	} else {
+		lbs_pr_debug(1, "Scan: Creating full region channel list\n");
+		wlan_scan_create_channel_list(priv, pscanchanlist,
+					      *pfilteredscan);
+	}
+
+out:
+	return pscancfgout;
+}
+
+/**
+ *  @brief Construct and send multiple scan config commands to the firmware
+ *
+ *  Previous routines have created a wlan_scan_cmd_config with any requested
+ *   TLVs.  This function splits the channel TLV into maxchanperscan lists
+ *   and sends the portion of the channel TLV along with the other TLVs
+ *   to the wlan_cmd routines for execution in the firmware.
+ *
+ *  @param priv            A pointer to wlan_private structure
+ *  @param maxchanperscan  Maximum number channels to be included in each
+ *                         scan command sent to firmware
+ *  @param filteredscan    Flag indicating whether or not a BSSID or SSID
+ *                         filter is being used for the firmware command
+ *                         scan command sent to firmware
+ *  @param pscancfgout     Scan configuration used for this scan.
+ *  @param pchantlvout     Pointer in the pscancfgout where the channel TLV
+ *                         should start.  This is past any other TLVs that
+ *                         must be sent down in each firmware command.
+ *  @param pscanchanlist   List of channels to scan in maxchanperscan segments
+ *
+ *  @return                0 or error return otherwise
+ */
+static int wlan_scan_channel_list(wlan_private * priv,
+				  int maxchanperscan,
+				  u8 filteredscan,
+				  struct wlan_scan_cmd_config * pscancfgout,
+				  struct mrvlietypes_chanlistparamset * pchantlvout,
+				  struct chanscanparamset * pscanchanlist)
+{
+	struct chanscanparamset *ptmpchan;
+	struct chanscanparamset *pstartchan;
+	u8 scanband;
+	int doneearly;
+	int tlvidx;
+	int ret = 0;
+
+	ENTER();
+
+	if (pscancfgout == 0 || pchantlvout == 0 || pscanchanlist == 0) {
+		lbs_pr_debug(1, "Scan: Null detect: %p, %p, %p\n",
+		       pscancfgout, pchantlvout, pscanchanlist);
+		return -1;
+	}
+
+	pchantlvout->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
+
+	/* Set the temp channel struct pointer to the start of the desired list */
+	ptmpchan = pscanchanlist;
+
+	/* Loop through the desired channel list, sending a new firmware scan
+	 *   commands for each maxchanperscan channels (or for 1,6,11 individually
+	 *   if configured accordingly)
+	 */
+	while (ptmpchan->channumber) {
+
+		tlvidx = 0;
+		pchantlvout->header.len = 0;
+		scanband = ptmpchan->radiotype;
+		pstartchan = ptmpchan;
+		doneearly = 0;
+
+		/* Construct the channel TLV for the scan command.  Continue to
+		 *  insert channel TLVs until:
+		 *    - the tlvidx hits the maximum configured per scan command
+		 *    - the next channel to insert is 0 (end of desired channel list)
+		 *    - doneearly is set (controlling individual scanning of 1,6,11)
+		 */
+		while (tlvidx < maxchanperscan && ptmpchan->channumber
+		       && !doneearly) {
+
+            lbs_pr_debug(1,
+                    "Scan: Chan(%3d), Radio(%d), mode(%d,%d), Dur(%d)\n",
+                ptmpchan->channumber, ptmpchan->radiotype,
+                ptmpchan->chanscanmode.passivescan,
+                ptmpchan->chanscanmode.disablechanfilt,
+                ptmpchan->maxscantime);
+
+			/* Copy the current channel TLV to the command being prepared */
+			memcpy(pchantlvout->chanscanparam + tlvidx,
+			       ptmpchan, sizeof(pchantlvout->chanscanparam));
+
+			/* Increment the TLV header length by the size appended */
+			pchantlvout->header.len +=
+			    sizeof(pchantlvout->chanscanparam);
+
+			/*
+			 *  The tlv buffer length is set to the number of bytes of the
+			 *    between the channel tlv pointer and the start of the
+			 *    tlv buffer.  This compensates for any TLVs that were appended
+			 *    before the channel list.
+			 */
+			pscancfgout->tlvbufferlen = ((u8 *) pchantlvout
+						     - pscancfgout->tlvbuffer);
+
+			/*  Add the size of the channel tlv header and the data length */
+			pscancfgout->tlvbufferlen +=
+			    (sizeof(pchantlvout->header)
+			     + pchantlvout->header.len);
+
+			/* Increment the index to the channel tlv we are constructing */
+			tlvidx++;
+
+			doneearly = 0;
+
+			/* Stop the loop if the *current* channel is in the 1,6,11 set
+			 *   and we are not filtering on a BSSID or SSID.
+			 */
+			if (!filteredscan && (ptmpchan->channumber == 1
+					      || ptmpchan->channumber == 6
+					      || ptmpchan->channumber == 11)) {
+				doneearly = 1;
+			}
+
+			/* Increment the tmp pointer to the next channel to be scanned */
+			ptmpchan++;
+
+			/* Stop the loop if the *next* channel is in the 1,6,11 set.
+			 *  This will cause it to be the only channel scanned on the next
+			 *  interation
+			 */
+			if (!filteredscan && (ptmpchan->channumber == 1
+					      || ptmpchan->channumber == 6
+					      || ptmpchan->channumber == 11)) {
+				doneearly = 1;
+			}
+		}
+
+		/* Send the scan command to the firmware with the specified cfg */
+		ret = libertas_prepare_and_send_command(priv, cmd_802_11_scan, 0,
+					    0, 0, pscancfgout);
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief Internal function used to start a scan based on an input config
+ *
+ *  Use the input user scan configuration information when provided in
+ *    order to send the appropriate scan commands to firmware to populate or
+ *    update the internal driver scan table
+ *
+ *  @param priv          A pointer to wlan_private structure
+ *  @param puserscanin   Pointer to the input configuration for the requested
+ *                       scan.
+ *
+ *  @return              0 or < 0 if error
+ */
+int wlan_scan_networks(wlan_private * priv,
+			      const struct wlan_ioctl_user_scan_cfg * puserscanin)
+{
+	wlan_adapter *adapter = priv->adapter;
+	struct mrvlietypes_chanlistparamset *pchantlvout;
+	struct chanscanparamset * scan_chan_list = NULL;
+	struct wlan_scan_cmd_config * scan_cfg = NULL;
+	u8 keeppreviousscan;
+	u8 filteredscan;
+	u8 scancurrentchanonly;
+	int maxchanperscan;
+	int ret;
+
+	ENTER();
+
+	scan_chan_list = kzalloc(sizeof(struct chanscanparamset) *
+				WLAN_IOCTL_USER_SCAN_CHAN_MAX, GFP_KERNEL);
+	if (scan_chan_list == NULL) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	scan_cfg = wlan_scan_setup_scan_config(priv,
+					       puserscanin,
+					       &pchantlvout,
+					       scan_chan_list,
+					       &maxchanperscan,
+					       &filteredscan,
+					       &scancurrentchanonly);
+	if (scan_cfg == NULL) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	keeppreviousscan = 0;
+
+	if (puserscanin) {
+		keeppreviousscan = puserscanin->keeppreviousscan;
+	}
+
+	if (!keeppreviousscan) {
+		memset(adapter->scantable, 0x00,
+		       sizeof(struct bss_descriptor) * MRVDRV_MAX_BSSID_LIST);
+		adapter->numinscantable = 0;
+	}
+
+	/* Keep the data path active if we are only scanning our current channel */
+	if (!scancurrentchanonly) {
+		netif_stop_queue(priv->wlan_dev.netdev);
+		netif_carrier_off(priv->wlan_dev.netdev);
+	}
+
+	ret = wlan_scan_channel_list(priv,
+				     maxchanperscan,
+				     filteredscan,
+				     scan_cfg,
+				     pchantlvout,
+				     scan_chan_list);
+
+	/*  Process the resulting scan table:
+	 *    - Remove any bad ssids
+	 *    - Update our current BSS information from scan data
+	 */
+	wlan_scan_process_results(priv);
+
+	if (priv->adapter->connect_status == libertas_connected) {
+		netif_carrier_on(priv->wlan_dev.netdev);
+		netif_wake_queue(priv->wlan_dev.netdev);
+	}
+
+out:
+	if (scan_cfg)
+		kfree(scan_cfg);
+
+	if (scan_chan_list)
+		kfree(scan_chan_list);
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief Inspect the scan response buffer for pointers to expected TLVs
+ *
+ *  TLVs can be included at the end of the scan response BSS information.
+ *    Parse the data in the buffer for pointers to TLVs that can potentially
+ *    be passed back in the response
+ *
+ *  @param ptlv        Pointer to the start of the TLV buffer to parse
+ *  @param tlvbufsize  size of the TLV buffer
+ *  @param ptsftlv     Output parameter: Pointer to the TSF TLV if found
+ *
+ *  @return            void
+ */
+static
+void wlan_ret_802_11_scan_get_tlv_ptrs(struct mrvlietypes_data * ptlv,
+				       int tlvbufsize,
+				       struct mrvlietypes_tsftimestamp ** ptsftlv)
+{
+	struct mrvlietypes_data *pcurrenttlv;
+	int tlvbufleft;
+	u16 tlvtype;
+	u16 tlvlen;
+
+	pcurrenttlv = ptlv;
+	tlvbufleft = tlvbufsize;
+	*ptsftlv = NULL;
+
+	lbs_pr_debug(1, "SCAN_RESP: tlvbufsize = %d\n", tlvbufsize);
+	lbs_dbg_hex("SCAN_RESP: TLV Buf", (u8 *) ptlv, tlvbufsize);
+
+	while (tlvbufleft >= sizeof(struct mrvlietypesheader)) {
+		tlvtype = le16_to_cpu(pcurrenttlv->header.type);
+		tlvlen = le16_to_cpu(pcurrenttlv->header.len);
+
+		switch (tlvtype) {
+		case TLV_TYPE_TSFTIMESTAMP:
+			*ptsftlv = (struct mrvlietypes_tsftimestamp *) pcurrenttlv;
+			break;
+
+		default:
+			lbs_pr_debug(1, "SCAN_RESP: Unhandled TLV = %d\n",
+			       tlvtype);
+			/* Give up, this seems corrupted */
+			return;
+		}		/* switch */
+
+		tlvbufleft -= (sizeof(ptlv->header) + tlvlen);
+		pcurrenttlv =
+		    (struct mrvlietypes_data *) (pcurrenttlv->Data + tlvlen);
+	}			/* while */
+}
+
+/**
+ *  @brief Interpret a BSS scan response returned from the firmware
+ *
+ *  Parse the various fixed fields and IEs passed back for a a BSS probe
+ *   response or beacon from the scan command.  Record information as needed
+ *   in the scan table struct bss_descriptor for that entry.
+ *
+ *  @param pBSSIDEntry  Output parameter: Pointer to the BSS Entry
+ *
+ *  @return             0 or -1
+ */
+static int InterpretBSSDescriptionWithIE(struct bss_descriptor * pBSSEntry,
+					 u8 ** pbeaconinfo, int *bytesleft)
+{
+	enum ieeetypes_elementid elemID;
+	struct ieeetypes_fhparamset *pFH;
+	struct ieeetypes_dsparamset *pDS;
+	struct ieeetypes_cfparamset *pCF;
+	struct ieeetypes_ibssparamset *pibss;
+	struct ieeetypes_capinfo *pcap;
+	struct WLAN_802_11_FIXED_IEs fixedie;
+	u8 *pcurrentptr;
+	u8 *pRate;
+	u8 elemlen;
+	u8 bytestocopy;
+	u8 ratesize;
+	u16 beaconsize;
+	u8 founddatarateie;
+	int bytesleftforcurrentbeacon;
+
+	struct WPA_SUPPLICANT *pwpa_supplicant;
+	struct WPA_SUPPLICANT *pwpa2_supplicant;
+	struct IE_WPA *pIe;
+	const u8 oui01[4] = { 0x00, 0x50, 0xf2, 0x01 };
+
+	struct ieeetypes_countryinfoset *pcountryinfo;
+
+	ENTER();
+
+	founddatarateie = 0;
+	ratesize = 0;
+	beaconsize = 0;
+
+	if (*bytesleft >= sizeof(beaconsize)) {
+		/* Extract & convert beacon size from the command buffer */
+		memcpy(&beaconsize, *pbeaconinfo, sizeof(beaconsize));
+		beaconsize = le16_to_cpu(beaconsize);
+		*bytesleft -= sizeof(beaconsize);
+		*pbeaconinfo += sizeof(beaconsize);
+	}
+
+	if (beaconsize == 0 || beaconsize > *bytesleft) {
+
+		*pbeaconinfo += *bytesleft;
+		*bytesleft = 0;
+
+		return -1;
+	}
+
+	/* Initialize the current working beacon pointer for this BSS iteration */
+	pcurrentptr = *pbeaconinfo;
+
+	/* Advance the return beacon pointer past the current beacon */
+	*pbeaconinfo += beaconsize;
+	*bytesleft -= beaconsize;
+
+	bytesleftforcurrentbeacon = beaconsize;
+
+	pwpa_supplicant = &pBSSEntry->wpa_supplicant;
+	pwpa2_supplicant = &pBSSEntry->wpa2_supplicant;
+
+	memcpy(pBSSEntry->macaddress, pcurrentptr, ETH_ALEN);
+	lbs_pr_debug(1, "InterpretIE: AP MAC Addr-%x:%x:%x:%x:%x:%x\n",
+	       pBSSEntry->macaddress[0], pBSSEntry->macaddress[1],
+	       pBSSEntry->macaddress[2], pBSSEntry->macaddress[3],
+	       pBSSEntry->macaddress[4], pBSSEntry->macaddress[5]);
+
+	pcurrentptr += ETH_ALEN;
+	bytesleftforcurrentbeacon -= ETH_ALEN;
+
+	if (bytesleftforcurrentbeacon < 12) {
+		lbs_pr_debug(1, "InterpretIE: Not enough bytes left\n");
+		return -1;
+	}
+
+	/*
+	 * next 4 fields are RSSI, time stamp, beacon interval,
+	 *   and capability information
+	 */
+
+	/* RSSI is 1 byte long */
+	pBSSEntry->rssi = le32_to_cpu((long)(*pcurrentptr));
+	lbs_pr_debug(1, "InterpretIE: RSSI=%02X\n", *pcurrentptr);
+	pcurrentptr += 1;
+	bytesleftforcurrentbeacon -= 1;
+
+	/* time stamp is 8 bytes long */
+	memcpy(fixedie.timestamp, pcurrentptr, 8);
+	memcpy(pBSSEntry->timestamp, pcurrentptr, 8);
+	pcurrentptr += 8;
+	bytesleftforcurrentbeacon -= 8;
+
+	/* beacon interval is 2 bytes long */
+	memcpy(&fixedie.beaconinterval, pcurrentptr, 2);
+	pBSSEntry->beaconperiod = le16_to_cpu(fixedie.beaconinterval);
+	pcurrentptr += 2;
+	bytesleftforcurrentbeacon -= 2;
+
+	/* capability information is 2 bytes long */
+	memcpy(&fixedie.capabilities, pcurrentptr, 2);
+	lbs_pr_debug(1, "InterpretIE: fixedie.capabilities=0x%X\n",
+	       fixedie.capabilities);
+	fixedie.capabilities = le16_to_cpu(fixedie.capabilities);
+	pcap = (struct ieeetypes_capinfo *) & fixedie.capabilities;
+	memcpy(&pBSSEntry->cap, pcap, sizeof(struct ieeetypes_capinfo));
+	pcurrentptr += 2;
+	bytesleftforcurrentbeacon -= 2;
+
+	/* rest of the current buffer are IE's */
+	lbs_pr_debug(1, "InterpretIE: IElength for this AP = %d\n",
+	       bytesleftforcurrentbeacon);
+
+	lbs_dbg_hex("InterpretIE: IE info", (u8 *) pcurrentptr,
+		bytesleftforcurrentbeacon);
+
+	if (pcap->privacy) {
+		lbs_pr_debug(1, "InterpretIE: AP WEP enabled\n");
+		pBSSEntry->privacy = wlan802_11privfilter8021xWEP;
+	} else {
+		pBSSEntry->privacy = wlan802_11privfilteracceptall;
+	}
+
+	if (pcap->ibss == 1) {
+		pBSSEntry->inframode = wlan802_11ibss;
+	} else {
+		pBSSEntry->inframode = wlan802_11infrastructure;
+	}
+
+	/* process variable IE */
+	while (bytesleftforcurrentbeacon >= 2) {
+		elemID = (enum ieeetypes_elementid) (*((u8 *) pcurrentptr));
+		elemlen = *((u8 *) pcurrentptr + 1);
+
+		if (bytesleftforcurrentbeacon < elemlen) {
+			lbs_pr_debug(1, "InterpretIE: error in processing IE, "
+			       "bytes left < IE length\n");
+			bytesleftforcurrentbeacon = 0;
+			continue;
+		}
+
+		switch (elemID) {
+
+		case SSID:
+			pBSSEntry->ssid.ssidlength = elemlen;
+			memcpy(pBSSEntry->ssid.ssid, (pcurrentptr + 2),
+			       elemlen);
+			lbs_pr_debug(1, "ssid: %32s", pBSSEntry->ssid.ssid);
+			break;
+
+		case SUPPORTED_RATES:
+			memcpy(pBSSEntry->datarates, (pcurrentptr + 2),
+			       elemlen);
+			memmove(pBSSEntry->libertas_supported_rates, (pcurrentptr + 2),
+				elemlen);
+			ratesize = elemlen;
+			founddatarateie = 1;
+			break;
+
+		case EXTRA_IE:
+			lbs_pr_debug(1, "InterpretIE: EXTRA_IE Found!\n");
+			pBSSEntry->extra_ie = 1;
+			break;
+
+		case FH_PARAM_SET:
+			pFH = (struct ieeetypes_fhparamset *) pcurrentptr;
+			memmove(&pBSSEntry->phyparamset.fhparamset, pFH,
+				sizeof(struct ieeetypes_fhparamset));
+			pBSSEntry->phyparamset.fhparamset.dwelltime
+			    =
+			    le16_to_cpu(pBSSEntry->phyparamset.fhparamset.
+					     dwelltime);
+			break;
+
+		case DS_PARAM_SET:
+			pDS = (struct ieeetypes_dsparamset *) pcurrentptr;
+
+			pBSSEntry->channel = pDS->currentchan;
+
+			memcpy(&pBSSEntry->phyparamset.dsparamset, pDS,
+			       sizeof(struct ieeetypes_dsparamset));
+			break;
+
+		case CF_PARAM_SET:
+			pCF = (struct ieeetypes_cfparamset *) pcurrentptr;
+
+			memcpy(&pBSSEntry->ssparamset.cfparamset, pCF,
+			       sizeof(struct ieeetypes_cfparamset));
+			break;
+
+		case IBSS_PARAM_SET:
+			pibss = (struct ieeetypes_ibssparamset *) pcurrentptr;
+			pBSSEntry->atimwindow =
+			    le32_to_cpu(pibss->atimwindow);
+
+			memmove(&pBSSEntry->ssparamset.ibssparamset, pibss,
+				sizeof(struct ieeetypes_ibssparamset));
+
+			pBSSEntry->ssparamset.ibssparamset.atimwindow
+			    =
+			    le16_to_cpu(pBSSEntry->ssparamset.ibssparamset.
+					     atimwindow);
+			break;
+
+			/* Handle Country Info IE */
+		case COUNTRY_INFO:
+			pcountryinfo =
+			    (struct ieeetypes_countryinfoset *) pcurrentptr;
+
+			if (pcountryinfo->len <
+			    sizeof(pcountryinfo->countrycode)
+			    || pcountryinfo->len > 254) {
+				lbs_pr_debug(1, "InterpretIE: 11D- Err "
+				       "CountryInfo len =%d min=%d max=254\n",
+				       pcountryinfo->len,
+				       sizeof(pcountryinfo->countrycode));
+				LEAVE();
+				return -1;
+			}
+
+			memcpy(&pBSSEntry->countryinfo,
+			       pcountryinfo, pcountryinfo->len + 2);
+			lbs_dbg_hex("InterpretIE: 11D- CountryInfo:",
+				(u8 *) pcountryinfo,
+				(u32) (pcountryinfo->len + 2));
+			break;
+
+		case EXTENDED_SUPPORTED_RATES:
+			/*
+			 * only process extended supported rate
+			 * if data rate is already found.
+			 * data rate IE should come before
+			 * extended supported rate IE
+			 */
+			if (founddatarateie) {
+				if ((elemlen + ratesize) > WLAN_SUPPORTED_RATES) {
+					bytestocopy =
+					    (WLAN_SUPPORTED_RATES - ratesize);
+				} else {
+					bytestocopy = elemlen;
+				}
+
+				pRate = (u8 *) pBSSEntry->datarates;
+				pRate += ratesize;
+				memmove(pRate, (pcurrentptr + 2), bytestocopy);
+
+				pRate = (u8 *) pBSSEntry->libertas_supported_rates;
+
+				pRate += ratesize;
+				memmove(pRate, (pcurrentptr + 2), bytestocopy);
+			}
+			break;
+
+		case VENDOR_SPECIFIC_221:
+#define IE_ID_LEN_FIELDS_BYTES 2
+			pIe = (struct IE_WPA *)pcurrentptr;
+
+			if (!memcmp(pIe->oui, oui01, sizeof(oui01))) {
+				pwpa_supplicant->wpa_ie_len
+				    = min_t(size_t, elemlen + IE_ID_LEN_FIELDS_BYTES,
+					  sizeof(pwpa_supplicant->wpa_ie));
+				memcpy(pwpa_supplicant->wpa_ie,
+				       pcurrentptr,
+				       pwpa_supplicant->wpa_ie_len);
+				lbs_dbg_hex("InterpretIE: Resp WPA_IE",
+					pwpa_supplicant->wpa_ie, elemlen);
+			}
+			break;
+		case WPA2_IE:
+			pIe = (struct IE_WPA *)pcurrentptr;
+			pwpa2_supplicant->wpa_ie_len
+			    = min_t(size_t, elemlen + IE_ID_LEN_FIELDS_BYTES,
+				  sizeof(pwpa2_supplicant->wpa_ie));
+			memcpy(pwpa2_supplicant->wpa_ie,
+			       pcurrentptr, pwpa2_supplicant->wpa_ie_len);
+
+			lbs_dbg_hex("InterpretIE: Resp WPA2_IE",
+				pwpa2_supplicant->wpa_ie, elemlen);
+			break;
+		case TIM:
+			break;
+
+		case CHALLENGE_TEXT:
+			break;
+		}
+
+		pcurrentptr += elemlen + 2;
+
+		/* need to account for IE ID and IE len */
+		bytesleftforcurrentbeacon -= (elemlen + 2);
+
+	}			/* while (bytesleftforcurrentbeacon > 2) */
+
+	return 0;
+}
+
+/**
+ *  @brief Compare two SSIDs
+ *
+ *  @param ssid1    A pointer to ssid to compare
+ *  @param ssid2    A pointer to ssid to compare
+ *
+ *  @return         0--ssid is same, otherwise is different
+ */
+int libertas_SSID_cmp(struct WLAN_802_11_SSID *ssid1, struct WLAN_802_11_SSID *ssid2)
+{
+	if (!ssid1 || !ssid2)
+		return -1;
+
+	if (ssid1->ssidlength != ssid2->ssidlength)
+		return -1;
+
+	return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssidlength);
+}
+
+/**
+ *  @brief This function finds a specific compatible BSSID in the scan list
+ *
+ *  @param adapter  A pointer to wlan_adapter
+ *  @param bssid    BSSID to find in the scan list
+ *  @param mode     Network mode: Infrastructure or IBSS
+ *
+ *  @return         index in BSSID list, or error return code (< 0)
+ */
+int libertas_find_BSSID_in_list(wlan_adapter * adapter, u8 * bssid, int mode)
+{
+	int ret = -ENETUNREACH;
+	int i;
+
+	if (!bssid)
+		return -EFAULT;
+
+	lbs_pr_debug(1, "FindBSSID: Num of BSSIDs = %d\n",
+	       adapter->numinscantable);
+
+	/* Look through the scan table for a compatible match. The ret return
+	 *   variable will be equal to the index in the scan table (greater
+	 *   than zero) if the network is compatible.  The loop will continue
+	 *   past a matched bssid that is not compatible in case there is an
+	 *   AP with multiple SSIDs assigned to the same BSSID
+	 */
+	for (i = 0; ret < 0 && i < adapter->numinscantable; i++) {
+		if (!memcmp(adapter->scantable[i].macaddress, bssid, ETH_ALEN)) {
+			switch (mode) {
+			case wlan802_11infrastructure:
+			case wlan802_11ibss:
+				ret = is_network_compatible(adapter, i, mode);
+				break;
+			default:
+				ret = i;
+				break;
+			}
+		}
+	}
+
+	return ret;
+}
+
+/**
+ *  @brief This function finds ssid in ssid list.
+ *
+ *  @param adapter  A pointer to wlan_adapter
+ *  @param ssid     SSID to find in the list
+ *  @param bssid    BSSID to qualify the SSID selection (if provided)
+ *  @param mode     Network mode: Infrastructure or IBSS
+ *
+ *  @return         index in BSSID list
+ */
+int libertas_find_SSID_in_list(wlan_adapter * adapter,
+		   struct WLAN_802_11_SSID *ssid, u8 * bssid, int mode)
+{
+	int net = -ENETUNREACH;
+	u8 bestrssi = 0;
+	int i;
+	int j;
+
+	lbs_pr_debug(1, "Num of Entries in Table = %d\n", adapter->numinscantable);
+
+	for (i = 0; i < adapter->numinscantable; i++) {
+		if (!libertas_SSID_cmp(&adapter->scantable[i].ssid, ssid) &&
+		    (!bssid ||
+		     !memcmp(adapter->scantable[i].
+			     macaddress, bssid, ETH_ALEN))) {
+			switch (mode) {
+			case wlan802_11infrastructure:
+			case wlan802_11ibss:
+				j = is_network_compatible(adapter, i, mode);
+
+				if (j >= 0) {
+					if (bssid) {
+						return i;
+					}
+
+					if (SCAN_RSSI
+					    (adapter->scantable[i].rssi)
+					    > bestrssi) {
+						bestrssi =
+						    SCAN_RSSI(adapter->
+							      scantable[i].
+							      rssi);
+						net = i;
+					}
+				} else {
+					if (net == -ENETUNREACH) {
+						net = j;
+					}
+				}
+				break;
+			case wlan802_11autounknown:
+			default:
+				if (SCAN_RSSI(adapter->scantable[i].rssi)
+				    > bestrssi) {
+					bestrssi =
+					    SCAN_RSSI(adapter->scantable[i].
+						      rssi);
+					net = i;
+				}
+				break;
+			}
+		}
+	}
+
+	return net;
+}
+
+/**
+ *  @brief This function finds the best SSID in the Scan List
+ *
+ *  Search the scan table for the best SSID that also matches the current
+ *   adapter network preference (infrastructure or adhoc)
+ *
+ *  @param adapter  A pointer to wlan_adapter
+ *
+ *  @return         index in BSSID list
+ */
+int libertas_find_best_SSID_in_list(wlan_adapter * adapter,
+                                    enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode)
+{
+	int bestnet = -ENETUNREACH;
+	u8 bestrssi = 0;
+	int i;
+
+	ENTER();
+
+	lbs_pr_debug(1, "Num of BSSIDs = %d\n", adapter->numinscantable);
+
+	for (i = 0; i < adapter->numinscantable; i++) {
+		switch (mode) {
+		case wlan802_11infrastructure:
+		case wlan802_11ibss:
+			if (is_network_compatible(adapter, i, mode) >= 0) {
+				if (SCAN_RSSI(adapter->scantable[i].rssi) >
+				    bestrssi) {
+					bestrssi =
+					    SCAN_RSSI(adapter->scantable[i].
+						      rssi);
+					bestnet = i;
+				}
+			}
+			break;
+		case wlan802_11autounknown:
+		default:
+			if (SCAN_RSSI(adapter->scantable[i].rssi) > bestrssi) {
+				bestrssi =
+				    SCAN_RSSI(adapter->scantable[i].rssi);
+				bestnet = i;
+			}
+			break;
+		}
+	}
+
+	LEAVE();
+	return bestnet;
+}
+
+/**
+ *  @brief Find the AP with specific ssid in the scan list
+ *
+ *  @param priv         A pointer to wlan_private structure
+ *  @param pSSID        A pointer to AP's ssid
+ *
+ *  @return             0--success, otherwise--fail
+ */
+int libertas_find_best_network_SSID(wlan_private * priv,
+                                    struct WLAN_802_11_SSID *pSSID,
+                                    enum WLAN_802_11_NETWORK_INFRASTRUCTURE preferred_mode,
+                                    enum WLAN_802_11_NETWORK_INFRASTRUCTURE *out_mode)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+	struct bss_descriptor *preqbssid;
+	int i;
+
+	ENTER();
+
+	memset(pSSID, 0, sizeof(struct WLAN_802_11_SSID));
+
+	wlan_scan_networks(priv, NULL);
+	if (adapter->surpriseremoved)
+		return -1;
+	wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending);
+
+	i = libertas_find_best_SSID_in_list(adapter, preferred_mode);
+	if (i < 0) {
+		ret = -1;
+		goto out;
+	}
+
+	preqbssid = &adapter->scantable[i];
+	memcpy(pSSID, &preqbssid->ssid,
+	       sizeof(struct WLAN_802_11_SSID));
+	*out_mode = preqbssid->inframode;
+
+	if (!pSSID->ssidlength) {
+		ret = -1;
+	}
+
+out:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief Scan Network
+ *
+ *  @param dev          A pointer to net_device structure
+ *  @param info         A pointer to iw_request_info structure
+ *  @param vwrq         A pointer to iw_param structure
+ *  @param extra        A pointer to extra data buf
+ *
+ *  @return             0 --success, otherwise fail
+ */
+int libertas_set_scan(struct net_device *dev, struct iw_request_info *info,
+		  struct iw_param *vwrq, char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	union iwreq_data wrqu;
+
+	ENTER();
+
+	if (!wlan_scan_networks(priv, NULL)) {
+		memset(&wrqu, 0, sizeof(union iwreq_data));
+		wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu,
+				    NULL);
+	}
+
+	if (adapter->surpriseremoved)
+		return -1;
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief Send a scan command for all available channels filtered on a spec
+ *
+ *  @param priv             A pointer to wlan_private structure
+ *  @param prequestedssid   A pointer to AP's ssid
+ *  @param keeppreviousscan Flag used to save/clear scan table before scan
+ *
+ *  @return                0-success, otherwise fail
+ */
+int libertas_send_specific_SSID_scan(wlan_private * priv,
+			 struct WLAN_802_11_SSID *prequestedssid,
+			 u8 keeppreviousscan)
+{
+	wlan_adapter *adapter = priv->adapter;
+	struct wlan_ioctl_user_scan_cfg scancfg;
+
+	ENTER();
+
+	if (prequestedssid == NULL) {
+		return -1;
+	}
+
+	memset(&scancfg, 0x00, sizeof(scancfg));
+
+	memcpy(scancfg.specificSSID, prequestedssid->ssid,
+	       prequestedssid->ssidlength);
+	scancfg.keeppreviousscan = keeppreviousscan;
+
+	wlan_scan_networks(priv, &scancfg);
+	if (adapter->surpriseremoved)
+		return -1;
+	wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending);
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief scan an AP with specific BSSID
+ *
+ *  @param priv             A pointer to wlan_private structure
+ *  @param bssid            A pointer to AP's bssid
+ *  @param keeppreviousscan Flag used to save/clear scan table before scan
+ *
+ *  @return          0-success, otherwise fail
+ */
+int libertas_send_specific_BSSID_scan(wlan_private * priv, u8 * bssid, u8 keeppreviousscan)
+{
+	struct wlan_ioctl_user_scan_cfg scancfg;
+
+	ENTER();
+
+	if (bssid == NULL) {
+		return -1;
+	}
+
+	memset(&scancfg, 0x00, sizeof(scancfg));
+	memcpy(scancfg.specificBSSID, bssid, sizeof(scancfg.specificBSSID));
+	scancfg.keeppreviousscan = keeppreviousscan;
+
+	wlan_scan_networks(priv, &scancfg);
+	if (priv->adapter->surpriseremoved)
+		return -1;
+	wait_event_interruptible(priv->adapter->cmd_pending,
+		!priv->adapter->nr_cmd_pending);
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief  Retrieve the scan table entries via wireless tools IOCTL call
+ *
+ *  @param dev          A pointer to net_device structure
+ *  @param info         A pointer to iw_request_info structure
+ *  @param dwrq         A pointer to iw_point structure
+ *  @param extra        A pointer to extra data buf
+ *
+ *  @return             0 --success, otherwise fail
+ */
+int libertas_get_scan(struct net_device *dev, struct iw_request_info *info,
+		  struct iw_point *dwrq, char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+	char *current_ev = extra;
+	char *end_buf = extra + IW_SCAN_MAX_DATA;
+	struct chan_freq_power *cfp;
+	struct bss_descriptor *pscantable;
+	char *current_val;	/* For rates */
+	struct iw_event iwe;	/* Temporary buffer */
+	int i;
+	int j;
+	int rate;
+#define PERFECT_RSSI ((u8)50)
+#define WORST_RSSI   ((u8)0)
+#define RSSI_DIFF    ((u8)(PERFECT_RSSI - WORST_RSSI))
+	u8 rssi;
+
+	u8 buf[16 + 256 * 2];
+	u8 *ptr;
+
+	ENTER();
+
+	/*
+	 * if there's either commands in the queue or one being
+	 * processed return -EAGAIN for iwlist to retry later.
+	 */
+    if (adapter->nr_cmd_pending)
+		return -EAGAIN;
+
+	if (adapter->connect_status == libertas_connected)
+		lbs_pr_debug(1, "Current ssid: %32s\n",
+		       adapter->curbssparams.ssid.ssid);
+
+	lbs_pr_debug(1, "Scan: Get: numinscantable = %d\n",
+	       adapter->numinscantable);
+
+	/* The old API using SIOCGIWAPLIST had a hard limit of IW_MAX_AP.
+	 * The new API using SIOCGIWSCAN is only limited by buffer size
+	 * WE-14 -> WE-16 the buffer is limited to IW_SCAN_MAX_DATA bytes
+	 * which is 4096.
+	 */
+	for (i = 0; i < adapter->numinscantable; i++) {
+		if ((current_ev + MAX_SCAN_CELL_SIZE) >= end_buf) {
+			lbs_pr_debug(1, "i=%d break out: current_ev=%p end_buf=%p "
+			       "MAX_SCAN_CELL_SIZE=%d\n",
+			       i, current_ev, end_buf, MAX_SCAN_CELL_SIZE);
+			break;
+		}
+
+		pscantable = &adapter->scantable[i];
+
+		lbs_pr_debug(1, "i=%d  ssid: %32s\n", i, pscantable->ssid.ssid);
+
+		cfp =
+		    libertas_find_cfp_by_band_and_channel(adapter, 0,
+						 pscantable->channel);
+		if (!cfp) {
+			lbs_pr_debug(1, "Invalid channel number %d\n",
+			       pscantable->channel);
+			continue;
+		}
+
+		if (!ssid_valid(&adapter->scantable[i].ssid)) {
+			continue;
+		}
+
+		/* First entry *MUST* be the AP MAC address */
+		iwe.cmd = SIOCGIWAP;
+		iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+		memcpy(iwe.u.ap_addr.sa_data,
+		       &adapter->scantable[i].macaddress, ETH_ALEN);
+
+		iwe.len = IW_EV_ADDR_LEN;
+		current_ev =
+		    iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len);
+
+		//Add the ESSID
+		iwe.u.data.length = adapter->scantable[i].ssid.ssidlength;
+
+		if (iwe.u.data.length > 32) {
+			iwe.u.data.length = 32;
+		}
+
+		iwe.cmd = SIOCGIWESSID;
+		iwe.u.data.flags = 1;
+		iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
+		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
+						  adapter->scantable[i].ssid.
+						  ssid);
+
+		//Add mode
+		iwe.cmd = SIOCGIWMODE;
+		iwe.u.mode = adapter->scantable[i].inframode + 1;
+		iwe.len = IW_EV_UINT_LEN;
+		current_ev =
+		    iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len);
+
+		//frequency
+		iwe.cmd = SIOCGIWFREQ;
+		iwe.u.freq.m = (long)cfp->freq * 100000;
+		iwe.u.freq.e = 1;
+		iwe.len = IW_EV_FREQ_LEN;
+		current_ev =
+		    iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len);
+
+		/* Add quality statistics */
+		iwe.cmd = IWEVQUAL;
+		iwe.u.qual.updated = IW_QUAL_ALL_UPDATED;
+		iwe.u.qual.level = SCAN_RSSI(adapter->scantable[i].rssi);
+
+		rssi = iwe.u.qual.level - MRVDRV_NF_DEFAULT_SCAN_VALUE;
+		iwe.u.qual.qual =
+		    (100 * RSSI_DIFF * RSSI_DIFF - (PERFECT_RSSI - rssi) *
+		     (15 * (RSSI_DIFF) + 62 * (PERFECT_RSSI - rssi))) /
+		    (RSSI_DIFF * RSSI_DIFF);
+		if (iwe.u.qual.qual > 100)
+			iwe.u.qual.qual = 100;
+		else if (iwe.u.qual.qual < 1)
+			iwe.u.qual.qual = 0;
+
+		if (adapter->NF[TYPE_BEACON][TYPE_NOAVG] == 0) {
+			iwe.u.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE;
+		} else {
+			iwe.u.qual.noise =
+			    CAL_NF(adapter->NF[TYPE_BEACON][TYPE_NOAVG]);
+		}
+		if ((adapter->inframode == wlan802_11ibss) &&
+		    !libertas_SSID_cmp(&adapter->curbssparams.ssid,
+			     &adapter->scantable[i].ssid)
+		    && adapter->adhoccreate) {
+			ret = libertas_prepare_and_send_command(priv,
+						    cmd_802_11_rssi,
+						    0,
+						    cmd_option_waitforrsp,
+						    0, NULL);
+
+			if (!ret) {
+				iwe.u.qual.level =
+				    CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] /
+					     AVG_SCALE,
+					     adapter->NF[TYPE_RXPD][TYPE_AVG] /
+					     AVG_SCALE);
+			}
+		}
+		iwe.len = IW_EV_QUAL_LEN;
+		current_ev =
+		    iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len);
+
+		/* Add encryption capability */
+		iwe.cmd = SIOCGIWENCODE;
+		if (adapter->scantable[i].privacy) {
+			iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+		} else {
+			iwe.u.data.flags = IW_ENCODE_DISABLED;
+		}
+		iwe.u.data.length = 0;
+		iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
+		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
+						  adapter->scantable->ssid.
+						  ssid);
+
+		current_val = current_ev + IW_EV_LCP_LEN;
+
+		iwe.cmd = SIOCGIWRATE;
+
+		iwe.u.bitrate.fixed = 0;
+		iwe.u.bitrate.disabled = 0;
+		iwe.u.bitrate.value = 0;
+
+		/* Bit rate given in 500 kb/s units (+ 0x80) */
+		for (j = 0; j < sizeof(adapter->scantable[i].libertas_supported_rates);
+		     j++) {
+			if (adapter->scantable[i].libertas_supported_rates[j] == 0) {
+				break;
+			}
+			rate =
+			    (adapter->scantable[i].libertas_supported_rates[j] & 0x7F) *
+			    500000;
+			if (rate > iwe.u.bitrate.value) {
+				iwe.u.bitrate.value = rate;
+			}
+
+			iwe.u.bitrate.value =
+			    (adapter->scantable[i].libertas_supported_rates[j]
+			     & 0x7f) * 500000;
+			iwe.len = IW_EV_PARAM_LEN;
+			current_ev =
+			    iwe_stream_add_value(current_ev, current_val,
+						 end_buf, &iwe, iwe.len);
+
+		}
+		if ((adapter->scantable[i].inframode == wlan802_11ibss)
+		    && !libertas_SSID_cmp(&adapter->curbssparams.ssid,
+				&adapter->scantable[i].ssid)
+		    && adapter->adhoccreate) {
+			iwe.u.bitrate.value = 22 * 500000;
+		}
+		iwe.len = IW_EV_PARAM_LEN;
+		current_ev =
+		    iwe_stream_add_value(current_ev, current_val, end_buf, &iwe,
+					 iwe.len);
+
+		/* Add new value to event */
+		current_val = current_ev + IW_EV_LCP_LEN;
+
+		if (adapter->scantable[i].wpa2_supplicant.wpa_ie[0] == WPA2_IE) {
+			memset(&iwe, 0, sizeof(iwe));
+			memset(buf, 0, sizeof(buf));
+			memcpy(buf, adapter->scantable[i].
+						wpa2_supplicant.wpa_ie,
+					adapter->scantable[i].wpa2_supplicant.
+						wpa_ie_len);
+			iwe.cmd = IWEVGENIE;
+			iwe.u.data.length = adapter->scantable[i].
+					wpa2_supplicant.wpa_ie_len;
+			iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
+			current_ev = iwe_stream_add_point(current_ev, end_buf,
+					&iwe, buf);
+		}
+		if (adapter->scantable[i].wpa_supplicant.wpa_ie[0] == WPA_IE) {
+			memset(&iwe, 0, sizeof(iwe));
+			memset(buf, 0, sizeof(buf));
+			memcpy(buf, adapter->scantable[i].
+						wpa_supplicant.wpa_ie,
+					adapter->scantable[i].wpa_supplicant.
+						wpa_ie_len);
+			iwe.cmd = IWEVGENIE;
+			iwe.u.data.length = adapter->scantable[i].
+					wpa_supplicant.wpa_ie_len;
+			iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
+			current_ev = iwe_stream_add_point(current_ev, end_buf,
+					&iwe, buf);
+		}
+
+
+		if (adapter->scantable[i].extra_ie != 0) {
+			memset(&iwe, 0, sizeof(iwe));
+			memset(buf, 0, sizeof(buf));
+			ptr = buf;
+			ptr += sprintf(ptr, "extra_ie");
+			iwe.u.data.length = strlen(buf);
+
+			lbs_pr_debug(1, "iwe.u.data.length %d\n",
+			       iwe.u.data.length);
+			lbs_pr_debug(1, "BUF: %s \n", buf);
+
+			iwe.cmd = IWEVCUSTOM;
+			iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
+			current_ev =
+			    iwe_stream_add_point(current_ev, end_buf, &iwe,
+						 buf);
+		}
+
+		current_val = current_ev + IW_EV_LCP_LEN;
+
+		/*
+		 * Check if we added any event
+		 */
+		if ((current_val - current_ev) > IW_EV_LCP_LEN)
+			current_ev = current_val;
+	}
+
+	dwrq->length = (current_ev - extra);
+	dwrq->flags = 0;
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief Prepare a scan command to be sent to the firmware
+ *
+ *  Use the wlan_scan_cmd_config sent to the command processing module in
+ *   the libertas_prepare_and_send_command to configure a cmd_ds_802_11_scan command
+ *   struct to send to firmware.
+ *
+ *  The fixed fields specifying the BSS type and BSSID filters as well as a
+ *   variable number/length of TLVs are sent in the command to firmware.
+ *
+ *  @param priv       A pointer to wlan_private structure
+ *  @param cmd        A pointer to cmd_ds_command structure to be sent to
+ *                    firmware with the cmd_DS_801_11_SCAN structure
+ *  @param pdata_buf  Void pointer cast of a wlan_scan_cmd_config struct used
+ *                    to set the fields/TLVs for the command sent to firmware
+ *
+ *  @return           0 or -1
+ *
+ *  @sa wlan_scan_create_channel_list
+ */
+int libertas_cmd_80211_scan(wlan_private * priv,
+			 struct cmd_ds_command *cmd, void *pdata_buf)
+{
+	struct cmd_ds_802_11_scan *pscan = &cmd->params.scan;
+	struct wlan_scan_cmd_config *pscancfg;
+
+	ENTER();
+
+	pscancfg = pdata_buf;
+
+	/* Set fixed field variables in scan command */
+	pscan->bsstype = pscancfg->bsstype;
+	memcpy(pscan->BSSID, pscancfg->specificBSSID, sizeof(pscan->BSSID));
+	memcpy(pscan->tlvbuffer, pscancfg->tlvbuffer, pscancfg->tlvbufferlen);
+
+	cmd->command = cpu_to_le16(cmd_802_11_scan);
+
+	/* size is equal to the sizeof(fixed portions) + the TLV len + header */
+	cmd->size = cpu_to_le16(sizeof(pscan->bsstype)
+				     + sizeof(pscan->BSSID)
+				     + pscancfg->tlvbufferlen + S_DS_GEN);
+
+	lbs_pr_debug(1, "SCAN_CMD: command=%x, size=%x, seqnum=%x\n",
+	       cmd->command, cmd->size, cmd->seqnum);
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief This function handles the command response of scan
+ *
+ *   The response buffer for the scan command has the following
+ *      memory layout:
+ *
+ *     .-----------------------------------------------------------.
+ *     |  header (4 * sizeof(u16)):  Standard command response hdr |
+ *     .-----------------------------------------------------------.
+ *     |  bufsize (u16) : sizeof the BSS Description data          |
+ *     .-----------------------------------------------------------.
+ *     |  NumOfSet (u8) : Number of BSS Descs returned             |
+ *     .-----------------------------------------------------------.
+ *     |  BSSDescription data (variable, size given in bufsize)    |
+ *     .-----------------------------------------------------------.
+ *     |  TLV data (variable, size calculated using header->size,  |
+ *     |            bufsize and sizeof the fixed fields above)     |
+ *     .-----------------------------------------------------------.
+ *
+ *  @param priv    A pointer to wlan_private structure
+ *  @param resp    A pointer to cmd_ds_command
+ *
+ *  @return        0 or -1
+ */
+int libertas_ret_80211_scan(wlan_private * priv, struct cmd_ds_command *resp)
+{
+	wlan_adapter *adapter = priv->adapter;
+	struct cmd_ds_802_11_scan_rsp *pscan;
+	struct bss_descriptor newbssentry;
+	struct mrvlietypes_data *ptlv;
+	struct mrvlietypes_tsftimestamp *ptsftlv;
+	u8 *pbssinfo;
+	u16 scanrespsize;
+	int bytesleft;
+	int numintable;
+	int bssIdx;
+	int idx;
+	int tlvbufsize;
+	u64 tsfval;
+
+	ENTER();
+
+	pscan = &resp->params.scanresp;
+
+	if (pscan->nr_sets > MRVDRV_MAX_BSSID_LIST) {
+        lbs_pr_debug(1,
+		       "SCAN_RESP: Invalid number of AP returned (%d)!!\n",
+		       pscan->nr_sets);
+		LEAVE();
+		return -1;
+	}
+
+	bytesleft = le16_to_cpu(pscan->bssdescriptsize);
+	lbs_pr_debug(1, "SCAN_RESP: bssdescriptsize %d\n", bytesleft);
+
+	scanrespsize = le16_to_cpu(resp->size);
+	lbs_pr_debug(1, "SCAN_RESP: returned %d AP before parsing\n",
+	       pscan->nr_sets);
+
+	numintable = adapter->numinscantable;
+	pbssinfo = pscan->bssdesc_and_tlvbuffer;
+
+	/* The size of the TLV buffer is equal to the entire command response
+	 *   size (scanrespsize) minus the fixed fields (sizeof()'s), the
+	 *   BSS Descriptions (bssdescriptsize as bytesLef) and the command
+	 *   response header (S_DS_GEN)
+	 */
+	tlvbufsize = scanrespsize - (bytesleft + sizeof(pscan->bssdescriptsize)
+				     + sizeof(pscan->nr_sets)
+				     + S_DS_GEN);
+
+	ptlv = (struct mrvlietypes_data *) (pscan->bssdesc_and_tlvbuffer + bytesleft);
+
+	/* Search the TLV buffer space in the scan response for any valid TLVs */
+	wlan_ret_802_11_scan_get_tlv_ptrs(ptlv, tlvbufsize, &ptsftlv);
+
+	/*
+	 *  Process each scan response returned (pscan->nr_sets).  Save
+	 *    the information in the newbssentry and then insert into the
+	 *    driver scan table either as an update to an existing entry
+	 *    or as an addition at the end of the table
+	 */
+	for (idx = 0; idx < pscan->nr_sets && bytesleft; idx++) {
+		/* Zero out the newbssentry we are about to store info in */
+		memset(&newbssentry, 0x00, sizeof(newbssentry));
+
+		/* Process the data fields and IEs returned for this BSS */
+		if ((InterpretBSSDescriptionWithIE(&newbssentry,
+						   &pbssinfo,
+						   &bytesleft) ==
+		     0)
+		    && CHECK_SSID_IS_VALID(&newbssentry.ssid)) {
+
+            lbs_pr_debug(1,
+			       "SCAN_RESP: BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n",
+			       newbssentry.macaddress[0],
+			       newbssentry.macaddress[1],
+			       newbssentry.macaddress[2],
+			       newbssentry.macaddress[3],
+			       newbssentry.macaddress[4],
+			       newbssentry.macaddress[5]);
+
+			/*
+			 * Search the scan table for the same bssid
+			 */
+			for (bssIdx = 0; bssIdx < numintable; bssIdx++) {
+				if (memcmp(newbssentry.macaddress,
+					   adapter->scantable[bssIdx].
+					   macaddress,
+					   sizeof(newbssentry.macaddress)) ==
+				    0) {
+					/*
+					 * If the SSID matches as well, it is a duplicate of
+					 *   this entry.  Keep the bssIdx set to this
+					 *   entry so we replace the old contents in the table
+					 */
+					if ((newbssentry.ssid.ssidlength ==
+					     adapter->scantable[bssIdx].ssid.
+					     ssidlength)
+					    &&
+					    (memcmp
+					     (newbssentry.ssid.ssid,
+					      adapter->scantable[bssIdx].ssid.
+					      ssid,
+					      newbssentry.ssid.ssidlength) ==
+					     0)) {
+                        lbs_pr_debug(1,
+						       "SCAN_RESP: Duplicate of index: %d\n",
+						       bssIdx);
+						break;
+					}
+				}
+			}
+			/*
+			 * If the bssIdx is equal to the number of entries in the table,
+			 *   the new entry was not a duplicate; append it to the scan
+			 *   table
+			 */
+			if (bssIdx == numintable) {
+				/* Range check the bssIdx, keep it limited to the last entry */
+				if (bssIdx == MRVDRV_MAX_BSSID_LIST) {
+					bssIdx--;
+				} else {
+					numintable++;
+				}
+			}
+
+			/*
+			 * If the TSF TLV was appended to the scan results, save the
+			 *   this entries TSF value in the networktsf field.  The
+			 *   networktsf is the firmware's TSF value at the time the
+			 *   beacon or probe response was received.
+			 */
+			if (ptsftlv) {
+				memcpy(&tsfval, &ptsftlv->tsftable[idx],
+				       sizeof(tsfval));
+				tsfval = le64_to_cpu(tsfval);
+
+				memcpy(&newbssentry.networktsf,
+				       &tsfval, sizeof(newbssentry.networktsf));
+			}
+
+			/* Copy the locally created newbssentry to the scan table */
+			memcpy(&adapter->scantable[bssIdx],
+			       &newbssentry,
+			       sizeof(adapter->scantable[bssIdx]));
+
+		} else {
+
+			/* error parsing/interpreting the scan response, skipped */
+			lbs_pr_debug(1, "SCAN_RESP: "
+			       "InterpretBSSDescriptionWithIE returned ERROR\n");
+		}
+	}
+
+	lbs_pr_debug(1, "SCAN_RESP: Scanned %2d APs, %d valid, %d total\n",
+	       pscan->nr_sets, numintable - adapter->numinscantable,
+	       numintable);
+
+	/* Update the total number of BSSIDs in the scan table */
+	adapter->numinscantable = numintable;
+
+	LEAVE();
+	return 0;
+}
diff --git a/drivers/net/wireless/libertas/scan.h b/drivers/net/wireless/libertas/scan.h
new file mode 100644
index 0000000..d93aa7f
--- /dev/null
+++ b/drivers/net/wireless/libertas/scan.h
@@ -0,0 +1,216 @@
+/* -*- mode: C; tab-width: 4; indent-tabs-mode: nil -*- */
+/* vi: set expandtab shiftwidth=4 tabstop=4 textwidth=78: */
+
+/**
+  * Interface for the wlan network scan routines
+  *
+  * Driver interface functions and type declarations for the scan module
+  *   implemented in wlan_scan.c.
+  */
+#ifndef _WLAN_SCAN_H
+#define _WLAN_SCAN_H
+
+#include "hostcmd.h"
+
+/**
+ *  @brief Maximum number of channels that can be sent in a setuserscan ioctl
+ *
+ *  @sa wlan_ioctl_user_scan_cfg
+ */
+#define WLAN_IOCTL_USER_SCAN_CHAN_MAX  50
+
+//! Infrastructure BSS scan type in wlan_scan_cmd_config
+#define WLAN_SCAN_BSS_TYPE_BSS         1
+
+//! Adhoc BSS scan type in wlan_scan_cmd_config
+#define WLAN_SCAN_BSS_TYPE_IBSS        2
+
+//! Adhoc or Infrastructure BSS scan type in wlan_scan_cmd_config, no filter
+#define WLAN_SCAN_BSS_TYPE_ANY         3
+
+/**
+ * @brief Structure used internally in the wlan driver to configure a scan.
+ *
+ * Sent to the command processing module to configure the firmware
+ *   scan command prepared by libertas_cmd_80211_scan.
+ *
+ * @sa wlan_scan_networks
+ *
+ */
+struct wlan_scan_cmd_config {
+    /**
+     *  @brief BSS type to be sent in the firmware command
+     *
+     *  Field can be used to restrict the types of networks returned in the
+     *    scan.  valid settings are:
+     *
+     *   - WLAN_SCAN_BSS_TYPE_BSS  (infrastructure)
+     *   - WLAN_SCAN_BSS_TYPE_IBSS (adhoc)
+     *   - WLAN_SCAN_BSS_TYPE_ANY  (unrestricted, adhoc and infrastructure)
+     */
+	u8 bsstype;
+
+    /**
+     *  @brief Specific BSSID used to filter scan results in the firmware
+     */
+	u8 specificBSSID[ETH_ALEN];
+
+    /**
+     *  @brief length of TLVs sent in command starting at tlvBuffer
+     */
+	int tlvbufferlen;
+
+    /**
+     *  @brief SSID TLV(s) and ChanList TLVs to be sent in the firmware command
+     *
+     *  @sa TLV_TYPE_CHANLIST, mrvlietypes_chanlistparamset_t
+     *  @sa TLV_TYPE_SSID, mrvlietypes_ssidparamset_t
+     */
+	u8 tlvbuffer[1];	//!< SSID TLV(s) and ChanList TLVs are stored here
+};
+
+/**
+ *  @brief IOCTL channel sub-structure sent in wlan_ioctl_user_scan_cfg
+ *
+ *  Multiple instances of this structure are included in the IOCTL command
+ *   to configure a instance of a scan on the specific channel.
+ */
+struct wlan_ioctl_user_scan_chan {
+	u8 channumber;		//!< channel Number to scan
+	u8 radiotype;		//!< Radio type: 'B/G' band = 0, 'A' band = 1
+	u8 scantype;		//!< Scan type: Active = 0, Passive = 1
+	u16 scantime;		//!< Scan duration in milliseconds; if 0 default used
+};
+
+/**
+ *  @brief IOCTL input structure to configure an immediate scan cmd to firmware
+ *
+ *  Used in the setuserscan (WLAN_SET_USER_SCAN) private ioctl.  Specifies
+ *   a number of parameters to be used in general for the scan as well
+ *   as a channel list (wlan_ioctl_user_scan_chan) for each scan period
+ *   desired.
+ *
+ *  @sa libertas_set_user_scan_ioctl
+ */
+struct wlan_ioctl_user_scan_cfg {
+
+    /**
+     *  @brief Flag set to keep the previous scan table intact
+     *
+     *  If set, the scan results will accumulate, replacing any previous
+     *   matched entries for a BSS with the new scan data
+     */
+	u8 keeppreviousscan;	//!< Do not erase the existing scan results
+
+    /**
+     *  @brief BSS type to be sent in the firmware command
+     *
+     *  Field can be used to restrict the types of networks returned in the
+     *    scan.  valid settings are:
+     *
+     *   - WLAN_SCAN_BSS_TYPE_BSS  (infrastructure)
+     *   - WLAN_SCAN_BSS_TYPE_IBSS (adhoc)
+     *   - WLAN_SCAN_BSS_TYPE_ANY  (unrestricted, adhoc and infrastructure)
+     */
+	u8 bsstype;
+
+    /**
+     *  @brief Configure the number of probe requests for active chan scans
+     */
+	u8 numprobes;
+
+    /**
+     *  @brief BSSID filter sent in the firmware command to limit the results
+     */
+	u8 specificBSSID[ETH_ALEN];
+
+    /**
+     *  @brief SSID filter sent in the firmware command to limit the results
+     */
+	char specificSSID[IW_ESSID_MAX_SIZE + 1];
+
+    /**
+     *  @brief Variable number (fixed maximum) of channels to scan up
+     */
+	struct wlan_ioctl_user_scan_chan chanlist[WLAN_IOCTL_USER_SCAN_CHAN_MAX];
+};
+
+/**
+ *  @brief Structure used to store information for each beacon/probe response
+ */
+struct bss_descriptor {
+	u8 macaddress[ETH_ALEN];
+
+	struct WLAN_802_11_SSID ssid;
+
+	/* WEP encryption requirement */
+	u32 privacy;
+
+	/* receive signal strength in dBm */
+	long rssi;
+
+	u32 channel;
+
+	u16 beaconperiod;
+
+	u32 atimwindow;
+
+	enum WLAN_802_11_NETWORK_INFRASTRUCTURE inframode;
+	u8 libertas_supported_rates[WLAN_SUPPORTED_RATES];
+
+	int extra_ie;
+
+	u8 timestamp[8];	//!< TSF value included in the beacon/probe response
+	union ieeetypes_phyparamset phyparamset;
+	union IEEEtypes_ssparamset ssparamset;
+	struct ieeetypes_capinfo cap;
+	u8 datarates[WLAN_SUPPORTED_RATES];
+
+	__le64 networktsf;		//!< TSF timestamp from the current firmware TSF
+
+	struct ieeetypes_countryinfofullset countryinfo;
+
+	struct WPA_SUPPLICANT wpa_supplicant;
+	struct WPA_SUPPLICANT wpa2_supplicant;
+
+};
+
+extern int libertas_SSID_cmp(struct WLAN_802_11_SSID *ssid1,
+		   struct WLAN_802_11_SSID *ssid2);
+extern int libertas_find_SSID_in_list(wlan_adapter * adapter, struct WLAN_802_11_SSID *ssid,
+			  u8 * bssid, int mode);
+int libertas_find_best_SSID_in_list(wlan_adapter * adapter, enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode);
+extern int libertas_find_BSSID_in_list(wlan_adapter * adapter, u8 * bssid, int mode);
+
+int libertas_find_best_network_SSID(wlan_private * priv,
+			struct WLAN_802_11_SSID *pSSID,
+			enum WLAN_802_11_NETWORK_INFRASTRUCTURE preferred_mode,
+			enum WLAN_802_11_NETWORK_INFRASTRUCTURE *out_mode);
+
+extern int libertas_send_specific_SSID_scan(wlan_private * priv,
+				struct WLAN_802_11_SSID *prequestedssid,
+				u8 keeppreviousscan);
+extern int libertas_send_specific_BSSID_scan(wlan_private * priv,
+				 u8 * bssid, u8 keeppreviousscan);
+
+extern int libertas_cmd_80211_scan(wlan_private * priv,
+				struct cmd_ds_command *cmd,
+				void *pdata_buf);
+
+extern int libertas_ret_80211_scan(wlan_private * priv,
+				struct cmd_ds_command *resp);
+
+int wlan_scan_networks(wlan_private * priv,
+                const struct wlan_ioctl_user_scan_cfg * puserscanin);
+
+struct ifreq;
+
+struct iw_point;
+struct iw_param;
+struct iw_request_info;
+extern int libertas_get_scan(struct net_device *dev, struct iw_request_info *info,
+			 struct iw_point *dwrq, char *extra);
+extern int libertas_set_scan(struct net_device *dev, struct iw_request_info *info,
+			 struct iw_param *vwrq, char *extra);
+
+#endif				/* _WLAN_SCAN_H */
diff --git a/drivers/net/wireless/libertas/thread.h b/drivers/net/wireless/libertas/thread.h
new file mode 100644
index 0000000..207b8a6
--- /dev/null
+++ b/drivers/net/wireless/libertas/thread.h
@@ -0,0 +1,52 @@
+#ifndef	__WLAN_THREAD_H_
+#define	__WLAN_THREAD_H_
+
+#include	<linux/kthread.h>
+
+struct wlan_thread {
+	struct task_struct *task;
+	wait_queue_head_t waitq;
+	pid_t pid;
+	void *priv;
+};
+
+static inline void wlan_activate_thread(struct wlan_thread * thr)
+{
+	/** Record the thread pid */
+	thr->pid = current->pid;
+
+	/** Initialize the wait queue */
+	init_waitqueue_head(&thr->waitq);
+}
+
+static inline void wlan_deactivate_thread(struct wlan_thread * thr)
+{
+	ENTER();
+
+	thr->pid = 0;
+
+	LEAVE();
+}
+
+static inline void wlan_create_thread(int (*wlanfunc) (void *),
+				      struct wlan_thread * thr, char *name)
+{
+	thr->task = kthread_run(wlanfunc, thr, "%s", name);
+}
+
+static inline int wlan_terminate_thread(struct wlan_thread * thr)
+{
+	ENTER();
+
+	/* Check if the thread is active or not */
+	if (!thr->pid) {
+		printk(KERN_ERR "Thread does not exist\n");
+		return -1;
+	}
+	kthread_stop(thr->task);
+
+	LEAVE();
+	return 0;
+}
+
+#endif
diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c
new file mode 100644
index 0000000..82d0622
--- /dev/null
+++ b/drivers/net/wireless/libertas/tx.c
@@ -0,0 +1,285 @@
+/**
+  * This file contains the handling of TX in wlan driver.
+  */
+#include <linux/netdevice.h>
+
+#include "hostcmd.h"
+#include "radiotap.h"
+#include "sbi.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "wext.h"
+
+/**
+ *  @brief This function converts Tx/Rx rates from IEEE80211_RADIOTAP_RATE
+ *  units (500 Kb/s) into Marvell WLAN format (see Table 8 in Section 3.2.1)
+ *
+ *  @param rate    Input rate
+ *  @return      Output Rate (0 if invalid)
+ */
+static u32 convert_radiotap_rate_to_mv(u8 rate)
+{
+	switch (rate) {
+	case 2:		/*   1 Mbps */
+		return 0 | (1 << 4);
+	case 4:		/*   2 Mbps */
+		return 1 | (1 << 4);
+	case 11:		/* 5.5 Mbps */
+		return 2 | (1 << 4);
+	case 22:		/*  11 Mbps */
+		return 3 | (1 << 4);
+	case 12:		/*   6 Mbps */
+		return 4 | (1 << 4);
+	case 18:		/*   9 Mbps */
+		return 5 | (1 << 4);
+	case 24:		/*  12 Mbps */
+		return 6 | (1 << 4);
+	case 36:		/*  18 Mbps */
+		return 7 | (1 << 4);
+	case 48:		/*  24 Mbps */
+		return 8 | (1 << 4);
+	case 72:		/*  36 Mbps */
+		return 9 | (1 << 4);
+	case 96:		/*  48 Mbps */
+		return 10 | (1 << 4);
+	case 108:		/*  54 Mbps */
+		return 11 | (1 << 4);
+	}
+	return 0;
+}
+
+/**
+ *  @brief This function processes a single packet and sends
+ *  to IF layer
+ *
+ *  @param priv    A pointer to wlan_private structure
+ *  @param skb     A pointer to skb which includes TX packet
+ *  @return 	   0 or -1
+ */
+static int SendSinglePacket(wlan_private * priv, struct sk_buff *skb)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+	struct txpd localtxpd;
+	struct txpd *plocaltxpd = &localtxpd;
+	u8 *p802x_hdr;
+	struct tx_radiotap_hdr *pradiotap_hdr;
+	u32 new_rate;
+	u8 *ptr = priv->adapter->tmptxbuf;
+
+	ENTER();
+
+	if (priv->adapter->surpriseremoved)
+		return -1;
+
+	if ((priv->adapter->debugmode & MRVDRV_DEBUG_TX_PATH) != 0)
+		lbs_dbg_hex("TX packet: ", skb->data,
+			 min_t(unsigned int, skb->len, 100));
+
+	if (!skb->len || (skb->len > MRVDRV_ETH_TX_PACKET_BUFFER_SIZE)) {
+		lbs_pr_debug(1, "Tx error: Bad skb length %d : %d\n",
+		       skb->len, MRVDRV_ETH_TX_PACKET_BUFFER_SIZE);
+		ret = -1;
+		goto done;
+	}
+
+	memset(plocaltxpd, 0, sizeof(struct txpd));
+
+	plocaltxpd->tx_packet_length = skb->len;
+
+	/* offset of actual data */
+	plocaltxpd->tx_packet_location = sizeof(struct txpd);
+
+	/* TxCtrl set by user or default */
+	plocaltxpd->tx_control = adapter->pkttxctrl;
+
+	p802x_hdr = skb->data;
+	if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) {
+
+		/* locate radiotap header */
+		pradiotap_hdr = (struct tx_radiotap_hdr *)skb->data;
+
+		/* set txpd fields from the radiotap header */
+		new_rate = convert_radiotap_rate_to_mv(pradiotap_hdr->rate);
+		if (new_rate != 0) {
+			/* erase tx_control[4:0] */
+			plocaltxpd->tx_control &= ~0x1f;
+			/* write new tx_control[4:0] */
+			plocaltxpd->tx_control |= new_rate;
+		}
+
+		/* skip the radiotap header */
+		p802x_hdr += sizeof(struct tx_radiotap_hdr);
+		plocaltxpd->tx_packet_length -= sizeof(struct tx_radiotap_hdr);
+
+	}
+	/* copy destination address from 802.3 or 802.11 header */
+	if (priv->adapter->linkmode == WLAN_LINKMODE_802_11)
+		memcpy(plocaltxpd->tx_dest_addr_high, p802x_hdr + 4, ETH_ALEN);
+	else
+		memcpy(plocaltxpd->tx_dest_addr_high, p802x_hdr, ETH_ALEN);
+
+	lbs_dbg_hex("txpd", (u8 *) plocaltxpd, sizeof(struct txpd));
+
+	if (IS_MESH_FRAME(skb)) {
+		plocaltxpd->tx_control |= TxPD_MESH_FRAME;
+	}
+
+	memcpy(ptr, plocaltxpd, sizeof(struct txpd));
+
+	ptr += sizeof(struct txpd);
+
+	lbs_dbg_hex("Tx Data", (u8 *) p802x_hdr, plocaltxpd->tx_packet_length);
+	memcpy(ptr, p802x_hdr, plocaltxpd->tx_packet_length);
+	ret = libertas_sbi_host_to_card(priv, MVMS_DAT,
+			       priv->adapter->tmptxbuf,
+			       plocaltxpd->tx_packet_length +
+			       sizeof(struct txpd));
+
+	if (ret) {
+		lbs_pr_debug(1, "Tx error: libertas_sbi_host_to_card failed: 0x%X\n", ret);
+		goto done;
+	}
+
+	lbs_pr_debug(1, "SendSinglePacket succeeds\n");
+
+      done:
+	if (!ret) {
+		priv->stats.tx_packets++;
+		priv->stats.tx_bytes += skb->len;
+	} else {
+		priv->stats.tx_dropped++;
+		priv->stats.tx_errors++;
+	}
+
+	if (!ret && priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) {
+		/* Keep the skb to echo it back once Tx feedback is
+		   received from FW */
+		skb_orphan(skb);
+		/* stop processing outgoing pkts */
+		netif_stop_queue(priv->wlan_dev.netdev);
+		/* freeze any packets already in our queues */
+		priv->adapter->TxLockFlag = 1;
+	} else {
+		dev_kfree_skb_any(skb);
+		priv->adapter->currenttxskb = NULL;
+	}
+
+	LEAVE();
+	return ret;
+}
+
+
+void libertas_tx_runqueue(wlan_private *priv)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int i;
+
+	spin_lock(&adapter->txqueue_lock);
+	for (i = 0; i < adapter->tx_queue_idx; i++) {
+		struct sk_buff *skb = adapter->tx_queue_ps[i];
+		spin_unlock(&adapter->txqueue_lock);
+		SendSinglePacket(priv, skb);
+		spin_lock(&adapter->txqueue_lock);
+	}
+	adapter->tx_queue_idx = 0;
+	spin_unlock(&adapter->txqueue_lock);
+}
+
+static void wlan_tx_queue(wlan_private *priv, struct sk_buff *skb)
+{
+	wlan_adapter *adapter = priv->adapter;
+
+	spin_lock(&adapter->txqueue_lock);
+
+	WARN_ON(priv->adapter->tx_queue_idx >= NR_TX_QUEUE);
+	adapter->tx_queue_ps[adapter->tx_queue_idx++] = skb;
+	if (adapter->tx_queue_idx == NR_TX_QUEUE)
+		netif_stop_queue(priv->wlan_dev.netdev);
+	else
+		netif_start_queue(priv->wlan_dev.netdev);
+
+	spin_unlock(&adapter->txqueue_lock);
+}
+
+/**
+ *  @brief This function checks the conditions and sends packet to IF
+ *  layer if everything is ok.
+ *
+ *  @param priv    A pointer to wlan_private structure
+ *  @return 	   n/a
+ */
+int libertas_process_tx(wlan_private * priv, struct sk_buff *skb)
+{
+	int ret = -1;
+
+	ENTER();
+
+	lbs_dbg_hex("TX Data", skb->data, min_t(unsigned int, skb->len, 100));
+
+	if (priv->wlan_dev.dnld_sent) {
+		lbs_pr_alert( "TX error: dnld_sent = %d, not sending\n",
+		       priv->wlan_dev.dnld_sent);
+		goto done;
+	}
+
+	if ((priv->adapter->psstate == PS_STATE_SLEEP) ||
+	    (priv->adapter->psstate == PS_STATE_PRE_SLEEP)) {
+		wlan_tx_queue(priv, skb);
+		return ret;
+	}
+
+	priv->adapter->currenttxskb = skb;
+
+	ret = SendSinglePacket(priv, skb);
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends to the host the last transmitted packet,
+ *  filling the radiotap headers with transmission information.
+ *
+ *  @param priv     A pointer to wlan_private structure
+ *  @param status   A 32 bit value containing transmission status.
+ *
+ *  @returns void
+ */
+void libertas_send_tx_feedback(wlan_private * priv)
+{
+	wlan_adapter *adapter = priv->adapter;
+	struct tx_radiotap_hdr *radiotap_hdr;
+	u32 status = adapter->eventcause;
+	int txfail;
+	int try_count;
+
+	if (adapter->radiomode != WLAN_RADIOMODE_RADIOTAP ||
+	    adapter->currenttxskb == NULL)
+		return;
+
+	radiotap_hdr = (struct tx_radiotap_hdr *)adapter->currenttxskb->data;
+
+	if ((adapter->debugmode & MRVDRV_DEBUG_TX_PATH) != 0)
+		lbs_dbg_hex("TX feedback: ", (u8 *) radiotap_hdr,
+			min_t(unsigned int, adapter->currenttxskb->len, 100));
+
+	txfail = (status >> 24);
+
+#if 0
+	/* The version of roofnet that we've tested does not use this yet
+	 * But it may be used in the future.
+	 */
+	if (txfail)
+		radiotap_hdr->flags &= IEEE80211_RADIOTAP_F_TX_FAIL;
+#endif
+	try_count = (status >> 16) & 0xff;
+	radiotap_hdr->data_retries = (try_count) ?
+	    (1 + adapter->txretrycount - try_count) : 0;
+	libertas_upload_rx_packet(priv, adapter->currenttxskb);
+	adapter->currenttxskb = NULL;
+	priv->adapter->TxLockFlag = 0;
+	if (priv->adapter->connect_status == libertas_connected)
+		netif_wake_queue(priv->wlan_dev.netdev);
+}
diff --git a/drivers/net/wireless/libertas/types.h b/drivers/net/wireless/libertas/types.h
new file mode 100644
index 0000000..09d62f8
--- /dev/null
+++ b/drivers/net/wireless/libertas/types.h
@@ -0,0 +1,289 @@
+/**
+  * This header file contains definition for global types
+  */
+#ifndef _WLAN_TYPES_
+#define _WLAN_TYPES_
+
+#include <linux/if_ether.h>
+
+/** IEEE type definitions  */
+enum ieeetypes_elementid {
+	SSID = 0,
+	SUPPORTED_RATES,
+	FH_PARAM_SET,
+	DS_PARAM_SET,
+	CF_PARAM_SET,
+	TIM,
+	IBSS_PARAM_SET,
+	COUNTRY_INFO = 7,
+
+	CHALLENGE_TEXT = 16,
+
+	EXTENDED_SUPPORTED_RATES = 50,
+
+	VENDOR_SPECIFIC_221 = 221,
+
+	WPA_IE = 221,
+	WPA2_IE = 48,
+
+	EXTRA_IE = 133,
+} __attribute__ ((packed));
+
+#define CAPINFO_MASK	(~(0xda00))
+
+struct ieeetypes_capinfo {
+	u8 ess:1;
+	u8 ibss:1;
+	u8 cfpollable:1;
+	u8 cfpollrqst:1;
+	u8 privacy:1;
+	u8 shortpreamble:1;
+	u8 pbcc:1;
+	u8 chanagility:1;
+	u8 spectrummgmt:1;
+	u8 rsrvd3:1;
+	u8 shortslottime:1;
+	u8 apsd:1;
+	u8 rsvrd2:1;
+	u8 dsssofdm:1;
+	u8 rsrvd1:2;
+} __attribute__ ((packed));
+
+struct ieeetypes_cfparamset {
+	u8 elementid;
+	u8 len;
+	u8 cfpcnt;
+	u8 cfpperiod;
+	u16 cfpmaxduration;
+	u16 cfpdurationremaining;
+} __attribute__ ((packed));
+
+
+struct ieeetypes_ibssparamset {
+	u8 elementid;
+	u8 len;
+	u16 atimwindow;
+} __attribute__ ((packed));
+
+union IEEEtypes_ssparamset {
+	struct ieeetypes_cfparamset cfparamset;
+	struct ieeetypes_ibssparamset ibssparamset;
+} __attribute__ ((packed));
+
+struct ieeetypes_fhparamset {
+	u8 elementid;
+	u8 len;
+	u16 dwelltime;
+	u8 hopset;
+	u8 hoppattern;
+	u8 hopindex;
+} __attribute__ ((packed));
+
+struct ieeetypes_dsparamset {
+	u8 elementid;
+	u8 len;
+	u8 currentchan;
+} __attribute__ ((packed));
+
+union ieeetypes_phyparamset {
+	struct ieeetypes_fhparamset fhparamset;
+	struct ieeetypes_dsparamset dsparamset;
+} __attribute__ ((packed));
+
+struct ieeetypes_assocrsp {
+	struct ieeetypes_capinfo capability;
+	u16 statuscode;
+	u16 aid;
+	u8 iebuffer[1];
+} __attribute__ ((packed));
+
+/** TLV  type ID definition */
+#define PROPRIETARY_TLV_BASE_ID		0x0100
+
+/* Terminating TLV type */
+#define MRVL_TERMINATE_TLV_ID		0xffff
+
+#define TLV_TYPE_SSID				0x0000
+#define TLV_TYPE_RATES				0x0001
+#define TLV_TYPE_PHY_FH				0x0002
+#define TLV_TYPE_PHY_DS				0x0003
+#define TLV_TYPE_CF				    0x0004
+#define TLV_TYPE_IBSS				0x0006
+
+#define TLV_TYPE_DOMAIN				0x0007
+
+#define TLV_TYPE_POWER_CAPABILITY	0x0021
+
+#define TLV_TYPE_KEY_MATERIAL       (PROPRIETARY_TLV_BASE_ID + 0)
+#define TLV_TYPE_CHANLIST           (PROPRIETARY_TLV_BASE_ID + 1)
+#define TLV_TYPE_NUMPROBES          (PROPRIETARY_TLV_BASE_ID + 2)
+#define TLV_TYPE_RSSI_LOW           (PROPRIETARY_TLV_BASE_ID + 4)
+#define TLV_TYPE_SNR_LOW            (PROPRIETARY_TLV_BASE_ID + 5)
+#define TLV_TYPE_FAILCOUNT          (PROPRIETARY_TLV_BASE_ID + 6)
+#define TLV_TYPE_BCNMISS            (PROPRIETARY_TLV_BASE_ID + 7)
+#define TLV_TYPE_LED_GPIO           (PROPRIETARY_TLV_BASE_ID + 8)
+#define TLV_TYPE_LEDBEHAVIOR        (PROPRIETARY_TLV_BASE_ID + 9)
+#define TLV_TYPE_PASSTHROUGH        (PROPRIETARY_TLV_BASE_ID + 10)
+#define TLV_TYPE_REASSOCAP          (PROPRIETARY_TLV_BASE_ID + 11)
+#define TLV_TYPE_POWER_TBL_2_4GHZ   (PROPRIETARY_TLV_BASE_ID + 12)
+#define TLV_TYPE_POWER_TBL_5GHZ     (PROPRIETARY_TLV_BASE_ID + 13)
+#define TLV_TYPE_BCASTPROBE	    (PROPRIETARY_TLV_BASE_ID + 14)
+#define TLV_TYPE_NUMSSID_PROBE	    (PROPRIETARY_TLV_BASE_ID + 15)
+#define TLV_TYPE_WMMQSTATUS   	    (PROPRIETARY_TLV_BASE_ID + 16)
+#define TLV_TYPE_CRYPTO_DATA	    (PROPRIETARY_TLV_BASE_ID + 17)
+#define TLV_TYPE_WILDCARDSSID	    (PROPRIETARY_TLV_BASE_ID + 18)
+#define TLV_TYPE_TSFTIMESTAMP	    (PROPRIETARY_TLV_BASE_ID + 19)
+#define TLV_TYPE_RSSI_HIGH          (PROPRIETARY_TLV_BASE_ID + 22)
+#define TLV_TYPE_SNR_HIGH           (PROPRIETARY_TLV_BASE_ID + 23)
+
+/** TLV related data structures*/
+struct mrvlietypesheader {
+	u16 type;
+	u16 len;
+} __attribute__ ((packed));
+
+struct mrvlietypes_data {
+	struct mrvlietypesheader header;
+	u8 Data[1];
+} __attribute__ ((packed));
+
+struct mrvlietypes_ratesparamset {
+	struct mrvlietypesheader header;
+	u8 rates[1];
+} __attribute__ ((packed));
+
+struct mrvlietypes_ssidparamset {
+	struct mrvlietypesheader header;
+	u8 ssid[1];
+} __attribute__ ((packed));
+
+struct mrvlietypes_wildcardssidparamset {
+	struct mrvlietypesheader header;
+	u8 MaxSsidlength;
+	u8 ssid[1];
+} __attribute__ ((packed));
+
+struct chanscanmode {
+	u8 passivescan:1;
+	u8 disablechanfilt:1;
+	u8 reserved_2_7:6;
+} __attribute__ ((packed));
+
+struct chanscanparamset {
+	u8 radiotype;
+	u8 channumber;
+	struct chanscanmode chanscanmode;
+	u16 minscantime;
+	u16 maxscantime;
+} __attribute__ ((packed));
+
+struct mrvlietypes_chanlistparamset {
+	struct mrvlietypesheader header;
+	struct chanscanparamset chanscanparam[1];
+} __attribute__ ((packed));
+
+struct cfparamset {
+	u8 cfpcnt;
+	u8 cfpperiod;
+	u16 cfpmaxduration;
+	u16 cfpdurationremaining;
+} __attribute__ ((packed));
+
+struct ibssparamset {
+	u16 atimwindow;
+} __attribute__ ((packed));
+
+struct mrvlietypes_ssparamset {
+	struct mrvlietypesheader header;
+	union {
+		struct cfparamset cfparamset[1];
+		struct ibssparamset ibssparamset[1];
+	} cf_ibss;
+} __attribute__ ((packed));
+
+struct fhparamset {
+	u16 dwelltime;
+	u8 hopset;
+	u8 hoppattern;
+	u8 hopindex;
+} __attribute__ ((packed));
+
+struct dsparamset {
+	u8 currentchan;
+} __attribute__ ((packed));
+
+struct mrvlietypes_phyparamset {
+	struct mrvlietypesheader header;
+	union {
+		struct fhparamset fhparamset[1];
+		struct dsparamset dsparamset[1];
+	} fh_ds;
+} __attribute__ ((packed));
+
+struct mrvlietypes_rsnparamset {
+	struct mrvlietypesheader header;
+	u8 rsnie[1];
+} __attribute__ ((packed));
+
+struct mrvlietypes_tsftimestamp {
+	struct mrvlietypesheader header;
+	__le64 tsftable[1];
+} __attribute__ ((packed));
+
+/**  Local Power capability */
+struct mrvlietypes_powercapability {
+	struct mrvlietypesheader header;
+	s8 minpower;
+	s8 maxpower;
+} __attribute__ ((packed));
+
+struct mrvlietypes_rssithreshold {
+	struct mrvlietypesheader header;
+	u8 rssivalue;
+	u8 rssifreq;
+} __attribute__ ((packed));
+
+struct mrvlietypes_snrthreshold {
+	struct mrvlietypesheader header;
+	u8 snrvalue;
+	u8 snrfreq;
+} __attribute__ ((packed));
+
+struct mrvlietypes_failurecount {
+	struct mrvlietypesheader header;
+	u8 failvalue;
+	u8 Failfreq;
+} __attribute__ ((packed));
+
+struct mrvlietypes_beaconsmissed {
+	struct mrvlietypesheader header;
+	u8 beaconmissed;
+	u8 reserved;
+} __attribute__ ((packed));
+
+struct mrvlietypes_numprobes {
+	struct mrvlietypesheader header;
+	u16 numprobes;
+} __attribute__ ((packed));
+
+struct mrvlietypes_bcastprobe {
+	struct mrvlietypesheader header;
+	u16 bcastprobe;
+} __attribute__ ((packed));
+
+struct mrvlietypes_numssidprobe {
+	struct mrvlietypesheader header;
+	u16 numssidprobe;
+} __attribute__ ((packed));
+
+struct led_pin {
+	u8 led;
+	u8 pin;
+} __attribute__ ((packed));
+
+struct mrvlietypes_ledgpio {
+	struct mrvlietypesheader header;
+	struct led_pin ledpin[1];
+} __attribute__ ((packed));
+
+#endif				/* _WLAN_TYPES_ */
diff --git a/drivers/net/wireless/libertas/version.h b/drivers/net/wireless/libertas/version.h
new file mode 100644
index 0000000..e86f65ae
--- /dev/null
+++ b/drivers/net/wireless/libertas/version.h
@@ -0,0 +1,8 @@
+#define DRIVER_RELEASE_VERSION "320.p0"
+const char libertas_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION
+#ifdef  DEBUG
+    "-dbg"
+#endif
+    "";
+
+
diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c
new file mode 100644
index 0000000..4a52336
--- /dev/null
+++ b/drivers/net/wireless/libertas/wext.c
@@ -0,0 +1,2769 @@
+/**
+  * This file contains ioctl functions
+  */
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+#include <linux/bitops.h>
+
+#include <net/ieee80211.h>
+#include <net/iw_handler.h>
+
+#include "host.h"
+#include "radiotap.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "join.h"
+#include "version.h"
+#include "wext.h"
+#include "assoc.h"
+
+
+/**
+ *  @brief Convert mw value to dbm value
+ *
+ *  @param mw	   the value of mw
+ *  @return 	   the value of dbm
+ */
+static int mw_to_dbm(int mw)
+{
+	if (mw < 2)
+		return 0;
+	else if (mw < 3)
+		return 3;
+	else if (mw < 4)
+		return 5;
+	else if (mw < 6)
+		return 7;
+	else if (mw < 7)
+		return 8;
+	else if (mw < 8)
+		return 9;
+	else if (mw < 10)
+		return 10;
+	else if (mw < 13)
+		return 11;
+	else if (mw < 16)
+		return 12;
+	else if (mw < 20)
+		return 13;
+	else if (mw < 25)
+		return 14;
+	else if (mw < 32)
+		return 15;
+	else if (mw < 40)
+		return 16;
+	else if (mw < 50)
+		return 17;
+	else if (mw < 63)
+		return 18;
+	else if (mw < 79)
+		return 19;
+	else if (mw < 100)
+		return 20;
+	else
+		return 21;
+}
+
+/**
+ *  @brief Find the channel frequency power info with specific channel
+ *
+ *  @param adapter 	A pointer to wlan_adapter structure
+ *  @param band		it can be BAND_A, BAND_G or BAND_B
+ *  @param channel      the channel for looking
+ *  @return 	   	A pointer to struct chan_freq_power structure or NULL if not find.
+ */
+struct chan_freq_power *libertas_find_cfp_by_band_and_channel(wlan_adapter * adapter,
+						 u8 band, u16 channel)
+{
+	struct chan_freq_power *cfp = NULL;
+	struct region_channel *rc;
+	int count = sizeof(adapter->region_channel) /
+	    sizeof(adapter->region_channel[0]);
+	int i, j;
+
+	for (j = 0; !cfp && (j < count); j++) {
+		rc = &adapter->region_channel[j];
+
+		if (adapter->enable11d)
+			rc = &adapter->universal_channel[j];
+		if (!rc->valid || !rc->CFP)
+			continue;
+		if (rc->band != band)
+			continue;
+		for (i = 0; i < rc->nrcfp; i++) {
+			if (rc->CFP[i].channel == channel) {
+				cfp = &rc->CFP[i];
+				break;
+			}
+		}
+	}
+
+	if (!cfp && channel)
+		lbs_pr_debug(1, "libertas_find_cfp_by_band_and_channel(): cannot find "
+		       "cfp by band %d & channel %d\n", band, channel);
+
+	return cfp;
+}
+
+/**
+ *  @brief Find the channel frequency power info with specific frequency
+ *
+ *  @param adapter 	A pointer to wlan_adapter structure
+ *  @param band		it can be BAND_A, BAND_G or BAND_B
+ *  @param freq	        the frequency for looking
+ *  @return 	   	A pointer to struct chan_freq_power structure or NULL if not find.
+ */
+static struct chan_freq_power *find_cfp_by_band_and_freq(wlan_adapter * adapter,
+						     u8 band, u32 freq)
+{
+	struct chan_freq_power *cfp = NULL;
+	struct region_channel *rc;
+	int count = sizeof(adapter->region_channel) /
+	    sizeof(adapter->region_channel[0]);
+	int i, j;
+
+	for (j = 0; !cfp && (j < count); j++) {
+		rc = &adapter->region_channel[j];
+
+		if (adapter->enable11d)
+			rc = &adapter->universal_channel[j];
+		if (!rc->valid || !rc->CFP)
+			continue;
+		if (rc->band != band)
+			continue;
+		for (i = 0; i < rc->nrcfp; i++) {
+			if (rc->CFP[i].freq == freq) {
+				cfp = &rc->CFP[i];
+				break;
+			}
+		}
+	}
+
+	if (!cfp && freq)
+		lbs_pr_debug(1, "find_cfp_by_band_and_freql(): cannot find cfp by "
+		       "band %d & freq %d\n", band, freq);
+
+	return cfp;
+}
+
+static int updatecurrentchannel(wlan_private * priv)
+{
+	int ret;
+
+	/*
+	 ** the channel in f/w could be out of sync, get the current channel
+	 */
+	ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_channel,
+				    cmd_opt_802_11_rf_channel_get,
+				    cmd_option_waitforrsp, 0, NULL);
+
+	lbs_pr_debug(1, "Current channel = %d\n",
+	       priv->adapter->curbssparams.channel);
+
+	return ret;
+}
+
+static int setcurrentchannel(wlan_private * priv, int channel)
+{
+	lbs_pr_debug(1, "Set channel = %d\n", channel);
+
+	/*
+	 **  Current channel is not set to adhocchannel requested, set channel
+	 */
+	return (libertas_prepare_and_send_command(priv, cmd_802_11_rf_channel,
+				      cmd_opt_802_11_rf_channel_set,
+				      cmd_option_waitforrsp, 0, &channel));
+}
+
+static int changeadhocchannel(wlan_private * priv, int channel)
+{
+	int ret = 0;
+	wlan_adapter *adapter = priv->adapter;
+
+	adapter->adhocchannel = channel;
+
+	updatecurrentchannel(priv);
+
+	if (adapter->curbssparams.channel == adapter->adhocchannel) {
+		/* adhocchannel is set to the current channel already */
+		LEAVE();
+		return 0;
+	}
+
+	lbs_pr_debug(1, "Updating channel from %d to %d\n",
+	       adapter->curbssparams.channel, adapter->adhocchannel);
+
+	setcurrentchannel(priv, adapter->adhocchannel);
+
+	updatecurrentchannel(priv);
+
+	if (adapter->curbssparams.channel != adapter->adhocchannel) {
+		lbs_pr_debug(1, "failed to updated channel to %d, channel = %d\n",
+		       adapter->adhocchannel, adapter->curbssparams.channel);
+		LEAVE();
+		return -1;
+	}
+
+	if (adapter->connect_status == libertas_connected) {
+		int i;
+		struct WLAN_802_11_SSID curadhocssid;
+
+		lbs_pr_debug(1, "channel Changed while in an IBSS\n");
+
+		/* Copy the current ssid */
+		memcpy(&curadhocssid, &adapter->curbssparams.ssid,
+		       sizeof(struct WLAN_802_11_SSID));
+
+		/* Exit Adhoc mode */
+		lbs_pr_debug(1, "In changeadhocchannel(): Sending Adhoc Stop\n");
+		ret = libertas_stop_adhoc_network(priv);
+
+		if (ret) {
+			LEAVE();
+			return ret;
+		}
+		/* Scan for the network, do not save previous results.  Stale
+		 *   scan data will cause us to join a non-existant adhoc network
+		 */
+		libertas_send_specific_SSID_scan(priv, &curadhocssid, 0);
+
+		// find out the BSSID that matches the current SSID
+		i = libertas_find_SSID_in_list(adapter, &curadhocssid, NULL,
+				   wlan802_11ibss);
+
+		if (i >= 0) {
+			lbs_pr_debug(1, "SSID found at %d in List,"
+			       "so join\n", i);
+			libertas_join_adhoc_network(priv, &adapter->scantable[i]);
+		} else {
+			// else send START command
+			lbs_pr_debug(1, "SSID not found in list, "
+			       "so creating adhoc with ssid = %s\n",
+			       curadhocssid.ssid);
+			libertas_start_adhoc_network(priv, &curadhocssid);
+		}		// end of else (START command)
+	}
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief Set Radio On/OFF
+ *
+ *  @param priv                 A pointer to wlan_private structure
+ *  @option 			Radio Option
+ *  @return 	   		0 --success, otherwise fail
+ */
+int wlan_radio_ioctl(wlan_private * priv, u8 option)
+{
+	int ret = 0;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	if (adapter->radioon != option) {
+		lbs_pr_debug(1, "Switching %s the Radio\n", option ? "On" : "Off");
+		adapter->radioon = option;
+
+		ret = libertas_prepare_and_send_command(priv,
+					    cmd_802_11_radio_control,
+					    cmd_act_set,
+					    cmd_option_waitforrsp, 0, NULL);
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief Copy rates
+ *
+ *  @param dest                 A pointer to Dest Buf
+ *  @param src		        A pointer to Src Buf
+ *  @param len                  The len of Src Buf
+ *  @return 	   	        Number of rates copyed
+ */
+static inline int copyrates(u8 * dest, int pos, u8 * src, int len)
+{
+	int i;
+
+	for (i = 0; i < len && src[i]; i++, pos++) {
+		if (pos >= sizeof(u8) * WLAN_SUPPORTED_RATES)
+			break;
+		dest[pos] = src[i];
+	}
+
+	return pos;
+}
+
+/**
+ *  @brief Get active data rates
+ *
+ *  @param adapter              A pointer to wlan_adapter structure
+ *  @param rate		        The buf to return the active rates
+ *  @return 	   	        The number of rates
+ */
+static int get_active_data_rates(wlan_adapter * adapter,
+				 u8* rates)
+{
+	int k = 0;
+
+	ENTER();
+
+	if (adapter->connect_status != libertas_connected) {
+		if (adapter->inframode == wlan802_11infrastructure) {
+			//Infra. mode
+			lbs_pr_debug(1, "Infra\n");
+			k = copyrates(rates, k, libertas_supported_rates,
+				      sizeof(libertas_supported_rates));
+		} else {
+			//ad-hoc mode
+			lbs_pr_debug(1, "Adhoc G\n");
+			k = copyrates(rates, k, libertas_adhoc_rates_g,
+				      sizeof(libertas_adhoc_rates_g));
+		}
+	} else {
+		k = copyrates(rates, 0, adapter->curbssparams.datarates,
+			      adapter->curbssparams.numofrates);
+	}
+
+	LEAVE();
+
+	return k;
+}
+
+static int wlan_get_name(struct net_device *dev, struct iw_request_info *info,
+			 char *cwrq, char *extra)
+{
+	const char *cp;
+	char comm[6] = { "COMM-" };
+	char mrvl[6] = { "MRVL-" };
+	int cnt;
+
+	ENTER();
+
+	strcpy(cwrq, mrvl);
+
+	cp = strstr(libertas_driver_version, comm);
+	if (cp == libertas_driver_version)	//skip leading "COMM-"
+		cp = libertas_driver_version + strlen(comm);
+	else
+		cp = libertas_driver_version;
+
+	cnt = strlen(mrvl);
+	cwrq += cnt;
+	while (cnt < 16 && (*cp != '-')) {
+		*cwrq++ = toupper(*cp++);
+		cnt++;
+	}
+	*cwrq = '\0';
+
+	LEAVE();
+
+	return 0;
+}
+
+static int wlan_get_freq(struct net_device *dev, struct iw_request_info *info,
+			 struct iw_freq *fwrq, char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	struct chan_freq_power *cfp;
+
+	ENTER();
+
+	cfp = libertas_find_cfp_by_band_and_channel(adapter, 0,
+					   adapter->curbssparams.channel);
+
+	if (!cfp) {
+		if (adapter->curbssparams.channel)
+			lbs_pr_debug(1, "Invalid channel=%d\n",
+			       adapter->curbssparams.channel);
+		return -EINVAL;
+	}
+
+	fwrq->m = (long)cfp->freq * 100000;
+	fwrq->e = 1;
+
+	lbs_pr_debug(1, "freq=%u\n", fwrq->m);
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_get_wap(struct net_device *dev, struct iw_request_info *info,
+			struct sockaddr *awrq, char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	if (adapter->connect_status == libertas_connected) {
+		memcpy(awrq->sa_data, adapter->curbssparams.bssid, ETH_ALEN);
+	} else {
+		memset(awrq->sa_data, 0, ETH_ALEN);
+	}
+	awrq->sa_family = ARPHRD_ETHER;
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_set_nick(struct net_device *dev, struct iw_request_info *info,
+			 struct iw_point *dwrq, char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	/*
+	 * Check the size of the string
+	 */
+
+	if (dwrq->length > 16) {
+		return -E2BIG;
+	}
+
+	mutex_lock(&adapter->lock);
+	memset(adapter->nodename, 0, sizeof(adapter->nodename));
+	memcpy(adapter->nodename, extra, dwrq->length);
+	mutex_unlock(&adapter->lock);
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_get_nick(struct net_device *dev, struct iw_request_info *info,
+			 struct iw_point *dwrq, char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	/*
+	 * Get the Nick Name saved
+	 */
+
+	mutex_lock(&adapter->lock);
+	strncpy(extra, adapter->nodename, 16);
+	mutex_unlock(&adapter->lock);
+
+	extra[16] = '\0';
+
+	/*
+	 * If none, we may want to get the one that was set
+	 */
+
+	/*
+	 * Push it out !
+	 */
+	dwrq->length = strlen(extra) + 1;
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_set_rts(struct net_device *dev, struct iw_request_info *info,
+			struct iw_param *vwrq, char *extra)
+{
+	int ret = 0;
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	int rthr = vwrq->value;
+
+	ENTER();
+
+	if (vwrq->disabled) {
+		adapter->rtsthsd = rthr = MRVDRV_RTS_MAX_VALUE;
+	} else {
+		if (rthr < MRVDRV_RTS_MIN_VALUE || rthr > MRVDRV_RTS_MAX_VALUE)
+			return -EINVAL;
+		adapter->rtsthsd = rthr;
+	}
+
+	ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib,
+				    cmd_act_set, cmd_option_waitforrsp,
+				    OID_802_11_RTS_THRESHOLD, &rthr);
+
+	LEAVE();
+	return ret;
+}
+
+static int wlan_get_rts(struct net_device *dev, struct iw_request_info *info,
+			struct iw_param *vwrq, char *extra)
+{
+	int ret = 0;
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	adapter->rtsthsd = 0;
+	ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib,
+				    cmd_act_get, cmd_option_waitforrsp,
+				    OID_802_11_RTS_THRESHOLD, NULL);
+	if (ret) {
+		LEAVE();
+		return ret;
+	}
+
+	vwrq->value = adapter->rtsthsd;
+	vwrq->disabled = ((vwrq->value < MRVDRV_RTS_MIN_VALUE)
+			  || (vwrq->value > MRVDRV_RTS_MAX_VALUE));
+	vwrq->fixed = 1;
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_set_frag(struct net_device *dev, struct iw_request_info *info,
+			 struct iw_param *vwrq, char *extra)
+{
+	int ret = 0;
+	int fthr = vwrq->value;
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	if (vwrq->disabled) {
+		adapter->fragthsd = fthr = MRVDRV_FRAG_MAX_VALUE;
+	} else {
+		if (fthr < MRVDRV_FRAG_MIN_VALUE
+		    || fthr > MRVDRV_FRAG_MAX_VALUE)
+			return -EINVAL;
+		adapter->fragthsd = fthr;
+	}
+
+	ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib,
+				    cmd_act_set, cmd_option_waitforrsp,
+				    OID_802_11_FRAGMENTATION_THRESHOLD, &fthr);
+	LEAVE();
+	return ret;
+}
+
+static int wlan_get_frag(struct net_device *dev, struct iw_request_info *info,
+			 struct iw_param *vwrq, char *extra)
+{
+	int ret = 0;
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	adapter->fragthsd = 0;
+	ret = libertas_prepare_and_send_command(priv,
+				    cmd_802_11_snmp_mib,
+				    cmd_act_get, cmd_option_waitforrsp,
+				    OID_802_11_FRAGMENTATION_THRESHOLD, NULL);
+	if (ret) {
+		LEAVE();
+		return ret;
+	}
+
+	vwrq->value = adapter->fragthsd;
+	vwrq->disabled = ((vwrq->value < MRVDRV_FRAG_MIN_VALUE)
+			  || (vwrq->value > MRVDRV_FRAG_MAX_VALUE));
+	vwrq->fixed = 1;
+
+	LEAVE();
+	return ret;
+}
+
+static int wlan_get_mode(struct net_device *dev,
+			 struct iw_request_info *info, u32 * uwrq, char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	switch (adapter->inframode) {
+	case wlan802_11ibss:
+		*uwrq = IW_MODE_ADHOC;
+		break;
+
+	case wlan802_11infrastructure:
+		*uwrq = IW_MODE_INFRA;
+		break;
+
+	default:
+	case wlan802_11autounknown:
+		*uwrq = IW_MODE_AUTO;
+		break;
+	}
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_get_txpow(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_param *vwrq, char *extra)
+{
+	int ret = 0;
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	ret = libertas_prepare_and_send_command(priv,
+				    cmd_802_11_rf_tx_power,
+				    cmd_act_tx_power_opt_get,
+				    cmd_option_waitforrsp, 0, NULL);
+
+	if (ret) {
+		LEAVE();
+		return ret;
+	}
+
+	lbs_pr_debug(1, "TXPOWER GET %d dbm.\n", adapter->txpowerlevel);
+	vwrq->value = adapter->txpowerlevel;
+	vwrq->fixed = 1;
+	if (adapter->radioon) {
+		vwrq->disabled = 0;
+		vwrq->flags = IW_TXPOW_DBM;
+	} else {
+		vwrq->disabled = 1;
+	}
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_set_retry(struct net_device *dev, struct iw_request_info *info,
+			  struct iw_param *vwrq, char *extra)
+{
+	int ret = 0;
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	if (vwrq->flags == IW_RETRY_LIMIT) {
+		/* The MAC has a 4-bit Total_Tx_Count register
+		   Total_Tx_Count = 1 + Tx_Retry_Count */
+#define TX_RETRY_MIN 0
+#define TX_RETRY_MAX 14
+		if (vwrq->value < TX_RETRY_MIN || vwrq->value > TX_RETRY_MAX)
+			return -EINVAL;
+
+		/* Adding 1 to convert retry count to try count */
+		adapter->txretrycount = vwrq->value + 1;
+
+		ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib,
+					    cmd_act_set,
+					    cmd_option_waitforrsp,
+					    OID_802_11_TX_RETRYCOUNT, NULL);
+
+		if (ret) {
+			LEAVE();
+			return ret;
+		}
+	} else {
+		return -EOPNOTSUPP;
+	}
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_get_retry(struct net_device *dev, struct iw_request_info *info,
+			  struct iw_param *vwrq, char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+
+	ENTER();
+	adapter->txretrycount = 0;
+	ret = libertas_prepare_and_send_command(priv,
+				    cmd_802_11_snmp_mib,
+				    cmd_act_get, cmd_option_waitforrsp,
+				    OID_802_11_TX_RETRYCOUNT, NULL);
+	if (ret) {
+		LEAVE();
+		return ret;
+	}
+	vwrq->disabled = 0;
+	if (!vwrq->flags) {
+		vwrq->flags = IW_RETRY_LIMIT;
+		/* Subtract 1 to convert try count to retry count */
+		vwrq->value = adapter->txretrycount - 1;
+	}
+
+	LEAVE();
+	return 0;
+}
+
+static inline void sort_channels(struct iw_freq *freq, int num)
+{
+	int i, j;
+	struct iw_freq temp;
+
+	for (i = 0; i < num; i++)
+		for (j = i + 1; j < num; j++)
+			if (freq[i].i > freq[j].i) {
+				temp.i = freq[i].i;
+				temp.m = freq[i].m;
+
+				freq[i].i = freq[j].i;
+				freq[i].m = freq[j].m;
+
+				freq[j].i = temp.i;
+				freq[j].m = temp.m;
+			}
+}
+
+/* data rate listing
+	MULTI_BANDS:
+		abg		a	b	b/g
+   Infra 	G(12)		A(8)	B(4)	G(12)
+   Adhoc 	A+B(12)		A(8)	B(4)	B(4)
+
+	non-MULTI_BANDS:
+					b	b/g
+   Infra 	     		    	B(4)	G(12)
+   Adhoc 	      		    	B(4)	B(4)
+ */
+/**
+ *  @brief Get Range Info
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info			A pointer to iw_request_info structure
+ *  @param vwrq 		A pointer to iw_param structure
+ *  @param extra		A pointer to extra data buf
+ *  @return 	   		0 --success, otherwise fail
+ */
+static int wlan_get_range(struct net_device *dev, struct iw_request_info *info,
+			  struct iw_point *dwrq, char *extra)
+{
+	int i, j;
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	struct iw_range *range = (struct iw_range *)extra;
+	struct chan_freq_power *cfp;
+	u8 rates[WLAN_SUPPORTED_RATES];
+
+	u8 flag = 0;
+
+	ENTER();
+
+	dwrq->length = sizeof(struct iw_range);
+	memset(range, 0, sizeof(struct iw_range));
+
+	range->min_nwid = 0;
+	range->max_nwid = 0;
+
+	memset(rates, 0, sizeof(rates));
+	range->num_bitrates = get_active_data_rates(adapter, rates);
+
+	for (i = 0; i < min_t(__u8, range->num_bitrates, IW_MAX_BITRATES) && rates[i];
+	     i++) {
+		range->bitrate[i] = (rates[i] & 0x7f) * 500000;
+	}
+	range->num_bitrates = i;
+	lbs_pr_debug(1, "IW_MAX_BITRATES=%d num_bitrates=%d\n", IW_MAX_BITRATES,
+	       range->num_bitrates);
+
+	range->num_frequency = 0;
+	if (priv->adapter->enable11d &&
+	    adapter->connect_status == libertas_connected) {
+		u8 chan_no;
+		u8 band;
+
+		struct parsed_region_chan_11d *parsed_region_chan =
+		    &adapter->parsed_region_chan;
+
+		if (parsed_region_chan == NULL) {
+			lbs_pr_debug(1, "11D:parsed_region_chan is NULL\n");
+			LEAVE();
+			return 0;
+		}
+		band = parsed_region_chan->band;
+		lbs_pr_debug(1, "band=%d NoOfChan=%d\n", band,
+		       parsed_region_chan->nr_chan);
+
+		for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
+		     && (i < parsed_region_chan->nr_chan); i++) {
+			chan_no = parsed_region_chan->chanpwr[i].chan;
+			lbs_pr_debug(1, "chan_no=%d\n", chan_no);
+			range->freq[range->num_frequency].i = (long)chan_no;
+			range->freq[range->num_frequency].m =
+			    (long)libertas_chan_2_freq(chan_no, band) * 100000;
+			range->freq[range->num_frequency].e = 1;
+			range->num_frequency++;
+		}
+		flag = 1;
+	}
+	if (!flag) {
+		for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
+		     && (j < sizeof(adapter->region_channel)
+			 / sizeof(adapter->region_channel[0])); j++) {
+			cfp = adapter->region_channel[j].CFP;
+			for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
+			     && adapter->region_channel[j].valid
+			     && cfp
+			     && (i < adapter->region_channel[j].nrcfp); i++) {
+				range->freq[range->num_frequency].i =
+				    (long)cfp->channel;
+				range->freq[range->num_frequency].m =
+				    (long)cfp->freq * 100000;
+				range->freq[range->num_frequency].e = 1;
+				cfp++;
+				range->num_frequency++;
+			}
+		}
+	}
+
+	lbs_pr_debug(1, "IW_MAX_FREQUENCIES=%d num_frequency=%d\n",
+	       IW_MAX_FREQUENCIES, range->num_frequency);
+
+	range->num_channels = range->num_frequency;
+
+	sort_channels(&range->freq[0], range->num_frequency);
+
+	/*
+	 * Set an indication of the max TCP throughput in bit/s that we can
+	 * expect using this interface
+	 */
+	if (i > 2)
+		range->throughput = 5000 * 1000;
+	else
+		range->throughput = 1500 * 1000;
+
+	range->min_rts = MRVDRV_RTS_MIN_VALUE;
+	range->max_rts = MRVDRV_RTS_MAX_VALUE;
+	range->min_frag = MRVDRV_FRAG_MIN_VALUE;
+	range->max_frag = MRVDRV_FRAG_MAX_VALUE;
+
+	range->encoding_size[0] = 5;
+	range->encoding_size[1] = 13;
+	range->num_encoding_sizes = 2;
+	range->max_encoding_tokens = 4;
+
+	range->min_pmp = 1000000;
+	range->max_pmp = 120000000;
+	range->min_pmt = 1000;
+	range->max_pmt = 1000000;
+	range->pmp_flags = IW_POWER_PERIOD;
+	range->pmt_flags = IW_POWER_TIMEOUT;
+	range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R;
+
+	/*
+	 * Minimum version we recommend
+	 */
+	range->we_version_source = 15;
+
+	/*
+	 * Version we are compiled with
+	 */
+	range->we_version_compiled = WIRELESS_EXT;
+
+	range->retry_capa = IW_RETRY_LIMIT;
+	range->retry_flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+
+	range->min_retry = TX_RETRY_MIN;
+	range->max_retry = TX_RETRY_MAX;
+
+	/*
+	 * Set the qual, level and noise range values
+	 */
+	range->max_qual.qual = 100;
+	range->max_qual.level = 0;
+	range->max_qual.noise = 0;
+	range->max_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+
+	range->avg_qual.qual = 70;
+	/* TODO: Find real 'good' to 'bad' threshold value for RSSI */
+	range->avg_qual.level = 0;
+	range->avg_qual.noise = 0;
+	range->avg_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+
+	range->sensitivity = 0;
+
+	/*
+	 * Setup the supported power level ranges
+	 */
+	memset(range->txpower, 0, sizeof(range->txpower));
+	range->txpower[0] = 5;
+	range->txpower[1] = 7;
+	range->txpower[2] = 9;
+	range->txpower[3] = 11;
+	range->txpower[4] = 13;
+	range->txpower[5] = 15;
+	range->txpower[6] = 17;
+	range->txpower[7] = 19;
+
+	range->num_txpower = 8;
+	range->txpower_capa = IW_TXPOW_DBM;
+	range->txpower_capa |= IW_TXPOW_RANGE;
+
+	range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
+				IW_EVENT_CAPA_MASK(SIOCGIWAP) |
+				IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
+	range->event_capa[1] = IW_EVENT_CAPA_K_1;
+
+	if (adapter->fwcapinfo & FW_CAPINFO_WPA) {
+		range->enc_capa =   IW_ENC_CAPA_WPA
+		                  | IW_ENC_CAPA_WPA2
+		                  | IW_ENC_CAPA_CIPHER_TKIP
+		                  | IW_ENC_CAPA_CIPHER_CCMP;
+	}
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_set_power(struct net_device *dev, struct iw_request_info *info,
+			  struct iw_param *vwrq, char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	/* PS is currently supported only in Infrastructure mode
+	 * Remove this check if it is to be supported in IBSS mode also
+	 */
+
+	if (vwrq->disabled) {
+		adapter->psmode = wlan802_11powermodecam;
+		if (adapter->psstate != PS_STATE_FULL_POWER) {
+			libertas_ps_wakeup(priv, cmd_option_waitforrsp);
+		}
+
+		return 0;
+	}
+
+	if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
+		lbs_pr_debug(1,
+		       "Setting power timeout command is not supported\n");
+		return -EINVAL;
+	} else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
+		lbs_pr_debug(1, "Setting power period command is not supported\n");
+		return -EINVAL;
+	}
+
+	if (adapter->psmode != wlan802_11powermodecam) {
+		return 0;
+	}
+
+	adapter->psmode = wlan802_11powermodemax_psp;
+
+	if (adapter->connect_status == libertas_connected) {
+		libertas_ps_sleep(priv, cmd_option_waitforrsp);
+	}
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_get_power(struct net_device *dev, struct iw_request_info *info,
+			  struct iw_param *vwrq, char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	int mode;
+
+	ENTER();
+
+	mode = adapter->psmode;
+
+	if ((vwrq->disabled = (mode == wlan802_11powermodecam))
+	    || adapter->connect_status == libertas_disconnected) {
+		LEAVE();
+		return 0;
+	}
+
+	vwrq->value = 0;
+
+	LEAVE();
+	return 0;
+}
+
+/*
+ * iwpriv settable callbacks
+ */
+
+static const iw_handler wlan_private_handler[] = {
+	NULL,			/* SIOCIWFIRSTPRIV */
+};
+
+static const struct iw_priv_args wlan_private_args[] = {
+	/*
+	 * { cmd, set_args, get_args, name }
+	 */
+	{
+	 WLANSCAN_TYPE,
+	 IW_PRIV_TYPE_CHAR | 8,
+	 IW_PRIV_TYPE_CHAR | 8,
+	 "scantype"},
+
+	{
+	 WLAN_SETINT_GETINT,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 ""},
+	{
+	 WLANNF,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 "getNF"},
+	{
+	 WLANRSSI,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 "getRSSI"},
+	{
+	 WLANENABLE11D,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 "enable11d"},
+	{
+	 WLANADHOCGRATE,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 "adhocgrate"},
+
+	{
+	 WLAN_SUBCMD_SET_PRESCAN,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 "prescan"},
+	{
+	 WLAN_SETONEINT_GETONEINT,
+	 IW_PRIV_TYPE_INT | 1,
+	 IW_PRIV_TYPE_INT | 1,
+	 ""},
+	{
+	 WLAN_BEACON_INTERVAL,
+	 IW_PRIV_TYPE_INT | 1,
+	 IW_PRIV_TYPE_INT | 1,
+	 "bcninterval"},
+	{
+	 WLAN_LISTENINTRVL,
+	 IW_PRIV_TYPE_INT | 1,
+	 IW_PRIV_TYPE_INT | 1,
+	 "lolisteninter"},
+	{
+	 WLAN_TXCONTROL,
+	 IW_PRIV_TYPE_INT | 1,
+	 IW_PRIV_TYPE_INT | 1,
+	 "txcontrol"},
+	{
+	 WLAN_NULLPKTINTERVAL,
+	 IW_PRIV_TYPE_INT | 1,
+	 IW_PRIV_TYPE_INT | 1,
+	 "psnullinterval"},
+	/* Using iwpriv sub-command feature */
+	{
+	 WLAN_SETONEINT_GETNONE,	/* IOCTL: 24 */
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_NONE,
+	 ""},
+
+	{
+	 WLAN_SUBCMD_SETRXANTENNA,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_NONE,
+	 "setrxant"},
+	{
+	 WLAN_SUBCMD_SETTXANTENNA,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_NONE,
+	 "settxant"},
+	{
+	 WLANSETAUTHALG,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_NONE,
+	 "authalgs",
+	 },
+	{
+	 WLANSET8021XAUTHALG,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_NONE,
+	 "8021xauthalgs",
+	 },
+	{
+	 WLANSETENCRYPTIONMODE,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_NONE,
+	 "encryptionmode",
+	 },
+	{
+	 WLANSETREGION,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_NONE,
+	 "setregioncode"},
+	{
+	 WLAN_SET_LISTEN_INTERVAL,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_NONE,
+	 "setlisteninter"},
+	{
+	 WLAN_SET_MULTIPLE_DTIM,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_NONE,
+	 "setmultipledtim"},
+	{
+	 WLAN_SET_ATIM_WINDOW,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_NONE,
+	 "atimwindow"},
+	{
+	 WLANSETBCNAVG,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_NONE,
+	 "setbcnavg"},
+	{
+	 WLANSETDATAAVG,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_NONE,
+	 "setdataavg"},
+	{
+	 WLAN_SET_LINKMODE,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_NONE,
+	 "linkmode"},
+	{
+	 WLAN_SET_RADIOMODE,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_NONE,
+	 "radiomode"},
+	{
+	 WLAN_SET_DEBUGMODE,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_NONE,
+	 "debugmode"},
+	{
+	 WLAN_SUBCMD_MESH_SET_TTL,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 IW_PRIV_TYPE_NONE,
+	 "mesh_set_ttl"},
+	{
+	 WLAN_SETNONE_GETONEINT,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 ""},
+	{
+	 WLANGETREGION,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 "getregioncode"},
+	{
+	 WLAN_GET_LISTEN_INTERVAL,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 "getlisteninter"},
+	{
+	 WLAN_GET_MULTIPLE_DTIM,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 "getmultipledtim"},
+	{
+	 WLAN_GET_TX_RATE,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 "gettxrate"},
+	{
+	 WLANGETBCNAVG,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 "getbcnavg"},
+	{
+	 WLAN_GET_LINKMODE,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 "get_linkmode"},
+	{
+	 WLAN_GET_RADIOMODE,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 "get_radiomode"},
+	{
+	 WLAN_GET_DEBUGMODE,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 "get_debugmode"},
+	{
+	 WLAN_SUBCMD_FWT_CLEANUP,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 "fwt_cleanup"},
+	{
+	 WLAN_SUBCMD_FWT_TIME,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 "fwt_time"},
+	{
+	 WLAN_SUBCMD_MESH_GET_TTL,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 "mesh_get_ttl"},
+	{
+	 WLAN_SETNONE_GETTWELVE_CHAR,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_CHAR | 12,
+	 ""},
+	{
+	 WLAN_SUBCMD_GETRXANTENNA,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_CHAR | 12,
+	 "getrxant"},
+	{
+	 WLAN_SUBCMD_GETTXANTENNA,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_CHAR | 12,
+	 "gettxant"},
+	{
+	 WLAN_GET_TSF,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_CHAR | 12,
+	 "gettsf"},
+	{
+	 WLAN_SETNONE_GETNONE,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_NONE,
+	 ""},
+	{
+	 WLANDEAUTH,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_NONE,
+	 "deauth"},
+	{
+	 WLANADHOCSTOP,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_NONE,
+	 "adhocstop"},
+	{
+	 WLANRADIOON,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_NONE,
+	 "radioon"},
+	{
+	 WLANRADIOOFF,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_NONE,
+	 "radiooff"},
+	{
+	 WLANWLANIDLEON,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_NONE,
+	 "wlanidle-on"},
+	{
+	 WLANWLANIDLEOFF,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_NONE,
+	 "wlanidle-off"},
+	{
+	 WLAN_SUBCMD_FWT_RESET,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_NONE,
+	 "fwt_reset"},
+	{
+	 WLAN_SUBCMD_BT_RESET,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_NONE,
+	 "bt_reset"},
+	{
+	 WLAN_SET128CHAR_GET128CHAR,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 ""},
+	/* BT Management */
+	{
+	 WLAN_SUBCMD_BT_ADD,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 "bt_add"},
+	{
+	 WLAN_SUBCMD_BT_DEL,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 "bt_del"},
+	{
+	 WLAN_SUBCMD_BT_LIST,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 "bt_list"},
+	/* FWT Management */
+	{
+	 WLAN_SUBCMD_FWT_ADD,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 "fwt_add"},
+	{
+	 WLAN_SUBCMD_FWT_DEL,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 "fwt_del"},
+	{
+	 WLAN_SUBCMD_FWT_LOOKUP,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 "fwt_lookup"},
+	{
+	 WLAN_SUBCMD_FWT_LIST_NEIGHBOR,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 "fwt_list_neigh"},
+	{
+	 WLAN_SUBCMD_FWT_LIST,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 "fwt_list"},
+	{
+	 WLAN_SUBCMD_FWT_LIST_ROUTE,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 "fwt_list_route"},
+	{
+	 WLANSCAN_MODE,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 "scanmode"},
+	{
+	 WLAN_GET_ADHOC_STATUS,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 "getadhocstatus"},
+	{
+	 WLAN_SETNONE_GETWORDCHAR,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_CHAR | 128,
+	 ""},
+	{
+	 WLANSETWPAIE,
+	 IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 24,
+	 IW_PRIV_TYPE_NONE,
+	 "setwpaie"},
+	{
+	 WLANGETLOG,
+	 IW_PRIV_TYPE_NONE,
+	 IW_PRIV_TYPE_CHAR | GETLOG_BUFSIZE,
+	 "getlog"},
+	{
+	 WLAN_SET_GET_SIXTEEN_INT,
+	 IW_PRIV_TYPE_INT | 16,
+	 IW_PRIV_TYPE_INT | 16,
+	 ""},
+	{
+	 WLAN_TPCCFG,
+	 IW_PRIV_TYPE_INT | 16,
+	 IW_PRIV_TYPE_INT | 16,
+	 "tpccfg"},
+	{
+	 WLAN_POWERCFG,
+	 IW_PRIV_TYPE_INT | 16,
+	 IW_PRIV_TYPE_INT | 16,
+	 "powercfg"},
+	{
+	 WLAN_AUTO_FREQ_SET,
+	 IW_PRIV_TYPE_INT | 16,
+	 IW_PRIV_TYPE_INT | 16,
+	 "setafc"},
+	{
+	 WLAN_AUTO_FREQ_GET,
+	 IW_PRIV_TYPE_INT | 16,
+	 IW_PRIV_TYPE_INT | 16,
+	 "getafc"},
+	{
+	 WLAN_SCANPROBES,
+	 IW_PRIV_TYPE_INT | 16,
+	 IW_PRIV_TYPE_INT | 16,
+	 "scanprobes"},
+	{
+	 WLAN_LED_GPIO_CTRL,
+	 IW_PRIV_TYPE_INT | 16,
+	 IW_PRIV_TYPE_INT | 16,
+	 "ledgpio"},
+	{
+	 WLAN_ADAPT_RATESET,
+	 IW_PRIV_TYPE_INT | 16,
+	 IW_PRIV_TYPE_INT | 16,
+	 "rateadapt"},
+	{
+	 WLAN_INACTIVITY_TIMEOUT,
+	 IW_PRIV_TYPE_INT | 16,
+	 IW_PRIV_TYPE_INT | 16,
+	 "inactivityto"},
+	{
+	 WLANSNR,
+	 IW_PRIV_TYPE_INT | 16,
+	 IW_PRIV_TYPE_INT | 16,
+	 "getSNR"},
+	{
+	 WLAN_GET_RATE,
+	 IW_PRIV_TYPE_INT | 16,
+	 IW_PRIV_TYPE_INT | 16,
+	 "getrate"},
+	{
+	 WLAN_GET_RXINFO,
+	 IW_PRIV_TYPE_INT | 16,
+	 IW_PRIV_TYPE_INT | 16,
+	 "getrxinfo"},
+};
+
+static struct iw_statistics *wlan_get_wireless_stats(struct net_device *dev)
+{
+	enum {
+		POOR = 30,
+		FAIR = 60,
+		GOOD = 80,
+		VERY_GOOD = 90,
+		EXCELLENT = 95,
+		PERFECT = 100
+	};
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	u32 rssi_qual;
+	u32 tx_qual;
+	u32 quality = 0;
+	int stats_valid = 0;
+	u8 rssi;
+	u32 tx_retries;
+
+	ENTER();
+
+	priv->wstats.status = adapter->inframode;
+
+	/* If we're not associated, all quality values are meaningless */
+	if (adapter->connect_status != libertas_connected)
+		goto out;
+
+	/* Quality by RSSI */
+	priv->wstats.qual.level =
+	    CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG],
+	     adapter->NF[TYPE_BEACON][TYPE_NOAVG]);
+
+	if (adapter->NF[TYPE_BEACON][TYPE_NOAVG] == 0) {
+		priv->wstats.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE;
+	} else {
+		priv->wstats.qual.noise =
+		    CAL_NF(adapter->NF[TYPE_BEACON][TYPE_NOAVG]);
+	}
+
+	lbs_pr_debug(1, "Signal Level = %#x\n", priv->wstats.qual.level);
+	lbs_pr_debug(1, "Noise = %#x\n", priv->wstats.qual.noise);
+
+	rssi = priv->wstats.qual.level - priv->wstats.qual.noise;
+	if (rssi < 15)
+		rssi_qual = rssi * POOR / 10;
+	else if (rssi < 20)
+		rssi_qual = (rssi - 15) * (FAIR - POOR) / 5 + POOR;
+	else if (rssi < 30)
+		rssi_qual = (rssi - 20) * (GOOD - FAIR) / 5 + FAIR;
+	else if (rssi < 40)
+		rssi_qual = (rssi - 30) * (VERY_GOOD - GOOD) /
+		    10 + GOOD;
+	else
+		rssi_qual = (rssi - 40) * (PERFECT - VERY_GOOD) /
+		    10 + VERY_GOOD;
+	quality = rssi_qual;
+
+	/* Quality by TX errors */
+	priv->wstats.discard.retries = priv->stats.tx_errors;
+
+	tx_retries = adapter->logmsg.retry;
+
+	if (tx_retries > 75)
+		tx_qual = (90 - tx_retries) * POOR / 15;
+	else if (tx_retries > 70)
+		tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR;
+	else if (tx_retries > 65)
+		tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR;
+	else if (tx_retries > 50)
+		tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) /
+		    15 + GOOD;
+	else
+		tx_qual = (50 - tx_retries) *
+		    (PERFECT - VERY_GOOD) / 50 + VERY_GOOD;
+	quality = min(quality, tx_qual);
+
+	priv->wstats.discard.code = adapter->logmsg.wepundecryptable;
+	priv->wstats.discard.fragment = adapter->logmsg.fcserror;
+	priv->wstats.discard.retries = tx_retries;
+	priv->wstats.discard.misc = adapter->logmsg.ackfailure;
+
+	/* Calculate quality */
+	priv->wstats.qual.qual = max(quality, (u32)100);
+	priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+	stats_valid = 1;
+
+	/* update stats asynchronously for future calls */
+	libertas_prepare_and_send_command(priv, cmd_802_11_rssi, 0,
+					0, 0, NULL);
+	libertas_prepare_and_send_command(priv, cmd_802_11_get_log, 0,
+					0, 0, NULL);
+out:
+	if (!stats_valid) {
+		priv->wstats.miss.beacon = 0;
+		priv->wstats.discard.retries = 0;
+		priv->wstats.qual.qual = 0;
+		priv->wstats.qual.level = 0;
+		priv->wstats.qual.noise = 0;
+		priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED;
+		priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID |
+		    IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
+	}
+
+	LEAVE ();
+	return &priv->wstats;
+
+
+}
+
+static int wlan_set_freq(struct net_device *dev, struct iw_request_info *info,
+		  struct iw_freq *fwrq, char *extra)
+{
+	int ret = 0;
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	int rc = -EINPROGRESS;	/* Call commit handler */
+	struct chan_freq_power *cfp;
+
+	ENTER();
+
+	/*
+	 * If setting by frequency, convert to a channel
+	 */
+	if (fwrq->e == 1) {
+
+		long f = fwrq->m / 100000;
+		int c = 0;
+
+		cfp = find_cfp_by_band_and_freq(adapter, 0, f);
+		if (!cfp) {
+			lbs_pr_debug(1, "Invalid freq=%ld\n", f);
+			return -EINVAL;
+		}
+
+		c = (int)cfp->channel;
+
+		if (c < 0)
+			return -EINVAL;
+
+		fwrq->e = 0;
+		fwrq->m = c;
+	}
+
+	/*
+	 * Setting by channel number
+	 */
+	if (fwrq->m > 1000 || fwrq->e > 0) {
+		rc = -EOPNOTSUPP;
+	} else {
+		int channel = fwrq->m;
+
+		cfp = libertas_find_cfp_by_band_and_channel(adapter, 0, channel);
+		if (!cfp) {
+			rc = -EINVAL;
+		} else {
+			if (adapter->inframode == wlan802_11ibss) {
+				rc = changeadhocchannel(priv, channel);
+				/*  If station is WEP enabled, send the
+				 *  command to set WEP in firmware
+				 */
+				if (adapter->secinfo.WEPstatus ==
+				    wlan802_11WEPenabled) {
+					lbs_pr_debug(1, "set_freq: WEP enabled\n");
+					ret = libertas_prepare_and_send_command(priv,
+								    cmd_802_11_set_wep,
+								    cmd_act_add,
+								    cmd_option_waitforrsp,
+								    0,
+								    NULL);
+
+					if (ret) {
+						LEAVE();
+						return ret;
+					}
+
+					adapter->currentpacketfilter |=
+					    cmd_act_mac_wep_enable;
+
+					libertas_set_mac_packet_filter(priv);
+				}
+			} else {
+				rc = -EOPNOTSUPP;
+			}
+		}
+	}
+
+	LEAVE();
+	return rc;
+}
+
+/**
+ *  @brief use index to get the data rate
+ *
+ *  @param index                The index of data rate
+ *  @return 	   		data rate or 0
+ */
+u32 libertas_index_to_data_rate(u8 index)
+{
+	if (index >= sizeof(libertas_wlan_data_rates))
+		index = 0;
+
+	return libertas_wlan_data_rates[index];
+}
+
+/**
+ *  @brief use rate to get the index
+ *
+ *  @param rate                 data rate
+ *  @return 	   		index or 0
+ */
+u8 libertas_data_rate_to_index(u32 rate)
+{
+	u8 *ptr;
+
+	if (rate)
+		if ((ptr = memchr(libertas_wlan_data_rates, (u8) rate,
+				  sizeof(libertas_wlan_data_rates))))
+			return (ptr - libertas_wlan_data_rates);
+
+	return 0;
+}
+
+static int wlan_set_rate(struct net_device *dev, struct iw_request_info *info,
+		  struct iw_param *vwrq, char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	u32 data_rate;
+	u16 action;
+	int ret = 0;
+	u8 rates[WLAN_SUPPORTED_RATES];
+	u8 *rate;
+
+	ENTER();
+
+	lbs_pr_debug(1, "Vwrq->value = %d\n", vwrq->value);
+
+	if (vwrq->value == -1) {
+		action = cmd_act_set_tx_auto;	// Auto
+		adapter->is_datarate_auto = 1;
+		adapter->datarate = 0;
+	} else {
+		if (vwrq->value % 100000) {
+			return -EINVAL;
+		}
+
+		data_rate = vwrq->value / 500000;
+
+		memset(rates, 0, sizeof(rates));
+		get_active_data_rates(adapter, rates);
+		rate = rates;
+		while (*rate) {
+			lbs_pr_debug(1, "Rate=0x%X  Wanted=0x%X\n", *rate,
+			       data_rate);
+			if ((*rate & 0x7f) == (data_rate & 0x7f))
+				break;
+			rate++;
+		}
+		if (!*rate) {
+			lbs_pr_alert( "The fixed data rate 0x%X is out "
+			       "of range.\n", data_rate);
+			return -EINVAL;
+		}
+
+		adapter->datarate = data_rate;
+		action = cmd_act_set_tx_fix_rate;
+		adapter->is_datarate_auto = 0;
+	}
+
+	ret = libertas_prepare_and_send_command(priv, cmd_802_11_data_rate,
+				    action, cmd_option_waitforrsp, 0, NULL);
+
+	LEAVE();
+	return ret;
+}
+
+static int wlan_get_rate(struct net_device *dev, struct iw_request_info *info,
+		  struct iw_param *vwrq, char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	if (adapter->is_datarate_auto) {
+		vwrq->fixed = 0;
+	} else {
+		vwrq->fixed = 1;
+	}
+
+	vwrq->value = adapter->datarate * 500000;
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_set_mode(struct net_device *dev,
+		  struct iw_request_info *info, u32 * uwrq, char *extra)
+{
+	int ret = 0;
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	struct assoc_request * assoc_req;
+	enum WLAN_802_11_NETWORK_INFRASTRUCTURE new_mode;
+
+	ENTER();
+
+	switch (*uwrq) {
+	case IW_MODE_ADHOC:
+		lbs_pr_debug(1, "Wanted mode is ad-hoc: current datarate=%#x\n",
+		       adapter->datarate);
+		new_mode = wlan802_11ibss;
+		adapter->adhocchannel = DEFAULT_AD_HOC_CHANNEL;
+		break;
+
+	case IW_MODE_INFRA:
+		lbs_pr_debug(1, "Wanted mode is Infrastructure\n");
+		new_mode = wlan802_11infrastructure;
+		break;
+
+	case IW_MODE_AUTO:
+		lbs_pr_debug(1, "Wanted mode is Auto\n");
+		new_mode = wlan802_11autounknown;
+		break;
+
+	default:
+		lbs_pr_debug(1, "Wanted mode is Unknown: 0x%x\n", *uwrq);
+		return -EINVAL;
+	}
+
+	mutex_lock(&adapter->lock);
+	assoc_req = wlan_get_association_request(adapter);
+	if (!assoc_req) {
+		ret = -ENOMEM;
+	} else {
+		assoc_req->mode = new_mode;
+	}
+
+	if (ret == 0) {
+		set_bit(ASSOC_FLAG_MODE, &assoc_req->flags);
+		wlan_postpone_association_work(priv);
+	} else {
+		wlan_cancel_association_work(priv);
+	}
+	mutex_unlock(&adapter->lock);
+
+	LEAVE();
+	return ret;
+}
+
+
+/**
+ *  @brief Get Encryption key
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info			A pointer to iw_request_info structure
+ *  @param vwrq 		A pointer to iw_param structure
+ *  @param extra		A pointer to extra data buf
+ *  @return 	   		0 --success, otherwise fail
+ */
+static int wlan_get_encode(struct net_device *dev,
+			   struct iw_request_info *info,
+			   struct iw_point *dwrq, u8 * extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+
+	ENTER();
+
+	lbs_pr_debug(1, "flags=0x%x index=%d length=%d wep_tx_keyidx=%d\n",
+	       dwrq->flags, index, dwrq->length, adapter->wep_tx_keyidx);
+
+	dwrq->flags = 0;
+
+	/* Authentication method */
+	switch (adapter->secinfo.authmode) {
+	case wlan802_11authmodeopen:
+		dwrq->flags = IW_ENCODE_OPEN;
+		break;
+
+	case wlan802_11authmodeshared:
+	case wlan802_11authmodenetworkEAP:
+		dwrq->flags = IW_ENCODE_RESTRICTED;
+		break;
+	default:
+		dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN;
+		break;
+	}
+
+	if ((adapter->secinfo.WEPstatus == wlan802_11WEPenabled)
+	    || adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) {
+		dwrq->flags &= ~IW_ENCODE_DISABLED;
+	} else {
+		dwrq->flags |= IW_ENCODE_DISABLED;
+	}
+
+	memset(extra, 0, 16);
+
+	mutex_lock(&adapter->lock);
+
+	/* Default to returning current transmit key */
+	if (index < 0)
+		index = adapter->wep_tx_keyidx;
+
+	if ((adapter->wep_keys[index].len) &&
+	    (adapter->secinfo.WEPstatus == wlan802_11WEPenabled)) {
+		memcpy(extra, adapter->wep_keys[index].key,
+		       adapter->wep_keys[index].len);
+		dwrq->length = adapter->wep_keys[index].len;
+
+		dwrq->flags |= (index + 1);
+		/* Return WEP enabled */
+		dwrq->flags &= ~IW_ENCODE_DISABLED;
+	} else if ((adapter->secinfo.WPAenabled)
+		   || (adapter->secinfo.WPA2enabled)) {
+		/* return WPA enabled */
+		dwrq->flags &= ~IW_ENCODE_DISABLED;
+	} else {
+		dwrq->flags |= IW_ENCODE_DISABLED;
+	}
+
+	mutex_unlock(&adapter->lock);
+
+	dwrq->flags |= IW_ENCODE_NOKEY;
+
+	lbs_pr_debug(1, "key:%02x:%02x:%02x:%02x:%02x:%02x keylen=%d\n",
+	       extra[0], extra[1], extra[2],
+	       extra[3], extra[4], extra[5], dwrq->length);
+
+	lbs_pr_debug(1, "Return flags=0x%x\n", dwrq->flags);
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief Set Encryption key (internal)
+ *
+ *  @param priv			A pointer to private card structure
+ *  @param key_material		A pointer to key material
+ *  @param key_length		length of key material
+ *  @param index		key index to set
+ *  @param set_tx_key		Force set TX key (1 = yes, 0 = no)
+ *  @return 	   		0 --success, otherwise fail
+ */
+static int wlan_set_wep_key(struct assoc_request *assoc_req,
+			    const char *key_material,
+			    u16 key_length,
+			    u16 index,
+			    int set_tx_key)
+{
+	struct WLAN_802_11_KEY *pkey;
+
+	ENTER();
+
+	/* Paranoid validation of key index */
+	if (index > 3) {
+		LEAVE();
+		return -EINVAL;
+	}
+
+	/* validate max key length */
+	if (key_length > KEY_LEN_WEP_104) {
+		LEAVE();
+		return -EINVAL;
+	}
+
+	pkey = &assoc_req->wep_keys[index];
+
+	if (key_length > 0) {
+		memset(pkey, 0, sizeof(struct WLAN_802_11_KEY));
+		pkey->type = KEY_TYPE_ID_WEP;
+
+		/* Standardize the key length */
+		pkey->len = (key_length > KEY_LEN_WEP_40) ?
+		                KEY_LEN_WEP_104 : KEY_LEN_WEP_40;
+		memcpy(pkey->key, key_material, key_length);
+	}
+
+	if (set_tx_key) {
+		/* Ensure the chosen key is valid */
+		if (!pkey->len) {
+			lbs_pr_debug(1, "key not set, so cannot enable it\n");
+			LEAVE();
+			return -EINVAL;
+		}
+		assoc_req->wep_tx_keyidx = index;
+	}
+
+	assoc_req->secinfo.WEPstatus = wlan802_11WEPenabled;
+
+	LEAVE();
+	return 0;
+}
+
+static int validate_key_index(u16 def_index, u16 raw_index,
+			      u16 *out_index, u16 *is_default)
+{
+	if (!out_index || !is_default)
+		return -EINVAL;
+
+	/* Verify index if present, otherwise use default TX key index */
+	if (raw_index > 0) {
+		if (raw_index > 4)
+			return -EINVAL;
+		*out_index = raw_index - 1;
+	} else {
+		*out_index = def_index;
+		*is_default = 1;
+	}
+	return 0;
+}
+
+static void disable_wep(struct assoc_request *assoc_req)
+{
+	int i;
+
+	/* Set Open System auth mode */
+	assoc_req->secinfo.authmode = wlan802_11authmodeopen;
+
+	/* Clear WEP keys and mark WEP as disabled */
+	assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled;
+	for (i = 0; i < 4; i++)
+		assoc_req->wep_keys[i].len = 0;
+
+	set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
+	set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
+}
+
+/**
+ *  @brief Set Encryption key
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info			A pointer to iw_request_info structure
+ *  @param vwrq 		A pointer to iw_param structure
+ *  @param extra		A pointer to extra data buf
+ *  @return 	   		0 --success, otherwise fail
+ */
+static int wlan_set_encode(struct net_device *dev,
+		    struct iw_request_info *info,
+		    struct iw_point *dwrq, char *extra)
+{
+	int ret = 0;
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	struct assoc_request * assoc_req;
+	u16 is_default = 0, index = 0, set_tx_key = 0;
+
+	ENTER();
+
+	mutex_lock(&adapter->lock);
+	assoc_req = wlan_get_association_request(adapter);
+	if (!assoc_req) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	if (dwrq->flags & IW_ENCODE_DISABLED) {
+		disable_wep (assoc_req);
+		goto out;
+	}
+
+	ret = validate_key_index(assoc_req->wep_tx_keyidx,
+	                         (dwrq->flags & IW_ENCODE_INDEX),
+	                         &index, &is_default);
+	if (ret) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* If WEP isn't enabled, or if there is no key data but a valid
+	 * index, set the TX key.
+	 */
+	if ((assoc_req->secinfo.WEPstatus != wlan802_11WEPenabled)
+	    || (dwrq->length == 0 && !is_default))
+		set_tx_key = 1;
+
+	ret = wlan_set_wep_key(assoc_req, extra, dwrq->length, index, set_tx_key);
+	if (ret)
+		goto out;
+
+	if (dwrq->length)
+		set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
+	if (set_tx_key)
+		set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags);
+
+	if (dwrq->flags & IW_ENCODE_RESTRICTED) {
+		assoc_req->secinfo.authmode = wlan802_11authmodeshared;
+	} else if (dwrq->flags & IW_ENCODE_OPEN) {
+		assoc_req->secinfo.authmode = wlan802_11authmodeopen;
+	}
+
+out:
+	if (ret == 0) {
+		set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
+		wlan_postpone_association_work(priv);
+	} else {
+		wlan_cancel_association_work(priv);
+	}
+	mutex_unlock(&adapter->lock);
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief Get Extended Encryption key (WPA/802.1x and WEP)
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info			A pointer to iw_request_info structure
+ *  @param vwrq 		A pointer to iw_param structure
+ *  @param extra		A pointer to extra data buf
+ *  @return 	   		0 on success, otherwise failure
+ */
+static int wlan_get_encodeext(struct net_device *dev,
+			      struct iw_request_info *info,
+			      struct iw_point *dwrq,
+			      char *extra)
+{
+	int ret = -EINVAL;
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+	int index, max_key_len;
+
+	ENTER();
+
+	max_key_len = dwrq->length - sizeof(*ext);
+	if (max_key_len < 0)
+		goto out;
+
+	index = dwrq->flags & IW_ENCODE_INDEX;
+	if (index) {
+		if (index < 1 || index > 4)
+			goto out;
+		index--;
+	} else {
+		index = adapter->wep_tx_keyidx;
+	}
+
+	if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY &&
+	    ext->alg != IW_ENCODE_ALG_WEP) {
+		if (index != 0 || adapter->inframode != wlan802_11infrastructure)
+			goto out;
+	}
+
+	dwrq->flags = index + 1;
+	memset(ext, 0, sizeof(*ext));
+
+	if ((adapter->secinfo.WEPstatus == wlan802_11WEPdisabled)
+	    && !adapter->secinfo.WPAenabled && !adapter->secinfo.WPA2enabled) {
+		ext->alg = IW_ENCODE_ALG_NONE;
+		ext->key_len = 0;
+		dwrq->flags |= IW_ENCODE_DISABLED;
+	} else {
+		u8 *key = NULL;
+
+		if ((adapter->secinfo.WEPstatus == wlan802_11WEPenabled)
+		    && !adapter->secinfo.WPAenabled
+		    && !adapter->secinfo.WPA2enabled) {
+			ext->alg = IW_ENCODE_ALG_WEP;
+			ext->key_len = adapter->wep_keys[index].len;
+			key = &adapter->wep_keys[index].key[0];
+		} else if ((adapter->secinfo.WEPstatus == wlan802_11WEPdisabled) &&
+		           (adapter->secinfo.WPAenabled ||
+		            adapter->secinfo.WPA2enabled)) {
+			/* WPA */
+			ext->alg = IW_ENCODE_ALG_TKIP;
+			ext->key_len = 0;
+		} else {
+			goto out;
+		}
+
+		if (ext->key_len > max_key_len) {
+			ret = -E2BIG;
+			goto out;
+		}
+
+		if (ext->key_len)
+			memcpy(ext->key, key, ext->key_len);
+		else
+			dwrq->flags |= IW_ENCODE_NOKEY;
+		dwrq->flags |= IW_ENCODE_ENABLED;
+	}
+	ret = 0;
+
+out:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief Set Encryption key Extended (WPA/802.1x and WEP)
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info			A pointer to iw_request_info structure
+ *  @param vwrq 		A pointer to iw_param structure
+ *  @param extra		A pointer to extra data buf
+ *  @return 	   		0 --success, otherwise fail
+ */
+static int wlan_set_encodeext(struct net_device *dev,
+			      struct iw_request_info *info,
+			      struct iw_point *dwrq,
+			      char *extra)
+{
+	int ret = 0;
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+	int alg = ext->alg;
+	struct assoc_request * assoc_req;
+
+	ENTER();
+
+	mutex_lock(&adapter->lock);
+	assoc_req = wlan_get_association_request(adapter);
+	if (!assoc_req) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	if ((alg == IW_ENCODE_ALG_NONE) || (dwrq->flags & IW_ENCODE_DISABLED)) {
+		disable_wep (assoc_req);
+	} else if (alg == IW_ENCODE_ALG_WEP) {
+		u16 is_default = 0, index, set_tx_key = 0;
+
+		ret = validate_key_index(assoc_req->wep_tx_keyidx,
+		                         (dwrq->flags & IW_ENCODE_INDEX),
+		                         &index, &is_default);
+		if (ret)
+			goto out;
+
+		/* If WEP isn't enabled, or if there is no key data but a valid
+		 * index, or if the set-TX-key flag was passed, set the TX key.
+		 */
+		if ((assoc_req->secinfo.WEPstatus != wlan802_11WEPenabled)
+		    || (dwrq->length == 0 && !is_default)
+		    || (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY))
+			set_tx_key = 1;
+
+		/* Copy key to driver */
+		ret = wlan_set_wep_key (assoc_req, ext->key, ext->key_len, index,
+					set_tx_key);
+		if (ret)
+			goto out;
+
+		if (dwrq->flags & IW_ENCODE_RESTRICTED) {
+			assoc_req->secinfo.authmode =
+			    wlan802_11authmodeshared;
+		} else if (dwrq->flags & IW_ENCODE_OPEN) {
+			assoc_req->secinfo.authmode =
+			    wlan802_11authmodeopen;
+		}
+
+		/* Mark the various WEP bits as modified */
+		set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
+		if (dwrq->length)
+			set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
+		if (set_tx_key)
+			set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags);
+
+	} else if ((alg == IW_ENCODE_ALG_TKIP) || (alg == IW_ENCODE_ALG_CCMP)) {
+		struct WLAN_802_11_KEY * pkey;
+
+		/* validate key length */
+		if (((alg == IW_ENCODE_ALG_TKIP)
+			&& (ext->key_len != KEY_LEN_WPA_TKIP))
+		    || ((alg == IW_ENCODE_ALG_CCMP)
+		        && (ext->key_len != KEY_LEN_WPA_AES))) {
+				lbs_pr_debug(1, "Invalid size %d for key of alg"
+				       "type %d.\n",
+				       ext->key_len,
+				       alg);
+				ret = -EINVAL;
+				goto out;
+		}
+
+		if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
+			pkey = &assoc_req->wpa_mcast_key;
+		else
+			pkey = &assoc_req->wpa_unicast_key;
+
+		memset(pkey, 0, sizeof (struct WLAN_802_11_KEY));
+		memcpy(pkey->key, ext->key, ext->key_len);
+		pkey->len = ext->key_len;
+		pkey->flags = KEY_INFO_WPA_ENABLED;
+
+		if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
+			pkey->flags |= KEY_INFO_WPA_MCAST;
+			set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
+		} else {
+			pkey->flags |= KEY_INFO_WPA_UNICAST;
+			set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
+		}
+
+		if (alg == IW_ENCODE_ALG_TKIP)
+			pkey->type = KEY_TYPE_ID_TKIP;
+		else if (alg == IW_ENCODE_ALG_CCMP)
+			pkey->type = KEY_TYPE_ID_AES;
+
+		/* If WPA isn't enabled yet, do that now */
+		if (   assoc_req->secinfo.WPAenabled == 0
+		    && assoc_req->secinfo.WPA2enabled == 0) {
+			assoc_req->secinfo.WPAenabled = 1;
+			assoc_req->secinfo.WPA2enabled = 1;
+			set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
+		}
+
+		disable_wep (assoc_req);
+	}
+
+out:
+	if (ret == 0) {
+		wlan_postpone_association_work(priv);
+	} else {
+		wlan_cancel_association_work(priv);
+	}
+	mutex_unlock(&adapter->lock);
+
+	LEAVE();
+	return ret;
+}
+
+
+static int wlan_set_genie(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_point *dwrq,
+			  char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+	struct assoc_request * assoc_req;
+
+	ENTER();
+
+	mutex_lock(&adapter->lock);
+	assoc_req = wlan_get_association_request(adapter);
+	if (!assoc_req) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	if (dwrq->length > MAX_WPA_IE_LEN ||
+	    (dwrq->length && extra == NULL)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (dwrq->length) {
+		memcpy(&assoc_req->wpa_ie[0], extra, dwrq->length);
+		assoc_req->wpa_ie_len = dwrq->length;
+	} else {
+		memset(&assoc_req->wpa_ie[0], 0, sizeof(adapter->wpa_ie));
+		assoc_req->wpa_ie_len = 0;
+	}
+
+out:
+	if (ret == 0) {
+		set_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags);
+		wlan_postpone_association_work(priv);
+	} else {
+		wlan_cancel_association_work(priv);
+	}
+	mutex_unlock(&adapter->lock);
+
+	LEAVE();
+	return ret;
+}
+
+static int wlan_get_genie(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_point *dwrq,
+			  char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	if (adapter->wpa_ie_len == 0) {
+		dwrq->length = 0;
+		LEAVE();
+		return 0;
+	}
+
+	if (dwrq->length < adapter->wpa_ie_len) {
+		LEAVE();
+		return -E2BIG;
+	}
+
+	dwrq->length = adapter->wpa_ie_len;
+	memcpy(extra, &adapter->wpa_ie[0], adapter->wpa_ie_len);
+
+	LEAVE();
+	return 0;
+}
+
+
+static int wlan_set_auth(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct iw_param *dwrq,
+			 char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	struct assoc_request * assoc_req;
+	int ret = 0;
+	int updated = 0;
+
+	ENTER();
+
+	mutex_lock(&adapter->lock);
+	assoc_req = wlan_get_association_request(adapter);
+	if (!assoc_req) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	switch (dwrq->flags & IW_AUTH_INDEX) {
+	case IW_AUTH_TKIP_COUNTERMEASURES:
+	case IW_AUTH_CIPHER_PAIRWISE:
+	case IW_AUTH_CIPHER_GROUP:
+	case IW_AUTH_KEY_MGMT:
+		/*
+		 * libertas does not use these parameters
+		 */
+		break;
+
+	case IW_AUTH_WPA_VERSION:
+		if (dwrq->value & IW_AUTH_WPA_VERSION_DISABLED) {
+			assoc_req->secinfo.WPAenabled = 0;
+			assoc_req->secinfo.WPA2enabled = 0;
+		}
+		if (dwrq->value & IW_AUTH_WPA_VERSION_WPA) {
+			assoc_req->secinfo.WPAenabled = 1;
+			assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled;
+			assoc_req->secinfo.authmode =
+			    wlan802_11authmodeopen;
+		}
+		if (dwrq->value & IW_AUTH_WPA_VERSION_WPA2) {
+			assoc_req->secinfo.WPA2enabled = 1;
+			assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled;
+			assoc_req->secinfo.authmode =
+			    wlan802_11authmodeopen;
+		}
+		updated = 1;
+		break;
+
+	case IW_AUTH_DROP_UNENCRYPTED:
+		if (dwrq->value) {
+			adapter->currentpacketfilter |=
+			    cmd_act_mac_strict_protection_enable;
+		} else {
+			adapter->currentpacketfilter &=
+			    ~cmd_act_mac_strict_protection_enable;
+		}
+		updated = 1;
+		break;
+
+	case IW_AUTH_80211_AUTH_ALG:
+		if (dwrq->value & IW_AUTH_ALG_SHARED_KEY) {
+			assoc_req->secinfo.authmode =
+			    wlan802_11authmodeshared;
+		} else if (dwrq->value & IW_AUTH_ALG_OPEN_SYSTEM) {
+			assoc_req->secinfo.authmode =
+			    wlan802_11authmodeopen;
+		} else if (dwrq->value & IW_AUTH_ALG_LEAP) {
+			assoc_req->secinfo.authmode =
+			    wlan802_11authmodenetworkEAP;
+		} else {
+			ret = -EINVAL;
+		}
+		updated = 1;
+		break;
+
+	case IW_AUTH_WPA_ENABLED:
+		if (dwrq->value) {
+			if (!assoc_req->secinfo.WPAenabled &&
+			    !assoc_req->secinfo.WPA2enabled) {
+				assoc_req->secinfo.WPAenabled = 1;
+				assoc_req->secinfo.WPA2enabled = 1;
+				assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled;
+				assoc_req->secinfo.authmode =
+				    wlan802_11authmodeopen;
+			}
+		} else {
+			assoc_req->secinfo.WPAenabled = 0;
+			assoc_req->secinfo.WPA2enabled = 0;
+		}
+		updated = 1;
+		break;
+
+	default:
+		ret = -EOPNOTSUPP;
+		break;
+	}
+
+out:
+	if (ret == 0) {
+		if (updated)
+			set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
+		wlan_postpone_association_work(priv);
+	} else if (ret != -EOPNOTSUPP) {
+		wlan_cancel_association_work(priv);
+	}
+	mutex_unlock(&adapter->lock);
+
+	LEAVE();
+	return ret;
+}
+
+static int wlan_get_auth(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct iw_param *dwrq,
+			 char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+
+	switch (dwrq->flags & IW_AUTH_INDEX) {
+	case IW_AUTH_WPA_VERSION:
+		dwrq->value = 0;
+		if (adapter->secinfo.WPAenabled)
+			dwrq->value |= IW_AUTH_WPA_VERSION_WPA;
+		if (adapter->secinfo.WPA2enabled)
+			dwrq->value |= IW_AUTH_WPA_VERSION_WPA2;
+		if (!dwrq->value)
+			dwrq->value |= IW_AUTH_WPA_VERSION_DISABLED;
+		break;
+
+	case IW_AUTH_DROP_UNENCRYPTED:
+		dwrq->value = 0;
+		if (adapter->currentpacketfilter &
+		    cmd_act_mac_strict_protection_enable)
+			dwrq->value = 1;
+		break;
+
+	case IW_AUTH_80211_AUTH_ALG:
+		switch (adapter->secinfo.authmode) {
+		case wlan802_11authmodeshared:
+			dwrq->value = IW_AUTH_ALG_SHARED_KEY;
+			break;
+		case wlan802_11authmodeopen:
+			dwrq->value = IW_AUTH_ALG_OPEN_SYSTEM;
+			break;
+		case wlan802_11authmodenetworkEAP:
+			dwrq->value = IW_AUTH_ALG_LEAP;
+			break;
+		default:
+			break;
+		}
+		break;
+
+	case IW_AUTH_WPA_ENABLED:
+		if (adapter->secinfo.WPAenabled && adapter->secinfo.WPA2enabled)
+			dwrq->value = 1;
+		break;
+
+	default:
+		LEAVE();
+		return -EOPNOTSUPP;
+	}
+
+	LEAVE();
+	return 0;
+}
+
+
+static int wlan_set_txpow(struct net_device *dev, struct iw_request_info *info,
+		   struct iw_param *vwrq, char *extra)
+{
+	int ret = 0;
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+
+	u16 dbm;
+
+	ENTER();
+
+	if (vwrq->disabled) {
+		wlan_radio_ioctl(priv, RADIO_OFF);
+		return 0;
+	}
+
+	adapter->preamble = cmd_type_auto_preamble;
+
+	wlan_radio_ioctl(priv, RADIO_ON);
+
+	if ((vwrq->flags & IW_TXPOW_TYPE) == IW_TXPOW_MWATT) {
+		dbm = (u16) mw_to_dbm(vwrq->value);
+	} else
+		dbm = (u16) vwrq->value;
+
+	/* auto tx power control */
+
+	if (vwrq->fixed == 0)
+		dbm = 0xffff;
+
+	lbs_pr_debug(1, "<1>TXPOWER SET %d dbm.\n", dbm);
+
+	ret = libertas_prepare_and_send_command(priv,
+				    cmd_802_11_rf_tx_power,
+				    cmd_act_tx_power_opt_set_low,
+				    cmd_option_waitforrsp, 0, (void *)&dbm);
+
+	LEAVE();
+	return ret;
+}
+
+static int wlan_get_essid(struct net_device *dev, struct iw_request_info *info,
+		   struct iw_point *dwrq, char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+
+	ENTER();
+	/*
+	 * Note : if dwrq->flags != 0, we should get the relevant SSID from
+	 * the SSID list...
+	 */
+
+	/*
+	 * Get the current SSID
+	 */
+	if (adapter->connect_status == libertas_connected) {
+		memcpy(extra, adapter->curbssparams.ssid.ssid,
+		       adapter->curbssparams.ssid.ssidlength);
+		extra[adapter->curbssparams.ssid.ssidlength] = '\0';
+	} else {
+		memset(extra, 0, 32);
+		extra[adapter->curbssparams.ssid.ssidlength] = '\0';
+	}
+	/*
+	 * If none, we may want to get the one that was set
+	 */
+
+	/* To make the driver backward compatible with WPA supplicant v0.2.4 */
+	if (dwrq->length == 32)	/* check with WPA supplicant buffer size */
+		dwrq->length = min_t(size_t, adapter->curbssparams.ssid.ssidlength,
+				   IW_ESSID_MAX_SIZE);
+	else
+		dwrq->length = adapter->curbssparams.ssid.ssidlength + 1;
+
+	dwrq->flags = 1;	/* active */
+
+	LEAVE();
+	return 0;
+}
+
+static int wlan_set_essid(struct net_device *dev, struct iw_request_info *info,
+		   struct iw_point *dwrq, char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+	struct WLAN_802_11_SSID ssid;
+	struct assoc_request * assoc_req;
+	int ssid_len = dwrq->length;
+
+	ENTER();
+
+	/*
+	 * WE-20 and earlier NULL pad the end of the SSID and increment
+	 * SSID length so it can be used like a string.  WE-21 and later don't,
+	 * but some userspace tools aren't able to cope with the change.
+	 */
+	if ((ssid_len > 0) && (extra[ssid_len - 1] == '\0'))
+		ssid_len--;
+
+	/* Check the size of the string */
+	if (ssid_len > IW_ESSID_MAX_SIZE) {
+		ret = -E2BIG;
+		goto out;
+	}
+
+	memset(&ssid, 0, sizeof(struct WLAN_802_11_SSID));
+
+	if (!dwrq->flags || !ssid_len) {
+		/* "any" SSID requested; leave SSID blank */
+	} else {
+		/* Specific SSID requested */
+		memcpy(&ssid.ssid, extra, ssid_len);
+		ssid.ssidlength = ssid_len;
+	}
+
+	lbs_pr_debug(1, "Requested new SSID = %s\n",
+	       (ssid.ssidlength > 0) ? (char *)ssid.ssid : "any");
+
+out:
+	mutex_lock(&adapter->lock);
+	if (ret == 0) {
+		/* Get or create the current association request */
+		assoc_req = wlan_get_association_request(adapter);
+		if (!assoc_req) {
+			ret = -ENOMEM;
+		} else {
+			/* Copy the SSID to the association request */
+			memcpy(&assoc_req->ssid, &ssid, sizeof(struct WLAN_802_11_SSID));
+			set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
+			wlan_postpone_association_work(priv);
+		}
+	}
+
+	/* Cancel the association request if there was an error */
+	if (ret != 0) {
+		wlan_cancel_association_work(priv);
+	}
+
+	mutex_unlock(&adapter->lock);
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief Connect to the AP or Ad-hoc Network with specific bssid
+ *
+ *  @param dev          A pointer to net_device structure
+ *  @param info         A pointer to iw_request_info structure
+ *  @param awrq         A pointer to iw_param structure
+ *  @param extra        A pointer to extra data buf
+ *  @return             0 --success, otherwise fail
+ */
+static int wlan_set_wap(struct net_device *dev, struct iw_request_info *info,
+		 struct sockaddr *awrq, char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	struct assoc_request * assoc_req;
+	int ret = 0;
+
+	ENTER();
+
+	if (awrq->sa_family != ARPHRD_ETHER)
+		return -EINVAL;
+
+	lbs_pr_debug(1, "ASSOC: WAP: sa_data: " MAC_FMT "\n", MAC_ARG(awrq->sa_data));
+
+	mutex_lock(&adapter->lock);
+
+	/* Get or create the current association request */
+	assoc_req = wlan_get_association_request(adapter);
+	if (!assoc_req) {
+		wlan_cancel_association_work(priv);
+		ret = -ENOMEM;
+	} else {
+		/* Copy the BSSID to the association request */
+		memcpy(&assoc_req->bssid, awrq->sa_data, ETH_ALEN);
+		set_bit(ASSOC_FLAG_BSSID, &assoc_req->flags);
+		wlan_postpone_association_work(priv);
+	}
+
+	mutex_unlock(&adapter->lock);
+
+	return ret;
+}
+
+void libertas_get_fwversion(wlan_adapter * adapter, char *fwversion, int maxlen)
+{
+	union {
+		u32 l;
+		u8 c[4];
+	} ver;
+	char fwver[32];
+
+	mutex_lock(&adapter->lock);
+	ver.l = adapter->fwreleasenumber;
+	mutex_unlock(&adapter->lock);
+
+	if (ver.c[3] == 0)
+		sprintf(fwver, "%u.%u.%u", ver.c[2], ver.c[1], ver.c[0]);
+	else
+		sprintf(fwver, "%u.%u.%u.p%u",
+			ver.c[2], ver.c[1], ver.c[0], ver.c[3]);
+
+	snprintf(fwversion, maxlen, fwver);
+}
+
+
+/*
+ * iwconfig settable callbacks
+ */
+static const iw_handler wlan_handler[] = {
+	(iw_handler) NULL,	/* SIOCSIWCOMMIT */
+	(iw_handler) wlan_get_name,	/* SIOCGIWNAME */
+	(iw_handler) NULL,	/* SIOCSIWNWID */
+	(iw_handler) NULL,	/* SIOCGIWNWID */
+	(iw_handler) wlan_set_freq,	/* SIOCSIWFREQ */
+	(iw_handler) wlan_get_freq,	/* SIOCGIWFREQ */
+	(iw_handler) wlan_set_mode,	/* SIOCSIWMODE */
+	(iw_handler) wlan_get_mode,	/* SIOCGIWMODE */
+	(iw_handler) NULL,	/* SIOCSIWSENS */
+	(iw_handler) NULL,	/* SIOCGIWSENS */
+	(iw_handler) NULL,	/* SIOCSIWRANGE */
+	(iw_handler) wlan_get_range,	/* SIOCGIWRANGE */
+	(iw_handler) NULL,	/* SIOCSIWPRIV */
+	(iw_handler) NULL,	/* SIOCGIWPRIV */
+	(iw_handler) NULL,	/* SIOCSIWSTATS */
+	(iw_handler) NULL,	/* SIOCGIWSTATS */
+	iw_handler_set_spy,	/* SIOCSIWSPY */
+	iw_handler_get_spy,	/* SIOCGIWSPY */
+	iw_handler_set_thrspy,	/* SIOCSIWTHRSPY */
+	iw_handler_get_thrspy,	/* SIOCGIWTHRSPY */
+	(iw_handler) wlan_set_wap,	/* SIOCSIWAP */
+	(iw_handler) wlan_get_wap,	/* SIOCGIWAP */
+	(iw_handler) NULL,	/* SIOCSIWMLME */
+	(iw_handler) NULL,	/* SIOCGIWAPLIST - deprecated */
+	(iw_handler) libertas_set_scan,	/* SIOCSIWSCAN */
+	(iw_handler) libertas_get_scan,	/* SIOCGIWSCAN */
+	(iw_handler) wlan_set_essid,	/* SIOCSIWESSID */
+	(iw_handler) wlan_get_essid,	/* SIOCGIWESSID */
+	(iw_handler) wlan_set_nick,	/* SIOCSIWNICKN */
+	(iw_handler) wlan_get_nick,	/* SIOCGIWNICKN */
+	(iw_handler) NULL,	/* -- hole -- */
+	(iw_handler) NULL,	/* -- hole -- */
+	(iw_handler) wlan_set_rate,	/* SIOCSIWRATE */
+	(iw_handler) wlan_get_rate,	/* SIOCGIWRATE */
+	(iw_handler) wlan_set_rts,	/* SIOCSIWRTS */
+	(iw_handler) wlan_get_rts,	/* SIOCGIWRTS */
+	(iw_handler) wlan_set_frag,	/* SIOCSIWFRAG */
+	(iw_handler) wlan_get_frag,	/* SIOCGIWFRAG */
+	(iw_handler) wlan_set_txpow,	/* SIOCSIWTXPOW */
+	(iw_handler) wlan_get_txpow,	/* SIOCGIWTXPOW */
+	(iw_handler) wlan_set_retry,	/* SIOCSIWRETRY */
+	(iw_handler) wlan_get_retry,	/* SIOCGIWRETRY */
+	(iw_handler) wlan_set_encode,	/* SIOCSIWENCODE */
+	(iw_handler) wlan_get_encode,	/* SIOCGIWENCODE */
+	(iw_handler) wlan_set_power,	/* SIOCSIWPOWER */
+	(iw_handler) wlan_get_power,	/* SIOCGIWPOWER */
+	(iw_handler) NULL,	/* -- hole -- */
+	(iw_handler) NULL,	/* -- hole -- */
+	(iw_handler) wlan_set_genie,	/* SIOCSIWGENIE */
+	(iw_handler) wlan_get_genie,	/* SIOCGIWGENIE */
+	(iw_handler) wlan_set_auth,	/* SIOCSIWAUTH */
+	(iw_handler) wlan_get_auth,	/* SIOCGIWAUTH */
+	(iw_handler) wlan_set_encodeext,/* SIOCSIWENCODEEXT */
+	(iw_handler) wlan_get_encodeext,/* SIOCGIWENCODEEXT */
+	(iw_handler) NULL,		/* SIOCSIWPMKSA */
+};
+
+struct iw_handler_def libertas_handler_def = {
+	.num_standard	= sizeof(wlan_handler) / sizeof(iw_handler),
+	.num_private	= sizeof(wlan_private_handler) / sizeof(iw_handler),
+	.num_private_args = sizeof(wlan_private_args) /
+		sizeof(struct iw_priv_args),
+	.standard	= (iw_handler *) wlan_handler,
+	.private	= (iw_handler *) wlan_private_handler,
+	.private_args	= (struct iw_priv_args *)wlan_private_args,
+	.get_wireless_stats = wlan_get_wireless_stats,
+};
diff --git a/drivers/net/wireless/libertas/wext.h b/drivers/net/wireless/libertas/wext.h
new file mode 100644
index 0000000..39f367c3
--- /dev/null
+++ b/drivers/net/wireless/libertas/wext.h
@@ -0,0 +1,147 @@
+/**
+  * This file contains definition for IOCTL call.
+  */
+#ifndef	_WLAN_WEXT_H_
+#define	_WLAN_WEXT_H_
+
+#define SUBCMD_OFFSET			4
+#define SUBCMD_DATA(x)			*((int *)(x->u.name + SUBCMD_OFFSET))
+
+/** PRIVATE CMD ID */
+#define	WLANIOCTL			SIOCIWFIRSTPRIV
+
+#define WLANSETWPAIE			(WLANIOCTL + 0)
+
+#define WLAN_SETINT_GETINT		(WLANIOCTL + 7)
+#define WLANNF					1
+#define WLANRSSI				2
+#define WLANENABLE11D				5
+#define WLANADHOCGRATE				6
+#define WLAN_SUBCMD_SET_PRESCAN			11
+
+#define WLAN_SETNONE_GETNONE	        (WLANIOCTL + 8)
+#define WLANDEAUTH                  		1
+#define WLANRADIOON                 		2
+#define WLANRADIOOFF                		3
+#define WLANREMOVEADHOCAES          		4
+#define WLANADHOCSTOP               		5
+#define WLANCIPHERTEST              		6
+#define WLANCRYPTOTEST				7
+
+#define WLANWLANIDLEON				10
+#define WLANWLANIDLEOFF				11
+#define WLAN_SUBCMD_BT_RESET			13
+#define WLAN_SUBCMD_FWT_RESET			14
+
+#define WLANGETLOG                  	(WLANIOCTL + 9)
+#define GETLOG_BUFSIZE  300
+
+#define WLANSCAN_TYPE			(WLANIOCTL + 11)
+
+#define WLAN_SETNONE_GETONEINT		(WLANIOCTL + 15)
+#define WLANGETREGION				1
+#define WLAN_GET_LISTEN_INTERVAL		2
+#define WLAN_GET_MULTIPLE_DTIM			3
+#define WLAN_GET_TX_RATE			4
+#define	WLANGETBCNAVG				5
+
+#define WLAN_GET_LINKMODE			6
+#define WLAN_GET_RADIOMODE			7
+#define WLAN_GET_DEBUGMODE			8
+#define WLAN_SUBCMD_FWT_CLEANUP			15
+#define WLAN_SUBCMD_FWT_TIME			16
+#define WLAN_SUBCMD_MESH_GET_TTL		17
+
+#define WLANREGCFRDWR			(WLANIOCTL + 18)
+
+#define WLAN_SETNONE_GETTWELVE_CHAR (WLANIOCTL + 19)
+#define WLAN_SUBCMD_GETRXANTENNA    1
+#define WLAN_SUBCMD_GETTXANTENNA    2
+#define WLAN_GET_TSF                3
+
+#define WLAN_SETNONE_GETWORDCHAR	(WLANIOCTL + 21)
+#define WLANGETADHOCAES				1
+
+#define WLAN_SETONEINT_GETONEINT	(WLANIOCTL + 23)
+#define WLAN_BEACON_INTERVAL			1
+#define	WLAN_LISTENINTRVL			4
+
+#define WLAN_TXCONTROL				6
+#define WLAN_NULLPKTINTERVAL			7
+
+#define WLAN_SETONEINT_GETNONE		(WLANIOCTL + 24)
+#define WLAN_SUBCMD_SETRXANTENNA		1
+#define WLAN_SUBCMD_SETTXANTENNA		2
+#define WLANSETAUTHALG				5
+#define WLANSET8021XAUTHALG			6
+#define WLANSETENCRYPTIONMODE			7
+#define WLANSETREGION				8
+#define WLAN_SET_LISTEN_INTERVAL		9
+
+#define WLAN_SET_MULTIPLE_DTIM			10
+#define WLAN_SET_ATIM_WINDOW			11
+#define WLANSETBCNAVG				13
+#define WLANSETDATAAVG				14
+#define WLAN_SET_LINKMODE			15
+#define WLAN_SET_RADIOMODE			16
+#define WLAN_SET_DEBUGMODE			17
+#define WLAN_SUBCMD_MESH_SET_TTL		18
+
+#define WLAN_SET128CHAR_GET128CHAR	(WLANIOCTL + 25)
+#define WLANSCAN_MODE				6
+
+#define WLAN_GET_ADHOC_STATUS			9
+
+#define WLAN_SUBCMD_BT_ADD			18
+#define WLAN_SUBCMD_BT_DEL   			19
+#define WLAN_SUBCMD_BT_LIST			20
+#define WLAN_SUBCMD_FWT_ADD				21
+#define WLAN_SUBCMD_FWT_DEL   		22
+#define WLAN_SUBCMD_FWT_LOOKUP		23
+#define WLAN_SUBCMD_FWT_LIST_NEIGHBOR			24
+#define WLAN_SUBCMD_FWT_LIST			25
+#define WLAN_SUBCMD_FWT_LIST_ROUTE			26
+
+#define WLAN_SET_GET_SIXTEEN_INT       (WLANIOCTL + 29)
+#define WLAN_TPCCFG                             1
+#define WLAN_POWERCFG                           2
+
+#define WLAN_AUTO_FREQ_SET			3
+#define WLAN_AUTO_FREQ_GET			4
+#define WLAN_LED_GPIO_CTRL			5
+#define WLAN_SCANPROBES 			6
+#define	WLAN_ADAPT_RATESET			8
+#define	WLAN_INACTIVITY_TIMEOUT			9
+#define WLANSNR					10
+#define WLAN_GET_RATE				11
+#define	WLAN_GET_RXINFO				12
+
+#define WLANCMD52RDWR			(WLANIOCTL + 30)
+#define WLANCMD53RDWR			(WLANIOCTL + 31)
+#define CMD53BUFLEN				32
+
+#define	REG_MAC					0x19
+#define	REG_BBP					0x1a
+#define	REG_RF					0x1b
+#define	REG_EEPROM				0x59
+#define WLAN_LINKMODE_802_3			0
+#define WLAN_LINKMODE_802_11			2
+#define WLAN_RADIOMODE_NONE    			0
+#define WLAN_RADIOMODE_RADIOTAP			2
+
+/** wlan_ioctl_regrdwr */
+struct wlan_ioctl_regrdwr {
+	/** Which register to access */
+	u16 whichreg;
+	/** Read or Write */
+	u16 action;
+	u32 offset;
+	u16 NOB;
+	u32 value;
+};
+
+extern struct iw_handler_def libertas_handler_def;
+int libertas_do_ioctl(struct net_device *dev, struct ifreq *req, int i);
+int wlan_radio_ioctl(wlan_private * priv, u8 option);
+
+#endif				/* _WLAN_WEXT_H_ */
diff --git a/drivers/net/wireless/netwave_cs.c b/drivers/net/wireless/netwave_cs.c
index a009ab5..45b00e1 100644
--- a/drivers/net/wireless/netwave_cs.c
+++ b/drivers/net/wireless/netwave_cs.c
@@ -1283,7 +1283,6 @@
 
 	skb_reserve( skb, 2);  /* Align IP on 16 byte */
 	skb_put( skb, rcvLen);
-	skb->dev = dev;
 
 	/* Copy packet fragments to the skb data area */
 	ptr = (u_char*) skb->data;
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 4e7f6cf..062286d 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -689,7 +689,7 @@
 	/* Note : gcc will optimise the whole section away if
 	 * WIRELESS_SPY is not defined... - Jean II */
 	if (SPY_NUMBER(priv)) {
-		orinoco_spy_gather(dev, skb->mac.raw + ETH_ALEN,
+		orinoco_spy_gather(dev, skb_mac_header(skb) + ETH_ALEN,
 				   desc->signal, desc->silence);
 	}
 }
@@ -770,7 +770,7 @@
 
 	/* Copy the 802.11 header to the skb */
 	memcpy(skb_put(skb, hdrlen), &(desc->frame_ctl), hdrlen);
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 
 	/* If any, copy the data from the card to the skb */
 	if (datalen > 0) {
@@ -915,7 +915,6 @@
 		memcpy(hdr->h_source, desc.addr2, ETH_ALEN);
 
 	dev->last_rx = jiffies;
-	skb->dev = dev;
 	skb->protocol = eth_type_trans(skb, dev);
 	skb->ip_summed = CHECKSUM_NONE;
 	if (fc & IEEE80211_FCTL_TODS)
diff --git a/drivers/net/wireless/prism54/islpci_eth.c b/drivers/net/wireless/prism54/islpci_eth.c
index b112291..dd070cc 100644
--- a/drivers/net/wireless/prism54/islpci_eth.c
+++ b/drivers/net/wireless/prism54/islpci_eth.c
@@ -136,7 +136,7 @@
 				printk("islpci_eth_transmit:wds_mac\n");
 #endif
 				memmove(skb->data + 6, src, skb->len);
-				memcpy(skb->data, wds_mac, 6);
+				skb_copy_to_linear_data(skb, wds_mac, 6);
 			} else {
 				memmove(skb->data, src, skb->len);
 			}
@@ -162,13 +162,16 @@
 
 			skb_put(newskb, init_wds ? skb->len + 6 : skb->len);
 			if (init_wds) {
-				memcpy(newskb->data + 6, skb->data, skb->len);
-				memcpy(newskb->data, wds_mac, 6);
+				skb_copy_from_linear_data(skb,
+							  newskb->data + 6,
+							  skb->len);
+				skb_copy_to_linear_data(newskb, wds_mac, 6);
 #ifdef ISLPCI_ETH_DEBUG
 				printk("islpci_eth_transmit:wds_mac\n");
 #endif
 			} else
-				memcpy(newskb->data, skb->data, skb->len);
+				skb_copy_from_linear_data(skb, newskb->data,
+							  skb->len);
 
 #if VERBOSE > SHOW_ERROR_MESSAGES
 			DEBUG(SHOW_TRACING, "memcpy %p %p %i wds %i\n",
@@ -303,7 +306,7 @@
 		skb_pull(*skb, sizeof (struct rfmon_header));
 
 	(*skb)->protocol = htons(ETH_P_802_2);
-	(*skb)->mac.raw = (*skb)->data;
+	skb_reset_mac_header(*skb);
 	(*skb)->pkt_type = PACKET_OTHERHOST;
 
 	return 0;
@@ -374,10 +377,6 @@
 	DEBUG(SHOW_BUFFER_CONTENTS, "\nrx %p ", skb->data);
 	display_buffer((char *) skb->data, skb->len);
 #endif
-
-	/* do some additional sk_buff and network layer parameters */
-	skb->dev = ndev;
-
 	/* take care of monitor mode and spy monitoring. */
 	if (unlikely(priv->iw_mode == IW_MODE_MONITOR))
 		discard = islpci_monitor_rx(priv, &skb);
@@ -398,8 +397,10 @@
 			/* Update spy records */
 			wireless_spy_update(ndev, annex->addr2, &wstats);
 
-			memcpy(skb->data + sizeof (struct rfmon_header),
-			       skb->data, 2 * ETH_ALEN);
+			skb_copy_from_linear_data(skb,
+						  (skb->data +
+						   sizeof(struct rfmon_header)),
+						  2 * ETH_ALEN);
 			skb_pull(skb, sizeof (struct rfmon_header));
 		}
 		skb->protocol = eth_type_trans(skb, ndev);
diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c
index 47b2ccb..3be6242 100644
--- a/drivers/net/wireless/ray_cs.c
+++ b/drivers/net/wireless/ray_cs.c
@@ -2232,7 +2232,6 @@
         return;
     }
     skb_reserve( skb, 2);   /* Align IP on 16 byte (TBD check this)*/
-    skb->dev = dev;
 
     DEBUG(4,"ray_cs rx_data total_len = %x, rx_len = %x\n",total_len,rx_len);
 
@@ -2243,7 +2242,8 @@
     rx_ptr += copy_from_rx_buff(local, rx_ptr, pkt_addr & RX_BUFF_END, rx_len);
     /* Get source address */
 #ifdef WIRELESS_SPY
-    memcpy(linksrcaddr, ((struct mac_header *)skb->data)->addr_2, ETH_ALEN);
+    skb_copy_from_linear_data_offset(skb, offsetof(struct mac_header, addr_2),
+				     linksrcaddr, ETH_ALEN);
 #endif
     /* Now, deal with encapsulation/translation/sniffer */
     if (!sniffer) {
diff --git a/drivers/net/wireless/strip.c b/drivers/net/wireless/strip.c
index f5ce1c6..2a299a0 100644
--- a/drivers/net/wireless/strip.c
+++ b/drivers/net/wireless/strip.c
@@ -2009,7 +2009,7 @@
 		       packetlen);
 		skb->dev = get_strip_dev(strip_info);
 		skb->protocol = header->protocol;
-		skb->mac.raw = skb->data;
+		skb_reset_mac_header(skb);
 
 		/* Having put a fake header on the front of the sk_buff for the */
 		/* benefit of tools like tcpdump, skb_pull now 'consumes' that  */
diff --git a/drivers/net/wireless/todo.txt b/drivers/net/wireless/todo.txt
deleted file mode 100644
index 3223401..0000000
--- a/drivers/net/wireless/todo.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-	Wireless Todo
-	-------------
-
-1) Bring other kernel Wireless LAN drivers here
-	Completed
-
-2) Bring new Wireless LAN driver not yet in the kernel there
-	See my web page for details
-	In particular : HostAP
-
-3) Misc
-	o Mark wavelan, wavelan_cs, netwave_cs drivers as obsolete
-	o Maybe arlan.c, ray_cs.c and strip.c also deserve to be obsolete
-
-	Jean II
diff --git a/drivers/net/wireless/wavelan.c b/drivers/net/wireless/wavelan.c
index 2aa3c76..1cf090d6 100644
--- a/drivers/net/wireless/wavelan.c
+++ b/drivers/net/wireless/wavelan.c
@@ -2512,14 +2512,13 @@
 		return;
 	}
 
-	skb->dev = dev;
-
 	/* Copy the packet to the buffer. */
 	obram_read(ioaddr, buf_off, skb_put(skb, sksize), sksize);
 	skb->protocol = eth_type_trans(skb, dev);
 
 #ifdef DEBUG_RX_INFO
-	wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read");
+	wv_packet_info(skb_mac_header(skb), sksize, dev->name,
+		       "wv_packet_read");
 #endif				/* DEBUG_RX_INFO */
 
 	/* Statistics-gathering and associated stuff.
@@ -2555,7 +2554,7 @@
 
 		/* Spying stuff */
 #ifdef IW_WIRELESS_SPY
-		wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE,
+		wl_spy_gather(dev, skb_mac_header(skb) + WAVELAN_ADDR_SIZE,
 			      stats);
 #endif /* IW_WIRELESS_SPY */
 #ifdef HISTOGRAM
@@ -2939,7 +2938,7 @@
 	 * need to pad. Jean II */
 	if (skb->len < ETH_ZLEN) {
 		memset(data, 0, ETH_ZLEN);
-		memcpy(data, skb->data, skb->len);
+		skb_copy_from_linear_data(skb, data, skb->len);
 		/* Write packet on the card */
 		if(wv_packet_write(dev, data, ETH_ZLEN))
 			return 1;	/* We failed */
diff --git a/drivers/net/wireless/wavelan_cs.c b/drivers/net/wireless/wavelan_cs.c
index b042397..67b867f8 100644
--- a/drivers/net/wireless/wavelan_cs.c
+++ b/drivers/net/wireless/wavelan_cs.c
@@ -2884,14 +2884,12 @@
       return;
     }
 
-  skb->dev = dev;
-
   skb_reserve(skb, 2);
   fd_p = read_ringbuf(dev, fd_p, (char *) skb_put(skb, sksize), sksize);
   skb->protocol = eth_type_trans(skb, dev);
 
 #ifdef DEBUG_RX_INFO
-  wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read");
+  wv_packet_info(skb_mac_header(skb), sksize, dev->name, "wv_packet_read");
 #endif	/* DEBUG_RX_INFO */
      
   /* Statistics gathering & stuff associated.
@@ -2925,7 +2923,7 @@
 #endif	/* WAVELAN_ROAMING */
 	  
 #ifdef WIRELESS_SPY
-      wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE, stats);
+      wl_spy_gather(dev, skb_mac_header(skb) + WAVELAN_ADDR_SIZE, stats);
 #endif	/* WIRELESS_SPY */
 #ifdef HISTOGRAM
       wl_his_gather(dev, stats);
diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c
index 6cb66a3..935b144 100644
--- a/drivers/net/wireless/zd1201.c
+++ b/drivers/net/wireless/zd1201.c
@@ -327,7 +327,6 @@
 			memcpy(skb_put(skb, 6), &data[datalen-8], 6);
 			memcpy(skb_put(skb, 2), &data[datalen-24], 2);
 			memcpy(skb_put(skb, len), data, len);
-			skb->dev = zd->dev;
 			skb->dev->last_rx = jiffies;
 			skb->protocol = eth_type_trans(skb, zd->dev);
 			zd->stats.rx_packets++;
@@ -385,7 +384,6 @@
 			memcpy(skb_put(skb, 2), &data[6], 2);
 			memcpy(skb_put(skb, len), data+8, len);
 		}
-		skb->dev = zd->dev;
 		skb->dev->last_rx = jiffies;
 		skb->protocol = eth_type_trans(skb, zd->dev);
 		zd->stats.rx_packets++;
@@ -809,10 +807,10 @@
 	txbuf[4] = 0x00;
 	txbuf[5] = 0x00;
 
-	memcpy(txbuf+6, skb->data+12, skb->len-12);
+	skb_copy_from_linear_data_offset(skb, 12, txbuf + 6, skb->len - 12);
 	if (pad)
 		txbuf[skb->len-12+6]=0;
-	memcpy(txbuf+skb->len-12+6+pad, skb->data, 12);
+	skb_copy_from_linear_data(skb, txbuf + skb->len - 12 + 6 + pad, 12);
 	*(__be16*)&txbuf[skb->len+6+pad] = htons(skb->len-12+6);
 	txbuf[txbuflen-1] = 0;
 
diff --git a/drivers/net/wireless/zd1211rw/Kconfig b/drivers/net/wireless/zd1211rw/Kconfig
index 66ed55b..d1ab24a 100644
--- a/drivers/net/wireless/zd1211rw/Kconfig
+++ b/drivers/net/wireless/zd1211rw/Kconfig
@@ -1,6 +1,7 @@
 config ZD1211RW
 	tristate "ZyDAS ZD1211/ZD1211B USB-wireless support"
-	depends on USB && IEEE80211 && IEEE80211_SOFTMAC && NET_RADIO && EXPERIMENTAL
+	depends on USB && IEEE80211_SOFTMAC && WLAN_80211 && EXPERIMENTAL
+	select WIRELESS_EXT
 	select FW_LOADER
 	---help---
 	  This is an experimental driver for the ZyDAS ZD1211/ZD1211B wireless
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
index 87ee3ee..95b4a2a 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -67,11 +67,12 @@
 	i += scnprint_mac_oui(chip->e2p_mac, buffer+i, size-i);
 	i += scnprintf(buffer+i, size-i, " ");
 	i += zd_rf_scnprint_id(&chip->rf, buffer+i, size-i);
-	i += scnprintf(buffer+i, size-i, " pa%1x %c%c%c%c", chip->pa_type,
+	i += scnprintf(buffer+i, size-i, " pa%1x %c%c%c%c%c", chip->pa_type,
 		chip->patch_cck_gain ? 'g' : '-',
 		chip->patch_cr157 ? '7' : '-',
 		chip->patch_6m_band_edge ? '6' : '-',
-		chip->new_phy_layout ? 'N' : '-');
+		chip->new_phy_layout ? 'N' : '-',
+		chip->al2230s_bit ? 'S' : '-');
 	return i;
 }
 
@@ -114,7 +115,7 @@
 	/* Allocate a single memory block for values and addresses. */
 	count16 = 2*count;
 	a16 = (zd_addr_t *) kmalloc(count16 * (sizeof(zd_addr_t) + sizeof(u16)),
-		                   GFP_NOFS);
+		                   GFP_KERNEL);
 	if (!a16) {
 		dev_dbg_f(zd_chip_dev(chip),
 			  "error ENOMEM in allocation of a16\n");
@@ -163,7 +164,7 @@
 
 	/* Allocate a single memory block for values and addresses. */
 	count16 = 2*count;
-	ioreqs16 = kmalloc(count16 * sizeof(struct zd_ioreq16), GFP_NOFS);
+	ioreqs16 = kmalloc(count16 * sizeof(struct zd_ioreq16), GFP_KERNEL);
 	if (!ioreqs16) {
 		r = -ENOMEM;
 		dev_dbg_f(zd_chip_dev(chip),
@@ -614,16 +615,24 @@
  * Vendor driver says: for FCC regulation, enabled per HWFeature 6M band edge
  * bit (for AL2230, AL2230S)
  */
-static int patch_6m_band_edge(struct zd_chip *chip, int channel)
+static int patch_6m_band_edge(struct zd_chip *chip, u8 channel)
+{
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	if (!chip->patch_6m_band_edge)
+		return 0;
+
+	return zd_rf_patch_6m_band_edge(&chip->rf, channel);
+}
+
+/* Generic implementation of 6M band edge patching, used by most RFs via
+ * zd_rf_generic_patch_6m() */
+int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel)
 {
 	struct zd_ioreq16 ioreqs[] = {
 		{ CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 },
 		{ CR47,  0x1e },
 	};
 
-	if (!chip->patch_6m_band_edge || !chip->rf.patch_6m_band_edge)
-		return 0;
-
 	/* FIXME: Channel 11 is not the edge for all regulatory domains. */
 	if (channel == 1 || channel == 11)
 		ioreqs[0].value = 0x12;
@@ -683,17 +692,17 @@
 		{ CR111, 0x27 }, { CR112, 0x27 }, { CR113, 0x27 },
 		{ CR114, 0x27 }, { CR115, 0x26 }, { CR116, 0x24 },
 		{ CR117, 0xfc }, { CR118, 0xfa }, { CR120, 0x4f },
-		{ CR123, 0x27 }, { CR125, 0xaa }, { CR127, 0x03 },
-		{ CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 },
-		{ CR131, 0x0C }, { CR136, 0xdf }, { CR137, 0x40 },
-		{ CR138, 0xa0 }, { CR139, 0xb0 }, { CR140, 0x99 },
-		{ CR141, 0x82 }, { CR142, 0x54 }, { CR143, 0x1c },
-		{ CR144, 0x6c }, { CR147, 0x07 }, { CR148, 0x4c },
-		{ CR149, 0x50 }, { CR150, 0x0e }, { CR151, 0x18 },
-		{ CR160, 0xfe }, { CR161, 0xee }, { CR162, 0xaa },
-		{ CR163, 0xfa }, { CR164, 0xfa }, { CR165, 0xea },
-		{ CR166, 0xbe }, { CR167, 0xbe }, { CR168, 0x6a },
-		{ CR169, 0xba }, { CR170, 0xba }, { CR171, 0xba },
+		{ CR125, 0xaa }, { CR127, 0x03 }, { CR128, 0x14 },
+		{ CR129, 0x12 }, { CR130, 0x10 }, { CR131, 0x0C },
+		{ CR136, 0xdf }, { CR137, 0x40 }, { CR138, 0xa0 },
+		{ CR139, 0xb0 }, { CR140, 0x99 }, { CR141, 0x82 },
+		{ CR142, 0x54 }, { CR143, 0x1c }, { CR144, 0x6c },
+		{ CR147, 0x07 }, { CR148, 0x4c }, { CR149, 0x50 },
+		{ CR150, 0x0e }, { CR151, 0x18 }, { CR160, 0xfe },
+		{ CR161, 0xee }, { CR162, 0xaa }, { CR163, 0xfa },
+		{ CR164, 0xfa }, { CR165, 0xea }, { CR166, 0xbe },
+		{ CR167, 0xbe }, { CR168, 0x6a }, { CR169, 0xba },
+		{ CR170, 0xba }, { CR171, 0xba },
 		/* Note: CR204 must lead the CR203 */
 		{ CR204, 0x7d },
 		{ },
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h
index e57ed75..ce0a5f6 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.h
+++ b/drivers/net/wireless/zd1211rw/zd_chip.h
@@ -833,6 +833,7 @@
 void zd_chip_disable_rx(struct zd_chip *chip);
 int zd_chip_enable_hwint(struct zd_chip *chip);
 int zd_chip_disable_hwint(struct zd_chip *chip);
+int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel);
 
 int zd_chip_set_rts_cts_rate_locked(struct zd_chip *chip,
 	u8 rts_rate, int preamble);
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 4c5f78e..6753d24 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -156,17 +156,8 @@
 static int reset_mode(struct zd_mac *mac)
 {
 	struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
-	struct zd_ioreq32 ioreqs[] = {
-		{ CR_RX_FILTER, STA_RX_FILTER },
-		{ CR_SNIFFER_ON, 0U },
-	};
-
-	if (ieee->iw_mode == IW_MODE_MONITOR) {
-		ioreqs[0].value = 0xffffffff;
-		ioreqs[1].value = 0x1;
-	}
-
-	return zd_iowrite32a(&mac->chip, ioreqs, ARRAY_SIZE(ioreqs));
+	u32 filter = (ieee->iw_mode == IW_MODE_MONITOR) ? ~0 : STA_RX_FILTER;
+	return zd_iowrite32(&mac->chip, CR_RX_FILTER, filter);
 }
 
 int zd_mac_open(struct net_device *netdev)
@@ -974,14 +965,14 @@
 	switch (ieee->iw_mode) {
 	case IW_MODE_ADHOC:
 		if ((fc & (IEEE80211_FCTL_TODS|IEEE80211_FCTL_FROMDS)) != 0 ||
-		    memcmp(hdr->addr3, ieee->bssid, ETH_ALEN) != 0)
+		    compare_ether_addr(hdr->addr3, ieee->bssid) != 0)
 			return 0;
 		break;
 	case IW_MODE_AUTO:
 	case IW_MODE_INFRA:
 		if ((fc & (IEEE80211_FCTL_TODS|IEEE80211_FCTL_FROMDS)) !=
 		    IEEE80211_FCTL_FROMDS ||
-		    memcmp(hdr->addr2, ieee->bssid, ETH_ALEN) != 0)
+		    compare_ether_addr(hdr->addr2, ieee->bssid) != 0)
 			return 0;
 		break;
 	default:
@@ -989,9 +980,9 @@
 		return 0;
 	}
 
-	return memcmp(hdr->addr1, netdev->dev_addr, ETH_ALEN) == 0 ||
+	return compare_ether_addr(hdr->addr1, netdev->dev_addr) == 0 ||
 	       (is_multicast_ether_addr(hdr->addr1) &&
-		memcmp(hdr->addr3, netdev->dev_addr, ETH_ALEN) != 0) ||
+		compare_ether_addr(hdr->addr3, netdev->dev_addr) != 0) ||
 	       (netdev->flags & IFF_PROMISC);
 }
 
@@ -1047,7 +1038,7 @@
 	hdr = (struct ieee80211_hdr_3addr *)buffer;
 	if (length < offsetof(struct ieee80211_hdr_3addr, addr3))
 		return;
-	if (memcmp(hdr->addr2, zd_mac_to_ieee80211(mac)->bssid, ETH_ALEN) != 0)
+	if (compare_ether_addr(hdr->addr2, zd_mac_to_ieee80211(mac)->bssid) != 0)
 		return;
 
 	spin_lock_irqsave(&mac->lock, flags);
diff --git a/drivers/net/wireless/zd1211rw/zd_rf.c b/drivers/net/wireless/zd1211rw/zd_rf.c
index f50cff3..549c23b 100644
--- a/drivers/net/wireless/zd1211rw/zd_rf.c
+++ b/drivers/net/wireless/zd1211rw/zd_rf.c
@@ -23,7 +23,7 @@
 #include "zd_ieee80211.h"
 #include "zd_chip.h"
 
-static const char *rfs[] = {
+static const char * const rfs[] = {
 	[0]		= "unknown RF0",
 	[1]		= "unknown RF1",
 	[UW2451_RF]	= "UW2451_RF",
@@ -34,7 +34,7 @@
 	[AL2210_RF]	= "AL2210_RF",
 	[MAXIM_NEW_RF]	= "MAXIM_NEW_RF",
 	[UW2453_RF]	= "UW2453_RF",
-	[AL2230S_RF]	= "AL2230S_RF",
+	[UNKNOWN_A_RF]	= "UNKNOWN_A_RF",
 	[RALINK_RF]	= "RALINK_RF",
 	[INTERSIL_RF]	= "INTERSIL_RF",
 	[RF2959_RF]	= "RF2959_RF",
@@ -154,3 +154,17 @@
 		r = t;
 	return r;
 }
+
+int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel)
+{
+	if (!rf->patch_6m_band_edge)
+		return 0;
+
+	return rf->patch_6m_band_edge(rf, channel);
+}
+
+int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel)
+{
+	return zd_chip_generic_patch_6m_band(zd_rf_to_chip(rf), channel);
+}
+
diff --git a/drivers/net/wireless/zd1211rw/zd_rf.h b/drivers/net/wireless/zd1211rw/zd_rf.h
index a57732e..aa9cc10 100644
--- a/drivers/net/wireless/zd1211rw/zd_rf.h
+++ b/drivers/net/wireless/zd1211rw/zd_rf.h
@@ -26,7 +26,7 @@
 #define AL2210_RF			0x7
 #define MAXIM_NEW_RF			0x8
 #define UW2453_RF			0x9
-#define AL2230S_RF			0xa
+#define UNKNOWN_A_RF			0xa
 #define RALINK_RF			0xb
 #define INTERSIL_RF			0xc
 #define RF2959_RF			0xd
@@ -47,17 +47,13 @@
 	u8 type;
 
 	u8 channel;
-	/*
-	 * Whether this RF should patch the 6M band edge
-	 * (assuming E2P_POD agrees)
-	 */
-	u8 patch_6m_band_edge:1;
 
 	/* RF-specific functions */
 	int (*init_hw)(struct zd_rf *rf);
 	int (*set_channel)(struct zd_rf *rf, u8 channel);
 	int (*switch_radio_on)(struct zd_rf *rf);
 	int (*switch_radio_off)(struct zd_rf *rf);
+	int (*patch_6m_band_edge)(struct zd_rf *rf, u8 channel);
 };
 
 const char *zd_rf_name(u8 type);
@@ -72,6 +68,9 @@
 int zd_switch_radio_on(struct zd_rf *rf);
 int zd_switch_radio_off(struct zd_rf *rf);
 
+int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel);
+int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel);
+
 /* Functions for individual RF chips */
 
 int zd_rf_init_rf2959(struct zd_rf *rf);
diff --git a/drivers/net/wireless/zd1211rw/zd_rf_al2230.c b/drivers/net/wireless/zd1211rw/zd_rf_al2230.c
index 5235a78..511392a 100644
--- a/drivers/net/wireless/zd1211rw/zd_rf_al2230.c
+++ b/drivers/net/wireless/zd1211rw/zd_rf_al2230.c
@@ -59,6 +59,18 @@
 	{ CR240, 0x57 }, { CR9,   0xe0 },
 };
 
+static const struct zd_ioreq16 ioreqs_init_al2230s[] = {
+	{ CR47,   0x1e }, /* MARK_002 */
+	{ CR106,  0x22 },
+	{ CR107,  0x2a }, /* MARK_002 */
+	{ CR109,  0x13 }, /* MARK_002 */
+	{ CR118,  0xf8 }, /* MARK_002 */
+	{ CR119,  0x12 }, { CR122,  0xe0 },
+	{ CR128,  0x10 }, /* MARK_001 from 0xe->0x10 */
+	{ CR129,  0x0e }, /* MARK_001 from 0xd->0x0e */
+	{ CR130,  0x10 }, /* MARK_001 from 0xb->0x0d */
+};
+
 static int zd1211b_al2230_finalize_rf(struct zd_chip *chip)
 {
 	int r;
@@ -90,7 +102,7 @@
 	int r;
 	struct zd_chip *chip = zd_rf_to_chip(rf);
 
-	static const struct zd_ioreq16 ioreqs[] = {
+	static const struct zd_ioreq16 ioreqs_init[] = {
 		{ CR15,   0x20 }, { CR23,   0x40 }, { CR24,  0x20 },
 		{ CR26,   0x11 }, { CR28,   0x3e }, { CR29,  0x00 },
 		{ CR44,   0x33 }, { CR106,  0x2a }, { CR107, 0x1a },
@@ -117,10 +129,9 @@
 		{ CR119,  0x10 }, { CR120,  0x4f }, { CR121, 0x77 },
 		{ CR122,  0xe0 }, { CR137,  0x88 }, { CR252, 0xff },
 		{ CR253,  0xff },
+	};
 
-		/* These following happen separately in the vendor driver */
-		{ },
-
+	static const struct zd_ioreq16 ioreqs_pll[] = {
 		/* shdnb(PLL_ON)=0 */
 		{ CR251,  0x2f },
 		/* shdnb(PLL_ON)=1 */
@@ -128,7 +139,7 @@
 		{ CR138,  0x28 }, { CR203,  0x06 },
 	};
 
-	static const u32 rv[] = {
+	static const u32 rv1[] = {
 		/* Channel 1 */
 		0x03f790,
 		0x033331,
@@ -137,6 +148,9 @@
 		0x0b3331,
 		0x03b812,
 		0x00fff3,
+	};
+
+	static const u32 rv2[] = {
 		0x000da4,
 		0x0f4dc5, /* fix freq shift, 0x04edc5 */
 		0x0805b6,
@@ -148,8 +162,9 @@
 		0x0bdffc,
 		0x00000d,
 		0x00500f,
+	};
 
-		/* These writes happen separately in the vendor driver */
+	static const u32 rv3[] = {
 		0x00d00f,
 		0x004c0f,
 		0x00540f,
@@ -157,11 +172,38 @@
 		0x00500f,
 	};
 
-	r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+	r = zd_iowrite16a_locked(chip, ioreqs_init, ARRAY_SIZE(ioreqs_init));
 	if (r)
 		return r;
 
-	r = zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS);
+	if (chip->al2230s_bit) {
+		r = zd_iowrite16a_locked(chip, ioreqs_init_al2230s,
+			ARRAY_SIZE(ioreqs_init_al2230s));
+		if (r)
+			return r;
+	}
+
+	r = zd_rfwritev_locked(chip, rv1, ARRAY_SIZE(rv1), RF_RV_BITS);
+	if (r)
+		return r;
+
+	/* improve band edge for AL2230S */
+	if (chip->al2230s_bit)
+		r = zd_rfwrite_locked(chip, 0x000824, RF_RV_BITS);
+	else
+		r = zd_rfwrite_locked(chip, 0x0005a4, RF_RV_BITS);
+	if (r)
+		return r;
+
+	r = zd_rfwritev_locked(chip, rv2, ARRAY_SIZE(rv2), RF_RV_BITS);
+	if (r)
+		return r;
+
+	r = zd_iowrite16a_locked(chip, ioreqs_pll, ARRAY_SIZE(ioreqs_pll));
+	if (r)
+		return r;
+
+	r = zd_rfwritev_locked(chip, rv3, ARRAY_SIZE(rv3), RF_RV_BITS);
 	if (r)
 		return r;
 
@@ -227,7 +269,9 @@
 		0x481dc0,
 		0xcfff00,
 		0x25a000,
+	};
 
+	static const u32 rv2[] = {
 		/* To improve AL2230 yield, improve phase noise, 4713 */
 		0x25a000,
 		0xa3b2f0,
@@ -250,7 +294,7 @@
 		{ CR251, 0x7f }, /* shdnb(PLL_ON)=1 */
 	};
 
-	static const u32 rv2[] = {
+	static const u32 rv3[] = {
 		/* To improve AL2230 yield, 4713 */
 		0xf01b00,
 		0xf01e00,
@@ -269,16 +313,35 @@
 	r = zd_iowrite16a_locked(chip, ioreqs1, ARRAY_SIZE(ioreqs1));
 	if (r)
 		return r;
+
+	if (chip->al2230s_bit) {
+		r = zd_iowrite16a_locked(chip, ioreqs_init_al2230s,
+			ARRAY_SIZE(ioreqs_init_al2230s));
+		if (r)
+			return r;
+	}
+
 	r = zd_rfwritev_cr_locked(chip, zd1211b_al2230_table[0], 3);
 	if (r)
 		return r;
 	r = zd_rfwritev_cr_locked(chip, rv1, ARRAY_SIZE(rv1));
 	if (r)
 		return r;
+
+	if (chip->al2230s_bit)
+		r = zd_rfwrite_locked(chip, 0x241000, RF_RV_BITS);
+	else
+		r = zd_rfwrite_locked(chip, 0x25a000, RF_RV_BITS);
+	if (r)
+		return r;
+
+	r = zd_rfwritev_cr_locked(chip, rv2, ARRAY_SIZE(rv2));
+	if (r)
+		return r;
 	r = zd_iowrite16a_locked(chip, ioreqs2, ARRAY_SIZE(ioreqs2));
 	if (r)
 		return r;
-	r = zd_rfwritev_cr_locked(chip, rv2, ARRAY_SIZE(rv2));
+	r = zd_rfwritev_cr_locked(chip, rv3, ARRAY_SIZE(rv3));
 	if (r)
 		return r;
 	r = zd_iowrite16a_locked(chip, ioreqs3, ARRAY_SIZE(ioreqs3));
@@ -358,12 +421,6 @@
 {
 	struct zd_chip *chip = zd_rf_to_chip(rf);
 
-	if (chip->al2230s_bit) {
-		dev_err(zd_chip_dev(chip), "AL2230S devices are not yet "
-			"supported by this driver.\n");
-		return -ENODEV;
-	}
-
 	rf->switch_radio_off = al2230_switch_radio_off;
 	if (chip->is_zd1211b) {
 		rf->init_hw = zd1211b_al2230_init_hw;
@@ -374,6 +431,6 @@
 		rf->set_channel = zd1211_al2230_set_channel;
 		rf->switch_radio_on = zd1211_al2230_switch_radio_on;
 	}
-	rf->patch_6m_band_edge = 1;
+	rf->patch_6m_band_edge = zd_rf_generic_patch_6m;
 	return 0;
 }
diff --git a/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c b/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c
index a289f95..5e5e9dd 100644
--- a/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c
+++ b/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c
@@ -51,9 +51,52 @@
 	0xd8c010,
 };
 
-static int al7230b_init_hw(struct zd_rf *rf)
+static const u32 rv_init1[] = {
+	0x3c9000,
+	0xbfffff,
+	0x700000,
+	0xf15d58,
+};
+
+static const u32 rv_init2[] = {
+	0xf15d59,
+	0xf15d5c,
+	0xf15d58,
+};
+
+static const struct zd_ioreq16 ioreqs_sw[] = {
+	{ CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 },
+	{ CR38,  0x38 }, { CR136, 0xdf },
+};
+
+static int zd1211b_al7230b_finalize(struct zd_chip *chip)
 {
-	int i, r;
+	int r;
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR80,  0x30 }, { CR81,  0x30 }, { CR79,  0x58 },
+		{ CR12,  0xf0 }, { CR77,  0x1b }, { CR78,  0x58 },
+		{ CR203, 0x04 },
+		{ },
+		{ CR240, 0x80 },
+	};
+
+	r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+	if (r)
+		return r;
+
+	if (chip->new_phy_layout) {
+		/* antenna selection? */
+		r = zd_iowrite16_locked(chip, 0xe5, CR9);
+		if (r)
+			return r;
+	}
+
+	return zd_iowrite16_locked(chip, 0x04, CR203);
+}
+
+static int zd1211_al7230b_init_hw(struct zd_rf *rf)
+{
+	int r;
 	struct zd_chip *chip = zd_rf_to_chip(rf);
 
 	/* All of these writes are identical to AL2230 unless otherwise
@@ -117,39 +160,24 @@
 	};
 
 	static const struct zd_ioreq16 ioreqs_2[] = {
-		/* PLL_ON */
-		{ CR251, 0x3f },
+		{ CR251, 0x3f }, /* PLL_ON */
 		{ CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 },
-		{ CR38, 0x38 }, { CR136, 0xdf },
+		{ CR38,  0x38 }, { CR136, 0xdf },
 	};
 
 	r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1));
 	if (r)
 		return r;
 
-	r = zd_rfwrite_cr_locked(chip, 0x09ec04);
-	if (r)
-		return r;
-	r = zd_rfwrite_cr_locked(chip, 0x8cccc8);
+	r = zd_rfwritev_cr_locked(chip, chan_rv[0], ARRAY_SIZE(chan_rv[0]));
 	if (r)
 		return r;
 
-	for (i = 0; i < ARRAY_SIZE(std_rv); i++) {
-		r = zd_rfwrite_cr_locked(chip, std_rv[i]);
-		if (r)
-			return r;
-	}
+	r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv));
+	if (r)
+		return r;
 
-	r = zd_rfwrite_cr_locked(chip, 0x3c9000);
-	if (r)
-		return r;
-	r = zd_rfwrite_cr_locked(chip, 0xbfffff);
-	if (r)
-		return r;
-	r = zd_rfwrite_cr_locked(chip, 0x700000);
-	if (r)
-		return r;
-	r = zd_rfwrite_cr_locked(chip, 0xf15d58);
+	r = zd_rfwritev_cr_locked(chip, rv_init1, ARRAY_SIZE(rv_init1));
 	if (r)
 		return r;
 
@@ -157,13 +185,7 @@
 	if (r)
 		return r;
 
-	r = zd_rfwrite_cr_locked(chip, 0xf15d59);
-	if (r)
-		return r;
-	r = zd_rfwrite_cr_locked(chip, 0xf15d5c);
-	if (r)
-		return r;
-	r = zd_rfwrite_cr_locked(chip, 0xf15d58);
+	r = zd_rfwritev_cr_locked(chip, rv_init2, ARRAY_SIZE(rv_init2));
 	if (r)
 		return r;
 
@@ -177,18 +199,134 @@
 	return 0;
 }
 
-static int al7230b_set_channel(struct zd_rf *rf, u8 channel)
+static int zd1211b_al7230b_init_hw(struct zd_rf *rf)
 {
-	int i, r;
-	const u32 *rv = chan_rv[channel-1];
+	int r;
 	struct zd_chip *chip = zd_rf_to_chip(rf);
 
-	struct zd_ioreq16 ioreqs_1[] = {
+	static const struct zd_ioreq16 ioreqs_1[] = {
+		{ CR240, 0x57 }, { CR9,   0x9 },
+		{ },
+		{ CR10,  0x8b }, { CR15,  0x20 },
+		{ CR17,  0x2B }, /* for newest (3rd cut) AL2230 */
+		{ CR20,  0x10 }, /* 4N25->Stone Request */
+		{ CR23,  0x40 }, { CR24,  0x20 }, { CR26,  0x93 },
+		{ CR28,  0x3e }, { CR29,  0x00 },
+		{ CR33,  0x28 }, /* 5613 */
+		{ CR34,  0x30 },
+		{ CR35,  0x3e }, /* for newest (3rd cut) AL2230 */
+		{ CR41,  0x24 }, { CR44,  0x32 },
+		{ CR46,  0x99 }, /* for newest (3rd cut) AL2230 */
+		{ CR47,  0x1e },
+
+		/* ZD1215 5610 */
+		{ CR48,  0x00 }, { CR49,  0x00 }, { CR51,  0x01 },
+		{ CR52,  0x80 }, { CR53,  0x7e }, { CR65,  0x00 },
+		{ CR66,  0x00 }, { CR67,  0x00 }, { CR68,  0x00 },
+		{ CR69,  0x28 },
+
+		{ CR79,  0x58 }, { CR80,  0x30 }, { CR81,  0x30 },
+		{ CR87,  0x0A }, { CR89,  0x04 },
+		{ CR90,  0x58 }, /* 5112 */
+		{ CR91,  0x00 }, /* 5613 */
+		{ CR92,  0x0a },
+		{ CR98,  0x8d }, /* 4804, for 1212 new algorithm */
+		{ CR99,  0x00 }, { CR100, 0x02 }, { CR101, 0x13 },
+		{ CR102, 0x27 },
+		{ CR106, 0x20 }, /* change to 0x24 for AL7230B */
+		{ CR109, 0x13 }, /* 4804, for 1212 new algorithm */
+		{ CR112, 0x1f },
+	};
+
+	static const struct zd_ioreq16 ioreqs_new_phy[] = {
+		{ CR107, 0x28 },
+		{ CR110, 0x1f }, /* 5127, 0x13->0x1f */
+		{ CR111, 0x1f }, /* 0x13 to 0x1f for AL7230B */
+		{ CR116, 0x2a }, { CR118, 0xfa }, { CR119, 0x12 },
+		{ CR121, 0x6c }, /* 5613 */
+	};
+
+	static const struct zd_ioreq16 ioreqs_old_phy[] = {
+		{ CR107, 0x24 },
+		{ CR110, 0x13 }, /* 5127, 0x13->0x1f */
+		{ CR111, 0x13 }, /* 0x13 to 0x1f for AL7230B */
+		{ CR116, 0x24 }, { CR118, 0xfc }, { CR119, 0x11 },
+		{ CR121, 0x6a }, /* 5613 */
+	};
+
+	static const struct zd_ioreq16 ioreqs_2[] = {
+		{ CR113, 0x27 }, { CR114, 0x27 }, { CR115, 0x24 },
+		{ CR117, 0xfa }, { CR120, 0x4f },
+		{ CR122, 0xfc }, /* E0->FCh at 4901 */
+		{ CR123, 0x57 }, /* 5613 */
+		{ CR125, 0xad }, /* 4804, for 1212 new algorithm */
+		{ CR126, 0x6c }, /* 5613 */
+		{ CR127, 0x03 }, /* 4804, for 1212 new algorithm */
+		{ CR130, 0x10 },
+		{ CR131, 0x00 }, /* 5112 */
+		{ CR137, 0x50 }, /* 5613 */
+		{ CR138, 0xa8 }, /* 5112 */
+		{ CR144, 0xac }, /* 5613 */
+		{ CR148, 0x40 }, /* 5112 */
+		{ CR149, 0x40 }, /* 4O07, 50->40 */
+		{ CR150, 0x1a }, /* 5112, 0C->1A */
+		{ CR252, 0x34 }, { CR253, 0x34 },
+		{ CR251, 0x2f }, /* PLL_OFF */
+	};
+
+	static const struct zd_ioreq16 ioreqs_3[] = {
+		{ CR251, 0x7f }, /* PLL_ON */
 		{ CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 },
 		{ CR38,  0x38 }, { CR136, 0xdf },
 	};
 
-	struct zd_ioreq16 ioreqs_2[] = {
+	r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1));
+	if (r)
+		return r;
+
+	if (chip->new_phy_layout)
+		r = zd_iowrite16a_locked(chip, ioreqs_new_phy,
+			ARRAY_SIZE(ioreqs_new_phy));
+	else
+		r = zd_iowrite16a_locked(chip, ioreqs_old_phy,
+			ARRAY_SIZE(ioreqs_old_phy));
+	if (r)
+		return r;
+
+	r = zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2));
+	if (r)
+		return r;
+
+	r = zd_rfwritev_cr_locked(chip, chan_rv[0], ARRAY_SIZE(chan_rv[0]));
+	if (r)
+		return r;
+
+	r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv));
+	if (r)
+		return r;
+
+	r = zd_rfwritev_cr_locked(chip, rv_init1, ARRAY_SIZE(rv_init1));
+	if (r)
+		return r;
+
+	r = zd_iowrite16a_locked(chip, ioreqs_3, ARRAY_SIZE(ioreqs_3));
+	if (r)
+		return r;
+
+	r = zd_rfwritev_cr_locked(chip, rv_init2, ARRAY_SIZE(rv_init2));
+	if (r)
+		return r;
+
+	return zd1211b_al7230b_finalize(chip);
+}
+
+static int zd1211_al7230b_set_channel(struct zd_rf *rf, u8 channel)
+{
+	int r;
+	const u32 *rv = chan_rv[channel-1];
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	static const struct zd_ioreq16 ioreqs[] = {
 		/* PLL_ON */
 		{ CR251, 0x3f },
 		{ CR203, 0x06 }, { CR240, 0x08 },
@@ -203,11 +341,9 @@
 	if (r)
 		return r;
 
-	for (i = 0; i < ARRAY_SIZE(std_rv); i++) {
-		r = zd_rfwrite_cr_locked(chip, std_rv[i]);
-		if (r)
-			return r;
-	}
+	r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv));
+	if (r)
+		return r;
 
 	r = zd_rfwrite_cr_locked(chip, 0x3c9000);
 	if (r)
@@ -216,24 +352,69 @@
 	if (r)
 		return r;
 
-	r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1));
+	r = zd_iowrite16a_locked(chip, ioreqs_sw, ARRAY_SIZE(ioreqs_sw));
 	if (r)
 		return r;
 
-	for (i = 0; i < 2; i++) {
-		r = zd_rfwrite_cr_locked(chip, rv[i]);
-		if (r)
-			return r;
-	}
+	r = zd_rfwritev_cr_locked(chip, rv, 2);
+	if (r)
+		return r;
 
 	r = zd_rfwrite_cr_locked(chip, 0x3c9000);
 	if (r)
 		return r;
 
-	return zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2));
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
 }
 
-static int al7230b_switch_radio_on(struct zd_rf *rf)
+static int zd1211b_al7230b_set_channel(struct zd_rf *rf, u8 channel)
+{
+	int r;
+	const u32 *rv = chan_rv[channel-1];
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	r = zd_iowrite16_locked(chip, 0x57, CR240);
+	if (r)
+		return r;
+	r = zd_iowrite16_locked(chip, 0xe4, CR9);
+	if (r)
+		return r;
+
+	/* PLL_OFF */
+	r = zd_iowrite16_locked(chip, 0x2f, CR251);
+	if (r)
+		return r;
+	r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv));
+	if (r)
+		return r;
+
+	r = zd_rfwrite_cr_locked(chip, 0x3c9000);
+	if (r)
+		return r;
+	r = zd_rfwrite_cr_locked(chip, 0xf15d58);
+	if (r)
+		return r;
+
+	r = zd_iowrite16a_locked(chip, ioreqs_sw, ARRAY_SIZE(ioreqs_sw));
+	if (r)
+		return r;
+
+	r = zd_rfwritev_cr_locked(chip, rv, 2);
+	if (r)
+		return r;
+
+	r = zd_rfwrite_cr_locked(chip, 0x3c9000);
+	if (r)
+		return r;
+
+	r = zd_iowrite16_locked(chip, 0x7f, CR251);
+	if (r)
+		return r;
+
+	return zd1211b_al7230b_finalize(chip);
+}
+
+static int zd1211_al7230b_switch_radio_on(struct zd_rf *rf)
 {
 	struct zd_chip *chip = zd_rf_to_chip(rf);
 	static const struct zd_ioreq16 ioreqs[] = {
@@ -244,6 +425,17 @@
 	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
 }
 
+static int zd1211b_al7230b_switch_radio_on(struct zd_rf *rf)
+{
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR11,  0x00 },
+		{ CR251, 0x7f },
+	};
+
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
 static int al7230b_switch_radio_off(struct zd_rf *rf)
 {
 	struct zd_chip *chip = zd_rf_to_chip(rf);
@@ -255,20 +447,45 @@
 	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
 }
 
+/* ZD1211B+AL7230B 6m band edge patching differs slightly from other
+ * configurations */
+static int zd1211b_al7230b_patch_6m(struct zd_rf *rf, u8 channel)
+{
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+	struct zd_ioreq16 ioreqs[] = {
+		{ CR128, 0x14 }, { CR129, 0x12 },
+	};
+
+	/* FIXME: Channel 11 is not the edge for all regulatory domains. */
+	if (channel == 1) {
+		ioreqs[0].value = 0x0e;
+		ioreqs[1].value = 0x10;
+	} else if (channel == 11) {
+		ioreqs[0].value = 0x10;
+		ioreqs[1].value = 0x10;
+	}
+
+	dev_dbg_f(zd_chip_dev(chip), "patching for channel %d\n", channel);
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
 int zd_rf_init_al7230b(struct zd_rf *rf)
 {
 	struct zd_chip *chip = zd_rf_to_chip(rf);
 
 	if (chip->is_zd1211b) {
-		dev_err(zd_chip_dev(chip), "AL7230B is currently not "
-			"supported for ZD1211B devices\n");
-		return -ENODEV;
+		rf->init_hw = zd1211b_al7230b_init_hw;
+		rf->switch_radio_on = zd1211b_al7230b_switch_radio_on;
+		rf->set_channel = zd1211b_al7230b_set_channel;
+		rf->patch_6m_band_edge = zd1211b_al7230b_patch_6m;
+	} else {
+		rf->init_hw = zd1211_al7230b_init_hw;
+		rf->switch_radio_on = zd1211_al7230b_switch_radio_on;
+		rf->set_channel = zd1211_al7230b_set_channel;
+		rf->patch_6m_band_edge = zd_rf_generic_patch_6m;
 	}
 
-	rf->init_hw = al7230b_init_hw;
-	rf->set_channel = al7230b_set_channel;
-	rf->switch_radio_on = al7230b_switch_radio_on;
 	rf->switch_radio_off = al7230b_switch_radio_off;
-	rf->patch_6m_band_edge = 1;
+
 	return 0;
 }
diff --git a/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c b/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c
index 5824727..2d736bd 100644
--- a/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c
+++ b/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c
@@ -21,7 +21,7 @@
 #include "zd_usb.h"
 #include "zd_chip.h"
 
-static u32 rf2959_table[][2] = {
+static const u32 rf2959_table[][2] = {
 	RF_CHANNEL( 1) = { 0x181979, 0x1e6666 },
 	RF_CHANNEL( 2) = { 0x181989, 0x1e6666 },
 	RF_CHANNEL( 3) = { 0x181999, 0x1e6666 },
@@ -228,7 +228,7 @@
 static int rf2959_set_channel(struct zd_rf *rf, u8 channel)
 {
 	int i, r;
-	u32 *rv = rf2959_table[channel-1];
+	const u32 *rv = rf2959_table[channel-1];
 	struct zd_chip *chip = zd_rf_to_chip(rf);
 
 	for (i = 0; i < 2; i++) {
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index edaaad2..e04cffc 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -52,6 +52,7 @@
 	{ USB_DEVICE(0x0b3b, 0x1630), .driver_info = DEVICE_ZD1211 },
 	{ USB_DEVICE(0x0586, 0x3401), .driver_info = DEVICE_ZD1211 },
 	{ USB_DEVICE(0x14ea, 0xab13), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x13b1, 0x001e), .driver_info = DEVICE_ZD1211 },
 	/* ZD1211B */
 	{ USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B },
 	{ USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B },
@@ -62,7 +63,10 @@
 	{ USB_DEVICE(0x0471, 0x1236), .driver_info = DEVICE_ZD1211B },
 	{ USB_DEVICE(0x13b1, 0x0024), .driver_info = DEVICE_ZD1211B },
 	{ USB_DEVICE(0x0586, 0x340f), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x0b05, 0x171b), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x0586, 0x3410), .driver_info = DEVICE_ZD1211B },
 	{ USB_DEVICE(0x0baf, 0x0121), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x0586, 0x3412), .driver_info = DEVICE_ZD1211B },
 	/* "Driverless" devices that need ejecting */
 	{ USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER },
 	{}
@@ -413,7 +417,7 @@
 
 	dev_dbg_f(zd_usb_dev(usb), "\n");
 
-	urb = usb_alloc_urb(0, GFP_NOFS);
+	urb = usb_alloc_urb(0, GFP_KERNEL);
 	if (!urb) {
 		r = -ENOMEM;
 		goto out;
@@ -431,7 +435,7 @@
 
 	/* TODO: make it a DMA buffer */
 	r = -ENOMEM;
-	transfer_buffer = kmalloc(USB_MAX_EP_INT_BUFFER, GFP_NOFS);
+	transfer_buffer = kmalloc(USB_MAX_EP_INT_BUFFER, GFP_KERNEL);
 	if (!transfer_buffer) {
 		dev_dbg_f(zd_usb_dev(usb),
 			"couldn't allocate transfer_buffer\n");
@@ -445,7 +449,7 @@
 			 intr->interval);
 
 	dev_dbg_f(zd_usb_dev(usb), "submit urb %p\n", intr->urb);
-	r = usb_submit_urb(urb, GFP_NOFS);
+	r = usb_submit_urb(urb, GFP_KERNEL);
 	if (r) {
 		dev_dbg_f(zd_usb_dev(usb),
 			 "Couldn't submit urb. Error number %d\n", r);
@@ -594,10 +598,10 @@
 	struct urb *urb;
 	void *buffer;
 
-	urb = usb_alloc_urb(0, GFP_NOFS);
+	urb = usb_alloc_urb(0, GFP_KERNEL);
 	if (!urb)
 		return NULL;
-	buffer = usb_buffer_alloc(udev, USB_MAX_RX_SIZE, GFP_NOFS,
+	buffer = usb_buffer_alloc(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
 		                  &urb->transfer_dma);
 	if (!buffer) {
 		usb_free_urb(urb);
@@ -630,7 +634,7 @@
 	dev_dbg_f(zd_usb_dev(usb), "\n");
 
 	r = -ENOMEM;
-	urbs = kcalloc(URBS_COUNT, sizeof(struct urb *), GFP_NOFS);
+	urbs = kcalloc(URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
 	if (!urbs)
 		goto error;
 	for (i = 0; i < URBS_COUNT; i++) {
@@ -651,7 +655,7 @@
 	spin_unlock_irq(&rx->lock);
 
 	for (i = 0; i < URBS_COUNT; i++) {
-		r = usb_submit_urb(urbs[i], GFP_NOFS);
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
 		if (r)
 			goto error_submit;
 	}
@@ -1157,7 +1161,7 @@
 	}
 
 	req_len = sizeof(struct usb_req_read_regs) + count * sizeof(__le16);
-	req = kmalloc(req_len, GFP_NOFS);
+	req = kmalloc(req_len, GFP_KERNEL);
 	if (!req)
 		return -ENOMEM;
 	req->id = cpu_to_le16(USB_REQ_READ_REGS);
@@ -1220,7 +1224,7 @@
 
 	req_len = sizeof(struct usb_req_write_regs) +
 		  count * sizeof(struct reg_data);
-	req = kmalloc(req_len, GFP_NOFS);
+	req = kmalloc(req_len, GFP_KERNEL);
 	if (!req)
 		return -ENOMEM;
 
@@ -1300,7 +1304,7 @@
 	bit_value_template &= ~(RF_IF_LE|RF_CLK|RF_DATA);
 
 	req_len = sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16);
-	req = kmalloc(req_len, GFP_NOFS);
+	req = kmalloc(req_len, GFP_KERNEL);
 	if (!req)
 		return -ENOMEM;
 
diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c
index 2412ce4..3f4a7cf 100644
--- a/drivers/net/yellowfin.c
+++ b/drivers/net/yellowfin.c
@@ -1137,7 +1137,6 @@
 				skb = dev_alloc_skb(pkt_len + 2);
 				if (skb == NULL)
 					break;
-				skb->dev = dev;
 				skb_reserve(skb, 2);	/* 16 byte align the IP header */
 				eth_copy_and_sum(skb, rx_skb->data, pkt_len, 0);
 				skb_put(skb, pkt_len);
diff --git a/drivers/net/znet.c b/drivers/net/znet.c
index b24b072..4032e9f 100644
--- a/drivers/net/znet.c
+++ b/drivers/net/znet.c
@@ -774,7 +774,6 @@
 				znet->stats.rx_dropped++;
 				break;
 			}
-			skb->dev = dev;
 
 			if (&znet->rx_cur[(pkt_len+1)>>1] > znet->rx_end) {
 				int semi_cnt = (znet->rx_end - znet->rx_cur)<<1;
diff --git a/drivers/parisc/led.c b/drivers/parisc/led.c
index d190c05..3df82fe 100644
--- a/drivers/parisc/led.c
+++ b/drivers/parisc/led.c
@@ -372,8 +372,6 @@
 		continue;
 	    if (LOOPBACK(in_dev->ifa_list->ifa_local))
 		continue;
-	    if (!dev->get_stats) 
-		continue;
 	    stats = dev->get_stats(dev);
 	    rx_total += stats->rx_packets;
 	    tx_total += stats->tx_packets;
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index a064f36..b5ac810 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -317,6 +317,10 @@
 {
 	int ret;
 
+	if (acpi_gbl_FADT.boot_flags & BAF_MSI_NOT_SUPPORTED) {
+		printk(KERN_INFO"ACPI FADT declares the system doesn't support MSI, so disable it\n");
+		pci_no_msi();
+	}
 	ret = register_acpi_bus_type(&acpi_pci_bus);
 	if (ret)
 		return 0;
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index a3c1755..39e80fc 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -434,11 +434,6 @@
 	drv->driver.mod_name = mod_name;
 	drv->driver.kobj.ktype = &pci_driver_kobj_type;
 
-	if (pci_multithread_probe)
-		drv->driver.multithread_probe = pci_multithread_probe;
-	else
-		drv->driver.multithread_probe = drv->multithread_probe;
-
 	spin_lock_init(&drv->dynids.lock);
 	INIT_LIST_HEAD(&drv->dynids.list);
 
@@ -574,6 +569,7 @@
 
 static int __init pci_driver_init(void)
 {
+	pci_bus_type.multithread_probe = pci_multithread_probe;
 	return bus_register(&pci_bus_type);
 }
 
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index d3eab05..2a45827 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -13,6 +13,7 @@
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/pci.h>
+#include <linux/pm.h>
 #include <linux/module.h>
 #include <linux/spinlock.h>
 #include <linux/string.h>
@@ -891,31 +892,48 @@
 }
 
 /**
- * pci_enable_wake - enable device to generate PME# when suspended
- * @dev: - PCI device to operate on
- * @state: - Current state of device.
- * @enable: - Flag to enable or disable generation
- * 
- * Set the bits in the device's PM Capabilities to generate PME# when
- * the system is suspended. 
+ * pci_enable_wake - enable PCI device as wakeup event source
+ * @dev: PCI device affected
+ * @state: PCI state from which device will issue wakeup events
+ * @enable: True to enable event generation; false to disable
  *
- * -EIO is returned if device doesn't have PM Capabilities. 
- * -EINVAL is returned if device supports it, but can't generate wake events.
- * 0 if operation is successful.
- * 
+ * This enables the device as a wakeup event source, or disables it.
+ * When such events involves platform-specific hooks, those hooks are
+ * called automatically by this routine.
+ *
+ * Devices with legacy power management (no standard PCI PM capabilities)
+ * always require such platform hooks.  Depending on the platform, devices
+ * supporting the standard PCI PME# signal may require such platform hooks;
+ * they always update bits in config space to allow PME# generation.
+ *
+ * -EIO is returned if the device can't ever be a wakeup event source.
+ * -EINVAL is returned if the device can't generate wakeup events from
+ * the specified PCI state.  Returns zero if the operation is successful.
  */
 int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
 {
 	int pm;
+	int status;
 	u16 value;
 
+	/* Note that drivers should verify device_may_wakeup(&dev->dev)
+	 * before calling this function.  Platform code should report
+	 * errors when drivers try to enable wakeup on devices that
+	 * can't issue wakeups, or on which wakeups were disabled by
+	 * userspace updating the /sys/devices.../power/wakeup file.
+	 */
+
+	status = call_platform_enable_wakeup(&dev->dev, enable);
+
 	/* find PCI PM capability in list */
 	pm = pci_find_capability(dev, PCI_CAP_ID_PM);
 
-	/* If device doesn't support PM Capabilities, but request is to disable
-	 * wake events, it's a nop; otherwise fail */
-	if (!pm) 
-		return enable ? -EIO : 0; 
+	/* If device doesn't support PM Capabilities, but caller wants to
+	 * disable wake events, it's a NOP.  Otherwise fail unless the
+	 * platform hooks handled this legacy device already.
+	 */
+	if (!pm)
+		return enable ? status : 0;
 
 	/* Check device's ability to generate PME# */
 	pci_read_config_word(dev,pm+PCI_PM_PMC,&value);
@@ -924,8 +942,14 @@
 	value >>= ffs(PCI_PM_CAP_PME_MASK) - 1;   /* First bit of mask */
 
 	/* Check if it can generate PME# from requested state. */
-	if (!value || !(value & (1 << state))) 
+	if (!value || !(value & (1 << state))) {
+		/* if it can't, revert what the platform hook changed,
+		 * always reporting the base "EINVAL, can't PME#" error
+		 */
+		if (enable)
+			call_platform_enable_wakeup(&dev->dev, 0);
 		return enable ? -EINVAL : 0;
+	}
 
 	pci_read_config_word(dev, pm + PCI_PM_CTRL, &value);
 
@@ -936,7 +960,7 @@
 		value &= ~PCI_PM_CTRL_PME_ENABLE;
 
 	pci_write_config_word(dev, pm + PCI_PM_CTRL, value);
-	
+
 	return 0;
 }
 
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 65d6f23..3411483 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -1303,119 +1303,6 @@
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_EESSC,	quirk_alder_ioapic );
 #endif
 
-enum ide_combined_type { COMBINED = 0, IDE = 1, LIBATA = 2 };
-/* Defaults to combined */
-static enum ide_combined_type combined_mode;
-
-static int __init combined_setup(char *str)
-{
-	if (!strncmp(str, "ide", 3))
-		combined_mode = IDE;
-	else if (!strncmp(str, "libata", 6))
-		combined_mode = LIBATA;
-	else /* "combined" or anything else defaults to old behavior */
-		combined_mode = COMBINED;
-
-	return 1;
-}
-__setup("combined_mode=", combined_setup);
-
-#ifdef CONFIG_SATA_INTEL_COMBINED
-static void __devinit quirk_intel_ide_combined(struct pci_dev *pdev)
-{
-	u8 prog, comb, tmp;
-	int ich = 0;
-
-	/*
-	 * Narrow down to Intel SATA PCI devices.
-	 */
-	switch (pdev->device) {
-	/* PCI ids taken from drivers/scsi/ata_piix.c */
-	case 0x24d1:
-	case 0x24df:
-	case 0x25a3:
-	case 0x25b0:
-		ich = 5;
-		break;
-	case 0x2651:
-	case 0x2652:
-	case 0x2653:
-	case 0x2680:	/* ESB2 */
-		ich = 6;
-		break;
-	case 0x27c0:
-	case 0x27c4:
-		ich = 7;
-		break;
-	case 0x2828:	/* ICH8M */
-		ich = 8;
-		break;
-	default:
-		/* we do not handle this PCI device */
-		return;
-	}
-
-	/*
-	 * Read combined mode register.
-	 */
-	pci_read_config_byte(pdev, 0x90, &tmp);	/* combined mode reg */
-
-	if (ich == 5) {
-		tmp &= 0x6;  /* interesting bits 2:1, PATA primary/secondary */
-		if (tmp == 0x4)		/* bits 10x */
-			comb = (1 << 0);	/* SATA port 0, PATA port 1 */
-		else if (tmp == 0x6)	/* bits 11x */
-			comb = (1 << 2);	/* PATA port 0, SATA port 1 */
-		else
-			return;			/* not in combined mode */
-	} else {
-		WARN_ON((ich != 6) && (ich != 7) && (ich != 8));
-		tmp &= 0x3;  /* interesting bits 1:0 */
-		if (tmp & (1 << 0))
-			comb = (1 << 2);	/* PATA port 0, SATA port 1 */
-		else if (tmp & (1 << 1))
-			comb = (1 << 0);	/* SATA port 0, PATA port 1 */
-		else
-			return;			/* not in combined mode */
-	}
-
-	/*
-	 * Read programming interface register.
-	 * (Tells us if it's legacy or native mode)
-	 */
-	pci_read_config_byte(pdev, PCI_CLASS_PROG, &prog);
-
-	/* if SATA port is in native mode, we're ok. */
-	if (prog & comb)
-		return;
-
-	/* Don't reserve any so the IDE driver can get them (but only if
-	 * combined_mode=ide).
-	 */
-	if (combined_mode == IDE)
-		return;
-
-	/* Grab them both for libata if combined_mode=libata. */
-	if (combined_mode == LIBATA) {
-		request_region(0x1f0, 8, "libata");	/* port 0 */
-		request_region(0x170, 8, "libata");	/* port 1 */
-		return;
-	}
-
-	/* SATA port is in legacy mode.  Reserve port so that
-	 * IDE driver does not attempt to use it.  If request_region
-	 * fails, it will be obvious at boot time, so we don't bother
-	 * checking return values.
-	 */
-	if (comb == (1 << 0))
-		request_region(0x1f0, 8, "libata");	/* port 0 */
-	else
-		request_region(0x170, 8, "libata");	/* port 1 */
-}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,    PCI_ANY_ID,	  quirk_intel_ide_combined );
-#endif /* CONFIG_SATA_INTEL_COMBINED */
-
-
 int pcie_mch_quirk;
 EXPORT_SYMBOL(pcie_mch_quirk);
 
diff --git a/drivers/pnp/card.c b/drivers/pnp/card.c
index 91c047a..dd6384b 100644
--- a/drivers/pnp/card.c
+++ b/drivers/pnp/card.c
@@ -311,7 +311,6 @@
 	return NULL;
 
 found:
-	down_write(&dev->dev.bus->subsys.rwsem);
 	dev->card_link = clink;
 	dev->dev.driver = &drv->link.driver;
 	if (pnp_bus_type.probe(&dev->dev))
@@ -319,14 +318,11 @@
 	if (device_bind_driver(&dev->dev))
 		goto err_out;
 
-	up_write(&dev->dev.bus->subsys.rwsem);
-
 	return dev;
 
 err_out:
 	dev->dev.driver = NULL;
 	dev->card_link = NULL;
-	up_write(&dev->dev.bus->subsys.rwsem);
 	return NULL;
 }
 
@@ -340,11 +336,9 @@
 	struct pnp_card_driver * drv = dev->card_link->driver;
 	if (!drv)
 		return;
-	down_write(&dev->dev.bus->subsys.rwsem);
 	drv->link.remove = &card_remove;
 	device_release_driver(&dev->dev);
 	drv->link.remove = &card_remove_first;
-	up_write(&dev->dev.bus->subsys.rwsem);
 }
 
 /*
diff --git a/drivers/ps3/vuart.c b/drivers/ps3/vuart.c
index 6c12744..7d7cab1 100644
--- a/drivers/ps3/vuart.c
+++ b/drivers/ps3/vuart.c
@@ -82,14 +82,6 @@
 	u64 unused[3];
 } __attribute__ ((aligned (32)));
 
-/* redefine dev_dbg to do a syntax check */
-
-#if !defined(DEBUG)
-#undef dev_dbg
-static inline int __attribute__ ((format (printf, 2, 3))) dev_dbg(
-	const struct device *_dev, const char *fmt, ...) {return 0;}
-#endif
-
 #define dump_ports_bmp(_b) _dump_ports_bmp(_b, __func__, __LINE__)
 static void __attribute__ ((unused)) _dump_ports_bmp(
 	const struct ports_bmp* bmp, const char* func, int line)
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index eb5dc62..9775210 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -398,6 +398,9 @@
 
 	if (device->state == device->target)
 		wake_up(&dasd_init_waitq);
+
+	/* let user-space know that the device status changed */
+	kobject_uevent(&device->cdev->dev.kobj, KOBJ_CHANGE);
 }
 
 /*
@@ -2171,6 +2174,51 @@
 	return ret;
 }
 
+struct dasd_ccw_req * dasd_generic_build_rdc(struct dasd_device *device,
+					     void *rdc_buffer,
+					     int rdc_buffer_size, char *magic)
+{
+	struct dasd_ccw_req *cqr;
+	struct ccw1 *ccw;
+
+	cqr = dasd_smalloc_request(magic, 1 /* RDC */, rdc_buffer_size, device);
+
+	if (IS_ERR(cqr)) {
+		DEV_MESSAGE(KERN_WARNING, device, "%s",
+			    "Could not allocate RDC request");
+		return cqr;
+	}
+
+	ccw = cqr->cpaddr;
+	ccw->cmd_code = CCW_CMD_RDC;
+	ccw->cda = (__u32)(addr_t)rdc_buffer;
+	ccw->count = rdc_buffer_size;
+
+	cqr->device = device;
+	cqr->expires = 10*HZ;
+	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
+	cqr->retries = 2;
+	cqr->buildclk = get_clock();
+	cqr->status = DASD_CQR_FILLED;
+	return cqr;
+}
+
+
+int dasd_generic_read_dev_chars(struct dasd_device *device, char *magic,
+				void **rdc_buffer, int rdc_buffer_size)
+{
+	int ret;
+	struct dasd_ccw_req *cqr;
+
+	cqr = dasd_generic_build_rdc(device, *rdc_buffer, rdc_buffer_size,
+				     magic);
+	if (IS_ERR(cqr))
+		return PTR_ERR(cqr);
+
+	ret = dasd_sleep_on(cqr);
+	dasd_sfree_request(cqr, cqr->device);
+	return ret;
+}
 
 static int __init
 dasd_init(void)
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index ed70852..6a89cef 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -19,6 +19,7 @@
 
 #include <asm/debug.h>
 #include <asm/uaccess.h>
+#include <asm/ipl.h>
 
 /* This is ugly... */
 #define PRINTK_HEADER "dasd_devmap:"
@@ -133,6 +134,8 @@
 __setup ("dasd=", dasd_call_setup);
 #endif	/* #ifndef MODULE */
 
+#define	DASD_IPLDEV	"ipldev"
+
 /*
  * Read a device busid/devno from a string.
  */
@@ -141,6 +144,20 @@
 {
 	int val, old_style;
 
+	/* Interpret ipldev busid */
+	if (strncmp(DASD_IPLDEV, *str, strlen(DASD_IPLDEV)) == 0) {
+		if (ipl_info.type != IPL_TYPE_CCW) {
+			MESSAGE(KERN_ERR, "%s", "ipl device is not a ccw "
+				"device");
+			return -EINVAL;
+		}
+		*id0 = 0;
+		*id1 = ipl_info.data.ccw.dev_id.ssid;
+		*devno = ipl_info.data.ccw.dev_id.devno;
+		*str += strlen(DASD_IPLDEV);
+
+		return 0;
+	}
 	/* check for leading '0x' */
 	old_style = 0;
 	if ((*str)[0] == '0' && (*str)[1] == 'x') {
@@ -829,6 +846,46 @@
 static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL);
 
 static ssize_t
+dasd_device_status_show(struct device *dev, struct device_attribute *attr,
+		     char *buf)
+{
+	struct dasd_device *device;
+	ssize_t len;
+
+	device = dasd_device_from_cdev(to_ccwdev(dev));
+	if (!IS_ERR(device)) {
+		switch (device->state) {
+		case DASD_STATE_NEW:
+			len = snprintf(buf, PAGE_SIZE, "new\n");
+			break;
+		case DASD_STATE_KNOWN:
+			len = snprintf(buf, PAGE_SIZE, "detected\n");
+			break;
+		case DASD_STATE_BASIC:
+			len = snprintf(buf, PAGE_SIZE, "basic\n");
+			break;
+		case DASD_STATE_UNFMT:
+			len = snprintf(buf, PAGE_SIZE, "unformatted\n");
+			break;
+		case DASD_STATE_READY:
+			len = snprintf(buf, PAGE_SIZE, "ready\n");
+			break;
+		case DASD_STATE_ONLINE:
+			len = snprintf(buf, PAGE_SIZE, "online\n");
+			break;
+		default:
+			len = snprintf(buf, PAGE_SIZE, "no stat\n");
+			break;
+		}
+		dasd_put_device(device);
+	} else
+		len = snprintf(buf, PAGE_SIZE, "unknown\n");
+	return len;
+}
+
+static DEVICE_ATTR(status, 0444, dasd_device_status_show, NULL);
+
+static ssize_t
 dasd_alias_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
 	struct dasd_devmap *devmap;
@@ -939,6 +996,7 @@
 static struct attribute * dasd_attrs[] = {
 	&dev_attr_readonly.attr,
 	&dev_attr_discipline.attr,
+	&dev_attr_status.attr,
 	&dev_attr_alias.attr,
 	&dev_attr_vendor.attr,
 	&dev_attr_uid.attr,
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index cecab22..c9583fb 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -450,6 +450,81 @@
 	return 0;
 }
 
+struct dasd_ccw_req * dasd_eckd_build_rcd_lpm(struct dasd_device *device,
+					      void *rcd_buffer,
+					      struct ciw *ciw, __u8 lpm)
+{
+	struct dasd_ccw_req *cqr;
+	struct ccw1 *ccw;
+
+	cqr = dasd_smalloc_request("ECKD", 1 /* RCD */, ciw->count, device);
+
+	if (IS_ERR(cqr)) {
+		DEV_MESSAGE(KERN_WARNING, device, "%s",
+			    "Could not allocate RCD request");
+		return cqr;
+	}
+
+	ccw = cqr->cpaddr;
+	ccw->cmd_code = ciw->cmd;
+	ccw->cda = (__u32)(addr_t)rcd_buffer;
+	ccw->count = ciw->count;
+
+	cqr->device = device;
+	cqr->expires = 10*HZ;
+	cqr->lpm = lpm;
+	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
+	cqr->retries = 2;
+	cqr->buildclk = get_clock();
+	cqr->status = DASD_CQR_FILLED;
+	return cqr;
+}
+
+static int dasd_eckd_read_conf_lpm(struct dasd_device *device,
+				   void **rcd_buffer,
+				   int *rcd_buffer_size, __u8 lpm)
+{
+	struct ciw *ciw;
+	char *rcd_buf = NULL;
+	int ret;
+	struct dasd_ccw_req *cqr;
+
+	/*
+	 * scan for RCD command in extended SenseID data
+	 */
+	ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD);
+	if (!ciw || ciw->cmd == 0) {
+		ret = -EOPNOTSUPP;
+		goto out_error;
+	}
+	rcd_buf = kzalloc(ciw->count, GFP_KERNEL | GFP_DMA);
+	if (!rcd_buf) {
+		ret = -ENOMEM;
+		goto out_error;
+	}
+	cqr = dasd_eckd_build_rcd_lpm(device, rcd_buf, ciw, lpm);
+	if (IS_ERR(cqr)) {
+		ret =  PTR_ERR(cqr);
+		goto out_error;
+	}
+	ret = dasd_sleep_on(cqr);
+	/*
+	 * on success we update the user input parms
+	 */
+	dasd_sfree_request(cqr, cqr->device);
+	if (ret)
+		goto out_error;
+
+	*rcd_buffer_size = ciw->count;
+	*rcd_buffer = rcd_buf;
+	return 0;
+out_error:
+	kfree(rcd_buf);
+	*rcd_buffer = NULL;
+	*rcd_buffer_size = 0;
+	return ret;
+}
+
 static int
 dasd_eckd_read_conf(struct dasd_device *device)
 {
@@ -469,8 +544,8 @@
 	/* get configuration data per operational path */
 	for (lpm = 0x80; lpm; lpm>>= 1) {
 		if (lpm & path_data->opm){
-			rc = read_conf_data_lpm(device->cdev, &conf_data,
-						&conf_len, lpm);
+			rc = dasd_eckd_read_conf_lpm(device, &conf_data,
+						     &conf_len, lpm);
 			if (rc && rc != -EOPNOTSUPP) {	/* -EOPNOTSUPP is ok */
 				MESSAGE(KERN_WARNING,
 					"Read configuration data returned "
@@ -639,7 +714,7 @@
 	/* Read Device Characteristics */
 	rdc_data = (void *) &(private->rdc_data);
 	memset(rdc_data, 0, sizeof(rdc_data));
-	rc = read_dev_chars(device->cdev, &rdc_data, 64);
+	rc = dasd_generic_read_dev_chars(device, "ECKD", &rdc_data, 64);
 	if (rc)
 		DEV_MESSAGE(KERN_WARNING, device,
 			    "Read device characteristics returned "
diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c
index be0909e..da16ead 100644
--- a/drivers/s390/block/dasd_fba.c
+++ b/drivers/s390/block/dasd_fba.c
@@ -135,7 +135,7 @@
 	}
 	/* Read Device Characteristics */
 	rdc_data = (void *) &(private->rdc_data);
-	rc = read_dev_chars(device->cdev, &rdc_data, 32);
+	rc = dasd_generic_read_dev_chars(device, "FBA ", &rdc_data, 32);
 	if (rc) {
 		DEV_MESSAGE(KERN_WARNING, device,
 			    "Read device characteristics returned error %d",
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index a2cc69e..241294c 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -509,6 +509,8 @@
 int dasd_generic_set_offline (struct ccw_device *cdev);
 int dasd_generic_notify(struct ccw_device *, int);
 
+int dasd_generic_read_dev_chars(struct dasd_device *, char *, void **, int);
+
 /* externals in dasd_devmap.c */
 extern int dasd_max_devindex;
 extern int dasd_probeonly;
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index 293e667..c210784 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -3,7 +3,7 @@
 #
 
 obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \
-	 sclp_info.o
+	 sclp_info.o sclp_config.o sclp_chp.o
 
 obj-$(CONFIG_TN3270) += raw3270.o
 obj-$(CONFIG_TN3270_CONSOLE) += con3270.o
@@ -29,3 +29,6 @@
 obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o
 obj-$(CONFIG_MONREADER) += monreader.o
 obj-$(CONFIG_MONWRITER) += monwriter.o
+
+zcore_mod-objs := sclp_sdias.o zcore.o
+obj-$(CONFIG_ZFCPDUMP) += zcore_mod.o
diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c
index 9a328f1..6000bde 100644
--- a/drivers/s390/char/con3215.c
+++ b/drivers/s390/char/con3215.c
@@ -813,12 +813,6 @@
 	spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
 }
 
-static int __init 
-con3215_consetup(struct console *co, char *options)
-{
-	return 0;
-}
-
 /*
  *  The console structure for the 3215 console
  */
@@ -827,7 +821,6 @@
 	.write	 = con3215_write,
 	.device	 = con3215_device,
 	.unblank = con3215_unblank,
-	.setup	 = con3215_consetup,
 	.flags	 = CON_PRINTBUFFER,
 };
 
diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c
index 8e7f2d7..fd34791 100644
--- a/drivers/s390/char/con3270.c
+++ b/drivers/s390/char/con3270.c
@@ -555,12 +555,6 @@
 	spin_unlock_irqrestore(&cp->view.lock, flags);
 }
 
-static int __init 
-con3270_consetup(struct console *co, char *options)
-{
-	return 0;
-}
-
 /*
  *  The console structure for the 3270 console
  */
@@ -569,7 +563,6 @@
 	.write	 = con3270_write,
 	.device	 = con3270_device,
 	.unblank = con3270_unblank,
-	.setup	 = con3270_consetup,
 	.flags	 = CON_PRINTBUFFER,
 };
 
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index f171de3..fa62e69 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -15,6 +15,7 @@
 #include <linux/timer.h>
 #include <linux/reboot.h>
 #include <linux/jiffies.h>
+#include <linux/init.h>
 #include <asm/types.h>
 #include <asm/s390_ext.h>
 
@@ -510,7 +511,7 @@
 }
 
 static struct sclp_register sclp_state_change_event = {
-	.receive_mask = EvTyp_StateChange_Mask,
+	.receive_mask = EVTYP_STATECHANGE_MASK,
 	.receiver_fn = sclp_state_change_cb
 };
 
@@ -930,3 +931,10 @@
 	sclp_init_mask(1);
 	return 0;
 }
+
+static __init int sclp_initcall(void)
+{
+	return sclp_init();
+}
+
+arch_initcall(sclp_initcall);
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index 7d29ab4..87ac4a3 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -19,33 +19,37 @@
 #define MAX_KMEM_PAGES (sizeof(unsigned long) << 3)
 #define MAX_CONSOLE_PAGES	4
 
-#define EvTyp_OpCmd		0x01
-#define EvTyp_Msg		0x02
-#define EvTyp_StateChange	0x08
-#define EvTyp_PMsgCmd		0x09
-#define EvTyp_CntlProgOpCmd	0x20
-#define EvTyp_CntlProgIdent	0x0B
-#define EvTyp_SigQuiesce	0x1D
-#define EvTyp_VT220Msg		0x1A
+#define EVTYP_OPCMD		0x01
+#define EVTYP_MSG		0x02
+#define EVTYP_STATECHANGE	0x08
+#define EVTYP_PMSGCMD		0x09
+#define EVTYP_CNTLPROGOPCMD	0x20
+#define EVTYP_CNTLPROGIDENT	0x0B
+#define EVTYP_SIGQUIESCE	0x1D
+#define EVTYP_VT220MSG		0x1A
+#define EVTYP_CONFMGMDATA	0x04
+#define EVTYP_SDIAS		0x1C
 
-#define EvTyp_OpCmd_Mask	0x80000000
-#define EvTyp_Msg_Mask		0x40000000
-#define EvTyp_StateChange_Mask	0x01000000
-#define EvTyp_PMsgCmd_Mask	0x00800000
-#define EvTyp_CtlProgOpCmd_Mask	0x00000001
-#define EvTyp_CtlProgIdent_Mask	0x00200000
-#define EvTyp_SigQuiesce_Mask	0x00000008
-#define EvTyp_VT220Msg_Mask	0x00000040
+#define EVTYP_OPCMD_MASK	0x80000000
+#define EVTYP_MSG_MASK		0x40000000
+#define EVTYP_STATECHANGE_MASK	0x01000000
+#define EVTYP_PMSGCMD_MASK	0x00800000
+#define EVTYP_CTLPROGOPCMD_MASK	0x00000001
+#define EVTYP_CTLPROGIDENT_MASK	0x00200000
+#define EVTYP_SIGQUIESCE_MASK	0x00000008
+#define EVTYP_VT220MSG_MASK	0x00000040
+#define EVTYP_CONFMGMDATA_MASK	0x10000000
+#define EVTYP_SDIAS_MASK	0x00000010
 
-#define GnrlMsgFlgs_DOM		0x8000
-#define GnrlMsgFlgs_SndAlrm	0x4000
-#define GnrlMsgFlgs_HoldMsg	0x2000
+#define GNRLMSGFLGS_DOM		0x8000
+#define GNRLMSGFLGS_SNDALRM	0x4000
+#define GNRLMSGFLGS_HOLDMSG	0x2000
 
-#define LnTpFlgs_CntlText	0x8000
-#define LnTpFlgs_LabelText	0x4000
-#define LnTpFlgs_DataText	0x2000
-#define LnTpFlgs_EndText	0x1000
-#define LnTpFlgs_PromptText	0x0800
+#define LNTPFLGS_CNTLTEXT	0x8000
+#define LNTPFLGS_LABELTEXT	0x4000
+#define LNTPFLGS_DATATEXT	0x2000
+#define LNTPFLGS_ENDTEXT	0x1000
+#define LNTPFLGS_PROMPTTEXT	0x0800
 
 typedef unsigned int sclp_cmdw_t;
 
@@ -56,15 +60,15 @@
 #define SCLP_CMDW_READ_SCP_INFO_FORCED	0x00120001
 
 #define GDS_ID_MDSMU		0x1310
-#define GDS_ID_MDSRouteInfo	0x1311
-#define GDS_ID_AgUnWrkCorr	0x1549
-#define GDS_ID_SNACondReport	0x1532
+#define GDS_ID_MDSROUTEINFO	0x1311
+#define GDS_ID_AGUNWRKCORR	0x1549
+#define GDS_ID_SNACONDREPORT	0x1532
 #define GDS_ID_CPMSU		0x1212
-#define GDS_ID_RoutTargInstr	0x154D
-#define GDS_ID_OpReq		0x8070
-#define GDS_ID_TextCmd		0x1320
+#define GDS_ID_ROUTTARGINSTR	0x154D
+#define GDS_ID_OPREQ		0x8070
+#define GDS_ID_TEXTCMD		0x1320
 
-#define GDS_KEY_SelfDefTextMsg	0x31
+#define GDS_KEY_SELFDEFTEXTMSG	0x31
 
 typedef u32 sccb_mask_t;	/* ATTENTION: assumes 32bit mask !!! */
 
diff --git a/drivers/s390/char/sclp_chp.c b/drivers/s390/char/sclp_chp.c
new file mode 100644
index 0000000..a66b914
--- /dev/null
+++ b/drivers/s390/char/sclp_chp.c
@@ -0,0 +1,196 @@
+/*
+ *  drivers/s390/char/sclp_chp.c
+ *
+ *    Copyright IBM Corp. 2007
+ *    Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/types.h>
+#include <linux/gfp.h>
+#include <linux/errno.h>
+#include <linux/completion.h>
+#include <asm/sclp.h>
+#include <asm/chpid.h>
+
+#include "sclp.h"
+
+#define TAG	"sclp_chp: "
+
+#define SCLP_CMDW_CONFIGURE_CHANNEL_PATH	0x000f0001
+#define SCLP_CMDW_DECONFIGURE_CHANNEL_PATH	0x000e0001
+#define SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION	0x00030001
+
+static inline sclp_cmdw_t get_configure_cmdw(struct chp_id chpid)
+{
+	return SCLP_CMDW_CONFIGURE_CHANNEL_PATH | chpid.id << 8;
+}
+
+static inline sclp_cmdw_t get_deconfigure_cmdw(struct chp_id chpid)
+{
+	return SCLP_CMDW_DECONFIGURE_CHANNEL_PATH | chpid.id << 8;
+}
+
+static void chp_callback(struct sclp_req *req, void *data)
+{
+	struct completion *completion = data;
+
+	complete(completion);
+}
+
+struct chp_cfg_sccb {
+	struct sccb_header header;
+	u8 ccm;
+	u8 reserved[6];
+	u8 cssid;
+} __attribute__((packed));
+
+struct chp_cfg_data {
+	struct chp_cfg_sccb sccb;
+	struct sclp_req req;
+	struct completion completion;
+} __attribute__((packed));
+
+static int do_configure(sclp_cmdw_t cmd)
+{
+	struct chp_cfg_data *data;
+	int rc;
+
+	/* Prepare sccb. */
+	data = (struct chp_cfg_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+	if (!data)
+		return -ENOMEM;
+	data->sccb.header.length = sizeof(struct chp_cfg_sccb);
+	data->req.command = cmd;
+	data->req.sccb = &(data->sccb);
+	data->req.status = SCLP_REQ_FILLED;
+	data->req.callback = chp_callback;
+	data->req.callback_data = &(data->completion);
+	init_completion(&data->completion);
+
+	/* Perform sclp request. */
+	rc = sclp_add_request(&(data->req));
+	if (rc)
+		goto out;
+	wait_for_completion(&data->completion);
+
+	/* Check response .*/
+	if (data->req.status != SCLP_REQ_DONE) {
+		printk(KERN_WARNING TAG "configure channel-path request failed "
+		       "(status=0x%02x)\n", data->req.status);
+		rc = -EIO;
+		goto out;
+	}
+	switch (data->sccb.header.response_code) {
+	case 0x0020:
+	case 0x0120:
+	case 0x0440:
+	case 0x0450:
+		break;
+	default:
+		printk(KERN_WARNING TAG "configure channel-path failed "
+		       "(cmd=0x%08x, response=0x%04x)\n", cmd,
+		       data->sccb.header.response_code);
+		rc = -EIO;
+		break;
+	}
+out:
+	free_page((unsigned long) data);
+
+	return rc;
+}
+
+/**
+ * sclp_chp_configure - perform configure channel-path sclp command
+ * @chpid: channel-path ID
+ *
+ * Perform configure channel-path command sclp command for specified chpid.
+ * Return 0 after command successfully finished, non-zero otherwise.
+ */
+int sclp_chp_configure(struct chp_id chpid)
+{
+	return do_configure(get_configure_cmdw(chpid));
+}
+
+/**
+ * sclp_chp_deconfigure - perform deconfigure channel-path sclp command
+ * @chpid: channel-path ID
+ *
+ * Perform deconfigure channel-path command sclp command for specified chpid
+ * and wait for completion. On success return 0. Return non-zero otherwise.
+ */
+int sclp_chp_deconfigure(struct chp_id chpid)
+{
+	return do_configure(get_deconfigure_cmdw(chpid));
+}
+
+struct chp_info_sccb {
+	struct sccb_header header;
+	u8 recognized[SCLP_CHP_INFO_MASK_SIZE];
+	u8 standby[SCLP_CHP_INFO_MASK_SIZE];
+	u8 configured[SCLP_CHP_INFO_MASK_SIZE];
+	u8 ccm;
+	u8 reserved[6];
+	u8 cssid;
+} __attribute__((packed));
+
+struct chp_info_data {
+	struct chp_info_sccb sccb;
+	struct sclp_req req;
+	struct completion completion;
+} __attribute__((packed));
+
+/**
+ * sclp_chp_read_info - perform read channel-path information sclp command
+ * @info: resulting channel-path information data
+ *
+ * Perform read channel-path information sclp command and wait for completion.
+ * On success, store channel-path information in @info and return 0. Return
+ * non-zero otherwise.
+ */
+int sclp_chp_read_info(struct sclp_chp_info *info)
+{
+	struct chp_info_data *data;
+	int rc;
+
+	/* Prepare sccb. */
+	data = (struct chp_info_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+	if (!data)
+		return -ENOMEM;
+	data->sccb.header.length = sizeof(struct chp_info_sccb);
+	data->req.command = SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION;
+	data->req.sccb = &(data->sccb);
+	data->req.status = SCLP_REQ_FILLED;
+	data->req.callback = chp_callback;
+	data->req.callback_data = &(data->completion);
+	init_completion(&data->completion);
+
+	/* Perform sclp request. */
+	rc = sclp_add_request(&(data->req));
+	if (rc)
+		goto out;
+	wait_for_completion(&data->completion);
+
+	/* Check response .*/
+	if (data->req.status != SCLP_REQ_DONE) {
+		printk(KERN_WARNING TAG "read channel-path info request failed "
+		       "(status=0x%02x)\n", data->req.status);
+		rc = -EIO;
+		goto out;
+	}
+	if (data->sccb.header.response_code != 0x0010) {
+		printk(KERN_WARNING TAG "read channel-path info failed "
+		       "(response=0x%04x)\n", data->sccb.header.response_code);
+		rc = -EIO;
+		goto out;
+	}
+	memcpy(info->recognized, data->sccb.recognized,
+	       SCLP_CHP_INFO_MASK_SIZE);
+	memcpy(info->standby, data->sccb.standby,
+	       SCLP_CHP_INFO_MASK_SIZE);
+	memcpy(info->configured, data->sccb.configured,
+	       SCLP_CHP_INFO_MASK_SIZE);
+out:
+	free_page((unsigned long) data);
+
+	return rc;
+}
diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c
new file mode 100644
index 0000000..5322e5e
--- /dev/null
+++ b/drivers/s390/char/sclp_config.c
@@ -0,0 +1,75 @@
+/*
+ *  drivers/s390/char/sclp_config.c
+ *
+ *    Copyright IBM Corp. 2007
+ *    Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
+ */
+
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/cpu.h>
+#include <linux/sysdev.h>
+#include <linux/workqueue.h>
+#include "sclp.h"
+
+#define TAG	"sclp_config: "
+
+struct conf_mgm_data {
+	u8 reserved;
+	u8 ev_qualifier;
+} __attribute__((packed));
+
+#define EV_QUAL_CAP_CHANGE	3
+
+static struct work_struct sclp_cpu_capability_work;
+
+static void sclp_cpu_capability_notify(struct work_struct *work)
+{
+	int cpu;
+	struct sys_device *sysdev;
+
+	printk(KERN_WARNING TAG "cpu capability changed.\n");
+	lock_cpu_hotplug();
+	for_each_online_cpu(cpu) {
+		sysdev = get_cpu_sysdev(cpu);
+		kobject_uevent(&sysdev->kobj, KOBJ_CHANGE);
+	}
+	unlock_cpu_hotplug();
+}
+
+static void sclp_conf_receiver_fn(struct evbuf_header *evbuf)
+{
+	struct conf_mgm_data *cdata;
+
+	cdata = (struct conf_mgm_data *)(evbuf + 1);
+	if (cdata->ev_qualifier == EV_QUAL_CAP_CHANGE)
+		schedule_work(&sclp_cpu_capability_work);
+}
+
+static struct sclp_register sclp_conf_register =
+{
+	.receive_mask = EVTYP_CONFMGMDATA_MASK,
+	.receiver_fn  = sclp_conf_receiver_fn,
+};
+
+static int __init sclp_conf_init(void)
+{
+	int rc;
+
+	INIT_WORK(&sclp_cpu_capability_work, sclp_cpu_capability_notify);
+
+	rc = sclp_register(&sclp_conf_register);
+	if (rc) {
+		printk(KERN_ERR TAG "failed to register (%d).\n", rc);
+		return rc;
+	}
+
+	if (!(sclp_conf_register.sclp_receive_mask & EVTYP_CONFMGMDATA_MASK)) {
+		printk(KERN_WARNING TAG "no configuration management.\n");
+		sclp_unregister(&sclp_conf_register);
+		rc = -ENOSYS;
+	}
+	return rc;
+}
+
+__initcall(sclp_conf_init);
diff --git a/drivers/s390/char/sclp_cpi.c b/drivers/s390/char/sclp_cpi.c
index 65aa2c8..29fe2a5 100644
--- a/drivers/s390/char/sclp_cpi.c
+++ b/drivers/s390/char/sclp_cpi.c
@@ -46,7 +46,7 @@
 /* Event type structure for write message and write priority message */
 static struct sclp_register sclp_cpi_event =
 {
-	.send_mask = EvTyp_CtlProgIdent_Mask
+	.send_mask = EVTYP_CTLPROGIDENT_MASK
 };
 
 MODULE_LICENSE("GPL");
@@ -201,7 +201,7 @@
 		       "console.\n");
 		return -EINVAL;
 	}
-	if (!(sclp_cpi_event.sclp_send_mask & EvTyp_CtlProgIdent_Mask)) {
+	if (!(sclp_cpi_event.sclp_send_mask & EVTYP_CTLPROGIDENT_MASK)) {
 		printk(KERN_WARNING "cpi: no control program identification "
 		       "support\n");
 		sclp_unregister(&sclp_cpi_event);
diff --git a/drivers/s390/char/sclp_quiesce.c b/drivers/s390/char/sclp_quiesce.c
index baa8fe6..45ff25e 100644
--- a/drivers/s390/char/sclp_quiesce.c
+++ b/drivers/s390/char/sclp_quiesce.c
@@ -43,7 +43,7 @@
 }
 
 static struct sclp_register sclp_quiesce_event = {
-	.receive_mask = EvTyp_SigQuiesce_Mask,
+	.receive_mask = EVTYP_SIGQUIESCE_MASK,
 	.receiver_fn = sclp_quiesce_handler
 };
 
diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c
index 2486783..bbd5b8b 100644
--- a/drivers/s390/char/sclp_rw.c
+++ b/drivers/s390/char/sclp_rw.c
@@ -30,7 +30,7 @@
 
 /* Event type structure for write message and write priority message */
 static struct sclp_register sclp_rw_event = {
-	.send_mask = EvTyp_Msg_Mask | EvTyp_PMsgCmd_Mask
+	.send_mask = EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK
 };
 
 /*
@@ -64,7 +64,7 @@
 	memset(sccb, 0, sizeof(struct write_sccb));
 	sccb->header.length = sizeof(struct write_sccb);
 	sccb->msg_buf.header.length = sizeof(struct msg_buf);
-	sccb->msg_buf.header.type = EvTyp_Msg;
+	sccb->msg_buf.header.type = EVTYP_MSG;
 	sccb->msg_buf.mdb.header.length = sizeof(struct mdb);
 	sccb->msg_buf.mdb.header.type = 1;
 	sccb->msg_buf.mdb.header.tag = 0xD4C4C240;	/* ebcdic "MDB " */
@@ -114,7 +114,7 @@
 	memset(mto, 0, sizeof(struct mto));
 	mto->length = sizeof(struct mto);
 	mto->type = 4;	/* message text object */
-	mto->line_type_flags = LnTpFlgs_EndText; /* end text */
+	mto->line_type_flags = LNTPFLGS_ENDTEXT; /* end text */
 
 	/* set pointer to first byte after struct mto. */
 	buffer->current_line = (char *) (mto + 1);
@@ -215,7 +215,7 @@
 		case '\a':	/* bell, one for several times	*/
 			/* set SCLP sound alarm bit in General Object */
 			buffer->sccb->msg_buf.mdb.go.general_msg_flags |=
-				GnrlMsgFlgs_SndAlrm;
+				GNRLMSGFLGS_SNDALRM;
 			break;
 		case '\t':	/* horizontal tabulator	 */
 			/* check if new mto needs to be created */
@@ -452,12 +452,12 @@
 		return -EIO;
 
 	sccb = buffer->sccb;
-	if (sclp_rw_event.sclp_send_mask & EvTyp_Msg_Mask)
+	if (sclp_rw_event.sclp_send_mask & EVTYP_MSG_MASK)
 		/* Use normal write message */
-		sccb->msg_buf.header.type = EvTyp_Msg;
-	else if (sclp_rw_event.sclp_send_mask & EvTyp_PMsgCmd_Mask)
+		sccb->msg_buf.header.type = EVTYP_MSG;
+	else if (sclp_rw_event.sclp_send_mask & EVTYP_PMSGCMD_MASK)
 		/* Use write priority message */
-		sccb->msg_buf.header.type = EvTyp_PMsgCmd;
+		sccb->msg_buf.header.type = EVTYP_PMSGCMD;
 	else
 		return -ENOSYS;
 	buffer->request.command = SCLP_CMDW_WRITE_EVENT_DATA;
diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c
new file mode 100644
index 0000000..52283da
--- /dev/null
+++ b/drivers/s390/char/sclp_sdias.c
@@ -0,0 +1,255 @@
+/*
+ * Sclp "store data in absolut storage"
+ *
+ * Copyright IBM Corp. 2003,2007
+ * Author(s): Michael Holzheu
+ */
+
+#include <linux/sched.h>
+#include <asm/sclp.h>
+#include <asm/debug.h>
+#include <asm/ipl.h>
+#include "sclp.h"
+#include "sclp_rw.h"
+
+#define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x)
+#define ERROR_MSG(x...) printk ( KERN_ALERT "SDIAS: " x )
+
+#define SDIAS_RETRIES 300
+#define SDIAS_SLEEP_TICKS 50
+
+#define EQ_STORE_DATA	0x0
+#define EQ_SIZE		0x1
+#define DI_FCP_DUMP	0x0
+#define ASA_SIZE_32	0x0
+#define ASA_SIZE_64	0x1
+#define EVSTATE_ALL_STORED	0x0
+#define EVSTATE_NO_DATA		0x3
+#define EVSTATE_PART_STORED	0x10
+
+static struct debug_info *sdias_dbf;
+
+static struct sclp_register sclp_sdias_register = {
+	.send_mask = EVTYP_SDIAS_MASK,
+};
+
+struct sdias_evbuf {
+	struct	evbuf_header hdr;
+	u8	event_qual;
+	u8	data_id;
+	u64	reserved2;
+	u32	event_id;
+	u16	reserved3;
+	u8	asa_size;
+	u8	event_status;
+	u32	reserved4;
+	u32	blk_cnt;
+	u64	asa;
+	u32	reserved5;
+	u32	fbn;
+	u32	reserved6;
+	u32	lbn;
+	u16	reserved7;
+	u16	dbs;
+} __attribute__((packed));
+
+struct sdias_sccb {
+	struct sccb_header  hdr;
+	struct sdias_evbuf  evbuf;
+} __attribute__((packed));
+
+static struct sdias_sccb sccb __attribute__((aligned(4096)));
+
+static int sclp_req_done;
+static wait_queue_head_t sdias_wq;
+static DEFINE_MUTEX(sdias_mutex);
+
+static void sdias_callback(struct sclp_req *request, void *data)
+{
+	struct sdias_sccb *sccb;
+
+	sccb = (struct sdias_sccb *) request->sccb;
+	sclp_req_done = 1;
+	wake_up(&sdias_wq); /* Inform caller, that request is complete */
+	TRACE("callback done\n");
+}
+
+static int sdias_sclp_send(struct sclp_req *req)
+{
+	int retries;
+	int rc;
+
+	for (retries = SDIAS_RETRIES; retries; retries--) {
+		sclp_req_done = 0;
+		TRACE("add request\n");
+		rc = sclp_add_request(req);
+		if (rc) {
+			/* not initiated, wait some time and retry */
+			set_current_state(TASK_INTERRUPTIBLE);
+			TRACE("add request failed: rc = %i\n",rc);
+			schedule_timeout(SDIAS_SLEEP_TICKS);
+			continue;
+		}
+		/* initiated, wait for completion of service call */
+		wait_event(sdias_wq, (sclp_req_done == 1));
+		if (req->status == SCLP_REQ_FAILED) {
+			TRACE("sclp request failed\n");
+			rc = -EIO;
+			continue;
+		}
+		TRACE("request done\n");
+		break;
+	}
+	return rc;
+}
+
+/*
+ * Get number of blocks (4K) available in the HSA
+ */
+int sclp_sdias_blk_count(void)
+{
+	struct sclp_req request;
+	int rc;
+
+	mutex_lock(&sdias_mutex);
+
+	memset(&sccb, 0, sizeof(sccb));
+	memset(&request, 0, sizeof(request));
+
+	sccb.hdr.length = sizeof(sccb);
+	sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf);
+	sccb.evbuf.hdr.type = EVTYP_SDIAS;
+	sccb.evbuf.event_qual = EQ_SIZE;
+	sccb.evbuf.data_id = DI_FCP_DUMP;
+	sccb.evbuf.event_id = 4712;
+	sccb.evbuf.dbs = 1;
+
+	request.sccb = &sccb;
+	request.command = SCLP_CMDW_WRITE_EVENT_DATA;
+	request.status = SCLP_REQ_FILLED;
+	request.callback = sdias_callback;
+
+	rc = sdias_sclp_send(&request);
+	if (rc) {
+		ERROR_MSG("sclp_send failed for get_nr_blocks\n");
+		goto out;
+	}
+	if (sccb.hdr.response_code != 0x0020) {
+		TRACE("send failed: %x\n", sccb.hdr.response_code);
+		rc = -EIO;
+		goto out;
+	}
+
+	switch (sccb.evbuf.event_status) {
+		case 0:
+			rc = sccb.evbuf.blk_cnt;
+			break;
+		default:
+			ERROR_MSG("SCLP error: %x\n", sccb.evbuf.event_status);
+			rc = -EIO;
+			goto out;
+	}
+	TRACE("%i blocks\n", rc);
+out:
+	mutex_unlock(&sdias_mutex);
+	return rc;
+}
+
+/*
+ * Copy from HSA to absolute storage (not reentrant):
+ *
+ * @dest     : Address of buffer where data should be copied
+ * @start_blk: Start Block (beginning with 1)
+ * @nr_blks  : Number of 4K blocks to copy
+ *
+ * Return Value: 0 : Requested 'number' of blocks of data copied
+ *		 <0: ERROR - negative event status
+ */
+int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)
+{
+	struct sclp_req request;
+	int rc;
+
+	mutex_lock(&sdias_mutex);
+
+	memset(&sccb, 0, sizeof(sccb));
+	memset(&request, 0, sizeof(request));
+
+	sccb.hdr.length = sizeof(sccb);
+	sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf);
+	sccb.evbuf.hdr.type = EVTYP_SDIAS;
+	sccb.evbuf.hdr.flags = 0;
+	sccb.evbuf.event_qual = EQ_STORE_DATA;
+	sccb.evbuf.data_id = DI_FCP_DUMP;
+	sccb.evbuf.event_id = 4712;
+#ifdef __s390x__
+	sccb.evbuf.asa_size = ASA_SIZE_64;
+#else
+	sccb.evbuf.asa_size = ASA_SIZE_32;
+#endif
+	sccb.evbuf.event_status = 0;
+	sccb.evbuf.blk_cnt = nr_blks;
+	sccb.evbuf.asa = (unsigned long)dest;
+	sccb.evbuf.fbn = start_blk;
+	sccb.evbuf.lbn = 0;
+	sccb.evbuf.dbs = 1;
+
+	request.sccb	 = &sccb;
+	request.command  = SCLP_CMDW_WRITE_EVENT_DATA;
+	request.status	 = SCLP_REQ_FILLED;
+	request.callback = sdias_callback;
+
+	rc = sdias_sclp_send(&request);
+	if (rc) {
+		ERROR_MSG("sclp_send failed: %x\n", rc);
+		goto out;
+	}
+	if (sccb.hdr.response_code != 0x0020) {
+		TRACE("copy failed: %x\n", sccb.hdr.response_code);
+		rc = -EIO;
+		goto out;
+	}
+
+	switch (sccb.evbuf.event_status) {
+		case EVSTATE_ALL_STORED:
+			TRACE("all stored\n");
+		case EVSTATE_PART_STORED:
+			TRACE("part stored: %i\n", sccb.evbuf.blk_cnt);
+			break;
+		case EVSTATE_NO_DATA:
+			TRACE("no data\n");
+		default:
+			ERROR_MSG("Error from SCLP while copying hsa. "
+				  "Event status = %x\n",
+				sccb.evbuf.event_status);
+			rc = -EIO;
+	}
+out:
+	mutex_unlock(&sdias_mutex);
+	return rc;
+}
+
+int __init sdias_init(void)
+{
+	int rc;
+
+	if (ipl_info.type != IPL_TYPE_FCP_DUMP)
+		return 0;
+	sdias_dbf = debug_register("dump_sdias", 4, 1, 4 * sizeof(long));
+	debug_register_view(sdias_dbf, &debug_sprintf_view);
+	debug_set_level(sdias_dbf, 6);
+	rc = sclp_register(&sclp_sdias_register);
+	if (rc) {
+		ERROR_MSG("sclp register failed\n");
+		return rc;
+	}
+	init_waitqueue_head(&sdias_wq);
+	TRACE("init done\n");
+	return 0;
+}
+
+void __exit sdias_exit(void)
+{
+	debug_unregister(sdias_dbf);
+	sclp_unregister(&sclp_sdias_register);
+}
diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c
index 076816b..e3b3d39 100644
--- a/drivers/s390/char/sclp_tty.c
+++ b/drivers/s390/char/sclp_tty.c
@@ -648,7 +648,7 @@
 	subvec = start;
 	while (subvec < end) {
 		subvec = find_gds_subvector(subvec, end,
-					    GDS_KEY_SelfDefTextMsg);
+					    GDS_KEY_SELFDEFTEXTMSG);
 		if (!subvec)
 			break;
 		sclp_eval_selfdeftextmsg((struct gds_subvector *)(subvec + 1),
@@ -664,7 +664,7 @@
 
 	vec = start;
 	while (vec < end) {
-		vec = find_gds_vector(vec, end, GDS_ID_TextCmd);
+		vec = find_gds_vector(vec, end, GDS_ID_TEXTCMD);
 		if (!vec)
 			break;
 		sclp_eval_textcmd((struct gds_subvector *)(vec + 1),
@@ -703,7 +703,7 @@
 
 static struct sclp_register sclp_input_event =
 {
-	.receive_mask = EvTyp_OpCmd_Mask | EvTyp_PMsgCmd_Mask,
+	.receive_mask = EVTYP_OPCMD_MASK | EVTYP_PMSGCMD_MASK,
 	.state_change_fn = sclp_tty_state_change,
 	.receiver_fn = sclp_tty_receiver
 };
diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c
index f77dc33..7263347 100644
--- a/drivers/s390/char/sclp_vt220.c
+++ b/drivers/s390/char/sclp_vt220.c
@@ -99,8 +99,8 @@
 
 /* Registration structure for our interest in SCLP event buffers */
 static struct sclp_register sclp_vt220_register = {
-	.send_mask		= EvTyp_VT220Msg_Mask,
-	.receive_mask		= EvTyp_VT220Msg_Mask,
+	.send_mask		= EVTYP_VT220MSG_MASK,
+	.receive_mask		= EVTYP_VT220MSG_MASK,
 	.state_change_fn	= NULL,
 	.receiver_fn		= sclp_vt220_receiver_fn
 };
@@ -202,7 +202,7 @@
 static int
 __sclp_vt220_emit(struct sclp_vt220_request *request)
 {
-	if (!(sclp_vt220_register.sclp_send_mask & EvTyp_VT220Msg_Mask)) {
+	if (!(sclp_vt220_register.sclp_send_mask & EVTYP_VT220MSG_MASK)) {
 		request->sclp_req.status = SCLP_REQ_FAILED;
 		return -EIO;
 	}
@@ -284,7 +284,7 @@
 	sccb->header.length = sizeof(struct sclp_vt220_sccb);
 	sccb->header.function_code = SCLP_NORMAL_WRITE;
 	sccb->header.response_code = 0x0000;
-	sccb->evbuf.type = EvTyp_VT220Msg;
+	sccb->evbuf.type = EVTYP_VT220MSG;
 	sccb->evbuf.length = sizeof(struct evbuf_header);
 
 	return request;
diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h
index bb4ff53..3b52f5c 100644
--- a/drivers/s390/char/tape.h
+++ b/drivers/s390/char/tape.h
@@ -103,6 +103,7 @@
 	TO_CRYPT_OFF,	/* Disable encrpytion */
 	TO_KEKL_SET,	/* Set KEK label */
 	TO_KEKL_QUERY,	/* Query KEK label */
+	TO_RDC,		/* Read device characteristics */
 	TO_SIZE,	/* #entries in tape_op_t */
 };
 
diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c
index 50f5eda..7e2b2ab 100644
--- a/drivers/s390/char/tape_3590.c
+++ b/drivers/s390/char/tape_3590.c
@@ -788,6 +788,7 @@
 	case TO_SIZE:
 	case TO_KEKL_SET:
 	case TO_KEKL_QUERY:
+	case TO_RDC:
 		break;
 	}
 	return TAPE_IO_SUCCESS;
@@ -1549,6 +1550,26 @@
 	return TAPE_IO_STOP;
 }
 
+
+static int tape_3590_read_dev_chars(struct tape_device *device,
+				    struct tape_3590_rdc_data *rdc_data)
+{
+	int rc;
+	struct tape_request *request;
+
+	request = tape_alloc_request(1, sizeof(*rdc_data));
+	if (IS_ERR(request))
+		return PTR_ERR(request);
+	request->op = TO_RDC;
+	tape_ccw_end(request->cpaddr, CCW_CMD_RDC, sizeof(*rdc_data),
+		     request->cpdata);
+	rc = tape_do_io(device, request);
+	if (rc == 0)
+		memcpy(rdc_data, request->cpdata, sizeof(*rdc_data));
+	tape_free_request(request);
+	return rc;
+}
+
 /*
  * Setup device function
  */
@@ -1557,7 +1578,7 @@
 {
 	int rc;
 	struct tape_3590_disc_data *data;
-	char *rdc_data;
+	struct tape_3590_rdc_data *rdc_data;
 
 	DBF_EVENT(6, "3590 device setup\n");
 	data = kzalloc(sizeof(struct tape_3590_disc_data), GFP_KERNEL | GFP_DMA);
@@ -1566,12 +1587,12 @@
 	data->read_back_op = READ_PREVIOUS;
 	device->discdata = data;
 
-	rdc_data = kmalloc(64, GFP_KERNEL | GFP_DMA);
+	rdc_data = kmalloc(sizeof(*rdc_data), GFP_KERNEL | GFP_DMA);
 	if (!rdc_data) {
 		rc = -ENOMEM;
 		goto fail_kmalloc;
 	}
-	rc = read_dev_chars(device->cdev, (void**)&rdc_data, 64);
+	rc = tape_3590_read_dev_chars(device, rdc_data);
 	if (rc) {
 		DBF_LH(3, "Read device characteristics failed!\n");
 		goto fail_kmalloc;
@@ -1579,7 +1600,7 @@
 	rc = tape_std_assign(device);
 	if (rc)
 		goto fail_rdc_data;
-	if (rdc_data[31] == 0x13) {
+	if (rdc_data->data[31] == 0x13) {
 		PRINT_INFO("Device has crypto support\n");
 		data->crypt_info.capability |= TAPE390_CRYPT_SUPPORTED_MASK;
 		tape_3592_disable_crypt(device);
diff --git a/drivers/s390/char/tape_3590.h b/drivers/s390/char/tape_3590.h
index aa51388..4534055 100644
--- a/drivers/s390/char/tape_3590.h
+++ b/drivers/s390/char/tape_3590.h
@@ -129,6 +129,10 @@
 	char pad2[116];
 } __attribute__ ((packed));
 
+struct tape_3590_rdc_data {
+	char data[64];
+} __attribute__ ((packed));
+
 /* Datastructures for 3592 encryption support */
 
 struct tape3592_kekl {
diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c
index e2a8a1a..2fae633 100644
--- a/drivers/s390/char/tape_core.c
+++ b/drivers/s390/char/tape_core.c
@@ -73,7 +73,7 @@
 	[TO_DIS] = "DIS",	[TO_ASSIGN] = "ASS",
 	[TO_UNASSIGN] = "UAS",  [TO_CRYPT_ON] = "CON",
 	[TO_CRYPT_OFF] = "COF",	[TO_KEKL_SET] = "KLS",
-	[TO_KEKL_QUERY] = "KLQ",
+	[TO_KEKL_QUERY] = "KLQ",[TO_RDC] = "RDC",
 };
 
 static int
@@ -911,6 +911,7 @@
 		case TO_ASSIGN:
 		case TO_UNASSIGN:
 		case TO_READ_ATTMSG:
+		case TO_RDC:
 			if (device->tape_state == TS_INIT)
 				break;
 			if (device->tape_state == TS_UNUSED)
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index b87d3b0..a5a00e9 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -125,7 +125,7 @@
 	  .recording_name = "EREP",
 	  .minor_num      = 0,
 	  .buffer_free    = 1,
-	  .priv_lock      = SPIN_LOCK_UNLOCKED,
+	  .priv_lock	  = __SPIN_LOCK_UNLOCKED(sys_ser[0].priv_lock),
 	  .autorecording  = 1,
 	  .autopurge      = 1,
 	},
@@ -134,7 +134,7 @@
 	  .recording_name = "ACCOUNT",
 	  .minor_num      = 1,
 	  .buffer_free    = 1,
-	  .priv_lock      = SPIN_LOCK_UNLOCKED,
+	  .priv_lock	  = __SPIN_LOCK_UNLOCKED(sys_ser[1].priv_lock),
 	  .autorecording  = 1,
 	  .autopurge      = 1,
 	},
@@ -143,7 +143,7 @@
 	  .recording_name = "SYMPTOM",
 	  .minor_num      = 2,
 	  .buffer_free    = 1,
-	  .priv_lock      = SPIN_LOCK_UNLOCKED,
+	  .priv_lock	  = __SPIN_LOCK_UNLOCKED(sys_ser[2].priv_lock),
 	  .autorecording  = 1,
 	  .autopurge      = 1,
 	}
@@ -385,6 +385,9 @@
 
 	struct vmlogrdr_priv_t * logptr = filp->private_data;
 
+	iucv_path_sever(logptr->path, NULL);
+	kfree(logptr->path);
+	logptr->path = NULL;
 	if (logptr->autorecording) {
 		ret = vmlogrdr_recording(logptr,0,logptr->autopurge);
 		if (ret)
diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c
new file mode 100644
index 0000000..89d43931
--- /dev/null
+++ b/drivers/s390/char/zcore.c
@@ -0,0 +1,651 @@
+/*
+ * zcore module to export memory content and register sets for creating system
+ * dumps on SCSI disks (zfcpdump). The "zcore/mem" debugfs file shows the same
+ * dump format as s390 standalone dumps.
+ *
+ * For more information please refer to Documentation/s390/zfcpdump.txt
+ *
+ * Copyright IBM Corp. 2003,2007
+ * Author(s): Michael Holzheu
+ */
+
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/utsname.h>
+#include <linux/debugfs.h>
+#include <asm/ipl.h>
+#include <asm/sclp.h>
+#include <asm/setup.h>
+#include <asm/sigp.h>
+#include <asm/uaccess.h>
+#include <asm/debug.h>
+#include <asm/processor.h>
+#include <asm/irqflags.h>
+
+#define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x)
+#define MSG(x...) printk( KERN_ALERT x )
+#define ERROR_MSG(x...) printk ( KERN_ALERT "DUMP: " x )
+
+#define TO_USER		0
+#define TO_KERNEL	1
+
+enum arch_id {
+	ARCH_S390	= 0,
+	ARCH_S390X	= 1,
+};
+
+/* dump system info */
+
+struct sys_info {
+	enum arch_id	arch;
+	unsigned long	sa_base;
+	u32		sa_size;
+	int		cpu_map[NR_CPUS];
+	unsigned long	mem_size;
+	union save_area	lc_mask;
+};
+
+static struct sys_info sys_info;
+static struct debug_info *zcore_dbf;
+static int hsa_available;
+static struct dentry *zcore_dir;
+static struct dentry *zcore_file;
+
+/*
+ * Copy memory from HSA to kernel or user memory (not reentrant):
+ *
+ * @dest:  Kernel or user buffer where memory should be copied to
+ * @src:   Start address within HSA where data should be copied
+ * @count: Size of buffer, which should be copied
+ * @mode:  Either TO_KERNEL or TO_USER
+ */
+static int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode)
+{
+	int offs, blk_num;
+	static char buf[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
+
+	if (count == 0)
+		return 0;
+
+	/* copy first block */
+	offs = 0;
+	if ((src % PAGE_SIZE) != 0) {
+		blk_num = src / PAGE_SIZE + 2;
+		if (sclp_sdias_copy(buf, blk_num, 1)) {
+			TRACE("sclp_sdias_copy() failed\n");
+			return -EIO;
+		}
+		offs = min((PAGE_SIZE - (src % PAGE_SIZE)), count);
+		if (mode == TO_USER) {
+			if (copy_to_user((__force __user void*) dest,
+					 buf + (src % PAGE_SIZE), offs))
+				return -EFAULT;
+		} else
+			memcpy(dest, buf + (src % PAGE_SIZE), offs);
+	}
+	if (offs == count)
+		goto out;
+
+	/* copy middle */
+	for (; (offs + PAGE_SIZE) <= count; offs += PAGE_SIZE) {
+		blk_num = (src + offs) / PAGE_SIZE + 2;
+		if (sclp_sdias_copy(buf, blk_num, 1)) {
+			TRACE("sclp_sdias_copy() failed\n");
+			return -EIO;
+		}
+		if (mode == TO_USER) {
+			if (copy_to_user((__force __user void*) dest + offs,
+					 buf, PAGE_SIZE))
+				return -EFAULT;
+		} else
+			memcpy(dest + offs, buf, PAGE_SIZE);
+	}
+	if (offs == count)
+		goto out;
+
+	/* copy last block */
+	blk_num = (src + offs) / PAGE_SIZE + 2;
+	if (sclp_sdias_copy(buf, blk_num, 1)) {
+		TRACE("sclp_sdias_copy() failed\n");
+		return -EIO;
+	}
+	if (mode == TO_USER) {
+		if (copy_to_user((__force __user void*) dest + offs, buf,
+				 PAGE_SIZE))
+			return -EFAULT;
+	} else
+		memcpy(dest + offs, buf, count - offs);
+out:
+	return 0;
+}
+
+static int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count)
+{
+	return memcpy_hsa((void __force *) dest, src, count, TO_USER);
+}
+
+static int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count)
+{
+	return memcpy_hsa(dest, src, count, TO_KERNEL);
+}
+
+static int memcpy_real(void *dest, unsigned long src, size_t count)
+{
+	unsigned long flags;
+	int rc = -EFAULT;
+	register unsigned long _dest asm("2") = (unsigned long) dest;
+	register unsigned long _len1 asm("3") = (unsigned long) count;
+	register unsigned long _src  asm("4") = src;
+	register unsigned long _len2 asm("5") = (unsigned long) count;
+
+	if (count == 0)
+		return 0;
+	flags = __raw_local_irq_stnsm(0xf8); /* switch to real mode */
+	asm volatile (
+		"0:	mvcle	%1,%2,0x0\n"
+		"1:	jo	0b\n"
+		"	lhi	%0,0x0\n"
+		"2:\n"
+		EX_TABLE(1b,2b)
+		: "+d" (rc)
+		: "d" (_dest), "d" (_src), "d" (_len1), "d" (_len2)
+		: "cc", "memory");
+	__raw_local_irq_ssm(flags);
+
+	return rc;
+}
+
+static int memcpy_real_user(__user void *dest, unsigned long src, size_t count)
+{
+	static char buf[4096];
+	int offs = 0, size;
+
+	while (offs < count) {
+		size = min(sizeof(buf), count - offs);
+		if (memcpy_real(buf, src + offs, size))
+			return -EFAULT;
+		if (copy_to_user(dest + offs, buf, size))
+			return -EFAULT;
+		offs += size;
+	}
+	return 0;
+}
+
+#ifdef __s390x__
+/*
+ * Convert s390x (64 bit) cpu info to s390 (32 bit) cpu info
+ */
+static void __init s390x_to_s390_regs(union save_area *out, union save_area *in,
+				      int cpu)
+{
+	int i;
+
+	for (i = 0; i < 16; i++) {
+		out->s390.gp_regs[i] = in->s390x.gp_regs[i] & 0x00000000ffffffff;
+		out->s390.acc_regs[i] = in->s390x.acc_regs[i];
+		out->s390.ctrl_regs[i] =
+			in->s390x.ctrl_regs[i] & 0x00000000ffffffff;
+	}
+	/* locore for 31 bit has only space for fpregs 0,2,4,6 */
+	out->s390.fp_regs[0] = in->s390x.fp_regs[0];
+	out->s390.fp_regs[1] = in->s390x.fp_regs[2];
+	out->s390.fp_regs[2] = in->s390x.fp_regs[4];
+	out->s390.fp_regs[3] = in->s390x.fp_regs[6];
+	memcpy(&(out->s390.psw[0]), &(in->s390x.psw[0]), 4);
+	out->s390.psw[1] |= 0x8; /* set bit 12 */
+	memcpy(&(out->s390.psw[4]),&(in->s390x.psw[12]), 4);
+	out->s390.psw[4] |= 0x80; /* set (31bit) addressing bit */
+	out->s390.pref_reg = in->s390x.pref_reg;
+	out->s390.timer = in->s390x.timer;
+	out->s390.clk_cmp = in->s390x.clk_cmp;
+}
+
+static void __init s390x_to_s390_save_areas(void)
+{
+	int i = 1;
+	static union save_area tmp;
+
+	while (zfcpdump_save_areas[i]) {
+		s390x_to_s390_regs(&tmp, zfcpdump_save_areas[i], i);
+		memcpy(zfcpdump_save_areas[i], &tmp, sizeof(tmp));
+		i++;
+	}
+}
+
+#endif /* __s390x__ */
+
+static int __init init_cpu_info(enum arch_id arch)
+{
+	union save_area *sa;
+
+	/* get info for boot cpu from lowcore, stored in the HSA */
+
+	sa = kmalloc(sizeof(*sa), GFP_KERNEL);
+	if (!sa) {
+		ERROR_MSG("kmalloc failed: %s: %i\n",__FUNCTION__, __LINE__);
+		return -ENOMEM;
+	}
+	if (memcpy_hsa_kernel(sa, sys_info.sa_base, sys_info.sa_size) < 0) {
+		ERROR_MSG("could not copy from HSA\n");
+		kfree(sa);
+		return -EIO;
+	}
+	zfcpdump_save_areas[0] = sa;
+
+#ifdef __s390x__
+	/* convert s390x regs to s390, if we are dumping an s390 Linux */
+
+	if (arch == ARCH_S390)
+		s390x_to_s390_save_areas();
+#endif
+
+	return 0;
+}
+
+static DEFINE_MUTEX(zcore_mutex);
+
+#define DUMP_VERSION	0x3
+#define DUMP_MAGIC	0xa8190173618f23fdULL
+#define DUMP_ARCH_S390X	2
+#define DUMP_ARCH_S390	1
+#define HEADER_SIZE	4096
+
+/* dump header dumped according to s390 crash dump format */
+
+struct zcore_header {
+	u64 magic;
+	u32 version;
+	u32 header_size;
+	u32 dump_level;
+	u32 page_size;
+	u64 mem_size;
+	u64 mem_start;
+	u64 mem_end;
+	u32 num_pages;
+	u32 pad1;
+	u64 tod;
+	cpuid_t cpu_id;
+	u32 arch_id;
+	u32 build_arch;
+	char pad2[4016];
+} __attribute__((packed,__aligned__(16)));
+
+static struct zcore_header zcore_header = {
+	.magic		= DUMP_MAGIC,
+	.version	= DUMP_VERSION,
+	.header_size	= 4096,
+	.dump_level	= 0,
+	.page_size	= PAGE_SIZE,
+	.mem_start	= 0,
+#ifdef __s390x__
+	.build_arch	= DUMP_ARCH_S390X,
+#else
+	.build_arch	= DUMP_ARCH_S390,
+#endif
+};
+
+/*
+ * Copy lowcore info to buffer. Use map in order to copy only register parts.
+ *
+ * @buf:    User buffer
+ * @sa:     Pointer to save area
+ * @sa_off: Offset in save area to copy
+ * @len:    Number of bytes to copy
+ */
+static int copy_lc(void __user *buf, void *sa, int sa_off, int len)
+{
+	int i;
+	char *lc_mask = (char*)&sys_info.lc_mask;
+
+	for (i = 0; i < len; i++) {
+		if (!lc_mask[i + sa_off])
+			continue;
+		if (copy_to_user(buf + i, sa + sa_off + i, 1))
+			return -EFAULT;
+	}
+	return 0;
+}
+
+/*
+ * Copy lowcores info to memory, if necessary
+ *
+ * @buf:   User buffer
+ * @addr:  Start address of buffer in dump memory
+ * @count: Size of buffer
+ */
+static int zcore_add_lc(char __user *buf, unsigned long start, size_t count)
+{
+	unsigned long end;
+	int i = 0;
+
+	if (count == 0)
+		return 0;
+
+	end = start + count;
+	while (zfcpdump_save_areas[i]) {
+		unsigned long cp_start, cp_end; /* copy range */
+		unsigned long sa_start, sa_end; /* save area range */
+		unsigned long prefix;
+		unsigned long sa_off, len, buf_off;
+
+		if (sys_info.arch == ARCH_S390)
+			prefix = zfcpdump_save_areas[i]->s390.pref_reg;
+		else
+			prefix = zfcpdump_save_areas[i]->s390x.pref_reg;
+
+		sa_start = prefix + sys_info.sa_base;
+		sa_end = prefix + sys_info.sa_base + sys_info.sa_size;
+
+		if ((end < sa_start) || (start > sa_end))
+			goto next;
+		cp_start = max(start, sa_start);
+		cp_end = min(end, sa_end);
+
+		buf_off = cp_start - start;
+		sa_off = cp_start - sa_start;
+		len = cp_end - cp_start;
+
+		TRACE("copy_lc for: %lx\n", start);
+		if (copy_lc(buf + buf_off, zfcpdump_save_areas[i], sa_off, len))
+			return -EFAULT;
+next:
+		i++;
+	}
+	return 0;
+}
+
+/*
+ * Read routine for zcore character device
+ * First 4K are dump header
+ * Next 32MB are HSA Memory
+ * Rest is read from absolute Memory
+ */
+static ssize_t zcore_read(struct file *file, char __user *buf, size_t count,
+			  loff_t *ppos)
+{
+	unsigned long mem_start; /* Start address in memory */
+	size_t mem_offs;	 /* Offset in dump memory */
+	size_t hdr_count;	 /* Size of header part of output buffer */
+	size_t size;
+	int rc;
+
+	mutex_lock(&zcore_mutex);
+
+	if (*ppos > (sys_info.mem_size + HEADER_SIZE)) {
+		rc = -EINVAL;
+		goto fail;
+	}
+
+	count = min(count, (size_t) (sys_info.mem_size + HEADER_SIZE - *ppos));
+
+	/* Copy dump header */
+	if (*ppos < HEADER_SIZE) {
+		size = min(count, (size_t) (HEADER_SIZE - *ppos));
+		if (copy_to_user(buf, &zcore_header + *ppos, size)) {
+			rc = -EFAULT;
+			goto fail;
+		}
+		hdr_count = size;
+		mem_start = 0;
+	} else {
+		hdr_count = 0;
+		mem_start = *ppos - HEADER_SIZE;
+	}
+
+	mem_offs = 0;
+
+	/* Copy from HSA data */
+	if (*ppos < (ZFCPDUMP_HSA_SIZE + HEADER_SIZE)) {
+		size = min((count - hdr_count), (size_t) (ZFCPDUMP_HSA_SIZE
+			   - mem_start));
+		rc = memcpy_hsa_user(buf + hdr_count, mem_start, size);
+		if (rc)
+			goto fail;
+
+		mem_offs += size;
+	}
+
+	/* Copy from real mem */
+	size = count - mem_offs - hdr_count;
+	rc = memcpy_real_user(buf + hdr_count + mem_offs, mem_start + mem_offs,
+			      size);
+	if (rc)
+		goto fail;
+
+	/*
+	 * Since s390 dump analysis tools like lcrash or crash
+	 * expect register sets in the prefix pages of the cpus,
+	 * we copy them into the read buffer, if necessary.
+	 * buf + hdr_count: Start of memory part of output buffer
+	 * mem_start: Start memory address to copy from
+	 * count - hdr_count: Size of memory area to copy
+	 */
+	if (zcore_add_lc(buf + hdr_count, mem_start, count - hdr_count)) {
+		rc = -EFAULT;
+		goto fail;
+	}
+	*ppos += count;
+fail:
+	mutex_unlock(&zcore_mutex);
+	return (rc < 0) ? rc : count;
+}
+
+static int zcore_open(struct inode *inode, struct file *filp)
+{
+	if (!hsa_available)
+		return -ENODATA;
+	else
+		return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
+}
+
+static int zcore_release(struct inode *inode, struct file *filep)
+{
+	diag308(DIAG308_REL_HSA, NULL);
+	hsa_available = 0;
+	return 0;
+}
+
+static loff_t zcore_lseek(struct file *file, loff_t offset, int orig)
+{
+	loff_t rc;
+
+	mutex_lock(&zcore_mutex);
+	switch (orig) {
+	case 0:
+		file->f_pos = offset;
+		rc = file->f_pos;
+		break;
+	case 1:
+		file->f_pos += offset;
+		rc = file->f_pos;
+		break;
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&zcore_mutex);
+	return rc;
+}
+
+static struct file_operations zcore_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= zcore_lseek,
+	.read		= zcore_read,
+	.open		= zcore_open,
+	.release	= zcore_release,
+};
+
+
+static void __init set_s390_lc_mask(union save_area *map)
+{
+	memset(&map->s390.ext_save, 0xff, sizeof(map->s390.ext_save));
+	memset(&map->s390.timer, 0xff, sizeof(map->s390.timer));
+	memset(&map->s390.clk_cmp, 0xff, sizeof(map->s390.clk_cmp));
+	memset(&map->s390.psw, 0xff, sizeof(map->s390.psw));
+	memset(&map->s390.pref_reg, 0xff, sizeof(map->s390.pref_reg));
+	memset(&map->s390.acc_regs, 0xff, sizeof(map->s390.acc_regs));
+	memset(&map->s390.fp_regs, 0xff, sizeof(map->s390.fp_regs));
+	memset(&map->s390.gp_regs, 0xff, sizeof(map->s390.gp_regs));
+	memset(&map->s390.ctrl_regs, 0xff, sizeof(map->s390.ctrl_regs));
+}
+
+static void __init set_s390x_lc_mask(union save_area *map)
+{
+	memset(&map->s390x.fp_regs, 0xff, sizeof(map->s390x.fp_regs));
+	memset(&map->s390x.gp_regs, 0xff, sizeof(map->s390x.gp_regs));
+	memset(&map->s390x.psw, 0xff, sizeof(map->s390x.psw));
+	memset(&map->s390x.pref_reg, 0xff, sizeof(map->s390x.pref_reg));
+	memset(&map->s390x.fp_ctrl_reg, 0xff, sizeof(map->s390x.fp_ctrl_reg));
+	memset(&map->s390x.tod_reg, 0xff, sizeof(map->s390x.tod_reg));
+	memset(&map->s390x.timer, 0xff, sizeof(map->s390x.timer));
+	memset(&map->s390x.clk_cmp, 0xff, sizeof(map->s390x.clk_cmp));
+	memset(&map->s390x.acc_regs, 0xff, sizeof(map->s390x.acc_regs));
+	memset(&map->s390x.ctrl_regs, 0xff, sizeof(map->s390x.ctrl_regs));
+}
+
+/*
+ * Initialize dump globals for a given architecture
+ */
+static int __init sys_info_init(enum arch_id arch)
+{
+	switch (arch) {
+	case ARCH_S390X:
+		MSG("DETECTED 'S390X (64 bit) OS'\n");
+		sys_info.sa_base = SAVE_AREA_BASE_S390X;
+		sys_info.sa_size = sizeof(struct save_area_s390x);
+		set_s390x_lc_mask(&sys_info.lc_mask);
+		break;
+	case ARCH_S390:
+		MSG("DETECTED 'S390 (32 bit) OS'\n");
+		sys_info.sa_base = SAVE_AREA_BASE_S390;
+		sys_info.sa_size = sizeof(struct save_area_s390);
+		set_s390_lc_mask(&sys_info.lc_mask);
+		break;
+	default:
+		ERROR_MSG("unknown architecture 0x%x.\n",arch);
+		return -EINVAL;
+	}
+	sys_info.arch = arch;
+	if (init_cpu_info(arch)) {
+		ERROR_MSG("get cpu info failed\n");
+		return -ENOMEM;
+	}
+	sys_info.mem_size = real_memory_size;
+
+	return 0;
+}
+
+static int __init check_sdias(void)
+{
+	int rc, act_hsa_size;
+
+	rc = sclp_sdias_blk_count();
+	if (rc < 0) {
+		ERROR_MSG("Could not determine HSA size\n");
+		return rc;
+	}
+	act_hsa_size = (rc - 1) * PAGE_SIZE;
+	if (act_hsa_size < ZFCPDUMP_HSA_SIZE) {
+		ERROR_MSG("HSA size too small: %i\n", act_hsa_size);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void __init zcore_header_init(int arch, struct zcore_header *hdr)
+{
+	if (arch == ARCH_S390X)
+		hdr->arch_id = DUMP_ARCH_S390X;
+	else
+		hdr->arch_id = DUMP_ARCH_S390;
+	hdr->mem_size = sys_info.mem_size;
+	hdr->mem_end = sys_info.mem_size;
+	hdr->num_pages = sys_info.mem_size / PAGE_SIZE;
+	hdr->tod = get_clock();
+	get_cpu_id(&hdr->cpu_id);
+}
+
+extern int sdias_init(void);
+
+static int __init zcore_init(void)
+{
+	unsigned char arch;
+	int rc;
+
+	if (ipl_info.type != IPL_TYPE_FCP_DUMP)
+		return -ENODATA;
+
+	zcore_dbf = debug_register("zcore", 4, 1, 4 * sizeof(long));
+	debug_register_view(zcore_dbf, &debug_sprintf_view);
+	debug_set_level(zcore_dbf, 6);
+
+	TRACE("devno:  %x\n", ipl_info.data.fcp.dev_id.devno);
+	TRACE("wwpn:   %llx\n", (unsigned long long) ipl_info.data.fcp.wwpn);
+	TRACE("lun:    %llx\n", (unsigned long long) ipl_info.data.fcp.lun);
+
+	rc = sdias_init();
+	if (rc)
+		goto fail;
+
+	rc = check_sdias();
+	if (rc) {
+		ERROR_MSG("Dump initialization failed\n");
+		goto fail;
+	}
+
+	rc = memcpy_hsa_kernel(&arch, __LC_AR_MODE_ID, 1);
+	if (rc) {
+		ERROR_MSG("sdial memcpy for arch id failed\n");
+		goto fail;
+	}
+
+#ifndef __s390x__
+	if (arch == ARCH_S390X) {
+		ERROR_MSG("32 bit dumper can't dump 64 bit system!\n");
+		rc = -EINVAL;
+		goto fail;
+	}
+#endif
+
+	rc = sys_info_init(arch);
+	if (rc) {
+		ERROR_MSG("arch init failed\n");
+		goto fail;
+	}
+
+	zcore_header_init(arch, &zcore_header);
+
+	zcore_dir = debugfs_create_dir("zcore" , NULL);
+	if (!zcore_dir) {
+		rc = -ENOMEM;
+		goto fail;
+	}
+	zcore_file = debugfs_create_file("mem", S_IRUSR, zcore_dir, NULL,
+					 &zcore_fops);
+	if (!zcore_file) {
+		debugfs_remove(zcore_dir);
+		rc = -ENOMEM;
+		goto fail;
+	}
+	hsa_available = 1;
+	return 0;
+
+fail:
+	diag308(DIAG308_REL_HSA, NULL);
+	return rc;
+}
+
+extern void sdias_exit(void);
+
+static void __exit zcore_exit(void)
+{
+	debug_unregister(zcore_dbf);
+	sdias_exit();
+	diag308(DIAG308_REL_HSA, NULL);
+}
+
+MODULE_AUTHOR("Copyright IBM Corp. 2003,2007");
+MODULE_DESCRIPTION("zcore module for zfcpdump support");
+MODULE_LICENSE("GPL");
+
+subsys_initcall(zcore_init);
+module_exit(zcore_exit);
diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile
index c490c2a..cfaf77b 100644
--- a/drivers/s390/cio/Makefile
+++ b/drivers/s390/cio/Makefile
@@ -2,7 +2,7 @@
 # Makefile for the S/390 common i/o drivers
 #
 
-obj-y += airq.o blacklist.o chsc.o cio.o css.o
+obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o
 ccw_device-objs += device.o device_fsm.o device_ops.o
 ccw_device-objs += device_id.o device_pgid.o device_status.o
 obj-y += ccw_device.o cmf.o
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index 5aeb68e..e5ccda6 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -75,8 +75,10 @@
 {
 	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
 
+	mutex_lock(&gdev->reg_mutex);
 	__ccwgroup_remove_symlinks(gdev);
 	device_unregister(dev);
+	mutex_unlock(&gdev->reg_mutex);
 }
 
 static ssize_t
@@ -173,7 +175,8 @@
 		return -ENOMEM;
 
 	atomic_set(&gdev->onoff, 0);
-
+	mutex_init(&gdev->reg_mutex);
+	mutex_lock(&gdev->reg_mutex);
 	for (i = 0; i < argc; i++) {
 		gdev->cdev[i] = get_ccwdev_by_busid(cdrv, argv[i]);
 
@@ -183,12 +186,12 @@
 		    || gdev->cdev[i]->id.driver_info !=
 		    gdev->cdev[0]->id.driver_info) {
 			rc = -EINVAL;
-			goto free_dev;
+			goto error;
 		}
 		/* Don't allow a device to belong to more than one group. */
 		if (gdev->cdev[i]->dev.driver_data) {
 			rc = -EINVAL;
-			goto free_dev;
+			goto error;
 		}
 		gdev->cdev[i]->dev.driver_data = gdev;
 	}
@@ -203,9 +206,8 @@
 			gdev->cdev[0]->dev.bus_id);
 
 	rc = device_register(&gdev->dev);
-	
 	if (rc)
-		goto free_dev;
+		goto error;
 	get_device(&gdev->dev);
 	rc = device_create_file(&gdev->dev, &dev_attr_ungroup);
 
@@ -216,6 +218,7 @@
 
 	rc = __ccwgroup_create_symlinks(gdev);
 	if (!rc) {
+		mutex_unlock(&gdev->reg_mutex);
 		put_device(&gdev->dev);
 		return 0;
 	}
@@ -224,19 +227,12 @@
 error:
 	for (i = 0; i < argc; i++)
 		if (gdev->cdev[i]) {
-			put_device(&gdev->cdev[i]->dev);
-			gdev->cdev[i]->dev.driver_data = NULL;
-		}
-	put_device(&gdev->dev);
-	return rc;
-free_dev:
-	for (i = 0; i < argc; i++)
-		if (gdev->cdev[i]) {
 			if (gdev->cdev[i]->dev.driver_data == gdev)
 				gdev->cdev[i]->dev.driver_data = NULL;
 			put_device(&gdev->cdev[i]->dev);
 		}
-	kfree(gdev);
+	mutex_unlock(&gdev->reg_mutex);
+	put_device(&gdev->dev);
 	return rc;
 }
 
@@ -422,8 +418,12 @@
 	get_driver(&cdriver->driver);
 	while ((dev = driver_find_device(&cdriver->driver, NULL, NULL,
 					 __ccwgroup_match_all))) {
-		__ccwgroup_remove_symlinks(to_ccwgroupdev(dev));
+		struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
+
+		mutex_lock(&gdev->reg_mutex);
+		__ccwgroup_remove_symlinks(gdev);
 		device_unregister(dev);
+		mutex_unlock(&gdev->reg_mutex);
 		put_device(dev);
 	}
 	put_driver(&cdriver->driver);
@@ -444,8 +444,10 @@
 	if (cdev->dev.driver_data) {
 		gdev = (struct ccwgroup_device *)cdev->dev.driver_data;
 		if (get_device(&gdev->dev)) {
+			mutex_lock(&gdev->reg_mutex);
 			if (device_is_registered(&gdev->dev))
 				return gdev;
+			mutex_unlock(&gdev->reg_mutex);
 			put_device(&gdev->dev);
 		}
 		return NULL;
@@ -465,6 +467,7 @@
 	if (gdev) {
 		__ccwgroup_remove_symlinks(gdev);
 		device_unregister(&gdev->dev);
+		mutex_unlock(&gdev->reg_mutex);
 		put_device(&gdev->dev);
 	}
 }
diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c
new file mode 100644
index 0000000..ac289e6
--- /dev/null
+++ b/drivers/s390/cio/chp.c
@@ -0,0 +1,683 @@
+/*
+ *  drivers/s390/cio/chp.c
+ *
+ *    Copyright IBM Corp. 1999,2007
+ *    Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
+ *		 Arnd Bergmann (arndb@de.ibm.com)
+ *		 Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/bug.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <asm/errno.h>
+#include <asm/chpid.h>
+#include <asm/sclp.h>
+
+#include "cio.h"
+#include "css.h"
+#include "ioasm.h"
+#include "cio_debug.h"
+#include "chp.h"
+
+#define to_channelpath(device) container_of(device, struct channel_path, dev)
+#define CHP_INFO_UPDATE_INTERVAL	1*HZ
+
+enum cfg_task_t {
+	cfg_none,
+	cfg_configure,
+	cfg_deconfigure
+};
+
+/* Map for pending configure tasks. */
+static enum cfg_task_t chp_cfg_task[__MAX_CSSID + 1][__MAX_CHPID + 1];
+static DEFINE_MUTEX(cfg_lock);
+static int cfg_busy;
+
+/* Map for channel-path status. */
+static struct sclp_chp_info chp_info;
+static DEFINE_MUTEX(info_lock);
+
+/* Time after which channel-path status may be outdated. */
+static unsigned long chp_info_expires;
+
+/* Workqueue to perform pending configure tasks. */
+static struct workqueue_struct *chp_wq;
+static struct work_struct cfg_work;
+
+/* Wait queue for configure completion events. */
+static wait_queue_head_t cfg_wait_queue;
+
+/* Return channel_path struct for given chpid. */
+static inline struct channel_path *chpid_to_chp(struct chp_id chpid)
+{
+	return css[chpid.cssid]->chps[chpid.id];
+}
+
+/* Set vary state for given chpid. */
+static void set_chp_logically_online(struct chp_id chpid, int onoff)
+{
+	chpid_to_chp(chpid)->state = onoff;
+}
+
+/* On succes return 0 if channel-path is varied offline, 1 if it is varied
+ * online. Return -ENODEV if channel-path is not registered. */
+int chp_get_status(struct chp_id chpid)
+{
+	return (chpid_to_chp(chpid) ? chpid_to_chp(chpid)->state : -ENODEV);
+}
+
+/**
+ * chp_get_sch_opm - return opm for subchannel
+ * @sch: subchannel
+ *
+ * Calculate and return the operational path mask (opm) based on the chpids
+ * used by the subchannel and the status of the associated channel-paths.
+ */
+u8 chp_get_sch_opm(struct subchannel *sch)
+{
+	struct chp_id chpid;
+	int opm;
+	int i;
+
+	opm = 0;
+	chp_id_init(&chpid);
+	for (i=0; i < 8; i++) {
+		opm <<= 1;
+		chpid.id = sch->schib.pmcw.chpid[i];
+		if (chp_get_status(chpid) != 0)
+			opm |= 1;
+	}
+	return opm;
+}
+
+/**
+ * chp_is_registered - check if a channel-path is registered
+ * @chpid: channel-path ID
+ *
+ * Return non-zero if a channel-path with the given chpid is registered,
+ * zero otherwise.
+ */
+int chp_is_registered(struct chp_id chpid)
+{
+	return chpid_to_chp(chpid) != NULL;
+}
+
+/*
+ * Function: s390_vary_chpid
+ * Varies the specified chpid online or offline
+ */
+static int s390_vary_chpid(struct chp_id chpid, int on)
+{
+	char dbf_text[15];
+	int status;
+
+	sprintf(dbf_text, on?"varyon%x.%02x":"varyoff%x.%02x", chpid.cssid,
+		chpid.id);
+	CIO_TRACE_EVENT( 2, dbf_text);
+
+	status = chp_get_status(chpid);
+	if (status < 0) {
+		printk(KERN_ERR "Can't vary unknown chpid %x.%02x\n",
+		       chpid.cssid, chpid.id);
+		return -EINVAL;
+	}
+
+	if (!on && !status) {
+		printk(KERN_ERR "chpid %x.%02x is already offline\n",
+		       chpid.cssid, chpid.id);
+		return -EINVAL;
+	}
+
+	set_chp_logically_online(chpid, on);
+	chsc_chp_vary(chpid, on);
+	return 0;
+}
+
+/*
+ * Channel measurement related functions
+ */
+static ssize_t chp_measurement_chars_read(struct kobject *kobj, char *buf,
+					  loff_t off, size_t count)
+{
+	struct channel_path *chp;
+	unsigned int size;
+
+	chp = to_channelpath(container_of(kobj, struct device, kobj));
+	if (!chp->cmg_chars)
+		return 0;
+
+	size = sizeof(struct cmg_chars);
+
+	if (off > size)
+		return 0;
+	if (off + count > size)
+		count = size - off;
+	memcpy(buf, chp->cmg_chars + off, count);
+	return count;
+}
+
+static struct bin_attribute chp_measurement_chars_attr = {
+	.attr = {
+		.name = "measurement_chars",
+		.mode = S_IRUSR,
+		.owner = THIS_MODULE,
+	},
+	.size = sizeof(struct cmg_chars),
+	.read = chp_measurement_chars_read,
+};
+
+static void chp_measurement_copy_block(struct cmg_entry *buf,
+				       struct channel_subsystem *css,
+				       struct chp_id chpid)
+{
+	void *area;
+	struct cmg_entry *entry, reference_buf;
+	int idx;
+
+	if (chpid.id < 128) {
+		area = css->cub_addr1;
+		idx = chpid.id;
+	} else {
+		area = css->cub_addr2;
+		idx = chpid.id - 128;
+	}
+	entry = area + (idx * sizeof(struct cmg_entry));
+	do {
+		memcpy(buf, entry, sizeof(*entry));
+		memcpy(&reference_buf, entry, sizeof(*entry));
+	} while (reference_buf.values[0] != buf->values[0]);
+}
+
+static ssize_t chp_measurement_read(struct kobject *kobj, char *buf,
+				    loff_t off, size_t count)
+{
+	struct channel_path *chp;
+	struct channel_subsystem *css;
+	unsigned int size;
+
+	chp = to_channelpath(container_of(kobj, struct device, kobj));
+	css = to_css(chp->dev.parent);
+
+	size = sizeof(struct cmg_entry);
+
+	/* Only allow single reads. */
+	if (off || count < size)
+		return 0;
+	chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->chpid);
+	count = size;
+	return count;
+}
+
+static struct bin_attribute chp_measurement_attr = {
+	.attr = {
+		.name = "measurement",
+		.mode = S_IRUSR,
+		.owner = THIS_MODULE,
+	},
+	.size = sizeof(struct cmg_entry),
+	.read = chp_measurement_read,
+};
+
+void chp_remove_cmg_attr(struct channel_path *chp)
+{
+	device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr);
+	device_remove_bin_file(&chp->dev, &chp_measurement_attr);
+}
+
+int chp_add_cmg_attr(struct channel_path *chp)
+{
+	int ret;
+
+	ret = device_create_bin_file(&chp->dev, &chp_measurement_chars_attr);
+	if (ret)
+		return ret;
+	ret = device_create_bin_file(&chp->dev, &chp_measurement_attr);
+	if (ret)
+		device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr);
+	return ret;
+}
+
+/*
+ * Files for the channel path entries.
+ */
+static ssize_t chp_status_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct channel_path *chp = container_of(dev, struct channel_path, dev);
+
+	if (!chp)
+		return 0;
+	return (chp_get_status(chp->chpid) ? sprintf(buf, "online\n") :
+		sprintf(buf, "offline\n"));
+}
+
+static ssize_t chp_status_write(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct channel_path *cp = container_of(dev, struct channel_path, dev);
+	char cmd[10];
+	int num_args;
+	int error;
+
+	num_args = sscanf(buf, "%5s", cmd);
+	if (!num_args)
+		return count;
+
+	if (!strnicmp(cmd, "on", 2) || !strcmp(cmd, "1"))
+		error = s390_vary_chpid(cp->chpid, 1);
+	else if (!strnicmp(cmd, "off", 3) || !strcmp(cmd, "0"))
+		error = s390_vary_chpid(cp->chpid, 0);
+	else
+		error = -EINVAL;
+
+	return error < 0 ? error : count;
+
+}
+
+static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write);
+
+static ssize_t chp_configure_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct channel_path *cp;
+	int status;
+
+	cp = container_of(dev, struct channel_path, dev);
+	status = chp_info_get_status(cp->chpid);
+	if (status < 0)
+		return status;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", status);
+}
+
+static int cfg_wait_idle(void);
+
+static ssize_t chp_configure_write(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	struct channel_path *cp;
+	int val;
+	char delim;
+
+	if (sscanf(buf, "%d %c", &val, &delim) != 1)
+		return -EINVAL;
+	if (val != 0 && val != 1)
+		return -EINVAL;
+	cp = container_of(dev, struct channel_path, dev);
+	chp_cfg_schedule(cp->chpid, val);
+	cfg_wait_idle();
+
+	return count;
+}
+
+static DEVICE_ATTR(configure, 0644, chp_configure_show, chp_configure_write);
+
+static ssize_t chp_type_show(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	struct channel_path *chp = container_of(dev, struct channel_path, dev);
+
+	if (!chp)
+		return 0;
+	return sprintf(buf, "%x\n", chp->desc.desc);
+}
+
+static DEVICE_ATTR(type, 0444, chp_type_show, NULL);
+
+static ssize_t chp_cmg_show(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct channel_path *chp = to_channelpath(dev);
+
+	if (!chp)
+		return 0;
+	if (chp->cmg == -1) /* channel measurements not available */
+		return sprintf(buf, "unknown\n");
+	return sprintf(buf, "%x\n", chp->cmg);
+}
+
+static DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL);
+
+static ssize_t chp_shared_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct channel_path *chp = to_channelpath(dev);
+
+	if (!chp)
+		return 0;
+	if (chp->shared == -1) /* channel measurements not available */
+		return sprintf(buf, "unknown\n");
+	return sprintf(buf, "%x\n", chp->shared);
+}
+
+static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL);
+
+static struct attribute * chp_attrs[] = {
+	&dev_attr_status.attr,
+	&dev_attr_configure.attr,
+	&dev_attr_type.attr,
+	&dev_attr_cmg.attr,
+	&dev_attr_shared.attr,
+	NULL,
+};
+
+static struct attribute_group chp_attr_group = {
+	.attrs = chp_attrs,
+};
+
+static void chp_release(struct device *dev)
+{
+	struct channel_path *cp;
+
+	cp = container_of(dev, struct channel_path, dev);
+	kfree(cp);
+}
+
+/**
+ * chp_new - register a new channel-path
+ * @chpid - channel-path ID
+ *
+ * Create and register data structure representing new channel-path. Return
+ * zero on success, non-zero otherwise.
+ */
+int chp_new(struct chp_id chpid)
+{
+	struct channel_path *chp;
+	int ret;
+
+	if (chp_is_registered(chpid))
+		return 0;
+	chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL);
+	if (!chp)
+		return -ENOMEM;
+
+	/* fill in status, etc. */
+	chp->chpid = chpid;
+	chp->state = 1;
+	chp->dev.parent = &css[chpid.cssid]->device;
+	chp->dev.release = chp_release;
+	snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp%x.%02x", chpid.cssid,
+		 chpid.id);
+
+	/* Obtain channel path description and fill it in. */
+	ret = chsc_determine_channel_path_description(chpid, &chp->desc);
+	if (ret)
+		goto out_free;
+	if ((chp->desc.flags & 0x80) == 0) {
+		ret = -ENODEV;
+		goto out_free;
+	}
+	/* Get channel-measurement characteristics. */
+	if (css_characteristics_avail && css_chsc_characteristics.scmc
+	    && css_chsc_characteristics.secm) {
+		ret = chsc_get_channel_measurement_chars(chp);
+		if (ret)
+			goto out_free;
+	} else {
+		static int msg_done;
+
+		if (!msg_done) {
+			printk(KERN_WARNING "cio: Channel measurements not "
+			       "available, continuing.\n");
+			msg_done = 1;
+		}
+		chp->cmg = -1;
+	}
+
+	/* make it known to the system */
+	ret = device_register(&chp->dev);
+	if (ret) {
+		printk(KERN_WARNING "%s: could not register %x.%02x\n",
+		       __func__, chpid.cssid, chpid.id);
+		goto out_free;
+	}
+	ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group);
+	if (ret) {
+		device_unregister(&chp->dev);
+		goto out_free;
+	}
+	mutex_lock(&css[chpid.cssid]->mutex);
+	if (css[chpid.cssid]->cm_enabled) {
+		ret = chp_add_cmg_attr(chp);
+		if (ret) {
+			sysfs_remove_group(&chp->dev.kobj, &chp_attr_group);
+			device_unregister(&chp->dev);
+			mutex_unlock(&css[chpid.cssid]->mutex);
+			goto out_free;
+		}
+	}
+	css[chpid.cssid]->chps[chpid.id] = chp;
+	mutex_unlock(&css[chpid.cssid]->mutex);
+	return ret;
+out_free:
+	kfree(chp);
+	return ret;
+}
+
+/**
+ * chp_get_chp_desc - return newly allocated channel-path description
+ * @chpid: channel-path ID
+ *
+ * On success return a newly allocated copy of the channel-path description
+ * data associated with the given channel-path ID. Return %NULL on error.
+ */
+void *chp_get_chp_desc(struct chp_id chpid)
+{
+	struct channel_path *chp;
+	struct channel_path_desc *desc;
+
+	chp = chpid_to_chp(chpid);
+	if (!chp)
+		return NULL;
+	desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL);
+	if (!desc)
+		return NULL;
+	memcpy(desc, &chp->desc, sizeof(struct channel_path_desc));
+	return desc;
+}
+
+/**
+ * chp_process_crw - process channel-path status change
+ * @id: channel-path ID number
+ * @status: non-zero if channel-path has become available, zero otherwise
+ *
+ * Handle channel-report-words indicating that the status of a channel-path
+ * has changed.
+ */
+void chp_process_crw(int id, int status)
+{
+	struct chp_id chpid;
+
+	chp_id_init(&chpid);
+	chpid.id = id;
+	if (status) {
+		if (!chp_is_registered(chpid))
+			chp_new(chpid);
+		chsc_chp_online(chpid);
+	} else
+		chsc_chp_offline(chpid);
+}
+
+static inline int info_bit_num(struct chp_id id)
+{
+	return id.id + id.cssid * (__MAX_CHPID + 1);
+}
+
+/* Force chp_info refresh on next call to info_validate(). */
+static void info_expire(void)
+{
+	mutex_lock(&info_lock);
+	chp_info_expires = jiffies - 1;
+	mutex_unlock(&info_lock);
+}
+
+/* Ensure that chp_info is up-to-date. */
+static int info_update(void)
+{
+	int rc;
+
+	mutex_lock(&info_lock);
+	rc = 0;
+	if (time_after(jiffies, chp_info_expires)) {
+		/* Data is too old, update. */
+		rc = sclp_chp_read_info(&chp_info);
+		chp_info_expires = jiffies + CHP_INFO_UPDATE_INTERVAL ;
+	}
+	mutex_unlock(&info_lock);
+
+	return rc;
+}
+
+/**
+ * chp_info_get_status - retrieve configure status of a channel-path
+ * @chpid: channel-path ID
+ *
+ * On success, return 0 for standby, 1 for configured, 2 for reserved,
+ * 3 for not recognized. Return negative error code on error.
+ */
+int chp_info_get_status(struct chp_id chpid)
+{
+	int rc;
+	int bit;
+
+	rc = info_update();
+	if (rc)
+		return rc;
+
+	bit = info_bit_num(chpid);
+	mutex_lock(&info_lock);
+	if (!chp_test_bit(chp_info.recognized, bit))
+		rc = CHP_STATUS_NOT_RECOGNIZED;
+	else if (chp_test_bit(chp_info.configured, bit))
+		rc = CHP_STATUS_CONFIGURED;
+	else if (chp_test_bit(chp_info.standby, bit))
+		rc = CHP_STATUS_STANDBY;
+	else
+		rc = CHP_STATUS_RESERVED;
+	mutex_unlock(&info_lock);
+
+	return rc;
+}
+
+/* Return configure task for chpid. */
+static enum cfg_task_t cfg_get_task(struct chp_id chpid)
+{
+	return chp_cfg_task[chpid.cssid][chpid.id];
+}
+
+/* Set configure task for chpid. */
+static void cfg_set_task(struct chp_id chpid, enum cfg_task_t cfg)
+{
+	chp_cfg_task[chpid.cssid][chpid.id] = cfg;
+}
+
+/* Perform one configure/deconfigure request. Reschedule work function until
+ * last request. */
+static void cfg_func(struct work_struct *work)
+{
+	struct chp_id chpid;
+	enum cfg_task_t t;
+
+	mutex_lock(&cfg_lock);
+	t = cfg_none;
+	chp_id_for_each(&chpid) {
+		t = cfg_get_task(chpid);
+		if (t != cfg_none) {
+			cfg_set_task(chpid, cfg_none);
+			break;
+		}
+	}
+	mutex_unlock(&cfg_lock);
+
+	switch (t) {
+	case cfg_configure:
+		sclp_chp_configure(chpid);
+		info_expire();
+		chsc_chp_online(chpid);
+		break;
+	case cfg_deconfigure:
+		sclp_chp_deconfigure(chpid);
+		info_expire();
+		chsc_chp_offline(chpid);
+		break;
+	case cfg_none:
+		/* Get updated information after last change. */
+		info_update();
+		mutex_lock(&cfg_lock);
+		cfg_busy = 0;
+		mutex_unlock(&cfg_lock);
+		wake_up_interruptible(&cfg_wait_queue);
+		return;
+	}
+	queue_work(chp_wq, &cfg_work);
+}
+
+/**
+ * chp_cfg_schedule - schedule chpid configuration request
+ * @chpid - channel-path ID
+ * @configure - Non-zero for configure, zero for deconfigure
+ *
+ * Schedule a channel-path configuration/deconfiguration request.
+ */
+void chp_cfg_schedule(struct chp_id chpid, int configure)
+{
+	CIO_MSG_EVENT(2, "chp_cfg_sched%x.%02x=%d\n", chpid.cssid, chpid.id,
+		      configure);
+	mutex_lock(&cfg_lock);
+	cfg_set_task(chpid, configure ? cfg_configure : cfg_deconfigure);
+	cfg_busy = 1;
+	mutex_unlock(&cfg_lock);
+	queue_work(chp_wq, &cfg_work);
+}
+
+/**
+ * chp_cfg_cancel_deconfigure - cancel chpid deconfiguration request
+ * @chpid - channel-path ID
+ *
+ * Cancel an active channel-path deconfiguration request if it has not yet
+ * been performed.
+ */
+void chp_cfg_cancel_deconfigure(struct chp_id chpid)
+{
+	CIO_MSG_EVENT(2, "chp_cfg_cancel:%x.%02x\n", chpid.cssid, chpid.id);
+	mutex_lock(&cfg_lock);
+	if (cfg_get_task(chpid) == cfg_deconfigure)
+		cfg_set_task(chpid, cfg_none);
+	mutex_unlock(&cfg_lock);
+}
+
+static int cfg_wait_idle(void)
+{
+	if (wait_event_interruptible(cfg_wait_queue, !cfg_busy))
+		return -ERESTARTSYS;
+	return 0;
+}
+
+static int __init chp_init(void)
+{
+	struct chp_id chpid;
+
+	chp_wq = create_singlethread_workqueue("cio_chp");
+	if (!chp_wq)
+		return -ENOMEM;
+	INIT_WORK(&cfg_work, cfg_func);
+	init_waitqueue_head(&cfg_wait_queue);
+	if (info_update())
+		return 0;
+	/* Register available channel-paths. */
+	chp_id_for_each(&chpid) {
+		if (chp_info_get_status(chpid) != CHP_STATUS_NOT_RECOGNIZED)
+			chp_new(chpid);
+	}
+
+	return 0;
+}
+
+subsys_initcall(chp_init);
diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h
new file mode 100644
index 0000000..6528656
--- /dev/null
+++ b/drivers/s390/cio/chp.h
@@ -0,0 +1,53 @@
+/*
+ *  drivers/s390/cio/chp.h
+ *
+ *    Copyright IBM Corp. 2007
+ *    Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#ifndef S390_CHP_H
+#define S390_CHP_H S390_CHP_H
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <asm/chpid.h>
+#include "chsc.h"
+
+#define CHP_STATUS_STANDBY		0
+#define CHP_STATUS_CONFIGURED		1
+#define CHP_STATUS_RESERVED		2
+#define CHP_STATUS_NOT_RECOGNIZED	3
+
+static inline int chp_test_bit(u8 *bitmap, int num)
+{
+	int byte = num >> 3;
+	int mask = 128 >> (num & 7);
+
+	return (bitmap[byte] & mask) ? 1 : 0;
+}
+
+
+struct channel_path {
+	struct chp_id chpid;
+	int state;
+	struct channel_path_desc desc;
+	/* Channel-measurement related stuff: */
+	int cmg;
+	int shared;
+	void *cmg_chars;
+	struct device dev;
+};
+
+int chp_get_status(struct chp_id chpid);
+u8 chp_get_sch_opm(struct subchannel *sch);
+int chp_is_registered(struct chp_id chpid);
+void *chp_get_chp_desc(struct chp_id chpid);
+void chp_process_crw(int id, int available);
+void chp_remove_cmg_attr(struct channel_path *chp);
+int chp_add_cmg_attr(struct channel_path *chp);
+int chp_new(struct chp_id chpid);
+void chp_cfg_schedule(struct chp_id chpid, int configure);
+void chp_cfg_cancel_deconfigure(struct chp_id chpid);
+int chp_info_get_status(struct chp_id chpid);
+
+#endif /* S390_CHP_H */
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 6f05a44..ea92ac4 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -15,202 +15,124 @@
 #include <linux/device.h>
 
 #include <asm/cio.h>
+#include <asm/chpid.h>
 
 #include "css.h"
 #include "cio.h"
 #include "cio_debug.h"
 #include "ioasm.h"
+#include "chp.h"
 #include "chsc.h"
 
 static void *sei_page;
 
-static int new_channel_path(int chpid);
+struct chsc_ssd_area {
+	struct chsc_header request;
+	u16 :10;
+	u16 ssid:2;
+	u16 :4;
+	u16 f_sch;	  /* first subchannel */
+	u16 :16;
+	u16 l_sch;	  /* last subchannel */
+	u32 :32;
+	struct chsc_header response;
+	u32 :32;
+	u8 sch_valid : 1;
+	u8 dev_valid : 1;
+	u8 st	     : 3; /* subchannel type */
+	u8 zeroes    : 3;
+	u8  unit_addr;	  /* unit address */
+	u16 devno;	  /* device number */
+	u8 path_mask;
+	u8 fla_valid_mask;
+	u16 sch;	  /* subchannel */
+	u8 chpid[8];	  /* chpids 0-7 */
+	u16 fla[8];	  /* full link addresses 0-7 */
+} __attribute__ ((packed));
 
-static inline void
-set_chp_logically_online(int chp, int onoff)
+int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
 {
-	css[0]->chps[chp]->state = onoff;
-}
+	unsigned long page;
+	struct chsc_ssd_area *ssd_area;
+	int ccode;
+	int ret;
+	int i;
+	int mask;
 
-static int
-get_chp_status(int chp)
-{
-	return (css[0]->chps[chp] ? css[0]->chps[chp]->state : -ENODEV);
-}
-
-void
-chsc_validate_chpids(struct subchannel *sch)
-{
-	int mask, chp;
-
-	for (chp = 0; chp <= 7; chp++) {
-		mask = 0x80 >> chp;
-		if (!get_chp_status(sch->schib.pmcw.chpid[chp]))
-			/* disable using this path */
-			sch->opm &= ~mask;
-	}
-}
-
-void
-chpid_is_actually_online(int chp)
-{
-	int state;
-
-	state = get_chp_status(chp);
-	if (state < 0) {
-		need_rescan = 1;
-		queue_work(slow_path_wq, &slow_path_work);
-	} else
-		WARN_ON(!state);
-}
-
-/* FIXME: this is _always_ called for every subchannel. shouldn't we
- *	  process more than one at a time? */
-static int
-chsc_get_sch_desc_irq(struct subchannel *sch, void *page)
-{
-	int ccode, j;
-
-	struct {
-		struct chsc_header request;
-		u16 reserved1a:10;
-		u16 ssid:2;
-		u16 reserved1b:4;
-		u16 f_sch;	  /* first subchannel */
-		u16 reserved2;
-		u16 l_sch;	  /* last subchannel */
-		u32 reserved3;
-		struct chsc_header response;
-		u32 reserved4;
-		u8 sch_valid : 1;
-		u8 dev_valid : 1;
-		u8 st	     : 3; /* subchannel type */
-		u8 zeroes    : 3;
-		u8  unit_addr;	  /* unit address */
-		u16 devno;	  /* device number */
-		u8 path_mask;
-		u8 fla_valid_mask;
-		u16 sch;	  /* subchannel */
-		u8 chpid[8];	  /* chpids 0-7 */
-		u16 fla[8];	  /* full link addresses 0-7 */
-	} __attribute__ ((packed)) *ssd_area;
-
-	ssd_area = page;
-
+	page = get_zeroed_page(GFP_KERNEL | GFP_DMA);
+	if (!page)
+		return -ENOMEM;
+	ssd_area = (struct chsc_ssd_area *) page;
 	ssd_area->request.length = 0x0010;
 	ssd_area->request.code = 0x0004;
-
-	ssd_area->ssid = sch->schid.ssid;
-	ssd_area->f_sch = sch->schid.sch_no;
-	ssd_area->l_sch = sch->schid.sch_no;
+	ssd_area->ssid = schid.ssid;
+	ssd_area->f_sch = schid.sch_no;
+	ssd_area->l_sch = schid.sch_no;
 
 	ccode = chsc(ssd_area);
+	/* Check response. */
 	if (ccode > 0) {
-		pr_debug("chsc returned with ccode = %d\n", ccode);
-		return (ccode == 3) ? -ENODEV : -EBUSY;
+		ret = (ccode == 3) ? -ENODEV : -EBUSY;
+		goto out_free;
 	}
-
-	switch (ssd_area->response.code) {
-	case 0x0001: /* everything ok */
-		break;
-	case 0x0002:
-		CIO_CRW_EVENT(2, "Invalid command!\n");
-		return -EINVAL;
-	case 0x0003:
-		CIO_CRW_EVENT(2, "Error in chsc request block!\n");
-		return -EINVAL;
-	case 0x0004:
-		CIO_CRW_EVENT(2, "Model does not provide ssd\n");
-		return -EOPNOTSUPP;
-	default:
-		CIO_CRW_EVENT(2, "Unknown CHSC response %d\n",
+	if (ssd_area->response.code != 0x0001) {
+		CIO_MSG_EVENT(2, "chsc: ssd failed for 0.%x.%04x (rc=%04x)\n",
+			      schid.ssid, schid.sch_no,
 			      ssd_area->response.code);
-		return -EIO;
+		ret = -EIO;
+		goto out_free;
 	}
-
-	/*
-	 * ssd_area->st stores the type of the detected
-	 * subchannel, with the following definitions:
-	 *
-	 * 0: I/O subchannel:	  All fields have meaning
-	 * 1: CHSC subchannel:	  Only sch_val, st and sch
-	 *			  have meaning
-	 * 2: Message subchannel: All fields except unit_addr
-	 *			  have meaning
-	 * 3: ADM subchannel:	  Only sch_val, st and sch
-	 *			  have meaning
-	 *
-	 * Other types are currently undefined.
-	 */
-	if (ssd_area->st > 3) { /* uhm, that looks strange... */
-		CIO_CRW_EVENT(0, "Strange subchannel type %d"
-			      " for sch 0.%x.%04x\n", ssd_area->st,
-			      sch->schid.ssid, sch->schid.sch_no);
-		/*
-		 * There may have been a new subchannel type defined in the
-		 * time since this code was written; since we don't know which
-		 * fields have meaning and what to do with it we just jump out
-		 */
-		return 0;
-	} else {
-		const char *type[4] = {"I/O", "chsc", "message", "ADM"};
-		CIO_CRW_EVENT(6, "ssd: sch 0.%x.%04x is %s subchannel\n",
-			      sch->schid.ssid, sch->schid.sch_no,
-			      type[ssd_area->st]);
-
-		sch->ssd_info.valid = 1;
-		sch->ssd_info.type = ssd_area->st;
+	if (!ssd_area->sch_valid) {
+		ret = -ENODEV;
+		goto out_free;
 	}
-
-	if (ssd_area->st == 0 || ssd_area->st == 2) {
-		for (j = 0; j < 8; j++) {
-			if (!((0x80 >> j) & ssd_area->path_mask &
-			      ssd_area->fla_valid_mask))
-				continue;
-			sch->ssd_info.chpid[j] = ssd_area->chpid[j];
-			sch->ssd_info.fla[j]   = ssd_area->fla[j];
+	/* Copy data */
+	ret = 0;
+	memset(ssd, 0, sizeof(struct chsc_ssd_info));
+	if ((ssd_area->st != 0) && (ssd_area->st != 2))
+		goto out_free;
+	ssd->path_mask = ssd_area->path_mask;
+	ssd->fla_valid_mask = ssd_area->fla_valid_mask;
+	for (i = 0; i < 8; i++) {
+		mask = 0x80 >> i;
+		if (ssd_area->path_mask & mask) {
+			chp_id_init(&ssd->chpid[i]);
+			ssd->chpid[i].id = ssd_area->chpid[i];
 		}
+		if (ssd_area->fla_valid_mask & mask)
+			ssd->fla[i] = ssd_area->fla[i];
 	}
+out_free:
+	free_page(page);
+	return ret;
+}
+
+static int check_for_io_on_path(struct subchannel *sch, int mask)
+{
+	int cc;
+
+	cc = stsch(sch->schid, &sch->schib);
+	if (cc)
+		return 0;
+	if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == mask)
+		return 1;
 	return 0;
 }
 
-int
-css_get_ssd_info(struct subchannel *sch)
+static void terminate_internal_io(struct subchannel *sch)
 {
-	int ret;
-	void *page;
-
-	page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
-	if (!page)
-		return -ENOMEM;
-	spin_lock_irq(sch->lock);
-	ret = chsc_get_sch_desc_irq(sch, page);
-	if (ret) {
-		static int cio_chsc_err_msg;
-		
-		if (!cio_chsc_err_msg) {
-			printk(KERN_ERR
-			       "chsc_get_sch_descriptions:"
-			       " Error %d while doing chsc; "
-			       "processing some machine checks may "
-			       "not work\n", ret);
-			cio_chsc_err_msg = 1;
-		}
+	if (cio_clear(sch)) {
+		/* Recheck device in case clear failed. */
+		sch->lpm = 0;
+		if (device_trigger_verify(sch) != 0)
+			css_schedule_eval(sch->schid);
+		return;
 	}
-	spin_unlock_irq(sch->lock);
-	free_page((unsigned long)page);
-	if (!ret) {
-		int j, chpid, mask;
-		/* Allocate channel path structures, if needed. */
-		for (j = 0; j < 8; j++) {
-			mask = 0x80 >> j;
-			chpid = sch->ssd_info.chpid[j];
-			if ((sch->schib.pmcw.pim & mask) &&
-			    (get_chp_status(chpid) < 0))
-			    new_channel_path(chpid);
-		}
-	}
-	return ret;
+	/* Request retry of internal operation. */
+	device_set_intretry(sch);
+	/* Call handler. */
+	if (sch->driver && sch->driver->termination)
+		sch->driver->termination(&sch->dev);
 }
 
 static int
@@ -219,7 +141,7 @@
 	int j;
 	int mask;
 	struct subchannel *sch;
-	struct channel_path *chpid;
+	struct chp_id *chpid;
 	struct schib schib;
 
 	sch = to_subchannel(dev);
@@ -243,106 +165,50 @@
 	if (sch->schib.pmcw.pim == 0x80)
 		goto out_unreg;
 
-	if ((sch->schib.scsw.actl & SCSW_ACTL_DEVACT) &&
-	    (sch->schib.scsw.actl & SCSW_ACTL_SCHACT) &&
-	    (sch->schib.pmcw.lpum == mask)) {
-		int cc;
-
-		cc = cio_clear(sch);
-		if (cc == -ENODEV)
+	if (check_for_io_on_path(sch, mask)) {
+		if (device_is_online(sch))
+			device_kill_io(sch);
+		else {
+			terminate_internal_io(sch);
+			/* Re-start path verification. */
+			if (sch->driver && sch->driver->verify)
+				sch->driver->verify(&sch->dev);
+		}
+	} else {
+		/* trigger path verification. */
+		if (sch->driver && sch->driver->verify)
+			sch->driver->verify(&sch->dev);
+		else if (sch->lpm == mask)
 			goto out_unreg;
-		/* Request retry of internal operation. */
-		device_set_intretry(sch);
-		/* Call handler. */
-		if (sch->driver && sch->driver->termination)
-			sch->driver->termination(&sch->dev);
-		goto out_unlock;
 	}
 
-	/* trigger path verification. */
-	if (sch->driver && sch->driver->verify)
-		sch->driver->verify(&sch->dev);
-	else if (sch->lpm == mask)
-		goto out_unreg;
-out_unlock:
 	spin_unlock_irq(sch->lock);
 	return 0;
+
 out_unreg:
-	spin_unlock_irq(sch->lock);
 	sch->lpm = 0;
-	if (css_enqueue_subchannel_slow(sch->schid)) {
-		css_clear_subchannel_slow_list();
-		need_rescan = 1;
-	}
+	spin_unlock_irq(sch->lock);
+	css_schedule_eval(sch->schid);
 	return 0;
 }
 
-static void
-s390_set_chpid_offline( __u8 chpid)
+void chsc_chp_offline(struct chp_id chpid)
 {
 	char dbf_txt[15];
-	struct device *dev;
 
-	sprintf(dbf_txt, "chpr%x", chpid);
+	sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id);
 	CIO_TRACE_EVENT(2, dbf_txt);
 
-	if (get_chp_status(chpid) <= 0)
+	if (chp_get_status(chpid) <= 0)
 		return;
-	dev = get_device(&css[0]->chps[chpid]->dev);
-	bus_for_each_dev(&css_bus_type, NULL, to_channelpath(dev),
+	bus_for_each_dev(&css_bus_type, NULL, &chpid,
 			 s390_subchannel_remove_chpid);
-
-	if (need_rescan || css_slow_subchannels_exist())
-		queue_work(slow_path_wq, &slow_path_work);
-	put_device(dev);
-}
-
-struct res_acc_data {
-	struct channel_path *chp;
-	u32 fla_mask;
-	u16 fla;
-};
-
-static int
-s390_process_res_acc_sch(struct res_acc_data *res_data, struct subchannel *sch)
-{
-	int found;
-	int chp;
-	int ccode;
-	
-	found = 0;
-	for (chp = 0; chp <= 7; chp++)
-		/*
-		 * check if chpid is in information updated by ssd
-		 */
-		if (sch->ssd_info.valid &&
-		    sch->ssd_info.chpid[chp] == res_data->chp->id &&
-		    (sch->ssd_info.fla[chp] & res_data->fla_mask)
-		    == res_data->fla) {
-			found = 1;
-			break;
-		}
-	
-	if (found == 0)
-		return 0;
-
-	/*
-	 * Do a stsch to update our subchannel structure with the
-	 * new path information and eventually check for logically
-	 * offline chpids.
-	 */
-	ccode = stsch(sch->schid, &sch->schib);
-	if (ccode > 0)
-		return 0;
-
-	return 0x80 >> chp;
 }
 
 static int
 s390_process_res_acc_new_sch(struct subchannel_id schid)
 {
 	struct schib schib;
-	int ret;
 	/*
 	 * We don't know the device yet, but since a path
 	 * may be available now to the device we'll have
@@ -353,14 +219,35 @@
 	 */
 	if (stsch_err(schid, &schib))
 		/* We're through */
-		return need_rescan ? -EAGAIN : -ENXIO;
+		return -ENXIO;
 
 	/* Put it on the slow path. */
-	ret = css_enqueue_subchannel_slow(schid);
-	if (ret) {
-		css_clear_subchannel_slow_list();
-		need_rescan = 1;
-		return -EAGAIN;
+	css_schedule_eval(schid);
+	return 0;
+}
+
+struct res_acc_data {
+	struct chp_id chpid;
+	u32 fla_mask;
+	u16 fla;
+};
+
+static int get_res_chpid_mask(struct chsc_ssd_info *ssd,
+			      struct res_acc_data *data)
+{
+	int i;
+	int mask;
+
+	for (i = 0; i < 8; i++) {
+		mask = 0x80 >> i;
+		if (!(ssd->path_mask & mask))
+			continue;
+		if (!chp_id_is_equal(&ssd->chpid[i], &data->chpid))
+			continue;
+		if ((ssd->fla_valid_mask & mask) &&
+		    ((ssd->fla[i] & data->fla_mask) != data->fla))
+			continue;
+		return mask;
 	}
 	return 0;
 }
@@ -379,14 +266,11 @@
 		return s390_process_res_acc_new_sch(schid);
 
 	spin_lock_irq(sch->lock);
-
-	chp_mask = s390_process_res_acc_sch(res_data, sch);
-
-	if (chp_mask == 0) {
-		spin_unlock_irq(sch->lock);
-		put_device(&sch->dev);
-		return 0;
-	}
+	chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data);
+	if (chp_mask == 0)
+		goto out;
+	if (stsch(sch->schid, &sch->schib))
+		goto out;
 	old_lpm = sch->lpm;
 	sch->lpm = ((sch->schib.pmcw.pim &
 		     sch->schib.pmcw.pam &
@@ -396,20 +280,18 @@
 		device_trigger_reprobe(sch);
 	else if (sch->driver && sch->driver->verify)
 		sch->driver->verify(&sch->dev);
-
+out:
 	spin_unlock_irq(sch->lock);
 	put_device(&sch->dev);
 	return 0;
 }
 
-
-static int
-s390_process_res_acc (struct res_acc_data *res_data)
+static void s390_process_res_acc (struct res_acc_data *res_data)
 {
-	int rc;
 	char dbf_txt[15];
 
-	sprintf(dbf_txt, "accpr%x", res_data->chp->id);
+	sprintf(dbf_txt, "accpr%x.%02x", res_data->chpid.cssid,
+		res_data->chpid.id);
 	CIO_TRACE_EVENT( 2, dbf_txt);
 	if (res_data->fla != 0) {
 		sprintf(dbf_txt, "fla%x", res_data->fla);
@@ -423,12 +305,7 @@
 	 * The more information we have (info), the less scanning
 	 * will we have to do.
 	 */
-	rc = for_each_subchannel(__s390_process_res_acc, res_data);
-	if (css_slow_subchannels_exist())
-		rc = -EAGAIN;
-	else if (rc != -EAGAIN)
-		rc = 0;
-	return rc;
+	for_each_subchannel(__s390_process_res_acc, res_data);
 }
 
 static int
@@ -480,43 +357,45 @@
 	/* ccdf has to be big enough for a link-incident record */
 } __attribute__ ((packed));
 
-static int chsc_process_sei_link_incident(struct chsc_sei_area *sei_area)
+static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area)
 {
-	int chpid;
+	struct chp_id chpid;
+	int id;
 
 	CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n",
 		      sei_area->rs, sei_area->rsid);
 	if (sei_area->rs != 4)
-		return 0;
-	chpid = __get_chpid_from_lir(sei_area->ccdf);
-	if (chpid < 0)
+		return;
+	id = __get_chpid_from_lir(sei_area->ccdf);
+	if (id < 0)
 		CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n");
-	else
-		s390_set_chpid_offline(chpid);
-
-	return 0;
+	else {
+		chp_id_init(&chpid);
+		chpid.id = id;
+		chsc_chp_offline(chpid);
+	}
 }
 
-static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
+static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
 {
 	struct res_acc_data res_data;
-	struct device *dev;
+	struct chp_id chpid;
 	int status;
-	int rc;
 
 	CIO_CRW_EVENT(4, "chsc: resource accessibility event (rs=%02x, "
 		      "rs_id=%04x)\n", sei_area->rs, sei_area->rsid);
 	if (sei_area->rs != 4)
-		return 0;
+		return;
+	chp_id_init(&chpid);
+	chpid.id = sei_area->rsid;
 	/* allocate a new channel path structure, if needed */
-	status = get_chp_status(sei_area->rsid);
+	status = chp_get_status(chpid);
 	if (status < 0)
-		new_channel_path(sei_area->rsid);
+		chp_new(chpid);
 	else if (!status)
-		return 0;
-	dev = get_device(&css[0]->chps[sei_area->rsid]->dev);
+		return;
 	memset(&res_data, 0, sizeof(struct res_acc_data));
-	res_data.chp = to_channelpath(dev);
+	res_data.chpid = chpid;
 	if ((sei_area->vf & 0xc0) != 0) {
 		res_data.fla = sei_area->fla;
 		if ((sei_area->vf & 0xc0) == 0xc0)
@@ -526,51 +405,82 @@
 			/* link address */
 			res_data.fla_mask = 0xff00;
 	}
-	rc = s390_process_res_acc(&res_data);
-	put_device(dev);
-
-	return rc;
+	s390_process_res_acc(&res_data);
 }
 
-static int chsc_process_sei(struct chsc_sei_area *sei_area)
-{
-	int rc;
+struct chp_config_data {
+	u8 map[32];
+	u8 op;
+	u8 pc;
+};
 
+static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
+{
+	struct chp_config_data *data;
+	struct chp_id chpid;
+	int num;
+
+	CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n");
+	if (sei_area->rs != 0)
+		return;
+	data = (struct chp_config_data *) &(sei_area->ccdf);
+	chp_id_init(&chpid);
+	for (num = 0; num <= __MAX_CHPID; num++) {
+		if (!chp_test_bit(data->map, num))
+			continue;
+		chpid.id = num;
+		printk(KERN_WARNING "cio: processing configure event %d for "
+		       "chpid %x.%02x\n", data->op, chpid.cssid, chpid.id);
+		switch (data->op) {
+		case 0:
+			chp_cfg_schedule(chpid, 1);
+			break;
+		case 1:
+			chp_cfg_schedule(chpid, 0);
+			break;
+		case 2:
+			chp_cfg_cancel_deconfigure(chpid);
+			break;
+		}
+	}
+}
+
+static void chsc_process_sei(struct chsc_sei_area *sei_area)
+{
 	/* Check if we might have lost some information. */
-	if (sei_area->flags & 0x40)
+	if (sei_area->flags & 0x40) {
 		CIO_CRW_EVENT(2, "chsc: event overflow\n");
+		css_schedule_eval_all();
+	}
 	/* which kind of information was stored? */
-	rc = 0;
 	switch (sei_area->cc) {
 	case 1: /* link incident*/
-		rc = chsc_process_sei_link_incident(sei_area);
+		chsc_process_sei_link_incident(sei_area);
 		break;
 	case 2: /* i/o resource accessibiliy */
-		rc = chsc_process_sei_res_acc(sei_area);
+		chsc_process_sei_res_acc(sei_area);
+		break;
+	case 8: /* channel-path-configuration notification */
+		chsc_process_sei_chp_config(sei_area);
 		break;
 	default: /* other stuff */
 		CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n",
 			      sei_area->cc);
 		break;
 	}
-
-	return rc;
 }
 
-int chsc_process_crw(void)
+void chsc_process_crw(void)
 {
 	struct chsc_sei_area *sei_area;
-	int ret;
-	int rc;
 
 	if (!sei_page)
-		return 0;
+		return;
 	/* Access to sei_page is serialized through machine check handler
 	 * thread, so no need for locking. */
 	sei_area = sei_page;
 
 	CIO_TRACE_EVENT( 2, "prcss");
-	ret = 0;
 	do {
 		memset(sei_area, 0, sizeof(*sei_area));
 		sei_area->request.length = 0x0010;
@@ -580,37 +490,26 @@
 
 		if (sei_area->response.code == 0x0001) {
 			CIO_CRW_EVENT(4, "chsc: sei successful\n");
-			rc = chsc_process_sei(sei_area);
-			if (rc)
-				ret = rc;
+			chsc_process_sei(sei_area);
 		} else {
 			CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n",
 				      sei_area->response.code);
-			ret = 0;
 			break;
 		}
 	} while (sei_area->flags & 0x80);
-
-	return ret;
 }
 
 static int
 __chp_add_new_sch(struct subchannel_id schid)
 {
 	struct schib schib;
-	int ret;
 
 	if (stsch_err(schid, &schib))
 		/* We're through */
-		return need_rescan ? -EAGAIN : -ENXIO;
+		return -ENXIO;
 
 	/* Put it on the slow path. */
-	ret = css_enqueue_subchannel_slow(schid);
-	if (ret) {
-		css_clear_subchannel_slow_list();
-		need_rescan = 1;
-		return -EAGAIN;
-	}
+	css_schedule_eval(schid);
 	return 0;
 }
 
@@ -619,10 +518,10 @@
 __chp_add(struct subchannel_id schid, void *data)
 {
 	int i, mask;
-	struct channel_path *chp;
+	struct chp_id *chpid;
 	struct subchannel *sch;
 
-	chp = data;
+	chpid = data;
 	sch = get_subchannel_by_schid(schid);
 	if (!sch)
 		/* Check if the subchannel is now available. */
@@ -631,7 +530,7 @@
 	for (i=0; i<8; i++) {
 		mask = 0x80 >> i;
 		if ((sch->schib.pmcw.pim & mask) &&
-		    (sch->schib.pmcw.chpid[i] == chp->id)) {
+		    (sch->schib.pmcw.chpid[i] == chpid->id)) {
 			if (stsch(sch->schid, &sch->schib) != 0) {
 				/* Endgame. */
 				spin_unlock_irq(sch->lock);
@@ -657,122 +556,58 @@
 	return 0;
 }
 
-static int
-chp_add(int chpid)
+void chsc_chp_online(struct chp_id chpid)
 {
-	int rc;
 	char dbf_txt[15];
-	struct device *dev;
 
-	if (!get_chp_status(chpid))
-		return 0; /* no need to do the rest */
-	
-	sprintf(dbf_txt, "cadd%x", chpid);
+	sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id);
 	CIO_TRACE_EVENT(2, dbf_txt);
 
-	dev = get_device(&css[0]->chps[chpid]->dev);
-	rc = for_each_subchannel(__chp_add, to_channelpath(dev));
-	if (css_slow_subchannels_exist())
-		rc = -EAGAIN;
-	if (rc != -EAGAIN)
-		rc = 0;
-	put_device(dev);
-	return rc;
+	if (chp_get_status(chpid) != 0)
+		for_each_subchannel(__chp_add, &chpid);
 }
 
-/* 
- * Handling of crw machine checks with channel path source.
- */
-int
-chp_process_crw(int chpid, int on)
-{
-	if (on == 0) {
-		/* Path has gone. We use the link incident routine.*/
-		s390_set_chpid_offline(chpid);
-		return 0; /* De-register is async anyway. */
-	}
-	/*
-	 * Path has come. Allocate a new channel path structure,
-	 * if needed.
-	 */
-	if (get_chp_status(chpid) < 0)
-		new_channel_path(chpid);
-	/* Avoid the extra overhead in process_rec_acc. */
-	return chp_add(chpid);
-}
-
-static int check_for_io_on_path(struct subchannel *sch, int index)
-{
-	int cc;
-
-	cc = stsch(sch->schid, &sch->schib);
-	if (cc)
-		return 0;
-	if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == (0x80 >> index))
-		return 1;
-	return 0;
-}
-
-static void terminate_internal_io(struct subchannel *sch)
-{
-	if (cio_clear(sch)) {
-		/* Recheck device in case clear failed. */
-		sch->lpm = 0;
-		if (device_trigger_verify(sch) != 0) {
-			if(css_enqueue_subchannel_slow(sch->schid)) {
-				css_clear_subchannel_slow_list();
-				need_rescan = 1;
-			}
-		}
-		return;
-	}
-	/* Request retry of internal operation. */
-	device_set_intretry(sch);
-	/* Call handler. */
-	if (sch->driver && sch->driver->termination)
-		sch->driver->termination(&sch->dev);
-}
-
-static void
-__s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on)
+static void __s390_subchannel_vary_chpid(struct subchannel *sch,
+					 struct chp_id chpid, int on)
 {
 	int chp, old_lpm;
+	int mask;
 	unsigned long flags;
 
-	if (!sch->ssd_info.valid)
-		return;
-	
 	spin_lock_irqsave(sch->lock, flags);
 	old_lpm = sch->lpm;
 	for (chp = 0; chp < 8; chp++) {
-		if (sch->ssd_info.chpid[chp] != chpid)
+		mask = 0x80 >> chp;
+		if (!(sch->ssd_info.path_mask & mask))
+			continue;
+		if (!chp_id_is_equal(&sch->ssd_info.chpid[chp], &chpid))
 			continue;
 
 		if (on) {
-			sch->opm |= (0x80 >> chp);
-			sch->lpm |= (0x80 >> chp);
+			sch->opm |= mask;
+			sch->lpm |= mask;
 			if (!old_lpm)
 				device_trigger_reprobe(sch);
 			else if (sch->driver && sch->driver->verify)
 				sch->driver->verify(&sch->dev);
 			break;
 		}
-		sch->opm &= ~(0x80 >> chp);
-		sch->lpm &= ~(0x80 >> chp);
-		if (check_for_io_on_path(sch, chp)) {
+		sch->opm &= ~mask;
+		sch->lpm &= ~mask;
+		if (check_for_io_on_path(sch, mask)) {
 			if (device_is_online(sch))
 				/* Path verification is done after killing. */
 				device_kill_io(sch);
-			else
+			else {
 				/* Kill and retry internal I/O. */
 				terminate_internal_io(sch);
-		} else if (!sch->lpm) {
-			if (device_trigger_verify(sch) != 0) {
-				if (css_enqueue_subchannel_slow(sch->schid)) {
-					css_clear_subchannel_slow_list();
-					need_rescan = 1;
-				}
+				/* Re-start path verification. */
+				if (sch->driver && sch->driver->verify)
+					sch->driver->verify(&sch->dev);
 			}
+		} else if (!sch->lpm) {
+			if (device_trigger_verify(sch) != 0)
+				css_schedule_eval(sch->schid);
 		} else if (sch->driver && sch->driver->verify)
 			sch->driver->verify(&sch->dev);
 		break;
@@ -780,11 +615,10 @@
 	spin_unlock_irqrestore(sch->lock, flags);
 }
 
-static int
-s390_subchannel_vary_chpid_off(struct device *dev, void *data)
+static int s390_subchannel_vary_chpid_off(struct device *dev, void *data)
 {
 	struct subchannel *sch;
-	__u8 *chpid;
+	struct chp_id *chpid;
 
 	sch = to_subchannel(dev);
 	chpid = data;
@@ -793,11 +627,10 @@
 	return 0;
 }
 
-static int
-s390_subchannel_vary_chpid_on(struct device *dev, void *data)
+static int s390_subchannel_vary_chpid_on(struct device *dev, void *data)
 {
 	struct subchannel *sch;
-	__u8 *chpid;
+	struct chp_id *chpid;
 
 	sch = to_subchannel(dev);
 	chpid = data;
@@ -821,40 +654,17 @@
 		/* We're through */
 		return -ENXIO;
 	/* Put it on the slow path. */
-	if (css_enqueue_subchannel_slow(schid)) {
-		css_clear_subchannel_slow_list();
-		need_rescan = 1;
-		return -EAGAIN;
-	}
+	css_schedule_eval(schid);
 	return 0;
 }
 
-/*
- * Function: s390_vary_chpid
- * Varies the specified chpid online or offline
+/**
+ * chsc_chp_vary - propagate channel-path vary operation to subchannels
+ * @chpid: channl-path ID
+ * @on: non-zero for vary online, zero for vary offline
  */
-static int
-s390_vary_chpid( __u8 chpid, int on)
+int chsc_chp_vary(struct chp_id chpid, int on)
 {
-	char dbf_text[15];
-	int status;
-
-	sprintf(dbf_text, on?"varyon%x":"varyoff%x", chpid);
-	CIO_TRACE_EVENT( 2, dbf_text);
-
-	status = get_chp_status(chpid);
-	if (status < 0) {
-		printk(KERN_ERR "Can't vary unknown chpid %02X\n", chpid);
-		return -EINVAL;
-	}
-
-	if (!on && !status) {
-		printk(KERN_ERR "chpid %x is already offline\n", chpid);
-		return -EINVAL;
-	}
-
-	set_chp_logically_online(chpid, on);
-
 	/*
 	 * Redo PathVerification on the devices the chpid connects to
 	 */
@@ -865,118 +675,9 @@
 	if (on)
 		/* Scan for new devices on varied on path. */
 		for_each_subchannel(__s390_vary_chpid_on, NULL);
-	if (need_rescan || css_slow_subchannels_exist())
-		queue_work(slow_path_wq, &slow_path_work);
 	return 0;
 }
 
-/*
- * Channel measurement related functions
- */
-static ssize_t
-chp_measurement_chars_read(struct kobject *kobj, char *buf, loff_t off,
-			   size_t count)
-{
-	struct channel_path *chp;
-	unsigned int size;
-
-	chp = to_channelpath(container_of(kobj, struct device, kobj));
-	if (!chp->cmg_chars)
-		return 0;
-
-	size = sizeof(struct cmg_chars);
-
-	if (off > size)
-		return 0;
-	if (off + count > size)
-		count = size - off;
-	memcpy(buf, chp->cmg_chars + off, count);
-	return count;
-}
-
-static struct bin_attribute chp_measurement_chars_attr = {
-	.attr = {
-		.name = "measurement_chars",
-		.mode = S_IRUSR,
-		.owner = THIS_MODULE,
-	},
-	.size = sizeof(struct cmg_chars),
-	.read = chp_measurement_chars_read,
-};
-
-static void
-chp_measurement_copy_block(struct cmg_entry *buf,
-			   struct channel_subsystem *css, int chpid)
-{
-	void *area;
-	struct cmg_entry *entry, reference_buf;
-	int idx;
-
-	if (chpid < 128) {
-		area = css->cub_addr1;
-		idx = chpid;
-	} else {
-		area = css->cub_addr2;
-		idx = chpid - 128;
-	}
-	entry = area + (idx * sizeof(struct cmg_entry));
-	do {
-		memcpy(buf, entry, sizeof(*entry));
-		memcpy(&reference_buf, entry, sizeof(*entry));
-	} while (reference_buf.values[0] != buf->values[0]);
-}
-
-static ssize_t
-chp_measurement_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
-{
-	struct channel_path *chp;
-	struct channel_subsystem *css;
-	unsigned int size;
-
-	chp = to_channelpath(container_of(kobj, struct device, kobj));
-	css = to_css(chp->dev.parent);
-
-	size = sizeof(struct cmg_entry);
-
-	/* Only allow single reads. */
-	if (off || count < size)
-		return 0;
-	chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->id);
-	count = size;
-	return count;
-}
-
-static struct bin_attribute chp_measurement_attr = {
-	.attr = {
-		.name = "measurement",
-		.mode = S_IRUSR,
-		.owner = THIS_MODULE,
-	},
-	.size = sizeof(struct cmg_entry),
-	.read = chp_measurement_read,
-};
-
-static void
-chsc_remove_chp_cmg_attr(struct channel_path *chp)
-{
-	device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr);
-	device_remove_bin_file(&chp->dev, &chp_measurement_attr);
-}
-
-static int
-chsc_add_chp_cmg_attr(struct channel_path *chp)
-{
-	int ret;
-
-	ret = device_create_bin_file(&chp->dev, &chp_measurement_chars_attr);
-	if (ret)
-		return ret;
-	ret = device_create_bin_file(&chp->dev, &chp_measurement_attr);
-	if (ret)
-		device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr);
-	return ret;
-}
-
 static void
 chsc_remove_cmg_attr(struct channel_subsystem *css)
 {
@@ -985,7 +686,7 @@
 	for (i = 0; i <= __MAX_CHPID; i++) {
 		if (!css->chps[i])
 			continue;
-		chsc_remove_chp_cmg_attr(css->chps[i]);
+		chp_remove_cmg_attr(css->chps[i]);
 	}
 }
 
@@ -998,7 +699,7 @@
 	for (i = 0; i <= __MAX_CHPID; i++) {
 		if (!css->chps[i])
 			continue;
-		ret = chsc_add_chp_cmg_attr(css->chps[i]);
+		ret = chp_add_cmg_attr(css->chps[i]);
 		if (ret)
 			goto cleanup;
 	}
@@ -1007,12 +708,11 @@
 	for (--i; i >= 0; i--) {
 		if (!css->chps[i])
 			continue;
-		chsc_remove_chp_cmg_attr(css->chps[i]);
+		chp_remove_cmg_attr(css->chps[i]);
 	}
 	return ret;
 }
 
-
 static int
 __chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
 {
@@ -1118,7 +818,7 @@
 		} else
 			chsc_remove_cmg_attr(css);
 	}
-	if (enable && !css->cm_enabled) {
+	if (!css->cm_enabled) {
 		free_page((unsigned long)css->cub_addr1);
 		free_page((unsigned long)css->cub_addr2);
 	}
@@ -1127,109 +827,8 @@
 	return ret;
 }
 
-/*
- * Files for the channel path entries.
- */
-static ssize_t
-chp_status_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
-	struct channel_path *chp = container_of(dev, struct channel_path, dev);
-
-	if (!chp)
-		return 0;
-	return (get_chp_status(chp->id) ? sprintf(buf, "online\n") :
-		sprintf(buf, "offline\n"));
-}
-
-static ssize_t
-chp_status_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
-	struct channel_path *cp = container_of(dev, struct channel_path, dev);
-	char cmd[10];
-	int num_args;
-	int error;
-
-	num_args = sscanf(buf, "%5s", cmd);
-	if (!num_args)
-		return count;
-
-	if (!strnicmp(cmd, "on", 2))
-		error = s390_vary_chpid(cp->id, 1);
-	else if (!strnicmp(cmd, "off", 3))
-		error = s390_vary_chpid(cp->id, 0);
-	else
-		error = -EINVAL;
-
-	return error < 0 ? error : count;
-
-}
-
-static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write);
-
-static ssize_t
-chp_type_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
-	struct channel_path *chp = container_of(dev, struct channel_path, dev);
-
-	if (!chp)
-		return 0;
-	return sprintf(buf, "%x\n", chp->desc.desc);
-}
-
-static DEVICE_ATTR(type, 0444, chp_type_show, NULL);
-
-static ssize_t
-chp_cmg_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
-	struct channel_path *chp = to_channelpath(dev);
-
-	if (!chp)
-		return 0;
-	if (chp->cmg == -1) /* channel measurements not available */
-		return sprintf(buf, "unknown\n");
-	return sprintf(buf, "%x\n", chp->cmg);
-}
-
-static DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL);
-
-static ssize_t
-chp_shared_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
-	struct channel_path *chp = to_channelpath(dev);
-
-	if (!chp)
-		return 0;
-	if (chp->shared == -1) /* channel measurements not available */
-		return sprintf(buf, "unknown\n");
-	return sprintf(buf, "%x\n", chp->shared);
-}
-
-static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL);
-
-static struct attribute * chp_attrs[] = {
-	&dev_attr_status.attr,
-	&dev_attr_type.attr,
-	&dev_attr_cmg.attr,
-	&dev_attr_shared.attr,
-	NULL,
-};
-
-static struct attribute_group chp_attr_group = {
-	.attrs = chp_attrs,
-};
-
-static void
-chp_release(struct device *dev)
-{
-	struct channel_path *cp;
-	
-	cp = container_of(dev, struct channel_path, dev);
-	kfree(cp);
-}
-
-static int
-chsc_determine_channel_path_description(int chpid,
-					struct channel_path_desc *desc)
+int chsc_determine_channel_path_description(struct chp_id chpid,
+					    struct channel_path_desc *desc)
 {
 	int ccode, ret;
 
@@ -1252,8 +851,8 @@
 	scpd_area->request.length = 0x0010;
 	scpd_area->request.code = 0x0002;
 
-	scpd_area->first_chpid = chpid;
-	scpd_area->last_chpid = chpid;
+	scpd_area->first_chpid = chpid.id;
+	scpd_area->last_chpid = chpid.id;
 
 	ccode = chsc(scpd_area);
 	if (ccode > 0) {
@@ -1316,8 +915,7 @@
 	}
 }
 
-static int
-chsc_get_channel_measurement_chars(struct channel_path *chp)
+int chsc_get_channel_measurement_chars(struct channel_path *chp)
 {
 	int ccode, ret;
 
@@ -1349,8 +947,8 @@
 	scmc_area->request.length = 0x0010;
 	scmc_area->request.code = 0x0022;
 
-	scmc_area->first_chpid = chp->id;
-	scmc_area->last_chpid = chp->id;
+	scmc_area->first_chpid = chp->chpid.id;
+	scmc_area->last_chpid = chp->chpid.id;
 
 	ccode = chsc(scmc_area);
 	if (ccode > 0) {
@@ -1392,94 +990,6 @@
 	return ret;
 }
 
-/*
- * Entries for chpids on the system bus.
- * This replaces /proc/chpids.
- */
-static int
-new_channel_path(int chpid)
-{
-	struct channel_path *chp;
-	int ret;
-
-	chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL);
-	if (!chp)
-		return -ENOMEM;
-
-	/* fill in status, etc. */
-	chp->id = chpid;
-	chp->state = 1;
-	chp->dev.parent = &css[0]->device;
-	chp->dev.release = chp_release;
-	snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp0.%x", chpid);
-
-	/* Obtain channel path description and fill it in. */
-	ret = chsc_determine_channel_path_description(chpid, &chp->desc);
-	if (ret)
-		goto out_free;
-	/* Get channel-measurement characteristics. */
-	if (css_characteristics_avail && css_chsc_characteristics.scmc
-	    && css_chsc_characteristics.secm) {
-		ret = chsc_get_channel_measurement_chars(chp);
-		if (ret)
-			goto out_free;
-	} else {
-		static int msg_done;
-
-		if (!msg_done) {
-			printk(KERN_WARNING "cio: Channel measurements not "
-			       "available, continuing.\n");
-			msg_done = 1;
-		}
-		chp->cmg = -1;
-	}
-
-	/* make it known to the system */
-	ret = device_register(&chp->dev);
-	if (ret) {
-		printk(KERN_WARNING "%s: could not register %02x\n",
-		       __func__, chpid);
-		goto out_free;
-	}
-	ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group);
-	if (ret) {
-		device_unregister(&chp->dev);
-		goto out_free;
-	}
-	mutex_lock(&css[0]->mutex);
-	if (css[0]->cm_enabled) {
-		ret = chsc_add_chp_cmg_attr(chp);
-		if (ret) {
-			sysfs_remove_group(&chp->dev.kobj, &chp_attr_group);
-			device_unregister(&chp->dev);
-			mutex_unlock(&css[0]->mutex);
-			goto out_free;
-		}
-	}
-	css[0]->chps[chpid] = chp;
-	mutex_unlock(&css[0]->mutex);
-	return ret;
-out_free:
-	kfree(chp);
-	return ret;
-}
-
-void *
-chsc_get_chp_desc(struct subchannel *sch, int chp_no)
-{
-	struct channel_path *chp;
-	struct channel_path_desc *desc;
-
-	chp = css[0]->chps[sch->schib.pmcw.chpid[chp_no]];
-	if (!chp)
-		return NULL;
-	desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL);
-	if (!desc)
-		return NULL;
-	memcpy(desc, &chp->desc, sizeof(struct channel_path_desc));
-	return desc;
-}
-
 static int __init
 chsc_alloc_sei_area(void)
 {
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
index 0fb2b02..2ad81d1 100644
--- a/drivers/s390/cio/chsc.h
+++ b/drivers/s390/cio/chsc.h
@@ -1,9 +1,10 @@
 #ifndef S390_CHSC_H
 #define S390_CHSC_H
 
-#define CHSC_SEI_ACC_CHPID        1
-#define CHSC_SEI_ACC_LINKADDR     2
-#define CHSC_SEI_ACC_FULLLINKADDR 3
+#include <linux/types.h>
+#include <linux/device.h>
+#include <asm/chpid.h>
+#include "schid.h"
 
 #define CHSC_SDA_OC_MSS   0x2
 
@@ -33,23 +34,9 @@
 	u8 chpp;
 } __attribute__ ((packed));
 
-struct channel_path {
-	int id;
-	int state;
-	struct channel_path_desc desc;
-	/* Channel-measurement related stuff: */
-	int cmg;
-	int shared;
-	void *cmg_chars;
-	struct device dev;
-};
+struct channel_path;
 
-extern void s390_process_css( void );
-extern void chsc_validate_chpids(struct subchannel *);
-extern void chpid_is_actually_online(int);
-extern int css_get_ssd_info(struct subchannel *);
-extern int chsc_process_crw(void);
-extern int chp_process_crw(int, int);
+extern void chsc_process_crw(void);
 
 struct css_general_char {
 	u64 : 41;
@@ -82,15 +69,26 @@
 extern struct css_general_char css_general_characteristics;
 extern struct css_chsc_char css_chsc_characteristics;
 
+struct chsc_ssd_info {
+	u8 path_mask;
+	u8 fla_valid_mask;
+	struct chp_id chpid[8];
+	u16 fla[8];
+};
+extern int chsc_get_ssd_info(struct subchannel_id schid,
+			     struct chsc_ssd_info *ssd);
 extern int chsc_determine_css_characteristics(void);
 extern int css_characteristics_avail;
 
-extern void *chsc_get_chp_desc(struct subchannel*, int);
-
 extern int chsc_enable_facility(int);
 struct channel_subsystem;
 extern int chsc_secm(struct channel_subsystem *, int);
 
-#define to_channelpath(device) container_of(device, struct channel_path, dev)
+int chsc_chp_vary(struct chp_id chpid, int on);
+int chsc_determine_channel_path_description(struct chp_id chpid,
+					    struct channel_path_desc *desc);
+void chsc_chp_online(struct chp_id chpid);
+void chsc_chp_offline(struct chp_id chpid);
+int chsc_get_channel_measurement_chars(struct channel_path *chp);
 
 #endif
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 9cb129a..ea1defb 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -22,6 +22,7 @@
 #include <asm/setup.h>
 #include <asm/reset.h>
 #include <asm/ipl.h>
+#include <asm/chpid.h>
 #include "airq.h"
 #include "cio.h"
 #include "css.h"
@@ -29,6 +30,7 @@
 #include "ioasm.h"
 #include "blacklist.h"
 #include "cio_debug.h"
+#include "chp.h"
 #include "../s390mach.h"
 
 debug_info_t *cio_debug_msg_id;
@@ -592,9 +594,10 @@
 		err = -ENODEV;
 		goto out;
 	}
-	sch->opm = 0xff;
-	if (!cio_is_console(sch->schid))
-		chsc_validate_chpids(sch);
+	if (cio_is_console(sch->schid))
+		sch->opm = 0xff;
+	else
+		sch->opm = chp_get_sch_opm(sch);
 	sch->lpm = sch->schib.pmcw.pam & sch->opm;
 
 	CIO_DEBUG(KERN_INFO, 0,
@@ -954,6 +957,7 @@
 {
 	int i, ret;
 	unsigned long long timeout;
+	struct chp_id chpid;
 
 	/* Reset subchannels. */
 	for_each_subchannel(__shutdown_subchannel_easy,  NULL);
@@ -963,8 +967,10 @@
 	__ctl_set_bit(14, 28);
 	/* Temporarily reenable machine checks. */
 	local_mcck_enable();
+	chp_id_init(&chpid);
 	for (i = 0; i <= __MAX_CHPID; i++) {
-		ret = rchp(i);
+		chpid.id = i;
+		ret = rchp(chpid);
 		if ((ret == 0) || (ret == 2))
 			/*
 			 * rchp either succeeded, or another rchp is already
@@ -1048,37 +1054,19 @@
 	do_reipl_asm(*((__u32*)&schid));
 }
 
-static struct schib __initdata ipl_schib;
-
-/*
- * ipl_save_parameters gets called very early. It is not allowed to access
- * anything in the bss section at all. The bss section is not cleared yet,
- * but may contain some ipl parameters written by the firmware.
- * These parameters (if present) are copied to 0x2000.
- * To avoid corruption of the ipl parameters, all variables used by this
- * function must reside on the stack or in the data section.
- */
-void ipl_save_parameters(void)
+int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo)
 {
 	struct subchannel_id schid;
-	unsigned int *ipl_ptr;
-	void *src, *dst;
+	struct schib schib;
 
 	schid = *(struct subchannel_id *)__LC_SUBCHANNEL_ID;
 	if (!schid.one)
-		return;
-	if (stsch(schid, &ipl_schib))
-		return;
-	if (!ipl_schib.pmcw.dnv)
-		return;
-	ipl_devno = ipl_schib.pmcw.dev;
-	ipl_flags |= IPL_DEVNO_VALID;
-	if (!ipl_schib.pmcw.qf)
-		return;
-	ipl_flags |= IPL_PARMBLOCK_VALID;
-	ipl_ptr = (unsigned int *)__LC_IPL_PARMBLOCK_PTR;
-	src = (void *)(unsigned long)*ipl_ptr;
-	dst = (void *)IPL_PARMBLOCK_ORIGIN;
-	memmove(dst, src, PAGE_SIZE);
-	*ipl_ptr = IPL_PARMBLOCK_ORIGIN;
+		return -ENODEV;
+	if (stsch(schid, &schib))
+		return -ENODEV;
+	if (!schib.pmcw.dnv)
+		return -ENODEV;
+	iplinfo->devno = schib.pmcw.dev;
+	iplinfo->is_qdio = schib.pmcw.qf;
+	return 0;
 }
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index 35154a2..7446c399 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -1,18 +1,11 @@
 #ifndef S390_CIO_H
 #define S390_CIO_H
 
-#include "schid.h"
 #include <linux/mutex.h>
-
-/*
- * where we put the ssd info
- */
-struct ssd_info {
-	__u8  valid:1;
-	__u8  type:7;		/* subchannel type */
-	__u8  chpid[8];		/* chpids */
-	__u16 fla[8];		/* full link addresses */
-} __attribute__ ((packed));
+#include <linux/device.h>
+#include <asm/chpid.h>
+#include "chsc.h"
+#include "schid.h"
 
 /*
  * path management control word
@@ -108,7 +101,7 @@
 	struct schib schib;	/* subchannel information block */
 	struct orb orb;		/* operation request block */
 	struct ccw1 sense_ccw;	/* static ccw for sense command */
-	struct ssd_info ssd_info;	/* subchannel description */
+	struct chsc_ssd_info ssd_info;	/* subchannel description */
 	struct device dev;	/* entry in device tree */
 	struct css_driver *driver;
 } __attribute__ ((aligned(8)));
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index 90b22fa..28abd69 100644
--- a/drivers/s390/cio/cmf.c
+++ b/drivers/s390/cio/cmf.c
@@ -476,7 +476,7 @@
 };
 
 static struct cmb_area cmb_area = {
-	.lock = SPIN_LOCK_UNLOCKED,
+	.lock = __SPIN_LOCK_UNLOCKED(cmb_area.lock),
 	.list = LIST_HEAD_INIT(cmb_area.list),
 	.num_channels  = 1024,
 };
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index fe0ace7..27c6d9e 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -20,8 +20,9 @@
 #include "ioasm.h"
 #include "chsc.h"
 #include "device.h"
+#include "idset.h"
+#include "chp.h"
 
-int need_rescan = 0;
 int css_init_done = 0;
 static int need_reprobe = 0;
 static int max_ssid = 0;
@@ -125,8 +126,52 @@
 	mutex_unlock(&sch->reg_mutex);
 }
 
-static int
-css_register_subchannel(struct subchannel *sch)
+static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw)
+{
+	int i;
+	int mask;
+
+	memset(ssd, 0, sizeof(struct chsc_ssd_info));
+	ssd->path_mask = pmcw->pim;
+	for (i = 0; i < 8; i++) {
+		mask = 0x80 >> i;
+		if (pmcw->pim & mask) {
+			chp_id_init(&ssd->chpid[i]);
+			ssd->chpid[i].id = pmcw->chpid[i];
+		}
+	}
+}
+
+static void ssd_register_chpids(struct chsc_ssd_info *ssd)
+{
+	int i;
+	int mask;
+
+	for (i = 0; i < 8; i++) {
+		mask = 0x80 >> i;
+		if (ssd->path_mask & mask)
+			if (!chp_is_registered(ssd->chpid[i]))
+				chp_new(ssd->chpid[i]);
+	}
+}
+
+void css_update_ssd_info(struct subchannel *sch)
+{
+	int ret;
+
+	if (cio_is_console(sch->schid)) {
+		/* Console is initialized too early for functions requiring
+		 * memory allocation. */
+		ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw);
+	} else {
+		ret = chsc_get_ssd_info(sch->schid, &sch->ssd_info);
+		if (ret)
+			ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw);
+		ssd_register_chpids(&sch->ssd_info);
+	}
+}
+
+static int css_register_subchannel(struct subchannel *sch)
 {
 	int ret;
 
@@ -135,9 +180,7 @@
 	sch->dev.bus = &css_bus_type;
 	sch->dev.release = &css_subchannel_release;
 	sch->dev.groups = subch_attr_groups;
-
-	css_get_ssd_info(sch);
-
+	css_update_ssd_info(sch);
 	/* make it known to the system */
 	ret = css_sch_device_register(sch);
 	if (ret) {
@@ -306,7 +349,7 @@
 	return css_probe_device(schid);
 }
 
-static int css_evaluate_subchannel(struct subchannel_id schid, int slow)
+static void css_evaluate_subchannel(struct subchannel_id schid, int slow)
 {
 	struct subchannel *sch;
 	int ret;
@@ -317,53 +360,66 @@
 		put_device(&sch->dev);
 	} else
 		ret = css_evaluate_new_subchannel(schid, slow);
-
-	return ret;
+	if (ret == -EAGAIN)
+		css_schedule_eval(schid);
 }
 
-static int
-css_rescan_devices(struct subchannel_id schid, void *data)
+static struct idset *slow_subchannel_set;
+static spinlock_t slow_subchannel_lock;
+
+static int __init slow_subchannel_init(void)
 {
-	return css_evaluate_subchannel(schid, 1);
-}
-
-struct slow_subchannel {
-	struct list_head slow_list;
-	struct subchannel_id schid;
-};
-
-static LIST_HEAD(slow_subchannels_head);
-static DEFINE_SPINLOCK(slow_subchannel_lock);
-
-static void
-css_trigger_slow_path(struct work_struct *unused)
-{
-	CIO_TRACE_EVENT(4, "slowpath");
-
-	if (need_rescan) {
-		need_rescan = 0;
-		for_each_subchannel(css_rescan_devices, NULL);
-		return;
+	spin_lock_init(&slow_subchannel_lock);
+	slow_subchannel_set = idset_sch_new();
+	if (!slow_subchannel_set) {
+		printk(KERN_WARNING "cio: could not allocate slow subchannel "
+		       "set\n");
+		return -ENOMEM;
 	}
+	return 0;
+}
 
+subsys_initcall(slow_subchannel_init);
+
+static void css_slow_path_func(struct work_struct *unused)
+{
+	struct subchannel_id schid;
+
+	CIO_TRACE_EVENT(4, "slowpath");
 	spin_lock_irq(&slow_subchannel_lock);
-	while (!list_empty(&slow_subchannels_head)) {
-		struct slow_subchannel *slow_sch =
-			list_entry(slow_subchannels_head.next,
-				   struct slow_subchannel, slow_list);
-
-		list_del_init(slow_subchannels_head.next);
+	init_subchannel_id(&schid);
+	while (idset_sch_get_first(slow_subchannel_set, &schid)) {
+		idset_sch_del(slow_subchannel_set, schid);
 		spin_unlock_irq(&slow_subchannel_lock);
-		css_evaluate_subchannel(slow_sch->schid, 1);
+		css_evaluate_subchannel(schid, 1);
 		spin_lock_irq(&slow_subchannel_lock);
-		kfree(slow_sch);
 	}
 	spin_unlock_irq(&slow_subchannel_lock);
 }
 
-DECLARE_WORK(slow_path_work, css_trigger_slow_path);
+static DECLARE_WORK(slow_path_work, css_slow_path_func);
 struct workqueue_struct *slow_path_wq;
 
+void css_schedule_eval(struct subchannel_id schid)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&slow_subchannel_lock, flags);
+	idset_sch_add(slow_subchannel_set, schid);
+	queue_work(slow_path_wq, &slow_path_work);
+	spin_unlock_irqrestore(&slow_subchannel_lock, flags);
+}
+
+void css_schedule_eval_all(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&slow_subchannel_lock, flags);
+	idset_fill(slow_subchannel_set);
+	queue_work(slow_path_wq, &slow_path_work);
+	spin_unlock_irqrestore(&slow_subchannel_lock, flags);
+}
+
 /* Reprobe subchannel if unregistered. */
 static int reprobe_subchannel(struct subchannel_id schid, void *data)
 {
@@ -426,33 +482,14 @@
 EXPORT_SYMBOL_GPL(css_schedule_reprobe);
 
 /*
- * Rescan for new devices. FIXME: This is slow.
- * This function is called when we have lost CRWs due to overflows and we have
- * to do subchannel housekeeping.
- */
-void
-css_reiterate_subchannels(void)
-{
-	css_clear_subchannel_slow_list();
-	need_rescan = 1;
-}
-
-/*
  * Called from the machine check handler for subchannel report words.
  */
-int
-css_process_crw(int rsid1, int rsid2)
+void css_process_crw(int rsid1, int rsid2)
 {
-	int ret;
 	struct subchannel_id mchk_schid;
 
 	CIO_CRW_EVENT(2, "source is subchannel %04X, subsystem id %x\n",
 		      rsid1, rsid2);
-
-	if (need_rescan)
-		/* We need to iterate all subchannels anyway. */
-		return -EAGAIN;
-
 	init_subchannel_id(&mchk_schid);
 	mchk_schid.sch_no = rsid1;
 	if (rsid2 != 0)
@@ -463,14 +500,7 @@
 	 * use stsch() to find out if the subchannel in question has come
 	 * or gone.
 	 */
-	ret = css_evaluate_subchannel(mchk_schid, 0);
-	if (ret == -EAGAIN) {
-		if (css_enqueue_subchannel_slow(mchk_schid)) {
-			css_clear_subchannel_slow_list();
-			need_rescan = 1;
-		}
-	}
-	return ret;
+	css_evaluate_subchannel(mchk_schid, 0);
 }
 
 static int __init
@@ -745,47 +775,6 @@
 
 subsys_initcall(init_channel_subsystem);
 
-int
-css_enqueue_subchannel_slow(struct subchannel_id schid)
-{
-	struct slow_subchannel *new_slow_sch;
-	unsigned long flags;
-
-	new_slow_sch = kzalloc(sizeof(struct slow_subchannel), GFP_ATOMIC);
-	if (!new_slow_sch)
-		return -ENOMEM;
-	new_slow_sch->schid = schid;
-	spin_lock_irqsave(&slow_subchannel_lock, flags);
-	list_add_tail(&new_slow_sch->slow_list, &slow_subchannels_head);
-	spin_unlock_irqrestore(&slow_subchannel_lock, flags);
-	return 0;
-}
-
-void
-css_clear_subchannel_slow_list(void)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&slow_subchannel_lock, flags);
-	while (!list_empty(&slow_subchannels_head)) {
-		struct slow_subchannel *slow_sch =
-			list_entry(slow_subchannels_head.next,
-				   struct slow_subchannel, slow_list);
-
-		list_del_init(slow_subchannels_head.next);
-		kfree(slow_sch);
-	}
-	spin_unlock_irqrestore(&slow_subchannel_lock, flags);
-}
-
-
-
-int
-css_slow_subchannels_exist(void)
-{
-	return (!list_empty(&slow_subchannels_head));
-}
-
 MODULE_LICENSE("GPL");
 EXPORT_SYMBOL(css_bus_type);
 EXPORT_SYMBOL_GPL(css_characteristics_avail);
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h
index ca2bab9..71fcfdc 100644
--- a/drivers/s390/cio/css.h
+++ b/drivers/s390/cio/css.h
@@ -4,8 +4,11 @@
 #include <linux/mutex.h>
 #include <linux/wait.h>
 #include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/types.h>
 
 #include <asm/cio.h>
+#include <asm/chpid.h>
 
 #include "schid.h"
 
@@ -143,13 +146,12 @@
 extern struct subchannel * get_subchannel_by_schid(struct subchannel_id);
 extern int css_init_done;
 extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *);
-extern int css_process_crw(int, int);
+extern void css_process_crw(int, int);
 extern void css_reiterate_subchannels(void);
+void css_update_ssd_info(struct subchannel *sch);
 
 #define __MAX_SUBCHANNEL 65535
 #define __MAX_SSID 3
-#define __MAX_CHPID 255
-#define __MAX_CSSID 0
 
 struct channel_subsystem {
 	u8 cssid;
@@ -185,16 +187,12 @@
 void device_kill_pending_timer(struct subchannel *);
 
 /* Helper functions to build lists for the slow path. */
-extern int css_enqueue_subchannel_slow(struct subchannel_id schid);
-void css_walk_subchannel_slow_list(void (*fn)(unsigned long));
-void css_clear_subchannel_slow_list(void);
-int css_slow_subchannels_exist(void);
-extern int need_rescan;
+void css_schedule_eval(struct subchannel_id schid);
+void css_schedule_eval_all(void);
 
 int sch_is_pseudo_sch(struct subchannel *);
 
 extern struct workqueue_struct *slow_path_wq;
-extern struct work_struct slow_path_work;
 
 int subchannel_add_files (struct device *);
 extern struct attribute_group *subch_attr_groups[];
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index e322111..a23ff58 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -56,13 +56,12 @@
 /* Store modalias string delimited by prefix/suffix string into buffer with
  * specified size. Return length of resulting string (excluding trailing '\0')
  * even if string doesn't fit buffer (snprintf semantics). */
-static int snprint_alias(char *buf, size_t size, const char *prefix,
+static int snprint_alias(char *buf, size_t size,
 			 struct ccw_device_id *id, const char *suffix)
 {
 	int len;
 
-	len = snprintf(buf, size, "%sccw:t%04Xm%02X", prefix, id->cu_type,
-		       id->cu_model);
+	len = snprintf(buf, size, "ccw:t%04Xm%02X", id->cu_type, id->cu_model);
 	if (len > size)
 		return len;
 	buf += len;
@@ -85,53 +84,40 @@
 	struct ccw_device *cdev = to_ccwdev(dev);
 	struct ccw_device_id *id = &(cdev->id);
 	int i = 0;
-	int len;
+	int len = 0;
+	int ret;
+	char modalias_buf[30];
 
 	/* CU_TYPE= */
-	len = snprintf(buffer, buffer_size, "CU_TYPE=%04X", id->cu_type) + 1;
-	if (len > buffer_size || i >= num_envp)
-		return -ENOMEM;
-	envp[i++] = buffer;
-	buffer += len;
-	buffer_size -= len;
+	ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
+			     "CU_TYPE=%04X", id->cu_type);
+	if (ret)
+		return ret;
 
 	/* CU_MODEL= */
-	len = snprintf(buffer, buffer_size, "CU_MODEL=%02X", id->cu_model) + 1;
-	if (len > buffer_size || i >= num_envp)
-		return -ENOMEM;
-	envp[i++] = buffer;
-	buffer += len;
-	buffer_size -= len;
+	ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
+			     "CU_MODEL=%02X", id->cu_model);
+	if (ret)
+		return ret;
 
 	/* The next two can be zero, that's ok for us */
 	/* DEV_TYPE= */
-	len = snprintf(buffer, buffer_size, "DEV_TYPE=%04X", id->dev_type) + 1;
-	if (len > buffer_size || i >= num_envp)
-		return -ENOMEM;
-	envp[i++] = buffer;
-	buffer += len;
-	buffer_size -= len;
+	ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
+			     "DEV_TYPE=%04X", id->dev_type);
+	if (ret)
+		return ret;
 
 	/* DEV_MODEL= */
-	len = snprintf(buffer, buffer_size, "DEV_MODEL=%02X",
-			(unsigned char) id->dev_model) + 1;
-	if (len > buffer_size || i >= num_envp)
-		return -ENOMEM;
-	envp[i++] = buffer;
-	buffer += len;
-	buffer_size -= len;
+	ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
+			     "DEV_MODEL=%02X", id->dev_model);
+	if (ret)
+		return ret;
 
 	/* MODALIAS=  */
-	len = snprint_alias(buffer, buffer_size, "MODALIAS=", id, "") + 1;
-	if (len > buffer_size || i >= num_envp)
-		return -ENOMEM;
-	envp[i++] = buffer;
-	buffer += len;
-	buffer_size -= len;
-
-	envp[i] = NULL;
-
-	return 0;
+	snprint_alias(modalias_buf, sizeof(modalias_buf), id, "");
+	ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
+			     "MODALIAS=%s", modalias_buf);
+	return ret;
 }
 
 struct bus_type ccw_bus_type;
@@ -230,12 +216,18 @@
 chpids_show (struct device * dev, struct device_attribute *attr, char * buf)
 {
 	struct subchannel *sch = to_subchannel(dev);
-	struct ssd_info *ssd = &sch->ssd_info;
+	struct chsc_ssd_info *ssd = &sch->ssd_info;
 	ssize_t ret = 0;
 	int chp;
+	int mask;
 
-	for (chp = 0; chp < 8; chp++)
-		ret += sprintf (buf+ret, "%02x ", ssd->chpid[chp]);
+	for (chp = 0; chp < 8; chp++) {
+		mask = 0x80 >> chp;
+		if (ssd->path_mask & mask)
+			ret += sprintf(buf + ret, "%02x ", ssd->chpid[chp].id);
+		else
+			ret += sprintf(buf + ret, "00 ");
+	}
 	ret += sprintf (buf+ret, "\n");
 	return min((ssize_t)PAGE_SIZE, ret);
 }
@@ -280,7 +272,7 @@
 	struct ccw_device_id *id = &(cdev->id);
 	int len;
 
-	len = snprint_alias(buf, PAGE_SIZE, "", id, "\n") + 1;
+	len = snprint_alias(buf, PAGE_SIZE, id, "\n") + 1;
 
 	return len > PAGE_SIZE ? PAGE_SIZE : len;
 }
@@ -298,16 +290,10 @@
 	return sch_is_pseudo_sch(to_subchannel(cdev->dev.parent));
 }
 
-static void ccw_device_unregister(struct work_struct *work)
+static void ccw_device_unregister(struct ccw_device *cdev)
 {
-	struct ccw_device_private *priv;
-	struct ccw_device *cdev;
-
-	priv = container_of(work, struct ccw_device_private, kick_work);
-	cdev = priv->cdev;
 	if (test_and_clear_bit(1, &cdev->private->registered))
-		device_unregister(&cdev->dev);
-	put_device(&cdev->dev);
+		device_del(&cdev->dev);
 }
 
 static void
@@ -324,11 +310,8 @@
 		spin_lock_irqsave(cdev->ccwlock, flags);
 		cdev->private->state = DEV_STATE_NOT_OPER;
 		spin_unlock_irqrestore(cdev->ccwlock, flags);
-		if (get_device(&cdev->dev)) {
-			PREPARE_WORK(&cdev->private->kick_work,
-				     ccw_device_unregister);
-			queue_work(ccw_device_work, &cdev->private->kick_work);
-		}
+		ccw_device_unregister(cdev);
+		put_device(&cdev->dev);
 		return ;
 	}
 	sch = to_subchannel(cdev->dev.parent);
@@ -413,11 +396,60 @@
 	return (ret == 0) ? -ENODEV : ret;
 }
 
-static ssize_t
-online_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+static void online_store_handle_offline(struct ccw_device *cdev)
+{
+	if (cdev->private->state == DEV_STATE_DISCONNECTED)
+		ccw_device_remove_disconnected(cdev);
+	else if (cdev->drv && cdev->drv->set_offline)
+		ccw_device_set_offline(cdev);
+}
+
+static int online_store_recog_and_online(struct ccw_device *cdev)
+{
+	int ret;
+
+	/* Do device recognition, if needed. */
+	if (cdev->id.cu_type == 0) {
+		ret = ccw_device_recognition(cdev);
+		if (ret) {
+			printk(KERN_WARNING"Couldn't start recognition "
+			       "for device %s (ret=%d)\n",
+			       cdev->dev.bus_id, ret);
+			return ret;
+		}
+		wait_event(cdev->private->wait_q,
+			   cdev->private->flags.recog_done);
+	}
+	if (cdev->drv && cdev->drv->set_online)
+		ccw_device_set_online(cdev);
+	return 0;
+}
+static void online_store_handle_online(struct ccw_device *cdev, int force)
+{
+	int ret;
+
+	ret = online_store_recog_and_online(cdev);
+	if (ret)
+		return;
+	if (force && cdev->private->state == DEV_STATE_BOXED) {
+		ret = ccw_device_stlck(cdev);
+		if (ret) {
+			printk(KERN_WARNING"ccw_device_stlck for device %s "
+			       "returned %d!\n", cdev->dev.bus_id, ret);
+			return;
+		}
+		if (cdev->id.cu_type == 0)
+			cdev->private->state = DEV_STATE_NOT_OPER;
+		online_store_recog_and_online(cdev);
+	}
+
+}
+
+static ssize_t online_store (struct device *dev, struct device_attribute *attr,
+			     const char *buf, size_t count)
 {
 	struct ccw_device *cdev = to_ccwdev(dev);
-	int i, force, ret;
+	int i, force;
 	char *tmp;
 
 	if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0)
@@ -434,51 +466,17 @@
 		force = 0;
 		i = simple_strtoul(buf, &tmp, 16);
 	}
-	if (i == 1) {
-		/* Do device recognition, if needed. */
-		if (cdev->id.cu_type == 0) {
-			ret = ccw_device_recognition(cdev);
-			if (ret) {
-				printk(KERN_WARNING"Couldn't start recognition "
-				       "for device %s (ret=%d)\n",
-				       cdev->dev.bus_id, ret);
-				goto out;
-			}
-			wait_event(cdev->private->wait_q,
-				   cdev->private->flags.recog_done);
-		}
-		if (cdev->drv && cdev->drv->set_online)
-			ccw_device_set_online(cdev);
-	} else if (i == 0) {
-		if (cdev->private->state == DEV_STATE_DISCONNECTED)
-			ccw_device_remove_disconnected(cdev);
-		else if (cdev->drv && cdev->drv->set_offline)
-			ccw_device_set_offline(cdev);
+
+	switch (i) {
+	case 0:
+		online_store_handle_offline(cdev);
+		break;
+	case 1:
+		online_store_handle_online(cdev, force);
+		break;
+	default:
+		count = -EINVAL;
 	}
-	if (force && cdev->private->state == DEV_STATE_BOXED) {
-		ret = ccw_device_stlck(cdev);
-		if (ret) {
-			printk(KERN_WARNING"ccw_device_stlck for device %s "
-			       "returned %d!\n", cdev->dev.bus_id, ret);
-			goto out;
-		}
-		/* Do device recognition, if needed. */
-		if (cdev->id.cu_type == 0) {
-			cdev->private->state = DEV_STATE_NOT_OPER;
-			ret = ccw_device_recognition(cdev);
-			if (ret) {
-				printk(KERN_WARNING"Couldn't start recognition "
-				       "for device %s (ret=%d)\n",
-				       cdev->dev.bus_id, ret);
-				goto out;
-			}
-			wait_event(cdev->private->wait_q,
-				   cdev->private->flags.recog_done);
-		}
-		if (cdev->drv && cdev->drv->set_online)
-			ccw_device_set_online(cdev);
-	}
-	out:
 	if (cdev->drv)
 		module_put(cdev->drv->owner);
 	atomic_set(&cdev->private->onoff, 0);
@@ -548,17 +546,10 @@
 	.attrs = ccwdev_attrs,
 };
 
-static int
-device_add_files (struct device *dev)
-{
-	return sysfs_create_group(&dev->kobj, &ccwdev_attr_group);
-}
-
-static void
-device_remove_files(struct device *dev)
-{
-	sysfs_remove_group(&dev->kobj, &ccwdev_attr_group);
-}
+struct attribute_group *ccwdev_attr_groups[] = {
+	&ccwdev_attr_group,
+	NULL,
+};
 
 /* this is a simple abstraction for device_register that sets the
  * correct bus type and adds the bus specific files */
@@ -573,10 +564,6 @@
 		return ret;
 
 	set_bit(1, &cdev->private->registered);
-	if ((ret = device_add_files(dev))) {
-		if (test_and_clear_bit(1, &cdev->private->registered))
-			device_del(dev);
-	}
 	return ret;
 }
 
@@ -648,10 +635,6 @@
 		return;
 	}
 	set_bit(1, &cdev->private->registered);
-	if (device_add_files(&cdev->dev)) {
-		if (test_and_clear_bit(1, &cdev->private->registered))
-			device_unregister(&cdev->dev);
-	}
 }
 
 void ccw_device_do_unreg_rereg(struct work_struct *work)
@@ -664,9 +647,7 @@
 	cdev = priv->cdev;
 	sch = to_subchannel(cdev->dev.parent);
 
-	device_remove_files(&cdev->dev);
-	if (test_and_clear_bit(1, &cdev->private->registered))
-		device_del(&cdev->dev);
+	ccw_device_unregister(cdev);
 	PREPARE_WORK(&cdev->private->kick_work,
 		     ccw_device_add_changed);
 	queue_work(ccw_device_work, &cdev->private->kick_work);
@@ -705,6 +686,7 @@
 	cdev->dev.parent = &sch->dev;
 	cdev->dev.release = ccw_device_release;
 	INIT_LIST_HEAD(&cdev->private->kick_work.entry);
+	cdev->dev.groups = ccwdev_attr_groups;
 	/* Do first half of device_register. */
 	device_initialize(&cdev->dev);
 	if (!get_device(&sch->dev)) {
@@ -736,6 +718,7 @@
 static void sch_attach_device(struct subchannel *sch,
 			      struct ccw_device *cdev)
 {
+	css_update_ssd_info(sch);
 	spin_lock_irq(sch->lock);
 	sch->dev.driver_data = cdev;
 	cdev->private->schid = sch->schid;
@@ -871,7 +854,7 @@
 	priv = container_of(work, struct ccw_device_private, kick_work);
 	cdev = priv->cdev;
 	sch = to_subchannel(cdev->dev.parent);
-
+	css_update_ssd_info(sch);
 	/*
 	 * io_subchannel_register() will also be called after device
 	 * recognition has been done for a boxed device (which will already
@@ -888,6 +871,12 @@
 		}
 		goto out;
 	}
+	/*
+	 * Now we know this subchannel will stay, we can throw
+	 * our delayed uevent.
+	 */
+	sch->dev.uevent_suppress = 0;
+	kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
 	/* make it known to the system */
 	ret = ccw_device_register(cdev);
 	if (ret) {
@@ -1133,15 +1122,8 @@
 	sch->dev.driver_data = NULL;
 	cdev->private->state = DEV_STATE_NOT_OPER;
 	spin_unlock_irqrestore(cdev->ccwlock, flags);
-	/*
-	 * Put unregistration on workqueue to avoid livelocks on the css bus
-	 * semaphore.
-	 */
-	if (get_device(&cdev->dev)) {
-		PREPARE_WORK(&cdev->private->kick_work,
-			     ccw_device_unregister);
-		queue_work(ccw_device_work, &cdev->private->kick_work);
-	}
+	ccw_device_unregister(cdev);
+	put_device(&cdev->dev);
 	return 0;
 }
 
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 089a3dd..898ec3b 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -15,6 +15,7 @@
 
 #include <asm/ccwdev.h>
 #include <asm/cio.h>
+#include <asm/chpid.h>
 
 #include "cio.h"
 #include "cio_debug.h"
@@ -22,6 +23,7 @@
 #include "device.h"
 #include "chsc.h"
 #include "ioasm.h"
+#include "chp.h"
 
 int
 device_is_online(struct subchannel *sch)
@@ -210,14 +212,18 @@
 __recover_lost_chpids(struct subchannel *sch, int old_lpm)
 {
 	int mask, i;
+	struct chp_id chpid;
 
+	chp_id_init(&chpid);
 	for (i = 0; i<8; i++) {
 		mask = 0x80 >> i;
 		if (!(sch->lpm & mask))
 			continue;
 		if (old_lpm & mask)
 			continue;
-		chpid_is_actually_online(sch->schib.pmcw.chpid[i]);
+		chpid.id = sch->schib.pmcw.chpid[i];
+		if (!chp_is_registered(chpid))
+			css_schedule_eval_all();
 	}
 }
 
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index 7c7775a..16f59fc 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -16,12 +16,14 @@
 
 #include <asm/ccwdev.h>
 #include <asm/idals.h>
+#include <asm/chpid.h>
 
 #include "cio.h"
 #include "cio_debug.h"
 #include "css.h"
 #include "chsc.h"
 #include "device.h"
+#include "chp.h"
 
 int ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags)
 {
@@ -606,9 +608,12 @@
 ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no)
 {
 	struct subchannel *sch;
+	struct chp_id chpid;
 
 	sch = to_subchannel(cdev->dev.parent);
-	return chsc_get_chp_desc(sch, chp_no);
+	chp_id_init(&chpid);
+	chpid.id = sch->schib.pmcw.chpid[chp_no];
+	return chp_get_chp_desc(chpid);
 }
 
 // FIXME: these have to go:
diff --git a/drivers/s390/cio/idset.c b/drivers/s390/cio/idset.c
new file mode 100644
index 0000000..16ea828
--- /dev/null
+++ b/drivers/s390/cio/idset.c
@@ -0,0 +1,112 @@
+/*
+ *  drivers/s390/cio/idset.c
+ *
+ *    Copyright IBM Corp. 2007
+ *    Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/slab.h>
+#include <asm/bitops.h>
+#include "idset.h"
+#include "css.h"
+
+struct idset {
+	int num_ssid;
+	int num_id;
+	unsigned long bitmap[0];
+};
+
+static inline unsigned long bitmap_size(int num_ssid, int num_id)
+{
+	return __BITOPS_WORDS(num_ssid * num_id) * sizeof(unsigned long);
+}
+
+static struct idset *idset_new(int num_ssid, int num_id)
+{
+	struct idset *set;
+
+	set = kzalloc(sizeof(struct idset) + bitmap_size(num_ssid, num_id),
+		      GFP_KERNEL);
+	if (set) {
+		set->num_ssid = num_ssid;
+		set->num_id = num_id;
+	}
+	return set;
+}
+
+void idset_free(struct idset *set)
+{
+	kfree(set);
+}
+
+void idset_clear(struct idset *set)
+{
+	memset(set->bitmap, 0, bitmap_size(set->num_ssid, set->num_id));
+}
+
+void idset_fill(struct idset *set)
+{
+	memset(set->bitmap, 0xff, bitmap_size(set->num_ssid, set->num_id));
+}
+
+static inline void idset_add(struct idset *set, int ssid, int id)
+{
+	set_bit(ssid * set->num_id + id, set->bitmap);
+}
+
+static inline void idset_del(struct idset *set, int ssid, int id)
+{
+	clear_bit(ssid * set->num_id + id, set->bitmap);
+}
+
+static inline int idset_contains(struct idset *set, int ssid, int id)
+{
+	return test_bit(ssid * set->num_id + id, set->bitmap);
+}
+
+static inline int idset_get_first(struct idset *set, int *ssid, int *id)
+{
+	int bitnum;
+
+	bitnum = find_first_bit(set->bitmap, set->num_ssid * set->num_id);
+	if (bitnum >= set->num_ssid * set->num_id)
+		return 0;
+	*ssid = bitnum / set->num_id;
+	*id = bitnum % set->num_id;
+	return 1;
+}
+
+struct idset *idset_sch_new(void)
+{
+	return idset_new(__MAX_SSID + 1, __MAX_SUBCHANNEL + 1);
+}
+
+void idset_sch_add(struct idset *set, struct subchannel_id schid)
+{
+	idset_add(set, schid.ssid, schid.sch_no);
+}
+
+void idset_sch_del(struct idset *set, struct subchannel_id schid)
+{
+	idset_del(set, schid.ssid, schid.sch_no);
+}
+
+int idset_sch_contains(struct idset *set, struct subchannel_id schid)
+{
+	return idset_contains(set, schid.ssid, schid.sch_no);
+}
+
+int idset_sch_get_first(struct idset *set, struct subchannel_id *schid)
+{
+	int ssid = 0;
+	int id = 0;
+	int rc;
+
+	rc = idset_get_first(set, &ssid, &id);
+	if (rc) {
+		init_subchannel_id(schid);
+		schid->ssid = ssid;
+		schid->sch_no = id;
+	}
+	return rc;
+}
diff --git a/drivers/s390/cio/idset.h b/drivers/s390/cio/idset.h
new file mode 100644
index 0000000..144466a
--- /dev/null
+++ b/drivers/s390/cio/idset.h
@@ -0,0 +1,25 @@
+/*
+ *  drivers/s390/cio/idset.h
+ *
+ *    Copyright IBM Corp. 2007
+ *    Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#ifndef S390_IDSET_H
+#define S390_IDSET_H S390_IDSET_H
+
+#include "schid.h"
+
+struct idset;
+
+void idset_free(struct idset *set);
+void idset_clear(struct idset *set);
+void idset_fill(struct idset *set);
+
+struct idset *idset_sch_new(void);
+void idset_sch_add(struct idset *set, struct subchannel_id id);
+void idset_sch_del(struct idset *set, struct subchannel_id id);
+int idset_sch_contains(struct idset *set, struct subchannel_id id);
+int idset_sch_get_first(struct idset *set, struct subchannel_id *id);
+
+#endif /* S390_IDSET_H */
diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h
index ad6d829..7153dd9 100644
--- a/drivers/s390/cio/ioasm.h
+++ b/drivers/s390/cio/ioasm.h
@@ -1,6 +1,7 @@
 #ifndef S390_CIO_IOASM_H
 #define S390_CIO_IOASM_H
 
+#include <asm/chpid.h>
 #include "schid.h"
 
 /*
@@ -189,9 +190,9 @@
 	return cc;
 }
 
-static inline int rchp(int chpid)
+static inline int rchp(struct chp_id chpid)
 {
-	register unsigned int reg1 asm ("1") = chpid;
+	register struct chp_id reg1 asm ("1") = chpid;
 	int ccode;
 
 	asm volatile(
diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c
index 05fac07..cba64e4 100644
--- a/drivers/s390/cio/qdio.c
+++ b/drivers/s390/cio/qdio.c
@@ -69,7 +69,6 @@
 
 static int qdio_performance_stats = 0;
 static int proc_perf_file_registration;
-static unsigned long i_p_c, i_p_nc, o_p_c, o_p_nc, ii_p_c, ii_p_nc;
 static struct qdio_perf_stats perf_stats;
 
 static int hydra_thinints;
@@ -111,6 +110,31 @@
 }
 
 /***************** SCRUBBER HELPER ROUTINES **********************/
+#ifdef CONFIG_64BIT
+static inline void qdio_perf_stat_inc(atomic64_t *count)
+{
+	if (qdio_performance_stats)
+		atomic64_inc(count);
+}
+
+static inline void qdio_perf_stat_dec(atomic64_t *count)
+{
+	if (qdio_performance_stats)
+		atomic64_dec(count);
+}
+#else /* CONFIG_64BIT */
+static inline void qdio_perf_stat_inc(atomic_t *count)
+{
+	if (qdio_performance_stats)
+		atomic_inc(count);
+}
+
+static inline void qdio_perf_stat_dec(atomic_t *count)
+{
+	if (qdio_performance_stats)
+		atomic_dec(count);
+}
+#endif /* CONFIG_64BIT */
 
 static inline __u64 
 qdio_get_micros(void)
@@ -277,8 +301,7 @@
 	QDIO_DBF_TEXT4(0,trace,"sigasync");
 	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
 
-	if (qdio_performance_stats)
-		perf_stats.siga_syncs++;
+	qdio_perf_stat_inc(&perf_stats.siga_syncs);
 
 	cc = do_siga_sync(q->schid, gpr2, gpr3);
 	if (cc)
@@ -323,8 +346,7 @@
 	__u32 busy_bit;
 	__u64 start_time=0;
 
-	if (qdio_performance_stats)
-		perf_stats.siga_outs++;
+	qdio_perf_stat_inc(&perf_stats.siga_outs);
 
 	QDIO_DBF_TEXT4(0,trace,"sigaout");
 	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
@@ -358,8 +380,7 @@
 	QDIO_DBF_TEXT4(0,trace,"sigain");
 	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
 
-	if (qdio_performance_stats)
-		perf_stats.siga_ins++;
+	qdio_perf_stat_inc(&perf_stats.siga_ins);
 
 	cc = do_siga_input(q->schid, q->mask);
 	
@@ -953,8 +974,7 @@
 
 	if (unlikely(qdio_reserve_q(q))) {
 		qdio_release_q(q);
-		if (qdio_performance_stats)
-			o_p_c++;
+		qdio_perf_stat_inc(&perf_stats.outbound_tl_runs_resched);
 		/* as we're sissies, we'll check next time */
 		if (likely(!atomic_read(&q->is_in_shutdown))) {
 			qdio_mark_q(q);
@@ -962,10 +982,8 @@
 		}
 		return;
 	}
-	if (qdio_performance_stats) {
-		o_p_nc++;
-		perf_stats.tl_runs++;
-	}
+	qdio_perf_stat_inc(&perf_stats.outbound_tl_runs);
+	qdio_perf_stat_inc(&perf_stats.tl_runs);
 
 	/* see comment in qdio_kick_outbound_q */
 	siga_attempts=atomic_read(&q->busy_siga_counter);
@@ -1139,17 +1157,6 @@
 {
 	int i;
 
-	static int old_pcis=0;
-	static int old_thinints=0;
-
-	if (qdio_performance_stats) {
-		if ((old_pcis==perf_stats.pcis)&&
-		    (old_thinints==perf_stats.thinints))
-			perf_stats.start_time_inbound=NOW;
-		else
-			old_pcis=perf_stats.pcis;
-	}
-
 	i=qdio_get_inbound_buffer_frontier(q);
 	if ( (i!=GET_SAVED_FRONTIER(q)) ||
 	     (q->error_status_flags&QDIO_STATUS_LOOK_FOR_ERROR) ) {
@@ -1337,10 +1344,7 @@
 	q->siga_error=0;
 	q->error_status_flags=0;
 
-	if (qdio_performance_stats) {
-		perf_stats.inbound_time+=NOW-perf_stats.start_time_inbound;
-		perf_stats.inbound_cnt++;
-	}
+	qdio_perf_stat_inc(&perf_stats.inbound_cnt);
 }
 
 static void
@@ -1360,8 +1364,7 @@
 	 */
 	if (unlikely(qdio_reserve_q(q))) {
 		qdio_release_q(q);
-		if (qdio_performance_stats)
-			ii_p_c++;
+		qdio_perf_stat_inc(&perf_stats.inbound_thin_tl_runs_resched);
 		/* 
 		 * as we might just be about to stop polling, we make
 		 * sure that we check again at least once more 
@@ -1369,8 +1372,7 @@
 		tiqdio_sched_tl();
 		return;
 	}
-	if (qdio_performance_stats)
-		ii_p_nc++;
+	qdio_perf_stat_inc(&perf_stats.inbound_thin_tl_runs);
 	if (unlikely(atomic_read(&q->is_in_shutdown))) {
 		qdio_unmark_q(q);
 		goto out;
@@ -1412,8 +1414,7 @@
 		for (i=0;i<irq_ptr->no_output_qs;i++) {
 			oq = irq_ptr->output_qs[i];
 			if (!qdio_is_outbound_q_done(oq)) {
-				if (qdio_performance_stats)
-					perf_stats.tl_runs--;
+				qdio_perf_stat_dec(&perf_stats.tl_runs);
 				__qdio_outbound_processing(oq);
 			}
 		}
@@ -1452,8 +1453,7 @@
 
 	if (unlikely(qdio_reserve_q(q))) {
 		qdio_release_q(q);
-		if (qdio_performance_stats)
-			i_p_c++;
+		qdio_perf_stat_inc(&perf_stats.inbound_tl_runs_resched);
 		/* as we're sissies, we'll check next time */
 		if (likely(!atomic_read(&q->is_in_shutdown))) {
 			qdio_mark_q(q);
@@ -1461,10 +1461,8 @@
 		}
 		return;
 	}
-	if (qdio_performance_stats) {
-		i_p_nc++;
-		perf_stats.tl_runs++;
-	}
+	qdio_perf_stat_inc(&perf_stats.inbound_tl_runs);
+	qdio_perf_stat_inc(&perf_stats.tl_runs);
 
 again:
 	if (qdio_has_inbound_q_moved(q)) {
@@ -1510,8 +1508,7 @@
 
 	if (unlikely(qdio_reserve_q(q))) {
 		qdio_release_q(q);
-		if (qdio_performance_stats)
-			ii_p_c++;
+		qdio_perf_stat_inc(&perf_stats.inbound_thin_tl_runs_resched);
 		/* 
 		 * as we might just be about to stop polling, we make
 		 * sure that we check again at least once more 
@@ -1602,8 +1599,7 @@
 {
 	QDIO_DBF_TEXT4(0,trace,"iqdio_tl");
 
-	if (qdio_performance_stats)
-		perf_stats.tl_runs++;
+	qdio_perf_stat_inc(&perf_stats.tl_runs);
 
 	tiqdio_inbound_checks();
 }
@@ -1914,10 +1910,7 @@
 {
 	QDIO_DBF_TEXT4(0,trace,"thin_int");
 
-	if (qdio_performance_stats) {
-		perf_stats.thinints++;
-		perf_stats.start_time_inbound=NOW;
-	}
+	qdio_perf_stat_inc(&perf_stats.thinints);
 
 	/* SVS only when needed:
 	 * issue SVS to benefit from iqdio interrupt avoidance
@@ -1972,17 +1965,12 @@
 	int i;
 	struct qdio_q *q;
 
-	if (qdio_performance_stats) {
-		perf_stats.pcis++;
-		perf_stats.start_time_inbound=NOW;
-	}
+	qdio_perf_stat_inc(&perf_stats.pcis);
 	for (i=0;i<irq_ptr->no_input_qs;i++) {
 		q=irq_ptr->input_qs[i];
 		if (q->is_input_q&QDIO_FLAG_NO_INPUT_INTERRUPT_CONTEXT)
 			qdio_mark_q(q);
 		else {
-			if (qdio_performance_stats)
-				perf_stats.tl_runs--;
 			__qdio_inbound_processing(q);
 		}
 	}
@@ -1992,8 +1980,7 @@
 		q=irq_ptr->output_qs[i];
 		if (qdio_is_outbound_q_done(q))
 			continue;
-		if (qdio_performance_stats)
-			perf_stats.tl_runs--;
+		qdio_perf_stat_dec(&perf_stats.tl_runs);
 		if (!irq_ptr->sync_done_on_outb_pcis)
 			SYNC_MEMORY;
 		__qdio_outbound_processing(q);
@@ -3463,18 +3450,12 @@
 	struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr;
 
 	/* This is the outbound handling of queues */
-	if (qdio_performance_stats)
-		perf_stats.start_time_outbound=NOW;
-
 	qdio_do_qdio_fill_output(q,qidx,count,buffers);
 
 	used_elements=atomic_add_return(count, &q->number_of_buffers_used) - count;
 
 	if (callflags&QDIO_FLAG_DONT_SIGA) {
-		if (qdio_performance_stats) {
-			perf_stats.outbound_time+=NOW-perf_stats.start_time_outbound;
-			perf_stats.outbound_cnt++;
-		}
+		qdio_perf_stat_inc(&perf_stats.outbound_cnt);
 		return;
 	}
 	if (q->is_iqdio_q) {
@@ -3504,8 +3485,7 @@
 				qdio_kick_outbound_q(q);
 			} else {
 				QDIO_DBF_TEXT3(0,trace, "fast-req");
-				if (qdio_performance_stats)
-					perf_stats.fast_reqs++;
+				qdio_perf_stat_inc(&perf_stats.fast_reqs);
 			}
 		}
 		/* 
@@ -3516,10 +3496,7 @@
 		__qdio_outbound_processing(q);
 	}
 
-	if (qdio_performance_stats) {
-		perf_stats.outbound_time+=NOW-perf_stats.start_time_outbound;
-		perf_stats.outbound_cnt++;
-	}
+	qdio_perf_stat_inc(&perf_stats.outbound_cnt);
 }
 
 /* count must be 1 in iqdio */
@@ -3589,33 +3566,67 @@
 		return 0;
 
 #define _OUTP_IT(x...) c+=sprintf(buffer+c,x)
-	_OUTP_IT("i_p_nc/c=%lu/%lu\n",i_p_nc,i_p_c);
-	_OUTP_IT("ii_p_nc/c=%lu/%lu\n",ii_p_nc,ii_p_c);
-	_OUTP_IT("o_p_nc/c=%lu/%lu\n",o_p_nc,o_p_c);
-	_OUTP_IT("Number of tasklet runs (total)                  : %lu\n",
-		 perf_stats.tl_runs);
+#ifdef CONFIG_64BIT
+	_OUTP_IT("Number of tasklet runs (total)                  : %li\n",
+		 (long)atomic64_read(&perf_stats.tl_runs));
+	_OUTP_IT("Inbound tasklet runs      tried/retried         : %li/%li\n",
+		 (long)atomic64_read(&perf_stats.inbound_tl_runs),
+		 (long)atomic64_read(&perf_stats.inbound_tl_runs_resched));
+	_OUTP_IT("Inbound-thin tasklet runs tried/retried         : %li/%li\n",
+		 (long)atomic64_read(&perf_stats.inbound_thin_tl_runs),
+		 (long)atomic64_read(&perf_stats.inbound_thin_tl_runs_resched));
+	_OUTP_IT("Outbound tasklet runs     tried/retried         : %li/%li\n",
+		 (long)atomic64_read(&perf_stats.outbound_tl_runs),
+		 (long)atomic64_read(&perf_stats.outbound_tl_runs_resched));
 	_OUTP_IT("\n");
-	_OUTP_IT("Number of SIGA sync's issued                    : %lu\n",
-		 perf_stats.siga_syncs);
-	_OUTP_IT("Number of SIGA in's issued                      : %lu\n",
-		 perf_stats.siga_ins);
-	_OUTP_IT("Number of SIGA out's issued                     : %lu\n",
-		 perf_stats.siga_outs);
-	_OUTP_IT("Number of PCIs caught                           : %lu\n",
-		 perf_stats.pcis);
-	_OUTP_IT("Number of adapter interrupts caught             : %lu\n",
-		 perf_stats.thinints);
-	_OUTP_IT("Number of fast requeues (outg. SBALs w/o SIGA)  : %lu\n",
-		 perf_stats.fast_reqs);
+	_OUTP_IT("Number of SIGA sync's issued                    : %li\n",
+		 (long)atomic64_read(&perf_stats.siga_syncs));
+	_OUTP_IT("Number of SIGA in's issued                      : %li\n",
+		 (long)atomic64_read(&perf_stats.siga_ins));
+	_OUTP_IT("Number of SIGA out's issued                     : %li\n",
+		 (long)atomic64_read(&perf_stats.siga_outs));
+	_OUTP_IT("Number of PCIs caught                           : %li\n",
+		 (long)atomic64_read(&perf_stats.pcis));
+	_OUTP_IT("Number of adapter interrupts caught             : %li\n",
+		 (long)atomic64_read(&perf_stats.thinints));
+	_OUTP_IT("Number of fast requeues (outg. SBALs w/o SIGA)  : %li\n",
+		 (long)atomic64_read(&perf_stats.fast_reqs));
 	_OUTP_IT("\n");
-	_OUTP_IT("Total time of all inbound actions (us) incl. UL : %lu\n",
-		 perf_stats.inbound_time);
-	_OUTP_IT("Number of inbound transfers                     : %lu\n",
-		 perf_stats.inbound_cnt);
-	_OUTP_IT("Total time of all outbound do_QDIOs (us)        : %lu\n",
-		 perf_stats.outbound_time);
-	_OUTP_IT("Number of do_QDIOs outbound                     : %lu\n",
-		 perf_stats.outbound_cnt);
+	_OUTP_IT("Number of inbound transfers                     : %li\n",
+		 (long)atomic64_read(&perf_stats.inbound_cnt));
+	_OUTP_IT("Number of do_QDIOs outbound                     : %li\n",
+		 (long)atomic64_read(&perf_stats.outbound_cnt));
+#else /* CONFIG_64BIT */
+	_OUTP_IT("Number of tasklet runs (total)                  : %i\n",
+		 atomic_read(&perf_stats.tl_runs));
+	_OUTP_IT("Inbound tasklet runs      tried/retried         : %i/%i\n",
+		 atomic_read(&perf_stats.inbound_tl_runs),
+		 atomic_read(&perf_stats.inbound_tl_runs_resched));
+	_OUTP_IT("Inbound-thin tasklet runs tried/retried         : %i/%i\n",
+		 atomic_read(&perf_stats.inbound_thin_tl_runs),
+		 atomic_read(&perf_stats.inbound_thin_tl_runs_resched));
+	_OUTP_IT("Outbound tasklet runs     tried/retried         : %i/%i\n",
+		 atomic_read(&perf_stats.outbound_tl_runs),
+		 atomic_read(&perf_stats.outbound_tl_runs_resched));
+	_OUTP_IT("\n");
+	_OUTP_IT("Number of SIGA sync's issued                    : %i\n",
+		 atomic_read(&perf_stats.siga_syncs));
+	_OUTP_IT("Number of SIGA in's issued                      : %i\n",
+		 atomic_read(&perf_stats.siga_ins));
+	_OUTP_IT("Number of SIGA out's issued                     : %i\n",
+		 atomic_read(&perf_stats.siga_outs));
+	_OUTP_IT("Number of PCIs caught                           : %i\n",
+		 atomic_read(&perf_stats.pcis));
+	_OUTP_IT("Number of adapter interrupts caught             : %i\n",
+		 atomic_read(&perf_stats.thinints));
+	_OUTP_IT("Number of fast requeues (outg. SBALs w/o SIGA)  : %i\n",
+		 atomic_read(&perf_stats.fast_reqs));
+	_OUTP_IT("\n");
+	_OUTP_IT("Number of inbound transfers                     : %i\n",
+		 atomic_read(&perf_stats.inbound_cnt));
+	_OUTP_IT("Number of do_QDIOs outbound                     : %i\n",
+		 atomic_read(&perf_stats.outbound_cnt));
+#endif /* CONFIG_64BIT */
 	_OUTP_IT("\n");
 
         return c;
@@ -3642,8 +3653,6 @@
 static void
 qdio_remove_procfs_entry(void)
 {
-	perf_stats.tl_runs=0;
-
         if (!proc_perf_file_registration) /* means if it went ok earlier */
 		remove_proc_entry(QDIO_PERF,&proc_root);
 }
@@ -3671,13 +3680,38 @@
 		qdio_performance_stats = i;
 		if (i==0) {
 			/* reset perf. stat. info */
-			i_p_nc = 0;
-			i_p_c = 0;
-			ii_p_nc = 0;
-			ii_p_c = 0;
-			o_p_nc = 0;
-			o_p_c = 0;
-			memset(&perf_stats, 0, sizeof(struct qdio_perf_stats));
+#ifdef CONFIG_64BIT
+			atomic64_set(&perf_stats.tl_runs, 0);
+			atomic64_set(&perf_stats.outbound_tl_runs, 0);
+			atomic64_set(&perf_stats.inbound_tl_runs, 0);
+			atomic64_set(&perf_stats.inbound_tl_runs_resched, 0);
+			atomic64_set(&perf_stats.inbound_thin_tl_runs, 0);
+			atomic64_set(&perf_stats.inbound_thin_tl_runs_resched,
+				     0);
+			atomic64_set(&perf_stats.siga_outs, 0);
+			atomic64_set(&perf_stats.siga_ins, 0);
+			atomic64_set(&perf_stats.siga_syncs, 0);
+			atomic64_set(&perf_stats.pcis, 0);
+			atomic64_set(&perf_stats.thinints, 0);
+			atomic64_set(&perf_stats.fast_reqs, 0);
+			atomic64_set(&perf_stats.outbound_cnt, 0);
+			atomic64_set(&perf_stats.inbound_cnt, 0);
+#else /* CONFIG_64BIT */
+			atomic_set(&perf_stats.tl_runs, 0);
+			atomic_set(&perf_stats.outbound_tl_runs, 0);
+			atomic_set(&perf_stats.inbound_tl_runs, 0);
+			atomic_set(&perf_stats.inbound_tl_runs_resched, 0);
+			atomic_set(&perf_stats.inbound_thin_tl_runs, 0);
+			atomic_set(&perf_stats.inbound_thin_tl_runs_resched, 0);
+			atomic_set(&perf_stats.siga_outs, 0);
+			atomic_set(&perf_stats.siga_ins, 0);
+			atomic_set(&perf_stats.siga_syncs, 0);
+			atomic_set(&perf_stats.pcis, 0);
+			atomic_set(&perf_stats.thinints, 0);
+			atomic_set(&perf_stats.fast_reqs, 0);
+			atomic_set(&perf_stats.outbound_cnt, 0);
+			atomic_set(&perf_stats.inbound_cnt, 0);
+#endif /* CONFIG_64BIT */
 		}
 	} else {
 		QDIO_PRINT_WARN("QDIO performance_stats: write 0 or 1 to this file!\n");
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index ec9af72..2895392 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -406,21 +406,43 @@
 #define CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS 0x04
 
 struct qdio_perf_stats {
-	unsigned long tl_runs;
+#ifdef CONFIG_64BIT
+	atomic64_t tl_runs;
+	atomic64_t outbound_tl_runs;
+	atomic64_t outbound_tl_runs_resched;
+	atomic64_t inbound_tl_runs;
+	atomic64_t inbound_tl_runs_resched;
+	atomic64_t inbound_thin_tl_runs;
+	atomic64_t inbound_thin_tl_runs_resched;
 
-	unsigned long siga_outs;
-	unsigned long siga_ins;
-	unsigned long siga_syncs;
-	unsigned long pcis;
-	unsigned long thinints;
-	unsigned long fast_reqs;
+	atomic64_t siga_outs;
+	atomic64_t siga_ins;
+	atomic64_t siga_syncs;
+	atomic64_t pcis;
+	atomic64_t thinints;
+	atomic64_t fast_reqs;
 
-	__u64 start_time_outbound;
-	unsigned long outbound_cnt;
-	unsigned long outbound_time;
-	__u64 start_time_inbound;
-	unsigned long inbound_cnt;
-	unsigned long inbound_time;
+	atomic64_t outbound_cnt;
+	atomic64_t inbound_cnt;
+#else /* CONFIG_64BIT */
+	atomic_t tl_runs;
+	atomic_t outbound_tl_runs;
+	atomic_t outbound_tl_runs_resched;
+	atomic_t inbound_tl_runs;
+	atomic_t inbound_tl_runs_resched;
+	atomic_t inbound_thin_tl_runs;
+	atomic_t inbound_thin_tl_runs_resched;
+
+	atomic_t siga_outs;
+	atomic_t siga_ins;
+	atomic_t siga_syncs;
+	atomic_t pcis;
+	atomic_t thinints;
+	atomic_t fast_reqs;
+
+	atomic_t outbound_cnt;
+	atomic_t inbound_cnt;
+#endif /* CONFIG_64BIT */
 };
 
 /* unlikely as the later the better */
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index bf37cdf..5aac0ec 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -423,27 +423,25 @@
 		       char *buffer, int buffer_size)
 {
 	struct ap_device *ap_dev = to_ap_dev(dev);
-	int length;
+	int retval = 0, length = 0, i = 0;
 
 	if (!ap_dev)
 		return -ENODEV;
 
 	/* Set up DEV_TYPE environment variable. */
-	envp[0] = buffer;
-	length = scnprintf(buffer, buffer_size, "DEV_TYPE=%04X",
-			   ap_dev->device_type);
-	if (buffer_size - length <= 0)
-		return -ENOMEM;
-	buffer += length;
-	buffer_size -= length;
+	retval = add_uevent_var(envp, num_envp, &i,
+				buffer, buffer_size, &length,
+				"DEV_TYPE=%04X", ap_dev->device_type);
+	if (retval)
+		return retval;
+
 	/* Add MODALIAS= */
-	envp[1] = buffer;
-	length = scnprintf(buffer, buffer_size, "MODALIAS=ap:t%02X",
-			   ap_dev->device_type);
-	if (buffer_size - length <= 0)
-		return -ENOMEM;
-	envp[2] = NULL;
-	return 0;
+	retval = add_uevent_var(envp, num_envp, &i,
+				buffer, buffer_size, &length,
+				"MODALIAS=ap:t%02X", ap_dev->device_type);
+
+	envp[i] = NULL;
+	return retval;
 }
 
 static struct bus_type ap_bus_type = {
diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c
index 7809a79..6dd64d0 100644
--- a/drivers/s390/net/claw.c
+++ b/drivers/s390/net/claw.c
@@ -3525,8 +3525,8 @@
                                 memcpy(skb_put(skb,len_of_data),
 					privptr->p_mtc_envelope,
 					len_of_data);
-                                skb->mac.raw=skb->data;
                                 skb->dev=dev;
+				skb_reset_mac_header(skb);
                                 skb->protocol=htons(ETH_P_IP);
                                 skb->ip_summed=CHECKSUM_UNNECESSARY;
                                 privptr->stats.rx_packets++;
diff --git a/drivers/s390/net/ctcmain.c b/drivers/s390/net/ctcmain.c
index 0d6d5fc..b20fd06 100644
--- a/drivers/s390/net/ctcmain.c
+++ b/drivers/s390/net/ctcmain.c
@@ -455,7 +455,7 @@
 			return;
 		}
 		skb_put(pskb, header->length);
-		pskb->mac.raw = pskb->data;
+		skb_reset_mac_header(pskb);
 		len -= header->length;
 		skb = dev_alloc_skb(pskb->len);
 		if (!skb) {
@@ -472,8 +472,9 @@
 			privptr->stats.rx_dropped++;
 			return;
 		}
-		memcpy(skb_put(skb, pskb->len), pskb->data, pskb->len);
-		skb->mac.raw = skb->data;
+		skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
+					  pskb->len);
+		skb_reset_mac_header(skb);
 		skb->dev = pskb->dev;
 		skb->protocol = pskb->protocol;
 		pskb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -706,7 +707,8 @@
 			spin_unlock(&ch->collect_lock);
 			return;
 		}
-		ch->trans_skb->tail = ch->trans_skb->data = ch->trans_skb_data;
+		ch->trans_skb->data = ch->trans_skb_data;
+		skb_reset_tail_pointer(ch->trans_skb);
 		ch->trans_skb->len = 0;
 		if (ch->prof.maxmulti < (ch->collect_len + 2))
 			ch->prof.maxmulti = ch->collect_len + 2;
@@ -715,8 +717,9 @@
 		*((__u16 *) skb_put(ch->trans_skb, 2)) = ch->collect_len + 2;
 		i = 0;
 		while ((skb = skb_dequeue(&ch->collect_queue))) {
-			memcpy(skb_put(ch->trans_skb, skb->len), skb->data,
-			       skb->len);
+			skb_copy_from_linear_data(skb, skb_put(ch->trans_skb,
+							       skb->len),
+						  skb->len);
 			privptr->stats.tx_packets++;
 			privptr->stats.tx_bytes += skb->len - LL_HEADER_LENGTH;
 			atomic_dec(&skb->users);
@@ -831,7 +834,8 @@
 		ctc_unpack_skb(ch, skb);
 	}
  again:
-	skb->data = skb->tail = ch->trans_skb_data;
+	skb->data = ch->trans_skb_data;
+	skb_reset_tail_pointer(skb);
 	skb->len = 0;
 	if (ctc_checkalloc_buffer(ch, 1))
 		return;
@@ -1638,21 +1642,19 @@
 	struct channel *ch;
 
 	DBF_TEXT(trace, 2, __FUNCTION__);
-	if ((ch =
-	     (struct channel *) kmalloc(sizeof (struct channel),
-					GFP_KERNEL)) == NULL) {
+	ch = kzalloc(sizeof(struct channel), GFP_KERNEL);
+	if (!ch) {
 		ctc_pr_warn("ctc: Out of memory in add_channel\n");
 		return -1;
 	}
-	memset(ch, 0, sizeof (struct channel));
-	if ((ch->ccw = kmalloc(8*sizeof(struct ccw1),
-					       GFP_KERNEL | GFP_DMA)) == NULL) {
+	/* assure all flags and counters are reset */
+	ch->ccw = kzalloc(8 * sizeof(struct ccw1), GFP_KERNEL | GFP_DMA);
+	if (!ch->ccw) {
 		kfree(ch);
 		ctc_pr_warn("ctc: Out of memory in add_channel\n");
 		return -1;
 	}
 
-	memset(ch->ccw, 0, 8*sizeof(struct ccw1));	// assure all flags and counters are reset
 
 	/**
 	 * "static" ccws are used in the following way:
@@ -1692,15 +1694,14 @@
 		return -1;
 	}
 	fsm_newstate(ch->fsm, CH_STATE_IDLE);
-	if ((ch->irb = kmalloc(sizeof (struct irb),
-					      GFP_KERNEL)) == NULL) {
+	ch->irb = kzalloc(sizeof(struct irb), GFP_KERNEL);
+	if (!ch->irb) {
 		ctc_pr_warn("ctc: Out of memory in add_channel\n");
 		kfree_fsm(ch->fsm);
 		kfree(ch->ccw);
 		kfree(ch);
 		return -1;
 	}
-	memset(ch->irb, 0, sizeof (struct irb));
 	while (*c && less_than((*c)->id, ch->id))
 		c = &(*c)->next;
 	if (*c && (!strncmp((*c)->id, ch->id, CTC_ID_SIZE))) {
@@ -2226,7 +2227,8 @@
 		 * IDAL support in CTC is broken, so we have to
 		 * care about skb's above 2G ourselves.
 		 */
-		hi = ((unsigned long) skb->tail + LL_HEADER_LENGTH) >> 31;
+		hi = ((unsigned long)skb_tail_pointer(skb) +
+		      LL_HEADER_LENGTH) >> 31;
 		if (hi) {
 			nskb = alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
 			if (!nskb) {
@@ -2262,11 +2264,12 @@
 				return -EBUSY;
 			}
 
-			ch->trans_skb->tail = ch->trans_skb->data;
+			skb_reset_tail_pointer(ch->trans_skb);
 			ch->trans_skb->len = 0;
 			ch->ccw[1].count = skb->len;
-			memcpy(skb_put(ch->trans_skb, skb->len), skb->data,
-			       skb->len);
+			skb_copy_from_linear_data(skb, skb_put(ch->trans_skb,
+							       skb->len),
+						  skb->len);
 			atomic_dec(&skb->users);
 			dev_kfree_skb_irq(skb);
 			ccw_idx = 0;
@@ -2745,14 +2748,13 @@
 	if (!get_device(&cgdev->dev))
 		return -ENODEV;
 
-	priv = kmalloc(sizeof (struct ctc_priv), GFP_KERNEL);
+	priv = kzalloc(sizeof(struct ctc_priv), GFP_KERNEL);
 	if (!priv) {
 		ctc_pr_err("%s: Out of memory\n", __func__);
 		put_device(&cgdev->dev);
 		return -ENOMEM;
 	}
 
-	memset(priv, 0, sizeof (struct ctc_priv));
 	rc = ctc_add_files(&cgdev->dev);
 	if (rc) {
 		kfree(priv);
@@ -2793,10 +2795,9 @@
 	DBF_TEXT(setup, 3, __FUNCTION__);
 
 	if (alloc_device) {
-		dev = kmalloc(sizeof (struct net_device), GFP_KERNEL);
+		dev = kzalloc(sizeof(struct net_device), GFP_KERNEL);
 		if (!dev)
 			return NULL;
-		memset(dev, 0, sizeof (struct net_device));
 	}
 
 	dev->priv = privptr;
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
index ecca104..08a994f 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -1576,7 +1576,7 @@
 	header->offset = card->tx_buffer->count;
 	header->type = card->lan_type;
 	header->slot = card->portno;
-	memcpy(header + 1, skb->data, skb->len);
+	skb_copy_from_linear_data(skb, header + 1, skb->len);
 	spin_unlock(&card->lock);
 	card->stats.tx_bytes += skb->len;
 	card->stats.tx_packets++;
@@ -1784,7 +1784,6 @@
 		card->stats.rx_dropped++;
 		return;
 	}
-	skb->dev = card->dev;
 	memcpy(skb_put(skb, skb_len), skb_data, skb_len);
 	skb->protocol =	card->lan_type_trans(skb, card->dev);
 	card->stats.rx_bytes += skb_len;
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c
index 594320c..e10e85e 100644
--- a/drivers/s390/net/netiucv.c
+++ b/drivers/s390/net/netiucv.c
@@ -635,7 +635,7 @@
 			return;
 		}
 		skb_put(pskb, header->next);
-		pskb->mac.raw = pskb->data;
+		skb_reset_mac_header(pskb);
 		skb = dev_alloc_skb(pskb->len);
 		if (!skb) {
 			PRINT_WARN("%s Out of memory in netiucv_unpack_skb\n",
@@ -645,8 +645,9 @@
 			privptr->stats.rx_dropped++;
 			return;
 		}
-		memcpy(skb_put(skb, pskb->len), pskb->data, pskb->len);
-		skb->mac.raw = skb->data;
+		skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
+					  pskb->len);
+		skb_reset_mac_header(skb);
 		skb->dev = pskb->dev;
 		skb->protocol = pskb->protocol;
 		pskb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -689,7 +690,8 @@
 			       msg->length, conn->max_buffsize);
 		return;
 	}
-	conn->rx_buff->data = conn->rx_buff->tail = conn->rx_buff->head;
+	conn->rx_buff->data = conn->rx_buff->head;
+	skb_reset_tail_pointer(conn->rx_buff);
 	conn->rx_buff->len = 0;
 	rc = iucv_message_receive(conn->path, msg, 0, conn->rx_buff->data,
 				  msg->length, NULL);
@@ -735,14 +737,17 @@
 			}
 		}
 	}
-	conn->tx_buff->data = conn->tx_buff->tail = conn->tx_buff->head;
+	conn->tx_buff->data = conn->tx_buff->head;
+	skb_reset_tail_pointer(conn->tx_buff);
 	conn->tx_buff->len = 0;
 	spin_lock_irqsave(&conn->collect_lock, saveflags);
 	while ((skb = skb_dequeue(&conn->collect_queue))) {
 		header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN;
 		memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header,
 		       NETIUCV_HDRLEN);
-		memcpy(skb_put(conn->tx_buff, skb->len), skb->data, skb->len);
+		skb_copy_from_linear_data(skb,
+					  skb_put(conn->tx_buff, skb->len),
+					  skb->len);
 		txbytes += skb->len;
 		txpackets++;
 		stat_maxcq++;
@@ -1164,8 +1169,8 @@
 		 * Copy the skb to a new allocated skb in lowmem only if the
 		 * data is located above 2G in memory or tailroom is < 2.
 		 */
-		unsigned long hi =
-			((unsigned long)(skb->tail + NETIUCV_HDRLEN)) >> 31;
+		unsigned long hi = ((unsigned long)(skb_tail_pointer(skb) +
+				    NETIUCV_HDRLEN)) >> 31;
 		int copied = 0;
 		if (hi || (skb_tailroom(skb) < 2)) {
 			nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +
diff --git a/drivers/s390/net/qeth.h b/drivers/s390/net/qeth.h
index 84b108d..b34eb82 100644
--- a/drivers/s390/net/qeth.h
+++ b/drivers/s390/net/qeth.h
@@ -288,6 +288,7 @@
  */
 #define IF_NAME_LEN	 	16
 #define QETH_TX_TIMEOUT		100 * HZ
+#define QETH_RCD_TIMEOUT	60 * HZ
 #define QETH_HEADER_SIZE	32
 #define MAX_PORTNO 		15
 #define QETH_FAKE_LL_LEN_ETH	ETH_HLEN
@@ -582,6 +583,8 @@
 	CH_STATE_ACTIVATING,
 	CH_STATE_HALTED,
 	CH_STATE_STOPPED,
+	CH_STATE_RCD,
+	CH_STATE_RCD_DONE,
 };
 /**
  * card state machine
diff --git a/drivers/s390/net/qeth_eddp.c b/drivers/s390/net/qeth_eddp.c
index 7c735e1..dd7034f 100644
--- a/drivers/s390/net/qeth_eddp.c
+++ b/drivers/s390/net/qeth_eddp.c
@@ -267,7 +267,8 @@
 
 	QETH_DBF_TEXT(trace, 5, "eddpcdtc");
 	if (skb_shinfo(eddp->skb)->nr_frags == 0) {
-		memcpy(dst, eddp->skb->data + eddp->skb_offset, len);
+		skb_copy_from_linear_data_offset(eddp->skb, eddp->skb_offset,
+						 dst, len);
 		*hcsum = csum_partial(eddp->skb->data + eddp->skb_offset, len,
 				      *hcsum);
 		eddp->skb_offset += len;
@@ -416,7 +417,7 @@
                        eddp->skb_offset += VLAN_HLEN;
 #endif /* CONFIG_QETH_VLAN */
        }
-	tcph = eddp->skb->h.th;
+	tcph = tcp_hdr(eddp->skb);
 	while (eddp->skb_offset < eddp->skb->len) {
 		data_len = min((int)skb_shinfo(eddp->skb)->gso_size,
 			       (int)(eddp->skb->len - eddp->skb_offset));
@@ -473,20 +474,24 @@
 	QETH_DBF_TEXT(trace, 5, "eddpficx");
 	/* create our segmentation headers and copy original headers */
 	if (skb->protocol == htons(ETH_P_IP))
-		eddp = qeth_eddp_create_eddp_data(qhdr, (u8 *)skb->nh.iph,
-				skb->nh.iph->ihl*4,
-				(u8 *)skb->h.th, skb->h.th->doff*4);
+		eddp = qeth_eddp_create_eddp_data(qhdr,
+						  skb_network_header(skb),
+						  ip_hdrlen(skb),
+						  skb_transport_header(skb),
+						  tcp_hdrlen(skb));
 	else
-		eddp = qeth_eddp_create_eddp_data(qhdr, (u8 *)skb->nh.ipv6h,
-				sizeof(struct ipv6hdr),
-				(u8 *)skb->h.th, skb->h.th->doff*4);
+		eddp = qeth_eddp_create_eddp_data(qhdr,
+						  skb_network_header(skb),
+						  sizeof(struct ipv6hdr),
+						  skb_transport_header(skb),
+						  tcp_hdrlen(skb));
 
 	if (eddp == NULL) {
 		QETH_DBF_TEXT(trace, 2, "eddpfcnm");
 		return -ENOMEM;
 	}
 	if (qhdr->hdr.l2.id == QETH_HEADER_TYPE_LAYER2) {
-		skb->mac.raw = (skb->data) + sizeof(struct qeth_hdr);
+		skb_set_mac_header(skb, sizeof(struct qeth_hdr));
 		memcpy(&eddp->mac, eth_hdr(skb), ETH_HLEN);
 #ifdef CONFIG_QETH_VLAN
 		if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q)) {
@@ -590,12 +595,13 @@
 	QETH_DBF_TEXT(trace, 5, "creddpct");
 	if (skb->protocol == htons(ETH_P_IP))
 		ctx = qeth_eddp_create_context_generic(card, skb,
-			sizeof(struct qeth_hdr) + skb->nh.iph->ihl*4 +
-			skb->h.th->doff*4);
+						       (sizeof(struct qeth_hdr) +
+						        ip_hdrlen(skb) +
+							tcp_hdrlen(skb)));
 	else if (skb->protocol == htons(ETH_P_IPV6))
 		ctx = qeth_eddp_create_context_generic(card, skb,
 			sizeof(struct qeth_hdr) + sizeof(struct ipv6hdr) +
-			skb->h.th->doff*4);
+			tcp_hdrlen(skb));
 	else
 		QETH_DBF_TEXT(trace, 2, "cetcpinv");
 
diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c
index d8a86f5..6fd8870 100644
--- a/drivers/s390/net/qeth_main.c
+++ b/drivers/s390/net/qeth_main.c
@@ -315,7 +315,8 @@
 }
 
 static long
-__qeth_check_irb_error(struct ccw_device *cdev, struct irb *irb)
+__qeth_check_irb_error(struct ccw_device *cdev, unsigned long intparm,
+		       struct irb *irb)
 {
 	if (!IS_ERR(irb))
 		return 0;
@@ -330,6 +331,14 @@
 		PRINT_WARN("timeout on device %s\n", cdev->dev.bus_id);
 		QETH_DBF_TEXT(trace, 2, "ckirberr");
 		QETH_DBF_TEXT_(trace, 2, "  rc%d", -ETIMEDOUT);
+		if (intparm == QETH_RCD_PARM) {
+			struct qeth_card *card = CARD_FROM_CDEV(cdev);
+
+			if (card && (card->data.ccwdev == cdev)) {
+				card->data.state = CH_STATE_DOWN;
+				wake_up(&card->wait_q);
+			}
+		}
 		break;
 	default:
 		PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb),
@@ -401,7 +410,7 @@
 
 	QETH_DBF_TEXT(trace,5,"irq");
 
-	if (__qeth_check_irb_error(cdev, irb))
+	if (__qeth_check_irb_error(cdev, intparm, irb))
 		return;
 	cstat = irb->scsw.cstat;
 	dstat = irb->scsw.dstat;
@@ -429,7 +438,8 @@
 		channel->state = CH_STATE_HALTED;
 
 	/*let's wake up immediately on data channel*/
-	if ((channel == &card->data) && (intparm != 0))
+	if ((channel == &card->data) && (intparm != 0) &&
+	    (intparm != QETH_RCD_PARM))
 		goto out;
 
 	if (intparm == QETH_CLEAR_CHANNEL_PARM) {
@@ -453,6 +463,10 @@
 			HEXDUMP16(WARN,"irb: ",irb);
 			HEXDUMP16(WARN,"sense data: ",irb->ecw);
 		}
+		if (intparm == QETH_RCD_PARM) {
+			channel->state = CH_STATE_DOWN;
+			goto out;
+		}
 		rc = qeth_get_problem(cdev,irb);
 		if (rc) {
 			qeth_schedule_recovery(card);
@@ -460,6 +474,10 @@
 		}
 	}
 
+	if (intparm == QETH_RCD_PARM) {
+		channel->state = CH_STATE_RCD_DONE;
+		goto out;
+	}
 	if (intparm) {
 		buffer = (struct qeth_cmd_buffer *) __va((addr_t)intparm);
 		buffer->state = BUF_STATE_PROCESSED;
@@ -1204,6 +1222,54 @@
 }
 
 
+static int qeth_read_conf_data(struct qeth_card *card, void **buffer,
+			       int *length)
+{
+	struct ciw *ciw;
+	char *rcd_buf;
+	int ret;
+	struct qeth_channel *channel = &card->data;
+	unsigned long flags;
+
+	/*
+	 * scan for RCD command in extended SenseID data
+	 */
+	ciw = ccw_device_get_ciw(channel->ccwdev, CIW_TYPE_RCD);
+	if (!ciw || ciw->cmd == 0)
+		return -EOPNOTSUPP;
+	rcd_buf = kzalloc(ciw->count, GFP_KERNEL | GFP_DMA);
+	if (!rcd_buf)
+		return -ENOMEM;
+
+	channel->ccw.cmd_code = ciw->cmd;
+	channel->ccw.cda = (__u32) __pa (rcd_buf);
+	channel->ccw.count = ciw->count;
+	channel->ccw.flags = CCW_FLAG_SLI;
+	channel->state = CH_STATE_RCD;
+	spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
+	ret = ccw_device_start_timeout(channel->ccwdev, &channel->ccw,
+				       QETH_RCD_PARM, LPM_ANYPATH, 0,
+				       QETH_RCD_TIMEOUT);
+	spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
+	if (!ret)
+		wait_event(card->wait_q,
+			   (channel->state == CH_STATE_RCD_DONE ||
+			    channel->state == CH_STATE_DOWN));
+	if (channel->state == CH_STATE_DOWN)
+		ret = -EIO;
+	else
+		channel->state = CH_STATE_DOWN;
+	if (ret) {
+		kfree(rcd_buf);
+		*buffer = NULL;
+		*length = 0;
+	} else {
+		*length = ciw->count;
+		*buffer = rcd_buf;
+	}
+	return ret;
+}
+
 static int
 qeth_get_unitaddr(struct qeth_card *card)
 {
@@ -1212,9 +1278,9 @@
 	int rc;
 
 	QETH_DBF_TEXT(setup, 2, "getunit");
-	rc = read_conf_data(CARD_DDEV(card), (void **) &prcd, &length);
+	rc = qeth_read_conf_data(card, (void **) &prcd, &length);
 	if (rc) {
-		PRINT_ERR("read_conf_data for device %s returned %i\n",
+		PRINT_ERR("qeth_read_conf_data for device %s returned %i\n",
 			  CARD_DDEV_ID(card), rc);
 		return rc;
 	}
@@ -1223,6 +1289,7 @@
 	card->info.cula = prcd[63];
 	card->info.guestlan = ((prcd[0x10] == _ascebc['V']) &&
 			       (prcd[0x11] == _ascebc['M']));
+	kfree(prcd);
 	return 0;
 }
 
@@ -2278,7 +2345,7 @@
 	    (card->info.link_type == QETH_LINK_TYPE_LANE_TR))
 	 	return tr_type_trans(skb,dev);
 #endif /* CONFIG_TR */
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	skb_pull(skb, ETH_HLEN );
 	eth = eth_hdr(skb);
 
@@ -2306,9 +2373,9 @@
 	struct iphdr *ip_hdr;
 
 	QETH_DBF_TEXT(trace,5,"skbfktr");
-	skb->mac.raw = skb->data - QETH_FAKE_LL_LEN_TR;
+	skb_set_mac_header(skb, -QETH_FAKE_LL_LEN_TR);
 	/* this is a fake ethernet header */
-	fake_hdr = (struct trh_hdr *) skb->mac.raw;
+	fake_hdr = tr_hdr(skb);
 
 	/* the destination MAC address */
 	switch (skb->pkt_type){
@@ -2359,9 +2426,9 @@
 	struct iphdr *ip_hdr;
 
 	QETH_DBF_TEXT(trace,5,"skbfketh");
-	skb->mac.raw = skb->data - QETH_FAKE_LL_LEN_ETH;
+	skb_set_mac_header(skb, -QETH_FAKE_LL_LEN_ETH);
 	/* this is a fake ethernet header */
-	fake_hdr = (struct ethhdr *) skb->mac.raw;
+	fake_hdr = eth_hdr(skb);
 
 	/* the destination MAC address */
 	switch (skb->pkt_type){
@@ -2461,7 +2528,7 @@
 	if (card->options.fake_ll)
 		qeth_rebuild_skb_fake_ll(card, skb, hdr);
 	else
-		skb->mac.raw = skb->data;
+		skb_reset_mac_header(skb);
 	skb->ip_summed = card->options.checksum_type;
 	if (card->options.checksum_type == HW_CHECKSUMMING){
 		if ( (hdr->hdr.l3.ext_flags &
@@ -2501,7 +2568,8 @@
 			vlan_tag = qeth_rebuild_skb(card, skb, hdr);
 		else { /*in case of OSN*/
 			skb_push(skb, sizeof(struct qeth_hdr));
-			memcpy(skb->data, hdr, sizeof(struct qeth_hdr));
+			skb_copy_to_linear_data(skb, hdr,
+						sizeof(struct qeth_hdr));
 		}
 		/* is device UP ? */
 		if (!(card->dev->flags & IFF_UP)){
@@ -3778,9 +3846,11 @@
 	}
 	/* try something else */
 	if (skb->protocol == ETH_P_IPV6)
-		return (skb->nh.raw[24] == 0xff) ? RTN_MULTICAST : 0;
+		return (skb_network_header(skb)[24] == 0xff) ?
+				RTN_MULTICAST : 0;
 	else if (skb->protocol == ETH_P_IP)
-		return ((skb->nh.raw[16] & 0xf0) == 0xe0) ? RTN_MULTICAST : 0;
+		return ((skb_network_header(skb)[16] & 0xf0) == 0xe0) ?
+				RTN_MULTICAST : 0;
 	/* ... */
 	if (!memcmp(skb->data, skb->dev->broadcast, 6))
 		return RTN_BROADCAST;
@@ -3818,18 +3888,20 @@
 			return card->info.is_multicast_different &
 				(card->qdio.no_out_queues - 1);
 		if (card->qdio.do_prio_queueing && (ipv == 4)) {
+			const u8 tos = ip_hdr(skb)->tos;
+
 			if (card->qdio.do_prio_queueing==QETH_PRIO_Q_ING_TOS){
-				if (skb->nh.iph->tos & IP_TOS_NOTIMPORTANT)
+				if (tos & IP_TOS_NOTIMPORTANT)
 					return 3;
-				if (skb->nh.iph->tos & IP_TOS_HIGHRELIABILITY)
+				if (tos & IP_TOS_HIGHRELIABILITY)
 					return 2;
-				if (skb->nh.iph->tos & IP_TOS_HIGHTHROUGHPUT)
+				if (tos & IP_TOS_HIGHTHROUGHPUT)
 					return 1;
-				if (skb->nh.iph->tos & IP_TOS_LOWDELAY)
+				if (tos & IP_TOS_LOWDELAY)
 					return 0;
 			}
 			if (card->qdio.do_prio_queueing==QETH_PRIO_Q_ING_PREC)
-				return 3 - (skb->nh.iph->tos >> 6);
+				return 3 - (tos >> 6);
 		} else if (card->qdio.do_prio_queueing && (ipv == 6)) {
 			/* TODO: IPv6!!! */
 		}
@@ -3866,9 +3938,9 @@
 		 * memcpys instead of one memmove to save cycles.
 		 */
 		skb_push(skb, VLAN_HLEN);
-		memcpy(skb->data, skb->data + 4, 4);
-		memcpy(skb->data + 4, skb->data + 8, 4);
-		memcpy(skb->data + 8, skb->data + 12, 4);
+		skb_copy_to_linear_data(skb, skb->data + 4, 4);
+		skb_copy_to_linear_data_offset(skb, 4, skb->data + 8, 4);
+		skb_copy_to_linear_data_offset(skb, 8, skb->data + 12, 4);
 		tag = (u16 *)(skb->data + 12);
 		/*
 		 * first two bytes  = ETH_P_8021Q (0x8100)
@@ -4039,7 +4111,8 @@
 			    *((u32 *) skb->dst->neighbour->primary_key);
 		} else {
 			/* fill in destination address used in ip header */
-			*((u32 *) (&hdr->hdr.l3.dest_addr[12])) = skb->nh.iph->daddr;
+			*((u32 *)(&hdr->hdr.l3.dest_addr[12])) =
+							   ip_hdr(skb)->daddr;
 		}
 	} else if (ipv == 6) { /* IPv6 or passthru */
 		hdr->hdr.l3.flags = qeth_get_qeth_hdr_flags6(cast_type);
@@ -4048,7 +4121,8 @@
 			       skb->dst->neighbour->primary_key, 16);
 		} else {
 			/* fill in destination address used in ip header */
-			memcpy(hdr->hdr.l3.dest_addr, &skb->nh.ipv6h->daddr, 16);
+			memcpy(hdr->hdr.l3.dest_addr,
+			       &ipv6_hdr(skb)->daddr, 16);
 		}
 	} else { /* passthrough */
                 if((skb->dev->type == ARPHRD_IEEE802_TR) &&
diff --git a/drivers/s390/net/qeth_mpc.h b/drivers/s390/net/qeth_mpc.h
index 0477c47..d74bc43d 100644
--- a/drivers/s390/net/qeth_mpc.h
+++ b/drivers/s390/net/qeth_mpc.h
@@ -37,6 +37,7 @@
 
 #define QETH_CLEAR_CHANNEL_PARM	-10
 #define QETH_HALT_CHANNEL_PARM	-11
+#define QETH_RCD_PARM -12
 
 /*****************************************************************************/
 /* IP Assist related definitions                                             */
diff --git a/drivers/s390/net/qeth_proc.c b/drivers/s390/net/qeth_proc.c
index 81f805c..89d56c8 100644
--- a/drivers/s390/net/qeth_proc.c
+++ b/drivers/s390/net/qeth_proc.c
@@ -37,7 +37,6 @@
 	struct device *dev = NULL;
 	loff_t nr = 0;
 
-	down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
 	if (*offset == 0)
 		return SEQ_START_TOKEN;
 	while (1) {
@@ -53,7 +52,6 @@
 static void
 qeth_procfile_seq_stop(struct seq_file *s, void* it)
 {
-	up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
 }
 
 static void *
diff --git a/drivers/s390/net/qeth_tso.h b/drivers/s390/net/qeth_tso.h
index 14504af..c20e923 100644
--- a/drivers/s390/net/qeth_tso.h
+++ b/drivers/s390/net/qeth_tso.h
@@ -40,8 +40,8 @@
 	QETH_DBF_TEXT(trace, 5, "tsofhdr");
 
 	hdr  = (struct qeth_hdr_tso *) skb->data;
-	iph  = skb->nh.iph;
-	tcph = skb->h.th;
+	iph  = ip_hdr(skb);
+	tcph = tcp_hdr(skb);
 	/*fix header to TSO values ...*/
 	hdr->hdr.hdr.l3.id = QETH_HEADER_TYPE_TSO;
 	/*set values which are fix for the first approach ...*/
@@ -63,13 +63,9 @@
 static inline void
 qeth_tso_set_tcpip_header(struct qeth_card *card, struct sk_buff *skb)
 {
-	struct iphdr *iph;
-	struct ipv6hdr *ip6h;
-	struct tcphdr *tcph;
-
-	iph  = skb->nh.iph;
-	ip6h = skb->nh.ipv6h;
-	tcph = skb->h.th;
+	struct iphdr *iph    = ip_hdr(skb);
+	struct ipv6hdr *ip6h = ipv6_hdr(skb);
+	struct tcphdr *tcph  = tcp_hdr(skb);
 
 	tcph->check = 0;
 	if (skb->protocol == ETH_P_IPV6) {
diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c
index 806bb1a..644a06e 100644
--- a/drivers/s390/s390mach.c
+++ b/drivers/s390/s390mach.c
@@ -21,6 +21,7 @@
 #include "cio/cio.h"
 #include "cio/chsc.h"
 #include "cio/css.h"
+#include "cio/chp.h"
 #include "s390mach.h"
 
 static struct semaphore m_sem;
@@ -44,14 +45,13 @@
 s390_collect_crw_info(void *param)
 {
 	struct crw crw[2];
-	int ccode, ret, slow;
+	int ccode;
 	struct semaphore *sem;
 	unsigned int chain;
 
 	sem = (struct semaphore *)param;
 repeat:
 	down_interruptible(sem);
-	slow = 0;
 	chain = 0;
 	while (1) {
 		if (unlikely(chain > 1)) {
@@ -84,9 +84,8 @@
 		/* Check for overflows. */
 		if (crw[chain].oflw) {
 			pr_debug("%s: crw overflow detected!\n", __FUNCTION__);
-			css_reiterate_subchannels();
+			css_schedule_eval_all();
 			chain = 0;
-			slow = 1;
 			continue;
 		}
 		switch (crw[chain].rsc) {
@@ -94,10 +93,7 @@
 			if (crw[0].chn && !chain)
 				break;
 			pr_debug("source is subchannel %04X\n", crw[0].rsid);
-			ret = css_process_crw (crw[0].rsid,
-					       chain ? crw[1].rsid : 0);
-			if (ret == -EAGAIN)
-				slow = 1;
+			css_process_crw(crw[0].rsid, chain ? crw[1].rsid : 0);
 			break;
 		case CRW_RSC_MONITOR:
 			pr_debug("source is monitoring facility\n");
@@ -116,28 +112,23 @@
 			}
 			switch (crw[0].erc) {
 			case CRW_ERC_IPARM: /* Path has come. */
-				ret = chp_process_crw(crw[0].rsid, 1);
+				chp_process_crw(crw[0].rsid, 1);
 				break;
 			case CRW_ERC_PERRI: /* Path has gone. */
 			case CRW_ERC_PERRN:
-				ret = chp_process_crw(crw[0].rsid, 0);
+				chp_process_crw(crw[0].rsid, 0);
 				break;
 			default:
 				pr_debug("Don't know how to handle erc=%x\n",
 					 crw[0].erc);
-				ret = 0;
 			}
-			if (ret == -EAGAIN)
-				slow = 1;
 			break;
 		case CRW_RSC_CONFIG:
 			pr_debug("source is configuration-alert facility\n");
 			break;
 		case CRW_RSC_CSS:
 			pr_debug("source is channel subsystem\n");
-			ret = chsc_process_crw();
-			if (ret == -EAGAIN)
-				slow = 1;
+			chsc_process_crw();
 			break;
 		default:
 			pr_debug("unknown source\n");
@@ -146,8 +137,6 @@
 		/* chain is always 0 or 1 here. */
 		chain = crw[chain].chn ? chain + 1 : 0;
 	}
-	if (slow)
-		queue_work(slow_path_wq, &slow_path_work);
 	goto repeat;
 	return 0;
 }
diff --git a/drivers/s390/sysinfo.c b/drivers/s390/sysinfo.c
index 090743d..19343f9 100644
--- a/drivers/s390/sysinfo.c
+++ b/drivers/s390/sysinfo.c
@@ -357,6 +357,24 @@
 
 __initcall(create_proc_sysinfo);
 
+int get_cpu_capability(unsigned int *capability)
+{
+	struct sysinfo_1_2_2 *info;
+	int rc;
+
+	info = (void *) get_zeroed_page(GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+	rc = stsi(info, 1, 2, 2);
+	if (rc == -ENOSYS)
+		goto out;
+	rc = 0;
+	*capability = info->capability;
+out:
+	free_page((unsigned long) info);
+	return rc;
+}
+
 /*
  * CPU capability might have changed. Therefore recalculate loops_per_jiffy.
  */
diff --git a/drivers/sbus/char/envctrl.c b/drivers/sbus/char/envctrl.c
index 2cea4f5..f2be2ea 100644
--- a/drivers/sbus/char/envctrl.c
+++ b/drivers/sbus/char/envctrl.c
@@ -726,7 +726,7 @@
  * Return: None.
  */
 static void envctrl_set_mon(struct i2c_child_t *pchild,
-			    char *chnl_desc,
+			    const char *chnl_desc,
 			    int chnl_no)
 {
 	/* Firmware only has temperature type.  It does not distinguish
@@ -763,8 +763,8 @@
 static void envctrl_init_adc(struct i2c_child_t *pchild, struct device_node *dp)
 {
 	int i = 0, len;
-	char *pos;
-	unsigned int *pval;
+	const char *pos;
+	const unsigned int *pval;
 
 	/* Firmware describe channels into a stream separated by a '\0'. */
 	pos = of_get_property(dp, "channels-description", &len);
@@ -859,7 +859,7 @@
 {
 	int len, i, tbls_size = 0;
 	struct device_node *dp = edev_child->prom_node;
-	void *pval;
+	const void *pval;
 
 	/* Get device address. */
 	pval = of_get_property(dp, "reg", &len);
diff --git a/drivers/sbus/char/flash.c b/drivers/sbus/char/flash.c
index 6e99507..262f01e 100644
--- a/drivers/sbus/char/flash.c
+++ b/drivers/sbus/char/flash.c
@@ -190,7 +190,7 @@
 	}
 	if (!sdev) {
 #ifdef CONFIG_PCI
-		struct linux_prom_registers *ebus_regs;
+		const struct linux_prom_registers *ebus_regs;
 
 		for_each_ebus(ebus) {
 			for_each_ebusdev(edev, ebus) {
diff --git a/drivers/sbus/char/openprom.c b/drivers/sbus/char/openprom.c
index 5041c9d..fbfeb89 100644
--- a/drivers/sbus/char/openprom.c
+++ b/drivers/sbus/char/openprom.c
@@ -44,7 +44,6 @@
 #include <asm/openpromio.h>
 #ifdef CONFIG_PCI
 #include <linux/pci.h>
-#include <asm/pbm.h>
 #endif
 
 MODULE_AUTHOR("Thomas K. Dyas (tdyas@noc.rutgers.edu) and Eddie C. Dost  (ecd@skynet.be)");
@@ -141,7 +140,7 @@
 
 static int opromgetprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize)
 {
-	void *pval;
+	const void *pval;
 	int len;
 
 	if (!dp ||
@@ -248,18 +247,17 @@
 	if (bufsize >= 2*sizeof(int)) {
 #ifdef CONFIG_PCI
 		struct pci_dev *pdev;
-		struct pcidev_cookie *pcp;
+		struct device_node *dp;
+
 		pdev = pci_get_bus_and_slot (((int *) op->oprom_array)[0],
 				      ((int *) op->oprom_array)[1]);
 
-		pcp = pdev->sysdata;
-		if (pcp != NULL) {
-			dp = pcp->prom_node;
-			data->current_node = dp;
-			*((int *)op->oprom_array) = dp->node;
-			op->oprom_size = sizeof(int);
-			err = copyout(argp, op, bufsize + sizeof(int));
-		}
+		dp = pci_device_to_OF_node(pdev);
+		data->current_node = dp;
+		*((int *)op->oprom_array) = dp->node;
+		op->oprom_size = sizeof(int);
+		err = copyout(argp, op, bufsize + sizeof(int));
+
 		pci_dev_put(pdev);
 #endif
 	}
@@ -410,7 +408,7 @@
 	struct opiocdesc op;
 	struct device_node *dp;
 	char *str;
-	void *pval;
+	const void *pval;
 	int err, len;
 
 	if (copy_from_user(&op, argp, sizeof(op)))
diff --git a/drivers/sbus/sbus.c b/drivers/sbus/sbus.c
index 6349dd6..eee590a 100644
--- a/drivers/sbus/sbus.c
+++ b/drivers/sbus/sbus.c
@@ -35,7 +35,7 @@
 static void __init fill_sbus_device(struct device_node *dp, struct sbus_dev *sdev)
 {
 	unsigned long base;
-	void *pval;
+	const void *pval;
 	int len, err;
 
 	sdev->prom_node = dp->node;
@@ -86,7 +86,7 @@
 
 static void __init sbus_bus_ranges_init(struct device_node *dp, struct sbus_bus *sbus)
 {
-	void *pval;
+	const void *pval;
 	int len;
 
 	pval = of_get_property(dp, "ranges", &len);
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 4cd280e..fcc4cb6 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -1763,9 +1763,15 @@
 	  The ESP was an on-board SCSI controller used on Sun 3/80
 	  machines.  Say Y here to compile in support for it.
 
+config SCSI_ESP_CORE
+	tristate "ESP Scsi Driver Core"
+	depends on SCSI
+	select SCSI_SPI_ATTRS
+
 config SCSI_SUNESP
 	tristate "Sparc ESP Scsi Driver"
 	depends on SBUS && SCSI
+	select SCSI_ESP_CORE
 	help
 	  This is the driver for the Sun ESP SCSI host adapter. The ESP
 	  chipset is present in most SPARC SBUS-based computers.
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 79ecf4e..70cff4c 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -106,7 +106,8 @@
 obj-$(CONFIG_MEGARAID_NEWGEN)	+= megaraid/
 obj-$(CONFIG_MEGARAID_SAS)	+= megaraid/
 obj-$(CONFIG_SCSI_ACARD)	+= atp870u.o
-obj-$(CONFIG_SCSI_SUNESP)	+= esp.o
+obj-$(CONFIG_SCSI_ESP_CORE)	+= esp_scsi.o
+obj-$(CONFIG_SCSI_SUNESP)	+= sun_esp.o
 obj-$(CONFIG_SCSI_GDTH)		+= gdth.o
 obj-$(CONFIG_SCSI_INITIO)	+= initio.o
 obj-$(CONFIG_SCSI_INIA100)	+= a100u2w.o
diff --git a/drivers/scsi/esp.c b/drivers/scsi/esp.c
deleted file mode 100644
index 2c2fe80..0000000
--- a/drivers/scsi/esp.c
+++ /dev/null
@@ -1,4394 +0,0 @@
-/* esp.c: ESP Sun SCSI driver.
- *
- * Copyright (C) 1995, 1998, 2006 David S. Miller (davem@davemloft.net)
- */
-
-/* TODO:
- *
- * 1) Maybe disable parity checking in config register one for SCSI1
- *    targets.  (Gilmore says parity error on the SBus can lock up
- *    old sun4c's)
- * 2) Add support for DMA2 pipelining.
- * 3) Add tagged queueing.
- */
-
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/blkdev.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-
-#include "esp.h"
-
-#include <asm/sbus.h>
-#include <asm/dma.h>
-#include <asm/system.h>
-#include <asm/ptrace.h>
-#include <asm/pgtable.h>
-#include <asm/oplib.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#ifndef __sparc_v9__
-#include <asm/machines.h>
-#include <asm/idprom.h>
-#endif
-
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_eh.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_tcq.h>
-
-#define DRV_VERSION "1.101"
-
-#define DEBUG_ESP
-/* #define DEBUG_ESP_HME */
-/* #define DEBUG_ESP_DATA */
-/* #define DEBUG_ESP_QUEUE */
-/* #define DEBUG_ESP_DISCONNECT */
-/* #define DEBUG_ESP_STATUS */
-/* #define DEBUG_ESP_PHASES */
-/* #define DEBUG_ESP_WORKBUS */
-/* #define DEBUG_STATE_MACHINE */
-/* #define DEBUG_ESP_CMDS */
-/* #define DEBUG_ESP_IRQS */
-/* #define DEBUG_SDTR */
-/* #define DEBUG_ESP_SG */
-
-/* Use the following to sprinkle debugging messages in a way which
- * suits you if combinations of the above become too verbose when
- * trying to track down a specific problem.
- */
-/* #define DEBUG_ESP_MISC */
-
-#if defined(DEBUG_ESP)
-#define ESPLOG(foo)  printk foo
-#else
-#define ESPLOG(foo)
-#endif /* (DEBUG_ESP) */
-
-#if defined(DEBUG_ESP_HME)
-#define ESPHME(foo)  printk foo
-#else
-#define ESPHME(foo)
-#endif
-
-#if defined(DEBUG_ESP_DATA)
-#define ESPDATA(foo)  printk foo
-#else
-#define ESPDATA(foo)
-#endif
-
-#if defined(DEBUG_ESP_QUEUE)
-#define ESPQUEUE(foo)  printk foo
-#else
-#define ESPQUEUE(foo)
-#endif
-
-#if defined(DEBUG_ESP_DISCONNECT)
-#define ESPDISC(foo)  printk foo
-#else
-#define ESPDISC(foo)
-#endif
-
-#if defined(DEBUG_ESP_STATUS)
-#define ESPSTAT(foo)  printk foo
-#else
-#define ESPSTAT(foo)
-#endif
-
-#if defined(DEBUG_ESP_PHASES)
-#define ESPPHASE(foo)  printk foo
-#else
-#define ESPPHASE(foo)
-#endif
-
-#if defined(DEBUG_ESP_WORKBUS)
-#define ESPBUS(foo)  printk foo
-#else
-#define ESPBUS(foo)
-#endif
-
-#if defined(DEBUG_ESP_IRQS)
-#define ESPIRQ(foo)  printk foo
-#else
-#define ESPIRQ(foo)
-#endif
-
-#if defined(DEBUG_SDTR)
-#define ESPSDTR(foo)  printk foo
-#else
-#define ESPSDTR(foo)
-#endif
-
-#if defined(DEBUG_ESP_MISC)
-#define ESPMISC(foo)  printk foo
-#else
-#define ESPMISC(foo)
-#endif
-
-/* Command phase enumeration. */
-enum {
-	not_issued    = 0x00,  /* Still in the issue_SC queue.          */
-
-	/* Various forms of selecting a target. */
-#define in_slct_mask    0x10
-	in_slct_norm  = 0x10,  /* ESP is arbitrating, normal selection  */
-	in_slct_stop  = 0x11,  /* ESP will select, then stop with IRQ   */
-	in_slct_msg   = 0x12,  /* select, then send a message           */
-	in_slct_tag   = 0x13,  /* select and send tagged queue msg      */
-	in_slct_sneg  = 0x14,  /* select and acquire sync capabilities  */
-
-	/* Any post selection activity. */
-#define in_phases_mask  0x20
-	in_datain     = 0x20,  /* Data is transferring from the bus     */
-	in_dataout    = 0x21,  /* Data is transferring to the bus       */
-	in_data_done  = 0x22,  /* Last DMA data operation done (maybe)  */
-	in_msgin      = 0x23,  /* Eating message from target            */
-	in_msgincont  = 0x24,  /* Eating more msg bytes from target     */
-	in_msgindone  = 0x25,  /* Decide what to do with what we got    */
-	in_msgout     = 0x26,  /* Sending message to target             */
-	in_msgoutdone = 0x27,  /* Done sending msg out                  */
-	in_cmdbegin   = 0x28,  /* Sending cmd after abnormal selection  */
-	in_cmdend     = 0x29,  /* Done sending slow cmd                 */
-	in_status     = 0x2a,  /* Was in status phase, finishing cmd    */
-	in_freeing    = 0x2b,  /* freeing the bus for cmd cmplt or disc */
-	in_the_dark   = 0x2c,  /* Don't know what bus phase we are in   */
-
-	/* Special states, ie. not normal bus transitions... */
-#define in_spec_mask    0x80
-	in_abortone   = 0x80,  /* Aborting one command currently        */
-	in_abortall   = 0x81,  /* Blowing away all commands we have     */
-	in_resetdev   = 0x82,  /* SCSI target reset in progress         */
-	in_resetbus   = 0x83,  /* SCSI bus reset in progress            */
-	in_tgterror   = 0x84,  /* Target did something stupid           */
-};
-
-enum {
-	/* Zero has special meaning, see skipahead[12]. */
-/*0*/	do_never,
-
-/*1*/	do_phase_determine,
-/*2*/	do_reset_bus,
-/*3*/	do_reset_complete,
-/*4*/	do_work_bus,
-/*5*/	do_intr_end
-};
-
-/* Forward declarations. */
-static irqreturn_t esp_intr(int irq, void *dev_id);
-
-/* Debugging routines */
-struct esp_cmdstrings {
-	u8 cmdchar;
-	char *text;
-} esp_cmd_strings[] = {
-	/* Miscellaneous */
-	{ ESP_CMD_NULL, "ESP_NOP", },
-	{ ESP_CMD_FLUSH, "FIFO_FLUSH", },
-	{ ESP_CMD_RC, "RSTESP", },
-	{ ESP_CMD_RS, "RSTSCSI", },
-	/* Disconnected State Group */
-	{ ESP_CMD_RSEL, "RESLCTSEQ", },
-	{ ESP_CMD_SEL, "SLCTNATN", },
-	{ ESP_CMD_SELA, "SLCTATN", },
-	{ ESP_CMD_SELAS, "SLCTATNSTOP", },
-	{ ESP_CMD_ESEL, "ENSLCTRESEL", },
-	{ ESP_CMD_DSEL, "DISSELRESEL", },
-	{ ESP_CMD_SA3, "SLCTATN3", },
-	{ ESP_CMD_RSEL3, "RESLCTSEQ", },
-	/* Target State Group */
-	{ ESP_CMD_SMSG, "SNDMSG", },
-	{ ESP_CMD_SSTAT, "SNDSTATUS", },
-	{ ESP_CMD_SDATA, "SNDDATA", },
-	{ ESP_CMD_DSEQ, "DISCSEQ", },
-	{ ESP_CMD_TSEQ, "TERMSEQ", },
-	{ ESP_CMD_TCCSEQ, "TRGTCMDCOMPSEQ", },
-	{ ESP_CMD_DCNCT, "DISC", },
-	{ ESP_CMD_RMSG, "RCVMSG", },
-	{ ESP_CMD_RCMD, "RCVCMD", },
-	{ ESP_CMD_RDATA, "RCVDATA", },
-	{ ESP_CMD_RCSEQ, "RCVCMDSEQ", },
-	/* Initiator State Group */
-	{ ESP_CMD_TI, "TRANSINFO", },
-	{ ESP_CMD_ICCSEQ, "INICMDSEQCOMP", },
-	{ ESP_CMD_MOK, "MSGACCEPTED", },
-	{ ESP_CMD_TPAD, "TPAD", },
-	{ ESP_CMD_SATN, "SATN", },
-	{ ESP_CMD_RATN, "RATN", },
-};
-#define NUM_ESP_COMMANDS  ((sizeof(esp_cmd_strings)) / (sizeof(struct esp_cmdstrings)))
-
-/* Print textual representation of an ESP command */
-static inline void esp_print_cmd(u8 espcmd)
-{
-	u8 dma_bit = espcmd & ESP_CMD_DMA;
-	int i;
-
-	espcmd &= ~dma_bit;
-	for (i = 0; i < NUM_ESP_COMMANDS; i++)
-		if (esp_cmd_strings[i].cmdchar == espcmd)
-			break;
-	if (i == NUM_ESP_COMMANDS)
-		printk("ESP_Unknown");
-	else
-		printk("%s%s", esp_cmd_strings[i].text,
-		       ((dma_bit) ? "+DMA" : ""));
-}
-
-/* Print the status register's value */
-static inline void esp_print_statreg(u8 statreg)
-{
-	u8 phase;
-
-	printk("STATUS<");
-	phase = statreg & ESP_STAT_PMASK;
-	printk("%s,", (phase == ESP_DOP ? "DATA-OUT" :
-		       (phase == ESP_DIP ? "DATA-IN" :
-			(phase == ESP_CMDP ? "COMMAND" :
-			 (phase == ESP_STATP ? "STATUS" :
-			  (phase == ESP_MOP ? "MSG-OUT" :
-			   (phase == ESP_MIP ? "MSG_IN" :
-			    "unknown")))))));
-	if (statreg & ESP_STAT_TDONE)
-		printk("TRANS_DONE,");
-	if (statreg & ESP_STAT_TCNT)
-		printk("TCOUNT_ZERO,");
-	if (statreg & ESP_STAT_PERR)
-		printk("P_ERROR,");
-	if (statreg & ESP_STAT_SPAM)
-		printk("SPAM,");
-	if (statreg & ESP_STAT_INTR)
-		printk("IRQ,");
-	printk(">");
-}
-
-/* Print the interrupt register's value */
-static inline void esp_print_ireg(u8 intreg)
-{
-	printk("INTREG< ");
-	if (intreg & ESP_INTR_S)
-		printk("SLCT_NATN ");
-	if (intreg & ESP_INTR_SATN)
-		printk("SLCT_ATN ");
-	if (intreg & ESP_INTR_RSEL)
-		printk("RSLCT ");
-	if (intreg & ESP_INTR_FDONE)
-		printk("FDONE ");
-	if (intreg & ESP_INTR_BSERV)
-		printk("BSERV ");
-	if (intreg & ESP_INTR_DC)
-		printk("DISCNCT ");
-	if (intreg & ESP_INTR_IC)
-		printk("ILL_CMD ");
-	if (intreg & ESP_INTR_SR)
-		printk("SCSI_BUS_RESET ");
-	printk(">");
-}
-
-/* Print the sequence step registers contents */
-static inline void esp_print_seqreg(u8 stepreg)
-{
-	stepreg &= ESP_STEP_VBITS;
-	printk("STEP<%s>",
-	       (stepreg == ESP_STEP_ASEL ? "SLCT_ARB_CMPLT" :
-		(stepreg == ESP_STEP_SID ? "1BYTE_MSG_SENT" :
-		 (stepreg == ESP_STEP_NCMD ? "NOT_IN_CMD_PHASE" :
-		  (stepreg == ESP_STEP_PPC ? "CMD_BYTES_LOST" :
-		   (stepreg == ESP_STEP_FINI4 ? "CMD_SENT_OK" :
-		    "UNKNOWN"))))));
-}
-
-static char *phase_string(int phase)
-{
-	switch (phase) {
-	case not_issued:
-		return "UNISSUED";
-	case in_slct_norm:
-		return "SLCTNORM";
-	case in_slct_stop:
-		return "SLCTSTOP";
-	case in_slct_msg:
-		return "SLCTMSG";
-	case in_slct_tag:
-		return "SLCTTAG";
-	case in_slct_sneg:
-		return "SLCTSNEG";
-	case in_datain:
-		return "DATAIN";
-	case in_dataout:
-		return "DATAOUT";
-	case in_data_done:
-		return "DATADONE";
-	case in_msgin:
-		return "MSGIN";
-	case in_msgincont:
-		return "MSGINCONT";
-	case in_msgindone:
-		return "MSGINDONE";
-	case in_msgout:
-		return "MSGOUT";
-	case in_msgoutdone:
-		return "MSGOUTDONE";
-	case in_cmdbegin:
-		return "CMDBEGIN";
-	case in_cmdend:
-		return "CMDEND";
-	case in_status:
-		return "STATUS";
-	case in_freeing:
-		return "FREEING";
-	case in_the_dark:
-		return "CLUELESS";
-	case in_abortone:
-		return "ABORTONE";
-	case in_abortall:
-		return "ABORTALL";
-	case in_resetdev:
-		return "RESETDEV";
-	case in_resetbus:
-		return "RESETBUS";
-	case in_tgterror:
-		return "TGTERROR";
-	default:
-		return "UNKNOWN";
-	};
-}
-
-#ifdef DEBUG_STATE_MACHINE
-static inline void esp_advance_phase(struct scsi_cmnd *s, int newphase)
-{
-	ESPLOG(("<%s>", phase_string(newphase)));
-	s->SCp.sent_command = s->SCp.phase;
-	s->SCp.phase = newphase;
-}
-#else
-#define esp_advance_phase(__s, __newphase) \
-	(__s)->SCp.sent_command = (__s)->SCp.phase; \
-	(__s)->SCp.phase = (__newphase);
-#endif
-
-#ifdef DEBUG_ESP_CMDS
-static inline void esp_cmd(struct esp *esp, u8 cmd)
-{
-	esp->espcmdlog[esp->espcmdent] = cmd;
-	esp->espcmdent = (esp->espcmdent + 1) & 31;
-	sbus_writeb(cmd, esp->eregs + ESP_CMD);
-}
-#else
-#define esp_cmd(__esp, __cmd)	\
-	sbus_writeb((__cmd), ((__esp)->eregs) + ESP_CMD)
-#endif
-
-#define ESP_INTSOFF(__dregs)	\
-	sbus_writel(sbus_readl((__dregs)+DMA_CSR)&~(DMA_INT_ENAB), (__dregs)+DMA_CSR)
-#define ESP_INTSON(__dregs)	\
-	sbus_writel(sbus_readl((__dregs)+DMA_CSR)|DMA_INT_ENAB, (__dregs)+DMA_CSR)
-#define ESP_IRQ_P(__dregs)	\
-	(sbus_readl((__dregs)+DMA_CSR) & (DMA_HNDL_INTR|DMA_HNDL_ERROR))
-
-/* How we use the various Linux SCSI data structures for operation.
- *
- * struct scsi_cmnd:
- *
- *   We keep track of the synchronous capabilities of a target
- *   in the device member, using sync_min_period and
- *   sync_max_offset.  These are the values we directly write
- *   into the ESP registers while running a command.  If offset
- *   is zero the ESP will use asynchronous transfers.
- *   If the borken flag is set we assume we shouldn't even bother
- *   trying to negotiate for synchronous transfer as this target
- *   is really stupid.  If we notice the target is dropping the
- *   bus, and we have been allowing it to disconnect, we clear
- *   the disconnect flag.
- */
-
-
-/* Manipulation of the ESP command queues.  Thanks to the aha152x driver
- * and its author, Juergen E. Fischer, for the methods used here.
- * Note that these are per-ESP queues, not global queues like
- * the aha152x driver uses.
- */
-static inline void append_SC(struct scsi_cmnd **SC, struct scsi_cmnd *new_SC)
-{
-	struct scsi_cmnd *end;
-
-	new_SC->host_scribble = (unsigned char *) NULL;
-	if (!*SC)
-		*SC = new_SC;
-	else {
-		for (end=*SC;end->host_scribble;end=(struct scsi_cmnd *)end->host_scribble)
-			;
-		end->host_scribble = (unsigned char *) new_SC;
-	}
-}
-
-static inline void prepend_SC(struct scsi_cmnd **SC, struct scsi_cmnd *new_SC)
-{
-	new_SC->host_scribble = (unsigned char *) *SC;
-	*SC = new_SC;
-}
-
-static inline struct scsi_cmnd *remove_first_SC(struct scsi_cmnd **SC)
-{
-	struct scsi_cmnd *ptr;
-	ptr = *SC;
-	if (ptr)
-		*SC = (struct scsi_cmnd *) (*SC)->host_scribble;
-	return ptr;
-}
-
-static inline struct scsi_cmnd *remove_SC(struct scsi_cmnd **SC, int target, int lun)
-{
-	struct scsi_cmnd *ptr, *prev;
-
-	for (ptr = *SC, prev = NULL;
-	     ptr && ((ptr->device->id != target) || (ptr->device->lun != lun));
-	     prev = ptr, ptr = (struct scsi_cmnd *) ptr->host_scribble)
-		;
-	if (ptr) {
-		if (prev)
-			prev->host_scribble=ptr->host_scribble;
-		else
-			*SC=(struct scsi_cmnd *)ptr->host_scribble;
-	}
-	return ptr;
-}
-
-/* Resetting various pieces of the ESP scsi driver chipset/buses. */
-static void esp_reset_dma(struct esp *esp)
-{
-	int can_do_burst16, can_do_burst32, can_do_burst64;
-	int can_do_sbus64;
-	u32 tmp;
-
-	can_do_burst16 = (esp->bursts & DMA_BURST16) != 0;
-	can_do_burst32 = (esp->bursts & DMA_BURST32) != 0;
-	can_do_burst64 = 0;
-	can_do_sbus64 = 0;
-	if (sbus_can_dma_64bit(esp->sdev))
-		can_do_sbus64 = 1;
-	if (sbus_can_burst64(esp->sdev))
-		can_do_burst64 = (esp->bursts & DMA_BURST64) != 0;
-
-	/* Punt the DVMA into a known state. */
-	if (esp->dma->revision != dvmahme) {
-		tmp = sbus_readl(esp->dregs + DMA_CSR);
-		sbus_writel(tmp | DMA_RST_SCSI, esp->dregs + DMA_CSR);
-		sbus_writel(tmp & ~DMA_RST_SCSI, esp->dregs + DMA_CSR);
-	}
-	switch (esp->dma->revision) {
-	case dvmahme:
-		/* This is the HME DVMA gate array. */
-
-		sbus_writel(DMA_RESET_FAS366, esp->dregs + DMA_CSR);
-		sbus_writel(DMA_RST_SCSI, esp->dregs + DMA_CSR);
-
-		esp->prev_hme_dmacsr = (DMA_PARITY_OFF|DMA_2CLKS|DMA_SCSI_DISAB|DMA_INT_ENAB);
-		esp->prev_hme_dmacsr &= ~(DMA_ENABLE|DMA_ST_WRITE|DMA_BRST_SZ);
-
-		if (can_do_burst64)
-			esp->prev_hme_dmacsr |= DMA_BRST64;
-		else if (can_do_burst32)
-			esp->prev_hme_dmacsr |= DMA_BRST32;
-
-		if (can_do_sbus64) {
-			esp->prev_hme_dmacsr |= DMA_SCSI_SBUS64;
-			sbus_set_sbus64(esp->sdev, esp->bursts);
-		}
-
-		/* This chip is horrible. */
-		while (sbus_readl(esp->dregs + DMA_CSR) & DMA_PEND_READ)
-			udelay(1);
-
-		sbus_writel(0, esp->dregs + DMA_CSR);
-		sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR);
-
-		/* This is necessary to avoid having the SCSI channel
-		 * engine lock up on us.
-		 */
-		sbus_writel(0, esp->dregs + DMA_ADDR);
-
-		break;
-	case dvmarev2:
-		/* This is the gate array found in the sun4m
-		 * NCR SBUS I/O subsystem.
-		 */
-		if (esp->erev != esp100) {
-			tmp = sbus_readl(esp->dregs + DMA_CSR);
-			sbus_writel(tmp | DMA_3CLKS, esp->dregs + DMA_CSR);
-		}
-		break;
-	case dvmarev3:
-		tmp = sbus_readl(esp->dregs + DMA_CSR);
-		tmp &= ~DMA_3CLKS;
-		tmp |= DMA_2CLKS;
-		if (can_do_burst32) {
-			tmp &= ~DMA_BRST_SZ;
-			tmp |= DMA_BRST32;
-		}
-		sbus_writel(tmp, esp->dregs + DMA_CSR);
-		break;
-	case dvmaesc1:
-		/* This is the DMA unit found on SCSI/Ether cards. */
-		tmp = sbus_readl(esp->dregs + DMA_CSR);
-		tmp |= DMA_ADD_ENABLE;
-		tmp &= ~DMA_BCNT_ENAB;
-		if (!can_do_burst32 && can_do_burst16) {
-			tmp |= DMA_ESC_BURST;
-		} else {
-			tmp &= ~(DMA_ESC_BURST);
-		}
-		sbus_writel(tmp, esp->dregs + DMA_CSR);
-		break;
-	default:
-		break;
-	};
-	ESP_INTSON(esp->dregs);
-}
-
-/* Reset the ESP chip, _not_ the SCSI bus. */
-static void __init esp_reset_esp(struct esp *esp)
-{
-	u8 family_code, version;
-	int i;
-
-	/* Now reset the ESP chip */
-	esp_cmd(esp, ESP_CMD_RC);
-	esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA);
-	esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA);
-
-	/* Reload the configuration registers */
-	sbus_writeb(esp->cfact, esp->eregs + ESP_CFACT);
-	esp->prev_stp = 0;
-	sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP);
-	esp->prev_soff = 0;
-	sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF);
-	sbus_writeb(esp->neg_defp, esp->eregs + ESP_TIMEO);
-
-	/* This is the only point at which it is reliable to read
-	 * the ID-code for a fast ESP chip variants.
-	 */
-	esp->max_period = ((35 * esp->ccycle) / 1000);
-	if (esp->erev == fast) {
-		version = sbus_readb(esp->eregs + ESP_UID);
-		family_code = (version & 0xf8) >> 3;
-		if (family_code == 0x02)
-			esp->erev = fas236;
-		else if (family_code == 0x0a)
-			esp->erev = fashme; /* Version is usually '5'. */
-		else
-			esp->erev = fas100a;
-		ESPMISC(("esp%d: FAST chip is %s (family=%d, version=%d)\n",
-			 esp->esp_id,
-			 (esp->erev == fas236) ? "fas236" :
-			 ((esp->erev == fas100a) ? "fas100a" :
-			  "fasHME"), family_code, (version & 7)));
-
-		esp->min_period = ((4 * esp->ccycle) / 1000);
-	} else {
-		esp->min_period = ((5 * esp->ccycle) / 1000);
-	}
-	esp->max_period = (esp->max_period + 3)>>2;
-	esp->min_period = (esp->min_period + 3)>>2;
-
-	sbus_writeb(esp->config1, esp->eregs + ESP_CFG1);
-	switch (esp->erev) {
-	case esp100:
-		/* nothing to do */
-		break;
-	case esp100a:
-		sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
-		break;
-	case esp236:
-		/* Slow 236 */
-		sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
-		esp->prev_cfg3 = esp->config3[0];
-		sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
-		break;
-	case fashme:
-		esp->config2 |= (ESP_CONFIG2_HME32 | ESP_CONFIG2_HMEFENAB);
-		/* fallthrough... */
-	case fas236:
-		/* Fast 236 or HME */
-		sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
-		for (i = 0; i < 16; i++) {
-			if (esp->erev == fashme) {
-				u8 cfg3;
-
-				cfg3 = ESP_CONFIG3_FCLOCK | ESP_CONFIG3_OBPUSH;
-				if (esp->scsi_id >= 8)
-					cfg3 |= ESP_CONFIG3_IDBIT3;
-				esp->config3[i] |= cfg3;
-			} else {
-				esp->config3[i] |= ESP_CONFIG3_FCLK;
-			}
-		}
-		esp->prev_cfg3 = esp->config3[0];
-		sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
-		if (esp->erev == fashme) {
-			esp->radelay = 80;
-		} else {
-			if (esp->diff)
-				esp->radelay = 0;
-			else
-				esp->radelay = 96;
-		}
-		break;
-	case fas100a:
-		/* Fast 100a */
-		sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
-		for (i = 0; i < 16; i++)
-			esp->config3[i] |= ESP_CONFIG3_FCLOCK;
-		esp->prev_cfg3 = esp->config3[0];
-		sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
-		esp->radelay = 32;
-		break;
-	default:
-		panic("esp: what could it be... I wonder...");
-		break;
-	};
-
-	/* Eat any bitrot in the chip */
-	sbus_readb(esp->eregs + ESP_INTRPT);
-	udelay(100);
-}
-
-/* This places the ESP into a known state at boot time. */
-static void __init esp_bootup_reset(struct esp *esp)
-{
-	u8 tmp;
-
-	/* Reset the DMA */
-	esp_reset_dma(esp);
-
-	/* Reset the ESP */
-	esp_reset_esp(esp);
-
-	/* Reset the SCSI bus, but tell ESP not to generate an irq */
-	tmp = sbus_readb(esp->eregs + ESP_CFG1);
-	tmp |= ESP_CONFIG1_SRRDISAB;
-	sbus_writeb(tmp, esp->eregs + ESP_CFG1);
-
-	esp_cmd(esp, ESP_CMD_RS);
-	udelay(400);
-
-	sbus_writeb(esp->config1, esp->eregs + ESP_CFG1);
-
-	/* Eat any bitrot in the chip and we are done... */
-	sbus_readb(esp->eregs + ESP_INTRPT);
-}
-
-static int __init esp_find_dvma(struct esp *esp, struct sbus_dev *dma_sdev)
-{
-	struct sbus_dev *sdev = esp->sdev;
-	struct sbus_dma *dma;
-
-	if (dma_sdev != NULL) {
-		for_each_dvma(dma) {
-			if (dma->sdev == dma_sdev)
-				break;
-		}
-	} else {
-		for_each_dvma(dma) {
-			/* If allocated already, can't use it. */
-			if (dma->allocated)
-				continue;
-
-			if (dma->sdev == NULL)
-				break;
-
-			/* If bus + slot are the same and it has the
-			 * correct OBP name, it's ours.
-			 */
-			if (sdev->bus == dma->sdev->bus &&
-			    sdev->slot == dma->sdev->slot &&
-			    (!strcmp(dma->sdev->prom_name, "dma") ||
-			     !strcmp(dma->sdev->prom_name, "espdma")))
-				break;
-		}
-	}
-
-	/* If we don't know how to handle the dvma,
-	 * do not use this device.
-	 */
-	if (dma == NULL) {
-		printk("Cannot find dvma for ESP%d's SCSI\n", esp->esp_id);
-		return -1;
-	}
-	if (dma->allocated) {
-		printk("esp%d: can't use my espdma\n", esp->esp_id);
-		return -1;
-	}
-	dma->allocated = 1;
-	esp->dma = dma;
-	esp->dregs = dma->regs;
-
-	return 0;
-}
-
-static int __init esp_map_regs(struct esp *esp, int hme)
-{
-	struct sbus_dev *sdev = esp->sdev;
-	struct resource *res;
-
-	/* On HME, two reg sets exist, first is DVMA,
-	 * second is ESP registers.
-	 */
-	if (hme)
-		res = &sdev->resource[1];
-	else
-		res = &sdev->resource[0];
-
-	esp->eregs = sbus_ioremap(res, 0, ESP_REG_SIZE, "ESP Registers");
-
-	if (esp->eregs == 0)
-		return -1;
-	return 0;
-}
-
-static int __init esp_map_cmdarea(struct esp *esp)
-{
-	struct sbus_dev *sdev = esp->sdev;
-
-	esp->esp_command = sbus_alloc_consistent(sdev, 16,
-						 &esp->esp_command_dvma);
-	if (esp->esp_command == NULL ||
-	    esp->esp_command_dvma == 0)
-		return -1;
-	return 0;
-}
-
-static int __init esp_register_irq(struct esp *esp)
-{
-	esp->ehost->irq = esp->irq = esp->sdev->irqs[0];
-
-	/* We used to try various overly-clever things to
-	 * reduce the interrupt processing overhead on
-	 * sun4c/sun4m when multiple ESP's shared the
-	 * same IRQ.  It was too complex and messy to
-	 * sanely maintain.
-	 */
-	if (request_irq(esp->ehost->irq, esp_intr,
-			IRQF_SHARED, "ESP SCSI", esp)) {
-		printk("esp%d: Cannot acquire irq line\n",
-		       esp->esp_id);
-		return -1;
-	}
-
-	printk("esp%d: IRQ %d ", esp->esp_id,
-	       esp->ehost->irq);
-
-	return 0;
-}
-
-static void __init esp_get_scsi_id(struct esp *esp)
-{
-	struct sbus_dev *sdev = esp->sdev;
-	struct device_node *dp = sdev->ofdev.node;
-
-	esp->scsi_id = of_getintprop_default(dp,
-					     "initiator-id",
-					     -1);
-	if (esp->scsi_id == -1)
-		esp->scsi_id = of_getintprop_default(dp,
-						     "scsi-initiator-id",
-						     -1);
-	if (esp->scsi_id == -1)
-		esp->scsi_id = (sdev->bus == NULL) ? 7 :
-			of_getintprop_default(sdev->bus->ofdev.node,
-					      "scsi-initiator-id",
-					      7);
-	esp->ehost->this_id = esp->scsi_id;
-	esp->scsi_id_mask = (1 << esp->scsi_id);
-
-}
-
-static void __init esp_get_clock_params(struct esp *esp)
-{
-	struct sbus_dev *sdev = esp->sdev;
-	int prom_node = esp->prom_node;
-	int sbus_prom_node;
-	unsigned int fmhz;
-	u8 ccf;
-
-	if (sdev != NULL && sdev->bus != NULL)
-		sbus_prom_node = sdev->bus->prom_node;
-	else
-		sbus_prom_node = 0;
-
-	/* This is getting messy but it has to be done
-	 * correctly or else you get weird behavior all
-	 * over the place.  We are trying to basically
-	 * figure out three pieces of information.
-	 *
-	 * a) Clock Conversion Factor
-	 *
-	 *    This is a representation of the input
-	 *    crystal clock frequency going into the
-	 *    ESP on this machine.  Any operation whose
-	 *    timing is longer than 400ns depends on this
-	 *    value being correct.  For example, you'll
-	 *    get blips for arbitration/selection during
-	 *    high load or with multiple targets if this
-	 *    is not set correctly.
-	 *
-	 * b) Selection Time-Out
-	 *
-	 *    The ESP isn't very bright and will arbitrate
-	 *    for the bus and try to select a target
-	 *    forever if you let it.  This value tells
-	 *    the ESP when it has taken too long to
-	 *    negotiate and that it should interrupt
-	 *    the CPU so we can see what happened.
-	 *    The value is computed as follows (from
-	 *    NCR/Symbios chip docs).
-	 *
-	 *          (Time Out Period) *  (Input Clock)
-	 *    STO = ----------------------------------
-	 *          (8192) * (Clock Conversion Factor)
-	 *
-	 *    You usually want the time out period to be
-	 *    around 250ms, I think we'll set it a little
-	 *    bit higher to account for fully loaded SCSI
-	 *    bus's and slow devices that don't respond so
-	 *    quickly to selection attempts. (yeah, I know
-	 *    this is out of spec. but there is a lot of
-	 *    buggy pieces of firmware out there so bite me)
-	 *
-	 * c) Imperical constants for synchronous offset
-	 *    and transfer period register values
-	 *
-	 *    This entails the smallest and largest sync
-	 *    period we could ever handle on this ESP.
-	 */
-
-	fmhz = prom_getintdefault(prom_node, "clock-frequency", -1);
-	if (fmhz == -1)
-		fmhz = (!sbus_prom_node) ? 0 :
-			prom_getintdefault(sbus_prom_node, "clock-frequency", -1);
-
-	if (fmhz <= (5000000))
-		ccf = 0;
-	else
-		ccf = (((5000000 - 1) + (fmhz))/(5000000));
-
-	if (!ccf || ccf > 8) {
-		/* If we can't find anything reasonable,
-		 * just assume 20MHZ.  This is the clock
-		 * frequency of the older sun4c's where I've
-		 * been unable to find the clock-frequency
-		 * PROM property.  All other machines provide
-		 * useful values it seems.
-		 */
-		ccf = ESP_CCF_F4;
-		fmhz = (20000000);
-	}
-
-	if (ccf == (ESP_CCF_F7 + 1))
-		esp->cfact = ESP_CCF_F0;
-	else if (ccf == ESP_CCF_NEVER)
-		esp->cfact = ESP_CCF_F2;
-	else
-		esp->cfact = ccf;
-	esp->raw_cfact = ccf;
-
-	esp->cfreq = fmhz;
-	esp->ccycle = ESP_MHZ_TO_CYCLE(fmhz);
-	esp->ctick = ESP_TICK(ccf, esp->ccycle);
-	esp->neg_defp = ESP_NEG_DEFP(fmhz, ccf);
-	esp->sync_defp = SYNC_DEFP_SLOW;
-
-	printk("SCSI ID %d Clk %dMHz CCYC=%d CCF=%d TOut %d ",
-	       esp->scsi_id, (fmhz / 1000000),
-	       (int)esp->ccycle, (int)ccf, (int) esp->neg_defp);
-}
-
-static void __init esp_get_bursts(struct esp *esp, struct sbus_dev *dma)
-{
-	struct sbus_dev *sdev = esp->sdev;
-	u8 bursts;
-
-	bursts = prom_getintdefault(esp->prom_node, "burst-sizes", 0xff);
-
-	if (dma) {
-		u8 tmp = prom_getintdefault(dma->prom_node,
-					    "burst-sizes", 0xff);
-		if (tmp != 0xff)
-			bursts &= tmp;
-	}
-
-	if (sdev->bus) {
-		u8 tmp = prom_getintdefault(sdev->bus->prom_node,
-					    "burst-sizes", 0xff);
-		if (tmp != 0xff)
-			bursts &= tmp;
-	}
-
-	if (bursts == 0xff ||
-	    (bursts & DMA_BURST16) == 0 ||
-	    (bursts & DMA_BURST32) == 0)
-		bursts = (DMA_BURST32 - 1);
-
-	esp->bursts = bursts;
-}
-
-static void __init esp_get_revision(struct esp *esp)
-{
-	u8 tmp;
-
-	esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7));
-	esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY);
-	sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
-
-	tmp = sbus_readb(esp->eregs + ESP_CFG2);
-	tmp &= ~ESP_CONFIG2_MAGIC;
-	if (tmp != (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) {
-		/* If what we write to cfg2 does not come back, cfg2
-		 * is not implemented, therefore this must be a plain
-		 * esp100.
-		 */
-		esp->erev = esp100;
-		printk("NCR53C90(esp100)\n");
-	} else {
-		esp->config2 = 0;
-		esp->prev_cfg3 = esp->config3[0] = 5;
-		sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
-		sbus_writeb(0, esp->eregs + ESP_CFG3);
-		sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
-
-		tmp = sbus_readb(esp->eregs + ESP_CFG3);
-		if (tmp != 5) {
-			/* The cfg2 register is implemented, however
-			 * cfg3 is not, must be esp100a.
-			 */
-			esp->erev = esp100a;
-			printk("NCR53C90A(esp100a)\n");
-		} else {
-			int target;
-
-			for (target = 0; target < 16; target++)
-				esp->config3[target] = 0;
-			esp->prev_cfg3 = 0;
-			sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
-
-			/* All of cfg{1,2,3} implemented, must be one of
-			 * the fas variants, figure out which one.
-			 */
-			if (esp->raw_cfact > ESP_CCF_F5) {
-				esp->erev = fast;
-				esp->sync_defp = SYNC_DEFP_FAST;
-				printk("NCR53C9XF(espfast)\n");
-			} else {
-				esp->erev = esp236;
-				printk("NCR53C9x(esp236)\n");
-			}
-			esp->config2 = 0;
-			sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
-		}
-	}
-}
-
-static void __init esp_init_swstate(struct esp *esp)
-{
-	int i;
-
-	/* Command queues... */
-	esp->current_SC = NULL;
-	esp->disconnected_SC = NULL;
-	esp->issue_SC = NULL;
-
-	/* Target and current command state... */
-	esp->targets_present = 0;
-	esp->resetting_bus = 0;
-	esp->snip = 0;
-
-	init_waitqueue_head(&esp->reset_queue);
-
-	/* Debugging... */
-	for(i = 0; i < 32; i++)
-		esp->espcmdlog[i] = 0;
-	esp->espcmdent = 0;
-
-	/* MSG phase state... */
-	for(i = 0; i < 16; i++) {
-		esp->cur_msgout[i] = 0;
-		esp->cur_msgin[i] = 0;
-	}
-	esp->prevmsgout = esp->prevmsgin = 0;
-	esp->msgout_len = esp->msgin_len = 0;
-
-	/* Clear the one behind caches to hold unmatchable values. */
-	esp->prev_soff = esp->prev_stp = esp->prev_cfg3 = 0xff;
-	esp->prev_hme_dmacsr = 0xffffffff;
-}
-
-static int __init detect_one_esp(struct scsi_host_template *tpnt,
-				 struct device *dev,
-				 struct sbus_dev *esp_dev,
-				 struct sbus_dev *espdma,
-				 struct sbus_bus *sbus,
-				 int hme)
-{
-	static int instance;
-	struct Scsi_Host *esp_host = scsi_host_alloc(tpnt, sizeof(struct esp));
-	struct esp *esp;
-	
-	if (!esp_host)
-		return -ENOMEM;
-
-	if (hme)
-		esp_host->max_id = 16;
-	esp = (struct esp *) esp_host->hostdata;
-	esp->ehost = esp_host;
-	esp->sdev = esp_dev;
-	esp->esp_id = instance;
-	esp->prom_node = esp_dev->prom_node;
-	prom_getstring(esp->prom_node, "name", esp->prom_name,
-		       sizeof(esp->prom_name));
-
-	if (esp_find_dvma(esp, espdma) < 0)
-		goto fail_unlink;
-	if (esp_map_regs(esp, hme) < 0) {
-		printk("ESP registers unmappable");
-		goto fail_dvma_release;
-	}
-	if (esp_map_cmdarea(esp) < 0) {
-		printk("ESP DVMA transport area unmappable");
-		goto fail_unmap_regs;
-	}
-	if (esp_register_irq(esp) < 0)
-		goto fail_unmap_cmdarea;
-
-	esp_get_scsi_id(esp);
-
-	esp->diff = prom_getbool(esp->prom_node, "differential");
-	if (esp->diff)
-		printk("Differential ");
-
-	esp_get_clock_params(esp);
-	esp_get_bursts(esp, espdma);
-	esp_get_revision(esp);
-	esp_init_swstate(esp);
-
-	esp_bootup_reset(esp);
-
-	if (scsi_add_host(esp_host, dev))
-		goto fail_free_irq;
-
-	dev_set_drvdata(&esp_dev->ofdev.dev, esp);
-
-	scsi_scan_host(esp_host);
-	instance++;
-
-	return 0;
-
-fail_free_irq:
-	free_irq(esp->ehost->irq, esp);
-
-fail_unmap_cmdarea:
-	sbus_free_consistent(esp->sdev, 16,
-			     (void *) esp->esp_command,
-			     esp->esp_command_dvma);
-
-fail_unmap_regs:
-	sbus_iounmap(esp->eregs, ESP_REG_SIZE);
-
-fail_dvma_release:
-	esp->dma->allocated = 0;
-
-fail_unlink:
-	scsi_host_put(esp_host);
-	return -1;
-}
-
-/* Detecting ESP chips on the machine.  This is the simple and easy
- * version.
- */
-static int __devexit esp_remove_common(struct esp *esp)
-{
-	unsigned int irq = esp->ehost->irq;
-
-	scsi_remove_host(esp->ehost);
-
-	ESP_INTSOFF(esp->dregs);
-#if 0
-	esp_reset_dma(esp);
-	esp_reset_esp(esp);
-#endif
-
-	free_irq(irq, esp);
-	sbus_free_consistent(esp->sdev, 16,
-			     (void *) esp->esp_command, esp->esp_command_dvma);
-	sbus_iounmap(esp->eregs, ESP_REG_SIZE);
-	esp->dma->allocated = 0;
-
-	scsi_host_put(esp->ehost);
-
-	return 0;
-}
-
-
-#ifdef CONFIG_SUN4
-
-#include <asm/sun4paddr.h>
-
-static struct sbus_dev sun4_esp_dev;
-
-static int __init esp_sun4_probe(struct scsi_host_template *tpnt)
-{
-	if (sun4_esp_physaddr) {
-		memset(&sun4_esp_dev, 0, sizeof(sun4_esp_dev));
-		sun4_esp_dev.reg_addrs[0].phys_addr = sun4_esp_physaddr;
-		sun4_esp_dev.irqs[0] = 4;
-		sun4_esp_dev.resource[0].start = sun4_esp_physaddr;
-		sun4_esp_dev.resource[0].end =
-			sun4_esp_physaddr + ESP_REG_SIZE - 1;
-		sun4_esp_dev.resource[0].flags = IORESOURCE_IO;
-
-		return detect_one_esp(tpnt, NULL,
-				      &sun4_esp_dev, NULL, NULL, 0);
-	}
-	return 0;
-}
-
-static int __devexit esp_sun4_remove(void)
-{
-	struct of_device *dev = &sun4_esp_dev.ofdev;
-	struct esp *esp = dev_get_drvdata(&dev->dev);
-
-	return esp_remove_common(esp);
-}
-
-#else /* !CONFIG_SUN4 */
-
-static int __devinit esp_sbus_probe(struct of_device *dev, const struct of_device_id *match)
-{
-	struct sbus_dev *sdev = to_sbus_device(&dev->dev);
-	struct device_node *dp = dev->node;
-	struct sbus_dev *dma_sdev = NULL;
-	int hme = 0;
-
-	if (dp->parent &&
-	    (!strcmp(dp->parent->name, "espdma") ||
-	     !strcmp(dp->parent->name, "dma")))
-		dma_sdev = sdev->parent;
-	else if (!strcmp(dp->name, "SUNW,fas")) {
-		dma_sdev = sdev;
-		hme = 1;
-	}
-
-	return detect_one_esp(match->data, &dev->dev,
-			      sdev, dma_sdev, sdev->bus, hme);
-}
-
-static int __devexit esp_sbus_remove(struct of_device *dev)
-{
-	struct esp *esp = dev_get_drvdata(&dev->dev);
-
-	return esp_remove_common(esp);
-}
-
-#endif /* !CONFIG_SUN4 */
-
-/* The info function will return whatever useful
- * information the developer sees fit.  If not provided, then
- * the name field will be used instead.
- */
-static const char *esp_info(struct Scsi_Host *host)
-{
-	struct esp *esp;
-
-	esp = (struct esp *) host->hostdata;
-	switch (esp->erev) {
-	case esp100:
-		return "Sparc ESP100 (NCR53C90)";
-	case esp100a:
-		return "Sparc ESP100A (NCR53C90A)";
-	case esp236:
-		return "Sparc ESP236";
-	case fas236:
-		return "Sparc ESP236-FAST";
-	case fashme:
-		return "Sparc ESP366-HME";
-	case fas100a:
-		return "Sparc ESP100A-FAST";
-	default:
-		return "Bogon ESP revision";
-	};
-}
-
-/* From Wolfgang Stanglmeier's NCR scsi driver. */
-struct info_str
-{
-	char *buffer;
-	int length;
-	int offset;
-	int pos;
-};
-
-static void copy_mem_info(struct info_str *info, char *data, int len)
-{
-	if (info->pos + len > info->length)
-		len = info->length - info->pos;
-
-	if (info->pos + len < info->offset) {
-		info->pos += len;
-		return;
-	}
-	if (info->pos < info->offset) {
-		data += (info->offset - info->pos);
-		len  -= (info->offset - info->pos);
-	}
-
-	if (len > 0) {
-		memcpy(info->buffer + info->pos, data, len);
-		info->pos += len;
-	}
-}
-
-static int copy_info(struct info_str *info, char *fmt, ...)
-{
-	va_list args;
-	char buf[81];
-	int len;
-
-	va_start(args, fmt);
-	len = vsprintf(buf, fmt, args);
-	va_end(args);
-
-	copy_mem_info(info, buf, len);
-	return len;
-}
-
-static int esp_host_info(struct esp *esp, char *ptr, off_t offset, int len)
-{
-	struct scsi_device *sdev;
-	struct info_str info;
-	int i;
-
-	info.buffer	= ptr;
-	info.length	= len;
-	info.offset	= offset;
-	info.pos	= 0;
-
-	copy_info(&info, "Sparc ESP Host Adapter:\n");
-	copy_info(&info, "\tPROM node\t\t%08x\n", (unsigned int) esp->prom_node);
-	copy_info(&info, "\tPROM name\t\t%s\n", esp->prom_name);
-	copy_info(&info, "\tESP Model\t\t");
-	switch (esp->erev) {
-	case esp100:
-		copy_info(&info, "ESP100\n");
-		break;
-	case esp100a:
-		copy_info(&info, "ESP100A\n");
-		break;
-	case esp236:
-		copy_info(&info, "ESP236\n");
-		break;
-	case fas236:
-		copy_info(&info, "FAS236\n");
-		break;
-	case fas100a:
-		copy_info(&info, "FAS100A\n");
-		break;
-	case fast:
-		copy_info(&info, "FAST\n");
-		break;
-	case fashme:
-		copy_info(&info, "Happy Meal FAS\n");
-		break;
-	case espunknown:
-	default:
-		copy_info(&info, "Unknown!\n");
-		break;
-	};
-	copy_info(&info, "\tDMA Revision\t\t");
-	switch (esp->dma->revision) {
-	case dvmarev0:
-		copy_info(&info, "Rev 0\n");
-		break;
-	case dvmaesc1:
-		copy_info(&info, "ESC Rev 1\n");
-		break;
-	case dvmarev1:
-		copy_info(&info, "Rev 1\n");
-		break;
-	case dvmarev2:
-		copy_info(&info, "Rev 2\n");
-		break;
-	case dvmarev3:
-		copy_info(&info, "Rev 3\n");
-		break;
-	case dvmarevplus:
-		copy_info(&info, "Rev 1+\n");
-		break;
-	case dvmahme:
-		copy_info(&info, "Rev HME/FAS\n");
-		break;
-	default:
-		copy_info(&info, "Unknown!\n");
-		break;
-	};
-	copy_info(&info, "\tLive Targets\t\t[ ");
-	for (i = 0; i < 15; i++) {
-		if (esp->targets_present & (1 << i))
-			copy_info(&info, "%d ", i);
-	}
-	copy_info(&info, "]\n\n");
-	
-	/* Now describe the state of each existing target. */
-	copy_info(&info, "Target #\tconfig3\t\tSync Capabilities\tDisconnect\tWide\n");
-
-	shost_for_each_device(sdev, esp->ehost) {
-		struct esp_device *esp_dev = sdev->hostdata;
-		uint id = sdev->id;
-
-		if (!(esp->targets_present & (1 << id)))
-			continue;
-
-		copy_info(&info, "%d\t\t", id);
-		copy_info(&info, "%08lx\t", esp->config3[id]);
-		copy_info(&info, "[%02lx,%02lx]\t\t\t",
-			esp_dev->sync_max_offset,
-			esp_dev->sync_min_period);
-		copy_info(&info, "%s\t\t",
-			esp_dev->disconnect ? "yes" : "no");
-		copy_info(&info, "%s\n",
-			(esp->config3[id] & ESP_CONFIG3_EWIDE) ? "yes" : "no");
-	}
-	return info.pos > info.offset? info.pos - info.offset : 0;
-}
-
-/* ESP proc filesystem code. */
-static int esp_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
-			 int length, int inout)
-{
-	struct esp *esp = (struct esp *) host->hostdata;
-
-	if (inout)
-		return -EINVAL; /* not yet */
-
-	if (start)
-		*start = buffer;
-
-	return esp_host_info(esp, buffer, offset, length);
-}
-
-static void esp_get_dmabufs(struct esp *esp, struct scsi_cmnd *sp)
-{
-	if (sp->use_sg == 0) {
-		sp->SCp.this_residual = sp->request_bufflen;
-		sp->SCp.buffer = (struct scatterlist *) sp->request_buffer;
-		sp->SCp.buffers_residual = 0;
-		if (sp->request_bufflen) {
-			sp->SCp.have_data_in = sbus_map_single(esp->sdev, sp->SCp.buffer,
-							       sp->SCp.this_residual,
-							       sp->sc_data_direction);
-			sp->SCp.ptr = (char *) ((unsigned long)sp->SCp.have_data_in);
-		} else {
-			sp->SCp.ptr = NULL;
-		}
-	} else {
-		sp->SCp.buffer = (struct scatterlist *) sp->request_buffer;
-		sp->SCp.buffers_residual = sbus_map_sg(esp->sdev,
-						       sp->SCp.buffer,
-						       sp->use_sg,
-						       sp->sc_data_direction);
-		sp->SCp.this_residual = sg_dma_len(sp->SCp.buffer);
-		sp->SCp.ptr = (char *) ((unsigned long)sg_dma_address(sp->SCp.buffer));
-	}
-}
-
-static void esp_release_dmabufs(struct esp *esp, struct scsi_cmnd *sp)
-{
-	if (sp->use_sg) {
-		sbus_unmap_sg(esp->sdev, sp->request_buffer, sp->use_sg,
-			      sp->sc_data_direction);
-	} else if (sp->request_bufflen) {
-		sbus_unmap_single(esp->sdev,
-				  sp->SCp.have_data_in,
-				  sp->request_bufflen,
-				  sp->sc_data_direction);
-	}
-}
-
-static void esp_restore_pointers(struct esp *esp, struct scsi_cmnd *sp)
-{
-	struct esp_pointers *ep = &esp->data_pointers[sp->device->id];
-
-	sp->SCp.ptr = ep->saved_ptr;
-	sp->SCp.buffer = ep->saved_buffer;
-	sp->SCp.this_residual = ep->saved_this_residual;
-	sp->SCp.buffers_residual = ep->saved_buffers_residual;
-}
-
-static void esp_save_pointers(struct esp *esp, struct scsi_cmnd *sp)
-{
-	struct esp_pointers *ep = &esp->data_pointers[sp->device->id];
-
-	ep->saved_ptr = sp->SCp.ptr;
-	ep->saved_buffer = sp->SCp.buffer;
-	ep->saved_this_residual = sp->SCp.this_residual;
-	ep->saved_buffers_residual = sp->SCp.buffers_residual;
-}
-
-/* Some rules:
- *
- *   1) Never ever panic while something is live on the bus.
- *      If there is to be any chance of syncing the disks this
- *      rule is to be obeyed.
- *
- *   2) Any target that causes a foul condition will no longer
- *      have synchronous transfers done to it, no questions
- *      asked.
- *
- *   3) Keep register accesses to a minimum.  Think about some
- *      day when we have Xbus machines this is running on and
- *      the ESP chip is on the other end of the machine on a
- *      different board from the cpu where this is running.
- */
-
-/* Fire off a command.  We assume the bus is free and that the only
- * case where we could see an interrupt is where we have disconnected
- * commands active and they are trying to reselect us.
- */
-static inline void esp_check_cmd(struct esp *esp, struct scsi_cmnd *sp)
-{
-	switch (sp->cmd_len) {
-	case 6:
-	case 10:
-	case 12:
-		esp->esp_slowcmd = 0;
-		break;
-
-	default:
-		esp->esp_slowcmd = 1;
-		esp->esp_scmdleft = sp->cmd_len;
-		esp->esp_scmdp = &sp->cmnd[0];
-		break;
-	};
-}
-
-static inline void build_sync_nego_msg(struct esp *esp, int period, int offset)
-{
-	esp->cur_msgout[0] = EXTENDED_MESSAGE;
-	esp->cur_msgout[1] = 3;
-	esp->cur_msgout[2] = EXTENDED_SDTR;
-	esp->cur_msgout[3] = period;
-	esp->cur_msgout[4] = offset;
-	esp->msgout_len = 5;
-}
-
-/* SIZE is in bits, currently HME only supports 16 bit wide transfers. */
-static inline void build_wide_nego_msg(struct esp *esp, int size)
-{
-	esp->cur_msgout[0] = EXTENDED_MESSAGE;
-	esp->cur_msgout[1] = 2;
-	esp->cur_msgout[2] = EXTENDED_WDTR;
-	switch (size) {
-	case 32:
-		esp->cur_msgout[3] = 2;
-		break;
-	case 16:
-		esp->cur_msgout[3] = 1;
-		break;
-	case 8:
-	default:
-		esp->cur_msgout[3] = 0;
-		break;
-	};
-
-	esp->msgout_len = 4;
-}
-
-static void esp_exec_cmd(struct esp *esp)
-{
-	struct scsi_cmnd *SCptr;
-	struct scsi_device *SDptr;
-	struct esp_device *esp_dev;
-	volatile u8 *cmdp = esp->esp_command;
-	u8 the_esp_command;
-	int lun, target;
-	int i;
-
-	/* Hold off if we have disconnected commands and
-	 * an IRQ is showing...
-	 */
-	if (esp->disconnected_SC && ESP_IRQ_P(esp->dregs))
-		return;
-
-	/* Grab first member of the issue queue. */
-	SCptr = esp->current_SC = remove_first_SC(&esp->issue_SC);
-
-	/* Safe to panic here because current_SC is null. */
-	if (!SCptr)
-		panic("esp: esp_exec_cmd and issue queue is NULL");
-
-	SDptr = SCptr->device;
-	esp_dev = SDptr->hostdata;
-	lun = SCptr->device->lun;
-	target = SCptr->device->id;
-
-	esp->snip = 0;
-	esp->msgout_len = 0;
-
-	/* Send it out whole, or piece by piece?   The ESP
-	 * only knows how to automatically send out 6, 10,
-	 * and 12 byte commands.  I used to think that the
-	 * Linux SCSI code would never throw anything other
-	 * than that to us, but then again there is the
-	 * SCSI generic driver which can send us anything.
-	 */
-	esp_check_cmd(esp, SCptr);
-
-	/* If arbitration/selection is successful, the ESP will leave
-	 * ATN asserted, causing the target to go into message out
-	 * phase.  The ESP will feed the target the identify and then
-	 * the target can only legally go to one of command,
-	 * datain/out, status, or message in phase, or stay in message
-	 * out phase (should we be trying to send a sync negotiation
-	 * message after the identify).  It is not allowed to drop
-	 * BSY, but some buggy targets do and we check for this
-	 * condition in the selection complete code.  Most of the time
-	 * we'll make the command bytes available to the ESP and it
-	 * will not interrupt us until it finishes command phase, we
-	 * cannot do this for command sizes the ESP does not
-	 * understand and in this case we'll get interrupted right
-	 * when the target goes into command phase.
-	 *
-	 * It is absolutely _illegal_ in the presence of SCSI-2 devices
-	 * to use the ESP select w/o ATN command.  When SCSI-2 devices are
-	 * present on the bus we _must_ always go straight to message out
-	 * phase with an identify message for the target.  Being that
-	 * selection attempts in SCSI-1 w/o ATN was an option, doing SCSI-2
-	 * selections should not confuse SCSI-1 we hope.
-	 */
-
-	if (esp_dev->sync) {
-		/* this targets sync is known */
-#ifndef __sparc_v9__
-do_sync_known:
-#endif
-		if (esp_dev->disconnect)
-			*cmdp++ = IDENTIFY(1, lun);
-		else
-			*cmdp++ = IDENTIFY(0, lun);
-
-		if (esp->esp_slowcmd) {
-			the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA);
-			esp_advance_phase(SCptr, in_slct_stop);
-		} else {
-			the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA);
-			esp_advance_phase(SCptr, in_slct_norm);
-		}
-	} else if (!(esp->targets_present & (1<<target)) || !(esp_dev->disconnect)) {
-		/* After the bootup SCSI code sends both the
-		 * TEST_UNIT_READY and INQUIRY commands we want
-		 * to at least attempt allowing the device to
-		 * disconnect.
-		 */
-		ESPMISC(("esp: Selecting device for first time. target=%d "
-			 "lun=%d\n", target, SCptr->device->lun));
-		if (!SDptr->borken && !esp_dev->disconnect)
-			esp_dev->disconnect = 1;
-
-		*cmdp++ = IDENTIFY(0, lun);
-		esp->prevmsgout = NOP;
-		esp_advance_phase(SCptr, in_slct_norm);
-		the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA);
-
-		/* Take no chances... */
-		esp_dev->sync_max_offset = 0;
-		esp_dev->sync_min_period = 0;
-	} else {
-		/* Sorry, I have had way too many problems with
-		 * various CDROM devices on ESP. -DaveM
-		 */
-		int cdrom_hwbug_wkaround = 0;
-
-#ifndef __sparc_v9__
-		/* Never allow disconnects or synchronous transfers on
-		 * SparcStation1 and SparcStation1+.  Allowing those
-		 * to be enabled seems to lockup the machine completely.
-		 */
-		if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
-		    (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
-			/* But we are nice and allow tapes and removable
-			 * disks (but not CDROMs) to disconnect.
-			 */
-			if(SDptr->type == TYPE_TAPE ||
-			   (SDptr->type != TYPE_ROM && SDptr->removable))
-				esp_dev->disconnect = 1;
-			else
-				esp_dev->disconnect = 0;
-			esp_dev->sync_max_offset = 0;
-			esp_dev->sync_min_period = 0;
-			esp_dev->sync = 1;
-			esp->snip = 0;
-			goto do_sync_known;
-		}
-#endif /* !(__sparc_v9__) */
-
-		/* We've talked to this guy before,
-		 * but never negotiated.  Let's try,
-		 * need to attempt WIDE first, before
-		 * sync nego, as per SCSI 2 standard.
-		 */
-		if (esp->erev == fashme && !esp_dev->wide) {
-			if (!SDptr->borken &&
-			   SDptr->type != TYPE_ROM &&
-			   SDptr->removable == 0) {
-				build_wide_nego_msg(esp, 16);
-				esp_dev->wide = 1;
-				esp->wnip = 1;
-				goto after_nego_msg_built;
-			} else {
-				esp_dev->wide = 1;
-				/* Fall through and try sync. */
-			}
-		}
-
-		if (!SDptr->borken) {
-			if ((SDptr->type == TYPE_ROM)) {
-				/* Nice try sucker... */
-				ESPMISC(("esp%d: Disabling sync for buggy "
-					 "CDROM.\n", esp->esp_id));
-				cdrom_hwbug_wkaround = 1;
-				build_sync_nego_msg(esp, 0, 0);
-			} else if (SDptr->removable != 0) {
-				ESPMISC(("esp%d: Not negotiating sync/wide but "
-					 "allowing disconnect for removable media.\n",
-					 esp->esp_id));
-				build_sync_nego_msg(esp, 0, 0);
-			} else {
-				build_sync_nego_msg(esp, esp->sync_defp, 15);
-			}
-		} else {
-			build_sync_nego_msg(esp, 0, 0);
-		}
-		esp_dev->sync = 1;
-		esp->snip = 1;
-
-after_nego_msg_built:
-		/* A fix for broken SCSI1 targets, when they disconnect
-		 * they lock up the bus and confuse ESP.  So disallow
-		 * disconnects for SCSI1 targets for now until we
-		 * find a better fix.
-		 *
-		 * Addendum: This is funny, I figured out what was going
-		 *           on.  The blotzed SCSI1 target would disconnect,
-		 *           one of the other SCSI2 targets or both would be
-		 *           disconnected as well.  The SCSI1 target would
-		 *           stay disconnected long enough that we start
-		 *           up a command on one of the SCSI2 targets.  As
-		 *           the ESP is arbitrating for the bus the SCSI1
-		 *           target begins to arbitrate as well to reselect
-		 *           the ESP.  The SCSI1 target refuses to drop it's
-		 *           ID bit on the data bus even though the ESP is
-		 *           at ID 7 and is the obvious winner for any
-		 *           arbitration.  The ESP is a poor sport and refuses
-		 *           to lose arbitration, it will continue indefinitely
-		 *           trying to arbitrate for the bus and can only be
-		 *           stopped via a chip reset or SCSI bus reset.
-		 *           Therefore _no_ disconnects for SCSI1 targets
-		 *           thank you very much. ;-)
-		 */
-		if(((SDptr->scsi_level < 3) &&
-		    (SDptr->type != TYPE_TAPE) &&
-		    SDptr->removable == 0) ||
-		    cdrom_hwbug_wkaround || SDptr->borken) {
-			ESPMISC((KERN_INFO "esp%d: Disabling DISCONNECT for target %d "
-				 "lun %d\n", esp->esp_id, SCptr->device->id, SCptr->device->lun));
-			esp_dev->disconnect = 0;
-			*cmdp++ = IDENTIFY(0, lun);
-		} else {
-			*cmdp++ = IDENTIFY(1, lun);
-		}
-
-		/* ESP fifo is only so big...
-		 * Make this look like a slow command.
-		 */
-		esp->esp_slowcmd = 1;
-		esp->esp_scmdleft = SCptr->cmd_len;
-		esp->esp_scmdp = &SCptr->cmnd[0];
-
-		the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA);
-		esp_advance_phase(SCptr, in_slct_msg);
-	}
-
-	if (!esp->esp_slowcmd)
-		for (i = 0; i < SCptr->cmd_len; i++)
-			*cmdp++ = SCptr->cmnd[i];
-
-	/* HME sucks... */
-	if (esp->erev == fashme)
-		sbus_writeb((target & 0xf) | (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT),
-			    esp->eregs + ESP_BUSID);
-	else
-		sbus_writeb(target & 7, esp->eregs + ESP_BUSID);
-	if (esp->prev_soff != esp_dev->sync_max_offset ||
-	    esp->prev_stp  != esp_dev->sync_min_period ||
-	    (esp->erev > esp100a &&
-	     esp->prev_cfg3 != esp->config3[target])) {
-		esp->prev_soff = esp_dev->sync_max_offset;
-		esp->prev_stp = esp_dev->sync_min_period;
-		sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF);
-		sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP);
-		if (esp->erev > esp100a) {
-			esp->prev_cfg3 = esp->config3[target];
-			sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
-		}
-	}
-	i = (cmdp - esp->esp_command);
-
-	if (esp->erev == fashme) {
-		esp_cmd(esp, ESP_CMD_FLUSH); /* Grrr! */
-
-		/* Set up the DMA and HME counters */
-		sbus_writeb(i, esp->eregs + ESP_TCLOW);
-		sbus_writeb(0, esp->eregs + ESP_TCMED);
-		sbus_writeb(0, esp->eregs + FAS_RLO);
-		sbus_writeb(0, esp->eregs + FAS_RHI);
-		esp_cmd(esp, the_esp_command);
-
-		/* Talk about touchy hardware... */
-		esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr |
-					 (DMA_SCSI_DISAB | DMA_ENABLE)) &
-					~(DMA_ST_WRITE));
-		sbus_writel(16, esp->dregs + DMA_COUNT);
-		sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR);
-		sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR);
-	} else {
-		u32 tmp;
-
-		/* Set up the DMA and ESP counters */
-		sbus_writeb(i, esp->eregs + ESP_TCLOW);
-		sbus_writeb(0, esp->eregs + ESP_TCMED);
-		tmp = sbus_readl(esp->dregs + DMA_CSR);
-		tmp &= ~DMA_ST_WRITE;
-		tmp |= DMA_ENABLE;
-		sbus_writel(tmp, esp->dregs + DMA_CSR);
-		if (esp->dma->revision == dvmaesc1) {
-			if (i) /* Workaround ESC gate array SBUS rerun bug. */
-				sbus_writel(PAGE_SIZE, esp->dregs + DMA_COUNT);
-		}
-		sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR);
-
-		/* Tell ESP to "go". */
-		esp_cmd(esp, the_esp_command);
-	}
-}
-
-/* Queue a SCSI command delivered from the mid-level Linux SCSI code. */
-static int esp_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
-{
-	struct esp *esp;
-
-	/* Set up func ptr and initial driver cmd-phase. */
-	SCpnt->scsi_done = done;
-	SCpnt->SCp.phase = not_issued;
-
-	/* We use the scratch area. */
-	ESPQUEUE(("esp_queue: target=%d lun=%d ", SCpnt->device->id, SCpnt->device->lun));
-	ESPDISC(("N<%02x,%02x>", SCpnt->device->id, SCpnt->device->lun));
-
-	esp = (struct esp *) SCpnt->device->host->hostdata;
-	esp_get_dmabufs(esp, SCpnt);
-	esp_save_pointers(esp, SCpnt); /* FIXME for tag queueing */
-
-	SCpnt->SCp.Status           = CHECK_CONDITION;
-	SCpnt->SCp.Message          = 0xff;
-	SCpnt->SCp.sent_command     = 0;
-
-	/* Place into our queue. */
-	if (SCpnt->cmnd[0] == REQUEST_SENSE) {
-		ESPQUEUE(("RQSENSE\n"));
-		prepend_SC(&esp->issue_SC, SCpnt);
-	} else {
-		ESPQUEUE(("\n"));
-		append_SC(&esp->issue_SC, SCpnt);
-	}
-
-	/* Run it now if we can. */
-	if (!esp->current_SC && !esp->resetting_bus)
-		esp_exec_cmd(esp);
-
-	return 0;
-}
-
-/* Dump driver state. */
-static void esp_dump_cmd(struct scsi_cmnd *SCptr)
-{
-	ESPLOG(("[tgt<%02x> lun<%02x> "
-		"pphase<%s> cphase<%s>]",
-		SCptr->device->id, SCptr->device->lun,
-		phase_string(SCptr->SCp.sent_command),
-		phase_string(SCptr->SCp.phase)));
-}
-
-static void esp_dump_state(struct esp *esp)
-{
-	struct scsi_cmnd *SCptr = esp->current_SC;
-#ifdef DEBUG_ESP_CMDS
-	int i;
-#endif
-
-	ESPLOG(("esp%d: dumping state\n", esp->esp_id));
-	ESPLOG(("esp%d: dma -- cond_reg<%08x> addr<%08x>\n",
-		esp->esp_id,
-		sbus_readl(esp->dregs + DMA_CSR),
-		sbus_readl(esp->dregs + DMA_ADDR)));
-	ESPLOG(("esp%d: SW [sreg<%02x> sstep<%02x> ireg<%02x>]\n",
-		esp->esp_id, esp->sreg, esp->seqreg, esp->ireg));
-	ESPLOG(("esp%d: HW reread [sreg<%02x> sstep<%02x> ireg<%02x>]\n",
-		esp->esp_id,
-		sbus_readb(esp->eregs + ESP_STATUS),
-		sbus_readb(esp->eregs + ESP_SSTEP),
-		sbus_readb(esp->eregs + ESP_INTRPT)));
-#ifdef DEBUG_ESP_CMDS
-	printk("esp%d: last ESP cmds [", esp->esp_id);
-	i = (esp->espcmdent - 1) & 31;
-	printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">");
-	i = (i - 1) & 31;
-	printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">");
-	i = (i - 1) & 31;
-	printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">");
-	i = (i - 1) & 31;
-	printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">");
-	printk("]\n");
-#endif /* (DEBUG_ESP_CMDS) */
-
-	if (SCptr) {
-		ESPLOG(("esp%d: current command ", esp->esp_id));
-		esp_dump_cmd(SCptr);
-	}
-	ESPLOG(("\n"));
-	SCptr = esp->disconnected_SC;
-	ESPLOG(("esp%d: disconnected ", esp->esp_id));
-	while (SCptr) {
-		esp_dump_cmd(SCptr);
-		SCptr = (struct scsi_cmnd *) SCptr->host_scribble;
-	}
-	ESPLOG(("\n"));
-}
-
-/* Abort a command.  The host_lock is acquired by caller. */
-static int esp_abort(struct scsi_cmnd *SCptr)
-{
-	struct esp *esp = (struct esp *) SCptr->device->host->hostdata;
-	int don;
-
-	ESPLOG(("esp%d: Aborting command\n", esp->esp_id));
-	esp_dump_state(esp);
-
-	/* Wheee, if this is the current command on the bus, the
-	 * best we can do is assert ATN and wait for msgout phase.
-	 * This should even fix a hung SCSI bus when we lose state
-	 * in the driver and timeout because the eventual phase change
-	 * will cause the ESP to (eventually) give an interrupt.
-	 */
-	if (esp->current_SC == SCptr) {
-		esp->cur_msgout[0] = ABORT;
-		esp->msgout_len = 1;
-		esp->msgout_ctr = 0;
-		esp_cmd(esp, ESP_CMD_SATN);
-		return SUCCESS;
-	}
-
-	/* If it is still in the issue queue then we can safely
-	 * call the completion routine and report abort success.
-	 */
-	don = (sbus_readl(esp->dregs + DMA_CSR) & DMA_INT_ENAB);
-	if (don) {
-		ESP_INTSOFF(esp->dregs);
-	}
-	if (esp->issue_SC) {
-		struct scsi_cmnd **prev, *this;
-		for (prev = (&esp->issue_SC), this = esp->issue_SC;
-		     this != NULL;
-		     prev = (struct scsi_cmnd **) &(this->host_scribble),
-			     this = (struct scsi_cmnd *) this->host_scribble) {
-
-			if (this == SCptr) {
-				*prev = (struct scsi_cmnd *) this->host_scribble;
-				this->host_scribble = NULL;
-
-				esp_release_dmabufs(esp, this);
-				this->result = DID_ABORT << 16;
-				this->scsi_done(this);
-
-				if (don)
-					ESP_INTSON(esp->dregs);
-
-				return SUCCESS;
-			}
-		}
-	}
-
-	/* Yuck, the command to abort is disconnected, it is not
-	 * worth trying to abort it now if something else is live
-	 * on the bus at this time.  So, we let the SCSI code wait
-	 * a little bit and try again later.
-	 */
-	if (esp->current_SC) {
-		if (don)
-			ESP_INTSON(esp->dregs);
-		return FAILED;
-	}
-
-	/* It's disconnected, we have to reconnect to re-establish
-	 * the nexus and tell the device to abort.  However, we really
-	 * cannot 'reconnect' per se.  Don't try to be fancy, just
-	 * indicate failure, which causes our caller to reset the whole
-	 * bus.
-	 */
-
-	if (don)
-		ESP_INTSON(esp->dregs);
-
-	return FAILED;
-}
-
-/* We've sent ESP_CMD_RS to the ESP, the interrupt had just
- * arrived indicating the end of the SCSI bus reset.  Our job
- * is to clean out the command queues and begin re-execution
- * of SCSI commands once more.
- */
-static int esp_finish_reset(struct esp *esp)
-{
-	struct scsi_cmnd *sp = esp->current_SC;
-
-	/* Clean up currently executing command, if any. */
-	if (sp != NULL) {
-		esp->current_SC = NULL;
-
-		esp_release_dmabufs(esp, sp);
-		sp->result = (DID_RESET << 16);
-
-		sp->scsi_done(sp);
-	}
-
-	/* Clean up disconnected queue, they have been invalidated
-	 * by the bus reset.
-	 */
-	if (esp->disconnected_SC) {
-		while ((sp = remove_first_SC(&esp->disconnected_SC)) != NULL) {
-			esp_release_dmabufs(esp, sp);
-			sp->result = (DID_RESET << 16);
-
-			sp->scsi_done(sp);
-		}
-	}
-
-	/* SCSI bus reset is complete. */
-	esp->resetting_bus = 0;
-	wake_up(&esp->reset_queue);
-
-	/* Ok, now it is safe to get commands going once more. */
-	if (esp->issue_SC)
-		esp_exec_cmd(esp);
-
-	return do_intr_end;
-}
-
-static int esp_do_resetbus(struct esp *esp)
-{
-	ESPLOG(("esp%d: Resetting scsi bus\n", esp->esp_id));
-	esp->resetting_bus = 1;
-	esp_cmd(esp, ESP_CMD_RS);
-
-	return do_intr_end;
-}
-
-/* Reset ESP chip, reset hanging bus, then kill active and
- * disconnected commands for targets without soft reset.
- *
- * The host_lock is acquired by caller.
- */
-static int esp_reset(struct scsi_cmnd *SCptr)
-{
-	struct esp *esp = (struct esp *) SCptr->device->host->hostdata;
-
-	spin_lock_irq(esp->ehost->host_lock);
-	(void) esp_do_resetbus(esp);
-	spin_unlock_irq(esp->ehost->host_lock);
-
-	wait_event(esp->reset_queue, (esp->resetting_bus == 0));
-
-	return SUCCESS;
-}
-
-/* Internal ESP done function. */
-static void esp_done(struct esp *esp, int error)
-{
-	struct scsi_cmnd *done_SC = esp->current_SC;
-
-	esp->current_SC = NULL;
-
-	esp_release_dmabufs(esp, done_SC);
-	done_SC->result = error;
-
-	done_SC->scsi_done(done_SC);
-
-	/* Bus is free, issue any commands in the queue. */
-	if (esp->issue_SC && !esp->current_SC)
-		esp_exec_cmd(esp);
-
-}
-
-/* Wheee, ESP interrupt engine. */  
-
-/* Forward declarations. */
-static int esp_do_phase_determine(struct esp *esp);
-static int esp_do_data_finale(struct esp *esp);
-static int esp_select_complete(struct esp *esp);
-static int esp_do_status(struct esp *esp);
-static int esp_do_msgin(struct esp *esp);
-static int esp_do_msgindone(struct esp *esp);
-static int esp_do_msgout(struct esp *esp);
-static int esp_do_cmdbegin(struct esp *esp);
-
-#define sreg_datainp(__sreg)  (((__sreg) & ESP_STAT_PMASK) == ESP_DIP)
-#define sreg_dataoutp(__sreg) (((__sreg) & ESP_STAT_PMASK) == ESP_DOP)
-
-/* Read any bytes found in the FAS366 fifo, storing them into
- * the ESP driver software state structure.
- */
-static void hme_fifo_read(struct esp *esp)
-{
-	u8 count = 0;
-	u8 status = esp->sreg;
-
-	/* Cannot safely frob the fifo for these following cases, but
-	 * we must always read the fifo when the reselect interrupt
-	 * is pending.
-	 */
-	if (((esp->ireg & ESP_INTR_RSEL) == 0)	&&
-	    (sreg_datainp(status)		||
-	     sreg_dataoutp(status)		||
-	     (esp->current_SC &&
-	      esp->current_SC->SCp.phase == in_data_done))) {
-		ESPHME(("<wkaround_skipped>"));
-	} else {
-		unsigned long fcnt = sbus_readb(esp->eregs + ESP_FFLAGS) & ESP_FF_FBYTES;
-
-		/* The HME stores bytes in multiples of 2 in the fifo. */
-		ESPHME(("hme_fifo[fcnt=%d", (int)fcnt));
-		while (fcnt) {
-			esp->hme_fifo_workaround_buffer[count++] =
-				sbus_readb(esp->eregs + ESP_FDATA);
-			esp->hme_fifo_workaround_buffer[count++] =
-				sbus_readb(esp->eregs + ESP_FDATA);
-			ESPHME(("<%02x,%02x>", esp->hme_fifo_workaround_buffer[count-2], esp->hme_fifo_workaround_buffer[count-1]));
-			fcnt--;
-		}
-		if (sbus_readb(esp->eregs + ESP_STATUS2) & ESP_STAT2_F1BYTE) {
-			ESPHME(("<poke_byte>"));
-			sbus_writeb(0, esp->eregs + ESP_FDATA);
-			esp->hme_fifo_workaround_buffer[count++] =
-				sbus_readb(esp->eregs + ESP_FDATA);
-			ESPHME(("<%02x,0x00>", esp->hme_fifo_workaround_buffer[count-1]));
-			ESPHME(("CMD_FLUSH"));
-			esp_cmd(esp, ESP_CMD_FLUSH);
-		} else {
-			ESPHME(("no_xtra_byte"));
-		}
-	}
-	ESPHME(("wkarnd_cnt=%d]", (int)count));
-	esp->hme_fifo_workaround_count = count;
-}
-
-static inline void hme_fifo_push(struct esp *esp, u8 *bytes, u8 count)
-{
-	esp_cmd(esp, ESP_CMD_FLUSH);
-	while (count) {
-		u8 tmp = *bytes++;
-		sbus_writeb(tmp, esp->eregs + ESP_FDATA);
-		sbus_writeb(0, esp->eregs + ESP_FDATA);
-		count--;
-	}
-}
-
-/* We try to avoid some interrupts by jumping ahead and see if the ESP
- * has gotten far enough yet.  Hence the following.
- */
-static inline int skipahead1(struct esp *esp, struct scsi_cmnd *scp,
-			     int prev_phase, int new_phase)
-{
-	if (scp->SCp.sent_command != prev_phase)
-		return 0;
-	if (ESP_IRQ_P(esp->dregs)) {
-		/* Yes, we are able to save an interrupt. */
-		if (esp->erev == fashme)
-			esp->sreg2 = sbus_readb(esp->eregs + ESP_STATUS2);
-		esp->sreg = (sbus_readb(esp->eregs + ESP_STATUS) & ~(ESP_STAT_INTR));
-		esp->ireg = sbus_readb(esp->eregs + ESP_INTRPT);
-		if (esp->erev == fashme) {
-			/* This chip is really losing. */
-			ESPHME(("HME["));
-			/* Must latch fifo before reading the interrupt
-			 * register else garbage ends up in the FIFO
-			 * which confuses the driver utterly.
-			 * Happy Meal indeed....
-			 */
-			ESPHME(("fifo_workaround]"));
-			if (!(esp->sreg2 & ESP_STAT2_FEMPTY) ||
-			    (esp->sreg2 & ESP_STAT2_F1BYTE))
-				hme_fifo_read(esp);
-		}
-		if (!(esp->ireg & ESP_INTR_SR))
-			return 0;
-		else
-			return do_reset_complete;
-	}
-	/* Ho hum, target is taking forever... */
-	scp->SCp.sent_command = new_phase; /* so we don't recurse... */
-	return do_intr_end;
-}
-
-static inline int skipahead2(struct esp *esp, struct scsi_cmnd *scp,
-			     int prev_phase1, int prev_phase2, int new_phase)
-{
-	if (scp->SCp.sent_command != prev_phase1 &&
-	    scp->SCp.sent_command != prev_phase2)
-		return 0;
-	if (ESP_IRQ_P(esp->dregs)) {
-		/* Yes, we are able to save an interrupt. */
-		if (esp->erev == fashme)
-			esp->sreg2 = sbus_readb(esp->eregs + ESP_STATUS2);
-		esp->sreg = (sbus_readb(esp->eregs + ESP_STATUS) & ~(ESP_STAT_INTR));
-		esp->ireg = sbus_readb(esp->eregs + ESP_INTRPT);
-		if (esp->erev == fashme) {
-			/* This chip is really losing. */
-			ESPHME(("HME["));
-
-			/* Must latch fifo before reading the interrupt
-			 * register else garbage ends up in the FIFO
-			 * which confuses the driver utterly.
-			 * Happy Meal indeed....
-			 */
-			ESPHME(("fifo_workaround]"));
-			if (!(esp->sreg2 & ESP_STAT2_FEMPTY) ||
-			    (esp->sreg2 & ESP_STAT2_F1BYTE))
-				hme_fifo_read(esp);
-		}
-		if (!(esp->ireg & ESP_INTR_SR))
-			return 0;
-		else
-			return do_reset_complete;
-	}
-	/* Ho hum, target is taking forever... */
-	scp->SCp.sent_command = new_phase; /* so we don't recurse... */
-	return do_intr_end;
-}
-
-/* Now some dma helpers. */
-static void dma_setup(struct esp *esp, __u32 addr, int count, int write)
-{
-	u32 nreg = sbus_readl(esp->dregs + DMA_CSR);
-
-	if (write)
-		nreg |= DMA_ST_WRITE;
-	else
-		nreg &= ~(DMA_ST_WRITE);
-	nreg |= DMA_ENABLE;
-	sbus_writel(nreg, esp->dregs + DMA_CSR);
-	if (esp->dma->revision == dvmaesc1) {
-		/* This ESC gate array sucks! */
-		__u32 src = addr;
-		__u32 dest = src + count;
-
-		if (dest & (PAGE_SIZE - 1))
-			count = PAGE_ALIGN(count);
-		sbus_writel(count, esp->dregs + DMA_COUNT);
-	}
-	sbus_writel(addr, esp->dregs + DMA_ADDR);
-}
-
-static void dma_drain(struct esp *esp)
-{
-	u32 tmp;
-
-	if (esp->dma->revision == dvmahme)
-		return;
-	if ((tmp = sbus_readl(esp->dregs + DMA_CSR)) & DMA_FIFO_ISDRAIN) {
-		switch (esp->dma->revision) {
-		default:
-			tmp |= DMA_FIFO_STDRAIN;
-			sbus_writel(tmp, esp->dregs + DMA_CSR);
-
-		case dvmarev3:
-		case dvmaesc1:
-			while (sbus_readl(esp->dregs + DMA_CSR) & DMA_FIFO_ISDRAIN)
-				udelay(1);
-		};
-	}
-}
-
-static void dma_invalidate(struct esp *esp)
-{
-	u32 tmp;
-
-	if (esp->dma->revision == dvmahme) {
-		sbus_writel(DMA_RST_SCSI, esp->dregs + DMA_CSR);
-
-		esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr |
-					 (DMA_PARITY_OFF | DMA_2CLKS |
-					  DMA_SCSI_DISAB | DMA_INT_ENAB)) &
-					~(DMA_ST_WRITE | DMA_ENABLE));
-
-		sbus_writel(0, esp->dregs + DMA_CSR);
-		sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR);
-
-		/* This is necessary to avoid having the SCSI channel
-		 * engine lock up on us.
-		 */
-		sbus_writel(0, esp->dregs + DMA_ADDR);
-	} else {
-		while ((tmp = sbus_readl(esp->dregs + DMA_CSR)) & DMA_PEND_READ)
-			udelay(1);
-
-		tmp &= ~(DMA_ENABLE | DMA_ST_WRITE | DMA_BCNT_ENAB);
-		tmp |= DMA_FIFO_INV;
-		sbus_writel(tmp, esp->dregs + DMA_CSR);
-		tmp &= ~DMA_FIFO_INV;
-		sbus_writel(tmp, esp->dregs + DMA_CSR);
-	}
-}
-
-static inline void dma_flashclear(struct esp *esp)
-{
-	dma_drain(esp);
-	dma_invalidate(esp);
-}
-
-static int dma_can_transfer(struct esp *esp, struct scsi_cmnd *sp)
-{
-	__u32 base, end, sz;
-
-	if (esp->dma->revision == dvmarev3) {
-		sz = sp->SCp.this_residual;
-		if (sz > 0x1000000)
-			sz = 0x1000000;
-	} else {
-		base = ((__u32)((unsigned long)sp->SCp.ptr));
-		base &= (0x1000000 - 1);
-		end = (base + sp->SCp.this_residual);
-		if (end > 0x1000000)
-			end = 0x1000000;
-		sz = (end - base);
-	}
-	return sz;
-}
-
-/* Misc. esp helper macros. */
-#define esp_setcount(__eregs, __cnt, __hme) \
-	sbus_writeb(((__cnt)&0xff), (__eregs) + ESP_TCLOW); \
-	sbus_writeb((((__cnt)>>8)&0xff), (__eregs) + ESP_TCMED); \
-	if (__hme) { \
-		sbus_writeb((((__cnt)>>16)&0xff), (__eregs) + FAS_RLO); \
-		sbus_writeb(0, (__eregs) + FAS_RHI); \
-	}
-
-#define esp_getcount(__eregs, __hme) \
-	((sbus_readb((__eregs) + ESP_TCLOW)&0xff) | \
-	 ((sbus_readb((__eregs) + ESP_TCMED)&0xff) << 8) | \
-         ((__hme) ? sbus_readb((__eregs) + FAS_RLO) << 16 : 0))
-
-#define fcount(__esp) \
-	(((__esp)->erev == fashme) ? \
-	  (__esp)->hme_fifo_workaround_count : \
-	  sbus_readb(((__esp)->eregs) + ESP_FFLAGS) & ESP_FF_FBYTES)
-
-#define fnzero(__esp) \
-	(((__esp)->erev == fashme) ? 0 : \
-	 sbus_readb(((__esp)->eregs) + ESP_FFLAGS) & ESP_FF_ONOTZERO)
-
-/* XXX speculative nops unnecessary when continuing amidst a data phase
- * XXX even on esp100!!!  another case of flooding the bus with I/O reg
- * XXX writes...
- */
-#define esp_maybe_nop(__esp) \
-	if ((__esp)->erev == esp100) \
-		esp_cmd((__esp), ESP_CMD_NULL)
-
-#define sreg_to_dataphase(__sreg) \
-	((((__sreg) & ESP_STAT_PMASK) == ESP_DOP) ? in_dataout : in_datain)
-
-/* The ESP100 when in synchronous data phase, can mistake a long final
- * REQ pulse from the target as an extra byte, it places whatever is on
- * the data lines into the fifo.  For now, we will assume when this
- * happens that the target is a bit quirky and we don't want to
- * be talking synchronously to it anyways.  Regardless, we need to
- * tell the ESP to eat the extraneous byte so that we can proceed
- * to the next phase.
- */
-static int esp100_sync_hwbug(struct esp *esp, struct scsi_cmnd *sp, int fifocnt)
-{
-	/* Do not touch this piece of code. */
-	if ((!(esp->erev == esp100)) ||
-	    (!(sreg_datainp((esp->sreg = sbus_readb(esp->eregs + ESP_STATUS))) &&
-	       !fifocnt) &&
-	     !(sreg_dataoutp(esp->sreg) && !fnzero(esp)))) {
-		if (sp->SCp.phase == in_dataout)
-			esp_cmd(esp, ESP_CMD_FLUSH);
-		return 0;
-	} else {
-		/* Async mode for this guy. */
-		build_sync_nego_msg(esp, 0, 0);
-
-		/* Ack the bogus byte, but set ATN first. */
-		esp_cmd(esp, ESP_CMD_SATN);
-		esp_cmd(esp, ESP_CMD_MOK);
-		return 1;
-	}
-}
-
-/* This closes the window during a selection with a reselect pending, because
- * we use DMA for the selection process the FIFO should hold the correct
- * contents if we get reselected during this process.  So we just need to
- * ack the possible illegal cmd interrupt pending on the esp100.
- */
-static inline int esp100_reconnect_hwbug(struct esp *esp)
-{
-	u8 tmp;
-
-	if (esp->erev != esp100)
-		return 0;
-	tmp = sbus_readb(esp->eregs + ESP_INTRPT);
-	if (tmp & ESP_INTR_SR)
-		return 1;
-	return 0;
-}
-
-/* This verifies the BUSID bits during a reselection so that we know which
- * target is talking to us.
- */
-static inline int reconnect_target(struct esp *esp)
-{
-	int it, me = esp->scsi_id_mask, targ = 0;
-
-	if (2 != fcount(esp))
-		return -1;
-	if (esp->erev == fashme) {
-		/* HME does not latch it's own BUS ID bits during
-		 * a reselection.  Also the target number is given
-		 * as an unsigned char, not as a sole bit number
-		 * like the other ESP's do.
-		 * Happy Meal indeed....
-		 */
-		targ = esp->hme_fifo_workaround_buffer[0];
-	} else {
-		it = sbus_readb(esp->eregs + ESP_FDATA);
-		if (!(it & me))
-			return -1;
-		it &= ~me;
-		if (it & (it - 1))
-			return -1;
-		while (!(it & 1))
-			targ++, it >>= 1;
-	}
-	return targ;
-}
-
-/* This verifies the identify from the target so that we know which lun is
- * being reconnected.
- */
-static inline int reconnect_lun(struct esp *esp)
-{
-	int lun;
-
-	if ((esp->sreg & ESP_STAT_PMASK) != ESP_MIP)
-		return -1;
-	if (esp->erev == fashme)
-		lun = esp->hme_fifo_workaround_buffer[1];
-	else
-		lun = sbus_readb(esp->eregs + ESP_FDATA);
-
-	/* Yes, you read this correctly.  We report lun of zero
-	 * if we see parity error.  ESP reports parity error for
-	 * the lun byte, and this is the only way to hope to recover
-	 * because the target is connected.
-	 */
-	if (esp->sreg & ESP_STAT_PERR)
-		return 0;
-
-	/* Check for illegal bits being set in the lun. */
-	if ((lun & 0x40) || !(lun & 0x80))
-		return -1;
-
-	return lun & 7;
-}
-
-/* This puts the driver in a state where it can revitalize a command that
- * is being continued due to reselection.
- */
-static inline void esp_connect(struct esp *esp, struct scsi_cmnd *sp)
-{
-	struct esp_device *esp_dev = sp->device->hostdata;
-
-	if (esp->prev_soff  != esp_dev->sync_max_offset ||
-	    esp->prev_stp   != esp_dev->sync_min_period ||
-	    (esp->erev > esp100a &&
-	     esp->prev_cfg3 != esp->config3[sp->device->id])) {
-		esp->prev_soff = esp_dev->sync_max_offset;
-		esp->prev_stp = esp_dev->sync_min_period;
-		sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF);
-		sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP);
-		if (esp->erev > esp100a) {
-			esp->prev_cfg3 = esp->config3[sp->device->id];
-			sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
-		}
-	}
-	esp->current_SC = sp;
-}
-
-/* This will place the current working command back into the issue queue
- * if we are to receive a reselection amidst a selection attempt.
- */
-static inline void esp_reconnect(struct esp *esp, struct scsi_cmnd *sp)
-{
-	if (!esp->disconnected_SC)
-		ESPLOG(("esp%d: Weird, being reselected but disconnected "
-			"command queue is empty.\n", esp->esp_id));
-	esp->snip = 0;
-	esp->current_SC = NULL;
-	sp->SCp.phase = not_issued;
-	append_SC(&esp->issue_SC, sp);
-}
-
-/* Begin message in phase. */
-static int esp_do_msgin(struct esp *esp)
-{
-	/* Must be very careful with the fifo on the HME */
-	if ((esp->erev != fashme) ||
-	    !(sbus_readb(esp->eregs + ESP_STATUS2) & ESP_STAT2_FEMPTY))
-		esp_cmd(esp, ESP_CMD_FLUSH);
-	esp_maybe_nop(esp);
-	esp_cmd(esp, ESP_CMD_TI);
-	esp->msgin_len = 1;
-	esp->msgin_ctr = 0;
-	esp_advance_phase(esp->current_SC, in_msgindone);
-	return do_work_bus;
-}
-
-/* This uses various DMA csr fields and the fifo flags count value to
- * determine how many bytes were successfully sent/received by the ESP.
- */
-static inline int esp_bytes_sent(struct esp *esp, int fifo_count)
-{
-	int rval = sbus_readl(esp->dregs + DMA_ADDR) - esp->esp_command_dvma;
-
-	if (esp->dma->revision == dvmarev1)
-		rval -= (4 - ((sbus_readl(esp->dregs + DMA_CSR) & DMA_READ_AHEAD)>>11));
-	return rval - fifo_count;
-}
-
-static inline void advance_sg(struct scsi_cmnd *sp)
-{
-	++sp->SCp.buffer;
-	--sp->SCp.buffers_residual;
-	sp->SCp.this_residual = sg_dma_len(sp->SCp.buffer);
-	sp->SCp.ptr = (char *)((unsigned long)sg_dma_address(sp->SCp.buffer));
-}
-
-/* Please note that the way I've coded these routines is that I _always_
- * check for a disconnect during any and all information transfer
- * phases.  The SCSI standard states that the target _can_ cause a BUS
- * FREE condition by dropping all MSG/CD/IO/BSY signals.  Also note
- * that during information transfer phases the target controls every
- * change in phase, the only thing the initiator can do is "ask" for
- * a message out phase by driving ATN true.  The target can, and sometimes
- * will, completely ignore this request so we cannot assume anything when
- * we try to force a message out phase to abort/reset a target.  Most of
- * the time the target will eventually be nice and go to message out, so
- * we may have to hold on to our state about what we want to tell the target
- * for some period of time.
- */
-
-/* I think I have things working here correctly.  Even partial transfers
- * within a buffer or sub-buffer should not upset us at all no matter
- * how bad the target and/or ESP fucks things up.
- */
-static int esp_do_data(struct esp *esp)
-{
-	struct scsi_cmnd *SCptr = esp->current_SC;
-	int thisphase, hmuch;
-
-	ESPDATA(("esp_do_data: "));
-	esp_maybe_nop(esp);
-	thisphase = sreg_to_dataphase(esp->sreg);
-	esp_advance_phase(SCptr, thisphase);
-	ESPDATA(("newphase<%s> ", (thisphase == in_datain) ? "DATAIN" : "DATAOUT"));
-	hmuch = dma_can_transfer(esp, SCptr);
-	if (hmuch > (64 * 1024) && (esp->erev != fashme))
-		hmuch = (64 * 1024);
-	ESPDATA(("hmuch<%d> ", hmuch));
-	esp->current_transfer_size = hmuch;
-
-	if (esp->erev == fashme) {
-		u32 tmp = esp->prev_hme_dmacsr;
-
-		/* Always set the ESP count registers first. */
-		esp_setcount(esp->eregs, hmuch, 1);
-
-		/* Get the DMA csr computed. */
-		tmp |= (DMA_SCSI_DISAB | DMA_ENABLE);
-		if (thisphase == in_datain)
-			tmp |= DMA_ST_WRITE;
-		else
-			tmp &= ~(DMA_ST_WRITE);
-		esp->prev_hme_dmacsr = tmp;
-
-		ESPDATA(("DMA|TI --> do_intr_end\n"));
-		if (thisphase == in_datain) {
-			sbus_writel(hmuch, esp->dregs + DMA_COUNT);
-			esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI);
-		} else {
-			esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI);
-			sbus_writel(hmuch, esp->dregs + DMA_COUNT);
-		}
-		sbus_writel((__u32)((unsigned long)SCptr->SCp.ptr), esp->dregs+DMA_ADDR);
-		sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR);
-	} else {
-		esp_setcount(esp->eregs, hmuch, 0);
-		dma_setup(esp, ((__u32)((unsigned long)SCptr->SCp.ptr)),
-			  hmuch, (thisphase == in_datain));
-		ESPDATA(("DMA|TI --> do_intr_end\n"));
-		esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI);
-	}
-	return do_intr_end;
-}
-
-/* See how successful the data transfer was. */
-static int esp_do_data_finale(struct esp *esp)
-{
-	struct scsi_cmnd *SCptr = esp->current_SC;
-	struct esp_device *esp_dev = SCptr->device->hostdata;
-	int bogus_data = 0, bytes_sent = 0, fifocnt, ecount = 0;
-
-	ESPDATA(("esp_do_data_finale: "));
-
-	if (SCptr->SCp.phase == in_datain) {
-		if (esp->sreg & ESP_STAT_PERR) {
-			/* Yuck, parity error.  The ESP asserts ATN
-			 * so that we can go to message out phase
-			 * immediately and inform the target that
-			 * something bad happened.
-			 */
-			ESPLOG(("esp%d: data bad parity detected.\n",
-				esp->esp_id));
-			esp->cur_msgout[0] = INITIATOR_ERROR;
-			esp->msgout_len = 1;
-		}
-		dma_drain(esp);
-	}
-	dma_invalidate(esp);
-
-	/* This could happen for the above parity error case. */
-	if (esp->ireg != ESP_INTR_BSERV) {
-		/* Please go to msgout phase, please please please... */
-		ESPLOG(("esp%d: !BSERV after data, probably to msgout\n",
-			esp->esp_id));
-		return esp_do_phase_determine(esp);
-	}	
-
-	/* Check for partial transfers and other horrible events.
-	 * Note, here we read the real fifo flags register even
-	 * on HME broken adapters because we skip the HME fifo
-	 * workaround code in esp_handle() if we are doing data
-	 * phase things.  We don't want to fuck directly with
-	 * the fifo like that, especially if doing synchronous
-	 * transfers!  Also, will need to double the count on
-	 * HME if we are doing wide transfers, as the HME fifo
-	 * will move and count 16-bit quantities during wide data.
-	 * SMCC _and_ Qlogic can both bite me.
-	 */
-	fifocnt = (sbus_readb(esp->eregs + ESP_FFLAGS) & ESP_FF_FBYTES);
-	if (esp->erev != fashme)
-		ecount = esp_getcount(esp->eregs, 0);
-	bytes_sent = esp->current_transfer_size;
-
-	ESPDATA(("trans_sz(%d), ", bytes_sent));
-	if (esp->erev == fashme) {
-		if (!(esp->sreg & ESP_STAT_TCNT)) {
-			ecount = esp_getcount(esp->eregs, 1);
-			bytes_sent -= ecount;
-		}
-
-		/* Always subtract any cruft remaining in the FIFO. */
-		if (esp->prev_cfg3 & ESP_CONFIG3_EWIDE)
-			fifocnt <<= 1;
-		if (SCptr->SCp.phase == in_dataout)
-			bytes_sent -= fifocnt;
-
-		/* I have an IBM disk which exhibits the following
-		 * behavior during writes to it.  It disconnects in
-		 * the middle of a partial transfer, the current sglist
-		 * buffer is 1024 bytes, the disk stops data transfer
-		 * at 512 bytes.
-		 *
-		 * However the FAS366 reports that 32 more bytes were
-		 * transferred than really were.  This is precisely
-		 * the size of a fully loaded FIFO in wide scsi mode.
-		 * The FIFO state recorded indicates that it is empty.
-		 *
-		 * I have no idea if this is a bug in the FAS366 chip
-		 * or a bug in the firmware on this IBM disk.  In any
-		 * event the following seems to be a good workaround.  -DaveM
-		 */
-		if (bytes_sent != esp->current_transfer_size &&
-		    SCptr->SCp.phase == in_dataout) {
-			int mask = (64 - 1);
-
-			if ((esp->prev_cfg3 & ESP_CONFIG3_EWIDE) == 0)
-				mask >>= 1;
-
-			if (bytes_sent & mask)
-				bytes_sent -= (bytes_sent & mask);
-		}
-	} else {
-		if (!(esp->sreg & ESP_STAT_TCNT))
-			bytes_sent -= ecount;
-		if (SCptr->SCp.phase == in_dataout)
-			bytes_sent -= fifocnt;
-	}
-
-	ESPDATA(("bytes_sent(%d), ", bytes_sent));
-
-	/* If we were in synchronous mode, check for peculiarities. */
-	if (esp->erev == fashme) {
-		if (esp_dev->sync_max_offset) {
-			if (SCptr->SCp.phase == in_dataout)
-				esp_cmd(esp, ESP_CMD_FLUSH);
-		} else {
-			esp_cmd(esp, ESP_CMD_FLUSH);
-		}
-	} else {
-		if (esp_dev->sync_max_offset)
-			bogus_data = esp100_sync_hwbug(esp, SCptr, fifocnt);
-		else
-			esp_cmd(esp, ESP_CMD_FLUSH);
-	}
-
-	/* Until we are sure of what has happened, we are certainly
-	 * in the dark.
-	 */
-	esp_advance_phase(SCptr, in_the_dark);
-
-	if (bytes_sent < 0) {
-		/* I've seen this happen due to lost state in this
-		 * driver.  No idea why it happened, but allowing
-		 * this value to be negative caused things to
-		 * lock up.  This allows greater chance of recovery.
-		 * In fact every time I've seen this, it has been
-		 * a driver bug without question.
-		 */
-		ESPLOG(("esp%d: yieee, bytes_sent < 0!\n", esp->esp_id));
-		ESPLOG(("esp%d: csz=%d fifocount=%d ecount=%d\n",
-			esp->esp_id,
-			esp->current_transfer_size, fifocnt, ecount));
-		ESPLOG(("esp%d: use_sg=%d ptr=%p this_residual=%d\n",
-			esp->esp_id,
-			SCptr->use_sg, SCptr->SCp.ptr, SCptr->SCp.this_residual));
-		ESPLOG(("esp%d: Forcing async for target %d\n", esp->esp_id, 
-			SCptr->device->id));
-		SCptr->device->borken = 1;
-		esp_dev->sync = 0;
-		bytes_sent = 0;
-	}
-
-	/* Update the state of our transfer. */
-	SCptr->SCp.ptr += bytes_sent;
-	SCptr->SCp.this_residual -= bytes_sent;
-	if (SCptr->SCp.this_residual < 0) {
-		/* shit */
-		ESPLOG(("esp%d: Data transfer overrun.\n", esp->esp_id));
-		SCptr->SCp.this_residual = 0;
-	}
-
-	/* Maybe continue. */
-	if (!bogus_data) {
-		ESPDATA(("!bogus_data, "));
-
-		/* NO MATTER WHAT, we advance the scatterlist,
-		 * if the target should decide to disconnect
-		 * in between scatter chunks (which is common)
-		 * we could die horribly!  I used to have the sg
-		 * advance occur only if we are going back into
-		 * (or are staying in) a data phase, you can
-		 * imagine the hell I went through trying to
-		 * figure this out.
-		 */
-		if (SCptr->use_sg && !SCptr->SCp.this_residual)
-			advance_sg(SCptr);
-		if (sreg_datainp(esp->sreg) || sreg_dataoutp(esp->sreg)) {
-			ESPDATA(("to more data\n"));
-			return esp_do_data(esp);
-		}
-		ESPDATA(("to new phase\n"));
-		return esp_do_phase_determine(esp);
-	}
-	/* Bogus data, just wait for next interrupt. */
-	ESPLOG(("esp%d: bogus_data during end of data phase\n",
-		esp->esp_id));
-	return do_intr_end;
-}
-
-/* We received a non-good status return at the end of
- * running a SCSI command.  This is used to decide if
- * we should clear our synchronous transfer state for
- * such a device when that happens.
- *
- * The idea is that when spinning up a disk or rewinding
- * a tape, we don't want to go into a loop re-negotiating
- * synchronous capabilities over and over.
- */
-static int esp_should_clear_sync(struct scsi_cmnd *sp)
-{
-	u8 cmd = sp->cmnd[0];
-
-	/* These cases are for spinning up a disk and
-	 * waiting for that spinup to complete.
-	 */
-	if (cmd == START_STOP)
-		return 0;
-
-	if (cmd == TEST_UNIT_READY)
-		return 0;
-
-	/* One more special case for SCSI tape drives,
-	 * this is what is used to probe the device for
-	 * completion of a rewind or tape load operation.
-	 */
-	if (sp->device->type == TYPE_TAPE) {
-		if (cmd == MODE_SENSE)
-			return 0;
-	}
-
-	return 1;
-}
-
-/* Either a command is completing or a target is dropping off the bus
- * to continue the command in the background so we can do other work.
- */
-static int esp_do_freebus(struct esp *esp)
-{
-	struct scsi_cmnd *SCptr = esp->current_SC;
-	struct esp_device *esp_dev = SCptr->device->hostdata;
-	int rval;
-
-	rval = skipahead2(esp, SCptr, in_status, in_msgindone, in_freeing);
-	if (rval)
-		return rval;
-	if (esp->ireg != ESP_INTR_DC) {
-		ESPLOG(("esp%d: Target will not disconnect\n", esp->esp_id));
-		return do_reset_bus; /* target will not drop BSY... */
-	}
-	esp->msgout_len = 0;
-	esp->prevmsgout = NOP;
-	if (esp->prevmsgin == COMMAND_COMPLETE) {
-		/* Normal end of nexus. */
-		if (esp->disconnected_SC || (esp->erev == fashme))
-			esp_cmd(esp, ESP_CMD_ESEL);
-
-		if (SCptr->SCp.Status != GOOD &&
-		    SCptr->SCp.Status != CONDITION_GOOD &&
-		    ((1<<SCptr->device->id) & esp->targets_present) &&
-		    esp_dev->sync &&
-		    esp_dev->sync_max_offset) {
-			/* SCSI standard says that the synchronous capabilities
-			 * should be renegotiated at this point.  Most likely
-			 * we are about to request sense from this target
-			 * in which case we want to avoid using sync
-			 * transfers until we are sure of the current target
-			 * state.
-			 */
-			ESPMISC(("esp: Status <%d> for target %d lun %d\n",
-				 SCptr->SCp.Status, SCptr->device->id, SCptr->device->lun));
-
-			/* But don't do this when spinning up a disk at
-			 * boot time while we poll for completion as it
-			 * fills up the console with messages.  Also, tapes
-			 * can report not ready many times right after
-			 * loading up a tape.
-			 */
-			if (esp_should_clear_sync(SCptr) != 0)
-				esp_dev->sync = 0;
-		}
-		ESPDISC(("F<%02x,%02x>", SCptr->device->id, SCptr->device->lun));
-		esp_done(esp, ((SCptr->SCp.Status & 0xff) |
-			       ((SCptr->SCp.Message & 0xff)<<8) |
-			       (DID_OK << 16)));
-	} else if (esp->prevmsgin == DISCONNECT) {
-		/* Normal disconnect. */
-		esp_cmd(esp, ESP_CMD_ESEL);
-		ESPDISC(("D<%02x,%02x>", SCptr->device->id, SCptr->device->lun));
-		append_SC(&esp->disconnected_SC, SCptr);
-		esp->current_SC = NULL;
-		if (esp->issue_SC)
-			esp_exec_cmd(esp);
-	} else {
-		/* Driver bug, we do not expect a disconnect here
-		 * and should not have advanced the state engine
-		 * to in_freeing.
-		 */
-		ESPLOG(("esp%d: last msg not disc and not cmd cmplt.\n",
-			esp->esp_id));
-		return do_reset_bus;
-	}
-	return do_intr_end;
-}
-
-/* When a reselect occurs, and we cannot find the command to
- * reconnect to in our queues, we do this.
- */
-static int esp_bad_reconnect(struct esp *esp)
-{
-	struct scsi_cmnd *sp;
-
-	ESPLOG(("esp%d: Eieeee, reconnecting unknown command!\n",
-		esp->esp_id));
-	ESPLOG(("QUEUE DUMP\n"));
-	sp = esp->issue_SC;
-	ESPLOG(("esp%d: issue_SC[", esp->esp_id));
-	while (sp) {
-		ESPLOG(("<%02x,%02x>", sp->device->id, sp->device->lun));
-		sp = (struct scsi_cmnd *) sp->host_scribble;
-	}
-	ESPLOG(("]\n"));
-	sp = esp->current_SC;
-	ESPLOG(("esp%d: current_SC[", esp->esp_id));
-	if (sp)
-		ESPLOG(("<%02x,%02x>", sp->device->id, sp->device->lun));
-	else
-		ESPLOG(("<NULL>"));
-	ESPLOG(("]\n"));
-	sp = esp->disconnected_SC;
-	ESPLOG(("esp%d: disconnected_SC[", esp->esp_id));
-	while (sp) {
-		ESPLOG(("<%02x,%02x>", sp->device->id, sp->device->lun));
-		sp = (struct scsi_cmnd *) sp->host_scribble;
-	}
-	ESPLOG(("]\n"));
-	return do_reset_bus;
-}
-
-/* Do the needy when a target tries to reconnect to us. */
-static int esp_do_reconnect(struct esp *esp)
-{
-	int lun, target;
-	struct scsi_cmnd *SCptr;
-
-	/* Check for all bogus conditions first. */
-	target = reconnect_target(esp);
-	if (target < 0) {
-		ESPDISC(("bad bus bits\n"));
-		return do_reset_bus;
-	}
-	lun = reconnect_lun(esp);
-	if (lun < 0) {
-		ESPDISC(("target=%2x, bad identify msg\n", target));
-		return do_reset_bus;
-	}
-
-	/* Things look ok... */
-	ESPDISC(("R<%02x,%02x>", target, lun));
-
-	/* Must not flush FIFO or DVMA on HME. */
-	if (esp->erev != fashme) {
-		esp_cmd(esp, ESP_CMD_FLUSH);
-		if (esp100_reconnect_hwbug(esp))
-			return do_reset_bus;
-		esp_cmd(esp, ESP_CMD_NULL);
-	}
-
-	SCptr = remove_SC(&esp->disconnected_SC, (u8) target, (u8) lun);
-	if (!SCptr)
-		return esp_bad_reconnect(esp);
-
-	esp_connect(esp, SCptr);
-	esp_cmd(esp, ESP_CMD_MOK);
-
-	if (esp->erev == fashme)
-		sbus_writeb(((SCptr->device->id & 0xf) |
-			     (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT)),
-			    esp->eregs + ESP_BUSID);
-
-	/* Reconnect implies a restore pointers operation. */
-	esp_restore_pointers(esp, SCptr);
-
-	esp->snip = 0;
-	esp_advance_phase(SCptr, in_the_dark);
-	return do_intr_end;
-}
-
-/* End of NEXUS (hopefully), pick up status + message byte then leave if
- * all goes well.
- */
-static int esp_do_status(struct esp *esp)
-{
-	struct scsi_cmnd *SCptr = esp->current_SC;
-	int intr, rval;
-
-	rval = skipahead1(esp, SCptr, in_the_dark, in_status);
-	if (rval)
-		return rval;
-	intr = esp->ireg;
-	ESPSTAT(("esp_do_status: "));
-	if (intr != ESP_INTR_DC) {
-		int message_out = 0; /* for parity problems */
-
-		/* Ack the message. */
-		ESPSTAT(("ack msg, "));
-		esp_cmd(esp, ESP_CMD_MOK);
-
-		if (esp->erev != fashme) {
-			dma_flashclear(esp);
-
-			/* Wait till the first bits settle. */
-			while (esp->esp_command[0] == 0xff)
-				udelay(1);
-		} else {
-			esp->esp_command[0] = esp->hme_fifo_workaround_buffer[0];
-			esp->esp_command[1] = esp->hme_fifo_workaround_buffer[1];
-		}
-
-		ESPSTAT(("got something, "));
-		/* ESP chimes in with one of
-		 *
-		 * 1) function done interrupt:
-		 *	both status and message in bytes
-		 *	are available
-		 *
-		 * 2) bus service interrupt:
-		 *	only status byte was acquired
-		 *
-		 * 3) Anything else:
-		 *	can't happen, but we test for it
-		 *	anyways
-		 *
-		 * ALSO: If bad parity was detected on either
-		 *       the status _or_ the message byte then
-		 *       the ESP has asserted ATN on the bus
-		 *       and we must therefore wait for the
-		 *       next phase change.
-		 */
-		if (intr & ESP_INTR_FDONE) {
-			/* We got it all, hallejulia. */
-			ESPSTAT(("got both, "));
-			SCptr->SCp.Status = esp->esp_command[0];
-			SCptr->SCp.Message = esp->esp_command[1];
-			esp->prevmsgin = SCptr->SCp.Message;
-			esp->cur_msgin[0] = SCptr->SCp.Message;
-			if (esp->sreg & ESP_STAT_PERR) {
-				/* There was bad parity for the
-				 * message byte, the status byte
-				 * was ok.
-				 */
-				message_out = MSG_PARITY_ERROR;
-			}
-		} else if (intr == ESP_INTR_BSERV) {
-			/* Only got status byte. */
-			ESPLOG(("esp%d: got status only, ", esp->esp_id));
-			if (!(esp->sreg & ESP_STAT_PERR)) {
-				SCptr->SCp.Status = esp->esp_command[0];
-				SCptr->SCp.Message = 0xff;
-			} else {
-				/* The status byte had bad parity.
-				 * we leave the scsi_pointer Status
-				 * field alone as we set it to a default
-				 * of CHECK_CONDITION in esp_queue.
-				 */
-				message_out = INITIATOR_ERROR;
-			}
-		} else {
-			/* This shouldn't happen ever. */
-			ESPSTAT(("got bolixed\n"));
-			esp_advance_phase(SCptr, in_the_dark);
-			return esp_do_phase_determine(esp);
-		}
-
-		if (!message_out) {
-			ESPSTAT(("status=%2x msg=%2x, ", SCptr->SCp.Status,
-				SCptr->SCp.Message));
-			if (SCptr->SCp.Message == COMMAND_COMPLETE) {
-				ESPSTAT(("and was COMMAND_COMPLETE\n"));
-				esp_advance_phase(SCptr, in_freeing);
-				return esp_do_freebus(esp);
-			} else {
-				ESPLOG(("esp%d: and _not_ COMMAND_COMPLETE\n",
-					esp->esp_id));
-				esp->msgin_len = esp->msgin_ctr = 1;
-				esp_advance_phase(SCptr, in_msgindone);
-				return esp_do_msgindone(esp);
-			}
-		} else {
-			/* With luck we'll be able to let the target
-			 * know that bad parity happened, it will know
-			 * which byte caused the problems and send it
-			 * again.  For the case where the status byte
-			 * receives bad parity, I do not believe most
-			 * targets recover very well.  We'll see.
-			 */
-			ESPLOG(("esp%d: bad parity somewhere mout=%2x\n",
-				esp->esp_id, message_out));
-			esp->cur_msgout[0] = message_out;
-			esp->msgout_len = esp->msgout_ctr = 1;
-			esp_advance_phase(SCptr, in_the_dark);
-			return esp_do_phase_determine(esp);
-		}
-	} else {
-		/* If we disconnect now, all hell breaks loose. */
-		ESPLOG(("esp%d: whoops, disconnect\n", esp->esp_id));
-		esp_advance_phase(SCptr, in_the_dark);
-		return esp_do_phase_determine(esp);
-	}
-}
-
-static int esp_enter_status(struct esp *esp)
-{
-	u8 thecmd = ESP_CMD_ICCSEQ;
-
-	esp_cmd(esp, ESP_CMD_FLUSH);
-	if (esp->erev != fashme) {
-		u32 tmp;
-
-		esp->esp_command[0] = esp->esp_command[1] = 0xff;
-		sbus_writeb(2, esp->eregs + ESP_TCLOW);
-		sbus_writeb(0, esp->eregs + ESP_TCMED);
-		tmp = sbus_readl(esp->dregs + DMA_CSR);
-		tmp |= (DMA_ST_WRITE | DMA_ENABLE);
-		sbus_writel(tmp, esp->dregs + DMA_CSR);
-		if (esp->dma->revision == dvmaesc1)
-			sbus_writel(0x100, esp->dregs + DMA_COUNT);
-		sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR);
-		thecmd |= ESP_CMD_DMA;
-	}
-	esp_cmd(esp, thecmd);
-	esp_advance_phase(esp->current_SC, in_status);
-
-	return esp_do_status(esp);
-}
-
-static int esp_disconnect_amidst_phases(struct esp *esp)
-{
-	struct scsi_cmnd *sp = esp->current_SC;
-	struct esp_device *esp_dev = sp->device->hostdata;
-
-	/* This means real problems if we see this
-	 * here.  Unless we were actually trying
-	 * to force the device to abort/reset.
-	 */
-	ESPLOG(("esp%d Disconnect amidst phases, ", esp->esp_id));
-	ESPLOG(("pphase<%s> cphase<%s>, ",
-		phase_string(sp->SCp.phase),
-		phase_string(sp->SCp.sent_command)));
-
-	if (esp->disconnected_SC != NULL || (esp->erev == fashme))
-		esp_cmd(esp, ESP_CMD_ESEL);
-
-	switch (esp->cur_msgout[0]) {
-	default:
-		/* We didn't expect this to happen at all. */
-		ESPLOG(("device is bolixed\n"));
-		esp_advance_phase(sp, in_tgterror);
-		esp_done(esp, (DID_ERROR << 16));
-		break;
-
-	case BUS_DEVICE_RESET:
-		ESPLOG(("device reset successful\n"));
-		esp_dev->sync_max_offset = 0;
-		esp_dev->sync_min_period = 0;
-		esp_dev->sync = 0;
-		esp_advance_phase(sp, in_resetdev);
-		esp_done(esp, (DID_RESET << 16));
-		break;
-
-	case ABORT:
-		ESPLOG(("device abort successful\n"));
-		esp_advance_phase(sp, in_abortone);
-		esp_done(esp, (DID_ABORT << 16));
-		break;
-
-	};
-	return do_intr_end;
-}
-
-static int esp_enter_msgout(struct esp *esp)
-{
-	esp_advance_phase(esp->current_SC, in_msgout);
-	return esp_do_msgout(esp);
-}
-
-static int esp_enter_msgin(struct esp *esp)
-{
-	esp_advance_phase(esp->current_SC, in_msgin);
-	return esp_do_msgin(esp);
-}
-
-static int esp_enter_cmd(struct esp *esp)
-{
-	esp_advance_phase(esp->current_SC, in_cmdbegin);
-	return esp_do_cmdbegin(esp);
-}
-
-static int esp_enter_badphase(struct esp *esp)
-{
-	ESPLOG(("esp%d: Bizarre bus phase %2x.\n", esp->esp_id,
-		esp->sreg & ESP_STAT_PMASK));
-	return do_reset_bus;
-}
-
-typedef int (*espfunc_t)(struct esp *);
-
-static espfunc_t phase_vector[] = {
-	esp_do_data,		/* ESP_DOP */
-	esp_do_data,		/* ESP_DIP */
-	esp_enter_cmd,		/* ESP_CMDP */
-	esp_enter_status,	/* ESP_STATP */
-	esp_enter_badphase,	/* ESP_STAT_PMSG */
-	esp_enter_badphase,	/* ESP_STAT_PMSG | ESP_STAT_PIO */
-	esp_enter_msgout,	/* ESP_MOP */
-	esp_enter_msgin,	/* ESP_MIP */
-};
-
-/* The target has control of the bus and we have to see where it has
- * taken us.
- */
-static int esp_do_phase_determine(struct esp *esp)
-{
-	if ((esp->ireg & ESP_INTR_DC) != 0)
-		return esp_disconnect_amidst_phases(esp);
-	return phase_vector[esp->sreg & ESP_STAT_PMASK](esp);
-}
-
-/* First interrupt after exec'ing a cmd comes here. */
-static int esp_select_complete(struct esp *esp)
-{
-	struct scsi_cmnd *SCptr = esp->current_SC;
-	struct esp_device *esp_dev = SCptr->device->hostdata;
-	int cmd_bytes_sent, fcnt;
-
-	if (esp->erev != fashme)
-		esp->seqreg = (sbus_readb(esp->eregs + ESP_SSTEP) & ESP_STEP_VBITS);
-
-	if (esp->erev == fashme)
-		fcnt = esp->hme_fifo_workaround_count;
-	else
-		fcnt = (sbus_readb(esp->eregs + ESP_FFLAGS) & ESP_FF_FBYTES);
-
-	cmd_bytes_sent = esp_bytes_sent(esp, fcnt);
-	dma_invalidate(esp);
-
-	/* Let's check to see if a reselect happened
-	 * while we we're trying to select.  This must
-	 * be checked first.
-	 */
-	if (esp->ireg == (ESP_INTR_RSEL | ESP_INTR_FDONE)) {
-		esp_reconnect(esp, SCptr);
-		return esp_do_reconnect(esp);
-	}
-
-	/* Looks like things worked, we should see a bus service &
-	 * a function complete interrupt at this point.  Note we
-	 * are doing a direct comparison because we don't want to
-	 * be fooled into thinking selection was successful if
-	 * ESP_INTR_DC is set, see below.
-	 */
-	if (esp->ireg == (ESP_INTR_FDONE | ESP_INTR_BSERV)) {
-		/* target speaks... */
-		esp->targets_present |= (1<<SCptr->device->id);
-
-		/* What if the target ignores the sdtr? */
-		if (esp->snip)
-			esp_dev->sync = 1;
-
-		/* See how far, if at all, we got in getting
-		 * the information out to the target.
-		 */
-		switch (esp->seqreg) {
-		default:
-
-		case ESP_STEP_ASEL:
-			/* Arbitration won, target selected, but
-			 * we are in some phase which is not command
-			 * phase nor is it message out phase.
-			 *
-			 * XXX We've confused the target, obviously.
-			 * XXX So clear it's state, but we also end
-			 * XXX up clearing everyone elses.  That isn't
-			 * XXX so nice.  I'd like to just reset this
-			 * XXX target, but if I cannot even get it's
-			 * XXX attention and finish selection to talk
-			 * XXX to it, there is not much more I can do.
-			 * XXX If we have a loaded bus we're going to
-			 * XXX spend the next second or so renegotiating
-			 * XXX for synchronous transfers.
-			 */
-			ESPLOG(("esp%d: STEP_ASEL for tgt %d\n",
-				esp->esp_id, SCptr->device->id));
-
-		case ESP_STEP_SID:
-			/* Arbitration won, target selected, went
-			 * to message out phase, sent one message
-			 * byte, then we stopped.  ATN is asserted
-			 * on the SCSI bus and the target is still
-			 * there hanging on.  This is a legal
-			 * sequence step if we gave the ESP a select
-			 * and stop command.
-			 *
-			 * XXX See above, I could set the borken flag
-			 * XXX in the device struct and retry the
-			 * XXX command.  But would that help for
-			 * XXX tagged capable targets?
-			 */
-
-		case ESP_STEP_NCMD:
-			/* Arbitration won, target selected, maybe
-			 * sent the one message byte in message out
-			 * phase, but we did not go to command phase
-			 * in the end.  Actually, we could have sent
-			 * only some of the message bytes if we tried
-			 * to send out the entire identify and tag
-			 * message using ESP_CMD_SA3.
-			 */
-			cmd_bytes_sent = 0;
-			break;
-
-		case ESP_STEP_PPC:
-			/* No, not the powerPC pinhead.  Arbitration
-			 * won, all message bytes sent if we went to
-			 * message out phase, went to command phase
-			 * but only part of the command was sent.
-			 *
-			 * XXX I've seen this, but usually in conjunction
-			 * XXX with a gross error which appears to have
-			 * XXX occurred between the time I told the
-			 * XXX ESP to arbitrate and when I got the
-			 * XXX interrupt.  Could I have misloaded the
-			 * XXX command bytes into the fifo?  Actually,
-			 * XXX I most likely missed a phase, and therefore
-			 * XXX went into never never land and didn't even
-			 * XXX know it.  That was the old driver though.
-			 * XXX What is even more peculiar is that the ESP
-			 * XXX showed the proper function complete and
-			 * XXX bus service bits in the interrupt register.
-			 */
-
-		case ESP_STEP_FINI4:
-		case ESP_STEP_FINI5:
-		case ESP_STEP_FINI6:
-		case ESP_STEP_FINI7:
-			/* Account for the identify message */
-			if (SCptr->SCp.phase == in_slct_norm)
-				cmd_bytes_sent -= 1;
-		};
-
-		if (esp->erev != fashme)
-			esp_cmd(esp, ESP_CMD_NULL);
-
-		/* Be careful, we could really get fucked during synchronous
-		 * data transfers if we try to flush the fifo now.
-		 */
-		if ((esp->erev != fashme) && /* not a Happy Meal and... */
-		    !fcnt && /* Fifo is empty and... */
-		    /* either we are not doing synchronous transfers or... */
-		    (!esp_dev->sync_max_offset ||
-		     /* We are not going into data in phase. */
-		     ((esp->sreg & ESP_STAT_PMASK) != ESP_DIP)))
-			esp_cmd(esp, ESP_CMD_FLUSH); /* flush is safe */
-
-		/* See how far we got if this is not a slow command. */
-		if (!esp->esp_slowcmd) {
-			if (cmd_bytes_sent < 0)
-				cmd_bytes_sent = 0;
-			if (cmd_bytes_sent != SCptr->cmd_len) {
-				/* Crapola, mark it as a slowcmd
-				 * so that we have some chance of
-				 * keeping the command alive with
-				 * good luck.
-				 *
-				 * XXX Actually, if we didn't send it all
-				 * XXX this means either we didn't set things
-				 * XXX up properly (driver bug) or the target
-				 * XXX or the ESP detected parity on one of
-				 * XXX the command bytes.  This makes much
-				 * XXX more sense, and therefore this code
-				 * XXX should be changed to send out a
-				 * XXX parity error message or if the status
-				 * XXX register shows no parity error then
-				 * XXX just expect the target to bring the
-				 * XXX bus into message in phase so that it
-				 * XXX can send us the parity error message.
-				 * XXX SCSI sucks...
-				 */
-				esp->esp_slowcmd = 1;
-				esp->esp_scmdp = &(SCptr->cmnd[cmd_bytes_sent]);
-				esp->esp_scmdleft = (SCptr->cmd_len - cmd_bytes_sent);
-			}
-		}
-
-		/* Now figure out where we went. */
-		esp_advance_phase(SCptr, in_the_dark);
-		return esp_do_phase_determine(esp);
-	}
-
-	/* Did the target even make it? */
-	if (esp->ireg == ESP_INTR_DC) {
-		/* wheee... nobody there or they didn't like
-		 * what we told it to do, clean up.
-		 */
-
-		/* If anyone is off the bus, but working on
-		 * a command in the background for us, tell
-		 * the ESP to listen for them.
-		 */
-		if (esp->disconnected_SC)
-			esp_cmd(esp, ESP_CMD_ESEL);
-
-		if (((1<<SCptr->device->id) & esp->targets_present) &&
-		    esp->seqreg != 0 &&
-		    (esp->cur_msgout[0] == EXTENDED_MESSAGE) &&
-		    (SCptr->SCp.phase == in_slct_msg ||
-		     SCptr->SCp.phase == in_slct_stop)) {
-			/* shit */
-			esp->snip = 0;
-			ESPLOG(("esp%d: Failed synchronous negotiation for target %d "
-				"lun %d\n", esp->esp_id, SCptr->device->id, SCptr->device->lun));
-			esp_dev->sync_max_offset = 0;
-			esp_dev->sync_min_period = 0;
-			esp_dev->sync = 1; /* so we don't negotiate again */
-
-			/* Run the command again, this time though we
-			 * won't try to negotiate for synchronous transfers.
-			 *
-			 * XXX I'd like to do something like send an
-			 * XXX INITIATOR_ERROR or ABORT message to the
-			 * XXX target to tell it, "Sorry I confused you,
-			 * XXX please come back and I will be nicer next
-			 * XXX time".  But that requires having the target
-			 * XXX on the bus, and it has dropped BSY on us.
-			 */
-			esp->current_SC = NULL;
-			esp_advance_phase(SCptr, not_issued);
-			prepend_SC(&esp->issue_SC, SCptr);
-			esp_exec_cmd(esp);
-			return do_intr_end;
-		}
-
-		/* Ok, this is normal, this is what we see during boot
-		 * or whenever when we are scanning the bus for targets.
-		 * But first make sure that is really what is happening.
-		 */
-		if (((1<<SCptr->device->id) & esp->targets_present)) {
-			ESPLOG(("esp%d: Warning, live target %d not responding to "
-				"selection.\n", esp->esp_id, SCptr->device->id));
-
-			/* This _CAN_ happen.  The SCSI standard states that
-			 * the target is to _not_ respond to selection if
-			 * _it_ detects bad parity on the bus for any reason.
-			 * Therefore, we assume that if we've talked successfully
-			 * to this target before, bad parity is the problem.
-			 */
-			esp_done(esp, (DID_PARITY << 16));
-		} else {
-			/* Else, there really isn't anyone there. */
-			ESPMISC(("esp: selection failure, maybe nobody there?\n"));
-			ESPMISC(("esp: target %d lun %d\n",
-				 SCptr->device->id, SCptr->device->lun));
-			esp_done(esp, (DID_BAD_TARGET << 16));
-		}
-		return do_intr_end;
-	}
-
-	ESPLOG(("esp%d: Selection failure.\n", esp->esp_id));
-	printk("esp%d: Currently -- ", esp->esp_id);
-	esp_print_ireg(esp->ireg); printk(" ");
-	esp_print_statreg(esp->sreg); printk(" ");
-	esp_print_seqreg(esp->seqreg); printk("\n");
-	printk("esp%d: New -- ", esp->esp_id);
-	esp->sreg = sbus_readb(esp->eregs + ESP_STATUS);
-	esp->seqreg = sbus_readb(esp->eregs + ESP_SSTEP);
-	esp->ireg = sbus_readb(esp->eregs + ESP_INTRPT);
-	esp_print_ireg(esp->ireg); printk(" ");
-	esp_print_statreg(esp->sreg); printk(" ");
-	esp_print_seqreg(esp->seqreg); printk("\n");
-	ESPLOG(("esp%d: resetting bus\n", esp->esp_id));
-	return do_reset_bus; /* ugh... */
-}
-
-/* Continue reading bytes for msgin phase. */
-static int esp_do_msgincont(struct esp *esp)
-{
-	if (esp->ireg & ESP_INTR_BSERV) {
-		/* in the right phase too? */
-		if ((esp->sreg & ESP_STAT_PMASK) == ESP_MIP) {
-			/* phew... */
-			esp_cmd(esp, ESP_CMD_TI);
-			esp_advance_phase(esp->current_SC, in_msgindone);
-			return do_intr_end;
-		}
-
-		/* We changed phase but ESP shows bus service,
-		 * in this case it is most likely that we, the
-		 * hacker who has been up for 20hrs straight
-		 * staring at the screen, drowned in coffee
-		 * smelling like retched cigarette ashes
-		 * have miscoded something..... so, try to
-		 * recover as best we can.
-		 */
-		ESPLOG(("esp%d: message in mis-carriage.\n", esp->esp_id));
-	}
-	esp_advance_phase(esp->current_SC, in_the_dark);
-	return do_phase_determine;
-}
-
-static int check_singlebyte_msg(struct esp *esp)
-{
-	esp->prevmsgin = esp->cur_msgin[0];
-	if (esp->cur_msgin[0] & 0x80) {
-		/* wheee... */
-		ESPLOG(("esp%d: target sends identify amidst phases\n",
-			esp->esp_id));
-		esp_advance_phase(esp->current_SC, in_the_dark);
-		return 0;
-	} else if (((esp->cur_msgin[0] & 0xf0) == 0x20) ||
-		   (esp->cur_msgin[0] == EXTENDED_MESSAGE)) {
-		esp->msgin_len = 2;
-		esp_advance_phase(esp->current_SC, in_msgincont);
-		return 0;
-	}
-	esp_advance_phase(esp->current_SC, in_the_dark);
-	switch (esp->cur_msgin[0]) {
-	default:
-		/* We don't want to hear about it. */
-		ESPLOG(("esp%d: msg %02x which we don't know about\n", esp->esp_id,
-			esp->cur_msgin[0]));
-		return MESSAGE_REJECT;
-
-	case NOP:
-		ESPLOG(("esp%d: target %d sends a nop\n", esp->esp_id,
-			esp->current_SC->device->id));
-		return 0;
-
-	case RESTORE_POINTERS:
-		/* In this case we might also have to backup the
-		 * "slow command" pointer.  It is rare to get such
-		 * a save/restore pointer sequence so early in the
-		 * bus transition sequences, but cover it.
-		 */
-		if (esp->esp_slowcmd) {
-			esp->esp_scmdleft = esp->current_SC->cmd_len;
-			esp->esp_scmdp = &esp->current_SC->cmnd[0];
-		}
-		esp_restore_pointers(esp, esp->current_SC);
-		return 0;
-
-	case SAVE_POINTERS:
-		esp_save_pointers(esp, esp->current_SC);
-		return 0;
-
-	case COMMAND_COMPLETE:
-	case DISCONNECT:
-		/* Freeing the bus, let it go. */
-		esp->current_SC->SCp.phase = in_freeing;
-		return 0;
-
-	case MESSAGE_REJECT:
-		ESPMISC(("msg reject, "));
-		if (esp->prevmsgout == EXTENDED_MESSAGE) {
-			struct esp_device *esp_dev = esp->current_SC->device->hostdata;
-
-			/* Doesn't look like this target can
-			 * do synchronous or WIDE transfers.
-			 */
-			ESPSDTR(("got reject, was trying nego, clearing sync/WIDE\n"));
-			esp_dev->sync = 1;
-			esp_dev->wide = 1;
-			esp_dev->sync_min_period = 0;
-			esp_dev->sync_max_offset = 0;
-			return 0;
-		} else {
-			ESPMISC(("not sync nego, sending ABORT\n"));
-			return ABORT;
-		}
-	};
-}
-
-/* Target negotiates for synchronous transfers before we do, this
- * is legal although very strange.  What is even funnier is that
- * the SCSI2 standard specifically recommends against targets doing
- * this because so many initiators cannot cope with this occurring.
- */
-static int target_with_ants_in_pants(struct esp *esp,
-				     struct scsi_cmnd *SCptr,
-				     struct esp_device *esp_dev)
-{
-	if (esp_dev->sync || SCptr->device->borken) {
-		/* sorry, no can do */
-		ESPSDTR(("forcing to async, "));
-		build_sync_nego_msg(esp, 0, 0);
-		esp_dev->sync = 1;
-		esp->snip = 1;
-		ESPLOG(("esp%d: hoping for msgout\n", esp->esp_id));
-		esp_advance_phase(SCptr, in_the_dark);
-		return EXTENDED_MESSAGE;
-	}
-
-	/* Ok, we'll check them out... */
-	return 0;
-}
-
-static void sync_report(struct esp *esp)
-{
-	int msg3, msg4;
-	char *type;
-
-	msg3 = esp->cur_msgin[3];
-	msg4 = esp->cur_msgin[4];
-	if (msg4) {
-		int hz = 1000000000 / (msg3 * 4);
-		int integer = hz / 1000000;
-		int fraction = (hz - (integer * 1000000)) / 10000;
-		if ((esp->erev == fashme) &&
-		    (esp->config3[esp->current_SC->device->id] & ESP_CONFIG3_EWIDE)) {
-			type = "FAST-WIDE";
-			integer <<= 1;
-			fraction <<= 1;
-		} else if ((msg3 * 4) < 200) {
-			type = "FAST";
-		} else {
-			type = "synchronous";
-		}
-
-		/* Do not transform this back into one big printk
-		 * again, it triggers a bug in our sparc64-gcc272
-		 * sibling call optimization.  -DaveM
-		 */
-		ESPLOG((KERN_INFO "esp%d: target %d ",
-			esp->esp_id, esp->current_SC->device->id));
-		ESPLOG(("[period %dns offset %d %d.%02dMHz ",
-			(int) msg3 * 4, (int) msg4,
-			integer, fraction));
-		ESPLOG(("%s SCSI%s]\n", type,
-			(((msg3 * 4) < 200) ? "-II" : "")));
-	} else {
-		ESPLOG((KERN_INFO "esp%d: target %d asynchronous\n",
-			esp->esp_id, esp->current_SC->device->id));
-	}
-}
-
-static int check_multibyte_msg(struct esp *esp)
-{
-	struct scsi_cmnd *SCptr = esp->current_SC;
-	struct esp_device *esp_dev = SCptr->device->hostdata;
-	u8 regval = 0;
-	int message_out = 0;
-
-	ESPSDTR(("chk multibyte msg: "));
-	if (esp->cur_msgin[2] == EXTENDED_SDTR) {
-		int period = esp->cur_msgin[3];
-		int offset = esp->cur_msgin[4];
-
-		ESPSDTR(("is sync nego response, "));
-		if (!esp->snip) {
-			int rval;
-
-			/* Target negotiates first! */
-			ESPSDTR(("target jumps the gun, "));
-			message_out = EXTENDED_MESSAGE; /* we must respond */
-			rval = target_with_ants_in_pants(esp, SCptr, esp_dev);
-			if (rval)
-				return rval;
-		}
-
-		ESPSDTR(("examining sdtr, "));
-
-		/* Offset cannot be larger than ESP fifo size. */
-		if (offset > 15) {
-			ESPSDTR(("offset too big %2x, ", offset));
-			offset = 15;
-			ESPSDTR(("sending back new offset\n"));
-			build_sync_nego_msg(esp, period, offset);
-			return EXTENDED_MESSAGE;
-		}
-
-		if (offset && period > esp->max_period) {
-			/* Yeee, async for this slow device. */
-			ESPSDTR(("period too long %2x, ", period));
-			build_sync_nego_msg(esp, 0, 0);
-			ESPSDTR(("hoping for msgout\n"));
-			esp_advance_phase(esp->current_SC, in_the_dark);
-			return EXTENDED_MESSAGE;
-		} else if (offset && period < esp->min_period) {
-			ESPSDTR(("period too short %2x, ", period));
-			period = esp->min_period;
-			if (esp->erev > esp236)
-				regval = 4;
-			else
-				regval = 5;
-		} else if (offset) {
-			int tmp;
-
-			ESPSDTR(("period is ok, "));
-			tmp = esp->ccycle / 1000;
-			regval = (((period << 2) + tmp - 1) / tmp);
-			if (regval && ((esp->erev == fas100a ||
-					esp->erev == fas236  ||
-					esp->erev == fashme))) {
-				if (period >= 50)
-					regval--;
-			}
-		}
-
-		if (offset) {
-			u8 bit;
-
-			esp_dev->sync_min_period = (regval & 0x1f);
-			esp_dev->sync_max_offset = (offset | esp->radelay);
-			if (esp->erev == fas100a || esp->erev == fas236 || esp->erev == fashme) {
-				if ((esp->erev == fas100a) || (esp->erev == fashme))
-					bit = ESP_CONFIG3_FAST;
-				else
-					bit = ESP_CONFIG3_FSCSI;
-				if (period < 50) {
-					/* On FAS366, if using fast-20 synchronous transfers
-					 * we need to make sure the REQ/ACK assert/deassert
-					 * control bits are clear.
-					 */
-					if (esp->erev == fashme)
-						esp_dev->sync_max_offset &= ~esp->radelay;
-					esp->config3[SCptr->device->id] |= bit;
-				} else {
-					esp->config3[SCptr->device->id] &= ~bit;
-				}
-				esp->prev_cfg3 = esp->config3[SCptr->device->id];
-				sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
-			}
-			esp->prev_soff = esp_dev->sync_max_offset;
-			esp->prev_stp = esp_dev->sync_min_period;
-			sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF);
-			sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP);
-			ESPSDTR(("soff=%2x stp=%2x cfg3=%2x\n",
-				 esp_dev->sync_max_offset,
-				 esp_dev->sync_min_period,
-				 esp->config3[SCptr->device->id]));
-
-			esp->snip = 0;
-		} else if (esp_dev->sync_max_offset) {
-			u8 bit;
-
-			/* back to async mode */
-			ESPSDTR(("unaccaptable sync nego, forcing async\n"));
-			esp_dev->sync_max_offset = 0;
-			esp_dev->sync_min_period = 0;
-			esp->prev_soff = 0;
-			esp->prev_stp = 0;
-			sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF);
-			sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP);
-			if (esp->erev == fas100a || esp->erev == fas236 || esp->erev == fashme) {
-				if ((esp->erev == fas100a) || (esp->erev == fashme))
-					bit = ESP_CONFIG3_FAST;
-				else
-					bit = ESP_CONFIG3_FSCSI;
-				esp->config3[SCptr->device->id] &= ~bit;
-				esp->prev_cfg3 = esp->config3[SCptr->device->id];
-				sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
-			}
-		}
-
-		sync_report(esp);
-
-		ESPSDTR(("chk multibyte msg: sync is known, "));
-		esp_dev->sync = 1;
-
-		if (message_out) {
-			ESPLOG(("esp%d: sending sdtr back, hoping for msgout\n",
-				esp->esp_id));
-			build_sync_nego_msg(esp, period, offset);
-			esp_advance_phase(SCptr, in_the_dark);
-			return EXTENDED_MESSAGE;
-		}
-
-		ESPSDTR(("returning zero\n"));
-		esp_advance_phase(SCptr, in_the_dark); /* ...or else! */
-		return 0;
-	} else if (esp->cur_msgin[2] == EXTENDED_WDTR) {
-		int size = 8 << esp->cur_msgin[3];
-
-		esp->wnip = 0;
-		if (esp->erev != fashme) {
-			ESPLOG(("esp%d: AIEEE wide msg received and not HME.\n",
-				esp->esp_id));
-			message_out = MESSAGE_REJECT;
-		} else if (size > 16) {
-			ESPLOG(("esp%d: AIEEE wide transfer for %d size "
-				"not supported.\n", esp->esp_id, size));
-			message_out = MESSAGE_REJECT;
-		} else {
-			/* Things look good; let's see what we got. */
-			if (size == 16) {
-				/* Set config 3 register for this target. */
-				esp->config3[SCptr->device->id] |= ESP_CONFIG3_EWIDE;
-			} else {
-				/* Just make sure it was one byte sized. */
-				if (size != 8) {
-					ESPLOG(("esp%d: Aieee, wide nego of %d size.\n",
-						esp->esp_id, size));
-					message_out = MESSAGE_REJECT;
-					goto finish;
-				}
-				/* Pure paranoia. */
-				esp->config3[SCptr->device->id] &= ~(ESP_CONFIG3_EWIDE);
-			}
-			esp->prev_cfg3 = esp->config3[SCptr->device->id];
-			sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
-
-			/* Regardless, next try for sync transfers. */
-			build_sync_nego_msg(esp, esp->sync_defp, 15);
-			esp_dev->sync = 1;
-			esp->snip = 1;
-			message_out = EXTENDED_MESSAGE;
-		}
-	} else if (esp->cur_msgin[2] == EXTENDED_MODIFY_DATA_POINTER) {
-		ESPLOG(("esp%d: rejecting modify data ptr msg\n", esp->esp_id));
-		message_out = MESSAGE_REJECT;
-	}
-finish:
-	esp_advance_phase(SCptr, in_the_dark);
-	return message_out;
-}
-
-static int esp_do_msgindone(struct esp *esp)
-{
-	struct scsi_cmnd *SCptr = esp->current_SC;
-	int message_out = 0, it = 0, rval;
-
-	rval = skipahead1(esp, SCptr, in_msgin, in_msgindone);
-	if (rval)
-		return rval;
-	if (SCptr->SCp.sent_command != in_status) {
-		if (!(esp->ireg & ESP_INTR_DC)) {
-			if (esp->msgin_len && (esp->sreg & ESP_STAT_PERR)) {
-				message_out = MSG_PARITY_ERROR;
-				esp_cmd(esp, ESP_CMD_FLUSH);
-			} else if (esp->erev != fashme &&
-			  (it = (sbus_readb(esp->eregs + ESP_FFLAGS) & ESP_FF_FBYTES)) != 1) {
-				/* We certainly dropped the ball somewhere. */
-				message_out = INITIATOR_ERROR;
-				esp_cmd(esp, ESP_CMD_FLUSH);
-			} else if (!esp->msgin_len) {
-				if (esp->erev == fashme)
-					it = esp->hme_fifo_workaround_buffer[0];
-				else
-					it = sbus_readb(esp->eregs + ESP_FDATA);
-				esp_advance_phase(SCptr, in_msgincont);
-			} else {
-				/* it is ok and we want it */
-				if (esp->erev == fashme)
-					it = esp->cur_msgin[esp->msgin_ctr] =
-						esp->hme_fifo_workaround_buffer[0];
-				else
-					it = esp->cur_msgin[esp->msgin_ctr] =
-						sbus_readb(esp->eregs + ESP_FDATA);
-				esp->msgin_ctr++;
-			}
-		} else {
-			esp_advance_phase(SCptr, in_the_dark);
-			return do_work_bus;
-		}
-	} else {
-		it = esp->cur_msgin[0];
-	}
-	if (!message_out && esp->msgin_len) {
-		if (esp->msgin_ctr < esp->msgin_len) {
-			esp_advance_phase(SCptr, in_msgincont);
-		} else if (esp->msgin_len == 1) {
-			message_out = check_singlebyte_msg(esp);
-		} else if (esp->msgin_len == 2) {
-			if (esp->cur_msgin[0] == EXTENDED_MESSAGE) {
-				if ((it + 2) >= 15) {
-					message_out = MESSAGE_REJECT;
-				} else {
-					esp->msgin_len = (it + 2);
-					esp_advance_phase(SCptr, in_msgincont);
-				}
-			} else {
-				message_out = MESSAGE_REJECT; /* foo on you */
-			}
-		} else {
-			message_out = check_multibyte_msg(esp);
-		}
-	}
-	if (message_out < 0) {
-		return -message_out;
-	} else if (message_out) {
-		if (((message_out != 1) &&
-		     ((message_out < 0x20) || (message_out & 0x80))))
-			esp->msgout_len = 1;
-		esp->cur_msgout[0] = message_out;
-		esp_cmd(esp, ESP_CMD_SATN);
-		esp_advance_phase(SCptr, in_the_dark);
-		esp->msgin_len = 0;
-	}
-	esp->sreg = sbus_readb(esp->eregs + ESP_STATUS);
-	esp->sreg &= ~(ESP_STAT_INTR);
-	if ((esp->sreg & (ESP_STAT_PMSG|ESP_STAT_PCD)) == (ESP_STAT_PMSG|ESP_STAT_PCD))
-		esp_cmd(esp, ESP_CMD_MOK);
-	if ((SCptr->SCp.sent_command == in_msgindone) &&
-	    (SCptr->SCp.phase == in_freeing))
-		return esp_do_freebus(esp);
-	return do_intr_end;
-}
-
-static int esp_do_cmdbegin(struct esp *esp)
-{
-	struct scsi_cmnd *SCptr = esp->current_SC;
-
-	esp_advance_phase(SCptr, in_cmdend);
-	if (esp->erev == fashme) {
-		u32 tmp = sbus_readl(esp->dregs + DMA_CSR);
-		int i;
-
-		for (i = 0; i < esp->esp_scmdleft; i++)
-			esp->esp_command[i] = *esp->esp_scmdp++;
-		esp->esp_scmdleft = 0;
-		esp_cmd(esp, ESP_CMD_FLUSH);
-		esp_setcount(esp->eregs, i, 1);
-		esp_cmd(esp, (ESP_CMD_DMA | ESP_CMD_TI));
-		tmp |= (DMA_SCSI_DISAB | DMA_ENABLE);
-		tmp &= ~(DMA_ST_WRITE);
-		sbus_writel(i, esp->dregs + DMA_COUNT);
-		sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR);
-		sbus_writel(tmp, esp->dregs + DMA_CSR);
-	} else {
-		u8 tmp;
-
-		esp_cmd(esp, ESP_CMD_FLUSH);
-		tmp = *esp->esp_scmdp++;
-		esp->esp_scmdleft--;
-		sbus_writeb(tmp, esp->eregs + ESP_FDATA);
-		esp_cmd(esp, ESP_CMD_TI);
-	}
-	return do_intr_end;
-}
-
-static int esp_do_cmddone(struct esp *esp)
-{
-	if (esp->erev == fashme)
-		dma_invalidate(esp);
-	else
-		esp_cmd(esp, ESP_CMD_NULL);
-
-	if (esp->ireg & ESP_INTR_BSERV) {
-		esp_advance_phase(esp->current_SC, in_the_dark);
-		return esp_do_phase_determine(esp);
-	}
-
-	ESPLOG(("esp%d: in do_cmddone() but didn't get BSERV interrupt.\n",
-		esp->esp_id));
-	return do_reset_bus;
-}
-
-static int esp_do_msgout(struct esp *esp)
-{
-	esp_cmd(esp, ESP_CMD_FLUSH);
-	switch (esp->msgout_len) {
-	case 1:
-		if (esp->erev == fashme)
-			hme_fifo_push(esp, &esp->cur_msgout[0], 1);
-		else
-			sbus_writeb(esp->cur_msgout[0], esp->eregs + ESP_FDATA);
-
-		esp_cmd(esp, ESP_CMD_TI);
-		break;
-
-	case 2:
-		esp->esp_command[0] = esp->cur_msgout[0];
-		esp->esp_command[1] = esp->cur_msgout[1];
-
-		if (esp->erev == fashme) {
-			hme_fifo_push(esp, &esp->cur_msgout[0], 2);
-			esp_cmd(esp, ESP_CMD_TI);
-		} else {
-			dma_setup(esp, esp->esp_command_dvma, 2, 0);
-			esp_setcount(esp->eregs, 2, 0);
-			esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI);
-		}
-		break;
-
-	case 4:
-		esp->esp_command[0] = esp->cur_msgout[0];
-		esp->esp_command[1] = esp->cur_msgout[1];
-		esp->esp_command[2] = esp->cur_msgout[2];
-		esp->esp_command[3] = esp->cur_msgout[3];
-		esp->snip = 1;
-
-		if (esp->erev == fashme) {
-			hme_fifo_push(esp, &esp->cur_msgout[0], 4);
-			esp_cmd(esp, ESP_CMD_TI);
-		} else {
-			dma_setup(esp, esp->esp_command_dvma, 4, 0);
-			esp_setcount(esp->eregs, 4, 0);
-			esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI);
-		}
-		break;
-
-	case 5:
-		esp->esp_command[0] = esp->cur_msgout[0];
-		esp->esp_command[1] = esp->cur_msgout[1];
-		esp->esp_command[2] = esp->cur_msgout[2];
-		esp->esp_command[3] = esp->cur_msgout[3];
-		esp->esp_command[4] = esp->cur_msgout[4];
-		esp->snip = 1;
-
-		if (esp->erev == fashme) {
-			hme_fifo_push(esp, &esp->cur_msgout[0], 5);
-			esp_cmd(esp, ESP_CMD_TI);
-		} else {
-			dma_setup(esp, esp->esp_command_dvma, 5, 0);
-			esp_setcount(esp->eregs, 5, 0);
-			esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI);
-		}
-		break;
-
-	default:
-		/* whoops */
-		ESPMISC(("bogus msgout sending NOP\n"));
-		esp->cur_msgout[0] = NOP;
-
-		if (esp->erev == fashme) {
-			hme_fifo_push(esp, &esp->cur_msgout[0], 1);
-		} else {
-			sbus_writeb(esp->cur_msgout[0], esp->eregs + ESP_FDATA);
-		}
-
-		esp->msgout_len = 1;
-		esp_cmd(esp, ESP_CMD_TI);
-		break;
-	};
-
-	esp_advance_phase(esp->current_SC, in_msgoutdone);
-	return do_intr_end;
-}
-
-static int esp_do_msgoutdone(struct esp *esp)
-{
-	if (esp->msgout_len > 1) {
-		/* XXX HME/FAS ATN deassert workaround required,
-		 * XXX no DMA flushing, only possible ESP_CMD_FLUSH
-		 * XXX to kill the fifo.
-		 */
-		if (esp->erev != fashme) {
-			u32 tmp;
-
-			while ((tmp = sbus_readl(esp->dregs + DMA_CSR)) & DMA_PEND_READ)
-				udelay(1);
-			tmp &= ~DMA_ENABLE;
-			sbus_writel(tmp, esp->dregs + DMA_CSR);
-			dma_invalidate(esp);
-		} else {
-			esp_cmd(esp, ESP_CMD_FLUSH);
-		}
-	}
-	if (!(esp->ireg & ESP_INTR_DC)) {
-		if (esp->erev != fashme)
-			esp_cmd(esp, ESP_CMD_NULL);
-		switch (esp->sreg & ESP_STAT_PMASK) {
-		case ESP_MOP:
-			/* whoops, parity error */
-			ESPLOG(("esp%d: still in msgout, parity error assumed\n",
-				esp->esp_id));
-			if (esp->msgout_len > 1)
-				esp_cmd(esp, ESP_CMD_SATN);
-			esp_advance_phase(esp->current_SC, in_msgout);
-			return do_work_bus;
-
-		case ESP_DIP:
-			break;
-
-		default:
-			/* Happy Meal fifo is touchy... */
-			if ((esp->erev != fashme) &&
-			    !fcount(esp) &&
-			    !(((struct esp_device *)esp->current_SC->device->hostdata)->sync_max_offset))
-				esp_cmd(esp, ESP_CMD_FLUSH);
-			break;
-
-		};
-	} else {
-		ESPLOG(("esp%d: disconnect, resetting bus\n", esp->esp_id));
-		return do_reset_bus;
-	}
-
-	/* If we sent out a synchronous negotiation message, update
-	 * our state.
-	 */
-	if (esp->cur_msgout[2] == EXTENDED_MESSAGE &&
-	    esp->cur_msgout[4] == EXTENDED_SDTR) {
-		esp->snip = 1; /* anal retentiveness... */
-	}
-
-	esp->prevmsgout = esp->cur_msgout[0];
-	esp->msgout_len = 0;
-	esp_advance_phase(esp->current_SC, in_the_dark);
-	return esp_do_phase_determine(esp);
-}
-
-static int esp_bus_unexpected(struct esp *esp)
-{
-	ESPLOG(("esp%d: command in weird state %2x\n",
-		esp->esp_id, esp->current_SC->SCp.phase));
-	return do_reset_bus;
-}
-
-static espfunc_t bus_vector[] = {
-	esp_do_data_finale,
-	esp_do_data_finale,
-	esp_bus_unexpected,
-	esp_do_msgin,
-	esp_do_msgincont,
-	esp_do_msgindone,
-	esp_do_msgout,
-	esp_do_msgoutdone,
-	esp_do_cmdbegin,
-	esp_do_cmddone,
-	esp_do_status,
-	esp_do_freebus,
-	esp_do_phase_determine,
-	esp_bus_unexpected,
-	esp_bus_unexpected,
-	esp_bus_unexpected,
-};
-
-/* This is the second tier in our dual-level SCSI state machine. */
-static int esp_work_bus(struct esp *esp)
-{
-	struct scsi_cmnd *SCptr = esp->current_SC;
-	unsigned int phase;
-
-	ESPBUS(("esp_work_bus: "));
-	if (!SCptr) {
-		ESPBUS(("reconnect\n"));
-		return esp_do_reconnect(esp);
-	}
-	phase = SCptr->SCp.phase;
-	if ((phase & 0xf0) == in_phases_mask)
-		return bus_vector[(phase & 0x0f)](esp);
-	else if ((phase & 0xf0) == in_slct_mask)
-		return esp_select_complete(esp);
-	else
-		return esp_bus_unexpected(esp);
-}
-
-static espfunc_t isvc_vector[] = {
-	NULL,
-	esp_do_phase_determine,
-	esp_do_resetbus,
-	esp_finish_reset,
-	esp_work_bus
-};
-
-/* Main interrupt handler for an esp adapter. */
-static void esp_handle(struct esp *esp)
-{
-	struct scsi_cmnd *SCptr;
-	int what_next = do_intr_end;
-
-	SCptr = esp->current_SC;
-
-	/* Check for errors. */
-	esp->sreg = sbus_readb(esp->eregs + ESP_STATUS);
-	esp->sreg &= (~ESP_STAT_INTR);
-	if (esp->erev == fashme) {
-		esp->sreg2 = sbus_readb(esp->eregs + ESP_STATUS2);
-		esp->seqreg = (sbus_readb(esp->eregs + ESP_SSTEP) & ESP_STEP_VBITS);
-	}
-
-	if (esp->sreg & (ESP_STAT_SPAM)) {
-		/* Gross error, could be due to one of:
-		 *
-		 * - top of fifo overwritten, could be because
-		 *   we tried to do a synchronous transfer with
-		 *   an offset greater than ESP fifo size
-		 *
-		 * - top of command register overwritten
-		 *
-		 * - DMA setup to go in one direction, SCSI
-		 *   bus points in the other, whoops
-		 *
-		 * - weird phase change during asynchronous
-		 *   data phase while we are initiator
-		 */
-		ESPLOG(("esp%d: Gross error sreg=%2x\n", esp->esp_id, esp->sreg));
-
-		/* If a command is live on the bus we cannot safely
-		 * reset the bus, so we'll just let the pieces fall
-		 * where they may.  Here we are hoping that the
-		 * target will be able to cleanly go away soon
-		 * so we can safely reset things.
-		 */
-		if (!SCptr) {
-			ESPLOG(("esp%d: No current cmd during gross error, "
-				"resetting bus\n", esp->esp_id));
-			what_next = do_reset_bus;
-			goto state_machine;
-		}
-	}
-
-	if (sbus_readl(esp->dregs + DMA_CSR) & DMA_HNDL_ERROR) {
-		/* A DMA gate array error.  Here we must
-		 * be seeing one of two things.  Either the
-		 * virtual to physical address translation
-		 * on the SBUS could not occur, else the
-		 * translation it did get pointed to a bogus
-		 * page.  Ho hum...
-		 */
-		ESPLOG(("esp%d: DMA error %08x\n", esp->esp_id,
-			sbus_readl(esp->dregs + DMA_CSR)));
-
-		/* DMA gate array itself must be reset to clear the
-		 * error condition.
-		 */
-		esp_reset_dma(esp);
-
-		what_next = do_reset_bus;
-		goto state_machine;
-	}
-
-	esp->ireg = sbus_readb(esp->eregs + ESP_INTRPT);   /* Unlatch intr reg */
-
-	if (esp->erev == fashme) {
-		/* This chip is really losing. */
-		ESPHME(("HME["));
-
-		ESPHME(("sreg2=%02x,", esp->sreg2));
-		/* Must latch fifo before reading the interrupt
-		 * register else garbage ends up in the FIFO
-		 * which confuses the driver utterly.
-		 */
-		if (!(esp->sreg2 & ESP_STAT2_FEMPTY) ||
-		    (esp->sreg2 & ESP_STAT2_F1BYTE)) {
-			ESPHME(("fifo_workaround]"));
-			hme_fifo_read(esp);
-		} else {
-			ESPHME(("no_fifo_workaround]"));
-		}
-	}
-
-	/* No current cmd is only valid at this point when there are
-	 * commands off the bus or we are trying a reset.
-	 */
-	if (!SCptr && !esp->disconnected_SC && !(esp->ireg & ESP_INTR_SR)) {
-		/* Panic is safe, since current_SC is null. */
-		ESPLOG(("esp%d: no command in esp_handle()\n", esp->esp_id));
-		panic("esp_handle: current_SC == penguin within interrupt!");
-	}
-
-	if (esp->ireg & (ESP_INTR_IC)) {
-		/* Illegal command fed to ESP.  Outside of obvious
-		 * software bugs that could cause this, there is
-		 * a condition with esp100 where we can confuse the
-		 * ESP into an erroneous illegal command interrupt
-		 * because it does not scrape the FIFO properly
-		 * for reselection.  See esp100_reconnect_hwbug()
-		 * to see how we try very hard to avoid this.
-		 */
-		ESPLOG(("esp%d: invalid command\n", esp->esp_id));
-
-		esp_dump_state(esp);
-
-		if (SCptr != NULL) {
-			/* Devices with very buggy firmware can drop BSY
-			 * during a scatter list interrupt when using sync
-			 * mode transfers.  We continue the transfer as
-			 * expected, the target drops the bus, the ESP
-			 * gets confused, and we get a illegal command
-			 * interrupt because the bus is in the disconnected
-			 * state now and ESP_CMD_TI is only allowed when
-			 * a nexus is alive on the bus.
-			 */
-			ESPLOG(("esp%d: Forcing async and disabling disconnect for "
-				"target %d\n", esp->esp_id, SCptr->device->id));
-			SCptr->device->borken = 1; /* foo on you */
-		}
-
-		what_next = do_reset_bus;
-	} else if (!(esp->ireg & ~(ESP_INTR_FDONE | ESP_INTR_BSERV | ESP_INTR_DC))) {
-		if (SCptr) {
-			unsigned int phase = SCptr->SCp.phase;
-
-			if (phase & in_phases_mask) {
-				what_next = esp_work_bus(esp);
-			} else if (phase & in_slct_mask) {
-				what_next = esp_select_complete(esp);
-			} else {
-				ESPLOG(("esp%d: interrupt for no good reason...\n",
-					esp->esp_id));
-				what_next = do_intr_end;
-			}
-		} else {
-			ESPLOG(("esp%d: BSERV or FDONE or DC while SCptr==NULL\n",
-				esp->esp_id));
-			what_next = do_reset_bus;
-		}
-	} else if (esp->ireg & ESP_INTR_SR) {
-		ESPLOG(("esp%d: SCSI bus reset interrupt\n", esp->esp_id));
-		what_next = do_reset_complete;
-	} else if (esp->ireg & (ESP_INTR_S | ESP_INTR_SATN)) {
-		ESPLOG(("esp%d: AIEEE we have been selected by another initiator!\n",
-			esp->esp_id));
-		what_next = do_reset_bus;
-	} else if (esp->ireg & ESP_INTR_RSEL) {
-		if (SCptr == NULL) {
-			/* This is ok. */
-			what_next = esp_do_reconnect(esp);
-		} else if (SCptr->SCp.phase & in_slct_mask) {
-			/* Only selection code knows how to clean
-			 * up properly.
-			 */
-			ESPDISC(("Reselected during selection attempt\n"));
-			what_next = esp_select_complete(esp);
-		} else {
-			ESPLOG(("esp%d: Reselected while bus is busy\n",
-				esp->esp_id));
-			what_next = do_reset_bus;
-		}
-	}
-
-	/* This is tier-one in our dual level SCSI state machine. */
-state_machine:
-	while (what_next != do_intr_end) {
-		if (what_next >= do_phase_determine &&
-		    what_next < do_intr_end) {
-			what_next = isvc_vector[what_next](esp);
-		} else {
-			/* state is completely lost ;-( */
-			ESPLOG(("esp%d: interrupt engine loses state, resetting bus\n",
-				esp->esp_id));
-			what_next = do_reset_bus;
-		}
-	}
-}
-
-/* Service only the ESP described by dev_id. */
-static irqreturn_t esp_intr(int irq, void *dev_id)
-{
-	struct esp *esp = dev_id;
-	unsigned long flags;
-
-	spin_lock_irqsave(esp->ehost->host_lock, flags);
-	if (ESP_IRQ_P(esp->dregs)) {
-		ESP_INTSOFF(esp->dregs);
-
-		ESPIRQ(("I[%d:%d](", smp_processor_id(), esp->esp_id));
-		esp_handle(esp);
-		ESPIRQ((")"));
-
-		ESP_INTSON(esp->dregs);
-	}
-	spin_unlock_irqrestore(esp->ehost->host_lock, flags);
-
-	return IRQ_HANDLED;
-}
-
-static int esp_slave_alloc(struct scsi_device *SDptr)
-{
-	struct esp_device *esp_dev =
-		kmalloc(sizeof(struct esp_device), GFP_ATOMIC);
-
-	if (!esp_dev)
-		return -ENOMEM;
-	memset(esp_dev, 0, sizeof(struct esp_device));
-	SDptr->hostdata = esp_dev;
-	return 0;
-}
-
-static void esp_slave_destroy(struct scsi_device *SDptr)
-{
-	struct esp *esp = (struct esp *) SDptr->host->hostdata;
-
-	esp->targets_present &= ~(1 << SDptr->id);
-	kfree(SDptr->hostdata);
-	SDptr->hostdata = NULL;
-}
-
-static struct scsi_host_template esp_template = {
-	.module			= THIS_MODULE,
-	.name			= "esp",
-	.info			= esp_info,
-	.slave_alloc		= esp_slave_alloc,
-	.slave_destroy		= esp_slave_destroy,
-	.queuecommand		= esp_queue,
-	.eh_abort_handler	= esp_abort,
-	.eh_bus_reset_handler	= esp_reset,
-	.can_queue		= 7,
-	.this_id		= 7,
-	.sg_tablesize		= SG_ALL,
-	.cmd_per_lun		= 1,
-	.use_clustering		= ENABLE_CLUSTERING,
-	.proc_name		= "esp",
-	.proc_info		= esp_proc_info,
-};
-
-#ifndef CONFIG_SUN4
-static struct of_device_id esp_match[] = {
-	{
-		.name = "SUNW,esp",
-		.data = &esp_template,
-	},
-	{
-		.name = "SUNW,fas",
-		.data = &esp_template,
-	},
-	{
-		.name = "esp",
-		.data = &esp_template,
-	},
-	{},
-};
-MODULE_DEVICE_TABLE(of, esp_match);
-
-static struct of_platform_driver esp_sbus_driver = {
-	.name		= "esp",
-	.match_table	= esp_match,
-	.probe		= esp_sbus_probe,
-	.remove		= __devexit_p(esp_sbus_remove),
-};
-#endif
-
-static int __init esp_init(void)
-{
-#ifdef CONFIG_SUN4
-	return esp_sun4_probe(&esp_template);
-#else
-	return of_register_driver(&esp_sbus_driver, &sbus_bus_type);
-#endif
-}
-
-static void __exit esp_exit(void)
-{
-#ifdef CONFIG_SUN4
-	esp_sun4_remove();
-#else
-	of_unregister_driver(&esp_sbus_driver);
-#endif
-}
-
-MODULE_DESCRIPTION("ESP Sun SCSI driver");
-MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(DRV_VERSION);
-
-module_init(esp_init);
-module_exit(esp_exit);
diff --git a/drivers/scsi/esp.h b/drivers/scsi/esp.h
deleted file mode 100644
index a98cda9..0000000
--- a/drivers/scsi/esp.h
+++ /dev/null
@@ -1,406 +0,0 @@
-/* $Id: esp.h,v 1.29 2001/12/11 04:55:47 davem Exp $
- * esp.h:  Defines and structures for the Sparc ESP (Enhanced SCSI
- *         Processor) driver under Linux.
- *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- */
-
-#ifndef _SPARC_ESP_H
-#define _SPARC_ESP_H
-
-/* For dvma controller register definitions. */
-#include <asm/dma.h>
-
-/* The ESP SCSI controllers have their register sets in three
- * "classes":
- *
- * 1) Registers which are both read and write.
- * 2) Registers which are read only.
- * 3) Registers which are write only.
- *
- * Yet, they all live within the same IO space.
- */
-
-/* All the ESP registers are one byte each and are accessed longwords
- * apart with a big-endian ordering to the bytes.
- */
-					/* Access    Description              Offset */
-#define ESP_TCLOW	0x00UL		/* rw  Low bits of the transfer count 0x00   */
-#define ESP_TCMED	0x04UL		/* rw  Mid bits of the transfer count 0x04   */
-#define ESP_FDATA	0x08UL		/* rw  FIFO data bits                 0x08   */
-#define ESP_CMD		0x0cUL		/* rw  SCSI command bits              0x0c   */
-#define ESP_STATUS	0x10UL		/* ro  ESP status register            0x10   */
-#define ESP_BUSID	ESP_STATUS	/* wo  Bus ID for select/reselect     0x10   */
-#define ESP_INTRPT	0x14UL		/* ro  Kind of interrupt              0x14   */
-#define ESP_TIMEO	ESP_INTRPT	/* wo  Timeout value for select/resel 0x14   */
-#define ESP_SSTEP	0x18UL		/* ro  Sequence step register         0x18   */
-#define ESP_STP		ESP_SSTEP	/* wo  Transfer period per sync       0x18   */
-#define ESP_FFLAGS	0x1cUL		/* ro  Bits of current FIFO info      0x1c   */
-#define ESP_SOFF	ESP_FFLAGS	/* wo  Sync offset                    0x1c   */
-#define ESP_CFG1	0x20UL		/* rw  First configuration register   0x20   */
-#define ESP_CFACT	0x24UL		/* wo  Clock conversion factor        0x24   */
-#define ESP_STATUS2	ESP_CFACT	/* ro  HME status2 register           0x24   */
-#define ESP_CTEST	0x28UL		/* wo  Chip test register             0x28   */
-#define ESP_CFG2	0x2cUL		/* rw  Second configuration register  0x2c   */
-#define ESP_CFG3	0x30UL		/* rw  Third configuration register   0x30   */
-#define ESP_TCHI	0x38UL		/* rw  High bits of transfer count    0x38   */
-#define ESP_UID		ESP_TCHI	/* ro  Unique ID code                 0x38   */
-#define FAS_RLO		ESP_TCHI	/* rw  HME extended counter           0x38   */
-#define ESP_FGRND	0x3cUL		/* rw  Data base for fifo             0x3c   */
-#define FAS_RHI		ESP_FGRND	/* rw  HME extended counter           0x3c   */
-#define ESP_REG_SIZE	0x40UL
-
-/* Various revisions of the ESP board. */
-enum esp_rev {
-	esp100     = 0x00,  /* NCR53C90 - very broken */
-	esp100a    = 0x01,  /* NCR53C90A */
-	esp236     = 0x02,
-	fas236     = 0x03,
-	fas100a    = 0x04,
-	fast       = 0x05,
-	fashme     = 0x06,
-	espunknown = 0x07
-};
-
-/* We allocate one of these for each scsi device and attach it to
- * SDptr->hostdata for use in the driver
- */
-struct esp_device {
-  unsigned char sync_min_period;
-  unsigned char sync_max_offset;
-  unsigned sync:1;
-  unsigned wide:1;
-  unsigned disconnect:1;
-};
-
-struct scsi_cmnd;
-
-/* We get one of these for each ESP probed. */
-struct esp {
-	void __iomem		*eregs;		/* ESP controller registers */
-	void __iomem		*dregs;		/* DMA controller registers */
-	struct sbus_dma		*dma;		/* DMA controller sw state */
-	struct Scsi_Host	*ehost;		/* Backpointer to SCSI Host */
-	struct sbus_dev		*sdev;		/* Pointer to SBus entry */
-
-	/* ESP Configuration Registers */
-	u8			config1;	/* Copy of the 1st config register */
-	u8			config2;	/* Copy of the 2nd config register */
-	u8			config3[16];	/* Copy of the 3rd config register */
-
-	/* The current command we are sending to the ESP chip.  This esp_command
-	 * ptr needs to be mapped in DVMA area so we can send commands and read
-	 * from the ESP fifo without burning precious CPU cycles.  Programmed I/O
-	 * sucks when we have the DVMA to do it for us.  The ESP is stupid and will
-	 * only send out 6, 10, and 12 byte SCSI commands, others we need to send
-	 * one byte at a time.  esp_slowcmd being set says that we are doing one
-	 * of the command types ESP doesn't understand, esp_scmdp keeps track of
-	 * which byte we are sending, esp_scmdleft says how many bytes to go.
-	 */
-	volatile u8		*esp_command;    /* Location of command (CPU view)  */
-	__u32			esp_command_dvma;/* Location of command (DVMA view) */
-	unsigned char		esp_clen;	 /* Length of this command */
-	unsigned char		esp_slowcmd;
-	unsigned char		*esp_scmdp;
-	unsigned char		esp_scmdleft;
-
-	/* The following are used to determine the cause of an IRQ. Upon every
-	 * IRQ entry we synchronize these with the hardware registers.
-	 */
-	u8			ireg;		/* Copy of ESP interrupt register */
-	u8			sreg;		/* Copy of ESP status register */
-	u8			seqreg;		/* Copy of ESP sequence step register */
-	u8			sreg2;		/* Copy of HME status2 register */
-
-	/* To save register writes to the ESP, which can be expensive, we
-	 * keep track of the previous value that various registers had for
-	 * the last target we connected to.  If they are the same for the
-	 * current target, we skip the register writes as they are not needed.
-	 */
-	u8			prev_soff, prev_stp;
-	u8			prev_cfg3, __cache_pad;
-
-	/* We also keep a cache of the previous FAS/HME DMA CSR register value.  */
-	u32			prev_hme_dmacsr;
-
-	/* The HME is the biggest piece of shit I have ever seen. */
-	u8			hme_fifo_workaround_buffer[16 * 2];
-	u8			hme_fifo_workaround_count;
-
-	/* For each target we keep track of save/restore data
-	 * pointer information.  This needs to be updated majorly
-	 * when we add support for tagged queueing.  -DaveM
-	 */
-	struct esp_pointers {
-		char			*saved_ptr;
-		struct scatterlist	*saved_buffer;
-		int			saved_this_residual;
-		int			saved_buffers_residual;
-	} data_pointers[16] /*XXX [MAX_TAGS_PER_TARGET]*/;
-
-	/* Clock periods, frequencies, synchronization, etc. */
-	unsigned int		cfreq;		/* Clock frequency in HZ */
-	unsigned int		cfact;		/* Clock conversion factor */
-	unsigned int		raw_cfact;	/* Raw copy from probing */
-	unsigned int		ccycle;		/* One ESP clock cycle */
-	unsigned int		ctick;		/* One ESP clock time */
-	unsigned int		radelay;	/* FAST chip req/ack delay */
-	unsigned int		neg_defp;	/* Default negotiation period */
-	unsigned int		sync_defp;	/* Default sync transfer period */
-	unsigned int		max_period;	/* longest our period can be */
-	unsigned int		min_period;	/* shortest period we can withstand */
-
-	struct esp		*next;		/* Next ESP we probed or NULL */
-	char			prom_name[64];	/* Name of ESP device from prom */
-	int			prom_node;	/* Prom node where ESP found */
-	int			esp_id;		/* Unique per-ESP ID number */
-
-	/* For slow to medium speed input clock rates we shoot for 5mb/s,
-	 * but for high input clock rates we try to do 10mb/s although I
-	 * don't think a transfer can even run that fast with an ESP even
-	 * with DMA2 scatter gather pipelining.
-	 */
-#define SYNC_DEFP_SLOW            0x32   /* 5mb/s  */
-#define SYNC_DEFP_FAST            0x19   /* 10mb/s */
-
-	unsigned int		snip;		/* Sync. negotiation in progress */
-	unsigned int		wnip;		/* WIDE negotiation in progress */
-	unsigned int		targets_present;/* targets spoken to before */
-
-	int		current_transfer_size;	/* Set at beginning of data dma */
-
-	u8			espcmdlog[32];	/* Log of current esp cmds sent. */
-	u8			espcmdent;	/* Current entry in esp cmd log. */
-
-	/* Misc. info about this ESP */
-	enum esp_rev		erev;		/* ESP revision */
-	int			irq;		/* SBus IRQ for this ESP */
-	int			scsi_id;	/* Who am I as initiator? */
-	int			scsi_id_mask;	/* Bitmask of 'me'. */
-	int			diff;		/* Differential SCSI bus? */
-	int			bursts;		/* Burst sizes our DVMA supports */
-
-	/* Our command queues, only one cmd lives in the current_SC queue. */
-	struct scsi_cmnd	*issue_SC;	/* Commands to be issued */
-	struct scsi_cmnd	*current_SC;	/* Who is currently working the bus */
-	struct scsi_cmnd	*disconnected_SC;/* Commands disconnected from the bus */
-
-	/* Message goo */
-	u8			cur_msgout[16];
-	u8			cur_msgin[16];
-	u8			prevmsgout, prevmsgin;
-	u8			msgout_len, msgin_len;
-	u8			msgout_ctr, msgin_ctr;
-
-	/* States that we cannot keep in the per cmd structure because they
-	 * cannot be assosciated with any specific command.
-	 */
-	u8			resetting_bus;
-	wait_queue_head_t	reset_queue;
-};
-
-/* Bitfield meanings for the above registers. */
-
-/* ESP config reg 1, read-write, found on all ESP chips */
-#define ESP_CONFIG1_ID        0x07             /* My BUS ID bits */
-#define ESP_CONFIG1_CHTEST    0x08             /* Enable ESP chip tests */
-#define ESP_CONFIG1_PENABLE   0x10             /* Enable parity checks */
-#define ESP_CONFIG1_PARTEST   0x20             /* Parity test mode enabled? */
-#define ESP_CONFIG1_SRRDISAB  0x40             /* Disable SCSI reset reports */
-#define ESP_CONFIG1_SLCABLE   0x80             /* Enable slow cable mode */
-
-/* ESP config reg 2, read-write, found only on esp100a+esp200+esp236 chips */
-#define ESP_CONFIG2_DMAPARITY 0x01             /* enable DMA Parity (200,236) */
-#define ESP_CONFIG2_REGPARITY 0x02             /* enable reg Parity (200,236) */
-#define ESP_CONFIG2_BADPARITY 0x04             /* Bad parity target abort  */
-#define ESP_CONFIG2_SCSI2ENAB 0x08             /* Enable SCSI-2 features (tmode only) */
-#define ESP_CONFIG2_HI        0x10             /* High Impedance DREQ ???  */
-#define ESP_CONFIG2_HMEFENAB  0x10             /* HME features enable */
-#define ESP_CONFIG2_BCM       0x20             /* Enable byte-ctrl (236)   */
-#define ESP_CONFIG2_DISPINT   0x20             /* Disable pause irq (hme) */
-#define ESP_CONFIG2_FENAB     0x40             /* Enable features (fas100,esp216)      */
-#define ESP_CONFIG2_SPL       0x40             /* Enable status-phase latch (esp236)   */
-#define ESP_CONFIG2_MKDONE    0x40             /* HME magic feature */
-#define ESP_CONFIG2_HME32     0x80             /* HME 32 extended */
-#define ESP_CONFIG2_MAGIC     0xe0             /* Invalid bits... */
-
-/* ESP config register 3 read-write, found only esp236+fas236+fas100a+hme chips */
-#define ESP_CONFIG3_FCLOCK    0x01             /* FAST SCSI clock rate (esp100a/hme) */
-#define ESP_CONFIG3_TEM       0x01             /* Enable thresh-8 mode (esp/fas236)  */
-#define ESP_CONFIG3_FAST      0x02             /* Enable FAST SCSI     (esp100a/hme) */
-#define ESP_CONFIG3_ADMA      0x02             /* Enable alternate-dma (esp/fas236)  */
-#define ESP_CONFIG3_TENB      0x04             /* group2 SCSI2 support (esp100a/hme) */
-#define ESP_CONFIG3_SRB       0x04             /* Save residual byte   (esp/fas236)  */
-#define ESP_CONFIG3_TMS       0x08             /* Three-byte msg's ok  (esp100a/hme) */
-#define ESP_CONFIG3_FCLK      0x08             /* Fast SCSI clock rate (esp/fas236)  */
-#define ESP_CONFIG3_IDMSG     0x10             /* ID message checking  (esp100a/hme) */
-#define ESP_CONFIG3_FSCSI     0x10             /* Enable FAST SCSI     (esp/fas236)  */
-#define ESP_CONFIG3_GTM       0x20             /* group2 SCSI2 support (esp/fas236)  */
-#define ESP_CONFIG3_IDBIT3    0x20             /* Bit 3 of HME SCSI-ID (hme)         */
-#define ESP_CONFIG3_TBMS      0x40             /* Three-byte msg's ok  (esp/fas236)  */
-#define ESP_CONFIG3_EWIDE     0x40             /* Enable Wide-SCSI     (hme)         */
-#define ESP_CONFIG3_IMS       0x80             /* ID msg chk'ng        (esp/fas236)  */
-#define ESP_CONFIG3_OBPUSH    0x80             /* Push odd-byte to dma (hme)         */
-
-/* ESP command register read-write */
-/* Group 1 commands:  These may be sent at any point in time to the ESP
- *                    chip.  None of them can generate interrupts 'cept
- *                    the "SCSI bus reset" command if you have not disabled
- *                    SCSI reset interrupts in the config1 ESP register.
- */
-#define ESP_CMD_NULL          0x00             /* Null command, ie. a nop */
-#define ESP_CMD_FLUSH         0x01             /* FIFO Flush */
-#define ESP_CMD_RC            0x02             /* Chip reset */
-#define ESP_CMD_RS            0x03             /* SCSI bus reset */
-
-/* Group 2 commands:  ESP must be an initiator and connected to a target
- *                    for these commands to work.
- */
-#define ESP_CMD_TI            0x10             /* Transfer Information */
-#define ESP_CMD_ICCSEQ        0x11             /* Initiator cmd complete sequence */
-#define ESP_CMD_MOK           0x12             /* Message okie-dokie */
-#define ESP_CMD_TPAD          0x18             /* Transfer Pad */
-#define ESP_CMD_SATN          0x1a             /* Set ATN */
-#define ESP_CMD_RATN          0x1b             /* De-assert ATN */
-
-/* Group 3 commands:  ESP must be in the MSGOUT or MSGIN state and be connected
- *                    to a target as the initiator for these commands to work.
- */
-#define ESP_CMD_SMSG          0x20             /* Send message */
-#define ESP_CMD_SSTAT         0x21             /* Send status */
-#define ESP_CMD_SDATA         0x22             /* Send data */
-#define ESP_CMD_DSEQ          0x23             /* Discontinue Sequence */
-#define ESP_CMD_TSEQ          0x24             /* Terminate Sequence */
-#define ESP_CMD_TCCSEQ        0x25             /* Target cmd cmplt sequence */
-#define ESP_CMD_DCNCT         0x27             /* Disconnect */
-#define ESP_CMD_RMSG          0x28             /* Receive Message */
-#define ESP_CMD_RCMD          0x29             /* Receive Command */
-#define ESP_CMD_RDATA         0x2a             /* Receive Data */
-#define ESP_CMD_RCSEQ         0x2b             /* Receive cmd sequence */
-
-/* Group 4 commands:  The ESP must be in the disconnected state and must
- *                    not be connected to any targets as initiator for
- *                    these commands to work.
- */
-#define ESP_CMD_RSEL          0x40             /* Reselect */
-#define ESP_CMD_SEL           0x41             /* Select w/o ATN */
-#define ESP_CMD_SELA          0x42             /* Select w/ATN */
-#define ESP_CMD_SELAS         0x43             /* Select w/ATN & STOP */
-#define ESP_CMD_ESEL          0x44             /* Enable selection */
-#define ESP_CMD_DSEL          0x45             /* Disable selections */
-#define ESP_CMD_SA3           0x46             /* Select w/ATN3 */
-#define ESP_CMD_RSEL3         0x47             /* Reselect3 */
-
-/* This bit enables the ESP's DMA on the SBus */
-#define ESP_CMD_DMA           0x80             /* Do DMA? */
-
-
-/* ESP status register read-only */
-#define ESP_STAT_PIO          0x01             /* IO phase bit */
-#define ESP_STAT_PCD          0x02             /* CD phase bit */
-#define ESP_STAT_PMSG         0x04             /* MSG phase bit */
-#define ESP_STAT_PMASK        0x07             /* Mask of phase bits */
-#define ESP_STAT_TDONE        0x08             /* Transfer Completed */
-#define ESP_STAT_TCNT         0x10             /* Transfer Counter Is Zero */
-#define ESP_STAT_PERR         0x20             /* Parity error */
-#define ESP_STAT_SPAM         0x40             /* Real bad error */
-/* This indicates the 'interrupt pending' condition on esp236, it is a reserved
- * bit on other revs of the ESP.
- */
-#define ESP_STAT_INTR         0x80             /* Interrupt */
-
-/* HME only: status 2 register */
-#define ESP_STAT2_SCHBIT      0x01 /* Upper bits 3-7 of sstep enabled */
-#define ESP_STAT2_FFLAGS      0x02 /* The fifo flags are now latched */
-#define ESP_STAT2_XCNT        0x04 /* The transfer counter is latched */
-#define ESP_STAT2_CREGA       0x08 /* The command reg is active now */
-#define ESP_STAT2_WIDE        0x10 /* Interface on this adapter is wide */
-#define ESP_STAT2_F1BYTE      0x20 /* There is one byte at top of fifo */
-#define ESP_STAT2_FMSB        0x40 /* Next byte in fifo is most significant */
-#define ESP_STAT2_FEMPTY      0x80 /* FIFO is empty */
-
-/* The status register can be masked with ESP_STAT_PMASK and compared
- * with the following values to determine the current phase the ESP
- * (at least thinks it) is in.  For our purposes we also add our own
- * software 'done' bit for our phase management engine.
- */
-#define ESP_DOP   (0)                                       /* Data Out  */
-#define ESP_DIP   (ESP_STAT_PIO)                            /* Data In   */
-#define ESP_CMDP  (ESP_STAT_PCD)                            /* Command   */
-#define ESP_STATP (ESP_STAT_PCD|ESP_STAT_PIO)               /* Status    */
-#define ESP_MOP   (ESP_STAT_PMSG|ESP_STAT_PCD)              /* Message Out */
-#define ESP_MIP   (ESP_STAT_PMSG|ESP_STAT_PCD|ESP_STAT_PIO) /* Message In */
-
-/* ESP interrupt register read-only */
-#define ESP_INTR_S            0x01             /* Select w/o ATN */
-#define ESP_INTR_SATN         0x02             /* Select w/ATN */
-#define ESP_INTR_RSEL         0x04             /* Reselected */
-#define ESP_INTR_FDONE        0x08             /* Function done */
-#define ESP_INTR_BSERV        0x10             /* Bus service */
-#define ESP_INTR_DC           0x20             /* Disconnect */
-#define ESP_INTR_IC           0x40             /* Illegal command given */
-#define ESP_INTR_SR           0x80             /* SCSI bus reset detected */
-
-/* Interrupt status macros */
-#define ESP_SRESET_IRQ(esp)  ((esp)->intreg & (ESP_INTR_SR))
-#define ESP_ILLCMD_IRQ(esp)  ((esp)->intreg & (ESP_INTR_IC))
-#define ESP_SELECT_WITH_ATN_IRQ(esp)     ((esp)->intreg & (ESP_INTR_SATN))
-#define ESP_SELECT_WITHOUT_ATN_IRQ(esp)  ((esp)->intreg & (ESP_INTR_S))
-#define ESP_SELECTION_IRQ(esp)  ((ESP_SELECT_WITH_ATN_IRQ(esp)) ||         \
-				 (ESP_SELECT_WITHOUT_ATN_IRQ(esp)))
-#define ESP_RESELECTION_IRQ(esp)         ((esp)->intreg & (ESP_INTR_RSEL))
-
-/* ESP sequence step register read-only */
-#define ESP_STEP_VBITS        0x07             /* Valid bits */
-#define ESP_STEP_ASEL         0x00             /* Selection&Arbitrate cmplt */
-#define ESP_STEP_SID          0x01             /* One msg byte sent */
-#define ESP_STEP_NCMD         0x02             /* Was not in command phase */
-#define ESP_STEP_PPC          0x03             /* Early phase chg caused cmnd
-                                                * bytes to be lost
-                                                */
-#define ESP_STEP_FINI4        0x04             /* Command was sent ok */
-
-/* Ho hum, some ESP's set the step register to this as well... */
-#define ESP_STEP_FINI5        0x05
-#define ESP_STEP_FINI6        0x06
-#define ESP_STEP_FINI7        0x07
-
-/* ESP chip-test register read-write */
-#define ESP_TEST_TARG         0x01             /* Target test mode */
-#define ESP_TEST_INI          0x02             /* Initiator test mode */
-#define ESP_TEST_TS           0x04             /* Tristate test mode */
-
-/* ESP unique ID register read-only, found on fas236+fas100a only */
-#define ESP_UID_F100A         0x00             /* ESP FAS100A  */
-#define ESP_UID_F236          0x02             /* ESP FAS236   */
-#define ESP_UID_REV           0x07             /* ESP revision */
-#define ESP_UID_FAM           0xf8             /* ESP family   */
-
-/* ESP fifo flags register read-only */
-/* Note that the following implies a 16 byte FIFO on the ESP. */
-#define ESP_FF_FBYTES         0x1f             /* Num bytes in FIFO */
-#define ESP_FF_ONOTZERO       0x20             /* offset ctr not zero (esp100) */
-#define ESP_FF_SSTEP          0xe0             /* Sequence step */
-
-/* ESP clock conversion factor register write-only */
-#define ESP_CCF_F0            0x00             /* 35.01MHz - 40MHz */
-#define ESP_CCF_NEVER         0x01             /* Set it to this and die */
-#define ESP_CCF_F2            0x02             /* 10MHz */
-#define ESP_CCF_F3            0x03             /* 10.01MHz - 15MHz */
-#define ESP_CCF_F4            0x04             /* 15.01MHz - 20MHz */
-#define ESP_CCF_F5            0x05             /* 20.01MHz - 25MHz */
-#define ESP_CCF_F6            0x06             /* 25.01MHz - 30MHz */
-#define ESP_CCF_F7            0x07             /* 30.01MHz - 35MHz */
-
-/* HME only... */
-#define ESP_BUSID_RESELID     0x10
-#define ESP_BUSID_CTR32BIT    0x40
-
-#define ESP_BUS_TIMEOUT        275             /* In milli-seconds */
-#define ESP_TIMEO_CONST       8192
-#define ESP_NEG_DEFP(mhz, cfact) \
-        ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (8192 * (cfact)))
-#define ESP_MHZ_TO_CYCLE(mhertz)  ((1000000000) / ((mhertz) / 1000))
-#define ESP_TICK(ccf, cycle)  ((7682 * (ccf) * (cycle) / 1000))
-
-#endif /* !(_SPARC_ESP_H) */
diff --git a/drivers/scsi/esp_scsi.c b/drivers/scsi/esp_scsi.c
new file mode 100644
index 0000000..99ce033
--- /dev/null
+++ b/drivers/scsi/esp_scsi.c
@@ -0,0 +1,2711 @@
+/* esp_scsi.c: ESP SCSI driver.
+ *
+ * Copyright (C) 2007 David S. Miller (davem@davemloft.net)
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/completion.h>
+#include <linux/kallsyms.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/irqreturn.h>
+
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_transport_spi.h>
+
+#include "esp_scsi.h"
+
+#define DRV_MODULE_NAME		"esp"
+#define PFX DRV_MODULE_NAME	": "
+#define DRV_VERSION		"2.000"
+#define DRV_MODULE_RELDATE	"April 19, 2007"
+
+/* SCSI bus reset settle time in seconds.  */
+static int esp_bus_reset_settle = 3;
+
+static u32 esp_debug;
+#define ESP_DEBUG_INTR		0x00000001
+#define ESP_DEBUG_SCSICMD	0x00000002
+#define ESP_DEBUG_RESET		0x00000004
+#define ESP_DEBUG_MSGIN		0x00000008
+#define ESP_DEBUG_MSGOUT	0x00000010
+#define ESP_DEBUG_CMDDONE	0x00000020
+#define ESP_DEBUG_DISCONNECT	0x00000040
+#define ESP_DEBUG_DATASTART	0x00000080
+#define ESP_DEBUG_DATADONE	0x00000100
+#define ESP_DEBUG_RECONNECT	0x00000200
+#define ESP_DEBUG_AUTOSENSE	0x00000400
+
+#define esp_log_intr(f, a...) \
+do {	if (esp_debug & ESP_DEBUG_INTR) \
+		printk(f, ## a); \
+} while (0)
+
+#define esp_log_reset(f, a...) \
+do {	if (esp_debug & ESP_DEBUG_RESET) \
+		printk(f, ## a); \
+} while (0)
+
+#define esp_log_msgin(f, a...) \
+do {	if (esp_debug & ESP_DEBUG_MSGIN) \
+		printk(f, ## a); \
+} while (0)
+
+#define esp_log_msgout(f, a...) \
+do {	if (esp_debug & ESP_DEBUG_MSGOUT) \
+		printk(f, ## a); \
+} while (0)
+
+#define esp_log_cmddone(f, a...) \
+do {	if (esp_debug & ESP_DEBUG_CMDDONE) \
+		printk(f, ## a); \
+} while (0)
+
+#define esp_log_disconnect(f, a...) \
+do {	if (esp_debug & ESP_DEBUG_DISCONNECT) \
+		printk(f, ## a); \
+} while (0)
+
+#define esp_log_datastart(f, a...) \
+do {	if (esp_debug & ESP_DEBUG_DATASTART) \
+		printk(f, ## a); \
+} while (0)
+
+#define esp_log_datadone(f, a...) \
+do {	if (esp_debug & ESP_DEBUG_DATADONE) \
+		printk(f, ## a); \
+} while (0)
+
+#define esp_log_reconnect(f, a...) \
+do {	if (esp_debug & ESP_DEBUG_RECONNECT) \
+		printk(f, ## a); \
+} while (0)
+
+#define esp_log_autosense(f, a...) \
+do {	if (esp_debug & ESP_DEBUG_AUTOSENSE) \
+		printk(f, ## a); \
+} while (0)
+
+#define esp_read8(REG)		esp->ops->esp_read8(esp, REG)
+#define esp_write8(VAL,REG)	esp->ops->esp_write8(esp, VAL, REG)
+
+static void esp_log_fill_regs(struct esp *esp,
+			      struct esp_event_ent *p)
+{
+	p->sreg = esp->sreg;
+	p->seqreg = esp->seqreg;
+	p->sreg2 = esp->sreg2;
+	p->ireg = esp->ireg;
+	p->select_state = esp->select_state;
+	p->event = esp->event;
+}
+
+void scsi_esp_cmd(struct esp *esp, u8 val)
+{
+	struct esp_event_ent *p;
+	int idx = esp->esp_event_cur;
+
+	p = &esp->esp_event_log[idx];
+	p->type = ESP_EVENT_TYPE_CMD;
+	p->val = val;
+	esp_log_fill_regs(esp, p);
+
+	esp->esp_event_cur = (idx + 1) & (ESP_EVENT_LOG_SZ - 1);
+
+	esp_write8(val, ESP_CMD);
+}
+EXPORT_SYMBOL(scsi_esp_cmd);
+
+static void esp_event(struct esp *esp, u8 val)
+{
+	struct esp_event_ent *p;
+	int idx = esp->esp_event_cur;
+
+	p = &esp->esp_event_log[idx];
+	p->type = ESP_EVENT_TYPE_EVENT;
+	p->val = val;
+	esp_log_fill_regs(esp, p);
+
+	esp->esp_event_cur = (idx + 1) & (ESP_EVENT_LOG_SZ - 1);
+
+	esp->event = val;
+}
+
+static void esp_dump_cmd_log(struct esp *esp)
+{
+	int idx = esp->esp_event_cur;
+	int stop = idx;
+
+	printk(KERN_INFO PFX "esp%d: Dumping command log\n",
+	       esp->host->unique_id);
+	do {
+		struct esp_event_ent *p = &esp->esp_event_log[idx];
+
+		printk(KERN_INFO PFX "esp%d: ent[%d] %s ",
+		       esp->host->unique_id, idx,
+		       p->type == ESP_EVENT_TYPE_CMD ? "CMD" : "EVENT");
+
+		printk("val[%02x] sreg[%02x] seqreg[%02x] "
+		       "sreg2[%02x] ireg[%02x] ss[%02x] event[%02x]\n",
+		       p->val, p->sreg, p->seqreg,
+		       p->sreg2, p->ireg, p->select_state, p->event);
+
+		idx = (idx + 1) & (ESP_EVENT_LOG_SZ - 1);
+	} while (idx != stop);
+}
+
+static void esp_flush_fifo(struct esp *esp)
+{
+	scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+	if (esp->rev == ESP236) {
+		int lim = 1000;
+
+		while (esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES) {
+			if (--lim == 0) {
+				printk(KERN_ALERT PFX "esp%d: ESP_FF_BYTES "
+				       "will not clear!\n",
+				       esp->host->unique_id);
+				break;
+			}
+			udelay(1);
+		}
+	}
+}
+
+static void hme_read_fifo(struct esp *esp)
+{
+	int fcnt = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES;
+	int idx = 0;
+
+	while (fcnt--) {
+		esp->fifo[idx++] = esp_read8(ESP_FDATA);
+		esp->fifo[idx++] = esp_read8(ESP_FDATA);
+	}
+	if (esp->sreg2 & ESP_STAT2_F1BYTE) {
+		esp_write8(0, ESP_FDATA);
+		esp->fifo[idx++] = esp_read8(ESP_FDATA);
+		scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+	}
+	esp->fifo_cnt = idx;
+}
+
+static void esp_set_all_config3(struct esp *esp, u8 val)
+{
+	int i;
+
+	for (i = 0; i < ESP_MAX_TARGET; i++)
+		esp->target[i].esp_config3 = val;
+}
+
+/* Reset the ESP chip, _not_ the SCSI bus. */
+static void esp_reset_esp(struct esp *esp)
+{
+	u8 family_code, version;
+
+	/* Now reset the ESP chip */
+	scsi_esp_cmd(esp, ESP_CMD_RC);
+	scsi_esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA);
+	scsi_esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA);
+
+	/* Reload the configuration registers */
+	esp_write8(esp->cfact, ESP_CFACT);
+
+	esp->prev_stp = 0;
+	esp_write8(esp->prev_stp, ESP_STP);
+
+	esp->prev_soff = 0;
+	esp_write8(esp->prev_soff, ESP_SOFF);
+
+	esp_write8(esp->neg_defp, ESP_TIMEO);
+
+	/* This is the only point at which it is reliable to read
+	 * the ID-code for a fast ESP chip variants.
+	 */
+	esp->max_period = ((35 * esp->ccycle) / 1000);
+	if (esp->rev == FAST) {
+		version = esp_read8(ESP_UID);
+		family_code = (version & 0xf8) >> 3;
+		if (family_code == 0x02)
+			esp->rev = FAS236;
+		else if (family_code == 0x0a)
+			esp->rev = FASHME; /* Version is usually '5'. */
+		else
+			esp->rev = FAS100A;
+		esp->min_period = ((4 * esp->ccycle) / 1000);
+	} else {
+		esp->min_period = ((5 * esp->ccycle) / 1000);
+	}
+	esp->max_period = (esp->max_period + 3)>>2;
+	esp->min_period = (esp->min_period + 3)>>2;
+
+	esp_write8(esp->config1, ESP_CFG1);
+	switch (esp->rev) {
+	case ESP100:
+		/* nothing to do */
+		break;
+
+	case ESP100A:
+		esp_write8(esp->config2, ESP_CFG2);
+		break;
+
+	case ESP236:
+		/* Slow 236 */
+		esp_write8(esp->config2, ESP_CFG2);
+		esp->prev_cfg3 = esp->target[0].esp_config3;
+		esp_write8(esp->prev_cfg3, ESP_CFG3);
+		break;
+
+	case FASHME:
+		esp->config2 |= (ESP_CONFIG2_HME32 | ESP_CONFIG2_HMEFENAB);
+		/* fallthrough... */
+
+	case FAS236:
+		/* Fast 236 or HME */
+		esp_write8(esp->config2, ESP_CFG2);
+		if (esp->rev == FASHME) {
+			u8 cfg3 = esp->target[0].esp_config3;
+
+			cfg3 |= ESP_CONFIG3_FCLOCK | ESP_CONFIG3_OBPUSH;
+			if (esp->scsi_id >= 8)
+				cfg3 |= ESP_CONFIG3_IDBIT3;
+			esp_set_all_config3(esp, cfg3);
+		} else {
+			u32 cfg3 = esp->target[0].esp_config3;
+
+			cfg3 |= ESP_CONFIG3_FCLK;
+			esp_set_all_config3(esp, cfg3);
+		}
+		esp->prev_cfg3 = esp->target[0].esp_config3;
+		esp_write8(esp->prev_cfg3, ESP_CFG3);
+		if (esp->rev == FASHME) {
+			esp->radelay = 80;
+		} else {
+			if (esp->flags & ESP_FLAG_DIFFERENTIAL)
+				esp->radelay = 0;
+			else
+				esp->radelay = 96;
+		}
+		break;
+
+	case FAS100A:
+		/* Fast 100a */
+		esp_write8(esp->config2, ESP_CFG2);
+		esp_set_all_config3(esp,
+				    (esp->target[0].esp_config3 |
+				     ESP_CONFIG3_FCLOCK));
+		esp->prev_cfg3 = esp->target[0].esp_config3;
+		esp_write8(esp->prev_cfg3, ESP_CFG3);
+		esp->radelay = 32;
+		break;
+
+	default:
+		break;
+	}
+
+	/* Eat any bitrot in the chip */
+	esp_read8(ESP_INTRPT);
+	udelay(100);
+}
+
+static void esp_map_dma(struct esp *esp, struct scsi_cmnd *cmd)
+{
+	struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd);
+	struct scatterlist *sg = cmd->request_buffer;
+	int dir = cmd->sc_data_direction;
+	int total, i;
+
+	if (dir == DMA_NONE)
+		return;
+
+	BUG_ON(cmd->use_sg == 0);
+
+	spriv->u.num_sg = esp->ops->map_sg(esp, sg,
+					   cmd->use_sg, dir);
+	spriv->cur_residue = sg_dma_len(sg);
+	spriv->cur_sg = sg;
+
+	total = 0;
+	for (i = 0; i < spriv->u.num_sg; i++)
+		total += sg_dma_len(&sg[i]);
+	spriv->tot_residue = total;
+}
+
+static dma_addr_t esp_cur_dma_addr(struct esp_cmd_entry *ent,
+				   struct scsi_cmnd *cmd)
+{
+	struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd);
+
+	if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+		return ent->sense_dma +
+			(ent->sense_ptr - cmd->sense_buffer);
+	}
+
+	return sg_dma_address(p->cur_sg) +
+		(sg_dma_len(p->cur_sg) -
+		 p->cur_residue);
+}
+
+static unsigned int esp_cur_dma_len(struct esp_cmd_entry *ent,
+				    struct scsi_cmnd *cmd)
+{
+	struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd);
+
+	if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+		return SCSI_SENSE_BUFFERSIZE -
+			(ent->sense_ptr - cmd->sense_buffer);
+	}
+	return p->cur_residue;
+}
+
+static void esp_advance_dma(struct esp *esp, struct esp_cmd_entry *ent,
+			    struct scsi_cmnd *cmd, unsigned int len)
+{
+	struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd);
+
+	if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+		ent->sense_ptr += len;
+		return;
+	}
+
+	p->cur_residue -= len;
+	p->tot_residue -= len;
+	if (p->cur_residue < 0 || p->tot_residue < 0) {
+		printk(KERN_ERR PFX "esp%d: Data transfer overflow.\n",
+		       esp->host->unique_id);
+		printk(KERN_ERR PFX "esp%d: cur_residue[%d] tot_residue[%d] "
+		       "len[%u]\n",
+		       esp->host->unique_id,
+		       p->cur_residue, p->tot_residue, len);
+		p->cur_residue = 0;
+		p->tot_residue = 0;
+	}
+	if (!p->cur_residue && p->tot_residue) {
+		p->cur_sg++;
+		p->cur_residue = sg_dma_len(p->cur_sg);
+	}
+}
+
+static void esp_unmap_dma(struct esp *esp, struct scsi_cmnd *cmd)
+{
+	struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd);
+	int dir = cmd->sc_data_direction;
+
+	if (dir == DMA_NONE)
+		return;
+
+	esp->ops->unmap_sg(esp, cmd->request_buffer,
+			   spriv->u.num_sg, dir);
+}
+
+static void esp_save_pointers(struct esp *esp, struct esp_cmd_entry *ent)
+{
+	struct scsi_cmnd *cmd = ent->cmd;
+	struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd);
+
+	if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+		ent->saved_sense_ptr = ent->sense_ptr;
+		return;
+	}
+	ent->saved_cur_residue = spriv->cur_residue;
+	ent->saved_cur_sg = spriv->cur_sg;
+	ent->saved_tot_residue = spriv->tot_residue;
+}
+
+static void esp_restore_pointers(struct esp *esp, struct esp_cmd_entry *ent)
+{
+	struct scsi_cmnd *cmd = ent->cmd;
+	struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd);
+
+	if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+		ent->sense_ptr = ent->saved_sense_ptr;
+		return;
+	}
+	spriv->cur_residue = ent->saved_cur_residue;
+	spriv->cur_sg = ent->saved_cur_sg;
+	spriv->tot_residue = ent->saved_tot_residue;
+}
+
+static void esp_check_command_len(struct esp *esp, struct scsi_cmnd *cmd)
+{
+	if (cmd->cmd_len == 6 ||
+	    cmd->cmd_len == 10 ||
+	    cmd->cmd_len == 12) {
+		esp->flags &= ~ESP_FLAG_DOING_SLOWCMD;
+	} else {
+		esp->flags |= ESP_FLAG_DOING_SLOWCMD;
+	}
+}
+
+static void esp_write_tgt_config3(struct esp *esp, int tgt)
+{
+	if (esp->rev > ESP100A) {
+		u8 val = esp->target[tgt].esp_config3;
+
+		if (val != esp->prev_cfg3) {
+			esp->prev_cfg3 = val;
+			esp_write8(val, ESP_CFG3);
+		}
+	}
+}
+
+static void esp_write_tgt_sync(struct esp *esp, int tgt)
+{
+	u8 off = esp->target[tgt].esp_offset;
+	u8 per = esp->target[tgt].esp_period;
+
+	if (off != esp->prev_soff) {
+		esp->prev_soff = off;
+		esp_write8(off, ESP_SOFF);
+	}
+	if (per != esp->prev_stp) {
+		esp->prev_stp = per;
+		esp_write8(per, ESP_STP);
+	}
+}
+
+static u32 esp_dma_length_limit(struct esp *esp, u32 dma_addr, u32 dma_len)
+{
+	if (esp->rev == FASHME) {
+		/* Arbitrary segment boundaries, 24-bit counts.  */
+		if (dma_len > (1U << 24))
+			dma_len = (1U << 24);
+	} else {
+		u32 base, end;
+
+		/* ESP chip limits other variants by 16-bits of transfer
+		 * count.  Actually on FAS100A and FAS236 we could get
+		 * 24-bits of transfer count by enabling ESP_CONFIG2_FENAB
+		 * in the ESP_CFG2 register but that causes other unwanted
+		 * changes so we don't use it currently.
+		 */
+		if (dma_len > (1U << 16))
+			dma_len = (1U << 16);
+
+		/* All of the DMA variants hooked up to these chips
+		 * cannot handle crossing a 24-bit address boundary.
+		 */
+		base = dma_addr & ((1U << 24) - 1U);
+		end = base + dma_len;
+		if (end > (1U << 24))
+			end = (1U <<24);
+		dma_len = end - base;
+	}
+	return dma_len;
+}
+
+static int esp_need_to_nego_wide(struct esp_target_data *tp)
+{
+	struct scsi_target *target = tp->starget;
+
+	return spi_width(target) != tp->nego_goal_width;
+}
+
+static int esp_need_to_nego_sync(struct esp_target_data *tp)
+{
+	struct scsi_target *target = tp->starget;
+
+	/* When offset is zero, period is "don't care".  */
+	if (!spi_offset(target) && !tp->nego_goal_offset)
+		return 0;
+
+	if (spi_offset(target) == tp->nego_goal_offset &&
+	    spi_period(target) == tp->nego_goal_period)
+		return 0;
+
+	return 1;
+}
+
+static int esp_alloc_lun_tag(struct esp_cmd_entry *ent,
+			     struct esp_lun_data *lp)
+{
+	if (!ent->tag[0]) {
+		/* Non-tagged, slot already taken?  */
+		if (lp->non_tagged_cmd)
+			return -EBUSY;
+
+		if (lp->hold) {
+			/* We are being held by active tagged
+			 * commands.
+			 */
+			if (lp->num_tagged)
+				return -EBUSY;
+
+			/* Tagged commands completed, we can unplug
+			 * the queue and run this untagged command.
+			 */
+			lp->hold = 0;
+		} else if (lp->num_tagged) {
+			/* Plug the queue until num_tagged decreases
+			 * to zero in esp_free_lun_tag.
+			 */
+			lp->hold = 1;
+			return -EBUSY;
+		}
+
+		lp->non_tagged_cmd = ent;
+		return 0;
+	} else {
+		/* Tagged command, see if blocked by a
+		 * non-tagged one.
+		 */
+		if (lp->non_tagged_cmd || lp->hold)
+			return -EBUSY;
+	}
+
+	BUG_ON(lp->tagged_cmds[ent->tag[1]]);
+
+	lp->tagged_cmds[ent->tag[1]] = ent;
+	lp->num_tagged++;
+
+	return 0;
+}
+
+static void esp_free_lun_tag(struct esp_cmd_entry *ent,
+			     struct esp_lun_data *lp)
+{
+	if (ent->tag[0]) {
+		BUG_ON(lp->tagged_cmds[ent->tag[1]] != ent);
+		lp->tagged_cmds[ent->tag[1]] = NULL;
+		lp->num_tagged--;
+	} else {
+		BUG_ON(lp->non_tagged_cmd != ent);
+		lp->non_tagged_cmd = NULL;
+	}
+}
+
+/* When a contingent allegiance conditon is created, we force feed a
+ * REQUEST_SENSE command to the device to fetch the sense data.  I
+ * tried many other schemes, relying on the scsi error handling layer
+ * to send out the REQUEST_SENSE automatically, but this was difficult
+ * to get right especially in the presence of applications like smartd
+ * which use SG_IO to send out their own REQUEST_SENSE commands.
+ */
+static void esp_autosense(struct esp *esp, struct esp_cmd_entry *ent)
+{
+	struct scsi_cmnd *cmd = ent->cmd;
+	struct scsi_device *dev = cmd->device;
+	int tgt, lun;
+	u8 *p, val;
+
+	tgt = dev->id;
+	lun = dev->lun;
+
+
+	if (!ent->sense_ptr) {
+		esp_log_autosense("esp%d: Doing auto-sense for "
+				  "tgt[%d] lun[%d]\n",
+				  esp->host->unique_id, tgt, lun);
+
+		ent->sense_ptr = cmd->sense_buffer;
+		ent->sense_dma = esp->ops->map_single(esp,
+						      ent->sense_ptr,
+						      SCSI_SENSE_BUFFERSIZE,
+						      DMA_FROM_DEVICE);
+	}
+	ent->saved_sense_ptr = ent->sense_ptr;
+
+	esp->active_cmd = ent;
+
+	p = esp->command_block;
+	esp->msg_out_len = 0;
+
+	*p++ = IDENTIFY(0, lun);
+	*p++ = REQUEST_SENSE;
+	*p++ = ((dev->scsi_level <= SCSI_2) ?
+		(lun << 5) : 0);
+	*p++ = 0;
+	*p++ = 0;
+	*p++ = SCSI_SENSE_BUFFERSIZE;
+	*p++ = 0;
+
+	esp->select_state = ESP_SELECT_BASIC;
+
+	val = tgt;
+	if (esp->rev == FASHME)
+		val |= ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT;
+	esp_write8(val, ESP_BUSID);
+
+	esp_write_tgt_sync(esp, tgt);
+	esp_write_tgt_config3(esp, tgt);
+
+	val = (p - esp->command_block);
+
+	if (esp->rev == FASHME)
+		scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+	esp->ops->send_dma_cmd(esp, esp->command_block_dma,
+			       val, 16, 0, ESP_CMD_DMA | ESP_CMD_SELA);
+}
+
+static struct esp_cmd_entry *find_and_prep_issuable_command(struct esp *esp)
+{
+	struct esp_cmd_entry *ent;
+
+	list_for_each_entry(ent, &esp->queued_cmds, list) {
+		struct scsi_cmnd *cmd = ent->cmd;
+		struct scsi_device *dev = cmd->device;
+		struct esp_lun_data *lp = dev->hostdata;
+
+		if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+			ent->tag[0] = 0;
+			ent->tag[1] = 0;
+			return ent;
+		}
+
+		if (!scsi_populate_tag_msg(cmd, &ent->tag[0])) {
+			ent->tag[0] = 0;
+			ent->tag[1] = 0;
+		}
+
+		if (esp_alloc_lun_tag(ent, lp) < 0)
+			continue;
+
+		return ent;
+	}
+
+	return NULL;
+}
+
+static void esp_maybe_execute_command(struct esp *esp)
+{
+	struct esp_target_data *tp;
+	struct esp_lun_data *lp;
+	struct scsi_device *dev;
+	struct scsi_cmnd *cmd;
+	struct esp_cmd_entry *ent;
+	int tgt, lun, i;
+	u32 val, start_cmd;
+	u8 *p;
+
+	if (esp->active_cmd ||
+	    (esp->flags & ESP_FLAG_RESETTING))
+		return;
+
+	ent = find_and_prep_issuable_command(esp);
+	if (!ent)
+		return;
+
+	if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+		esp_autosense(esp, ent);
+		return;
+	}
+
+	cmd = ent->cmd;
+	dev = cmd->device;
+	tgt = dev->id;
+	lun = dev->lun;
+	tp = &esp->target[tgt];
+	lp = dev->hostdata;
+
+	list_del(&ent->list);
+	list_add(&ent->list, &esp->active_cmds);
+
+	esp->active_cmd = ent;
+
+	esp_map_dma(esp, cmd);
+	esp_save_pointers(esp, ent);
+
+	esp_check_command_len(esp, cmd);
+
+	p = esp->command_block;
+
+	esp->msg_out_len = 0;
+	if (tp->flags & ESP_TGT_CHECK_NEGO) {
+		/* Need to negotiate.  If the target is broken
+		 * go for synchronous transfers and non-wide.
+		 */
+		if (tp->flags & ESP_TGT_BROKEN) {
+			tp->flags &= ~ESP_TGT_DISCONNECT;
+			tp->nego_goal_period = 0;
+			tp->nego_goal_offset = 0;
+			tp->nego_goal_width = 0;
+			tp->nego_goal_tags = 0;
+		}
+
+		/* If the settings are not changing, skip this.  */
+		if (spi_width(tp->starget) == tp->nego_goal_width &&
+		    spi_period(tp->starget) == tp->nego_goal_period &&
+		    spi_offset(tp->starget) == tp->nego_goal_offset) {
+			tp->flags &= ~ESP_TGT_CHECK_NEGO;
+			goto build_identify;
+		}
+
+		if (esp->rev == FASHME && esp_need_to_nego_wide(tp)) {
+			esp->msg_out_len =
+				spi_populate_width_msg(&esp->msg_out[0],
+						       (tp->nego_goal_width ?
+							1 : 0));
+			tp->flags |= ESP_TGT_NEGO_WIDE;
+		} else if (esp_need_to_nego_sync(tp)) {
+			esp->msg_out_len =
+				spi_populate_sync_msg(&esp->msg_out[0],
+						      tp->nego_goal_period,
+						      tp->nego_goal_offset);
+			tp->flags |= ESP_TGT_NEGO_SYNC;
+		} else {
+			tp->flags &= ~ESP_TGT_CHECK_NEGO;
+		}
+
+		/* Process it like a slow command.  */
+		if (tp->flags & (ESP_TGT_NEGO_WIDE | ESP_TGT_NEGO_SYNC))
+			esp->flags |= ESP_FLAG_DOING_SLOWCMD;
+	}
+
+build_identify:
+	/* If we don't have a lun-data struct yet, we're probing
+	 * so do not disconnect.  Also, do not disconnect unless
+	 * we have a tag on this command.
+	 */
+	if (lp && (tp->flags & ESP_TGT_DISCONNECT) && ent->tag[0])
+		*p++ = IDENTIFY(1, lun);
+	else
+		*p++ = IDENTIFY(0, lun);
+
+	if (ent->tag[0] && esp->rev == ESP100) {
+		/* ESP100 lacks select w/atn3 command, use select
+		 * and stop instead.
+		 */
+		esp->flags |= ESP_FLAG_DOING_SLOWCMD;
+	}
+
+	if (!(esp->flags & ESP_FLAG_DOING_SLOWCMD)) {
+		start_cmd = ESP_CMD_DMA | ESP_CMD_SELA;
+		if (ent->tag[0]) {
+			*p++ = ent->tag[0];
+			*p++ = ent->tag[1];
+
+			start_cmd = ESP_CMD_DMA | ESP_CMD_SA3;
+		}
+
+		for (i = 0; i < cmd->cmd_len; i++)
+			*p++ = cmd->cmnd[i];
+
+		esp->select_state = ESP_SELECT_BASIC;
+	} else {
+		esp->cmd_bytes_left = cmd->cmd_len;
+		esp->cmd_bytes_ptr = &cmd->cmnd[0];
+
+		if (ent->tag[0]) {
+			for (i = esp->msg_out_len - 1;
+			     i >= 0; i--)
+				esp->msg_out[i + 2] = esp->msg_out[i];
+			esp->msg_out[0] = ent->tag[0];
+			esp->msg_out[1] = ent->tag[1];
+			esp->msg_out_len += 2;
+		}
+
+		start_cmd = ESP_CMD_DMA | ESP_CMD_SELAS;
+		esp->select_state = ESP_SELECT_MSGOUT;
+	}
+	val = tgt;
+	if (esp->rev == FASHME)
+		val |= ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT;
+	esp_write8(val, ESP_BUSID);
+
+	esp_write_tgt_sync(esp, tgt);
+	esp_write_tgt_config3(esp, tgt);
+
+	val = (p - esp->command_block);
+
+	if (esp_debug & ESP_DEBUG_SCSICMD) {
+		printk("ESP: tgt[%d] lun[%d] scsi_cmd [ ", tgt, lun);
+		for (i = 0; i < cmd->cmd_len; i++)
+			printk("%02x ", cmd->cmnd[i]);
+		printk("]\n");
+	}
+
+	if (esp->rev == FASHME)
+		scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+	esp->ops->send_dma_cmd(esp, esp->command_block_dma,
+			       val, 16, 0, start_cmd);
+}
+
+static struct esp_cmd_entry *esp_get_ent(struct esp *esp)
+{
+	struct list_head *head = &esp->esp_cmd_pool;
+	struct esp_cmd_entry *ret;
+
+	if (list_empty(head)) {
+		ret = kzalloc(sizeof(struct esp_cmd_entry), GFP_ATOMIC);
+	} else {
+		ret = list_entry(head->next, struct esp_cmd_entry, list);
+		list_del(&ret->list);
+		memset(ret, 0, sizeof(*ret));
+	}
+	return ret;
+}
+
+static void esp_put_ent(struct esp *esp, struct esp_cmd_entry *ent)
+{
+	list_add(&ent->list, &esp->esp_cmd_pool);
+}
+
+static void esp_cmd_is_done(struct esp *esp, struct esp_cmd_entry *ent,
+			    struct scsi_cmnd *cmd, unsigned int result)
+{
+	struct scsi_device *dev = cmd->device;
+	int tgt = dev->id;
+	int lun = dev->lun;
+
+	esp->active_cmd = NULL;
+	esp_unmap_dma(esp, cmd);
+	esp_free_lun_tag(ent, dev->hostdata);
+	cmd->result = result;
+
+	if (ent->eh_done) {
+		complete(ent->eh_done);
+		ent->eh_done = NULL;
+	}
+
+	if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+		esp->ops->unmap_single(esp, ent->sense_dma,
+				       SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+		ent->sense_ptr = NULL;
+
+		/* Restore the message/status bytes to what we actually
+		 * saw originally.  Also, report that we are providing
+		 * the sense data.
+		 */
+		cmd->result = ((DRIVER_SENSE << 24) |
+			       (DID_OK << 16) |
+			       (COMMAND_COMPLETE << 8) |
+			       (SAM_STAT_CHECK_CONDITION << 0));
+
+		ent->flags &= ~ESP_CMD_FLAG_AUTOSENSE;
+		if (esp_debug & ESP_DEBUG_AUTOSENSE) {
+			int i;
+
+			printk("esp%d: tgt[%d] lun[%d] AUTO SENSE[ ",
+			       esp->host->unique_id, tgt, lun);
+			for (i = 0; i < 18; i++)
+				printk("%02x ", cmd->sense_buffer[i]);
+			printk("]\n");
+		}
+	}
+
+	cmd->scsi_done(cmd);
+
+	list_del(&ent->list);
+	esp_put_ent(esp, ent);
+
+	esp_maybe_execute_command(esp);
+}
+
+static unsigned int compose_result(unsigned int status, unsigned int message,
+				   unsigned int driver_code)
+{
+	return (status | (message << 8) | (driver_code << 16));
+}
+
+static void esp_event_queue_full(struct esp *esp, struct esp_cmd_entry *ent)
+{
+	struct scsi_device *dev = ent->cmd->device;
+	struct esp_lun_data *lp = dev->hostdata;
+
+	scsi_track_queue_full(dev, lp->num_tagged - 1);
+}
+
+static int esp_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+{
+	struct scsi_device *dev = cmd->device;
+	struct esp *esp = host_to_esp(dev->host);
+	struct esp_cmd_priv *spriv;
+	struct esp_cmd_entry *ent;
+
+	ent = esp_get_ent(esp);
+	if (!ent)
+		return SCSI_MLQUEUE_HOST_BUSY;
+
+	ent->cmd = cmd;
+
+	cmd->scsi_done = done;
+
+	spriv = ESP_CMD_PRIV(cmd);
+	spriv->u.dma_addr = ~(dma_addr_t)0x0;
+
+	list_add_tail(&ent->list, &esp->queued_cmds);
+
+	esp_maybe_execute_command(esp);
+
+	return 0;
+}
+
+static int esp_check_gross_error(struct esp *esp)
+{
+	if (esp->sreg & ESP_STAT_SPAM) {
+		/* Gross Error, could be one of:
+		 * - top of fifo overwritten
+		 * - top of command register overwritten
+		 * - DMA programmed with wrong direction
+		 * - improper phase change
+		 */
+		printk(KERN_ERR PFX "esp%d: Gross error sreg[%02x]\n",
+		       esp->host->unique_id, esp->sreg);
+		/* XXX Reset the chip. XXX */
+		return 1;
+	}
+	return 0;
+}
+
+static int esp_check_spur_intr(struct esp *esp)
+{
+	switch (esp->rev) {
+	case ESP100:
+	case ESP100A:
+		/* The interrupt pending bit of the status register cannot
+		 * be trusted on these revisions.
+		 */
+		esp->sreg &= ~ESP_STAT_INTR;
+		break;
+
+	default:
+		if (!(esp->sreg & ESP_STAT_INTR)) {
+			esp->ireg = esp_read8(ESP_INTRPT);
+			if (esp->ireg & ESP_INTR_SR)
+				return 1;
+
+			/* If the DMA is indicating interrupt pending and the
+			 * ESP is not, the only possibility is a DMA error.
+			 */
+			if (!esp->ops->dma_error(esp)) {
+				printk(KERN_ERR PFX "esp%d: Spurious irq, "
+				       "sreg=%x.\n",
+				       esp->host->unique_id, esp->sreg);
+				return -1;
+			}
+
+			printk(KERN_ERR PFX "esp%d: DMA error\n",
+			       esp->host->unique_id);
+
+			/* XXX Reset the chip. XXX */
+			return -1;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+static void esp_schedule_reset(struct esp *esp)
+{
+	esp_log_reset("ESP: esp_schedule_reset() from %p\n",
+		      __builtin_return_address(0));
+	esp->flags |= ESP_FLAG_RESETTING;
+	esp_event(esp, ESP_EVENT_RESET);
+}
+
+/* In order to avoid having to add a special half-reconnected state
+ * into the driver we just sit here and poll through the rest of
+ * the reselection process to get the tag message bytes.
+ */
+static struct esp_cmd_entry *esp_reconnect_with_tag(struct esp *esp,
+						    struct esp_lun_data *lp)
+{
+	struct esp_cmd_entry *ent;
+	int i;
+
+	if (!lp->num_tagged) {
+		printk(KERN_ERR PFX "esp%d: Reconnect w/num_tagged==0\n",
+		       esp->host->unique_id);
+		return NULL;
+	}
+
+	esp_log_reconnect("ESP: reconnect tag, ");
+
+	for (i = 0; i < ESP_QUICKIRQ_LIMIT; i++) {
+		if (esp->ops->irq_pending(esp))
+			break;
+	}
+	if (i == ESP_QUICKIRQ_LIMIT) {
+		printk(KERN_ERR PFX "esp%d: Reconnect IRQ1 timeout\n",
+		       esp->host->unique_id);
+		return NULL;
+	}
+
+	esp->sreg = esp_read8(ESP_STATUS);
+	esp->ireg = esp_read8(ESP_INTRPT);
+
+	esp_log_reconnect("IRQ(%d:%x:%x), ",
+			  i, esp->ireg, esp->sreg);
+
+	if (esp->ireg & ESP_INTR_DC) {
+		printk(KERN_ERR PFX "esp%d: Reconnect, got disconnect.\n",
+		       esp->host->unique_id);
+		return NULL;
+	}
+
+	if ((esp->sreg & ESP_STAT_PMASK) != ESP_MIP) {
+		printk(KERN_ERR PFX "esp%d: Reconnect, not MIP sreg[%02x].\n",
+		       esp->host->unique_id, esp->sreg);
+		return NULL;
+	}
+
+	/* DMA in the tag bytes... */
+	esp->command_block[0] = 0xff;
+	esp->command_block[1] = 0xff;
+	esp->ops->send_dma_cmd(esp, esp->command_block_dma,
+			       2, 2, 1, ESP_CMD_DMA | ESP_CMD_TI);
+
+	/* ACK the msssage.  */
+	scsi_esp_cmd(esp, ESP_CMD_MOK);
+
+	for (i = 0; i < ESP_RESELECT_TAG_LIMIT; i++) {
+		if (esp->ops->irq_pending(esp)) {
+			esp->sreg = esp_read8(ESP_STATUS);
+			esp->ireg = esp_read8(ESP_INTRPT);
+			if (esp->ireg & ESP_INTR_FDONE)
+				break;
+		}
+		udelay(1);
+	}
+	if (i == ESP_RESELECT_TAG_LIMIT) {
+		printk(KERN_ERR PFX "esp%d: Reconnect IRQ2 timeout\n",
+		       esp->host->unique_id);
+		return NULL;
+	}
+	esp->ops->dma_drain(esp);
+	esp->ops->dma_invalidate(esp);
+
+	esp_log_reconnect("IRQ2(%d:%x:%x) tag[%x:%x]\n",
+			  i, esp->ireg, esp->sreg,
+			  esp->command_block[0],
+			  esp->command_block[1]);
+
+	if (esp->command_block[0] < SIMPLE_QUEUE_TAG ||
+	    esp->command_block[0] > ORDERED_QUEUE_TAG) {
+		printk(KERN_ERR PFX "esp%d: Reconnect, bad tag "
+		       "type %02x.\n",
+		       esp->host->unique_id, esp->command_block[0]);
+		return NULL;
+	}
+
+	ent = lp->tagged_cmds[esp->command_block[1]];
+	if (!ent) {
+		printk(KERN_ERR PFX "esp%d: Reconnect, no entry for "
+		       "tag %02x.\n",
+		       esp->host->unique_id, esp->command_block[1]);
+		return NULL;
+	}
+
+	return ent;
+}
+
+static int esp_reconnect(struct esp *esp)
+{
+	struct esp_cmd_entry *ent;
+	struct esp_target_data *tp;
+	struct esp_lun_data *lp;
+	struct scsi_device *dev;
+	int target, lun;
+
+	BUG_ON(esp->active_cmd);
+	if (esp->rev == FASHME) {
+		/* FASHME puts the target and lun numbers directly
+		 * into the fifo.
+		 */
+		target = esp->fifo[0];
+		lun = esp->fifo[1] & 0x7;
+	} else {
+		u8 bits = esp_read8(ESP_FDATA);
+
+		/* Older chips put the lun directly into the fifo, but
+		 * the target is given as a sample of the arbitration
+		 * lines on the bus at reselection time.  So we should
+		 * see the ID of the ESP and the one reconnecting target
+		 * set in the bitmap.
+		 */
+		if (!(bits & esp->scsi_id_mask))
+			goto do_reset;
+		bits &= ~esp->scsi_id_mask;
+		if (!bits || (bits & (bits - 1)))
+			goto do_reset;
+
+		target = ffs(bits) - 1;
+		lun = (esp_read8(ESP_FDATA) & 0x7);
+
+		scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+		if (esp->rev == ESP100) {
+			u8 ireg = esp_read8(ESP_INTRPT);
+			/* This chip has a bug during reselection that can
+			 * cause a spurious illegal-command interrupt, which
+			 * we simply ACK here.  Another possibility is a bus
+			 * reset so we must check for that.
+			 */
+			if (ireg & ESP_INTR_SR)
+				goto do_reset;
+		}
+		scsi_esp_cmd(esp, ESP_CMD_NULL);
+	}
+
+	esp_write_tgt_sync(esp, target);
+	esp_write_tgt_config3(esp, target);
+
+	scsi_esp_cmd(esp, ESP_CMD_MOK);
+
+	if (esp->rev == FASHME)
+		esp_write8(target | ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT,
+			   ESP_BUSID);
+
+	tp = &esp->target[target];
+	dev = __scsi_device_lookup_by_target(tp->starget, lun);
+	if (!dev) {
+		printk(KERN_ERR PFX "esp%d: Reconnect, no lp "
+		       "tgt[%u] lun[%u]\n",
+		       esp->host->unique_id, target, lun);
+		goto do_reset;
+	}
+	lp = dev->hostdata;
+
+	ent = lp->non_tagged_cmd;
+	if (!ent) {
+		ent = esp_reconnect_with_tag(esp, lp);
+		if (!ent)
+			goto do_reset;
+	}
+
+	esp->active_cmd = ent;
+
+	if (ent->flags & ESP_CMD_FLAG_ABORT) {
+		esp->msg_out[0] = ABORT_TASK_SET;
+		esp->msg_out_len = 1;
+		scsi_esp_cmd(esp, ESP_CMD_SATN);
+	}
+
+	esp_event(esp, ESP_EVENT_CHECK_PHASE);
+	esp_restore_pointers(esp, ent);
+	esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
+	return 1;
+
+do_reset:
+	esp_schedule_reset(esp);
+	return 0;
+}
+
+static int esp_finish_select(struct esp *esp)
+{
+	struct esp_cmd_entry *ent;
+	struct scsi_cmnd *cmd;
+	u8 orig_select_state;
+
+	orig_select_state = esp->select_state;
+
+	/* No longer selecting.  */
+	esp->select_state = ESP_SELECT_NONE;
+
+	esp->seqreg = esp_read8(ESP_SSTEP) & ESP_STEP_VBITS;
+	ent = esp->active_cmd;
+	cmd = ent->cmd;
+
+	if (esp->ops->dma_error(esp)) {
+		/* If we see a DMA error during or as a result of selection,
+		 * all bets are off.
+		 */
+		esp_schedule_reset(esp);
+		esp_cmd_is_done(esp, ent, cmd, (DID_ERROR << 16));
+		return 0;
+	}
+
+	esp->ops->dma_invalidate(esp);
+
+	if (esp->ireg == (ESP_INTR_RSEL | ESP_INTR_FDONE)) {
+		struct esp_target_data *tp = &esp->target[cmd->device->id];
+
+		/* Carefully back out of the selection attempt.  Release
+		 * resources (such as DMA mapping & TAG) and reset state (such
+		 * as message out and command delivery variables).
+		 */
+		if (!(ent->flags & ESP_CMD_FLAG_AUTOSENSE)) {
+			esp_unmap_dma(esp, cmd);
+			esp_free_lun_tag(ent, cmd->device->hostdata);
+			tp->flags &= ~(ESP_TGT_NEGO_SYNC | ESP_TGT_NEGO_WIDE);
+			esp->flags &= ~ESP_FLAG_DOING_SLOWCMD;
+			esp->cmd_bytes_ptr = NULL;
+			esp->cmd_bytes_left = 0;
+		} else {
+			esp->ops->unmap_single(esp, ent->sense_dma,
+					       SCSI_SENSE_BUFFERSIZE,
+					       DMA_FROM_DEVICE);
+			ent->sense_ptr = NULL;
+		}
+
+		/* Now that the state is unwound properly, put back onto
+		 * the issue queue.  This command is no longer active.
+		 */
+		list_del(&ent->list);
+		list_add(&ent->list, &esp->queued_cmds);
+		esp->active_cmd = NULL;
+
+		/* Return value ignored by caller, it directly invokes
+		 * esp_reconnect().
+		 */
+		return 0;
+	}
+
+	if (esp->ireg == ESP_INTR_DC) {
+		struct scsi_device *dev = cmd->device;
+
+		/* Disconnect.  Make sure we re-negotiate sync and
+		 * wide parameters if this target starts responding
+		 * again in the future.
+		 */
+		esp->target[dev->id].flags |= ESP_TGT_CHECK_NEGO;
+
+		scsi_esp_cmd(esp, ESP_CMD_ESEL);
+		esp_cmd_is_done(esp, ent, cmd, (DID_BAD_TARGET << 16));
+		return 1;
+	}
+
+	if (esp->ireg == (ESP_INTR_FDONE | ESP_INTR_BSERV)) {
+		/* Selection successful.  On pre-FAST chips we have
+		 * to do a NOP and possibly clean out the FIFO.
+		 */
+		if (esp->rev <= ESP236) {
+			int fcnt = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES;
+
+			scsi_esp_cmd(esp, ESP_CMD_NULL);
+
+			if (!fcnt &&
+			    (!esp->prev_soff ||
+			     ((esp->sreg & ESP_STAT_PMASK) != ESP_DIP)))
+				esp_flush_fifo(esp);
+		}
+
+		/* If we are doing a slow command, negotiation, etc.
+		 * we'll do the right thing as we transition to the
+		 * next phase.
+		 */
+		esp_event(esp, ESP_EVENT_CHECK_PHASE);
+		return 0;
+	}
+
+	printk("ESP: Unexpected selection completion ireg[%x].\n",
+	       esp->ireg);
+	esp_schedule_reset(esp);
+	return 0;
+}
+
+static int esp_data_bytes_sent(struct esp *esp, struct esp_cmd_entry *ent,
+			       struct scsi_cmnd *cmd)
+{
+	int fifo_cnt, ecount, bytes_sent, flush_fifo;
+
+	fifo_cnt = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES;
+	if (esp->prev_cfg3 & ESP_CONFIG3_EWIDE)
+		fifo_cnt <<= 1;
+
+	ecount = 0;
+	if (!(esp->sreg & ESP_STAT_TCNT)) {
+		ecount = ((unsigned int)esp_read8(ESP_TCLOW) |
+			  (((unsigned int)esp_read8(ESP_TCMED)) << 8));
+		if (esp->rev == FASHME)
+			ecount |= ((unsigned int)esp_read8(FAS_RLO)) << 16;
+	}
+
+	bytes_sent = esp->data_dma_len;
+	bytes_sent -= ecount;
+
+	if (!(ent->flags & ESP_CMD_FLAG_WRITE))
+		bytes_sent -= fifo_cnt;
+
+	flush_fifo = 0;
+	if (!esp->prev_soff) {
+		/* Synchronous data transfer, always flush fifo. */
+		flush_fifo = 1;
+	} else {
+		if (esp->rev == ESP100) {
+			u32 fflags, phase;
+
+			/* ESP100 has a chip bug where in the synchronous data
+			 * phase it can mistake a final long REQ pulse from the
+			 * target as an extra data byte.  Fun.
+			 *
+			 * To detect this case we resample the status register
+			 * and fifo flags.  If we're still in a data phase and
+			 * we see spurious chunks in the fifo, we return error
+			 * to the caller which should reset and set things up
+			 * such that we only try future transfers to this
+			 * target in synchronous mode.
+			 */
+			esp->sreg = esp_read8(ESP_STATUS);
+			phase = esp->sreg & ESP_STAT_PMASK;
+			fflags = esp_read8(ESP_FFLAGS);
+
+			if ((phase == ESP_DOP &&
+			     (fflags & ESP_FF_ONOTZERO)) ||
+			    (phase == ESP_DIP &&
+			     (fflags & ESP_FF_FBYTES)))
+				return -1;
+		}
+		if (!(ent->flags & ESP_CMD_FLAG_WRITE))
+			flush_fifo = 1;
+	}
+
+	if (flush_fifo)
+		esp_flush_fifo(esp);
+
+	return bytes_sent;
+}
+
+static void esp_setsync(struct esp *esp, struct esp_target_data *tp,
+			u8 scsi_period, u8 scsi_offset,
+			u8 esp_stp, u8 esp_soff)
+{
+	spi_period(tp->starget) = scsi_period;
+	spi_offset(tp->starget) = scsi_offset;
+	spi_width(tp->starget) = (tp->flags & ESP_TGT_WIDE) ? 1 : 0;
+
+	if (esp_soff) {
+		esp_stp &= 0x1f;
+		esp_soff |= esp->radelay;
+		if (esp->rev >= FAS236) {
+			u8 bit = ESP_CONFIG3_FSCSI;
+			if (esp->rev >= FAS100A)
+				bit = ESP_CONFIG3_FAST;
+
+			if (scsi_period < 50) {
+				if (esp->rev == FASHME)
+					esp_soff &= ~esp->radelay;
+				tp->esp_config3 |= bit;
+			} else {
+				tp->esp_config3 &= ~bit;
+			}
+			esp->prev_cfg3 = tp->esp_config3;
+			esp_write8(esp->prev_cfg3, ESP_CFG3);
+		}
+	}
+
+	tp->esp_period = esp->prev_stp = esp_stp;
+	tp->esp_offset = esp->prev_soff = esp_soff;
+
+	esp_write8(esp_soff, ESP_SOFF);
+	esp_write8(esp_stp, ESP_STP);
+
+	tp->flags &= ~(ESP_TGT_NEGO_SYNC | ESP_TGT_CHECK_NEGO);
+
+	spi_display_xfer_agreement(tp->starget);
+}
+
+static void esp_msgin_reject(struct esp *esp)
+{
+	struct esp_cmd_entry *ent = esp->active_cmd;
+	struct scsi_cmnd *cmd = ent->cmd;
+	struct esp_target_data *tp;
+	int tgt;
+
+	tgt = cmd->device->id;
+	tp = &esp->target[tgt];
+
+	if (tp->flags & ESP_TGT_NEGO_WIDE) {
+		tp->flags &= ~(ESP_TGT_NEGO_WIDE | ESP_TGT_WIDE);
+
+		if (!esp_need_to_nego_sync(tp)) {
+			tp->flags &= ~ESP_TGT_CHECK_NEGO;
+			scsi_esp_cmd(esp, ESP_CMD_RATN);
+		} else {
+			esp->msg_out_len =
+				spi_populate_sync_msg(&esp->msg_out[0],
+						      tp->nego_goal_period,
+						      tp->nego_goal_offset);
+			tp->flags |= ESP_TGT_NEGO_SYNC;
+			scsi_esp_cmd(esp, ESP_CMD_SATN);
+		}
+		return;
+	}
+
+	if (tp->flags & ESP_TGT_NEGO_SYNC) {
+		tp->flags &= ~(ESP_TGT_NEGO_SYNC | ESP_TGT_CHECK_NEGO);
+		tp->esp_period = 0;
+		tp->esp_offset = 0;
+		esp_setsync(esp, tp, 0, 0, 0, 0);
+		scsi_esp_cmd(esp, ESP_CMD_RATN);
+		return;
+	}
+
+	esp->msg_out[0] = ABORT_TASK_SET;
+	esp->msg_out_len = 1;
+	scsi_esp_cmd(esp, ESP_CMD_SATN);
+}
+
+static void esp_msgin_sdtr(struct esp *esp, struct esp_target_data *tp)
+{
+	u8 period = esp->msg_in[3];
+	u8 offset = esp->msg_in[4];
+	u8 stp;
+
+	if (!(tp->flags & ESP_TGT_NEGO_SYNC))
+		goto do_reject;
+
+	if (offset > 15)
+		goto do_reject;
+
+	if (offset) {
+		int rounded_up, one_clock;
+
+		if (period > esp->max_period) {
+			period = offset = 0;
+			goto do_sdtr;
+		}
+		if (period < esp->min_period)
+			goto do_reject;
+
+		one_clock = esp->ccycle / 1000;
+		rounded_up = (period << 2);
+		rounded_up = (rounded_up + one_clock - 1) / one_clock;
+		stp = rounded_up;
+		if (stp && esp->rev >= FAS236) {
+			if (stp >= 50)
+				stp--;
+		}
+	} else {
+		stp = 0;
+	}
+
+	esp_setsync(esp, tp, period, offset, stp, offset);
+	return;
+
+do_reject:
+	esp->msg_out[0] = MESSAGE_REJECT;
+	esp->msg_out_len = 1;
+	scsi_esp_cmd(esp, ESP_CMD_SATN);
+	return;
+
+do_sdtr:
+	tp->nego_goal_period = period;
+	tp->nego_goal_offset = offset;
+	esp->msg_out_len =
+		spi_populate_sync_msg(&esp->msg_out[0],
+				      tp->nego_goal_period,
+				      tp->nego_goal_offset);
+	scsi_esp_cmd(esp, ESP_CMD_SATN);
+}
+
+static void esp_msgin_wdtr(struct esp *esp, struct esp_target_data *tp)
+{
+	int size = 8 << esp->msg_in[3];
+	u8 cfg3;
+
+	if (esp->rev != FASHME)
+		goto do_reject;
+
+	if (size != 8 && size != 16)
+		goto do_reject;
+
+	if (!(tp->flags & ESP_TGT_NEGO_WIDE))
+		goto do_reject;
+
+	cfg3 = tp->esp_config3;
+	if (size == 16) {
+		tp->flags |= ESP_TGT_WIDE;
+		cfg3 |= ESP_CONFIG3_EWIDE;
+	} else {
+		tp->flags &= ~ESP_TGT_WIDE;
+		cfg3 &= ~ESP_CONFIG3_EWIDE;
+	}
+	tp->esp_config3 = cfg3;
+	esp->prev_cfg3 = cfg3;
+	esp_write8(cfg3, ESP_CFG3);
+
+	tp->flags &= ~ESP_TGT_NEGO_WIDE;
+
+	spi_period(tp->starget) = 0;
+	spi_offset(tp->starget) = 0;
+	if (!esp_need_to_nego_sync(tp)) {
+		tp->flags &= ~ESP_TGT_CHECK_NEGO;
+		scsi_esp_cmd(esp, ESP_CMD_RATN);
+	} else {
+		esp->msg_out_len =
+			spi_populate_sync_msg(&esp->msg_out[0],
+					      tp->nego_goal_period,
+					      tp->nego_goal_offset);
+		tp->flags |= ESP_TGT_NEGO_SYNC;
+		scsi_esp_cmd(esp, ESP_CMD_SATN);
+	}
+	return;
+
+do_reject:
+	esp->msg_out[0] = MESSAGE_REJECT;
+	esp->msg_out_len = 1;
+	scsi_esp_cmd(esp, ESP_CMD_SATN);
+}
+
+static void esp_msgin_extended(struct esp *esp)
+{
+	struct esp_cmd_entry *ent = esp->active_cmd;
+	struct scsi_cmnd *cmd = ent->cmd;
+	struct esp_target_data *tp;
+	int tgt = cmd->device->id;
+
+	tp = &esp->target[tgt];
+	if (esp->msg_in[2] == EXTENDED_SDTR) {
+		esp_msgin_sdtr(esp, tp);
+		return;
+	}
+	if (esp->msg_in[2] == EXTENDED_WDTR) {
+		esp_msgin_wdtr(esp, tp);
+		return;
+	}
+
+	printk("ESP: Unexpected extended msg type %x\n",
+	       esp->msg_in[2]);
+
+	esp->msg_out[0] = ABORT_TASK_SET;
+	esp->msg_out_len = 1;
+	scsi_esp_cmd(esp, ESP_CMD_SATN);
+}
+
+/* Analyze msgin bytes received from target so far.  Return non-zero
+ * if there are more bytes needed to complete the message.
+ */
+static int esp_msgin_process(struct esp *esp)
+{
+	u8 msg0 = esp->msg_in[0];
+	int len = esp->msg_in_len;
+
+	if (msg0 & 0x80) {
+		/* Identify */
+		printk("ESP: Unexpected msgin identify\n");
+		return 0;
+	}
+
+	switch (msg0) {
+	case EXTENDED_MESSAGE:
+		if (len == 1)
+			return 1;
+		if (len < esp->msg_in[1] + 2)
+			return 1;
+		esp_msgin_extended(esp);
+		return 0;
+
+	case IGNORE_WIDE_RESIDUE: {
+		struct esp_cmd_entry *ent;
+		struct esp_cmd_priv *spriv;
+		if (len == 1)
+			return 1;
+
+		if (esp->msg_in[1] != 1)
+			goto do_reject;
+
+		ent = esp->active_cmd;
+		spriv = ESP_CMD_PRIV(ent->cmd);
+
+		if (spriv->cur_residue == sg_dma_len(spriv->cur_sg)) {
+			spriv->cur_sg--;
+			spriv->cur_residue = 1;
+		} else
+			spriv->cur_residue++;
+		spriv->tot_residue++;
+		return 0;
+	}
+	case NOP:
+		return 0;
+	case RESTORE_POINTERS:
+		esp_restore_pointers(esp, esp->active_cmd);
+		return 0;
+	case SAVE_POINTERS:
+		esp_save_pointers(esp, esp->active_cmd);
+		return 0;
+
+	case COMMAND_COMPLETE:
+	case DISCONNECT: {
+		struct esp_cmd_entry *ent = esp->active_cmd;
+
+		ent->message = msg0;
+		esp_event(esp, ESP_EVENT_FREE_BUS);
+		esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
+		return 0;
+	}
+	case MESSAGE_REJECT:
+		esp_msgin_reject(esp);
+		return 0;
+
+	default:
+	do_reject:
+		esp->msg_out[0] = MESSAGE_REJECT;
+		esp->msg_out_len = 1;
+		scsi_esp_cmd(esp, ESP_CMD_SATN);
+		return 0;
+	}
+}
+
+static int esp_process_event(struct esp *esp)
+{
+	int write;
+
+again:
+	write = 0;
+	switch (esp->event) {
+	case ESP_EVENT_CHECK_PHASE:
+		switch (esp->sreg & ESP_STAT_PMASK) {
+		case ESP_DOP:
+			esp_event(esp, ESP_EVENT_DATA_OUT);
+			break;
+		case ESP_DIP:
+			esp_event(esp, ESP_EVENT_DATA_IN);
+			break;
+		case ESP_STATP:
+			esp_flush_fifo(esp);
+			scsi_esp_cmd(esp, ESP_CMD_ICCSEQ);
+			esp_event(esp, ESP_EVENT_STATUS);
+			esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
+			return 1;
+
+		case ESP_MOP:
+			esp_event(esp, ESP_EVENT_MSGOUT);
+			break;
+
+		case ESP_MIP:
+			esp_event(esp, ESP_EVENT_MSGIN);
+			break;
+
+		case ESP_CMDP:
+			esp_event(esp, ESP_EVENT_CMD_START);
+			break;
+
+		default:
+			printk("ESP: Unexpected phase, sreg=%02x\n",
+			       esp->sreg);
+			esp_schedule_reset(esp);
+			return 0;
+		}
+		goto again;
+		break;
+
+	case ESP_EVENT_DATA_IN:
+		write = 1;
+		/* fallthru */
+
+	case ESP_EVENT_DATA_OUT: {
+		struct esp_cmd_entry *ent = esp->active_cmd;
+		struct scsi_cmnd *cmd = ent->cmd;
+		dma_addr_t dma_addr = esp_cur_dma_addr(ent, cmd);
+		unsigned int dma_len = esp_cur_dma_len(ent, cmd);
+
+		if (esp->rev == ESP100)
+			scsi_esp_cmd(esp, ESP_CMD_NULL);
+
+		if (write)
+			ent->flags |= ESP_CMD_FLAG_WRITE;
+		else
+			ent->flags &= ~ESP_CMD_FLAG_WRITE;
+
+		dma_len = esp_dma_length_limit(esp, dma_addr, dma_len);
+		esp->data_dma_len = dma_len;
+
+		if (!dma_len) {
+			printk(KERN_ERR PFX "esp%d: DMA length is zero!\n",
+			       esp->host->unique_id);
+			printk(KERN_ERR PFX "esp%d: cur adr[%08llx] len[%08x]\n",
+			       esp->host->unique_id,
+			       (unsigned long long)esp_cur_dma_addr(ent, cmd),
+			       esp_cur_dma_len(ent, cmd));
+			esp_schedule_reset(esp);
+			return 0;
+		}
+
+		esp_log_datastart("ESP: start data addr[%08llx] len[%u] "
+				  "write(%d)\n",
+				  (unsigned long long)dma_addr, dma_len, write);
+
+		esp->ops->send_dma_cmd(esp, dma_addr, dma_len, dma_len,
+				       write, ESP_CMD_DMA | ESP_CMD_TI);
+		esp_event(esp, ESP_EVENT_DATA_DONE);
+		break;
+	}
+	case ESP_EVENT_DATA_DONE: {
+		struct esp_cmd_entry *ent = esp->active_cmd;
+		struct scsi_cmnd *cmd = ent->cmd;
+		int bytes_sent;
+
+		if (esp->ops->dma_error(esp)) {
+			printk("ESP: data done, DMA error, resetting\n");
+			esp_schedule_reset(esp);
+			return 0;
+		}
+
+		if (ent->flags & ESP_CMD_FLAG_WRITE) {
+			/* XXX parity errors, etc. XXX */
+
+			esp->ops->dma_drain(esp);
+		}
+		esp->ops->dma_invalidate(esp);
+
+		if (esp->ireg != ESP_INTR_BSERV) {
+			/* We should always see exactly a bus-service
+			 * interrupt at the end of a successful transfer.
+			 */
+			printk("ESP: data done, not BSERV, resetting\n");
+			esp_schedule_reset(esp);
+			return 0;
+		}
+
+		bytes_sent = esp_data_bytes_sent(esp, ent, cmd);
+
+		esp_log_datadone("ESP: data done flgs[%x] sent[%d]\n",
+				 ent->flags, bytes_sent);
+
+		if (bytes_sent < 0) {
+			/* XXX force sync mode for this target XXX */
+			esp_schedule_reset(esp);
+			return 0;
+		}
+
+		esp_advance_dma(esp, ent, cmd, bytes_sent);
+		esp_event(esp, ESP_EVENT_CHECK_PHASE);
+		goto again;
+		break;
+	}
+
+	case ESP_EVENT_STATUS: {
+		struct esp_cmd_entry *ent = esp->active_cmd;
+
+		if (esp->ireg & ESP_INTR_FDONE) {
+			ent->status = esp_read8(ESP_FDATA);
+			ent->message = esp_read8(ESP_FDATA);
+			scsi_esp_cmd(esp, ESP_CMD_MOK);
+		} else if (esp->ireg == ESP_INTR_BSERV) {
+			ent->status = esp_read8(ESP_FDATA);
+			ent->message = 0xff;
+			esp_event(esp, ESP_EVENT_MSGIN);
+			return 0;
+		}
+
+		if (ent->message != COMMAND_COMPLETE) {
+			printk("ESP: Unexpected message %x in status\n",
+			       ent->message);
+			esp_schedule_reset(esp);
+			return 0;
+		}
+
+		esp_event(esp, ESP_EVENT_FREE_BUS);
+		esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
+		break;
+	}
+	case ESP_EVENT_FREE_BUS: {
+		struct esp_cmd_entry *ent = esp->active_cmd;
+		struct scsi_cmnd *cmd = ent->cmd;
+
+		if (ent->message == COMMAND_COMPLETE ||
+		    ent->message == DISCONNECT)
+			scsi_esp_cmd(esp, ESP_CMD_ESEL);
+
+		if (ent->message == COMMAND_COMPLETE) {
+			esp_log_cmddone("ESP: Command done status[%x] "
+					"message[%x]\n",
+					ent->status, ent->message);
+			if (ent->status == SAM_STAT_TASK_SET_FULL)
+				esp_event_queue_full(esp, ent);
+
+			if (ent->status == SAM_STAT_CHECK_CONDITION &&
+			    !(ent->flags & ESP_CMD_FLAG_AUTOSENSE)) {
+				ent->flags |= ESP_CMD_FLAG_AUTOSENSE;
+				esp_autosense(esp, ent);
+			} else {
+				esp_cmd_is_done(esp, ent, cmd,
+						compose_result(ent->status,
+							       ent->message,
+							       DID_OK));
+			}
+		} else if (ent->message == DISCONNECT) {
+			esp_log_disconnect("ESP: Disconnecting tgt[%d] "
+					   "tag[%x:%x]\n",
+					   cmd->device->id,
+					   ent->tag[0], ent->tag[1]);
+
+			esp->active_cmd = NULL;
+			esp_maybe_execute_command(esp);
+		} else {
+			printk("ESP: Unexpected message %x in freebus\n",
+			       ent->message);
+			esp_schedule_reset(esp);
+			return 0;
+		}
+		if (esp->active_cmd)
+			esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
+		break;
+	}
+	case ESP_EVENT_MSGOUT: {
+		scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+
+		if (esp_debug & ESP_DEBUG_MSGOUT) {
+			int i;
+			printk("ESP: Sending message [ ");
+			for (i = 0; i < esp->msg_out_len; i++)
+				printk("%02x ", esp->msg_out[i]);
+			printk("]\n");
+		}
+
+		if (esp->rev == FASHME) {
+			int i;
+
+			/* Always use the fifo.  */
+			for (i = 0; i < esp->msg_out_len; i++) {
+				esp_write8(esp->msg_out[i], ESP_FDATA);
+				esp_write8(0, ESP_FDATA);
+			}
+			scsi_esp_cmd(esp, ESP_CMD_TI);
+		} else {
+			if (esp->msg_out_len == 1) {
+				esp_write8(esp->msg_out[0], ESP_FDATA);
+				scsi_esp_cmd(esp, ESP_CMD_TI);
+			} else {
+				/* Use DMA. */
+				memcpy(esp->command_block,
+				       esp->msg_out,
+				       esp->msg_out_len);
+
+				esp->ops->send_dma_cmd(esp,
+						       esp->command_block_dma,
+						       esp->msg_out_len,
+						       esp->msg_out_len,
+						       0,
+						       ESP_CMD_DMA|ESP_CMD_TI);
+			}
+		}
+		esp_event(esp, ESP_EVENT_MSGOUT_DONE);
+		break;
+	}
+	case ESP_EVENT_MSGOUT_DONE:
+		if (esp->rev == FASHME) {
+			scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+		} else {
+			if (esp->msg_out_len > 1)
+				esp->ops->dma_invalidate(esp);
+		}
+
+		if (!(esp->ireg & ESP_INTR_DC)) {
+			if (esp->rev != FASHME)
+				scsi_esp_cmd(esp, ESP_CMD_NULL);
+		}
+		esp_event(esp, ESP_EVENT_CHECK_PHASE);
+		goto again;
+	case ESP_EVENT_MSGIN:
+		if (esp->ireg & ESP_INTR_BSERV) {
+			if (esp->rev == FASHME) {
+				if (!(esp_read8(ESP_STATUS2) &
+				      ESP_STAT2_FEMPTY))
+					scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+			} else {
+				scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+				if (esp->rev == ESP100)
+					scsi_esp_cmd(esp, ESP_CMD_NULL);
+			}
+			scsi_esp_cmd(esp, ESP_CMD_TI);
+			esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
+			return 1;
+		}
+		if (esp->ireg & ESP_INTR_FDONE) {
+			u8 val;
+
+			if (esp->rev == FASHME)
+				val = esp->fifo[0];
+			else
+				val = esp_read8(ESP_FDATA);
+			esp->msg_in[esp->msg_in_len++] = val;
+
+			esp_log_msgin("ESP: Got msgin byte %x\n", val);
+
+			if (!esp_msgin_process(esp))
+				esp->msg_in_len = 0;
+
+			if (esp->rev == FASHME)
+				scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+
+			scsi_esp_cmd(esp, ESP_CMD_MOK);
+
+			if (esp->event != ESP_EVENT_FREE_BUS)
+				esp_event(esp, ESP_EVENT_CHECK_PHASE);
+		} else {
+			printk("ESP: MSGIN neither BSERV not FDON, resetting");
+			esp_schedule_reset(esp);
+			return 0;
+		}
+		break;
+	case ESP_EVENT_CMD_START:
+		memcpy(esp->command_block, esp->cmd_bytes_ptr,
+		       esp->cmd_bytes_left);
+		if (esp->rev == FASHME)
+			scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+		esp->ops->send_dma_cmd(esp, esp->command_block_dma,
+				       esp->cmd_bytes_left, 16, 0,
+				       ESP_CMD_DMA | ESP_CMD_TI);
+		esp_event(esp, ESP_EVENT_CMD_DONE);
+		esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
+		break;
+	case ESP_EVENT_CMD_DONE:
+		esp->ops->dma_invalidate(esp);
+		if (esp->ireg & ESP_INTR_BSERV) {
+			esp_event(esp, ESP_EVENT_CHECK_PHASE);
+			goto again;
+		}
+		esp_schedule_reset(esp);
+		return 0;
+		break;
+
+	case ESP_EVENT_RESET:
+		scsi_esp_cmd(esp, ESP_CMD_RS);
+		break;
+
+	default:
+		printk("ESP: Unexpected event %x, resetting\n",
+		       esp->event);
+		esp_schedule_reset(esp);
+		return 0;
+		break;
+	}
+	return 1;
+}
+
+static void esp_reset_cleanup_one(struct esp *esp, struct esp_cmd_entry *ent)
+{
+	struct scsi_cmnd *cmd = ent->cmd;
+
+	esp_unmap_dma(esp, cmd);
+	esp_free_lun_tag(ent, cmd->device->hostdata);
+	cmd->result = DID_RESET << 16;
+
+	if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+		esp->ops->unmap_single(esp, ent->sense_dma,
+				       SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+		ent->sense_ptr = NULL;
+	}
+
+	cmd->scsi_done(cmd);
+	list_del(&ent->list);
+	esp_put_ent(esp, ent);
+}
+
+static void esp_clear_hold(struct scsi_device *dev, void *data)
+{
+	struct esp_lun_data *lp = dev->hostdata;
+
+	BUG_ON(lp->num_tagged);
+	lp->hold = 0;
+}
+
+static void esp_reset_cleanup(struct esp *esp)
+{
+	struct esp_cmd_entry *ent, *tmp;
+	int i;
+
+	list_for_each_entry_safe(ent, tmp, &esp->queued_cmds, list) {
+		struct scsi_cmnd *cmd = ent->cmd;
+
+		list_del(&ent->list);
+		cmd->result = DID_RESET << 16;
+		cmd->scsi_done(cmd);
+		esp_put_ent(esp, ent);
+	}
+
+	list_for_each_entry_safe(ent, tmp, &esp->active_cmds, list) {
+		if (ent == esp->active_cmd)
+			esp->active_cmd = NULL;
+		esp_reset_cleanup_one(esp, ent);
+	}
+
+	BUG_ON(esp->active_cmd != NULL);
+
+	/* Force renegotiation of sync/wide transfers.  */
+	for (i = 0; i < ESP_MAX_TARGET; i++) {
+		struct esp_target_data *tp = &esp->target[i];
+
+		tp->esp_period = 0;
+		tp->esp_offset = 0;
+		tp->esp_config3 &= ~(ESP_CONFIG3_EWIDE |
+				     ESP_CONFIG3_FSCSI |
+				     ESP_CONFIG3_FAST);
+		tp->flags &= ~ESP_TGT_WIDE;
+		tp->flags |= ESP_TGT_CHECK_NEGO;
+
+		if (tp->starget)
+			starget_for_each_device(tp->starget, NULL,
+						esp_clear_hold);
+	}
+}
+
+/* Runs under host->lock */
+static void __esp_interrupt(struct esp *esp)
+{
+	int finish_reset, intr_done;
+	u8 phase;
+
+	esp->sreg = esp_read8(ESP_STATUS);
+
+	if (esp->flags & ESP_FLAG_RESETTING) {
+		finish_reset = 1;
+	} else {
+		if (esp_check_gross_error(esp))
+			return;
+
+		finish_reset = esp_check_spur_intr(esp);
+		if (finish_reset < 0)
+			return;
+	}
+
+	esp->ireg = esp_read8(ESP_INTRPT);
+
+	if (esp->ireg & ESP_INTR_SR)
+		finish_reset = 1;
+
+	if (finish_reset) {
+		esp_reset_cleanup(esp);
+		if (esp->eh_reset) {
+			complete(esp->eh_reset);
+			esp->eh_reset = NULL;
+		}
+		return;
+	}
+
+	phase = (esp->sreg & ESP_STAT_PMASK);
+	if (esp->rev == FASHME) {
+		if (((phase != ESP_DIP && phase != ESP_DOP) &&
+		     esp->select_state == ESP_SELECT_NONE &&
+		     esp->event != ESP_EVENT_STATUS &&
+		     esp->event != ESP_EVENT_DATA_DONE) ||
+		    (esp->ireg & ESP_INTR_RSEL)) {
+			esp->sreg2 = esp_read8(ESP_STATUS2);
+			if (!(esp->sreg2 & ESP_STAT2_FEMPTY) ||
+			    (esp->sreg2 & ESP_STAT2_F1BYTE))
+				hme_read_fifo(esp);
+		}
+	}
+
+	esp_log_intr("ESP: intr sreg[%02x] seqreg[%02x] "
+		     "sreg2[%02x] ireg[%02x]\n",
+		     esp->sreg, esp->seqreg, esp->sreg2, esp->ireg);
+
+	intr_done = 0;
+
+	if (esp->ireg & (ESP_INTR_S | ESP_INTR_SATN | ESP_INTR_IC)) {
+		printk("ESP: unexpected IREG %02x\n", esp->ireg);
+		if (esp->ireg & ESP_INTR_IC)
+			esp_dump_cmd_log(esp);
+
+		esp_schedule_reset(esp);
+	} else {
+		if (!(esp->ireg & ESP_INTR_RSEL)) {
+			/* Some combination of FDONE, BSERV, DC.  */
+			if (esp->select_state != ESP_SELECT_NONE)
+				intr_done = esp_finish_select(esp);
+		} else if (esp->ireg & ESP_INTR_RSEL) {
+			if (esp->active_cmd)
+				(void) esp_finish_select(esp);
+			intr_done = esp_reconnect(esp);
+		}
+	}
+	while (!intr_done)
+		intr_done = esp_process_event(esp);
+}
+
+irqreturn_t scsi_esp_intr(int irq, void *dev_id)
+{
+	struct esp *esp = dev_id;
+	unsigned long flags;
+	irqreturn_t ret;
+
+	spin_lock_irqsave(esp->host->host_lock, flags);
+	ret = IRQ_NONE;
+	if (esp->ops->irq_pending(esp)) {
+		ret = IRQ_HANDLED;
+		for (;;) {
+			int i;
+
+			__esp_interrupt(esp);
+			if (!(esp->flags & ESP_FLAG_QUICKIRQ_CHECK))
+				break;
+			esp->flags &= ~ESP_FLAG_QUICKIRQ_CHECK;
+
+			for (i = 0; i < ESP_QUICKIRQ_LIMIT; i++) {
+				if (esp->ops->irq_pending(esp))
+					break;
+			}
+			if (i == ESP_QUICKIRQ_LIMIT)
+				break;
+		}
+	}
+	spin_unlock_irqrestore(esp->host->host_lock, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL(scsi_esp_intr);
+
+static void __devinit esp_get_revision(struct esp *esp)
+{
+	u8 val;
+
+	esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7));
+	esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY);
+	esp_write8(esp->config2, ESP_CFG2);
+
+	val = esp_read8(ESP_CFG2);
+	val &= ~ESP_CONFIG2_MAGIC;
+	if (val != (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) {
+		/* If what we write to cfg2 does not come back, cfg2 is not
+		 * implemented, therefore this must be a plain esp100.
+		 */
+		esp->rev = ESP100;
+	} else {
+		esp->config2 = 0;
+		esp_set_all_config3(esp, 5);
+		esp->prev_cfg3 = 5;
+		esp_write8(esp->config2, ESP_CFG2);
+		esp_write8(0, ESP_CFG3);
+		esp_write8(esp->prev_cfg3, ESP_CFG3);
+
+		val = esp_read8(ESP_CFG3);
+		if (val != 5) {
+			/* The cfg2 register is implemented, however
+			 * cfg3 is not, must be esp100a.
+			 */
+			esp->rev = ESP100A;
+		} else {
+			esp_set_all_config3(esp, 0);
+			esp->prev_cfg3 = 0;
+			esp_write8(esp->prev_cfg3, ESP_CFG3);
+
+			/* All of cfg{1,2,3} implemented, must be one of
+			 * the fas variants, figure out which one.
+			 */
+			if (esp->cfact == 0 || esp->cfact > ESP_CCF_F5) {
+				esp->rev = FAST;
+				esp->sync_defp = SYNC_DEFP_FAST;
+			} else {
+				esp->rev = ESP236;
+			}
+			esp->config2 = 0;
+			esp_write8(esp->config2, ESP_CFG2);
+		}
+	}
+}
+
+static void __devinit esp_init_swstate(struct esp *esp)
+{
+	int i;
+
+	INIT_LIST_HEAD(&esp->queued_cmds);
+	INIT_LIST_HEAD(&esp->active_cmds);
+	INIT_LIST_HEAD(&esp->esp_cmd_pool);
+
+	/* Start with a clear state, domain validation (via ->slave_configure,
+	 * spi_dv_device()) will attempt to enable SYNC, WIDE, and tagged
+	 * commands.
+	 */
+	for (i = 0 ; i < ESP_MAX_TARGET; i++) {
+		esp->target[i].flags = 0;
+		esp->target[i].nego_goal_period = 0;
+		esp->target[i].nego_goal_offset = 0;
+		esp->target[i].nego_goal_width = 0;
+		esp->target[i].nego_goal_tags = 0;
+	}
+}
+
+/* This places the ESP into a known state at boot time. */
+static void __devinit esp_bootup_reset(struct esp *esp)
+{
+	u8 val;
+
+	/* Reset the DMA */
+	esp->ops->reset_dma(esp);
+
+	/* Reset the ESP */
+	esp_reset_esp(esp);
+
+	/* Reset the SCSI bus, but tell ESP not to generate an irq */
+	val = esp_read8(ESP_CFG1);
+	val |= ESP_CONFIG1_SRRDISAB;
+	esp_write8(val, ESP_CFG1);
+
+	scsi_esp_cmd(esp, ESP_CMD_RS);
+	udelay(400);
+
+	esp_write8(esp->config1, ESP_CFG1);
+
+	/* Eat any bitrot in the chip and we are done... */
+	esp_read8(ESP_INTRPT);
+}
+
+static void __devinit esp_set_clock_params(struct esp *esp)
+{
+	int fmhz;
+	u8 ccf;
+
+	/* This is getting messy but it has to be done correctly or else
+	 * you get weird behavior all over the place.  We are trying to
+	 * basically figure out three pieces of information.
+	 *
+	 * a) Clock Conversion Factor
+	 *
+	 *    This is a representation of the input crystal clock frequency
+	 *    going into the ESP on this machine.  Any operation whose timing
+	 *    is longer than 400ns depends on this value being correct.  For
+	 *    example, you'll get blips for arbitration/selection during high
+	 *    load or with multiple targets if this is not set correctly.
+	 *
+	 * b) Selection Time-Out
+	 *
+	 *    The ESP isn't very bright and will arbitrate for the bus and try
+	 *    to select a target forever if you let it.  This value tells the
+	 *    ESP when it has taken too long to negotiate and that it should
+	 *    interrupt the CPU so we can see what happened.  The value is
+	 *    computed as follows (from NCR/Symbios chip docs).
+	 *
+	 *          (Time Out Period) *  (Input Clock)
+	 *    STO = ----------------------------------
+	 *          (8192) * (Clock Conversion Factor)
+	 *
+	 *    We use a time out period of 250ms (ESP_BUS_TIMEOUT).
+	 *
+	 * c) Imperical constants for synchronous offset and transfer period
+         *    register values
+	 *
+	 *    This entails the smallest and largest sync period we could ever
+	 *    handle on this ESP.
+	 */
+	fmhz = esp->cfreq;
+
+	ccf = ((fmhz / 1000000) + 4) / 5;
+	if (ccf == 1)
+		ccf = 2;
+
+	/* If we can't find anything reasonable, just assume 20MHZ.
+	 * This is the clock frequency of the older sun4c's where I've
+	 * been unable to find the clock-frequency PROM property.  All
+	 * other machines provide useful values it seems.
+	 */
+	if (fmhz <= 5000000 || ccf < 1 || ccf > 8) {
+		fmhz = 20000000;
+		ccf = 4;
+	}
+
+	esp->cfact = (ccf == 8 ? 0 : ccf);
+	esp->cfreq = fmhz;
+	esp->ccycle = ESP_MHZ_TO_CYCLE(fmhz);
+	esp->ctick = ESP_TICK(ccf, esp->ccycle);
+	esp->neg_defp = ESP_NEG_DEFP(fmhz, ccf);
+	esp->sync_defp = SYNC_DEFP_SLOW;
+}
+
+static const char *esp_chip_names[] = {
+	"ESP100",
+	"ESP100A",
+	"ESP236",
+	"FAS236",
+	"FAS100A",
+	"FAST",
+	"FASHME",
+};
+
+static struct scsi_transport_template *esp_transport_template;
+
+int __devinit scsi_esp_register(struct esp *esp, struct device *dev)
+{
+	static int instance;
+	int err;
+
+	esp->host->transportt = esp_transport_template;
+	esp->host->max_lun = ESP_MAX_LUN;
+	esp->host->cmd_per_lun = 2;
+
+	esp_set_clock_params(esp);
+
+	esp_get_revision(esp);
+
+	esp_init_swstate(esp);
+
+	esp_bootup_reset(esp);
+
+	printk(KERN_INFO PFX "esp%u, regs[%1p:%1p] irq[%u]\n",
+	       esp->host->unique_id, esp->regs, esp->dma_regs,
+	       esp->host->irq);
+	printk(KERN_INFO PFX "esp%u is a %s, %u MHz (ccf=%u), SCSI ID %u\n",
+	       esp->host->unique_id, esp_chip_names[esp->rev],
+	       esp->cfreq / 1000000, esp->cfact, esp->scsi_id);
+
+	/* Let the SCSI bus reset settle. */
+	ssleep(esp_bus_reset_settle);
+
+	err = scsi_add_host(esp->host, dev);
+	if (err)
+		return err;
+
+	esp->host->unique_id = instance++;
+
+	scsi_scan_host(esp->host);
+
+	return 0;
+}
+EXPORT_SYMBOL(scsi_esp_register);
+
+void __devexit scsi_esp_unregister(struct esp *esp)
+{
+	scsi_remove_host(esp->host);
+}
+EXPORT_SYMBOL(scsi_esp_unregister);
+
+static int esp_slave_alloc(struct scsi_device *dev)
+{
+	struct esp *esp = host_to_esp(dev->host);
+	struct esp_target_data *tp = &esp->target[dev->id];
+	struct esp_lun_data *lp;
+
+	lp = kzalloc(sizeof(*lp), GFP_KERNEL);
+	if (!lp)
+		return -ENOMEM;
+	dev->hostdata = lp;
+
+	tp->starget = dev->sdev_target;
+
+	spi_min_period(tp->starget) = esp->min_period;
+	spi_max_offset(tp->starget) = 15;
+
+	if (esp->flags & ESP_FLAG_WIDE_CAPABLE)
+		spi_max_width(tp->starget) = 1;
+	else
+		spi_max_width(tp->starget) = 0;
+
+	return 0;
+}
+
+static int esp_slave_configure(struct scsi_device *dev)
+{
+	struct esp *esp = host_to_esp(dev->host);
+	struct esp_target_data *tp = &esp->target[dev->id];
+	int goal_tags, queue_depth;
+
+	goal_tags = 0;
+
+	if (dev->tagged_supported) {
+		/* XXX make this configurable somehow XXX */
+		goal_tags = ESP_DEFAULT_TAGS;
+
+		if (goal_tags > ESP_MAX_TAG)
+			goal_tags = ESP_MAX_TAG;
+	}
+
+	queue_depth = goal_tags;
+	if (queue_depth < dev->host->cmd_per_lun)
+		queue_depth = dev->host->cmd_per_lun;
+
+	if (goal_tags) {
+		scsi_set_tag_type(dev, MSG_ORDERED_TAG);
+		scsi_activate_tcq(dev, queue_depth);
+	} else {
+		scsi_deactivate_tcq(dev, queue_depth);
+	}
+	tp->flags |= ESP_TGT_DISCONNECT;
+
+	if (!spi_initial_dv(dev->sdev_target))
+		spi_dv_device(dev);
+
+	return 0;
+}
+
+static void esp_slave_destroy(struct scsi_device *dev)
+{
+	struct esp_lun_data *lp = dev->hostdata;
+
+	kfree(lp);
+	dev->hostdata = NULL;
+}
+
+static int esp_eh_abort_handler(struct scsi_cmnd *cmd)
+{
+	struct esp *esp = host_to_esp(cmd->device->host);
+	struct esp_cmd_entry *ent, *tmp;
+	struct completion eh_done;
+	unsigned long flags;
+
+	/* XXX This helps a lot with debugging but might be a bit
+	 * XXX much for the final driver.
+	 */
+	spin_lock_irqsave(esp->host->host_lock, flags);
+	printk(KERN_ERR PFX "esp%d: Aborting command [%p:%02x]\n",
+	       esp->host->unique_id, cmd, cmd->cmnd[0]);
+	ent = esp->active_cmd;
+	if (ent)
+		printk(KERN_ERR PFX "esp%d: Current command [%p:%02x]\n",
+		       esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]);
+	list_for_each_entry(ent, &esp->queued_cmds, list) {
+		printk(KERN_ERR PFX "esp%d: Queued command [%p:%02x]\n",
+		       esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]);
+	}
+	list_for_each_entry(ent, &esp->active_cmds, list) {
+		printk(KERN_ERR PFX "esp%d: Active command [%p:%02x]\n",
+		       esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]);
+	}
+	esp_dump_cmd_log(esp);
+	spin_unlock_irqrestore(esp->host->host_lock, flags);
+
+	spin_lock_irqsave(esp->host->host_lock, flags);
+
+	ent = NULL;
+	list_for_each_entry(tmp, &esp->queued_cmds, list) {
+		if (tmp->cmd == cmd) {
+			ent = tmp;
+			break;
+		}
+	}
+
+	if (ent) {
+		/* Easiest case, we didn't even issue the command
+		 * yet so it is trivial to abort.
+		 */
+		list_del(&ent->list);
+
+		cmd->result = DID_ABORT << 16;
+		cmd->scsi_done(cmd);
+
+		esp_put_ent(esp, ent);
+
+		goto out_success;
+	}
+
+	init_completion(&eh_done);
+
+	ent = esp->active_cmd;
+	if (ent && ent->cmd == cmd) {
+		/* Command is the currently active command on
+		 * the bus.  If we already have an output message
+		 * pending, no dice.
+		 */
+		if (esp->msg_out_len)
+			goto out_failure;
+
+		/* Send out an abort, encouraging the target to
+		 * go to MSGOUT phase by asserting ATN.
+		 */
+		esp->msg_out[0] = ABORT_TASK_SET;
+		esp->msg_out_len = 1;
+		ent->eh_done = &eh_done;
+
+		scsi_esp_cmd(esp, ESP_CMD_SATN);
+	} else {
+		/* The command is disconnected.  This is not easy to
+		 * abort.  For now we fail and let the scsi error
+		 * handling layer go try a scsi bus reset or host
+		 * reset.
+		 *
+		 * What we could do is put together a scsi command
+		 * solely for the purpose of sending an abort message
+		 * to the target.  Coming up with all the code to
+		 * cook up scsi commands, special case them everywhere,
+		 * etc. is for questionable gain and it would be better
+		 * if the generic scsi error handling layer could do at
+		 * least some of that for us.
+		 *
+		 * Anyways this is an area for potential future improvement
+		 * in this driver.
+		 */
+		goto out_failure;
+	}
+
+	spin_unlock_irqrestore(esp->host->host_lock, flags);
+
+	if (!wait_for_completion_timeout(&eh_done, 5 * HZ)) {
+		spin_lock_irqsave(esp->host->host_lock, flags);
+		ent->eh_done = NULL;
+		spin_unlock_irqrestore(esp->host->host_lock, flags);
+
+		return FAILED;
+	}
+
+	return SUCCESS;
+
+out_success:
+	spin_unlock_irqrestore(esp->host->host_lock, flags);
+	return SUCCESS;
+
+out_failure:
+	/* XXX This might be a good location to set ESP_TGT_BROKEN
+	 * XXX since we know which target/lun in particular is
+	 * XXX causing trouble.
+	 */
+	spin_unlock_irqrestore(esp->host->host_lock, flags);
+	return FAILED;
+}
+
+static int esp_eh_bus_reset_handler(struct scsi_cmnd *cmd)
+{
+	struct esp *esp = host_to_esp(cmd->device->host);
+	struct completion eh_reset;
+	unsigned long flags;
+
+	init_completion(&eh_reset);
+
+	spin_lock_irqsave(esp->host->host_lock, flags);
+
+	esp->eh_reset = &eh_reset;
+
+	/* XXX This is too simple... We should add lots of
+	 * XXX checks here so that if we find that the chip is
+	 * XXX very wedged we return failure immediately so
+	 * XXX that we can perform a full chip reset.
+	 */
+	esp->flags |= ESP_FLAG_RESETTING;
+	scsi_esp_cmd(esp, ESP_CMD_RS);
+
+	spin_unlock_irqrestore(esp->host->host_lock, flags);
+
+	ssleep(esp_bus_reset_settle);
+
+	if (!wait_for_completion_timeout(&eh_reset, 5 * HZ)) {
+		spin_lock_irqsave(esp->host->host_lock, flags);
+		esp->eh_reset = NULL;
+		spin_unlock_irqrestore(esp->host->host_lock, flags);
+
+		return FAILED;
+	}
+
+	return SUCCESS;
+}
+
+/* All bets are off, reset the entire device.  */
+static int esp_eh_host_reset_handler(struct scsi_cmnd *cmd)
+{
+	struct esp *esp = host_to_esp(cmd->device->host);
+	unsigned long flags;
+
+	spin_lock_irqsave(esp->host->host_lock, flags);
+	esp_bootup_reset(esp);
+	esp_reset_cleanup(esp);
+	spin_unlock_irqrestore(esp->host->host_lock, flags);
+
+	ssleep(esp_bus_reset_settle);
+
+	return SUCCESS;
+}
+
+static const char *esp_info(struct Scsi_Host *host)
+{
+	return "esp";
+}
+
+struct scsi_host_template scsi_esp_template = {
+	.module			= THIS_MODULE,
+	.name			= "esp",
+	.info			= esp_info,
+	.queuecommand		= esp_queuecommand,
+	.slave_alloc		= esp_slave_alloc,
+	.slave_configure	= esp_slave_configure,
+	.slave_destroy		= esp_slave_destroy,
+	.eh_abort_handler	= esp_eh_abort_handler,
+	.eh_bus_reset_handler	= esp_eh_bus_reset_handler,
+	.eh_host_reset_handler	= esp_eh_host_reset_handler,
+	.can_queue		= 7,
+	.this_id		= 7,
+	.sg_tablesize		= SG_ALL,
+	.use_clustering		= ENABLE_CLUSTERING,
+	.max_sectors		= 0xffff,
+	.skip_settle_delay	= 1,
+};
+EXPORT_SYMBOL(scsi_esp_template);
+
+static void esp_get_signalling(struct Scsi_Host *host)
+{
+	struct esp *esp = host_to_esp(host);
+	enum spi_signal_type type;
+
+	if (esp->flags & ESP_FLAG_DIFFERENTIAL)
+		type = SPI_SIGNAL_HVD;
+	else
+		type = SPI_SIGNAL_SE;
+
+	spi_signalling(host) = type;
+}
+
+static void esp_set_offset(struct scsi_target *target, int offset)
+{
+	struct Scsi_Host *host = dev_to_shost(target->dev.parent);
+	struct esp *esp = host_to_esp(host);
+	struct esp_target_data *tp = &esp->target[target->id];
+
+	tp->nego_goal_offset = offset;
+	tp->flags |= ESP_TGT_CHECK_NEGO;
+}
+
+static void esp_set_period(struct scsi_target *target, int period)
+{
+	struct Scsi_Host *host = dev_to_shost(target->dev.parent);
+	struct esp *esp = host_to_esp(host);
+	struct esp_target_data *tp = &esp->target[target->id];
+
+	tp->nego_goal_period = period;
+	tp->flags |= ESP_TGT_CHECK_NEGO;
+}
+
+static void esp_set_width(struct scsi_target *target, int width)
+{
+	struct Scsi_Host *host = dev_to_shost(target->dev.parent);
+	struct esp *esp = host_to_esp(host);
+	struct esp_target_data *tp = &esp->target[target->id];
+
+	tp->nego_goal_width = (width ? 1 : 0);
+	tp->flags |= ESP_TGT_CHECK_NEGO;
+}
+
+static struct spi_function_template esp_transport_ops = {
+	.set_offset		= esp_set_offset,
+	.show_offset		= 1,
+	.set_period		= esp_set_period,
+	.show_period		= 1,
+	.set_width		= esp_set_width,
+	.show_width		= 1,
+	.get_signalling		= esp_get_signalling,
+};
+
+static int __init esp_init(void)
+{
+	BUILD_BUG_ON(sizeof(struct scsi_pointer) <
+		     sizeof(struct esp_cmd_priv));
+
+	esp_transport_template = spi_attach_transport(&esp_transport_ops);
+	if (!esp_transport_template)
+		return -ENODEV;
+
+	return 0;
+}
+
+static void __exit esp_exit(void)
+{
+	spi_release_transport(esp_transport_template);
+}
+
+MODULE_DESCRIPTION("ESP SCSI driver core");
+MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+module_param(esp_bus_reset_settle, int, 0);
+MODULE_PARM_DESC(esp_bus_reset_settle,
+		 "ESP scsi bus reset delay in seconds");
+
+module_param(esp_debug, int, 0);
+MODULE_PARM_DESC(esp_debug,
+"ESP bitmapped debugging message enable value:\n"
+"	0x00000001	Log interrupt events\n"
+"	0x00000002	Log scsi commands\n"
+"	0x00000004	Log resets\n"
+"	0x00000008	Log message in events\n"
+"	0x00000010	Log message out events\n"
+"	0x00000020	Log command completion\n"
+"	0x00000040	Log disconnects\n"
+"	0x00000080	Log data start\n"
+"	0x00000100	Log data done\n"
+"	0x00000200	Log reconnects\n"
+"	0x00000400	Log auto-sense data\n"
+);
+
+module_init(esp_init);
+module_exit(esp_exit);
diff --git a/drivers/scsi/esp_scsi.h b/drivers/scsi/esp_scsi.h
new file mode 100644
index 0000000..8d4a669
--- /dev/null
+++ b/drivers/scsi/esp_scsi.h
@@ -0,0 +1,560 @@
+/* esp_scsi.h: Defines and structures for the ESP drier.
+ *
+ * Copyright (C) 2007 David S. Miller (davem@davemloft.net)
+ */
+
+#ifndef _ESP_SCSI_H
+#define _ESP_SCSI_H
+
+					/* Access    Description      Offset */
+#define ESP_TCLOW	0x00UL		/* rw  Low bits transfer count 0x00  */
+#define ESP_TCMED	0x01UL		/* rw  Mid bits transfer count 0x04  */
+#define ESP_FDATA	0x02UL		/* rw  FIFO data bits          0x08  */
+#define ESP_CMD		0x03UL		/* rw  SCSI command bits       0x0c  */
+#define ESP_STATUS	0x04UL		/* ro  ESP status register     0x10  */
+#define ESP_BUSID	ESP_STATUS	/* wo  BusID for sel/resel     0x10  */
+#define ESP_INTRPT	0x05UL		/* ro  Kind of interrupt       0x14  */
+#define ESP_TIMEO	ESP_INTRPT	/* wo  Timeout for sel/resel   0x14  */
+#define ESP_SSTEP	0x06UL		/* ro  Sequence step register  0x18  */
+#define ESP_STP		ESP_SSTEP	/* wo  Transfer period/sync    0x18  */
+#define ESP_FFLAGS	0x07UL		/* ro  Bits current FIFO info  0x1c  */
+#define ESP_SOFF	ESP_FFLAGS	/* wo  Sync offset             0x1c  */
+#define ESP_CFG1	0x08UL		/* rw  First cfg register      0x20  */
+#define ESP_CFACT	0x09UL		/* wo  Clock conv factor       0x24  */
+#define ESP_STATUS2	ESP_CFACT	/* ro  HME status2 register    0x24  */
+#define ESP_CTEST	0x0aUL		/* wo  Chip test register      0x28  */
+#define ESP_CFG2	0x0bUL		/* rw  Second cfg register     0x2c  */
+#define ESP_CFG3	0x0cUL		/* rw  Third cfg register      0x30  */
+#define ESP_TCHI	0x0eUL		/* rw  High bits transf count  0x38  */
+#define ESP_UID		ESP_TCHI	/* ro  Unique ID code          0x38  */
+#define FAS_RLO		ESP_TCHI	/* rw  HME extended counter    0x38  */
+#define ESP_FGRND	0x0fUL		/* rw  Data base for fifo      0x3c  */
+#define FAS_RHI		ESP_FGRND	/* rw  HME extended counter    0x3c  */
+
+#define SBUS_ESP_REG_SIZE	0x40UL
+
+/* Bitfield meanings for the above registers. */
+
+/* ESP config reg 1, read-write, found on all ESP chips */
+#define ESP_CONFIG1_ID        0x07      /* My BUS ID bits */
+#define ESP_CONFIG1_CHTEST    0x08      /* Enable ESP chip tests */
+#define ESP_CONFIG1_PENABLE   0x10      /* Enable parity checks */
+#define ESP_CONFIG1_PARTEST   0x20      /* Parity test mode enabled? */
+#define ESP_CONFIG1_SRRDISAB  0x40      /* Disable SCSI reset reports */
+#define ESP_CONFIG1_SLCABLE   0x80      /* Enable slow cable mode */
+
+/* ESP config reg 2, read-write, found only on esp100a+esp200+esp236 chips */
+#define ESP_CONFIG2_DMAPARITY 0x01      /* enable DMA Parity (200,236) */
+#define ESP_CONFIG2_REGPARITY 0x02      /* enable reg Parity (200,236) */
+#define ESP_CONFIG2_BADPARITY 0x04      /* Bad parity target abort  */
+#define ESP_CONFIG2_SCSI2ENAB 0x08      /* Enable SCSI-2 features (tgtmode) */
+#define ESP_CONFIG2_HI        0x10      /* High Impedance DREQ ???  */
+#define ESP_CONFIG2_HMEFENAB  0x10      /* HME features enable */
+#define ESP_CONFIG2_BCM       0x20      /* Enable byte-ctrl (236)   */
+#define ESP_CONFIG2_DISPINT   0x20      /* Disable pause irq (hme) */
+#define ESP_CONFIG2_FENAB     0x40      /* Enable features (fas100,216) */
+#define ESP_CONFIG2_SPL       0x40      /* Enable status-phase latch (236) */
+#define ESP_CONFIG2_MKDONE    0x40      /* HME magic feature */
+#define ESP_CONFIG2_HME32     0x80      /* HME 32 extended */
+#define ESP_CONFIG2_MAGIC     0xe0      /* Invalid bits... */
+
+/* ESP config register 3 read-write, found only esp236+fas236+fas100a+hme chips */
+#define ESP_CONFIG3_FCLOCK    0x01     /* FAST SCSI clock rate (esp100a/hme) */
+#define ESP_CONFIG3_TEM       0x01     /* Enable thresh-8 mode (esp/fas236)  */
+#define ESP_CONFIG3_FAST      0x02     /* Enable FAST SCSI     (esp100a/hme) */
+#define ESP_CONFIG3_ADMA      0x02     /* Enable alternate-dma (esp/fas236)  */
+#define ESP_CONFIG3_TENB      0x04     /* group2 SCSI2 support (esp100a/hme) */
+#define ESP_CONFIG3_SRB       0x04     /* Save residual byte   (esp/fas236)  */
+#define ESP_CONFIG3_TMS       0x08     /* Three-byte msg's ok  (esp100a/hme) */
+#define ESP_CONFIG3_FCLK      0x08     /* Fast SCSI clock rate (esp/fas236)  */
+#define ESP_CONFIG3_IDMSG     0x10     /* ID message checking  (esp100a/hme) */
+#define ESP_CONFIG3_FSCSI     0x10     /* Enable FAST SCSI     (esp/fas236)  */
+#define ESP_CONFIG3_GTM       0x20     /* group2 SCSI2 support (esp/fas236)  */
+#define ESP_CONFIG3_IDBIT3    0x20     /* Bit 3 of HME SCSI-ID (hme)         */
+#define ESP_CONFIG3_TBMS      0x40     /* Three-byte msg's ok  (esp/fas236)  */
+#define ESP_CONFIG3_EWIDE     0x40     /* Enable Wide-SCSI     (hme)         */
+#define ESP_CONFIG3_IMS       0x80     /* ID msg chk'ng        (esp/fas236)  */
+#define ESP_CONFIG3_OBPUSH    0x80     /* Push odd-byte to dma (hme)         */
+
+/* ESP command register read-write */
+/* Group 1 commands:  These may be sent at any point in time to the ESP
+ *                    chip.  None of them can generate interrupts 'cept
+ *                    the "SCSI bus reset" command if you have not disabled
+ *                    SCSI reset interrupts in the config1 ESP register.
+ */
+#define ESP_CMD_NULL          0x00     /* Null command, ie. a nop */
+#define ESP_CMD_FLUSH         0x01     /* FIFO Flush */
+#define ESP_CMD_RC            0x02     /* Chip reset */
+#define ESP_CMD_RS            0x03     /* SCSI bus reset */
+
+/* Group 2 commands:  ESP must be an initiator and connected to a target
+ *                    for these commands to work.
+ */
+#define ESP_CMD_TI            0x10     /* Transfer Information */
+#define ESP_CMD_ICCSEQ        0x11     /* Initiator cmd complete sequence */
+#define ESP_CMD_MOK           0x12     /* Message okie-dokie */
+#define ESP_CMD_TPAD          0x18     /* Transfer Pad */
+#define ESP_CMD_SATN          0x1a     /* Set ATN */
+#define ESP_CMD_RATN          0x1b     /* De-assert ATN */
+
+/* Group 3 commands:  ESP must be in the MSGOUT or MSGIN state and be connected
+ *                    to a target as the initiator for these commands to work.
+ */
+#define ESP_CMD_SMSG          0x20     /* Send message */
+#define ESP_CMD_SSTAT         0x21     /* Send status */
+#define ESP_CMD_SDATA         0x22     /* Send data */
+#define ESP_CMD_DSEQ          0x23     /* Discontinue Sequence */
+#define ESP_CMD_TSEQ          0x24     /* Terminate Sequence */
+#define ESP_CMD_TCCSEQ        0x25     /* Target cmd cmplt sequence */
+#define ESP_CMD_DCNCT         0x27     /* Disconnect */
+#define ESP_CMD_RMSG          0x28     /* Receive Message */
+#define ESP_CMD_RCMD          0x29     /* Receive Command */
+#define ESP_CMD_RDATA         0x2a     /* Receive Data */
+#define ESP_CMD_RCSEQ         0x2b     /* Receive cmd sequence */
+
+/* Group 4 commands:  The ESP must be in the disconnected state and must
+ *                    not be connected to any targets as initiator for
+ *                    these commands to work.
+ */
+#define ESP_CMD_RSEL          0x40     /* Reselect */
+#define ESP_CMD_SEL           0x41     /* Select w/o ATN */
+#define ESP_CMD_SELA          0x42     /* Select w/ATN */
+#define ESP_CMD_SELAS         0x43     /* Select w/ATN & STOP */
+#define ESP_CMD_ESEL          0x44     /* Enable selection */
+#define ESP_CMD_DSEL          0x45     /* Disable selections */
+#define ESP_CMD_SA3           0x46     /* Select w/ATN3 */
+#define ESP_CMD_RSEL3         0x47     /* Reselect3 */
+
+/* This bit enables the ESP's DMA on the SBus */
+#define ESP_CMD_DMA           0x80     /* Do DMA? */
+
+/* ESP status register read-only */
+#define ESP_STAT_PIO          0x01     /* IO phase bit */
+#define ESP_STAT_PCD          0x02     /* CD phase bit */
+#define ESP_STAT_PMSG         0x04     /* MSG phase bit */
+#define ESP_STAT_PMASK        0x07     /* Mask of phase bits */
+#define ESP_STAT_TDONE        0x08     /* Transfer Completed */
+#define ESP_STAT_TCNT         0x10     /* Transfer Counter Is Zero */
+#define ESP_STAT_PERR         0x20     /* Parity error */
+#define ESP_STAT_SPAM         0x40     /* Real bad error */
+/* This indicates the 'interrupt pending' condition on esp236, it is a reserved
+ * bit on other revs of the ESP.
+ */
+#define ESP_STAT_INTR         0x80             /* Interrupt */
+
+/* The status register can be masked with ESP_STAT_PMASK and compared
+ * with the following values to determine the current phase the ESP
+ * (at least thinks it) is in.  For our purposes we also add our own
+ * software 'done' bit for our phase management engine.
+ */
+#define ESP_DOP   (0)                                       /* Data Out  */
+#define ESP_DIP   (ESP_STAT_PIO)                            /* Data In   */
+#define ESP_CMDP  (ESP_STAT_PCD)                            /* Command   */
+#define ESP_STATP (ESP_STAT_PCD|ESP_STAT_PIO)               /* Status    */
+#define ESP_MOP   (ESP_STAT_PMSG|ESP_STAT_PCD)              /* Message Out */
+#define ESP_MIP   (ESP_STAT_PMSG|ESP_STAT_PCD|ESP_STAT_PIO) /* Message In */
+
+/* HME only: status 2 register */
+#define ESP_STAT2_SCHBIT      0x01 /* Upper bits 3-7 of sstep enabled */
+#define ESP_STAT2_FFLAGS      0x02 /* The fifo flags are now latched */
+#define ESP_STAT2_XCNT        0x04 /* The transfer counter is latched */
+#define ESP_STAT2_CREGA       0x08 /* The command reg is active now */
+#define ESP_STAT2_WIDE        0x10 /* Interface on this adapter is wide */
+#define ESP_STAT2_F1BYTE      0x20 /* There is one byte at top of fifo */
+#define ESP_STAT2_FMSB        0x40 /* Next byte in fifo is most significant */
+#define ESP_STAT2_FEMPTY      0x80 /* FIFO is empty */
+
+/* ESP interrupt register read-only */
+#define ESP_INTR_S            0x01     /* Select w/o ATN */
+#define ESP_INTR_SATN         0x02     /* Select w/ATN */
+#define ESP_INTR_RSEL         0x04     /* Reselected */
+#define ESP_INTR_FDONE        0x08     /* Function done */
+#define ESP_INTR_BSERV        0x10     /* Bus service */
+#define ESP_INTR_DC           0x20     /* Disconnect */
+#define ESP_INTR_IC           0x40     /* Illegal command given */
+#define ESP_INTR_SR           0x80     /* SCSI bus reset detected */
+
+/* ESP sequence step register read-only */
+#define ESP_STEP_VBITS        0x07     /* Valid bits */
+#define ESP_STEP_ASEL         0x00     /* Selection&Arbitrate cmplt */
+#define ESP_STEP_SID          0x01     /* One msg byte sent */
+#define ESP_STEP_NCMD         0x02     /* Was not in command phase */
+#define ESP_STEP_PPC          0x03     /* Early phase chg caused cmnd
+                                        * bytes to be lost
+                                        */
+#define ESP_STEP_FINI4        0x04     /* Command was sent ok */
+
+/* Ho hum, some ESP's set the step register to this as well... */
+#define ESP_STEP_FINI5        0x05
+#define ESP_STEP_FINI6        0x06
+#define ESP_STEP_FINI7        0x07
+
+/* ESP chip-test register read-write */
+#define ESP_TEST_TARG         0x01     /* Target test mode */
+#define ESP_TEST_INI          0x02     /* Initiator test mode */
+#define ESP_TEST_TS           0x04     /* Tristate test mode */
+
+/* ESP unique ID register read-only, found on fas236+fas100a only */
+#define ESP_UID_F100A         0x00     /* ESP FAS100A  */
+#define ESP_UID_F236          0x02     /* ESP FAS236   */
+#define ESP_UID_REV           0x07     /* ESP revision */
+#define ESP_UID_FAM           0xf8     /* ESP family   */
+
+/* ESP fifo flags register read-only */
+/* Note that the following implies a 16 byte FIFO on the ESP. */
+#define ESP_FF_FBYTES         0x1f     /* Num bytes in FIFO */
+#define ESP_FF_ONOTZERO       0x20     /* offset ctr not zero (esp100) */
+#define ESP_FF_SSTEP          0xe0     /* Sequence step */
+
+/* ESP clock conversion factor register write-only */
+#define ESP_CCF_F0            0x00     /* 35.01MHz - 40MHz */
+#define ESP_CCF_NEVER         0x01     /* Set it to this and die */
+#define ESP_CCF_F2            0x02     /* 10MHz */
+#define ESP_CCF_F3            0x03     /* 10.01MHz - 15MHz */
+#define ESP_CCF_F4            0x04     /* 15.01MHz - 20MHz */
+#define ESP_CCF_F5            0x05     /* 20.01MHz - 25MHz */
+#define ESP_CCF_F6            0x06     /* 25.01MHz - 30MHz */
+#define ESP_CCF_F7            0x07     /* 30.01MHz - 35MHz */
+
+/* HME only... */
+#define ESP_BUSID_RESELID     0x10
+#define ESP_BUSID_CTR32BIT    0x40
+
+#define ESP_BUS_TIMEOUT        250     /* In milli-seconds */
+#define ESP_TIMEO_CONST       8192
+#define ESP_NEG_DEFP(mhz, cfact) \
+        ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (8192 * (cfact)))
+#define ESP_MHZ_TO_CYCLE(mhertz)  ((1000000000) / ((mhertz) / 1000))
+#define ESP_TICK(ccf, cycle)  ((7682 * (ccf) * (cycle) / 1000))
+
+/* For slow to medium speed input clock rates we shoot for 5mb/s, but for high
+ * input clock rates we try to do 10mb/s although I don't think a transfer can
+ * even run that fast with an ESP even with DMA2 scatter gather pipelining.
+ */
+#define SYNC_DEFP_SLOW            0x32   /* 5mb/s  */
+#define SYNC_DEFP_FAST            0x19   /* 10mb/s */
+
+struct esp_cmd_priv {
+	union {
+		dma_addr_t	dma_addr;
+		int		num_sg;
+	} u;
+
+	unsigned int		cur_residue;
+	struct scatterlist	*cur_sg;
+	unsigned int		tot_residue;
+};
+#define ESP_CMD_PRIV(CMD)	((struct esp_cmd_priv *)(&(CMD)->SCp))
+
+enum esp_rev {
+	ESP100     = 0x00,  /* NCR53C90 - very broken */
+	ESP100A    = 0x01,  /* NCR53C90A */
+	ESP236     = 0x02,
+	FAS236     = 0x03,
+	FAS100A    = 0x04,
+	FAST       = 0x05,
+	FASHME     = 0x06,
+};
+
+struct esp_cmd_entry {
+	struct list_head	list;
+
+	struct scsi_cmnd	*cmd;
+
+	unsigned int		saved_cur_residue;
+	struct scatterlist	*saved_cur_sg;
+	unsigned int		saved_tot_residue;
+
+	u8			flags;
+#define ESP_CMD_FLAG_WRITE	0x01 /* DMA is a write */
+#define ESP_CMD_FLAG_ABORT	0x02 /* being aborted */
+#define ESP_CMD_FLAG_AUTOSENSE	0x04 /* Doing automatic REQUEST_SENSE */
+
+	u8			tag[2];
+
+	u8			status;
+	u8			message;
+
+	unsigned char		*sense_ptr;
+	unsigned char		*saved_sense_ptr;
+	dma_addr_t		sense_dma;
+
+	struct completion	*eh_done;
+};
+
+/* XXX make this configurable somehow XXX */
+#define ESP_DEFAULT_TAGS	16
+
+#define ESP_MAX_TARGET		16
+#define ESP_MAX_LUN		8
+#define ESP_MAX_TAG		256
+
+struct esp_lun_data {
+	struct esp_cmd_entry	*non_tagged_cmd;
+	int			num_tagged;
+	int			hold;
+	struct esp_cmd_entry	*tagged_cmds[ESP_MAX_TAG];
+};
+
+struct esp_target_data {
+	/* These are the ESP_STP, ESP_SOFF, and ESP_CFG3 register values which
+	 * match the currently negotiated settings for this target.  The SCSI
+	 * protocol values are maintained in spi_{offset,period,wide}(starget).
+	 */
+	u8			esp_period;
+	u8			esp_offset;
+	u8			esp_config3;
+
+	u8			flags;
+#define ESP_TGT_WIDE		0x01
+#define ESP_TGT_DISCONNECT	0x02
+#define ESP_TGT_NEGO_WIDE	0x04
+#define ESP_TGT_NEGO_SYNC	0x08
+#define ESP_TGT_CHECK_NEGO	0x40
+#define ESP_TGT_BROKEN		0x80
+
+	/* When ESP_TGT_CHECK_NEGO is set, on the next scsi command to this
+	 * device we will try to negotiate the following parameters.
+	 */
+	u8			nego_goal_period;
+	u8			nego_goal_offset;
+	u8			nego_goal_width;
+	u8			nego_goal_tags;
+
+	struct scsi_target	*starget;
+};
+
+struct esp_event_ent {
+	u8			type;
+#define ESP_EVENT_TYPE_EVENT	0x01
+#define ESP_EVENT_TYPE_CMD	0x02
+	u8			val;
+
+	u8			sreg;
+	u8			seqreg;
+	u8			sreg2;
+	u8			ireg;
+	u8			select_state;
+	u8			event;
+	u8			__pad;
+};
+
+struct esp;
+struct esp_driver_ops {
+	/* Read and write the ESP 8-bit registers.  On some
+	 * applications of the ESP chip the registers are at 4-byte
+	 * instead of 1-byte intervals.
+	 */
+	void (*esp_write8)(struct esp *esp, u8 val, unsigned long reg);
+	u8 (*esp_read8)(struct esp *esp, unsigned long reg);
+
+	/* Map and unmap DMA memory.  Eventually the driver will be
+	 * converted to the generic DMA API as soon as SBUS is able to
+	 * cope with that.  At such time we can remove this.
+	 */
+	dma_addr_t (*map_single)(struct esp *esp, void *buf,
+				 size_t sz, int dir);
+	int (*map_sg)(struct esp *esp, struct scatterlist *sg,
+		      int num_sg, int dir);
+	void (*unmap_single)(struct esp *esp, dma_addr_t addr,
+			     size_t sz, int dir);
+	void (*unmap_sg)(struct esp *esp, struct scatterlist *sg,
+			 int num_sg, int dir);
+
+	/* Return non-zero if there is an IRQ pending.  Usually this
+	 * status bit lives in the DMA controller sitting in front of
+	 * the ESP.  This has to be accurate or else the ESP interrupt
+	 * handler will not run.
+	 */
+	int (*irq_pending)(struct esp *esp);
+
+	/* Reset the DMA engine entirely.  On return, ESP interrupts
+	 * should be enabled.  Often the interrupt enabling is
+	 * controlled in the DMA engine.
+	 */
+	void (*reset_dma)(struct esp *esp);
+
+	/* Drain any pending DMA in the DMA engine after a transfer.
+	 * This is for writes to memory.
+	 */
+	void (*dma_drain)(struct esp *esp);
+
+	/* Invalidate the DMA engine after a DMA transfer.  */
+	void (*dma_invalidate)(struct esp *esp);
+
+	/* Setup an ESP command that will use a DMA transfer.
+	 * The 'esp_count' specifies what transfer length should be
+	 * programmed into the ESP transfer counter registers, whereas
+	 * the 'dma_count' is the length that should be programmed into
+	 * the DMA controller.  Usually they are the same.  If 'write'
+	 * is non-zero, this transfer is a write into memory.  'cmd'
+	 * holds the ESP command that should be issued by calling
+	 * scsi_esp_cmd() at the appropriate time while programming
+	 * the DMA hardware.
+	 */
+	void (*send_dma_cmd)(struct esp *esp, u32 dma_addr, u32 esp_count,
+			     u32 dma_count, int write, u8 cmd);
+
+	/* Return non-zero if the DMA engine is reporting an error
+	 * currently.
+	 */
+	int (*dma_error)(struct esp *esp);
+};
+
+#define ESP_MAX_MSG_SZ		8
+#define ESP_EVENT_LOG_SZ	32
+
+#define ESP_QUICKIRQ_LIMIT	100
+#define ESP_RESELECT_TAG_LIMIT	2500
+
+struct esp {
+	void __iomem		*regs;
+	void __iomem		*dma_regs;
+
+	const struct esp_driver_ops *ops;
+
+	struct Scsi_Host	*host;
+	void			*dev;
+
+	struct esp_cmd_entry	*active_cmd;
+
+	struct list_head	queued_cmds;
+	struct list_head	active_cmds;
+
+	u8			*command_block;
+	dma_addr_t		command_block_dma;
+
+	unsigned int		data_dma_len;
+
+	/* The following are used to determine the cause of an IRQ. Upon every
+	 * IRQ entry we synchronize these with the hardware registers.
+	 */
+	u8			sreg;
+	u8			seqreg;
+	u8			sreg2;
+	u8			ireg;
+
+	u32			prev_hme_dmacsr;
+	u8			prev_soff;
+	u8			prev_stp;
+	u8			prev_cfg3;
+	u8			__pad;
+
+	struct list_head	esp_cmd_pool;
+
+	struct esp_target_data	target[ESP_MAX_TARGET];
+
+	int			fifo_cnt;
+	u8			fifo[16];
+
+	struct esp_event_ent	esp_event_log[ESP_EVENT_LOG_SZ];
+	int			esp_event_cur;
+
+	u8			msg_out[ESP_MAX_MSG_SZ];
+	int			msg_out_len;
+
+	u8			msg_in[ESP_MAX_MSG_SZ];
+	int			msg_in_len;
+
+	u8			bursts;
+	u8			config1;
+	u8			config2;
+
+	u8			scsi_id;
+	u32			scsi_id_mask;
+
+	enum esp_rev		rev;
+
+	u32			flags;
+#define ESP_FLAG_DIFFERENTIAL	0x00000001
+#define ESP_FLAG_RESETTING	0x00000002
+#define ESP_FLAG_DOING_SLOWCMD	0x00000004
+#define ESP_FLAG_WIDE_CAPABLE	0x00000008
+#define ESP_FLAG_QUICKIRQ_CHECK	0x00000010
+
+	u8			select_state;
+#define ESP_SELECT_NONE		0x00 /* Not selecting */
+#define ESP_SELECT_BASIC	0x01 /* Select w/o MSGOUT phase */
+#define ESP_SELECT_MSGOUT	0x02 /* Select with MSGOUT */
+
+	/* When we are not selecting, we are expecting an event.  */
+	u8			event;
+#define ESP_EVENT_NONE		0x00
+#define ESP_EVENT_CMD_START	0x01
+#define ESP_EVENT_CMD_DONE	0x02
+#define ESP_EVENT_DATA_IN	0x03
+#define ESP_EVENT_DATA_OUT	0x04
+#define ESP_EVENT_DATA_DONE	0x05
+#define ESP_EVENT_MSGIN		0x06
+#define ESP_EVENT_MSGIN_MORE	0x07
+#define ESP_EVENT_MSGIN_DONE	0x08
+#define ESP_EVENT_MSGOUT	0x09
+#define ESP_EVENT_MSGOUT_DONE	0x0a
+#define ESP_EVENT_STATUS	0x0b
+#define ESP_EVENT_FREE_BUS	0x0c
+#define ESP_EVENT_CHECK_PHASE	0x0d
+#define ESP_EVENT_RESET		0x10
+
+	/* Probed in esp_get_clock_params() */
+	u32			cfact;
+	u32			cfreq;
+	u32			ccycle;
+	u32			ctick;
+	u32			neg_defp;
+	u32			sync_defp;
+
+	/* Computed in esp_reset_esp() */
+	u32			max_period;
+	u32			min_period;
+	u32			radelay;
+
+	/* Slow command state.  */
+	u8			*cmd_bytes_ptr;
+	int			cmd_bytes_left;
+
+	struct completion	*eh_reset;
+
+	struct sbus_dma		*dma;
+};
+
+#define host_to_esp(host)	((struct esp *)(host)->hostdata)
+
+/* A front-end driver for the ESP chip should do the following in
+ * it's device probe routine:
+ * 1) Allocate the host and private area using scsi_host_alloc()
+ *    with size 'sizeof(struct esp)'.  The first argument to
+ *    scsi_host_alloc() should be &scsi_esp_template.
+ * 2) Set host->max_id as appropriate.
+ * 3) Set esp->host to the scsi_host itself, and esp->dev
+ *    to the device object pointer.
+ * 4) Hook up esp->ops to the front-end implementation.
+ * 5) If the ESP chip supports wide transfers, set ESP_FLAG_WIDE_CAPABLE
+ *    in esp->flags.
+ * 6) Map the DMA and ESP chip registers.
+ * 7) DMA map the ESP command block, store the DMA address
+ *    in esp->command_block_dma.
+ * 8) Register the scsi_esp_intr() interrupt handler.
+ * 9) Probe for and provide the following chip properties:
+ *    esp->scsi_id (assign to esp->host->this_id too)
+ *    esp->scsi_id_mask
+ *    If ESP bus is differential, set ESP_FLAG_DIFFERENTIAL
+ *    esp->cfreq
+ *    DMA burst bit mask in esp->bursts, if necessary
+ * 10) Perform any actions necessary before the ESP device can
+ *     be programmed for the first time.  On some configs, for
+ *     example, the DMA engine has to be reset before ESP can
+ *     be programmed.
+ * 11) If necessary, call dev_set_drvdata() as needed.
+ * 12) Call scsi_esp_register() with prepared 'esp' structure
+ *     and a device pointer if possible.
+ * 13) Check scsi_esp_register() return value, release all resources
+ *     if an error was returned.
+ */
+extern struct scsi_host_template scsi_esp_template;
+extern int scsi_esp_register(struct esp *, struct device *);
+
+extern void scsi_esp_unregister(struct esp *);
+extern irqreturn_t scsi_esp_intr(int, void *);
+extern void scsi_esp_cmd(struct esp *, u8);
+
+#endif /* !(_ESP_SCSI_H) */
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index 38c3a29..bd8e7f3 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -435,7 +435,7 @@
 	struct class_device *cdev;
 	struct Scsi_Host *shost = ERR_PTR(-ENXIO), *p;
 
-	down_read(&class->subsys.rwsem);
+	down(&class->sem);
 	list_for_each_entry(cdev, &class->children, node) {
 		p = class_to_shost(cdev);
 		if (p->host_no == hostnum) {
@@ -443,7 +443,7 @@
 			break;
 		}
 	}
-	up_read(&class->subsys.rwsem);
+	up(&class->sem);
 
 	return shost;
 }
diff --git a/drivers/scsi/ibmvscsi/ibmvstgt.c b/drivers/scsi/ibmvscsi/ibmvstgt.c
index 4368ca0..a39a478 100644
--- a/drivers/scsi/ibmvscsi/ibmvstgt.c
+++ b/drivers/scsi/ibmvscsi/ibmvstgt.c
@@ -897,9 +897,9 @@
 {
 	struct device_node *rootdn;
 	const char *id, *model, *name;
-	unsigned int *num;
+	const unsigned int *num;
 
-	rootdn = find_path_device("/");
+	rootdn = of_find_node_by_path("/");
 	if (!rootdn)
 		return -ENOENT;
 
@@ -912,10 +912,11 @@
 	if (name)
 		strncpy(partition_name, name, sizeof(partition_name));
 
-	num = (unsigned int *) get_property(rootdn, "ibm,partition-no", NULL);
+	num = get_property(rootdn, "ibm,partition-no", NULL);
 	if (num)
 		partition_number = *num;
 
+	of_node_put(rootdn);
 	return 0;
 }
 
diff --git a/drivers/scsi/ibmvscsi/rpa_vscsi.c b/drivers/scsi/ibmvscsi/rpa_vscsi.c
index 227c0f2..0a533f3 100644
--- a/drivers/scsi/ibmvscsi/rpa_vscsi.c
+++ b/drivers/scsi/ibmvscsi/rpa_vscsi.c
@@ -157,7 +157,7 @@
 	const unsigned int *p_number_ptr;
 
 	/* Retrieve information about this partition */
-	rootdn = find_path_device("/");
+	rootdn = of_find_node_by_path("/");
 	if (!rootdn) {
 		return;
 	}
@@ -169,6 +169,7 @@
 	p_number_ptr = get_property(rootdn, "ibm,partition-no", NULL);
 	if (p_number_ptr)
 		partition_number = *p_number_ptr;
+	of_node_put(rootdn);
 }
 
 static void set_adapter_info(struct ibmvscsi_host_data *hostdata)
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index 95045e3..e9bd299 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -3770,7 +3770,8 @@
  * Return value:
  *	0 on success / non-zero on failure
  **/
-static int ipr_sata_reset(struct ata_port *ap, unsigned int *classes)
+static int ipr_sata_reset(struct ata_port *ap, unsigned int *classes,
+				unsigned long deadline)
 {
 	struct ipr_sata_port *sata_port = ap->private_data;
 	struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg;
diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c
index 9f10689..c4195ea 100644
--- a/drivers/scsi/qlogicpti.c
+++ b/drivers/scsi/qlogicpti.c
@@ -1403,7 +1403,7 @@
 	struct scsi_host_template *tpnt = match->data;
 	struct Scsi_Host *host;
 	struct qlogicpti *qpti;
-	char *fcode;
+	const char *fcode;
 
 	/* Sometimes Antares cards come up not completely
 	 * setup, and we get a report of a zero IRQ.
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 9f7482d..05d79af 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -31,7 +31,7 @@
 
 
 #define SG_MEMPOOL_NR		ARRAY_SIZE(scsi_sg_pools)
-#define SG_MEMPOOL_SIZE		32
+#define SG_MEMPOOL_SIZE		2
 
 struct scsi_host_sg_pool {
 	size_t		size;
diff --git a/drivers/scsi/scsi_netlink.c b/drivers/scsi/scsi_netlink.c
index 1b59b27..4bf9aa547 100644
--- a/drivers/scsi/scsi_netlink.c
+++ b/drivers/scsi/scsi_netlink.c
@@ -50,7 +50,7 @@
 	while (skb->len >= NLMSG_SPACE(0)) {
 		err = 0;
 
-		nlh = (struct nlmsghdr *) skb->data;
+		nlh = nlmsg_hdr(skb);
 		if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*hdr))) ||
 		    (skb->len < nlh->nlmsg_len)) {
 			printk(KERN_WARNING "%s: discarding partial skb\n",
@@ -168,7 +168,8 @@
 	}
 
 	scsi_nl_sock = netlink_kernel_create(NETLINK_SCSITRANSPORT,
-				SCSI_NL_GRP_CNT, scsi_nl_rcv, THIS_MODULE);
+				SCSI_NL_GRP_CNT, scsi_nl_rcv, NULL,
+				THIS_MODULE);
 	if (!scsi_nl_sock) {
 		printk(KERN_ERR "%s: register of recieve handler failed\n",
 				__FUNCTION__);
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index ce0d14a..aabaa05 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -1081,7 +1081,7 @@
 			struct nlmsghdr	*nlh;
 			struct iscsi_uevent *ev;
 
-			nlh = (struct nlmsghdr *)skb->data;
+			nlh = nlmsg_hdr(skb);
 			if (nlh->nlmsg_len < sizeof(*nlh) ||
 			    skb->len < nlh->nlmsg_len) {
 				break;
@@ -1435,7 +1435,7 @@
 	if (err)
 		goto unregister_conn_class;
 
-	nls = netlink_kernel_create(NETLINK_ISCSI, 1, iscsi_if_rx,
+	nls = netlink_kernel_create(NETLINK_ISCSI, 1, iscsi_if_rx, NULL,
 			THIS_MODULE);
 	if (!nls) {
 		err = -ENOBUFS;
diff --git a/drivers/scsi/sun_esp.c b/drivers/scsi/sun_esp.c
new file mode 100644
index 0000000..8c766bc
--- /dev/null
+++ b/drivers/scsi/sun_esp.c
@@ -0,0 +1,634 @@
+/* sun_esp.c: ESP front-end for Sparc SBUS systems.
+ *
+ * Copyright (C) 2007 David S. Miller (davem@davemloft.net)
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <asm/sbus.h>
+
+#include <scsi/scsi_host.h>
+
+#include "esp_scsi.h"
+
+#define DRV_MODULE_NAME		"sun_esp"
+#define PFX DRV_MODULE_NAME	": "
+#define DRV_VERSION		"1.000"
+#define DRV_MODULE_RELDATE	"April 19, 2007"
+
+#define dma_read32(REG) \
+	sbus_readl(esp->dma_regs + (REG))
+#define dma_write32(VAL, REG) \
+	sbus_writel((VAL), esp->dma_regs + (REG))
+
+static int __devinit esp_sbus_find_dma(struct esp *esp, struct sbus_dev *dma_sdev)
+{
+	struct sbus_dev *sdev = esp->dev;
+	struct sbus_dma *dma;
+
+	if (dma_sdev != NULL) {
+		for_each_dvma(dma) {
+			if (dma->sdev == dma_sdev)
+				break;
+		}
+	} else {
+		for_each_dvma(dma) {
+			if (dma->sdev == NULL)
+				break;
+
+			/* If bus + slot are the same and it has the
+			 * correct OBP name, it's ours.
+			 */
+			if (sdev->bus == dma->sdev->bus &&
+			    sdev->slot == dma->sdev->slot &&
+			    (!strcmp(dma->sdev->prom_name, "dma") ||
+			     !strcmp(dma->sdev->prom_name, "espdma")))
+				break;
+		}
+	}
+
+	if (dma == NULL) {
+		printk(KERN_ERR PFX "[%s] Cannot find dma.\n",
+		       sdev->ofdev.node->full_name);
+		return -ENODEV;
+	}
+	esp->dma = dma;
+	esp->dma_regs = dma->regs;
+
+	return 0;
+
+}
+
+static int __devinit esp_sbus_map_regs(struct esp *esp, int hme)
+{
+	struct sbus_dev *sdev = esp->dev;
+	struct resource *res;
+
+	/* On HME, two reg sets exist, first is DVMA,
+	 * second is ESP registers.
+	 */
+	if (hme)
+		res = &sdev->resource[1];
+	else
+		res = &sdev->resource[0];
+
+	esp->regs = sbus_ioremap(res, 0, SBUS_ESP_REG_SIZE, "ESP");
+	if (!esp->regs)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int __devinit esp_sbus_map_command_block(struct esp *esp)
+{
+	struct sbus_dev *sdev = esp->dev;
+
+	esp->command_block = sbus_alloc_consistent(sdev, 16,
+						   &esp->command_block_dma);
+	if (!esp->command_block)
+		return -ENOMEM;
+	return 0;
+}
+
+static int __devinit esp_sbus_register_irq(struct esp *esp)
+{
+	struct Scsi_Host *host = esp->host;
+	struct sbus_dev *sdev = esp->dev;
+
+	host->irq = sdev->irqs[0];
+	return request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, "ESP", esp);
+}
+
+static void __devinit esp_get_scsi_id(struct esp *esp)
+{
+	struct sbus_dev *sdev = esp->dev;
+	struct device_node *dp = sdev->ofdev.node;
+
+	esp->scsi_id = of_getintprop_default(dp, "initiator-id", 0xff);
+	if (esp->scsi_id != 0xff)
+		goto done;
+
+	esp->scsi_id = of_getintprop_default(dp, "scsi-initiator-id", 0xff);
+	if (esp->scsi_id != 0xff)
+		goto done;
+
+	if (!sdev->bus) {
+		/* SUN4 */
+		esp->scsi_id = 7;
+		goto done;
+	}
+
+	esp->scsi_id = of_getintprop_default(sdev->bus->ofdev.node,
+					     "scsi-initiator-id", 7);
+
+done:
+	esp->host->this_id = esp->scsi_id;
+	esp->scsi_id_mask = (1 << esp->scsi_id);
+}
+
+static void __devinit esp_get_differential(struct esp *esp)
+{
+	struct sbus_dev *sdev = esp->dev;
+	struct device_node *dp = sdev->ofdev.node;
+
+	if (of_find_property(dp, "differential", NULL))
+		esp->flags |= ESP_FLAG_DIFFERENTIAL;
+	else
+		esp->flags &= ~ESP_FLAG_DIFFERENTIAL;
+}
+
+static void __devinit esp_get_clock_params(struct esp *esp)
+{
+	struct sbus_dev *sdev = esp->dev;
+	struct device_node *dp = sdev->ofdev.node;
+	struct device_node *bus_dp;
+	int fmhz;
+
+	bus_dp = NULL;
+	if (sdev != NULL && sdev->bus != NULL)
+		bus_dp = sdev->bus->ofdev.node;
+
+	fmhz = of_getintprop_default(dp, "clock-frequency", 0);
+	if (fmhz == 0)
+		fmhz = (!bus_dp) ? 0 :
+			of_getintprop_default(bus_dp, "clock-frequency", 0);
+
+	esp->cfreq = fmhz;
+}
+
+static void __devinit esp_get_bursts(struct esp *esp, struct sbus_dev *dma)
+{
+	struct sbus_dev *sdev = esp->dev;
+	struct device_node *dp = sdev->ofdev.node;
+	u8 bursts;
+
+	bursts = of_getintprop_default(dp, "burst-sizes", 0xff);
+	if (dma) {
+		struct device_node *dma_dp = dma->ofdev.node;
+		u8 val = of_getintprop_default(dma_dp, "burst-sizes", 0xff);
+		if (val != 0xff)
+			bursts &= val;
+	}
+
+	if (sdev->bus) {
+		u8 val = of_getintprop_default(sdev->bus->ofdev.node,
+					       "burst-sizes", 0xff);
+		if (val != 0xff)
+			bursts &= val;
+	}
+
+	if (bursts == 0xff ||
+	    (bursts & DMA_BURST16) == 0 ||
+	    (bursts & DMA_BURST32) == 0)
+		bursts = (DMA_BURST32 - 1);
+
+	esp->bursts = bursts;
+}
+
+static void __devinit esp_sbus_get_props(struct esp *esp, struct sbus_dev *espdma)
+{
+	esp_get_scsi_id(esp);
+	esp_get_differential(esp);
+	esp_get_clock_params(esp);
+	esp_get_bursts(esp, espdma);
+}
+
+static void sbus_esp_write8(struct esp *esp, u8 val, unsigned long reg)
+{
+	sbus_writeb(val, esp->regs + (reg * 4UL));
+}
+
+static u8 sbus_esp_read8(struct esp *esp, unsigned long reg)
+{
+	return sbus_readb(esp->regs + (reg * 4UL));
+}
+
+static dma_addr_t sbus_esp_map_single(struct esp *esp, void *buf,
+				      size_t sz, int dir)
+{
+	return sbus_map_single(esp->dev, buf, sz, dir);
+}
+
+static int sbus_esp_map_sg(struct esp *esp, struct scatterlist *sg,
+				  int num_sg, int dir)
+{
+	return sbus_map_sg(esp->dev, sg, num_sg, dir);
+}
+
+static void sbus_esp_unmap_single(struct esp *esp, dma_addr_t addr,
+				  size_t sz, int dir)
+{
+	sbus_unmap_single(esp->dev, addr, sz, dir);
+}
+
+static void sbus_esp_unmap_sg(struct esp *esp, struct scatterlist *sg,
+			      int num_sg, int dir)
+{
+	sbus_unmap_sg(esp->dev, sg, num_sg, dir);
+}
+
+static int sbus_esp_irq_pending(struct esp *esp)
+{
+	if (dma_read32(DMA_CSR) & (DMA_HNDL_INTR | DMA_HNDL_ERROR))
+		return 1;
+	return 0;
+}
+
+static void sbus_esp_reset_dma(struct esp *esp)
+{
+	int can_do_burst16, can_do_burst32, can_do_burst64;
+	int can_do_sbus64, lim;
+	u32 val;
+
+	can_do_burst16 = (esp->bursts & DMA_BURST16) != 0;
+	can_do_burst32 = (esp->bursts & DMA_BURST32) != 0;
+	can_do_burst64 = 0;
+	can_do_sbus64 = 0;
+	if (sbus_can_dma_64bit(esp->dev))
+		can_do_sbus64 = 1;
+	if (sbus_can_burst64(esp->sdev))
+		can_do_burst64 = (esp->bursts & DMA_BURST64) != 0;
+
+	/* Put the DVMA into a known state. */
+	if (esp->dma->revision != dvmahme) {
+		val = dma_read32(DMA_CSR);
+		dma_write32(val | DMA_RST_SCSI, DMA_CSR);
+		dma_write32(val & ~DMA_RST_SCSI, DMA_CSR);
+	}
+	switch (esp->dma->revision) {
+	case dvmahme:
+		dma_write32(DMA_RESET_FAS366, DMA_CSR);
+		dma_write32(DMA_RST_SCSI, DMA_CSR);
+
+		esp->prev_hme_dmacsr = (DMA_PARITY_OFF | DMA_2CLKS |
+					DMA_SCSI_DISAB | DMA_INT_ENAB);
+
+		esp->prev_hme_dmacsr &= ~(DMA_ENABLE | DMA_ST_WRITE |
+					  DMA_BRST_SZ);
+
+		if (can_do_burst64)
+			esp->prev_hme_dmacsr |= DMA_BRST64;
+		else if (can_do_burst32)
+			esp->prev_hme_dmacsr |= DMA_BRST32;
+
+		if (can_do_sbus64) {
+			esp->prev_hme_dmacsr |= DMA_SCSI_SBUS64;
+			sbus_set_sbus64(esp->dev, esp->bursts);
+		}
+
+		lim = 1000;
+		while (dma_read32(DMA_CSR) & DMA_PEND_READ) {
+			if (--lim == 0) {
+				printk(KERN_ALERT PFX "esp%d: DMA_PEND_READ "
+				       "will not clear!\n",
+				       esp->host->unique_id);
+				break;
+			}
+			udelay(1);
+		}
+
+		dma_write32(0, DMA_CSR);
+		dma_write32(esp->prev_hme_dmacsr, DMA_CSR);
+
+		dma_write32(0, DMA_ADDR);
+		break;
+
+	case dvmarev2:
+		if (esp->rev != ESP100) {
+			val = dma_read32(DMA_CSR);
+			dma_write32(val | DMA_3CLKS, DMA_CSR);
+		}
+		break;
+
+	case dvmarev3:
+		val = dma_read32(DMA_CSR);
+		val &= ~DMA_3CLKS;
+		val |= DMA_2CLKS;
+		if (can_do_burst32) {
+			val &= ~DMA_BRST_SZ;
+			val |= DMA_BRST32;
+		}
+		dma_write32(val, DMA_CSR);
+		break;
+
+	case dvmaesc1:
+		val = dma_read32(DMA_CSR);
+		val |= DMA_ADD_ENABLE;
+		val &= ~DMA_BCNT_ENAB;
+		if (!can_do_burst32 && can_do_burst16) {
+			val |= DMA_ESC_BURST;
+		} else {
+			val &= ~(DMA_ESC_BURST);
+		}
+		dma_write32(val, DMA_CSR);
+		break;
+
+	default:
+		break;
+	}
+
+	/* Enable interrupts.  */
+	val = dma_read32(DMA_CSR);
+	dma_write32(val | DMA_INT_ENAB, DMA_CSR);
+}
+
+static void sbus_esp_dma_drain(struct esp *esp)
+{
+	u32 csr;
+	int lim;
+
+	if (esp->dma->revision == dvmahme)
+		return;
+
+	csr = dma_read32(DMA_CSR);
+	if (!(csr & DMA_FIFO_ISDRAIN))
+		return;
+
+	if (esp->dma->revision != dvmarev3 && esp->dma->revision != dvmaesc1)
+		dma_write32(csr | DMA_FIFO_STDRAIN, DMA_CSR);
+
+	lim = 1000;
+	while (dma_read32(DMA_CSR) & DMA_FIFO_ISDRAIN) {
+		if (--lim == 0) {
+			printk(KERN_ALERT PFX "esp%d: DMA will not drain!\n",
+			       esp->host->unique_id);
+			break;
+		}
+		udelay(1);
+	}
+}
+
+static void sbus_esp_dma_invalidate(struct esp *esp)
+{
+	if (esp->dma->revision == dvmahme) {
+		dma_write32(DMA_RST_SCSI, DMA_CSR);
+
+		esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr |
+					 (DMA_PARITY_OFF | DMA_2CLKS |
+					  DMA_SCSI_DISAB | DMA_INT_ENAB)) &
+					~(DMA_ST_WRITE | DMA_ENABLE));
+
+		dma_write32(0, DMA_CSR);
+		dma_write32(esp->prev_hme_dmacsr, DMA_CSR);
+
+		/* This is necessary to avoid having the SCSI channel
+		 * engine lock up on us.
+		 */
+		dma_write32(0, DMA_ADDR);
+	} else {
+		u32 val;
+		int lim;
+
+		lim = 1000;
+		while ((val = dma_read32(DMA_CSR)) & DMA_PEND_READ) {
+			if (--lim == 0) {
+				printk(KERN_ALERT PFX "esp%d: DMA will not "
+				       "invalidate!\n", esp->host->unique_id);
+				break;
+			}
+			udelay(1);
+		}
+
+		val &= ~(DMA_ENABLE | DMA_ST_WRITE | DMA_BCNT_ENAB);
+		val |= DMA_FIFO_INV;
+		dma_write32(val, DMA_CSR);
+		val &= ~DMA_FIFO_INV;
+		dma_write32(val, DMA_CSR);
+	}
+}
+
+static void sbus_esp_send_dma_cmd(struct esp *esp, u32 addr, u32 esp_count,
+				  u32 dma_count, int write, u8 cmd)
+{
+	u32 csr;
+
+	BUG_ON(!(cmd & ESP_CMD_DMA));
+
+	sbus_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW);
+	sbus_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED);
+	if (esp->rev == FASHME) {
+		sbus_esp_write8(esp, (esp_count >> 16) & 0xff, FAS_RLO);
+		sbus_esp_write8(esp, 0, FAS_RHI);
+
+		scsi_esp_cmd(esp, cmd);
+
+		csr = esp->prev_hme_dmacsr;
+		csr |= DMA_SCSI_DISAB | DMA_ENABLE;
+		if (write)
+			csr |= DMA_ST_WRITE;
+		else
+			csr &= ~DMA_ST_WRITE;
+		esp->prev_hme_dmacsr = csr;
+
+		dma_write32(dma_count, DMA_COUNT);
+		dma_write32(addr, DMA_ADDR);
+		dma_write32(csr, DMA_CSR);
+	} else {
+		csr = dma_read32(DMA_CSR);
+		csr |= DMA_ENABLE;
+		if (write)
+			csr |= DMA_ST_WRITE;
+		else
+			csr &= ~DMA_ST_WRITE;
+		dma_write32(csr, DMA_CSR);
+		if (esp->dma->revision == dvmaesc1) {
+			u32 end = PAGE_ALIGN(addr + dma_count + 16U);
+			dma_write32(end - addr, DMA_COUNT);
+		}
+		dma_write32(addr, DMA_ADDR);
+
+		scsi_esp_cmd(esp, cmd);
+	}
+
+}
+
+static int sbus_esp_dma_error(struct esp *esp)
+{
+	u32 csr = dma_read32(DMA_CSR);
+
+	if (csr & DMA_HNDL_ERROR)
+		return 1;
+
+	return 0;
+}
+
+static const struct esp_driver_ops sbus_esp_ops = {
+	.esp_write8	=	sbus_esp_write8,
+	.esp_read8	=	sbus_esp_read8,
+	.map_single	=	sbus_esp_map_single,
+	.map_sg		=	sbus_esp_map_sg,
+	.unmap_single	=	sbus_esp_unmap_single,
+	.unmap_sg	=	sbus_esp_unmap_sg,
+	.irq_pending	=	sbus_esp_irq_pending,
+	.reset_dma	=	sbus_esp_reset_dma,
+	.dma_drain	=	sbus_esp_dma_drain,
+	.dma_invalidate	=	sbus_esp_dma_invalidate,
+	.send_dma_cmd	=	sbus_esp_send_dma_cmd,
+	.dma_error	=	sbus_esp_dma_error,
+};
+
+static int __devinit esp_sbus_probe_one(struct device *dev,
+					struct sbus_dev *esp_dev,
+					struct sbus_dev *espdma,
+					struct sbus_bus *sbus,
+					int hme)
+{
+	struct scsi_host_template *tpnt = &scsi_esp_template;
+	struct Scsi_Host *host;
+	struct esp *esp;
+	int err;
+
+	host = scsi_host_alloc(tpnt, sizeof(struct esp));
+
+	err = -ENOMEM;
+	if (!host)
+		goto fail;
+
+	host->max_id = (hme ? 16 : 8);
+	esp = host_to_esp(host);
+
+	esp->host = host;
+	esp->dev = esp_dev;
+	esp->ops = &sbus_esp_ops;
+
+	if (hme)
+		esp->flags |= ESP_FLAG_WIDE_CAPABLE;
+
+	err = esp_sbus_find_dma(esp, espdma);
+	if (err < 0)
+		goto fail_unlink;
+
+	err = esp_sbus_map_regs(esp, hme);
+	if (err < 0)
+		goto fail_unlink;
+
+	err = esp_sbus_map_command_block(esp);
+	if (err < 0)
+		goto fail_unmap_regs;
+
+	err = esp_sbus_register_irq(esp);
+	if (err < 0)
+		goto fail_unmap_command_block;
+
+	esp_sbus_get_props(esp, espdma);
+
+	/* Before we try to touch the ESP chip, ESC1 dma can
+	 * come up with the reset bit set, so make sure that
+	 * is clear first.
+	 */
+	if (esp->dma->revision == dvmaesc1) {
+		u32 val = dma_read32(DMA_CSR);
+
+		dma_write32(val & ~DMA_RST_SCSI, DMA_CSR);
+	}
+
+	dev_set_drvdata(&esp_dev->ofdev.dev, esp);
+
+	err = scsi_esp_register(esp, dev);
+	if (err)
+		goto fail_free_irq;
+
+	return 0;
+
+fail_free_irq:
+	free_irq(host->irq, esp);
+fail_unmap_command_block:
+	sbus_free_consistent(esp->dev, 16,
+			     esp->command_block,
+			     esp->command_block_dma);
+fail_unmap_regs:
+	sbus_iounmap(esp->regs, SBUS_ESP_REG_SIZE);
+fail_unlink:
+	scsi_host_put(host);
+fail:
+	return err;
+}
+
+static int __devinit esp_sbus_probe(struct of_device *dev, const struct of_device_id *match)
+{
+	struct sbus_dev *sdev = to_sbus_device(&dev->dev);
+	struct device_node *dp = dev->node;
+	struct sbus_dev *dma_sdev = NULL;
+	int hme = 0;
+
+	if (dp->parent &&
+	    (!strcmp(dp->parent->name, "espdma") ||
+	     !strcmp(dp->parent->name, "dma")))
+		dma_sdev = sdev->parent;
+	else if (!strcmp(dp->name, "SUNW,fas")) {
+		dma_sdev = sdev;
+		hme = 1;
+	}
+
+	return esp_sbus_probe_one(&dev->dev, sdev, dma_sdev,
+				  sdev->bus, hme);
+}
+
+static int __devexit esp_sbus_remove(struct of_device *dev)
+{
+	struct esp *esp = dev_get_drvdata(&dev->dev);
+	unsigned int irq = esp->host->irq;
+	u32 val;
+
+	scsi_esp_unregister(esp);
+
+	/* Disable interrupts.  */
+	val = dma_read32(DMA_CSR);
+	dma_write32(val & ~DMA_INT_ENAB, DMA_CSR);
+
+	free_irq(irq, esp);
+	sbus_free_consistent(esp->dev, 16,
+			     esp->command_block,
+			     esp->command_block_dma);
+	sbus_iounmap(esp->regs, SBUS_ESP_REG_SIZE);
+
+	scsi_host_put(esp->host);
+
+	return 0;
+}
+
+static struct of_device_id esp_match[] = {
+	{
+		.name = "SUNW,esp",
+	},
+	{
+		.name = "SUNW,fas",
+	},
+	{
+		.name = "esp",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, esp_match);
+
+static struct of_platform_driver esp_sbus_driver = {
+	.name		= "esp",
+	.match_table	= esp_match,
+	.probe		= esp_sbus_probe,
+	.remove		= __devexit_p(esp_sbus_remove),
+};
+
+static int __init sunesp_init(void)
+{
+	return of_register_driver(&esp_sbus_driver, &sbus_bus_type);
+}
+
+static void __exit sunesp_exit(void)
+{
+	of_unregister_driver(&esp_sbus_driver);
+}
+
+MODULE_DESCRIPTION("Sun ESP SCSI driver");
+MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+module_init(sunesp_init);
+module_exit(sunesp_exit);
diff --git a/drivers/serial/pmac_zilog.c b/drivers/serial/pmac_zilog.c
index 752ef07..be8d757 100644
--- a/drivers/serial/pmac_zilog.c
+++ b/drivers/serial/pmac_zilog.c
@@ -1467,7 +1467,8 @@
 	if (ZS_IS_IRDA(uap))
 		uap->port_type = PMAC_SCC_IRDA;
 	if (ZS_IS_INTMODEM(uap)) {
-		struct device_node* i2c_modem = find_devices("i2c-modem");
+		struct device_node* i2c_modem =
+			of_find_node_by_name(NULL, "i2c-modem");
 		if (i2c_modem) {
 			const char* mid =
 				get_property(i2c_modem, "modem-id", NULL);
@@ -1482,6 +1483,7 @@
 			}
 			printk(KERN_INFO "pmac_zilog: i2c-modem detected, id: %d\n",
 				mid ? (*mid) : 0);
+			of_node_put(i2c_modem);
 		} else {
 			printk(KERN_INFO "pmac_zilog: serial modem detected\n");
 		}
diff --git a/drivers/serial/sunsu.c b/drivers/serial/sunsu.c
index 96a852a..bfd4417 100644
--- a/drivers/serial/sunsu.c
+++ b/drivers/serial/sunsu.c
@@ -1387,8 +1387,8 @@
 	struct device_node *ap = of_find_node_by_path("/aliases");
 
 	if (ap) {
-		char *keyb = of_get_property(ap, "keyboard", NULL);
-		char *ms = of_get_property(ap, "mouse", NULL);
+		const char *keyb = of_get_property(ap, "keyboard", NULL);
+		const char *ms = of_get_property(ap, "mouse", NULL);
 
 		if (keyb) {
 			if (dp == of_find_node_by_path(keyb))
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 8b7ff46..f5de58a 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -15,7 +15,6 @@
 obj-$(CONFIG_USB_UHCI_HCD)	+= host/
 obj-$(CONFIG_USB_SL811_HCD)	+= host/
 obj-$(CONFIG_USB_U132_HCD)	+= host/
-obj-$(CONFIG_ETRAX_USB_HOST)	+= host/
 obj-$(CONFIG_USB_OHCI_AT91)	+= host/
 
 obj-$(CONFIG_USB_ACM)		+= class/
@@ -27,10 +26,7 @@
 obj-$(CONFIG_USB_ACECAD)	+= input/
 obj-$(CONFIG_USB_AIPTEK)	+= input/
 obj-$(CONFIG_USB_ATI_REMOTE)	+= input/
-obj-$(CONFIG_USB_HID)		+= input/
-obj-$(CONFIG_USB_KBD)		+= input/
 obj-$(CONFIG_USB_KBTAB)		+= input/
-obj-$(CONFIG_USB_MOUSE)		+= input/
 obj-$(CONFIG_USB_MTOUCH)	+= input/
 obj-$(CONFIG_USB_POWERMATE)	+= input/
 obj-$(CONFIG_USB_WACOM)		+= input/
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index 3dfa3e4..30b7bfb 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -4,6 +4,7 @@
  *
  *  Copyright (C) 2004 David Woodhouse, Duncan Sands, Roman Kagan
  *  Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru)
+ *  Copyright (C) 2007 Simon Arlott
  *
  *  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
@@ -34,14 +35,14 @@
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/init.h>
-#include <linux/device.h>	/* FIXME: linux/firmware.h should include it itself */
+#include <linux/device.h>
 #include <linux/firmware.h>
 #include <linux/mutex.h>
 
 #include "usbatm.h"
 
-#define DRIVER_AUTHOR	"Roman Kagan, David Woodhouse, Duncan Sands"
-#define DRIVER_VERSION	"0.2"
+#define DRIVER_AUTHOR	"Roman Kagan, David Woodhouse, Duncan Sands, Simon Arlott"
+#define DRIVER_VERSION	"0.3"
 #define DRIVER_DESC	"Conexant AccessRunner ADSL USB modem driver"
 
 static const char cxacru_driver_name[] = "cxacru";
@@ -64,7 +65,7 @@
 #define SDRAM_ENA	0x1
 
 #define CMD_TIMEOUT	2000	/* msecs */
-#define POLL_INTERVAL	5000	/* msecs */
+#define POLL_INTERVAL	1	/* secs */
 
 /* commands for interaction with the modem through the control channel before
  * firmware is loaded  */
@@ -146,6 +147,13 @@
 	CXINF_MAX = 0x1c,
 };
 
+enum cxacru_poll_state {
+	CXPOLL_STOPPING,
+	CXPOLL_STOPPED,
+	CXPOLL_POLLING,
+	CXPOLL_SHUTDOWN
+};
+
 struct cxacru_modem_type {
 	u32 pll_f_clk;
 	u32 pll_b_clk;
@@ -158,7 +166,12 @@
 	const struct cxacru_modem_type *modem_type;
 
 	int line_status;
+	struct mutex adsl_state_serialize;
+	int adsl_status;
 	struct delayed_work poll_work;
+	u32 card_info[CXINF_MAX];
+	struct mutex poll_state_serialize;
+	int poll_state;
 
 	/* contol handles */
 	struct mutex cm_serialize;
@@ -170,6 +183,275 @@
 	struct completion snd_done;
 };
 
+static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
+	u8 *wdata, int wsize, u8 *rdata, int rsize);
+static void cxacru_poll_status(struct work_struct *work);
+
+/* Card info exported through sysfs */
+#define CXACRU__ATTR_INIT(_name) \
+static DEVICE_ATTR(_name, S_IRUGO, cxacru_sysfs_show_##_name, NULL)
+
+#define CXACRU_CMD_INIT(_name) \
+static DEVICE_ATTR(_name, S_IWUSR | S_IRUGO, \
+	cxacru_sysfs_show_##_name, cxacru_sysfs_store_##_name)
+
+#define CXACRU_ATTR_INIT(_value, _type, _name) \
+static ssize_t cxacru_sysfs_show_##_name(struct device *dev, \
+	struct device_attribute *attr, char *buf) \
+{ \
+	struct usb_interface *intf = to_usb_interface(dev); \
+	struct usbatm_data *usbatm_instance = usb_get_intfdata(intf); \
+	struct cxacru_data *instance = usbatm_instance->driver_data; \
+	return cxacru_sysfs_showattr_##_type(instance->card_info[_value], buf); \
+} \
+CXACRU__ATTR_INIT(_name)
+
+#define CXACRU_ATTR_CREATE(_v, _t, _name) CXACRU_DEVICE_CREATE_FILE(_name)
+#define CXACRU_CMD_CREATE(_name)          CXACRU_DEVICE_CREATE_FILE(_name)
+#define CXACRU__ATTR_CREATE(_name)        CXACRU_DEVICE_CREATE_FILE(_name)
+
+#define CXACRU_ATTR_REMOVE(_v, _t, _name) CXACRU_DEVICE_REMOVE_FILE(_name)
+#define CXACRU_CMD_REMOVE(_name)          CXACRU_DEVICE_REMOVE_FILE(_name)
+#define CXACRU__ATTR_REMOVE(_name)        CXACRU_DEVICE_REMOVE_FILE(_name)
+
+static ssize_t cxacru_sysfs_showattr_u32(u32 value, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%u\n", value);
+}
+
+static ssize_t cxacru_sysfs_showattr_s8(s8 value, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", value);
+}
+
+static ssize_t cxacru_sysfs_showattr_dB(s16 value, char *buf)
+{
+	if (unlikely(value < 0)) {
+		return snprintf(buf, PAGE_SIZE, "%d.%02u\n",
+						value / 100, -value % 100);
+	} else {
+		return snprintf(buf, PAGE_SIZE, "%d.%02u\n",
+						value / 100, value % 100);
+	}
+}
+
+static ssize_t cxacru_sysfs_showattr_bool(u32 value, char *buf)
+{
+	switch (value) {
+	case 0: return snprintf(buf, PAGE_SIZE, "no\n");
+	case 1: return snprintf(buf, PAGE_SIZE, "yes\n");
+	default: return 0;
+	}
+}
+
+static ssize_t cxacru_sysfs_showattr_LINK(u32 value, char *buf)
+{
+	switch (value) {
+	case 1: return snprintf(buf, PAGE_SIZE, "not connected\n");
+	case 2: return snprintf(buf, PAGE_SIZE, "connected\n");
+	case 3: return snprintf(buf, PAGE_SIZE, "lost\n");
+	default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
+	}
+}
+
+static ssize_t cxacru_sysfs_showattr_LINE(u32 value, char *buf)
+{
+	switch (value) {
+	case 0: return snprintf(buf, PAGE_SIZE, "down\n");
+	case 1: return snprintf(buf, PAGE_SIZE, "attempting to activate\n");
+	case 2: return snprintf(buf, PAGE_SIZE, "training\n");
+	case 3: return snprintf(buf, PAGE_SIZE, "channel analysis\n");
+	case 4: return snprintf(buf, PAGE_SIZE, "exchange\n");
+	case 5: return snprintf(buf, PAGE_SIZE, "up\n");
+	case 6: return snprintf(buf, PAGE_SIZE, "waiting\n");
+	case 7: return snprintf(buf, PAGE_SIZE, "initialising\n");
+	default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
+	}
+}
+
+static ssize_t cxacru_sysfs_showattr_MODU(u32 value, char *buf)
+{
+	switch (value) {
+	case 0: return 0;
+	case 1: return snprintf(buf, PAGE_SIZE, "ANSI T1.413\n");
+	case 2: return snprintf(buf, PAGE_SIZE, "ITU-T G.992.1 (G.DMT)\n");
+	case 3: return snprintf(buf, PAGE_SIZE, "ITU-T G.992.2 (G.LITE)\n");
+	default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
+	}
+}
+
+/*
+ * This could use MAC_ADDRESS_HIGH and MAC_ADDRESS_LOW, but since
+ * this data is already in atm_dev there's no point.
+ *
+ * MAC_ADDRESS_HIGH = 0x????5544
+ * MAC_ADDRESS_LOW  = 0x33221100
+ * Where 00-55 are bytes 0-5 of the MAC.
+ */
+static ssize_t cxacru_sysfs_show_mac_address(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usbatm_data *usbatm_instance = usb_get_intfdata(intf);
+	struct atm_dev *atm_dev = usbatm_instance->atm_dev;
+
+	return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+			atm_dev->esi[0], atm_dev->esi[1], atm_dev->esi[2],
+			atm_dev->esi[3], atm_dev->esi[4], atm_dev->esi[5]);
+}
+
+static ssize_t cxacru_sysfs_show_adsl_state(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usbatm_data *usbatm_instance = usb_get_intfdata(intf);
+	struct cxacru_data *instance = usbatm_instance->driver_data;
+	u32 value = instance->card_info[CXINF_LINE_STARTABLE];
+
+	switch (value) {
+	case 0: return snprintf(buf, PAGE_SIZE, "running\n");
+	case 1: return snprintf(buf, PAGE_SIZE, "stopped\n");
+	default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
+	}
+}
+
+static ssize_t cxacru_sysfs_store_adsl_state(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usbatm_data *usbatm_instance = usb_get_intfdata(intf);
+	struct cxacru_data *instance = usbatm_instance->driver_data;
+	int ret;
+	int poll = -1;
+	char str_cmd[8];
+	int len = strlen(buf);
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EACCES;
+
+	ret = sscanf(buf, "%7s", str_cmd);
+	if (ret != 1)
+		return -EINVAL;
+	ret = 0;
+
+	if (mutex_lock_interruptible(&instance->adsl_state_serialize))
+		return -ERESTARTSYS;
+
+	if (!strcmp(str_cmd, "stop") || !strcmp(str_cmd, "restart")) {
+		ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_STOP, NULL, 0, NULL, 0);
+		if (ret < 0) {
+			atm_err(usbatm_instance, "change adsl state:"
+				" CHIP_ADSL_LINE_STOP returned %d\n", ret);
+
+			ret = -EIO;
+		} else {
+			ret = len;
+			poll = CXPOLL_STOPPED;
+		}
+	}
+
+	/* Line status is only updated every second
+	 * and the device appears to only react to
+	 * START/STOP every second too. Wait 1.5s to
+	 * be sure that restart will have an effect. */
+	if (!strcmp(str_cmd, "restart"))
+		msleep(1500);
+
+	if (!strcmp(str_cmd, "start") || !strcmp(str_cmd, "restart")) {
+		ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);
+		if (ret < 0) {
+			atm_err(usbatm_instance, "change adsl state:"
+				" CHIP_ADSL_LINE_START returned %d\n", ret);
+
+			ret = -EIO;
+		} else {
+			ret = len;
+			poll = CXPOLL_POLLING;
+		}
+	}
+
+	if (!strcmp(str_cmd, "poll")) {
+		ret = len;
+		poll = CXPOLL_POLLING;
+	}
+
+	if (ret == 0) {
+		ret = -EINVAL;
+		poll = -1;
+	}
+
+	if (poll == CXPOLL_POLLING) {
+		mutex_lock(&instance->poll_state_serialize);
+		switch (instance->poll_state) {
+		case CXPOLL_STOPPED:
+			/* start polling */
+			instance->poll_state = CXPOLL_POLLING;
+			break;
+
+		case CXPOLL_STOPPING:
+			/* abort stop request */
+			instance->poll_state = CXPOLL_POLLING;
+		case CXPOLL_POLLING:
+		case CXPOLL_SHUTDOWN:
+			/* don't start polling */
+			poll = -1;
+		}
+		mutex_unlock(&instance->poll_state_serialize);
+	} else if (poll == CXPOLL_STOPPED) {
+		mutex_lock(&instance->poll_state_serialize);
+		/* request stop */
+		if (instance->poll_state == CXPOLL_POLLING)
+			instance->poll_state = CXPOLL_STOPPING;
+		mutex_unlock(&instance->poll_state_serialize);
+	}
+
+	mutex_unlock(&instance->adsl_state_serialize);
+
+	if (poll == CXPOLL_POLLING)
+		cxacru_poll_status(&instance->poll_work.work);
+
+	return ret;
+}
+
+/*
+ * All device attributes are included in CXACRU_ALL_FILES
+ * so that the same list can be used multiple times:
+ *     INIT   (define the device attributes)
+ *     CREATE (create all the device files)
+ *     REMOVE (remove all the device files)
+ *
+ * With the last two being defined as needed in the functions
+ * they are used in before calling CXACRU_ALL_FILES()
+ */
+#define CXACRU_ALL_FILES(_action) \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_RATE,           u32,  downstream_rate); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_RATE,             u32,  upstream_rate); \
+CXACRU_ATTR_##_action(CXINF_LINK_STATUS,               LINK, link_status); \
+CXACRU_ATTR_##_action(CXINF_LINE_STATUS,               LINE, line_status); \
+CXACRU__ATTR_##_action(                                      mac_address); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_SNR_MARGIN,       dB,   upstream_snr_margin); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_SNR_MARGIN,     dB,   downstream_snr_margin); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_ATTENUATION,      dB,   upstream_attenuation); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_ATTENUATION,    dB,   downstream_attenuation); \
+CXACRU_ATTR_##_action(CXINF_TRANSMITTER_POWER,         s8,   transmitter_power); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_BITS_PER_FRAME,   u32,  upstream_bits_per_frame); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_BITS_PER_FRAME, u32,  downstream_bits_per_frame); \
+CXACRU_ATTR_##_action(CXINF_STARTUP_ATTEMPTS,          u32,  startup_attempts); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_CRC_ERRORS,       u32,  upstream_crc_errors); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_CRC_ERRORS,     u32,  downstream_crc_errors); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_FEC_ERRORS,       u32,  upstream_fec_errors); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_FEC_ERRORS,     u32,  downstream_fec_errors); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_HEC_ERRORS,       u32,  upstream_hec_errors); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_HEC_ERRORS,     u32,  downstream_hec_errors); \
+CXACRU_ATTR_##_action(CXINF_LINE_STARTABLE,            bool, line_startable); \
+CXACRU_ATTR_##_action(CXINF_MODULATION,                MODU, modulation); \
+CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND,              u32,  adsl_headend); \
+CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND_ENVIRONMENT,  u32,  adsl_headend_environment); \
+CXACRU_ATTR_##_action(CXINF_CONTROLLER_VERSION,        u32,  adsl_controller_version); \
+CXACRU_CMD_##_action(                                        adsl_state);
+
+CXACRU_ALL_FILES(INIT);
+
 /* the following three functions are stolen from drivers/usb/core/message.c */
 static void cxacru_blocking_completion(struct urb *urb)
 {
@@ -347,8 +629,6 @@
 	return 0;
 }
 
-static void cxacru_poll_status(struct work_struct *work);
-
 static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
 		struct atm_dev *atm_dev)
 {
@@ -357,6 +637,7 @@
 	struct atm_dev *atm_dev = usbatm_instance->atm_dev;
 	*/
 	int ret;
+	int start_polling = 1;
 
 	dbg("cxacru_atm_start");
 
@@ -369,14 +650,35 @@
 	}
 
 	/* start ADSL */
+	mutex_lock(&instance->adsl_state_serialize);
 	ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);
 	if (ret < 0) {
 		atm_err(usbatm_instance, "cxacru_atm_start: CHIP_ADSL_LINE_START returned %d\n", ret);
+		mutex_unlock(&instance->adsl_state_serialize);
 		return ret;
 	}
 
 	/* Start status polling */
-	cxacru_poll_status(&instance->poll_work.work);
+	mutex_lock(&instance->poll_state_serialize);
+	switch (instance->poll_state) {
+	case CXPOLL_STOPPED:
+		/* start polling */
+		instance->poll_state = CXPOLL_POLLING;
+		break;
+
+	case CXPOLL_STOPPING:
+		/* abort stop request */
+		instance->poll_state = CXPOLL_POLLING;
+	case CXPOLL_POLLING:
+	case CXPOLL_SHUTDOWN:
+		/* don't start polling */
+		start_polling = 0;
+	}
+	mutex_unlock(&instance->poll_state_serialize);
+	mutex_unlock(&instance->adsl_state_serialize);
+
+	if (start_polling)
+		cxacru_poll_status(&instance->poll_work.work);
 	return 0;
 }
 
@@ -387,14 +689,46 @@
 	u32 buf[CXINF_MAX] = {};
 	struct usbatm_data *usbatm = instance->usbatm;
 	struct atm_dev *atm_dev = usbatm->atm_dev;
+	int keep_polling = 1;
 	int ret;
 
 	ret = cxacru_cm_get_array(instance, CM_REQUEST_CARD_INFO_GET, buf, CXINF_MAX);
 	if (ret < 0) {
-		atm_warn(usbatm, "poll status: error %d\n", ret);
+		if (ret != -ESHUTDOWN)
+			atm_warn(usbatm, "poll status: error %d\n", ret);
+
+		mutex_lock(&instance->poll_state_serialize);
+		if (instance->poll_state != CXPOLL_SHUTDOWN) {
+			instance->poll_state = CXPOLL_STOPPED;
+
+			if (ret != -ESHUTDOWN)
+				atm_warn(usbatm, "polling disabled, set adsl_state"
+						" to 'start' or 'poll' to resume\n");
+		}
+		mutex_unlock(&instance->poll_state_serialize);
 		goto reschedule;
 	}
 
+	memcpy(instance->card_info, buf, sizeof(instance->card_info));
+
+	if (instance->adsl_status != buf[CXINF_LINE_STARTABLE]) {
+		instance->adsl_status = buf[CXINF_LINE_STARTABLE];
+
+		switch (instance->adsl_status) {
+		case 0:
+			atm_printk(KERN_INFO, usbatm, "ADSL state: running\n");
+			break;
+
+		case 1:
+			atm_printk(KERN_INFO, usbatm, "ADSL state: stopped\n");
+			break;
+
+		default:
+			atm_printk(KERN_INFO, usbatm, "Unknown adsl status %02x\n", instance->adsl_status);
+			break;
+		}
+	}
+
 	if (instance->line_status == buf[CXINF_LINE_STATUS])
 		goto reschedule;
 
@@ -449,7 +783,20 @@
 		break;
 	}
 reschedule:
-	schedule_delayed_work(&instance->poll_work, msecs_to_jiffies(POLL_INTERVAL));
+
+	mutex_lock(&instance->poll_state_serialize);
+	if (instance->poll_state == CXPOLL_STOPPING &&
+				instance->adsl_status == 1 && /* stopped */
+				instance->line_status == 0) /* down */
+		instance->poll_state = CXPOLL_STOPPED;
+
+	if (instance->poll_state == CXPOLL_STOPPED)
+		keep_polling = 0;
+	mutex_unlock(&instance->poll_state_serialize);
+
+	if (keep_polling)
+		schedule_delayed_work(&instance->poll_work,
+				round_jiffies_relative(POLL_INTERVAL*HZ));
 }
 
 static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw,
@@ -684,6 +1031,14 @@
 
 	instance->usbatm = usbatm_instance;
 	instance->modem_type = (struct cxacru_modem_type *) id->driver_info;
+	memset(instance->card_info, 0, sizeof(instance->card_info));
+
+	mutex_init(&instance->poll_state_serialize);
+	instance->poll_state = CXPOLL_STOPPED;
+	instance->line_status = -1;
+	instance->adsl_status = -1;
+
+	mutex_init(&instance->adsl_state_serialize);
 
 	instance->rcv_buf = (u8 *) __get_free_page(GFP_KERNEL);
 	if (!instance->rcv_buf) {
@@ -710,6 +1065,13 @@
 		goto fail;
 	}
 
+	#define CXACRU_DEVICE_CREATE_FILE(_name) \
+		ret = device_create_file(&intf->dev, &dev_attr_##_name); \
+		if (unlikely(ret)) \
+			goto fail_sysfs;
+	CXACRU_ALL_FILES(CREATE);
+	#undef CXACRU_DEVICE_CREATE_FILE
+
 	usb_fill_int_urb(instance->rcv_urb,
 			usb_dev, usb_rcvintpipe(usb_dev, CXACRU_EP_CMD),
 			instance->rcv_buf, PAGE_SIZE,
@@ -730,6 +1092,14 @@
 
 	return 0;
 
+ fail_sysfs:
+	dbg("cxacru_bind: device_create_file failed (%d)\n", ret);
+
+	#define CXACRU_DEVICE_REMOVE_FILE(_name) \
+		device_remove_file(&intf->dev, &dev_attr_##_name);
+	CXACRU_ALL_FILES(REMOVE);
+	#undef CXACRU_DEVICE_REVOVE_FILE
+
  fail:
 	free_page((unsigned long) instance->snd_buf);
 	free_page((unsigned long) instance->rcv_buf);
@@ -744,6 +1114,7 @@
 		struct usb_interface *intf)
 {
 	struct cxacru_data *instance = usbatm_instance->driver_data;
+	int is_polling = 1;
 
 	dbg("cxacru_unbind entered");
 
@@ -752,8 +1123,20 @@
 		return;
 	}
 
-	while (!cancel_delayed_work(&instance->poll_work))
-	       flush_scheduled_work();
+	mutex_lock(&instance->poll_state_serialize);
+	BUG_ON(instance->poll_state == CXPOLL_SHUTDOWN);
+
+	/* ensure that status polling continues unless
+	 * it has already stopped */
+	if (instance->poll_state == CXPOLL_STOPPED)
+		is_polling = 0;
+
+	/* stop polling from being stopped or started */
+	instance->poll_state = CXPOLL_SHUTDOWN;
+	mutex_unlock(&instance->poll_state_serialize);
+
+	if (is_polling)
+		cancel_rearming_delayed_work(&instance->poll_work);
 
 	usb_kill_urb(instance->snd_urb);
 	usb_kill_urb(instance->rcv_urb);
@@ -762,6 +1145,12 @@
 
 	free_page((unsigned long) instance->snd_buf);
 	free_page((unsigned long) instance->rcv_buf);
+
+	#define CXACRU_DEVICE_REMOVE_FILE(_name) \
+		device_remove_file(&intf->dev, &dev_attr_##_name);
+	CXACRU_ALL_FILES(REMOVE);
+	#undef CXACRU_DEVICE_REVOVE_FILE
+
 	kfree(instance);
 
 	usbatm_instance->driver_data = NULL;
diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c
index ec63b0e..b3f779f 100644
--- a/drivers/usb/atm/usbatm.c
+++ b/drivers/usb/atm/usbatm.c
@@ -274,6 +274,9 @@
 			(!(channel->usbatm->flags & UDSL_IGNORE_EILSEQ) ||
 			 urb->status != -EILSEQ ))
 	{
+		if (urb->status == -ESHUTDOWN)
+			return;
+
 		if (printk_ratelimit())
 			atm_warn(channel->usbatm, "%s: urb 0x%p failed (%d)!\n",
 				__func__, urb, urb->status);
@@ -343,7 +346,7 @@
 		UDSL_ASSERT(sarb->tail + ATM_CELL_PAYLOAD <= sarb->end);
 	}
 
-	memcpy(sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
+	memcpy(skb_tail_pointer(sarb), source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
 	__skb_put(sarb, ATM_CELL_PAYLOAD);
 
 	if (pti & 1) {
@@ -370,7 +373,7 @@
 			goto out;
 		}
 
-		if (crc32_be(~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) {
+		if (crc32_be(~0, skb_tail_pointer(sarb) - pdu_length, pdu_length) != 0xc704dd7b) {
 			atm_rldbg(instance, "%s: packet failed crc check (vcc: 0x%p)!\n",
 				  __func__, vcc);
 			atomic_inc(&vcc->stats->rx_err);
@@ -396,7 +399,9 @@
 			goto out;	/* atm_charge increments rx_drop */
 		}
 
-		memcpy(skb->data, sarb->tail - pdu_length, length);
+		skb_copy_to_linear_data(skb,
+					skb_tail_pointer(sarb) - pdu_length,
+					length);
 		__skb_put(skb, length);
 
 		vdbg("%s: sending skb 0x%p, skb->len %u, skb->truesize %u",
@@ -484,7 +489,7 @@
 		ptr[4] = 0xec;
 		ptr += ATM_CELL_HEADER;
 
-		memcpy(ptr, skb->data, data_len);
+		skb_copy_from_linear_data(skb, ptr, data_len);
 		ptr += data_len;
 		__skb_pull(skb, data_len);
 
@@ -966,6 +971,14 @@
 	/* temp init ATM device, set to 128kbit */
 	atm_dev->link_rate = 128 * 1000 / 424;
 
+	ret = sysfs_create_link(&atm_dev->class_dev.kobj,
+				&instance->usb_intf->dev.kobj, "device");
+	if (ret) {
+		atm_err(instance, "%s: sysfs_create_link failed: %d\n",
+					__func__, ret);
+		goto fail_sysfs;
+	}
+
 	if (instance->driver->atm_start && ((ret = instance->driver->atm_start(instance, atm_dev)) < 0)) {
 		atm_err(instance, "%s: atm_start failed: %d!\n", __func__, ret);
 		goto fail;
@@ -984,6 +997,8 @@
 	return 0;
 
  fail:
+	sysfs_remove_link(&atm_dev->class_dev.kobj, "device");
+ fail_sysfs:
 	instance->atm_dev = NULL;
 	atm_dev_deregister(atm_dev); /* usbatm_atm_dev_close will eventually be called */
 	return ret;
@@ -1316,8 +1331,10 @@
 	kfree(instance->cell_buf);
 
 	/* ATM finalize */
-	if (instance->atm_dev)
+	if (instance->atm_dev) {
+		sysfs_remove_link(&instance->atm_dev->class_dev.kobj, "device");
 		atm_dev_deregister(instance->atm_dev);
+	}
 
 	usbatm_put_instance(instance);	/* taken in usbatm_usb_probe */
 }
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 31ae661..14de3b1 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -212,7 +212,41 @@
 	}
 	return rc;
 }
+/*
+ * attributes exported through sysfs
+ */
+static ssize_t show_caps
+(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct acm *acm = usb_get_intfdata(intf);
 
+	return sprintf(buf, "%d", acm->ctrl_caps);
+}
+static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
+
+static ssize_t show_country_codes
+(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct acm *acm = usb_get_intfdata(intf);
+
+	memcpy(buf, acm->country_codes, acm->country_code_size);
+	return acm->country_code_size;
+}
+
+static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
+
+static ssize_t show_country_rel_date
+(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct acm *acm = usb_get_intfdata(intf);
+
+	return sprintf(buf, "%d", acm->country_rel_date);
+}
+
+static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
 /*
  * Interrupt handlers for various ACM device responses
  */
@@ -514,6 +548,7 @@
 	usb_free_urb(acm->writeurb);
 	for (i = 0; i < nr; i++)
 		usb_free_urb(acm->ru[i].urb);
+	kfree(acm->country_codes);
 	kfree(acm);
 }
 
@@ -761,6 +796,7 @@
 		      const struct usb_device_id *id)
 {
 	struct usb_cdc_union_desc *union_header = NULL;
+	struct usb_cdc_country_functional_desc *cfd = NULL;
 	char *buffer = intf->altsetting->extra;
 	int buflen = intf->altsetting->extralen;
 	struct usb_interface *control_interface;
@@ -824,8 +860,9 @@
 				union_header = (struct usb_cdc_union_desc *)
 							buffer;
 				break;
-			case USB_CDC_COUNTRY_TYPE: /* maybe somehow export */
-				break; /* for now we ignore it */
+			case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
+				cfd = (struct usb_cdc_country_functional_desc *)buffer;
+				break;
 			case USB_CDC_HEADER_TYPE: /* maybe check version */ 
 				break; /* for now we ignore it */ 
 			case USB_CDC_ACM_TYPE:
@@ -983,6 +1020,34 @@
 		goto alloc_fail7;
 	}
 
+	usb_set_intfdata (intf, acm);
+
+	i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
+	if (i < 0)
+		goto alloc_fail8;
+
+	if (cfd) { /* export the country data */
+		acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
+		if (!acm->country_codes)
+			goto skip_countries;
+		acm->country_code_size = cfd->bLength - 4;
+		memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4);
+		acm->country_rel_date = cfd->iCountryCodeRelDate;
+
+		i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
+		if (i < 0) {
+			kfree(acm->country_codes);
+			goto skip_countries;
+		}
+
+		i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate);
+		if (i < 0) {
+			kfree(acm->country_codes);
+			goto skip_countries;
+		}
+	}
+
+skip_countries:
 	usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
 			 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
 	acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
@@ -1006,9 +1071,10 @@
 	tty_register_device(acm_tty_driver, minor, &control_interface->dev);
 
 	acm_table[minor] = acm;
-	usb_set_intfdata (intf, acm);
-	return 0;
 
+	return 0;
+alloc_fail8:
+	usb_free_urb(acm->writeurb);
 alloc_fail7:
 	for (i = 0; i < num_rx_buf; i++)
 		usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
@@ -1027,7 +1093,7 @@
 
 static void acm_disconnect(struct usb_interface *intf)
 {
-	struct acm *acm = usb_get_intfdata (intf);
+	struct acm *acm = usb_get_intfdata(intf);
 	struct usb_device *usb_dev = interface_to_usbdev(intf);
 	int i;
 
@@ -1041,6 +1107,11 @@
 		mutex_unlock(&open_mutex);
 		return;
 	}
+	if (acm->country_codes){
+		device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
+		device_remove_file(&intf->dev, &dev_attr_iCountryCodeRelDate);
+	}
+	device_remove_file(&intf->dev, &dev_attr_bmCapabilities);
 	acm->dev = NULL;
 	usb_set_intfdata(acm->control, NULL);
 	usb_set_intfdata(acm->data, NULL);
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 1bcaea3..09f7765 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -91,6 +91,9 @@
 	struct urb *ctrlurb, *writeurb;			/* urbs */
 	u8 *ctrl_buffer;				/* buffers of urbs */
 	dma_addr_t ctrl_dma;				/* dma handles of buffers */
+	u8 *country_codes;				/* country codes from device */
+	unsigned int country_code_size;			/* size of this buffer */
+	unsigned int country_rel_date;			/* release date of version */
 	struct acm_wb wb[ACM_NW];
 	struct acm_ru ru[ACM_NR];
 	struct acm_rb rb[ACM_NR];
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index 2fc0f88..f493fb1 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -31,7 +31,30 @@
 	  For the format of the various /proc/bus/usb/ files, please read
 	  <file:Documentation/usb/proc_usb_info.txt>.
 
-	  Most users want to say Y here.
+	  Usbfs files can't handle Access Control Lists (ACL), which are the
+	  default way to grant access to USB devices for untrusted users of a
+	  desktop system. The usbfs functionality is replaced by real
+	  device-nodes managed by udev. These nodes live in /dev/bus/usb and
+	  are used by libusb.
+
+config USB_DEVICE_CLASS
+	bool "USB device class-devices (DEPRECATED)"
+	depends on USB
+	default n
+	---help---
+	  Userspace access to USB devices is granted by device-nodes exported
+	  directly from the usbdev in sysfs. Old versions of the driver
+	  core and udev needed additional class devices to export device nodes.
+
+	  These additional devices are difficult to handle in userspace, if
+	  information about USB interfaces must be available. One device contains
+	  the device node, the other device contains the interface data. Both
+	  devices are at the same level in sysfs (siblings) and one can't access
+	  the other. The device node created directly by the usbdev is the parent
+	  device of the interface and therefore easily accessible from the interface
+	  event.
+
+	  This option provides backward compatibility if needed.
 
 config USB_DYNAMIC_MINORS
 	bool "Dynamic USB minor allocation (EXPERIMENTAL)"
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index aefc798..6753ca0 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -246,7 +246,6 @@
 
 	if (start > end)
 		return start;
-	down_read(&usb_bus_type.subsys.rwsem);
 	if (iface) {
 		driver_name = (iface->dev.driver
 				? iface->dev.driver->name
@@ -263,7 +262,6 @@
 			 desc->bInterfaceSubClass,
 			 desc->bInterfaceProtocol,
 			 driver_name);
-	up_read(&usb_bus_type.subsys.rwsem);
 	return start;
 }
 
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 36e7a84..927a181 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -57,7 +57,6 @@
 
 #define USB_MAXBUS			64
 #define USB_DEVICE_MAX			USB_MAXBUS * 128
-static struct class *usb_device_class;
 
 /* Mutual exclusion for removal, open, and release */
 DEFINE_MUTEX(usbfs_mutex);
@@ -421,14 +420,11 @@
 	if (test_bit(ifnum, &ps->ifclaimed))
 		return 0;
 
-	/* lock against other changes to driver bindings */
-	down_write(&usb_bus_type.subsys.rwsem);
 	intf = usb_ifnum_to_if(dev, ifnum);
 	if (!intf)
 		err = -ENOENT;
 	else
 		err = usb_driver_claim_interface(&usbfs_driver, intf, ps);
-	up_write(&usb_bus_type.subsys.rwsem);
 	if (err == 0)
 		set_bit(ifnum, &ps->ifclaimed);
 	return err;
@@ -444,8 +440,6 @@
 	if (ifnum >= 8*sizeof(ps->ifclaimed))
 		return err;
 	dev = ps->dev;
-	/* lock against other changes to driver bindings */
-	down_write(&usb_bus_type.subsys.rwsem);
 	intf = usb_ifnum_to_if(dev, ifnum);
 	if (!intf)
 		err = -ENOENT;
@@ -453,7 +447,6 @@
 		usb_driver_release_interface(&usbfs_driver, intf);
 		err = 0;
 	}
-	up_write(&usb_bus_type.subsys.rwsem);
 	return err;
 }
 
@@ -520,22 +513,25 @@
 	return ret;
 }
 
-static struct usb_device *usbdev_lookup_minor(int minor)
+static int __match_minor(struct device *dev, void *data)
 {
-	struct device *device;
-	struct usb_device *udev = NULL;
+	int minor = *((int *)data);
 
-	down(&usb_device_class->sem);
-	list_for_each_entry(device, &usb_device_class->devices, node) {
-		if (device->devt == MKDEV(USB_DEVICE_MAJOR, minor)) {
-			udev = device->platform_data;
-			break;
-		}
-	}
-	up(&usb_device_class->sem);
+	if (dev->devt == MKDEV(USB_DEVICE_MAJOR, minor))
+		return 1;
+	return 0;
+}
 
-	return udev;
-};
+static struct usb_device *usbdev_lookup_by_minor(int minor)
+{
+	struct device *dev;
+
+	dev = bus_find_device(&usb_bus_type, NULL, &minor, __match_minor);
+	if (!dev)
+		return NULL;
+	put_device(dev);
+	return container_of(dev, struct usb_device, dev);
+}
 
 /*
  * file operations
@@ -554,11 +550,14 @@
 		goto out;
 
 	ret = -ENOENT;
-	/* check if we are called from a real node or usbfs */
+	/* usbdev device-node */
 	if (imajor(inode) == USB_DEVICE_MAJOR)
-		dev = usbdev_lookup_minor(iminor(inode));
+		dev = usbdev_lookup_by_minor(iminor(inode));
+#ifdef CONFIG_USB_DEVICEFS
+	/* procfs file */
 	if (!dev)
 		dev = inode->i_private;
+#endif
 	if (!dev)
 		goto out;
 	ret = usb_autoresume_device(dev);
@@ -581,7 +580,7 @@
 	ps->disccontext = NULL;
 	ps->ifclaimed = 0;
 	security_task_getsecid(current, &ps->secid);
-	wmb();
+	smp_wmb();
 	list_add_tail(&ps->list, &dev->filelist);
 	file->private_data = ps;
  out:
@@ -813,7 +812,6 @@
 
 	if (copy_from_user(&gd, arg, sizeof(gd)))
 		return -EFAULT;
-	down_read(&usb_bus_type.subsys.rwsem);
 	intf = usb_ifnum_to_if(ps->dev, gd.interface);
 	if (!intf || !intf->dev.driver)
 		ret = -ENODATA;
@@ -822,7 +820,6 @@
 				sizeof(gd.driver));
 		ret = (copy_to_user(arg, &gd, sizeof(gd)) ? -EFAULT : 0);
 	}
-	up_read(&usb_bus_type.subsys.rwsem);
 	return ret;
 }
 
@@ -1351,15 +1348,12 @@
 
 	/* disconnect kernel driver from interface */
 	case USBDEVFS_DISCONNECT:
-
-		down_write(&usb_bus_type.subsys.rwsem);
 		if (intf->dev.driver) {
 			driver = to_usb_driver(intf->dev.driver);
 			dev_dbg (&intf->dev, "disconnect by usbfs\n");
 			usb_driver_release_interface(driver, intf);
 		} else
 			retval = -ENODATA;
-		up_write(&usb_bus_type.subsys.rwsem);
 		break;
 
 	/* let kernel drivers try to (re)bind to the interface */
@@ -1371,7 +1365,6 @@
 
 	/* talk directly to the interface's driver */
 	default:
-		down_read(&usb_bus_type.subsys.rwsem);
 		if (intf->dev.driver)
 			driver = to_usb_driver(intf->dev.driver);
 		if (driver == NULL || driver->ioctl == NULL) {
@@ -1381,7 +1374,6 @@
 			if (retval == -ENOIOCTLCMD)
 				retval = -ENOTTY;
 		}
-		up_read(&usb_bus_type.subsys.rwsem);
 	}
 
 	/* cleanup and return */
@@ -1583,7 +1575,7 @@
 	return mask;
 }
 
-const struct file_operations usbfs_device_file_operations = {
+const struct file_operations usbdev_file_operations = {
 	.llseek =	usbdev_lseek,
 	.read =		usbdev_read,
 	.poll =		usbdev_poll,
@@ -1592,50 +1584,53 @@
 	.release =	usbdev_release,
 };
 
-static int usbdev_add(struct usb_device *dev)
+#ifdef CONFIG_USB_DEVICE_CLASS
+static struct class *usb_classdev_class;
+
+static int usb_classdev_add(struct usb_device *dev)
 {
 	int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1);
 
-	dev->usbfs_dev = device_create(usb_device_class, &dev->dev,
+	dev->usb_classdev = device_create(usb_classdev_class, &dev->dev,
 				MKDEV(USB_DEVICE_MAJOR, minor),
 				"usbdev%d.%d", dev->bus->busnum, dev->devnum);
-	if (IS_ERR(dev->usbfs_dev))
-		return PTR_ERR(dev->usbfs_dev);
+	if (IS_ERR(dev->usb_classdev))
+		return PTR_ERR(dev->usb_classdev);
 
-	dev->usbfs_dev->platform_data = dev;
 	return 0;
 }
 
-static void usbdev_remove(struct usb_device *dev)
+static void usb_classdev_remove(struct usb_device *dev)
 {
-	device_unregister(dev->usbfs_dev);
+	device_unregister(dev->usb_classdev);
 }
 
-static int usbdev_notify(struct notifier_block *self, unsigned long action,
-			 void *dev)
+static int usb_classdev_notify(struct notifier_block *self,
+			       unsigned long action, void *dev)
 {
 	switch (action) {
 	case USB_DEVICE_ADD:
-		if (usbdev_add(dev))
+		if (usb_classdev_add(dev))
 			return NOTIFY_BAD;
 		break;
 	case USB_DEVICE_REMOVE:
-		usbdev_remove(dev);
+		usb_classdev_remove(dev);
 		break;
 	}
 	return NOTIFY_OK;
 }
 
 static struct notifier_block usbdev_nb = {
-	.notifier_call = 	usbdev_notify,
+	.notifier_call = 	usb_classdev_notify,
 };
+#endif
 
 static struct cdev usb_device_cdev = {
 	.kobj   = {.name = "usb_device", },
 	.owner  = THIS_MODULE,
 };
 
-int __init usbdev_init(void)
+int __init usb_devio_init(void)
 {
 	int retval;
 
@@ -1645,38 +1640,38 @@
 		err("unable to register minors for usb_device");
 		goto out;
 	}
-	cdev_init(&usb_device_cdev, &usbfs_device_file_operations);
+	cdev_init(&usb_device_cdev, &usbdev_file_operations);
 	retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);
 	if (retval) {
 		err("unable to get usb_device major %d", USB_DEVICE_MAJOR);
 		goto error_cdev;
 	}
-	usb_device_class = class_create(THIS_MODULE, "usb_device");
-	if (IS_ERR(usb_device_class)) {
+#ifdef CONFIG_USB_DEVICE_CLASS
+	usb_classdev_class = class_create(THIS_MODULE, "usb_device");
+	if (IS_ERR(usb_classdev_class)) {
 		err("unable to register usb_device class");
-		retval = PTR_ERR(usb_device_class);
-		goto error_class;
+		retval = PTR_ERR(usb_classdev_class);
+		cdev_del(&usb_device_cdev);
+		usb_classdev_class = NULL;
+		goto out;
 	}
 
 	usb_register_notify(&usbdev_nb);
-
+#endif
 out:
 	return retval;
 
-error_class:
-	usb_device_class = NULL;
-	cdev_del(&usb_device_cdev);
-
 error_cdev:
 	unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
 	goto out;
 }
 
-void usbdev_cleanup(void)
+void usb_devio_cleanup(void)
 {
+#ifdef CONFIG_USB_DEVICE_CLASS
 	usb_unregister_notify(&usbdev_nb);
-	class_destroy(usb_device_class);
+	class_destroy(usb_classdev_class);
+#endif
 	cdev_del(&usb_device_cdev);
 	unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
 }
-
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 9e3e943..b9f7f90 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -287,9 +287,9 @@
  * way to bind to an interface is to return the private data from
  * the driver's probe() method.
  *
- * Callers must own the device lock and the driver model's usb_bus_type.subsys
- * writelock.  So driver probe() entries don't need extra locking,
- * but other call contexts may need to explicitly claim those locks.
+ * Callers must own the device lock, so driver probe() entries don't need
+ * extra locking, but other call contexts may need to explicitly claim that
+ * lock.
  */
 int usb_driver_claim_interface(struct usb_driver *driver,
 				struct usb_interface *iface, void* priv)
@@ -330,9 +330,9 @@
  * also causes the driver disconnect() method to be called.
  *
  * This call is synchronous, and may not be used in an interrupt context.
- * Callers must own the device lock and the driver model's usb_bus_type.subsys
- * writelock.  So driver disconnect() entries don't need extra locking,
- * but other call contexts may need to explicitly claim those locks.
+ * Callers must own the device lock, so driver disconnect() entries don't
+ * need extra locking, but other call contexts may need to explicitly claim
+ * that lock.
  */
 void usb_driver_release_interface(struct usb_driver *driver,
 					struct usb_interface *iface)
@@ -574,23 +574,10 @@
 }
 
 #ifdef	CONFIG_HOTPLUG
-
-/*
- * This sends an uevent to userspace, typically helping to load driver
- * or other modules, configure the device, and more.  Drivers can provide
- * a MODULE_DEVICE_TABLE to help with module loading subtasks.
- *
- * We're called either from khubd (the typical case) or from root hub
- * (init, kapmd, modprobe, rmmod, etc), but the agents need to handle
- * delays in event delivery.  Use sysfs (and DEVPATH) to make sure the
- * device (and this configuration!) are still present.
- */
 static int usb_uevent(struct device *dev, char **envp, int num_envp,
 		      char *buffer, int buffer_size)
 {
-	struct usb_interface *intf;
 	struct usb_device *usb_dev;
-	struct usb_host_interface *alt;
 	int i = 0;
 	int length = 0;
 
@@ -600,13 +587,11 @@
 	/* driver is often null here; dev_dbg() would oops */
 	pr_debug ("usb %s: uevent\n", dev->bus_id);
 
-	if (is_usb_device(dev)) {
+	if (is_usb_device(dev))
 		usb_dev = to_usb_device(dev);
-		alt = NULL;
-	} else {
-		intf = to_usb_interface(dev);
+	else {
+		struct usb_interface *intf = to_usb_interface(dev);
 		usb_dev = interface_to_usbdev(intf);
-		alt = intf->cur_altsetting;
 	}
 
 	if (usb_dev->devnum < 0) {
@@ -621,9 +606,7 @@
 #ifdef	CONFIG_USB_DEVICEFS
 	/* If this is available, userspace programs can directly read
 	 * all the device descriptors we don't tell them about.  Or
-	 * even act as usermode drivers.
-	 *
-	 * FIXME reduce hardwired intelligence here
+	 * act as usermode drivers.
 	 */
 	if (add_uevent_var(envp, num_envp, &i,
 			   buffer, buffer_size, &length,
@@ -650,44 +633,29 @@
 			   usb_dev->descriptor.bDeviceProtocol))
 		return -ENOMEM;
 
-	if (!is_usb_device(dev)) {
-
-		if (add_uevent_var(envp, num_envp, &i,
+	if (add_uevent_var(envp, num_envp, &i,
 			   buffer, buffer_size, &length,
-			   "INTERFACE=%d/%d/%d",
-			   alt->desc.bInterfaceClass,
-			   alt->desc.bInterfaceSubClass,
-			   alt->desc.bInterfaceProtocol))
-			return -ENOMEM;
+			   "BUSNUM=%03d",
+			   usb_dev->bus->busnum))
+		return -ENOMEM;
 
-		if (add_uevent_var(envp, num_envp, &i,
+	if (add_uevent_var(envp, num_envp, &i,
 			   buffer, buffer_size, &length,
-			   "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
-			   le16_to_cpu(usb_dev->descriptor.idVendor),
-			   le16_to_cpu(usb_dev->descriptor.idProduct),
-			   le16_to_cpu(usb_dev->descriptor.bcdDevice),
-			   usb_dev->descriptor.bDeviceClass,
-			   usb_dev->descriptor.bDeviceSubClass,
-			   usb_dev->descriptor.bDeviceProtocol,
-			   alt->desc.bInterfaceClass,
-			   alt->desc.bInterfaceSubClass,
-			   alt->desc.bInterfaceProtocol))
-			return -ENOMEM;
-	}
+			   "DEVNUM=%03d",
+			   usb_dev->devnum))
+		return -ENOMEM;
 
 	envp[i] = NULL;
-
 	return 0;
 }
 
 #else
 
 static int usb_uevent(struct device *dev, char **envp,
-			int num_envp, char *buffer, int buffer_size)
+		      int num_envp, char *buffer, int buffer_size)
 {
 	return -ENODEV;
 }
-
 #endif	/* CONFIG_HOTPLUG */
 
 /**
@@ -872,8 +840,10 @@
 
 done:
 	// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
-	if (status == 0)
+	if (status == 0) {
+		udev->autoresume_disabled = 0;
 		udev->dev.power.power_state.event = PM_EVENT_ON;
+	}
 	return status;
 }
 
@@ -962,6 +932,7 @@
 {
 	int			i;
 	struct usb_interface	*intf;
+	unsigned long		suspend_time;
 
 	/* For autosuspend, fail fast if anything is in use or autosuspend
 	 * is disabled.  Also fail if any interfaces require remote wakeup
@@ -970,9 +941,10 @@
 	udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
 	if (udev->pm_usage_cnt > 0)
 		return -EBUSY;
-	if (!udev->autosuspend_delay)
+	if (udev->autosuspend_delay < 0 || udev->autosuspend_disabled)
 		return -EPERM;
 
+	suspend_time = udev->last_busy + udev->autosuspend_delay;
 	if (udev->actconfig) {
 		for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
 			intf = udev->actconfig->interface[i];
@@ -988,6 +960,24 @@
 			}
 		}
 	}
+
+	/* If everything is okay but the device hasn't been idle for long
+	 * enough, queue a delayed autosuspend request.
+	 */
+	if (time_after(suspend_time, jiffies)) {
+		if (!timer_pending(&udev->autosuspend.timer)) {
+
+			/* The value of jiffies may change between the
+			 * time_after() comparison above and the subtraction
+			 * below.  That's okay; the system behaves sanely
+			 * when a timer is registered for the present moment
+			 * or for the past.
+			 */
+			queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
+					suspend_time - jiffies);
+			}
+		return -EAGAIN;
+	}
 	return 0;
 }
 
@@ -1033,26 +1023,25 @@
  *
  * This routine can run only in process context.
  */
-int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
+static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
 {
 	int			status = 0;
 	int			i = 0;
 	struct usb_interface	*intf;
 	struct usb_device	*parent = udev->parent;
 
-	cancel_delayed_work(&udev->autosuspend);
-	if (udev->state == USB_STATE_NOTATTACHED)
-		return 0;
-	if (udev->state == USB_STATE_SUSPENDED)
-		return 0;
+	if (udev->state == USB_STATE_NOTATTACHED ||
+			udev->state == USB_STATE_SUSPENDED)
+		goto done;
 
 	udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
 
 	if (udev->auto_pm) {
 		status = autosuspend_check(udev);
 		if (status < 0)
-			return status;
+			goto done;
 	}
+	cancel_delayed_work(&udev->autosuspend);
 
 	/* Suspend all the interfaces and then udev itself */
 	if (udev->actconfig) {
@@ -1077,6 +1066,7 @@
 	} else if (parent)
 		usb_autosuspend_device(parent);
 
+ done:
 	// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
 	return status;
 }
@@ -1109,7 +1099,7 @@
  *
  * This routine can run only in process context.
  */
-int usb_resume_both(struct usb_device *udev)
+static int usb_resume_both(struct usb_device *udev)
 {
 	int			status = 0;
 	int			i;
@@ -1117,11 +1107,17 @@
 	struct usb_device	*parent = udev->parent;
 
 	cancel_delayed_work(&udev->autosuspend);
-	if (udev->state == USB_STATE_NOTATTACHED)
-		return -ENODEV;
+	if (udev->state == USB_STATE_NOTATTACHED) {
+		status = -ENODEV;
+		goto done;
+	}
 
 	/* Propagate the resume up the tree, if necessary */
 	if (udev->state == USB_STATE_SUSPENDED) {
+		if (udev->auto_pm && udev->autoresume_disabled) {
+			status = -EPERM;
+			goto done;
+		}
 		if (parent) {
 			status = usb_autoresume_device(parent);
 			if (status == 0) {
@@ -1167,6 +1163,7 @@
 		}
 	}
 
+ done:
 	// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
 	return status;
 }
@@ -1181,20 +1178,34 @@
 	int	status = 0;
 
 	usb_pm_lock(udev);
+	udev->auto_pm = 1;
 	udev->pm_usage_cnt += inc_usage_cnt;
 	WARN_ON(udev->pm_usage_cnt < 0);
 	if (inc_usage_cnt >= 0 && udev->pm_usage_cnt > 0) {
-		udev->auto_pm = 1;
-		status = usb_resume_both(udev);
+		if (udev->state == USB_STATE_SUSPENDED)
+			status = usb_resume_both(udev);
 		if (status != 0)
 			udev->pm_usage_cnt -= inc_usage_cnt;
-	} else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0)
-		queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
-				udev->autosuspend_delay);
+		else if (inc_usage_cnt)
+			udev->last_busy = jiffies;
+	} else if (inc_usage_cnt <= 0 && udev->pm_usage_cnt <= 0) {
+		if (inc_usage_cnt)
+			udev->last_busy = jiffies;
+		status = usb_suspend_both(udev, PMSG_SUSPEND);
+	}
 	usb_pm_unlock(udev);
 	return status;
 }
 
+/* usb_autosuspend_work - callback routine to autosuspend a USB device */
+void usb_autosuspend_work(struct work_struct *work)
+{
+	struct usb_device *udev =
+		container_of(work, struct usb_device, autosuspend.work);
+
+	usb_autopm_do_device(udev, 0);
+}
+
 /**
  * usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces
  * @udev: the usb_device to autosuspend
@@ -1286,15 +1297,20 @@
 	if (intf->condition == USB_INTERFACE_UNBOUND)
 		status = -ENODEV;
 	else {
+		udev->auto_pm = 1;
 		intf->pm_usage_cnt += inc_usage_cnt;
 		if (inc_usage_cnt >= 0 && intf->pm_usage_cnt > 0) {
-			udev->auto_pm = 1;
-			status = usb_resume_both(udev);
+			if (udev->state == USB_STATE_SUSPENDED)
+				status = usb_resume_both(udev);
 			if (status != 0)
 				intf->pm_usage_cnt -= inc_usage_cnt;
-		} else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0)
-			queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
-					udev->autosuspend_delay);
+			else if (inc_usage_cnt)
+				udev->last_busy = jiffies;
+		} else if (inc_usage_cnt <= 0 && intf->pm_usage_cnt <= 0) {
+			if (inc_usage_cnt)
+				udev->last_busy = jiffies;
+			status = usb_suspend_both(udev, PMSG_SUSPEND);
+		}
 	}
 	usb_pm_unlock(udev);
 	return status;
@@ -1353,11 +1369,14 @@
  * or @intf is unbound.  A typical example would be a character-device
  * driver when its device file is opened.
  *
- * The routine increments @intf's usage counter.  So long as the counter
- * is greater than 0, autosuspend will not be allowed for @intf or its
- * usb_device.  When the driver is finished using @intf it should call
- * usb_autopm_put_interface() to decrement the usage counter and queue
- * a delayed autosuspend request (if the counter is <= 0).
+ *
+ * The routine increments @intf's usage counter.  (However if the
+ * autoresume fails then the counter is re-decremented.)  So long as the
+ * counter is greater than 0, autosuspend will not be allowed for @intf
+ * or its usb_device.  When the driver is finished using @intf it should
+ * call usb_autopm_put_interface() to decrement the usage counter and
+ * queue a delayed autosuspend request (if the counter is <= 0).
+ *
  *
  * Note that @intf->pm_usage_cnt is owned by the interface driver.  The
  * core will not change its value other than the increment and decrement
@@ -1405,50 +1424,96 @@
 }
 EXPORT_SYMBOL_GPL(usb_autopm_set_interface);
 
+#else
+
+void usb_autosuspend_work(struct work_struct *work)
+{}
+
 #endif /* CONFIG_USB_SUSPEND */
 
-static int usb_suspend(struct device *dev, pm_message_t message)
+/**
+ * usb_external_suspend_device - external suspend of a USB device and its interfaces
+ * @udev: the usb_device to suspend
+ * @msg: Power Management message describing this state transition
+ *
+ * This routine handles external suspend requests: ones not generated
+ * internally by a USB driver (autosuspend) but rather coming from the user
+ * (via sysfs) or the PM core (system sleep).  The suspend will be carried
+ * out regardless of @udev's usage counter or those of its interfaces,
+ * and regardless of whether or not remote wakeup is enabled.  Of course,
+ * interface drivers still have the option of failing the suspend (if
+ * there are unsuspended children, for example).
+ *
+ * The caller must hold @udev's device lock.
+ */
+int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg)
 {
 	int	status;
 
-	if (is_usb_device(dev)) {
-		struct usb_device *udev = to_usb_device(dev);
-
-		usb_pm_lock(udev);
-		udev->auto_pm = 0;
-		status = usb_suspend_both(udev, message);
-		usb_pm_unlock(udev);
-	} else
-		status = 0;
+	usb_pm_lock(udev);
+	udev->auto_pm = 0;
+	status = usb_suspend_both(udev, msg);
+	usb_pm_unlock(udev);
 	return status;
 }
 
+/**
+ * usb_external_resume_device - external resume of a USB device and its interfaces
+ * @udev: the usb_device to resume
+ *
+ * This routine handles external resume requests: ones not generated
+ * internally by a USB driver (autoresume) but rather coming from the user
+ * (via sysfs), the PM core (system resume), or the device itself (remote
+ * wakeup).  @udev's usage counter is unaffected.
+ *
+ * The caller must hold @udev's device lock.
+ */
+int usb_external_resume_device(struct usb_device *udev)
+{
+	int	status;
+
+	usb_pm_lock(udev);
+	udev->auto_pm = 0;
+	status = usb_resume_both(udev);
+	usb_pm_unlock(udev);
+
+	/* Now that the device is awake, we can start trying to autosuspend
+	 * it again. */
+	if (status == 0)
+		usb_try_autosuspend_device(udev);
+	return status;
+}
+
+static int usb_suspend(struct device *dev, pm_message_t message)
+{
+	if (!is_usb_device(dev))	/* Ignore PM for interfaces */
+		return 0;
+	return usb_external_suspend_device(to_usb_device(dev), message);
+}
+
 static int usb_resume(struct device *dev)
 {
-	int	status;
+	struct usb_device	*udev;
 
-	if (is_usb_device(dev)) {
-		struct usb_device *udev = to_usb_device(dev);
-
-		usb_pm_lock(udev);
-		udev->auto_pm = 0;
-		status = usb_resume_both(udev);
-		usb_pm_unlock(udev);
-
-		/* Rebind drivers that had no suspend method? */
-	} else
-		status = 0;
-	return status;
+	if (!is_usb_device(dev))	/* Ignore PM for interfaces */
+		return 0;
+	udev = to_usb_device(dev);
+	if (udev->autoresume_disabled)
+		return -EPERM;
+	return usb_external_resume_device(udev);
 }
 
+#else
+
+#define usb_suspend	NULL
+#define usb_resume	NULL
+
 #endif /* CONFIG_PM */
 
 struct bus_type usb_bus_type = {
 	.name =		"usb",
 	.match =	usb_device_match,
 	.uevent =	usb_uevent,
-#ifdef CONFIG_PM
 	.suspend =	usb_suspend,
 	.resume =	usb_resume,
-#endif
 };
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index b26c19e..40cf882 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -37,6 +37,7 @@
 #include <asm/irq.h>
 #include <asm/byteorder.h>
 #include <linux/platform_device.h>
+#include <linux/workqueue.h>
 
 #include <linux/usb.h>
 
@@ -544,6 +545,8 @@
 	unsigned long	flags;
 	char		buffer[4];	/* Any root hubs with > 31 ports? */
 
+	if (unlikely(!hcd->rh_registered))
+		return;
 	if (!hcd->uses_new_polling && !hcd->status_urb)
 		return;
 
@@ -1296,14 +1299,26 @@
 	return status;
 }
 
+/* Workqueue routine for root-hub remote wakeup */
+static void hcd_resume_work(struct work_struct *work)
+{
+	struct usb_hcd *hcd = container_of(work, struct usb_hcd, wakeup_work);
+	struct usb_device *udev = hcd->self.root_hub;
+
+	usb_lock_device(udev);
+	usb_mark_last_busy(udev);
+	usb_external_resume_device(udev);
+	usb_unlock_device(udev);
+}
+
 /**
  * usb_hcd_resume_root_hub - called by HCD to resume its root hub 
  * @hcd: host controller for this root hub
  *
  * The USB host controller calls this function when its root hub is
  * suspended (with the remote wakeup feature enabled) and a remote
- * wakeup request is received.  It queues a request for khubd to
- * resume the root hub (that is, manage its downstream ports again).
+ * wakeup request is received.  The routine submits a workqueue request
+ * to resume the root hub (that is, manage its downstream ports again).
  */
 void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
 {
@@ -1311,7 +1326,7 @@
 
 	spin_lock_irqsave (&hcd_root_hub_lock, flags);
 	if (hcd->rh_registered)
-		usb_resume_root_hub (hcd->self.root_hub);
+		queue_work(ksuspend_usb_wq, &hcd->wakeup_work);
 	spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
 }
 EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);
@@ -1500,6 +1515,9 @@
 	init_timer(&hcd->rh_timer);
 	hcd->rh_timer.function = rh_timer_func;
 	hcd->rh_timer.data = (unsigned long) hcd;
+#ifdef CONFIG_PM
+	INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
+#endif
 
 	hcd->driver = driver;
 	hcd->product_desc = (driver->product_desc) ? driver->product_desc :
@@ -1666,16 +1684,20 @@
 	hcd->rh_registered = 0;
 	spin_unlock_irq (&hcd_root_hub_lock);
 
+#ifdef CONFIG_PM
+	flush_workqueue(ksuspend_usb_wq);
+#endif
+
 	mutex_lock(&usb_bus_list_lock);
 	usb_disconnect(&hcd->self.root_hub);
 	mutex_unlock(&usb_bus_list_lock);
 
-	hcd->poll_rh = 0;
-	del_timer_sync(&hcd->rh_timer);
-
 	hcd->driver->stop(hcd);
 	hcd->state = HC_STATE_HALT;
 
+	hcd->poll_rh = 0;
+	del_timer_sync(&hcd->rh_timer);
+
 	if (hcd->irq >= 0)
 		free_irq(hcd->irq, hcd);
 	usb_deregister_bus(&hcd->self);
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 2a269ca..ef50fa4 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -68,6 +68,9 @@
 
 	struct timer_list	rh_timer;	/* drives root-hub polling */
 	struct urb		*status_urb;	/* the current status urb */
+#ifdef CONFIG_PM
+	struct work_struct	wakeup_work;	/* for remote wakeup */
+#endif
 
 	/*
 	 * hardware info/state
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index b89a98e..bde29ab 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -119,8 +119,7 @@
 		"first one fails");
 
 
-#ifdef	DEBUG
-static inline char *portspeed (int portstatus)
+static inline char *portspeed(int portstatus)
 {
 	if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED))
     		return "480 Mb/s";
@@ -129,7 +128,6 @@
 	else
 		return "12 Mb/s";
 }
-#endif
 
 /* Note that hdev or one of its children must be locked! */
 static inline struct usb_hub *hdev_to_hub(struct usb_device *hdev)
@@ -1369,11 +1367,15 @@
 	}
 #endif
 
+	/* export the usbdev device-node for libusb */
+	udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
+			(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
+
 	/* Register the device.  The device driver is responsible
-	 * for adding the device files to usbfs and sysfs and for
-	 * configuring the device.
+	 * for adding the device files to sysfs and for configuring
+	 * the device.
 	 */
-	err = device_add (&udev->dev);
+	err = device_add(&udev->dev);
 	if (err) {
 		dev_err(&udev->dev, "can't device_add, error %d\n", err);
 		goto fail;
@@ -1857,12 +1859,8 @@
 	usb_lock_device(udev);
 	if (udev->state == USB_STATE_SUSPENDED) {
 		dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
-		status = usb_autoresume_device(udev);
-
-		/* Give the interface drivers a chance to do something,
-		 * then autosuspend the device again. */
-		if (status == 0)
-			usb_autosuspend_device(udev);
+		usb_mark_last_busy(udev);
+		status = usb_external_resume_device(udev);
 	}
 	usb_unlock_device(udev);
 	return status;
@@ -1986,13 +1984,6 @@
 #define hub_resume NULL
 #endif
 
-void usb_resume_root_hub(struct usb_device *hdev)
-{
-	struct usb_hub *hub = hdev_to_hub(hdev);
-
-	kick_khubd(hub);
-}
-
 
 /* USB 2.0 spec, 7.1.7.3 / fig 7-29:
  *
diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c
index 11dad22..cddfc62 100644
--- a/drivers/usb/core/inode.c
+++ b/drivers/usb/core/inode.c
@@ -662,7 +662,7 @@
 	sprintf (name, "%03d", dev->devnum);
 	dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG,
 					    dev->bus->usbfs_dentry, dev,
-					    &usbfs_device_file_operations,
+					    &usbdev_file_operations,
 					    devuid, devgid);
 	if (dev->usbfs_dentry == NULL) {
 		err ("error creating usbfs device entry");
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 217a3d6..b743478 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -412,10 +412,24 @@
 		io->urbs [i]->status = -EINPROGRESS;
 		io->urbs [i]->actual_length = 0;
 
+		/*
+		 * Some systems need to revert to PIO when DMA is temporarily
+		 * unavailable.  For their sakes, both transfer_buffer and
+		 * transfer_dma are set when possible.  However this can only
+		 * work on systems without HIGHMEM, since DMA buffers located
+		 * in high memory are not directly addressable by the CPU for
+		 * PIO ... so when HIGHMEM is in use, transfer_buffer is NULL
+		 * to prevent stale pointers and to help spot bugs.
+		 */
 		if (dma) {
-			/* hc may use _only_ transfer_dma */
 			io->urbs [i]->transfer_dma = sg_dma_address (sg + i);
 			len = sg_dma_len (sg + i);
+#ifdef CONFIG_HIGHMEM
+			io->urbs[i]->transfer_buffer = NULL;
+#else
+			io->urbs[i]->transfer_buffer =
+				page_address(sg[i].page) + sg[i].offset;
+#endif
 		} else {
 			/* hc may use _only_ transfer_buffer */
 			io->urbs [i]->transfer_buffer =
@@ -1305,7 +1319,7 @@
 	return 0;
 }
 
-static void release_interface(struct device *dev)
+void usb_release_interface(struct device *dev)
 {
 	struct usb_interface *intf = to_usb_interface(dev);
 	struct usb_interface_cache *intfc =
@@ -1315,6 +1329,67 @@
 	kfree(intf);
 }
 
+#ifdef	CONFIG_HOTPLUG
+static int usb_if_uevent(struct device *dev, char **envp, int num_envp,
+		 char *buffer, int buffer_size)
+{
+	struct usb_device *usb_dev;
+	struct usb_interface *intf;
+	struct usb_host_interface *alt;
+	int i = 0;
+	int length = 0;
+
+	if (!dev)
+		return -ENODEV;
+
+	/* driver is often null here; dev_dbg() would oops */
+	pr_debug ("usb %s: uevent\n", dev->bus_id);
+
+	intf = to_usb_interface(dev);
+	usb_dev = interface_to_usbdev(intf);
+	alt = intf->cur_altsetting;
+
+	if (add_uevent_var(envp, num_envp, &i,
+		   buffer, buffer_size, &length,
+		   "INTERFACE=%d/%d/%d",
+		   alt->desc.bInterfaceClass,
+		   alt->desc.bInterfaceSubClass,
+		   alt->desc.bInterfaceProtocol))
+		return -ENOMEM;
+
+	if (add_uevent_var(envp, num_envp, &i,
+		   buffer, buffer_size, &length,
+		   "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
+		   le16_to_cpu(usb_dev->descriptor.idVendor),
+		   le16_to_cpu(usb_dev->descriptor.idProduct),
+		   le16_to_cpu(usb_dev->descriptor.bcdDevice),
+		   usb_dev->descriptor.bDeviceClass,
+		   usb_dev->descriptor.bDeviceSubClass,
+		   usb_dev->descriptor.bDeviceProtocol,
+		   alt->desc.bInterfaceClass,
+		   alt->desc.bInterfaceSubClass,
+		   alt->desc.bInterfaceProtocol))
+		return -ENOMEM;
+
+	envp[i] = NULL;
+	return 0;
+}
+
+#else
+
+static int usb_if_uevent(struct device *dev, char **envp,
+			 int num_envp, char *buffer, int buffer_size)
+{
+	return -ENODEV;
+}
+#endif	/* CONFIG_HOTPLUG */
+
+struct device_type usb_if_device_type = {
+	.name =		"usb_interface",
+	.release =	usb_release_interface,
+	.uevent =	usb_if_uevent,
+};
+
 /*
  * usb_set_configuration - Makes a particular device setting be current
  * @dev: the device whose configuration is being updated
@@ -1349,7 +1424,7 @@
  *
  * This call is synchronous. The calling context must be able to sleep,
  * must own the device lock, and must not hold the driver model's USB
- * bus rwsem; usb device driver probe() methods cannot use this routine.
+ * bus mutex; usb device driver probe() methods cannot use this routine.
  *
  * Returns zero on success, or else the status code returned by the
  * underlying call that failed.  On successful completion, each interface
@@ -1478,8 +1553,8 @@
 		intf->dev.parent = &dev->dev;
 		intf->dev.driver = NULL;
 		intf->dev.bus = &usb_bus_type;
+		intf->dev.type = &usb_if_device_type;
 		intf->dev.dma_mask = dev->dev.dma_mask;
-		intf->dev.release = release_interface;
 		device_initialize (&intf->dev);
 		mark_quiesced(intf);
 		sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index f08ec85..739f520 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -42,7 +42,7 @@
 {
 #ifdef	CONFIG_USB_SUSPEND
 	/* disable autosuspend, but allow the user to re-enable it via sysfs */
-	udev->autosuspend_delay = 0;
+	udev->autosuspend_disabled = 1;
 #endif
 }
 
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 311d5df..e7c9823 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -11,6 +11,7 @@
 
 
 #include <linux/kernel.h>
+#include <linux/string.h>
 #include <linux/usb.h>
 #include "usb.h"
 
@@ -117,6 +118,16 @@
 static DEVICE_ATTR(speed, S_IRUGO, show_speed, NULL);
 
 static ssize_t
+show_busnum(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct usb_device *udev;
+
+	udev = to_usb_device(dev);
+	return sprintf(buf, "%d\n", udev->bus->busnum);
+}
+static DEVICE_ATTR(busnum, S_IRUGO, show_busnum, NULL);
+
+static ssize_t
 show_devnum(struct device *dev, struct device_attribute *attr, char *buf)
 {
 	struct usb_device *udev;
@@ -165,7 +176,7 @@
 {
 	struct usb_device *udev = to_usb_device(dev);
 
-	return sprintf(buf, "%u\n", udev->autosuspend_delay / HZ);
+	return sprintf(buf, "%d\n", udev->autosuspend_delay / HZ);
 }
 
 static ssize_t
@@ -173,39 +184,115 @@
 		const char *buf, size_t count)
 {
 	struct usb_device *udev = to_usb_device(dev);
-	unsigned value, old;
+	int value;
 
-	if (sscanf(buf, "%u", &value) != 1 || value >= INT_MAX/HZ)
+	if (sscanf(buf, "%d", &value) != 1 || value >= INT_MAX/HZ ||
+			value <= - INT_MAX/HZ)
 		return -EINVAL;
 	value *= HZ;
 
-	old = udev->autosuspend_delay;
 	udev->autosuspend_delay = value;
-	if (value > 0 && old == 0)
+	if (value >= 0)
 		usb_try_autosuspend_device(udev);
-
+	else {
+		if (usb_autoresume_device(udev) == 0)
+			usb_autosuspend_device(udev);
+	}
 	return count;
 }
 
 static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR,
 		show_autosuspend, set_autosuspend);
 
+static const char on_string[] = "on";
+static const char auto_string[] = "auto";
+static const char suspend_string[] = "suspend";
+
+static ssize_t
+show_level(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct usb_device *udev = to_usb_device(dev);
+	const char *p = auto_string;
+
+	if (udev->state == USB_STATE_SUSPENDED) {
+		if (udev->autoresume_disabled)
+			p = suspend_string;
+	} else {
+		if (udev->autosuspend_disabled)
+			p = on_string;
+	}
+	return sprintf(buf, "%s\n", p);
+}
+
+static ssize_t
+set_level(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct usb_device *udev = to_usb_device(dev);
+	int len = count;
+	char *cp;
+	int rc = 0;
+
+	cp = memchr(buf, '\n', count);
+	if (cp)
+		len = cp - buf;
+
+	usb_lock_device(udev);
+
+	/* Setting the flags without calling usb_pm_lock is a subject to
+	 * races, but who cares...
+	 */
+	if (len == sizeof on_string - 1 &&
+			strncmp(buf, on_string, len) == 0) {
+		udev->autosuspend_disabled = 1;
+		udev->autoresume_disabled = 0;
+		rc = usb_external_resume_device(udev);
+
+	} else if (len == sizeof auto_string - 1 &&
+			strncmp(buf, auto_string, len) == 0) {
+		udev->autosuspend_disabled = 0;
+		udev->autoresume_disabled = 0;
+		rc = usb_external_resume_device(udev);
+
+	} else if (len == sizeof suspend_string - 1 &&
+			strncmp(buf, suspend_string, len) == 0) {
+		udev->autosuspend_disabled = 0;
+		udev->autoresume_disabled = 1;
+		rc = usb_external_suspend_device(udev, PMSG_SUSPEND);
+
+	} else
+		rc = -EINVAL;
+
+	usb_unlock_device(udev);
+	return (rc < 0 ? rc : count);
+}
+
+static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level);
+
 static char power_group[] = "power";
 
 static int add_power_attributes(struct device *dev)
 {
 	int rc = 0;
 
-	if (is_usb_device(dev))
+	if (is_usb_device(dev)) {
 		rc = sysfs_add_file_to_group(&dev->kobj,
 				&dev_attr_autosuspend.attr,
 				power_group);
+		if (rc == 0)
+			rc = sysfs_add_file_to_group(&dev->kobj,
+					&dev_attr_level.attr,
+					power_group);
+	}
 	return rc;
 }
 
 static void remove_power_attributes(struct device *dev)
 {
 	sysfs_remove_file_from_group(&dev->kobj,
+			&dev_attr_level.attr,
+			power_group);
+	sysfs_remove_file_from_group(&dev->kobj,
 			&dev_attr_autosuspend.attr,
 			power_group);
 }
@@ -270,6 +357,7 @@
 	&dev_attr_bNumConfigurations.attr,
 	&dev_attr_bMaxPacketSize0.attr,
 	&dev_attr_speed.attr,
+	&dev_attr_busnum.attr,
 	&dev_attr_devnum.attr,
 	&dev_attr_version.attr,
 	&dev_attr_maxchild.attr,
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 54b42ce..dfd1b5c 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -49,12 +49,13 @@
 
 static int nousb;	/* Disable USB when built into kernel image */
 
-struct workqueue_struct *ksuspend_usb_wq;	/* For autosuspend */
+/* Workqueue for autosuspend and for remote wakeup of root hubs */
+struct workqueue_struct *ksuspend_usb_wq;
 
 #ifdef	CONFIG_USB_SUSPEND
 static int usb_autosuspend_delay = 2;		/* Default delay value,
 						 * in seconds */
-module_param_named(autosuspend, usb_autosuspend_delay, uint, 0644);
+module_param_named(autosuspend, usb_autosuspend_delay, int, 0644);
 MODULE_PARM_DESC(autosuspend, "default autosuspend delay");
 
 #else
@@ -196,6 +197,11 @@
 	kfree(udev);
 }
 
+struct device_type usb_device_type = {
+	.name =		"usb_device",
+	.release =	usb_release_dev,
+};
+
 #ifdef	CONFIG_PM
 
 static int ksuspend_usb_init(void)
@@ -211,27 +217,6 @@
 	destroy_workqueue(ksuspend_usb_wq);
 }
 
-#ifdef	CONFIG_USB_SUSPEND
-
-/* usb_autosuspend_work - callback routine to autosuspend a USB device */
-static void usb_autosuspend_work(struct work_struct *work)
-{
-	struct usb_device *udev =
-		container_of(work, struct usb_device, autosuspend.work);
-
-	usb_pm_lock(udev);
-	udev->auto_pm = 1;
-	usb_suspend_both(udev, PMSG_SUSPEND);
-	usb_pm_unlock(udev);
-}
-
-#else
-
-static void usb_autosuspend_work(struct work_struct *work)
-{}
-
-#endif	/* CONFIG_USB_SUSPEND */
-
 #else
 
 #define ksuspend_usb_init()	0
@@ -267,13 +252,10 @@
 
 	device_initialize(&dev->dev);
 	dev->dev.bus = &usb_bus_type;
+	dev->dev.type = &usb_device_type;
 	dev->dev.dma_mask = bus->controller->dma_mask;
-	dev->dev.release = usb_release_dev;
 	dev->state = USB_STATE_ATTACHED;
 
-	/* This magic assignment distinguishes devices from interfaces */
-	dev->dev.platform_data = &usb_generic_driver;
-
 	INIT_LIST_HEAD(&dev->ep0.urb_list);
 	dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
 	dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
@@ -902,9 +884,9 @@
 	retval = usb_register(&usbfs_driver);
 	if (retval)
 		goto driver_register_failed;
-	retval = usbdev_init();
+	retval = usb_devio_init();
 	if (retval)
-		goto usbdevice_init_failed;
+		goto usb_devio_init_failed;
 	retval = usbfs_init();
 	if (retval)
 		goto fs_init_failed;
@@ -919,8 +901,8 @@
 hub_init_failed:
 	usbfs_cleanup();
 fs_init_failed:
-	usbdev_cleanup();
-usbdevice_init_failed:
+	usb_devio_cleanup();
+usb_devio_init_failed:
 	usb_deregister(&usbfs_driver);
 driver_register_failed:
 	usb_major_cleanup();
@@ -947,7 +929,7 @@
 	usb_major_cleanup();
 	usbfs_cleanup();
 	usb_deregister(&usbfs_driver);
-	usbdev_cleanup();
+	usb_devio_cleanup();
 	usb_hub_cleanup();
 	usb_host_cleanup();
 	bus_unregister(&usb_bus_type);
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 08b5a04..bf2eb0d 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -21,7 +21,6 @@
 extern int usb_set_configuration(struct usb_device *dev, int configuration);
 
 extern void usb_kick_khubd(struct usb_device *dev);
-extern void usb_resume_root_hub(struct usb_device *dev);
 extern int usb_match_device(struct usb_device *dev,
 			    const struct usb_device_id *id);
 
@@ -34,10 +33,12 @@
 
 #ifdef	CONFIG_PM
 
-extern int usb_suspend_both(struct usb_device *udev, pm_message_t msg);
-extern int usb_resume_both(struct usb_device *udev);
+extern void usb_autosuspend_work(struct work_struct *work);
 extern int usb_port_suspend(struct usb_device *dev);
 extern int usb_port_resume(struct usb_device *dev);
+extern int usb_external_suspend_device(struct usb_device *udev,
+		pm_message_t msg);
+extern int usb_external_resume_device(struct usb_device *udev);
 
 static inline void usb_pm_lock(struct usb_device *udev)
 {
@@ -51,11 +52,6 @@
 
 #else
 
-#define usb_suspend_both(udev, msg)	0
-static inline int usb_resume_both(struct usb_device *udev)
-{
-	return 0;
-}
 #define usb_port_suspend(dev)		0
 #define usb_port_resume(dev)		0
 static inline void usb_pm_lock(struct usb_device *udev) {}
@@ -82,15 +78,13 @@
 
 extern struct workqueue_struct *ksuspend_usb_wq;
 extern struct bus_type usb_bus_type;
+extern struct device_type usb_device_type;
+extern struct device_type usb_if_device_type;
 extern struct usb_device_driver usb_generic_driver;
 
-/* Here's how we tell apart devices and interfaces.  Luckily there's
- * no such thing as a platform USB device, so we can steal the use
- * of the platform_data field. */
-
 static inline int is_usb_device(const struct device *dev)
 {
-	return dev->platform_data == &usb_generic_driver;
+	return dev->type == &usb_device_type;
 }
 
 /* Do the same for device drivers and interface drivers. */
@@ -126,11 +120,11 @@
 extern struct mutex usbfs_mutex;
 extern struct usb_driver usbfs_driver;
 extern const struct file_operations usbfs_devices_fops;
-extern const struct file_operations usbfs_device_file_operations;
+extern const struct file_operations usbdev_file_operations;
 extern void usbfs_conn_disc_event(void);
 
-extern int usbdev_init(void);
-extern void usbdev_cleanup(void);
+extern int usb_devio_init(void);
+extern void usb_devio_cleanup(void);
 
 struct dev_state {
 	struct list_head list;      /* state list */
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 4097a86..8065f2b 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -68,6 +68,27 @@
 	   Many controller drivers are platform-specific; these
 	   often need board-specific hooks.
 
+config USB_GADGET_FSL_USB2
+	boolean "Freescale Highspeed USB DR Peripheral Controller"
+	depends on MPC834x || PPC_MPC831x
+	select USB_GADGET_DUALSPEED
+	help
+	   Some of Freescale PowerPC processors have a High Speed
+	   Dual-Role(DR) USB controller, which supports device mode.
+
+	   The number of programmable endpoints is different through
+	   SOC revisions.
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "fsl_usb2_udc" and force
+	   all gadget drivers to also be dynamically linked.
+
+config USB_FSL_USB2
+	tristate
+	depends on USB_GADGET_FSL_USB2
+	default USB_GADGET
+	select USB_GADGET_SELECTED
+
 config USB_GADGET_NET2280
 	boolean "NetChip 228x"
 	depends on PCI
@@ -370,6 +391,7 @@
 
 config USB_FILE_STORAGE
 	tristate "File-backed Storage Gadget"
+	depends on BLOCK
 	help
 	  The File-backed Storage Gadget acts as a USB Mass Storage
 	  disk drive.  As its storage repository it can use a regular
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index e71e086..5db1939 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -8,6 +8,7 @@
 obj-$(CONFIG_USB_OMAP)		+= omap_udc.o
 obj-$(CONFIG_USB_LH7A40X)	+= lh7a40x_udc.o
 obj-$(CONFIG_USB_AT91)		+= at91_udc.o
+obj-$(CONFIG_USB_FSL_USB2)	+= fsl_usb2_udc.o
 
 #
 # USB gadget drivers
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 04e6b85..1dd8b57 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -282,6 +282,9 @@
 #define DEV_CONFIG_CDC
 #endif
 
+#ifdef CONFIG_USB_GADGET_FSL_USB2
+#define DEV_CONFIG_CDC
+#endif
 
 /* For CDC-incapable hardware, choose the simple cdc subset.
  * Anything that talks bulk (without notable bugs) can do this.
@@ -1735,7 +1738,8 @@
 		defer_kevent (dev, WORK_RX_MEMORY);
 	if (retval) {
 		DEBUG (dev, "rx submit --> %d\n", retval);
-		dev_kfree_skb_any (skb);
+		if (skb)
+			dev_kfree_skb_any(skb);
 		spin_lock(&dev->req_lock);
 		list_add (&req->list, &dev->rx_reqs);
 		spin_unlock(&dev->req_lock);
@@ -1766,7 +1770,6 @@
 			break;
 		}
 
-		skb->dev = dev->net;
 		skb->protocol = eth_type_trans (skb, dev->net);
 		dev->stats.rx_packets++;
 		dev->stats.rx_bytes += skb->len;
diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_usb2_udc.c
new file mode 100644
index 0000000..157054e
--- /dev/null
+++ b/drivers/usb/gadget/fsl_usb2_udc.c
@@ -0,0 +1,2500 @@
+/*
+ * Copyright (C) 2004-2007 Freescale Semicondutor, Inc. All rights reserved.
+ *
+ * Author: Li Yang <leoli@freescale.com>
+ *         Jiang Bo <tanya.jiang@freescale.com>
+ *
+ * Description:
+ * Freescale high-speed USB SOC DR module device controller driver.
+ * This can be found on MPC8349E/MPC8313E cpus.
+ * The driver is previously named as mpc_udc.  Based on bare board
+ * code from Dave Liu and Shlomi Gridish.
+ *
+ * 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.
+ */
+
+#undef VERBOSE
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb_gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/dmapool.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <asm/dma.h>
+#include <asm/cacheflush.h>
+
+#include "fsl_usb2_udc.h"
+
+#define	DRIVER_DESC	"Freescale High-Speed USB SOC Device Controller driver"
+#define	DRIVER_AUTHOR	"Li Yang/Jiang Bo"
+#define	DRIVER_VERSION	"Apr 20, 2007"
+
+#define	DMA_ADDR_INVALID	(~(dma_addr_t)0)
+
+static const char driver_name[] = "fsl-usb2-udc";
+static const char driver_desc[] = DRIVER_DESC;
+
+volatile static struct usb_dr_device *dr_regs = NULL;
+volatile static struct usb_sys_interface *usb_sys_regs = NULL;
+
+/* it is initialized in probe()  */
+static struct fsl_udc *udc_controller = NULL;
+
+static const struct usb_endpoint_descriptor
+fsl_ep0_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	0,
+	.bmAttributes =		USB_ENDPOINT_XFER_CONTROL,
+	.wMaxPacketSize =	USB_MAX_CTRL_PAYLOAD,
+};
+
+static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state);
+static int fsl_udc_resume(struct platform_device *pdev);
+static void fsl_ep_fifo_flush(struct usb_ep *_ep);
+
+#ifdef CONFIG_PPC32
+#define fsl_readl(addr)		in_le32(addr)
+#define fsl_writel(addr, val32) out_le32(val32, addr)
+#else
+#define fsl_readl(addr)		readl(addr)
+#define fsl_writel(addr, val32) writel(addr, val32)
+#endif
+
+/********************************************************************
+ *	Internal Used Function
+********************************************************************/
+/*-----------------------------------------------------------------
+ * done() - retire a request; caller blocked irqs
+ * @status : request status to be set, only works when
+ *	request is still in progress.
+ *--------------------------------------------------------------*/
+static void done(struct fsl_ep *ep, struct fsl_req *req, int status)
+{
+	struct fsl_udc *udc = NULL;
+	unsigned char stopped = ep->stopped;
+	struct ep_td_struct *curr_td, *next_td;
+	int j;
+
+	udc = (struct fsl_udc *)ep->udc;
+	/* Removed the req from fsl_ep->queue */
+	list_del_init(&req->queue);
+
+	/* req.status should be set as -EINPROGRESS in ep_queue() */
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	/* Free dtd for the request */
+	next_td = req->head;
+	for (j = 0; j < req->dtd_count; j++) {
+		curr_td = next_td;
+		if (j != req->dtd_count - 1) {
+			next_td = curr_td->next_td_virt;
+		}
+		dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma);
+	}
+
+	if (req->mapped) {
+		dma_unmap_single(ep->udc->gadget.dev.parent,
+			req->req.dma, req->req.length,
+			ep_is_in(ep)
+				? DMA_TO_DEVICE
+				: DMA_FROM_DEVICE);
+		req->req.dma = DMA_ADDR_INVALID;
+		req->mapped = 0;
+	} else
+		dma_sync_single_for_cpu(ep->udc->gadget.dev.parent,
+			req->req.dma, req->req.length,
+			ep_is_in(ep)
+				? DMA_TO_DEVICE
+				: DMA_FROM_DEVICE);
+
+	if (status && (status != -ESHUTDOWN))
+		VDBG("complete %s req %p stat %d len %u/%u",
+			ep->ep.name, &req->req, status,
+			req->req.actual, req->req.length);
+
+	ep->stopped = 1;
+
+	spin_unlock(&ep->udc->lock);
+	/* complete() is from gadget layer,
+	 * eg fsg->bulk_in_complete() */
+	if (req->req.complete)
+		req->req.complete(&ep->ep, &req->req);
+
+	spin_lock(&ep->udc->lock);
+	ep->stopped = stopped;
+}
+
+/*-----------------------------------------------------------------
+ * nuke(): delete all requests related to this ep
+ * called with spinlock held
+ *--------------------------------------------------------------*/
+static void nuke(struct fsl_ep *ep, int status)
+{
+	ep->stopped = 1;
+
+	/* Flush fifo */
+	fsl_ep_fifo_flush(&ep->ep);
+
+	/* Whether this eq has request linked */
+	while (!list_empty(&ep->queue)) {
+		struct fsl_req *req = NULL;
+
+		req = list_entry(ep->queue.next, struct fsl_req, queue);
+		done(ep, req, status);
+	}
+}
+
+/*------------------------------------------------------------------
+	Internal Hardware related function
+ ------------------------------------------------------------------*/
+
+static int dr_controller_setup(struct fsl_udc *udc)
+{
+	unsigned int tmp = 0, portctrl = 0, ctrl = 0;
+	unsigned long timeout;
+#define FSL_UDC_RESET_TIMEOUT 1000
+
+	/* before here, make sure dr_regs has been initialized */
+	if (!udc)
+		return -EINVAL;
+
+	/* Stop and reset the usb controller */
+	tmp = fsl_readl(&dr_regs->usbcmd);
+	tmp &= ~USB_CMD_RUN_STOP;
+	fsl_writel(tmp, &dr_regs->usbcmd);
+
+	tmp = fsl_readl(&dr_regs->usbcmd);
+	tmp |= USB_CMD_CTRL_RESET;
+	fsl_writel(tmp, &dr_regs->usbcmd);
+
+	/* Wait for reset to complete */
+	timeout = jiffies + FSL_UDC_RESET_TIMEOUT;
+	while (fsl_readl(&dr_regs->usbcmd) & USB_CMD_CTRL_RESET) {
+		if (time_after(jiffies, timeout)) {
+			ERR("udc reset timeout! \n");
+			return -ETIMEDOUT;
+		}
+		cpu_relax();
+	}
+
+	/* Set the controller as device mode */
+	tmp = fsl_readl(&dr_regs->usbmode);
+	tmp |= USB_MODE_CTRL_MODE_DEVICE;
+	/* Disable Setup Lockout */
+	tmp |= USB_MODE_SETUP_LOCK_OFF;
+	fsl_writel(tmp, &dr_regs->usbmode);
+
+	/* Clear the setup status */
+	fsl_writel(0, &dr_regs->usbsts);
+
+	tmp = udc->ep_qh_dma;
+	tmp &= USB_EP_LIST_ADDRESS_MASK;
+	fsl_writel(tmp, &dr_regs->endpointlistaddr);
+
+	VDBG("vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x",
+		(int)udc->ep_qh, (int)tmp,
+		fsl_readl(&dr_regs->endpointlistaddr));
+
+	/* Config PHY interface */
+	portctrl = fsl_readl(&dr_regs->portsc1);
+	portctrl &= ~PORTSCX_PHY_TYPE_SEL;
+	switch (udc->phy_mode) {
+	case FSL_USB2_PHY_ULPI:
+		portctrl |= PORTSCX_PTS_ULPI;
+		break;
+	case FSL_USB2_PHY_UTMI:
+	case FSL_USB2_PHY_UTMI_WIDE:
+		portctrl |= PORTSCX_PTS_UTMI;
+		break;
+	case FSL_USB2_PHY_SERIAL:
+		portctrl |= PORTSCX_PTS_FSLS;
+		break;
+	default:
+		return -EINVAL;
+	}
+	fsl_writel(portctrl, &dr_regs->portsc1);
+
+	/* Config control enable i/o output, cpu endian register */
+	ctrl = __raw_readl(&usb_sys_regs->control);
+	ctrl |= USB_CTRL_IOENB;
+	__raw_writel(ctrl, &usb_sys_regs->control);
+
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+	/* Turn on cache snooping hardware, since some PowerPC platforms
+	 * wholly rely on hardware to deal with cache coherent. */
+
+	/* Setup Snooping for all the 4GB space */
+	tmp = SNOOP_SIZE_2GB;	/* starts from 0x0, size 2G */
+	__raw_writel(tmp, &usb_sys_regs->snoop1);
+	tmp |= 0x80000000;	/* starts from 0x8000000, size 2G */
+	__raw_writel(tmp, &usb_sys_regs->snoop2);
+#endif
+
+	return 0;
+}
+
+/* Enable DR irq and set controller to run state */
+static void dr_controller_run(struct fsl_udc *udc)
+{
+	u32 temp;
+
+	/* Enable DR irq reg */
+	temp = USB_INTR_INT_EN | USB_INTR_ERR_INT_EN
+		| USB_INTR_PTC_DETECT_EN | USB_INTR_RESET_EN
+		| USB_INTR_DEVICE_SUSPEND | USB_INTR_SYS_ERR_EN;
+
+	fsl_writel(temp, &dr_regs->usbintr);
+
+	/* Clear stopped bit */
+	udc->stopped = 0;
+
+	/* Set the controller as device mode */
+	temp = fsl_readl(&dr_regs->usbmode);
+	temp |= USB_MODE_CTRL_MODE_DEVICE;
+	fsl_writel(temp, &dr_regs->usbmode);
+
+	/* Set controller to Run */
+	temp = fsl_readl(&dr_regs->usbcmd);
+	temp |= USB_CMD_RUN_STOP;
+	fsl_writel(temp, &dr_regs->usbcmd);
+
+	return;
+}
+
+static void dr_controller_stop(struct fsl_udc *udc)
+{
+	unsigned int tmp;
+
+	/* disable all INTR */
+	fsl_writel(0, &dr_regs->usbintr);
+
+	/* Set stopped bit for isr */
+	udc->stopped = 1;
+
+	/* disable IO output */
+/*	usb_sys_regs->control = 0; */
+
+	/* set controller to Stop */
+	tmp = fsl_readl(&dr_regs->usbcmd);
+	tmp &= ~USB_CMD_RUN_STOP;
+	fsl_writel(tmp, &dr_regs->usbcmd);
+
+	return;
+}
+
+void dr_ep_setup(unsigned char ep_num, unsigned char dir, unsigned char ep_type)
+{
+	unsigned int tmp_epctrl = 0;
+
+	tmp_epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+	if (dir) {
+		if (ep_num)
+			tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST;
+		tmp_epctrl |= EPCTRL_TX_ENABLE;
+		tmp_epctrl |= ((unsigned int)(ep_type)
+				<< EPCTRL_TX_EP_TYPE_SHIFT);
+	} else {
+		if (ep_num)
+			tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST;
+		tmp_epctrl |= EPCTRL_RX_ENABLE;
+		tmp_epctrl |= ((unsigned int)(ep_type)
+				<< EPCTRL_RX_EP_TYPE_SHIFT);
+	}
+
+	fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]);
+}
+
+static void
+dr_ep_change_stall(unsigned char ep_num, unsigned char dir, int value)
+{
+	u32 tmp_epctrl = 0;
+
+	tmp_epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+
+	if (value) {
+		/* set the stall bit */
+		if (dir)
+			tmp_epctrl |= EPCTRL_TX_EP_STALL;
+		else
+			tmp_epctrl |= EPCTRL_RX_EP_STALL;
+	} else {
+		/* clear the stall bit and reset data toggle */
+		if (dir) {
+			tmp_epctrl &= ~EPCTRL_TX_EP_STALL;
+			tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST;
+		} else {
+			tmp_epctrl &= ~EPCTRL_RX_EP_STALL;
+			tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST;
+		}
+	}
+	fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]);
+}
+
+/* Get stall status of a specific ep
+   Return: 0: not stalled; 1:stalled */
+static int dr_ep_get_stall(unsigned char ep_num, unsigned char dir)
+{
+	u32 epctrl;
+
+	epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+	if (dir)
+		return (epctrl & EPCTRL_TX_EP_STALL) ? 1 : 0;
+	else
+		return (epctrl & EPCTRL_RX_EP_STALL) ? 1 : 0;
+}
+
+/********************************************************************
+	Internal Structure Build up functions
+********************************************************************/
+
+/*------------------------------------------------------------------
+* struct_ep_qh_setup(): set the Endpoint Capabilites field of QH
+ * @zlt: Zero Length Termination Select (1: disable; 0: enable)
+ * @mult: Mult field
+ ------------------------------------------------------------------*/
+static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num,
+		unsigned char dir, unsigned char ep_type,
+		unsigned int max_pkt_len,
+		unsigned int zlt, unsigned char mult)
+{
+	struct ep_queue_head *p_QH = &udc->ep_qh[2 * ep_num + dir];
+	unsigned int tmp = 0;
+
+	/* set the Endpoint Capabilites in QH */
+	switch (ep_type) {
+	case USB_ENDPOINT_XFER_CONTROL:
+		/* Interrupt On Setup (IOS). for control ep  */
+		tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
+			| EP_QUEUE_HEAD_IOS;
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
+			| (mult << EP_QUEUE_HEAD_MULT_POS);
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+	case USB_ENDPOINT_XFER_INT:
+		tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS;
+		break;
+	default:
+		VDBG("error ep type is %d", ep_type);
+		return;
+	}
+	if (zlt)
+		tmp |= EP_QUEUE_HEAD_ZLT_SEL;
+	p_QH->max_pkt_length = cpu_to_le32(tmp);
+
+	return;
+}
+
+/* Setup qh structure and ep register for ep0. */
+static void ep0_setup(struct fsl_udc *udc)
+{
+	/* the intialization of an ep includes: fields in QH, Regs,
+	 * fsl_ep struct */
+	struct_ep_qh_setup(udc, 0, USB_RECV, USB_ENDPOINT_XFER_CONTROL,
+			USB_MAX_CTRL_PAYLOAD, 0, 0);
+	struct_ep_qh_setup(udc, 0, USB_SEND, USB_ENDPOINT_XFER_CONTROL,
+			USB_MAX_CTRL_PAYLOAD, 0, 0);
+	dr_ep_setup(0, USB_RECV, USB_ENDPOINT_XFER_CONTROL);
+	dr_ep_setup(0, USB_SEND, USB_ENDPOINT_XFER_CONTROL);
+
+	return;
+
+}
+
+/***********************************************************************
+		Endpoint Management Functions
+***********************************************************************/
+
+/*-------------------------------------------------------------------------
+ * when configurations are set, or when interface settings change
+ * for example the do_set_interface() in gadget layer,
+ * the driver will enable or disable the relevant endpoints
+ * ep0 doesn't use this routine. It is always enabled.
+-------------------------------------------------------------------------*/
+static int fsl_ep_enable(struct usb_ep *_ep,
+		const struct usb_endpoint_descriptor *desc)
+{
+	struct fsl_udc *udc = NULL;
+	struct fsl_ep *ep = NULL;
+	unsigned short max = 0;
+	unsigned char mult = 0, zlt;
+	int retval = -EINVAL;
+	unsigned long flags = 0;
+
+	ep = container_of(_ep, struct fsl_ep, ep);
+
+	/* catch various bogus parameters */
+	if (!_ep || !desc || ep->desc
+			|| (desc->bDescriptorType != USB_DT_ENDPOINT))
+		return -EINVAL;
+
+	udc = ep->udc;
+
+	if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN))
+		return -ESHUTDOWN;
+
+	max = le16_to_cpu(desc->wMaxPacketSize);
+
+	/* Disable automatic zlp generation.  Driver is reponsible to indicate
+	 * explicitly through req->req.zero.  This is needed to enable multi-td
+	 * request. */
+	zlt = 1;
+
+	/* Assume the max packet size from gadget is always correct */
+	switch (desc->bmAttributes & 0x03) {
+	case USB_ENDPOINT_XFER_CONTROL:
+	case USB_ENDPOINT_XFER_BULK:
+	case USB_ENDPOINT_XFER_INT:
+		/* mult = 0.  Execute N Transactions as demonstrated by
+		 * the USB variable length packet protocol where N is
+		 * computed using the Maximum Packet Length (dQH) and
+		 * the Total Bytes field (dTD) */
+		mult = 0;
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		/* Calculate transactions needed for high bandwidth iso */
+		mult = (unsigned char)(1 + ((max >> 11) & 0x03));
+		max = max & 0x8ff;	/* bit 0~10 */
+		/* 3 transactions at most */
+		if (mult > 3)
+			goto en_done;
+		break;
+	default:
+		goto en_done;
+	}
+
+	spin_lock_irqsave(&udc->lock, flags);
+	ep->ep.maxpacket = max;
+	ep->desc = desc;
+	ep->stopped = 0;
+
+	/* Controller related setup */
+	/* Init EPx Queue Head (Ep Capabilites field in QH
+	 * according to max, zlt, mult) */
+	struct_ep_qh_setup(udc, (unsigned char) ep_index(ep),
+			(unsigned char) ((desc->bEndpointAddress & USB_DIR_IN)
+					?  USB_SEND : USB_RECV),
+			(unsigned char) (desc->bmAttributes
+					& USB_ENDPOINT_XFERTYPE_MASK),
+			max, zlt, mult);
+
+	/* Init endpoint ctrl register */
+	dr_ep_setup((unsigned char) ep_index(ep),
+			(unsigned char) ((desc->bEndpointAddress & USB_DIR_IN)
+					? USB_SEND : USB_RECV),
+			(unsigned char) (desc->bmAttributes
+					& USB_ENDPOINT_XFERTYPE_MASK));
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+	retval = 0;
+
+	VDBG("enabled %s (ep%d%s) maxpacket %d",ep->ep.name,
+			ep->desc->bEndpointAddress & 0x0f,
+			(desc->bEndpointAddress & USB_DIR_IN)
+				? "in" : "out", max);
+en_done:
+	return retval;
+}
+
+/*---------------------------------------------------------------------
+ * @ep : the ep being unconfigured. May not be ep0
+ * Any pending and uncomplete req will complete with status (-ESHUTDOWN)
+*---------------------------------------------------------------------*/
+static int fsl_ep_disable(struct usb_ep *_ep)
+{
+	struct fsl_udc *udc = NULL;
+	struct fsl_ep *ep = NULL;
+	unsigned long flags = 0;
+	u32 epctrl;
+	int ep_num;
+
+	ep = container_of(_ep, struct fsl_ep, ep);
+	if (!_ep || !ep->desc) {
+		VDBG("%s not enabled", _ep ? ep->ep.name : NULL);
+		return -EINVAL;
+	}
+
+	/* disable ep on controller */
+	ep_num = ep_index(ep);
+	epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+	if (ep_is_in(ep))
+		epctrl &= ~EPCTRL_TX_ENABLE;
+	else
+		epctrl &= ~EPCTRL_RX_ENABLE;
+	fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
+
+	udc = (struct fsl_udc *)ep->udc;
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/* nuke all pending requests (does flush) */
+	nuke(ep, -ESHUTDOWN);
+
+	ep->desc = 0;
+	ep->stopped = 1;
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	VDBG("disabled %s OK", _ep->name);
+	return 0;
+}
+
+/*---------------------------------------------------------------------
+ * allocate a request object used by this endpoint
+ * the main operation is to insert the req->queue to the eq->queue
+ * Returns the request, or null if one could not be allocated
+*---------------------------------------------------------------------*/
+static struct usb_request *
+fsl_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+	struct fsl_req *req = NULL;
+
+	req = kzalloc(sizeof *req, gfp_flags);
+	if (!req)
+		return NULL;
+
+	req->req.dma = DMA_ADDR_INVALID;
+	INIT_LIST_HEAD(&req->queue);
+
+	return &req->req;
+}
+
+static void fsl_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct fsl_req *req = NULL;
+
+	req = container_of(_req, struct fsl_req, req);
+
+	if (_req)
+		kfree(req);
+}
+
+/*------------------------------------------------------------------
+ * Allocate an I/O buffer
+*---------------------------------------------------------------------*/
+static void *fsl_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
+		dma_addr_t *dma, gfp_t gfp_flags)
+{
+	struct fsl_ep *ep;
+
+	if (!_ep)
+		return NULL;
+
+	ep = container_of(_ep, struct fsl_ep, ep);
+
+	return dma_alloc_coherent(ep->udc->gadget.dev.parent,
+			bytes, dma, gfp_flags);
+}
+
+/*------------------------------------------------------------------
+ * frees an i/o buffer
+*---------------------------------------------------------------------*/
+static void fsl_free_buffer(struct usb_ep *_ep, void *buf,
+		dma_addr_t dma, unsigned bytes)
+{
+	struct fsl_ep *ep;
+
+	if (!_ep)
+		return NULL;
+
+	ep = container_of(_ep, struct fsl_ep, ep);
+
+	dma_free_coherent(ep->udc->gadget.dev.parent, bytes, buf, dma);
+}
+
+/*-------------------------------------------------------------------------*/
+static int fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
+{
+	int i = ep_index(ep) * 2 + ep_is_in(ep);
+	u32 temp, bitmask, tmp_stat;
+	struct ep_queue_head *dQH = &ep->udc->ep_qh[i];
+
+	/* VDBG("QH addr Register 0x%8x", dr_regs->endpointlistaddr);
+	VDBG("ep_qh[%d] addr is 0x%8x", i, (u32)&(ep->udc->ep_qh[i])); */
+
+	bitmask = ep_is_in(ep)
+		? (1 << (ep_index(ep) + 16))
+		: (1 << (ep_index(ep)));
+
+	/* check if the pipe is empty */
+	if (!(list_empty(&ep->queue))) {
+		/* Add td to the end */
+		struct fsl_req *lastreq;
+		lastreq = list_entry(ep->queue.prev, struct fsl_req, queue);
+		lastreq->tail->next_td_ptr =
+			cpu_to_le32(req->head->td_dma & DTD_ADDR_MASK);
+		/* Read prime bit, if 1 goto done */
+		if (fsl_readl(&dr_regs->endpointprime) & bitmask)
+			goto out;
+
+		do {
+			/* Set ATDTW bit in USBCMD */
+			temp = fsl_readl(&dr_regs->usbcmd);
+			fsl_writel(temp | USB_CMD_ATDTW, &dr_regs->usbcmd);
+
+			/* Read correct status bit */
+			tmp_stat = fsl_readl(&dr_regs->endptstatus) & bitmask;
+
+		} while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_ATDTW));
+
+		/* Write ATDTW bit to 0 */
+		temp = fsl_readl(&dr_regs->usbcmd);
+		fsl_writel(temp & ~USB_CMD_ATDTW, &dr_regs->usbcmd);
+
+		if (tmp_stat)
+			goto out;
+	}
+
+	/* Write dQH next pointer and terminate bit to 0 */
+	temp = req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK;
+	dQH->next_dtd_ptr = cpu_to_le32(temp);
+
+	/* Clear active and halt bit */
+	temp = cpu_to_le32(~(EP_QUEUE_HEAD_STATUS_ACTIVE
+			| EP_QUEUE_HEAD_STATUS_HALT));
+	dQH->size_ioc_int_sts &= temp;
+
+	/* Prime endpoint by writing 1 to ENDPTPRIME */
+	temp = ep_is_in(ep)
+		? (1 << (ep_index(ep) + 16))
+		: (1 << (ep_index(ep)));
+	fsl_writel(temp, &dr_regs->endpointprime);
+out:
+	return 0;
+}
+
+/* Fill in the dTD structure
+ * @req: request that the transfer belongs to
+ * @length: return actually data length of the dTD
+ * @dma: return dma address of the dTD
+ * @is_last: return flag if it is the last dTD of the request
+ * return: pointer to the built dTD */
+static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length,
+		dma_addr_t *dma, int *is_last)
+{
+	u32 swap_temp;
+	struct ep_td_struct *dtd;
+
+	/* how big will this transfer be? */
+	*length = min(req->req.length - req->req.actual,
+			(unsigned)EP_MAX_LENGTH_TRANSFER);
+
+	dtd = dma_pool_alloc(udc_controller->td_pool, GFP_KERNEL, dma);
+	if (dtd == NULL)
+		return dtd;
+
+	dtd->td_dma = *dma;
+	/* Clear reserved field */
+	swap_temp = cpu_to_le32(dtd->size_ioc_sts);
+	swap_temp &= ~DTD_RESERVED_FIELDS;
+	dtd->size_ioc_sts = cpu_to_le32(swap_temp);
+
+	/* Init all of buffer page pointers */
+	swap_temp = (u32) (req->req.dma + req->req.actual);
+	dtd->buff_ptr0 = cpu_to_le32(swap_temp);
+	dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000);
+	dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000);
+	dtd->buff_ptr3 = cpu_to_le32(swap_temp + 0x3000);
+	dtd->buff_ptr4 = cpu_to_le32(swap_temp + 0x4000);
+
+	req->req.actual += *length;
+
+	/* zlp is needed if req->req.zero is set */
+	if (req->req.zero) {
+		if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0)
+			*is_last = 1;
+		else
+			*is_last = 0;
+	} else if (req->req.length == req->req.actual)
+		*is_last = 1;
+	else
+		*is_last = 0;
+
+	if ((*is_last) == 0)
+		VDBG("multi-dtd request!\n");
+	/* Fill in the transfer size; set active bit */
+	swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE);
+
+	/* Enable interrupt for the last dtd of a request */
+	if (*is_last && !req->req.no_interrupt)
+		swap_temp |= DTD_IOC;
+
+	dtd->size_ioc_sts = cpu_to_le32(swap_temp);
+
+	mb();
+
+	VDBG("length = %d address= 0x%x", *length, (int)*dma);
+
+	return dtd;
+}
+
+/* Generate dtd chain for a request */
+static int fsl_req_to_dtd(struct fsl_req *req)
+{
+	unsigned	count;
+	int		is_last;
+	int		is_first =1;
+	struct ep_td_struct	*last_dtd = NULL, *dtd;
+	dma_addr_t dma;
+
+	do {
+		dtd = fsl_build_dtd(req, &count, &dma, &is_last);
+		if (dtd == NULL)
+			return -ENOMEM;
+
+		if (is_first) {
+			is_first = 0;
+			req->head = dtd;
+		} else {
+			last_dtd->next_td_ptr = cpu_to_le32(dma);
+			last_dtd->next_td_virt = dtd;
+		}
+		last_dtd = dtd;
+
+		req->dtd_count++;
+	} while (!is_last);
+
+	dtd->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE);
+
+	req->tail = dtd;
+
+	return 0;
+}
+
+/* queues (submits) an I/O request to an endpoint */
+static int
+fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+	struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep);
+	struct fsl_req *req = container_of(_req, struct fsl_req, req);
+	struct fsl_udc *udc;
+	unsigned long flags;
+	int is_iso = 0;
+
+	/* catch various bogus parameters */
+	if (!_req || !req->req.complete || !req->req.buf
+			|| !list_empty(&req->queue)) {
+		VDBG("%s, bad params\n", __FUNCTION__);
+		return -EINVAL;
+	}
+	if (!_ep || (!ep->desc && ep_index(ep))) {
+		VDBG("%s, bad ep\n", __FUNCTION__);
+		return -EINVAL;
+	}
+	if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+		if (req->req.length > ep->ep.maxpacket)
+			return -EMSGSIZE;
+		is_iso = 1;
+	}
+
+	udc = ep->udc;
+	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	req->ep = ep;
+
+	/* map virtual address to hardware */
+	if (req->req.dma == DMA_ADDR_INVALID) {
+		req->req.dma = dma_map_single(ep->udc->gadget.dev.parent,
+					req->req.buf,
+					req->req.length, ep_is_in(ep)
+						? DMA_TO_DEVICE
+						: DMA_FROM_DEVICE);
+		req->mapped = 1;
+	} else {
+		dma_sync_single_for_device(ep->udc->gadget.dev.parent,
+					req->req.dma, req->req.length,
+					ep_is_in(ep)
+						? DMA_TO_DEVICE
+						: DMA_FROM_DEVICE);
+		req->mapped = 0;
+	}
+
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+	req->dtd_count = 0;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/* build dtds and push them to device queue */
+	if (!fsl_req_to_dtd(req)) {
+		fsl_queue_td(ep, req);
+	} else {
+		spin_unlock_irqrestore(&udc->lock, flags);
+		return -ENOMEM;
+	}
+
+	/* Update ep0 state */
+	if ((ep_index(ep) == 0))
+		udc->ep0_state = DATA_STATE_XMIT;
+
+	/* irq handler advances the queue */
+	if (req != NULL)
+		list_add_tail(&req->queue, &ep->queue);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+/* dequeues (cancels, unlinks) an I/O request from an endpoint */
+static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep);
+	struct fsl_req *req;
+	unsigned long flags;
+	int ep_num, stopped, ret = 0;
+	u32 epctrl;
+
+	if (!_ep || !_req)
+		return -EINVAL;
+
+	spin_lock_irqsave(&ep->udc->lock, flags);
+	stopped = ep->stopped;
+
+	/* Stop the ep before we deal with the queue */
+	ep->stopped = 1;
+	ep_num = ep_index(ep);
+	epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+	if (ep_is_in(ep))
+		epctrl &= ~EPCTRL_TX_ENABLE;
+	else
+		epctrl &= ~EPCTRL_RX_ENABLE;
+	fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
+
+	/* make sure it's actually queued on this endpoint */
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->req == _req)
+			break;
+	}
+	if (&req->req != _req) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* The request is in progress, or completed but not dequeued */
+	if (ep->queue.next == &req->queue) {
+		_req->status = -ECONNRESET;
+		fsl_ep_fifo_flush(_ep);	/* flush current transfer */
+
+		/* The request isn't the last request in this ep queue */
+		if (req->queue.next != &ep->queue) {
+			struct ep_queue_head *qh;
+			struct fsl_req *next_req;
+
+			qh = ep->qh;
+			next_req = list_entry(req->queue.next, struct fsl_req,
+					queue);
+
+			/* Point the QH to the first TD of next request */
+			fsl_writel((u32) next_req->head, &qh->curr_dtd_ptr);
+		}
+
+		/* The request hasn't been processed, patch up the TD chain */
+	} else {
+		struct fsl_req *prev_req;
+
+		prev_req = list_entry(req->queue.prev, struct fsl_req, queue);
+		fsl_writel(fsl_readl(&req->tail->next_td_ptr),
+				&prev_req->tail->next_td_ptr);
+
+	}
+
+	done(ep, req, -ECONNRESET);
+
+	/* Enable EP */
+out:	epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+	if (ep_is_in(ep))
+		epctrl |= EPCTRL_TX_ENABLE;
+	else
+		epctrl |= EPCTRL_RX_ENABLE;
+	fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
+	ep->stopped = stopped;
+
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+	return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------
+ * modify the endpoint halt feature
+ * @ep: the non-isochronous endpoint being stalled
+ * @value: 1--set halt  0--clear halt
+ * Returns zero, or a negative error code.
+*----------------------------------------------------------------*/
+static int fsl_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	struct fsl_ep *ep = NULL;
+	unsigned long flags = 0;
+	int status = -EOPNOTSUPP;	/* operation not supported */
+	unsigned char ep_dir = 0, ep_num = 0;
+	struct fsl_udc *udc = NULL;
+
+	ep = container_of(_ep, struct fsl_ep, ep);
+	udc = ep->udc;
+	if (!_ep || !ep->desc) {
+		status = -EINVAL;
+		goto out;
+	}
+
+	if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+		status = -EOPNOTSUPP;
+		goto out;
+	}
+
+	/* Attempt to halt IN ep will fail if any transfer requests
+	 * are still queue */
+	if (value && ep_is_in(ep) && !list_empty(&ep->queue)) {
+		status = -EAGAIN;
+		goto out;
+	}
+
+	status = 0;
+	ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV;
+	ep_num = (unsigned char)(ep_index(ep));
+	spin_lock_irqsave(&ep->udc->lock, flags);
+	dr_ep_change_stall(ep_num, ep_dir, value);
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+
+	if (ep_index(ep) == 0) {
+		udc->ep0_state = WAIT_FOR_SETUP;
+		udc->ep0_dir = 0;
+	}
+out:
+	VDBG(" %s %s halt stat %d", ep->ep.name,
+			value ?  "set" : "clear", status);
+
+	return status;
+}
+
+static void fsl_ep_fifo_flush(struct usb_ep *_ep)
+{
+	struct fsl_ep *ep;
+	int ep_num, ep_dir;
+	u32 bits;
+	unsigned long timeout;
+#define FSL_UDC_FLUSH_TIMEOUT 1000
+
+	if (!_ep) {
+		return;
+	} else {
+		ep = container_of(_ep, struct fsl_ep, ep);
+		if (!ep->desc)
+			return;
+	}
+	ep_num = ep_index(ep);
+	ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV;
+
+	if (ep_num == 0)
+		bits = (1 << 16) | 1;
+	else if (ep_dir == USB_SEND)
+		bits = 1 << (16 + ep_num);
+	else
+		bits = 1 << ep_num;
+
+	timeout = jiffies + FSL_UDC_FLUSH_TIMEOUT;
+	do {
+		fsl_writel(bits, &dr_regs->endptflush);
+
+		/* Wait until flush complete */
+		while (fsl_readl(&dr_regs->endptflush)) {
+			if (time_after(jiffies, timeout)) {
+				ERR("ep flush timeout\n");
+				return;
+			}
+			cpu_relax();
+		}
+		/* See if we need to flush again */
+	} while (fsl_readl(&dr_regs->endptstatus) & bits);
+}
+
+static struct usb_ep_ops fsl_ep_ops = {
+	.enable = fsl_ep_enable,
+	.disable = fsl_ep_disable,
+
+	.alloc_request = fsl_alloc_request,
+	.free_request = fsl_free_request,
+
+	.alloc_buffer = fsl_alloc_buffer,
+	.free_buffer = fsl_free_buffer,
+
+	.queue = fsl_ep_queue,
+	.dequeue = fsl_ep_dequeue,
+
+	.set_halt = fsl_ep_set_halt,
+	.fifo_flush = fsl_ep_fifo_flush,	/* flush fifo */
+};
+
+/*-------------------------------------------------------------------------
+		Gadget Driver Layer Operations
+-------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------
+ * Get the current frame number (from DR frame_index Reg )
+ *----------------------------------------------------------------------*/
+static int fsl_get_frame(struct usb_gadget *gadget)
+{
+	return (int)(fsl_readl(&dr_regs->frindex) & USB_FRINDEX_MASKS);
+}
+
+/*-----------------------------------------------------------------------
+ * Tries to wake up the host connected to this gadget
+ -----------------------------------------------------------------------*/
+static int fsl_wakeup(struct usb_gadget *gadget)
+{
+	struct fsl_udc *udc = container_of(gadget, struct fsl_udc, gadget);
+	u32 portsc;
+
+	/* Remote wakeup feature not enabled by host */
+	if (!udc->remote_wakeup)
+		return -ENOTSUPP;
+
+	portsc = fsl_readl(&dr_regs->portsc1);
+	/* not suspended? */
+	if (!(portsc & PORTSCX_PORT_SUSPEND))
+		return 0;
+	/* trigger force resume */
+	portsc |= PORTSCX_PORT_FORCE_RESUME;
+	fsl_writel(portsc, &dr_regs->portsc1);
+	return 0;
+}
+
+static int can_pullup(struct fsl_udc *udc)
+{
+	return udc->driver && udc->softconnect && udc->vbus_active;
+}
+
+/* Notify controller that VBUS is powered, Called by whatever
+   detects VBUS sessions */
+static int fsl_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+	struct fsl_udc	*udc;
+	unsigned long	flags;
+
+	udc = container_of(gadget, struct fsl_udc, gadget);
+	spin_lock_irqsave(&udc->lock, flags);
+	VDBG("VBUS %s\n", is_active ? "on" : "off");
+	udc->vbus_active = (is_active != 0);
+	if (can_pullup(udc))
+		fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
+				&dr_regs->usbcmd);
+	else
+		fsl_writel((fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP),
+				&dr_regs->usbcmd);
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return 0;
+}
+
+/* constrain controller's VBUS power usage
+ * This call is used by gadget drivers during SET_CONFIGURATION calls,
+ * reporting how much power the device may consume.  For example, this
+ * could affect how quickly batteries are recharged.
+ *
+ * Returns zero on success, else negative errno.
+ */
+static int fsl_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+#ifdef CONFIG_USB_OTG
+	struct fsl_udc *udc;
+
+	udc = container_of(gadget, struct fsl_udc, gadget);
+
+	if (udc->transceiver)
+		return otg_set_power(udc->transceiver, mA);
+#endif
+	return -ENOTSUPP;
+}
+
+/* Change Data+ pullup status
+ * this func is used by usb_gadget_connect/disconnet
+ */
+static int fsl_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct fsl_udc *udc;
+
+	udc = container_of(gadget, struct fsl_udc, gadget);
+	udc->softconnect = (is_on != 0);
+	if (can_pullup(udc))
+		fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
+				&dr_regs->usbcmd);
+	else
+		fsl_writel((fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP),
+				&dr_regs->usbcmd);
+
+	return 0;
+}
+
+/* defined in usb_gadget.h */
+static struct usb_gadget_ops fsl_gadget_ops = {
+	.get_frame = fsl_get_frame,
+	.wakeup = fsl_wakeup,
+/*	.set_selfpowered = fsl_set_selfpowered,	*/ /* Always selfpowered */
+	.vbus_session = fsl_vbus_session,
+	.vbus_draw = fsl_vbus_draw,
+	.pullup = fsl_pullup,
+};
+
+/* Set protocol stall on ep0, protocol stall will automatically be cleared
+   on new transaction */
+static void ep0stall(struct fsl_udc *udc)
+{
+	u32 tmp;
+
+	/* must set tx and rx to stall at the same time */
+	tmp = fsl_readl(&dr_regs->endptctrl[0]);
+	tmp |= EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL;
+	fsl_writel(tmp, &dr_regs->endptctrl[0]);
+	udc->ep0_state = WAIT_FOR_SETUP;
+	udc->ep0_dir = 0;
+}
+
+/* Prime a status phase for ep0 */
+static int ep0_prime_status(struct fsl_udc *udc, int direction)
+{
+	struct fsl_req *req = udc->status_req;
+	struct fsl_ep *ep;
+	int status = 0;
+
+	if (direction == EP_DIR_IN)
+		udc->ep0_dir = USB_DIR_IN;
+	else
+		udc->ep0_dir = USB_DIR_OUT;
+
+	ep = &udc->eps[0];
+	udc->ep0_state = WAIT_FOR_OUT_STATUS;
+
+	req->ep = ep;
+	req->req.length = 0;
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+	req->req.complete = NULL;
+	req->dtd_count = 0;
+
+	if (fsl_req_to_dtd(req) == 0)
+		status = fsl_queue_td(ep, req);
+	else
+		return -ENOMEM;
+
+	if (status)
+		ERR("Can't queue ep0 status request \n");
+	list_add_tail(&req->queue, &ep->queue);
+
+	return status;
+}
+
+static inline int udc_reset_ep_queue(struct fsl_udc *udc, u8 pipe)
+{
+	struct fsl_ep *ep = get_ep_by_pipe(udc, pipe);
+
+	if (!ep->name)
+		return 0;
+
+	nuke(ep, -ESHUTDOWN);
+
+	return 0;
+}
+
+/*
+ * ch9 Set address
+ */
+static void ch9setaddress(struct fsl_udc *udc, u16 value, u16 index, u16 length)
+{
+	/* Save the new address to device struct */
+	udc->device_address = (u8) value;
+	/* Update usb state */
+	udc->usb_state = USB_STATE_ADDRESS;
+	/* Status phase */
+	if (ep0_prime_status(udc, EP_DIR_IN))
+		ep0stall(udc);
+}
+
+/*
+ * ch9 Get status
+ */
+static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,
+		u16 index, u16 length)
+{
+	u16 tmp = 0;		/* Status, cpu endian */
+
+	struct fsl_req *req;
+	struct fsl_ep *ep;
+	int status = 0;
+
+	ep = &udc->eps[0];
+
+	if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
+		/* Get device status */
+		tmp = 1 << USB_DEVICE_SELF_POWERED;
+		tmp |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP;
+	} else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
+		/* Get interface status */
+		/* We don't have interface information in udc driver */
+		tmp = 0;
+	} else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) {
+		/* Get endpoint status */
+		struct fsl_ep *target_ep;
+
+		target_ep = get_ep_by_pipe(udc, get_pipe_by_windex(index));
+
+		/* stall if endpoint doesn't exist */
+		if (!target_ep->desc)
+			goto stall;
+		tmp = dr_ep_get_stall(ep_index(target_ep), ep_is_in(target_ep))
+				<< USB_ENDPOINT_HALT;
+	}
+
+	udc->ep0_dir = USB_DIR_IN;
+	/* Borrow the per device status_req */
+	req = udc->status_req;
+	/* Fill in the reqest structure */
+	*((u16 *) req->req.buf) = cpu_to_le16(tmp);
+	req->ep = ep;
+	req->req.length = 2;
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+	req->req.complete = NULL;
+	req->dtd_count = 0;
+
+	/* prime the data phase */
+	if ((fsl_req_to_dtd(req) == 0))
+		status = fsl_queue_td(ep, req);
+	else			/* no mem */
+		goto stall;
+
+	if (status) {
+		ERR("Can't respond to getstatus request \n");
+		goto stall;
+	}
+	list_add_tail(&req->queue, &ep->queue);
+	udc->ep0_state = DATA_STATE_XMIT;
+	return;
+stall:
+	ep0stall(udc);
+}
+
+static void setup_received_irq(struct fsl_udc *udc,
+		struct usb_ctrlrequest *setup)
+{
+	u16 wValue = le16_to_cpu(setup->wValue);
+	u16 wIndex = le16_to_cpu(setup->wIndex);
+	u16 wLength = le16_to_cpu(setup->wLength);
+
+	udc_reset_ep_queue(udc, 0);
+
+	switch (setup->bRequest) {
+		/* Request that need Data+Status phase from udc */
+	case USB_REQ_GET_STATUS:
+		if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_STANDARD))
+					!= (USB_DIR_IN | USB_TYPE_STANDARD))
+			break;
+		ch9getstatus(udc, setup->bRequestType, wValue, wIndex, wLength);
+		break;
+
+		/* Requests that need Status phase from udc */
+	case USB_REQ_SET_ADDRESS:
+		if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD
+						| USB_RECIP_DEVICE))
+			break;
+		ch9setaddress(udc, wValue, wIndex, wLength);
+		break;
+
+		/* Handled by udc, no data, status by udc */
+	case USB_REQ_CLEAR_FEATURE:
+	case USB_REQ_SET_FEATURE:
+	{	/* status transaction */
+		int rc = -EOPNOTSUPP;
+
+		if ((setup->bRequestType & USB_RECIP_MASK)
+				== USB_RECIP_ENDPOINT) {
+			int pipe = get_pipe_by_windex(wIndex);
+			struct fsl_ep *ep;
+
+			if (wValue != 0 || wLength != 0 || pipe > udc->max_ep)
+				break;
+			ep = get_ep_by_pipe(udc, pipe);
+
+			spin_unlock(&udc->lock);
+			rc = fsl_ep_set_halt(&ep->ep,
+					(setup->bRequest == USB_REQ_SET_FEATURE)
+						? 1 : 0);
+			spin_lock(&udc->lock);
+
+		} else if ((setup->bRequestType & USB_RECIP_MASK)
+				== USB_RECIP_DEVICE) {
+			/* Note: The driver has not include OTG support yet.
+			 * This will be set when OTG support is added */
+			if (!udc->gadget.is_otg)
+				break;
+			else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE)
+				udc->gadget.b_hnp_enable = 1;
+			else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT)
+				udc->gadget.a_hnp_support = 1;
+			else if (setup->bRequest ==
+					USB_DEVICE_A_ALT_HNP_SUPPORT)
+				udc->gadget.a_alt_hnp_support = 1;
+			rc = 0;
+		}
+		if (rc == 0) {
+			if (ep0_prime_status(udc, EP_DIR_IN))
+				ep0stall(udc);
+		}
+		break;
+	}
+		/* Requests handled by gadget */
+	default:
+		if (wLength) {
+			/* Data phase from gadget, status phase from udc */
+			udc->ep0_dir = (setup->bRequestType & USB_DIR_IN)
+					?  USB_DIR_IN : USB_DIR_OUT;
+			spin_unlock(&udc->lock);
+			if (udc->driver->setup(&udc->gadget,
+					&udc->local_setup_buff) < 0)
+				ep0stall(udc);
+			spin_lock(&udc->lock);
+			udc->ep0_state = (setup->bRequestType & USB_DIR_IN)
+					?  DATA_STATE_XMIT : DATA_STATE_RECV;
+
+		} else {
+			/* No data phase, IN status from gadget */
+			udc->ep0_dir = USB_DIR_IN;
+			spin_unlock(&udc->lock);
+			if (udc->driver->setup(&udc->gadget,
+					&udc->local_setup_buff) < 0)
+				ep0stall(udc);
+			spin_lock(&udc->lock);
+			udc->ep0_state = WAIT_FOR_OUT_STATUS;
+		}
+		break;
+	}
+}
+
+/* Process request for Data or Status phase of ep0
+ * prime status phase if needed */
+static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0,
+		struct fsl_req *req)
+{
+	if (udc->usb_state == USB_STATE_ADDRESS) {
+		/* Set the new address */
+		u32 new_address = (u32) udc->device_address;
+		fsl_writel(new_address << USB_DEVICE_ADDRESS_BIT_POS,
+				&dr_regs->deviceaddr);
+	}
+
+	done(ep0, req, 0);
+
+	switch (udc->ep0_state) {
+	case DATA_STATE_XMIT:
+		/* receive status phase */
+		if (ep0_prime_status(udc, EP_DIR_OUT))
+			ep0stall(udc);
+		break;
+	case DATA_STATE_RECV:
+		/* send status phase */
+		if (ep0_prime_status(udc, EP_DIR_IN))
+			ep0stall(udc);
+		break;
+	case WAIT_FOR_OUT_STATUS:
+		udc->ep0_state = WAIT_FOR_SETUP;
+		break;
+	case WAIT_FOR_SETUP:
+		ERR("Unexpect ep0 packets \n");
+		break;
+	default:
+		ep0stall(udc);
+		break;
+	}
+}
+
+/* Tripwire mechanism to ensure a setup packet payload is extracted without
+ * being corrupted by another incoming setup packet */
+static void tripwire_handler(struct fsl_udc *udc, u8 ep_num, u8 *buffer_ptr)
+{
+	u32 temp;
+	struct ep_queue_head *qh;
+
+	qh = &udc->ep_qh[ep_num * 2 + EP_DIR_OUT];
+
+	/* Clear bit in ENDPTSETUPSTAT */
+	temp = fsl_readl(&dr_regs->endptsetupstat);
+	fsl_writel(temp | (1 << ep_num), &dr_regs->endptsetupstat);
+
+	/* while a hazard exists when setup package arrives */
+	do {
+		/* Set Setup Tripwire */
+		temp = fsl_readl(&dr_regs->usbcmd);
+		fsl_writel(temp | USB_CMD_SUTW, &dr_regs->usbcmd);
+
+		/* Copy the setup packet to local buffer */
+		memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8);
+	} while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_SUTW));
+
+	/* Clear Setup Tripwire */
+	temp = fsl_readl(&dr_regs->usbcmd);
+	fsl_writel(temp & ~USB_CMD_SUTW, &dr_regs->usbcmd);
+}
+
+/* process-ep_req(): free the completed Tds for this req */
+static int process_ep_req(struct fsl_udc *udc, int pipe,
+		struct fsl_req *curr_req)
+{
+	struct ep_td_struct *curr_td;
+	int	td_complete, actual, remaining_length, j, tmp;
+	int	status = 0;
+	int	errors = 0;
+	struct  ep_queue_head *curr_qh = &udc->ep_qh[pipe];
+	int direction = pipe % 2;
+
+	curr_td = curr_req->head;
+	td_complete = 0;
+	actual = curr_req->req.length;
+
+	for (j = 0; j < curr_req->dtd_count; j++) {
+		remaining_length = (le32_to_cpu(curr_td->size_ioc_sts)
+					& DTD_PACKET_SIZE)
+				>> DTD_LENGTH_BIT_POS;
+		actual -= remaining_length;
+
+		if ((errors = le32_to_cpu(curr_td->size_ioc_sts) &
+						DTD_ERROR_MASK)) {
+			if (errors & DTD_STATUS_HALTED) {
+				ERR("dTD error %08x QH=%d\n", errors, pipe);
+				/* Clear the errors and Halt condition */
+				tmp = le32_to_cpu(curr_qh->size_ioc_int_sts);
+				tmp &= ~errors;
+				curr_qh->size_ioc_int_sts = cpu_to_le32(tmp);
+				status = -EPIPE;
+				/* FIXME: continue with next queued TD? */
+
+				break;
+			}
+			if (errors & DTD_STATUS_DATA_BUFF_ERR) {
+				VDBG("Transfer overflow");
+				status = -EPROTO;
+				break;
+			} else if (errors & DTD_STATUS_TRANSACTION_ERR) {
+				VDBG("ISO error");
+				status = -EILSEQ;
+				break;
+			} else
+				ERR("Unknown error has occured (0x%x)!\r\n",
+					errors);
+
+		} else if (le32_to_cpu(curr_td->size_ioc_sts)
+				& DTD_STATUS_ACTIVE) {
+			VDBG("Request not complete");
+			status = REQ_UNCOMPLETE;
+			return status;
+		} else if (remaining_length) {
+			if (direction) {
+				VDBG("Transmit dTD remaining length not zero");
+				status = -EPROTO;
+				break;
+			} else {
+				td_complete++;
+				break;
+			}
+		} else {
+			td_complete++;
+			VDBG("dTD transmitted successful ");
+		}
+
+		if (j != curr_req->dtd_count - 1)
+			curr_td = (struct ep_td_struct *)curr_td->next_td_virt;
+	}
+
+	if (status)
+		return status;
+
+	curr_req->req.actual = actual;
+
+	return 0;
+}
+
+/* Process a DTD completion interrupt */
+static void dtd_complete_irq(struct fsl_udc *udc)
+{
+	u32 bit_pos;
+	int i, ep_num, direction, bit_mask, status;
+	struct fsl_ep *curr_ep;
+	struct fsl_req *curr_req, *temp_req;
+
+	/* Clear the bits in the register */
+	bit_pos = fsl_readl(&dr_regs->endptcomplete);
+	fsl_writel(bit_pos, &dr_regs->endptcomplete);
+
+	if (!bit_pos)
+		return;
+
+	for (i = 0; i < udc->max_ep * 2; i++) {
+		ep_num = i >> 1;
+		direction = i % 2;
+
+		bit_mask = 1 << (ep_num + 16 * direction);
+
+		if (!(bit_pos & bit_mask))
+			continue;
+
+		curr_ep = get_ep_by_pipe(udc, i);
+
+		/* If the ep is configured */
+		if (curr_ep->name == NULL) {
+			WARN("Invalid EP?");
+			continue;
+		}
+
+		/* process the req queue until an uncomplete request */
+		list_for_each_entry_safe(curr_req, temp_req, &curr_ep->queue,
+				queue) {
+			status = process_ep_req(udc, i, curr_req);
+
+			VDBG("status of process_ep_req= %d, ep = %d",
+					status, ep_num);
+			if (status == REQ_UNCOMPLETE)
+				break;
+			/* write back status to req */
+			curr_req->req.status = status;
+
+			if (ep_num == 0) {
+				ep0_req_complete(udc, curr_ep, curr_req);
+				break;
+			} else
+				done(curr_ep, curr_req, status);
+		}
+	}
+}
+
+/* Process a port change interrupt */
+static void port_change_irq(struct fsl_udc *udc)
+{
+	u32 speed;
+
+	if (udc->bus_reset)
+		udc->bus_reset = 0;
+
+	/* Bus resetting is finished */
+	if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) {
+		/* Get the speed */
+		speed = (fsl_readl(&dr_regs->portsc1)
+				& PORTSCX_PORT_SPEED_MASK);
+		switch (speed) {
+		case PORTSCX_PORT_SPEED_HIGH:
+			udc->gadget.speed = USB_SPEED_HIGH;
+			break;
+		case PORTSCX_PORT_SPEED_FULL:
+			udc->gadget.speed = USB_SPEED_FULL;
+			break;
+		case PORTSCX_PORT_SPEED_LOW:
+			udc->gadget.speed = USB_SPEED_LOW;
+			break;
+		default:
+			udc->gadget.speed = USB_SPEED_UNKNOWN;
+			break;
+		}
+	}
+
+	/* Update USB state */
+	if (!udc->resume_state)
+		udc->usb_state = USB_STATE_DEFAULT;
+}
+
+/* Process suspend interrupt */
+static void suspend_irq(struct fsl_udc *udc)
+{
+	udc->resume_state = udc->usb_state;
+	udc->usb_state = USB_STATE_SUSPENDED;
+
+	/* report suspend to the driver, serial.c does not support this */
+	if (udc->driver->suspend)
+		udc->driver->suspend(&udc->gadget);
+}
+
+static void bus_resume(struct fsl_udc *udc)
+{
+	udc->usb_state = udc->resume_state;
+	udc->resume_state = 0;
+
+	/* report resume to the driver, serial.c does not support this */
+	if (udc->driver->resume)
+		udc->driver->resume(&udc->gadget);
+}
+
+/* Clear up all ep queues */
+static int reset_queues(struct fsl_udc *udc)
+{
+	u8 pipe;
+
+	for (pipe = 0; pipe < udc->max_pipes; pipe++)
+		udc_reset_ep_queue(udc, pipe);
+
+	/* report disconnect; the driver is already quiesced */
+	udc->driver->disconnect(&udc->gadget);
+
+	return 0;
+}
+
+/* Process reset interrupt */
+static void reset_irq(struct fsl_udc *udc)
+{
+	u32 temp;
+	unsigned long timeout;
+
+	/* Clear the device address */
+	temp = fsl_readl(&dr_regs->deviceaddr);
+	fsl_writel(temp & ~USB_DEVICE_ADDRESS_MASK, &dr_regs->deviceaddr);
+
+	udc->device_address = 0;
+
+	/* Clear usb state */
+	udc->resume_state = 0;
+	udc->ep0_dir = 0;
+	udc->ep0_state = WAIT_FOR_SETUP;
+	udc->remote_wakeup = 0;	/* default to 0 on reset */
+	udc->gadget.b_hnp_enable = 0;
+	udc->gadget.a_hnp_support = 0;
+	udc->gadget.a_alt_hnp_support = 0;
+
+	/* Clear all the setup token semaphores */
+	temp = fsl_readl(&dr_regs->endptsetupstat);
+	fsl_writel(temp, &dr_regs->endptsetupstat);
+
+	/* Clear all the endpoint complete status bits */
+	temp = fsl_readl(&dr_regs->endptcomplete);
+	fsl_writel(temp, &dr_regs->endptcomplete);
+
+	timeout = jiffies + 100;
+	while (fsl_readl(&dr_regs->endpointprime)) {
+		/* Wait until all endptprime bits cleared */
+		if (time_after(jiffies, timeout)) {
+			ERR("Timeout for reset\n");
+			break;
+		}
+		cpu_relax();
+	}
+
+	/* Write 1s to the flush register */
+	fsl_writel(0xffffffff, &dr_regs->endptflush);
+
+	if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) {
+		VDBG("Bus reset");
+		/* Bus is reseting */
+		udc->bus_reset = 1;
+		/* Reset all the queues, include XD, dTD, EP queue
+		 * head and TR Queue */
+		reset_queues(udc);
+		udc->usb_state = USB_STATE_DEFAULT;
+	} else {
+		VDBG("Controller reset");
+		/* initialize usb hw reg except for regs for EP, not
+		 * touch usbintr reg */
+		dr_controller_setup(udc);
+
+		/* Reset all internal used Queues */
+		reset_queues(udc);
+
+		ep0_setup(udc);
+
+		/* Enable DR IRQ reg, Set Run bit, change udc state */
+		dr_controller_run(udc);
+		udc->usb_state = USB_STATE_ATTACHED;
+	}
+}
+
+/*
+ * USB device controller interrupt handler
+ */
+static irqreturn_t fsl_udc_irq(int irq, void *_udc)
+{
+	struct fsl_udc *udc = _udc;
+	u32 irq_src;
+	irqreturn_t status = IRQ_NONE;
+	unsigned long flags;
+
+	/* Disable ISR for OTG host mode */
+	if (udc->stopped)
+		return IRQ_NONE;
+	spin_lock_irqsave(&udc->lock, flags);
+	irq_src = fsl_readl(&dr_regs->usbsts) & fsl_readl(&dr_regs->usbintr);
+	/* Clear notification bits */
+	fsl_writel(irq_src, &dr_regs->usbsts);
+
+	/* VDBG("irq_src [0x%8x]", irq_src); */
+
+	/* Need to resume? */
+	if (udc->usb_state == USB_STATE_SUSPENDED)
+		if ((fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_SUSPEND) == 0)
+			bus_resume(udc);
+
+	/* USB Interrupt */
+	if (irq_src & USB_STS_INT) {
+		VDBG("Packet int");
+		/* Setup package, we only support ep0 as control ep */
+		if (fsl_readl(&dr_regs->endptsetupstat) & EP_SETUP_STATUS_EP0) {
+			tripwire_handler(udc, 0,
+					(u8 *) (&udc->local_setup_buff));
+			setup_received_irq(udc, &udc->local_setup_buff);
+			status = IRQ_HANDLED;
+		}
+
+		/* completion of dtd */
+		if (fsl_readl(&dr_regs->endptcomplete)) {
+			dtd_complete_irq(udc);
+			status = IRQ_HANDLED;
+		}
+	}
+
+	/* SOF (for ISO transfer) */
+	if (irq_src & USB_STS_SOF) {
+		status = IRQ_HANDLED;
+	}
+
+	/* Port Change */
+	if (irq_src & USB_STS_PORT_CHANGE) {
+		port_change_irq(udc);
+		status = IRQ_HANDLED;
+	}
+
+	/* Reset Received */
+	if (irq_src & USB_STS_RESET) {
+		reset_irq(udc);
+		status = IRQ_HANDLED;
+	}
+
+	/* Sleep Enable (Suspend) */
+	if (irq_src & USB_STS_SUSPEND) {
+		suspend_irq(udc);
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) {
+		VDBG("Error IRQ %x ", irq_src);
+	}
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return status;
+}
+
+/*----------------------------------------------------------------*
+ * Hook to gadget drivers
+ * Called by initialization code of gadget drivers
+*----------------------------------------------------------------*/
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+	int retval = -ENODEV;
+	unsigned long flags = 0;
+
+	if (!udc_controller)
+		return -ENODEV;
+
+	if (!driver || (driver->speed != USB_SPEED_FULL
+				&& driver->speed != USB_SPEED_HIGH)
+			|| !driver->bind || !driver->disconnect
+			|| !driver->setup)
+		return -EINVAL;
+
+	if (udc_controller->driver)
+		return -EBUSY;
+
+	/* lock is needed but whether should use this lock or another */
+	spin_lock_irqsave(&udc_controller->lock, flags);
+
+	driver->driver.bus = 0;
+	/* hook up the driver */
+	udc_controller->driver = driver;
+	udc_controller->gadget.dev.driver = &driver->driver;
+	spin_unlock_irqrestore(&udc_controller->lock, flags);
+
+	/* bind udc driver to gadget driver */
+	retval = driver->bind(&udc_controller->gadget);
+	if (retval) {
+		VDBG("bind to %s --> %d", driver->driver.name, retval);
+		udc_controller->gadget.dev.driver = 0;
+		udc_controller->driver = 0;
+		goto out;
+	}
+
+	/* Enable DR IRQ reg and Set usbcmd reg  Run bit */
+	dr_controller_run(udc_controller);
+	udc_controller->usb_state = USB_STATE_ATTACHED;
+	udc_controller->ep0_state = WAIT_FOR_SETUP;
+	udc_controller->ep0_dir = 0;
+	printk(KERN_INFO "%s: bind to driver %s \n",
+			udc_controller->gadget.name, driver->driver.name);
+
+out:
+	if (retval)
+		printk("retval %d \n", retval);
+	return retval;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+/* Disconnect from gadget driver */
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct fsl_ep *loop_ep;
+	unsigned long flags;
+
+	if (!udc_controller)
+		return -ENODEV;
+
+	if (!driver || driver != udc_controller->driver || !driver->unbind)
+		return -EINVAL;
+
+#ifdef CONFIG_USB_OTG
+	if (udc_controller->transceiver)
+		(void)otg_set_peripheral(udc_controller->transceiver, 0);
+#endif
+
+	/* stop DR, disable intr */
+	dr_controller_stop(udc_controller);
+
+	/* in fact, no needed */
+	udc_controller->usb_state = USB_STATE_ATTACHED;
+	udc_controller->ep0_state = WAIT_FOR_SETUP;
+	udc_controller->ep0_dir = 0;
+
+	/* stand operation */
+	spin_lock_irqsave(&udc_controller->lock, flags);
+	udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
+	nuke(&udc_controller->eps[0], -ESHUTDOWN);
+	list_for_each_entry(loop_ep, &udc_controller->gadget.ep_list,
+			ep.ep_list)
+		nuke(loop_ep, -ESHUTDOWN);
+	spin_unlock_irqrestore(&udc_controller->lock, flags);
+
+	/* unbind gadget and unhook driver. */
+	driver->unbind(&udc_controller->gadget);
+	udc_controller->gadget.dev.driver = 0;
+	udc_controller->driver = 0;
+
+	printk("unregistered gadget driver '%s'\r\n", driver->driver.name);
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/*-------------------------------------------------------------------------
+		PROC File System Support
+-------------------------------------------------------------------------*/
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+#include <linux/seq_file.h>
+
+static const char proc_filename[] = "driver/fsl_usb2_udc";
+
+static int fsl_proc_read(char *page, char **start, off_t off, int count,
+		int *eof, void *_dev)
+{
+	char *buf = page;
+	char *next = buf;
+	unsigned size = count;
+	unsigned long flags;
+	int t, i;
+	u32 tmp_reg;
+	struct fsl_ep *ep = NULL;
+	struct fsl_req *req;
+
+	struct fsl_udc *udc = udc_controller;
+	if (off != 0)
+		return 0;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/* ------basic driver infomation ---- */
+	t = scnprintf(next, size,
+			DRIVER_DESC "\n"
+			"%s version: %s\n"
+			"Gadget driver: %s\n\n",
+			driver_name, DRIVER_VERSION,
+			udc->driver ? udc->driver->driver.name : "(none)");
+	size -= t;
+	next += t;
+
+	/* ------ DR Registers ----- */
+	tmp_reg = fsl_readl(&dr_regs->usbcmd);
+	t = scnprintf(next, size,
+			"USBCMD reg:\n"
+			"SetupTW: %d\n"
+			"Run/Stop: %s\n\n",
+			(tmp_reg & USB_CMD_SUTW) ? 1 : 0,
+			(tmp_reg & USB_CMD_RUN_STOP) ? "Run" : "Stop");
+	size -= t;
+	next += t;
+
+	tmp_reg = fsl_readl(&dr_regs->usbsts);
+	t = scnprintf(next, size,
+			"USB Status Reg:\n"
+			"Dr Suspend: %d" "Reset Received: %d" "System Error: %s"
+			"USB Error Interrupt: %s\n\n",
+			(tmp_reg & USB_STS_SUSPEND) ? 1 : 0,
+			(tmp_reg & USB_STS_RESET) ? 1 : 0,
+			(tmp_reg & USB_STS_SYS_ERR) ? "Err" : "Normal",
+			(tmp_reg & USB_STS_ERR) ? "Err detected" : "No err");
+	size -= t;
+	next += t;
+
+	tmp_reg = fsl_readl(&dr_regs->usbintr);
+	t = scnprintf(next, size,
+			"USB Intrrupt Enable Reg:\n"
+			"Sleep Enable: %d" "SOF Received Enable: %d"
+			"Reset Enable: %d\n"
+			"System Error Enable: %d"
+			"Port Change Dectected Enable: %d\n"
+			"USB Error Intr Enable: %d" "USB Intr Enable: %d\n\n",
+			(tmp_reg & USB_INTR_DEVICE_SUSPEND) ? 1 : 0,
+			(tmp_reg & USB_INTR_SOF_EN) ? 1 : 0,
+			(tmp_reg & USB_INTR_RESET_EN) ? 1 : 0,
+			(tmp_reg & USB_INTR_SYS_ERR_EN) ? 1 : 0,
+			(tmp_reg & USB_INTR_PTC_DETECT_EN) ? 1 : 0,
+			(tmp_reg & USB_INTR_ERR_INT_EN) ? 1 : 0,
+			(tmp_reg & USB_INTR_INT_EN) ? 1 : 0);
+	size -= t;
+	next += t;
+
+	tmp_reg = fsl_readl(&dr_regs->frindex);
+	t = scnprintf(next, size,
+			"USB Frame Index Reg:" "Frame Number is 0x%x\n\n",
+			(tmp_reg & USB_FRINDEX_MASKS));
+	size -= t;
+	next += t;
+
+	tmp_reg = fsl_readl(&dr_regs->deviceaddr);
+	t = scnprintf(next, size,
+			"USB Device Address Reg:" "Device Addr is 0x%x\n\n",
+			(tmp_reg & USB_DEVICE_ADDRESS_MASK));
+	size -= t;
+	next += t;
+
+	tmp_reg = fsl_readl(&dr_regs->endpointlistaddr);
+	t = scnprintf(next, size,
+			"USB Endpoint List Address Reg:"
+			"Device Addr is 0x%x\n\n",
+			(tmp_reg & USB_EP_LIST_ADDRESS_MASK));
+	size -= t;
+	next += t;
+
+	tmp_reg = fsl_readl(&dr_regs->portsc1);
+	t = scnprintf(next, size,
+		"USB Port Status&Control Reg:\n"
+		"Port Transceiver Type : %s" "Port Speed: %s \n"
+		"PHY Low Power Suspend: %s" "Port Reset: %s"
+		"Port Suspend Mode: %s \n" "Over-current Change: %s"
+		"Port Enable/Disable Change: %s\n"
+		"Port Enabled/Disabled: %s"
+		"Current Connect Status: %s\n\n", ( {
+			char *s;
+			switch (tmp_reg & PORTSCX_PTS_FSLS) {
+			case PORTSCX_PTS_UTMI:
+				s = "UTMI"; break;
+			case PORTSCX_PTS_ULPI:
+				s = "ULPI "; break;
+			case PORTSCX_PTS_FSLS:
+				s = "FS/LS Serial"; break;
+			default:
+				s = "None"; break;
+			}
+			s;} ), ( {
+			char *s;
+			switch (tmp_reg & PORTSCX_PORT_SPEED_UNDEF) {
+			case PORTSCX_PORT_SPEED_FULL:
+				s = "Full Speed"; break;
+			case PORTSCX_PORT_SPEED_LOW:
+				s = "Low Speed"; break;
+			case PORTSCX_PORT_SPEED_HIGH:
+				s = "High Speed"; break;
+			default:
+				s = "Undefined"; break;
+			}
+			s;
+		} ),
+		(tmp_reg & PORTSCX_PHY_LOW_POWER_SPD) ?
+		"Normal PHY mode" : "Low power mode",
+		(tmp_reg & PORTSCX_PORT_RESET) ? "In Reset" :
+		"Not in Reset",
+		(tmp_reg & PORTSCX_PORT_SUSPEND) ? "In " : "Not in",
+		(tmp_reg & PORTSCX_OVER_CURRENT_CHG) ? "Dected" :
+		"No",
+		(tmp_reg & PORTSCX_PORT_EN_DIS_CHANGE) ? "Disable" :
+		"Not change",
+		(tmp_reg & PORTSCX_PORT_ENABLE) ? "Enable" :
+		"Not correct",
+		(tmp_reg & PORTSCX_CURRENT_CONNECT_STATUS) ?
+		"Attached" : "Not-Att");
+	size -= t;
+	next += t;
+
+	tmp_reg = fsl_readl(&dr_regs->usbmode);
+	t = scnprintf(next, size,
+			"USB Mode Reg:" "Controller Mode is : %s\n\n", ( {
+				char *s;
+				switch (tmp_reg & USB_MODE_CTRL_MODE_HOST) {
+				case USB_MODE_CTRL_MODE_IDLE:
+					s = "Idle"; break;
+				case USB_MODE_CTRL_MODE_DEVICE:
+					s = "Device Controller"; break;
+				case USB_MODE_CTRL_MODE_HOST:
+					s = "Host Controller"; break;
+				default:
+					s = "None"; break;
+				}
+				s;
+			} ));
+	size -= t;
+	next += t;
+
+	tmp_reg = fsl_readl(&dr_regs->endptsetupstat);
+	t = scnprintf(next, size,
+			"Endpoint Setup Status Reg:" "SETUP on ep 0x%x\n\n",
+			(tmp_reg & EP_SETUP_STATUS_MASK));
+	size -= t;
+	next += t;
+
+	for (i = 0; i < udc->max_ep / 2; i++) {
+		tmp_reg = fsl_readl(&dr_regs->endptctrl[i]);
+		t = scnprintf(next, size, "EP Ctrl Reg [0x%x]: = [0x%x]\n",
+				i, tmp_reg);
+		size -= t;
+		next += t;
+	}
+	tmp_reg = fsl_readl(&dr_regs->endpointprime);
+	t = scnprintf(next, size, "EP Prime Reg = [0x%x]\n", tmp_reg);
+	size -= t;
+	next += t;
+
+	tmp_reg = usb_sys_regs->snoop1;
+	t = scnprintf(next, size, "\nSnoop1 Reg : = [0x%x]\n\n", tmp_reg);
+	size -= t;
+	next += t;
+
+	tmp_reg = usb_sys_regs->control;
+	t = scnprintf(next, size, "General Control Reg : = [0x%x]\n\n",
+			tmp_reg);
+	size -= t;
+	next += t;
+
+	/* ------fsl_udc, fsl_ep, fsl_request structure information ----- */
+	ep = &udc->eps[0];
+	t = scnprintf(next, size, "For %s Maxpkt is 0x%x index is 0x%x\n",
+			ep->ep.name, ep_maxpacket(ep), ep_index(ep));
+	size -= t;
+	next += t;
+
+	if (list_empty(&ep->queue)) {
+		t = scnprintf(next, size, "its req queue is empty\n\n");
+		size -= t;
+		next += t;
+	} else {
+		list_for_each_entry(req, &ep->queue, queue) {
+			t = scnprintf(next, size,
+				"req %p actual 0x%x length 0x%x  buf %p\n",
+				&req->req, req->req.actual,
+				req->req.length, req->req.buf);
+			size -= t;
+			next += t;
+		}
+	}
+	/* other gadget->eplist ep */
+	list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
+		if (ep->desc) {
+			t = scnprintf(next, size,
+					"\nFor %s Maxpkt is 0x%x "
+					"index is 0x%x\n",
+					ep->ep.name, ep_maxpacket(ep),
+					ep_index(ep));
+			size -= t;
+			next += t;
+
+			if (list_empty(&ep->queue)) {
+				t = scnprintf(next, size,
+						"its req queue is empty\n\n");
+				size -= t;
+				next += t;
+			} else {
+				list_for_each_entry(req, &ep->queue, queue) {
+					t = scnprintf(next, size,
+						"req %p actual 0x%x length"
+						"0x%x  buf %p\n",
+						&req->req, req->req.actual,
+						req->req.length, req->req.buf);
+					size -= t;
+					next += t;
+					}	/* end for each_entry of ep req */
+				}	/* end for else */
+			}	/* end for if(ep->queue) */
+		}		/* end (ep->desc) */
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	*eof = 1;
+	return count - size;
+}
+
+#define create_proc_file()	create_proc_read_entry(proc_filename, \
+				0, NULL, fsl_proc_read, NULL)
+
+#define remove_proc_file()	remove_proc_entry(proc_filename, NULL)
+
+#else				/* !CONFIG_USB_GADGET_DEBUG_FILES */
+
+#define create_proc_file()	do {} while (0)
+#define remove_proc_file()	do {} while (0)
+
+#endif				/* CONFIG_USB_GADGET_DEBUG_FILES */
+
+/*-------------------------------------------------------------------------*/
+
+/* Release udc structures */
+static void fsl_udc_release(struct device *dev)
+{
+	complete(udc_controller->done);
+	dma_free_coherent(dev, udc_controller->ep_qh_size,
+			udc_controller->ep_qh, udc_controller->ep_qh_dma);
+	kfree(udc_controller);
+}
+
+/******************************************************************
+	Internal structure setup functions
+*******************************************************************/
+/*------------------------------------------------------------------
+ * init resource for globle controller
+ * Return the udc handle on success or NULL on failure
+ ------------------------------------------------------------------*/
+static struct fsl_udc *__init struct_udc_setup(struct platform_device *pdev)
+{
+	struct fsl_udc *udc;
+	struct fsl_usb2_platform_data *pdata;
+	size_t size;
+
+	udc = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL);
+	if (udc == NULL) {
+		ERR("malloc udc failed\n");
+		return NULL;
+	}
+
+	pdata = pdev->dev.platform_data;
+	udc->phy_mode = pdata->phy_mode;
+	/* max_ep_nr is bidirectional ep number, max_ep doubles the number */
+	udc->max_ep = pdata->max_ep_nr * 2;
+
+	udc->eps = kzalloc(sizeof(struct fsl_ep) * udc->max_ep, GFP_KERNEL);
+	if (!udc->eps) {
+		ERR("malloc fsl_ep failed\n");
+		goto cleanup;
+	}
+
+	/* initialized QHs, take care of alignment */
+	size = udc->max_ep * sizeof(struct ep_queue_head);
+	if (size < QH_ALIGNMENT)
+		size = QH_ALIGNMENT;
+	else if ((size % QH_ALIGNMENT) != 0) {
+		size += QH_ALIGNMENT + 1;
+		size &= ~(QH_ALIGNMENT - 1);
+	}
+	udc->ep_qh = dma_alloc_coherent(&pdev->dev, size,
+					&udc->ep_qh_dma, GFP_KERNEL);
+	if (!udc->ep_qh) {
+		ERR("malloc QHs for udc failed\n");
+		kfree(udc->eps);
+		goto cleanup;
+	}
+
+	udc->ep_qh_size = size;
+
+	/* Initialize ep0 status request structure */
+	/* FIXME: fsl_alloc_request() ignores ep argument */
+	udc->status_req = container_of(fsl_alloc_request(NULL, GFP_KERNEL),
+			struct fsl_req, req);
+	/* allocate a small amount of memory to get valid address */
+	udc->status_req->req.buf = kmalloc(8, GFP_KERNEL);
+	udc->status_req->req.dma = virt_to_phys(udc->status_req->req.buf);
+
+	udc->resume_state = USB_STATE_NOTATTACHED;
+	udc->usb_state = USB_STATE_POWERED;
+	udc->ep0_dir = 0;
+	udc->remote_wakeup = 0;	/* default to 0 on reset */
+	spin_lock_init(&udc->lock);
+
+	return udc;
+
+cleanup:
+	kfree(udc);
+	return NULL;
+}
+
+/*----------------------------------------------------------------
+ * Setup the fsl_ep struct for eps
+ * Link fsl_ep->ep to gadget->ep_list
+ * ep0out is not used so do nothing here
+ * ep0in should be taken care
+ *--------------------------------------------------------------*/
+static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index,
+		char *name, int link)
+{
+	struct fsl_ep *ep = &udc->eps[index];
+
+	ep->udc = udc;
+	strcpy(ep->name, name);
+	ep->ep.name = ep->name;
+
+	ep->ep.ops = &fsl_ep_ops;
+	ep->stopped = 0;
+
+	/* for ep0: maxP defined in desc
+	 * for other eps, maxP is set by epautoconfig() called by gadget layer
+	 */
+	ep->ep.maxpacket = (unsigned short) ~0;
+
+	/* the queue lists any req for this ep */
+	INIT_LIST_HEAD(&ep->queue);
+
+	/* gagdet.ep_list used for ep_autoconfig so no ep0 */
+	if (link)
+		list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+	ep->gadget = &udc->gadget;
+	ep->qh = &udc->ep_qh[index];
+
+	return 0;
+}
+
+/* Driver probe function
+ * all intialize operations implemented here except enabling usb_intr reg
+ */
+static int __init fsl_udc_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int ret = -ENODEV;
+	unsigned int i;
+
+	if (strcmp(pdev->name, driver_name)) {
+		VDBG("Wrong device\n");
+		return -ENODEV;
+	}
+
+	/* board setup should have been done in the platform code */
+
+	/* Initialize the udc structure including QH member and other member */
+	udc_controller = struct_udc_setup(pdev);
+	if (!udc_controller) {
+		VDBG("udc_controller is NULL \n");
+		return -ENOMEM;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENXIO;
+
+	if (!request_mem_region(res->start, res->end - res->start + 1,
+				driver_name)) {
+		ERR("request mem region for %s failed \n", pdev->name);
+		return -EBUSY;
+	}
+
+	dr_regs = ioremap(res->start, res->end - res->start + 1);
+	if (!dr_regs) {
+		ret = -ENOMEM;
+		goto err1;
+	}
+
+	usb_sys_regs = (struct usb_sys_interface *)
+			((u32)dr_regs + USB_DR_SYS_OFFSET);
+
+	udc_controller->irq = platform_get_irq(pdev, 0);
+	if (!udc_controller->irq) {
+		ret = -ENODEV;
+		goto err2;
+	}
+
+	ret = request_irq(udc_controller->irq, fsl_udc_irq, SA_SHIRQ,
+			driver_name, udc_controller);
+	if (ret != 0) {
+		ERR("cannot request irq %d err %d \n",
+				udc_controller->irq, ret);
+		goto err2;
+	}
+
+	/* initialize usb hw reg except for regs for EP,
+	 * leave usbintr reg untouched */
+	dr_controller_setup(udc_controller);
+
+	/* Setup gadget structure */
+	udc_controller->gadget.ops = &fsl_gadget_ops;
+	udc_controller->gadget.is_dualspeed = 1;
+	udc_controller->gadget.ep0 = &udc_controller->eps[0].ep;
+	INIT_LIST_HEAD(&udc_controller->gadget.ep_list);
+	udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
+	udc_controller->gadget.name = driver_name;
+
+	/* Setup gadget.dev and register with kernel */
+	strcpy(udc_controller->gadget.dev.bus_id, "gadget");
+	udc_controller->gadget.dev.release = fsl_udc_release;
+	udc_controller->gadget.dev.parent = &pdev->dev;
+	ret = device_register(&udc_controller->gadget.dev);
+	if (ret < 0)
+		goto err3;
+
+	/* setup QH and epctrl for ep0 */
+	ep0_setup(udc_controller);
+
+	/* setup udc->eps[] for ep0 */
+	struct_ep_setup(udc_controller, 0, "ep0", 0);
+	/* for ep0: the desc defined here;
+	 * for other eps, gadget layer called ep_enable with defined desc
+	 */
+	udc_controller->eps[0].desc = &fsl_ep0_desc;
+	udc_controller->eps[0].ep.maxpacket = USB_MAX_CTRL_PAYLOAD;
+
+	/* setup the udc->eps[] for non-control endpoints and link
+	 * to gadget.ep_list */
+	for (i = 1; i < (int)(udc_controller->max_ep / 2); i++) {
+		char name[14];
+
+		sprintf(name, "ep%dout", i);
+		struct_ep_setup(udc_controller, i * 2, name, 1);
+		sprintf(name, "ep%din", i);
+		struct_ep_setup(udc_controller, i * 2 + 1, name, 1);
+	}
+
+	/* use dma_pool for TD management */
+	udc_controller->td_pool = dma_pool_create("udc_td", &pdev->dev,
+			sizeof(struct ep_td_struct),
+			DTD_ALIGNMENT, UDC_DMA_BOUNDARY);
+	if (udc_controller->td_pool == NULL) {
+		ret = -ENOMEM;
+		goto err4;
+	}
+	create_proc_file();
+	return 0;
+
+err4:
+	device_unregister(&udc_controller->gadget.dev);
+err3:
+	free_irq(udc_controller->irq, udc_controller);
+err2:
+	iounmap(dr_regs);
+err1:
+	release_mem_region(res->start, res->end - res->start + 1);
+	return ret;
+}
+
+/* Driver removal function
+ * Free resources and finish pending transactions
+ */
+static int __exit fsl_udc_remove(struct platform_device *pdev)
+{
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	DECLARE_COMPLETION(done);
+
+	if (!udc_controller)
+		return -ENODEV;
+	udc_controller->done = &done;
+
+	/* DR has been stopped in usb_gadget_unregister_driver() */
+	remove_proc_file();
+
+	/* Free allocated memory */
+	kfree(udc_controller->status_req->req.buf);
+	kfree(udc_controller->status_req);
+	kfree(udc_controller->eps);
+
+	dma_pool_destroy(udc_controller->td_pool);
+	free_irq(udc_controller->irq, udc_controller);
+	iounmap(dr_regs);
+	release_mem_region(res->start, res->end - res->start + 1);
+
+	device_unregister(&udc_controller->gadget.dev);
+	/* free udc --wait for the release() finished */
+	wait_for_completion(&done);
+
+	return 0;
+}
+
+/*-----------------------------------------------------------------
+ * Modify Power management attributes
+ * Used by OTG statemachine to disable gadget temporarily
+ -----------------------------------------------------------------*/
+static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	dr_controller_stop(udc_controller);
+	return 0;
+}
+
+/*-----------------------------------------------------------------
+ * Invoked on USB resume. May be called in_interrupt.
+ * Here we start the DR controller and enable the irq
+ *-----------------------------------------------------------------*/
+static int fsl_udc_resume(struct platform_device *pdev)
+{
+	/* Enable DR irq reg and set controller Run */
+	if (udc_controller->stopped) {
+		dr_controller_setup(udc_controller);
+		dr_controller_run(udc_controller);
+	}
+	udc_controller->usb_state = USB_STATE_ATTACHED;
+	udc_controller->ep0_state = WAIT_FOR_SETUP;
+	udc_controller->ep0_dir = 0;
+	return 0;
+}
+
+/*-------------------------------------------------------------------------
+	Register entry point for the peripheral controller driver
+--------------------------------------------------------------------------*/
+
+static struct platform_driver udc_driver = {
+	.remove  = __exit_p(fsl_udc_remove),
+	/* these suspend and resume are not usb suspend and resume */
+	.suspend = fsl_udc_suspend,
+	.resume  = fsl_udc_resume,
+	.driver  = {
+		.name = (char *)driver_name,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init udc_init(void)
+{
+	printk(KERN_INFO "%s (%s)\n", driver_desc, DRIVER_VERSION);
+	return platform_driver_probe(&udc_driver, fsl_udc_probe);
+}
+
+module_init(udc_init);
+
+static void __exit udc_exit(void)
+{
+	platform_driver_unregister(&udc_driver);
+	printk("%s unregistered \n", driver_desc);
+}
+
+module_exit(udc_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h
new file mode 100644
index 0000000..c6291e0
--- /dev/null
+++ b/drivers/usb/gadget/fsl_usb2_udc.h
@@ -0,0 +1,579 @@
+/*
+ * Freescale USB device/endpoint management registers
+ */
+#ifndef __FSL_USB2_UDC_H
+#define __FSL_USB2_UDC_H
+
+/* ### define USB registers here
+ */
+#define USB_MAX_CTRL_PAYLOAD		64
+#define USB_DR_SYS_OFFSET		0x400
+
+ /* USB DR device mode registers (Little Endian) */
+struct usb_dr_device {
+	/* Capability register */
+	u8 res1[256];
+	u16 caplength;		/* Capability Register Length */
+	u16 hciversion;		/* Host Controller Interface Version */
+	u32 hcsparams;		/* Host Controller Structual Parameters */
+	u32 hccparams;		/* Host Controller Capability Parameters */
+	u8 res2[20];
+	u32 dciversion;		/* Device Controller Interface Version */
+	u32 dccparams;		/* Device Controller Capability Parameters */
+	u8 res3[24];
+	/* Operation register */
+	u32 usbcmd;		/* USB Command Register */
+	u32 usbsts;		/* USB Status Register */
+	u32 usbintr;		/* USB Interrupt Enable Register */
+	u32 frindex;		/* Frame Index Register */
+	u8 res4[4];
+	u32 deviceaddr;		/* Device Address */
+	u32 endpointlistaddr;	/* Endpoint List Address Register */
+	u8 res5[4];
+	u32 burstsize;		/* Master Interface Data Burst Size Register */
+	u32 txttfilltuning;	/* Transmit FIFO Tuning Controls Register */
+	u8 res6[24];
+	u32 configflag;		/* Configure Flag Register */
+	u32 portsc1;		/* Port 1 Status and Control Register */
+	u8 res7[28];
+	u32 otgsc;		/* On-The-Go Status and Control */
+	u32 usbmode;		/* USB Mode Register */
+	u32 endptsetupstat;	/* Endpoint Setup Status Register */
+	u32 endpointprime;	/* Endpoint Initialization Register */
+	u32 endptflush;		/* Endpoint Flush Register */
+	u32 endptstatus;	/* Endpoint Status Register */
+	u32 endptcomplete;	/* Endpoint Complete Register */
+	u32 endptctrl[6];	/* Endpoint Control Registers */
+};
+
+ /* USB DR host mode registers (Little Endian) */
+struct usb_dr_host {
+	/* Capability register */
+	u8 res1[256];
+	u16 caplength;		/* Capability Register Length */
+	u16 hciversion;		/* Host Controller Interface Version */
+	u32 hcsparams;		/* Host Controller Structual Parameters */
+	u32 hccparams;		/* Host Controller Capability Parameters */
+	u8 res2[20];
+	u32 dciversion;		/* Device Controller Interface Version */
+	u32 dccparams;		/* Device Controller Capability Parameters */
+	u8 res3[24];
+	/* Operation register */
+	u32 usbcmd;		/* USB Command Register */
+	u32 usbsts;		/* USB Status Register */
+	u32 usbintr;		/* USB Interrupt Enable Register */
+	u32 frindex;		/* Frame Index Register */
+	u8 res4[4];
+	u32 periodiclistbase;	/* Periodic Frame List Base Address Register */
+	u32 asynclistaddr;	/* Current Asynchronous List Address Register */
+	u8 res5[4];
+	u32 burstsize;		/* Master Interface Data Burst Size Register */
+	u32 txttfilltuning;	/* Transmit FIFO Tuning Controls Register */
+	u8 res6[24];
+	u32 configflag;		/* Configure Flag Register */
+	u32 portsc1;		/* Port 1 Status and Control Register */
+	u8 res7[28];
+	u32 otgsc;		/* On-The-Go Status and Control */
+	u32 usbmode;		/* USB Mode Register */
+	u32 endptsetupstat;	/* Endpoint Setup Status Register */
+	u32 endpointprime;	/* Endpoint Initialization Register */
+	u32 endptflush;		/* Endpoint Flush Register */
+	u32 endptstatus;	/* Endpoint Status Register */
+	u32 endptcomplete;	/* Endpoint Complete Register */
+	u32 endptctrl[6];	/* Endpoint Control Registers */
+};
+
+ /* non-EHCI USB system interface registers (Big Endian) */
+struct usb_sys_interface {
+	u32 snoop1;
+	u32 snoop2;
+	u32 age_cnt_thresh;	/* Age Count Threshold Register */
+	u32 pri_ctrl;		/* Priority Control Register */
+	u32 si_ctrl;		/* System Interface Control Register */
+	u8 res[236];
+	u32 control;		/* General Purpose Control Register */
+};
+
+/* ep0 transfer state */
+#define WAIT_FOR_SETUP          0
+#define DATA_STATE_XMIT         1
+#define DATA_STATE_NEED_ZLP     2
+#define WAIT_FOR_OUT_STATUS     3
+#define DATA_STATE_RECV         4
+
+/* Frame Index Register Bit Masks */
+#define	USB_FRINDEX_MASKS			0x3fff
+/* USB CMD  Register Bit Masks */
+#define  USB_CMD_RUN_STOP                     0x00000001
+#define  USB_CMD_CTRL_RESET                   0x00000002
+#define  USB_CMD_PERIODIC_SCHEDULE_EN         0x00000010
+#define  USB_CMD_ASYNC_SCHEDULE_EN            0x00000020
+#define  USB_CMD_INT_AA_DOORBELL              0x00000040
+#define  USB_CMD_ASP                          0x00000300
+#define  USB_CMD_ASYNC_SCH_PARK_EN            0x00000800
+#define  USB_CMD_SUTW                         0x00002000
+#define  USB_CMD_ATDTW                        0x00004000
+#define  USB_CMD_ITC                          0x00FF0000
+
+/* bit 15,3,2 are frame list size */
+#define  USB_CMD_FRAME_SIZE_1024              0x00000000
+#define  USB_CMD_FRAME_SIZE_512               0x00000004
+#define  USB_CMD_FRAME_SIZE_256               0x00000008
+#define  USB_CMD_FRAME_SIZE_128               0x0000000C
+#define  USB_CMD_FRAME_SIZE_64                0x00008000
+#define  USB_CMD_FRAME_SIZE_32                0x00008004
+#define  USB_CMD_FRAME_SIZE_16                0x00008008
+#define  USB_CMD_FRAME_SIZE_8                 0x0000800C
+
+/* bit 9-8 are async schedule park mode count */
+#define  USB_CMD_ASP_00                       0x00000000
+#define  USB_CMD_ASP_01                       0x00000100
+#define  USB_CMD_ASP_10                       0x00000200
+#define  USB_CMD_ASP_11                       0x00000300
+#define  USB_CMD_ASP_BIT_POS                  8
+
+/* bit 23-16 are interrupt threshold control */
+#define  USB_CMD_ITC_NO_THRESHOLD             0x00000000
+#define  USB_CMD_ITC_1_MICRO_FRM              0x00010000
+#define  USB_CMD_ITC_2_MICRO_FRM              0x00020000
+#define  USB_CMD_ITC_4_MICRO_FRM              0x00040000
+#define  USB_CMD_ITC_8_MICRO_FRM              0x00080000
+#define  USB_CMD_ITC_16_MICRO_FRM             0x00100000
+#define  USB_CMD_ITC_32_MICRO_FRM             0x00200000
+#define  USB_CMD_ITC_64_MICRO_FRM             0x00400000
+#define  USB_CMD_ITC_BIT_POS                  16
+
+/* USB STS Register Bit Masks */
+#define  USB_STS_INT                          0x00000001
+#define  USB_STS_ERR                          0x00000002
+#define  USB_STS_PORT_CHANGE                  0x00000004
+#define  USB_STS_FRM_LST_ROLL                 0x00000008
+#define  USB_STS_SYS_ERR                      0x00000010
+#define  USB_STS_IAA                          0x00000020
+#define  USB_STS_RESET                        0x00000040
+#define  USB_STS_SOF                          0x00000080
+#define  USB_STS_SUSPEND                      0x00000100
+#define  USB_STS_HC_HALTED                    0x00001000
+#define  USB_STS_RCL                          0x00002000
+#define  USB_STS_PERIODIC_SCHEDULE            0x00004000
+#define  USB_STS_ASYNC_SCHEDULE               0x00008000
+
+/* USB INTR Register Bit Masks */
+#define  USB_INTR_INT_EN                      0x00000001
+#define  USB_INTR_ERR_INT_EN                  0x00000002
+#define  USB_INTR_PTC_DETECT_EN               0x00000004
+#define  USB_INTR_FRM_LST_ROLL_EN             0x00000008
+#define  USB_INTR_SYS_ERR_EN                  0x00000010
+#define  USB_INTR_ASYN_ADV_EN                 0x00000020
+#define  USB_INTR_RESET_EN                    0x00000040
+#define  USB_INTR_SOF_EN                      0x00000080
+#define  USB_INTR_DEVICE_SUSPEND              0x00000100
+
+/* Device Address bit masks */
+#define  USB_DEVICE_ADDRESS_MASK              0xFE000000
+#define  USB_DEVICE_ADDRESS_BIT_POS           25
+
+/* endpoint list address bit masks */
+#define USB_EP_LIST_ADDRESS_MASK              0xfffff800
+
+/* PORTSCX  Register Bit Masks */
+#define  PORTSCX_CURRENT_CONNECT_STATUS       0x00000001
+#define  PORTSCX_CONNECT_STATUS_CHANGE        0x00000002
+#define  PORTSCX_PORT_ENABLE                  0x00000004
+#define  PORTSCX_PORT_EN_DIS_CHANGE           0x00000008
+#define  PORTSCX_OVER_CURRENT_ACT             0x00000010
+#define  PORTSCX_OVER_CURRENT_CHG             0x00000020
+#define  PORTSCX_PORT_FORCE_RESUME            0x00000040
+#define  PORTSCX_PORT_SUSPEND                 0x00000080
+#define  PORTSCX_PORT_RESET                   0x00000100
+#define  PORTSCX_LINE_STATUS_BITS             0x00000C00
+#define  PORTSCX_PORT_POWER                   0x00001000
+#define  PORTSCX_PORT_INDICTOR_CTRL           0x0000C000
+#define  PORTSCX_PORT_TEST_CTRL               0x000F0000
+#define  PORTSCX_WAKE_ON_CONNECT_EN           0x00100000
+#define  PORTSCX_WAKE_ON_CONNECT_DIS          0x00200000
+#define  PORTSCX_WAKE_ON_OVER_CURRENT         0x00400000
+#define  PORTSCX_PHY_LOW_POWER_SPD            0x00800000
+#define  PORTSCX_PORT_FORCE_FULL_SPEED        0x01000000
+#define  PORTSCX_PORT_SPEED_MASK              0x0C000000
+#define  PORTSCX_PORT_WIDTH                   0x10000000
+#define  PORTSCX_PHY_TYPE_SEL                 0xC0000000
+
+/* bit 11-10 are line status */
+#define  PORTSCX_LINE_STATUS_SE0              0x00000000
+#define  PORTSCX_LINE_STATUS_JSTATE           0x00000400
+#define  PORTSCX_LINE_STATUS_KSTATE           0x00000800
+#define  PORTSCX_LINE_STATUS_UNDEF            0x00000C00
+#define  PORTSCX_LINE_STATUS_BIT_POS          10
+
+/* bit 15-14 are port indicator control */
+#define  PORTSCX_PIC_OFF                      0x00000000
+#define  PORTSCX_PIC_AMBER                    0x00004000
+#define  PORTSCX_PIC_GREEN                    0x00008000
+#define  PORTSCX_PIC_UNDEF                    0x0000C000
+#define  PORTSCX_PIC_BIT_POS                  14
+
+/* bit 19-16 are port test control */
+#define  PORTSCX_PTC_DISABLE                  0x00000000
+#define  PORTSCX_PTC_JSTATE                   0x00010000
+#define  PORTSCX_PTC_KSTATE                   0x00020000
+#define  PORTSCX_PTC_SEQNAK                   0x00030000
+#define  PORTSCX_PTC_PACKET                   0x00040000
+#define  PORTSCX_PTC_FORCE_EN                 0x00050000
+#define  PORTSCX_PTC_BIT_POS                  16
+
+/* bit 27-26 are port speed */
+#define  PORTSCX_PORT_SPEED_FULL              0x00000000
+#define  PORTSCX_PORT_SPEED_LOW               0x04000000
+#define  PORTSCX_PORT_SPEED_HIGH              0x08000000
+#define  PORTSCX_PORT_SPEED_UNDEF             0x0C000000
+#define  PORTSCX_SPEED_BIT_POS                26
+
+/* bit 28 is parallel transceiver width for UTMI interface */
+#define  PORTSCX_PTW                          0x10000000
+#define  PORTSCX_PTW_8BIT                     0x00000000
+#define  PORTSCX_PTW_16BIT                    0x10000000
+
+/* bit 31-30 are port transceiver select */
+#define  PORTSCX_PTS_UTMI                     0x00000000
+#define  PORTSCX_PTS_ULPI                     0x80000000
+#define  PORTSCX_PTS_FSLS                     0xC0000000
+#define  PORTSCX_PTS_BIT_POS                  30
+
+/* otgsc Register Bit Masks */
+#define  OTGSC_CTRL_VUSB_DISCHARGE            0x00000001
+#define  OTGSC_CTRL_VUSB_CHARGE               0x00000002
+#define  OTGSC_CTRL_OTG_TERM                  0x00000008
+#define  OTGSC_CTRL_DATA_PULSING              0x00000010
+#define  OTGSC_STS_USB_ID                     0x00000100
+#define  OTGSC_STS_A_VBUS_VALID               0x00000200
+#define  OTGSC_STS_A_SESSION_VALID            0x00000400
+#define  OTGSC_STS_B_SESSION_VALID            0x00000800
+#define  OTGSC_STS_B_SESSION_END              0x00001000
+#define  OTGSC_STS_1MS_TOGGLE                 0x00002000
+#define  OTGSC_STS_DATA_PULSING               0x00004000
+#define  OTGSC_INTSTS_USB_ID                  0x00010000
+#define  OTGSC_INTSTS_A_VBUS_VALID            0x00020000
+#define  OTGSC_INTSTS_A_SESSION_VALID         0x00040000
+#define  OTGSC_INTSTS_B_SESSION_VALID         0x00080000
+#define  OTGSC_INTSTS_B_SESSION_END           0x00100000
+#define  OTGSC_INTSTS_1MS                     0x00200000
+#define  OTGSC_INTSTS_DATA_PULSING            0x00400000
+#define  OTGSC_INTR_USB_ID                    0x01000000
+#define  OTGSC_INTR_A_VBUS_VALID              0x02000000
+#define  OTGSC_INTR_A_SESSION_VALID           0x04000000
+#define  OTGSC_INTR_B_SESSION_VALID           0x08000000
+#define  OTGSC_INTR_B_SESSION_END             0x10000000
+#define  OTGSC_INTR_1MS_TIMER                 0x20000000
+#define  OTGSC_INTR_DATA_PULSING              0x40000000
+
+/* USB MODE Register Bit Masks */
+#define  USB_MODE_CTRL_MODE_IDLE              0x00000000
+#define  USB_MODE_CTRL_MODE_DEVICE            0x00000002
+#define  USB_MODE_CTRL_MODE_HOST              0x00000003
+#define  USB_MODE_CTRL_MODE_RSV               0x00000001
+#define  USB_MODE_SETUP_LOCK_OFF              0x00000008
+#define  USB_MODE_STREAM_DISABLE              0x00000010
+/* Endpoint Flush Register */
+#define EPFLUSH_TX_OFFSET		      0x00010000
+#define EPFLUSH_RX_OFFSET		      0x00000000
+
+/* Endpoint Setup Status bit masks */
+#define  EP_SETUP_STATUS_MASK                 0x0000003F
+#define  EP_SETUP_STATUS_EP0		      0x00000001
+
+/* ENDPOINTCTRLx  Register Bit Masks */
+#define  EPCTRL_TX_ENABLE                     0x00800000
+#define  EPCTRL_TX_DATA_TOGGLE_RST            0x00400000	/* Not EP0 */
+#define  EPCTRL_TX_DATA_TOGGLE_INH            0x00200000	/* Not EP0 */
+#define  EPCTRL_TX_TYPE                       0x000C0000
+#define  EPCTRL_TX_DATA_SOURCE                0x00020000	/* Not EP0 */
+#define  EPCTRL_TX_EP_STALL                   0x00010000
+#define  EPCTRL_RX_ENABLE                     0x00000080
+#define  EPCTRL_RX_DATA_TOGGLE_RST            0x00000040	/* Not EP0 */
+#define  EPCTRL_RX_DATA_TOGGLE_INH            0x00000020	/* Not EP0 */
+#define  EPCTRL_RX_TYPE                       0x0000000C
+#define  EPCTRL_RX_DATA_SINK                  0x00000002	/* Not EP0 */
+#define  EPCTRL_RX_EP_STALL                   0x00000001
+
+/* bit 19-18 and 3-2 are endpoint type */
+#define  EPCTRL_EP_TYPE_CONTROL               0
+#define  EPCTRL_EP_TYPE_ISO                   1
+#define  EPCTRL_EP_TYPE_BULK                  2
+#define  EPCTRL_EP_TYPE_INTERRUPT             3
+#define  EPCTRL_TX_EP_TYPE_SHIFT              18
+#define  EPCTRL_RX_EP_TYPE_SHIFT              2
+
+/* SNOOPn Register Bit Masks */
+#define  SNOOP_ADDRESS_MASK                   0xFFFFF000
+#define  SNOOP_SIZE_ZERO                      0x00	/* snooping disable */
+#define  SNOOP_SIZE_4KB                       0x0B	/* 4KB snoop size */
+#define  SNOOP_SIZE_8KB                       0x0C
+#define  SNOOP_SIZE_16KB                      0x0D
+#define  SNOOP_SIZE_32KB                      0x0E
+#define  SNOOP_SIZE_64KB                      0x0F
+#define  SNOOP_SIZE_128KB                     0x10
+#define  SNOOP_SIZE_256KB                     0x11
+#define  SNOOP_SIZE_512KB                     0x12
+#define  SNOOP_SIZE_1MB                       0x13
+#define  SNOOP_SIZE_2MB                       0x14
+#define  SNOOP_SIZE_4MB                       0x15
+#define  SNOOP_SIZE_8MB                       0x16
+#define  SNOOP_SIZE_16MB                      0x17
+#define  SNOOP_SIZE_32MB                      0x18
+#define  SNOOP_SIZE_64MB                      0x19
+#define  SNOOP_SIZE_128MB                     0x1A
+#define  SNOOP_SIZE_256MB                     0x1B
+#define  SNOOP_SIZE_512MB                     0x1C
+#define  SNOOP_SIZE_1GB                       0x1D
+#define  SNOOP_SIZE_2GB                       0x1E	/* 2GB snoop size */
+
+/* pri_ctrl Register Bit Masks */
+#define  PRI_CTRL_PRI_LVL1                    0x0000000C
+#define  PRI_CTRL_PRI_LVL0                    0x00000003
+
+/* si_ctrl Register Bit Masks */
+#define  SI_CTRL_ERR_DISABLE                  0x00000010
+#define  SI_CTRL_IDRC_DISABLE                 0x00000008
+#define  SI_CTRL_RD_SAFE_EN                   0x00000004
+#define  SI_CTRL_RD_PREFETCH_DISABLE          0x00000002
+#define  SI_CTRL_RD_PREFEFETCH_VAL            0x00000001
+
+/* control Register Bit Masks */
+#define  USB_CTRL_IOENB                       0x00000004
+#define  USB_CTRL_ULPI_INT0EN                 0x00000001
+
+/* Endpoint Queue Head data struct
+ * Rem: all the variables of qh are LittleEndian Mode
+ * and NEXT_POINTER_MASK should operate on a LittleEndian, Phy Addr
+ */
+struct ep_queue_head {
+	u32 max_pkt_length;	/* Mult(31-30) , Zlt(29) , Max Pkt len
+				   and IOS(15) */
+	u32 curr_dtd_ptr;	/* Current dTD Pointer(31-5) */
+	u32 next_dtd_ptr;	/* Next dTD Pointer(31-5), T(0) */
+	u32 size_ioc_int_sts;	/* Total bytes (30-16), IOC (15),
+				   MultO(11-10), STS (7-0)  */
+	u32 buff_ptr0;		/* Buffer pointer Page 0 (31-12) */
+	u32 buff_ptr1;		/* Buffer pointer Page 1 (31-12) */
+	u32 buff_ptr2;		/* Buffer pointer Page 2 (31-12) */
+	u32 buff_ptr3;		/* Buffer pointer Page 3 (31-12) */
+	u32 buff_ptr4;		/* Buffer pointer Page 4 (31-12) */
+	u32 res1;
+	u8 setup_buffer[8];	/* Setup data 8 bytes */
+	u32 res2[4];
+};
+
+/* Endpoint Queue Head Bit Masks */
+#define  EP_QUEUE_HEAD_MULT_POS               30
+#define  EP_QUEUE_HEAD_ZLT_SEL                0x20000000
+#define  EP_QUEUE_HEAD_MAX_PKT_LEN_POS        16
+#define  EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info)   (((ep_info)>>16)&0x07ff)
+#define  EP_QUEUE_HEAD_IOS                    0x00008000
+#define  EP_QUEUE_HEAD_NEXT_TERMINATE         0x00000001
+#define  EP_QUEUE_HEAD_IOC                    0x00008000
+#define  EP_QUEUE_HEAD_MULTO                  0x00000C00
+#define  EP_QUEUE_HEAD_STATUS_HALT	      0x00000040
+#define  EP_QUEUE_HEAD_STATUS_ACTIVE          0x00000080
+#define  EP_QUEUE_CURRENT_OFFSET_MASK         0x00000FFF
+#define  EP_QUEUE_HEAD_NEXT_POINTER_MASK      0xFFFFFFE0
+#define  EP_QUEUE_FRINDEX_MASK                0x000007FF
+#define  EP_MAX_LENGTH_TRANSFER               0x4000
+
+/* Endpoint Transfer Descriptor data struct */
+/* Rem: all the variables of td are LittleEndian Mode */
+struct ep_td_struct {
+	u32 next_td_ptr;	/* Next TD pointer(31-5), T(0) set
+				   indicate invalid */
+	u32 size_ioc_sts;	/* Total bytes (30-16), IOC (15),
+				   MultO(11-10), STS (7-0)  */
+	u32 buff_ptr0;		/* Buffer pointer Page 0 */
+	u32 buff_ptr1;		/* Buffer pointer Page 1 */
+	u32 buff_ptr2;		/* Buffer pointer Page 2 */
+	u32 buff_ptr3;		/* Buffer pointer Page 3 */
+	u32 buff_ptr4;		/* Buffer pointer Page 4 */
+	u32 res;
+	/* 32 bytes */
+	dma_addr_t td_dma;	/* dma address for this td */
+	/* virtual address of next td specified in next_td_ptr */
+	struct ep_td_struct *next_td_virt;
+};
+
+/* Endpoint Transfer Descriptor bit Masks */
+#define  DTD_NEXT_TERMINATE                   0x00000001
+#define  DTD_IOC                              0x00008000
+#define  DTD_STATUS_ACTIVE                    0x00000080
+#define  DTD_STATUS_HALTED                    0x00000040
+#define  DTD_STATUS_DATA_BUFF_ERR             0x00000020
+#define  DTD_STATUS_TRANSACTION_ERR           0x00000008
+#define  DTD_RESERVED_FIELDS                  0x80007300
+#define  DTD_ADDR_MASK                        0xFFFFFFE0
+#define  DTD_PACKET_SIZE                      0x7FFF0000
+#define  DTD_LENGTH_BIT_POS                   16
+#define  DTD_ERROR_MASK                       (DTD_STATUS_HALTED | \
+                                               DTD_STATUS_DATA_BUFF_ERR | \
+                                               DTD_STATUS_TRANSACTION_ERR)
+/* Alignment requirements; must be a power of two */
+#define DTD_ALIGNMENT				0x20
+#define QH_ALIGNMENT				2048
+
+/* Controller dma boundary */
+#define UDC_DMA_BOUNDARY			0x1000
+
+/* -----------------------------------------------------------------------*/
+/* ##### enum data
+*/
+typedef enum {
+	e_ULPI,
+	e_UTMI_8BIT,
+	e_UTMI_16BIT,
+	e_SERIAL
+} e_PhyInterface;
+
+/*-------------------------------------------------------------------------*/
+
+/* ### driver private data
+ */
+struct fsl_req {
+	struct usb_request req;
+	struct list_head queue;
+	/* ep_queue() func will add
+	   a request->queue into a udc_ep->queue 'd tail */
+	struct fsl_ep *ep;
+	unsigned mapped:1;
+
+	struct ep_td_struct *head, *tail;	/* For dTD List
+						   cpu endian Virtual addr */
+	unsigned int dtd_count;
+};
+
+#define REQ_UNCOMPLETE			1
+
+struct fsl_ep {
+	struct usb_ep ep;
+	struct list_head queue;
+	struct fsl_udc *udc;
+	struct ep_queue_head *qh;
+	const struct usb_endpoint_descriptor *desc;
+	struct usb_gadget *gadget;
+
+	char name[14];
+	unsigned stopped:1;
+};
+
+#define EP_DIR_IN	1
+#define EP_DIR_OUT	0
+
+struct fsl_udc {
+
+	struct usb_gadget gadget;
+	struct usb_gadget_driver *driver;
+	struct fsl_ep *eps;
+	unsigned int max_ep;
+	unsigned int irq;
+
+	struct usb_ctrlrequest local_setup_buff;
+	spinlock_t lock;
+	struct otg_transceiver *transceiver;
+	unsigned softconnect:1;
+	unsigned vbus_active:1;
+	unsigned stopped:1;
+	unsigned remote_wakeup:1;
+
+	struct ep_queue_head *ep_qh;	/* Endpoints Queue-Head */
+	struct fsl_req *status_req;	/* ep0 status request */
+	struct dma_pool *td_pool;	/* dma pool for DTD */
+	enum fsl_usb2_phy_modes phy_mode;
+
+	size_t ep_qh_size;		/* size after alignment adjustment*/
+	dma_addr_t ep_qh_dma;		/* dma address of QH */
+
+	u32 max_pipes;		/* Device max pipes */
+	u32 max_use_endpts;	/* Max endpointes to be used */
+	u32 bus_reset;		/* Device is bus reseting */
+	u32 resume_state;	/* USB state to resume */
+	u32 usb_state;		/* USB current state */
+	u32 usb_next_state;	/* USB next state */
+	u32 ep0_state;		/* Endpoint zero state */
+	u32 ep0_dir;		/* Endpoint zero direction: can be
+				   USB_DIR_IN or USB_DIR_OUT */
+	u32 usb_sof_count;	/* SOF count */
+	u32 errors;		/* USB ERRORs count */
+	u8 device_address;	/* Device USB address */
+
+	struct completion *done;	/* to make sure release() is done */
+};
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+#define DBG(fmt, args...) 	printk(KERN_DEBUG "[%s]  " fmt "\n", \
+				__FUNCTION__, ## args)
+#else
+#define DBG(fmt, args...)	do{}while(0)
+#endif
+
+#if 0
+static void dump_msg(const char *label, const u8 * buf, unsigned int length)
+{
+	unsigned int start, num, i;
+	char line[52], *p;
+
+	if (length >= 512)
+		return;
+	DBG("%s, length %u:\n", label, length);
+	start = 0;
+	while (length > 0) {
+		num = min(length, 16u);
+		p = line;
+		for (i = 0; i < num; ++i) {
+			if (i == 8)
+				*p++ = ' ';
+			sprintf(p, " %02x", buf[i]);
+			p += 3;
+		}
+		*p = 0;
+		printk(KERN_DEBUG "%6x: %s\n", start, line);
+		buf += num;
+		start += num;
+		length -= num;
+	}
+}
+#endif
+
+#ifdef VERBOSE
+#define VDBG		DBG
+#else
+#define VDBG(stuff...)	do{}while(0)
+#endif
+
+#define ERR(stuff...)		printk(KERN_ERR "udc: " stuff)
+#define WARN(stuff...)		printk(KERN_WARNING "udc: " stuff)
+#define INFO(stuff...)		printk(KERN_INFO "udc: " stuff)
+
+/*-------------------------------------------------------------------------*/
+
+/* ### Add board specific defines here
+ */
+
+/*
+ * ### pipe direction macro from device view
+ */
+#define USB_RECV	0	/* OUT EP */
+#define USB_SEND	1	/* IN EP */
+
+/*
+ * ### internal used help routines.
+ */
+#define ep_index(EP)		((EP)->desc->bEndpointAddress&0xF)
+#define ep_maxpacket(EP)	((EP)->ep.maxpacket)
+#define ep_is_in(EP)	( (ep_index(EP) == 0) ? (EP->udc->ep0_dir == \
+			USB_DIR_IN ):((EP)->desc->bEndpointAddress \
+			& USB_DIR_IN)==USB_DIR_IN)
+#define get_ep_by_pipe(udc, pipe)	((pipe == 1)? &udc->eps[0]: \
+					&udc->eps[pipe])
+#define get_pipe_by_windex(windex)	((windex & USB_ENDPOINT_NUMBER_MASK) \
+					* 2 + ((windex & USB_DIR_IN) ? 1 : 0))
+#define get_pipe_by_ep(EP)	(ep_index(EP) * 2 + ep_is_in(EP))
+
+#endif
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index 2e3d662..d041b91 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -99,6 +99,12 @@
 #define gadget_is_imx(g)	0
 #endif
 
+#ifdef CONFIG_USB_GADGET_FSL_USB2
+#define gadget_is_fsl_usb2(g)	!strcmp("fsl-usb2-udc", (g)->name)
+#else
+#define gadget_is_fsl_usb2(g)	0
+#endif
+
 /* Mentor high speed function controller */
 #ifdef CONFIG_USB_GADGET_MUSBHSFC
 #define gadget_is_musbhsfc(g)	!strcmp("musbhsfc_udc", (g)->name)
@@ -177,5 +183,7 @@
 		return 0x17;
 	else if (gadget_is_husb2dev(gadget))
 		return 0x18;
+	else if (gadget_is_fsl_usb2(gadget))
+		return 0x19;
 	return -ENOENT;
 }
diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c
index f01890d..2c043a1 100644
--- a/drivers/usb/gadget/pxa2xx_udc.c
+++ b/drivers/usb/gadget/pxa2xx_udc.c
@@ -71,7 +71,7 @@
  * by the host to interact with this device, and allocates endpoints to
  * the different protocol interfaces.  The controller driver virtualizes
  * usb hardware so that the gadget drivers will be more portable.
- * 
+ *
  * This UDC hardware wants to implement a bit too much USB protocol, so
  * it constrains the sorts of USB configuration change events that work.
  * The errata for these chips are misleading; some "fixed" bugs from
@@ -141,7 +141,7 @@
 #endif
 
 /* ---------------------------------------------------------------------------
- * 	endpoint related parts of the api to the usb controller hardware,
+ *	endpoint related parts of the api to the usb controller hardware,
  *	used by gadget driver; and the inner talker-to-hardware core.
  * ---------------------------------------------------------------------------
  */
@@ -293,7 +293,7 @@
 
 #ifdef	USE_DMA
 	/* for (some) bulk and ISO endpoints, try to get a DMA channel and
-	 * bind it to the endpoint.  otherwise use PIO. 
+	 * bind it to the endpoint.  otherwise use PIO.
 	 */
 	switch (ep->bmAttributes) {
 	case USB_ENDPOINT_XFER_ISOC:
@@ -304,7 +304,7 @@
 		if (!use_dma || !ep->reg_drcmr)
 			break;
 		ep->dma = pxa_request_dma ((char *)_ep->name,
- 				(le16_to_cpu (desc->wMaxPacketSize) > 64)
+				(le16_to_cpu (desc->wMaxPacketSize) > 64)
 					? DMA_PRIO_MEDIUM /* some iso */
 					: DMA_PRIO_LOW,
 				dma_nodesc_handler, ep);
@@ -361,7 +361,7 @@
  */
 
 /*
- * 	pxa2xx_ep_alloc_request - allocate a request data structure
+ *	pxa2xx_ep_alloc_request - allocate a request data structure
  */
 static struct usb_request *
 pxa2xx_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags)
@@ -378,7 +378,7 @@
 
 
 /*
- * 	pxa2xx_ep_free_request - deallocate a request data structure
+ *	pxa2xx_ep_free_request - deallocate a request data structure
  */
 static void
 pxa2xx_ep_free_request (struct usb_ep *_ep, struct usb_request *_req)
@@ -1031,7 +1031,7 @@
 
 
 /*
- * 	nuke - dequeue ALL requests
+ *	nuke - dequeue ALL requests
  */
 static void nuke(struct pxa2xx_ep *ep, int status)
 {
@@ -1136,16 +1136,16 @@
 		ep->dev->req_pending = 0;
 		ep->dev->ep0state = EP0_STALL;
 
- 	/* and bulk/intr endpoints like dropping stalls too */
- 	} else {
- 		unsigned i;
- 		for (i = 0; i < 1000; i += 20) {
- 			if (*ep->reg_udccs & UDCCS_BI_SST)
- 				break;
- 			udelay(20);
- 		}
-  	}
- 	local_irq_restore(flags);
+	/* and bulk/intr endpoints like dropping stalls too */
+	} else {
+		unsigned i;
+		for (i = 0; i < 1000; i += 20) {
+			if (*ep->reg_udccs & UDCCS_BI_SST)
+				break;
+			udelay(20);
+		}
+	}
+	local_irq_restore(flags);
 
 	DBG(DBG_VERBOSE, "%s halt\n", _ep->name);
 	return 0;
@@ -1216,7 +1216,7 @@
 
 
 /* ---------------------------------------------------------------------------
- * 	device-scoped parts of the api to the usb controller hardware
+ *	device-scoped parts of the api to the usb controller hardware
  * ---------------------------------------------------------------------------
  */
 
@@ -1239,7 +1239,7 @@
 static void udc_disable(struct pxa2xx_udc *);
 
 /* We disable the UDC -- and its 48 MHz clock -- whenever it's not
- * in active use.  
+ * in active use.
  */
 static int pullup(struct pxa2xx_udc *udc, int is_active)
 {
@@ -1464,24 +1464,10 @@
 
 #endif	/* CONFIG_USB_GADGET_DEBUG_FILES */
 
-/* "function" sysfs attribute */
-static ssize_t
-show_function (struct device *_dev, struct device_attribute *attr, char *buf)
-{
-	struct pxa2xx_udc	*dev = dev_get_drvdata (_dev);
-
-	if (!dev->driver
-			|| !dev->driver->function
-			|| strlen (dev->driver->function) > PAGE_SIZE)
-		return 0;
-	return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function);
-}
-static DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
-
 /*-------------------------------------------------------------------------*/
 
 /*
- * 	udc_disable - disable USB device controller
+ *	udc_disable - disable USB device controller
  */
 static void udc_disable(struct pxa2xx_udc *dev)
 {
@@ -1507,7 +1493,7 @@
 
 
 /*
- * 	udc_reinit - initialize software state
+ *	udc_reinit - initialize software state
  */
 static void udc_reinit(struct pxa2xx_udc *dev)
 {
@@ -1635,18 +1621,20 @@
 	dev->gadget.dev.driver = &driver->driver;
 	dev->pullup = 1;
 
-	device_add (&dev->gadget.dev);
+	retval = device_add (&dev->gadget.dev);
+	if (retval) {
+fail:
+		dev->driver = NULL;
+		dev->gadget.dev.driver = NULL;
+		return retval;
+	}
 	retval = driver->bind(&dev->gadget);
 	if (retval) {
 		DMSG("bind to driver %s --> error %d\n",
 				driver->driver.name, retval);
 		device_del (&dev->gadget.dev);
-
-		dev->driver = NULL;
-		dev->gadget.dev.driver = NULL;
-		return retval;
+		goto fail;
 	}
-	device_create_file(dev->dev, &dev_attr_function);
 
 	/* ... then enable host detection and ep0; and we're ready
 	 * for set_configuration as well as eventual disconnect.
@@ -1704,7 +1692,6 @@
 	dev->driver = NULL;
 
 	device_del (&dev->gadget.dev);
-	device_remove_file(dev->dev, &dev_attr_function);
 
 	DMSG("unregistered gadget driver '%s'\n", driver->driver.name);
 	dump_state(dev);
@@ -2474,12 +2461,12 @@
 #define IXP465_AD		0x00000200
 
 /*
- * 	probe - binds to the platform device
+ *	probe - binds to the platform device
  */
 static int __init pxa2xx_udc_probe(struct platform_device *pdev)
 {
 	struct pxa2xx_udc *dev = &memory;
-	int retval, out_dma = 1, vbus_irq;
+	int retval, out_dma = 1, vbus_irq, irq;
 	u32 chiprev;
 
 	/* insist on Intel/ARM/XScale */
@@ -2522,7 +2509,11 @@
 		return -ENODEV;
 	}
 
-	pr_debug("%s: IRQ %d%s%s%s\n", driver_name, IRQ_USB,
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return -ENODEV;
+
+	pr_debug("%s: IRQ %d%s%s%s\n", driver_name, irq,
 		dev->has_cfr ? "" : " (!cfr)",
 		out_dma ? "" : " (broken dma-out)",
 		SIZE_STR DMASTR
@@ -2570,11 +2561,11 @@
 	dev->vbus = is_vbus_present();
 
 	/* irq setup after old hardware state is cleaned up */
-	retval = request_irq(IRQ_USB, pxa2xx_udc_irq,
+	retval = request_irq(irq, pxa2xx_udc_irq,
 			IRQF_DISABLED, driver_name, dev);
 	if (retval != 0) {
-		printk(KERN_ERR "%s: can't get irq %i, err %d\n",
-			driver_name, IRQ_USB, retval);
+		printk(KERN_ERR "%s: can't get irq %d, err %d\n",
+			driver_name, irq, retval);
 		return -EBUSY;
 	}
 	dev->got_irq = 1;
@@ -2589,7 +2580,7 @@
 			printk(KERN_ERR "%s: can't get irq %i, err %d\n",
 				driver_name, LUBBOCK_USB_DISC_IRQ, retval);
 lubbock_fail0:
-			free_irq(IRQ_USB, dev);
+			free_irq(irq, dev);
 			return -EBUSY;
 		}
 		retval = request_irq(LUBBOCK_USB_IRQ,
@@ -2616,7 +2607,7 @@
 		if (retval != 0) {
 			printk(KERN_ERR "%s: can't get irq %i, err %d\n",
 				driver_name, vbus_irq, retval);
-			free_irq(IRQ_USB, dev);
+			free_irq(irq, dev);
 			return -EBUSY;
 		}
 	}
@@ -2641,7 +2632,7 @@
 	remove_proc_files();
 
 	if (dev->got_irq) {
-		free_irq(IRQ_USB, dev);
+		free_irq(platform_get_irq(pdev, 0), dev);
 		dev->got_irq = 0;
 	}
 #ifdef CONFIG_ARCH_LUBBOCK
@@ -2668,7 +2659,7 @@
  *
  * For now, we punt and forcibly disconnect from the USB host when PXA
  * enters any suspend state.  While we're disconnected, we always disable
- * the 48MHz USB clock ... allowing PXA sleep and/or 33 MHz idle states. 
+ * the 48MHz USB clock ... allowing PXA sleep and/or 33 MHz idle states.
  * Boards without software pullup control shouldn't use those states.
  * VBUS IRQs should probably be ignored so that the PXA device just acts
  * "dead" to USB hosts until system resume.
@@ -2701,7 +2692,6 @@
 /*-------------------------------------------------------------------------*/
 
 static struct platform_driver udc_driver = {
-	.probe		= pxa2xx_udc_probe,
 	.shutdown	= pxa2xx_udc_shutdown,
 	.remove		= __exit_p(pxa2xx_udc_remove),
 	.suspend	= pxa2xx_udc_suspend,
@@ -2715,7 +2705,7 @@
 static int __init udc_init(void)
 {
 	printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION);
-	return platform_driver_register(&udc_driver);
+	return platform_driver_probe(&udc_driver, pxa2xx_udc_probe);
 }
 module_init(udc_init);
 
diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h
index 4c3c725..397b149 100644
--- a/drivers/usb/gadget/rndis.h
+++ b/drivers/usb/gadget/rndis.h
@@ -195,7 +195,7 @@
 	__le32	PerPacketInfoLength;
 	__le32	VcHandle;
 	__le32	Reserved;
-};
+} __attribute__ ((packed));
 
 struct rndis_config_parameter
 {
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index a2e58c8..2ff396bd 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -15,4 +15,3 @@
 obj-$(CONFIG_USB_SL811_HCD)	+= sl811-hcd.o
 obj-$(CONFIG_USB_SL811_CS)	+= sl811_cs.o
 obj-$(CONFIG_USB_U132_HCD)	+= u132-hcd.o
-obj-$(CONFIG_ETRAX_ARCH_V10)	+= hc_crisv10.o
diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h
index caac0d1..f28736a 100644
--- a/drivers/usb/host/ehci-fsl.h
+++ b/drivers/usb/host/ehci-fsl.h
@@ -31,7 +31,7 @@
 #define FSL_SOC_USB_SNOOP1	0x400	/* NOTE: big-endian */
 #define FSL_SOC_USB_SNOOP2	0x404	/* NOTE: big-endian */
 #define FSL_SOC_USB_AGECNTTHRSH	0x408	/* NOTE: big-endian */
-#define FSL_SOC_USB_SICTRL	0x40c	/* NOTE: big-endian */
-#define FSL_SOC_USB_PRICTRL	0x410	/* NOTE: big-endian */
+#define FSL_SOC_USB_PRICTRL	0x40c	/* NOTE: big-endian */
+#define FSL_SOC_USB_SICTRL	0x410	/* NOTE: big-endian */
 #define FSL_SOC_USB_CTRL	0x500	/* NOTE: big-endian */
 #endif				/* _EHCI_FSL_H */
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 1813b7ca..f4d301b 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -136,6 +136,10 @@
 	/* restore CMD_RUN, framelist size, and irq threshold */
 	ehci_writel(ehci, ehci->command, &ehci->regs->command);
 
+	/* Some controller/firmware combinations need a delay during which
+	 * they set up the port statuses.  See Bugzilla #8190. */
+	mdelay(8);
+
 	/* manually resume the ports we suspended during bus_suspend() */
 	i = HCS_N_PORTS (ehci->hcs_params);
 	while (i--) {
diff --git a/drivers/usb/host/hc_crisv10.c b/drivers/usb/host/hc_crisv10.c
deleted file mode 100644
index 32f7caf..0000000
--- a/drivers/usb/host/hc_crisv10.c
+++ /dev/null
@@ -1,4550 +0,0 @@
-/*
- * usb-host.c: ETRAX 100LX USB Host Controller Driver (HCD)
- *
- * Copyright (c) 2002, 2003 Axis Communications AB.
- */
-
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/unistd.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/spinlock.h>
-
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/dma.h>
-#include <asm/system.h>
-#include <asm/arch/svinto.h>
-
-#include <linux/usb.h>
-/* Ugly include because we don't live with the other host drivers. */
-#include <../drivers/usb/core/hcd.h>
-#include <../drivers/usb/core/usb.h>
-
-#include "hc_crisv10.h"
-
-#define ETRAX_USB_HC_IRQ USB_HC_IRQ_NBR
-#define ETRAX_USB_RX_IRQ USB_DMA_RX_IRQ_NBR
-#define ETRAX_USB_TX_IRQ USB_DMA_TX_IRQ_NBR
-
-static const char *usb_hcd_version = "$Revision: 1.2 $";
-
-#undef KERN_DEBUG
-#define KERN_DEBUG ""
-
-
-#undef USB_DEBUG_RH
-#undef USB_DEBUG_EPID
-#undef USB_DEBUG_SB
-#undef USB_DEBUG_DESC
-#undef USB_DEBUG_URB
-#undef USB_DEBUG_TRACE
-#undef USB_DEBUG_BULK
-#undef USB_DEBUG_CTRL
-#undef USB_DEBUG_INTR
-#undef USB_DEBUG_ISOC
-
-#ifdef USB_DEBUG_RH
-#define dbg_rh(format, arg...) printk(KERN_DEBUG __FILE__ ": (RH) " format "\n" , ## arg)
-#else
-#define dbg_rh(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_EPID
-#define dbg_epid(format, arg...) printk(KERN_DEBUG __FILE__ ": (EPID) " format "\n" , ## arg)
-#else
-#define dbg_epid(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_SB
-#define dbg_sb(format, arg...) printk(KERN_DEBUG __FILE__ ": (SB) " format "\n" , ## arg)
-#else
-#define dbg_sb(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_CTRL
-#define dbg_ctrl(format, arg...) printk(KERN_DEBUG __FILE__ ": (CTRL) " format "\n" , ## arg)
-#else
-#define dbg_ctrl(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_BULK
-#define dbg_bulk(format, arg...) printk(KERN_DEBUG __FILE__ ": (BULK) " format "\n" , ## arg)
-#else
-#define dbg_bulk(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_INTR
-#define dbg_intr(format, arg...) printk(KERN_DEBUG __FILE__ ": (INTR) " format "\n" , ## arg)
-#else
-#define dbg_intr(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_ISOC
-#define dbg_isoc(format, arg...) printk(KERN_DEBUG __FILE__ ": (ISOC) " format "\n" , ## arg)
-#else
-#define dbg_isoc(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_TRACE
-#define DBFENTER (printk(": Entering: %s\n", __FUNCTION__))
-#define DBFEXIT  (printk(": Exiting:  %s\n", __FUNCTION__))
-#else
-#define DBFENTER do {} while (0)
-#define DBFEXIT  do {} while (0)
-#endif
-
-#define usb_pipeslow(pipe)	(((pipe) >> 26) & 1)
-
-/*-------------------------------------------------------------------
- Virtual Root Hub
- -------------------------------------------------------------------*/
-
-static __u8 root_hub_dev_des[] =
-{
-	0x12,  /*  __u8  bLength; */
-	0x01,  /*  __u8  bDescriptorType; Device */
-	0x00,  /*  __le16 bcdUSB; v1.0 */
-	0x01,
-	0x09,  /*  __u8  bDeviceClass; HUB_CLASSCODE */
-	0x00,  /*  __u8  bDeviceSubClass; */
-	0x00,  /*  __u8  bDeviceProtocol; */
-	0x08,  /*  __u8  bMaxPacketSize0; 8 Bytes */
-	0x00,  /*  __le16 idVendor; */
-	0x00,
-	0x00,  /*  __le16 idProduct; */
-	0x00,
-	0x00,  /*  __le16 bcdDevice; */
-	0x00,
-	0x00,  /*  __u8  iManufacturer; */
-	0x02,  /*  __u8  iProduct; */
-	0x01,  /*  __u8  iSerialNumber; */
-	0x01   /*  __u8  bNumConfigurations; */
-};
-
-/* Configuration descriptor */
-static __u8 root_hub_config_des[] =
-{
-	0x09,  /*  __u8  bLength; */
-	0x02,  /*  __u8  bDescriptorType; Configuration */
-	0x19,  /*  __le16 wTotalLength; */
-	0x00,
-	0x01,  /*  __u8  bNumInterfaces; */
-	0x01,  /*  __u8  bConfigurationValue; */
-	0x00,  /*  __u8  iConfiguration; */
-	0x40,  /*  __u8  bmAttributes; Bit 7: Bus-powered */
-	0x00,  /*  __u8  MaxPower; */
-
-     /* interface */
-	0x09,  /*  __u8  if_bLength; */
-	0x04,  /*  __u8  if_bDescriptorType; Interface */
-	0x00,  /*  __u8  if_bInterfaceNumber; */
-	0x00,  /*  __u8  if_bAlternateSetting; */
-	0x01,  /*  __u8  if_bNumEndpoints; */
-	0x09,  /*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
-	0x00,  /*  __u8  if_bInterfaceSubClass; */
-	0x00,  /*  __u8  if_bInterfaceProtocol; */
-	0x00,  /*  __u8  if_iInterface; */
-
-     /* endpoint */
-	0x07,  /*  __u8  ep_bLength; */
-	0x05,  /*  __u8  ep_bDescriptorType; Endpoint */
-	0x81,  /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
-	0x03,  /*  __u8  ep_bmAttributes; Interrupt */
-	0x08,  /*  __le16 ep_wMaxPacketSize; 8 Bytes */
-	0x00,
-	0xff   /*  __u8  ep_bInterval; 255 ms */
-};
-
-static __u8 root_hub_hub_des[] =
-{
-	0x09,  /*  __u8  bLength; */
-	0x29,  /*  __u8  bDescriptorType; Hub-descriptor */
-	0x02,  /*  __u8  bNbrPorts; */
-	0x00,  /* __u16  wHubCharacteristics; */
-	0x00,
-	0x01,  /*  __u8  bPwrOn2pwrGood; 2ms */
-	0x00,  /*  __u8  bHubContrCurrent; 0 mA */
-	0x00,  /*  __u8  DeviceRemovable; *** 7 Ports max *** */
-	0xff   /*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
-};
-
-static DEFINE_TIMER(bulk_start_timer, NULL, 0, 0);
-static DEFINE_TIMER(bulk_eot_timer, NULL, 0, 0);
-
-/* We want the start timer to expire before the eot timer, because the former might start
-   traffic, thus making it unnecessary for the latter to time out. */
-#define BULK_START_TIMER_INTERVAL (HZ/10) /* 100 ms */
-#define BULK_EOT_TIMER_INTERVAL (HZ/10+2) /* 120 ms */
-
-#define OK(x) len = (x); dbg_rh("OK(%d): line: %d", x, __LINE__); break
-#define CHECK_ALIGN(x) if (((__u32)(x)) & 0x00000003) \
-{panic("Alignment check (DWORD) failed at %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);}
-
-#define SLAB_FLAG     (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL)
-#define KMALLOC_FLAG  (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL)
-
-/* Most helpful debugging aid */
-#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__))))
-
-/* Alternative assert define which stops after a failed assert. */
-/*
-#define assert(expr)                                      \
-{                                                         \
-        if (!(expr)) {                                    \
-                err("assert failed at line %d",__LINE__); \
-                while (1);                                \
-        }                                                 \
-}
-*/
-
-
-/* FIXME: Should RX_BUF_SIZE be a config option, or maybe we should adjust it dynamically?
-   To adjust it dynamically we would have to get an interrupt when we reach the end
-   of the rx descriptor list, or when we get close to the end, and then allocate more
-   descriptors. */
-
-#define NBR_OF_RX_DESC     512
-#define RX_DESC_BUF_SIZE   1024
-#define RX_BUF_SIZE        (NBR_OF_RX_DESC * RX_DESC_BUF_SIZE)
-
-/* The number of epids is, among other things, used for pre-allocating
-   ctrl, bulk and isoc EP descriptors (one for each epid).
-   Assumed to be > 1 when initiating the DMA lists. */
-#define NBR_OF_EPIDS       32
-
-/* Support interrupt traffic intervals up to 128 ms. */
-#define MAX_INTR_INTERVAL 128
-
-/* If periodic traffic (intr or isoc) is to be used, then one entry in the EP table
-   must be "invalid". By this we mean that we shouldn't care about epid attentions
-   for this epid, or at least handle them differently from epid attentions for "valid"
-   epids. This define determines which one to use (don't change it). */
-#define INVALID_EPID     31
-/* A special epid for the bulk dummys. */
-#define DUMMY_EPID       30
-
-/* This is just a software cache for the valid entries in R_USB_EPT_DATA. */
-static __u32 epid_usage_bitmask;
-
-/* A bitfield to keep information on in/out traffic is needed to uniquely identify
-   an endpoint on a device, since the most significant bit which indicates traffic
-   direction is lacking in the ep_id field (ETRAX epids can handle both in and
-   out traffic on endpoints that are otherwise identical). The USB framework, however,
-   relies on them to be handled separately.  For example, bulk IN and OUT urbs cannot
-   be queued in the same list, since they would block each other. */
-static __u32 epid_out_traffic;
-
-/* DMA IN cache bug. Align the DMA IN buffers to 32 bytes, i.e. a cache line.
-   Since RX_DESC_BUF_SIZE is 1024 is a multiple of 32, all rx buffers will be cache aligned. */
-static volatile unsigned char RxBuf[RX_BUF_SIZE] __attribute__ ((aligned (32)));
-static volatile USB_IN_Desc_t RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned (4)));
-
-/* Pointers into RxDescList. */
-static volatile USB_IN_Desc_t *myNextRxDesc;
-static volatile USB_IN_Desc_t *myLastRxDesc;
-static volatile USB_IN_Desc_t *myPrevRxDesc;
-
-/* EP descriptors must be 32-bit aligned. */
-static volatile USB_EP_Desc_t TxCtrlEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
-static volatile USB_EP_Desc_t TxBulkEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
-/* After each enabled bulk EP (IN or OUT) we put two disabled EP descriptors with the eol flag set,
-   causing the DMA to stop the DMA channel. The first of these two has the intr flag set, which
-   gives us a dma8_sub0_descr interrupt. When we receive this, we advance the DMA one step in the
-   EP list and then restart the bulk channel, thus forcing a switch between bulk EP descriptors
-   in each frame. */
-static volatile USB_EP_Desc_t TxBulkDummyEPList[NBR_OF_EPIDS][2] __attribute__ ((aligned (4)));
-
-static volatile USB_EP_Desc_t TxIsocEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
-static volatile USB_SB_Desc_t TxIsocSB_zout __attribute__ ((aligned (4)));
-
-static volatile USB_EP_Desc_t TxIntrEPList[MAX_INTR_INTERVAL] __attribute__ ((aligned (4)));
-static volatile USB_SB_Desc_t TxIntrSB_zout __attribute__ ((aligned (4)));
-
-/* A zout transfer makes a memory access at the address of its buf pointer, which means that setting
-   this buf pointer to 0 will cause an access to the flash. In addition to this, setting sw_len to 0
-   results in a 16/32 bytes (depending on DMA burst size) transfer. Instead, we set it to 1, and point
-   it to this buffer. */
-static int zout_buffer[4] __attribute__ ((aligned (4)));
-
-/* Cache for allocating new EP and SB descriptors. */
-static struct kmem_cache *usb_desc_cache;
-
-/* Cache for the registers allocated in the top half. */
-static struct kmem_cache *top_half_reg_cache;
-
-/* Cache for the data allocated in the isoc descr top half. */
-static struct kmem_cache *isoc_compl_cache;
-
-static struct usb_bus *etrax_usb_bus;
-
-/* This is a circular (double-linked) list of the active urbs for each epid.
-   The head is never removed, and new urbs are linked onto the list as
-   urb_entry_t elements. Don't reference urb_list directly; use the wrapper
-   functions instead. Note that working with these lists might require spinlock
-   protection. */
-static struct list_head urb_list[NBR_OF_EPIDS];
-
-/* Read about the need and usage of this lock in submit_ctrl_urb. */
-static spinlock_t urb_list_lock;
-
-/* Used when unlinking asynchronously. */
-static struct list_head urb_unlink_list;
-
-/* for returning string descriptors in UTF-16LE */
-static int ascii2utf (char *ascii, __u8 *utf, int utfmax)
-{
-	int retval;
-
-	for (retval = 0; *ascii && utfmax > 1; utfmax -= 2, retval += 2) {
-		*utf++ = *ascii++ & 0x7f;
-		*utf++ = 0;
-	}
-	return retval;
-}
-
-static int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
-{
-	char buf [30];
-
-	// assert (len > (2 * (sizeof (buf) + 1)));
-	// assert (strlen (type) <= 8);
-
-	// language ids
-	if (id == 0) {
-		*data++ = 4; *data++ = 3;	/* 4 bytes data */
-		*data++ = 0; *data++ = 0;	/* some language id */
-		return 4;
-
-	// serial number
-	} else if (id == 1) {
-		sprintf (buf, "%x", serial);
-
-	// product description
-	} else if (id == 2) {
-		sprintf (buf, "USB %s Root Hub", type);
-
-	// id 3 == vendor description
-
-	// unsupported IDs --> "stall"
-	} else
-	    return 0;
-
-	data [0] = 2 + ascii2utf (buf, data + 2, len - 2);
-	data [1] = 3;
-	return data [0];
-}
-
-/* Wrappers around the list functions (include/linux/list.h). */
-
-static inline int urb_list_empty(int epid)
-{
-	return list_empty(&urb_list[epid]);
-}
-
-/* Returns first urb for this epid, or NULL if list is empty. */
-static inline struct urb *urb_list_first(int epid)
-{
-	struct urb *first_urb = 0;
-
-	if (!urb_list_empty(epid)) {
-		/* Get the first urb (i.e. head->next). */
-		urb_entry_t *urb_entry = list_entry((&urb_list[epid])->next, urb_entry_t, list);
-		first_urb = urb_entry->urb;
-	}
-	return first_urb;
-}
-
-/* Adds an urb_entry last in the list for this epid. */
-static inline void urb_list_add(struct urb *urb, int epid)
-{
-	urb_entry_t *urb_entry = kmalloc(sizeof(urb_entry_t), KMALLOC_FLAG);
-	assert(urb_entry);
-
-	urb_entry->urb = urb;
-	list_add_tail(&urb_entry->list, &urb_list[epid]);
-}
-
-/* Search through the list for an element that contains this urb. (The list
-   is expected to be short and the one we are about to delete will often be
-   the first in the list.) */
-static inline urb_entry_t *__urb_list_entry(struct urb *urb, int epid)
-{
-	struct list_head *entry;
-	struct list_head *tmp;
-	urb_entry_t *urb_entry;
-
-	list_for_each_safe(entry, tmp, &urb_list[epid]) {
-		urb_entry = list_entry(entry, urb_entry_t, list);
-		assert(urb_entry);
-		assert(urb_entry->urb);
-
-		if (urb_entry->urb == urb) {
-			return urb_entry;
-		}
-	}
-	return 0;
-}
-
-/* Delete an urb from the list. */
-static inline void urb_list_del(struct urb *urb, int epid)
-{
-	urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
-	assert(urb_entry);
-
-	/* Delete entry and free. */
-	list_del(&urb_entry->list);
-	kfree(urb_entry);
-}
-
-/* Move an urb to the end of the list. */
-static inline void urb_list_move_last(struct urb *urb, int epid)
-{
-	urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
-	assert(urb_entry);
-
-	list_move_tail(&urb_entry->list, &urb_list[epid]);
-}
-
-/* Get the next urb in the list. */
-static inline struct urb *urb_list_next(struct urb *urb, int epid)
-{
-	urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
-
-	assert(urb_entry);
-
-	if (urb_entry->list.next != &urb_list[epid]) {
-		struct list_head *elem = urb_entry->list.next;
-		urb_entry = list_entry(elem, urb_entry_t, list);
-		return urb_entry->urb;
-	} else {
-		return NULL;
-	}
-}
-
-
-
-/* For debug purposes only. */
-static inline void urb_list_dump(int epid)
-{
-	struct list_head *entry;
-	struct list_head *tmp;
-	urb_entry_t *urb_entry;
-	int i = 0;
-
-	info("Dumping urb list for epid %d", epid);
-
-	list_for_each_safe(entry, tmp, &urb_list[epid]) {
-		urb_entry = list_entry(entry, urb_entry_t, list);
-		info("   entry %d, urb = 0x%lx", i, (unsigned long)urb_entry->urb);
-	}
-}
-
-static void init_rx_buffers(void);
-static int etrax_rh_unlink_urb(struct urb *urb);
-static void etrax_rh_send_irq(struct urb *urb);
-static void etrax_rh_init_int_timer(struct urb *urb);
-static void etrax_rh_int_timer_do(unsigned long ptr);
-
-static int etrax_usb_setup_epid(struct urb *urb);
-static int etrax_usb_lookup_epid(struct urb *urb);
-static int etrax_usb_allocate_epid(void);
-static void etrax_usb_free_epid(int epid);
-
-static int etrax_remove_from_sb_list(struct urb *urb);
-
-static void* etrax_usb_buffer_alloc(struct usb_bus* bus, size_t size,
-	unsigned mem_flags, dma_addr_t *dma);
-static void etrax_usb_buffer_free(struct usb_bus *bus, size_t size, void *addr, dma_addr_t dma);
-
-static void etrax_usb_add_to_bulk_sb_list(struct urb *urb, int epid);
-static void etrax_usb_add_to_ctrl_sb_list(struct urb *urb, int epid);
-static void etrax_usb_add_to_intr_sb_list(struct urb *urb, int epid);
-static void etrax_usb_add_to_isoc_sb_list(struct urb *urb, int epid);
-
-static int etrax_usb_submit_bulk_urb(struct urb *urb);
-static int etrax_usb_submit_ctrl_urb(struct urb *urb);
-static int etrax_usb_submit_intr_urb(struct urb *urb);
-static int etrax_usb_submit_isoc_urb(struct urb *urb);
-
-static int etrax_usb_submit_urb(struct urb *urb, unsigned mem_flags);
-static int etrax_usb_unlink_urb(struct urb *urb, int status);
-static int etrax_usb_get_frame_number(struct usb_device *usb_dev);
-
-static irqreturn_t etrax_usb_tx_interrupt(int irq, void *vhc);
-static irqreturn_t etrax_usb_rx_interrupt(int irq, void *vhc);
-static irqreturn_t etrax_usb_hc_interrupt_top_half(int irq, void *vhc);
-static void etrax_usb_hc_interrupt_bottom_half(void *data);
-
-static void etrax_usb_isoc_descr_interrupt_bottom_half(void *data);
-
-
-/* The following is a list of interrupt handlers for the host controller interrupts we use.
-   They are called from etrax_usb_hc_interrupt_bottom_half. */
-static void etrax_usb_hc_isoc_eof_interrupt(void);
-static void etrax_usb_hc_bulk_eot_interrupt(int timer_induced);
-static void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg);
-static void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg);
-static void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg);
-
-static int etrax_rh_submit_urb (struct urb *urb);
-
-/* Forward declaration needed because they are used in the rx interrupt routine. */
-static void etrax_usb_complete_urb(struct urb *urb, int status);
-static void etrax_usb_complete_bulk_urb(struct urb *urb, int status);
-static void etrax_usb_complete_ctrl_urb(struct urb *urb, int status);
-static void etrax_usb_complete_intr_urb(struct urb *urb, int status);
-static void etrax_usb_complete_isoc_urb(struct urb *urb, int status);
-
-static int etrax_usb_hc_init(void);
-static void etrax_usb_hc_cleanup(void);
-
-static struct usb_operations etrax_usb_device_operations =
-{
-	.get_frame_number = etrax_usb_get_frame_number,
-	.submit_urb = etrax_usb_submit_urb,
-	.unlink_urb = etrax_usb_unlink_urb,
-        .buffer_alloc = etrax_usb_buffer_alloc,
-        .buffer_free = etrax_usb_buffer_free
-};
-
-/* Note that these functions are always available in their "__" variants, for use in
-   error situations. The "__" missing variants are controlled by the USB_DEBUG_DESC/
-   USB_DEBUG_URB macros. */
-static void __dump_urb(struct urb* purb)
-{
-	printk("\nurb                  :0x%08lx\n", (unsigned long)purb);
-	printk("dev                   :0x%08lx\n", (unsigned long)purb->dev);
-	printk("pipe                  :0x%08x\n", purb->pipe);
-	printk("status                :%d\n", purb->status);
-	printk("transfer_flags        :0x%08x\n", purb->transfer_flags);
-	printk("transfer_buffer       :0x%08lx\n", (unsigned long)purb->transfer_buffer);
-	printk("transfer_buffer_length:%d\n", purb->transfer_buffer_length);
-	printk("actual_length         :%d\n", purb->actual_length);
-	printk("setup_packet          :0x%08lx\n", (unsigned long)purb->setup_packet);
-	printk("start_frame           :%d\n", purb->start_frame);
-	printk("number_of_packets     :%d\n", purb->number_of_packets);
-	printk("interval              :%d\n", purb->interval);
-	printk("error_count           :%d\n", purb->error_count);
-	printk("context               :0x%08lx\n", (unsigned long)purb->context);
-	printk("complete              :0x%08lx\n\n", (unsigned long)purb->complete);
-}
-
-static void __dump_in_desc(volatile USB_IN_Desc_t *in)
-{
-	printk("\nUSB_IN_Desc at 0x%08lx\n", (unsigned long)in);
-	printk("  sw_len  : 0x%04x (%d)\n", in->sw_len, in->sw_len);
-	printk("  command : 0x%04x\n", in->command);
-	printk("  next    : 0x%08lx\n", in->next);
-	printk("  buf     : 0x%08lx\n", in->buf);
-	printk("  hw_len  : 0x%04x (%d)\n", in->hw_len, in->hw_len);
-	printk("  status  : 0x%04x\n\n", in->status);
-}
-
-static void __dump_sb_desc(volatile USB_SB_Desc_t *sb)
-{
-	char tt = (sb->command & 0x30) >> 4;
-	char *tt_string;
-
-	switch (tt) {
-	case 0:
-		tt_string = "zout";
-		break;
-	case 1:
-		tt_string = "in";
-		break;
-	case 2:
-		tt_string = "out";
-		break;
-	case 3:
-		tt_string = "setup";
-		break;
-	default:
-		tt_string = "unknown (weird)";
-	}
-
-	printk("\n   USB_SB_Desc at 0x%08lx\n", (unsigned long)sb);
-	printk("     command : 0x%04x\n", sb->command);
-	printk("        rem     : %d\n", (sb->command & 0x3f00) >> 8);
-	printk("        full    : %d\n", (sb->command & 0x40) >> 6);
-	printk("        tt      : %d (%s)\n", tt, tt_string);
-	printk("        intr    : %d\n", (sb->command & 0x8) >> 3);
-	printk("        eot     : %d\n", (sb->command & 0x2) >> 1);
-	printk("        eol     : %d\n", sb->command & 0x1);
-	printk("     sw_len  : 0x%04x (%d)\n", sb->sw_len, sb->sw_len);
-	printk("     next    : 0x%08lx\n", sb->next);
-	printk("     buf     : 0x%08lx\n\n", sb->buf);
-}
-
-
-static void __dump_ep_desc(volatile USB_EP_Desc_t *ep)
-{
-	printk("\nUSB_EP_Desc at 0x%08lx\n", (unsigned long)ep);
-	printk("  command : 0x%04x\n", ep->command);
-	printk("     ep_id   : %d\n", (ep->command & 0x1f00) >> 8);
-	printk("     enable  : %d\n", (ep->command & 0x10) >> 4);
-	printk("     intr    : %d\n", (ep->command & 0x8) >> 3);
-	printk("     eof     : %d\n", (ep->command & 0x2) >> 1);
-	printk("     eol     : %d\n", ep->command & 0x1);
-	printk("  hw_len  : 0x%04x (%d)\n", ep->hw_len, ep->hw_len);
-	printk("  next    : 0x%08lx\n", ep->next);
-	printk("  sub     : 0x%08lx\n\n", ep->sub);
-}
-
-static inline void __dump_ep_list(int pipe_type)
-{
-	volatile USB_EP_Desc_t *ep;
-	volatile USB_EP_Desc_t *first_ep;
-	volatile USB_SB_Desc_t *sb;
-
-	switch (pipe_type)
-	{
-	case PIPE_BULK:
-		first_ep = &TxBulkEPList[0];
-		break;
-	case PIPE_CONTROL:
-		first_ep = &TxCtrlEPList[0];
-		break;
-	case PIPE_INTERRUPT:
-		first_ep = &TxIntrEPList[0];
-		break;
-	case PIPE_ISOCHRONOUS:
-		first_ep = &TxIsocEPList[0];
-		break;
-	default:
-		warn("Cannot dump unknown traffic type");
-		return;
-	}
-	ep = first_ep;
-
-	printk("\n\nDumping EP list...\n\n");
-
-	do {
-		__dump_ep_desc(ep);
-		/* Cannot phys_to_virt on 0 as it turns into 80000000, which is != 0. */
-		sb = ep->sub ? phys_to_virt(ep->sub) : 0;
-		while (sb) {
-			__dump_sb_desc(sb);
-			sb = sb->next ? phys_to_virt(sb->next) : 0;
-		}
-		ep = (volatile USB_EP_Desc_t *)(phys_to_virt(ep->next));
-
-	} while (ep != first_ep);
-}
-
-static inline void __dump_ept_data(int epid)
-{
-	unsigned long flags;
-	__u32 r_usb_ept_data;
-
-	if (epid < 0 || epid > 31) {
-		printk("Cannot dump ept data for invalid epid %d\n", epid);
-		return;
-	}
-
-	save_flags(flags);
-	cli();
-	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
-	nop();
-	r_usb_ept_data = *R_USB_EPT_DATA;
-	restore_flags(flags);
-
-	printk("\nR_USB_EPT_DATA = 0x%x for epid %d :\n", r_usb_ept_data, epid);
-	if (r_usb_ept_data == 0) {
-		/* No need for more detailed printing. */
-		return;
-	}
-	printk("  valid           : %d\n", (r_usb_ept_data & 0x80000000) >> 31);
-	printk("  hold            : %d\n", (r_usb_ept_data & 0x40000000) >> 30);
-	printk("  error_count_in  : %d\n", (r_usb_ept_data & 0x30000000) >> 28);
-	printk("  t_in            : %d\n", (r_usb_ept_data & 0x08000000) >> 27);
-	printk("  low_speed       : %d\n", (r_usb_ept_data & 0x04000000) >> 26);
-	printk("  port            : %d\n", (r_usb_ept_data & 0x03000000) >> 24);
-	printk("  error_code      : %d\n", (r_usb_ept_data & 0x00c00000) >> 22);
-	printk("  t_out           : %d\n", (r_usb_ept_data & 0x00200000) >> 21);
-	printk("  error_count_out : %d\n", (r_usb_ept_data & 0x00180000) >> 19);
-	printk("  max_len         : %d\n", (r_usb_ept_data & 0x0003f800) >> 11);
-	printk("  ep              : %d\n", (r_usb_ept_data & 0x00000780) >> 7);
-	printk("  dev             : %d\n", (r_usb_ept_data & 0x0000003f));
-}
-
-static inline void __dump_ept_data_list(void)
-{
-	int i;
-
-	printk("Dumping the whole R_USB_EPT_DATA list\n");
-
-	for (i = 0; i < 32; i++) {
-		__dump_ept_data(i);
-	}
-}
-#ifdef USB_DEBUG_DESC
-#define dump_in_desc(...) __dump_in_desc(...)
-#define dump_sb_desc(...) __dump_sb_desc(...)
-#define dump_ep_desc(...) __dump_ep_desc(...)
-#else
-#define dump_in_desc(...) do {} while (0)
-#define dump_sb_desc(...) do {} while (0)
-#define dump_ep_desc(...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_URB
-#define dump_urb(x)     __dump_urb(x)
-#else
-#define dump_urb(x)     do {} while (0)
-#endif
-
-static void init_rx_buffers(void)
-{
-	int i;
-
-	DBFENTER;
-
-	for (i = 0; i < (NBR_OF_RX_DESC - 1); i++) {
-		RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
-		RxDescList[i].command = 0;
-		RxDescList[i].next = virt_to_phys(&RxDescList[i + 1]);
-		RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE));
-		RxDescList[i].hw_len = 0;
-		RxDescList[i].status = 0;
-
-		/* DMA IN cache bug. (struct etrax_dma_descr has the same layout as USB_IN_Desc
-		   for the relevant fields.) */
-		prepare_rx_descriptor((struct etrax_dma_descr*)&RxDescList[i]);
-
-	}
-
-	RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
-	RxDescList[i].command = IO_STATE(USB_IN_command, eol, yes);
-	RxDescList[i].next = virt_to_phys(&RxDescList[0]);
-	RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE));
-	RxDescList[i].hw_len = 0;
-	RxDescList[i].status = 0;
-
-	myNextRxDesc = &RxDescList[0];
-	myLastRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
-	myPrevRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
-
-	*R_DMA_CH9_FIRST = virt_to_phys(myNextRxDesc);
-	*R_DMA_CH9_CMD = IO_STATE(R_DMA_CH9_CMD, cmd, start);
-
-	DBFEXIT;
-}
-
-static void init_tx_bulk_ep(void)
-{
-	int i;
-
-	DBFENTER;
-
-	for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
-		CHECK_ALIGN(&TxBulkEPList[i]);
-		TxBulkEPList[i].hw_len = 0;
-		TxBulkEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
-		TxBulkEPList[i].sub = 0;
-		TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[i + 1]);
-
-		/* Initiate two EPs, disabled and with the eol flag set. No need for any
-		   preserved epid. */
-
-		/* The first one has the intr flag set so we get an interrupt when the DMA
-		   channel is about to become disabled. */
-		CHECK_ALIGN(&TxBulkDummyEPList[i][0]);
-		TxBulkDummyEPList[i][0].hw_len = 0;
-		TxBulkDummyEPList[i][0].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) |
-						   IO_STATE(USB_EP_command, eol, yes) |
-						   IO_STATE(USB_EP_command, intr, yes));
-		TxBulkDummyEPList[i][0].sub = 0;
-		TxBulkDummyEPList[i][0].next = virt_to_phys(&TxBulkDummyEPList[i][1]);
-
-		/* The second one. */
-		CHECK_ALIGN(&TxBulkDummyEPList[i][1]);
-		TxBulkDummyEPList[i][1].hw_len = 0;
-		TxBulkDummyEPList[i][1].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) |
-						   IO_STATE(USB_EP_command, eol, yes));
-		TxBulkDummyEPList[i][1].sub = 0;
-		/* The last dummy's next pointer is the same as the current EP's next pointer. */
-		TxBulkDummyEPList[i][1].next = virt_to_phys(&TxBulkEPList[i + 1]);
-	}
-
-	/* Configure the last one. */
-	CHECK_ALIGN(&TxBulkEPList[i]);
-	TxBulkEPList[i].hw_len = 0;
-	TxBulkEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) |
-				   IO_FIELD(USB_EP_command, epid, i));
-	TxBulkEPList[i].sub = 0;
-	TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[0]);
-
-	/* No need configuring dummy EPs for the last one as it will never be used for
-	   bulk traffic (i == INVALD_EPID at this point). */
-
-	/* Set up to start on the last EP so we will enable it when inserting traffic
-	   for the first time (imitating the situation where the DMA has stopped
-	   because there was no more traffic). */
-	*R_DMA_CH8_SUB0_EP = virt_to_phys(&TxBulkEPList[i]);
-	/* No point in starting the bulk channel yet.
-	 *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); */
-	DBFEXIT;
-}
-
-static void init_tx_ctrl_ep(void)
-{
-	int i;
-
-	DBFENTER;
-
-	for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
-		CHECK_ALIGN(&TxCtrlEPList[i]);
-		TxCtrlEPList[i].hw_len = 0;
-		TxCtrlEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
-		TxCtrlEPList[i].sub = 0;
-		TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[i + 1]);
-	}
-
-	CHECK_ALIGN(&TxCtrlEPList[i]);
-	TxCtrlEPList[i].hw_len = 0;
-	TxCtrlEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) |
-				   IO_FIELD(USB_EP_command, epid, i));
-
-	TxCtrlEPList[i].sub = 0;
-	TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[0]);
-
-	*R_DMA_CH8_SUB1_EP = virt_to_phys(&TxCtrlEPList[0]);
-	*R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
-
-	DBFEXIT;
-}
-
-
-static void init_tx_intr_ep(void)
-{
-	int i;
-
-	DBFENTER;
-
-	/* Read comment at zout_buffer declaration for an explanation to this. */
-	TxIntrSB_zout.sw_len = 1;
-	TxIntrSB_zout.next = 0;
-	TxIntrSB_zout.buf = virt_to_phys(&zout_buffer[0]);
-	TxIntrSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) |
-				 IO_STATE(USB_SB_command, tt, zout) |
-				 IO_STATE(USB_SB_command, full, yes) |
-				 IO_STATE(USB_SB_command, eot, yes) |
-				 IO_STATE(USB_SB_command, eol, yes));
-
-	for (i = 0; i < (MAX_INTR_INTERVAL - 1); i++) {
-		CHECK_ALIGN(&TxIntrEPList[i]);
-		TxIntrEPList[i].hw_len = 0;
-		TxIntrEPList[i].command =
-			(IO_STATE(USB_EP_command, eof, yes) |
-			 IO_STATE(USB_EP_command, enable, yes) |
-			 IO_FIELD(USB_EP_command, epid, INVALID_EPID));
-		TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout);
-		TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[i + 1]);
-	}
-
-	CHECK_ALIGN(&TxIntrEPList[i]);
-	TxIntrEPList[i].hw_len = 0;
-	TxIntrEPList[i].command =
-		(IO_STATE(USB_EP_command, eof, yes) |
-		 IO_STATE(USB_EP_command, eol, yes) |
-		 IO_STATE(USB_EP_command, enable, yes) |
-		 IO_FIELD(USB_EP_command, epid, INVALID_EPID));
-	TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout);
-	TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[0]);
-
-	*R_DMA_CH8_SUB2_EP = virt_to_phys(&TxIntrEPList[0]);
-	*R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
-	DBFEXIT;
-}
-
-static void init_tx_isoc_ep(void)
-{
-	int i;
-
-	DBFENTER;
-
-	/* Read comment at zout_buffer declaration for an explanation to this. */
-	TxIsocSB_zout.sw_len = 1;
-	TxIsocSB_zout.next = 0;
-	TxIsocSB_zout.buf = virt_to_phys(&zout_buffer[0]);
-	TxIsocSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) |
-				 IO_STATE(USB_SB_command, tt, zout) |
-				 IO_STATE(USB_SB_command, full, yes) |
-				 IO_STATE(USB_SB_command, eot, yes) |
-				 IO_STATE(USB_SB_command, eol, yes));
-
-	/* The last isochronous EP descriptor is a dummy. */
-
-	for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
-		CHECK_ALIGN(&TxIsocEPList[i]);
-		TxIsocEPList[i].hw_len = 0;
-		TxIsocEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
-		TxIsocEPList[i].sub = 0;
-		TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[i + 1]);
-	}
-
-	CHECK_ALIGN(&TxIsocEPList[i]);
-	TxIsocEPList[i].hw_len = 0;
-
-	/* Must enable the last EP descr to get eof interrupt. */
-	TxIsocEPList[i].command = (IO_STATE(USB_EP_command, enable, yes) |
-				   IO_STATE(USB_EP_command, eof, yes) |
-				   IO_STATE(USB_EP_command, eol, yes) |
-				   IO_FIELD(USB_EP_command, epid, INVALID_EPID));
-	TxIsocEPList[i].sub = virt_to_phys(&TxIsocSB_zout);
-	TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[0]);
-
-	*R_DMA_CH8_SUB3_EP = virt_to_phys(&TxIsocEPList[0]);
-	*R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start);
-
-	DBFEXIT;
-}
-
-static void etrax_usb_unlink_intr_urb(struct urb *urb)
-{
-	volatile USB_EP_Desc_t *first_ep;  /* First EP in the list. */
-	volatile USB_EP_Desc_t *curr_ep;   /* Current EP, the iterator. */
-	volatile USB_EP_Desc_t *next_ep;   /* The EP after current. */
-	volatile USB_EP_Desc_t *unlink_ep; /* The one we should remove from the list. */
-
-	int epid;
-
-	/* Read 8.8.4 in Designer's Reference, "Removing an EP Descriptor from the List". */
-
-	DBFENTER;
-
-	epid = ((etrax_urb_priv_t *)urb->hcpriv)->epid;
-
-	first_ep = &TxIntrEPList[0];
-	curr_ep = first_ep;
-
-
-	/* Note that this loop removes all EP descriptors with this epid. This assumes
-	   that all EP descriptors belong to the one and only urb for this epid. */
-
-	do {
-		next_ep = (USB_EP_Desc_t *)phys_to_virt(curr_ep->next);
-
-		if (IO_EXTRACT(USB_EP_command, epid, next_ep->command) == epid) {
-
-			dbg_intr("Found EP to unlink for epid %d", epid);
-
-			/* This is the one we should unlink. */
-			unlink_ep = next_ep;
-
-			/* Actually unlink the EP from the DMA list. */
-			curr_ep->next = unlink_ep->next;
-
-			/* Wait until the DMA is no longer at this descriptor. */
-			while (*R_DMA_CH8_SUB2_EP == virt_to_phys(unlink_ep));
-
-			/* Now we are free to remove it and its SB descriptor.
-			   Note that it is assumed here that there is only one sb in the
-			   sb list for this ep. */
-			kmem_cache_free(usb_desc_cache, phys_to_virt(unlink_ep->sub));
-			kmem_cache_free(usb_desc_cache, (USB_EP_Desc_t *)unlink_ep);
-		}
-
-		curr_ep = phys_to_virt(curr_ep->next);
-
-	} while (curr_ep != first_ep);
-        urb->hcpriv = NULL;
-}
-
-void etrax_usb_do_intr_recover(int epid)
-{
-	USB_EP_Desc_t *first_ep, *tmp_ep;
-
-	DBFENTER;
-
-	first_ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB2_EP);
-	tmp_ep = first_ep;
-
-	/* What this does is simply to walk the list of interrupt
-	   ep descriptors and enable those that are disabled. */
-
-	do {
-		if (IO_EXTRACT(USB_EP_command, epid, tmp_ep->command) == epid &&
-		    !(tmp_ep->command & IO_MASK(USB_EP_command, enable))) {
-			tmp_ep->command |= IO_STATE(USB_EP_command, enable, yes);
-		}
-
-		tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next);
-
-	} while (tmp_ep != first_ep);
-
-
-	DBFEXIT;
-}
-
-static int etrax_rh_unlink_urb (struct urb *urb)
-{
-	etrax_hc_t *hc;
-
-	DBFENTER;
-
-	hc = urb->dev->bus->hcpriv;
-
-	if (hc->rh.urb == urb) {
-		hc->rh.send = 0;
-		del_timer(&hc->rh.rh_int_timer);
-	}
-
-	DBFEXIT;
-	return 0;
-}
-
-static void etrax_rh_send_irq(struct urb *urb)
-{
-	__u16 data = 0;
-	etrax_hc_t *hc = urb->dev->bus->hcpriv;
-	DBFENTER;
-
-/*
-  dbg_rh("R_USB_FM_NUMBER   : 0x%08X", *R_USB_FM_NUMBER);
-  dbg_rh("R_USB_FM_REMAINING: 0x%08X", *R_USB_FM_REMAINING);
-*/
-
-	data |= (hc->rh.wPortChange_1) ? (1 << 1) : 0;
-	data |= (hc->rh.wPortChange_2) ? (1 << 2) : 0;
-
-	*((__u16 *)urb->transfer_buffer) = cpu_to_le16(data);
-	/* FIXME: Why is actual_length set to 1 when data is 2 bytes?
-	   Since only 1 byte is used, why not declare data as __u8? */
-	urb->actual_length = 1;
-	urb->status = 0;
-
-	if (hc->rh.send && urb->complete) {
-		dbg_rh("wPortChange_1: 0x%04X", hc->rh.wPortChange_1);
-		dbg_rh("wPortChange_2: 0x%04X", hc->rh.wPortChange_2);
-
-		urb->complete(urb, NULL);
-	}
-
-	DBFEXIT;
-}
-
-static void etrax_rh_init_int_timer(struct urb *urb)
-{
-	etrax_hc_t *hc;
-
-	DBFENTER;
-
-	hc = urb->dev->bus->hcpriv;
-	hc->rh.interval = urb->interval;
-	init_timer(&hc->rh.rh_int_timer);
-	hc->rh.rh_int_timer.function = etrax_rh_int_timer_do;
-	hc->rh.rh_int_timer.data = (unsigned long)urb;
-	/* FIXME: Is the jiffies resolution enough? All intervals < 10 ms will be mapped
-	   to 0, and the rest to the nearest lower 10 ms. */
-	hc->rh.rh_int_timer.expires = jiffies + ((HZ * hc->rh.interval) / 1000);
-	add_timer(&hc->rh.rh_int_timer);
-
-	DBFEXIT;
-}
-
-static void etrax_rh_int_timer_do(unsigned long ptr)
-{
-	struct urb *urb;
-	etrax_hc_t *hc;
-
-	DBFENTER;
-
-	urb = (struct urb*)ptr;
-	hc = urb->dev->bus->hcpriv;
-
-	if (hc->rh.send) {
-		etrax_rh_send_irq(urb);
-	}
-
-	DBFEXIT;
-}
-
-static int etrax_usb_setup_epid(struct urb *urb)
-{
-	int epid;
-	char devnum, endpoint, out_traffic, slow;
-	int maxlen;
-	unsigned long flags;
-
-	DBFENTER;
-
-	epid = etrax_usb_lookup_epid(urb);
-	if ((epid != -1)){
-		/* An epid that fits this urb has been found. */
-		DBFEXIT;
-		return epid;
-	}
-
-	/* We must find and initiate a new epid for this urb. */
-	epid = etrax_usb_allocate_epid();
-
-	if (epid == -1) {
-		/* Failed to allocate a new epid. */
-		DBFEXIT;
-		return epid;
-	}
-
-	/* We now have a new epid to use. Initiate it. */
-	set_bit(epid, (void *)&epid_usage_bitmask);
-
-	devnum = usb_pipedevice(urb->pipe);
-	endpoint = usb_pipeendpoint(urb->pipe);
-	slow = usb_pipeslow(urb->pipe);
-	maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
-	if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
-		/* We want both IN and OUT control traffic to be put on the same EP/SB list. */
-		out_traffic = 1;
-	} else {
-		out_traffic = usb_pipeout(urb->pipe);
-	}
-
-	save_flags(flags);
-	cli();
-
-	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
-	nop();
-
-	if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-		*R_USB_EPT_DATA_ISO = IO_STATE(R_USB_EPT_DATA_ISO, valid, yes) |
-			/* FIXME: Change any to the actual port? */
-			IO_STATE(R_USB_EPT_DATA_ISO, port, any) |
-			IO_FIELD(R_USB_EPT_DATA_ISO, max_len, maxlen) |
-			IO_FIELD(R_USB_EPT_DATA_ISO, ep, endpoint) |
-			IO_FIELD(R_USB_EPT_DATA_ISO, dev, devnum);
-	} else {
-		*R_USB_EPT_DATA = IO_STATE(R_USB_EPT_DATA, valid, yes) |
-			IO_FIELD(R_USB_EPT_DATA, low_speed, slow) |
-			/* FIXME: Change any to the actual port? */
-			IO_STATE(R_USB_EPT_DATA, port, any) |
-			IO_FIELD(R_USB_EPT_DATA, max_len, maxlen) |
-			IO_FIELD(R_USB_EPT_DATA, ep, endpoint) |
-			IO_FIELD(R_USB_EPT_DATA, dev, devnum);
-	}
-
-	restore_flags(flags);
-
-	if (out_traffic) {
-		set_bit(epid, (void *)&epid_out_traffic);
-	} else {
-		clear_bit(epid, (void *)&epid_out_traffic);
-	}
-
-	dbg_epid("Setting up epid %d with devnum %d, endpoint %d and max_len %d (%s)",
-		 epid, devnum, endpoint, maxlen, out_traffic ? "OUT" : "IN");
-
-	DBFEXIT;
-	return epid;
-}
-
-static void etrax_usb_free_epid(int epid)
-{
-	unsigned long flags;
-
-	DBFENTER;
-
-	if (!test_bit(epid, (void *)&epid_usage_bitmask)) {
-		warn("Trying to free unused epid %d", epid);
-		DBFEXIT;
-		return;
-	}
-
-	save_flags(flags);
-	cli();
-
-	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
-	nop();
-	while (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold));
-	/* This will, among other things, set the valid field to 0. */
-	*R_USB_EPT_DATA = 0;
-	restore_flags(flags);
-
-	clear_bit(epid, (void *)&epid_usage_bitmask);
-
-
-	dbg_epid("Freed epid %d", epid);
-
-	DBFEXIT;
-}
-
-static int etrax_usb_lookup_epid(struct urb *urb)
-{
-	int i;
-	__u32 data;
-	char devnum, endpoint, slow, out_traffic;
-	int maxlen;
-	unsigned long flags;
-
-	DBFENTER;
-
-	devnum = usb_pipedevice(urb->pipe);
-	endpoint = usb_pipeendpoint(urb->pipe);
-	slow = usb_pipeslow(urb->pipe);
-	maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
-	if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
-		/* We want both IN and OUT control traffic to be put on the same EP/SB list. */
-		out_traffic = 1;
-	} else {
-		out_traffic = usb_pipeout(urb->pipe);
-	}
-
-	/* Step through att epids. */
-	for (i = 0; i < NBR_OF_EPIDS; i++) {
-		if (test_bit(i, (void *)&epid_usage_bitmask) &&
-		    test_bit(i, (void *)&epid_out_traffic) == out_traffic) {
-
-			save_flags(flags);
-			cli();
-			*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, i);
-			nop();
-
-			if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-				data = *R_USB_EPT_DATA_ISO;
-				restore_flags(flags);
-
-				if ((IO_MASK(R_USB_EPT_DATA_ISO, valid) & data) &&
-				    (IO_EXTRACT(R_USB_EPT_DATA_ISO, dev, data) == devnum) &&
-				    (IO_EXTRACT(R_USB_EPT_DATA_ISO, ep, data) == endpoint) &&
-				    (IO_EXTRACT(R_USB_EPT_DATA_ISO, max_len, data) == maxlen)) {
-					dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)",
-						 i, devnum, endpoint, out_traffic ? "OUT" : "IN");
-					DBFEXIT;
-					return i;
-				}
-			} else {
-				data = *R_USB_EPT_DATA;
-				restore_flags(flags);
-
-				if ((IO_MASK(R_USB_EPT_DATA, valid) & data) &&
-				    (IO_EXTRACT(R_USB_EPT_DATA, dev, data) == devnum) &&
-				    (IO_EXTRACT(R_USB_EPT_DATA, ep, data) == endpoint) &&
-				    (IO_EXTRACT(R_USB_EPT_DATA, low_speed, data) == slow) &&
-				    (IO_EXTRACT(R_USB_EPT_DATA, max_len, data) == maxlen)) {
-					dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)",
-						 i, devnum, endpoint, out_traffic ? "OUT" : "IN");
-					DBFEXIT;
-					return i;
-				}
-			}
-		}
-	}
-
-	DBFEXIT;
-	return -1;
-}
-
-static int etrax_usb_allocate_epid(void)
-{
-	int i;
-
-	DBFENTER;
-
-	for (i = 0; i < NBR_OF_EPIDS; i++) {
-		if (!test_bit(i, (void *)&epid_usage_bitmask)) {
-			dbg_epid("Found free epid %d", i);
-			DBFEXIT;
-			return i;
-		}
-	}
-
-	dbg_epid("Found no free epids");
-	DBFEXIT;
-	return -1;
-}
-
-static int etrax_usb_submit_urb(struct urb *urb, unsigned mem_flags)
-{
-	etrax_hc_t *hc;
-	int ret = -EINVAL;
-
-	DBFENTER;
-
-	if (!urb->dev || !urb->dev->bus) {
-		return -ENODEV;
-	}
-	if (usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)) <= 0) {
-		info("Submit urb to pipe with maxpacketlen 0, pipe 0x%X\n", urb->pipe);
-		return -EMSGSIZE;
-	}
-
-	if (urb->timeout) {
-		/* FIXME. */
-		warn("urb->timeout specified, ignoring.");
-	}
-
-	hc = (etrax_hc_t*)urb->dev->bus->hcpriv;
-
-	if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
-		/* This request is for the Virtual Root Hub. */
-		ret = etrax_rh_submit_urb(urb);
-
-	} else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
-
-		ret = etrax_usb_submit_bulk_urb(urb);
-
-	} else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
-
-		ret = etrax_usb_submit_ctrl_urb(urb);
-
-	} else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
-		int bustime;
-
-		if (urb->bandwidth == 0) {
-			bustime = usb_check_bandwidth(urb->dev, urb);
-			if (bustime < 0) {
-				ret = bustime;
-			} else {
-				ret = etrax_usb_submit_intr_urb(urb);
-				if (ret == 0)
-					usb_claim_bandwidth(urb->dev, urb, bustime, 0);
-			}
-		} else {
-			/* Bandwidth already set. */
-			ret = etrax_usb_submit_intr_urb(urb);
-		}
-
-	} else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-		int bustime;
-
-		if (urb->bandwidth == 0) {
-			bustime = usb_check_bandwidth(urb->dev, urb);
-			if (bustime < 0) {
-				ret = bustime;
-			} else {
-				ret = etrax_usb_submit_isoc_urb(urb);
-				if (ret == 0)
-					usb_claim_bandwidth(urb->dev, urb, bustime, 0);
-			}
-		} else {
-			/* Bandwidth already set. */
-			ret = etrax_usb_submit_isoc_urb(urb);
-		}
-	}
-
-	DBFEXIT;
-
-        if (ret != 0)
-          printk("Submit URB error %d\n", ret);
-
-	return ret;
-}
-
-static int etrax_usb_unlink_urb(struct urb *urb, int status)
-{
-	etrax_hc_t *hc;
-	etrax_urb_priv_t *urb_priv;
-	int epid;
-	unsigned int flags;
-
-	DBFENTER;
-
-	if (!urb) {
-		return -EINVAL;
-	}
-
-	/* Disable interrupts here since a descriptor interrupt for the isoc epid
-	   will modify the sb list.  This could possibly be done more granular, but
-	   unlink_urb should not be used frequently anyway.
-	*/
-
-	save_flags(flags);
-	cli();
-
-	if (!urb->dev || !urb->dev->bus) {
-		restore_flags(flags);
-		return -ENODEV;
-	}
-	if (!urb->hcpriv) {
-		/* This happens if a device driver calls unlink on an urb that
-		   was never submitted (lazy driver) or if the urb was completed
-		   while unlink was being called. */
-		restore_flags(flags);
-		return 0;
-	}
-	if (urb->transfer_flags & URB_ASYNC_UNLINK) {
-		/* FIXME. */
-		/* If URB_ASYNC_UNLINK is set:
-		   unlink
-		   move to a separate urb list
-		   call complete at next sof with ECONNRESET
-
-		   If not:
-		   wait 1 ms
-		   unlink
-		   call complete with ENOENT
-		*/
-		warn("URB_ASYNC_UNLINK set, ignoring.");
-	}
-
-	/* One might think that urb->status = -EINPROGRESS would be a requirement for unlinking,
-	   but that doesn't work for interrupt and isochronous traffic since they are completed
-	   repeatedly, and urb->status is set then. That may in itself be a bug though. */
-
-	hc = urb->dev->bus->hcpriv;
-	urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-	epid = urb_priv->epid;
-
-	/* Set the urb status (synchronous unlink). */
-	urb->status = -ENOENT;
-	urb_priv->urb_state = UNLINK;
-
-	if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
-		int ret;
-		ret = etrax_rh_unlink_urb(urb);
-		DBFEXIT;
-		restore_flags(flags);
-		return ret;
-
-	} else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
-
-		dbg_bulk("Unlink of bulk urb (0x%lx)", (unsigned long)urb);
-
-		if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
-			/* The EP was enabled, disable it and wait. */
-			TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
-
-			/* Ah, the luxury of busy-wait. */
-			while (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[epid]));
-		}
-		/* Kicking dummy list out of the party. */
-		TxBulkEPList[epid].next = virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]);
-
-	} else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
-
-		dbg_ctrl("Unlink of ctrl urb (0x%lx)", (unsigned long)urb);
-
-		if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
-			/* The EP was enabled, disable it and wait. */
-			TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
-
-			/* Ah, the luxury of busy-wait. */
-			while (*R_DMA_CH8_SUB1_EP == virt_to_phys(&TxCtrlEPList[epid]));
-		}
-
-	} else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
-
-		dbg_intr("Unlink of intr urb (0x%lx)", (unsigned long)urb);
-
-		/* Separate function because it's a tad more complicated. */
-		etrax_usb_unlink_intr_urb(urb);
-
-	} else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-
-		dbg_isoc("Unlink of isoc urb (0x%lx)", (unsigned long)urb);
-
-		if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
-			/* The EP was enabled, disable it and wait. */
-			TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
-
-			/* Ah, the luxury of busy-wait. */
-			while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid]));
-		}
-	}
-
-	/* Note that we need to remove the urb from the urb list *before* removing its SB
-	   descriptors. (This means that the isoc eof handler might get a null urb when we
-	   are unlinking the last urb.) */
-
-	if (usb_pipetype(urb->pipe) == PIPE_BULK) {
-
-		urb_list_del(urb, epid);
-		TxBulkEPList[epid].sub = 0;
-		etrax_remove_from_sb_list(urb);
-
-	} else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
-
-		urb_list_del(urb, epid);
-		TxCtrlEPList[epid].sub = 0;
-		etrax_remove_from_sb_list(urb);
-
-	} else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
-
-		urb_list_del(urb, epid);
-		/* Sanity check (should never happen). */
-		assert(urb_list_empty(epid));
-
-		/* Release allocated bandwidth. */
-		usb_release_bandwidth(urb->dev, urb, 0);
-
-	} else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-
-		if (usb_pipeout(urb->pipe)) {
-
-			USB_SB_Desc_t *iter_sb, *prev_sb, *next_sb;
-
-			if (__urb_list_entry(urb, epid)) {
-
-				urb_list_del(urb, epid);
-				iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0;
-				prev_sb = 0;
-				while (iter_sb && (iter_sb != urb_priv->first_sb)) {
-					prev_sb = iter_sb;
-					iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
-				}
-
-				if (iter_sb == 0) {
-					/* Unlink of the URB currently being transmitted. */
-					prev_sb = 0;
-					iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0;
-				}
-
-				while (iter_sb && (iter_sb != urb_priv->last_sb)) {
-					iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
-				}
-				if (iter_sb) {
-					next_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
-				} else {
-					/* This should only happen if the DMA has completed
-					   processing the SB list for this EP while interrupts
-					   are disabled. */
-					dbg_isoc("Isoc urb not found, already sent?");
-					next_sb = 0;
-				}
-				if (prev_sb) {
-					prev_sb->next = next_sb ? virt_to_phys(next_sb) : 0;
-				} else {
-					TxIsocEPList[epid].sub = next_sb ? virt_to_phys(next_sb) : 0;
-				}
-
-				etrax_remove_from_sb_list(urb);
-				if (urb_list_empty(epid)) {
-					TxIsocEPList[epid].sub = 0;
-					dbg_isoc("Last isoc out urb epid %d", epid);
-				} else if (next_sb || prev_sb) {
-					dbg_isoc("Re-enable isoc out epid %d", epid);
-
-					TxIsocEPList[epid].hw_len = 0;
-					TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
-				} else {
-					TxIsocEPList[epid].sub = 0;
-					dbg_isoc("URB list non-empty and no SB list, EP disabled");
-				}
-			} else {
-				dbg_isoc("Urb 0x%p not found, completed already?", urb);
-			}
-		} else {
-
-			urb_list_del(urb, epid);
-
-			/* For in traffic there is only one SB descriptor for each EP even
-			   though there may be several urbs (all urbs point at the same SB). */
-			if (urb_list_empty(epid)) {
-				/* No more urbs, remove the SB. */
-				TxIsocEPList[epid].sub = 0;
-				etrax_remove_from_sb_list(urb);
-			} else {
-				TxIsocEPList[epid].hw_len = 0;
-				TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
-			}
-		}
-		/* Release allocated bandwidth. */
-		usb_release_bandwidth(urb->dev, urb, 1);
-	}
-	/* Free the epid if urb list is empty. */
-	if (urb_list_empty(epid)) {
-		etrax_usb_free_epid(epid);
-	}
-	restore_flags(flags);
-
-	/* Must be done before calling completion handler. */
-	kfree(urb_priv);
-	urb->hcpriv = 0;
-
-	if (urb->complete) {
-		urb->complete(urb, NULL);
-	}
-
-	DBFEXIT;
-	return 0;
-}
-
-static int etrax_usb_get_frame_number(struct usb_device *usb_dev)
-{
-	DBFENTER;
-	DBFEXIT;
-	return (*R_USB_FM_NUMBER & 0x7ff);
-}
-
-static irqreturn_t etrax_usb_tx_interrupt(int irq, void *vhc)
-{
-	DBFENTER;
-
-	/* This interrupt handler could be used when unlinking EP descriptors. */
-
-	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub0_descr)) {
-		USB_EP_Desc_t *ep;
-
-		//dbg_bulk("dma8_sub0_descr (BULK) intr.");
-
-		/* It should be safe clearing the interrupt here, since we don't expect to get a new
-		   one until we restart the bulk channel. */
-		*R_DMA_CH8_SUB0_CLR_INTR = IO_STATE(R_DMA_CH8_SUB0_CLR_INTR, clr_descr, do);
-
-		/* Wait while the DMA is running (though we don't expect it to be). */
-		while (*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd));
-
-		/* Advance the DMA to the next EP descriptor. */
-		ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP);
-
-		//dbg_bulk("descr intr: DMA is at 0x%lx", (unsigned long)ep);
-
-		/* ep->next is already a physical address; no need for a virt_to_phys. */
-		*R_DMA_CH8_SUB0_EP = ep->next;
-
-		/* Start the DMA bulk channel again. */
-		*R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
-	}
-	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub1_descr)) {
-		struct urb *urb;
-		int epid;
-		etrax_urb_priv_t *urb_priv;
-		unsigned long int flags;
-
-		dbg_ctrl("dma8_sub1_descr (CTRL) intr.");
-		*R_DMA_CH8_SUB1_CLR_INTR = IO_STATE(R_DMA_CH8_SUB1_CLR_INTR, clr_descr, do);
-
-		/* The complete callback gets called so we cli. */
-		save_flags(flags);
-		cli();
-
-		for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
-			if ((TxCtrlEPList[epid].sub == 0) ||
-			    (epid == DUMMY_EPID) ||
-			    (epid == INVALID_EPID)) {
-				/* Nothing here to see. */
-				continue;
-			}
-
-			/* Get the first urb (if any). */
-			urb = urb_list_first(epid);
-
-			if (urb) {
-
-				/* Sanity check. */
-				assert(usb_pipetype(urb->pipe) == PIPE_CONTROL);
-
-				urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-				assert(urb_priv);
-
-				if (urb_priv->urb_state == WAITING_FOR_DESCR_INTR) {
-					assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
-
-					etrax_usb_complete_urb(urb, 0);
-				}
-			}
-		}
-		restore_flags(flags);
-	}
-	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub2_descr)) {
-		dbg_intr("dma8_sub2_descr (INTR) intr.");
-		*R_DMA_CH8_SUB2_CLR_INTR = IO_STATE(R_DMA_CH8_SUB2_CLR_INTR, clr_descr, do);
-	}
-	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub3_descr)) {
-		struct urb *urb;
-		int epid;
-		int epid_done;
-		etrax_urb_priv_t *urb_priv;
-		USB_SB_Desc_t *sb_desc;
-
-		usb_isoc_complete_data_t *comp_data = NULL;
-
-		/* One or more isoc out transfers are done. */
-		dbg_isoc("dma8_sub3_descr (ISOC) intr.");
-
-		/* For each isoc out EP search for the first sb_desc with the intr flag
-		   set.  This descriptor must be the last packet from an URB.  Then
-		   traverse the URB list for the EP until the URB with urb_priv->last_sb
-		   matching the intr-marked sb_desc is found.  All URBs before this have
-		   been sent.
-		*/
-
-		for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
-			/* Skip past epids with no SB lists, epids used for in traffic,
-			   and special (dummy, invalid) epids. */
-			if ((TxIsocEPList[epid].sub == 0) ||
-			    (test_bit(epid, (void *)&epid_out_traffic) == 0) ||
-			    (epid == DUMMY_EPID) ||
-			    (epid == INVALID_EPID)) {
-				/* Nothing here to see. */
-				continue;
-			}
-			sb_desc = phys_to_virt(TxIsocEPList[epid].sub);
-
-			/* Find the last descriptor of the currently active URB for this ep.
-			   This is the first descriptor in the sub list marked for a descriptor
-			   interrupt. */
-			while (sb_desc && !IO_EXTRACT(USB_SB_command, intr, sb_desc->command)) {
-				sb_desc = sb_desc->next ? phys_to_virt(sb_desc->next) : 0;
-			}
-			assert(sb_desc);
-
-			dbg_isoc("Check epid %d, sub 0x%p, SB 0x%p",
-				 epid,
-				 phys_to_virt(TxIsocEPList[epid].sub),
-				 sb_desc);
-
-			epid_done = 0;
-
-			/* Get the first urb (if any). */
-			urb = urb_list_first(epid);
-			assert(urb);
-
-			while (urb && !epid_done) {
-
-				/* Sanity check. */
-				assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS);
-
-				if (!usb_pipeout(urb->pipe)) {
-					/* descr interrupts are generated only for out pipes. */
-					epid_done = 1;
-					continue;
-				}
-
-				urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-				assert(urb_priv);
-
-				if (sb_desc != urb_priv->last_sb) {
-
-					/* This urb has been sent. */
-					dbg_isoc("out URB 0x%p sent", urb);
-
-					urb_priv->urb_state = TRANSFER_DONE;
-
-				} else if ((sb_desc == urb_priv->last_sb) &&
-					   !(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) {
-
-					assert((sb_desc->command & IO_MASK(USB_SB_command, eol)) == IO_STATE(USB_SB_command, eol, yes));
-					assert(sb_desc->next == 0);
-
-					dbg_isoc("out URB 0x%p last in list, epid disabled", urb);
-					TxIsocEPList[epid].sub = 0;
-					TxIsocEPList[epid].hw_len = 0;
-					urb_priv->urb_state = TRANSFER_DONE;
-
-					epid_done = 1;
-
-				} else {
-					epid_done = 1;
-				}
-				if (!epid_done) {
-					urb = urb_list_next(urb, epid);
-				}
-			}
-
-		}
-
-		*R_DMA_CH8_SUB3_CLR_INTR = IO_STATE(R_DMA_CH8_SUB3_CLR_INTR, clr_descr, do);
-
-		comp_data = (usb_isoc_complete_data_t*)kmem_cache_alloc(isoc_compl_cache, GFP_ATOMIC);
-		assert(comp_data != NULL);
-
-                INIT_WORK(&comp_data->usb_bh, etrax_usb_isoc_descr_interrupt_bottom_half, comp_data);
-                schedule_work(&comp_data->usb_bh);
-	}
-
-	DBFEXIT;
-        return IRQ_HANDLED;
-}
-
-static void etrax_usb_isoc_descr_interrupt_bottom_half(void *data)
-{
-	usb_isoc_complete_data_t *comp_data = (usb_isoc_complete_data_t*)data;
-
-	struct urb *urb;
-	int epid;
-	int epid_done;
-	etrax_urb_priv_t *urb_priv;
-
-	DBFENTER;
-
-	dbg_isoc("dma8_sub3_descr (ISOC) bottom half.");
-
-	for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
-		unsigned long flags;
-
-		save_flags(flags);
-		cli();
-
-		epid_done = 0;
-
-		/* The descriptor interrupt handler has marked all transmitted isoch. out
-		   URBs with TRANSFER_DONE.  Now we traverse all epids and for all that
- 		   have isoch. out traffic traverse its URB list and complete the
-		   transmitted URB.
-		*/
-
-		while (!epid_done) {
-
-			/* Get the first urb (if any). */
-			urb = urb_list_first(epid);
-			if (urb == 0) {
-				epid_done = 1;
-				continue;
-			}
-
-			if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) {
-					epid_done = 1;
-					continue;
-			}
-
-			if (!usb_pipeout(urb->pipe)) {
-				/* descr interrupts are generated only for out pipes. */
-				epid_done = 1;
-				continue;
-			}
-
-			dbg_isoc("Check epid %d, SB 0x%p", epid, (char*)TxIsocEPList[epid].sub);
-
-			urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-			assert(urb_priv);
-
-			if (urb_priv->urb_state == TRANSFER_DONE) {
-				int i;
-				struct usb_iso_packet_descriptor *packet;
-
-				/* This urb has been sent. */
-				dbg_isoc("Completing isoc out URB 0x%p", urb);
-
-				for (i = 0; i < urb->number_of_packets; i++) {
-					packet = &urb->iso_frame_desc[i];
-					packet->status = 0;
-					packet->actual_length = packet->length;
-				}
-
-				etrax_usb_complete_isoc_urb(urb, 0);
-
-				if (urb_list_empty(epid)) {
-					etrax_usb_free_epid(epid);
-					epid_done = 1;
-				}
-			} else {
-				epid_done = 1;
-			}
-		}
-		restore_flags(flags);
-
-	}
-	kmem_cache_free(isoc_compl_cache, comp_data);
-
-	DBFEXIT;
-}
-
-
-
-static irqreturn_t etrax_usb_rx_interrupt(int irq, void *vhc)
-{
-	struct urb *urb;
-	etrax_urb_priv_t *urb_priv;
-	int epid = 0;
-	unsigned long flags;
-
-	/* Isoc diagnostics. */
-	static int curr_fm = 0;
-	static int prev_fm = 0;
-
-	DBFENTER;
-
-	/* Clear this interrupt. */
-	*R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do);
-
-	/* Note that this while loop assumes that all packets span only
-	   one rx descriptor. */
-
-	/* The reason we cli here is that we call the driver's callback functions. */
-	save_flags(flags);
-	cli();
-
-	while (myNextRxDesc->status & IO_MASK(USB_IN_status, eop)) {
-
-		epid = IO_EXTRACT(USB_IN_status, epid, myNextRxDesc->status);
-		urb = urb_list_first(epid);
-
-		//printk("eop for epid %d, first urb 0x%lx\n", epid, (unsigned long)urb);
-
-		if (!urb) {
-			err("No urb for epid %d in rx interrupt", epid);
-			__dump_ept_data(epid);
-			goto skip_out;
-		}
-
-		/* Note that we cannot indescriminately assert(usb_pipein(urb->pipe)) since
-		   ctrl pipes are not. */
-
-		if (myNextRxDesc->status & IO_MASK(USB_IN_status, error)) {
-			__u32 r_usb_ept_data;
-			int no_error = 0;
-
-			assert(test_bit(epid, (void *)&epid_usage_bitmask));
-
-			*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
-			nop();
-			if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-				r_usb_ept_data = *R_USB_EPT_DATA_ISO;
-
-				if ((r_usb_ept_data & IO_MASK(R_USB_EPT_DATA_ISO, valid)) &&
-				    (IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data) == 0) &&
-				    (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata))) {
-					/* Not an error, just a failure to receive an expected iso
-					   in packet in this frame.  This is not documented
-					   in the designers reference.
-					*/
-					no_error++;
-				} else {
-					warn("R_USB_EPT_DATA_ISO for epid %d = 0x%x", epid, r_usb_ept_data);
-				}
-			} else {
-				r_usb_ept_data = *R_USB_EPT_DATA;
-				warn("R_USB_EPT_DATA for epid %d = 0x%x", epid, r_usb_ept_data);
-			}
-
-			if (!no_error){
-				warn("error in rx desc->status, epid %d, first urb = 0x%lx",
-				     epid, (unsigned long)urb);
-				__dump_in_desc(myNextRxDesc);
-
-				warn("R_USB_STATUS = 0x%x", *R_USB_STATUS);
-
-				/* Check that ept was disabled when error occurred. */
-				switch (usb_pipetype(urb->pipe)) {
-				case PIPE_BULK:
-					assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)));
-					break;
-				case PIPE_CONTROL:
-					assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)));
-					break;
-				case PIPE_INTERRUPT:
-					assert(!(TxIntrEPList[epid].command & IO_MASK(USB_EP_command, enable)));
-					break;
-				case PIPE_ISOCHRONOUS:
-					assert(!(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)));
-					break;
-				default:
-					warn("etrax_usb_rx_interrupt: bad pipetype %d in urb 0x%p",
-					     usb_pipetype(urb->pipe),
-					     urb);
-				}
-				etrax_usb_complete_urb(urb, -EPROTO);
-				goto skip_out;
-			}
-		}
-
-		urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-		assert(urb_priv);
-
-		if ((usb_pipetype(urb->pipe) == PIPE_BULK) ||
-		    (usb_pipetype(urb->pipe) == PIPE_CONTROL) ||
-		    (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) {
-
-			if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
-				/* We get nodata for empty data transactions, and the rx descriptor's
-				   hw_len field is not valid in that case. No data to copy in other
-				   words. */
-			} else {
-				/* Make sure the data fits in the buffer. */
-				assert(urb_priv->rx_offset + myNextRxDesc->hw_len
-				       <= urb->transfer_buffer_length);
-
-				memcpy(urb->transfer_buffer + urb_priv->rx_offset,
-				       phys_to_virt(myNextRxDesc->buf), myNextRxDesc->hw_len);
-				urb_priv->rx_offset += myNextRxDesc->hw_len;
-			}
-
-			if (myNextRxDesc->status & IO_MASK(USB_IN_status, eot)) {
-				if ((usb_pipetype(urb->pipe) == PIPE_CONTROL) &&
-				    ((TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)) ==
-				     IO_STATE(USB_EP_command, enable, yes))) {
-					/* The EP is still enabled, so the OUT packet used to ack
-					   the in data is probably not processed yet.  If the EP
-					   sub pointer has not moved beyond urb_priv->last_sb mark
-					   it for a descriptor interrupt and complete the urb in
-					   the descriptor interrupt handler.
-					*/
-					USB_SB_Desc_t *sub = TxCtrlEPList[urb_priv->epid].sub ? phys_to_virt(TxCtrlEPList[urb_priv->epid].sub) : 0;
-
-					while ((sub != NULL) && (sub != urb_priv->last_sb)) {
-						sub = sub->next ? phys_to_virt(sub->next) : 0;
-					}
-					if (sub != NULL) {
-						/* The urb has not been fully processed. */
-						urb_priv->urb_state = WAITING_FOR_DESCR_INTR;
-					} else {
-						warn("(CTRL) epid enabled and urb (0x%p) processed, ep->sub=0x%p", urb, (char*)TxCtrlEPList[urb_priv->epid].sub);
-						etrax_usb_complete_urb(urb, 0);
-					}
-				} else {
-					etrax_usb_complete_urb(urb, 0);
-				}
-			}
-
-		} else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-
-			struct usb_iso_packet_descriptor *packet;
-
-			if (urb_priv->urb_state == UNLINK) {
-				info("Ignoring rx data for urb being unlinked.");
-				goto skip_out;
-			} else if (urb_priv->urb_state == NOT_STARTED) {
-				info("What? Got rx data for urb that isn't started?");
-				goto skip_out;
-			}
-
-			packet = &urb->iso_frame_desc[urb_priv->isoc_packet_counter];
-			packet->status = 0;
-
-			if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
-				/* We get nodata for empty data transactions, and the rx descriptor's
-				   hw_len field is not valid in that case. We copy 0 bytes however to
-				   stay in synch. */
-				packet->actual_length = 0;
-			} else {
-				packet->actual_length = myNextRxDesc->hw_len;
-				/* Make sure the data fits in the buffer. */
-				assert(packet->actual_length <= packet->length);
-				memcpy(urb->transfer_buffer + packet->offset,
-				       phys_to_virt(myNextRxDesc->buf), packet->actual_length);
-			}
-
-			/* Increment the packet counter. */
-			urb_priv->isoc_packet_counter++;
-
-			/* Note that we don't care about the eot field in the rx descriptor's status.
-			   It will always be set for isoc traffic. */
-			if (urb->number_of_packets == urb_priv->isoc_packet_counter) {
-
-				/* Out-of-synch diagnostics. */
-				curr_fm = (*R_USB_FM_NUMBER & 0x7ff);
-				if (((prev_fm + urb_priv->isoc_packet_counter) % (0x7ff + 1)) != curr_fm) {
-					/* This test is wrong, if there is more than one isoc
-					   in endpoint active it will always calculate wrong
-					   since prev_fm is shared by all endpoints.
-
-					   FIXME Make this check per URB using urb->start_frame.
-					*/
-					dbg_isoc("Out of synch? Previous frame = %d, current frame = %d",
-						 prev_fm, curr_fm);
-
-				}
-				prev_fm = curr_fm;
-
-				/* Complete the urb with status OK. */
-				etrax_usb_complete_isoc_urb(urb, 0);
-			}
-		}
-
-	skip_out:
-
-		/* DMA IN cache bug. Flush the DMA IN buffer from the cache. (struct etrax_dma_descr
-		   has the same layout as USB_IN_Desc for the relevant fields.) */
-		prepare_rx_descriptor((struct etrax_dma_descr*)myNextRxDesc);
-
-		myPrevRxDesc = myNextRxDesc;
-		myPrevRxDesc->command |= IO_MASK(USB_IN_command, eol);
-		myLastRxDesc->command &= ~IO_MASK(USB_IN_command, eol);
-		myLastRxDesc = myPrevRxDesc;
-
-		myNextRxDesc->status = 0;
-		myNextRxDesc = phys_to_virt(myNextRxDesc->next);
-	}
-
-	restore_flags(flags);
-
-	DBFEXIT;
-
-        return IRQ_HANDLED;
-}
-
-
-/* This function will unlink the SB descriptors associated with this urb. */
-static int etrax_remove_from_sb_list(struct urb *urb)
-{
-	USB_SB_Desc_t *next_sb, *first_sb, *last_sb;
-	etrax_urb_priv_t *urb_priv;
-	int i = 0;
-
-	DBFENTER;
-
-	urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-	assert(urb_priv);
-
-	/* Just a sanity check. Since we don't fiddle with the DMA list the EP descriptor
-	   doesn't really need to be disabled, it's just that we expect it to be. */
-	if (usb_pipetype(urb->pipe) == PIPE_BULK) {
-		assert(!(TxBulkEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
-	} else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
-		assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
-	}
-
-	first_sb = urb_priv->first_sb;
-	last_sb = urb_priv->last_sb;
-
-	assert(first_sb);
-	assert(last_sb);
-
-	while (first_sb != last_sb) {
-		next_sb = (USB_SB_Desc_t *)phys_to_virt(first_sb->next);
-		kmem_cache_free(usb_desc_cache, first_sb);
-		first_sb = next_sb;
-		i++;
-	}
-	kmem_cache_free(usb_desc_cache, last_sb);
-	i++;
-	dbg_sb("%d SB descriptors freed", i);
-	/* Compare i with urb->number_of_packets for Isoc traffic.
-	   Should be same when calling unlink_urb */
-
-	DBFEXIT;
-
-	return i;
-}
-
-static int etrax_usb_submit_bulk_urb(struct urb *urb)
-{
-	int epid;
-	int empty;
-	unsigned long flags;
-	etrax_urb_priv_t *urb_priv;
-
-	DBFENTER;
-
-	/* Epid allocation, empty check and list add must be protected.
-	   Read about this in etrax_usb_submit_ctrl_urb. */
-
-	spin_lock_irqsave(&urb_list_lock, flags);
-	epid = etrax_usb_setup_epid(urb);
-	if (epid == -1) {
-		DBFEXIT;
-		spin_unlock_irqrestore(&urb_list_lock, flags);
-		return -ENOMEM;
-	}
-	empty = urb_list_empty(epid);
-	urb_list_add(urb, epid);
-	spin_unlock_irqrestore(&urb_list_lock, flags);
-
-	dbg_bulk("Adding bulk %s urb 0x%lx to %s list, epid %d",
-		 usb_pipein(urb->pipe) ? "IN" : "OUT", (unsigned long)urb, empty ? "empty" : "", epid);
-
-	/* Mark the urb as being in progress. */
-	urb->status = -EINPROGRESS;
-
-	/* Setup the hcpriv data. */
-	urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
-	assert(urb_priv != NULL);
-	/* This sets rx_offset to 0. */
-	urb_priv->urb_state = NOT_STARTED;
-	urb->hcpriv = urb_priv;
-
-	if (empty) {
-		etrax_usb_add_to_bulk_sb_list(urb, epid);
-	}
-
-	DBFEXIT;
-
-	return 0;
-}
-
-static void etrax_usb_add_to_bulk_sb_list(struct urb *urb, int epid)
-{
-	USB_SB_Desc_t *sb_desc;
-	etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-	unsigned long flags;
-	char maxlen;
-
-	DBFENTER;
-
-	dbg_bulk("etrax_usb_add_to_bulk_sb_list, urb 0x%lx", (unsigned long)urb);
-
-	maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
-
-	sb_desc = kmem_cache_zalloc(usb_desc_cache, SLAB_FLAG);
-	assert(sb_desc != NULL);
-
-
-	if (usb_pipeout(urb->pipe)) {
-
-		dbg_bulk("Grabbing bulk OUT, urb 0x%lx, epid %d", (unsigned long)urb, epid);
-
-		/* This is probably a sanity check of the bulk transaction length
-		   not being larger than 64 kB. */
-		if (urb->transfer_buffer_length > 0xffff) {
-			panic("urb->transfer_buffer_length > 0xffff");
-		}
-
-		sb_desc->sw_len = urb->transfer_buffer_length;
-
-		/* The rem field is don't care if it's not a full-length transfer, so setting
-		   it shouldn't hurt. Also, rem isn't used for OUT traffic. */
-		sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) |
-				    IO_STATE(USB_SB_command, tt, out) |
-				    IO_STATE(USB_SB_command, eot, yes) |
-				    IO_STATE(USB_SB_command, eol, yes));
-
-		/* The full field is set to yes, even if we don't actually check that this is
-		   a full-length transfer (i.e., that transfer_buffer_length % maxlen = 0).
-		   Setting full prevents the USB controller from sending an empty packet in
-		   that case.  However, if URB_ZERO_PACKET was set we want that. */
-		if (!(urb->transfer_flags & URB_ZERO_PACKET)) {
-			sb_desc->command |= IO_STATE(USB_SB_command, full, yes);
-		}
-
-		sb_desc->buf = virt_to_phys(urb->transfer_buffer);
-		sb_desc->next = 0;
-
-	} else if (usb_pipein(urb->pipe)) {
-
-		dbg_bulk("Grabbing bulk IN, urb 0x%lx, epid %d", (unsigned long)urb, epid);
-
-		sb_desc->sw_len = urb->transfer_buffer_length ?
-			(urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
-
-		/* The rem field is don't care if it's not a full-length transfer, so setting
-		   it shouldn't hurt. */
-		sb_desc->command =
-			(IO_FIELD(USB_SB_command, rem,
-				  urb->transfer_buffer_length % maxlen) |
-			 IO_STATE(USB_SB_command, tt, in) |
-			 IO_STATE(USB_SB_command, eot, yes) |
-			 IO_STATE(USB_SB_command, eol, yes));
-
-		sb_desc->buf = 0;
-		sb_desc->next = 0;
-	}
-
-	urb_priv->first_sb = sb_desc;
-	urb_priv->last_sb = sb_desc;
-	urb_priv->epid = epid;
-
-	urb->hcpriv = urb_priv;
-
-	/* Reset toggle bits and reset error count. */
-	save_flags(flags);
-	cli();
-
-	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
-	nop();
-
-	/* FIXME: Is this a special case since the hold field is checked,
-	   or should we check hold in a lot of other cases as well? */
-	if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) {
-		panic("Hold was set in %s", __FUNCTION__);
-	}
-
-	/* Reset error counters (regardless of which direction this traffic is). */
-	*R_USB_EPT_DATA &=
-		~(IO_MASK(R_USB_EPT_DATA, error_count_in) |
-		  IO_MASK(R_USB_EPT_DATA, error_count_out));
-
-	/* Software must preset the toggle bits. */
-	if (usb_pipeout(urb->pipe)) {
-		char toggle =
-			usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
-		*R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_out);
-		*R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_out, toggle);
-	} else {
-		char toggle =
-			usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
-		*R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_in);
-		*R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_in, toggle);
-	}
-
-	/* Assert that the EP descriptor is disabled. */
-	assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)));
-
-	/* The reason we set the EP's sub pointer directly instead of
-	   walking the SB list and linking it last in the list is that we only
-	   have one active urb at a time (the rest are queued). */
-
-	/* Note that we cannot have interrupts running when we have set the SB descriptor
-	   but the EP is not yet enabled.  If a bulk eot happens for another EP, we will
-	   find this EP disabled and with a SB != 0, which will make us think that it's done. */
-	TxBulkEPList[epid].sub = virt_to_phys(sb_desc);
-	TxBulkEPList[epid].hw_len = 0;
-	/* Note that we don't have to fill in the ep_id field since this
-	   was done when we allocated the EP descriptors in init_tx_bulk_ep. */
-
-	/* Check if the dummy list is already with us (if several urbs were queued). */
-	if (TxBulkEPList[epid].next != virt_to_phys(&TxBulkDummyEPList[epid][0])) {
-
-		dbg_bulk("Inviting dummy list to the party for urb 0x%lx, epid %d",
-			 (unsigned long)urb, epid);
-
-		/* The last EP in the dummy list already has its next pointer set to
-		   TxBulkEPList[epid].next. */
-
-		/* We don't need to check if the DMA is at this EP or not before changing the
-		   next pointer, since we will do it in one 32-bit write (EP descriptors are
-		   32-bit aligned). */
-		TxBulkEPList[epid].next = virt_to_phys(&TxBulkDummyEPList[epid][0]);
-	}
-	/* Enable the EP descr. */
-	dbg_bulk("Enabling bulk EP for urb 0x%lx, epid %d", (unsigned long)urb, epid);
-	TxBulkEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
-
-	/* Everything is set up, safe to enable interrupts again. */
-	restore_flags(flags);
-
-	/* If the DMA bulk channel isn't running, we need to restart it if it
-	   has stopped at the last EP descriptor (DMA stopped because there was
-	   no more traffic) or if it has stopped at a dummy EP with the intr flag
-	   set (DMA stopped because we were too slow in inserting new traffic). */
-	if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) {
-
-		USB_EP_Desc_t *ep;
-		ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP);
-		dbg_bulk("DMA channel not running in add");
-		dbg_bulk("DMA is at 0x%lx", (unsigned long)ep);
-
-		if (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[NBR_OF_EPIDS - 1]) ||
-		    (ep->command & 0x8) >> 3) {
-			*R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
-			/* Update/restart the bulk start timer since we just started the channel. */
-			mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL);
-			/* Update/restart the bulk eot timer since we just inserted traffic. */
-			mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
-		}
-	}
-
-	DBFEXIT;
-}
-
-static void etrax_usb_complete_bulk_urb(struct urb *urb, int status)
-{
-	etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-	int epid = urb_priv->epid;
-	unsigned long flags;
-
-	DBFENTER;
-
-	if (status)
-		warn("Completing bulk urb with status %d.", status);
-
-	dbg_bulk("Completing bulk urb 0x%lx for epid %d", (unsigned long)urb, epid);
-
-	/* Update the urb list. */
-	urb_list_del(urb, epid);
-
-	/* For an IN pipe, we always set the actual length, regardless of whether there was
-	   an error or not (which means the device driver can use the data if it wants to). */
-	if (usb_pipein(urb->pipe)) {
-		urb->actual_length = urb_priv->rx_offset;
-	} else {
-		/* Set actual_length for OUT urbs also; the USB mass storage driver seems
-		   to want that. We wouldn't know of any partial writes if there was an error. */
-		if (status == 0) {
-			urb->actual_length = urb->transfer_buffer_length;
-		} else {
-			urb->actual_length = 0;
-		}
-	}
-
-	/* FIXME: Is there something of the things below we shouldn't do if there was an error?
-	   Like, maybe we shouldn't toggle the toggle bits, or maybe we shouldn't insert more traffic. */
-
-	save_flags(flags);
-	cli();
-
-	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
-	nop();
-
-	/* We need to fiddle with the toggle bits because the hardware doesn't do it for us. */
-	if (usb_pipeout(urb->pipe)) {
-		char toggle =
-			IO_EXTRACT(R_USB_EPT_DATA, t_out, *R_USB_EPT_DATA);
-		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
-			      usb_pipeout(urb->pipe), toggle);
-	} else {
-		char toggle =
-			IO_EXTRACT(R_USB_EPT_DATA, t_in, *R_USB_EPT_DATA);
-		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
-			      usb_pipeout(urb->pipe), toggle);
-	}
-	restore_flags(flags);
-
-	/* Remember to free the SBs. */
-	etrax_remove_from_sb_list(urb);
-	kfree(urb_priv);
-	urb->hcpriv = 0;
-
-	/* If there are any more urb's in the list we'd better start sending */
-	if (!urb_list_empty(epid)) {
-
-		struct urb *new_urb;
-
-		/* Get the first urb. */
-		new_urb = urb_list_first(epid);
-		assert(new_urb);
-
-		dbg_bulk("More bulk for epid %d", epid);
-
-		etrax_usb_add_to_bulk_sb_list(new_urb, epid);
-	}
-
-	urb->status = status;
-
-	/* We let any non-zero status from the layer above have precedence. */
-	if (status == 0) {
-		/* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length)
-		   is to be treated as an error. */
-		if (urb->transfer_flags & URB_SHORT_NOT_OK) {
-			if (usb_pipein(urb->pipe) &&
-			    (urb->actual_length !=
-			     usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) {
-				urb->status = -EREMOTEIO;
-			}
-		}
-	}
-
-	if (urb->complete) {
-		urb->complete(urb, NULL);
-	}
-
-	if (urb_list_empty(epid)) {
-		/* This means that this EP is now free, deconfigure it. */
-		etrax_usb_free_epid(epid);
-
-		/* No more traffic; time to clean up.
-		   Must set sub pointer to 0, since we look at the sub pointer when handling
-		   the bulk eot interrupt. */
-
-		dbg_bulk("No bulk for epid %d", epid);
-
-		TxBulkEPList[epid].sub = 0;
-
-		/* Unlink the dummy list. */
-
-		dbg_bulk("Kicking dummy list out of party for urb 0x%lx, epid %d",
-			 (unsigned long)urb, epid);
-
-		/* No need to wait for the DMA before changing the next pointer.
-		   The modulo NBR_OF_EPIDS isn't actually necessary, since we will never use
-		   the last one (INVALID_EPID) for actual traffic. */
-		TxBulkEPList[epid].next =
-			virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]);
-	}
-
-	DBFEXIT;
-}
-
-static int etrax_usb_submit_ctrl_urb(struct urb *urb)
-{
-	int epid;
-	int empty;
-	unsigned long flags;
-	etrax_urb_priv_t *urb_priv;
-
-	DBFENTER;
-
-	/* FIXME: Return -ENXIO if there is already a queued urb for this endpoint? */
-
-	/* Epid allocation, empty check and list add must be protected.
-
-	   Epid allocation because if we find an existing epid for this endpoint an urb might be
-	   completed (emptying the list) before we add the new urb to the list, causing the epid
-	   to be de-allocated. We would then start the transfer with an invalid epid -> epid attn.
-
-	   Empty check and add because otherwise we might conclude that the list is not empty,
-	   after which it becomes empty before we add the new urb to the list, causing us not to
-	   insert the new traffic into the SB list. */
-
-	spin_lock_irqsave(&urb_list_lock, flags);
-	epid = etrax_usb_setup_epid(urb);
-	if (epid == -1) {
-		spin_unlock_irqrestore(&urb_list_lock, flags);
-		DBFEXIT;
-		return -ENOMEM;
-	}
-	empty = urb_list_empty(epid);
-	urb_list_add(urb, epid);
-	spin_unlock_irqrestore(&urb_list_lock, flags);
-
-	dbg_ctrl("Adding ctrl urb 0x%lx to %s list, epid %d",
-		 (unsigned long)urb, empty ? "empty" : "", epid);
-
-	/* Mark the urb as being in progress. */
-	urb->status = -EINPROGRESS;
-
-	/* Setup the hcpriv data. */
-	urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
-	assert(urb_priv != NULL);
-	/* This sets rx_offset to 0. */
-	urb_priv->urb_state = NOT_STARTED;
-	urb->hcpriv = urb_priv;
-
-	if (empty) {
-		etrax_usb_add_to_ctrl_sb_list(urb, epid);
-	}
-
-	DBFEXIT;
-
-	return 0;
-}
-
-static void etrax_usb_add_to_ctrl_sb_list(struct urb *urb, int epid)
-{
-	USB_SB_Desc_t *sb_desc_setup;
-	USB_SB_Desc_t *sb_desc_data;
-	USB_SB_Desc_t *sb_desc_status;
-
-	etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-
-	unsigned long flags;
-	char maxlen;
-
-	DBFENTER;
-
-	maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
-
-	sb_desc_setup = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
-	assert(sb_desc_setup != NULL);
-	sb_desc_status = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
-	assert(sb_desc_status != NULL);
-
-	/* Initialize the mandatory setup SB descriptor (used only in control transfers) */
-	sb_desc_setup->sw_len = 8;
-	sb_desc_setup->command = (IO_FIELD(USB_SB_command, rem, 0) |
-				  IO_STATE(USB_SB_command, tt, setup) |
-				  IO_STATE(USB_SB_command, full, yes) |
-				  IO_STATE(USB_SB_command, eot, yes));
-
-	sb_desc_setup->buf = virt_to_phys(urb->setup_packet);
-
-	if (usb_pipeout(urb->pipe)) {
-		dbg_ctrl("Transfer for epid %d is OUT", epid);
-
-		/* If this Control OUT transfer has an optional data stage we add an OUT token
-		   before the mandatory IN (status) token, hence the reordered SB list */
-
-		sb_desc_setup->next = virt_to_phys(sb_desc_status);
-		if (urb->transfer_buffer) {
-
-			dbg_ctrl("This OUT transfer has an extra data stage");
-
-			sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
-			assert(sb_desc_data != NULL);
-
-			sb_desc_setup->next = virt_to_phys(sb_desc_data);
-
-			sb_desc_data->sw_len = urb->transfer_buffer_length;
-			sb_desc_data->command = (IO_STATE(USB_SB_command, tt, out) |
-						 IO_STATE(USB_SB_command, full, yes) |
-						 IO_STATE(USB_SB_command, eot, yes));
-			sb_desc_data->buf = virt_to_phys(urb->transfer_buffer);
-			sb_desc_data->next = virt_to_phys(sb_desc_status);
-		}
-
-		sb_desc_status->sw_len = 1;
-		sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) |
-					   IO_STATE(USB_SB_command, tt, in) |
-					   IO_STATE(USB_SB_command, eot, yes) |
-					   IO_STATE(USB_SB_command, intr, yes) |
-					   IO_STATE(USB_SB_command, eol, yes));
-
-		sb_desc_status->buf = 0;
-		sb_desc_status->next = 0;
-
-	} else if (usb_pipein(urb->pipe)) {
-
-		dbg_ctrl("Transfer for epid %d is IN", epid);
-		dbg_ctrl("transfer_buffer_length = %d", urb->transfer_buffer_length);
-		dbg_ctrl("rem is calculated to %d", urb->transfer_buffer_length % maxlen);
-
-		sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
-		assert(sb_desc_data != NULL);
-
-		sb_desc_setup->next = virt_to_phys(sb_desc_data);
-
-		sb_desc_data->sw_len = urb->transfer_buffer_length ?
-			(urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
-		dbg_ctrl("sw_len got %d", sb_desc_data->sw_len);
-
-		sb_desc_data->command =
-			(IO_FIELD(USB_SB_command, rem,
-				  urb->transfer_buffer_length % maxlen) |
-			 IO_STATE(USB_SB_command, tt, in) |
-			 IO_STATE(USB_SB_command, eot, yes));
-
-		sb_desc_data->buf = 0;
-		sb_desc_data->next = virt_to_phys(sb_desc_status);
-
-		/* Read comment at zout_buffer declaration for an explanation to this. */
-		sb_desc_status->sw_len = 1;
-		sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) |
-					   IO_STATE(USB_SB_command, tt, zout) |
-					   IO_STATE(USB_SB_command, full, yes) |
-					   IO_STATE(USB_SB_command, eot, yes) |
-					   IO_STATE(USB_SB_command, intr, yes) |
-					   IO_STATE(USB_SB_command, eol, yes));
-
-		sb_desc_status->buf = virt_to_phys(&zout_buffer[0]);
-		sb_desc_status->next = 0;
-	}
-
-	urb_priv->first_sb = sb_desc_setup;
-	urb_priv->last_sb = sb_desc_status;
-	urb_priv->epid = epid;
-
-	urb_priv->urb_state = STARTED;
-
-	/* Reset toggle bits and reset error count, remember to di and ei */
-	/* Warning: it is possible that this locking doesn't work with bottom-halves */
-
-	save_flags(flags);
-	cli();
-
-	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
-	nop();
-	if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) {
-		panic("Hold was set in %s", __FUNCTION__);
-	}
-
-
-	/* FIXME: Compare with etrax_usb_add_to_bulk_sb_list where the toggle bits
-	   are set to a specific value. Why the difference? Read "Transfer and Toggle Bits
-	   in Designer's Reference, p. 8 - 11. */
-	*R_USB_EPT_DATA &=
-		~(IO_MASK(R_USB_EPT_DATA, error_count_in) |
-		  IO_MASK(R_USB_EPT_DATA, error_count_out) |
-		  IO_MASK(R_USB_EPT_DATA, t_in) |
-		  IO_MASK(R_USB_EPT_DATA, t_out));
-
-	/* Since we use the rx interrupt to complete ctrl urbs, we can enable interrupts now
-	   (i.e. we don't check the sub pointer on an eot interrupt like we do for bulk traffic). */
-	restore_flags(flags);
-
-	/* Assert that the EP descriptor is disabled. */
-	assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)));
-
-	/* Set up and enable the EP descriptor. */
-	TxCtrlEPList[epid].sub = virt_to_phys(sb_desc_setup);
-	TxCtrlEPList[epid].hw_len = 0;
-	TxCtrlEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
-
-	/* We start the DMA sub channel without checking if it's running or not, because:
-	   1) If it's already running, issuing the start command is a nop.
-	   2) We avoid a test-and-set race condition. */
-	*R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
-
-	DBFEXIT;
-}
-
-static void etrax_usb_complete_ctrl_urb(struct urb *urb, int status)
-{
-	etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-	int epid = urb_priv->epid;
-
-	DBFENTER;
-
-	if (status)
-		warn("Completing ctrl urb with status %d.", status);
-
-	dbg_ctrl("Completing ctrl epid %d, urb 0x%lx", epid, (unsigned long)urb);
-
-	/* Remove this urb from the list. */
-	urb_list_del(urb, epid);
-
-	/* For an IN pipe, we always set the actual length, regardless of whether there was
-	   an error or not (which means the device driver can use the data if it wants to). */
-	if (usb_pipein(urb->pipe)) {
-		urb->actual_length = urb_priv->rx_offset;
-	}
-
-	/* FIXME: Is there something of the things below we shouldn't do if there was an error?
-	   Like, maybe we shouldn't insert more traffic. */
-
-	/* Remember to free the SBs. */
-	etrax_remove_from_sb_list(urb);
-	kfree(urb_priv);
-	urb->hcpriv = 0;
-
-	/* If there are any more urbs in the list we'd better start sending. */
-	if (!urb_list_empty(epid)) {
-		struct urb *new_urb;
-
-		/* Get the first urb. */
-		new_urb = urb_list_first(epid);
-		assert(new_urb);
-
-		dbg_ctrl("More ctrl for epid %d, first urb = 0x%lx", epid, (unsigned long)new_urb);
-
-		etrax_usb_add_to_ctrl_sb_list(new_urb, epid);
-	}
-
-	urb->status = status;
-
-	/* We let any non-zero status from the layer above have precedence. */
-	if (status == 0) {
-		/* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length)
-		   is to be treated as an error. */
-		if (urb->transfer_flags & URB_SHORT_NOT_OK) {
-			if (usb_pipein(urb->pipe) &&
-			    (urb->actual_length !=
-			     usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) {
-				urb->status = -EREMOTEIO;
-			}
-		}
-	}
-
-	if (urb->complete) {
-		urb->complete(urb, NULL);
-	}
-
-	if (urb_list_empty(epid)) {
-		/* No more traffic. Time to clean up. */
-		etrax_usb_free_epid(epid);
-		/* Must set sub pointer to 0. */
-		dbg_ctrl("No ctrl for epid %d", epid);
-		TxCtrlEPList[epid].sub = 0;
-	}
-
-	DBFEXIT;
-}
-
-static int etrax_usb_submit_intr_urb(struct urb *urb)
-{
-
-	int epid;
-
-	DBFENTER;
-
-	if (usb_pipeout(urb->pipe)) {
-		/* Unsupported transfer type.
-		   We don't support interrupt out traffic. (If we do, we can't support
-		   intervals for neither in or out traffic, but are forced to schedule all
-		   interrupt traffic in one frame.) */
-		return -EINVAL;
-	}
-
-	epid = etrax_usb_setup_epid(urb);
-	if (epid == -1) {
-		DBFEXIT;
-		return -ENOMEM;
-	}
-
-	if (!urb_list_empty(epid)) {
-		/* There is already a queued urb for this endpoint. */
-		etrax_usb_free_epid(epid);
-		return -ENXIO;
-	}
-
-	urb->status = -EINPROGRESS;
-
-	dbg_intr("Add intr urb 0x%lx, to list, epid %d", (unsigned long)urb, epid);
-
-	urb_list_add(urb, epid);
-	etrax_usb_add_to_intr_sb_list(urb, epid);
-
-	return 0;
-
-	DBFEXIT;
-}
-
-static void etrax_usb_add_to_intr_sb_list(struct urb *urb, int epid)
-{
-
-	volatile USB_EP_Desc_t *tmp_ep;
-	volatile USB_EP_Desc_t *first_ep;
-
-	char maxlen;
-	int interval;
-	int i;
-
-	etrax_urb_priv_t *urb_priv;
-
-	DBFENTER;
-
-	maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
-	interval = urb->interval;
-
-	urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
-	assert(urb_priv != NULL);
-	urb->hcpriv = urb_priv;
-
-	first_ep = &TxIntrEPList[0];
-
-	/* Round of the interval to 2^n, it is obvious that this code favours
-	   smaller numbers, but that is actually a good thing */
-	/* FIXME: The "rounding error" for larger intervals will be quite
-	   large. For in traffic this shouldn't be a problem since it will only
-	   mean that we "poll" more often. */
-	for (i = 0; interval; i++) {
-		interval = interval >> 1;
-	}
-	interval = 1 << (i - 1);
-
-	dbg_intr("Interval rounded to %d", interval);
-
-	tmp_ep = first_ep;
-	i = 0;
-	do {
-		if (tmp_ep->command & IO_MASK(USB_EP_command, eof)) {
-			if ((i % interval) == 0) {
-				/* Insert the traffic ep after tmp_ep */
-				USB_EP_Desc_t *ep_desc;
-				USB_SB_Desc_t *sb_desc;
-
-				dbg_intr("Inserting EP for epid %d", epid);
-
-				ep_desc = (USB_EP_Desc_t *)
-					kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
-				sb_desc = (USB_SB_Desc_t *)
-					kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
-				assert(ep_desc != NULL);
-				CHECK_ALIGN(ep_desc);
-				assert(sb_desc != NULL);
-
-				ep_desc->sub = virt_to_phys(sb_desc);
-				ep_desc->hw_len = 0;
-				ep_desc->command = (IO_FIELD(USB_EP_command, epid, epid) |
-						    IO_STATE(USB_EP_command, enable, yes));
-
-
-				/* Round upwards the number of packets of size maxlen
-				   that this SB descriptor should receive. */
-				sb_desc->sw_len = urb->transfer_buffer_length ?
-					(urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
-				sb_desc->next = 0;
-				sb_desc->buf = 0;
-				sb_desc->command =
-					(IO_FIELD(USB_SB_command, rem, urb->transfer_buffer_length % maxlen) |
-					 IO_STATE(USB_SB_command, tt, in) |
-					 IO_STATE(USB_SB_command, eot, yes) |
-					 IO_STATE(USB_SB_command, eol, yes));
-
-				ep_desc->next = tmp_ep->next;
-				tmp_ep->next = virt_to_phys(ep_desc);
-			}
-			i++;
-		}
-		tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next);
-	} while (tmp_ep != first_ep);
-
-
-	/* Note that first_sb/last_sb doesn't apply to interrupt traffic. */
-	urb_priv->epid = epid;
-
-	/* We start the DMA sub channel without checking if it's running or not, because:
-	   1) If it's already running, issuing the start command is a nop.
-	   2) We avoid a test-and-set race condition. */
-	*R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
-
-	DBFEXIT;
-}
-
-
-
-static void etrax_usb_complete_intr_urb(struct urb *urb, int status)
-{
-	etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-	int epid = urb_priv->epid;
-
-	DBFENTER;
-
-	if (status)
-		warn("Completing intr urb with status %d.", status);
-
-	dbg_intr("Completing intr epid %d, urb 0x%lx", epid, (unsigned long)urb);
-
-	urb->status = status;
-	urb->actual_length = urb_priv->rx_offset;
-
-	dbg_intr("interrupt urb->actual_length = %d", urb->actual_length);
-
-	/* We let any non-zero status from the layer above have precedence. */
-	if (status == 0) {
-		/* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length)
-		   is to be treated as an error. */
-		if (urb->transfer_flags & URB_SHORT_NOT_OK) {
-			if (urb->actual_length !=
-			    usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) {
-				urb->status = -EREMOTEIO;
-			}
-		}
-	}
-
-	/* The driver will resubmit the URB so we need to remove it first */
-        etrax_usb_unlink_urb(urb, 0);
-	if (urb->complete) {
-		urb->complete(urb, NULL);
-	}
-
-	DBFEXIT;
-}
-
-
-static int etrax_usb_submit_isoc_urb(struct urb *urb)
-{
-	int epid;
-	unsigned long flags;
-
-	DBFENTER;
-
-	dbg_isoc("Submitting isoc urb = 0x%lx", (unsigned long)urb);
-
-	/* Epid allocation, empty check and list add must be protected.
-	   Read about this in etrax_usb_submit_ctrl_urb. */
-
-	spin_lock_irqsave(&urb_list_lock, flags);
-	/* Is there an active epid for this urb ? */
-	epid = etrax_usb_setup_epid(urb);
-	if (epid == -1) {
-		DBFEXIT;
-		spin_unlock_irqrestore(&urb_list_lock, flags);
-		return -ENOMEM;
-	}
-
-	/* Ok, now we got valid endpoint, lets insert some traffic */
-
-	urb->status = -EINPROGRESS;
-
-	/* Find the last urb in the URB_List and add this urb after that one.
-	   Also add the traffic, that is do an etrax_usb_add_to_isoc_sb_list.  This
-	   is important to make this in "real time" since isochronous traffic is
-	   time sensitive. */
-
-	dbg_isoc("Adding isoc urb to (possibly empty) list");
-	urb_list_add(urb, epid);
-	etrax_usb_add_to_isoc_sb_list(urb, epid);
-	spin_unlock_irqrestore(&urb_list_lock, flags);
-
-	DBFEXIT;
-
-	return 0;
-}
-
-static void etrax_usb_check_error_isoc_ep(const int epid)
-{
-	unsigned long int flags;
-	int error_code;
-	__u32 r_usb_ept_data;
-
-	/* We can't read R_USB_EPID_ATTN here since it would clear the iso_eof,
-	   bulk_eot and epid_attn interrupts.  So we just check the status of
-	   the epid without testing if for it in R_USB_EPID_ATTN. */
-
-
-	save_flags(flags);
-	cli();
-	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
-	nop();
-	/* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO
-	   registers, they are located at the same address and are of the same size.
-	   In other words, this read should be ok for isoc also. */
-	r_usb_ept_data = *R_USB_EPT_DATA;
-	restore_flags(flags);
-
-	error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data);
-
-	if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) {
-		warn("Hold was set for epid %d.", epid);
-		return;
-	}
-
-	if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, no_error)) {
-
-		/* This indicates that the SB list of the ept was completed before
-		   new data was appended to it.  This is not an error, but indicates
-		   large system or USB load and could possibly cause trouble for
-		   very timing sensitive USB device drivers so we log it.
-		*/
-		info("Isoc. epid %d disabled with no error", epid);
-		return;
-
-	} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, stall)) {
-		/* Not really a protocol error, just says that the endpoint gave
-		   a stall response. Note that error_code cannot be stall for isoc. */
-		panic("Isoc traffic cannot stall");
-
-	} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, bus_error)) {
-		/* Two devices responded to a transaction request. Must be resolved
-		   by software. FIXME: Reset ports? */
-		panic("Bus error for epid %d."
-		      " Two devices responded to transaction request",
-		      epid);
-
-	} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
-		/* DMA overrun or underrun. */
-		warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
-
-		/* It seems that error_code = buffer_error in
-		   R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS
-		   are the same error. */
-	}
-}
-
-
-static void etrax_usb_add_to_isoc_sb_list(struct urb *urb, int epid)
-{
-
-	int i = 0;
-
-	etrax_urb_priv_t *urb_priv;
-	USB_SB_Desc_t *prev_sb_desc,  *next_sb_desc, *temp_sb_desc;
-
-	DBFENTER;
-
-	prev_sb_desc = next_sb_desc = temp_sb_desc = NULL;
-
-	urb_priv = kzalloc(sizeof(etrax_urb_priv_t), GFP_ATOMIC);
-	assert(urb_priv != NULL);
-
-	urb->hcpriv = urb_priv;
-	urb_priv->epid = epid;
-
-	if (usb_pipeout(urb->pipe)) {
-
-		if (urb->number_of_packets == 0) panic("etrax_usb_add_to_isoc_sb_list 0 packets\n");
-
-		dbg_isoc("Transfer for epid %d is OUT", epid);
-		dbg_isoc("%d packets in URB", urb->number_of_packets);
-
-		/* Create one SB descriptor for each packet and link them together. */
-		for (i = 0; i < urb->number_of_packets; i++) {
-			if (!urb->iso_frame_desc[i].length)
-				continue;
-
-			next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_ATOMIC);
-			assert(next_sb_desc != NULL);
-
-			if (urb->iso_frame_desc[i].length > 0) {
-
-				next_sb_desc->command = (IO_STATE(USB_SB_command, tt, out) |
-							 IO_STATE(USB_SB_command, eot, yes));
-
-				next_sb_desc->sw_len = urb->iso_frame_desc[i].length;
-				next_sb_desc->buf = virt_to_phys((char*)urb->transfer_buffer + urb->iso_frame_desc[i].offset);
-
-				/* Check if full length transfer. */
-				if (urb->iso_frame_desc[i].length ==
-				    usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) {
-					next_sb_desc->command |= IO_STATE(USB_SB_command, full, yes);
-				}
-			} else {
-				dbg_isoc("zero len packet");
-				next_sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) |
-							 IO_STATE(USB_SB_command, tt, zout) |
-							 IO_STATE(USB_SB_command, eot, yes) |
-							 IO_STATE(USB_SB_command, full, yes));
-
-				next_sb_desc->sw_len = 1;
-				next_sb_desc->buf = virt_to_phys(&zout_buffer[0]);
-			}
-
-			/* First SB descriptor that belongs to this urb */
-			if (i == 0)
-				urb_priv->first_sb = next_sb_desc;
-			else
-				prev_sb_desc->next = virt_to_phys(next_sb_desc);
-
-			prev_sb_desc = next_sb_desc;
-		}
-
-		next_sb_desc->command |= (IO_STATE(USB_SB_command, intr, yes) |
-					  IO_STATE(USB_SB_command, eol, yes));
-		next_sb_desc->next = 0;
-		urb_priv->last_sb = next_sb_desc;
-
-	} else if (usb_pipein(urb->pipe)) {
-
-		dbg_isoc("Transfer for epid %d is IN", epid);
-		dbg_isoc("transfer_buffer_length = %d", urb->transfer_buffer_length);
-		dbg_isoc("rem is calculated to %d", urb->iso_frame_desc[urb->number_of_packets - 1].length);
-
-		/* Note that in descriptors for periodic traffic are not consumed. This means that
-		   the USB controller never propagates in the SB list. In other words, if there already
-		   is an SB descriptor in the list for this EP we don't have to do anything. */
-		if (TxIsocEPList[epid].sub == 0) {
-			dbg_isoc("Isoc traffic not already running, allocating SB");
-
-			next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_ATOMIC);
-			assert(next_sb_desc != NULL);
-
-			next_sb_desc->command = (IO_STATE(USB_SB_command, tt, in) |
-						 IO_STATE(USB_SB_command, eot, yes) |
-						 IO_STATE(USB_SB_command, eol, yes));
-
-			next_sb_desc->next = 0;
-			next_sb_desc->sw_len = 1; /* Actual number of packets is not relevant
-						     for periodic in traffic as long as it is more
-						     than zero.  Set to 1 always. */
-			next_sb_desc->buf = 0;
-
-			/* The rem field is don't care for isoc traffic, so we don't set it. */
-
-			/* Only one SB descriptor that belongs to this urb. */
-			urb_priv->first_sb = next_sb_desc;
-			urb_priv->last_sb = next_sb_desc;
-
-		} else {
-
-			dbg_isoc("Isoc traffic already running, just setting first/last_sb");
-
-			/* Each EP for isoc in will have only one SB descriptor, setup when submitting the
-			   already active urb. Note that even though we may have several first_sb/last_sb
-			   pointing at the same SB descriptor, they are freed only once (when the list has
-			   become empty). */
-			urb_priv->first_sb = phys_to_virt(TxIsocEPList[epid].sub);
-			urb_priv->last_sb = phys_to_virt(TxIsocEPList[epid].sub);
-			return;
-		}
-
-	}
-
-	/* Find the spot to insert this urb and add it. */
-	if (TxIsocEPList[epid].sub == 0) {
-		/* First SB descriptor inserted in this list (in or out). */
-		dbg_isoc("Inserting SB desc first in list");
-		TxIsocEPList[epid].hw_len = 0;
-		TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb);
-
-	} else {
-		/* Isochronous traffic is already running, insert new traffic last (only out). */
-		dbg_isoc("Inserting SB desc last in list");
-		temp_sb_desc = phys_to_virt(TxIsocEPList[epid].sub);
-		while ((temp_sb_desc->command & IO_MASK(USB_SB_command, eol)) !=
-		       IO_STATE(USB_SB_command, eol, yes)) {
-			assert(temp_sb_desc->next);
-			temp_sb_desc = phys_to_virt(temp_sb_desc->next);
-		}
-		dbg_isoc("Appending list on desc 0x%p", temp_sb_desc);
-
-		/* Next pointer must be set before eol is removed. */
-		temp_sb_desc->next = virt_to_phys(urb_priv->first_sb);
-		/* Clear the previous end of list flag since there is a new in the
-		   added SB descriptor list. */
-		temp_sb_desc->command &= ~IO_MASK(USB_SB_command, eol);
-
-		if (!(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) {
-			/* 8.8.5 in Designer's Reference says we should check for and correct
-			   any errors in the EP here.  That should not be necessary if epid_attn
-			   is handled correctly, so we assume all is ok. */
-			dbg_isoc("EP disabled");
-			etrax_usb_check_error_isoc_ep(epid);
-
-			/* The SB list was exhausted. */
-			if (virt_to_phys(urb_priv->last_sb) != TxIsocEPList[epid].sub) {
-				/* The new sublist did not get processed before the EP was
-				   disabled.  Setup the EP again. */
-				dbg_isoc("Set EP sub to new list");
-				TxIsocEPList[epid].hw_len = 0;
-				TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb);
-			}
-		}
-	}
-
-	if (urb->transfer_flags & URB_ISO_ASAP) {
-		/* The isoc transfer should be started as soon as possible. The start_frame
-		   field is a return value if URB_ISO_ASAP was set. Comparing R_USB_FM_NUMBER
-		   with a USB Chief trace shows that the first isoc IN token is sent 2 frames
-		   later. I'm not sure how this affects usage of the start_frame field by the
-		   device driver, or how it affects things when USB_ISO_ASAP is not set, so
-		   therefore there's no compensation for the 2 frame "lag" here. */
-		urb->start_frame = (*R_USB_FM_NUMBER & 0x7ff);
-		TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
-		urb_priv->urb_state = STARTED;
-		dbg_isoc("URB_ISO_ASAP set, urb->start_frame set to %d", urb->start_frame);
-	} else {
-		/* Not started yet. */
-		urb_priv->urb_state = NOT_STARTED;
-		dbg_isoc("urb_priv->urb_state set to NOT_STARTED");
-	}
-
-       /* We start the DMA sub channel without checking if it's running or not, because:
-	  1) If it's already running, issuing the start command is a nop.
-	  2) We avoid a test-and-set race condition. */
-	*R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start);
-
-	DBFEXIT;
-}
-
-static void etrax_usb_complete_isoc_urb(struct urb *urb, int status)
-{
-	etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-	int epid = urb_priv->epid;
-	int auto_resubmit = 0;
-
-	DBFENTER;
-	dbg_isoc("complete urb 0x%p, status %d", urb, status);
-
-	if (status)
-		warn("Completing isoc urb with status %d.", status);
-
-	if (usb_pipein(urb->pipe)) {
-		int i;
-
-		/* Make that all isoc packets have status and length set before
-		   completing the urb. */
-		for (i = urb_priv->isoc_packet_counter; i < urb->number_of_packets; i++) {
-			urb->iso_frame_desc[i].actual_length = 0;
-			urb->iso_frame_desc[i].status = -EPROTO;
-		}
-
-		urb_list_del(urb, epid);
-
-		if (!list_empty(&urb_list[epid])) {
-			((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED;
-		} else {
-			unsigned long int flags;
-			if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
-				/* The EP was enabled, disable it and wait. */
-				TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
-
-				/* Ah, the luxury of busy-wait. */
-				while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid]));
-			}
-
-			etrax_remove_from_sb_list(urb);
-			TxIsocEPList[epid].sub = 0;
-			TxIsocEPList[epid].hw_len = 0;
-
-			save_flags(flags);
-			cli();
-			etrax_usb_free_epid(epid);
-			restore_flags(flags);
-		}
-
-		urb->hcpriv = 0;
-		kfree(urb_priv);
-
-		/* Release allocated bandwidth. */
-		usb_release_bandwidth(urb->dev, urb, 0);
-	} else if (usb_pipeout(urb->pipe)) {
-		int freed_descr;
-
-		dbg_isoc("Isoc out urb complete 0x%p", urb);
-
-		/* Update the urb list. */
-		urb_list_del(urb, epid);
-
-		freed_descr = etrax_remove_from_sb_list(urb);
-		dbg_isoc("freed %d descriptors of %d packets", freed_descr, urb->number_of_packets);
-		assert(freed_descr == urb->number_of_packets);
-		urb->hcpriv = 0;
-		kfree(urb_priv);
-
-		/* Release allocated bandwidth. */
-		usb_release_bandwidth(urb->dev, urb, 0);
-	}
-
-	urb->status = status;
-	if (urb->complete) {
-		urb->complete(urb, NULL);
-	}
-
-	if (auto_resubmit) {
-		/* Check that urb was not unlinked by the complete callback. */
-		if (__urb_list_entry(urb, epid)) {
-			/* Move this one down the list. */
-			urb_list_move_last(urb, epid);
-
-			/* Mark the now first urb as started (may already be). */
-			((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED;
-
-			/* Must set this to 0 since this urb is still active after
-			   completion. */
-			urb_priv->isoc_packet_counter = 0;
-		} else {
-			warn("(ISOC) automatic resubmit urb 0x%p removed by complete.", urb);
-		}
-	}
-
-	DBFEXIT;
-}
-
-static void etrax_usb_complete_urb(struct urb *urb, int status)
-{
-	switch (usb_pipetype(urb->pipe)) {
-	case PIPE_BULK:
-		etrax_usb_complete_bulk_urb(urb, status);
-		break;
-	case PIPE_CONTROL:
-		etrax_usb_complete_ctrl_urb(urb, status);
-		break;
-	case PIPE_INTERRUPT:
-		etrax_usb_complete_intr_urb(urb, status);
-		break;
-	case PIPE_ISOCHRONOUS:
-		etrax_usb_complete_isoc_urb(urb, status);
-		break;
-	default:
-		err("Unknown pipetype");
-	}
-}
-
-
-
-static irqreturn_t etrax_usb_hc_interrupt_top_half(int irq, void *vhc)
-{
-	usb_interrupt_registers_t *reg;
-	unsigned long flags;
-	__u32 irq_mask;
-	__u8 status;
-	__u32 epid_attn;
-	__u16 port_status_1;
-	__u16 port_status_2;
-	__u32 fm_number;
-
-	DBFENTER;
-
-	/* Read critical registers into local variables, do kmalloc afterwards. */
-	save_flags(flags);
-	cli();
-
-	irq_mask = *R_USB_IRQ_MASK_READ;
-	/* Reading R_USB_STATUS clears the ctl_status interrupt. Note that R_USB_STATUS
-	   must be read before R_USB_EPID_ATTN since reading the latter clears the
-	   ourun and perror fields of R_USB_STATUS. */
-	status = *R_USB_STATUS;
-
-	/* Reading R_USB_EPID_ATTN clears the iso_eof, bulk_eot and epid_attn interrupts. */
-	epid_attn = *R_USB_EPID_ATTN;
-
-	/* Reading R_USB_RH_PORT_STATUS_1 and R_USB_RH_PORT_STATUS_2 clears the
-	   port_status interrupt. */
-	port_status_1 = *R_USB_RH_PORT_STATUS_1;
-	port_status_2 = *R_USB_RH_PORT_STATUS_2;
-
-	/* Reading R_USB_FM_NUMBER clears the sof interrupt. */
-	/* Note: the lower 11 bits contain the actual frame number, sent with each sof. */
-	fm_number = *R_USB_FM_NUMBER;
-
-	restore_flags(flags);
-
-	reg = (usb_interrupt_registers_t *)kmem_cache_alloc(top_half_reg_cache, GFP_ATOMIC);
-
-	assert(reg != NULL);
-
-	reg->hc = (etrax_hc_t *)vhc;
-
-	/* Now put register values into kmalloc'd area. */
-	reg->r_usb_irq_mask_read = irq_mask;
-	reg->r_usb_status = status;
-	reg->r_usb_epid_attn = epid_attn;
-	reg->r_usb_rh_port_status_1 = port_status_1;
-	reg->r_usb_rh_port_status_2 = port_status_2;
-	reg->r_usb_fm_number = fm_number;
-
-        INIT_WORK(&reg->usb_bh, etrax_usb_hc_interrupt_bottom_half, reg);
-        schedule_work(&reg->usb_bh);
-
-	DBFEXIT;
-
-        return IRQ_HANDLED;
-}
-
-static void etrax_usb_hc_interrupt_bottom_half(void *data)
-{
-	usb_interrupt_registers_t *reg = (usb_interrupt_registers_t *)data;
-	__u32 irq_mask = reg->r_usb_irq_mask_read;
-
-	DBFENTER;
-
-	/* Interrupts are handled in order of priority. */
-	if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, epid_attn)) {
-		etrax_usb_hc_epid_attn_interrupt(reg);
-	}
-	if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, port_status)) {
-		etrax_usb_hc_port_status_interrupt(reg);
-	}
-	if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, ctl_status)) {
-		etrax_usb_hc_ctl_status_interrupt(reg);
-	}
-	if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, iso_eof)) {
-		etrax_usb_hc_isoc_eof_interrupt();
-	}
-	if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, bulk_eot)) {
-		/* Update/restart the bulk start timer since obviously the channel is running. */
-		mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL);
-		/* Update/restart the bulk eot timer since we just received an bulk eot interrupt. */
-		mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
-
-		etrax_usb_hc_bulk_eot_interrupt(0);
-	}
-
-	kmem_cache_free(top_half_reg_cache, reg);
-
-	DBFEXIT;
-}
-
-
-void etrax_usb_hc_isoc_eof_interrupt(void)
-{
-	struct urb *urb;
-	etrax_urb_priv_t *urb_priv;
-	int epid;
-	unsigned long flags;
-
-	DBFENTER;
-
-	/* Do not check the invalid epid (it has a valid sub pointer). */
-	for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
-
-		/* Do not check the invalid epid (it has a valid sub pointer). */
-		if ((epid == DUMMY_EPID) || (epid == INVALID_EPID))
-			continue;
-
-		/* Disable interrupts to block the isoc out descriptor interrupt handler
-		   from being called while the isoc EPID list is being checked.
-		*/
-		save_flags(flags);
-		cli();
-
-		if (TxIsocEPList[epid].sub == 0) {
-			/* Nothing here to see. */
-			restore_flags(flags);
-			continue;
-		}
-
-		/* Get the first urb (if any). */
-		urb = urb_list_first(epid);
-		if (urb == 0) {
-			warn("Ignoring NULL urb");
-			restore_flags(flags);
-			continue;
-		}
-		if (usb_pipein(urb->pipe)) {
-
-			/* Sanity check. */
-			assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS);
-
-			urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-			assert(urb_priv);
-
-			if (urb_priv->urb_state == NOT_STARTED) {
-
-				/* If ASAP is not set and urb->start_frame is the current frame,
-				   start the transfer. */
-				if (!(urb->transfer_flags & URB_ISO_ASAP) &&
-				    (urb->start_frame == (*R_USB_FM_NUMBER & 0x7ff))) {
-
-					dbg_isoc("Enabling isoc IN EP descr for epid %d", epid);
-					TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
-
-					/* This urb is now active. */
-					urb_priv->urb_state = STARTED;
-					continue;
-				}
-			}
-		}
-		restore_flags(flags);
-	}
-
-	DBFEXIT;
-
-}
-
-void etrax_usb_hc_bulk_eot_interrupt(int timer_induced)
-{
- 	int epid;
-
-	/* The technique is to run one urb at a time, wait for the eot interrupt at which
-	   point the EP descriptor has been disabled. */
-
-	DBFENTER;
-	dbg_bulk("bulk eot%s", timer_induced ? ", called by timer" : "");
-
-	for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
-
-		if (!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) &&
-		    (TxBulkEPList[epid].sub != 0)) {
-
-			struct urb *urb;
-			etrax_urb_priv_t *urb_priv;
-			unsigned long flags;
-			__u32 r_usb_ept_data;
-
-			/* Found a disabled EP descriptor which has a non-null sub pointer.
-			   Verify that this ctrl EP descriptor got disabled no errors.
-			   FIXME: Necessary to check error_code? */
-			dbg_bulk("for epid %d?", epid);
-
-			/* Get the first urb. */
-			urb = urb_list_first(epid);
-
-			/* FIXME: Could this happen for valid reasons? Why did it disappear? Because of
-			   wrong unlinking? */
-			if (!urb) {
-				warn("NULL urb for epid %d", epid);
-				continue;
-			}
-
-			assert(urb);
-			urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-			assert(urb_priv);
-
-			/* Sanity checks. */
-			assert(usb_pipetype(urb->pipe) == PIPE_BULK);
-			if (phys_to_virt(TxBulkEPList[epid].sub) != urb_priv->last_sb) {
-				err("bulk endpoint got disabled before reaching last sb");
-			}
-
-			/* For bulk IN traffic, there seems to be a race condition between
-			   between the bulk eot and eop interrupts, or rather an uncertainty regarding
-			   the order in which they happen. Normally we expect the eop interrupt from
-			   DMA channel 9 to happen before the eot interrupt.
-
-			   Therefore, we complete the bulk IN urb in the rx interrupt handler instead. */
-
-			if (usb_pipein(urb->pipe)) {
-				dbg_bulk("in urb, continuing");
-				continue;
-			}
-
-			save_flags(flags);
-			cli();
-			*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
-			nop();
-			r_usb_ept_data = *R_USB_EPT_DATA;
-			restore_flags(flags);
-
-			if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) ==
-			    IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
-				/* This means that the endpoint has no error, is disabled
-				   and had inserted traffic, i.e. transfer successfully completed. */
-				etrax_usb_complete_bulk_urb(urb, 0);
-			} else {
-				/* Shouldn't happen. We expect errors to be caught by epid attention. */
-				err("Found disabled bulk EP desc, error_code != no_error");
-			}
-		}
-	}
-
-	/* Normally, we should find (at least) one disabled EP descriptor with a valid sub pointer.
-	   However, because of the uncertainty in the deliverance of the eop/eot interrupts, we may
-	   not.  Also, we might find two disabled EPs when handling an eot interrupt, and then find
-	   none the next time. */
-
-	DBFEXIT;
-
-}
-
-void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg)
-{
-	/* This function handles the epid attention interrupt.  There are a variety of reasons
-	   for this interrupt to happen (Designer's Reference, p. 8 - 22 for the details):
-
-	   invalid ep_id  - Invalid epid in an EP (EP disabled).
-	   stall	  - Not strictly an error condition (EP disabled).
-	   3rd error      - Three successive transaction errors  (EP disabled).
-	   buffer ourun   - Buffer overrun or underrun (EP disabled).
-	   past eof1      - Intr or isoc transaction proceeds past EOF1.
-	   near eof       - Intr or isoc transaction would not fit inside the frame.
-	   zout transfer  - If zout transfer for a bulk endpoint (EP disabled).
-	   setup transfer - If setup transfer for a non-ctrl endpoint (EP disabled). */
-
-	int epid;
-
-
-	DBFENTER;
-
-	assert(reg != NULL);
-
-	/* Note that we loop through all epids. We still want to catch errors for
-	   the invalid one, even though we might handle them differently. */
-	for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
-
-		if (test_bit(epid, (void *)&reg->r_usb_epid_attn)) {
-
-			struct urb *urb;
-			__u32 r_usb_ept_data;
-			unsigned long flags;
-			int error_code;
-
-			save_flags(flags);
-			cli();
-			*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
-			nop();
-			/* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO
-			   registers, they are located at the same address and are of the same size.
-			   In other words, this read should be ok for isoc also. */
-			r_usb_ept_data = *R_USB_EPT_DATA;
-			restore_flags(flags);
-
-			/* First some sanity checks. */
-			if (epid == INVALID_EPID) {
-				/* FIXME: What if it became disabled? Could seriously hurt interrupt
-				   traffic. (Use do_intr_recover.) */
-				warn("Got epid_attn for INVALID_EPID (%d).", epid);
-				err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data);
-				err("R_USB_STATUS = 0x%x", reg->r_usb_status);
-				continue;
-			} else 	if (epid == DUMMY_EPID) {
-				/* We definitely don't care about these ones. Besides, they are
-				   always disabled, so any possible disabling caused by the
-				   epid attention interrupt is irrelevant. */
-				warn("Got epid_attn for DUMMY_EPID (%d).", epid);
-				continue;
-			}
-
-			/* Get the first urb in the urb list for this epid. We blatantly assume
-			   that only the first urb could have caused the epid attention.
-			   (For bulk and ctrl, only one urb is active at any one time. For intr
-			   and isoc we remove them once they are completed.) */
-			urb = urb_list_first(epid);
-
-			if (urb == NULL) {
-				err("Got epid_attn for epid %i with no urb.", epid);
-				err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data);
-				err("R_USB_STATUS = 0x%x", reg->r_usb_status);
-				continue;
-			}
-
-			switch (usb_pipetype(urb->pipe)) {
-			case PIPE_BULK:
-				warn("Got epid attn for bulk endpoint, epid %d", epid);
-				break;
-			case PIPE_CONTROL:
-				warn("Got epid attn for control endpoint, epid %d", epid);
-				break;
-			case PIPE_INTERRUPT:
-				warn("Got epid attn for interrupt endpoint, epid %d", epid);
-				break;
-			case PIPE_ISOCHRONOUS:
-				warn("Got epid attn for isochronous endpoint, epid %d", epid);
-				break;
-			}
-
-			if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) {
-				if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) {
-					warn("Hold was set for epid %d.", epid);
-					continue;
-				}
-			}
-
-			/* Even though error_code occupies bits 22 - 23 in both R_USB_EPT_DATA and
-			   R_USB_EPT_DATA_ISOC, we separate them here so we don't forget in other places. */
-			if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-				error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data);
-			} else {
-				error_code = IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data);
-			}
-
-			/* Using IO_STATE_VALUE on R_USB_EPT_DATA should be ok for isoc also. */
-			if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
-
-				/* Isoc traffic doesn't have error_count_in/error_count_out. */
-				if ((usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) &&
-				    (IO_EXTRACT(R_USB_EPT_DATA, error_count_in, r_usb_ept_data) == 3 ||
-				     IO_EXTRACT(R_USB_EPT_DATA, error_count_out, r_usb_ept_data) == 3)) {
-					/* 3rd error. */
-					warn("3rd error for epid %i", epid);
-					etrax_usb_complete_urb(urb, -EPROTO);
-
-				} else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
-
-					warn("Perror for epid %d", epid);
-
-					if (!(r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, valid))) {
-						/* invalid ep_id */
-						panic("Perror because of invalid epid."
-						      " Deconfigured too early?");
-					} else {
-						/* past eof1, near eof, zout transfer, setup transfer */
-
-						/* Dump the urb and the relevant EP descriptor list. */
-
-						__dump_urb(urb);
-						__dump_ept_data(epid);
-						__dump_ep_list(usb_pipetype(urb->pipe));
-
-						panic("Something wrong with DMA descriptor contents."
-						      " Too much traffic inserted?");
-					}
-				} else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
-					/* buffer ourun */
-					panic("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
-				}
-
-			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, stall)) {
-				/* Not really a protocol error, just says that the endpoint gave
-				   a stall response. Note that error_code cannot be stall for isoc. */
-				if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-					panic("Isoc traffic cannot stall");
-				}
-
-				warn("Stall for epid %d", epid);
-				etrax_usb_complete_urb(urb, -EPIPE);
-
-			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, bus_error)) {
-				/* Two devices responded to a transaction request. Must be resolved
-				   by software. FIXME: Reset ports? */
-				panic("Bus error for epid %d."
-				      " Two devices responded to transaction request",
-				      epid);
-
-			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
-				/* DMA overrun or underrun. */
-				warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
-
-				/* It seems that error_code = buffer_error in
-				   R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS
-				   are the same error. */
-				etrax_usb_complete_urb(urb, -EPROTO);
-			}
-		}
-	}
-
-	DBFEXIT;
-
-}
-
-void etrax_usb_bulk_start_timer_func(unsigned long dummy)
-{
-
-	/* We might enable an EP descriptor behind the current DMA position when it's about
-	   to decide that there are no more bulk traffic and it should stop the bulk channel.
-	   Therefore we periodically check if the bulk channel is stopped and there is an
-	   enabled bulk EP descriptor, in which case we start the bulk channel. */
-	dbg_bulk("bulk_start_timer timed out.");
-
-	if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) {
-		int epid;
-
-		dbg_bulk("Bulk DMA channel not running.");
-
-		for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
-			if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
-				dbg_bulk("Found enabled EP for epid %d, starting bulk channel.\n",
-					 epid);
-				*R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
-
-				/* Restart the bulk eot timer since we just started the bulk channel. */
-				mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
-
-				/* No need to search any further. */
-				break;
-			}
-		}
-	} else {
-		dbg_bulk("Bulk DMA channel running.");
-	}
-}
-
-void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg)
-{
-	etrax_hc_t *hc = reg->hc;
-	__u16 r_usb_rh_port_status_1 = reg->r_usb_rh_port_status_1;
-	__u16 r_usb_rh_port_status_2 = reg->r_usb_rh_port_status_2;
-
-	DBFENTER;
-
-	/* The Etrax RH does not include a wPortChange register, so this has to be handled in software
-	   (by saving the old port status value for comparison when the port status interrupt happens).
-	   See section 11.16.2.6.2 in the USB 1.1 spec for details. */
-
-	dbg_rh("hc->rh.prev_wPortStatus_1 = 0x%x", hc->rh.prev_wPortStatus_1);
-	dbg_rh("hc->rh.prev_wPortStatus_2 = 0x%x", hc->rh.prev_wPortStatus_2);
-	dbg_rh("r_usb_rh_port_status_1 = 0x%x", r_usb_rh_port_status_1);
-	dbg_rh("r_usb_rh_port_status_2 = 0x%x", r_usb_rh_port_status_2);
-
-	/* C_PORT_CONNECTION is set on any transition. */
-	hc->rh.wPortChange_1 |=
-		((r_usb_rh_port_status_1 & (1 << RH_PORT_CONNECTION)) !=
-		 (hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_CONNECTION))) ?
-		(1 << RH_PORT_CONNECTION) : 0;
-
-	hc->rh.wPortChange_2 |=
-		((r_usb_rh_port_status_2 & (1 << RH_PORT_CONNECTION)) !=
-		 (hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_CONNECTION))) ?
-		(1 << RH_PORT_CONNECTION) : 0;
-
-	/* C_PORT_ENABLE is _only_ set on a one to zero transition, i.e. when
-	   the port is disabled, not when it's enabled. */
-	hc->rh.wPortChange_1 |=
-		((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_ENABLE))
-		 && !(r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ?
-		(1 << RH_PORT_ENABLE) : 0;
-
-	hc->rh.wPortChange_2 |=
-		((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_ENABLE))
-		 && !(r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ?
-		(1 << RH_PORT_ENABLE) : 0;
-
-	/* C_PORT_SUSPEND is set to one when the device has transitioned out
-	   of the suspended state, i.e. when suspend goes from one to zero. */
-	hc->rh.wPortChange_1 |=
-		((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_SUSPEND))
-		 && !(r_usb_rh_port_status_1 & (1 << RH_PORT_SUSPEND))) ?
-		(1 << RH_PORT_SUSPEND) : 0;
-
-	hc->rh.wPortChange_2 |=
-		((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_SUSPEND))
-		 && !(r_usb_rh_port_status_2 & (1 << RH_PORT_SUSPEND))) ?
-		(1 << RH_PORT_SUSPEND) : 0;
-
-
-	/* C_PORT_RESET is set when reset processing on this port is complete. */
-	hc->rh.wPortChange_1 |=
-		((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_RESET))
-		 && !(r_usb_rh_port_status_1 & (1 << RH_PORT_RESET))) ?
-		(1 << RH_PORT_RESET) : 0;
-
-	hc->rh.wPortChange_2 |=
-		((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_RESET))
-		 && !(r_usb_rh_port_status_2 & (1 << RH_PORT_RESET))) ?
-		(1 << RH_PORT_RESET) : 0;
-
-	/* Save the new values for next port status change. */
-	hc->rh.prev_wPortStatus_1 = r_usb_rh_port_status_1;
-	hc->rh.prev_wPortStatus_2 = r_usb_rh_port_status_2;
-
-	dbg_rh("hc->rh.wPortChange_1 set to 0x%x", hc->rh.wPortChange_1);
-	dbg_rh("hc->rh.wPortChange_2 set to 0x%x", hc->rh.wPortChange_2);
-
-	DBFEXIT;
-
-}
-
-void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg)
-{
-	DBFENTER;
-
-	/* FIXME: What should we do if we get ourun or perror? Dump the EP and SB
-	   list for the corresponding epid? */
-	if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
-		panic("USB controller got ourun.");
-	}
-	if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
-
-		/* Before, etrax_usb_do_intr_recover was called on this epid if it was
-		   an interrupt pipe. I don't see how re-enabling all EP descriptors
-		   will help if there was a programming error. */
-		panic("USB controller got perror.");
-	}
-
-	if (reg->r_usb_status & IO_MASK(R_USB_STATUS, device_mode)) {
-		/* We should never operate in device mode. */
-		panic("USB controller in device mode.");
-	}
-
-	/* These if-statements could probably be nested. */
-	if (reg->r_usb_status & IO_MASK(R_USB_STATUS, host_mode)) {
-		info("USB controller in host mode.");
-	}
-	if (reg->r_usb_status & IO_MASK(R_USB_STATUS, started)) {
-		info("USB controller started.");
-	}
-	if (reg->r_usb_status & IO_MASK(R_USB_STATUS, running)) {
-		info("USB controller running.");
-	}
-
-	DBFEXIT;
-
-}
-
-
-static int etrax_rh_submit_urb(struct urb *urb)
-{
-	struct usb_device *usb_dev = urb->dev;
-	etrax_hc_t *hc = usb_dev->bus->hcpriv;
-	unsigned int pipe = urb->pipe;
-	struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet;
-	void *data = urb->transfer_buffer;
-	int leni = urb->transfer_buffer_length;
-	int len = 0;
-	int stat = 0;
-
-	__u16 bmRType_bReq;
-	__u16 wValue;
-	__u16 wIndex;
-	__u16 wLength;
-
-	DBFENTER;
-
-	/* FIXME: What is this interrupt urb that is sent to the root hub? */
-	if (usb_pipetype (pipe) == PIPE_INTERRUPT) {
-		dbg_rh("Root-Hub submit IRQ: every %d ms", urb->interval);
-		hc->rh.urb = urb;
-		hc->rh.send = 1;
-		/* FIXME: We could probably remove this line since it's done
-		   in etrax_rh_init_int_timer. (Don't remove it from
-		   etrax_rh_init_int_timer though.) */
-		hc->rh.interval = urb->interval;
-		etrax_rh_init_int_timer(urb);
-		DBFEXIT;
-
-		return 0;
-	}
-
-	bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8);
-	wValue = le16_to_cpu(cmd->wValue);
-	wIndex = le16_to_cpu(cmd->wIndex);
-	wLength = le16_to_cpu(cmd->wLength);
-
-	dbg_rh("bmRType_bReq : 0x%04x (%d)", bmRType_bReq, bmRType_bReq);
-	dbg_rh("wValue       : 0x%04x (%d)", wValue, wValue);
-	dbg_rh("wIndex       : 0x%04x (%d)", wIndex, wIndex);
-	dbg_rh("wLength      : 0x%04x (%d)", wLength, wLength);
-
-	switch (bmRType_bReq) {
-
-		/* Request Destination:
-		   without flags: Device,
-		   RH_INTERFACE: interface,
-		   RH_ENDPOINT: endpoint,
-		   RH_CLASS means HUB here,
-		   RH_OTHER | RH_CLASS  almost ever means HUB_PORT here
-		 */
-
-	case RH_GET_STATUS:
-		*(__u16 *) data = cpu_to_le16 (1);
-		OK (2);
-
-	case RH_GET_STATUS | RH_INTERFACE:
-		*(__u16 *) data = cpu_to_le16 (0);
-		OK (2);
-
-	case RH_GET_STATUS | RH_ENDPOINT:
-		*(__u16 *) data = cpu_to_le16 (0);
-		OK (2);
-
-	case RH_GET_STATUS | RH_CLASS:
-		*(__u32 *) data = cpu_to_le32 (0);
-		OK (4);		/* hub power ** */
-
-	case RH_GET_STATUS | RH_OTHER | RH_CLASS:
-		if (wIndex == 1) {
-			*((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_1);
-			*((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_1);
-		} else if (wIndex == 2) {
-			*((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_2);
-			*((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_2);
-		} else {
-			dbg_rh("RH_GET_STATUS whith invalid wIndex!");
-			OK(0);
-		}
-
-		OK(4);
-
-	case RH_CLEAR_FEATURE | RH_ENDPOINT:
-		switch (wValue) {
-		case (RH_ENDPOINT_STALL):
-			OK (0);
-		}
-		break;
-
-	case RH_CLEAR_FEATURE | RH_CLASS:
-		switch (wValue) {
-		case (RH_C_HUB_OVER_CURRENT):
-			OK (0);	/* hub power over current ** */
-		}
-		break;
-
-	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
-		switch (wValue) {
-		case (RH_PORT_ENABLE):
-			if (wIndex == 1) {
-
-				dbg_rh("trying to do disable port 1");
-
-				*R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, yes);
-
-				while (hc->rh.prev_wPortStatus_1 &
-				       IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes));
-				*R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no);
-				dbg_rh("Port 1 is disabled");
-
-			} else if (wIndex == 2) {
-
-				dbg_rh("trying to do disable port 2");
-
-				*R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, yes);
-
-				while (hc->rh.prev_wPortStatus_2 &
-				       IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes));
-				*R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no);
-				dbg_rh("Port 2 is disabled");
-
-			} else {
-				dbg_rh("RH_CLEAR_FEATURE->RH_PORT_ENABLE "
-				       "with invalid wIndex == %d!", wIndex);
-			}
-
-			OK (0);
-		case (RH_PORT_SUSPEND):
-			/* Opposite to suspend should be resume, so we'll do a resume. */
-			/* FIXME: USB 1.1, 11.16.2.2 says:
-			   "Clearing the PORT_SUSPEND feature causes a host-initiated resume
-			   on the specified port. If the port is not in the Suspended state,
-			   the hub should treat this request as a functional no-operation."
-			   Shouldn't we check if the port is in a suspended state before
-			   resuming? */
-
-			/* Make sure the controller isn't busy. */
-			while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
-			if (wIndex == 1) {
-				*R_USB_COMMAND =
-					IO_STATE(R_USB_COMMAND, port_sel, port1) |
-					IO_STATE(R_USB_COMMAND, port_cmd, resume) |
-					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
-			} else if (wIndex == 2) {
-				*R_USB_COMMAND =
-					IO_STATE(R_USB_COMMAND, port_sel, port2) |
-					IO_STATE(R_USB_COMMAND, port_cmd, resume) |
-					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
-			} else {
-				dbg_rh("RH_CLEAR_FEATURE->RH_PORT_SUSPEND "
-				       "with invalid wIndex == %d!", wIndex);
-			}
-
-			OK (0);
-		case (RH_PORT_POWER):
-			OK (0);	/* port power ** */
-		case (RH_C_PORT_CONNECTION):
-			if (wIndex == 1) {
-				hc->rh.wPortChange_1 &= ~(1 << RH_PORT_CONNECTION);
-			} else if (wIndex == 2) {
-				hc->rh.wPortChange_2 &= ~(1 << RH_PORT_CONNECTION);
-			} else {
-				dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_CONNECTION "
-				       "with invalid wIndex == %d!", wIndex);
-			}
-
-			OK (0);
-		case (RH_C_PORT_ENABLE):
-			if (wIndex == 1) {
-				hc->rh.wPortChange_1 &= ~(1 << RH_PORT_ENABLE);
-			} else if (wIndex == 2) {
-				hc->rh.wPortChange_2 &= ~(1 << RH_PORT_ENABLE);
-			} else {
-				dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_ENABLE "
-				       "with invalid wIndex == %d!", wIndex);
-			}
-			OK (0);
-		case (RH_C_PORT_SUSPEND):
-/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
-			OK (0);
-		case (RH_C_PORT_OVER_CURRENT):
-			OK (0);	/* port power over current ** */
-		case (RH_C_PORT_RESET):
-			if (wIndex == 1) {
-				hc->rh.wPortChange_1 &= ~(1 << RH_PORT_RESET);
-			} else if (wIndex == 2) {
-				hc->rh.wPortChange_2 &= ~(1 << RH_PORT_RESET);
-			} else {
-				dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_RESET "
-				       "with invalid index == %d!", wIndex);
-			}
-
-			OK (0);
-
-		}
-		break;
-
-	case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
-		switch (wValue) {
-		case (RH_PORT_SUSPEND):
-
-			/* Make sure the controller isn't busy. */
-			while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
-			if (wIndex == 1) {
-				*R_USB_COMMAND =
-					IO_STATE(R_USB_COMMAND, port_sel, port1) |
-					IO_STATE(R_USB_COMMAND, port_cmd, suspend) |
-					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
-			} else if (wIndex == 2) {
-				*R_USB_COMMAND =
-					IO_STATE(R_USB_COMMAND, port_sel, port2) |
-					IO_STATE(R_USB_COMMAND, port_cmd, suspend) |
-					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
-			} else {
-				dbg_rh("RH_SET_FEATURE->RH_PORT_SUSPEND "
-				       "with invalid wIndex == %d!", wIndex);
-			}
-
-			OK (0);
-		case (RH_PORT_RESET):
-			if (wIndex == 1) {
-
-			port_1_reset:
-				dbg_rh("Doing reset of port 1");
-
-				/* Make sure the controller isn't busy. */
-				while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
-				*R_USB_COMMAND =
-					IO_STATE(R_USB_COMMAND, port_sel, port1) |
-					IO_STATE(R_USB_COMMAND, port_cmd, reset) |
-					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
-
-				/* We must wait at least 10 ms for the device to recover.
-				   15 ms should be enough. */
-				udelay(15000);
-
-				/* Wait for reset bit to go low (should be done by now). */
-				while (hc->rh.prev_wPortStatus_1 &
-				       IO_STATE(R_USB_RH_PORT_STATUS_1, reset, yes));
-
-				/* If the port status is
-				   1) connected and enabled then there is a device and everything is fine
-				   2) neither connected nor enabled then there is no device, also fine
-				   3) connected and not enabled then we try again
-				   (Yes, there are other port status combinations besides these.) */
-
-				if ((hc->rh.prev_wPortStatus_1 &
-				     IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) &&
-				    (hc->rh.prev_wPortStatus_1 &
-				     IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) {
-					dbg_rh("Connected device on port 1, but port not enabled?"
-					       " Trying reset again.");
-					goto port_2_reset;
-				}
-
-				/* Diagnostic printouts. */
-				if ((hc->rh.prev_wPortStatus_1 &
-				     IO_STATE(R_USB_RH_PORT_STATUS_1, connected, no)) &&
-				    (hc->rh.prev_wPortStatus_1 &
-				     IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) {
-					dbg_rh("No connected device on port 1");
-				} else if ((hc->rh.prev_wPortStatus_1 &
-					    IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) &&
-					   (hc->rh.prev_wPortStatus_1 &
-					    IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes))) {
-					dbg_rh("Connected device on port 1, port 1 enabled");
-				}
-
-			} else if (wIndex == 2) {
-
-			port_2_reset:
-				dbg_rh("Doing reset of port 2");
-
-				/* Make sure the controller isn't busy. */
-				while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
-				/* Issue the reset command. */
-				*R_USB_COMMAND =
-					IO_STATE(R_USB_COMMAND, port_sel, port2) |
-					IO_STATE(R_USB_COMMAND, port_cmd, reset) |
-					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
-
-				/* We must wait at least 10 ms for the device to recover.
-				   15 ms should be enough. */
-				udelay(15000);
-
-				/* Wait for reset bit to go low (should be done by now). */
-				while (hc->rh.prev_wPortStatus_2 &
-				       IO_STATE(R_USB_RH_PORT_STATUS_2, reset, yes));
-
-				/* If the port status is
-				   1) connected and enabled then there is a device and everything is fine
-				   2) neither connected nor enabled then there is no device, also fine
-				   3) connected and not enabled then we try again
-				   (Yes, there are other port status combinations besides these.) */
-
-				if ((hc->rh.prev_wPortStatus_2 &
-				     IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) &&
-				    (hc->rh.prev_wPortStatus_2 &
-				     IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) {
-					dbg_rh("Connected device on port 2, but port not enabled?"
-					       " Trying reset again.");
-					goto port_2_reset;
-				}
-
-				/* Diagnostic printouts. */
-				if ((hc->rh.prev_wPortStatus_2 &
-				     IO_STATE(R_USB_RH_PORT_STATUS_2, connected, no)) &&
-				    (hc->rh.prev_wPortStatus_2 &
-				     IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) {
-					dbg_rh("No connected device on port 2");
-				} else if ((hc->rh.prev_wPortStatus_2 &
-					    IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) &&
-					   (hc->rh.prev_wPortStatus_2 &
-					    IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes))) {
-					dbg_rh("Connected device on port 2, port 2 enabled");
-				}
-
-			} else {
-				dbg_rh("RH_SET_FEATURE->RH_PORT_RESET with invalid wIndex = %d", wIndex);
-			}
-
-			/* Make sure the controller isn't busy. */
-			while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
-			/* If all enabled ports were disabled the host controller goes down into
-			   started mode, so we need to bring it back into the running state.
-			   (This is safe even if it's already in the running state.) */
-			*R_USB_COMMAND =
-				IO_STATE(R_USB_COMMAND, port_sel, nop) |
-				IO_STATE(R_USB_COMMAND, port_cmd, reset) |
-				IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
-
-			dbg_rh("...Done");
-			OK(0);
-
-		case (RH_PORT_POWER):
-			OK (0);	/* port power ** */
-		case (RH_PORT_ENABLE):
-			/* There is no port enable command in the host controller, so if the
-			   port is already enabled, we do nothing. If not, we reset the port
-			   (with an ugly goto). */
-
-			if (wIndex == 1) {
-				if (hc->rh.prev_wPortStatus_1 &
-				    IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no)) {
-					goto port_1_reset;
-				}
-			} else if (wIndex == 2) {
-				if (hc->rh.prev_wPortStatus_2 &
-				    IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no)) {
-					goto port_2_reset;
-				}
-			} else {
-				dbg_rh("RH_SET_FEATURE->RH_GET_STATUS with invalid wIndex = %d", wIndex);
-			}
-			OK (0);
-		}
-		break;
-
-	case RH_SET_ADDRESS:
-		hc->rh.devnum = wValue;
-		dbg_rh("RH address set to: %d", hc->rh.devnum);
-		OK (0);
-
-	case RH_GET_DESCRIPTOR:
-		switch ((wValue & 0xff00) >> 8) {
-		case (0x01):	/* device descriptor */
-			len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_dev_des), wLength));
-			memcpy (data, root_hub_dev_des, len);
-			OK (len);
-		case (0x02):	/* configuration descriptor */
-			len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_config_des), wLength));
-			memcpy (data, root_hub_config_des, len);
-			OK (len);
-		case (0x03):	/* string descriptors */
-			len = usb_root_hub_string (wValue & 0xff,
-						   0xff, "ETRAX 100LX",
-						   data, wLength);
-			if (len > 0) {
-				OK(min(leni, len));
-			} else {
-				stat = -EPIPE;
-			}
-
-		}
-		break;
-
-	case RH_GET_DESCRIPTOR | RH_CLASS:
-		root_hub_hub_des[2] = hc->rh.numports;
-		len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_hub_des), wLength));
-		memcpy (data, root_hub_hub_des, len);
-		OK (len);
-
-	case RH_GET_CONFIGURATION:
-		*(__u8 *) data = 0x01;
-		OK (1);
-
-	case RH_SET_CONFIGURATION:
-		OK (0);
-
-	default:
-		stat = -EPIPE;
-	}
-
-	urb->actual_length = len;
-	urb->status = stat;
-	urb->dev = NULL;
-	if (urb->complete) {
-		urb->complete(urb, NULL);
-	}
-	DBFEXIT;
-
-	return 0;
-}
-
-static void
-etrax_usb_bulk_eot_timer_func(unsigned long dummy)
-{
-	/* Because of a race condition in the top half, we might miss a bulk eot.
-	   This timer "simulates" a bulk eot if we don't get one for a while, hopefully
-	   correcting the situation. */
-	dbg_bulk("bulk_eot_timer timed out.");
-	etrax_usb_hc_bulk_eot_interrupt(1);
-}
-
-static void*
-etrax_usb_buffer_alloc(struct usb_bus* bus, size_t size,
-	unsigned mem_flags, dma_addr_t *dma)
-{
-  return kmalloc(size, mem_flags);
-}
-
-static void
-etrax_usb_buffer_free(struct usb_bus *bus, size_t size, void *addr, dma_addr_t dma)
-{
-  kfree(addr);
-}
-
-
-static struct device fake_device;
-
-static int __init etrax_usb_hc_init(void)
-{
-	static etrax_hc_t *hc;
-	struct usb_bus *bus;
-	struct usb_device *usb_rh;
-	int i;
-
-	DBFENTER;
-
-	info("ETRAX 100LX USB-HCD %s (c) 2001-2003 Axis Communications AB\n", usb_hcd_version);
-
- 	hc = kmalloc(sizeof(etrax_hc_t), GFP_KERNEL);
-	assert(hc != NULL);
-
-	/* We use kmem_cache_* to make sure that all DMA desc. are dword aligned */
-	/* Note that we specify sizeof(USB_EP_Desc_t) as the size, but also allocate
-	   SB descriptors from this cache. This is ok since sizeof(USB_EP_Desc_t) ==
-	   sizeof(USB_SB_Desc_t). */
-
-	usb_desc_cache = kmem_cache_create("usb_desc_cache", sizeof(USB_EP_Desc_t), 0,
-					   SLAB_HWCACHE_ALIGN, 0, 0);
-	assert(usb_desc_cache != NULL);
-
-	top_half_reg_cache = kmem_cache_create("top_half_reg_cache",
-					       sizeof(usb_interrupt_registers_t),
-					       0, SLAB_HWCACHE_ALIGN, 0, 0);
-	assert(top_half_reg_cache != NULL);
-
-	isoc_compl_cache = kmem_cache_create("isoc_compl_cache",
-						sizeof(usb_isoc_complete_data_t),
-						0, SLAB_HWCACHE_ALIGN, 0, 0);
-	assert(isoc_compl_cache != NULL);
-
-	etrax_usb_bus = bus = usb_alloc_bus(&etrax_usb_device_operations);
-	hc->bus = bus;
-	bus->bus_name="ETRAX 100LX";
-	bus->hcpriv = hc;
-
-	/* Initialize RH to the default address.
-	   And make sure that we have no status change indication */
-	hc->rh.numports = 2;  /* The RH has two ports */
-	hc->rh.devnum = 1;
-	hc->rh.wPortChange_1 = 0;
-	hc->rh.wPortChange_2 = 0;
-
-	/* Also initate the previous values to zero */
-	hc->rh.prev_wPortStatus_1 = 0;
-	hc->rh.prev_wPortStatus_2 = 0;
-
-	/* Initialize the intr-traffic flags */
-	/* FIXME: This isn't used. (Besides, the error field isn't initialized.) */
-	hc->intr.sleeping = 0;
-	hc->intr.wq = NULL;
-
-	epid_usage_bitmask = 0;
-	epid_out_traffic = 0;
-
-	/* Mark the invalid epid as being used. */
-	set_bit(INVALID_EPID, (void *)&epid_usage_bitmask);
-	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, INVALID_EPID);
-	nop();
-	/* The valid bit should still be set ('invalid' is in our world; not the hardware's). */
-	*R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, yes) |
-			   IO_FIELD(R_USB_EPT_DATA, max_len, 1));
-
-	/* Mark the dummy epid as being used. */
-	set_bit(DUMMY_EPID, (void *)&epid_usage_bitmask);
-	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, DUMMY_EPID);
-	nop();
-	*R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, no) |
-			   IO_FIELD(R_USB_EPT_DATA, max_len, 1));
-
-	/* Initialize the urb list by initiating a head for each list. */
-	for (i = 0; i < NBR_OF_EPIDS; i++) {
-		INIT_LIST_HEAD(&urb_list[i]);
-	}
-	spin_lock_init(&urb_list_lock);
-
-	INIT_LIST_HEAD(&urb_unlink_list);
-
-
-	/* Initiate the bulk start timer. */
-	init_timer(&bulk_start_timer);
-	bulk_start_timer.expires = jiffies + BULK_START_TIMER_INTERVAL;
-	bulk_start_timer.function = etrax_usb_bulk_start_timer_func;
-	add_timer(&bulk_start_timer);
-
-
-	/* Initiate the bulk eot timer. */
-	init_timer(&bulk_eot_timer);
-	bulk_eot_timer.expires = jiffies + BULK_EOT_TIMER_INTERVAL;
-	bulk_eot_timer.function = etrax_usb_bulk_eot_timer_func;
-	add_timer(&bulk_eot_timer);
-
-	/* Set up the data structures for USB traffic. Note that this must be done before
-	   any interrupt that relies on sane DMA list occurrs. */
-	init_rx_buffers();
-	init_tx_bulk_ep();
-	init_tx_ctrl_ep();
-	init_tx_intr_ep();
-	init_tx_isoc_ep();
-
-        device_initialize(&fake_device);
-        kobject_set_name(&fake_device.kobj, "etrax_usb");
-        kobject_add(&fake_device.kobj);
-	kobject_uevent(&fake_device.kobj, KOBJ_ADD);
-        hc->bus->controller = &fake_device;
-	usb_register_bus(hc->bus);
-
-	*R_IRQ_MASK2_SET =
-		/* Note that these interrupts are not used. */
-		IO_STATE(R_IRQ_MASK2_SET, dma8_sub0_descr, set) |
-		/* Sub channel 1 (ctrl) descr. interrupts are used. */
-		IO_STATE(R_IRQ_MASK2_SET, dma8_sub1_descr, set) |
-		IO_STATE(R_IRQ_MASK2_SET, dma8_sub2_descr, set) |
-		/* Sub channel 3 (isoc) descr. interrupts are used. */
-		IO_STATE(R_IRQ_MASK2_SET, dma8_sub3_descr, set);
-
-	/* Note that the dma9_descr interrupt is not used. */
-	*R_IRQ_MASK2_SET =
-		IO_STATE(R_IRQ_MASK2_SET, dma9_eop, set) |
-		IO_STATE(R_IRQ_MASK2_SET, dma9_descr, set);
-
-	/* FIXME: Enable iso_eof only when isoc traffic is running. */
-	*R_USB_IRQ_MASK_SET =
-		IO_STATE(R_USB_IRQ_MASK_SET, iso_eof, set) |
-		IO_STATE(R_USB_IRQ_MASK_SET, bulk_eot, set) |
-		IO_STATE(R_USB_IRQ_MASK_SET, epid_attn, set) |
-		IO_STATE(R_USB_IRQ_MASK_SET, port_status, set) |
-		IO_STATE(R_USB_IRQ_MASK_SET, ctl_status, set);
-
-
-	if (request_irq(ETRAX_USB_HC_IRQ, etrax_usb_hc_interrupt_top_half, 0,
-			"ETRAX 100LX built-in USB (HC)", hc)) {
-		err("Could not allocate IRQ %d for USB", ETRAX_USB_HC_IRQ);
-		etrax_usb_hc_cleanup();
-		DBFEXIT;
-		return -1;
-	}
-
-	if (request_irq(ETRAX_USB_RX_IRQ, etrax_usb_rx_interrupt, 0,
-			"ETRAX 100LX built-in USB (Rx)", hc)) {
-		err("Could not allocate IRQ %d for USB", ETRAX_USB_RX_IRQ);
-		etrax_usb_hc_cleanup();
-		DBFEXIT;
-		return -1;
-	}
-
-	if (request_irq(ETRAX_USB_TX_IRQ, etrax_usb_tx_interrupt, 0,
-			"ETRAX 100LX built-in USB (Tx)", hc)) {
-		err("Could not allocate IRQ %d for USB", ETRAX_USB_TX_IRQ);
-		etrax_usb_hc_cleanup();
-		DBFEXIT;
-		return -1;
-	}
-
-	/* R_USB_COMMAND:
-	   USB commands in host mode. The fields in this register should all be
-	   written to in one write. Do not read-modify-write one field at a time. A
-	   write to this register will trigger events in the USB controller and an
-	   incomplete command may lead to unpredictable results, and in worst case
-	   even to a deadlock in the controller.
-	   (Note however that the busy field is read-only, so no need to write to it.) */
-
-	/* Check the busy bit before writing to R_USB_COMMAND. */
-
-	while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
-	/* Reset the USB interface. */
-	*R_USB_COMMAND =
-		IO_STATE(R_USB_COMMAND, port_sel, nop) |
-		IO_STATE(R_USB_COMMAND, port_cmd, reset) |
-		IO_STATE(R_USB_COMMAND, ctrl_cmd, reset);
-
-	/* Designer's Reference, p. 8 - 10 says we should Initate R_USB_FM_PSTART to 0x2A30 (10800),
-	   to guarantee that control traffic gets 10% of the bandwidth, and periodic transfer may
-	   allocate the rest (90%). This doesn't work though. Read on for a lenghty explanation.
-
-	   While there is a difference between rev. 2 and rev. 3 of the ETRAX 100LX regarding the NAK
-	   behaviour, it doesn't solve this problem. What happens is that a control transfer will not
-	   be interrupted in its data stage when PSTART happens (the point at which periodic traffic
-	   is started). Thus, if PSTART is set to 10800 and its IN or OUT token is NAKed until just before
-	   PSTART happens, it will continue the IN/OUT transfer as long as it's ACKed. After it's done,
-	   there may be too little time left for an isochronous transfer, causing an epid attention
-	   interrupt due to perror. The work-around for this is to let the control transfers run at the
-	   end of the frame instead of at the beginning, and will be interrupted just fine if it doesn't
-	   fit into the frame. However, since there will *always* be a control transfer at the beginning
-	   of the frame, regardless of what we set PSTART to, that transfer might be a 64-byte transfer
-	   which consumes up to 15% of the frame, leaving only 85% for periodic traffic. The solution to
-	   this would be to 'dummy allocate' 5% of the frame with the usb_claim_bandwidth function to make
-	   sure that the periodic transfers that are inserted will always fit in the frame.
-
-	   The idea was suggested that a control transfer could be split up into several 8 byte transfers,
-	   so that it would be interrupted by PSTART, but since this can't be done for an IN transfer this
-	   hasn't been implemented.
-
-	   The value 11960 is chosen to be just after the SOF token, with a couple of bit times extra
-	   for possible bit stuffing. */
-
-	*R_USB_FM_PSTART = IO_FIELD(R_USB_FM_PSTART, value, 11960);
-
-#ifdef CONFIG_ETRAX_USB_HOST_PORT1
-	*R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no);
-#endif
-
-#ifdef CONFIG_ETRAX_USB_HOST_PORT2
-	*R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no);
-#endif
-
-	while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
-	/* Configure the USB interface as a host controller. */
-	*R_USB_COMMAND =
-		IO_STATE(R_USB_COMMAND, port_sel, nop) |
-		IO_STATE(R_USB_COMMAND, port_cmd, reset) |
-		IO_STATE(R_USB_COMMAND, ctrl_cmd, host_config);
-
-	/* Note: Do not reset any ports here. Await the port status interrupts, to have a controlled
-	   sequence of resetting the ports. If we reset both ports now, and there are devices
-	   on both ports, we will get a bus error because both devices will answer the set address
-	   request. */
-
-	while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
-	/* Start processing of USB traffic. */
-	*R_USB_COMMAND =
-		IO_STATE(R_USB_COMMAND, port_sel, nop) |
-		IO_STATE(R_USB_COMMAND, port_cmd, reset) |
-		IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
-
-	while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
-	usb_rh = usb_alloc_dev(NULL, hc->bus, 0);
-	hc->bus->root_hub = usb_rh;
-        usb_rh->state = USB_STATE_ADDRESS;
-        usb_rh->speed = USB_SPEED_FULL;
-        usb_rh->devnum = 1;
-        hc->bus->devnum_next = 2;
-        usb_rh->ep0.desc.wMaxPacketSize = __const_cpu_to_le16(64);
-        usb_get_device_descriptor(usb_rh, USB_DT_DEVICE_SIZE);
-	usb_new_device(usb_rh);
-
-	DBFEXIT;
-
-	return 0;
-}
-
-static void etrax_usb_hc_cleanup(void)
-{
-	DBFENTER;
-
-	free_irq(ETRAX_USB_HC_IRQ, NULL);
-	free_irq(ETRAX_USB_RX_IRQ, NULL);
-	free_irq(ETRAX_USB_TX_IRQ, NULL);
-
-	usb_deregister_bus(etrax_usb_bus);
-
-	/* FIXME: call kmem_cache_destroy here? */
-
-	DBFEXIT;
-}
-
-module_init(etrax_usb_hc_init);
-module_exit(etrax_usb_hc_cleanup);
diff --git a/drivers/usb/host/hc_crisv10.h b/drivers/usb/host/hc_crisv10.h
deleted file mode 100644
index 62f7711..0000000
--- a/drivers/usb/host/hc_crisv10.h
+++ /dev/null
@@ -1,289 +0,0 @@
-#ifndef __LINUX_ETRAX_USB_H
-#define __LINUX_ETRAX_USB_H
-
-#include <linux/types.h>
-#include <linux/list.h>
-
-typedef struct USB_IN_Desc {
-	volatile __u16 sw_len;
-	volatile __u16 command;
-	volatile unsigned long next;
-	volatile unsigned long buf;
-	volatile __u16 hw_len;
-	volatile __u16 status;
-} USB_IN_Desc_t;
-
-typedef struct USB_SB_Desc {
-	volatile __u16 sw_len;
-	volatile __u16 command;
-	volatile unsigned long next;
-	volatile unsigned long buf;
-	__u32 dummy;
-} USB_SB_Desc_t;
-
-typedef struct USB_EP_Desc {
-	volatile __u16 hw_len;
-	volatile __u16 command;
-	volatile unsigned long sub;
-	volatile unsigned long next;
-	__u32 dummy;
-} USB_EP_Desc_t;
-
-struct virt_root_hub {
-	int devnum;
-	void *urb;
-	void *int_addr;
-	int send;
-	int interval;
-	int numports;
-	struct timer_list rh_int_timer;
-	volatile __u16 wPortChange_1;
-	volatile __u16 wPortChange_2;
-	volatile __u16 prev_wPortStatus_1;
-	volatile __u16 prev_wPortStatus_2;
-};
-
-struct etrax_usb_intr_traffic {
-	int sleeping;
-	int error;
-	struct wait_queue *wq;
-};
-
-typedef struct etrax_usb_hc {
-	struct usb_bus *bus;
-	struct virt_root_hub rh;
-	struct etrax_usb_intr_traffic intr;
-} etrax_hc_t;
-
-typedef enum {
-	STARTED,
-	NOT_STARTED,
-	UNLINK,
-	TRANSFER_DONE,
-	WAITING_FOR_DESCR_INTR
-} etrax_usb_urb_state_t;
-
-
-
-typedef struct etrax_usb_urb_priv {
-	/* The first_sb field is used for freeing all SB descriptors belonging
-	   to an urb. The corresponding ep descriptor's sub pointer cannot be
-	   used for this since the DMA advances the sub pointer as it processes
-	   the sb list. */
-	USB_SB_Desc_t *first_sb;
-	/* The last_sb field referes to the last SB descriptor that belongs to
-	   this urb. This is important to know so we can free the SB descriptors
-	   that ranges between first_sb and last_sb. */
-	USB_SB_Desc_t *last_sb;
-
-	/* The rx_offset field is used in ctrl and bulk traffic to keep track
-	   of the offset in the urb's transfer_buffer where incoming data should be
-	   copied to. */
-	__u32 rx_offset;
-
-	/* Counter used in isochronous transfers to keep track of the
-	   number of packets received/transmitted.  */
-	__u32 isoc_packet_counter;
-
-	/* This field is used to pass information about the urb's current state between
-	   the various interrupt handlers (thus marked volatile). */
-	volatile etrax_usb_urb_state_t urb_state;
-
-	/* Connection between the submitted urb and ETRAX epid number */
-	__u8 epid;
-
-	/* The rx_data_list field is used for periodic traffic, to hold
-	   received data for later processing in the the complete_urb functions,
-	   where the data us copied to the urb's transfer_buffer. Basically, we
-	   use this intermediate storage because we don't know when it's safe to
-	   reuse the transfer_buffer (FIXME?). */
-	struct list_head rx_data_list;
-} etrax_urb_priv_t;
-
-/* This struct is for passing data from the top half to the bottom half. */
-typedef struct usb_interrupt_registers
-{
-	etrax_hc_t *hc;
-	__u32 r_usb_epid_attn;
-	__u8 r_usb_status;
-	__u16 r_usb_rh_port_status_1;
-	__u16 r_usb_rh_port_status_2;
-	__u32 r_usb_irq_mask_read;
-	__u32 r_usb_fm_number;
-	struct work_struct usb_bh;
-} usb_interrupt_registers_t;
-
-/* This struct is for passing data from the isoc top half to the isoc bottom half. */
-typedef struct usb_isoc_complete_data
-{
-	struct urb *urb;
-	struct work_struct usb_bh;
-} usb_isoc_complete_data_t;
-
-/* This struct holds data we get from the rx descriptors for DMA channel 9
-   for periodic traffic (intr and isoc). */
-typedef struct rx_data
-{
-	void *data;
-	int length;
-	struct list_head list;
-} rx_data_t;
-
-typedef struct urb_entry
-{
-	struct urb *urb;
-	struct list_head list;
-} urb_entry_t;
-
-/* ---------------------------------------------------------------------------
-   Virtual Root HUB
-   ------------------------------------------------------------------------- */
-/* destination of request */
-#define RH_INTERFACE               0x01
-#define RH_ENDPOINT                0x02
-#define RH_OTHER                   0x03
-
-#define RH_CLASS                   0x20
-#define RH_VENDOR                  0x40
-
-/* Requests: bRequest << 8 | bmRequestType */
-#define RH_GET_STATUS           0x0080
-#define RH_CLEAR_FEATURE        0x0100
-#define RH_SET_FEATURE          0x0300
-#define RH_SET_ADDRESS		0x0500
-#define RH_GET_DESCRIPTOR	0x0680
-#define RH_SET_DESCRIPTOR       0x0700
-#define RH_GET_CONFIGURATION	0x0880
-#define RH_SET_CONFIGURATION	0x0900
-#define RH_GET_STATE            0x0280
-#define RH_GET_INTERFACE        0x0A80
-#define RH_SET_INTERFACE        0x0B00
-#define RH_SYNC_FRAME           0x0C80
-/* Our Vendor Specific Request */
-#define RH_SET_EP               0x2000
-
-
-/* Hub port features */
-#define RH_PORT_CONNECTION         0x00
-#define RH_PORT_ENABLE             0x01
-#define RH_PORT_SUSPEND            0x02
-#define RH_PORT_OVER_CURRENT       0x03
-#define RH_PORT_RESET              0x04
-#define RH_PORT_POWER              0x08
-#define RH_PORT_LOW_SPEED          0x09
-#define RH_C_PORT_CONNECTION       0x10
-#define RH_C_PORT_ENABLE           0x11
-#define RH_C_PORT_SUSPEND          0x12
-#define RH_C_PORT_OVER_CURRENT     0x13
-#define RH_C_PORT_RESET            0x14
-
-/* Hub features */
-#define RH_C_HUB_LOCAL_POWER       0x00
-#define RH_C_HUB_OVER_CURRENT      0x01
-
-#define RH_DEVICE_REMOTE_WAKEUP    0x00
-#define RH_ENDPOINT_STALL          0x01
-
-/* Our Vendor Specific feature */
-#define RH_REMOVE_EP               0x00
-
-
-#define RH_ACK                     0x01
-#define RH_REQ_ERR                 -1
-#define RH_NACK                    0x00
-
-/* Field definitions for */
-
-#define USB_IN_command__eol__BITNR      0 /* command macros */
-#define USB_IN_command__eol__WIDTH      1
-#define USB_IN_command__eol__no         0
-#define USB_IN_command__eol__yes        1
-
-#define USB_IN_command__intr__BITNR     3
-#define USB_IN_command__intr__WIDTH     1
-#define USB_IN_command__intr__no        0
-#define USB_IN_command__intr__yes       1
-
-#define USB_IN_status__eop__BITNR       1 /* status macros. */
-#define USB_IN_status__eop__WIDTH       1
-#define USB_IN_status__eop__no          0
-#define USB_IN_status__eop__yes         1
-
-#define USB_IN_status__eot__BITNR       5
-#define USB_IN_status__eot__WIDTH       1
-#define USB_IN_status__eot__no          0
-#define USB_IN_status__eot__yes         1
-
-#define USB_IN_status__error__BITNR     6
-#define USB_IN_status__error__WIDTH     1
-#define USB_IN_status__error__no        0
-#define USB_IN_status__error__yes       1
-
-#define USB_IN_status__nodata__BITNR    7
-#define USB_IN_status__nodata__WIDTH    1
-#define USB_IN_status__nodata__no       0
-#define USB_IN_status__nodata__yes      1
-
-#define USB_IN_status__epid__BITNR      8
-#define USB_IN_status__epid__WIDTH      5
-
-#define USB_EP_command__eol__BITNR      0
-#define USB_EP_command__eol__WIDTH      1
-#define USB_EP_command__eol__no         0
-#define USB_EP_command__eol__yes        1
-
-#define USB_EP_command__eof__BITNR      1
-#define USB_EP_command__eof__WIDTH      1
-#define USB_EP_command__eof__no         0
-#define USB_EP_command__eof__yes        1
-
-#define USB_EP_command__intr__BITNR     3
-#define USB_EP_command__intr__WIDTH     1
-#define USB_EP_command__intr__no        0
-#define USB_EP_command__intr__yes       1
-
-#define USB_EP_command__enable__BITNR   4
-#define USB_EP_command__enable__WIDTH   1
-#define USB_EP_command__enable__no      0
-#define USB_EP_command__enable__yes     1
-
-#define USB_EP_command__hw_valid__BITNR 5
-#define USB_EP_command__hw_valid__WIDTH 1
-#define USB_EP_command__hw_valid__no    0
-#define USB_EP_command__hw_valid__yes   1
-
-#define USB_EP_command__epid__BITNR     8
-#define USB_EP_command__epid__WIDTH     5
-
-#define USB_SB_command__eol__BITNR      0 /* command macros. */
-#define USB_SB_command__eol__WIDTH      1
-#define USB_SB_command__eol__no         0
-#define USB_SB_command__eol__yes        1
-
-#define USB_SB_command__eot__BITNR      1
-#define USB_SB_command__eot__WIDTH      1
-#define USB_SB_command__eot__no         0
-#define USB_SB_command__eot__yes        1
-
-#define USB_SB_command__intr__BITNR     3
-#define USB_SB_command__intr__WIDTH     1
-#define USB_SB_command__intr__no        0
-#define USB_SB_command__intr__yes       1
-
-#define USB_SB_command__tt__BITNR       4
-#define USB_SB_command__tt__WIDTH       2
-#define USB_SB_command__tt__zout        0
-#define USB_SB_command__tt__in          1
-#define USB_SB_command__tt__out         2
-#define USB_SB_command__tt__setup       3
-
-
-#define USB_SB_command__rem__BITNR      8
-#define USB_SB_command__rem__WIDTH      6
-
-#define USB_SB_command__full__BITNR     6
-#define USB_SB_command__full__WIDTH     1
-#define USB_SB_command__full__no        0
-#define USB_SB_command__full__yes       1
-
-#endif
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index f0d29ed..e8bbe8b 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -486,9 +486,6 @@
 	 * or if bus glue did the same (e.g. for PCI add-in cards with
 	 * PCI PM support).
 	 */
-	ohci_dbg (ohci, "resetting from state '%s', control = 0x%x\n",
-			hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
-			ohci_readl (ohci, &ohci->regs->control));
 	if ((ohci->hc_control & OHCI_CTRL_RWC) != 0
 			&& !device_may_wakeup(hcd->self.controller))
 		device_init_wakeup(hcd->self.controller, 1);
@@ -744,9 +741,6 @@
 {
 	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);
 
-	ohci_dbg (ohci, "stop %s controller (state 0x%02x)\n",
-		hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
-		hcd->state);
 	ohci_dump (ohci, 1);
 
 	flush_scheduled_work();
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index b331ac4..7970560 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -20,10 +20,16 @@
 
 /*-------------------------------------------------------------------------*/
 
+static int broken_suspend(struct usb_hcd *hcd)
+{
+	device_init_wakeup(&hcd->self.root_hub->dev, 0);
+	return 0;
+}
+
 /* AMD 756, for most chips (early revs), corrupts register
  * values on read ... so enable the vendor workaround.
  */
-static int __devinit ohci_quirk_amd756(struct usb_hcd *hcd)
+static int ohci_quirk_amd756(struct usb_hcd *hcd)
 {
 	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
 
@@ -31,16 +37,14 @@
 	ohci_dbg (ohci, "AMD756 erratum 4 workaround\n");
 
 	/* also erratum 10 (suspend/resume issues) */
-	device_init_wakeup(&hcd->self.root_hub->dev, 0);
-
-	return 0;
+	return broken_suspend(hcd);
 }
 
 /* Apple's OHCI driver has a lot of bizarre workarounds
  * for this chip.  Evidently control and bulk lists
  * can get confused.  (B&W G3 models, and ...)
  */
-static int __devinit ohci_quirk_opti(struct usb_hcd *hcd)
+static int ohci_quirk_opti(struct usb_hcd *hcd)
 {
 	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
 
@@ -53,7 +57,7 @@
  * identify the USB (fn2). This quirk might apply to more or
  * even all NSC stuff.
  */
-static int __devinit ohci_quirk_ns(struct usb_hcd *hcd)
+static int ohci_quirk_ns(struct usb_hcd *hcd)
 {
 	struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
 	struct pci_dev	*b;
@@ -75,7 +79,7 @@
  * delays before control or bulk queues get re-activated
  * in finish_unlinks()
  */
-static int __devinit ohci_quirk_zfmicro(struct usb_hcd *hcd)
+static int ohci_quirk_zfmicro(struct usb_hcd *hcd)
 {
 	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
 
@@ -88,7 +92,7 @@
 /* Check for Toshiba SCC OHCI which has big endian registers
  * and little endian in memory data structures
  */
-static int __devinit ohci_quirk_toshiba_scc(struct usb_hcd *hcd)
+static int ohci_quirk_toshiba_scc(struct usb_hcd *hcd)
 {
 	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
 
@@ -129,6 +133,18 @@
 		PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, 0x01b6),
 		.driver_data = (unsigned long)ohci_quirk_toshiba_scc,
 	},
+	{
+		/* Toshiba portege 4000 */
+		.vendor		= PCI_VENDOR_ID_AL,
+		.device		= 0x5237,
+		.subvendor	= PCI_VENDOR_ID_TOSHIBA_2,
+		.subdevice	= 0x0004,
+		.driver_data	= (unsigned long) broken_suspend,
+	},
+	{
+		PCI_DEVICE(PCI_VENDOR_ID_ITE, 0x8152),
+		.driver_data = (unsigned long) broken_suspend,
+	},
 	/* FIXME for some of the early AMD 760 southbridges, OHCI
 	 * won't work at all.  blacklist them.
 	 */
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index 19a0cc0..4aed305 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -123,10 +123,14 @@
 
 static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)
 {
-	if (!list_empty(&td->list))
+	if (!list_empty(&td->list)) {
 		dev_warn(uhci_dev(uhci), "td %p still in list!\n", td);
-	if (!list_empty(&td->fl_list))
+		WARN_ON(1);
+	}
+	if (!list_empty(&td->fl_list)) {
 		dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td);
+		WARN_ON(1);
+	}
 
 	dma_pool_free(uhci->td_pool, td, td->dma_handle);
 }
@@ -291,8 +295,10 @@
 static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
 {
 	WARN_ON(qh->state != QH_STATE_IDLE && qh->udev);
-	if (!list_empty(&qh->queue))
+	if (!list_empty(&qh->queue)) {
 		dev_warn(uhci_dev(uhci), "qh %p list not empty!\n", qh);
+		WARN_ON(1);
+	}
 
 	list_del(&qh->node);
 	if (qh->udev) {
@@ -740,9 +746,11 @@
 {
 	struct uhci_td *td, *tmp;
 
-	if (!list_empty(&urbp->node))
+	if (!list_empty(&urbp->node)) {
 		dev_warn(uhci_dev(uhci), "urb %p still on QH's list!\n",
 				urbp->urb);
+		WARN_ON(1);
+	}
 
 	list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
 		uhci_remove_td_from_urbp(td);
diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig
index 69a9f3b..a792e42 100644
--- a/drivers/usb/input/Kconfig
+++ b/drivers/usb/input/Kconfig
@@ -4,151 +4,6 @@
 comment "USB Input Devices"
 	depends on USB
 
-config USB_HID
-	tristate "USB Human Interface Device (full HID) support"
-	default y
-	depends on USB && INPUT
-	select HID
-	---help---
-	  Say Y here if you want full HID support to connect USB keyboards,
-	  mice, joysticks, graphic tablets, or any other HID based devices
-	  to your computer via USB, as well as Uninterruptible Power Supply
-	  (UPS) and monitor control devices.
-
-	  You can't use this driver and the HIDBP (Boot Protocol) keyboard
-	  and mouse drivers at the same time. More information is available:
-	  <file:Documentation/input/input.txt>.
-
-	  If unsure, say Y.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called usbhid.
-
-comment "Input core support is needed for USB HID input layer or HIDBP support"
-	depends on USB_HID && INPUT=n
-
-config USB_HIDINPUT_POWERBOOK
-	bool "Enable support for iBook/PowerBook special keys"
-	default n
-	depends on USB_HID
-	help
-	  Say Y here if you want support for the special keys (Fn, Numlock) on
-	  Apple iBooks and PowerBooks.
-
-	  If unsure, say N.
-
-config HID_FF
-	bool "Force feedback support (EXPERIMENTAL)"
-	depends on USB_HID && EXPERIMENTAL
-	help
-	  Say Y here is you want force feedback support for a few HID devices.
-	  See below for a list of supported devices.
-
-	  See <file:Documentation/input/ff.txt> for a description of the force
-	  feedback API.
-
-	  If unsure, say N.
-
-config HID_PID
-	bool "PID device support"
-	depends on HID_FF
-	help
-	  Say Y here if you have a PID-compliant device and wish to enable force
-	  feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such
-	  devices.
-
-config LOGITECH_FF
-	bool "Logitech devices support"
-	depends on HID_FF
-	select INPUT_FF_MEMLESS if USB_HID
-	help
-	  Say Y here if you have one of these devices:
-	  - Logitech WingMan Cordless RumblePad
-	  - Logitech WingMan Cordless RumblePad 2
-	  - Logitech WingMan Force 3D
-	  - Logitech Formula Force EX
-	  - Logitech MOMO Force wheel
-
-	  and if you want to enable force feedback for them.
-	  Note: if you say N here, this device will still be supported, but without
-	  force feedback.
-
-config PANTHERLORD_FF
-	bool "PantherLord USB/PS2 2in1 Adapter support"
-	depends on HID_FF
-	select INPUT_FF_MEMLESS if USB_HID
-	help
-	  Say Y here if you have a PantherLord USB/PS2 2in1 Adapter and want
-	  to enable force feedback support for it.
-
-config THRUSTMASTER_FF
-	bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)"
-	depends on HID_FF && EXPERIMENTAL
-	select INPUT_FF_MEMLESS if USB_HID
-	help
-	  Say Y here if you have a THRUSTMASTER FireStore Dual Power 2,
-	  and want to enable force feedback support for it.
-	  Note: if you say N here, this device will still be supported, but without
-	  force feedback.
-
-config ZEROPLUS_FF
-	bool "Zeroplus based game controller support"
-	depends on HID_FF
-	select INPUT_FF_MEMLESS if USB_HID
-	help
-	  Say Y here if you have a Zeroplus based game controller and want to
-	  enable force feedback for it.
-
-config USB_HIDDEV
-	bool "/dev/hiddev raw HID device support"
-	depends on USB_HID
-	help
-	  Say Y here if you want to support HID devices (from the USB
-	  specification standpoint) that aren't strictly user interface
-	  devices, like monitor controls and Uninterruptable Power Supplies.
-
-	  This module supports these devices separately using a separate
-	  event interface on /dev/usb/hiddevX (char 180:96 to 180:111).
-
-	  If unsure, say Y.
-
-menu "USB HID Boot Protocol drivers"
-	depends on USB!=n && USB_HID!=y
-
-config USB_KBD
-	tristate "USB HIDBP Keyboard (simple Boot) support"
-	depends on USB && INPUT
-	---help---
-	  Say Y here only if you are absolutely sure that you don't want
-	  to use the generic HID driver for your USB keyboard and prefer
-	  to use the keyboard in its limited Boot Protocol mode instead.
-
-	  This is almost certainly not what you want.  This is mostly
-	  useful for embedded applications or simple keyboards.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called usbkbd.
-
-	  If even remotely unsure, say N.
-
-config USB_MOUSE
-	tristate "USB HIDBP Mouse (simple Boot) support"
-	depends on USB && INPUT
-	---help---
-	  Say Y here only if you are absolutely sure that you don't want
-	  to use the generic HID driver for your USB mouse and prefer
-	  to use the mouse in its limited Boot Protocol mode instead.
-
-	  This is almost certainly not what you want.  This is mostly
-	  useful for embedded applications or simple mice.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called usbmouse.
-
-	  If even remotely unsure, say N.
-
-endmenu
-
 config USB_AIPTEK
 	tristate "Aiptek 6000U/8000U tablet support"
 	depends on USB && INPUT
diff --git a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile
index a9d206c..9bf420e 100644
--- a/drivers/usb/input/Makefile
+++ b/drivers/usb/input/Makefile
@@ -4,40 +4,12 @@
 
 # Multipart objects.
 wacom-objs	:= wacom_wac.o wacom_sys.o
-usbhid-objs	:= hid-core.o
-
-# Optional parts of multipart objects.
-
-ifeq ($(CONFIG_USB_HIDDEV),y)
-	usbhid-objs	+= hiddev.o
-endif
-ifeq ($(CONFIG_HID_PID),y)
-	usbhid-objs	+= hid-pidff.o
-endif
-ifeq ($(CONFIG_LOGITECH_FF),y)
-	usbhid-objs	+= hid-lgff.o
-endif
-ifeq ($(CONFIG_PANTHERLORD_FF),y)
-	usbhid-objs	+= hid-plff.o
-endif
-ifeq ($(CONFIG_THRUSTMASTER_FF),y)
-	usbhid-objs	+= hid-tmff.o
-endif
-ifeq ($(CONFIG_ZEROPLUS_FF),y)
-	usbhid-objs	+= hid-zpff.o
-endif
-ifeq ($(CONFIG_HID_FF),y)
-	usbhid-objs	+= hid-ff.o
-endif
 
 obj-$(CONFIG_USB_AIPTEK)	+= aiptek.o
 obj-$(CONFIG_USB_ATI_REMOTE)	+= ati_remote.o
 obj-$(CONFIG_USB_ATI_REMOTE2)	+= ati_remote2.o
-obj-$(CONFIG_USB_HID)		+= usbhid.o
-obj-$(CONFIG_USB_KBD)		+= usbkbd.o
 obj-$(CONFIG_USB_KBTAB)		+= kbtab.o
 obj-$(CONFIG_USB_KEYSPAN_REMOTE)	+= keyspan_remote.o
-obj-$(CONFIG_USB_MOUSE)		+= usbmouse.o
 obj-$(CONFIG_USB_MTOUCH)	+= mtouchusb.o
 obj-$(CONFIG_USB_ITMTOUCH)	+= itmtouch.o
 obj-$(CONFIG_USB_EGALAX)	+= touchkitusb.o
diff --git a/drivers/usb/input/ati_remote2.c b/drivers/usb/input/ati_remote2.c
index 83f1f79..6459be9 100644
--- a/drivers/usb/input/ati_remote2.c
+++ b/drivers/usb/input/ati_remote2.c
@@ -2,6 +2,7 @@
  * ati_remote2 - ATI/Philips USB RF remote driver
  *
  * Copyright (C) 2005 Ville Syrjala <syrjala@sci.fi>
+ * Copyright (C) 2007 Peter Stokes <linux@dadeos.freeserve.co.uk>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2
@@ -11,13 +12,29 @@
 #include <linux/usb/input.h>
 
 #define DRIVER_DESC    "ATI/Philips USB RF remote driver"
-#define DRIVER_VERSION "0.1"
+#define DRIVER_VERSION "0.2"
 
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_VERSION(DRIVER_VERSION);
 MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>");
 MODULE_LICENSE("GPL");
 
+/*
+ * ATI Remote Wonder II Channel Configuration
+ *
+ * The remote control can by assigned one of sixteen "channels" in order to facilitate
+ * the use of multiple remote controls within range of each other.
+ * A remote's "channel" may be altered by pressing and holding the "PC" button for
+ * approximately 3 seconds, after which the button will slowly flash the count of the
+ * currently configured "channel", using the numeric keypad enter a number between 1 and
+ * 16 and then the "PC" button again, the button will slowly flash the count of the
+ * newly configured "channel".
+ */
+
+static unsigned int channel_mask = 0xFFFF;
+module_param(channel_mask, uint, 0644);
+MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...<1:Channel2><0:Channel1>");
+
 static unsigned int mode_mask = 0x1F;
 module_param(mode_mask, uint, 0644);
 MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>");
@@ -146,15 +163,23 @@
 {
 	struct input_dev *idev = ar2->idev;
 	u8 *data = ar2->buf[0];
+	int channel, mode;
 
-	if (data[0] > 4) {
+	channel = data[0] >> 4;
+
+	if (!((1 << channel) & channel_mask))
+		return;
+
+	mode = data[0] & 0x0F;
+
+	if (mode > 4) {
 		dev_err(&ar2->intf[0]->dev,
 			"Unknown mode byte (%02x %02x %02x %02x)\n",
 			data[3], data[2], data[1], data[0]);
 		return;
 	}
 
-	if (!((1 << data[0]) & mode_mask))
+	if (!((1 << mode) & mode_mask))
 		return;
 
 	input_event(idev, EV_REL, REL_X, (s8) data[1]);
@@ -177,9 +202,16 @@
 {
 	struct input_dev *idev = ar2->idev;
 	u8 *data = ar2->buf[1];
-	int hw_code, index;
+	int channel, mode, hw_code, index;
 
-	if (data[0] > 4) {
+	channel = data[0] >> 4;
+
+	if (!((1 << channel) & channel_mask))
+		return;
+
+	mode = data[0] & 0x0F;
+
+	if (mode > 4) {
 		dev_err(&ar2->intf[1]->dev,
 			"Unknown mode byte (%02x %02x %02x %02x)\n",
 			data[3], data[2], data[1], data[0]);
@@ -199,16 +231,16 @@
 		 * events for the mouse pad so we filter out any subsequent
 		 * events from the same mode key.
 		 */
-		if (ar2->mode == data[0])
+		if (ar2->mode == mode)
 			return;
 
 		if (data[1] == 0)
-			ar2->mode = data[0];
+			ar2->mode = mode;
 
-		hw_code |= data[0] << 8;
+		hw_code |= mode << 8;
 	}
 
-	if (!((1 << data[0]) & mode_mask))
+	if (!((1 << mode) & mode_mask))
 		return;
 
 	index = ati_remote2_lookup(hw_code);
@@ -379,6 +411,41 @@
 	}
 }
 
+static int ati_remote2_setup(struct ati_remote2 *ar2)
+{
+	int r, i, channel;
+
+	/*
+	 * Configure receiver to only accept input from remote "channel"
+	 *  channel == 0  -> Accept input from any remote channel
+	 *  channel == 1  -> Only accept input from remote channel 1
+	 *  channel == 2  -> Only accept input from remote channel 2
+	 *  ...
+	 *  channel == 16 -> Only accept input from remote channel 16
+	 */
+
+	channel = 0;
+	for (i = 0; i < 16; i++) {
+		if ((1 << i) & channel_mask) {
+			if (!(~(1 << i) & 0xFFFF & channel_mask))
+				channel = i + 1;
+			break;
+		}
+	}
+
+	r = usb_control_msg(ar2->udev, usb_sndctrlpipe(ar2->udev, 0),
+			    0x20,
+			    USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+			    channel, 0x0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+	if (r) {
+		dev_err(&ar2->udev->dev, "%s - failed to set channel due to error: %d\n",
+			__FUNCTION__, r);
+		return r;
+	}
+
+	return 0;
+}
+
 static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id)
 {
 	struct usb_device *udev = interface_to_usbdev(interface);
@@ -409,6 +476,10 @@
 	if (r)
 		goto fail2;
 
+	r = ati_remote2_setup(ar2);
+	if (r)
+		goto fail2;
+
 	usb_make_path(udev, ar2->phys, sizeof(ar2->phys));
 	strlcat(ar2->phys, "/input0", sizeof(ar2->phys));
 
diff --git a/drivers/usb/input/gtco.c b/drivers/usb/input/gtco.c
index 203cdc1bb..ae756e0 100644
--- a/drivers/usb/input/gtco.c
+++ b/drivers/usb/input/gtco.c
@@ -1047,13 +1047,10 @@
 
 	/* Grab private device ptr */
 	struct gtco    *device = usb_get_intfdata (interface);
-	struct input_dev *inputdev;
-
-	inputdev = device->inputdevice;
 
 	/* Now reverse all the registration stuff */
 	if (device) {
-		input_unregister_device(inputdev);
+		input_unregister_device(device->inputdevice);
 		usb_kill_urb(device->urbinfo);
 		usb_free_urb(device->urbinfo);
 		usb_buffer_free(device->usbdev, REPORT_MAX_SIZE,
diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c
deleted file mode 100644
index 827a75a..0000000
--- a/drivers/usb/input/hid-core.c
+++ /dev/null
@@ -1,1477 +0,0 @@
-/*
- *  USB HID support for Linux
- *
- *  Copyright (c) 1999 Andreas Gal
- *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
- *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
- *  Copyright (c) 2006-2007 Jiri Kosina
- */
-
-/*
- * 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.
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/mm.h>
-#include <linux/smp_lock.h>
-#include <linux/spinlock.h>
-#include <asm/unaligned.h>
-#include <asm/byteorder.h>
-#include <linux/input.h>
-#include <linux/wait.h>
-
-#include <linux/usb.h>
-
-#include <linux/hid.h>
-#include <linux/hiddev.h>
-#include <linux/hid-debug.h>
-#include "usbhid.h"
-
-/*
- * Version Information
- */
-
-#define DRIVER_VERSION "v2.6"
-#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik"
-#define DRIVER_DESC "USB HID core driver"
-#define DRIVER_LICENSE "GPL"
-
-static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick",
-				"Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"};
-/*
- * Module parameters.
- */
-
-static unsigned int hid_mousepoll_interval;
-module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644);
-MODULE_PARM_DESC(mousepoll, "Polling interval of mice");
-
-/*
- * Input submission and I/O error handler.
- */
-
-static void hid_io_error(struct hid_device *hid);
-
-/* Start up the input URB */
-static int hid_start_in(struct hid_device *hid)
-{
-	unsigned long flags;
-	int rc = 0;
-	struct usbhid_device *usbhid = hid->driver_data;
-
-	spin_lock_irqsave(&usbhid->inlock, flags);
-	if (hid->open > 0 && !test_bit(HID_SUSPENDED, &usbhid->iofl) &&
-			!test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
-		rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
-		if (rc != 0)
-			clear_bit(HID_IN_RUNNING, &usbhid->iofl);
-	}
-	spin_unlock_irqrestore(&usbhid->inlock, flags);
-	return rc;
-}
-
-/* I/O retry timer routine */
-static void hid_retry_timeout(unsigned long _hid)
-{
-	struct hid_device *hid = (struct hid_device *) _hid;
-	struct usbhid_device *usbhid = hid->driver_data;
-
-	dev_dbg(&usbhid->intf->dev, "retrying intr urb\n");
-	if (hid_start_in(hid))
-		hid_io_error(hid);
-}
-
-/* Workqueue routine to reset the device or clear a halt */
-static void hid_reset(struct work_struct *work)
-{
-	struct usbhid_device *usbhid =
-		container_of(work, struct usbhid_device, reset_work);
-	struct hid_device *hid = usbhid->hid;
-	int rc_lock, rc = 0;
-
-	if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) {
-		dev_dbg(&usbhid->intf->dev, "clear halt\n");
-		rc = usb_clear_halt(hid_to_usb_dev(hid), usbhid->urbin->pipe);
-		clear_bit(HID_CLEAR_HALT, &usbhid->iofl);
-		hid_start_in(hid);
-	}
-
-	else if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) {
-		dev_dbg(&usbhid->intf->dev, "resetting device\n");
-		rc = rc_lock = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf);
-		if (rc_lock >= 0) {
-			rc = usb_reset_composite_device(hid_to_usb_dev(hid), usbhid->intf);
-			if (rc_lock)
-				usb_unlock_device(hid_to_usb_dev(hid));
-		}
-		clear_bit(HID_RESET_PENDING, &usbhid->iofl);
-	}
-
-	switch (rc) {
-	case 0:
-		if (!test_bit(HID_IN_RUNNING, &usbhid->iofl))
-			hid_io_error(hid);
-		break;
-	default:
-		err("can't reset device, %s-%s/input%d, status %d",
-				hid_to_usb_dev(hid)->bus->bus_name,
-				hid_to_usb_dev(hid)->devpath,
-				usbhid->ifnum, rc);
-		/* FALLTHROUGH */
-	case -EHOSTUNREACH:
-	case -ENODEV:
-	case -EINTR:
-		break;
-	}
-}
-
-/* Main I/O error handler */
-static void hid_io_error(struct hid_device *hid)
-{
-	unsigned long flags;
-	struct usbhid_device *usbhid = hid->driver_data;
-
-	spin_lock_irqsave(&usbhid->inlock, flags);
-
-	/* Stop when disconnected */
-	if (usb_get_intfdata(usbhid->intf) == NULL)
-		goto done;
-
-	/* When an error occurs, retry at increasing intervals */
-	if (usbhid->retry_delay == 0) {
-		usbhid->retry_delay = 13;	/* Then 26, 52, 104, 104, ... */
-		usbhid->stop_retry = jiffies + msecs_to_jiffies(1000);
-	} else if (usbhid->retry_delay < 100)
-		usbhid->retry_delay *= 2;
-
-	if (time_after(jiffies, usbhid->stop_retry)) {
-
-		/* Retries failed, so do a port reset */
-		if (!test_and_set_bit(HID_RESET_PENDING, &usbhid->iofl)) {
-			schedule_work(&usbhid->reset_work);
-			goto done;
-		}
-	}
-
-	mod_timer(&usbhid->io_retry,
-			jiffies + msecs_to_jiffies(usbhid->retry_delay));
-done:
-	spin_unlock_irqrestore(&usbhid->inlock, flags);
-}
-
-/*
- * Input interrupt completion handler.
- */
-
-static void hid_irq_in(struct urb *urb)
-{
-	struct hid_device	*hid = urb->context;
-	struct usbhid_device 	*usbhid = hid->driver_data;
-	int			status;
-
-	switch (urb->status) {
-		case 0:			/* success */
-			usbhid->retry_delay = 0;
-			hid_input_report(urb->context, HID_INPUT_REPORT,
-					 urb->transfer_buffer,
-					 urb->actual_length, 1);
-			break;
-		case -EPIPE:		/* stall */
-			clear_bit(HID_IN_RUNNING, &usbhid->iofl);
-			set_bit(HID_CLEAR_HALT, &usbhid->iofl);
-			schedule_work(&usbhid->reset_work);
-			return;
-		case -ECONNRESET:	/* unlink */
-		case -ENOENT:
-		case -ESHUTDOWN:	/* unplug */
-			clear_bit(HID_IN_RUNNING, &usbhid->iofl);
-			return;
-		case -EILSEQ:		/* protocol error or unplug */
-		case -EPROTO:		/* protocol error or unplug */
-		case -ETIME:		/* protocol error or unplug */
-		case -ETIMEDOUT:	/* Should never happen, but... */
-			clear_bit(HID_IN_RUNNING, &usbhid->iofl);
-			hid_io_error(hid);
-			return;
-		default:		/* error */
-			warn("input irq status %d received", urb->status);
-	}
-
-	status = usb_submit_urb(urb, GFP_ATOMIC);
-	if (status) {
-		clear_bit(HID_IN_RUNNING, &usbhid->iofl);
-		if (status != -EPERM) {
-			err("can't resubmit intr, %s-%s/input%d, status %d",
-					hid_to_usb_dev(hid)->bus->bus_name,
-					hid_to_usb_dev(hid)->devpath,
-					usbhid->ifnum, status);
-			hid_io_error(hid);
-		}
-	}
-}
-
-static int hid_submit_out(struct hid_device *hid)
-{
-	struct hid_report *report;
-	struct usbhid_device *usbhid = hid->driver_data;
-
-	report = usbhid->out[usbhid->outtail];
-
-	hid_output_report(report, usbhid->outbuf);
-	usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0);
-	usbhid->urbout->dev = hid_to_usb_dev(hid);
-
-	dbg("submitting out urb");
-
-	if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) {
-		err("usb_submit_urb(out) failed");
-		return -1;
-	}
-
-	return 0;
-}
-
-static int hid_submit_ctrl(struct hid_device *hid)
-{
-	struct hid_report *report;
-	unsigned char dir;
-	int len;
-	struct usbhid_device *usbhid = hid->driver_data;
-
-	report = usbhid->ctrl[usbhid->ctrltail].report;
-	dir = usbhid->ctrl[usbhid->ctrltail].dir;
-
-	len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
-	if (dir == USB_DIR_OUT) {
-		hid_output_report(report, usbhid->ctrlbuf);
-		usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
-		usbhid->urbctrl->transfer_buffer_length = len;
-	} else {
-		int maxpacket, padlen;
-
-		usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0);
-		maxpacket = usb_maxpacket(hid_to_usb_dev(hid), usbhid->urbctrl->pipe, 0);
-		if (maxpacket > 0) {
-			padlen = (len + maxpacket - 1) / maxpacket;
-			padlen *= maxpacket;
-			if (padlen > usbhid->bufsize)
-				padlen = usbhid->bufsize;
-		} else
-			padlen = 0;
-		usbhid->urbctrl->transfer_buffer_length = padlen;
-	}
-	usbhid->urbctrl->dev = hid_to_usb_dev(hid);
-
-	usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
-	usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT;
-	usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id);
-	usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum);
-	usbhid->cr->wLength = cpu_to_le16(len);
-
-	dbg("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u",
-		usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report",
-		usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength);
-
-	if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) {
-		err("usb_submit_urb(ctrl) failed");
-		return -1;
-	}
-
-	return 0;
-}
-
-/*
- * Output interrupt completion handler.
- */
-
-static void hid_irq_out(struct urb *urb)
-{
-	struct hid_device *hid = urb->context;
-	struct usbhid_device *usbhid = hid->driver_data;
-	unsigned long flags;
-	int unplug = 0;
-
-	switch (urb->status) {
-		case 0:			/* success */
-			break;
-		case -ESHUTDOWN:	/* unplug */
-			unplug = 1;
-		case -EILSEQ:		/* protocol error or unplug */
-		case -EPROTO:		/* protocol error or unplug */
-		case -ECONNRESET:	/* unlink */
-		case -ENOENT:
-			break;
-		default:		/* error */
-			warn("output irq status %d received", urb->status);
-	}
-
-	spin_lock_irqsave(&usbhid->outlock, flags);
-
-	if (unplug)
-		usbhid->outtail = usbhid->outhead;
-	else
-		usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
-
-	if (usbhid->outhead != usbhid->outtail) {
-		if (hid_submit_out(hid)) {
-			clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
-			wake_up(&hid->wait);
-		}
-		spin_unlock_irqrestore(&usbhid->outlock, flags);
-		return;
-	}
-
-	clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
-	spin_unlock_irqrestore(&usbhid->outlock, flags);
-	wake_up(&hid->wait);
-}
-
-/*
- * Control pipe completion handler.
- */
-
-static void hid_ctrl(struct urb *urb)
-{
-	struct hid_device *hid = urb->context;
-	struct usbhid_device *usbhid = hid->driver_data;
-	unsigned long flags;
-	int unplug = 0;
-
-	spin_lock_irqsave(&usbhid->ctrllock, flags);
-
-	switch (urb->status) {
-		case 0:			/* success */
-			if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN)
-				hid_input_report(urb->context, usbhid->ctrl[usbhid->ctrltail].report->type,
-						urb->transfer_buffer, urb->actual_length, 0);
-			break;
-		case -ESHUTDOWN:	/* unplug */
-			unplug = 1;
-		case -EILSEQ:		/* protocol error or unplug */
-		case -EPROTO:		/* protocol error or unplug */
-		case -ECONNRESET:	/* unlink */
-		case -ENOENT:
-		case -EPIPE:		/* report not available */
-			break;
-		default:		/* error */
-			warn("ctrl urb status %d received", urb->status);
-	}
-
-	if (unplug)
-		usbhid->ctrltail = usbhid->ctrlhead;
-	else
-		usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
-
-	if (usbhid->ctrlhead != usbhid->ctrltail) {
-		if (hid_submit_ctrl(hid)) {
-			clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
-			wake_up(&hid->wait);
-		}
-		spin_unlock_irqrestore(&usbhid->ctrllock, flags);
-		return;
-	}
-
-	clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
-	spin_unlock_irqrestore(&usbhid->ctrllock, flags);
-	wake_up(&hid->wait);
-}
-
-void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir)
-{
-	int head;
-	unsigned long flags;
-	struct usbhid_device *usbhid = hid->driver_data;
-
-	if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN)
-		return;
-
-	if (usbhid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) {
-
-		spin_lock_irqsave(&usbhid->outlock, flags);
-
-		if ((head = (usbhid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == usbhid->outtail) {
-			spin_unlock_irqrestore(&usbhid->outlock, flags);
-			warn("output queue full");
-			return;
-		}
-
-		usbhid->out[usbhid->outhead] = report;
-		usbhid->outhead = head;
-
-		if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl))
-			if (hid_submit_out(hid))
-				clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
-
-		spin_unlock_irqrestore(&usbhid->outlock, flags);
-		return;
-	}
-
-	spin_lock_irqsave(&usbhid->ctrllock, flags);
-
-	if ((head = (usbhid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == usbhid->ctrltail) {
-		spin_unlock_irqrestore(&usbhid->ctrllock, flags);
-		warn("control queue full");
-		return;
-	}
-
-	usbhid->ctrl[usbhid->ctrlhead].report = report;
-	usbhid->ctrl[usbhid->ctrlhead].dir = dir;
-	usbhid->ctrlhead = head;
-
-	if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl))
-		if (hid_submit_ctrl(hid))
-			clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
-
-	spin_unlock_irqrestore(&usbhid->ctrllock, flags);
-}
-
-static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
-{
-	struct hid_device *hid = dev->private;
-	struct hid_field *field;
-	int offset;
-
-	if (type == EV_FF)
-		return input_ff_event(dev, type, code, value);
-
-	if (type != EV_LED)
-		return -1;
-
-	if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) {
-		warn("event field not found");
-		return -1;
-	}
-
-	hid_set_field(field, offset, value);
-	usbhid_submit_report(hid, field->report, USB_DIR_OUT);
-
-	return 0;
-}
-
-int usbhid_wait_io(struct hid_device *hid)
-{
-	struct usbhid_device *usbhid = hid->driver_data;
-
-	if (!wait_event_timeout(hid->wait, (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl) &&
-					!test_bit(HID_OUT_RUNNING, &usbhid->iofl)),
-					10*HZ)) {
-		dbg("timeout waiting for ctrl or out queue to clear");
-		return -1;
-	}
-
-	return 0;
-}
-
-static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle)
-{
-	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
-		HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, (idle << 8) | report,
-		ifnum, NULL, 0, USB_CTRL_SET_TIMEOUT);
-}
-
-static int hid_get_class_descriptor(struct usb_device *dev, int ifnum,
-		unsigned char type, void *buf, int size)
-{
-	int result, retries = 4;
-
-	memset(buf, 0, size);
-
-	do {
-		result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
-				USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN,
-				(type << 8), ifnum, buf, size, USB_CTRL_GET_TIMEOUT);
-		retries--;
-	} while (result < size && retries);
-	return result;
-}
-
-int usbhid_open(struct hid_device *hid)
-{
-	++hid->open;
-	if (hid_start_in(hid))
-		hid_io_error(hid);
-	return 0;
-}
-
-void usbhid_close(struct hid_device *hid)
-{
-	struct usbhid_device *usbhid = hid->driver_data;
-
-	if (!--hid->open)
-		usb_kill_urb(usbhid->urbin);
-}
-
-#define USB_VENDOR_ID_PANJIT		0x134c
-
-#define USB_VENDOR_ID_TURBOX		0x062a
-#define USB_DEVICE_ID_TURBOX_KEYBOARD	0x0201
-#define USB_VENDOR_ID_CIDC		0x1677
-
-/*
- * Initialize all reports
- */
-
-void usbhid_init_reports(struct hid_device *hid)
-{
-	struct hid_report *report;
-	struct usbhid_device *usbhid = hid->driver_data;
-	int err, ret;
-
-	list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list)
-		usbhid_submit_report(hid, report, USB_DIR_IN);
-
-	list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
-		usbhid_submit_report(hid, report, USB_DIR_IN);
-
-	err = 0;
-	ret = usbhid_wait_io(hid);
-	while (ret) {
-		err |= ret;
-		if (test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
-			usb_kill_urb(usbhid->urbctrl);
-		if (test_bit(HID_OUT_RUNNING, &usbhid->iofl))
-			usb_kill_urb(usbhid->urbout);
-		ret = usbhid_wait_io(hid);
-	}
-
-	if (err)
-		warn("timeout initializing reports");
-}
-
-#define USB_VENDOR_ID_GTCO		0x078c
-#define USB_DEVICE_ID_GTCO_90		0x0090
-#define USB_DEVICE_ID_GTCO_100		0x0100
-#define USB_DEVICE_ID_GTCO_101		0x0101
-#define USB_DEVICE_ID_GTCO_103		0x0103
-#define USB_DEVICE_ID_GTCO_104		0x0104
-#define USB_DEVICE_ID_GTCO_105		0x0105
-#define USB_DEVICE_ID_GTCO_106		0x0106
-#define USB_DEVICE_ID_GTCO_107		0x0107
-#define USB_DEVICE_ID_GTCO_108		0x0108
-#define USB_DEVICE_ID_GTCO_200		0x0200
-#define USB_DEVICE_ID_GTCO_201		0x0201
-#define USB_DEVICE_ID_GTCO_202		0x0202
-#define USB_DEVICE_ID_GTCO_203		0x0203
-#define USB_DEVICE_ID_GTCO_204		0x0204
-#define USB_DEVICE_ID_GTCO_205		0x0205
-#define USB_DEVICE_ID_GTCO_206		0x0206
-#define USB_DEVICE_ID_GTCO_207		0x0207
-#define USB_DEVICE_ID_GTCO_300		0x0300
-#define USB_DEVICE_ID_GTCO_301		0x0301
-#define USB_DEVICE_ID_GTCO_302		0x0302
-#define USB_DEVICE_ID_GTCO_303		0x0303
-#define USB_DEVICE_ID_GTCO_304		0x0304
-#define USB_DEVICE_ID_GTCO_305		0x0305
-#define USB_DEVICE_ID_GTCO_306		0x0306
-#define USB_DEVICE_ID_GTCO_307		0x0307
-#define USB_DEVICE_ID_GTCO_308		0x0308
-#define USB_DEVICE_ID_GTCO_309		0x0309
-#define USB_DEVICE_ID_GTCO_400		0x0400
-#define USB_DEVICE_ID_GTCO_401		0x0401
-#define USB_DEVICE_ID_GTCO_402		0x0402
-#define USB_DEVICE_ID_GTCO_403		0x0403
-#define USB_DEVICE_ID_GTCO_404		0x0404
-#define USB_DEVICE_ID_GTCO_405		0x0405
-#define USB_DEVICE_ID_GTCO_500		0x0500
-#define USB_DEVICE_ID_GTCO_501		0x0501
-#define USB_DEVICE_ID_GTCO_502		0x0502
-#define USB_DEVICE_ID_GTCO_503		0x0503
-#define USB_DEVICE_ID_GTCO_504		0x0504
-#define USB_DEVICE_ID_GTCO_1000		0x1000
-#define USB_DEVICE_ID_GTCO_1001		0x1001
-#define USB_DEVICE_ID_GTCO_1002		0x1002
-#define USB_DEVICE_ID_GTCO_1003		0x1003
-#define USB_DEVICE_ID_GTCO_1004		0x1004
-#define USB_DEVICE_ID_GTCO_1005		0x1005
-#define USB_DEVICE_ID_GTCO_1006		0x1006
-
-#define USB_VENDOR_ID_WACOM		0x056a
-
-#define USB_VENDOR_ID_ACECAD		0x0460
-#define USB_DEVICE_ID_ACECAD_FLAIR	0x0004
-#define USB_DEVICE_ID_ACECAD_302	0x0008
-
-#define USB_VENDOR_ID_KBGEAR		0x084e
-#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO	0x1001
-
-#define USB_VENDOR_ID_AIPTEK		0x08ca
-#define USB_DEVICE_ID_AIPTEK_01		0x0001
-#define USB_DEVICE_ID_AIPTEK_10		0x0010
-#define USB_DEVICE_ID_AIPTEK_20		0x0020
-#define USB_DEVICE_ID_AIPTEK_21		0x0021
-#define USB_DEVICE_ID_AIPTEK_22		0x0022
-#define USB_DEVICE_ID_AIPTEK_23		0x0023
-#define USB_DEVICE_ID_AIPTEK_24		0x0024
-
-#define USB_VENDOR_ID_GRIFFIN		0x077d
-#define USB_DEVICE_ID_POWERMATE		0x0410
-#define USB_DEVICE_ID_SOUNDKNOB		0x04AA
-
-#define USB_VENDOR_ID_ATEN		0x0557
-#define USB_DEVICE_ID_ATEN_UC100KM	0x2004
-#define USB_DEVICE_ID_ATEN_CS124U	0x2202
-#define USB_DEVICE_ID_ATEN_2PORTKVM	0x2204
-#define USB_DEVICE_ID_ATEN_4PORTKVM	0x2205
-#define USB_DEVICE_ID_ATEN_4PORTKVMC	0x2208
-
-#define USB_VENDOR_ID_TOPMAX		0x0663
-#define USB_DEVICE_ID_TOPMAX_COBRAPAD	0x0103
-
-#define USB_VENDOR_ID_HAPP		0x078b
-#define USB_DEVICE_ID_UGCI_DRIVING	0x0010
-#define USB_DEVICE_ID_UGCI_FLYING	0x0020
-#define USB_DEVICE_ID_UGCI_FIGHTING	0x0030
-
-#define USB_VENDOR_ID_MGE		0x0463
-#define USB_DEVICE_ID_MGE_UPS		0xffff
-#define USB_DEVICE_ID_MGE_UPS1		0x0001
-
-#define USB_VENDOR_ID_ONTRAK		0x0a07
-#define USB_DEVICE_ID_ONTRAK_ADU100	0x0064
-
-#define USB_VENDOR_ID_ESSENTIAL_REALITY	0x0d7f
-#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
-
-#define USB_VENDOR_ID_A4TECH		0x09da
-#define USB_DEVICE_ID_A4TECH_WCP32PU	0x0006
-
-#define USB_VENDOR_ID_AASHIMA		0x06d6
-#define USB_DEVICE_ID_AASHIMA_GAMEPAD	0x0025
-#define USB_DEVICE_ID_AASHIMA_PREDATOR	0x0026
-
-#define USB_VENDOR_ID_CYPRESS		0x04b4
-#define USB_DEVICE_ID_CYPRESS_MOUSE	0x0001
-#define USB_DEVICE_ID_CYPRESS_HIDCOM	0x5500
-#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE	0x7417
-
-#define USB_VENDOR_ID_BERKSHIRE		0x0c98
-#define USB_DEVICE_ID_BERKSHIRE_PCWD	0x1140
-
-#define USB_VENDOR_ID_ALPS		0x0433
-#define USB_DEVICE_ID_IBM_GAMEPAD	0x1101
-
-#define USB_VENDOR_ID_SAITEK		0x06a3
-#define USB_DEVICE_ID_SAITEK_RUMBLEPAD	0xff17
-
-#define USB_VENDOR_ID_NEC		0x073e
-#define USB_DEVICE_ID_NEC_USB_GAME_PAD	0x0301
-
-#define USB_VENDOR_ID_CHIC		0x05fe
-#define USB_DEVICE_ID_CHIC_GAMEPAD	0x0014
-
-#define USB_VENDOR_ID_GLAB		0x06c2
-#define USB_DEVICE_ID_4_PHIDGETSERVO_30	0x0038
-#define USB_DEVICE_ID_1_PHIDGETSERVO_30	0x0039
-#define USB_DEVICE_ID_0_0_4_IF_KIT	0x0040
-#define USB_DEVICE_ID_0_16_16_IF_KIT	0x0044
-#define USB_DEVICE_ID_8_8_8_IF_KIT	0x0045
-#define USB_DEVICE_ID_0_8_7_IF_KIT	0x0051
-#define USB_DEVICE_ID_0_8_8_IF_KIT	0x0053
-#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL	0x0058
-
-#define USB_VENDOR_ID_WISEGROUP		0x0925
-#define USB_DEVICE_ID_1_PHIDGETSERVO_20	0x8101
-#define USB_DEVICE_ID_4_PHIDGETSERVO_20	0x8104
-#define USB_DEVICE_ID_8_8_4_IF_KIT	0x8201
-#define USB_DEVICE_ID_DUAL_USB_JOYPAD   0x8866
-
-#define USB_VENDOR_ID_WISEGROUP_LTD	0x6677
-#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802
-
-#define USB_VENDOR_ID_CODEMERCS		0x07c0
-#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST	0x1500
-#define USB_DEVICE_ID_CODEMERCS_IOW_LAST	0x15ff
-
-#define USB_VENDOR_ID_DELORME		0x1163
-#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100
-#define USB_DEVICE_ID_DELORME_EM_LT20	0x0200
-
-#define USB_VENDOR_ID_MCC		0x09db
-#define USB_DEVICE_ID_MCC_PMD1024LS	0x0076
-#define USB_DEVICE_ID_MCC_PMD1208LS	0x007a
-
-#define USB_VENDOR_ID_VERNIER		0x08f7
-#define USB_DEVICE_ID_VERNIER_LABPRO	0x0001
-#define USB_DEVICE_ID_VERNIER_GOTEMP	0x0002
-#define USB_DEVICE_ID_VERNIER_SKIP	0x0003
-#define USB_DEVICE_ID_VERNIER_CYCLOPS	0x0004
-
-#define USB_VENDOR_ID_LD		0x0f11
-#define USB_DEVICE_ID_LD_CASSY		0x1000
-#define USB_DEVICE_ID_LD_POCKETCASSY	0x1010
-#define USB_DEVICE_ID_LD_MOBILECASSY	0x1020
-#define USB_DEVICE_ID_LD_JWM		0x1080
-#define USB_DEVICE_ID_LD_DMMP		0x1081
-#define USB_DEVICE_ID_LD_UMIP		0x1090
-#define USB_DEVICE_ID_LD_XRAY1		0x1100
-#define USB_DEVICE_ID_LD_XRAY2		0x1101
-#define USB_DEVICE_ID_LD_VIDEOCOM	0x1200
-#define USB_DEVICE_ID_LD_COM3LAB	0x2000
-#define USB_DEVICE_ID_LD_TELEPORT	0x2010
-#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020
-#define USB_DEVICE_ID_LD_POWERCONTROL	0x2030
-#define USB_DEVICE_ID_LD_MACHINETEST	0x2040
-
-#define USB_VENDOR_ID_APPLE		0x05ac
-#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE	0x0304
-#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI	0x020e
-#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO	0x020f
-#define USB_DEVICE_ID_APPLE_GEYSER_ANSI	0x0214
-#define USB_DEVICE_ID_APPLE_GEYSER_ISO	0x0215
-#define USB_DEVICE_ID_APPLE_GEYSER_JIS	0x0216
-#define USB_DEVICE_ID_APPLE_GEYSER3_ANSI	0x0217
-#define USB_DEVICE_ID_APPLE_GEYSER3_ISO	0x0218
-#define USB_DEVICE_ID_APPLE_GEYSER3_JIS	0x0219
-#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI	0x021a
-#define USB_DEVICE_ID_APPLE_GEYSER4_ISO	0x021b
-#define USB_DEVICE_ID_APPLE_GEYSER4_JIS	0x021c
-#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY	0x030a
-#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY	0x030b
-#define USB_DEVICE_ID_APPLE_IR		0x8240
-
-#define USB_VENDOR_ID_CHERRY		0x046a
-#define USB_DEVICE_ID_CHERRY_CYMOTION	0x0023
-
-#define USB_VENDOR_ID_YEALINK		0x6993
-#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K	0xb001
-
-#define USB_VENDOR_ID_ALCOR		0x058f
-#define USB_DEVICE_ID_ALCOR_USBRS232	0x9720
-
-#define USB_VENDOR_ID_SUN		0x0430
-#define USB_DEVICE_ID_RARITAN_KVM_DONGLE	0xcdab
-
-#define USB_VENDOR_ID_AIRCABLE		0x16CA
-#define USB_DEVICE_ID_AIRCABLE1		0x1502
-
-#define USB_VENDOR_ID_LOGITECH		0x046d
-#define USB_DEVICE_ID_LOGITECH_USB_RECEIVER	0xc101
-#define USB_DEVICE_ID_LOGITECH_USB_RECEIVER_2	0xc517
-#define USB_DEVICE_ID_DINOVO_EDGE	0xc714
-
-#define USB_VENDOR_ID_IMATION		0x0718
-#define USB_DEVICE_ID_DISC_STAKKA	0xd000
-
-#define USB_VENDOR_ID_PANTHERLORD	0x0810
-#define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK	0x0001
-
-#define USB_VENDOR_ID_SONY			0x054c
-#define USB_DEVICE_ID_SONY_PS3_CONTROLLER	0x0268
-
-/*
- * Alphabetically sorted blacklist by quirk type.
- */
-
-static const struct hid_blacklist {
-	__u16 idVendor;
-	__u16 idProduct;
-	unsigned quirks;
-} hid_blacklist[] = {
-
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES },
-
-	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_103, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_104, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_105, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_106, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_107, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_108, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_200, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_201, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_202, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_203, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_204, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_205, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_206, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_207, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_300, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_301, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_302, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_303, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_304, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_305, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_306, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_307, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_308, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_309, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_400, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_401, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_402, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_403, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_404, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_500, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_501, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_503, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1001, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1003, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE },
-
-	{ USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE },
-
-	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
-	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
-	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
-	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
-	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
-	{ USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
-	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
-	{ USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
-
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL },
-	{ USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_7 },
-	{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_5 },
-
-	{ USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD },
-	{ USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD },
-	{ USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD },
-	{ USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD },
-	{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
-	{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
-	{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
-	{ USB_VENDOR_ID_NEC, USB_DEVICE_ID_NEC_USB_GAME_PAD, HID_QUIRK_BADPAD },
-	{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
-	{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
-
-	{ USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION },
-
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD},
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD},
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD},
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IR, HID_QUIRK_IGNORE },
-
-	{ USB_VENDOR_ID_PANJIT, 0x0001, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_PANJIT, 0x0002, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_PANJIT, 0x0003, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_PANJIT, 0x0004, HID_QUIRK_IGNORE },
-
-	{ USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET },
-
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_USB_RECEIVER, HID_QUIRK_BAD_RELATIVE_KEYS },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_USB_RECEIVER_2, HID_QUIRK_LOGITECH_S510_DESCRIPTOR },
-
-	{ USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
-
-	{ USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER, HID_QUIRK_SONY_PS3_CONTROLLER },
-
-	{ USB_VENDOR_ID_CIDC, 0x0103, HID_QUIRK_IGNORE },
-
-	{ 0, 0 }
-};
-
-/*
- * Traverse the supplied list of reports and find the longest
- */
-static void hid_find_max_report(struct hid_device *hid, unsigned int type, int *max)
-{
-	struct hid_report *report;
-	int size;
-
-	list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
-		size = ((report->size - 1) >> 3) + 1;
-		if (type == HID_INPUT_REPORT && hid->report_enum[type].numbered)
-			size++;
-		if (*max < size)
-			*max = size;
-	}
-}
-
-static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid)
-{
-	struct usbhid_device *usbhid = hid->driver_data;
-
-	if (!(usbhid->inbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->inbuf_dma)))
-		return -1;
-	if (!(usbhid->outbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->outbuf_dma)))
-		return -1;
-	if (!(usbhid->cr = usb_buffer_alloc(dev, sizeof(*(usbhid->cr)), GFP_ATOMIC, &usbhid->cr_dma)))
-		return -1;
-	if (!(usbhid->ctrlbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->ctrlbuf_dma)))
-		return -1;
-
-	return 0;
-}
-
-static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
-{
-	struct usbhid_device *usbhid = hid->driver_data;
-
-	if (usbhid->inbuf)
-		usb_buffer_free(dev, usbhid->bufsize, usbhid->inbuf, usbhid->inbuf_dma);
-	if (usbhid->outbuf)
-		usb_buffer_free(dev, usbhid->bufsize, usbhid->outbuf, usbhid->outbuf_dma);
-	if (usbhid->cr)
-		usb_buffer_free(dev, sizeof(*(usbhid->cr)), usbhid->cr, usbhid->cr_dma);
-	if (usbhid->ctrlbuf)
-		usb_buffer_free(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma);
-}
-
-/*
- * Cherry Cymotion keyboard have an invalid HID report descriptor,
- * that needs fixing before we can parse it.
- */
-
-static void hid_fixup_cymotion_descriptor(char *rdesc, int rsize)
-{
-	if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) {
-		info("Fixing up Cherry Cymotion report descriptor");
-		rdesc[11] = rdesc[16] = 0xff;
-		rdesc[12] = rdesc[17] = 0x03;
-	}
-}
-
-/*
- * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
- * to "operational".  Without this, the ps3 controller will not report any
- * events.
- */
-static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum)
-{
-	int result;
-	char *buf = kmalloc(18, GFP_KERNEL);
-
-	if (!buf)
-		return;
-
-	result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
-				 HID_REQ_GET_REPORT,
-				 USB_DIR_IN | USB_TYPE_CLASS |
-				 USB_RECIP_INTERFACE,
-				 (3 << 8) | 0xf2, ifnum, buf, 17,
-				 USB_CTRL_GET_TIMEOUT);
-
-	if (result < 0)
-		err("%s failed: %d\n", __func__, result);
-
-	kfree(buf);
-}
-
-/*
- * Logitech S510 keyboard sends in report #3 keys which are far
- * above the logical maximum described in descriptor. This extends
- * the original value of 0x28c of logical maximum to 0x104d
- */
-static void hid_fixup_s510_descriptor(unsigned char *rdesc, int rsize)
-{
-	if (rsize >= 90 && rdesc[83] == 0x26
-			&& rdesc[84] == 0x8c
-			&& rdesc[85] == 0x02) {
-		info("Fixing up Logitech S510 report descriptor");
-		rdesc[84] = rdesc[89] = 0x4d;
-		rdesc[85] = rdesc[90] = 0x10;
-	}
-}
-
-static struct hid_device *usb_hid_configure(struct usb_interface *intf)
-{
-	struct usb_host_interface *interface = intf->cur_altsetting;
-	struct usb_device *dev = interface_to_usbdev (intf);
-	struct hid_descriptor *hdesc;
-	struct hid_device *hid;
-	unsigned quirks = 0, rsize = 0;
-	char *rdesc;
-	int n, len, insize = 0;
-	struct usbhid_device *usbhid;
-
-	/* Ignore all Wacom devices */
-	if (le16_to_cpu(dev->descriptor.idVendor) == USB_VENDOR_ID_WACOM)
-		return NULL;
-	/* ignore all Code Mercenaries IOWarrior devices */
-	if (le16_to_cpu(dev->descriptor.idVendor) == USB_VENDOR_ID_CODEMERCS)
-		if (le16_to_cpu(dev->descriptor.idProduct) >= USB_DEVICE_ID_CODEMERCS_IOW_FIRST &&
-		    le16_to_cpu(dev->descriptor.idProduct) <= USB_DEVICE_ID_CODEMERCS_IOW_LAST)
-			return NULL;
-
-	for (n = 0; hid_blacklist[n].idVendor; n++)
-		if ((hid_blacklist[n].idVendor == le16_to_cpu(dev->descriptor.idVendor)) &&
-			(hid_blacklist[n].idProduct == le16_to_cpu(dev->descriptor.idProduct)))
-				quirks = hid_blacklist[n].quirks;
-
-	/* Many keyboards and mice don't like to be polled for reports,
-	 * so we will always set the HID_QUIRK_NOGET flag for them. */
-	if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) {
-		if (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD ||
-			interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)
-				quirks |= HID_QUIRK_NOGET;
-	}
-
-	if (quirks & HID_QUIRK_IGNORE)
-		return NULL;
-
-	if ((quirks & HID_QUIRK_IGNORE_MOUSE) &&
-		(interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE))
-			return NULL;
-
-
-	if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) &&
-	    (!interface->desc.bNumEndpoints ||
-	     usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {
-		dbg("class descriptor not present\n");
-		return NULL;
-	}
-
-	for (n = 0; n < hdesc->bNumDescriptors; n++)
-		if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT)
-			rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);
-
-	if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
-		dbg("weird size of report descriptor (%u)", rsize);
-		return NULL;
-	}
-
-	if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
-		dbg("couldn't allocate rdesc memory");
-		return NULL;
-	}
-
-	hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0);
-
-	if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) {
-		dbg("reading report descriptor failed");
-		kfree(rdesc);
-		return NULL;
-	}
-
-	if ((quirks & HID_QUIRK_CYMOTION))
-		hid_fixup_cymotion_descriptor(rdesc, rsize);
-
-	if (quirks & HID_QUIRK_LOGITECH_S510_DESCRIPTOR)
-		hid_fixup_s510_descriptor(rdesc, rsize);
-
-#ifdef CONFIG_HID_DEBUG
-	printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n);
-	for (n = 0; n < rsize; n++)
-		printk(" %02x", (unsigned char) rdesc[n]);
-	printk("\n");
-#endif
-
-	if (!(hid = hid_parse_report(rdesc, n))) {
-		dbg("parsing report descriptor failed");
-		kfree(rdesc);
-		return NULL;
-	}
-
-	kfree(rdesc);
-	hid->quirks = quirks;
-
-	if (!(usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL)))
-		goto fail;
-
-	hid->driver_data = usbhid;
-	usbhid->hid = hid;
-
-	usbhid->bufsize = HID_MIN_BUFFER_SIZE;
-	hid_find_max_report(hid, HID_INPUT_REPORT, &usbhid->bufsize);
-	hid_find_max_report(hid, HID_OUTPUT_REPORT, &usbhid->bufsize);
-	hid_find_max_report(hid, HID_FEATURE_REPORT, &usbhid->bufsize);
-
-	if (usbhid->bufsize > HID_MAX_BUFFER_SIZE)
-		usbhid->bufsize = HID_MAX_BUFFER_SIZE;
-
-	hid_find_max_report(hid, HID_INPUT_REPORT, &insize);
-
-	if (insize > HID_MAX_BUFFER_SIZE)
-		insize = HID_MAX_BUFFER_SIZE;
-
-	if (hid_alloc_buffers(dev, hid)) {
-		hid_free_buffers(dev, hid);
-		goto fail;
-	}
-
-	for (n = 0; n < interface->desc.bNumEndpoints; n++) {
-
-		struct usb_endpoint_descriptor *endpoint;
-		int pipe;
-		int interval;
-
-		endpoint = &interface->endpoint[n].desc;
-		if ((endpoint->bmAttributes & 3) != 3)		/* Not an interrupt endpoint */
-			continue;
-
-		interval = endpoint->bInterval;
-
-		/* Change the polling interval of mice. */
-		if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0)
-			interval = hid_mousepoll_interval;
-
-		if (usb_endpoint_dir_in(endpoint)) {
-			if (usbhid->urbin)
-				continue;
-			if (!(usbhid->urbin = usb_alloc_urb(0, GFP_KERNEL)))
-				goto fail;
-			pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
-			usb_fill_int_urb(usbhid->urbin, dev, pipe, usbhid->inbuf, insize,
-					 hid_irq_in, hid, interval);
-			usbhid->urbin->transfer_dma = usbhid->inbuf_dma;
-			usbhid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-		} else {
-			if (usbhid->urbout)
-				continue;
-			if (!(usbhid->urbout = usb_alloc_urb(0, GFP_KERNEL)))
-				goto fail;
-			pipe = usb_sndintpipe(dev, endpoint->bEndpointAddress);
-			usb_fill_int_urb(usbhid->urbout, dev, pipe, usbhid->outbuf, 0,
-					 hid_irq_out, hid, interval);
-			usbhid->urbout->transfer_dma = usbhid->outbuf_dma;
-			usbhid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-		}
-	}
-
-	if (!usbhid->urbin) {
-		err("couldn't find an input interrupt endpoint");
-		goto fail;
-	}
-
-	init_waitqueue_head(&hid->wait);
-
-	INIT_WORK(&usbhid->reset_work, hid_reset);
-	setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
-
-	spin_lock_init(&usbhid->inlock);
-	spin_lock_init(&usbhid->outlock);
-	spin_lock_init(&usbhid->ctrllock);
-
-	hid->version = le16_to_cpu(hdesc->bcdHID);
-	hid->country = hdesc->bCountryCode;
-	hid->dev = &intf->dev;
-	usbhid->intf = intf;
-	usbhid->ifnum = interface->desc.bInterfaceNumber;
-
-	hid->name[0] = 0;
-
-	if (dev->manufacturer)
-		strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));
-
-	if (dev->product) {
-		if (dev->manufacturer)
-			strlcat(hid->name, " ", sizeof(hid->name));
-		strlcat(hid->name, dev->product, sizeof(hid->name));
-	}
-
-	if (!strlen(hid->name))
-		snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",
-			 le16_to_cpu(dev->descriptor.idVendor),
-			 le16_to_cpu(dev->descriptor.idProduct));
-
-	hid->bus = BUS_USB;
-	hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
-	hid->product = le16_to_cpu(dev->descriptor.idProduct);
-
-	usb_make_path(dev, hid->phys, sizeof(hid->phys));
-	strlcat(hid->phys, "/input", sizeof(hid->phys));
-	len = strlen(hid->phys);
-	if (len < sizeof(hid->phys) - 1)
-		snprintf(hid->phys + len, sizeof(hid->phys) - len,
-			 "%d", intf->altsetting[0].desc.bInterfaceNumber);
-
-	if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
-		hid->uniq[0] = 0;
-
-	usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);
-	if (!usbhid->urbctrl)
-		goto fail;
-
-	usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr,
-			     usbhid->ctrlbuf, 1, hid_ctrl, hid);
-	usbhid->urbctrl->setup_dma = usbhid->cr_dma;
-	usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
-	usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
-	hid->hidinput_input_event = usb_hidinput_input_event;
-	hid->hid_open = usbhid_open;
-	hid->hid_close = usbhid_close;
-#ifdef CONFIG_USB_HIDDEV
-	hid->hiddev_hid_event = hiddev_hid_event;
-	hid->hiddev_report_event = hiddev_report_event;
-#endif
-	return hid;
-
-fail:
-	usb_free_urb(usbhid->urbin);
-	usb_free_urb(usbhid->urbout);
-	usb_free_urb(usbhid->urbctrl);
-	hid_free_buffers(dev, hid);
-	hid_free_device(hid);
-
-	return NULL;
-}
-
-static void hid_disconnect(struct usb_interface *intf)
-{
-	struct hid_device *hid = usb_get_intfdata (intf);
-	struct usbhid_device *usbhid;
-
-	if (!hid)
-		return;
-
-	usbhid = hid->driver_data;
-
-	spin_lock_irq(&usbhid->inlock);	/* Sync with error handler */
-	usb_set_intfdata(intf, NULL);
-	spin_unlock_irq(&usbhid->inlock);
-	usb_kill_urb(usbhid->urbin);
-	usb_kill_urb(usbhid->urbout);
-	usb_kill_urb(usbhid->urbctrl);
-
-	del_timer_sync(&usbhid->io_retry);
-	flush_scheduled_work();
-
-	if (hid->claimed & HID_CLAIMED_INPUT)
-		hidinput_disconnect(hid);
-	if (hid->claimed & HID_CLAIMED_HIDDEV)
-		hiddev_disconnect(hid);
-
-	usb_free_urb(usbhid->urbin);
-	usb_free_urb(usbhid->urbctrl);
-	usb_free_urb(usbhid->urbout);
-
-	hid_free_buffers(hid_to_usb_dev(hid), hid);
-	hid_free_device(hid);
-}
-
-static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
-{
-	struct hid_device *hid;
-	char path[64];
-	int i;
-	char *c;
-
-	dbg("HID probe called for ifnum %d",
-			intf->altsetting->desc.bInterfaceNumber);
-
-	if (!(hid = usb_hid_configure(intf)))
-		return -ENODEV;
-
-	usbhid_init_reports(hid);
-	hid_dump_device(hid);
-
-	if (!hidinput_connect(hid))
-		hid->claimed |= HID_CLAIMED_INPUT;
-	if (!hiddev_connect(hid))
-		hid->claimed |= HID_CLAIMED_HIDDEV;
-
-	usb_set_intfdata(intf, hid);
-
-	if (!hid->claimed) {
-		printk ("HID device not claimed by input or hiddev\n");
-		hid_disconnect(intf);
-		return -ENODEV;
-	}
-
-	if ((hid->claimed & HID_CLAIMED_INPUT))
-		hid_ff_init(hid);
-
-	if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER)
-		hid_fixup_sony_ps3_controller(interface_to_usbdev(intf),
-			intf->cur_altsetting->desc.bInterfaceNumber);
-
-	printk(KERN_INFO);
-
-	if (hid->claimed & HID_CLAIMED_INPUT)
-		printk("input");
-	if (hid->claimed == (HID_CLAIMED_INPUT | HID_CLAIMED_HIDDEV))
-		printk(",");
-	if (hid->claimed & HID_CLAIMED_HIDDEV)
-		printk("hiddev%d", hid->minor);
-
-	c = "Device";
-	for (i = 0; i < hid->maxcollection; i++) {
-		if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
-		    (hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK &&
-		    (hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) {
-			c = hid_types[hid->collection[i].usage & 0xffff];
-			break;
-		}
-	}
-
-	usb_make_path(interface_to_usbdev(intf), path, 63);
-
-	printk(": USB HID v%x.%02x %s [%s] on %s\n",
-		hid->version >> 8, hid->version & 0xff, c, hid->name, path);
-
-	return 0;
-}
-
-static int hid_suspend(struct usb_interface *intf, pm_message_t message)
-{
-	struct hid_device *hid = usb_get_intfdata (intf);
-	struct usbhid_device *usbhid = hid->driver_data;
-
-	spin_lock_irq(&usbhid->inlock);	/* Sync with error handler */
-	set_bit(HID_SUSPENDED, &usbhid->iofl);
-	spin_unlock_irq(&usbhid->inlock);
-	del_timer(&usbhid->io_retry);
-	usb_kill_urb(usbhid->urbin);
-	dev_dbg(&intf->dev, "suspend\n");
-	return 0;
-}
-
-static int hid_resume(struct usb_interface *intf)
-{
-	struct hid_device *hid = usb_get_intfdata (intf);
-	struct usbhid_device *usbhid = hid->driver_data;
-	int status;
-
-	clear_bit(HID_SUSPENDED, &usbhid->iofl);
-	usbhid->retry_delay = 0;
-	status = hid_start_in(hid);
-	dev_dbg(&intf->dev, "resume status %d\n", status);
-	return status;
-}
-
-/* Treat USB reset pretty much the same as suspend/resume */
-static void hid_pre_reset(struct usb_interface *intf)
-{
-	/* FIXME: What if the interface is already suspended? */
-	hid_suspend(intf, PMSG_ON);
-}
-
-static void hid_post_reset(struct usb_interface *intf)
-{
-	struct usb_device *dev = interface_to_usbdev (intf);
-
-	hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0);
-	/* FIXME: Any more reinitialization needed? */
-
-	hid_resume(intf);
-}
-
-static struct usb_device_id hid_usb_ids [] = {
-	{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
-		.bInterfaceClass = USB_INTERFACE_CLASS_HID },
-	{ }						/* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, hid_usb_ids);
-
-static struct usb_driver hid_driver = {
-	.name =		"usbhid",
-	.probe =	hid_probe,
-	.disconnect =	hid_disconnect,
-	.suspend =	hid_suspend,
-	.resume =	hid_resume,
-	.pre_reset =	hid_pre_reset,
-	.post_reset =	hid_post_reset,
-	.id_table =	hid_usb_ids,
-};
-
-static int __init hid_init(void)
-{
-	int retval;
-	retval = hiddev_init();
-	if (retval)
-		goto hiddev_init_fail;
-	retval = usb_register(&hid_driver);
-	if (retval)
-		goto usb_register_fail;
-	info(DRIVER_VERSION ":" DRIVER_DESC);
-
-	return 0;
-usb_register_fail:
-	hiddev_exit();
-hiddev_init_fail:
-	return retval;
-}
-
-static void __exit hid_exit(void)
-{
-	usb_deregister(&hid_driver);
-	hiddev_exit();
-}
-
-module_init(hid_init);
-module_exit(hid_exit);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE(DRIVER_LICENSE);
diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c
index 75bfab9..77145f9 100644
--- a/drivers/usb/misc/adutux.c
+++ b/drivers/usb/misc/adutux.c
@@ -285,23 +285,24 @@
 	/* save device in the file's private structure */
 	file->private_data = dev;
 
-	/* initialize in direction */
-	dev->read_buffer_length = 0;
+	if (dev->open_count == 1) {
+		/* initialize in direction */
+		dev->read_buffer_length = 0;
 
-	/* fixup first read by having urb waiting for it */
-	usb_fill_int_urb(dev->interrupt_in_urb,dev->udev,
-			 usb_rcvintpipe(dev->udev,
-			 		dev->interrupt_in_endpoint->bEndpointAddress),
-			 dev->interrupt_in_buffer,
-			 le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
-			 adu_interrupt_in_callback, dev,
-			 dev->interrupt_in_endpoint->bInterval);
-	/* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */
-	dev->read_urb_finished = 0;
-	usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
-	/* we ignore failure */
-	/* end of fixup for first read */
-
+		/* fixup first read by having urb waiting for it */
+		usb_fill_int_urb(dev->interrupt_in_urb,dev->udev,
+				 usb_rcvintpipe(dev->udev,
+				 		dev->interrupt_in_endpoint->bEndpointAddress),
+				 dev->interrupt_in_buffer,
+				 le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
+				 adu_interrupt_in_callback, dev,
+				 dev->interrupt_in_endpoint->bInterval);
+		/* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */
+		dev->read_urb_finished = 0;
+		retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
+		if (retval)
+			--dev->open_count;
+	}
 	up(&dev->sem);
 
 exit_no_device:
@@ -469,7 +470,7 @@
 							 adu_interrupt_in_callback,
 							 dev,
 							 dev->interrupt_in_endpoint->bInterval);
-					retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
+					retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC);
 					if (!retval) {
 						spin_unlock_irqrestore(&dev->buflock, flags);
 						dbg(2," %s : submitted OK", __FUNCTION__);
@@ -539,7 +540,7 @@
 	size_t bytes_written = 0;
 	size_t bytes_to_write;
 	size_t buffer_size;
-	int retval = 0;
+	int retval;
 	int timeout = 0;
 
 	dbg(2," %s : enter, count = %Zd", __FUNCTION__, count);
@@ -547,7 +548,9 @@
 	dev = file->private_data;
 
 	/* lock this object */
-	down_interruptible(&dev->sem);
+	retval = down_interruptible(&dev->sem);
+	if (retval)
+		goto exit_nolock;
 
 	/* verify that the device wasn't unplugged */
 	if (dev->udev == NULL || dev->minor == 0) {
@@ -575,7 +578,11 @@
 			}
 			up(&dev->sem);
 			timeout = interruptible_sleep_on_timeout(&dev->write_wait, timeout);
-			down_interruptible(&dev->sem);
+			retval = down_interruptible(&dev->sem);
+			if (retval) {
+				retval = bytes_written ? bytes_written : retval;
+				goto exit_nolock;
+			}
 			if (timeout > 0) {
 				break;
 			}
@@ -637,6 +644,7 @@
 exit:
 	/* unlock the device */
 	up(&dev->sem);
+exit_nolock:
 
 	dbg(2," %s : leave, return value %d", __FUNCTION__, retval);
 
diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c
index b63b5f3..d721380 100644
--- a/drivers/usb/misc/cypress_cy7c63.c
+++ b/drivers/usb/misc/cypress_cy7c63.c
@@ -246,11 +246,13 @@
 	struct cypress *dev;
 
 	dev = usb_get_intfdata(interface);
-	usb_set_intfdata(interface, NULL);
 
 	/* remove device attribute files */
 	device_remove_file(&interface->dev, &dev_attr_port0);
 	device_remove_file(&interface->dev, &dev_attr_port1);
+	/* the intfdata can be set to NULL only after the
+	 * device files have been removed */
+	usb_set_intfdata(interface, NULL);
 
 	usb_put_dev(dev->udev);
 
diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c
index bc3327e..e2172e5 100644
--- a/drivers/usb/misc/ftdi-elan.c
+++ b/drivers/usb/misc/ftdi-elan.c
@@ -2304,7 +2304,6 @@
 #define OHCI_QUIRK_SUPERIO 0x02
 #define OHCI_QUIRK_INITRESET 0x04
 #define OHCI_BIG_ENDIAN 0x08
-#define OHCI_QUIRK_ZFMICRO 0x10
 #define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
 #define OHCI_INTR_INIT (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | \
         OHCI_INTR_WDH)
@@ -2910,24 +2909,28 @@
         INIT_LIST_HEAD(&ftdi_static_list);
         status_queue = create_singlethread_workqueue("ftdi-status-control");
 	if (!status_queue)
-		goto err1;
+		goto err_status_queue;
         command_queue = create_singlethread_workqueue("ftdi-command-engine");
 	if (!command_queue)
-		goto err2;
+		goto err_command_queue;
         respond_queue = create_singlethread_workqueue("ftdi-respond-engine");
 	if (!respond_queue)
-		goto err3;
+		goto err_respond_queue;
         result = usb_register(&ftdi_elan_driver);
-        if (result)
+        if (result) {
+		destroy_workqueue(status_queue);
+		destroy_workqueue(command_queue);
+		destroy_workqueue(respond_queue);
                 printk(KERN_ERR "usb_register failed. Error number %d\n",
 		       result);
+	}
         return result;
 
- err3:
+ err_respond_queue:
 	destroy_workqueue(command_queue);
- err2:
+ err_command_queue:
 	destroy_workqueue(status_queue);
- err1:
+ err_status_queue:
 	printk(KERN_ERR "%s couldn't create workqueue\n", ftdi_elan_driver.name);
 	return -ENOMEM;
 }
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index d69665c..fc51207 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -118,7 +118,7 @@
 			       USB_DIR_IN | USB_TYPE_CLASS |
 			       USB_RECIP_INTERFACE, (type << 8) + id,
 			       inter->desc.bInterfaceNumber, buf, size,
-			       GET_TIMEOUT);
+			       GET_TIMEOUT*HZ);
 }
 //#endif
 
@@ -133,7 +133,7 @@
 			       USB_TYPE_CLASS | USB_RECIP_INTERFACE,
 			       (type << 8) + id,
 			       intf->cur_altsetting->desc.bInterfaceNumber, buf,
-			       size, 1);
+			       size, HZ);
 }
 
 /*---------------------*/
@@ -417,14 +417,14 @@
 		if (!int_out_urb) {
 			retval = -ENOMEM;
 			dbg("%s Unable to allocate urb ", __func__);
-			goto error;
+			goto error_no_urb;
 		}
 		buf = usb_buffer_alloc(dev->udev, dev->report_size,
 				       GFP_KERNEL, &int_out_urb->transfer_dma);
 		if (!buf) {
 			retval = -ENOMEM;
 			dbg("%s Unable to allocate buffer ", __func__);
-			goto error;
+			goto error_no_buffer;
 		}
 		usb_fill_int_urb(int_out_urb, dev->udev,
 				 usb_sndintpipe(dev->udev,
@@ -459,7 +459,9 @@
 error:
 	usb_buffer_free(dev->udev, dev->report_size, buf,
 			int_out_urb->transfer_dma);
+error_no_buffer:
 	usb_free_urb(int_out_urb);
+error_no_urb:
 	atomic_dec(&dev->write_busy);
 	wake_up_interruptible(&dev->write_wait);
 exit:
@@ -748,7 +750,6 @@
 	struct usb_endpoint_descriptor *endpoint;
 	int i;
 	int retval = -ENOMEM;
-	int idele = 0;
 
 	/* allocate memory for our device state and intialize it */
 	dev = kzalloc(sizeof(struct iowarrior), GFP_KERNEL);
@@ -824,11 +825,10 @@
 
 	/* Set the idle timeout to 0, if this is interface 0 */
 	if (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) {
-		idele = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-					0x0A,
-					USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
-					0, NULL, 0, USB_CTRL_SET_TIMEOUT);
-		dbg("idele = %d", idele);
+	    usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			    0x0A,
+			    USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
+			    0, NULL, 0, USB_CTRL_SET_TIMEOUT);
 	}
 	/* allow device read and ioctl */
 	dev->present = 1;
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index 788a11e..11555bd 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -62,6 +62,8 @@
 #define USB_DEVICE_ID_VERNIER_SKIP	0x0003
 #define USB_DEVICE_ID_VERNIER_CYCLOPS	0x0004
 
+#define USB_VENDOR_ID_MICROCHIP		0x04d8
+#define USB_DEVICE_ID_PICDEM		0x000c
 
 #ifdef CONFIG_USB_DYNAMIC_MINORS
 #define USB_LD_MINOR_BASE	0
@@ -89,6 +91,7 @@
 	{ USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) },
 	{ USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) },
 	{ USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) },
+	{ USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICDEM) },
 	{ }					/* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, ld_usb_table);
diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c
index ada2ebc..887ef95 100644
--- a/drivers/usb/misc/usblcd.c
+++ b/drivers/usb/misc/usblcd.c
@@ -47,6 +47,7 @@
 #define to_lcd_dev(d) container_of(d, struct usb_lcd, kref)
 
 static struct usb_driver lcd_driver;
+static DEFINE_MUTEX(usb_lcd_open_mutex);
 
 
 static void lcd_delete(struct kref *kref)
@@ -68,6 +69,7 @@
 
 	subminor = iminor(inode);
 
+	mutex_lock(&usb_lcd_open_mutex);
 	interface = usb_find_interface(&lcd_driver, subminor);
 	if (!interface) {
 		err ("USBLCD: %s - error, can't find device for minor %d",
@@ -89,6 +91,7 @@
 	file->private_data = dev;
 
 exit:
+	mutex_unlock(&usb_lcd_open_mutex);
 	return retval;
 }
 
@@ -347,7 +350,7 @@
         int minor = interface->minor;
 
         /* prevent skel_open() from racing skel_disconnect() */
-        lock_kernel();
+        mutex_lock(&usb_lcd_open_mutex);
 
         dev = usb_get_intfdata(interface);
         usb_set_intfdata(interface, NULL);
@@ -355,7 +358,7 @@
         /* give back our minor */
         usb_deregister_dev(interface, &lcd_class);
  
-	unlock_kernel();
+	mutex_unlock(&usb_lcd_open_mutex);
 
 	/* decrement our usage count */
 	kref_put(&dev->kref, lcd_delete);
diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c
index b2bedd9..0af11a6 100644
--- a/drivers/usb/mon/mon_bin.c
+++ b/drivers/usb/mon/mon_bin.c
@@ -356,8 +356,10 @@
 	if (!usb_pipecontrol(urb->pipe) || ev_type != 'S')
 		return '-';
 
-	if (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)
+	if (urb->dev->bus->uses_dma &&
+	    (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) {
 		return mon_dmapeek(setupb, urb->setup_dma, SETUP_LEN);
+	}
 	if (urb->setup_packet == NULL)
 		return 'Z';
 
@@ -369,7 +371,8 @@
     unsigned int offset, struct urb *urb, unsigned int length)
 {
 
-	if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) {
+	if (urb->dev->bus->uses_dma &&
+	    (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
 		mon_dmapeek_vec(rp, offset, urb->transfer_dma, length);
 		return 0;
 	}
@@ -440,7 +443,7 @@
 	/* We use the fact that usb_pipein() returns 0x80 */
 	ep->epnum = usb_pipeendpoint(urb->pipe) | usb_pipein(urb->pipe);
 	ep->devnum = usb_pipedevice(urb->pipe);
-	ep->busnum = rp->r.m_bus->u_bus->busnum;
+	ep->busnum = urb->dev->bus->busnum;
 	ep->id = (unsigned long) urb;
 	ep->ts_sec = ts.tv_sec;
 	ep->ts_usec = ts.tv_usec;
@@ -500,7 +503,7 @@
 	/* We use the fact that usb_pipein() returns 0x80 */
 	ep->epnum = usb_pipeendpoint(urb->pipe) | usb_pipein(urb->pipe);
 	ep->devnum = usb_pipedevice(urb->pipe);
-	ep->busnum = rp->r.m_bus->u_bus->busnum;
+	ep->busnum = urb->dev->bus->busnum;
 	ep->id = (unsigned long) urb;
 	ep->status = error;
 
@@ -515,7 +518,6 @@
 static int mon_bin_open(struct inode *inode, struct file *file)
 {
 	struct mon_bus *mbus;
-	struct usb_bus *ubus;
 	struct mon_reader_bin *rp;
 	size_t size;
 	int rc;
@@ -525,7 +527,7 @@
 		mutex_unlock(&mon_lock);
 		return -ENODEV;
 	}
-	if ((ubus = mbus->u_bus) == NULL) {
+	if (mbus != &mon_bus0 && mbus->u_bus == NULL) {
 		printk(KERN_ERR TAG ": consistency error on open\n");
 		mutex_unlock(&mon_lock);
 		return -ENODEV;
diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c
index c9739e7..8a1df2c 100644
--- a/drivers/usb/mon/mon_main.c
+++ b/drivers/usb/mon/mon_main.c
@@ -16,8 +16,6 @@
 #include "usb_mon.h"
 #include "../core/hcd.h"
 
-static void mon_submit(struct usb_bus *ubus, struct urb *urb);
-static void mon_complete(struct usb_bus *ubus, struct urb *urb);
 static void mon_stop(struct mon_bus *mbus);
 static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus);
 static void mon_bus_drop(struct kref *r);
@@ -25,6 +23,7 @@
 
 DEFINE_MUTEX(mon_lock);
 
+struct mon_bus mon_bus0;		/* Pseudo bus meaning "all buses" */
 static LIST_HEAD(mon_buses);		/* All buses we know: struct mon_bus */
 
 /*
@@ -35,22 +34,19 @@
 void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r)
 {
 	unsigned long flags;
-	struct usb_bus *ubus;
+	struct list_head *p;
 
 	spin_lock_irqsave(&mbus->lock, flags);
 	if (mbus->nreaders == 0) {
-		ubus = mbus->u_bus;
-		if (ubus->monitored) {
-			/*
-			 * Something is really broken, refuse to go on and
-			 * possibly corrupt ops pointers or worse.
-			 */
-			printk(KERN_ERR TAG ": bus %d is already monitored\n",
-			    ubus->busnum);
-			spin_unlock_irqrestore(&mbus->lock, flags);
-			return;
+		if (mbus == &mon_bus0) {
+			list_for_each (p, &mon_buses) {
+				struct mon_bus *m1;
+				m1 = list_entry(p, struct mon_bus, bus_link);
+				m1->u_bus->monitored = 1;
+			}
+		} else {
+			mbus->u_bus->monitored = 1;
 		}
-		ubus->monitored = 1;
 	}
 	mbus->nreaders++;
 	list_add_tail(&r->r_link, &mbus->r_list);
@@ -80,77 +76,79 @@
 
 /*
  */
-static void mon_submit(struct usb_bus *ubus, struct urb *urb)
+static void mon_bus_submit(struct mon_bus *mbus, struct urb *urb)
 {
-	struct mon_bus *mbus;
 	unsigned long flags;
 	struct list_head *pos;
 	struct mon_reader *r;
 
-	mbus = ubus->mon_bus;
-	if (mbus == NULL)
-		goto out_unlocked;
-
 	spin_lock_irqsave(&mbus->lock, flags);
-	if (mbus->nreaders == 0)
-		goto out_locked;
-
 	mbus->cnt_events++;
 	list_for_each (pos, &mbus->r_list) {
 		r = list_entry(pos, struct mon_reader, r_link);
 		r->rnf_submit(r->r_data, urb);
 	}
-
 	spin_unlock_irqrestore(&mbus->lock, flags);
 	return;
+}
 
-out_locked:
-	spin_unlock_irqrestore(&mbus->lock, flags);
-out_unlocked:
-	return;
+static void mon_submit(struct usb_bus *ubus, struct urb *urb)
+{
+	struct mon_bus *mbus;
+
+	if ((mbus = ubus->mon_bus) != NULL)
+		mon_bus_submit(mbus, urb);
+	mon_bus_submit(&mon_bus0, urb);
 }
 
 /*
  */
-static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int error)
+static void mon_bus_submit_error(struct mon_bus *mbus, struct urb *urb, int error)
 {
-	struct mon_bus *mbus;
 	unsigned long flags;
 	struct list_head *pos;
 	struct mon_reader *r;
 
-	mbus = ubus->mon_bus;
-	if (mbus == NULL)
-		goto out_unlocked;
-
 	spin_lock_irqsave(&mbus->lock, flags);
-	if (mbus->nreaders == 0)
-		goto out_locked;
-
 	mbus->cnt_events++;
 	list_for_each (pos, &mbus->r_list) {
 		r = list_entry(pos, struct mon_reader, r_link);
 		r->rnf_error(r->r_data, urb, error);
 	}
-
 	spin_unlock_irqrestore(&mbus->lock, flags);
 	return;
+}
 
-out_locked:
-	spin_unlock_irqrestore(&mbus->lock, flags);
-out_unlocked:
-	return;
+static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int error)
+{
+	struct mon_bus *mbus;
+
+	if ((mbus = ubus->mon_bus) != NULL)
+		mon_bus_submit_error(mbus, urb, error);
+	mon_bus_submit_error(&mon_bus0, urb, error);
 }
 
 /*
  */
-static void mon_complete(struct usb_bus *ubus, struct urb *urb)
+static void mon_bus_complete(struct mon_bus *mbus, struct urb *urb)
 {
-	struct mon_bus *mbus;
 	unsigned long flags;
 	struct list_head *pos;
 	struct mon_reader *r;
 
+	spin_lock_irqsave(&mbus->lock, flags);
+	mbus->cnt_events++;
+	list_for_each (pos, &mbus->r_list) {
+		r = list_entry(pos, struct mon_reader, r_link);
+		r->rnf_complete(r->r_data, urb);
+	}
+	spin_unlock_irqrestore(&mbus->lock, flags);
+}
+
+static void mon_complete(struct usb_bus *ubus, struct urb *urb)
+{
+	struct mon_bus *mbus;
+
 	mbus = ubus->mon_bus;
 	if (mbus == NULL) {
 		/*
@@ -162,13 +160,8 @@
 		return;
 	}
 
-	spin_lock_irqsave(&mbus->lock, flags);
-	mbus->cnt_events++;
-	list_for_each (pos, &mbus->r_list) {
-		r = list_entry(pos, struct mon_reader, r_link);
-		r->rnf_complete(r->r_data, urb);
-	}
-	spin_unlock_irqrestore(&mbus->lock, flags);
+	mon_bus_complete(mbus, urb);
+	mon_bus_complete(&mon_bus0, urb);
 }
 
 /* int (*unlink_urb) (struct urb *urb, int status); */
@@ -179,14 +172,26 @@
 static void mon_stop(struct mon_bus *mbus)
 {
 	struct usb_bus *ubus = mbus->u_bus;
+	struct list_head *p;
 
-	/*
-	 * A stop can be called for a dissolved mon_bus in case of
-	 * a reader staying across an rmmod foo_hcd.
-	 */
-	if (ubus != NULL) {
-		ubus->monitored = 0;
-		mb();
+	if (mbus == &mon_bus0) {
+		list_for_each (p, &mon_buses) {
+			mbus = list_entry(p, struct mon_bus, bus_link);
+			/*
+			 * We do not change nreaders here, so rely on mon_lock.
+			 */
+			if (mbus->nreaders == 0 && (ubus = mbus->u_bus) != NULL)
+				ubus->monitored = 0;
+		}
+	} else {
+		/*
+		 * A stop can be called for a dissolved mon_bus in case of
+		 * a reader staying across an rmmod foo_hcd, so test ->u_bus.
+		 */
+		if (mon_bus0.nreaders == 0 && (ubus = mbus->u_bus) != NULL) {
+			ubus->monitored = 0;
+			mb();
+		}
 	}
 }
 
@@ -199,6 +204,10 @@
 static void mon_bus_add(struct usb_bus *ubus)
 {
 	mon_bus_init(ubus);
+	mutex_lock(&mon_lock);
+	if (mon_bus0.nreaders != 0)
+		ubus->monitored = 1;
+	mutex_unlock(&mon_lock);
 }
 
 /*
@@ -250,12 +259,7 @@
 static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus)
 {
 
-	/*
-	 * Never happens, but...
-	 */
 	if (ubus->monitored) {
-		printk(KERN_ERR TAG ": bus %d is dissolved while monitored\n",
-		    ubus->busnum);
 		ubus->monitored = 0;
 		mb();
 	}
@@ -263,6 +267,8 @@
 	ubus->mon_bus = NULL;
 	mbus->u_bus = NULL;
 	mb();
+
+	/* We want synchronize_irq() here, but that needs an argument. */
 }
 
 /*
@@ -295,9 +301,8 @@
 	 */
 	mbus->u_bus = ubus;
 	ubus->mon_bus = mbus;
-	mbus->uses_dma = ubus->uses_dma;
 
-	mbus->text_inited = mon_text_add(mbus, ubus);
+	mbus->text_inited = mon_text_add(mbus, ubus->busnum);
 	// mon_bin_add(...)
 
 	mutex_lock(&mon_lock);
@@ -309,6 +314,18 @@
 	return;
 }
 
+static void mon_bus0_init(void)
+{
+	struct mon_bus *mbus = &mon_bus0;
+
+	kref_init(&mbus->ref);
+	spin_lock_init(&mbus->lock);
+	INIT_LIST_HEAD(&mbus->r_list);
+
+	mbus->text_inited = mon_text_add(mbus, 0);
+	// mbus->bin_inited = mon_bin_add(mbus, 0);
+}
+
 /*
  * Search a USB bus by number. Notice that USB bus numbers start from one,
  * which we may later use to identify "all" with zero.
@@ -322,6 +339,9 @@
 	struct list_head *p;
 	struct mon_bus *mbus;
 
+	if (num == 0) {
+		return &mon_bus0;
+	}
 	list_for_each (p, &mon_buses) {
 		mbus = list_entry(p, struct mon_bus, bus_link);
 		if (mbus->u_bus->busnum == num) {
@@ -341,6 +361,8 @@
 	if ((rc = mon_bin_init()) != 0)
 		goto err_bin;
 
+	mon_bus0_init();
+
 	if (usb_mon_register(&mon_ops_0) != 0) {
 		printk(KERN_NOTICE TAG ": unable to register with the core\n");
 		rc = -ENODEV;
@@ -374,6 +396,7 @@
 	usb_mon_deregister();
 
 	mutex_lock(&mon_lock);
+
 	while (!list_empty(&mon_buses)) {
 		p = mon_buses.next;
 		mbus = list_entry(p, struct mon_bus, bus_link);
@@ -397,6 +420,11 @@
 		mon_dissolve(mbus, mbus->u_bus);
 		kref_put(&mbus->ref, mon_bus_drop);
 	}
+
+	mbus = &mon_bus0;
+	if (mbus->text_inited)
+		mon_text_del(mbus);
+
 	mutex_unlock(&mon_lock);
 
 	mon_text_exit();
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index 494ee3b..ec0cc51 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -31,9 +31,21 @@
  * to a local DoS. But we have to keep to root in order to prevent
  * password sniffing from HID devices.
  */
-#define EVENT_MAX  (2*PAGE_SIZE / sizeof(struct mon_event_text))
+#define EVENT_MAX  (4*PAGE_SIZE / sizeof(struct mon_event_text))
 
-#define PRINTF_DFL  160
+/*
+ * Potentially unlimited number; we limit it for similar allocations.
+ * The usbfs limits this to 128, but we're not quite as generous.
+ */
+#define ISODESC_MAX   5
+
+#define PRINTF_DFL  250   /* with 5 ISOs segs */
+
+struct mon_iso_desc {
+	int status;
+	unsigned int offset;
+	unsigned int length;	/* Unsigned here, signed in URB. Historic. */
+};
 
 struct mon_event_text {
 	struct list_head e_link;
@@ -41,10 +53,16 @@
 	unsigned int pipe;	/* Pipe */
 	unsigned long id;	/* From pointer, most of the time */
 	unsigned int tstamp;
+	int busnum;
 	int length;		/* Depends on type: xfer length or act length */
 	int status;
+	int interval;
+	int start_frame;
+	int error_count;
 	char setup_flag;
 	char data_flag;
+	int numdesc;		/* Full number */
+	struct mon_iso_desc isodesc[ISODESC_MAX];
 	unsigned char setup[SETUP_MAX];
 	unsigned char data[DATA_MAX];
 };
@@ -68,6 +86,28 @@
 
 static void mon_text_ctor(void *, struct kmem_cache *, unsigned long);
 
+struct mon_text_ptr {
+	int cnt, limit;
+	char *pbuf;
+};
+
+static struct mon_event_text *
+    mon_text_read_wait(struct mon_reader_text *rp, struct file *file);
+static void mon_text_read_head_t(struct mon_reader_text *rp,
+	struct mon_text_ptr *p, const struct mon_event_text *ep);
+static void mon_text_read_head_u(struct mon_reader_text *rp,
+	struct mon_text_ptr *p, const struct mon_event_text *ep);
+static void mon_text_read_statset(struct mon_reader_text *rp,
+	struct mon_text_ptr *p, const struct mon_event_text *ep);
+static void mon_text_read_intstat(struct mon_reader_text *rp,
+	struct mon_text_ptr *p, const struct mon_event_text *ep);
+static void mon_text_read_isostat(struct mon_reader_text *rp,
+	struct mon_text_ptr *p, const struct mon_event_text *ep);
+static void mon_text_read_isodesc(struct mon_reader_text *rp,
+	struct mon_text_ptr *p, const struct mon_event_text *ep);
+static void mon_text_read_data(struct mon_reader_text *rp,
+    struct mon_text_ptr *p, const struct mon_event_text *ep);
+
 /*
  * mon_text_submit
  * mon_text_complete
@@ -84,8 +124,10 @@
 	if (!usb_pipecontrol(urb->pipe) || ev_type != 'S')
 		return '-';
 
-	if (mbus->uses_dma && (urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
+	if (urb->dev->bus->uses_dma &&
+	    (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) {
 		return mon_dmapeek(ep->setup, urb->setup_dma, SETUP_MAX);
+	}
 	if (urb->setup_packet == NULL)
 		return 'Z';	/* '0' would be not as pretty. */
 
@@ -104,10 +146,10 @@
 		len = DATA_MAX;
 
 	if (usb_pipein(pipe)) {
-		if (ev_type == 'S')
+		if (ev_type != 'C')
 			return '<';
 	} else {
-		if (ev_type == 'C')
+		if (ev_type != 'S')
 			return '>';
 	}
 
@@ -120,8 +162,10 @@
 	 * contain non-NULL garbage in case the upper level promised to
 	 * set DMA for the HCD.
 	 */
-	if (mbus->uses_dma && (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
+	if (urb->dev->bus->uses_dma &&
+	    (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
 		return mon_dmapeek(ep->data, urb->transfer_dma, len);
+	}
 
 	if (urb->transfer_buffer == NULL)
 		return 'Z';	/* '0' would be not as pretty. */
@@ -146,6 +190,9 @@
 {
 	struct mon_event_text *ep;
 	unsigned int stamp;
+	struct usb_iso_packet_descriptor *fp;
+	struct mon_iso_desc *dp;
+	int i, ndesc;
 
 	stamp = mon_get_timestamp();
 
@@ -158,12 +205,36 @@
 	ep->type = ev_type;
 	ep->pipe = urb->pipe;
 	ep->id = (unsigned long) urb;
+	ep->busnum = urb->dev->bus->busnum;
 	ep->tstamp = stamp;
 	ep->length = (ev_type == 'S') ?
 	    urb->transfer_buffer_length : urb->actual_length;
 	/* Collecting status makes debugging sense for submits, too */
 	ep->status = urb->status;
 
+	if (usb_pipeint(urb->pipe)) {
+		ep->interval = urb->interval;
+	} else if (usb_pipeisoc(urb->pipe)) {
+		ep->interval = urb->interval;
+		ep->start_frame = urb->start_frame;
+		ep->error_count = urb->error_count;
+	}
+	ep->numdesc = urb->number_of_packets;
+	if (usb_pipeisoc(urb->pipe) && urb->number_of_packets > 0) {
+		if ((ndesc = urb->number_of_packets) > ISODESC_MAX)
+			ndesc = ISODESC_MAX;
+		fp = urb->iso_frame_desc;
+		dp = ep->isodesc;
+		for (i = 0; i < ndesc; i++) {
+			dp->status = fp->status;
+			dp->offset = fp->offset;
+			dp->length = (ev_type == 'S') ?
+			    fp->length : fp->actual_length;
+			fp++;
+			dp++;
+		}
+	}
+
 	ep->setup_flag = mon_text_get_setup(ep, urb, ev_type, rp->r.m_bus);
 	ep->data_flag = mon_text_get_data(ep, urb, ep->length, ev_type,
 			rp->r.m_bus);
@@ -199,6 +270,7 @@
 	ep->type = 'E';
 	ep->pipe = urb->pipe;
 	ep->id = (unsigned long) urb;
+	ep->busnum = 0;
 	ep->tstamp = 0;
 	ep->length = 0;
 	ep->status = error;
@@ -237,13 +309,11 @@
 static int mon_text_open(struct inode *inode, struct file *file)
 {
 	struct mon_bus *mbus;
-	struct usb_bus *ubus;
 	struct mon_reader_text *rp;
 	int rc;
 
 	mutex_lock(&mon_lock);
 	mbus = inode->i_private;
-	ubus = mbus->u_bus;
 
 	rp = kzalloc(sizeof(struct mon_reader_text), GFP_KERNEL);
 	if (rp == NULL) {
@@ -267,8 +337,7 @@
 	rp->r.rnf_error = mon_text_error;
 	rp->r.rnf_complete = mon_text_complete;
 
-	snprintf(rp->slab_name, SLAB_NAME_SZ, "mon%dt_%lx", ubus->busnum,
-	    (long)rp);
+	snprintf(rp->slab_name, SLAB_NAME_SZ, "mon_text_%p", rp);
 	rp->e_slab = kmem_cache_create(rp->slab_name,
 	    sizeof(struct mon_event_text), sizeof(long), 0,
 	    mon_text_ctor, NULL);
@@ -300,17 +369,75 @@
  *   dd if=/dbg/usbmon/0t bs=10
  * Also, we do not allow seeks and do not bother advancing the offset.
  */
-static ssize_t mon_text_read(struct file *file, char __user *buf,
+static ssize_t mon_text_read_t(struct file *file, char __user *buf,
 				size_t nbytes, loff_t *ppos)
 {
 	struct mon_reader_text *rp = file->private_data;
+	struct mon_event_text *ep;
+	struct mon_text_ptr ptr;
+
+	if (IS_ERR(ep = mon_text_read_wait(rp, file)))
+		return PTR_ERR(ep);
+	mutex_lock(&rp->printf_lock);
+	ptr.cnt = 0;
+	ptr.pbuf = rp->printf_buf;
+	ptr.limit = rp->printf_size;
+
+	mon_text_read_head_t(rp, &ptr, ep);
+	mon_text_read_statset(rp, &ptr, ep);
+	ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
+	    " %d", ep->length);
+	mon_text_read_data(rp, &ptr, ep);
+
+	if (copy_to_user(buf, rp->printf_buf, ptr.cnt))
+		ptr.cnt = -EFAULT;
+	mutex_unlock(&rp->printf_lock);
+	kmem_cache_free(rp->e_slab, ep);
+	return ptr.cnt;
+}
+
+static ssize_t mon_text_read_u(struct file *file, char __user *buf,
+				size_t nbytes, loff_t *ppos)
+{
+	struct mon_reader_text *rp = file->private_data;
+	struct mon_event_text *ep;
+	struct mon_text_ptr ptr;
+
+	if (IS_ERR(ep = mon_text_read_wait(rp, file)))
+		return PTR_ERR(ep);
+	mutex_lock(&rp->printf_lock);
+	ptr.cnt = 0;
+	ptr.pbuf = rp->printf_buf;
+	ptr.limit = rp->printf_size;
+
+	mon_text_read_head_u(rp, &ptr, ep);
+	if (ep->type == 'E') {
+		mon_text_read_statset(rp, &ptr, ep);
+	} else if (usb_pipeisoc(ep->pipe)) {
+		mon_text_read_isostat(rp, &ptr, ep);
+		mon_text_read_isodesc(rp, &ptr, ep);
+	} else if (usb_pipeint(ep->pipe)) {
+		mon_text_read_intstat(rp, &ptr, ep);
+	} else {
+		mon_text_read_statset(rp, &ptr, ep);
+	}
+	ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
+	    " %d", ep->length);
+	mon_text_read_data(rp, &ptr, ep);
+
+	if (copy_to_user(buf, rp->printf_buf, ptr.cnt))
+		ptr.cnt = -EFAULT;
+	mutex_unlock(&rp->printf_lock);
+	kmem_cache_free(rp->e_slab, ep);
+	return ptr.cnt;
+}
+
+static struct mon_event_text *mon_text_read_wait(struct mon_reader_text *rp,
+    struct file *file)
+{
 	struct mon_bus *mbus = rp->r.m_bus;
 	DECLARE_WAITQUEUE(waita, current);
 	struct mon_event_text *ep;
-	int cnt, limit;
-	char *pbuf;
-	char udir, utype;
-	int data_len, i;
 
 	add_wait_queue(&rp->wait, &waita);
 	set_current_state(TASK_INTERRUPTIBLE);
@@ -318,7 +445,7 @@
 		if (file->f_flags & O_NONBLOCK) {
 			set_current_state(TASK_RUNNING);
 			remove_wait_queue(&rp->wait, &waita);
-			return -EWOULDBLOCK;	/* Same as EAGAIN in Linux */
+			return ERR_PTR(-EWOULDBLOCK);
 		}
 		/*
 		 * We do not count nwaiters, because ->release is supposed
@@ -327,17 +454,19 @@
 		schedule();
 		if (signal_pending(current)) {
 			remove_wait_queue(&rp->wait, &waita);
-			return -EINTR;
+			return ERR_PTR(-EINTR);
 		}
 		set_current_state(TASK_INTERRUPTIBLE);
 	}
 	set_current_state(TASK_RUNNING);
 	remove_wait_queue(&rp->wait, &waita);
+	return ep;
+}
 
-	mutex_lock(&rp->printf_lock);
-	cnt = 0;
-	pbuf = rp->printf_buf;
-	limit = rp->printf_size;
+static void mon_text_read_head_t(struct mon_reader_text *rp,
+	struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
+	char udir, utype;
 
 	udir = usb_pipein(ep->pipe) ? 'i' : 'o';
 	switch (usb_pipetype(ep->pipe)) {
@@ -346,13 +475,38 @@
 	case PIPE_CONTROL:	utype = 'C'; break;
 	default: /* PIPE_BULK */  utype = 'B';
 	}
-	cnt += snprintf(pbuf + cnt, limit - cnt,
+	p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
 	    "%lx %u %c %c%c:%03u:%02u",
 	    ep->id, ep->tstamp, ep->type,
-	    utype, udir, usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe));
+	    utype, udir,
+	    usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe));
+}
+
+static void mon_text_read_head_u(struct mon_reader_text *rp,
+	struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
+	char udir, utype;
+
+	udir = usb_pipein(ep->pipe) ? 'i' : 'o';
+	switch (usb_pipetype(ep->pipe)) {
+	case PIPE_ISOCHRONOUS:	utype = 'Z'; break;
+	case PIPE_INTERRUPT:	utype = 'I'; break;
+	case PIPE_CONTROL:	utype = 'C'; break;
+	default: /* PIPE_BULK */  utype = 'B';
+	}
+	p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+	    "%lx %u %c %c%c:%d:%03u:%u",
+	    ep->id, ep->tstamp, ep->type,
+	    utype, udir,
+	    ep->busnum, usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe));
+}
+
+static void mon_text_read_statset(struct mon_reader_text *rp,
+	struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
 
 	if (ep->setup_flag == 0) {   /* Setup packet is present and captured */
-		cnt += snprintf(pbuf + cnt, limit - cnt,
+		p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
 		    " s %02x %02x %04x %04x %04x",
 		    ep->setup[0],
 		    ep->setup[1],
@@ -360,40 +514,86 @@
 		    (ep->setup[5] << 8) | ep->setup[4],
 		    (ep->setup[7] << 8) | ep->setup[6]);
 	} else if (ep->setup_flag != '-') { /* Unable to capture setup packet */
-		cnt += snprintf(pbuf + cnt, limit - cnt,
+		p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
 		    " %c __ __ ____ ____ ____", ep->setup_flag);
 	} else {                     /* No setup for this kind of URB */
-		cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->status);
+		p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+		    " %d", ep->status);
 	}
-	cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->length);
+}
+
+static void mon_text_read_intstat(struct mon_reader_text *rp,
+	struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
+	p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+	    " %d:%d", ep->status, ep->interval);
+}
+
+static void mon_text_read_isostat(struct mon_reader_text *rp,
+	struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
+	if (ep->type == 'S') {
+		p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+		    " %d:%d:%d", ep->status, ep->interval, ep->start_frame);
+	} else {
+		p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+		    " %d:%d:%d:%d",
+		    ep->status, ep->interval, ep->start_frame, ep->error_count);
+	}
+}
+
+static void mon_text_read_isodesc(struct mon_reader_text *rp,
+	struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
+	int ndesc;	/* Display this many */
+	int i;
+	const struct mon_iso_desc *dp;
+
+	p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+	    " %d", ep->numdesc);
+	ndesc = ep->numdesc;
+	if (ndesc > ISODESC_MAX)
+		ndesc = ISODESC_MAX;
+	if (ndesc < 0)
+		ndesc = 0;
+	dp = ep->isodesc;
+	for (i = 0; i < ndesc; i++) {
+		p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+		    " %d:%u:%u", dp->status, dp->offset, dp->length);
+		dp++;
+	}
+}
+
+static void mon_text_read_data(struct mon_reader_text *rp,
+    struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
+	int data_len, i;
 
 	if ((data_len = ep->length) > 0) {
 		if (ep->data_flag == 0) {
-			cnt += snprintf(pbuf + cnt, limit - cnt, " =");
+			p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+			    " =");
 			if (data_len >= DATA_MAX)
 				data_len = DATA_MAX;
 			for (i = 0; i < data_len; i++) {
 				if (i % 4 == 0) {
-					cnt += snprintf(pbuf + cnt, limit - cnt,
+					p->cnt += snprintf(p->pbuf + p->cnt,
+					    p->limit - p->cnt,
 					    " ");
 				}
-				cnt += snprintf(pbuf + cnt, limit - cnt,
+				p->cnt += snprintf(p->pbuf + p->cnt,
+				    p->limit - p->cnt,
 				    "%02x", ep->data[i]);
 			}
-			cnt += snprintf(pbuf + cnt, limit - cnt, "\n");
+			p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+			    "\n");
 		} else {
-			cnt += snprintf(pbuf + cnt, limit - cnt,
+			p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
 			    " %c\n", ep->data_flag);
 		}
 	} else {
-		cnt += snprintf(pbuf + cnt, limit - cnt, "\n");
+		p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, "\n");
 	}
-
-	if (copy_to_user(buf, rp->printf_buf, cnt))
-		cnt = -EFAULT;
-	mutex_unlock(&rp->printf_lock);
-	kmem_cache_free(rp->e_slab, ep);
-	return cnt;
 }
 
 static int mon_text_release(struct inode *inode, struct file *file)
@@ -439,34 +639,46 @@
 	return 0;
 }
 
-static const struct file_operations mon_fops_text = {
+static const struct file_operations mon_fops_text_t = {
 	.owner =	THIS_MODULE,
 	.open =		mon_text_open,
 	.llseek =	no_llseek,
-	.read =		mon_text_read,
-	/* .write =	mon_text_write, */
-	/* .poll =		mon_text_poll, */
-	/* .ioctl =	mon_text_ioctl, */
+	.read =		mon_text_read_t,
 	.release =	mon_text_release,
 };
 
-int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus)
+static const struct file_operations mon_fops_text_u = {
+	.owner =	THIS_MODULE,
+	.open =		mon_text_open,
+	.llseek =	no_llseek,
+	.read =		mon_text_read_u,
+	.release =	mon_text_release,
+};
+
+int mon_text_add(struct mon_bus *mbus, int busnum)
 {
 	struct dentry *d;
 	enum { NAMESZ = 10 };
 	char name[NAMESZ];
 	int rc;
 
-	rc = snprintf(name, NAMESZ, "%dt", ubus->busnum);
+	rc = snprintf(name, NAMESZ, "%dt", busnum);
 	if (rc <= 0 || rc >= NAMESZ)
 		goto err_print_t;
-	d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text);
+	d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_t);
 	if (d == NULL)
 		goto err_create_t;
 	mbus->dent_t = d;
 
-	/* XXX The stats do not belong to here (text API), but oh well... */
-	rc = snprintf(name, NAMESZ, "%ds", ubus->busnum);
+	rc = snprintf(name, NAMESZ, "%du", busnum);
+	if (rc <= 0 || rc >= NAMESZ)
+		goto err_print_u;
+	d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_u);
+	if (d == NULL)
+		goto err_create_u;
+	mbus->dent_u = d;
+
+	rc = snprintf(name, NAMESZ, "%ds", busnum);
 	if (rc <= 0 || rc >= NAMESZ)
 		goto err_print_s;
 	d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_stat);
@@ -478,6 +690,10 @@
 
 err_create_s:
 err_print_s:
+	debugfs_remove(mbus->dent_u);
+	mbus->dent_u = NULL;
+err_create_u:
+err_print_u:
 	debugfs_remove(mbus->dent_t);
 	mbus->dent_t = NULL;
 err_create_t:
@@ -487,6 +703,7 @@
 
 void mon_text_del(struct mon_bus *mbus)
 {
+	debugfs_remove(mbus->dent_u);
 	debugfs_remove(mbus->dent_t);
 	debugfs_remove(mbus->dent_s);
 }
diff --git a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h
index efdfd89..13d6325 100644
--- a/drivers/usb/mon/usb_mon.h
+++ b/drivers/usb/mon/usb_mon.h
@@ -22,7 +22,7 @@
 	int text_inited;
 	struct dentry *dent_s;		/* Debugging file */
 	struct dentry *dent_t;		/* Text interface file */
-	int uses_dma;
+	struct dentry *dent_u;		/* Second text interface file */
 
 	/* Ref */
 	int nreaders;			/* Under mon_lock AND mbus->lock */
@@ -52,7 +52,7 @@
 
 struct mon_bus *mon_bus_lookup(unsigned int num);
 
-int /*bool*/ mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus);
+int /*bool*/ mon_text_add(struct mon_bus *mbus, int busnum);
 void mon_text_del(struct mon_bus *mbus);
 // void mon_bin_add(struct mon_bus *);
 
@@ -81,4 +81,6 @@
 
 extern const struct file_operations mon_fops_stat;
 
+extern struct mon_bus mon_bus0;		/* Only for redundant checks */
+
 #endif /* __USB_MON_H */
diff --git a/drivers/usb/net/asix.c b/drivers/usb/net/asix.c
index 5808ea0..d5ef97b 100644
--- a/drivers/usb/net/asix.c
+++ b/drivers/usb/net/asix.c
@@ -298,7 +298,7 @@
 		if (ax_skb) {
 			ax_skb->len = size;
 			ax_skb->data = packet;
-			ax_skb->tail = packet + size;
+			skb_set_tail_pointer(ax_skb, size);
 			usbnet_skb_return(dev, ax_skb);
 		} else {
 			return 0;
@@ -338,7 +338,7 @@
 	    && ((headroom + tailroom) >= (4 + padlen))) {
 		if ((headroom < 4) || (tailroom < padlen)) {
 			skb->data = memmove(skb->head + 4, skb->data, skb->len);
-			skb->tail = skb->data + skb->len;
+			skb_set_tail_pointer(skb, skb->len);
 		}
 	} else {
 		struct sk_buff *skb2;
@@ -352,11 +352,11 @@
 	skb_push(skb, 4);
 	packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4);
 	cpu_to_le32s(&packet_len);
-	memcpy(skb->data, &packet_len, sizeof(packet_len));
+	skb_copy_to_linear_data(skb, &packet_len, sizeof(packet_len));
 
 	if ((skb->len % 512) == 0) {
 		cpu_to_le32s(&padbytes);
-		memcpy( skb->tail, &padbytes, sizeof(padbytes));
+		memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes));
 		skb_put(skb, sizeof(padbytes));
 	}
 	return skb;
diff --git a/drivers/usb/net/catc.c b/drivers/usb/net/catc.c
index 4852012..86e90c5 100644
--- a/drivers/usb/net/catc.c
+++ b/drivers/usb/net/catc.c
@@ -255,7 +255,6 @@
 		if (!(skb = dev_alloc_skb(pkt_len)))
 			return;
 
-		skb->dev = catc->netdev;
 		eth_copy_and_sum(skb, pkt_start + pkt_offset, pkt_len, 0);
 		skb_put(skb, pkt_len);
 
@@ -356,7 +355,7 @@
  * Transmit routines.
  */
 
-static void catc_tx_run(struct catc *catc)
+static int catc_tx_run(struct catc *catc)
 {
 	int status;
 
@@ -374,12 +373,14 @@
 	catc->tx_ptr = 0;
 
 	catc->netdev->trans_start = jiffies;
+	return status;
 }
 
 static void catc_tx_done(struct urb *urb)
 {
 	struct catc *catc = urb->context;
 	unsigned long flags;
+	int r;
 
 	if (urb->status == -ECONNRESET) {
 		dbg("Tx Reset.");
@@ -398,10 +399,13 @@
 
 	spin_lock_irqsave(&catc->tx_lock, flags);
 
-	if (catc->tx_ptr)
-		catc_tx_run(catc);
-	else
+	if (catc->tx_ptr) {
+		r = catc_tx_run(catc);
+		if (unlikely(r < 0))
+			clear_bit(TX_RUNNING, &catc->flags);
+	} else {
 		clear_bit(TX_RUNNING, &catc->flags);
+	}
 
 	netif_wake_queue(catc->netdev);
 
@@ -412,6 +416,7 @@
 {
 	struct catc *catc = netdev_priv(netdev);
 	unsigned long flags;
+	int r = 0;
 	char *tx_buf;
 
 	spin_lock_irqsave(&catc->tx_lock, flags);
@@ -419,11 +424,14 @@
 	catc->tx_ptr = (((catc->tx_ptr - 1) >> 6) + 1) << 6;
 	tx_buf = catc->tx_buf[catc->tx_idx] + catc->tx_ptr;
 	*((u16*)tx_buf) = (catc->is_f5u011) ? cpu_to_be16((u16)skb->len) : cpu_to_le16((u16)skb->len);
-	memcpy(tx_buf + 2, skb->data, skb->len);
+	skb_copy_from_linear_data(skb, tx_buf + 2, skb->len);
 	catc->tx_ptr += skb->len + 2;
 
-	if (!test_and_set_bit(TX_RUNNING, &catc->flags))
-		catc_tx_run(catc);
+	if (!test_and_set_bit(TX_RUNNING, &catc->flags)) {
+		r = catc_tx_run(catc);
+		if (r < 0)
+			clear_bit(TX_RUNNING, &catc->flags);
+	}
 
 	if ((catc->is_f5u011 && catc->tx_ptr)
 	     || (catc->tx_ptr >= ((TX_MAX_BURST - 1) * (PKT_SZ + 2))))
@@ -431,8 +439,10 @@
 
 	spin_unlock_irqrestore(&catc->tx_lock, flags);
 
-	catc->stats.tx_bytes += skb->len;
-	catc->stats.tx_packets++;
+	if (r >= 0) {
+		catc->stats.tx_bytes += skb->len;
+		catc->stats.tx_packets++;
+	}
 
 	dev_kfree_skb(skb);
 
diff --git a/drivers/usb/net/dm9601.c b/drivers/usb/net/dm9601.c
index 5130cc7..a6763860 100644
--- a/drivers/usb/net/dm9601.c
+++ b/drivers/usb/net/dm9601.c
@@ -12,6 +12,7 @@
 
 #include <linux/module.h>
 #include <linux/sched.h>
+#include <linux/stddef.h>
 #include <linux/init.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -85,7 +86,7 @@
 			       usb_sndctrlpipe(dev->udev, 0),
 			       DM_WRITE_REG,
 			       USB_DIR_OUT | USB_TYPE_VENDOR |USB_RECIP_DEVICE,
-			       value, reg, 0, 0, USB_CTRL_SET_TIMEOUT);
+			       value, reg, NULL, 0, USB_CTRL_SET_TIMEOUT);
 }
 
 static void dm_write_async_callback(struct urb *urb)
@@ -171,7 +172,7 @@
 
 	usb_fill_control_urb(urb, dev->udev,
 			     usb_sndctrlpipe(dev->udev, 0),
-			     (void *)req, 0, 0, dm_write_async_callback, req);
+			     (void *)req, NULL, 0, dm_write_async_callback, req);
 
 	status = usb_submit_urb(urb, GFP_ATOMIC);
 	if (status < 0) {
diff --git a/drivers/usb/net/gl620a.c b/drivers/usb/net/gl620a.c
index d257a8e..031cf5c 100644
--- a/drivers/usb/net/gl620a.c
+++ b/drivers/usb/net/gl620a.c
@@ -157,7 +157,7 @@
 		if ((headroom < (4 + 4*1)) || (tailroom < padlen)) {
 			skb->data = memmove(skb->head + (4 + 4*1),
 					     skb->data, skb->len);
-			skb->tail = skb->data + skb->len;
+			skb_set_tail_pointer(skb, skb->len);
 		}
 	} else {
 		struct sk_buff	*skb2;
diff --git a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c
index de95268..a0cc05d 100644
--- a/drivers/usb/net/kaweth.c
+++ b/drivers/usb/net/kaweth.c
@@ -636,8 +636,6 @@
 
 		skb_reserve(skb, 2);    /* Align IP on 16 byte boundaries */
 
-		skb->dev = net;
-
 		eth_copy_and_sum(skb, kaweth->rx_buf + 2, pkt_len, 0);
 
 		skb_put(skb, pkt_len);
diff --git a/drivers/usb/net/net1080.c b/drivers/usb/net/net1080.c
index ccebfde..19bf8da 100644
--- a/drivers/usb/net/net1080.c
+++ b/drivers/usb/net/net1080.c
@@ -520,7 +520,7 @@
 			skb->data = memmove(skb->head
 						+ sizeof (struct nc_header),
 					    skb->data, skb->len);
-			skb->tail = skb->data + len;
+			skb_set_tail_pointer(skb, len);
 			goto encapsulate;
 		}
 	}
diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c
index 6d12961..a05fd97 100644
--- a/drivers/usb/net/pegasus.c
+++ b/drivers/usb/net/pegasus.c
@@ -575,7 +575,6 @@
 		 */
 		if (pegasus->rx_pool[i] == NULL)
 			return;
-		pegasus->rx_pool[i]->dev = pegasus->net;
 		skb_reserve(pegasus->rx_pool[i], 2);
 	}
 }
@@ -848,16 +847,6 @@
 		 * d[0].NO_CARRIER kicks in only with failed TX.
 		 * ... so monitoring with MII may be safest.
 		 */
-		if (pegasus->features & TRUST_LINK_STATUS) {
-			if (d[5] & LINK_STATUS)
-				netif_carrier_on(net);
-			else
-				netif_carrier_off(net);
-		} else {
-			/* Never set carrier _on_ based on ! NO_CARRIER */
-			if (d[0] & NO_CARRIER)
-				netif_carrier_off(net);	
-		}
 
 		/* bytes 3-4 == rx_lostpkt, reg 2E/2F */
 		pegasus->stats.rx_missed_errors += ((d[3] & 0x7f) << 8) | d[4];
@@ -890,7 +879,7 @@
 	netif_stop_queue(net);
 
 	((__le16 *) pegasus->tx_buff)[0] = cpu_to_le16(l16);
-	memcpy(pegasus->tx_buff + 2, skb->data, skb->len);
+	skb_copy_from_linear_data(skb, pegasus->tx_buff + 2, skb->len);
 	usb_fill_bulk_urb(pegasus->tx_urb, pegasus->usb,
 			  usb_sndbulkpipe(pegasus->usb, 2),
 			  pegasus->tx_buff, count,
@@ -1415,8 +1404,10 @@
 	unlink_all_urbs(pegasus);
 	free_all_urbs(pegasus);
 	free_skb_pool(pegasus);
-	if (pegasus->rx_skb)
+	if (pegasus->rx_skb != NULL) {
 		dev_kfree_skb(pegasus->rx_skb);
+		pegasus->rx_skb = NULL;
+	}
 	free_netdev(pegasus->net);
 }
 
diff --git a/drivers/usb/net/pegasus.h b/drivers/usb/net/pegasus.h
index c7aadb4..c746782 100644
--- a/drivers/usb/net/pegasus.h
+++ b/drivers/usb/net/pegasus.h
@@ -11,7 +11,6 @@
 
 #define	PEGASUS_II		0x80000000
 #define	HAS_HOME_PNA		0x40000000
-#define	TRUST_LINK_STATUS	0x20000000
 
 #define	PEGASUS_MTU		1536
 #define	RX_SKBS			4
@@ -204,7 +203,7 @@
 PEGASUS_DEV( "Allied Telesyn Int. AT-USB100", VENDOR_ALLIEDTEL, 0xb100,
 		DEFAULT_GPIO_RESET | PEGASUS_II )
 PEGASUS_DEV( "Belkin F5D5050 USB Ethernet", VENDOR_BELKIN, 0x0121,
-		DEFAULT_GPIO_RESET | PEGASUS_II | TRUST_LINK_STATUS )
+		DEFAULT_GPIO_RESET | PEGASUS_II )
 PEGASUS_DEV( "Billionton USB-100", VENDOR_BILLIONTON, 0x0986,
 		DEFAULT_GPIO_RESET )
 PEGASUS_DEV( "Billionton USBLP-100", VENDOR_BILLIONTON, 0x0987,
diff --git a/drivers/usb/net/rndis_host.c b/drivers/usb/net/rndis_host.c
index 39a21c7..980e4aa 100644
--- a/drivers/usb/net/rndis_host.c
+++ b/drivers/usb/net/rndis_host.c
@@ -253,6 +253,7 @@
  * of that mess as possible.
  */
 #define OID_802_3_PERMANENT_ADDRESS	ccpu2(0x01010101)
+#define OID_GEN_MAXIMUM_FRAME_SIZE	ccpu2(0x00010106)
 #define OID_GEN_CURRENT_PACKET_FILTER	ccpu2(0x0001010e)
 
 /*
@@ -349,7 +350,7 @@
 			case RNDIS_MSG_INDICATE: {	/* fault */
 				// struct rndis_indicate *msg = (void *)buf;
 				dev_info(&info->control->dev,
-					 "rndis fault indication\n");
+					"rndis fault indication\n");
 				}
 				break;
 			case RNDIS_MSG_KEEPALIVE: {	/* ping */
@@ -387,6 +388,71 @@
 	return -ETIMEDOUT;
 }
 
+/*
+ * rndis_query:
+ *
+ * Performs a query for @oid along with 0 or more bytes of payload as
+ * specified by @in_len. If @reply_len is not set to -1 then the reply
+ * length is checked against this value, resulting in an error if it
+ * doesn't match.
+ *
+ * NOTE: Adding a payload exactly or greater than the size of the expected
+ * response payload is an evident requirement MSFT added for ActiveSync.
+ *
+ * The only exception is for OIDs that return a variably sized response,
+ * in which case no payload should be added.  This undocumented (and
+ * nonsensical!) issue was found by sniffing protocol requests from the
+ * ActiveSync 4.1 Windows driver.
+ */
+static int rndis_query(struct usbnet *dev, struct usb_interface *intf,
+		void *buf, u32 oid, u32 in_len,
+		void **reply, int *reply_len)
+{
+	int retval;
+	union {
+		void			*buf;
+		struct rndis_msg_hdr	*header;
+		struct rndis_query	*get;
+		struct rndis_query_c	*get_c;
+	} u;
+	u32 off, len;
+
+	u.buf = buf;
+
+	memset(u.get, 0, sizeof *u.get + in_len);
+	u.get->msg_type = RNDIS_MSG_QUERY;
+	u.get->msg_len = cpu_to_le32(sizeof *u.get + in_len);
+	u.get->oid = oid;
+	u.get->len = cpu_to_le32(in_len);
+	u.get->offset = ccpu2(20);
+
+	retval = rndis_command(dev, u.header);
+	if (unlikely(retval < 0)) {
+		dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed, %d\n",
+				oid, retval);
+		return retval;
+	}
+
+	off = le32_to_cpu(u.get_c->offset);
+	len = le32_to_cpu(u.get_c->len);
+	if (unlikely((8 + off + len) > CONTROL_BUFFER_SIZE))
+		goto response_error;
+
+	if (*reply_len != -1 && len != *reply_len)
+		goto response_error;
+
+	*reply = (unsigned char *) &u.get_c->request_id + off;
+	*reply_len = len;
+
+	return retval;
+
+response_error:
+	dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) "
+			"invalid response - off %d len %d\n",
+		oid, off, len);
+	return -EDOM;
+}
+
 static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
 {
 	int			retval;
@@ -403,6 +469,8 @@
 		struct rndis_set_c	*set_c;
 	} u;
 	u32			tmp;
+	int			reply_len;
+	unsigned char		*bp;
 
 	/* we can't rely on i/o from stack working, or stack allocation */
 	u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
@@ -421,6 +489,12 @@
 	 * TX we'll stick to one Ethernet packet plus RNDIS framing.
 	 * For RX we handle drivers that zero-pad to end-of-packet.
 	 * Don't let userspace change these settings.
+	 *
+	 * NOTE: there still seems to be wierdness here, as if we need
+	 * to do some more things to make sure WinCE targets accept this.
+	 * They default to jumbograms of 8KB or 16KB, which is absurd
+	 * for such low data rates and which is also more than Linux
+	 * can usually expect to allocate for SKB data...
 	 */
 	net->hard_header_len += sizeof (struct rndis_data_hdr);
 	dev->hard_mtu = net->mtu + net->hard_header_len;
@@ -434,7 +508,7 @@
 	if (unlikely(retval < 0)) {
 		/* it might not even be an RNDIS device!! */
 		dev_err(&intf->dev, "RNDIS init failed, %d\n", retval);
- 		goto fail_and_release;
+		goto fail_and_release;
 	}
 	tmp = le32_to_cpu(u.init_c->max_transfer_size);
 	if (tmp < dev->hard_mtu) {
@@ -450,34 +524,15 @@
 		dev->hard_mtu, tmp, dev->rx_urb_size,
 		1 << le32_to_cpu(u.init_c->packet_alignment));
 
-	/* Get designated host ethernet address.
-	 *
-	 * Adding a payload exactly the same size as the expected response
-	 * payload is an evident requirement MSFT added for ActiveSync.
-	 * This undocumented (and nonsensical) issue was found by sniffing
-	 * protocol requests from the ActiveSync 4.1 Windows driver.
-	 */
-	memset(u.get, 0, sizeof *u.get + 48);
-	u.get->msg_type = RNDIS_MSG_QUERY;
-	u.get->msg_len = ccpu2(sizeof *u.get + 48);
-	u.get->oid = OID_802_3_PERMANENT_ADDRESS;
-	u.get->len = ccpu2(48);
-	u.get->offset = ccpu2(20);
-
-	retval = rndis_command(dev, u.header);
-	if (unlikely(retval < 0)) {
+	/* Get designated host ethernet address */
+	reply_len = ETH_ALEN;
+	retval = rndis_query(dev, intf, u.buf, OID_802_3_PERMANENT_ADDRESS,
+			48, (void **) &bp, &reply_len);
+	if (unlikely(retval< 0)) {
 		dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval);
 		goto fail_and_release;
 	}
-	tmp = le32_to_cpu(u.get_c->offset);
-	if (unlikely((tmp + 8) > (CONTROL_BUFFER_SIZE - ETH_ALEN)
-			|| u.get_c->len != ccpu2(ETH_ALEN))) {
-		dev_err(&intf->dev, "rndis ethaddr off %d len %d ?\n",
-			tmp, le32_to_cpu(u.get_c->len));
-		retval = -EDOM;
-		goto fail_and_release;
-	}
-	memcpy(net->dev_addr, tmp + (char *)&u.get_c->request_id, ETH_ALEN);
+	memcpy(net->dev_addr, bp, ETH_ALEN);
 
 	/* set a nonzero filter to enable data transfers */
 	memset(u.set, 0, sizeof *u.set);
@@ -502,6 +557,7 @@
 fail_and_release:
 	usb_set_intfdata(info->data, NULL);
 	usb_driver_release_interface(driver_of(intf), info->data);
+	info->data = NULL;
 fail:
 	kfree(u.buf);
 	return retval;
@@ -588,7 +644,7 @@
 		if (likely((sizeof *hdr) <= room)) {
 			skb->data = memmove(skb->head + sizeof *hdr,
 					    skb->data, len);
-			skb->tail = skb->data + len;
+			skb_set_tail_pointer(skb, len);
 			goto fill;
 		}
 	}
@@ -618,7 +674,7 @@
 
 static const struct driver_info	rndis_info = {
 	.description =	"RNDIS device",
-	.flags =	FLAG_ETHER | FLAG_FRAMING_RN,
+	.flags =	FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT,
 	.bind =		rndis_bind,
 	.unbind =	rndis_unbind,
 	.status =	rndis_status,
diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c
index ea153dc..fa598f0 100644
--- a/drivers/usb/net/rtl8150.c
+++ b/drivers/usb/net/rtl8150.c
@@ -646,7 +646,6 @@
 		if (!skb) {
 			return;
 		}
-		skb->dev = dev->netdev;
 		skb_reserve(skb, 2);
 		dev->rx_skb_pool[i] = skb;
 	}
diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c
index de69b18..f9cd42d 100644
--- a/drivers/usb/net/usbnet.c
+++ b/drivers/usb/net/usbnet.c
@@ -203,7 +203,6 @@
 {
 	int	status;
 
-	skb->dev = dev->net;
 	skb->protocol = eth_type_trans (skb, dev->net);
 	dev->stats.rx_packets++;
 	dev->stats.rx_bytes += skb->len;
@@ -735,8 +734,7 @@
 {
 	struct usbnet *dev = netdev_priv(net);
 
-	/* REVISIT don't always return "usbnet" */
-	strncpy (info->driver, driver_name, sizeof info->driver);
+	strncpy (info->driver, dev->driver_name, sizeof info->driver);
 	strncpy (info->version, DRIVER_VERSION, sizeof info->version);
 	strncpy (info->fw_version, dev->driver_info->description,
 		sizeof info->fw_version);
@@ -1116,10 +1114,12 @@
 	struct driver_info		*info;
 	struct usb_device		*xdev;
 	int				status;
+	const char			*name;
 
+	name = udev->dev.driver->name;
 	info = (struct driver_info *) prod->driver_info;
 	if (!info) {
-		dev_dbg (&udev->dev, "blacklisted by %s\n", driver_name);
+		dev_dbg (&udev->dev, "blacklisted by %s\n", name);
 		return -ENODEV;
 	}
 	xdev = interface_to_usbdev (udev);
@@ -1139,6 +1139,7 @@
 	dev = netdev_priv(net);
 	dev->udev = xdev;
 	dev->driver_info = info;
+	dev->driver_name = name;
 	dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV
 				| NETIF_MSG_PROBE | NETIF_MSG_LINK);
 	skb_queue_head_init (&dev->rxq);
diff --git a/drivers/usb/net/usbnet.h b/drivers/usb/net/usbnet.h
index 07c70ab..cbb53e0 100644
--- a/drivers/usb/net/usbnet.h
+++ b/drivers/usb/net/usbnet.h
@@ -29,6 +29,7 @@
 	/* housekeeping */
 	struct usb_device	*udev;
 	struct driver_info	*driver_info;
+	const char		*driver_name;
 	wait_queue_head_t	*wait;
 	struct mutex		phy_mutex;
 
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index 2f4d303..ba5d1dc 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -423,11 +423,11 @@
 	  module will be called mct_u232.
 
 config USB_SERIAL_MOS7720
-	tristate "USB Moschip 7720 Single Port Serial Driver"
+	tristate "USB Moschip 7720 Serial Driver"
 	depends on USB_SERIAL
 	---help---
-	  Say Y here if you want to use a USB Serial single port adapter from
-	  Moschip Semiconductor Tech.
+	  Say Y here if you want to use USB Serial single and double
+	  port adapters from Moschip Semiconductor Tech.
 
 	  To compile this driver as a module, choose M here: the
 	  module will be called mos7720.
diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c
index 11dad42..b675735 100644
--- a/drivers/usb/serial/aircable.c
+++ b/drivers/usb/serial/aircable.c
@@ -209,6 +209,7 @@
 	int count, result;
 	struct aircable_private *priv = usb_get_serial_port_data(port);
 	unsigned char* buf;
+	u16 *dbuf;
 	dbg("%s - port %d", __FUNCTION__, port->number);
 	if (port->write_urb_busy)
 		return;
@@ -226,8 +227,8 @@
 
 	buf[0] = TX_HEADER_0;
 	buf[1] = TX_HEADER_1;
-	buf[2] = (unsigned char)count;
-	buf[3] = (unsigned char)(count >> 8);
+	dbuf = (u16 *)&buf[2];
+	*dbuf = cpu_to_le16((u16)count);
 	serial_buf_get(priv->tx_buf,buf + HCI_HEADER_LENGTH, MAX_HCI_FRAMESIZE);
 
 	memcpy(port->write_urb->transfer_buffer, buf,
@@ -434,7 +435,7 @@
 			    __FUNCTION__, urb->status);
 			port->write_urb->transfer_buffer_length = 1;
 			port->write_urb->dev = port->serial->dev;
-			result = usb_submit_urb(port->write_urb, GFP_KERNEL);
+			result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
 			if (result)
 				dev_err(&urb->dev->dev,
 					"%s - failed resubmitting write urb, error %d\n",
diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c
index edd6857..ea2175b 100644
--- a/drivers/usb/serial/ark3116.c
+++ b/drivers/usb/serial/ark3116.c
@@ -341,7 +341,7 @@
 
 	result = usb_serial_generic_open(port, filp);
 	if (result)
-		return result;
+		goto err_out;
 
 	/* open */
 	ARK3116_RCV(serial, 111, 0xFE, 0xC0, 0x0000, 0x0003, 0x02, buf);
@@ -372,6 +372,7 @@
 	if (port->tty)
 		ark3116_set_termios(port, &tmp_termios);
 
+err_out:
 	kfree(buf);
 
 	return result;
diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c
index d7d0ba9..e831cb7 100644
--- a/drivers/usb/serial/cp2101.c
+++ b/drivers/usb/serial/cp2101.c
@@ -58,9 +58,11 @@
 	{ USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */
 	{ USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */
 	{ USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */
+	{ USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */
 	{ USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */
 	{ USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */
 	{ USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */
+	{ USB_DEVICE(0x10C4, 0x80DD) }, /* Tracient RFID */
 	{ USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */
 	{ USB_DEVICE(0x10C4, 0x813D) }, /* Burnside Telecom Deskmobile */
 	{ USB_DEVICE(0x10C4, 0x814A) }, /* West Mountain Radio RIGblaster P&P */
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 8ff9d54..95a1805 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -342,6 +342,7 @@
 	{ USB_DEVICE(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_PIEGROUP_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_TNC_X_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_USBX_707_PID) },
 	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2101_PID) },
 	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2102_PID) },
 	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2103_PID) },
@@ -1433,6 +1434,7 @@
 		dbg("%s - write limit hit\n", __FUNCTION__);
 		return 0;
 	}
+	priv->tx_outstanding_urbs++;
 	spin_unlock_irqrestore(&priv->tx_lock, flags);
 
 	data_offset = priv->write_offset;
@@ -1450,14 +1452,15 @@
 	buffer = kmalloc (transfer_size, GFP_ATOMIC);
 	if (!buffer) {
 		err("%s ran out of kernel memory for urb ...", __FUNCTION__);
-		return -ENOMEM;
+		count = -ENOMEM;
+		goto error_no_buffer;
 	}
 
 	urb = usb_alloc_urb(0, GFP_ATOMIC);
 	if (!urb) {
 		err("%s - no more free urbs", __FUNCTION__);
-		kfree (buffer);
-		return -ENOMEM;
+		count = -ENOMEM;
+		goto error_no_urb;
 	}
 
 	/* Copy data */
@@ -1499,10 +1502,9 @@
 	if (status) {
 		err("%s - failed submitting write urb, error %d", __FUNCTION__, status);
 		count = status;
-		kfree (buffer);
+		goto error;
 	} else {
 		spin_lock_irqsave(&priv->tx_lock, flags);
-		++priv->tx_outstanding_urbs;
 		priv->tx_outstanding_bytes += count;
 		priv->tx_bytes += count;
 		spin_unlock_irqrestore(&priv->tx_lock, flags);
@@ -1510,10 +1512,19 @@
 
 	/* we are done with this urb, so let the host driver
 	 * really free it when it is finished with it */
-	usb_free_urb (urb);
+	usb_free_urb(urb);
 
 	dbg("%s write returning: %d", __FUNCTION__, count);
 	return count;
+error:
+	usb_free_urb(urb);
+error_no_urb:
+	kfree (buffer);
+error_no_buffer:
+	spin_lock_irqsave(&priv->tx_lock, flags);
+	priv->tx_outstanding_urbs--;
+	spin_unlock_irqrestore(&priv->tx_lock, flags);
+	return count;
 } /* ftdi_write */
 
 
diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
index 513cfe1..77ad0a0 100644
--- a/drivers/usb/serial/ftdi_sio.h
+++ b/drivers/usb/serial/ftdi_sio.h
@@ -31,6 +31,7 @@
 #define FTDI_RELAIS_PID	0xFA10  /* Relais device from Rudolf Gugler */
 #define FTDI_NF_RIC_VID	0x0DCD	/* Vendor Id */
 #define FTDI_NF_RIC_PID	0x0001	/* Product Id */
+#define FTDI_USBX_707_PID 0xF857	/* ADSTech IR Blaster USBX-707 */
 
 
 /* www.canusb.com Lawicel CANUSB device */
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 6a26a2e..18f74ac 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -111,7 +111,7 @@
 
 	struct TxFifo		txfifo;			/* transmit fifo -- size will be maxTxCredits */
 	struct urb		*write_urb;		/* write URB for this port */
-	char			write_in_progress;	/* TRUE while a write URB is outstanding */
+	bool			write_in_progress;	/* 'true' while a write URB is outstanding */
 	spinlock_t		ep_lock;
 
 	__u8			shadowLCR;		/* last LCR value received */
@@ -123,11 +123,11 @@
 	__u8			validDataMask;
 	__u32			baudRate;
 
-	char			open;
-	char			openPending;
-	char			commandPending;
-	char			closePending;
-	char			chaseResponsePending;
+	bool			open;
+	bool			openPending;
+	bool			commandPending;
+	bool			closePending;
+	bool			chaseResponsePending;
 
 	wait_queue_head_t	wait_chase;		/* for handling sleeping while waiting for chase to finish */
 	wait_queue_head_t	wait_open;		/* for handling sleeping while waiting for open to finish */
@@ -156,7 +156,7 @@
 	__u8			bulk_in_endpoint;		/* the bulk in endpoint handle */
 	unsigned char *		bulk_in_buffer;			/* the buffer we use for the bulk in endpoint */
 	struct urb *		read_urb;			/* our bulk read urb */
-	int			read_in_progress;
+	bool			read_in_progress;
 	spinlock_t		es_lock;
 
 	__u8			bulk_out_endpoint;		/* the bulk out endpoint handle */
@@ -212,7 +212,7 @@
 
 static int low_latency = 1;	/* tty low latency flag, on by default */
 
-static int CmdUrbs = 0;		/* Number of outstanding Command Write Urbs */
+static atomic_t CmdUrbs;		/* Number of outstanding Command Write Urbs */
 
 
 /* local function prototypes */
@@ -631,14 +631,14 @@
 				if (edge_serial->rxBytesAvail > 0 &&
 				    !edge_serial->read_in_progress) {
 					dbg("%s - posting a read", __FUNCTION__);
-					edge_serial->read_in_progress = TRUE;
+					edge_serial->read_in_progress = true;
 
 					/* we have pending bytes on the bulk in pipe, send a request */
 					edge_serial->read_urb->dev = edge_serial->serial->dev;
 					result = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC);
 					if (result) {
 						dev_err(&edge_serial->serial->dev->dev, "%s - usb_submit_urb(read bulk) failed with result = %d\n", __FUNCTION__, result);
-						edge_serial->read_in_progress = FALSE;
+						edge_serial->read_in_progress = false;
 					}
 				}
 				spin_unlock(&edge_serial->es_lock);
@@ -695,13 +695,13 @@
 
 	if (urb->status) {
 		dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
-		edge_serial->read_in_progress = FALSE;
+		edge_serial->read_in_progress = false;
 		return;
 	}
 
 	if (urb->actual_length == 0) {
 		dbg("%s - read bulk callback with no data", __FUNCTION__);
-		edge_serial->read_in_progress = FALSE;
+		edge_serial->read_in_progress = false;
 		return;
 	}
 
@@ -725,10 +725,10 @@
 		status = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC);
 		if (status) {
 			dev_err(&urb->dev->dev, "%s - usb_submit_urb(read bulk) failed, status = %d\n", __FUNCTION__, status);
-			edge_serial->read_in_progress = FALSE;
+			edge_serial->read_in_progress = false;
 		}
 	} else {
-		edge_serial->read_in_progress = FALSE;
+		edge_serial->read_in_progress = false;
 	}
 
 	spin_unlock(&edge_serial->es_lock);
@@ -759,7 +759,7 @@
 	}
 
 	// Release the Write URB
-	edge_port->write_in_progress = FALSE;
+	edge_port->write_in_progress = false;
 
 	// Check if more data needs to be sent
 	send_more_port_data((struct edgeport_serial *)(usb_get_serial_data(edge_port->port->serial)), edge_port);
@@ -779,8 +779,8 @@
 
 	dbg("%s", __FUNCTION__);
 
-	CmdUrbs--;
-	dbg("%s - FREE URB %p (outstanding %d)", __FUNCTION__, urb, CmdUrbs);
+	atomic_dec(&CmdUrbs);
+	dbg("%s - FREE URB %p (outstanding %d)", __FUNCTION__, urb, atomic_read(&CmdUrbs));
 
 
 	/* clean up the transfer buffer */
@@ -802,7 +802,7 @@
 		tty_wakeup(tty);
 
 	/* we have completed the command */
-	edge_port->commandPending = FALSE;
+	edge_port->commandPending = false;
 	wake_up(&edge_port->wait_command);
 }
 
@@ -868,7 +868,7 @@
 				  port0->bulk_in_buffer,
 				  edge_serial->read_urb->transfer_buffer_length,
 				  edge_bulk_in_callback, edge_serial);
-		edge_serial->read_in_progress = FALSE;
+		edge_serial->read_in_progress = false;
 
 		/* start interrupt read for this edgeport
 		 * this interrupt will continue as long as the edgeport is connected */
@@ -890,26 +890,26 @@
 	/* initialize our port settings */
 	edge_port->txCredits            = 0;			/* Can't send any data yet */
 	edge_port->shadowMCR            = MCR_MASTER_IE;	/* Must always set this bit to enable ints! */
-	edge_port->chaseResponsePending = FALSE;
+	edge_port->chaseResponsePending = false;
 
 	/* send a open port command */
-	edge_port->openPending = TRUE;
-	edge_port->open        = FALSE;
+	edge_port->openPending = true;
+	edge_port->open        = false;
 	response = send_iosp_ext_cmd (edge_port, IOSP_CMD_OPEN_PORT, 0);
 
 	if (response < 0) {
 		dev_err(&port->dev, "%s - error sending open port command\n", __FUNCTION__);
-		edge_port->openPending = FALSE;
+		edge_port->openPending = false;
 		return -ENODEV;
 	}
 
 	/* now wait for the port to be completely opened */
-	wait_event_timeout(edge_port->wait_open, (edge_port->openPending != TRUE), OPEN_TIMEOUT);
+	wait_event_timeout(edge_port->wait_open, !edge_port->openPending, OPEN_TIMEOUT);
 
-	if (edge_port->open == FALSE) {
+	if (!edge_port->open) {
 		/* open timed out */
 		dbg("%s - open timedout", __FUNCTION__);
-		edge_port->openPending = FALSE;
+		edge_port->openPending = false;
 		return -ENODEV;
 	}
 
@@ -928,7 +928,7 @@
 
 	/* Allocate a URB for the write */
 	edge_port->write_urb = usb_alloc_urb (0, GFP_KERNEL);
-	edge_port->write_in_progress = FALSE;
+	edge_port->write_in_progress = false;
 
 	if (!edge_port->write_urb) {
 		dbg("%s - no memory", __FUNCTION__);
@@ -966,7 +966,7 @@
 		lastCredits = edge_port->txCredits;
 
 		// Did we get our Chase response
-		if (edge_port->chaseResponsePending == FALSE) {
+		if (!edge_port->chaseResponsePending) {
 			dbg("%s - Got Chase Response", __FUNCTION__);
 
 			// did we get all of our credit back?
@@ -985,7 +985,7 @@
 			// No activity.. count down.
 			loop--;
 			if (loop == 0) {
-				edge_port->chaseResponsePending = FALSE;
+				edge_port->chaseResponsePending = false;
 				dbg("%s - Chase TIMEOUT", __FUNCTION__);
 				return;
 			}
@@ -1068,13 +1068,13 @@
 	// block until tx is empty
 	block_until_tx_empty(edge_port);
 
-	edge_port->closePending = TRUE;
+	edge_port->closePending = true;
 
 	if ((!edge_serial->is_epic) ||
 	    ((edge_serial->is_epic) &&
 	     (edge_serial->epic_descriptor.Supports.IOSPChase))) {
 		/* flush and chase */
-		edge_port->chaseResponsePending = TRUE;
+		edge_port->chaseResponsePending = true;
 
 		dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__);
 		status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0);
@@ -1082,7 +1082,7 @@
 			// block until chase finished
 			block_until_chase_response(edge_port);
 		} else {
-			edge_port->chaseResponsePending = FALSE;
+			edge_port->chaseResponsePending = false;
 		}
 	}
 
@@ -1094,10 +1094,10 @@
 		send_iosp_ext_cmd (edge_port, IOSP_CMD_CLOSE_PORT, 0);
 	}
 
-	//port->close = TRUE;
-	edge_port->closePending = FALSE;
-	edge_port->open = FALSE;
-	edge_port->openPending = FALSE;
+	//port->close = true;
+	edge_port->closePending = false;
+	edge_port->open = false;
+	edge_port->openPending = false;
 
 	usb_kill_urb(edge_port->write_urb);
 
@@ -1247,7 +1247,7 @@
 	}
 
 	// lock this write
-	edge_port->write_in_progress = TRUE;
+	edge_port->write_in_progress = true;
 
 	// get a pointer to the write_urb
 	urb = edge_port->write_urb;
@@ -1261,7 +1261,7 @@
 	buffer = kmalloc (count+2, GFP_ATOMIC);
 	if (buffer == NULL) {
 		dev_err(&edge_port->port->dev, "%s - no more kernel memory...\n", __FUNCTION__);
-		edge_port->write_in_progress = FALSE;
+		edge_port->write_in_progress = false;
 		goto exit_send;
 	}
 	buffer[0] = IOSP_BUILD_DATA_HDR1 (edge_port->port->number - edge_port->port->serial->minor, count);
@@ -1301,7 +1301,7 @@
 	if (status) {
 		/* something went wrong */
 		dev_err(&edge_port->port->dev, "%s - usb_submit_urb(write bulk) failed, status = %d, data lost\n", __FUNCTION__, status);
-		edge_port->write_in_progress = FALSE;
+		edge_port->write_in_progress = false;
 
 		/* revert the credits as something bad happened. */
 		edge_port->txCredits += count;
@@ -1332,7 +1332,7 @@
 
 	if (edge_port == NULL)
 		return -ENODEV;
-	if (edge_port->closePending == TRUE)
+	if (edge_port->closePending)
 		return -ENODEV;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
@@ -1371,7 +1371,7 @@
 
 	if (edge_port == NULL)
 		return -ENODEV;
-	if (edge_port->closePending == TRUE)
+	if (edge_port->closePending)
 		return -ENODEV;
 
 	if (!edge_port->open) {
@@ -1762,7 +1762,7 @@
 	    ((edge_serial->is_epic) &&
 	     (edge_serial->epic_descriptor.Supports.IOSPChase))) {
 		/* flush and chase */
-		edge_port->chaseResponsePending = TRUE;
+		edge_port->chaseResponsePending = true;
 
 		dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__);
 		status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0);
@@ -1770,7 +1770,7 @@
 			// block until chase finished
 			block_until_chase_response(edge_port);
 		} else {
-			edge_port->chaseResponsePending = FALSE;
+			edge_port->chaseResponsePending = false;
 		}
 	}
 
@@ -1952,13 +1952,13 @@
 				// Also, we currently clear flag and close the port regardless of content of above's Byte3.
 				// We could choose to do something else when Byte3 says Timeout on Chase from Edgeport,
 				// like wait longer in block_until_chase_response, but for now we don't. 
-				edge_port->chaseResponsePending = FALSE;
+				edge_port->chaseResponsePending = false;
 				wake_up (&edge_port->wait_chase);
 				return;
 
 			case IOSP_EXT_STATUS_RX_CHECK_RSP:
 				dbg("%s ========== Port %u CHECK_RSP Sequence = %02x =============\n", __FUNCTION__, edge_serial->rxPort, byte3 );
-				//Port->RxCheckRsp = TRUE;
+				//Port->RxCheckRsp = true;
 				return;
 		}
 	}
@@ -1974,8 +1974,8 @@
 			change_port_settings (edge_port, edge_port->port->tty->termios);
 
 		/* we have completed the open */
-		edge_port->openPending = FALSE;
-		edge_port->open = TRUE;
+		edge_port->openPending = false;
+		edge_port->open = true;
 		wake_up(&edge_port->wait_open);
 		return;
 	}
@@ -1983,7 +1983,7 @@
 	// If port is closed, silently discard all rcvd status. We can
 	// have cases where buffered status is received AFTER the close
 	// port command is sent to the Edgeport.
-	if ((!edge_port->open ) || (edge_port->closePending)) {
+	if (!edge_port->open || edge_port->closePending) {
 		return;
 	}
 
@@ -1991,14 +1991,14 @@
 		// Not currently sent by Edgeport
 		case IOSP_STATUS_LSR:
 			dbg("%s - Port %u LSR Status = %02x", __FUNCTION__, edge_serial->rxPort, byte2);
-			handle_new_lsr (edge_port, FALSE, byte2, 0);
+			handle_new_lsr(edge_port, false, byte2, 0);
 			break;
 
 		case IOSP_STATUS_LSR_DATA:
 			dbg("%s - Port %u LSR Status = %02x, Data = %02x", __FUNCTION__, edge_serial->rxPort, byte2, byte3);
 			// byte2 is LSR Register
 			// byte3 is broken data byte
-			handle_new_lsr (edge_port, TRUE, byte2, byte3);
+			handle_new_lsr(edge_port, true, byte2, byte3);
 			break;
 			//
 			//	case IOSP_EXT_4_STATUS:
@@ -2317,14 +2317,14 @@
 	if (!urb)
 		return -ENOMEM;
 
-	CmdUrbs++;
-	dbg("%s - ALLOCATE URB %p (outstanding %d)", __FUNCTION__, urb, CmdUrbs);
+	atomic_inc(&CmdUrbs);
+	dbg("%s - ALLOCATE URB %p (outstanding %d)", __FUNCTION__, urb, atomic_read(&CmdUrbs));
 
 	usb_fill_bulk_urb (urb, edge_serial->serial->dev, 
 		       usb_sndbulkpipe(edge_serial->serial->dev, edge_serial->bulk_out_endpoint),
 		       buffer, length, edge_bulk_out_cmd_callback, edge_port);
 
-	edge_port->commandPending = TRUE;
+	edge_port->commandPending = true;
 	status = usb_submit_urb(urb, GFP_ATOMIC);
 
 	if (status) {
@@ -2332,16 +2332,16 @@
 		dev_err(&edge_port->port->dev, "%s - usb_submit_urb(write command) failed, status = %d\n", __FUNCTION__, status);
 		usb_kill_urb(urb);
 		usb_free_urb(urb);
-		CmdUrbs--;
+		atomic_dec(&CmdUrbs);
 		return status;
 	}
 
 	// wait for command to finish
 	timeout = COMMAND_TIMEOUT;
 #if 0
-	wait_event (&edge_port->wait_command, (edge_port->commandPending == FALSE));
+	wait_event (&edge_port->wait_command, !edge_port->commandPending);
 
-	if (edge_port->commandPending == TRUE) {
+	if (edge_port->commandPending) {
 		/* command timed out */
 		dbg("%s - command timed out", __FUNCTION__);
 		status = -EINVAL;
@@ -2524,8 +2524,8 @@
 
 	dbg("%s - port %d", __FUNCTION__, edge_port->port->number);
 
-	if ((!edge_port->open) &&
-	    (!edge_port->openPending)) {
+	if (!edge_port->open &&
+	    !edge_port->openPending) {
 		dbg("%s - port not opened", __FUNCTION__);
 		return;
 	}
@@ -2836,9 +2836,9 @@
 	struct usb_device *dev;
 	int i, j;
 	int response;
-	int interrupt_in_found;
-	int bulk_in_found;
-	int bulk_out_found;
+	bool interrupt_in_found;
+	bool bulk_in_found;
+	bool bulk_out_found;
 	static __u32 descriptor[3] = {	EDGE_COMPATIBILITY_MASK0,
 					EDGE_COMPATIBILITY_MASK1,
 					EDGE_COMPATIBILITY_MASK2 };
@@ -2936,14 +2936,14 @@
 	if (edge_serial->is_epic) {
 		/* EPIC thing, set up our interrupt polling now and our read urb, so
 		 * that the device knows it really is connected. */
-		interrupt_in_found = bulk_in_found = bulk_out_found = FALSE;
+		interrupt_in_found = bulk_in_found = bulk_out_found = false;
 		for (i = 0; i < serial->interface->altsetting[0].desc.bNumEndpoints; ++i) {
 			struct usb_endpoint_descriptor *endpoint;
 			int buffer_size;
 
 			endpoint = &serial->interface->altsetting[0].endpoint[i].desc;
 			buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
-			if ((!interrupt_in_found) &&
+			if (!interrupt_in_found &&
 			    (usb_endpoint_is_int_in(endpoint))) {
 				/* we found a interrupt in endpoint */
 				dbg("found interrupt in");
@@ -2972,10 +2972,10 @@
 						 edge_serial,
 						 endpoint->bInterval);
 
-				interrupt_in_found = TRUE;
+				interrupt_in_found = true;
 			}
 
-			if ((!bulk_in_found) &&
+			if (!bulk_in_found &&
 			    (usb_endpoint_is_bulk_in(endpoint))) {
 				/* we found a bulk in endpoint */
 				dbg("found bulk in");
@@ -3001,19 +3001,19 @@
 						  endpoint->wMaxPacketSize,
 						  edge_bulk_in_callback,
 						  edge_serial);
-				bulk_in_found = TRUE;
+				bulk_in_found = true;
 			}
 
-			if ((!bulk_out_found) &&
+			if (!bulk_out_found &&
 			    (usb_endpoint_is_bulk_out(endpoint))) {
 				/* we found a bulk out endpoint */
 				dbg("found bulk out");
 				edge_serial->bulk_out_endpoint = endpoint->bEndpointAddress;
-				bulk_out_found = TRUE;
+				bulk_out_found = true;
 			}
 		}
 
-		if ((!interrupt_in_found) || (!bulk_in_found) || (!bulk_out_found)) {
+		if (!interrupt_in_found || !bulk_in_found || !bulk_out_found) {
 			err ("Error - the proper endpoints were not found!");
 			return -ENODEV;
 		}
@@ -3083,6 +3083,7 @@
 	retval = usb_register(&io_driver);
 	if (retval) 
 		goto failed_usb_register;
+	atomic_set(&CmdUrbs, 0);
 	info(DRIVER_DESC " " DRIVER_VERSION);
 	return 0;
 
diff --git a/drivers/usb/serial/io_edgeport.h b/drivers/usb/serial/io_edgeport.h
index 29a913a..cb201c1 100644
--- a/drivers/usb/serial/io_edgeport.h
+++ b/drivers/usb/serial/io_edgeport.h
@@ -19,12 +19,6 @@
 #define MAX_RS232_PORTS		8	/* Max # of RS-232 ports per device */
 
 /* typedefs that the insideout headers need */
-#ifndef TRUE
-	#define TRUE		(1)
-#endif
-#ifndef FALSE
-	#define FALSE		(0)
-#endif
 #ifndef LOW8
 	#define LOW8(a)		((unsigned char)(a & 0xff))
 #endif
diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
index d16e2e1..4df0ec7 100644
--- a/drivers/usb/serial/ipaq.c
+++ b/drivers/usb/serial/ipaq.c
@@ -255,6 +255,7 @@
 	{ USB_DEVICE(0x04DD, 0x9102) }, /* SHARP WS003SH USB Modem */
 	{ USB_DEVICE(0x04DD, 0x9121) }, /* SHARP WS004SH USB Modem */
 	{ USB_DEVICE(0x04DD, 0x9123) }, /* SHARP WS007SH USB Modem */
+	{ USB_DEVICE(0x04DD, 0x9151) }, /* SHARP S01SH USB Modem */
 	{ USB_DEVICE(0x04E8, 0x5F00) }, /* Samsung NEXiO USB Sync */
 	{ USB_DEVICE(0x04E8, 0x5F01) }, /* Samsung NEXiO USB Sync */
 	{ USB_DEVICE(0x04E8, 0x5F02) }, /* Samsung NEXiO USB Sync */
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index b2097c4..7b085f3 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -238,7 +238,7 @@
 	if (rc < 0)
 		err("Reading line status failed (error = %d)", rc);
 	else {
-		status = status_buf[0] + (status_buf[1]<<8);
+		status = le16_to_cpu(*(u16 *)status_buf);
 
 		info("%s - read status %x %x", __FUNCTION__,
 		     status_buf[0], status_buf[1]);
@@ -257,7 +257,7 @@
 static int klsi_105_startup (struct usb_serial *serial)
 {
 	struct klsi_105_private *priv;
-	int i;
+	int i, j;
 
 	/* check if we support the product id (see keyspan.c)
 	 * FIXME
@@ -265,12 +265,12 @@
 
 	/* allocate the private data structure */
 	for (i=0; i<serial->num_ports; i++) {
-		int j;
 		priv = kmalloc(sizeof(struct klsi_105_private),
 						   GFP_KERNEL);
 		if (!priv) {
 			dbg("%skmalloc for klsi_105_private failed.", __FUNCTION__);
-			return -ENOMEM;
+			i--;
+			goto err_cleanup;
 		}
 		/* set initial values for control structures */
 		priv->cfg.pktlen    = 5;
@@ -292,15 +292,14 @@
 			priv->write_urb_pool[j] = urb;
 			if (urb == NULL) {
 				err("No more urbs???");
-				continue;
+				goto err_cleanup;
 			}
 
-			urb->transfer_buffer = NULL;
 			urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE,
 							GFP_KERNEL);
 			if (!urb->transfer_buffer) {
 				err("%s - out of memory for urb buffers.", __FUNCTION__);
-				continue;
+				goto err_cleanup;
 			}
 		}
 
@@ -308,7 +307,20 @@
 		init_waitqueue_head(&serial->port[i]->write_wait);
 	}
 	
-	return (0);
+	return 0;
+
+err_cleanup:
+	for (; i >= 0; i--) {
+		priv = usb_get_serial_port_data(serial->port[i]);
+		for (j=0; j < NUM_URBS; j++) {
+			if (priv->write_urb_pool[j]) {
+				kfree(priv->write_urb_pool[j]->transfer_buffer);
+				usb_free_urb(priv->write_urb_pool[j]);
+			}
+		}
+		usb_set_serial_port_data(serial->port[i], NULL);
+	}
+	return -ENOMEM;
 } /* klsi_105_startup */
 
 
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index 4cd839b..3db1adc 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -438,17 +438,21 @@
 	if (retval) {
 		err("usb_submit_urb(read bulk) failed pipe 0x%x err %d",
 		    port->read_urb->pipe, retval);
-		goto exit;
+		goto error;
 	}
 
 	port->interrupt_in_urb->dev = port->serial->dev;
 	retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
-	if (retval)
+	if (retval) {
+		usb_kill_urb(port->read_urb);
 		err(" usb_submit_urb(read int) failed pipe 0x%x err %d",
 		    port->interrupt_in_urb->pipe, retval);
-
-exit:
+		goto error;
+	}
 	return 0;
+
+error:
+	return retval;
 } /* mct_u232_open */
 
 
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index 19bf403..b563e2a 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -103,11 +103,9 @@
 {
 	int result;
 	int length;
-	__u32 *data;
-	unsigned int status;
+	__u8 *data;
 	__u8 sp1;
 	__u8 sp2;
-	__u8 st;
 
 	dbg("%s"," : Entering\n");
 
@@ -141,18 +139,19 @@
 	 * Byte 2 IIR Port 2 (port.number is 1)
 	 * Byte 3 --------------
 	 * Byte 4 FIFO status for both */
-	if (length && length > 4) {
+
+	/* the above description is inverted
+	 * 	oneukum 2007-03-14 */
+
+	if (unlikely(length != 4)) {
 		dbg("Wrong data !!!");
 		return;
 	}
 
-	status = *data;
+	sp1 = data[3];
+	sp2 = data[2];
 
-	sp1 = (status & 0xff000000)>>24;
-	sp2 = (status & 0x00ff0000)>>16;
-	st = status & 0x000000ff;
-
-	if ((sp1 & 0x01) || (sp2 & 0x01)) {
+	if ((sp1 | sp2) & 0x01) {
 		/* No Interrupt Pending in both the ports */
 		dbg("No Interrupt !!!");
 	} else {
@@ -333,6 +332,7 @@
 	int response;
 	int port_number;
 	char data;
+	int allocated_urbs = 0;
 	int j;
 
 	serial = port->serial;
@@ -353,7 +353,7 @@
 
 	/* Initialising the write urb pool */
 	for (j = 0; j < NUM_URBS; ++j) {
-		urb = usb_alloc_urb(0,GFP_ATOMIC);
+		urb = usb_alloc_urb(0,GFP_KERNEL);
 		mos7720_port->write_urb_pool[j] = urb;
 
 		if (urb == NULL) {
@@ -365,10 +365,16 @@
 					       GFP_KERNEL);
 		if (!urb->transfer_buffer) {
 			err("%s-out of memory for urb buffers.", __FUNCTION__);
+			usb_free_urb(mos7720_port->write_urb_pool[j]);
+			mos7720_port->write_urb_pool[j] = NULL;
 			continue;
 		}
+		allocated_urbs++;
 	}
 
+	if (!allocated_urbs)
+		return -ENOMEM;
+
 	 /* Initialize MCS7720 -- Write Init values to corresponding Registers
 	  *
 	  * Register Index
@@ -526,7 +532,7 @@
 	}
 
 	for (i = 0; i < NUM_URBS; ++i) {
-		if (mos7720_port->write_urb_pool[i]->status == -EINPROGRESS)
+		if (mos7720_port->write_urb_pool[i] && mos7720_port->write_urb_pool[i]->status == -EINPROGRESS)
 			chars += URB_TRANSFER_BUFFER_SIZE;
 	}
 	dbg("%s - returns %d", __FUNCTION__, chars);
@@ -629,7 +635,7 @@
 	}
 
 	for (i = 0; i < NUM_URBS; ++i) {
-		if (mos7720_port->write_urb_pool[i]->status != -EINPROGRESS)
+		if (mos7720_port->write_urb_pool[i] && mos7720_port->write_urb_pool[i]->status != -EINPROGRESS)
 			room += URB_TRANSFER_BUFFER_SIZE;
 	}
 
@@ -664,7 +670,7 @@
 	urb = NULL;
 
 	for (i = 0; i < NUM_URBS; ++i) {
-		if (mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) {
+		if (mos7720_port->write_urb_pool[i] && mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) {
 			urb = mos7720_port->write_urb_pool[i];
 			dbg("URB:%d",i);
 			break;
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index c6cca85..2366e7b 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -176,9 +176,12 @@
 	int port_num;		/*Actual port number in the device(1,2,etc) */
 	struct urb *write_urb;	/* write URB for this port */
 	struct urb *read_urb;	/* read URB for this port */
+	struct urb *int_urb;
 	__u8 shadowLCR;		/* last LCR value received */
 	__u8 shadowMCR;		/* last MCR value received */
 	char open;
+	char open_ports;
+	char zombie;
 	wait_queue_head_t wait_chase;	/* for handling sleeping while waiting for chase to finish */
 	wait_queue_head_t delta_msr_wait;	/* for handling sleeping while waiting for msr change to happen */
 	int delta_msr_cond;
@@ -191,17 +194,17 @@
 	__u8 DcrRegOffset;
 	//for processing control URBS in interrupt context
 	struct urb *control_urb;
+	struct usb_ctrlrequest *dr;
 	char *ctrl_buf;
 	int MsrLsr;
 
+	spinlock_t pool_lock;
 	struct urb *write_urb_pool[NUM_URBS];
+	char busy[NUM_URBS];
 };
 
 
 static int debug;
-static int mos7840_num_ports;	//this says the number of ports in the device
-static int mos7840_num_open_ports;
-
 
 /*
  * mos7840_set_reg_sync
@@ -254,7 +257,7 @@
 	struct usb_device *dev = port->serial->dev;
 	val = val & 0x00ff;
 	// For the UART control registers, the application number need to be Or'ed
-	if (mos7840_num_ports == 4) {
+	if (port->serial->num_ports == 4) {
 		val |=
 		    (((__u16) port->number - (__u16) (port->serial->minor)) +
 		     1) << 8;
@@ -294,7 +297,7 @@
 
 	//dbg("application number is %4x \n",(((__u16)port->number - (__u16)(port->serial->minor))+1)<<8);
 	/*Wval  is same as application number */
-	if (mos7840_num_ports == 4) {
+	if (port->serial->num_ports == 4) {
 		Wval =
 		    (((__u16) port->number - (__u16) (port->serial->minor)) +
 		     1) << 8;
@@ -352,7 +355,7 @@
 	return (struct moschip_port *)usb_get_serial_port_data(port);
 }
 
-static int mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr)
+static void mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr)
 {
 	struct moschip_port *mos7840_port;
 	struct async_icount *icount;
@@ -366,22 +369,24 @@
 		/* update input line counters */
 		if (new_msr & MOS_MSR_DELTA_CTS) {
 			icount->cts++;
+			smp_wmb();
 		}
 		if (new_msr & MOS_MSR_DELTA_DSR) {
 			icount->dsr++;
+			smp_wmb();
 		}
 		if (new_msr & MOS_MSR_DELTA_CD) {
 			icount->dcd++;
+			smp_wmb();
 		}
 		if (new_msr & MOS_MSR_DELTA_RI) {
 			icount->rng++;
+			smp_wmb();
 		}
 	}
-
-	return 0;
 }
 
-static int mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr)
+static void mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr)
 {
 	struct async_icount *icount;
 
@@ -400,18 +405,20 @@
 	icount = &port->icount;
 	if (new_lsr & SERIAL_LSR_BI) {
 		icount->brk++;
+		smp_wmb();
 	}
 	if (new_lsr & SERIAL_LSR_OE) {
 		icount->overrun++;
+		smp_wmb();
 	}
 	if (new_lsr & SERIAL_LSR_PE) {
 		icount->parity++;
+		smp_wmb();
 	}
 	if (new_lsr & SERIAL_LSR_FE) {
 		icount->frame++;
+		smp_wmb();
 	}
-
-	return 0;
 }
 
 /************************************************************************/
@@ -426,12 +433,15 @@
 	unsigned char *data;
 	struct moschip_port *mos7840_port;
 	__u8 regval = 0x0;
+	int result = 0;
 
 	if (!urb) {
 		dbg("%s", "Invalid Pointer !!!!:\n");
 		return;
 	}
 
+	mos7840_port = (struct moschip_port *)urb->context;
+
 	switch (urb->status) {
 	case 0:
 		/* success */
@@ -449,8 +459,6 @@
 		goto exit;
 	}
 
-	mos7840_port = (struct moschip_port *)urb->context;
-
 	dbg("%s urb buffer size is %d\n", __FUNCTION__, urb->actual_length);
 	dbg("%s mos7840_port->MsrLsr is %d port %d\n", __FUNCTION__,
 	    mos7840_port->MsrLsr, mos7840_port->port_num);
@@ -462,21 +470,26 @@
 	else if (mos7840_port->MsrLsr == 1)
 		mos7840_handle_new_lsr(mos7840_port, regval);
 
-      exit:
-	return;
+exit:
+	spin_lock(&mos7840_port->pool_lock);
+	if (!mos7840_port->zombie)
+		result = usb_submit_urb(mos7840_port->int_urb, GFP_ATOMIC);
+	spin_unlock(&mos7840_port->pool_lock);
+	if (result) {
+		dev_err(&urb->dev->dev,
+			"%s - Error %d submitting interrupt urb\n",
+			__FUNCTION__, result);
+	}
 }
 
 static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg,
 			   __u16 * val)
 {
 	struct usb_device *dev = mcs->port->serial->dev;
-	struct usb_ctrlrequest *dr = NULL;
-	unsigned char *buffer = NULL;
-	int ret = 0;
-	buffer = (__u8 *) mcs->ctrl_buf;
+	struct usb_ctrlrequest *dr = mcs->dr;
+	unsigned char *buffer = mcs->ctrl_buf;
+	int ret;
 
-//      dr=(struct usb_ctrlrequest *)(buffer);
-	dr = (void *)(buffer + 2);
 	dr->bRequestType = MCS_RD_RTYPE;
 	dr->bRequest = MCS_RDREQ;
 	dr->wValue = cpu_to_le16(Wval);	//0;
@@ -506,8 +519,8 @@
 	__u16 Data;
 	unsigned char *data;
 	__u8 sp[5], st;
-	int i;
-	__u16 wval;
+	int i, rv = 0;
+	__u16 wval, wreg = 0;
 
 	dbg("%s", " : Entering\n");
 	if (!urb) {
@@ -569,31 +582,34 @@
 					dbg("Serial Port %d: Receiver status error or ", i);
 					dbg("address bit detected in 9-bit mode\n");
 					mos7840_port->MsrLsr = 1;
-					mos7840_get_reg(mos7840_port, wval,
-							LINE_STATUS_REGISTER,
-							&Data);
+					wreg = LINE_STATUS_REGISTER;
 					break;
 				case SERIAL_IIR_MS:
 					dbg("Serial Port %d: Modem status change\n", i);
 					mos7840_port->MsrLsr = 0;
-					mos7840_get_reg(mos7840_port, wval,
-							MODEM_STATUS_REGISTER,
-							&Data);
+					wreg = MODEM_STATUS_REGISTER;
 					break;
 				}
+				spin_lock(&mos7840_port->pool_lock);
+				if (!mos7840_port->zombie) {
+					rv = mos7840_get_reg(mos7840_port, wval, wreg, &Data);
+				} else {
+					spin_unlock(&mos7840_port->pool_lock);
+					return;
+				}
+				spin_unlock(&mos7840_port->pool_lock);
 			}
 		}
 	}
-      exit:
+	if (!(rv < 0)) /* the completion handler for the control urb will resubmit */
+		return;
+exit:
 	result = usb_submit_urb(urb, GFP_ATOMIC);
 	if (result) {
 		dev_err(&urb->dev->dev,
 			"%s - Error %d submitting interrupt urb\n",
 			__FUNCTION__, result);
 	}
-
-	return;
-
 }
 
 static int mos7840_port_paranoia_check(struct usb_serial_port *port,
@@ -634,7 +650,8 @@
 	if (!port ||
 	    mos7840_port_paranoia_check(port, function) ||
 	    mos7840_serial_paranoia_check(port->serial, function)) {
-		/* then say that we don't have a valid usb_serial thing, which will                  * end up genrating -ENODEV return values */
+		/* then say that we don't have a valid usb_serial thing, which will
+		 * end up genrating -ENODEV return values */
 		return NULL;
 	}
 
@@ -699,6 +716,7 @@
 			tty_flip_buffer_push(tty);
 		}
 		mos7840_port->icount.rx += urb->actual_length;
+		smp_wmb();
 		dbg("mos7840_port->icount.rx is %d:\n",
 		    mos7840_port->icount.rx);
 	}
@@ -708,15 +726,14 @@
 		return;
 	}
 
-	if (mos7840_port->read_urb->status != -EINPROGRESS) {
-		mos7840_port->read_urb->dev = serial->dev;
 
-		status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
+	mos7840_port->read_urb->dev = serial->dev;
 
-		if (status) {
-			dbg(" usb_submit_urb(read bulk) failed, status = %d",
-			    status);
-		}
+	status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
+
+	if (status) {
+		dbg(" usb_submit_urb(read bulk) failed, status = %d",
+		 status);
 	}
 }
 
@@ -730,17 +747,28 @@
 {
 	struct moschip_port *mos7840_port;
 	struct tty_struct *tty;
+	int i;
+
 	if (!urb) {
 		dbg("%s", "Invalid Pointer !!!!:\n");
 		return;
 	}
 
+	mos7840_port = (struct moschip_port *)urb->context;
+	spin_lock(&mos7840_port->pool_lock);
+	for (i = 0; i < NUM_URBS; i++) {
+		if (urb == mos7840_port->write_urb_pool[i]) {
+			mos7840_port->busy[i] = 0;
+			break;
+		}
+	}
+	spin_unlock(&mos7840_port->pool_lock);
+
 	if (urb->status) {
 		dbg("nonzero write bulk status received:%d\n", urb->status);
 		return;
 	}
 
-	mos7840_port = (struct moschip_port *)urb->context;
 	if (!mos7840_port) {
 		dbg("%s", "NULL mos7840_port pointer \n");
 		return;
@@ -792,13 +820,13 @@
 	__u16 Data;
 	int status;
 	struct moschip_port *mos7840_port;
+	struct moschip_port *port0;
 
 	if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
 		dbg("%s", "Port Paranoia failed \n");
 		return -ENODEV;
 	}
 
-	mos7840_num_open_ports++;
 	serial = port->serial;
 
 	if (mos7840_serial_paranoia_check(serial, __FUNCTION__)) {
@@ -807,16 +835,18 @@
 	}
 
 	mos7840_port = mos7840_get_port_private(port);
+	port0 = mos7840_get_port_private(serial->port[0]);
 
-	if (mos7840_port == NULL)
+	if (mos7840_port == NULL || port0 == NULL)
 		return -ENODEV;
 
 	usb_clear_halt(serial->dev, port->write_urb->pipe);
 	usb_clear_halt(serial->dev, port->read_urb->pipe);
+	port0->open_ports++;
 
 	/* Initialising the write urb pool */
 	for (j = 0; j < NUM_URBS; ++j) {
-		urb = usb_alloc_urb(0, GFP_ATOMIC);
+		urb = usb_alloc_urb(0, GFP_KERNEL);
 		mos7840_port->write_urb_pool[j] = urb;
 
 		if (urb == NULL) {
@@ -824,10 +854,10 @@
 			continue;
 		}
 
-		urb->transfer_buffer = NULL;
-		urb->transfer_buffer =
-		    kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
+		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
 		if (!urb->transfer_buffer) {
+			usb_free_urb(urb);
+			mos7840_port->write_urb_pool[j] = NULL;
 			err("%s-out of memory for urb buffers.", __FUNCTION__);
 			continue;
 		}
@@ -879,9 +909,7 @@
 	}
 	Data |= 0x08;		//Driver done bit
 	Data |= 0x20;		//rx_disable
-	status = 0;
-	status =
-	    mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data);
+	status = mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data);
 	if (status < 0) {
 		dbg("writing Controlreg failed\n");
 		return -1;
@@ -893,7 +921,6 @@
 	////////////////////////////////////
 
 	Data = 0x00;
-	status = 0;
 	status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
 	if (status < 0) {
 		dbg("disableing interrupts failed\n");
@@ -901,7 +928,6 @@
 	}
 	// Set FIFO_CONTROL_REGISTER to the default value
 	Data = 0x00;
-	status = 0;
 	status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
 	if (status < 0) {
 		dbg("Writing FIFO_CONTROL_REGISTER  failed\n");
@@ -909,7 +935,6 @@
 	}
 
 	Data = 0xcf;
-	status = 0;
 	status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
 	if (status < 0) {
 		dbg("Writing FIFO_CONTROL_REGISTER  failed\n");
@@ -917,22 +942,18 @@
 	}
 
 	Data = 0x03;
-	status = 0;
 	status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
 	mos7840_port->shadowLCR = Data;
 
 	Data = 0x0b;
-	status = 0;
 	status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
 	mos7840_port->shadowMCR = Data;
 
 	Data = 0x00;
-	status = 0;
 	status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
 	mos7840_port->shadowLCR = Data;
 
 	Data |= SERIAL_LCR_DLAB;	//data latch enable in LCR 0x80
-	status = 0;
 	status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
 
 	Data = 0x0c;
@@ -999,7 +1020,7 @@
 /* Check to see if we've set up our endpoint info yet    *
      * (can't set it up in mos7840_startup as the structures *
      * were not set up at that time.)                        */
-	if (mos7840_num_open_ports == 1) {
+	if (port0->open_ports == 1) {
 		if (serial->port[0]->interrupt_in_buffer == NULL) {
 
 			/* set up interrupt urb */
@@ -1097,6 +1118,7 @@
 {
 	int i;
 	int chars = 0;
+	unsigned long flags;
 	struct moschip_port *mos7840_port;
 
 	dbg("%s \n", " mos7840_chars_in_buffer:entering ...........");
@@ -1112,13 +1134,15 @@
 		return -1;
 	}
 
+	spin_lock_irqsave(&mos7840_port->pool_lock,flags);
 	for (i = 0; i < NUM_URBS; ++i) {
-		if (mos7840_port->write_urb_pool[i]->status == -EINPROGRESS) {
+		if (mos7840_port->busy[i]) {
 			chars += URB_TRANSFER_BUFFER_SIZE;
 		}
 	}
+	spin_unlock_irqrestore(&mos7840_port->pool_lock,flags);
 	dbg("%s - returns %d", __FUNCTION__, chars);
-	return (chars);
+	return chars;
 
 }
 
@@ -1172,6 +1196,7 @@
 {
 	struct usb_serial *serial;
 	struct moschip_port *mos7840_port;
+	struct moschip_port *port0;
 	int j;
 	__u16 Data;
 
@@ -1189,10 +1214,10 @@
 	}
 
 	mos7840_port = mos7840_get_port_private(port);
+	port0 = mos7840_get_port_private(serial->port[0]);
 
-	if (mos7840_port == NULL) {
+	if (mos7840_port == NULL || port0 == NULL)
 		return;
-	}
 
 	for (j = 0; j < NUM_URBS; ++j)
 		usb_kill_urb(mos7840_port->write_urb_pool[j]);
@@ -1234,12 +1259,13 @@
 	}
 //              if(mos7840_port->ctrl_buf != NULL)
 //                      kfree(mos7840_port->ctrl_buf);
-	mos7840_num_open_ports--;
+	port0->open_ports--;
 	dbg("mos7840_num_open_ports in close%d:in port%d\n",
-	    mos7840_num_open_ports, port->number);
-	if (mos7840_num_open_ports == 0) {
+	    port0->open_ports, port->number);
+	if (port0->open_ports == 0) {
 		if (serial->port[0]->interrupt_in_urb) {
 			dbg("%s", "Shutdown interrupt_in_urb\n");
+			usb_kill_urb(serial->port[0]->interrupt_in_urb);
 		}
 	}
 
@@ -1368,6 +1394,7 @@
 {
 	int i;
 	int room = 0;
+	unsigned long flags;
 	struct moschip_port *mos7840_port;
 
 	dbg("%s \n", " mos7840_write_room:entering ...........");
@@ -1384,14 +1411,17 @@
 		return -1;
 	}
 
+	spin_lock_irqsave(&mos7840_port->pool_lock, flags);
 	for (i = 0; i < NUM_URBS; ++i) {
-		if (mos7840_port->write_urb_pool[i]->status != -EINPROGRESS) {
+		if (!mos7840_port->busy[i]) {
 			room += URB_TRANSFER_BUFFER_SIZE;
 		}
 	}
+	spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
 
+	room = (room == 0) ? 0 : room - URB_TRANSFER_BUFFER_SIZE + 1;
 	dbg("%s - returns %d", __FUNCTION__, room);
-	return (room);
+	return room;
 
 }
 
@@ -1410,6 +1440,7 @@
 	int i;
 	int bytes_sent = 0;
 	int transfer_size;
+	unsigned long flags;
 
 	struct moschip_port *mos7840_port;
 	struct usb_serial *serial;
@@ -1476,13 +1507,16 @@
 	/* try to find a free urb in the list */
 	urb = NULL;
 
+	spin_lock_irqsave(&mos7840_port->pool_lock, flags);
 	for (i = 0; i < NUM_URBS; ++i) {
-		if (mos7840_port->write_urb_pool[i]->status != -EINPROGRESS) {
+		if (!mos7840_port->busy[i]) {
+			mos7840_port->busy[i] = 1;
 			urb = mos7840_port->write_urb_pool[i];
 			dbg("\nURB:%d", i);
 			break;
 		}
 	}
+	spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
 
 	if (urb == NULL) {
 		dbg("%s - no more free urbs", __FUNCTION__);
@@ -1518,6 +1552,7 @@
 	status = usb_submit_urb(urb, GFP_ATOMIC);
 
 	if (status) {
+		mos7840_port->busy[i] = 0;
 		err("%s - usb_submit_urb(write bulk) failed with status = %d",
 		    __FUNCTION__, status);
 		bytes_sent = status;
@@ -1525,6 +1560,7 @@
 	}
 	bytes_sent = transfer_size;
 	mos7840_port->icount.tx += transfer_size;
+	smp_wmb();
 	dbg("mos7840_port->icount.tx is %d:\n", mos7840_port->icount.tx);
       exit:
 
@@ -2490,6 +2526,7 @@
 			if (signal_pending(current))
 				return -ERESTARTSYS;
 			cnow = mos7840_port->icount;
+			smp_rmb();
 			if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
 			    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
 				return -EIO;	/* no change => error */
@@ -2506,6 +2543,7 @@
 
 	case TIOCGICOUNT:
 		cnow = mos7840_port->icount;
+		smp_rmb();
 		icount.cts = cnow.cts;
 		icount.dsr = cnow.dsr;
 		icount.rng = cnow.rng;
@@ -2535,19 +2573,18 @@
 
 static int mos7840_calc_num_ports(struct usb_serial *serial)
 {
+	int mos7840_num_ports = 0;
 
 	dbg("numberofendpoints: %d \n",
 	    (int)serial->interface->cur_altsetting->desc.bNumEndpoints);
 	dbg("numberofendpoints: %d \n",
 	    (int)serial->interface->altsetting->desc.bNumEndpoints);
 	if (serial->interface->cur_altsetting->desc.bNumEndpoints == 5) {
-		mos7840_num_ports = 2;
-		serial->type->num_ports = 2;
+		mos7840_num_ports = serial->num_ports = 2;
 	} else if (serial->interface->cur_altsetting->desc.bNumEndpoints == 9) {
-		mos7840_num_ports = 4;
-		serial->type->num_bulk_in = 4;
-		serial->type->num_bulk_out = 4;
-		serial->type->num_ports = 4;
+		serial->num_bulk_in = 4;
+		serial->num_bulk_out = 4;
+		mos7840_num_ports = serial->num_ports = 4;
 	}
 
 	return mos7840_num_ports;
@@ -2583,7 +2620,9 @@
 		mos7840_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL);
 		if (mos7840_port == NULL) {
 			err("%s - Out of memory", __FUNCTION__);
-			return -ENOMEM;
+			status = -ENOMEM;
+			i--; /* don't follow NULL pointer cleaning up */
+			goto error;
 		}
 
 		/* Initialize all port interrupt end point to port 0 int endpoint *
@@ -2591,6 +2630,7 @@
 
 		mos7840_port->port = serial->port[i];
 		mos7840_set_port_private(serial->port[i], mos7840_port);
+		spin_lock_init(&mos7840_port->pool_lock);
 
 		mos7840_port->port_num = ((serial->port[i]->number -
 					   (serial->port[i]->serial->minor)) +
@@ -2601,22 +2641,22 @@
 			mos7840_port->ControlRegOffset = 0x1;
 			mos7840_port->DcrRegOffset = 0x4;
 		} else if ((mos7840_port->port_num == 2)
-			   && (mos7840_num_ports == 4)) {
+			   && (serial->num_ports == 4)) {
 			mos7840_port->SpRegOffset = 0x8;
 			mos7840_port->ControlRegOffset = 0x9;
 			mos7840_port->DcrRegOffset = 0x16;
 		} else if ((mos7840_port->port_num == 2)
-			   && (mos7840_num_ports == 2)) {
+			   && (serial->num_ports == 2)) {
 			mos7840_port->SpRegOffset = 0xa;
 			mos7840_port->ControlRegOffset = 0xb;
 			mos7840_port->DcrRegOffset = 0x19;
 		} else if ((mos7840_port->port_num == 3)
-			   && (mos7840_num_ports == 4)) {
+			   && (serial->num_ports == 4)) {
 			mos7840_port->SpRegOffset = 0xa;
 			mos7840_port->ControlRegOffset = 0xb;
 			mos7840_port->DcrRegOffset = 0x19;
 		} else if ((mos7840_port->port_num == 4)
-			   && (mos7840_num_ports == 4)) {
+			   && (serial->num_ports == 4)) {
 			mos7840_port->SpRegOffset = 0xc;
 			mos7840_port->ControlRegOffset = 0xd;
 			mos7840_port->DcrRegOffset = 0x1c;
@@ -2701,21 +2741,19 @@
 			dbg("CLK_START_VALUE_REGISTER Writing success status%d\n", status);
 
 		Data = 0x20;
-		status = 0;
 		status =
 		    mos7840_set_reg_sync(serial->port[i], CLK_MULTI_REGISTER,
 					 Data);
 		if (status < 0) {
 			dbg("Writing CLK_MULTI_REGISTER failed status-0x%x\n",
 			    status);
-			break;
+			goto error;
 		} else
 			dbg("CLK_MULTI_REGISTER Writing success status%d\n",
 			    status);
 
 		//write value 0x0 to scratchpad register
 		Data = 0x00;
-		status = 0;
 		status =
 		    mos7840_set_uart_reg(serial->port[i], SCRATCH_PAD_REGISTER,
 					 Data);
@@ -2729,7 +2767,7 @@
 
 		//Zero Length flag register
 		if ((mos7840_port->port_num != 1)
-		    && (mos7840_num_ports == 2)) {
+		    && (serial->num_ports == 2)) {
 
 			Data = 0xff;
 			status = 0;
@@ -2770,14 +2808,17 @@
 				    i + 1, status);
 
 		}
-		mos7840_port->control_urb = usb_alloc_urb(0, GFP_ATOMIC);
+		mos7840_port->control_urb = usb_alloc_urb(0, GFP_KERNEL);
 		mos7840_port->ctrl_buf = kmalloc(16, GFP_KERNEL);
-
+		mos7840_port->dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+		if (!mos7840_port->control_urb || !mos7840_port->ctrl_buf || !mos7840_port->dr) {
+			status = -ENOMEM;
+			goto error;
+		}
 	}
 
 	//Zero Length flag enable
 	Data = 0x0f;
-	status = 0;
 	status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, Data);
 	if (status < 0) {
 		dbg("Writing ZLP_REG5 failed status-0x%x\n", status);
@@ -2789,6 +2830,17 @@
 	usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
 			(__u8) 0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5 * HZ);
 	return 0;
+error:
+	for (/* nothing */; i >= 0; i--) {
+		mos7840_port = mos7840_get_port_private(serial->port[i]);
+
+		kfree(mos7840_port->dr);
+		kfree(mos7840_port->ctrl_buf);
+		usb_free_urb(mos7840_port->control_urb);
+		kfree(mos7840_port);
+		serial->port[i] = NULL;
+	}
+	return status;
 }
 
 /****************************************************************************
@@ -2799,6 +2851,7 @@
 static void mos7840_shutdown(struct usb_serial *serial)
 {
 	int i;
+	unsigned long flags;
 	struct moschip_port *mos7840_port;
 	dbg("%s \n", " shutdown :entering..........");
 
@@ -2814,8 +2867,12 @@
 
 	for (i = 0; i < serial->num_ports; ++i) {
 		mos7840_port = mos7840_get_port_private(serial->port[i]);
-		kfree(mos7840_port->ctrl_buf);
+		spin_lock_irqsave(&mos7840_port->pool_lock, flags);
+		mos7840_port->zombie = 1;
+		spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
 		usb_kill_urb(mos7840_port->control_urb);
+		kfree(mos7840_port->ctrl_buf);
+		kfree(mos7840_port->dr);
 		kfree(mos7840_port);
 		mos7840_set_port_private(serial->port[i], NULL);
 	}
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index 0216ac1..4adfab9 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -69,6 +69,7 @@
 static int  omninet_write		(struct usb_serial_port *port, const unsigned char *buf, int count);
 static int  omninet_write_room		(struct usb_serial_port *port);
 static void omninet_shutdown		(struct usb_serial *serial);
+static int omninet_attach		(struct usb_serial *serial);
 
 static struct usb_device_id id_table [] = {
 	{ USB_DEVICE(ZYXEL_VENDOR_ID, ZYXEL_OMNINET_ID) },
@@ -99,6 +100,7 @@
 	.num_bulk_in =		1,
 	.num_bulk_out =		2,
 	.num_ports =		1,
+	.attach =		omninet_attach,
 	.open =			omninet_open,
 	.close =		omninet_close,
 	.write =		omninet_write,
@@ -145,22 +147,30 @@
 	__u8	od_outseq;	// Sequence number for bulk_out URBs
 };
 
-static int omninet_open (struct usb_serial_port *port, struct file *filp)
+static int omninet_attach (struct usb_serial *serial)
 {
-	struct usb_serial	*serial = port->serial;
-	struct usb_serial_port	*wport;
-	struct omninet_data	*od;
-	int			result = 0;
-
-	dbg("%s - port %d", __FUNCTION__, port->number);
+	struct omninet_data *od;
+	struct usb_serial_port *port = serial->port[0];
 
 	od = kmalloc( sizeof(struct omninet_data), GFP_KERNEL );
 	if( !od ) {
 		err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct omninet_data));
 		return -ENOMEM;
 	}
-
 	usb_set_serial_port_data(port, od);
+	return 0;
+}
+
+static int omninet_open (struct usb_serial_port *port, struct file *filp)
+{
+	struct usb_serial	*serial = port->serial;
+	struct usb_serial_port	*wport;
+	struct omninet_data	*od = usb_get_serial_port_data(port);
+	int			result = 0;
+
+	dbg("%s - port %d", __FUNCTION__, port->number);
+
+	od = kmalloc( sizeof(struct omninet_data), GFP_KERNEL );
 	wport = serial->port[1];
 	wport->tty = port->tty;
 
@@ -170,24 +180,17 @@
 		      port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
 		      omninet_read_bulk_callback, port);
 	result = usb_submit_urb(port->read_urb, GFP_KERNEL);
-	if (result)
+	if (result) {
 		err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
+	}
 
 	return result;
 }
 
 static void omninet_close (struct usb_serial_port *port, struct file * filp)
 {
-	struct usb_serial 	*serial = port->serial;
-	struct usb_serial_port 	*wport;
-
 	dbg("%s - port %d", __FUNCTION__, port->number);
-
-	wport = serial->port[1];
-	usb_kill_urb(wport->write_urb);
 	usb_kill_urb(port->read_urb);
-
-	kfree(usb_get_serial_port_data(port));
 }
 
 
@@ -326,7 +329,12 @@
 
 static void omninet_shutdown (struct usb_serial *serial)
 {
+	struct usb_serial_port *wport = serial->port[1];
+	struct usb_serial_port *port = serial->port[0];
 	dbg ("%s", __FUNCTION__);
+
+	usb_kill_urb(wport->write_urb);
+	kfree(usb_get_serial_port_data(port));
 }
 
 
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index e178e6f..8c3f55b 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -113,6 +113,12 @@
 #define ANYDATA_VENDOR_ID			0x16d5
 #define ANYDATA_PRODUCT_ID			0x6501
 
+#define BANDRICH_VENDOR_ID			0x1A8D
+#define BANDRICH_PRODUCT_C100_1			0x1002
+#define BANDRICH_PRODUCT_C100_2			0x1003
+
+#define DELL_VENDOR_ID				0x413C
+
 static struct usb_device_id option_ids[] = {
 	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
 	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
@@ -165,6 +171,9 @@
 	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2130) }, /* Novatel Merlin ES620 SM Bus */
 	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2410) }, /* Novatel EU740 */
 	{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) },
+	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1) },
+	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2) },
+	{ USB_DEVICE(DELL_VENDOR_ID, 0x8118) },		/* Dell Wireless 5510 Mobile Broadband HSDPA ExpressCard */
 	{ } /* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, option_ids);
@@ -591,12 +600,6 @@
 	return (0);
 }
 
-static inline void stop_urb(struct urb *urb)
-{
-	if (urb && urb->status == -EINPROGRESS)
-		usb_kill_urb(urb);
-}
-
 static void option_close(struct usb_serial_port *port, struct file *filp)
 {
 	int i;
@@ -614,9 +617,9 @@
 
 		/* Stop reading/writing urbs */
 		for (i = 0; i < N_IN_URB; i++)
-			stop_urb(portdata->in_urbs[i]);
+			usb_kill_urb(portdata->in_urbs[i]);
 		for (i = 0; i < N_OUT_URB; i++)
-			stop_urb(portdata->out_urbs[i]);
+			usb_kill_urb(portdata->out_urbs[i]);
 	}
 	port->tty = NULL;
 }
@@ -747,9 +750,9 @@
 		port = serial->port[i];
 		portdata = usb_get_serial_port_data(port);
 		for (j = 0; j < N_IN_URB; j++)
-			stop_urb(portdata->in_urbs[j]);
+			usb_kill_urb(portdata->in_urbs[j]);
 		for (j = 0; j < N_OUT_URB; j++)
-			stop_urb(portdata->out_urbs[j]);
+			usb_kill_urb(portdata->out_urbs[j]);
 	}
 
 	/* Now free them */
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index ecedd83..644607d 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -456,12 +456,6 @@
 	return (0);
 }
 
-static inline void stop_urb(struct urb *urb)
-{
-	if (urb && urb->status == -EINPROGRESS)
-		usb_kill_urb(urb);
-}
-
 static void sierra_close(struct usb_serial_port *port, struct file *filp)
 {
 	int i;
@@ -479,9 +473,9 @@
 
 		/* Stop reading/writing urbs */
 		for (i = 0; i < N_IN_URB; i++)
-			stop_urb(portdata->in_urbs[i]);
+			usb_unlink_urb(portdata->in_urbs[i]);
 		for (i = 0; i < N_OUT_URB; i++)
-			stop_urb(portdata->out_urbs[i]);
+			usb_unlink_urb(portdata->out_urbs[i]);
 	}
 	port->tty = NULL;
 }
@@ -583,17 +577,26 @@
 	/* Stop reading/writing urbs */
 	for (i = 0; i < serial->num_ports; ++i) {
 		port = serial->port[i];
+		if (!port)
+			continue;
 		portdata = usb_get_serial_port_data(port);
+		if (!portdata)
+			continue;
+
 		for (j = 0; j < N_IN_URB; j++)
-			stop_urb(portdata->in_urbs[j]);
+			usb_unlink_urb(portdata->in_urbs[j]);
 		for (j = 0; j < N_OUT_URB; j++)
-			stop_urb(portdata->out_urbs[j]);
+			usb_unlink_urb(portdata->out_urbs[j]);
 	}
 
 	/* Now free them */
 	for (i = 0; i < serial->num_ports; ++i) {
 		port = serial->port[i];
+		if (!port)
+			continue;
 		portdata = usb_get_serial_port_data(port);
+		if (!portdata)
+			continue;
 
 		for (j = 0; j < N_IN_URB; j++) {
 			if (portdata->in_urbs[j]) {
@@ -612,6 +615,8 @@
 	/* Now free per port private data */
 	for (i = 0; i < serial->num_ports; i++) {
 		port = serial->port[i];
+		if (!port)
+			continue;
 		kfree(usb_get_serial_port_data(port));
 	}
 }
diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
index 2f59ff2..ffbe601 100644
--- a/drivers/usb/serial/visor.c
+++ b/drivers/usb/serial/visor.c
@@ -384,19 +384,21 @@
 		dbg("%s - write limit hit\n", __FUNCTION__);
 		return 0;
 	}
+	priv->outstanding_urbs++;
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	buffer = kmalloc (count, GFP_ATOMIC);
 	if (!buffer) {
 		dev_err(&port->dev, "out of memory\n");
-		return -ENOMEM;
+		count = -ENOMEM;
+		goto error_no_buffer;
 	}
 
 	urb = usb_alloc_urb(0, GFP_ATOMIC);
 	if (!urb) {
 		dev_err(&port->dev, "no more free urbs\n");
-		kfree (buffer);
-		return -ENOMEM;
+		count = -ENOMEM;
+		goto error_no_urb;
 	}
 
 	memcpy (buffer, buf, count);
@@ -415,19 +417,27 @@
 		dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed with status = %d\n",
 			__FUNCTION__, status);
 		count = status;
-		kfree (buffer);
+		goto error;
 	} else {
 		spin_lock_irqsave(&priv->lock, flags);
-		++priv->outstanding_urbs;
 		priv->bytes_out += count;
 		spin_unlock_irqrestore(&priv->lock, flags);
 	}
 
 	/* we are done with this urb, so let the host driver
 	 * really free it when it is finished with it */
-	usb_free_urb (urb);
+	usb_free_urb(urb);
 
 	return count;
+error:
+	usb_free_urb(urb);
+error_no_urb:
+	kfree(buffer);
+error_no_buffer:
+	spin_lock_irqsave(&priv->lock, flags);
+	--priv->outstanding_urbs;
+	spin_unlock_irqrestore(&priv->lock, flags);
+	return count;
 }
 
 
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index bf16e9e..27c5f8f 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -1109,7 +1109,7 @@
 	command_port = port->serial->port[COMMAND_PORT];
 	command_info = usb_get_serial_port_data(command_port);
 	spin_lock_irqsave(&command_info->lock, flags);
-	command_info->command_finished = FALSE;
+	command_info->command_finished = false;
 	
 	transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer;
 	transfer_buffer[0] = command;
@@ -1124,12 +1124,12 @@
 	spin_unlock_irqrestore(&command_info->lock, flags);
 
 	/* wait for the command to complete */
-	wait_event_interruptible_timeout(command_info->wait_command, 
-		(command_info->command_finished != FALSE), COMMAND_TIMEOUT);
+	wait_event_interruptible_timeout(command_info->wait_command,
+		(bool)command_info->command_finished, COMMAND_TIMEOUT);
 
 	spin_lock_irqsave(&command_info->lock, flags);
 
-	if (command_info->command_finished == FALSE) {
+	if (command_info->command_finished == false) {
 		dbg("%s - command timed out.", __FUNCTION__);
 		retval = -ETIMEDOUT;
 		goto exit;
diff --git a/drivers/usb/serial/whiteheat.h b/drivers/usb/serial/whiteheat.h
index d714eff..f160797 100644
--- a/drivers/usb/serial/whiteheat.h
+++ b/drivers/usb/serial/whiteheat.h
@@ -20,10 +20,6 @@
 #define __LINUX_USB_SERIAL_WHITEHEAT_H
 
 
-#define FALSE	0
-#define TRUE	1
-
-
 /* WhiteHEAT commands */
 #define WHITEHEAT_OPEN			1	/* open the port */
 #define WHITEHEAT_CLOSE			2	/* close the port */
diff --git a/drivers/usb/storage/libusual.c b/drivers/usb/storage/libusual.c
index 599ad10..06d1107 100644
--- a/drivers/usb/storage/libusual.c
+++ b/drivers/usb/storage/libusual.c
@@ -117,6 +117,7 @@
 static int usu_probe(struct usb_interface *intf,
 			 const struct usb_device_id *id)
 {
+	int rc;
 	unsigned long type;
 	struct task_struct* task;
 	unsigned long flags;
@@ -135,7 +136,7 @@
 
 	task = kthread_run(usu_probe_thread, (void*)type, "libusual_%d", type);
 	if (IS_ERR(task)) {
-		int rc = PTR_ERR(task);
+		rc = PTR_ERR(task);
 		printk(KERN_WARNING "libusual: "
 		    "Unable to start the thread for %s: %d\n",
 		    bias_names[type], rc);
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 4a9d0d5..8b3145ab 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -1371,15 +1371,6 @@
 		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_FL_IGNORE_RESIDUE ),
 
-/* This prevents the kernel from detecting the virtual cd-drive with the
- * Windows drivers.  <johann.wilhelm@student.tugraz.at>
-*/
-UNUSUAL_DEV( 0x12d1, 0x1003, 0x0000, 0xffff,
-		"HUAWEI",
-		"E220 USB-UMTS Install",
-		US_SC_DEVICE, US_PR_DEVICE, NULL,
-		US_FL_IGNORE_DEVICE),
-
 /* Reported by Vilius Bilinkevicius <vilisas AT xxx DOT lt) */
 UNUSUAL_DEV(  0x132b, 0x000b, 0x0001, 0x0001,
 		"Minolta",
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c
index 46929a1..8432bf1 100644
--- a/drivers/usb/usb-skeleton.c
+++ b/drivers/usb/usb-skeleton.c
@@ -34,18 +34,25 @@
 };
 MODULE_DEVICE_TABLE(usb, skel_table);
 
+/* to prevent a race between open and disconnect */
+static DEFINE_MUTEX(skel_open_lock);
+
 
 /* Get a minor range for your devices from the usb maintainer */
 #define USB_SKEL_MINOR_BASE	192
 
 /* our private defines. if this grows any larger, use your own .h file */
 #define MAX_TRANSFER		(PAGE_SIZE - 512)
+/* MAX_TRANSFER is chosen so that the VM is not stressed by
+   allocations > PAGE_SIZE and the number of packets in a page
+   is an integer 512 is the largest possible packet on EHCI */
 #define WRITES_IN_FLIGHT	8
+/* arbitrarily chosen */
 
 /* Structure to hold all of our device specific stuff */
 struct usb_skel {
-	struct usb_device       *dev;			/* the usb device for this device */
-	struct usb_interface    *interface;		/* the interface for this device */
+	struct usb_device	*udev;			/* the usb device for this device */
+	struct usb_interface	*interface;		/* the interface for this device */
 	struct semaphore	limit_sem;		/* limiting the number of writes in progress */
 	unsigned char           *bulk_in_buffer;	/* the buffer to receive data */
 	size_t			bulk_in_size;		/* the size of the receive buffer */
@@ -76,8 +83,10 @@
 
 	subminor = iminor(inode);
 
+	mutex_lock(&skel_open_lock);
 	interface = usb_find_interface(&skel_driver, subminor);
 	if (!interface) {
+		mutex_unlock(&skel_open_lock);
 		err ("%s - error, can't find device for minor %d",
 		     __FUNCTION__, subminor);
 		retval = -ENODEV;
@@ -86,12 +95,15 @@
 
 	dev = usb_get_intfdata(interface);
 	if (!dev) {
+		mutex_unlock(&skel_open_lock);
 		retval = -ENODEV;
 		goto exit;
 	}
 
 	/* increment our usage count for the device */
 	kref_get(&dev->kref);
+	/* now we can drop the lock */
+	mutex_unlock(&skel_open_lock);
 
 	/* prevent the device from being autosuspended */
 	retval = usb_autopm_get_interface(interface);
@@ -201,12 +213,6 @@
 		goto exit;
 	}
 
-	mutex_lock(&dev->io_mutex);
-	if (!dev->interface) {		/* disconnect() was called */
-		retval = -ENODEV;
-		goto error;
-	}
-
 	/* create a urb, and a buffer for it, and copy the data to the urb */
 	urb = usb_alloc_urb(0, GFP_KERNEL);
 	if (!urb) {
@@ -225,6 +231,14 @@
 		goto error;
 	}
 
+	/* this lock makes sure we don't submit URBs to gone devices */
+	mutex_lock(&dev->io_mutex);
+	if (!dev->interface) {		/* disconnect() was called */
+		mutex_unlock(&dev->io_mutex);
+		retval = -ENODEV;
+		goto error;
+	}
+
 	/* initialize the urb properly */
 	usb_fill_bulk_urb(urb, dev->udev,
 			  usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
@@ -233,6 +247,7 @@
 
 	/* send the data out the bulk port */
 	retval = usb_submit_urb(urb, GFP_KERNEL);
+	mutex_unlock(&dev->io_mutex);
 	if (retval) {
 		err("%s - failed submitting write urb, error %d", __FUNCTION__, retval);
 		goto error;
@@ -241,7 +256,7 @@
 	/* release our reference to this urb, the USB core will eventually free it entirely */
 	usb_free_urb(urb);
 
-	mutex_unlock(&dev->io_mutex);
+
 	return writesize;
 
 error:
@@ -249,7 +264,6 @@
 		usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
 		usb_free_urb(urb);
 	}
-	mutex_unlock(&dev->io_mutex);
 	up(&dev->limit_sem);
 
 exit:
@@ -344,6 +358,7 @@
 
 error:
 	if (dev)
+		/* this frees allocated memory */
 		kref_put(&dev->kref, skel_delete);
 	return retval;
 }
@@ -354,20 +369,21 @@
 	int minor = interface->minor;
 
 	/* prevent skel_open() from racing skel_disconnect() */
-	lock_kernel();
+	mutex_lock(&skel_open_lock);
 
 	dev = usb_get_intfdata(interface);
 	usb_set_intfdata(interface, NULL);
 
 	/* give back our minor */
 	usb_deregister_dev(interface, &skel_class);
+	mutex_unlock(&skel_open_lock);
 
 	/* prevent more I/O from starting */
 	mutex_lock(&dev->io_mutex);
 	dev->interface = NULL;
 	mutex_unlock(&dev->io_mutex);
 
-	unlock_kernel();
+
 
 	/* decrement our usage count */
 	kref_put(&dev->kref, skel_delete);
@@ -380,6 +396,7 @@
 	.probe =	skel_probe,
 	.disconnect =	skel_disconnect,
 	.id_table =	skel_table,
+	.supports_autosuspend = 1,
 };
 
 static int __init usb_skel_init(void)
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 8372ace..b1cb72c 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -396,7 +396,7 @@
 
 config FB_OF
 	bool "Open Firmware frame buffer device support"
-	depends on (FB = y) && (PPC64 || PPC_OF)
+	depends on (FB = y) && (PPC64 || PPC_OF) && (!PPC_PSERIES || PCI)
 	select FB_CFB_FILLRECT
 	select FB_CFB_COPYAREA
 	select FB_CFB_IMAGEBLIT
diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c
index d7627fc..8514f2a 100644
--- a/drivers/video/aty/atyfb_base.c
+++ b/drivers/video/aty/atyfb_base.c
@@ -2899,7 +2899,7 @@
 			struct fb_info *info, unsigned long addr)
 {
 	struct atyfb_par *par = info->par;
-	struct pcidev_cookie *pcp;
+	struct device_node *dp;
 	char prop[128];
 	int node, len, i, j, ret;
 	u32 mem, chip_id;
@@ -3037,8 +3037,8 @@
 			node = 0;
 	}
 
-	pcp = pdev->sysdata;
-	if (node == pcp->prom_node->node) {
+	dp = pci_device_to_OF_node(pdev);
+	if (node == dp->node) {
 		struct fb_var_screeninfo *var = &default_var;
 		unsigned int N, P, Q, M, T, R;
 		u32 v_total, h_total;
diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c
index 1bf6f42..a4b3fd1 100644
--- a/drivers/video/aty/radeon_base.c
+++ b/drivers/video/aty/radeon_base.c
@@ -410,7 +410,7 @@
 }
 #endif
 
-#ifdef CONFIG_PPC_OF
+#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
 /*
  * Read XTAL (ref clock), SCLK and MCLK from Open Firmware device
  * tree. Hopefully, ATI OF driver is kind enough to fill these
@@ -440,7 +440,7 @@
 
        	return 0;
 }
-#endif /* CONFIG_PPC_OF */
+#endif /* CONFIG_PPC_OF || CONFIG_SPARC */
 
 /*
  * Read PLL infos from chip registers
@@ -645,7 +645,7 @@
 	rinfo->pll.ref_div = INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK;
 
 
-#ifdef CONFIG_PPC_OF
+#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
 	/*
 	 * Retrieve PLL infos from Open Firmware first
 	 */
@@ -653,7 +653,7 @@
        		printk(KERN_INFO "radeonfb: Retrieved PLL infos from Open Firmware\n");
 		goto found;
 	}
-#endif /* CONFIG_PPC_OF */
+#endif /* CONFIG_PPC_OF || CONFIG_SPARC */
 
 	/*
 	 * Check out if we have an X86 which gave us some PLL informations
@@ -2231,7 +2231,7 @@
 	    rinfo->family == CHIP_FAMILY_RS200)
 		rinfo->errata |= CHIP_ERRATA_PLL_DELAY;
 
-#ifdef CONFIG_PPC_OF
+#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
 	/* On PPC, we obtain the OF device-node pointer to the firmware
 	 * data for this chip
 	 */
@@ -2240,6 +2240,8 @@
 		printk(KERN_WARNING "radeonfb (%s): Cannot match card to OF node !\n",
 		       pci_name(rinfo->pdev));
 
+#endif /* CONFIG_PPC_OF || CONFIG_SPARC */
+#ifdef CONFIG_PPC_OF
 	/* On PPC, the firmware sets up a memory mapping that tends
 	 * to cause lockups when enabling the engine. We reconfigure
 	 * the card internal memory mappings properly
diff --git a/drivers/video/aty/radeon_monitor.c b/drivers/video/aty/radeon_monitor.c
index 38c7dbf..737b5c0 100644
--- a/drivers/video/aty/radeon_monitor.c
+++ b/drivers/video/aty/radeon_monitor.c
@@ -52,7 +52,7 @@
 }
 
 
-#ifdef CONFIG_PPC_OF
+#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
 /*
  * Try to find monitor informations & EDID data out of the Open Firmware
  * device-tree. This also contains some "hacks" to work around a few machine
@@ -156,7 +156,7 @@
 	}
         return MT_NONE;
 }
-#endif /* CONFIG_PPC_OF */
+#endif /* CONFIG_PPC_OF || CONFIG_SPARC */
 
 
 static int __devinit radeon_get_panel_info_BIOS(struct radeonfb_info *rinfo)
@@ -495,11 +495,11 @@
 		 * Old single head cards
 		 */
 		if (!rinfo->has_CRTC2) {
-#ifdef CONFIG_PPC_OF
+#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
 			if (rinfo->mon1_type == MT_NONE)
 				rinfo->mon1_type = radeon_probe_OF_head(rinfo, 0,
 									&rinfo->mon1_EDID);
-#endif /* CONFIG_PPC_OF */
+#endif /* CONFIG_PPC_OF || CONFIG_SPARC */
 #ifdef CONFIG_FB_RADEON_I2C
 			if (rinfo->mon1_type == MT_NONE)
 				rinfo->mon1_type =
@@ -544,11 +544,11 @@
 		/*
 		 * Probe primary head (DVI or laptop internal panel)
 		 */
-#ifdef CONFIG_PPC_OF
+#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
 		if (rinfo->mon1_type == MT_NONE)
 			rinfo->mon1_type = radeon_probe_OF_head(rinfo, 0,
 								&rinfo->mon1_EDID);
-#endif /* CONFIG_PPC_OF */
+#endif /* CONFIG_PPC_OF || CONFIG_SPARC */
 #ifdef CONFIG_FB_RADEON_I2C
 		if (rinfo->mon1_type == MT_NONE)
 			rinfo->mon1_type = radeon_probe_i2c_connector(rinfo, ddc_dvi,
@@ -572,11 +572,11 @@
 		/*
 		 * Probe secondary head (mostly VGA, can be DVI)
 		 */
-#ifdef CONFIG_PPC_OF
+#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
 		if (rinfo->mon2_type == MT_NONE)
 			rinfo->mon2_type = radeon_probe_OF_head(rinfo, 1,
 								&rinfo->mon2_EDID);
-#endif /* CONFIG_PPC_OF */
+#endif /* CONFIG_PPC_OF || defined(CONFIG_SPARC) */
 #ifdef CONFIG_FB_RADEON_I2C
 		if (rinfo->mon2_type == MT_NONE)
 			rinfo->mon2_type = radeon_probe_i2c_connector(rinfo, ddc_vga,
diff --git a/drivers/video/aty/radeon_pm.c b/drivers/video/aty/radeon_pm.c
index 9a2b0d6..c411293 100644
--- a/drivers/video/aty/radeon_pm.c
+++ b/drivers/video/aty/radeon_pm.c
@@ -1262,7 +1262,7 @@
 	/* This is the code for the Aluminium PowerBooks M10 / iBooks M11 */
 	if (rinfo->family == CHIP_FAMILY_RV350) {
 		u32 sdram_mode_reg = rinfo->save_regs[35];
-		static u32 default_mrtable[] =
+		static const u32 default_mrtable[] =
 			{ 0x21320032,
 			  0x21321000, 0xa1321000, 0x21321000, 0xffffffff,
 			  0x21320032, 0xa1320032, 0x21320032, 0xffffffff,
diff --git a/drivers/video/aty/radeonfb.h b/drivers/video/aty/radeonfb.h
index d5ff224..3190003 100644
--- a/drivers/video/aty/radeonfb.h
+++ b/drivers/video/aty/radeonfb.h
@@ -16,7 +16,7 @@
 
 #include <asm/io.h>
 
-#ifdef CONFIG_PPC_OF
+#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
 #include <asm/prom.h>
 #endif
 
@@ -292,7 +292,7 @@
 	unsigned long		fb_local_base;
 
 	struct pci_dev		*pdev;
-#ifdef CONFIG_PPC_OF
+#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
 	struct device_node	*of_node;
 #endif
 
diff --git a/drivers/video/cg3.c b/drivers/video/cg3.c
index 767c850..f042428 100644
--- a/drivers/video/cg3.c
+++ b/drivers/video/cg3.c
@@ -266,7 +266,7 @@
 static void __devinit cg3_rdi_maybe_fixup_var(struct fb_var_screeninfo *var,
 					      struct device_node *dp)
 {
-	char *params;
+	const char *params;
 	char *p;
 	int ww, hh;
 
diff --git a/drivers/video/controlfb.c b/drivers/video/controlfb.c
index fd60dba..8b76273 100644
--- a/drivers/video/controlfb.c
+++ b/drivers/video/controlfb.c
@@ -179,12 +179,14 @@
 int init_module(void)
 {
 	struct device_node *dp;
+	int ret = -ENXIO;
 
-	dp = find_devices("control");
+	dp = of_find_node_by_name(NULL, "control");
 	if (dp != 0 && !control_of_init(dp))
-		return 0;
+		ret = 0;
+	of_node_put(dp);
 
-	return -ENXIO;
+	return ret;
 }
 
 void cleanup_module(void)
@@ -589,16 +591,18 @@
 {
 	struct device_node *dp;
 	char *option = NULL;
+	int ret = -ENXIO;
 
 	if (fb_get_options("controlfb", &option))
 		return -ENODEV;
 	control_setup(option);
 
-	dp = find_devices("control");
+	dp = of_find_node_by_name(NULL, "control");
 	if (dp != 0 && !control_of_init(dp))
-		return 0;
+		ret = 0;
+	of_node_put(dp);
 
-	return -ENXIO;
+	return ret;
 }
 
 module_init(control_init);
diff --git a/drivers/video/igafb.c b/drivers/video/igafb.c
index 90592fb..eb1a481 100644
--- a/drivers/video/igafb.c
+++ b/drivers/video/igafb.c
@@ -44,8 +44,8 @@
 
 #include <asm/io.h>
 
-#ifdef __sparc__
-#include <asm/pbm.h>
+#ifdef CONFIG_SPARC
+#include <asm/prom.h>
 #include <asm/pcic.h>
 #endif
 
@@ -96,7 +96,7 @@
 	.vmode		= FB_VMODE_NONINTERLACED
 };
 
-#ifdef __sparc__
+#ifdef CONFIG_SPARC
 struct fb_var_screeninfo default_var_1024x768 __initdata = {
 	/* 1024x768, 75 Hz, Non-Interlaced (78.75 MHz dotclock) */
 	.xres		= 1024,
@@ -188,7 +188,7 @@
         pci_outb(par, val, reg+1);
 }
 
-#endif /* __sparc__ */
+#endif /* CONFIG_SPARC */
 
 /*
  *  Very important functionality for the JavaEngine1 computer:
@@ -217,7 +217,7 @@
 		iga_outb(par, 0, IGA_EXT_CNTRL, IGA_IDX_OVERSCAN_COLOR + i);
 }
 
-#ifdef __sparc__
+#ifdef CONFIG_SPARC
 static int igafb_mmap(struct fb_info *info,
 		      struct vm_area_struct *vma)
 {
@@ -271,7 +271,7 @@
 	vma->vm_flags |= VM_IO;
 	return 0;
 }
-#endif /* __sparc__ */
+#endif /* CONFIG_SPARC */
 
 static int igafb_setcolreg(unsigned regno, unsigned red, unsigned green,
                            unsigned blue, unsigned transp,
@@ -323,7 +323,7 @@
 	.fb_fillrect	= cfb_fillrect,
 	.fb_copyarea	= cfb_copyarea,
 	.fb_imageblit	= cfb_imageblit,
-#ifdef __sparc__
+#ifdef CONFIG_SPARC
 	.fb_mmap 	= igafb_mmap,
 #endif
 };
@@ -424,7 +424,7 @@
 
 	par->frame_buffer_phys = addr & PCI_BASE_ADDRESS_MEM_MASK;
 
-#ifdef __sparc__
+#ifdef CONFIG_SPARC
 	/*
 	 * The following is sparc specific and this is why:
 	 *
@@ -477,8 +477,8 @@
 	 * Set default vmode and cmode from PROM properties.
 	 */
 	{
-                struct pcidev_cookie *cookie = pdev->sysdata;
-                int node = cookie->prom_node;
+		struct device_node *dp = pci_device_to_OF_node(pdev);
+                int node = dp->node;
                 int width = prom_getintdefault(node, "width", 1024);
                 int height = prom_getintdefault(node, "height", 768);
                 int depth = prom_getintdefault(node, "depth", 8);
@@ -534,7 +534,7 @@
 		kfree(info);
         }
 
-#ifdef __sparc__
+#ifdef CONFIG_SPARC
 	    /*
 	     * Add /dev/fb mmap values.
 	     */
@@ -552,7 +552,7 @@
 	    par->mmap_map[1].size = PAGE_SIZE * 2; /* X wants 2 pages */
 	    par->mmap_map[1].prot_mask = SRMMU_CACHE;
 	    par->mmap_map[1].prot_flag = SRMMU_WRITE;
-#endif /* __sparc__ */
+#endif /* CONFIG_SPARC */
 
 	return 0;
 }
diff --git a/fs/Kconfig b/fs/Kconfig
index 3c4886b..a42f767 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -2019,7 +2019,8 @@
 config AFS_FS
 	tristate "Andrew File System support (AFS) (EXPERIMENTAL)"
 	depends on INET && EXPERIMENTAL
-	select RXRPC
+	select AF_RXRPC
+	select KEYS
 	help
 	  If you say Y here, you will get an experimental Andrew File System
 	  driver. It currently only supports unsecured read-only AFS access.
@@ -2028,8 +2029,15 @@
 
 	  If unsure, say N.
 
-config RXRPC
-	tristate
+config AFS_DEBUG
+	bool "AFS dynamic debugging"
+	depends on AFS_FS
+	help
+	  Say Y here to make runtime controllable debugging messages appear.
+
+	  See <file:Documentation/filesystems/afs.txt> for more information.
+
+	  If unsure, say N.
 
 config 9P_FS
 	tristate "Plan 9 Resource Sharing Support (9P2000) (Experimental)"
diff --git a/fs/afs/Makefile b/fs/afs/Makefile
index 4029c9d..01545eb 100644
--- a/fs/afs/Makefile
+++ b/fs/afs/Makefile
@@ -2,8 +2,6 @@
 # Makefile for Red Hat Linux AFS client.
 #
 
-#CFLAGS += -finstrument-functions
-
 kafs-objs := \
 	callback.o \
 	cell.o \
@@ -12,14 +10,15 @@
 	file.o \
 	fsclient.o \
 	inode.o \
-	kafsasyncd.o \
-	kafstimod.o \
 	main.o \
 	misc.o \
 	mntpt.o \
 	proc.o \
+	rxrpc.o \
+	security.o \
 	server.o \
 	super.o \
+	use-rtnetlink.o \
 	vlclient.o \
 	vlocation.o \
 	vnode.o \
diff --git a/fs/afs/afs.h b/fs/afs/afs.h
new file mode 100644
index 0000000..52d0752
--- /dev/null
+++ b/fs/afs/afs.h
@@ -0,0 +1,146 @@
+/* AFS common types
+ *
+ * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#ifndef AFS_H
+#define AFS_H
+
+#include <linux/in.h>
+
+#define AFS_MAXCELLNAME	64		/* maximum length of a cell name */
+#define AFS_MAXVOLNAME	64		/* maximum length of a volume name */
+
+typedef unsigned			afs_volid_t;
+typedef unsigned			afs_vnodeid_t;
+typedef unsigned long long		afs_dataversion_t;
+
+typedef enum {
+	AFSVL_RWVOL,			/* read/write volume */
+	AFSVL_ROVOL,			/* read-only volume */
+	AFSVL_BACKVOL,			/* backup volume */
+} __attribute__((packed)) afs_voltype_t;
+
+typedef enum {
+	AFS_FTYPE_INVALID	= 0,
+	AFS_FTYPE_FILE		= 1,
+	AFS_FTYPE_DIR		= 2,
+	AFS_FTYPE_SYMLINK	= 3,
+} afs_file_type_t;
+
+/*
+ * AFS file identifier
+ */
+struct afs_fid {
+	afs_volid_t	vid;		/* volume ID */
+	afs_vnodeid_t	vnode;		/* file index within volume */
+	unsigned	unique;		/* unique ID number (file index version) */
+};
+
+/*
+ * AFS callback notification
+ */
+typedef enum {
+	AFSCM_CB_UNTYPED	= 0,	/* no type set on CB break */
+	AFSCM_CB_EXCLUSIVE	= 1,	/* CB exclusive to CM [not implemented] */
+	AFSCM_CB_SHARED		= 2,	/* CB shared by other CM's */
+	AFSCM_CB_DROPPED	= 3,	/* CB promise cancelled by file server */
+} afs_callback_type_t;
+
+struct afs_callback {
+	struct afs_fid		fid;		/* file identifier */
+	unsigned		version;	/* callback version */
+	unsigned		expiry;		/* time at which expires */
+	afs_callback_type_t	type;		/* type of callback */
+};
+
+#define AFSCBMAX 50	/* maximum callbacks transferred per bulk op */
+
+/*
+ * AFS volume information
+ */
+struct afs_volume_info {
+	afs_volid_t		vid;		/* volume ID */
+	afs_voltype_t		type;		/* type of this volume */
+	afs_volid_t		type_vids[5];	/* volume ID's for possible types for this vol */
+
+	/* list of fileservers serving this volume */
+	size_t			nservers;	/* number of entries used in servers[] */
+	struct {
+		struct in_addr	addr;		/* fileserver address */
+	} servers[8];
+};
+
+/*
+ * AFS security ACE access mask
+ */
+typedef u32 afs_access_t;
+#define AFS_ACE_READ		0x00000001U	/* - permission to read a file/dir */
+#define AFS_ACE_WRITE		0x00000002U	/* - permission to write/chmod a file */
+#define AFS_ACE_INSERT		0x00000004U	/* - permission to create dirent in a dir */
+#define AFS_ACE_LOOKUP		0x00000008U	/* - permission to lookup a file/dir in a dir */
+#define AFS_ACE_DELETE		0x00000010U	/* - permission to delete a dirent from a dir */
+#define AFS_ACE_LOCK		0x00000020U	/* - permission to lock a file */
+#define AFS_ACE_ADMINISTER	0x00000040U	/* - permission to change ACL */
+#define AFS_ACE_USER_A		0x01000000U	/* - 'A' user-defined permission */
+#define AFS_ACE_USER_B		0x02000000U	/* - 'B' user-defined permission */
+#define AFS_ACE_USER_C		0x04000000U	/* - 'C' user-defined permission */
+#define AFS_ACE_USER_D		0x08000000U	/* - 'D' user-defined permission */
+#define AFS_ACE_USER_E		0x10000000U	/* - 'E' user-defined permission */
+#define AFS_ACE_USER_F		0x20000000U	/* - 'F' user-defined permission */
+#define AFS_ACE_USER_G		0x40000000U	/* - 'G' user-defined permission */
+#define AFS_ACE_USER_H		0x80000000U	/* - 'H' user-defined permission */
+
+/*
+ * AFS file status information
+ */
+struct afs_file_status {
+	unsigned		if_version;	/* interface version */
+#define AFS_FSTATUS_VERSION	1
+
+	afs_file_type_t		type;		/* file type */
+	unsigned		nlink;		/* link count */
+	u64			size;		/* file size */
+	afs_dataversion_t	data_version;	/* current data version */
+	u32			author;		/* author ID */
+	u32			owner;		/* owner ID */
+	u32			group;		/* group ID */
+	afs_access_t		caller_access;	/* access rights for authenticated caller */
+	afs_access_t		anon_access;	/* access rights for unauthenticated caller */
+	umode_t			mode;		/* UNIX mode */
+	struct afs_fid		parent;		/* parent dir ID for non-dirs only */
+	time_t			mtime_client;	/* last time client changed data */
+	time_t			mtime_server;	/* last time server changed data */
+};
+
+/*
+ * AFS file status change request
+ */
+struct afs_store_status {
+	u32			mask;		/* which bits of the struct are set */
+	u32			mtime_client;	/* last time client changed data */
+	u32			owner;		/* owner ID */
+	u32			group;		/* group ID */
+	umode_t			mode;		/* UNIX mode */
+};
+
+#define AFS_SET_MTIME		0x01		/* set the mtime */
+#define AFS_SET_OWNER		0x02		/* set the owner ID */
+#define AFS_SET_GROUP		0x04		/* set the group ID (unsupported?) */
+#define AFS_SET_MODE		0x08		/* set the UNIX mode */
+#define AFS_SET_SEG_SIZE	0x10		/* set the segment size (unsupported) */
+
+/*
+ * AFS volume synchronisation information
+ */
+struct afs_volsync {
+	time_t			creation;	/* volume creation time */
+};
+
+#endif /* AFS_H */
diff --git a/fs/afs/afs_cm.h b/fs/afs/afs_cm.h
new file mode 100644
index 0000000..7b4d4fa
--- /dev/null
+++ b/fs/afs/afs_cm.h
@@ -0,0 +1,32 @@
+/* AFS Cache Manager definitions
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#ifndef AFS_CM_H
+#define AFS_CM_H
+
+#define AFS_CM_PORT		7001	/* AFS file server port */
+#define CM_SERVICE		1	/* AFS File Service ID */
+
+enum AFS_CM_Operations {
+	CBCallBack		= 204,	/* break callback promises */
+	CBInitCallBackState	= 205,	/* initialise callback state */
+	CBProbe			= 206,	/* probe client */
+	CBGetLock		= 207,	/* get contents of CM lock table */
+	CBGetCE			= 208,	/* get cache file description */
+	CBGetXStatsVersion	= 209,	/* get version of extended statistics */
+	CBGetXStats		= 210,	/* get contents of extended statistics data */
+	CBInitCallBackState3	= 213,	/* initialise callback state, version 3 */
+	CBGetCapabilities	= 65538, /* get client capabilities */
+};
+
+#define AFS_CAP_ERROR_TRANSLATION	0x1
+
+#endif /* AFS_FS_H */
diff --git a/fs/afs/afs_fs.h b/fs/afs/afs_fs.h
new file mode 100644
index 0000000..89e0d16
--- /dev/null
+++ b/fs/afs/afs_fs.h
@@ -0,0 +1,48 @@
+/* AFS File Service definitions
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#ifndef AFS_FS_H
+#define AFS_FS_H
+
+#define AFS_FS_PORT		7000	/* AFS file server port */
+#define FS_SERVICE		1	/* AFS File Service ID */
+
+enum AFS_FS_Operations {
+	FSFETCHDATA		= 130,	/* AFS Fetch file data */
+	FSFETCHSTATUS		= 132,	/* AFS Fetch file status */
+	FSREMOVEFILE		= 136,	/* AFS Remove a file */
+	FSCREATEFILE		= 137,	/* AFS Create a file */
+	FSRENAME		= 138,	/* AFS Rename or move a file or directory */
+	FSSYMLINK		= 139,	/* AFS Create a symbolic link */
+	FSLINK			= 140,	/* AFS Create a hard link */
+	FSMAKEDIR		= 141,	/* AFS Create a directory */
+	FSREMOVEDIR		= 142,	/* AFS Remove a directory */
+	FSGIVEUPCALLBACKS	= 147,	/* AFS Discard callback promises */
+	FSGETVOLUMEINFO		= 148,	/* AFS Get root volume information */
+	FSGETROOTVOLUME		= 151,	/* AFS Get root volume name */
+	FSLOOKUP		= 161,	/* AFS lookup file in directory */
+};
+
+enum AFS_FS_Errors {
+	VSALVAGE	= 101,	/* volume needs salvaging */
+	VNOVNODE	= 102,	/* no such file/dir (vnode) */
+	VNOVOL		= 103,	/* no such volume or volume unavailable */
+	VVOLEXISTS	= 104,	/* volume name already exists */
+	VNOSERVICE	= 105,	/* volume not currently in service */
+	VOFFLINE	= 106,	/* volume is currently offline (more info available [VVL-spec]) */
+	VONLINE		= 107,	/* volume is already online */
+	VDISKFULL	= 108,	/* disk partition is full */
+	VOVERQUOTA	= 109,	/* volume's maximum quota exceeded */
+	VBUSY		= 110,	/* volume is temporarily unavailable */
+	VMOVED		= 111,	/* volume moved to new server - ask this FS where */
+};
+
+#endif /* AFS_FS_H */
diff --git a/fs/afs/vlclient.h b/fs/afs/afs_vl.h
similarity index 74%
rename from fs/afs/vlclient.h
rename to fs/afs/afs_vl.h
index e3d6011..8bbefe0 100644
--- a/fs/afs/vlclient.h
+++ b/fs/afs/afs_vl.h
@@ -1,6 +1,6 @@
-/* vlclient.h: Volume Location Service client interface
+/* AFS Volume Location Service client interface
  *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
@@ -9,10 +9,19 @@
  * 2 of the License, or (at your option) any later version.
  */
 
-#ifndef _LINUX_AFS_VLCLIENT_H
-#define _LINUX_AFS_VLCLIENT_H
+#ifndef AFS_VL_H
+#define AFS_VL_H
 
-#include "types.h"
+#include "afs.h"
+
+#define AFS_VL_PORT		7003	/* volume location service port */
+#define VL_SERVICE		52	/* RxRPC service ID for the Volume Location service */
+
+enum AFSVL_Operations {
+	VLGETENTRYBYID		= 503,	/* AFS Get Cache Entry By ID operation ID */
+	VLGETENTRYBYNAME	= 504,	/* AFS Get Cache Entry By Name operation ID */
+	VLPROBE			= 514,	/* AFS Probe Volume Location Service operation ID */
+};
 
 enum AFSVL_Errors {
 	AFSVL_IDEXIST 		= 363520,	/* Volume Id entry exists in vl database */
@@ -40,14 +49,16 @@
 	AFSVL_BADVOLOPER 	= 363542,	/* Bad volume operation code */
 	AFSVL_BADRELLOCKTYPE 	= 363543,	/* Bad release lock type */
 	AFSVL_RERELEASE 	= 363544,	/* Status report: last release was aborted */
-	AFSVL_BADSERVERFLAG 	= 363545,	/* Invalid replication site server °ag */
+	AFSVL_BADSERVERFLAG 	= 363545,	/* Invalid replication site server °ag */
 	AFSVL_PERM 		= 363546,	/* No permission access */
 	AFSVL_NOMEM 		= 363547,	/* malloc/realloc failed to alloc enough memory */
 };
 
-/* maps to "struct vldbentry" in vvl-spec.pdf */
+/*
+ * maps to "struct vldbentry" in vvl-spec.pdf
+ */
 struct afs_vldbentry {
-	char		name[65];		/* name of volume (including NUL char) */
+	char		name[65];		/* name of volume (with NUL char) */
 	afs_voltype_t	type;			/* volume type */
 	unsigned	num_servers;		/* num servers that hold instances of this vol */
 	unsigned	clone_id;		/* cloning ID */
@@ -68,26 +79,6 @@
 #define AFS_VLSF_RWVOL		0x0004	/* this server holds a R/W instance of the volume */
 #define AFS_VLSF_BACKVOL	0x0008	/* this server holds a backup instance of the volume */
 	} servers[8];
-
 };
 
-/* look up a volume location database entry by name */
-extern int afs_rxvl_get_entry_by_name(struct afs_server *server,
-				      const char *volname,
-				      unsigned volnamesz,
-				      struct afs_cache_vlocation *entry);
-
-/* look up a volume location database entry by ID */
-extern int afs_rxvl_get_entry_by_id(struct afs_server *server,
-				    afs_volid_t	volid,
-				    afs_voltype_t voltype,
-				    struct afs_cache_vlocation *entry);
-
-extern int afs_rxvl_get_entry_by_id_async(struct afs_async_op *op,
-					  afs_volid_t volid,
-					  afs_voltype_t voltype);
-
-extern int afs_rxvl_get_entry_by_id_async2(struct afs_async_op *op,
-					   struct afs_cache_vlocation *entry);
-
-#endif /* _LINUX_AFS_VLCLIENT_H */
+#endif /* AFS_VL_H */
diff --git a/fs/afs/cache.c b/fs/afs/cache.c
new file mode 100644
index 0000000..de0d7de
--- /dev/null
+++ b/fs/afs/cache.c
@@ -0,0 +1,256 @@
+/* AFS caching stuff
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#ifdef AFS_CACHING_SUPPORT
+static cachefs_match_val_t afs_cell_cache_match(void *target,
+						const void *entry);
+static void afs_cell_cache_update(void *source, void *entry);
+
+struct cachefs_index_def afs_cache_cell_index_def = {
+	.name			= "cell_ix",
+	.data_size		= sizeof(struct afs_cache_cell),
+	.keys[0]		= { CACHEFS_INDEX_KEYS_ASCIIZ, 64 },
+	.match			= afs_cell_cache_match,
+	.update			= afs_cell_cache_update,
+};
+#endif
+
+/*
+ * match a cell record obtained from the cache
+ */
+#ifdef AFS_CACHING_SUPPORT
+static cachefs_match_val_t afs_cell_cache_match(void *target,
+						const void *entry)
+{
+	const struct afs_cache_cell *ccell = entry;
+	struct afs_cell *cell = target;
+
+	_enter("{%s},{%s}", ccell->name, cell->name);
+
+	if (strncmp(ccell->name, cell->name, sizeof(ccell->name)) == 0) {
+		_leave(" = SUCCESS");
+		return CACHEFS_MATCH_SUCCESS;
+	}
+
+	_leave(" = FAILED");
+	return CACHEFS_MATCH_FAILED;
+}
+#endif
+
+/*
+ * update a cell record in the cache
+ */
+#ifdef AFS_CACHING_SUPPORT
+static void afs_cell_cache_update(void *source, void *entry)
+{
+	struct afs_cache_cell *ccell = entry;
+	struct afs_cell *cell = source;
+
+	_enter("%p,%p", source, entry);
+
+	strncpy(ccell->name, cell->name, sizeof(ccell->name));
+
+	memcpy(ccell->vl_servers,
+	       cell->vl_addrs,
+	       min(sizeof(ccell->vl_servers), sizeof(cell->vl_addrs)));
+
+}
+#endif
+
+#ifdef AFS_CACHING_SUPPORT
+static cachefs_match_val_t afs_vlocation_cache_match(void *target,
+						     const void *entry);
+static void afs_vlocation_cache_update(void *source, void *entry);
+
+struct cachefs_index_def afs_vlocation_cache_index_def = {
+	.name		= "vldb",
+	.data_size	= sizeof(struct afs_cache_vlocation),
+	.keys[0]	= { CACHEFS_INDEX_KEYS_ASCIIZ, 64 },
+	.match		= afs_vlocation_cache_match,
+	.update		= afs_vlocation_cache_update,
+};
+#endif
+
+/*
+ * match a VLDB record stored in the cache
+ * - may also load target from entry
+ */
+#ifdef AFS_CACHING_SUPPORT
+static cachefs_match_val_t afs_vlocation_cache_match(void *target,
+						     const void *entry)
+{
+	const struct afs_cache_vlocation *vldb = entry;
+	struct afs_vlocation *vlocation = target;
+
+	_enter("{%s},{%s}", vlocation->vldb.name, vldb->name);
+
+	if (strncmp(vlocation->vldb.name, vldb->name, sizeof(vldb->name)) == 0
+	    ) {
+		if (!vlocation->valid ||
+		    vlocation->vldb.rtime == vldb->rtime
+		    ) {
+			vlocation->vldb = *vldb;
+			vlocation->valid = 1;
+			_leave(" = SUCCESS [c->m]");
+			return CACHEFS_MATCH_SUCCESS;
+		} else if (memcmp(&vlocation->vldb, vldb, sizeof(*vldb)) != 0) {
+			/* delete if VIDs for this name differ */
+			if (memcmp(&vlocation->vldb.vid,
+				   &vldb->vid,
+				   sizeof(vldb->vid)) != 0) {
+				_leave(" = DELETE");
+				return CACHEFS_MATCH_SUCCESS_DELETE;
+			}
+
+			_leave(" = UPDATE");
+			return CACHEFS_MATCH_SUCCESS_UPDATE;
+		} else {
+			_leave(" = SUCCESS");
+			return CACHEFS_MATCH_SUCCESS;
+		}
+	}
+
+	_leave(" = FAILED");
+	return CACHEFS_MATCH_FAILED;
+}
+#endif
+
+/*
+ * update a VLDB record stored in the cache
+ */
+#ifdef AFS_CACHING_SUPPORT
+static void afs_vlocation_cache_update(void *source, void *entry)
+{
+	struct afs_cache_vlocation *vldb = entry;
+	struct afs_vlocation *vlocation = source;
+
+	_enter("");
+
+	*vldb = vlocation->vldb;
+}
+#endif
+
+#ifdef AFS_CACHING_SUPPORT
+static cachefs_match_val_t afs_volume_cache_match(void *target,
+						  const void *entry);
+static void afs_volume_cache_update(void *source, void *entry);
+
+struct cachefs_index_def afs_volume_cache_index_def = {
+	.name		= "volume",
+	.data_size	= sizeof(struct afs_cache_vhash),
+	.keys[0]	= { CACHEFS_INDEX_KEYS_BIN, 1 },
+	.keys[1]	= { CACHEFS_INDEX_KEYS_BIN, 1 },
+	.match		= afs_volume_cache_match,
+	.update		= afs_volume_cache_update,
+};
+#endif
+
+/*
+ * match a volume hash record stored in the cache
+ */
+#ifdef AFS_CACHING_SUPPORT
+static cachefs_match_val_t afs_volume_cache_match(void *target,
+						  const void *entry)
+{
+	const struct afs_cache_vhash *vhash = entry;
+	struct afs_volume *volume = target;
+
+	_enter("{%u},{%u}", volume->type, vhash->vtype);
+
+	if (volume->type == vhash->vtype) {
+		_leave(" = SUCCESS");
+		return CACHEFS_MATCH_SUCCESS;
+	}
+
+	_leave(" = FAILED");
+	return CACHEFS_MATCH_FAILED;
+}
+#endif
+
+/*
+ * update a volume hash record stored in the cache
+ */
+#ifdef AFS_CACHING_SUPPORT
+static void afs_volume_cache_update(void *source, void *entry)
+{
+	struct afs_cache_vhash *vhash = entry;
+	struct afs_volume *volume = source;
+
+	_enter("");
+
+	vhash->vtype = volume->type;
+}
+#endif
+
+#ifdef AFS_CACHING_SUPPORT
+static cachefs_match_val_t afs_vnode_cache_match(void *target,
+						 const void *entry);
+static void afs_vnode_cache_update(void *source, void *entry);
+
+struct cachefs_index_def afs_vnode_cache_index_def = {
+	.name		= "vnode",
+	.data_size	= sizeof(struct afs_cache_vnode),
+	.keys[0]	= { CACHEFS_INDEX_KEYS_BIN, 4 },
+	.match		= afs_vnode_cache_match,
+	.update		= afs_vnode_cache_update,
+};
+#endif
+
+/*
+ * match a vnode record stored in the cache
+ */
+#ifdef AFS_CACHING_SUPPORT
+static cachefs_match_val_t afs_vnode_cache_match(void *target,
+						 const void *entry)
+{
+	const struct afs_cache_vnode *cvnode = entry;
+	struct afs_vnode *vnode = target;
+
+	_enter("{%x,%x,%Lx},{%x,%x,%Lx}",
+	       vnode->fid.vnode,
+	       vnode->fid.unique,
+	       vnode->status.version,
+	       cvnode->vnode_id,
+	       cvnode->vnode_unique,
+	       cvnode->data_version);
+
+	if (vnode->fid.vnode != cvnode->vnode_id) {
+		_leave(" = FAILED");
+		return CACHEFS_MATCH_FAILED;
+	}
+
+	if (vnode->fid.unique != cvnode->vnode_unique ||
+	    vnode->status.version != cvnode->data_version) {
+		_leave(" = DELETE");
+		return CACHEFS_MATCH_SUCCESS_DELETE;
+	}
+
+	_leave(" = SUCCESS");
+	return CACHEFS_MATCH_SUCCESS;
+}
+#endif
+
+/*
+ * update a vnode record stored in the cache
+ */
+#ifdef AFS_CACHING_SUPPORT
+static void afs_vnode_cache_update(void *source, void *entry)
+{
+	struct afs_cache_vnode *cvnode = entry;
+	struct afs_vnode *vnode = source;
+
+	_enter("");
+
+	cvnode->vnode_id	= vnode->fid.vnode;
+	cvnode->vnode_unique	= vnode->fid.unique;
+	cvnode->data_version	= vnode->status.version;
+}
+#endif
diff --git a/fs/afs/cache.h b/fs/afs/cache.h
index 9eb7722..36a3642 100644
--- a/fs/afs/cache.h
+++ b/fs/afs/cache.h
@@ -1,4 +1,4 @@
-/* cache.h: AFS local cache management interface
+/* AFS local cache management interface
  *
  * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
@@ -9,8 +9,8 @@
  * 2 of the License, or (at your option) any later version.
  */
 
-#ifndef _LINUX_AFS_CACHE_H
-#define _LINUX_AFS_CACHE_H
+#ifndef AFS_CACHE_H
+#define AFS_CACHE_H
 
 #undef AFS_CACHING_SUPPORT
 
@@ -20,8 +20,4 @@
 #endif
 #include "types.h"
 
-#ifdef __KERNEL__
-
-#endif /* __KERNEL__ */
-
-#endif /* _LINUX_AFS_CACHE_H */
+#endif /* AFS_CACHE_H */
diff --git a/fs/afs/callback.c b/fs/afs/callback.c
index 9cb206e..639399f 100644
--- a/fs/afs/callback.c
+++ b/fs/afs/callback.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002 Red Hat, Inc. All rights reserved.
+ * Copyright (c) 2002, 2007 Red Hat, Inc. All rights reserved.
  *
  * This software may be freely redistributed under the terms of the
  * GNU General Public License.
@@ -16,85 +16,187 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
-#include "server.h"
-#include "vnode.h"
+#include <linux/circ_buf.h>
 #include "internal.h"
-#include "cmservice.h"
 
-/*****************************************************************************/
+unsigned afs_vnode_update_timeout = 10;
+
+#define afs_breakring_space(server) \
+	CIRC_SPACE((server)->cb_break_head, (server)->cb_break_tail,	\
+		   ARRAY_SIZE((server)->cb_break))
+
+//static void afs_callback_updater(struct work_struct *);
+
+static struct workqueue_struct *afs_callback_update_worker;
+
 /*
  * allow the fileserver to request callback state (re-)initialisation
  */
-int SRXAFSCM_InitCallBackState(struct afs_server *server)
+void afs_init_callback_state(struct afs_server *server)
 {
-	struct list_head callbacks;
+	struct afs_vnode *vnode;
 
-	_enter("%p", server);
+	_enter("{%p}", server);
 
-	INIT_LIST_HEAD(&callbacks);
-
-	/* transfer the callback list from the server to a temp holding area */
 	spin_lock(&server->cb_lock);
 
-	list_add(&callbacks, &server->cb_promises);
-	list_del_init(&server->cb_promises);
-
-	/* munch our way through the list, grabbing the inode, dropping all the
-	 * locks and regetting them in the right order
-	 */
-	while (!list_empty(&callbacks)) {
-		struct afs_vnode *vnode;
-		struct inode *inode;
-
-		vnode = list_entry(callbacks.next, struct afs_vnode, cb_link);
-		list_del_init(&vnode->cb_link);
-
-		/* try and grab the inode - may fail */
-		inode = igrab(AFS_VNODE_TO_I(vnode));
-		if (inode) {
-			int release = 0;
-
-			spin_unlock(&server->cb_lock);
-			spin_lock(&vnode->lock);
-
-			if (vnode->cb_server == server) {
-				vnode->cb_server = NULL;
-				afs_kafstimod_del_timer(&vnode->cb_timeout);
-				spin_lock(&afs_cb_hash_lock);
-				list_del_init(&vnode->cb_hash_link);
-				spin_unlock(&afs_cb_hash_lock);
-				release = 1;
-			}
-
-			spin_unlock(&vnode->lock);
-
-			iput(inode);
-			afs_put_server(server);
-
-			spin_lock(&server->cb_lock);
-		}
+	/* kill all the promises on record from this server */
+	while (!RB_EMPTY_ROOT(&server->cb_promises)) {
+		vnode = rb_entry(server->cb_promises.rb_node,
+				 struct afs_vnode, cb_promise);
+		_debug("UNPROMISE { vid=%x vn=%u uq=%u}",
+		       vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
+		rb_erase(&vnode->cb_promise, &server->cb_promises);
+		vnode->cb_promised = false;
 	}
 
 	spin_unlock(&server->cb_lock);
+	_leave("");
+}
 
-	_leave(" = 0");
-	return 0;
-} /* end SRXAFSCM_InitCallBackState() */
+/*
+ * handle the data invalidation side of a callback being broken
+ */
+void afs_broken_callback_work(struct work_struct *work)
+{
+	struct afs_vnode *vnode =
+		container_of(work, struct afs_vnode, cb_broken_work);
 
-/*****************************************************************************/
+	_enter("");
+
+	if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
+		return;
+
+	/* we're only interested in dealing with a broken callback on *this*
+	 * vnode and only if no-one else has dealt with it yet */
+	if (!mutex_trylock(&vnode->validate_lock))
+		return; /* someone else is dealing with it */
+
+	if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) {
+		if (S_ISDIR(vnode->vfs_inode.i_mode))
+			afs_clear_permits(vnode);
+
+		if (afs_vnode_fetch_status(vnode, NULL, NULL) < 0)
+			goto out;
+
+		if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
+			goto out;
+
+		/* if the vnode's data version number changed then its contents
+		 * are different */
+		if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) {
+			_debug("zap data {%x:%u}",
+			       vnode->fid.vid, vnode->fid.vnode);
+			invalidate_remote_inode(&vnode->vfs_inode);
+		}
+	}
+
+out:
+	mutex_unlock(&vnode->validate_lock);
+
+	/* avoid the potential race whereby the mutex_trylock() in this
+	 * function happens again between the clear_bit() and the
+	 * mutex_unlock() */
+	if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) {
+		_debug("requeue");
+		queue_work(afs_callback_update_worker, &vnode->cb_broken_work);
+	}
+	_leave("");
+}
+
+/*
+ * actually break a callback
+ */
+static void afs_break_callback(struct afs_server *server,
+			       struct afs_vnode *vnode)
+{
+	_enter("");
+
+	set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
+
+	if (vnode->cb_promised) {
+		spin_lock(&vnode->lock);
+
+		_debug("break callback");
+
+		spin_lock(&server->cb_lock);
+		if (vnode->cb_promised) {
+			rb_erase(&vnode->cb_promise, &server->cb_promises);
+			vnode->cb_promised = false;
+		}
+		spin_unlock(&server->cb_lock);
+
+		queue_work(afs_callback_update_worker, &vnode->cb_broken_work);
+		spin_unlock(&vnode->lock);
+	}
+}
+
+/*
+ * allow the fileserver to explicitly break one callback
+ * - happens when
+ *   - the backing file is changed
+ *   - a lock is released
+ */
+static void afs_break_one_callback(struct afs_server *server,
+				   struct afs_fid *fid)
+{
+	struct afs_vnode *vnode;
+	struct rb_node *p;
+
+	_debug("find");
+	spin_lock(&server->fs_lock);
+	p = server->fs_vnodes.rb_node;
+	while (p) {
+		vnode = rb_entry(p, struct afs_vnode, server_rb);
+		if (fid->vid < vnode->fid.vid)
+			p = p->rb_left;
+		else if (fid->vid > vnode->fid.vid)
+			p = p->rb_right;
+		else if (fid->vnode < vnode->fid.vnode)
+			p = p->rb_left;
+		else if (fid->vnode > vnode->fid.vnode)
+			p = p->rb_right;
+		else if (fid->unique < vnode->fid.unique)
+			p = p->rb_left;
+		else if (fid->unique > vnode->fid.unique)
+			p = p->rb_right;
+		else
+			goto found;
+	}
+
+	/* not found so we just ignore it (it may have moved to another
+	 * server) */
+not_available:
+	_debug("not avail");
+	spin_unlock(&server->fs_lock);
+	_leave("");
+	return;
+
+found:
+	_debug("found");
+	ASSERTCMP(server, ==, vnode->server);
+
+	if (!igrab(AFS_VNODE_TO_I(vnode)))
+		goto not_available;
+	spin_unlock(&server->fs_lock);
+
+	afs_break_callback(server, vnode);
+	iput(&vnode->vfs_inode);
+	_leave("");
+}
+
 /*
  * allow the fileserver to break callback promises
  */
-int SRXAFSCM_CallBack(struct afs_server *server, size_t count,
-		      struct afs_callback callbacks[])
+void afs_break_callbacks(struct afs_server *server, size_t count,
+			 struct afs_callback callbacks[])
 {
-	_enter("%p,%u,", server, count);
+	_enter("%p,%zu,", server, count);
+
+	ASSERT(server != NULL);
+	ASSERTCMP(count, <=, AFSCBMAX);
 
 	for (; count > 0; callbacks++, count--) {
-		struct afs_vnode *vnode = NULL;
-		struct inode *inode = NULL;
-		int valid = 0;
-
 		_debug("- Fid { vl=%08x n=%u u=%u }  CB { v=%u x=%u t=%u }",
 		       callbacks->fid.vid,
 		       callbacks->fid.vnode,
@@ -103,67 +205,270 @@
 		       callbacks->expiry,
 		       callbacks->type
 		       );
-
-		/* find the inode for this fid */
-		spin_lock(&afs_cb_hash_lock);
-
-		list_for_each_entry(vnode,
-				    &afs_cb_hash(server, &callbacks->fid),
-				    cb_hash_link) {
-			if (memcmp(&vnode->fid, &callbacks->fid,
-				   sizeof(struct afs_fid)) != 0)
-				continue;
-
-			/* right vnode, but is it same server? */
-			if (vnode->cb_server != server)
-				break; /* no */
-
-			/* try and nail the inode down */
-			inode = igrab(AFS_VNODE_TO_I(vnode));
-			break;
-		}
-
-		spin_unlock(&afs_cb_hash_lock);
-
-		if (inode) {
-			/* we've found the record for this vnode */
-			spin_lock(&vnode->lock);
-			if (vnode->cb_server == server) {
-				/* the callback _is_ on the calling server */
-				vnode->cb_server = NULL;
-				valid = 1;
-
-				afs_kafstimod_del_timer(&vnode->cb_timeout);
-				vnode->flags |= AFS_VNODE_CHANGED;
-
-				spin_lock(&server->cb_lock);
-				list_del_init(&vnode->cb_link);
-				spin_unlock(&server->cb_lock);
-
-				spin_lock(&afs_cb_hash_lock);
-				list_del_init(&vnode->cb_hash_link);
-				spin_unlock(&afs_cb_hash_lock);
-			}
-			spin_unlock(&vnode->lock);
-
-			if (valid) {
-				invalidate_remote_inode(inode);
-				afs_put_server(server);
-			}
-			iput(inode);
-		}
+		afs_break_one_callback(server, &callbacks->fid);
 	}
 
-	_leave(" = 0");
-	return 0;
-} /* end SRXAFSCM_CallBack() */
+	_leave("");
+	return;
+}
 
-/*****************************************************************************/
 /*
- * allow the fileserver to see if the cache manager is still alive
+ * record the callback for breaking
+ * - the caller must hold server->cb_lock
  */
-int SRXAFSCM_Probe(struct afs_server *server)
+static void afs_do_give_up_callback(struct afs_server *server,
+				    struct afs_vnode *vnode)
 {
-	_debug("SRXAFSCM_Probe(%p)\n", server);
-	return 0;
-} /* end SRXAFSCM_Probe() */
+	struct afs_callback *cb;
+
+	_enter("%p,%p", server, vnode);
+
+	cb = &server->cb_break[server->cb_break_head];
+	cb->fid		= vnode->fid;
+	cb->version	= vnode->cb_version;
+	cb->expiry	= vnode->cb_expiry;
+	cb->type	= vnode->cb_type;
+	smp_wmb();
+	server->cb_break_head =
+		(server->cb_break_head + 1) &
+		(ARRAY_SIZE(server->cb_break) - 1);
+
+	/* defer the breaking of callbacks to try and collect as many as
+	 * possible to ship in one operation */
+	switch (atomic_inc_return(&server->cb_break_n)) {
+	case 1 ... AFSCBMAX - 1:
+		queue_delayed_work(afs_callback_update_worker,
+				   &server->cb_break_work, HZ * 2);
+		break;
+	case AFSCBMAX:
+		afs_flush_callback_breaks(server);
+		break;
+	default:
+		break;
+	}
+
+	ASSERT(server->cb_promises.rb_node != NULL);
+	rb_erase(&vnode->cb_promise, &server->cb_promises);
+	vnode->cb_promised = false;
+	_leave("");
+}
+
+/*
+ * discard the callback on a deleted item
+ */
+void afs_discard_callback_on_delete(struct afs_vnode *vnode)
+{
+	struct afs_server *server = vnode->server;
+
+	_enter("%d", vnode->cb_promised);
+
+	if (!vnode->cb_promised) {
+		_leave(" [not promised]");
+		return;
+	}
+
+	ASSERT(server != NULL);
+
+	spin_lock(&server->cb_lock);
+	if (vnode->cb_promised) {
+		ASSERT(server->cb_promises.rb_node != NULL);
+		rb_erase(&vnode->cb_promise, &server->cb_promises);
+		vnode->cb_promised = false;
+	}
+	spin_unlock(&server->cb_lock);
+	_leave("");
+}
+
+/*
+ * give up the callback registered for a vnode on the file server when the
+ * inode is being cleared
+ */
+void afs_give_up_callback(struct afs_vnode *vnode)
+{
+	struct afs_server *server = vnode->server;
+
+	DECLARE_WAITQUEUE(myself, current);
+
+	_enter("%d", vnode->cb_promised);
+
+	_debug("GIVE UP INODE %p", &vnode->vfs_inode);
+
+	if (!vnode->cb_promised) {
+		_leave(" [not promised]");
+		return;
+	}
+
+	ASSERT(server != NULL);
+
+	spin_lock(&server->cb_lock);
+	if (vnode->cb_promised && afs_breakring_space(server) == 0) {
+		add_wait_queue(&server->cb_break_waitq, &myself);
+		for (;;) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			if (!vnode->cb_promised ||
+			    afs_breakring_space(server) != 0)
+				break;
+			spin_unlock(&server->cb_lock);
+			schedule();
+			spin_lock(&server->cb_lock);
+		}
+		remove_wait_queue(&server->cb_break_waitq, &myself);
+		__set_current_state(TASK_RUNNING);
+	}
+
+	/* of course, it's always possible for the server to break this vnode's
+	 * callback first... */
+	if (vnode->cb_promised)
+		afs_do_give_up_callback(server, vnode);
+
+	spin_unlock(&server->cb_lock);
+	_leave("");
+}
+
+/*
+ * dispatch a deferred give up callbacks operation
+ */
+void afs_dispatch_give_up_callbacks(struct work_struct *work)
+{
+	struct afs_server *server =
+		container_of(work, struct afs_server, cb_break_work.work);
+
+	_enter("");
+
+	/* tell the fileserver to discard the callback promises it has
+	 * - in the event of ENOMEM or some other error, we just forget that we
+	 *   had callbacks entirely, and the server will call us later to break
+	 *   them
+	 */
+	afs_fs_give_up_callbacks(server, &afs_async_call);
+}
+
+/*
+ * flush the outstanding callback breaks on a server
+ */
+void afs_flush_callback_breaks(struct afs_server *server)
+{
+	cancel_delayed_work(&server->cb_break_work);
+	queue_delayed_work(afs_callback_update_worker,
+			   &server->cb_break_work, 0);
+}
+
+#if 0
+/*
+ * update a bunch of callbacks
+ */
+static void afs_callback_updater(struct work_struct *work)
+{
+	struct afs_server *server;
+	struct afs_vnode *vnode, *xvnode;
+	time_t now;
+	long timeout;
+	int ret;
+
+	server = container_of(work, struct afs_server, updater);
+
+	_enter("");
+
+	now = get_seconds();
+
+	/* find the first vnode to update */
+	spin_lock(&server->cb_lock);
+	for (;;) {
+		if (RB_EMPTY_ROOT(&server->cb_promises)) {
+			spin_unlock(&server->cb_lock);
+			_leave(" [nothing]");
+			return;
+		}
+
+		vnode = rb_entry(rb_first(&server->cb_promises),
+				 struct afs_vnode, cb_promise);
+		if (atomic_read(&vnode->usage) > 0)
+			break;
+		rb_erase(&vnode->cb_promise, &server->cb_promises);
+		vnode->cb_promised = false;
+	}
+
+	timeout = vnode->update_at - now;
+	if (timeout > 0) {
+		queue_delayed_work(afs_vnode_update_worker,
+				   &afs_vnode_update, timeout * HZ);
+		spin_unlock(&server->cb_lock);
+		_leave(" [nothing]");
+		return;
+	}
+
+	list_del_init(&vnode->update);
+	atomic_inc(&vnode->usage);
+	spin_unlock(&server->cb_lock);
+
+	/* we can now perform the update */
+	_debug("update %s", vnode->vldb.name);
+	vnode->state = AFS_VL_UPDATING;
+	vnode->upd_rej_cnt = 0;
+	vnode->upd_busy_cnt = 0;
+
+	ret = afs_vnode_update_record(vl, &vldb);
+	switch (ret) {
+	case 0:
+		afs_vnode_apply_update(vl, &vldb);
+		vnode->state = AFS_VL_UPDATING;
+		break;
+	case -ENOMEDIUM:
+		vnode->state = AFS_VL_VOLUME_DELETED;
+		break;
+	default:
+		vnode->state = AFS_VL_UNCERTAIN;
+		break;
+	}
+
+	/* and then reschedule */
+	_debug("reschedule");
+	vnode->update_at = get_seconds() + afs_vnode_update_timeout;
+
+	spin_lock(&server->cb_lock);
+
+	if (!list_empty(&server->cb_promises)) {
+		/* next update in 10 minutes, but wait at least 1 second more
+		 * than the newest record already queued so that we don't spam
+		 * the VL server suddenly with lots of requests
+		 */
+		xvnode = list_entry(server->cb_promises.prev,
+				    struct afs_vnode, update);
+		if (vnode->update_at <= xvnode->update_at)
+			vnode->update_at = xvnode->update_at + 1;
+		xvnode = list_entry(server->cb_promises.next,
+				    struct afs_vnode, update);
+		timeout = xvnode->update_at - now;
+		if (timeout < 0)
+			timeout = 0;
+	} else {
+		timeout = afs_vnode_update_timeout;
+	}
+
+	list_add_tail(&vnode->update, &server->cb_promises);
+
+	_debug("timeout %ld", timeout);
+	queue_delayed_work(afs_vnode_update_worker,
+			   &afs_vnode_update, timeout * HZ);
+	spin_unlock(&server->cb_lock);
+	afs_put_vnode(vl);
+}
+#endif
+
+/*
+ * initialise the callback update process
+ */
+int __init afs_callback_update_init(void)
+{
+	afs_callback_update_worker =
+		create_singlethread_workqueue("kafs_callbackd");
+	return afs_callback_update_worker ? 0 : -ENOMEM;
+}
+
+/*
+ * shut down the callback update process
+ */
+void __exit afs_callback_update_kill(void)
+{
+	destroy_workqueue(afs_callback_update_worker);
+}
diff --git a/fs/afs/cell.c b/fs/afs/cell.c
index 1fc5783..9b1311a 100644
--- a/fs/afs/cell.c
+++ b/fs/afs/cell.c
@@ -1,4 +1,4 @@
-/* cell.c: AFS cell and server record management
+/* AFS cell and server record management
  *
  * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
@@ -11,15 +11,9 @@
 
 #include <linux/module.h>
 #include <linux/slab.h>
-#include <rxrpc/peer.h>
-#include <rxrpc/connection.h>
-#include "volume.h"
-#include "cell.h"
-#include "server.h"
-#include "transport.h"
-#include "vlclient.h"
-#include "kafstimod.h"
-#include "super.h"
+#include <linux/key.h>
+#include <linux/ctype.h>
+#include <keys/rxrpc-type.h>
 #include "internal.h"
 
 DECLARE_RWSEM(afs_proc_cells_sem);
@@ -28,66 +22,47 @@
 static struct list_head afs_cells = LIST_HEAD_INIT(afs_cells);
 static DEFINE_RWLOCK(afs_cells_lock);
 static DECLARE_RWSEM(afs_cells_sem); /* add/remove serialisation */
+static DECLARE_WAIT_QUEUE_HEAD(afs_cells_freeable_wq);
 static struct afs_cell *afs_cell_root;
 
-#ifdef AFS_CACHING_SUPPORT
-static cachefs_match_val_t afs_cell_cache_match(void *target,
-						const void *entry);
-static void afs_cell_cache_update(void *source, void *entry);
-
-struct cachefs_index_def afs_cache_cell_index_def = {
-	.name			= "cell_ix",
-	.data_size		= sizeof(struct afs_cache_cell),
-	.keys[0]		= { CACHEFS_INDEX_KEYS_ASCIIZ, 64 },
-	.match			= afs_cell_cache_match,
-	.update			= afs_cell_cache_update,
-};
-#endif
-
-/*****************************************************************************/
 /*
- * create a cell record
- * - "name" is the name of the cell
- * - "vllist" is a colon separated list of IP addresses in "a.b.c.d" format
+ * allocate a cell record and fill in its name, VL server address list and
+ * allocate an anonymous key
  */
-int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell)
+static struct afs_cell *afs_cell_alloc(const char *name, char *vllist)
 {
 	struct afs_cell *cell;
-	char *next;
+	size_t namelen;
+	char keyname[4 + AFS_MAXCELLNAME + 1], *cp, *dp, *next;
 	int ret;
 
-	_enter("%s", name);
+	_enter("%s,%s", name, vllist);
 
 	BUG_ON(!name); /* TODO: want to look up "this cell" in the cache */
 
+	namelen = strlen(name);
+	if (namelen > AFS_MAXCELLNAME)
+		return ERR_PTR(-ENAMETOOLONG);
+
 	/* allocate and initialise a cell record */
-	cell = kmalloc(sizeof(struct afs_cell) + strlen(name) + 1, GFP_KERNEL);
+	cell = kzalloc(sizeof(struct afs_cell) + namelen + 1, GFP_KERNEL);
 	if (!cell) {
 		_leave(" = -ENOMEM");
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 	}
 
-	down_write(&afs_cells_sem);
+	memcpy(cell->name, name, namelen);
+	cell->name[namelen] = 0;
 
-	memset(cell, 0, sizeof(struct afs_cell));
-	atomic_set(&cell->usage, 0);
-
+	atomic_set(&cell->usage, 1);
 	INIT_LIST_HEAD(&cell->link);
-
-	rwlock_init(&cell->sv_lock);
-	INIT_LIST_HEAD(&cell->sv_list);
-	INIT_LIST_HEAD(&cell->sv_graveyard);
-	spin_lock_init(&cell->sv_gylock);
-
+	rwlock_init(&cell->servers_lock);
+	INIT_LIST_HEAD(&cell->servers);
 	init_rwsem(&cell->vl_sem);
 	INIT_LIST_HEAD(&cell->vl_list);
-	INIT_LIST_HEAD(&cell->vl_graveyard);
-	spin_lock_init(&cell->vl_gylock);
-
-	strcpy(cell->name,name);
+	spin_lock_init(&cell->vl_lock);
 
 	/* fill in the VL server list from the rest of the string */
-	ret = -EINVAL;
 	do {
 		unsigned a, b, c, d;
 
@@ -96,20 +71,75 @@
 			*next++ = 0;
 
 		if (sscanf(vllist, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
-			goto badaddr;
+			goto bad_address;
 
 		if (a > 255 || b > 255 || c > 255 || d > 255)
-			goto badaddr;
+			goto bad_address;
 
 		cell->vl_addrs[cell->vl_naddrs++].s_addr =
 			htonl((a << 24) | (b << 16) | (c << 8) | d);
 
-		if (cell->vl_naddrs >= AFS_CELL_MAX_ADDRS)
-			break;
+	} while (cell->vl_naddrs < AFS_CELL_MAX_ADDRS && (vllist = next));
 
-	} while(vllist = next, vllist);
+	/* create a key to represent an anonymous user */
+	memcpy(keyname, "afs@", 4);
+	dp = keyname + 4;
+	cp = cell->name;
+	do {
+		*dp++ = toupper(*cp);
+	} while (*cp++);
+	cell->anonymous_key = key_alloc(&key_type_rxrpc, keyname, 0, 0, current,
+					KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA);
+	if (IS_ERR(cell->anonymous_key)) {
+		_debug("no key");
+		ret = PTR_ERR(cell->anonymous_key);
+		goto error;
+	}
 
-	/* add a proc dir for this cell */
+	ret = key_instantiate_and_link(cell->anonymous_key, NULL, 0,
+				       NULL, NULL);
+	if (ret < 0) {
+		_debug("instantiate failed");
+		goto error;
+	}
+
+	_debug("anon key %p{%x}",
+	       cell->anonymous_key, key_serial(cell->anonymous_key));
+
+	_leave(" = %p", cell);
+	return cell;
+
+bad_address:
+	printk(KERN_ERR "kAFS: bad VL server IP address\n");
+	ret = -EINVAL;
+error:
+	key_put(cell->anonymous_key);
+	kfree(cell);
+	_leave(" = %d", ret);
+	return ERR_PTR(ret);
+}
+
+/*
+ * create a cell record
+ * - "name" is the name of the cell
+ * - "vllist" is a colon separated list of IP addresses in "a.b.c.d" format
+ */
+struct afs_cell *afs_cell_create(const char *name, char *vllist)
+{
+	struct afs_cell *cell;
+	int ret;
+
+	_enter("%s,%s", name, vllist);
+
+	cell = afs_cell_alloc(name, vllist);
+	if (IS_ERR(cell)) {
+		_leave(" = %ld", PTR_ERR(cell));
+		return cell;
+	}
+
+	down_write(&afs_cells_sem);
+
+	/* add a proc directory for this cell */
 	ret = afs_proc_cell_setup(cell);
 	if (ret < 0)
 		goto error;
@@ -130,31 +160,28 @@
 	down_write(&afs_proc_cells_sem);
 	list_add_tail(&cell->proc_link, &afs_proc_cells);
 	up_write(&afs_proc_cells_sem);
-
-	*_cell = cell;
 	up_write(&afs_cells_sem);
 
-	_leave(" = 0 (%p)", cell);
-	return 0;
+	_leave(" = %p", cell);
+	return cell;
 
- badaddr:
-	printk(KERN_ERR "kAFS: bad VL server IP address: '%s'\n", vllist);
- error:
+error:
 	up_write(&afs_cells_sem);
+	key_put(cell->anonymous_key);
 	kfree(cell);
 	_leave(" = %d", ret);
-	return ret;
-} /* end afs_cell_create() */
+	return ERR_PTR(ret);
+}
 
-/*****************************************************************************/
 /*
- * initialise the cell database from module parameters
+ * set the root cell information
+ * - can be called with a module parameter string
+ * - can be called from a write to /proc/fs/afs/rootcell
  */
 int afs_cell_init(char *rootcell)
 {
 	struct afs_cell *old_root, *new_root;
 	char *cp;
-	int ret;
 
 	_enter("");
 
@@ -162,82 +189,60 @@
 		/* module is loaded with no parameters, or built statically.
 		 * - in the future we might initialize cell DB here.
 		 */
-		_leave(" = 0 (but no root)");
+		_leave(" = 0 [no root]");
 		return 0;
 	}
 
 	cp = strchr(rootcell, ':');
 	if (!cp) {
 		printk(KERN_ERR "kAFS: no VL server IP addresses specified\n");
-		_leave(" = %d (no colon)", -EINVAL);
+		_leave(" = -EINVAL");
 		return -EINVAL;
 	}
 
 	/* allocate a cell record for the root cell */
 	*cp++ = 0;
-	ret = afs_cell_create(rootcell, cp, &new_root);
-	if (ret < 0) {
-		_leave(" = %d", ret);
-		return ret;
+	new_root = afs_cell_create(rootcell, cp);
+	if (IS_ERR(new_root)) {
+		_leave(" = %ld", PTR_ERR(new_root));
+		return PTR_ERR(new_root);
 	}
 
-	/* as afs_put_cell() takes locks by itself, we have to do
-	 * a little gymnastics to be race-free.
-	 */
-	afs_get_cell(new_root);
-
+	/* install the new cell */
 	write_lock(&afs_cells_lock);
-	while (afs_cell_root) {
-		old_root = afs_cell_root;
-		afs_cell_root = NULL;
-		write_unlock(&afs_cells_lock);
-		afs_put_cell(old_root);
-		write_lock(&afs_cells_lock);
-	}
+	old_root = afs_cell_root;
 	afs_cell_root = new_root;
 	write_unlock(&afs_cells_lock);
+	afs_put_cell(old_root);
 
-	_leave(" = %d", ret);
-	return ret;
+	_leave(" = 0");
+	return 0;
+}
 
-} /* end afs_cell_init() */
-
-/*****************************************************************************/
 /*
  * lookup a cell record
  */
-int afs_cell_lookup(const char *name, unsigned namesz, struct afs_cell **_cell)
+struct afs_cell *afs_cell_lookup(const char *name, unsigned namesz)
 {
 	struct afs_cell *cell;
-	int ret;
 
 	_enter("\"%*.*s\",", namesz, namesz, name ? name : "");
 
-	*_cell = NULL;
+	down_read(&afs_cells_sem);
+	read_lock(&afs_cells_lock);
 
 	if (name) {
 		/* if the cell was named, look for it in the cell record list */
-		ret = -ENOENT;
-		cell = NULL;
-		read_lock(&afs_cells_lock);
-
 		list_for_each_entry(cell, &afs_cells, link) {
 			if (strncmp(cell->name, name, namesz) == 0) {
 				afs_get_cell(cell);
 				goto found;
 			}
 		}
-		cell = NULL;
+		cell = ERR_PTR(-ENOENT);
 	found:
-
-		read_unlock(&afs_cells_lock);
-
-		if (cell)
-			ret = 0;
-	}
-	else {
-		read_lock(&afs_cells_lock);
-
+		;
+	} else {
 		cell = afs_cell_root;
 		if (!cell) {
 			/* this should not happen unless user tries to mount
@@ -246,44 +251,35 @@
 			 * ENOENT might be "more appropriate" but they happen
 			 * for other reasons.
 			 */
-			ret = -EDESTADDRREQ;
-		}
-		else {
+			cell = ERR_PTR(-EDESTADDRREQ);
+		} else {
 			afs_get_cell(cell);
-			ret = 0;
 		}
 
-		read_unlock(&afs_cells_lock);
 	}
 
-	*_cell = cell;
-	_leave(" = %d (%p)", ret, cell);
-	return ret;
+	read_unlock(&afs_cells_lock);
+	up_read(&afs_cells_sem);
+	_leave(" = %p", cell);
+	return cell;
+}
 
-} /* end afs_cell_lookup() */
-
-/*****************************************************************************/
 /*
  * try and get a cell record
  */
-struct afs_cell *afs_get_cell_maybe(struct afs_cell **_cell)
+struct afs_cell *afs_get_cell_maybe(struct afs_cell *cell)
 {
-	struct afs_cell *cell;
-
 	write_lock(&afs_cells_lock);
 
-	cell = *_cell;
 	if (cell && !list_empty(&cell->link))
 		afs_get_cell(cell);
 	else
 		cell = NULL;
 
 	write_unlock(&afs_cells_lock);
-
 	return cell;
-} /* end afs_get_cell_maybe() */
+}
 
-/*****************************************************************************/
 /*
  * destroy a cell record
  */
@@ -294,8 +290,7 @@
 
 	_enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name);
 
-	/* sanity check */
-	BUG_ON(atomic_read(&cell->usage) <= 0);
+	ASSERTCMP(atomic_read(&cell->usage), >, 0);
 
 	/* to prevent a race, the decrement and the dequeue must be effectively
 	 * atomic */
@@ -307,36 +302,49 @@
 		return;
 	}
 
+	ASSERT(list_empty(&cell->servers));
+	ASSERT(list_empty(&cell->vl_list));
+
 	write_unlock(&afs_cells_lock);
 
-	BUG_ON(!list_empty(&cell->sv_list));
-	BUG_ON(!list_empty(&cell->sv_graveyard));
-	BUG_ON(!list_empty(&cell->vl_list));
-	BUG_ON(!list_empty(&cell->vl_graveyard));
+	wake_up(&afs_cells_freeable_wq);
 
 	_leave(" [unused]");
-} /* end afs_put_cell() */
+}
 
-/*****************************************************************************/
 /*
  * destroy a cell record
+ * - must be called with the afs_cells_sem write-locked
+ * - cell->link should have been broken by the caller
  */
 static void afs_cell_destroy(struct afs_cell *cell)
 {
 	_enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name);
 
-	/* to prevent a race, the decrement and the dequeue must be effectively
-	 * atomic */
-	write_lock(&afs_cells_lock);
+	ASSERTCMP(atomic_read(&cell->usage), >=, 0);
+	ASSERT(list_empty(&cell->link));
 
-	/* sanity check */
-	BUG_ON(atomic_read(&cell->usage) != 0);
+	/* wait for everyone to stop using the cell */
+	if (atomic_read(&cell->usage) > 0) {
+		DECLARE_WAITQUEUE(myself, current);
 
-	list_del_init(&cell->link);
+		_debug("wait for cell %s", cell->name);
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		add_wait_queue(&afs_cells_freeable_wq, &myself);
 
-	write_unlock(&afs_cells_lock);
+		while (atomic_read(&cell->usage) > 0) {
+			schedule();
+			set_current_state(TASK_UNINTERRUPTIBLE);
+		}
 
-	down_write(&afs_cells_sem);
+		remove_wait_queue(&afs_cells_freeable_wq, &myself);
+		set_current_state(TASK_RUNNING);
+	}
+
+	_debug("cell dead");
+	ASSERTCMP(atomic_read(&cell->usage), ==, 0);
+	ASSERT(list_empty(&cell->servers));
+	ASSERT(list_empty(&cell->vl_list));
 
 	afs_proc_cell_remove(cell);
 
@@ -348,104 +356,26 @@
 	cachefs_relinquish_cookie(cell->cache, 0);
 #endif
 
-	up_write(&afs_cells_sem);
-
-	BUG_ON(!list_empty(&cell->sv_list));
-	BUG_ON(!list_empty(&cell->sv_graveyard));
-	BUG_ON(!list_empty(&cell->vl_list));
-	BUG_ON(!list_empty(&cell->vl_graveyard));
-
-	/* finish cleaning up the cell */
+	key_put(cell->anonymous_key);
 	kfree(cell);
 
 	_leave(" [destroyed]");
-} /* end afs_cell_destroy() */
+}
 
-/*****************************************************************************/
-/*
- * lookup the server record corresponding to an Rx RPC peer
- */
-int afs_server_find_by_peer(const struct rxrpc_peer *peer,
-			    struct afs_server **_server)
-{
-	struct afs_server *server;
-	struct afs_cell *cell;
-
-	_enter("%p{a=%08x},", peer, ntohl(peer->addr.s_addr));
-
-	/* search the cell list */
-	read_lock(&afs_cells_lock);
-
-	list_for_each_entry(cell, &afs_cells, link) {
-
-		_debug("? cell %s",cell->name);
-
-		write_lock(&cell->sv_lock);
-
-		/* check the active list */
-		list_for_each_entry(server, &cell->sv_list, link) {
-			_debug("?? server %08x", ntohl(server->addr.s_addr));
-
-			if (memcmp(&server->addr, &peer->addr,
-				   sizeof(struct in_addr)) == 0)
-				goto found_server;
-		}
-
-		/* check the inactive list */
-		spin_lock(&cell->sv_gylock);
-		list_for_each_entry(server, &cell->sv_graveyard, link) {
-			_debug("?? dead server %08x",
-			       ntohl(server->addr.s_addr));
-
-			if (memcmp(&server->addr, &peer->addr,
-				   sizeof(struct in_addr)) == 0)
-				goto found_dead_server;
-		}
-		spin_unlock(&cell->sv_gylock);
-
-		write_unlock(&cell->sv_lock);
-	}
-	read_unlock(&afs_cells_lock);
-
-	_leave(" = -ENOENT");
-	return -ENOENT;
-
-	/* we found it in the graveyard - resurrect it */
- found_dead_server:
-	list_move_tail(&server->link, &cell->sv_list);
-	afs_get_server(server);
-	afs_kafstimod_del_timer(&server->timeout);
-	spin_unlock(&cell->sv_gylock);
-	goto success;
-
-	/* we found it - increment its ref count and return it */
- found_server:
-	afs_get_server(server);
-
- success:
-	write_unlock(&cell->sv_lock);
-	read_unlock(&afs_cells_lock);
-
-	*_server = server;
-	_leave(" = 0 (s=%p c=%p)", server, cell);
-	return 0;
-
-} /* end afs_server_find_by_peer() */
-
-/*****************************************************************************/
 /*
  * purge in-memory cell database on module unload or afs_init() failure
  * - the timeout daemon is stopped before calling this
  */
 void afs_cell_purge(void)
 {
-	struct afs_vlocation *vlocation;
 	struct afs_cell *cell;
 
 	_enter("");
 
 	afs_put_cell(afs_cell_root);
 
+	down_write(&afs_cells_sem);
+
 	while (!list_empty(&afs_cells)) {
 		cell = NULL;
 
@@ -464,104 +394,11 @@
 			_debug("PURGING CELL %s (%d)",
 			       cell->name, atomic_read(&cell->usage));
 
-			BUG_ON(!list_empty(&cell->sv_list));
-			BUG_ON(!list_empty(&cell->vl_list));
-
-			/* purge the cell's VL graveyard list */
-			_debug(" - clearing VL graveyard");
-
-			spin_lock(&cell->vl_gylock);
-
-			while (!list_empty(&cell->vl_graveyard)) {
-				vlocation = list_entry(cell->vl_graveyard.next,
-						       struct afs_vlocation,
-						       link);
-				list_del_init(&vlocation->link);
-
-				afs_kafstimod_del_timer(&vlocation->timeout);
-
-				spin_unlock(&cell->vl_gylock);
-
-				afs_vlocation_do_timeout(vlocation);
-				/* TODO: race if move to use krxtimod instead
-				 * of kafstimod */
-
-				spin_lock(&cell->vl_gylock);
-			}
-
-			spin_unlock(&cell->vl_gylock);
-
-			/* purge the cell's server graveyard list */
-			_debug(" - clearing server graveyard");
-
-			spin_lock(&cell->sv_gylock);
-
-			while (!list_empty(&cell->sv_graveyard)) {
-				struct afs_server *server;
-
-				server = list_entry(cell->sv_graveyard.next,
-						    struct afs_server, link);
-				list_del_init(&server->link);
-
-				afs_kafstimod_del_timer(&server->timeout);
-
-				spin_unlock(&cell->sv_gylock);
-
-				afs_server_do_timeout(server);
-
-				spin_lock(&cell->sv_gylock);
-			}
-
-			spin_unlock(&cell->sv_gylock);
-
 			/* now the cell should be left with no references */
 			afs_cell_destroy(cell);
 		}
 	}
 
+	up_write(&afs_cells_sem);
 	_leave("");
-} /* end afs_cell_purge() */
-
-/*****************************************************************************/
-/*
- * match a cell record obtained from the cache
- */
-#ifdef AFS_CACHING_SUPPORT
-static cachefs_match_val_t afs_cell_cache_match(void *target,
-						const void *entry)
-{
-	const struct afs_cache_cell *ccell = entry;
-	struct afs_cell *cell = target;
-
-	_enter("{%s},{%s}", ccell->name, cell->name);
-
-	if (strncmp(ccell->name, cell->name, sizeof(ccell->name)) == 0) {
-		_leave(" = SUCCESS");
-		return CACHEFS_MATCH_SUCCESS;
-	}
-
-	_leave(" = FAILED");
-	return CACHEFS_MATCH_FAILED;
-} /* end afs_cell_cache_match() */
-#endif
-
-/*****************************************************************************/
-/*
- * update a cell record in the cache
- */
-#ifdef AFS_CACHING_SUPPORT
-static void afs_cell_cache_update(void *source, void *entry)
-{
-	struct afs_cache_cell *ccell = entry;
-	struct afs_cell *cell = source;
-
-	_enter("%p,%p", source, entry);
-
-	strncpy(ccell->name, cell->name, sizeof(ccell->name));
-
-	memcpy(ccell->vl_servers,
-	       cell->vl_addrs,
-	       min(sizeof(ccell->vl_servers), sizeof(cell->vl_addrs)));
-
-} /* end afs_cell_cache_update() */
-#endif
+}
diff --git a/fs/afs/cell.h b/fs/afs/cell.h
deleted file mode 100644
index 4834910..0000000
--- a/fs/afs/cell.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/* cell.h: AFS cell record
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_AFS_CELL_H
-#define _LINUX_AFS_CELL_H
-
-#include "types.h"
-#include "cache.h"
-
-#define AFS_CELL_MAX_ADDRS 15
-
-extern volatile int afs_cells_being_purged; /* T when cells are being purged by rmmod */
-
-/*****************************************************************************/
-/*
- * entry in the cached cell catalogue
- */
-struct afs_cache_cell
-{
-	char			name[64];	/* cell name (padded with NULs) */
-	struct in_addr		vl_servers[15];	/* cached cell VL servers */
-};
-
-/*****************************************************************************/
-/*
- * AFS cell record
- */
-struct afs_cell
-{
-	atomic_t		usage;
-	struct list_head	link;		/* main cell list link */
-	struct list_head	proc_link;	/* /proc cell list link */
-	struct proc_dir_entry	*proc_dir;	/* /proc dir for this cell */
-#ifdef AFS_CACHING_SUPPORT
-	struct cachefs_cookie	*cache;		/* caching cookie */
-#endif
-
-	/* server record management */
-	rwlock_t		sv_lock;	/* active server list lock */
-	struct list_head	sv_list;	/* active server list */
-	struct list_head	sv_graveyard;	/* inactive server list */
-	spinlock_t		sv_gylock;	/* inactive server list lock */
-
-	/* volume location record management */
-	struct rw_semaphore	vl_sem;		/* volume management serialisation semaphore */
-	struct list_head	vl_list;	/* cell's active VL record list */
-	struct list_head	vl_graveyard;	/* cell's inactive VL record list */
-	spinlock_t		vl_gylock;	/* graveyard lock */
-	unsigned short		vl_naddrs;	/* number of VL servers in addr list */
-	unsigned short		vl_curr_svix;	/* current server index */
-	struct in_addr		vl_addrs[AFS_CELL_MAX_ADDRS];	/* cell VL server addresses */
-
-	char			name[0];	/* cell name - must go last */
-};
-
-extern int afs_cell_init(char *rootcell);
-
-extern int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell);
-
-extern int afs_cell_lookup(const char *name, unsigned nmsize, struct afs_cell **_cell);
-
-#define afs_get_cell(C) do { atomic_inc(&(C)->usage); } while(0)
-
-extern struct afs_cell *afs_get_cell_maybe(struct afs_cell **_cell);
-
-extern void afs_put_cell(struct afs_cell *cell);
-
-extern void afs_cell_purge(void);
-
-#endif /* _LINUX_AFS_CELL_H */
diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c
index 3d097fd..6685f4c 100644
--- a/fs/afs/cmservice.c
+++ b/fs/afs/cmservice.c
@@ -1,4 +1,4 @@
-/* cmservice.c: AFS Cache Manager Service
+/* AFS Cache Manager Service
  *
  * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
@@ -12,641 +12,463 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/sched.h>
-#include <linux/completion.h>
-#include "server.h"
-#include "cell.h"
-#include "transport.h"
-#include <rxrpc/rxrpc.h>
-#include <rxrpc/transport.h>
-#include <rxrpc/connection.h>
-#include <rxrpc/call.h>
-#include "cmservice.h"
+#include <linux/ip.h>
 #include "internal.h"
+#include "afs_cm.h"
 
-static unsigned afscm_usage;		/* AFS cache manager usage count */
-static struct rw_semaphore afscm_sem;	/* AFS cache manager start/stop semaphore */
+struct workqueue_struct *afs_cm_workqueue;
 
-static int afscm_new_call(struct rxrpc_call *call);
-static void afscm_attention(struct rxrpc_call *call);
-static void afscm_error(struct rxrpc_call *call);
-static void afscm_aemap(struct rxrpc_call *call);
+static int afs_deliver_cb_init_call_back_state(struct afs_call *,
+					       struct sk_buff *, bool);
+static int afs_deliver_cb_init_call_back_state3(struct afs_call *,
+						struct sk_buff *, bool);
+static int afs_deliver_cb_probe(struct afs_call *, struct sk_buff *, bool);
+static int afs_deliver_cb_callback(struct afs_call *, struct sk_buff *, bool);
+static int afs_deliver_cb_get_capabilities(struct afs_call *, struct sk_buff *,
+					   bool);
+static void afs_cm_destructor(struct afs_call *);
 
-static void _SRXAFSCM_CallBack(struct rxrpc_call *call);
-static void _SRXAFSCM_InitCallBackState(struct rxrpc_call *call);
-static void _SRXAFSCM_Probe(struct rxrpc_call *call);
+/*
+ * CB.CallBack operation type
+ */
+static const struct afs_call_type afs_SRXCBCallBack = {
+	.name		= "CB.CallBack",
+	.deliver	= afs_deliver_cb_callback,
+	.abort_to_error	= afs_abort_to_error,
+	.destructor	= afs_cm_destructor,
+};
 
-typedef void (*_SRXAFSCM_xxxx_t)(struct rxrpc_call *call);
+/*
+ * CB.InitCallBackState operation type
+ */
+static const struct afs_call_type afs_SRXCBInitCallBackState = {
+	.name		= "CB.InitCallBackState",
+	.deliver	= afs_deliver_cb_init_call_back_state,
+	.abort_to_error	= afs_abort_to_error,
+	.destructor	= afs_cm_destructor,
+};
 
-static const struct rxrpc_operation AFSCM_ops[] = {
-	{
-		.id	= 204,
-		.asize	= RXRPC_APP_MARK_EOF,
-		.name	= "CallBack",
-		.user	= _SRXAFSCM_CallBack,
-	},
-	{
-		.id	= 205,
-		.asize	= RXRPC_APP_MARK_EOF,
-		.name	= "InitCallBackState",
-		.user	= _SRXAFSCM_InitCallBackState,
-	},
-	{
-		.id	= 206,
-		.asize	= RXRPC_APP_MARK_EOF,
-		.name	= "Probe",
-		.user	= _SRXAFSCM_Probe,
-	},
-#if 0
-	{
-		.id	= 207,
-		.asize	= RXRPC_APP_MARK_EOF,
-		.name	= "GetLock",
-		.user	= _SRXAFSCM_GetLock,
-	},
-	{
-		.id	= 208,
-		.asize	= RXRPC_APP_MARK_EOF,
-		.name	= "GetCE",
-		.user	= _SRXAFSCM_GetCE,
-	},
-	{
-		.id	= 209,
-		.asize	= RXRPC_APP_MARK_EOF,
-		.name	= "GetXStatsVersion",
-		.user	= _SRXAFSCM_GetXStatsVersion,
-	},
-	{
-		.id	= 210,
-		.asize	= RXRPC_APP_MARK_EOF,
-		.name	= "GetXStats",
-		.user	= _SRXAFSCM_GetXStats,
+/*
+ * CB.InitCallBackState3 operation type
+ */
+static const struct afs_call_type afs_SRXCBInitCallBackState3 = {
+	.name		= "CB.InitCallBackState3",
+	.deliver	= afs_deliver_cb_init_call_back_state3,
+	.abort_to_error	= afs_abort_to_error,
+	.destructor	= afs_cm_destructor,
+};
+
+/*
+ * CB.Probe operation type
+ */
+static const struct afs_call_type afs_SRXCBProbe = {
+	.name		= "CB.Probe",
+	.deliver	= afs_deliver_cb_probe,
+	.abort_to_error	= afs_abort_to_error,
+	.destructor	= afs_cm_destructor,
+};
+
+/*
+ * CB.GetCapabilities operation type
+ */
+static const struct afs_call_type afs_SRXCBGetCapabilites = {
+	.name		= "CB.GetCapabilities",
+	.deliver	= afs_deliver_cb_get_capabilities,
+	.abort_to_error	= afs_abort_to_error,
+	.destructor	= afs_cm_destructor,
+};
+
+/*
+ * route an incoming cache manager call
+ * - return T if supported, F if not
+ */
+bool afs_cm_incoming_call(struct afs_call *call)
+{
+	u32 operation_id = ntohl(call->operation_ID);
+
+	_enter("{CB.OP %u}", operation_id);
+
+	switch (operation_id) {
+	case CBCallBack:
+		call->type = &afs_SRXCBCallBack;
+		return true;
+	case CBInitCallBackState:
+		call->type = &afs_SRXCBInitCallBackState;
+		return true;
+	case CBInitCallBackState3:
+		call->type = &afs_SRXCBInitCallBackState3;
+		return true;
+	case CBProbe:
+		call->type = &afs_SRXCBProbe;
+		return true;
+	case CBGetCapabilities:
+		call->type = &afs_SRXCBGetCapabilites;
+		return true;
+	default:
+		return false;
 	}
-#endif
-};
+}
 
-static struct rxrpc_service AFSCM_service = {
-	.name		= "AFS/CM",
-	.owner		= THIS_MODULE,
-	.link		= LIST_HEAD_INIT(AFSCM_service.link),
-	.new_call	= afscm_new_call,
-	.service_id	= 1,
-	.attn_func	= afscm_attention,
-	.error_func	= afscm_error,
-	.aemap_func	= afscm_aemap,
-	.ops_begin	= &AFSCM_ops[0],
-	.ops_end	= &AFSCM_ops[ARRAY_SIZE(AFSCM_ops)],
-};
-
-static DECLARE_COMPLETION(kafscmd_alive);
-static DECLARE_COMPLETION(kafscmd_dead);
-static DECLARE_WAIT_QUEUE_HEAD(kafscmd_sleepq);
-static LIST_HEAD(kafscmd_attention_list);
-static LIST_HEAD(afscm_calls);
-static DEFINE_SPINLOCK(afscm_calls_lock);
-static DEFINE_SPINLOCK(kafscmd_attention_lock);
-static int kafscmd_die;
-
-/*****************************************************************************/
 /*
- * AFS Cache Manager kernel thread
+ * clean up a cache manager call
  */
-static int kafscmd(void *arg)
+static void afs_cm_destructor(struct afs_call *call)
 {
-	DECLARE_WAITQUEUE(myself, current);
+	_enter("");
 
-	struct rxrpc_call *call;
-	_SRXAFSCM_xxxx_t func;
-	int die;
+	afs_put_server(call->server);
+	call->server = NULL;
+	kfree(call->buffer);
+	call->buffer = NULL;
+}
 
-	printk(KERN_INFO "kAFS: Started kafscmd %d\n", current->pid);
-
-	daemonize("kafscmd");
-
-	complete(&kafscmd_alive);
-
-	/* loop around looking for things to attend to */
-	do {
-		if (list_empty(&kafscmd_attention_list)) {
-			set_current_state(TASK_INTERRUPTIBLE);
-			add_wait_queue(&kafscmd_sleepq, &myself);
-
-			for (;;) {
-				set_current_state(TASK_INTERRUPTIBLE);
-				if (!list_empty(&kafscmd_attention_list) ||
-				    signal_pending(current) ||
-				    kafscmd_die)
-					break;
-
-				schedule();
-			}
-
-			remove_wait_queue(&kafscmd_sleepq, &myself);
-			set_current_state(TASK_RUNNING);
-		}
-
-		die = kafscmd_die;
-
-		/* dequeue the next call requiring attention */
-		call = NULL;
-		spin_lock(&kafscmd_attention_lock);
-
-		if (!list_empty(&kafscmd_attention_list)) {
-			call = list_entry(kafscmd_attention_list.next,
-					  struct rxrpc_call,
-					  app_attn_link);
-			list_del_init(&call->app_attn_link);
-			die = 0;
-		}
-
-		spin_unlock(&kafscmd_attention_lock);
-
-		if (call) {
-			/* act upon it */
-			_debug("@@@ Begin Attend Call %p", call);
-
-			func = call->app_user;
-			if (func)
-				func(call);
-
-			rxrpc_put_call(call);
-
-			_debug("@@@ End Attend Call %p", call);
-		}
-
-	} while(!die);
-
-	/* and that's all */
-	complete_and_exit(&kafscmd_dead, 0);
-
-} /* end kafscmd() */
-
-/*****************************************************************************/
 /*
- * handle a call coming in to the cache manager
- * - if I want to keep the call, I must increment its usage count
- * - the return value will be negated and passed back in an abort packet if
- *   non-zero
- * - serialised by virtue of there only being one krxiod
+ * allow the fileserver to see if the cache manager is still alive
  */
-static int afscm_new_call(struct rxrpc_call *call)
+static void SRXAFSCB_CallBack(struct work_struct *work)
 {
-	_enter("%p{cid=%u u=%d}",
-	       call, ntohl(call->call_id), atomic_read(&call->usage));
+	struct afs_call *call = container_of(work, struct afs_call, work);
 
-	rxrpc_get_call(call);
+	_enter("");
 
-	/* add to my current call list */
-	spin_lock(&afscm_calls_lock);
-	list_add(&call->app_link,&afscm_calls);
-	spin_unlock(&afscm_calls_lock);
+	/* be sure to send the reply *before* attempting to spam the AFS server
+	 * with FSFetchStatus requests on the vnodes with broken callbacks lest
+	 * the AFS server get into a vicious cycle of trying to break further
+	 * callbacks because it hadn't received completion of the CBCallBack op
+	 * yet */
+	afs_send_empty_reply(call);
 
-	_leave(" = 0");
+	afs_break_callbacks(call->server, call->count, call->request);
+	_leave("");
+}
+
+/*
+ * deliver request data to a CB.CallBack call
+ */
+static int afs_deliver_cb_callback(struct afs_call *call, struct sk_buff *skb,
+				   bool last)
+{
+	struct afs_callback *cb;
+	struct afs_server *server;
+	struct in_addr addr;
+	__be32 *bp;
+	u32 tmp;
+	int ret, loop;
+
+	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
+
+	switch (call->unmarshall) {
+	case 0:
+		call->offset = 0;
+		call->unmarshall++;
+
+		/* extract the FID array and its count in two steps */
+	case 1:
+		_debug("extract FID count");
+		ret = afs_extract_data(call, skb, last, &call->tmp, 4);
+		switch (ret) {
+		case 0:		break;
+		case -EAGAIN:	return 0;
+		default:	return ret;
+		}
+
+		call->count = ntohl(call->tmp);
+		_debug("FID count: %u", call->count);
+		if (call->count > AFSCBMAX)
+			return -EBADMSG;
+
+		call->buffer = kmalloc(call->count * 3 * 4, GFP_KERNEL);
+		if (!call->buffer)
+			return -ENOMEM;
+		call->offset = 0;
+		call->unmarshall++;
+
+	case 2:
+		_debug("extract FID array");
+		ret = afs_extract_data(call, skb, last, call->buffer,
+				       call->count * 3 * 4);
+		switch (ret) {
+		case 0:		break;
+		case -EAGAIN:	return 0;
+		default:	return ret;
+		}
+
+		_debug("unmarshall FID array");
+		call->request = kcalloc(call->count,
+					sizeof(struct afs_callback),
+					GFP_KERNEL);
+		if (!call->request)
+			return -ENOMEM;
+
+		cb = call->request;
+		bp = call->buffer;
+		for (loop = call->count; loop > 0; loop--, cb++) {
+			cb->fid.vid	= ntohl(*bp++);
+			cb->fid.vnode	= ntohl(*bp++);
+			cb->fid.unique	= ntohl(*bp++);
+			cb->type	= AFSCM_CB_UNTYPED;
+		}
+
+		call->offset = 0;
+		call->unmarshall++;
+
+		/* extract the callback array and its count in two steps */
+	case 3:
+		_debug("extract CB count");
+		ret = afs_extract_data(call, skb, last, &call->tmp, 4);
+		switch (ret) {
+		case 0:		break;
+		case -EAGAIN:	return 0;
+		default:	return ret;
+		}
+
+		tmp = ntohl(call->tmp);
+		_debug("CB count: %u", tmp);
+		if (tmp != call->count && tmp != 0)
+			return -EBADMSG;
+		call->offset = 0;
+		call->unmarshall++;
+		if (tmp == 0)
+			goto empty_cb_array;
+
+	case 4:
+		_debug("extract CB array");
+		ret = afs_extract_data(call, skb, last, call->request,
+				       call->count * 3 * 4);
+		switch (ret) {
+		case 0:		break;
+		case -EAGAIN:	return 0;
+		default:	return ret;
+		}
+
+		_debug("unmarshall CB array");
+		cb = call->request;
+		bp = call->buffer;
+		for (loop = call->count; loop > 0; loop--, cb++) {
+			cb->version	= ntohl(*bp++);
+			cb->expiry	= ntohl(*bp++);
+			cb->type	= ntohl(*bp++);
+		}
+
+	empty_cb_array:
+		call->offset = 0;
+		call->unmarshall++;
+
+	case 5:
+		_debug("trailer");
+		if (skb->len != 0)
+			return -EBADMSG;
+		break;
+	}
+
+	if (!last)
+		return 0;
+
+	call->state = AFS_CALL_REPLYING;
+
+	/* we'll need the file server record as that tells us which set of
+	 * vnodes to operate upon */
+	memcpy(&addr, &ip_hdr(skb)->saddr, 4);
+	server = afs_find_server(&addr);
+	if (!server)
+		return -ENOTCONN;
+	call->server = server;
+
+	INIT_WORK(&call->work, SRXAFSCB_CallBack);
+	schedule_work(&call->work);
 	return 0;
+}
 
-} /* end afscm_new_call() */
-
-/*****************************************************************************/
 /*
- * queue on the kafscmd queue for attention
+ * allow the fileserver to request callback state (re-)initialisation
  */
-static void afscm_attention(struct rxrpc_call *call)
+static void SRXAFSCB_InitCallBackState(struct work_struct *work)
 {
-	_enter("%p{cid=%u u=%d}",
-	       call, ntohl(call->call_id), atomic_read(&call->usage));
+	struct afs_call *call = container_of(work, struct afs_call, work);
 
-	spin_lock(&kafscmd_attention_lock);
+	_enter("{%p}", call->server);
 
-	if (list_empty(&call->app_attn_link)) {
-		list_add_tail(&call->app_attn_link, &kafscmd_attention_list);
-		rxrpc_get_call(call);
-	}
+	afs_init_callback_state(call->server);
+	afs_send_empty_reply(call);
+	_leave("");
+}
 
-	spin_unlock(&kafscmd_attention_lock);
-
-	wake_up(&kafscmd_sleepq);
-
-	_leave(" {u=%d}", atomic_read(&call->usage));
-} /* end afscm_attention() */
-
-/*****************************************************************************/
 /*
- * handle my call being aborted
- * - clean up, dequeue and put my ref to the call
+ * deliver request data to a CB.InitCallBackState call
  */
-static void afscm_error(struct rxrpc_call *call)
+static int afs_deliver_cb_init_call_back_state(struct afs_call *call,
+					       struct sk_buff *skb,
+					       bool last)
 {
-	int removed;
+	struct afs_server *server;
+	struct in_addr addr;
 
-	_enter("%p{est=%s ac=%u er=%d}",
-	       call,
-	       rxrpc_call_error_states[call->app_err_state],
-	       call->app_abort_code,
-	       call->app_errno);
+	_enter(",{%u},%d", skb->len, last);
 
-	spin_lock(&kafscmd_attention_lock);
+	if (skb->len > 0)
+		return -EBADMSG;
+	if (!last)
+		return 0;
 
-	if (list_empty(&call->app_attn_link)) {
-		list_add_tail(&call->app_attn_link, &kafscmd_attention_list);
-		rxrpc_get_call(call);
+	/* no unmarshalling required */
+	call->state = AFS_CALL_REPLYING;
+
+	/* we'll need the file server record as that tells us which set of
+	 * vnodes to operate upon */
+	memcpy(&addr, &ip_hdr(skb)->saddr, 4);
+	server = afs_find_server(&addr);
+	if (!server)
+		return -ENOTCONN;
+	call->server = server;
+
+	INIT_WORK(&call->work, SRXAFSCB_InitCallBackState);
+	schedule_work(&call->work);
+	return 0;
+}
+
+/*
+ * deliver request data to a CB.InitCallBackState3 call
+ */
+static int afs_deliver_cb_init_call_back_state3(struct afs_call *call,
+						struct sk_buff *skb,
+						bool last)
+{
+	struct afs_server *server;
+	struct in_addr addr;
+
+	_enter(",{%u},%d", skb->len, last);
+
+	if (!last)
+		return 0;
+
+	/* no unmarshalling required */
+	call->state = AFS_CALL_REPLYING;
+
+	/* we'll need the file server record as that tells us which set of
+	 * vnodes to operate upon */
+	memcpy(&addr, &ip_hdr(skb)->saddr, 4);
+	server = afs_find_server(&addr);
+	if (!server)
+		return -ENOTCONN;
+	call->server = server;
+
+	INIT_WORK(&call->work, SRXAFSCB_InitCallBackState);
+	schedule_work(&call->work);
+	return 0;
+}
+
+/*
+ * allow the fileserver to see if the cache manager is still alive
+ */
+static void SRXAFSCB_Probe(struct work_struct *work)
+{
+	struct afs_call *call = container_of(work, struct afs_call, work);
+
+	_enter("");
+	afs_send_empty_reply(call);
+	_leave("");
+}
+
+/*
+ * deliver request data to a CB.Probe call
+ */
+static int afs_deliver_cb_probe(struct afs_call *call, struct sk_buff *skb,
+				bool last)
+{
+	_enter(",{%u},%d", skb->len, last);
+
+	if (skb->len > 0)
+		return -EBADMSG;
+	if (!last)
+		return 0;
+
+	/* no unmarshalling required */
+	call->state = AFS_CALL_REPLYING;
+
+	INIT_WORK(&call->work, SRXAFSCB_Probe);
+	schedule_work(&call->work);
+	return 0;
+}
+
+/*
+ * allow the fileserver to ask about the cache manager's capabilities
+ */
+static void SRXAFSCB_GetCapabilities(struct work_struct *work)
+{
+	struct afs_interface *ifs;
+	struct afs_call *call = container_of(work, struct afs_call, work);
+	int loop, nifs;
+
+	struct {
+		struct /* InterfaceAddr */ {
+			__be32 nifs;
+			__be32 uuid[11];
+			__be32 ifaddr[32];
+			__be32 netmask[32];
+			__be32 mtu[32];
+		} ia;
+		struct /* Capabilities */ {
+			__be32 capcount;
+			__be32 caps[1];
+		} cap;
+	} reply;
+
+	_enter("");
+
+	nifs = 0;
+	ifs = kcalloc(32, sizeof(*ifs), GFP_KERNEL);
+	if (ifs) {
+		nifs = afs_get_ipv4_interfaces(ifs, 32, false);
+		if (nifs < 0) {
+			kfree(ifs);
+			ifs = NULL;
+			nifs = 0;
+		}
 	}
 
-	spin_unlock(&kafscmd_attention_lock);
+	memset(&reply, 0, sizeof(reply));
+	reply.ia.nifs = htonl(nifs);
 
-	removed = 0;
-	spin_lock(&afscm_calls_lock);
-	if (!list_empty(&call->app_link)) {
-		list_del_init(&call->app_link);
-		removed = 1;
+	reply.ia.uuid[0] = htonl(afs_uuid.time_low);
+	reply.ia.uuid[1] = htonl(afs_uuid.time_mid);
+	reply.ia.uuid[2] = htonl(afs_uuid.time_hi_and_version);
+	reply.ia.uuid[3] = htonl((s8) afs_uuid.clock_seq_hi_and_reserved);
+	reply.ia.uuid[4] = htonl((s8) afs_uuid.clock_seq_low);
+	for (loop = 0; loop < 6; loop++)
+		reply.ia.uuid[loop + 5] = htonl((s8) afs_uuid.node[loop]);
+
+	if (ifs) {
+		for (loop = 0; loop < nifs; loop++) {
+			reply.ia.ifaddr[loop] = ifs[loop].address.s_addr;
+			reply.ia.netmask[loop] = ifs[loop].netmask.s_addr;
+			reply.ia.mtu[loop] = htonl(ifs[loop].mtu);
+		}
 	}
-	spin_unlock(&afscm_calls_lock);
 
-	if (removed)
-		rxrpc_put_call(call);
-
-	wake_up(&kafscmd_sleepq);
+	reply.cap.capcount = htonl(1);
+	reply.cap.caps[0] = htonl(AFS_CAP_ERROR_TRANSLATION);
+	afs_send_simple_reply(call, &reply, sizeof(reply));
 
 	_leave("");
-} /* end afscm_error() */
+}
 
-/*****************************************************************************/
 /*
- * map afs abort codes to/from Linux error codes
- * - called with call->lock held
+ * deliver request data to a CB.GetCapabilities call
  */
-static void afscm_aemap(struct rxrpc_call *call)
+static int afs_deliver_cb_get_capabilities(struct afs_call *call,
+					   struct sk_buff *skb, bool last)
 {
-	switch (call->app_err_state) {
-	case RXRPC_ESTATE_LOCAL_ABORT:
-		call->app_abort_code = -call->app_errno;
-		break;
-	case RXRPC_ESTATE_PEER_ABORT:
-		call->app_errno = -ECONNABORTED;
-		break;
-	default:
-		break;
-	}
-} /* end afscm_aemap() */
+	_enter(",{%u},%d", skb->len, last);
 
-/*****************************************************************************/
-/*
- * start the cache manager service if not already started
- */
-int afscm_start(void)
-{
-	int ret;
+	if (skb->len > 0)
+		return -EBADMSG;
+	if (!last)
+		return 0;
 
-	down_write(&afscm_sem);
-	if (!afscm_usage) {
-		ret = kernel_thread(kafscmd, NULL, 0);
-		if (ret < 0)
-			goto out;
+	/* no unmarshalling required */
+	call->state = AFS_CALL_REPLYING;
 
-		wait_for_completion(&kafscmd_alive);
-
-		ret = rxrpc_add_service(afs_transport, &AFSCM_service);
-		if (ret < 0)
-			goto kill;
-
-		afs_kafstimod_add_timer(&afs_mntpt_expiry_timer,
-					afs_mntpt_expiry_timeout * HZ);
-	}
-
-	afscm_usage++;
-	up_write(&afscm_sem);
-
+	INIT_WORK(&call->work, SRXAFSCB_GetCapabilities);
+	schedule_work(&call->work);
 	return 0;
-
- kill:
-	kafscmd_die = 1;
-	wake_up(&kafscmd_sleepq);
-	wait_for_completion(&kafscmd_dead);
-
- out:
-	up_write(&afscm_sem);
-	return ret;
-
-} /* end afscm_start() */
-
-/*****************************************************************************/
-/*
- * stop the cache manager service
- */
-void afscm_stop(void)
-{
-	struct rxrpc_call *call;
-
-	down_write(&afscm_sem);
-
-	BUG_ON(afscm_usage == 0);
-	afscm_usage--;
-
-	if (afscm_usage == 0) {
-		/* don't want more incoming calls */
-		rxrpc_del_service(afs_transport, &AFSCM_service);
-
-		/* abort any calls I've still got open (the afscm_error() will
-		 * dequeue them) */
-		spin_lock(&afscm_calls_lock);
-		while (!list_empty(&afscm_calls)) {
-			call = list_entry(afscm_calls.next,
-					  struct rxrpc_call,
-					  app_link);
-
-			list_del_init(&call->app_link);
-			rxrpc_get_call(call);
-			spin_unlock(&afscm_calls_lock);
-
-			rxrpc_call_abort(call, -ESRCH); /* abort, dequeue and
-							 * put */
-
-			_debug("nuking active call %08x.%d",
-			       ntohl(call->conn->conn_id),
-			       ntohl(call->call_id));
-			rxrpc_put_call(call);
-			rxrpc_put_call(call);
-
-			spin_lock(&afscm_calls_lock);
-		}
-		spin_unlock(&afscm_calls_lock);
-
-		/* get rid of my daemon */
-		kafscmd_die = 1;
-		wake_up(&kafscmd_sleepq);
-		wait_for_completion(&kafscmd_dead);
-
-		/* dispose of any calls waiting for attention */
-		spin_lock(&kafscmd_attention_lock);
-		while (!list_empty(&kafscmd_attention_list)) {
-			call = list_entry(kafscmd_attention_list.next,
-					  struct rxrpc_call,
-					  app_attn_link);
-
-			list_del_init(&call->app_attn_link);
-			spin_unlock(&kafscmd_attention_lock);
-
-			rxrpc_put_call(call);
-
-			spin_lock(&kafscmd_attention_lock);
-		}
-		spin_unlock(&kafscmd_attention_lock);
-
-		afs_kafstimod_del_timer(&afs_mntpt_expiry_timer);
-	}
-
-	up_write(&afscm_sem);
-
-} /* end afscm_stop() */
-
-/*****************************************************************************/
-/*
- * handle the fileserver breaking a set of callbacks
- */
-static void _SRXAFSCM_CallBack(struct rxrpc_call *call)
-{
-	struct afs_server *server;
-	size_t count, qty, tmp;
-	int ret = 0, removed;
-
-	_enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]);
-
-	server = afs_server_get_from_peer(call->conn->peer);
-
-	switch (call->app_call_state) {
-		/* we've received the last packet
-		 * - drain all the data from the call and send the reply
-		 */
-	case RXRPC_CSTATE_SRVR_GOT_ARGS:
-		ret = -EBADMSG;
-		qty = call->app_ready_qty;
-		if (qty < 8 || qty > 50 * (6 * 4) + 8)
-			break;
-
-		{
-			struct afs_callback *cb, *pcb;
-			int loop;
-			__be32 *fp, *bp;
-
-			fp = rxrpc_call_alloc_scratch(call, qty);
-
-			/* drag the entire argument block out to the scratch
-			 * space */
-			ret = rxrpc_call_read_data(call, fp, qty, 0);
-			if (ret < 0)
-				break;
-
-			/* and unmarshall the parameter block */
-			ret = -EBADMSG;
-			count = ntohl(*fp++);
-			if (count>AFSCBMAX ||
-			    (count * (3 * 4) + 8 != qty &&
-			     count * (6 * 4) + 8 != qty))
-				break;
-
-			bp = fp + count*3;
-			tmp = ntohl(*bp++);
-			if (tmp > 0 && tmp != count)
-				break;
-			if (tmp == 0)
-				bp = NULL;
-
-			pcb = cb = rxrpc_call_alloc_scratch_s(
-				call, struct afs_callback);
-
-			for (loop = count - 1; loop >= 0; loop--) {
-				pcb->fid.vid	= ntohl(*fp++);
-				pcb->fid.vnode	= ntohl(*fp++);
-				pcb->fid.unique	= ntohl(*fp++);
-				if (bp) {
-					pcb->version	= ntohl(*bp++);
-					pcb->expiry	= ntohl(*bp++);
-					pcb->type	= ntohl(*bp++);
-				}
-				else {
-					pcb->version	= 0;
-					pcb->expiry	= 0;
-					pcb->type	= AFSCM_CB_UNTYPED;
-				}
-				pcb++;
-			}
-
-			/* invoke the actual service routine */
-			ret = SRXAFSCM_CallBack(server, count, cb);
-			if (ret < 0)
-				break;
-		}
-
-		/* send the reply */
-		ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET,
-					    GFP_KERNEL, 0, &count);
-		if (ret < 0)
-			break;
-		break;
-
-		/* operation complete */
-	case RXRPC_CSTATE_COMPLETE:
-		call->app_user = NULL;
-		removed = 0;
-		spin_lock(&afscm_calls_lock);
-		if (!list_empty(&call->app_link)) {
-			list_del_init(&call->app_link);
-			removed = 1;
-		}
-		spin_unlock(&afscm_calls_lock);
-
-		if (removed)
-			rxrpc_put_call(call);
-		break;
-
-		/* operation terminated on error */
-	case RXRPC_CSTATE_ERROR:
-		call->app_user = NULL;
-		break;
-
-	default:
-		break;
-	}
-
-	if (ret < 0)
-		rxrpc_call_abort(call, ret);
-
-	afs_put_server(server);
-
-	_leave(" = %d", ret);
-
-} /* end _SRXAFSCM_CallBack() */
-
-/*****************************************************************************/
-/*
- * handle the fileserver asking us to initialise our callback state
- */
-static void _SRXAFSCM_InitCallBackState(struct rxrpc_call *call)
-{
-	struct afs_server *server;
-	size_t count;
-	int ret = 0, removed;
-
-	_enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]);
-
-	server = afs_server_get_from_peer(call->conn->peer);
-
-	switch (call->app_call_state) {
-		/* we've received the last packet - drain all the data from the
-		 * call */
-	case RXRPC_CSTATE_SRVR_GOT_ARGS:
-		/* shouldn't be any args */
-		ret = -EBADMSG;
-		break;
-
-		/* send the reply when asked for it */
-	case RXRPC_CSTATE_SRVR_SND_REPLY:
-		/* invoke the actual service routine */
-		ret = SRXAFSCM_InitCallBackState(server);
-		if (ret < 0)
-			break;
-
-		ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET,
-					    GFP_KERNEL, 0, &count);
-		if (ret < 0)
-			break;
-		break;
-
-		/* operation complete */
-	case RXRPC_CSTATE_COMPLETE:
-		call->app_user = NULL;
-		removed = 0;
-		spin_lock(&afscm_calls_lock);
-		if (!list_empty(&call->app_link)) {
-			list_del_init(&call->app_link);
-			removed = 1;
-		}
-		spin_unlock(&afscm_calls_lock);
-
-		if (removed)
-			rxrpc_put_call(call);
-		break;
-
-		/* operation terminated on error */
-	case RXRPC_CSTATE_ERROR:
-		call->app_user = NULL;
-		break;
-
-	default:
-		break;
-	}
-
-	if (ret < 0)
-		rxrpc_call_abort(call, ret);
-
-	afs_put_server(server);
-
-	_leave(" = %d", ret);
-
-} /* end _SRXAFSCM_InitCallBackState() */
-
-/*****************************************************************************/
-/*
- * handle a probe from a fileserver
- */
-static void _SRXAFSCM_Probe(struct rxrpc_call *call)
-{
-	struct afs_server *server;
-	size_t count;
-	int ret = 0, removed;
-
-	_enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]);
-
-	server = afs_server_get_from_peer(call->conn->peer);
-
-	switch (call->app_call_state) {
-		/* we've received the last packet - drain all the data from the
-		 * call */
-	case RXRPC_CSTATE_SRVR_GOT_ARGS:
-		/* shouldn't be any args */
-		ret = -EBADMSG;
-		break;
-
-		/* send the reply when asked for it */
-	case RXRPC_CSTATE_SRVR_SND_REPLY:
-		/* invoke the actual service routine */
-		ret = SRXAFSCM_Probe(server);
-		if (ret < 0)
-			break;
-
-		ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET,
-					    GFP_KERNEL, 0, &count);
-		if (ret < 0)
-			break;
-		break;
-
-		/* operation complete */
-	case RXRPC_CSTATE_COMPLETE:
-		call->app_user = NULL;
-		removed = 0;
-		spin_lock(&afscm_calls_lock);
-		if (!list_empty(&call->app_link)) {
-			list_del_init(&call->app_link);
-			removed = 1;
-		}
-		spin_unlock(&afscm_calls_lock);
-
-		if (removed)
-			rxrpc_put_call(call);
-		break;
-
-		/* operation terminated on error */
-	case RXRPC_CSTATE_ERROR:
-		call->app_user = NULL;
-		break;
-
-	default:
-		break;
-	}
-
-	if (ret < 0)
-		rxrpc_call_abort(call, ret);
-
-	afs_put_server(server);
-
-	_leave(" = %d", ret);
-
-} /* end _SRXAFSCM_Probe() */
+}
diff --git a/fs/afs/cmservice.h b/fs/afs/cmservice.h
deleted file mode 100644
index af8d4d6..0000000
--- a/fs/afs/cmservice.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* cmservice.h: AFS Cache Manager Service declarations
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_AFS_CMSERVICE_H
-#define _LINUX_AFS_CMSERVICE_H
-
-#include <rxrpc/transport.h>
-#include "types.h"
-
-/* cache manager start/stop */
-extern int afscm_start(void);
-extern void afscm_stop(void);
-
-/* cache manager server functions */
-extern int SRXAFSCM_InitCallBackState(struct afs_server *server);
-extern int SRXAFSCM_CallBack(struct afs_server *server,
-			     size_t count,
-			     struct afs_callback callbacks[]);
-extern int SRXAFSCM_Probe(struct afs_server *server);
-
-#endif /* _LINUX_AFS_CMSERVICE_H */
diff --git a/fs/afs/dir.c b/fs/afs/dir.c
index b6dc2eb..dac5b99 100644
--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -15,45 +15,53 @@
 #include <linux/slab.h>
 #include <linux/fs.h>
 #include <linux/pagemap.h>
-#include <linux/smp_lock.h>
-#include "vnode.h"
-#include "volume.h"
-#include <rxrpc/call.h>
-#include "super.h"
+#include <linux/ctype.h>
 #include "internal.h"
 
-static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry,
-				     struct nameidata *nd);
+static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
+				 struct nameidata *nd);
 static int afs_dir_open(struct inode *inode, struct file *file);
-static int afs_dir_readdir(struct file *file, void *dirent, filldir_t filldir);
+static int afs_readdir(struct file *file, void *dirent, filldir_t filldir);
 static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd);
 static int afs_d_delete(struct dentry *dentry);
-static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen,
+static void afs_d_release(struct dentry *dentry);
+static int afs_lookup_filldir(void *_cookie, const char *name, int nlen,
 				  loff_t fpos, u64 ino, unsigned dtype);
+static int afs_create(struct inode *dir, struct dentry *dentry, int mode,
+		      struct nameidata *nd);
+static int afs_mkdir(struct inode *dir, struct dentry *dentry, int mode);
+static int afs_rmdir(struct inode *dir, struct dentry *dentry);
+static int afs_unlink(struct inode *dir, struct dentry *dentry);
+static int afs_link(struct dentry *from, struct inode *dir,
+		    struct dentry *dentry);
+static int afs_symlink(struct inode *dir, struct dentry *dentry,
+		       const char *content);
+static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
+		      struct inode *new_dir, struct dentry *new_dentry);
 
 const struct file_operations afs_dir_file_operations = {
 	.open		= afs_dir_open,
-	.readdir	= afs_dir_readdir,
+	.release	= afs_release,
+	.readdir	= afs_readdir,
 };
 
 const struct inode_operations afs_dir_inode_operations = {
-	.lookup		= afs_dir_lookup,
+	.create		= afs_create,
+	.lookup		= afs_lookup,
+	.link		= afs_link,
+	.unlink		= afs_unlink,
+	.symlink	= afs_symlink,
+	.mkdir		= afs_mkdir,
+	.rmdir		= afs_rmdir,
+	.rename		= afs_rename,
+	.permission	= afs_permission,
 	.getattr	= afs_inode_getattr,
-#if 0 /* TODO */
-	.create		= afs_dir_create,
-	.link		= afs_dir_link,
-	.unlink		= afs_dir_unlink,
-	.symlink	= afs_dir_symlink,
-	.mkdir		= afs_dir_mkdir,
-	.rmdir		= afs_dir_rmdir,
-	.mknod		= afs_dir_mknod,
-	.rename		= afs_dir_rename,
-#endif
 };
 
 static struct dentry_operations afs_fs_dentry_operations = {
 	.d_revalidate	= afs_d_revalidate,
 	.d_delete	= afs_d_delete,
+	.d_release	= afs_d_release,
 };
 
 #define AFS_DIR_HASHTBL_SIZE	128
@@ -105,14 +113,13 @@
 	union afs_dir_block blocks[PAGE_SIZE / sizeof(union afs_dir_block)];
 };
 
-struct afs_dir_lookup_cookie {
+struct afs_lookup_cookie {
 	struct afs_fid	fid;
 	const char	*name;
 	size_t		nlen;
 	int		found;
 };
 
-/*****************************************************************************/
 /*
  * check that a directory page is valid
  */
@@ -128,9 +135,10 @@
 	if (qty == 0)
 		goto error;
 
-	if (page->index==0 && qty!=ntohs(dbuf->blocks[0].pagehdr.npages)) {
+	if (page->index == 0 && qty != ntohs(dbuf->blocks[0].pagehdr.npages)) {
 		printk("kAFS: %s(%lu): wrong number of dir blocks %d!=%hu\n",
-		       __FUNCTION__,dir->i_ino,qty,ntohs(dbuf->blocks[0].pagehdr.npages));
+		       __FUNCTION__, dir->i_ino, qty,
+		       ntohs(dbuf->blocks[0].pagehdr.npages));
 		goto error;
 	}
 #endif
@@ -157,13 +165,11 @@
 	SetPageChecked(page);
 	return;
 
- error:
+error:
 	SetPageChecked(page);
 	SetPageError(page);
+}
 
-} /* end afs_dir_check_page() */
-
-/*****************************************************************************/
 /*
  * discard a page cached in the pagecache
  */
@@ -171,20 +177,22 @@
 {
 	kunmap(page);
 	page_cache_release(page);
+}
 
-} /* end afs_dir_put_page() */
-
-/*****************************************************************************/
 /*
  * get a page into the pagecache
  */
-static struct page *afs_dir_get_page(struct inode *dir, unsigned long index)
+static struct page *afs_dir_get_page(struct inode *dir, unsigned long index,
+				     struct key *key)
 {
 	struct page *page;
+	struct file file = {
+		.private_data = key,
+	};
 
 	_enter("{%lu},%lu", dir->i_ino, index);
 
-	page = read_mapping_page(dir->i_mapping, index, NULL);
+	page = read_mapping_page(dir->i_mapping, index, &file);
 	if (!IS_ERR(page)) {
 		wait_on_page_locked(page);
 		kmap(page);
@@ -197,12 +205,12 @@
 	}
 	return page;
 
- fail:
+fail:
 	afs_dir_put_page(page);
+	_leave(" = -EIO");
 	return ERR_PTR(-EIO);
-} /* end afs_dir_get_page() */
+}
 
-/*****************************************************************************/
 /*
  * open an AFS directory file
  */
@@ -213,15 +221,12 @@
 	BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048);
 	BUILD_BUG_ON(sizeof(union afs_dirent) != 32);
 
-	if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED)
+	if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(inode)->flags))
 		return -ENOENT;
 
-	_leave(" = 0");
-	return 0;
+	return afs_open(inode, file);
+}
 
-} /* end afs_dir_open() */
-
-/*****************************************************************************/
 /*
  * deal with one block in an AFS directory
  */
@@ -250,7 +255,7 @@
 		/* skip entries marked unused in the bitmap */
 		if (!(block->pagehdr.bitmap[offset / 8] &
 		      (1 << (offset % 8)))) {
-			_debug("ENT[%Zu.%u]: unused\n",
+			_debug("ENT[%Zu.%u]: unused",
 			       blkoff / sizeof(union afs_dir_block), offset);
 			if (offset >= curr)
 				*fpos = blkoff +
@@ -264,7 +269,7 @@
 			       sizeof(*block) -
 			       offset * sizeof(union afs_dirent));
 
-		_debug("ENT[%Zu.%u]: %s %Zu \"%s\"\n",
+		_debug("ENT[%Zu.%u]: %s %Zu \"%s\"",
 		       blkoff / sizeof(union afs_dir_block), offset,
 		       (offset < curr ? "skip" : "fill"),
 		       nlen, dire->u.name);
@@ -274,7 +279,7 @@
 			if (next >= AFS_DIRENT_PER_BLOCK) {
 				_debug("ENT[%Zu.%u]:"
 				       " %u travelled beyond end dir block"
-				       " (len %u/%Zu)\n",
+				       " (len %u/%Zu)",
 				       blkoff / sizeof(union afs_dir_block),
 				       offset, next, tmp, nlen);
 				return -EIO;
@@ -282,13 +287,13 @@
 			if (!(block->pagehdr.bitmap[next / 8] &
 			      (1 << (next % 8)))) {
 				_debug("ENT[%Zu.%u]:"
-				       " %u unmarked extension (len %u/%Zu)\n",
+				       " %u unmarked extension (len %u/%Zu)",
 				       blkoff / sizeof(union afs_dir_block),
 				       offset, next, tmp, nlen);
 				return -EIO;
 			}
 
-			_debug("ENT[%Zu.%u]: ext %u/%Zu\n",
+			_debug("ENT[%Zu.%u]: ext %u/%Zu",
 			       blkoff / sizeof(union afs_dir_block),
 			       next, tmp, nlen);
 			next++;
@@ -304,7 +309,7 @@
 			      nlen,
 			      blkoff + offset * sizeof(union afs_dirent),
 			      ntohl(dire->u.vnode),
-			      filldir == afs_dir_lookup_filldir ?
+			      filldir == afs_lookup_filldir ?
 			      ntohl(dire->u.unique) : DT_UNKNOWN);
 		if (ret < 0) {
 			_leave(" = 0 [full]");
@@ -316,16 +321,15 @@
 
 	_leave(" = 1 [more]");
 	return 1;
-} /* end afs_dir_iterate_block() */
+}
 
-/*****************************************************************************/
 /*
- * read an AFS directory
+ * iterate through the data blob that lists the contents of an AFS directory
  */
 static int afs_dir_iterate(struct inode *dir, unsigned *fpos, void *cookie,
-			   filldir_t filldir)
+			   filldir_t filldir, struct key *key)
 {
-	union afs_dir_block	*dblock;
+	union afs_dir_block *dblock;
 	struct afs_dir_page *dbuf;
 	struct page *page;
 	unsigned blkoff, limit;
@@ -333,7 +337,7 @@
 
 	_enter("{%lu},%u,,", dir->i_ino, *fpos);
 
-	if (AFS_FS_I(dir)->flags & AFS_VNODE_DELETED) {
+	if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dir)->flags)) {
 		_leave(" = -ESTALE");
 		return -ESTALE;
 	}
@@ -348,7 +352,7 @@
 		blkoff = *fpos & ~(sizeof(union afs_dir_block) - 1);
 
 		/* fetch the appropriate page from the directory */
-		page = afs_dir_get_page(dir, blkoff / PAGE_SIZE);
+		page = afs_dir_get_page(dir, blkoff / PAGE_SIZE, key);
 		if (IS_ERR(page)) {
 			ret = PTR_ERR(page);
 			break;
@@ -377,43 +381,50 @@
 		ret = 0;
 	}
 
- out:
+out:
 	_leave(" = %d", ret);
 	return ret;
-} /* end afs_dir_iterate() */
+}
 
-/*****************************************************************************/
 /*
  * read an AFS directory
  */
-static int afs_dir_readdir(struct file *file, void *cookie, filldir_t filldir)
+static int afs_readdir(struct file *file, void *cookie, filldir_t filldir)
 {
 	unsigned fpos;
 	int ret;
 
-	_enter("{%Ld,{%lu}}", file->f_pos, file->f_path.dentry->d_inode->i_ino);
+	_enter("{%Ld,{%lu}}",
+	       file->f_pos, file->f_path.dentry->d_inode->i_ino);
+
+	ASSERT(file->private_data != NULL);
 
 	fpos = file->f_pos;
-	ret = afs_dir_iterate(file->f_path.dentry->d_inode, &fpos, cookie, filldir);
+	ret = afs_dir_iterate(file->f_path.dentry->d_inode, &fpos,
+			      cookie, filldir, file->private_data);
 	file->f_pos = fpos;
 
 	_leave(" = %d", ret);
 	return ret;
-} /* end afs_dir_readdir() */
+}
 
-/*****************************************************************************/
 /*
  * search the directory for a name
  * - if afs_dir_iterate_block() spots this function, it'll pass the FID
  *   uniquifier through dtype
  */
-static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen,
-				  loff_t fpos, u64 ino, unsigned dtype)
+static int afs_lookup_filldir(void *_cookie, const char *name, int nlen,
+			      loff_t fpos, u64 ino, unsigned dtype)
 {
-	struct afs_dir_lookup_cookie *cookie = _cookie;
+	struct afs_lookup_cookie *cookie = _cookie;
 
-	_enter("{%s,%Zu},%s,%u,,%lu,%u",
-	       cookie->name, cookie->nlen, name, nlen, ino, dtype);
+	_enter("{%s,%Zu},%s,%u,,%llu,%u",
+	       cookie->name, cookie->nlen, name, nlen,
+	       (unsigned long long) ino, dtype);
+
+	/* insanity checks first */
+	BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048);
+	BUILD_BUG_ON(sizeof(union afs_dirent) != 32);
 
 	if (cookie->nlen != nlen || memcmp(cookie->name, name, nlen) != 0) {
 		_leave(" = 0 [no]");
@@ -426,38 +437,21 @@
 
 	_leave(" = -1 [found]");
 	return -1;
-} /* end afs_dir_lookup_filldir() */
+}
 
-/*****************************************************************************/
 /*
- * look up an entry in a directory
+ * do a lookup in a directory
+ * - just returns the FID the dentry name maps to if found
  */
-static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry,
-				     struct nameidata *nd)
+static int afs_do_lookup(struct inode *dir, struct dentry *dentry,
+			 struct afs_fid *fid, struct key *key)
 {
-	struct afs_dir_lookup_cookie cookie;
+	struct afs_lookup_cookie cookie;
 	struct afs_super_info *as;
-	struct afs_vnode *vnode;
-	struct inode *inode;
 	unsigned fpos;
 	int ret;
 
-	_enter("{%lu},%p{%s}", dir->i_ino, dentry, dentry->d_name.name);
-
-	/* insanity checks first */
-	BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048);
-	BUILD_BUG_ON(sizeof(union afs_dirent) != 32);
-
-	if (dentry->d_name.len > 255) {
-		_leave(" = -ENAMETOOLONG");
-		return ERR_PTR(-ENAMETOOLONG);
-	}
-
-	vnode = AFS_FS_I(dir);
-	if (vnode->flags & AFS_VNODE_DELETED) {
-		_leave(" = -ESTALE");
-		return ERR_PTR(-ESTALE);
-	}
+	_enter("{%lu},%p{%s},", dir->i_ino, dentry, dentry->d_name.name);
 
 	as = dir->i_sb->s_fs_info;
 
@@ -468,174 +462,229 @@
 	cookie.found	= 0;
 
 	fpos = 0;
-	ret = afs_dir_iterate(dir, &fpos, &cookie, afs_dir_lookup_filldir);
+	ret = afs_dir_iterate(dir, &fpos, &cookie, afs_lookup_filldir,
+			      key);
 	if (ret < 0) {
-		_leave(" = %d", ret);
-		return ERR_PTR(ret);
+		_leave(" = %d [iter]", ret);
+		return ret;
 	}
 
 	ret = -ENOENT;
 	if (!cookie.found) {
-		_leave(" = %d", ret);
+		_leave(" = -ENOENT [not found]");
+		return -ENOENT;
+	}
+
+	*fid = cookie.fid;
+	_leave(" = 0 { vn=%u u=%u }", fid->vnode, fid->unique);
+	return 0;
+}
+
+/*
+ * look up an entry in a directory
+ */
+static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
+				 struct nameidata *nd)
+{
+	struct afs_vnode *vnode;
+	struct afs_fid fid;
+	struct inode *inode;
+	struct key *key;
+	int ret;
+
+	vnode = AFS_FS_I(dir);
+
+	_enter("{%x:%d},%p{%s},",
+	       vnode->fid.vid, vnode->fid.vnode, dentry, dentry->d_name.name);
+
+	ASSERTCMP(dentry->d_inode, ==, NULL);
+
+	if (dentry->d_name.len > 255) {
+		_leave(" = -ENAMETOOLONG");
+		return ERR_PTR(-ENAMETOOLONG);
+	}
+
+	if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
+		_leave(" = -ESTALE");
+		return ERR_PTR(-ESTALE);
+	}
+
+	key = afs_request_key(vnode->volume->cell);
+	if (IS_ERR(key)) {
+		_leave(" = %ld [key]", PTR_ERR(key));
+		return ERR_PTR(PTR_ERR(key));
+	}
+
+	ret = afs_validate(vnode, key);
+	if (ret < 0) {
+		key_put(key);
+		_leave(" = %d [val]", ret);
 		return ERR_PTR(ret);
 	}
 
-	/* instantiate the dentry */
-	ret = afs_iget(dir->i_sb, &cookie.fid, &inode);
+	ret = afs_do_lookup(dir, dentry, &fid, key);
 	if (ret < 0) {
-		_leave(" = %d", ret);
+		key_put(key);
+		if (ret == -ENOENT) {
+			d_add(dentry, NULL);
+			_leave(" = NULL [negative]");
+			return NULL;
+		}
+		_leave(" = %d [do]", ret);
 		return ERR_PTR(ret);
 	}
+	dentry->d_fsdata = (void *)(unsigned long) vnode->status.data_version;
+
+	/* instantiate the dentry */
+	inode = afs_iget(dir->i_sb, key, &fid, NULL, NULL);
+	key_put(key);
+	if (IS_ERR(inode)) {
+		_leave(" = %ld", PTR_ERR(inode));
+		return ERR_PTR(PTR_ERR(inode));
+	}
 
 	dentry->d_op = &afs_fs_dentry_operations;
-	dentry->d_fsdata = (void *) (unsigned long) vnode->status.version;
 
 	d_add(dentry, inode);
 	_leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%lu }",
-	       cookie.fid.vnode,
-	       cookie.fid.unique,
+	       fid.vnode,
+	       fid.unique,
 	       dentry->d_inode->i_ino,
 	       dentry->d_inode->i_version);
 
 	return NULL;
-} /* end afs_dir_lookup() */
+}
 
-/*****************************************************************************/
 /*
  * check that a dentry lookup hit has found a valid entry
  * - NOTE! the hit can be a negative hit too, so we can't assume we have an
  *   inode
- * (derived from nfs_lookup_revalidate)
  */
 static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
-	struct afs_dir_lookup_cookie cookie;
+	struct afs_vnode *vnode, *dir;
+	struct afs_fid fid;
 	struct dentry *parent;
-	struct inode *inode, *dir;
-	unsigned fpos;
+	struct key *key;
+	void *dir_version;
 	int ret;
 
-	_enter("{sb=%p n=%s},", dentry->d_sb, dentry->d_name.name);
+	vnode = AFS_FS_I(dentry->d_inode);
+
+	if (dentry->d_inode)
+		_enter("{v={%x:%u} n=%s fl=%lx},",
+		       vnode->fid.vid, vnode->fid.vnode, dentry->d_name.name,
+		       vnode->flags);
+	else
+		_enter("{neg n=%s}", dentry->d_name.name);
+
+	key = afs_request_key(AFS_FS_S(dentry->d_sb)->volume->cell);
+	if (IS_ERR(key))
+		key = NULL;
 
 	/* lock down the parent dentry so we can peer at it */
-	parent = dget_parent(dentry->d_parent);
-
-	dir = parent->d_inode;
-	inode = dentry->d_inode;
-
-	/* handle a negative dentry */
-	if (!inode)
+	parent = dget_parent(dentry);
+	if (!parent->d_inode)
 		goto out_bad;
 
-	/* handle a bad inode */
-	if (is_bad_inode(inode)) {
-		printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n",
-		       dentry->d_parent->d_name.name, dentry->d_name.name);
-		goto out_bad;
-	}
+	dir = AFS_FS_I(parent->d_inode);
 
-	/* force a full look up if the parent directory changed since last the
-	 * server was consulted
-	 * - otherwise this inode must still exist, even if the inode details
-	 *   themselves have changed
-	 */
-	if (AFS_FS_I(dir)->flags & AFS_VNODE_CHANGED)
-		afs_vnode_fetch_status(AFS_FS_I(dir));
+	/* validate the parent directory */
+	if (test_bit(AFS_VNODE_MODIFIED, &dir->flags))
+		afs_validate(dir, key);
 
-	if (AFS_FS_I(dir)->flags & AFS_VNODE_DELETED) {
+	if (test_bit(AFS_VNODE_DELETED, &dir->flags)) {
 		_debug("%s: parent dir deleted", dentry->d_name.name);
 		goto out_bad;
 	}
 
-	if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED) {
-		_debug("%s: file already deleted", dentry->d_name.name);
-		goto out_bad;
-	}
+	dir_version = (void *) (unsigned long) dir->status.data_version;
+	if (dentry->d_fsdata == dir_version)
+		goto out_valid; /* the dir contents are unchanged */
 
-	if ((unsigned long) dentry->d_fsdata !=
-	    (unsigned long) AFS_FS_I(dir)->status.version) {
-		_debug("%s: parent changed %lu -> %u",
-		       dentry->d_name.name,
-		       (unsigned long) dentry->d_fsdata,
-		       (unsigned) AFS_FS_I(dir)->status.version);
+	_debug("dir modified");
 
-		/* search the directory for this vnode */
-		cookie.name	= dentry->d_name.name;
-		cookie.nlen	= dentry->d_name.len;
-		cookie.fid.vid	= AFS_FS_I(inode)->volume->vid;
-		cookie.found	= 0;
-
-		fpos = 0;
-		ret = afs_dir_iterate(dir, &fpos, &cookie,
-				      afs_dir_lookup_filldir);
-		if (ret < 0) {
-			_debug("failed to iterate dir %s: %d",
-			       parent->d_name.name, ret);
+	/* search the directory for this vnode */
+	ret = afs_do_lookup(&dir->vfs_inode, dentry, &fid, key);
+	switch (ret) {
+	case 0:
+		/* the filename maps to something */
+		if (!dentry->d_inode)
 			goto out_bad;
-		}
-
-		if (!cookie.found) {
-			_debug("%s: dirent not found", dentry->d_name.name);
-			goto not_found;
+		if (is_bad_inode(dentry->d_inode)) {
+			printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n",
+			       parent->d_name.name, dentry->d_name.name);
+			goto out_bad;
 		}
 
 		/* if the vnode ID has changed, then the dirent points to a
 		 * different file */
-		if (cookie.fid.vnode != AFS_FS_I(inode)->fid.vnode) {
-			_debug("%s: dirent changed", dentry->d_name.name);
+		if (fid.vnode != vnode->fid.vnode) {
+			_debug("%s: dirent changed [%u != %u]",
+			       dentry->d_name.name, fid.vnode,
+			       vnode->fid.vnode);
 			goto not_found;
 		}
 
 		/* if the vnode ID uniqifier has changed, then the file has
-		 * been deleted */
-		if (cookie.fid.unique != AFS_FS_I(inode)->fid.unique) {
+		 * been deleted and replaced, and the original vnode ID has
+		 * been reused */
+		if (fid.unique != vnode->fid.unique) {
 			_debug("%s: file deleted (uq %u -> %u I:%lu)",
-			       dentry->d_name.name,
-			       cookie.fid.unique,
-			       AFS_FS_I(inode)->fid.unique,
-			       inode->i_version);
-			spin_lock(&AFS_FS_I(inode)->lock);
-			AFS_FS_I(inode)->flags |= AFS_VNODE_DELETED;
-			spin_unlock(&AFS_FS_I(inode)->lock);
-			invalidate_remote_inode(inode);
-			goto out_bad;
+			       dentry->d_name.name, fid.unique,
+			       vnode->fid.unique, dentry->d_inode->i_version);
+			spin_lock(&vnode->lock);
+			set_bit(AFS_VNODE_DELETED, &vnode->flags);
+			spin_unlock(&vnode->lock);
+			goto not_found;
 		}
+		goto out_valid;
 
-		dentry->d_fsdata =
-			(void *) (unsigned long) AFS_FS_I(dir)->status.version;
+	case -ENOENT:
+		/* the filename is unknown */
+		_debug("%s: dirent not found", dentry->d_name.name);
+		if (dentry->d_inode)
+			goto not_found;
+		goto out_valid;
+
+	default:
+		_debug("failed to iterate dir %s: %d",
+		       parent->d_name.name, ret);
+		goto out_bad;
 	}
 
- out_valid:
+out_valid:
+	dentry->d_fsdata = dir_version;
+out_skip:
 	dput(parent);
+	key_put(key);
 	_leave(" = 1 [valid]");
 	return 1;
 
 	/* the dirent, if it exists, now points to a different vnode */
- not_found:
+not_found:
 	spin_lock(&dentry->d_lock);
 	dentry->d_flags |= DCACHE_NFSFS_RENAMED;
 	spin_unlock(&dentry->d_lock);
 
- out_bad:
-	if (inode) {
+out_bad:
+	if (dentry->d_inode) {
 		/* don't unhash if we have submounts */
 		if (have_submounts(dentry))
-			goto out_valid;
+			goto out_skip;
 	}
 
-	shrink_dcache_parent(dentry);
-
 	_debug("dropping dentry %s/%s",
-	       dentry->d_parent->d_name.name, dentry->d_name.name);
+	       parent->d_name.name, dentry->d_name.name);
+	shrink_dcache_parent(dentry);
 	d_drop(dentry);
-
 	dput(parent);
+	key_put(key);
 
 	_leave(" = 0 [bad]");
 	return 0;
-} /* end afs_d_revalidate() */
+}
 
-/*****************************************************************************/
 /*
  * allow the VFS to enquire as to whether a dentry should be unhashed (mustn't
  * sleep)
@@ -649,15 +698,444 @@
 	if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
 		goto zap;
 
-	if (dentry->d_inode) {
-		if (AFS_FS_I(dentry->d_inode)->flags & AFS_VNODE_DELETED)
+	if (dentry->d_inode &&
+	    test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dentry->d_inode)->flags))
 			goto zap;
-	}
 
 	_leave(" = 0 [keep]");
 	return 0;
 
- zap:
+zap:
 	_leave(" = 1 [zap]");
 	return 1;
-} /* end afs_d_delete() */
+}
+
+/*
+ * handle dentry release
+ */
+static void afs_d_release(struct dentry *dentry)
+{
+	_enter("%s", dentry->d_name.name);
+}
+
+/*
+ * create a directory on an AFS filesystem
+ */
+static int afs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	struct afs_file_status status;
+	struct afs_callback cb;
+	struct afs_server *server;
+	struct afs_vnode *dvnode, *vnode;
+	struct afs_fid fid;
+	struct inode *inode;
+	struct key *key;
+	int ret;
+
+	dvnode = AFS_FS_I(dir);
+
+	_enter("{%x:%d},{%s},%o",
+	       dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, mode);
+
+	ret = -ENAMETOOLONG;
+	if (dentry->d_name.len > 255)
+		goto error;
+
+	key = afs_request_key(dvnode->volume->cell);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error;
+	}
+
+	mode |= S_IFDIR;
+	ret = afs_vnode_create(dvnode, key, dentry->d_name.name,
+			       mode, &fid, &status, &cb, &server);
+	if (ret < 0)
+		goto mkdir_error;
+
+	inode = afs_iget(dir->i_sb, key, &fid, &status, &cb);
+	if (IS_ERR(inode)) {
+		/* ENOMEM at a really inconvenient time - just abandon the new
+		 * directory on the server */
+		ret = PTR_ERR(inode);
+		goto iget_error;
+	}
+
+	/* apply the status report we've got for the new vnode */
+	vnode = AFS_FS_I(inode);
+	spin_lock(&vnode->lock);
+	vnode->update_cnt++;
+	spin_unlock(&vnode->lock);
+	afs_vnode_finalise_status_update(vnode, server);
+	afs_put_server(server);
+
+	d_instantiate(dentry, inode);
+	if (d_unhashed(dentry)) {
+		_debug("not hashed");
+		d_rehash(dentry);
+	}
+	key_put(key);
+	_leave(" = 0");
+	return 0;
+
+iget_error:
+	afs_put_server(server);
+mkdir_error:
+	key_put(key);
+error:
+	d_drop(dentry);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * remove a directory from an AFS filesystem
+ */
+static int afs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	struct afs_vnode *dvnode, *vnode;
+	struct key *key;
+	int ret;
+
+	dvnode = AFS_FS_I(dir);
+
+	_enter("{%x:%d},{%s}",
+	       dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name);
+
+	ret = -ENAMETOOLONG;
+	if (dentry->d_name.len > 255)
+		goto error;
+
+	key = afs_request_key(dvnode->volume->cell);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error;
+	}
+
+	ret = afs_vnode_remove(dvnode, key, dentry->d_name.name, true);
+	if (ret < 0)
+		goto rmdir_error;
+
+	if (dentry->d_inode) {
+		vnode = AFS_FS_I(dentry->d_inode);
+		clear_nlink(&vnode->vfs_inode);
+		set_bit(AFS_VNODE_DELETED, &vnode->flags);
+		afs_discard_callback_on_delete(vnode);
+	}
+
+	key_put(key);
+	_leave(" = 0");
+	return 0;
+
+rmdir_error:
+	key_put(key);
+error:
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * remove a file from an AFS filesystem
+ */
+static int afs_unlink(struct inode *dir, struct dentry *dentry)
+{
+	struct afs_vnode *dvnode, *vnode;
+	struct key *key;
+	int ret;
+
+	dvnode = AFS_FS_I(dir);
+
+	_enter("{%x:%d},{%s}",
+	       dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name);
+
+	ret = -ENAMETOOLONG;
+	if (dentry->d_name.len > 255)
+		goto error;
+
+	key = afs_request_key(dvnode->volume->cell);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error;
+	}
+
+	if (dentry->d_inode) {
+		vnode = AFS_FS_I(dentry->d_inode);
+
+		/* make sure we have a callback promise on the victim */
+		ret = afs_validate(vnode, key);
+		if (ret < 0)
+			goto error;
+	}
+
+	ret = afs_vnode_remove(dvnode, key, dentry->d_name.name, false);
+	if (ret < 0)
+		goto remove_error;
+
+	if (dentry->d_inode) {
+		/* if the file wasn't deleted due to excess hard links, the
+		 * fileserver will break the callback promise on the file - if
+		 * it had one - before it returns to us, and if it was deleted,
+		 * it won't
+		 *
+		 * however, if we didn't have a callback promise outstanding,
+		 * or it was outstanding on a different server, then it won't
+		 * break it either...
+		 */
+		vnode = AFS_FS_I(dentry->d_inode);
+		if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
+			_debug("AFS_VNODE_DELETED");
+		if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags))
+			_debug("AFS_VNODE_CB_BROKEN");
+		set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
+		ret = afs_validate(vnode, key);
+		_debug("nlink %d [val %d]", vnode->vfs_inode.i_nlink, ret);
+	}
+
+	key_put(key);
+	_leave(" = 0");
+	return 0;
+
+remove_error:
+	key_put(key);
+error:
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * create a regular file on an AFS filesystem
+ */
+static int afs_create(struct inode *dir, struct dentry *dentry, int mode,
+		      struct nameidata *nd)
+{
+	struct afs_file_status status;
+	struct afs_callback cb;
+	struct afs_server *server;
+	struct afs_vnode *dvnode, *vnode;
+	struct afs_fid fid;
+	struct inode *inode;
+	struct key *key;
+	int ret;
+
+	dvnode = AFS_FS_I(dir);
+
+	_enter("{%x:%d},{%s},%o,",
+	       dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, mode);
+
+	ret = -ENAMETOOLONG;
+	if (dentry->d_name.len > 255)
+		goto error;
+
+	key = afs_request_key(dvnode->volume->cell);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error;
+	}
+
+	mode |= S_IFREG;
+	ret = afs_vnode_create(dvnode, key, dentry->d_name.name,
+			       mode, &fid, &status, &cb, &server);
+	if (ret < 0)
+		goto create_error;
+
+	inode = afs_iget(dir->i_sb, key, &fid, &status, &cb);
+	if (IS_ERR(inode)) {
+		/* ENOMEM at a really inconvenient time - just abandon the new
+		 * directory on the server */
+		ret = PTR_ERR(inode);
+		goto iget_error;
+	}
+
+	/* apply the status report we've got for the new vnode */
+	vnode = AFS_FS_I(inode);
+	spin_lock(&vnode->lock);
+	vnode->update_cnt++;
+	spin_unlock(&vnode->lock);
+	afs_vnode_finalise_status_update(vnode, server);
+	afs_put_server(server);
+
+	d_instantiate(dentry, inode);
+	if (d_unhashed(dentry)) {
+		_debug("not hashed");
+		d_rehash(dentry);
+	}
+	key_put(key);
+	_leave(" = 0");
+	return 0;
+
+iget_error:
+	afs_put_server(server);
+create_error:
+	key_put(key);
+error:
+	d_drop(dentry);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * create a hard link between files in an AFS filesystem
+ */
+static int afs_link(struct dentry *from, struct inode *dir,
+		    struct dentry *dentry)
+{
+	struct afs_vnode *dvnode, *vnode;
+	struct key *key;
+	int ret;
+
+	vnode = AFS_FS_I(from->d_inode);
+	dvnode = AFS_FS_I(dir);
+
+	_enter("{%x:%d},{%x:%d},{%s}",
+	       vnode->fid.vid, vnode->fid.vnode,
+	       dvnode->fid.vid, dvnode->fid.vnode,
+	       dentry->d_name.name);
+
+	ret = -ENAMETOOLONG;
+	if (dentry->d_name.len > 255)
+		goto error;
+
+	key = afs_request_key(dvnode->volume->cell);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error;
+	}
+
+	ret = afs_vnode_link(dvnode, vnode, key, dentry->d_name.name);
+	if (ret < 0)
+		goto link_error;
+
+	atomic_inc(&vnode->vfs_inode.i_count);
+	d_instantiate(dentry, &vnode->vfs_inode);
+	key_put(key);
+	_leave(" = 0");
+	return 0;
+
+link_error:
+	key_put(key);
+error:
+	d_drop(dentry);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * create a symlink in an AFS filesystem
+ */
+static int afs_symlink(struct inode *dir, struct dentry *dentry,
+		       const char *content)
+{
+	struct afs_file_status status;
+	struct afs_server *server;
+	struct afs_vnode *dvnode, *vnode;
+	struct afs_fid fid;
+	struct inode *inode;
+	struct key *key;
+	int ret;
+
+	dvnode = AFS_FS_I(dir);
+
+	_enter("{%x:%d},{%s},%s",
+	       dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name,
+	       content);
+
+	ret = -ENAMETOOLONG;
+	if (dentry->d_name.len > 255)
+		goto error;
+
+	ret = -EINVAL;
+	if (strlen(content) > 1023)
+		goto error;
+
+	key = afs_request_key(dvnode->volume->cell);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error;
+	}
+
+	ret = afs_vnode_symlink(dvnode, key, dentry->d_name.name, content,
+				&fid, &status, &server);
+	if (ret < 0)
+		goto create_error;
+
+	inode = afs_iget(dir->i_sb, key, &fid, &status, NULL);
+	if (IS_ERR(inode)) {
+		/* ENOMEM at a really inconvenient time - just abandon the new
+		 * directory on the server */
+		ret = PTR_ERR(inode);
+		goto iget_error;
+	}
+
+	/* apply the status report we've got for the new vnode */
+	vnode = AFS_FS_I(inode);
+	spin_lock(&vnode->lock);
+	vnode->update_cnt++;
+	spin_unlock(&vnode->lock);
+	afs_vnode_finalise_status_update(vnode, server);
+	afs_put_server(server);
+
+	d_instantiate(dentry, inode);
+	if (d_unhashed(dentry)) {
+		_debug("not hashed");
+		d_rehash(dentry);
+	}
+	key_put(key);
+	_leave(" = 0");
+	return 0;
+
+iget_error:
+	afs_put_server(server);
+create_error:
+	key_put(key);
+error:
+	d_drop(dentry);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * rename a file in an AFS filesystem and/or move it between directories
+ */
+static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
+		      struct inode *new_dir, struct dentry *new_dentry)
+{
+	struct afs_vnode *orig_dvnode, *new_dvnode, *vnode;
+	struct key *key;
+	int ret;
+
+	vnode = AFS_FS_I(old_dentry->d_inode);
+	orig_dvnode = AFS_FS_I(old_dir);
+	new_dvnode = AFS_FS_I(new_dir);
+
+	_enter("{%x:%d},{%x:%d},{%x:%d},{%s}",
+	       orig_dvnode->fid.vid, orig_dvnode->fid.vnode,
+	       vnode->fid.vid, vnode->fid.vnode,
+	       new_dvnode->fid.vid, new_dvnode->fid.vnode,
+	       new_dentry->d_name.name);
+
+	ret = -ENAMETOOLONG;
+	if (new_dentry->d_name.len > 255)
+		goto error;
+
+	key = afs_request_key(orig_dvnode->volume->cell);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error;
+	}
+
+	ret = afs_vnode_rename(orig_dvnode, new_dvnode, key,
+			       old_dentry->d_name.name,
+			       new_dentry->d_name.name);
+	if (ret < 0)
+		goto rename_error;
+	key_put(key);
+	_leave(" = 0");
+	return 0;
+
+rename_error:
+	key_put(key);
+error:
+	d_drop(new_dentry);
+	_leave(" = %d", ret);
+	return ret;
+}
diff --git a/fs/afs/errors.h b/fs/afs/errors.h
deleted file mode 100644
index 574d94a..0000000
--- a/fs/afs/errors.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* errors.h: AFS abort/error codes
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_AFS_ERRORS_H
-#define _LINUX_AFS_ERRORS_H
-
-#include "types.h"
-
-/* file server abort codes */
-typedef enum {
-	VSALVAGE	= 101,	/* volume needs salvaging */
-	VNOVNODE	= 102,	/* no such file/dir (vnode) */
-	VNOVOL		= 103,	/* no such volume or volume unavailable */
-	VVOLEXISTS	= 104,	/* volume name already exists */
-	VNOSERVICE	= 105,	/* volume not currently in service */
-	VOFFLINE	= 106,	/* volume is currently offline (more info available [VVL-spec]) */
-	VONLINE		= 107,	/* volume is already online */
-	VDISKFULL	= 108,	/* disk partition is full */
-	VOVERQUOTA	= 109,	/* volume's maximum quota exceeded */
-	VBUSY		= 110,	/* volume is temporarily unavailable */
-	VMOVED		= 111,	/* volume moved to new server - ask this FS where */
-} afs_rxfs_abort_t;
-
-extern int afs_abort_to_error(int abortcode);
-
-#endif /* _LINUX_AFS_ERRORS_H */
diff --git a/fs/afs/file.c b/fs/afs/file.c
index b176345..ae25649 100644
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -1,6 +1,6 @@
-/* file.c: AFS filesystem file handling
+/* AFS filesystem file handling
  *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
@@ -15,22 +15,25 @@
 #include <linux/slab.h>
 #include <linux/fs.h>
 #include <linux/pagemap.h>
-#include "volume.h"
-#include "vnode.h"
-#include <rxrpc/call.h>
 #include "internal.h"
 
-#if 0
-static int afs_file_open(struct inode *inode, struct file *file);
-static int afs_file_release(struct inode *inode, struct file *file);
-#endif
-
 static int afs_file_readpage(struct file *file, struct page *page);
 static void afs_file_invalidatepage(struct page *page, unsigned long offset);
 static int afs_file_releasepage(struct page *page, gfp_t gfp_flags);
 
+const struct file_operations afs_file_operations = {
+	.open		= afs_open,
+	.release	= afs_release,
+	.llseek		= generic_file_llseek,
+	.read		= do_sync_read,
+	.aio_read	= generic_file_aio_read,
+	.mmap		= generic_file_readonly_mmap,
+	.sendfile	= generic_file_sendfile,
+};
+
 const struct inode_operations afs_file_inode_operations = {
 	.getattr	= afs_inode_getattr,
+	.permission	= afs_permission,
 };
 
 const struct address_space_operations afs_fs_aops = {
@@ -40,7 +43,48 @@
 	.invalidatepage	= afs_file_invalidatepage,
 };
 
-/*****************************************************************************/
+/*
+ * open an AFS file or directory and attach a key to it
+ */
+int afs_open(struct inode *inode, struct file *file)
+{
+	struct afs_vnode *vnode = AFS_FS_I(inode);
+	struct key *key;
+	int ret;
+
+	_enter("{%x:%x},", vnode->fid.vid, vnode->fid.vnode);
+
+	key = afs_request_key(vnode->volume->cell);
+	if (IS_ERR(key)) {
+		_leave(" = %ld [key]", PTR_ERR(key));
+		return PTR_ERR(key);
+	}
+
+	ret = afs_validate(vnode, key);
+	if (ret < 0) {
+		_leave(" = %d [val]", ret);
+		return ret;
+	}
+
+	file->private_data = key;
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * release an AFS file or directory and discard its key
+ */
+int afs_release(struct inode *inode, struct file *file)
+{
+	struct afs_vnode *vnode = AFS_FS_I(inode);
+
+	_enter("{%x:%x},", vnode->fid.vid, vnode->fid.vnode);
+
+	key_put(file->private_data);
+	_leave(" = 0");
+	return 0;
+}
+
 /*
  * deal with notification that a page was read from the cache
  */
@@ -58,10 +102,9 @@
 		SetPageUptodate(page);
 	unlock_page(page);
 
-} /* end afs_file_readpage_read_complete() */
+}
 #endif
 
-/*****************************************************************************/
 /*
  * deal with notification that a page was written to the cache
  */
@@ -74,41 +117,38 @@
 	_enter("%p,%p,%p,%d", cookie_data, page, data, error);
 
 	unlock_page(page);
-
-} /* end afs_file_readpage_write_complete() */
+}
 #endif
 
-/*****************************************************************************/
 /*
  * AFS read page from file (or symlink)
  */
 static int afs_file_readpage(struct file *file, struct page *page)
 {
-	struct afs_rxfs_fetch_descriptor desc;
-#ifdef AFS_CACHING_SUPPORT
-	struct cachefs_page *pageio;
-#endif
 	struct afs_vnode *vnode;
 	struct inode *inode;
+	struct key *key;
+	size_t len;
+	off_t offset;
 	int ret;
 
 	inode = page->mapping->host;
 
-	_enter("{%lu},{%lu}", inode->i_ino, page->index);
+	ASSERT(file != NULL);
+	key = file->private_data;
+	ASSERT(key != NULL);
+
+	_enter("{%x},{%lu},{%lu}", key_serial(key), inode->i_ino, page->index);
 
 	vnode = AFS_FS_I(inode);
 
 	BUG_ON(!PageLocked(page));
 
 	ret = -ESTALE;
-	if (vnode->flags & AFS_VNODE_DELETED)
+	if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
 		goto error;
 
 #ifdef AFS_CACHING_SUPPORT
-	ret = cachefs_page_get_private(page, &pageio, GFP_NOIO);
-	if (ret < 0)
-		goto error;
-
 	/* is it cached? */
 	ret = cachefs_read_or_alloc_page(vnode->cache,
 					 page,
@@ -132,26 +172,19 @@
 	case -ENOBUFS:
 	case -ENODATA:
 	default:
-		desc.fid	= vnode->fid;
-		desc.offset	= page->index << PAGE_CACHE_SHIFT;
-		desc.size	= min((size_t) (inode->i_size - desc.offset),
-				      (size_t) PAGE_SIZE);
-		desc.buffer	= kmap(page);
-
-		clear_page(desc.buffer);
+		offset = page->index << PAGE_CACHE_SHIFT;
+		len = min_t(size_t, i_size_read(inode) - offset, PAGE_SIZE);
 
 		/* read the contents of the file from the server into the
 		 * page */
-		ret = afs_vnode_fetch_data(vnode, &desc);
-		kunmap(page);
+		ret = afs_vnode_fetch_data(vnode, key, offset, len, page);
 		if (ret < 0) {
-			if (ret==-ENOENT) {
+			if (ret == -ENOENT) {
 				_debug("got NOENT from server"
 				       " - marking file deleted and stale");
-				vnode->flags |= AFS_VNODE_DELETED;
+				set_bit(AFS_VNODE_DELETED, &vnode->flags);
 				ret = -ESTALE;
 			}
-
 #ifdef AFS_CACHING_SUPPORT
 			cachefs_uncache_page(vnode->cache, page);
 #endif
@@ -178,16 +211,13 @@
 	_leave(" = 0");
 	return 0;
 
- error:
+error:
 	SetPageError(page);
 	unlock_page(page);
-
 	_leave(" = %d", ret);
 	return ret;
+}
 
-} /* end afs_file_readpage() */
-
-/*****************************************************************************/
 /*
  * get a page cookie for the specified page
  */
@@ -202,10 +232,9 @@
 
 	_leave(" = %d", ret);
 	return ret;
-} /* end afs_cache_get_page_cookie() */
+}
 #endif
 
-/*****************************************************************************/
 /*
  * invalidate part or all of a page
  */
@@ -240,9 +269,8 @@
 	}
 
 	_leave(" = %d", ret);
-} /* end afs_file_invalidatepage() */
+}
 
-/*****************************************************************************/
 /*
  * release a page and cleanup its private data
  */
@@ -267,4 +295,4 @@
 
 	_leave(" = 0");
 	return 0;
-} /* end afs_file_releasepage() */
+}
diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c
index 61bc371..2393d2a 100644
--- a/fs/afs/fsclient.c
+++ b/fs/afs/fsclient.c
@@ -1,6 +1,6 @@
-/* fsclient.c: AFS File Server client stubs
+/* AFS File Server client stubs
  *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
@@ -11,827 +11,927 @@
 
 #include <linux/init.h>
 #include <linux/sched.h>
-#include <rxrpc/rxrpc.h>
-#include <rxrpc/transport.h>
-#include <rxrpc/connection.h>
-#include <rxrpc/call.h>
-#include "fsclient.h"
-#include "cmservice.h"
-#include "vnode.h"
-#include "server.h"
-#include "errors.h"
+#include <linux/circ_buf.h>
 #include "internal.h"
+#include "afs_fs.h"
 
-#define FSFETCHSTATUS		132	/* AFS Fetch file status */
-#define FSFETCHDATA		130	/* AFS Fetch file data */
-#define FSGIVEUPCALLBACKS	147	/* AFS Discard callback promises */
-#define FSGETVOLUMEINFO		148	/* AFS Get root volume information */
-#define FSGETROOTVOLUME		151	/* AFS Get root volume name */
-#define FSLOOKUP		161	/* AFS lookup file in directory */
-
-/*****************************************************************************/
 /*
- * map afs abort codes to/from Linux error codes
- * - called with call->lock held
+ * decode an AFSFid block
  */
-static void afs_rxfs_aemap(struct rxrpc_call *call)
+static void xdr_decode_AFSFid(const __be32 **_bp, struct afs_fid *fid)
 {
-	switch (call->app_err_state) {
-	case RXRPC_ESTATE_LOCAL_ABORT:
-		call->app_abort_code = -call->app_errno;
-		break;
-	case RXRPC_ESTATE_PEER_ABORT:
-		call->app_errno = afs_abort_to_error(call->app_abort_code);
-		break;
-	default:
-		break;
-	}
-} /* end afs_rxfs_aemap() */
+	const __be32 *bp = *_bp;
 
-/*****************************************************************************/
+	fid->vid		= ntohl(*bp++);
+	fid->vnode		= ntohl(*bp++);
+	fid->unique		= ntohl(*bp++);
+	*_bp = bp;
+}
+
 /*
- * get the root volume name from a fileserver
- * - this operation doesn't seem to work correctly in OpenAFS server 1.2.2
+ * decode an AFSFetchStatus block
  */
-#if 0
-int afs_rxfs_get_root_volume(struct afs_server *server,
-			     char *buf, size_t *buflen)
+static void xdr_decode_AFSFetchStatus(const __be32 **_bp,
+				      struct afs_file_status *status,
+				      struct afs_vnode *vnode)
 {
-	struct rxrpc_connection *conn;
-	struct rxrpc_call *call;
-	struct kvec piov[2];
-	size_t sent;
-	int ret;
-	u32 param[1];
+	const __be32 *bp = *_bp;
+	umode_t mode;
+	u64 data_version, size;
+	u32 changed = 0; /* becomes non-zero if ctime-type changes seen */
 
-	DECLARE_WAITQUEUE(myself, current);
+#define EXTRACT(DST)				\
+	do {					\
+		u32 x = ntohl(*bp++);		\
+		changed |= DST - x;		\
+		DST = x;			\
+	} while (0)
 
-	kenter("%p,%p,%u",server, buf, *buflen);
+	status->if_version = ntohl(*bp++);
+	EXTRACT(status->type);
+	EXTRACT(status->nlink);
+	size = ntohl(*bp++);
+	data_version = ntohl(*bp++);
+	EXTRACT(status->author);
+	EXTRACT(status->owner);
+	EXTRACT(status->caller_access); /* call ticket dependent */
+	EXTRACT(status->anon_access);
+	EXTRACT(status->mode);
+	EXTRACT(status->parent.vnode);
+	EXTRACT(status->parent.unique);
+	bp++; /* seg size */
+	status->mtime_client = ntohl(*bp++);
+	status->mtime_server = ntohl(*bp++);
+	EXTRACT(status->group);
+	bp++; /* sync counter */
+	data_version |= (u64) ntohl(*bp++) << 32;
+	bp++; /* lock count */
+	size |= (u64) ntohl(*bp++) << 32;
+	bp++; /* spare 4 */
+	*_bp = bp;
 
-	/* get hold of the fileserver connection */
-	ret = afs_server_get_fsconn(server, &conn);
-	if (ret < 0)
-		goto out;
-
-	/* create a call through that connection */
-	ret = rxrpc_create_call(conn, NULL, NULL, afs_rxfs_aemap, &call);
-	if (ret < 0) {
-		printk("kAFS: Unable to create call: %d\n", ret);
-		goto out_put_conn;
+	if (size != status->size) {
+		status->size = size;
+		changed |= true;
 	}
-	call->app_opcode = FSGETROOTVOLUME;
+	status->mode &= S_IALLUGO;
 
-	/* we want to get event notifications from the call */
-	add_wait_queue(&call->waitq, &myself);
+	_debug("vnode time %lx, %lx",
+	       status->mtime_client, status->mtime_server);
 
-	/* marshall the parameters */
-	param[0] = htonl(FSGETROOTVOLUME);
+	if (vnode) {
+		status->parent.vid = vnode->fid.vid;
+		if (changed && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) {
+			_debug("vnode changed");
+			i_size_write(&vnode->vfs_inode, size);
+			vnode->vfs_inode.i_uid = status->owner;
+			vnode->vfs_inode.i_gid = status->group;
+			vnode->vfs_inode.i_version = vnode->fid.unique;
+			vnode->vfs_inode.i_nlink = status->nlink;
 
-	piov[0].iov_len = sizeof(param);
-	piov[0].iov_base = param;
-
-	/* send the parameters to the server */
-	ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS,
-				    0, &sent);
-	if (ret < 0)
-		goto abort;
-
-	/* wait for the reply to completely arrive */
-	for (;;) {
-		set_current_state(TASK_INTERRUPTIBLE);
-		if (call->app_call_state != RXRPC_CSTATE_CLNT_RCV_REPLY ||
-		    signal_pending(current))
-			break;
-		schedule();
-	}
-	set_current_state(TASK_RUNNING);
-
-	ret = -EINTR;
-	if (signal_pending(current))
-		goto abort;
-
-	switch (call->app_call_state) {
-	case RXRPC_CSTATE_ERROR:
-		ret = call->app_errno;
-		kdebug("Got Error: %d", ret);
-		goto out_unwait;
-
-	case RXRPC_CSTATE_CLNT_GOT_REPLY:
-		/* read the reply */
-		kdebug("Got Reply: qty=%d", call->app_ready_qty);
-
-		ret = -EBADMSG;
-		if (call->app_ready_qty <= 4)
-			goto abort;
-
-		ret = rxrpc_call_read_data(call, NULL, call->app_ready_qty, 0);
-		if (ret < 0)
-			goto abort;
-
-#if 0
-		/* unmarshall the reply */
-		bp = buffer;
-		for (loop = 0; loop < 65; loop++)
-			entry->name[loop] = ntohl(*bp++);
-		entry->name[64] = 0;
-
-		entry->type = ntohl(*bp++);
-		entry->num_servers = ntohl(*bp++);
-
-		for (loop = 0; loop < 8; loop++)
-			entry->servers[loop].addr.s_addr = *bp++;
-
-		for (loop = 0; loop < 8; loop++)
-			entry->servers[loop].partition = ntohl(*bp++);
-
-		for (loop = 0; loop < 8; loop++)
-			entry->servers[loop].flags = ntohl(*bp++);
-
-		for (loop = 0; loop < 3; loop++)
-			entry->volume_ids[loop] = ntohl(*bp++);
-
-		entry->clone_id = ntohl(*bp++);
-		entry->flags = ntohl(*bp);
-#endif
-
-		/* success */
-		ret = 0;
-		goto out_unwait;
-
-	default:
-		BUG();
-	}
-
- abort:
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	rxrpc_call_abort(call, ret);
-	schedule();
- out_unwait:
-	set_current_state(TASK_RUNNING);
-	remove_wait_queue(&call->waitq, &myself);
-	rxrpc_put_call(call);
- out_put_conn:
-	afs_server_release_fsconn(server, conn);
- out:
-	kleave("");
-	return ret;
-} /* end afs_rxfs_get_root_volume() */
-#endif
-
-/*****************************************************************************/
-/*
- * get information about a volume
- */
-#if 0
-int afs_rxfs_get_volume_info(struct afs_server *server,
-			     const char *name,
-			     struct afs_volume_info *vinfo)
-{
-	struct rxrpc_connection *conn;
-	struct rxrpc_call *call;
-	struct kvec piov[3];
-	size_t sent;
-	int ret;
-	u32 param[2], *bp, zero;
-
-	DECLARE_WAITQUEUE(myself, current);
-
-	_enter("%p,%s,%p", server, name, vinfo);
-
-	/* get hold of the fileserver connection */
-	ret = afs_server_get_fsconn(server, &conn);
-	if (ret < 0)
-		goto out;
-
-	/* create a call through that connection */
-	ret = rxrpc_create_call(conn, NULL, NULL, afs_rxfs_aemap, &call);
-	if (ret < 0) {
-		printk("kAFS: Unable to create call: %d\n", ret);
-		goto out_put_conn;
-	}
-	call->app_opcode = FSGETVOLUMEINFO;
-
-	/* we want to get event notifications from the call */
-	add_wait_queue(&call->waitq, &myself);
-
-	/* marshall the parameters */
-	piov[1].iov_len = strlen(name);
-	piov[1].iov_base = (char *) name;
-
-	zero = 0;
-	piov[2].iov_len = (4 - (piov[1].iov_len & 3)) & 3;
-	piov[2].iov_base = &zero;
-
-	param[0] = htonl(FSGETVOLUMEINFO);
-	param[1] = htonl(piov[1].iov_len);
-
-	piov[0].iov_len = sizeof(param);
-	piov[0].iov_base = param;
-
-	/* send the parameters to the server */
-	ret = rxrpc_call_write_data(call, 3, piov, RXRPC_LAST_PACKET, GFP_NOFS,
-				    0, &sent);
-	if (ret < 0)
-		goto abort;
-
-	/* wait for the reply to completely arrive */
-	bp = rxrpc_call_alloc_scratch(call, 64);
-
-	ret = rxrpc_call_read_data(call, bp, 64,
-				   RXRPC_CALL_READ_BLOCK |
-				   RXRPC_CALL_READ_ALL);
-	if (ret < 0) {
-		if (ret == -ECONNABORTED) {
-			ret = call->app_errno;
-			goto out_unwait;
+			mode = vnode->vfs_inode.i_mode;
+			mode &= ~S_IALLUGO;
+			mode |= status->mode;
+			barrier();
+			vnode->vfs_inode.i_mode = mode;
 		}
-		goto abort;
+
+		vnode->vfs_inode.i_ctime.tv_sec	= status->mtime_server;
+		vnode->vfs_inode.i_mtime	= vnode->vfs_inode.i_ctime;
+		vnode->vfs_inode.i_atime	= vnode->vfs_inode.i_ctime;
 	}
 
-	/* unmarshall the reply */
-	vinfo->vid = ntohl(*bp++);
-	vinfo->type = ntohl(*bp++);
+	if (status->data_version != data_version) {
+		status->data_version = data_version;
+		if (vnode && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) {
+			_debug("vnode modified %llx on {%x:%u}",
+			       (unsigned long long) data_version,
+			       vnode->fid.vid, vnode->fid.vnode);
+			set_bit(AFS_VNODE_MODIFIED, &vnode->flags);
+			set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags);
+		}
+	}
+}
 
-	vinfo->type_vids[0] = ntohl(*bp++);
-	vinfo->type_vids[1] = ntohl(*bp++);
-	vinfo->type_vids[2] = ntohl(*bp++);
-	vinfo->type_vids[3] = ntohl(*bp++);
-	vinfo->type_vids[4] = ntohl(*bp++);
+/*
+ * decode an AFSCallBack block
+ */
+static void xdr_decode_AFSCallBack(const __be32 **_bp, struct afs_vnode *vnode)
+{
+	const __be32 *bp = *_bp;
 
-	vinfo->nservers = ntohl(*bp++);
-	vinfo->servers[0].addr.s_addr = *bp++;
-	vinfo->servers[1].addr.s_addr = *bp++;
-	vinfo->servers[2].addr.s_addr = *bp++;
-	vinfo->servers[3].addr.s_addr = *bp++;
-	vinfo->servers[4].addr.s_addr = *bp++;
-	vinfo->servers[5].addr.s_addr = *bp++;
-	vinfo->servers[6].addr.s_addr = *bp++;
-	vinfo->servers[7].addr.s_addr = *bp++;
+	vnode->cb_version	= ntohl(*bp++);
+	vnode->cb_expiry	= ntohl(*bp++);
+	vnode->cb_type		= ntohl(*bp++);
+	vnode->cb_expires	= vnode->cb_expiry + get_seconds();
+	*_bp = bp;
+}
 
-	ret = -EBADMSG;
-	if (vinfo->nservers > 8)
-		goto abort;
+static void xdr_decode_AFSCallBack_raw(const __be32 **_bp,
+				       struct afs_callback *cb)
+{
+	const __be32 *bp = *_bp;
 
-	/* success */
-	ret = 0;
+	cb->version	= ntohl(*bp++);
+	cb->expiry	= ntohl(*bp++);
+	cb->type	= ntohl(*bp++);
+	*_bp = bp;
+}
 
- out_unwait:
-	set_current_state(TASK_RUNNING);
-	remove_wait_queue(&call->waitq, &myself);
-	rxrpc_put_call(call);
- out_put_conn:
-	afs_server_release_fsconn(server, conn);
- out:
-	_leave("");
-	return ret;
+/*
+ * decode an AFSVolSync block
+ */
+static void xdr_decode_AFSVolSync(const __be32 **_bp,
+				  struct afs_volsync *volsync)
+{
+	const __be32 *bp = *_bp;
 
- abort:
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	rxrpc_call_abort(call, ret);
-	schedule();
-	goto out_unwait;
+	volsync->creation = ntohl(*bp++);
+	bp++; /* spare2 */
+	bp++; /* spare3 */
+	bp++; /* spare4 */
+	bp++; /* spare5 */
+	bp++; /* spare6 */
+	*_bp = bp;
+}
 
-} /* end afs_rxfs_get_volume_info() */
-#endif
+/*
+ * deliver reply data to an FS.FetchStatus
+ */
+static int afs_deliver_fs_fetch_status(struct afs_call *call,
+				       struct sk_buff *skb, bool last)
+{
+	struct afs_vnode *vnode = call->reply;
+	const __be32 *bp;
 
-/*****************************************************************************/
+	_enter(",,%u", last);
+
+	afs_transfer_reply(call, skb);
+	if (!last)
+		return 0;
+
+	if (call->reply_size != call->reply_max)
+		return -EBADMSG;
+
+	/* unmarshall the reply once we've received all of it */
+	bp = call->buffer;
+	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode);
+	xdr_decode_AFSCallBack(&bp, vnode);
+	if (call->reply2)
+		xdr_decode_AFSVolSync(&bp, call->reply2);
+
+	_leave(" = 0 [done]");
+	return 0;
+}
+
+/*
+ * FS.FetchStatus operation type
+ */
+static const struct afs_call_type afs_RXFSFetchStatus = {
+	.name		= "FS.FetchStatus",
+	.deliver	= afs_deliver_fs_fetch_status,
+	.abort_to_error	= afs_abort_to_error,
+	.destructor	= afs_flat_call_destructor,
+};
+
 /*
  * fetch the status information for a file
  */
-int afs_rxfs_fetch_file_status(struct afs_server *server,
-			       struct afs_vnode *vnode,
-			       struct afs_volsync *volsync)
+int afs_fs_fetch_file_status(struct afs_server *server,
+			     struct key *key,
+			     struct afs_vnode *vnode,
+			     struct afs_volsync *volsync,
+			     const struct afs_wait_mode *wait_mode)
 {
-	struct afs_server_callslot callslot;
-	struct rxrpc_call *call;
-	struct kvec piov[1];
-	size_t sent;
-	int ret;
+	struct afs_call *call;
 	__be32 *bp;
 
-	DECLARE_WAITQUEUE(myself, current);
+	_enter(",%x,{%x:%d},,",
+	       key_serial(key), vnode->fid.vid, vnode->fid.vnode);
 
-	_enter("%p,{%u,%u,%u}",
-	       server, vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
+	call = afs_alloc_flat_call(&afs_RXFSFetchStatus, 16, (21 + 3 + 6) * 4);
+	if (!call)
+		return -ENOMEM;
 
-	/* get hold of the fileserver connection */
-	ret = afs_server_request_callslot(server, &callslot);
-	if (ret < 0)
-		goto out;
-
-	/* create a call through that connection */
-	ret = rxrpc_create_call(callslot.conn, NULL, NULL, afs_rxfs_aemap,
-				&call);
-	if (ret < 0) {
-		printk("kAFS: Unable to create call: %d\n", ret);
-		goto out_put_conn;
-	}
-	call->app_opcode = FSFETCHSTATUS;
-
-	/* we want to get event notifications from the call */
-	add_wait_queue(&call->waitq, &myself);
+	call->key = key;
+	call->reply = vnode;
+	call->reply2 = volsync;
+	call->service_id = FS_SERVICE;
+	call->port = htons(AFS_FS_PORT);
 
 	/* marshall the parameters */
-	bp = rxrpc_call_alloc_scratch(call, 16);
+	bp = call->request;
 	bp[0] = htonl(FSFETCHSTATUS);
 	bp[1] = htonl(vnode->fid.vid);
 	bp[2] = htonl(vnode->fid.vnode);
 	bp[3] = htonl(vnode->fid.unique);
 
-	piov[0].iov_len = 16;
-	piov[0].iov_base = bp;
+	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
+}
 
-	/* send the parameters to the server */
-	ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS,
-				    0, &sent);
-	if (ret < 0)
-		goto abort;
+/*
+ * deliver reply data to an FS.FetchData
+ */
+static int afs_deliver_fs_fetch_data(struct afs_call *call,
+				     struct sk_buff *skb, bool last)
+{
+	struct afs_vnode *vnode = call->reply;
+	const __be32 *bp;
+	struct page *page;
+	void *buffer;
+	int ret;
 
-	/* wait for the reply to completely arrive */
-	bp = rxrpc_call_alloc_scratch(call, 120);
+	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
 
-	ret = rxrpc_call_read_data(call, bp, 120,
-				   RXRPC_CALL_READ_BLOCK |
-				   RXRPC_CALL_READ_ALL);
-	if (ret < 0) {
-		if (ret == -ECONNABORTED) {
-			ret = call->app_errno;
-			goto out_unwait;
+	switch (call->unmarshall) {
+	case 0:
+		call->offset = 0;
+		call->unmarshall++;
+
+		/* extract the returned data length */
+	case 1:
+		_debug("extract data length");
+		ret = afs_extract_data(call, skb, last, &call->tmp, 4);
+		switch (ret) {
+		case 0:		break;
+		case -EAGAIN:	return 0;
+		default:	return ret;
 		}
-		goto abort;
+
+		call->count = ntohl(call->tmp);
+		_debug("DATA length: %u", call->count);
+		if (call->count > PAGE_SIZE)
+			return -EBADMSG;
+		call->offset = 0;
+		call->unmarshall++;
+
+		if (call->count < PAGE_SIZE) {
+			buffer = kmap_atomic(call->reply3, KM_USER0);
+			memset(buffer + PAGE_SIZE - call->count, 0,
+			       call->count);
+			kunmap_atomic(buffer, KM_USER0);
+		}
+
+		/* extract the returned data */
+	case 2:
+		_debug("extract data");
+		page = call->reply3;
+		buffer = kmap_atomic(page, KM_USER0);
+		ret = afs_extract_data(call, skb, last, buffer, call->count);
+		kunmap_atomic(buffer, KM_USER0);
+		switch (ret) {
+		case 0:		break;
+		case -EAGAIN:	return 0;
+		default:	return ret;
+		}
+
+		call->offset = 0;
+		call->unmarshall++;
+
+		/* extract the metadata */
+	case 3:
+		ret = afs_extract_data(call, skb, last, call->buffer,
+				       (21 + 3 + 6) * 4);
+		switch (ret) {
+		case 0:		break;
+		case -EAGAIN:	return 0;
+		default:	return ret;
+		}
+
+		bp = call->buffer;
+		xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode);
+		xdr_decode_AFSCallBack(&bp, vnode);
+		if (call->reply2)
+			xdr_decode_AFSVolSync(&bp, call->reply2);
+
+		call->offset = 0;
+		call->unmarshall++;
+
+	case 4:
+		_debug("trailer");
+		if (skb->len != 0)
+			return -EBADMSG;
+		break;
 	}
 
-	/* unmarshall the reply */
-	vnode->status.if_version	= ntohl(*bp++);
-	vnode->status.type		= ntohl(*bp++);
-	vnode->status.nlink		= ntohl(*bp++);
-	vnode->status.size		= ntohl(*bp++);
-	vnode->status.version		= ntohl(*bp++);
-	vnode->status.author		= ntohl(*bp++);
-	vnode->status.owner		= ntohl(*bp++);
-	vnode->status.caller_access	= ntohl(*bp++);
-	vnode->status.anon_access	= ntohl(*bp++);
-	vnode->status.mode		= ntohl(*bp++);
-	vnode->status.parent.vid	= vnode->fid.vid;
-	vnode->status.parent.vnode	= ntohl(*bp++);
-	vnode->status.parent.unique	= ntohl(*bp++);
-	bp++; /* seg size */
-	vnode->status.mtime_client	= ntohl(*bp++);
-	vnode->status.mtime_server	= ntohl(*bp++);
-	bp++; /* group */
-	bp++; /* sync counter */
-	vnode->status.version |= ((unsigned long long) ntohl(*bp++)) << 32;
-	bp++; /* spare2 */
-	bp++; /* spare3 */
-	bp++; /* spare4 */
+	if (!last)
+		return 0;
 
-	vnode->cb_version		= ntohl(*bp++);
-	vnode->cb_expiry		= ntohl(*bp++);
-	vnode->cb_type			= ntohl(*bp++);
+	_leave(" = 0 [done]");
+	return 0;
+}
 
-	if (volsync) {
-		volsync->creation	= ntohl(*bp++);
-		bp++; /* spare2 */
-		bp++; /* spare3 */
-		bp++; /* spare4 */
-		bp++; /* spare5 */
-		bp++; /* spare6 */
-	}
-
-	/* success */
-	ret = 0;
-
- out_unwait:
-	set_current_state(TASK_RUNNING);
-	remove_wait_queue(&call->waitq, &myself);
-	rxrpc_put_call(call);
- out_put_conn:
-	afs_server_release_callslot(server, &callslot);
- out:
-	_leave("");
-	return ret;
-
- abort:
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	rxrpc_call_abort(call, ret);
-	schedule();
-	goto out_unwait;
-} /* end afs_rxfs_fetch_file_status() */
-
-/*****************************************************************************/
 /*
- * fetch the contents of a file or directory
+ * FS.FetchData operation type
  */
-int afs_rxfs_fetch_file_data(struct afs_server *server,
-			     struct afs_vnode *vnode,
-			     struct afs_rxfs_fetch_descriptor *desc,
-			     struct afs_volsync *volsync)
+static const struct afs_call_type afs_RXFSFetchData = {
+	.name		= "FS.FetchData",
+	.deliver	= afs_deliver_fs_fetch_data,
+	.abort_to_error	= afs_abort_to_error,
+	.destructor	= afs_flat_call_destructor,
+};
+
+/*
+ * fetch data from a file
+ */
+int afs_fs_fetch_data(struct afs_server *server,
+		      struct key *key,
+		      struct afs_vnode *vnode,
+		      off_t offset, size_t length,
+		      struct page *buffer,
+		      const struct afs_wait_mode *wait_mode)
 {
-	struct afs_server_callslot callslot;
-	struct rxrpc_call *call;
-	struct kvec piov[1];
-	size_t sent;
-	int ret;
+	struct afs_call *call;
 	__be32 *bp;
 
-	DECLARE_WAITQUEUE(myself, current);
+	_enter("");
 
-	_enter("%p,{fid={%u,%u,%u},sz=%Zu,of=%lu}",
-	       server,
-	       desc->fid.vid,
-	       desc->fid.vnode,
-	       desc->fid.unique,
-	       desc->size,
-	       desc->offset);
+	call = afs_alloc_flat_call(&afs_RXFSFetchData, 24, (21 + 3 + 6) * 4);
+	if (!call)
+		return -ENOMEM;
 
-	/* get hold of the fileserver connection */
-	ret = afs_server_request_callslot(server, &callslot);
-	if (ret < 0)
-		goto out;
-
-	/* create a call through that connection */
-	ret = rxrpc_create_call(callslot.conn, NULL, NULL, afs_rxfs_aemap, &call);
-	if (ret < 0) {
-		printk("kAFS: Unable to create call: %d\n", ret);
-		goto out_put_conn;
-	}
-	call->app_opcode = FSFETCHDATA;
-
-	/* we want to get event notifications from the call */
-	add_wait_queue(&call->waitq, &myself);
+	call->key = key;
+	call->reply = vnode;
+	call->reply2 = NULL; /* volsync */
+	call->reply3 = buffer;
+	call->service_id = FS_SERVICE;
+	call->port = htons(AFS_FS_PORT);
 
 	/* marshall the parameters */
-	bp = rxrpc_call_alloc_scratch(call, 24);
+	bp = call->request;
 	bp[0] = htonl(FSFETCHDATA);
-	bp[1] = htonl(desc->fid.vid);
-	bp[2] = htonl(desc->fid.vnode);
-	bp[3] = htonl(desc->fid.unique);
-	bp[4] = htonl(desc->offset);
-	bp[5] = htonl(desc->size);
+	bp[1] = htonl(vnode->fid.vid);
+	bp[2] = htonl(vnode->fid.vnode);
+	bp[3] = htonl(vnode->fid.unique);
+	bp[4] = htonl(offset);
+	bp[5] = htonl(length);
 
-	piov[0].iov_len = 24;
-	piov[0].iov_base = bp;
+	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
+}
 
-	/* send the parameters to the server */
-	ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS,
-				    0, &sent);
-	if (ret < 0)
-		goto abort;
-
-	/* wait for the data count to arrive */
-	ret = rxrpc_call_read_data(call, bp, 4, RXRPC_CALL_READ_BLOCK);
-	if (ret < 0)
-		goto read_failed;
-
-	desc->actual = ntohl(bp[0]);
-	if (desc->actual != desc->size) {
-		ret = -EBADMSG;
-		goto abort;
-	}
-
-	/* call the app to read the actual data */
-	rxrpc_call_reset_scratch(call);
-
-	ret = rxrpc_call_read_data(call, desc->buffer, desc->actual,
-				   RXRPC_CALL_READ_BLOCK);
-	if (ret < 0)
-		goto read_failed;
-
-	/* wait for the rest of the reply to completely arrive */
-	rxrpc_call_reset_scratch(call);
-	bp = rxrpc_call_alloc_scratch(call, 120);
-
-	ret = rxrpc_call_read_data(call, bp, 120,
-				   RXRPC_CALL_READ_BLOCK |
-				   RXRPC_CALL_READ_ALL);
-	if (ret < 0)
-		goto read_failed;
-
-	/* unmarshall the reply */
-	vnode->status.if_version	= ntohl(*bp++);
-	vnode->status.type		= ntohl(*bp++);
-	vnode->status.nlink		= ntohl(*bp++);
-	vnode->status.size		= ntohl(*bp++);
-	vnode->status.version		= ntohl(*bp++);
-	vnode->status.author		= ntohl(*bp++);
-	vnode->status.owner		= ntohl(*bp++);
-	vnode->status.caller_access	= ntohl(*bp++);
-	vnode->status.anon_access	= ntohl(*bp++);
-	vnode->status.mode		= ntohl(*bp++);
-	vnode->status.parent.vid	= desc->fid.vid;
-	vnode->status.parent.vnode	= ntohl(*bp++);
-	vnode->status.parent.unique	= ntohl(*bp++);
-	bp++; /* seg size */
-	vnode->status.mtime_client	= ntohl(*bp++);
-	vnode->status.mtime_server	= ntohl(*bp++);
-	bp++; /* group */
-	bp++; /* sync counter */
-	vnode->status.version |= ((unsigned long long) ntohl(*bp++)) << 32;
-	bp++; /* spare2 */
-	bp++; /* spare3 */
-	bp++; /* spare4 */
-
-	vnode->cb_version		= ntohl(*bp++);
-	vnode->cb_expiry		= ntohl(*bp++);
-	vnode->cb_type			= ntohl(*bp++);
-
-	if (volsync) {
-		volsync->creation	= ntohl(*bp++);
-		bp++; /* spare2 */
-		bp++; /* spare3 */
-		bp++; /* spare4 */
-		bp++; /* spare5 */
-		bp++; /* spare6 */
-	}
-
-	/* success */
-	ret = 0;
-
- out_unwait:
-	set_current_state(TASK_RUNNING);
-	remove_wait_queue(&call->waitq,&myself);
-	rxrpc_put_call(call);
- out_put_conn:
-	afs_server_release_callslot(server, &callslot);
- out:
-	_leave(" = %d", ret);
-	return ret;
-
- read_failed:
-	if (ret == -ECONNABORTED) {
-		ret = call->app_errno;
-		goto out_unwait;
-	}
-
- abort:
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	rxrpc_call_abort(call, ret);
-	schedule();
-	goto out_unwait;
-
-} /* end afs_rxfs_fetch_file_data() */
-
-/*****************************************************************************/
 /*
- * ask the AFS fileserver to discard a callback request on a file
+ * deliver reply data to an FS.GiveUpCallBacks
  */
-int afs_rxfs_give_up_callback(struct afs_server *server,
-			      struct afs_vnode *vnode)
+static int afs_deliver_fs_give_up_callbacks(struct afs_call *call,
+					    struct sk_buff *skb, bool last)
 {
-	struct afs_server_callslot callslot;
-	struct rxrpc_call *call;
-	struct kvec piov[1];
-	size_t sent;
-	int ret;
-	__be32 *bp;
+	_enter(",{%u},%d", skb->len, last);
 
-	DECLARE_WAITQUEUE(myself, current);
+	if (skb->len > 0)
+		return -EBADMSG; /* shouldn't be any reply data */
+	return 0;
+}
 
-	_enter("%p,{%u,%u,%u}",
-	       server, vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
+/*
+ * FS.GiveUpCallBacks operation type
+ */
+static const struct afs_call_type afs_RXFSGiveUpCallBacks = {
+	.name		= "FS.GiveUpCallBacks",
+	.deliver	= afs_deliver_fs_give_up_callbacks,
+	.abort_to_error	= afs_abort_to_error,
+	.destructor	= afs_flat_call_destructor,
+};
 
-	/* get hold of the fileserver connection */
-	ret = afs_server_request_callslot(server, &callslot);
-	if (ret < 0)
-		goto out;
+/*
+ * give up a set of callbacks
+ * - the callbacks are held in the server->cb_break ring
+ */
+int afs_fs_give_up_callbacks(struct afs_server *server,
+			     const struct afs_wait_mode *wait_mode)
+{
+	struct afs_call *call;
+	size_t ncallbacks;
+	__be32 *bp, *tp;
+	int loop;
 
-	/* create a call through that connection */
-	ret = rxrpc_create_call(callslot.conn, NULL, NULL, afs_rxfs_aemap, &call);
-	if (ret < 0) {
-		printk("kAFS: Unable to create call: %d\n", ret);
-		goto out_put_conn;
-	}
-	call->app_opcode = FSGIVEUPCALLBACKS;
+	ncallbacks = CIRC_CNT(server->cb_break_head, server->cb_break_tail,
+			      ARRAY_SIZE(server->cb_break));
 
-	/* we want to get event notifications from the call */
-	add_wait_queue(&call->waitq, &myself);
+	_enter("{%zu},", ncallbacks);
+
+	if (ncallbacks == 0)
+		return 0;
+	if (ncallbacks > AFSCBMAX)
+		ncallbacks = AFSCBMAX;
+
+	_debug("break %zu callbacks", ncallbacks);
+
+	call = afs_alloc_flat_call(&afs_RXFSGiveUpCallBacks,
+				   12 + ncallbacks * 6 * 4, 0);
+	if (!call)
+		return -ENOMEM;
+
+	call->service_id = FS_SERVICE;
+	call->port = htons(AFS_FS_PORT);
 
 	/* marshall the parameters */
-	bp = rxrpc_call_alloc_scratch(call, (1 + 4 + 4) * 4);
-
-	piov[0].iov_len = (1 + 4 + 4) * 4;
-	piov[0].iov_base = bp;
-
+	bp = call->request;
+	tp = bp + 2 + ncallbacks * 3;
 	*bp++ = htonl(FSGIVEUPCALLBACKS);
-	*bp++ = htonl(1);
+	*bp++ = htonl(ncallbacks);
+	*tp++ = htonl(ncallbacks);
+
+	atomic_sub(ncallbacks, &server->cb_break_n);
+	for (loop = ncallbacks; loop > 0; loop--) {
+		struct afs_callback *cb =
+			&server->cb_break[server->cb_break_tail];
+
+		*bp++ = htonl(cb->fid.vid);
+		*bp++ = htonl(cb->fid.vnode);
+		*bp++ = htonl(cb->fid.unique);
+		*tp++ = htonl(cb->version);
+		*tp++ = htonl(cb->expiry);
+		*tp++ = htonl(cb->type);
+		smp_mb();
+		server->cb_break_tail =
+			(server->cb_break_tail + 1) &
+			(ARRAY_SIZE(server->cb_break) - 1);
+	}
+
+	ASSERT(ncallbacks > 0);
+	wake_up_nr(&server->cb_break_waitq, ncallbacks);
+
+	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
+}
+
+/*
+ * deliver reply data to an FS.CreateFile or an FS.MakeDir
+ */
+static int afs_deliver_fs_create_vnode(struct afs_call *call,
+				       struct sk_buff *skb, bool last)
+{
+	struct afs_vnode *vnode = call->reply;
+	const __be32 *bp;
+
+	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
+
+	afs_transfer_reply(call, skb);
+	if (!last)
+		return 0;
+
+	if (call->reply_size != call->reply_max)
+		return -EBADMSG;
+
+	/* unmarshall the reply once we've received all of it */
+	bp = call->buffer;
+	xdr_decode_AFSFid(&bp, call->reply2);
+	xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL);
+	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode);
+	xdr_decode_AFSCallBack_raw(&bp, call->reply4);
+	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
+
+	_leave(" = 0 [done]");
+	return 0;
+}
+
+/*
+ * FS.CreateFile and FS.MakeDir operation type
+ */
+static const struct afs_call_type afs_RXFSCreateXXXX = {
+	.name		= "FS.CreateXXXX",
+	.deliver	= afs_deliver_fs_create_vnode,
+	.abort_to_error	= afs_abort_to_error,
+	.destructor	= afs_flat_call_destructor,
+};
+
+/*
+ * create a file or make a directory
+ */
+int afs_fs_create(struct afs_server *server,
+		  struct key *key,
+		  struct afs_vnode *vnode,
+		  const char *name,
+		  umode_t mode,
+		  struct afs_fid *newfid,
+		  struct afs_file_status *newstatus,
+		  struct afs_callback *newcb,
+		  const struct afs_wait_mode *wait_mode)
+{
+	struct afs_call *call;
+	size_t namesz, reqsz, padsz;
+	__be32 *bp;
+
+	_enter("");
+
+	namesz = strlen(name);
+	padsz = (4 - (namesz & 3)) & 3;
+	reqsz = (5 * 4) + namesz + padsz + (6 * 4);
+
+	call = afs_alloc_flat_call(&afs_RXFSCreateXXXX, reqsz,
+				   (3 + 21 + 21 + 3 + 6) * 4);
+	if (!call)
+		return -ENOMEM;
+
+	call->key = key;
+	call->reply = vnode;
+	call->reply2 = newfid;
+	call->reply3 = newstatus;
+	call->reply4 = newcb;
+	call->service_id = FS_SERVICE;
+	call->port = htons(AFS_FS_PORT);
+
+	/* marshall the parameters */
+	bp = call->request;
+	*bp++ = htonl(S_ISDIR(mode) ? FSMAKEDIR : FSCREATEFILE);
 	*bp++ = htonl(vnode->fid.vid);
 	*bp++ = htonl(vnode->fid.vnode);
 	*bp++ = htonl(vnode->fid.unique);
-	*bp++ = htonl(1);
-	*bp++ = htonl(vnode->cb_version);
-	*bp++ = htonl(vnode->cb_expiry);
-	*bp++ = htonl(vnode->cb_type);
-
-	/* send the parameters to the server */
-	ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS,
-				    0, &sent);
-	if (ret < 0)
-		goto abort;
-
-	/* wait for the reply to completely arrive */
-	for (;;) {
-		set_current_state(TASK_INTERRUPTIBLE);
-		if (call->app_call_state != RXRPC_CSTATE_CLNT_RCV_REPLY ||
-		    signal_pending(current))
-			break;
-		schedule();
+	*bp++ = htonl(namesz);
+	memcpy(bp, name, namesz);
+	bp = (void *) bp + namesz;
+	if (padsz > 0) {
+		memset(bp, 0, padsz);
+		bp = (void *) bp + padsz;
 	}
-	set_current_state(TASK_RUNNING);
+	*bp++ = htonl(AFS_SET_MODE);
+	*bp++ = 0; /* mtime */
+	*bp++ = 0; /* owner */
+	*bp++ = 0; /* group */
+	*bp++ = htonl(mode & S_IALLUGO); /* unix mode */
+	*bp++ = 0; /* segment size */
 
-	ret = -EINTR;
-	if (signal_pending(current))
-		goto abort;
+	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
+}
 
-	switch (call->app_call_state) {
-	case RXRPC_CSTATE_ERROR:
-		ret = call->app_errno;
-		goto out_unwait;
-
-	case RXRPC_CSTATE_CLNT_GOT_REPLY:
-		ret = 0;
-		goto out_unwait;
-
-	default:
-		BUG();
-	}
-
- out_unwait:
-	set_current_state(TASK_RUNNING);
-	remove_wait_queue(&call->waitq, &myself);
-	rxrpc_put_call(call);
- out_put_conn:
-	afs_server_release_callslot(server, &callslot);
- out:
-	_leave("");
-	return ret;
-
- abort:
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	rxrpc_call_abort(call, ret);
-	schedule();
-	goto out_unwait;
-} /* end afs_rxfs_give_up_callback() */
-
-/*****************************************************************************/
 /*
- * look a filename up in a directory
- * - this operation doesn't seem to work correctly in OpenAFS server 1.2.2
+ * deliver reply data to an FS.RemoveFile or FS.RemoveDir
  */
-#if 0
-int afs_rxfs_lookup(struct afs_server *server,
-		    struct afs_vnode *dir,
-		    const char *filename,
-		    struct afs_vnode *vnode,
-		    struct afs_volsync *volsync)
+static int afs_deliver_fs_remove(struct afs_call *call,
+				 struct sk_buff *skb, bool last)
 {
-	struct rxrpc_connection *conn;
-	struct rxrpc_call *call;
-	struct kvec piov[3];
-	size_t sent;
-	int ret;
-	u32 *bp, zero;
+	struct afs_vnode *vnode = call->reply;
+	const __be32 *bp;
 
-	DECLARE_WAITQUEUE(myself, current);
+	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
 
-	kenter("%p,{%u,%u,%u},%s",
-	       server, fid->vid, fid->vnode, fid->unique, filename);
+	afs_transfer_reply(call, skb);
+	if (!last)
+		return 0;
 
-	/* get hold of the fileserver connection */
-	ret = afs_server_get_fsconn(server, &conn);
-	if (ret < 0)
-		goto out;
+	if (call->reply_size != call->reply_max)
+		return -EBADMSG;
 
-	/* create a call through that connection */
-	ret = rxrpc_create_call(conn, NULL, NULL, afs_rxfs_aemap, &call);
-	if (ret < 0) {
-		printk("kAFS: Unable to create call: %d\n", ret);
-		goto out_put_conn;
-	}
-	call->app_opcode = FSLOOKUP;
+	/* unmarshall the reply once we've received all of it */
+	bp = call->buffer;
+	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode);
+	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
 
-	/* we want to get event notifications from the call */
-	add_wait_queue(&call->waitq,&myself);
+	_leave(" = 0 [done]");
+	return 0;
+}
+
+/*
+ * FS.RemoveDir/FS.RemoveFile operation type
+ */
+static const struct afs_call_type afs_RXFSRemoveXXXX = {
+	.name		= "FS.RemoveXXXX",
+	.deliver	= afs_deliver_fs_remove,
+	.abort_to_error	= afs_abort_to_error,
+	.destructor	= afs_flat_call_destructor,
+};
+
+/*
+ * remove a file or directory
+ */
+int afs_fs_remove(struct afs_server *server,
+		  struct key *key,
+		  struct afs_vnode *vnode,
+		  const char *name,
+		  bool isdir,
+		  const struct afs_wait_mode *wait_mode)
+{
+	struct afs_call *call;
+	size_t namesz, reqsz, padsz;
+	__be32 *bp;
+
+	_enter("");
+
+	namesz = strlen(name);
+	padsz = (4 - (namesz & 3)) & 3;
+	reqsz = (5 * 4) + namesz + padsz;
+
+	call = afs_alloc_flat_call(&afs_RXFSRemoveXXXX, reqsz, (21 + 6) * 4);
+	if (!call)
+		return -ENOMEM;
+
+	call->key = key;
+	call->reply = vnode;
+	call->service_id = FS_SERVICE;
+	call->port = htons(AFS_FS_PORT);
 
 	/* marshall the parameters */
-	bp = rxrpc_call_alloc_scratch(call, 20);
-
-	zero = 0;
-
-	piov[0].iov_len = 20;
-	piov[0].iov_base = bp;
-	piov[1].iov_len = strlen(filename);
-	piov[1].iov_base = (char *) filename;
-	piov[2].iov_len = (4 - (piov[1].iov_len & 3)) & 3;
-	piov[2].iov_base = &zero;
-
-	*bp++ = htonl(FSLOOKUP);
-	*bp++ = htonl(dirfid->vid);
-	*bp++ = htonl(dirfid->vnode);
-	*bp++ = htonl(dirfid->unique);
-	*bp++ = htonl(piov[1].iov_len);
-
-	/* send the parameters to the server */
-	ret = rxrpc_call_write_data(call, 3, piov, RXRPC_LAST_PACKET, GFP_NOFS,
-				    0, &sent);
-	if (ret < 0)
-		goto abort;
-
-	/* wait for the reply to completely arrive */
-	bp = rxrpc_call_alloc_scratch(call, 220);
-
-	ret = rxrpc_call_read_data(call, bp, 220,
-				   RXRPC_CALL_READ_BLOCK |
-				   RXRPC_CALL_READ_ALL);
-	if (ret < 0) {
-		if (ret == -ECONNABORTED) {
-			ret = call->app_errno;
-			goto out_unwait;
-		}
-		goto abort;
+	bp = call->request;
+	*bp++ = htonl(isdir ? FSREMOVEDIR : FSREMOVEFILE);
+	*bp++ = htonl(vnode->fid.vid);
+	*bp++ = htonl(vnode->fid.vnode);
+	*bp++ = htonl(vnode->fid.unique);
+	*bp++ = htonl(namesz);
+	memcpy(bp, name, namesz);
+	bp = (void *) bp + namesz;
+	if (padsz > 0) {
+		memset(bp, 0, padsz);
+		bp = (void *) bp + padsz;
 	}
 
-	/* unmarshall the reply */
-	fid->vid		= ntohl(*bp++);
-	fid->vnode		= ntohl(*bp++);
-	fid->unique		= ntohl(*bp++);
+	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
+}
 
-	vnode->status.if_version	= ntohl(*bp++);
-	vnode->status.type		= ntohl(*bp++);
-	vnode->status.nlink		= ntohl(*bp++);
-	vnode->status.size		= ntohl(*bp++);
-	vnode->status.version		= ntohl(*bp++);
-	vnode->status.author		= ntohl(*bp++);
-	vnode->status.owner		= ntohl(*bp++);
-	vnode->status.caller_access	= ntohl(*bp++);
-	vnode->status.anon_access	= ntohl(*bp++);
-	vnode->status.mode		= ntohl(*bp++);
-	vnode->status.parent.vid	= dirfid->vid;
-	vnode->status.parent.vnode	= ntohl(*bp++);
-	vnode->status.parent.unique	= ntohl(*bp++);
-	bp++; /* seg size */
-	vnode->status.mtime_client	= ntohl(*bp++);
-	vnode->status.mtime_server	= ntohl(*bp++);
-	bp++; /* group */
-	bp++; /* sync counter */
-	vnode->status.version |= ((unsigned long long) ntohl(*bp++)) << 32;
-	bp++; /* spare2 */
-	bp++; /* spare3 */
-	bp++; /* spare4 */
+/*
+ * deliver reply data to an FS.Link
+ */
+static int afs_deliver_fs_link(struct afs_call *call,
+			       struct sk_buff *skb, bool last)
+{
+	struct afs_vnode *dvnode = call->reply, *vnode = call->reply2;
+	const __be32 *bp;
 
-	dir->status.if_version		= ntohl(*bp++);
-	dir->status.type		= ntohl(*bp++);
-	dir->status.nlink		= ntohl(*bp++);
-	dir->status.size		= ntohl(*bp++);
-	dir->status.version		= ntohl(*bp++);
-	dir->status.author		= ntohl(*bp++);
-	dir->status.owner		= ntohl(*bp++);
-	dir->status.caller_access	= ntohl(*bp++);
-	dir->status.anon_access		= ntohl(*bp++);
-	dir->status.mode		= ntohl(*bp++);
-	dir->status.parent.vid		= dirfid->vid;
-	dir->status.parent.vnode	= ntohl(*bp++);
-	dir->status.parent.unique	= ntohl(*bp++);
-	bp++; /* seg size */
-	dir->status.mtime_client	= ntohl(*bp++);
-	dir->status.mtime_server	= ntohl(*bp++);
-	bp++; /* group */
-	bp++; /* sync counter */
-	dir->status.version |= ((unsigned long long) ntohl(*bp++)) << 32;
-	bp++; /* spare2 */
-	bp++; /* spare3 */
-	bp++; /* spare4 */
+	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
 
-	callback->fid		= *fid;
-	callback->version	= ntohl(*bp++);
-	callback->expiry	= ntohl(*bp++);
-	callback->type		= ntohl(*bp++);
+	afs_transfer_reply(call, skb);
+	if (!last)
+		return 0;
 
-	if (volsync) {
-		volsync->creation	= ntohl(*bp++);
-		bp++; /* spare2 */
-		bp++; /* spare3 */
-		bp++; /* spare4 */
-		bp++; /* spare5 */
-		bp++; /* spare6 */
+	if (call->reply_size != call->reply_max)
+		return -EBADMSG;
+
+	/* unmarshall the reply once we've received all of it */
+	bp = call->buffer;
+	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode);
+	xdr_decode_AFSFetchStatus(&bp, &dvnode->status, dvnode);
+	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
+
+	_leave(" = 0 [done]");
+	return 0;
+}
+
+/*
+ * FS.Link operation type
+ */
+static const struct afs_call_type afs_RXFSLink = {
+	.name		= "FS.Link",
+	.deliver	= afs_deliver_fs_link,
+	.abort_to_error	= afs_abort_to_error,
+	.destructor	= afs_flat_call_destructor,
+};
+
+/*
+ * make a hard link
+ */
+int afs_fs_link(struct afs_server *server,
+		struct key *key,
+		struct afs_vnode *dvnode,
+		struct afs_vnode *vnode,
+		const char *name,
+		const struct afs_wait_mode *wait_mode)
+{
+	struct afs_call *call;
+	size_t namesz, reqsz, padsz;
+	__be32 *bp;
+
+	_enter("");
+
+	namesz = strlen(name);
+	padsz = (4 - (namesz & 3)) & 3;
+	reqsz = (5 * 4) + namesz + padsz + (3 * 4);
+
+	call = afs_alloc_flat_call(&afs_RXFSLink, reqsz, (21 + 21 + 6) * 4);
+	if (!call)
+		return -ENOMEM;
+
+	call->key = key;
+	call->reply = dvnode;
+	call->reply2 = vnode;
+	call->service_id = FS_SERVICE;
+	call->port = htons(AFS_FS_PORT);
+
+	/* marshall the parameters */
+	bp = call->request;
+	*bp++ = htonl(FSLINK);
+	*bp++ = htonl(dvnode->fid.vid);
+	*bp++ = htonl(dvnode->fid.vnode);
+	*bp++ = htonl(dvnode->fid.unique);
+	*bp++ = htonl(namesz);
+	memcpy(bp, name, namesz);
+	bp = (void *) bp + namesz;
+	if (padsz > 0) {
+		memset(bp, 0, padsz);
+		bp = (void *) bp + padsz;
+	}
+	*bp++ = htonl(vnode->fid.vid);
+	*bp++ = htonl(vnode->fid.vnode);
+	*bp++ = htonl(vnode->fid.unique);
+
+	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
+}
+
+/*
+ * deliver reply data to an FS.Symlink
+ */
+static int afs_deliver_fs_symlink(struct afs_call *call,
+				  struct sk_buff *skb, bool last)
+{
+	struct afs_vnode *vnode = call->reply;
+	const __be32 *bp;
+
+	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
+
+	afs_transfer_reply(call, skb);
+	if (!last)
+		return 0;
+
+	if (call->reply_size != call->reply_max)
+		return -EBADMSG;
+
+	/* unmarshall the reply once we've received all of it */
+	bp = call->buffer;
+	xdr_decode_AFSFid(&bp, call->reply2);
+	xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL);
+	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode);
+	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
+
+	_leave(" = 0 [done]");
+	return 0;
+}
+
+/*
+ * FS.Symlink operation type
+ */
+static const struct afs_call_type afs_RXFSSymlink = {
+	.name		= "FS.Symlink",
+	.deliver	= afs_deliver_fs_symlink,
+	.abort_to_error	= afs_abort_to_error,
+	.destructor	= afs_flat_call_destructor,
+};
+
+/*
+ * create a symbolic link
+ */
+int afs_fs_symlink(struct afs_server *server,
+		   struct key *key,
+		   struct afs_vnode *vnode,
+		   const char *name,
+		   const char *contents,
+		   struct afs_fid *newfid,
+		   struct afs_file_status *newstatus,
+		   const struct afs_wait_mode *wait_mode)
+{
+	struct afs_call *call;
+	size_t namesz, reqsz, padsz, c_namesz, c_padsz;
+	__be32 *bp;
+
+	_enter("");
+
+	namesz = strlen(name);
+	padsz = (4 - (namesz & 3)) & 3;
+
+	c_namesz = strlen(contents);
+	c_padsz = (4 - (c_namesz & 3)) & 3;
+
+	reqsz = (6 * 4) + namesz + padsz + c_namesz + c_padsz + (6 * 4);
+
+	call = afs_alloc_flat_call(&afs_RXFSSymlink, reqsz,
+				   (3 + 21 + 21 + 6) * 4);
+	if (!call)
+		return -ENOMEM;
+
+	call->key = key;
+	call->reply = vnode;
+	call->reply2 = newfid;
+	call->reply3 = newstatus;
+	call->service_id = FS_SERVICE;
+	call->port = htons(AFS_FS_PORT);
+
+	/* marshall the parameters */
+	bp = call->request;
+	*bp++ = htonl(FSSYMLINK);
+	*bp++ = htonl(vnode->fid.vid);
+	*bp++ = htonl(vnode->fid.vnode);
+	*bp++ = htonl(vnode->fid.unique);
+	*bp++ = htonl(namesz);
+	memcpy(bp, name, namesz);
+	bp = (void *) bp + namesz;
+	if (padsz > 0) {
+		memset(bp, 0, padsz);
+		bp = (void *) bp + padsz;
+	}
+	*bp++ = htonl(c_namesz);
+	memcpy(bp, contents, c_namesz);
+	bp = (void *) bp + c_namesz;
+	if (c_padsz > 0) {
+		memset(bp, 0, c_padsz);
+		bp = (void *) bp + c_padsz;
+	}
+	*bp++ = htonl(AFS_SET_MODE);
+	*bp++ = 0; /* mtime */
+	*bp++ = 0; /* owner */
+	*bp++ = 0; /* group */
+	*bp++ = htonl(S_IRWXUGO); /* unix mode */
+	*bp++ = 0; /* segment size */
+
+	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
+}
+
+/*
+ * deliver reply data to an FS.Rename
+ */
+static int afs_deliver_fs_rename(struct afs_call *call,
+				  struct sk_buff *skb, bool last)
+{
+	struct afs_vnode *orig_dvnode = call->reply, *new_dvnode = call->reply2;
+	const __be32 *bp;
+
+	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
+
+	afs_transfer_reply(call, skb);
+	if (!last)
+		return 0;
+
+	if (call->reply_size != call->reply_max)
+		return -EBADMSG;
+
+	/* unmarshall the reply once we've received all of it */
+	bp = call->buffer;
+	xdr_decode_AFSFetchStatus(&bp, &orig_dvnode->status, orig_dvnode);
+	if (new_dvnode != orig_dvnode)
+		xdr_decode_AFSFetchStatus(&bp, &new_dvnode->status, new_dvnode);
+	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
+
+	_leave(" = 0 [done]");
+	return 0;
+}
+
+/*
+ * FS.Rename operation type
+ */
+static const struct afs_call_type afs_RXFSRename = {
+	.name		= "FS.Rename",
+	.deliver	= afs_deliver_fs_rename,
+	.abort_to_error	= afs_abort_to_error,
+	.destructor	= afs_flat_call_destructor,
+};
+
+/*
+ * create a symbolic link
+ */
+int afs_fs_rename(struct afs_server *server,
+		  struct key *key,
+		  struct afs_vnode *orig_dvnode,
+		  const char *orig_name,
+		  struct afs_vnode *new_dvnode,
+		  const char *new_name,
+		  const struct afs_wait_mode *wait_mode)
+{
+	struct afs_call *call;
+	size_t reqsz, o_namesz, o_padsz, n_namesz, n_padsz;
+	__be32 *bp;
+
+	_enter("");
+
+	o_namesz = strlen(orig_name);
+	o_padsz = (4 - (o_namesz & 3)) & 3;
+
+	n_namesz = strlen(new_name);
+	n_padsz = (4 - (n_namesz & 3)) & 3;
+
+	reqsz = (4 * 4) +
+		4 + o_namesz + o_padsz +
+		(3 * 4) +
+		4 + n_namesz + n_padsz;
+
+	call = afs_alloc_flat_call(&afs_RXFSRename, reqsz, (21 + 21 + 6) * 4);
+	if (!call)
+		return -ENOMEM;
+
+	call->key = key;
+	call->reply = orig_dvnode;
+	call->reply2 = new_dvnode;
+	call->service_id = FS_SERVICE;
+	call->port = htons(AFS_FS_PORT);
+
+	/* marshall the parameters */
+	bp = call->request;
+	*bp++ = htonl(FSRENAME);
+	*bp++ = htonl(orig_dvnode->fid.vid);
+	*bp++ = htonl(orig_dvnode->fid.vnode);
+	*bp++ = htonl(orig_dvnode->fid.unique);
+	*bp++ = htonl(o_namesz);
+	memcpy(bp, orig_name, o_namesz);
+	bp = (void *) bp + o_namesz;
+	if (o_padsz > 0) {
+		memset(bp, 0, o_padsz);
+		bp = (void *) bp + o_padsz;
 	}
 
-	/* success */
-	ret = 0;
+	*bp++ = htonl(new_dvnode->fid.vid);
+	*bp++ = htonl(new_dvnode->fid.vnode);
+	*bp++ = htonl(new_dvnode->fid.unique);
+	*bp++ = htonl(n_namesz);
+	memcpy(bp, new_name, n_namesz);
+	bp = (void *) bp + n_namesz;
+	if (n_padsz > 0) {
+		memset(bp, 0, n_padsz);
+		bp = (void *) bp + n_padsz;
+	}
 
- out_unwait:
-	set_current_state(TASK_RUNNING);
-	remove_wait_queue(&call->waitq, &myself);
-	rxrpc_put_call(call);
- out_put_conn:
-	afs_server_release_fsconn(server, conn);
- out:
-	kleave("");
-	return ret;
-
- abort:
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	rxrpc_call_abort(call, ret);
-	schedule();
-	goto out_unwait;
-} /* end afs_rxfs_lookup() */
-#endif
+	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
+}
diff --git a/fs/afs/fsclient.h b/fs/afs/fsclient.h
deleted file mode 100644
index 8ba3e74..0000000
--- a/fs/afs/fsclient.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* fsclient.h: AFS File Server client stub declarations
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_AFS_FSCLIENT_H
-#define _LINUX_AFS_FSCLIENT_H
-
-#include "server.h"
-
-extern int afs_rxfs_get_volume_info(struct afs_server *server,
-				    const char *name,
-				    struct afs_volume_info *vinfo);
-
-extern int afs_rxfs_fetch_file_status(struct afs_server *server,
-				      struct afs_vnode *vnode,
-				      struct afs_volsync *volsync);
-
-struct afs_rxfs_fetch_descriptor {
-	struct afs_fid	fid;		/* file ID to fetch */
-	size_t		size;		/* total number of bytes to fetch */
-	off_t		offset;		/* offset in file to start from */
-	void		*buffer;	/* read buffer */
-	size_t		actual;		/* actual size sent back by server */
-};
-
-extern int afs_rxfs_fetch_file_data(struct afs_server *server,
-				    struct afs_vnode *vnode,
-				    struct afs_rxfs_fetch_descriptor *desc,
-				    struct afs_volsync *volsync);
-
-extern int afs_rxfs_give_up_callback(struct afs_server *server,
-				     struct afs_vnode *vnode);
-
-/* this doesn't appear to work in OpenAFS server */
-extern int afs_rxfs_lookup(struct afs_server *server,
-			   struct afs_vnode *dir,
-			   const char *filename,
-			   struct afs_vnode *vnode,
-			   struct afs_volsync *volsync);
-
-/* this is apparently mis-implemented in OpenAFS server */
-extern int afs_rxfs_get_root_volume(struct afs_server *server,
-				    char *buf,
-				    size_t *buflen);
-
-
-#endif /* _LINUX_AFS_FSCLIENT_H */
diff --git a/fs/afs/inode.c b/fs/afs/inode.c
index 9d9bca6..c184a4e 100644
--- a/fs/afs/inode.c
+++ b/fs/afs/inode.c
@@ -19,9 +19,6 @@
 #include <linux/slab.h>
 #include <linux/fs.h>
 #include <linux/pagemap.h>
-#include "volume.h"
-#include "vnode.h"
-#include "super.h"
 #include "internal.h"
 
 struct afs_iget_data {
@@ -29,26 +26,25 @@
 	struct afs_volume	*volume;	/* volume on which resides */
 };
 
-/*****************************************************************************/
 /*
  * map the AFS file status to the inode member variables
  */
-static int afs_inode_map_status(struct afs_vnode *vnode)
+static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
 {
 	struct inode *inode = AFS_VNODE_TO_I(vnode);
 
-	_debug("FS: ft=%d lk=%d sz=%Zu ver=%Lu mod=%hu",
+	_debug("FS: ft=%d lk=%d sz=%llu ver=%Lu mod=%hu",
 	       vnode->status.type,
 	       vnode->status.nlink,
-	       vnode->status.size,
-	       vnode->status.version,
+	       (unsigned long long) vnode->status.size,
+	       vnode->status.data_version,
 	       vnode->status.mode);
 
 	switch (vnode->status.type) {
 	case AFS_FTYPE_FILE:
 		inode->i_mode	= S_IFREG | vnode->status.mode;
 		inode->i_op	= &afs_file_inode_operations;
-		inode->i_fop	= &generic_ro_fops;
+		inode->i_fop	= &afs_file_operations;
 		break;
 	case AFS_FTYPE_DIR:
 		inode->i_mode	= S_IFDIR | vnode->status.mode;
@@ -77,9 +73,9 @@
 
 	/* check to see whether a symbolic link is really a mountpoint */
 	if (vnode->status.type == AFS_FTYPE_SYMLINK) {
-		afs_mntpt_check_symlink(vnode);
+		afs_mntpt_check_symlink(vnode, key);
 
-		if (vnode->flags & AFS_VNODE_MOUNTPOINT) {
+		if (test_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags)) {
 			inode->i_mode	= S_IFDIR | vnode->status.mode;
 			inode->i_op	= &afs_mntpt_inode_operations;
 			inode->i_fop	= &afs_mntpt_file_operations;
@@ -87,30 +83,8 @@
 	}
 
 	return 0;
-} /* end afs_inode_map_status() */
+}
 
-/*****************************************************************************/
-/*
- * attempt to fetch the status of an inode, coelescing multiple simultaneous
- * fetches
- */
-static int afs_inode_fetch_status(struct inode *inode)
-{
-	struct afs_vnode *vnode;
-	int ret;
-
-	vnode = AFS_FS_I(inode);
-
-	ret = afs_vnode_fetch_status(vnode);
-
-	if (ret == 0)
-		ret = afs_inode_map_status(vnode);
-
-	return ret;
-
-} /* end afs_inode_fetch_status() */
-
-/*****************************************************************************/
 /*
  * iget5() comparator
  */
@@ -120,9 +94,8 @@
 
 	return inode->i_ino == data->fid.vnode &&
 		inode->i_version == data->fid.unique;
-} /* end afs_iget5_test() */
+}
 
-/*****************************************************************************/
 /*
  * iget5() inode initialiser
  */
@@ -137,14 +110,14 @@
 	vnode->volume = data->volume;
 
 	return 0;
-} /* end afs_iget5_set() */
+}
 
-/*****************************************************************************/
 /*
  * inode retrieval
  */
-inline int afs_iget(struct super_block *sb, struct afs_fid *fid,
-		    struct inode **_inode)
+struct inode *afs_iget(struct super_block *sb, struct key *key,
+		       struct afs_fid *fid, struct afs_file_status *status,
+		       struct afs_callback *cb)
 {
 	struct afs_iget_data data = { .fid = *fid };
 	struct afs_super_info *as;
@@ -161,20 +134,18 @@
 			     &data);
 	if (!inode) {
 		_leave(" = -ENOMEM");
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 	}
 
+	_debug("GOT INODE %p { vl=%x vn=%x, u=%x }",
+	       inode, fid->vid, fid->vnode, fid->unique);
+
 	vnode = AFS_FS_I(inode);
 
 	/* deal with an existing inode */
 	if (!(inode->i_state & I_NEW)) {
-		ret = afs_vnode_fetch_status(vnode);
-		if (ret==0)
-			*_inode = inode;
-		else
-			iput(inode);
-		_leave(" = %d", ret);
-		return ret;
+		_leave(" = %p", inode);
+		return inode;
 	}
 
 #ifdef AFS_CACHING_SUPPORT
@@ -186,100 +157,185 @@
 			       &vnode->cache);
 #endif
 
-	/* okay... it's a new inode */
-	inode->i_flags |= S_NOATIME;
-	vnode->flags |= AFS_VNODE_CHANGED;
-	ret = afs_inode_fetch_status(inode);
-	if (ret<0)
+	if (!status) {
+		/* it's a remotely extant inode */
+		set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
+		ret = afs_vnode_fetch_status(vnode, NULL, key);
+		if (ret < 0)
+			goto bad_inode;
+	} else {
+		/* it's an inode we just created */
+		memcpy(&vnode->status, status, sizeof(vnode->status));
+
+		if (!cb) {
+			/* it's a symlink we just created (the fileserver
+			 * didn't give us a callback) */
+			vnode->cb_version = 0;
+			vnode->cb_expiry = 0;
+			vnode->cb_type = 0;
+			vnode->cb_expires = get_seconds();
+		} else {
+			vnode->cb_version = cb->version;
+			vnode->cb_expiry = cb->expiry;
+			vnode->cb_type = cb->type;
+			vnode->cb_expires = vnode->cb_expiry + get_seconds();
+		}
+	}
+
+	ret = afs_inode_map_status(vnode, key);
+	if (ret < 0)
 		goto bad_inode;
 
 	/* success */
+	clear_bit(AFS_VNODE_UNSET, &vnode->flags);
+	inode->i_flags |= S_NOATIME;
 	unlock_new_inode(inode);
-
-	*_inode = inode;
-	_leave(" = 0 [CB { v=%u x=%lu t=%u }]",
-	       vnode->cb_version,
-	       vnode->cb_timeout.timo_jif,
-	       vnode->cb_type);
-	return 0;
+	_leave(" = %p [CB { v=%u t=%u }]", inode, vnode->cb_version, vnode->cb_type);
+	return inode;
 
 	/* failure */
- bad_inode:
+bad_inode:
 	make_bad_inode(inode);
 	unlock_new_inode(inode);
 	iput(inode);
 
 	_leave(" = %d [bad]", ret);
-	return ret;
-} /* end afs_iget() */
+	return ERR_PTR(ret);
+}
 
-/*****************************************************************************/
+/*
+ * validate a vnode/inode
+ * - there are several things we need to check
+ *   - parent dir data changes (rm, rmdir, rename, mkdir, create, link,
+ *     symlink)
+ *   - parent dir metadata changed (security changes)
+ *   - dentry data changed (write, truncate)
+ *   - dentry metadata changed (security changes)
+ */
+int afs_validate(struct afs_vnode *vnode, struct key *key)
+{
+	int ret;
+
+	_enter("{v={%x:%u} fl=%lx},%x",
+	       vnode->fid.vid, vnode->fid.vnode, vnode->flags,
+	       key_serial(key));
+
+	if (vnode->cb_promised &&
+	    !test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) &&
+	    !test_bit(AFS_VNODE_MODIFIED, &vnode->flags) &&
+	    !test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) {
+		if (vnode->cb_expires < get_seconds() + 10) {
+			_debug("callback expired");
+			set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
+		} else {
+			goto valid;
+		}
+	}
+
+	if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
+		goto valid;
+
+	mutex_lock(&vnode->validate_lock);
+
+	/* if the promise has expired, we need to check the server again to get
+	 * a new promise - note that if the (parent) directory's metadata was
+	 * changed then the security may be different and we may no longer have
+	 * access */
+	if (!vnode->cb_promised ||
+	    test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) {
+		_debug("not promised");
+		ret = afs_vnode_fetch_status(vnode, NULL, key);
+		if (ret < 0)
+			goto error_unlock;
+		_debug("new promise [fl=%lx]", vnode->flags);
+	}
+
+	if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
+		_debug("file already deleted");
+		ret = -ESTALE;
+		goto error_unlock;
+	}
+
+	/* if the vnode's data version number changed then its contents are
+	 * different */
+	if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) {
+		_debug("zap data {%x:%d}", vnode->fid.vid, vnode->fid.vnode);
+		invalidate_remote_inode(&vnode->vfs_inode);
+	}
+
+	clear_bit(AFS_VNODE_MODIFIED, &vnode->flags);
+	mutex_unlock(&vnode->validate_lock);
+valid:
+	_leave(" = 0");
+	return 0;
+
+error_unlock:
+	mutex_unlock(&vnode->validate_lock);
+	_leave(" = %d", ret);
+	return ret;
+}
+
 /*
  * read the attributes of an inode
  */
 int afs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry,
 		      struct kstat *stat)
 {
-	struct afs_vnode *vnode;
 	struct inode *inode;
-	int ret;
 
 	inode = dentry->d_inode;
 
 	_enter("{ ino=%lu v=%lu }", inode->i_ino, inode->i_version);
 
-	vnode = AFS_FS_I(inode);
-
-	ret = afs_inode_fetch_status(inode);
-	if (ret == -ENOENT) {
-		_leave(" = %d [%d %p]",
-		       ret, atomic_read(&dentry->d_count), dentry->d_inode);
-		return ret;
-	}
-	else if (ret < 0) {
-		make_bad_inode(inode);
-		_leave(" = %d", ret);
-		return ret;
-	}
-
-	/* transfer attributes from the inode structure to the stat
-	 * structure */
 	generic_fillattr(inode, stat);
-
-	_leave(" = 0 CB { v=%u x=%u t=%u }",
-	       vnode->cb_version,
-	       vnode->cb_expiry,
-	       vnode->cb_type);
-
 	return 0;
-} /* end afs_inode_getattr() */
+}
 
-/*****************************************************************************/
 /*
  * clear an AFS inode
  */
 void afs_clear_inode(struct inode *inode)
 {
+	struct afs_permits *permits;
 	struct afs_vnode *vnode;
 
 	vnode = AFS_FS_I(inode);
 
-	_enter("ino=%lu { vn=%08x v=%u x=%u t=%u }",
-	       inode->i_ino,
+	_enter("{%x:%d.%d} v=%u x=%u t=%u }",
+	       vnode->fid.vid,
 	       vnode->fid.vnode,
+	       vnode->fid.unique,
 	       vnode->cb_version,
 	       vnode->cb_expiry,
-	       vnode->cb_type
-	       );
+	       vnode->cb_type);
 
-	BUG_ON(inode->i_ino != vnode->fid.vnode);
+	_debug("CLEAR INODE %p", inode);
 
-	afs_vnode_give_up_callback(vnode);
+	ASSERTCMP(inode->i_ino, ==, vnode->fid.vnode);
+
+	afs_give_up_callback(vnode);
+
+	if (vnode->server) {
+		spin_lock(&vnode->server->fs_lock);
+		rb_erase(&vnode->server_rb, &vnode->server->fs_vnodes);
+		spin_unlock(&vnode->server->fs_lock);
+		afs_put_server(vnode->server);
+		vnode->server = NULL;
+	}
+
+	ASSERT(!vnode->cb_promised);
 
 #ifdef AFS_CACHING_SUPPORT
 	cachefs_relinquish_cookie(vnode->cache, 0);
 	vnode->cache = NULL;
 #endif
 
+	mutex_lock(&vnode->permits_lock);
+	permits = vnode->permits;
+	rcu_assign_pointer(vnode->permits, NULL);
+	mutex_unlock(&vnode->permits_lock);
+	if (permits)
+		call_rcu(&permits->rcu, afs_zap_permits);
+
 	_leave("");
-} /* end afs_clear_inode() */
+}
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 5151d5d..34665f7 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -1,6 +1,6 @@
-/* internal.h: internal AFS stuff
+/* internal AFS stuff
  *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
@@ -9,47 +9,390 @@
  * 2 of the License, or (at your option) any later version.
  */
 
-#ifndef AFS_INTERNAL_H
-#define AFS_INTERNAL_H
-
 #include <linux/compiler.h>
 #include <linux/kernel.h>
 #include <linux/fs.h>
 #include <linux/pagemap.h>
+#include <linux/skbuff.h>
+#include <linux/rxrpc.h>
+#include <linux/key.h>
+#include "afs.h"
+#include "afs_vl.h"
+
+#define AFS_CELL_MAX_ADDRS 15
+
+struct afs_call;
+
+typedef enum {
+	AFS_VL_NEW,			/* new, uninitialised record */
+	AFS_VL_CREATING,		/* creating record */
+	AFS_VL_VALID,			/* record is pending */
+	AFS_VL_NO_VOLUME,		/* no such volume available */
+	AFS_VL_UPDATING,		/* update in progress */
+	AFS_VL_VOLUME_DELETED,		/* volume was deleted */
+	AFS_VL_UNCERTAIN,		/* uncertain state (update failed) */
+} __attribute__((packed)) afs_vlocation_state_t;
+
+struct afs_mount_params {
+	bool			rwpath;		/* T if the parent should be considered R/W */
+	bool			force;		/* T to force cell type */
+	afs_voltype_t		type;		/* type of volume requested */
+	int			volnamesz;	/* size of volume name */
+	const char		*volname;	/* name of volume to mount */
+	struct afs_cell		*cell;		/* cell in which to find volume */
+	struct afs_volume	*volume;	/* volume record */
+	struct key		*key;		/* key to use for secure mounting */
+};
 
 /*
- * debug tracing
+ * definition of how to wait for the completion of an operation
  */
-#define kenter(FMT, a...)	printk("==> %s("FMT")\n",__FUNCTION__ , ## a)
-#define kleave(FMT, a...)	printk("<== %s()"FMT"\n",__FUNCTION__ , ## a)
-#define kdebug(FMT, a...)	printk(FMT"\n" , ## a)
-#define kproto(FMT, a...)	printk("### "FMT"\n" , ## a)
-#define knet(FMT, a...)		printk(FMT"\n" , ## a)
+struct afs_wait_mode {
+	/* RxRPC received message notification */
+	void (*rx_wakeup)(struct afs_call *call);
 
-#ifdef __KDEBUG
-#define _enter(FMT, a...)	kenter(FMT , ## a)
-#define _leave(FMT, a...)	kleave(FMT , ## a)
-#define _debug(FMT, a...)	kdebug(FMT , ## a)
-#define _proto(FMT, a...)	kproto(FMT , ## a)
-#define _net(FMT, a...)		knet(FMT , ## a)
-#else
-#define _enter(FMT, a...)	do { } while(0)
-#define _leave(FMT, a...)	do { } while(0)
-#define _debug(FMT, a...)	do { } while(0)
-#define _proto(FMT, a...)	do { } while(0)
-#define _net(FMT, a...)		do { } while(0)
+	/* synchronous call waiter and call dispatched notification */
+	int (*wait)(struct afs_call *call);
+
+	/* asynchronous call completion */
+	void (*async_complete)(void *reply, int error);
+};
+
+extern const struct afs_wait_mode afs_sync_call;
+extern const struct afs_wait_mode afs_async_call;
+
+/*
+ * a record of an in-progress RxRPC call
+ */
+struct afs_call {
+	const struct afs_call_type *type;	/* type of call */
+	const struct afs_wait_mode *wait_mode;	/* completion wait mode */
+	wait_queue_head_t	waitq;		/* processes awaiting completion */
+	struct work_struct	async_work;	/* asynchronous work processor */
+	struct work_struct	work;		/* actual work processor */
+	struct sk_buff_head	rx_queue;	/* received packets */
+	struct rxrpc_call	*rxcall;	/* RxRPC call handle */
+	struct key		*key;		/* security for this call */
+	struct afs_server	*server;	/* server affected by incoming CM call */
+	void			*request;	/* request data (first part) */
+	void			*request2;	/* request data (second part) */
+	void			*buffer;	/* reply receive buffer */
+	void			*reply;		/* reply buffer (first part) */
+	void			*reply2;	/* reply buffer (second part) */
+	void			*reply3;	/* reply buffer (third part) */
+	void			*reply4;	/* reply buffer (fourth part) */
+	enum {					/* call state */
+		AFS_CALL_REQUESTING,	/* request is being sent for outgoing call */
+		AFS_CALL_AWAIT_REPLY,	/* awaiting reply to outgoing call */
+		AFS_CALL_AWAIT_OP_ID,	/* awaiting op ID on incoming call */
+		AFS_CALL_AWAIT_REQUEST,	/* awaiting request data on incoming call */
+		AFS_CALL_REPLYING,	/* replying to incoming call */
+		AFS_CALL_AWAIT_ACK,	/* awaiting final ACK of incoming call */
+		AFS_CALL_COMPLETE,	/* successfully completed */
+		AFS_CALL_BUSY,		/* server was busy */
+		AFS_CALL_ABORTED,	/* call was aborted */
+		AFS_CALL_ERROR,		/* call failed due to error */
+	}			state;
+	int			error;		/* error code */
+	unsigned		request_size;	/* size of request data */
+	unsigned		reply_max;	/* maximum size of reply */
+	unsigned		reply_size;	/* current size of reply */
+	unsigned short		offset;		/* offset into received data store */
+	unsigned char		unmarshall;	/* unmarshalling phase */
+	bool			incoming;	/* T if incoming call */
+	u16			service_id;	/* RxRPC service ID to call */
+	__be16			port;		/* target UDP port */
+	__be32			operation_ID;	/* operation ID for an incoming call */
+	u32			count;		/* count for use in unmarshalling */
+	__be32			tmp;		/* place to extract temporary data */
+};
+
+struct afs_call_type {
+	const char *name;
+
+	/* deliver request or reply data to an call
+	 * - returning an error will cause the call to be aborted
+	 */
+	int (*deliver)(struct afs_call *call, struct sk_buff *skb,
+		       bool last);
+
+	/* map an abort code to an error number */
+	int (*abort_to_error)(u32 abort_code);
+
+	/* clean up a call */
+	void (*destructor)(struct afs_call *call);
+};
+
+/*
+ * AFS superblock private data
+ * - there's one superblock per volume
+ */
+struct afs_super_info {
+	struct afs_volume	*volume;	/* volume record */
+	char			rwparent;	/* T if parent is R/W AFS volume */
+};
+
+static inline struct afs_super_info *AFS_FS_S(struct super_block *sb)
+{
+	return sb->s_fs_info;
+}
+
+extern struct file_system_type afs_fs_type;
+
+/*
+ * entry in the cached cell catalogue
+ */
+struct afs_cache_cell {
+	char		name[AFS_MAXCELLNAME];	/* cell name (padded with NULs) */
+	struct in_addr	vl_servers[15];		/* cached cell VL servers */
+};
+
+/*
+ * AFS cell record
+ */
+struct afs_cell {
+	atomic_t		usage;
+	struct list_head	link;		/* main cell list link */
+	struct key		*anonymous_key;	/* anonymous user key for this cell */
+	struct list_head	proc_link;	/* /proc cell list link */
+	struct proc_dir_entry	*proc_dir;	/* /proc dir for this cell */
+#ifdef AFS_CACHING_SUPPORT
+	struct cachefs_cookie	*cache;		/* caching cookie */
 #endif
 
-static inline void afs_discard_my_signals(void)
-{
-	while (signal_pending(current)) {
-		siginfo_t sinfo;
+	/* server record management */
+	rwlock_t		servers_lock;	/* active server list lock */
+	struct list_head	servers;	/* active server list */
 
-		spin_lock_irq(&current->sighand->siglock);
-		dequeue_signal(current,&current->blocked, &sinfo);
-		spin_unlock_irq(&current->sighand->siglock);
-	}
-}
+	/* volume location record management */
+	struct rw_semaphore	vl_sem;		/* volume management serialisation semaphore */
+	struct list_head	vl_list;	/* cell's active VL record list */
+	spinlock_t		vl_lock;	/* vl_list lock */
+	unsigned short		vl_naddrs;	/* number of VL servers in addr list */
+	unsigned short		vl_curr_svix;	/* current server index */
+	struct in_addr		vl_addrs[AFS_CELL_MAX_ADDRS];	/* cell VL server addresses */
+
+	char			name[0];	/* cell name - must go last */
+};
+
+/*
+ * entry in the cached volume location catalogue
+ */
+struct afs_cache_vlocation {
+	/* volume name (lowercase, padded with NULs) */
+	uint8_t			name[AFS_MAXVOLNAME + 1];
+
+	uint8_t			nservers;	/* number of entries used in servers[] */
+	uint8_t			vidmask;	/* voltype mask for vid[] */
+	uint8_t			srvtmask[8];	/* voltype masks for servers[] */
+#define AFS_VOL_VTM_RW	0x01 /* R/W version of the volume is available (on this server) */
+#define AFS_VOL_VTM_RO	0x02 /* R/O version of the volume is available (on this server) */
+#define AFS_VOL_VTM_BAK	0x04 /* backup version of the volume is available (on this server) */
+
+	afs_volid_t		vid[3];		/* volume IDs for R/W, R/O and Bak volumes */
+	struct in_addr		servers[8];	/* fileserver addresses */
+	time_t			rtime;		/* last retrieval time */
+};
+
+/*
+ * volume -> vnode hash table entry
+ */
+struct afs_cache_vhash {
+	afs_voltype_t		vtype;		/* which volume variation */
+	uint8_t			hash_bucket;	/* which hash bucket this represents */
+} __attribute__((packed));
+
+/*
+ * AFS volume location record
+ */
+struct afs_vlocation {
+	atomic_t		usage;
+	time_t			time_of_death;	/* time at which put reduced usage to 0 */
+	struct list_head	link;		/* link in cell volume location list */
+	struct list_head	grave;		/* link in master graveyard list */
+	struct list_head	update;		/* link in master update list */
+	struct afs_cell		*cell;		/* cell to which volume belongs */
+#ifdef AFS_CACHING_SUPPORT
+	struct cachefs_cookie	*cache;		/* caching cookie */
+#endif
+	struct afs_cache_vlocation vldb;	/* volume information DB record */
+	struct afs_volume	*vols[3];	/* volume access record pointer (index by type) */
+	wait_queue_head_t	waitq;		/* status change waitqueue */
+	time_t			update_at;	/* time at which record should be updated */
+	spinlock_t		lock;		/* access lock */
+	afs_vlocation_state_t	state;		/* volume location state */
+	unsigned short		upd_rej_cnt;	/* ENOMEDIUM count during update */
+	unsigned short		upd_busy_cnt;	/* EBUSY count during update */
+	bool			valid;		/* T if valid */
+};
+
+/*
+ * AFS fileserver record
+ */
+struct afs_server {
+	atomic_t		usage;
+	time_t			time_of_death;	/* time at which put reduced usage to 0 */
+	struct in_addr		addr;		/* server address */
+	struct afs_cell		*cell;		/* cell in which server resides */
+	struct list_head	link;		/* link in cell's server list */
+	struct list_head	grave;		/* link in master graveyard list */
+	struct rb_node		master_rb;	/* link in master by-addr tree */
+	struct rw_semaphore	sem;		/* access lock */
+
+	/* file service access */
+	struct rb_root		fs_vnodes;	/* vnodes backed by this server (ordered by FID) */
+	unsigned long		fs_act_jif;	/* time at which last activity occurred */
+	unsigned long		fs_dead_jif;	/* time at which no longer to be considered dead */
+	spinlock_t		fs_lock;	/* access lock */
+	int			fs_state;      	/* 0 or reason FS currently marked dead (-errno) */
+
+	/* callback promise management */
+	struct rb_root		cb_promises;	/* vnode expiration list (ordered earliest first) */
+	struct delayed_work	cb_updater;	/* callback updater */
+	struct delayed_work	cb_break_work;	/* collected break dispatcher */
+	wait_queue_head_t	cb_break_waitq;	/* space available in cb_break waitqueue */
+	spinlock_t		cb_lock;	/* access lock */
+	struct afs_callback	cb_break[64];	/* ring of callbacks awaiting breaking */
+	atomic_t		cb_break_n;	/* number of pending breaks */
+	u8			cb_break_head;	/* head of callback breaking ring */
+	u8			cb_break_tail;	/* tail of callback breaking ring */
+};
+
+/*
+ * AFS volume access record
+ */
+struct afs_volume {
+	atomic_t		usage;
+	struct afs_cell		*cell;		/* cell to which belongs (unrefd ptr) */
+	struct afs_vlocation	*vlocation;	/* volume location */
+#ifdef AFS_CACHING_SUPPORT
+	struct cachefs_cookie	*cache;		/* caching cookie */
+#endif
+	afs_volid_t		vid;		/* volume ID */
+	afs_voltype_t		type;		/* type of volume */
+	char			type_force;	/* force volume type (suppress R/O -> R/W) */
+	unsigned short		nservers;	/* number of server slots filled */
+	unsigned short		rjservers;	/* number of servers discarded due to -ENOMEDIUM */
+	struct afs_server	*servers[8];	/* servers on which volume resides (ordered) */
+	struct rw_semaphore	server_sem;	/* lock for accessing current server */
+};
+
+/*
+ * vnode catalogue entry
+ */
+struct afs_cache_vnode {
+	afs_vnodeid_t		vnode_id;	/* vnode ID */
+	unsigned		vnode_unique;	/* vnode ID uniquifier */
+	afs_dataversion_t	data_version;	/* data version */
+};
+
+/*
+ * AFS inode private data
+ */
+struct afs_vnode {
+	struct inode		vfs_inode;	/* the VFS's inode record */
+
+	struct afs_volume	*volume;	/* volume on which vnode resides */
+	struct afs_server	*server;	/* server currently supplying this file */
+	struct afs_fid		fid;		/* the file identifier for this inode */
+	struct afs_file_status	status;		/* AFS status info for this file */
+#ifdef AFS_CACHING_SUPPORT
+	struct cachefs_cookie	*cache;		/* caching cookie */
+#endif
+	struct afs_permits	*permits;	/* cache of permits so far obtained */
+	struct mutex		permits_lock;	/* lock for altering permits list */
+	struct mutex		validate_lock;	/* lock for validating this vnode */
+	wait_queue_head_t	update_waitq;	/* status fetch waitqueue */
+	int			update_cnt;	/* number of outstanding ops that will update the
+						 * status */
+	spinlock_t		lock;		/* waitqueue/flags lock */
+	unsigned long		flags;
+#define AFS_VNODE_CB_BROKEN	0		/* set if vnode's callback was broken */
+#define AFS_VNODE_UNSET		1		/* set if vnode attributes not yet set */
+#define AFS_VNODE_MODIFIED	2		/* set if vnode's data modified */
+#define AFS_VNODE_ZAP_DATA	3		/* set if vnode's data should be invalidated */
+#define AFS_VNODE_DELETED	4		/* set if vnode deleted on server */
+#define AFS_VNODE_MOUNTPOINT	5		/* set if vnode is a mountpoint symlink */
+
+	long			acl_order;	/* ACL check count (callback break count) */
+
+	/* outstanding callback notification on this file */
+	struct rb_node		server_rb;	/* link in server->fs_vnodes */
+	struct rb_node		cb_promise;	/* link in server->cb_promises */
+	struct work_struct	cb_broken_work;	/* work to be done on callback break */
+	time_t			cb_expires;	/* time at which callback expires */
+	time_t			cb_expires_at;	/* time used to order cb_promise */
+	unsigned		cb_version;	/* callback version */
+	unsigned		cb_expiry;	/* callback expiry time */
+	afs_callback_type_t	cb_type;	/* type of callback */
+	bool			cb_promised;	/* true if promise still holds */
+};
+
+/*
+ * cached security record for one user's attempt to access a vnode
+ */
+struct afs_permit {
+	struct key		*key;		/* RxRPC ticket holding a security context */
+	afs_access_t		access_mask;	/* access mask for this key */
+};
+
+/*
+ * cache of security records from attempts to access a vnode
+ */
+struct afs_permits {
+	struct rcu_head		rcu;		/* disposal procedure */
+	int			count;		/* number of records */
+	struct afs_permit	permits[0];	/* the permits so far examined */
+};
+
+/*
+ * record of one of a system's set of network interfaces
+ */
+struct afs_interface {
+	unsigned	index;		/* interface index */
+	struct in_addr	address;	/* IPv4 address bound to interface */
+	struct in_addr	netmask;	/* netmask applied to address */
+	unsigned	mtu;		/* MTU of interface */
+};
+
+/*
+ * UUID definition [internet draft]
+ * - the timestamp is a 60-bit value, split 32/16/12, and goes in 100ns
+ *   increments since midnight 15th October 1582
+ *   - add AFS_UUID_TO_UNIX_TIME to convert unix time in 100ns units to UUID
+ *     time
+ * - the clock sequence is a 14-bit counter to avoid duplicate times
+ */
+struct afs_uuid {
+	u32		time_low;			/* low part of timestamp */
+	u16		time_mid;			/* mid part of timestamp */
+	u16		time_hi_and_version;		/* high part of timestamp and version  */
+#define AFS_UUID_TO_UNIX_TIME	0x01b21dd213814000ULL
+#define AFS_UUID_TIMEHI_MASK	0x0fff
+#define AFS_UUID_VERSION_TIME	0x1000	/* time-based UUID */
+#define AFS_UUID_VERSION_NAME	0x3000	/* name-based UUID */
+#define AFS_UUID_VERSION_RANDOM	0x4000	/* (pseudo-)random generated UUID */
+	u8		clock_seq_hi_and_reserved;	/* clock seq hi and variant */
+#define AFS_UUID_CLOCKHI_MASK	0x3f
+#define AFS_UUID_VARIANT_STD	0x80
+	u8		clock_seq_low;			/* clock seq low */
+	u8		node[6];			/* spatially unique node ID (MAC addr) */
+};
+
+/*****************************************************************************/
+/*
+ * callback.c
+ */
+extern void afs_init_callback_state(struct afs_server *);
+extern void afs_broken_callback_work(struct work_struct *);
+extern void afs_break_callbacks(struct afs_server *, size_t,
+				struct afs_callback[]);
+extern void afs_discard_callback_on_delete(struct afs_vnode *);
+extern void afs_give_up_callback(struct afs_vnode *);
+extern void afs_dispatch_give_up_callbacks(struct work_struct *);
+extern void afs_flush_callback_breaks(struct afs_server *);
+extern int __init afs_callback_update_init(void);
+extern void __exit afs_callback_update_kill(void);
 
 /*
  * cell.c
@@ -60,57 +403,156 @@
 extern struct cachefs_index_def afs_cache_cell_index_def;
 #endif
 
+#define afs_get_cell(C) do { atomic_inc(&(C)->usage); } while(0)
+extern int afs_cell_init(char *);
+extern struct afs_cell *afs_cell_create(const char *, char *);
+extern struct afs_cell *afs_cell_lookup(const char *, unsigned);
+extern struct afs_cell *afs_grab_cell(struct afs_cell *);
+extern void afs_put_cell(struct afs_cell *);
+extern void afs_cell_purge(void);
+
+/*
+ * cmservice.c
+ */
+extern bool afs_cm_incoming_call(struct afs_call *);
+
 /*
  * dir.c
  */
 extern const struct inode_operations afs_dir_inode_operations;
 extern const struct file_operations afs_dir_file_operations;
 
+extern int afs_permission(struct inode *, int, struct nameidata *);
+
 /*
  * file.c
  */
 extern const struct address_space_operations afs_fs_aops;
 extern const struct inode_operations afs_file_inode_operations;
+extern const struct file_operations afs_file_operations;
+
+extern int afs_open(struct inode *, struct file *);
+extern int afs_release(struct inode *, struct file *);
 
 #ifdef AFS_CACHING_SUPPORT
-extern int afs_cache_get_page_cookie(struct page *page,
-				     struct cachefs_page **_page_cookie);
+extern int afs_cache_get_page_cookie(struct page *, struct cachefs_page **);
 #endif
 
 /*
+ * fsclient.c
+ */
+extern int afs_fs_fetch_file_status(struct afs_server *, struct key *,
+				    struct afs_vnode *, struct afs_volsync *,
+				    const struct afs_wait_mode *);
+extern int afs_fs_give_up_callbacks(struct afs_server *,
+				    const struct afs_wait_mode *);
+extern int afs_fs_fetch_data(struct afs_server *, struct key *,
+			     struct afs_vnode *, off_t, size_t, struct page *,
+			     const struct afs_wait_mode *);
+extern int afs_fs_create(struct afs_server *, struct key *,
+			 struct afs_vnode *, const char *, umode_t,
+			 struct afs_fid *, struct afs_file_status *,
+			 struct afs_callback *,
+			 const struct afs_wait_mode *);
+extern int afs_fs_remove(struct afs_server *, struct key *,
+			 struct afs_vnode *, const char *, bool,
+			 const struct afs_wait_mode *);
+extern int afs_fs_link(struct afs_server *, struct key *, struct afs_vnode *,
+		       struct afs_vnode *, const char *,
+		       const struct afs_wait_mode *);
+extern int afs_fs_symlink(struct afs_server *, struct key *,
+			  struct afs_vnode *, const char *, const char *,
+			  struct afs_fid *, struct afs_file_status *,
+			  const struct afs_wait_mode *);
+extern int afs_fs_rename(struct afs_server *, struct key *,
+			 struct afs_vnode *, const char *,
+			 struct afs_vnode *, const char *,
+			 const struct afs_wait_mode *);
+
+/*
  * inode.c
  */
-extern int afs_iget(struct super_block *sb, struct afs_fid *fid,
-		    struct inode **_inode);
-extern int afs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry,
-			     struct kstat *stat);
-extern void afs_clear_inode(struct inode *inode);
-
-/*
- * key_afs.c
- */
-#ifdef CONFIG_KEYS
-extern int afs_key_register(void);
-extern void afs_key_unregister(void);
-#endif
+extern struct inode *afs_iget(struct super_block *, struct key *,
+			      struct afs_fid *, struct afs_file_status *,
+			      struct afs_callback *);
+extern int afs_validate(struct afs_vnode *, struct key *);
+extern int afs_inode_getattr(struct vfsmount *, struct dentry *,
+			     struct kstat *);
+extern void afs_zap_permits(struct rcu_head *);
+extern void afs_clear_inode(struct inode *);
 
 /*
  * main.c
  */
+extern struct afs_uuid afs_uuid;
 #ifdef AFS_CACHING_SUPPORT
 extern struct cachefs_netfs afs_cache_netfs;
 #endif
 
 /*
+ * misc.c
+ */
+extern int afs_abort_to_error(u32);
+
+/*
  * mntpt.c
  */
 extern const struct inode_operations afs_mntpt_inode_operations;
 extern const struct file_operations afs_mntpt_file_operations;
-extern struct afs_timer afs_mntpt_expiry_timer;
-extern struct afs_timer_ops afs_mntpt_expiry_timer_ops;
 extern unsigned long afs_mntpt_expiry_timeout;
 
-extern int afs_mntpt_check_symlink(struct afs_vnode *vnode);
+extern int afs_mntpt_check_symlink(struct afs_vnode *, struct key *);
+extern void afs_mntpt_kill_timer(void);
+extern void afs_umount_begin(struct vfsmount *, int);
+
+/*
+ * proc.c
+ */
+extern int afs_proc_init(void);
+extern void afs_proc_cleanup(void);
+extern int afs_proc_cell_setup(struct afs_cell *);
+extern void afs_proc_cell_remove(struct afs_cell *);
+
+/*
+ * rxrpc.c
+ */
+extern int afs_open_socket(void);
+extern void afs_close_socket(void);
+extern int afs_make_call(struct in_addr *, struct afs_call *, gfp_t,
+			 const struct afs_wait_mode *);
+extern struct afs_call *afs_alloc_flat_call(const struct afs_call_type *,
+					    size_t, size_t);
+extern void afs_flat_call_destructor(struct afs_call *);
+extern void afs_transfer_reply(struct afs_call *, struct sk_buff *);
+extern void afs_send_empty_reply(struct afs_call *);
+extern void afs_send_simple_reply(struct afs_call *, const void *, size_t);
+extern int afs_extract_data(struct afs_call *, struct sk_buff *, bool, void *,
+			    size_t);
+
+/*
+ * security.c
+ */
+extern void afs_clear_permits(struct afs_vnode *);
+extern void afs_cache_permit(struct afs_vnode *, struct key *, long);
+extern struct key *afs_request_key(struct afs_cell *);
+extern int afs_permission(struct inode *, int, struct nameidata *);
+
+/*
+ * server.c
+ */
+extern spinlock_t afs_server_peer_lock;
+
+#define afs_get_server(S)					\
+do {								\
+	_debug("GET SERVER %d", atomic_read(&(S)->usage));	\
+	atomic_inc(&(S)->usage);				\
+} while(0)
+
+extern struct afs_server *afs_lookup_server(struct afs_cell *,
+					    const struct in_addr *);
+extern struct afs_server *afs_find_server(const struct in_addr *);
+extern void afs_put_server(struct afs_server *);
+extern void __exit afs_purge_servers(void);
 
 /*
  * super.c
@@ -118,22 +560,211 @@
 extern int afs_fs_init(void);
 extern void afs_fs_exit(void);
 
-#define AFS_CB_HASH_COUNT (PAGE_SIZE / sizeof(struct list_head))
-
-extern struct list_head afs_cb_hash_tbl[];
-extern spinlock_t afs_cb_hash_lock;
-
-#define afs_cb_hash(SRV,FID) \
-	afs_cb_hash_tbl[((unsigned long)(SRV) + \
-			(FID)->vid + (FID)->vnode + (FID)->unique) % \
-			AFS_CB_HASH_COUNT]
+/*
+ * use-rtnetlink.c
+ */
+extern int afs_get_ipv4_interfaces(struct afs_interface *, size_t, bool);
+extern int afs_get_MAC_address(u8 [6]);
 
 /*
- * proc.c
+ * vlclient.c
  */
-extern int afs_proc_init(void);
-extern void afs_proc_cleanup(void);
-extern int afs_proc_cell_setup(struct afs_cell *cell);
-extern void afs_proc_cell_remove(struct afs_cell *cell);
+#ifdef AFS_CACHING_SUPPORT
+extern struct cachefs_index_def afs_vlocation_cache_index_def;
+#endif
 
-#endif /* AFS_INTERNAL_H */
+extern int afs_vl_get_entry_by_name(struct in_addr *, struct key *,
+				    const char *, struct afs_cache_vlocation *,
+				    const struct afs_wait_mode *);
+extern int afs_vl_get_entry_by_id(struct in_addr *, struct key *,
+				  afs_volid_t, afs_voltype_t,
+				  struct afs_cache_vlocation *,
+				  const struct afs_wait_mode *);
+
+/*
+ * vlocation.c
+ */
+#define afs_get_vlocation(V) do { atomic_inc(&(V)->usage); } while(0)
+
+extern int __init afs_vlocation_update_init(void);
+extern struct afs_vlocation *afs_vlocation_lookup(struct afs_cell *,
+						  struct key *,
+						  const char *, size_t);
+extern void afs_put_vlocation(struct afs_vlocation *);
+extern void __exit afs_vlocation_purge(void);
+
+/*
+ * vnode.c
+ */
+#ifdef AFS_CACHING_SUPPORT
+extern struct cachefs_index_def afs_vnode_cache_index_def;
+#endif
+
+extern struct afs_timer_ops afs_vnode_cb_timed_out_ops;
+
+static inline struct afs_vnode *AFS_FS_I(struct inode *inode)
+{
+	return container_of(inode, struct afs_vnode, vfs_inode);
+}
+
+static inline struct inode *AFS_VNODE_TO_I(struct afs_vnode *vnode)
+{
+	return &vnode->vfs_inode;
+}
+
+extern void afs_vnode_finalise_status_update(struct afs_vnode *,
+					     struct afs_server *);
+extern int afs_vnode_fetch_status(struct afs_vnode *, struct afs_vnode *,
+				  struct key *);
+extern int afs_vnode_fetch_data(struct afs_vnode *, struct key *,
+				off_t, size_t, struct page *);
+extern int afs_vnode_create(struct afs_vnode *, struct key *, const char *,
+			    umode_t, struct afs_fid *, struct afs_file_status *,
+			    struct afs_callback *, struct afs_server **);
+extern int afs_vnode_remove(struct afs_vnode *, struct key *, const char *,
+			    bool);
+extern int afs_vnode_link(struct afs_vnode *, struct afs_vnode *, struct key *,
+			  const char *);
+extern int afs_vnode_symlink(struct afs_vnode *, struct key *, const char *,
+			     const char *, struct afs_fid *,
+			     struct afs_file_status *, struct afs_server **);
+extern int afs_vnode_rename(struct afs_vnode *, struct afs_vnode *,
+			    struct key *, const char *, const char *);
+
+/*
+ * volume.c
+ */
+#ifdef AFS_CACHING_SUPPORT
+extern struct cachefs_index_def afs_volume_cache_index_def;
+#endif
+
+#define afs_get_volume(V) do { atomic_inc(&(V)->usage); } while(0)
+
+extern void afs_put_volume(struct afs_volume *);
+extern struct afs_volume *afs_volume_lookup(struct afs_mount_params *);
+extern struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *);
+extern int afs_volume_release_fileserver(struct afs_vnode *,
+					 struct afs_server *, int);
+
+/*****************************************************************************/
+/*
+ * debug tracing
+ */
+extern unsigned afs_debug;
+
+#define dbgprintk(FMT,...) \
+	printk("[%x%-6.6s] "FMT"\n", smp_processor_id(), current->comm ,##__VA_ARGS__)
+
+/* make sure we maintain the format strings, even when debugging is disabled */
+static inline __attribute__((format(printf,1,2)))
+void _dbprintk(const char *fmt, ...)
+{
+}
+
+#define kenter(FMT,...)	dbgprintk("==> %s("FMT")",__FUNCTION__ ,##__VA_ARGS__)
+#define kleave(FMT,...)	dbgprintk("<== %s()"FMT"",__FUNCTION__ ,##__VA_ARGS__)
+#define kdebug(FMT,...)	dbgprintk("    "FMT ,##__VA_ARGS__)
+
+
+#if defined(__KDEBUG)
+#define _enter(FMT,...)	kenter(FMT,##__VA_ARGS__)
+#define _leave(FMT,...)	kleave(FMT,##__VA_ARGS__)
+#define _debug(FMT,...)	kdebug(FMT,##__VA_ARGS__)
+
+#elif defined(CONFIG_AFS_DEBUG)
+#define AFS_DEBUG_KENTER	0x01
+#define AFS_DEBUG_KLEAVE	0x02
+#define AFS_DEBUG_KDEBUG	0x04
+
+#define _enter(FMT,...)					\
+do {							\
+	if (unlikely(afs_debug & AFS_DEBUG_KENTER))	\
+		kenter(FMT,##__VA_ARGS__);		\
+} while (0)
+
+#define _leave(FMT,...)					\
+do {							\
+	if (unlikely(afs_debug & AFS_DEBUG_KLEAVE))	\
+		kleave(FMT,##__VA_ARGS__);		\
+} while (0)
+
+#define _debug(FMT,...)					\
+do {							\
+	if (unlikely(afs_debug & AFS_DEBUG_KDEBUG))	\
+		kdebug(FMT,##__VA_ARGS__);		\
+} while (0)
+
+#else
+#define _enter(FMT,...)	_dbprintk("==> %s("FMT")",__FUNCTION__ ,##__VA_ARGS__)
+#define _leave(FMT,...)	_dbprintk("<== %s()"FMT"",__FUNCTION__ ,##__VA_ARGS__)
+#define _debug(FMT,...)	_dbprintk("    "FMT ,##__VA_ARGS__)
+#endif
+
+/*
+ * debug assertion checking
+ */
+#if 1 // defined(__KDEBUGALL)
+
+#define ASSERT(X)						\
+do {								\
+	if (unlikely(!(X))) {					\
+		printk(KERN_ERR "\n");				\
+		printk(KERN_ERR "AFS: Assertion failed\n");	\
+		BUG();						\
+	}							\
+} while(0)
+
+#define ASSERTCMP(X, OP, Y)						\
+do {									\
+	if (unlikely(!((X) OP (Y)))) {					\
+		printk(KERN_ERR "\n");					\
+		printk(KERN_ERR "AFS: Assertion failed\n");		\
+		printk(KERN_ERR "%lu " #OP " %lu is false\n",		\
+		       (unsigned long)(X), (unsigned long)(Y));		\
+		printk(KERN_ERR "0x%lx " #OP " 0x%lx is false\n",	\
+		       (unsigned long)(X), (unsigned long)(Y));		\
+		BUG();							\
+	}								\
+} while(0)
+
+#define ASSERTIF(C, X)						\
+do {								\
+	if (unlikely((C) && !(X))) {				\
+		printk(KERN_ERR "\n");				\
+		printk(KERN_ERR "AFS: Assertion failed\n");	\
+		BUG();						\
+	}							\
+} while(0)
+
+#define ASSERTIFCMP(C, X, OP, Y)					\
+do {									\
+	if (unlikely((C) && !((X) OP (Y)))) {				\
+		printk(KERN_ERR "\n");					\
+		printk(KERN_ERR "AFS: Assertion failed\n");		\
+		printk(KERN_ERR "%lu " #OP " %lu is false\n",		\
+		       (unsigned long)(X), (unsigned long)(Y));		\
+		printk(KERN_ERR "0x%lx " #OP " 0x%lx is false\n",	\
+		       (unsigned long)(X), (unsigned long)(Y));		\
+		BUG();							\
+	}								\
+} while(0)
+
+#else
+
+#define ASSERT(X)				\
+do {						\
+} while(0)
+
+#define ASSERTCMP(X, OP, Y)			\
+do {						\
+} while(0)
+
+#define ASSERTIF(C, X)				\
+do {						\
+} while(0)
+
+#define ASSERTIFCMP(C, X, OP, Y)		\
+do {						\
+} while(0)
+
+#endif /* __KDEBUGALL */
diff --git a/fs/afs/kafsasyncd.c b/fs/afs/kafsasyncd.c
deleted file mode 100644
index 615df24..0000000
--- a/fs/afs/kafsasyncd.c
+++ /dev/null
@@ -1,255 +0,0 @@
-/* kafsasyncd.c: AFS asynchronous operation daemon
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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 AFS async daemon is used to the following:
- * - probe "dead" servers to see whether they've come back to life yet.
- * - probe "live" servers that we haven't talked to for a while to see if they are better
- *   candidates for serving than what we're currently using
- * - poll volume location servers to keep up to date volume location lists
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/completion.h>
-#include <linux/freezer.h>
-#include "cell.h"
-#include "server.h"
-#include "volume.h"
-#include "kafsasyncd.h"
-#include "kafstimod.h"
-#include <rxrpc/call.h>
-#include <asm/errno.h>
-#include "internal.h"
-
-static DECLARE_COMPLETION(kafsasyncd_alive);
-static DECLARE_COMPLETION(kafsasyncd_dead);
-static DECLARE_WAIT_QUEUE_HEAD(kafsasyncd_sleepq);
-static struct task_struct *kafsasyncd_task;
-static int kafsasyncd_die;
-
-static int kafsasyncd(void *arg);
-
-static LIST_HEAD(kafsasyncd_async_attnq);
-static LIST_HEAD(kafsasyncd_async_busyq);
-static DEFINE_SPINLOCK(kafsasyncd_async_lock);
-
-static void kafsasyncd_null_call_attn_func(struct rxrpc_call *call)
-{
-}
-
-static void kafsasyncd_null_call_error_func(struct rxrpc_call *call)
-{
-}
-
-/*****************************************************************************/
-/*
- * start the async daemon
- */
-int afs_kafsasyncd_start(void)
-{
-	int ret;
-
-	ret = kernel_thread(kafsasyncd, NULL, 0);
-	if (ret < 0)
-		return ret;
-
-	wait_for_completion(&kafsasyncd_alive);
-
-	return ret;
-} /* end afs_kafsasyncd_start() */
-
-/*****************************************************************************/
-/*
- * stop the async daemon
- */
-void afs_kafsasyncd_stop(void)
-{
-	/* get rid of my daemon */
-	kafsasyncd_die = 1;
-	wake_up(&kafsasyncd_sleepq);
-	wait_for_completion(&kafsasyncd_dead);
-
-} /* end afs_kafsasyncd_stop() */
-
-/*****************************************************************************/
-/*
- * probing daemon
- */
-static int kafsasyncd(void *arg)
-{
-	struct afs_async_op *op;
-	int die;
-
-	DECLARE_WAITQUEUE(myself, current);
-
-	kafsasyncd_task = current;
-
-	printk("kAFS: Started kafsasyncd %d\n", current->pid);
-
-	daemonize("kafsasyncd");
-
-	complete(&kafsasyncd_alive);
-
-	/* loop around looking for things to attend to */
-	do {
-		set_current_state(TASK_INTERRUPTIBLE);
-		add_wait_queue(&kafsasyncd_sleepq, &myself);
-
-		for (;;) {
-			if (!list_empty(&kafsasyncd_async_attnq) ||
-			    signal_pending(current) ||
-			    kafsasyncd_die)
-				break;
-
-			schedule();
-			set_current_state(TASK_INTERRUPTIBLE);
-		}
-
-		remove_wait_queue(&kafsasyncd_sleepq, &myself);
-		set_current_state(TASK_RUNNING);
-
-		try_to_freeze();
-
-		/* discard pending signals */
-		afs_discard_my_signals();
-
-		die = kafsasyncd_die;
-
-		/* deal with the next asynchronous operation requiring
-		 * attention */
-		if (!list_empty(&kafsasyncd_async_attnq)) {
-			struct afs_async_op *op;
-
-			_debug("@@@ Begin Asynchronous Operation");
-
-			op = NULL;
-			spin_lock(&kafsasyncd_async_lock);
-
-			if (!list_empty(&kafsasyncd_async_attnq)) {
-				op = list_entry(kafsasyncd_async_attnq.next,
-						struct afs_async_op, link);
-				list_move_tail(&op->link,
-					      &kafsasyncd_async_busyq);
-			}
-
-			spin_unlock(&kafsasyncd_async_lock);
-
-			_debug("@@@ Operation %p {%p}\n",
-			       op, op ? op->ops : NULL);
-
-			if (op)
-				op->ops->attend(op);
-
-			_debug("@@@ End Asynchronous Operation");
-		}
-
-	} while(!die);
-
-	/* need to kill all outstanding asynchronous operations before
-	 * exiting */
-	kafsasyncd_task = NULL;
-	spin_lock(&kafsasyncd_async_lock);
-
-	/* fold the busy and attention queues together */
-	list_splice_init(&kafsasyncd_async_busyq,
-			 &kafsasyncd_async_attnq);
-
-	/* dequeue kafsasyncd from all their wait queues */
-	list_for_each_entry(op, &kafsasyncd_async_attnq, link) {
-		op->call->app_attn_func = kafsasyncd_null_call_attn_func;
-		op->call->app_error_func = kafsasyncd_null_call_error_func;
-		remove_wait_queue(&op->call->waitq, &op->waiter);
-	}
-
-	spin_unlock(&kafsasyncd_async_lock);
-
-	/* abort all the operations */
-	while (!list_empty(&kafsasyncd_async_attnq)) {
-		op = list_entry(kafsasyncd_async_attnq.next, struct afs_async_op, link);
-		list_del_init(&op->link);
-
-		rxrpc_call_abort(op->call, -EIO);
-		rxrpc_put_call(op->call);
-		op->call = NULL;
-
-		op->ops->discard(op);
-	}
-
-	/* and that's all */
-	_leave("");
-	complete_and_exit(&kafsasyncd_dead, 0);
-
-} /* end kafsasyncd() */
-
-/*****************************************************************************/
-/*
- * begin an operation
- * - place operation on busy queue
- */
-void afs_kafsasyncd_begin_op(struct afs_async_op *op)
-{
-	_enter("");
-
-	spin_lock(&kafsasyncd_async_lock);
-
-	init_waitqueue_entry(&op->waiter, kafsasyncd_task);
-	add_wait_queue(&op->call->waitq, &op->waiter);
-
-	list_move_tail(&op->link, &kafsasyncd_async_busyq);
-
-	spin_unlock(&kafsasyncd_async_lock);
-
-	_leave("");
-} /* end afs_kafsasyncd_begin_op() */
-
-/*****************************************************************************/
-/*
- * request attention for an operation
- * - move to attention queue
- */
-void afs_kafsasyncd_attend_op(struct afs_async_op *op)
-{
-	_enter("");
-
-	spin_lock(&kafsasyncd_async_lock);
-
-	list_move_tail(&op->link, &kafsasyncd_async_attnq);
-
-	spin_unlock(&kafsasyncd_async_lock);
-
-	wake_up(&kafsasyncd_sleepq);
-
-	_leave("");
-} /* end afs_kafsasyncd_attend_op() */
-
-/*****************************************************************************/
-/*
- * terminate an operation
- * - remove from either queue
- */
-void afs_kafsasyncd_terminate_op(struct afs_async_op *op)
-{
-	_enter("");
-
-	spin_lock(&kafsasyncd_async_lock);
-
-	if (!list_empty(&op->link)) {
-		list_del_init(&op->link);
-		remove_wait_queue(&op->call->waitq, &op->waiter);
-	}
-
-	spin_unlock(&kafsasyncd_async_lock);
-
-	wake_up(&kafsasyncd_sleepq);
-
-	_leave("");
-} /* end afs_kafsasyncd_terminate_op() */
diff --git a/fs/afs/kafsasyncd.h b/fs/afs/kafsasyncd.h
deleted file mode 100644
index 791803f..0000000
--- a/fs/afs/kafsasyncd.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/* kafsasyncd.h: AFS asynchronous operation daemon
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_AFS_KAFSASYNCD_H
-#define _LINUX_AFS_KAFSASYNCD_H
-
-#include "types.h"
-
-struct afs_async_op;
-
-struct afs_async_op_ops {
-	void (*attend)(struct afs_async_op *op);
-	void (*discard)(struct afs_async_op *op);
-};
-
-/*****************************************************************************/
-/*
- * asynchronous operation record
- */
-struct afs_async_op
-{
-	struct list_head		link;
-	struct afs_server		*server;	/* server being contacted */
-	struct rxrpc_call		*call;		/* RxRPC call performing op */
-	wait_queue_t			waiter;		/* wait queue for kafsasyncd */
-	const struct afs_async_op_ops	*ops;		/* operations */
-};
-
-static inline void afs_async_op_init(struct afs_async_op *op,
-				     const struct afs_async_op_ops *ops)
-{
-	INIT_LIST_HEAD(&op->link);
-	op->call = NULL;
-	op->ops = ops;
-}
-
-extern int afs_kafsasyncd_start(void);
-extern void afs_kafsasyncd_stop(void);
-
-extern void afs_kafsasyncd_begin_op(struct afs_async_op *op);
-extern void afs_kafsasyncd_attend_op(struct afs_async_op *op);
-extern void afs_kafsasyncd_terminate_op(struct afs_async_op *op);
-
-#endif /* _LINUX_AFS_KAFSASYNCD_H */
diff --git a/fs/afs/kafstimod.c b/fs/afs/kafstimod.c
deleted file mode 100644
index 694344e..0000000
--- a/fs/afs/kafstimod.c
+++ /dev/null
@@ -1,205 +0,0 @@
-/* kafstimod.c: AFS timeout daemon
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/completion.h>
-#include <linux/freezer.h>
-#include "cell.h"
-#include "volume.h"
-#include "kafstimod.h"
-#include <asm/errno.h>
-#include "internal.h"
-
-static DECLARE_COMPLETION(kafstimod_alive);
-static DECLARE_COMPLETION(kafstimod_dead);
-static DECLARE_WAIT_QUEUE_HEAD(kafstimod_sleepq);
-static int kafstimod_die;
-
-static LIST_HEAD(kafstimod_list);
-static DEFINE_SPINLOCK(kafstimod_lock);
-
-static int kafstimod(void *arg);
-
-/*****************************************************************************/
-/*
- * start the timeout daemon
- */
-int afs_kafstimod_start(void)
-{
-	int ret;
-
-	ret = kernel_thread(kafstimod, NULL, 0);
-	if (ret < 0)
-		return ret;
-
-	wait_for_completion(&kafstimod_alive);
-
-	return ret;
-} /* end afs_kafstimod_start() */
-
-/*****************************************************************************/
-/*
- * stop the timeout daemon
- */
-void afs_kafstimod_stop(void)
-{
-	/* get rid of my daemon */
-	kafstimod_die = 1;
-	wake_up(&kafstimod_sleepq);
-	wait_for_completion(&kafstimod_dead);
-
-} /* end afs_kafstimod_stop() */
-
-/*****************************************************************************/
-/*
- * timeout processing daemon
- */
-static int kafstimod(void *arg)
-{
-	struct afs_timer *timer;
-
-	DECLARE_WAITQUEUE(myself, current);
-
-	printk("kAFS: Started kafstimod %d\n", current->pid);
-
-	daemonize("kafstimod");
-
-	complete(&kafstimod_alive);
-
-	/* loop around looking for things to attend to */
- loop:
-	set_current_state(TASK_INTERRUPTIBLE);
-	add_wait_queue(&kafstimod_sleepq, &myself);
-
-	for (;;) {
-		unsigned long jif;
-		signed long timeout;
-
-		/* deal with the server being asked to die */
-		if (kafstimod_die) {
-			remove_wait_queue(&kafstimod_sleepq, &myself);
-			_leave("");
-			complete_and_exit(&kafstimod_dead, 0);
-		}
-
-		try_to_freeze();
-
-		/* discard pending signals */
-		afs_discard_my_signals();
-
-		/* work out the time to elapse before the next event */
-		spin_lock(&kafstimod_lock);
-		if (list_empty(&kafstimod_list)) {
-			timeout = MAX_SCHEDULE_TIMEOUT;
-		}
-		else {
-			timer = list_entry(kafstimod_list.next,
-					   struct afs_timer, link);
-			timeout = timer->timo_jif;
-			jif = jiffies;
-
-			if (time_before_eq((unsigned long) timeout, jif))
-				goto immediate;
-
-			else {
-				timeout = (long) timeout - (long) jiffies;
-			}
-		}
-		spin_unlock(&kafstimod_lock);
-
-		schedule_timeout(timeout);
-
-		set_current_state(TASK_INTERRUPTIBLE);
-	}
-
-	/* the thing on the front of the queue needs processing
-	 * - we come here with the lock held and timer pointing to the expired
-	 *   entry
-	 */
- immediate:
-	remove_wait_queue(&kafstimod_sleepq, &myself);
-	set_current_state(TASK_RUNNING);
-
-	_debug("@@@ Begin Timeout of %p", timer);
-
-	/* dequeue the timer */
-	list_del_init(&timer->link);
-	spin_unlock(&kafstimod_lock);
-
-	/* call the timeout function */
-	timer->ops->timed_out(timer);
-
-	_debug("@@@ End Timeout");
-	goto loop;
-
-} /* end kafstimod() */
-
-/*****************************************************************************/
-/*
- * (re-)queue a timer
- */
-void afs_kafstimod_add_timer(struct afs_timer *timer, unsigned long timeout)
-{
-	struct afs_timer *ptimer;
-	struct list_head *_p;
-
-	_enter("%p,%lu", timer, timeout);
-
-	spin_lock(&kafstimod_lock);
-
-	list_del(&timer->link);
-
-	/* the timer was deferred or reset - put it back in the queue at the
-	 * right place */
-	timer->timo_jif = jiffies + timeout;
-
-	list_for_each(_p, &kafstimod_list) {
-		ptimer = list_entry(_p, struct afs_timer, link);
-		if (time_before(timer->timo_jif, ptimer->timo_jif))
-			break;
-	}
-
-	list_add_tail(&timer->link, _p); /* insert before stopping point */
-
-	spin_unlock(&kafstimod_lock);
-
-	wake_up(&kafstimod_sleepq);
-
-	_leave("");
-} /* end afs_kafstimod_add_timer() */
-
-/*****************************************************************************/
-/*
- * dequeue a timer
- * - returns 0 if the timer was deleted or -ENOENT if it wasn't queued
- */
-int afs_kafstimod_del_timer(struct afs_timer *timer)
-{
-	int ret = 0;
-
-	_enter("%p", timer);
-
-	spin_lock(&kafstimod_lock);
-
-	if (list_empty(&timer->link))
-		ret = -ENOENT;
-	else
-		list_del_init(&timer->link);
-
-	spin_unlock(&kafstimod_lock);
-
-	wake_up(&kafstimod_sleepq);
-
-	_leave(" = %d", ret);
-	return ret;
-} /* end afs_kafstimod_del_timer() */
diff --git a/fs/afs/kafstimod.h b/fs/afs/kafstimod.h
deleted file mode 100644
index e312f1a..0000000
--- a/fs/afs/kafstimod.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* kafstimod.h: AFS timeout daemon
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_AFS_KAFSTIMOD_H
-#define _LINUX_AFS_KAFSTIMOD_H
-
-#include "types.h"
-
-struct afs_timer;
-
-struct afs_timer_ops {
-	/* called when the front of the timer queue has timed out */
-	void (*timed_out)(struct afs_timer *timer);
-};
-
-/*****************************************************************************/
-/*
- * AFS timer/timeout record
- */
-struct afs_timer
-{
-	struct list_head		link;		/* link in timer queue */
-	unsigned long			timo_jif;	/* timeout time */
-	const struct afs_timer_ops	*ops;		/* timeout expiry function */
-};
-
-static inline void afs_timer_init(struct afs_timer *timer,
-				  const struct afs_timer_ops *ops)
-{
-	INIT_LIST_HEAD(&timer->link);
-	timer->ops = ops;
-}
-
-extern int afs_kafstimod_start(void);
-extern void afs_kafstimod_stop(void);
-
-extern void afs_kafstimod_add_timer(struct afs_timer *timer,
-				    unsigned long timeout);
-extern int afs_kafstimod_del_timer(struct afs_timer *timer);
-
-#endif /* _LINUX_AFS_KAFSTIMOD_H */
diff --git a/fs/afs/main.c b/fs/afs/main.c
index f2704ba..40c2704 100644
--- a/fs/afs/main.c
+++ b/fs/afs/main.c
@@ -1,4 +1,4 @@
-/* main.c: AFS client file system
+/* AFS client file system
  *
  * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
@@ -13,43 +13,21 @@
 #include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/completion.h>
-#include <rxrpc/rxrpc.h>
-#include <rxrpc/transport.h>
-#include <rxrpc/call.h>
-#include <rxrpc/peer.h>
-#include "cache.h"
-#include "cell.h"
-#include "server.h"
-#include "fsclient.h"
-#include "cmservice.h"
-#include "kafstimod.h"
-#include "kafsasyncd.h"
 #include "internal.h"
 
-struct rxrpc_transport *afs_transport;
-
-static int afs_adding_peer(struct rxrpc_peer *peer);
-static void afs_discarding_peer(struct rxrpc_peer *peer);
-
-
 MODULE_DESCRIPTION("AFS Client File System");
 MODULE_AUTHOR("Red Hat, Inc.");
 MODULE_LICENSE("GPL");
 
+unsigned afs_debug;
+module_param_named(debug, afs_debug, uint, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(afs_debug, "AFS debugging mask");
+
 static char *rootcell;
 
 module_param(rootcell, charp, 0);
 MODULE_PARM_DESC(rootcell, "root AFS cell name and VL server IP addr list");
 
-
-static struct rxrpc_peer_ops afs_peer_ops = {
-	.adding		= afs_adding_peer,
-	.discarding	= afs_discarding_peer,
-};
-
-struct list_head afs_cb_hash_tbl[AFS_CB_HASH_COUNT];
-DEFINE_SPINLOCK(afs_cb_hash_lock);
-
 #ifdef AFS_CACHING_SUPPORT
 static struct cachefs_netfs_operations afs_cache_ops = {
 	.get_page_cookie	= afs_cache_get_page_cookie,
@@ -62,20 +40,63 @@
 };
 #endif
 
-/*****************************************************************************/
+struct afs_uuid afs_uuid;
+
+/*
+ * get a client UUID
+ */
+static int __init afs_get_client_UUID(void)
+{
+	struct timespec ts;
+	u64 uuidtime;
+	u16 clockseq;
+	int ret;
+
+	/* read the MAC address of one of the external interfaces and construct
+	 * a UUID from it */
+	ret = afs_get_MAC_address(afs_uuid.node);
+	if (ret < 0)
+		return ret;
+
+	getnstimeofday(&ts);
+	uuidtime = (u64) ts.tv_sec * 1000 * 1000 * 10;
+	uuidtime += ts.tv_nsec / 100;
+	uuidtime += AFS_UUID_TO_UNIX_TIME;
+	afs_uuid.time_low = uuidtime;
+	afs_uuid.time_mid = uuidtime >> 32;
+	afs_uuid.time_hi_and_version = (uuidtime >> 48) & AFS_UUID_TIMEHI_MASK;
+	afs_uuid.time_hi_and_version = AFS_UUID_VERSION_TIME;
+
+	get_random_bytes(&clockseq, 2);
+	afs_uuid.clock_seq_low = clockseq;
+	afs_uuid.clock_seq_hi_and_reserved =
+		(clockseq >> 8) & AFS_UUID_CLOCKHI_MASK;
+	afs_uuid.clock_seq_hi_and_reserved = AFS_UUID_VARIANT_STD;
+
+	_debug("AFS UUID: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+	       afs_uuid.time_low,
+	       afs_uuid.time_mid,
+	       afs_uuid.time_hi_and_version,
+	       afs_uuid.clock_seq_hi_and_reserved,
+	       afs_uuid.clock_seq_low,
+	       afs_uuid.node[0], afs_uuid.node[1], afs_uuid.node[2],
+	       afs_uuid.node[3], afs_uuid.node[4], afs_uuid.node[5]);
+
+	return 0;
+}
+
 /*
  * initialise the AFS client FS module
  */
 static int __init afs_init(void)
 {
-	int loop, ret;
+	int ret;
 
 	printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 registering.\n");
 
-	/* initialise the callback hash table */
-	spin_lock_init(&afs_cb_hash_lock);
-	for (loop = AFS_CB_HASH_COUNT - 1; loop >= 0; loop--)
-		INIT_LIST_HEAD(&afs_cb_hash_tbl[loop]);
+	ret = afs_get_client_UUID();
+	if (ret < 0)
+		return ret;
 
 	/* register the /proc stuff */
 	ret = afs_proc_init();
@@ -87,70 +108,56 @@
 	ret = cachefs_register_netfs(&afs_cache_netfs,
 				     &afs_cache_cell_index_def);
 	if (ret < 0)
-		goto error;
-#endif
-
-#ifdef CONFIG_KEYS_TURNED_OFF
-	ret = afs_key_register();
-	if (ret < 0)
 		goto error_cache;
 #endif
 
 	/* initialise the cell DB */
 	ret = afs_cell_init(rootcell);
 	if (ret < 0)
-		goto error_keys;
+		goto error_cell_init;
 
-	/* start the timeout daemon */
-	ret = afs_kafstimod_start();
+	/* initialise the VL update process */
+	ret = afs_vlocation_update_init();
 	if (ret < 0)
-		goto error_keys;
+		goto error_vl_update_init;
 
-	/* start the async operation daemon */
-	ret = afs_kafsasyncd_start();
-	if (ret < 0)
-		goto error_kafstimod;
+	/* initialise the callback update process */
+	ret = afs_callback_update_init();
 
 	/* create the RxRPC transport */
-	ret = rxrpc_create_transport(7001, &afs_transport);
+	ret = afs_open_socket();
 	if (ret < 0)
-		goto error_kafsasyncd;
-
-	afs_transport->peer_ops = &afs_peer_ops;
+		goto error_open_socket;
 
 	/* register the filesystems */
 	ret = afs_fs_init();
 	if (ret < 0)
-		goto error_transport;
+		goto error_fs;
 
 	return ret;
 
- error_transport:
-	rxrpc_put_transport(afs_transport);
- error_kafsasyncd:
-	afs_kafsasyncd_stop();
- error_kafstimod:
-	afs_kafstimod_stop();
- error_keys:
-#ifdef CONFIG_KEYS_TURNED_OFF
-	afs_key_unregister();
- error_cache:
-#endif
+error_fs:
+	afs_close_socket();
+error_open_socket:
+error_vl_update_init:
+error_cell_init:
 #ifdef AFS_CACHING_SUPPORT
 	cachefs_unregister_netfs(&afs_cache_netfs);
- error:
+error_cache:
 #endif
+	afs_callback_update_kill();
+	afs_vlocation_purge();
 	afs_cell_purge();
 	afs_proc_cleanup();
 	printk(KERN_ERR "kAFS: failed to register: %d\n", ret);
 	return ret;
-} /* end afs_init() */
+}
 
 /* XXX late_initcall is kludgy, but the only alternative seems to create
  * a transport upon the first mount, which is worse. Or is it?
  */
 late_initcall(afs_init);	/* must be called after net/ to create socket */
-/*****************************************************************************/
+
 /*
  * clean up on module removal
  */
@@ -159,127 +166,16 @@
 	printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 unregistering.\n");
 
 	afs_fs_exit();
-	rxrpc_put_transport(afs_transport);
-	afs_kafstimod_stop();
-	afs_kafsasyncd_stop();
+	afs_close_socket();
+	afs_purge_servers();
+	afs_callback_update_kill();
+	afs_vlocation_purge();
+	flush_scheduled_work();
 	afs_cell_purge();
-#ifdef CONFIG_KEYS_TURNED_OFF
-	afs_key_unregister();
-#endif
 #ifdef AFS_CACHING_SUPPORT
 	cachefs_unregister_netfs(&afs_cache_netfs);
 #endif
 	afs_proc_cleanup();
-
-} /* end afs_exit() */
+}
 
 module_exit(afs_exit);
-
-/*****************************************************************************/
-/*
- * notification that new peer record is being added
- * - called from krxsecd
- * - return an error to induce an abort
- * - mustn't sleep (caller holds an rwlock)
- */
-static int afs_adding_peer(struct rxrpc_peer *peer)
-{
-	struct afs_server *server;
-	int ret;
-
-	_debug("kAFS: Adding new peer %08x\n", ntohl(peer->addr.s_addr));
-
-	/* determine which server the peer resides in (if any) */
-	ret = afs_server_find_by_peer(peer, &server);
-	if (ret < 0)
-		return ret; /* none that we recognise, so abort */
-
-	_debug("Server %p{u=%d}\n", server, atomic_read(&server->usage));
-
-	_debug("Cell %p{u=%d}\n",
-	       server->cell, atomic_read(&server->cell->usage));
-
-	/* cross-point the structs under a global lock */
-	spin_lock(&afs_server_peer_lock);
-	peer->user = server;
-	server->peer = peer;
-	spin_unlock(&afs_server_peer_lock);
-
-	afs_put_server(server);
-
-	return 0;
-} /* end afs_adding_peer() */
-
-/*****************************************************************************/
-/*
- * notification that a peer record is being discarded
- * - called from krxiod or krxsecd
- */
-static void afs_discarding_peer(struct rxrpc_peer *peer)
-{
-	struct afs_server *server;
-
-	_enter("%p",peer);
-
-	_debug("Discarding peer %08x (rtt=%lu.%lumS)\n",
-	       ntohl(peer->addr.s_addr),
-	       (long) (peer->rtt / 1000),
-	       (long) (peer->rtt % 1000));
-
-	/* uncross-point the structs under a global lock */
-	spin_lock(&afs_server_peer_lock);
-	server = peer->user;
-	if (server) {
-		peer->user = NULL;
-		server->peer = NULL;
-	}
-	spin_unlock(&afs_server_peer_lock);
-
-	_leave("");
-
-} /* end afs_discarding_peer() */
-
-/*****************************************************************************/
-/*
- * clear the dead space between task_struct and kernel stack
- * - called by supplying -finstrument-functions to gcc
- */
-#if 0
-void __cyg_profile_func_enter (void *this_fn, void *call_site)
-__attribute__((no_instrument_function));
-
-void __cyg_profile_func_enter (void *this_fn, void *call_site)
-{
-       asm volatile("  movl    %%esp,%%edi     \n"
-                    "  andl    %0,%%edi        \n"
-                    "  addl    %1,%%edi        \n"
-                    "  movl    %%esp,%%ecx     \n"
-                    "  subl    %%edi,%%ecx     \n"
-                    "  shrl    $2,%%ecx        \n"
-                    "  movl    $0xedededed,%%eax     \n"
-                    "  rep stosl               \n"
-                    :
-                    : "i"(~(THREAD_SIZE - 1)), "i"(sizeof(struct thread_info))
-                    : "eax", "ecx", "edi", "memory", "cc"
-                    );
-}
-
-void __cyg_profile_func_exit(void *this_fn, void *call_site)
-__attribute__((no_instrument_function));
-
-void __cyg_profile_func_exit(void *this_fn, void *call_site)
-{
-       asm volatile("  movl    %%esp,%%edi     \n"
-                    "  andl    %0,%%edi        \n"
-                    "  addl    %1,%%edi        \n"
-                    "  movl    %%esp,%%ecx     \n"
-                    "  subl    %%edi,%%ecx     \n"
-                    "  shrl    $2,%%ecx        \n"
-                    "  movl    $0xdadadada,%%eax     \n"
-                    "  rep stosl               \n"
-                    :
-                    : "i"(~(THREAD_SIZE - 1)), "i"(sizeof(struct thread_info))
-                    : "eax", "ecx", "edi", "memory", "cc"
-                    );
-}
-#endif
diff --git a/fs/afs/misc.c b/fs/afs/misc.c
index e4fce66..cdb9792 100644
--- a/fs/afs/misc.c
+++ b/fs/afs/misc.c
@@ -1,6 +1,6 @@
-/* misc.c: miscellaneous bits
+/* miscellaneous bits
  *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
@@ -12,19 +12,20 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/errno.h>
-#include "errors.h"
 #include "internal.h"
+#include "afs_fs.h"
 
-/*****************************************************************************/
 /*
  * convert an AFS abort code to a Linux error number
  */
-int afs_abort_to_error(int abortcode)
+int afs_abort_to_error(u32 abort_code)
 {
-	switch (abortcode) {
+	switch (abort_code) {
+	case 13:		return -EACCES;
+	case 30:		return -EROFS;
 	case VSALVAGE:		return -EIO;
 	case VNOVNODE:		return -ENOENT;
-	case VNOVOL:		return -ENXIO;
+	case VNOVOL:		return -ENOMEDIUM;
 	case VVOLEXISTS:	return -EEXIST;
 	case VNOSERVICE:	return -EIO;
 	case VOFFLINE:		return -ENOENT;
@@ -33,7 +34,24 @@
 	case VOVERQUOTA:	return -EDQUOT;
 	case VBUSY:		return -EBUSY;
 	case VMOVED:		return -ENXIO;
-	default:		return -EIO;
+	case 0x2f6df0c:		return -EACCES;
+	case 0x2f6df0f:		return -EBUSY;
+	case 0x2f6df10:		return -EEXIST;
+	case 0x2f6df11:		return -EXDEV;
+	case 0x2f6df13:		return -ENOTDIR;
+	case 0x2f6df14:		return -EISDIR;
+	case 0x2f6df15:		return -EINVAL;
+	case 0x2f6df1a:		return -EFBIG;
+	case 0x2f6df1b:		return -ENOSPC;
+	case 0x2f6df1d:		return -EROFS;
+	case 0x2f6df1e:		return -EMLINK;
+	case 0x2f6df20:		return -EDOM;
+	case 0x2f6df21:		return -ERANGE;
+	case 0x2f6df22:		return -EDEADLK;
+	case 0x2f6df23:		return -ENAMETOOLONG;
+	case 0x2f6df24:		return -ENOLCK;
+	case 0x2f6df26:		return -ENOTEMPTY;
+	case 0x2f6df78:		return -EDQUOT;
+	default:		return -EREMOTEIO;
 	}
-
-} /* end afs_abort_to_error() */
+}
diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c
index 68495f0..b905ae3 100644
--- a/fs/afs/mntpt.c
+++ b/fs/afs/mntpt.c
@@ -1,4 +1,4 @@
-/* mntpt.c: mountpoint management
+/* mountpoint management
  *
  * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
@@ -18,10 +18,6 @@
 #include <linux/mount.h>
 #include <linux/namei.h>
 #include <linux/mnt_namespace.h>
-#include "super.h"
-#include "cell.h"
-#include "volume.h"
-#include "vnode.h"
 #include "internal.h"
 
 
@@ -30,6 +26,7 @@
 				       struct nameidata *nd);
 static int afs_mntpt_open(struct inode *inode, struct file *file);
 static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd);
+static void afs_mntpt_expiry_timed_out(struct work_struct *work);
 
 const struct file_operations afs_mntpt_file_operations = {
 	.open		= afs_mntpt_open,
@@ -43,24 +40,19 @@
 };
 
 static LIST_HEAD(afs_vfsmounts);
+static DECLARE_DELAYED_WORK(afs_mntpt_expiry_timer, afs_mntpt_expiry_timed_out);
 
-static void afs_mntpt_expiry_timed_out(struct afs_timer *timer);
+unsigned long afs_mntpt_expiry_timeout = 10 * 60;
 
-struct afs_timer_ops afs_mntpt_expiry_timer_ops = {
-	.timed_out	= afs_mntpt_expiry_timed_out,
-};
-
-struct afs_timer afs_mntpt_expiry_timer;
-
-unsigned long afs_mntpt_expiry_timeout = 20;
-
-/*****************************************************************************/
 /*
  * check a symbolic link to see whether it actually encodes a mountpoint
  * - sets the AFS_VNODE_MOUNTPOINT flag on the vnode appropriately
  */
-int afs_mntpt_check_symlink(struct afs_vnode *vnode)
+int afs_mntpt_check_symlink(struct afs_vnode *vnode, struct key *key)
 {
+	struct file file = {
+		.private_data = key,
+	};
 	struct page *page;
 	size_t size;
 	char *buf;
@@ -69,7 +61,7 @@
 	_enter("{%u,%u}", vnode->fid.vnode, vnode->fid.unique);
 
 	/* read the contents of the symlink into the pagecache */
-	page = read_mapping_page(AFS_VNODE_TO_I(vnode)->i_mapping, 0, NULL);
+	page = read_mapping_page(AFS_VNODE_TO_I(vnode)->i_mapping, 0, &file);
 	if (IS_ERR(page)) {
 		ret = PTR_ERR(page);
 		goto out;
@@ -85,7 +77,7 @@
 
 	/* examine the symlink's contents */
 	size = vnode->status.size;
-	_debug("symlink to %*.*s", size, (int) size, buf);
+	_debug("symlink to %*.*s", (int) size, (int) size, buf);
 
 	if (size > 2 &&
 	    (buf[0] == '%' || buf[0] == '#') &&
@@ -93,22 +85,20 @@
 	    ) {
 		_debug("symlink is a mountpoint");
 		spin_lock(&vnode->lock);
-		vnode->flags |= AFS_VNODE_MOUNTPOINT;
+		set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
 		spin_unlock(&vnode->lock);
 	}
 
 	ret = 0;
 
- out_free:
+out_free:
 	kunmap(page);
 	page_cache_release(page);
- out:
+out:
 	_leave(" = %d", ret);
 	return ret;
+}
 
-} /* end afs_mntpt_check_symlink() */
-
-/*****************************************************************************/
 /*
  * no valid lookup procedure on this sort of dir
  */
@@ -116,7 +106,7 @@
 				       struct dentry *dentry,
 				       struct nameidata *nd)
 {
-	kenter("%p,%p{%p{%s},%s}",
+	_enter("%p,%p{%p{%s},%s}",
 	       dir,
 	       dentry,
 	       dentry->d_parent,
@@ -125,15 +115,14 @@
 	       dentry->d_name.name);
 
 	return ERR_PTR(-EREMOTE);
-} /* end afs_mntpt_lookup() */
+}
 
-/*****************************************************************************/
 /*
  * no valid open procedure on this sort of dir
  */
 static int afs_mntpt_open(struct inode *inode, struct file *file)
 {
-	kenter("%p,%p{%p{%s},%s}",
+	_enter("%p,%p{%p{%s},%s}",
 	       inode, file,
 	       file->f_path.dentry->d_parent,
 	       file->f_path.dentry->d_parent ?
@@ -142,9 +131,8 @@
 	       file->f_path.dentry->d_name.name);
 
 	return -EREMOTE;
-} /* end afs_mntpt_open() */
+}
 
-/*****************************************************************************/
 /*
  * create a vfsmount to be automounted
  */
@@ -157,7 +145,7 @@
 	char *buf, *devname = NULL, *options = NULL;
 	int ret;
 
-	kenter("{%s}", mntpt->d_name.name);
+	_enter("{%s}", mntpt->d_name.name);
 
 	BUG_ON(!mntpt->d_inode);
 
@@ -201,79 +189,108 @@
 		strcat(options, ",rwpath");
 
 	/* try and do the mount */
-	kdebug("--- attempting mount %s -o %s ---", devname, options);
+	_debug("--- attempting mount %s -o %s ---", devname, options);
 	mnt = vfs_kern_mount(&afs_fs_type, 0, devname, options);
-	kdebug("--- mount result %p ---", mnt);
+	_debug("--- mount result %p ---", mnt);
 
 	free_page((unsigned long) devname);
 	free_page((unsigned long) options);
-	kleave(" = %p", mnt);
+	_leave(" = %p", mnt);
 	return mnt;
 
- error:
+error:
 	if (page)
 		page_cache_release(page);
 	if (devname)
 		free_page((unsigned long) devname);
 	if (options)
 		free_page((unsigned long) options);
-	kleave(" = %d", ret);
+	_leave(" = %d", ret);
 	return ERR_PTR(ret);
-} /* end afs_mntpt_do_automount() */
+}
 
-/*****************************************************************************/
 /*
  * follow a link from a mountpoint directory, thus causing it to be mounted
  */
 static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
 	struct vfsmount *newmnt;
-	struct dentry *old_dentry;
 	int err;
 
-	kenter("%p{%s},{%s:%p{%s}}",
+	_enter("%p{%s},{%s:%p{%s},}",
 	       dentry,
 	       dentry->d_name.name,
 	       nd->mnt->mnt_devname,
 	       dentry,
 	       nd->dentry->d_name.name);
 
-	newmnt = afs_mntpt_do_automount(dentry);
+	dput(nd->dentry);
+	nd->dentry = dget(dentry);
+
+	newmnt = afs_mntpt_do_automount(nd->dentry);
 	if (IS_ERR(newmnt)) {
 		path_release(nd);
 		return (void *)newmnt;
 	}
 
-	old_dentry = nd->dentry;
-	nd->dentry = dentry;
-	err = do_add_mount(newmnt, nd, 0, &afs_vfsmounts);
-	nd->dentry = old_dentry;
-
-	path_release(nd);
-
-	if (!err) {
-		mntget(newmnt);
+	mntget(newmnt);
+	err = do_add_mount(newmnt, nd, MNT_SHRINKABLE, &afs_vfsmounts);
+	switch (err) {
+	case 0:
+		mntput(nd->mnt);
+		dput(nd->dentry);
 		nd->mnt = newmnt;
-		dget(newmnt->mnt_root);
-		nd->dentry = newmnt->mnt_root;
+		nd->dentry = dget(newmnt->mnt_root);
+		schedule_delayed_work(&afs_mntpt_expiry_timer,
+				      afs_mntpt_expiry_timeout * HZ);
+		break;
+	case -EBUSY:
+		/* someone else made a mount here whilst we were busy */
+		while (d_mountpoint(nd->dentry) &&
+		       follow_down(&nd->mnt, &nd->dentry))
+			;
+		err = 0;
+	default:
+		mntput(newmnt);
+		break;
 	}
 
-	kleave(" = %d", err);
+	_leave(" = %d", err);
 	return ERR_PTR(err);
-} /* end afs_mntpt_follow_link() */
+}
 
-/*****************************************************************************/
 /*
  * handle mountpoint expiry timer going off
  */
-static void afs_mntpt_expiry_timed_out(struct afs_timer *timer)
+static void afs_mntpt_expiry_timed_out(struct work_struct *work)
 {
-	kenter("");
+	_enter("");
 
-	mark_mounts_for_expiry(&afs_vfsmounts);
+	if (!list_empty(&afs_vfsmounts)) {
+		mark_mounts_for_expiry(&afs_vfsmounts);
+		schedule_delayed_work(&afs_mntpt_expiry_timer,
+				      afs_mntpt_expiry_timeout * HZ);
+	}
 
-	afs_kafstimod_add_timer(&afs_mntpt_expiry_timer,
-				afs_mntpt_expiry_timeout * HZ);
+	_leave("");
+}
 
-	kleave("");
-} /* end afs_mntpt_expiry_timed_out() */
+/*
+ * kill the AFS mountpoint timer if it's still running
+ */
+void afs_mntpt_kill_timer(void)
+{
+	_enter("");
+
+	ASSERT(list_empty(&afs_vfsmounts));
+	cancel_delayed_work(&afs_mntpt_expiry_timer);
+	flush_scheduled_work();
+}
+
+/*
+ * begin unmount by attempting to remove all automounted mountpoints we added
+ */
+void afs_umount_begin(struct vfsmount *vfsmnt, int flags)
+{
+	shrink_submounts(vfsmnt, &afs_vfsmounts);
+}
diff --git a/fs/afs/mount.h b/fs/afs/mount.h
deleted file mode 100644
index 9d2f46e..0000000
--- a/fs/afs/mount.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* mount.h: mount parameters
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_AFS_MOUNT_H
-#define _LINUX_AFS_MOUNT_H
-
-struct afs_mountdata {
-	const char		*volume;	/* name of volume */
-	const char		*cell;		/* name of cell containing volume */
-	const char		*cache;		/* name of cache block device */
-	size_t			nservers;	/* number of server addresses listed */
-	uint32_t		servers[10];	/* IP addresses of servers in this cell */
-};
-
-#endif /* _LINUX_AFS_MOUNT_H */
diff --git a/fs/afs/proc.c b/fs/afs/proc.c
index ae6b85b..d5601f6 100644
--- a/fs/afs/proc.c
+++ b/fs/afs/proc.c
@@ -1,4 +1,4 @@
-/* proc.c: /proc interface for AFS
+/* /proc interface for AFS
  *
  * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
@@ -13,8 +13,6 @@
 #include <linux/module.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
-#include "cell.h"
-#include "volume.h"
 #include <asm/uaccess.h>
 #include "internal.h"
 
@@ -130,7 +128,6 @@
 	.release	= afs_proc_cell_servers_release,
 };
 
-/*****************************************************************************/
 /*
  * initialise the /proc/fs/afs/ directory
  */
@@ -142,47 +139,43 @@
 
 	proc_afs = proc_mkdir("fs/afs", NULL);
 	if (!proc_afs)
-		goto error;
+		goto error_dir;
 	proc_afs->owner = THIS_MODULE;
 
 	p = create_proc_entry("cells", 0, proc_afs);
 	if (!p)
-		goto error_proc;
+		goto error_cells;
 	p->proc_fops = &afs_proc_cells_fops;
 	p->owner = THIS_MODULE;
 
 	p = create_proc_entry("rootcell", 0, proc_afs);
 	if (!p)
-		goto error_cells;
+		goto error_rootcell;
 	p->proc_fops = &afs_proc_rootcell_fops;
 	p->owner = THIS_MODULE;
 
 	_leave(" = 0");
 	return 0;
 
- error_cells:
+error_rootcell:
  	remove_proc_entry("cells", proc_afs);
- error_proc:
+error_cells:
 	remove_proc_entry("fs/afs", NULL);
- error:
+error_dir:
 	_leave(" = -ENOMEM");
 	return -ENOMEM;
+}
 
-} /* end afs_proc_init() */
-
-/*****************************************************************************/
 /*
  * clean up the /proc/fs/afs/ directory
  */
 void afs_proc_cleanup(void)
 {
+	remove_proc_entry("rootcell", proc_afs);
 	remove_proc_entry("cells", proc_afs);
-
 	remove_proc_entry("fs/afs", NULL);
+}
 
-} /* end afs_proc_cleanup() */
-
-/*****************************************************************************/
 /*
  * open "/proc/fs/afs/cells" which provides a summary of extant cells
  */
@@ -199,9 +192,8 @@
 	m->private = PDE(inode)->data;
 
 	return 0;
-} /* end afs_proc_cells_open() */
+}
 
-/*****************************************************************************/
 /*
  * set up the iterator to start reading from the cells list and return the
  * first item
@@ -225,9 +217,8 @@
 			break;
 
 	return _p != &afs_proc_cells ? _p : NULL;
-} /* end afs_proc_cells_start() */
+}
 
-/*****************************************************************************/
 /*
  * move to next cell in cells list
  */
@@ -241,19 +232,16 @@
 	_p = v == (void *) 1 ? afs_proc_cells.next : _p->next;
 
 	return _p != &afs_proc_cells ? _p : NULL;
-} /* end afs_proc_cells_next() */
+}
 
-/*****************************************************************************/
 /*
  * clean up after reading from the cells list
  */
 static void afs_proc_cells_stop(struct seq_file *p, void *v)
 {
 	up_read(&afs_proc_cells_sem);
+}
 
-} /* end afs_proc_cells_stop() */
-
-/*****************************************************************************/
 /*
  * display a header line followed by a load of cell lines
  */
@@ -261,19 +249,18 @@
 {
 	struct afs_cell *cell = list_entry(v, struct afs_cell, proc_link);
 
-	/* display header on line 1 */
 	if (v == (void *) 1) {
+		/* display header on line 1 */
 		seq_puts(m, "USE NAME\n");
 		return 0;
 	}
 
 	/* display one cell per line on subsequent lines */
-	seq_printf(m, "%3d %s\n", atomic_read(&cell->usage), cell->name);
-
+	seq_printf(m, "%3d %s\n",
+		   atomic_read(&cell->usage), cell->name);
 	return 0;
-} /* end afs_proc_cells_show() */
+}
 
-/*****************************************************************************/
 /*
  * handle writes to /proc/fs/afs/cells
  * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]"
@@ -326,30 +313,32 @@
 
 	if (strcmp(kbuf, "add") == 0) {
 		struct afs_cell *cell;
-		ret = afs_cell_create(name, args, &cell);
-		if (ret < 0)
-			goto done;
 
+		cell = afs_cell_create(name, args);
+		if (IS_ERR(cell)) {
+			ret = PTR_ERR(cell);
+			goto done;
+		}
+
+		afs_put_cell(cell);
 		printk("kAFS: Added new cell '%s'\n", name);
-	}
-	else {
+	} else {
 		goto inval;
 	}
 
 	ret = size;
 
- done:
+done:
 	kfree(kbuf);
 	_leave(" = %d", ret);
 	return ret;
 
- inval:
+inval:
 	ret = -EINVAL;
 	printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n");
 	goto done;
-} /* end afs_proc_cells_write() */
+}
 
-/*****************************************************************************/
 /*
  * Stubs for /proc/fs/afs/rootcell
  */
@@ -369,7 +358,6 @@
 	return 0;
 }
 
-/*****************************************************************************/
 /*
  * handle writes to /proc/fs/afs/rootcell
  * - to initialize rootcell: echo "cell.name:192.168.231.14"
@@ -407,14 +395,13 @@
 	if (ret >= 0)
 		ret = size;	/* consume everything, always */
 
- infault:
+infault:
 	kfree(kbuf);
- nomem:
+nomem:
 	_leave(" = %d", ret);
 	return ret;
-} /* end afs_proc_rootcell_write() */
+}
 
-/*****************************************************************************/
 /*
  * initialise /proc/fs/afs/<cell>/
  */
@@ -426,25 +413,25 @@
 
 	cell->proc_dir = proc_mkdir(cell->name, proc_afs);
 	if (!cell->proc_dir)
-		return -ENOMEM;
+		goto error_dir;
 
 	p = create_proc_entry("servers", 0, cell->proc_dir);
 	if (!p)
-		goto error_proc;
+		goto error_servers;
 	p->proc_fops = &afs_proc_cell_servers_fops;
 	p->owner = THIS_MODULE;
 	p->data = cell;
 
 	p = create_proc_entry("vlservers", 0, cell->proc_dir);
 	if (!p)
-		goto error_servers;
+		goto error_vlservers;
 	p->proc_fops = &afs_proc_cell_vlservers_fops;
 	p->owner = THIS_MODULE;
 	p->data = cell;
 
 	p = create_proc_entry("volumes", 0, cell->proc_dir);
 	if (!p)
-		goto error_vlservers;
+		goto error_volumes;
 	p->proc_fops = &afs_proc_cell_volumes_fops;
 	p->owner = THIS_MODULE;
 	p->data = cell;
@@ -452,17 +439,17 @@
 	_leave(" = 0");
 	return 0;
 
- error_vlservers:
+error_volumes:
 	remove_proc_entry("vlservers", cell->proc_dir);
- error_servers:
+error_vlservers:
 	remove_proc_entry("servers", cell->proc_dir);
- error_proc:
+error_servers:
 	remove_proc_entry(cell->name, proc_afs);
+error_dir:
 	_leave(" = -ENOMEM");
 	return -ENOMEM;
-} /* end afs_proc_cell_setup() */
+}
 
-/*****************************************************************************/
 /*
  * remove /proc/fs/afs/<cell>/
  */
@@ -476,9 +463,8 @@
 	remove_proc_entry(cell->name, proc_afs);
 
 	_leave("");
-} /* end afs_proc_cell_remove() */
+}
 
-/*****************************************************************************/
 /*
  * open "/proc/fs/afs/<cell>/volumes" which provides a summary of extant cells
  */
@@ -488,7 +474,7 @@
 	struct seq_file *m;
 	int ret;
 
-	cell = afs_get_cell_maybe((struct afs_cell **) &PDE(inode)->data);
+	cell = PDE(inode)->data;
 	if (!cell)
 		return -ENOENT;
 
@@ -500,25 +486,16 @@
 	m->private = cell;
 
 	return 0;
-} /* end afs_proc_cell_volumes_open() */
+}
 
-/*****************************************************************************/
 /*
  * close the file and release the ref to the cell
  */
 static int afs_proc_cell_volumes_release(struct inode *inode, struct file *file)
 {
-	struct afs_cell *cell = PDE(inode)->data;
-	int ret;
+	return seq_release(inode, file);
+}
 
-	ret = seq_release(inode,file);
-
-	afs_put_cell(cell);
-
-	return ret;
-} /* end afs_proc_cell_volumes_release() */
-
-/*****************************************************************************/
 /*
  * set up the iterator to start reading from the cells list and return the
  * first item
@@ -545,9 +522,8 @@
 			break;
 
 	return _p != &cell->vl_list ? _p : NULL;
-} /* end afs_proc_cell_volumes_start() */
+}
 
-/*****************************************************************************/
 /*
  * move to next cell in cells list
  */
@@ -562,12 +538,11 @@
 	(*_pos)++;
 
 	_p = v;
-	_p = v == (void *) 1 ? cell->vl_list.next : _p->next;
+	_p = (v == (void *) 1) ? cell->vl_list.next : _p->next;
 
-	return _p != &cell->vl_list ? _p : NULL;
-} /* end afs_proc_cell_volumes_next() */
+	return (_p != &cell->vl_list) ? _p : NULL;
+}
 
-/*****************************************************************************/
 /*
  * clean up after reading from the cells list
  */
@@ -576,10 +551,18 @@
 	struct afs_cell *cell = p->private;
 
 	up_read(&cell->vl_sem);
+}
 
-} /* end afs_proc_cell_volumes_stop() */
+const char afs_vlocation_states[][4] = {
+	[AFS_VL_NEW]			= "New",
+	[AFS_VL_CREATING]		= "Crt",
+	[AFS_VL_VALID]			= "Val",
+	[AFS_VL_NO_VOLUME]		= "NoV",
+	[AFS_VL_UPDATING]		= "Upd",
+	[AFS_VL_VOLUME_DELETED]		= "Del",
+	[AFS_VL_UNCERTAIN]		= "Unc",
+};
 
-/*****************************************************************************/
 /*
  * display a header line followed by a load of volume lines
  */
@@ -590,23 +573,22 @@
 
 	/* display header on line 1 */
 	if (v == (void *) 1) {
-		seq_puts(m, "USE VLID[0]  VLID[1]  VLID[2]  NAME\n");
+		seq_puts(m, "USE STT VLID[0]  VLID[1]  VLID[2]  NAME\n");
 		return 0;
 	}
 
 	/* display one cell per line on subsequent lines */
-	seq_printf(m, "%3d %08x %08x %08x %s\n",
+	seq_printf(m, "%3d %s %08x %08x %08x %s\n",
 		   atomic_read(&vlocation->usage),
+		   afs_vlocation_states[vlocation->state],
 		   vlocation->vldb.vid[0],
 		   vlocation->vldb.vid[1],
 		   vlocation->vldb.vid[2],
-		   vlocation->vldb.name
-		   );
+		   vlocation->vldb.name);
 
 	return 0;
-} /* end afs_proc_cell_volumes_show() */
+}
 
-/*****************************************************************************/
 /*
  * open "/proc/fs/afs/<cell>/vlservers" which provides a list of volume
  * location server
@@ -617,11 +599,11 @@
 	struct seq_file *m;
 	int ret;
 
-	cell = afs_get_cell_maybe((struct afs_cell**)&PDE(inode)->data);
+	cell = PDE(inode)->data;
 	if (!cell)
 		return -ENOENT;
 
-	ret = seq_open(file,&afs_proc_cell_vlservers_ops);
+	ret = seq_open(file, &afs_proc_cell_vlservers_ops);
 	if (ret<0)
 		return ret;
 
@@ -629,26 +611,17 @@
 	m->private = cell;
 
 	return 0;
-} /* end afs_proc_cell_vlservers_open() */
+}
 
-/*****************************************************************************/
 /*
  * close the file and release the ref to the cell
  */
 static int afs_proc_cell_vlservers_release(struct inode *inode,
 					   struct file *file)
 {
-	struct afs_cell *cell = PDE(inode)->data;
-	int ret;
+	return seq_release(inode, file);
+}
 
-	ret = seq_release(inode,file);
-
-	afs_put_cell(cell);
-
-	return ret;
-} /* end afs_proc_cell_vlservers_release() */
-
-/*****************************************************************************/
 /*
  * set up the iterator to start reading from the cells list and return the
  * first item
@@ -672,9 +645,8 @@
 		return NULL;
 
 	return &cell->vl_addrs[pos];
-} /* end afs_proc_cell_vlservers_start() */
+}
 
-/*****************************************************************************/
 /*
  * move to next cell in cells list
  */
@@ -692,9 +664,8 @@
 		return NULL;
 
 	return &cell->vl_addrs[pos];
-} /* end afs_proc_cell_vlservers_next() */
+}
 
-/*****************************************************************************/
 /*
  * clean up after reading from the cells list
  */
@@ -703,10 +674,8 @@
 	struct afs_cell *cell = p->private;
 
 	up_read(&cell->vl_sem);
+}
 
-} /* end afs_proc_cell_vlservers_stop() */
-
-/*****************************************************************************/
 /*
  * display a header line followed by a load of volume lines
  */
@@ -722,11 +691,9 @@
 
 	/* display one cell per line on subsequent lines */
 	seq_printf(m, "%u.%u.%u.%u\n", NIPQUAD(addr->s_addr));
-
 	return 0;
-} /* end afs_proc_cell_vlservers_show() */
+}
 
-/*****************************************************************************/
 /*
  * open "/proc/fs/afs/<cell>/servers" which provides a summary of active
  * servers
@@ -737,7 +704,7 @@
 	struct seq_file *m;
 	int ret;
 
-	cell = afs_get_cell_maybe((struct afs_cell **) &PDE(inode)->data);
+	cell = PDE(inode)->data;
 	if (!cell)
 		return -ENOENT;
 
@@ -747,34 +714,24 @@
 
 	m = file->private_data;
 	m->private = cell;
-
 	return 0;
-} /* end afs_proc_cell_servers_open() */
+}
 
-/*****************************************************************************/
 /*
  * close the file and release the ref to the cell
  */
 static int afs_proc_cell_servers_release(struct inode *inode,
 					 struct file *file)
 {
-	struct afs_cell *cell = PDE(inode)->data;
-	int ret;
+	return seq_release(inode, file);
+}
 
-	ret = seq_release(inode, file);
-
-	afs_put_cell(cell);
-
-	return ret;
-} /* end afs_proc_cell_servers_release() */
-
-/*****************************************************************************/
 /*
  * set up the iterator to start reading from the cells list and return the
  * first item
  */
 static void *afs_proc_cell_servers_start(struct seq_file *m, loff_t *_pos)
-	__acquires(m->private->sv_lock)
+	__acquires(m->private->servers_lock)
 {
 	struct list_head *_p;
 	struct afs_cell *cell = m->private;
@@ -783,7 +740,7 @@
 	_enter("cell=%p pos=%Ld", cell, *_pos);
 
 	/* lock the list against modification */
-	read_lock(&cell->sv_lock);
+	read_lock(&cell->servers_lock);
 
 	/* allow for the header line */
 	if (!pos)
@@ -791,14 +748,13 @@
 	pos--;
 
 	/* find the n'th element in the list */
-	list_for_each(_p, &cell->sv_list)
+	list_for_each(_p, &cell->servers)
 		if (!pos--)
 			break;
 
-	return _p != &cell->sv_list ? _p : NULL;
-} /* end afs_proc_cell_servers_start() */
+	return _p != &cell->servers ? _p : NULL;
+}
 
-/*****************************************************************************/
 /*
  * move to next cell in cells list
  */
@@ -813,25 +769,22 @@
 	(*_pos)++;
 
 	_p = v;
-	_p = v == (void *) 1 ? cell->sv_list.next : _p->next;
+	_p = v == (void *) 1 ? cell->servers.next : _p->next;
 
-	return _p != &cell->sv_list ? _p : NULL;
-} /* end afs_proc_cell_servers_next() */
+	return _p != &cell->servers ? _p : NULL;
+}
 
-/*****************************************************************************/
 /*
  * clean up after reading from the cells list
  */
 static void afs_proc_cell_servers_stop(struct seq_file *p, void *v)
-	__releases(p->private->sv_lock)
+	__releases(p->private->servers_lock)
 {
 	struct afs_cell *cell = p->private;
 
-	read_unlock(&cell->sv_lock);
+	read_unlock(&cell->servers_lock);
+}
 
-} /* end afs_proc_cell_servers_stop() */
-
-/*****************************************************************************/
 /*
  * display a header line followed by a load of volume lines
  */
@@ -849,10 +802,7 @@
 	/* display one cell per line on subsequent lines */
 	sprintf(ipaddr, "%u.%u.%u.%u", NIPQUAD(server->addr));
 	seq_printf(m, "%3d %-15.15s %5d\n",
-		   atomic_read(&server->usage),
-		   ipaddr,
-		   server->fs_state
-		   );
+		   atomic_read(&server->usage), ipaddr, server->fs_state);
 
 	return 0;
-} /* end afs_proc_cell_servers_show() */
+}
diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c
new file mode 100644
index 0000000..222c1a3
--- /dev/null
+++ b/fs/afs/rxrpc.c
@@ -0,0 +1,782 @@
+/* Maintain an RxRPC server socket to do AFS communications through
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#include <net/sock.h>
+#include <net/af_rxrpc.h>
+#include <rxrpc/packet.h>
+#include "internal.h"
+#include "afs_cm.h"
+
+static struct socket *afs_socket; /* my RxRPC socket */
+static struct workqueue_struct *afs_async_calls;
+static atomic_t afs_outstanding_calls;
+static atomic_t afs_outstanding_skbs;
+
+static void afs_wake_up_call_waiter(struct afs_call *);
+static int afs_wait_for_call_to_complete(struct afs_call *);
+static void afs_wake_up_async_call(struct afs_call *);
+static int afs_dont_wait_for_call_to_complete(struct afs_call *);
+static void afs_process_async_call(struct work_struct *);
+static void afs_rx_interceptor(struct sock *, unsigned long, struct sk_buff *);
+static int afs_deliver_cm_op_id(struct afs_call *, struct sk_buff *, bool);
+
+/* synchronous call management */
+const struct afs_wait_mode afs_sync_call = {
+	.rx_wakeup	= afs_wake_up_call_waiter,
+	.wait		= afs_wait_for_call_to_complete,
+};
+
+/* asynchronous call management */
+const struct afs_wait_mode afs_async_call = {
+	.rx_wakeup	= afs_wake_up_async_call,
+	.wait		= afs_dont_wait_for_call_to_complete,
+};
+
+/* asynchronous incoming call management */
+static const struct afs_wait_mode afs_async_incoming_call = {
+	.rx_wakeup	= afs_wake_up_async_call,
+};
+
+/* asynchronous incoming call initial processing */
+static const struct afs_call_type afs_RXCMxxxx = {
+	.name		= "CB.xxxx",
+	.deliver	= afs_deliver_cm_op_id,
+	.abort_to_error	= afs_abort_to_error,
+};
+
+static void afs_collect_incoming_call(struct work_struct *);
+
+static struct sk_buff_head afs_incoming_calls;
+static DECLARE_WORK(afs_collect_incoming_call_work, afs_collect_incoming_call);
+
+/*
+ * open an RxRPC socket and bind it to be a server for callback notifications
+ * - the socket is left in blocking mode and non-blocking ops use MSG_DONTWAIT
+ */
+int afs_open_socket(void)
+{
+	struct sockaddr_rxrpc srx;
+	struct socket *socket;
+	int ret;
+
+	_enter("");
+
+	skb_queue_head_init(&afs_incoming_calls);
+
+	afs_async_calls = create_singlethread_workqueue("kafsd");
+	if (!afs_async_calls) {
+		_leave(" = -ENOMEM [wq]");
+		return -ENOMEM;
+	}
+
+	ret = sock_create_kern(AF_RXRPC, SOCK_DGRAM, PF_INET, &socket);
+	if (ret < 0) {
+		destroy_workqueue(afs_async_calls);
+		_leave(" = %d [socket]", ret);
+		return ret;
+	}
+
+	socket->sk->sk_allocation = GFP_NOFS;
+
+	/* bind the callback manager's address to make this a server socket */
+	srx.srx_family			= AF_RXRPC;
+	srx.srx_service			= CM_SERVICE;
+	srx.transport_type		= SOCK_DGRAM;
+	srx.transport_len		= sizeof(srx.transport.sin);
+	srx.transport.sin.sin_family	= AF_INET;
+	srx.transport.sin.sin_port	= htons(AFS_CM_PORT);
+	memset(&srx.transport.sin.sin_addr, 0,
+	       sizeof(srx.transport.sin.sin_addr));
+
+	ret = kernel_bind(socket, (struct sockaddr *) &srx, sizeof(srx));
+	if (ret < 0) {
+		sock_release(socket);
+		_leave(" = %d [bind]", ret);
+		return ret;
+	}
+
+	rxrpc_kernel_intercept_rx_messages(socket, afs_rx_interceptor);
+
+	afs_socket = socket;
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * close the RxRPC socket AFS was using
+ */
+void afs_close_socket(void)
+{
+	_enter("");
+
+	sock_release(afs_socket);
+
+	_debug("dework");
+	destroy_workqueue(afs_async_calls);
+
+	ASSERTCMP(atomic_read(&afs_outstanding_skbs), ==, 0);
+	ASSERTCMP(atomic_read(&afs_outstanding_calls), ==, 0);
+	_leave("");
+}
+
+/*
+ * note that the data in a socket buffer is now delivered and that the buffer
+ * should be freed
+ */
+static void afs_data_delivered(struct sk_buff *skb)
+{
+	if (!skb) {
+		_debug("DLVR NULL [%d]", atomic_read(&afs_outstanding_skbs));
+		dump_stack();
+	} else {
+		_debug("DLVR %p{%u} [%d]",
+		       skb, skb->mark, atomic_read(&afs_outstanding_skbs));
+		if (atomic_dec_return(&afs_outstanding_skbs) == -1)
+			BUG();
+		rxrpc_kernel_data_delivered(skb);
+	}
+}
+
+/*
+ * free a socket buffer
+ */
+static void afs_free_skb(struct sk_buff *skb)
+{
+	if (!skb) {
+		_debug("FREE NULL [%d]", atomic_read(&afs_outstanding_skbs));
+		dump_stack();
+	} else {
+		_debug("FREE %p{%u} [%d]",
+		       skb, skb->mark, atomic_read(&afs_outstanding_skbs));
+		if (atomic_dec_return(&afs_outstanding_skbs) == -1)
+			BUG();
+		rxrpc_kernel_free_skb(skb);
+	}
+}
+
+/*
+ * free a call
+ */
+static void afs_free_call(struct afs_call *call)
+{
+	_debug("DONE %p{%s} [%d]",
+	       call, call->type->name, atomic_read(&afs_outstanding_calls));
+	if (atomic_dec_return(&afs_outstanding_calls) == -1)
+		BUG();
+
+	ASSERTCMP(call->rxcall, ==, NULL);
+	ASSERT(!work_pending(&call->async_work));
+	ASSERT(skb_queue_empty(&call->rx_queue));
+	ASSERT(call->type->name != NULL);
+
+	kfree(call->request);
+	kfree(call);
+}
+
+/*
+ * allocate a call with flat request and reply buffers
+ */
+struct afs_call *afs_alloc_flat_call(const struct afs_call_type *type,
+				     size_t request_size, size_t reply_size)
+{
+	struct afs_call *call;
+
+	call = kzalloc(sizeof(*call), GFP_NOFS);
+	if (!call)
+		goto nomem_call;
+
+	_debug("CALL %p{%s} [%d]",
+	       call, type->name, atomic_read(&afs_outstanding_calls));
+	atomic_inc(&afs_outstanding_calls);
+
+	call->type = type;
+	call->request_size = request_size;
+	call->reply_max = reply_size;
+
+	if (request_size) {
+		call->request = kmalloc(request_size, GFP_NOFS);
+		if (!call->request)
+			goto nomem_free;
+	}
+
+	if (reply_size) {
+		call->buffer = kmalloc(reply_size, GFP_NOFS);
+		if (!call->buffer)
+			goto nomem_free;
+	}
+
+	init_waitqueue_head(&call->waitq);
+	skb_queue_head_init(&call->rx_queue);
+	return call;
+
+nomem_free:
+	afs_free_call(call);
+nomem_call:
+	return NULL;
+}
+
+/*
+ * clean up a call with flat buffer
+ */
+void afs_flat_call_destructor(struct afs_call *call)
+{
+	_enter("");
+
+	kfree(call->request);
+	call->request = NULL;
+	kfree(call->buffer);
+	call->buffer = NULL;
+}
+
+/*
+ * initiate a call
+ */
+int afs_make_call(struct in_addr *addr, struct afs_call *call, gfp_t gfp,
+		  const struct afs_wait_mode *wait_mode)
+{
+	struct sockaddr_rxrpc srx;
+	struct rxrpc_call *rxcall;
+	struct msghdr msg;
+	struct kvec iov[1];
+	int ret;
+
+	_enter("%x,{%d},", addr->s_addr, ntohs(call->port));
+
+	ASSERT(call->type != NULL);
+	ASSERT(call->type->name != NULL);
+
+	_debug("MAKE %p{%s} [%d]",
+	       call, call->type->name, atomic_read(&afs_outstanding_calls));
+
+	call->wait_mode = wait_mode;
+	INIT_WORK(&call->async_work, afs_process_async_call);
+
+	memset(&srx, 0, sizeof(srx));
+	srx.srx_family = AF_RXRPC;
+	srx.srx_service = call->service_id;
+	srx.transport_type = SOCK_DGRAM;
+	srx.transport_len = sizeof(srx.transport.sin);
+	srx.transport.sin.sin_family = AF_INET;
+	srx.transport.sin.sin_port = call->port;
+	memcpy(&srx.transport.sin.sin_addr, addr, 4);
+
+	/* create a call */
+	rxcall = rxrpc_kernel_begin_call(afs_socket, &srx, call->key,
+					 (unsigned long) call, gfp);
+	call->key = NULL;
+	if (IS_ERR(rxcall)) {
+		ret = PTR_ERR(rxcall);
+		goto error_kill_call;
+	}
+
+	call->rxcall = rxcall;
+
+	/* send the request */
+	iov[0].iov_base	= call->request;
+	iov[0].iov_len	= call->request_size;
+
+	msg.msg_name		= NULL;
+	msg.msg_namelen		= 0;
+	msg.msg_iov		= (struct iovec *) iov;
+	msg.msg_iovlen		= 1;
+	msg.msg_control		= NULL;
+	msg.msg_controllen	= 0;
+	msg.msg_flags		= 0;
+
+	/* have to change the state *before* sending the last packet as RxRPC
+	 * might give us the reply before it returns from sending the
+	 * request */
+	call->state = AFS_CALL_AWAIT_REPLY;
+	ret = rxrpc_kernel_send_data(rxcall, &msg, call->request_size);
+	if (ret < 0)
+		goto error_do_abort;
+
+	/* at this point, an async call may no longer exist as it may have
+	 * already completed */
+	return wait_mode->wait(call);
+
+error_do_abort:
+	rxrpc_kernel_abort_call(rxcall, RX_USER_ABORT);
+	rxrpc_kernel_end_call(rxcall);
+	call->rxcall = NULL;
+error_kill_call:
+	call->type->destructor(call);
+	afs_free_call(call);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * handles intercepted messages that were arriving in the socket's Rx queue
+ * - called with the socket receive queue lock held to ensure message ordering
+ * - called with softirqs disabled
+ */
+static void afs_rx_interceptor(struct sock *sk, unsigned long user_call_ID,
+			       struct sk_buff *skb)
+{
+	struct afs_call *call = (struct afs_call *) user_call_ID;
+
+	_enter("%p,,%u", call, skb->mark);
+
+	_debug("ICPT %p{%u} [%d]",
+	       skb, skb->mark, atomic_read(&afs_outstanding_skbs));
+
+	ASSERTCMP(sk, ==, afs_socket->sk);
+	atomic_inc(&afs_outstanding_skbs);
+
+	if (!call) {
+		/* its an incoming call for our callback service */
+		skb_queue_tail(&afs_incoming_calls, skb);
+		schedule_work(&afs_collect_incoming_call_work);
+	} else {
+		/* route the messages directly to the appropriate call */
+		skb_queue_tail(&call->rx_queue, skb);
+		call->wait_mode->rx_wakeup(call);
+	}
+
+	_leave("");
+}
+
+/*
+ * deliver messages to a call
+ */
+static void afs_deliver_to_call(struct afs_call *call)
+{
+	struct sk_buff *skb;
+	bool last;
+	u32 abort_code;
+	int ret;
+
+	_enter("");
+
+	while ((call->state == AFS_CALL_AWAIT_REPLY ||
+		call->state == AFS_CALL_AWAIT_OP_ID ||
+		call->state == AFS_CALL_AWAIT_REQUEST ||
+		call->state == AFS_CALL_AWAIT_ACK) &&
+	       (skb = skb_dequeue(&call->rx_queue))) {
+		switch (skb->mark) {
+		case RXRPC_SKB_MARK_DATA:
+			_debug("Rcv DATA");
+			last = rxrpc_kernel_is_data_last(skb);
+			ret = call->type->deliver(call, skb, last);
+			switch (ret) {
+			case 0:
+				if (last &&
+				    call->state == AFS_CALL_AWAIT_REPLY)
+					call->state = AFS_CALL_COMPLETE;
+				break;
+			case -ENOTCONN:
+				abort_code = RX_CALL_DEAD;
+				goto do_abort;
+			case -ENOTSUPP:
+				abort_code = RX_INVALID_OPERATION;
+				goto do_abort;
+			default:
+				abort_code = RXGEN_CC_UNMARSHAL;
+				if (call->state != AFS_CALL_AWAIT_REPLY)
+					abort_code = RXGEN_SS_UNMARSHAL;
+			do_abort:
+				rxrpc_kernel_abort_call(call->rxcall,
+							abort_code);
+				call->error = ret;
+				call->state = AFS_CALL_ERROR;
+				break;
+			}
+			afs_data_delivered(skb);
+			skb = NULL;
+			continue;
+		case RXRPC_SKB_MARK_FINAL_ACK:
+			_debug("Rcv ACK");
+			call->state = AFS_CALL_COMPLETE;
+			break;
+		case RXRPC_SKB_MARK_BUSY:
+			_debug("Rcv BUSY");
+			call->error = -EBUSY;
+			call->state = AFS_CALL_BUSY;
+			break;
+		case RXRPC_SKB_MARK_REMOTE_ABORT:
+			abort_code = rxrpc_kernel_get_abort_code(skb);
+			call->error = call->type->abort_to_error(abort_code);
+			call->state = AFS_CALL_ABORTED;
+			_debug("Rcv ABORT %u -> %d", abort_code, call->error);
+			break;
+		case RXRPC_SKB_MARK_NET_ERROR:
+			call->error = -rxrpc_kernel_get_error_number(skb);
+			call->state = AFS_CALL_ERROR;
+			_debug("Rcv NET ERROR %d", call->error);
+			break;
+		case RXRPC_SKB_MARK_LOCAL_ERROR:
+			call->error = -rxrpc_kernel_get_error_number(skb);
+			call->state = AFS_CALL_ERROR;
+			_debug("Rcv LOCAL ERROR %d", call->error);
+			break;
+		default:
+			BUG();
+			break;
+		}
+
+		afs_free_skb(skb);
+	}
+
+	/* make sure the queue is empty if the call is done with (we might have
+	 * aborted the call early because of an unmarshalling error) */
+	if (call->state >= AFS_CALL_COMPLETE) {
+		while ((skb = skb_dequeue(&call->rx_queue)))
+			afs_free_skb(skb);
+		if (call->incoming) {
+			rxrpc_kernel_end_call(call->rxcall);
+			call->rxcall = NULL;
+			call->type->destructor(call);
+			afs_free_call(call);
+		}
+	}
+
+	_leave("");
+}
+
+/*
+ * wait synchronously for a call to complete
+ */
+static int afs_wait_for_call_to_complete(struct afs_call *call)
+{
+	struct sk_buff *skb;
+	int ret;
+
+	DECLARE_WAITQUEUE(myself, current);
+
+	_enter("");
+
+	add_wait_queue(&call->waitq, &myself);
+	for (;;) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		/* deliver any messages that are in the queue */
+		if (!skb_queue_empty(&call->rx_queue)) {
+			__set_current_state(TASK_RUNNING);
+			afs_deliver_to_call(call);
+			continue;
+		}
+
+		ret = call->error;
+		if (call->state >= AFS_CALL_COMPLETE)
+			break;
+		ret = -EINTR;
+		if (signal_pending(current))
+			break;
+		schedule();
+	}
+
+	remove_wait_queue(&call->waitq, &myself);
+	__set_current_state(TASK_RUNNING);
+
+	/* kill the call */
+	if (call->state < AFS_CALL_COMPLETE) {
+		_debug("call incomplete");
+		rxrpc_kernel_abort_call(call->rxcall, RX_CALL_DEAD);
+		while ((skb = skb_dequeue(&call->rx_queue)))
+			afs_free_skb(skb);
+	}
+
+	_debug("call complete");
+	rxrpc_kernel_end_call(call->rxcall);
+	call->rxcall = NULL;
+	call->type->destructor(call);
+	afs_free_call(call);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * wake up a waiting call
+ */
+static void afs_wake_up_call_waiter(struct afs_call *call)
+{
+	wake_up(&call->waitq);
+}
+
+/*
+ * wake up an asynchronous call
+ */
+static void afs_wake_up_async_call(struct afs_call *call)
+{
+	_enter("");
+	queue_work(afs_async_calls, &call->async_work);
+}
+
+/*
+ * put a call into asynchronous mode
+ * - mustn't touch the call descriptor as the call my have completed by the
+ *   time we get here
+ */
+static int afs_dont_wait_for_call_to_complete(struct afs_call *call)
+{
+	_enter("");
+	return -EINPROGRESS;
+}
+
+/*
+ * delete an asynchronous call
+ */
+static void afs_delete_async_call(struct work_struct *work)
+{
+	struct afs_call *call =
+		container_of(work, struct afs_call, async_work);
+
+	_enter("");
+
+	afs_free_call(call);
+
+	_leave("");
+}
+
+/*
+ * perform processing on an asynchronous call
+ * - on a multiple-thread workqueue this work item may try to run on several
+ *   CPUs at the same time
+ */
+static void afs_process_async_call(struct work_struct *work)
+{
+	struct afs_call *call =
+		container_of(work, struct afs_call, async_work);
+
+	_enter("");
+
+	if (!skb_queue_empty(&call->rx_queue))
+		afs_deliver_to_call(call);
+
+	if (call->state >= AFS_CALL_COMPLETE && call->wait_mode) {
+		if (call->wait_mode->async_complete)
+			call->wait_mode->async_complete(call->reply,
+							call->error);
+		call->reply = NULL;
+
+		/* kill the call */
+		rxrpc_kernel_end_call(call->rxcall);
+		call->rxcall = NULL;
+		if (call->type->destructor)
+			call->type->destructor(call);
+
+		/* we can't just delete the call because the work item may be
+		 * queued */
+		PREPARE_WORK(&call->async_work, afs_delete_async_call);
+		queue_work(afs_async_calls, &call->async_work);
+	}
+
+	_leave("");
+}
+
+/*
+ * empty a socket buffer into a flat reply buffer
+ */
+void afs_transfer_reply(struct afs_call *call, struct sk_buff *skb)
+{
+	size_t len = skb->len;
+
+	if (skb_copy_bits(skb, 0, call->buffer + call->reply_size, len) < 0)
+		BUG();
+	call->reply_size += len;
+}
+
+/*
+ * accept the backlog of incoming calls
+ */
+static void afs_collect_incoming_call(struct work_struct *work)
+{
+	struct rxrpc_call *rxcall;
+	struct afs_call *call = NULL;
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&afs_incoming_calls))) {
+		_debug("new call");
+
+		/* don't need the notification */
+		afs_free_skb(skb);
+
+		if (!call) {
+			call = kzalloc(sizeof(struct afs_call), GFP_KERNEL);
+			if (!call) {
+				rxrpc_kernel_reject_call(afs_socket);
+				return;
+			}
+
+			INIT_WORK(&call->async_work, afs_process_async_call);
+			call->wait_mode = &afs_async_incoming_call;
+			call->type = &afs_RXCMxxxx;
+			init_waitqueue_head(&call->waitq);
+			skb_queue_head_init(&call->rx_queue);
+			call->state = AFS_CALL_AWAIT_OP_ID;
+
+			_debug("CALL %p{%s} [%d]",
+			       call, call->type->name,
+			       atomic_read(&afs_outstanding_calls));
+			atomic_inc(&afs_outstanding_calls);
+		}
+
+		rxcall = rxrpc_kernel_accept_call(afs_socket,
+						  (unsigned long) call);
+		if (!IS_ERR(rxcall)) {
+			call->rxcall = rxcall;
+			call = NULL;
+		}
+	}
+
+	if (call)
+		afs_free_call(call);
+}
+
+/*
+ * grab the operation ID from an incoming cache manager call
+ */
+static int afs_deliver_cm_op_id(struct afs_call *call, struct sk_buff *skb,
+				bool last)
+{
+	size_t len = skb->len;
+	void *oibuf = (void *) &call->operation_ID;
+
+	_enter("{%u},{%zu},%d", call->offset, len, last);
+
+	ASSERTCMP(call->offset, <, 4);
+
+	/* the operation ID forms the first four bytes of the request data */
+	len = min_t(size_t, len, 4 - call->offset);
+	if (skb_copy_bits(skb, 0, oibuf + call->offset, len) < 0)
+		BUG();
+	if (!pskb_pull(skb, len))
+		BUG();
+	call->offset += len;
+
+	if (call->offset < 4) {
+		if (last) {
+			_leave(" = -EBADMSG [op ID short]");
+			return -EBADMSG;
+		}
+		_leave(" = 0 [incomplete]");
+		return 0;
+	}
+
+	call->state = AFS_CALL_AWAIT_REQUEST;
+
+	/* ask the cache manager to route the call (it'll change the call type
+	 * if successful) */
+	if (!afs_cm_incoming_call(call))
+		return -ENOTSUPP;
+
+	/* pass responsibility for the remainer of this message off to the
+	 * cache manager op */
+	return call->type->deliver(call, skb, last);
+}
+
+/*
+ * send an empty reply
+ */
+void afs_send_empty_reply(struct afs_call *call)
+{
+	struct msghdr msg;
+	struct iovec iov[1];
+
+	_enter("");
+
+	iov[0].iov_base		= NULL;
+	iov[0].iov_len		= 0;
+	msg.msg_name		= NULL;
+	msg.msg_namelen		= 0;
+	msg.msg_iov		= iov;
+	msg.msg_iovlen		= 0;
+	msg.msg_control		= NULL;
+	msg.msg_controllen	= 0;
+	msg.msg_flags		= 0;
+
+	call->state = AFS_CALL_AWAIT_ACK;
+	switch (rxrpc_kernel_send_data(call->rxcall, &msg, 0)) {
+	case 0:
+		_leave(" [replied]");
+		return;
+
+	case -ENOMEM:
+		_debug("oom");
+		rxrpc_kernel_abort_call(call->rxcall, RX_USER_ABORT);
+	default:
+		rxrpc_kernel_end_call(call->rxcall);
+		call->rxcall = NULL;
+		call->type->destructor(call);
+		afs_free_call(call);
+		_leave(" [error]");
+		return;
+	}
+}
+
+/*
+ * send a simple reply
+ */
+void afs_send_simple_reply(struct afs_call *call, const void *buf, size_t len)
+{
+	struct msghdr msg;
+	struct iovec iov[1];
+
+	_enter("");
+
+	iov[0].iov_base		= (void *) buf;
+	iov[0].iov_len		= len;
+	msg.msg_name		= NULL;
+	msg.msg_namelen		= 0;
+	msg.msg_iov		= iov;
+	msg.msg_iovlen		= 1;
+	msg.msg_control		= NULL;
+	msg.msg_controllen	= 0;
+	msg.msg_flags		= 0;
+
+	call->state = AFS_CALL_AWAIT_ACK;
+	switch (rxrpc_kernel_send_data(call->rxcall, &msg, len)) {
+	case 0:
+		_leave(" [replied]");
+		return;
+
+	case -ENOMEM:
+		_debug("oom");
+		rxrpc_kernel_abort_call(call->rxcall, RX_USER_ABORT);
+	default:
+		rxrpc_kernel_end_call(call->rxcall);
+		call->rxcall = NULL;
+		call->type->destructor(call);
+		afs_free_call(call);
+		_leave(" [error]");
+		return;
+	}
+}
+
+/*
+ * extract a piece of data from the received data socket buffers
+ */
+int afs_extract_data(struct afs_call *call, struct sk_buff *skb,
+		     bool last, void *buf, size_t count)
+{
+	size_t len = skb->len;
+
+	_enter("{%u},{%zu},%d,,%zu", call->offset, len, last, count);
+
+	ASSERTCMP(call->offset, <, count);
+
+	len = min_t(size_t, len, count - call->offset);
+	if (skb_copy_bits(skb, 0, buf + call->offset, len) < 0 ||
+	    !pskb_pull(skb, len))
+		BUG();
+	call->offset += len;
+
+	if (call->offset < count) {
+		if (last) {
+			_leave(" = -EBADMSG [%d < %zu]", call->offset, count);
+			return -EBADMSG;
+		}
+		_leave(" = -EAGAIN");
+		return -EAGAIN;
+	}
+	return 0;
+}
diff --git a/fs/afs/security.c b/fs/afs/security.c
new file mode 100644
index 0000000..f9f424d
--- /dev/null
+++ b/fs/afs/security.c
@@ -0,0 +1,356 @@
+/* AFS security handling
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/ctype.h>
+#include <keys/rxrpc-type.h>
+#include "internal.h"
+
+/*
+ * get a key
+ */
+struct key *afs_request_key(struct afs_cell *cell)
+{
+	struct key *key;
+
+	_enter("{%x}", key_serial(cell->anonymous_key));
+
+	_debug("key %s", cell->anonymous_key->description);
+	key = request_key(&key_type_rxrpc, cell->anonymous_key->description,
+			  NULL);
+	if (IS_ERR(key)) {
+		if (PTR_ERR(key) != -ENOKEY) {
+			_leave(" = %ld", PTR_ERR(key));
+			return key;
+		}
+
+		/* act as anonymous user */
+		_leave(" = {%x} [anon]", key_serial(cell->anonymous_key));
+		return key_get(cell->anonymous_key);
+	} else {
+		/* act as authorised user */
+		_leave(" = {%x} [auth]", key_serial(key));
+		return key;
+	}
+}
+
+/*
+ * dispose of a permits list
+ */
+void afs_zap_permits(struct rcu_head *rcu)
+{
+	struct afs_permits *permits =
+		container_of(rcu, struct afs_permits, rcu);
+	int loop;
+
+	_enter("{%d}", permits->count);
+
+	for (loop = permits->count - 1; loop >= 0; loop--)
+		key_put(permits->permits[loop].key);
+	kfree(permits);
+}
+
+/*
+ * dispose of a permits list in which all the key pointers have been copied
+ */
+static void afs_dispose_of_permits(struct rcu_head *rcu)
+{
+	struct afs_permits *permits =
+		container_of(rcu, struct afs_permits, rcu);
+
+	_enter("{%d}", permits->count);
+
+	kfree(permits);
+}
+
+/*
+ * get the authorising vnode - this is the specified inode itself if it's a
+ * directory or it's the parent directory if the specified inode is a file or
+ * symlink
+ * - the caller must release the ref on the inode
+ */
+static struct afs_vnode *afs_get_auth_inode(struct afs_vnode *vnode,
+					    struct key *key)
+{
+	struct afs_vnode *auth_vnode;
+	struct inode *auth_inode;
+
+	_enter("");
+
+	if (S_ISDIR(vnode->vfs_inode.i_mode)) {
+		auth_inode = igrab(&vnode->vfs_inode);
+		ASSERT(auth_inode != NULL);
+	} else {
+		auth_inode = afs_iget(vnode->vfs_inode.i_sb, key,
+				      &vnode->status.parent, NULL, NULL);
+		if (IS_ERR(auth_inode))
+			return ERR_PTR(PTR_ERR(auth_inode));
+	}
+
+	auth_vnode = AFS_FS_I(auth_inode);
+	_leave(" = {%x}", auth_vnode->fid.vnode);
+	return auth_vnode;
+}
+
+/*
+ * clear the permit cache on a directory vnode
+ */
+void afs_clear_permits(struct afs_vnode *vnode)
+{
+	struct afs_permits *permits;
+
+	_enter("{%x}", vnode->fid.vnode);
+
+	mutex_lock(&vnode->permits_lock);
+	permits = vnode->permits;
+	rcu_assign_pointer(vnode->permits, NULL);
+	mutex_unlock(&vnode->permits_lock);
+
+	if (permits)
+		call_rcu(&permits->rcu, afs_zap_permits);
+	_leave("");
+}
+
+/*
+ * add the result obtained for a vnode to its or its parent directory's cache
+ * for the key used to access it
+ */
+void afs_cache_permit(struct afs_vnode *vnode, struct key *key, long acl_order)
+{
+	struct afs_permits *permits, *xpermits;
+	struct afs_permit *permit;
+	struct afs_vnode *auth_vnode;
+	int count, loop;
+
+	_enter("{%x},%x,%lx", vnode->fid.vnode, key_serial(key), acl_order);
+
+	auth_vnode = afs_get_auth_inode(vnode, key);
+	if (IS_ERR(auth_vnode)) {
+		_leave(" [get error %ld]", PTR_ERR(auth_vnode));
+		return;
+	}
+
+	mutex_lock(&auth_vnode->permits_lock);
+
+	/* guard against a rename being detected whilst we waited for the
+	 * lock */
+	if (memcmp(&auth_vnode->fid, &vnode->status.parent,
+		   sizeof(struct afs_fid)) != 0) {
+		_debug("renamed");
+		goto out_unlock;
+	}
+
+	/* have to be careful as the directory's callback may be broken between
+	 * us receiving the status we're trying to cache and us getting the
+	 * lock to update the cache for the status */
+	if (auth_vnode->acl_order - acl_order > 0) {
+		_debug("ACL changed?");
+		goto out_unlock;
+	}
+
+	/* always update the anonymous mask */
+	_debug("anon access %x", vnode->status.anon_access);
+	auth_vnode->status.anon_access = vnode->status.anon_access;
+	if (key == vnode->volume->cell->anonymous_key)
+		goto out_unlock;
+
+	xpermits = auth_vnode->permits;
+	count = 0;
+	if (xpermits) {
+		/* see if the permit is already in the list
+		 * - if it is then we just amend the list
+		 */
+		count = xpermits->count;
+		permit = xpermits->permits;
+		for (loop = count; loop > 0; loop--) {
+			if (permit->key == key) {
+				permit->access_mask =
+					vnode->status.caller_access;
+				goto out_unlock;
+			}
+			permit++;
+		}
+	}
+
+	permits = kmalloc(sizeof(*permits) + sizeof(*permit) * (count + 1),
+			  GFP_NOFS);
+	if (!permits)
+		goto out_unlock;
+
+	memcpy(permits->permits, xpermits->permits,
+	       count * sizeof(struct afs_permit));
+
+	_debug("key %x access %x",
+	       key_serial(key), vnode->status.caller_access);
+	permits->permits[count].access_mask = vnode->status.caller_access;
+	permits->permits[count].key = key_get(key);
+	permits->count = count + 1;
+
+	rcu_assign_pointer(auth_vnode->permits, permits);
+	if (xpermits)
+		call_rcu(&xpermits->rcu, afs_dispose_of_permits);
+
+out_unlock:
+	mutex_unlock(&auth_vnode->permits_lock);
+	iput(&auth_vnode->vfs_inode);
+	_leave("");
+}
+
+/*
+ * check with the fileserver to see if the directory or parent directory is
+ * permitted to be accessed with this authorisation, and if so, what access it
+ * is granted
+ */
+static int afs_check_permit(struct afs_vnode *vnode, struct key *key,
+			    afs_access_t *_access)
+{
+	struct afs_permits *permits;
+	struct afs_permit *permit;
+	struct afs_vnode *auth_vnode;
+	bool valid;
+	int loop, ret;
+
+	_enter("");
+
+	auth_vnode = afs_get_auth_inode(vnode, key);
+	if (IS_ERR(auth_vnode)) {
+		*_access = 0;
+		_leave(" = %ld", PTR_ERR(auth_vnode));
+		return PTR_ERR(auth_vnode);
+	}
+
+	ASSERT(S_ISDIR(auth_vnode->vfs_inode.i_mode));
+
+	/* check the permits to see if we've got one yet */
+	if (key == auth_vnode->volume->cell->anonymous_key) {
+		_debug("anon");
+		*_access = auth_vnode->status.anon_access;
+		valid = true;
+	} else {
+		valid = false;
+		rcu_read_lock();
+		permits = rcu_dereference(auth_vnode->permits);
+		if (permits) {
+			permit = permits->permits;
+			for (loop = permits->count; loop > 0; loop--) {
+				if (permit->key == key) {
+					_debug("found in cache");
+					*_access = permit->access_mask;
+					valid = true;
+					break;
+				}
+				permit++;
+			}
+		}
+		rcu_read_unlock();
+	}
+
+	if (!valid) {
+		/* check the status on the file we're actually interested in
+		 * (the post-processing will cache the result on auth_vnode) */
+		_debug("no valid permit");
+
+		set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
+		ret = afs_vnode_fetch_status(vnode, auth_vnode, key);
+		if (ret < 0) {
+			iput(&auth_vnode->vfs_inode);
+			*_access = 0;
+			_leave(" = %d", ret);
+			return ret;
+		}
+	}
+
+	*_access = vnode->status.caller_access;
+	iput(&auth_vnode->vfs_inode);
+	_leave(" = 0 [access %x]", *_access);
+	return 0;
+}
+
+/*
+ * check the permissions on an AFS file
+ * - AFS ACLs are attached to directories only, and a file is controlled by its
+ *   parent directory's ACL
+ */
+int afs_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+	struct afs_vnode *vnode = AFS_FS_I(inode);
+	afs_access_t access;
+	struct key *key;
+	int ret;
+
+	_enter("{{%x:%x},%lx},%x,",
+	       vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask);
+
+	key = afs_request_key(vnode->volume->cell);
+	if (IS_ERR(key)) {
+		_leave(" = %ld [key]", PTR_ERR(key));
+		return PTR_ERR(key);
+	}
+
+	/* if the promise has expired, we need to check the server again */
+	if (!vnode->cb_promised) {
+		_debug("not promised");
+		ret = afs_vnode_fetch_status(vnode, NULL, key);
+		if (ret < 0)
+			goto error;
+		_debug("new promise [fl=%lx]", vnode->flags);
+	}
+
+	/* check the permits to see if we've got one yet */
+	ret = afs_check_permit(vnode, key, &access);
+	if (ret < 0)
+		goto error;
+
+	/* interpret the access mask */
+	_debug("REQ %x ACC %x on %s",
+	       mask, access, S_ISDIR(inode->i_mode) ? "dir" : "file");
+
+	if (S_ISDIR(inode->i_mode)) {
+		if (mask & MAY_EXEC) {
+			if (!(access & AFS_ACE_LOOKUP))
+				goto permission_denied;
+		} else if (mask & MAY_READ) {
+			if (!(access & AFS_ACE_READ))
+				goto permission_denied;
+		} else if (mask & MAY_WRITE) {
+			if (!(access & (AFS_ACE_DELETE | /* rmdir, unlink, rename from */
+					AFS_ACE_INSERT | /* create, mkdir, symlink, rename to */
+					AFS_ACE_WRITE))) /* chmod */
+				goto permission_denied;
+		} else {
+			BUG();
+		}
+	} else {
+		if (!(access & AFS_ACE_LOOKUP))
+			goto permission_denied;
+		if (mask & (MAY_EXEC | MAY_READ)) {
+			if (!(access & AFS_ACE_READ))
+				goto permission_denied;
+		} else if (mask & MAY_WRITE) {
+			if (!(access & AFS_ACE_WRITE))
+				goto permission_denied;
+		}
+	}
+
+	key_put(key);
+	ret = generic_permission(inode, mask, NULL);
+	_leave(" = %d", ret);
+	return ret;
+
+permission_denied:
+	ret = -EACCES;
+error:
+	key_put(key);
+	_leave(" = %d", ret);
+	return ret;
+}
diff --git a/fs/afs/server.c b/fs/afs/server.c
index 44aff81..96bb23b 100644
--- a/fs/afs/server.c
+++ b/fs/afs/server.c
@@ -1,6 +1,6 @@
-/* server.c: AFS server record management
+/* AFS server record management
  *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
@@ -11,489 +11,314 @@
 
 #include <linux/sched.h>
 #include <linux/slab.h>
-#include <rxrpc/peer.h>
-#include <rxrpc/connection.h>
-#include "volume.h"
-#include "cell.h"
-#include "server.h"
-#include "transport.h"
-#include "vlclient.h"
-#include "kafstimod.h"
 #include "internal.h"
 
-DEFINE_SPINLOCK(afs_server_peer_lock);
+unsigned afs_server_timeout = 10;	/* server timeout in seconds */
 
-#define FS_SERVICE_ID		1	/* AFS Volume Location Service ID */
-#define VL_SERVICE_ID		52	/* AFS Volume Location Service ID */
+static void afs_reap_server(struct work_struct *);
 
-static void __afs_server_timeout(struct afs_timer *timer)
+/* tree of all the servers, indexed by IP address */
+static struct rb_root afs_servers = RB_ROOT;
+static DEFINE_RWLOCK(afs_servers_lock);
+
+/* LRU list of all the servers not currently in use */
+static LIST_HEAD(afs_server_graveyard);
+static DEFINE_SPINLOCK(afs_server_graveyard_lock);
+static DECLARE_DELAYED_WORK(afs_server_reaper, afs_reap_server);
+
+/*
+ * install a server record in the master tree
+ */
+static int afs_install_server(struct afs_server *server)
 {
-	struct afs_server *server =
-		list_entry(timer, struct afs_server, timeout);
+	struct afs_server *xserver;
+	struct rb_node **pp, *p;
+	int ret;
 
-	_debug("SERVER TIMEOUT [%p{u=%d}]",
-	       server, atomic_read(&server->usage));
+	_enter("%p", server);
 
-	afs_server_do_timeout(server);
+	write_lock(&afs_servers_lock);
+
+	ret = -EEXIST;
+	pp = &afs_servers.rb_node;
+	p = NULL;
+	while (*pp) {
+		p = *pp;
+		_debug("- consider %p", p);
+		xserver = rb_entry(p, struct afs_server, master_rb);
+		if (server->addr.s_addr < xserver->addr.s_addr)
+			pp = &(*pp)->rb_left;
+		else if (server->addr.s_addr > xserver->addr.s_addr)
+			pp = &(*pp)->rb_right;
+		else
+			goto error;
+	}
+
+	rb_link_node(&server->master_rb, p, pp);
+	rb_insert_color(&server->master_rb, &afs_servers);
+	ret = 0;
+
+error:
+	write_unlock(&afs_servers_lock);
+	return ret;
 }
 
-static const struct afs_timer_ops afs_server_timer_ops = {
-	.timed_out	= __afs_server_timeout,
-};
-
-/*****************************************************************************/
 /*
- * lookup a server record in a cell
- * - TODO: search the cell's server list
+ * allocate a new server record
  */
-int afs_server_lookup(struct afs_cell *cell, const struct in_addr *addr,
-		      struct afs_server **_server)
+static struct afs_server *afs_alloc_server(struct afs_cell *cell,
+					   const struct in_addr *addr)
 {
-	struct afs_server *server, *active, *zombie;
-	int loop;
+	struct afs_server *server;
 
-	_enter("%p,%08x,", cell, ntohl(addr->s_addr));
+	_enter("");
 
-	/* allocate and initialise a server record */
 	server = kzalloc(sizeof(struct afs_server), GFP_KERNEL);
-	if (!server) {
+	if (server) {
+		atomic_set(&server->usage, 1);
+		server->cell = cell;
+
+		INIT_LIST_HEAD(&server->link);
+		INIT_LIST_HEAD(&server->grave);
+		init_rwsem(&server->sem);
+		spin_lock_init(&server->fs_lock);
+		server->fs_vnodes = RB_ROOT;
+		server->cb_promises = RB_ROOT;
+		spin_lock_init(&server->cb_lock);
+		init_waitqueue_head(&server->cb_break_waitq);
+		INIT_DELAYED_WORK(&server->cb_break_work,
+				  afs_dispatch_give_up_callbacks);
+
+		memcpy(&server->addr, addr, sizeof(struct in_addr));
+		server->addr.s_addr = addr->s_addr;
+	}
+
+	_leave(" = %p{%d}", server, atomic_read(&server->usage));
+	return server;
+}
+
+/*
+ * get an FS-server record for a cell
+ */
+struct afs_server *afs_lookup_server(struct afs_cell *cell,
+				     const struct in_addr *addr)
+{
+	struct afs_server *server, *candidate;
+
+	_enter("%p,"NIPQUAD_FMT, cell, NIPQUAD(addr->s_addr));
+
+	/* quick scan of the list to see if we already have the server */
+	read_lock(&cell->servers_lock);
+
+	list_for_each_entry(server, &cell->servers, link) {
+		if (server->addr.s_addr == addr->s_addr)
+			goto found_server_quickly;
+	}
+	read_unlock(&cell->servers_lock);
+
+	candidate = afs_alloc_server(cell, addr);
+	if (!candidate) {
 		_leave(" = -ENOMEM");
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 	}
 
-	atomic_set(&server->usage, 1);
+	write_lock(&cell->servers_lock);
 
-	INIT_LIST_HEAD(&server->link);
-	init_rwsem(&server->sem);
-	INIT_LIST_HEAD(&server->fs_callq);
-	spin_lock_init(&server->fs_lock);
-	INIT_LIST_HEAD(&server->cb_promises);
-	spin_lock_init(&server->cb_lock);
-
-	for (loop = 0; loop < AFS_SERVER_CONN_LIST_SIZE; loop++)
-		server->fs_conn_cnt[loop] = 4;
-
-	memcpy(&server->addr, addr, sizeof(struct in_addr));
-	server->addr.s_addr = addr->s_addr;
-
-	afs_timer_init(&server->timeout, &afs_server_timer_ops);
-
-	/* add to the cell */
-	write_lock(&cell->sv_lock);
-
-	/* check the active list */
-	list_for_each_entry(active, &cell->sv_list, link) {
-		if (active->addr.s_addr == addr->s_addr)
-			goto use_active_server;
+	/* check the cell's server list again */
+	list_for_each_entry(server, &cell->servers, link) {
+		if (server->addr.s_addr == addr->s_addr)
+			goto found_server;
 	}
 
-	/* check the inactive list */
-	spin_lock(&cell->sv_gylock);
-	list_for_each_entry(zombie, &cell->sv_graveyard, link) {
-		if (zombie->addr.s_addr == addr->s_addr)
-			goto resurrect_server;
-	}
-	spin_unlock(&cell->sv_gylock);
+	_debug("new");
+	server = candidate;
+	if (afs_install_server(server) < 0)
+		goto server_in_two_cells;
 
 	afs_get_cell(cell);
-	server->cell = cell;
-	list_add_tail(&server->link, &cell->sv_list);
+	list_add_tail(&server->link, &cell->servers);
 
-	write_unlock(&cell->sv_lock);
+	write_unlock(&cell->servers_lock);
+	_leave(" = %p{%d}", server, atomic_read(&server->usage));
+	return server;
 
-	*_server = server;
-	_leave(" = 0 (%p)", server);
-	return 0;
+	/* found a matching server quickly */
+found_server_quickly:
+	_debug("found quickly");
+	afs_get_server(server);
+	read_unlock(&cell->servers_lock);
+no_longer_unused:
+	if (!list_empty(&server->grave)) {
+		spin_lock(&afs_server_graveyard_lock);
+		list_del_init(&server->grave);
+		spin_unlock(&afs_server_graveyard_lock);
+	}
+	_leave(" = %p{%d}", server, atomic_read(&server->usage));
+	return server;
 
-	/* found a matching active server */
- use_active_server:
-	_debug("active server");
-	afs_get_server(active);
-	write_unlock(&cell->sv_lock);
+	/* found a matching server on the second pass */
+found_server:
+	_debug("found");
+	afs_get_server(server);
+	write_unlock(&cell->servers_lock);
+	kfree(candidate);
+	goto no_longer_unused;
 
-	kfree(server);
+	/* found a server that seems to be in two cells */
+server_in_two_cells:
+	write_unlock(&cell->servers_lock);
+	kfree(candidate);
+	printk(KERN_NOTICE "kAFS:"
+	       " Server "NIPQUAD_FMT" appears to be in two cells\n",
+	       NIPQUAD(*addr));
+	_leave(" = -EEXIST");
+	return ERR_PTR(-EEXIST);
+}
 
-	*_server = active;
-	_leave(" = 0 (%p)", active);
-	return 0;
+/*
+ * look up a server by its IP address
+ */
+struct afs_server *afs_find_server(const struct in_addr *_addr)
+{
+	struct afs_server *server = NULL;
+	struct rb_node *p;
+	struct in_addr addr = *_addr;
 
-	/* found a matching server in the graveyard, so resurrect it and
-	 * dispose of the new record */
- resurrect_server:
-	_debug("resurrecting server");
+	_enter(NIPQUAD_FMT, NIPQUAD(addr.s_addr));
 
-	list_move_tail(&zombie->link, &cell->sv_list);
-	afs_get_server(zombie);
-	afs_kafstimod_del_timer(&zombie->timeout);
-	spin_unlock(&cell->sv_gylock);
-	write_unlock(&cell->sv_lock);
+	read_lock(&afs_servers_lock);
 
-	kfree(server);
+	p = afs_servers.rb_node;
+	while (p) {
+		server = rb_entry(p, struct afs_server, master_rb);
 
-	*_server = zombie;
-	_leave(" = 0 (%p)", zombie);
-	return 0;
+		_debug("- consider %p", p);
 
-} /* end afs_server_lookup() */
+		if (addr.s_addr < server->addr.s_addr) {
+			p = p->rb_left;
+		} else if (addr.s_addr > server->addr.s_addr) {
+			p = p->rb_right;
+		} else {
+			afs_get_server(server);
+			goto found;
+		}
+	}
 
-/*****************************************************************************/
+	server = NULL;
+found:
+	read_unlock(&afs_servers_lock);
+	ASSERTIFCMP(server, server->addr.s_addr, ==, addr.s_addr);
+	_leave(" = %p", server);
+	return server;
+}
+
 /*
  * destroy a server record
  * - removes from the cell list
  */
 void afs_put_server(struct afs_server *server)
 {
-	struct afs_cell *cell;
-
 	if (!server)
 		return;
 
-	_enter("%p", server);
+	_enter("%p{%d}", server, atomic_read(&server->usage));
 
-	cell = server->cell;
+	_debug("PUT SERVER %d", atomic_read(&server->usage));
 
-	/* sanity check */
-	BUG_ON(atomic_read(&server->usage) <= 0);
-
-	/* to prevent a race, the decrement and the dequeue must be effectively
-	 * atomic */
-	write_lock(&cell->sv_lock);
+	ASSERTCMP(atomic_read(&server->usage), >, 0);
 
 	if (likely(!atomic_dec_and_test(&server->usage))) {
-		write_unlock(&cell->sv_lock);
 		_leave("");
 		return;
 	}
 
-	spin_lock(&cell->sv_gylock);
-	list_move_tail(&server->link, &cell->sv_graveyard);
+	afs_flush_callback_breaks(server);
 
-	/* time out in 10 secs */
-	afs_kafstimod_add_timer(&server->timeout, 10 * HZ);
+	spin_lock(&afs_server_graveyard_lock);
+	if (atomic_read(&server->usage) == 0) {
+		list_move_tail(&server->grave, &afs_server_graveyard);
+		server->time_of_death = get_seconds();
+		schedule_delayed_work(&afs_server_reaper,
+				      afs_server_timeout * HZ);
+	}
+	spin_unlock(&afs_server_graveyard_lock);
+	_leave(" [dead]");
+}
 
-	spin_unlock(&cell->sv_gylock);
-	write_unlock(&cell->sv_lock);
-
-	_leave(" [killed]");
-} /* end afs_put_server() */
-
-/*****************************************************************************/
 /*
- * timeout server record
- * - removes from the cell's graveyard if the usage count is zero
+ * destroy a dead server
  */
-void afs_server_do_timeout(struct afs_server *server)
+static void afs_destroy_server(struct afs_server *server)
 {
-	struct rxrpc_peer *peer;
-	struct afs_cell *cell;
-	int loop;
-
 	_enter("%p", server);
 
-	cell = server->cell;
+	ASSERTCMP(server->fs_vnodes.rb_node, ==, NULL);
+	ASSERTCMP(server->cb_promises.rb_node, ==, NULL);
+	ASSERTCMP(server->cb_break_head, ==, server->cb_break_tail);
+	ASSERTCMP(atomic_read(&server->cb_break_n), ==, 0);
 
-	BUG_ON(atomic_read(&server->usage) < 0);
-
-	/* remove from graveyard if still dead */
-	spin_lock(&cell->vl_gylock);
-	if (atomic_read(&server->usage) == 0)
-		list_del_init(&server->link);
-	else
-		server = NULL;
-	spin_unlock(&cell->vl_gylock);
-
-	if (!server) {
-		_leave("");
-		return; /* resurrected */
-	}
-
-	/* we can now destroy it properly */
-	afs_put_cell(cell);
-
-	/* uncross-point the structs under a global lock */
-	spin_lock(&afs_server_peer_lock);
-	peer = server->peer;
-	if (peer) {
-		server->peer = NULL;
-		peer->user = NULL;
-	}
-	spin_unlock(&afs_server_peer_lock);
-
-	/* finish cleaning up the server */
-	for (loop = AFS_SERVER_CONN_LIST_SIZE - 1; loop >= 0; loop--)
-		if (server->fs_conn[loop])
-			rxrpc_put_connection(server->fs_conn[loop]);
-
-	if (server->vlserver)
-		rxrpc_put_connection(server->vlserver);
-
+	afs_put_cell(server->cell);
 	kfree(server);
+}
 
-	_leave(" [destroyed]");
-} /* end afs_server_do_timeout() */
-
-/*****************************************************************************/
 /*
- * get a callslot on a connection to the fileserver on the specified server
+ * reap dead server records
  */
-int afs_server_request_callslot(struct afs_server *server,
-				struct afs_server_callslot *callslot)
+static void afs_reap_server(struct work_struct *work)
 {
-	struct afs_server_callslot *pcallslot;
-	struct rxrpc_connection *conn;
-	int nconn, ret;
+	LIST_HEAD(corpses);
+	struct afs_server *server;
+	unsigned long delay, expiry;
+	time_t now;
 
-	_enter("%p,",server);
+	now = get_seconds();
+	spin_lock(&afs_server_graveyard_lock);
 
-	INIT_LIST_HEAD(&callslot->link);
-	callslot->task = current;
-	callslot->conn = NULL;
-	callslot->nconn = -1;
-	callslot->ready = 0;
+	while (!list_empty(&afs_server_graveyard)) {
+		server = list_entry(afs_server_graveyard.next,
+				    struct afs_server, grave);
 
-	ret = 0;
-	conn = NULL;
-
-	/* get hold of a callslot first */
-	spin_lock(&server->fs_lock);
-
-	/* resurrect the server if it's death timeout has expired */
-	if (server->fs_state) {
-		if (time_before(jiffies, server->fs_dead_jif)) {
-			ret = server->fs_state;
-			spin_unlock(&server->fs_lock);
-			_leave(" = %d [still dead]", ret);
-			return ret;
+		/* the queue is ordered most dead first */
+		expiry = server->time_of_death + afs_server_timeout;
+		if (expiry > now) {
+			delay = (expiry - now) * HZ;
+			if (!schedule_delayed_work(&afs_server_reaper, delay)) {
+				cancel_delayed_work(&afs_server_reaper);
+				schedule_delayed_work(&afs_server_reaper,
+						      delay);
+			}
+			break;
 		}
 
-		server->fs_state = 0;
-	}
-
-	/* try and find a connection that has spare callslots */
-	for (nconn = 0; nconn < AFS_SERVER_CONN_LIST_SIZE; nconn++) {
-		if (server->fs_conn_cnt[nconn] > 0) {
-			server->fs_conn_cnt[nconn]--;
-			spin_unlock(&server->fs_lock);
-			callslot->nconn = nconn;
-			goto obtained_slot;
+		write_lock(&server->cell->servers_lock);
+		write_lock(&afs_servers_lock);
+		if (atomic_read(&server->usage) > 0) {
+			list_del_init(&server->grave);
+		} else {
+			list_move_tail(&server->grave, &corpses);
+			list_del_init(&server->link);
+			rb_erase(&server->master_rb, &afs_servers);
 		}
+		write_unlock(&afs_servers_lock);
+		write_unlock(&server->cell->servers_lock);
 	}
 
-	/* none were available - wait interruptibly for one to become
-	 * available */
-	set_current_state(TASK_INTERRUPTIBLE);
-	list_add_tail(&callslot->link, &server->fs_callq);
-	spin_unlock(&server->fs_lock);
+	spin_unlock(&afs_server_graveyard_lock);
 
-	while (!callslot->ready && !signal_pending(current)) {
-		schedule();
-		set_current_state(TASK_INTERRUPTIBLE);
+	/* now reap the corpses we've extracted */
+	while (!list_empty(&corpses)) {
+		server = list_entry(corpses.next, struct afs_server, grave);
+		list_del(&server->grave);
+		afs_destroy_server(server);
 	}
+}
 
-	set_current_state(TASK_RUNNING);
-
-	/* even if we were interrupted we may still be queued */
-	if (!callslot->ready) {
-		spin_lock(&server->fs_lock);
-		list_del_init(&callslot->link);
-		spin_unlock(&server->fs_lock);
-	}
-
-	nconn = callslot->nconn;
-
-	/* if interrupted, we must release any slot we also got before
-	 * returning an error */
-	if (signal_pending(current)) {
-		ret = -EINTR;
-		goto error_release;
-	}
-
-	/* if we were woken up with an error, then pass that error back to the
-	 * called */
-	if (nconn < 0) {
-		_leave(" = %d", callslot->errno);
-		return callslot->errno;
-	}
-
-	/* were we given a connection directly? */
-	if (callslot->conn) {
-		/* yes - use it */
-		_leave(" = 0 (nc=%d)", nconn);
-		return 0;
-	}
-
-	/* got a callslot, but no connection */
- obtained_slot:
-
-	/* need to get hold of the RxRPC connection */
-	down_write(&server->sem);
-
-	/* quick check to see if there's an outstanding error */
-	ret = server->fs_state;
-	if (ret)
-		goto error_release_upw;
-
-	if (server->fs_conn[nconn]) {
-		/* reuse an existing connection */
-		rxrpc_get_connection(server->fs_conn[nconn]);
-		callslot->conn = server->fs_conn[nconn];
-	}
-	else {
-		/* create a new connection */
-		ret = rxrpc_create_connection(afs_transport,
-					      htons(7000),
-					      server->addr.s_addr,
-					      FS_SERVICE_ID,
-					      NULL,
-					      &server->fs_conn[nconn]);
-
-		if (ret < 0)
-			goto error_release_upw;
-
-		callslot->conn = server->fs_conn[0];
-		rxrpc_get_connection(callslot->conn);
-	}
-
-	up_write(&server->sem);
-
- 	_leave(" = 0");
-	return 0;
-
-	/* handle an error occurring */
- error_release_upw:
-	up_write(&server->sem);
-
- error_release:
-	/* either release the callslot or pass it along to another deserving
-	 * task */
-	spin_lock(&server->fs_lock);
-
-	if (nconn < 0) {
-		/* no callslot allocated */
-	}
-	else if (list_empty(&server->fs_callq)) {
-		/* no one waiting */
-		server->fs_conn_cnt[nconn]++;
-		spin_unlock(&server->fs_lock);
-	}
-	else {
-		/* someone's waiting - dequeue them and wake them up */
-		pcallslot = list_entry(server->fs_callq.next,
-				       struct afs_server_callslot, link);
-		list_del_init(&pcallslot->link);
-
-		pcallslot->errno = server->fs_state;
-		if (!pcallslot->errno) {
-			/* pass them out callslot details */
-			callslot->conn = xchg(&pcallslot->conn,
-					      callslot->conn);
-			pcallslot->nconn = nconn;
-			callslot->nconn = nconn = -1;
-		}
-		pcallslot->ready = 1;
-		wake_up_process(pcallslot->task);
-		spin_unlock(&server->fs_lock);
-	}
-
-	rxrpc_put_connection(callslot->conn);
-	callslot->conn = NULL;
-
-	_leave(" = %d", ret);
-	return ret;
-
-} /* end afs_server_request_callslot() */
-
-/*****************************************************************************/
 /*
- * release a callslot back to the server
- * - transfers the RxRPC connection to the next pending callslot if possible
+ * discard all the server records for rmmod
  */
-void afs_server_release_callslot(struct afs_server *server,
-				 struct afs_server_callslot *callslot)
+void __exit afs_purge_servers(void)
 {
-	struct afs_server_callslot *pcallslot;
-
-	_enter("{ad=%08x,cnt=%u},{%d}",
-	       ntohl(server->addr.s_addr),
-	       server->fs_conn_cnt[callslot->nconn],
-	       callslot->nconn);
-
-	BUG_ON(callslot->nconn < 0);
-
-	spin_lock(&server->fs_lock);
-
-	if (list_empty(&server->fs_callq)) {
-		/* no one waiting */
-		server->fs_conn_cnt[callslot->nconn]++;
-		spin_unlock(&server->fs_lock);
-	}
-	else {
-		/* someone's waiting - dequeue them and wake them up */
-		pcallslot = list_entry(server->fs_callq.next,
-				       struct afs_server_callslot, link);
-		list_del_init(&pcallslot->link);
-
-		pcallslot->errno = server->fs_state;
-		if (!pcallslot->errno) {
-			/* pass them out callslot details */
-			callslot->conn = xchg(&pcallslot->conn, callslot->conn);
-			pcallslot->nconn = callslot->nconn;
-			callslot->nconn = -1;
-		}
-
-		pcallslot->ready = 1;
-		wake_up_process(pcallslot->task);
-		spin_unlock(&server->fs_lock);
-	}
-
-	rxrpc_put_connection(callslot->conn);
-
-	_leave("");
-} /* end afs_server_release_callslot() */
-
-/*****************************************************************************/
-/*
- * get a handle to a connection to the vlserver (volume location) on the
- * specified server
- */
-int afs_server_get_vlconn(struct afs_server *server,
-			  struct rxrpc_connection **_conn)
-{
-	struct rxrpc_connection *conn;
-	int ret;
-
-	_enter("%p,", server);
-
-	ret = 0;
-	conn = NULL;
-	down_read(&server->sem);
-
-	if (server->vlserver) {
-		/* reuse an existing connection */
-		rxrpc_get_connection(server->vlserver);
-		conn = server->vlserver;
-		up_read(&server->sem);
-	}
-	else {
-		/* create a new connection */
-		up_read(&server->sem);
-		down_write(&server->sem);
-		if (!server->vlserver) {
-			ret = rxrpc_create_connection(afs_transport,
-						      htons(7003),
-						      server->addr.s_addr,
-						      VL_SERVICE_ID,
-						      NULL,
-						      &server->vlserver);
-		}
-		if (ret == 0) {
-			rxrpc_get_connection(server->vlserver);
-			conn = server->vlserver;
-		}
-		up_write(&server->sem);
-	}
-
-	*_conn = conn;
-	_leave(" = %d", ret);
-	return ret;
-} /* end afs_server_get_vlconn() */
+	afs_server_timeout = 0;
+	cancel_delayed_work(&afs_server_reaper);
+	schedule_delayed_work(&afs_server_reaper, 0);
+}
diff --git a/fs/afs/server.h b/fs/afs/server.h
deleted file mode 100644
index c3d2411..0000000
--- a/fs/afs/server.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/* server.h: AFS server record
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_AFS_SERVER_H
-#define _LINUX_AFS_SERVER_H
-
-#include "types.h"
-#include "kafstimod.h"
-#include <rxrpc/peer.h>
-#include <linux/rwsem.h>
-
-extern spinlock_t afs_server_peer_lock;
-
-/*****************************************************************************/
-/*
- * AFS server record
- */
-struct afs_server
-{
-	atomic_t		usage;
-	struct afs_cell		*cell;		/* cell in which server resides */
-	struct list_head	link;		/* link in cell's server list */
-	struct rw_semaphore	sem;		/* access lock */
-	struct afs_timer	timeout;	/* graveyard timeout */
-	struct in_addr		addr;		/* server address */
-	struct rxrpc_peer	*peer;		/* peer record for this server */
-	struct rxrpc_connection	*vlserver;	/* connection to the volume location service */
-
-	/* file service access */
-#define AFS_SERVER_CONN_LIST_SIZE 2
-	struct rxrpc_connection	*fs_conn[AFS_SERVER_CONN_LIST_SIZE]; /* FS connections */
-	unsigned		fs_conn_cnt[AFS_SERVER_CONN_LIST_SIZE];	/* per conn call count */
-	struct list_head	fs_callq;	/* queue of processes waiting to make a call */
-	spinlock_t		fs_lock;	/* access lock */
-	int			fs_state;      	/* 0 or reason FS currently marked dead (-errno) */
-	unsigned		fs_rtt;		/* FS round trip time */
-	unsigned long		fs_act_jif;	/* time at which last activity occurred */
-	unsigned long		fs_dead_jif;	/* time at which no longer to be considered dead */
-
-	/* callback promise management */
-	struct list_head	cb_promises;	/* as yet unbroken promises from this server */
-	spinlock_t		cb_lock;	/* access lock */
-};
-
-extern int afs_server_lookup(struct afs_cell *cell,
-			     const struct in_addr *addr,
-			     struct afs_server **_server);
-
-#define afs_get_server(S) do { atomic_inc(&(S)->usage); } while(0)
-
-extern void afs_put_server(struct afs_server *server);
-extern void afs_server_do_timeout(struct afs_server *server);
-
-extern int afs_server_find_by_peer(const struct rxrpc_peer *peer,
-				   struct afs_server **_server);
-
-extern int afs_server_get_vlconn(struct afs_server *server,
-				 struct rxrpc_connection **_conn);
-
-static inline
-struct afs_server *afs_server_get_from_peer(struct rxrpc_peer *peer)
-{
-	struct afs_server *server;
-
-	spin_lock(&afs_server_peer_lock);
-	server = peer->user;
-	if (server)
-		afs_get_server(server);
-	spin_unlock(&afs_server_peer_lock);
-
-	return server;
-}
-
-/*****************************************************************************/
-/*
- * AFS server callslot grant record
- */
-struct afs_server_callslot
-{
-	struct list_head	link;		/* link in server's list */
-	struct task_struct	*task;		/* process waiting to make call */
-	struct rxrpc_connection	*conn;		/* connection to use (or NULL on error) */
-	short			nconn;		/* connection slot number (-1 on error) */
-	char			ready;		/* T when ready */
-	int			errno;		/* error number if nconn==-1 */
-};
-
-extern int afs_server_request_callslot(struct afs_server *server,
-				       struct afs_server_callslot *callslot);
-
-extern void afs_server_release_callslot(struct afs_server *server,
-					struct afs_server_callslot *callslot);
-
-#endif /* _LINUX_AFS_SERVER_H */
diff --git a/fs/afs/super.c b/fs/afs/super.c
index eb7e323..cebd03c 100644
--- a/fs/afs/super.c
+++ b/fs/afs/super.c
@@ -1,5 +1,6 @@
-/*
- * Copyright (c) 2002 Red Hat, Inc. All rights reserved.
+/* AFS superblock handling
+ *
+ * Copyright (c) 2002, 2007 Red Hat, Inc. All rights reserved.
  *
  * This software may be freely redistributed under the terms of the
  * GNU General Public License.
@@ -9,7 +10,7 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * Authors: David Howells <dhowells@redhat.com>
- *          David Woodhouse <dwmw2@cambridge.redhat.com>
+ *          David Woodhouse <dwmw2@redhat.com>
  *
  */
 
@@ -19,22 +20,10 @@
 #include <linux/slab.h>
 #include <linux/fs.h>
 #include <linux/pagemap.h>
-#include "vnode.h"
-#include "volume.h"
-#include "cell.h"
-#include "cmservice.h"
-#include "fsclient.h"
-#include "super.h"
 #include "internal.h"
 
 #define AFS_FS_MAGIC 0x6B414653 /* 'kAFS' */
 
-struct afs_mount_params {
-	int			rwpath;
-	struct afs_cell		*default_cell;
-	struct afs_volume	*volume;
-};
-
 static void afs_i_init_once(void *foo, struct kmem_cache *cachep,
 			    unsigned long flags);
 
@@ -62,13 +51,13 @@
 	.drop_inode	= generic_delete_inode,
 	.destroy_inode	= afs_destroy_inode,
 	.clear_inode	= afs_clear_inode,
+	.umount_begin	= afs_umount_begin,
 	.put_super	= afs_put_super,
 };
 
 static struct kmem_cache *afs_inode_cachep;
 static atomic_t afs_count_active_inodes;
 
-/*****************************************************************************/
 /*
  * initialise the filesystem
  */
@@ -78,8 +67,6 @@
 
 	_enter("");
 
-	afs_timer_init(&afs_mntpt_expiry_timer, &afs_mntpt_expiry_timer_ops);
-
 	/* create ourselves an inode cache */
 	atomic_set(&afs_count_active_inodes, 0);
 
@@ -99,20 +86,22 @@
 	ret = register_filesystem(&afs_fs_type);
 	if (ret < 0) {
 		kmem_cache_destroy(afs_inode_cachep);
-		kleave(" = %d", ret);
+		_leave(" = %d", ret);
 		return ret;
 	}
 
-	kleave(" = 0");
+	_leave(" = 0");
 	return 0;
-} /* end afs_fs_init() */
+}
 
-/*****************************************************************************/
 /*
  * clean up the filesystem
  */
 void __exit afs_fs_exit(void)
 {
+	_enter("");
+
+	afs_mntpt_kill_timer();
 	unregister_filesystem(&afs_fs_type);
 
 	if (atomic_read(&afs_count_active_inodes) != 0) {
@@ -122,10 +111,9 @@
 	}
 
 	kmem_cache_destroy(afs_inode_cachep);
+	_leave("");
+}
 
-} /* end afs_fs_exit() */
-
-/*****************************************************************************/
 /*
  * check that an argument has a value
  */
@@ -136,9 +124,8 @@
 		return 0;
 	}
 	return 1;
-} /* end want_arg() */
+}
 
-/*****************************************************************************/
 /*
  * check that there's no subsequent value
  */
@@ -150,18 +137,17 @@
 		return 0;
 	}
 	return 1;
-} /* end want_no_value() */
+}
 
-/*****************************************************************************/
 /*
  * parse the mount options
  * - this function has been shamelessly adapted from the ext3 fs which
  *   shamelessly adapted it from the msdos fs
  */
-static int afs_super_parse_options(struct afs_mount_params *params,
-				   char *options,
-				   const char **devname)
+static int afs_parse_options(struct afs_mount_params *params,
+			     char *options, const char **devname)
 {
+	struct afs_cell *cell;
 	char *key, *value;
 	int ret;
 
@@ -170,51 +156,135 @@
 	options[PAGE_SIZE - 1] = 0;
 
 	ret = 0;
-	while ((key = strsep(&options, ",")) != 0)
-	{
+	while ((key = strsep(&options, ","))) {
 		value = strchr(key, '=');
 		if (value)
 			*value++ = 0;
 
-		printk("kAFS: KEY: %s, VAL:%s\n", key, value ?: "-");
+		_debug("kAFS: KEY: %s, VAL:%s", key, value ?: "-");
 
 		if (strcmp(key, "rwpath") == 0) {
 			if (!want_no_value(&value, "rwpath"))
 				return -EINVAL;
 			params->rwpath = 1;
-			continue;
-		}
-		else if (strcmp(key, "vol") == 0) {
+		} else if (strcmp(key, "vol") == 0) {
 			if (!want_arg(&value, "vol"))
 				return -EINVAL;
 			*devname = value;
-			continue;
-		}
-		else if (strcmp(key, "cell") == 0) {
+		} else if (strcmp(key, "cell") == 0) {
 			if (!want_arg(&value, "cell"))
 				return -EINVAL;
-			afs_put_cell(params->default_cell);
-			ret = afs_cell_lookup(value,
-					      strlen(value),
-					      &params->default_cell);
-			if (ret < 0)
-				return -EINVAL;
-			continue;
+			cell = afs_cell_lookup(value, strlen(value));
+			if (IS_ERR(cell))
+				return PTR_ERR(cell);
+			afs_put_cell(params->cell);
+			params->cell = cell;
+		} else {
+			printk("kAFS: Unknown mount option: '%s'\n",  key);
+			ret = -EINVAL;
+			goto error;
 		}
-
-		printk("kAFS: Unknown mount option: '%s'\n",  key);
-		ret = -EINVAL;
-		goto error;
 	}
 
 	ret = 0;
-
- error:
+error:
 	_leave(" = %d", ret);
 	return ret;
-} /* end afs_super_parse_options() */
+}
 
-/*****************************************************************************/
+/*
+ * parse a device name to get cell name, volume name, volume type and R/W
+ * selector
+ * - this can be one of the following:
+ *	"%[cell:]volume[.]"		R/W volume
+ *	"#[cell:]volume[.]"		R/O or R/W volume (rwpath=0),
+ *					 or R/W (rwpath=1) volume
+ *	"%[cell:]volume.readonly"	R/O volume
+ *	"#[cell:]volume.readonly"	R/O volume
+ *	"%[cell:]volume.backup"		Backup volume
+ *	"#[cell:]volume.backup"		Backup volume
+ */
+static int afs_parse_device_name(struct afs_mount_params *params,
+				 const char *name)
+{
+	struct afs_cell *cell;
+	const char *cellname, *suffix;
+	int cellnamesz;
+
+	_enter(",%s", name);
+
+	if (!name) {
+		printk(KERN_ERR "kAFS: no volume name specified\n");
+		return -EINVAL;
+	}
+
+	if ((name[0] != '%' && name[0] != '#') || !name[1]) {
+		printk(KERN_ERR "kAFS: unparsable volume name\n");
+		return -EINVAL;
+	}
+
+	/* determine the type of volume we're looking for */
+	params->type = AFSVL_ROVOL;
+	params->force = false;
+	if (params->rwpath || name[0] == '%') {
+		params->type = AFSVL_RWVOL;
+		params->force = true;
+	}
+	name++;
+
+	/* split the cell name out if there is one */
+	params->volname = strchr(name, ':');
+	if (params->volname) {
+		cellname = name;
+		cellnamesz = params->volname - name;
+		params->volname++;
+	} else {
+		params->volname = name;
+		cellname = NULL;
+		cellnamesz = 0;
+	}
+
+	/* the volume type is further affected by a possible suffix */
+	suffix = strrchr(params->volname, '.');
+	if (suffix) {
+		if (strcmp(suffix, ".readonly") == 0) {
+			params->type = AFSVL_ROVOL;
+			params->force = true;
+		} else if (strcmp(suffix, ".backup") == 0) {
+			params->type = AFSVL_BACKVOL;
+			params->force = true;
+		} else if (suffix[1] == 0) {
+		} else {
+			suffix = NULL;
+		}
+	}
+
+	params->volnamesz = suffix ?
+		suffix - params->volname : strlen(params->volname);
+
+	_debug("cell %*.*s [%p]",
+	       cellnamesz, cellnamesz, cellname ?: "", params->cell);
+
+	/* lookup the cell record */
+	if (cellname || !params->cell) {
+		cell = afs_cell_lookup(cellname, cellnamesz);
+		if (IS_ERR(cell)) {
+			printk(KERN_ERR "kAFS: unable to lookup cell '%s'\n",
+			       cellname ?: "");
+			return PTR_ERR(cell);
+		}
+		afs_put_cell(params->cell);
+		params->cell = cell;
+	}
+
+	_debug("CELL:%s [%p] VOLUME:%*.*s SUFFIX:%s TYPE:%d%s",
+	       params->cell->name, params->cell,
+	       params->volnamesz, params->volnamesz, params->volname,
+	       suffix ?: "-", params->type, params->force ? " FORCE" : "");
+
+	return 0;
+}
+
 /*
  * check a superblock to see if it's the one we're looking for
  */
@@ -224,13 +294,12 @@
 	struct afs_super_info *as = sb->s_fs_info;
 
 	return as->volume == params->volume;
-} /* end afs_test_super() */
+}
 
-/*****************************************************************************/
 /*
  * fill in the superblock
  */
-static int afs_fill_super(struct super_block *sb, void *data, int silent)
+static int afs_fill_super(struct super_block *sb, void *data)
 {
 	struct afs_mount_params *params = data;
 	struct afs_super_info *as = NULL;
@@ -239,7 +308,7 @@
 	struct inode *inode = NULL;
 	int ret;
 
-	kenter("");
+	_enter("");
 
 	/* allocate a superblock info record */
 	as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL);
@@ -262,9 +331,9 @@
 	fid.vid		= as->volume->vid;
 	fid.vnode	= 1;
 	fid.unique	= 1;
-	ret = afs_iget(sb, &fid, &inode);
-	if (ret < 0)
-		goto error;
+	inode = afs_iget(sb, params->key, &fid, NULL, NULL);
+	if (IS_ERR(inode))
+		goto error_inode;
 
 	ret = -ENOMEM;
 	root = d_alloc_root(inode);
@@ -273,21 +342,23 @@
 
 	sb->s_root = root;
 
-	kleave(" = 0");
+	_leave(" = 0");
 	return 0;
 
- error:
+error_inode:
+	ret = PTR_ERR(inode);
+	inode = NULL;
+error:
 	iput(inode);
 	afs_put_volume(as->volume);
 	kfree(as);
 
 	sb->s_fs_info = NULL;
 
-	kleave(" = %d", ret);
+	_leave(" = %d", ret);
 	return ret;
-} /* end afs_fill_super() */
+}
 
-/*****************************************************************************/
 /*
  * get an AFS superblock
  * - TODO: don't use get_sb_nodev(), but rather call sget() directly
@@ -300,69 +371,80 @@
 {
 	struct afs_mount_params params;
 	struct super_block *sb;
+	struct afs_volume *vol;
+	struct key *key;
 	int ret;
 
 	_enter(",,%s,%p", dev_name, options);
 
 	memset(&params, 0, sizeof(params));
 
-	/* start the cache manager */
-	ret = afscm_start();
-	if (ret < 0) {
-		_leave(" = %d", ret);
-		return ret;
-	}
-
-	/* parse the options */
+	/* parse the options and device name */
 	if (options) {
-		ret = afs_super_parse_options(&params, options, &dev_name);
+		ret = afs_parse_options(&params, options, &dev_name);
 		if (ret < 0)
 			goto error;
-		if (!dev_name) {
-			printk("kAFS: no volume name specified\n");
-			ret = -EINVAL;
-			goto error;
-		}
 	}
 
-	/* parse the device name */
-	ret = afs_volume_lookup(dev_name,
-				params.default_cell,
-				params.rwpath,
-				&params.volume);
+
+	ret = afs_parse_device_name(&params, dev_name);
 	if (ret < 0)
 		goto error;
 
-	/* allocate a deviceless superblock */
-	sb = sget(fs_type, afs_test_super, set_anon_super, &params);
-	if (IS_ERR(sb))
-		goto error;
-
-	sb->s_flags = flags;
-
-	ret = afs_fill_super(sb, &params, flags & MS_SILENT ? 1 : 0);
-	if (ret < 0) {
-		up_write(&sb->s_umount);
-		deactivate_super(sb);
+	/* try and do the mount securely */
+	key = afs_request_key(params.cell);
+	if (IS_ERR(key)) {
+		_leave(" = %ld [key]", PTR_ERR(key));
+		ret = PTR_ERR(key);
 		goto error;
 	}
-	sb->s_flags |= MS_ACTIVE;
-	simple_set_mnt(mnt, sb);
+	params.key = key;
 
+	/* parse the device name */
+	vol = afs_volume_lookup(&params);
+	if (IS_ERR(vol)) {
+		ret = PTR_ERR(vol);
+		goto error;
+	}
+	params.volume = vol;
+
+	/* allocate a deviceless superblock */
+	sb = sget(fs_type, afs_test_super, set_anon_super, &params);
+	if (IS_ERR(sb)) {
+		ret = PTR_ERR(sb);
+		goto error;
+	}
+
+	if (!sb->s_root) {
+		/* initial superblock/root creation */
+		_debug("create");
+		sb->s_flags = flags;
+		ret = afs_fill_super(sb, &params);
+		if (ret < 0) {
+			up_write(&sb->s_umount);
+			deactivate_super(sb);
+			goto error;
+		}
+		sb->s_flags |= MS_ACTIVE;
+	} else {
+		_debug("reuse");
+		ASSERTCMP(sb->s_flags, &, MS_ACTIVE);
+	}
+
+	simple_set_mnt(mnt, sb);
 	afs_put_volume(params.volume);
-	afs_put_cell(params.default_cell);
-	_leave(" = 0 [%p]", 0, sb);
+	afs_put_cell(params.cell);
+	_leave(" = 0 [%p]", sb);
 	return 0;
 
- error:
+error:
 	afs_put_volume(params.volume);
-	afs_put_cell(params.default_cell);
-	afscm_stop();
+	afs_put_cell(params.cell);
+	key_put(params.key);
 	_leave(" = %d", ret);
 	return ret;
-} /* end afs_get_sb() */
+}
 
-/*****************************************************************************/
 /*
  * finish the unmounting process on the superblock
  */
@@ -373,35 +455,30 @@
 	_enter("");
 
 	afs_put_volume(as->volume);
-	afscm_stop();
 
 	_leave("");
-} /* end afs_put_super() */
+}
 
-/*****************************************************************************/
 /*
  * initialise an inode cache slab element prior to any use
  */
 static void afs_i_init_once(void *_vnode, struct kmem_cache *cachep,
 			    unsigned long flags)
 {
-	struct afs_vnode *vnode = (struct afs_vnode *) _vnode;
+	struct afs_vnode *vnode = _vnode;
 
 	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
 	    SLAB_CTOR_CONSTRUCTOR) {
 		memset(vnode, 0, sizeof(*vnode));
 		inode_init_once(&vnode->vfs_inode);
 		init_waitqueue_head(&vnode->update_waitq);
+		mutex_init(&vnode->permits_lock);
+		mutex_init(&vnode->validate_lock);
 		spin_lock_init(&vnode->lock);
-		INIT_LIST_HEAD(&vnode->cb_link);
-		INIT_LIST_HEAD(&vnode->cb_hash_link);
-		afs_timer_init(&vnode->cb_timeout,
-			       &afs_vnode_cb_timed_out_ops);
+		INIT_WORK(&vnode->cb_broken_work, afs_broken_callback_work);
 	}
+}
 
-} /* end afs_i_init_once() */
-
-/*****************************************************************************/
 /*
  * allocate an AFS inode struct from our slab cache
  */
@@ -409,8 +486,7 @@
 {
 	struct afs_vnode *vnode;
 
-	vnode = (struct afs_vnode *)
-		kmem_cache_alloc(afs_inode_cachep, GFP_KERNEL);
+	vnode = kmem_cache_alloc(afs_inode_cachep, GFP_KERNEL);
 	if (!vnode)
 		return NULL;
 
@@ -421,21 +497,25 @@
 
 	vnode->volume		= NULL;
 	vnode->update_cnt	= 0;
-	vnode->flags		= 0;
+	vnode->flags		= 1 << AFS_VNODE_UNSET;
+	vnode->cb_promised	= false;
 
 	return &vnode->vfs_inode;
-} /* end afs_alloc_inode() */
+}
 
-/*****************************************************************************/
 /*
  * destroy an AFS inode struct
  */
 static void afs_destroy_inode(struct inode *inode)
 {
+	struct afs_vnode *vnode = AFS_FS_I(inode);
+
 	_enter("{%lu}", inode->i_ino);
 
-	kmem_cache_free(afs_inode_cachep, AFS_FS_I(inode));
+	_debug("DESTROY INODE %p", inode);
 
+	ASSERTCMP(vnode->server, ==, NULL);
+
+	kmem_cache_free(afs_inode_cachep, vnode);
 	atomic_dec(&afs_count_active_inodes);
-
-} /* end afs_destroy_inode() */
+}
diff --git a/fs/afs/super.h b/fs/afs/super.h
deleted file mode 100644
index 32de8cc..0000000
--- a/fs/afs/super.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* super.h: AFS filesystem internal private data
- *
- * Copyright (c) 2002 Red Hat, Inc. All rights reserved.
- *
- * This software may be freely redistributed under the terms of the
- * GNU General Public License.
- *
- * 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.
- *
- * Authors: David Woodhouse <dwmw2@cambridge.redhat.com>
- *          David Howells <dhowells@redhat.com>
- *
- */
-
-#ifndef _LINUX_AFS_SUPER_H
-#define _LINUX_AFS_SUPER_H
-
-#include <linux/fs.h>
-#include "server.h"
-
-#ifdef __KERNEL__
-
-/*****************************************************************************/
-/*
- * AFS superblock private data
- * - there's one superblock per volume
- */
-struct afs_super_info
-{
-	struct afs_volume	*volume;	/* volume record */
-	char			rwparent;	/* T if parent is R/W AFS volume */
-};
-
-static inline struct afs_super_info *AFS_FS_S(struct super_block *sb)
-{
-	return sb->s_fs_info;
-}
-
-extern struct file_system_type afs_fs_type;
-
-#endif /* __KERNEL__ */
-
-#endif /* _LINUX_AFS_SUPER_H */
diff --git a/fs/afs/transport.h b/fs/afs/transport.h
deleted file mode 100644
index 7013ae6..0000000
--- a/fs/afs/transport.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* transport.h: AFS transport management
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_AFS_TRANSPORT_H
-#define _LINUX_AFS_TRANSPORT_H
-
-#include "types.h"
-#include <rxrpc/transport.h>
-
-/* the cache manager transport endpoint */
-extern struct rxrpc_transport *afs_transport;
-
-#endif /* _LINUX_AFS_TRANSPORT_H */
diff --git a/fs/afs/types.h b/fs/afs/types.h
deleted file mode 100644
index b1a2367..0000000
--- a/fs/afs/types.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/* types.h: AFS types
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_AFS_TYPES_H
-#define _LINUX_AFS_TYPES_H
-
-#ifdef __KERNEL__
-#include <rxrpc/types.h>
-#endif /* __KERNEL__ */
-
-typedef unsigned			afs_volid_t;
-typedef unsigned			afs_vnodeid_t;
-typedef unsigned long long		afs_dataversion_t;
-
-typedef enum {
-	AFSVL_RWVOL,			/* read/write volume */
-	AFSVL_ROVOL,			/* read-only volume */
-	AFSVL_BACKVOL,			/* backup volume */
-} __attribute__((packed)) afs_voltype_t;
-
-typedef enum {
-	AFS_FTYPE_INVALID	= 0,
-	AFS_FTYPE_FILE		= 1,
-	AFS_FTYPE_DIR		= 2,
-	AFS_FTYPE_SYMLINK	= 3,
-} afs_file_type_t;
-
-#ifdef __KERNEL__
-
-struct afs_cell;
-struct afs_vnode;
-
-/*****************************************************************************/
-/*
- * AFS file identifier
- */
-struct afs_fid
-{
-	afs_volid_t	vid;		/* volume ID */
-	afs_vnodeid_t	vnode;		/* file index within volume */
-	unsigned	unique;		/* unique ID number (file index version) */
-};
-
-/*****************************************************************************/
-/*
- * AFS callback notification
- */
-typedef enum {
-	AFSCM_CB_UNTYPED	= 0,	/* no type set on CB break */
-	AFSCM_CB_EXCLUSIVE	= 1,	/* CB exclusive to CM [not implemented] */
-	AFSCM_CB_SHARED		= 2,	/* CB shared by other CM's */
-	AFSCM_CB_DROPPED	= 3,	/* CB promise cancelled by file server */
-} afs_callback_type_t;
-
-struct afs_callback
-{
-	struct afs_server	*server;	/* server that made the promise */
-	struct afs_fid		fid;		/* file identifier */
-	unsigned		version;	/* callback version */
-	unsigned		expiry;		/* time at which expires */
-	afs_callback_type_t	type;		/* type of callback */
-};
-
-#define AFSCBMAX 50
-
-/*****************************************************************************/
-/*
- * AFS volume information
- */
-struct afs_volume_info
-{
-	afs_volid_t		vid;		/* volume ID */
-	afs_voltype_t		type;		/* type of this volume */
-	afs_volid_t		type_vids[5];	/* volume ID's for possible types for this vol */
-	
-	/* list of fileservers serving this volume */
-	size_t			nservers;	/* number of entries used in servers[] */
-	struct {
-		struct in_addr	addr;		/* fileserver address */
-	} servers[8];
-};
-
-/*****************************************************************************/
-/*
- * AFS file status information
- */
-struct afs_file_status
-{
-	unsigned		if_version;	/* interface version */
-#define AFS_FSTATUS_VERSION	1
-
-	afs_file_type_t		type;		/* file type */
-	unsigned		nlink;		/* link count */
-	size_t			size;		/* file size */
-	afs_dataversion_t	version;	/* current data version */
-	unsigned		author;		/* author ID */
-	unsigned		owner;		/* owner ID */
-	unsigned		caller_access;	/* access rights for authenticated caller */
-	unsigned		anon_access;	/* access rights for unauthenticated caller */
-	umode_t			mode;		/* UNIX mode */
-	struct afs_fid		parent;		/* parent file ID */
-	time_t			mtime_client;	/* last time client changed data */
-	time_t			mtime_server;	/* last time server changed data */
-};
-
-/*****************************************************************************/
-/*
- * AFS volume synchronisation information
- */
-struct afs_volsync
-{
-	time_t			creation;	/* volume creation time */
-};
-
-#endif /* __KERNEL__ */
-
-#endif /* _LINUX_AFS_TYPES_H */
diff --git a/fs/afs/use-rtnetlink.c b/fs/afs/use-rtnetlink.c
new file mode 100644
index 0000000..f8991c7
--- /dev/null
+++ b/fs/afs/use-rtnetlink.c
@@ -0,0 +1,473 @@
+/* RTNETLINK client
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_addr.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <net/netlink.h>
+#include "internal.h"
+
+struct afs_rtm_desc {
+	struct socket		*nlsock;
+	struct afs_interface	*bufs;
+	u8			*mac;
+	size_t			nbufs;
+	size_t			maxbufs;
+	void			*data;
+	ssize_t			datalen;
+	size_t			datamax;
+	int			msg_seq;
+	unsigned		mac_index;
+	bool			wantloopback;
+	int (*parse)(struct afs_rtm_desc *, struct nlmsghdr *);
+};
+
+/*
+ * parse an RTM_GETADDR response
+ */
+static int afs_rtm_getaddr_parse(struct afs_rtm_desc *desc,
+				 struct nlmsghdr *nlhdr)
+{
+	struct afs_interface *this;
+	struct ifaddrmsg *ifa;
+	struct rtattr *rtattr;
+	const char *name;
+	size_t len;
+
+	ifa = (struct ifaddrmsg *) NLMSG_DATA(nlhdr);
+
+	_enter("{ix=%d,af=%d}", ifa->ifa_index, ifa->ifa_family);
+
+	if (ifa->ifa_family != AF_INET) {
+		_leave(" = 0 [family %d]", ifa->ifa_family);
+		return 0;
+	}
+	if (desc->nbufs >= desc->maxbufs) {
+		_leave(" = 0 [max %zu/%zu]", desc->nbufs, desc->maxbufs);
+		return 0;
+	}
+
+	this = &desc->bufs[desc->nbufs];
+
+	this->index = ifa->ifa_index;
+	this->netmask.s_addr = inet_make_mask(ifa->ifa_prefixlen);
+	this->mtu = 0;
+
+	rtattr = NLMSG_DATA(nlhdr) + NLMSG_ALIGN(sizeof(struct ifaddrmsg));
+	len = NLMSG_PAYLOAD(nlhdr, sizeof(struct ifaddrmsg));
+
+	name = "unknown";
+	for (; RTA_OK(rtattr, len); rtattr = RTA_NEXT(rtattr, len)) {
+		switch (rtattr->rta_type) {
+		case IFA_ADDRESS:
+			memcpy(&this->address, RTA_DATA(rtattr), 4);
+			break;
+		case IFA_LABEL:
+			name = RTA_DATA(rtattr);
+			break;
+		}
+	}
+
+	_debug("%s: "NIPQUAD_FMT"/"NIPQUAD_FMT,
+	       name, NIPQUAD(this->address), NIPQUAD(this->netmask));
+
+	desc->nbufs++;
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * parse an RTM_GETLINK response for MTUs
+ */
+static int afs_rtm_getlink_if_parse(struct afs_rtm_desc *desc,
+				    struct nlmsghdr *nlhdr)
+{
+	struct afs_interface *this;
+	struct ifinfomsg *ifi;
+	struct rtattr *rtattr;
+	const char *name;
+	size_t len, loop;
+
+	ifi = (struct ifinfomsg *) NLMSG_DATA(nlhdr);
+
+	_enter("{ix=%d}", ifi->ifi_index);
+
+	for (loop = 0; loop < desc->nbufs; loop++) {
+		this = &desc->bufs[loop];
+		if (this->index == ifi->ifi_index)
+			goto found;
+	}
+
+	_leave(" = 0 [no match]");
+	return 0;
+
+found:
+	if (ifi->ifi_type == ARPHRD_LOOPBACK && !desc->wantloopback) {
+		_leave(" = 0 [loopback]");
+		return 0;
+	}
+
+	rtattr = NLMSG_DATA(nlhdr) + NLMSG_ALIGN(sizeof(struct ifinfomsg));
+	len = NLMSG_PAYLOAD(nlhdr, sizeof(struct ifinfomsg));
+
+	name = "unknown";
+	for (; RTA_OK(rtattr, len); rtattr = RTA_NEXT(rtattr, len)) {
+		switch (rtattr->rta_type) {
+		case IFLA_MTU:
+			memcpy(&this->mtu, RTA_DATA(rtattr), 4);
+			break;
+		case IFLA_IFNAME:
+			name = RTA_DATA(rtattr);
+			break;
+		}
+	}
+
+	_debug("%s: "NIPQUAD_FMT"/"NIPQUAD_FMT" mtu %u",
+	       name, NIPQUAD(this->address), NIPQUAD(this->netmask),
+	       this->mtu);
+
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * parse an RTM_GETLINK response for the MAC address belonging to the lowest
+ * non-internal interface
+ */
+static int afs_rtm_getlink_mac_parse(struct afs_rtm_desc *desc,
+				     struct nlmsghdr *nlhdr)
+{
+	struct ifinfomsg *ifi;
+	struct rtattr *rtattr;
+	const char *name;
+	size_t remain, len;
+	bool set;
+
+	ifi = (struct ifinfomsg *) NLMSG_DATA(nlhdr);
+
+	_enter("{ix=%d}", ifi->ifi_index);
+
+	if (ifi->ifi_index >= desc->mac_index) {
+		_leave(" = 0 [high]");
+		return 0;
+	}
+	if (ifi->ifi_type == ARPHRD_LOOPBACK) {
+		_leave(" = 0 [loopback]");
+		return 0;
+	}
+
+	rtattr = NLMSG_DATA(nlhdr) + NLMSG_ALIGN(sizeof(struct ifinfomsg));
+	remain = NLMSG_PAYLOAD(nlhdr, sizeof(struct ifinfomsg));
+
+	name = "unknown";
+	set = false;
+	for (; RTA_OK(rtattr, remain); rtattr = RTA_NEXT(rtattr, remain)) {
+		switch (rtattr->rta_type) {
+		case IFLA_ADDRESS:
+			len = RTA_PAYLOAD(rtattr);
+			memcpy(desc->mac, RTA_DATA(rtattr),
+			       min_t(size_t, len, 6));
+			desc->mac_index = ifi->ifi_index;
+			set = true;
+			break;
+		case IFLA_IFNAME:
+			name = RTA_DATA(rtattr);
+			break;
+		}
+	}
+
+	if (set)
+		_debug("%s: %02x:%02x:%02x:%02x:%02x:%02x",
+		       name,
+		       desc->mac[0], desc->mac[1], desc->mac[2],
+		       desc->mac[3], desc->mac[4], desc->mac[5]);
+
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * read the rtnetlink response and pass to parsing routine
+ */
+static int afs_read_rtm(struct afs_rtm_desc *desc)
+{
+	struct nlmsghdr *nlhdr, tmphdr;
+	struct msghdr msg;
+	struct kvec iov[1];
+	void *data;
+	bool last = false;
+	int len, ret, remain;
+
+	_enter("");
+
+	do {
+		/* first of all peek to see how big the packet is */
+		memset(&msg, 0, sizeof(msg));
+		iov[0].iov_base = &tmphdr;
+		iov[0].iov_len = sizeof(tmphdr);
+		len = kernel_recvmsg(desc->nlsock, &msg, iov, 1,
+				     sizeof(tmphdr), MSG_PEEK | MSG_TRUNC);
+		if (len < 0) {
+			_leave(" = %d [peek]", len);
+			return len;
+		}
+		if (len == 0)
+			continue;
+		if (len < sizeof(tmphdr) || len < NLMSG_PAYLOAD(&tmphdr, 0)) {
+			_leave(" = -EMSGSIZE");
+			return -EMSGSIZE;
+		}
+
+		if (desc->datamax < len) {
+			kfree(desc->data);
+			desc->data = NULL;
+			data = kmalloc(len, GFP_KERNEL);
+			if (!data)
+				return -ENOMEM;
+			desc->data = data;
+		}
+		desc->datamax = len;
+
+		/* read all the data from this packet */
+		iov[0].iov_base = desc->data;
+		iov[0].iov_len = desc->datamax;
+		desc->datalen = kernel_recvmsg(desc->nlsock, &msg, iov, 1,
+					       desc->datamax, 0);
+		if (desc->datalen < 0) {
+			_leave(" = %zd [recv]", desc->datalen);
+			return desc->datalen;
+		}
+
+		nlhdr = desc->data;
+
+		/* check if the header is valid */
+		if (!NLMSG_OK(nlhdr, desc->datalen) ||
+		    nlhdr->nlmsg_type == NLMSG_ERROR) {
+			_leave(" = -EIO");
+			return -EIO;
+		}
+
+		/* see if this is the last message */
+		if (nlhdr->nlmsg_type == NLMSG_DONE ||
+		    !(nlhdr->nlmsg_flags & NLM_F_MULTI))
+			last = true;
+
+		/* parse the bits we got this time */
+		nlmsg_for_each_msg(nlhdr, desc->data, desc->datalen, remain) {
+			ret = desc->parse(desc, nlhdr);
+			if (ret < 0) {
+				_leave(" = %d [parse]", ret);
+				return ret;
+			}
+		}
+
+	} while (!last);
+
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * list the interface bound addresses to get the address and netmask
+ */
+static int afs_rtm_getaddr(struct afs_rtm_desc *desc)
+{
+	struct msghdr msg;
+	struct kvec iov[1];
+	int ret;
+
+	struct {
+		struct nlmsghdr nl_msg __attribute__((aligned(NLMSG_ALIGNTO)));
+		struct ifaddrmsg addr_msg __attribute__((aligned(NLMSG_ALIGNTO)));
+	} request;
+
+	_enter("");
+
+	memset(&request, 0, sizeof(request));
+
+	request.nl_msg.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+	request.nl_msg.nlmsg_type = RTM_GETADDR;
+	request.nl_msg.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+	request.nl_msg.nlmsg_seq = desc->msg_seq++;
+	request.nl_msg.nlmsg_pid = 0;
+
+	memset(&msg, 0, sizeof(msg));
+	iov[0].iov_base = &request;
+	iov[0].iov_len = sizeof(request);
+
+	ret = kernel_sendmsg(desc->nlsock, &msg, iov, 1, iov[0].iov_len);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * list the interface link statuses to get the MTUs
+ */
+static int afs_rtm_getlink(struct afs_rtm_desc *desc)
+{
+	struct msghdr msg;
+	struct kvec iov[1];
+	int ret;
+
+	struct {
+		struct nlmsghdr nl_msg __attribute__((aligned(NLMSG_ALIGNTO)));
+		struct ifinfomsg link_msg __attribute__((aligned(NLMSG_ALIGNTO)));
+	} request;
+
+	_enter("");
+
+	memset(&request, 0, sizeof(request));
+
+	request.nl_msg.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	request.nl_msg.nlmsg_type = RTM_GETLINK;
+	request.nl_msg.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
+	request.nl_msg.nlmsg_seq = desc->msg_seq++;
+	request.nl_msg.nlmsg_pid = 0;
+
+	memset(&msg, 0, sizeof(msg));
+	iov[0].iov_base = &request;
+	iov[0].iov_len = sizeof(request);
+
+	ret = kernel_sendmsg(desc->nlsock, &msg, iov, 1, iov[0].iov_len);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * cull any interface records for which there isn't an MTU value
+ */
+static void afs_cull_interfaces(struct afs_rtm_desc *desc)
+{
+	struct afs_interface *bufs = desc->bufs;
+	size_t nbufs = desc->nbufs;
+	int loop, point = 0;
+
+	_enter("{%zu}", nbufs);
+
+	for (loop = 0; loop < nbufs; loop++) {
+		if (desc->bufs[loop].mtu != 0) {
+			if (loop != point) {
+				ASSERTCMP(loop, >, point);
+				bufs[point] = bufs[loop];
+			}
+			point++;
+		}
+	}
+
+	desc->nbufs = point;
+	_leave(" [%zu/%zu]", desc->nbufs, nbufs);
+}
+
+/*
+ * get a list of this system's interface IPv4 addresses, netmasks and MTUs
+ * - returns the number of interface records in the buffer
+ */
+int afs_get_ipv4_interfaces(struct afs_interface *bufs, size_t maxbufs,
+			    bool wantloopback)
+{
+	struct afs_rtm_desc desc;
+	int ret, loop;
+
+	_enter("");
+
+	memset(&desc, 0, sizeof(desc));
+	desc.bufs = bufs;
+	desc.maxbufs = maxbufs;
+	desc.wantloopback = wantloopback;
+
+	ret = sock_create_kern(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE,
+			       &desc.nlsock);
+	if (ret < 0) {
+		_leave(" = %d [sock]", ret);
+		return ret;
+	}
+
+	/* issue RTM_GETADDR */
+	desc.parse = afs_rtm_getaddr_parse;
+	ret = afs_rtm_getaddr(&desc);
+	if (ret < 0)
+		goto error;
+	ret = afs_read_rtm(&desc);
+	if (ret < 0)
+		goto error;
+
+	/* issue RTM_GETLINK */
+	desc.parse = afs_rtm_getlink_if_parse;
+	ret = afs_rtm_getlink(&desc);
+	if (ret < 0)
+		goto error;
+	ret = afs_read_rtm(&desc);
+	if (ret < 0)
+		goto error;
+
+	afs_cull_interfaces(&desc);
+	ret = desc.nbufs;
+
+	for (loop = 0; loop < ret; loop++)
+		_debug("[%d] "NIPQUAD_FMT"/"NIPQUAD_FMT" mtu %u",
+		       bufs[loop].index,
+		       NIPQUAD(bufs[loop].address),
+		       NIPQUAD(bufs[loop].netmask),
+		       bufs[loop].mtu);
+
+error:
+	kfree(desc.data);
+	sock_release(desc.nlsock);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * get a MAC address from a random ethernet interface that has a real one
+ * - the buffer should be 6 bytes in size
+ */
+int afs_get_MAC_address(u8 mac[6])
+{
+	struct afs_rtm_desc desc;
+	int ret;
+
+	_enter("");
+
+	memset(&desc, 0, sizeof(desc));
+	desc.mac = mac;
+	desc.mac_index = UINT_MAX;
+
+	ret = sock_create_kern(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE,
+			       &desc.nlsock);
+	if (ret < 0) {
+		_leave(" = %d [sock]", ret);
+		return ret;
+	}
+
+	/* issue RTM_GETLINK */
+	desc.parse = afs_rtm_getlink_mac_parse;
+	ret = afs_rtm_getlink(&desc);
+	if (ret < 0)
+		goto error;
+	ret = afs_read_rtm(&desc);
+	if (ret < 0)
+		goto error;
+
+	if (desc.mac_index < UINT_MAX) {
+		/* got a MAC address */
+		_debug("[%d] %02x:%02x:%02x:%02x:%02x:%02x",
+		       desc.mac_index,
+		       mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+	} else {
+		ret = -ENONET;
+	}
+
+error:
+	sock_release(desc.nlsock);
+	_leave(" = %d", ret);
+	return ret;
+}
diff --git a/fs/afs/vlclient.c b/fs/afs/vlclient.c
index 7b0e319..36c1306 100644
--- a/fs/afs/vlclient.c
+++ b/fs/afs/vlclient.c
@@ -1,4 +1,4 @@
-/* vlclient.c: AFS Volume Location Service client
+/* AFS Volume Location Service client
  *
  * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
@@ -11,685 +11,208 @@
 
 #include <linux/init.h>
 #include <linux/sched.h>
-#include <rxrpc/rxrpc.h>
-#include <rxrpc/transport.h>
-#include <rxrpc/connection.h>
-#include <rxrpc/call.h>
-#include "server.h"
-#include "volume.h"
-#include "vlclient.h"
-#include "kafsasyncd.h"
-#include "kafstimod.h"
-#include "errors.h"
 #include "internal.h"
 
-#define VLGETENTRYBYID		503	/* AFS Get Cache Entry By ID operation ID */
-#define VLGETENTRYBYNAME	504	/* AFS Get Cache Entry By Name operation ID */
-#define VLPROBE			514	/* AFS Probe Volume Location Service operation ID */
-
-static void afs_rxvl_get_entry_by_id_attn(struct rxrpc_call *call);
-static void afs_rxvl_get_entry_by_id_error(struct rxrpc_call *call);
-
-/*****************************************************************************/
 /*
- * map afs VL abort codes to/from Linux error codes
- * - called with call->lock held
+ * map volume locator abort codes to error codes
  */
-static void afs_rxvl_aemap(struct rxrpc_call *call)
+static int afs_vl_abort_to_error(u32 abort_code)
 {
-	int err;
+	_enter("%u", abort_code);
 
-	_enter("{%u,%u,%d}",
-	       call->app_err_state, call->app_abort_code, call->app_errno);
-
-	switch (call->app_err_state) {
-	case RXRPC_ESTATE_LOCAL_ABORT:
-		call->app_abort_code = -call->app_errno;
-		return;
-
-	case RXRPC_ESTATE_PEER_ABORT:
-		switch (call->app_abort_code) {
-		case AFSVL_IDEXIST:		err = -EEXIST;		break;
-		case AFSVL_IO:			err = -EREMOTEIO;	break;
-		case AFSVL_NAMEEXIST:		err = -EEXIST;		break;
-		case AFSVL_CREATEFAIL:		err = -EREMOTEIO;	break;
-		case AFSVL_NOENT:		err = -ENOMEDIUM;	break;
-		case AFSVL_EMPTY:		err = -ENOMEDIUM;	break;
-		case AFSVL_ENTDELETED:		err = -ENOMEDIUM;	break;
-		case AFSVL_BADNAME:		err = -EINVAL;		break;
-		case AFSVL_BADINDEX:		err = -EINVAL;		break;
-		case AFSVL_BADVOLTYPE:		err = -EINVAL;		break;
-		case AFSVL_BADSERVER:		err = -EINVAL;		break;
-		case AFSVL_BADPARTITION:	err = -EINVAL;		break;
-		case AFSVL_REPSFULL:		err = -EFBIG;		break;
-		case AFSVL_NOREPSERVER:		err = -ENOENT;		break;
-		case AFSVL_DUPREPSERVER:	err = -EEXIST;		break;
-		case AFSVL_RWNOTFOUND:		err = -ENOENT;		break;
-		case AFSVL_BADREFCOUNT:		err = -EINVAL;		break;
-		case AFSVL_SIZEEXCEEDED:	err = -EINVAL;		break;
-		case AFSVL_BADENTRY:		err = -EINVAL;		break;
-		case AFSVL_BADVOLIDBUMP:	err = -EINVAL;		break;
-		case AFSVL_IDALREADYHASHED:	err = -EINVAL;		break;
-		case AFSVL_ENTRYLOCKED:		err = -EBUSY;		break;
-		case AFSVL_BADVOLOPER:		err = -EBADRQC;		break;
-		case AFSVL_BADRELLOCKTYPE:	err = -EINVAL;		break;
-		case AFSVL_RERELEASE:		err = -EREMOTEIO;	break;
-		case AFSVL_BADSERVERFLAG:	err = -EINVAL;		break;
-		case AFSVL_PERM:		err = -EACCES;		break;
-		case AFSVL_NOMEM:		err = -EREMOTEIO;	break;
-		default:
-			err = afs_abort_to_error(call->app_abort_code);
-			break;
-		}
-		call->app_errno = err;
-		return;
-
+	switch (abort_code) {
+	case AFSVL_IDEXIST:		return -EEXIST;
+	case AFSVL_IO:			return -EREMOTEIO;
+	case AFSVL_NAMEEXIST:		return -EEXIST;
+	case AFSVL_CREATEFAIL:		return -EREMOTEIO;
+	case AFSVL_NOENT:		return -ENOMEDIUM;
+	case AFSVL_EMPTY:		return -ENOMEDIUM;
+	case AFSVL_ENTDELETED:		return -ENOMEDIUM;
+	case AFSVL_BADNAME:		return -EINVAL;
+	case AFSVL_BADINDEX:		return -EINVAL;
+	case AFSVL_BADVOLTYPE:		return -EINVAL;
+	case AFSVL_BADSERVER:		return -EINVAL;
+	case AFSVL_BADPARTITION:	return -EINVAL;
+	case AFSVL_REPSFULL:		return -EFBIG;
+	case AFSVL_NOREPSERVER:		return -ENOENT;
+	case AFSVL_DUPREPSERVER:	return -EEXIST;
+	case AFSVL_RWNOTFOUND:		return -ENOENT;
+	case AFSVL_BADREFCOUNT:		return -EINVAL;
+	case AFSVL_SIZEEXCEEDED:	return -EINVAL;
+	case AFSVL_BADENTRY:		return -EINVAL;
+	case AFSVL_BADVOLIDBUMP:	return -EINVAL;
+	case AFSVL_IDALREADYHASHED:	return -EINVAL;
+	case AFSVL_ENTRYLOCKED:		return -EBUSY;
+	case AFSVL_BADVOLOPER:		return -EBADRQC;
+	case AFSVL_BADRELLOCKTYPE:	return -EINVAL;
+	case AFSVL_RERELEASE:		return -EREMOTEIO;
+	case AFSVL_BADSERVERFLAG:	return -EINVAL;
+	case AFSVL_PERM:		return -EACCES;
+	case AFSVL_NOMEM:		return -EREMOTEIO;
 	default:
-		return;
+		return afs_abort_to_error(abort_code);
 	}
-} /* end afs_rxvl_aemap() */
+}
 
-#if 0
-/*****************************************************************************/
 /*
- * probe a volume location server to see if it is still alive -- unused
+ * deliver reply data to a VL.GetEntryByXXX call
  */
-static int afs_rxvl_probe(struct afs_server *server, int alloc_flags)
+static int afs_deliver_vl_get_entry_by_xxx(struct afs_call *call,
+					   struct sk_buff *skb, bool last)
 {
-	struct rxrpc_connection *conn;
-	struct rxrpc_call *call;
-	struct kvec piov[1];
-	size_t sent;
-	int ret;
-	__be32 param[1];
-
-	DECLARE_WAITQUEUE(myself, current);
-
-	/* get hold of the vlserver connection */
-	ret = afs_server_get_vlconn(server, &conn);
-	if (ret < 0)
-		goto out;
-
-	/* create a call through that connection */
-	ret = rxrpc_create_call(conn, NULL, NULL, afs_rxvl_aemap, &call);
-	if (ret < 0) {
-		printk("kAFS: Unable to create call: %d\n", ret);
-		goto out_put_conn;
-	}
-	call->app_opcode = VLPROBE;
-
-	/* we want to get event notifications from the call */
-	add_wait_queue(&call->waitq, &myself);
-
-	/* marshall the parameters */
-	param[0] = htonl(VLPROBE);
-	piov[0].iov_len = sizeof(param);
-	piov[0].iov_base = param;
-
-	/* send the parameters to the server */
-	ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET,
-				    alloc_flags, 0, &sent);
-	if (ret < 0)
-		goto abort;
-
-	/* wait for the reply to completely arrive */
-	for (;;) {
-		set_current_state(TASK_INTERRUPTIBLE);
-		if (call->app_call_state != RXRPC_CSTATE_CLNT_RCV_REPLY ||
-		    signal_pending(current))
-			break;
-		schedule();
-	}
-	set_current_state(TASK_RUNNING);
-
-	ret = -EINTR;
-	if (signal_pending(current))
-		goto abort;
-
-	switch (call->app_call_state) {
-	case RXRPC_CSTATE_ERROR:
-		ret = call->app_errno;
-		goto out_unwait;
-
-	case RXRPC_CSTATE_CLNT_GOT_REPLY:
-		ret = 0;
-		goto out_unwait;
-
-	default:
-		BUG();
-	}
-
- abort:
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	rxrpc_call_abort(call, ret);
-	schedule();
-
- out_unwait:
-	set_current_state(TASK_RUNNING);
-	remove_wait_queue(&call->waitq, &myself);
-	rxrpc_put_call(call);
- out_put_conn:
-	rxrpc_put_connection(conn);
- out:
-	return ret;
-
-} /* end afs_rxvl_probe() */
-#endif
-
-/*****************************************************************************/
-/*
- * look up a volume location database entry by name
- */
-int afs_rxvl_get_entry_by_name(struct afs_server *server,
-			       const char *volname,
-			       unsigned volnamesz,
-			       struct afs_cache_vlocation *entry)
-{
-	DECLARE_WAITQUEUE(myself, current);
-
-	struct rxrpc_connection *conn;
-	struct rxrpc_call *call;
-	struct kvec piov[3];
-	unsigned tmp;
-	size_t sent;
-	int ret, loop;
-	__be32 *bp, param[2], zero;
-
-	_enter(",%*.*s,%u,", volnamesz, volnamesz, volname, volnamesz);
-
-	memset(entry, 0, sizeof(*entry));
-
-	/* get hold of the vlserver connection */
-	ret = afs_server_get_vlconn(server, &conn);
-	if (ret < 0)
-		goto out;
-
-	/* create a call through that connection */
-	ret = rxrpc_create_call(conn, NULL, NULL, afs_rxvl_aemap, &call);
-	if (ret < 0) {
-		printk("kAFS: Unable to create call: %d\n", ret);
-		goto out_put_conn;
-	}
-	call->app_opcode = VLGETENTRYBYNAME;
-
-	/* we want to get event notifications from the call */
-	add_wait_queue(&call->waitq, &myself);
-
-	/* marshall the parameters */
-	piov[1].iov_len = volnamesz;
-	piov[1].iov_base = (char *) volname;
-
-	zero = 0;
-	piov[2].iov_len = (4 - (piov[1].iov_len & 3)) & 3;
-	piov[2].iov_base = &zero;
-
-	param[0] = htonl(VLGETENTRYBYNAME);
-	param[1] = htonl(piov[1].iov_len);
-
-	piov[0].iov_len = sizeof(param);
-	piov[0].iov_base = param;
-
-	/* send the parameters to the server */
-	ret = rxrpc_call_write_data(call, 3, piov, RXRPC_LAST_PACKET, GFP_NOFS,
-				    0, &sent);
-	if (ret < 0)
-		goto abort;
-
-	/* wait for the reply to completely arrive */
-	bp = rxrpc_call_alloc_scratch(call, 384);
-
-	ret = rxrpc_call_read_data(call, bp, 384,
-				   RXRPC_CALL_READ_BLOCK |
-				   RXRPC_CALL_READ_ALL);
-	if (ret < 0) {
-		if (ret == -ECONNABORTED) {
-			ret = call->app_errno;
-			goto out_unwait;
-		}
-		goto abort;
-	}
-
-	/* unmarshall the reply */
-	for (loop = 0; loop < 64; loop++)
-		entry->name[loop] = ntohl(*bp++);
-	bp++; /* final NUL */
-
-	bp++; /* type */
-	entry->nservers = ntohl(*bp++);
-
-	for (loop = 0; loop < 8; loop++)
-		entry->servers[loop].s_addr = *bp++;
-
-	bp += 8; /* partition IDs */
-
-	for (loop = 0; loop < 8; loop++) {
-		tmp = ntohl(*bp++);
-		if (tmp & AFS_VLSF_RWVOL)
-			entry->srvtmask[loop] |= AFS_VOL_VTM_RW;
-		if (tmp & AFS_VLSF_ROVOL)
-			entry->srvtmask[loop] |= AFS_VOL_VTM_RO;
-		if (tmp & AFS_VLSF_BACKVOL)
-			entry->srvtmask[loop] |= AFS_VOL_VTM_BAK;
-	}
-
-	entry->vid[0] = ntohl(*bp++);
-	entry->vid[1] = ntohl(*bp++);
-	entry->vid[2] = ntohl(*bp++);
-
-	bp++; /* clone ID */
-
-	tmp = ntohl(*bp++); /* flags */
-	if (tmp & AFS_VLF_RWEXISTS)
-		entry->vidmask |= AFS_VOL_VTM_RW;
-	if (tmp & AFS_VLF_ROEXISTS)
-		entry->vidmask |= AFS_VOL_VTM_RO;
-	if (tmp & AFS_VLF_BACKEXISTS)
-		entry->vidmask |= AFS_VOL_VTM_BAK;
-
-	ret = -ENOMEDIUM;
-	if (!entry->vidmask)
-		goto abort;
-
-	/* success */
-	entry->rtime = get_seconds();
-	ret = 0;
-
- out_unwait:
-	set_current_state(TASK_RUNNING);
-	remove_wait_queue(&call->waitq, &myself);
-	rxrpc_put_call(call);
- out_put_conn:
-	rxrpc_put_connection(conn);
- out:
-	_leave(" = %d", ret);
-	return ret;
-
- abort:
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	rxrpc_call_abort(call, ret);
-	schedule();
-	goto out_unwait;
-} /* end afs_rxvl_get_entry_by_name() */
-
-/*****************************************************************************/
-/*
- * look up a volume location database entry by ID
- */
-int afs_rxvl_get_entry_by_id(struct afs_server *server,
-			     afs_volid_t volid,
-			     afs_voltype_t voltype,
-			     struct afs_cache_vlocation *entry)
-{
-	DECLARE_WAITQUEUE(myself, current);
-
-	struct rxrpc_connection *conn;
-	struct rxrpc_call *call;
-	struct kvec piov[1];
-	unsigned tmp;
-	size_t sent;
-	int ret, loop;
-	__be32 *bp, param[3];
-
-	_enter(",%x,%d,", volid, voltype);
-
-	memset(entry, 0, sizeof(*entry));
-
-	/* get hold of the vlserver connection */
-	ret = afs_server_get_vlconn(server, &conn);
-	if (ret < 0)
-		goto out;
-
-	/* create a call through that connection */
-	ret = rxrpc_create_call(conn, NULL, NULL, afs_rxvl_aemap, &call);
-	if (ret < 0) {
-		printk("kAFS: Unable to create call: %d\n", ret);
-		goto out_put_conn;
-	}
-	call->app_opcode = VLGETENTRYBYID;
-
-	/* we want to get event notifications from the call */
-	add_wait_queue(&call->waitq, &myself);
-
-	/* marshall the parameters */
-	param[0] = htonl(VLGETENTRYBYID);
-	param[1] = htonl(volid);
-	param[2] = htonl(voltype);
-
-	piov[0].iov_len = sizeof(param);
-	piov[0].iov_base = param;
-
-	/* send the parameters to the server */
-	ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS,
-				    0, &sent);
-	if (ret < 0)
-		goto abort;
-
-	/* wait for the reply to completely arrive */
-	bp = rxrpc_call_alloc_scratch(call, 384);
-
-	ret = rxrpc_call_read_data(call, bp, 384,
-				   RXRPC_CALL_READ_BLOCK |
-				   RXRPC_CALL_READ_ALL);
-	if (ret < 0) {
-		if (ret == -ECONNABORTED) {
-			ret = call->app_errno;
-			goto out_unwait;
-		}
-		goto abort;
-	}
-
-	/* unmarshall the reply */
-	for (loop = 0; loop < 64; loop++)
-		entry->name[loop] = ntohl(*bp++);
-	bp++; /* final NUL */
-
-	bp++; /* type */
-	entry->nservers = ntohl(*bp++);
-
-	for (loop = 0; loop < 8; loop++)
-		entry->servers[loop].s_addr = *bp++;
-
-	bp += 8; /* partition IDs */
-
-	for (loop = 0; loop < 8; loop++) {
-		tmp = ntohl(*bp++);
-		if (tmp & AFS_VLSF_RWVOL)
-			entry->srvtmask[loop] |= AFS_VOL_VTM_RW;
-		if (tmp & AFS_VLSF_ROVOL)
-			entry->srvtmask[loop] |= AFS_VOL_VTM_RO;
-		if (tmp & AFS_VLSF_BACKVOL)
-			entry->srvtmask[loop] |= AFS_VOL_VTM_BAK;
-	}
-
-	entry->vid[0] = ntohl(*bp++);
-	entry->vid[1] = ntohl(*bp++);
-	entry->vid[2] = ntohl(*bp++);
-
-	bp++; /* clone ID */
-
-	tmp = ntohl(*bp++); /* flags */
-	if (tmp & AFS_VLF_RWEXISTS)
-		entry->vidmask |= AFS_VOL_VTM_RW;
-	if (tmp & AFS_VLF_ROEXISTS)
-		entry->vidmask |= AFS_VOL_VTM_RO;
-	if (tmp & AFS_VLF_BACKEXISTS)
-		entry->vidmask |= AFS_VOL_VTM_BAK;
-
-	ret = -ENOMEDIUM;
-	if (!entry->vidmask)
-		goto abort;
-
-#if 0 /* TODO: remove */
-	entry->nservers = 3;
-	entry->servers[0].s_addr = htonl(0xac101249);
-	entry->servers[1].s_addr = htonl(0xac101243);
-	entry->servers[2].s_addr = htonl(0xac10125b /*0xac10125b*/);
-
-	entry->srvtmask[0] = AFS_VOL_VTM_RO;
-	entry->srvtmask[1] = AFS_VOL_VTM_RO;
-	entry->srvtmask[2] = AFS_VOL_VTM_RO | AFS_VOL_VTM_RW;
-#endif
-
-	/* success */
-	entry->rtime = get_seconds();
-	ret = 0;
-
- out_unwait:
-	set_current_state(TASK_RUNNING);
-	remove_wait_queue(&call->waitq, &myself);
-	rxrpc_put_call(call);
- out_put_conn:
-	rxrpc_put_connection(conn);
- out:
-	_leave(" = %d", ret);
-	return ret;
-
- abort:
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	rxrpc_call_abort(call, ret);
-	schedule();
-	goto out_unwait;
-} /* end afs_rxvl_get_entry_by_id() */
-
-/*****************************************************************************/
-/*
- * look up a volume location database entry by ID asynchronously
- */
-int afs_rxvl_get_entry_by_id_async(struct afs_async_op *op,
-				   afs_volid_t volid,
-				   afs_voltype_t voltype)
-{
-	struct rxrpc_connection *conn;
-	struct rxrpc_call *call;
-	struct kvec piov[1];
-	size_t sent;
-	int ret;
-	__be32 param[3];
-
-	_enter(",%x,%d,", volid, voltype);
-
-	/* get hold of the vlserver connection */
-	ret = afs_server_get_vlconn(op->server, &conn);
-	if (ret < 0) {
-		_leave(" = %d", ret);
-		return ret;
-	}
-
-	/* create a call through that connection */
-	ret = rxrpc_create_call(conn,
-				afs_rxvl_get_entry_by_id_attn,
-				afs_rxvl_get_entry_by_id_error,
-				afs_rxvl_aemap,
-				&op->call);
-	rxrpc_put_connection(conn);
-
-	if (ret < 0) {
-		printk("kAFS: Unable to create call: %d\n", ret);
-		_leave(" = %d", ret);
-		return ret;
-	}
-
-	op->call->app_opcode = VLGETENTRYBYID;
-	op->call->app_user = op;
-
-	call = op->call;
-	rxrpc_get_call(call);
-
-	/* send event notifications from the call to kafsasyncd */
-	afs_kafsasyncd_begin_op(op);
-
-	/* marshall the parameters */
-	param[0] = htonl(VLGETENTRYBYID);
-	param[1] = htonl(volid);
-	param[2] = htonl(voltype);
-
-	piov[0].iov_len = sizeof(param);
-	piov[0].iov_base = param;
-
-	/* allocate result read buffer in scratch space */
-	call->app_scr_ptr = rxrpc_call_alloc_scratch(op->call, 384);
-
-	/* send the parameters to the server */
-	ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS,
-				    0, &sent);
-	if (ret < 0) {
-		rxrpc_call_abort(call, ret); /* handle from kafsasyncd */
-		ret = 0;
-		goto out;
-	}
-
-	/* wait for the reply to completely arrive */
-	ret = rxrpc_call_read_data(call, call->app_scr_ptr, 384, 0);
-	switch (ret) {
-	case 0:
-	case -EAGAIN:
-	case -ECONNABORTED:
-		ret = 0;
-		break;	/* all handled by kafsasyncd */
-
-	default:
-		rxrpc_call_abort(call, ret); /* make kafsasyncd handle it */
-		ret = 0;
-		break;
-	}
-
- out:
-	rxrpc_put_call(call);
-	_leave(" = %d", ret);
-	return ret;
-
-} /* end afs_rxvl_get_entry_by_id_async() */
-
-/*****************************************************************************/
-/*
- * attend to the asynchronous get VLDB entry by ID
- */
-int afs_rxvl_get_entry_by_id_async2(struct afs_async_op *op,
-				    struct afs_cache_vlocation *entry)
-{
+	struct afs_cache_vlocation *entry;
 	__be32 *bp;
-	__u32 tmp;
-	int loop, ret;
+	u32 tmp;
+	int loop;
 
-	_enter("{op=%p cst=%u}", op, op->call->app_call_state);
+	_enter(",,%u", last);
 
-	memset(entry, 0, sizeof(*entry));
+	afs_transfer_reply(call, skb);
+	if (!last)
+		return 0;
 
-	if (op->call->app_call_state == RXRPC_CSTATE_COMPLETE) {
-		/* operation finished */
-		afs_kafsasyncd_terminate_op(op);
+	if (call->reply_size != call->reply_max)
+		return -EBADMSG;
 
-		bp = op->call->app_scr_ptr;
+	/* unmarshall the reply once we've received all of it */
+	entry = call->reply;
+	bp = call->buffer;
 
-		/* unmarshall the reply */
-		for (loop = 0; loop < 64; loop++)
-			entry->name[loop] = ntohl(*bp++);
-		bp++; /* final NUL */
+	for (loop = 0; loop < 64; loop++)
+		entry->name[loop] = ntohl(*bp++);
+	entry->name[loop] = 0;
+	bp++; /* final NUL */
 
-		bp++; /* type */
-		entry->nservers = ntohl(*bp++);
+	bp++; /* type */
+	entry->nservers = ntohl(*bp++);
 
-		for (loop = 0; loop < 8; loop++)
-			entry->servers[loop].s_addr = *bp++;
+	for (loop = 0; loop < 8; loop++)
+		entry->servers[loop].s_addr = *bp++;
 
-		bp += 8; /* partition IDs */
+	bp += 8; /* partition IDs */
 
-		for (loop = 0; loop < 8; loop++) {
-			tmp = ntohl(*bp++);
-			if (tmp & AFS_VLSF_RWVOL)
-				entry->srvtmask[loop] |= AFS_VOL_VTM_RW;
-			if (tmp & AFS_VLSF_ROVOL)
-				entry->srvtmask[loop] |= AFS_VOL_VTM_RO;
-			if (tmp & AFS_VLSF_BACKVOL)
-				entry->srvtmask[loop] |= AFS_VOL_VTM_BAK;
-		}
-
-		entry->vid[0] = ntohl(*bp++);
-		entry->vid[1] = ntohl(*bp++);
-		entry->vid[2] = ntohl(*bp++);
-
-		bp++; /* clone ID */
-
-		tmp = ntohl(*bp++); /* flags */
-		if (tmp & AFS_VLF_RWEXISTS)
-			entry->vidmask |= AFS_VOL_VTM_RW;
-		if (tmp & AFS_VLF_ROEXISTS)
-			entry->vidmask |= AFS_VOL_VTM_RO;
-		if (tmp & AFS_VLF_BACKEXISTS)
-			entry->vidmask |= AFS_VOL_VTM_BAK;
-
-		ret = -ENOMEDIUM;
-		if (!entry->vidmask) {
-			rxrpc_call_abort(op->call, ret);
-			goto done;
-		}
-
-#if 0 /* TODO: remove */
-		entry->nservers = 3;
-		entry->servers[0].s_addr = htonl(0xac101249);
-		entry->servers[1].s_addr = htonl(0xac101243);
-		entry->servers[2].s_addr = htonl(0xac10125b /*0xac10125b*/);
-
-		entry->srvtmask[0] = AFS_VOL_VTM_RO;
-		entry->srvtmask[1] = AFS_VOL_VTM_RO;
-		entry->srvtmask[2] = AFS_VOL_VTM_RO | AFS_VOL_VTM_RW;
-#endif
-
-		/* success */
-		entry->rtime = get_seconds();
-		ret = 0;
-		goto done;
+	for (loop = 0; loop < 8; loop++) {
+		tmp = ntohl(*bp++);
+		entry->srvtmask[loop] = 0;
+		if (tmp & AFS_VLSF_RWVOL)
+			entry->srvtmask[loop] |= AFS_VOL_VTM_RW;
+		if (tmp & AFS_VLSF_ROVOL)
+			entry->srvtmask[loop] |= AFS_VOL_VTM_RO;
+		if (tmp & AFS_VLSF_BACKVOL)
+			entry->srvtmask[loop] |= AFS_VOL_VTM_BAK;
 	}
 
-	if (op->call->app_call_state == RXRPC_CSTATE_ERROR) {
-		/* operation error */
-		ret = op->call->app_errno;
-		goto done;
-	}
+	entry->vid[0] = ntohl(*bp++);
+	entry->vid[1] = ntohl(*bp++);
+	entry->vid[2] = ntohl(*bp++);
 
-	_leave(" = -EAGAIN");
-	return -EAGAIN;
+	bp++; /* clone ID */
 
- done:
-	rxrpc_put_call(op->call);
-	op->call = NULL;
-	_leave(" = %d", ret);
-	return ret;
-} /* end afs_rxvl_get_entry_by_id_async2() */
+	tmp = ntohl(*bp++); /* flags */
+	entry->vidmask = 0;
+	if (tmp & AFS_VLF_RWEXISTS)
+		entry->vidmask |= AFS_VOL_VTM_RW;
+	if (tmp & AFS_VLF_ROEXISTS)
+		entry->vidmask |= AFS_VOL_VTM_RO;
+	if (tmp & AFS_VLF_BACKEXISTS)
+		entry->vidmask |= AFS_VOL_VTM_BAK;
+	if (!entry->vidmask)
+		return -EBADMSG;
 
-/*****************************************************************************/
+	_leave(" = 0 [done]");
+	return 0;
+}
+
 /*
- * handle attention events on an async get-entry-by-ID op
- * - called from krxiod
+ * VL.GetEntryByName operation type
  */
-static void afs_rxvl_get_entry_by_id_attn(struct rxrpc_call *call)
-{
-	struct afs_async_op *op = call->app_user;
+static const struct afs_call_type afs_RXVLGetEntryByName = {
+	.name		= "VL.GetEntryByName",
+	.deliver	= afs_deliver_vl_get_entry_by_xxx,
+	.abort_to_error	= afs_vl_abort_to_error,
+	.destructor	= afs_flat_call_destructor,
+};
 
-	_enter("{op=%p cst=%u}", op, call->app_call_state);
-
-	switch (call->app_call_state) {
-	case RXRPC_CSTATE_COMPLETE:
-		afs_kafsasyncd_attend_op(op);
-		break;
-	case RXRPC_CSTATE_CLNT_RCV_REPLY:
-		if (call->app_async_read)
-			break;
-	case RXRPC_CSTATE_CLNT_GOT_REPLY:
-		if (call->app_read_count == 0)
-			break;
-		printk("kAFS: Reply bigger than expected"
-		       " {cst=%u asyn=%d mark=%Zu rdy=%Zu pr=%u%s}",
-		       call->app_call_state,
-		       call->app_async_read,
-		       call->app_mark,
-		       call->app_ready_qty,
-		       call->pkt_rcv_count,
-		       call->app_last_rcv ? " last" : "");
-
-		rxrpc_call_abort(call, -EBADMSG);
-		break;
-	default:
-		BUG();
-	}
-
-	_leave("");
-
-} /* end afs_rxvl_get_entry_by_id_attn() */
-
-/*****************************************************************************/
 /*
- * handle error events on an async get-entry-by-ID op
- * - called from krxiod
+ * VL.GetEntryById operation type
  */
-static void afs_rxvl_get_entry_by_id_error(struct rxrpc_call *call)
+static const struct afs_call_type afs_RXVLGetEntryById = {
+	.name		= "VL.GetEntryById",
+	.deliver	= afs_deliver_vl_get_entry_by_xxx,
+	.abort_to_error	= afs_vl_abort_to_error,
+	.destructor	= afs_flat_call_destructor,
+};
+
+/*
+ * dispatch a get volume entry by name operation
+ */
+int afs_vl_get_entry_by_name(struct in_addr *addr,
+			     struct key *key,
+			     const char *volname,
+			     struct afs_cache_vlocation *entry,
+			     const struct afs_wait_mode *wait_mode)
 {
-	struct afs_async_op *op = call->app_user;
+	struct afs_call *call;
+	size_t volnamesz, reqsz, padsz;
+	__be32 *bp;
 
-	_enter("{op=%p cst=%u}", op, call->app_call_state);
+	_enter("");
 
-	afs_kafsasyncd_attend_op(op);
+	volnamesz = strlen(volname);
+	padsz = (4 - (volnamesz & 3)) & 3;
+	reqsz = 8 + volnamesz + padsz;
 
-	_leave("");
+	call = afs_alloc_flat_call(&afs_RXVLGetEntryByName, reqsz, 384);
+	if (!call)
+		return -ENOMEM;
 
-} /* end afs_rxvl_get_entry_by_id_error() */
+	call->key = key;
+	call->reply = entry;
+	call->service_id = VL_SERVICE;
+	call->port = htons(AFS_VL_PORT);
+
+	/* marshall the parameters */
+	bp = call->request;
+	*bp++ = htonl(VLGETENTRYBYNAME);
+	*bp++ = htonl(volnamesz);
+	memcpy(bp, volname, volnamesz);
+	if (padsz > 0)
+		memset((void *) bp + volnamesz, 0, padsz);
+
+	/* initiate the call */
+	return afs_make_call(addr, call, GFP_KERNEL, wait_mode);
+}
+
+/*
+ * dispatch a get volume entry by ID operation
+ */
+int afs_vl_get_entry_by_id(struct in_addr *addr,
+			   struct key *key,
+			   afs_volid_t volid,
+			   afs_voltype_t voltype,
+			   struct afs_cache_vlocation *entry,
+			   const struct afs_wait_mode *wait_mode)
+{
+	struct afs_call *call;
+	__be32 *bp;
+
+	_enter("");
+
+	call = afs_alloc_flat_call(&afs_RXVLGetEntryById, 12, 384);
+	if (!call)
+		return -ENOMEM;
+
+	call->key = key;
+	call->reply = entry;
+	call->service_id = VL_SERVICE;
+	call->port = htons(AFS_VL_PORT);
+
+	/* marshall the parameters */
+	bp = call->request;
+	*bp++ = htonl(VLGETENTRYBYID);
+	*bp++ = htonl(volid);
+	*bp   = htonl(voltype);
+
+	/* initiate the call */
+	return afs_make_call(addr, call, GFP_KERNEL, wait_mode);
+}
diff --git a/fs/afs/vlocation.c b/fs/afs/vlocation.c
index 782ee7c..6c8e95a 100644
--- a/fs/afs/vlocation.c
+++ b/fs/afs/vlocation.c
@@ -1,6 +1,6 @@
-/* vlocation.c: volume location management
+/* AFS volume location management
  *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
@@ -12,131 +12,61 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
-#include <linux/pagemap.h>
-#include "volume.h"
-#include "cell.h"
-#include "cmservice.h"
-#include "fsclient.h"
-#include "vlclient.h"
-#include "kafstimod.h"
-#include <rxrpc/connection.h>
 #include "internal.h"
 
-#define AFS_VLDB_TIMEOUT HZ*1000
+unsigned afs_vlocation_timeout = 10;	/* volume location timeout in seconds */
+unsigned afs_vlocation_update_timeout = 10 * 60;
 
-static void afs_vlocation_update_timer(struct afs_timer *timer);
-static void afs_vlocation_update_attend(struct afs_async_op *op);
-static void afs_vlocation_update_discard(struct afs_async_op *op);
-static void __afs_put_vlocation(struct afs_vlocation *vlocation);
+static void afs_vlocation_reaper(struct work_struct *);
+static void afs_vlocation_updater(struct work_struct *);
 
-static void __afs_vlocation_timeout(struct afs_timer *timer)
-{
-	struct afs_vlocation *vlocation =
-		list_entry(timer, struct afs_vlocation, timeout);
+static LIST_HEAD(afs_vlocation_updates);
+static LIST_HEAD(afs_vlocation_graveyard);
+static DEFINE_SPINLOCK(afs_vlocation_updates_lock);
+static DEFINE_SPINLOCK(afs_vlocation_graveyard_lock);
+static DECLARE_DELAYED_WORK(afs_vlocation_reap, afs_vlocation_reaper);
+static DECLARE_DELAYED_WORK(afs_vlocation_update, afs_vlocation_updater);
+static struct workqueue_struct *afs_vlocation_update_worker;
 
-	_debug("VL TIMEOUT [%s{u=%d}]",
-	       vlocation->vldb.name, atomic_read(&vlocation->usage));
-
-	afs_vlocation_do_timeout(vlocation);
-}
-
-static const struct afs_timer_ops afs_vlocation_timer_ops = {
-	.timed_out	= __afs_vlocation_timeout,
-};
-
-static const struct afs_timer_ops afs_vlocation_update_timer_ops = {
-	.timed_out	= afs_vlocation_update_timer,
-};
-
-static const struct afs_async_op_ops afs_vlocation_update_op_ops = {
-	.attend		= afs_vlocation_update_attend,
-	.discard	= afs_vlocation_update_discard,
-};
-
-static LIST_HEAD(afs_vlocation_update_pendq);	/* queue of VLs awaiting update */
-static struct afs_vlocation *afs_vlocation_update;	/* VL currently being updated */
-static DEFINE_SPINLOCK(afs_vlocation_update_lock); /* lock guarding update queue */
-
-#ifdef AFS_CACHING_SUPPORT
-static cachefs_match_val_t afs_vlocation_cache_match(void *target,
-						     const void *entry);
-static void afs_vlocation_cache_update(void *source, void *entry);
-
-struct cachefs_index_def afs_vlocation_cache_index_def = {
-	.name		= "vldb",
-	.data_size	= sizeof(struct afs_cache_vlocation),
-	.keys[0]	= { CACHEFS_INDEX_KEYS_ASCIIZ, 64 },
-	.match		= afs_vlocation_cache_match,
-	.update		= afs_vlocation_cache_update,
-};
-#endif
-
-/*****************************************************************************/
 /*
  * iterate through the VL servers in a cell until one of them admits knowing
  * about the volume in question
- * - caller must have cell->vl_sem write-locked
  */
-static int afs_vlocation_access_vl_by_name(struct afs_vlocation *vlocation,
-					   const char *name,
-					   unsigned namesz,
+static int afs_vlocation_access_vl_by_name(struct afs_vlocation *vl,
+					   struct key *key,
 					   struct afs_cache_vlocation *vldb)
 {
-	struct afs_server *server = NULL;
-	struct afs_cell *cell = vlocation->cell;
+	struct afs_cell *cell = vl->cell;
+	struct in_addr addr;
 	int count, ret;
 
-	_enter("%s,%*.*s,%u", cell->name, namesz, namesz, name, namesz);
+	_enter("%s,%s", cell->name, vl->vldb.name);
 
+	down_write(&vl->cell->vl_sem);
 	ret = -ENOMEDIUM;
 	for (count = cell->vl_naddrs; count > 0; count--) {
-		_debug("CellServ[%hu]: %08x",
-		       cell->vl_curr_svix,
-		       cell->vl_addrs[cell->vl_curr_svix].s_addr);
+		addr = cell->vl_addrs[cell->vl_curr_svix];
 
-		/* try and create a server */
-		ret = afs_server_lookup(cell,
-					&cell->vl_addrs[cell->vl_curr_svix],
-					&server);
-		switch (ret) {
-		case 0:
-			break;
-		case -ENOMEM:
-		case -ENONET:
-			goto out;
-		default:
-			goto rotate;
-		}
+		_debug("CellServ[%hu]: %08x", cell->vl_curr_svix, addr.s_addr);
 
 		/* attempt to access the VL server */
-		ret = afs_rxvl_get_entry_by_name(server, name, namesz, vldb);
+		ret = afs_vl_get_entry_by_name(&addr, key, vl->vldb.name, vldb,
+					       &afs_sync_call);
 		switch (ret) {
 		case 0:
-			afs_put_server(server);
 			goto out;
 		case -ENOMEM:
 		case -ENONET:
 		case -ENETUNREACH:
 		case -EHOSTUNREACH:
 		case -ECONNREFUSED:
-			down_write(&server->sem);
-			if (server->vlserver) {
-				rxrpc_put_connection(server->vlserver);
-				server->vlserver = NULL;
-			}
-			up_write(&server->sem);
-			afs_put_server(server);
 			if (ret == -ENOMEM || ret == -ENONET)
 				goto out;
 			goto rotate;
 		case -ENOMEDIUM:
-			afs_put_server(server);
 			goto out;
 		default:
-			afs_put_server(server);
-			ret = -ENOMEDIUM;
+			ret = -EIO;
 			goto rotate;
 		}
 
@@ -146,76 +76,66 @@
 		cell->vl_curr_svix %= cell->vl_naddrs;
 	}
 
- out:
+out:
+	up_write(&vl->cell->vl_sem);
 	_leave(" = %d", ret);
 	return ret;
+}
 
-} /* end afs_vlocation_access_vl_by_name() */
-
-/*****************************************************************************/
 /*
  * iterate through the VL servers in a cell until one of them admits knowing
  * about the volume in question
- * - caller must have cell->vl_sem write-locked
  */
-static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vlocation,
+static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vl,
+					 struct key *key,
 					 afs_volid_t volid,
 					 afs_voltype_t voltype,
 					 struct afs_cache_vlocation *vldb)
 {
-	struct afs_server *server = NULL;
-	struct afs_cell *cell = vlocation->cell;
+	struct afs_cell *cell = vl->cell;
+	struct in_addr addr;
 	int count, ret;
 
 	_enter("%s,%x,%d,", cell->name, volid, voltype);
 
+	down_write(&vl->cell->vl_sem);
 	ret = -ENOMEDIUM;
 	for (count = cell->vl_naddrs; count > 0; count--) {
-		_debug("CellServ[%hu]: %08x",
-		       cell->vl_curr_svix,
-		       cell->vl_addrs[cell->vl_curr_svix].s_addr);
+		addr = cell->vl_addrs[cell->vl_curr_svix];
 
-		/* try and create a server */
-		ret = afs_server_lookup(cell,
-					&cell->vl_addrs[cell->vl_curr_svix],
-					&server);
-		switch (ret) {
-		case 0:
-			break;
-		case -ENOMEM:
-		case -ENONET:
-			goto out;
-		default:
-			goto rotate;
-		}
+		_debug("CellServ[%hu]: %08x", cell->vl_curr_svix, addr.s_addr);
 
 		/* attempt to access the VL server */
-		ret = afs_rxvl_get_entry_by_id(server, volid, voltype, vldb);
+		ret = afs_vl_get_entry_by_id(&addr, key, volid, voltype, vldb,
+					     &afs_sync_call);
 		switch (ret) {
 		case 0:
-			afs_put_server(server);
 			goto out;
 		case -ENOMEM:
 		case -ENONET:
 		case -ENETUNREACH:
 		case -EHOSTUNREACH:
 		case -ECONNREFUSED:
-			down_write(&server->sem);
-			if (server->vlserver) {
-				rxrpc_put_connection(server->vlserver);
-				server->vlserver = NULL;
-			}
-			up_write(&server->sem);
-			afs_put_server(server);
 			if (ret == -ENOMEM || ret == -ENONET)
 				goto out;
 			goto rotate;
+		case -EBUSY:
+			vl->upd_busy_cnt++;
+			if (vl->upd_busy_cnt <= 3) {
+				if (vl->upd_busy_cnt > 1) {
+					/* second+ BUSY - sleep a little bit */
+					set_current_state(TASK_UNINTERRUPTIBLE);
+					schedule_timeout(1);
+					__set_current_state(TASK_RUNNING);
+				}
+				continue;
+			}
+			break;
 		case -ENOMEDIUM:
-			afs_put_server(server);
-			goto out;
+			vl->upd_rej_cnt++;
+			goto rotate;
 		default:
-			afs_put_server(server);
-			ret = -ENOMEDIUM;
+			ret = -EIO;
 			goto rotate;
 		}
 
@@ -223,729 +143,579 @@
 	rotate:
 		cell->vl_curr_svix++;
 		cell->vl_curr_svix %= cell->vl_naddrs;
+		vl->upd_busy_cnt = 0;
 	}
 
- out:
+out:
+	if (ret < 0 && vl->upd_rej_cnt > 0) {
+		printk(KERN_NOTICE "kAFS:"
+		       " Active volume no longer valid '%s'\n",
+		       vl->vldb.name);
+		vl->valid = 0;
+		ret = -ENOMEDIUM;
+	}
+
+	up_write(&vl->cell->vl_sem);
 	_leave(" = %d", ret);
 	return ret;
+}
 
-} /* end afs_vlocation_access_vl_by_id() */
+/*
+ * allocate a volume location record
+ */
+static struct afs_vlocation *afs_vlocation_alloc(struct afs_cell *cell,
+						 const char *name,
+						 size_t namesz)
+{
+	struct afs_vlocation *vl;
 
-/*****************************************************************************/
+	vl = kzalloc(sizeof(struct afs_vlocation), GFP_KERNEL);
+	if (vl) {
+		vl->cell = cell;
+		vl->state = AFS_VL_NEW;
+		atomic_set(&vl->usage, 1);
+		INIT_LIST_HEAD(&vl->link);
+		INIT_LIST_HEAD(&vl->grave);
+		INIT_LIST_HEAD(&vl->update);
+		init_waitqueue_head(&vl->waitq);
+		spin_lock_init(&vl->lock);
+		memcpy(vl->vldb.name, name, namesz);
+	}
+
+	_leave(" = %p", vl);
+	return vl;
+}
+
+/*
+ * update record if we found it in the cache
+ */
+static int afs_vlocation_update_record(struct afs_vlocation *vl,
+				       struct key *key,
+				       struct afs_cache_vlocation *vldb)
+{
+	afs_voltype_t voltype;
+	afs_volid_t vid;
+	int ret;
+
+	/* try to look up a cached volume in the cell VL databases by ID */
+	_debug("Locally Cached: %s %02x { %08x(%x) %08x(%x) %08x(%x) }",
+	       vl->vldb.name,
+	       vl->vldb.vidmask,
+	       ntohl(vl->vldb.servers[0].s_addr),
+	       vl->vldb.srvtmask[0],
+	       ntohl(vl->vldb.servers[1].s_addr),
+	       vl->vldb.srvtmask[1],
+	       ntohl(vl->vldb.servers[2].s_addr),
+	       vl->vldb.srvtmask[2]);
+
+	_debug("Vids: %08x %08x %08x",
+	       vl->vldb.vid[0],
+	       vl->vldb.vid[1],
+	       vl->vldb.vid[2]);
+
+	if (vl->vldb.vidmask & AFS_VOL_VTM_RW) {
+		vid = vl->vldb.vid[0];
+		voltype = AFSVL_RWVOL;
+	} else if (vl->vldb.vidmask & AFS_VOL_VTM_RO) {
+		vid = vl->vldb.vid[1];
+		voltype = AFSVL_ROVOL;
+	} else if (vl->vldb.vidmask & AFS_VOL_VTM_BAK) {
+		vid = vl->vldb.vid[2];
+		voltype = AFSVL_BACKVOL;
+	} else {
+		BUG();
+		vid = 0;
+		voltype = 0;
+	}
+
+	/* contact the server to make sure the volume is still available
+	 * - TODO: need to handle disconnected operation here
+	 */
+	ret = afs_vlocation_access_vl_by_id(vl, key, vid, voltype, vldb);
+	switch (ret) {
+		/* net error */
+	default:
+		printk(KERN_WARNING "kAFS:"
+		       " failed to update volume '%s' (%x) up in '%s': %d\n",
+		       vl->vldb.name, vid, vl->cell->name, ret);
+		_leave(" = %d", ret);
+		return ret;
+
+		/* pulled from local cache into memory */
+	case 0:
+		_leave(" = 0");
+		return 0;
+
+		/* uh oh... looks like the volume got deleted */
+	case -ENOMEDIUM:
+		printk(KERN_ERR "kAFS:"
+		       " volume '%s' (%x) does not exist '%s'\n",
+		       vl->vldb.name, vid, vl->cell->name);
+
+		/* TODO: make existing record unavailable */
+		_leave(" = %d", ret);
+		return ret;
+	}
+}
+
+/*
+ * apply the update to a VL record
+ */
+static void afs_vlocation_apply_update(struct afs_vlocation *vl,
+				       struct afs_cache_vlocation *vldb)
+{
+	_debug("Done VL Lookup: %s %02x { %08x(%x) %08x(%x) %08x(%x) }",
+	       vldb->name, vldb->vidmask,
+	       ntohl(vldb->servers[0].s_addr), vldb->srvtmask[0],
+	       ntohl(vldb->servers[1].s_addr), vldb->srvtmask[1],
+	       ntohl(vldb->servers[2].s_addr), vldb->srvtmask[2]);
+
+	_debug("Vids: %08x %08x %08x",
+	       vldb->vid[0], vldb->vid[1], vldb->vid[2]);
+
+	if (strcmp(vldb->name, vl->vldb.name) != 0)
+		printk(KERN_NOTICE "kAFS:"
+		       " name of volume '%s' changed to '%s' on server\n",
+		       vl->vldb.name, vldb->name);
+
+	vl->vldb = *vldb;
+
+#ifdef AFS_CACHING_SUPPORT
+	/* update volume entry in local cache */
+	cachefs_update_cookie(vl->cache);
+#endif
+}
+
+/*
+ * fill in a volume location record, consulting the cache and the VL server
+ * both
+ */
+static int afs_vlocation_fill_in_record(struct afs_vlocation *vl,
+					struct key *key)
+{
+	struct afs_cache_vlocation vldb;
+	int ret;
+
+	_enter("");
+
+	ASSERTCMP(vl->valid, ==, 0);
+
+	memset(&vldb, 0, sizeof(vldb));
+
+	/* see if we have an in-cache copy (will set vl->valid if there is) */
+#ifdef AFS_CACHING_SUPPORT
+	cachefs_acquire_cookie(cell->cache,
+			       &afs_volume_cache_index_def,
+			       vlocation,
+			       &vl->cache);
+#endif
+
+	if (vl->valid) {
+		/* try to update a known volume in the cell VL databases by
+		 * ID as the name may have changed */
+		_debug("found in cache");
+		ret = afs_vlocation_update_record(vl, key, &vldb);
+	} else {
+		/* try to look up an unknown volume in the cell VL databases by
+		 * name */
+		ret = afs_vlocation_access_vl_by_name(vl, key, &vldb);
+		if (ret < 0) {
+			printk("kAFS: failed to locate '%s' in cell '%s'\n",
+			       vl->vldb.name, vl->cell->name);
+			return ret;
+		}
+	}
+
+	afs_vlocation_apply_update(vl, &vldb);
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * queue a vlocation record for updates
+ */
+void afs_vlocation_queue_for_updates(struct afs_vlocation *vl)
+{
+	struct afs_vlocation *xvl;
+
+	/* wait at least 10 minutes before updating... */
+	vl->update_at = get_seconds() + afs_vlocation_update_timeout;
+
+	spin_lock(&afs_vlocation_updates_lock);
+
+	if (!list_empty(&afs_vlocation_updates)) {
+		/* ... but wait at least 1 second more than the newest record
+		 * already queued so that we don't spam the VL server suddenly
+		 * with lots of requests
+		 */
+		xvl = list_entry(afs_vlocation_updates.prev,
+				 struct afs_vlocation, update);
+		if (vl->update_at <= xvl->update_at)
+			vl->update_at = xvl->update_at + 1;
+	} else {
+		queue_delayed_work(afs_vlocation_update_worker,
+				   &afs_vlocation_update,
+				   afs_vlocation_update_timeout * HZ);
+	}
+
+	list_add_tail(&vl->update, &afs_vlocation_updates);
+	spin_unlock(&afs_vlocation_updates_lock);
+}
+
 /*
  * lookup volume location
- * - caller must have cell->vol_sem write-locked
  * - iterate through the VL servers in a cell until one of them admits knowing
  *   about the volume in question
  * - lookup in the local cache if not able to find on the VL server
  * - insert/update in the local cache if did get a VL response
  */
-int afs_vlocation_lookup(struct afs_cell *cell,
-			 const char *name,
-			 unsigned namesz,
-			 struct afs_vlocation **_vlocation)
+struct afs_vlocation *afs_vlocation_lookup(struct afs_cell *cell,
+					   struct key *key,
+					   const char *name,
+					   size_t namesz)
 {
-	struct afs_cache_vlocation vldb;
-	struct afs_vlocation *vlocation;
-	afs_voltype_t voltype;
-	afs_volid_t vid;
-	int active = 0, ret;
+	struct afs_vlocation *vl;
+	int ret;
 
-	_enter("{%s},%*.*s,%u,", cell->name, namesz, namesz, name, namesz);
+	_enter("{%s},{%x},%*.*s,%zu",
+	       cell->name, key_serial(key),
+	       (int) namesz, (int) namesz, name, namesz);
 
-	if (namesz > sizeof(vlocation->vldb.name)) {
+	if (namesz > sizeof(vl->vldb.name)) {
 		_leave(" = -ENAMETOOLONG");
-		return -ENAMETOOLONG;
+		return ERR_PTR(-ENAMETOOLONG);
 	}
 
-	/* search the cell's active list first */
-	list_for_each_entry(vlocation, &cell->vl_list, link) {
-		if (namesz < sizeof(vlocation->vldb.name) &&
-		    vlocation->vldb.name[namesz] != '\0')
+	/* see if we have an in-memory copy first */
+	down_write(&cell->vl_sem);
+	spin_lock(&cell->vl_lock);
+	list_for_each_entry(vl, &cell->vl_list, link) {
+		if (vl->vldb.name[namesz] != '\0')
 			continue;
-
-		if (memcmp(vlocation->vldb.name, name, namesz) == 0)
+		if (memcmp(vl->vldb.name, name, namesz) == 0)
 			goto found_in_memory;
 	}
-
-	/* search the cell's graveyard list second */
-	spin_lock(&cell->vl_gylock);
-	list_for_each_entry(vlocation, &cell->vl_graveyard, link) {
-		if (namesz < sizeof(vlocation->vldb.name) &&
-		    vlocation->vldb.name[namesz] != '\0')
-			continue;
-
-		if (memcmp(vlocation->vldb.name, name, namesz) == 0)
-			goto found_in_graveyard;
-	}
-	spin_unlock(&cell->vl_gylock);
+	spin_unlock(&cell->vl_lock);
 
 	/* not in the cell's in-memory lists - create a new record */
-	vlocation = kzalloc(sizeof(struct afs_vlocation), GFP_KERNEL);
-	if (!vlocation)
-		return -ENOMEM;
-
-	atomic_set(&vlocation->usage, 1);
-	INIT_LIST_HEAD(&vlocation->link);
-	rwlock_init(&vlocation->lock);
-	memcpy(vlocation->vldb.name, name, namesz);
-
-	afs_timer_init(&vlocation->timeout, &afs_vlocation_timer_ops);
-	afs_timer_init(&vlocation->upd_timer, &afs_vlocation_update_timer_ops);
-	afs_async_op_init(&vlocation->upd_op, &afs_vlocation_update_op_ops);
+	vl = afs_vlocation_alloc(cell, name, namesz);
+	if (!vl) {
+		up_write(&cell->vl_sem);
+		return ERR_PTR(-ENOMEM);
+	}
 
 	afs_get_cell(cell);
-	vlocation->cell = cell;
 
-	list_add_tail(&vlocation->link, &cell->vl_list);
+	list_add_tail(&vl->link, &cell->vl_list);
+	vl->state = AFS_VL_CREATING;
+	up_write(&cell->vl_sem);
 
-#ifdef AFS_CACHING_SUPPORT
-	/* we want to store it in the cache, plus it might already be
-	 * encached */
-	cachefs_acquire_cookie(cell->cache,
-			       &afs_volume_cache_index_def,
-			       vlocation,
-			       &vlocation->cache);
-
-	if (vlocation->valid)
-		goto found_in_cache;
-#endif
-
-	/* try to look up an unknown volume in the cell VL databases by name */
-	ret = afs_vlocation_access_vl_by_name(vlocation, name, namesz, &vldb);
-	if (ret < 0) {
-		printk("kAFS: failed to locate '%*.*s' in cell '%s'\n",
-		       namesz, namesz, name, cell->name);
-		goto error;
-	}
-
-	goto found_on_vlserver;
-
- found_in_graveyard:
-	/* found in the graveyard - resurrect */
-	_debug("found in graveyard");
-	atomic_inc(&vlocation->usage);
-	list_move_tail(&vlocation->link, &cell->vl_list);
-	spin_unlock(&cell->vl_gylock);
-
-	afs_kafstimod_del_timer(&vlocation->timeout);
-	goto active;
-
- found_in_memory:
-	/* found in memory - check to see if it's active */
-	_debug("found in memory");
-	atomic_inc(&vlocation->usage);
-
- active:
-	active = 1;
-
-#ifdef AFS_CACHING_SUPPORT
- found_in_cache:
-#endif
-	/* try to look up a cached volume in the cell VL databases by ID */
-	_debug("found in cache");
-
-	_debug("Locally Cached: %s %02x { %08x(%x) %08x(%x) %08x(%x) }",
-	       vlocation->vldb.name,
-	       vlocation->vldb.vidmask,
-	       ntohl(vlocation->vldb.servers[0].s_addr),
-	       vlocation->vldb.srvtmask[0],
-	       ntohl(vlocation->vldb.servers[1].s_addr),
-	       vlocation->vldb.srvtmask[1],
-	       ntohl(vlocation->vldb.servers[2].s_addr),
-	       vlocation->vldb.srvtmask[2]
-	       );
-
-	_debug("Vids: %08x %08x %08x",
-	       vlocation->vldb.vid[0],
-	       vlocation->vldb.vid[1],
-	       vlocation->vldb.vid[2]);
-
-	if (vlocation->vldb.vidmask & AFS_VOL_VTM_RW) {
-		vid = vlocation->vldb.vid[0];
-		voltype = AFSVL_RWVOL;
-	}
-	else if (vlocation->vldb.vidmask & AFS_VOL_VTM_RO) {
-		vid = vlocation->vldb.vid[1];
-		voltype = AFSVL_ROVOL;
-	}
-	else if (vlocation->vldb.vidmask & AFS_VOL_VTM_BAK) {
-		vid = vlocation->vldb.vid[2];
-		voltype = AFSVL_BACKVOL;
-	}
-	else {
-		BUG();
-		vid = 0;
-		voltype = 0;
-	}
-
-	ret = afs_vlocation_access_vl_by_id(vlocation, vid, voltype, &vldb);
-	switch (ret) {
-		/* net error */
-	default:
-		printk("kAFS: failed to volume '%*.*s' (%x) up in '%s': %d\n",
-		       namesz, namesz, name, vid, cell->name, ret);
-		goto error;
-
-		/* pulled from local cache into memory */
-	case 0:
-		goto found_on_vlserver;
-
-		/* uh oh... looks like the volume got deleted */
-	case -ENOMEDIUM:
-		printk("kAFS: volume '%*.*s' (%x) does not exist '%s'\n",
-		       namesz, namesz, name, vid, cell->name);
-
-		/* TODO: make existing record unavailable */
-		goto error;
-	}
-
- found_on_vlserver:
-	_debug("Done VL Lookup: %*.*s %02x { %08x(%x) %08x(%x) %08x(%x) }",
-	       namesz, namesz, name,
-	       vldb.vidmask,
-	       ntohl(vldb.servers[0].s_addr), vldb.srvtmask[0],
-	       ntohl(vldb.servers[1].s_addr), vldb.srvtmask[1],
-	       ntohl(vldb.servers[2].s_addr), vldb.srvtmask[2]
-	       );
-
-	_debug("Vids: %08x %08x %08x", vldb.vid[0], vldb.vid[1], vldb.vid[2]);
-
-	if ((namesz < sizeof(vlocation->vldb.name) &&
-	     vlocation->vldb.name[namesz] != '\0') ||
-	    memcmp(vldb.name, name, namesz) != 0)
-		printk("kAFS: name of volume '%*.*s' changed to '%s' on server\n",
-		       namesz, namesz, name, vldb.name);
-
-	memcpy(&vlocation->vldb, &vldb, sizeof(vlocation->vldb));
-
-	afs_kafstimod_add_timer(&vlocation->upd_timer, 10 * HZ);
-
-#ifdef AFS_CACHING_SUPPORT
-	/* update volume entry in local cache */
-	cachefs_update_cookie(vlocation->cache);
-#endif
-
-	*_vlocation = vlocation;
-	_leave(" = 0 (%p)",vlocation);
-	return 0;
-
- error:
-	if (vlocation) {
-		if (active) {
-			__afs_put_vlocation(vlocation);
-		}
-		else {
-			list_del(&vlocation->link);
-#ifdef AFS_CACHING_SUPPORT
-			cachefs_relinquish_cookie(vlocation->cache, 0);
-#endif
-			afs_put_cell(vlocation->cell);
-			kfree(vlocation);
-		}
-	}
-
-	_leave(" = %d", ret);
-	return ret;
-} /* end afs_vlocation_lookup() */
-
-/*****************************************************************************/
-/*
- * finish using a volume location record
- * - caller must have cell->vol_sem write-locked
- */
-static void __afs_put_vlocation(struct afs_vlocation *vlocation)
-{
-	struct afs_cell *cell;
-
-	if (!vlocation)
-		return;
-
-	_enter("%s", vlocation->vldb.name);
-
-	cell = vlocation->cell;
-
-	/* sanity check */
-	BUG_ON(atomic_read(&vlocation->usage) <= 0);
-
-	spin_lock(&cell->vl_gylock);
-	if (likely(!atomic_dec_and_test(&vlocation->usage))) {
-		spin_unlock(&cell->vl_gylock);
-		_leave("");
-		return;
-	}
-
-	/* move to graveyard queue */
-	list_move_tail(&vlocation->link,&cell->vl_graveyard);
-
-	/* remove from pending timeout queue (refcounted if actually being
-	 * updated) */
-	list_del_init(&vlocation->upd_op.link);
-
-	/* time out in 10 secs */
-	afs_kafstimod_del_timer(&vlocation->upd_timer);
-	afs_kafstimod_add_timer(&vlocation->timeout, 10 * HZ);
-
-	spin_unlock(&cell->vl_gylock);
-
-	_leave(" [killed]");
-} /* end __afs_put_vlocation() */
-
-/*****************************************************************************/
-/*
- * finish using a volume location record
- */
-void afs_put_vlocation(struct afs_vlocation *vlocation)
-{
-	if (vlocation) {
-		struct afs_cell *cell = vlocation->cell;
-
-		down_write(&cell->vl_sem);
-		__afs_put_vlocation(vlocation);
-		up_write(&cell->vl_sem);
-	}
-} /* end afs_put_vlocation() */
-
-/*****************************************************************************/
-/*
- * timeout vlocation record
- * - removes from the cell's graveyard if the usage count is zero
- */
-void afs_vlocation_do_timeout(struct afs_vlocation *vlocation)
-{
-	struct afs_cell *cell;
-
-	_enter("%s", vlocation->vldb.name);
-
-	cell = vlocation->cell;
-
-	BUG_ON(atomic_read(&vlocation->usage) < 0);
-
-	/* remove from graveyard if still dead */
-	spin_lock(&cell->vl_gylock);
-	if (atomic_read(&vlocation->usage) == 0)
-		list_del_init(&vlocation->link);
-	else
-		vlocation = NULL;
-	spin_unlock(&cell->vl_gylock);
-
-	if (!vlocation) {
-		_leave("");
-		return; /* resurrected */
-	}
-
-	/* we can now destroy it properly */
-#ifdef AFS_CACHING_SUPPORT
-	cachefs_relinquish_cookie(vlocation->cache, 0);
-#endif
-	afs_put_cell(cell);
-
-	kfree(vlocation);
-
-	_leave(" [destroyed]");
-} /* end afs_vlocation_do_timeout() */
-
-/*****************************************************************************/
-/*
- * send an update operation to the currently selected server
- */
-static int afs_vlocation_update_begin(struct afs_vlocation *vlocation)
-{
-	afs_voltype_t voltype;
-	afs_volid_t vid;
-	int ret;
-
-	_enter("%s{ufs=%u ucs=%u}",
-	       vlocation->vldb.name,
-	       vlocation->upd_first_svix,
-	       vlocation->upd_curr_svix);
-
-	/* try to look up a cached volume in the cell VL databases by ID */
-	if (vlocation->vldb.vidmask & AFS_VOL_VTM_RW) {
-		vid = vlocation->vldb.vid[0];
-		voltype = AFSVL_RWVOL;
-	}
-	else if (vlocation->vldb.vidmask & AFS_VOL_VTM_RO) {
-		vid = vlocation->vldb.vid[1];
-		voltype = AFSVL_ROVOL;
-	}
-	else if (vlocation->vldb.vidmask & AFS_VOL_VTM_BAK) {
-		vid = vlocation->vldb.vid[2];
-		voltype = AFSVL_BACKVOL;
-	}
-	else {
-		BUG();
-		vid = 0;
-		voltype = 0;
-	}
-
-	/* contact the chosen server */
-	ret = afs_server_lookup(
-		vlocation->cell,
-		&vlocation->cell->vl_addrs[vlocation->upd_curr_svix],
-		&vlocation->upd_op.server);
-
-	switch (ret) {
-	case 0:
-		break;
-	case -ENOMEM:
-	case -ENONET:
-	default:
-		_leave(" = %d", ret);
-		return ret;
-	}
-
-	/* initiate the update operation */
-	ret = afs_rxvl_get_entry_by_id_async(&vlocation->upd_op, vid, voltype);
-	if (ret < 0) {
-		_leave(" = %d", ret);
-		return ret;
-	}
-
-	_leave(" = %d", ret);
-	return ret;
-} /* end afs_vlocation_update_begin() */
-
-/*****************************************************************************/
-/*
- * abandon updating a VL record
- * - does not restart the update timer
- */
-static void afs_vlocation_update_abandon(struct afs_vlocation *vlocation,
-					 afs_vlocation_upd_t state,
-					 int ret)
-{
-	_enter("%s,%u", vlocation->vldb.name, state);
-
+fill_in_record:
+	ret = afs_vlocation_fill_in_record(vl, key);
 	if (ret < 0)
-		printk("kAFS: Abandoning VL update '%s': %d\n",
-		       vlocation->vldb.name, ret);
+		goto error_abandon;
+	spin_lock(&vl->lock);
+	vl->state = AFS_VL_VALID;
+	spin_unlock(&vl->lock);
+	wake_up(&vl->waitq);
 
-	/* discard the server record */
-	afs_put_server(vlocation->upd_op.server);
-	vlocation->upd_op.server = NULL;
+	/* schedule for regular updates */
+	afs_vlocation_queue_for_updates(vl);
+	goto success;
 
-	spin_lock(&afs_vlocation_update_lock);
-	afs_vlocation_update = NULL;
-	vlocation->upd_state = state;
+found_in_memory:
+	/* found in memory */
+	_debug("found in memory");
+	atomic_inc(&vl->usage);
+	spin_unlock(&cell->vl_lock);
+	if (!list_empty(&vl->grave)) {
+		spin_lock(&afs_vlocation_graveyard_lock);
+		list_del_init(&vl->grave);
+		spin_unlock(&afs_vlocation_graveyard_lock);
+	}
+	up_write(&cell->vl_sem);
 
-	/* TODO: start updating next VL record on pending list */
+	/* see if it was an abandoned record that we might try filling in */
+	spin_lock(&vl->lock);
+	while (vl->state != AFS_VL_VALID) {
+		afs_vlocation_state_t state = vl->state;
 
-	spin_unlock(&afs_vlocation_update_lock);
+		_debug("invalid [state %d]", state);
 
-	_leave("");
-} /* end afs_vlocation_update_abandon() */
-
-/*****************************************************************************/
-/*
- * handle periodic update timeouts and busy retry timeouts
- * - called from kafstimod
- */
-static void afs_vlocation_update_timer(struct afs_timer *timer)
-{
-	struct afs_vlocation *vlocation =
-		list_entry(timer, struct afs_vlocation, upd_timer);
-	int ret;
-
-	_enter("%s", vlocation->vldb.name);
-
-	/* only update if not in the graveyard (defend against putting too) */
-	spin_lock(&vlocation->cell->vl_gylock);
-
-	if (!atomic_read(&vlocation->usage))
-		goto out_unlock1;
-
-	spin_lock(&afs_vlocation_update_lock);
-
-	/* if we were woken up due to EBUSY sleep then restart immediately if
-	 * possible or else jump to front of pending queue */
-	if (vlocation->upd_state == AFS_VLUPD_BUSYSLEEP) {
-		if (afs_vlocation_update) {
-			list_add(&vlocation->upd_op.link,
-				 &afs_vlocation_update_pendq);
+		if (state == AFS_VL_NEW || state == AFS_VL_NO_VOLUME) {
+			vl->state = AFS_VL_CREATING;
+			spin_unlock(&vl->lock);
+			goto fill_in_record;
 		}
-		else {
-			afs_get_vlocation(vlocation);
-			afs_vlocation_update = vlocation;
-			vlocation->upd_state = AFS_VLUPD_INPROGRESS;
-		}
-		goto out_unlock2;
-	}
 
-	/* put on pending queue if there's already another update in progress */
-	if (afs_vlocation_update) {
-		vlocation->upd_state = AFS_VLUPD_PENDING;
-		list_add_tail(&vlocation->upd_op.link,
-			      &afs_vlocation_update_pendq);
-		goto out_unlock2;
-	}
+		/* must now wait for creation or update by someone else to
+		 * complete */
+		_debug("wait");
 
-	/* hold a ref on it while actually updating */
-	afs_get_vlocation(vlocation);
-	afs_vlocation_update = vlocation;
-	vlocation->upd_state = AFS_VLUPD_INPROGRESS;
-
-	spin_unlock(&afs_vlocation_update_lock);
-	spin_unlock(&vlocation->cell->vl_gylock);
-
-	/* okay... we can start the update */
-	_debug("BEGIN VL UPDATE [%s]", vlocation->vldb.name);
-	vlocation->upd_first_svix = vlocation->cell->vl_curr_svix;
-	vlocation->upd_curr_svix = vlocation->upd_first_svix;
-	vlocation->upd_rej_cnt = 0;
-	vlocation->upd_busy_cnt = 0;
-
-	ret = afs_vlocation_update_begin(vlocation);
-	if (ret < 0) {
-		afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, ret);
-		afs_kafstimod_add_timer(&vlocation->upd_timer,
-					AFS_VLDB_TIMEOUT);
-		afs_put_vlocation(vlocation);
-	}
-
-	_leave("");
-	return;
-
- out_unlock2:
-	spin_unlock(&afs_vlocation_update_lock);
- out_unlock1:
-	spin_unlock(&vlocation->cell->vl_gylock);
-	_leave("");
-	return;
-
-} /* end afs_vlocation_update_timer() */
-
-/*****************************************************************************/
-/*
- * attend to an update operation upon which an event happened
- * - called in kafsasyncd context
- */
-static void afs_vlocation_update_attend(struct afs_async_op *op)
-{
-	struct afs_cache_vlocation vldb;
-	struct afs_vlocation *vlocation =
-		list_entry(op, struct afs_vlocation, upd_op);
-	unsigned tmp;
-	int ret;
-
-	_enter("%s", vlocation->vldb.name);
-
-	ret = afs_rxvl_get_entry_by_id_async2(op, &vldb);
-	switch (ret) {
-	case -EAGAIN:
-		_leave(" [unfinished]");
-		return;
-
-	case 0:
-		_debug("END VL UPDATE: %d\n", ret);
-		vlocation->valid = 1;
-
-		_debug("Done VL Lookup: %02x { %08x(%x) %08x(%x) %08x(%x) }",
-		       vldb.vidmask,
-		       ntohl(vldb.servers[0].s_addr), vldb.srvtmask[0],
-		       ntohl(vldb.servers[1].s_addr), vldb.srvtmask[1],
-		       ntohl(vldb.servers[2].s_addr), vldb.srvtmask[2]
-		       );
-
-		_debug("Vids: %08x %08x %08x",
-		       vldb.vid[0], vldb.vid[1], vldb.vid[2]);
-
-		afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, 0);
-
-		down_write(&vlocation->cell->vl_sem);
-
-		/* actually update the cache */
-		if (strncmp(vldb.name, vlocation->vldb.name,
-			    sizeof(vlocation->vldb.name)) != 0)
-			printk("kAFS: name of volume '%s'"
-			       " changed to '%s' on server\n",
-			       vlocation->vldb.name, vldb.name);
-
-		memcpy(&vlocation->vldb, &vldb, sizeof(vlocation->vldb));
-
-#if 0
-		/* TODO update volume entry in local cache */
-#endif
-
-		up_write(&vlocation->cell->vl_sem);
-
+		spin_unlock(&vl->lock);
+		ret = wait_event_interruptible(vl->waitq,
+					       vl->state == AFS_VL_NEW ||
+					       vl->state == AFS_VL_VALID ||
+					       vl->state == AFS_VL_NO_VOLUME);
 		if (ret < 0)
-			printk("kAFS: failed to update local cache: %d\n", ret);
-
-		afs_kafstimod_add_timer(&vlocation->upd_timer,
-					AFS_VLDB_TIMEOUT);
-		afs_put_vlocation(vlocation);
-		_leave(" [found]");
-		return;
-
-	case -ENOMEDIUM:
-		vlocation->upd_rej_cnt++;
-		goto try_next;
-
-		/* the server is locked - retry in a very short while */
-	case -EBUSY:
-		vlocation->upd_busy_cnt++;
-		if (vlocation->upd_busy_cnt > 3)
-			goto try_next; /* too many retries */
-
-		afs_vlocation_update_abandon(vlocation,
-					     AFS_VLUPD_BUSYSLEEP, 0);
-		afs_kafstimod_add_timer(&vlocation->upd_timer, HZ / 2);
-		afs_put_vlocation(vlocation);
-		_leave(" [busy]");
-		return;
-
-	case -ENETUNREACH:
-	case -EHOSTUNREACH:
-	case -ECONNREFUSED:
-	case -EREMOTEIO:
-		/* record bad vlserver info in the cell too
-		 * - TODO: use down_write_trylock() if available
-		 */
-		if (vlocation->upd_curr_svix == vlocation->cell->vl_curr_svix)
-			vlocation->cell->vl_curr_svix =
-				vlocation->cell->vl_curr_svix %
-				vlocation->cell->vl_naddrs;
-
-	case -EBADRQC:
-	case -EINVAL:
-	case -EACCES:
-	case -EBADMSG:
-		goto try_next;
-
-	default:
-		goto abandon;
+			goto error;
+		spin_lock(&vl->lock);
 	}
+	spin_unlock(&vl->lock);
 
-	/* try contacting the next server */
- try_next:
-	vlocation->upd_busy_cnt = 0;
+success:
+	_leave(" = %p",vl);
+	return vl;
 
-	/* discard the server record */
-	afs_put_server(vlocation->upd_op.server);
-	vlocation->upd_op.server = NULL;
+error_abandon:
+	spin_lock(&vl->lock);
+	vl->state = AFS_VL_NEW;
+	spin_unlock(&vl->lock);
+	wake_up(&vl->waitq);
+error:
+	ASSERT(vl != NULL);
+	afs_put_vlocation(vl);
+	_leave(" = %d", ret);
+	return ERR_PTR(ret);
+}
 
-	tmp = vlocation->cell->vl_naddrs;
-	if (tmp == 0)
-		goto abandon;
-
-	vlocation->upd_curr_svix++;
-	if (vlocation->upd_curr_svix >= tmp)
-		vlocation->upd_curr_svix = 0;
-	if (vlocation->upd_first_svix >= tmp)
-		vlocation->upd_first_svix = tmp - 1;
-
-	/* move to the next server */
-	if (vlocation->upd_curr_svix != vlocation->upd_first_svix) {
-		afs_vlocation_update_begin(vlocation);
-		_leave(" [next]");
-		return;
-	}
-
-	/* run out of servers to try - was the volume rejected? */
-	if (vlocation->upd_rej_cnt > 0) {
-		printk("kAFS: Active volume no longer valid '%s'\n",
-		       vlocation->vldb.name);
-		vlocation->valid = 0;
-		afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, 0);
-		afs_kafstimod_add_timer(&vlocation->upd_timer,
-					AFS_VLDB_TIMEOUT);
-		afs_put_vlocation(vlocation);
-		_leave(" [invalidated]");
-		return;
-	}
-
-	/* abandon the update */
- abandon:
-	afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, ret);
-	afs_kafstimod_add_timer(&vlocation->upd_timer, HZ * 10);
-	afs_put_vlocation(vlocation);
-	_leave(" [abandoned]");
-
-} /* end afs_vlocation_update_attend() */
-
-/*****************************************************************************/
 /*
- * deal with an update operation being discarded
- * - called in kafsasyncd context when it's dying due to rmmod
- * - the call has already been aborted and put()'d
+ * finish using a volume location record
  */
-static void afs_vlocation_update_discard(struct afs_async_op *op)
+void afs_put_vlocation(struct afs_vlocation *vl)
 {
-	struct afs_vlocation *vlocation =
-		list_entry(op, struct afs_vlocation, upd_op);
+	if (!vl)
+		return;
 
-	_enter("%s", vlocation->vldb.name);
+	_enter("%s", vl->vldb.name);
 
-	afs_put_server(op->server);
-	op->server = NULL;
+	ASSERTCMP(atomic_read(&vl->usage), >, 0);
 
-	afs_put_vlocation(vlocation);
+	if (likely(!atomic_dec_and_test(&vl->usage))) {
+		_leave("");
+		return;
+	}
 
-	_leave("");
-} /* end afs_vlocation_update_discard() */
+	spin_lock(&afs_vlocation_graveyard_lock);
+	if (atomic_read(&vl->usage) == 0) {
+		_debug("buried");
+		list_move_tail(&vl->grave, &afs_vlocation_graveyard);
+		vl->time_of_death = get_seconds();
+		schedule_delayed_work(&afs_vlocation_reap,
+				      afs_vlocation_timeout * HZ);
 
-/*****************************************************************************/
+		/* suspend updates on this record */
+		if (!list_empty(&vl->update)) {
+			spin_lock(&afs_vlocation_updates_lock);
+			list_del_init(&vl->update);
+			spin_unlock(&afs_vlocation_updates_lock);
+		}
+	}
+	spin_unlock(&afs_vlocation_graveyard_lock);
+	_leave(" [killed?]");
+}
+
 /*
- * match a VLDB record stored in the cache
- * - may also load target from entry
+ * destroy a dead volume location record
  */
+static void afs_vlocation_destroy(struct afs_vlocation *vl)
+{
+	_enter("%p", vl);
+
 #ifdef AFS_CACHING_SUPPORT
-static cachefs_match_val_t afs_vlocation_cache_match(void *target,
-						     const void *entry)
-{
-	const struct afs_cache_vlocation *vldb = entry;
-	struct afs_vlocation *vlocation = target;
-
-	_enter("{%s},{%s}", vlocation->vldb.name, vldb->name);
-
-	if (strncmp(vlocation->vldb.name, vldb->name, sizeof(vldb->name)) == 0
-	    ) {
-		if (!vlocation->valid ||
-		    vlocation->vldb.rtime == vldb->rtime
-		    ) {
-			vlocation->vldb = *vldb;
-			vlocation->valid = 1;
-			_leave(" = SUCCESS [c->m]");
-			return CACHEFS_MATCH_SUCCESS;
-		}
-		/* need to update cache if cached info differs */
-		else if (memcmp(&vlocation->vldb, vldb, sizeof(*vldb)) != 0) {
-			/* delete if VIDs for this name differ */
-			if (memcmp(&vlocation->vldb.vid,
-				   &vldb->vid,
-				   sizeof(vldb->vid)) != 0) {
-				_leave(" = DELETE");
-				return CACHEFS_MATCH_SUCCESS_DELETE;
-			}
-
-			_leave(" = UPDATE");
-			return CACHEFS_MATCH_SUCCESS_UPDATE;
-		}
-		else {
-			_leave(" = SUCCESS");
-			return CACHEFS_MATCH_SUCCESS;
-		}
-	}
-
-	_leave(" = FAILED");
-	return CACHEFS_MATCH_FAILED;
-} /* end afs_vlocation_cache_match() */
+	cachefs_relinquish_cookie(vl->cache, 0);
 #endif
 
-/*****************************************************************************/
+	afs_put_cell(vl->cell);
+	kfree(vl);
+}
+
 /*
- * update a VLDB record stored in the cache
+ * reap dead volume location records
  */
-#ifdef AFS_CACHING_SUPPORT
-static void afs_vlocation_cache_update(void *source, void *entry)
+static void afs_vlocation_reaper(struct work_struct *work)
 {
-	struct afs_cache_vlocation *vldb = entry;
-	struct afs_vlocation *vlocation = source;
+	LIST_HEAD(corpses);
+	struct afs_vlocation *vl;
+	unsigned long delay, expiry;
+	time_t now;
 
 	_enter("");
 
-	*vldb = vlocation->vldb;
+	now = get_seconds();
+	spin_lock(&afs_vlocation_graveyard_lock);
 
-} /* end afs_vlocation_cache_update() */
-#endif
+	while (!list_empty(&afs_vlocation_graveyard)) {
+		vl = list_entry(afs_vlocation_graveyard.next,
+				struct afs_vlocation, grave);
+
+		_debug("check %p", vl);
+
+		/* the queue is ordered most dead first */
+		expiry = vl->time_of_death + afs_vlocation_timeout;
+		if (expiry > now) {
+			delay = (expiry - now) * HZ;
+			_debug("delay %lu", delay);
+			if (!schedule_delayed_work(&afs_vlocation_reap,
+						   delay)) {
+				cancel_delayed_work(&afs_vlocation_reap);
+				schedule_delayed_work(&afs_vlocation_reap,
+						      delay);
+			}
+			break;
+		}
+
+		spin_lock(&vl->cell->vl_lock);
+		if (atomic_read(&vl->usage) > 0) {
+			_debug("no reap");
+			list_del_init(&vl->grave);
+		} else {
+			_debug("reap");
+			list_move_tail(&vl->grave, &corpses);
+			list_del_init(&vl->link);
+		}
+		spin_unlock(&vl->cell->vl_lock);
+	}
+
+	spin_unlock(&afs_vlocation_graveyard_lock);
+
+	/* now reap the corpses we've extracted */
+	while (!list_empty(&corpses)) {
+		vl = list_entry(corpses.next, struct afs_vlocation, grave);
+		list_del(&vl->grave);
+		afs_vlocation_destroy(vl);
+	}
+
+	_leave("");
+}
+
+/*
+ * initialise the VL update process
+ */
+int __init afs_vlocation_update_init(void)
+{
+	afs_vlocation_update_worker =
+		create_singlethread_workqueue("kafs_vlupdated");
+	return afs_vlocation_update_worker ? 0 : -ENOMEM;
+}
+
+/*
+ * discard all the volume location records for rmmod
+ */
+void __exit afs_vlocation_purge(void)
+{
+	afs_vlocation_timeout = 0;
+
+	spin_lock(&afs_vlocation_updates_lock);
+	list_del_init(&afs_vlocation_updates);
+	spin_unlock(&afs_vlocation_updates_lock);
+	cancel_delayed_work(&afs_vlocation_update);
+	queue_delayed_work(afs_vlocation_update_worker,
+			   &afs_vlocation_update, 0);
+	destroy_workqueue(afs_vlocation_update_worker);
+
+	cancel_delayed_work(&afs_vlocation_reap);
+	schedule_delayed_work(&afs_vlocation_reap, 0);
+}
+
+/*
+ * update a volume location
+ */
+static void afs_vlocation_updater(struct work_struct *work)
+{
+	struct afs_cache_vlocation vldb;
+	struct afs_vlocation *vl, *xvl;
+	time_t now;
+	long timeout;
+	int ret;
+
+	_enter("");
+
+	now = get_seconds();
+
+	/* find a record to update */
+	spin_lock(&afs_vlocation_updates_lock);
+	for (;;) {
+		if (list_empty(&afs_vlocation_updates)) {
+			spin_unlock(&afs_vlocation_updates_lock);
+			_leave(" [nothing]");
+			return;
+		}
+
+		vl = list_entry(afs_vlocation_updates.next,
+				struct afs_vlocation, update);
+		if (atomic_read(&vl->usage) > 0)
+			break;
+		list_del_init(&vl->update);
+	}
+
+	timeout = vl->update_at - now;
+	if (timeout > 0) {
+		queue_delayed_work(afs_vlocation_update_worker,
+				   &afs_vlocation_update, timeout * HZ);
+		spin_unlock(&afs_vlocation_updates_lock);
+		_leave(" [nothing]");
+		return;
+	}
+
+	list_del_init(&vl->update);
+	atomic_inc(&vl->usage);
+	spin_unlock(&afs_vlocation_updates_lock);
+
+	/* we can now perform the update */
+	_debug("update %s", vl->vldb.name);
+	vl->state = AFS_VL_UPDATING;
+	vl->upd_rej_cnt = 0;
+	vl->upd_busy_cnt = 0;
+
+	ret = afs_vlocation_update_record(vl, NULL, &vldb);
+	spin_lock(&vl->lock);
+	switch (ret) {
+	case 0:
+		afs_vlocation_apply_update(vl, &vldb);
+		vl->state = AFS_VL_VALID;
+		break;
+	case -ENOMEDIUM:
+		vl->state = AFS_VL_VOLUME_DELETED;
+		break;
+	default:
+		vl->state = AFS_VL_UNCERTAIN;
+		break;
+	}
+	spin_unlock(&vl->lock);
+	wake_up(&vl->waitq);
+
+	/* and then reschedule */
+	_debug("reschedule");
+	vl->update_at = get_seconds() + afs_vlocation_update_timeout;
+
+	spin_lock(&afs_vlocation_updates_lock);
+
+	if (!list_empty(&afs_vlocation_updates)) {
+		/* next update in 10 minutes, but wait at least 1 second more
+		 * than the newest record already queued so that we don't spam
+		 * the VL server suddenly with lots of requests
+		 */
+		xvl = list_entry(afs_vlocation_updates.prev,
+				 struct afs_vlocation, update);
+		if (vl->update_at <= xvl->update_at)
+			vl->update_at = xvl->update_at + 1;
+		xvl = list_entry(afs_vlocation_updates.next,
+				 struct afs_vlocation, update);
+		timeout = xvl->update_at - now;
+		if (timeout < 0)
+			timeout = 0;
+	} else {
+		timeout = afs_vlocation_update_timeout;
+	}
+
+	ASSERT(list_empty(&vl->update));
+
+	list_add_tail(&vl->update, &afs_vlocation_updates);
+
+	_debug("timeout %ld", timeout);
+	queue_delayed_work(afs_vlocation_update_worker,
+			   &afs_vlocation_update, timeout * HZ);
+	spin_unlock(&afs_vlocation_updates_lock);
+	afs_put_vlocation(vl);
+}
diff --git a/fs/afs/vnode.c b/fs/afs/vnode.c
index cf62da5..a1904ab 100644
--- a/fs/afs/vnode.c
+++ b/fs/afs/vnode.c
@@ -1,6 +1,6 @@
-/* vnode.c: AFS vnode management
+/* AFS vnode management
  *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
@@ -14,142 +14,237 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
-#include <linux/pagemap.h>
-#include "volume.h"
-#include "cell.h"
-#include "cmservice.h"
-#include "fsclient.h"
-#include "vlclient.h"
-#include "vnode.h"
 #include "internal.h"
 
-static void afs_vnode_cb_timed_out(struct afs_timer *timer);
-
-struct afs_timer_ops afs_vnode_cb_timed_out_ops = {
-	.timed_out	= afs_vnode_cb_timed_out,
-};
-
-#ifdef AFS_CACHING_SUPPORT
-static cachefs_match_val_t afs_vnode_cache_match(void *target,
-						 const void *entry);
-static void afs_vnode_cache_update(void *source, void *entry);
-
-struct cachefs_index_def afs_vnode_cache_index_def = {
-	.name		= "vnode",
-	.data_size	= sizeof(struct afs_cache_vnode),
-	.keys[0]	= { CACHEFS_INDEX_KEYS_BIN, 4 },
-	.match		= afs_vnode_cache_match,
-	.update		= afs_vnode_cache_update,
-};
-#endif
-
-/*****************************************************************************/
-/*
- * handle a callback timing out
- * TODO: retain a ref to vnode struct for an outstanding callback timeout
- */
-static void afs_vnode_cb_timed_out(struct afs_timer *timer)
+#if 0
+static noinline bool dump_tree_aux(struct rb_node *node, struct rb_node *parent,
+				   int depth, char lr)
 {
-	struct afs_server *oldserver;
 	struct afs_vnode *vnode;
+	bool bad = false;
 
-	vnode = list_entry(timer, struct afs_vnode, cb_timeout);
+	if (!node)
+		return false;
 
-	_enter("%p", vnode);
+	if (node->rb_left)
+		bad = dump_tree_aux(node->rb_left, node, depth + 2, '/');
 
-	/* set the changed flag in the vnode and release the server */
-	spin_lock(&vnode->lock);
-
-	oldserver = xchg(&vnode->cb_server, NULL);
-	if (oldserver) {
-		vnode->flags |= AFS_VNODE_CHANGED;
-
-		spin_lock(&afs_cb_hash_lock);
-		list_del_init(&vnode->cb_hash_link);
-		spin_unlock(&afs_cb_hash_lock);
-
-		spin_lock(&oldserver->cb_lock);
-		list_del_init(&vnode->cb_link);
-		spin_unlock(&oldserver->cb_lock);
+	vnode = rb_entry(node, struct afs_vnode, cb_promise);
+	_debug("%c %*.*s%c%p {%d}",
+	       rb_is_red(node) ? 'R' : 'B',
+	       depth, depth, "", lr,
+	       vnode, vnode->cb_expires_at);
+	if (rb_parent(node) != parent) {
+		printk("BAD: %p != %p\n", rb_parent(node), parent);
+		bad = true;
 	}
 
-	spin_unlock(&vnode->lock);
+	if (node->rb_right)
+		bad |= dump_tree_aux(node->rb_right, node, depth + 2, '\\');
 
-	afs_put_server(oldserver);
+	return bad;
+}
 
-	_leave("");
-} /* end afs_vnode_cb_timed_out() */
+static noinline void dump_tree(const char *name, struct afs_server *server)
+{
+	_enter("%s", name);
+	if (dump_tree_aux(server->cb_promises.rb_node, NULL, 0, '-'))
+		BUG();
+}
+#endif
 
-/*****************************************************************************/
 /*
- * finish off updating the recorded status of a file
+ * insert a vnode into the backing server's vnode tree
+ */
+static void afs_install_vnode(struct afs_vnode *vnode,
+			      struct afs_server *server)
+{
+	struct afs_server *old_server = vnode->server;
+	struct afs_vnode *xvnode;
+	struct rb_node *parent, **p;
+
+	_enter("%p,%p", vnode, server);
+
+	if (old_server) {
+		spin_lock(&old_server->fs_lock);
+		rb_erase(&vnode->server_rb, &old_server->fs_vnodes);
+		spin_unlock(&old_server->fs_lock);
+	}
+
+	afs_get_server(server);
+	vnode->server = server;
+	afs_put_server(old_server);
+
+	/* insert into the server's vnode tree in FID order */
+	spin_lock(&server->fs_lock);
+
+	parent = NULL;
+	p = &server->fs_vnodes.rb_node;
+	while (*p) {
+		parent = *p;
+		xvnode = rb_entry(parent, struct afs_vnode, server_rb);
+		if (vnode->fid.vid < xvnode->fid.vid)
+			p = &(*p)->rb_left;
+		else if (vnode->fid.vid > xvnode->fid.vid)
+			p = &(*p)->rb_right;
+		else if (vnode->fid.vnode < xvnode->fid.vnode)
+			p = &(*p)->rb_left;
+		else if (vnode->fid.vnode > xvnode->fid.vnode)
+			p = &(*p)->rb_right;
+		else if (vnode->fid.unique < xvnode->fid.unique)
+			p = &(*p)->rb_left;
+		else if (vnode->fid.unique > xvnode->fid.unique)
+			p = &(*p)->rb_right;
+		else
+			BUG(); /* can't happen unless afs_iget() malfunctions */
+	}
+
+	rb_link_node(&vnode->server_rb, parent, p);
+	rb_insert_color(&vnode->server_rb, &server->fs_vnodes);
+
+	spin_unlock(&server->fs_lock);
+	_leave("");
+}
+
+/*
+ * insert a vnode into the promising server's update/expiration tree
+ * - caller must hold vnode->lock
+ */
+static void afs_vnode_note_promise(struct afs_vnode *vnode,
+				   struct afs_server *server)
+{
+	struct afs_server *old_server;
+	struct afs_vnode *xvnode;
+	struct rb_node *parent, **p;
+
+	_enter("%p,%p", vnode, server);
+
+	ASSERT(server != NULL);
+
+	old_server = vnode->server;
+	if (vnode->cb_promised) {
+		if (server == old_server &&
+		    vnode->cb_expires == vnode->cb_expires_at) {
+			_leave(" [no change]");
+			return;
+		}
+
+		spin_lock(&old_server->cb_lock);
+		if (vnode->cb_promised) {
+			_debug("delete");
+			rb_erase(&vnode->cb_promise, &old_server->cb_promises);
+			vnode->cb_promised = false;
+		}
+		spin_unlock(&old_server->cb_lock);
+	}
+
+	if (vnode->server != server)
+		afs_install_vnode(vnode, server);
+
+	vnode->cb_expires_at = vnode->cb_expires;
+	_debug("PROMISE on %p {%lu}",
+	       vnode, (unsigned long) vnode->cb_expires_at);
+
+	/* abuse an RB-tree to hold the expiration order (we may have multiple
+	 * items with the same expiration time) */
+	spin_lock(&server->cb_lock);
+
+	parent = NULL;
+	p = &server->cb_promises.rb_node;
+	while (*p) {
+		parent = *p;
+		xvnode = rb_entry(parent, struct afs_vnode, cb_promise);
+		if (vnode->cb_expires_at < xvnode->cb_expires_at)
+			p = &(*p)->rb_left;
+		else
+			p = &(*p)->rb_right;
+	}
+
+	rb_link_node(&vnode->cb_promise, parent, p);
+	rb_insert_color(&vnode->cb_promise, &server->cb_promises);
+	vnode->cb_promised = true;
+
+	spin_unlock(&server->cb_lock);
+	_leave("");
+}
+
+/*
+ * handle remote file deletion by discarding the callback promise
+ */
+static void afs_vnode_deleted_remotely(struct afs_vnode *vnode)
+{
+	struct afs_server *server;
+
+	set_bit(AFS_VNODE_DELETED, &vnode->flags);
+
+	server = vnode->server;
+	if (vnode->cb_promised) {
+		spin_lock(&server->cb_lock);
+		if (vnode->cb_promised) {
+			rb_erase(&vnode->cb_promise, &server->cb_promises);
+			vnode->cb_promised = false;
+		}
+		spin_unlock(&server->cb_lock);
+	}
+
+	spin_lock(&vnode->server->fs_lock);
+	rb_erase(&vnode->server_rb, &vnode->server->fs_vnodes);
+	spin_unlock(&vnode->server->fs_lock);
+
+	vnode->server = NULL;
+	afs_put_server(server);
+}
+
+/*
+ * finish off updating the recorded status of a file after a successful
+ * operation completion
  * - starts callback expiry timer
  * - adds to server's callback list
  */
-static void afs_vnode_finalise_status_update(struct afs_vnode *vnode,
-					     struct afs_server *server,
-					     int ret)
+void afs_vnode_finalise_status_update(struct afs_vnode *vnode,
+				      struct afs_server *server)
 {
 	struct afs_server *oldserver = NULL;
 
-	_enter("%p,%p,%d", vnode, server, ret);
+	_enter("%p,%p", vnode, server);
 
 	spin_lock(&vnode->lock);
-
-	vnode->flags &= ~AFS_VNODE_CHANGED;
-
-	if (ret == 0) {
-		/* adjust the callback timeout appropriately */
-		afs_kafstimod_add_timer(&vnode->cb_timeout,
-					vnode->cb_expiry * HZ);
-
-		spin_lock(&afs_cb_hash_lock);
-		list_move_tail(&vnode->cb_hash_link,
-			      &afs_cb_hash(server, &vnode->fid));
-		spin_unlock(&afs_cb_hash_lock);
-
-		/* swap ref to old callback server with that for new callback
-		 * server */
-		oldserver = xchg(&vnode->cb_server, server);
-		if (oldserver != server) {
-			if (oldserver) {
-				spin_lock(&oldserver->cb_lock);
-				list_del_init(&vnode->cb_link);
-				spin_unlock(&oldserver->cb_lock);
-			}
-
-			afs_get_server(server);
-			spin_lock(&server->cb_lock);
-			list_add_tail(&vnode->cb_link, &server->cb_promises);
-			spin_unlock(&server->cb_lock);
-		}
-		else {
-			/* same server */
-			oldserver = NULL;
-		}
-	}
-	else if (ret == -ENOENT) {
-		/* the file was deleted - clear the callback timeout */
-		oldserver = xchg(&vnode->cb_server, NULL);
-		afs_kafstimod_del_timer(&vnode->cb_timeout);
-
-		_debug("got NOENT from server - marking file deleted");
-		vnode->flags |= AFS_VNODE_DELETED;
-	}
-
+	clear_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
+	afs_vnode_note_promise(vnode, server);
 	vnode->update_cnt--;
-
+	ASSERTCMP(vnode->update_cnt, >=, 0);
 	spin_unlock(&vnode->lock);
 
 	wake_up_all(&vnode->update_waitq);
-
 	afs_put_server(oldserver);
-
 	_leave("");
+}
 
-} /* end afs_vnode_finalise_status_update() */
+/*
+ * finish off updating the recorded status of a file after an operation failed
+ */
+static void afs_vnode_status_update_failed(struct afs_vnode *vnode, int ret)
+{
+	_enter("%p,%d", vnode, ret);
 
-/*****************************************************************************/
+	spin_lock(&vnode->lock);
+
+	clear_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
+
+	if (ret == -ENOENT) {
+		/* the file was deleted on the server */
+		_debug("got NOENT from server - marking file deleted");
+		afs_vnode_deleted_remotely(vnode);
+	}
+
+	vnode->update_cnt--;
+	ASSERTCMP(vnode->update_cnt, >=, 0);
+	spin_unlock(&vnode->lock);
+
+	wake_up_all(&vnode->update_waitq);
+	_leave("");
+}
+
 /*
  * fetch file status from the volume
  * - don't issue a fetch if:
@@ -157,9 +252,11 @@
  *   - there are any outstanding ops that will fetch the status
  * - TODO implement local caching
  */
-int afs_vnode_fetch_status(struct afs_vnode *vnode)
+int afs_vnode_fetch_status(struct afs_vnode *vnode,
+			   struct afs_vnode *auth_vnode, struct key *key)
 {
 	struct afs_server *server;
+	unsigned long acl_order;
 	int ret;
 
 	DECLARE_WAITQUEUE(myself, current);
@@ -168,38 +265,49 @@
 	       vnode->volume->vlocation->vldb.name,
 	       vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
 
-	if (!(vnode->flags & AFS_VNODE_CHANGED) && vnode->cb_server) {
+	if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) &&
+	    vnode->cb_promised) {
 		_leave(" [unchanged]");
 		return 0;
 	}
 
-	if (vnode->flags & AFS_VNODE_DELETED) {
+	if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
 		_leave(" [deleted]");
 		return -ENOENT;
 	}
 
+	acl_order = 0;
+	if (auth_vnode)
+		acl_order = auth_vnode->acl_order;
+
 	spin_lock(&vnode->lock);
 
-	if (!(vnode->flags & AFS_VNODE_CHANGED)) {
+	if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) &&
+	    vnode->cb_promised) {
 		spin_unlock(&vnode->lock);
 		_leave(" [unchanged]");
 		return 0;
 	}
 
+	ASSERTCMP(vnode->update_cnt, >=, 0);
+
 	if (vnode->update_cnt > 0) {
 		/* someone else started a fetch */
+		_debug("wait on fetch %d", vnode->update_cnt);
+
 		set_current_state(TASK_UNINTERRUPTIBLE);
+		ASSERT(myself.func != NULL);
 		add_wait_queue(&vnode->update_waitq, &myself);
 
 		/* wait for the status to be updated */
 		for (;;) {
-			if (!(vnode->flags & AFS_VNODE_CHANGED))
+			if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags))
 				break;
-			if (vnode->flags & AFS_VNODE_DELETED)
+			if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
 				break;
 
-			/* it got updated and invalidated all before we saw
-			 * it */
+			/* check to see if it got updated and invalidated all
+			 * before we saw it */
 			if (vnode->update_cnt == 0) {
 				remove_wait_queue(&vnode->update_waitq,
 						  &myself);
@@ -219,10 +327,11 @@
 		spin_unlock(&vnode->lock);
 		set_current_state(TASK_RUNNING);
 
-		return vnode->flags & AFS_VNODE_DELETED ? -ENOENT : 0;
+		return test_bit(AFS_VNODE_DELETED, &vnode->flags) ?
+			-ENOENT : 0;
 	}
 
- get_anyway:
+get_anyway:
 	/* okay... we're going to have to initiate the op */
 	vnode->update_cnt++;
 
@@ -232,39 +341,60 @@
 	 * vnode */
 	do {
 		/* pick a server to query */
-		ret = afs_volume_pick_fileserver(vnode->volume, &server);
-		if (ret<0)
-			return ret;
+		server = afs_volume_pick_fileserver(vnode);
+		if (IS_ERR(server))
+			goto no_server;
 
-		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
+		_debug("USING SERVER: %p{%08x}",
+		       server, ntohl(server->addr.s_addr));
 
-		ret = afs_rxfs_fetch_file_status(server, vnode, NULL);
+		ret = afs_fs_fetch_file_status(server, key, vnode, NULL,
+					       &afs_sync_call);
 
-	} while (!afs_volume_release_fileserver(vnode->volume, server, ret));
+	} while (!afs_volume_release_fileserver(vnode, server, ret));
 
 	/* adjust the flags */
-	afs_vnode_finalise_status_update(vnode, server, ret);
+	if (ret == 0) {
+		_debug("adjust");
+		if (auth_vnode)
+			afs_cache_permit(vnode, key, acl_order);
+		afs_vnode_finalise_status_update(vnode, server);
+		afs_put_server(server);
+	} else {
+		_debug("failed [%d]", ret);
+		afs_vnode_status_update_failed(vnode, ret);
+	}
 
-	_leave(" = %d", ret);
+	ASSERTCMP(vnode->update_cnt, >=, 0);
+
+	_leave(" = %d [cnt %d]", ret, vnode->update_cnt);
 	return ret;
-} /* end afs_vnode_fetch_status() */
 
-/*****************************************************************************/
+no_server:
+	spin_lock(&vnode->lock);
+	vnode->update_cnt--;
+	ASSERTCMP(vnode->update_cnt, >=, 0);
+	spin_unlock(&vnode->lock);
+	_leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
+	return PTR_ERR(server);
+}
+
 /*
  * fetch file data from the volume
- * - TODO implement caching and server failover
+ * - TODO implement caching
  */
-int afs_vnode_fetch_data(struct afs_vnode *vnode,
-			 struct afs_rxfs_fetch_descriptor *desc)
+int afs_vnode_fetch_data(struct afs_vnode *vnode, struct key *key,
+			 off_t offset, size_t length, struct page *page)
 {
 	struct afs_server *server;
 	int ret;
 
-	_enter("%s,{%u,%u,%u}",
+	_enter("%s{%u,%u,%u},%x,,,",
 	       vnode->volume->vlocation->vldb.name,
 	       vnode->fid.vid,
 	       vnode->fid.vnode,
-	       vnode->fid.unique);
+	       vnode->fid.unique,
+	       key_serial(key));
 
 	/* this op will fetch the status */
 	spin_lock(&vnode->lock);
@@ -275,120 +405,351 @@
 	 * vnode */
 	do {
 		/* pick a server to query */
-		ret = afs_volume_pick_fileserver(vnode->volume, &server);
-		if (ret < 0)
-			return ret;
+		server = afs_volume_pick_fileserver(vnode);
+		if (IS_ERR(server))
+			goto no_server;
 
 		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
 
-		ret = afs_rxfs_fetch_file_data(server, vnode, desc, NULL);
+		ret = afs_fs_fetch_data(server, key, vnode, offset, length,
+					page, &afs_sync_call);
 
-	} while (!afs_volume_release_fileserver(vnode->volume, server, ret));
+	} while (!afs_volume_release_fileserver(vnode, server, ret));
 
 	/* adjust the flags */
-	afs_vnode_finalise_status_update(vnode, server, ret);
+	if (ret == 0) {
+		afs_vnode_finalise_status_update(vnode, server);
+		afs_put_server(server);
+	} else {
+		afs_vnode_status_update_failed(vnode, ret);
+	}
 
 	_leave(" = %d", ret);
 	return ret;
 
-} /* end afs_vnode_fetch_data() */
+no_server:
+	spin_lock(&vnode->lock);
+	vnode->update_cnt--;
+	ASSERTCMP(vnode->update_cnt, >=, 0);
+	spin_unlock(&vnode->lock);
+	return PTR_ERR(server);
+}
 
-/*****************************************************************************/
 /*
- * break any outstanding callback on a vnode
- * - only relevent to server that issued it
+ * make a file or a directory
  */
-int afs_vnode_give_up_callback(struct afs_vnode *vnode)
+int afs_vnode_create(struct afs_vnode *vnode, struct key *key,
+		     const char *name, umode_t mode, struct afs_fid *newfid,
+		     struct afs_file_status *newstatus,
+		     struct afs_callback *newcb, struct afs_server **_server)
 {
 	struct afs_server *server;
 	int ret;
 
-	_enter("%s,{%u,%u,%u}",
+	_enter("%s{%u,%u,%u},%x,%s,,",
 	       vnode->volume->vlocation->vldb.name,
 	       vnode->fid.vid,
 	       vnode->fid.vnode,
-	       vnode->fid.unique);
+	       vnode->fid.unique,
+	       key_serial(key),
+	       name);
 
-	spin_lock(&afs_cb_hash_lock);
-	list_del_init(&vnode->cb_hash_link);
-	spin_unlock(&afs_cb_hash_lock);
-
-	/* set the changed flag in the vnode and release the server */
+	/* this op will fetch the status on the directory we're creating in */
 	spin_lock(&vnode->lock);
-
-	afs_kafstimod_del_timer(&vnode->cb_timeout);
-
-	server = xchg(&vnode->cb_server, NULL);
-	if (server) {
-		vnode->flags |= AFS_VNODE_CHANGED;
-
-		spin_lock(&server->cb_lock);
-		list_del_init(&vnode->cb_link);
-		spin_unlock(&server->cb_lock);
-	}
-
+	vnode->update_cnt++;
 	spin_unlock(&vnode->lock);
 
-	ret = 0;
-	if (server) {
-		ret = afs_rxfs_give_up_callback(server, vnode);
-		afs_put_server(server);
+	do {
+		/* pick a server to query */
+		server = afs_volume_pick_fileserver(vnode);
+		if (IS_ERR(server))
+			goto no_server;
+
+		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
+
+		ret = afs_fs_create(server, key, vnode, name, mode, newfid,
+				    newstatus, newcb, &afs_sync_call);
+
+	} while (!afs_volume_release_fileserver(vnode, server, ret));
+
+	/* adjust the flags */
+	if (ret == 0) {
+		afs_vnode_finalise_status_update(vnode, server);
+		*_server = server;
+	} else {
+		afs_vnode_status_update_failed(vnode, ret);
+		*_server = NULL;
 	}
 
-	_leave(" = %d", ret);
+	_leave(" = %d [cnt %d]", ret, vnode->update_cnt);
 	return ret;
-} /* end afs_vnode_give_up_callback() */
 
-/*****************************************************************************/
+no_server:
+	spin_lock(&vnode->lock);
+	vnode->update_cnt--;
+	ASSERTCMP(vnode->update_cnt, >=, 0);
+	spin_unlock(&vnode->lock);
+	_leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
+	return PTR_ERR(server);
+}
+
 /*
- * match a vnode record stored in the cache
+ * remove a file or directory
  */
-#ifdef AFS_CACHING_SUPPORT
-static cachefs_match_val_t afs_vnode_cache_match(void *target,
-						 const void *entry)
+int afs_vnode_remove(struct afs_vnode *vnode, struct key *key, const char *name,
+		     bool isdir)
 {
-	const struct afs_cache_vnode *cvnode = entry;
-	struct afs_vnode *vnode = target;
+	struct afs_server *server;
+	int ret;
 
-	_enter("{%x,%x,%Lx},{%x,%x,%Lx}",
+	_enter("%s{%u,%u,%u},%x,%s",
+	       vnode->volume->vlocation->vldb.name,
+	       vnode->fid.vid,
 	       vnode->fid.vnode,
 	       vnode->fid.unique,
-	       vnode->status.version,
-	       cvnode->vnode_id,
-	       cvnode->vnode_unique,
-	       cvnode->data_version);
+	       key_serial(key),
+	       name);
 
-	if (vnode->fid.vnode != cvnode->vnode_id) {
-		_leave(" = FAILED");
-		return CACHEFS_MATCH_FAILED;
+	/* this op will fetch the status on the directory we're removing from */
+	spin_lock(&vnode->lock);
+	vnode->update_cnt++;
+	spin_unlock(&vnode->lock);
+
+	do {
+		/* pick a server to query */
+		server = afs_volume_pick_fileserver(vnode);
+		if (IS_ERR(server))
+			goto no_server;
+
+		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
+
+		ret = afs_fs_remove(server, key, vnode, name, isdir,
+				    &afs_sync_call);
+
+	} while (!afs_volume_release_fileserver(vnode, server, ret));
+
+	/* adjust the flags */
+	if (ret == 0) {
+		afs_vnode_finalise_status_update(vnode, server);
+		afs_put_server(server);
+	} else {
+		afs_vnode_status_update_failed(vnode, ret);
 	}
 
-	if (vnode->fid.unique != cvnode->vnode_unique ||
-	    vnode->status.version != cvnode->data_version) {
-		_leave(" = DELETE");
-		return CACHEFS_MATCH_SUCCESS_DELETE;
-	}
+	_leave(" = %d [cnt %d]", ret, vnode->update_cnt);
+	return ret;
 
-	_leave(" = SUCCESS");
-	return CACHEFS_MATCH_SUCCESS;
-} /* end afs_vnode_cache_match() */
-#endif
+no_server:
+	spin_lock(&vnode->lock);
+	vnode->update_cnt--;
+	ASSERTCMP(vnode->update_cnt, >=, 0);
+	spin_unlock(&vnode->lock);
+	_leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
+	return PTR_ERR(server);
+}
 
-/*****************************************************************************/
 /*
- * update a vnode record stored in the cache
+ * create a hard link
  */
-#ifdef AFS_CACHING_SUPPORT
-static void afs_vnode_cache_update(void *source, void *entry)
+extern int afs_vnode_link(struct afs_vnode *dvnode, struct afs_vnode *vnode,
+			  struct key *key, const char *name)
 {
-	struct afs_cache_vnode *cvnode = entry;
-	struct afs_vnode *vnode = source;
+	struct afs_server *server;
+	int ret;
 
-	_enter("");
+	_enter("%s{%u,%u,%u},%s{%u,%u,%u},%x,%s",
+	       dvnode->volume->vlocation->vldb.name,
+	       dvnode->fid.vid,
+	       dvnode->fid.vnode,
+	       dvnode->fid.unique,
+	       vnode->volume->vlocation->vldb.name,
+	       vnode->fid.vid,
+	       vnode->fid.vnode,
+	       vnode->fid.unique,
+	       key_serial(key),
+	       name);
 
-	cvnode->vnode_id	= vnode->fid.vnode;
-	cvnode->vnode_unique	= vnode->fid.unique;
-	cvnode->data_version	= vnode->status.version;
+	/* this op will fetch the status on the directory we're removing from */
+	spin_lock(&vnode->lock);
+	vnode->update_cnt++;
+	spin_unlock(&vnode->lock);
+	spin_lock(&dvnode->lock);
+	dvnode->update_cnt++;
+	spin_unlock(&dvnode->lock);
 
-} /* end afs_vnode_cache_update() */
-#endif
+	do {
+		/* pick a server to query */
+		server = afs_volume_pick_fileserver(dvnode);
+		if (IS_ERR(server))
+			goto no_server;
+
+		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
+
+		ret = afs_fs_link(server, key, dvnode, vnode, name,
+				  &afs_sync_call);
+
+	} while (!afs_volume_release_fileserver(dvnode, server, ret));
+
+	/* adjust the flags */
+	if (ret == 0) {
+		afs_vnode_finalise_status_update(vnode, server);
+		afs_vnode_finalise_status_update(dvnode, server);
+		afs_put_server(server);
+	} else {
+		afs_vnode_status_update_failed(vnode, ret);
+		afs_vnode_status_update_failed(dvnode, ret);
+	}
+
+	_leave(" = %d [cnt %d]", ret, vnode->update_cnt);
+	return ret;
+
+no_server:
+	spin_lock(&vnode->lock);
+	vnode->update_cnt--;
+	ASSERTCMP(vnode->update_cnt, >=, 0);
+	spin_unlock(&vnode->lock);
+	spin_lock(&dvnode->lock);
+	dvnode->update_cnt--;
+	ASSERTCMP(dvnode->update_cnt, >=, 0);
+	spin_unlock(&dvnode->lock);
+	_leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
+	return PTR_ERR(server);
+}
+
+/*
+ * create a symbolic link
+ */
+int afs_vnode_symlink(struct afs_vnode *vnode, struct key *key,
+		      const char *name, const char *content,
+		      struct afs_fid *newfid,
+		      struct afs_file_status *newstatus,
+		      struct afs_server **_server)
+{
+	struct afs_server *server;
+	int ret;
+
+	_enter("%s{%u,%u,%u},%x,%s,%s,,,",
+	       vnode->volume->vlocation->vldb.name,
+	       vnode->fid.vid,
+	       vnode->fid.vnode,
+	       vnode->fid.unique,
+	       key_serial(key),
+	       name, content);
+
+	/* this op will fetch the status on the directory we're creating in */
+	spin_lock(&vnode->lock);
+	vnode->update_cnt++;
+	spin_unlock(&vnode->lock);
+
+	do {
+		/* pick a server to query */
+		server = afs_volume_pick_fileserver(vnode);
+		if (IS_ERR(server))
+			goto no_server;
+
+		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
+
+		ret = afs_fs_symlink(server, key, vnode, name, content,
+				     newfid, newstatus, &afs_sync_call);
+
+	} while (!afs_volume_release_fileserver(vnode, server, ret));
+
+	/* adjust the flags */
+	if (ret == 0) {
+		afs_vnode_finalise_status_update(vnode, server);
+		*_server = server;
+	} else {
+		afs_vnode_status_update_failed(vnode, ret);
+		*_server = NULL;
+	}
+
+	_leave(" = %d [cnt %d]", ret, vnode->update_cnt);
+	return ret;
+
+no_server:
+	spin_lock(&vnode->lock);
+	vnode->update_cnt--;
+	ASSERTCMP(vnode->update_cnt, >=, 0);
+	spin_unlock(&vnode->lock);
+	_leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
+	return PTR_ERR(server);
+}
+
+/*
+ * rename a file
+ */
+int afs_vnode_rename(struct afs_vnode *orig_dvnode,
+		     struct afs_vnode *new_dvnode,
+		     struct key *key,
+		     const char *orig_name,
+		     const char *new_name)
+{
+	struct afs_server *server;
+	int ret;
+
+	_enter("%s{%u,%u,%u},%s{%u,%u,%u},%x,%s,%s",
+	       orig_dvnode->volume->vlocation->vldb.name,
+	       orig_dvnode->fid.vid,
+	       orig_dvnode->fid.vnode,
+	       orig_dvnode->fid.unique,
+	       new_dvnode->volume->vlocation->vldb.name,
+	       new_dvnode->fid.vid,
+	       new_dvnode->fid.vnode,
+	       new_dvnode->fid.unique,
+	       key_serial(key),
+	       orig_name,
+	       new_name);
+
+	/* this op will fetch the status on both the directories we're dealing
+	 * with */
+	spin_lock(&orig_dvnode->lock);
+	orig_dvnode->update_cnt++;
+	spin_unlock(&orig_dvnode->lock);
+	if (new_dvnode != orig_dvnode) {
+		spin_lock(&new_dvnode->lock);
+		new_dvnode->update_cnt++;
+		spin_unlock(&new_dvnode->lock);
+	}
+
+	do {
+		/* pick a server to query */
+		server = afs_volume_pick_fileserver(orig_dvnode);
+		if (IS_ERR(server))
+			goto no_server;
+
+		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
+
+		ret = afs_fs_rename(server, key, orig_dvnode, orig_name,
+				    new_dvnode, new_name, &afs_sync_call);
+
+	} while (!afs_volume_release_fileserver(orig_dvnode, server, ret));
+
+	/* adjust the flags */
+	if (ret == 0) {
+		afs_vnode_finalise_status_update(orig_dvnode, server);
+		if (new_dvnode != orig_dvnode)
+			afs_vnode_finalise_status_update(new_dvnode, server);
+		afs_put_server(server);
+	} else {
+		afs_vnode_status_update_failed(orig_dvnode, ret);
+		if (new_dvnode != orig_dvnode)
+			afs_vnode_status_update_failed(new_dvnode, ret);
+	}
+
+	_leave(" = %d [cnt %d]", ret, orig_dvnode->update_cnt);
+	return ret;
+
+no_server:
+	spin_lock(&orig_dvnode->lock);
+	orig_dvnode->update_cnt--;
+	ASSERTCMP(orig_dvnode->update_cnt, >=, 0);
+	spin_unlock(&orig_dvnode->lock);
+	if (new_dvnode != orig_dvnode) {
+		spin_lock(&new_dvnode->lock);
+		new_dvnode->update_cnt--;
+		ASSERTCMP(new_dvnode->update_cnt, >=, 0);
+		spin_unlock(&new_dvnode->lock);
+	}
+	_leave(" = %ld [cnt %d]", PTR_ERR(server), orig_dvnode->update_cnt);
+	return PTR_ERR(server);
+}
diff --git a/fs/afs/vnode.h b/fs/afs/vnode.h
deleted file mode 100644
index b86a971..0000000
--- a/fs/afs/vnode.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/* vnode.h: AFS vnode record
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_AFS_VNODE_H
-#define _LINUX_AFS_VNODE_H
-
-#include <linux/fs.h>
-#include "server.h"
-#include "kafstimod.h"
-#include "cache.h"
-
-#ifdef __KERNEL__
-
-struct afs_rxfs_fetch_descriptor;
-
-/*****************************************************************************/
-/*
- * vnode catalogue entry
- */
-struct afs_cache_vnode
-{
-	afs_vnodeid_t		vnode_id;	/* vnode ID */
-	unsigned		vnode_unique;	/* vnode ID uniquifier */
-	afs_dataversion_t	data_version;	/* data version */
-};
-
-#ifdef AFS_CACHING_SUPPORT
-extern struct cachefs_index_def afs_vnode_cache_index_def;
-#endif
-
-/*****************************************************************************/
-/*
- * AFS inode private data
- */
-struct afs_vnode
-{
-	struct inode		vfs_inode;	/* the VFS's inode record */
-
-	struct afs_volume	*volume;	/* volume on which vnode resides */
-	struct afs_fid		fid;		/* the file identifier for this inode */
-	struct afs_file_status	status;		/* AFS status info for this file */
-#ifdef AFS_CACHING_SUPPORT
-	struct cachefs_cookie	*cache;		/* caching cookie */
-#endif
-
-	wait_queue_head_t	update_waitq;	/* status fetch waitqueue */
-	unsigned		update_cnt;	/* number of outstanding ops that will update the
-						 * status */
-	spinlock_t		lock;		/* waitqueue/flags lock */
-	unsigned		flags;
-#define AFS_VNODE_CHANGED	0x00000001	/* set if vnode reported changed by callback */
-#define AFS_VNODE_DELETED	0x00000002	/* set if vnode deleted on server */
-#define AFS_VNODE_MOUNTPOINT	0x00000004	/* set if vnode is a mountpoint symlink */
-
-	/* outstanding callback notification on this file */
-	struct afs_server	*cb_server;	/* server that made the current promise */
-	struct list_head	cb_link;	/* link in server's promises list */
-	struct list_head	cb_hash_link;	/* link in master callback hash */
-	struct afs_timer	cb_timeout;	/* timeout on promise */
-	unsigned		cb_version;	/* callback version */
-	unsigned		cb_expiry;	/* callback expiry time */
-	afs_callback_type_t	cb_type;	/* type of callback */
-};
-
-static inline struct afs_vnode *AFS_FS_I(struct inode *inode)
-{
-	return container_of(inode,struct afs_vnode,vfs_inode);
-}
-
-static inline struct inode *AFS_VNODE_TO_I(struct afs_vnode *vnode)
-{
-	return &vnode->vfs_inode;
-}
-
-extern int afs_vnode_fetch_status(struct afs_vnode *vnode);
-
-extern int afs_vnode_fetch_data(struct afs_vnode *vnode,
-				struct afs_rxfs_fetch_descriptor *desc);
-
-extern int afs_vnode_give_up_callback(struct afs_vnode *vnode);
-
-extern struct afs_timer_ops afs_vnode_cb_timed_out_ops;
-
-#endif /* __KERNEL__ */
-
-#endif /* _LINUX_AFS_VNODE_H */
diff --git a/fs/afs/volume.c b/fs/afs/volume.c
index 768c6db..dd160ca 100644
--- a/fs/afs/volume.c
+++ b/fs/afs/volume.c
@@ -1,6 +1,6 @@
-/* volume.c: AFS volume management
+/* AFS volume management
  *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
@@ -15,35 +15,10 @@
 #include <linux/slab.h>
 #include <linux/fs.h>
 #include <linux/pagemap.h>
-#include "volume.h"
-#include "vnode.h"
-#include "cell.h"
-#include "cache.h"
-#include "cmservice.h"
-#include "fsclient.h"
-#include "vlclient.h"
 #include "internal.h"
 
-#ifdef __KDEBUG
 static const char *afs_voltypes[] = { "R/W", "R/O", "BAK" };
-#endif
 
-#ifdef AFS_CACHING_SUPPORT
-static cachefs_match_val_t afs_volume_cache_match(void *target,
-						  const void *entry);
-static void afs_volume_cache_update(void *source, void *entry);
-
-struct cachefs_index_def afs_volume_cache_index_def = {
-	.name		= "volume",
-	.data_size	= sizeof(struct afs_cache_vhash),
-	.keys[0]	= { CACHEFS_INDEX_KEYS_BIN, 1 },
-	.keys[1]	= { CACHEFS_INDEX_KEYS_BIN, 1 },
-	.match		= afs_volume_cache_match,
-	.update		= afs_volume_cache_update,
-};
-#endif
-
-/*****************************************************************************/
 /*
  * lookup a volume by name
  * - this can be one of the following:
@@ -66,118 +41,52 @@
  * - Rule 3: If parent volume is R/W, then only mount R/W volume unless
  *           explicitly told otherwise
  */
-int afs_volume_lookup(const char *name, struct afs_cell *cell, int rwpath,
-		      struct afs_volume **_volume)
+struct afs_volume *afs_volume_lookup(struct afs_mount_params *params)
 {
 	struct afs_vlocation *vlocation = NULL;
 	struct afs_volume *volume = NULL;
-	afs_voltype_t type;
-	const char *cellname, *volname, *suffix;
+	struct afs_server *server = NULL;
 	char srvtmask;
-	int force, ret, loop, cellnamesz, volnamesz;
+	int ret, loop;
 
-	_enter("%s,,%d,", name, rwpath);
-
-	if (!name || (name[0] != '%' && name[0] != '#') || !name[1]) {
-		printk("kAFS: unparsable volume name\n");
-		return -EINVAL;
-	}
-
-	/* determine the type of volume we're looking for */
-	force = 0;
-	type = AFSVL_ROVOL;
-
-	if (rwpath || name[0] == '%') {
-		type = AFSVL_RWVOL;
-		force = 1;
-	}
-
-	suffix = strrchr(name, '.');
-	if (suffix) {
-		if (strcmp(suffix, ".readonly") == 0) {
-			type = AFSVL_ROVOL;
-			force = 1;
-		}
-		else if (strcmp(suffix, ".backup") == 0) {
-			type = AFSVL_BACKVOL;
-			force = 1;
-		}
-		else if (suffix[1] == 0) {
-		}
-		else {
-			suffix = NULL;
-		}
-	}
-
-	/* split the cell and volume names */
-	name++;
-	volname = strchr(name, ':');
-	if (volname) {
-		cellname = name;
-		cellnamesz = volname - name;
-		volname++;
-	}
-	else {
-		volname = name;
-		cellname = NULL;
-		cellnamesz = 0;
-	}
-
-	volnamesz = suffix ? suffix - volname : strlen(volname);
-
-	_debug("CELL:%*.*s [%p] VOLUME:%*.*s SUFFIX:%s TYPE:%d%s",
-	       cellnamesz, cellnamesz, cellname ?: "", cell,
-	       volnamesz, volnamesz, volname, suffix ?: "-",
-	       type,
-	       force ? " FORCE" : "");
-
-	/* lookup the cell record */
-	if (cellname || !cell) {
-		ret = afs_cell_lookup(cellname, cellnamesz, &cell);
-		if (ret<0) {
-			printk("kAFS: unable to lookup cell '%s'\n",
-			       cellname ?: "");
-			goto error;
-		}
-	}
-	else {
-		afs_get_cell(cell);
-	}
+	_enter("{%*.*s,%d}",
+	       params->volnamesz, params->volnamesz, params->volname, params->rwpath);
 
 	/* lookup the volume location record */
-	ret = afs_vlocation_lookup(cell, volname, volnamesz, &vlocation);
-	if (ret < 0)
+	vlocation = afs_vlocation_lookup(params->cell, params->key,
+					 params->volname, params->volnamesz);
+	if (IS_ERR(vlocation)) {
+		ret = PTR_ERR(vlocation);
+		vlocation = NULL;
 		goto error;
+	}
 
 	/* make the final decision on the type we want */
 	ret = -ENOMEDIUM;
-	if (force && !(vlocation->vldb.vidmask & (1 << type)))
+	if (params->force && !(vlocation->vldb.vidmask & (1 << params->type)))
 		goto error;
 
 	srvtmask = 0;
 	for (loop = 0; loop < vlocation->vldb.nservers; loop++)
 		srvtmask |= vlocation->vldb.srvtmask[loop];
 
-	if (force) {
-		if (!(srvtmask & (1 << type)))
+	if (params->force) {
+		if (!(srvtmask & (1 << params->type)))
 			goto error;
-	}
-	else if (srvtmask & AFS_VOL_VTM_RO) {
-		type = AFSVL_ROVOL;
-	}
-	else if (srvtmask & AFS_VOL_VTM_RW) {
-		type = AFSVL_RWVOL;
-	}
-	else {
+	} else if (srvtmask & AFS_VOL_VTM_RO) {
+		params->type = AFSVL_ROVOL;
+	} else if (srvtmask & AFS_VOL_VTM_RW) {
+		params->type = AFSVL_RWVOL;
+	} else {
 		goto error;
 	}
 
-	down_write(&cell->vl_sem);
+	down_write(&params->cell->vl_sem);
 
 	/* is the volume already active? */
-	if (vlocation->vols[type]) {
+	if (vlocation->vols[params->type]) {
 		/* yes - re-use it */
-		volume = vlocation->vols[type];
+		volume = vlocation->vols[params->type];
 		afs_get_volume(volume);
 		goto success;
 	}
@@ -191,23 +100,24 @@
 		goto error_up;
 
 	atomic_set(&volume->usage, 1);
-	volume->type		= type;
-	volume->type_force	= force;
-	volume->cell		= cell;
-	volume->vid		= vlocation->vldb.vid[type];
+	volume->type		= params->type;
+	volume->type_force	= params->force;
+	volume->cell		= params->cell;
+	volume->vid		= vlocation->vldb.vid[params->type];
 
 	init_rwsem(&volume->server_sem);
 
 	/* look up all the applicable server records */
 	for (loop = 0; loop < 8; loop++) {
 		if (vlocation->vldb.srvtmask[loop] & (1 << volume->type)) {
-			ret = afs_server_lookup(
-				volume->cell,
-				&vlocation->vldb.servers[loop],
-				&volume->servers[volume->nservers]);
-			if (ret < 0)
+			server = afs_lookup_server(
+			       volume->cell, &vlocation->vldb.servers[loop]);
+			if (IS_ERR(server)) {
+				ret = PTR_ERR(server);
 				goto error_discard;
+			}
 
+			volume->servers[volume->nservers] = server;
 			volume->nservers++;
 		}
 	}
@@ -223,35 +133,34 @@
 	afs_get_vlocation(vlocation);
 	volume->vlocation = vlocation;
 
-	vlocation->vols[type] = volume;
+	vlocation->vols[volume->type] = volume;
 
- success:
+success:
 	_debug("kAFS selected %s volume %08x",
 	       afs_voltypes[volume->type], volume->vid);
-	*_volume = volume;
-	ret = 0;
+	up_write(&params->cell->vl_sem);
+	afs_put_vlocation(vlocation);
+	_leave(" = %p", volume);
+	return volume;
 
 	/* clean up */
- error_up:
-	up_write(&cell->vl_sem);
- error:
+error_up:
+	up_write(&params->cell->vl_sem);
+error:
 	afs_put_vlocation(vlocation);
-	afs_put_cell(cell);
+	_leave(" = %d", ret);
+	return ERR_PTR(ret);
 
-	_leave(" = %d (%p)", ret, volume);
-	return ret;
-
- error_discard:
-	up_write(&cell->vl_sem);
+error_discard:
+	up_write(&params->cell->vl_sem);
 
 	for (loop = volume->nservers - 1; loop >= 0; loop--)
 		afs_put_server(volume->servers[loop]);
 
 	kfree(volume);
 	goto error;
-} /* end afs_volume_lookup() */
+}
 
-/*****************************************************************************/
 /*
  * destroy a volume record
  */
@@ -265,10 +174,9 @@
 
 	_enter("%p", volume);
 
-	vlocation = volume->vlocation;
+	ASSERTCMP(atomic_read(&volume->usage), >, 0);
 
-	/* sanity check */
-	BUG_ON(atomic_read(&volume->usage) <= 0);
+	vlocation = volume->vlocation;
 
 	/* to prevent a race, the decrement and the dequeue must be effectively
 	 * atomic */
@@ -296,21 +204,27 @@
 	kfree(volume);
 
 	_leave(" [destroyed]");
-} /* end afs_put_volume() */
+}
 
-/*****************************************************************************/
 /*
  * pick a server to use to try accessing this volume
  * - returns with an elevated usage count on the server chosen
  */
-int afs_volume_pick_fileserver(struct afs_volume *volume,
-			       struct afs_server **_server)
+struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *vnode)
 {
+	struct afs_volume *volume = vnode->volume;
 	struct afs_server *server;
 	int ret, state, loop;
 
 	_enter("%s", volume->vlocation->vldb.name);
 
+	/* stick with the server we're already using if we can */
+	if (vnode->server && vnode->server->fs_state == 0) {
+		afs_get_server(vnode->server);
+		_leave(" = %p [current]", vnode->server);
+		return vnode->server;
+	}
+
 	down_read(&volume->server_sem);
 
 	/* handle the no-server case */
@@ -318,7 +232,7 @@
 		ret = volume->rjservers ? -ENOMEDIUM : -ESTALE;
 		up_read(&volume->server_sem);
 		_leave(" = %d [no servers]", ret);
-		return ret;
+		return ERR_PTR(ret);
 	}
 
 	/* basically, just search the list for the first live server and use
@@ -328,15 +242,16 @@
 		server = volume->servers[loop];
 		state = server->fs_state;
 
+		_debug("consider %d [%d]", loop, state);
+
 		switch (state) {
 			/* found an apparently healthy server */
 		case 0:
 			afs_get_server(server);
 			up_read(&volume->server_sem);
-			*_server = server;
-			_leave(" = 0 (picked %08x)",
-			       ntohl(server->addr.s_addr));
-			return 0;
+			_leave(" = %p (picked %08x)",
+			       server, ntohl(server->addr.s_addr));
+			return server;
 
 		case -ENETUNREACH:
 			if (ret == 0)
@@ -372,20 +287,21 @@
 	 */
 	up_read(&volume->server_sem);
 	_leave(" = %d", ret);
-	return ret;
-} /* end afs_volume_pick_fileserver() */
+	return ERR_PTR(ret);
+}
 
-/*****************************************************************************/
 /*
  * release a server after use
  * - releases the ref on the server struct that was acquired by picking
  * - records result of using a particular server to access a volume
  * - return 0 to try again, 1 if okay or to issue error
+ * - the caller must release the server struct if result was 0
  */
-int afs_volume_release_fileserver(struct afs_volume *volume,
+int afs_volume_release_fileserver(struct afs_vnode *vnode,
 				  struct afs_server *server,
 				  int result)
 {
+	struct afs_volume *volume = vnode->volume;
 	unsigned loop;
 
 	_enter("%s,%08x,%d",
@@ -396,14 +312,16 @@
 		/* success */
 	case 0:
 		server->fs_act_jif = jiffies;
-		break;
+		server->fs_state = 0;
+		_leave("");
+		return 1;
 
 		/* the fileserver denied all knowledge of the volume */
 	case -ENOMEDIUM:
 		server->fs_act_jif = jiffies;
 		down_write(&volume->server_sem);
 
-		/* first, find where the server is in the active list (if it
+		/* firstly, find where the server is in the active list (if it
 		 * is) */
 		for (loop = 0; loop < volume->nservers; loop++)
 			if (volume->servers[loop] == server)
@@ -441,6 +359,7 @@
 	case -ENETUNREACH:
 	case -EHOSTUNREACH:
 	case -ECONNREFUSED:
+	case -ETIME:
 	case -ETIMEDOUT:
 	case -EREMOTEIO:
 		/* mark the server as dead
@@ -460,60 +379,17 @@
 		server->fs_act_jif = jiffies;
 	case -ENOMEM:
 	case -ENONET:
-		break;
+		/* tell the caller to accept the result */
+		afs_put_server(server);
+		_leave(" [local failure]");
+		return 1;
 	}
 
-	/* tell the caller to accept the result */
-	afs_put_server(server);
-	_leave("");
-	return 1;
-
 	/* tell the caller to loop around and try the next server */
- try_next_server_upw:
+try_next_server_upw:
 	up_write(&volume->server_sem);
- try_next_server:
+try_next_server:
 	afs_put_server(server);
 	_leave(" [try next server]");
 	return 0;
-
-} /* end afs_volume_release_fileserver() */
-
-/*****************************************************************************/
-/*
- * match a volume hash record stored in the cache
- */
-#ifdef AFS_CACHING_SUPPORT
-static cachefs_match_val_t afs_volume_cache_match(void *target,
-						  const void *entry)
-{
-	const struct afs_cache_vhash *vhash = entry;
-	struct afs_volume *volume = target;
-
-	_enter("{%u},{%u}", volume->type, vhash->vtype);
-
-	if (volume->type == vhash->vtype) {
-		_leave(" = SUCCESS");
-		return CACHEFS_MATCH_SUCCESS;
-	}
-
-	_leave(" = FAILED");
-	return CACHEFS_MATCH_FAILED;
-} /* end afs_volume_cache_match() */
-#endif
-
-/*****************************************************************************/
-/*
- * update a volume hash record stored in the cache
- */
-#ifdef AFS_CACHING_SUPPORT
-static void afs_volume_cache_update(void *source, void *entry)
-{
-	struct afs_cache_vhash *vhash = entry;
-	struct afs_volume *volume = source;
-
-	_enter("");
-
-	vhash->vtype = volume->type;
-
-} /* end afs_volume_cache_update() */
-#endif
+}
diff --git a/fs/afs/volume.h b/fs/afs/volume.h
deleted file mode 100644
index bfdcf19..0000000
--- a/fs/afs/volume.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/* volume.h: AFS volume management
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_AFS_VOLUME_H
-#define _LINUX_AFS_VOLUME_H
-
-#include "types.h"
-#include "fsclient.h"
-#include "kafstimod.h"
-#include "kafsasyncd.h"
-#include "cache.h"
-
-typedef enum {
-	AFS_VLUPD_SLEEP,		/* sleeping waiting for update timer to fire */
-	AFS_VLUPD_PENDING,		/* on pending queue */
-	AFS_VLUPD_INPROGRESS,		/* op in progress */
-	AFS_VLUPD_BUSYSLEEP,		/* sleeping because server returned EBUSY */
-	
-} __attribute__((packed)) afs_vlocation_upd_t;
-
-/*****************************************************************************/
-/*
- * entry in the cached volume location catalogue
- */
-struct afs_cache_vlocation
-{
-	uint8_t			name[64];	/* volume name (lowercase, padded with NULs) */
-	uint8_t			nservers;	/* number of entries used in servers[] */
-	uint8_t			vidmask;	/* voltype mask for vid[] */
-	uint8_t			srvtmask[8];	/* voltype masks for servers[] */
-#define AFS_VOL_VTM_RW	0x01 /* R/W version of the volume is available (on this server) */
-#define AFS_VOL_VTM_RO	0x02 /* R/O version of the volume is available (on this server) */
-#define AFS_VOL_VTM_BAK	0x04 /* backup version of the volume is available (on this server) */
-
-	afs_volid_t		vid[3];		/* volume IDs for R/W, R/O and Bak volumes */
-	struct in_addr		servers[8];	/* fileserver addresses */
-	time_t			rtime;		/* last retrieval time */
-};
-
-#ifdef AFS_CACHING_SUPPORT
-extern struct cachefs_index_def afs_vlocation_cache_index_def;
-#endif
-
-/*****************************************************************************/
-/*
- * volume -> vnode hash table entry
- */
-struct afs_cache_vhash
-{
-	afs_voltype_t		vtype;		/* which volume variation */
-	uint8_t			hash_bucket;	/* which hash bucket this represents */
-} __attribute__((packed));
-
-#ifdef AFS_CACHING_SUPPORT
-extern struct cachefs_index_def afs_volume_cache_index_def;
-#endif
-
-/*****************************************************************************/
-/*
- * AFS volume location record
- */
-struct afs_vlocation
-{
-	atomic_t		usage;
-	struct list_head	link;		/* link in cell volume location list */
-	struct afs_timer	timeout;	/* decaching timer */
-	struct afs_cell		*cell;		/* cell to which volume belongs */
-#ifdef AFS_CACHING_SUPPORT
-	struct cachefs_cookie	*cache;		/* caching cookie */
-#endif
-	struct afs_cache_vlocation vldb;	/* volume information DB record */
-	struct afs_volume	*vols[3];	/* volume access record pointer (index by type) */
-	rwlock_t		lock;		/* access lock */
-	unsigned long		read_jif;	/* time at which last read from vlserver */
-	struct afs_timer	upd_timer;	/* update timer */
-	struct afs_async_op	upd_op;		/* update operation */
-	afs_vlocation_upd_t	upd_state;	/* update state */
-	unsigned short		upd_first_svix;	/* first server index during update */
-	unsigned short		upd_curr_svix;	/* current server index during update */
-	unsigned short		upd_rej_cnt;	/* ENOMEDIUM count during update */
-	unsigned short		upd_busy_cnt;	/* EBUSY count during update */
-	unsigned short		valid;		/* T if valid */
-};
-
-extern int afs_vlocation_lookup(struct afs_cell *cell,
-				const char *name,
-				unsigned namesz,
-				struct afs_vlocation **_vlocation);
-
-#define afs_get_vlocation(V) do { atomic_inc(&(V)->usage); } while(0)
-
-extern void afs_put_vlocation(struct afs_vlocation *vlocation);
-extern void afs_vlocation_do_timeout(struct afs_vlocation *vlocation);
-
-/*****************************************************************************/
-/*
- * AFS volume access record
- */
-struct afs_volume
-{
-	atomic_t		usage;
-	struct afs_cell		*cell;		/* cell to which belongs (unrefd ptr) */
-	struct afs_vlocation	*vlocation;	/* volume location */
-#ifdef AFS_CACHING_SUPPORT
-	struct cachefs_cookie	*cache;		/* caching cookie */
-#endif
-	afs_volid_t		vid;		/* volume ID */
-	afs_voltype_t		type;		/* type of volume */
-	char			type_force;	/* force volume type (suppress R/O -> R/W) */
-	unsigned short		nservers;	/* number of server slots filled */
-	unsigned short		rjservers;	/* number of servers discarded due to -ENOMEDIUM */
-	struct afs_server	*servers[8];	/* servers on which volume resides (ordered) */
-	struct rw_semaphore	server_sem;	/* lock for accessing current server */
-};
-
-extern int afs_volume_lookup(const char *name,
-			     struct afs_cell *cell,
-			     int rwpath,
-			     struct afs_volume **_volume);
-
-#define afs_get_volume(V) do { atomic_inc(&(V)->usage); } while(0)
-
-extern void afs_put_volume(struct afs_volume *volume);
-
-extern int afs_volume_pick_fileserver(struct afs_volume *volume,
-				      struct afs_server **_server);
-
-extern int afs_volume_release_fileserver(struct afs_volume *volume,
-					 struct afs_server *server,
-					 int result);
-
-#endif /* _LINUX_AFS_VOLUME_H */
diff --git a/fs/bio.c b/fs/bio.c
index 7618bcb..693940d 100644
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -28,7 +28,7 @@
 #include <linux/blktrace_api.h>
 #include <scsi/sg.h>		/* for struct sg_iovec */
 
-#define BIO_POOL_SIZE 256
+#define BIO_POOL_SIZE 2
 
 static struct kmem_cache *bio_slab __read_mostly;
 
@@ -38,7 +38,7 @@
  * a small number of entries is fine, not going to be performance critical.
  * basically we just need to survive
  */
-#define BIO_SPLIT_ENTRIES 8	
+#define BIO_SPLIT_ENTRIES 2
 mempool_t *bio_split_pool __read_mostly;
 
 struct biovec_slab {
@@ -1120,7 +1120,7 @@
  * create memory pools for biovec's in a bio_set.
  * use the global biovec slabs created for general use.
  */
-static int biovec_create_pools(struct bio_set *bs, int pool_entries, int scale)
+static int biovec_create_pools(struct bio_set *bs, int pool_entries)
 {
 	int i;
 
@@ -1128,9 +1128,6 @@
 		struct biovec_slab *bp = bvec_slabs + i;
 		mempool_t **bvp = bs->bvec_pools + i;
 
-		if (pool_entries > 1 && i >= scale)
-			pool_entries >>= 1;
-
 		*bvp = mempool_create_slab_pool(pool_entries, bp->slab);
 		if (!*bvp)
 			return -ENOMEM;
@@ -1161,7 +1158,7 @@
 	kfree(bs);
 }
 
-struct bio_set *bioset_create(int bio_pool_size, int bvec_pool_size, int scale)
+struct bio_set *bioset_create(int bio_pool_size, int bvec_pool_size)
 {
 	struct bio_set *bs = kzalloc(sizeof(*bs), GFP_KERNEL);
 
@@ -1172,7 +1169,7 @@
 	if (!bs->bio_pool)
 		goto bad;
 
-	if (!biovec_create_pools(bs, bvec_pool_size, scale))
+	if (!biovec_create_pools(bs, bvec_pool_size))
 		return bs;
 
 bad:
@@ -1196,38 +1193,12 @@
 
 static int __init init_bio(void)
 {
-	int megabytes, bvec_pool_entries;
-	int scale = BIOVEC_NR_POOLS;
-
 	bio_slab = kmem_cache_create("bio", sizeof(struct bio), 0,
 				SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
 
 	biovec_init_slabs();
 
-	megabytes = nr_free_pages() >> (20 - PAGE_SHIFT);
-
-	/*
-	 * find out where to start scaling
-	 */
-	if (megabytes <= 16)
-		scale = 0;
-	else if (megabytes <= 32)
-		scale = 1;
-	else if (megabytes <= 64)
-		scale = 2;
-	else if (megabytes <= 96)
-		scale = 3;
-	else if (megabytes <= 128)
-		scale = 4;
-
-	/*
-	 * Limit number of entries reserved -- mempools are only used when
-	 * the system is completely unable to allocate memory, so we only
-	 * need enough to make progress.
-	 */
-	bvec_pool_entries = 1 + scale;
-
-	fs_bio_set = bioset_create(BIO_POOL_SIZE, bvec_pool_entries, scale);
+	fs_bio_set = bioset_create(BIO_POOL_SIZE, 2);
 	if (!fs_bio_set)
 		panic("bio: can't allocate bios\n");
 
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c
index 8b1c5d8..c68b055 100644
--- a/fs/compat_ioctl.c
+++ b/fs/compat_ioctl.c
@@ -266,6 +266,23 @@
 	return err;
 }
 
+static int do_siocgstampns(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+	struct compat_timespec __user *up = compat_ptr(arg);
+	struct timespec kts;
+	mm_segment_t old_fs = get_fs();
+	int err;
+
+	set_fs(KERNEL_DS);
+	err = sys_ioctl(fd, cmd, (unsigned long)&kts);
+	set_fs(old_fs);
+	if (!err) {
+		err = put_user(kts.tv_sec, &up->tv_sec);
+		err |= __put_user(kts.tv_nsec, &up->tv_nsec);
+	}
+	return err;
+}
+
 struct ifmap32 {
 	compat_ulong_t mem_start;
 	compat_ulong_t mem_end;
@@ -2437,6 +2454,7 @@
 /* Note SIOCRTMSG is no longer, so this is safe and * the user would have seen just an -EINVAL anyways. */
 HANDLE_IOCTL(SIOCRTMSG, ret_einval)
 HANDLE_IOCTL(SIOCGSTAMP, do_siocgstamp)
+HANDLE_IOCTL(SIOCGSTAMPNS, do_siocgstampns)
 #endif
 #ifdef CONFIG_BLOCK
 HANDLE_IOCTL(HDIO_GETGEO, hdio_getgeo)
diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
index 682f928..2e124e0 100644
--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -179,6 +179,48 @@
 }
 EXPORT_SYMBOL_GPL(debugfs_create_u32);
 
+static void debugfs_u64_set(void *data, u64 val)
+{
+	*(u64 *)data = val;
+}
+
+static u64 debugfs_u64_get(void *data)
+{
+	return *(u64 *)data;
+}
+DEFINE_SIMPLE_ATTRIBUTE(fops_u64, debugfs_u64_get, debugfs_u64_set, "%llu\n");
+
+/**
+ * debugfs_create_u64 - create a debugfs file that is used to read and write an unsigned 64-bit value
+ * @name: a pointer to a string containing the name of the file to create.
+ * @mode: the permission that the file should have
+ * @parent: a pointer to the parent dentry for this file.  This should be a
+ *          directory dentry if set.  If this parameter is %NULL, then the
+ *          file will be created in the root of the debugfs filesystem.
+ * @value: a pointer to the variable that the file should read to and write
+ *         from.
+ *
+ * This function creates a file in debugfs with the given name that
+ * contains the value of the variable @value.  If the @mode variable is so
+ * set, it can be read from, and written to.
+ *
+ * This function will return a pointer to a dentry if it succeeds.  This
+ * pointer must be passed to the debugfs_remove() function when the file is
+ * to be removed (no automatic cleanup happens if your module is unloaded,
+ * you are responsible here.)  If an error occurs, %NULL will be returned.
+ *
+ * If debugfs is not enabled in the kernel, the value -%ENODEV will be
+ * returned.  It is not wise to check for this value, but rather, check for
+ * %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
+ * code.
+ */
+struct dentry *debugfs_create_u64(const char *name, mode_t mode,
+				 struct dentry *parent, u64 *value)
+{
+	return debugfs_create_file(name, mode, parent, value, &fops_u64);
+}
+EXPORT_SYMBOL_GPL(debugfs_create_u64);
+
 static ssize_t read_file_bool(struct file *file, char __user *user_buf,
 			      size_t count, loff_t *ppos)
 {
diff --git a/fs/ecryptfs/netlink.c b/fs/ecryptfs/netlink.c
index e3aa225..fe91863 100644
--- a/fs/ecryptfs/netlink.c
+++ b/fs/ecryptfs/netlink.c
@@ -97,7 +97,7 @@
  */
 static int ecryptfs_process_nl_response(struct sk_buff *skb)
 {
-	struct nlmsghdr *nlh = (struct nlmsghdr*)skb->data;
+	struct nlmsghdr *nlh = nlmsg_hdr(skb);
 	struct ecryptfs_message *msg = NLMSG_DATA(nlh);
 	int rc;
 
@@ -181,7 +181,7 @@
 				"rc = [%d]\n", rc);
 		return;
 	}
-	nlh = (struct nlmsghdr *)skb->data;
+	nlh = nlmsg_hdr(skb);
 	if (!NLMSG_OK(nlh, skb->len)) {
 		ecryptfs_printk(KERN_ERR, "Received corrupt netlink "
 				"message\n");
@@ -229,7 +229,7 @@
 
 	ecryptfs_nl_sock = netlink_kernel_create(NETLINK_ECRYPTFS, 0,
 						 ecryptfs_receive_nl_message,
-						 THIS_MODULE);
+						 NULL, THIS_MODULE);
 	if (!ecryptfs_nl_sock) {
 		rc = -EIO;
 		ecryptfs_printk(KERN_ERR, "Failed to create netlink socket\n");
diff --git a/fs/jffs2/LICENCE b/fs/jffs2/LICENCE
index cd81d83..5628859 100644
--- a/fs/jffs2/LICENCE
+++ b/fs/jffs2/LICENCE
@@ -1,7 +1,7 @@
 The files in this directory and elsewhere which refer to this LICENCE
 file are part of JFFS2, the Journalling Flash File System v2.
 
-	Copyright (C) 2001, 2002 Red Hat, Inc.
+	Copyright © 2001-2007 Red Hat, Inc. and others
 
 JFFS2 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
@@ -28,8 +28,3 @@
 This exception does not invalidate any other reasons why a work based on
 this file might be covered by the GNU General Public License.
 
-For information on obtaining alternative licences for JFFS2, see 
-http://sources.redhat.com/jffs2/jffs2-licence.html
-
-
-	$Id: LICENCE,v 1.1 2002/05/20 14:56:37 dwmw2 Exp $
diff --git a/fs/jffs2/Makefile b/fs/jffs2/Makefile
index 7f28ee0..c32b241 100644
--- a/fs/jffs2/Makefile
+++ b/fs/jffs2/Makefile
@@ -1,7 +1,6 @@
 #
 # Makefile for the Linux Journalling Flash File System v2 (JFFS2)
 #
-# $Id: Makefile.common,v 1.11 2005/09/07 08:34:53 havasi Exp $
 #
 
 obj-$(CONFIG_JFFS2_FS) += jffs2.o
diff --git a/fs/jffs2/README.Locking b/fs/jffs2/README.Locking
index c8f0bd6..d14d5a4 100644
--- a/fs/jffs2/README.Locking
+++ b/fs/jffs2/README.Locking
@@ -1,4 +1,3 @@
-	$Id: README.Locking,v 1.12 2005/04/13 13:22:35 dwmw2 Exp $
 
 	JFFS2 LOCKING DOCUMENTATION
 	---------------------------
diff --git a/fs/jffs2/TODO b/fs/jffs2/TODO
index d0e23b2..5d3ea40 100644
--- a/fs/jffs2/TODO
+++ b/fs/jffs2/TODO
@@ -1,4 +1,3 @@
-$Id: TODO,v 1.18 2005/09/22 11:24:56 dedekind Exp $
 
  - support asynchronous operation -- add a per-fs 'reserved_space' count,
    let each outstanding write reserve the _maximum_ amount of physical
@@ -30,8 +29,6 @@
      the full dirent, we only need to go to the flash in lookup() when we think we've
      got a match, and in readdir(). 
    - Doubly-linked next_in_ino list to allow us to free obsoleted raw_node_refs immediately?
-   - Remove totlen from jffs2_raw_node_ref? Need to have totlen passed into
-	jffs2_mark_node_obsolete(). Can all callers work it out?
    - Remove size from jffs2_raw_node_frag. 
 
 dedekind:
diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c
index 73f0d60..a46101e 100644
--- a/fs/jffs2/acl.c
+++ b/fs/jffs2/acl.c
@@ -1,13 +1,14 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2006  NEC Corporation
+ * Copyright © 2006  NEC Corporation
  *
  * Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
  */
+
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h
index fa327db..c84378c 100644
--- a/fs/jffs2/acl.h
+++ b/fs/jffs2/acl.h
@@ -1,13 +1,14 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2006  NEC Corporation
+ * Copyright © 2006  NEC Corporation
  *
  * Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
  */
+
 struct jffs2_acl_entry {
 	jint16_t	e_tag;
 	jint16_t	e_perm;
diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c
index 888f236..0c82dfc 100644
--- a/fs/jffs2/background.c
+++ b/fs/jffs2/background.c
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: background.c,v 1.54 2005/05/20 21:37:12 gleixner Exp $
- *
  */
 
 #include <linux/kernel.h>
diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c
index 07119c4..0ca2fff 100644
--- a/fs/jffs2/build.c
+++ b/fs/jffs2/build.c
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: build.c,v 1.85 2005/11/07 11:14:38 gleixner Exp $
- *
  */
 
 #include <linux/kernel.h>
diff --git a/fs/jffs2/compr.c b/fs/jffs2/compr.c
index 7001ba2..485d065 100644
--- a/fs/jffs2/compr.c
+++ b/fs/jffs2/compr.c
@@ -1,16 +1,14 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  * Created by Arjan van de Ven <arjanv@redhat.com>
  *
- * Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ * Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
  *                    University of Szeged, Hungary
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: compr.c,v 1.46 2005/11/07 11:14:38 gleixner Exp $
- *
  */
 
 #include "compr.h"
@@ -268,144 +266,6 @@
         return 0;
 }
 
-#ifdef CONFIG_JFFS2_PROC
-
-#define JFFS2_STAT_BUF_SIZE 16000
-
-char *jffs2_list_compressors(void)
-{
-        struct jffs2_compressor *this;
-        char *buf, *act_buf;
-
-        act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL);
-        list_for_each_entry(this, &jffs2_compressor_list, list) {
-                act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority);
-                if ((this->disabled)||(!this->compress))
-                        act_buf += sprintf(act_buf,"disabled");
-                else
-                        act_buf += sprintf(act_buf,"enabled");
-                act_buf += sprintf(act_buf,"\n");
-        }
-        return buf;
-}
-
-char *jffs2_stats(void)
-{
-        struct jffs2_compressor *this;
-        char *buf, *act_buf;
-
-        act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL);
-
-        act_buf += sprintf(act_buf,"JFFS2 compressor statistics:\n");
-        act_buf += sprintf(act_buf,"%10s   ","none");
-        act_buf += sprintf(act_buf,"compr: %d blocks (%d)  decompr: %d blocks\n", none_stat_compr_blocks,
-                           none_stat_compr_size, none_stat_decompr_blocks);
-        spin_lock(&jffs2_compressor_list_lock);
-        list_for_each_entry(this, &jffs2_compressor_list, list) {
-                act_buf += sprintf(act_buf,"%10s ",this->name);
-                if ((this->disabled)||(!this->compress))
-                        act_buf += sprintf(act_buf,"- ");
-                else
-                        act_buf += sprintf(act_buf,"+ ");
-                act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d)  decompr: %d blocks ", this->stat_compr_blocks,
-                                   this->stat_compr_new_size, this->stat_compr_orig_size,
-                                   this->stat_decompr_blocks);
-                act_buf += sprintf(act_buf,"\n");
-        }
-        spin_unlock(&jffs2_compressor_list_lock);
-
-        return buf;
-}
-
-char *jffs2_get_compression_mode_name(void)
-{
-        switch (jffs2_compression_mode) {
-        case JFFS2_COMPR_MODE_NONE:
-                return "none";
-        case JFFS2_COMPR_MODE_PRIORITY:
-                return "priority";
-        case JFFS2_COMPR_MODE_SIZE:
-                return "size";
-        }
-        return "unkown";
-}
-
-int jffs2_set_compression_mode_name(const char *name)
-{
-        if (!strcmp("none",name)) {
-                jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
-                return 0;
-        }
-        if (!strcmp("priority",name)) {
-                jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
-                return 0;
-        }
-        if (!strcmp("size",name)) {
-                jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
-                return 0;
-        }
-        return 1;
-}
-
-static int jffs2_compressor_Xable(const char *name, int disabled)
-{
-        struct jffs2_compressor *this;
-        spin_lock(&jffs2_compressor_list_lock);
-        list_for_each_entry(this, &jffs2_compressor_list, list) {
-                if (!strcmp(this->name, name)) {
-                        this->disabled = disabled;
-                        spin_unlock(&jffs2_compressor_list_lock);
-                        return 0;
-                }
-        }
-        spin_unlock(&jffs2_compressor_list_lock);
-        printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name);
-        return 1;
-}
-
-int jffs2_enable_compressor_name(const char *name)
-{
-        return jffs2_compressor_Xable(name, 0);
-}
-
-int jffs2_disable_compressor_name(const char *name)
-{
-        return jffs2_compressor_Xable(name, 1);
-}
-
-int jffs2_set_compressor_priority(const char *name, int priority)
-{
-        struct jffs2_compressor *this,*comp;
-        spin_lock(&jffs2_compressor_list_lock);
-        list_for_each_entry(this, &jffs2_compressor_list, list) {
-                if (!strcmp(this->name, name)) {
-                        this->priority = priority;
-                        comp = this;
-                        goto reinsert;
-                }
-        }
-        spin_unlock(&jffs2_compressor_list_lock);
-        printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name);
-        return 1;
-reinsert:
-        /* list is sorted in the order of priority, so if
-           we change it we have to reinsert it into the
-           good place */
-        list_del(&comp->list);
-        list_for_each_entry(this, &jffs2_compressor_list, list) {
-                if (this->priority < comp->priority) {
-                        list_add(&comp->list, this->list.prev);
-                        spin_unlock(&jffs2_compressor_list_lock);
-                        return 0;
-                }
-        }
-        list_add_tail(&comp->list, &jffs2_compressor_list);
-        spin_unlock(&jffs2_compressor_list_lock);
-        return 0;
-}
-
-#endif
-
 void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
 {
         if (orig != comprbuf)
diff --git a/fs/jffs2/compr.h b/fs/jffs2/compr.h
index 509b8b1..68cc701 100644
--- a/fs/jffs2/compr.h
+++ b/fs/jffs2/compr.h
@@ -1,13 +1,10 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ * Copyright © 2004   Ferenc Havasi <havasi@inf.u-szeged.hu>,
  *                    University of Szeged, Hungary
  *
- * For licensing information, see the file 'LICENCE' in the
- * jffs2 directory.
- *
- * $Id: compr.h,v 1.9 2005/11/07 11:14:38 gleixner Exp $
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
  */
 
@@ -76,16 +73,6 @@
 
 void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig);
 
-#ifdef CONFIG_JFFS2_PROC
-int jffs2_enable_compressor_name(const char *name);
-int jffs2_disable_compressor_name(const char *name);
-int jffs2_set_compression_mode_name(const char *mode_name);
-char *jffs2_get_compression_mode_name(void);
-int jffs2_set_compressor_priority(const char *mode_name, int priority);
-char *jffs2_list_compressors(void);
-char *jffs2_stats(void);
-#endif
-
 /* Compressor modules */
 /* These functions will be called by jffs2_compressors_init/exit */
 
diff --git a/fs/jffs2/compr_rtime.c b/fs/jffs2/compr_rtime.c
index 2eb1b74..0d0bfd2 100644
--- a/fs/jffs2/compr_rtime.c
+++ b/fs/jffs2/compr_rtime.c
@@ -1,13 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by Arjan van de Ven <arjanv@redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: compr_rtime.c,v 1.14 2004/06/23 16:34:40 havasi Exp $
  *
  *
  * Very simple lz77-ish encoder.
diff --git a/fs/jffs2/compr_rubin.c b/fs/jffs2/compr_rubin.c
index e792e67..ea0431e 100644
--- a/fs/jffs2/compr_rubin.c
+++ b/fs/jffs2/compr_rubin.c
@@ -1,23 +1,94 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by Arjan van de Ven <arjanv@redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: compr_rubin.c,v 1.20 2004/06/23 16:34:40 havasi Exp $
- *
  */
 
 #include <linux/string.h>
 #include <linux/types.h>
 #include <linux/jffs2.h>
-#include "compr_rubin.h"
-#include "histo_mips.h"
+#include <linux/errno.h>
 #include "compr.h"
 
+
+#define RUBIN_REG_SIZE   16
+#define UPPER_BIT_RUBIN    (((long) 1)<<(RUBIN_REG_SIZE-1))
+#define LOWER_BITS_RUBIN   ((((long) 1)<<(RUBIN_REG_SIZE-1))-1)
+
+
+#define BIT_DIVIDER_MIPS 1043
+static int bits_mips[8] = { 277,249,290,267,229,341,212,241}; /* mips32 */
+
+#include <linux/errno.h>
+
+struct pushpull {
+	unsigned char *buf;
+	unsigned int buflen;
+	unsigned int ofs;
+	unsigned int reserve;
+};
+
+struct rubin_state {
+	unsigned long p;
+	unsigned long q;
+	unsigned long rec_q;
+	long bit_number;
+	struct pushpull pp;
+	int bit_divider;
+	int bits[8];
+};
+
+static inline void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen, unsigned ofs, unsigned reserve)
+{
+	pp->buf = buf;
+	pp->buflen = buflen;
+	pp->ofs = ofs;
+	pp->reserve = reserve;
+}
+
+static inline int pushbit(struct pushpull *pp, int bit, int use_reserved)
+{
+	if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve)) {
+		return -ENOSPC;
+	}
+
+	if (bit) {
+		pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs &7)));
+	}
+	else {
+		pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs &7)));
+	}
+	pp->ofs++;
+
+	return 0;
+}
+
+static inline int pushedbits(struct pushpull *pp)
+{
+	return pp->ofs;
+}
+
+static inline int pullbit(struct pushpull *pp)
+{
+	int bit;
+
+	bit = (pp->buf[pp->ofs >> 3] >> (7-(pp->ofs & 7))) & 1;
+
+	pp->ofs++;
+	return bit;
+}
+
+static inline int pulledbits(struct pushpull *pp)
+{
+	return pp->ofs;
+}
+
+
 static void init_rubin(struct rubin_state *rs, int div, int *bits)
 {
 	int c;
diff --git a/fs/jffs2/compr_rubin.h b/fs/jffs2/compr_rubin.h
deleted file mode 100644
index bf1a934..0000000
--- a/fs/jffs2/compr_rubin.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* Rubin encoder/decoder header       */
-/* work started at   : aug   3, 1994  */
-/* last modification : aug  15, 1994  */
-/* $Id: compr_rubin.h,v 1.7 2005/11/07 11:14:38 gleixner Exp $ */
-
-#include "pushpull.h"
-
-#define RUBIN_REG_SIZE   16
-#define UPPER_BIT_RUBIN    (((long) 1)<<(RUBIN_REG_SIZE-1))
-#define LOWER_BITS_RUBIN   ((((long) 1)<<(RUBIN_REG_SIZE-1))-1)
-
-
-struct rubin_state {
-	unsigned long p;
-	unsigned long q;
-	unsigned long rec_q;
-	long bit_number;
-	struct pushpull pp;
-	int bit_divider;
-	int bits[8];
-};
diff --git a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c
index 0c1fc6e..2b87fcc 100644
--- a/fs/jffs2/compr_zlib.c
+++ b/fs/jffs2/compr_zlib.c
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: compr_zlib.c,v 1.32 2005/11/07 11:14:38 gleixner Exp $
- *
  */
 
 #if !defined(__KERNEL__) && !defined(__ECOS)
diff --git a/fs/jffs2/comprtest.c b/fs/jffs2/comprtest.c
deleted file mode 100644
index f0fb8be..0000000
--- a/fs/jffs2/comprtest.c
+++ /dev/null
@@ -1,307 +0,0 @@
-/* $Id: comprtest.c,v 1.6 2005/11/07 11:14:38 gleixner Exp $ */
-
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/module.h>
-#include <asm/types.h>
-#if 0
-#define TESTDATA_LEN 512
-static unsigned char testdata[TESTDATA_LEN] = {
- 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x83, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00,
- 0xb0, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x06, 0x00, 0x28, 0x00,
- 0x1e, 0x00, 0x1b, 0x00, 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x80, 0x04, 0x08,
- 0x34, 0x80, 0x04, 0x08, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0xf4, 0x80, 0x04, 0x08,
- 0xf4, 0x80, 0x04, 0x08, 0x13, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x08,
- 0x00, 0x80, 0x04, 0x08, 0x0d, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x10, 0x95, 0x04, 0x08,
- 0x10, 0x95, 0x04, 0x08, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
- 0x00, 0x10, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x58, 0x05, 0x00, 0x00, 0x58, 0x95, 0x04, 0x08,
- 0x58, 0x95, 0x04, 0x08, 0xa0, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x08, 0x81, 0x04, 0x08,
- 0x08, 0x81, 0x04, 0x08, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x6c, 0x64, 0x2d, 0x6c, 0x69, 0x6e, 0x75,
- 0x78, 0x2e, 0x73, 0x6f, 0x2e, 0x32, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
- 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00,
- 0x0c, 0x83, 0x04, 0x08, 0x81, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
- 0x1c, 0x83, 0x04, 0x08, 0xac, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00,
- 0x2c, 0x83, 0x04, 0x08, 0xdd, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
- 0x3c, 0x83, 0x04, 0x08, 0x2e, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
- 0x4c, 0x83, 0x04, 0x08, 0x7d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
- 0x00, 0x85, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x67,
- 0x6d, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x5f, 0x00, 0x6c, 0x69, 0x62, 0x63,
- 0x2e, 0x73, 0x6f, 0x2e, 0x36, 0x00, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x00, 0x5f, 0x5f, 0x63};
-#else
-#define TESTDATA_LEN 3481
-static unsigned char testdata[TESTDATA_LEN] = {
- 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x22, 0x64, 0x62, 0x65, 0x6e, 0x63, 0x68,
- 0x2e, 0x68, 0x22, 0x0a, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x4d, 0x41, 0x58,
- 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x20, 0x31, 0x30, 0x30, 0x30, 0x0a, 0x0a, 0x73, 0x74, 0x61,
- 0x74, 0x69, 0x63, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x62, 0x75, 0x66, 0x5b, 0x37, 0x30, 0x30,
- 0x30, 0x30, 0x5d, 0x3b, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x20,
- 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x3b, 0x0a, 0x0a, 0x73, 0x74, 0x61,
- 0x74, 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x7b, 0x0a, 0x09, 0x69, 0x6e,
- 0x74, 0x20, 0x66, 0x64, 0x3b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c,
- 0x65, 0x3b, 0x0a, 0x7d, 0x20, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x4d, 0x41, 0x58, 0x5f,
- 0x46, 0x49, 0x4c, 0x45, 0x53, 0x5d, 0x3b, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f,
- 0x5f, 0x75, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e,
- 0x61, 0x6d, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72,
- 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x75,
- 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x21, 0x3d, 0x20,
- 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28,
- 0x25, 0x64, 0x29, 0x20, 0x75, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x25, 0x73, 0x20, 0x66, 0x61,
- 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09,
- 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75,
- 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72,
- 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a,
- 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x66,
- 0x69, 0x6c, 0x65, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x64, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20,
- 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x3b, 0x0a,
- 0x09, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a,
- 0x09, 0x09, 0x73, 0x20, 0x3d, 0x20, 0x4d, 0x49, 0x4e, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66,
- 0x28, 0x62, 0x75, 0x66, 0x29, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09,
- 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x66, 0x64, 0x2c, 0x20, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x73,
- 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x2d, 0x3d, 0x20, 0x73, 0x3b, 0x0a,
- 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x6f, 0x70,
- 0x65, 0x6e, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20,
- 0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20,
- 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x64, 0x2c,
- 0x20, 0x69, 0x3b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x20, 0x3d,
- 0x20, 0x4f, 0x5f, 0x52, 0x44, 0x57, 0x52, 0x7c, 0x4f, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x3b,
- 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x73, 0x74, 0x61, 0x74, 0x20, 0x73, 0x74,
- 0x3b, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x63, 0x6f,
- 0x75, 0x6e, 0x74, 0x3b, 0x0a, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28,
- 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x69,
- 0x7a, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x20, 0x7c,
- 0x3d, 0x20, 0x4f, 0x5f, 0x54, 0x52, 0x55, 0x4e, 0x43, 0x3b, 0x0a, 0x0a, 0x09, 0x66, 0x64, 0x20,
- 0x3d, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x66, 0x6c,
- 0x61, 0x67, 0x73, 0x2c, 0x20, 0x30, 0x36, 0x30, 0x30, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20,
- 0x28, 0x66, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x2d, 0x31, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70,
- 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x6f, 0x70, 0x65, 0x6e,
- 0x20, 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x68,
- 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22,
- 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65,
- 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x68,
- 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28,
- 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72,
- 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x66, 0x73, 0x74, 0x61, 0x74, 0x28, 0x66, 0x64, 0x2c,
- 0x20, 0x26, 0x73, 0x74, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65,
- 0x20, 0x3e, 0x20, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b,
- 0x0a, 0x23, 0x69, 0x66, 0x20, 0x44, 0x45, 0x42, 0x55, 0x47, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69,
- 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64,
- 0x69, 0x6e, 0x67, 0x20, 0x25, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x25, 0x64, 0x20, 0x66, 0x72, 0x6f,
- 0x6d, 0x20, 0x25, 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66,
- 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x28, 0x69, 0x6e, 0x74,
- 0x29, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x23, 0x65,
- 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x09, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x66, 0x69,
- 0x6c, 0x65, 0x28, 0x66, 0x64, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x2d, 0x20, 0x73, 0x74,
- 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x20, 0x65, 0x6c,
- 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3c, 0x20, 0x73, 0x74,
- 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72,
- 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x69, 0x6e, 0x67,
- 0x20, 0x25, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x25, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x25,
- 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x28, 0x69, 0x6e,
- 0x74, 0x29, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09,
- 0x09, 0x66, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x28, 0x66, 0x64, 0x2c, 0x20, 0x73,
- 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69,
- 0x3d, 0x30, 0x3b, 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x3b, 0x69,
- 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, 0x61, 0x62,
- 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x3d, 0x20,
- 0x30, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66,
- 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53,
- 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x66, 0x69,
- 0x6c, 0x65, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x66, 0x75, 0x6c, 0x6c, 0x20, 0x66, 0x6f,
- 0x72, 0x20, 0x25, 0x73, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b,
- 0x0a, 0x09, 0x09, 0x65, 0x78, 0x69, 0x74, 0x28, 0x31, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09,
- 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65,
- 0x20, 0x3d, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x3b, 0x0a, 0x09, 0x66, 0x74, 0x61, 0x62,
- 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x64, 0x3b, 0x0a, 0x09,
- 0x69, 0x66, 0x20, 0x28, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2b, 0x2b, 0x20, 0x25, 0x20, 0x31, 0x30,
- 0x30, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e,
- 0x74, 0x66, 0x28, 0x22, 0x2e, 0x22, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76,
- 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x69, 0x6e, 0x74,
- 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x7a,
- 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x0a, 0x7b,
- 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x62,
- 0x75, 0x66, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x6d, 0x65, 0x6d, 0x73,
- 0x65, 0x74, 0x28, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f,
- 0x66, 0x28, 0x62, 0x75, 0x66, 0x29, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28,
- 0x69, 0x3d, 0x30, 0x3b, 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x3b,
- 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, 0x61,
- 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x3d,
- 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a,
- 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, 0x58,
- 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x31, 0x0a,
- 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64,
- 0x6f, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x3a, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20,
- 0x25, 0x64, 0x20, 0x77, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20,
- 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x25, 0x64, 0x20, 0x6f, 0x66, 0x73, 0x3d, 0x25, 0x64, 0x5c, 0x6e,
- 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e,
- 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c,
- 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x3b, 0x0a,
- 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b,
- 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x6c, 0x73, 0x65, 0x65, 0x6b, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c,
- 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c,
- 0x20, 0x53, 0x45, 0x45, 0x4b, 0x5f, 0x53, 0x45, 0x54, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20,
- 0x28, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d,
- 0x2e, 0x66, 0x64, 0x2c, 0x20, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20,
- 0x21, 0x3d, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69,
- 0x6e, 0x74, 0x66, 0x28, 0x22, 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65,
- 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x5c, 0x6e,
- 0x22, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d,
- 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, 0x69,
- 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73,
- 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29,
- 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20,
- 0x28, 0x69, 0x3d, 0x30, 0x3b, 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53,
- 0x3b, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74,
- 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d,
- 0x3d, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b,
- 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41,
- 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69,
- 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x72, 0x65, 0x61,
- 0x64, 0x3a, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x20, 0x77, 0x61, 0x73,
- 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x25,
- 0x64, 0x20, 0x6f, 0x66, 0x73, 0x3d, 0x25, 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e,
- 0x74, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c,
- 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75,
- 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x6c, 0x73, 0x65, 0x65, 0x6b, 0x28, 0x66, 0x74,
- 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73,
- 0x65, 0x74, 0x2c, 0x20, 0x53, 0x45, 0x45, 0x4b, 0x5f, 0x53, 0x45, 0x54, 0x29, 0x3b, 0x0a, 0x09,
- 0x72, 0x65, 0x61, 0x64, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66,
- 0x64, 0x2c, 0x20, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x7d,
- 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28,
- 0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69,
- 0x6e, 0x74, 0x20, 0x69, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x3d, 0x30, 0x3b,
- 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x3b, 0x69, 0x2b, 0x2b, 0x29,
- 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b,
- 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x68, 0x61, 0x6e,
- 0x64, 0x6c, 0x65, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09,
- 0x69, 0x66, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c,
- 0x45, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22,
- 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x3a, 0x20, 0x68,
- 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x20, 0x77, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x74,
- 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20,
- 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72,
- 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x66, 0x74, 0x61,
- 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x29, 0x3b, 0x0a, 0x09, 0x66, 0x74, 0x61,
- 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x20,
- 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x6d, 0x6b,
- 0x64, 0x69, 0x72, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29,
- 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61,
- 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x6d, 0x6b, 0x64, 0x69, 0x72,
- 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x30, 0x37, 0x30, 0x30, 0x29, 0x20, 0x21, 0x3d,
- 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x44, 0x45, 0x42, 0x55, 0x47, 0x0a,
- 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x6d, 0x6b, 0x64, 0x69, 0x72, 0x20,
- 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e,
- 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6e, 0x61,
- 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72,
- 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x7d, 0x0a,
- 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x72, 0x6d, 0x64, 0x69, 0x72,
- 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x0a, 0x7b, 0x0a,
- 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29,
- 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x72, 0x6d, 0x64, 0x69, 0x72, 0x28, 0x66, 0x6e,
- 0x61, 0x6d, 0x65, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70,
- 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x72, 0x6d, 0x64, 0x69, 0x72, 0x20, 0x25, 0x73, 0x20,
- 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20,
- 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c,
- 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29,
- 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f,
- 0x5f, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x6f, 0x6c,
- 0x64, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x6e, 0x65, 0x77, 0x29, 0x0a, 0x7b, 0x0a,
- 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x6f, 0x6c, 0x64, 0x29, 0x3b, 0x0a,
- 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x6e, 0x65, 0x77, 0x29, 0x3b, 0x0a,
- 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x6f, 0x6c, 0x64,
- 0x2c, 0x20, 0x6e, 0x65, 0x77, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09,
- 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20,
- 0x25, 0x73, 0x20, 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73,
- 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x6f, 0x6c, 0x64, 0x2c, 0x20, 0x6e, 0x65, 0x77, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72,
- 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d,
- 0x0a, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x28,
- 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74,
- 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74,
- 0x20, 0x73, 0x74, 0x61, 0x74, 0x20, 0x73, 0x74, 0x3b, 0x0a, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75,
- 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69,
- 0x66, 0x20, 0x28, 0x73, 0x74, 0x61, 0x74, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x26,
- 0x73, 0x74, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72,
- 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x73, 0x74,
- 0x61, 0x74, 0x3a, 0x20, 0x25, 0x73, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x25, 0x64, 0x20, 0x25,
- 0x73, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d,
- 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f,
- 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74,
- 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x53, 0x5f, 0x49,
- 0x53, 0x44, 0x49, 0x52, 0x28, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x29,
- 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28,
- 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x21, 0x3d, 0x20, 0x73, 0x69,
- 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22,
- 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x3a, 0x20, 0x25, 0x73,
- 0x20, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x25, 0x64, 0x20, 0x25,
- 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d,
- 0x65, 0x2c, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x29, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69,
- 0x7a, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a,
- 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x28,
- 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74,
- 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x64, 0x6f, 0x5f, 0x6f, 0x70, 0x65,
- 0x6e, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x35, 0x30, 0x30, 0x30, 0x2c, 0x20, 0x73,
- 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x64, 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28,
- 0x35, 0x30, 0x30, 0x30, 0x29, 0x3b, 0x0a, 0x7d, 0x0a
-};
-#endif
-static unsigned char comprbuf[TESTDATA_LEN];
-static unsigned char decomprbuf[TESTDATA_LEN];
-
-int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
-		     unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
-unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
-			     uint32_t *datalen, uint32_t *cdatalen);
-
-int init_module(void ) {
-	unsigned char comprtype;
-	uint32_t c, d;
-	int ret;
-
-	printk("Original data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
-	       testdata[0],testdata[1],testdata[2],testdata[3],
-	       testdata[4],testdata[5],testdata[6],testdata[7],
-	       testdata[8],testdata[9],testdata[10],testdata[11],
-	       testdata[12],testdata[13],testdata[14],testdata[15]);
-	d = TESTDATA_LEN;
-	c = TESTDATA_LEN;
-	comprtype = jffs2_compress(testdata, comprbuf, &d, &c);
-
-	printk("jffs2_compress used compression type %d. Compressed size %d, uncompressed size %d\n",
-	       comprtype, c, d);
-	printk("Compressed data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
-	       comprbuf[0],comprbuf[1],comprbuf[2],comprbuf[3],
-	       comprbuf[4],comprbuf[5],comprbuf[6],comprbuf[7],
-	       comprbuf[8],comprbuf[9],comprbuf[10],comprbuf[11],
-	       comprbuf[12],comprbuf[13],comprbuf[14],comprbuf[15]);
-
-	ret = jffs2_decompress(comprtype, comprbuf, decomprbuf, c, d);
-	printk("jffs2_decompress returned %d\n", ret);
-	printk("Decompressed data:  %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
-	       decomprbuf[0],decomprbuf[1],decomprbuf[2],decomprbuf[3],
-	       decomprbuf[4],decomprbuf[5],decomprbuf[6],decomprbuf[7],
-	       decomprbuf[8],decomprbuf[9],decomprbuf[10],decomprbuf[11],
-	       decomprbuf[12],decomprbuf[13],decomprbuf[14],decomprbuf[15]);
-	if (memcmp(decomprbuf, testdata, d))
-		printk("Compression and decompression corrupted data\n");
-	else
-		printk("Compression good for %d bytes\n", d);
-	return 1;
-}
diff --git a/fs/jffs2/debug.c b/fs/jffs2/debug.c
index 4189e4a..3a32c64 100644
--- a/fs/jffs2/debug.c
+++ b/fs/jffs2/debug.c
@@ -1,15 +1,14 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: debug.c,v 1.12 2005/11/07 11:14:39 gleixner Exp $
- *
  */
+
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/pagemap.h>
diff --git a/fs/jffs2/debug.h b/fs/jffs2/debug.h
index f89c85d..2a49f2c 100644
--- a/fs/jffs2/debug.h
+++ b/fs/jffs2/debug.h
@@ -1,15 +1,14 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: debug.h,v 1.21 2005/11/07 11:14:39 gleixner Exp $
- *
  */
+
 #ifndef _JFFS2_DEBUG_H_
 #define _JFFS2_DEBUG_H_
 
diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c
index 9fa2e27..c1dfca3 100644
--- a/fs/jffs2/dir.c
+++ b/fs/jffs2/dir.c
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: dir.c,v 1.90 2005/11/07 11:14:39 gleixner Exp $
- *
  */
 
 #include <linux/kernel.h>
diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c
index ad01210..66e7c2f 100644
--- a/fs/jffs2/erase.c
+++ b/fs/jffs2/erase.c
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: erase.c,v 1.85 2005/09/20 14:53:15 dedekind Exp $
- *
  */
 
 #include <linux/kernel.h>
@@ -333,7 +331,7 @@
 
 		*bad_offset = ofs;
 
-		ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf);
+		ret = c->mtd->read(c->mtd, ofs, readlen, &retlen, ebuf);
 		if (ret) {
 			printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret);
 			goto fail;
diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c
index e82eeaf..9987127 100644
--- a/fs/jffs2/file.c
+++ b/fs/jffs2/file.c
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: file.c,v 1.104 2005/10/18 23:29:35 tpoynor Exp $
- *
  */
 
 #include <linux/kernel.h>
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index abb90c0..1d3b7a9 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: fs.c,v 1.66 2005/09/27 13:17:29 dedekind Exp $
- *
  */
 
 #include <linux/capability.h>
@@ -672,6 +670,13 @@
 			return ret;
 	}
 
+	/* and an UBI volume */
+	if (jffs2_ubivol(c)) {
+		ret = jffs2_ubivol_setup(c);
+		if (ret)
+			return ret;
+	}
+
 	return ret;
 }
 
@@ -690,4 +695,9 @@
 	if (jffs2_nor_wbuf_flash(c)) {
 		jffs2_nor_wbuf_flash_cleanup(c);
 	}
+
+	/* and an UBI volume */
+	if (jffs2_ubivol(c)) {
+		jffs2_ubivol_cleanup(c);
+	}
 }
diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c
index 3a3cf22..2d99e06 100644
--- a/fs/jffs2/gc.c
+++ b/fs/jffs2/gc.c
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: gc.c,v 1.155 2005/11/07 11:14:39 gleixner Exp $
- *
  */
 
 #include <linux/kernel.h>
@@ -144,7 +142,8 @@
 			       c->unchecked_size);
 			jffs2_dbg_dump_block_lists_nolock(c);
 			spin_unlock(&c->erase_completion_lock);
-			BUG();
+			up(&c->alloc_sem);
+			return -ENOSPC;
 		}
 
 		spin_unlock(&c->erase_completion_lock);
diff --git a/fs/jffs2/ioctl.c b/fs/jffs2/ioctl.c
index 6909983..f4d525b 100644
--- a/fs/jffs2/ioctl.c
+++ b/fs/jffs2/ioctl.c
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: ioctl.c,v 1.10 2005/11/07 11:14:40 gleixner Exp $
- *
  */
 
 #include <linux/fs.h>
diff --git a/fs/jffs2/jffs2_fs_i.h b/fs/jffs2/jffs2_fs_i.h
index 3a56607..0b78fdc 100644
--- a/fs/jffs2/jffs2_fs_i.h
+++ b/fs/jffs2/jffs2_fs_i.h
@@ -1,4 +1,13 @@
-/* $Id: jffs2_fs_i.h,v 1.19 2005/11/07 11:14:52 gleixner Exp $ */
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright © 2001-2007 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
 
 #ifndef _JFFS2_FS_I
 #define _JFFS2_FS_I
diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h
index ea88f69..b13298a 100644
--- a/fs/jffs2/jffs2_fs_sb.h
+++ b/fs/jffs2/jffs2_fs_sb.h
@@ -1,4 +1,13 @@
-/* $Id: jffs2_fs_sb.h,v 1.54 2005/09/21 13:37:34 dedekind Exp $ */
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright © 2001-2007 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
 
 #ifndef _JFFS2_FS_SB
 #define _JFFS2_FS_SB
diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c
index 83f9881..35c1a5e 100644
--- a/fs/jffs2/malloc.c
+++ b/fs/jffs2/malloc.c
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: malloc.c,v 1.31 2005/11/07 11:14:40 gleixner Exp $
- *
  */
 
 #include <linux/kernel.h>
diff --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c
index 5a6b4d6..4bf8608 100644
--- a/fs/jffs2/nodelist.c
+++ b/fs/jffs2/nodelist.c
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: nodelist.c,v 1.115 2005/11/07 11:14:40 gleixner Exp $
- *
  */
 
 #include <linux/kernel.h>
@@ -54,7 +52,7 @@
 	*prev = new;
 }
 
-void jffs2_truncate_fragtree(struct jffs2_sb_info *c, struct rb_root *list, uint32_t size)
+uint32_t jffs2_truncate_fragtree(struct jffs2_sb_info *c, struct rb_root *list, uint32_t size)
 {
 	struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size);
 
@@ -76,18 +74,24 @@
 	}
 
 	if (size == 0)
-		return;
+		return 0;
 
-	/*
-	 * If the last fragment starts at the RAM page boundary, it is
-	 * REF_PRISTINE irrespective of its size.
-	 */
 	frag = frag_last(list);
+
+	/* Sanity check for truncation to longer than we started with... */
+	if (!frag)
+		return 0;
+	if (frag->ofs + frag->size < size)
+		return frag->ofs + frag->size;
+
+	/* If the last fragment starts at the RAM page boundary, it is
+	 * REF_PRISTINE irrespective of its size. */
 	if (frag->node && (frag->ofs & (PAGE_CACHE_SIZE - 1)) == 0) {
 		dbg_fragtree2("marking the last fragment 0x%08x-0x%08x REF_PRISTINE.\n",
 			frag->ofs, frag->ofs + frag->size);
 		frag->node->raw->flash_offset = ref_offset(frag->node->raw) | REF_PRISTINE;
 	}
+	return size;
 }
 
 static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c,
@@ -397,466 +401,6 @@
 	return 0;
 }
 
-/*
- * Check the data CRC of the node.
- *
- * Returns: 0 if the data CRC is correct;
- * 	    1 - if incorrect;
- *	    error code if an error occured.
- */
-static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *tn)
-{
-	struct jffs2_raw_node_ref *ref = tn->fn->raw;
-	int err = 0, pointed = 0;
-	struct jffs2_eraseblock *jeb;
-	unsigned char *buffer;
-	uint32_t crc, ofs, len;
-	size_t retlen;
-
-	BUG_ON(tn->csize == 0);
-
-	if (!jffs2_is_writebuffered(c))
-		goto adj_acc;
-
-	/* Calculate how many bytes were already checked */
-	ofs = ref_offset(ref) + sizeof(struct jffs2_raw_inode);
-	len = ofs % c->wbuf_pagesize;
-	if (likely(len))
-		len = c->wbuf_pagesize - len;
-
-	if (len >= tn->csize) {
-		dbg_readinode("no need to check node at %#08x, data length %u, data starts at %#08x - it has already been checked.\n",
-			ref_offset(ref), tn->csize, ofs);
-		goto adj_acc;
-	}
-
-	ofs += len;
-	len = tn->csize - len;
-
-	dbg_readinode("check node at %#08x, data length %u, partial CRC %#08x, correct CRC %#08x, data starts at %#08x, start checking from %#08x - %u bytes.\n",
-		ref_offset(ref), tn->csize, tn->partial_crc, tn->data_crc, ofs - len, ofs, len);
-
-#ifndef __ECOS
-	/* TODO: instead, incapsulate point() stuff to jffs2_flash_read(),
-	 * adding and jffs2_flash_read_end() interface. */
-	if (c->mtd->point) {
-		err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer);
-		if (!err && retlen < tn->csize) {
-			JFFS2_WARNING("MTD point returned len too short: %zu instead of %u.\n", retlen, tn->csize);
-			c->mtd->unpoint(c->mtd, buffer, ofs, len);
-		} else if (err)
-			JFFS2_WARNING("MTD point failed: error code %d.\n", err);
-		else
-			pointed = 1; /* succefully pointed to device */
-	}
-#endif
-
-	if (!pointed) {
-		buffer = kmalloc(len, GFP_KERNEL);
-		if (unlikely(!buffer))
-			return -ENOMEM;
-
-		/* TODO: this is very frequent pattern, make it a separate
-		 * routine */
-		err = jffs2_flash_read(c, ofs, len, &retlen, buffer);
-		if (err) {
-			JFFS2_ERROR("can not read %d bytes from 0x%08x, error code: %d.\n", len, ofs, err);
-			goto free_out;
-		}
-
-		if (retlen != len) {
-			JFFS2_ERROR("short read at %#08x: %zd instead of %d.\n", ofs, retlen, len);
-			err = -EIO;
-			goto free_out;
-		}
-	}
-
-	/* Continue calculating CRC */
-	crc = crc32(tn->partial_crc, buffer, len);
-	if(!pointed)
-		kfree(buffer);
-#ifndef __ECOS
-	else
-		c->mtd->unpoint(c->mtd, buffer, ofs, len);
-#endif
-
-	if (crc != tn->data_crc) {
-		JFFS2_NOTICE("wrong data CRC in data node at 0x%08x: read %#08x, calculated %#08x.\n",
-			ofs, tn->data_crc, crc);
-		return 1;
-	}
-
-adj_acc:
-	jeb = &c->blocks[ref->flash_offset / c->sector_size];
-	len = ref_totlen(c, jeb, ref);
-
-	/*
-	 * Mark the node as having been checked and fix the
-	 * accounting accordingly.
-	 */
-	spin_lock(&c->erase_completion_lock);
-	jeb->used_size += len;
-	jeb->unchecked_size -= len;
-	c->used_size += len;
-	c->unchecked_size -= len;
-	spin_unlock(&c->erase_completion_lock);
-
-	return 0;
-
-free_out:
-	if(!pointed)
-		kfree(buffer);
-#ifndef __ECOS
-	else
-		c->mtd->unpoint(c->mtd, buffer, ofs, len);
-#endif
-	return err;
-}
-
-/*
- * Helper function for jffs2_add_older_frag_to_fragtree().
- *
- * Checks the node if we are in the checking stage.
- */
-static int check_node(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info *tn)
-{
-	int ret;
-
-	BUG_ON(ref_obsolete(tn->fn->raw));
-
-	/* We only check the data CRC of unchecked nodes */
-	if (ref_flags(tn->fn->raw) != REF_UNCHECKED)
-		return 0;
-
-	dbg_fragtree2("check node %#04x-%#04x, phys offs %#08x.\n",
-		tn->fn->ofs, tn->fn->ofs + tn->fn->size, ref_offset(tn->fn->raw));
-
-	ret = check_node_data(c, tn);
-	if (unlikely(ret < 0)) {
-		JFFS2_ERROR("check_node_data() returned error: %d.\n",
-			ret);
-	} else if (unlikely(ret > 0)) {
-		dbg_fragtree2("CRC error, mark it obsolete.\n");
-		jffs2_mark_node_obsolete(c, tn->fn->raw);
-	}
-
-	return ret;
-}
-
-/*
- * Helper function for jffs2_add_older_frag_to_fragtree().
- *
- * Called when the new fragment that is being inserted
- * splits a hole fragment.
- */
-static int split_hole(struct jffs2_sb_info *c, struct rb_root *root,
-		      struct jffs2_node_frag *newfrag, struct jffs2_node_frag *hole)
-{
-	dbg_fragtree2("fragment %#04x-%#04x splits the hole %#04x-%#04x\n",
-		newfrag->ofs, newfrag->ofs + newfrag->size, hole->ofs, hole->ofs + hole->size);
-
-	if (hole->ofs == newfrag->ofs) {
-		/*
-		 * Well, the new fragment actually starts at the same offset as
-		 * the hole.
-		 */
-		if (hole->ofs + hole->size > newfrag->ofs + newfrag->size) {
-			/*
-			 * We replace the overlapped left part of the hole by
-			 * the new node.
-			 */
-
-			dbg_fragtree2("insert fragment %#04x-%#04x and cut the left part of the hole\n",
-				newfrag->ofs, newfrag->ofs + newfrag->size);
-			rb_replace_node(&hole->rb, &newfrag->rb, root);
-
-			hole->ofs += newfrag->size;
-			hole->size -= newfrag->size;
-
-			/*
-			 * We know that 'hole' should be the right hand
-			 * fragment.
-			 */
-			jffs2_fragtree_insert(hole, newfrag);
-			rb_insert_color(&hole->rb, root);
-		} else {
-			/*
-			 * Ah, the new fragment is of the same size as the hole.
-			 * Relace the hole by it.
-			 */
-			dbg_fragtree2("insert fragment %#04x-%#04x and overwrite hole\n",
-				newfrag->ofs, newfrag->ofs + newfrag->size);
-			rb_replace_node(&hole->rb, &newfrag->rb, root);
-			jffs2_free_node_frag(hole);
-		}
-	} else {
-		/* The new fragment lefts some hole space at the left */
-
-		struct jffs2_node_frag * newfrag2 = NULL;
-
-		if (hole->ofs + hole->size > newfrag->ofs + newfrag->size) {
-			/* The new frag also lefts some space at the right */
-			newfrag2 = new_fragment(NULL, newfrag->ofs +
-				newfrag->size, hole->ofs + hole->size
-				- newfrag->ofs - newfrag->size);
-			if (unlikely(!newfrag2)) {
-				jffs2_free_node_frag(newfrag);
-				return -ENOMEM;
-			}
-		}
-
-		hole->size = newfrag->ofs - hole->ofs;
-		dbg_fragtree2("left the hole %#04x-%#04x at the left and inserd fragment %#04x-%#04x\n",
-			hole->ofs, hole->ofs + hole->size, newfrag->ofs, newfrag->ofs + newfrag->size);
-
-		jffs2_fragtree_insert(newfrag, hole);
-		rb_insert_color(&newfrag->rb, root);
-
-		if (newfrag2) {
-			dbg_fragtree2("left the hole %#04x-%#04x at the right\n",
-				newfrag2->ofs, newfrag2->ofs + newfrag2->size);
-			jffs2_fragtree_insert(newfrag2, newfrag);
-			rb_insert_color(&newfrag2->rb, root);
-		}
-	}
-
-	return 0;
-}
-
-/*
- * This function is used when we build inode. It expects the nodes are passed
- * in the decreasing version order. The whole point of this is to improve the
- * inodes checking on NAND: we check the nodes' data CRC only when they are not
- * obsoleted. Previously, add_frag_to_fragtree() function was used and
- * nodes were passed to it in the increasing version ordes and CRCs of all
- * nodes were checked.
- *
- * Note: tn->fn->size shouldn't be zero.
- *
- * Returns 0 if the node was inserted
- *         1 if it wasn't inserted (since it is obsolete)
- *         < 0 an if error occured
- */
-int jffs2_add_older_frag_to_fragtree(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
-				     struct jffs2_tmp_dnode_info *tn)
-{
-	struct jffs2_node_frag *this, *newfrag;
-	uint32_t lastend;
-	struct jffs2_full_dnode *fn = tn->fn;
-	struct rb_root *root = &f->fragtree;
-	uint32_t fn_size = fn->size, fn_ofs = fn->ofs;
-	int err, checked = 0;
-	int ref_flag;
-
-	dbg_fragtree("insert fragment %#04x-%#04x, ver %u\n", fn_ofs, fn_ofs + fn_size, tn->version);
-
-	/* Skip all the nodes which are completed before this one starts */
-	this = jffs2_lookup_node_frag(root, fn_ofs);
-	if (this)
-		dbg_fragtree2("'this' found %#04x-%#04x (%s)\n", this->ofs, this->ofs + this->size, this->node ? "data" : "hole");
-
-	if (this)
-		lastend = this->ofs + this->size;
-	else
-		lastend = 0;
-
-	/* Detect the preliminary type of node */
-	if (fn->size >= PAGE_CACHE_SIZE)
-		ref_flag = REF_PRISTINE;
-	else
-		ref_flag = REF_NORMAL;
-
-	/* See if we ran off the end of the root */
-	if (lastend <= fn_ofs) {
-		/* We did */
-
-		/*
-		 * We are going to insert the new node into the
-		 * fragment tree, so check it.
-		 */
-		err = check_node(c, f, tn);
-		if (err != 0)
-			return err;
-
-		fn->frags = 1;
-
-		newfrag = new_fragment(fn, fn_ofs, fn_size);
-		if (unlikely(!newfrag))
-			return -ENOMEM;
-
-		err = no_overlapping_node(c, root, newfrag, this, lastend);
-		if (unlikely(err != 0)) {
-			jffs2_free_node_frag(newfrag);
-			return err;
-		}
-
-		goto out_ok;
-	}
-
-	fn->frags = 0;
-
-	while (1) {
-		/*
-		 * Here we have:
-		 * fn_ofs < this->ofs + this->size && fn_ofs >= this->ofs.
-		 *
-		 * Remember, 'this' has higher version, any non-hole node
-		 * which is already in the fragtree is newer then the newly
-		 * inserted.
-		 */
-		if (!this->node) {
-			/*
-			 * 'this' is the hole fragment, so at least the
-			 * beginning of the new fragment is valid.
-			 */
-
-			/*
-			 * We are going to insert the new node into the
-			 * fragment tree, so check it.
-			 */
-			if (!checked) {
-				err = check_node(c, f, tn);
-				if (unlikely(err != 0))
-					return err;
-				checked = 1;
-			}
-
-			if (this->ofs + this->size >= fn_ofs + fn_size) {
-				/* We split the hole on two parts */
-
-				fn->frags += 1;
-				newfrag = new_fragment(fn, fn_ofs, fn_size);
-				if (unlikely(!newfrag))
-					return -ENOMEM;
-
-				err = split_hole(c, root, newfrag, this);
-				if (unlikely(err))
-					return err;
-				goto out_ok;
-			}
-
-			/*
-			 * The beginning of the new fragment is valid since it
-			 * overlaps the hole node.
-			 */
-
-			ref_flag = REF_NORMAL;
-
-			fn->frags += 1;
-			newfrag = new_fragment(fn, fn_ofs,
-					this->ofs + this->size - fn_ofs);
-			if (unlikely(!newfrag))
-				return -ENOMEM;
-
-			if (fn_ofs == this->ofs) {
-				/*
-				 * The new node starts at the same offset as
-				 * the hole and supersieds the hole.
-				 */
-				dbg_fragtree2("add the new fragment instead of hole %#04x-%#04x, refcnt %d\n",
-					fn_ofs, fn_ofs + this->ofs + this->size - fn_ofs, fn->frags);
-
-				rb_replace_node(&this->rb, &newfrag->rb, root);
-				jffs2_free_node_frag(this);
-			} else {
-				/*
-				 * The hole becomes shorter as its right part
-				 * is supersieded by the new fragment.
-				 */
-				dbg_fragtree2("reduce size of hole %#04x-%#04x to %#04x-%#04x\n",
-					this->ofs, this->ofs + this->size, this->ofs, this->ofs + this->size - newfrag->size);
-
-				dbg_fragtree2("add new fragment %#04x-%#04x, refcnt %d\n", fn_ofs,
-					fn_ofs + this->ofs + this->size - fn_ofs, fn->frags);
-
-				this->size -= newfrag->size;
-				jffs2_fragtree_insert(newfrag, this);
-				rb_insert_color(&newfrag->rb, root);
-			}
-
-			fn_ofs += newfrag->size;
-			fn_size -= newfrag->size;
-			this = rb_entry(rb_next(&newfrag->rb),
-					struct jffs2_node_frag, rb);
-
-			dbg_fragtree2("switch to the next 'this' fragment: %#04x-%#04x %s\n",
-				this->ofs, this->ofs + this->size, this->node ? "(data)" : "(hole)");
-		}
-
-		/*
-		 * 'This' node is not the hole so it obsoletes the new fragment
-		 * either fully or partially.
-		 */
-		if (this->ofs + this->size >= fn_ofs + fn_size) {
-			/* The new node is obsolete, drop it */
-			if (fn->frags == 0) {
-				dbg_fragtree2("%#04x-%#04x is obsolete, mark it obsolete\n", fn_ofs, fn_ofs + fn_size);
-				ref_flag = REF_OBSOLETE;
-			}
-			goto out_ok;
-		} else {
-			struct jffs2_node_frag *new_this;
-
-			/* 'This' node obsoletes the beginning of the new node */
-			dbg_fragtree2("the beginning %#04x-%#04x is obsolete\n", fn_ofs, this->ofs + this->size);
-
-			ref_flag = REF_NORMAL;
-
-			fn_size -= this->ofs + this->size - fn_ofs;
-			fn_ofs = this->ofs + this->size;
-			dbg_fragtree2("now considering %#04x-%#04x\n", fn_ofs, fn_ofs + fn_size);
-
-			new_this = rb_entry(rb_next(&this->rb), struct jffs2_node_frag, rb);
-			if (!new_this) {
-				/*
-				 * There is no next fragment. Add the rest of
-				 * the new node as the right-hand child.
-				 */
-				if (!checked) {
-					err = check_node(c, f, tn);
-					if (unlikely(err != 0))
-						return err;
-					checked = 1;
-				}
-
-				fn->frags += 1;
-				newfrag = new_fragment(fn, fn_ofs, fn_size);
-				if (unlikely(!newfrag))
-					return -ENOMEM;
-
-				dbg_fragtree2("there are no more fragments, insert %#04x-%#04x\n",
-					newfrag->ofs, newfrag->ofs + newfrag->size);
-				rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right);
-				rb_insert_color(&newfrag->rb, root);
-				goto out_ok;
-			} else {
-				this = new_this;
-				dbg_fragtree2("switch to the next 'this' fragment: %#04x-%#04x %s\n",
-					this->ofs, this->ofs + this->size, this->node ? "(data)" : "(hole)");
-			}
-		}
-	}
-
-out_ok:
-	BUG_ON(fn->size < PAGE_CACHE_SIZE && ref_flag == REF_PRISTINE);
-
-	if (ref_flag == REF_OBSOLETE) {
-		dbg_fragtree2("the node is obsolete now\n");
-		/* jffs2_mark_node_obsolete() will adjust space accounting */
-		jffs2_mark_node_obsolete(c, fn->raw);
-		return 1;
-	}
-
-	dbg_fragtree2("the node is \"%s\" now\n", ref_flag == REF_NORMAL ? "REF_NORMAL" : "REF_PRISTINE");
-
-	/* Space accounting was adjusted at check_node_data() */
-	spin_lock(&c->erase_completion_lock);
-	fn->raw->flash_offset = ref_offset(fn->raw) | ref_flag;
-	spin_unlock(&c->erase_completion_lock);
-
-	return 0;
-}
-
 void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state)
 {
 	spin_lock(&c->inocache_lock);
diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h
index 4178b4b5..25126a0 100644
--- a/fs/jffs2/nodelist.h
+++ b/fs/jffs2/nodelist.h
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: nodelist.h,v 1.140 2005/09/07 08:34:54 havasi Exp $
- *
  */
 
 #ifndef __JFFS2_NODELIST_H__
@@ -40,6 +38,9 @@
 #define cpu_to_je32(x) ((jint32_t){x})
 #define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)})
 
+#define constant_cpu_to_je16(x) ((jint16_t){x})
+#define constant_cpu_to_je32(x) ((jint32_t){x})
+
 #define je16_to_cpu(x) ((x).v16)
 #define je32_to_cpu(x) ((x).v32)
 #define jemode_to_cpu(x) (jffs2_to_os_mode((x).m))
@@ -48,6 +49,9 @@
 #define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)})
 #define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))})
 
+#define constant_cpu_to_je16(x) ((jint16_t){__constant_cpu_to_be16(x)})
+#define constant_cpu_to_je32(x) ((jint32_t){__constant_cpu_to_be32(x)})
+
 #define je16_to_cpu(x) (be16_to_cpu(x.v16))
 #define je32_to_cpu(x) (be32_to_cpu(x.v32))
 #define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m)))
@@ -56,6 +60,9 @@
 #define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)})
 #define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))})
 
+#define constant_cpu_to_je16(x) ((jint16_t){__constant_cpu_to_le16(x)})
+#define constant_cpu_to_je32(x) ((jint32_t){__constant_cpu_to_le32(x)})
+
 #define je16_to_cpu(x) (le16_to_cpu(x.v16))
 #define je32_to_cpu(x) (le32_to_cpu(x.v32))
 #define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m)))
@@ -216,7 +223,20 @@
 	uint32_t version;
 	uint32_t data_crc;
 	uint32_t partial_crc;
-	uint32_t csize;
+	uint16_t csize;
+	uint16_t overlapped;
+};
+
+/* Temporary data structure used during readinode. */
+struct jffs2_readinode_info
+{
+	struct rb_root tn_root;
+	struct jffs2_tmp_dnode_info *mdata_tn;
+	uint32_t highest_version;
+	uint32_t latest_mctime;
+	uint32_t mctime_ver;
+	struct jffs2_full_dirent *fds;
+	struct jffs2_raw_node_ref *latest_ref;
 };
 
 struct jffs2_full_dirent
@@ -319,6 +339,15 @@
 #define frag_right(frag) rb_entry((frag)->rb.rb_right, struct jffs2_node_frag, rb)
 #define frag_erase(frag, list) rb_erase(&frag->rb, list);
 
+#define tn_next(tn) rb_entry(rb_next(&(tn)->rb), struct jffs2_tmp_dnode_info, rb)
+#define tn_prev(tn) rb_entry(rb_prev(&(tn)->rb), struct jffs2_tmp_dnode_info, rb)
+#define tn_parent(tn) rb_entry(rb_parent(&(tn)->rb), struct jffs2_tmp_dnode_info, rb)
+#define tn_left(tn) rb_entry((tn)->rb.rb_left, struct jffs2_tmp_dnode_info, rb)
+#define tn_right(tn) rb_entry((tn)->rb.rb_right, struct jffs2_tmp_dnode_info, rb)
+#define tn_erase(tn, list) rb_erase(&tn->rb, list);
+#define tn_last(list) rb_entry(rb_last(list), struct jffs2_tmp_dnode_info, rb)
+#define tn_first(list) rb_entry(rb_first(list), struct jffs2_tmp_dnode_info, rb)
+
 /* nodelist.c */
 void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list);
 void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state);
@@ -333,8 +362,7 @@
 struct rb_node *rb_prev(struct rb_node *);
 void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root);
 int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn);
-void jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size);
-int jffs2_add_older_frag_to_fragtree(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info *tn);
+uint32_t jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size);
 struct jffs2_raw_node_ref *jffs2_link_node_ref(struct jffs2_sb_info *c,
 					       struct jffs2_eraseblock *jeb,
 					       uint32_t ofs, uint32_t len,
diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c
index d883769..dbc908a 100644
--- a/fs/jffs2/nodemgmt.c
+++ b/fs/jffs2/nodemgmt.c
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: nodemgmt.c,v 1.127 2005/09/20 15:49:12 dedekind Exp $
- *
  */
 
 #include <linux/kernel.h>
@@ -172,6 +170,11 @@
 static void jffs2_close_nextblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
 
+	if (c->nextblock == NULL) {
+		D1(printk(KERN_DEBUG "jffs2_close_nextblock: Erase block at 0x%08x has already been placed in a list\n",
+		  jeb->offset));
+		return;
+	}
 	/* Check, if we have a dirty block now, or if it was dirty already */
 	if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) {
 		c->dirty_size += jeb->wasted_size;
diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h
index e07a0ed..80daea9 100644
--- a/fs/jffs2/os-linux.h
+++ b/fs/jffs2/os-linux.h
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2002-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: os-linux.h,v 1.64 2005/09/30 13:59:13 dedekind Exp $
- *
  */
 
 #ifndef __JFFS2_OS_LINUX_H__
@@ -98,6 +96,9 @@
 #define jffs2_nor_wbuf_flash(c) (0)
 #define jffs2_nor_wbuf_flash_setup(c) (0)
 #define jffs2_nor_wbuf_flash_cleanup(c) do {} while (0)
+#define jffs2_ubivol(c) (0)
+#define jffs2_ubivol_setup(c) (0)
+#define jffs2_ubivol_cleanup(c) do {} while (0)
 
 #else /* NAND and/or ECC'd NOR support present */
 
@@ -133,6 +134,9 @@
 #define jffs2_dataflash(c) (c->mtd->type == MTD_DATAFLASH)
 int jffs2_dataflash_setup(struct jffs2_sb_info *c);
 void jffs2_dataflash_cleanup(struct jffs2_sb_info *c);
+#define jffs2_ubivol(c) (c->mtd->type == MTD_UBIVOLUME)
+int jffs2_ubivol_setup(struct jffs2_sb_info *c);
+void jffs2_ubivol_cleanup(struct jffs2_sb_info *c);
 
 #define jffs2_nor_wbuf_flash(c) (c->mtd->type == MTD_NORFLASH && ! (c->mtd->flags & MTD_BIT_WRITEABLE))
 int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c);
diff --git a/fs/jffs2/pushpull.h b/fs/jffs2/pushpull.h
deleted file mode 100644
index c0c2a91..0000000
--- a/fs/jffs2/pushpull.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * JFFS2 -- Journalling Flash File System, Version 2.
- *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
- *
- * Created by David Woodhouse <dwmw2@infradead.org>
- *
- * For licensing information, see the file 'LICENCE' in this directory.
- *
- * $Id: pushpull.h,v 1.10 2004/11/16 20:36:11 dwmw2 Exp $
- *
- */
-
-#ifndef __PUSHPULL_H__
-#define __PUSHPULL_H__
-
-#include <linux/errno.h>
-
-struct pushpull {
-	unsigned char *buf;
-	unsigned int buflen;
-	unsigned int ofs;
-	unsigned int reserve;
-};
-
-
-static inline void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen, unsigned ofs, unsigned reserve)
-{
-	pp->buf = buf;
-	pp->buflen = buflen;
-	pp->ofs = ofs;
-	pp->reserve = reserve;
-}
-
-static inline int pushbit(struct pushpull *pp, int bit, int use_reserved)
-{
-	if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve)) {
-		return -ENOSPC;
-	}
-
-	if (bit) {
-		pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs &7)));
-	}
-	else {
-		pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs &7)));
-	}
-	pp->ofs++;
-
-	return 0;
-}
-
-static inline int pushedbits(struct pushpull *pp)
-{
-	return pp->ofs;
-}
-
-static inline int pullbit(struct pushpull *pp)
-{
-	int bit;
-
-	bit = (pp->buf[pp->ofs >> 3] >> (7-(pp->ofs & 7))) & 1;
-
-	pp->ofs++;
-	return bit;
-}
-
-static inline int pulledbits(struct pushpull *pp)
-{
-	return pp->ofs;
-}
-
-#endif /* __PUSHPULL_H__ */
diff --git a/fs/jffs2/read.c b/fs/jffs2/read.c
index f3b86da..cfe05c1 100644
--- a/fs/jffs2/read.c
+++ b/fs/jffs2/read.c
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: read.c,v 1.42 2005/11/07 11:14:41 gleixner Exp $
- *
  */
 
 #include <linux/kernel.h>
diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c
index 717a48c..6aff389 100644
--- a/fs/jffs2/readinode.c
+++ b/fs/jffs2/readinode.c
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: readinode.c,v 1.143 2005/11/07 11:14:41 gleixner Exp $
- *
  */
 
 #include <linux/kernel.h>
@@ -22,30 +20,510 @@
 #include "nodelist.h"
 
 /*
- * Put a new tmp_dnode_info into the temporaty RB-tree, keeping the list in
- * order of increasing version.
+ * Check the data CRC of the node.
+ *
+ * Returns: 0 if the data CRC is correct;
+ * 	    1 - if incorrect;
+ *	    error code if an error occured.
  */
-static void jffs2_add_tn_to_tree(struct jffs2_tmp_dnode_info *tn, struct rb_root *list)
+static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *tn)
 {
-	struct rb_node **p = &list->rb_node;
-	struct rb_node * parent = NULL;
-	struct jffs2_tmp_dnode_info *this;
+	struct jffs2_raw_node_ref *ref = tn->fn->raw;
+	int err = 0, pointed = 0;
+	struct jffs2_eraseblock *jeb;
+	unsigned char *buffer;
+	uint32_t crc, ofs, len;
+	size_t retlen;
 
-	while (*p) {
-		parent = *p;
-		this = rb_entry(parent, struct jffs2_tmp_dnode_info, rb);
+	BUG_ON(tn->csize == 0);
 
-		/* There may actually be a collision here, but it doesn't
-		   actually matter. As long as the two nodes with the same
-		   version are together, it's all fine. */
-		if (tn->version > this->version)
-			p = &(*p)->rb_left;
-		else
-			p = &(*p)->rb_right;
+	if (!jffs2_is_writebuffered(c))
+		goto adj_acc;
+
+	/* Calculate how many bytes were already checked */
+	ofs = ref_offset(ref) + sizeof(struct jffs2_raw_inode);
+	len = ofs % c->wbuf_pagesize;
+	if (likely(len))
+		len = c->wbuf_pagesize - len;
+
+	if (len >= tn->csize) {
+		dbg_readinode("no need to check node at %#08x, data length %u, data starts at %#08x - it has already been checked.\n",
+			ref_offset(ref), tn->csize, ofs);
+		goto adj_acc;
 	}
 
-	rb_link_node(&tn->rb, parent, p);
-	rb_insert_color(&tn->rb, list);
+	ofs += len;
+	len = tn->csize - len;
+
+	dbg_readinode("check node at %#08x, data length %u, partial CRC %#08x, correct CRC %#08x, data starts at %#08x, start checking from %#08x - %u bytes.\n",
+		ref_offset(ref), tn->csize, tn->partial_crc, tn->data_crc, ofs - len, ofs, len);
+
+#ifndef __ECOS
+	/* TODO: instead, incapsulate point() stuff to jffs2_flash_read(),
+	 * adding and jffs2_flash_read_end() interface. */
+	if (c->mtd->point) {
+		err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer);
+		if (!err && retlen < tn->csize) {
+			JFFS2_WARNING("MTD point returned len too short: %zu instead of %u.\n", retlen, tn->csize);
+			c->mtd->unpoint(c->mtd, buffer, ofs, len);
+		} else if (err)
+			JFFS2_WARNING("MTD point failed: error code %d.\n", err);
+		else
+			pointed = 1; /* succefully pointed to device */
+	}
+#endif
+
+	if (!pointed) {
+		buffer = kmalloc(len, GFP_KERNEL);
+		if (unlikely(!buffer))
+			return -ENOMEM;
+
+		/* TODO: this is very frequent pattern, make it a separate
+		 * routine */
+		err = jffs2_flash_read(c, ofs, len, &retlen, buffer);
+		if (err) {
+			JFFS2_ERROR("can not read %d bytes from 0x%08x, error code: %d.\n", len, ofs, err);
+			goto free_out;
+		}
+
+		if (retlen != len) {
+			JFFS2_ERROR("short read at %#08x: %zd instead of %d.\n", ofs, retlen, len);
+			err = -EIO;
+			goto free_out;
+		}
+	}
+
+	/* Continue calculating CRC */
+	crc = crc32(tn->partial_crc, buffer, len);
+	if(!pointed)
+		kfree(buffer);
+#ifndef __ECOS
+	else
+		c->mtd->unpoint(c->mtd, buffer, ofs, len);
+#endif
+
+	if (crc != tn->data_crc) {
+		JFFS2_NOTICE("wrong data CRC in data node at 0x%08x: read %#08x, calculated %#08x.\n",
+			ofs, tn->data_crc, crc);
+		return 1;
+	}
+
+adj_acc:
+	jeb = &c->blocks[ref->flash_offset / c->sector_size];
+	len = ref_totlen(c, jeb, ref);
+	/* If it should be REF_NORMAL, it'll get marked as such when
+	   we build the fragtree, shortly. No need to worry about GC
+	   moving it while it's marked REF_PRISTINE -- GC won't happen
+	   till we've finished checking every inode anyway. */
+	ref->flash_offset |= REF_PRISTINE;
+	/*
+	 * Mark the node as having been checked and fix the
+	 * accounting accordingly.
+	 */
+	spin_lock(&c->erase_completion_lock);
+	jeb->used_size += len;
+	jeb->unchecked_size -= len;
+	c->used_size += len;
+	c->unchecked_size -= len;
+	jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
+	spin_unlock(&c->erase_completion_lock);
+
+	return 0;
+
+free_out:
+	if(!pointed)
+		kfree(buffer);
+#ifndef __ECOS
+	else
+		c->mtd->unpoint(c->mtd, buffer, ofs, len);
+#endif
+	return err;
+}
+
+/*
+ * Helper function for jffs2_add_older_frag_to_fragtree().
+ *
+ * Checks the node if we are in the checking stage.
+ */
+static int check_tn_node(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *tn)
+{
+	int ret;
+
+	BUG_ON(ref_obsolete(tn->fn->raw));
+
+	/* We only check the data CRC of unchecked nodes */
+	if (ref_flags(tn->fn->raw) != REF_UNCHECKED)
+		return 0;
+
+	dbg_readinode("check node %#04x-%#04x, phys offs %#08x\n",
+		      tn->fn->ofs, tn->fn->ofs + tn->fn->size, ref_offset(tn->fn->raw));
+
+	ret = check_node_data(c, tn);
+	if (unlikely(ret < 0)) {
+		JFFS2_ERROR("check_node_data() returned error: %d.\n",
+			ret);
+	} else if (unlikely(ret > 0)) {
+		dbg_readinode("CRC error, mark it obsolete.\n");
+		jffs2_mark_node_obsolete(c, tn->fn->raw);
+	}
+
+	return ret;
+}
+
+static struct jffs2_tmp_dnode_info *jffs2_lookup_tn(struct rb_root *tn_root, uint32_t offset)
+{
+	struct rb_node *next;
+	struct jffs2_tmp_dnode_info *tn = NULL;
+
+	dbg_readinode("root %p, offset %d\n", tn_root, offset);
+
+	next = tn_root->rb_node;
+
+	while (next) {
+		tn = rb_entry(next, struct jffs2_tmp_dnode_info, rb);
+
+		if (tn->fn->ofs < offset)
+			next = tn->rb.rb_right;
+		else if (tn->fn->ofs >= offset)
+			next = tn->rb.rb_left;
+		else
+			break;
+	}
+
+	return tn;
+}
+
+
+static void jffs2_kill_tn(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *tn)
+{
+	jffs2_mark_node_obsolete(c, tn->fn->raw);
+	jffs2_free_full_dnode(tn->fn);
+	jffs2_free_tmp_dnode_info(tn);
+}
+/*
+ * This function is used when we read an inode. Data nodes arrive in
+ * arbitrary order -- they may be older or newer than the nodes which
+ * are already in the tree. Where overlaps occur, the older node can
+ * be discarded as long as the newer passes the CRC check. We don't
+ * bother to keep track of holes in this rbtree, and neither do we deal
+ * with frags -- we can have multiple entries starting at the same
+ * offset, and the one with the smallest length will come first in the
+ * ordering.
+ *
+ * Returns 0 if the node was inserted
+ *         1 if the node is obsolete (because we can't mark it so yet)
+ *         < 0 an if error occurred
+ */
+static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
+				struct jffs2_readinode_info *rii,
+				struct jffs2_tmp_dnode_info *tn)
+{
+	uint32_t fn_end = tn->fn->ofs + tn->fn->size;
+	struct jffs2_tmp_dnode_info *insert_point = NULL, *this;
+
+	dbg_readinode("insert fragment %#04x-%#04x, ver %u\n", tn->fn->ofs, fn_end, tn->version);
+
+	/* If a node has zero dsize, we only have to keep if it if it might be the
+	   node with highest version -- i.e. the one which will end up as f->metadata.
+	   Note that such nodes won't be REF_UNCHECKED since there are no data to
+	   check anyway. */
+	if (!tn->fn->size) {
+		if (rii->mdata_tn) {
+			/* We had a candidate mdata node already */
+			dbg_readinode("kill old mdata with ver %d\n", rii->mdata_tn->version);
+			jffs2_kill_tn(c, rii->mdata_tn);
+		}
+		rii->mdata_tn = tn;
+		dbg_readinode("keep new mdata with ver %d\n", tn->version);
+		return 0;
+	}
+
+	/* Find the earliest node which _may_ be relevant to this one */
+	this = jffs2_lookup_tn(&rii->tn_root, tn->fn->ofs);
+	if (!this) {
+		/* First addition to empty tree. $DEITY how I love the easy cases */
+		rb_link_node(&tn->rb, NULL, &rii->tn_root.rb_node);
+		rb_insert_color(&tn->rb, &rii->tn_root);
+		dbg_readinode("keep new frag\n");
+		return 0;
+	}
+
+	/* If we add a new node it'll be somewhere under here. */
+	insert_point = this;
+
+	/* If the node is coincident with another at a lower address,
+	   back up until the other node is found. It may be relevant */
+	while (tn->overlapped)
+		tn = tn_prev(tn);
+
+	dbg_readinode("'this' found %#04x-%#04x (%s)\n", this->fn->ofs, this->fn->ofs + this->fn->size, this->fn ? "data" : "hole");
+
+	while (this) {
+		if (this->fn->ofs > fn_end)
+			break;
+		dbg_readinode("Ponder this ver %d, 0x%x-0x%x\n",
+			      this->version, this->fn->ofs, this->fn->size);
+
+		if (this->version == tn->version) {
+			/* Version number collision means REF_PRISTINE GC. Accept either of them
+			   as long as the CRC is correct. Check the one we have already...  */
+			if (!check_tn_node(c, this)) {
+				/* The one we already had was OK. Keep it and throw away the new one */
+				dbg_readinode("Like old node. Throw away new\n");
+				jffs2_kill_tn(c, tn);
+				return 0;
+			} else {
+				/* Who cares if the new one is good; keep it for now anyway. */
+				rb_replace_node(&this->rb, &tn->rb, &rii->tn_root);
+				/* Same overlapping from in front and behind */
+				tn->overlapped = this->overlapped;
+				jffs2_kill_tn(c, this);
+				dbg_readinode("Like new node. Throw away old\n");
+				return 0;
+			}
+		}
+		if (this->version < tn->version &&
+		    this->fn->ofs >= tn->fn->ofs &&
+		    this->fn->ofs + this->fn->size <= fn_end) {
+			/* New node entirely overlaps 'this' */
+			if (check_tn_node(c, tn)) {
+				dbg_readinode("new node bad CRC\n");
+				jffs2_kill_tn(c, tn);
+				return 0;
+			}
+			/* ... and is good. Kill 'this'... */
+			rb_replace_node(&this->rb, &tn->rb, &rii->tn_root);
+			tn->overlapped = this->overlapped;
+			jffs2_kill_tn(c, this);
+			/* ... and any subsequent nodes which are also overlapped */
+			this = tn_next(tn);
+			while (this && this->fn->ofs + this->fn->size < fn_end) {
+				struct jffs2_tmp_dnode_info *next = tn_next(this);
+				if (this->version < tn->version) {
+					tn_erase(this, &rii->tn_root);
+					dbg_readinode("Kill overlapped ver %d, 0x%x-0x%x\n",
+						      this->version, this->fn->ofs,
+						      this->fn->ofs+this->fn->size);
+					jffs2_kill_tn(c, this);
+				}
+				this = next;
+			}
+			dbg_readinode("Done inserting new\n");
+			return 0;
+		}
+		if (this->version > tn->version &&
+		    this->fn->ofs <= tn->fn->ofs &&
+		    this->fn->ofs+this->fn->size >= fn_end) {
+			/* New node entirely overlapped by 'this' */
+			if (!check_tn_node(c, this)) {
+				dbg_readinode("Good CRC on old node. Kill new\n");
+				jffs2_kill_tn(c, tn);
+				return 0;
+			}
+			/* ... but 'this' was bad. Replace it... */
+			rb_replace_node(&this->rb, &tn->rb, &rii->tn_root);
+			dbg_readinode("Bad CRC on old overlapping node. Kill it\n");
+			jffs2_kill_tn(c, this);
+			return 0;
+		}
+		/* We want to be inserted under the last node which is
+		   either at a lower offset _or_ has a smaller range */
+		if (this->fn->ofs < tn->fn->ofs ||
+		    (this->fn->ofs == tn->fn->ofs &&
+		     this->fn->size <= tn->fn->size))
+			insert_point = this;
+
+		this = tn_next(this);
+	}
+	dbg_readinode("insert_point %p, ver %d, 0x%x-0x%x, ov %d\n",
+		      insert_point, insert_point->version, insert_point->fn->ofs,
+		      insert_point->fn->ofs+insert_point->fn->size,
+		      insert_point->overlapped);
+	/* We neither completely obsoleted nor were completely
+	   obsoleted by an earlier node. Insert under insert_point */
+	{
+		struct rb_node *parent = &insert_point->rb;
+		struct rb_node **link = &parent;
+
+		while (*link) {
+			parent = *link;
+			insert_point = rb_entry(parent, struct jffs2_tmp_dnode_info, rb);
+			if (tn->fn->ofs > insert_point->fn->ofs)
+				link = &insert_point->rb.rb_right;
+			else if (tn->fn->ofs < insert_point->fn->ofs ||
+				 tn->fn->size < insert_point->fn->size)
+				link = &insert_point->rb.rb_left;
+			else
+				link = &insert_point->rb.rb_right;
+		}
+		rb_link_node(&tn->rb, &insert_point->rb, link);
+		rb_insert_color(&tn->rb, &rii->tn_root);
+	}
+	/* If there's anything behind that overlaps us, note it */
+	this = tn_prev(tn);
+	if (this) {
+		while (1) {
+			if (this->fn->ofs + this->fn->size > tn->fn->ofs) {
+				dbg_readinode("Node is overlapped by %p (v %d, 0x%x-0x%x)\n",
+					      this, this->version, this->fn->ofs,
+					      this->fn->ofs+this->fn->size);
+				tn->overlapped = 1;
+				break;
+			}
+			if (!this->overlapped)
+				break;
+			this = tn_prev(this);
+		}
+	}
+
+	/* If the new node overlaps anything ahead, note it */
+	this = tn_next(tn);
+	while (this && this->fn->ofs < fn_end) {
+		this->overlapped = 1;
+		dbg_readinode("Node ver %d, 0x%x-0x%x is overlapped\n",
+			      this->version, this->fn->ofs,
+			      this->fn->ofs+this->fn->size);
+		this = tn_next(this);
+	}
+	return 0;
+}
+
+/* Trivial function to remove the last node in the tree. Which by definition
+   has no right-hand -- so can be removed just by making its only child (if
+   any) take its place under its parent. */
+static void eat_last(struct rb_root *root, struct rb_node *node)
+{
+	struct rb_node *parent = rb_parent(node);
+	struct rb_node **link;
+
+	/* LAST! */
+	BUG_ON(node->rb_right);
+
+	if (!parent)
+		link = &root->rb_node;
+	else if (node == parent->rb_left)
+		link = &parent->rb_left;
+	else
+		link = &parent->rb_right;
+
+	*link = node->rb_left;
+	/* Colour doesn't matter now. Only the parent pointer. */
+	if (node->rb_left)
+		node->rb_left->rb_parent_color = node->rb_parent_color;
+}
+
+/* We put this in reverse order, so we can just use eat_last */
+static void ver_insert(struct rb_root *ver_root, struct jffs2_tmp_dnode_info *tn)
+{
+	struct rb_node **link = &ver_root->rb_node;
+	struct rb_node *parent = NULL;
+	struct jffs2_tmp_dnode_info *this_tn;
+
+	while (*link) {
+		parent = *link;
+		this_tn = rb_entry(parent, struct jffs2_tmp_dnode_info, rb);
+
+		if (tn->version > this_tn->version)
+			link = &parent->rb_left;
+		else
+			link = &parent->rb_right;
+	}
+	dbg_readinode("Link new node at %p (root is %p)\n", link, ver_root);
+	rb_link_node(&tn->rb, parent, link);
+	rb_insert_color(&tn->rb, ver_root);
+}
+
+/* Build final, normal fragtree from tn tree. It doesn't matter which order
+   we add nodes to the real fragtree, as long as they don't overlap. And
+   having thrown away the majority of overlapped nodes as we went, there
+   really shouldn't be many sets of nodes which do overlap. If we start at
+   the end, we can use the overlap markers -- we can just eat nodes which
+   aren't overlapped, and when we encounter nodes which _do_ overlap we
+   sort them all into a temporary tree in version order before replaying them. */
+static int jffs2_build_inode_fragtree(struct jffs2_sb_info *c,
+				      struct jffs2_inode_info *f,
+				      struct jffs2_readinode_info *rii)
+{
+	struct jffs2_tmp_dnode_info *pen, *last, *this;
+	struct rb_root ver_root = RB_ROOT;
+	uint32_t high_ver = 0;
+
+	if (rii->mdata_tn) {
+		dbg_readinode("potential mdata is ver %d at %p\n", rii->mdata_tn->version, rii->mdata_tn);
+		high_ver = rii->mdata_tn->version;
+		rii->latest_ref = rii->mdata_tn->fn->raw;
+	}
+#ifdef JFFS2_DBG_READINODE_MESSAGES
+	this = tn_last(&rii->tn_root);
+	while (this) {
+		dbg_readinode("tn %p ver %d range 0x%x-0x%x ov %d\n", this, this->version, this->fn->ofs,
+			     this->fn->ofs+this->fn->size, this->overlapped);
+		this = tn_prev(this);
+	}
+#endif
+	pen = tn_last(&rii->tn_root);
+	while ((last = pen)) {
+		pen = tn_prev(last);
+
+		eat_last(&rii->tn_root, &last->rb);
+		ver_insert(&ver_root, last);
+
+		if (unlikely(last->overlapped))
+			continue;
+
+		/* Now we have a bunch of nodes in reverse version
+		   order, in the tree at ver_root. Most of the time,
+		   there'll actually be only one node in the 'tree',
+		   in fact. */
+		this = tn_last(&ver_root);
+
+		while (this) {
+			struct jffs2_tmp_dnode_info *vers_next;
+			int ret;
+			vers_next = tn_prev(this);
+			eat_last(&ver_root, &this->rb);
+			if (check_tn_node(c, this)) {
+				dbg_readinode("node ver %x, 0x%x-0x%x failed CRC\n",
+					     this->version, this->fn->ofs,
+					     this->fn->ofs+this->fn->size);
+				jffs2_kill_tn(c, this);
+			} else {
+				if (this->version > high_ver) {
+					/* Note that this is different from the other
+					   highest_version, because this one is only
+					   counting _valid_ nodes which could give the
+					   latest inode metadata */
+					high_ver = this->version;
+					rii->latest_ref = this->fn->raw;
+				}
+				dbg_readinode("Add %p (v %x, 0x%x-0x%x, ov %d) to fragtree\n",
+					     this, this->version, this->fn->ofs,
+					     this->fn->ofs+this->fn->size, this->overlapped);
+
+				ret = jffs2_add_full_dnode_to_inode(c, f, this->fn);
+				if (ret) {
+					/* Free the nodes in vers_root; let the caller
+					   deal with the rest */
+					JFFS2_ERROR("Add node to tree failed %d\n", ret);
+					while (1) {
+						vers_next = tn_prev(this);
+						if (check_tn_node(c, this))
+							jffs2_mark_node_obsolete(c, this->fn->raw);
+						jffs2_free_full_dnode(this->fn);
+						jffs2_free_tmp_dnode_info(this);
+						this = vers_next;
+						if (!this)
+							break;
+						eat_last(&ver_root, &vers_next->rb);
+					}
+					return ret;
+				}
+				jffs2_free_tmp_dnode_info(this);
+			}
+			this = vers_next;
+		}
+	}
+	return 0;
 }
 
 static void jffs2_free_tmp_dnode_info_list(struct rb_root *list)
@@ -112,8 +590,8 @@
  * 	    negative error code on failure.
  */
 static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref,
-				struct jffs2_raw_dirent *rd, size_t read, struct jffs2_full_dirent **fdp,
-				uint32_t *latest_mctime, uint32_t *mctime_ver)
+				struct jffs2_raw_dirent *rd, size_t read,
+				struct jffs2_readinode_info *rii)
 {
 	struct jffs2_full_dirent *fd;
 	uint32_t crc;
@@ -125,7 +603,8 @@
 	if (unlikely(crc != je32_to_cpu(rd->node_crc))) {
 		JFFS2_NOTICE("header CRC failed on dirent node at %#08x: read %#08x, calculated %#08x\n",
 			     ref_offset(ref), je32_to_cpu(rd->node_crc), crc);
-		return 1;
+		jffs2_mark_node_obsolete(c, ref);
+		return 0;
 	}
 
 	/* If we've never checked the CRCs on this node, check them now */
@@ -137,7 +616,8 @@
 		if (unlikely(PAD((rd->nsize + sizeof(*rd))) != PAD(je32_to_cpu(rd->totlen)))) {
 			JFFS2_ERROR("illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n",
 				    ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen));
-			return 1;
+			jffs2_mark_node_obsolete(c, ref);
+			return 0;
 		}
 
 		jeb = &c->blocks[ref->flash_offset / c->sector_size];
@@ -161,10 +641,13 @@
 	fd->ino = je32_to_cpu(rd->ino);
 	fd->type = rd->type;
 
+	if (fd->version > rii->highest_version)
+		rii->highest_version = fd->version;
+
 	/* Pick out the mctime of the latest dirent */
-	if(fd->version > *mctime_ver && je32_to_cpu(rd->mctime)) {
-		*mctime_ver = fd->version;
-		*latest_mctime = je32_to_cpu(rd->mctime);
+	if(fd->version > rii->mctime_ver && je32_to_cpu(rd->mctime)) {
+		rii->mctime_ver = fd->version;
+		rii->latest_mctime = je32_to_cpu(rd->mctime);
 	}
 
 	/*
@@ -201,7 +684,7 @@
 	 * Wheee. We now have a complete jffs2_full_dirent structure, with
 	 * the name in it and everything. Link it into the list
 	 */
-	jffs2_add_fd_to_list(c, fd, fdp);
+	jffs2_add_fd_to_list(c, fd, &rii->fds);
 
 	return 0;
 }
@@ -210,13 +693,13 @@
  * Helper function for jffs2_get_inode_nodes().
  * It is called every time an inode node is found.
  *
- * Returns: 0 on succes;
+ * Returns: 0 on success;
  * 	    1 if the node should be marked obsolete;
  * 	    negative error code on failure.
  */
 static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref,
-			     struct jffs2_raw_inode *rd, struct rb_root *tnp, int rdlen,
-			     uint32_t *latest_mctime, uint32_t *mctime_ver)
+			     struct jffs2_raw_inode *rd, int rdlen,
+			     struct jffs2_readinode_info *rii)
 {
 	struct jffs2_tmp_dnode_info *tn;
 	uint32_t len, csize;
@@ -230,7 +713,8 @@
 	if (unlikely(crc != je32_to_cpu(rd->node_crc))) {
 		JFFS2_NOTICE("node CRC failed on dnode at %#08x: read %#08x, calculated %#08x\n",
 			     ref_offset(ref), je32_to_cpu(rd->node_crc), crc);
-		return 1;
+		jffs2_mark_node_obsolete(c, ref);
+		return 0;
 	}
 
 	tn = jffs2_alloc_tmp_dnode_info();
@@ -342,6 +826,10 @@
 	tn->data_crc = je32_to_cpu(rd->data_crc);
 	tn->csize = csize;
 	tn->fn->raw = ref;
+	tn->overlapped = 0;
+
+	if (tn->version > rii->highest_version)
+		rii->highest_version = tn->version;
 
 	/* There was a bug where we wrote hole nodes out with
 	   csize/dsize swapped. Deal with it */
@@ -353,13 +841,25 @@
 	dbg_readinode("dnode @%08x: ver %u, offset %#04x, dsize %#04x, csize %#04x\n",
 		  ref_offset(ref), je32_to_cpu(rd->version), je32_to_cpu(rd->offset), je32_to_cpu(rd->dsize), csize);
 
-	jffs2_add_tn_to_tree(tn, tnp);
+	ret = jffs2_add_tn_to_tree(c, rii, tn);
 
+	if (ret) {
+		jffs2_free_full_dnode(tn->fn);
+	free_out:
+		jffs2_free_tmp_dnode_info(tn);
+		return ret;
+	}
+#ifdef JFFS2_DBG_READINODE_MESSAGES
+	dbg_readinode("After adding ver %d:\n", tn->version);
+	tn = tn_first(&rii->tn_root);
+	while (tn) {
+		dbg_readinode("%p: v %d r 0x%x-0x%x ov %d\n",
+			     tn, tn->version, tn->fn->ofs,
+			     tn->fn->ofs+tn->fn->size, tn->overlapped);
+		tn = tn_next(tn);
+	}
+#endif
 	return 0;
-
-free_out:
-	jffs2_free_tmp_dnode_info(tn);
-	return ret;
 }
 
 /*
@@ -379,7 +879,8 @@
 		JFFS2_ERROR("Node is {%04x,%04x,%08x,%08x}. Please report this error.\n",
                             je16_to_cpu(un->magic), je16_to_cpu(un->nodetype),
                             je32_to_cpu(un->totlen), je32_to_cpu(un->hdr_crc));
-		return 1;
+		jffs2_mark_node_obsolete(c, ref);
+		return 0;
 	}
 
 	un->nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(un->nodetype));
@@ -407,7 +908,8 @@
 	case JFFS2_FEATURE_RWCOMPAT_DELETE:
 		JFFS2_NOTICE("unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n",
 			     je16_to_cpu(un->nodetype), ref_offset(ref));
-		return 1;
+		jffs2_mark_node_obsolete(c, ref);
+		return 0;
 	}
 
 	return 0;
@@ -421,92 +923,62 @@
  * 	    negative error code on failure.
  */
 static int read_more(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref,
-		     int right_size, int *rdlen, unsigned char *buf, unsigned char *bufstart)
+		     int needed_len, int *rdlen, unsigned char *buf)
 {
-	int right_len, err, len;
+	int err, to_read = needed_len - *rdlen;
 	size_t retlen;
 	uint32_t offs;
 
 	if (jffs2_is_writebuffered(c)) {
-		right_len = c->wbuf_pagesize - (bufstart - buf);
-		if (right_size + (int)(bufstart - buf) > c->wbuf_pagesize)
-			right_len += c->wbuf_pagesize;
-	} else
-		right_len = right_size;
+		int rem = to_read % c->wbuf_pagesize;
 
-	if (*rdlen == right_len)
-		return 0;
+		if (rem)
+			to_read += c->wbuf_pagesize - rem;
+	}
 
 	/* We need to read more data */
 	offs = ref_offset(ref) + *rdlen;
-	if (jffs2_is_writebuffered(c)) {
-		bufstart = buf + c->wbuf_pagesize;
-		len = c->wbuf_pagesize;
-	} else {
-		bufstart = buf + *rdlen;
-		len = right_size - *rdlen;
-	}
 
-	dbg_readinode("read more %d bytes\n", len);
+	dbg_readinode("read more %d bytes\n", to_read);
 
-	err = jffs2_flash_read(c, offs, len, &retlen, bufstart);
+	err = jffs2_flash_read(c, offs, to_read, &retlen, buf + *rdlen);
 	if (err) {
 		JFFS2_ERROR("can not read %d bytes from 0x%08x, "
-			"error code: %d.\n", len, offs, err);
+			"error code: %d.\n", to_read, offs, err);
 		return err;
 	}
 
-	if (retlen < len) {
+	if (retlen < to_read) {
 		JFFS2_ERROR("short read at %#08x: %zu instead of %d.\n",
-				offs, retlen, len);
+				offs, retlen, to_read);
 		return -EIO;
 	}
 
-	*rdlen = right_len;
-
+	*rdlen += to_read;
 	return 0;
 }
 
 /* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated
-   with this ino, returning the former in order of version */
+   with this ino. Perform a preliminary ordering on data nodes, throwing away
+   those which are completely obsoleted by newer ones. The naïve approach we
+   use to take of just returning them _all_ in version order will cause us to
+   run out of memory in certain degenerate cases. */
 static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
-				 struct rb_root *tnp, struct jffs2_full_dirent **fdp,
-				 uint32_t *highest_version, uint32_t *latest_mctime,
-				 uint32_t *mctime_ver)
+				 struct jffs2_readinode_info *rii)
 {
 	struct jffs2_raw_node_ref *ref, *valid_ref;
-	struct rb_root ret_tn = RB_ROOT;
-	struct jffs2_full_dirent *ret_fd = NULL;
 	unsigned char *buf = NULL;
 	union jffs2_node_union *node;
 	size_t retlen;
 	int len, err;
 
-	*mctime_ver = 0;
+	rii->mctime_ver = 0;
 
 	dbg_readinode("ino #%u\n", f->inocache->ino);
 
-	if (jffs2_is_writebuffered(c)) {
-		/*
-		 * If we have the write buffer, we assume the minimal I/O unit
-		 * is c->wbuf_pagesize. We implement some optimizations which in
-		 * this case and we need a temporary buffer of size =
-		 * 2*c->wbuf_pagesize bytes (see comments in read_dnode()).
-		 * Basically, we want to read not only the node header, but the
-		 * whole wbuf (NAND page in case of NAND) or 2, if the node
-		 * header overlaps the border between the 2 wbufs.
-		 */
-		len = 2*c->wbuf_pagesize;
-	} else {
-		/*
-		 * When there is no write buffer, the size of the temporary
-		 * buffer is the size of the larges node header.
-		 */
-		len = sizeof(union jffs2_node_union);
-	}
-
 	/* FIXME: in case of NOR and available ->point() this
 	 * needs to be fixed. */
+	len = sizeof(union jffs2_node_union) + c->wbuf_pagesize;
 	buf = kmalloc(len, GFP_KERNEL);
 	if (!buf)
 		return -ENOMEM;
@@ -516,8 +988,6 @@
 	if (!valid_ref && f->inocache->ino != 1)
 		JFFS2_WARNING("Eep. No valid nodes for ino #%u.\n", f->inocache->ino);
 	while (valid_ref) {
-		unsigned char *bufstart;
-
 		/* We can hold a pointer to a non-obsolete node without the spinlock,
 		   but _obsolete_ nodes may disappear at any time, if the block
 		   they're in gets erased. So if we mark 'ref' obsolete while we're
@@ -533,32 +1003,31 @@
 		/*
 		 * At this point we don't know the type of the node we're going
 		 * to read, so we do not know the size of its header. In order
-		 * to minimize the amount of flash IO we assume the node has
-		 * size = JFFS2_MIN_NODE_HEADER.
+		 * to minimize the amount of flash IO we assume the header is
+		 * of size = JFFS2_MIN_NODE_HEADER.
 		 */
+		len = JFFS2_MIN_NODE_HEADER;
 		if (jffs2_is_writebuffered(c)) {
+			int end, rem;
+
 			/*
-			 * We treat 'buf' as 2 adjacent wbufs. We want to
-			 * adjust bufstart such as it points to the
-			 * beginning of the node within this wbuf.
+			 * We are about to read JFFS2_MIN_NODE_HEADER bytes,
+			 * but this flash has some minimal I/O unit. It is
+			 * possible that we'll need to read more soon, so read
+			 * up to the next min. I/O unit, in order not to
+			 * re-read the same min. I/O unit twice.
 			 */
-			bufstart = buf + (ref_offset(ref) % c->wbuf_pagesize);
-			/* We will read either one wbuf or 2 wbufs. */
-			len = c->wbuf_pagesize - (bufstart - buf);
-			if (JFFS2_MIN_NODE_HEADER + (int)(bufstart - buf) > c->wbuf_pagesize) {
-				/* The header spans the border of the first wbuf */
-				len += c->wbuf_pagesize;
-			}
-		} else {
-			bufstart = buf;
-			len = JFFS2_MIN_NODE_HEADER;
+			end = ref_offset(ref) + len;
+			rem = end % c->wbuf_pagesize;
+			if (rem)
+				end += c->wbuf_pagesize - rem;
+			len = end - ref_offset(ref);
 		}
 
 		dbg_readinode("read %d bytes at %#08x(%d).\n", len, ref_offset(ref), ref_flags(ref));
 
 		/* FIXME: point() */
-		err = jffs2_flash_read(c, ref_offset(ref), len,
-				       &retlen, bufstart);
+		err = jffs2_flash_read(c, ref_offset(ref), len, &retlen, buf);
 		if (err) {
 			JFFS2_ERROR("can not read %d bytes from 0x%08x, " "error code: %d.\n", len, ref_offset(ref), err);
 			goto free_out;
@@ -570,7 +1039,7 @@
 			goto free_out;
 		}
 
-		node = (union jffs2_node_union *)bufstart;
+		node = (union jffs2_node_union *)buf;
 
 		/* No need to mask in the valid bit; it shouldn't be invalid */
 		if (je32_to_cpu(node->u.hdr_crc) != crc32(0, node, sizeof(node->u)-4)) {
@@ -583,10 +1052,10 @@
 			jffs2_mark_node_obsolete(c, ref);
 			goto cont;
 		}
-		/* Due to poor choice of crc32 seed, an all-zero node will have a correct CRC */
-		if (!je32_to_cpu(node->u.hdr_crc) && !je16_to_cpu(node->u.nodetype) &&
-		    !je16_to_cpu(node->u.magic) && !je32_to_cpu(node->u.totlen)) {
-			JFFS2_NOTICE("All zero node header at %#08x.\n", ref_offset(ref));
+		if (je16_to_cpu(node->u.magic) != JFFS2_MAGIC_BITMASK) {
+			/* Not a JFFS2 node, whinge and move on */
+			JFFS2_NOTICE("Wrong magic bitmask 0x%04x in node header at %#08x.\n",
+				     je16_to_cpu(node->u.magic), ref_offset(ref));
 			jffs2_mark_node_obsolete(c, ref);
 			goto cont;
 		}
@@ -596,46 +1065,34 @@
 		case JFFS2_NODETYPE_DIRENT:
 
 			if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_raw_dirent)) {
-				err = read_more(c, ref, sizeof(struct jffs2_raw_dirent), &len, buf, bufstart);
+				err = read_more(c, ref, sizeof(struct jffs2_raw_dirent), &len, buf);
 				if (unlikely(err))
 					goto free_out;
 			}
 
-			err = read_direntry(c, ref, &node->d, retlen, &ret_fd, latest_mctime, mctime_ver);
-			if (err == 1) {
-				jffs2_mark_node_obsolete(c, ref);
-				break;
-			} else if (unlikely(err))
+			err = read_direntry(c, ref, &node->d, retlen, rii);
+			if (unlikely(err))
 				goto free_out;
 
-			if (je32_to_cpu(node->d.version) > *highest_version)
-				*highest_version = je32_to_cpu(node->d.version);
-
 			break;
 
 		case JFFS2_NODETYPE_INODE:
 
 			if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_raw_inode)) {
-				err = read_more(c, ref, sizeof(struct jffs2_raw_inode), &len, buf, bufstart);
+				err = read_more(c, ref, sizeof(struct jffs2_raw_inode), &len, buf);
 				if (unlikely(err))
 					goto free_out;
 			}
 
-			err = read_dnode(c, ref, &node->i, &ret_tn, len, latest_mctime, mctime_ver);
-			if (err == 1) {
-				jffs2_mark_node_obsolete(c, ref);
-				break;
-			} else if (unlikely(err))
+			err = read_dnode(c, ref, &node->i, len, rii);
+			if (unlikely(err))
 				goto free_out;
 
-			if (je32_to_cpu(node->i.version) > *highest_version)
-				*highest_version = je32_to_cpu(node->i.version);
-
 			break;
 
 		default:
 			if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_unknown_node)) {
-				err = read_more(c, ref, sizeof(struct jffs2_unknown_node), &len, buf, bufstart);
+				err = read_more(c, ref, sizeof(struct jffs2_unknown_node), &len, buf);
 				if (unlikely(err))
 					goto free_out;
 			}
@@ -653,17 +1110,19 @@
 	}
 
 	spin_unlock(&c->erase_completion_lock);
-	*tnp = ret_tn;
-	*fdp = ret_fd;
 	kfree(buf);
 
+	f->highest_version = rii->highest_version;
+
 	dbg_readinode("nodes of inode #%u were read, the highest version is %u, latest_mctime %u, mctime_ver %u.\n",
-			f->inocache->ino, *highest_version, *latest_mctime, *mctime_ver);
+		      f->inocache->ino, rii->highest_version, rii->latest_mctime,
+		      rii->mctime_ver);
 	return 0;
 
  free_out:
-	jffs2_free_tmp_dnode_info_list(&ret_tn);
-	jffs2_free_full_dirent_list(ret_fd);
+	jffs2_free_tmp_dnode_info_list(&rii->tn_root);
+	jffs2_free_full_dirent_list(rii->fds);
+	rii->fds = NULL;
 	kfree(buf);
 	return err;
 }
@@ -672,20 +1131,17 @@
 					struct jffs2_inode_info *f,
 					struct jffs2_raw_inode *latest_node)
 {
-	struct jffs2_tmp_dnode_info *tn;
-	struct rb_root tn_list;
-	struct rb_node *rb, *repl_rb;
-	struct jffs2_full_dirent *fd_list;
-	struct jffs2_full_dnode *fn, *first_fn = NULL;
-	uint32_t crc;
-	uint32_t latest_mctime, mctime_ver;
+	struct jffs2_readinode_info rii;
+	uint32_t crc, new_size;
 	size_t retlen;
 	int ret;
 
 	dbg_readinode("ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink);
 
+	memset(&rii, 0, sizeof(rii));
+
 	/* Grab all nodes relevant to this ino */
-	ret = jffs2_get_inode_nodes(c, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
+	ret = jffs2_get_inode_nodes(c, f, &rii);
 
 	if (ret) {
 		JFFS2_ERROR("cannot read nodes for ino %u, returned error is %d\n", f->inocache->ino, ret);
@@ -693,74 +1149,42 @@
 			jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
 		return ret;
 	}
-	f->dents = fd_list;
 
-	rb = rb_first(&tn_list);
-
-	while (rb) {
-		cond_resched();
-		tn = rb_entry(rb, struct jffs2_tmp_dnode_info, rb);
-		fn = tn->fn;
-		ret = 1;
-		dbg_readinode("consider node ver %u, phys offset "
-			"%#08x(%d), range %u-%u.\n", tn->version,
-			ref_offset(fn->raw), ref_flags(fn->raw),
-			fn->ofs, fn->ofs + fn->size);
-
-		if (fn->size) {
-			ret = jffs2_add_older_frag_to_fragtree(c, f, tn);
-			/* TODO: the error code isn't checked, check it */
-			jffs2_dbg_fragtree_paranoia_check_nolock(f);
-			BUG_ON(ret < 0);
-			if (!first_fn && ret == 0)
-				first_fn = fn;
-		} else if (!first_fn) {
-			first_fn = fn;
-			f->metadata = fn;
-			ret = 0; /* Prevent freeing the metadata update node */
-		} else
-			jffs2_mark_node_obsolete(c, fn->raw);
-
-		BUG_ON(rb->rb_left);
-		if (rb_parent(rb) && rb_parent(rb)->rb_left == rb) {
-			/* We were then left-hand child of our parent. We need
-			 * to move our own right-hand child into our place. */
-			repl_rb = rb->rb_right;
-			if (repl_rb)
-				rb_set_parent(repl_rb, rb_parent(rb));
-		} else
-			repl_rb = NULL;
-
-		rb = rb_next(rb);
-
-		/* Remove the spent tn from the tree; don't bother rebalancing
-		 * but put our right-hand child in our own place. */
-		if (rb_parent(&tn->rb)) {
-			if (rb_parent(&tn->rb)->rb_left == &tn->rb)
-				rb_parent(&tn->rb)->rb_left = repl_rb;
-			else if (rb_parent(&tn->rb)->rb_right == &tn->rb)
-				rb_parent(&tn->rb)->rb_right = repl_rb;
-			else BUG();
-		} else if (tn->rb.rb_right)
-			rb_set_parent(tn->rb.rb_right, NULL);
-
-		jffs2_free_tmp_dnode_info(tn);
-		if (ret) {
-			dbg_readinode("delete dnode %u-%u.\n",
-				fn->ofs, fn->ofs + fn->size);
-			jffs2_free_full_dnode(fn);
+	ret = jffs2_build_inode_fragtree(c, f, &rii);
+	if (ret) {
+		JFFS2_ERROR("Failed to build final fragtree for inode #%u: error %d\n",
+			    f->inocache->ino, ret);
+		if (f->inocache->state == INO_STATE_READING)
+			jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
+		jffs2_free_tmp_dnode_info_list(&rii.tn_root);
+		/* FIXME: We could at least crc-check them all */
+		if (rii.mdata_tn) {
+			jffs2_free_full_dnode(rii.mdata_tn->fn);
+			jffs2_free_tmp_dnode_info(rii.mdata_tn);
+			rii.mdata_tn = NULL;
 		}
+		return ret;
 	}
+
+	if (rii.mdata_tn) {
+		if (rii.mdata_tn->fn->raw == rii.latest_ref) {
+			f->metadata = rii.mdata_tn->fn;
+			jffs2_free_tmp_dnode_info(rii.mdata_tn);
+		} else {
+			jffs2_kill_tn(c, rii.mdata_tn);
+		}
+		rii.mdata_tn = NULL;
+	}
+
+	f->dents = rii.fds;
+
 	jffs2_dbg_fragtree_paranoia_check_nolock(f);
 
-	BUG_ON(first_fn && ref_obsolete(first_fn->raw));
-
-	fn = first_fn;
-	if (unlikely(!first_fn)) {
+	if (unlikely(!rii.latest_ref)) {
 		/* No data nodes for this inode. */
 		if (f->inocache->ino != 1) {
 			JFFS2_WARNING("no data nodes found for ino #%u\n", f->inocache->ino);
-			if (!fd_list) {
+			if (!rii.fds) {
 				if (f->inocache->state == INO_STATE_READING)
 					jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
 				return -EIO;
@@ -778,7 +1202,7 @@
 		return 0;
 	}
 
-	ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node);
+	ret = jffs2_flash_read(c, ref_offset(rii.latest_ref), sizeof(*latest_node), &retlen, (void *)latest_node);
 	if (ret || retlen != sizeof(*latest_node)) {
 		JFFS2_ERROR("failed to read from flash: error %d, %zd of %zd bytes read\n",
 			ret, retlen, sizeof(*latest_node));
@@ -791,7 +1215,7 @@
 	crc = crc32(0, latest_node, sizeof(*latest_node)-8);
 	if (crc != je32_to_cpu(latest_node->node_crc)) {
 		JFFS2_ERROR("CRC failed for read_inode of inode %u at physical location 0x%x\n",
-			f->inocache->ino, ref_offset(fn->raw));
+			f->inocache->ino, ref_offset(rii.latest_ref));
 		up(&f->sem);
 		jffs2_do_clear_inode(c, f);
 		return -EIO;
@@ -799,17 +1223,22 @@
 
 	switch(jemode_to_cpu(latest_node->mode) & S_IFMT) {
 	case S_IFDIR:
-		if (mctime_ver > je32_to_cpu(latest_node->version)) {
+		if (rii.mctime_ver > je32_to_cpu(latest_node->version)) {
 			/* The times in the latest_node are actually older than
 			   mctime in the latest dirent. Cheat. */
-			latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime);
+			latest_node->ctime = latest_node->mtime = cpu_to_je32(rii.latest_mctime);
 		}
 		break;
 
 
 	case S_IFREG:
 		/* If it was a regular file, truncate it to the latest node's isize */
-		jffs2_truncate_fragtree(c, &f->fragtree, je32_to_cpu(latest_node->isize));
+		new_size = jffs2_truncate_fragtree(c, &f->fragtree, je32_to_cpu(latest_node->isize));
+		if (new_size != je32_to_cpu(latest_node->isize)) {
+			JFFS2_WARNING("Truncating ino #%u to %d bytes failed because it only had %d bytes to start with!\n",
+				      f->inocache->ino, je32_to_cpu(latest_node->isize), new_size);
+			latest_node->isize = cpu_to_je32(new_size);
+		}
 		break;
 
 	case S_IFLNK:
@@ -832,7 +1261,7 @@
 				return -ENOMEM;
 			}
 
-			ret = jffs2_flash_read(c, ref_offset(fn->raw) + sizeof(*latest_node),
+			ret = jffs2_flash_read(c, ref_offset(rii.latest_ref) + sizeof(*latest_node),
 						je32_to_cpu(latest_node->csize), &retlen, (char *)f->target);
 
 			if (ret  || retlen != je32_to_cpu(latest_node->csize)) {
diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c
index 7fb45bd4..2a1c976 100644
--- a/fs/jffs2/scan.c
+++ b/fs/jffs2/scan.c
@@ -1,15 +1,14 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: scan.c,v 1.125 2005/09/30 13:59:13 dedekind Exp $
- *
  */
+
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
@@ -636,16 +635,17 @@
 
 		if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) {
 			uint32_t inbuf_ofs;
-			uint32_t empty_start;
+			uint32_t empty_start, scan_end;
 
 			empty_start = ofs;
 			ofs += 4;
+			scan_end = min_t(uint32_t, EMPTY_SCAN_SIZE(c->sector_size)/8, buf_len);
 
 			D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs));
 		more_empty:
 			inbuf_ofs = ofs - buf_ofs;
-			while (inbuf_ofs < buf_len) {
-				if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) {
+			while (inbuf_ofs < scan_end) {
+				if (unlikely(*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff)) {
 					printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n",
 					       empty_start, ofs);
 					if ((err = jffs2_scan_dirty_space(c, jeb, ofs-empty_start)))
@@ -666,7 +666,11 @@
 				D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size)));
 				return BLK_STATE_CLEANMARKER;
 			}
-
+			if (!buf_size && (scan_end != buf_len)) {/* XIP/point case */
+				scan_end = buf_len;
+				goto more_empty;
+			}
+			
 			/* See how much more there is to read in this eraseblock... */
 			buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
 			if (!buf_len) {
@@ -676,6 +680,8 @@
 					  empty_start));
 				break;
 			}
+			/* point never reaches here */
+			scan_end = buf_len;
 			D1(printk(KERN_DEBUG "Reading another 0x%x at 0x%08x\n", buf_len, ofs));
 			err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
 			if (err)
@@ -734,18 +740,8 @@
 			ofs += 4;
 			continue;
 		}
-		/* Due to poor choice of crc32 seed, an all-zero node will have a correct CRC */
-		if (!je32_to_cpu(node->hdr_crc) && !je16_to_cpu(node->nodetype) &&
-		    !je16_to_cpu(node->magic) && !je32_to_cpu(node->totlen)) {
-			noisy_printk(&noise, "jffs2_scan_eraseblock(): All zero node header at 0x%08x.\n", ofs);
-			if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
-				return err;
-			ofs += 4;
-			continue;
-		}
 
-		if (ofs + je32_to_cpu(node->totlen) >
-		    jeb->offset + c->sector_size) {
+		if (ofs + je32_to_cpu(node->totlen) > jeb->offset + c->sector_size) {
 			/* Eep. Node goes over the end of the erase block. */
 			printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n",
 			       ofs, je32_to_cpu(node->totlen));
@@ -952,8 +948,7 @@
 				 struct jffs2_raw_inode *ri, uint32_t ofs, struct jffs2_summary *s)
 {
 	struct jffs2_inode_cache *ic;
-	uint32_t ino = je32_to_cpu(ri->ino);
-	int err;
+	uint32_t crc, ino = je32_to_cpu(ri->ino);
 
 	D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs));
 
@@ -966,21 +961,22 @@
 	   Which means that the _full_ amount of time to get to proper write mode with GC
 	   operational may actually be _longer_ than before. Sucks to be me. */
 
+	/* Check the node CRC in any case. */
+	crc = crc32(0, ri, sizeof(*ri)-8);
+	if (crc != je32_to_cpu(ri->node_crc)) {
+		printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on "
+		       "node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+		       ofs, je32_to_cpu(ri->node_crc), crc);
+		/*
+		 * We believe totlen because the CRC on the node
+		 * _header_ was OK, just the node itself failed.
+		 */
+		return jffs2_scan_dirty_space(c, jeb,
+					      PAD(je32_to_cpu(ri->totlen)));
+	}
+
 	ic = jffs2_get_ino_cache(c, ino);
 	if (!ic) {
-		/* Inocache get failed. Either we read a bogus ino# or it's just genuinely the
-		   first node we found for this inode. Do a CRC check to protect against the former
-		   case */
-		uint32_t crc = crc32(0, ri, sizeof(*ri)-8);
-
-		if (crc != je32_to_cpu(ri->node_crc)) {
-			printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
-			       ofs, je32_to_cpu(ri->node_crc), crc);
-			/* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
-			if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(ri->totlen)))))
-				return err;
-			return 0;
-		}
 		ic = jffs2_scan_make_ino_cache(c, ino);
 		if (!ic)
 			return -ENOMEM;
diff --git a/fs/jffs2/security.c b/fs/jffs2/security.c
index 52a9894..bc9f6ba 100644
--- a/fs/jffs2/security.c
+++ b/fs/jffs2/security.c
@@ -1,13 +1,14 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2006  NEC Corporation
+ * Copyright © 2006  NEC Corporation
  *
  * Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
  */
+
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
diff --git a/fs/jffs2/summary.c b/fs/jffs2/summary.c
index 30f8884..d828b29 100644
--- a/fs/jffs2/summary.c
+++ b/fs/jffs2/summary.c
@@ -1,16 +1,14 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2004  Ferenc Havasi <havasi@inf.u-szeged.hu>,
- *                     Zoltan Sogor <weth@inf.u-szeged.hu>,
- *                     Patrik Kluba <pajko@halom.u-szeged.hu>,
- *                     University of Szeged, Hungary
- *               2006  KaiGai Kohei <kaigai@ak.jp.nec.com>
+ * Copyright © 2004  Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ *                   Zoltan Sogor <weth@inf.u-szeged.hu>,
+ *                   Patrik Kluba <pajko@halom.u-szeged.hu>,
+ *                   University of Szeged, Hungary
+ *             2006  KaiGai Kohei <kaigai@ak.jp.nec.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: summary.c,v 1.4 2005/09/26 11:37:21 havasi Exp $
- *
  */
 
 #include <linux/kernel.h>
diff --git a/fs/jffs2/summary.h b/fs/jffs2/summary.h
index 6bf1f6a..0c6669e 100644
--- a/fs/jffs2/summary.h
+++ b/fs/jffs2/summary.h
@@ -1,15 +1,13 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2004  Ferenc Havasi <havasi@inf.u-szeged.hu>,
- *                     Zoltan Sogor <weth@inf.u-szeged.hu>,
- *                     Patrik Kluba <pajko@halom.u-szeged.hu>,
- *                     University of Szeged, Hungary
+ * Copyright © 2004  Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ *                   Zoltan Sogor <weth@inf.u-szeged.hu>,
+ *                   Patrik Kluba <pajko@halom.u-szeged.hu>,
+ *                   University of Szeged, Hungary
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: summary.h,v 1.2 2005/09/26 11:37:21 havasi Exp $
- *
  */
 
 #ifndef JFFS2_SUMMARY_H
diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
index cc7e8e7..e51164a 100644
--- a/fs/jffs2/super.c
+++ b/fs/jffs2/super.c
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: super.c,v 1.110 2005/11/07 11:14:42 gleixner Exp $
- *
  */
 
 #include <linux/kernel.h>
@@ -347,7 +345,7 @@
 #ifdef CONFIG_JFFS2_SUMMARY
 	       " (SUMMARY) "
 #endif
-	       " (C) 2001-2006 Red Hat, Inc.\n");
+	       " © 2001-2006 Red Hat, Inc.\n");
 
 	jffs2_inode_cachep = kmem_cache_create("jffs2_i",
 					     sizeof(struct jffs2_inode_info),
diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c
index 7e4882c..b7339c3 100644
--- a/fs/jffs2/symlink.c
+++ b/fs/jffs2/symlink.c
@@ -1,17 +1,14 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: symlink.c,v 1.19 2005/11/07 11:14:42 gleixner Exp $
- *
  */
 
-
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c
index 4fac6dd..c556e85 100644
--- a/fs/jffs2/wbuf.c
+++ b/fs/jffs2/wbuf.c
@@ -1,16 +1,14 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
- * Copyright (C) 2004 Thomas Gleixner <tglx@linutronix.de>
+ * Copyright © 2001-2007 Red Hat, Inc.
+ * Copyright © 2004 Thomas Gleixner <tglx@linutronix.de>
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  * Modified debugged and enhanced by Thomas Gleixner <tglx@linutronix.de>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: wbuf.c,v 1.100 2005/09/30 13:59:13 dedekind Exp $
- *
  */
 
 #include <linux/kernel.h>
@@ -345,6 +343,9 @@
 		return;
 	}
 
+	/* The summary is not recovered, so it must be disabled for this erase block */
+	jffs2_sum_disable_collecting(c->summary);
+
 	ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, nr_refile);
 	if (ret) {
 		printk(KERN_WARNING "Failed to allocate node refs for wbuf recovery. Data loss ensues.\n");
@@ -967,9 +968,9 @@
 
 static const struct jffs2_unknown_node oob_cleanmarker =
 {
-	.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK),
-	.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
-	.totlen = cpu_to_je32(8)
+	.magic = constant_cpu_to_je16(JFFS2_MAGIC_BITMASK),
+	.nodetype = constant_cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
+	.totlen = constant_cpu_to_je32(8)
 };
 
 /*
@@ -1208,3 +1209,27 @@
 void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c) {
 	kfree(c->wbuf);
 }
+
+int jffs2_ubivol_setup(struct jffs2_sb_info *c) {
+	c->cleanmarker_size = 0;
+
+	if (c->mtd->writesize == 1)
+		/* We do not need write-buffer */
+		return 0;
+
+	init_rwsem(&c->wbuf_sem);
+
+	c->wbuf_pagesize =  c->mtd->writesize;
+	c->wbuf_ofs = 0xFFFFFFFF;
+	c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
+	if (!c->wbuf)
+		return -ENOMEM;
+
+	printk(KERN_INFO "JFFS2 write-buffering enabled buffer (%d) erasesize (%d)\n", c->wbuf_pagesize, c->sector_size);
+
+	return 0;
+}
+
+void jffs2_ubivol_cleanup(struct jffs2_sb_info *c) {
+	kfree(c->wbuf);
+}
diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c
index 6717679..c9fe0ab 100644
--- a/fs/jffs2/write.c
+++ b/fs/jffs2/write.c
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: write.c,v 1.97 2005/11/07 11:14:42 gleixner Exp $
- *
  */
 
 #include <linux/kernel.h>
@@ -507,8 +505,7 @@
 	uint32_t alloclen;
 	int ret;
 
-	if (1 /* alternative branch needs testing */ ||
-	    !jffs2_can_mark_obsolete(c)) {
+	if (!jffs2_can_mark_obsolete(c)) {
 		/* We can't mark stuff obsolete on the medium. We need to write a deletion dirent */
 
 		rd = jffs2_alloc_raw_dirent();
diff --git a/fs/jffs2/writev.c b/fs/jffs2/writev.c
index c638ae1..b9276b1 100644
--- a/fs/jffs2/writev.c
+++ b/fs/jffs2/writev.c
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: writev.c,v 1.8 2005/09/09 15:11:58 havasi Exp $
- *
  */
 
 #include <linux/kernel.h>
diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c
index 4bb3f18..78fc088 100644
--- a/fs/jffs2/xattr.c
+++ b/fs/jffs2/xattr.c
@@ -1,13 +1,14 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2006  NEC Corporation
+ * Copyright © 2006  NEC Corporation
  *
  * Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
  */
+
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
diff --git a/fs/jffs2/xattr.h b/fs/jffs2/xattr.h
index 06a5c69..3b0ff29 100644
--- a/fs/jffs2/xattr.h
+++ b/fs/jffs2/xattr.h
@@ -1,13 +1,14 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2006  NEC Corporation
+ * Copyright © 2006  NEC Corporation
  *
  * Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
  */
+
 #ifndef _JFFS2_FS_XATTR_H_
 #define _JFFS2_FS_XATTR_H_
 
diff --git a/fs/jffs2/xattr_trusted.c b/fs/jffs2/xattr_trusted.c
index ed046e1..8ec5765 100644
--- a/fs/jffs2/xattr_trusted.c
+++ b/fs/jffs2/xattr_trusted.c
@@ -1,13 +1,14 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2006  NEC Corporation
+ * Copyright © 2006  NEC Corporation
  *
  * Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
  */
+
 #include <linux/kernel.h>
 #include <linux/fs.h>
 #include <linux/jffs2.h>
diff --git a/fs/jffs2/xattr_user.c b/fs/jffs2/xattr_user.c
index 2f8e9aa..40942bc 100644
--- a/fs/jffs2/xattr_user.c
+++ b/fs/jffs2/xattr_user.c
@@ -1,13 +1,14 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2006  NEC Corporation
+ * Copyright © 2006  NEC Corporation
  *
  * Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
  */
+
 #include <linux/kernel.h>
 #include <linux/fs.h>
 #include <linux/jffs2.h>
diff --git a/fs/namei.c b/fs/namei.c
index ee60cc4..880052c 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1243,22 +1243,13 @@
 	return err;
 }
 
-/*
- * Restricted form of lookup. Doesn't follow links, single-component only,
- * needs parent already locked. Doesn't follow mounts.
- * SMP-safe.
- */
-static struct dentry * __lookup_hash(struct qstr *name, struct dentry * base, struct nameidata *nd)
+static inline struct dentry *__lookup_hash_kern(struct qstr *name, struct dentry *base, struct nameidata *nd)
 {
-	struct dentry * dentry;
+	struct dentry *dentry;
 	struct inode *inode;
 	int err;
 
 	inode = base->d_inode;
-	err = permission(inode, MAY_EXEC, nd);
-	dentry = ERR_PTR(err);
-	if (err)
-		goto out;
 
 	/*
 	 * See if the low-level filesystem might want
@@ -1287,35 +1278,76 @@
 	return dentry;
 }
 
+/*
+ * Restricted form of lookup. Doesn't follow links, single-component only,
+ * needs parent already locked. Doesn't follow mounts.
+ * SMP-safe.
+ */
+static inline struct dentry * __lookup_hash(struct qstr *name, struct dentry *base, struct nameidata *nd)
+{
+	struct dentry *dentry;
+	struct inode *inode;
+	int err;
+
+	inode = base->d_inode;
+
+	err = permission(inode, MAY_EXEC, nd);
+	dentry = ERR_PTR(err);
+	if (err)
+		goto out;
+
+	dentry = __lookup_hash_kern(name, base, nd);
+out:
+	return dentry;
+}
+
 static struct dentry *lookup_hash(struct nameidata *nd)
 {
 	return __lookup_hash(&nd->last, nd->dentry, nd);
 }
 
 /* SMP-safe */
-struct dentry * lookup_one_len(const char * name, struct dentry * base, int len)
+static inline int __lookup_one_len(const char *name, struct qstr *this, struct dentry *base, int len)
 {
 	unsigned long hash;
-	struct qstr this;
 	unsigned int c;
 
-	this.name = name;
-	this.len = len;
+	this->name = name;
+	this->len = len;
 	if (!len)
-		goto access;
+		return -EACCES;
 
 	hash = init_name_hash();
 	while (len--) {
 		c = *(const unsigned char *)name++;
 		if (c == '/' || c == '\0')
-			goto access;
+			return -EACCES;
 		hash = partial_name_hash(c, hash);
 	}
-	this.hash = end_name_hash(hash);
+	this->hash = end_name_hash(hash);
+	return 0;
+}
 
+struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
+{
+	int err;
+	struct qstr this;
+
+	err = __lookup_one_len(name, &this, base, len);
+	if (err)
+		return ERR_PTR(err);
 	return __lookup_hash(&this, base, NULL);
-access:
-	return ERR_PTR(-EACCES);
+}
+
+struct dentry *lookup_one_len_kern(const char *name, struct dentry *base, int len)
+{
+	int err;
+	struct qstr this;
+
+	err = __lookup_one_len(name, &this, base, len);
+	if (err)
+		return ERR_PTR(err);
+	return __lookup_hash_kern(&this, base, NULL);
 }
 
 /*
diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c
index f27e5378..a0c8667 100644
--- a/fs/ocfs2/alloc.c
+++ b/fs/ocfs2/alloc.c
@@ -27,6 +27,7 @@
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/highmem.h>
+#include <linux/swap.h>
 
 #define MLOG_MASK_PREFIX ML_DISK_ALLOC
 #include <cluster/masklog.h>
@@ -34,6 +35,7 @@
 #include "ocfs2.h"
 
 #include "alloc.h"
+#include "aops.h"
 #include "dlmglue.h"
 #include "extent_map.h"
 #include "inode.h"
@@ -47,62 +49,242 @@
 
 #include "buffer_head_io.h"
 
-static int ocfs2_extent_contig(struct inode *inode,
-			       struct ocfs2_extent_rec *ext,
-			       u64 blkno);
-
-static int ocfs2_create_new_meta_bhs(struct ocfs2_super *osb,
-				     handle_t *handle,
-				     struct inode *inode,
-				     int wanted,
-				     struct ocfs2_alloc_context *meta_ac,
-				     struct buffer_head *bhs[]);
-
-static int ocfs2_add_branch(struct ocfs2_super *osb,
-			    handle_t *handle,
-			    struct inode *inode,
-			    struct buffer_head *fe_bh,
-			    struct buffer_head *eb_bh,
-			    struct buffer_head *last_eb_bh,
-			    struct ocfs2_alloc_context *meta_ac);
-
-static int ocfs2_shift_tree_depth(struct ocfs2_super *osb,
-				  handle_t *handle,
-				  struct inode *inode,
-				  struct buffer_head *fe_bh,
-				  struct ocfs2_alloc_context *meta_ac,
-				  struct buffer_head **ret_new_eb_bh);
-
-static int ocfs2_do_insert_extent(struct ocfs2_super *osb,
-				  handle_t *handle,
-				  struct inode *inode,
-				  struct buffer_head *fe_bh,
-				  u64 blkno,
-				  u32 new_clusters);
-
-static int ocfs2_find_branch_target(struct ocfs2_super *osb,
-				    struct inode *inode,
-				    struct buffer_head *fe_bh,
-				    struct buffer_head **target_bh);
-
-static int ocfs2_find_new_last_ext_blk(struct ocfs2_super *osb,
-				       struct inode *inode,
-				       struct ocfs2_dinode *fe,
-				       unsigned int new_i_clusters,
-				       struct buffer_head *old_last_eb,
-				       struct buffer_head **new_last_eb);
-
 static void ocfs2_free_truncate_context(struct ocfs2_truncate_context *tc);
 
-static int ocfs2_extent_contig(struct inode *inode,
-			       struct ocfs2_extent_rec *ext,
-			       u64 blkno)
+/*
+ * Structures which describe a path through a btree, and functions to
+ * manipulate them.
+ *
+ * The idea here is to be as generic as possible with the tree
+ * manipulation code.
+ */
+struct ocfs2_path_item {
+	struct buffer_head		*bh;
+	struct ocfs2_extent_list	*el;
+};
+
+#define OCFS2_MAX_PATH_DEPTH	5
+
+struct ocfs2_path {
+	int			p_tree_depth;
+	struct ocfs2_path_item	p_node[OCFS2_MAX_PATH_DEPTH];
+};
+
+#define path_root_bh(_path) ((_path)->p_node[0].bh)
+#define path_root_el(_path) ((_path)->p_node[0].el)
+#define path_leaf_bh(_path) ((_path)->p_node[(_path)->p_tree_depth].bh)
+#define path_leaf_el(_path) ((_path)->p_node[(_path)->p_tree_depth].el)
+#define path_num_items(_path) ((_path)->p_tree_depth + 1)
+
+/*
+ * Reset the actual path elements so that we can re-use the structure
+ * to build another path. Generally, this involves freeing the buffer
+ * heads.
+ */
+static void ocfs2_reinit_path(struct ocfs2_path *path, int keep_root)
 {
-	return blkno == (le64_to_cpu(ext->e_blkno) +
-			 ocfs2_clusters_to_blocks(inode->i_sb,
-						  le32_to_cpu(ext->e_clusters)));
+	int i, start = 0, depth = 0;
+	struct ocfs2_path_item *node;
+
+	if (keep_root)
+		start = 1;
+
+	for(i = start; i < path_num_items(path); i++) {
+		node = &path->p_node[i];
+
+		brelse(node->bh);
+		node->bh = NULL;
+		node->el = NULL;
+	}
+
+	/*
+	 * Tree depth may change during truncate, or insert. If we're
+	 * keeping the root extent list, then make sure that our path
+	 * structure reflects the proper depth.
+	 */
+	if (keep_root)
+		depth = le16_to_cpu(path_root_el(path)->l_tree_depth);
+
+	path->p_tree_depth = depth;
 }
 
+static void ocfs2_free_path(struct ocfs2_path *path)
+{
+	if (path) {
+		ocfs2_reinit_path(path, 0);
+		kfree(path);
+	}
+}
+
+/*
+ * Make the *dest path the same as src and re-initialize src path to
+ * have a root only.
+ */
+static void ocfs2_mv_path(struct ocfs2_path *dest, struct ocfs2_path *src)
+{
+	int i;
+
+	BUG_ON(path_root_bh(dest) != path_root_bh(src));
+
+	for(i = 1; i < OCFS2_MAX_PATH_DEPTH; i++) {
+		brelse(dest->p_node[i].bh);
+
+		dest->p_node[i].bh = src->p_node[i].bh;
+		dest->p_node[i].el = src->p_node[i].el;
+
+		src->p_node[i].bh = NULL;
+		src->p_node[i].el = NULL;
+	}
+}
+
+/*
+ * Insert an extent block at given index.
+ *
+ * This will not take an additional reference on eb_bh.
+ */
+static inline void ocfs2_path_insert_eb(struct ocfs2_path *path, int index,
+					struct buffer_head *eb_bh)
+{
+	struct ocfs2_extent_block *eb = (struct ocfs2_extent_block *)eb_bh->b_data;
+
+	/*
+	 * Right now, no root bh is an extent block, so this helps
+	 * catch code errors with dinode trees. The assertion can be
+	 * safely removed if we ever need to insert extent block
+	 * structures at the root.
+	 */
+	BUG_ON(index == 0);
+
+	path->p_node[index].bh = eb_bh;
+	path->p_node[index].el = &eb->h_list;
+}
+
+static struct ocfs2_path *ocfs2_new_path(struct buffer_head *root_bh,
+					 struct ocfs2_extent_list *root_el)
+{
+	struct ocfs2_path *path;
+
+	BUG_ON(le16_to_cpu(root_el->l_tree_depth) >= OCFS2_MAX_PATH_DEPTH);
+
+	path = kzalloc(sizeof(*path), GFP_NOFS);
+	if (path) {
+		path->p_tree_depth = le16_to_cpu(root_el->l_tree_depth);
+		get_bh(root_bh);
+		path_root_bh(path) = root_bh;
+		path_root_el(path) = root_el;
+	}
+
+	return path;
+}
+
+/*
+ * Allocate and initialize a new path based on a disk inode tree.
+ */
+static struct ocfs2_path *ocfs2_new_inode_path(struct buffer_head *di_bh)
+{
+	struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
+	struct ocfs2_extent_list *el = &di->id2.i_list;
+
+	return ocfs2_new_path(di_bh, el);
+}
+
+/*
+ * Convenience function to journal all components in a path.
+ */
+static int ocfs2_journal_access_path(struct inode *inode, handle_t *handle,
+				     struct ocfs2_path *path)
+{
+	int i, ret = 0;
+
+	if (!path)
+		goto out;
+
+	for(i = 0; i < path_num_items(path); i++) {
+		ret = ocfs2_journal_access(handle, inode, path->p_node[i].bh,
+					   OCFS2_JOURNAL_ACCESS_WRITE);
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto out;
+		}
+	}
+
+out:
+	return ret;
+}
+
+enum ocfs2_contig_type {
+	CONTIG_NONE = 0,
+	CONTIG_LEFT,
+	CONTIG_RIGHT
+};
+
+
+/*
+ * NOTE: ocfs2_block_extent_contig(), ocfs2_extents_adjacent() and
+ * ocfs2_extent_contig only work properly against leaf nodes!
+ */
+static int ocfs2_block_extent_contig(struct super_block *sb,
+				     struct ocfs2_extent_rec *ext,
+				     u64 blkno)
+{
+	u64 blk_end = le64_to_cpu(ext->e_blkno);
+
+	blk_end += ocfs2_clusters_to_blocks(sb,
+				    le16_to_cpu(ext->e_leaf_clusters));
+
+	return blkno == blk_end;
+}
+
+static int ocfs2_extents_adjacent(struct ocfs2_extent_rec *left,
+				  struct ocfs2_extent_rec *right)
+{
+	u32 left_range;
+
+	left_range = le32_to_cpu(left->e_cpos) +
+		le16_to_cpu(left->e_leaf_clusters);
+
+	return (left_range == le32_to_cpu(right->e_cpos));
+}
+
+static enum ocfs2_contig_type
+	ocfs2_extent_contig(struct inode *inode,
+			    struct ocfs2_extent_rec *ext,
+			    struct ocfs2_extent_rec *insert_rec)
+{
+	u64 blkno = le64_to_cpu(insert_rec->e_blkno);
+
+	if (ocfs2_extents_adjacent(ext, insert_rec) &&
+	    ocfs2_block_extent_contig(inode->i_sb, ext, blkno))
+			return CONTIG_RIGHT;
+
+	blkno = le64_to_cpu(ext->e_blkno);
+	if (ocfs2_extents_adjacent(insert_rec, ext) &&
+	    ocfs2_block_extent_contig(inode->i_sb, insert_rec, blkno))
+		return CONTIG_LEFT;
+
+	return CONTIG_NONE;
+}
+
+/*
+ * NOTE: We can have pretty much any combination of contiguousness and
+ * appending.
+ *
+ * The usefulness of APPEND_TAIL is more in that it lets us know that
+ * we'll have to update the path to that leaf.
+ */
+enum ocfs2_append_type {
+	APPEND_NONE = 0,
+	APPEND_TAIL,
+};
+
+struct ocfs2_insert_type {
+	enum ocfs2_append_type	ins_appending;
+	enum ocfs2_contig_type	ins_contig;
+	int			ins_contig_index;
+	int			ins_free_records;
+	int			ins_tree_depth;
+};
+
 /*
  * How many free extents have we got before we need more meta data?
  */
@@ -242,6 +424,28 @@
 }
 
 /*
+ * Helper function for ocfs2_add_branch() and ocfs2_shift_tree_depth().
+ *
+ * Returns the sum of the rightmost extent rec logical offset and
+ * cluster count.
+ *
+ * ocfs2_add_branch() uses this to determine what logical cluster
+ * value should be populated into the leftmost new branch records.
+ *
+ * ocfs2_shift_tree_depth() uses this to determine the # clusters
+ * value for the new topmost tree record.
+ */
+static inline u32 ocfs2_sum_rightmost_rec(struct ocfs2_extent_list  *el)
+{
+	int i;
+
+	i = le16_to_cpu(el->l_next_free_rec) - 1;
+
+	return le32_to_cpu(el->l_recs[i].e_cpos) +
+		ocfs2_rec_clusters(el, &el->l_recs[i]);
+}
+
+/*
  * Add an entire tree branch to our inode. eb_bh is the extent block
  * to start at, if we don't want to start the branch at the dinode
  * structure.
@@ -250,7 +454,7 @@
  * for the new last extent block.
  *
  * the new branch will be 'empty' in the sense that every block will
- * contain a single record with e_clusters == 0.
+ * contain a single record with cluster count == 0.
  */
 static int ocfs2_add_branch(struct ocfs2_super *osb,
 			    handle_t *handle,
@@ -268,6 +472,7 @@
 	struct ocfs2_extent_block *eb;
 	struct ocfs2_extent_list  *eb_el;
 	struct ocfs2_extent_list  *el;
+	u32 new_cpos;
 
 	mlog_entry_void();
 
@@ -302,6 +507,9 @@
 		goto bail;
 	}
 
+	eb = (struct ocfs2_extent_block *)last_eb_bh->b_data;
+	new_cpos = ocfs2_sum_rightmost_rec(&eb->h_list);
+
 	/* Note: new_eb_bhs[new_blocks - 1] is the guy which will be
 	 * linked with the rest of the tree.
 	 * conversly, new_eb_bhs[0] is the new bottommost leaf.
@@ -330,9 +538,18 @@
 		eb->h_next_leaf_blk = 0;
 		eb_el->l_tree_depth = cpu_to_le16(i);
 		eb_el->l_next_free_rec = cpu_to_le16(1);
-		eb_el->l_recs[0].e_cpos = fe->i_clusters;
+		/*
+		 * This actually counts as an empty extent as
+		 * c_clusters == 0
+		 */
+		eb_el->l_recs[0].e_cpos = cpu_to_le32(new_cpos);
 		eb_el->l_recs[0].e_blkno = cpu_to_le64(next_blkno);
-		eb_el->l_recs[0].e_clusters = cpu_to_le32(0);
+		/*
+		 * eb_el isn't always an interior node, but even leaf
+		 * nodes want a zero'd flags and reserved field so
+		 * this gets the whole 32 bits regardless of use.
+		 */
+		eb_el->l_recs[0].e_int_clusters = cpu_to_le32(0);
 		if (!eb_el->l_tree_depth)
 			new_last_eb_blk = le64_to_cpu(eb->h_blkno);
 
@@ -376,8 +593,8 @@
 	 * either be on the fe, or the extent block passed in. */
 	i = le16_to_cpu(el->l_next_free_rec);
 	el->l_recs[i].e_blkno = cpu_to_le64(next_blkno);
-	el->l_recs[i].e_cpos = fe->i_clusters;
-	el->l_recs[i].e_clusters = 0;
+	el->l_recs[i].e_cpos = cpu_to_le32(new_cpos);
+	el->l_recs[i].e_int_clusters = 0;
 	le16_add_cpu(&el->l_next_free_rec, 1);
 
 	/* fe needs a new last extent block pointer, as does the
@@ -425,6 +642,7 @@
 				  struct buffer_head **ret_new_eb_bh)
 {
 	int status, i;
+	u32 new_clusters;
 	struct buffer_head *new_eb_bh = NULL;
 	struct ocfs2_dinode *fe;
 	struct ocfs2_extent_block *eb;
@@ -461,11 +679,8 @@
 	/* copy the fe data into the new extent block */
 	eb_el->l_tree_depth = fe_el->l_tree_depth;
 	eb_el->l_next_free_rec = fe_el->l_next_free_rec;
-	for(i = 0; i < le16_to_cpu(fe_el->l_next_free_rec); i++) {
-		eb_el->l_recs[i].e_cpos = fe_el->l_recs[i].e_cpos;
-		eb_el->l_recs[i].e_clusters = fe_el->l_recs[i].e_clusters;
-		eb_el->l_recs[i].e_blkno = fe_el->l_recs[i].e_blkno;
-	}
+	for(i = 0; i < le16_to_cpu(fe_el->l_next_free_rec); i++)
+		eb_el->l_recs[i] = fe_el->l_recs[i];
 
 	status = ocfs2_journal_dirty(handle, new_eb_bh);
 	if (status < 0) {
@@ -480,16 +695,15 @@
 		goto bail;
 	}
 
+	new_clusters = ocfs2_sum_rightmost_rec(eb_el);
+
 	/* update fe now */
 	le16_add_cpu(&fe_el->l_tree_depth, 1);
 	fe_el->l_recs[0].e_cpos = 0;
 	fe_el->l_recs[0].e_blkno = eb->h_blkno;
-	fe_el->l_recs[0].e_clusters = fe->i_clusters;
-	for(i = 1; i < le16_to_cpu(fe_el->l_next_free_rec); i++) {
-		fe_el->l_recs[i].e_cpos = 0;
-		fe_el->l_recs[i].e_clusters = 0;
-		fe_el->l_recs[i].e_blkno = 0;
-	}
+	fe_el->l_recs[0].e_int_clusters = cpu_to_le32(new_clusters);
+	for(i = 1; i < le16_to_cpu(fe_el->l_next_free_rec); i++)
+		memset(&fe_el->l_recs[i], 0, sizeof(struct ocfs2_extent_rec));
 	fe_el->l_next_free_rec = cpu_to_le16(1);
 
 	/* If this is our 1st tree depth shift, then last_eb_blk
@@ -515,199 +729,6 @@
 }
 
 /*
- * Expects the tree to already have room in the rightmost leaf for the
- * extent.  Updates all the extent blocks (and the dinode) on the way
- * down.
- */
-static int ocfs2_do_insert_extent(struct ocfs2_super *osb,
-				  handle_t *handle,
-				  struct inode *inode,
-				  struct buffer_head *fe_bh,
-				  u64 start_blk,
-				  u32 new_clusters)
-{
-	int status, i, num_bhs = 0;
-	u64 next_blkno;
-	u16 next_free;
-	struct buffer_head **eb_bhs = NULL;
-	struct ocfs2_dinode *fe;
-	struct ocfs2_extent_block *eb;
-	struct ocfs2_extent_list  *el;
-
-	mlog_entry_void();
-
-	status = ocfs2_journal_access(handle, inode, fe_bh,
-				      OCFS2_JOURNAL_ACCESS_WRITE);
-	if (status < 0) {
-		mlog_errno(status);
-		goto bail;
-	}
-
-	fe = (struct ocfs2_dinode *) fe_bh->b_data;
-	el = &fe->id2.i_list;
-	if (el->l_tree_depth) {
-		/* This is another operation where we want to be
-		 * careful about our tree updates. An error here means
-		 * none of the previous changes we made should roll
-		 * forward. As a result, we have to record the buffers
-		 * for this part of the tree in an array and reserve a
-		 * journal write to them before making any changes. */
-		num_bhs = le16_to_cpu(fe->id2.i_list.l_tree_depth);
-		eb_bhs = kcalloc(num_bhs, sizeof(struct buffer_head *),
-				 GFP_KERNEL);
-		if (!eb_bhs) {
-			status = -ENOMEM;
-			mlog_errno(status);
-			goto bail;
-		}
-
-		i = 0;
-		while(el->l_tree_depth) {
-			next_free = le16_to_cpu(el->l_next_free_rec);
-			if (next_free == 0) {
-				ocfs2_error(inode->i_sb,
-					    "Dinode %llu has a bad extent list",
-					    (unsigned long long)OCFS2_I(inode)->ip_blkno);
-				status = -EIO;
-				goto bail;
-			}
-			next_blkno = le64_to_cpu(el->l_recs[next_free - 1].e_blkno);
-
-			BUG_ON(i >= num_bhs);
-			status = ocfs2_read_block(osb, next_blkno, &eb_bhs[i],
-						  OCFS2_BH_CACHED, inode);
-			if (status < 0) {
-				mlog_errno(status);
-				goto bail;
-			}
-			eb = (struct ocfs2_extent_block *) eb_bhs[i]->b_data;
-			if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) {
-				OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb,
-								 eb);
-				status = -EIO;
-				goto bail;
-			}
-
-			status = ocfs2_journal_access(handle, inode, eb_bhs[i],
-						      OCFS2_JOURNAL_ACCESS_WRITE);
-			if (status < 0) {
-				mlog_errno(status);
-				goto bail;
-			}
-
-			el = &eb->h_list;
-			i++;
-			/* When we leave this loop, eb_bhs[num_bhs - 1] will
-			 * hold the bottom-most leaf extent block. */
-		}
-		BUG_ON(el->l_tree_depth);
-
-		el = &fe->id2.i_list;
-		/* If we have tree depth, then the fe update is
-		 * trivial, and we want to switch el out for the
-		 * bottom-most leaf in order to update it with the
-		 * actual extent data below. */
-		next_free = le16_to_cpu(el->l_next_free_rec);
-		if (next_free == 0) {
-			ocfs2_error(inode->i_sb,
-				    "Dinode %llu has a bad extent list",
-				    (unsigned long long)OCFS2_I(inode)->ip_blkno);
-			status = -EIO;
-			goto bail;
-		}
-		le32_add_cpu(&el->l_recs[next_free - 1].e_clusters,
-			     new_clusters);
-		/* (num_bhs - 1) to avoid the leaf */
-		for(i = 0; i < (num_bhs - 1); i++) {
-			eb = (struct ocfs2_extent_block *) eb_bhs[i]->b_data;
-			el = &eb->h_list;
-
-			/* finally, make our actual change to the
-			 * intermediate extent blocks. */
-			next_free = le16_to_cpu(el->l_next_free_rec);
-			le32_add_cpu(&el->l_recs[next_free - 1].e_clusters,
-				     new_clusters);
-
-			status = ocfs2_journal_dirty(handle, eb_bhs[i]);
-			if (status < 0)
-				mlog_errno(status);
-		}
-		BUG_ON(i != (num_bhs - 1));
-		/* note that the leaf block wasn't touched in
-		 * the loop above */
-		eb = (struct ocfs2_extent_block *) eb_bhs[num_bhs - 1]->b_data;
-		el = &eb->h_list;
-		BUG_ON(el->l_tree_depth);
-	}
-
-	/* yay, we can finally add the actual extent now! */
-	i = le16_to_cpu(el->l_next_free_rec) - 1;
-	if (le16_to_cpu(el->l_next_free_rec) &&
-	    ocfs2_extent_contig(inode, &el->l_recs[i], start_blk)) {
-		le32_add_cpu(&el->l_recs[i].e_clusters, new_clusters);
-	} else if (le16_to_cpu(el->l_next_free_rec) &&
-		   (le32_to_cpu(el->l_recs[i].e_clusters) == 0)) {
-		/* having an empty extent at eof is legal. */
-		if (el->l_recs[i].e_cpos != fe->i_clusters) {
-			ocfs2_error(inode->i_sb,
-				    "Dinode %llu trailing extent is bad: "
-				    "cpos (%u) != number of clusters (%u)",
-				    (unsigned long long)OCFS2_I(inode)->ip_blkno,
-				    le32_to_cpu(el->l_recs[i].e_cpos),
-				    le32_to_cpu(fe->i_clusters));
-			status = -EIO;
-			goto bail;
-		}
-		el->l_recs[i].e_blkno = cpu_to_le64(start_blk);
-		el->l_recs[i].e_clusters = cpu_to_le32(new_clusters);
-	} else {
-		/* No contiguous record, or no empty record at eof, so
-		 * we add a new one. */
-
-		BUG_ON(le16_to_cpu(el->l_next_free_rec) >=
-		       le16_to_cpu(el->l_count));
-		i = le16_to_cpu(el->l_next_free_rec);
-
-		el->l_recs[i].e_blkno = cpu_to_le64(start_blk);
-		el->l_recs[i].e_clusters = cpu_to_le32(new_clusters);
-		el->l_recs[i].e_cpos = fe->i_clusters;
-		le16_add_cpu(&el->l_next_free_rec, 1);
-	}
-
-	/*
-	 * extent_map errors are not fatal, so they are ignored outside
-	 * of flushing the thing.
-	 */
-	status = ocfs2_extent_map_append(inode, &el->l_recs[i],
-					 new_clusters);
-	if (status) {
-		mlog_errno(status);
-		ocfs2_extent_map_drop(inode, le32_to_cpu(fe->i_clusters));
-	}
-
-	status = ocfs2_journal_dirty(handle, fe_bh);
-	if (status < 0)
-		mlog_errno(status);
-	if (fe->id2.i_list.l_tree_depth) {
-		status = ocfs2_journal_dirty(handle, eb_bhs[num_bhs - 1]);
-		if (status < 0)
-			mlog_errno(status);
-	}
-
-	status = 0;
-bail:
-	if (eb_bhs) {
-		for (i = 0; i < num_bhs; i++)
-			if (eb_bhs[i])
-				brelse(eb_bhs[i]);
-		kfree(eb_bhs);
-	}
-
-	mlog_exit(status);
-	return status;
-}
-
-/*
  * Should only be called when there is no space left in any of the
  * leaf nodes. What we want to do is find the lowest tree depth
  * non-leaf extent block with room for new records. There are three
@@ -807,53 +828,1548 @@
 	return status;
 }
 
-/* the caller needs to update fe->i_clusters */
+/*
+ * This is only valid for leaf nodes, which are the only ones that can
+ * have empty extents anyway.
+ */
+static inline int ocfs2_is_empty_extent(struct ocfs2_extent_rec *rec)
+{
+	return !rec->e_leaf_clusters;
+}
+
+/*
+ * This function will discard the rightmost extent record.
+ */
+static void ocfs2_shift_records_right(struct ocfs2_extent_list *el)
+{
+	int next_free = le16_to_cpu(el->l_next_free_rec);
+	int count = le16_to_cpu(el->l_count);
+	unsigned int num_bytes;
+
+	BUG_ON(!next_free);
+	/* This will cause us to go off the end of our extent list. */
+	BUG_ON(next_free >= count);
+
+	num_bytes = sizeof(struct ocfs2_extent_rec) * next_free;
+
+	memmove(&el->l_recs[1], &el->l_recs[0], num_bytes);
+}
+
+static void ocfs2_rotate_leaf(struct ocfs2_extent_list *el,
+			      struct ocfs2_extent_rec *insert_rec)
+{
+	int i, insert_index, next_free, has_empty, num_bytes;
+	u32 insert_cpos = le32_to_cpu(insert_rec->e_cpos);
+	struct ocfs2_extent_rec *rec;
+
+	next_free = le16_to_cpu(el->l_next_free_rec);
+	has_empty = ocfs2_is_empty_extent(&el->l_recs[0]);
+
+	BUG_ON(!next_free);
+
+	/* The tree code before us didn't allow enough room in the leaf. */
+	if (el->l_next_free_rec == el->l_count && !has_empty)
+		BUG();
+
+	/*
+	 * The easiest way to approach this is to just remove the
+	 * empty extent and temporarily decrement next_free.
+	 */
+	if (has_empty) {
+		/*
+		 * If next_free was 1 (only an empty extent), this
+		 * loop won't execute, which is fine. We still want
+		 * the decrement above to happen.
+		 */
+		for(i = 0; i < (next_free - 1); i++)
+			el->l_recs[i] = el->l_recs[i+1];
+
+		next_free--;
+	}
+
+	/*
+	 * Figure out what the new record index should be.
+	 */
+	for(i = 0; i < next_free; i++) {
+		rec = &el->l_recs[i];
+
+		if (insert_cpos < le32_to_cpu(rec->e_cpos))
+			break;
+	}
+	insert_index = i;
+
+	mlog(0, "ins %u: index %d, has_empty %d, next_free %d, count %d\n",
+	     insert_cpos, insert_index, has_empty, next_free, le16_to_cpu(el->l_count));
+
+	BUG_ON(insert_index < 0);
+	BUG_ON(insert_index >= le16_to_cpu(el->l_count));
+	BUG_ON(insert_index > next_free);
+
+	/*
+	 * No need to memmove if we're just adding to the tail.
+	 */
+	if (insert_index != next_free) {
+		BUG_ON(next_free >= le16_to_cpu(el->l_count));
+
+		num_bytes = next_free - insert_index;
+		num_bytes *= sizeof(struct ocfs2_extent_rec);
+		memmove(&el->l_recs[insert_index + 1],
+			&el->l_recs[insert_index],
+			num_bytes);
+	}
+
+	/*
+	 * Either we had an empty extent, and need to re-increment or
+	 * there was no empty extent on a non full rightmost leaf node,
+	 * in which case we still need to increment.
+	 */
+	next_free++;
+	el->l_next_free_rec = cpu_to_le16(next_free);
+	/*
+	 * Make sure none of the math above just messed up our tree.
+	 */
+	BUG_ON(le16_to_cpu(el->l_next_free_rec) > le16_to_cpu(el->l_count));
+
+	el->l_recs[insert_index] = *insert_rec;
+
+}
+
+/*
+ * Create an empty extent record .
+ *
+ * l_next_free_rec may be updated.
+ *
+ * If an empty extent already exists do nothing.
+ */
+static void ocfs2_create_empty_extent(struct ocfs2_extent_list *el)
+{
+	int next_free = le16_to_cpu(el->l_next_free_rec);
+
+	BUG_ON(le16_to_cpu(el->l_tree_depth) != 0);
+
+	if (next_free == 0)
+		goto set_and_inc;
+
+	if (ocfs2_is_empty_extent(&el->l_recs[0]))
+		return;
+
+	mlog_bug_on_msg(el->l_count == el->l_next_free_rec,
+			"Asked to create an empty extent in a full list:\n"
+			"count = %u, tree depth = %u",
+			le16_to_cpu(el->l_count),
+			le16_to_cpu(el->l_tree_depth));
+
+	ocfs2_shift_records_right(el);
+
+set_and_inc:
+	le16_add_cpu(&el->l_next_free_rec, 1);
+	memset(&el->l_recs[0], 0, sizeof(struct ocfs2_extent_rec));
+}
+
+/*
+ * For a rotation which involves two leaf nodes, the "root node" is
+ * the lowest level tree node which contains a path to both leafs. This
+ * resulting set of information can be used to form a complete "subtree"
+ *
+ * This function is passed two full paths from the dinode down to a
+ * pair of adjacent leaves. It's task is to figure out which path
+ * index contains the subtree root - this can be the root index itself
+ * in a worst-case rotation.
+ *
+ * The array index of the subtree root is passed back.
+ */
+static int ocfs2_find_subtree_root(struct inode *inode,
+				   struct ocfs2_path *left,
+				   struct ocfs2_path *right)
+{
+	int i = 0;
+
+	/*
+	 * Check that the caller passed in two paths from the same tree.
+	 */
+	BUG_ON(path_root_bh(left) != path_root_bh(right));
+
+	do {
+		i++;
+
+		/*
+		 * The caller didn't pass two adjacent paths.
+		 */
+		mlog_bug_on_msg(i > left->p_tree_depth,
+				"Inode %lu, left depth %u, right depth %u\n"
+				"left leaf blk %llu, right leaf blk %llu\n",
+				inode->i_ino, left->p_tree_depth,
+				right->p_tree_depth,
+				(unsigned long long)path_leaf_bh(left)->b_blocknr,
+				(unsigned long long)path_leaf_bh(right)->b_blocknr);
+	} while (left->p_node[i].bh->b_blocknr ==
+		 right->p_node[i].bh->b_blocknr);
+
+	return i - 1;
+}
+
+typedef void (path_insert_t)(void *, struct buffer_head *);
+
+/*
+ * Traverse a btree path in search of cpos, starting at root_el.
+ *
+ * This code can be called with a cpos larger than the tree, in which
+ * case it will return the rightmost path.
+ */
+static int __ocfs2_find_path(struct inode *inode,
+			     struct ocfs2_extent_list *root_el, u32 cpos,
+			     path_insert_t *func, void *data)
+{
+	int i, ret = 0;
+	u32 range;
+	u64 blkno;
+	struct buffer_head *bh = NULL;
+	struct ocfs2_extent_block *eb;
+	struct ocfs2_extent_list *el;
+	struct ocfs2_extent_rec *rec;
+	struct ocfs2_inode_info *oi = OCFS2_I(inode);
+
+	el = root_el;
+	while (el->l_tree_depth) {
+		if (le16_to_cpu(el->l_next_free_rec) == 0) {
+			ocfs2_error(inode->i_sb,
+				    "Inode %llu has empty extent list at "
+				    "depth %u\n",
+				    (unsigned long long)oi->ip_blkno,
+				    le16_to_cpu(el->l_tree_depth));
+			ret = -EROFS;
+			goto out;
+
+		}
+
+		for(i = 0; i < le16_to_cpu(el->l_next_free_rec) - 1; i++) {
+			rec = &el->l_recs[i];
+
+			/*
+			 * In the case that cpos is off the allocation
+			 * tree, this should just wind up returning the
+			 * rightmost record.
+			 */
+			range = le32_to_cpu(rec->e_cpos) +
+				ocfs2_rec_clusters(el, rec);
+			if (cpos >= le32_to_cpu(rec->e_cpos) && cpos < range)
+			    break;
+		}
+
+		blkno = le64_to_cpu(el->l_recs[i].e_blkno);
+		if (blkno == 0) {
+			ocfs2_error(inode->i_sb,
+				    "Inode %llu has bad blkno in extent list "
+				    "at depth %u (index %d)\n",
+				    (unsigned long long)oi->ip_blkno,
+				    le16_to_cpu(el->l_tree_depth), i);
+			ret = -EROFS;
+			goto out;
+		}
+
+		brelse(bh);
+		bh = NULL;
+		ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), blkno,
+				       &bh, OCFS2_BH_CACHED, inode);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		eb = (struct ocfs2_extent_block *) bh->b_data;
+		el = &eb->h_list;
+		if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) {
+			OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb, eb);
+			ret = -EIO;
+			goto out;
+		}
+
+		if (le16_to_cpu(el->l_next_free_rec) >
+		    le16_to_cpu(el->l_count)) {
+			ocfs2_error(inode->i_sb,
+				    "Inode %llu has bad count in extent list "
+				    "at block %llu (next free=%u, count=%u)\n",
+				    (unsigned long long)oi->ip_blkno,
+				    (unsigned long long)bh->b_blocknr,
+				    le16_to_cpu(el->l_next_free_rec),
+				    le16_to_cpu(el->l_count));
+			ret = -EROFS;
+			goto out;
+		}
+
+		if (func)
+			func(data, bh);
+	}
+
+out:
+	/*
+	 * Catch any trailing bh that the loop didn't handle.
+	 */
+	brelse(bh);
+
+	return ret;
+}
+
+/*
+ * Given an initialized path (that is, it has a valid root extent
+ * list), this function will traverse the btree in search of the path
+ * which would contain cpos.
+ *
+ * The path traveled is recorded in the path structure.
+ *
+ * Note that this will not do any comparisons on leaf node extent
+ * records, so it will work fine in the case that we just added a tree
+ * branch.
+ */
+struct find_path_data {
+	int index;
+	struct ocfs2_path *path;
+};
+static void find_path_ins(void *data, struct buffer_head *bh)
+{
+	struct find_path_data *fp = data;
+
+	get_bh(bh);
+	ocfs2_path_insert_eb(fp->path, fp->index, bh);
+	fp->index++;
+}
+static int ocfs2_find_path(struct inode *inode, struct ocfs2_path *path,
+			   u32 cpos)
+{
+	struct find_path_data data;
+
+	data.index = 1;
+	data.path = path;
+	return __ocfs2_find_path(inode, path_root_el(path), cpos,
+				 find_path_ins, &data);
+}
+
+static void find_leaf_ins(void *data, struct buffer_head *bh)
+{
+	struct ocfs2_extent_block *eb =(struct ocfs2_extent_block *)bh->b_data;
+	struct ocfs2_extent_list *el = &eb->h_list;
+	struct buffer_head **ret = data;
+
+	/* We want to retain only the leaf block. */
+	if (le16_to_cpu(el->l_tree_depth) == 0) {
+		get_bh(bh);
+		*ret = bh;
+	}
+}
+/*
+ * Find the leaf block in the tree which would contain cpos. No
+ * checking of the actual leaf is done.
+ *
+ * Some paths want to call this instead of allocating a path structure
+ * and calling ocfs2_find_path().
+ *
+ * This function doesn't handle non btree extent lists.
+ */
+int ocfs2_find_leaf(struct inode *inode, struct ocfs2_extent_list *root_el,
+		    u32 cpos, struct buffer_head **leaf_bh)
+{
+	int ret;
+	struct buffer_head *bh = NULL;
+
+	ret = __ocfs2_find_path(inode, root_el, cpos, find_leaf_ins, &bh);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	*leaf_bh = bh;
+out:
+	return ret;
+}
+
+/*
+ * Adjust the adjacent records (left_rec, right_rec) involved in a rotation.
+ *
+ * Basically, we've moved stuff around at the bottom of the tree and
+ * we need to fix up the extent records above the changes to reflect
+ * the new changes.
+ *
+ * left_rec: the record on the left.
+ * left_child_el: is the child list pointed to by left_rec
+ * right_rec: the record to the right of left_rec
+ * right_child_el: is the child list pointed to by right_rec
+ *
+ * By definition, this only works on interior nodes.
+ */
+static void ocfs2_adjust_adjacent_records(struct ocfs2_extent_rec *left_rec,
+				  struct ocfs2_extent_list *left_child_el,
+				  struct ocfs2_extent_rec *right_rec,
+				  struct ocfs2_extent_list *right_child_el)
+{
+	u32 left_clusters, right_end;
+
+	/*
+	 * Interior nodes never have holes. Their cpos is the cpos of
+	 * the leftmost record in their child list. Their cluster
+	 * count covers the full theoretical range of their child list
+	 * - the range between their cpos and the cpos of the record
+	 * immediately to their right.
+	 */
+	left_clusters = le32_to_cpu(right_child_el->l_recs[0].e_cpos);
+	left_clusters -= le32_to_cpu(left_rec->e_cpos);
+	left_rec->e_int_clusters = cpu_to_le32(left_clusters);
+
+	/*
+	 * Calculate the rightmost cluster count boundary before
+	 * moving cpos - we will need to adjust clusters after
+	 * updating e_cpos to keep the same highest cluster count.
+	 */
+	right_end = le32_to_cpu(right_rec->e_cpos);
+	right_end += le32_to_cpu(right_rec->e_int_clusters);
+
+	right_rec->e_cpos = left_rec->e_cpos;
+	le32_add_cpu(&right_rec->e_cpos, left_clusters);
+
+	right_end -= le32_to_cpu(right_rec->e_cpos);
+	right_rec->e_int_clusters = cpu_to_le32(right_end);
+}
+
+/*
+ * Adjust the adjacent root node records involved in a
+ * rotation. left_el_blkno is passed in as a key so that we can easily
+ * find it's index in the root list.
+ */
+static void ocfs2_adjust_root_records(struct ocfs2_extent_list *root_el,
+				      struct ocfs2_extent_list *left_el,
+				      struct ocfs2_extent_list *right_el,
+				      u64 left_el_blkno)
+{
+	int i;
+
+	BUG_ON(le16_to_cpu(root_el->l_tree_depth) <=
+	       le16_to_cpu(left_el->l_tree_depth));
+
+	for(i = 0; i < le16_to_cpu(root_el->l_next_free_rec) - 1; i++) {
+		if (le64_to_cpu(root_el->l_recs[i].e_blkno) == left_el_blkno)
+			break;
+	}
+
+	/*
+	 * The path walking code should have never returned a root and
+	 * two paths which are not adjacent.
+	 */
+	BUG_ON(i >= (le16_to_cpu(root_el->l_next_free_rec) - 1));
+
+	ocfs2_adjust_adjacent_records(&root_el->l_recs[i], left_el,
+				      &root_el->l_recs[i + 1], right_el);
+}
+
+/*
+ * We've changed a leaf block (in right_path) and need to reflect that
+ * change back up the subtree.
+ *
+ * This happens in multiple places:
+ *   - When we've moved an extent record from the left path leaf to the right
+ *     path leaf to make room for an empty extent in the left path leaf.
+ *   - When our insert into the right path leaf is at the leftmost edge
+ *     and requires an update of the path immediately to it's left. This
+ *     can occur at the end of some types of rotation and appending inserts.
+ */
+static void ocfs2_complete_edge_insert(struct inode *inode, handle_t *handle,
+				       struct ocfs2_path *left_path,
+				       struct ocfs2_path *right_path,
+				       int subtree_index)
+{
+	int ret, i, idx;
+	struct ocfs2_extent_list *el, *left_el, *right_el;
+	struct ocfs2_extent_rec *left_rec, *right_rec;
+	struct buffer_head *root_bh = left_path->p_node[subtree_index].bh;
+
+	/*
+	 * Update the counts and position values within all the
+	 * interior nodes to reflect the leaf rotation we just did.
+	 *
+	 * The root node is handled below the loop.
+	 *
+	 * We begin the loop with right_el and left_el pointing to the
+	 * leaf lists and work our way up.
+	 *
+	 * NOTE: within this loop, left_el and right_el always refer
+	 * to the *child* lists.
+	 */
+	left_el = path_leaf_el(left_path);
+	right_el = path_leaf_el(right_path);
+	for(i = left_path->p_tree_depth - 1; i > subtree_index; i--) {
+		mlog(0, "Adjust records at index %u\n", i);
+
+		/*
+		 * One nice property of knowing that all of these
+		 * nodes are below the root is that we only deal with
+		 * the leftmost right node record and the rightmost
+		 * left node record.
+		 */
+		el = left_path->p_node[i].el;
+		idx = le16_to_cpu(left_el->l_next_free_rec) - 1;
+		left_rec = &el->l_recs[idx];
+
+		el = right_path->p_node[i].el;
+		right_rec = &el->l_recs[0];
+
+		ocfs2_adjust_adjacent_records(left_rec, left_el, right_rec,
+					      right_el);
+
+		ret = ocfs2_journal_dirty(handle, left_path->p_node[i].bh);
+		if (ret)
+			mlog_errno(ret);
+
+		ret = ocfs2_journal_dirty(handle, right_path->p_node[i].bh);
+		if (ret)
+			mlog_errno(ret);
+
+		/*
+		 * Setup our list pointers now so that the current
+		 * parents become children in the next iteration.
+		 */
+		left_el = left_path->p_node[i].el;
+		right_el = right_path->p_node[i].el;
+	}
+
+	/*
+	 * At the root node, adjust the two adjacent records which
+	 * begin our path to the leaves.
+	 */
+
+	el = left_path->p_node[subtree_index].el;
+	left_el = left_path->p_node[subtree_index + 1].el;
+	right_el = right_path->p_node[subtree_index + 1].el;
+
+	ocfs2_adjust_root_records(el, left_el, right_el,
+				  left_path->p_node[subtree_index + 1].bh->b_blocknr);
+
+	root_bh = left_path->p_node[subtree_index].bh;
+
+	ret = ocfs2_journal_dirty(handle, root_bh);
+	if (ret)
+		mlog_errno(ret);
+}
+
+static int ocfs2_rotate_subtree_right(struct inode *inode,
+				      handle_t *handle,
+				      struct ocfs2_path *left_path,
+				      struct ocfs2_path *right_path,
+				      int subtree_index)
+{
+	int ret, i;
+	struct buffer_head *right_leaf_bh;
+	struct buffer_head *left_leaf_bh = NULL;
+	struct buffer_head *root_bh;
+	struct ocfs2_extent_list *right_el, *left_el;
+	struct ocfs2_extent_rec move_rec;
+
+	left_leaf_bh = path_leaf_bh(left_path);
+	left_el = path_leaf_el(left_path);
+
+	if (left_el->l_next_free_rec != left_el->l_count) {
+		ocfs2_error(inode->i_sb,
+			    "Inode %llu has non-full interior leaf node %llu"
+			    "(next free = %u)",
+			    (unsigned long long)OCFS2_I(inode)->ip_blkno,
+			    (unsigned long long)left_leaf_bh->b_blocknr,
+			    le16_to_cpu(left_el->l_next_free_rec));
+		return -EROFS;
+	}
+
+	/*
+	 * This extent block may already have an empty record, so we
+	 * return early if so.
+	 */
+	if (ocfs2_is_empty_extent(&left_el->l_recs[0]))
+		return 0;
+
+	root_bh = left_path->p_node[subtree_index].bh;
+	BUG_ON(root_bh != right_path->p_node[subtree_index].bh);
+
+	ret = ocfs2_journal_access(handle, inode, root_bh,
+				   OCFS2_JOURNAL_ACCESS_WRITE);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	for(i = subtree_index + 1; i < path_num_items(right_path); i++) {
+		ret = ocfs2_journal_access(handle, inode,
+					   right_path->p_node[i].bh,
+					   OCFS2_JOURNAL_ACCESS_WRITE);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		ret = ocfs2_journal_access(handle, inode,
+					   left_path->p_node[i].bh,
+					   OCFS2_JOURNAL_ACCESS_WRITE);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+	}
+
+	right_leaf_bh = path_leaf_bh(right_path);
+	right_el = path_leaf_el(right_path);
+
+	/* This is a code error, not a disk corruption. */
+	mlog_bug_on_msg(!right_el->l_next_free_rec, "Inode %llu: Rotate fails "
+			"because rightmost leaf block %llu is empty\n",
+			(unsigned long long)OCFS2_I(inode)->ip_blkno,
+			(unsigned long long)right_leaf_bh->b_blocknr);
+
+	ocfs2_create_empty_extent(right_el);
+
+	ret = ocfs2_journal_dirty(handle, right_leaf_bh);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	/* Do the copy now. */
+	i = le16_to_cpu(left_el->l_next_free_rec) - 1;
+	move_rec = left_el->l_recs[i];
+	right_el->l_recs[0] = move_rec;
+
+	/*
+	 * Clear out the record we just copied and shift everything
+	 * over, leaving an empty extent in the left leaf.
+	 *
+	 * We temporarily subtract from next_free_rec so that the
+	 * shift will lose the tail record (which is now defunct).
+	 */
+	le16_add_cpu(&left_el->l_next_free_rec, -1);
+	ocfs2_shift_records_right(left_el);
+	memset(&left_el->l_recs[0], 0, sizeof(struct ocfs2_extent_rec));
+	le16_add_cpu(&left_el->l_next_free_rec, 1);
+
+	ret = ocfs2_journal_dirty(handle, left_leaf_bh);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	ocfs2_complete_edge_insert(inode, handle, left_path, right_path,
+				subtree_index);
+
+out:
+	return ret;
+}
+
+/*
+ * Given a full path, determine what cpos value would return us a path
+ * containing the leaf immediately to the left of the current one.
+ *
+ * Will return zero if the path passed in is already the leftmost path.
+ */
+static int ocfs2_find_cpos_for_left_leaf(struct super_block *sb,
+					 struct ocfs2_path *path, u32 *cpos)
+{
+	int i, j, ret = 0;
+	u64 blkno;
+	struct ocfs2_extent_list *el;
+
+	BUG_ON(path->p_tree_depth == 0);
+
+	*cpos = 0;
+
+	blkno = path_leaf_bh(path)->b_blocknr;
+
+	/* Start at the tree node just above the leaf and work our way up. */
+	i = path->p_tree_depth - 1;
+	while (i >= 0) {
+		el = path->p_node[i].el;
+
+		/*
+		 * Find the extent record just before the one in our
+		 * path.
+		 */
+		for(j = 0; j < le16_to_cpu(el->l_next_free_rec); j++) {
+			if (le64_to_cpu(el->l_recs[j].e_blkno) == blkno) {
+				if (j == 0) {
+					if (i == 0) {
+						/*
+						 * We've determined that the
+						 * path specified is already
+						 * the leftmost one - return a
+						 * cpos of zero.
+						 */
+						goto out;
+					}
+					/*
+					 * The leftmost record points to our
+					 * leaf - we need to travel up the
+					 * tree one level.
+					 */
+					goto next_node;
+				}
+
+				*cpos = le32_to_cpu(el->l_recs[j - 1].e_cpos);
+				*cpos = *cpos + ocfs2_rec_clusters(el,
+							   &el->l_recs[j - 1]);
+				*cpos = *cpos - 1;
+				goto out;
+			}
+		}
+
+		/*
+		 * If we got here, we never found a valid node where
+		 * the tree indicated one should be.
+		 */
+		ocfs2_error(sb,
+			    "Invalid extent tree at extent block %llu\n",
+			    (unsigned long long)blkno);
+		ret = -EROFS;
+		goto out;
+
+next_node:
+		blkno = path->p_node[i].bh->b_blocknr;
+		i--;
+	}
+
+out:
+	return ret;
+}
+
+static int ocfs2_extend_rotate_transaction(handle_t *handle, int subtree_depth,
+					   struct ocfs2_path *path)
+{
+	int credits = (path->p_tree_depth - subtree_depth) * 2 + 1;
+
+	if (handle->h_buffer_credits < credits)
+		return ocfs2_extend_trans(handle, credits);
+
+	return 0;
+}
+
+/*
+ * Trap the case where we're inserting into the theoretical range past
+ * the _actual_ left leaf range. Otherwise, we'll rotate a record
+ * whose cpos is less than ours into the right leaf.
+ *
+ * It's only necessary to look at the rightmost record of the left
+ * leaf because the logic that calls us should ensure that the
+ * theoretical ranges in the path components above the leaves are
+ * correct.
+ */
+static int ocfs2_rotate_requires_path_adjustment(struct ocfs2_path *left_path,
+						 u32 insert_cpos)
+{
+	struct ocfs2_extent_list *left_el;
+	struct ocfs2_extent_rec *rec;
+	int next_free;
+
+	left_el = path_leaf_el(left_path);
+	next_free = le16_to_cpu(left_el->l_next_free_rec);
+	rec = &left_el->l_recs[next_free - 1];
+
+	if (insert_cpos > le32_to_cpu(rec->e_cpos))
+		return 1;
+	return 0;
+}
+
+/*
+ * Rotate all the records in a btree right one record, starting at insert_cpos.
+ *
+ * The path to the rightmost leaf should be passed in.
+ *
+ * The array is assumed to be large enough to hold an entire path (tree depth).
+ *
+ * Upon succesful return from this function:
+ *
+ * - The 'right_path' array will contain a path to the leaf block
+ *   whose range contains e_cpos.
+ * - That leaf block will have a single empty extent in list index 0.
+ * - In the case that the rotation requires a post-insert update,
+ *   *ret_left_path will contain a valid path which can be passed to
+ *   ocfs2_insert_path().
+ */
+static int ocfs2_rotate_tree_right(struct inode *inode,
+				   handle_t *handle,
+				   u32 insert_cpos,
+				   struct ocfs2_path *right_path,
+				   struct ocfs2_path **ret_left_path)
+{
+	int ret, start;
+	u32 cpos;
+	struct ocfs2_path *left_path = NULL;
+
+	*ret_left_path = NULL;
+
+	left_path = ocfs2_new_path(path_root_bh(right_path),
+				   path_root_el(right_path));
+	if (!left_path) {
+		ret = -ENOMEM;
+		mlog_errno(ret);
+		goto out;
+	}
+
+	ret = ocfs2_find_cpos_for_left_leaf(inode->i_sb, right_path, &cpos);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	mlog(0, "Insert: %u, first left path cpos: %u\n", insert_cpos, cpos);
+
+	/*
+	 * What we want to do here is:
+	 *
+	 * 1) Start with the rightmost path.
+	 *
+	 * 2) Determine a path to the leaf block directly to the left
+	 *    of that leaf.
+	 *
+	 * 3) Determine the 'subtree root' - the lowest level tree node
+	 *    which contains a path to both leaves.
+	 *
+	 * 4) Rotate the subtree.
+	 *
+	 * 5) Find the next subtree by considering the left path to be
+	 *    the new right path.
+	 *
+	 * The check at the top of this while loop also accepts
+	 * insert_cpos == cpos because cpos is only a _theoretical_
+	 * value to get us the left path - insert_cpos might very well
+	 * be filling that hole.
+	 *
+	 * Stop at a cpos of '0' because we either started at the
+	 * leftmost branch (i.e., a tree with one branch and a
+	 * rotation inside of it), or we've gone as far as we can in
+	 * rotating subtrees.
+	 */
+	while (cpos && insert_cpos <= cpos) {
+		mlog(0, "Rotating a tree: ins. cpos: %u, left path cpos: %u\n",
+		     insert_cpos, cpos);
+
+		ret = ocfs2_find_path(inode, left_path, cpos);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		mlog_bug_on_msg(path_leaf_bh(left_path) ==
+				path_leaf_bh(right_path),
+				"Inode %lu: error during insert of %u "
+				"(left path cpos %u) results in two identical "
+				"paths ending at %llu\n",
+				inode->i_ino, insert_cpos, cpos,
+				(unsigned long long)
+				path_leaf_bh(left_path)->b_blocknr);
+
+		if (ocfs2_rotate_requires_path_adjustment(left_path,
+							  insert_cpos)) {
+			mlog(0, "Path adjustment required\n");
+
+			/*
+			 * We've rotated the tree as much as we
+			 * should. The rest is up to
+			 * ocfs2_insert_path() to complete, after the
+			 * record insertion. We indicate this
+			 * situation by returning the left path.
+			 *
+			 * The reason we don't adjust the records here
+			 * before the record insert is that an error
+			 * later might break the rule where a parent
+			 * record e_cpos will reflect the actual
+			 * e_cpos of the 1st nonempty record of the
+			 * child list.
+			 */
+			*ret_left_path = left_path;
+			goto out_ret_path;
+		}
+
+		start = ocfs2_find_subtree_root(inode, left_path, right_path);
+
+		mlog(0, "Subtree root at index %d (blk %llu, depth %d)\n",
+		     start,
+		     (unsigned long long) right_path->p_node[start].bh->b_blocknr,
+		     right_path->p_tree_depth);
+
+		ret = ocfs2_extend_rotate_transaction(handle, start,
+						      right_path);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		ret = ocfs2_rotate_subtree_right(inode, handle, left_path,
+						 right_path, start);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		/*
+		 * There is no need to re-read the next right path
+		 * as we know that it'll be our current left
+		 * path. Optimize by copying values instead.
+		 */
+		ocfs2_mv_path(right_path, left_path);
+
+		ret = ocfs2_find_cpos_for_left_leaf(inode->i_sb, right_path,
+						    &cpos);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+	}
+
+out:
+	ocfs2_free_path(left_path);
+
+out_ret_path:
+	return ret;
+}
+
+/*
+ * Do the final bits of extent record insertion at the target leaf
+ * list. If this leaf is part of an allocation tree, it is assumed
+ * that the tree above has been prepared.
+ */
+static void ocfs2_insert_at_leaf(struct ocfs2_extent_rec *insert_rec,
+				 struct ocfs2_extent_list *el,
+				 struct ocfs2_insert_type *insert,
+				 struct inode *inode)
+{
+	int i = insert->ins_contig_index;
+	unsigned int range;
+	struct ocfs2_extent_rec *rec;
+
+	BUG_ON(le16_to_cpu(el->l_tree_depth) != 0);
+
+	/*
+	 * Contiguous insert - either left or right.
+	 */
+	if (insert->ins_contig != CONTIG_NONE) {
+		rec = &el->l_recs[i];
+		if (insert->ins_contig == CONTIG_LEFT) {
+			rec->e_blkno = insert_rec->e_blkno;
+			rec->e_cpos = insert_rec->e_cpos;
+		}
+		le16_add_cpu(&rec->e_leaf_clusters,
+			     le16_to_cpu(insert_rec->e_leaf_clusters));
+		return;
+	}
+
+	/*
+	 * Handle insert into an empty leaf.
+	 */
+	if (le16_to_cpu(el->l_next_free_rec) == 0 ||
+	    ((le16_to_cpu(el->l_next_free_rec) == 1) &&
+	     ocfs2_is_empty_extent(&el->l_recs[0]))) {
+		el->l_recs[0] = *insert_rec;
+		el->l_next_free_rec = cpu_to_le16(1);
+		return;
+	}
+
+	/*
+	 * Appending insert.
+	 */
+	if (insert->ins_appending == APPEND_TAIL) {
+		i = le16_to_cpu(el->l_next_free_rec) - 1;
+		rec = &el->l_recs[i];
+		range = le32_to_cpu(rec->e_cpos)
+			+ le16_to_cpu(rec->e_leaf_clusters);
+		BUG_ON(le32_to_cpu(insert_rec->e_cpos) < range);
+
+		mlog_bug_on_msg(le16_to_cpu(el->l_next_free_rec) >=
+				le16_to_cpu(el->l_count),
+				"inode %lu, depth %u, count %u, next free %u, "
+				"rec.cpos %u, rec.clusters %u, "
+				"insert.cpos %u, insert.clusters %u\n",
+				inode->i_ino,
+				le16_to_cpu(el->l_tree_depth),
+				le16_to_cpu(el->l_count),
+				le16_to_cpu(el->l_next_free_rec),
+				le32_to_cpu(el->l_recs[i].e_cpos),
+				le16_to_cpu(el->l_recs[i].e_leaf_clusters),
+				le32_to_cpu(insert_rec->e_cpos),
+				le16_to_cpu(insert_rec->e_leaf_clusters));
+		i++;
+		el->l_recs[i] = *insert_rec;
+		le16_add_cpu(&el->l_next_free_rec, 1);
+		return;
+	}
+
+	/*
+	 * Ok, we have to rotate.
+	 *
+	 * At this point, it is safe to assume that inserting into an
+	 * empty leaf and appending to a leaf have both been handled
+	 * above.
+	 *
+	 * This leaf needs to have space, either by the empty 1st
+	 * extent record, or by virtue of an l_next_rec < l_count.
+	 */
+	ocfs2_rotate_leaf(el, insert_rec);
+}
+
+static inline void ocfs2_update_dinode_clusters(struct inode *inode,
+						struct ocfs2_dinode *di,
+						u32 clusters)
+{
+	le32_add_cpu(&di->i_clusters, clusters);
+	spin_lock(&OCFS2_I(inode)->ip_lock);
+	OCFS2_I(inode)->ip_clusters = le32_to_cpu(di->i_clusters);
+	spin_unlock(&OCFS2_I(inode)->ip_lock);
+}
+
+static int ocfs2_append_rec_to_path(struct inode *inode, handle_t *handle,
+				    struct ocfs2_extent_rec *insert_rec,
+				    struct ocfs2_path *right_path,
+				    struct ocfs2_path **ret_left_path)
+{
+	int ret, i, next_free;
+	struct buffer_head *bh;
+	struct ocfs2_extent_list *el;
+	struct ocfs2_path *left_path = NULL;
+
+	*ret_left_path = NULL;
+
+	/*
+	 * This shouldn't happen for non-trees. The extent rec cluster
+	 * count manipulation below only works for interior nodes.
+	 */
+	BUG_ON(right_path->p_tree_depth == 0);
+
+	/*
+	 * If our appending insert is at the leftmost edge of a leaf,
+	 * then we might need to update the rightmost records of the
+	 * neighboring path.
+	 */
+	el = path_leaf_el(right_path);
+	next_free = le16_to_cpu(el->l_next_free_rec);
+	if (next_free == 0 ||
+	    (next_free == 1 && ocfs2_is_empty_extent(&el->l_recs[0]))) {
+		u32 left_cpos;
+
+		ret = ocfs2_find_cpos_for_left_leaf(inode->i_sb, right_path,
+						    &left_cpos);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		mlog(0, "Append may need a left path update. cpos: %u, "
+		     "left_cpos: %u\n", le32_to_cpu(insert_rec->e_cpos),
+		     left_cpos);
+
+		/*
+		 * No need to worry if the append is already in the
+		 * leftmost leaf.
+		 */
+		if (left_cpos) {
+			left_path = ocfs2_new_path(path_root_bh(right_path),
+						   path_root_el(right_path));
+			if (!left_path) {
+				ret = -ENOMEM;
+				mlog_errno(ret);
+				goto out;
+			}
+
+			ret = ocfs2_find_path(inode, left_path, left_cpos);
+			if (ret) {
+				mlog_errno(ret);
+				goto out;
+			}
+
+			/*
+			 * ocfs2_insert_path() will pass the left_path to the
+			 * journal for us.
+			 */
+		}
+	}
+
+	ret = ocfs2_journal_access_path(inode, handle, right_path);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	el = path_root_el(right_path);
+	bh = path_root_bh(right_path);
+	i = 0;
+	while (1) {
+		struct ocfs2_extent_rec *rec;
+
+		next_free = le16_to_cpu(el->l_next_free_rec);
+		if (next_free == 0) {
+			ocfs2_error(inode->i_sb,
+				    "Dinode %llu has a bad extent list",
+				    (unsigned long long)OCFS2_I(inode)->ip_blkno);
+			ret = -EIO;
+			goto out;
+		}
+
+		rec = &el->l_recs[next_free - 1];
+
+		rec->e_int_clusters = insert_rec->e_cpos;
+		le32_add_cpu(&rec->e_int_clusters,
+			     le16_to_cpu(insert_rec->e_leaf_clusters));
+		le32_add_cpu(&rec->e_int_clusters,
+			     -le32_to_cpu(rec->e_cpos));
+
+		ret = ocfs2_journal_dirty(handle, bh);
+		if (ret)
+			mlog_errno(ret);
+
+		/* Don't touch the leaf node */
+		if (++i >= right_path->p_tree_depth)
+			break;
+
+		bh = right_path->p_node[i].bh;
+		el = right_path->p_node[i].el;
+	}
+
+	*ret_left_path = left_path;
+	ret = 0;
+out:
+	if (ret != 0)
+		ocfs2_free_path(left_path);
+
+	return ret;
+}
+
+/*
+ * This function only does inserts on an allocation b-tree. For dinode
+ * lists, ocfs2_insert_at_leaf() is called directly.
+ *
+ * right_path is the path we want to do the actual insert
+ * in. left_path should only be passed in if we need to update that
+ * portion of the tree after an edge insert.
+ */
+static int ocfs2_insert_path(struct inode *inode,
+			     handle_t *handle,
+			     struct ocfs2_path *left_path,
+			     struct ocfs2_path *right_path,
+			     struct ocfs2_extent_rec *insert_rec,
+			     struct ocfs2_insert_type *insert)
+{
+	int ret, subtree_index;
+	struct buffer_head *leaf_bh = path_leaf_bh(right_path);
+	struct ocfs2_extent_list *el;
+
+	/*
+	 * Pass both paths to the journal. The majority of inserts
+	 * will be touching all components anyway.
+	 */
+	ret = ocfs2_journal_access_path(inode, handle, right_path);
+	if (ret < 0) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	if (left_path) {
+		int credits = handle->h_buffer_credits;
+
+		/*
+		 * There's a chance that left_path got passed back to
+		 * us without being accounted for in the
+		 * journal. Extend our transaction here to be sure we
+		 * can change those blocks.
+		 */
+		credits += left_path->p_tree_depth;
+
+		ret = ocfs2_extend_trans(handle, credits);
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		ret = ocfs2_journal_access_path(inode, handle, left_path);
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto out;
+		}
+	}
+
+	el = path_leaf_el(right_path);
+
+	ocfs2_insert_at_leaf(insert_rec, el, insert, inode);
+	ret = ocfs2_journal_dirty(handle, leaf_bh);
+	if (ret)
+		mlog_errno(ret);
+
+	if (left_path) {
+		/*
+		 * The rotate code has indicated that we need to fix
+		 * up portions of the tree after the insert.
+		 *
+		 * XXX: Should we extend the transaction here?
+		 */
+		subtree_index = ocfs2_find_subtree_root(inode, left_path,
+							right_path);
+		ocfs2_complete_edge_insert(inode, handle, left_path,
+					   right_path, subtree_index);
+	}
+
+	ret = 0;
+out:
+	return ret;
+}
+
+static int ocfs2_do_insert_extent(struct inode *inode,
+				  handle_t *handle,
+				  struct buffer_head *di_bh,
+				  struct ocfs2_extent_rec *insert_rec,
+				  struct ocfs2_insert_type *type)
+{
+	int ret, rotate = 0;
+	u32 cpos;
+	struct ocfs2_path *right_path = NULL;
+	struct ocfs2_path *left_path = NULL;
+	struct ocfs2_dinode *di;
+	struct ocfs2_extent_list *el;
+
+	di = (struct ocfs2_dinode *) di_bh->b_data;
+	el = &di->id2.i_list;
+
+	ret = ocfs2_journal_access(handle, inode, di_bh,
+				   OCFS2_JOURNAL_ACCESS_WRITE);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	if (le16_to_cpu(el->l_tree_depth) == 0) {
+		ocfs2_insert_at_leaf(insert_rec, el, type, inode);
+		goto out_update_clusters;
+	}
+
+	right_path = ocfs2_new_inode_path(di_bh);
+	if (!right_path) {
+		ret = -ENOMEM;
+		mlog_errno(ret);
+		goto out;
+	}
+
+	/*
+	 * Determine the path to start with. Rotations need the
+	 * rightmost path, everything else can go directly to the
+	 * target leaf.
+	 */
+	cpos = le32_to_cpu(insert_rec->e_cpos);
+	if (type->ins_appending == APPEND_NONE &&
+	    type->ins_contig == CONTIG_NONE) {
+		rotate = 1;
+		cpos = UINT_MAX;
+	}
+
+	ret = ocfs2_find_path(inode, right_path, cpos);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	/*
+	 * Rotations and appends need special treatment - they modify
+	 * parts of the tree's above them.
+	 *
+	 * Both might pass back a path immediate to the left of the
+	 * one being inserted to. This will be cause
+	 * ocfs2_insert_path() to modify the rightmost records of
+	 * left_path to account for an edge insert.
+	 *
+	 * XXX: When modifying this code, keep in mind that an insert
+	 * can wind up skipping both of these two special cases...
+	 */
+	if (rotate) {
+		ret = ocfs2_rotate_tree_right(inode, handle,
+					      le32_to_cpu(insert_rec->e_cpos),
+					      right_path, &left_path);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+	} else if (type->ins_appending == APPEND_TAIL
+		   && type->ins_contig != CONTIG_LEFT) {
+		ret = ocfs2_append_rec_to_path(inode, handle, insert_rec,
+					       right_path, &left_path);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+	}
+
+	ret = ocfs2_insert_path(inode, handle, left_path, right_path,
+				insert_rec, type);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+out_update_clusters:
+	ocfs2_update_dinode_clusters(inode, di,
+				     le16_to_cpu(insert_rec->e_leaf_clusters));
+
+	ret = ocfs2_journal_dirty(handle, di_bh);
+	if (ret)
+		mlog_errno(ret);
+
+out:
+	ocfs2_free_path(left_path);
+	ocfs2_free_path(right_path);
+
+	return ret;
+}
+
+static void ocfs2_figure_contig_type(struct inode *inode,
+				     struct ocfs2_insert_type *insert,
+				     struct ocfs2_extent_list *el,
+				     struct ocfs2_extent_rec *insert_rec)
+{
+	int i;
+	enum ocfs2_contig_type contig_type = CONTIG_NONE;
+
+	BUG_ON(le16_to_cpu(el->l_tree_depth) != 0);
+
+	for(i = 0; i < le16_to_cpu(el->l_next_free_rec); i++) {
+		contig_type = ocfs2_extent_contig(inode, &el->l_recs[i],
+						  insert_rec);
+		if (contig_type != CONTIG_NONE) {
+			insert->ins_contig_index = i;
+			break;
+		}
+	}
+	insert->ins_contig = contig_type;
+}
+
+/*
+ * This should only be called against the righmost leaf extent list.
+ *
+ * ocfs2_figure_appending_type() will figure out whether we'll have to
+ * insert at the tail of the rightmost leaf.
+ *
+ * This should also work against the dinode list for tree's with 0
+ * depth. If we consider the dinode list to be the rightmost leaf node
+ * then the logic here makes sense.
+ */
+static void ocfs2_figure_appending_type(struct ocfs2_insert_type *insert,
+					struct ocfs2_extent_list *el,
+					struct ocfs2_extent_rec *insert_rec)
+{
+	int i;
+	u32 cpos = le32_to_cpu(insert_rec->e_cpos);
+	struct ocfs2_extent_rec *rec;
+
+	insert->ins_appending = APPEND_NONE;
+
+	BUG_ON(le16_to_cpu(el->l_tree_depth) != 0);
+
+	if (!el->l_next_free_rec)
+		goto set_tail_append;
+
+	if (ocfs2_is_empty_extent(&el->l_recs[0])) {
+		/* Were all records empty? */
+		if (le16_to_cpu(el->l_next_free_rec) == 1)
+			goto set_tail_append;
+	}
+
+	i = le16_to_cpu(el->l_next_free_rec) - 1;
+	rec = &el->l_recs[i];
+
+	if (cpos >=
+	    (le32_to_cpu(rec->e_cpos) + le16_to_cpu(rec->e_leaf_clusters)))
+		goto set_tail_append;
+
+	return;
+
+set_tail_append:
+	insert->ins_appending = APPEND_TAIL;
+}
+
+/*
+ * Helper function called at the begining of an insert.
+ *
+ * This computes a few things that are commonly used in the process of
+ * inserting into the btree:
+ *   - Whether the new extent is contiguous with an existing one.
+ *   - The current tree depth.
+ *   - Whether the insert is an appending one.
+ *   - The total # of free records in the tree.
+ *
+ * All of the information is stored on the ocfs2_insert_type
+ * structure.
+ */
+static int ocfs2_figure_insert_type(struct inode *inode,
+				    struct buffer_head *di_bh,
+				    struct buffer_head **last_eb_bh,
+				    struct ocfs2_extent_rec *insert_rec,
+				    struct ocfs2_insert_type *insert)
+{
+	int ret;
+	struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
+	struct ocfs2_extent_block *eb;
+	struct ocfs2_extent_list *el;
+	struct ocfs2_path *path = NULL;
+	struct buffer_head *bh = NULL;
+
+	el = &di->id2.i_list;
+	insert->ins_tree_depth = le16_to_cpu(el->l_tree_depth);
+
+	if (el->l_tree_depth) {
+		/*
+		 * If we have tree depth, we read in the
+		 * rightmost extent block ahead of time as
+		 * ocfs2_figure_insert_type() and ocfs2_add_branch()
+		 * may want it later.
+		 */
+		ret = ocfs2_read_block(OCFS2_SB(inode->i_sb),
+				       le64_to_cpu(di->i_last_eb_blk), &bh,
+				       OCFS2_BH_CACHED, inode);
+		if (ret) {
+			mlog_exit(ret);
+			goto out;
+		}
+		eb = (struct ocfs2_extent_block *) bh->b_data;
+		el = &eb->h_list;
+	}
+
+	/*
+	 * Unless we have a contiguous insert, we'll need to know if
+	 * there is room left in our allocation tree for another
+	 * extent record.
+	 *
+	 * XXX: This test is simplistic, we can search for empty
+	 * extent records too.
+	 */
+	insert->ins_free_records = le16_to_cpu(el->l_count) -
+		le16_to_cpu(el->l_next_free_rec);
+
+	if (!insert->ins_tree_depth) {
+		ocfs2_figure_contig_type(inode, insert, el, insert_rec);
+		ocfs2_figure_appending_type(insert, el, insert_rec);
+		return 0;
+	}
+
+	path = ocfs2_new_inode_path(di_bh);
+	if (!path) {
+		ret = -ENOMEM;
+		mlog_errno(ret);
+		goto out;
+	}
+
+	/*
+	 * In the case that we're inserting past what the tree
+	 * currently accounts for, ocfs2_find_path() will return for
+	 * us the rightmost tree path. This is accounted for below in
+	 * the appending code.
+	 */
+	ret = ocfs2_find_path(inode, path, le32_to_cpu(insert_rec->e_cpos));
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	el = path_leaf_el(path);
+
+	/*
+	 * Now that we have the path, there's two things we want to determine:
+	 * 1) Contiguousness (also set contig_index if this is so)
+	 *
+	 * 2) Are we doing an append? We can trivially break this up
+         *     into two types of appends: simple record append, or a
+         *     rotate inside the tail leaf.
+	 */
+	ocfs2_figure_contig_type(inode, insert, el, insert_rec);
+
+	/*
+	 * The insert code isn't quite ready to deal with all cases of
+	 * left contiguousness. Specifically, if it's an insert into
+	 * the 1st record in a leaf, it will require the adjustment of
+	 * cluster count on the last record of the path directly to it's
+	 * left. For now, just catch that case and fool the layers
+	 * above us. This works just fine for tree_depth == 0, which
+	 * is why we allow that above.
+	 */
+	if (insert->ins_contig == CONTIG_LEFT &&
+	    insert->ins_contig_index == 0)
+		insert->ins_contig = CONTIG_NONE;
+
+	/*
+	 * Ok, so we can simply compare against last_eb to figure out
+	 * whether the path doesn't exist. This will only happen in
+	 * the case that we're doing a tail append, so maybe we can
+	 * take advantage of that information somehow.
+	 */
+	if (le64_to_cpu(di->i_last_eb_blk) == path_leaf_bh(path)->b_blocknr) {
+		/*
+		 * Ok, ocfs2_find_path() returned us the rightmost
+		 * tree path. This might be an appending insert. There are
+		 * two cases:
+		 *    1) We're doing a true append at the tail:
+		 *	-This might even be off the end of the leaf
+		 *    2) We're "appending" by rotating in the tail
+		 */
+		ocfs2_figure_appending_type(insert, el, insert_rec);
+	}
+
+out:
+	ocfs2_free_path(path);
+
+	if (ret == 0)
+		*last_eb_bh = bh;
+	else
+		brelse(bh);
+	return ret;
+}
+
+/*
+ * Insert an extent into an inode btree.
+ *
+ * The caller needs to update fe->i_clusters
+ */
 int ocfs2_insert_extent(struct ocfs2_super *osb,
 			handle_t *handle,
 			struct inode *inode,
 			struct buffer_head *fe_bh,
+			u32 cpos,
 			u64 start_blk,
 			u32 new_clusters,
 			struct ocfs2_alloc_context *meta_ac)
 {
-	int status, i, shift;
+	int status, shift;
 	struct buffer_head *last_eb_bh = NULL;
 	struct buffer_head *bh = NULL;
-	struct ocfs2_dinode *fe;
-	struct ocfs2_extent_block *eb;
-	struct ocfs2_extent_list  *el;
+	struct ocfs2_insert_type insert = {0, };
+	struct ocfs2_extent_rec rec;
 
-	mlog_entry_void();
+	mlog(0, "add %u clusters at position %u to inode %llu\n",
+	     new_clusters, cpos, (unsigned long long)OCFS2_I(inode)->ip_blkno);
 
-	mlog(0, "add %u clusters starting at block %llu to inode %llu\n",
-	     new_clusters, (unsigned long long)start_blk,
-	     (unsigned long long)OCFS2_I(inode)->ip_blkno);
+	mlog_bug_on_msg(!ocfs2_sparse_alloc(osb) &&
+			(OCFS2_I(inode)->ip_clusters != cpos),
+			"Device %s, asking for sparse allocation: inode %llu, "
+			"cpos %u, clusters %u\n",
+			osb->dev_str,
+			(unsigned long long)OCFS2_I(inode)->ip_blkno, cpos,
+			OCFS2_I(inode)->ip_clusters);
 
-	fe = (struct ocfs2_dinode *) fe_bh->b_data;
-	el = &fe->id2.i_list;
+	memset(&rec, 0, sizeof(rec));
+	rec.e_cpos = cpu_to_le32(cpos);
+	rec.e_blkno = cpu_to_le64(start_blk);
+	rec.e_leaf_clusters = cpu_to_le16(new_clusters);
 
-	if (el->l_tree_depth) {
-		/* jump to end of tree */
-		status = ocfs2_read_block(osb, le64_to_cpu(fe->i_last_eb_blk),
-					  &last_eb_bh, OCFS2_BH_CACHED, inode);
-		if (status < 0) {
-			mlog_exit(status);
-			goto bail;
-		}
-		eb = (struct ocfs2_extent_block *) last_eb_bh->b_data;
-		el = &eb->h_list;
+	status = ocfs2_figure_insert_type(inode, fe_bh, &last_eb_bh, &rec,
+					  &insert);
+	if (status < 0) {
+		mlog_errno(status);
+		goto bail;
 	}
 
-	/* Can we allocate without adding/shifting tree bits? */
-	i = le16_to_cpu(el->l_next_free_rec) - 1;
-	if (le16_to_cpu(el->l_next_free_rec) == 0
-	    || (le16_to_cpu(el->l_next_free_rec) < le16_to_cpu(el->l_count))
-	    || le32_to_cpu(el->l_recs[i].e_clusters) == 0
-	    || ocfs2_extent_contig(inode, &el->l_recs[i], start_blk))
-		goto out_add;
+	mlog(0, "Insert.appending: %u, Insert.Contig: %u, "
+	     "Insert.contig_index: %d, Insert.free_records: %d, "
+	     "Insert.tree_depth: %d\n",
+	     insert.ins_appending, insert.ins_contig, insert.ins_contig_index,
+	     insert.ins_free_records, insert.ins_tree_depth);
 
-	mlog(0, "ocfs2_allocate_extent: couldn't do a simple add, traversing "
-	     "tree now.\n");
+	/*
+	 * Avoid growing the tree unless we're out of records and the
+	 * insert type requres one.
+	 */
+	if (insert.ins_contig != CONTIG_NONE || insert.ins_free_records)
+		goto out_add;
 
 	shift = ocfs2_find_branch_target(osb, inode, fe_bh, &bh);
 	if (shift < 0) {
@@ -866,13 +2382,9 @@
 	 * and didn't find room for any more extents - we need to add
 	 * another tree level */
 	if (shift) {
-		/* if we hit a leaf, we'd better be empty :) */
-		BUG_ON(le16_to_cpu(el->l_next_free_rec) !=
-		       le16_to_cpu(el->l_count));
 		BUG_ON(bh);
-		mlog(0, "ocfs2_allocate_extent: need to shift tree depth "
-		     "(current = %u)\n",
-		     le16_to_cpu(fe->id2.i_list.l_tree_depth));
+		mlog(0, "need to shift tree depth "
+		     "(current = %d)\n", insert.ins_tree_depth);
 
 		/* ocfs2_shift_tree_depth will return us a buffer with
 		 * the new extent block (so we can pass that to
@@ -883,15 +2395,16 @@
 			mlog_errno(status);
 			goto bail;
 		}
+		insert.ins_tree_depth++;
 		/* Special case: we have room now if we shifted from
 		 * tree_depth 0 */
-		if (fe->id2.i_list.l_tree_depth == cpu_to_le16(1))
+		if (insert.ins_tree_depth == 1)
 			goto out_add;
 	}
 
 	/* call ocfs2_add_branch to add the final part of the tree with
 	 * the new data. */
-	mlog(0, "ocfs2_allocate_extent: add branch. bh = %p\n", bh);
+	mlog(0, "add branch. bh = %p\n", bh);
 	status = ocfs2_add_branch(osb, handle, inode, fe_bh, bh, last_eb_bh,
 				  meta_ac);
 	if (status < 0) {
@@ -900,11 +2413,12 @@
 	}
 
 out_add:
-	/* Finally, we can add clusters. */
-	status = ocfs2_do_insert_extent(osb, handle, inode, fe_bh,
-					start_blk, new_clusters);
+	/* Finally, we can add clusters. This might rotate the tree for us. */
+	status = ocfs2_do_insert_extent(inode, handle, fe_bh, &rec, &insert);
 	if (status < 0)
 		mlog_errno(status);
+	else
+		ocfs2_extent_map_insert_rec(inode, &rec);
 
 bail:
 	if (bh)
@@ -1447,168 +2961,389 @@
  * block will be deleted, and if it will, what the new last extent
  * block will be so we can update his h_next_leaf_blk field, as well
  * as the dinodes i_last_eb_blk */
-static int ocfs2_find_new_last_ext_blk(struct ocfs2_super *osb,
-				       struct inode *inode,
-				       struct ocfs2_dinode *fe,
-				       u32 new_i_clusters,
-				       struct buffer_head *old_last_eb,
+static int ocfs2_find_new_last_ext_blk(struct inode *inode,
+				       unsigned int clusters_to_del,
+				       struct ocfs2_path *path,
 				       struct buffer_head **new_last_eb)
 {
-	int i, status = 0;
-	u64 block = 0;
+	int next_free, ret = 0;
+	u32 cpos;
+	struct ocfs2_extent_rec *rec;
 	struct ocfs2_extent_block *eb;
 	struct ocfs2_extent_list *el;
 	struct buffer_head *bh = NULL;
 
 	*new_last_eb = NULL;
 
-	if (!OCFS2_IS_VALID_DINODE(fe)) {
-		OCFS2_RO_ON_INVALID_DINODE(inode->i_sb, fe);
-		status = -EIO;
-		goto bail;
-	}
-
 	/* we have no tree, so of course, no last_eb. */
-	if (!fe->id2.i_list.l_tree_depth)
-		goto bail;
+	if (!path->p_tree_depth)
+		goto out;
 
 	/* trunc to zero special case - this makes tree_depth = 0
 	 * regardless of what it is.  */
-	if (!new_i_clusters)
-		goto bail;
+	if (OCFS2_I(inode)->ip_clusters == clusters_to_del)
+		goto out;
 
-	eb = (struct ocfs2_extent_block *) old_last_eb->b_data;
-	el = &(eb->h_list);
+	el = path_leaf_el(path);
 	BUG_ON(!el->l_next_free_rec);
 
-	/* Make sure that this guy will actually be empty after we
-	 * clear away the data. */
-	if (le32_to_cpu(el->l_recs[0].e_cpos) < new_i_clusters)
-		goto bail;
+	/*
+	 * Make sure that this extent list will actually be empty
+	 * after we clear away the data. We can shortcut out if
+	 * there's more than one non-empty extent in the
+	 * list. Otherwise, a check of the remaining extent is
+	 * necessary.
+	 */
+	next_free = le16_to_cpu(el->l_next_free_rec);
+	rec = NULL;
+	if (ocfs2_is_empty_extent(&el->l_recs[0])) {
+		if (next_free > 2)
+			goto out;
 
-	/* Ok, at this point, we know that last_eb will definitely
-	 * change, so lets traverse the tree and find the second to
-	 * last extent block. */
-	el = &(fe->id2.i_list);
-	/* go down the tree, */
-	do {
-		for(i = (le16_to_cpu(el->l_next_free_rec) - 1); i >= 0; i--) {
-			if (le32_to_cpu(el->l_recs[i].e_cpos) <
-			    new_i_clusters) {
-				block = le64_to_cpu(el->l_recs[i].e_blkno);
-				break;
-			}
-		}
-		BUG_ON(i < 0);
+		/* We may have a valid extent in index 1, check it. */
+		if (next_free == 2)
+			rec = &el->l_recs[1];
 
-		if (bh) {
-			brelse(bh);
-			bh = NULL;
-		}
+		/*
+		 * Fall through - no more nonempty extents, so we want
+		 * to delete this leaf.
+		 */
+	} else {
+		if (next_free > 1)
+			goto out;
 
-		status = ocfs2_read_block(osb, block, &bh, OCFS2_BH_CACHED,
-					 inode);
-		if (status < 0) {
-			mlog_errno(status);
-			goto bail;
-		}
-		eb = (struct ocfs2_extent_block *) bh->b_data;
-		el = &eb->h_list;
-		if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) {
-			OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb, eb);
-			status = -EIO;
-			goto bail;
-		}
-	} while (el->l_tree_depth);
+		rec = &el->l_recs[0];
+	}
+
+	if (rec) {
+		/*
+		 * Check it we'll only be trimming off the end of this
+		 * cluster.
+		 */
+		if (le16_to_cpu(rec->e_leaf_clusters) > clusters_to_del)
+			goto out;
+	}
+
+	ret = ocfs2_find_cpos_for_left_leaf(inode->i_sb, path, &cpos);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	ret = ocfs2_find_leaf(inode, path_root_el(path), cpos, &bh);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	eb = (struct ocfs2_extent_block *) bh->b_data;
+	el = &eb->h_list;
+	if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) {
+		OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb, eb);
+		ret = -EROFS;
+		goto out;
+	}
 
 	*new_last_eb = bh;
 	get_bh(*new_last_eb);
-	mlog(0, "returning block %llu\n",
-	     (unsigned long long)le64_to_cpu(eb->h_blkno));
-bail:
-	if (bh)
-		brelse(bh);
+	mlog(0, "returning block %llu, (cpos: %u)\n",
+	     (unsigned long long)le64_to_cpu(eb->h_blkno), cpos);
+out:
+	brelse(bh);
 
-	return status;
+	return ret;
+}
+
+/*
+ * Trim some clusters off the rightmost edge of a tree. Only called
+ * during truncate.
+ *
+ * The caller needs to:
+ *   - start journaling of each path component.
+ *   - compute and fully set up any new last ext block
+ */
+static int ocfs2_trim_tree(struct inode *inode, struct ocfs2_path *path,
+			   handle_t *handle, struct ocfs2_truncate_context *tc,
+			   u32 clusters_to_del, u64 *delete_start)
+{
+	int ret, i, index = path->p_tree_depth;
+	u32 new_edge = 0;
+	u64 deleted_eb = 0;
+	struct buffer_head *bh;
+	struct ocfs2_extent_list *el;
+	struct ocfs2_extent_rec *rec;
+
+	*delete_start = 0;
+
+	while (index >= 0) {
+		bh = path->p_node[index].bh;
+		el = path->p_node[index].el;
+
+		mlog(0, "traveling tree (index = %d, block = %llu)\n",
+		     index,  (unsigned long long)bh->b_blocknr);
+
+		BUG_ON(le16_to_cpu(el->l_next_free_rec) == 0);
+
+		if (index !=
+		    (path->p_tree_depth - le16_to_cpu(el->l_tree_depth))) {
+			ocfs2_error(inode->i_sb,
+				    "Inode %lu has invalid ext. block %llu",
+				    inode->i_ino,
+				    (unsigned long long)bh->b_blocknr);
+			ret = -EROFS;
+			goto out;
+		}
+
+find_tail_record:
+		i = le16_to_cpu(el->l_next_free_rec) - 1;
+		rec = &el->l_recs[i];
+
+		mlog(0, "Extent list before: record %d: (%u, %u, %llu), "
+		     "next = %u\n", i, le32_to_cpu(rec->e_cpos),
+		     ocfs2_rec_clusters(el, rec),
+		     (unsigned long long)le64_to_cpu(rec->e_blkno),
+		     le16_to_cpu(el->l_next_free_rec));
+
+		BUG_ON(ocfs2_rec_clusters(el, rec) < clusters_to_del);
+
+		if (le16_to_cpu(el->l_tree_depth) == 0) {
+			/*
+			 * If the leaf block contains a single empty
+			 * extent and no records, we can just remove
+			 * the block.
+			 */
+			if (i == 0 && ocfs2_is_empty_extent(rec)) {
+				memset(rec, 0,
+				       sizeof(struct ocfs2_extent_rec));
+				el->l_next_free_rec = cpu_to_le16(0);
+
+				goto delete;
+			}
+
+			/*
+			 * Remove any empty extents by shifting things
+			 * left. That should make life much easier on
+			 * the code below. This condition is rare
+			 * enough that we shouldn't see a performance
+			 * hit.
+			 */
+			if (ocfs2_is_empty_extent(&el->l_recs[0])) {
+				le16_add_cpu(&el->l_next_free_rec, -1);
+
+				for(i = 0;
+				    i < le16_to_cpu(el->l_next_free_rec); i++)
+					el->l_recs[i] = el->l_recs[i + 1];
+
+				memset(&el->l_recs[i], 0,
+				       sizeof(struct ocfs2_extent_rec));
+
+				/*
+				 * We've modified our extent list. The
+				 * simplest way to handle this change
+				 * is to being the search from the
+				 * start again.
+				 */
+				goto find_tail_record;
+			}
+
+			le16_add_cpu(&rec->e_leaf_clusters, -clusters_to_del);
+
+			/*
+			 * We'll use "new_edge" on our way back up the
+			 * tree to know what our rightmost cpos is.
+			 */
+			new_edge = le16_to_cpu(rec->e_leaf_clusters);
+			new_edge += le32_to_cpu(rec->e_cpos);
+
+			/*
+			 * The caller will use this to delete data blocks.
+			 */
+			*delete_start = le64_to_cpu(rec->e_blkno)
+				+ ocfs2_clusters_to_blocks(inode->i_sb,
+					le16_to_cpu(rec->e_leaf_clusters));
+
+			/*
+			 * If it's now empty, remove this record.
+			 */
+			if (le16_to_cpu(rec->e_leaf_clusters) == 0) {
+				memset(rec, 0,
+				       sizeof(struct ocfs2_extent_rec));
+				le16_add_cpu(&el->l_next_free_rec, -1);
+			}
+		} else {
+			if (le64_to_cpu(rec->e_blkno) == deleted_eb) {
+				memset(rec, 0,
+				       sizeof(struct ocfs2_extent_rec));
+				le16_add_cpu(&el->l_next_free_rec, -1);
+
+				goto delete;
+			}
+
+			/* Can this actually happen? */
+			if (le16_to_cpu(el->l_next_free_rec) == 0)
+				goto delete;
+
+			/*
+			 * We never actually deleted any clusters
+			 * because our leaf was empty. There's no
+			 * reason to adjust the rightmost edge then.
+			 */
+			if (new_edge == 0)
+				goto delete;
+
+			rec->e_int_clusters = cpu_to_le32(new_edge);
+			le32_add_cpu(&rec->e_int_clusters,
+				     -le32_to_cpu(rec->e_cpos));
+
+			 /*
+			  * A deleted child record should have been
+			  * caught above.
+			  */
+			 BUG_ON(le32_to_cpu(rec->e_int_clusters) == 0);
+		}
+
+delete:
+		ret = ocfs2_journal_dirty(handle, bh);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		mlog(0, "extent list container %llu, after: record %d: "
+		     "(%u, %u, %llu), next = %u.\n",
+		     (unsigned long long)bh->b_blocknr, i,
+		     le32_to_cpu(rec->e_cpos), ocfs2_rec_clusters(el, rec),
+		     (unsigned long long)le64_to_cpu(rec->e_blkno),
+		     le16_to_cpu(el->l_next_free_rec));
+
+		/*
+		 * We must be careful to only attempt delete of an
+		 * extent block (and not the root inode block).
+		 */
+		if (index > 0 && le16_to_cpu(el->l_next_free_rec) == 0) {
+			struct ocfs2_extent_block *eb =
+				(struct ocfs2_extent_block *)bh->b_data;
+
+			/*
+			 * Save this for use when processing the
+			 * parent block.
+			 */
+			deleted_eb = le64_to_cpu(eb->h_blkno);
+
+			mlog(0, "deleting this extent block.\n");
+
+			ocfs2_remove_from_cache(inode, bh);
+
+			BUG_ON(ocfs2_rec_clusters(el, &el->l_recs[0]));
+			BUG_ON(le32_to_cpu(el->l_recs[0].e_cpos));
+			BUG_ON(le64_to_cpu(el->l_recs[0].e_blkno));
+
+			if (le16_to_cpu(eb->h_suballoc_slot) == 0) {
+				/*
+				 * This code only understands how to
+				 * lock the suballocator in slot 0,
+				 * which is fine because allocation is
+				 * only ever done out of that
+				 * suballocator too. A future version
+				 * might change that however, so avoid
+				 * a free if we don't know how to
+				 * handle it. This way an fs incompat
+				 * bit will not be necessary.
+				 */
+				ret = ocfs2_free_extent_block(handle,
+							      tc->tc_ext_alloc_inode,
+							      tc->tc_ext_alloc_bh,
+							      eb);
+
+				/* An error here is not fatal. */
+				if (ret < 0)
+					mlog_errno(ret);
+			}
+		} else {
+			deleted_eb = 0;
+		}
+
+		index--;
+	}
+
+	ret = 0;
+out:
+	return ret;
 }
 
 static int ocfs2_do_truncate(struct ocfs2_super *osb,
 			     unsigned int clusters_to_del,
 			     struct inode *inode,
 			     struct buffer_head *fe_bh,
-			     struct buffer_head *old_last_eb_bh,
 			     handle_t *handle,
-			     struct ocfs2_truncate_context *tc)
+			     struct ocfs2_truncate_context *tc,
+			     struct ocfs2_path *path)
 {
-	int status, i, depth;
+	int status;
 	struct ocfs2_dinode *fe;
-	struct ocfs2_extent_block *eb;
 	struct ocfs2_extent_block *last_eb = NULL;
 	struct ocfs2_extent_list *el;
-	struct buffer_head *eb_bh = NULL;
 	struct buffer_head *last_eb_bh = NULL;
-	u64 next_eb = 0;
 	u64 delete_blk = 0;
 
 	fe = (struct ocfs2_dinode *) fe_bh->b_data;
 
-	status = ocfs2_find_new_last_ext_blk(osb,
-					     inode,
-					     fe,
-					     le32_to_cpu(fe->i_clusters) -
-					     		clusters_to_del,
-					     old_last_eb_bh,
-					     &last_eb_bh);
+	status = ocfs2_find_new_last_ext_blk(inode, clusters_to_del,
+					     path, &last_eb_bh);
 	if (status < 0) {
 		mlog_errno(status);
 		goto bail;
 	}
-	if (last_eb_bh)
-		last_eb = (struct ocfs2_extent_block *) last_eb_bh->b_data;
 
-	status = ocfs2_journal_access(handle, inode, fe_bh,
-				      OCFS2_JOURNAL_ACCESS_WRITE);
+	/*
+	 * Each component will be touched, so we might as well journal
+	 * here to avoid having to handle errors later.
+	 */
+	status = ocfs2_journal_access_path(inode, handle, path);
 	if (status < 0) {
 		mlog_errno(status);
 		goto bail;
 	}
+
+	if (last_eb_bh) {
+		status = ocfs2_journal_access(handle, inode, last_eb_bh,
+					      OCFS2_JOURNAL_ACCESS_WRITE);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail;
+		}
+
+		last_eb = (struct ocfs2_extent_block *) last_eb_bh->b_data;
+	}
+
 	el = &(fe->id2.i_list);
 
+	/*
+	 * Lower levels depend on this never happening, but it's best
+	 * to check it up here before changing the tree.
+	 */
+	if (el->l_tree_depth && el->l_recs[0].e_int_clusters == 0) {
+		ocfs2_error(inode->i_sb,
+			    "Inode %lu has an empty extent record, depth %u\n",
+			    inode->i_ino, le16_to_cpu(el->l_tree_depth));
+		status = -EROFS;
+		goto bail;
+	}
+
 	spin_lock(&OCFS2_I(inode)->ip_lock);
 	OCFS2_I(inode)->ip_clusters = le32_to_cpu(fe->i_clusters) -
 				      clusters_to_del;
 	spin_unlock(&OCFS2_I(inode)->ip_lock);
 	le32_add_cpu(&fe->i_clusters, -clusters_to_del);
-	fe->i_mtime = cpu_to_le64(CURRENT_TIME.tv_sec);
-	fe->i_mtime_nsec = cpu_to_le32(CURRENT_TIME.tv_nsec);
 
-	i = le16_to_cpu(el->l_next_free_rec) - 1;
-
-	BUG_ON(le32_to_cpu(el->l_recs[i].e_clusters) < clusters_to_del);
-	le32_add_cpu(&el->l_recs[i].e_clusters, -clusters_to_del);
-	/* tree depth zero, we can just delete the clusters, otherwise
-	 * we need to record the offset of the next level extent block
-	 * as we may overwrite it. */
-	if (!el->l_tree_depth)
-		delete_blk = le64_to_cpu(el->l_recs[i].e_blkno)
-			+ ocfs2_clusters_to_blocks(osb->sb,
-					le32_to_cpu(el->l_recs[i].e_clusters));
-	else
-		next_eb = le64_to_cpu(el->l_recs[i].e_blkno);
-
-	if (!el->l_recs[i].e_clusters) {
-		/* if we deleted the whole extent record, then clear
-		 * out the other fields and update the extent
-		 * list. For depth > 0 trees, we've already recorded
-		 * the extent block in 'next_eb' */
-		el->l_recs[i].e_cpos = 0;
-		el->l_recs[i].e_blkno = 0;
-		BUG_ON(!el->l_next_free_rec);
-		le16_add_cpu(&el->l_next_free_rec, -1);
+	status = ocfs2_trim_tree(inode, path, handle, tc,
+				 clusters_to_del, &delete_blk);
+	if (status) {
+		mlog_errno(status);
+		goto bail;
 	}
 
-	depth = le16_to_cpu(el->l_tree_depth);
-	if (!fe->i_clusters) {
+	if (le32_to_cpu(fe->i_clusters) == 0) {
 		/* trunc to zero is a special case. */
 		el->l_tree_depth = 0;
 		fe->i_last_eb_blk = 0;
@@ -1625,12 +3360,6 @@
 		/* If there will be a new last extent block, then by
 		 * definition, there cannot be any leaves to the right of
 		 * him. */
-		status = ocfs2_journal_access(handle, inode, last_eb_bh,
-					      OCFS2_JOURNAL_ACCESS_WRITE);
-		if (status < 0) {
-			mlog_errno(status);
-			goto bail;
-		}
 		last_eb->h_next_leaf_blk = 0;
 		status = ocfs2_journal_dirty(handle, last_eb_bh);
 		if (status < 0) {
@@ -1639,125 +3368,249 @@
 		}
 	}
 
-	/* if our tree depth > 0, update all the tree blocks below us. */
-	while (depth) {
-		mlog(0, "traveling tree (depth = %d, next_eb = %llu)\n",
-		     depth,  (unsigned long long)next_eb);
-		status = ocfs2_read_block(osb, next_eb, &eb_bh,
-					  OCFS2_BH_CACHED, inode);
+	if (delete_blk) {
+		status = ocfs2_truncate_log_append(osb, handle, delete_blk,
+						   clusters_to_del);
 		if (status < 0) {
 			mlog_errno(status);
 			goto bail;
 		}
-		eb = (struct ocfs2_extent_block *)eb_bh->b_data;
-		if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) {
-			OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb, eb);
-			status = -EIO;
-			goto bail;
-		}
-		el = &(eb->h_list);
-
-		status = ocfs2_journal_access(handle, inode, eb_bh,
-					      OCFS2_JOURNAL_ACCESS_WRITE);
-		if (status < 0) {
-			mlog_errno(status);
-			goto bail;
-		}
-
-		BUG_ON(le16_to_cpu(el->l_next_free_rec) == 0);
-		BUG_ON(depth != (le16_to_cpu(el->l_tree_depth) + 1));
-
-		i = le16_to_cpu(el->l_next_free_rec) - 1;
-
-		mlog(0, "extent block %llu, before: record %d: "
-		     "(%u, %u, %llu), next = %u\n",
-		     (unsigned long long)le64_to_cpu(eb->h_blkno), i,
-		     le32_to_cpu(el->l_recs[i].e_cpos),
-		     le32_to_cpu(el->l_recs[i].e_clusters),
-		     (unsigned long long)le64_to_cpu(el->l_recs[i].e_blkno),
-		     le16_to_cpu(el->l_next_free_rec));
-
-		BUG_ON(le32_to_cpu(el->l_recs[i].e_clusters) < clusters_to_del);
-		le32_add_cpu(&el->l_recs[i].e_clusters, -clusters_to_del);
-
-		next_eb = le64_to_cpu(el->l_recs[i].e_blkno);
-		/* bottom-most block requires us to delete data.*/
-		if (!el->l_tree_depth)
-			delete_blk = le64_to_cpu(el->l_recs[i].e_blkno)
-				+ ocfs2_clusters_to_blocks(osb->sb,
-					le32_to_cpu(el->l_recs[i].e_clusters));
-		if (!el->l_recs[i].e_clusters) {
-			el->l_recs[i].e_cpos = 0;
-			el->l_recs[i].e_blkno = 0;
-			BUG_ON(!el->l_next_free_rec);
-			le16_add_cpu(&el->l_next_free_rec, -1);
-		}
-		mlog(0, "extent block %llu, after: record %d: "
-		     "(%u, %u, %llu), next = %u\n",
-		     (unsigned long long)le64_to_cpu(eb->h_blkno), i,
-		     le32_to_cpu(el->l_recs[i].e_cpos),
-		     le32_to_cpu(el->l_recs[i].e_clusters),
-		     (unsigned long long)le64_to_cpu(el->l_recs[i].e_blkno),
-		     le16_to_cpu(el->l_next_free_rec));
-
-		status = ocfs2_journal_dirty(handle, eb_bh);
-		if (status < 0) {
-			mlog_errno(status);
-			goto bail;
-		}
-
-		if (!el->l_next_free_rec) {
-			mlog(0, "deleting this extent block.\n");
-
-			ocfs2_remove_from_cache(inode, eb_bh);
-
-			BUG_ON(el->l_recs[0].e_clusters);
-			BUG_ON(el->l_recs[0].e_cpos);
-			BUG_ON(el->l_recs[0].e_blkno);
-			if (eb->h_suballoc_slot == 0) {
-				/*
-				 * This code only understands how to
-				 * lock the suballocator in slot 0,
-				 * which is fine because allocation is
-				 * only ever done out of that
-				 * suballocator too. A future version
-				 * might change that however, so avoid
-				 * a free if we don't know how to
-				 * handle it. This way an fs incompat
-				 * bit will not be necessary.
-				 */
-				status = ocfs2_free_extent_block(handle,
-								 tc->tc_ext_alloc_inode,
-								 tc->tc_ext_alloc_bh,
-								 eb);
-				if (status < 0) {
-					mlog_errno(status);
-					goto bail;
-				}
-			}
-		}
-		brelse(eb_bh);
-		eb_bh = NULL;
-		depth--;
-	}
-
-	BUG_ON(!delete_blk);
-	status = ocfs2_truncate_log_append(osb, handle, delete_blk,
-					   clusters_to_del);
-	if (status < 0) {
-		mlog_errno(status);
-		goto bail;
 	}
 	status = 0;
 bail:
-	if (!status)
-		ocfs2_extent_map_trunc(inode, le32_to_cpu(fe->i_clusters));
-	else
-		ocfs2_extent_map_drop(inode, 0);
+
 	mlog_exit(status);
 	return status;
 }
 
+static int ocfs2_writeback_zero_func(handle_t *handle, struct buffer_head *bh)
+{
+	set_buffer_uptodate(bh);
+	mark_buffer_dirty(bh);
+	return 0;
+}
+
+static int ocfs2_ordered_zero_func(handle_t *handle, struct buffer_head *bh)
+{
+	set_buffer_uptodate(bh);
+	mark_buffer_dirty(bh);
+	return ocfs2_journal_dirty_data(handle, bh);
+}
+
+static void ocfs2_zero_cluster_pages(struct inode *inode, loff_t isize,
+				     struct page **pages, int numpages,
+				     u64 phys, handle_t *handle)
+{
+	int i, ret, partial = 0;
+	void *kaddr;
+	struct page *page;
+	unsigned int from, to = PAGE_CACHE_SIZE;
+	struct super_block *sb = inode->i_sb;
+
+	BUG_ON(!ocfs2_sparse_alloc(OCFS2_SB(sb)));
+
+	if (numpages == 0)
+		goto out;
+
+	from = isize & (PAGE_CACHE_SIZE - 1); /* 1st page offset */
+	if (PAGE_CACHE_SHIFT > OCFS2_SB(sb)->s_clustersize_bits) {
+		/*
+		 * Since 'from' has been capped to a value below page
+		 * size, this calculation won't be able to overflow
+		 * 'to'
+		 */
+		to = ocfs2_align_bytes_to_clusters(sb, from);
+
+		/*
+		 * The truncate tail in this case should never contain
+		 * more than one page at maximum. The loop below also
+		 * assumes this.
+		 */
+		BUG_ON(numpages != 1);
+	}
+
+	for(i = 0; i < numpages; i++) {
+		page = pages[i];
+
+		BUG_ON(from > PAGE_CACHE_SIZE);
+		BUG_ON(to > PAGE_CACHE_SIZE);
+
+		ret = ocfs2_map_page_blocks(page, &phys, inode, from, to, 0);
+		if (ret)
+			mlog_errno(ret);
+
+		kaddr = kmap_atomic(page, KM_USER0);
+		memset(kaddr + from, 0, to - from);
+		kunmap_atomic(kaddr, KM_USER0);
+
+		/*
+		 * Need to set the buffers we zero'd into uptodate
+		 * here if they aren't - ocfs2_map_page_blocks()
+		 * might've skipped some
+		 */
+		if (ocfs2_should_order_data(inode)) {
+			ret = walk_page_buffers(handle,
+						page_buffers(page),
+						from, to, &partial,
+						ocfs2_ordered_zero_func);
+			if (ret < 0)
+				mlog_errno(ret);
+		} else {
+			ret = walk_page_buffers(handle, page_buffers(page),
+						from, to, &partial,
+						ocfs2_writeback_zero_func);
+			if (ret < 0)
+				mlog_errno(ret);
+		}
+
+		if (!partial)
+			SetPageUptodate(page);
+
+		flush_dcache_page(page);
+
+		/*
+		 * Every page after the 1st one should be completely zero'd.
+		 */
+		from = 0;
+	}
+out:
+	if (pages) {
+		for (i = 0; i < numpages; i++) {
+			page = pages[i];
+			unlock_page(page);
+			mark_page_accessed(page);
+			page_cache_release(page);
+		}
+	}
+}
+
+static int ocfs2_grab_eof_pages(struct inode *inode, loff_t isize, struct page **pages,
+				int *num, u64 *phys)
+{
+	int i, numpages = 0, ret = 0;
+	unsigned int csize = OCFS2_SB(inode->i_sb)->s_clustersize;
+	unsigned int ext_flags;
+	struct super_block *sb = inode->i_sb;
+	struct address_space *mapping = inode->i_mapping;
+	unsigned long index;
+	u64 next_cluster_bytes;
+
+	BUG_ON(!ocfs2_sparse_alloc(OCFS2_SB(sb)));
+
+	/* Cluster boundary, so we don't need to grab any pages. */
+	if ((isize & (csize - 1)) == 0)
+		goto out;
+
+	ret = ocfs2_extent_map_get_blocks(inode, isize >> sb->s_blocksize_bits,
+					  phys, NULL, &ext_flags);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	/* Tail is a hole. */
+	if (*phys == 0)
+		goto out;
+
+	/* Tail is marked as unwritten, we can count on write to zero
+	 * in that case. */
+	if (ext_flags & OCFS2_EXT_UNWRITTEN)
+		goto out;
+
+	next_cluster_bytes = ocfs2_align_bytes_to_clusters(inode->i_sb, isize);
+	index = isize >> PAGE_CACHE_SHIFT;
+	do {
+		pages[numpages] = grab_cache_page(mapping, index);
+		if (!pages[numpages]) {
+			ret = -ENOMEM;
+			mlog_errno(ret);
+			goto out;
+		}
+
+		numpages++;
+		index++;
+	} while (index < (next_cluster_bytes >> PAGE_CACHE_SHIFT));
+
+out:
+	if (ret != 0) {
+		if (pages) {
+			for (i = 0; i < numpages; i++) {
+				if (pages[i]) {
+					unlock_page(pages[i]);
+					page_cache_release(pages[i]);
+				}
+			}
+		}
+		numpages = 0;
+	}
+
+	*num = numpages;
+
+	return ret;
+}
+
+/*
+ * Zero the area past i_size but still within an allocated
+ * cluster. This avoids exposing nonzero data on subsequent file
+ * extends.
+ *
+ * We need to call this before i_size is updated on the inode because
+ * otherwise block_write_full_page() will skip writeout of pages past
+ * i_size. The new_i_size parameter is passed for this reason.
+ */
+int ocfs2_zero_tail_for_truncate(struct inode *inode, handle_t *handle,
+				 u64 new_i_size)
+{
+	int ret, numpages;
+	loff_t endbyte;
+	struct page **pages = NULL;
+	u64 phys;
+
+	/*
+	 * File systems which don't support sparse files zero on every
+	 * extend.
+	 */
+	if (!ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb)))
+		return 0;
+
+	pages = kcalloc(ocfs2_pages_per_cluster(inode->i_sb),
+			sizeof(struct page *), GFP_NOFS);
+	if (pages == NULL) {
+		ret = -ENOMEM;
+		mlog_errno(ret);
+		goto out;
+	}
+
+	ret = ocfs2_grab_eof_pages(inode, new_i_size, pages, &numpages, &phys);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	if (numpages == 0)
+		goto out;
+
+	ocfs2_zero_cluster_pages(inode, new_i_size, pages, numpages, phys,
+				 handle);
+
+	/*
+	 * Initiate writeout of the pages we zero'd here. We don't
+	 * wait on them - the truncate_inode_pages() call later will
+	 * do that for us.
+	 */
+	endbyte = ocfs2_align_bytes_to_clusters(inode->i_sb, new_i_size);
+	ret = do_sync_mapping_range(inode->i_mapping, new_i_size,
+				    endbyte - 1, SYNC_FILE_RANGE_WRITE);
+	if (ret)
+		mlog_errno(ret);
+
+out:
+	if (pages)
+		kfree(pages);
+
+	return ret;
+}
+
 /*
  * It is expected, that by the time you call this function,
  * inode->i_size and fe->i_size have been adjusted.
@@ -1770,82 +3623,90 @@
 			  struct ocfs2_truncate_context *tc)
 {
 	int status, i, credits, tl_sem = 0;
-	u32 clusters_to_del, target_i_clusters;
-	u64 last_eb = 0;
-	struct ocfs2_dinode *fe;
-	struct ocfs2_extent_block *eb;
+	u32 clusters_to_del, new_highest_cpos, range;
 	struct ocfs2_extent_list *el;
-	struct buffer_head *last_eb_bh;
 	handle_t *handle = NULL;
 	struct inode *tl_inode = osb->osb_tl_inode;
+	struct ocfs2_path *path = NULL;
 
 	mlog_entry_void();
 
 	down_write(&OCFS2_I(inode)->ip_alloc_sem);
 
-	target_i_clusters = ocfs2_clusters_for_bytes(osb->sb,
+	new_highest_cpos = ocfs2_clusters_for_bytes(osb->sb,
 						     i_size_read(inode));
 
-	last_eb_bh = tc->tc_last_eb_bh;
-	tc->tc_last_eb_bh = NULL;
-
-	fe = (struct ocfs2_dinode *) fe_bh->b_data;
-
-	if (fe->id2.i_list.l_tree_depth) {
-		eb = (struct ocfs2_extent_block *) last_eb_bh->b_data;
-		el = &eb->h_list;
-	} else
-		el = &fe->id2.i_list;
-	last_eb = le64_to_cpu(fe->i_last_eb_blk);
-start:
-	mlog(0, "ocfs2_commit_truncate: fe->i_clusters = %u, "
-	     "last_eb = %llu, fe->i_last_eb_blk = %llu, "
-	     "fe->id2.i_list.l_tree_depth = %u last_eb_bh = %p\n",
-	     le32_to_cpu(fe->i_clusters), (unsigned long long)last_eb,
-	     (unsigned long long)le64_to_cpu(fe->i_last_eb_blk),
-	     le16_to_cpu(fe->id2.i_list.l_tree_depth), last_eb_bh);
-
-	if (last_eb != le64_to_cpu(fe->i_last_eb_blk)) {
-		mlog(0, "last_eb changed!\n");
-		BUG_ON(!fe->id2.i_list.l_tree_depth);
-		last_eb = le64_to_cpu(fe->i_last_eb_blk);
-		/* i_last_eb_blk may have changed, read it if
-		 * necessary. We don't have to worry about the
-		 * truncate to zero case here (where there becomes no
-		 * last_eb) because we never loop back after our work
-		 * is done. */
-		if (last_eb_bh) {
-			brelse(last_eb_bh);
-			last_eb_bh = NULL;
-		}
-
-		status = ocfs2_read_block(osb, last_eb,
-					  &last_eb_bh, OCFS2_BH_CACHED,
-					  inode);
-		if (status < 0) {
-			mlog_errno(status);
-			goto bail;
-		}
-		eb = (struct ocfs2_extent_block *) last_eb_bh->b_data;
-		if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) {
-			OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb, eb);
-			status = -EIO;
-			goto bail;
-		}
-		el = &(eb->h_list);
+	path = ocfs2_new_inode_path(fe_bh);
+	if (!path) {
+		status = -ENOMEM;
+		mlog_errno(status);
+		goto bail;
 	}
 
-	/* by now, el will point to the extent list on the bottom most
-	 * portion of this tree. */
-	i = le16_to_cpu(el->l_next_free_rec) - 1;
-	if (le32_to_cpu(el->l_recs[i].e_cpos) >= target_i_clusters)
-		clusters_to_del = le32_to_cpu(el->l_recs[i].e_clusters);
-	else
-		clusters_to_del = (le32_to_cpu(el->l_recs[i].e_clusters) +
-				   le32_to_cpu(el->l_recs[i].e_cpos)) -
-				  target_i_clusters;
+	ocfs2_extent_map_trunc(inode, new_highest_cpos);
 
-	mlog(0, "clusters_to_del = %u in this pass\n", clusters_to_del);
+start:
+	/*
+	 * Check that we still have allocation to delete.
+	 */
+	if (OCFS2_I(inode)->ip_clusters == 0) {
+		status = 0;
+		goto bail;
+	}
+
+	/*
+	 * Truncate always works against the rightmost tree branch.
+	 */
+	status = ocfs2_find_path(inode, path, UINT_MAX);
+	if (status) {
+		mlog_errno(status);
+		goto bail;
+	}
+
+	mlog(0, "inode->ip_clusters = %u, tree_depth = %u\n",
+	     OCFS2_I(inode)->ip_clusters, path->p_tree_depth);
+
+	/*
+	 * By now, el will point to the extent list on the bottom most
+	 * portion of this tree. Only the tail record is considered in
+	 * each pass.
+	 *
+	 * We handle the following cases, in order:
+	 * - empty extent: delete the remaining branch
+	 * - remove the entire record
+	 * - remove a partial record
+	 * - no record needs to be removed (truncate has completed)
+	 */
+	el = path_leaf_el(path);
+	if (le16_to_cpu(el->l_next_free_rec) == 0) {
+		ocfs2_error(inode->i_sb,
+			    "Inode %llu has empty extent block at %llu\n",
+			    (unsigned long long)OCFS2_I(inode)->ip_blkno,
+			    (unsigned long long)path_leaf_bh(path)->b_blocknr);
+		status = -EROFS;
+		goto bail;
+	}
+
+	i = le16_to_cpu(el->l_next_free_rec) - 1;
+	range = le32_to_cpu(el->l_recs[i].e_cpos) +
+		ocfs2_rec_clusters(el, &el->l_recs[i]);
+	if (i == 0 && ocfs2_is_empty_extent(&el->l_recs[i])) {
+		clusters_to_del = 0;
+	} else if (le32_to_cpu(el->l_recs[i].e_cpos) >= new_highest_cpos) {
+		clusters_to_del = ocfs2_rec_clusters(el, &el->l_recs[i]);
+	} else if (range > new_highest_cpos) {
+		clusters_to_del = (ocfs2_rec_clusters(el, &el->l_recs[i]) +
+				   le32_to_cpu(el->l_recs[i].e_cpos)) -
+				  new_highest_cpos;
+	} else {
+		status = 0;
+		goto bail;
+	}
+
+	mlog(0, "clusters_to_del = %u in this pass, tail blk=%llu\n",
+	     clusters_to_del, (unsigned long long)path_leaf_bh(path)->b_blocknr);
+
+	BUG_ON(clusters_to_del == 0);
 
 	mutex_lock(&tl_inode->i_mutex);
 	tl_sem = 1;
@@ -1861,7 +3722,8 @@
 	}
 
 	credits = ocfs2_calc_tree_trunc_credits(osb->sb, clusters_to_del,
-						fe, el);
+						(struct ocfs2_dinode *)fe_bh->b_data,
+						el);
 	handle = ocfs2_start_trans(osb, credits);
 	if (IS_ERR(handle)) {
 		status = PTR_ERR(handle);
@@ -1870,13 +3732,8 @@
 		goto bail;
 	}
 
-	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
-	status = ocfs2_mark_inode_dirty(handle, inode, fe_bh);
-	if (status < 0)
-		mlog_errno(status);
-
-	status = ocfs2_do_truncate(osb, clusters_to_del, inode, fe_bh,
-				   last_eb_bh, handle, tc);
+	status = ocfs2_do_truncate(osb, clusters_to_del, inode, fe_bh, handle,
+				   tc, path);
 	if (status < 0) {
 		mlog_errno(status);
 		goto bail;
@@ -1888,9 +3745,14 @@
 	ocfs2_commit_trans(osb, handle);
 	handle = NULL;
 
-	BUG_ON(le32_to_cpu(fe->i_clusters) < target_i_clusters);
-	if (le32_to_cpu(fe->i_clusters) > target_i_clusters)
-		goto start;
+	ocfs2_reinit_path(path, 1);
+
+	/*
+	 * The check above will catch the case where we've truncated
+	 * away all allocation.
+	 */
+	goto start;
+
 bail:
 	up_write(&OCFS2_I(inode)->ip_alloc_sem);
 
@@ -1902,8 +3764,7 @@
 	if (handle)
 		ocfs2_commit_trans(osb, handle);
 
-	if (last_eb_bh)
-		brelse(last_eb_bh);
+	ocfs2_free_path(path);
 
 	/* This will drop the ext_alloc cluster lock for us */
 	ocfs2_free_truncate_context(tc);
@@ -1912,7 +3773,6 @@
 	return status;
 }
 
-
 /*
  * Expects the inode to already be locked. This will figure out which
  * inodes need to be locked and will put them on the returned truncate
@@ -1923,7 +3783,7 @@
 			   struct buffer_head *fe_bh,
 			   struct ocfs2_truncate_context **tc)
 {
-	int status, metadata_delete;
+	int status, metadata_delete, i;
 	unsigned int new_i_clusters;
 	struct ocfs2_dinode *fe;
 	struct ocfs2_extent_block *eb;
@@ -1944,21 +3804,6 @@
 	     "%llu\n", fe->i_clusters, new_i_clusters,
 	     (unsigned long long)fe->i_size);
 
-	if (le32_to_cpu(fe->i_clusters) <= new_i_clusters) {
-		ocfs2_error(inode->i_sb, "Dinode %llu has cluster count "
-			    "%u and size %llu whereas struct inode has "
-			    "cluster count %u and size %llu which caused an "
-			    "invalid truncate to %u clusters.",
-			    (unsigned long long)le64_to_cpu(fe->i_blkno),
-			    le32_to_cpu(fe->i_clusters),
-			    (unsigned long long)le64_to_cpu(fe->i_size),
-			    OCFS2_I(inode)->ip_clusters, i_size_read(inode),
-			    new_i_clusters);
-		mlog_meta_lvb(ML_ERROR, &OCFS2_I(inode)->ip_meta_lockres);
-		status = -EIO;
-		goto bail;
-	}
-
 	*tc = kzalloc(sizeof(struct ocfs2_truncate_context), GFP_KERNEL);
 	if (!(*tc)) {
 		status = -ENOMEM;
@@ -1986,7 +3831,15 @@
 			goto bail;
 		}
 		el = &(eb->h_list);
-		if (le32_to_cpu(el->l_recs[0].e_cpos) >= new_i_clusters)
+
+		i = 0;
+		if (ocfs2_is_empty_extent(&el->l_recs[0]))
+			i = 1;
+		/*
+		 * XXX: Should we check that next_free_rec contains
+		 * the extent?
+		 */
+		if (le32_to_cpu(el->l_recs[i].e_cpos) >= new_i_clusters)
 			metadata_delete = 1;
 	}
 
diff --git a/fs/ocfs2/alloc.h b/fs/ocfs2/alloc.h
index 0b82e80..fbcb593 100644
--- a/fs/ocfs2/alloc.h
+++ b/fs/ocfs2/alloc.h
@@ -31,7 +31,8 @@
 			handle_t *handle,
 			struct inode *inode,
 			struct buffer_head *fe_bh,
-			u64 blkno,
+			u32 cpos,
+			u64 start_blk,
 			u32 new_clusters,
 			struct ocfs2_alloc_context *meta_ac);
 int ocfs2_num_free_extents(struct ocfs2_super *osb,
@@ -70,6 +71,8 @@
 	struct buffer_head *tc_last_eb_bh;
 };
 
+int ocfs2_zero_tail_for_truncate(struct inode *inode, handle_t *handle,
+				 u64 new_i_size);
 int ocfs2_prepare_truncate(struct ocfs2_super *osb,
 			   struct inode *inode,
 			   struct buffer_head *fe_bh,
@@ -79,4 +82,26 @@
 			  struct buffer_head *fe_bh,
 			  struct ocfs2_truncate_context *tc);
 
+int ocfs2_find_leaf(struct inode *inode, struct ocfs2_extent_list *root_el,
+		    u32 cpos, struct buffer_head **leaf_bh);
+
+/*
+ * Helper function to look at the # of clusters in an extent record.
+ */
+static inline unsigned int ocfs2_rec_clusters(struct ocfs2_extent_list *el,
+					      struct ocfs2_extent_rec *rec)
+{
+	/*
+	 * Cluster count in extent records is slightly different
+	 * between interior nodes and leaf nodes. This is to support
+	 * unwritten extents which need a flags field in leaf node
+	 * records, thus shrinking the available space for a clusters
+	 * field.
+	 */
+	if (el->l_tree_depth)
+		return le32_to_cpu(rec->e_int_clusters);
+	else
+		return le16_to_cpu(rec->e_leaf_clusters);
+}
+
 #endif /* OCFS2_ALLOC_H */
diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c
index 875c114..56963e6 100644
--- a/fs/ocfs2/aops.c
+++ b/fs/ocfs2/aops.c
@@ -24,6 +24,8 @@
 #include <linux/highmem.h>
 #include <linux/pagemap.h>
 #include <asm/byteorder.h>
+#include <linux/swap.h>
+#include <linux/pipe_fs_i.h>
 
 #define MLOG_MASK_PREFIX ML_FILE_IO
 #include <cluster/masklog.h>
@@ -37,6 +39,7 @@
 #include "file.h"
 #include "inode.h"
 #include "journal.h"
+#include "suballoc.h"
 #include "super.h"
 #include "symlink.h"
 
@@ -134,7 +137,9 @@
 			   struct buffer_head *bh_result, int create)
 {
 	int err = 0;
+	unsigned int ext_flags;
 	u64 p_blkno, past_eof;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
 
 	mlog_entry("(0x%p, %llu, 0x%p, %d)\n", inode,
 		   (unsigned long long)iblock, bh_result, create);
@@ -149,17 +154,8 @@
 		goto bail;
 	}
 
-	/* this can happen if another node truncs after our extend! */
-	spin_lock(&OCFS2_I(inode)->ip_lock);
-	if (iblock >= ocfs2_clusters_to_blocks(inode->i_sb,
-					       OCFS2_I(inode)->ip_clusters))
-		err = -EIO;
-	spin_unlock(&OCFS2_I(inode)->ip_lock);
-	if (err)
-		goto bail;
-
-	err = ocfs2_extent_map_get_blocks(inode, iblock, 1, &p_blkno,
-					  NULL);
+	err = ocfs2_extent_map_get_blocks(inode, iblock, &p_blkno, NULL,
+					  &ext_flags);
 	if (err) {
 		mlog(ML_ERROR, "Error %d from get_blocks(0x%p, %llu, 1, "
 		     "%llu, NULL)\n", err, inode, (unsigned long long)iblock,
@@ -167,23 +163,40 @@
 		goto bail;
 	}
 
-	map_bh(bh_result, inode->i_sb, p_blkno);
+	/*
+	 * ocfs2 never allocates in this function - the only time we
+	 * need to use BH_New is when we're extending i_size on a file
+	 * system which doesn't support holes, in which case BH_New
+	 * allows block_prepare_write() to zero.
+	 */
+	mlog_bug_on_msg(create && p_blkno == 0 && ocfs2_sparse_alloc(osb),
+			"ino %lu, iblock %llu\n", inode->i_ino,
+			(unsigned long long)iblock);
 
-	if (bh_result->b_blocknr == 0) {
-		err = -EIO;
-		mlog(ML_ERROR, "iblock = %llu p_blkno = %llu blkno=(%llu)\n",
-		     (unsigned long long)iblock,
-		     (unsigned long long)p_blkno,
-		     (unsigned long long)OCFS2_I(inode)->ip_blkno);
+	/* Treat the unwritten extent as a hole for zeroing purposes. */
+	if (p_blkno && !(ext_flags & OCFS2_EXT_UNWRITTEN))
+		map_bh(bh_result, inode->i_sb, p_blkno);
+
+	if (!ocfs2_sparse_alloc(osb)) {
+		if (p_blkno == 0) {
+			err = -EIO;
+			mlog(ML_ERROR,
+			     "iblock = %llu p_blkno = %llu blkno=(%llu)\n",
+			     (unsigned long long)iblock,
+			     (unsigned long long)p_blkno,
+			     (unsigned long long)OCFS2_I(inode)->ip_blkno);
+			mlog(ML_ERROR, "Size %llu, clusters %u\n", (unsigned long long)i_size_read(inode), OCFS2_I(inode)->ip_clusters);
+			dump_stack();
+		}
+
+		past_eof = ocfs2_blocks_for_bytes(inode->i_sb, i_size_read(inode));
+		mlog(0, "Inode %lu, past_eof = %llu\n", inode->i_ino,
+		     (unsigned long long)past_eof);
+
+		if (create && (iblock >= past_eof))
+			set_buffer_new(bh_result);
 	}
 
-	past_eof = ocfs2_blocks_for_bytes(inode->i_sb, i_size_read(inode));
-	mlog(0, "Inode %lu, past_eof = %llu\n", inode->i_ino,
-	     (unsigned long long)past_eof);
-
-	if (create && (iblock >= past_eof))
-		set_buffer_new(bh_result);
-
 bail:
 	if (err < 0)
 		err = -EIO;
@@ -276,8 +289,11 @@
 	return ret;
 }
 
-/* This can also be called from ocfs2_write_zero_page() which has done
- * it's own cluster locking. */
+/*
+ * This is called from ocfs2_write_zero_page() which has handled it's
+ * own cluster locking and has ensured allocation exists for those
+ * blocks to be written.
+ */
 int ocfs2_prepare_write_nolock(struct inode *inode, struct page *page,
 			       unsigned from, unsigned to)
 {
@@ -292,44 +308,17 @@
 	return ret;
 }
 
-/*
- * ocfs2_prepare_write() can be an outer-most ocfs2 call when it is called
- * from loopback.  It must be able to perform its own locking around
- * ocfs2_get_block().
- */
-static int ocfs2_prepare_write(struct file *file, struct page *page,
-			       unsigned from, unsigned to)
-{
-	struct inode *inode = page->mapping->host;
-	int ret;
-
-	mlog_entry("(0x%p, 0x%p, %u, %u)\n", file, page, from, to);
-
-	ret = ocfs2_meta_lock_with_page(inode, NULL, 0, page);
-	if (ret != 0) {
-		mlog_errno(ret);
-		goto out;
-	}
-
-	ret = ocfs2_prepare_write_nolock(inode, page, from, to);
-
-	ocfs2_meta_unlock(inode, 0);
-out:
-	mlog_exit(ret);
-	return ret;
-}
-
 /* Taken from ext3. We don't necessarily need the full blown
  * functionality yet, but IMHO it's better to cut and paste the whole
  * thing so we can avoid introducing our own bugs (and easily pick up
  * their fixes when they happen) --Mark */
-static int walk_page_buffers(	handle_t *handle,
-				struct buffer_head *head,
-				unsigned from,
-				unsigned to,
-				int *partial,
-				int (*fn)(	handle_t *handle,
-						struct buffer_head *bh))
+int walk_page_buffers(	handle_t *handle,
+			struct buffer_head *head,
+			unsigned from,
+			unsigned to,
+			int *partial,
+			int (*fn)(	handle_t *handle,
+					struct buffer_head *bh))
 {
 	struct buffer_head *bh;
 	unsigned block_start, block_end;
@@ -388,95 +377,6 @@
 	return handle;
 }
 
-static int ocfs2_commit_write(struct file *file, struct page *page,
-			      unsigned from, unsigned to)
-{
-	int ret;
-	struct buffer_head *di_bh = NULL;
-	struct inode *inode = page->mapping->host;
-	handle_t *handle = NULL;
-	struct ocfs2_dinode *di;
-
-	mlog_entry("(0x%p, 0x%p, %u, %u)\n", file, page, from, to);
-
-	/* NOTE: ocfs2_file_aio_write has ensured that it's safe for
-	 * us to continue here without rechecking the I/O against
-	 * changed inode values.
-	 *
-	 * 1) We're currently holding the inode alloc lock, so no
-	 *    nodes can change it underneath us.
-	 *
-	 * 2) We've had to take the metadata lock at least once
-	 *    already to check for extending writes, suid removal, etc.
-	 *    The meta data update code then ensures that we don't get a
-	 *    stale inode allocation image (i_size, i_clusters, etc).
-	 */
-
-	ret = ocfs2_meta_lock_with_page(inode, &di_bh, 1, page);
-	if (ret != 0) {
-		mlog_errno(ret);
-		goto out;
-	}
-
-	ret = ocfs2_data_lock_with_page(inode, 1, page);
-	if (ret != 0) {
-		mlog_errno(ret);
-		goto out_unlock_meta;
-	}
-
-	handle = ocfs2_start_walk_page_trans(inode, page, from, to);
-	if (IS_ERR(handle)) {
-		ret = PTR_ERR(handle);
-		goto out_unlock_data;
-	}
-
-	/* Mark our buffer early. We'd rather catch this error up here
-	 * as opposed to after a successful commit_write which would
-	 * require us to set back inode->i_size. */
-	ret = ocfs2_journal_access(handle, inode, di_bh,
-				   OCFS2_JOURNAL_ACCESS_WRITE);
-	if (ret < 0) {
-		mlog_errno(ret);
-		goto out_commit;
-	}
-
-	/* might update i_size */
-	ret = generic_commit_write(file, page, from, to);
-	if (ret < 0) {
-		mlog_errno(ret);
-		goto out_commit;
-	}
-
-	di = (struct ocfs2_dinode *)di_bh->b_data;
-
-	/* ocfs2_mark_inode_dirty() is too heavy to use here. */
-	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
-	di->i_mtime = di->i_ctime = cpu_to_le64(inode->i_mtime.tv_sec);
-	di->i_mtime_nsec = di->i_ctime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec);
-
-	inode->i_blocks = ocfs2_align_bytes_to_sectors((u64)(i_size_read(inode)));
-	di->i_size = cpu_to_le64((u64)i_size_read(inode));
-
-	ret = ocfs2_journal_dirty(handle, di_bh);
-	if (ret < 0) {
-		mlog_errno(ret);
-		goto out_commit;
-	}
-
-out_commit:
-	ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
-out_unlock_data:
-	ocfs2_data_unlock(inode, 1);
-out_unlock_meta:
-	ocfs2_meta_unlock(inode, 1);
-out:
-	if (di_bh)
-		brelse(di_bh);
-
-	mlog_exit(ret);
-	return ret;
-}
-
 static sector_t ocfs2_bmap(struct address_space *mapping, sector_t block)
 {
 	sector_t status;
@@ -499,8 +399,7 @@
 		down_read(&OCFS2_I(inode)->ip_alloc_sem);
 	}
 
-	err = ocfs2_extent_map_get_blocks(inode, block, 1, &p_blkno,
-					  NULL);
+	err = ocfs2_extent_map_get_blocks(inode, block, &p_blkno, NULL, NULL);
 
 	if (!INODE_JOURNAL(inode)) {
 		up_read(&OCFS2_I(inode)->ip_alloc_sem);
@@ -540,8 +439,8 @@
 				     struct buffer_head *bh_result, int create)
 {
 	int ret;
-	u64 p_blkno, inode_blocks;
-	int contig_blocks;
+	u64 p_blkno, inode_blocks, contig_blocks;
+	unsigned int ext_flags;
 	unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits;
 	unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits;
 
@@ -549,33 +448,20 @@
 	 * nicely aligned and of the right size, so there's no need
 	 * for us to check any of that. */
 
-	spin_lock(&OCFS2_I(inode)->ip_lock);
-	inode_blocks = ocfs2_clusters_to_blocks(inode->i_sb,
-						OCFS2_I(inode)->ip_clusters);
-
-	/*
-	 * For a read which begins past the end of file, we return a hole.
-	 */
-	if (!create && (iblock >= inode_blocks)) {
-		spin_unlock(&OCFS2_I(inode)->ip_lock);
-		ret = 0;
-		goto bail;
-	}
+	inode_blocks = ocfs2_blocks_for_bytes(inode->i_sb, i_size_read(inode));
 
 	/*
 	 * Any write past EOF is not allowed because we'd be extending.
 	 */
 	if (create && (iblock + max_blocks) > inode_blocks) {
-		spin_unlock(&OCFS2_I(inode)->ip_lock);
 		ret = -EIO;
 		goto bail;
 	}
-	spin_unlock(&OCFS2_I(inode)->ip_lock);
 
 	/* This figures out the size of the next contiguous block, and
 	 * our logical offset */
-	ret = ocfs2_extent_map_get_blocks(inode, iblock, 1, &p_blkno,
-					  &contig_blocks);
+	ret = ocfs2_extent_map_get_blocks(inode, iblock, &p_blkno,
+					  &contig_blocks, &ext_flags);
 	if (ret) {
 		mlog(ML_ERROR, "get_blocks() failed iblock=%llu\n",
 		     (unsigned long long)iblock);
@@ -583,7 +469,37 @@
 		goto bail;
 	}
 
-	map_bh(bh_result, inode->i_sb, p_blkno);
+	if (!ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb)) && !p_blkno) {
+		ocfs2_error(inode->i_sb,
+			    "Inode %llu has a hole at block %llu\n",
+			    (unsigned long long)OCFS2_I(inode)->ip_blkno,
+			    (unsigned long long)iblock);
+		ret = -EROFS;
+		goto bail;
+	}
+
+	/*
+	 * get_more_blocks() expects us to describe a hole by clearing
+	 * the mapped bit on bh_result().
+	 *
+	 * Consider an unwritten extent as a hole.
+	 */
+	if (p_blkno && !(ext_flags & OCFS2_EXT_UNWRITTEN))
+		map_bh(bh_result, inode->i_sb, p_blkno);
+	else {
+		/*
+		 * ocfs2_prepare_inode_for_write() should have caught
+		 * the case where we'd be filling a hole and triggered
+		 * a buffered write instead.
+		 */
+		if (create) {
+			ret = -EIO;
+			mlog_errno(ret);
+			goto bail;
+		}
+
+		clear_buffer_mapped(bh_result);
+	}
 
 	/* make sure we don't map more than max_blocks blocks here as
 	   that's all the kernel will handle at this point. */
@@ -606,12 +522,17 @@
 			     void *private)
 {
 	struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode;
+	int level;
 
 	/* this io's submitter should not have unlocked this before we could */
 	BUG_ON(!ocfs2_iocb_is_rw_locked(iocb));
+
 	ocfs2_iocb_clear_rw_locked(iocb);
-	up_read(&inode->i_alloc_sem);
-	ocfs2_rw_unlock(inode, 0);
+
+	level = ocfs2_iocb_rw_locked_level(iocb);
+	if (!level)
+		up_read(&inode->i_alloc_sem);
+	ocfs2_rw_unlock(inode, level);
 }
 
 /*
@@ -647,23 +568,27 @@
 
 	mlog_entry_void();
 
-	/*
-	 * We get PR data locks even for O_DIRECT.  This allows
-	 * concurrent O_DIRECT I/O but doesn't let O_DIRECT with
-	 * extending and buffered zeroing writes race.  If they did
-	 * race then the buffered zeroing could be written back after
-	 * the O_DIRECT I/O.  It's one thing to tell people not to mix
-	 * buffered and O_DIRECT writes, but expecting them to
-	 * understand that file extension is also an implicit buffered
-	 * write is too much.  By getting the PR we force writeback of
-	 * the buffered zeroing before proceeding.
-	 */
-	ret = ocfs2_data_lock(inode, 0);
-	if (ret < 0) {
-		mlog_errno(ret);
-		goto out;
+	if (!ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) {
+		/*
+		 * We get PR data locks even for O_DIRECT.  This
+		 * allows concurrent O_DIRECT I/O but doesn't let
+		 * O_DIRECT with extending and buffered zeroing writes
+		 * race.  If they did race then the buffered zeroing
+		 * could be written back after the O_DIRECT I/O.  It's
+		 * one thing to tell people not to mix buffered and
+		 * O_DIRECT writes, but expecting them to understand
+		 * that file extension is also an implicit buffered
+		 * write is too much.  By getting the PR we force
+		 * writeback of the buffered zeroing before
+		 * proceeding.
+		 */
+		ret = ocfs2_data_lock(inode, 0);
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto out;
+		}
+		ocfs2_data_unlock(inode, 0);
 	}
-	ocfs2_data_unlock(inode, 0);
 
 	ret = blockdev_direct_IO_no_locking(rw, iocb, inode,
 					    inode->i_sb->s_bdev, iov, offset,
@@ -675,11 +600,715 @@
 	return ret;
 }
 
+static void ocfs2_figure_cluster_boundaries(struct ocfs2_super *osb,
+					    u32 cpos,
+					    unsigned int *start,
+					    unsigned int *end)
+{
+	unsigned int cluster_start = 0, cluster_end = PAGE_CACHE_SIZE;
+
+	if (unlikely(PAGE_CACHE_SHIFT > osb->s_clustersize_bits)) {
+		unsigned int cpp;
+
+		cpp = 1 << (PAGE_CACHE_SHIFT - osb->s_clustersize_bits);
+
+		cluster_start = cpos % cpp;
+		cluster_start = cluster_start << osb->s_clustersize_bits;
+
+		cluster_end = cluster_start + osb->s_clustersize;
+	}
+
+	BUG_ON(cluster_start > PAGE_SIZE);
+	BUG_ON(cluster_end > PAGE_SIZE);
+
+	if (start)
+		*start = cluster_start;
+	if (end)
+		*end = cluster_end;
+}
+
+/*
+ * 'from' and 'to' are the region in the page to avoid zeroing.
+ *
+ * If pagesize > clustersize, this function will avoid zeroing outside
+ * of the cluster boundary.
+ *
+ * from == to == 0 is code for "zero the entire cluster region"
+ */
+static void ocfs2_clear_page_regions(struct page *page,
+				     struct ocfs2_super *osb, u32 cpos,
+				     unsigned from, unsigned to)
+{
+	void *kaddr;
+	unsigned int cluster_start, cluster_end;
+
+	ocfs2_figure_cluster_boundaries(osb, cpos, &cluster_start, &cluster_end);
+
+	kaddr = kmap_atomic(page, KM_USER0);
+
+	if (from || to) {
+		if (from > cluster_start)
+			memset(kaddr + cluster_start, 0, from - cluster_start);
+		if (to < cluster_end)
+			memset(kaddr + to, 0, cluster_end - to);
+	} else {
+		memset(kaddr + cluster_start, 0, cluster_end - cluster_start);
+	}
+
+	kunmap_atomic(kaddr, KM_USER0);
+}
+
+/*
+ * Some of this taken from block_prepare_write(). We already have our
+ * mapping by now though, and the entire write will be allocating or
+ * it won't, so not much need to use BH_New.
+ *
+ * This will also skip zeroing, which is handled externally.
+ */
+int ocfs2_map_page_blocks(struct page *page, u64 *p_blkno,
+			  struct inode *inode, unsigned int from,
+			  unsigned int to, int new)
+{
+	int ret = 0;
+	struct buffer_head *head, *bh, *wait[2], **wait_bh = wait;
+	unsigned int block_end, block_start;
+	unsigned int bsize = 1 << inode->i_blkbits;
+
+	if (!page_has_buffers(page))
+		create_empty_buffers(page, bsize, 0);
+
+	head = page_buffers(page);
+	for (bh = head, block_start = 0; bh != head || !block_start;
+	     bh = bh->b_this_page, block_start += bsize) {
+		block_end = block_start + bsize;
+
+		/*
+		 * Ignore blocks outside of our i/o range -
+		 * they may belong to unallocated clusters.
+		 */
+		if (block_start >= to || block_end <= from) {
+			if (PageUptodate(page))
+				set_buffer_uptodate(bh);
+			continue;
+		}
+
+		/*
+		 * For an allocating write with cluster size >= page
+		 * size, we always write the entire page.
+		 */
+
+		if (buffer_new(bh))
+			clear_buffer_new(bh);
+
+		if (!buffer_mapped(bh)) {
+			map_bh(bh, inode->i_sb, *p_blkno);
+			unmap_underlying_metadata(bh->b_bdev, bh->b_blocknr);
+		}
+
+		if (PageUptodate(page)) {
+			if (!buffer_uptodate(bh))
+				set_buffer_uptodate(bh);
+		} else if (!buffer_uptodate(bh) && !buffer_delay(bh) &&
+		     (block_start < from || block_end > to)) {
+			ll_rw_block(READ, 1, &bh);
+			*wait_bh++=bh;
+		}
+
+		*p_blkno = *p_blkno + 1;
+	}
+
+	/*
+	 * If we issued read requests - let them complete.
+	 */
+	while(wait_bh > wait) {
+		wait_on_buffer(*--wait_bh);
+		if (!buffer_uptodate(*wait_bh))
+			ret = -EIO;
+	}
+
+	if (ret == 0 || !new)
+		return ret;
+
+	/*
+	 * If we get -EIO above, zero out any newly allocated blocks
+	 * to avoid exposing stale data.
+	 */
+	bh = head;
+	block_start = 0;
+	do {
+		void *kaddr;
+
+		block_end = block_start + bsize;
+		if (block_end <= from)
+			goto next_bh;
+		if (block_start >= to)
+			break;
+
+		kaddr = kmap_atomic(page, KM_USER0);
+		memset(kaddr+block_start, 0, bh->b_size);
+		flush_dcache_page(page);
+		kunmap_atomic(kaddr, KM_USER0);
+		set_buffer_uptodate(bh);
+		mark_buffer_dirty(bh);
+
+next_bh:
+		block_start = block_end;
+		bh = bh->b_this_page;
+	} while (bh != head);
+
+	return ret;
+}
+
+/*
+ * This will copy user data from the buffer page in the splice
+ * context.
+ *
+ * For now, we ignore SPLICE_F_MOVE as that would require some extra
+ * communication out all the way to ocfs2_write().
+ */
+int ocfs2_map_and_write_splice_data(struct inode *inode,
+				  struct ocfs2_write_ctxt *wc, u64 *p_blkno,
+				  unsigned int *ret_from, unsigned int *ret_to)
+{
+	int ret;
+	unsigned int to, from, cluster_start, cluster_end;
+	char *src, *dst;
+	struct ocfs2_splice_write_priv *sp = wc->w_private;
+	struct pipe_buffer *buf = sp->s_buf;
+	unsigned long bytes, src_from;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	ocfs2_figure_cluster_boundaries(osb, wc->w_cpos, &cluster_start,
+					&cluster_end);
+
+	from = sp->s_offset;
+	src_from = sp->s_buf_offset;
+	bytes = wc->w_count;
+
+	if (wc->w_large_pages) {
+		/*
+		 * For cluster size < page size, we have to
+		 * calculate pos within the cluster and obey
+		 * the rightmost boundary.
+		 */
+		bytes = min(bytes, (unsigned long)(osb->s_clustersize
+				   - (wc->w_pos & (osb->s_clustersize - 1))));
+	}
+	to = from + bytes;
+
+	if (wc->w_this_page_new)
+		ret = ocfs2_map_page_blocks(wc->w_this_page, p_blkno, inode,
+					    cluster_start, cluster_end, 1);
+	else
+		ret = ocfs2_map_page_blocks(wc->w_this_page, p_blkno, inode,
+					    from, to, 0);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	BUG_ON(from > PAGE_CACHE_SIZE);
+	BUG_ON(to > PAGE_CACHE_SIZE);
+	BUG_ON(from > osb->s_clustersize);
+	BUG_ON(to > osb->s_clustersize);
+
+	src = buf->ops->map(sp->s_pipe, buf, 1);
+	dst = kmap_atomic(wc->w_this_page, KM_USER1);
+	memcpy(dst + from, src + src_from, bytes);
+	kunmap_atomic(wc->w_this_page, KM_USER1);
+	buf->ops->unmap(sp->s_pipe, buf, src);
+
+	wc->w_finished_copy = 1;
+
+	*ret_from = from;
+	*ret_to = to;
+out:
+
+	return bytes ? (unsigned int)bytes : ret;
+}
+
+/*
+ * This will copy user data from the iovec in the buffered write
+ * context.
+ */
+int ocfs2_map_and_write_user_data(struct inode *inode,
+				  struct ocfs2_write_ctxt *wc, u64 *p_blkno,
+				  unsigned int *ret_from, unsigned int *ret_to)
+{
+	int ret;
+	unsigned int to, from, cluster_start, cluster_end;
+	unsigned long bytes, src_from;
+	char *dst;
+	struct ocfs2_buffered_write_priv *bp = wc->w_private;
+	const struct iovec *cur_iov = bp->b_cur_iov;
+	char __user *buf;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	ocfs2_figure_cluster_boundaries(osb, wc->w_cpos, &cluster_start,
+					&cluster_end);
+
+	buf = cur_iov->iov_base + bp->b_cur_off;
+	src_from = (unsigned long)buf & ~PAGE_CACHE_MASK;
+
+	from = wc->w_pos & (PAGE_CACHE_SIZE - 1);
+
+	/*
+	 * This is a lot of comparisons, but it reads quite
+	 * easily, which is important here.
+	 */
+	/* Stay within the src page */
+	bytes = PAGE_SIZE - src_from;
+	/* Stay within the vector */
+	bytes = min(bytes,
+		    (unsigned long)(cur_iov->iov_len - bp->b_cur_off));
+	/* Stay within count */
+	bytes = min(bytes, (unsigned long)wc->w_count);
+	/*
+	 * For clustersize > page size, just stay within
+	 * target page, otherwise we have to calculate pos
+	 * within the cluster and obey the rightmost
+	 * boundary.
+	 */
+	if (wc->w_large_pages) {
+		/*
+		 * For cluster size < page size, we have to
+		 * calculate pos within the cluster and obey
+		 * the rightmost boundary.
+		 */
+		bytes = min(bytes, (unsigned long)(osb->s_clustersize
+				   - (wc->w_pos & (osb->s_clustersize - 1))));
+	} else {
+		/*
+		 * cluster size > page size is the most common
+		 * case - we just stay within the target page
+		 * boundary.
+		 */
+		bytes = min(bytes, PAGE_CACHE_SIZE - from);
+	}
+
+	to = from + bytes;
+
+	if (wc->w_this_page_new)
+		ret = ocfs2_map_page_blocks(wc->w_this_page, p_blkno, inode,
+					    cluster_start, cluster_end, 1);
+	else
+		ret = ocfs2_map_page_blocks(wc->w_this_page, p_blkno, inode,
+					    from, to, 0);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	BUG_ON(from > PAGE_CACHE_SIZE);
+	BUG_ON(to > PAGE_CACHE_SIZE);
+	BUG_ON(from > osb->s_clustersize);
+	BUG_ON(to > osb->s_clustersize);
+
+	dst = kmap(wc->w_this_page);
+	memcpy(dst + from, bp->b_src_buf + src_from, bytes);
+	kunmap(wc->w_this_page);
+
+	/*
+	 * XXX: This is slow, but simple. The caller of
+	 * ocfs2_buffered_write_cluster() is responsible for
+	 * passing through the iovecs, so it's difficult to
+	 * predict what our next step is in here after our
+	 * initial write. A future version should be pushing
+	 * that iovec manipulation further down.
+	 *
+	 * By setting this, we indicate that a copy from user
+	 * data was done, and subsequent calls for this
+	 * cluster will skip copying more data.
+	 */
+	wc->w_finished_copy = 1;
+
+	*ret_from = from;
+	*ret_to = to;
+out:
+
+	return bytes ? (unsigned int)bytes : ret;
+}
+
+/*
+ * Map, fill and write a page to disk.
+ *
+ * The work of copying data is done via callback.  Newly allocated
+ * pages which don't take user data will be zero'd (set 'new' to
+ * indicate an allocating write)
+ *
+ * Returns a negative error code or the number of bytes copied into
+ * the page.
+ */
+int ocfs2_write_data_page(struct inode *inode, handle_t *handle,
+			  u64 *p_blkno, struct page *page,
+			  struct ocfs2_write_ctxt *wc, int new)
+{
+	int ret, copied = 0;
+	unsigned int from = 0, to = 0;
+	unsigned int cluster_start, cluster_end;
+	unsigned int zero_from = 0, zero_to = 0;
+
+	ocfs2_figure_cluster_boundaries(OCFS2_SB(inode->i_sb), wc->w_cpos,
+					&cluster_start, &cluster_end);
+
+	if ((wc->w_pos >> PAGE_CACHE_SHIFT) == page->index
+	    && !wc->w_finished_copy) {
+
+		wc->w_this_page = page;
+		wc->w_this_page_new = new;
+		ret = wc->w_write_data_page(inode, wc, p_blkno, &from, &to);
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		copied = ret;
+
+		zero_from = from;
+		zero_to = to;
+		if (new) {
+			from = cluster_start;
+			to = cluster_end;
+		}
+	} else {
+		/*
+		 * If we haven't allocated the new page yet, we
+		 * shouldn't be writing it out without copying user
+		 * data. This is likely a math error from the caller.
+		 */
+		BUG_ON(!new);
+
+		from = cluster_start;
+		to = cluster_end;
+
+		ret = ocfs2_map_page_blocks(page, p_blkno, inode,
+					    cluster_start, cluster_end, 1);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+	}
+
+	/*
+	 * Parts of newly allocated pages need to be zero'd.
+	 *
+	 * Above, we have also rewritten 'to' and 'from' - as far as
+	 * the rest of the function is concerned, the entire cluster
+	 * range inside of a page needs to be written.
+	 *
+	 * We can skip this if the page is up to date - it's already
+	 * been zero'd from being read in as a hole.
+	 */
+	if (new && !PageUptodate(page))
+		ocfs2_clear_page_regions(page, OCFS2_SB(inode->i_sb),
+					 wc->w_cpos, zero_from, zero_to);
+
+	flush_dcache_page(page);
+
+	if (ocfs2_should_order_data(inode)) {
+		ret = walk_page_buffers(handle,
+					page_buffers(page),
+					from, to, NULL,
+					ocfs2_journal_dirty_data);
+		if (ret < 0)
+			mlog_errno(ret);
+	}
+
+	/*
+	 * We don't use generic_commit_write() because we need to
+	 * handle our own i_size update.
+	 */
+	ret = block_commit_write(page, from, to);
+	if (ret)
+		mlog_errno(ret);
+out:
+
+	return copied ? copied : ret;
+}
+
+/*
+ * Do the actual write of some data into an inode. Optionally allocate
+ * in order to fulfill the write.
+ *
+ * cpos is the logical cluster offset within the file to write at
+ *
+ * 'phys' is the physical mapping of that offset. a 'phys' value of
+ * zero indicates that allocation is required. In this case, data_ac
+ * and meta_ac should be valid (meta_ac can be null if metadata
+ * allocation isn't required).
+ */
+static ssize_t ocfs2_write(struct file *file, u32 phys, handle_t *handle,
+			   struct buffer_head *di_bh,
+			   struct ocfs2_alloc_context *data_ac,
+			   struct ocfs2_alloc_context *meta_ac,
+			   struct ocfs2_write_ctxt *wc)
+{
+	int ret, i, numpages = 1, new;
+	unsigned int copied = 0;
+	u32 tmp_pos;
+	u64 v_blkno, p_blkno;
+	struct address_space *mapping = file->f_mapping;
+	struct inode *inode = mapping->host;
+	unsigned long index, start;
+	struct page **cpages;
+
+	new = phys == 0 ? 1 : 0;
+
+	/*
+	 * Figure out how many pages we'll be manipulating here. For
+	 * non allocating write, we just change the one
+	 * page. Otherwise, we'll need a whole clusters worth.
+	 */
+	if (new)
+		numpages = ocfs2_pages_per_cluster(inode->i_sb);
+
+	cpages = kzalloc(sizeof(*cpages) * numpages, GFP_NOFS);
+	if (!cpages) {
+		ret = -ENOMEM;
+		mlog_errno(ret);
+		return ret;
+	}
+
+	/*
+	 * Fill our page array first. That way we've grabbed enough so
+	 * that we can zero and flush if we error after adding the
+	 * extent.
+	 */
+	if (new) {
+		start = ocfs2_align_clusters_to_page_index(inode->i_sb,
+							   wc->w_cpos);
+		v_blkno = ocfs2_clusters_to_blocks(inode->i_sb, wc->w_cpos);
+	} else {
+		start = wc->w_pos >> PAGE_CACHE_SHIFT;
+		v_blkno = wc->w_pos >> inode->i_sb->s_blocksize_bits;
+	}
+
+	for(i = 0; i < numpages; i++) {
+		index = start + i;
+
+		cpages[i] = grab_cache_page(mapping, index);
+		if (!cpages[i]) {
+			ret = -ENOMEM;
+			mlog_errno(ret);
+			goto out;
+		}
+	}
+
+	if (new) {
+		/*
+		 * This is safe to call with the page locks - it won't take
+		 * any additional semaphores or cluster locks.
+		 */
+		tmp_pos = wc->w_cpos;
+		ret = ocfs2_do_extend_allocation(OCFS2_SB(inode->i_sb), inode,
+						 &tmp_pos, 1, di_bh, handle,
+						 data_ac, meta_ac, NULL);
+		/*
+		 * This shouldn't happen because we must have already
+		 * calculated the correct meta data allocation required. The
+		 * internal tree allocation code should know how to increase
+		 * transaction credits itself.
+		 *
+		 * If need be, we could handle -EAGAIN for a
+		 * RESTART_TRANS here.
+		 */
+		mlog_bug_on_msg(ret == -EAGAIN,
+				"Inode %llu: EAGAIN return during allocation.\n",
+				(unsigned long long)OCFS2_I(inode)->ip_blkno);
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto out;
+		}
+	}
+
+	ret = ocfs2_extent_map_get_blocks(inode, v_blkno, &p_blkno, NULL,
+					  NULL);
+	if (ret < 0) {
+
+		/*
+		 * XXX: Should we go readonly here?
+		 */
+
+		mlog_errno(ret);
+		goto out;
+	}
+
+	BUG_ON(p_blkno == 0);
+
+	for(i = 0; i < numpages; i++) {
+		ret = ocfs2_write_data_page(inode, handle, &p_blkno, cpages[i],
+					    wc, new);
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		copied += ret;
+	}
+
+out:
+	for(i = 0; i < numpages; i++) {
+		unlock_page(cpages[i]);
+		mark_page_accessed(cpages[i]);
+		page_cache_release(cpages[i]);
+	}
+	kfree(cpages);
+
+	return copied ? copied : ret;
+}
+
+static void ocfs2_write_ctxt_init(struct ocfs2_write_ctxt *wc,
+				  struct ocfs2_super *osb, loff_t pos,
+				  size_t count, ocfs2_page_writer *cb,
+				  void *cb_priv)
+{
+	wc->w_count = count;
+	wc->w_pos = pos;
+	wc->w_cpos = wc->w_pos >> osb->s_clustersize_bits;
+	wc->w_finished_copy = 0;
+
+	if (unlikely(PAGE_CACHE_SHIFT > osb->s_clustersize_bits))
+		wc->w_large_pages = 1;
+	else
+		wc->w_large_pages = 0;
+
+	wc->w_write_data_page = cb;
+	wc->w_private = cb_priv;
+}
+
+/*
+ * Write a cluster to an inode. The cluster may not be allocated yet,
+ * in which case it will be. This only exists for buffered writes -
+ * O_DIRECT takes a more "traditional" path through the kernel.
+ *
+ * The caller is responsible for incrementing pos, written counts, etc
+ *
+ * For file systems that don't support sparse files, pre-allocation
+ * and page zeroing up until cpos should be done prior to this
+ * function call.
+ *
+ * Callers should be holding i_sem, and the rw cluster lock.
+ *
+ * Returns the number of user bytes written, or less than zero for
+ * error.
+ */
+ssize_t ocfs2_buffered_write_cluster(struct file *file, loff_t pos,
+				     size_t count, ocfs2_page_writer *actor,
+				     void *priv)
+{
+	int ret, credits = OCFS2_INODE_UPDATE_CREDITS;
+	ssize_t written = 0;
+	u32 phys;
+	struct inode *inode = file->f_mapping->host;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct buffer_head *di_bh = NULL;
+	struct ocfs2_dinode *di;
+	struct ocfs2_alloc_context *data_ac = NULL;
+	struct ocfs2_alloc_context *meta_ac = NULL;
+	handle_t *handle;
+	struct ocfs2_write_ctxt wc;
+
+	ocfs2_write_ctxt_init(&wc, osb, pos, count, actor, priv);
+
+	ret = ocfs2_meta_lock(inode, &di_bh, 1);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+	di = (struct ocfs2_dinode *)di_bh->b_data;
+
+	/*
+	 * Take alloc sem here to prevent concurrent lookups. That way
+	 * the mapping, zeroing and tree manipulation within
+	 * ocfs2_write() will be safe against ->readpage(). This
+	 * should also serve to lock out allocation from a shared
+	 * writeable region.
+	 */
+	down_write(&OCFS2_I(inode)->ip_alloc_sem);
+
+	ret = ocfs2_get_clusters(inode, wc.w_cpos, &phys, NULL, NULL);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_meta;
+	}
+
+	/* phys == 0 means that allocation is required. */
+	if (phys == 0) {
+		ret = ocfs2_lock_allocators(inode, di, 1, &data_ac, &meta_ac);
+		if (ret) {
+			mlog_errno(ret);
+			goto out_meta;
+		}
+
+		credits = ocfs2_calc_extend_credits(inode->i_sb, di, 1);
+	}
+
+	ret = ocfs2_data_lock(inode, 1);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_meta;
+	}
+
+	handle = ocfs2_start_trans(osb, credits);
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
+		mlog_errno(ret);
+		goto out_data;
+	}
+
+	written = ocfs2_write(file, phys, handle, di_bh, data_ac,
+			      meta_ac, &wc);
+	if (written < 0) {
+		ret = written;
+		mlog_errno(ret);
+		goto out_commit;
+	}
+
+	ret = ocfs2_journal_access(handle, inode, di_bh,
+				   OCFS2_JOURNAL_ACCESS_WRITE);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_commit;
+	}
+
+	pos += written;
+	if (pos > inode->i_size) {
+		i_size_write(inode, pos);
+		mark_inode_dirty(inode);
+	}
+	inode->i_blocks = ocfs2_inode_sector_count(inode);
+	di->i_size = cpu_to_le64((u64)i_size_read(inode));
+	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	di->i_mtime = di->i_ctime = cpu_to_le64(inode->i_mtime.tv_sec);
+	di->i_mtime_nsec = di->i_ctime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec);
+
+	ret = ocfs2_journal_dirty(handle, di_bh);
+	if (ret)
+		mlog_errno(ret);
+
+out_commit:
+	ocfs2_commit_trans(osb, handle);
+
+out_data:
+	ocfs2_data_unlock(inode, 1);
+
+out_meta:
+	up_write(&OCFS2_I(inode)->ip_alloc_sem);
+	ocfs2_meta_unlock(inode, 1);
+
+out:
+	brelse(di_bh);
+	if (data_ac)
+		ocfs2_free_alloc_context(data_ac);
+	if (meta_ac)
+		ocfs2_free_alloc_context(meta_ac);
+
+	return written ? written : ret;
+}
+
 const struct address_space_operations ocfs2_aops = {
 	.readpage	= ocfs2_readpage,
 	.writepage	= ocfs2_writepage,
-	.prepare_write	= ocfs2_prepare_write,
-	.commit_write	= ocfs2_commit_write,
 	.bmap		= ocfs2_bmap,
 	.sync_page	= block_sync_page,
 	.direct_IO	= ocfs2_direct_IO,
diff --git a/fs/ocfs2/aops.h b/fs/ocfs2/aops.h
index f446a15..45821d4 100644
--- a/fs/ocfs2/aops.h
+++ b/fs/ocfs2/aops.h
@@ -30,12 +30,83 @@
 							 unsigned from,
 							 unsigned to);
 
+int ocfs2_map_page_blocks(struct page *page, u64 *p_blkno,
+			  struct inode *inode, unsigned int from,
+			  unsigned int to, int new);
+
+int walk_page_buffers(	handle_t *handle,
+			struct buffer_head *head,
+			unsigned from,
+			unsigned to,
+			int *partial,
+			int (*fn)(	handle_t *handle,
+					struct buffer_head *bh));
+
+struct ocfs2_write_ctxt;
+typedef int (ocfs2_page_writer)(struct inode *, struct ocfs2_write_ctxt *,
+				u64 *, unsigned int *, unsigned int *);
+
+ssize_t ocfs2_buffered_write_cluster(struct file *file, loff_t pos,
+				     size_t count, ocfs2_page_writer *actor,
+				     void *priv);
+
+struct ocfs2_write_ctxt {
+	size_t				w_count;
+	loff_t				w_pos;
+	u32				w_cpos;
+	unsigned int			w_finished_copy;
+
+	/* This is true if page_size > cluster_size */
+	unsigned int			w_large_pages;
+
+	/* Filler callback and private data */
+	ocfs2_page_writer		*w_write_data_page;
+	void				*w_private;
+
+	/* Only valid for the filler callback */
+	struct page			*w_this_page;
+	unsigned int			w_this_page_new;
+};
+
+struct ocfs2_buffered_write_priv {
+	char				*b_src_buf;
+	const struct iovec		*b_cur_iov; /* Current iovec */
+	size_t				b_cur_off; /* Offset in the
+						    * current iovec */
+};
+int ocfs2_map_and_write_user_data(struct inode *inode,
+				  struct ocfs2_write_ctxt *wc,
+				  u64 *p_blkno,
+				  unsigned int *ret_from,
+				  unsigned int *ret_to);
+
+struct ocfs2_splice_write_priv {
+	struct splice_desc		*s_sd;
+	struct pipe_buffer		*s_buf;
+	struct pipe_inode_info		*s_pipe;
+	/* Neither offset value is ever larger than one page */
+	unsigned int			s_offset;
+	unsigned int			s_buf_offset;
+};
+int ocfs2_map_and_write_splice_data(struct inode *inode,
+				    struct ocfs2_write_ctxt *wc,
+				    u64 *p_blkno,
+				    unsigned int *ret_from,
+				    unsigned int *ret_to);
+
 /* all ocfs2_dio_end_io()'s fault */
 #define ocfs2_iocb_is_rw_locked(iocb) \
 	test_bit(0, (unsigned long *)&iocb->private)
-#define ocfs2_iocb_set_rw_locked(iocb) \
-	set_bit(0, (unsigned long *)&iocb->private)
+static inline void ocfs2_iocb_set_rw_locked(struct kiocb *iocb, int level)
+{
+	set_bit(0, (unsigned long *)&iocb->private);
+	if (level)
+		set_bit(1, (unsigned long *)&iocb->private);
+	else
+		clear_bit(1, (unsigned long *)&iocb->private);
+}
 #define ocfs2_iocb_clear_rw_locked(iocb) \
 	clear_bit(0, (unsigned long *)&iocb->private)
-
+#define ocfs2_iocb_rw_locked_level(iocb) \
+	test_bit(1, (unsigned long *)&iocb->private)
 #endif /* OCFS2_FILE_H */
diff --git a/fs/ocfs2/cluster/quorum.c b/fs/ocfs2/cluster/quorum.c
index 4705d65..bbacf7d 100644
--- a/fs/ocfs2/cluster/quorum.c
+++ b/fs/ocfs2/cluster/quorum.c
@@ -46,6 +46,7 @@
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
+#include <linux/reboot.h>
 
 #include "heartbeat.h"
 #include "nodemanager.h"
@@ -72,7 +73,9 @@
 	/* panic spins with interrupts enabled.  with preempt
 	 * threads can still schedule, etc, etc */
 	o2hb_stop_all_regions();
-	panic("ocfs2 is very sorry to be fencing this system by panicing\n");
+
+	printk("ocfs2 is very sorry to be fencing this system by restarting\n");
+	emergency_restart();
 }
 
 /* Indicate that a timeout occured on a hearbeat region write. The
diff --git a/fs/ocfs2/cluster/tcp_internal.h b/fs/ocfs2/cluster/tcp_internal.h
index 4dae5df..9606111 100644
--- a/fs/ocfs2/cluster/tcp_internal.h
+++ b/fs/ocfs2/cluster/tcp_internal.h
@@ -38,6 +38,9 @@
  * locking semantics of the file system using the protocol.  It should 
  * be somewhere else, I'm sure, but right now it isn't.
  *
+ * New in version 8:
+ * 	- Replace delete inode votes with a cluster lock
+ *
  * New in version 7:
  * 	- DLM join domain includes the live nodemap
  *
@@ -57,7 +60,7 @@
  * 	- full 64 bit i_size in the metadata lock lvbs
  * 	- introduction of "rw" lock and pushing meta/data locking down
  */
-#define O2NET_PROTOCOL_VERSION 7ULL
+#define O2NET_PROTOCOL_VERSION 8ULL
 struct o2net_handshake {
 	__be64	protocol_version;
 	__be64	connector_id;
diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c
index 66821e1..67e6866 100644
--- a/fs/ocfs2/dir.c
+++ b/fs/ocfs2/dir.c
@@ -358,15 +358,17 @@
 {
 	int status;
 	int extend;
-	u64 p_blkno;
+	u64 p_blkno, v_blkno;
 
 	spin_lock(&OCFS2_I(dir)->ip_lock);
 	extend = (i_size_read(dir) == ocfs2_clusters_to_bytes(sb, OCFS2_I(dir)->ip_clusters));
 	spin_unlock(&OCFS2_I(dir)->ip_lock);
 
 	if (extend) {
-		status = ocfs2_do_extend_allocation(OCFS2_SB(sb), dir, 1,
-						    parent_fe_bh, handle,
+		u32 offset = OCFS2_I(dir)->ip_clusters;
+
+		status = ocfs2_do_extend_allocation(OCFS2_SB(sb), dir, &offset,
+						    1, parent_fe_bh, handle,
 						    data_ac, meta_ac, NULL);
 		BUG_ON(status == -EAGAIN);
 		if (status < 0) {
@@ -375,9 +377,8 @@
 		}
 	}
 
-	status = ocfs2_extent_map_get_blocks(dir, (dir->i_blocks >>
-						   (sb->s_blocksize_bits - 9)),
-					     1, &p_blkno, NULL);
+	v_blkno = ocfs2_blocks_for_bytes(sb, i_size_read(dir));
+	status = ocfs2_extent_map_get_blocks(dir, v_blkno, &p_blkno, NULL, NULL);
 	if (status < 0) {
 		mlog_errno(status);
 		goto bail;
@@ -486,7 +487,7 @@
 
 	dir_i_size += dir->i_sb->s_blocksize;
 	i_size_write(dir, dir_i_size);
-	dir->i_blocks = ocfs2_align_bytes_to_sectors(dir_i_size);
+	dir->i_blocks = ocfs2_inode_sector_count(dir);
 	status = ocfs2_mark_inode_dirty(handle, dir, parent_fe_bh);
 	if (status < 0) {
 		mlog_errno(status);
diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c
index c558442..d836b98 100644
--- a/fs/ocfs2/dlm/dlmdomain.c
+++ b/fs/ocfs2/dlm/dlmdomain.c
@@ -430,11 +430,10 @@
 
 			dlm_lockres_put(res);
 
-			cond_resched_lock(&dlm->spinlock);
-
 			if (dropped)
 				goto redo_bucket;
 		}
+		cond_resched_lock(&dlm->spinlock);
 		num += n;
 		mlog(0, "%s: touched %d lockreses in bucket %d "
 		     "(tot=%d)\n", dlm->name, n, i, num);
@@ -1035,7 +1034,7 @@
 {
 	int status = 0, tmpstat, node;
 	struct domain_join_ctxt *ctxt;
-	enum dlm_query_join_response response;
+	enum dlm_query_join_response response = JOIN_DISALLOW;
 
 	mlog_entry("%p", dlm);
 
diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c
index 6d4a83d..c1807a4 100644
--- a/fs/ocfs2/dlm/dlmrecovery.c
+++ b/fs/ocfs2/dlm/dlmrecovery.c
@@ -611,6 +611,7 @@
 			}
 		} while (status != 0);
 
+		spin_lock(&dlm_reco_state_lock);
 		switch (ndata->state) {
 			case DLM_RECO_NODE_DATA_INIT:
 			case DLM_RECO_NODE_DATA_FINALIZE_SENT:
@@ -641,6 +642,7 @@
 				     ndata->node_num, dead_node);
 				break;
 		}
+		spin_unlock(&dlm_reco_state_lock);
 	}
 
 	mlog(0, "done requesting all lock info\n");
diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
index e335541..27e43b0 100644
--- a/fs/ocfs2/dlmglue.c
+++ b/fs/ocfs2/dlmglue.c
@@ -225,11 +225,17 @@
 	.flags		= 0,
 };
 
+static struct ocfs2_lock_res_ops ocfs2_inode_open_lops = {
+	.get_osb	= ocfs2_get_inode_osb,
+	.flags		= 0,
+};
+
 static inline int ocfs2_is_inode_lock(struct ocfs2_lock_res *lockres)
 {
 	return lockres->l_type == OCFS2_LOCK_TYPE_META ||
 		lockres->l_type == OCFS2_LOCK_TYPE_DATA ||
-		lockres->l_type == OCFS2_LOCK_TYPE_RW;
+		lockres->l_type == OCFS2_LOCK_TYPE_RW ||
+		lockres->l_type == OCFS2_LOCK_TYPE_OPEN;
 }
 
 static inline struct inode *ocfs2_lock_res_inode(struct ocfs2_lock_res *lockres)
@@ -373,6 +379,9 @@
 		case OCFS2_LOCK_TYPE_DATA:
 			ops = &ocfs2_inode_data_lops;
 			break;
+		case OCFS2_LOCK_TYPE_OPEN:
+			ops = &ocfs2_inode_open_lops;
+			break;
 		default:
 			mlog_bug_on_msg(1, "type: %d\n", type);
 			ops = NULL; /* thanks, gcc */
@@ -1129,6 +1138,12 @@
 		goto bail;
 	}
 
+	ret = ocfs2_create_new_lock(osb, &OCFS2_I(inode)->ip_open_lockres, 0, 0);
+	if (ret) {
+		mlog_errno(ret);
+		goto bail;
+	}
+
 bail:
 	mlog_exit(ret);
 	return ret;
@@ -1182,6 +1197,99 @@
 	mlog_exit_void();
 }
 
+/*
+ * ocfs2_open_lock always get PR mode lock.
+ */
+int ocfs2_open_lock(struct inode *inode)
+{
+	int status = 0;
+	struct ocfs2_lock_res *lockres;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	BUG_ON(!inode);
+
+	mlog_entry_void();
+
+	mlog(0, "inode %llu take PRMODE open lock\n",
+	     (unsigned long long)OCFS2_I(inode)->ip_blkno);
+
+	if (ocfs2_mount_local(osb))
+		goto out;
+
+	lockres = &OCFS2_I(inode)->ip_open_lockres;
+
+	status = ocfs2_cluster_lock(OCFS2_SB(inode->i_sb), lockres,
+				    LKM_PRMODE, 0, 0);
+	if (status < 0)
+		mlog_errno(status);
+
+out:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_try_open_lock(struct inode *inode, int write)
+{
+	int status = 0, level;
+	struct ocfs2_lock_res *lockres;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	BUG_ON(!inode);
+
+	mlog_entry_void();
+
+	mlog(0, "inode %llu try to take %s open lock\n",
+	     (unsigned long long)OCFS2_I(inode)->ip_blkno,
+	     write ? "EXMODE" : "PRMODE");
+
+	if (ocfs2_mount_local(osb))
+		goto out;
+
+	lockres = &OCFS2_I(inode)->ip_open_lockres;
+
+	level = write ? LKM_EXMODE : LKM_PRMODE;
+
+	/*
+	 * The file system may already holding a PRMODE/EXMODE open lock.
+	 * Since we pass LKM_NOQUEUE, the request won't block waiting on
+	 * other nodes and the -EAGAIN will indicate to the caller that
+	 * this inode is still in use.
+	 */
+	status = ocfs2_cluster_lock(OCFS2_SB(inode->i_sb), lockres,
+				    level, LKM_NOQUEUE, 0);
+
+out:
+	mlog_exit(status);
+	return status;
+}
+
+/*
+ * ocfs2_open_unlock unlock PR and EX mode open locks.
+ */
+void ocfs2_open_unlock(struct inode *inode)
+{
+	struct ocfs2_lock_res *lockres = &OCFS2_I(inode)->ip_open_lockres;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	mlog_entry_void();
+
+	mlog(0, "inode %llu drop open lock\n",
+	     (unsigned long long)OCFS2_I(inode)->ip_blkno);
+
+	if (ocfs2_mount_local(osb))
+		goto out;
+
+	if(lockres->l_ro_holders)
+		ocfs2_cluster_unlock(OCFS2_SB(inode->i_sb), lockres,
+				     LKM_PRMODE);
+	if(lockres->l_ex_holders)
+		ocfs2_cluster_unlock(OCFS2_SB(inode->i_sb), lockres,
+				     LKM_EXMODE);
+
+out:
+	mlog_exit_void();
+}
+
 int ocfs2_data_lock_full(struct inode *inode,
 			 int write,
 			 int arg_flags)
@@ -1387,8 +1495,7 @@
 	if (S_ISLNK(inode->i_mode) && !oi->ip_clusters)
 		inode->i_blocks = 0;
 	else
-		inode->i_blocks =
-			ocfs2_align_bytes_to_sectors(i_size_read(inode));
+		inode->i_blocks = ocfs2_inode_sector_count(inode);
 
 	inode->i_uid     = be32_to_cpu(lvb->lvb_iuid);
 	inode->i_gid     = be32_to_cpu(lvb->lvb_igid);
@@ -1479,12 +1586,15 @@
 {
 	int status = 0;
 	struct ocfs2_inode_info *oi = OCFS2_I(inode);
-	struct ocfs2_lock_res *lockres = NULL;
+	struct ocfs2_lock_res *lockres = &oi->ip_meta_lockres;
 	struct ocfs2_dinode *fe;
 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
 
 	mlog_entry_void();
 
+	if (ocfs2_mount_local(osb))
+		goto bail;
+
 	spin_lock(&oi->ip_lock);
 	if (oi->ip_flags & OCFS2_INODE_DELETED) {
 		mlog(0, "Orphaned inode %llu was deleted while we "
@@ -1496,22 +1606,16 @@
 	}
 	spin_unlock(&oi->ip_lock);
 
-	if (!ocfs2_mount_local(osb)) {
-		lockres = &oi->ip_meta_lockres;
-
-		if (!ocfs2_should_refresh_lock_res(lockres))
-			goto bail;
-	}
+	if (!ocfs2_should_refresh_lock_res(lockres))
+		goto bail;
 
 	/* This will discard any caching information we might have had
 	 * for the inode metadata. */
 	ocfs2_metadata_cache_purge(inode);
 
-	/* will do nothing for inode types that don't use the extent
-	 * map (directories, bitmap files, etc) */
 	ocfs2_extent_map_trunc(inode, 0);
 
-	if (lockres && ocfs2_meta_lvb_is_trustable(inode, lockres)) {
+	if (ocfs2_meta_lvb_is_trustable(inode, lockres)) {
 		mlog(0, "Trusting LVB on inode %llu\n",
 		     (unsigned long long)oi->ip_blkno);
 		ocfs2_refresh_inode_from_lvb(inode);
@@ -1558,8 +1662,7 @@
 
 	status = 0;
 bail_refresh:
-	if (lockres)
-		ocfs2_complete_lock_res_refresh(lockres, status);
+	ocfs2_complete_lock_res_refresh(lockres, status);
 bail:
 	mlog_exit(status);
 	return status;
@@ -1630,7 +1733,6 @@
 		wait_event(osb->recovery_event,
 			   ocfs2_node_map_is_empty(osb, &osb->recovery_map));
 
-	acquired = 0;
 	lockres = &OCFS2_I(inode)->ip_meta_lockres;
 	level = ex ? LKM_EXMODE : LKM_PRMODE;
 	dlm_flags = 0;
@@ -2458,13 +2560,20 @@
 	 * ocfs2_clear_inode has done it for us. */
 
 	err = ocfs2_drop_lock(OCFS2_SB(inode->i_sb),
-			      &OCFS2_I(inode)->ip_data_lockres);
+			      &OCFS2_I(inode)->ip_open_lockres);
 	if (err < 0)
 		mlog_errno(err);
 
 	status = err;
 
 	err = ocfs2_drop_lock(OCFS2_SB(inode->i_sb),
+			      &OCFS2_I(inode)->ip_data_lockres);
+	if (err < 0)
+		mlog_errno(err);
+	if (err < 0 && !status)
+		status = err;
+
+	err = ocfs2_drop_lock(OCFS2_SB(inode->i_sb),
 			      &OCFS2_I(inode)->ip_meta_lockres);
 	if (err < 0)
 		mlog_errno(err);
diff --git a/fs/ocfs2/dlmglue.h b/fs/ocfs2/dlmglue.h
index c343fca..59cb566 100644
--- a/fs/ocfs2/dlmglue.h
+++ b/fs/ocfs2/dlmglue.h
@@ -80,6 +80,9 @@
 		       int write);
 int ocfs2_rw_lock(struct inode *inode, int write);
 void ocfs2_rw_unlock(struct inode *inode, int write);
+int ocfs2_open_lock(struct inode *inode);
+int ocfs2_try_open_lock(struct inode *inode, int write);
+void ocfs2_open_unlock(struct inode *inode);
 int ocfs2_meta_lock_atime(struct inode *inode,
 			  struct vfsmount *vfsmnt,
 			  int *level);
diff --git a/fs/ocfs2/extent_map.c b/fs/ocfs2/extent_map.c
index 80ac69f..ba2b2ab 100644
--- a/fs/ocfs2/extent_map.c
+++ b/fs/ocfs2/extent_map.c
@@ -3,8 +3,7 @@
  *
  * extent_map.c
  *
- * In-memory extent map for OCFS2.  Man, this code was prettier in
- * the library.
+ * Block/Cluster mapping functions
  *
  * Copyright (C) 2004 Oracle.  All rights reserved.
  *
@@ -26,1016 +25,528 @@
 #include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/rbtree.h>
 
 #define MLOG_MASK_PREFIX ML_EXTENT_MAP
 #include <cluster/masklog.h>
 
 #include "ocfs2.h"
 
+#include "alloc.h"
 #include "extent_map.h"
 #include "inode.h"
 #include "super.h"
 
 #include "buffer_head_io.h"
 
-
 /*
- * SUCK SUCK SUCK
- * Our headers are so bad that struct ocfs2_extent_map is in ocfs.h
+ * The extent caching implementation is intentionally trivial.
+ *
+ * We only cache a small number of extents stored directly on the
+ * inode, so linear order operations are acceptable. If we ever want
+ * to increase the size of the extent map, then these algorithms must
+ * get smarter.
  */
 
-struct ocfs2_extent_map_entry {
-	struct rb_node e_node;
-	int e_tree_depth;
-	struct ocfs2_extent_rec e_rec;
-};
-
-struct ocfs2_em_insert_context {
-	int need_left;
-	int need_right;
-	struct ocfs2_extent_map_entry *new_ent;
-	struct ocfs2_extent_map_entry *old_ent;
-	struct ocfs2_extent_map_entry *left_ent;
-	struct ocfs2_extent_map_entry *right_ent;
-};
-
-static struct kmem_cache *ocfs2_em_ent_cachep = NULL;
-
-
-static struct ocfs2_extent_map_entry *
-ocfs2_extent_map_lookup(struct ocfs2_extent_map *em,
-			u32 cpos, u32 clusters,
-			struct rb_node ***ret_p,
-			struct rb_node **ret_parent);
-static int ocfs2_extent_map_insert(struct inode *inode,
-				   struct ocfs2_extent_rec *rec,
-				   int tree_depth);
-static int ocfs2_extent_map_insert_entry(struct ocfs2_extent_map *em,
-					 struct ocfs2_extent_map_entry *ent);
-static int ocfs2_extent_map_find_leaf(struct inode *inode,
-				      u32 cpos, u32 clusters,
-				      struct ocfs2_extent_list *el);
-static int ocfs2_extent_map_lookup_read(struct inode *inode,
-					u32 cpos, u32 clusters,
-					struct ocfs2_extent_map_entry **ret_ent);
-static int ocfs2_extent_map_try_insert(struct inode *inode,
-				       struct ocfs2_extent_rec *rec,
-				       int tree_depth,
-				       struct ocfs2_em_insert_context *ctxt);
-
-/* returns 1 only if the rec contains all the given clusters -- that is that
- * rec's cpos is <= the cluster cpos and that the rec endpoint (cpos +
- * clusters) is >= the argument's endpoint */
-static int ocfs2_extent_rec_contains_clusters(struct ocfs2_extent_rec *rec,
-					      u32 cpos, u32 clusters)
+void ocfs2_extent_map_init(struct inode *inode)
 {
-	if (le32_to_cpu(rec->e_cpos) > cpos)
-		return 0;
-	if (cpos + clusters > le32_to_cpu(rec->e_cpos) + 
-			      le32_to_cpu(rec->e_clusters))
-		return 0;
-	return 1;
+	struct ocfs2_inode_info *oi = OCFS2_I(inode);
+
+	oi->ip_extent_map.em_num_items = 0;
+	INIT_LIST_HEAD(&oi->ip_extent_map.em_list);
 }
 
+static void __ocfs2_extent_map_lookup(struct ocfs2_extent_map *em,
+				      unsigned int cpos,
+				      struct ocfs2_extent_map_item **ret_emi)
+{
+	unsigned int range;
+	struct ocfs2_extent_map_item *emi;
+
+	*ret_emi = NULL;
+
+	list_for_each_entry(emi, &em->em_list, ei_list) {
+		range = emi->ei_cpos + emi->ei_clusters;
+
+		if (cpos >= emi->ei_cpos && cpos < range) {
+			list_move(&emi->ei_list, &em->em_list);
+
+			*ret_emi = emi;
+			break;
+		}
+	}
+}
+
+static int ocfs2_extent_map_lookup(struct inode *inode, unsigned int cpos,
+				   unsigned int *phys, unsigned int *len,
+				   unsigned int *flags)
+{
+	unsigned int coff;
+	struct ocfs2_inode_info *oi = OCFS2_I(inode);
+	struct ocfs2_extent_map_item *emi;
+
+	spin_lock(&oi->ip_lock);
+
+	__ocfs2_extent_map_lookup(&oi->ip_extent_map, cpos, &emi);
+	if (emi) {
+		coff = cpos - emi->ei_cpos;
+		*phys = emi->ei_phys + coff;
+		if (len)
+			*len = emi->ei_clusters - coff;
+		if (flags)
+			*flags = emi->ei_flags;
+	}
+
+	spin_unlock(&oi->ip_lock);
+
+	if (emi == NULL)
+		return -ENOENT;
+
+	return 0;
+}
 
 /*
- * Find an entry in the tree that intersects the region passed in.
- * Note that this will find straddled intervals, it is up to the
- * callers to enforce any boundary conditions.
- *
- * Callers must hold ip_lock.  This lookup is not guaranteed to return
- * a tree_depth 0 match, and as such can race inserts if the lock
- * were not held.
- *
- * The rb_node garbage lets insertion share the search.  Trivial
- * callers pass NULL.
+ * Forget about all clusters equal to or greater than cpos.
  */
-static struct ocfs2_extent_map_entry *
-ocfs2_extent_map_lookup(struct ocfs2_extent_map *em,
-			u32 cpos, u32 clusters,
-			struct rb_node ***ret_p,
-			struct rb_node **ret_parent)
+void ocfs2_extent_map_trunc(struct inode *inode, unsigned int cpos)
 {
-	struct rb_node **p = &em->em_extents.rb_node;
-	struct rb_node *parent = NULL;
-	struct ocfs2_extent_map_entry *ent = NULL;
+	struct list_head *p, *n;
+	struct ocfs2_extent_map_item *emi;
+	struct ocfs2_inode_info *oi = OCFS2_I(inode);
+	struct ocfs2_extent_map *em = &oi->ip_extent_map;
+	LIST_HEAD(tmp_list);
+	unsigned int range;
 
-	while (*p)
-	{
-		parent = *p;
-		ent = rb_entry(parent, struct ocfs2_extent_map_entry,
-			       e_node);
-		if ((cpos + clusters) <= le32_to_cpu(ent->e_rec.e_cpos)) {
-			p = &(*p)->rb_left;
-			ent = NULL;
-		} else if (cpos >= (le32_to_cpu(ent->e_rec.e_cpos) +
-				    le32_to_cpu(ent->e_rec.e_clusters))) {
-			p = &(*p)->rb_right;
-			ent = NULL;
-		} else
+	spin_lock(&oi->ip_lock);
+	list_for_each_safe(p, n, &em->em_list) {
+		emi = list_entry(p, struct ocfs2_extent_map_item, ei_list);
+
+		if (emi->ei_cpos >= cpos) {
+			/* Full truncate of this record. */
+			list_move(&emi->ei_list, &tmp_list);
+			BUG_ON(em->em_num_items == 0);
+			em->em_num_items--;
+			continue;
+		}
+
+		range = emi->ei_cpos + emi->ei_clusters;
+		if (range > cpos) {
+			/* Partial truncate */
+			emi->ei_clusters = cpos - emi->ei_cpos;
+		}
+	}
+	spin_unlock(&oi->ip_lock);
+
+	list_for_each_safe(p, n, &tmp_list) {
+		emi = list_entry(p, struct ocfs2_extent_map_item, ei_list);
+		list_del(&emi->ei_list);
+		kfree(emi);
+	}
+}
+
+/*
+ * Is any part of emi2 contained within emi1
+ */
+static int ocfs2_ei_is_contained(struct ocfs2_extent_map_item *emi1,
+				 struct ocfs2_extent_map_item *emi2)
+{
+	unsigned int range1, range2;
+
+	/*
+	 * Check if logical start of emi2 is inside emi1
+	 */
+	range1 = emi1->ei_cpos + emi1->ei_clusters;
+	if (emi2->ei_cpos >= emi1->ei_cpos && emi2->ei_cpos < range1)
+		return 1;
+
+	/*
+	 * Check if logical end of emi2 is inside emi1
+	 */
+	range2 = emi2->ei_cpos + emi2->ei_clusters;
+	if (range2 > emi1->ei_cpos && range2 <= range1)
+		return 1;
+
+	return 0;
+}
+
+static void ocfs2_copy_emi_fields(struct ocfs2_extent_map_item *dest,
+				  struct ocfs2_extent_map_item *src)
+{
+	dest->ei_cpos = src->ei_cpos;
+	dest->ei_phys = src->ei_phys;
+	dest->ei_clusters = src->ei_clusters;
+	dest->ei_flags = src->ei_flags;
+}
+
+/*
+ * Try to merge emi with ins. Returns 1 if merge succeeds, zero
+ * otherwise.
+ */
+static int ocfs2_try_to_merge_extent_map(struct ocfs2_extent_map_item *emi,
+					 struct ocfs2_extent_map_item *ins)
+{
+	/*
+	 * Handle contiguousness
+	 */
+	if (ins->ei_phys == (emi->ei_phys + emi->ei_clusters) &&
+	    ins->ei_cpos == (emi->ei_cpos + emi->ei_clusters) &&
+	    ins->ei_flags == emi->ei_flags) {
+		emi->ei_clusters += ins->ei_clusters;
+		return 1;
+	} else if ((ins->ei_phys + ins->ei_clusters) == emi->ei_phys &&
+		   (ins->ei_cpos + ins->ei_clusters) == emi->ei_phys &&
+		   ins->ei_flags == emi->ei_flags) {
+		emi->ei_phys = ins->ei_phys;
+		emi->ei_cpos = ins->ei_cpos;
+		emi->ei_clusters += ins->ei_clusters;
+		return 1;
+	}
+
+	/*
+	 * Overlapping extents - this shouldn't happen unless we've
+	 * split an extent to change it's flags. That is exceedingly
+	 * rare, so there's no sense in trying to optimize it yet.
+	 */
+	if (ocfs2_ei_is_contained(emi, ins) ||
+	    ocfs2_ei_is_contained(ins, emi)) {
+		ocfs2_copy_emi_fields(emi, ins);
+		return 1;
+	}
+
+	/* No merge was possible. */
+	return 0;
+}
+
+/*
+ * In order to reduce complexity on the caller, this insert function
+ * is intentionally liberal in what it will accept.
+ *
+ * The only rule is that the truncate call *must* be used whenever
+ * records have been deleted. This avoids inserting overlapping
+ * records with different physical mappings.
+ */
+void ocfs2_extent_map_insert_rec(struct inode *inode,
+				 struct ocfs2_extent_rec *rec)
+{
+	struct ocfs2_inode_info *oi = OCFS2_I(inode);
+	struct ocfs2_extent_map *em = &oi->ip_extent_map;
+	struct ocfs2_extent_map_item *emi, *new_emi = NULL;
+	struct ocfs2_extent_map_item ins;
+
+	ins.ei_cpos = le32_to_cpu(rec->e_cpos);
+	ins.ei_phys = ocfs2_blocks_to_clusters(inode->i_sb,
+					       le64_to_cpu(rec->e_blkno));
+	ins.ei_clusters = le16_to_cpu(rec->e_leaf_clusters);
+	ins.ei_flags = rec->e_flags;
+
+search:
+	spin_lock(&oi->ip_lock);
+
+	list_for_each_entry(emi, &em->em_list, ei_list) {
+		if (ocfs2_try_to_merge_extent_map(emi, &ins)) {
+			list_move(&emi->ei_list, &em->em_list);
+			spin_unlock(&oi->ip_lock);
+			goto out;
+		}
+	}
+
+	/*
+	 * No item could be merged.
+	 *
+	 * Either allocate and add a new item, or overwrite the last recently
+	 * inserted.
+	 */
+
+	if (em->em_num_items < OCFS2_MAX_EXTENT_MAP_ITEMS) {
+		if (new_emi == NULL) {
+			spin_unlock(&oi->ip_lock);
+
+			new_emi = kmalloc(sizeof(*new_emi), GFP_NOFS);
+			if (new_emi == NULL)
+				goto out;
+
+			goto search;
+		}
+
+		ocfs2_copy_emi_fields(new_emi, &ins);
+		list_add(&new_emi->ei_list, &em->em_list);
+		em->em_num_items++;
+		new_emi = NULL;
+	} else {
+		BUG_ON(list_empty(&em->em_list) || em->em_num_items == 0);
+		emi = list_entry(em->em_list.prev,
+				 struct ocfs2_extent_map_item, ei_list);
+		list_move(&emi->ei_list, &em->em_list);
+		ocfs2_copy_emi_fields(emi, &ins);
+	}
+
+	spin_unlock(&oi->ip_lock);
+
+out:
+	if (new_emi)
+		kfree(new_emi);
+}
+
+/*
+ * Return the 1st index within el which contains an extent start
+ * larger than v_cluster.
+ */
+static int ocfs2_search_for_hole_index(struct ocfs2_extent_list *el,
+				       u32 v_cluster)
+{
+	int i;
+	struct ocfs2_extent_rec *rec;
+
+	for(i = 0; i < le16_to_cpu(el->l_next_free_rec); i++) {
+		rec = &el->l_recs[i];
+
+		if (v_cluster < le32_to_cpu(rec->e_cpos))
 			break;
 	}
 
-	if (ret_p != NULL)
-		*ret_p = p;
-	if (ret_parent != NULL)
-		*ret_parent = parent;
-	return ent;
+	return i;
 }
 
 /*
- * Find the leaf containing the interval we want.  While we're on our
- * way down the tree, fill in every record we see at any depth, because
- * we might want it later.
+ * Figure out the size of a hole which starts at v_cluster within the given
+ * extent list.
  *
- * Note that this code is run without ip_lock.  That's because it
- * sleeps while reading.  If someone is also filling the extent list at
- * the same time we are, we might have to restart.
+ * If there is no more allocation past v_cluster, we return the maximum
+ * cluster size minus v_cluster.
+ *
+ * If we have in-inode extents, then el points to the dinode list and
+ * eb_bh is NULL. Otherwise, eb_bh should point to the extent block
+ * containing el.
  */
-static int ocfs2_extent_map_find_leaf(struct inode *inode,
-				      u32 cpos, u32 clusters,
-				      struct ocfs2_extent_list *el)
+static int ocfs2_figure_hole_clusters(struct inode *inode,
+				      struct ocfs2_extent_list *el,
+				      struct buffer_head *eb_bh,
+				      u32 v_cluster,
+				      u32 *num_clusters)
 {
-	int i, ret;
-	struct buffer_head *eb_bh = NULL;
-	u64 blkno;
-	u32 rec_end;
-	struct ocfs2_extent_block *eb;
-	struct ocfs2_extent_rec *rec;
+	int ret, i;
+	struct buffer_head *next_eb_bh = NULL;
+	struct ocfs2_extent_block *eb, *next_eb;
 
-	/*
-	 * The bh data containing the el cannot change here, because
-	 * we hold alloc_sem.  So we can do this without other
-	 * locks.
-	 */
-	while (el->l_tree_depth)
-	{
-		blkno = 0;
-		for (i = 0; i < le16_to_cpu(el->l_next_free_rec); i++) {
-			rec = &el->l_recs[i];
-			rec_end = (le32_to_cpu(rec->e_cpos) +
-				   le32_to_cpu(rec->e_clusters));
+	i = ocfs2_search_for_hole_index(el, v_cluster);
 
-			ret = -EBADR;
-			if (rec_end > OCFS2_I(inode)->ip_clusters) {
-				mlog_errno(ret);
-				ocfs2_error(inode->i_sb,
-					    "Extent %d at e_blkno %llu of inode %llu goes past ip_clusters of %u\n",
-					    i,
-					    (unsigned long long)le64_to_cpu(rec->e_blkno),
-					    (unsigned long long)OCFS2_I(inode)->ip_blkno,
-					    OCFS2_I(inode)->ip_clusters);
-				goto out_free;
-			}
-
-			if (rec_end <= cpos) {
-				ret = ocfs2_extent_map_insert(inode, rec,
-						le16_to_cpu(el->l_tree_depth));
-				if (ret && (ret != -EEXIST)) {
-					mlog_errno(ret);
-					goto out_free;
-				}
-				continue;
-			}
-			if ((cpos + clusters) <= le32_to_cpu(rec->e_cpos)) {
-				ret = ocfs2_extent_map_insert(inode, rec,
-						le16_to_cpu(el->l_tree_depth));
-				if (ret && (ret != -EEXIST)) {
-					mlog_errno(ret);
-					goto out_free;
-				}
-				continue;
-			}
-
-			/*
-			 * We've found a record that matches our
-			 * interval.  We don't insert it because we're
-			 * about to traverse it.
-			 */
-
-			/* Check to see if we're stradling */
-			ret = -ESRCH;
-			if (!ocfs2_extent_rec_contains_clusters(rec,
-							        cpos,
-								clusters)) {
-				mlog_errno(ret);
-				goto out_free;
-			}
-
-			/*
-			 * If we've already found a record, the el has
-			 * two records covering the same interval.
-			 * EEEK!
-			 */
-			ret = -EBADR;
-			if (blkno) {
-				mlog_errno(ret);
-				ocfs2_error(inode->i_sb,
-					    "Multiple extents for (cpos = %u, clusters = %u) on inode %llu; e_blkno %llu and rec %d at e_blkno %llu\n",
-					    cpos, clusters,
-					    (unsigned long long)OCFS2_I(inode)->ip_blkno,
-					    (unsigned long long)blkno, i,
-					    (unsigned long long)le64_to_cpu(rec->e_blkno));
-				goto out_free;
-			}
-
-			blkno = le64_to_cpu(rec->e_blkno);
-		}
+	if (i == le16_to_cpu(el->l_next_free_rec) && eb_bh) {
+		eb = (struct ocfs2_extent_block *)eb_bh->b_data;
 
 		/*
-		 * We don't support holes, and we're still up
-		 * in the branches, so we'd better have found someone
+		 * Check the next leaf for any extents.
 		 */
-		ret = -EBADR;
-		if (!blkno) {
-			ocfs2_error(inode->i_sb,
-				    "No record found for (cpos = %u, clusters = %u) on inode %llu\n",
-				    cpos, clusters,
-				    (unsigned long long)OCFS2_I(inode)->ip_blkno);
-			mlog_errno(ret);
-			goto out_free;
-		}
 
-		if (eb_bh) {
-			brelse(eb_bh);
-			eb_bh = NULL;
-		}
+		if (le64_to_cpu(eb->h_next_leaf_blk) == 0ULL)
+			goto no_more_extents;
+
 		ret = ocfs2_read_block(OCFS2_SB(inode->i_sb),
-				       blkno, &eb_bh, OCFS2_BH_CACHED,
-				       inode);
+				       le64_to_cpu(eb->h_next_leaf_blk),
+				       &next_eb_bh, OCFS2_BH_CACHED, inode);
 		if (ret) {
 			mlog_errno(ret);
-			goto out_free;
+			goto out;
 		}
-		eb = (struct ocfs2_extent_block *)eb_bh->b_data;
-		if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) {
-			OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb, eb);
-			ret = -EIO;
-			goto out_free;
+		next_eb = (struct ocfs2_extent_block *)next_eb_bh->b_data;
+
+		if (!OCFS2_IS_VALID_EXTENT_BLOCK(next_eb)) {
+			ret = -EROFS;
+			OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb, next_eb);
+			goto out;
 		}
-		el = &eb->h_list;
+
+		el = &next_eb->h_list;
+
+		i = ocfs2_search_for_hole_index(el, v_cluster);
 	}
 
-	BUG_ON(el->l_tree_depth);
-
-	for (i = 0; i < le16_to_cpu(el->l_next_free_rec); i++) {
-		rec = &el->l_recs[i];
-
-		if ((le32_to_cpu(rec->e_cpos) + le32_to_cpu(rec->e_clusters)) >
-		    OCFS2_I(inode)->ip_clusters) {
-			ret = -EBADR;
-			mlog_errno(ret);
-			ocfs2_error(inode->i_sb,
-				    "Extent %d at e_blkno %llu of inode %llu goes past ip_clusters of %u\n",
-				    i,
-				    (unsigned long long)le64_to_cpu(rec->e_blkno),
-				    (unsigned long long)OCFS2_I(inode)->ip_blkno,
-				    OCFS2_I(inode)->ip_clusters);
-			return ret;
-		}
-
-		ret = ocfs2_extent_map_insert(inode, rec,
-					      le16_to_cpu(el->l_tree_depth));
-		if (ret && (ret != -EEXIST)) {
-			mlog_errno(ret);
-			goto out_free;
-		}
+no_more_extents:
+	if (i == le16_to_cpu(el->l_next_free_rec)) {
+		/*
+		 * We're at the end of our existing allocation. Just
+		 * return the maximum number of clusters we could
+		 * possibly allocate.
+		 */
+		*num_clusters = UINT_MAX - v_cluster;
+	} else {
+		*num_clusters = le32_to_cpu(el->l_recs[i].e_cpos) - v_cluster;
 	}
 
 	ret = 0;
-
-out_free:
-	if (eb_bh)
-		brelse(eb_bh);
-
+out:
+	brelse(next_eb_bh);
 	return ret;
 }
 
 /*
- * This lookup actually will read from disk.  It has one invariant:
- * It will never re-traverse blocks.  This means that all inserts should
- * be new regions or more granular regions (both allowed by insert).
+ * Return the index of the extent record which contains cluster #v_cluster.
+ * -1 is returned if it was not found.
+ *
+ * Should work fine on interior and exterior nodes.
  */
-static int ocfs2_extent_map_lookup_read(struct inode *inode,
-					u32 cpos,
-					u32 clusters,
-					struct ocfs2_extent_map_entry **ret_ent)
+static int ocfs2_search_extent_list(struct ocfs2_extent_list *el,
+				    u32 v_cluster)
 {
-	int ret;
-	u64 blkno;
-	struct ocfs2_extent_map *em = &OCFS2_I(inode)->ip_map;
-	struct ocfs2_extent_map_entry *ent;
-	struct buffer_head *bh = NULL;
-	struct ocfs2_extent_block *eb;
-	struct ocfs2_dinode *di;
-	struct ocfs2_extent_list *el;
+	int ret = -1;
+	int i;
+	struct ocfs2_extent_rec *rec;
+	u32 rec_end, rec_start, clusters;
 
-	spin_lock(&OCFS2_I(inode)->ip_lock);
-	ent = ocfs2_extent_map_lookup(em, cpos, clusters, NULL, NULL);
-	if (ent) {
-		if (!ent->e_tree_depth) {
-			spin_unlock(&OCFS2_I(inode)->ip_lock);
-			*ret_ent = ent;
-			return 0;
-		}
-		blkno = le64_to_cpu(ent->e_rec.e_blkno);
-		spin_unlock(&OCFS2_I(inode)->ip_lock);
+	for(i = 0; i < le16_to_cpu(el->l_next_free_rec); i++) {
+		rec = &el->l_recs[i];
 
-		ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), blkno, &bh,
-				       OCFS2_BH_CACHED, inode);
-		if (ret) {
-			mlog_errno(ret);
-			if (bh)
-				brelse(bh);
-			return ret;
-		}
-		eb = (struct ocfs2_extent_block *)bh->b_data;
-		if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) {
-			OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb, eb);
-			brelse(bh);
-			return -EIO;
-		}
-		el = &eb->h_list;
-	} else {
-		spin_unlock(&OCFS2_I(inode)->ip_lock);
+		rec_start = le32_to_cpu(rec->e_cpos);
+		clusters = ocfs2_rec_clusters(el, rec);
 
-		ret = ocfs2_read_block(OCFS2_SB(inode->i_sb),
-				       OCFS2_I(inode)->ip_blkno, &bh,
-				       OCFS2_BH_CACHED, inode);
-		if (ret) {
-			mlog_errno(ret);
-			if (bh)
-				brelse(bh);
-			return ret;
+		rec_end = rec_start + clusters;
+
+		if (v_cluster >= rec_start && v_cluster < rec_end) {
+			ret = i;
+			break;
 		}
-		di = (struct ocfs2_dinode *)bh->b_data;
-		if (!OCFS2_IS_VALID_DINODE(di)) {
-			brelse(bh);
-			OCFS2_RO_ON_INVALID_DINODE(inode->i_sb, di);
-			return -EIO;
-		}
-		el = &di->id2.i_list;
 	}
 
-	ret = ocfs2_extent_map_find_leaf(inode, cpos, clusters, el);
-	brelse(bh);
+	return ret;
+}
+
+int ocfs2_get_clusters(struct inode *inode, u32 v_cluster,
+		       u32 *p_cluster, u32 *num_clusters,
+		       unsigned int *extent_flags)
+{
+	int ret, i;
+	unsigned int flags = 0;
+	struct buffer_head *di_bh = NULL;
+	struct buffer_head *eb_bh = NULL;
+	struct ocfs2_dinode *di;
+	struct ocfs2_extent_block *eb;
+	struct ocfs2_extent_list *el;
+	struct ocfs2_extent_rec *rec;
+	u32 coff;
+
+	ret = ocfs2_extent_map_lookup(inode, v_cluster, p_cluster,
+				      num_clusters, extent_flags);
+	if (ret == 0)
+		goto out;
+
+	ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), OCFS2_I(inode)->ip_blkno,
+			       &di_bh, OCFS2_BH_CACHED, inode);
 	if (ret) {
 		mlog_errno(ret);
-		return ret;
+		goto out;
 	}
 
-	ent = ocfs2_extent_map_lookup(em, cpos, clusters, NULL, NULL);
-	if (!ent) {
-		ret = -ESRCH;
-		mlog_errno(ret);
-		return ret;
+	di = (struct ocfs2_dinode *) di_bh->b_data;
+	el = &di->id2.i_list;
+
+	if (el->l_tree_depth) {
+		ret = ocfs2_find_leaf(inode, el, v_cluster, &eb_bh);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		eb = (struct ocfs2_extent_block *) eb_bh->b_data;
+		el = &eb->h_list;
+
+		if (el->l_tree_depth) {
+			ocfs2_error(inode->i_sb,
+				    "Inode %lu has non zero tree depth in "
+				    "leaf block %llu\n", inode->i_ino,
+				    (unsigned long long)eb_bh->b_blocknr);
+			ret = -EROFS;
+			goto out;
+		}
 	}
 
-	/* FIXME: Make sure this isn't a corruption */
-	BUG_ON(ent->e_tree_depth);
+	i = ocfs2_search_extent_list(el, v_cluster);
+	if (i == -1) {
+		/*
+		 * A hole was found. Return some canned values that
+		 * callers can key on. If asked for, num_clusters will
+		 * be populated with the size of the hole.
+		 */
+		*p_cluster = 0;
+		if (num_clusters) {
+			ret = ocfs2_figure_hole_clusters(inode, el, eb_bh,
+							 v_cluster,
+							 num_clusters);
+			if (ret) {
+				mlog_errno(ret);
+				goto out;
+			}
+		}
+	} else {
+		rec = &el->l_recs[i];
 
-	*ret_ent = ent;
+		BUG_ON(v_cluster < le32_to_cpu(rec->e_cpos));
 
-	return 0;
+		if (!rec->e_blkno) {
+			ocfs2_error(inode->i_sb, "Inode %lu has bad extent "
+				    "record (%u, %u, 0)", inode->i_ino,
+				    le32_to_cpu(rec->e_cpos),
+				    ocfs2_rec_clusters(el, rec));
+			ret = -EROFS;
+			goto out;
+		}
+
+		coff = v_cluster - le32_to_cpu(rec->e_cpos);
+
+		*p_cluster = ocfs2_blocks_to_clusters(inode->i_sb,
+						    le64_to_cpu(rec->e_blkno));
+		*p_cluster = *p_cluster + coff;
+
+		if (num_clusters)
+			*num_clusters = ocfs2_rec_clusters(el, rec) - coff;
+
+		flags = rec->e_flags;
+
+		ocfs2_extent_map_insert_rec(inode, rec);
+	}
+
+	if (extent_flags)
+		*extent_flags = flags;
+
+out:
+	brelse(di_bh);
+	brelse(eb_bh);
+	return ret;
 }
 
 /*
- * Callers must hold ip_lock.  This can insert pieces of the tree,
- * thus racing lookup if the lock weren't held.
+ * This expects alloc_sem to be held. The allocation cannot change at
+ * all while the map is in the process of being updated.
  */
-static int ocfs2_extent_map_insert_entry(struct ocfs2_extent_map *em,
-					 struct ocfs2_extent_map_entry *ent)
-{
-	struct rb_node **p, *parent;
-	struct ocfs2_extent_map_entry *old_ent;
-
-	old_ent = ocfs2_extent_map_lookup(em, le32_to_cpu(ent->e_rec.e_cpos),
-					  le32_to_cpu(ent->e_rec.e_clusters),
-					  &p, &parent);
-	if (old_ent)
-		return -EEXIST;
-
-	rb_link_node(&ent->e_node, parent, p);
-	rb_insert_color(&ent->e_node, &em->em_extents);
-
-	return 0;
-}
-
-
-/*
- * Simple rule: on any return code other than -EAGAIN, anything left
- * in the insert_context will be freed.
- *
- * Simple rule #2: A return code of -EEXIST from this function or
- * its calls to ocfs2_extent_map_insert_entry() signifies that another
- * thread beat us to the insert.  It is not an actual error, but it
- * tells the caller we have no more work to do.
- */
-static int ocfs2_extent_map_try_insert(struct inode *inode,
-				       struct ocfs2_extent_rec *rec,
-				       int tree_depth,
-				       struct ocfs2_em_insert_context *ctxt)
+int ocfs2_extent_map_get_blocks(struct inode *inode, u64 v_blkno, u64 *p_blkno,
+				u64 *ret_count, unsigned int *extent_flags)
 {
 	int ret;
-	struct ocfs2_extent_map *em = &OCFS2_I(inode)->ip_map;
-	struct ocfs2_extent_map_entry *old_ent;
+	int bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1);
+	u32 cpos, num_clusters, p_cluster;
+	u64 boff = 0;
 
-	ctxt->need_left = 0;
-	ctxt->need_right = 0;
-	ctxt->old_ent = NULL;
+	cpos = ocfs2_blocks_to_clusters(inode->i_sb, v_blkno);
 
-	spin_lock(&OCFS2_I(inode)->ip_lock);
-	ret = ocfs2_extent_map_insert_entry(em, ctxt->new_ent);
-	if (!ret) {
-		ctxt->new_ent = NULL;
-		goto out_unlock;
-	}
-
-	/* Since insert_entry failed, the map MUST have old_ent */
-	old_ent = ocfs2_extent_map_lookup(em, le32_to_cpu(rec->e_cpos),
-					  le32_to_cpu(rec->e_clusters),
-					  NULL, NULL);
-
-	BUG_ON(!old_ent);
-
-	if (old_ent->e_tree_depth < tree_depth) {
-		/* Another thread beat us to the lower tree_depth */
-		ret = -EEXIST;
-		goto out_unlock;
-	}
-
-	if (old_ent->e_tree_depth == tree_depth) {
-		/*
-		 * Another thread beat us to this tree_depth.
-		 * Let's make sure we agree with that thread (the
-		 * extent_rec should be identical).
-		 */
-		if (!memcmp(rec, &old_ent->e_rec,
-			    sizeof(struct ocfs2_extent_rec)))
-			ret = 0;
-		else
-			/* FIXME: Should this be ESRCH/EBADR??? */
-			ret = -EEXIST;
-
-		goto out_unlock;
+	ret = ocfs2_get_clusters(inode, cpos, &p_cluster, &num_clusters,
+				 extent_flags);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
 	}
 
 	/*
-	 * We do it in this order specifically so that no actual tree
-	 * changes occur until we have all the pieces we need.  We
-	 * don't want malloc failures to leave an inconsistent tree.
-	 * Whenever we drop the lock, another process could be
-	 * inserting.  Also note that, if another process just beat us
-	 * to an insert, we might not need the same pieces we needed
-	 * the first go round.  In the end, the pieces we need will
-	 * be used, and the pieces we don't will be freed.
+	 * p_cluster == 0 indicates a hole.
 	 */
-	ctxt->need_left = !!(le32_to_cpu(rec->e_cpos) >
-			     le32_to_cpu(old_ent->e_rec.e_cpos));
-	ctxt->need_right = !!((le32_to_cpu(old_ent->e_rec.e_cpos) +
-			       le32_to_cpu(old_ent->e_rec.e_clusters)) >
-			      (le32_to_cpu(rec->e_cpos) + le32_to_cpu(rec->e_clusters)));
-	ret = -EAGAIN;
-	if (ctxt->need_left) {
-		if (!ctxt->left_ent)
-			goto out_unlock;
-		*(ctxt->left_ent) = *old_ent;
-		ctxt->left_ent->e_rec.e_clusters =
-			cpu_to_le32(le32_to_cpu(rec->e_cpos) -
-				    le32_to_cpu(ctxt->left_ent->e_rec.e_cpos));
-	}
-	if (ctxt->need_right) {
-		if (!ctxt->right_ent)
-			goto out_unlock;
-		*(ctxt->right_ent) = *old_ent;
-		ctxt->right_ent->e_rec.e_cpos =
-			cpu_to_le32(le32_to_cpu(rec->e_cpos) +
-				    le32_to_cpu(rec->e_clusters));
-		ctxt->right_ent->e_rec.e_clusters =
-			cpu_to_le32((le32_to_cpu(old_ent->e_rec.e_cpos) +
-				     le32_to_cpu(old_ent->e_rec.e_clusters)) -
-				    le32_to_cpu(ctxt->right_ent->e_rec.e_cpos));
-	}
-
-	rb_erase(&old_ent->e_node, &em->em_extents);
-	/* Now that he's erased, set him up for deletion */
-	ctxt->old_ent = old_ent;
-
-	if (ctxt->need_left) {
-		ret = ocfs2_extent_map_insert_entry(em,
-						    ctxt->left_ent);
-		if (ret)
-			goto out_unlock;
-		ctxt->left_ent = NULL;
-	}
-
-	if (ctxt->need_right) {
-		ret = ocfs2_extent_map_insert_entry(em,
-						    ctxt->right_ent);
-		if (ret)
-			goto out_unlock;
-		ctxt->right_ent = NULL;
-	}
-
-	ret = ocfs2_extent_map_insert_entry(em, ctxt->new_ent);
-
-	if (!ret)
-		ctxt->new_ent = NULL;
-
-out_unlock:
-	spin_unlock(&OCFS2_I(inode)->ip_lock);
-
-	return ret;
-}
-
-
-static int ocfs2_extent_map_insert(struct inode *inode,
-				   struct ocfs2_extent_rec *rec,
-				   int tree_depth)
-{
-	int ret;
-	struct ocfs2_em_insert_context ctxt = {0, };
-
-	if ((le32_to_cpu(rec->e_cpos) + le32_to_cpu(rec->e_clusters)) >
-	    OCFS2_I(inode)->ip_map.em_clusters) {
-		ret = -EBADR;
-		mlog_errno(ret);
-		return ret;
-	}
-
-	/* Zero e_clusters means a truncated tail record.  It better be EOF */
-	if (!rec->e_clusters) {
-		if ((le32_to_cpu(rec->e_cpos) + le32_to_cpu(rec->e_clusters)) !=
-		    OCFS2_I(inode)->ip_map.em_clusters) {
-			ret = -EBADR;
-			mlog_errno(ret);
-			ocfs2_error(inode->i_sb,
-				    "Zero e_clusters on non-tail extent record at e_blkno %llu on inode %llu\n",
-				    (unsigned long long)le64_to_cpu(rec->e_blkno),
-				    (unsigned long long)OCFS2_I(inode)->ip_blkno);
-			return ret;
-		}
-
-		/* Ignore the truncated tail */
-		return 0;
-	}
-
-	ret = -ENOMEM;
-	ctxt.new_ent = kmem_cache_alloc(ocfs2_em_ent_cachep,
-					GFP_NOFS);
-	if (!ctxt.new_ent) {
-		mlog_errno(ret);
-		return ret;
-	}
-
-	ctxt.new_ent->e_rec = *rec;
-	ctxt.new_ent->e_tree_depth = tree_depth;
-
-	do {
-		ret = -ENOMEM;
-		if (ctxt.need_left && !ctxt.left_ent) {
-			ctxt.left_ent =
-				kmem_cache_alloc(ocfs2_em_ent_cachep,
-						 GFP_NOFS);
-			if (!ctxt.left_ent)
-				break;
-		}
-		if (ctxt.need_right && !ctxt.right_ent) {
-			ctxt.right_ent =
-				kmem_cache_alloc(ocfs2_em_ent_cachep,
-						 GFP_NOFS);
-			if (!ctxt.right_ent)
-				break;
-		}
-
-		ret = ocfs2_extent_map_try_insert(inode, rec,
-						  tree_depth, &ctxt);
-	} while (ret == -EAGAIN);
-
-	if ((ret < 0) && (ret != -EEXIST))
-		mlog_errno(ret);
-
-	if (ctxt.left_ent)
-		kmem_cache_free(ocfs2_em_ent_cachep, ctxt.left_ent);
-	if (ctxt.right_ent)
-		kmem_cache_free(ocfs2_em_ent_cachep, ctxt.right_ent);
-	if (ctxt.old_ent)
-		kmem_cache_free(ocfs2_em_ent_cachep, ctxt.old_ent);
-	if (ctxt.new_ent)
-		kmem_cache_free(ocfs2_em_ent_cachep, ctxt.new_ent);
-
-	return ret;
-}
-
-/*
- * Append this record to the tail of the extent map.  It must be
- * tree_depth 0.  The record might be an extension of an existing
- * record, and as such that needs to be handled.  eg:
- *
- * Existing record in the extent map:
- *
- *	cpos = 10, len = 10
- *	|---------|
- *
- * New Record:
- *
- *	cpos = 10, len = 20
- *	|------------------|
- *
- * The passed record is the new on-disk record.  The new_clusters value
- * is how many clusters were added to the file.  If the append is a
- * contiguous append, the new_clusters has been added to
- * rec->e_clusters.  If the append is an entirely new extent, then
- * rec->e_clusters is == new_clusters.
- */
-int ocfs2_extent_map_append(struct inode *inode,
-			    struct ocfs2_extent_rec *rec,
-			    u32 new_clusters)
-{
-	int ret;
-	struct ocfs2_extent_map *em = &OCFS2_I(inode)->ip_map;
-	struct ocfs2_extent_map_entry *ent;
-	struct ocfs2_extent_rec *old;
-
-	BUG_ON(!new_clusters);
-	BUG_ON(le32_to_cpu(rec->e_clusters) < new_clusters);
-
-	if (em->em_clusters < OCFS2_I(inode)->ip_clusters) {
-		/*
-		 * Size changed underneath us on disk.  Drop any
-		 * straddling records and update our idea of
-		 * i_clusters
-		 */
-		ocfs2_extent_map_drop(inode, em->em_clusters - 1);
-		em->em_clusters = OCFS2_I(inode)->ip_clusters;
-	}
-
-	mlog_bug_on_msg((le32_to_cpu(rec->e_cpos) +
-			 le32_to_cpu(rec->e_clusters)) !=
-			(em->em_clusters + new_clusters),
-			"Inode %llu:\n"
-			"rec->e_cpos = %u + rec->e_clusters = %u = %u\n"
-			"em->em_clusters = %u + new_clusters = %u = %u\n",
-			(unsigned long long)OCFS2_I(inode)->ip_blkno,
-			le32_to_cpu(rec->e_cpos), le32_to_cpu(rec->e_clusters),
-			le32_to_cpu(rec->e_cpos) + le32_to_cpu(rec->e_clusters),
-			em->em_clusters, new_clusters,
-			em->em_clusters + new_clusters);
-
-	em->em_clusters += new_clusters;
-
-	ret = -ENOENT;
-	if (le32_to_cpu(rec->e_clusters) > new_clusters) {
-		/* This is a contiguous append */
-		ent = ocfs2_extent_map_lookup(em, le32_to_cpu(rec->e_cpos), 1,
-					      NULL, NULL);
-		if (ent) {
-			old = &ent->e_rec;
-			BUG_ON((le32_to_cpu(rec->e_cpos) +
-				le32_to_cpu(rec->e_clusters)) !=
-				 (le32_to_cpu(old->e_cpos) +
-				  le32_to_cpu(old->e_clusters) +
-				  new_clusters));
-			if (ent->e_tree_depth == 0) {
-				BUG_ON(le32_to_cpu(old->e_cpos) !=
-				       le32_to_cpu(rec->e_cpos));
-				BUG_ON(le64_to_cpu(old->e_blkno) !=
-				       le64_to_cpu(rec->e_blkno));
-				ret = 0;
-			}
-			/*
-			 * Let non-leafs fall through as -ENOENT to
-			 * force insertion of the new leaf.
-			 */
-			le32_add_cpu(&old->e_clusters, new_clusters);
-		}
-	}
-
-	if (ret == -ENOENT)
-		ret = ocfs2_extent_map_insert(inode, rec, 0);
-	if (ret < 0)
-		mlog_errno(ret);
-	return ret;
-}
-
-#if 0
-/* Code here is included but defined out as it completes the extent
- * map api and may be used in the future. */
-
-/*
- * Look up the record containing this cluster offset.  This record is
- * part of the extent map.  Do not free it.  Any changes you make to
- * it will reflect in the extent map.  So, if your last extent
- * is (cpos = 10, clusters = 10) and you truncate the file by 5
- * clusters, you can do:
- *
- * ret = ocfs2_extent_map_get_rec(em, orig_size - 5, &rec);
- * rec->e_clusters -= 5;
- *
- * The lookup does not read from disk.  If the map isn't filled in for
- * an entry, you won't find it.
- *
- * Also note that the returned record is valid until alloc_sem is
- * dropped.  After that, truncate and extend can happen.  Caveat Emptor.
- */
-int ocfs2_extent_map_get_rec(struct inode *inode, u32 cpos,
-			     struct ocfs2_extent_rec **rec,
-			     int *tree_depth)
-{
-	int ret = -ENOENT;
-	struct ocfs2_extent_map *em = &OCFS2_I(inode)->ip_map;
-	struct ocfs2_extent_map_entry *ent;
-
-	*rec = NULL;
-
-	if (cpos >= OCFS2_I(inode)->ip_clusters)
-		return -EINVAL;
-
-	if (cpos >= em->em_clusters) {
-		/*
-		 * Size changed underneath us on disk.  Drop any
-		 * straddling records and update our idea of
-		 * i_clusters
-		 */
-		ocfs2_extent_map_drop(inode, em->em_clusters - 1);
-		em->em_clusters = OCFS2_I(inode)->ip_clusters ;
-	}
-
-	ent = ocfs2_extent_map_lookup(&OCFS2_I(inode)->ip_map, cpos, 1,
-				      NULL, NULL);
-
-	if (ent) {
-		*rec = &ent->e_rec;
-		if (tree_depth)
-			*tree_depth = ent->e_tree_depth;
-		ret = 0;
-	}
-
-	return ret;
-}
-
-int ocfs2_extent_map_get_clusters(struct inode *inode,
-				  u32 v_cpos, int count,
-				  u32 *p_cpos, int *ret_count)
-{
-	int ret;
-	u32 coff, ccount;
-	struct ocfs2_extent_map *em = &OCFS2_I(inode)->ip_map;
-	struct ocfs2_extent_map_entry *ent = NULL;
-
-	*p_cpos = ccount = 0;
-
-	if ((v_cpos + count) > OCFS2_I(inode)->ip_clusters)
-		return -EINVAL;
-
-	if ((v_cpos + count) > em->em_clusters) {
-		/*
-		 * Size changed underneath us on disk.  Drop any
-		 * straddling records and update our idea of
-		 * i_clusters
-		 */
-		ocfs2_extent_map_drop(inode, em->em_clusters - 1);
-		em->em_clusters = OCFS2_I(inode)->ip_clusters;
-	}
-
-
-	ret = ocfs2_extent_map_lookup_read(inode, v_cpos, count, &ent);
-	if (ret)
-		return ret;
-
-	if (ent) {
-		/* We should never find ourselves straddling an interval */
-		if (!ocfs2_extent_rec_contains_clusters(&ent->e_rec,
-							v_cpos,
-							count))
-			return -ESRCH;
-
-		coff = v_cpos - le32_to_cpu(ent->e_rec.e_cpos);
-		*p_cpos = ocfs2_blocks_to_clusters(inode->i_sb,
-				le64_to_cpu(ent->e_rec.e_blkno)) +
-			  coff;
-
-		if (ret_count)
-			*ret_count = le32_to_cpu(ent->e_rec.e_clusters) - coff;
-
-		return 0;
-	}
-
-
-	return -ENOENT;
-}
-
-#endif  /*  0  */
-
-int ocfs2_extent_map_get_blocks(struct inode *inode,
-				u64 v_blkno, int count,
-				u64 *p_blkno, int *ret_count)
-{
-	int ret;
-	u64 boff;
-	u32 cpos, clusters;
-	int bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1);
-	struct ocfs2_extent_map_entry *ent = NULL;
-	struct ocfs2_extent_map *em = &OCFS2_I(inode)->ip_map;
-	struct ocfs2_extent_rec *rec;
-
-	*p_blkno = 0;
-
-	cpos = ocfs2_blocks_to_clusters(inode->i_sb, v_blkno);
-	clusters = ocfs2_blocks_to_clusters(inode->i_sb,
-					    (u64)count + bpc - 1);
-	if ((cpos + clusters) > OCFS2_I(inode)->ip_clusters) {
-		ret = -EINVAL;
-		mlog_errno(ret);
-		return ret;
-	}
-
-	if ((cpos + clusters) > em->em_clusters) {
-		/*
-		 * Size changed underneath us on disk.  Drop any
-		 * straddling records and update our idea of
-		 * i_clusters
-		 */
-		ocfs2_extent_map_drop(inode, em->em_clusters - 1);
-		em->em_clusters = OCFS2_I(inode)->ip_clusters;
-	}
-
-	ret = ocfs2_extent_map_lookup_read(inode, cpos, clusters, &ent);
-	if (ret) {
-		mlog_errno(ret);
-		return ret;
-	}
-
-	if (ent)
-	{
-		rec = &ent->e_rec;
-
-		/* We should never find ourselves straddling an interval */
-		if (!ocfs2_extent_rec_contains_clusters(rec, cpos, clusters)) {
-			ret = -ESRCH;
-			mlog_errno(ret);
-			return ret;
-		}
-
-		boff = ocfs2_clusters_to_blocks(inode->i_sb, cpos -
-						le32_to_cpu(rec->e_cpos));
+	if (p_cluster) {
+		boff = ocfs2_clusters_to_blocks(inode->i_sb, p_cluster);
 		boff += (v_blkno & (u64)(bpc - 1));
-		*p_blkno = le64_to_cpu(rec->e_blkno) + boff;
-
-		if (ret_count) {
-			*ret_count = ocfs2_clusters_to_blocks(inode->i_sb,
-					le32_to_cpu(rec->e_clusters)) - boff;
-		}
-
-		return 0;
 	}
 
-	return -ENOENT;
-}
+	*p_blkno = boff;
 
-int ocfs2_extent_map_init(struct inode *inode)
-{
-	struct ocfs2_extent_map *em = &OCFS2_I(inode)->ip_map;
-
-	em->em_extents = RB_ROOT;
-	em->em_clusters = 0;
-
-	return 0;
-}
-
-/* Needs the lock */
-static void __ocfs2_extent_map_drop(struct inode *inode,
-				    u32 new_clusters,
-				    struct rb_node **free_head,
-				    struct ocfs2_extent_map_entry **tail_ent)
-{
-	struct rb_node *node, *next;
-	struct ocfs2_extent_map *em = &OCFS2_I(inode)->ip_map;
-	struct ocfs2_extent_map_entry *ent;
-
-	*free_head = NULL;
-
-	ent = NULL;
-	node = rb_last(&em->em_extents);
-	while (node)
-	{
-		next = rb_prev(node);
-
-		ent = rb_entry(node, struct ocfs2_extent_map_entry,
-			       e_node);
-		if (le32_to_cpu(ent->e_rec.e_cpos) < new_clusters)
-			break;
-
-		rb_erase(&ent->e_node, &em->em_extents);
-
-		node->rb_right = *free_head;
-		*free_head = node;
-
-		ent = NULL;
-		node = next;
+	if (ret_count) {
+		*ret_count = ocfs2_clusters_to_blocks(inode->i_sb, num_clusters);
+		*ret_count -= v_blkno & (u64)(bpc - 1);
 	}
 
-	/* Do we have an entry straddling new_clusters? */
-	if (tail_ent) {
-		if (ent &&
-		    ((le32_to_cpu(ent->e_rec.e_cpos) +
-		      le32_to_cpu(ent->e_rec.e_clusters)) > new_clusters))
-			*tail_ent = ent;
-		else
-			*tail_ent = NULL;
-	}
-}
-
-static void __ocfs2_extent_map_drop_cleanup(struct rb_node *free_head)
-{
-	struct rb_node *node;
-	struct ocfs2_extent_map_entry *ent;
-
-	while (free_head) {
-		node = free_head;
-		free_head = node->rb_right;
-
-		ent = rb_entry(node, struct ocfs2_extent_map_entry,
-			       e_node);
-		kmem_cache_free(ocfs2_em_ent_cachep, ent);
-	}
-}
-
-/*
- * Remove all entries past new_clusters, inclusive of an entry that
- * contains new_clusters.  This is effectively a cache forget.
- *
- * If you want to also clip the last extent by some number of clusters,
- * you need to call ocfs2_extent_map_trunc().
- * This code does not check or modify ip_clusters.
- */
-int ocfs2_extent_map_drop(struct inode *inode, u32 new_clusters)
-{
-	struct rb_node *free_head = NULL;
-	struct ocfs2_extent_map *em = &OCFS2_I(inode)->ip_map;
-	struct ocfs2_extent_map_entry *ent;
-
-	spin_lock(&OCFS2_I(inode)->ip_lock);
-
-	__ocfs2_extent_map_drop(inode, new_clusters, &free_head, &ent);
-
-	if (ent) {
-		rb_erase(&ent->e_node, &em->em_extents);
-		ent->e_node.rb_right = free_head;
-		free_head = &ent->e_node;
-	}
-
-	spin_unlock(&OCFS2_I(inode)->ip_lock);
-
-	if (free_head)
-		__ocfs2_extent_map_drop_cleanup(free_head);
-
-	return 0;
-}
-
-/*
- * Remove all entries past new_clusters and also clip any extent
- * straddling new_clusters, if there is one.  This does not check
- * or modify ip_clusters
- */
-int ocfs2_extent_map_trunc(struct inode *inode, u32 new_clusters)
-{
-	struct rb_node *free_head = NULL;
-	struct ocfs2_extent_map_entry *ent = NULL;
-
-	spin_lock(&OCFS2_I(inode)->ip_lock);
-
-	__ocfs2_extent_map_drop(inode, new_clusters, &free_head, &ent);
-
-	if (ent)
-		ent->e_rec.e_clusters = cpu_to_le32(new_clusters -
-					       le32_to_cpu(ent->e_rec.e_cpos));
-
-	OCFS2_I(inode)->ip_map.em_clusters = new_clusters;
-
-	spin_unlock(&OCFS2_I(inode)->ip_lock);
-
-	if (free_head)
-		__ocfs2_extent_map_drop_cleanup(free_head);
-
-	return 0;
-}
-
-int __init init_ocfs2_extent_maps(void)
-{
-	ocfs2_em_ent_cachep =
-		kmem_cache_create("ocfs2_em_ent",
-				  sizeof(struct ocfs2_extent_map_entry),
-				  0, SLAB_HWCACHE_ALIGN, NULL, NULL);
-	if (!ocfs2_em_ent_cachep)
-		return -ENOMEM;
-
-	return 0;
-}
-
-void exit_ocfs2_extent_maps(void)
-{
-	kmem_cache_destroy(ocfs2_em_ent_cachep);
+out:
+	return ret;
 }
diff --git a/fs/ocfs2/extent_map.h b/fs/ocfs2/extent_map.h
index fa3745e..de91e3e 100644
--- a/fs/ocfs2/extent_map.h
+++ b/fs/ocfs2/extent_map.h
@@ -25,22 +25,29 @@
 #ifndef _EXTENT_MAP_H
 #define _EXTENT_MAP_H
 
-int init_ocfs2_extent_maps(void);
-void exit_ocfs2_extent_maps(void);
+struct ocfs2_extent_map_item {
+	unsigned int			ei_cpos;
+	unsigned int			ei_phys;
+	unsigned int			ei_clusters;
+	unsigned int			ei_flags;
 
-/*
- * EVERY CALL here except _init, _trunc, and _drop expects alloc_sem
- * to be held.  The allocation cannot change at all while the map is
- * in the process of being updated.
- */
-int ocfs2_extent_map_init(struct inode *inode);
-int ocfs2_extent_map_append(struct inode *inode,
-			    struct ocfs2_extent_rec *rec,
-			    u32 new_clusters);
-int ocfs2_extent_map_get_blocks(struct inode *inode,
-				u64 v_blkno, int count,
-				u64 *p_blkno, int *ret_count);
-int ocfs2_extent_map_drop(struct inode *inode, u32 new_clusters);
-int ocfs2_extent_map_trunc(struct inode *inode, u32 new_clusters);
+	struct list_head		ei_list;
+};
+
+#define OCFS2_MAX_EXTENT_MAP_ITEMS			3
+struct ocfs2_extent_map {
+	unsigned int			em_num_items;
+	struct list_head		em_list;
+};
+
+void ocfs2_extent_map_init(struct inode *inode);
+void ocfs2_extent_map_trunc(struct inode *inode, unsigned int cluster);
+void ocfs2_extent_map_insert_rec(struct inode *inode,
+				 struct ocfs2_extent_rec *rec);
+
+int ocfs2_get_clusters(struct inode *inode, u32 v_cluster, u32 *p_cluster,
+		       u32 *num_clusters, unsigned int *extent_flags);
+int ocfs2_extent_map_get_blocks(struct inode *inode, u64 v_blkno, u64 *p_blkno,
+				u64 *ret_count, unsigned int *extent_flags);
 
 #endif  /* _EXTENT_MAP_H */
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index f2cd3bf..520a2a6 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -33,6 +33,7 @@
 #include <linux/sched.h>
 #include <linux/pipe_fs_i.h>
 #include <linux/mount.h>
+#include <linux/writeback.h>
 
 #define MLOG_MASK_PREFIX ML_INODE
 #include <cluster/masklog.h>
@@ -215,7 +216,7 @@
 
 	mlog_entry_void();
 	i_size_write(inode, new_i_size);
-	inode->i_blocks = ocfs2_align_bytes_to_sectors(new_i_size);
+	inode->i_blocks = ocfs2_inode_sector_count(inode);
 	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
 
 	status = ocfs2_mark_inode_dirty(handle, inode, fe_bh);
@@ -261,6 +262,7 @@
 {
 	int status;
 	handle_t *handle;
+	struct ocfs2_dinode *di;
 
 	mlog_entry_void();
 
@@ -274,12 +276,39 @@
 		goto out;
 	}
 
-	status = ocfs2_set_inode_size(handle, inode, fe_bh, new_i_size);
+	status = ocfs2_journal_access(handle, inode, fe_bh,
+				      OCFS2_JOURNAL_ACCESS_WRITE);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out_commit;
+	}
+
+	/*
+	 * Do this before setting i_size.
+	 */
+	status = ocfs2_zero_tail_for_truncate(inode, handle, new_i_size);
+	if (status) {
+		mlog_errno(status);
+		goto out_commit;
+	}
+
+	i_size_write(inode, new_i_size);
+	inode->i_blocks = ocfs2_align_bytes_to_sectors(new_i_size);
+	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+
+	di = (struct ocfs2_dinode *) fe_bh->b_data;
+	di->i_size = cpu_to_le64(new_i_size);
+	di->i_ctime = di->i_mtime = cpu_to_le64(inode->i_ctime.tv_sec);
+	di->i_ctime_nsec = di->i_mtime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec);
+
+	status = ocfs2_journal_dirty(handle, fe_bh);
 	if (status < 0)
 		mlog_errno(status);
 
+out_commit:
 	ocfs2_commit_trans(osb, handle);
 out:
+
 	mlog_exit(status);
 	return status;
 }
@@ -342,19 +371,6 @@
 		mlog_errno(status);
 		goto bail;
 	}
-	ocfs2_data_unlock(inode, 1);
-
-	if (le32_to_cpu(fe->i_clusters) ==
-	    ocfs2_clusters_for_bytes(osb->sb, new_i_size)) {
-		mlog(0, "fe->i_clusters = %u, so we do a simple truncate\n",
-		     fe->i_clusters);
-		/* No allocation change is required, so lets fast path
-		 * this truncate. */
-		status = ocfs2_simple_size_update(inode, di_bh, new_i_size);
-		if (status < 0)
-			mlog_errno(status);
-		goto bail;
-	}
 
 	/* alright, we're going to need to do a full blown alloc size
 	 * change. Orphan the inode so that recovery can complete the
@@ -363,22 +379,25 @@
 	status = ocfs2_orphan_for_truncate(osb, inode, di_bh, new_i_size);
 	if (status < 0) {
 		mlog_errno(status);
-		goto bail;
+		goto bail_unlock_data;
 	}
 
 	status = ocfs2_prepare_truncate(osb, inode, di_bh, &tc);
 	if (status < 0) {
 		mlog_errno(status);
-		goto bail;
+		goto bail_unlock_data;
 	}
 
 	status = ocfs2_commit_truncate(osb, inode, di_bh, tc);
 	if (status < 0) {
 		mlog_errno(status);
-		goto bail;
+		goto bail_unlock_data;
 	}
 
 	/* TODO: orphan dir cleanup here. */
+bail_unlock_data:
+	ocfs2_data_unlock(inode, 1);
+
 bail:
 
 	mlog_exit(status);
@@ -397,6 +416,7 @@
  */
 int ocfs2_do_extend_allocation(struct ocfs2_super *osb,
 			       struct inode *inode,
+			       u32 *logical_offset,
 			       u32 clusters_to_add,
 			       struct buffer_head *fe_bh,
 			       handle_t *handle,
@@ -460,18 +480,14 @@
 	block = ocfs2_clusters_to_blocks(osb->sb, bit_off);
 	mlog(0, "Allocating %u clusters at block %u for inode %llu\n",
 	     num_bits, bit_off, (unsigned long long)OCFS2_I(inode)->ip_blkno);
-	status = ocfs2_insert_extent(osb, handle, inode, fe_bh, block,
-				     num_bits, meta_ac);
+	status = ocfs2_insert_extent(osb, handle, inode, fe_bh,
+				     *logical_offset, block, num_bits,
+				     meta_ac);
 	if (status < 0) {
 		mlog_errno(status);
 		goto leave;
 	}
 
-	le32_add_cpu(&fe->i_clusters, num_bits);
-	spin_lock(&OCFS2_I(inode)->ip_lock);
-	OCFS2_I(inode)->ip_clusters = le32_to_cpu(fe->i_clusters);
-	spin_unlock(&OCFS2_I(inode)->ip_lock);
-
 	status = ocfs2_journal_dirty(handle, fe_bh);
 	if (status < 0) {
 		mlog_errno(status);
@@ -479,6 +495,7 @@
 	}
 
 	clusters_to_add -= num_bits;
+	*logical_offset += num_bits;
 
 	if (clusters_to_add) {
 		mlog(0, "need to alloc once more, clusters = %u, wanted = "
@@ -494,14 +511,87 @@
 	return status;
 }
 
+/*
+ * For a given allocation, determine which allocators will need to be
+ * accessed, and lock them, reserving the appropriate number of bits.
+ *
+ * Called from ocfs2_extend_allocation() for file systems which don't
+ * support holes, and from ocfs2_write() for file systems which
+ * understand sparse inodes.
+ */
+int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di,
+			  u32 clusters_to_add,
+			  struct ocfs2_alloc_context **data_ac,
+			  struct ocfs2_alloc_context **meta_ac)
+{
+	int ret, num_free_extents;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	*meta_ac = NULL;
+	*data_ac = NULL;
+
+	mlog(0, "extend inode %llu, i_size = %lld, di->i_clusters = %u, "
+	     "clusters_to_add = %u\n",
+	     (unsigned long long)OCFS2_I(inode)->ip_blkno, i_size_read(inode),
+	     le32_to_cpu(di->i_clusters), clusters_to_add);
+
+	num_free_extents = ocfs2_num_free_extents(osb, inode, di);
+	if (num_free_extents < 0) {
+		ret = num_free_extents;
+		mlog_errno(ret);
+		goto out;
+	}
+
+	/*
+	 * Sparse allocation file systems need to be more conservative
+	 * with reserving room for expansion - the actual allocation
+	 * happens while we've got a journal handle open so re-taking
+	 * a cluster lock (because we ran out of room for another
+	 * extent) will violate ordering rules.
+	 *
+	 * Most of the time we'll only be seeing this 1 cluster at a time
+	 * anyway.
+	 */
+	if (!num_free_extents ||
+	    (ocfs2_sparse_alloc(osb) && num_free_extents < clusters_to_add)) {
+		ret = ocfs2_reserve_new_metadata(osb, di, meta_ac);
+		if (ret < 0) {
+			if (ret != -ENOSPC)
+				mlog_errno(ret);
+			goto out;
+		}
+	}
+
+	ret = ocfs2_reserve_clusters(osb, clusters_to_add, data_ac);
+	if (ret < 0) {
+		if (ret != -ENOSPC)
+			mlog_errno(ret);
+		goto out;
+	}
+
+out:
+	if (ret) {
+		if (*meta_ac) {
+			ocfs2_free_alloc_context(*meta_ac);
+			*meta_ac = NULL;
+		}
+
+		/*
+		 * We cannot have an error and a non null *data_ac.
+		 */
+	}
+
+	return ret;
+}
+
 static int ocfs2_extend_allocation(struct inode *inode,
 				   u32 clusters_to_add)
 {
 	int status = 0;
 	int restart_func = 0;
 	int drop_alloc_sem = 0;
-	int credits, num_free_extents;
-	u32 prev_clusters;
+	int credits;
+	u32 prev_clusters, logical_start;
 	struct buffer_head *bh = NULL;
 	struct ocfs2_dinode *fe = NULL;
 	handle_t *handle = NULL;
@@ -512,6 +602,12 @@
 
 	mlog_entry("(clusters_to_add = %u)\n", clusters_to_add);
 
+	/*
+	 * This function only exists for file systems which don't
+	 * support holes.
+	 */
+	BUG_ON(ocfs2_sparse_alloc(osb));
+
 	status = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno, &bh,
 				  OCFS2_BH_CACHED, inode);
 	if (status < 0) {
@@ -526,39 +622,11 @@
 		goto leave;
 	}
 
+	logical_start = OCFS2_I(inode)->ip_clusters;
+
 restart_all:
 	BUG_ON(le32_to_cpu(fe->i_clusters) != OCFS2_I(inode)->ip_clusters);
 
-	mlog(0, "extend inode %llu, i_size = %lld, fe->i_clusters = %u, "
-	     "clusters_to_add = %u\n",
-	     (unsigned long long)OCFS2_I(inode)->ip_blkno, i_size_read(inode),
-	     fe->i_clusters, clusters_to_add);
-
-	num_free_extents = ocfs2_num_free_extents(osb,
-						  inode,
-						  fe);
-	if (num_free_extents < 0) {
-		status = num_free_extents;
-		mlog_errno(status);
-		goto leave;
-	}
-
-	if (!num_free_extents) {
-		status = ocfs2_reserve_new_metadata(osb, fe, &meta_ac);
-		if (status < 0) {
-			if (status != -ENOSPC)
-				mlog_errno(status);
-			goto leave;
-		}
-	}
-
-	status = ocfs2_reserve_clusters(osb, clusters_to_add, &data_ac);
-	if (status < 0) {
-		if (status != -ENOSPC)
-			mlog_errno(status);
-		goto leave;
-	}
-
 	/* blocks peope in read/write from reading our allocation
 	 * until we're done changing it. We depend on i_mutex to block
 	 * other extend/truncate calls while we're here. Ordering wrt
@@ -566,6 +634,13 @@
 	down_write(&OCFS2_I(inode)->ip_alloc_sem);
 	drop_alloc_sem = 1;
 
+	status = ocfs2_lock_allocators(inode, fe, clusters_to_add, &data_ac,
+				       &meta_ac);
+	if (status) {
+		mlog_errno(status);
+		goto leave;
+	}
+
 	credits = ocfs2_calc_extend_credits(osb->sb, fe, clusters_to_add);
 	handle = ocfs2_start_trans(osb, credits);
 	if (IS_ERR(handle)) {
@@ -590,6 +665,7 @@
 
 	status = ocfs2_do_extend_allocation(osb,
 					    inode,
+					    &logical_start,
 					    clusters_to_add,
 					    bh,
 					    handle,
@@ -778,7 +854,7 @@
 			     size_t tail_to_skip)
 {
 	int ret = 0;
-	u32 clusters_to_add;
+	u32 clusters_to_add = 0;
 
 	BUG_ON(!tail_to_skip && !di_bh);
 
@@ -790,6 +866,11 @@
   		goto out;
 	BUG_ON(new_i_size < i_size_read(inode));
 
+	if (ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) {
+		BUG_ON(tail_to_skip != 0);
+		goto out_update_size;
+	}
+
 	clusters_to_add = ocfs2_clusters_for_bytes(inode->i_sb, new_i_size) - 
 		OCFS2_I(inode)->ip_clusters;
 
@@ -825,6 +906,7 @@
 		goto out_unlock;
 	}
 
+out_update_size:
 	if (!tail_to_skip) {
 		/* We're being called from ocfs2_setattr() which wants
 		 * us to update i_size */
@@ -834,7 +916,8 @@
 	}
 
 out_unlock:
-	ocfs2_data_unlock(inode, 1);
+	if (!ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb)))
+		ocfs2_data_unlock(inode, 1);
 
 out:
 	return ret;
@@ -972,7 +1055,8 @@
 
 	ret = ocfs2_meta_lock(inode, NULL, 0);
 	if (ret) {
-		mlog_errno(ret);
+		if (ret != -ENOENT)
+			mlog_errno(ret);
 		goto out;
 	}
 
@@ -1035,10 +1119,49 @@
 	return ret;
 }
 
+/*
+ * Will look for holes and unwritten extents in the range starting at
+ * pos for count bytes (inclusive).
+ */
+static int ocfs2_check_range_for_holes(struct inode *inode, loff_t pos,
+				       size_t count)
+{
+	int ret = 0;
+	unsigned int extent_flags;
+	u32 cpos, clusters, extent_len, phys_cpos;
+	struct super_block *sb = inode->i_sb;
+
+	cpos = pos >> OCFS2_SB(sb)->s_clustersize_bits;
+	clusters = ocfs2_clusters_for_bytes(sb, pos + count) - cpos;
+
+	while (clusters) {
+		ret = ocfs2_get_clusters(inode, cpos, &phys_cpos, &extent_len,
+					 &extent_flags);
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		if (phys_cpos == 0 || (extent_flags & OCFS2_EXT_UNWRITTEN)) {
+			ret = 1;
+			break;
+		}
+
+		if (extent_len > clusters)
+			extent_len = clusters;
+
+		clusters -= extent_len;
+		cpos += extent_len;
+	}
+out:
+	return ret;
+}
+
 static int ocfs2_prepare_inode_for_write(struct dentry *dentry,
 					 loff_t *ppos,
 					 size_t count,
-					 int appending)
+					 int appending,
+					 int *direct_io)
 {
 	int ret = 0, meta_level = appending;
 	struct inode *inode = dentry->d_inode;
@@ -1089,6 +1212,49 @@
 		} else {
 			saved_pos = *ppos;
 		}
+
+		if (ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) {
+			loff_t end = saved_pos + count;
+
+			/*
+			 * Skip the O_DIRECT checks if we don't need
+			 * them.
+			 */
+			if (!direct_io || !(*direct_io))
+				break;
+
+			/*
+			 * Allowing concurrent direct writes means
+			 * i_size changes wouldn't be synchronized, so
+			 * one node could wind up truncating another
+			 * nodes writes.
+			 */
+			if (end > i_size_read(inode)) {
+				*direct_io = 0;
+				break;
+			}
+
+			/*
+			 * We don't fill holes during direct io, so
+			 * check for them here. If any are found, the
+			 * caller will have to retake some cluster
+			 * locks and initiate the io as buffered.
+			 */
+			ret = ocfs2_check_range_for_holes(inode, saved_pos,
+							  count);
+			if (ret == 1) {
+				*direct_io = 0;
+				ret = 0;
+			} else if (ret < 0)
+				mlog_errno(ret);
+			break;
+		}
+
+		/*
+		 * The rest of this loop is concerned with legacy file
+		 * systems which don't support sparse files.
+		 */
+
 		newsize = count + saved_pos;
 
 		mlog(0, "pos=%lld newsize=%lld cursize=%lld\n",
@@ -1141,55 +1307,264 @@
 	return ret;
 }
 
+static inline void
+ocfs2_set_next_iovec(const struct iovec **iovp, size_t *basep, size_t bytes)
+{
+	const struct iovec *iov = *iovp;
+	size_t base = *basep;
+
+	do {
+		int copy = min(bytes, iov->iov_len - base);
+
+		bytes -= copy;
+		base += copy;
+		if (iov->iov_len == base) {
+			iov++;
+			base = 0;
+		}
+	} while (bytes);
+	*iovp = iov;
+	*basep = base;
+}
+
+static struct page * ocfs2_get_write_source(struct ocfs2_buffered_write_priv *bp,
+					    const struct iovec *cur_iov,
+					    size_t iov_offset)
+{
+	int ret;
+	char *buf;
+	struct page *src_page = NULL;
+
+	buf = cur_iov->iov_base + iov_offset;
+
+	if (!segment_eq(get_fs(), KERNEL_DS)) {
+		/*
+		 * Pull in the user page. We want to do this outside
+		 * of the meta data locks in order to preserve locking
+		 * order in case of page fault.
+		 */
+		ret = get_user_pages(current, current->mm,
+				     (unsigned long)buf & PAGE_CACHE_MASK, 1,
+				     0, 0, &src_page, NULL);
+		if (ret == 1)
+			bp->b_src_buf = kmap(src_page);
+		else
+			src_page = ERR_PTR(-EFAULT);
+	} else {
+		bp->b_src_buf = buf;
+	}
+
+	return src_page;
+}
+
+static void ocfs2_put_write_source(struct ocfs2_buffered_write_priv *bp,
+				   struct page *page)
+{
+	if (page) {
+		kunmap(page);
+		page_cache_release(page);
+	}
+}
+
+static ssize_t ocfs2_file_buffered_write(struct file *file, loff_t *ppos,
+					 const struct iovec *iov,
+					 unsigned long nr_segs,
+					 size_t count,
+					 ssize_t o_direct_written)
+{
+	int ret = 0;
+	ssize_t copied, total = 0;
+	size_t iov_offset = 0;
+	const struct iovec *cur_iov = iov;
+	struct ocfs2_buffered_write_priv bp;
+	struct page *page;
+
+	/*
+	 * handle partial DIO write.  Adjust cur_iov if needed.
+	 */
+	ocfs2_set_next_iovec(&cur_iov, &iov_offset, o_direct_written);
+
+	do {
+		bp.b_cur_off = iov_offset;
+		bp.b_cur_iov = cur_iov;
+
+		page = ocfs2_get_write_source(&bp, cur_iov, iov_offset);
+		if (IS_ERR(page)) {
+			ret = PTR_ERR(page);
+			goto out;
+		}
+
+		copied = ocfs2_buffered_write_cluster(file, *ppos, count,
+						      ocfs2_map_and_write_user_data,
+						      &bp);
+
+		ocfs2_put_write_source(&bp, page);
+
+		if (copied < 0) {
+			mlog_errno(copied);
+			ret = copied;
+			goto out;
+		}
+
+		total += copied;
+		*ppos = *ppos + copied;
+		count -= copied;
+
+		ocfs2_set_next_iovec(&cur_iov, &iov_offset, copied);
+	} while(count);
+
+out:
+	return total ? total : ret;
+}
+
+static int ocfs2_check_iovec(const struct iovec *iov, size_t *counted,
+			     unsigned long *nr_segs)
+{
+	size_t ocount;		/* original count */
+	unsigned long seg;
+
+	ocount = 0;
+	for (seg = 0; seg < *nr_segs; seg++) {
+		const struct iovec *iv = &iov[seg];
+
+		/*
+		 * If any segment has a negative length, or the cumulative
+		 * length ever wraps negative then return -EINVAL.
+		 */
+		ocount += iv->iov_len;
+		if (unlikely((ssize_t)(ocount|iv->iov_len) < 0))
+			return -EINVAL;
+		if (access_ok(VERIFY_READ, iv->iov_base, iv->iov_len))
+			continue;
+		if (seg == 0)
+			return -EFAULT;
+		*nr_segs = seg;
+		ocount -= iv->iov_len;	/* This segment is no good */
+		break;
+	}
+
+	*counted = ocount;
+	return 0;
+}
+
 static ssize_t ocfs2_file_aio_write(struct kiocb *iocb,
 				    const struct iovec *iov,
 				    unsigned long nr_segs,
 				    loff_t pos)
 {
-	int ret, rw_level, have_alloc_sem = 0;
-	struct file *filp = iocb->ki_filp;
-	struct inode *inode = filp->f_path.dentry->d_inode;
-	int appending = filp->f_flags & O_APPEND ? 1 : 0;
+	int ret, direct_io, appending, rw_level, have_alloc_sem  = 0;
+	int can_do_direct, sync = 0;
+	ssize_t written = 0;
+	size_t ocount;		/* original count */
+	size_t count;		/* after file limit checks */
+	loff_t *ppos = &iocb->ki_pos;
+	struct file *file = iocb->ki_filp;
+	struct inode *inode = file->f_path.dentry->d_inode;
 
-	mlog_entry("(0x%p, %u, '%.*s')\n", filp,
+	mlog_entry("(0x%p, %u, '%.*s')\n", file,
 		   (unsigned int)nr_segs,
-		   filp->f_path.dentry->d_name.len,
-		   filp->f_path.dentry->d_name.name);
+		   file->f_path.dentry->d_name.len,
+		   file->f_path.dentry->d_name.name);
 
-	/* happy write of zero bytes */
 	if (iocb->ki_left == 0)
 		return 0;
 
+	ret = ocfs2_check_iovec(iov, &ocount, &nr_segs);
+	if (ret)
+		return ret;
+
+	count = ocount;
+
+	vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
+
+	appending = file->f_flags & O_APPEND ? 1 : 0;
+	direct_io = file->f_flags & O_DIRECT ? 1 : 0;
+
 	mutex_lock(&inode->i_mutex);
+
+relock:
 	/* to match setattr's i_mutex -> i_alloc_sem -> rw_lock ordering */
-	if (filp->f_flags & O_DIRECT) {
-		have_alloc_sem = 1;
+	if (direct_io) {
 		down_read(&inode->i_alloc_sem);
+		have_alloc_sem = 1;
 	}
 
 	/* concurrent O_DIRECT writes are allowed */
-	rw_level = (filp->f_flags & O_DIRECT) ? 0 : 1;
+	rw_level = !direct_io;
 	ret = ocfs2_rw_lock(inode, rw_level);
 	if (ret < 0) {
-		rw_level = -1;
 		mlog_errno(ret);
-		goto out;
+		goto out_sems;
 	}
 
-	ret = ocfs2_prepare_inode_for_write(filp->f_path.dentry, &iocb->ki_pos,
-					    iocb->ki_left, appending);
+	can_do_direct = direct_io;
+	ret = ocfs2_prepare_inode_for_write(file->f_path.dentry, ppos,
+					    iocb->ki_left, appending,
+					    &can_do_direct);
 	if (ret < 0) {
 		mlog_errno(ret);
 		goto out;
 	}
 
+	/*
+	 * We can't complete the direct I/O as requested, fall back to
+	 * buffered I/O.
+	 */
+	if (direct_io && !can_do_direct) {
+		ocfs2_rw_unlock(inode, rw_level);
+		up_read(&inode->i_alloc_sem);
+
+		have_alloc_sem = 0;
+		rw_level = -1;
+
+		direct_io = 0;
+		sync = 1;
+		goto relock;
+	}
+
+	if (!sync && ((file->f_flags & O_SYNC) || IS_SYNC(inode)))
+		sync = 1;
+
+	/*
+	 * XXX: Is it ok to execute these checks a second time?
+	 */
+	ret = generic_write_checks(file, ppos, &count, S_ISBLK(inode->i_mode));
+	if (ret)
+		goto out;
+
+	/*
+	 * Set pos so that sync_page_range_nolock() below understands
+	 * where to start from. We might've moved it around via the
+	 * calls above. The range we want to actually sync starts from
+	 * *ppos here.
+	 *
+	 */
+	pos = *ppos;
+
 	/* communicate with ocfs2_dio_end_io */
-	ocfs2_iocb_set_rw_locked(iocb);
+	ocfs2_iocb_set_rw_locked(iocb, rw_level);
 
-	ret = generic_file_aio_write_nolock(iocb, iov, nr_segs, iocb->ki_pos);
+	if (direct_io) {
+		written = generic_file_direct_write(iocb, iov, &nr_segs, *ppos,
+						    ppos, count, ocount);
+		if (written < 0) {
+			ret = written;
+			goto out_dio;
+		}
+	} else {
+		written = ocfs2_file_buffered_write(file, ppos, iov, nr_segs,
+						    count, written);
+		if (written < 0) {
+			ret = written;
+			if (ret != -EFAULT || ret != -ENOSPC)
+				mlog_errno(ret);
+			goto out;
+		}
+	}
 
+out_dio:
 	/* buffered aio wouldn't have proper lock coverage today */
-	BUG_ON(ret == -EIOCBQUEUED && !(filp->f_flags & O_DIRECT));
+	BUG_ON(ret == -EIOCBQUEUED && !(file->f_flags & O_DIRECT));
 
 	/* 
 	 * deep in g_f_a_w_n()->ocfs2_direct_IO we pass in a ocfs2_dio_end_io
@@ -1207,13 +1582,102 @@
 	}
 
 out:
+	if (rw_level != -1)
+		ocfs2_rw_unlock(inode, rw_level);
+
+out_sems:
 	if (have_alloc_sem)
 		up_read(&inode->i_alloc_sem);
-	if (rw_level != -1) 
-		ocfs2_rw_unlock(inode, rw_level);
+
+	if (written > 0 && sync) {
+		ssize_t err;
+
+		err = sync_page_range_nolock(inode, file->f_mapping, pos, count);
+		if (err < 0)
+			written = err;
+	}
+
 	mutex_unlock(&inode->i_mutex);
 
 	mlog_exit(ret);
+	return written ? written : ret;
+}
+
+static int ocfs2_splice_write_actor(struct pipe_inode_info *pipe,
+				    struct pipe_buffer *buf,
+				    struct splice_desc *sd)
+{
+	int ret, count, total = 0;
+	ssize_t copied = 0;
+	struct ocfs2_splice_write_priv sp;
+
+	ret = buf->ops->pin(pipe, buf);
+	if (ret)
+		goto out;
+
+	sp.s_sd = sd;
+	sp.s_buf = buf;
+	sp.s_pipe = pipe;
+	sp.s_offset = sd->pos & ~PAGE_CACHE_MASK;
+	sp.s_buf_offset = buf->offset;
+
+	count = sd->len;
+	if (count + sp.s_offset > PAGE_CACHE_SIZE)
+		count = PAGE_CACHE_SIZE - sp.s_offset;
+
+	do {
+		/*
+		 * splice wants us to copy up to one page at a
+		 * time. For pagesize > cluster size, this means we
+		 * might enter ocfs2_buffered_write_cluster() more
+		 * than once, so keep track of our progress here.
+		 */
+		copied = ocfs2_buffered_write_cluster(sd->file,
+						      (loff_t)sd->pos + total,
+						      count,
+						      ocfs2_map_and_write_splice_data,
+						      &sp);
+		if (copied < 0) {
+			mlog_errno(copied);
+			ret = copied;
+			goto out;
+		}
+
+		count -= copied;
+		sp.s_offset += copied;
+		sp.s_buf_offset += copied;
+		total += copied;
+	} while (count);
+
+	ret = 0;
+out:
+
+	return total ? total : ret;
+}
+
+static ssize_t __ocfs2_file_splice_write(struct pipe_inode_info *pipe,
+					 struct file *out,
+					 loff_t *ppos,
+					 size_t len,
+					 unsigned int flags)
+{
+	int ret, err;
+	struct address_space *mapping = out->f_mapping;
+	struct inode *inode = mapping->host;
+
+	ret = __splice_from_pipe(pipe, out, ppos, len, flags,
+				 ocfs2_splice_write_actor);
+	if (ret > 0) {
+		*ppos += ret;
+
+		if (unlikely((out->f_flags & O_SYNC) || IS_SYNC(inode))) {
+			err = generic_osync_inode(inode, mapping,
+						  OSYNC_METADATA|OSYNC_DATA);
+			if (err)
+				ret = err;
+		}
+	}
+
 	return ret;
 }
 
@@ -1239,14 +1703,15 @@
 		goto out;
 	}
 
-	ret = ocfs2_prepare_inode_for_write(out->f_path.dentry, ppos, len, 0);
+	ret = ocfs2_prepare_inode_for_write(out->f_path.dentry, ppos, len, 0,
+					    NULL);
 	if (ret < 0) {
 		mlog_errno(ret);
 		goto out_unlock;
 	}
 
 	/* ok, we're done with i_size and alloc work */
-	ret = generic_file_splice_write_nolock(pipe, out, ppos, len, flags);
+	ret = __ocfs2_file_splice_write(pipe, out, ppos, len, flags);
 
 out_unlock:
 	ocfs2_rw_unlock(inode, 1);
@@ -1323,7 +1788,7 @@
 		}
 		rw_level = 0;
 		/* communicate with ocfs2_dio_end_io */
-		ocfs2_iocb_set_rw_locked(iocb);
+		ocfs2_iocb_set_rw_locked(iocb, rw_level);
 	}
 
 	/*
diff --git a/fs/ocfs2/file.h b/fs/ocfs2/file.h
index cc973f0..2c4460f 100644
--- a/fs/ocfs2/file.h
+++ b/fs/ocfs2/file.h
@@ -39,12 +39,17 @@
 };
 int ocfs2_do_extend_allocation(struct ocfs2_super *osb,
 			       struct inode *inode,
+			       u32 *cluster_start,
 			       u32 clusters_to_add,
 			       struct buffer_head *fe_bh,
 			       handle_t *handle,
 			       struct ocfs2_alloc_context *data_ac,
 			       struct ocfs2_alloc_context *meta_ac,
 			       enum ocfs2_alloc_restarted *reason);
+int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di,
+			  u32 clusters_to_add,
+			  struct ocfs2_alloc_context **data_ac,
+			  struct ocfs2_alloc_context **meta_ac);
 int ocfs2_setattr(struct dentry *dentry, struct iattr *attr);
 int ocfs2_getattr(struct vfsmount *mnt, struct dentry *dentry,
 		  struct kstat *stat);
diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c
index 28ab56f..21a6050 100644
--- a/fs/ocfs2/inode.c
+++ b/fs/ocfs2/inode.c
@@ -89,24 +89,6 @@
 		inode->i_flags |= S_DIRSYNC;
 }
 
-struct inode *ocfs2_ilookup_for_vote(struct ocfs2_super *osb,
-				     u64 blkno,
-				     int delete_vote)
-{
-	struct ocfs2_find_inode_args args;
-
-	/* ocfs2_ilookup_for_vote should *only* be called from the
-	 * vote thread */
-	BUG_ON(current != osb->vote_task);
-
-	args.fi_blkno = blkno;
-	args.fi_flags = OCFS2_FI_FLAG_NOWAIT;
-	if (delete_vote)
-		args.fi_flags |= OCFS2_FI_FLAG_DELETE;
-	args.fi_ino = ino_from_blkno(osb->sb, blkno);
-	return ilookup5(osb->sb, args.fi_ino, ocfs2_find_actor, &args);
-}
-
 struct inode *ocfs2_iget(struct ocfs2_super *osb, u64 blkno, int flags)
 {
 	struct inode *inode = NULL;
@@ -182,28 +164,6 @@
 	if (oi->ip_blkno != args->fi_blkno)
 		goto bail;
 
-	/* OCFS2_FI_FLAG_NOWAIT is *only* set from
-	 * ocfs2_ilookup_for_vote which won't create an inode for one
-	 * that isn't found. The vote thread which doesn't want to get
-	 * an inode which is in the process of going away - otherwise
-	 * the call to __wait_on_freeing_inode in find_inode_fast will
-	 * cause it to deadlock on an inode which may be waiting on a
-	 * vote (or lock release) in delete_inode */
-	if ((args->fi_flags & OCFS2_FI_FLAG_NOWAIT) &&
-	    (inode->i_state & (I_FREEING|I_CLEAR))) {
-		/* As stated above, we're not going to return an
-		 * inode.  In the case of a delete vote, the voting
-		 * code is going to signal the other node to go
-		 * ahead. Mark that state here, so this freeing inode
-		 * has the state when it gets to delete_inode. */
-		if (args->fi_flags & OCFS2_FI_FLAG_DELETE) {
-			spin_lock(&oi->ip_lock);
-			ocfs2_mark_inode_remotely_deleted(inode);
-			spin_unlock(&oi->ip_lock);
-		}
-		goto bail;
-	}
-
 	ret = 1;
 bail:
 	mlog_exit(ret);
@@ -261,6 +221,9 @@
 		goto bail;
 	}
 
+	OCFS2_I(inode)->ip_clusters = le32_to_cpu(fe->i_clusters);
+	OCFS2_I(inode)->ip_attr = le32_to_cpu(fe->i_attr);
+
 	inode->i_version = 1;
 	inode->i_generation = le32_to_cpu(fe->i_generation);
 	inode->i_rdev = huge_decode_dev(le64_to_cpu(fe->id1.dev1.i_rdev));
@@ -272,8 +235,7 @@
 	if (S_ISLNK(inode->i_mode) && !fe->i_clusters)
 		inode->i_blocks = 0;
 	else
-		inode->i_blocks =
-			ocfs2_align_bytes_to_sectors(le64_to_cpu(fe->i_size));
+		inode->i_blocks = ocfs2_inode_sector_count(inode);
 	inode->i_mapping->a_ops = &ocfs2_aops;
 	inode->i_atime.tv_sec = le64_to_cpu(fe->i_atime);
 	inode->i_atime.tv_nsec = le32_to_cpu(fe->i_atime_nsec);
@@ -288,10 +250,6 @@
 		     (unsigned long long)OCFS2_I(inode)->ip_blkno,
 		     (unsigned long long)fe->i_blkno);
 
-	OCFS2_I(inode)->ip_clusters = le32_to_cpu(fe->i_clusters);
-	OCFS2_I(inode)->ip_orphaned_slot = OCFS2_INVALID_SLOT;
-	OCFS2_I(inode)->ip_attr = le32_to_cpu(fe->i_attr);
-
 	inode->i_nlink = le16_to_cpu(fe->i_links_count);
 
 	if (fe->i_flags & cpu_to_le32(OCFS2_SYSTEM_FL))
@@ -347,6 +305,9 @@
 
 		ocfs2_inode_lock_res_init(&OCFS2_I(inode)->ip_meta_lockres,
 					  OCFS2_LOCK_TYPE_META, 0, inode);
+
+		ocfs2_inode_lock_res_init(&OCFS2_I(inode)->ip_open_lockres,
+					  OCFS2_LOCK_TYPE_OPEN, 0, inode);
 	}
 
 	ocfs2_inode_lock_res_init(&OCFS2_I(inode)->ip_rw_lockres,
@@ -421,7 +382,7 @@
 	 * cluster lock before trusting anything anyway.
 	 */
 	can_lock = !(args->fi_flags & OCFS2_FI_FLAG_SYSFILE)
-		&& !(args->fi_flags & OCFS2_FI_FLAG_NOLOCK)
+		&& !(args->fi_flags & OCFS2_FI_FLAG_ORPHAN_RECOVERY)
 		&& !ocfs2_mount_local(osb);
 
 	/*
@@ -438,7 +399,17 @@
 				  OCFS2_LOCK_TYPE_META,
 				  generation, inode);
 
+	ocfs2_inode_lock_res_init(&OCFS2_I(inode)->ip_open_lockres,
+				  OCFS2_LOCK_TYPE_OPEN,
+				  0, inode);
+
 	if (can_lock) {
+		status = ocfs2_open_lock(inode);
+		if (status) {
+			make_bad_inode(inode);
+			mlog_errno(status);
+			return status;
+		}
 		status = ocfs2_meta_lock(inode, NULL, 0);
 		if (status) {
 			make_bad_inode(inode);
@@ -447,6 +418,14 @@
 		}
 	}
 
+	if (args->fi_flags & OCFS2_FI_FLAG_ORPHAN_RECOVERY) {
+		status = ocfs2_try_open_lock(inode, 0);
+		if (status) {
+			make_bad_inode(inode);	
+			return status;
+		}
+	}
+
 	status = ocfs2_read_block(osb, args->fi_blkno, &bh, 0,
 				  can_lock ? inode : NULL);
 	if (status < 0) {
@@ -507,50 +486,56 @@
 				     struct buffer_head *fe_bh)
 {
 	int status = 0;
-	handle_t *handle = NULL;
 	struct ocfs2_truncate_context *tc = NULL;
 	struct ocfs2_dinode *fe;
+	handle_t *handle = NULL;
 
 	mlog_entry_void();
 
 	fe = (struct ocfs2_dinode *) fe_bh->b_data;
 
-	/* zero allocation, zero truncate :) */
-	if (!fe->i_clusters)
-		goto bail;
+	if (fe->i_clusters) {
+		handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
+		if (IS_ERR(handle)) {
+			status = PTR_ERR(handle);
+			mlog_errno(status);
+			goto out;
+		}
 
-	handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
-	if (IS_ERR(handle)) {
-		status = PTR_ERR(handle);
+		status = ocfs2_journal_access(handle, inode, fe_bh,
+					      OCFS2_JOURNAL_ACCESS_WRITE);
+		if (status < 0) {
+			mlog_errno(status);
+			goto out;
+		}
+
+		i_size_write(inode, 0);
+
+		status = ocfs2_mark_inode_dirty(handle, inode, fe_bh);
+		if (status < 0) {
+			mlog_errno(status);
+			goto out;
+		}
+
+		ocfs2_commit_trans(osb, handle);
 		handle = NULL;
-		mlog_errno(status);
-		goto bail;
+
+		status = ocfs2_prepare_truncate(osb, inode, fe_bh, &tc);
+		if (status < 0) {
+			mlog_errno(status);
+			goto out;
+		}
+
+		status = ocfs2_commit_truncate(osb, inode, fe_bh, tc);
+		if (status < 0) {
+			mlog_errno(status);
+			goto out;
+		}
 	}
 
-	status = ocfs2_set_inode_size(handle, inode, fe_bh, 0ULL);
-	if (status < 0) {
-		mlog_errno(status);
-		goto bail;
-	}
-
-	ocfs2_commit_trans(osb, handle);
-	handle = NULL;
-
-	status = ocfs2_prepare_truncate(osb, inode, fe_bh, &tc);
-	if (status < 0) {
-		mlog_errno(status);
-		goto bail;
-	}
-
-	status = ocfs2_commit_truncate(osb, inode, fe_bh, tc);
-	if (status < 0) {
-		mlog_errno(status);
-		goto bail;
-	}
-bail:
+out:
 	if (handle)
 		ocfs2_commit_trans(osb, handle);
-
 	mlog_exit(status);
 	return status;
 }
@@ -678,10 +663,10 @@
 	struct inode *orphan_dir_inode = NULL;
 	struct buffer_head *orphan_dir_bh = NULL;
 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct ocfs2_dinode *di;
 
-	/* We've already voted on this so it should be readonly - no
-	 * spinlock needed. */
-	orphaned_slot = OCFS2_I(inode)->ip_orphaned_slot;
+	di = (struct ocfs2_dinode *) di_bh->b_data;
+	orphaned_slot = le16_to_cpu(di->i_orphaned_slot);
 
 	status = ocfs2_check_orphan_recovery_state(osb, orphaned_slot);
 	if (status)
@@ -839,11 +824,20 @@
 		goto bail;
 	}
 
-	status = ocfs2_request_delete_vote(inode);
-	/* -EBUSY means that other nodes are still using the
-	 * inode. We're done here though, so avoid doing anything on
-	 * disk and let them worry about deleting it. */
-	if (status == -EBUSY) {
+	/*
+	 * This is how ocfs2 determines whether an inode is still live
+	 * within the cluster. Every node takes a shared read lock on
+	 * the inode open lock in ocfs2_read_locked_inode(). When we
+	 * get to ->delete_inode(), each node tries to convert it's
+	 * lock to an exclusive. Trylocks are serialized by the inode
+	 * meta data lock. If the upconvert suceeds, we know the inode
+	 * is no longer live and can be deleted.
+	 *
+	 * Though we call this with the meta data lock held, the
+	 * trylock keeps us from ABBA deadlock.
+	 */
+	status = ocfs2_try_open_lock(inode, 1);
+	if (status == -EAGAIN) {
 		status = 0;
 		mlog(0, "Skipping delete of %llu because it is in use on"
 		     "other nodes\n", (unsigned long long)oi->ip_blkno);
@@ -854,21 +848,10 @@
 		goto bail;
 	}
 
-	spin_lock(&oi->ip_lock);
-	if (oi->ip_orphaned_slot == OCFS2_INVALID_SLOT) {
-		/* Nobody knew which slot this inode was orphaned
-		 * into. This may happen during node death and
-		 * recovery knows how to clean it up so we can safely
-		 * ignore this inode for now on. */
-		mlog(0, "Nobody knew where inode %llu was orphaned!\n",
-		     (unsigned long long)oi->ip_blkno);
-	} else {
-		*wipe = 1;
-
-		mlog(0, "Inode %llu is ok to wipe from orphan dir %d\n",
-		     (unsigned long long)oi->ip_blkno, oi->ip_orphaned_slot);
-	}
-	spin_unlock(&oi->ip_lock);
+	*wipe = 1;
+	mlog(0, "Inode %llu is ok to wipe from orphan dir %u\n",
+	     (unsigned long long)oi->ip_blkno,
+	     le16_to_cpu(di->i_orphaned_slot));
 
 bail:
 	return status;
@@ -1001,11 +984,16 @@
 	mlog_bug_on_msg(OCFS2_SB(inode->i_sb) == NULL,
 			"Inode=%lu\n", inode->i_ino);
 
+	/* For remove delete_inode vote, we hold open lock before,
+	 * now it is time to unlock PR and EX open locks. */
+	ocfs2_open_unlock(inode);
+
 	/* Do these before all the other work so that we don't bounce
 	 * the vote thread while waiting to destroy the locks. */
 	ocfs2_mark_lockres_freeing(&oi->ip_rw_lockres);
 	ocfs2_mark_lockres_freeing(&oi->ip_meta_lockres);
 	ocfs2_mark_lockres_freeing(&oi->ip_data_lockres);
+	ocfs2_mark_lockres_freeing(&oi->ip_open_lockres);
 
 	/* We very well may get a clear_inode before all an inodes
 	 * metadata has hit disk. Of course, we can't drop any cluster
@@ -1020,8 +1008,7 @@
 			"Clear inode of %llu, inode has io markers\n",
 			(unsigned long long)oi->ip_blkno);
 
-	ocfs2_extent_map_drop(inode, 0);
-	ocfs2_extent_map_init(inode);
+	ocfs2_extent_map_trunc(inode, 0);
 
 	status = ocfs2_drop_inode_locks(inode);
 	if (status < 0)
@@ -1030,6 +1017,7 @@
 	ocfs2_lock_res_free(&oi->ip_rw_lockres);
 	ocfs2_lock_res_free(&oi->ip_meta_lockres);
 	ocfs2_lock_res_free(&oi->ip_data_lockres);
+	ocfs2_lock_res_free(&oi->ip_open_lockres);
 
 	ocfs2_metadata_cache_purge(inode);
 
@@ -1086,9 +1074,6 @@
 	mlog(0, "Drop inode %llu, nlink = %u, ip_flags = 0x%x\n",
 	     (unsigned long long)oi->ip_blkno, inode->i_nlink, oi->ip_flags);
 
-	/* Testing ip_orphaned_slot here wouldn't work because we may
-	 * not have gotten a delete_inode vote from any other nodes
-	 * yet. */
 	if (oi->ip_flags & OCFS2_INODE_MAYBE_ORPHANED)
 		generic_delete_inode(inode);
 	else
@@ -1121,8 +1106,8 @@
 		return NULL;
 	}
 
-	tmperr = ocfs2_extent_map_get_blocks(inode, block, 1,
-					     &p_blkno, NULL);
+	tmperr = ocfs2_extent_map_get_blocks(inode, block, &p_blkno, NULL,
+					     NULL);
 	if (tmperr < 0) {
 		mlog_errno(tmperr);
 		goto fail;
@@ -1259,7 +1244,7 @@
 	if (S_ISLNK(inode->i_mode) && le32_to_cpu(fe->i_clusters) == 0)
 		inode->i_blocks = 0;
 	else
-		inode->i_blocks = ocfs2_align_bytes_to_sectors(i_size_read(inode));
+		inode->i_blocks = ocfs2_inode_sector_count(inode);
 	inode->i_atime.tv_sec = le64_to_cpu(fe->i_atime);
 	inode->i_atime.tv_nsec = le32_to_cpu(fe->i_atime_nsec);
 	inode->i_mtime.tv_sec = le64_to_cpu(fe->i_mtime);
diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h
index 1a7dd29..03ae075 100644
--- a/fs/ocfs2/inode.h
+++ b/fs/ocfs2/inode.h
@@ -26,6 +26,8 @@
 #ifndef OCFS2_INODE_H
 #define OCFS2_INODE_H
 
+#include "extent_map.h"
+
 /* OCFS2 Inode Private Data */
 struct ocfs2_inode_info
 {
@@ -34,6 +36,7 @@
 	struct ocfs2_lock_res		ip_rw_lockres;
 	struct ocfs2_lock_res		ip_meta_lockres;
 	struct ocfs2_lock_res		ip_data_lockres;
+	struct ocfs2_lock_res		ip_open_lockres;
 
 	/* protects allocation changes on this inode. */
 	struct rw_semaphore		ip_alloc_sem;
@@ -42,9 +45,7 @@
 	spinlock_t			ip_lock;
 	u32				ip_open_count;
 	u32				ip_clusters;
-	struct ocfs2_extent_map		ip_map;
 	struct list_head		ip_io_markers;
-	int				ip_orphaned_slot;
 
 	struct mutex			ip_io_mutex;
 
@@ -64,6 +65,8 @@
 
 	struct ocfs2_caching_info	ip_metadata_cache;
 
+	struct ocfs2_extent_map		ip_extent_map;
+
 	struct inode			vfs_inode;
 };
 
@@ -117,14 +120,9 @@
 void ocfs2_drop_inode(struct inode *inode);
 
 /* Flags for ocfs2_iget() */
-#define OCFS2_FI_FLAG_NOWAIT	0x1
-#define OCFS2_FI_FLAG_DELETE	0x2
-#define OCFS2_FI_FLAG_SYSFILE	0x4
-#define OCFS2_FI_FLAG_NOLOCK	0x8
+#define OCFS2_FI_FLAG_SYSFILE		0x4
+#define OCFS2_FI_FLAG_ORPHAN_RECOVERY	0x8
 struct inode *ocfs2_iget(struct ocfs2_super *osb, u64 feoff, int flags);
-struct inode *ocfs2_ilookup_for_vote(struct ocfs2_super *osb,
-				     u64 blkno,
-				     int delete_vote);
 int ocfs2_inode_init_private(struct inode *inode);
 int ocfs2_inode_revalidate(struct dentry *dentry);
 int ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe,
@@ -144,4 +142,11 @@
 
 void ocfs2_set_inode_flags(struct inode *inode);
 
+static inline blkcnt_t ocfs2_inode_sector_count(struct inode *inode)
+{
+	int c_to_s_bits = OCFS2_SB(inode->i_sb)->s_clustersize_bits - 9;
+
+	return (blkcnt_t)(OCFS2_I(inode)->ip_clusters << c_to_s_bits);
+}
+
 #endif /* OCFS2_INODE_H */
diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c
index 825cb0a..5a8a90d 100644
--- a/fs/ocfs2/journal.c
+++ b/fs/ocfs2/journal.c
@@ -649,29 +649,20 @@
 static int ocfs2_force_read_journal(struct inode *inode)
 {
 	int status = 0;
-	int i, p_blocks;
-	u64 v_blkno, p_blkno;
-#define CONCURRENT_JOURNAL_FILL 32
+	int i;
+	u64 v_blkno, p_blkno, p_blocks, num_blocks;
+#define CONCURRENT_JOURNAL_FILL 32ULL
 	struct buffer_head *bhs[CONCURRENT_JOURNAL_FILL];
 
 	mlog_entry_void();
 
-	BUG_ON(inode->i_blocks !=
-		     ocfs2_align_bytes_to_sectors(i_size_read(inode)));
-
 	memset(bhs, 0, sizeof(struct buffer_head *) * CONCURRENT_JOURNAL_FILL);
 
-	mlog(0, "Force reading %llu blocks\n",
-		(unsigned long long)(inode->i_blocks >>
-			(inode->i_sb->s_blocksize_bits - 9)));
-
+	num_blocks = ocfs2_blocks_for_bytes(inode->i_sb, inode->i_size);
 	v_blkno = 0;
-	while (v_blkno <
-	       (inode->i_blocks >> (inode->i_sb->s_blocksize_bits - 9))) {
-
+	while (v_blkno < num_blocks) {
 		status = ocfs2_extent_map_get_blocks(inode, v_blkno,
-						     1, &p_blkno,
-						     &p_blocks);
+						     &p_blkno, &p_blocks, NULL);
 		if (status < 0) {
 			mlog_errno(status);
 			goto bail;
@@ -1306,7 +1297,7 @@
 				continue;
 
 			iter = ocfs2_iget(osb, le64_to_cpu(de->inode),
-					  OCFS2_FI_FLAG_NOLOCK);
+					  OCFS2_FI_FLAG_ORPHAN_RECOVERY);
 			if (IS_ERR(iter))
 				continue;
 
@@ -1418,7 +1409,6 @@
 		/* Set the proper information to get us going into
 		 * ocfs2_delete_inode. */
 		oi->ip_flags |= OCFS2_INODE_MAYBE_ORPHANED;
-		oi->ip_orphaned_slot = slot;
 		spin_unlock(&oi->ip_lock);
 
 		iput(inode);
diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h
index d026b4f..3db5de4 100644
--- a/fs/ocfs2/journal.h
+++ b/fs/ocfs2/journal.h
@@ -390,7 +390,7 @@
 	/* We may be deleting metadata blocks, so metadata alloc dinode +
 	   one desc. block for each possible delete. */
 	if (tree_depth && next_free == 1 &&
-	    le32_to_cpu(last_el->l_recs[i].e_clusters) == clusters_to_del)
+	    ocfs2_rec_clusters(last_el, &last_el->l_recs[i]) == clusters_to_del)
 		credits += 1 + tree_depth;
 
 	/* update to the truncate log. */
diff --git a/fs/ocfs2/mmap.c b/fs/ocfs2/mmap.c
index 51b0204..af01158 100644
--- a/fs/ocfs2/mmap.c
+++ b/fs/ocfs2/mmap.c
@@ -85,8 +85,11 @@
 	int ret = 0, lock_level = 0;
 	struct ocfs2_super *osb = OCFS2_SB(file->f_dentry->d_inode->i_sb);
 
-	/* We don't want to support shared writable mappings yet. */
-	if (!ocfs2_mount_local(osb) &&
+	/*
+	 * Only support shared writeable mmap for local mounts which
+	 * don't know about holes.
+	 */
+	if ((!ocfs2_mount_local(osb) || ocfs2_sparse_alloc(osb)) &&
 	    ((vma->vm_flags & VM_SHARED) || (vma->vm_flags & VM_MAYSHARE)) &&
 	    ((vma->vm_flags & VM_WRITE) || (vma->vm_flags & VM_MAYWRITE))) {
 		mlog(0, "disallow shared writable mmaps %lx\n", vma->vm_flags);
diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c
index 28dd757..2bcf353 100644
--- a/fs/ocfs2/namei.c
+++ b/fs/ocfs2/namei.c
@@ -175,8 +175,6 @@
 
 	inode = ocfs2_iget(OCFS2_SB(dir->i_sb), blkno, 0);
 	if (IS_ERR(inode)) {
-		mlog(ML_ERROR, "Unable to create inode %llu\n",
-		     (unsigned long long)blkno);
 		ret = ERR_PTR(-EACCES);
 		goto bail_unlock;
 	}
@@ -189,7 +187,6 @@
 	 * unlink. */
 	spin_lock(&oi->ip_lock);
 	oi->ip_flags &= ~OCFS2_INODE_MAYBE_ORPHANED;
-	oi->ip_orphaned_slot = OCFS2_INVALID_SLOT;
 	spin_unlock(&oi->ip_lock);
 
 bail_add:
@@ -288,7 +285,7 @@
 
 	i_size_write(inode, inode->i_sb->s_blocksize);
 	inode->i_nlink = 2;
-	inode->i_blocks = ocfs2_align_bytes_to_sectors(inode->i_sb->s_blocksize);
+	inode->i_blocks = ocfs2_inode_sector_count(inode);
 	status = ocfs2_mark_inode_dirty(handle, inode, fe_bh);
 	if (status < 0) {
 		mlog_errno(status);
@@ -1486,8 +1483,7 @@
 	struct buffer_head **bhs = NULL;
 	const char *c;
 	struct super_block *sb = osb->sb;
-	u64 p_blkno;
-	int p_blocks;
+	u64 p_blkno, p_blocks;
 	int virtual, blocks, status, i, bytes_left;
 
 	bytes_left = i_size_read(inode) + 1;
@@ -1514,8 +1510,8 @@
 		goto bail;
 	}
 
-	status = ocfs2_extent_map_get_blocks(inode, 0, 1, &p_blkno,
-					     &p_blocks);
+	status = ocfs2_extent_map_get_blocks(inode, 0, &p_blkno, &p_blocks,
+					     NULL);
 	if (status < 0) {
 		mlog_errno(status);
 		goto bail;
@@ -1674,8 +1670,11 @@
 	inode->i_rdev = 0;
 	newsize = l - 1;
 	if (l > ocfs2_fast_symlink_chars(sb)) {
+		u32 offset = 0;
+
 		inode->i_op = &ocfs2_symlink_inode_operations;
-		status = ocfs2_do_extend_allocation(osb, inode, 1, new_fe_bh,
+		status = ocfs2_do_extend_allocation(osb, inode, &offset, 1,
+						    new_fe_bh,
 						    handle, data_ac, NULL,
 						    NULL);
 		if (status < 0) {
@@ -1689,7 +1688,7 @@
 			goto bail;
 		}
 		i_size_write(inode, newsize);
-		inode->i_blocks = ocfs2_align_bytes_to_sectors(newsize);
+		inode->i_blocks = ocfs2_inode_sector_count(inode);
 	} else {
 		inode->i_op = &ocfs2_fast_symlink_inode_operations;
 		memcpy((char *) fe->id2.i_symlink, symname, l);
@@ -2222,9 +2221,7 @@
 	/* Record which orphan dir our inode now resides
 	 * in. delete_inode will use this to determine which orphan
 	 * dir to lock. */
-	spin_lock(&OCFS2_I(inode)->ip_lock);
-	OCFS2_I(inode)->ip_orphaned_slot = osb->slot_num;
-	spin_unlock(&OCFS2_I(inode)->ip_lock);
+	fe->i_orphaned_slot = cpu_to_le16(osb->slot_num);
 
 	mlog(0, "Inode %llu orphaned in slot %d\n",
 	     (unsigned long long)OCFS2_I(inode)->ip_blkno, osb->slot_num);
diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h
index db8e77c..82cc92d 100644
--- a/fs/ocfs2/ocfs2.h
+++ b/fs/ocfs2/ocfs2.h
@@ -46,11 +46,6 @@
 #include "endian.h"
 #include "ocfs2_lockid.h"
 
-struct ocfs2_extent_map {
-	u32		em_clusters;
-	struct rb_root	em_extents;
-};
-
 /* Most user visible OCFS2 inodes will have very few pieces of
  * metadata, but larger files (including bitmaps, etc) must be taken
  * into account when designing an access scheme. We allow a small
@@ -303,6 +298,13 @@
 	return 1;
 }
 
+static inline int ocfs2_sparse_alloc(struct ocfs2_super *osb)
+{
+	if (osb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC)
+		return 1;
+	return 0;
+}
+
 /* set / clear functions because cluster events can make these happen
  * in parallel so we want the transitions to be atomic. this also
  * means that any future flags osb_flags must be protected by spinlock
@@ -461,6 +463,49 @@
 	return (unsigned long)((bytes + 511) >> 9);
 }
 
+static inline unsigned int ocfs2_page_index_to_clusters(struct super_block *sb,
+							unsigned long pg_index)
+{
+	u32 clusters = pg_index;
+	unsigned int cbits = OCFS2_SB(sb)->s_clustersize_bits;
+
+	if (unlikely(PAGE_CACHE_SHIFT > cbits))
+		clusters = pg_index << (PAGE_CACHE_SHIFT - cbits);
+	else if (PAGE_CACHE_SHIFT < cbits)
+		clusters = pg_index >> (cbits - PAGE_CACHE_SHIFT);
+
+	return clusters;
+}
+
+/*
+ * Find the 1st page index which covers the given clusters.
+ */
+static inline unsigned long ocfs2_align_clusters_to_page_index(struct super_block *sb,
+							u32 clusters)
+{
+	unsigned int cbits = OCFS2_SB(sb)->s_clustersize_bits;
+	unsigned long index = clusters;
+
+	if (PAGE_CACHE_SHIFT > cbits) {
+		index = clusters >> (PAGE_CACHE_SHIFT - cbits);
+	} else if (PAGE_CACHE_SHIFT < cbits) {
+		index = clusters << (cbits - PAGE_CACHE_SHIFT);
+	}
+
+	return index;
+}
+
+static inline unsigned int ocfs2_pages_per_cluster(struct super_block *sb)
+{
+	unsigned int cbits = OCFS2_SB(sb)->s_clustersize_bits;
+	unsigned int pages_per_cluster = 1;
+
+	if (PAGE_CACHE_SHIFT < cbits)
+		pages_per_cluster = 1 << (cbits - PAGE_CACHE_SHIFT);
+
+	return pages_per_cluster;
+}
+
 #define ocfs2_set_bit ext2_set_bit
 #define ocfs2_clear_bit ext2_clear_bit
 #define ocfs2_test_bit ext2_test_bit
diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h
index e61e218..7130647 100644
--- a/fs/ocfs2/ocfs2_fs.h
+++ b/fs/ocfs2/ocfs2_fs.h
@@ -86,7 +86,8 @@
 	OCFS2_SB(sb)->s_feature_incompat &= ~(mask)
 
 #define OCFS2_FEATURE_COMPAT_SUPP	OCFS2_FEATURE_COMPAT_BACKUP_SB
-#define OCFS2_FEATURE_INCOMPAT_SUPP	OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT
+#define OCFS2_FEATURE_INCOMPAT_SUPP	(OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT \
+					 | OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC)
 #define OCFS2_FEATURE_RO_COMPAT_SUPP	0
 
 /*
@@ -155,6 +156,12 @@
 #define OCFS2_FL_MODIFIABLE	(0x000100FF)	/* User modifiable flags */
 
 /*
+ * Extent record flags (e_node.leaf.flags)
+ */
+#define OCFS2_EXT_UNWRITTEN	(0x01)	/* Extent is allocated but
+					 * unwritten */
+
+/*
  * ioctl commands
  */
 #define OCFS2_IOC_GETFLAGS	_IOR('f', 1, long)
@@ -282,10 +289,21 @@
 /*
  * On disk extent record for OCFS2
  * It describes a range of clusters on disk.
+ *
+ * Length fields are divided into interior and leaf node versions.
+ * This leaves room for a flags field (OCFS2_EXT_*) in the leaf nodes.
  */
 struct ocfs2_extent_rec {
 /*00*/	__le32 e_cpos;		/* Offset into the file, in clusters */
-	__le32 e_clusters;	/* Clusters covered by this extent */
+	union {
+		__le32 e_int_clusters; /* Clusters covered by all children */
+		struct {
+			__le16 e_leaf_clusters; /* Clusters covered by this
+						   extent */
+			__u8 e_reserved1;
+			__u8 e_flags; /* Extent flags */
+		};
+	};
 	__le64 e_blkno;		/* Physical disk offset, in blocks */
 /*10*/
 };
@@ -311,7 +329,10 @@
 /*00*/	__le16 l_tree_depth;		/* Extent tree depth from this
 					   point.  0 means data extents
 					   hang directly off this
-					   header (a leaf) */
+					   header (a leaf)
+					   NOTE: The high 8 bits cannot be
+					   used - tree_depth is never that big.
+					*/
 	__le16 l_count;			/* Number of extent records */
 	__le16 l_next_free_rec;		/* Next unused extent slot */
 	__le16 l_reserved1;
@@ -446,7 +467,9 @@
 	__le32 i_ctime_nsec;
 	__le32 i_mtime_nsec;
 	__le32 i_attr;
-	__le32 i_reserved1;
+	__le16 i_orphaned_slot;		/* Only valid when OCFS2_ORPHANED_FL
+					   was set in i_flags */
+	__le16 i_reserved1;
 /*70*/	__le64 i_reserved2[8];
 /*B8*/	union {
 		__le64 i_pad1;		/* Generic way to refer to this
diff --git a/fs/ocfs2/ocfs2_lockid.h b/fs/ocfs2/ocfs2_lockid.h
index 4d5d565..4ca02b1 100644
--- a/fs/ocfs2/ocfs2_lockid.h
+++ b/fs/ocfs2/ocfs2_lockid.h
@@ -44,6 +44,7 @@
 	OCFS2_LOCK_TYPE_RENAME,
 	OCFS2_LOCK_TYPE_RW,
 	OCFS2_LOCK_TYPE_DENTRY,
+	OCFS2_LOCK_TYPE_OPEN,
 	OCFS2_NUM_LOCK_TYPES
 };
 
@@ -69,6 +70,9 @@
 		case OCFS2_LOCK_TYPE_DENTRY:
 			c = 'N';
 			break;
+		case OCFS2_LOCK_TYPE_OPEN:
+			c = 'O';
+			break;
 		default:
 			c = '\0';
 	}
@@ -85,6 +89,7 @@
 	 * important job it does, anyway. */
 	[OCFS2_LOCK_TYPE_RW] = "Write/Read",
 	[OCFS2_LOCK_TYPE_DENTRY] = "Dentry",
+	[OCFS2_LOCK_TYPE_OPEN] = "Open",
 };
 
 static inline const char *ocfs2_lock_type_string(enum ocfs2_lock_type type)
diff --git a/fs/ocfs2/slot_map.c b/fs/ocfs2/slot_map.c
index 2d3ac32..d921a28 100644
--- a/fs/ocfs2/slot_map.c
+++ b/fs/ocfs2/slot_map.c
@@ -197,7 +197,7 @@
 		goto bail;
 	}
 
-	status = ocfs2_extent_map_get_blocks(inode, 0ULL, 1, &blkno, NULL);
+	status = ocfs2_extent_map_get_blocks(inode, 0ULL, &blkno, NULL, NULL);
 	if (status < 0) {
 		mlog_errno(status);
 		goto bail;
diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c
index 6dbb117..0da655a 100644
--- a/fs/ocfs2/suballoc.c
+++ b/fs/ocfs2/suballoc.c
@@ -381,8 +381,7 @@
 					     le32_to_cpu(fe->i_clusters)));
 	spin_unlock(&OCFS2_I(alloc_inode)->ip_lock);
 	i_size_write(alloc_inode, le64_to_cpu(fe->i_size));
-	alloc_inode->i_blocks =
-		ocfs2_align_bytes_to_sectors(i_size_read(alloc_inode));
+	alloc_inode->i_blocks = ocfs2_inode_sector_count(alloc_inode);
 
 	status = 0;
 bail:
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index 6534f92..5c9e824 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -806,9 +806,6 @@
 
 	ocfs2_print_version();
 
-	if (init_ocfs2_extent_maps())
-		return -ENOMEM;
-
 	status = init_ocfs2_uptodate_cache();
 	if (status < 0) {
 		mlog_errno(status);
@@ -837,7 +834,6 @@
 	if (status < 0) {
 		ocfs2_free_mem_caches();
 		exit_ocfs2_uptodate_cache();
-		exit_ocfs2_extent_maps();
 	}
 
 	mlog_exit(status);
@@ -863,8 +859,6 @@
 
 	unregister_filesystem(&ocfs2_fs_type);
 
-	exit_ocfs2_extent_maps();
-
 	exit_ocfs2_uptodate_cache();
 
 	mlog_exit_void();
@@ -963,6 +957,7 @@
 		ocfs2_lock_res_init_once(&oi->ip_rw_lockres);
 		ocfs2_lock_res_init_once(&oi->ip_meta_lockres);
 		ocfs2_lock_res_init_once(&oi->ip_data_lockres);
+		ocfs2_lock_res_init_once(&oi->ip_open_lockres);
 
 		ocfs2_metadata_cache_init(&oi->vfs_inode);
 
diff --git a/fs/ocfs2/vote.c b/fs/ocfs2/vote.c
index f30e63b..4f82a2f 100644
--- a/fs/ocfs2/vote.c
+++ b/fs/ocfs2/vote.c
@@ -63,17 +63,10 @@
 	__be32 h_node_num;    /* node sending this particular message. */
 };
 
-/* OCFS2_MAX_FILENAME_LEN is 255 characters, but we want to align this
- * for the network. */
-#define OCFS2_VOTE_FILENAME_LEN 256
 struct ocfs2_vote_msg
 {
 	struct ocfs2_msg_hdr v_hdr;
-	union {
-		__be32 v_generic1;
-		__be32 v_orphaned_slot;	/* Used during delete votes */
-		__be32 v_nlink;		/* Used during unlink votes */
-	} md1;				/* Message type dependant 1 */
+	__be32 v_reserved1;
 };
 
 /* Responses are given these values to maintain backwards
@@ -86,7 +79,6 @@
 {
 	struct ocfs2_msg_hdr r_hdr;
 	__be32 r_response;
-	__be32 r_orphaned_slot;
 };
 
 struct ocfs2_vote_work {
@@ -96,7 +88,6 @@
 
 enum ocfs2_vote_request {
 	OCFS2_VOTE_REQ_INVALID = 0,
-	OCFS2_VOTE_REQ_DELETE,
 	OCFS2_VOTE_REQ_MOUNT,
 	OCFS2_VOTE_REQ_UMOUNT,
 	OCFS2_VOTE_REQ_LAST
@@ -151,135 +142,23 @@
 	ocfs2_node_map_set_bit(osb, &osb->umount_map, node_num);
 }
 
-void ocfs2_mark_inode_remotely_deleted(struct inode *inode)
-{
-	struct ocfs2_inode_info *oi = OCFS2_I(inode);
-
-	assert_spin_locked(&oi->ip_lock);
-	/* We set the SKIP_DELETE flag on the inode so we don't try to
-	 * delete it in delete_inode ourselves, thus avoiding
-	 * unecessary lock pinging. If the other node failed to wipe
-	 * the inode as a result of a crash, then recovery will pick
-	 * up the slack. */
-	oi->ip_flags |= OCFS2_INODE_DELETED|OCFS2_INODE_SKIP_DELETE;
-}
-
-static int ocfs2_process_delete_request(struct inode *inode,
-					int *orphaned_slot)
-{
-	int response = OCFS2_RESPONSE_BUSY;
-
-	mlog(0, "DELETE vote on inode %lu, read lnk_cnt = %u, slot = %d\n",
-	     inode->i_ino, inode->i_nlink, *orphaned_slot);
-
-	spin_lock(&OCFS2_I(inode)->ip_lock);
-
-	/* Whatever our vote response is, we want to make sure that
-	 * the orphaned slot is recorded properly on this node *and*
-	 * on the requesting node. Technically, if the requesting node
-	 * did not know which slot the inode is orphaned in but we
-	 * respond with BUSY he doesn't actually need the orphaned
-	 * slot, but it doesn't hurt to do it here anyway. */
-	if ((*orphaned_slot) != OCFS2_INVALID_SLOT) {
-		mlog_bug_on_msg(OCFS2_I(inode)->ip_orphaned_slot !=
-				OCFS2_INVALID_SLOT &&
-				OCFS2_I(inode)->ip_orphaned_slot !=
-				(*orphaned_slot),
-				"Inode %llu: This node thinks it's "
-				"orphaned in slot %d, messaged it's in %d\n",
-				(unsigned long long)OCFS2_I(inode)->ip_blkno,
-				OCFS2_I(inode)->ip_orphaned_slot,
-				*orphaned_slot);
-
-		mlog(0, "Setting orphaned slot for inode %llu to %d\n",
-		     (unsigned long long)OCFS2_I(inode)->ip_blkno,
-		     *orphaned_slot);
-
-		OCFS2_I(inode)->ip_orphaned_slot = *orphaned_slot;
-	} else {
-		mlog(0, "Sending back orphaned slot %d for inode %llu\n",
-		     OCFS2_I(inode)->ip_orphaned_slot,
-		     (unsigned long long)OCFS2_I(inode)->ip_blkno);
-
-		*orphaned_slot = OCFS2_I(inode)->ip_orphaned_slot;
-	}
-
-	/* vote no if the file is still open. */
-	if (OCFS2_I(inode)->ip_open_count) {
-		mlog(0, "open count = %u\n",
-		     OCFS2_I(inode)->ip_open_count);
-		spin_unlock(&OCFS2_I(inode)->ip_lock);
-		goto done;
-	}
-	spin_unlock(&OCFS2_I(inode)->ip_lock);
-
-	/* directories are a bit ugly... What if someone is sitting in
-	 * it? We want to make sure the inode is removed completely as
-	 * a result of the iput in process_vote. */
-	if (S_ISDIR(inode->i_mode) && (atomic_read(&inode->i_count) != 1)) {
-		mlog(0, "i_count = %u\n", atomic_read(&inode->i_count));
-		goto done;
-	}
-
-	if (filemap_fdatawrite(inode->i_mapping)) {
-		mlog(ML_ERROR, "Could not sync inode %llu for delete!\n",
-		     (unsigned long long)OCFS2_I(inode)->ip_blkno);
-		goto done;
-	}
-	sync_mapping_buffers(inode->i_mapping);
-	truncate_inode_pages(inode->i_mapping, 0);
-	ocfs2_extent_map_trunc(inode, 0);
-
-	spin_lock(&OCFS2_I(inode)->ip_lock);
-	/* double check open count - someone might have raced this
-	 * thread into ocfs2_file_open while we were writing out
-	 * data. If we're to allow a wipe of this inode now, we *must*
-	 * hold the spinlock until we've marked it. */
-	if (OCFS2_I(inode)->ip_open_count) {
-		mlog(0, "Raced to wipe! open count = %u\n",
-		     OCFS2_I(inode)->ip_open_count);
-		spin_unlock(&OCFS2_I(inode)->ip_lock);
-		goto done;
-	}
-
-	/* Mark the inode as being wiped from disk. */
-	ocfs2_mark_inode_remotely_deleted(inode);
-	spin_unlock(&OCFS2_I(inode)->ip_lock);
-
-	/* Not sure this is necessary anymore. */
-	d_prune_aliases(inode);
-
-	/* If we get here, then we're voting 'yes', so commit the
-	 * delete on our side. */
-	response = OCFS2_RESPONSE_OK;
-done:
-	return response;
-}
-
 static void ocfs2_process_vote(struct ocfs2_super *osb,
 			       struct ocfs2_vote_msg *msg)
 {
 	int net_status, vote_response;
-	int orphaned_slot = 0;
-	unsigned int node_num, generation;
+	unsigned int node_num;
 	u64 blkno;
 	enum ocfs2_vote_request request;
-	struct inode *inode = NULL;
 	struct ocfs2_msg_hdr *hdr = &msg->v_hdr;
 	struct ocfs2_response_msg response;
 
 	/* decode the network mumbo jumbo into local variables. */
 	request = be32_to_cpu(hdr->h_request);
 	blkno = be64_to_cpu(hdr->h_blkno);
-	generation = be32_to_cpu(hdr->h_generation);
 	node_num = be32_to_cpu(hdr->h_node_num);
-	if (request == OCFS2_VOTE_REQ_DELETE)
-		orphaned_slot = be32_to_cpu(msg->md1.v_orphaned_slot);
 
-	mlog(0, "processing vote: request = %u, blkno = %llu, "
-	     "generation = %u, node_num = %u, priv1 = %u\n", request,
-	     (unsigned long long)blkno, generation, node_num,
-	     be32_to_cpu(msg->md1.v_generic1));
+	mlog(0, "processing vote: request = %u, blkno = %llu, node_num = %u\n",
+	     request, (unsigned long long)blkno, node_num);
 
 	if (!ocfs2_is_valid_vote_request(request)) {
 		mlog(ML_ERROR, "Invalid vote request %d from node %u\n",
@@ -302,52 +181,6 @@
 		break;
 	}
 
-	/* We cannot process the remaining message types before we're
-	 * fully mounted. It's perfectly safe however to send a 'yes'
-	 * response as we can't possibly have any of the state they're
-	 * asking us to modify yet. */
-	if (atomic_read(&osb->vol_state) == VOLUME_INIT)
-		goto respond;
-
-	/* If we get here, then the request is against an inode. */
-	inode = ocfs2_ilookup_for_vote(osb, blkno,
-				       request == OCFS2_VOTE_REQ_DELETE);
-
-	/* Not finding the inode is perfectly valid - it means we're
-	 * not interested in what the other node is about to do to it
-	 * so in those cases we automatically respond with an
-	 * affirmative. Cluster locking ensures that we won't race
-	 * interest in the inode with this vote request. */
-	if (!inode)
-		goto respond;
-
-	/* Check generation values. It's possible for us to get a
-	 * request against a stale inode. If so then we proceed as if
-	 * we had not found an inode in the first place. */
-	if (inode->i_generation != generation) {
-		mlog(0, "generation passed %u != inode generation = %u, "
-		     "ip_flags = %x, ip_blkno = %llu, msg %llu, i_count = %u, "
-		     "message type = %u\n", generation, inode->i_generation,
-		     OCFS2_I(inode)->ip_flags,
-		     (unsigned long long)OCFS2_I(inode)->ip_blkno,
-		     (unsigned long long)blkno, atomic_read(&inode->i_count),
-		     request);
-		iput(inode);
-		inode = NULL;
-		goto respond;
-	}
-
-	switch (request) {
-	case OCFS2_VOTE_REQ_DELETE:
-		vote_response = ocfs2_process_delete_request(inode,
-							     &orphaned_slot);
-		break;
-	default:
-		mlog(ML_ERROR, "node %u, invalid request: %u\n",
-		     node_num, request);
-		vote_response = OCFS2_RESPONSE_BAD_MSG;
-	}
-
 respond:
 	/* Response struture is small so we just put it on the stack
 	 * and stuff it inline. */
@@ -357,7 +190,6 @@
 	response.r_hdr.h_generation = hdr->h_generation;
 	response.r_hdr.h_node_num = cpu_to_be32(osb->node_num);
 	response.r_response = cpu_to_be32(vote_response);
-	response.r_orphaned_slot = cpu_to_be32(orphaned_slot);
 
 	net_status = o2net_send_message(OCFS2_MESSAGE_TYPE_RESPONSE,
 					osb->net_key,
@@ -373,9 +205,6 @@
 	    && net_status != -ENOTCONN)
 		mlog(ML_ERROR, "message to node %u fails with error %d!\n",
 		     node_num, net_status);
-
-	if (inode)
-		iput(inode);
 }
 
 static void ocfs2_vote_thread_do_work(struct ocfs2_super *osb)
@@ -634,8 +463,7 @@
 static struct ocfs2_vote_msg * ocfs2_new_vote_request(struct ocfs2_super *osb,
 						      u64 blkno,
 						      unsigned int generation,
-						      enum ocfs2_vote_request type,
-						      u32 priv)
+						      enum ocfs2_vote_request type)
 {
 	struct ocfs2_vote_msg *request;
 	struct ocfs2_msg_hdr *hdr;
@@ -651,8 +479,6 @@
 		hdr->h_request = cpu_to_be32(type);
 		hdr->h_blkno = cpu_to_be64(blkno);
 		hdr->h_generation = cpu_to_be32(generation);
-
-		request->md1.v_generic1 = cpu_to_be32(priv);
 	}
 
 	return request;
@@ -664,7 +490,7 @@
 				 struct ocfs2_vote_msg *request,
 				 struct ocfs2_net_response_cb *callback)
 {
-	int status, response;
+	int status, response = -EBUSY;
 	unsigned int response_id;
 	struct ocfs2_msg_hdr *hdr;
 
@@ -686,109 +512,12 @@
 	return status;
 }
 
-static int ocfs2_request_vote(struct inode *inode,
-			      struct ocfs2_vote_msg *request,
-			      struct ocfs2_net_response_cb *callback)
-{
-	int status;
-	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
-
-	if (ocfs2_inode_is_new(inode))
-		return 0;
-
-	status = -EAGAIN;
-	while (status == -EAGAIN) {
-		if (!(osb->s_mount_opt & OCFS2_MOUNT_NOINTR) &&
-		    signal_pending(current))
-			return -ERESTARTSYS;
-
-		status = ocfs2_super_lock(osb, 0);
-		if (status < 0) {
-			mlog_errno(status);
-			break;
-		}
-
-		status = 0;
-		if (!ocfs2_node_map_is_only(osb, &osb->mounted_map,
-					   osb->node_num))
-			status = ocfs2_do_request_vote(osb, request, callback);
-
-		ocfs2_super_unlock(osb, 0);
-	}
-	return status;
-}
-
-static void ocfs2_delete_response_cb(void *priv,
-				     struct ocfs2_response_msg *resp)
-{
-	int orphaned_slot, node;
-	struct inode *inode = priv;
-
-	orphaned_slot = be32_to_cpu(resp->r_orphaned_slot);
-	node = be32_to_cpu(resp->r_hdr.h_node_num);
-	mlog(0, "node %d tells us that inode %llu is orphaned in slot %d\n",
-	     node, (unsigned long long)OCFS2_I(inode)->ip_blkno,
-	     orphaned_slot);
-
-	/* The other node may not actually know which slot the inode
-	 * is orphaned in. */
-	if (orphaned_slot == OCFS2_INVALID_SLOT)
-		return;
-
-	/* Ok, the responding node knows which slot this inode is
-	 * orphaned in. We verify that the information is correct and
-	 * then record this in the inode. ocfs2_delete_inode will use
-	 * this information to determine which lock to take. */
-	spin_lock(&OCFS2_I(inode)->ip_lock);
-	mlog_bug_on_msg(OCFS2_I(inode)->ip_orphaned_slot != orphaned_slot &&
-			OCFS2_I(inode)->ip_orphaned_slot
-			!= OCFS2_INVALID_SLOT, "Inode %llu: Node %d says it's "
-			"orphaned in slot %d, we think it's in %d\n",
-			(unsigned long long)OCFS2_I(inode)->ip_blkno,
-			be32_to_cpu(resp->r_hdr.h_node_num),
-			orphaned_slot, OCFS2_I(inode)->ip_orphaned_slot);
-
-	OCFS2_I(inode)->ip_orphaned_slot = orphaned_slot;
-	spin_unlock(&OCFS2_I(inode)->ip_lock);
-}
-
-int ocfs2_request_delete_vote(struct inode *inode)
-{
-	int orphaned_slot, status;
-	struct ocfs2_net_response_cb delete_cb;
-	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
-	struct ocfs2_vote_msg *request;
-
-	spin_lock(&OCFS2_I(inode)->ip_lock);
-	orphaned_slot = OCFS2_I(inode)->ip_orphaned_slot;
-	spin_unlock(&OCFS2_I(inode)->ip_lock);
-
-	delete_cb.rc_cb = ocfs2_delete_response_cb;
-	delete_cb.rc_priv = inode;
-
-	mlog(0, "Inode %llu, we start thinking orphaned slot is %d\n",
-	     (unsigned long long)OCFS2_I(inode)->ip_blkno, orphaned_slot);
-
-	status = -ENOMEM;
-	request = ocfs2_new_vote_request(osb, OCFS2_I(inode)->ip_blkno,
-					 inode->i_generation,
-					 OCFS2_VOTE_REQ_DELETE, orphaned_slot);
-	if (request) {
-		status = ocfs2_request_vote(inode, request, &delete_cb);
-
-		kfree(request);
-	}
-
-	return status;
-}
-
 int ocfs2_request_mount_vote(struct ocfs2_super *osb)
 {
 	int status;
 	struct ocfs2_vote_msg *request = NULL;
 
-	request = ocfs2_new_vote_request(osb, 0ULL, 0,
-					 OCFS2_VOTE_REQ_MOUNT, 0);
+	request = ocfs2_new_vote_request(osb, 0ULL, 0, OCFS2_VOTE_REQ_MOUNT);
 	if (!request) {
 		status = -ENOMEM;
 		goto bail;
@@ -821,8 +550,7 @@
 	int status;
 	struct ocfs2_vote_msg *request = NULL;
 
-	request = ocfs2_new_vote_request(osb, 0ULL, 0,
-					 OCFS2_VOTE_REQ_UMOUNT, 0);
+	request = ocfs2_new_vote_request(osb, 0ULL, 0, OCFS2_VOTE_REQ_UMOUNT);
 	if (!request) {
 		status = -ENOMEM;
 		goto bail;
@@ -969,7 +697,6 @@
 	     be32_to_cpu(work->w_msg.v_hdr.h_generation));
 	mlog(0, "h_node_num = %u\n",
 	     be32_to_cpu(work->w_msg.v_hdr.h_node_num));
-	mlog(0, "v_generic1 = %u\n", be32_to_cpu(work->w_msg.md1.v_generic1));
 
 	spin_lock(&osb->vote_task_lock);
 	list_add_tail(&work->w_list, &osb->vote_list);
diff --git a/fs/ocfs2/vote.h b/fs/ocfs2/vote.h
index 53ebc1c6..9ea46f6 100644
--- a/fs/ocfs2/vote.h
+++ b/fs/ocfs2/vote.h
@@ -38,14 +38,11 @@
 	wake_up(&osb->vote_event);
 }
 
-int ocfs2_request_delete_vote(struct inode *inode);
 int ocfs2_request_mount_vote(struct ocfs2_super *osb);
 int ocfs2_request_umount_vote(struct ocfs2_super *osb);
 int ocfs2_register_net_handlers(struct ocfs2_super *osb);
 void ocfs2_unregister_net_handlers(struct ocfs2_super *osb);
 
-void ocfs2_mark_inode_remotely_deleted(struct inode *inode);
-
 void ocfs2_remove_node_from_vote_queues(struct ocfs2_super *osb,
 					int node_num);
 #endif
diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c
index abdf068..eca471bc 100644
--- a/fs/proc/proc_devtree.c
+++ b/fs/proc/proc_devtree.c
@@ -38,7 +38,7 @@
 		n = count;
 	else
 		*eof = 1;
-	memcpy(page, pp->value + off, n);
+	memcpy(page, (char *)pp->value + off, n);
 	*start = page;
 	return n;
 }
diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c
index c8178b7..2cac562 100644
--- a/fs/reiserfs/xattr.c
+++ b/fs/reiserfs/xattr.c
@@ -68,7 +68,7 @@
 	if (!privroot)
 		return ERR_PTR(-ENODATA);
 
-	mutex_lock(&privroot->d_inode->i_mutex);
+	mutex_lock_nested(&privroot->d_inode->i_mutex, I_MUTEX_XATTR);
 	if (REISERFS_SB(sb)->xattr_root) {
 		xaroot = dget(REISERFS_SB(sb)->xattr_root);
 		goto out;
diff --git a/fs/super.c b/fs/super.c
index 60b1e50..8341e4e 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -725,16 +725,6 @@
 	return (void *)s->s_bdev == data;
 }
 
-static void bdev_uevent(struct block_device *bdev, enum kobject_action action)
-{
-	if (bdev->bd_disk) {
-		if (bdev->bd_part)
-			kobject_uevent(&bdev->bd_part->kobj, action);
-		else
-			kobject_uevent(&bdev->bd_disk->kobj, action);
-	}
-}
-
 int get_sb_bdev(struct file_system_type *fs_type,
 	int flags, const char *dev_name, void *data,
 	int (*fill_super)(struct super_block *, void *, int),
@@ -782,7 +772,6 @@
 		}
 
 		s->s_flags |= MS_ACTIVE;
-		bdev_uevent(bdev, KOBJ_MOUNT);
 	}
 
 	return simple_set_mnt(mnt, s);
@@ -801,7 +790,6 @@
 {
 	struct block_device *bdev = sb->s_bdev;
 
-	bdev_uevent(bdev, KOBJ_UMOUNT);
 	generic_shutdown_super(sb);
 	sync_blockdev(bdev);
 	close_bdev_excl(bdev);
diff --git a/fs/sync.c b/fs/sync.c
index d0feff6..5cb9e7e 100644
--- a/fs/sync.c
+++ b/fs/sync.c
@@ -239,13 +239,11 @@
 /*
  * `endbyte' is inclusive
  */
-int do_sync_file_range(struct file *file, loff_t offset, loff_t endbyte,
-			unsigned int flags)
+int do_sync_mapping_range(struct address_space *mapping, loff_t offset,
+			  loff_t endbyte, unsigned int flags)
 {
 	int ret;
-	struct address_space *mapping;
 
-	mapping = file->f_mapping;
 	if (!mapping) {
 		ret = -EINVAL;
 		goto out;
@@ -275,4 +273,4 @@
 out:
 	return ret;
 }
-EXPORT_SYMBOL_GPL(do_sync_file_range);
+EXPORT_SYMBOL_GPL(do_sync_mapping_range);
diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c
index d3b9f5f..8ea2a51 100644
--- a/fs/sysfs/bin.c
+++ b/fs/sysfs/bin.c
@@ -59,7 +59,7 @@
 	if (copy_to_user(userbuf, buffer, count))
 		return -EFAULT;
 
-	pr_debug("offs = %lld, *off = %lld, count = %zd\n", offs, *off, count);
+	pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count);
 
 	*off = offs + count;
 
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index fc46333..db0413a 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -633,6 +633,7 @@
 	struct kobject 		*kobj;
 	void			(*func)(void *);
 	void			*data;
+	struct module		*owner;
 	struct work_struct	work;
 };
 
@@ -643,6 +644,7 @@
 
 	(ss->func)(ss->data);
 	kobject_put(ss->kobj);
+	module_put(ss->owner);
 	kfree(ss);
 }
 
@@ -651,6 +653,7 @@
  * @kobj: object we're acting for.
  * @func: callback function to invoke later.
  * @data: argument to pass to @func.
+ * @owner: module owning the callback code
  *
  * sysfs attribute methods must not unregister themselves or their parent
  * kobject (which would amount to the same thing).  Attempts to do so will
@@ -663,20 +666,25 @@
  * until @func returns.
  *
  * Returns 0 if the request was submitted, -ENOMEM if storage could not
- * be allocated.
+ * be allocated, -ENODEV if a reference to @owner isn't available.
  */
 int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *),
-		void *data)
+		void *data, struct module *owner)
 {
 	struct sysfs_schedule_callback_struct *ss;
 
+	if (!try_module_get(owner))
+		return -ENODEV;
 	ss = kmalloc(sizeof(*ss), GFP_KERNEL);
-	if (!ss)
+	if (!ss) {
+		module_put(owner);
 		return -ENOMEM;
+	}
 	kobject_get(kobj);
 	ss->kobj = kobj;
 	ss->func = func;
 	ss->data = data;
+	ss->owner = owner;
 	INIT_WORK(&ss->work, sysfs_schedule_callback_work);
 	schedule_work(&ss->work);
 	return 0;
diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c
index b20951c..52eed2a 100644
--- a/fs/sysfs/group.c
+++ b/fs/sysfs/group.c
@@ -70,9 +70,11 @@
 {
 	struct dentry * dir;
 
-	if (grp->name)
-		dir = lookup_one_len(grp->name, kobj->dentry,
+	if (grp->name) {
+		dir = lookup_one_len_kern(grp->name, kobj->dentry,
 				strlen(grp->name));
+		BUG_ON(IS_ERR(dir));
+	}
 	else
 		dir = dget(kobj->dentry);
 
diff --git a/include/acpi/actbl.h b/include/acpi/actbl.h
index 09469e7..955adfb 100644
--- a/include/acpi/actbl.h
+++ b/include/acpi/actbl.h
@@ -276,6 +276,7 @@
 
 #define BAF_LEGACY_DEVICES              0x0001
 #define BAF_8042_KEYBOARD_CONTROLLER    0x0002
+#define BAF_MSI_NOT_SUPPORTED           0x0008
 
 #define FADT2_REVISION_ID               3
 #define FADT2_MINUS_REVISION_ID         2
diff --git a/include/asm-alpha/socket.h b/include/asm-alpha/socket.h
index d22ab97..1fede7f 100644
--- a/include/asm-alpha/socket.h
+++ b/include/asm-alpha/socket.h
@@ -52,6 +52,8 @@
 
 #define SO_PEERSEC		30
 #define SO_PASSSEC		34
+#define SO_TIMESTAMPNS		35
+#define SCM_TIMESTAMPNS		SO_TIMESTAMPNS
 
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_SECURITY_AUTHENTICATION		19
diff --git a/include/asm-alpha/sockios.h b/include/asm-alpha/sockios.h
index e4961a7..7932c7a 100644
--- a/include/asm-alpha/sockios.h
+++ b/include/asm-alpha/sockios.h
@@ -10,6 +10,7 @@
 #define SIOCSPGRP	_IOW('s', 8, pid_t)
 #define SIOCGPGRP	_IOR('s', 9, pid_t)
 
-#define SIOCGSTAMP	0x8906		/* Get stamp - linux-specific */
+#define SIOCGSTAMP	0x8906		/* Get stamp (timeval) */
+#define SIOCGSTAMPNS	0x8907		/* Get stamp (timespec) */
 
 #endif /* _ASM_ALPHA_SOCKIOS_H */
diff --git a/include/asm-alpha/string.h b/include/asm-alpha/string.h
index 9e44fea..b02b8a2 100644
--- a/include/asm-alpha/string.h
+++ b/include/asm-alpha/string.h
@@ -61,8 +61,6 @@
  ? __constant_c_memset((s),0x0001000100010001UL*(unsigned short)(c),(n)) \
  : __memsetw((s),(c),(n)))
 
-extern int strcasecmp(const char *, const char *);
-
 #endif /* __KERNEL__ */
 
 #endif /* __ALPHA_STRING_H__ */
diff --git a/include/asm-arm/div64.h b/include/asm-arm/div64.h
index 37e0a96..0b5f881 100644
--- a/include/asm-arm/div64.h
+++ b/include/asm-arm/div64.h
@@ -2,6 +2,7 @@
 #define __ASM_ARM_DIV64
 
 #include <asm/system.h>
+#include <linux/types.h>
 
 /*
  * The semantics of do_div() are:
@@ -223,4 +224,6 @@
 
 #endif
 
+extern uint64_t div64_64(uint64_t dividend, uint64_t divisor);
+
 #endif
diff --git a/include/asm-arm/socket.h b/include/asm-arm/socket.h
index 19f7df7..65a1a64 100644
--- a/include/asm-arm/socket.h
+++ b/include/asm-arm/socket.h
@@ -49,5 +49,7 @@
 
 #define SO_PEERSEC		31
 #define SO_PASSSEC		34
+#define SO_TIMESTAMPNS		35
+#define SCM_TIMESTAMPNS		SO_TIMESTAMPNS
 
 #endif /* _ASM_SOCKET_H */
diff --git a/include/asm-arm/sockios.h b/include/asm-arm/sockios.h
index 77c3408..a2588a2 100644
--- a/include/asm-arm/sockios.h
+++ b/include/asm-arm/sockios.h
@@ -7,6 +7,7 @@
 #define FIOGETOWN	0x8903
 #define SIOCGPGRP	0x8904
 #define SIOCATMARK	0x8905
-#define SIOCGSTAMP	0x8906		/* Get stamp */
+#define SIOCGSTAMP	0x8906		/* Get stamp (timeval) */
+#define SIOCGSTAMPNS	0x8907		/* Get stamp (timespec) */
 
 #endif
diff --git a/include/asm-arm26/socket.h b/include/asm-arm26/socket.h
index 19f7df7..65a1a64 100644
--- a/include/asm-arm26/socket.h
+++ b/include/asm-arm26/socket.h
@@ -49,5 +49,7 @@
 
 #define SO_PEERSEC		31
 #define SO_PASSSEC		34
+#define SO_TIMESTAMPNS		35
+#define SCM_TIMESTAMPNS		SO_TIMESTAMPNS
 
 #endif /* _ASM_SOCKET_H */
diff --git a/include/asm-arm26/sockios.h b/include/asm-arm26/sockios.h
index 77c3408..a2588a2 100644
--- a/include/asm-arm26/sockios.h
+++ b/include/asm-arm26/sockios.h
@@ -7,6 +7,7 @@
 #define FIOGETOWN	0x8903
 #define SIOCGPGRP	0x8904
 #define SIOCATMARK	0x8905
-#define SIOCGSTAMP	0x8906		/* Get stamp */
+#define SIOCGSTAMP	0x8906		/* Get stamp (timeval) */
+#define SIOCGSTAMPNS	0x8907		/* Get stamp (timespec) */
 
 #endif
diff --git a/include/asm-avr32/arch-at32ap/io.h b/include/asm-avr32/arch-at32ap/io.h
new file mode 100644
index 0000000..ee59e40
--- /dev/null
+++ b/include/asm-avr32/arch-at32ap/io.h
@@ -0,0 +1,39 @@
+#ifndef __ASM_AVR32_ARCH_AT32AP_IO_H
+#define __ASM_AVR32_ARCH_AT32AP_IO_H
+
+/* For "bizarre" halfword swapping */
+#include <linux/byteorder/swabb.h>
+
+#if defined(CONFIG_AP7000_32_BIT_SMC)
+# define __swizzle_addr_b(addr)	(addr ^ 3UL)
+# define __swizzle_addr_w(addr)	(addr ^ 2UL)
+# define __swizzle_addr_l(addr)	(addr)
+# define ioswabb(a, x)		(x)
+# define ioswabw(a, x)		(x)
+# define ioswabl(a, x)		(x)
+# define __mem_ioswabb(a, x)	(x)
+# define __mem_ioswabw(a, x)	swab16(x)
+# define __mem_ioswabl(a, x)	swab32(x)
+#elif defined(CONFIG_AP7000_16_BIT_SMC)
+# define __swizzle_addr_b(addr)	(addr ^ 1UL)
+# define __swizzle_addr_w(addr)	(addr)
+# define __swizzle_addr_l(addr)	(addr)
+# define ioswabb(a, x)		(x)
+# define ioswabw(a, x)		(x)
+# define ioswabl(a, x)		swahw32(x)
+# define __mem_ioswabb(a, x)	(x)
+# define __mem_ioswabw(a, x)	swab16(x)
+# define __mem_ioswabl(a, x)	swahb32(x)
+#else
+# define __swizzle_addr_b(addr)	(addr)
+# define __swizzle_addr_w(addr)	(addr)
+# define __swizzle_addr_l(addr)	(addr)
+# define ioswabb(a, x)		(x)
+# define ioswabw(a, x)		swab16(x)
+# define ioswabl(a, x)		swab32(x)
+# define __mem_ioswabb(a, x)	(x)
+# define __mem_ioswabw(a, x)	(x)
+# define __mem_ioswabl(a, x)	(x)
+#endif
+
+#endif /* __ASM_AVR32_ARCH_AT32AP_IO_H */
diff --git a/include/asm-avr32/arch-at32ap/smc.h b/include/asm-avr32/arch-at32ap/smc.h
index 3732b32..07152b7 100644
--- a/include/asm-avr32/arch-at32ap/smc.h
+++ b/include/asm-avr32/arch-at32ap/smc.h
@@ -48,10 +48,32 @@
 	unsigned int	nwe_controlled:1;
 
 	/*
+	 * 0: NWAIT is disabled
+	 * 1: Reserved
+	 * 2: NWAIT is frozen mode
+	 * 3: NWAIT in ready mode
+	 */
+	unsigned int	nwait_mode:2;
+
+	/*
 	 * 0: Byte select access type
 	 * 1: Byte write access type
 	 */
 	unsigned int	byte_write:1;
+
+	/*
+	 * Number of clock cycles before data is released after
+	 * the rising edge of the read controlling signal
+	 *
+	 * Total cycles from SMC is tdf_cycles + 1
+	 */
+	unsigned int	tdf_cycles:4;
+
+	/*
+	 * 0: TDF optimization disabled
+	 * 1: TDF optimization enabled
+	 */
+	unsigned int	tdf_mode:1;
 };
 
 extern int smc_set_configuration(int cs, const struct smc_config *config);
diff --git a/include/asm-avr32/arch-at32ap/time.h b/include/asm-avr32/arch-at32ap/time.h
new file mode 100644
index 0000000..cc8a434
--- /dev/null
+++ b/include/asm-avr32/arch-at32ap/time.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2007 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _ASM_AVR32_ARCH_AT32AP_TIME_H
+#define _ASM_AVR32_ARCH_AT32AP_TIME_H
+
+#include <linux/platform_device.h>
+
+extern struct irqaction timer_irqaction;
+extern struct platform_device at32_systc0_device;
+extern void local_timer_interrupt(int irq, void *dev_id);
+
+#define TIMER_BCR					0x000000c0
+#define TIMER_BCR_SYNC						 0
+#define TIMER_BMR					0x000000c4
+#define TIMER_BMR_TC0XC0S					 0
+#define TIMER_BMR_TC1XC1S					 2
+#define TIMER_BMR_TC2XC2S					 4
+#define TIMER_CCR					0x00000000
+#define TIMER_CCR_CLKDIS					 1
+#define TIMER_CCR_CLKEN						 0
+#define TIMER_CCR_SWTRG						 2
+#define TIMER_CMR					0x00000004
+#define TIMER_CMR_ABETRG					10
+#define TIMER_CMR_ACPA						16
+#define TIMER_CMR_ACPC						18
+#define TIMER_CMR_AEEVT						20
+#define TIMER_CMR_ASWTRG					22
+#define TIMER_CMR_BCPB						24
+#define TIMER_CMR_BCPC						26
+#define TIMER_CMR_BEEVT						28
+#define TIMER_CMR_BSWTRG					30
+#define TIMER_CMR_BURST						 4
+#define TIMER_CMR_CLKI						 3
+#define TIMER_CMR_CPCDIS					 7
+#define TIMER_CMR_CPCSTOP					 6
+#define TIMER_CMR_CPCTRG					14
+#define TIMER_CMR_EEVT						10
+#define TIMER_CMR_EEVTEDG					 8
+#define TIMER_CMR_ENETRG					12
+#define TIMER_CMR_ETRGEDG					 8
+#define TIMER_CMR_LDBDIS					 7
+#define TIMER_CMR_LDBSTOP					 6
+#define TIMER_CMR_LDRA						16
+#define TIMER_CMR_LDRB						18
+#define TIMER_CMR_TCCLKS					 0
+#define TIMER_CMR_WAVE						15
+#define TIMER_CMR_WAVSEL					13
+#define TIMER_CV					0x00000010
+#define TIMER_CV_CV						 0
+#define TIMER_IDR					0x00000028
+#define TIMER_IDR_COVFS						 0
+#define TIMER_IDR_CPAS						 2
+#define TIMER_IDR_CPBS						 3
+#define TIMER_IDR_CPCS						 4
+#define TIMER_IDR_ETRGS						 7
+#define TIMER_IDR_LDRAS						 5
+#define TIMER_IDR_LDRBS						 6
+#define TIMER_IDR_LOVRS						 1
+#define TIMER_IER					0x00000024
+#define TIMER_IER_COVFS						 0
+#define TIMER_IER_CPAS						 2
+#define TIMER_IER_CPBS						 3
+#define TIMER_IER_CPCS						 4
+#define TIMER_IER_ETRGS						 7
+#define TIMER_IER_LDRAS						 5
+#define TIMER_IER_LDRBS						 6
+#define TIMER_IER_LOVRS						 1
+#define TIMER_IMR					0x0000002c
+#define TIMER_IMR_COVFS						 0
+#define TIMER_IMR_CPAS						 2
+#define TIMER_IMR_CPBS						 3
+#define TIMER_IMR_CPCS						 4
+#define TIMER_IMR_ETRGS						 7
+#define TIMER_IMR_LDRAS						 5
+#define TIMER_IMR_LDRBS						 6
+#define TIMER_IMR_LOVRS						 1
+#define TIMER_RA					0x00000014
+#define TIMER_RA_RA						 0
+#define TIMER_RB					0x00000018
+#define TIMER_RB_RB						 0
+#define TIMER_RC					0x0000001c
+#define TIMER_RC_RC						 0
+#define TIMER_SR					0x00000020
+#define TIMER_SR_CLKSTA						16
+#define TIMER_SR_COVFS						 0
+#define TIMER_SR_CPAS						 2
+#define TIMER_SR_CPBS						 3
+#define TIMER_SR_CPCS						 4
+#define TIMER_SR_ETRGS						 7
+#define TIMER_SR_LDRAS						 5
+#define TIMER_SR_LDRBS						 6
+#define TIMER_SR_LOVRS						 1
+#define TIMER_SR_MTIOA						17
+#define TIMER_SR_MTIOB						18
+
+/* Bit manipulation macros */
+#define TIMER_BIT(name)		(1 << TIMER_##name)
+#define TIMER_BF(name,value)	((value) << TIMER_##name)
+
+/* Register access macros */
+#define timer_read(port,instance,reg) \
+	__raw_readl(port + (0x40 * instance) + TIMER_##reg)
+#define timer_write(port,instance,reg,value) \
+	__raw_writel((value), port + (0x40 * instance) + TIMER_##reg)
+
+#endif /* _ASM_AVR32_ARCH_AT32AP_TIME_H */
diff --git a/include/asm-avr32/atomic.h b/include/asm-avr32/atomic.h
index c40b603..b9c2548 100644
--- a/include/asm-avr32/atomic.h
+++ b/include/asm-avr32/atomic.h
@@ -173,7 +173,7 @@
 }
 
 #define atomic_xchg(v, new)	(xchg(&((v)->counter), new))
-#define atomic_cmpxchg(v, o, n)	((int)cmpxchg(&((v)->counter), (o), (n)))
+#define atomic_cmpxchg(v, o, n)	(cmpxchg(&((v)->counter), (o), (n)))
 
 #define atomic_sub(i, v)	(void)atomic_sub_return(i, v)
 #define atomic_add(i, v)	(void)atomic_add_return(i, v)
diff --git a/include/asm-avr32/bug.h b/include/asm-avr32/bug.h
index 521766b..afdcd79 100644
--- a/include/asm-avr32/bug.h
+++ b/include/asm-avr32/bug.h
@@ -18,27 +18,53 @@
 
 #ifdef CONFIG_DEBUG_BUGVERBOSE
 
-#define BUG()								\
-	do {								\
-		asm volatile(".hword	%0\n\t"				\
-			     ".hword	%1\n\t"				\
-			     ".long	%2"				\
-			     :						\
-			     : "n"(AVR32_BUG_OPCODE),			\
-			       "i"(__LINE__), "X"(__FILE__));		\
-	} while (0)
+#define _BUG_OR_WARN(flags)						\
+	asm volatile(							\
+		"1:	.hword	%0\n"					\
+		"	.section __bug_table,\"a\",@progbits\n"		\
+		"2:	.long	1b\n"					\
+		"	.long	%1\n"					\
+		"	.short	%2\n"					\
+		"	.short	%3\n"					\
+		"	.org	2b + %4\n"				\
+		"	.previous"					\
+		:							\
+		: "i"(AVR32_BUG_OPCODE), "i"(__FILE__),			\
+		  "i"(__LINE__), "i"(flags),				\
+		  "i"(sizeof(struct bug_entry)))
 
 #else
 
-#define BUG()								\
-	do {								\
-		asm volatile(".hword	%0\n\t"				\
-			     : : "n"(AVR32_BUG_OPCODE));		\
-	} while (0)
+#define _BUG_OR_WARN(flags)						\
+	asm volatile(							\
+		"1:	.hword	%0\n"					\
+		"	.section __bug_table,\"a\",@progbits\n"		\
+		"2:	.long	1b\n"					\
+		"	.short	%1\n"					\
+		"	.org	2b + %2\n"				\
+		"	.previous"					\
+		:							\
+		: "i"(AVR32_BUG_OPCODE), "i"(flags),			\
+		  "i"(sizeof(struct bug_entry)))
 
 #endif /* CONFIG_DEBUG_BUGVERBOSE */
 
+#define BUG()								\
+	do {								\
+		_BUG_OR_WARN(0);					\
+		for (;;);						\
+	} while (0)
+
+#define WARN_ON(condition)							\
+	({								\
+		typeof(condition) __ret_warn_on = (condition);		\
+		if (unlikely(__ret_warn_on))				\
+			_BUG_OR_WARN(BUGFLAG_WARNING);			\
+		unlikely(__ret_warn_on);				\
+	})
+
 #define HAVE_ARCH_BUG
+#define HAVE_ARCH_WARN_ON
 
 #endif /* CONFIG_BUG */
 
diff --git a/include/asm-avr32/io.h b/include/asm-avr32/io.h
index c08e810..e30d4b3 100644
--- a/include/asm-avr32/io.h
+++ b/include/asm-avr32/io.h
@@ -1,13 +1,15 @@
 #ifndef __ASM_AVR32_IO_H
 #define __ASM_AVR32_IO_H
 
+#include <linux/kernel.h>
 #include <linux/string.h>
-
-#ifdef __KERNEL__
+#include <linux/types.h>
 
 #include <asm/addrspace.h>
 #include <asm/byteorder.h>
 
+#include <asm/arch/io.h>
+
 /* virt_to_phys will only work when address is in P1 or P2 */
 static __inline__ unsigned long virt_to_phys(volatile void *address)
 {
@@ -36,104 +38,215 @@
 extern void __raw_readsw(const void __iomem *addr, void *data, int wordlen);
 extern void __raw_readsl(const void __iomem *addr, void *data, int longlen);
 
-static inline void writeb(unsigned char b, volatile void __iomem *addr)
+static inline void __raw_writeb(u8 v, volatile void __iomem *addr)
 {
-	*(volatile unsigned char __force *)addr = b;
+	*(volatile u8 __force *)addr = v;
 }
-static inline void writew(unsigned short b, volatile void __iomem *addr)
+static inline void __raw_writew(u16 v, volatile void __iomem *addr)
 {
-	*(volatile unsigned short __force *)addr = b;
+	*(volatile u16 __force *)addr = v;
 }
-static inline void writel(unsigned int b, volatile void __iomem *addr)
+static inline void __raw_writel(u32 v, volatile void __iomem *addr)
 {
-	*(volatile unsigned int __force *)addr = b;
+	*(volatile u32 __force *)addr = v;
 }
-#define __raw_writeb writeb
-#define __raw_writew writew
-#define __raw_writel writel
 
-static inline unsigned char readb(const volatile void __iomem *addr)
+static inline u8 __raw_readb(const volatile void __iomem *addr)
 {
-	return *(const volatile unsigned char __force *)addr;
+	return *(const volatile u8 __force *)addr;
 }
-static inline unsigned short readw(const volatile void __iomem *addr)
+static inline u16 __raw_readw(const volatile void __iomem *addr)
 {
-	return *(const volatile unsigned short __force *)addr;
+	return *(const volatile u16 __force *)addr;
 }
-static inline unsigned int readl(const volatile void __iomem *addr)
+static inline u32 __raw_readl(const volatile void __iomem *addr)
 {
-	return *(const volatile unsigned int __force *)addr;
+	return *(const volatile u32 __force *)addr;
 }
-#define __raw_readb readb
-#define __raw_readw readw
-#define __raw_readl readl
 
-#define writesb(p, d, l)	__raw_writesb((unsigned int)p, d, l)
-#define writesw(p, d, l)	__raw_writesw((unsigned int)p, d, l)
-#define writesl(p, d, l)	__raw_writesl((unsigned int)p, d, l)
+/* Convert I/O port address to virtual address */
+#ifndef __io
+# define __io(p)	((void *)phys_to_uncached(p))
+#endif
 
-#define readsb(p, d, l)		__raw_readsb((unsigned int)p, d, l)
-#define readsw(p, d, l)		__raw_readsw((unsigned int)p, d, l)
-#define readsl(p, d, l)		__raw_readsl((unsigned int)p, d, l)
+/*
+ * Not really sure about the best way to slow down I/O on
+ * AVR32. Defining it as a no-op until we have an actual test case.
+ */
+#define SLOW_DOWN_IO	do { } while (0)
 
+#define __BUILD_MEMORY_SINGLE(pfx, bwl, type)				\
+static inline void							\
+pfx##write##bwl(type val, volatile void __iomem *addr)			\
+{									\
+	volatile type *__addr;						\
+	type __val;							\
+									\
+	__addr = (void *)__swizzle_addr_##bwl((unsigned long)(addr));	\
+	__val = pfx##ioswab##bwl(__addr, val);				\
+									\
+	BUILD_BUG_ON(sizeof(type) > sizeof(unsigned long));		\
+									\
+	*__addr = __val;						\
+}									\
+									\
+static inline type pfx##read##bwl(const volatile void __iomem *addr)	\
+{									\
+	volatile type *__addr;						\
+	type __val;							\
+									\
+	__addr = (void *)__swizzle_addr_##bwl((unsigned long)(addr));	\
+									\
+	BUILD_BUG_ON(sizeof(type) > sizeof(unsigned long));		\
+									\
+	__val = *__addr;						\
+	return pfx##ioswab##bwl(__addr, __val);				\
+}
+
+#define __BUILD_IOPORT_SINGLE(pfx, bwl, type, p, slow)			\
+static inline void pfx##out##bwl##p(type val, unsigned long port)	\
+{									\
+	volatile type *__addr;						\
+	type __val;							\
+									\
+	__addr = __io(__swizzle_addr_##bwl(port));			\
+	__val = pfx##ioswab##bwl(__addr, val);				\
+									\
+	BUILD_BUG_ON(sizeof(type) > sizeof(unsigned long));		\
+									\
+	*__addr = __val;						\
+	slow;								\
+}									\
+									\
+static inline type pfx##in##bwl##p(unsigned long port)			\
+{									\
+	volatile type *__addr;						\
+	type __val;							\
+									\
+	__addr = __io(__swizzle_addr_##bwl(port));			\
+									\
+	BUILD_BUG_ON(sizeof(type) > sizeof(unsigned long));		\
+									\
+	__val = *__addr;						\
+	slow;								\
+									\
+	return pfx##ioswab##bwl(__addr, __val);				\
+}
+
+#define __BUILD_MEMORY_PFX(bus, bwl, type)				\
+	__BUILD_MEMORY_SINGLE(bus, bwl, type)
+
+#define BUILDIO_MEM(bwl, type)						\
+	__BUILD_MEMORY_PFX(, bwl, type)					\
+	__BUILD_MEMORY_PFX(__mem_, bwl, type)
+
+#define __BUILD_IOPORT_PFX(bus, bwl, type)				\
+	__BUILD_IOPORT_SINGLE(bus, bwl, type, ,)			\
+	__BUILD_IOPORT_SINGLE(bus, bwl, type, _p, SLOW_DOWN_IO)
+
+#define BUILDIO_IOPORT(bwl, type)					\
+	__BUILD_IOPORT_PFX(, bwl, type)					\
+	__BUILD_IOPORT_PFX(__mem_, bwl, type)
+
+BUILDIO_MEM(b, u8)
+BUILDIO_MEM(w, u16)
+BUILDIO_MEM(l, u32)
+
+BUILDIO_IOPORT(b, u8)
+BUILDIO_IOPORT(w, u16)
+BUILDIO_IOPORT(l, u32)
+
+#define readb_relaxed			readb
+#define readw_relaxed			readw
+#define readl_relaxed			readl
+
+#define __BUILD_MEMORY_STRING(bwl, type)				\
+static inline void writes##bwl(volatile void __iomem *addr,		\
+			       const void *data, unsigned int count)	\
+{									\
+	const type *__data = data;					\
+									\
+	while (count--)							\
+		__mem_write##bwl(*__data++, addr);			\
+}									\
+									\
+static inline void reads##bwl(const volatile void __iomem *addr,	\
+			      void *data, unsigned int count)		\
+{									\
+	type *__data = data;						\
+									\
+	while (count--)							\
+		*__data++ = __mem_read##bwl(addr);			\
+}
+
+#define __BUILD_IOPORT_STRING(bwl, type)				\
+static inline void outs##bwl(unsigned long port, const void *data,	\
+			     unsigned int count)			\
+{									\
+	const type *__data = data;					\
+									\
+	while (count--)							\
+		__mem_out##bwl(*__data++, port);			\
+}									\
+									\
+static inline void ins##bwl(unsigned long port, void *data,		\
+			   unsigned int count)				\
+{									\
+	type *__data = data;						\
+									\
+	while (count--)							\
+		*__data++ = __mem_in##bwl(port);			\
+}
+
+#define BUILDSTRING(bwl, type)						\
+	__BUILD_MEMORY_STRING(bwl, type)				\
+	__BUILD_IOPORT_STRING(bwl, type)
+
+BUILDSTRING(b, u8)
+BUILDSTRING(w, u16)
+BUILDSTRING(l, u32)
 
 /*
  * io{read,write}{8,16,32} macros in both le (for PCI style consumers) and native be
  */
 #ifndef ioread8
 
-#define ioread8(p)	({ unsigned int __v = __raw_readb(p); __v; })
+#define ioread8(p)		((unsigned int)readb(p))
 
-#define ioread16(p)	({ unsigned int __v = le16_to_cpu(__raw_readw(p)); __v; })
-#define ioread16be(p)	({ unsigned int __v = be16_to_cpu(__raw_readw(p)); __v; })
+#define ioread16(p)		((unsigned int)readw(p))
+#define ioread16be(p)		((unsigned int)__raw_readw(p))
 
-#define ioread32(p)	({ unsigned int __v = le32_to_cpu(__raw_readl(p)); __v; })
-#define ioread32be(p)	({ unsigned int __v = be32_to_cpu(__raw_readl(p)); __v; })
+#define ioread32(p)		((unsigned int)readl(p))
+#define ioread32be(p)		((unsigned int)__raw_readl(p))
 
-#define iowrite8(v,p)	__raw_writeb(v, p)
+#define iowrite8(v,p)		writeb(v, p)
 
-#define iowrite16(v,p)	__raw_writew(cpu_to_le16(v), p)
-#define iowrite16be(v,p)	__raw_writew(cpu_to_be16(v), p)
+#define iowrite16(v,p)		writew(v, p)
+#define iowrite16be(v,p)	__raw_writew(v, p)
 
-#define iowrite32(v,p)	__raw_writel(cpu_to_le32(v), p)
-#define iowrite32be(v,p)	__raw_writel(cpu_to_be32(v), p)
+#define iowrite32(v,p)		writel(v, p)
+#define iowrite32be(v,p)	__raw_writel(v, p)
 
-#define ioread8_rep(p,d,c)	__raw_readsb(p,d,c)
-#define ioread16_rep(p,d,c)	__raw_readsw(p,d,c)
-#define ioread32_rep(p,d,c)	__raw_readsl(p,d,c)
+#define ioread8_rep(p,d,c)	readsb(p,d,c)
+#define ioread16_rep(p,d,c)	readsw(p,d,c)
+#define ioread32_rep(p,d,c)	readsl(p,d,c)
 
-#define iowrite8_rep(p,s,c)	__raw_writesb(p,s,c)
-#define iowrite16_rep(p,s,c)	__raw_writesw(p,s,c)
-#define iowrite32_rep(p,s,c)	__raw_writesl(p,s,c)
+#define iowrite8_rep(p,s,c)	writesb(p,s,c)
+#define iowrite16_rep(p,s,c)	writesw(p,s,c)
+#define iowrite32_rep(p,s,c)	writesl(p,s,c)
 
 #endif
 
-
-/*
- * These two are only here because ALSA _thinks_ it needs them...
- */
 static inline void memcpy_fromio(void * to, const volatile void __iomem *from,
 				 unsigned long count)
 {
-	char *p = to;
-	while (count) {
-		count--;
-		*p = readb(from);
-		p++;
-		from++;
-	}
+	memcpy(to, (const void __force *)from, count);
 }
 
 static inline void  memcpy_toio(volatile void __iomem *to, const void * from,
 				unsigned long count)
 {
-	const char *p = from;
-	while (count) {
-		count--;
-		writeb(*p, to);
-		p++;
-		to++;
-	}
+	memcpy((void __force *)to, from, count);
 }
 
 static inline void memset_io(volatile void __iomem *addr, unsigned char val,
@@ -142,99 +255,8 @@
 	memset((void __force *)addr, val, count);
 }
 
-/*
- * Bad read/write accesses...
- */
-extern void __readwrite_bug(const char *fn);
-
 #define IO_SPACE_LIMIT	0xffffffff
 
-/* Convert I/O port address to virtual address */
-#define __io(p)		((void __iomem *)phys_to_uncached(p))
-
-/*
- *  IO port access primitives
- *  -------------------------
- *
- * The AVR32 doesn't have special IO access instructions; all IO is memory
- * mapped. Note that these are defined to perform little endian accesses
- * only. Their primary purpose is to access PCI and ISA peripherals.
- *
- * Note that for a big endian machine, this implies that the following
- * big endian mode connectivity is in place.
- *
- * The machine specific io.h include defines __io to translate an "IO"
- * address to a memory address.
- *
- * Note that we prevent GCC re-ordering or caching values in expressions
- * by introducing sequence points into the in*() definitions.  Note that
- * __raw_* do not guarantee this behaviour.
- *
- * The {in,out}[bwl] macros are for emulating x86-style PCI/ISA IO space.
- */
-#define outb(v, p)		__raw_writeb(v, __io(p))
-#define outw(v, p)		__raw_writew(cpu_to_le16(v), __io(p))
-#define outl(v, p)		__raw_writel(cpu_to_le32(v), __io(p))
-
-#define inb(p)			__raw_readb(__io(p))
-#define inw(p)			le16_to_cpu(__raw_readw(__io(p)))
-#define inl(p)			le32_to_cpu(__raw_readl(__io(p)))
-
-static inline void __outsb(unsigned long port, void *addr, unsigned int count)
-{
-	while (count--) {
-		outb(*(u8 *)addr, port);
-		addr++;
-	}
-}
-
-static inline void __insb(unsigned long port, void *addr, unsigned int count)
-{
-	while (count--) {
-		*(u8 *)addr = inb(port);
-		addr++;
-	}
-}
-
-static inline void __outsw(unsigned long port, void *addr, unsigned int count)
-{
-	while (count--) {
-		outw(*(u16 *)addr, port);
-		addr += 2;
-	}
-}
-
-static inline void __insw(unsigned long port, void *addr, unsigned int count)
-{
-	while (count--) {
-		*(u16 *)addr = inw(port);
-		addr += 2;
-	}
-}
-
-static inline void __outsl(unsigned long port, void *addr, unsigned int count)
-{
-	while (count--) {
-		outl(*(u32 *)addr, port);
-		addr += 4;
-	}
-}
-
-static inline void __insl(unsigned long port, void *addr, unsigned int count)
-{
-	while (count--) {
-		*(u32 *)addr = inl(port);
-		addr += 4;
-	}
-}
-
-#define outsb(port, addr, count)	__outsb(port, addr, count)
-#define insb(port, addr, count)		__insb(port, addr, count)
-#define outsw(port, addr, count)	__outsw(port, addr, count)
-#define insw(port, addr, count)		__insw(port, addr, count)
-#define outsl(port, addr, count)	__outsl(port, addr, count)
-#define insl(port, addr, count)		__insl(port, addr, count)
-
 extern void __iomem *__ioremap(unsigned long offset, size_t size,
 			       unsigned long flags);
 extern void __iounmap(void __iomem *addr);
@@ -292,6 +314,4 @@
  */
 #define xlate_dev_kmem_ptr(p)   p
 
-#endif /* __KERNEL__ */
-
 #endif /* __ASM_AVR32_IO_H */
diff --git a/include/asm-avr32/processor.h b/include/asm-avr32/processor.h
index f691377..6a64833 100644
--- a/include/asm-avr32/processor.h
+++ b/include/asm-avr32/processor.h
@@ -40,6 +40,14 @@
 	TLB_INVALID
 };
 
+#define AVR32_FEATURE_RMW	(1 << 0)
+#define AVR32_FEATURE_DSP	(1 << 1)
+#define AVR32_FEATURE_SIMD	(1 << 2)
+#define AVR32_FEATURE_OCD	(1 << 3)
+#define AVR32_FEATURE_PCTR	(1 << 4)
+#define AVR32_FEATURE_JAVA	(1 << 5)
+#define AVR32_FEATURE_FPU	(1 << 6)
+
 struct avr32_cpuinfo {
 	struct clk *clk;
 	unsigned long loops_per_jiffy;
@@ -48,6 +56,7 @@
 	unsigned short arch_revision;
 	unsigned short cpu_revision;
 	enum tlb_config tlb_config;
+	unsigned long features;
 
 	struct cache_info icache;
 	struct cache_info dcache;
@@ -125,10 +134,10 @@
 #define thread_saved_pc(tsk)    ((tsk)->thread.cpu_context.pc)
 
 struct pt_regs;
-void show_trace(struct task_struct *task, unsigned long *stack,
-		struct pt_regs *regs);
-
 extern unsigned long get_wchan(struct task_struct *p);
+extern void show_regs_log_lvl(struct pt_regs *regs, const char *log_lvl);
+extern void show_stack_log_lvl(struct task_struct *tsk, unsigned long sp,
+			       struct pt_regs *regs, const char *log_lvl);
 
 #define KSTK_EIP(tsk)	((tsk)->thread.cpu_context.pc)
 #define KSTK_ESP(tsk)	((tsk)->thread.cpu_context.ksp)
diff --git a/include/asm-avr32/setup.h b/include/asm-avr32/setup.h
index 0a52242..1ff1a21 100644
--- a/include/asm-avr32/setup.h
+++ b/include/asm-avr32/setup.h
@@ -124,19 +124,12 @@
 #define for_each_tag(t,base)						\
 	for (t = base; t->hdr.size; t = tag_next(t))
 
-extern struct tag_mem_range *mem_phys;
-extern struct tag_mem_range *mem_reserved;
-extern struct tag_mem_range *mem_ramdisk;
-
 extern struct tag *bootloader_tags;
 
-extern void setup_bootmem(void);
-extern void setup_processor(void);
-extern void board_setup_fbmem(unsigned long fbmem_start,
-			      unsigned long fbmem_size);
+extern resource_size_t fbmem_start;
+extern resource_size_t fbmem_size;
 
-/* Chip-specific hook to enable the use of SDRAM */
-void chip_enable_sdram(void);
+void setup_processor(void);
 
 #endif /* !__ASSEMBLY__ */
 
diff --git a/include/asm-avr32/socket.h b/include/asm-avr32/socket.h
index 543229d..a0d0507 100644
--- a/include/asm-avr32/socket.h
+++ b/include/asm-avr32/socket.h
@@ -49,5 +49,7 @@
 
 #define SO_PEERSEC		31
 #define SO_PASSSEC		34
+#define SO_TIMESTAMPNS		35
+#define SCM_TIMESTAMPNS		SO_TIMESTAMPNS
 
 #endif /* __ASM_AVR32_SOCKET_H */
diff --git a/include/asm-avr32/sockios.h b/include/asm-avr32/sockios.h
index 84f3d65..0802d74 100644
--- a/include/asm-avr32/sockios.h
+++ b/include/asm-avr32/sockios.h
@@ -7,6 +7,7 @@
 #define FIOGETOWN	0x8903
 #define SIOCGPGRP	0x8904
 #define SIOCATMARK	0x8905
-#define SIOCGSTAMP	0x8906		/* Get stamp */
+#define SIOCGSTAMP	0x8906		/* Get stamp (timeval) */
+#define SIOCGSTAMPNS	0x8907		/* Get stamp (timespec) */
 
 #endif /* __ASM_AVR32_SOCKIOS_H */
diff --git a/include/asm-avr32/sysreg.h b/include/asm-avr32/sysreg.h
index f91975f..c02bc83 100644
--- a/include/asm-avr32/sysreg.h
+++ b/include/asm-avr32/sysreg.h
@@ -7,326 +7,281 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-#ifndef __ASM_AVR32_SYSREG_H__
-#define __ASM_AVR32_SYSREG_H__
+#ifndef __ASM_AVR32_SYSREG_H
+#define __ASM_AVR32_SYSREG_H
 
 /* sysreg register offsets */
-#define SYSREG_SR                               0x0000
-#define SYSREG_EVBA                             0x0004
-#define SYSREG_ACBA                             0x0008
-#define SYSREG_CPUCR                            0x000c
-#define SYSREG_ECR                              0x0010
-#define SYSREG_RSR_SUP                          0x0014
-#define SYSREG_RSR_INT0                         0x0018
-#define SYSREG_RSR_INT1                         0x001c
-#define SYSREG_RSR_INT2                         0x0020
-#define SYSREG_RSR_INT3                         0x0024
-#define SYSREG_RSR_EX                           0x0028
-#define SYSREG_RSR_NMI                          0x002c
-#define SYSREG_RSR_DBG                          0x0030
-#define SYSREG_RAR_SUP                          0x0034
-#define SYSREG_RAR_INT0                         0x0038
-#define SYSREG_RAR_INT1                         0x003c
-#define SYSREG_RAR_INT2                         0x0040
-#define SYSREG_RAR_INT3                         0x0044
-#define SYSREG_RAR_EX                           0x0048
-#define SYSREG_RAR_NMI                          0x004c
-#define SYSREG_RAR_DBG                          0x0050
-#define SYSREG_JECR                             0x0054
-#define SYSREG_JOSP                             0x0058
-#define SYSREG_JAVA_LV0                         0x005c
-#define SYSREG_JAVA_LV1                         0x0060
-#define SYSREG_JAVA_LV2                         0x0064
-#define SYSREG_JAVA_LV3                         0x0068
-#define SYSREG_JAVA_LV4                         0x006c
-#define SYSREG_JAVA_LV5                         0x0070
-#define SYSREG_JAVA_LV6                         0x0074
-#define SYSREG_JAVA_LV7                         0x0078
-#define SYSREG_JTBA                             0x007c
-#define SYSREG_JBCR                             0x0080
-#define SYSREG_CONFIG0                          0x0100
-#define SYSREG_CONFIG1                          0x0104
-#define SYSREG_COUNT                            0x0108
-#define SYSREG_COMPARE                          0x010c
-#define SYSREG_TLBEHI                           0x0110
-#define SYSREG_TLBELO                           0x0114
-#define SYSREG_PTBR                             0x0118
-#define SYSREG_TLBEAR                           0x011c
-#define SYSREG_MMUCR                            0x0120
-#define SYSREG_TLBARLO                          0x0124
-#define SYSREG_TLBARHI                          0x0128
-#define SYSREG_PCCNT                            0x012c
-#define SYSREG_PCNT0                            0x0130
-#define SYSREG_PCNT1                            0x0134
-#define SYSREG_PCCR                             0x0138
-#define SYSREG_BEAR                             0x013c
+#define SYSREG_SR				0x0000
+#define SYSREG_EVBA				0x0004
+#define SYSREG_ACBA				0x0008
+#define SYSREG_CPUCR				0x000c
+#define SYSREG_ECR				0x0010
+#define SYSREG_RSR_SUP				0x0014
+#define SYSREG_RSR_INT0				0x0018
+#define SYSREG_RSR_INT1				0x001c
+#define SYSREG_RSR_INT2				0x0020
+#define SYSREG_RSR_INT3				0x0024
+#define SYSREG_RSR_EX				0x0028
+#define SYSREG_RSR_NMI				0x002c
+#define SYSREG_RSR_DBG				0x0030
+#define SYSREG_RAR_SUP				0x0034
+#define SYSREG_RAR_INT0				0x0038
+#define SYSREG_RAR_INT1				0x003c
+#define SYSREG_RAR_INT2				0x0040
+#define SYSREG_RAR_INT3				0x0044
+#define SYSREG_RAR_EX				0x0048
+#define SYSREG_RAR_NMI				0x004c
+#define SYSREG_RAR_DBG				0x0050
+#define SYSREG_JECR				0x0054
+#define SYSREG_JOSP				0x0058
+#define SYSREG_JAVA_LV0				0x005c
+#define SYSREG_JAVA_LV1				0x0060
+#define SYSREG_JAVA_LV2				0x0064
+#define SYSREG_JAVA_LV3				0x0068
+#define SYSREG_JAVA_LV4				0x006c
+#define SYSREG_JAVA_LV5				0x0070
+#define SYSREG_JAVA_LV6				0x0074
+#define SYSREG_JAVA_LV7				0x0078
+#define SYSREG_JTBA				0x007c
+#define SYSREG_JBCR				0x0080
+#define SYSREG_CONFIG0				0x0100
+#define SYSREG_CONFIG1				0x0104
+#define SYSREG_COUNT				0x0108
+#define SYSREG_COMPARE				0x010c
+#define SYSREG_TLBEHI				0x0110
+#define SYSREG_TLBELO				0x0114
+#define SYSREG_PTBR				0x0118
+#define SYSREG_TLBEAR				0x011c
+#define SYSREG_MMUCR				0x0120
+#define SYSREG_TLBARLO				0x0124
+#define SYSREG_TLBARHI				0x0128
+#define SYSREG_PCCNT				0x012c
+#define SYSREG_PCNT0				0x0130
+#define SYSREG_PCNT1				0x0134
+#define SYSREG_PCCR				0x0138
+#define SYSREG_BEAR				0x013c
+#define SYSREG_SABAL				0x0300
+#define SYSREG_SABAH				0x0304
+#define SYSREG_SABD				0x0308
 
 /* Bitfields in SR */
-#define SYSREG_SR_C_OFFSET                      0
-#define SYSREG_SR_C_SIZE                        1
-#define SYSREG_Z_OFFSET                         1
-#define SYSREG_Z_SIZE                           1
-#define SYSREG_SR_N_OFFSET                      2
-#define SYSREG_SR_N_SIZE                        1
-#define SYSREG_SR_V_OFFSET                      3
-#define SYSREG_SR_V_SIZE                        1
-#define SYSREG_Q_OFFSET                         4
-#define SYSREG_Q_SIZE                           1
-#define SYSREG_GM_OFFSET                        16
-#define SYSREG_GM_SIZE                          1
-#define SYSREG_I0M_OFFSET                       17
-#define SYSREG_I0M_SIZE                         1
-#define SYSREG_I1M_OFFSET                       18
-#define SYSREG_I1M_SIZE                         1
-#define SYSREG_I2M_OFFSET                       19
-#define SYSREG_I2M_SIZE                         1
-#define SYSREG_I3M_OFFSET                       20
-#define SYSREG_I3M_SIZE                         1
-#define SYSREG_EM_OFFSET                        21
-#define SYSREG_EM_SIZE                          1
-#define SYSREG_M0_OFFSET                        22
-#define SYSREG_M0_SIZE                          1
-#define SYSREG_M1_OFFSET                        23
-#define SYSREG_M1_SIZE                          1
-#define SYSREG_M2_OFFSET                        24
-#define SYSREG_M2_SIZE                          1
-#define SYSREG_SR_D_OFFSET                      26
-#define SYSREG_SR_D_SIZE                        1
-#define SYSREG_DM_OFFSET                        27
-#define SYSREG_DM_SIZE                          1
-#define SYSREG_SR_J_OFFSET                      28
-#define SYSREG_SR_J_SIZE                        1
-#define SYSREG_R_OFFSET                         29
-#define SYSREG_R_SIZE                           1
-#define SYSREG_H_OFFSET                         30
-#define SYSREG_H_SIZE                           1
-
-/* Bitfields in EVBA */
-
-/* Bitfields in ACBA */
+#define SYSREG_SR_C_OFFSET			0
+#define SYSREG_SR_C_SIZE			1
+#define SYSREG_Z_OFFSET				1
+#define SYSREG_Z_SIZE				1
+#define SYSREG_SR_N_OFFSET			2
+#define SYSREG_SR_N_SIZE			1
+#define SYSREG_SR_V_OFFSET			3
+#define SYSREG_SR_V_SIZE			1
+#define SYSREG_Q_OFFSET				4
+#define SYSREG_Q_SIZE				1
+#define SYSREG_L_OFFSET				5
+#define SYSREG_L_SIZE				1
+#define SYSREG_T_OFFSET				14
+#define SYSREG_T_SIZE				1
+#define SYSREG_SR_R_OFFSET			15
+#define SYSREG_SR_R_SIZE			1
+#define SYSREG_GM_OFFSET			16
+#define SYSREG_GM_SIZE				1
+#define SYSREG_I0M_OFFSET			17
+#define SYSREG_I0M_SIZE				1
+#define SYSREG_I1M_OFFSET			18
+#define SYSREG_I1M_SIZE				1
+#define SYSREG_I2M_OFFSET			19
+#define SYSREG_I2M_SIZE				1
+#define SYSREG_I3M_OFFSET			20
+#define SYSREG_I3M_SIZE				1
+#define SYSREG_EM_OFFSET			21
+#define SYSREG_EM_SIZE				1
+#define SYSREG_M0_OFFSET			22
+#define SYSREG_M0_SIZE				1
+#define SYSREG_M1_OFFSET			23
+#define SYSREG_M1_SIZE				1
+#define SYSREG_M2_OFFSET			24
+#define SYSREG_M2_SIZE				1
+#define SYSREG_SR_D_OFFSET			26
+#define SYSREG_SR_D_SIZE			1
+#define SYSREG_DM_OFFSET			27
+#define SYSREG_DM_SIZE				1
+#define SYSREG_SR_J_OFFSET			28
+#define SYSREG_SR_J_SIZE			1
+#define SYSREG_H_OFFSET				29
+#define SYSREG_H_SIZE				1
 
 /* Bitfields in CPUCR */
-#define SYSREG_BI_OFFSET                        0
-#define SYSREG_BI_SIZE                          1
-#define SYSREG_BE_OFFSET                        1
-#define SYSREG_BE_SIZE                          1
-#define SYSREG_FE_OFFSET                        2
-#define SYSREG_FE_SIZE                          1
-#define SYSREG_RE_OFFSET                        3
-#define SYSREG_RE_SIZE                          1
-#define SYSREG_IBE_OFFSET                       4
-#define SYSREG_IBE_SIZE                         1
-#define SYSREG_IEE_OFFSET                       5
-#define SYSREG_IEE_SIZE                         1
-
-/* Bitfields in ECR */
-#define SYSREG_ECR_OFFSET                       0
-#define SYSREG_ECR_SIZE                         32
-
-/* Bitfields in RSR_SUP */
-
-/* Bitfields in RSR_INT0 */
-
-/* Bitfields in RSR_INT1 */
-
-/* Bitfields in RSR_INT2 */
-
-/* Bitfields in RSR_INT3 */
-
-/* Bitfields in RSR_EX */
-
-/* Bitfields in RSR_NMI */
-
-/* Bitfields in RSR_DBG */
-
-/* Bitfields in RAR_SUP */
-
-/* Bitfields in RAR_INT0 */
-
-/* Bitfields in RAR_INT1 */
-
-/* Bitfields in RAR_INT2 */
-
-/* Bitfields in RAR_INT3 */
-
-/* Bitfields in RAR_EX */
-
-/* Bitfields in RAR_NMI */
-
-/* Bitfields in RAR_DBG */
-
-/* Bitfields in JECR */
-
-/* Bitfields in JOSP */
-
-/* Bitfields in JAVA_LV0 */
-
-/* Bitfields in JAVA_LV1 */
-
-/* Bitfields in JAVA_LV2 */
-
-/* Bitfields in JAVA_LV3 */
-
-/* Bitfields in JAVA_LV4 */
-
-/* Bitfields in JAVA_LV5 */
-
-/* Bitfields in JAVA_LV6 */
-
-/* Bitfields in JAVA_LV7 */
-
-/* Bitfields in JTBA */
-
-/* Bitfields in JBCR */
+#define SYSREG_BI_OFFSET			0
+#define SYSREG_BI_SIZE				1
+#define SYSREG_BE_OFFSET			1
+#define SYSREG_BE_SIZE				1
+#define SYSREG_FE_OFFSET			2
+#define SYSREG_FE_SIZE				1
+#define SYSREG_RE_OFFSET			3
+#define SYSREG_RE_SIZE				1
+#define SYSREG_IBE_OFFSET			4
+#define SYSREG_IBE_SIZE				1
+#define SYSREG_IEE_OFFSET			5
+#define SYSREG_IEE_SIZE				1
 
 /* Bitfields in CONFIG0 */
-#define SYSREG_CONFIG0_D_OFFSET                 1
-#define SYSREG_CONFIG0_D_SIZE                   1
-#define SYSREG_CONFIG0_S_OFFSET                 2
-#define SYSREG_CONFIG0_S_SIZE                   1
-#define SYSREG_O_OFFSET                         3
-#define SYSREG_O_SIZE                           1
-#define SYSREG_P_OFFSET                         4
-#define SYSREG_P_SIZE                           1
-#define SYSREG_CONFIG0_J_OFFSET                 5
-#define SYSREG_CONFIG0_J_SIZE                   1
-#define SYSREG_F_OFFSET                         6
-#define SYSREG_F_SIZE                           1
-#define SYSREG_MMUT_OFFSET                      7
-#define SYSREG_MMUT_SIZE                        3
-#define SYSREG_AR_OFFSET                        10
-#define SYSREG_AR_SIZE                          3
-#define SYSREG_AT_OFFSET                        13
-#define SYSREG_AT_SIZE                          3
-#define SYSREG_PROCESSORREVISION_OFFSET         16
-#define SYSREG_PROCESSORREVISION_SIZE           8
-#define SYSREG_PROCESSORID_OFFSET               24
-#define SYSREG_PROCESSORID_SIZE                 8
+#define SYSREG_CONFIG0_R_OFFSET			0
+#define SYSREG_CONFIG0_R_SIZE			1
+#define SYSREG_CONFIG0_D_OFFSET			1
+#define SYSREG_CONFIG0_D_SIZE			1
+#define SYSREG_CONFIG0_S_OFFSET			2
+#define SYSREG_CONFIG0_S_SIZE			1
+#define SYSREG_CONFIG0_O_OFFSET			3
+#define SYSREG_CONFIG0_O_SIZE			1
+#define SYSREG_CONFIG0_P_OFFSET			4
+#define SYSREG_CONFIG0_P_SIZE			1
+#define SYSREG_CONFIG0_J_OFFSET			5
+#define SYSREG_CONFIG0_J_SIZE			1
+#define SYSREG_CONFIG0_F_OFFSET			6
+#define SYSREG_CONFIG0_F_SIZE			1
+#define SYSREG_MMUT_OFFSET			7
+#define SYSREG_MMUT_SIZE			3
+#define SYSREG_AR_OFFSET			10
+#define SYSREG_AR_SIZE				3
+#define SYSREG_AT_OFFSET			13
+#define SYSREG_AT_SIZE				3
+#define SYSREG_PROCESSORREVISION_OFFSET		16
+#define SYSREG_PROCESSORREVISION_SIZE		8
+#define SYSREG_PROCESSORID_OFFSET		24
+#define SYSREG_PROCESSORID_SIZE			8
 
 /* Bitfields in CONFIG1 */
-#define SYSREG_DASS_OFFSET                      0
-#define SYSREG_DASS_SIZE                        3
-#define SYSREG_DLSZ_OFFSET                      3
-#define SYSREG_DLSZ_SIZE                        3
-#define SYSREG_DSET_OFFSET                      6
-#define SYSREG_DSET_SIZE                        4
-#define SYSREG_IASS_OFFSET                      10
-#define SYSREG_IASS_SIZE                        2
-#define SYSREG_ILSZ_OFFSET                      13
-#define SYSREG_ILSZ_SIZE                        3
-#define SYSREG_ISET_OFFSET                      16
-#define SYSREG_ISET_SIZE                        4
-#define SYSREG_DMMUSZ_OFFSET                    20
-#define SYSREG_DMMUSZ_SIZE                      6
-#define SYSREG_IMMUSZ_OFFSET                    26
-#define SYSREG_IMMUSZ_SIZE                      6
-
-/* Bitfields in COUNT */
-
-/* Bitfields in COMPARE */
+#define SYSREG_DASS_OFFSET			0
+#define SYSREG_DASS_SIZE			3
+#define SYSREG_DLSZ_OFFSET			3
+#define SYSREG_DLSZ_SIZE			3
+#define SYSREG_DSET_OFFSET			6
+#define SYSREG_DSET_SIZE			4
+#define SYSREG_IASS_OFFSET			10
+#define SYSREG_IASS_SIZE			3
+#define SYSREG_ILSZ_OFFSET			13
+#define SYSREG_ILSZ_SIZE			3
+#define SYSREG_ISET_OFFSET			16
+#define SYSREG_ISET_SIZE			4
+#define SYSREG_DMMUSZ_OFFSET			20
+#define SYSREG_DMMUSZ_SIZE			6
+#define SYSREG_IMMUSZ_OFFSET			26
+#define SYSREG_IMMUSZ_SIZE			6
 
 /* Bitfields in TLBEHI */
-#define SYSREG_ASID_OFFSET                      0
-#define SYSREG_ASID_SIZE                        8
-#define SYSREG_TLBEHI_I_OFFSET                  8
-#define SYSREG_TLBEHI_I_SIZE                    1
-#define SYSREG_TLBEHI_V_OFFSET                  9
-#define SYSREG_TLBEHI_V_SIZE                    1
-#define SYSREG_VPN_OFFSET                       10
-#define SYSREG_VPN_SIZE                         22
+#define SYSREG_ASID_OFFSET			0
+#define SYSREG_ASID_SIZE			8
+#define SYSREG_TLBEHI_I_OFFSET			8
+#define SYSREG_TLBEHI_I_SIZE			1
+#define SYSREG_TLBEHI_V_OFFSET			9
+#define SYSREG_TLBEHI_V_SIZE			1
+#define SYSREG_VPN_OFFSET			10
+#define SYSREG_VPN_SIZE				22
 
 /* Bitfields in TLBELO */
-#define SYSREG_W_OFFSET                         0
-#define SYSREG_W_SIZE                           1
-#define SYSREG_TLBELO_D_OFFSET                  1
-#define SYSREG_TLBELO_D_SIZE                    1
-#define SYSREG_SZ_OFFSET                        2
-#define SYSREG_SZ_SIZE                          2
-#define SYSREG_AP_OFFSET                        4
-#define SYSREG_AP_SIZE                          3
-#define SYSREG_B_OFFSET                         7
-#define SYSREG_B_SIZE                           1
-#define SYSREG_G_OFFSET                         8
-#define SYSREG_G_SIZE                           1
-#define SYSREG_TLBELO_C_OFFSET                  9
-#define SYSREG_TLBELO_C_SIZE                    1
-#define SYSREG_PFN_OFFSET                       10
-#define SYSREG_PFN_SIZE                         22
-
-/* Bitfields in PTBR */
-
-/* Bitfields in TLBEAR */
+#define SYSREG_W_OFFSET				0
+#define SYSREG_W_SIZE				1
+#define SYSREG_TLBELO_D_OFFSET			1
+#define SYSREG_TLBELO_D_SIZE			1
+#define SYSREG_SZ_OFFSET			2
+#define SYSREG_SZ_SIZE				2
+#define SYSREG_AP_OFFSET			4
+#define SYSREG_AP_SIZE				3
+#define SYSREG_B_OFFSET				7
+#define SYSREG_B_SIZE				1
+#define SYSREG_G_OFFSET				8
+#define SYSREG_G_SIZE				1
+#define SYSREG_TLBELO_C_OFFSET			9
+#define SYSREG_TLBELO_C_SIZE			1
+#define SYSREG_PFN_OFFSET			10
+#define SYSREG_PFN_SIZE				22
 
 /* Bitfields in MMUCR */
-#define SYSREG_E_OFFSET                         0
-#define SYSREG_E_SIZE                           1
-#define SYSREG_M_OFFSET                         1
-#define SYSREG_M_SIZE                           1
-#define SYSREG_MMUCR_I_OFFSET                   2
-#define SYSREG_MMUCR_I_SIZE                     1
-#define SYSREG_MMUCR_N_OFFSET                   3
-#define SYSREG_MMUCR_N_SIZE                     1
-#define SYSREG_MMUCR_S_OFFSET                   4
-#define SYSREG_MMUCR_S_SIZE                     1
-#define SYSREG_DLA_OFFSET                       8
-#define SYSREG_DLA_SIZE                         6
-#define SYSREG_DRP_OFFSET                       14
-#define SYSREG_DRP_SIZE                         6
-#define SYSREG_ILA_OFFSET                       20
-#define SYSREG_ILA_SIZE                         6
-#define SYSREG_IRP_OFFSET                       26
-#define SYSREG_IRP_SIZE                         6
-
-/* Bitfields in TLBARLO */
-
-/* Bitfields in TLBARHI */
-
-/* Bitfields in PCCNT */
-
-/* Bitfields in PCNT0 */
-
-/* Bitfields in PCNT1 */
+#define SYSREG_E_OFFSET				0
+#define SYSREG_E_SIZE				1
+#define SYSREG_M_OFFSET				1
+#define SYSREG_M_SIZE				1
+#define SYSREG_MMUCR_I_OFFSET			2
+#define SYSREG_MMUCR_I_SIZE			1
+#define SYSREG_MMUCR_N_OFFSET			3
+#define SYSREG_MMUCR_N_SIZE			1
+#define SYSREG_MMUCR_S_OFFSET			4
+#define SYSREG_MMUCR_S_SIZE			1
+#define SYSREG_DLA_OFFSET			8
+#define SYSREG_DLA_SIZE				6
+#define SYSREG_DRP_OFFSET			14
+#define SYSREG_DRP_SIZE				6
+#define SYSREG_ILA_OFFSET			20
+#define SYSREG_ILA_SIZE				6
+#define SYSREG_IRP_OFFSET			26
+#define SYSREG_IRP_SIZE				6
 
 /* Bitfields in PCCR */
-
-/* Bitfields in BEAR */
+#define SYSREG_PCCR_R_OFFSET			1
+#define SYSREG_PCCR_R_SIZE			1
+#define SYSREG_PCCR_C_OFFSET			2
+#define SYSREG_PCCR_C_SIZE			1
+#define SYSREG_PCCR_S_OFFSET			3
+#define SYSREG_PCCR_S_SIZE			1
+#define SYSREG_IEC_OFFSET			4
+#define SYSREG_IEC_SIZE				1
+#define SYSREG_IE0_OFFSET			5
+#define SYSREG_IE0_SIZE				1
+#define SYSREG_IE1_OFFSET			6
+#define SYSREG_IE1_SIZE				1
+#define SYSREG_FC_OFFSET			8
+#define SYSREG_FC_SIZE				1
+#define SYSREG_F0_OFFSET			9
+#define SYSREG_F0_SIZE				1
+#define SYSREG_F1_OFFSET			10
+#define SYSREG_F1_SIZE				1
+#define SYSREG_CONF0_OFFSET			12
+#define SYSREG_CONF0_SIZE			6
+#define SYSREG_CONF1_OFFSET			18
+#define SYSREG_CONF1_SIZE			6
 
 /* Constants for ECR */
-#define ECR_UNRECOVERABLE                       0
-#define ECR_TLB_MULTIPLE                        1
-#define ECR_BUS_ERROR_WRITE                     2
-#define ECR_BUS_ERROR_READ                      3
-#define ECR_NMI                                 4
-#define ECR_ADDR_ALIGN_X                        5
-#define ECR_PROTECTION_X                        6
-#define ECR_DEBUG                               7
-#define ECR_ILLEGAL_OPCODE                      8
-#define ECR_UNIMPL_INSTRUCTION                  9
-#define ECR_PRIVILEGE_VIOLATION                 10
-#define ECR_FPE                                 11
-#define ECR_COPROC_ABSENT                       12
-#define ECR_ADDR_ALIGN_R                        13
-#define ECR_ADDR_ALIGN_W                        14
-#define ECR_PROTECTION_R                        15
-#define ECR_PROTECTION_W                        16
-#define ECR_DTLB_MODIFIED                       17
-#define ECR_TLB_MISS_X                          20
-#define ECR_TLB_MISS_R                          24
-#define ECR_TLB_MISS_W                          28
+#define ECR_UNRECOVERABLE			0
+#define ECR_TLB_MULTIPLE			1
+#define ECR_BUS_ERROR_WRITE			2
+#define ECR_BUS_ERROR_READ			3
+#define ECR_NMI					4
+#define ECR_ADDR_ALIGN_X			5
+#define ECR_PROTECTION_X			6
+#define ECR_DEBUG				7
+#define ECR_ILLEGAL_OPCODE			8
+#define ECR_UNIMPL_INSTRUCTION			9
+#define ECR_PRIVILEGE_VIOLATION			10
+#define ECR_FPE					11
+#define ECR_COPROC_ABSENT			12
+#define ECR_ADDR_ALIGN_R			13
+#define ECR_ADDR_ALIGN_W			14
+#define ECR_PROTECTION_R			15
+#define ECR_PROTECTION_W			16
+#define ECR_DTLB_MODIFIED			17
+#define ECR_TLB_MISS_X				20
+#define ECR_TLB_MISS_R				24
+#define ECR_TLB_MISS_W				28
 
 /* Bit manipulation macros */
-#define SYSREG_BIT(name)                        (1 << SYSREG_##name##_OFFSET)
-#define SYSREG_BF(name,value)                   (((value) & ((1 << SYSREG_##name##_SIZE) - 1)) << SYSREG_##name##_OFFSET)
-#define SYSREG_BFEXT(name,value)                (((value) >> SYSREG_##name##_OFFSET) & ((1 << SYSREG_##name##_SIZE) - 1))
-#define SYSREG_BFINS(name,value,old)            (((old) & ~(((1 << SYSREG_##name##_SIZE) - 1) << SYSREG_##name##_OFFSET)) | SYSREG_BF(name,value))
+#define SYSREG_BIT(name)				\
+	(1 << SYSREG_##name##_OFFSET)
+#define SYSREG_BF(name,value)				\
+	(((value) & ((1 << SYSREG_##name##_SIZE) - 1))	\
+	 << SYSREG_##name##_OFFSET)
+#define SYSREG_BFEXT(name,value)\
+	(((value) >> SYSREG_##name##_OFFSET)		\
+	 & ((1 << SYSREG_##name##_SIZE) - 1))
+#define SYSREG_BFINS(name,value,old)			\
+	(((old) & ~(((1 << SYSREG_##name##_SIZE) - 1)	\
+		    << SYSREG_##name##_OFFSET))		\
+	 | SYSREG_BF(name,value))
 
+/* Register access macros */
 #ifdef __CHECKER__
 extern unsigned long __builtin_mfsr(unsigned long reg);
 extern void __builtin_mtsr(unsigned long reg, unsigned long value);
 #endif
 
-/* Register access macros */
-#define sysreg_read(reg)                        __builtin_mfsr(SYSREG_##reg)
-#define sysreg_write(reg, value)                __builtin_mtsr(SYSREG_##reg, value)
+#define sysreg_read(reg)		__builtin_mfsr(SYSREG_##reg)
+#define sysreg_write(reg, value)	__builtin_mtsr(SYSREG_##reg, value)
 
-#endif /* __ASM_AVR32_SYSREG_H__ */
+#endif /* __ASM_AVR32_SYSREG_H */
diff --git a/include/asm-avr32/system.h b/include/asm-avr32/system.h
index ac596058..a8236ba 100644
--- a/include/asm-avr32/system.h
+++ b/include/asm-avr32/system.h
@@ -9,6 +9,7 @@
 #define __ASM_AVR32_SYSTEM_H
 
 #include <linux/compiler.h>
+#include <linux/linkage.h>
 #include <linux/types.h>
 
 #include <asm/ptrace.h>
@@ -140,15 +141,9 @@
 				   sizeof(*(ptr))))
 
 struct pt_regs;
-extern void __die(const char *, struct pt_regs *, unsigned long,
-		  const char *, const char *, unsigned long);
-extern void __die_if_kernel(const char *, struct pt_regs *, unsigned long,
-			    const char *, const char *, unsigned long);
-
-#define die(msg, regs, err)					\
-	__die(msg, regs, err, __FILE__ ":", __FUNCTION__, __LINE__)
-#define die_if_kernel(msg, regs, err)					\
-	__die_if_kernel(msg, regs, err, __FILE__ ":", __FUNCTION__, __LINE__)
+void NORET_TYPE die(const char *str, struct pt_regs *regs, long err);
+void _exception(long signr, struct pt_regs *regs, int code,
+		unsigned long addr);
 
 #define arch_align_stack(x)	(x)
 
diff --git a/include/asm-avr32/thread_info.h b/include/asm-avr32/thread_info.h
index d1f5b35..a2e606d 100644
--- a/include/asm-avr32/thread_info.h
+++ b/include/asm-avr32/thread_info.h
@@ -83,6 +83,7 @@
 #define TIF_SINGLE_STEP		6	/* single step after next break */
 #define TIF_MEMDIE		7
 #define TIF_RESTORE_SIGMASK	8	/* restore signal mask in do_signal */
+#define TIF_CPU_GOING_TO_SLEEP	9	/* CPU is entering sleep 0 mode */
 #define TIF_USERSPACE		31      /* true if FS sets userspace */
 
 #define _TIF_SYSCALL_TRACE	(1 << TIF_SYSCALL_TRACE)
@@ -94,6 +95,7 @@
 #define _TIF_SINGLE_STEP	(1 << TIF_SINGLE_STEP)
 #define _TIF_MEMDIE		(1 << TIF_MEMDIE)
 #define _TIF_RESTORE_SIGMASK	(1 << TIF_RESTORE_SIGMASK)
+#define _TIF_CPU_GOING_TO_SLEEP (1 << TIF_CPU_GOING_TO_SLEEP)
 
 /* XXX: These two masks must never span more than 16 bits! */
 /* work to do on interrupt/exception return */
diff --git a/include/asm-avr32/uaccess.h b/include/asm-avr32/uaccess.h
index 74a679e9..ed09239 100644
--- a/include/asm-avr32/uaccess.h
+++ b/include/asm-avr32/uaccess.h
@@ -181,24 +181,23 @@
 
 #define __get_user_nocheck(x, ptr, size)				\
 ({									\
-	typeof(*(ptr)) __gu_val = (typeof(*(ptr)) __force)0;		\
+	unsigned long __gu_val = 0;					\
 	int __gu_err = 0;						\
 									\
 	switch (size) {							\
 	case 1: __get_user_asm("ub", __gu_val, ptr, __gu_err); break;	\
 	case 2: __get_user_asm("uh", __gu_val, ptr, __gu_err); break;	\
 	case 4: __get_user_asm("w", __gu_val, ptr, __gu_err); break;	\
-	case 8: __get_user_asm("d", __gu_val, ptr, __gu_err); break;	\
 	default: __gu_err = __get_user_bad(); break;			\
 	}								\
 									\
-	x = __gu_val;							\
+	x = (typeof(*(ptr)))__gu_val;					\
 	__gu_err;							\
 })
 
 #define __get_user_check(x, ptr, size)					\
 ({									\
-	typeof(*(ptr)) __gu_val = (typeof(*(ptr)) __force)0;		\
+	unsigned long __gu_val = 0;					\
 	const typeof(*(ptr)) __user * __gu_addr = (ptr);		\
 	int __gu_err = 0;						\
 									\
@@ -216,10 +215,6 @@
 			__get_user_asm("w", __gu_val, __gu_addr,	\
 				       __gu_err);			\
 			break;						\
-		case 8:							\
-			__get_user_asm("d", __gu_val, __gu_addr,	\
-				       __gu_err);			\
-			break;						\
 		default:						\
 			__gu_err = __get_user_bad();			\
 			break;						\
@@ -227,7 +222,7 @@
 	} else {							\
 		__gu_err = -EFAULT;					\
 	}								\
-	x = __gu_val;							\
+	x = (typeof(*(ptr)))__gu_val;					\
 	__gu_err;							\
 })
 
diff --git a/include/asm-cris/socket.h b/include/asm-cris/socket.h
index 01cfdf1..5b18dfd 100644
--- a/include/asm-cris/socket.h
+++ b/include/asm-cris/socket.h
@@ -51,6 +51,8 @@
 
 #define SO_PEERSEC             31
 #define SO_PASSSEC		34
+#define SO_TIMESTAMPNS		35
+#define SCM_TIMESTAMPNS		SO_TIMESTAMPNS
 
 #endif /* _ASM_SOCKET_H */
 
diff --git a/include/asm-cris/sockios.h b/include/asm-cris/sockios.h
index 6c4012f..cfe7bfe 100644
--- a/include/asm-cris/sockios.h
+++ b/include/asm-cris/sockios.h
@@ -7,6 +7,7 @@
 #define FIOGETOWN	0x8903
 #define SIOCGPGRP	0x8904
 #define SIOCATMARK	0x8905
-#define SIOCGSTAMP	0x8906		/* Get stamp */
+#define SIOCGSTAMP	0x8906		/* Get stamp (timeval) */
+#define SIOCGSTAMPNS	0x8907		/* Get stamp (timespec) */
 
 #endif
diff --git a/include/asm-frv/socket.h b/include/asm-frv/socket.h
index 31db18f..a823bef 100644
--- a/include/asm-frv/socket.h
+++ b/include/asm-frv/socket.h
@@ -49,6 +49,8 @@
 
 #define SO_PEERSEC		31
 #define SO_PASSSEC		34
+#define SO_TIMESTAMPNS		35
+#define SCM_TIMESTAMPNS		SO_TIMESTAMPNS
 
 #endif /* _ASM_SOCKET_H */
 
diff --git a/include/asm-frv/sockios.h b/include/asm-frv/sockios.h
index 8a6e4b2..5dbdd13 100644
--- a/include/asm-frv/sockios.h
+++ b/include/asm-frv/sockios.h
@@ -7,7 +7,8 @@
 #define FIOGETOWN	0x8903
 #define SIOCGPGRP	0x8904
 #define SIOCATMARK	0x8905
-#define SIOCGSTAMP	0x8906		/* Get stamp */
+#define SIOCGSTAMP	0x8906		/* Get stamp (timeval) */
+#define SIOCGSTAMPNS	0x8907		/* Get stamp (timespec) */
 
 #endif /* _ASM_SOCKIOS__ */
 
diff --git a/include/asm-generic/div64.h b/include/asm-generic/div64.h
index 8f4e319..a4a4937 100644
--- a/include/asm-generic/div64.h
+++ b/include/asm-generic/div64.h
@@ -30,6 +30,11 @@
 	__rem;							\
  })
 
+static inline uint64_t div64_64(uint64_t dividend, uint64_t divisor)
+{
+	return dividend / divisor;
+}
+
 #elif BITS_PER_LONG == 32
 
 extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor);
@@ -49,6 +54,8 @@
 	__rem;						\
  })
 
+extern uint64_t div64_64(uint64_t dividend, uint64_t divisor);
+
 #else /* BITS_PER_LONG == ?? */
 
 # error do_div() does not yet support the C64
diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h
index 6d7e279..dc8f99e 100644
--- a/include/asm-generic/pgtable.h
+++ b/include/asm-generic/pgtable.h
@@ -139,8 +139,15 @@
 #define pte_same(A,B)	(pte_val(A) == pte_val(B))
 #endif
 
-#ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_DIRTY
-#define page_test_and_clear_dirty(page) (0)
+#ifndef __HAVE_ARCH_PAGE_TEST_DIRTY
+#define page_test_dirty(page)		(0)
+#endif
+
+#ifndef __HAVE_ARCH_PAGE_CLEAR_DIRTY
+#define page_clear_dirty(page)		do { } while (0)
+#endif
+
+#ifndef __HAVE_ARCH_PAGE_TEST_DIRTY
 #define pte_maybe_dirty(pte)		pte_dirty(pte)
 #else
 #define pte_maybe_dirty(pte)		(1)
diff --git a/include/asm-h8300/socket.h b/include/asm-h8300/socket.h
index ebc830f..39911d8 100644
--- a/include/asm-h8300/socket.h
+++ b/include/asm-h8300/socket.h
@@ -49,5 +49,7 @@
 
 #define SO_PEERSEC		31
 #define SO_PASSSEC		34
+#define SO_TIMESTAMPNS		35
+#define SCM_TIMESTAMPNS		SO_TIMESTAMPNS
 
 #endif /* _ASM_SOCKET_H */
diff --git a/include/asm-h8300/sockios.h b/include/asm-h8300/sockios.h
index d005d95..e9c7ec8 100644
--- a/include/asm-h8300/sockios.h
+++ b/include/asm-h8300/sockios.h
@@ -7,6 +7,7 @@
 #define FIOGETOWN	0x8903
 #define SIOCGPGRP	0x8904
 #define SIOCATMARK	0x8905
-#define SIOCGSTAMP	0x8906		/* Get stamp */
+#define SIOCGSTAMP	0x8906		/* Get stamp (timeval) */
+#define SIOCGSTAMPNS	0x8907		/* Get stamp (timespec) */
 
 #endif /* __ARCH_H8300_SOCKIOS__ */
diff --git a/include/asm-i386/agp.h b/include/asm-i386/agp.h
index 9075083..6af173d 100644
--- a/include/asm-i386/agp.h
+++ b/include/asm-i386/agp.h
@@ -12,8 +12,10 @@
  * data corruption on some CPUs.
  */
 
-int map_page_into_agp(struct page *page);
-int unmap_page_from_agp(struct page *page);
+/* Caller's responsibility to call global_flush_tlb() for
+ * performance reasons */
+#define map_page_into_agp(page) change_page_attr(page, 1, PAGE_KERNEL_NOCACHE)
+#define unmap_page_from_agp(page) change_page_attr(page, 1, PAGE_KERNEL)
 #define flush_agp_mappings() global_flush_tlb()
 
 /* Could use CLFLUSH here if the cpu supports it. But then it would
diff --git a/include/asm-i386/div64.h b/include/asm-i386/div64.h
index 75c67c7..438e980 100644
--- a/include/asm-i386/div64.h
+++ b/include/asm-i386/div64.h
@@ -1,6 +1,8 @@
 #ifndef __I386_DIV64
 #define __I386_DIV64
 
+#include <linux/types.h>
+
 /*
  * do_div() is NOT a C function. It wants to return
  * two values (the quotient and the remainder), but
@@ -45,4 +47,6 @@
 	return dum2;
 
 }
+
+extern uint64_t div64_64(uint64_t dividend, uint64_t divisor);
 #endif
diff --git a/include/asm-i386/socket.h b/include/asm-i386/socket.h
index 5755d57..99ca648 100644
--- a/include/asm-i386/socket.h
+++ b/include/asm-i386/socket.h
@@ -49,5 +49,7 @@
 
 #define SO_PEERSEC		31
 #define SO_PASSSEC		34
+#define SO_TIMESTAMPNS		35
+#define SCM_TIMESTAMPNS		SO_TIMESTAMPNS
 
 #endif /* _ASM_SOCKET_H */
diff --git a/include/asm-i386/sockios.h b/include/asm-i386/sockios.h
index 6b747f8..ff528c7 100644
--- a/include/asm-i386/sockios.h
+++ b/include/asm-i386/sockios.h
@@ -7,6 +7,7 @@
 #define FIOGETOWN	0x8903
 #define SIOCGPGRP	0x8904
 #define SIOCATMARK	0x8905
-#define SIOCGSTAMP	0x8906		/* Get stamp */
+#define SIOCGSTAMP	0x8906		/* Get stamp (timeval) */
+#define SIOCGSTAMPNS	0x8907		/* Get stamp (timespec) */
 
 #endif
diff --git a/include/asm-ia64/socket.h b/include/asm-ia64/socket.h
index d638ef3..9e42ce4 100644
--- a/include/asm-ia64/socket.h
+++ b/include/asm-ia64/socket.h
@@ -58,5 +58,7 @@
 
 #define SO_PEERSEC             31
 #define SO_PASSSEC		34
+#define SO_TIMESTAMPNS		35
+#define SCM_TIMESTAMPNS		SO_TIMESTAMPNS
 
 #endif /* _ASM_IA64_SOCKET_H */
diff --git a/include/asm-ia64/sockios.h b/include/asm-ia64/sockios.h
index cf94857..15c9246 100644
--- a/include/asm-ia64/sockios.h
+++ b/include/asm-ia64/sockios.h
@@ -14,6 +14,7 @@
 #define FIOGETOWN	0x8903
 #define SIOCGPGRP	0x8904
 #define SIOCATMARK	0x8905
-#define SIOCGSTAMP	0x8906		/* Get stamp */
+#define SIOCGSTAMP	0x8906		/* Get stamp (timeval) */
+#define SIOCGSTAMPNS	0x8907		/* Get stamp (timespec) */
 
 #endif /* _ASM_IA64_SOCKIOS_H */
diff --git a/include/asm-m32r/socket.h b/include/asm-m32r/socket.h
index acdf748..793d5d3 100644
--- a/include/asm-m32r/socket.h
+++ b/include/asm-m32r/socket.h
@@ -49,5 +49,7 @@
 
 #define SO_PEERSEC		31
 #define SO_PASSSEC		34
+#define SO_TIMESTAMPNS		35
+#define SCM_TIMESTAMPNS		SO_TIMESTAMPNS
 
 #endif /* _ASM_M32R_SOCKET_H */
diff --git a/include/asm-m32r/sockios.h b/include/asm-m32r/sockios.h
index f89962e..6c1fb9b 100644
--- a/include/asm-m32r/sockios.h
+++ b/include/asm-m32r/sockios.h
@@ -7,6 +7,7 @@
 #define FIOGETOWN	0x8903
 #define SIOCGPGRP	0x8904
 #define SIOCATMARK	0x8905
-#define SIOCGSTAMP	0x8906		/* Get stamp */
+#define SIOCGSTAMP	0x8906		/* Get stamp (timeval) */
+#define SIOCGSTAMPNS	0x8907		/* Get stamp (timespec) */
 
 #endif  /* _ASM_M32R_SOCKIOS_H */
diff --git a/include/asm-m68k/div64.h b/include/asm-m68k/div64.h
index 9f65de1..33caad1 100644
--- a/include/asm-m68k/div64.h
+++ b/include/asm-m68k/div64.h
@@ -1,6 +1,8 @@
 #ifndef _M68K_DIV64_H
 #define _M68K_DIV64_H
 
+#include <linux/types.h>
+
 /* n = n / base; return rem; */
 
 #define do_div(n, base) ({					\
@@ -23,4 +25,5 @@
 	__rem;							\
 })
 
+extern uint64_t div64_64(uint64_t dividend, uint64_t divisor);
 #endif /* _M68K_DIV64_H */
diff --git a/include/asm-m68k/socket.h b/include/asm-m68k/socket.h
index a5966ec..6d21b90 100644
--- a/include/asm-m68k/socket.h
+++ b/include/asm-m68k/socket.h
@@ -49,5 +49,7 @@
 
 #define SO_PEERSEC             31
 #define SO_PASSSEC		34
+#define SO_TIMESTAMPNS		35
+#define SCM_TIMESTAMPNS		SO_TIMESTAMPNS
 
 #endif /* _ASM_SOCKET_H */
diff --git a/include/asm-m68k/sockios.h b/include/asm-m68k/sockios.h
index 9b9ed97..c04a239 100644
--- a/include/asm-m68k/sockios.h
+++ b/include/asm-m68k/sockios.h
@@ -7,6 +7,7 @@
 #define FIOGETOWN	0x8903
 #define SIOCGPGRP	0x8904
 #define SIOCATMARK	0x8905
-#define SIOCGSTAMP	0x8906		/* Get stamp */
+#define SIOCGSTAMP	0x8906		/* Get stamp (timeval) */
+#define SIOCGSTAMPNS	0x8907		/* Get stamp (timespec) */
 
 #endif /* __ARCH_M68K_SOCKIOS__ */
diff --git a/include/asm-mips/cacheflush.h b/include/asm-mips/cacheflush.h
index 28d907d..4933b49 100644
--- a/include/asm-mips/cacheflush.h
+++ b/include/asm-mips/cacheflush.h
@@ -96,6 +96,6 @@
 unsigned long __init run_uncached(void *func);
 
 extern void *kmap_coherent(struct page *page, unsigned long addr);
-extern void kunmap_coherent(struct page *page);
+extern void kunmap_coherent(void);
 
 #endif /* _ASM_CACHEFLUSH_H */
diff --git a/include/asm-mips/div64.h b/include/asm-mips/div64.h
index d107832..66189f5 100644
--- a/include/asm-mips/div64.h
+++ b/include/asm-mips/div64.h
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2000, 2004  Maciej W. Rozycki
- * Copyright (C) 2003 Ralf Baechle
+ * Copyright (C) 2003, 07 Ralf Baechle (ralf@linux-mips.org)
  *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file "COPYING" in the main directory of this archive
@@ -9,6 +9,8 @@
 #ifndef _ASM_DIV64_H
 #define _ASM_DIV64_H
 
+#include <linux/types.h>
+
 #if (_MIPS_SZLONG == 32)
 
 #include <asm/compiler.h>
@@ -78,6 +80,8 @@
 	__quot = __quot << 32 | __low; \
 	(n) = __quot; \
 	__mod; })
+
+extern uint64_t div64_64(uint64_t dividend, uint64_t divisor);
 #endif /* (_MIPS_SZLONG == 32) */
 
 #if (_MIPS_SZLONG == 64)
@@ -101,6 +105,11 @@
 	(n) = __quot; \
 	__mod; })
 
+static inline uint64_t div64_64(uint64_t dividend, uint64_t divisor)
+{
+	return dividend / divisor;
+}
+
 #endif /* (_MIPS_SZLONG == 64) */
 
 #endif /* _ASM_DIV64_H */
diff --git a/include/asm-mips/jmr3927/irq.h b/include/asm-mips/jmr3927/irq.h
deleted file mode 100644
index e3e7ed3..0000000
--- a/include/asm-mips/jmr3927/irq.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- *  linux/include/asm-mips/tx3927/irq.h
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2001 Toshiba Corporation
- */
-#ifndef __ASM_TX3927_IRQ_H
-#define __ASM_TX3927_IRQ_H
-
-#ifndef __ASSEMBLY__
-
-#include <asm/irq.h>
-
-struct tb_irq_space {
-	struct tb_irq_space* next;
-	int start_irqno;
-	int nr_irqs;
-	void (*mask_func)(int irq_nr, int space_id);
-	void (*unmask_func)(int irq_no, int space_id);
-	const char *name;
-	int space_id;
-	int can_share;
-};
-extern struct tb_irq_space* tb_irq_spaces;
-
-static __inline__ void add_tb_irq_space(struct tb_irq_space* sp)
-{
-	sp->next = tb_irq_spaces;
-	tb_irq_spaces = sp;
-}
-
-
-struct pt_regs;
-extern void
-toshibaboards_spurious(struct pt_regs *regs, int irq);
-extern void
-toshibaboards_irqdispatch(struct pt_regs *regs, int irq);
-
-extern struct irqaction *
-toshibaboards_get_irq_action(int irq);
-extern int
-toshibaboards_setup_irq(int irq, struct irqaction * new);
-
-
-extern int (*toshibaboards_gen_iack)(void);
-
-#endif /* !__ASSEMBLY__ */
-
-#define NR_ISA_IRQS 16
-#define TB_IRQ_IS_ISA(irq)	\
-	(0 <= (irq) && (irq) < NR_ISA_IRQS)
-#define TB_IRQ_TO_ISA_IRQ(irq)	(irq)
-
-#endif /* __ASM_TX3927_IRQ_H */
diff --git a/include/asm-mips/jmr3927/jmr3927.h b/include/asm-mips/jmr3927/jmr3927.h
index c50e68f..958e297 100644
--- a/include/asm-mips/jmr3927/jmr3927.h
+++ b/include/asm-mips/jmr3927/jmr3927.h
@@ -1,5 +1,5 @@
 /*
- * Defines for the TJSYS JMR-TX3927/JMI-3927IO2/JMY-1394IF.
+ * Defines for the TJSYS JMR-TX3927
  *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file "COPYING" in the main directory of this archive
@@ -12,10 +12,7 @@
 
 #include <asm/jmr3927/tx3927.h>
 #include <asm/addrspace.h>
-#include <asm/jmr3927/irq.h>
-#ifndef __ASSEMBLY__
 #include <asm/system.h>
-#endif
 
 /* CS */
 #define JMR3927_ROMCE0	0x1fc00000	/* 4M */
@@ -35,28 +32,10 @@
 #define JMR3927_SDRAM_SIZE	0x02000000	/* 32M */
 #define JMR3927_PORT_BASE	KSEG1
 
-/* select indirect initiator access per errata */
-#define JMR3927_INIT_INDIRECT_PCI
-#define PCI_ISTAT_IDICC           0x1000
-#define PCI_IPCIBE_IBE_LONG       0
-#define PCI_IPCIBE_ICMD_IOREAD    2
-#define PCI_IPCIBE_ICMD_IOWRITE   3
-#define PCI_IPCIBE_ICMD_MEMREAD   6
-#define PCI_IPCIBE_ICMD_MEMWRITE  7
-#define PCI_IPCIBE_ICMD_SHIFT     4
-
 /* Address map (virtual address) */
 #define JMR3927_ROM0_BASE	(KSEG1 + JMR3927_ROMCE0)
 #define JMR3927_ROM1_BASE	(KSEG1 + JMR3927_ROMCE1)
 #define JMR3927_IOC_BASE	(KSEG1 + JMR3927_ROMCE2)
-#define JMR3927_IOB_BASE	(KSEG1 + JMR3927_ROMCE3)
-#define JMR3927_ISAMEM_BASE	(JMR3927_IOB_BASE)
-#define JMR3927_ISAIO_BASE	(JMR3927_IOB_BASE + 0x01000000)
-#define JMR3927_ISAC_BASE	(JMR3927_IOB_BASE + 0x02000000)
-#define JMR3927_LCDVGA_REG_BASE	(JMR3927_IOB_BASE + 0x03000000)
-#define JMR3927_LCDVGA_MEM_BASE	(JMR3927_IOB_BASE + 0x03800000)
-#define JMR3927_JMY1394_BASE	(KSEG1 + JMR3927_ROMCE5)
-#define JMR3927_PREMIER3_BASE	(JMR3927_JMY1394_BASE + 0x00100000)
 #define JMR3927_PCIMEM_BASE	(KSEG1 + JMR3927_PCIMEM)
 #define JMR3927_PCIIO_BASE	(KSEG1 + JMR3927_PCIIO)
 
@@ -72,25 +51,14 @@
 #define JMR3927_IOC_INTP_ADDR	(JMR3927_IOC_BASE + 0x000b0000)
 #define JMR3927_IOC_RESET_ADDR	(JMR3927_IOC_BASE + 0x000f0000)
 
-#define JMR3927_ISAC_REV_ADDR	(JMR3927_ISAC_BASE + 0x00000000)
-#define JMR3927_ISAC_EINTS_ADDR	(JMR3927_ISAC_BASE + 0x00200000)
-#define JMR3927_ISAC_EINTM_ADDR	(JMR3927_ISAC_BASE + 0x00300000)
-#define JMR3927_ISAC_NMI_ADDR	(JMR3927_ISAC_BASE + 0x00400000)
-#define JMR3927_ISAC_LED_ADDR	(JMR3927_ISAC_BASE + 0x00500000)
-#define JMR3927_ISAC_INTP_ADDR	(JMR3927_ISAC_BASE + 0x00800000)
-#define JMR3927_ISAC_INTS1_ADDR	(JMR3927_ISAC_BASE + 0x00900000)
-#define JMR3927_ISAC_INTS2_ADDR	(JMR3927_ISAC_BASE + 0x00a00000)
-#define JMR3927_ISAC_INTM_ADDR	(JMR3927_ISAC_BASE + 0x00b00000)
-
 /* Flash ROM */
 #define JMR3927_FLASH_BASE	(JMR3927_ROM0_BASE)
 #define JMR3927_FLASH_SIZE	0x00400000
 
-/* bits for IOC_REV/IOC_BREV/ISAC_REV (high byte) */
+/* bits for IOC_REV/IOC_BREV (high byte) */
 #define JMR3927_IDT_MASK	0xfc
 #define JMR3927_REV_MASK	0x03
 #define JMR3927_IOC_IDT		0xe0
-#define JMR3927_ISAC_IDT	0x20
 
 /* bits for IOC_INTS1/IOC_INTS2/IOC_INTM/IOC_INTP (high byte) */
 #define JMR3927_IOC_INTB_PCIA	0
@@ -114,40 +82,6 @@
 #define JMR3927_IOC_RESET_CPU	1
 #define JMR3927_IOC_RESET_PCI	2
 
-/* bits for ISAC_EINTS/ISAC_EINTM (high byte) */
-#define JMR3927_ISAC_EINTB_IOCHK	2
-#define JMR3927_ISAC_EINTB_BWTH	4
-#define JMR3927_ISAC_EINTF_IOCHK	(1 << JMR3927_ISAC_EINTB_IOCHK)
-#define JMR3927_ISAC_EINTF_BWTH	(1 << JMR3927_ISAC_EINTB_BWTH)
-
-/* bits for ISAC_LED (high byte) */
-#define JMR3927_ISAC_LED_ISALED	0x01
-#define JMR3927_ISAC_LED_USRLED	0x02
-
-/* bits for ISAC_INTS/ISAC_INTM/ISAC_INTP (high byte) */
-#define JMR3927_ISAC_INTB_IRQ5	0
-#define JMR3927_ISAC_INTB_IRQKB	1
-#define JMR3927_ISAC_INTB_IRQMOUSE	2
-#define JMR3927_ISAC_INTB_IRQ4	3
-#define JMR3927_ISAC_INTB_IRQ12	4
-#define JMR3927_ISAC_INTB_IRQ3	5
-#define JMR3927_ISAC_INTB_IRQ10	6
-#define JMR3927_ISAC_INTB_ISAER	7
-#define JMR3927_ISAC_INTF_IRQ5	(1 << JMR3927_ISAC_INTB_IRQ5)
-#define JMR3927_ISAC_INTF_IRQKB	(1 << JMR3927_ISAC_INTB_IRQKB)
-#define JMR3927_ISAC_INTF_IRQMOUSE	(1 << JMR3927_ISAC_INTB_IRQMOUSE)
-#define JMR3927_ISAC_INTF_IRQ4	(1 << JMR3927_ISAC_INTB_IRQ4)
-#define JMR3927_ISAC_INTF_IRQ12	(1 << JMR3927_ISAC_INTB_IRQ12)
-#define JMR3927_ISAC_INTF_IRQ3	(1 << JMR3927_ISAC_INTB_IRQ3)
-#define JMR3927_ISAC_INTF_IRQ10	(1 << JMR3927_ISAC_INTB_IRQ10)
-#define JMR3927_ISAC_INTF_ISAER	(1 << JMR3927_ISAC_INTB_ISAER)
-
-#ifndef __ASSEMBLY__
-
-#if 0
-#define jmr3927_ioc_reg_out(d, a)	((*(volatile unsigned short *)(a)) = (d) << 8)
-#define jmr3927_ioc_reg_in(a)		(((*(volatile unsigned short *)(a)) >> 8) & 0xff)
-#else
 #if defined(__BIG_ENDIAN)
 #define jmr3927_ioc_reg_out(d, a)	((*(volatile unsigned char *)(a)) = (d))
 #define jmr3927_ioc_reg_in(a)		(*(volatile unsigned char *)(a))
@@ -157,31 +91,9 @@
 #else
 #error "No Endian"
 #endif
-#endif
-#define jmr3927_isac_reg_out(d, a)	((*(volatile unsigned char *)(a)) = (d))
-#define jmr3927_isac_reg_in(a)		(*(volatile unsigned char *)(a))
-
-static inline int jmr3927_have_isac(void)
-{
-	unsigned char idt;
-	unsigned long flags;
-	unsigned long romcr3;
-
-	local_irq_save(flags);
-	romcr3 = tx3927_romcptr->cr[3];
-	tx3927_romcptr->cr[3] &= 0xffffefff;	/* do not wait infinitely */
-	idt = jmr3927_isac_reg_in(JMR3927_ISAC_REV_ADDR) & JMR3927_IDT_MASK;
-	tx3927_romcptr->cr[3] = romcr3;
-	local_irq_restore(flags);
-
-	return idt == JMR3927_ISAC_IDT;
-}
-#define jmr3927_have_nvram() \
-	((jmr3927_ioc_reg_in(JMR3927_IOC_REV_ADDR) & JMR3927_IDT_MASK) == JMR3927_IOC_IDT)
 
 /* LED macro */
 #define jmr3927_led_set(n/*0-16*/)	jmr3927_ioc_reg_out(~(n), JMR3927_IOC_LED_ADDR)
-#define jmr3927_io_led_set(n/*0-3*/)	jmr3927_isac_reg_out((n), JMR3927_ISAC_LED_ADDR)
 
 #define jmr3927_led_and_set(n/*0-16*/)	jmr3927_ioc_reg_out((~(n)) & jmr3927_ioc_reg_in(JMR3927_IOC_LED_ADDR), JMR3927_IOC_LED_ADDR)
 
@@ -190,10 +102,6 @@
 #define jmr3927_dipsw2()	((tx3927_pioptr->din & (1 << 10)) == 0)
 #define jmr3927_dipsw3()	((jmr3927_ioc_reg_in(JMR3927_IOC_DIPSW_ADDR) & 2) == 0)
 #define jmr3927_dipsw4()	((jmr3927_ioc_reg_in(JMR3927_IOC_DIPSW_ADDR) & 1) == 0)
-#define jmr3927_io_dipsw()	(jmr3927_isac_reg_in(JMR3927_ISAC_LED_ADDR) >> 4)
-
-
-#endif /* !__ASSEMBLY__ */
 
 /*
  * IRQ mappings
@@ -206,16 +114,10 @@
  */
 #define JMR3927_NR_IRQ_IRC	16	/* On-Chip IRC */
 #define JMR3927_NR_IRQ_IOC	8	/* PCI/MODEM/INT[6:7] */
-#define JMR3927_NR_IRQ_ISAC	8	/* ISA */
 
-
-#define JMR3927_IRQ_IRC	NR_ISA_IRQS
+#define JMR3927_IRQ_IRC	16
 #define JMR3927_IRQ_IOC	(JMR3927_IRQ_IRC + JMR3927_NR_IRQ_IRC)
-#define JMR3927_IRQ_ISAC	(JMR3927_IRQ_IOC + JMR3927_NR_IRQ_IOC)
-#define JMR3927_IRQ_END	(JMR3927_IRQ_ISAC + JMR3927_NR_IRQ_ISAC)
-#define JMR3927_IRQ_IS_IRC(irq)	(JMR3927_IRQ_IRC <= (irq) && (irq) < JMR3927_IRQ_IOC)
-#define JMR3927_IRQ_IS_IOC(irq)		(JMR3927_IRQ_IOC <= (irq) && (irq) < JMR3927_IRQ_ISAC)
-#define JMR3927_IRQ_IS_ISAC(irq)	(JMR3927_IRQ_ISAC <= (irq) && (irq) < JMR3927_IRQ_END)
+#define JMR3927_IRQ_END	(JMR3927_IRQ_IOC + JMR3927_NR_IRQ_IOC)
 
 #define JMR3927_IRQ_IRC_INT0	(JMR3927_IRQ_IRC + TX3927_IR_INT0)
 #define JMR3927_IRQ_IRC_INT1	(JMR3927_IRQ_IRC + TX3927_IR_INT1)
@@ -240,37 +142,13 @@
 #define JMR3927_IRQ_IOC_INT6	(JMR3927_IRQ_IOC + JMR3927_IOC_INTB_INT6)
 #define JMR3927_IRQ_IOC_INT7	(JMR3927_IRQ_IOC + JMR3927_IOC_INTB_INT7)
 #define JMR3927_IRQ_IOC_SOFT	(JMR3927_IRQ_IOC + JMR3927_IOC_INTB_SOFT)
-#define JMR3927_IRQ_ISAC_IRQ5	(JMR3927_IRQ_ISAC + JMR3927_ISAC_INTB_IRQ5)
-#define JMR3927_IRQ_ISAC_IRQKB	(JMR3927_IRQ_ISAC + JMR3927_ISAC_INTB_IRQKB)
-#define JMR3927_IRQ_ISAC_IRQMOUSE	(JMR3927_IRQ_ISAC + JMR3927_ISAC_INTB_IRQMOUSE)
-#define JMR3927_IRQ_ISAC_IRQ4	(JMR3927_IRQ_ISAC + JMR3927_ISAC_INTB_IRQ4)
-#define JMR3927_IRQ_ISAC_IRQ12	(JMR3927_IRQ_ISAC + JMR3927_ISAC_INTB_IRQ12)
-#define JMR3927_IRQ_ISAC_IRQ3	(JMR3927_IRQ_ISAC + JMR3927_ISAC_INTB_IRQ3)
-#define JMR3927_IRQ_ISAC_IRQ10	(JMR3927_IRQ_ISAC + JMR3927_ISAC_INTB_IRQ10)
-#define JMR3927_IRQ_ISAC_ISAER	(JMR3927_IRQ_ISAC + JMR3927_ISAC_INTB_ISAER)
 
-#if 0	/* auto detect */
-/* RTL8019AS 10M Ether (JMI-3927IO2:JPW2:1-2 Short) */
-#define JMR3927_IRQ_ETHER1	JMR3927_IRQ_IRC_INT0
-#endif
 /* IOC (PCI, MODEM) */
 #define JMR3927_IRQ_IOCINT	JMR3927_IRQ_IRC_INT1
-/* ISAC (ISA, PCMCIA, KEYBOARD, MOUSE) */
-#define JMR3927_IRQ_ISACINT	JMR3927_IRQ_IRC_INT2
 /* TC35815 100M Ether (JMR-TX3912:JPW4:2-3 Short) */
 #define JMR3927_IRQ_ETHER0	JMR3927_IRQ_IRC_INT3
 /* Clock Tick (10ms) */
 #define JMR3927_IRQ_TICK	JMR3927_IRQ_IRC_TMR0
-#define JMR3927_IRQ_IDE		JMR3927_IRQ_ISAC_IRQ12
-
-/* IEEE1394 (Note that this may conflicts with RTL8019AS 10M Ether...) */
-#define JMR3927_IRQ_PREMIER3	JMR3927_IRQ_IRC_INT0
-
-/* I/O Ports */
-/* RTL8019AS 10M Ether */
-#define JMR3927_ETHER1_PORT	(JMR3927_ISAIO_BASE - JMR3927_PORT_BASE + 0x280)
-#define JMR3927_KBD_PORT	(JMR3927_ISAIO_BASE - JMR3927_PORT_BASE + 0x00800060)
-#define JMR3927_IDE_PORT	(JMR3927_ISAIO_BASE - JMR3927_PORT_BASE + 0x001001f0)
 
 /* Clocks */
 #define JMR3927_CORECLK	132710400	/* 132.7MHz */
diff --git a/include/asm-mips/jmr3927/tx3927.h b/include/asm-mips/jmr3927/tx3927.h
index b3d67c7..0b9073b 100644
--- a/include/asm-mips/jmr3927/tx3927.h
+++ b/include/asm-mips/jmr3927/tx3927.h
@@ -22,8 +22,6 @@
 #define TX3927_SIO_REG(ch)	(0xfffef300 + (ch) * 0x100)
 #define TX3927_PIO_REG		0xfffef500
 
-#ifndef __ASSEMBLY__
-
 struct tx3927_sdramc_reg {
 	volatile unsigned long cr[8];
 	volatile unsigned long tr[3];
@@ -164,8 +162,6 @@
 	volatile unsigned long pdcr;
 };
 
-#endif /* !__ASSEMBLY__ */
-
 /*
  * SDRAMC
  */
@@ -348,8 +344,6 @@
 #define TX3927_PCFG_SELDMA_ALL	0x0000000f
 #define TX3927_PCFG_SELDMA(ch)	(0x00000001<<(ch))
 
-#ifndef __ASSEMBLY__
-
 #define tx3927_sdramcptr	((struct tx3927_sdramc_reg *)TX3927_SDRAMC_REG)
 #define tx3927_romcptr		((struct tx3927_romc_reg *)TX3927_ROMC_REG)
 #define tx3927_dmaptr		((struct tx3927_dma_reg *)TX3927_DMA_REG)
@@ -360,6 +354,4 @@
 #define tx3927_sioptr(ch)	((struct txx927_sio_reg *)TX3927_SIO_REG(ch))
 #define tx3927_pioptr		((struct txx927_pio_reg *)TX3927_PIO_REG)
 
-#endif /* !__ASSEMBLY__ */
-
 #endif /* __ASM_TX3927_H */
diff --git a/include/asm-mips/jmr3927/txx927.h b/include/asm-mips/jmr3927/txx927.h
index 9d5792e..58a8ff6 100644
--- a/include/asm-mips/jmr3927/txx927.h
+++ b/include/asm-mips/jmr3927/txx927.h
@@ -10,8 +10,6 @@
 #ifndef __ASM_TXX927_H
 #define __ASM_TXX927_H
 
-#ifndef __ASSEMBLY__
-
 struct txx927_tmr_reg {
 	volatile unsigned long tcr;
 	volatile unsigned long tisr;
@@ -52,9 +50,6 @@
 	volatile unsigned long maskext;
 };
 
-#endif /* !__ASSEMBLY__ */
-
-
 /*
  * TMR
  */
diff --git a/include/asm-mips/paccess.h b/include/asm-mips/paccess.h
index 147844e..8c08fa9 100644
--- a/include/asm-mips/paccess.h
+++ b/include/asm-mips/paccess.h
@@ -34,7 +34,7 @@
 #define __get_dbe(x,ptr,size)						\
 ({									\
 	long __gu_err;							\
-	__typeof(*(ptr)) __gu_val;					\
+	__typeof__(*(ptr)) __gu_val;					\
 	unsigned long __gu_addr;					\
 	__asm__("":"=r" (__gu_val));					\
 	__gu_addr = (unsigned long) (ptr);				\
diff --git a/include/asm-mips/sgi/hpc3.h b/include/asm-mips/sgi/hpc3.h
index fcec52b..c4729f5 100644
--- a/include/asm-mips/sgi/hpc3.h
+++ b/include/asm-mips/sgi/hpc3.h
@@ -206,7 +206,7 @@
 #define HPC3_GIOMISC_ERTIME	0x1	/* Enable external timer real time. */
 #define HPC3_GIOMISC_DENDIAN	0x2	/* dma descriptor endian, 1=lit 0=big */
 
-	volatile u32 eeprom;		/* EEPROM data reg. */
+	u32 eeprom;			/* EEPROM data reg. */
 #define HPC3_EEPROM_EPROT	0x01	/* Protect register enable */
 #define HPC3_EEPROM_CSEL	0x02	/* Chip select */
 #define HPC3_EEPROM_ECLK	0x04	/* EEPROM clock */
diff --git a/include/asm-mips/sgi/ip22.h b/include/asm-mips/sgi/ip22.h
index 6592f3b..f4981c4 100644
--- a/include/asm-mips/sgi/ip22.h
+++ b/include/asm-mips/sgi/ip22.h
@@ -72,7 +72,7 @@
 
 #define ip22_is_fullhouse()	(sgioc->sysid & SGIOC_SYSID_FULLHOUSE)
 
-extern unsigned short ip22_eeprom_read(volatile unsigned int *ctrl, int reg);
+extern unsigned short ip22_eeprom_read(unsigned int *ctrl, int reg);
 extern unsigned short ip22_nvram_read(int reg);
 
 #endif
diff --git a/include/asm-mips/sgi/mc.h b/include/asm-mips/sgi/mc.h
index c52f783..1576c23 100644
--- a/include/asm-mips/sgi/mc.h
+++ b/include/asm-mips/sgi/mc.h
@@ -57,7 +57,7 @@
 	volatile u32 divider;	/* Divider reg for RPSS */
 
 	u32 _unused5;
-	volatile u32 eeprom;	/* EEPROM byte reg for r4k */
+	u32 eeprom;		/* EEPROM byte reg for r4k */
 #define SGIMC_EEPROM_PRE	0x00000001 /* eeprom chip PRE pin assertion */
 #define SGIMC_EEPROM_CSEL	0x00000002 /* Active high, eeprom chip select */
 #define SGIMC_EEPROM_SECLOCK	0x00000004 /* EEPROM serial clock */
diff --git a/include/asm-mips/sibyte/bcm1480_int.h b/include/asm-mips/sibyte/bcm1480_int.h
index 42d4cf0..c0d5206 100644
--- a/include/asm-mips/sibyte/bcm1480_int.h
+++ b/include/asm-mips/sibyte/bcm1480_int.h
@@ -157,6 +157,7 @@
  * Mask values for each interrupt
  */
 
+#define _BCM1480_INT_MASK(w,n)              _SB_MAKEMASK(w,((n) & 0x3F))
 #define _BCM1480_INT_MASK1(n)               _SB_MAKEMASK1(((n) & 0x3F))
 #define _BCM1480_INT_OFFSET(n)              (((n) & 0x40) << 6)
 
@@ -195,6 +196,7 @@
 #define M_BCM1480_INT_PMI_HIGH              _BCM1480_INT_MASK1(K_BCM1480_INT_PMI_HIGH)
 #define M_BCM1480_INT_PMO_LOW               _BCM1480_INT_MASK1(K_BCM1480_INT_PMO_LOW)
 #define M_BCM1480_INT_PMO_HIGH              _BCM1480_INT_MASK1(K_BCM1480_INT_PMO_HIGH)
+#define M_BCM1480_INT_MBOX_ALL              _BCM1480_INT_MASK(8,K_BCM1480_INT_MBOX_0_0)
 #define M_BCM1480_INT_MBOX_0_0              _BCM1480_INT_MASK1(K_BCM1480_INT_MBOX_0_0)
 #define M_BCM1480_INT_MBOX_0_1              _BCM1480_INT_MASK1(K_BCM1480_INT_MBOX_0_1)
 #define M_BCM1480_INT_MBOX_0_2              _BCM1480_INT_MASK1(K_BCM1480_INT_MBOX_0_2)
diff --git a/include/asm-mips/sibyte/bcm1480_mc.h b/include/asm-mips/sibyte/bcm1480_mc.h
index 6bdc941..a6a4374 100644
--- a/include/asm-mips/sibyte/bcm1480_mc.h
+++ b/include/asm-mips/sibyte/bcm1480_mc.h
@@ -382,6 +382,10 @@
 #define M_BCM1480_MC_CS6                    _SB_MAKEMASK1(10)
 #define M_BCM1480_MC_CS7                    _SB_MAKEMASK1(11)
 
+#define M_BCM1480_MC_CS                  _SB_MAKEMASK(8,S_BCM1480_MC_CS0)
+#define V_BCM1480_MC_CS(x)               _SB_MAKEVALUE(x,S_BCM1480_MC_CS0)
+#define G_BCM1480_MC_CS(x)               _SB_GETVALUE(x,S_BCM1480_MC_CS0,M_BCM1480_MC_CS0)
+
 #define M_BCM1480_MC_CMD_ACTIVE             _SB_MAKEMASK1(16)
 
 /*
@@ -412,6 +416,8 @@
 #define K_BCM1480_MC_DRAM_TYPE_DDR2	    2
 #endif
 
+#define K_BCM1480_MC_DRAM_TYPE_DDR2_PASS1   0
+
 #define V_BCM1480_MC_DRAM_TYPE_JEDEC        V_BCM1480_MC_DRAM_TYPE(K_BCM1480_MC_DRAM_TYPE_JEDEC)
 #define V_BCM1480_MC_DRAM_TYPE_FCRAM        V_BCM1480_MC_DRAM_TYPE(K_BCM1480_MC_DRAM_TYPE_FCRAM)
 
@@ -511,6 +517,22 @@
 #define M_BCM1480_MC_WR_ODT6_CS6	    _SB_MAKEMASK1(31)
 
 #define M_BCM1480_MC_CS_ODD_ODT_EN	    _SB_MAKEMASK1(32)
+
+#define S_BCM1480_MC_ODT0	            0
+#define M_BCM1480_MC_ODT0		    _SB_MAKEMASK(8,S_BCM1480_MC_ODT0)
+#define V_BCM1480_MC_ODT0(x)		    _SB_MAKEVALUE(x,S_BCM1480_MC_ODT0)
+
+#define S_BCM1480_MC_ODT2	            8
+#define M_BCM1480_MC_ODT2		    _SB_MAKEMASK(8,S_BCM1480_MC_ODT2)
+#define V_BCM1480_MC_ODT2(x)		    _SB_MAKEVALUE(x,S_BCM1480_MC_ODT2)
+
+#define S_BCM1480_MC_ODT4	            16
+#define M_BCM1480_MC_ODT4		    _SB_MAKEMASK(8,S_BCM1480_MC_ODT4)
+#define V_BCM1480_MC_ODT4(x)		    _SB_MAKEVALUE(x,S_BCM1480_MC_ODT4)
+
+#define S_BCM1480_MC_ODT6	            24
+#define M_BCM1480_MC_ODT6		    _SB_MAKEMASK(8,S_BCM1480_MC_ODT6)
+#define V_BCM1480_MC_ODT6(x)		    _SB_MAKEVALUE(x,S_BCM1480_MC_ODT6)
 #endif
 
 /*
@@ -588,11 +610,11 @@
 #define	M_BCM1480_MC_DQO_SHIFT            _SB_MAKEMASK1(47)
 #endif
 
-#define S_BCM1480_MC_DLL_DEFAULT            48
-#define M_BCM1480_MC_DLL_DEFAULT            _SB_MAKEMASK(6,S_BCM1480_MC_DLL_DEFAULT)
-#define V_BCM1480_MC_DLL_DEFAULT(x)         _SB_MAKEVALUE(x,S_BCM1480_MC_DLL_DEFAULT)
-#define G_BCM1480_MC_DLL_DEFAULT(x)         _SB_GETVALUE(x,S_BCM1480_MC_DLL_DEFAULT,M_BCM1480_MC_DLL_DEFAULT)
-#define V_BCM1480_MC_DLL_DEFAULT_DEFAULT    V_BCM1480_MC_DLL_DEFAULT(0x10)
+#define S_BCM1480_MC_DLL_DEFAULT           48
+#define M_BCM1480_MC_DLL_DEFAULT           _SB_MAKEMASK(6,S_BCM1480_MC_DLL_DEFAULT)
+#define V_BCM1480_MC_DLL_DEFAULT(x)        _SB_MAKEVALUE(x,S_BCM1480_MC_DLL_DEFAULT)
+#define G_BCM1480_MC_DLL_DEFAULT(x)        _SB_GETVALUE(x,S_BCM1480_MC_DLL_DEFAULT,M_BCM1480_MC_DLL_DEFAULT)
+#define V_BCM1480_MC_DLL_DEFAULT_DEFAULT   V_BCM1480_MC_DLL_DEFAULT(0x10)
 
 #if SIBYTE_HDR_FEATURE(1480, PASS2)
 #define S_BCM1480_MC_DLL_REGCTRL	  54
diff --git a/include/asm-mips/sibyte/bcm1480_regs.h b/include/asm-mips/sibyte/bcm1480_regs.h
index c2dd2fe..bda391d 100644
--- a/include/asm-mips/sibyte/bcm1480_regs.h
+++ b/include/asm-mips/sibyte/bcm1480_regs.h
@@ -230,6 +230,7 @@
 
 #define A_BCM1480_DUART_IMRREG(chan)	    (A_BCM1480_DUART(chan) + R_BCM1480_DUART_IMRREG(chan))
 #define A_BCM1480_DUART_ISRREG(chan)	    (A_BCM1480_DUART(chan) + R_BCM1480_DUART_ISRREG(chan))
+#define A_BCM1480_DUART_IN_PORT(chan)       (A_BCM1480_DUART(chan) + R_DUART_INP_ORT)
 
 /*
  * These constants are the absolute addresses.
@@ -404,6 +405,21 @@
 #define R_BCM1480_IMR_ALIAS_MAILBOX_0           0x0000		/* 0x0x0 */
 #define R_BCM1480_IMR_ALIAS_MAILBOX_0_SET       0x0008		/* 0x0x8 */
 
+/*
+ * these macros work together to build the address of a mailbox
+ * register, e.g., A_BCM1480_MAILBOX_REGISTER(0,R_BCM1480_IMR_MAILBOX_SET,2)
+ * for mbox_0_set_cpu2 returns 0x00100240C8
+ */
+#define R_BCM1480_IMR_MAILBOX_CPU         0x00
+#define R_BCM1480_IMR_MAILBOX_SET         0x08
+#define R_BCM1480_IMR_MAILBOX_CLR         0x10
+#define R_BCM1480_IMR_MAILBOX_NUM_SPACING 0x20
+#define A_BCM1480_MAILBOX_REGISTER(num,reg,cpu) \
+    (A_BCM1480_IMR_CPU0_BASE + \
+     (num * R_BCM1480_IMR_MAILBOX_NUM_SPACING) + \
+     (cpu * BCM1480_IMR_REGISTER_SPACING) + \
+     (R_BCM1480_IMR_MAILBOX_0_CPU + reg))
+
 /*  *********************************************************************
     * System Performance Counter Registers (Section 4.7)
     ********************************************************************* */
@@ -428,6 +444,10 @@
 #define A_BCM1480_SCD_PERF_CNT_6            0x0010020500
 #define A_BCM1480_SCD_PERF_CNT_7            0x0010020508
 
+#define BCM1480_SCD_NUM_PERF_CNT 8
+#define BCM1480_SCD_PERF_CNT_SPACING 8
+#define A_BCM1480_SCD_PERF_CNT(n) (A_SCD_PERF_CNT_0+(n*BCM1480_SCD_PERF_CNT_SPACING))
+
 /*  *********************************************************************
     * System Bus Watcher Registers (Section 4.8)
     ********************************************************************* */
diff --git a/include/asm-mips/sibyte/bcm1480_scd.h b/include/asm-mips/sibyte/bcm1480_scd.h
index 648bed9..6111d6d 100644
--- a/include/asm-mips/sibyte/bcm1480_scd.h
+++ b/include/asm-mips/sibyte/bcm1480_scd.h
@@ -10,7 +10,7 @@
     *
     *********************************************************************
     *
-    *  Copyright 2000,2001,2002,2003
+    *  Copyright 2000,2001,2002,2003,2004,2005
     *  Broadcom Corporation. All rights reserved.
     *
     *  This program is free software; you can redistribute it and/or
@@ -78,6 +78,7 @@
 #define K_SYS_PART_BCM1280          0x1206
 #define K_SYS_PART_BCM1455          0x1407
 #define K_SYS_PART_BCM1255          0x1257
+#define K_SYS_PART_BCM1158          0x1156
 
 /*
  * Manufacturing Information Register (Table 14)
@@ -237,58 +238,42 @@
  * System Performance Counter Configuration Register (Table 31)
  * Register: PERF_CNT_CFG_0
  *
- * Since the clear/enable bits are moved compared to the
- * 1250 and there are more fields, this register will be BCM1480 specific.
+ * SPC_CFG_SRC[0-3] is the same as the 1250.
+ * SPC_CFG_SRC[4-7] only exist on the 1480
+ * The clear/enable bits are in different locations on the 1250 and 1480.
  */
 
-#define S_BCM1480_SPC_CFG_SRC0              0
-#define M_BCM1480_SPC_CFG_SRC0              _SB_MAKEMASK(8,S_BCM1480_SPC_CFG_SRC0)
-#define V_BCM1480_SPC_CFG_SRC0(x)           _SB_MAKEVALUE(x,S_BCM1480_SPC_CFG_SRC0)
-#define G_BCM1480_SPC_CFG_SRC0(x)           _SB_GETVALUE(x,S_BCM1480_SPC_CFG_SRC0,M_BCM1480_SPC_CFG_SRC0)
+#define S_SPC_CFG_SRC4              32
+#define M_SPC_CFG_SRC4              _SB_MAKEMASK(8,S_SPC_CFG_SRC4)
+#define V_SPC_CFG_SRC4(x)           _SB_MAKEVALUE(x,S_SPC_CFG_SRC4)
+#define G_SPC_CFG_SRC4(x)           _SB_GETVALUE(x,S_SPC_CFG_SRC4,M_SPC_CFG_SRC4)
 
-#define S_BCM1480_SPC_CFG_SRC1              8
-#define M_BCM1480_SPC_CFG_SRC1              _SB_MAKEMASK(8,S_BCM1480_SPC_CFG_SRC1)
-#define V_BCM1480_SPC_CFG_SRC1(x)           _SB_MAKEVALUE(x,S_BCM1480_SPC_CFG_SRC1)
-#define G_BCM1480_SPC_CFG_SRC1(x)           _SB_GETVALUE(x,S_BCM1480_SPC_CFG_SRC1,M_BCM1480_SPC_CFG_SRC1)
+#define S_SPC_CFG_SRC5              40
+#define M_SPC_CFG_SRC5              _SB_MAKEMASK(8,S_SPC_CFG_SRC5)
+#define V_SPC_CFG_SRC5(x)           _SB_MAKEVALUE(x,S_SPC_CFG_SRC5)
+#define G_SPC_CFG_SRC5(x)           _SB_GETVALUE(x,S_SPC_CFG_SRC5,M_SPC_CFG_SRC5)
 
-#define S_BCM1480_SPC_CFG_SRC2              16
-#define M_BCM1480_SPC_CFG_SRC2              _SB_MAKEMASK(8,S_BCM1480_SPC_CFG_SRC2)
-#define V_BCM1480_SPC_CFG_SRC2(x)           _SB_MAKEVALUE(x,S_BCM1480_SPC_CFG_SRC2)
-#define G_BCM1480_SPC_CFG_SRC2(x)           _SB_GETVALUE(x,S_BCM1480_SPC_CFG_SRC2,M_BCM1480_SPC_CFG_SRC2)
+#define S_SPC_CFG_SRC6              48
+#define M_SPC_CFG_SRC6              _SB_MAKEMASK(8,S_SPC_CFG_SRC6)
+#define V_SPC_CFG_SRC6(x)           _SB_MAKEVALUE(x,S_SPC_CFG_SRC6)
+#define G_SPC_CFG_SRC6(x)           _SB_GETVALUE(x,S_SPC_CFG_SRC6,M_SPC_CFG_SRC6)
 
-#define S_BCM1480_SPC_CFG_SRC3              24
-#define M_BCM1480_SPC_CFG_SRC3              _SB_MAKEMASK(8,S_BCM1480_SPC_CFG_SRC3)
-#define V_BCM1480_SPC_CFG_SRC3(x)           _SB_MAKEVALUE(x,S_BCM1480_SPC_CFG_SRC3)
-#define G_BCM1480_SPC_CFG_SRC3(x)           _SB_GETVALUE(x,S_BCM1480_SPC_CFG_SRC3,M_BCM1480_SPC_CFG_SRC3)
-
-#define S_BCM1480_SPC_CFG_SRC4              32
-#define M_BCM1480_SPC_CFG_SRC4              _SB_MAKEMASK(8,S_BCM1480_SPC_CFG_SRC4)
-#define V_BCM1480_SPC_CFG_SRC4(x)           _SB_MAKEVALUE(x,S_BCM1480_SPC_CFG_SRC4)
-#define G_BCM1480_SPC_CFG_SRC4(x)           _SB_GETVALUE(x,S_BCM1480_SPC_CFG_SRC4,M_BCM1480_SPC_CFG_SRC4)
-
-#define S_BCM1480_SPC_CFG_SRC5              40
-#define M_BCM1480_SPC_CFG_SRC5              _SB_MAKEMASK(8,S_BCM1480_SPC_CFG_SRC5)
-#define V_BCM1480_SPC_CFG_SRC5(x)           _SB_MAKEVALUE(x,S_BCM1480_SPC_CFG_SRC5)
-#define G_BCM1480_SPC_CFG_SRC5(x)           _SB_GETVALUE(x,S_BCM1480_SPC_CFG_SRC5,M_BCM1480_SPC_CFG_SRC5)
-
-#define S_BCM1480_SPC_CFG_SRC6              48
-#define M_BCM1480_SPC_CFG_SRC6              _SB_MAKEMASK(8,S_BCM1480_SPC_CFG_SRC6)
-#define V_BCM1480_SPC_CFG_SRC6(x)           _SB_MAKEVALUE(x,S_BCM1480_SPC_CFG_SRC6)
-#define G_BCM1480_SPC_CFG_SRC6(x)           _SB_GETVALUE(x,S_BCM1480_SPC_CFG_SRC6,M_BCM1480_SPC_CFG_SRC6)
-
-#define S_BCM1480_SPC_CFG_SRC7              56
-#define M_BCM1480_SPC_CFG_SRC7              _SB_MAKEMASK(8,S_BCM1480_SPC_CFG_SRC7)
-#define V_BCM1480_SPC_CFG_SRC7(x)           _SB_MAKEVALUE(x,S_BCM1480_SPC_CFG_SRC7)
-#define G_BCM1480_SPC_CFG_SRC7(x)           _SB_GETVALUE(x,S_BCM1480_SPC_CFG_SRC7,M_BCM1480_SPC_CFG_SRC7)
+#define S_SPC_CFG_SRC7              56
+#define M_SPC_CFG_SRC7              _SB_MAKEMASK(8,S_SPC_CFG_SRC7)
+#define V_SPC_CFG_SRC7(x)           _SB_MAKEVALUE(x,S_SPC_CFG_SRC7)
+#define G_SPC_CFG_SRC7(x)           _SB_GETVALUE(x,S_SPC_CFG_SRC7,M_SPC_CFG_SRC7)
 
 /*
  * System Performance Counter Control Register (Table 32)
  * Register: PERF_CNT_CFG_1
  * BCM1480 specific
  */
-
-#define M_BCM1480_SPC_CFG_CLEAR             _SB_MAKEMASK1(0)
-#define M_BCM1480_SPC_CFG_ENABLE            _SB_MAKEMASK1(1)
+#define M_BCM1480_SPC_CFG_CLEAR     _SB_MAKEMASK1(0)
+#define M_BCM1480_SPC_CFG_ENABLE    _SB_MAKEMASK1(1)
+#if SIBYTE_HDR_FEATURE_CHIP(1480)
+#define M_SPC_CFG_CLEAR			M_BCM1480_SPC_CFG_CLEAR
+#define M_SPC_CFG_ENABLE		M_BCM1480_SPC_CFG_ENABLE
+#endif
 
 /*
  * System Performance Counters (Table 33)
@@ -405,20 +390,10 @@
  * Trace Control Register (Table 49)
  * Register: TRACE_CFG
  *
- * Bits 0..8 are the same as the BCM1250, rest are different.
- * Entire register is redefined below.
+ * BCM1480 changes to this register (other than location of the CUR_ADDR field)
+ * are defined below.
  */
 
-#define M_BCM1480_SCD_TRACE_CFG_RESET       _SB_MAKEMASK1(0)
-#define M_BCM1480_SCD_TRACE_CFG_START_READ  _SB_MAKEMASK1(1)
-#define M_BCM1480_SCD_TRACE_CFG_START       _SB_MAKEMASK1(2)
-#define M_BCM1480_SCD_TRACE_CFG_STOP        _SB_MAKEMASK1(3)
-#define M_BCM1480_SCD_TRACE_CFG_FREEZE      _SB_MAKEMASK1(4)
-#define M_BCM1480_SCD_TRACE_CFG_FREEZE_FULL _SB_MAKEMASK1(5)
-#define M_BCM1480_SCD_TRACE_CFG_DEBUG_FULL  _SB_MAKEMASK1(6)
-#define M_BCM1480_SCD_TRACE_CFG_FULL        _SB_MAKEMASK1(7)
-#define M_BCM1480_SCD_TRACE_CFG_FORCE_CNT   _SB_MAKEMASK1(8)
-
 #define S_BCM1480_SCD_TRACE_CFG_MODE        16
 #define M_BCM1480_SCD_TRACE_CFG_MODE        _SB_MAKEMASK(2,S_BCM1480_SCD_TRACE_CFG_MODE)
 #define V_BCM1480_SCD_TRACE_CFG_MODE(x)     _SB_MAKEVALUE(x,S_BCM1480_SCD_TRACE_CFG_MODE)
@@ -428,9 +403,4 @@
 #define K_BCM1480_SCD_TRACE_CFG_MODE_BYTEEN_INT	1
 #define K_BCM1480_SCD_TRACE_CFG_MODE_FLOW_ID	2
 
-#define S_BCM1480_SCD_TRACE_CFG_CUR_ADDR    24
-#define M_BCM1480_SCD_TRACE_CFG_CUR_ADDR    _SB_MAKEMASK(8,S_BCM1480_SCD_TRACE_CFG_CUR_ADDR)
-#define V_BCM1480_SCD_TRACE_CFG_CUR_ADDR(x) _SB_MAKEVALUE(x,S_BCM1480_SCD_TRACE_CFG_CUR_ADDR)
-#define G_BCM1480_SCD_TRACE_CFG_CUR_ADDR(x) _SB_GETVALUE(x,S_BCM1480_SCD_TRACE_CFG_CUR_ADDR,M_BCM1480_SCD_TRACE_CFG_CUR_ADDR)
-
 #endif /* _BCM1480_SCD_H */
diff --git a/include/asm-mips/sibyte/board.h b/include/asm-mips/sibyte/board.h
index 3dfe29e..73bce90 100644
--- a/include/asm-mips/sibyte/board.h
+++ b/include/asm-mips/sibyte/board.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2000, 2001, 2002, 2003 Broadcom Corporation
+ * Copyright (C) 2000,2001,2002,2003,2004 Broadcom Corporation
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -19,8 +19,8 @@
 #ifndef _SIBYTE_BOARD_H
 #define _SIBYTE_BOARD_H
 
-
 #if defined(CONFIG_SIBYTE_SWARM) || defined(CONFIG_SIBYTE_PTSWARM) || \
+    defined(CONFIG_SIBYTE_PT1120) || defined(CONFIG_SIBYTE_PT1125) || \
     defined(CONFIG_SIBYTE_CRHONE) || defined(CONFIG_SIBYTE_CRHINE) || \
     defined(CONFIG_SIBYTE_LITTLESUR)
 #include <asm/sibyte/swarm.h>
@@ -55,6 +55,16 @@
 #define setleds(t0,t1,c0,c1,c2,c3)
 #endif /* LEDS_PHYS */
 
+#else
+
+void swarm_setup(void);
+
+#ifdef LEDS_PHYS
+extern void setleds(char *str);
+#else
+#define setleds(s) do { } while (0)
+#endif /* LEDS_PHYS */
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* _SIBYTE_BOARD_H */
diff --git a/include/asm-mips/sibyte/carmel.h b/include/asm-mips/sibyte/carmel.h
index 57c53e6..11cad71 100644
--- a/include/asm-mips/sibyte/carmel.h
+++ b/include/asm-mips/sibyte/carmel.h
@@ -18,7 +18,6 @@
 #ifndef __ASM_SIBYTE_CARMEL_H
 #define __ASM_SIBYTE_CARMEL_H
 
-
 #include <asm/sibyte/sb1250.h>
 #include <asm/sibyte/sb1250_int.h>
 
diff --git a/include/asm-mips/sibyte/sb1250_int.h b/include/asm-mips/sibyte/sb1250_int.h
index 05c7b39..94e8299 100644
--- a/include/asm-mips/sibyte/sb1250_int.h
+++ b/include/asm-mips/sibyte/sb1250_int.h
@@ -45,8 +45,6 @@
  * First, the interrupt numbers.
  */
 
-#if SIBYTE_HDR_FEATURE_1250_112x
-
 #define K_INT_SOURCES               64
 
 #define K_INT_WATCHDOG_TIMER_0      0
@@ -152,6 +150,7 @@
 #define M_INT_MBOX_1                _SB_MAKEMASK1(K_INT_MBOX_1)
 #define M_INT_MBOX_2                _SB_MAKEMASK1(K_INT_MBOX_2)
 #define M_INT_MBOX_3                _SB_MAKEMASK1(K_INT_MBOX_3)
+#define M_INT_MBOX_ALL              _SB_MAKEMASK(4,K_INT_MBOX_0)
 #if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1)
 #define M_INT_CYCLE_CP0_INT	    _SB_MAKEMASK1(K_INT_CYCLE_CP0_INT)
 #define M_INT_CYCLE_CP1_INT	    _SB_MAKEMASK1(K_INT_CYCLE_CP1_INT)
@@ -247,5 +246,3 @@
 
 
 #endif	/* 1250/112x */
-
-#endif
diff --git a/include/asm-mips/sibyte/sb1250_mac.h b/include/asm-mips/sibyte/sb1250_mac.h
index adfc688..833c8b5 100644
--- a/include/asm-mips/sibyte/sb1250_mac.h
+++ b/include/asm-mips/sibyte/sb1250_mac.h
@@ -129,9 +129,9 @@
 #define M_MAC_BYPASS_16             _SB_MAKEMASK1(42)
 #define M_MAC_BYPASS_FCS_CHK	    _SB_MAKEMASK1(43)
 
-#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1)
+#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480)
 #define M_MAC_RX_CH_SEL_MSB	    _SB_MAKEMASK1(44)
-#endif /* 1250 PASS2 || 112x PASS1 */
+#endif /* 1250 PASS2 || 112x PASS1 || 1480*/
 
 #if SIBYTE_HDR_FEATURE(1250, PASS3) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480)
 #define M_MAC_SPLIT_CH_SEL	    _SB_MAKEMASK1(45)
@@ -223,9 +223,9 @@
 /* XXX: Can't enable, as it has the same name as a pass2+ define below.  */
 /* #define M_MAC_TX_WR_THRSH           _SB_MAKEMASK(6,S_MAC_TX_WR_THRSH) */
 #endif /* up to 1250 PASS1 */
-#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1)
+#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480)
 #define M_MAC_TX_WR_THRSH           _SB_MAKEMASK(7,S_MAC_TX_WR_THRSH)
-#endif /* 1250 PASS2 || 112x PASS1 */
+#endif /* 1250 PASS2 || 112x PASS1 || 1480 */
 #define V_MAC_TX_WR_THRSH(x)        _SB_MAKEVALUE(x,S_MAC_TX_WR_THRSH)
 #define G_MAC_TX_WR_THRSH(x)        _SB_GETVALUE(x,S_MAC_TX_WR_THRSH,M_MAC_TX_WR_THRSH)
 
@@ -234,9 +234,9 @@
 /* XXX: Can't enable, as it has the same name as a pass2+ define below.  */
 /* #define M_MAC_TX_RD_THRSH           _SB_MAKEMASK(6,S_MAC_TX_RD_THRSH) */
 #endif /* up to 1250 PASS1 */
-#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1)
+#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480)
 #define M_MAC_TX_RD_THRSH           _SB_MAKEMASK(7,S_MAC_TX_RD_THRSH)
-#endif /* 1250 PASS2 || 112x PASS1 */
+#endif /* 1250 PASS2 || 112x PASS1 || 1480 */
 #define V_MAC_TX_RD_THRSH(x)        _SB_MAKEVALUE(x,S_MAC_TX_RD_THRSH)
 #define G_MAC_TX_RD_THRSH(x)        _SB_GETVALUE(x,S_MAC_TX_RD_THRSH,M_MAC_TX_RD_THRSH)
 
@@ -260,12 +260,12 @@
 #define V_MAC_RX_RL_THRSH(x)        _SB_MAKEVALUE(x,S_MAC_RX_RL_THRSH)
 #define G_MAC_RX_RL_THRSH(x)        _SB_GETVALUE(x,S_MAC_RX_RL_THRSH,M_MAC_RX_RL_THRSH)
 
-#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1)
+#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480)
 #define S_MAC_ENC_FC_THRSH           _SB_MAKE64(56)
 #define M_MAC_ENC_FC_THRSH           _SB_MAKEMASK(6,S_MAC_ENC_FC_THRSH)
 #define V_MAC_ENC_FC_THRSH(x)        _SB_MAKEVALUE(x,S_MAC_ENC_FC_THRSH)
 #define G_MAC_ENC_FC_THRSH(x)        _SB_GETVALUE(x,S_MAC_ENC_FC_THRSH,M_MAC_ENC_FC_THRSH)
-#endif /* 1250 PASS2 || 112x PASS1 */
+#endif /* 1250 PASS2 || 112x PASS1 || 1480 */
 
 /*
  * MAC Frame Configuration Registers (Table 9-15)
@@ -462,9 +462,9 @@
 #define M_MAC_LTCOL_ERR             _SB_MAKEMASK1(44)
 #define M_MAC_EXCOL_ERR             _SB_MAKEMASK1(45)
 #define M_MAC_CNTR_OVRFL_ERR        _SB_MAKEMASK1(46)
-#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1)
+#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480)
 #define M_MAC_SPLIT_EN		    _SB_MAKEMASK1(47) 	/* interrupt mask only */
-#endif /* 1250 PASS2 || 112x PASS1 */
+#endif /* 1250 PASS2 || 112x PASS1 || 1480 */
 
 #define S_MAC_COUNTER_ADDR          _SB_MAKE64(47)
 #define M_MAC_COUNTER_ADDR          _SB_MAKEMASK(5,S_MAC_COUNTER_ADDR)
@@ -598,9 +598,9 @@
 #define M_MAC_MCAST_INV         _SB_MAKEMASK1(4)
 #define M_MAC_BCAST_EN          _SB_MAKEMASK1(5)
 #define M_MAC_DIRECT_INV        _SB_MAKEMASK1(6)
-#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1)
+#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480)
 #define M_MAC_ALLMCAST_EN	_SB_MAKEMASK1(7)
-#endif /* 1250 PASS2 || 112x PASS1 */
+#endif /* 1250 PASS2 || 112x PASS1 || 1480 */
 
 #define S_MAC_IPHDR_OFFSET      _SB_MAKE64(8)
 #define M_MAC_IPHDR_OFFSET      _SB_MAKEMASK(8,S_MAC_IPHDR_OFFSET)
diff --git a/include/asm-mips/sibyte/sb1250_mc.h b/include/asm-mips/sibyte/sb1250_mc.h
index 26e42149..4fe848f 100644
--- a/include/asm-mips/sibyte/sb1250_mc.h
+++ b/include/asm-mips/sibyte/sb1250_mc.h
@@ -295,7 +295,7 @@
 
 #if SIBYTE_HDR_FEATURE(1250, PASS3) || SIBYTE_HDR_FEATURE(112x, PASS1)
 #define M_MC_PRE_ON_A8              _SB_MAKEMASK1(36)
-#define M_MC_RAM_WITH_A13           _SB_MAKEMASK1(38)
+#define M_MC_RAM_WITH_A13           _SB_MAKEMASK1(37)
 #endif /* 1250 PASS3 || 112x PASS1 */
 
 
diff --git a/include/asm-mips/sibyte/sb1250_regs.h b/include/asm-mips/sibyte/sb1250_regs.h
index bab3a45..da7c188 100644
--- a/include/asm-mips/sibyte/sb1250_regs.h
+++ b/include/asm-mips/sibyte/sb1250_regs.h
@@ -131,6 +131,7 @@
 
 #endif
 
+
 /*  *********************************************************************
     * PCI Interface Registers
     ********************************************************************* */
@@ -239,14 +240,14 @@
 #define R_MAC_VLANTAG                   0x00000110
 #define R_MAC_FRAMECFG                  0x00000118
 #define R_MAC_EOPCNT                    0x00000120
-#define R_MAC_FIFO_PTRS                 0x00000130
+#define R_MAC_FIFO_PTRS                 0x00000128
 #define R_MAC_ADFILTER_CFG              0x00000200
 #define R_MAC_ETHERNET_ADDR             0x00000208
 #define R_MAC_PKT_TYPE                  0x00000210
-#if SIBYTE_HDR_FEATURE(1250, PASS3) || SIBYTE_HDR_FEATURE(112x, PASS1)
+#if SIBYTE_HDR_FEATURE(1250, PASS3) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480)
 #define R_MAC_ADMASK0			0x00000218
 #define R_MAC_ADMASK1			0x00000220
-#endif /* 1250 PASS3 || 112x PASS1 */
+#endif /* 1250 PASS3 || 112x PASS1 || 1480 */
 #define R_MAC_HASH_BASE                 0x00000240
 #define R_MAC_ADDR_BASE                 0x00000280
 #define R_MAC_CHLO0_BASE                0x00000300
@@ -256,9 +257,9 @@
 #define R_MAC_INT_MASK                  0x00000410
 #define R_MAC_TXD_CTL                   0x00000420
 #define R_MAC_MDIO                      0x00000428
-#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1)
+#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480)
 #define R_MAC_STATUS1		        0x00000430
-#endif /* 1250 PASS2 || 112x PASS1 */
+#endif /* 1250 PASS2 || 112x PASS1 || 1480 */
 #define R_MAC_DEBUG_STATUS              0x00000448
 
 #define MAC_HASH_COUNT			8
@@ -289,11 +290,11 @@
 #define R_DUART_RX_HOLD             0x160
 #define R_DUART_TX_HOLD             0x170
 
-#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1)
+#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480)
 #define R_DUART_FULL_CTL	    0x140
 #define R_DUART_OPCR_X		    0x180
 #define R_DUART_AUXCTL_X	    0x190
-#endif /* 1250 PASS2 || 112x PASS1 */
+#endif /* 1250 PASS2 || 112x PASS1 || 1480*/
 
 
 /*
@@ -308,6 +309,7 @@
 #define R_DUART_IMR_B               0x350
 #define R_DUART_OUT_PORT            0x360
 #define R_DUART_OPCR                0x370
+#define R_DUART_IN_PORT             0x380
 
 #define R_DUART_SET_OPR		    0x3B0
 #define R_DUART_CLEAR_OPR	    0x3C0
@@ -685,12 +687,17 @@
 #define A_ADDR_TRAP_REG_DEBUG	    0x0010020460
 #endif /* 1250 PASS2 || 112x PASS1 || 1480 */
 
+#define ADDR_TRAP_SPACING 8
+#define NUM_ADDR_TRAP 4
+#define A_ADDR_TRAP_UP(n) (A_ADDR_TRAP_UP_0 + ((n) * ADDR_TRAP_SPACING))
+#define A_ADDR_TRAP_DOWN(n) (A_ADDR_TRAP_DOWN_0 + ((n) * ADDR_TRAP_SPACING))
+#define A_ADDR_TRAP_CFG(n) (A_ADDR_TRAP_CFG_0 + ((n) * ADDR_TRAP_SPACING))
+
 
 /*  *********************************************************************
     * System Interrupt Mapper Registers
     ********************************************************************* */
 
-#if SIBYTE_HDR_FEATURE_1250_112x
 #define A_IMR_CPU0_BASE                 0x0010020000
 #define A_IMR_CPU1_BASE                 0x0010022000
 #define IMR_REGISTER_SPACING            0x2000
@@ -700,6 +707,7 @@
 #define A_IMR_REGISTER(cpu,reg) (A_IMR_MAPPER(cpu)+(reg))
 
 #define R_IMR_INTERRUPT_DIAG            0x0010
+#define R_IMR_INTERRUPT_LDT             0x0018
 #define R_IMR_INTERRUPT_MASK            0x0028
 #define R_IMR_INTERRUPT_TRACE           0x0038
 #define R_IMR_INTERRUPT_SOURCE_STATUS   0x0040
@@ -715,7 +723,14 @@
 #define R_IMR_INTERRUPT_STATUS_COUNT    7
 #define R_IMR_INTERRUPT_MAP_BASE        0x0200
 #define R_IMR_INTERRUPT_MAP_COUNT       64
-#endif	/* 1250/112x */
+
+/*
+ * these macros work together to build the address of a mailbox
+ * register, e.g., A_MAILBOX_REGISTER(R_IMR_MAILBOX_SET_CPU,1)
+ * for mbox_0_set_cpu2 returns 0x00100240C8
+ */
+#define A_MAILBOX_REGISTER(reg,cpu) \
+    (A_IMR_CPU0_BASE + (cpu * IMR_REGISTER_SPACING) + reg)
 
 /*  *********************************************************************
     * System Performance Counter Registers
@@ -727,6 +742,10 @@
 #define A_SCD_PERF_CNT_2            0x00100204E0
 #define A_SCD_PERF_CNT_3            0x00100204E8
 
+#define SCD_NUM_PERF_CNT 4
+#define SCD_PERF_CNT_SPACING 8
+#define A_SCD_PERF_CNT(n) (A_SCD_PERF_CNT_0+(n*SCD_PERF_CNT_SPACING))
+
 /*  *********************************************************************
     * System Bus Watcher Registers
     ********************************************************************* */
@@ -772,6 +791,15 @@
 #define A_SCD_TRACE_SEQUENCE_6      0x0010020A90
 #define A_SCD_TRACE_SEQUENCE_7      0x0010020A98
 
+#define TRACE_REGISTER_SPACING 8
+#define TRACE_NUM_REGISTERS    8
+#define A_SCD_TRACE_EVENT(n) (((n) & 4) ? \
+   (A_SCD_TRACE_EVENT_4 + (((n) & 3) * TRACE_REGISTER_SPACING)) : \
+   (A_SCD_TRACE_EVENT_0 + ((n) * TRACE_REGISTER_SPACING)))
+#define A_SCD_TRACE_SEQUENCE(n) (((n) & 4) ? \
+   (A_SCD_TRACE_SEQUENCE_4 + (((n) & 3) * TRACE_REGISTER_SPACING)) : \
+   (A_SCD_TRACE_SEQUENCE_0 + ((n) * TRACE_REGISTER_SPACING)))
+
 /*  *********************************************************************
     * System Generic DMA Registers
     ********************************************************************* */
diff --git a/include/asm-mips/sibyte/sb1250_scd.h b/include/asm-mips/sibyte/sb1250_scd.h
index b6a7d8f..9ea3da3 100644
--- a/include/asm-mips/sibyte/sb1250_scd.h
+++ b/include/asm-mips/sibyte/sb1250_scd.h
@@ -10,7 +10,7 @@
     *
     *********************************************************************
     *
-    *  Copyright 2000,2001,2002,2003
+    *  Copyright 2000,2001,2002,2003,2004,2005
     *  Broadcom Corporation. All rights reserved.
     *
     *  This program is free software; you can redistribute it and/or
@@ -150,7 +150,7 @@
  * (For the assembler version, sysrev and dest may be the same register.
  * Also, it clobbers AT.)
  */
-#ifdef __ASSEMBLY__
+#ifdef __ASSEMBLER__
 #define SYS_SOC_TYPE(dest, sysrev)					\
 	.set push ;							\
 	.set reorder ;							\
@@ -214,6 +214,7 @@
 #define G_SYS_YPOS(x)             _SB_GETVALUE(x,S_SYS_YPOS,M_SYS_YPOS)
 #endif
 
+
 /*
  * System Config Register (Table 4-2)
  * Register: SCD_SYSTEM_CFG
@@ -360,13 +361,13 @@
  */
 
 #define V_SCD_TIMER_FREQ            1000000
-#define V_SCD_TIMER_WIDTH           23
 
 #define S_SCD_TIMER_INIT            0
-#define M_SCD_TIMER_INIT            _SB_MAKEMASK(V_SCD_TIMER_WIDTH,S_SCD_TIMER_INIT)
+#define M_SCD_TIMER_INIT            _SB_MAKEMASK(23,S_SCD_TIMER_INIT)
 #define V_SCD_TIMER_INIT(x)         _SB_MAKEVALUE(x,S_SCD_TIMER_INIT)
 #define G_SCD_TIMER_INIT(x)         _SB_GETVALUE(x,S_SCD_TIMER_INIT,M_SCD_TIMER_INIT)
 
+#define V_SCD_TIMER_WIDTH	    23
 #define S_SCD_TIMER_CNT             0
 #define M_SCD_TIMER_CNT             _SB_MAKEMASK(V_SCD_TIMER_WIDTH,S_SCD_TIMER_CNT)
 #define V_SCD_TIMER_CNT(x)         _SB_MAKEVALUE(x,S_SCD_TIMER_CNT)
@@ -380,7 +381,6 @@
  * System Performance Counters
  */
 
-#if SIBYTE_HDR_FEATURE_1250_112x
 #define S_SPC_CFG_SRC0            0
 #define M_SPC_CFG_SRC0            _SB_MAKEMASK(8,S_SPC_CFG_SRC0)
 #define V_SPC_CFG_SRC0(x)         _SB_MAKEVALUE(x,S_SPC_CFG_SRC0)
@@ -401,6 +401,7 @@
 #define V_SPC_CFG_SRC3(x)         _SB_MAKEVALUE(x,S_SPC_CFG_SRC3)
 #define G_SPC_CFG_SRC3(x)         _SB_GETVALUE(x,S_SPC_CFG_SRC3,M_SPC_CFG_SRC3)
 
+#if SIBYTE_HDR_FEATURE_1250_112x
 #define M_SPC_CFG_CLEAR		_SB_MAKEMASK1(32)
 #define M_SPC_CFG_ENABLE	_SB_MAKEMASK1(33)
 #endif
@@ -516,8 +517,6 @@
  * Trace Buffer Config register
  */
 
-#if SIBYTE_HDR_FEATURE_1250_112x
-
 #define M_SCD_TRACE_CFG_RESET           _SB_MAKEMASK1(0)
 #define M_SCD_TRACE_CFG_START_READ      _SB_MAKEMASK1(1)
 #define M_SCD_TRACE_CFG_START           _SB_MAKEMASK1(2)
@@ -526,17 +525,26 @@
 #define M_SCD_TRACE_CFG_FREEZE_FULL     _SB_MAKEMASK1(5)
 #define M_SCD_TRACE_CFG_DEBUG_FULL      _SB_MAKEMASK1(6)
 #define M_SCD_TRACE_CFG_FULL            _SB_MAKEMASK1(7)
-#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1)
+#if SIBYTE_HDR_FEATURE(1250, PASS2) || SIBYTE_HDR_FEATURE(112x, PASS1) || SIBYTE_HDR_FEATURE_CHIP(1480)
 #define M_SCD_TRACE_CFG_FORCECNT        _SB_MAKEMASK1(8)
-#endif /* 1250 PASS2 || 112x PASS1 */
+#endif /* 1250 PASS2 || 112x PASS1 || 1480 */
 
+/*
+ * This field is the same on the 1250/112x and 1480, just located in
+ * a slightly different place in the register.
+ */
+#if SIBYTE_HDR_FEATURE_1250_112x
 #define S_SCD_TRACE_CFG_CUR_ADDR        10
+#else
+#if SIBYTE_HDR_FEATURE_CHIP(1480)
+#define S_SCD_TRACE_CFG_CUR_ADDR        24
+#endif	/* 1480 */
+#endif  /* 1250/112x */
+
 #define M_SCD_TRACE_CFG_CUR_ADDR        _SB_MAKEMASK(8,S_SCD_TRACE_CFG_CUR_ADDR)
 #define V_SCD_TRACE_CFG_CUR_ADDR(x)     _SB_MAKEVALUE(x,S_SCD_TRACE_CFG_CUR_ADDR)
 #define G_SCD_TRACE_CFG_CUR_ADDR(x)     _SB_GETVALUE(x,S_SCD_TRACE_CFG_CUR_ADDR,M_SCD_TRACE_CFG_CUR_ADDR)
 
-#endif	/* 1250/112x */
-
 /*
  * Trace Event registers
  */
diff --git a/include/asm-mips/sibyte/swarm.h b/include/asm-mips/sibyte/swarm.h
index 86db37e..540865f 100644
--- a/include/asm-mips/sibyte/swarm.h
+++ b/include/asm-mips/sibyte/swarm.h
@@ -32,6 +32,18 @@
 #define SIBYTE_HAVE_IDE    1
 #define SIBYTE_DEFAULT_CONSOLE "ttyS0,115200"
 #endif
+#ifdef CONFIG_SIBYTE_PT1120
+#define SIBYTE_BOARD_NAME "PT1120"
+#define SIBYTE_HAVE_PCMCIA 1
+#define SIBYTE_HAVE_IDE    1
+#define SIBYTE_DEFAULT_CONSOLE "ttyS0,115200"
+#endif
+#ifdef CONFIG_SIBYTE_PT1125
+#define SIBYTE_BOARD_NAME "PT1125"
+#define SIBYTE_HAVE_PCMCIA 1
+#define SIBYTE_HAVE_IDE    1
+#define SIBYTE_DEFAULT_CONSOLE "ttyS0,115200"
+#endif
 #ifdef CONFIG_SIBYTE_LITTLESUR
 #define SIBYTE_BOARD_NAME "BCM91250C2 (LittleSur)"
 #define SIBYTE_HAVE_PCMCIA 0
diff --git a/include/asm-mips/socket.h b/include/asm-mips/socket.h
index 36ebe4e..9594568 100644
--- a/include/asm-mips/socket.h
+++ b/include/asm-mips/socket.h
@@ -70,6 +70,8 @@
 #define SO_SNDBUFFORCE		31
 #define SO_RCVBUFFORCE		33
 #define SO_PASSSEC		34
+#define SO_TIMESTAMPNS		35
+#define SCM_TIMESTAMPNS		SO_TIMESTAMPNS
 
 #ifdef __KERNEL__
 
diff --git a/include/asm-mips/sockios.h b/include/asm-mips/sockios.h
index 87a50bf..ed1a5f7 100644
--- a/include/asm-mips/sockios.h
+++ b/include/asm-mips/sockios.h
@@ -20,6 +20,7 @@
 #define SIOCSPGRP	_IOW('s', 8, pid_t)
 #define SIOCGPGRP	_IOR('s', 9, pid_t)
 
-#define SIOCGSTAMP	0x8906			/* Get stamp - linux-specific */
+#define SIOCGSTAMP	0x8906		/* Get stamp (timeval) */
+#define SIOCGSTAMPNS	0x8907		/* Get stamp (timespec) */
 
 #endif /* _ASM_SOCKIOS_H */
diff --git a/include/asm-parisc/socket.h b/include/asm-parisc/socket.h
index ce2eae1..99e868f 100644
--- a/include/asm-parisc/socket.h
+++ b/include/asm-parisc/socket.h
@@ -33,6 +33,8 @@
 #define SO_PEERCRED	0x4011
 #define SO_TIMESTAMP	0x4012
 #define SCM_TIMESTAMP	SO_TIMESTAMP
+#define SO_TIMESTAMPNS	0x4013
+#define SCM_TIMESTAMPNS	SO_TIMESTAMPNS
 
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_SECURITY_AUTHENTICATION		0x4016
diff --git a/include/asm-parisc/sockios.h b/include/asm-parisc/sockios.h
index aace496..dabfbc7 100644
--- a/include/asm-parisc/sockios.h
+++ b/include/asm-parisc/sockios.h
@@ -7,6 +7,7 @@
 #define FIOGETOWN	0x8903
 #define SIOCGPGRP	0x8904
 #define SIOCATMARK	0x8905
-#define SIOCGSTAMP	0x8906		/* Get stamp */
+#define SIOCGSTAMP	0x8906		/* Get stamp (timeval) */
+#define SIOCGSTAMPNS	0x8907		/* Get stamp (timespec) */
 
 #endif
diff --git a/include/asm-powerpc/asm-compat.h b/include/asm-powerpc/asm-compat.h
index c89bd58..c19e736 100644
--- a/include/asm-powerpc/asm-compat.h
+++ b/include/asm-powerpc/asm-compat.h
@@ -78,6 +78,15 @@
 #define PPC_STLCX	stringify_in_c(stdcx.)
 #define PPC_CNTLZL	stringify_in_c(cntlzd)
 
+/* Move to CR, single-entry optimized version. Only available
+ * on POWER4 and later.
+ */
+#ifdef CONFIG_POWER4_ONLY
+#define PPC_MTOCRF	stringify_in_c(mtocrf)
+#else
+#define PPC_MTOCRF	stringify_in_c(mtcrf)
+#endif
+
 #else /* 32-bit */
 
 /* operations for longs and pointers */
@@ -89,6 +98,7 @@
 #define PPC_LLARX	stringify_in_c(lwarx)
 #define PPC_STLCX	stringify_in_c(stwcx.)
 #define PPC_CNTLZL	stringify_in_c(cntlzw)
+#define PPC_MTOCRF	stringify_in_c(mtcrf)
 
 #endif
 
diff --git a/include/asm-powerpc/cacheflush.h b/include/asm-powerpc/cacheflush.h
index 08e93e7..ba667a383 100644
--- a/include/asm-powerpc/cacheflush.h
+++ b/include/asm-powerpc/cacheflush.h
@@ -64,6 +64,12 @@
 	memcpy(dst, src, len)
 
 
+
+#ifdef CONFIG_DEBUG_PAGEALLOC
+/* internal debugging function */
+void kernel_map_pages(struct page *page, int numpages, int enable);
+#endif
+
 #endif /* __KERNEL__ */
 
 #endif /* _ASM_POWERPC_CACHEFLUSH_H */
diff --git a/include/asm-powerpc/cell-pmu.h b/include/asm-powerpc/cell-pmu.h
index 35b9577..8066eed 100644
--- a/include/asm-powerpc/cell-pmu.h
+++ b/include/asm-powerpc/cell-pmu.h
@@ -97,11 +97,6 @@
 extern u32  cbe_get_and_clear_pm_interrupts(u32 cpu);
 extern void cbe_sync_irq(int node);
 
-/* Utility functions, macros */
-extern u32 cbe_get_hw_thread_id(int cpu);
-
-#define cbe_cpu_to_node(cpu) ((cpu) >> 1)
-
 #define CBE_COUNT_SUPERVISOR_MODE       0
 #define CBE_COUNT_HYPERVISOR_MODE       1
 #define CBE_COUNT_PROBLEM_MODE          2
diff --git a/include/asm-powerpc/cputable.h b/include/asm-powerpc/cputable.h
index e870b53..4345249 100644
--- a/include/asm-powerpc/cputable.h
+++ b/include/asm-powerpc/cputable.h
@@ -48,6 +48,7 @@
 	PPC_OPROFILE_G4 = 3,
 	PPC_OPROFILE_BOOKE = 4,
 	PPC_OPROFILE_CELL = 5,
+	PPC_OPROFILE_PA6T = 6,
 };
 
 enum powerpc_pmc_type {
@@ -223,6 +224,10 @@
 	    CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \
 	    CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \
 	    CPU_FTR_PPC_LE)
+#define CPU_FTRS_750CL	(CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \
+	    CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \
+	    CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \
+	    CPU_FTR_HAS_HIGH_BATS | CPU_FTR_PPC_LE)
 #define CPU_FTRS_750FX1	(CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \
 	    CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \
 	    CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \
@@ -235,9 +240,9 @@
 	    CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \
 	    CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \
 	    CPU_FTR_DUAL_PLL_750FX | CPU_FTR_HAS_HIGH_BATS | CPU_FTR_PPC_LE)
-#define CPU_FTRS_750GX	(CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | \
-	    CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | \
-	    CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \
+#define CPU_FTRS_750GX	(CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \
+	    CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \
+	    CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \
 	    CPU_FTR_DUAL_PLL_750FX | CPU_FTR_HAS_HIGH_BATS | CPU_FTR_PPC_LE)
 #define CPU_FTRS_7400_NOTAU	(CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \
 	    CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \
diff --git a/include/asm-powerpc/current.h b/include/asm-powerpc/current.h
index b8708ae..e2c7f06 100644
--- a/include/asm-powerpc/current.h
+++ b/include/asm-powerpc/current.h
@@ -12,6 +12,7 @@
 struct task_struct;
 
 #ifdef __powerpc64__
+#include <linux/stddef.h>
 #include <asm/paca.h>
 
 static inline struct task_struct *get_current(void)
diff --git a/include/asm-powerpc/edac.h b/include/asm-powerpc/edac.h
new file mode 100644
index 0000000..6ead88b
--- /dev/null
+++ b/include/asm-powerpc/edac.h
@@ -0,0 +1,40 @@
+/*
+ * PPC EDAC common defs
+ *
+ * Author: Dave Jiang <djiang@mvista.com>
+ *
+ * 2007 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#ifndef ASM_EDAC_H
+#define ASM_EDAC_H
+/*
+ * ECC atomic, DMA, SMP and interrupt safe scrub function.
+ * Implements the per arch atomic_scrub() that EDAC use for software
+ * ECC scrubbing.  It reads memory and then writes back the original
+ * value, allowing the hardware to detect and correct memory errors.
+ */
+static __inline__ void atomic_scrub(void *va, u32 size)
+{
+	unsigned int *virt_addr = va;
+	unsigned int temp;
+	unsigned int i;
+
+	for (i = 0; i < size / sizeof(*virt_addr); i++, virt_addr++) {
+		/* Very carefully read and write to memory atomically
+		 * so we are interrupt, DMA and SMP safe.
+		 */
+		__asm__ __volatile__ ("\n\
+				1:	lwarx	%0,0,%1\n\
+					stwcx.	%0,0,%1\n\
+					bne-	1b\n\
+					isync"
+					: "=&r"(temp)
+					: "r"(virt_addr)
+					: "cr0", "memory");
+	}
+}
+
+#endif
diff --git a/include/asm-powerpc/eeh_event.h b/include/asm-powerpc/eeh_event.h
index dc6bf0f..cc3cb04 100644
--- a/include/asm-powerpc/eeh_event.h
+++ b/include/asm-powerpc/eeh_event.h
@@ -30,8 +30,6 @@
 	struct list_head     list;
 	struct device_node 	*dn;   /* struct device node */
 	struct pci_dev       *dev;  /* affected device */
-	enum pci_channel_state state; /* PCI bus state for the affected device */
-	int time_unavail;    /* milliseconds until device might be available */
 };
 
 /**
@@ -46,9 +44,7 @@
  * (from a workqueue).
  */
 int eeh_send_failure_event (struct device_node *dn,
-                            struct pci_dev *dev,
-                            enum pci_channel_state state,
-                            int time_unavail);
+                            struct pci_dev *dev);
 
 /* Main recovery function */
 struct pci_dn * handle_eeh_events (struct eeh_event *);
diff --git a/include/asm-powerpc/ibmebus.h b/include/asm-powerpc/ibmebus.h
index 6611211..87d396e 100644
--- a/include/asm-powerpc/ibmebus.h
+++ b/include/asm-powerpc/ibmebus.h
@@ -2,36 +2,37 @@
  * IBM PowerPC eBus Infrastructure Support.
  *
  * Copyright (c) 2005 IBM Corporation
+ *  Joachim Fenkes <fenkes@de.ibm.com>
  *  Heiko J Schick <schickhj@de.ibm.com>
- *    
+ *
  * All rights reserved.
  *
- * This source code is distributed under a dual license of GPL v2.0 and OpenIB 
- * BSD. 
+ * This source code is distributed under a dual license of GPL v2.0 and OpenIB
+ * BSD.
  *
  * OpenIB BSD License
  *
- * Redistribution and use in source and binary forms, with or without 
- * modification, are permitted provided that the following conditions are met: 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
  *
- * Redistributions of source code must retain the above copyright notice, this 
- * list of conditions and the following disclaimer. 
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
  *
- * Redistributions in binary form must reproduce the above copyright notice, 
- * this list of conditions and the following disclaimer in the documentation 
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
  * and/or other materials
- * provided with the distribution. 
+ * provided with the distribution.
  *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 
+ * 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.
  */
 
@@ -46,12 +47,11 @@
 
 extern struct bus_type ibmebus_bus_type;
 
-struct ibmebus_dev {	
-	const char *name;
+struct ibmebus_dev {
 	struct of_device ofdev;
 };
 
-struct ibmebus_driver {	
+struct ibmebus_driver {
 	char *name;
 	struct of_device_id *id_table;
 	int (*probe) (struct ibmebus_dev *dev, const struct of_device_id *id);
@@ -63,7 +63,7 @@
 void ibmebus_unregister_driver(struct ibmebus_driver *drv);
 
 int ibmebus_request_irq(struct ibmebus_dev *dev,
-			u32 ist, 
+			u32 ist,
 			irq_handler_t handler,
 			unsigned long irq_flags, const char * devname,
 			void *dev_id);
diff --git a/include/asm-powerpc/immap_86xx.h b/include/asm-powerpc/immap_86xx.h
index d905b66..59b9e07 100644
--- a/include/asm-powerpc/immap_86xx.h
+++ b/include/asm-powerpc/immap_86xx.h
@@ -85,81 +85,6 @@
 	char	res19[472];
 } ccsr_pci_t;
 
-/* PCI Express Registers */
-typedef struct ccsr_pex {
-	uint    pex_config_addr;        /* 0x.000 - PCI Express Configuration Address Register */
-	uint    pex_config_data;        /* 0x.004 - PCI Express Configuration Data Register */
-	char    res1[4];
-	uint    pex_otb_cpl_tor;        /* 0x.00c - PCI Express Outbound completion timeout register */
-	uint    pex_conf_tor;           /* 0x.010 - PCI Express configuration timeout register */
-	char    res2[12];
-	uint    pex_pme_mes_dr;         /* 0x.020 - PCI Express PME and message detect register */
-	uint    pex_pme_mes_disr;       /* 0x.024 - PCI Express PME and message disable register */
-	uint    pex_pme_mes_ier;        /* 0x.028 - PCI Express PME and message interrupt enable register */
-	uint    pex_pmcr;               /* 0x.02c - PCI Express power management command register */
-	char    res3[3024];
-	uint    pexotar0;               /* 0x.c00 - PCI Express outbound translation address register 0 */
-	uint    pexotear0;              /* 0x.c04 - PCI Express outbound translation extended address register 0*/
-	char    res4[8];
-	uint    pexowar0;               /* 0x.c10 - PCI Express outbound window attributes register 0*/
-	char    res5[12];
-	uint    pexotar1;               /* 0x.c20 - PCI Express outbound translation address register 1 */
-	uint    pexotear1;              /* 0x.c24 - PCI Express outbound translation extended address register 1*/
-	uint    pexowbar1;              /* 0x.c28 - PCI Express outbound window base address register 1*/
-	char    res6[4];
-	uint    pexowar1;               /* 0x.c30 - PCI Express outbound window attributes register 1*/
-	char    res7[12];
-	uint    pexotar2;               /* 0x.c40 - PCI Express outbound translation address register 2 */
-	uint    pexotear2;              /* 0x.c44 - PCI Express outbound translation extended address register 2*/
-	uint    pexowbar2;              /* 0x.c48 - PCI Express outbound window base address register 2*/
-	char    res8[4];
-	uint    pexowar2;               /* 0x.c50 - PCI Express outbound window attributes register 2*/
-	char    res9[12];
-	uint    pexotar3;               /* 0x.c60 - PCI Express outbound translation address register 3 */
-	uint    pexotear3;              /* 0x.c64 - PCI Express outbound translation extended address register 3*/
-	uint    pexowbar3;              /* 0x.c68 - PCI Express outbound window base address register 3*/
-	char    res10[4];
-	uint    pexowar3;               /* 0x.c70 - PCI Express outbound window attributes register 3*/
-	char    res11[12];
-	uint    pexotar4;               /* 0x.c80 - PCI Express outbound translation address register 4 */
-	uint    pexotear4;              /* 0x.c84 - PCI Express outbound translation extended address register 4*/
-	uint    pexowbar4;              /* 0x.c88 - PCI Express outbound window base address register 4*/
-	char    res12[4];
-	uint    pexowar4;               /* 0x.c90 - PCI Express outbound window attributes register 4*/
-	char    res13[12];
-	char    res14[256];
-	uint    pexitar3;               /* 0x.da0 - PCI Express inbound translation address register 3 */
-	char    res15[4];
-	uint    pexiwbar3;              /* 0x.da8 - PCI Express inbound window base address register 3 */
-	uint    pexiwbear3;             /* 0x.dac - PCI Express inbound window base extended address register 3 */
-	uint    pexiwar3;               /* 0x.db0 - PCI Express inbound window attributes register 3 */
-	char    res16[12];
-	uint    pexitar2;               /* 0x.dc0 - PCI Express inbound translation address register 2 */
-	char    res17[4];
-	uint    pexiwbar2;              /* 0x.dc8 - PCI Express inbound window base address register 2 */
-	uint    pexiwbear2;             /* 0x.dcc - PCI Express inbound window base extended address register 2 */
-	uint    pexiwar2;               /* 0x.dd0 - PCI Express inbound window attributes register 2 */
-	char    res18[12];
-	uint    pexitar1;               /* 0x.de0 - PCI Express inbound translation address register 2 */
-	char    res19[4];
-	uint    pexiwbar1;              /* 0x.de8 - PCI Express inbound window base address register 2 */
-	uint    pexiwbear1;             /* 0x.dec - PCI Express inbound window base extended address register 2 */
-	uint    pexiwar1;               /* 0x.df0 - PCI Express inbound window attributes register 2 */
-	char    res20[12];
-	uint    pex_err_dr;             /* 0x.e00 - PCI Express error detect register */
-	char    res21[4];
-	uint    pex_err_en;             /* 0x.e08 - PCI Express error interrupt enable register */
-	char    res22[4];
-	uint    pex_err_disr;           /* 0x.e10 - PCI Express error disable register */
-	char    res23[12];
-	uint    pex_err_cap_stat;       /* 0x.e20 - PCI Express error capture status register */
-	char    res24[4];
-	uint    pex_err_cap_r0;         /* 0x.e28 - PCI Express error capture register 0 */
-	uint    pex_err_cap_r1;         /* 0x.e2c - PCI Express error capture register 0 */
-	uint    pex_err_cap_r2;         /* 0x.e30 - PCI Express error capture register 0 */
-	uint    pex_err_cap_r3;         /* 0x.e34 - PCI Express error capture register 0 */
-} ccsr_pex_t;
-
 /* Global Utility Registers */
 typedef struct ccsr_guts {
 	uint	porpllsr;	/* 0x.0000 - POR PLL Ratio Status Register */
diff --git a/include/asm-powerpc/io.h b/include/asm-powerpc/io.h
index 301c9bb..350c9bd 100644
--- a/include/asm-powerpc/io.h
+++ b/include/asm-powerpc/io.h
@@ -11,7 +11,12 @@
 
 /* Check of existence of legacy devices */
 extern int check_legacy_ioport(unsigned long base_port);
-#define PNPBIOS_BASE	0xf000	/* only relevant for PReP */
+#define I8042_DATA_REG	0x60
+#define FDC_BASE	0x3f0
+/* only relevant for PReP */
+#define _PIDXR		0x279
+#define _PNPWRP		0xa79
+#define PNPBIOS_BASE	0xf000
 
 #include <linux/compiler.h>
 #include <asm/page.h>
diff --git a/include/asm-powerpc/kprobes.h b/include/asm-powerpc/kprobes.h
index 3a5dd49..f850ca7 100644
--- a/include/asm-powerpc/kprobes.h
+++ b/include/asm-powerpc/kprobes.h
@@ -87,6 +87,11 @@
 struct arch_specific_insn {
 	/* copy of original instruction */
 	kprobe_opcode_t *insn;
+	/*
+	 * Set in kprobes code, initially to 0. If the instruction can be
+	 * eumulated, this is set to 1, if not, to -1.
+	 */
+	int boostable;
 };
 
 struct prev_kprobe {
diff --git a/include/asm-powerpc/machdep.h b/include/asm-powerpc/machdep.h
index 1b04e57..b204926 100644
--- a/include/asm-powerpc/machdep.h
+++ b/include/asm-powerpc/machdep.h
@@ -153,9 +153,6 @@
 	 */
 	long	 	(*feature_call)(unsigned int feature, ...);
 
-	/* Check availability of legacy devices like i8042 */
-	int 		(*check_legacy_ioport)(unsigned int baseport);
-
 	/* Get legacy PCI/IDE interrupt mapping */ 
 	int		(*pci_get_legacy_ide_irq)(struct pci_dev *dev, int channel);
 	
diff --git a/include/asm-powerpc/mmu-hash64.h b/include/asm-powerpc/mmu-hash64.h
new file mode 100644
index 0000000..6739457
--- /dev/null
+++ b/include/asm-powerpc/mmu-hash64.h
@@ -0,0 +1,400 @@
+#ifndef _ASM_POWERPC_MMU_HASH64_H_
+#define _ASM_POWERPC_MMU_HASH64_H_
+/*
+ * PowerPC64 memory management structures
+ *
+ * Dave Engebretsen & Mike Corrigan <{engebret|mikejc}@us.ibm.com>
+ *   PPC64 rework.
+ *
+ * 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.
+ */
+
+#include <asm/asm-compat.h>
+#include <asm/page.h>
+
+/*
+ * Segment table
+ */
+
+#define STE_ESID_V	0x80
+#define STE_ESID_KS	0x20
+#define STE_ESID_KP	0x10
+#define STE_ESID_N	0x08
+
+#define STE_VSID_SHIFT	12
+
+/* Location of cpu0's segment table */
+#define STAB0_PAGE	0x6
+#define STAB0_OFFSET	(STAB0_PAGE << 12)
+#define STAB0_PHYS_ADDR	(STAB0_OFFSET + PHYSICAL_START)
+
+#ifndef __ASSEMBLY__
+extern char initial_stab[];
+#endif /* ! __ASSEMBLY */
+
+/*
+ * SLB
+ */
+
+#define SLB_NUM_BOLTED		3
+#define SLB_CACHE_ENTRIES	8
+
+/* Bits in the SLB ESID word */
+#define SLB_ESID_V		ASM_CONST(0x0000000008000000) /* valid */
+
+/* Bits in the SLB VSID word */
+#define SLB_VSID_SHIFT		12
+#define SLB_VSID_B		ASM_CONST(0xc000000000000000)
+#define SLB_VSID_B_256M		ASM_CONST(0x0000000000000000)
+#define SLB_VSID_B_1T		ASM_CONST(0x4000000000000000)
+#define SLB_VSID_KS		ASM_CONST(0x0000000000000800)
+#define SLB_VSID_KP		ASM_CONST(0x0000000000000400)
+#define SLB_VSID_N		ASM_CONST(0x0000000000000200) /* no-execute */
+#define SLB_VSID_L		ASM_CONST(0x0000000000000100)
+#define SLB_VSID_C		ASM_CONST(0x0000000000000080) /* class */
+#define SLB_VSID_LP		ASM_CONST(0x0000000000000030)
+#define SLB_VSID_LP_00		ASM_CONST(0x0000000000000000)
+#define SLB_VSID_LP_01		ASM_CONST(0x0000000000000010)
+#define SLB_VSID_LP_10		ASM_CONST(0x0000000000000020)
+#define SLB_VSID_LP_11		ASM_CONST(0x0000000000000030)
+#define SLB_VSID_LLP		(SLB_VSID_L|SLB_VSID_LP)
+
+#define SLB_VSID_KERNEL		(SLB_VSID_KP)
+#define SLB_VSID_USER		(SLB_VSID_KP|SLB_VSID_KS|SLB_VSID_C)
+
+#define SLBIE_C			(0x08000000)
+
+/*
+ * Hash table
+ */
+
+#define HPTES_PER_GROUP 8
+
+#define HPTE_V_AVPN_SHIFT	7
+#define HPTE_V_AVPN		ASM_CONST(0xffffffffffffff80)
+#define HPTE_V_AVPN_VAL(x)	(((x) & HPTE_V_AVPN) >> HPTE_V_AVPN_SHIFT)
+#define HPTE_V_COMPARE(x,y)	(!(((x) ^ (y)) & HPTE_V_AVPN))
+#define HPTE_V_BOLTED		ASM_CONST(0x0000000000000010)
+#define HPTE_V_LOCK		ASM_CONST(0x0000000000000008)
+#define HPTE_V_LARGE		ASM_CONST(0x0000000000000004)
+#define HPTE_V_SECONDARY	ASM_CONST(0x0000000000000002)
+#define HPTE_V_VALID		ASM_CONST(0x0000000000000001)
+
+#define HPTE_R_PP0		ASM_CONST(0x8000000000000000)
+#define HPTE_R_TS		ASM_CONST(0x4000000000000000)
+#define HPTE_R_RPN_SHIFT	12
+#define HPTE_R_RPN		ASM_CONST(0x3ffffffffffff000)
+#define HPTE_R_FLAGS		ASM_CONST(0x00000000000003ff)
+#define HPTE_R_PP		ASM_CONST(0x0000000000000003)
+#define HPTE_R_N		ASM_CONST(0x0000000000000004)
+#define HPTE_R_C		ASM_CONST(0x0000000000000080)
+#define HPTE_R_R		ASM_CONST(0x0000000000000100)
+
+/* Values for PP (assumes Ks=0, Kp=1) */
+/* pp0 will always be 0 for linux     */
+#define PP_RWXX	0	/* Supervisor read/write, User none */
+#define PP_RWRX 1	/* Supervisor read/write, User read */
+#define PP_RWRW 2	/* Supervisor read/write, User read/write */
+#define PP_RXRX 3	/* Supervisor read,       User read */
+
+#ifndef __ASSEMBLY__
+
+typedef struct {
+	unsigned long v;
+	unsigned long r;
+} hpte_t;
+
+extern hpte_t *htab_address;
+extern unsigned long htab_size_bytes;
+extern unsigned long htab_hash_mask;
+
+/*
+ * Page size definition
+ *
+ *    shift : is the "PAGE_SHIFT" value for that page size
+ *    sllp  : is a bit mask with the value of SLB L || LP to be or'ed
+ *            directly to a slbmte "vsid" value
+ *    penc  : is the HPTE encoding mask for the "LP" field:
+ *
+ */
+struct mmu_psize_def
+{
+	unsigned int	shift;	/* number of bits */
+	unsigned int	penc;	/* HPTE encoding */
+	unsigned int	tlbiel;	/* tlbiel supported for that page size */
+	unsigned long	avpnm;	/* bits to mask out in AVPN in the HPTE */
+	unsigned long	sllp;	/* SLB L||LP (exact mask to use in slbmte) */
+};
+
+#endif /* __ASSEMBLY__ */
+
+/*
+ * The kernel use the constants below to index in the page sizes array.
+ * The use of fixed constants for this purpose is better for performances
+ * of the low level hash refill handlers.
+ *
+ * A non supported page size has a "shift" field set to 0
+ *
+ * Any new page size being implemented can get a new entry in here. Whether
+ * the kernel will use it or not is a different matter though. The actual page
+ * size used by hugetlbfs is not defined here and may be made variable
+ */
+
+#define MMU_PAGE_4K		0	/* 4K */
+#define MMU_PAGE_64K		1	/* 64K */
+#define MMU_PAGE_64K_AP		2	/* 64K Admixed (in a 4K segment) */
+#define MMU_PAGE_1M		3	/* 1M */
+#define MMU_PAGE_16M		4	/* 16M */
+#define MMU_PAGE_16G		5	/* 16G */
+#define MMU_PAGE_COUNT		6
+
+#ifndef __ASSEMBLY__
+
+/*
+ * The current system page sizes
+ */
+extern struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT];
+extern int mmu_linear_psize;
+extern int mmu_virtual_psize;
+extern int mmu_vmalloc_psize;
+extern int mmu_io_psize;
+
+/*
+ * If the processor supports 64k normal pages but not 64k cache
+ * inhibited pages, we have to be prepared to switch processes
+ * to use 4k pages when they create cache-inhibited mappings.
+ * If this is the case, mmu_ci_restrictions will be set to 1.
+ */
+extern int mmu_ci_restrictions;
+
+#ifdef CONFIG_HUGETLB_PAGE
+/*
+ * The page size index of the huge pages for use by hugetlbfs
+ */
+extern int mmu_huge_psize;
+
+#endif /* CONFIG_HUGETLB_PAGE */
+
+/*
+ * This function sets the AVPN and L fields of the HPTE  appropriately
+ * for the page size
+ */
+static inline unsigned long hpte_encode_v(unsigned long va, int psize)
+{
+	unsigned long v =
+	v = (va >> 23) & ~(mmu_psize_defs[psize].avpnm);
+	v <<= HPTE_V_AVPN_SHIFT;
+	if (psize != MMU_PAGE_4K)
+		v |= HPTE_V_LARGE;
+	return v;
+}
+
+/*
+ * This function sets the ARPN, and LP fields of the HPTE appropriately
+ * for the page size. We assume the pa is already "clean" that is properly
+ * aligned for the requested page size
+ */
+static inline unsigned long hpte_encode_r(unsigned long pa, int psize)
+{
+	unsigned long r;
+
+	/* A 4K page needs no special encoding */
+	if (psize == MMU_PAGE_4K)
+		return pa & HPTE_R_RPN;
+	else {
+		unsigned int penc = mmu_psize_defs[psize].penc;
+		unsigned int shift = mmu_psize_defs[psize].shift;
+		return (pa & ~((1ul << shift) - 1)) | (penc << 12);
+	}
+	return r;
+}
+
+/*
+ * This hashes a virtual address for a 256Mb segment only for now
+ */
+
+static inline unsigned long hpt_hash(unsigned long va, unsigned int shift)
+{
+	return ((va >> 28) & 0x7fffffffffUL) ^ ((va & 0x0fffffffUL) >> shift);
+}
+
+extern int __hash_page_4K(unsigned long ea, unsigned long access,
+			  unsigned long vsid, pte_t *ptep, unsigned long trap,
+			  unsigned int local);
+extern int __hash_page_64K(unsigned long ea, unsigned long access,
+			   unsigned long vsid, pte_t *ptep, unsigned long trap,
+			   unsigned int local);
+struct mm_struct;
+extern int hash_page(unsigned long ea, unsigned long access, unsigned long trap);
+extern int hash_huge_page(struct mm_struct *mm, unsigned long access,
+			  unsigned long ea, unsigned long vsid, int local,
+			  unsigned long trap);
+
+extern int htab_bolt_mapping(unsigned long vstart, unsigned long vend,
+			     unsigned long pstart, unsigned long mode,
+			     int psize);
+
+extern void htab_initialize(void);
+extern void htab_initialize_secondary(void);
+extern void hpte_init_native(void);
+extern void hpte_init_lpar(void);
+extern void hpte_init_iSeries(void);
+extern void hpte_init_beat(void);
+
+extern void stabs_alloc(void);
+extern void slb_initialize(void);
+extern void slb_flush_and_rebolt(void);
+extern void stab_initialize(unsigned long stab);
+
+#endif /* __ASSEMBLY__ */
+
+/*
+ * VSID allocation
+ *
+ * We first generate a 36-bit "proto-VSID".  For kernel addresses this
+ * is equal to the ESID, for user addresses it is:
+ *	(context << 15) | (esid & 0x7fff)
+ *
+ * The two forms are distinguishable because the top bit is 0 for user
+ * addresses, whereas the top two bits are 1 for kernel addresses.
+ * Proto-VSIDs with the top two bits equal to 0b10 are reserved for
+ * now.
+ *
+ * The proto-VSIDs are then scrambled into real VSIDs with the
+ * multiplicative hash:
+ *
+ *	VSID = (proto-VSID * VSID_MULTIPLIER) % VSID_MODULUS
+ *	where	VSID_MULTIPLIER = 268435399 = 0xFFFFFC7
+ *		VSID_MODULUS = 2^36-1 = 0xFFFFFFFFF
+ *
+ * This scramble is only well defined for proto-VSIDs below
+ * 0xFFFFFFFFF, so both proto-VSID and actual VSID 0xFFFFFFFFF are
+ * reserved.  VSID_MULTIPLIER is prime, so in particular it is
+ * co-prime to VSID_MODULUS, making this a 1:1 scrambling function.
+ * Because the modulus is 2^n-1 we can compute it efficiently without
+ * a divide or extra multiply (see below).
+ *
+ * This scheme has several advantages over older methods:
+ *
+ * 	- We have VSIDs allocated for every kernel address
+ * (i.e. everything above 0xC000000000000000), except the very top
+ * segment, which simplifies several things.
+ *
+ * 	- We allow for 15 significant bits of ESID and 20 bits of
+ * context for user addresses.  i.e. 8T (43 bits) of address space for
+ * up to 1M contexts (although the page table structure and context
+ * allocation will need changes to take advantage of this).
+ *
+ * 	- The scramble function gives robust scattering in the hash
+ * table (at least based on some initial results).  The previous
+ * method was more susceptible to pathological cases giving excessive
+ * hash collisions.
+ */
+/*
+ * WARNING - If you change these you must make sure the asm
+ * implementations in slb_allocate (slb_low.S), do_stab_bolted
+ * (head.S) and ASM_VSID_SCRAMBLE (below) are changed accordingly.
+ *
+ * You'll also need to change the precomputed VSID values in head.S
+ * which are used by the iSeries firmware.
+ */
+
+#define VSID_MULTIPLIER	ASM_CONST(200730139)	/* 28-bit prime */
+#define VSID_BITS	36
+#define VSID_MODULUS	((1UL<<VSID_BITS)-1)
+
+#define CONTEXT_BITS	19
+#define USER_ESID_BITS	16
+
+#define USER_VSID_RANGE	(1UL << (USER_ESID_BITS + SID_SHIFT))
+
+/*
+ * This macro generates asm code to compute the VSID scramble
+ * function.  Used in slb_allocate() and do_stab_bolted.  The function
+ * computed is: (protovsid*VSID_MULTIPLIER) % VSID_MODULUS
+ *
+ *	rt = register continaing the proto-VSID and into which the
+ *		VSID will be stored
+ *	rx = scratch register (clobbered)
+ *
+ * 	- rt and rx must be different registers
+ * 	- The answer will end up in the low 36 bits of rt.  The higher
+ * 	  bits may contain other garbage, so you may need to mask the
+ * 	  result.
+ */
+#define ASM_VSID_SCRAMBLE(rt, rx)	\
+	lis	rx,VSID_MULTIPLIER@h;					\
+	ori	rx,rx,VSID_MULTIPLIER@l;				\
+	mulld	rt,rt,rx;		/* rt = rt * MULTIPLIER */	\
+									\
+	srdi	rx,rt,VSID_BITS;					\
+	clrldi	rt,rt,(64-VSID_BITS);					\
+	add	rt,rt,rx;		/* add high and low bits */	\
+	/* Now, r3 == VSID (mod 2^36-1), and lies between 0 and		\
+	 * 2^36-1+2^28-1.  That in particular means that if r3 >=	\
+	 * 2^36-1, then r3+1 has the 2^36 bit set.  So, if r3+1 has	\
+	 * the bit clear, r3 already has the answer we want, if it	\
+	 * doesn't, the answer is the low 36 bits of r3+1.  So in all	\
+	 * cases the answer is the low 36 bits of (r3 + ((r3+1) >> 36))*/\
+	addi	rx,rt,1;						\
+	srdi	rx,rx,VSID_BITS;	/* extract 2^36 bit */		\
+	add	rt,rt,rx
+
+
+#ifndef __ASSEMBLY__
+
+typedef unsigned long mm_context_id_t;
+
+typedef struct {
+	mm_context_id_t id;
+	u16 user_psize;			/* page size index */
+	u16 sllp;			/* SLB entry page size encoding */
+#ifdef CONFIG_HUGETLB_PAGE
+	u16 low_htlb_areas, high_htlb_areas;
+#endif
+	unsigned long vdso_base;
+} mm_context_t;
+
+
+static inline unsigned long vsid_scramble(unsigned long protovsid)
+{
+#if 0
+	/* The code below is equivalent to this function for arguments
+	 * < 2^VSID_BITS, which is all this should ever be called
+	 * with.  However gcc is not clever enough to compute the
+	 * modulus (2^n-1) without a second multiply. */
+	return ((protovsid * VSID_MULTIPLIER) % VSID_MODULUS);
+#else /* 1 */
+	unsigned long x;
+
+	x = protovsid * VSID_MULTIPLIER;
+	x = (x >> VSID_BITS) + (x & VSID_MODULUS);
+	return (x + ((x+1) >> VSID_BITS)) & VSID_MODULUS;
+#endif /* 1 */
+}
+
+/* This is only valid for addresses >= KERNELBASE */
+static inline unsigned long get_kernel_vsid(unsigned long ea)
+{
+	return vsid_scramble(ea >> SID_SHIFT);
+}
+
+/* This is only valid for user addresses (which are below 2^41) */
+static inline unsigned long get_vsid(unsigned long context, unsigned long ea)
+{
+	return vsid_scramble((context << USER_ESID_BITS)
+			     | (ea >> SID_SHIFT));
+}
+
+#define VSID_SCRAMBLE(pvsid)	(((pvsid) * VSID_MULTIPLIER) % VSID_MODULUS)
+#define KERNEL_VSID(ea)		VSID_SCRAMBLE(GET_ESID(ea))
+
+/* Physical address used by some IO functions */
+typedef unsigned long phys_addr_t;
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ASM_POWERPC_MMU_HASH64_H_ */
diff --git a/include/asm-powerpc/mmu.h b/include/asm-powerpc/mmu.h
index 200055a..06b3e6d 100644
--- a/include/asm-powerpc/mmu.h
+++ b/include/asm-powerpc/mmu.h
@@ -2,407 +2,14 @@
 #define _ASM_POWERPC_MMU_H_
 #ifdef __KERNEL__
 
-#ifndef CONFIG_PPC64
-#include <asm-ppc/mmu.h>
+#ifdef CONFIG_PPC64
+/* 64-bit classic hash table MMU */
+#  include <asm/mmu-hash64.h>
 #else
-
-/*
- * PowerPC memory management structures
- *
- * Dave Engebretsen & Mike Corrigan <{engebret|mikejc}@us.ibm.com>
- *   PPC64 rework.
- *
- * 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.
- */
-
-#include <asm/asm-compat.h>
-#include <asm/page.h>
-
-/*
- * Segment table
- */
-
-#define STE_ESID_V	0x80
-#define STE_ESID_KS	0x20
-#define STE_ESID_KP	0x10
-#define STE_ESID_N	0x08
-
-#define STE_VSID_SHIFT	12
-
-/* Location of cpu0's segment table */
-#define STAB0_PAGE	0x6
-#define STAB0_OFFSET	(STAB0_PAGE << 12)
-#define STAB0_PHYS_ADDR	(STAB0_OFFSET + PHYSICAL_START)
-
-#ifndef __ASSEMBLY__
-extern char initial_stab[];
-#endif /* ! __ASSEMBLY */
-
-/*
- * SLB
- */
-
-#define SLB_NUM_BOLTED		3
-#define SLB_CACHE_ENTRIES	8
-
-/* Bits in the SLB ESID word */
-#define SLB_ESID_V		ASM_CONST(0x0000000008000000) /* valid */
-
-/* Bits in the SLB VSID word */
-#define SLB_VSID_SHIFT		12
-#define SLB_VSID_B		ASM_CONST(0xc000000000000000)
-#define SLB_VSID_B_256M		ASM_CONST(0x0000000000000000)
-#define SLB_VSID_B_1T		ASM_CONST(0x4000000000000000)
-#define SLB_VSID_KS		ASM_CONST(0x0000000000000800)
-#define SLB_VSID_KP		ASM_CONST(0x0000000000000400)
-#define SLB_VSID_N		ASM_CONST(0x0000000000000200) /* no-execute */
-#define SLB_VSID_L		ASM_CONST(0x0000000000000100)
-#define SLB_VSID_C		ASM_CONST(0x0000000000000080) /* class */
-#define SLB_VSID_LP		ASM_CONST(0x0000000000000030)
-#define SLB_VSID_LP_00		ASM_CONST(0x0000000000000000)
-#define SLB_VSID_LP_01		ASM_CONST(0x0000000000000010)
-#define SLB_VSID_LP_10		ASM_CONST(0x0000000000000020)
-#define SLB_VSID_LP_11		ASM_CONST(0x0000000000000030)
-#define SLB_VSID_LLP		(SLB_VSID_L|SLB_VSID_LP)
-
-#define SLB_VSID_KERNEL		(SLB_VSID_KP)
-#define SLB_VSID_USER		(SLB_VSID_KP|SLB_VSID_KS|SLB_VSID_C)
-
-#define SLBIE_C			(0x08000000)
-
-/*
- * Hash table
- */
-
-#define HPTES_PER_GROUP 8
-
-#define HPTE_V_AVPN_SHIFT	7
-#define HPTE_V_AVPN		ASM_CONST(0xffffffffffffff80)
-#define HPTE_V_AVPN_VAL(x)	(((x) & HPTE_V_AVPN) >> HPTE_V_AVPN_SHIFT)
-#define HPTE_V_COMPARE(x,y)	(!(((x) ^ (y)) & HPTE_V_AVPN))
-#define HPTE_V_BOLTED		ASM_CONST(0x0000000000000010)
-#define HPTE_V_LOCK		ASM_CONST(0x0000000000000008)
-#define HPTE_V_LARGE		ASM_CONST(0x0000000000000004)
-#define HPTE_V_SECONDARY	ASM_CONST(0x0000000000000002)
-#define HPTE_V_VALID		ASM_CONST(0x0000000000000001)
-
-#define HPTE_R_PP0		ASM_CONST(0x8000000000000000)
-#define HPTE_R_TS		ASM_CONST(0x4000000000000000)
-#define HPTE_R_RPN_SHIFT	12
-#define HPTE_R_RPN		ASM_CONST(0x3ffffffffffff000)
-#define HPTE_R_FLAGS		ASM_CONST(0x00000000000003ff)
-#define HPTE_R_PP		ASM_CONST(0x0000000000000003)
-#define HPTE_R_N		ASM_CONST(0x0000000000000004)
-#define HPTE_R_C		ASM_CONST(0x0000000000000080)
-#define HPTE_R_R		ASM_CONST(0x0000000000000100)
-
-/* Values for PP (assumes Ks=0, Kp=1) */
-/* pp0 will always be 0 for linux     */
-#define PP_RWXX	0	/* Supervisor read/write, User none */
-#define PP_RWRX 1	/* Supervisor read/write, User read */
-#define PP_RWRW 2	/* Supervisor read/write, User read/write */
-#define PP_RXRX 3	/* Supervisor read,       User read */
-
-#ifndef __ASSEMBLY__
-
-typedef struct {
-	unsigned long v;
-	unsigned long r;
-} hpte_t;
-
-extern hpte_t *htab_address;
-extern unsigned long htab_size_bytes;
-extern unsigned long htab_hash_mask;
-
-/*
- * Page size definition
- *
- *    shift : is the "PAGE_SHIFT" value for that page size
- *    sllp  : is a bit mask with the value of SLB L || LP to be or'ed
- *            directly to a slbmte "vsid" value
- *    penc  : is the HPTE encoding mask for the "LP" field:
- *
- */
-struct mmu_psize_def
-{
-	unsigned int	shift;	/* number of bits */
-	unsigned int	penc;	/* HPTE encoding */
-	unsigned int	tlbiel;	/* tlbiel supported for that page size */
-	unsigned long	avpnm;	/* bits to mask out in AVPN in the HPTE */
-	unsigned long	sllp;	/* SLB L||LP (exact mask to use in slbmte) */
-};
-
-#endif /* __ASSEMBLY__ */
-
-/*
- * The kernel use the constants below to index in the page sizes array.
- * The use of fixed constants for this purpose is better for performances
- * of the low level hash refill handlers.
- *
- * A non supported page size has a "shift" field set to 0
- *
- * Any new page size being implemented can get a new entry in here. Whether
- * the kernel will use it or not is a different matter though. The actual page
- * size used by hugetlbfs is not defined here and may be made variable
- */
-
-#define MMU_PAGE_4K		0	/* 4K */
-#define MMU_PAGE_64K		1	/* 64K */
-#define MMU_PAGE_64K_AP		2	/* 64K Admixed (in a 4K segment) */
-#define MMU_PAGE_1M		3	/* 1M */
-#define MMU_PAGE_16M		4	/* 16M */
-#define MMU_PAGE_16G		5	/* 16G */
-#define MMU_PAGE_COUNT		6
-
-#ifndef __ASSEMBLY__
-
-/*
- * The current system page sizes
- */
-extern struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT];
-extern int mmu_linear_psize;
-extern int mmu_virtual_psize;
-extern int mmu_vmalloc_psize;
-extern int mmu_io_psize;
-
-/*
- * If the processor supports 64k normal pages but not 64k cache
- * inhibited pages, we have to be prepared to switch processes
- * to use 4k pages when they create cache-inhibited mappings.
- * If this is the case, mmu_ci_restrictions will be set to 1.
- */
-extern int mmu_ci_restrictions;
-
-#ifdef CONFIG_HUGETLB_PAGE
-/*
- * The page size index of the huge pages for use by hugetlbfs
- */
-extern int mmu_huge_psize;
-
-#endif /* CONFIG_HUGETLB_PAGE */
-
-/*
- * This function sets the AVPN and L fields of the HPTE  appropriately
- * for the page size
- */
-static inline unsigned long hpte_encode_v(unsigned long va, int psize)
-{
-	unsigned long v =
-	v = (va >> 23) & ~(mmu_psize_defs[psize].avpnm);
-	v <<= HPTE_V_AVPN_SHIFT;
-	if (psize != MMU_PAGE_4K)
-		v |= HPTE_V_LARGE;
-	return v;
-}
-
-/*
- * This function sets the ARPN, and LP fields of the HPTE appropriately
- * for the page size. We assume the pa is already "clean" that is properly
- * aligned for the requested page size
- */
-static inline unsigned long hpte_encode_r(unsigned long pa, int psize)
-{
-	unsigned long r;
-
-	/* A 4K page needs no special encoding */
-	if (psize == MMU_PAGE_4K)
-		return pa & HPTE_R_RPN;
-	else {
-		unsigned int penc = mmu_psize_defs[psize].penc;
-		unsigned int shift = mmu_psize_defs[psize].shift;
-		return (pa & ~((1ul << shift) - 1)) | (penc << 12);
-	}
-	return r;
-}
-
-/*
- * This hashes a virtual address for a 256Mb segment only for now
- */
-
-static inline unsigned long hpt_hash(unsigned long va, unsigned int shift)
-{
-	return ((va >> 28) & 0x7fffffffffUL) ^ ((va & 0x0fffffffUL) >> shift);
-}
-
-extern int __hash_page_4K(unsigned long ea, unsigned long access,
-			  unsigned long vsid, pte_t *ptep, unsigned long trap,
-			  unsigned int local);
-extern int __hash_page_64K(unsigned long ea, unsigned long access,
-			   unsigned long vsid, pte_t *ptep, unsigned long trap,
-			   unsigned int local);
-struct mm_struct;
-extern int hash_huge_page(struct mm_struct *mm, unsigned long access,
-			  unsigned long ea, unsigned long vsid, int local,
-			  unsigned long trap);
-
-extern int htab_bolt_mapping(unsigned long vstart, unsigned long vend,
-			     unsigned long pstart, unsigned long mode,
-			     int psize);
-
-extern void htab_initialize(void);
-extern void htab_initialize_secondary(void);
-extern void hpte_init_native(void);
-extern void hpte_init_lpar(void);
-extern void hpte_init_iSeries(void);
-extern void hpte_init_beat(void);
-
-extern void stabs_alloc(void);
-extern void slb_initialize(void);
-extern void slb_flush_and_rebolt(void);
-extern void stab_initialize(unsigned long stab);
-
-#endif /* __ASSEMBLY__ */
-
-/*
- * VSID allocation
- *
- * We first generate a 36-bit "proto-VSID".  For kernel addresses this
- * is equal to the ESID, for user addresses it is:
- *	(context << 15) | (esid & 0x7fff)
- *
- * The two forms are distinguishable because the top bit is 0 for user
- * addresses, whereas the top two bits are 1 for kernel addresses.
- * Proto-VSIDs with the top two bits equal to 0b10 are reserved for
- * now.
- *
- * The proto-VSIDs are then scrambled into real VSIDs with the
- * multiplicative hash:
- *
- *	VSID = (proto-VSID * VSID_MULTIPLIER) % VSID_MODULUS
- *	where	VSID_MULTIPLIER = 268435399 = 0xFFFFFC7
- *		VSID_MODULUS = 2^36-1 = 0xFFFFFFFFF
- *
- * This scramble is only well defined for proto-VSIDs below
- * 0xFFFFFFFFF, so both proto-VSID and actual VSID 0xFFFFFFFFF are
- * reserved.  VSID_MULTIPLIER is prime, so in particular it is
- * co-prime to VSID_MODULUS, making this a 1:1 scrambling function.
- * Because the modulus is 2^n-1 we can compute it efficiently without
- * a divide or extra multiply (see below).
- *
- * This scheme has several advantages over older methods:
- *
- * 	- We have VSIDs allocated for every kernel address
- * (i.e. everything above 0xC000000000000000), except the very top
- * segment, which simplifies several things.
- *
- * 	- We allow for 15 significant bits of ESID and 20 bits of
- * context for user addresses.  i.e. 8T (43 bits) of address space for
- * up to 1M contexts (although the page table structure and context
- * allocation will need changes to take advantage of this).
- *
- * 	- The scramble function gives robust scattering in the hash
- * table (at least based on some initial results).  The previous
- * method was more susceptible to pathological cases giving excessive
- * hash collisions.
- */
-/*
- * WARNING - If you change these you must make sure the asm
- * implementations in slb_allocate (slb_low.S), do_stab_bolted
- * (head.S) and ASM_VSID_SCRAMBLE (below) are changed accordingly.
- *
- * You'll also need to change the precomputed VSID values in head.S
- * which are used by the iSeries firmware.
- */
-
-#define VSID_MULTIPLIER	ASM_CONST(200730139)	/* 28-bit prime */
-#define VSID_BITS	36
-#define VSID_MODULUS	((1UL<<VSID_BITS)-1)
-
-#define CONTEXT_BITS	19
-#define USER_ESID_BITS	16
-
-#define USER_VSID_RANGE	(1UL << (USER_ESID_BITS + SID_SHIFT))
-
-/*
- * This macro generates asm code to compute the VSID scramble
- * function.  Used in slb_allocate() and do_stab_bolted.  The function
- * computed is: (protovsid*VSID_MULTIPLIER) % VSID_MODULUS
- *
- *	rt = register continaing the proto-VSID and into which the
- *		VSID will be stored
- *	rx = scratch register (clobbered)
- *
- * 	- rt and rx must be different registers
- * 	- The answer will end up in the low 36 bits of rt.  The higher
- * 	  bits may contain other garbage, so you may need to mask the
- * 	  result.
- */
-#define ASM_VSID_SCRAMBLE(rt, rx)	\
-	lis	rx,VSID_MULTIPLIER@h;					\
-	ori	rx,rx,VSID_MULTIPLIER@l;				\
-	mulld	rt,rt,rx;		/* rt = rt * MULTIPLIER */	\
-									\
-	srdi	rx,rt,VSID_BITS;					\
-	clrldi	rt,rt,(64-VSID_BITS);					\
-	add	rt,rt,rx;		/* add high and low bits */	\
-	/* Now, r3 == VSID (mod 2^36-1), and lies between 0 and		\
-	 * 2^36-1+2^28-1.  That in particular means that if r3 >=	\
-	 * 2^36-1, then r3+1 has the 2^36 bit set.  So, if r3+1 has	\
-	 * the bit clear, r3 already has the answer we want, if it	\
-	 * doesn't, the answer is the low 36 bits of r3+1.  So in all	\
-	 * cases the answer is the low 36 bits of (r3 + ((r3+1) >> 36))*/\
-	addi	rx,rt,1;						\
-	srdi	rx,rx,VSID_BITS;	/* extract 2^36 bit */		\
-	add	rt,rt,rx
-
-
-#ifndef __ASSEMBLY__
-
-typedef unsigned long mm_context_id_t;
-
-typedef struct {
-	mm_context_id_t id;
-	u16 user_psize;			/* page size index */
-	u16 sllp;			/* SLB entry page size encoding */
-#ifdef CONFIG_HUGETLB_PAGE
-	u16 low_htlb_areas, high_htlb_areas;
+/* 32-bit.  FIXME: split up the 32-bit MMU types, and revise for
+ * arch/powerpc */
+#  include <asm-ppc/mmu.h>
 #endif
-	unsigned long vdso_base;
-} mm_context_t;
 
-
-static inline unsigned long vsid_scramble(unsigned long protovsid)
-{
-#if 0
-	/* The code below is equivalent to this function for arguments
-	 * < 2^VSID_BITS, which is all this should ever be called
-	 * with.  However gcc is not clever enough to compute the
-	 * modulus (2^n-1) without a second multiply. */
-	return ((protovsid * VSID_MULTIPLIER) % VSID_MODULUS);
-#else /* 1 */
-	unsigned long x;
-
-	x = protovsid * VSID_MULTIPLIER;
-	x = (x >> VSID_BITS) + (x & VSID_MODULUS);
-	return (x + ((x+1) >> VSID_BITS)) & VSID_MODULUS;
-#endif /* 1 */
-}
-
-/* This is only valid for addresses >= KERNELBASE */
-static inline unsigned long get_kernel_vsid(unsigned long ea)
-{
-	return vsid_scramble(ea >> SID_SHIFT);
-}
-
-/* This is only valid for user addresses (which are below 2^41) */
-static inline unsigned long get_vsid(unsigned long context, unsigned long ea)
-{
-	return vsid_scramble((context << USER_ESID_BITS)
-			     | (ea >> SID_SHIFT));
-}
-
-#define VSID_SCRAMBLE(pvsid)	(((pvsid) * VSID_MULTIPLIER) % VSID_MODULUS)
-#define KERNEL_VSID(ea)		VSID_SCRAMBLE(GET_ESID(ea))
-
-/* Physical address used by some IO functions */
-typedef unsigned long phys_addr_t;
-
-
-#endif /* __ASSEMBLY */
-
-#endif /* CONFIG_PPC64 */
 #endif /* __KERNEL__ */
 #endif /* _ASM_POWERPC_MMU_H_ */
diff --git a/include/asm-powerpc/mpic.h b/include/asm-powerpc/mpic.h
index cb204a7..e4d5fc5 100644
--- a/include/asm-powerpc/mpic.h
+++ b/include/asm-powerpc/mpic.h
@@ -199,7 +199,7 @@
 };
 
 
-#ifdef CONFIG_MPIC_BROKEN_U3
+#ifdef CONFIG_MPIC_U3_HT_IRQS
 /* Fixup table entry */
 struct mpic_irq_fixup
 {
@@ -208,7 +208,7 @@
 	u32		data;
 	unsigned int	index;
 };
-#endif /* CONFIG_MPIC_BROKEN_U3 */
+#endif /* CONFIG_MPIC_U3_HT_IRQS */
 
 
 enum mpic_reg_type {
@@ -239,7 +239,7 @@
 
 	/* The "linux" controller struct */
 	struct irq_chip		hc_irq;
-#ifdef CONFIG_MPIC_BROKEN_U3
+#ifdef CONFIG_MPIC_U3_HT_IRQS
 	struct irq_chip		hc_ht_irq;
 #endif
 #ifdef CONFIG_SMP
@@ -268,7 +268,7 @@
 	/* Spurious vector to program into unused sources */
 	unsigned int		spurious_vec;
 
-#ifdef CONFIG_MPIC_BROKEN_U3
+#ifdef CONFIG_MPIC_U3_HT_IRQS
 	/* The fixup table */
 	struct mpic_irq_fixup	*fixups;
 	spinlock_t		fixup_lock;
@@ -313,7 +313,7 @@
 /* Set this for a big-endian MPIC */
 #define MPIC_BIG_ENDIAN			0x00000002
 /* Broken U3 MPIC */
-#define MPIC_BROKEN_U3			0x00000004
+#define MPIC_U3_HT_IRQS			0x00000004
 /* Broken IPI registers (autodetected) */
 #define MPIC_BROKEN_IPI			0x00000008
 /* MPIC wants a reset */
@@ -352,7 +352,7 @@
  * @senses_num: number of entries in the array
  *
  * Note about the sense array. If none is passed, all interrupts are
- * setup to be level negative unless MPIC_BROKEN_U3 is set in which
+ * setup to be level negative unless MPIC_U3_HT_IRQS is set in which
  * case they are edge positive (and the array is ignored anyway).
  * The values in the array start at the first source of the MPIC,
  * that is senses[0] correspond to linux irq "irq_offset".
diff --git a/include/asm-powerpc/of_device.h b/include/asm-powerpc/of_device.h
index a889b20..4f1aabe 100644
--- a/include/asm-powerpc/of_device.h
+++ b/include/asm-powerpc/of_device.h
@@ -32,5 +32,8 @@
 extern void of_device_unregister(struct of_device *ofdev);
 extern void of_release_dev(struct device *dev);
 
+extern int of_device_uevent(struct device *dev,
+	char **envp, int num_envp, char *buffer, int buffer_size);
+
 #endif /* __KERNEL__ */
 #endif /* _ASM_POWERPC_OF_DEVICE_H */
diff --git a/include/asm-powerpc/oprofile_impl.h b/include/asm-powerpc/oprofile_impl.h
index 94c0ad2..8d6b47f 100644
--- a/include/asm-powerpc/oprofile_impl.h
+++ b/include/asm-powerpc/oprofile_impl.h
@@ -57,6 +57,8 @@
 extern struct op_powerpc_model op_model_power4;
 extern struct op_powerpc_model op_model_7450;
 extern struct op_powerpc_model op_model_cell;
+extern struct op_powerpc_model op_model_pa6t;
+
 
 /* All the classic PPC parts use these */
 static inline unsigned int classic_ctr_read(unsigned int i)
diff --git a/include/asm-powerpc/paca.h b/include/asm-powerpc/paca.h
index 0d3adc0..cf95274 100644
--- a/include/asm-powerpc/paca.h
+++ b/include/asm-powerpc/paca.h
@@ -70,6 +70,7 @@
 	s16 hw_cpu_id;			/* Physical processor number */
 	u8 cpu_start;			/* At startup, processor spins until */
 					/* this becomes non-zero. */
+	struct slb_shadow *slb_shadow_ptr;
 
 	/*
 	 * Now, starting in cacheline 2, the exception save areas
@@ -93,6 +94,7 @@
 	u64 stab_rr;			/* stab/slb round-robin counter */
 	u64 saved_r1;			/* r1 save for RTAS calls */
 	u64 saved_msr;			/* MSR saved here by enter_rtas */
+	u16 trap_save;			/* Used when bad stack is encountered */
 	u8 soft_enabled;		/* irq soft-enable flag */
 	u8 hard_enabled;		/* set if irqs are enabled in MSR */
 	u8 io_sync;			/* writel() needs spin_unlock sync */
@@ -101,8 +103,6 @@
 	u64 user_time;			/* accumulated usermode TB ticks */
 	u64 system_time;		/* accumulated system TB ticks */
 	u64 startpurr;			/* PURR/TB value snapshot */
-
-	struct slb_shadow *slb_shadow_ptr;
 };
 
 extern struct paca_struct paca[];
diff --git a/include/asm-powerpc/parport.h b/include/asm-powerpc/parport.h
index 3fca21d..b37b81e 100644
--- a/include/asm-powerpc/parport.h
+++ b/include/asm-powerpc/parport.h
@@ -20,18 +20,18 @@
 static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
 {
 	struct device_node *np;
-	u32 *prop;
+	const u32 *prop;
 	u32 io1, io2;
 	int propsize;
 	int count = 0;
 	for (np = NULL; (np = of_find_compatible_node(np,
 						      "parallel",
 						      "pnpPNP,400")) != NULL;) {
-		prop = (u32 *)get_property(np, "reg", &propsize);
+		prop = of_get_property(np, "reg", &propsize);
 		if (!prop || propsize > 6*sizeof(u32))
 			continue;
 		io1 = prop[1]; io2 = prop[2];
-		prop = (u32 *)get_property(np, "interrupts", NULL);
+		prop = of_get_property(np, "interrupts", NULL);
 		if (!prop)
 			continue;
 		if (parport_pc_probe_port(io1, io2, prop[0], autodma, NULL) != NULL)
diff --git a/include/asm-powerpc/pci.h b/include/asm-powerpc/pci.h
index ac656ee..ce0f13e 100644
--- a/include/asm-powerpc/pci.h
+++ b/include/asm-powerpc/pci.h
@@ -70,19 +70,22 @@
  */
 #define PCI_DISABLE_MWI
 
-extern struct dma_mapping_ops *pci_dma_ops;
+#ifdef CONFIG_PCI
+extern void set_pci_dma_ops(struct dma_mapping_ops *dma_ops);
+extern struct dma_mapping_ops *get_pci_dma_ops(void);
 
 /* For DAC DMA, we currently don't support it by default, but
  * we let 64-bit platforms override this.
  */
 static inline int pci_dac_dma_supported(struct pci_dev *hwdev,u64 mask)
 {
-	if (pci_dma_ops && pci_dma_ops->dac_dma_supported)
-		return pci_dma_ops->dac_dma_supported(&hwdev->dev, mask);
+	struct dma_mapping_ops *d = get_pci_dma_ops();
+
+	if (d && d->dac_dma_supported)
+		return d->dac_dma_supported(&hwdev->dev, mask);
 	return 0;
 }
 
-#ifdef CONFIG_PCI
 static inline void pci_dma_burst_advice(struct pci_dev *pdev,
 					enum pci_dma_burst_strategy *strat,
 					unsigned long *strategy_parameter)
@@ -99,6 +102,9 @@
 	*strat = PCI_DMA_BURST_MULTIPLE;
 	*strategy_parameter = cacheline_size;
 }
+#else	/* CONFIG_PCI */
+#define set_pci_dma_ops(d)
+#define get_pci_dma_ops()	NULL
 #endif
 
 extern int pci_domain_nr(struct pci_bus *bus);
diff --git a/include/asm-powerpc/pgtable-4k.h b/include/asm-powerpc/pgtable-4k.h
index 345d9b0..a28fa8b 100644
--- a/include/asm-powerpc/pgtable-4k.h
+++ b/include/asm-powerpc/pgtable-4k.h
@@ -97,3 +97,6 @@
 
 #define pud_ERROR(e) \
 	printk("%s:%d: bad pud %08lx.\n", __FILE__, __LINE__, pud_val(e))
+
+#define remap_4k_pfn(vma, addr, pfn, prot)	\
+	remap_pfn_range((vma), (addr), (pfn), PAGE_SIZE, (prot))
diff --git a/include/asm-powerpc/pgtable-64k.h b/include/asm-powerpc/pgtable-64k.h
index 4b7126c..5e84f07 100644
--- a/include/asm-powerpc/pgtable-64k.h
+++ b/include/asm-powerpc/pgtable-64k.h
@@ -35,6 +35,7 @@
 #define _PAGE_HPTE_SUB	0x0ffff000 /* combo only: sub pages HPTE bits */
 #define _PAGE_HPTE_SUB0	0x08000000 /* combo only: first sub page */
 #define _PAGE_COMBO	0x10000000 /* this is a combo 4k page */
+#define _PAGE_4K_PFN	0x20000000 /* PFN is for a single 4k page */
 #define _PAGE_F_SECOND  0x00008000 /* full page: hidx bits */
 #define _PAGE_F_GIX     0x00007000 /* full page: hidx bits */
 
@@ -93,6 +94,10 @@
 #define pte_pagesize_index(pte)	\
 	(((pte) & _PAGE_COMBO)? MMU_PAGE_4K: MMU_PAGE_64K)
 
+#define remap_4k_pfn(vma, addr, pfn, prot)				\
+	remap_pfn_range((vma), (addr), (pfn), PAGE_SIZE,		\
+			__pgprot(pgprot_val((prot)) | _PAGE_4K_PFN))
+
 #endif /*  __ASSEMBLY__ */
 #endif /* __KERNEL__ */
 #endif /* _ASM_POWERPC_PGTABLE_64K_H */
diff --git a/include/asm-powerpc/pgtable.h b/include/asm-powerpc/pgtable.h
index 10f52743f..19edb69 100644
--- a/include/asm-powerpc/pgtable.h
+++ b/include/asm-powerpc/pgtable.h
@@ -272,7 +272,10 @@
 	return pte; }
 
 /* Atomic PTE updates */
-static inline unsigned long pte_update(pte_t *p, unsigned long clr)
+static inline unsigned long pte_update(struct mm_struct *mm,
+				       unsigned long addr,
+				       pte_t *ptep, unsigned long clr,
+				       int huge)
 {
 	unsigned long old, tmp;
 
@@ -283,20 +286,15 @@
 	andc	%1,%0,%4 \n\
 	stdcx.	%1,0,%3 \n\
 	bne-	1b"
-	: "=&r" (old), "=&r" (tmp), "=m" (*p)
-	: "r" (p), "r" (clr), "m" (*p), "i" (_PAGE_BUSY)
+	: "=&r" (old), "=&r" (tmp), "=m" (*ptep)
+	: "r" (ptep), "r" (clr), "m" (*ptep), "i" (_PAGE_BUSY)
 	: "cc" );
+
+	if (old & _PAGE_HASHPTE)
+		hpte_need_flush(mm, addr, ptep, old, huge);
 	return old;
 }
 
-/* PTE updating functions, this function puts the PTE in the
- * batch, doesn't actually triggers the hash flush immediately,
- * you need to call flush_tlb_pending() to do that.
- * Pass -1 for "normal" size (4K or 64K)
- */
-extern void hpte_update(struct mm_struct *mm, unsigned long addr,
-			pte_t *ptep, unsigned long pte, int huge);
-
 static inline int __ptep_test_and_clear_young(struct mm_struct *mm,
 					      unsigned long addr, pte_t *ptep)
 {
@@ -304,11 +302,7 @@
 
        	if ((pte_val(*ptep) & (_PAGE_ACCESSED | _PAGE_HASHPTE)) == 0)
 		return 0;
-	old = pte_update(ptep, _PAGE_ACCESSED);
-	if (old & _PAGE_HASHPTE) {
-		hpte_update(mm, addr, ptep, old, 0);
-		flush_tlb_pending();
-	}
+	old = pte_update(mm, addr, ptep, _PAGE_ACCESSED, 0);
 	return (old & _PAGE_ACCESSED) != 0;
 }
 #define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG
@@ -331,9 +325,7 @@
 
        	if ((pte_val(*ptep) & _PAGE_DIRTY) == 0)
 		return 0;
-	old = pte_update(ptep, _PAGE_DIRTY);
-	if (old & _PAGE_HASHPTE)
-		hpte_update(mm, addr, ptep, old, 0);
+	old = pte_update(mm, addr, ptep, _PAGE_DIRTY, 0);
 	return (old & _PAGE_DIRTY) != 0;
 }
 #define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_DIRTY
@@ -352,9 +344,7 @@
 
        	if ((pte_val(*ptep) & _PAGE_RW) == 0)
        		return;
-	old = pte_update(ptep, _PAGE_RW);
-	if (old & _PAGE_HASHPTE)
-		hpte_update(mm, addr, ptep, old, 0);
+	old = pte_update(mm, addr, ptep, _PAGE_RW, 0);
 }
 
 /*
@@ -378,7 +368,6 @@
 ({									\
 	int __dirty = __ptep_test_and_clear_dirty((__vma)->vm_mm, __address, \
 						  __ptep); 		\
-	flush_tlb_page(__vma, __address);				\
 	__dirty;							\
 })
 
@@ -386,20 +375,14 @@
 static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
 				       unsigned long addr, pte_t *ptep)
 {
-	unsigned long old = pte_update(ptep, ~0UL);
-
-	if (old & _PAGE_HASHPTE)
-		hpte_update(mm, addr, ptep, old, 0);
+	unsigned long old = pte_update(mm, addr, ptep, ~0UL, 0);
 	return __pte(old);
 }
 
 static inline void pte_clear(struct mm_struct *mm, unsigned long addr,
 			     pte_t * ptep)
 {
-	unsigned long old = pte_update(ptep, ~0UL);
-
-	if (old & _PAGE_HASHPTE)
-		hpte_update(mm, addr, ptep, old, 0);
+	pte_update(mm, addr, ptep, ~0UL, 0);
 }
 
 /*
@@ -408,10 +391,8 @@
 static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
 			      pte_t *ptep, pte_t pte)
 {
-	if (pte_present(*ptep)) {
+	if (pte_present(*ptep))
 		pte_clear(mm, addr, ptep);
-		flush_tlb_pending();
-	}
 	pte = __pte(pte_val(pte) & ~_PAGE_HPTEFLAGS);
 	*ptep = pte;
 }
@@ -467,16 +448,6 @@
 
 extern void paging_init(void);
 
-/*
- * This gets called at the end of handling a page fault, when
- * the kernel has put a new PTE into the page table for the process.
- * We use it to put a corresponding HPTE into the hash table
- * ahead of time, instead of waiting for the inevitable extra
- * hash-table miss exception.
- */
-struct vm_area_struct;
-extern void update_mmu_cache(struct vm_area_struct *, unsigned long, pte_t);
-
 /* Encode and de-code a swap entry */
 #define __swp_type(entry)	(((entry).val >> 1) & 0x3f)
 #define __swp_offset(entry)	((entry).val >> 8)
@@ -522,6 +493,7 @@
 	return pt;
 }
 
+
 #include <asm-generic/pgtable.h>
 
 #endif /* __ASSEMBLY__ */
diff --git a/include/asm-powerpc/pmc.h b/include/asm-powerpc/pmc.h
index 8588be6..d6a616a 100644
--- a/include/asm-powerpc/pmc.h
+++ b/include/asm-powerpc/pmc.h
@@ -30,6 +30,7 @@
 
 #ifdef CONFIG_PPC64
 void power4_enable_pmcs(void);
+void pasemi_enable_pmcs(void);
 #endif
 
 #endif /* __KERNEL__ */
diff --git a/include/asm-powerpc/ppc-pci.h b/include/asm-powerpc/ppc-pci.h
index ab6eddb..d74b296 100644
--- a/include/asm-powerpc/ppc-pci.h
+++ b/include/asm-powerpc/ppc-pci.h
@@ -10,6 +10,8 @@
 #define _ASM_POWERPC_PPC_PCI_H
 #ifdef __KERNEL__
 
+#ifdef CONFIG_PCI
+
 #include <linux/pci.h>
 #include <asm/pci-bridge.h>
 
@@ -22,7 +24,7 @@
 extern struct list_head hose_list;
 extern int global_phb_number;
 
-extern unsigned long find_and_init_phbs(void);
+extern void find_and_init_phbs(void);
 
 extern struct pci_dev *ppc64_isabridge_dev;	/* may be NULL if no ISA bus */
 
@@ -68,7 +70,7 @@
 void eeh_slot_error_detail (struct pci_dn *pdn, int severity);
 
 /**
- * rtas_pci_enableo - enable IO transfers for this slot
+ * rtas_pci_enable - enable IO transfers for this slot
  * @pdn:       pci device node
  * @function:  either EEH_THAW_MMIO or EEH_THAW_DMA 
  *
@@ -89,6 +91,7 @@
  * Returns a non-zero value if the reset failed.
  */
 int rtas_set_slot_reset (struct pci_dn *);
+int eeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs);
 
 /** 
  * eeh_restore_bars - Restore device configuration info.
@@ -126,5 +129,10 @@
 
 #endif
 
+#else /* CONFIG_PCI */
+static inline void find_and_init_phbs(void) { }
+static inline void init_pci_config_tokens(void) { }
+#endif /* !CONFIG_PCI */
+
 #endif /* __KERNEL__ */
 #endif /* _ASM_POWERPC_PPC_PCI_H */
diff --git a/include/asm-powerpc/processor.h b/include/asm-powerpc/processor.h
index a26c32e..d947b16 100644
--- a/include/asm-powerpc/processor.h
+++ b/include/asm-powerpc/processor.h
@@ -133,7 +133,6 @@
 	mm_segment_t	fs;		/* for get_fs() validation */
 #ifdef CONFIG_PPC32
 	void		*pgdir;		/* root of page-table tree */
-	signed long	last_syscall;
 #endif
 #if defined(CONFIG_4xx) || defined (CONFIG_BOOKE)
 	unsigned long	dbcr0;		/* debug control register values */
diff --git a/include/asm-powerpc/prom.h b/include/asm-powerpc/prom.h
index 020ed01..ec400f6 100644
--- a/include/asm-powerpc/prom.h
+++ b/include/asm-powerpc/prom.h
@@ -18,7 +18,9 @@
 #include <linux/types.h>
 #include <linux/proc_fs.h>
 #include <linux/platform_device.h>
+#include <asm/irq.h>
 #include <asm/atomic.h>
+#include <asm/io.h>
 
 /* Definitions used by the flattened device tree */
 #define OF_DT_HEADER		0xd00dfeed	/* marker */
@@ -58,6 +60,8 @@
 	u32	boot_cpuid_phys;	/* Physical CPU id we're booting on */
 	/* version 3 fields below */
 	u32	dt_strings_size;	/* size of the DT strings block */
+	/* version 17 fields below */
+	u32	dt_struct_size;		/* size of the DT structure block */
 };
 
 
@@ -68,7 +72,7 @@
 struct property {
 	char	*name;
 	int	length;
-	unsigned char *value;
+	void	*value;
 	struct property *next;
 };
 
@@ -108,14 +112,6 @@
 }
 
 
-/* OBSOLETE: Old style node lookup */
-extern struct device_node *find_devices(const char *name);
-extern struct device_node *find_type_devices(const char *type);
-extern struct device_node *find_path_device(const char *path);
-extern struct device_node *find_compatible_devices(const char *type,
-						   const char *compat);
-extern struct device_node *find_all_nodes(void);
-
 /* New style node lookup */
 extern struct device_node *of_find_node_by_name(struct device_node *from,
 	const char *name);
@@ -159,15 +155,17 @@
 extern void finish_device_tree(void);
 extern void unflatten_device_tree(void);
 extern void early_init_devtree(void *);
-extern int device_is_compatible(const struct device_node *device,
+extern int of_device_is_compatible(const struct device_node *device,
 				const char *);
+#define device_is_compatible(d, c)	of_device_is_compatible((d), (c))
 extern int machine_is_compatible(const char *compat);
-extern const void *get_property(const struct device_node *node,
+extern const void *of_get_property(const struct device_node *node,
 				const char *name,
 				int *lenp);
+#define get_property(a, b, c)	of_get_property((a), (b), (c))
 extern void print_properties(struct device_node *node);
-extern int prom_n_addr_cells(struct device_node* np);
-extern int prom_n_size_cells(struct device_node* np);
+extern int of_n_addr_cells(struct device_node* np);
+extern int of_n_size_cells(struct device_node* np);
 extern int prom_n_intr_cells(struct device_node* np);
 extern void prom_get_irq_senses(unsigned char *senses, int off, int max);
 extern int prom_add_property(struct device_node* np, struct property* prop);
@@ -350,6 +348,16 @@
 	return irq;
 }
 
+static inline void __iomem *of_iomap(struct device_node *np, int index)
+{
+	struct resource res;
+
+	if (of_address_to_resource(np, index, &res))
+		return NULL;
+
+	return ioremap(res.start, 1 + res.end - res.start);
+}
+
 
 #endif /* __KERNEL__ */
 #endif /* _POWERPC_PROM_H */
diff --git a/include/asm-powerpc/reg.h b/include/asm-powerpc/reg.h
index 0d7f016..749c7f9 100644
--- a/include/asm-powerpc/reg.h
+++ b/include/asm-powerpc/reg.h
@@ -469,12 +469,68 @@
 #define SPRN_SIAR	780
 #define SPRN_SDAR	781
 
-#define PA6T_SPRN_PMC0	787
-#define PA6T_SPRN_PMC1	788
-#define PA6T_SPRN_PMC2	789
-#define PA6T_SPRN_PMC3	790
-#define PA6T_SPRN_PMC4	791
-#define PA6T_SPRN_PMC5	792
+#define SPRN_PA6T_MMCR0 795
+#define   PA6T_MMCR0_EN0	0x0000000000000001UL
+#define   PA6T_MMCR0_EN1	0x0000000000000002UL
+#define   PA6T_MMCR0_EN2	0x0000000000000004UL
+#define   PA6T_MMCR0_EN3	0x0000000000000008UL
+#define   PA6T_MMCR0_EN4	0x0000000000000010UL
+#define   PA6T_MMCR0_EN5	0x0000000000000020UL
+#define   PA6T_MMCR0_SUPEN	0x0000000000000040UL
+#define   PA6T_MMCR0_PREN	0x0000000000000080UL
+#define   PA6T_MMCR0_HYPEN	0x0000000000000100UL
+#define   PA6T_MMCR0_FCM0	0x0000000000000200UL
+#define   PA6T_MMCR0_FCM1	0x0000000000000400UL
+#define   PA6T_MMCR0_INTGEN	0x0000000000000800UL
+#define   PA6T_MMCR0_INTEN0	0x0000000000001000UL
+#define   PA6T_MMCR0_INTEN1	0x0000000000002000UL
+#define   PA6T_MMCR0_INTEN2	0x0000000000004000UL
+#define   PA6T_MMCR0_INTEN3	0x0000000000008000UL
+#define   PA6T_MMCR0_INTEN4	0x0000000000010000UL
+#define   PA6T_MMCR0_INTEN5	0x0000000000020000UL
+#define   PA6T_MMCR0_DISCNT	0x0000000000040000UL
+#define   PA6T_MMCR0_UOP	0x0000000000080000UL
+#define   PA6T_MMCR0_TRG	0x0000000000100000UL
+#define   PA6T_MMCR0_TRGEN	0x0000000000200000UL
+#define   PA6T_MMCR0_TRGREG	0x0000000001600000UL
+#define   PA6T_MMCR0_SIARLOG	0x0000000002000000UL
+#define   PA6T_MMCR0_SDARLOG	0x0000000004000000UL
+#define   PA6T_MMCR0_PROEN	0x0000000008000000UL
+#define   PA6T_MMCR0_PROLOG	0x0000000010000000UL
+#define   PA6T_MMCR0_DAMEN2	0x0000000020000000UL
+#define   PA6T_MMCR0_DAMEN3	0x0000000040000000UL
+#define   PA6T_MMCR0_DAMEN4	0x0000000080000000UL
+#define   PA6T_MMCR0_DAMEN5	0x0000000100000000UL
+#define   PA6T_MMCR0_DAMSEL2	0x0000000200000000UL
+#define   PA6T_MMCR0_DAMSEL3	0x0000000400000000UL
+#define   PA6T_MMCR0_DAMSEL4	0x0000000800000000UL
+#define   PA6T_MMCR0_DAMSEL5	0x0000001000000000UL
+#define   PA6T_MMCR0_HANDDIS	0x0000002000000000UL
+#define   PA6T_MMCR0_PCTEN	0x0000004000000000UL
+#define   PA6T_MMCR0_SOCEN	0x0000008000000000UL
+#define   PA6T_MMCR0_SOCMOD	0x0000010000000000UL
+
+#define SPRN_PA6T_MMCR1 798
+#define   PA6T_MMCR1_ES2	0x00000000000000ffUL
+#define   PA6T_MMCR1_ES3	0x000000000000ff00UL
+#define   PA6T_MMCR1_ES4	0x0000000000ff0000UL
+#define   PA6T_MMCR1_ES5	0x00000000ff000000UL
+
+#define SPRN_PA6T_SIAR  780
+#define SPRN_PA6T_UPMC0 771
+#define SPRN_PA6T_UPMC1 772
+#define SPRN_PA6T_UPMC2 773
+#define SPRN_PA6T_UPMC3 774
+#define SPRN_PA6T_UPMC4 775
+#define SPRN_PA6T_UPMC5 776
+#define SPRN_PA6T_UMMCR0 779
+#define SPRN_PA6T_UMMCR1 782
+#define SPRN_PA6T_PMC0  787
+#define SPRN_PA6T_PMC1  788
+#define SPRN_PA6T_PMC2  789
+#define SPRN_PA6T_PMC3  790
+#define SPRN_PA6T_PMC4  791
+#define SPRN_PA6T_PMC5  792
 
 #else /* 32-bit */
 #define SPRN_MMCR0	952	/* Monitor Mode Control Register 0 */
diff --git a/include/asm-powerpc/socket.h b/include/asm-powerpc/socket.h
index c8b1da5..403e9fd 100644
--- a/include/asm-powerpc/socket.h
+++ b/include/asm-powerpc/socket.h
@@ -56,5 +56,7 @@
 
 #define SO_PEERSEC		31
 #define SO_PASSSEC		34
+#define SO_TIMESTAMPNS		35
+#define SCM_TIMESTAMPNS		SO_TIMESTAMPNS
 
 #endif	/* _ASM_POWERPC_SOCKET_H */
diff --git a/include/asm-powerpc/sockios.h b/include/asm-powerpc/sockios.h
index 590078d..55cef76 100644
--- a/include/asm-powerpc/sockios.h
+++ b/include/asm-powerpc/sockios.h
@@ -14,6 +14,7 @@
 #define FIOGETOWN	0x8903
 #define SIOCGPGRP	0x8904
 #define SIOCATMARK	0x8905
-#define SIOCGSTAMP	0x8906		/* Get stamp */
+#define SIOCGSTAMP	0x8906		/* Get stamp (timeval) */
+#define SIOCGSTAMPNS	0x8907		/* Get stamp (timespec) */
 
 #endif	/* _ASM_POWERPC_SOCKIOS_H */
diff --git a/include/asm-powerpc/spu_csa.h b/include/asm-powerpc/spu_csa.h
index 8aad061..02e56a6 100644
--- a/include/asm-powerpc/spu_csa.h
+++ b/include/asm-powerpc/spu_csa.h
@@ -242,6 +242,7 @@
 	u64 spu_chnldata_RW[32];
 	u32 spu_mailbox_data[4];
 	u32 pu_mailbox_data[1];
+	u64 dar, dsisr;
 	unsigned long suspend_time;
 	spinlock_t register_lock;
 };
diff --git a/include/asm-powerpc/string.h b/include/asm-powerpc/string.h
index faa407f..aa40f92 100644
--- a/include/asm-powerpc/string.h
+++ b/include/asm-powerpc/string.h
@@ -14,8 +14,6 @@
 #define __HAVE_ARCH_MEMCMP
 #define __HAVE_ARCH_MEMCHR
 
-extern int strcasecmp(const char *, const char *);
-extern int strncasecmp(const char *, const char *, __kernel_size_t);
 extern char * strcpy(char *,const char *);
 extern char * strncpy(char *,const char *, __kernel_size_t);
 extern __kernel_size_t strlen(const char *);
diff --git a/include/asm-powerpc/system.h b/include/asm-powerpc/system.h
index f7b1227..d3e0906 100644
--- a/include/asm-powerpc/system.h
+++ b/include/asm-powerpc/system.h
@@ -131,6 +131,7 @@
 extern void giveup_altivec(struct task_struct *);
 extern void load_up_altivec(struct task_struct *);
 extern int emulate_altivec(struct pt_regs *);
+extern void enable_kernel_spe(void);
 extern void giveup_spe(struct task_struct *);
 extern void load_up_spe(struct task_struct *);
 extern int fix_alignment(struct pt_regs *);
diff --git a/include/asm-powerpc/tlb.h b/include/asm-powerpc/tlb.h
index 4e2a834..0a17682 100644
--- a/include/asm-powerpc/tlb.h
+++ b/include/asm-powerpc/tlb.h
@@ -38,7 +38,6 @@
 
 static inline void tlb_flush(struct mmu_gather *tlb)
 {
-	flush_tlb_pending();
 	pte_free_finish();
 }
 
diff --git a/include/asm-powerpc/tlbflush.h b/include/asm-powerpc/tlbflush.h
index 93c7d0c..86e6266 100644
--- a/include/asm-powerpc/tlbflush.h
+++ b/include/asm-powerpc/tlbflush.h
@@ -17,10 +17,73 @@
  */
 #ifdef __KERNEL__
 
-
 struct mm_struct;
+struct vm_area_struct;
 
-#ifdef CONFIG_PPC64
+#if defined(CONFIG_4xx) || defined(CONFIG_8xx) || defined(CONFIG_FSL_BOOKE)
+/*
+ * TLB flushing for software loaded TLB chips
+ *
+ * TODO: (CONFIG_FSL_BOOKE) determine if flush_tlb_range &
+ * flush_tlb_kernel_range are best implemented as tlbia vs
+ * specific tlbie's
+ */
+
+extern void _tlbie(unsigned long address);
+
+#if defined(CONFIG_40x) || defined(CONFIG_8xx)
+#define _tlbia()	asm volatile ("tlbia; sync" : : : "memory")
+#else /* CONFIG_44x || CONFIG_FSL_BOOKE */
+extern void _tlbia(void);
+#endif
+
+static inline void flush_tlb_mm(struct mm_struct *mm)
+{
+	_tlbia();
+}
+
+static inline void flush_tlb_page(struct vm_area_struct *vma,
+				  unsigned long vmaddr)
+{
+	_tlbie(vmaddr);
+}
+
+static inline void flush_tlb_page_nohash(struct vm_area_struct *vma,
+					 unsigned long vmaddr)
+{
+	_tlbie(vmaddr);
+}
+
+static inline void flush_tlb_range(struct vm_area_struct *vma,
+				   unsigned long start, unsigned long end)
+{
+	_tlbia();
+}
+
+static inline void flush_tlb_kernel_range(unsigned long start,
+					  unsigned long end)
+{
+	_tlbia();
+}
+
+#elif defined(CONFIG_PPC32)
+/*
+ * TLB flushing for "classic" hash-MMMU 32-bit CPUs, 6xx, 7xx, 7xxx
+ */
+extern void _tlbie(unsigned long address);
+extern void _tlbia(void);
+
+extern void flush_tlb_mm(struct mm_struct *mm);
+extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr);
+extern void flush_tlb_page_nohash(struct vm_area_struct *vma, unsigned long addr);
+extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
+			    unsigned long end);
+extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
+
+#else
+/*
+ * TLB flushing for 64-bit has-MMU CPUs
+ */
 
 #include <linux/percpu.h>
 #include <asm/page.h>
@@ -28,46 +91,70 @@
 #define PPC64_TLB_BATCH_NR 192
 
 struct ppc64_tlb_batch {
-	unsigned long index;
-	struct mm_struct *mm;
-	real_pte_t pte[PPC64_TLB_BATCH_NR];
-	unsigned long vaddr[PPC64_TLB_BATCH_NR];
-	unsigned int psize;
+	int			active;
+	unsigned long		index;
+	struct mm_struct	*mm;
+	real_pte_t		pte[PPC64_TLB_BATCH_NR];
+	unsigned long		vaddr[PPC64_TLB_BATCH_NR];
+	unsigned int		psize;
 };
 DECLARE_PER_CPU(struct ppc64_tlb_batch, ppc64_tlb_batch);
 
 extern void __flush_tlb_pending(struct ppc64_tlb_batch *batch);
 
-static inline void flush_tlb_pending(void)
+extern void hpte_need_flush(struct mm_struct *mm, unsigned long addr,
+			    pte_t *ptep, unsigned long pte, int huge);
+
+#define __HAVE_ARCH_ENTER_LAZY_MMU_MODE
+
+static inline void arch_enter_lazy_mmu_mode(void)
 {
-	struct ppc64_tlb_batch *batch = &get_cpu_var(ppc64_tlb_batch);
+	struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch);
+
+	batch->active = 1;
+}
+
+static inline void arch_leave_lazy_mmu_mode(void)
+{
+	struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch);
 
 	if (batch->index)
 		__flush_tlb_pending(batch);
-	put_cpu_var(ppc64_tlb_batch);
+	batch->active = 0;
 }
 
+#define arch_flush_lazy_mmu_mode()      do {} while (0)
+
+
 extern void flush_hash_page(unsigned long va, real_pte_t pte, int psize,
 			    int local);
 extern void flush_hash_range(unsigned long number, int local);
 
-#else /* CONFIG_PPC64 */
 
-#include <linux/mm.h>
+static inline void flush_tlb_mm(struct mm_struct *mm)
+{
+}
 
-extern void _tlbie(unsigned long address);
-extern void _tlbia(void);
+static inline void flush_tlb_page(struct vm_area_struct *vma,
+				  unsigned long vmaddr)
+{
+}
 
-/*
- * TODO: (CONFIG_FSL_BOOKE) determine if flush_tlb_range &
- * flush_tlb_kernel_range are best implemented as tlbia vs
- * specific tlbie's
- */
+static inline void flush_tlb_page_nohash(struct vm_area_struct *vma,
+					 unsigned long vmaddr)
+{
+}
 
-#if (defined(CONFIG_4xx) && !defined(CONFIG_44x)) || defined(CONFIG_8xx)
-#define flush_tlb_pending()	asm volatile ("tlbia; sync" : : : "memory")
-#elif defined(CONFIG_4xx) || defined(CONFIG_FSL_BOOKE)
-#define flush_tlb_pending()	_tlbia()
+static inline void flush_tlb_range(struct vm_area_struct *vma,
+				   unsigned long start, unsigned long end)
+{
+}
+
+static inline void flush_tlb_kernel_range(unsigned long start,
+					  unsigned long end)
+{
+}
+
 #endif
 
 /*
@@ -81,64 +168,13 @@
  */
 extern void update_mmu_cache(struct vm_area_struct *, unsigned long, pte_t);
 
-#endif /* CONFIG_PPC64 */
-
-#if defined(CONFIG_PPC64) || defined(CONFIG_4xx) || \
-	defined(CONFIG_FSL_BOOKE) || defined(CONFIG_8xx)
-
-static inline void flush_tlb_mm(struct mm_struct *mm)
-{
-	flush_tlb_pending();
-}
-
-static inline void flush_tlb_page(struct vm_area_struct *vma,
-				unsigned long vmaddr)
-{
-#ifdef CONFIG_PPC64
-	flush_tlb_pending();
-#else
-	_tlbie(vmaddr);
-#endif
-}
-
-static inline void flush_tlb_page_nohash(struct vm_area_struct *vma,
-					 unsigned long vmaddr)
-{
-#ifndef CONFIG_PPC64
-	_tlbie(vmaddr);
-#endif
-}
-
-static inline void flush_tlb_range(struct vm_area_struct *vma,
-		unsigned long start, unsigned long end)
-{
-	flush_tlb_pending();
-}
-
-static inline void flush_tlb_kernel_range(unsigned long start,
-		unsigned long end)
-{
-	flush_tlb_pending();
-}
-
-#else	/* 6xx, 7xx, 7xxx cpus */
-
-extern void flush_tlb_mm(struct mm_struct *mm);
-extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr);
-extern void flush_tlb_page_nohash(struct vm_area_struct *vma, unsigned long addr);
-extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
-			    unsigned long end);
-extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
-
-#endif
-
 /*
  * This is called in munmap when we have freed up some page-table
  * pages.  We don't need to do anything here, there's nothing special
  * about our page-table pages.  -- paulus
  */
 static inline void flush_tlb_pgtables(struct mm_struct *mm,
-		unsigned long start, unsigned long end)
+				      unsigned long start, unsigned long end)
 {
 }
 
diff --git a/include/asm-powerpc/uaccess.h b/include/asm-powerpc/uaccess.h
index adbf16b..8e798e3 100644
--- a/include/asm-powerpc/uaccess.h
+++ b/include/asm-powerpc/uaccess.h
@@ -110,12 +110,18 @@
 	__get_user_nocheck((x), (ptr), sizeof(*(ptr)))
 #define __put_user(x, ptr) \
 	__put_user_nocheck((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr)))
+
 #ifndef __powerpc64__
 #define __get_user64(x, ptr) \
 	__get_user64_nocheck((x), (ptr), sizeof(*(ptr)))
 #define __put_user64(x, ptr) __put_user(x, ptr)
 #endif
 
+#define __get_user_inatomic(x, ptr) \
+	__get_user_nosleep((x), (ptr), sizeof(*(ptr)))
+#define __put_user_inatomic(x, ptr) \
+	__put_user_nosleep((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr)))
+
 #define __get_user_unaligned __get_user
 #define __put_user_unaligned __put_user
 
@@ -198,6 +204,16 @@
 	__pu_err;							\
 })
 
+#define __put_user_nosleep(x, ptr, size)			\
+({								\
+	long __pu_err;						\
+	__typeof__(*(ptr)) __user *__pu_addr = (ptr);		\
+	__chk_user_ptr(ptr);					\
+	__put_user_size((x), __pu_addr, (size), __pu_err);	\
+	__pu_err;						\
+})
+
+
 extern long __get_user_bad(void);
 
 #define __get_user_asm(x, addr, err, op)		\
@@ -297,6 +313,18 @@
 	__gu_err;							\
 })
 
+#define __get_user_nosleep(x, ptr, size)			\
+({								\
+	long __gu_err;						\
+	unsigned long __gu_val;					\
+	const __typeof__(*(ptr)) __user *__gu_addr = (ptr);	\
+	__chk_user_ptr(ptr);					\
+	__get_user_size(__gu_val, __gu_addr, (size), __gu_err);	\
+	(x) = (__typeof__(*(ptr)))__gu_val;			\
+	__gu_err;						\
+})
+
+
 /* more complex routines */
 
 extern unsigned long __copy_tofrom_user(void __user *to,
diff --git a/include/asm-powerpc/ucc_fast.h b/include/asm-powerpc/ucc_fast.h
index 39d1c90..f529f70 100644
--- a/include/asm-powerpc/ucc_fast.h
+++ b/include/asm-powerpc/ucc_fast.h
@@ -159,6 +159,9 @@
 	struct ucc_fast *uf_regs;	/* a pointer to memory map of UCC regs. */
 	u32 *p_ucce;		/* a pointer to the event register in memory. */
 	u32 *p_uccm;		/* a pointer to the mask register in memory. */
+#ifdef CONFIG_UGETH_TX_ON_DEMAND
+	u16 *p_utodr;		/* pointer to the transmit on demand register */
+#endif
 	int enabled_tx;		/* Whether channel is enabled for Tx (ENT) */
 	int enabled_rx;		/* Whether channel is enabled for Rx (ENR) */
 	int stopped_tx;		/* Whether channel has been stopped for Tx
diff --git a/include/asm-powerpc/uic.h b/include/asm-powerpc/uic.h
new file mode 100644
index 0000000..970eb7e
--- /dev/null
+++ b/include/asm-powerpc/uic.h
@@ -0,0 +1,23 @@
+/*
+ * include/asm-powerpc/uic.h
+ *
+ * IBM PPC4xx UIC external definitions and structure.
+ *
+ * Maintainer: David Gibson <dwg@au1.ibm.com>
+ * Copyright 2007 IBM Corporation.
+ *
+ * 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.
+ */
+#ifndef _ASM_POWERPC_UIC_H
+#define _ASM_POWERPC_UIC_H
+
+#ifdef __KERNEL__
+
+extern void __init uic_init_tree(void);
+extern unsigned int uic_get_irq(void);
+
+#endif /* __KERNEL__ */
+#endif /* _ASM_POWERPC_UIC_H */
diff --git a/include/asm-ppc/ibm4xx.h b/include/asm-ppc/ibm4xx.h
index 92fd02d..ed6891a 100644
--- a/include/asm-ppc/ibm4xx.h
+++ b/include/asm-ppc/ibm4xx.h
@@ -47,12 +47,8 @@
 #include <platforms/4xx/walnut.h>
 #endif
 
-#if defined(CONFIG_XILINX_ML300)
-#include <platforms/4xx/xilinx_ml300.h>
-#endif
-
-#if defined(CONFIG_XILINX_ML403)
-#include <platforms/4xx/xilinx_ml403.h>
+#if defined(CONFIG_XILINX_VIRTEX)
+#include <platforms/4xx/virtex.h>
 #endif
 
 #ifndef __ASSEMBLY__
diff --git a/include/asm-ppc/ppc_sys.h b/include/asm-ppc/ppc_sys.h
index 40f197a..de99e92 100644
--- a/include/asm-ppc/ppc_sys.h
+++ b/include/asm-ppc/ppc_sys.h
@@ -33,8 +33,6 @@
 #include <asm/mpc52xx.h>
 #elif defined(CONFIG_MPC10X_BRIDGE)
 #include <asm/mpc10x.h>
-#elif defined(CONFIG_XILINX_VIRTEX)
-#include <platforms/4xx/virtex.h>
 #else
 #error "need definition of ppc_sys_devices"
 #endif
diff --git a/include/asm-ppc/prom.h b/include/asm-ppc/prom.h
index adc5ae7..901f7fa 100644
--- a/include/asm-ppc/prom.h
+++ b/include/asm-ppc/prom.h
@@ -34,7 +34,8 @@
  */
 #define machine_is_compatible(x)		0
 #define of_find_compatible_node(f, t, c)	NULL
-#define get_property(p, n, l)			NULL
+#define of_get_property(p, n, l)		NULL
+#define get_property(a, b, c)			of_get_property((a), (b), (c))
 
 #endif /* _PPC_PROM_H */
 #endif /* __KERNEL__ */
diff --git a/include/asm-s390/bug.h b/include/asm-s390/bug.h
index 8768983..838684d 100644
--- a/include/asm-s390/bug.h
+++ b/include/asm-s390/bug.h
@@ -1,27 +1,70 @@
-#ifndef _S390_BUG_H
-#define _S390_BUG_H
+#ifndef _ASM_S390_BUG_H
+#define _ASM_S390_BUG_H
 
 #include <linux/kernel.h>
 
 #ifdef CONFIG_BUG
 
-static inline __attribute__((noreturn)) void __do_illegal_op(void)
-{
-#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
-	__builtin_trap();
+#ifdef CONFIG_64BIT
+#define S390_LONG ".quad"
 #else
-	asm volatile(".long 0");
+#define S390_LONG ".long"
 #endif
-}
 
-#define BUG() do { \
-	printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \
-	__do_illegal_op(); \
+#ifdef CONFIG_DEBUG_BUGVERBOSE
+
+#define __EMIT_BUG(x) do {					\
+	asm volatile(						\
+		"0:	j	0b+2\n"				\
+		"1:\n"						\
+		".section .rodata.str,\"aMS\",@progbits,1\n"	\
+		"2:	.asciz	\""__FILE__"\"\n"		\
+		".previous\n"					\
+		".section __bug_table,\"a\"\n"			\
+		"3:\t"	S390_LONG "\t1b,2b\n"			\
+		"	.short	%0,%1\n"			\
+		"	.org	3b+%2\n"			\
+		".previous\n"					\
+		: : "i" (__LINE__),				\
+		    "i" (x),					\
+		    "i" (sizeof(struct bug_entry)));		\
 } while (0)
 
+#else /* CONFIG_DEBUG_BUGVERBOSE */
+
+#define __EMIT_BUG(x) do {				\
+	asm volatile(					\
+		"0:	j	0b+2\n"			\
+		"1:\n"					\
+		".section __bug_table,\"a\"\n"		\
+		"2:\t"	S390_LONG "\t1b\n"		\
+		"	.short	%0\n"			\
+		"	.org	2b+%1\n"		\
+		".previous\n"				\
+		: : "i" (x),				\
+		    "i" (sizeof(struct bug_entry)));	\
+} while (0)
+
+#endif /* CONFIG_DEBUG_BUGVERBOSE */
+
+#define BUG()	__EMIT_BUG(0)
+
+#define WARN_ON(x) ({					\
+	typeof(x) __ret_warn_on = (x);			\
+	if (__builtin_constant_p(__ret_warn_on)) {	\
+		if (__ret_warn_on)			\
+			__EMIT_BUG(BUGFLAG_WARNING);	\
+	} else {					\
+		if (unlikely(__ret_warn_on))		\
+			__EMIT_BUG(BUGFLAG_WARNING);	\
+	}						\
+	unlikely(__ret_warn_on);			\
+})
+
 #define HAVE_ARCH_BUG
-#endif
+#define HAVE_ARCH_WARN_ON
+#endif /* CONFIG_BUG */
 
 #include <asm-generic/bug.h>
 
-#endif
+#endif /* _ASM_S390_BUG_H */
diff --git a/include/asm-s390/ccwdev.h b/include/asm-s390/ccwdev.h
index cfc8153..6795ece 100644
--- a/include/asm-s390/ccwdev.h
+++ b/include/asm-s390/ccwdev.h
@@ -164,9 +164,9 @@
 extern int ccw_device_halt(struct ccw_device *, unsigned long);
 extern int ccw_device_clear(struct ccw_device *, unsigned long);
 
-extern int read_dev_chars(struct ccw_device *cdev, void **buffer, int length);
-extern int read_conf_data(struct ccw_device *cdev, void **buffer, int *length);
-extern int read_conf_data_lpm(struct ccw_device *cdev, void **buffer,
+extern int __deprecated read_dev_chars(struct ccw_device *cdev, void **buffer, int length);
+extern int __deprecated read_conf_data(struct ccw_device *cdev, void **buffer, int *length);
+extern int __deprecated read_conf_data_lpm(struct ccw_device *cdev, void **buffer,
 			      int *length, __u8 lpm);
 
 extern int ccw_device_set_online(struct ccw_device *cdev);
diff --git a/include/asm-s390/ccwgroup.h b/include/asm-s390/ccwgroup.h
index d2f9c0d..925b3dd 100644
--- a/include/asm-s390/ccwgroup.h
+++ b/include/asm-s390/ccwgroup.h
@@ -11,6 +11,7 @@
 		CCWGROUP_ONLINE,
 	} state;
 	atomic_t onoff;
+	struct mutex reg_mutex;
 	unsigned int count;		/* number of attached slave devices */
 	struct device	dev;		/* master device		    */
 	struct ccw_device *cdev[0];	/* variable number, allocate as needed */
diff --git a/include/asm-s390/chpid.h b/include/asm-s390/chpid.h
new file mode 100644
index 0000000..b203336
--- /dev/null
+++ b/include/asm-s390/chpid.h
@@ -0,0 +1,53 @@
+/*
+ *  drivers/s390/cio/chpid.h
+ *
+ *    Copyright IBM Corp. 2007
+ *    Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#ifndef _ASM_S390_CHPID_H
+#define _ASM_S390_CHPID_H _ASM_S390_CHPID_H
+
+#include <linux/string.h>
+#include <asm/types.h>
+#include <asm/cio.h>
+
+#define __MAX_CHPID 255
+
+struct chp_id {
+	u8 reserved1;
+	u8 cssid;
+	u8 reserved2;
+	u8 id;
+} __attribute__((packed));
+
+static inline void chp_id_init(struct chp_id *chpid)
+{
+	memset(chpid, 0, sizeof(struct chp_id));
+}
+
+static inline int chp_id_is_equal(struct chp_id *a, struct chp_id *b)
+{
+	return (a->id == b->id) && (a->cssid == b->cssid);
+}
+
+static inline void chp_id_next(struct chp_id *chpid)
+{
+	if (chpid->id < __MAX_CHPID)
+		chpid->id++;
+	else {
+		chpid->id = 0;
+		chpid->cssid++;
+	}
+}
+
+static inline int chp_id_is_valid(struct chp_id *chpid)
+{
+	return (chpid->cssid <= __MAX_CSSID);
+}
+
+
+#define chp_id_for_each(c) \
+	for (chp_id_init(c); chp_id_is_valid(c); chp_id_next(c))
+
+#endif /* _ASM_S390_CHPID_H */
diff --git a/include/asm-s390/cio.h b/include/asm-s390/cio.h
index d927850..f738d28 100644
--- a/include/asm-s390/cio.h
+++ b/include/asm-s390/cio.h
@@ -13,6 +13,7 @@
 #ifdef __KERNEL__
 
 #define LPM_ANYPATH 0xff
+#define __MAX_CSSID 0
 
 /*
  * subchannel status word
@@ -292,6 +293,13 @@
 
 extern void reipl_ccw_dev(struct ccw_dev_id *id);
 
+struct cio_iplinfo {
+	u16 devno;
+	int is_qdio;
+};
+
+extern int cio_get_iplinfo(struct cio_iplinfo *iplinfo);
+
 #endif
 
 #endif
diff --git a/include/asm-s390/elf.h b/include/asm-s390/elf.h
index c0d629d..91d0632 100644
--- a/include/asm-s390/elf.h
+++ b/include/asm-s390/elf.h
@@ -188,7 +188,8 @@
 /* This yields a mask that user programs can use to figure out what
    instruction set this CPU supports. */
 
-#define ELF_HWCAP (0)
+extern unsigned long elf_hwcap;
+#define ELF_HWCAP (elf_hwcap)
 
 /* This yields a string that ld.so will use to load implementation
    specific libraries for optimization.  This is more specific in
@@ -197,7 +198,9 @@
    For the moment, we have only optimizations for the Intel generations,
    but that could change... */
 
-#define ELF_PLATFORM (NULL)
+#define ELF_PLATFORM_SIZE 8
+extern char elf_platform[];
+#define ELF_PLATFORM (elf_platform)
 
 #ifndef __s390x__
 #define SET_PERSONALITY(ex, ibcs2) set_personality((ibcs2)?PER_SVR4:PER_LINUX)
diff --git a/include/asm-s390/ipl.h b/include/asm-s390/ipl.h
index 0eb6408..bdcd448 100644
--- a/include/asm-s390/ipl.h
+++ b/include/asm-s390/ipl.h
@@ -8,6 +8,8 @@
 #define _ASM_S390_IPL_H
 
 #include <asm/types.h>
+#include <asm/cio.h>
+#include <asm/setup.h>
 
 #define IPL_PARMBLOCK_ORIGIN	0x2000
 
@@ -74,12 +76,12 @@
 } __attribute__((packed));
 
 /*
- * IPL validity flags and parameters as detected in head.S
+ * IPL validity flags
  */
 extern u32 ipl_flags;
-extern u16 ipl_devno;
 
 extern u32 dump_prefix_page;
+
 extern void do_reipl(void);
 extern void ipl_save_parameters(void);
 
@@ -89,6 +91,35 @@
 	IPL_NSS_VALID		= 4,
 };
 
+enum ipl_type {
+	IPL_TYPE_UNKNOWN	= 1,
+	IPL_TYPE_CCW		= 2,
+	IPL_TYPE_FCP		= 4,
+	IPL_TYPE_FCP_DUMP	= 8,
+	IPL_TYPE_NSS		= 16,
+};
+
+struct ipl_info
+{
+	enum ipl_type type;
+	union {
+		struct {
+			struct ccw_dev_id dev_id;
+		} ccw;
+		struct {
+			struct ccw_dev_id dev_id;
+			u64 wwpn;
+			u64 lun;
+		} fcp;
+		struct {
+			char name[NSS_NAME_SIZE + 1];
+		} nss;
+	} data;
+};
+
+extern struct ipl_info ipl_info;
+extern void setup_ipl_info(void);
+
 /*
  * DIAG 308 support
  */
diff --git a/include/asm-s390/kdebug.h b/include/asm-s390/kdebug.h
index 1b50f89..d2d7ad2 100644
--- a/include/asm-s390/kdebug.h
+++ b/include/asm-s390/kdebug.h
@@ -22,8 +22,21 @@
  */
 extern int register_die_notifier(struct notifier_block *);
 extern int unregister_die_notifier(struct notifier_block *);
-extern int register_page_fault_notifier(struct notifier_block *);
-extern int unregister_page_fault_notifier(struct notifier_block *);
+
+/*
+ * These are only here because kprobes.c wants them to implement a
+ * blatant layering violation. Will hopefully go away soon once all
+ * architectures are updated.
+ */
+static inline int register_page_fault_notifier(struct notifier_block *nb)
+{
+	return 0;
+}
+static inline int unregister_page_fault_notifier(struct notifier_block *nb)
+{
+	return 0;
+}
+
 extern struct atomic_notifier_head s390die_chain;
 
 enum die_val {
@@ -39,7 +52,6 @@
 	DIE_GPF,
 	DIE_CALL,
 	DIE_NMI_IPI,
-	DIE_PAGE_FAULT,
 };
 
 static inline int notify_die(enum die_val val, const char *str,
diff --git a/include/asm-s390/kprobes.h b/include/asm-s390/kprobes.h
index b847ff0..830fe4c 100644
--- a/include/asm-s390/kprobes.h
+++ b/include/asm-s390/kprobes.h
@@ -97,18 +97,10 @@
 int  is_prohibited_opcode(kprobe_opcode_t *instruction);
 void get_instruction_type(struct arch_specific_insn *ainsn);
 
+int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
+int kprobe_exceptions_notify(struct notifier_block *self,
+	unsigned long val, void *data);
+
 #define flush_insn_slot(p)	do { } while (0)
 
 #endif	/* _ASM_S390_KPROBES_H */
-
-#ifdef CONFIG_KPROBES
-
-extern int kprobe_exceptions_notify(struct notifier_block *self,
-					unsigned long val, void *data);
-#else	/* !CONFIG_KPROBES */
-static inline int kprobe_exceptions_notify(struct notifier_block *self,
-						unsigned long val, void *data)
-{
-	return 0;
-}
-#endif
diff --git a/include/asm-s390/lowcore.h b/include/asm-s390/lowcore.h
index 4a31d0a..801a6fd 100644
--- a/include/asm-s390/lowcore.h
+++ b/include/asm-s390/lowcore.h
@@ -147,6 +147,52 @@
 void mcck_int_handler(void);
 void io_int_handler(void);
 
+struct save_area_s390 {
+	u32	ext_save;
+	u64	timer;
+	u64	clk_cmp;
+	u8	pad1[24];
+	u8	psw[8];
+	u32	pref_reg;
+	u8	pad2[20];
+	u32	acc_regs[16];
+	u64	fp_regs[4];
+	u32	gp_regs[16];
+	u32	ctrl_regs[16];
+}  __attribute__((packed));
+
+struct save_area_s390x {
+	u64	fp_regs[16];
+	u64	gp_regs[16];
+	u8	psw[16];
+	u8	pad1[8];
+	u32	pref_reg;
+	u32	fp_ctrl_reg;
+	u8	pad2[4];
+	u32	tod_reg;
+	u64	timer;
+	u64	clk_cmp;
+	u8	pad3[8];
+	u32	acc_regs[16];
+	u64	ctrl_regs[16];
+}  __attribute__((packed));
+
+union save_area {
+	struct save_area_s390	s390;
+	struct save_area_s390x	s390x;
+};
+
+#define SAVE_AREA_BASE_S390	0xd4
+#define SAVE_AREA_BASE_S390X	0x1200
+
+#ifndef __s390x__
+#define SAVE_AREA_SIZE sizeof(struct save_area_s390)
+#define SAVE_AREA_BASE SAVE_AREA_BASE_S390
+#else
+#define SAVE_AREA_SIZE sizeof(struct save_area_s390x)
+#define SAVE_AREA_BASE SAVE_AREA_BASE_S390X
+#endif
+
 struct _lowcore
 {
 #ifndef __s390x__
@@ -183,17 +229,19 @@
 	__u16        subchannel_nr;            /* 0x0ba */
 	__u32        io_int_parm;              /* 0x0bc */
 	__u32        io_int_word;              /* 0x0c0 */
-        __u8         pad3[0xD4-0xC4];          /* 0x0c4 */
+	__u8	     pad3[0xc8-0xc4];	       /* 0x0c4 */
+	__u32	     stfl_fac_list;	       /* 0x0c8 */
+	__u8	     pad4[0xd4-0xcc];	       /* 0x0cc */
 	__u32        extended_save_area_addr;  /* 0x0d4 */
 	__u32        cpu_timer_save_area[2];   /* 0x0d8 */
 	__u32        clock_comp_save_area[2];  /* 0x0e0 */
 	__u32        mcck_interruption_code[2]; /* 0x0e8 */
-	__u8         pad4[0xf4-0xf0];          /* 0x0f0 */
+	__u8	     pad5[0xf4-0xf0];	       /* 0x0f0 */
 	__u32        external_damage_code;     /* 0x0f4 */
 	__u32        failing_storage_address;  /* 0x0f8 */
-	__u8         pad5[0x100-0xfc];         /* 0x0fc */
+	__u8	     pad6[0x100-0xfc];	       /* 0x0fc */
 	__u32        st_status_fixed_logout[4];/* 0x100 */
-	__u8         pad6[0x120-0x110];        /* 0x110 */
+	__u8	     pad7[0x120-0x110];        /* 0x110 */
 	__u32        access_regs_save_area[16];/* 0x120 */
 	__u32        floating_pt_save_area[8]; /* 0x160 */
 	__u32        gpregs_save_area[16];     /* 0x180 */
diff --git a/include/asm-s390/pgtable.h b/include/asm-s390/pgtable.h
index 13c1654..8fe8d42 100644
--- a/include/asm-s390/pgtable.h
+++ b/include/asm-s390/pgtable.h
@@ -753,14 +753,14 @@
  * should therefore only be called if it is not mapped in any
  * address space.
  */
-static inline int page_test_and_clear_dirty(struct page *page)
+static inline int page_test_dirty(struct page *page)
 {
-	unsigned long physpage = page_to_phys(page);
-	int skey = page_get_storage_key(physpage);
+	return (page_get_storage_key(page_to_phys(page)) & _PAGE_CHANGED) != 0;
+}
 
-	if (skey & _PAGE_CHANGED)
-		page_set_storage_key(physpage, skey & ~_PAGE_CHANGED);
-	return skey & _PAGE_CHANGED;
+static inline void page_clear_dirty(struct page *page)
+{
+	page_set_storage_key(page_to_phys(page), PAGE_DEFAULT_KEY);
 }
 
 /*
@@ -953,7 +953,8 @@
 #define __HAVE_ARCH_PTEP_CLEAR_FLUSH
 #define __HAVE_ARCH_PTEP_SET_WRPROTECT
 #define __HAVE_ARCH_PTE_SAME
-#define __HAVE_ARCH_PAGE_TEST_AND_CLEAR_DIRTY
+#define __HAVE_ARCH_PAGE_TEST_DIRTY
+#define __HAVE_ARCH_PAGE_CLEAR_DIRTY
 #define __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG
 #include <asm-generic/pgtable.h>
 
diff --git a/include/asm-s390/processor.h b/include/asm-s390/processor.h
index 33b80ce..e0fcea8 100644
--- a/include/asm-s390/processor.h
+++ b/include/asm-s390/processor.h
@@ -57,6 +57,7 @@
 
 extern void s390_adjust_jiffies(void);
 extern void print_cpu_info(struct cpuinfo_S390 *);
+extern int get_cpu_capability(unsigned int *);
 
 /* Lazy FPU handling on uni-processor */
 extern struct task_struct *last_task_used_math;
@@ -196,6 +197,7 @@
 extern char *task_show_regs(struct task_struct *task, char *buffer);
 
 extern void show_registers(struct pt_regs *regs);
+extern void show_code(struct pt_regs *regs);
 extern void show_trace(struct task_struct *task, unsigned long *sp);
 
 unsigned long get_wchan(struct task_struct *p);
diff --git a/include/asm-s390/sclp.h b/include/asm-s390/sclp.h
index 468b970..21ed647 100644
--- a/include/asm-s390/sclp.h
+++ b/include/asm-s390/sclp.h
@@ -9,6 +9,7 @@
 #define _ASM_S390_SCLP_H
 
 #include <linux/types.h>
+#include <asm/chpid.h>
 
 struct sccb_header {
 	u16	length;
@@ -33,7 +34,20 @@
 	u8	_reserved3[4096 - 112];	/* 112-4095 */
 } __attribute__((packed, aligned(4096)));
 
+#define SCLP_CHP_INFO_MASK_SIZE		32
+
+struct sclp_chp_info {
+	u8 recognized[SCLP_CHP_INFO_MASK_SIZE];
+	u8 standby[SCLP_CHP_INFO_MASK_SIZE];
+	u8 configured[SCLP_CHP_INFO_MASK_SIZE];
+};
+
 extern struct sclp_readinfo_sccb s390_readinfo_sccb;
 extern void sclp_readinfo_early(void);
+extern int sclp_sdias_blk_count(void);
+extern int sclp_sdias_copy(void *dest, int blk_num, int nr_blks);
+extern int sclp_chp_configure(struct chp_id chpid);
+extern int sclp_chp_deconfigure(struct chp_id chpid);
+extern int sclp_chp_read_info(struct sclp_chp_info *info);
 
 #endif /* _ASM_S390_SCLP_H */
diff --git a/include/asm-s390/setup.h b/include/asm-s390/setup.h
index 44c7aee..a76a6b8 100644
--- a/include/asm-s390/setup.h
+++ b/include/asm-s390/setup.h
@@ -40,6 +40,7 @@
 };
 
 extern struct mem_chunk memory_chunk[];
+extern unsigned long real_memory_size;
 
 #ifdef CONFIG_S390_SWITCH_AMODE
 extern unsigned int switch_amode;
@@ -77,6 +78,7 @@
 #endif /* __s390x__ */
 
 #define MACHINE_HAS_SCLP	(!MACHINE_IS_P390)
+#define ZFCPDUMP_HSA_SIZE	(32UL<<20)
 
 /*
  * Console mode. Override with conmode=
diff --git a/include/asm-s390/smp.h b/include/asm-s390/smp.h
index b957e4c..0a28e6d 100644
--- a/include/asm-s390/smp.h
+++ b/include/asm-s390/smp.h
@@ -54,9 +54,6 @@
 
 #define raw_smp_processor_id()	(S390_lowcore.cpu_data.cpu_nr)
 
-extern int smp_get_cpu(cpumask_t cpu_map);
-extern void smp_put_cpu(int cpu);
-
 static inline __u16 hard_smp_processor_id(void)
 {
         __u16 cpu_address;
@@ -114,9 +111,8 @@
 }
 
 #define smp_cpu_not_running(cpu)	1
-#define smp_get_cpu(cpu) ({ 0; })
-#define smp_put_cpu(cpu) ({ 0; })
 #define smp_setup_cpu_possible_map()	do { } while (0)
 #endif
 
+extern union save_area *zfcpdump_save_areas[NR_CPUS + 1];
 #endif
diff --git a/include/asm-s390/socket.h b/include/asm-s390/socket.h
index 1778a49..1161ebe 100644
--- a/include/asm-s390/socket.h
+++ b/include/asm-s390/socket.h
@@ -57,5 +57,7 @@
 
 #define SO_PEERSEC		31
 #define SO_PASSSEC		34
+#define SO_TIMESTAMPNS		35
+#define SCM_TIMESTAMPNS		SO_TIMESTAMPNS
 
 #endif /* _ASM_SOCKET_H */
diff --git a/include/asm-s390/sockios.h b/include/asm-s390/sockios.h
index 412aeb4d..f4fc16c 100644
--- a/include/asm-s390/sockios.h
+++ b/include/asm-s390/sockios.h
@@ -15,6 +15,7 @@
 #define FIOGETOWN	0x8903
 #define SIOCGPGRP	0x8904
 #define SIOCATMARK	0x8905
-#define SIOCGSTAMP	0x8906		/* Get stamp */
+#define SIOCGSTAMP	0x8906		/* Get stamp (timeval) */
+#define SIOCGSTAMPNS	0x8907		/* Get stamp (timespec) */
 
 #endif
diff --git a/include/asm-sh/socket.h b/include/asm-sh/socket.h
index ca70362..c48d6fc 100644
--- a/include/asm-sh/socket.h
+++ b/include/asm-sh/socket.h
@@ -49,5 +49,7 @@
 
 #define SO_PEERSEC		31
 #define SO_PASSSEC		34
+#define SO_TIMESTAMPNS		35
+#define SCM_TIMESTAMPNS		SO_TIMESTAMPNS
 
 #endif /* __ASM_SH_SOCKET_H */
diff --git a/include/asm-sh/sockios.h b/include/asm-sh/sockios.h
index 08a71df..cf8b96b 100644
--- a/include/asm-sh/sockios.h
+++ b/include/asm-sh/sockios.h
@@ -9,5 +9,6 @@
 #define SIOCSPGRP	_IOW('s', 8, pid_t)
 #define SIOCGPGRP	_IOR('s', 9, pid_t)
 
-#define SIOCGSTAMP	_IOR('s', 100, struct timeval) /* Get stamp - linux-specific */
+#define SIOCGSTAMP	_IOR('s', 100, struct timeval) /* Get stamp (timeval) */
+#define SIOCGSTAMPNS	_IOR('s', 101, struct timespec) /* Get stamp (timespec) */
 #endif /* __ASM_SH_SOCKIOS_H */
diff --git a/include/asm-sh/string.h b/include/asm-sh/string.h
index 95bc7db..55f8db6 100644
--- a/include/asm-sh/string.h
+++ b/include/asm-sh/string.h
@@ -126,9 +126,6 @@
 #define __HAVE_ARCH_STRLEN
 extern size_t strlen(const char *);
 
-/* arch/sh/lib/strcasecmp.c */
-extern int strcasecmp(const char *, const char *);
-
 #endif /* __KERNEL__ */
 
 #endif /* __ASM_SH_STRING_H */
diff --git a/include/asm-sh64/sockios.h b/include/asm-sh64/sockios.h
index 1ae23ae..419e76f 100644
--- a/include/asm-sh64/sockios.h
+++ b/include/asm-sh64/sockios.h
@@ -20,5 +20,6 @@
 #define SIOCSPGRP	_IOW('s', 8, pid_t)
 #define SIOCGPGRP	_IOR('s', 9, pid_t)
 
-#define SIOCGSTAMP	_IOR('s', 100, struct timeval) /* Get stamp - linux-specific */
+#define SIOCGSTAMP	_IOR('s', 100, struct timeval) /* Get stamp (timeval) */
+#define SIOCGSTAMPNS	_IOR('s', 101, struct timespec) /* Get stamp (timespec) */
 #endif /* __ASM_SH64_SOCKIOS_H */
diff --git a/include/asm-sparc/prom.h b/include/asm-sparc/prom.h
index 274868d..9ea105e 100644
--- a/include/asm-sparc/prom.h
+++ b/include/asm-sparc/prom.h
@@ -35,8 +35,8 @@
 };
 
 struct device_node {
-	char	*name;
-	char	*type;
+	const char	*name;
+	const char	*type;
 	phandle	node;
 	char	*path_component_name;
 	char	*full_name;
@@ -85,12 +85,14 @@
 extern struct device_node *of_get_parent(const struct device_node *node);
 extern struct device_node *of_get_next_child(const struct device_node *node,
 					     struct device_node *prev);
-extern struct property *of_find_property(struct device_node *np,
+extern struct property *of_find_property(const struct device_node *np,
 					 const char *name,
 					 int *lenp);
-extern int of_device_is_compatible(struct device_node *device, const char *);
-extern void *of_get_property(struct device_node *node, const char *name,
-			     int *lenp);
+extern int of_device_is_compatible(const struct device_node *device,
+				   const char *);
+extern const void *of_get_property(const struct device_node *node,
+				   const char *name,
+				   int *lenp);
 #define get_property(node,name,lenp) of_get_property(node,name,lenp)
 extern int of_set_property(struct device_node *node, const char *name, void *val, int len);
 extern int of_getintprop_default(struct device_node *np,
diff --git a/include/asm-sparc/socket.h b/include/asm-sparc/socket.h
index f6c4e5b..7c14239 100644
--- a/include/asm-sparc/socket.h
+++ b/include/asm-sparc/socket.h
@@ -49,6 +49,8 @@
 
 #define SO_PEERSEC		0x001e
 #define SO_PASSSEC		0x001f
+#define SO_TIMESTAMPNS		0x0021
+#define SCM_TIMESTAMPNS		SO_TIMESTAMPNS
 
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_SECURITY_AUTHENTICATION		0x5001
diff --git a/include/asm-sparc/sockios.h b/include/asm-sparc/sockios.h
index 0c01b59..990ea74 100644
--- a/include/asm-sparc/sockios.h
+++ b/include/asm-sparc/sockios.h
@@ -7,7 +7,8 @@
 #define FIOGETOWN	0x8903
 #define SIOCGPGRP	0x8904
 #define SIOCATMARK	0x8905
-#define SIOCGSTAMP	0x8906		/* Get stamp */
+#define SIOCGSTAMP	0x8906		/* Get stamp (timeval) */
+#define SIOCGSTAMPNS	0x8907		/* Get stamp (timespec) */
 
 #endif /* !(_ASM_SPARC_SOCKIOS_H) */
 
diff --git a/include/asm-sparc64/cpudata.h b/include/asm-sparc64/cpudata.h
index f2cc941..e89922d 100644
--- a/include/asm-sparc64/cpudata.h
+++ b/include/asm-sparc64/cpudata.h
@@ -17,8 +17,8 @@
 typedef struct {
 	/* Dcache line 1 */
 	unsigned int	__softirq_pending; /* must be 1st, see rtrap.S */
-	unsigned int	multiplier;
-	unsigned int	counter;
+	unsigned int	__pad0_1;
+	unsigned int	__pad0_2;
 	unsigned int	__pad1;
 	unsigned long	clock_tick;	/* %tick's per second */
 	unsigned long	udelay_val;
diff --git a/include/asm-sparc64/device.h b/include/asm-sparc64/device.h
index d8f9872..d5a4559 100644
--- a/include/asm-sparc64/device.h
+++ b/include/asm-sparc64/device.h
@@ -3,5 +3,21 @@
  *
  * This file is released under the GPLv2
  */
-#include <asm-generic/device.h>
+#ifndef _ASM_SPARC64_DEVICE_H
+#define _ASM_SPARC64_DEVICE_H
 
+struct device_node;
+struct of_device;
+
+struct dev_archdata {
+	void			*iommu;
+	void			*stc;
+	void			*host_controller;
+
+	struct device_node	*prom_node;
+	struct of_device	*op;
+
+	unsigned int		msi_num;
+};
+
+#endif /* _ASM_SPARC64_DEVICE_H */
diff --git a/include/asm-sparc64/ebus.h b/include/asm-sparc64/ebus.h
index a4afe9d..9c1c6db 100644
--- a/include/asm-sparc64/ebus.h
+++ b/include/asm-sparc64/ebus.h
@@ -8,7 +8,6 @@
 #ifndef __SPARC64_EBUS_H
 #define __SPARC64_EBUS_H
 
-#include <asm/pbm.h>
 #include <asm/oplib.h>
 #include <asm/prom.h>
 #include <asm/of_device.h>
@@ -41,7 +40,6 @@
 	struct of_device		ofdev;
 	struct linux_ebus		*next;
 	struct linux_ebus_device	*devices;
-	struct pci_pbm_info		*parent;
 	struct pci_dev			*self;
 	int				 index;
 	int				 is_rio;
diff --git a/include/asm-sparc64/floppy.h b/include/asm-sparc64/floppy.h
index 331013a..4aa0925 100644
--- a/include/asm-sparc64/floppy.h
+++ b/include/asm-sparc64/floppy.h
@@ -549,7 +549,7 @@
 	if (!strcmp(edev->prom_node->name, "fdthree"))
 		return 1;
 	if (!strcmp(edev->prom_node->name, "floppy")) {
-		char *compat;
+		const char *compat;
 
 		compat = of_get_property(edev->prom_node,
 					 "compatible", NULL);
@@ -661,7 +661,7 @@
 		struct linux_ebus_device *edev = NULL;
 		unsigned long config = 0;
 		void __iomem *auxio_reg;
-		char *state_prop;
+		const char *state_prop;
 
 		for_each_ebus(ebus) {
 			for_each_ebusdev(edev, ebus) {
diff --git a/include/asm-sparc64/io.h b/include/asm-sparc64/io.h
index 30b912d..ad595b6 100644
--- a/include/asm-sparc64/io.h
+++ b/include/asm-sparc64/io.h
@@ -24,14 +24,6 @@
 #define page_to_phys(page)	(page_to_pfn(page) << PAGE_SHIFT)
 #define BIO_VMERGE_BOUNDARY	8192
 
-/* Different PCI controllers we support have their PCI MEM space
- * mapped to an either 2GB (Psycho) or 4GB (Sabre) aligned area,
- * so need to chop off the top 33 or 32 bits.
- */
-extern unsigned long pci_memspace_mask;
-
-#define bus_dvma_to_mem(__vaddr) ((__vaddr) & pci_memspace_mask)
-
 static __inline__ u8 _inb(unsigned long addr)
 {
 	u8 ret;
diff --git a/include/asm-sparc64/iommu.h b/include/asm-sparc64/iommu.h
index 0de7a3d..e199594 100644
--- a/include/asm-sparc64/iommu.h
+++ b/include/asm-sparc64/iommu.h
@@ -7,15 +7,50 @@
 #define _SPARC64_IOMMU_H
 
 /* The format of an iopte in the page tables. */
-#define IOPTE_VALID   0x8000000000000000UL /* IOPTE is valid                  */
-#define IOPTE_64K     0x2000000000000000UL /* IOPTE is for 64k page           */
-#define IOPTE_STBUF   0x1000000000000000UL /* DVMA can use streaming buffer   */
-#define IOPTE_INTRA   0x0800000000000000UL /* SBUS slot-->slot direct transfer*/
-#define IOPTE_CONTEXT 0x07ff800000000000UL /* Context number		      */
-#define IOPTE_PAGE    0x00007fffffffe000UL /* Physical page number (PA[42:13])*/
-#define IOPTE_CACHE   0x0000000000000010UL /* Cached (in UPA E-cache)         */
-#define IOPTE_WRITE   0x0000000000000002UL /* Writeable                       */
+#define IOPTE_VALID   0x8000000000000000UL
+#define IOPTE_64K     0x2000000000000000UL
+#define IOPTE_STBUF   0x1000000000000000UL
+#define IOPTE_INTRA   0x0800000000000000UL
+#define IOPTE_CONTEXT 0x07ff800000000000UL
+#define IOPTE_PAGE    0x00007fffffffe000UL
+#define IOPTE_CACHE   0x0000000000000010UL
+#define IOPTE_WRITE   0x0000000000000002UL
 
 #define IOMMU_NUM_CTXS	4096
 
+struct iommu_arena {
+	unsigned long	*map;
+	unsigned int	hint;
+	unsigned int	limit;
+};
+
+struct iommu {
+	spinlock_t		lock;
+	struct iommu_arena	arena;
+	iopte_t			*page_table;
+	u32			page_table_map_base;
+	unsigned long		iommu_control;
+	unsigned long		iommu_tsbbase;
+	unsigned long		iommu_flush;
+	unsigned long		iommu_ctxflush;
+	unsigned long		write_complete_reg;
+	unsigned long		dummy_page;
+	unsigned long		dummy_page_pa;
+	unsigned long		ctx_lowest_free;
+	DECLARE_BITMAP(ctx_bitmap, IOMMU_NUM_CTXS);
+	u32			dma_addr_mask;
+};
+
+struct strbuf {
+	int			strbuf_enabled;
+	unsigned long		strbuf_control;
+	unsigned long		strbuf_pflush;
+	unsigned long		strbuf_fsync;
+	unsigned long		strbuf_ctxflush;
+	unsigned long		strbuf_ctxmatch_base;
+	unsigned long		strbuf_flushflag_pa;
+	volatile unsigned long *strbuf_flushflag;
+	volatile unsigned long	__flushflag_buf[(64+(64-1)) / sizeof(long)];
+};
+
 #endif /* !(_SPARC_IOMMU_H) */
diff --git a/include/asm-sparc64/isa.h b/include/asm-sparc64/isa.h
index d9728b9..ecd9290 100644
--- a/include/asm-sparc64/isa.h
+++ b/include/asm-sparc64/isa.h
@@ -7,7 +7,6 @@
 #ifndef __SPARC64_ISA_H
 #define __SPARC64_ISA_H
 
-#include <asm/pbm.h>
 #include <asm/oplib.h>
 #include <asm/prom.h>
 #include <asm/of_device.h>
@@ -29,7 +28,6 @@
 	struct of_device	ofdev;
 	struct sparc_isa_bridge	*next;
 	struct sparc_isa_device	*devices;
-	struct pci_pbm_info	*parent;
 	struct pci_dev		*self;
 	int			index;
 	struct device_node	*prom_node;
diff --git a/include/asm-sparc64/parport.h b/include/asm-sparc64/parport.h
index 284dfd0..6340a52 100644
--- a/include/asm-sparc64/parport.h
+++ b/include/asm-sparc64/parport.h
@@ -103,7 +103,7 @@
 	if (!strcmp(edev->prom_node->name, "ecpp"))
 		return 1;
 	if (!strcmp(edev->prom_node->name, "parallel")) {
-		char *compat;
+		const char *compat;
 
 		compat = of_get_property(edev->prom_node,
 					 "compatible", NULL);
diff --git a/include/asm-sparc64/pbm.h b/include/asm-sparc64/pbm.h
index 7a246d8..c008cecc 100644
--- a/include/asm-sparc64/pbm.h
+++ b/include/asm-sparc64/pbm.h
@@ -1,7 +1,6 @@
-/* $Id: pbm.h,v 1.27 2001/08/12 13:18:23 davem Exp $
- * pbm.h: UltraSparc PCI controller software state.
+/* pbm.h: UltraSparc PCI controller software state.
  *
- * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1997, 1998, 1999, 2007 David S. Miller (davem@davemloft.net)
  */
 
 #ifndef __SPARC64_PBM_H
@@ -30,90 +29,7 @@
  * PCI bus.
  */
 
-struct pci_controller_info;
-
-/* This contains the software state necessary to drive a PCI
- * controller's IOMMU.
- */
-struct pci_iommu_arena {
-	unsigned long	*map;
-	unsigned int	hint;
-	unsigned int	limit;
-};
-
-struct pci_iommu {
-	/* This protects the controller's IOMMU and all
-	 * streaming buffers underneath.
-	 */
-	spinlock_t	lock;
-
-	struct pci_iommu_arena arena;
-
-	/* IOMMU page table, a linear array of ioptes. */
-	iopte_t		*page_table;		/* The page table itself. */
-
-	/* Base PCI memory space address where IOMMU mappings
-	 * begin.
-	 */
-	u32		page_table_map_base;
-
-	/* IOMMU Controller Registers */
-	unsigned long	iommu_control;		/* IOMMU control register */
-	unsigned long	iommu_tsbbase;		/* IOMMU page table base register */
-	unsigned long	iommu_flush;		/* IOMMU page flush register */
-	unsigned long	iommu_ctxflush;		/* IOMMU context flush register */
-
-	/* This is a register in the PCI controller, which if
-	 * read will have no side-effects but will guarantee
-	 * completion of all previous writes into IOMMU/STC.
-	 */
-	unsigned long	write_complete_reg;
-
-	/* In order to deal with some buggy third-party PCI bridges that
-	 * do wrong prefetching, we never mark valid mappings as invalid.
-	 * Instead we point them at this dummy page.
-	 */
-	unsigned long	dummy_page;
-	unsigned long	dummy_page_pa;
-
-	/* CTX allocation. */
-	unsigned long ctx_lowest_free;
-	unsigned long ctx_bitmap[IOMMU_NUM_CTXS / (sizeof(unsigned long) * 8)];
-
-	/* Here a PCI controller driver describes the areas of
-	 * PCI memory space where DMA to/from physical memory
-	 * are addressed.  Drivers interrogate the PCI layer
-	 * if their device has addressing limitations.  They
-	 * do so via pci_dma_supported, and pass in a mask of
-	 * DMA address bits their device can actually drive.
-	 *
-	 * The test for being usable is:
-	 * 	(device_mask & dma_addr_mask) == dma_addr_mask
-	 */
-	u32 dma_addr_mask;
-};
-
-extern void pci_iommu_table_init(struct pci_iommu *iommu, int tsbsize, u32 dma_offset, u32 dma_addr_mask);
-
-/* This describes a PCI bus module's streaming buffer. */
-struct pci_strbuf {
-	int		strbuf_enabled;		/* Present and using it? */
-
-	/* Streaming Buffer Control Registers */
-	unsigned long	strbuf_control;		/* STC control register */
-	unsigned long	strbuf_pflush;		/* STC page flush register */
-	unsigned long	strbuf_fsync;		/* STC flush synchronization reg */
-	unsigned long	strbuf_ctxflush;	/* STC context flush register */
-	unsigned long	strbuf_ctxmatch_base;	/* STC context flush match reg */
-	unsigned long	strbuf_flushflag_pa;	/* Physical address of flush flag */
-	volatile unsigned long *strbuf_flushflag; /* The flush flag itself */
-
-	/* And this is the actual flush flag area.
-	 * We allocate extra because the chips require
-	 * a 64-byte aligned area.
-	 */
-	volatile unsigned long	__flushflag_buf[(64 + (64 - 1)) / sizeof(long)];
-};
+extern void pci_iommu_table_init(struct iommu *iommu, int tsbsize, u32 dma_offset, u32 dma_addr_mask);
 
 #define PCI_STC_FLUSHFLAG_INIT(STC) \
 	(*((STC)->strbuf_flushflag) = 0UL)
@@ -126,6 +42,8 @@
 #define PROM_PCIRNG_MAX		64
 #define PROM_PCIIMAP_MAX	64
 
+struct pci_controller_info;
+
 struct pci_pbm_info {
 	/* PCI controller we sit under. */
 	struct pci_controller_info	*parent;
@@ -160,11 +78,6 @@
 
 	/* OBP specific information. */
 	struct device_node		*prom_node;
-	struct linux_prom_pci_ranges	*pbm_ranges;
-	int				num_pbm_ranges;
-	struct linux_prom_pci_intmap	*pbm_intmap;
-	int				num_pbm_intmap;
-	struct linux_prom_pci_intmask	*pbm_intmask;
 	u64				ino_bitmap;
 
 	/* PBM I/O and Memory space resources. */
@@ -197,13 +110,10 @@
 #endif /* !(CONFIG_PCI_MSI) */
 
 	/* This PBM's streaming buffer. */
-	struct pci_strbuf		stc;
+	struct strbuf			stc;
 
 	/* IOMMU state, potentially shared by both PBM segments. */
-	struct pci_iommu		*iommu;
-
-	/* PCI slot mapping. */
-	unsigned int			pci_first_slot;
+	struct iommu			*iommu;
 
 	/* Now things for the actual PCI bus probes. */
 	unsigned int			pci_first_busno;
@@ -220,17 +130,12 @@
 	 */
 	int				index;
 
-	/* Do the PBMs both exist in the same PCI domain? */
-	int				pbms_same_domain;
-
 	/* The PCI bus modules controlled by us. */
 	struct pci_pbm_info		pbm_A;
 	struct pci_pbm_info		pbm_B;
 
 	/* Operations which are controller specific. */
 	void (*scan_bus)(struct pci_controller_info *);
-	void (*base_address_update)(struct pci_dev *, int);
-	void (*resource_adjust)(struct pci_dev *, struct resource *, struct resource *);
 
 #ifdef CONFIG_PCI_MSI
 	int (*setup_msi_irq)(unsigned int *virt_irq_p, struct pci_dev *pdev,
@@ -244,27 +149,4 @@
 	unsigned int			pci_last_busno;
 };
 
-/* PCI devices which are not bridges have this placed in their pci_dev
- * sysdata member.  This makes OBP aware PCI device drivers easier to
- * code.
- */
-struct pcidev_cookie {
-	struct pci_pbm_info		*pbm;
-	struct device_node		*prom_node;
-	struct of_device		*op;
-	struct linux_prom_pci_registers	prom_regs[PROMREG_MAX];
-	int num_prom_regs;
-	struct linux_prom_pci_registers prom_assignments[PROMREG_MAX];
-	int num_prom_assignments;
-#ifdef CONFIG_PCI_MSI
-	unsigned int			msi_num;
-#endif
-};
-
-/* Currently these are the same across all PCI controllers
- * we support.  Someday they may not be...
- */
-#define PCI_IRQ_IGN	0x000007c0	/* Interrupt Group Number */
-#define PCI_IRQ_INO	0x0000003f	/* Interrupt Number */
-
 #endif /* !(__SPARC64_PBM_H) */
diff --git a/include/asm-sparc64/pci.h b/include/asm-sparc64/pci.h
index b14a725b..47cea16 100644
--- a/include/asm-sparc64/pci.h
+++ b/include/asm-sparc64/pci.h
@@ -54,7 +54,7 @@
 	void (*dma_sync_sg_for_cpu)(struct pci_dev *, struct scatterlist *, int, int);
 };
 
-extern struct pci_iommu_ops *pci_iommu_ops;
+extern const struct pci_iommu_ops *pci_iommu_ops;
 
 /* Allocate and map kernel buffer using consistent mode DMA for a device.
  * hwdev should be valid struct pci_dev pointer for PCI devices.
diff --git a/include/asm-sparc64/pgtable.h b/include/asm-sparc64/pgtable.h
index b12be7a..46705ef 100644
--- a/include/asm-sparc64/pgtable.h
+++ b/include/asm-sparc64/pgtable.h
@@ -737,20 +737,6 @@
 extern pte_t pgoff_to_pte(unsigned long);
 #define PTE_FILE_MAX_BITS	(64UL - PAGE_SHIFT - 1UL)
 
-extern unsigned long prom_virt_to_phys(unsigned long, int *);
-
-extern unsigned long sun4u_get_pte(unsigned long);
-
-static inline unsigned long __get_phys(unsigned long addr)
-{
-	return sun4u_get_pte(addr);
-}
-
-static inline int __get_iospace(unsigned long addr)
-{
-	return ((sun4u_get_pte(addr) & 0xf0000000) >> 28);
-}
-
 extern unsigned long *sparc64_valid_addr_bitmap;
 
 /* Needs to be defined here and not in linux/mm.h, as it is arch dependent */
@@ -791,6 +777,8 @@
 extern void sun4v_register_fault_status(void);
 extern void sun4v_ktsb_register(void);
 
+extern unsigned long cmdline_memory_size;
+
 #endif /* !(__ASSEMBLY__) */
 
 #endif /* !(_SPARC64_PGTABLE_H) */
diff --git a/include/asm-sparc64/prom.h b/include/asm-sparc64/prom.h
index 0eca2d9..ddad5f9 100644
--- a/include/asm-sparc64/prom.h
+++ b/include/asm-sparc64/prom.h
@@ -36,8 +36,8 @@
 
 struct of_irq_controller;
 struct device_node {
-	char	*name;
-	char	*type;
+	const char	*name;
+	const char	*type;
 	phandle	node;
 	char	*path_component_name;
 	char	*full_name;
@@ -93,11 +93,13 @@
 extern struct device_node *of_get_parent(const struct device_node *node);
 extern struct device_node *of_get_next_child(const struct device_node *node,
 					     struct device_node *prev);
-extern struct property *of_find_property(struct device_node *np,
+extern struct property *of_find_property(const struct device_node *np,
 					 const char *name,
 					 int *lenp);
-extern int of_device_is_compatible(struct device_node *device, const char *);
-extern void *of_get_property(struct device_node *node, const char *name,
+extern int of_device_is_compatible(const struct device_node *device,
+				   const char *);
+extern const void *of_get_property(const struct device_node *node,
+			     const char *name,
 			     int *lenp);
 #define get_property(node,name,lenp) of_get_property(node,name,lenp)
 extern int of_set_property(struct device_node *node, const char *name, void *val, int len);
diff --git a/include/asm-sparc64/smp.h b/include/asm-sparc64/smp.h
index 388249b..cca5480 100644
--- a/include/asm-sparc64/smp.h
+++ b/include/asm-sparc64/smp.h
@@ -42,15 +42,15 @@
 #define raw_smp_processor_id() (current_thread_info()->cpu)
 
 extern void smp_setup_cpu_possible_map(void);
+extern unsigned char boot_cpu_id;
 
 #endif /* !(__ASSEMBLY__) */
 
 #else
 
 #define smp_setup_cpu_possible_map() do { } while (0)
+#define boot_cpu_id	(0)
 
 #endif /* !(CONFIG_SMP) */
 
-#define NO_PROC_ID		0xFF
-
 #endif /* !(_SPARC64_SMP_H) */
diff --git a/include/asm-sparc64/socket.h b/include/asm-sparc64/socket.h
index 754d46a..986441d 100644
--- a/include/asm-sparc64/socket.h
+++ b/include/asm-sparc64/socket.h
@@ -49,6 +49,8 @@
 
 #define SO_PEERSEC		0x001e
 #define SO_PASSSEC		0x001f
+#define SO_TIMESTAMPNS		0x0021
+#define SCM_TIMESTAMPNS		SO_TIMESTAMPNS
 
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_SECURITY_AUTHENTICATION		0x5001
diff --git a/include/asm-sparc64/sockios.h b/include/asm-sparc64/sockios.h
index 6735bab4..c7d9900 100644
--- a/include/asm-sparc64/sockios.h
+++ b/include/asm-sparc64/sockios.h
@@ -7,7 +7,8 @@
 #define FIOGETOWN	0x8903
 #define SIOCGPGRP	0x8904
 #define SIOCATMARK	0x8905
-#define SIOCGSTAMP	0x8906		/* Get stamp */
+#define SIOCGSTAMP	0x8906		/* Get stamp (timeval) */
+#define SIOCGSTAMPNS	0x8907		/* Get stamp (timespec) */
 
 #endif /* !(_ASM_SPARC64_SOCKIOS_H) */
 
diff --git a/include/asm-sparc64/sparsemem.h b/include/asm-sparc64/sparsemem.h
index ed5c9d8..77bcd2b 100644
--- a/include/asm-sparc64/sparsemem.h
+++ b/include/asm-sparc64/sparsemem.h
@@ -3,7 +3,7 @@
 
 #ifdef __KERNEL__
 
-#define SECTION_SIZE_BITS       26
+#define SECTION_SIZE_BITS       31
 #define MAX_PHYSADDR_BITS       42
 #define MAX_PHYSMEM_BITS        42
 
diff --git a/include/asm-sparc64/timer.h b/include/asm-sparc64/timer.h
index d435594..ccbd694 100644
--- a/include/asm-sparc64/timer.h
+++ b/include/asm-sparc64/timer.h
@@ -11,22 +11,19 @@
 
 
 struct sparc64_tick_ops {
-	void (*init_tick)(unsigned long);
 	unsigned long (*get_tick)(void);
-	unsigned long (*get_compare)(void);
-	unsigned long (*add_tick)(unsigned long, unsigned long);
-	unsigned long (*add_compare)(unsigned long);
+	int (*add_compare)(unsigned long);
 	unsigned long softint_mask;
+	void (*disable_irq)(void);
+
+	void (*init_tick)(void);
+	unsigned long (*add_tick)(unsigned long);
+
+	char *name;
 };
 
 extern struct sparc64_tick_ops *tick_ops;
 
-#ifdef CONFIG_SMP
-extern unsigned long timer_tick_offset;
-struct pt_regs;
-extern void timer_tick_interrupt(struct pt_regs *);
-#endif
-
 extern unsigned long sparc64_get_clock_tick(unsigned int cpu);
 
 #endif /* _SPARC64_TIMER_H */
diff --git a/include/asm-sparc64/ttable.h b/include/asm-sparc64/ttable.h
index c2a16e1..bbb9c8f 100644
--- a/include/asm-sparc64/ttable.h
+++ b/include/asm-sparc64/ttable.h
@@ -157,23 +157,6 @@
 	ba,a,pt	%xcc, rtrap_irq;			\
 	.previous;
 
-#define TICK_SMP_IRQ					\
-	rdpr	%pil, %g2;				\
-	wrpr	%g0, 15, %pil;				\
-	sethi	%hi(1f-4), %g7;				\
-	ba,pt	%xcc, etrap_irq;			\
-	 or	%g7, %lo(1f-4), %g7;			\
-	nop;						\
-	nop;						\
-	nop;						\
-	.subsection	2;				\
-1:	call	trace_hardirqs_off;			\
-	 nop;						\
-	call	smp_percpu_timer_interrupt;		\
-	 add	%sp, PTREGS_OFF, %o0;			\
-	ba,a,pt	%xcc, rtrap_irq;			\
-	.previous;
-
 #else
 
 #define TRAP_IRQ(routine, level)			\
@@ -186,16 +169,6 @@
 	 add	%sp, PTREGS_OFF, %o1;			\
 	ba,a,pt	%xcc, rtrap_irq;
 	
-#define TICK_SMP_IRQ					\
-	rdpr	%pil, %g2;				\
-	wrpr	%g0, 15, %pil;				\
-	sethi	%hi(109f), %g7;				\
-	ba,pt	%xcc, etrap_irq;			\
-109:	 or	%g7, %lo(109b), %g7;			\
-	call	smp_percpu_timer_interrupt;		\
-	 add	%sp, PTREGS_OFF, %o0;			\
-	ba,a,pt	%xcc, rtrap_irq;
-
 #endif
 
 #define TRAP_IVEC TRAP_NOSAVE(do_ivec)
diff --git a/include/asm-um/div64.h b/include/asm-um/div64.h
index 1e17f74..7b73b2c 100644
--- a/include/asm-um/div64.h
+++ b/include/asm-um/div64.h
@@ -3,4 +3,5 @@
 
 #include "asm/arch/div64.h"
 
+extern uint64_t div64_64(uint64_t dividend, uint64_t divisor);
 #endif
diff --git a/include/asm-v850/socket.h b/include/asm-v850/socket.h
index 0dfe55a..a4c2493 100644
--- a/include/asm-v850/socket.h
+++ b/include/asm-v850/socket.h
@@ -49,5 +49,7 @@
 
 #define SO_PEERSEC		31
 #define SO_PASSSEC		34
+#define SO_TIMESTAMPNS		35
+#define SCM_TIMESTAMPNS		SO_TIMESTAMPNS
 
 #endif /* __V850_SOCKET_H__ */
diff --git a/include/asm-v850/sockios.h b/include/asm-v850/sockios.h
index cf4874c..823e106 100644
--- a/include/asm-v850/sockios.h
+++ b/include/asm-v850/sockios.h
@@ -7,6 +7,7 @@
 #define FIOGETOWN	0x8903
 #define SIOCGPGRP	0x8904
 #define SIOCATMARK	0x8905
-#define SIOCGSTAMP	0x8906		/* Get stamp */
+#define SIOCGSTAMP	0x8906		/* Get stamp (timeval) */
+#define SIOCGSTAMPNS	0x8907		/* Get stamp (timespec) */
 
 #endif /* __V850_SOCKIOS_H__ */
diff --git a/include/asm-x86_64/agp.h b/include/asm-x86_64/agp.h
index 06c52ee..de33866 100644
--- a/include/asm-x86_64/agp.h
+++ b/include/asm-x86_64/agp.h
@@ -10,8 +10,10 @@
  * with different cachability attributes for the same page.
  */
 
-int map_page_into_agp(struct page *page);
-int unmap_page_from_agp(struct page *page);
+/* Caller's responsibility to call global_flush_tlb() for
+ * performance reasons */
+#define map_page_into_agp(page) change_page_attr(page, 1, PAGE_KERNEL_NOCACHE)
+#define unmap_page_from_agp(page) change_page_attr(page, 1, PAGE_KERNEL)
 #define flush_agp_mappings() global_flush_tlb()
 
 /* Could use CLFLUSH here if the cpu supports it. But then it would
diff --git a/include/asm-x86_64/socket.h b/include/asm-x86_64/socket.h
index b467026..90af60c 100644
--- a/include/asm-x86_64/socket.h
+++ b/include/asm-x86_64/socket.h
@@ -49,5 +49,7 @@
 
 #define SO_PEERSEC             31
 #define SO_PASSSEC		34
+#define SO_TIMESTAMPNS		35
+#define SCM_TIMESTAMPNS		SO_TIMESTAMPNS
 
 #endif /* _ASM_SOCKET_H */
diff --git a/include/asm-x86_64/sockios.h b/include/asm-x86_64/sockios.h
index 2eefd10..d726ba2 100644
--- a/include/asm-x86_64/sockios.h
+++ b/include/asm-x86_64/sockios.h
@@ -7,6 +7,7 @@
 #define FIOGETOWN	0x8903
 #define SIOCGPGRP	0x8904
 #define SIOCATMARK	0x8905
-#define SIOCGSTAMP	0x8906		/* Get stamp */
+#define SIOCGSTAMP	0x8906		/* Get stamp (timeval) */
+#define SIOCGSTAMPNS	0x8907		/* Get stamp (timespec) */
 
 #endif
diff --git a/include/asm-xtensa/div64.h b/include/asm-xtensa/div64.h
index c4a1057..20965e3 100644
--- a/include/asm-xtensa/div64.h
+++ b/include/asm-xtensa/div64.h
@@ -11,9 +11,15 @@
 #ifndef _XTENSA_DIV64_H
 #define _XTENSA_DIV64_H
 
+#include <linux/types.h>
+
 #define do_div(n,base) ({ \
 	int __res = n % ((unsigned int) base); \
 	n /= (unsigned int) base; \
 	__res; })
 
+static inline uint64_t div64_64(uint64_t dividend, uint64_t divisor)
+{
+	return dividend / divisor;
+}
 #endif
diff --git a/include/asm-xtensa/socket.h b/include/asm-xtensa/socket.h
index 971d231..1f5aeac 100644
--- a/include/asm-xtensa/socket.h
+++ b/include/asm-xtensa/socket.h
@@ -60,5 +60,7 @@
 #define SO_ACCEPTCONN		30
 #define SO_PEERSEC		31
 #define SO_PASSSEC		34
+#define SO_TIMESTAMPNS		35
+#define SCM_TIMESTAMPNS		SO_TIMESTAMPNS
 
 #endif	/* _XTENSA_SOCKET_H */
diff --git a/include/asm-xtensa/sockios.h b/include/asm-xtensa/sockios.h
index 20d2ba1..efe0af3 100644
--- a/include/asm-xtensa/sockios.h
+++ b/include/asm-xtensa/sockios.h
@@ -25,6 +25,7 @@
 #define SIOCSPGRP	_IOW('s', 8, pid_t)
 #define SIOCGPGRP	_IOR('s', 9, pid_t)
 
-#define SIOCGSTAMP	0x8906		/* Get stamp - linux-specific */
+#define SIOCGSTAMP	0x8906		/* Get stamp (timeval) */
+#define SIOCGSTAMPNS	0x8907		/* Get stamp (timespec) */
 
 #endif	/* _XTENSA_SOCKIOS_H */
diff --git a/include/keys/rxrpc-type.h b/include/keys/rxrpc-type.h
new file mode 100644
index 0000000..e2ee73a
--- /dev/null
+++ b/include/keys/rxrpc-type.h
@@ -0,0 +1,22 @@
+/* RxRPC key type
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#ifndef _KEYS_RXRPC_TYPE_H
+#define _KEYS_RXRPC_TYPE_H
+
+#include <linux/key.h>
+
+/*
+ * key type for AF_RXRPC keys
+ */
+extern struct key_type key_type_rxrpc;
+
+#endif /* _KEYS_USER_TYPE_H */
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index e81e301..4ff0f57d 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -69,9 +69,7 @@
 header-y += hysdn_if.h
 header-y += i2c-dev.h
 header-y += i8k.h
-header-y += icmp.h
 header-y += if_arcnet.h
-header-y += if_arp.h
 header-y += if_bonding.h
 header-y += if_cablemodem.h
 header-y += if_fc.h
@@ -88,7 +86,6 @@
 header-y += in6.h
 header-y += in_route.h
 header-y += ioctl.h
-header-y += ip.h
 header-y += ipmi_msgdefs.h
 header-y += ip_mp_alg.h
 header-y += ipsec.h
@@ -116,6 +113,7 @@
 header-y += nfs2.h
 header-y += nfs4_mount.h
 header-y += nfs_mount.h
+header-y += nl80211.h
 header-y += oom.h
 header-y += param.h
 header-y += pci_regs.h
@@ -210,8 +208,10 @@
 unifdef-y += hpet.h
 unifdef-y += i2c.h
 unifdef-y += i2o-dev.h
+unifdef-y += icmp.h
 unifdef-y += icmpv6.h
 unifdef-y += if_addr.h
+unifdef-y += if_arp.h
 unifdef-y += if_bridge.h
 unifdef-y += if_ec.h
 unifdef-y += if_eql.h
@@ -231,6 +231,7 @@
 unifdef-y += in.h
 unifdef-y += inotify.h
 unifdef-y += input.h
+unifdef-y += ip.h
 unifdef-y += ipc.h
 unifdef-y += ipmi.h
 unifdef-y += ipv6.h
diff --git a/include/linux/ata.h b/include/linux/ata.h
index 6caeb98..edb31bf 100644
--- a/include/linux/ata.h
+++ b/include/linux/ata.h
@@ -159,11 +159,19 @@
 	ATA_CMD_INIT_DEV_PARAMS	= 0x91,
 	ATA_CMD_READ_NATIVE_MAX	= 0xF8,
 	ATA_CMD_READ_NATIVE_MAX_EXT = 0x27,
+	ATA_CMD_SET_MAX		= 0xF9,
+	ATA_CMD_SET_MAX_EXT	= 0x37,
 	ATA_CMD_READ_LOG_EXT	= 0x2f,
 
 	/* READ_LOG_EXT pages */
 	ATA_LOG_SATA_NCQ	= 0x10,
 
+	/* READ/WRITE LONG (obsolete) */
+	ATA_CMD_READ_LONG	= 0x22,
+	ATA_CMD_READ_LONG_ONCE	= 0x23,
+	ATA_CMD_WRITE_LONG	= 0x32,
+	ATA_CMD_WRITE_LONG_ONCE	= 0x33,
+
 	/* SETFEATURES stuff */
 	SETFEATURES_XFER	= 0x03,
 	XFER_UDMA_7		= 0x47,
@@ -194,6 +202,8 @@
 	SETFEATURES_WC_ON	= 0x02, /* Enable write cache */
 	SETFEATURES_WC_OFF	= 0x82, /* Disable write cache */
 
+	SETFEATURES_SPINUP	= 0x07, /* Spin-up drive */
+
 	/* ATAPI stuff */
 	ATAPI_PKT_DMA		= (1 << 0),
 	ATAPI_DMADIR		= (1 << 2),	/* ATAPI data dir:
diff --git a/include/linux/atalk.h b/include/linux/atalk.h
index d12984d..ced8a1e 100644
--- a/include/linux/atalk.h
+++ b/include/linux/atalk.h
@@ -101,7 +101,7 @@
 
 static __inline__ struct ddpehdr *ddp_hdr(struct sk_buff *skb)
 {
-	return (struct ddpehdr *)skb->h.raw;
+	return (struct ddpehdr *)skb_transport_header(skb);
 }
 
 /* AppleTalk AARP headers */
@@ -129,7 +129,7 @@
 
 static __inline__ struct elapaarp *aarp_hdr(struct sk_buff *skb)
 {
-	return (struct elapaarp *)skb->h.raw;
+	return (struct elapaarp *)skb_transport_header(skb);
 }
 
 /* Not specified - how long till we drop a resolved entry */
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 08daf32..4d85262 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -276,7 +276,7 @@
 extern mempool_t *bio_split_pool;
 extern void bio_pair_release(struct bio_pair *dbio);
 
-extern struct bio_set *bioset_create(int, int, int);
+extern struct bio_set *bioset_create(int, int);
 extern void bioset_free(struct bio_set *);
 
 extern struct bio *bio_alloc(gfp_t, int);
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 83dcd8c..a686eab 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -116,6 +116,7 @@
 
 	struct as_io_context *aic;
 	struct rb_root cic_root;
+	void *ioc_data;
 };
 
 void put_io_context(struct io_context *ioc);
diff --git a/include/linux/dccp.h b/include/linux/dccp.h
index 1cb054b..fda2148 100644
--- a/include/linux/dccp.h
+++ b/include/linux/dccp.h
@@ -260,19 +260,20 @@
 
 static inline struct dccp_hdr *dccp_hdr(const struct sk_buff *skb)
 {
-	return (struct dccp_hdr *)skb->h.raw;
+	return (struct dccp_hdr *)skb_transport_header(skb);
 }
 
 static inline struct dccp_hdr *dccp_zeroed_hdr(struct sk_buff *skb, int headlen)
 {
-	skb->h.raw = skb_push(skb, headlen);
-	memset(skb->h.raw, 0, headlen);
-	return dccp_hdr(skb);
+	skb_push(skb, headlen);
+	skb_reset_transport_header(skb);
+	return memset(skb_transport_header(skb), 0, headlen);
 }
 
 static inline struct dccp_hdr_ext *dccp_hdrx(const struct sk_buff *skb)
 {
-	return (struct dccp_hdr_ext *)(skb->h.raw + sizeof(struct dccp_hdr));
+	return (struct dccp_hdr_ext *)(skb_transport_header(skb) +
+				       sizeof(struct dccp_hdr));
 }
 
 static inline unsigned int __dccp_basic_hdr_len(const struct dccp_hdr *dh)
@@ -301,12 +302,14 @@
 
 static inline struct dccp_hdr_request *dccp_hdr_request(struct sk_buff *skb)
 {
-	return (struct dccp_hdr_request *)(skb->h.raw + dccp_basic_hdr_len(skb));
+	return (struct dccp_hdr_request *)(skb_transport_header(skb) +
+					   dccp_basic_hdr_len(skb));
 }
 
 static inline struct dccp_hdr_ack_bits *dccp_hdr_ack_bits(const struct sk_buff *skb)
 {
-	return (struct dccp_hdr_ack_bits *)(skb->h.raw + dccp_basic_hdr_len(skb));
+	return (struct dccp_hdr_ack_bits *)(skb_transport_header(skb) +
+					    dccp_basic_hdr_len(skb));
 }
 
 static inline u64 dccp_hdr_ack_seq(const struct sk_buff *skb)
@@ -317,12 +320,14 @@
 
 static inline struct dccp_hdr_response *dccp_hdr_response(struct sk_buff *skb)
 {
-	return (struct dccp_hdr_response *)(skb->h.raw + dccp_basic_hdr_len(skb));
+	return (struct dccp_hdr_response *)(skb_transport_header(skb) +
+					    dccp_basic_hdr_len(skb));
 }
 
 static inline struct dccp_hdr_reset *dccp_hdr_reset(struct sk_buff *skb)
 {
-	return (struct dccp_hdr_reset *)(skb->h.raw + dccp_basic_hdr_len(skb));
+	return (struct dccp_hdr_reset *)(skb_transport_header(skb) +
+					 dccp_basic_hdr_len(skb));
 }
 
 static inline unsigned int __dccp_hdr_len(const struct dccp_hdr *dh)
@@ -460,26 +465,27 @@
  * @dccps_service_list - second .. last service code on passive socket
  * @dccps_timestamp_time - time of latest TIMESTAMP option
  * @dccps_timestamp_echo - latest timestamp received on a TIMESTAMP option
- * @dccps_l_ack_ratio -
- * @dccps_r_ack_ratio -
+ * @dccps_l_ack_ratio - feature-local Ack Ratio
+ * @dccps_r_ack_ratio - feature-remote Ack Ratio
  * @dccps_pcslen - sender   partial checksum coverage (via sockopt)
  * @dccps_pcrlen - receiver partial checksum coverage (via sockopt)
  * @dccps_ndp_count - number of Non Data Packets since last data packet
- * @dccps_mss_cache -
- * @dccps_minisock -
+ * @dccps_mss_cache - current value of MSS (path MTU minus header sizes)
+ * @dccps_minisock - associated minisock (accessed via dccp_msk)
  * @dccps_hc_rx_ackvec - rx half connection ack vector
- * @dccps_hc_rx_ccid -
- * @dccps_hc_tx_ccid -
- * @dccps_options_received -
- * @dccps_epoch -
- * @dccps_role - Role of this sock, one of %dccp_role
- * @dccps_hc_rx_insert_options -
- * @dccps_hc_tx_insert_options -
+ * @dccps_hc_rx_ccid - CCID used for the receiver (or receiving half-connection)
+ * @dccps_hc_tx_ccid - CCID used for the sender (or sending half-connection)
+ * @dccps_options_received - parsed set of retrieved options
+ * @dccps_role - role of this sock, one of %dccp_role
+ * @dccps_hc_rx_insert_options - receiver wants to add options when acking
+ * @dccps_hc_tx_insert_options - sender wants to add options when sending
  * @dccps_xmit_timer - timer for when CCID is not ready to send
+ * @dccps_syn_rtt - RTT sample from Request/Response exchange (in usecs)
  */
 struct dccp_sock {
 	/* inet_connection_sock has to be the first member of dccp_sock */
 	struct inet_connection_sock	dccps_inet_connection;
+#define dccps_syn_rtt			dccps_inet_connection.icsk_ack.lrcvtime
 	__u64				dccps_swl;
 	__u64				dccps_swh;
 	__u64				dccps_awl;
diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h
index 9fa0983..5a9c495 100644
--- a/include/linux/debugfs.h
+++ b/include/linux/debugfs.h
@@ -44,6 +44,8 @@
 				  struct dentry *parent, u16 *value);
 struct dentry *debugfs_create_u32(const char *name, mode_t mode,
 				  struct dentry *parent, u32 *value);
+struct dentry *debugfs_create_u64(const char *name, mode_t mode,
+				  struct dentry *parent, u64 *value);
 struct dentry *debugfs_create_bool(const char *name, mode_t mode,
 				  struct dentry *parent, u32 *value);
 
@@ -104,6 +106,13 @@
 	return ERR_PTR(-ENODEV);
 }
 
+static inline struct dentry *debugfs_create_u64(const char *name, mode_t mode,
+						struct dentry *parent,
+						u64 *value)
+{
+	return ERR_PTR(-ENODEV);
+}
+
 static inline struct dentry *debugfs_create_bool(const char *name, mode_t mode,
 						 struct dentry *parent,
 						 u32 *value)
diff --git a/include/linux/device.h b/include/linux/device.h
index 5cf30e9..a0cd2ce 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -34,9 +34,24 @@
 struct device_driver;
 struct class;
 struct class_device;
+struct bus_type;
+
+struct bus_attribute {
+	struct attribute	attr;
+	ssize_t (*show)(struct bus_type *, char * buf);
+	ssize_t (*store)(struct bus_type *, const char * buf, size_t count);
+};
+
+#define BUS_ATTR(_name,_mode,_show,_store)	\
+struct bus_attribute bus_attr_##_name = __ATTR(_name,_mode,_show,_store)
+
+extern int __must_check bus_create_file(struct bus_type *,
+					struct bus_attribute *);
+extern void bus_remove_file(struct bus_type *, struct bus_attribute *);
 
 struct bus_type {
 	const char		* name;
+	struct module		* owner;
 
 	struct subsystem	subsys;
 	struct kset		drivers;
@@ -49,6 +64,8 @@
 	struct bus_attribute	* bus_attrs;
 	struct device_attribute	* dev_attrs;
 	struct driver_attribute	* drv_attrs;
+	struct bus_attribute drivers_autoprobe_attr;
+	struct bus_attribute drivers_probe_attr;
 
 	int		(*match)(struct device * dev, struct device_driver * drv);
 	int		(*uevent)(struct device *dev, char **envp,
@@ -61,6 +78,9 @@
 	int (*suspend_late)(struct device * dev, pm_message_t state);
 	int (*resume_early)(struct device * dev);
 	int (*resume)(struct device * dev);
+
+	unsigned int drivers_autoprobe:1;
+	unsigned int multithread_probe:1;
 };
 
 extern int __must_check bus_register(struct bus_type * bus);
@@ -102,26 +122,10 @@
 #define BUS_NOTIFY_UNBIND_DRIVER	0x00000004 /* driver about to be
 						      unbound */
 
-/* sysfs interface for exporting bus attributes */
-
-struct bus_attribute {
-	struct attribute	attr;
-	ssize_t (*show)(struct bus_type *, char * buf);
-	ssize_t (*store)(struct bus_type *, const char * buf, size_t count);
-};
-
-#define BUS_ATTR(_name,_mode,_show,_store)	\
-struct bus_attribute bus_attr_##_name = __ATTR(_name,_mode,_show,_store)
-
-extern int __must_check bus_create_file(struct bus_type *,
-					struct bus_attribute *);
-extern void bus_remove_file(struct bus_type *, struct bus_attribute *);
-
 struct device_driver {
 	const char		* name;
 	struct bus_type		* bus;
 
-	struct completion	unloaded;
 	struct kobject		kobj;
 	struct klist		klist_devices;
 	struct klist_node	knode_bus;
@@ -135,8 +139,6 @@
 	void	(*shutdown)	(struct device * dev);
 	int	(*suspend)	(struct device * dev, pm_message_t state);
 	int	(*resume)	(struct device * dev);
-
-	unsigned int multithread_probe:1;
 };
 
 
@@ -181,10 +183,9 @@
 	struct list_head	children;
 	struct list_head	devices;
 	struct list_head	interfaces;
+	struct kset		class_dirs;
 	struct semaphore	sem;	/* locks both the children and interfaces lists */
 
-	struct kobject		*virtual_dir;
-
 	struct class_attribute		* class_attrs;
 	struct class_device_attribute	* class_dev_attrs;
 	struct device_attribute		* dev_attrs;
@@ -328,11 +329,23 @@
 					__attribute__((format(printf,5,6)));
 extern void class_device_destroy(struct class *cls, dev_t devt);
 
+/*
+ * The type of device, "struct device" is embedded in. A class
+ * or bus can contain devices of different types
+ * like "partitions" and "disks", "mouse" and "event".
+ * This identifies the device type and carries type-specific
+ * information, equivalent to the kobj_type of a kobject.
+ * If "name" is specified, the uevent will contain it in
+ * the DEVTYPE variable.
+ */
 struct device_type {
-	struct device_attribute *attrs;
+	const char *name;
+	struct attribute_group **groups;
 	int (*uevent)(struct device *dev, char **envp, int num_envp,
 		      char *buffer, int buffer_size);
 	void (*release)(struct device *dev);
+	int (*suspend)(struct device * dev, pm_message_t state);
+	int (*resume)(struct device * dev);
 };
 
 /* interface for exporting device attributes */
@@ -354,8 +367,12 @@
 					       struct bin_attribute *attr);
 extern void device_remove_bin_file(struct device *dev,
 				   struct bin_attribute *attr);
-extern int device_schedule_callback(struct device *dev,
-		void (*func)(struct device *));
+extern int device_schedule_callback_owner(struct device *dev,
+		void (*func)(struct device *), struct module *owner);
+
+/* This is a macro to avoid include problems with THIS_MODULE */
+#define device_schedule_callback(dev, func)			\
+	device_schedule_callback_owner(dev, func, THIS_MODULE)
 
 /* device resource management */
 typedef void (*dr_release_t)(struct device *dev, void *res);
@@ -554,7 +571,11 @@
 #define dev_dbg(dev, format, arg...)		\
 	dev_printk(KERN_DEBUG , dev , format , ## arg)
 #else
-#define dev_dbg(dev, format, arg...) do { (void)(dev); } while (0)
+static inline int __attribute__ ((format (printf, 2, 3)))
+dev_dbg(struct device * dev, const char * fmt, ...)
+{
+	return 0;
+}
 #endif
 
 #define dev_err(dev, format, arg...)		\
diff --git a/include/linux/dvb/audio.h b/include/linux/dvb/audio.h
index 0874a67..89412e1 100644
--- a/include/linux/dvb/audio.h
+++ b/include/linux/dvb/audio.h
@@ -47,7 +47,9 @@
 typedef enum {
 	AUDIO_STEREO,
 	AUDIO_MONO_LEFT,
-	AUDIO_MONO_RIGHT
+	AUDIO_MONO_RIGHT,
+	AUDIO_MONO,
+	AUDIO_STEREO_SWAPPED
 } audio_channel_select_t;
 
 
@@ -133,5 +135,6 @@
  * extracted by the PES parser.
  */
 #define AUDIO_GET_PTS              _IOR('o', 19, __u64)
+#define AUDIO_BILINGUAL_CHANNEL_SELECT _IO('o', 20)
 
 #endif /* _DVBAUDIO_H_ */
diff --git a/include/linux/dvb/version.h b/include/linux/dvb/version.h
index 6183c9c..126e0c2 100644
--- a/include/linux/dvb/version.h
+++ b/include/linux/dvb/version.h
@@ -24,6 +24,6 @@
 #define _DVBVERSION_H_
 
 #define DVB_API_VERSION 3
-#define DVB_API_VERSION_MINOR 1
+#define DVB_API_VERSION_MINOR 2
 
 #endif /*_DVBVERSION_H_*/
diff --git a/include/linux/dvb/video.h b/include/linux/dvb/video.h
index faebfda..93e4c3a 100644
--- a/include/linux/dvb/video.h
+++ b/include/linux/dvb/video.h
@@ -80,14 +80,70 @@
 } video_play_state_t;
 
 
+/* Decoder commands */
+#define VIDEO_CMD_PLAY        (0)
+#define VIDEO_CMD_STOP        (1)
+#define VIDEO_CMD_FREEZE      (2)
+#define VIDEO_CMD_CONTINUE    (3)
+
+/* Flags for VIDEO_CMD_FREEZE */
+#define VIDEO_CMD_FREEZE_TO_BLACK     	(1 << 0)
+
+/* Flags for VIDEO_CMD_STOP */
+#define VIDEO_CMD_STOP_TO_BLACK      	(1 << 0)
+#define VIDEO_CMD_STOP_IMMEDIATELY     	(1 << 1)
+
+/* Play input formats: */
+/* The decoder has no special format requirements */
+#define VIDEO_PLAY_FMT_NONE         (0)
+/* The decoder requires full GOPs */
+#define VIDEO_PLAY_FMT_GOP          (1)
+
+/* The structure must be zeroed before use by the application
+   This ensures it can be extended safely in the future. */
+struct video_command {
+	__u32 cmd;
+	__u32 flags;
+	union {
+		struct {
+			__u64 pts;
+		} stop;
+
+		struct {
+			/* 0 or 1000 specifies normal speed,
+			   1 specifies forward single stepping,
+			   -1 specifies backward single stepping,
+			   >1: playback at speed/1000 of the normal speed,
+			   <-1: reverse playback at (-speed/1000) of the normal speed. */
+			__s32 speed;
+			__u32 format;
+		} play;
+
+		struct {
+			__u32 data[16];
+		} raw;
+	};
+};
+
+/* FIELD_UNKNOWN can be used if the hardware does not know whether
+   the Vsync is for an odd, even or progressive (i.e. non-interlaced)
+   field. */
+#define VIDEO_VSYNC_FIELD_UNKNOWN  	(0)
+#define VIDEO_VSYNC_FIELD_ODD 		(1)
+#define VIDEO_VSYNC_FIELD_EVEN		(2)
+#define VIDEO_VSYNC_FIELD_PROGRESSIVE	(3)
+
 struct video_event {
 	int32_t type;
 #define VIDEO_EVENT_SIZE_CHANGED	1
 #define VIDEO_EVENT_FRAME_RATE_CHANGED	2
+#define VIDEO_EVENT_DECODER_STOPPED 	3
+#define VIDEO_EVENT_VSYNC 		4
 	time_t timestamp;
 	union {
 		video_size_t size;
 		unsigned int frame_rate;	/* in frames per 1000sec */
+		unsigned char vsync_field;	/* unknown/odd/even/progressive */
 	} u;
 };
 
@@ -213,4 +269,10 @@
  */
 #define VIDEO_GET_PTS              _IOR('o', 57, __u64)
 
+/* Read the number of displayed frames since the decoder was started */
+#define VIDEO_GET_FRAME_COUNT  	   _IOR('o', 58, __u64)
+
+#define VIDEO_COMMAND     	   _IOWR('o', 59, struct video_command)
+#define VIDEO_TRY_COMMAND 	   _IOWR('o', 60, struct video_command)
+
 #endif /*_DVBVIDEO_H_*/
diff --git a/include/linux/fib_rules.h b/include/linux/fib_rules.h
index 8270aac..87b606b 100644
--- a/include/linux/fib_rules.h
+++ b/include/linux/fib_rules.h
@@ -5,8 +5,13 @@
 #include <linux/rtnetlink.h>
 
 /* rule is permanent, and cannot be deleted */
-#define FIB_RULE_PERMANENT	1
-#define FIB_RULE_INVERT		2
+#define FIB_RULE_PERMANENT	0x00000001
+#define FIB_RULE_INVERT		0x00000002
+#define FIB_RULE_UNRESOLVED	0x00000004
+#define FIB_RULE_DEV_DETACHED	0x00000008
+
+/* try to find source address in routing lookups */
+#define FIB_RULE_FIND_SADDR	0x00010000
 
 struct fib_rule_hdr
 {
@@ -29,7 +34,7 @@
 	FRA_DST,	/* destination address */
 	FRA_SRC,	/* source address */
 	FRA_IFNAME,	/* interface name */
-	FRA_UNUSED1,
+	FRA_GOTO,	/* target to jump to (FR_ACT_GOTO) */
 	FRA_UNUSED2,
 	FRA_PRIORITY,	/* priority/preference */
 	FRA_UNUSED3,
@@ -51,8 +56,8 @@
 {
 	FR_ACT_UNSPEC,
 	FR_ACT_TO_TBL,		/* Pass to fixed table */
-	FR_ACT_RES1,
-	FR_ACT_RES2,
+	FR_ACT_GOTO,		/* Jump to another rule */
+	FR_ACT_NOP,		/* No operation */
 	FR_ACT_RES3,
 	FR_ACT_RES4,
 	FR_ACT_BLACKHOLE,	/* Drop without notification */
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 86ec3f4..095a9c9 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -843,8 +843,13 @@
 extern int fcntl_getlease(struct file *filp);
 
 /* fs/sync.c */
-extern int do_sync_file_range(struct file *file, loff_t offset, loff_t endbyte,
-			unsigned int flags);
+extern int do_sync_mapping_range(struct address_space *mapping, loff_t offset,
+			loff_t endbyte, unsigned int flags);
+static inline int do_sync_file_range(struct file *file, loff_t offset,
+			loff_t endbyte, unsigned int flags)
+{
+	return do_sync_mapping_range(file->f_mapping, offset, endbyte, flags);
+}
 
 /* fs/locks.c */
 extern void locks_init_lock(struct file_lock *);
diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h
index abb64c4..73710d6 100644
--- a/include/linux/fsl_devices.h
+++ b/include/linux/fsl_devices.h
@@ -120,44 +120,5 @@
 	u32	sysclk;
 };
 
-/* Ethernet interface (phy management and speed)
-*/
-enum enet_interface {
-	ENET_10_MII,		/* 10 Base T,   MII interface */
-	ENET_10_RMII,		/* 10 Base T,  RMII interface */
-	ENET_10_RGMII,		/* 10 Base T, RGMII interface */
-	ENET_100_MII,		/* 100 Base T,   MII interface */
-	ENET_100_RMII,		/* 100 Base T,  RMII interface */
-	ENET_100_RGMII,		/* 100 Base T, RGMII interface */
-	ENET_1000_GMII,		/* 1000 Base T,  GMII interface */
-	ENET_1000_RGMII,	/* 1000 Base T, RGMII interface */
-	ENET_1000_TBI,		/* 1000 Base T,   TBI interface */
-	ENET_1000_RTBI		/* 1000 Base T,  RTBI interface */
-};
-
-struct ucc_geth_platform_data {
-	/* device specific information */
-	u32			device_flags;
-	u32			phy_reg_addr;
-
-	/* board specific information */
-	u32			board_flags;
-	u8			rx_clock;
-	u8			tx_clock;
-	u32			phy_id;
-	enum enet_interface	phy_interface;
-	u32			phy_interrupt;
-	u8			mac_addr[6];
-};
-
-/* Flags related to UCC Gigabit Ethernet device features */
-#define FSL_UGETH_DEV_HAS_GIGABIT		0x00000001
-#define FSL_UGETH_DEV_HAS_COALESCE		0x00000002
-#define FSL_UGETH_DEV_HAS_RMON			0x00000004
-
-/* Flags in ucc_geth_platform_data */
-#define FSL_UGETH_BRD_HAS_PHY_INTR		0x00000001
-				/* if not set use a timer */
-
 #endif /* _FSL_DEVICE_H_ */
 #endif /* __KERNEL__ */
diff --git a/include/linux/hdlc.h b/include/linux/hdlc.h
index d4b3339..db390c5 100644
--- a/include/linux/hdlc.h
+++ b/include/linux/hdlc.h
@@ -43,8 +43,7 @@
 	void (*stop)(struct net_device *dev); /* if open & !DCD */
 	void (*detach)(struct net_device *dev);
 	int (*ioctl)(struct net_device *dev, struct ifreq *ifr);
-	unsigned short (*type_trans)(struct sk_buff *skb,
-				     struct net_device *dev);
+	__be16 (*type_trans)(struct sk_buff *skb, struct net_device *dev);
 	struct module *module;
 	struct hdlc_proto *next; /* next protocol in the list */
 };
@@ -132,8 +131,8 @@
 {
 	hdlc_device *hdlc = dev_to_hdlc(dev);
 
-	skb->mac.raw  = skb->data;
-	skb->dev      = dev;
+	skb->dev = dev;
+	skb_reset_mac_header(skb);
 
 	if (hdlc->proto->type_trans)
 		return hdlc->proto->type_trans(skb, dev);
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 8c97d4d..37076b1 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -247,6 +247,11 @@
  * HID device quirks.
  */
 
+/* 
+ * Increase this if you need to configure more HID quirks at module load time
+ */
+#define MAX_USBHID_BOOT_QUIRKS 4
+
 #define HID_QUIRK_INVERT			0x00000001
 #define HID_QUIRK_NOTOUCH			0x00000002
 #define HID_QUIRK_IGNORE			0x00000004
@@ -267,8 +272,9 @@
 #define HID_QUIRK_SKIP_OUTPUT_REPORTS		0x00020000
 #define HID_QUIRK_IGNORE_MOUSE			0x00040000
 #define HID_QUIRK_SONY_PS3_CONTROLLER		0x00080000
-#define HID_QUIRK_LOGITECH_S510_DESCRIPTOR	0x00100000
+#define HID_QUIRK_LOGITECH_DESCRIPTOR		0x00100000
 #define HID_QUIRK_DUPLICATE_USAGES		0x00200000
+#define HID_QUIRK_RESET_LEDS			0x00400000
 
 /*
  * This is the global environment of the parser. This information is
@@ -494,6 +500,12 @@
 void hid_free_device(struct hid_device *device);
 struct hid_device *hid_parse_report(__u8 *start, unsigned size);
 
+/* HID quirks API */
+u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct);
+int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct, const u32 quirks);
+int usbhid_quirks_init(char **quirks_param);
+void usbhid_quirks_exit(void);
+
 #ifdef CONFIG_HID_FF
 int hid_ff_init(struct hid_device *hid);
 
diff --git a/include/linux/icmp.h b/include/linux/icmp.h
index 24da4fb..474f2a5 100644
--- a/include/linux/icmp.h
+++ b/include/linux/icmp.h
@@ -82,6 +82,15 @@
   } un;
 };
 
+#ifdef __KERNEL__
+#include <linux/skbuff.h>
+
+static inline struct icmphdr *icmp_hdr(const struct sk_buff *skb)
+{
+	return (struct icmphdr *)skb_transport_header(skb);
+}
+#endif
+
 /*
  *	constants for (set|get)sockopt
  */
diff --git a/include/linux/icmpv6.h b/include/linux/icmpv6.h
index 68d3526..7c5e981 100644
--- a/include/linux/icmpv6.h
+++ b/include/linux/icmpv6.h
@@ -75,6 +75,15 @@
 #define icmp6_router_pref	icmp6_dataun.u_nd_ra.router_pref
 };
 
+#ifdef __KERNEL__
+#include <linux/skbuff.h>
+
+static inline struct icmp6hdr *icmp6_hdr(const struct sk_buff *skb)
+{
+	return (struct icmp6hdr *)skb_transport_header(skb);
+}
+#endif
+
 #define ICMPV6_ROUTER_PREF_LOW		0x3
 #define ICMPV6_ROUTER_PREF_MEDIUM	0x0
 #define ICMPV6_ROUTER_PREF_HIGH		0x1
diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h
index d557e4c..43f3bed 100644
--- a/include/linux/if_addr.h
+++ b/include/linux/if_addr.h
@@ -39,6 +39,7 @@
 #define IFA_F_TEMPORARY		IFA_F_SECONDARY
 
 #define	IFA_F_NODAD		0x02
+#define IFA_F_OPTIMISTIC	0x04
 #define	IFA_F_HOMEADDRESS	0x10
 #define IFA_F_DEPRECATED	0x20
 #define IFA_F_TENTATIVE		0x40
diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h
index 7f57142..ed7b93c 100644
--- a/include/linux/if_arp.h
+++ b/include/linux/if_arp.h
@@ -148,4 +148,13 @@
 
 };
 
+#ifdef __KERNEL__
+#include <linux/skbuff.h>
+
+static inline struct arphdr *arp_hdr(const struct sk_buff *skb)
+{
+	return (struct arphdr *)skb_network_header(skb);
+}
+#endif
+
 #endif	/* _LINUX_IF_ARP_H */
diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
index fd1b6eb..4ff211d 100644
--- a/include/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -105,7 +105,8 @@
 #include <linux/netdevice.h>
 
 extern void brioctl_set(int (*ioctl_hook)(unsigned int, void __user *));
-extern int (*br_handle_frame_hook)(struct net_bridge_port *p, struct sk_buff **pskb);
+extern struct sk_buff *(*br_handle_frame_hook)(struct net_bridge_port *p,
+					       struct sk_buff *skb);
 extern int (*br_should_route_hook)(struct sk_buff **pskb);
 
 #endif
diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h
index ab08f35..1db774c 100644
--- a/include/linux/if_ether.h
+++ b/include/linux/if_ether.h
@@ -61,6 +61,7 @@
 #define ETH_P_8021Q	0x8100          /* 802.1Q VLAN Extended Header  */
 #define ETH_P_IPX	0x8137		/* IPX over DIX			*/
 #define ETH_P_IPV6	0x86DD		/* IPv6 over bluebook		*/
+#define ETH_P_PAUSE	0x8808		/* IEEE Pause frames. See 802.3 31B */
 #define ETH_P_SLOW	0x8809		/* Slow Protocol. See 802.3ad 43B */
 #define ETH_P_WCCP	0x883E		/* Web-cache coordination protocol
 					 * defined in draft-wilson-wrec-wccp-v2-00.txt */
@@ -112,7 +113,7 @@
 
 static inline struct ethhdr *eth_hdr(const struct sk_buff *skb)
 {
-	return (struct ethhdr *)skb->mac.raw;
+	return (struct ethhdr *)skb_mac_header(skb);
 }
 
 #ifdef CONFIG_SYSCTL
diff --git a/include/linux/if_link.h b/include/linux/if_link.h
index 35ed3b5..604c243 100644
--- a/include/linux/if_link.h
+++ b/include/linux/if_link.h
@@ -126,6 +126,7 @@
 	IFLA_INET6_STATS,	/* statistics			*/
 	IFLA_INET6_MCAST,	/* MC things. What of them?	*/
 	IFLA_INET6_CACHEINFO,	/* time values and max reasm size */
+	IFLA_INET6_ICMP6STATS,	/* statistics (icmpv6)		*/
 	__IFLA_INET6_MAX
 };
 
diff --git a/include/linux/if_packet.h b/include/linux/if_packet.h
index f3de05c..ad09609 100644
--- a/include/linux/if_packet.h
+++ b/include/linux/if_packet.h
@@ -42,6 +42,7 @@
 #define PACKET_STATISTICS		6
 #define PACKET_COPY_THRESH		7
 #define PACKET_AUXDATA			8
+#define PACKET_ORIGDEV			9
 
 struct tpacket_stats
 {
diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h
index e33ee76..6f987be 100644
--- a/include/linux/if_pppox.h
+++ b/include/linux/if_pppox.h
@@ -111,7 +111,17 @@
 	struct pppoe_tag tag[0];
 } __attribute__ ((packed));
 
+/* Length of entire PPPoE + PPP header */
+#define PPPOE_SES_HLEN	8
+
 #ifdef __KERNEL__
+#include <linux/skbuff.h>
+
+static inline struct pppoe_hdr *pppoe_hdr(const struct sk_buff *skb)
+{
+	return (struct pppoe_hdr *)skb_network_header(skb);
+}
+
 struct pppoe_opt {
 	struct net_device      *dev;	  /* device associated with socket*/
 	int			ifindex;  /* ifindex of device associated with socket */
diff --git a/include/linux/if_tr.h b/include/linux/if_tr.h
index 2f94cf2..046e9d9 100644
--- a/include/linux/if_tr.h
+++ b/include/linux/if_tr.h
@@ -47,7 +47,7 @@
 
 static inline struct trh_hdr *tr_hdr(const struct sk_buff *skb)
 {
-	return (struct trh_hdr *)skb->mac.raw;
+	return (struct trh_hdr *)skb_mac_header(skb);
 }
 #ifdef CONFIG_SYSCTL
 extern struct ctl_table tr_table[];
diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h
index d103580..81e9bc9 100644
--- a/include/linux/if_vlan.h
+++ b/include/linux/if_vlan.h
@@ -51,7 +51,7 @@
 
 static inline struct vlan_ethhdr *vlan_eth_hdr(const struct sk_buff *skb)
 {
-	return (struct vlan_ethhdr *)skb->mac.raw;
+	return (struct vlan_ethhdr *)skb_mac_header(skb);
 }
 
 struct vlan_hdr {
@@ -275,8 +275,8 @@
 	veth->h_vlan_TCI = htons(tag);
 
 	skb->protocol = __constant_htons(ETH_P_8021Q);
-	skb->mac.raw -= VLAN_HLEN;
-	skb->nh.raw -= VLAN_HLEN;
+	skb->mac_header -= VLAN_HLEN;
+	skb->network_header -= VLAN_HLEN;
 
 	return skb;
 }
diff --git a/include/linux/if_wanpipe_common.h b/include/linux/if_wanpipe_common.h
deleted file mode 100644
index 6e5461d..0000000
--- a/include/linux/if_wanpipe_common.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*****************************************************************************
-* if_wanipe_common.h   Sangoma Driver/Socket common area definitions.
-*
-* Author:       Nenad Corbic <ncorbic@sangoma.com>
-*
-* Copyright:    (c) 2000 Sangoma Technologies Inc.
-*
-*               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.
-* ============================================================================
-* Jan 13, 2000  Nenad Corbic      Initial version
-*****************************************************************************/
-
-
-#ifndef _WANPIPE_SOCK_DRIVER_COMMON_H
-#define _WANPIPE_SOCK_DRIVER_COMMON_H
-
-typedef struct {
-	struct net_device *slave;
-	atomic_t packet_sent;
-	atomic_t receive_block;
-	atomic_t command;
-	atomic_t disconnect;
-	atomic_t driver_busy;
-	long common_critical;
-	struct timer_list *tx_timer;
-	struct sock *sk;		/* Wanpipe Sock bind's here */ 
-	int (*func)(struct sk_buff *skb, struct net_device *dev, 
-		    struct sock *sk);
-
-	struct work_struct wanpipe_work;    /* deferred keventd work */
-	unsigned char rw_bind;			  /* Sock bind state */
-	unsigned char usedby;
-	unsigned char state;
-	unsigned char svc;
-	unsigned short lcn;
-	void *mbox;
-} wanpipe_common_t;
-
-
-enum {
-	WANSOCK_UNCONFIGURED,	/* link/channel is not configured */
-	WANSOCK_DISCONNECTED,	/* link/channel is disconnected */
-	WANSOCK_CONNECTING,		/* connection is in progress */
-	WANSOCK_CONNECTED,		/* link/channel is operational */
-	WANSOCK_LIMIT,		/* for verification only */
-	WANSOCK_DUALPORT,		/* for Dual Port cards */
-	WANSOCK_DISCONNECTING,
-	WANSOCK_BINDED,
-	WANSOCK_BIND_LISTEN,
-	WANSOCK_LISTEN
-};
-
-#endif
-
-
diff --git a/include/linux/igmp.h b/include/linux/igmp.h
index a113fe6..f510e7e 100644
--- a/include/linux/igmp.h
+++ b/include/linux/igmp.h
@@ -80,6 +80,27 @@
 	__be32 srcs[0];
 };
 
+#ifdef __KERNEL__
+#include <linux/skbuff.h>
+
+static inline struct igmphdr *igmp_hdr(const struct sk_buff *skb)
+{
+	return (struct igmphdr *)skb_transport_header(skb);
+}
+
+static inline struct igmpv3_report *
+			igmpv3_report_hdr(const struct sk_buff *skb)
+{
+	return (struct igmpv3_report *)skb_transport_header(skb);
+}
+
+static inline struct igmpv3_query *
+			igmpv3_query_hdr(const struct sk_buff *skb)
+{
+	return (struct igmpv3_query *)skb_transport_header(skb);
+}
+#endif
+
 #define IGMP_HOST_MEMBERSHIP_QUERY	0x11	/* From RFC1112 */
 #define IGMP_HOST_MEMBERSHIP_REPORT	0x12	/* Ditto */
 #define IGMP_DVMRP			0x13	/* DVMRP routing */
diff --git a/include/linux/in.h b/include/linux/in.h
index 1912e7c..3975cbf 100644
--- a/include/linux/in.h
+++ b/include/linux/in.h
@@ -83,6 +83,7 @@
 #define IP_PMTUDISC_DONT		0	/* Never send DF frames */
 #define IP_PMTUDISC_WANT		1	/* Use per route hints	*/
 #define IP_PMTUDISC_DO			2	/* Always DF		*/
+#define IP_PMTUDISC_PROBE		3       /* Ignore dst pmtu      */
 
 #define IP_MULTICAST_IF			32
 #define IP_MULTICAST_TTL 		33
diff --git a/include/linux/in6.h b/include/linux/in6.h
index 4e8350a..2a61c82 100644
--- a/include/linux/in6.h
+++ b/include/linux/in6.h
@@ -44,10 +44,8 @@
  * NOTE: Be aware the IN6ADDR_* constants and in6addr_* externals are defined
  * in network byte order, not in host byte order as are the IPv4 equivalents
  */
-#if 0
 extern const struct in6_addr in6addr_any;
 #define IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }
-#endif
 extern const struct in6_addr in6addr_loopback;
 #define IN6ADDR_LOOPBACK_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }
 
@@ -179,6 +177,7 @@
 #define IPV6_PMTUDISC_DONT		0
 #define IPV6_PMTUDISC_WANT		1
 #define IPV6_PMTUDISC_DO		2
+#define IPV6_PMTUDISC_PROBE		3
 
 /* Flowlabel */
 #define IPV6_FLOWLABEL_MGR	32
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index 6859a3b..71ea9231 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -99,7 +99,6 @@
 extern struct resource iomem_resource;
 
 extern int request_resource(struct resource *root, struct resource *new);
-extern struct resource * ____request_resource(struct resource *root, struct resource *new);
 extern int release_resource(struct resource *new);
 extern int insert_resource(struct resource *parent, struct resource *new);
 extern int allocate_resource(struct resource *root, struct resource *new,
diff --git a/include/linux/ip.h b/include/linux/ip.h
index 1d36b97..bd0a2a8 100644
--- a/include/linux/ip.h
+++ b/include/linux/ip.h
@@ -104,6 +104,20 @@
 	/*The options start here. */
 };
 
+#ifdef __KERNEL__
+#include <linux/skbuff.h>
+
+static inline struct iphdr *ip_hdr(const struct sk_buff *skb)
+{
+	return (struct iphdr *)skb_network_header(skb);
+}
+
+static inline struct iphdr *ipip_hdr(const struct sk_buff *skb)
+{
+	return (struct iphdr *)skb_transport_header(skb);
+}
+#endif
+
 struct ip_auth_hdr {
 	__u8  nexthdr;
 	__u8  hdrlen;		/* This one is measured in 32 bit units! */
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 713eb5e..09ea01a 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -178,6 +178,9 @@
 #endif
 	__s32		proxy_ndp;
 	__s32		accept_source_route;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	__s32		optimistic_dad;
+#endif
 	void		*sysctl;
 };
 
@@ -208,6 +211,7 @@
 	DEVCONF_PROXY_NDP,
 	__DEVCONF_OPTIMISTIC_DAD,
 	DEVCONF_ACCEPT_SOURCE_ROUTE,
+	DEVCONF_OPTIMISTIC_DAD,
 	DEVCONF_MAX
 };
 
@@ -219,6 +223,16 @@
 #include <net/if_inet6.h>       /* struct ipv6_mc_socklist */
 #include <net/inet_sock.h>
 
+static inline struct ipv6hdr *ipv6_hdr(const struct sk_buff *skb)
+{
+	return (struct ipv6hdr *)skb_network_header(skb);
+}
+
+static inline struct ipv6hdr *ipipv6_hdr(const struct sk_buff *skb)
+{
+	return (struct ipv6hdr *)skb_transport_header(skb);
+}
+
 /* 
    This structure contains results of exthdrs parsing
    as offsets from skb->nh.
diff --git a/include/linux/jhash.h b/include/linux/jhash.h
index 82c7ae4..2a2f99f 100644
--- a/include/linux/jhash.h
+++ b/include/linux/jhash.h
@@ -84,7 +84,7 @@
 /* A special optimized version that handles 1 or more of u32s.
  * The length parameter here is the number of u32s in the key.
  */
-static inline u32 jhash2(u32 *k, u32 length, u32 initval)
+static inline u32 jhash2(const u32 *k, u32 length, u32 initval)
 {
 	u32 a, b, c, len;
 
diff --git a/include/linux/kallsyms.h b/include/linux/kallsyms.h
index 1cebcbc..3e3b92d 100644
--- a/include/linux/kallsyms.h
+++ b/include/linux/kallsyms.h
@@ -7,6 +7,8 @@
 
 
 #define KSYM_NAME_LEN 127
+#define KSYM_SYMBOL_LEN (sizeof("%s+%#lx/%#lx [%s]") + KSYM_NAME_LEN +	\
+			 2*(BITS_PER_LONG*3/10) + MODULE_NAME_LEN + 1)
 
 #ifdef CONFIG_KALLSYMS
 /* Lookup the address for a symbol. Returns 0 if not found. */
@@ -22,7 +24,10 @@
 			    unsigned long *offset,
 			    char **modname, char *namebuf);
 
-/* Replace "%s" in format with address, if found */
+/* Look up a kernel symbol and return it in a text buffer. */
+extern int sprint_symbol(char *buffer, unsigned long address);
+
+/* Look up a kernel symbol and print it to the kernel messages. */
 extern void __print_symbol(const char *fmt, unsigned long address);
 
 #else /* !CONFIG_KALLSYMS */
@@ -47,6 +52,12 @@
 	return NULL;
 }
 
+static inline int sprint_symbol(char *buffer, unsigned long addr)
+{
+	*buffer = '\0';
+	return 0;
+}
+
 /* Stupid that this does nothing, but I didn't create this mess. */
 #define __print_symbol(fmt, addr)
 #endif /*CONFIG_KALLSYMS*/
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 9ddf25c..e2f41b0 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -121,6 +121,7 @@
 	__attribute__ ((format (printf, 3, 0)));
 extern char *kasprintf(gfp_t gfp, const char *fmt, ...)
 	__attribute__ ((format (printf, 2, 3)));
+extern char *kvasprintf(gfp_t gfp, const char *fmt, va_list args);
 
 extern int sscanf(const char *, const char *, ...)
 	__attribute__ ((format (scanf, 2, 3)));
diff --git a/include/linux/key.h b/include/linux/key.h
index 169f05e4..a9220e7 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -160,6 +160,8 @@
 	 */
 	union {
 		struct list_head	link;
+		unsigned long		x[2];
+		void			*p[2];
 	} type_data;
 
 	/* key data
diff --git a/include/linux/kobject.h b/include/linux/kobject.h
index b850e03..eb0e63e 100644
--- a/include/linux/kobject.h
+++ b/include/linux/kobject.h
@@ -22,7 +22,6 @@
 #include <linux/sysfs.h>
 #include <linux/compiler.h>
 #include <linux/spinlock.h>
-#include <linux/rwsem.h>
 #include <linux/kref.h>
 #include <linux/kernel.h>
 #include <linux/wait.h>
@@ -43,11 +42,9 @@
 	KOBJ_ADD	= (__force kobject_action_t) 0x01,	/* exclusive to core */
 	KOBJ_REMOVE	= (__force kobject_action_t) 0x02,	/* exclusive to core */
 	KOBJ_CHANGE	= (__force kobject_action_t) 0x03,	/* device state change */
-	KOBJ_MOUNT	= (__force kobject_action_t) 0x04,	/* mount event for block devices (broken) */
-	KOBJ_UMOUNT	= (__force kobject_action_t) 0x05,	/* umount event for block devices (broken) */
-	KOBJ_OFFLINE	= (__force kobject_action_t) 0x06,	/* device offline */
-	KOBJ_ONLINE	= (__force kobject_action_t) 0x07,	/* device online */
-	KOBJ_MOVE	= (__force kobject_action_t) 0x08,	/* device move */
+	KOBJ_OFFLINE	= (__force kobject_action_t) 0x04,	/* device offline */
+	KOBJ_ONLINE	= (__force kobject_action_t) 0x05,	/* device online */
+	KOBJ_MOVE	= (__force kobject_action_t) 0x06,	/* device move */
 };
 
 struct kobject {
@@ -89,6 +86,8 @@
 extern struct kobject * kobject_get(struct kobject *);
 extern void kobject_put(struct kobject *);
 
+extern struct kobject *kobject_kset_add_dir(struct kset *kset,
+					    struct kobject *, const char *);
 extern struct kobject *kobject_add_dir(struct kobject *, const char *);
 
 extern char * kobject_get_path(struct kobject *, gfp_t);
@@ -175,7 +174,6 @@
 
 struct subsystem {
 	struct kset		kset;
-	struct rw_semaphore	rwsem;
 };
 
 #define decl_subsys(_name,_type,_uevent_ops) \
diff --git a/include/linux/ktime.h b/include/linux/ktime.h
index 248305b..81bb9c7 100644
--- a/include/linux/ktime.h
+++ b/include/linux/ktime.h
@@ -259,6 +259,12 @@
 
 #endif
 
+static inline s64 ktime_to_us(const ktime_t kt)
+{
+	struct timeval tv = ktime_to_timeval(kt);
+	return (s64) tv.tv_sec * USEC_PER_SEC + tv.tv_usec;
+}
+
 /*
  * The resolution of the clocks. The resolution value is returned in
  * the clock_getres() system call to give application programmers an
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 0cfbcb6..d8cfc72 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -210,6 +210,7 @@
 
 	/* host set flags */
 	ATA_HOST_SIMPLEX	= (1 << 0),	/* Host is simplex, one DMA channel per host only */
+	ATA_HOST_STARTED	= (1 << 1),	/* Host started */
 
 	/* various lengths of time */
 	ATA_TMOUT_BOOT		= 30 * HZ,	/* heuristic */
@@ -281,11 +282,13 @@
 	ATA_EHI_NO_AUTOPSY	= (1 << 2),  /* no autopsy */
 	ATA_EHI_QUIET		= (1 << 3),  /* be quiet */
 
-	ATA_EHI_DID_RESET	= (1 << 16), /* already reset this port */
-	ATA_EHI_PRINTINFO	= (1 << 17), /* print configuration info */
-	ATA_EHI_SETMODE		= (1 << 18), /* configure transfer mode */
-	ATA_EHI_POST_SETMODE	= (1 << 19), /* revaildating after setmode */
+	ATA_EHI_DID_SOFTRESET	= (1 << 16), /* already soft-reset this port */
+	ATA_EHI_DID_HARDRESET	= (1 << 17), /* already soft-reset this port */
+	ATA_EHI_PRINTINFO	= (1 << 18), /* print configuration info */
+	ATA_EHI_SETMODE		= (1 << 19), /* configure transfer mode */
+	ATA_EHI_POST_SETMODE	= (1 << 20), /* revaildating after setmode */
 
+	ATA_EHI_DID_RESET	= ATA_EHI_DID_SOFTRESET | ATA_EHI_DID_HARDRESET,
 	ATA_EHI_RESET_MODIFIER_MASK = ATA_EHI_RESUME_LINK,
 
 	/* max repeat if error condition is still set after ->error_handler */
@@ -367,34 +370,6 @@
 	void __iomem		*scr_addr;
 };
 
-struct ata_probe_ent {
-	struct list_head	node;
-	struct device 		*dev;
-	const struct ata_port_operations *port_ops;
-	struct scsi_host_template *sht;
-	struct ata_ioports	port[ATA_MAX_PORTS];
-	unsigned int		n_ports;
-	unsigned int		dummy_port_mask;
-	unsigned int		pio_mask;
-	unsigned int		mwdma_mask;
-	unsigned int		udma_mask;
-	unsigned long		irq;
-	unsigned long		irq2;
-	unsigned int		irq_flags;
-	unsigned long		port_flags;
-	unsigned long		_host_flags;
-	void __iomem * const	*iomap;
-	void			*private_data;
-
-	/* port_info for the secondary port.  Together with irq2, it's
-	 * used to implement non-uniform secondary port.  Currently,
-	 * the only user is ata_piix combined mode.  This workaround
-	 * will be removed together with ata_probe_ent when init model
-	 * is updated.
-	 */
-	const struct ata_port_info *pinfo2;
-};
-
 struct ata_host {
 	spinlock_t		lock;
 	struct device 		*dev;
@@ -427,6 +402,7 @@
 	int			dma_dir;
 
 	unsigned int		pad_len;
+	unsigned int		sect_size;
 
 	unsigned int		nbytes;
 	unsigned int		curbytes;
@@ -472,6 +448,7 @@
 	struct scsi_device	*sdev;		/* attached SCSI device */
 	/* n_sector is used as CLEAR_OFFSET, read comment above CLEAR_OFFSET */
 	u64			n_sectors;	/* size of device, if ATA */
+	u64			n_sectors_boot;	/* size of ATA device at startup */
 	unsigned int		class;		/* ATA_DEV_xxx */
 	u16			id[ATA_ID_WORDS]; /* IDENTIFY xxx DEVICE data */
 	u8			pio_mode;
@@ -597,11 +574,11 @@
 struct ata_port_operations {
 	void (*port_disable) (struct ata_port *);
 
-	void (*dev_config) (struct ata_port *, struct ata_device *);
+	void (*dev_config) (struct ata_device *);
 
 	void (*set_piomode) (struct ata_port *, struct ata_device *);
 	void (*set_dmamode) (struct ata_port *, struct ata_device *);
-	unsigned long (*mode_filter) (const struct ata_port *, struct ata_device *, unsigned long);
+	unsigned long (*mode_filter) (struct ata_device *, unsigned long);
 
 	void (*tf_load) (struct ata_port *ap, const struct ata_taskfile *tf);
 	void (*tf_read) (struct ata_port *ap, struct ata_taskfile *tf);
@@ -616,6 +593,8 @@
 
 	void (*post_set_mode) (struct ata_port *ap);
 
+	int (*cable_detect) (struct ata_port *ap);
+
 	int  (*check_atapi_dma) (struct ata_queued_cmd *qc);
 
 	void (*bmdma_setup) (struct ata_queued_cmd *qc);
@@ -664,6 +643,7 @@
 	unsigned long		mwdma_mask;
 	unsigned long		udma_mask;
 	const struct ata_port_operations *port_ops;
+	irq_handler_t		irq_handler;
 	void 			*private_data;
 };
 
@@ -686,6 +666,7 @@
 extern const unsigned long sata_deb_timing_long[];
 
 extern const struct ata_port_operations ata_dummy_port_ops;
+extern const struct ata_port_info ata_dummy_port_info;
 
 static inline const unsigned long *
 sata_ehc_deb_timing(struct ata_eh_context *ehc)
@@ -701,6 +682,7 @@
 	return ap->ops == &ata_dummy_port_ops;
 }
 
+extern void sata_print_link_status(struct ata_port *ap);
 extern void ata_port_probe(struct ata_port *);
 extern void __sata_phy_reset(struct ata_port *ap);
 extern void sata_phy_reset(struct ata_port *ap);
@@ -728,7 +710,15 @@
 #endif
 extern int ata_pci_clear_simplex(struct pci_dev *pdev);
 #endif /* CONFIG_PCI */
-extern int ata_device_add(const struct ata_probe_ent *ent);
+extern struct ata_host *ata_host_alloc(struct device *dev, int max_ports);
+extern struct ata_host *ata_host_alloc_pinfo(struct device *dev,
+			const struct ata_port_info * const * ppi, int n_ports);
+extern int ata_host_start(struct ata_host *host);
+extern int ata_host_register(struct ata_host *host,
+			     struct scsi_host_template *sht);
+extern int ata_host_activate(struct ata_host *host, int irq,
+			     irq_handler_t irq_handler, unsigned long irq_flags,
+			     struct scsi_host_template *sht);
 extern void ata_host_detach(struct ata_host *host);
 extern void ata_host_init(struct ata_host *, struct device *,
 			  unsigned long, const struct ata_port_operations *);
@@ -828,11 +818,17 @@
 extern int ata_scsi_change_queue_depth(struct scsi_device *sdev,
 				       int queue_depth);
 extern struct ata_device *ata_dev_pair(struct ata_device *adev);
+extern int ata_do_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev);
 extern u8 ata_irq_on(struct ata_port *ap);
 extern u8 ata_dummy_irq_on(struct ata_port *ap);
 extern u8 ata_irq_ack(struct ata_port *ap, unsigned int chk_drq);
 extern u8 ata_dummy_irq_ack(struct ata_port *ap, unsigned int chk_drq);
 
+extern int ata_cable_40wire(struct ata_port *ap);
+extern int ata_cable_80wire(struct ata_port *ap);
+extern int ata_cable_sata(struct ata_port *ap);
+extern int ata_cable_unknown(struct ata_port *ap);
+
 /*
  * Timing helpers
  */
@@ -870,10 +866,13 @@
 	unsigned long		val;
 };
 
-extern struct ata_probe_ent *
-ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int portmask);
+extern int ata_pci_init_native_host(struct ata_host *host,
+				    unsigned int port_mask);
+extern int ata_pci_prepare_native_host(struct pci_dev *pdev,
+				const struct ata_port_info * const * ppi,
+				int n_ports, struct ata_host **r_host);
 extern int pci_test_config_bits(struct pci_dev *pdev, const struct pci_bits *bits);
-extern unsigned long ata_pci_default_filter(const struct ata_port *, struct ata_device *, unsigned long);
+extern unsigned long ata_pci_default_filter(struct ata_device *, unsigned long);
 #endif /* CONFIG_PCI */
 
 /*
@@ -1173,6 +1172,7 @@
 	qc->n_elem = 0;
 	qc->err_mask = 0;
 	qc->pad_len = 0;
+	qc->sect_size = ATA_SECT_SIZE;
 
 	ata_tf_init(qc->dev, &qc->tf);
 
@@ -1220,7 +1220,7 @@
 
 static inline struct ata_port *ata_shost_to_port(struct Scsi_Host *host)
 {
-	return (struct ata_port *) &host->hostdata[0];
+	return *(struct ata_port **)&host->hostdata[0];
 }
 
 #endif /* __LINUX_LIBATA_H__ */
diff --git a/include/linux/mtd/iflash.h b/include/linux/mtd/iflash.h
deleted file mode 100644
index 9aa5b4f..0000000
--- a/include/linux/mtd/iflash.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/* $Id: iflash.h,v 1.2 2000/11/13 18:01:54 dwmw2 Exp $ */
-
-#ifndef __MTD_IFLASH_H__
-#define __MTD_IFLASH_H__
-
-/* Extended CIS registers for Series 2 and 2+ cards */
-/* The registers are all offsets from 0x4000 */
-#define CISREG_CSR		0x0100
-#define CISREG_WP		0x0104
-#define CISREG_RDYBSY		0x0140
-
-/* Extended CIS registers for Series 2 cards */
-#define CISREG_SLEEP		0x0118
-#define CISREG_RDY_MASK		0x0120
-#define CISREG_RDY_STATUS	0x0130
-
-/* Extended CIS registers for Series 2+ cards */
-#define CISREG_VCR		0x010c
-
-/* Card Status Register */
-#define CSR_SRESET		0x20	/* Soft reset */
-#define CSR_CMWP		0x10	/* Common memory write protect */
-#define CSR_PWRDOWN		0x08	/* Power down status */
-#define CSR_CISWP		0x04	/* Common memory CIS WP */
-#define CSR_WP			0x02	/* Mechanical write protect */
-#define CSR_READY		0x01	/* Ready/busy status */
-
-/* Write Protection Register */
-#define WP_BLKEN		0x04	/* Enable block locking */
-#define WP_CMWP			0x02	/* Common memory write protect */
-#define WP_CISWP		0x01	/* Common memory CIS WP */
-
-/* Voltage Control Register */
-#define VCR_VCC_LEVEL		0x80	/* 0 = 5V, 1 = 3.3V */
-#define VCR_VPP_VALID		0x02	/* Vpp Valid */
-#define VCR_VPP_GEN		0x01	/* Integrated Vpp generator */
-
-/* Ready/Busy Mode Register */
-#define RDYBSY_RACK		0x02	/* Ready acknowledge */
-#define RDYBSY_MODE		0x01	/* 1 = high performance */
-
-#define LOW(x) ((x) & 0xff)
-
-/* 28F008SA-Compatible Command Set */
-#define IF_READ_ARRAY		0xffff
-#define IF_INTEL_ID		0x9090
-#define IF_READ_CSR		0x7070
-#define IF_CLEAR_CSR		0x5050
-#define IF_WRITE		0x4040
-#define IF_BLOCK_ERASE		0x2020
-#define IF_ERASE_SUSPEND	0xb0b0
-#define IF_CONFIRM		0xd0d0
-
-/* 28F016SA Performance Enhancement Commands */
-#define IF_READ_PAGE		0x7575
-#define IF_PAGE_SWAP		0x7272
-#define IF_SINGLE_LOAD		0x7474
-#define IF_SEQ_LOAD		0xe0e0
-#define IF_PAGE_WRITE		0x0c0c
-#define IF_RDY_MODE		0x9696
-#define IF_RDY_LEVEL		0x0101
-#define IF_RDY_PULSE_WRITE	0x0202
-#define IF_RDY_PULSE_ERASE	0x0303
-#define IF_RDY_DISABLE		0x0404
-#define IF_LOCK_BLOCK		0x7777
-#define IF_UPLOAD_STATUS	0x9797
-#define IF_READ_ESR		0x7171
-#define IF_ERASE_UNLOCKED	0xa7a7
-#define IF_SLEEP		0xf0f0
-#define IF_ABORT		0x8080
-#define IF_UPLOAD_DEVINFO	0x9999
-
-/* Definitions for Compatible Status Register */
-#define CSR_WR_READY		0x8080	/* Write state machine status */
-#define CSR_ERA_SUSPEND		0x4040	/* Erase suspend status */
-#define CSR_ERA_ERR		0x2020	/* Erase status */
-#define CSR_WR_ERR		0x1010	/* Data write status */
-#define CSR_VPP_LOW		0x0808	/* Vpp status */
-
-/* Definitions for Global Status Register */
-#define GSR_WR_READY		0x8080	/* Write state machine status */
-#define GSR_OP_SUSPEND		0x4040	/* Operation suspend status */
-#define GSR_OP_ERR		0x2020	/* Device operation status */
-#define GSR_SLEEP		0x1010	/* Device sleep status */
-#define GSR_QUEUE_FULL		0x0808	/* Queue status */
-#define GSR_PAGE_AVAIL		0x0404	/* Page buffer available status */
-#define GSR_PAGE_READY		0x0202	/* Page buffer status */
-#define GSR_PAGE_SELECT		0x0101	/* Page buffer select status */
-
-/* Definitions for Block Status Register */
-#define BSR_READY		0x8080	/* Block status */
-#define BSR_UNLOCK		0x4040	/* Block lock status */
-#define BSR_FAILED		0x2020	/* Block operation status */
-#define BSR_ABORTED		0x1010	/* Operation abort status */
-#define BSR_QUEUE_FULL		0x0808	/* Queue status */
-#define BSR_VPP_LOW		0x0404	/* Vpp status */
-
-#endif /* __MTD_IFLASH_H__ */
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 3d956c3a..45d482c 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -53,6 +53,7 @@
 	u_int32_t offset;			/* At which this region starts, from the beginning of the MTD */
 	u_int32_t erasesize;		/* For this region */
 	u_int32_t numblocks;		/* Number of blocks of erasesize in this region */
+	unsigned long *lockmap;		/* If keeping bitmap of locks */
 };
 
 /*
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 9752388..cf197ad 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -431,6 +431,7 @@
 #define NAND_MFR_RENESAS	0x07
 #define NAND_MFR_STMICRO	0x20
 #define NAND_MFR_HYNIX		0xad
+#define NAND_MFR_MICRON		0x2c
 
 /**
  * struct nand_flash_dev - NAND Flash Device ID Structure
diff --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h
new file mode 100644
index 0000000..3d967b6
--- /dev/null
+++ b/include/linux/mtd/ubi.h
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+#ifndef __LINUX_UBI_H__
+#define __LINUX_UBI_H__
+
+#include <asm/ioctl.h>
+#include <linux/types.h>
+#include <mtd/ubi-user.h>
+
+/*
+ * UBI data type hint constants.
+ *
+ * UBI_LONGTERM: long-term data
+ * UBI_SHORTTERM: short-term data
+ * UBI_UNKNOWN: data persistence is unknown
+ *
+ * These constants are used when data is written to UBI volumes in order to
+ * help the UBI wear-leveling unit to find more appropriate physical
+ * eraseblocks.
+ */
+enum {
+	UBI_LONGTERM = 1,
+	UBI_SHORTTERM,
+	UBI_UNKNOWN
+};
+
+/*
+ * enum ubi_open_mode - UBI volume open mode constants.
+ *
+ * UBI_READONLY: read-only mode
+ * UBI_READWRITE: read-write mode
+ * UBI_EXCLUSIVE: exclusive mode
+ */
+enum {
+	UBI_READONLY = 1,
+	UBI_READWRITE,
+	UBI_EXCLUSIVE
+};
+
+/**
+ * struct ubi_volume_info - UBI volume description data structure.
+ * @vol_id: volume ID
+ * @ubi_num: UBI device number this volume belongs to
+ * @size: how many physical eraseblocks are reserved for this volume
+ * @used_bytes: how many bytes of data this volume contains
+ * @used_ebs: how many physical eraseblocks of this volume actually contain any
+ * data
+ * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ * @corrupted: non-zero if the volume is corrupted (static volumes only)
+ * @upd_marker: non-zero if the volume has update marker set
+ * @alignment: volume alignment
+ * @usable_leb_size: how many bytes are available in logical eraseblocks of
+ * this volume
+ * @name_len: volume name length
+ * @name: volume name
+ * @cdev: UBI volume character device major and minor numbers
+ *
+ * The @corrupted flag is only relevant to static volumes and is always zero
+ * for dynamic ones. This is because UBI does not care about dynamic volume
+ * data protection and only cares about protecting static volume data.
+ *
+ * The @upd_marker flag is set if the volume update operation was interrupted.
+ * Before touching the volume data during the update operation, UBI first sets
+ * the update marker flag for this volume. If the volume update operation was
+ * further interrupted, the update marker indicates this. If the update marker
+ * is set, the contents of the volume is certainly damaged and a new volume
+ * update operation has to be started.
+ *
+ * To put it differently, @corrupted and @upd_marker fields have different
+ * semantics:
+ *     o the @corrupted flag means that this static volume is corrupted for some
+ *       reasons, but not because an interrupted volume update
+ *     o the @upd_marker field means that the volume is damaged because of an
+ *       interrupted update operation.
+ *
+ * I.e., the @corrupted flag is never set if the @upd_marker flag is set.
+ *
+ * The @used_bytes and @used_ebs fields are only really needed for static
+ * volumes and contain the number of bytes stored in this static volume and how
+ * many eraseblock this data occupies. In case of dynamic volumes, the
+ * @used_bytes field is equivalent to @size*@usable_leb_size, and the @used_ebs
+ * field is equivalent to @size.
+ *
+ * In general, logical eraseblock size is a property of the UBI device, not
+ * of the UBI volume. Indeed, the logical eraseblock size depends on the
+ * physical eraseblock size and on how much bytes UBI headers consume. But
+ * because of the volume alignment (@alignment), the usable size of logical
+ * eraseblocks if a volume may be less. The following equation is true:
+ * 	@usable_leb_size = LEB size - (LEB size mod @alignment),
+ * where LEB size is the logical eraseblock size defined by the UBI device.
+ *
+ * The alignment is multiple to the minimal flash input/output unit size or %1
+ * if all the available space is used.
+ *
+ * To put this differently, alignment may be considered is a way to change
+ * volume logical eraseblock sizes.
+ */
+struct ubi_volume_info {
+	int ubi_num;
+	int vol_id;
+	int size;
+	long long used_bytes;
+	int used_ebs;
+	int vol_type;
+	int corrupted;
+	int upd_marker;
+	int alignment;
+	int usable_leb_size;
+	int name_len;
+	const char *name;
+	dev_t cdev;
+};
+
+/**
+ * struct ubi_device_info - UBI device description data structure.
+ * @ubi_num: ubi device number
+ * @leb_size: logical eraseblock size on this UBI device
+ * @min_io_size: minimal I/O unit size
+ * @ro_mode: if this device is in read-only mode
+ * @cdev: UBI character device major and minor numbers
+ *
+ * Note, @leb_size is the logical eraseblock size offered by the UBI device.
+ * Volumes of this UBI device may have smaller logical eraseblock size if their
+ * alignment is not equivalent to %1.
+ */
+struct ubi_device_info {
+	int ubi_num;
+	int leb_size;
+	int min_io_size;
+	int ro_mode;
+	dev_t cdev;
+};
+
+/* UBI descriptor given to users when they open UBI volumes */
+struct ubi_volume_desc;
+
+int ubi_get_device_info(int ubi_num, struct ubi_device_info *di);
+void ubi_get_volume_info(struct ubi_volume_desc *desc,
+			 struct ubi_volume_info *vi);
+struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode);
+struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
+					   int mode);
+void ubi_close_volume(struct ubi_volume_desc *desc);
+int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
+		 int len, int check);
+int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
+		  int offset, int len, int dtype);
+int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
+		   int len, int dtype);
+int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum);
+int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum);
+int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum);
+
+/*
+ * This function is the same as the 'ubi_leb_read()' function, but it does not
+ * provide the checking capability.
+ */
+static inline int ubi_read(struct ubi_volume_desc *desc, int lnum, char *buf,
+			   int offset, int len)
+{
+	return ubi_leb_read(desc, lnum, buf, offset, len, 0);
+}
+
+/*
+ * This function is the same as the 'ubi_leb_write()' functions, but it does
+ * not have the data type argument.
+ */
+static inline int ubi_write(struct ubi_volume_desc *desc, int lnum,
+			    const void *buf, int offset, int len)
+{
+	return ubi_leb_write(desc, lnum, buf, offset, len, UBI_UNKNOWN);
+}
+
+/*
+ * This function is the same as the 'ubi_leb_change()' functions, but it does
+ * not have the data type argument.
+ */
+static inline int ubi_change(struct ubi_volume_desc *desc, int lnum,
+				    const void *buf, int len)
+{
+	return ubi_leb_change(desc, lnum, buf, len, UBI_UNKNOWN);
+}
+
+#endif /* !__LINUX_UBI_H__ */
diff --git a/include/linux/namei.h b/include/linux/namei.h
index d39a5a6..b7dd249 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -82,6 +82,7 @@
 extern void release_open_intent(struct nameidata *);
 
 extern struct dentry * lookup_one_len(const char *, struct dentry *, int);
+extern struct dentry *lookup_one_len_kern(const char *, struct dentry *, int);
 
 extern int follow_down(struct vfsmount **, struct dentry **);
 extern int follow_up(struct vfsmount **, struct dentry **);
diff --git a/include/linux/net.h b/include/linux/net.h
index 4db21e6..efc4517 100644
--- a/include/linux/net.h
+++ b/include/linux/net.h
@@ -24,7 +24,7 @@
 struct poll_table_struct;
 struct inode;
 
-#define NPROTO		33		/* should be enough for now..	*/
+#define NPROTO		34		/* should be enough for now..	*/
 
 #define SYS_SOCKET	1		/* sys_socket(2)		*/
 #define SYS_BIND	2		/* sys_bind(2)			*/
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 1a52854..ac0c92b 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -42,6 +42,8 @@
 struct vlan_group;
 struct ethtool_ops;
 struct netpoll_info;
+/* 802.11 specific */
+struct wireless_dev;
 					/* source back-compat hooks */
 #define SET_ETHTOOL_OPS(netdev,ops) \
 	( (netdev)->ethtool_ops = (ops) )
@@ -347,13 +349,15 @@
 
 
 	struct net_device_stats* (*get_stats)(struct net_device *dev);
+	struct net_device_stats	stats;
 
+#ifdef CONFIG_WIRELESS_EXT
 	/* List of functions to handle Wireless Extensions (instead of ioctl).
 	 * See <net/iw_handler.h> for details. Jean II */
 	const struct iw_handler_def *	wireless_handlers;
 	/* Instance data managed by the core of Wireless Extensions. */
 	struct iw_public_data *	wireless_data;
-
+#endif
 	const struct ethtool_ops *ethtool_ops;
 
 	/*
@@ -398,6 +402,8 @@
 	void                    *ip6_ptr;       /* IPv6 specific data */
 	void			*ec_ptr;	/* Econet specific data	*/
 	void			*ax25_ptr;	/* AX.25 specific data */
+	struct wireless_dev	*ieee80211_ptr;	/* IEEE 802.11 specific data,
+						   assign before registering */
 
 /*
  * Cache line mostly used on receive path (including eth_type_trans())
@@ -647,8 +653,10 @@
 static inline void netif_wake_queue(struct net_device *dev)
 {
 #ifdef CONFIG_NETPOLL_TRAP
-	if (netpoll_trap())
+	if (netpoll_trap()) {
+		clear_bit(__LINK_STATE_XOFF, &dev->state);
 		return;
+	}
 #endif
 	if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->state))
 		__netif_schedule(dev);
@@ -656,10 +664,6 @@
 
 static inline void netif_stop_queue(struct net_device *dev)
 {
-#ifdef CONFIG_NETPOLL_TRAP
-	if (netpoll_trap())
-		return;
-#endif
 	set_bit(__LINK_STATE_XOFF, &dev->state);
 }
 
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
index 70d3b4f..10b5c62 100644
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -281,9 +281,6 @@
 			struct nf_info *info,
 			unsigned int verdict);
 
-extern void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *);
-extern void nf_ct_attach(struct sk_buff *, struct sk_buff *);
-
 /* FIXME: Before cache is ever used, this must be implemented for real. */
 extern void nf_invalidate_cache(int pf);
 
@@ -388,11 +385,18 @@
 {
 	return 1;
 }
-static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {}
 struct flowi;
 static inline void
 nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, int family) {}
 #endif /*CONFIG_NETFILTER*/
 
+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+extern void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *);
+extern void nf_ct_attach(struct sk_buff *, struct sk_buff *);
+extern void (*nf_ct_destroy)(struct nf_conntrack *);
+#else
+static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {}
+#endif
+
 #endif /*__KERNEL__*/
 #endif /*__LINUX_NETFILTER_H*/
diff --git a/include/linux/netfilter/nf_conntrack_tcp.h b/include/linux/netfilter/nf_conntrack_tcp.h
index 007af4c..22ce299 100644
--- a/include/linux/netfilter/nf_conntrack_tcp.h
+++ b/include/linux/netfilter/nf_conntrack_tcp.h
@@ -30,6 +30,11 @@
 /* Be liberal in window checking */
 #define IP_CT_TCP_FLAG_BE_LIBERAL		0x08
 
+struct nf_ct_tcp_flags {
+	u_int8_t flags;
+	u_int8_t mask;
+};
+
 #ifdef __KERNEL__
 
 struct ip_ct_tcp_state {
diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h
index 1e9c821..0f9311d 100644
--- a/include/linux/netfilter/nfnetlink.h
+++ b/include/linux/netfilter/nfnetlink.h
@@ -62,11 +62,11 @@
 #define NFA_DATA(nfa)   ((void *)(((char *)(nfa)) + NFA_LENGTH(0)))
 #define NFA_PAYLOAD(nfa) ((int)((nfa)->nfa_len) - NFA_LENGTH(0))
 #define NFA_NEST(skb, type) \
-({	struct nfattr *__start = (struct nfattr *) (skb)->tail; \
+({	struct nfattr *__start = (struct nfattr *)skb_tail_pointer(skb); \
 	NFA_PUT(skb, (NFNL_NFA_NEST | type), 0, NULL); \
 	__start;  })
 #define NFA_NEST_END(skb, start) \
-({      (start)->nfa_len = ((skb)->tail - (unsigned char *) (start)); \
+({      (start)->nfa_len = skb_tail_pointer(skb) - (unsigned char *)(start); \
         (skb)->len; })
 #define NFA_NEST_CANCEL(skb, start) \
 ({      if (start) \
@@ -111,7 +111,7 @@
 struct nfnl_callback
 {
 	int (*call)(struct sock *nl, struct sk_buff *skb, 
-		struct nlmsghdr *nlh, struct nfattr *cda[], int *errp);
+		struct nlmsghdr *nlh, struct nfattr *cda[]);
 	u_int16_t attr_count;	/* number of nfattr's */
 };
 
@@ -129,19 +129,6 @@
 ({ if (skb_tailroom(skb) < (int)NFA_SPACE(attrlen)) goto nfattr_failure; \
    __nfa_fill(skb, attrtype, attrlen, data); })
 
-extern struct semaphore nfnl_sem;
-
-#define nfnl_shlock()		down(&nfnl_sem)
-#define nfnl_shlock_nowait()	down_trylock(&nfnl_sem)
-
-#define nfnl_shunlock()		do { up(&nfnl_sem); \
-				     if(nfnl && nfnl->sk_receive_queue.qlen) \
-					    nfnl->sk_data_ready(nfnl, 0); \
-                        	} while(0)
-
-extern void nfnl_lock(void);
-extern void nfnl_unlock(void);
-
 extern int nfnetlink_subsys_register(struct nfnetlink_subsystem *n);
 extern int nfnetlink_subsys_unregister(struct nfnetlink_subsystem *n);
 
diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h
index b5883cc..d7c3503 100644
--- a/include/linux/netfilter/nfnetlink_conntrack.h
+++ b/include/linux/netfilter/nfnetlink_conntrack.h
@@ -83,6 +83,10 @@
 enum ctattr_protoinfo_tcp {
 	CTA_PROTOINFO_TCP_UNSPEC,
 	CTA_PROTOINFO_TCP_STATE,
+	CTA_PROTOINFO_TCP_WSCALE_ORIGINAL,
+	CTA_PROTOINFO_TCP_WSCALE_REPLY,
+	CTA_PROTOINFO_TCP_FLAGS_ORIGINAL,
+	CTA_PROTOINFO_TCP_FLAGS_REPLY,
 	__CTA_PROTOINFO_TCP_MAX
 };
 #define CTA_PROTOINFO_TCP_MAX (__CTA_PROTOINFO_TCP_MAX - 1)
diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h
index 55689f3..1906003 100644
--- a/include/linux/netfilter_bridge.h
+++ b/include/linux/netfilter_bridge.h
@@ -7,6 +7,7 @@
 #include <linux/netfilter.h>
 #include <linux/if_ether.h>
 #include <linux/if_vlan.h>
+#include <linux/if_pppox.h>
 
 /* Bridge Hooks */
 /* After promisc drops, checksum checks. */
@@ -58,8 +59,14 @@
  * enough room for the encapsulating header (if there is one). */
 static inline int nf_bridge_pad(const struct sk_buff *skb)
 {
- 	return (skb->nf_bridge && skb->protocol == htons(ETH_P_8021Q))
-		? VLAN_HLEN : 0;
+	int padding = 0;
+
+	if (skb->nf_bridge && skb->protocol == htons(ETH_P_8021Q))
+		padding = VLAN_HLEN;
+	else if (skb->nf_bridge && skb->protocol == htons(ETH_P_PPP_SES))
+		padding = PPPOE_SES_HLEN;
+
+	return padding;
 }
 
 struct bridge_skb_cb {
diff --git a/include/linux/netfilter_bridge/ebt_802_3.h b/include/linux/netfilter_bridge/ebt_802_3.h
index 07f044f..a11b0c2 100644
--- a/include/linux/netfilter_bridge/ebt_802_3.h
+++ b/include/linux/netfilter_bridge/ebt_802_3.h
@@ -54,7 +54,7 @@
 
 static inline struct ebt_802_3_hdr *ebt_802_3_hdr(const struct sk_buff *skb)
 {
-	return (struct ebt_802_3_hdr *)skb->mac.raw;
+	return (struct ebt_802_3_hdr *)skb_mac_header(skb);
 }
 #endif
 
diff --git a/include/linux/netfilter_bridge/ebt_arp.h b/include/linux/netfilter_bridge/ebt_arp.h
index 97e4dbd..cbf4843 100644
--- a/include/linux/netfilter_bridge/ebt_arp.h
+++ b/include/linux/netfilter_bridge/ebt_arp.h
@@ -8,8 +8,10 @@
 #define EBT_ARP_DST_IP 0x10
 #define EBT_ARP_SRC_MAC 0x20
 #define EBT_ARP_DST_MAC 0x40
+#define EBT_ARP_GRAT 0x80
 #define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
-   EBT_ARP_SRC_IP | EBT_ARP_DST_IP | EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC)
+   EBT_ARP_SRC_IP | EBT_ARP_DST_IP | EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC | \
+   EBT_ARP_GRAT)
 #define EBT_ARP_MATCH "arp"
 
 struct ebt_arp_info
diff --git a/include/linux/netfilter_ipv4/Kbuild b/include/linux/netfilter_ipv4/Kbuild
index 1803378..7185792 100644
--- a/include/linux/netfilter_ipv4/Kbuild
+++ b/include/linux/netfilter_ipv4/Kbuild
@@ -1,9 +1,3 @@
-header-y += ip_conntrack_helper.h
-header-y += ip_conntrack_protocol.h
-header-y += ip_conntrack_sctp.h
-header-y += ip_conntrack_tcp.h
-header-y += ip_conntrack_tftp.h
-header-y += ip_nat_pptp.h
 header-y += ipt_addrtype.h
 header-y += ipt_ah.h
 header-y += ipt_CLASSIFY.h
@@ -49,13 +43,5 @@
 header-y += ipt_TTL.h
 header-y += ipt_ULOG.h
 
-unifdef-y += ip_conntrack.h
-unifdef-y += ip_conntrack_h323.h
-unifdef-y += ip_conntrack_irc.h
-unifdef-y += ip_conntrack_pptp.h
-unifdef-y += ip_conntrack_proto_gre.h
-unifdef-y += ip_conntrack_tuple.h
-unifdef-y += ip_nat.h
-unifdef-y += ip_nat_rule.h
 unifdef-y += ip_queue.h
 unifdef-y += ip_tables.h
diff --git a/include/linux/netfilter_ipv4/ip_conntrack.h b/include/linux/netfilter_ipv4/ip_conntrack.h
deleted file mode 100644
index da9274e..0000000
--- a/include/linux/netfilter_ipv4/ip_conntrack.h
+++ /dev/null
@@ -1,402 +0,0 @@
-#ifndef _IP_CONNTRACK_H
-#define _IP_CONNTRACK_H
-
-#include <linux/netfilter/nf_conntrack_common.h>
-
-#ifdef __KERNEL__
-#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
-#include <linux/bitops.h>
-#include <linux/compiler.h>
-#include <asm/atomic.h>
-
-#include <linux/timer.h>
-#include <linux/netfilter_ipv4/ip_conntrack_tcp.h>
-#include <linux/netfilter_ipv4/ip_conntrack_icmp.h>
-#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
-#include <linux/netfilter_ipv4/ip_conntrack_sctp.h>
-
-/* per conntrack: protocol private data */
-union ip_conntrack_proto {
-	/* insert conntrack proto private data here */
-	struct ip_ct_gre gre;
-	struct ip_ct_sctp sctp;
-	struct ip_ct_tcp tcp;
-	struct ip_ct_icmp icmp;
-};
-
-union ip_conntrack_expect_proto {
-	/* insert expect proto private data here */
-};
-
-/* Add protocol helper include file here */
-#include <linux/netfilter_ipv4/ip_conntrack_h323.h>
-#include <linux/netfilter_ipv4/ip_conntrack_pptp.h>
-#include <linux/netfilter_ipv4/ip_conntrack_amanda.h>
-#include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
-#include <linux/netfilter_ipv4/ip_conntrack_irc.h>
-
-/* per conntrack: application helper private data */
-union ip_conntrack_help {
-	/* insert conntrack helper private data (master) here */
-	struct ip_ct_h323_master ct_h323_info;
-	struct ip_ct_pptp_master ct_pptp_info;
-	struct ip_ct_ftp_master ct_ftp_info;
-	struct ip_ct_irc_master ct_irc_info;
-};
-
-#ifdef CONFIG_IP_NF_NAT_NEEDED
-#include <linux/netfilter_ipv4/ip_nat.h>
-#include <linux/netfilter_ipv4/ip_nat_pptp.h>
-
-/* per conntrack: nat application helper private data */
-union ip_conntrack_nat_help {
-	/* insert nat helper private data here */
-	struct ip_nat_pptp nat_pptp_info;
-};
-#endif
-
-#include <linux/types.h>
-#include <linux/skbuff.h>
-
-#ifdef CONFIG_NETFILTER_DEBUG
-#define IP_NF_ASSERT(x)							\
-do {									\
-	if (!(x))							\
-		/* Wooah!  I'm tripping my conntrack in a frenzy of	\
-		   netplay... */					\
-		printk("NF_IP_ASSERT: %s:%i(%s)\n",			\
-		       __FILE__, __LINE__, __FUNCTION__);		\
-} while(0)
-#else
-#define IP_NF_ASSERT(x)
-#endif
-
-struct ip_conntrack_helper;
-
-struct ip_conntrack
-{
-	/* Usage count in here is 1 for hash table/destruct timer, 1 per skb,
-           plus 1 for any connection(s) we are `master' for */
-	struct nf_conntrack ct_general;
-
-	/* Have we seen traffic both ways yet? (bitset) */
-	unsigned long status;
-
-	/* Timer function; drops refcnt when it goes off. */
-	struct timer_list timeout;
-
-#ifdef CONFIG_IP_NF_CT_ACCT
-	/* Accounting Information (same cache line as other written members) */
-	struct ip_conntrack_counter counters[IP_CT_DIR_MAX];
-#endif
-	/* If we were expected by an expectation, this will be it */
-	struct ip_conntrack *master;
-
-	/* Current number of expected connections */
-	unsigned int expecting;
-
-	/* Unique ID that identifies this conntrack*/
-	unsigned int id;
-
-	/* Helper, if any. */
-	struct ip_conntrack_helper *helper;
-
-	/* Storage reserved for other modules: */
-	union ip_conntrack_proto proto;
-
-	union ip_conntrack_help help;
-
-#ifdef CONFIG_IP_NF_NAT_NEEDED
-	struct {
-		struct ip_nat_info info;
-		union ip_conntrack_nat_help help;
-#if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \
-	defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE)
-		int masq_index;
-#endif
-	} nat;
-#endif /* CONFIG_IP_NF_NAT_NEEDED */
-
-#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
-	u_int32_t mark;
-#endif
-
-#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK
-	u_int32_t secmark;
-#endif
-
-	/* Traversed often, so hopefully in different cacheline to top */
-	/* These are my tuples; original and reply */
-	struct ip_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX];
-};
-
-struct ip_conntrack_expect
-{
-	/* Internal linked list (global expectation list) */
-	struct list_head list;
-
-	/* We expect this tuple, with the following mask */
-	struct ip_conntrack_tuple tuple, mask;
- 
-	/* Function to call after setup and insertion */
-	void (*expectfn)(struct ip_conntrack *new,
-			 struct ip_conntrack_expect *this);
-
-	/* The conntrack of the master connection */
-	struct ip_conntrack *master;
-
-	/* Timer function; deletes the expectation. */
-	struct timer_list timeout;
-
-	/* Usage count. */
-	atomic_t use;
-
-	/* Unique ID */
-	unsigned int id;
-
-	/* Flags */
-	unsigned int flags;
-
-#ifdef CONFIG_IP_NF_NAT_NEEDED
-	__be32 saved_ip;
-	/* This is the original per-proto part, used to map the
-	 * expected connection the way the recipient expects. */
-	union ip_conntrack_manip_proto saved_proto;
-	/* Direction relative to the master connection. */
-	enum ip_conntrack_dir dir;
-#endif
-};
-
-#define IP_CT_EXPECT_PERMANENT	0x1
-
-static inline struct ip_conntrack *
-tuplehash_to_ctrack(const struct ip_conntrack_tuple_hash *hash)
-{
-	return container_of(hash, struct ip_conntrack,
-			    tuplehash[hash->tuple.dst.dir]);
-}
-
-/* get master conntrack via master expectation */
-#define master_ct(conntr) (conntr->master)
-
-/* Alter reply tuple (maybe alter helper). */
-extern void
-ip_conntrack_alter_reply(struct ip_conntrack *conntrack,
-			 const struct ip_conntrack_tuple *newreply);
-
-/* Is this tuple taken? (ignoring any belonging to the given
-   conntrack). */
-extern int
-ip_conntrack_tuple_taken(const struct ip_conntrack_tuple *tuple,
-			 const struct ip_conntrack *ignored_conntrack);
-
-/* Return conntrack_info and tuple hash for given skb. */
-static inline struct ip_conntrack *
-ip_conntrack_get(const struct sk_buff *skb, enum ip_conntrack_info *ctinfo)
-{
-	*ctinfo = skb->nfctinfo;
-	return (struct ip_conntrack *)skb->nfct;
-}
-
-/* decrement reference count on a conntrack */
-static inline void
-ip_conntrack_put(struct ip_conntrack *ct)
-{
-	IP_NF_ASSERT(ct);
-	nf_conntrack_put(&ct->ct_general);
-}
-
-extern int invert_tuplepr(struct ip_conntrack_tuple *inverse,
-			  const struct ip_conntrack_tuple *orig);
-
-extern void __ip_ct_refresh_acct(struct ip_conntrack *ct,
-			         enum ip_conntrack_info ctinfo,
-			         const struct sk_buff *skb,
-			         unsigned long extra_jiffies,
-				 int do_acct);
-
-/* Refresh conntrack for this many jiffies and do accounting */
-static inline void ip_ct_refresh_acct(struct ip_conntrack *ct, 
-				      enum ip_conntrack_info ctinfo,
-				      const struct sk_buff *skb,
-				      unsigned long extra_jiffies)
-{
-	__ip_ct_refresh_acct(ct, ctinfo, skb, extra_jiffies, 1);
-}
-
-/* Refresh conntrack for this many jiffies */
-static inline void ip_ct_refresh(struct ip_conntrack *ct,
-				 const struct sk_buff *skb,
-				 unsigned long extra_jiffies)
-{
-	__ip_ct_refresh_acct(ct, 0, skb, extra_jiffies, 0);
-}
-
-/* These are for NAT.  Icky. */
-/* Update TCP window tracking data when NAT mangles the packet */
-extern void ip_conntrack_tcp_update(struct sk_buff *skb,
-				    struct ip_conntrack *conntrack,
-				    enum ip_conntrack_dir dir);
-
-/* Call me when a conntrack is destroyed. */
-extern void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack);
-
-/* Fake conntrack entry for untracked connections */
-extern struct ip_conntrack ip_conntrack_untracked;
-
-/* Returns new sk_buff, or NULL */
-struct sk_buff *
-ip_ct_gather_frags(struct sk_buff *skb, u_int32_t user);
-
-/* Iterate over all conntracks: if iter returns true, it's deleted. */
-extern void
-ip_ct_iterate_cleanup(int (*iter)(struct ip_conntrack *i, void *data),
-		      void *data);
-
-extern struct ip_conntrack_helper *
-__ip_conntrack_helper_find_byname(const char *);
-extern struct ip_conntrack_helper *
-ip_conntrack_helper_find_get(const struct ip_conntrack_tuple *tuple);
-extern void ip_conntrack_helper_put(struct ip_conntrack_helper *helper);
-
-extern struct ip_conntrack_protocol *
-__ip_conntrack_proto_find(u_int8_t protocol);
-extern struct ip_conntrack_protocol *
-ip_conntrack_proto_find_get(u_int8_t protocol);
-extern void ip_conntrack_proto_put(struct ip_conntrack_protocol *proto);
-
-extern void ip_ct_remove_expectations(struct ip_conntrack *ct);
-
-extern struct ip_conntrack *ip_conntrack_alloc(struct ip_conntrack_tuple *,
-					       struct ip_conntrack_tuple *);
-
-extern void ip_conntrack_free(struct ip_conntrack *ct);
-
-extern void ip_conntrack_hash_insert(struct ip_conntrack *ct);
-
-extern struct ip_conntrack_expect *
-__ip_conntrack_expect_find(const struct ip_conntrack_tuple *tuple);
-
-extern struct ip_conntrack_expect *
-ip_conntrack_expect_find_get(const struct ip_conntrack_tuple *tuple);
-
-extern struct ip_conntrack_tuple_hash *
-__ip_conntrack_find(const struct ip_conntrack_tuple *tuple,
-                    const struct ip_conntrack *ignored_conntrack);
-
-extern void ip_conntrack_flush(void);
-
-/* It's confirmed if it is, or has been in the hash table. */
-static inline int is_confirmed(struct ip_conntrack *ct)
-{
-	return test_bit(IPS_CONFIRMED_BIT, &ct->status);
-}
-
-static inline int is_dying(struct ip_conntrack *ct)
-{
-	return test_bit(IPS_DYING_BIT, &ct->status);
-}
-
-extern unsigned int ip_conntrack_htable_size;
-extern int ip_conntrack_checksum;
- 
-#define CONNTRACK_STAT_INC(count) (__get_cpu_var(ip_conntrack_stat).count++)
-#define CONNTRACK_STAT_INC_ATOMIC(count)		\
-do {							\
-	local_bh_disable();				\
-	__get_cpu_var(ip_conntrack_stat).count++;	\
-	local_bh_enable();				\
-} while (0)
-
-#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS
-#include <linux/notifier.h>
-#include <linux/interrupt.h>
-
-struct ip_conntrack_ecache {
-	struct ip_conntrack *ct;
-	unsigned int events;
-};
-DECLARE_PER_CPU(struct ip_conntrack_ecache, ip_conntrack_ecache);
-
-#define CONNTRACK_ECACHE(x)	(__get_cpu_var(ip_conntrack_ecache).x)
- 
-extern struct atomic_notifier_head ip_conntrack_chain;
-extern struct atomic_notifier_head ip_conntrack_expect_chain;
-
-static inline int ip_conntrack_register_notifier(struct notifier_block *nb)
-{
-	return atomic_notifier_chain_register(&ip_conntrack_chain, nb);
-}
-
-static inline int ip_conntrack_unregister_notifier(struct notifier_block *nb)
-{
-	return atomic_notifier_chain_unregister(&ip_conntrack_chain, nb);
-}
-
-static inline int 
-ip_conntrack_expect_register_notifier(struct notifier_block *nb)
-{
-	return atomic_notifier_chain_register(&ip_conntrack_expect_chain, nb);
-}
-
-static inline int
-ip_conntrack_expect_unregister_notifier(struct notifier_block *nb)
-{
-	return atomic_notifier_chain_unregister(&ip_conntrack_expect_chain,
-			nb);
-}
-
-extern void ip_ct_deliver_cached_events(const struct ip_conntrack *ct);
-extern void __ip_ct_event_cache_init(struct ip_conntrack *ct);
-
-static inline void 
-ip_conntrack_event_cache(enum ip_conntrack_events event,
-			 const struct sk_buff *skb)
-{
-	struct ip_conntrack *ct = (struct ip_conntrack *)skb->nfct;
-	struct ip_conntrack_ecache *ecache;
-	
-	local_bh_disable();
-	ecache = &__get_cpu_var(ip_conntrack_ecache);
-	if (ct != ecache->ct)
-		__ip_ct_event_cache_init(ct);
-	ecache->events |= event;
-	local_bh_enable();
-}
-
-static inline void ip_conntrack_event(enum ip_conntrack_events event,
-				      struct ip_conntrack *ct)
-{
-	if (is_confirmed(ct) && !is_dying(ct))
-		atomic_notifier_call_chain(&ip_conntrack_chain, event, ct);
-}
-
-static inline void 
-ip_conntrack_expect_event(enum ip_conntrack_expect_events event,
-			  struct ip_conntrack_expect *exp)
-{
-	atomic_notifier_call_chain(&ip_conntrack_expect_chain, event, exp);
-}
-#else /* CONFIG_IP_NF_CONNTRACK_EVENTS */
-static inline void ip_conntrack_event_cache(enum ip_conntrack_events event, 
-					    const struct sk_buff *skb) {}
-static inline void ip_conntrack_event(enum ip_conntrack_events event, 
-				      struct ip_conntrack *ct) {}
-static inline void ip_ct_deliver_cached_events(const struct ip_conntrack *ct) {}
-static inline void 
-ip_conntrack_expect_event(enum ip_conntrack_expect_events event, 
-			  struct ip_conntrack_expect *exp) {}
-#endif /* CONFIG_IP_NF_CONNTRACK_EVENTS */
-
-#ifdef CONFIG_IP_NF_NAT_NEEDED
-static inline int ip_nat_initialized(struct ip_conntrack *conntrack,
-				     enum ip_nat_manip_type manip)
-{
-	if (manip == IP_NAT_MANIP_SRC)
-		return test_bit(IPS_SRC_NAT_DONE_BIT, &conntrack->status);
-	return test_bit(IPS_DST_NAT_DONE_BIT, &conntrack->status);
-}
-#endif /* CONFIG_IP_NF_NAT_NEEDED */
-
-#endif /* __KERNEL__ */
-#endif /* _IP_CONNTRACK_H */
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_amanda.h b/include/linux/netfilter_ipv4/ip_conntrack_amanda.h
deleted file mode 100644
index de3e41f..0000000
--- a/include/linux/netfilter_ipv4/ip_conntrack_amanda.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef _IP_CONNTRACK_AMANDA_H
-#define _IP_CONNTRACK_AMANDA_H
-/* AMANDA tracking. */
-
-struct ip_conntrack_expect;
-extern unsigned int (*ip_nat_amanda_hook)(struct sk_buff **pskb,
-					  enum ip_conntrack_info ctinfo,
-					  unsigned int matchoff,
-					  unsigned int matchlen,
-					  struct ip_conntrack_expect *exp);
-#endif /* _IP_CONNTRACK_AMANDA_H */
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_core.h b/include/linux/netfilter_ipv4/ip_conntrack_core.h
deleted file mode 100644
index e3a6df0..0000000
--- a/include/linux/netfilter_ipv4/ip_conntrack_core.h
+++ /dev/null
@@ -1,61 +0,0 @@
-#ifndef _IP_CONNTRACK_CORE_H
-#define _IP_CONNTRACK_CORE_H
-#include <linux/netfilter.h>
-
-#define MAX_IP_CT_PROTO 256
-extern struct ip_conntrack_protocol *ip_ct_protos[MAX_IP_CT_PROTO];
-
-/* This header is used to share core functionality between the
-   standalone connection tracking module, and the compatibility layer's use
-   of connection tracking. */
-extern unsigned int ip_conntrack_in(unsigned int hooknum,
-				    struct sk_buff **pskb,
-				    const struct net_device *in,
-				    const struct net_device *out,
-				    int (*okfn)(struct sk_buff *));
-
-extern int ip_conntrack_init(void);
-extern void ip_conntrack_cleanup(void);
-
-struct ip_conntrack_protocol;
-
-extern int
-ip_ct_get_tuple(const struct iphdr *iph,
-		const struct sk_buff *skb,
-		unsigned int dataoff,
-		struct ip_conntrack_tuple *tuple,
-		const struct ip_conntrack_protocol *protocol);
-
-extern int
-ip_ct_invert_tuple(struct ip_conntrack_tuple *inverse,
-		   const struct ip_conntrack_tuple *orig,
-		   const struct ip_conntrack_protocol *protocol);
-
-/* Find a connection corresponding to a tuple. */
-struct ip_conntrack_tuple_hash *
-ip_conntrack_find_get(const struct ip_conntrack_tuple *tuple,
-		      const struct ip_conntrack *ignored_conntrack);
-
-extern int __ip_conntrack_confirm(struct sk_buff **pskb);
-
-/* Confirm a connection: returns NF_DROP if packet must be dropped. */
-static inline int ip_conntrack_confirm(struct sk_buff **pskb)
-{
-	struct ip_conntrack *ct = (struct ip_conntrack *)(*pskb)->nfct;
-	int ret = NF_ACCEPT;
-
-	if (ct) {
-		if (!is_confirmed(ct) && !is_dying(ct))
-			ret = __ip_conntrack_confirm(pskb);
-		ip_ct_deliver_cached_events(ct);
-	}
-	return ret;
-}
-
-extern void ip_ct_unlink_expect(struct ip_conntrack_expect *exp);
-
-extern struct list_head *ip_conntrack_hash;
-extern struct list_head ip_conntrack_expect_list;
-extern rwlock_t ip_conntrack_lock;
-#endif /* _IP_CONNTRACK_CORE_H */
-
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_ftp.h b/include/linux/netfilter_ipv4/ip_conntrack_ftp.h
deleted file mode 100644
index 2129fc3..0000000
--- a/include/linux/netfilter_ipv4/ip_conntrack_ftp.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef _IP_CONNTRACK_FTP_H
-#define _IP_CONNTRACK_FTP_H
-/* FTP tracking. */
-
-/* This enum is exposed to userspace */
-enum ip_ct_ftp_type
-{
-	/* PORT command from client */
-	IP_CT_FTP_PORT,
-	/* PASV response from server */
-	IP_CT_FTP_PASV,
-	/* EPRT command from client */
-	IP_CT_FTP_EPRT,
-	/* EPSV response from server */
-	IP_CT_FTP_EPSV,
-};
-
-#ifdef __KERNEL__
-
-#define FTP_PORT	21
-
-#define NUM_SEQ_TO_REMEMBER 2
-/* This structure exists only once per master */
-struct ip_ct_ftp_master {
-	/* Valid seq positions for cmd matching after newline */
-	u_int32_t seq_aft_nl[IP_CT_DIR_MAX][NUM_SEQ_TO_REMEMBER];
-	/* 0 means seq_match_aft_nl not set */
-	int seq_aft_nl_num[IP_CT_DIR_MAX];
-};
-
-struct ip_conntrack_expect;
-
-/* For NAT to hook in when we find a packet which describes what other
- * connection we should expect. */
-extern unsigned int (*ip_nat_ftp_hook)(struct sk_buff **pskb,
-				       enum ip_conntrack_info ctinfo,
-				       enum ip_ct_ftp_type type,
-				       unsigned int matchoff,
-				       unsigned int matchlen,
-				       struct ip_conntrack_expect *exp,
-				       u32 *seq);
-#endif /* __KERNEL__ */
-
-#endif /* _IP_CONNTRACK_FTP_H */
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_h323.h b/include/linux/netfilter_ipv4/ip_conntrack_h323.h
deleted file mode 100644
index 18f7698..0000000
--- a/include/linux/netfilter_ipv4/ip_conntrack_h323.h
+++ /dev/null
@@ -1,89 +0,0 @@
-#ifndef _IP_CONNTRACK_H323_H
-#define _IP_CONNTRACK_H323_H
-
-#ifdef __KERNEL__
-
-#include <linux/netfilter/nf_conntrack_h323_asn1.h>
-
-#define RAS_PORT 1719
-#define Q931_PORT 1720
-#define H323_RTP_CHANNEL_MAX 4	/* Audio, video, FAX and other */
-
-/* This structure exists only once per master */
-struct ip_ct_h323_master {
-
-	/* Original and NATed Q.931 or H.245 signal ports */
-	u_int16_t sig_port[IP_CT_DIR_MAX];
-
-	/* Original and NATed RTP ports */
-	u_int16_t rtp_port[H323_RTP_CHANNEL_MAX][IP_CT_DIR_MAX];
-
-	union {
-		/* RAS connection timeout */
-		u_int32_t timeout;
-
-		/* Next TPKT length (for separate TPKT header and data) */
-		u_int16_t tpkt_len[IP_CT_DIR_MAX];
-	};
-};
-
-struct ip_conntrack_expect;
-
-extern int get_h225_addr(unsigned char *data, TransportAddress * addr,
-			 __be32 * ip, u_int16_t * port);
-extern void ip_conntrack_h245_expect(struct ip_conntrack *new,
-				     struct ip_conntrack_expect *this);
-extern void ip_conntrack_q931_expect(struct ip_conntrack *new,
-				     struct ip_conntrack_expect *this);
-extern int (*set_h245_addr_hook) (struct sk_buff ** pskb,
-				  unsigned char **data, int dataoff,
-				  H245_TransportAddress * addr,
-				  __be32 ip, u_int16_t port);
-extern int (*set_h225_addr_hook) (struct sk_buff ** pskb,
-				  unsigned char **data, int dataoff,
-				  TransportAddress * addr,
-				  __be32 ip, u_int16_t port);
-extern int (*set_sig_addr_hook) (struct sk_buff ** pskb,
-				 struct ip_conntrack * ct,
-				 enum ip_conntrack_info ctinfo,
-				 unsigned char **data,
-				 TransportAddress * addr, int count);
-extern int (*set_ras_addr_hook) (struct sk_buff ** pskb,
-				 struct ip_conntrack * ct,
-				 enum ip_conntrack_info ctinfo,
-				 unsigned char **data,
-				 TransportAddress * addr, int count);
-extern int (*nat_rtp_rtcp_hook) (struct sk_buff ** pskb,
-				 struct ip_conntrack * ct,
-				 enum ip_conntrack_info ctinfo,
-				 unsigned char **data, int dataoff,
-				 H245_TransportAddress * addr,
-				 u_int16_t port, u_int16_t rtp_port,
-				 struct ip_conntrack_expect * rtp_exp,
-				 struct ip_conntrack_expect * rtcp_exp);
-extern int (*nat_t120_hook) (struct sk_buff ** pskb, struct ip_conntrack * ct,
-			     enum ip_conntrack_info ctinfo,
-			     unsigned char **data, int dataoff,
-			     H245_TransportAddress * addr, u_int16_t port,
-			     struct ip_conntrack_expect * exp);
-extern int (*nat_h245_hook) (struct sk_buff ** pskb, struct ip_conntrack * ct,
-			     enum ip_conntrack_info ctinfo,
-			     unsigned char **data, int dataoff,
-			     TransportAddress * addr, u_int16_t port,
-			     struct ip_conntrack_expect * exp);
-extern int (*nat_callforwarding_hook) (struct sk_buff ** pskb,
-				       struct ip_conntrack * ct,
-				       enum ip_conntrack_info ctinfo,
-				       unsigned char **data, int dataoff,
-				       TransportAddress * addr,
-				       u_int16_t port,
-				       struct ip_conntrack_expect * exp);
-extern int (*nat_q931_hook) (struct sk_buff ** pskb, struct ip_conntrack * ct,
-			     enum ip_conntrack_info ctinfo,
-			     unsigned char **data, TransportAddress * addr,
-			     int idx, u_int16_t port,
-			     struct ip_conntrack_expect * exp);
-
-#endif
-
-#endif
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_helper.h b/include/linux/netfilter_ipv4/ip_conntrack_helper.h
deleted file mode 100644
index 77fe868..0000000
--- a/include/linux/netfilter_ipv4/ip_conntrack_helper.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* IP connection tracking helpers. */
-#ifndef _IP_CONNTRACK_HELPER_H
-#define _IP_CONNTRACK_HELPER_H
-#include <linux/netfilter_ipv4/ip_conntrack.h>
-
-struct module;
-
-struct ip_conntrack_helper
-{	
-	struct list_head list; 		/* Internal use. */
-
-	const char *name;		/* name of the module */
-	struct module *me;		/* pointer to self */
-	unsigned int max_expected;	/* Maximum number of concurrent 
-					 * expected connections */
-	unsigned int timeout;		/* timeout for expecteds */
-
-	/* Mask of things we will help (compared against server response) */
-	struct ip_conntrack_tuple tuple;
-	struct ip_conntrack_tuple mask;
-	
-	/* Function to call when data passes; return verdict, or -1 to
-           invalidate. */
-	int (*help)(struct sk_buff **pskb,
-		    struct ip_conntrack *ct,
-		    enum ip_conntrack_info conntrackinfo);
-
-	void (*destroy)(struct ip_conntrack *ct);
-
-	int (*to_nfattr)(struct sk_buff *skb, const struct ip_conntrack *ct);
-};
-
-extern int ip_conntrack_helper_register(struct ip_conntrack_helper *);
-extern void ip_conntrack_helper_unregister(struct ip_conntrack_helper *);
-
-/* Allocate space for an expectation: this is mandatory before calling 
-   ip_conntrack_expect_related.  You will have to call put afterwards. */
-extern struct ip_conntrack_expect *
-ip_conntrack_expect_alloc(struct ip_conntrack *master);
-extern void ip_conntrack_expect_put(struct ip_conntrack_expect *exp);
-
-/* Add an expected connection: can have more than one per connection */
-extern int ip_conntrack_expect_related(struct ip_conntrack_expect *exp);
-extern void ip_conntrack_unexpect_related(struct ip_conntrack_expect *exp);
-
-#endif /*_IP_CONNTRACK_HELPER_H*/
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_icmp.h b/include/linux/netfilter_ipv4/ip_conntrack_icmp.h
deleted file mode 100644
index eed5ee3..0000000
--- a/include/linux/netfilter_ipv4/ip_conntrack_icmp.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _IP_CONNTRACK_ICMP_H
-#define _IP_CONNTRACK_ICMP_H
-
-#include <net/netfilter/ipv4/nf_conntrack_icmp.h>
-
-#endif /* _IP_CONNTRACK_ICMP_H */
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_irc.h b/include/linux/netfilter_ipv4/ip_conntrack_irc.h
deleted file mode 100644
index 16601e0..0000000
--- a/include/linux/netfilter_ipv4/ip_conntrack_irc.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* IRC extension for IP connection tracking.
- * (C) 2000 by Harald Welte <laforge@gnumonks.org>
- * based on RR's ip_conntrack_ftp.h
- *
- * ip_conntrack_irc.h,v 1.6 2000/11/07 18:26:42 laforge Exp
- *
- *      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.
- *
- *
- */
-#ifndef _IP_CONNTRACK_IRC_H
-#define _IP_CONNTRACK_IRC_H
-
-/* This structure exists only once per master */
-struct ip_ct_irc_master {
-};
-
-#ifdef __KERNEL__
-extern unsigned int (*ip_nat_irc_hook)(struct sk_buff **pskb,
-				       enum ip_conntrack_info ctinfo,
-				       unsigned int matchoff,
-				       unsigned int matchlen,
-				       struct ip_conntrack_expect *exp);
-
-#define IRC_PORT	6667
-
-#endif /* __KERNEL__ */
-
-#endif /* _IP_CONNTRACK_IRC_H */
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_pptp.h b/include/linux/netfilter_ipv4/ip_conntrack_pptp.h
deleted file mode 100644
index 2644b1f..0000000
--- a/include/linux/netfilter_ipv4/ip_conntrack_pptp.h
+++ /dev/null
@@ -1,326 +0,0 @@
-/* PPTP constants and structs */
-#ifndef _CONNTRACK_PPTP_H
-#define _CONNTRACK_PPTP_H
-
-/* state of the control session */
-enum pptp_ctrlsess_state {
-	PPTP_SESSION_NONE,			/* no session present */
-	PPTP_SESSION_ERROR,			/* some session error */
-	PPTP_SESSION_STOPREQ,			/* stop_sess request seen */
-	PPTP_SESSION_REQUESTED,			/* start_sess request seen */
-	PPTP_SESSION_CONFIRMED,			/* session established */
-};
-
-/* state of the call inside the control session */
-enum pptp_ctrlcall_state {
-	PPTP_CALL_NONE,
-	PPTP_CALL_ERROR,
-	PPTP_CALL_OUT_REQ,
-	PPTP_CALL_OUT_CONF,
-	PPTP_CALL_IN_REQ,
-	PPTP_CALL_IN_REP,
-	PPTP_CALL_IN_CONF,
-	PPTP_CALL_CLEAR_REQ,
-};
-
-
-/* conntrack private data */
-struct ip_ct_pptp_master {
-	enum pptp_ctrlsess_state sstate;	/* session state */
-
-	/* everything below is going to be per-expectation in newnat,
-	 * since there could be more than one call within one session */
-	enum pptp_ctrlcall_state cstate;	/* call state */
-	__be16 pac_call_id;			/* call id of PAC, host byte order */
-	__be16 pns_call_id;			/* call id of PNS, host byte order */
-
-	/* in pre-2.6.11 this used to be per-expect. Now it is per-conntrack
-	 * and therefore imposes a fixed limit on the number of maps */
-	struct ip_ct_gre_keymap *keymap_orig, *keymap_reply;
-};
-
-/* conntrack_expect private member */
-struct ip_ct_pptp_expect {
-	enum pptp_ctrlcall_state cstate; 	/* call state */
-	__be16 pac_call_id;			/* call id of PAC */
-	__be16 pns_call_id;			/* call id of PNS */
-};
-
-
-#ifdef __KERNEL__
-
-#define IP_CONNTR_PPTP		PPTP_CONTROL_PORT
-
-#define PPTP_CONTROL_PORT	1723
-
-#define PPTP_PACKET_CONTROL	1
-#define PPTP_PACKET_MGMT	2
-
-#define PPTP_MAGIC_COOKIE	0x1a2b3c4d
-
-struct pptp_pkt_hdr {
-	__u16	packetLength;
-	__be16	packetType;
-	__be32	magicCookie;
-};
-
-/* PptpControlMessageType values */
-#define PPTP_START_SESSION_REQUEST	1
-#define PPTP_START_SESSION_REPLY	2
-#define PPTP_STOP_SESSION_REQUEST	3
-#define PPTP_STOP_SESSION_REPLY		4
-#define PPTP_ECHO_REQUEST		5
-#define PPTP_ECHO_REPLY			6
-#define PPTP_OUT_CALL_REQUEST		7
-#define PPTP_OUT_CALL_REPLY		8
-#define PPTP_IN_CALL_REQUEST		9
-#define PPTP_IN_CALL_REPLY		10
-#define PPTP_IN_CALL_CONNECT		11
-#define PPTP_CALL_CLEAR_REQUEST		12
-#define PPTP_CALL_DISCONNECT_NOTIFY	13
-#define PPTP_WAN_ERROR_NOTIFY		14
-#define PPTP_SET_LINK_INFO		15
-
-#define PPTP_MSG_MAX			15
-
-/* PptpGeneralError values */
-#define PPTP_ERROR_CODE_NONE		0
-#define PPTP_NOT_CONNECTED		1
-#define PPTP_BAD_FORMAT			2
-#define PPTP_BAD_VALUE			3
-#define PPTP_NO_RESOURCE		4
-#define PPTP_BAD_CALLID			5
-#define PPTP_REMOVE_DEVICE_ERROR	6
-
-struct PptpControlHeader {
-	__be16	messageType;
-	__u16	reserved;
-};
-
-/* FramingCapability Bitmap Values */
-#define PPTP_FRAME_CAP_ASYNC		0x1
-#define PPTP_FRAME_CAP_SYNC		0x2
-
-/* BearerCapability Bitmap Values */
-#define PPTP_BEARER_CAP_ANALOG		0x1
-#define PPTP_BEARER_CAP_DIGITAL		0x2
-
-struct PptpStartSessionRequest {
-	__be16	protocolVersion;
-	__u16	reserved1;
-	__be32	framingCapability;
-	__be32	bearerCapability;
-	__be16	maxChannels;
-	__be16	firmwareRevision;
-	__u8	hostName[64];
-	__u8	vendorString[64];
-};
-
-/* PptpStartSessionResultCode Values */
-#define PPTP_START_OK			1
-#define PPTP_START_GENERAL_ERROR	2
-#define PPTP_START_ALREADY_CONNECTED	3
-#define PPTP_START_NOT_AUTHORIZED	4
-#define PPTP_START_UNKNOWN_PROTOCOL	5
-
-struct PptpStartSessionReply {
-	__be16	protocolVersion;
-	__u8	resultCode;
-	__u8	generalErrorCode;
-	__be32	framingCapability;
-	__be32	bearerCapability;
-	__be16	maxChannels;
-	__be16	firmwareRevision;
-	__u8	hostName[64];
-	__u8	vendorString[64];
-};
-
-/* PptpStopReasons */
-#define PPTP_STOP_NONE			1
-#define PPTP_STOP_PROTOCOL		2
-#define PPTP_STOP_LOCAL_SHUTDOWN	3
-
-struct PptpStopSessionRequest {
-	__u8	reason;
-	__u8	reserved1;
-	__u16	reserved2;
-};
-
-/* PptpStopSessionResultCode */
-#define PPTP_STOP_OK			1
-#define PPTP_STOP_GENERAL_ERROR		2
-
-struct PptpStopSessionReply {
-	__u8	resultCode;
-	__u8	generalErrorCode;
-	__u16	reserved1;
-};
-
-struct PptpEchoRequest {
-	__be32 identNumber;
-};
-
-/* PptpEchoReplyResultCode */
-#define PPTP_ECHO_OK			1
-#define PPTP_ECHO_GENERAL_ERROR		2
-
-struct PptpEchoReply {
-	__be32	identNumber;
-	__u8	resultCode;
-	__u8	generalErrorCode;
-	__u16	reserved;
-};
-
-/* PptpFramingType */
-#define PPTP_ASYNC_FRAMING		1
-#define PPTP_SYNC_FRAMING		2
-#define PPTP_DONT_CARE_FRAMING		3
-
-/* PptpCallBearerType */
-#define PPTP_ANALOG_TYPE		1
-#define PPTP_DIGITAL_TYPE		2
-#define PPTP_DONT_CARE_BEARER_TYPE	3
-
-struct PptpOutCallRequest {
-	__be16	callID;
-	__be16	callSerialNumber;
-	__be32	minBPS;
-	__be32	maxBPS;
-	__be32	bearerType;
-	__be32	framingType;
-	__be16	packetWindow;
-	__be16	packetProcDelay;
-	__be16	phoneNumberLength;
-	__u16	reserved1;
-	__u8	phoneNumber[64];
-	__u8	subAddress[64];
-};
-
-/* PptpCallResultCode */
-#define PPTP_OUTCALL_CONNECT		1
-#define PPTP_OUTCALL_GENERAL_ERROR	2
-#define PPTP_OUTCALL_NO_CARRIER		3
-#define PPTP_OUTCALL_BUSY		4
-#define PPTP_OUTCALL_NO_DIAL_TONE	5
-#define PPTP_OUTCALL_TIMEOUT		6
-#define PPTP_OUTCALL_DONT_ACCEPT	7
-
-struct PptpOutCallReply {
-	__be16	callID;
-	__be16	peersCallID;
-	__u8	resultCode;
-	__u8	generalErrorCode;
-	__be16	causeCode;
-	__be32	connectSpeed;
-	__be16	packetWindow;
-	__be16	packetProcDelay;
-	__be32	physChannelID;
-};
-
-struct PptpInCallRequest {
-	__be16	callID;
-	__be16	callSerialNumber;
-	__be32	callBearerType;
-	__be32	physChannelID;
-	__be16	dialedNumberLength;
-	__be16	dialingNumberLength;
-	__u8	dialedNumber[64];
-	__u8	dialingNumber[64];
-	__u8	subAddress[64];
-};
-
-/* PptpInCallResultCode */
-#define PPTP_INCALL_ACCEPT		1
-#define PPTP_INCALL_GENERAL_ERROR	2
-#define PPTP_INCALL_DONT_ACCEPT		3
-
-struct PptpInCallReply {
-	__be16	callID;
-	__be16	peersCallID;
-	__u8	resultCode;
-	__u8	generalErrorCode;
-	__be16	packetWindow;
-	__be16	packetProcDelay;
-	__u16	reserved;
-};
-
-struct PptpInCallConnected {
-	__be16	peersCallID;
-	__u16	reserved;
-	__be32	connectSpeed;
-	__be16	packetWindow;
-	__be16	packetProcDelay;
-	__be32	callFramingType;
-};
-
-struct PptpClearCallRequest {
-	__be16	callID;
-	__u16	reserved;
-};
-
-struct PptpCallDisconnectNotify {
-	__be16	callID;
-	__u8	resultCode;
-	__u8	generalErrorCode;
-	__be16	causeCode;
-	__u16	reserved;
-	__u8	callStatistics[128];
-};
-
-struct PptpWanErrorNotify {
-	__be16	peersCallID;
-	__u16	reserved;
-	__be32	crcErrors;
-	__be32	framingErrors;
-	__be32	hardwareOverRuns;
-	__be32	bufferOverRuns;
-	__be32	timeoutErrors;
-	__be32	alignmentErrors;
-};
-
-struct PptpSetLinkInfo {
-	__be16	peersCallID;
-	__u16	reserved;
-	__be32	sendAccm;
-	__be32	recvAccm;
-};
-
-union pptp_ctrl_union {
-	struct PptpStartSessionRequest	sreq;
-	struct PptpStartSessionReply	srep;
-	struct PptpStopSessionRequest	streq;
-	struct PptpStopSessionReply	strep;
-	struct PptpOutCallRequest	ocreq;
-	struct PptpOutCallReply		ocack;
-	struct PptpInCallRequest	icreq;
-	struct PptpInCallReply		icack;
-	struct PptpInCallConnected	iccon;
-	struct PptpClearCallRequest	clrreq;
-	struct PptpCallDisconnectNotify disc;
-	struct PptpWanErrorNotify	wanerr;
-	struct PptpSetLinkInfo		setlink;
-};
-
-extern int
-(*ip_nat_pptp_hook_outbound)(struct sk_buff **pskb,
-			  struct ip_conntrack *ct,
-			  enum ip_conntrack_info ctinfo,
-			  struct PptpControlHeader *ctlh,
-			  union pptp_ctrl_union *pptpReq);
-
-extern int
-(*ip_nat_pptp_hook_inbound)(struct sk_buff **pskb,
-			  struct ip_conntrack *ct,
-			  enum ip_conntrack_info ctinfo,
-			  struct PptpControlHeader *ctlh,
-			  union pptp_ctrl_union *pptpReq);
-
-extern void
-(*ip_nat_pptp_hook_exp_gre)(struct ip_conntrack_expect *exp_orig,
-			    struct ip_conntrack_expect *exp_reply);
-
-extern void
-(*ip_nat_pptp_hook_expectfn)(struct ip_conntrack *ct,
-			     struct ip_conntrack_expect *exp);
-#endif /* __KERNEL__ */
-#endif /* _CONNTRACK_PPTP_H */
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h b/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h
deleted file mode 100644
index e371e0f..0000000
--- a/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h
+++ /dev/null
@@ -1,114 +0,0 @@
-#ifndef _CONNTRACK_PROTO_GRE_H
-#define _CONNTRACK_PROTO_GRE_H
-#include <asm/byteorder.h>
-
-/* GRE PROTOCOL HEADER */
-
-/* GRE Version field */
-#define GRE_VERSION_1701	0x0
-#define GRE_VERSION_PPTP	0x1
-
-/* GRE Protocol field */
-#define GRE_PROTOCOL_PPTP	0x880B
-
-/* GRE Flags */
-#define GRE_FLAG_C		0x80
-#define GRE_FLAG_R		0x40
-#define GRE_FLAG_K		0x20
-#define GRE_FLAG_S		0x10
-#define GRE_FLAG_A		0x80
-
-#define GRE_IS_C(f)	((f)&GRE_FLAG_C)
-#define GRE_IS_R(f)	((f)&GRE_FLAG_R)
-#define GRE_IS_K(f)	((f)&GRE_FLAG_K)
-#define GRE_IS_S(f)	((f)&GRE_FLAG_S)
-#define GRE_IS_A(f)	((f)&GRE_FLAG_A)
-
-/* GRE is a mess: Four different standards */
-struct gre_hdr {
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-	__u16	rec:3,
-		srr:1,
-		seq:1,
-		key:1,
-		routing:1,
-		csum:1,
-		version:3,
-		reserved:4,
-		ack:1;
-#elif defined(__BIG_ENDIAN_BITFIELD)
-	__u16	csum:1,
-		routing:1,
-		key:1,
-		seq:1,
-		srr:1,
-		rec:3,
-		ack:1,
-		reserved:4,
-		version:3;
-#else
-#error "Adjust your <asm/byteorder.h> defines"
-#endif
-	__be16	protocol;
-};
-
-/* modified GRE header for PPTP */
-struct gre_hdr_pptp {
-	__u8   flags;		/* bitfield */
-	__u8   version;		/* should be GRE_VERSION_PPTP */
-	__be16 protocol;	/* should be GRE_PROTOCOL_PPTP */
-	__be16 payload_len;	/* size of ppp payload, not inc. gre header */
-	__be16 call_id;		/* peer's call_id for this session */
-	__be32 seq;		/* sequence number.  Present if S==1 */
-	__be32 ack;		/* seq number of highest packet recieved by */
-				/*  sender in this session */
-};
-
-
-/* this is part of ip_conntrack */
-struct ip_ct_gre {
-	unsigned int stream_timeout;
-	unsigned int timeout;
-};
-
-#ifdef __KERNEL__
-struct ip_conntrack_expect;
-struct ip_conntrack;
-
-/* structure for original <-> reply keymap */
-struct ip_ct_gre_keymap {
-	struct list_head list;
-
-	struct ip_conntrack_tuple tuple;
-};
-
-/* add new tuple->key_reply pair to keymap */
-int ip_ct_gre_keymap_add(struct ip_conntrack *ct,
-			 struct ip_conntrack_tuple *t,
-			 int reply);
-
-/* delete keymap entries */
-void ip_ct_gre_keymap_destroy(struct ip_conntrack *ct);
-
-
-/* get pointer to gre key, if present */
-static inline __be32 *gre_key(struct gre_hdr *greh)
-{
-	if (!greh->key)
-		return NULL;
-	if (greh->csum || greh->routing)
-		return (__be32 *) (greh+sizeof(*greh)+4);
-	return (__be32 *) (greh+sizeof(*greh));
-}
-
-/* get pointer ot gre csum, if present */
-static inline __sum16 *gre_csum(struct gre_hdr *greh)
-{
-	if (!greh->csum)
-		return NULL;
-	return (__sum16 *) (greh+sizeof(*greh));
-}
-
-#endif /* __KERNEL__ */
-
-#endif /* _CONNTRACK_PROTO_GRE_H */
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_protocol.h b/include/linux/netfilter_ipv4/ip_conntrack_protocol.h
deleted file mode 100644
index 2c76b87..0000000
--- a/include/linux/netfilter_ipv4/ip_conntrack_protocol.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/* Header for use in defining a given protocol for connection tracking. */
-#ifndef _IP_CONNTRACK_PROTOCOL_H
-#define _IP_CONNTRACK_PROTOCOL_H
-#include <linux/netfilter_ipv4/ip_conntrack.h>
-#include <linux/netfilter/nfnetlink_conntrack.h>
-
-struct seq_file;
-
-struct ip_conntrack_protocol
-{
-	/* Protocol number. */
-	u_int8_t proto;
-
-	/* Protocol name */
-	const char *name;
-
-	/* Try to fill in the third arg: dataoff is offset past IP
-           hdr.  Return true if possible. */
-	int (*pkt_to_tuple)(const struct sk_buff *skb,
-			   unsigned int dataoff,
-			   struct ip_conntrack_tuple *tuple);
-
-	/* Invert the per-proto part of the tuple: ie. turn xmit into reply.
-	 * Some packets can't be inverted: return 0 in that case.
-	 */
-	int (*invert_tuple)(struct ip_conntrack_tuple *inverse,
-			    const struct ip_conntrack_tuple *orig);
-
-	/* Print out the per-protocol part of the tuple. Return like seq_* */
-	int (*print_tuple)(struct seq_file *,
-			   const struct ip_conntrack_tuple *);
-
-	/* Print out the private part of the conntrack. */
-	int (*print_conntrack)(struct seq_file *, const struct ip_conntrack *);
-
-	/* Returns verdict for packet, or -1 for invalid. */
-	int (*packet)(struct ip_conntrack *conntrack,
-		      const struct sk_buff *skb,
-		      enum ip_conntrack_info ctinfo);
-
-	/* Called when a new connection for this protocol found;
-	 * returns TRUE if it's OK.  If so, packet() called next. */
-	int (*new)(struct ip_conntrack *conntrack, const struct sk_buff *skb);
-
-	/* Called when a conntrack entry is destroyed */
-	void (*destroy)(struct ip_conntrack *conntrack);
-
-	int (*error)(struct sk_buff *skb, enum ip_conntrack_info *ctinfo,
-		     unsigned int hooknum);
-
-	/* convert protoinfo to nfnetink attributes */
-	int (*to_nfattr)(struct sk_buff *skb, struct nfattr *nfa,
-			 const struct ip_conntrack *ct);
-
-	/* convert nfnetlink attributes to protoinfo */
-	int (*from_nfattr)(struct nfattr *tb[], struct ip_conntrack *ct);
-
-	int (*tuple_to_nfattr)(struct sk_buff *skb,
-			       const struct ip_conntrack_tuple *t);
-	int (*nfattr_to_tuple)(struct nfattr *tb[],
-			       struct ip_conntrack_tuple *t);
-
-	/* Module (if any) which this is connected to. */
-	struct module *me;
-};
-
-/* Protocol registration. */
-extern int ip_conntrack_protocol_register(struct ip_conntrack_protocol *proto);
-extern void ip_conntrack_protocol_unregister(struct ip_conntrack_protocol *proto);
-/* Existing built-in protocols */
-extern struct ip_conntrack_protocol ip_conntrack_protocol_tcp;
-extern struct ip_conntrack_protocol ip_conntrack_protocol_udp;
-extern struct ip_conntrack_protocol ip_conntrack_protocol_icmp;
-extern struct ip_conntrack_protocol ip_conntrack_generic_protocol;
-extern int ip_conntrack_protocol_tcp_init(void);
-
-/* Log invalid packets */
-extern unsigned int ip_ct_log_invalid;
-
-extern int ip_ct_port_tuple_to_nfattr(struct sk_buff *,
-				      const struct ip_conntrack_tuple *);
-extern int ip_ct_port_nfattr_to_tuple(struct nfattr *tb[],
-				      struct ip_conntrack_tuple *);
-
-#ifdef CONFIG_SYSCTL
-#ifdef DEBUG_INVALID_PACKETS
-#define LOG_INVALID(proto) \
-	(ip_ct_log_invalid == (proto) || ip_ct_log_invalid == IPPROTO_RAW)
-#else
-#define LOG_INVALID(proto) \
-	((ip_ct_log_invalid == (proto) || ip_ct_log_invalid == IPPROTO_RAW) \
-	 && net_ratelimit())
-#endif
-#else
-#define LOG_INVALID(proto) 0
-#endif /* CONFIG_SYSCTL */
-
-#endif /*_IP_CONNTRACK_PROTOCOL_H*/
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_sctp.h b/include/linux/netfilter_ipv4/ip_conntrack_sctp.h
deleted file mode 100644
index 4099a04..0000000
--- a/include/linux/netfilter_ipv4/ip_conntrack_sctp.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _IP_CONNTRACK_SCTP_H
-#define _IP_CONNTRACK_SCTP_H
-
-#include <linux/netfilter/nf_conntrack_sctp.h>
-
-#endif /* _IP_CONNTRACK_SCTP_H */
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_sip.h b/include/linux/netfilter_ipv4/ip_conntrack_sip.h
deleted file mode 100644
index bef6c64..0000000
--- a/include/linux/netfilter_ipv4/ip_conntrack_sip.h
+++ /dev/null
@@ -1,40 +0,0 @@
-#ifndef __IP_CONNTRACK_SIP_H__
-#define __IP_CONNTRACK_SIP_H__
-#ifdef __KERNEL__
-
-#define SIP_PORT	5060
-#define SIP_TIMEOUT	3600
-
-enum sip_header_pos {
-	POS_REG_REQ_URI,
-	POS_REQ_URI,
-	POS_FROM,
-	POS_TO,
-	POS_VIA,
-	POS_CONTACT,
-	POS_CONTENT,
-	POS_MEDIA,
-	POS_OWNER,
-	POS_CONNECTION,
-	POS_SDP_HEADER,
-};
-
-extern unsigned int (*ip_nat_sip_hook)(struct sk_buff **pskb,
-				       enum ip_conntrack_info ctinfo,
-				       struct ip_conntrack *ct,
-				       const char **dptr);
-extern unsigned int (*ip_nat_sdp_hook)(struct sk_buff **pskb,
-				       enum ip_conntrack_info ctinfo,
-				       struct ip_conntrack_expect *exp,
-				       const char *dptr);
-
-extern int ct_sip_get_info(const char *dptr, size_t dlen,
-			   unsigned int *matchoff,
-			   unsigned int *matchlen,
-			   enum sip_header_pos pos);
-extern int ct_sip_lnlen(const char *line, const char *limit);
-extern const char *ct_sip_search(const char *needle, const char *haystack,
-				 size_t needle_len, size_t haystack_len,
-				 int case_sensitive);
-#endif /* __KERNEL__ */
-#endif /* __IP_CONNTRACK_SIP_H__ */
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_tcp.h b/include/linux/netfilter_ipv4/ip_conntrack_tcp.h
deleted file mode 100644
index 876b8fb..0000000
--- a/include/linux/netfilter_ipv4/ip_conntrack_tcp.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _IP_CONNTRACK_TCP_H
-#define _IP_CONNTRACK_TCP_H
-
-#include <linux/netfilter/nf_conntrack_tcp.h>
-
-#endif /* _IP_CONNTRACK_TCP_H */
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_tftp.h b/include/linux/netfilter_ipv4/ip_conntrack_tftp.h
deleted file mode 100644
index a404fc0..0000000
--- a/include/linux/netfilter_ipv4/ip_conntrack_tftp.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef _IP_CT_TFTP
-#define _IP_CT_TFTP
-
-#define TFTP_PORT 69
-
-struct tftphdr {
-	__be16 opcode;
-};
-
-#define TFTP_OPCODE_READ	1
-#define TFTP_OPCODE_WRITE	2
-#define TFTP_OPCODE_DATA	3
-#define TFTP_OPCODE_ACK		4
-#define TFTP_OPCODE_ERROR	5
-
-extern unsigned int (*ip_nat_tftp_hook)(struct sk_buff **pskb,
-				 enum ip_conntrack_info ctinfo,
-				 struct ip_conntrack_expect *exp);
-
-#endif /* _IP_CT_TFTP */
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_tuple.h b/include/linux/netfilter_ipv4/ip_conntrack_tuple.h
deleted file mode 100644
index c228bde..0000000
--- a/include/linux/netfilter_ipv4/ip_conntrack_tuple.h
+++ /dev/null
@@ -1,146 +0,0 @@
-#ifndef _IP_CONNTRACK_TUPLE_H
-#define _IP_CONNTRACK_TUPLE_H
-
-#include <linux/types.h>
-#include <linux/netfilter/nf_conntrack_tuple_common.h>
-
-/* A `tuple' is a structure containing the information to uniquely
-  identify a connection.  ie. if two packets have the same tuple, they
-  are in the same connection; if not, they are not.
-
-  We divide the structure along "manipulatable" and
-  "non-manipulatable" lines, for the benefit of the NAT code.
-*/
-
-/* The protocol-specific manipulable parts of the tuple: always in
-   network order! */
-union ip_conntrack_manip_proto
-{
-	/* Add other protocols here. */
-	u_int16_t all;
-
-	struct {
-		__be16 port;
-	} tcp;
-	struct {
-		__be16 port;
-	} udp;
-	struct {
-		__be16 id;
-	} icmp;
-	struct {
-		__be16 port;
-	} sctp;
-	struct {
-		__be16 key;	/* key is 32bit, pptp only uses 16 */
-	} gre;
-};
-
-/* The manipulable part of the tuple. */
-struct ip_conntrack_manip
-{
-	__be32 ip;
-	union ip_conntrack_manip_proto u;
-};
-
-/* This contains the information to distinguish a connection. */
-struct ip_conntrack_tuple
-{
-	struct ip_conntrack_manip src;
-
-	/* These are the parts of the tuple which are fixed. */
-	struct {
-		__be32 ip;
-		union {
-			/* Add other protocols here. */
-			u_int16_t all;
-
-			struct {
-				__be16 port;
-			} tcp;
-			struct {
-				__be16 port;
-			} udp;
-			struct {
-				u_int8_t type, code;
-			} icmp;
-			struct {
-				__be16 port;
-			} sctp;
-			struct {
-				__be16 key;	/* key is 32bit, 
-						 * pptp only uses 16 */
-			} gre;
-		} u;
-
-		/* The protocol. */
-		u_int8_t protonum;
-
-		/* The direction (for tuplehash) */
-		u_int8_t dir;
-	} dst;
-};
-
-/* This is optimized opposed to a memset of the whole structure.  Everything we
- * really care about is the  source/destination unions */
-#define IP_CT_TUPLE_U_BLANK(tuple) 				\
-	do {							\
-		(tuple)->src.u.all = 0;				\
-		(tuple)->dst.u.all = 0;				\
-	} while (0)
-
-#ifdef __KERNEL__
-
-#define DUMP_TUPLE(tp)						\
-DEBUGP("tuple %p: %u %u.%u.%u.%u:%hu -> %u.%u.%u.%u:%hu\n",	\
-       (tp), (tp)->dst.protonum,				\
-       NIPQUAD((tp)->src.ip), ntohs((tp)->src.u.all),		\
-       NIPQUAD((tp)->dst.ip), ntohs((tp)->dst.u.all))
-
-/* If we're the first tuple, it's the original dir. */
-#define DIRECTION(h) ((enum ip_conntrack_dir)(h)->tuple.dst.dir)
-
-/* Connections have two entries in the hash table: one for each way */
-struct ip_conntrack_tuple_hash
-{
-	struct list_head list;
-
-	struct ip_conntrack_tuple tuple;
-};
-
-#endif /* __KERNEL__ */
-
-static inline int ip_ct_tuple_src_equal(const struct ip_conntrack_tuple *t1,
-				        const struct ip_conntrack_tuple *t2)
-{
-	return t1->src.ip == t2->src.ip
-		&& t1->src.u.all == t2->src.u.all;
-}
-
-static inline int ip_ct_tuple_dst_equal(const struct ip_conntrack_tuple *t1,
-				        const struct ip_conntrack_tuple *t2)
-{
-	return t1->dst.ip == t2->dst.ip
-		&& t1->dst.u.all == t2->dst.u.all
-		&& t1->dst.protonum == t2->dst.protonum;
-}
-
-static inline int ip_ct_tuple_equal(const struct ip_conntrack_tuple *t1,
-				    const struct ip_conntrack_tuple *t2)
-{
-	return ip_ct_tuple_src_equal(t1, t2) && ip_ct_tuple_dst_equal(t1, t2);
-}
-
-static inline int ip_ct_tuple_mask_cmp(const struct ip_conntrack_tuple *t,
-				       const struct ip_conntrack_tuple *tuple,
-				       const struct ip_conntrack_tuple *mask)
-{
-	return !(((t->src.ip ^ tuple->src.ip) & mask->src.ip)
-		 || ((t->dst.ip ^ tuple->dst.ip) & mask->dst.ip)
-		 || ((t->src.u.all ^ tuple->src.u.all) & mask->src.u.all)
-		 || ((t->dst.u.all ^ tuple->dst.u.all) & mask->dst.u.all)
-		 || ((t->dst.protonum ^ tuple->dst.protonum)
-		     & mask->dst.protonum));
-}
-
-#endif /* _IP_CONNTRACK_TUPLE_H */
diff --git a/include/linux/netfilter_ipv4/ip_nat.h b/include/linux/netfilter_ipv4/ip_nat.h
deleted file mode 100644
index bbca89a..0000000
--- a/include/linux/netfilter_ipv4/ip_nat.h
+++ /dev/null
@@ -1,79 +0,0 @@
-#ifndef _IP_NAT_H
-#define _IP_NAT_H
-#include <linux/netfilter_ipv4.h>
-#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
-
-#define IP_NAT_MAPPING_TYPE_MAX_NAMELEN 16
-
-enum ip_nat_manip_type
-{
-	IP_NAT_MANIP_SRC,
-	IP_NAT_MANIP_DST
-};
-
-/* SRC manip occurs POST_ROUTING or LOCAL_IN */
-#define HOOK2MANIP(hooknum) ((hooknum) != NF_IP_POST_ROUTING && (hooknum) != NF_IP_LOCAL_IN)
-
-#define IP_NAT_RANGE_MAP_IPS 1
-#define IP_NAT_RANGE_PROTO_SPECIFIED 2
-#define IP_NAT_RANGE_PROTO_RANDOM 4 /* add randomness to "port" selection */
-
-/* NAT sequence number modifications */
-struct ip_nat_seq {
-	/* position of the last TCP sequence number 
-	 * modification (if any) */
-	u_int32_t correction_pos;
-	/* sequence number offset before and after last modification */
-	int16_t offset_before, offset_after;
-};
-
-/* Single range specification. */
-struct ip_nat_range
-{
-	/* Set to OR of flags above. */
-	unsigned int flags;
-
-	/* Inclusive: network order. */
-	__be32 min_ip, max_ip;
-
-	/* Inclusive: network order */
-	union ip_conntrack_manip_proto min, max;
-};
-
-/* For backwards compat: don't use in modern code. */
-struct ip_nat_multi_range_compat
-{
-	unsigned int rangesize; /* Must be 1. */
-
-	/* hangs off end. */
-	struct ip_nat_range range[1];
-};
-
-#ifdef __KERNEL__
-#include <linux/list.h>
-
-/* Protects NAT hash tables, and NAT-private part of conntracks. */
-extern rwlock_t ip_nat_lock;
-
-/* The structure embedded in the conntrack structure. */
-struct ip_nat_info
-{
-	struct list_head bysource;
-	struct ip_nat_seq seq[IP_CT_DIR_MAX];
-};
-
-struct ip_conntrack;
-
-/* Set up the info structure to map into this range. */
-extern unsigned int ip_nat_setup_info(struct ip_conntrack *conntrack,
-				      const struct ip_nat_range *range,
-				      unsigned int hooknum);
-
-/* Is this tuple already taken? (not by us)*/
-extern int ip_nat_used_tuple(const struct ip_conntrack_tuple *tuple,
-			     const struct ip_conntrack *ignored_conntrack);
-
-#else  /* !__KERNEL__: iptables wants this to compile. */
-#define ip_nat_multi_range ip_nat_multi_range_compat
-#endif /*__KERNEL__*/
-#endif
diff --git a/include/linux/netfilter_ipv4/ip_nat_core.h b/include/linux/netfilter_ipv4/ip_nat_core.h
deleted file mode 100644
index 60566f9f..0000000
--- a/include/linux/netfilter_ipv4/ip_nat_core.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef _IP_NAT_CORE_H
-#define _IP_NAT_CORE_H
-#include <linux/list.h>
-#include <linux/netfilter_ipv4/ip_conntrack.h>
-
-/* This header used to share core functionality between the standalone
-   NAT module, and the compatibility layer's use of NAT for masquerading. */
-
-extern unsigned int ip_nat_packet(struct ip_conntrack *ct,
-			       enum ip_conntrack_info conntrackinfo,
-			       unsigned int hooknum,
-			       struct sk_buff **pskb);
-
-extern int ip_nat_icmp_reply_translation(struct ip_conntrack *ct,
-					 enum ip_conntrack_info ctinfo,
-					 unsigned int hooknum,
-					 struct sk_buff **pskb);
-#endif /* _IP_NAT_CORE_H */
diff --git a/include/linux/netfilter_ipv4/ip_nat_helper.h b/include/linux/netfilter_ipv4/ip_nat_helper.h
deleted file mode 100644
index bf9cb10..0000000
--- a/include/linux/netfilter_ipv4/ip_nat_helper.h
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef _IP_NAT_HELPER_H
-#define _IP_NAT_HELPER_H
-/* NAT protocol helper routines. */
-
-#include <linux/netfilter_ipv4/ip_conntrack.h>
-#include <linux/module.h>
-
-struct sk_buff;
-
-/* These return true or false. */
-extern int ip_nat_mangle_tcp_packet(struct sk_buff **skb,
-				struct ip_conntrack *ct,
-				enum ip_conntrack_info ctinfo,
-				unsigned int match_offset,
-				unsigned int match_len,
-				const char *rep_buffer,
-				unsigned int rep_len);
-extern int ip_nat_mangle_udp_packet(struct sk_buff **skb,
-				struct ip_conntrack *ct,
-				enum ip_conntrack_info ctinfo,
-				unsigned int match_offset,
-				unsigned int match_len,
-				const char *rep_buffer,
-				unsigned int rep_len);
-extern int ip_nat_seq_adjust(struct sk_buff **pskb, 
-			     struct ip_conntrack *ct, 
-			     enum ip_conntrack_info ctinfo);
-
-/* Setup NAT on this expected conntrack so it follows master, but goes
- * to port ct->master->saved_proto. */
-extern void ip_nat_follow_master(struct ip_conntrack *ct,
-				 struct ip_conntrack_expect *this);
-#endif
diff --git a/include/linux/netfilter_ipv4/ip_nat_pptp.h b/include/linux/netfilter_ipv4/ip_nat_pptp.h
deleted file mode 100644
index 36668bf..0000000
--- a/include/linux/netfilter_ipv4/ip_nat_pptp.h
+++ /dev/null
@@ -1,11 +0,0 @@
-/* PPTP constants and structs */
-#ifndef _NAT_PPTP_H
-#define _NAT_PPTP_H
-
-/* conntrack private data */
-struct ip_nat_pptp {
-	__be16 pns_call_id;		/* NAT'ed PNS call id */
-	__be16 pac_call_id;		/* NAT'ed PAC call id */
-};
-
-#endif /* _NAT_PPTP_H */
diff --git a/include/linux/netfilter_ipv4/ip_nat_protocol.h b/include/linux/netfilter_ipv4/ip_nat_protocol.h
deleted file mode 100644
index 612a436..0000000
--- a/include/linux/netfilter_ipv4/ip_nat_protocol.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/* Header for use in defining a given protocol. */
-#ifndef _IP_NAT_PROTOCOL_H
-#define _IP_NAT_PROTOCOL_H
-#include <linux/init.h>
-#include <linux/list.h>
-
-#include <linux/netfilter_ipv4/ip_nat.h>
-#include <linux/netfilter/nfnetlink_conntrack.h>
-
-struct iphdr;
-struct ip_nat_range;
-
-struct ip_nat_protocol
-{
-	/* Protocol name */
-	const char *name;
-
-	/* Protocol number. */
-	unsigned int protonum;
-
-	struct module *me;
-
-	/* Translate a packet to the target according to manip type.
-	   Return true if succeeded. */
-	int (*manip_pkt)(struct sk_buff **pskb,
-			 unsigned int iphdroff,
-			 const struct ip_conntrack_tuple *tuple,
-			 enum ip_nat_manip_type maniptype);
-
-	/* Is the manipable part of the tuple between min and max incl? */
-	int (*in_range)(const struct ip_conntrack_tuple *tuple,
-			enum ip_nat_manip_type maniptype,
-			const union ip_conntrack_manip_proto *min,
-			const union ip_conntrack_manip_proto *max);
-
-	/* Alter the per-proto part of the tuple (depending on
-	   maniptype), to give a unique tuple in the given range if
-	   possible; return false if not.  Per-protocol part of tuple
-	   is initialized to the incoming packet. */
-	int (*unique_tuple)(struct ip_conntrack_tuple *tuple,
-			    const struct ip_nat_range *range,
-			    enum ip_nat_manip_type maniptype,
-			    const struct ip_conntrack *conntrack);
-
-	int (*range_to_nfattr)(struct sk_buff *skb,
-			       const struct ip_nat_range *range);
-
-	int (*nfattr_to_range)(struct nfattr *tb[],
-			       struct ip_nat_range *range);
-};
-
-/* Protocol registration. */
-extern int ip_nat_protocol_register(struct ip_nat_protocol *proto);
-extern void ip_nat_protocol_unregister(struct ip_nat_protocol *proto);
-
-extern struct ip_nat_protocol *ip_nat_proto_find_get(u_int8_t protocol);
-extern void ip_nat_proto_put(struct ip_nat_protocol *proto);
-
-/* Built-in protocols. */
-extern struct ip_nat_protocol ip_nat_protocol_tcp;
-extern struct ip_nat_protocol ip_nat_protocol_udp;
-extern struct ip_nat_protocol ip_nat_protocol_icmp;
-extern struct ip_nat_protocol ip_nat_unknown_protocol;
-
-extern int init_protocols(void) __init;
-extern void cleanup_protocols(void);
-extern struct ip_nat_protocol *find_nat_proto(u_int16_t protonum);
-
-extern int ip_nat_port_range_to_nfattr(struct sk_buff *skb,
-				       const struct ip_nat_range *range);
-extern int ip_nat_port_nfattr_to_range(struct nfattr *tb[],
-				       struct ip_nat_range *range);
-
-#endif /*_IP_NAT_PROTO_H*/
diff --git a/include/linux/netfilter_ipv4/ip_nat_rule.h b/include/linux/netfilter_ipv4/ip_nat_rule.h
deleted file mode 100644
index 73b9552..0000000
--- a/include/linux/netfilter_ipv4/ip_nat_rule.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef _IP_NAT_RULE_H
-#define _IP_NAT_RULE_H
-#include <linux/netfilter_ipv4/ip_conntrack.h>
-#include <linux/netfilter_ipv4/ip_tables.h>
-#include <linux/netfilter_ipv4/ip_nat.h>
-
-#ifdef __KERNEL__
-
-extern int ip_nat_rule_init(void) __init;
-extern void ip_nat_rule_cleanup(void);
-extern int ip_nat_rule_find(struct sk_buff **pskb,
-			    unsigned int hooknum,
-			    const struct net_device *in,
-			    const struct net_device *out,
-			    struct ip_conntrack *ct,
-			    struct ip_nat_info *info);
-
-extern unsigned int
-alloc_null_binding(struct ip_conntrack *conntrack,
-		   struct ip_nat_info *info,
-		   unsigned int hooknum);
-
-extern unsigned int
-alloc_null_binding_confirmed(struct ip_conntrack *conntrack,
-			     struct ip_nat_info *info,
-			     unsigned int hooknum);
-#endif
-#endif /* _IP_NAT_RULE_H */
diff --git a/include/linux/netfilter_ipv4/ipt_SAME.h b/include/linux/netfilter_ipv4/ipt_SAME.h
index cc4c0b2..be6e682 100644
--- a/include/linux/netfilter_ipv4/ipt_SAME.h
+++ b/include/linux/netfilter_ipv4/ipt_SAME.h
@@ -13,7 +13,7 @@
 	u_int32_t *iparray;
 
 	/* hangs off end. */
-	struct ip_nat_range range[IPT_SAME_MAX_RANGE];
+	struct nf_nat_range range[IPT_SAME_MAX_RANGE];
 };
 
 #endif /*_IPT_SAME_H*/
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index 2a20f48..f41688f 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -138,6 +138,11 @@
 #include <linux/capability.h>
 #include <linux/skbuff.h>
 
+static inline struct nlmsghdr *nlmsg_hdr(const struct sk_buff *skb)
+{
+	return (struct nlmsghdr *)skb->data;
+}
+
 struct netlink_skb_parms
 {
 	struct ucred		creds;		/* Skb credentials	*/
@@ -152,7 +157,10 @@
 #define NETLINK_CREDS(skb)	(&NETLINK_CB((skb)).creds)
 
 
-extern struct sock *netlink_kernel_create(int unit, unsigned int groups, void (*input)(struct sock *sk, int len), struct module *module);
+extern struct sock *netlink_kernel_create(int unit, unsigned int groups,
+					  void (*input)(struct sock *sk, int len),
+					  struct mutex *cb_mutex,
+					  struct module *module);
 extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err);
 extern int netlink_has_listeners(struct sock *sk, unsigned int group);
 extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock);
@@ -171,9 +179,16 @@
 
 /*
  *	skb should fit one page. This choice is good for headerless malloc.
+ *	But we should limit to 8K so that userspace does not have to
+ *	use enormous buffer sizes on recvmsg() calls just to avoid
+ *	MSG_TRUNC when PAGE_SIZE is very large.
  */
-#define NLMSG_GOODORDER 0
-#define NLMSG_GOODSIZE (SKB_MAX_ORDER(0, NLMSG_GOODORDER))
+#if PAGE_SIZE < 8192UL
+#define NLMSG_GOODSIZE	SKB_WITH_OVERHEAD(PAGE_SIZE)
+#else
+#define NLMSG_GOODSIZE	SKB_WITH_OVERHEAD(8192UL)
+#endif
+
 #define NLMSG_DEFAULT_SIZE (NLMSG_GOODSIZE - NLMSG_HDRLEN)
 
 
@@ -217,18 +232,6 @@
 #define NLMSG_PUT(skb, pid, seq, type, len) \
 	NLMSG_NEW(skb, pid, seq, type, len, 0)
 
-#define NLMSG_NEW_ANSWER(skb, cb, type, len, flags) \
-	NLMSG_NEW(skb, NETLINK_CB((cb)->skb).pid, \
-		  (cb)->nlh->nlmsg_seq, type, len, flags)
-
-#define NLMSG_END(skb, nlh) \
-({	(nlh)->nlmsg_len = (skb)->tail - (unsigned char *) (nlh); \
-	(skb)->len; })
-
-#define NLMSG_CANCEL(skb, nlh) \
-({	skb_trim(skb, (unsigned char *) (nlh) - (skb)->data); \
-	-1; })
-
 extern int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
 			      struct nlmsghdr *nlh,
 			      int (*dump)(struct sk_buff *skb, struct netlink_callback*),
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
new file mode 100644
index 0000000..9a30ba2
--- /dev/null
+++ b/include/linux/nl80211.h
@@ -0,0 +1,38 @@
+#ifndef __LINUX_NL80211_H
+#define __LINUX_NL80211_H
+/*
+ * 802.11 netlink interface public header
+ *
+ * Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net>
+ */
+
+/**
+ * enum nl80211_iftype - (virtual) interface types
+ * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides
+ * @NL80211_IFTYPE_ADHOC: independent BSS member
+ * @NL80211_IFTYPE_STATION: managed BSS member
+ * @NL80211_IFTYPE_AP: access point
+ * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points
+ * @NL80211_IFTYPE_WDS: wireless distribution interface
+ * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames
+ * @__NL80211_IFTYPE_AFTER_LAST: internal use
+ *
+ * These values are used with the NL80211_ATTR_IFTYPE
+ * to set the type of an interface.
+ *
+ */
+enum nl80211_iftype {
+	NL80211_IFTYPE_UNSPECIFIED,
+	NL80211_IFTYPE_ADHOC,
+	NL80211_IFTYPE_STATION,
+	NL80211_IFTYPE_AP,
+	NL80211_IFTYPE_AP_VLAN,
+	NL80211_IFTYPE_WDS,
+	NL80211_IFTYPE_MONITOR,
+
+	/* keep last */
+	__NL80211_IFTYPE_AFTER_LAST
+};
+#define NL80211_IFTYPE_MAX (__NL80211_IFTYPE_AFTER_LAST - 1)
+
+#endif /* __LINUX_NL80211_H */
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 9cd0d0e..9632659 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -133,7 +133,7 @@
 static inline void SetPageUptodate(struct page *page)
 {
 	if (!test_and_set_bit(PG_uptodate, &page->flags))
-		page_test_and_clear_dirty(page);
+		page_clear_dirty(page);
 }
 #else
 #define SetPageUptodate(page)	set_bit(PG_uptodate, &(page)->flags)
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 481ea06..9724910 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -361,8 +361,6 @@
 	struct pci_error_handlers *err_handler;
 	struct device_driver	driver;
 	struct pci_dynids dynids;
-
-	int multithread_probe;
 };
 
 #define	to_pci_driver(drv) container_of(drv,struct pci_driver, driver)
@@ -840,6 +838,7 @@
 void pcim_iounmap(struct pci_dev *pdev, void __iomem *addr);
 void __iomem * const * pcim_iomap_table(struct pci_dev *pdev);
 int pcim_iomap_regions(struct pci_dev *pdev, u16 mask, const char *name);
+void pcim_iounmap_regions(struct pci_dev *pdev, u16 mask);
 
 extern int pci_pci_problems;
 #define PCIPCI_FAIL		1	/* No PCI PCI DMA */
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 600308f..1b0ddbb 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -368,7 +368,6 @@
 #define PCI_DEVICE_ID_ATI_IXP400_SATA   0x4379
 #define PCI_DEVICE_ID_ATI_IXP400_SATA2	0x437a
 #define PCI_DEVICE_ID_ATI_IXP600_SATA	0x4380
-#define PCI_DEVICE_ID_ATI_IXP600_SRAID	0x4381
 #define PCI_DEVICE_ID_ATI_IXP600_SMBUS	0x4385
 #define PCI_DEVICE_ID_ATI_IXP600_IDE	0x438c
 
@@ -1459,6 +1458,8 @@
 
 #define PCI_VENDOR_ID_TOSHIBA_2		0x102f
 #define PCI_DEVICE_ID_TOSHIBA_TC35815CF	0x0030
+#define PCI_DEVICE_ID_TOSHIBA_TC35815_NWU	0x0031
+#define PCI_DEVICE_ID_TOSHIBA_TC35815_TX4939	0x0032
 #define PCI_DEVICE_ID_TOSHIBA_TC86C001_IDE	0x0105
 #define PCI_DEVICE_ID_TOSHIBA_TC86C001_MISC	0x0108
 #define PCI_DEVICE_ID_TOSHIBA_SPIDER_NET 0x01b3
diff --git a/include/linux/phy.h b/include/linux/phy.h
index edd4c88..2a659789f 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -55,6 +55,7 @@
 	PHY_INTERFACE_MODE_TBI,
 	PHY_INTERFACE_MODE_RMII,
 	PHY_INTERFACE_MODE_RGMII,
+	PHY_INTERFACE_MODE_RGMII_ID,
 	PHY_INTERFACE_MODE_RTBI
 } phy_interface_t;
 
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 21db05a..6e8fa30 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -112,21 +112,25 @@
 
 typedef int __bitwise suspend_disk_method_t;
 
-#define	PM_DISK_FIRMWARE	((__force suspend_disk_method_t) 1)
-#define	PM_DISK_PLATFORM	((__force suspend_disk_method_t) 2)
-#define	PM_DISK_SHUTDOWN	((__force suspend_disk_method_t) 3)
-#define	PM_DISK_REBOOT		((__force suspend_disk_method_t) 4)
-#define	PM_DISK_TEST		((__force suspend_disk_method_t) 5)
-#define	PM_DISK_TESTPROC	((__force suspend_disk_method_t) 6)
-#define	PM_DISK_MAX		((__force suspend_disk_method_t) 7)
+/* invalid must be 0 so struct pm_ops initialisers can leave it out */
+#define PM_DISK_INVALID		((__force suspend_disk_method_t) 0)
+#define	PM_DISK_PLATFORM	((__force suspend_disk_method_t) 1)
+#define	PM_DISK_SHUTDOWN	((__force suspend_disk_method_t) 2)
+#define	PM_DISK_REBOOT		((__force suspend_disk_method_t) 3)
+#define	PM_DISK_TEST		((__force suspend_disk_method_t) 4)
+#define	PM_DISK_TESTPROC	((__force suspend_disk_method_t) 5)
+#define	PM_DISK_MAX		((__force suspend_disk_method_t) 6)
 
 /**
  * struct pm_ops - Callbacks for managing platform dependent suspend states.
  * @valid: Callback to determine whether the given state can be entered.
  * 	If %CONFIG_SOFTWARE_SUSPEND is set then %PM_SUSPEND_DISK is
- *	always valid and never passed to this call.
- *	If not assigned, all suspend states are advertised as valid
- *	in /sys/power/state (but can still be rejected by prepare or enter.)
+ *	always valid and never passed to this call. If not assigned,
+ *	no suspend states are valid.
+ *	Valid states are advertised in /sys/power/state but can still
+ *	be rejected by prepare or enter if the conditions aren't right.
+ *	There is a %pm_valid_only_mem function available that can be assigned
+ *	to this if you only implement mem sleep.
  *
  * @prepare: Prepare the platform for the given suspend state. Can return a
  *	negative error code if necessary.
@@ -137,17 +141,16 @@
  * @finish: Called when the system has left the given state and all devices
  *	are resumed. The return value is ignored.
  *
- * @pm_disk_mode: Set to the disk method that the user should be able to
- *	configure for suspend-to-disk. Since %PM_DISK_SHUTDOWN,
- *	%PM_DISK_REBOOT, %PM_DISK_TEST and %PM_DISK_TESTPROC
- *	are always allowed, currently only %PM_DISK_PLATFORM
- *	makes sense. If the user then choses %PM_DISK_PLATFORM,
- *	the @prepare call will be called before suspending to disk
- *	(if present), the @enter call should be present and will
- *	be called after all state has been saved and the machine
- *	is ready to be shut down/suspended/..., and the @finish
- *	callback is called after state has been restored. All
- *	these calls are called with %PM_SUSPEND_DISK as the state.
+ * @pm_disk_mode: The generic code always allows one of the shutdown methods
+ *	%PM_DISK_SHUTDOWN, %PM_DISK_REBOOT, %PM_DISK_TEST and
+ *	%PM_DISK_TESTPROC. If this variable is set, the mode it is set
+ *	to is allowed in addition to those modes and is also made default.
+ *	When this mode is sent selected, the @prepare call will be called
+ *	before suspending to disk (if present), the @enter call should be
+ *	present and will be called after all state has been saved and the
+ *	machine is ready to be powered off; the @finish callback is called
+ *	after state has been restored. All these calls are called with
+ *	%PM_SUSPEND_DISK as the state.
  */
 struct pm_ops {
 	int (*valid)(suspend_state_t state);
@@ -165,6 +168,25 @@
 extern struct pm_ops *pm_ops;
 extern int pm_suspend(suspend_state_t state);
 
+extern int pm_valid_only_mem(suspend_state_t state);
+
+/**
+ * arch_suspend_disable_irqs - disable IRQs for suspend
+ *
+ * Disables IRQs (in the default case). This is a weak symbol in the common
+ * code and thus allows architectures to override it if more needs to be
+ * done. Not called for suspend to disk.
+ */
+extern void arch_suspend_disable_irqs(void);
+
+/**
+ * arch_suspend_enable_irqs - enable IRQs after suspend
+ *
+ * Enables IRQs (in the default case). This is a weak symbol in the common
+ * code and thus allows architectures to override it if more needs to be
+ * done. Not called for suspend to disk.
+ */
+extern void arch_suspend_enable_irqs(void);
 
 /*
  * Device power management
@@ -273,6 +295,20 @@
 		__suspend_report_result(__FUNCTION__, fn, ret);		\
 	} while (0)
 
+/*
+ * Platform hook to activate device wakeup capability, if that's not already
+ * handled by enable_irq_wake() etc.
+ * Returns zero on success, else negative errno
+ */
+extern int (*platform_enable_wakeup)(struct device *dev, int is_on);
+
+static inline int call_platform_enable_wakeup(struct device *dev, int is_on)
+{
+	if (platform_enable_wakeup)
+		return (*platform_enable_wakeup)(dev, is_on);
+	return 0;
+}
+
 #else /* !CONFIG_PM */
 
 static inline int device_suspend(pm_message_t state)
@@ -294,6 +330,11 @@
 
 #define suspend_report_result(fn, ret) do { } while (0)
 
+static inline int call_platform_enable_wakeup(struct device *dev, int is_on)
+{
+	return 0;
+}
+
 #endif
 
 /* changes to device_may_wakeup take effect on the next pm state change.
diff --git a/include/linux/pmu.h b/include/linux/pmu.h
index 7831773..b0952e5 100644
--- a/include/linux/pmu.h
+++ b/include/linux/pmu.h
@@ -168,24 +168,16 @@
 
 struct pmu_sleep_notifier
 {
-	int (*notifier_call)(struct pmu_sleep_notifier *self, int when);
+	void (*notifier_call)(struct pmu_sleep_notifier *self, int when);
 	int priority;
 	struct list_head list;
 };
 
 /* Code values for calling sleep/wakeup handlers
- *
- * Note: If a sleep request got cancelled, all drivers will get
- * the PBOOK_SLEEP_REJECT, even those who didn't get the PBOOK_SLEEP_REQUEST.
  */
 #define PBOOK_SLEEP_REQUEST	1
 #define PBOOK_SLEEP_NOW		2
-#define PBOOK_SLEEP_REJECT	3
-#define PBOOK_WAKE		4
-
-/* Result codes returned by the notifiers */
-#define PBOOK_SLEEP_OK		0
-#define PBOOK_SLEEP_REFUSE	-1
+#define PBOOK_WAKE		3
 
 /* priority levels in notifiers */
 #define SLEEP_LEVEL_VIDEO	100	/* Video driver (first wake) */
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index 4a629ea..1fae30a 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -574,13 +574,6 @@
 #define rtattr_parse_nested(tb, max, rta) \
 	rtattr_parse((tb), (max), RTA_DATA((rta)), RTA_PAYLOAD((rta)))
 
-struct rtnetlink_link
-{
-	int (*doit)(struct sk_buff *, struct nlmsghdr*, void *attr);
-	int (*dumpit)(struct sk_buff *, struct netlink_callback *cb);
-};
-
-extern struct rtnetlink_link * rtnetlink_links[NPROTO];
 extern int rtnetlink_send(struct sk_buff *skb, u32 pid, u32 group, int echo);
 extern int rtnl_unicast(struct sk_buff *skb, u32 pid);
 extern int rtnl_notify(struct sk_buff *skb, u32 pid, u32 group,
@@ -605,7 +598,7 @@
 
 #define RTA_PUT_NOHDR(skb, attrlen, data) \
 ({	RTA_APPEND(skb, RTA_ALIGN(attrlen), data); \
-	memset(skb->tail - (RTA_ALIGN(attrlen) - attrlen), 0, \
+	memset(skb_tail_pointer(skb) - (RTA_ALIGN(attrlen) - attrlen), 0, \
 	       RTA_ALIGN(attrlen) - attrlen); })
 
 #define RTA_PUT_U8(skb, attrtype, value) \
@@ -637,12 +630,12 @@
 	RTA_PUT(skb, attrtype, 0, NULL);
 
 #define RTA_NEST(skb, type) \
-({	struct rtattr *__start = (struct rtattr *) (skb)->tail; \
+({	struct rtattr *__start = (struct rtattr *)skb_tail_pointer(skb); \
 	RTA_PUT(skb, type, 0, NULL); \
 	__start;  })
 
 #define RTA_NEST_END(skb, start) \
-({	(start)->rta_len = ((skb)->tail - (unsigned char *) (start)); \
+({	(start)->rta_len = skb_tail_pointer(skb) - (unsigned char *)(start); \
 	(skb)->len; })
 
 #define RTA_NEST_CANCEL(skb, start) \
diff --git a/include/linux/rxrpc.h b/include/linux/rxrpc.h
new file mode 100644
index 0000000..f7b826b
--- /dev/null
+++ b/include/linux/rxrpc.h
@@ -0,0 +1,62 @@
+/* AF_RXRPC parameters
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#ifndef _LINUX_RXRPC_H
+#define _LINUX_RXRPC_H
+
+#include <linux/in.h>
+#include <linux/in6.h>
+
+/*
+ * RxRPC socket address
+ */
+struct sockaddr_rxrpc {
+	sa_family_t	srx_family;	/* address family */
+	u16		srx_service;	/* service desired */
+	u16		transport_type;	/* type of transport socket (SOCK_DGRAM) */
+	u16		transport_len;	/* length of transport address */
+	union {
+		sa_family_t family;		/* transport address family */
+		struct sockaddr_in sin;		/* IPv4 transport address */
+		struct sockaddr_in6 sin6;	/* IPv6 transport address */
+	} transport;
+};
+
+/*
+ * RxRPC socket options
+ */
+#define RXRPC_SECURITY_KEY		1	/* [clnt] set client security key */
+#define RXRPC_SECURITY_KEYRING		2	/* [srvr] set ring of server security keys */
+#define RXRPC_EXCLUSIVE_CONNECTION	3	/* [clnt] use exclusive RxRPC connection */
+#define RXRPC_MIN_SECURITY_LEVEL	4	/* minimum security level */
+
+/*
+ * RxRPC control messages
+ * - terminal messages mean that a user call ID tag can be recycled
+ */
+#define RXRPC_USER_CALL_ID	1	/* user call ID specifier */
+#define RXRPC_ABORT		2	/* abort request / notification [terminal] */
+#define RXRPC_ACK		3	/* [Server] RPC op final ACK received [terminal] */
+#define RXRPC_NET_ERROR		5	/* network error received [terminal] */
+#define RXRPC_BUSY		6	/* server busy received [terminal] */
+#define RXRPC_LOCAL_ERROR	7	/* local error generated [terminal] */
+#define RXRPC_NEW_CALL		8	/* [Server] new incoming call notification */
+#define RXRPC_ACCEPT		9	/* [Server] accept request */
+
+/*
+ * RxRPC security levels
+ */
+#define RXRPC_SECURITY_PLAIN	0	/* plain secure-checksummed packets only */
+#define RXRPC_SECURITY_AUTH	1	/* authenticated packets */
+#define RXRPC_SECURITY_ENCRYPT	2	/* encrypted packets */
+
+
+#endif /* _LINUX_RXRPC_H */
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 49fe299..a170758 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -196,13 +196,13 @@
 extern cpumask_t nohz_cpu_mask;
 
 /*
- * Only dump TASK_* tasks. (-1 for all tasks)
+ * Only dump TASK_* tasks. (0 for all tasks)
  */
 extern void show_state_filter(unsigned long state_filter);
 
 static inline void show_state(void)
 {
-	show_state_filter(-1);
+	show_state_filter(0);
 }
 
 extern void show_regs(struct pt_regs *);
diff --git a/include/linux/sctp.h b/include/linux/sctp.h
index d4f8656..d70df61 100644
--- a/include/linux/sctp.h
+++ b/include/linux/sctp.h
@@ -63,6 +63,15 @@
 	__be32 checksum;
 } __attribute__((packed)) sctp_sctphdr_t;
 
+#ifdef __KERNEL__
+#include <linux/skbuff.h>
+
+static inline struct sctphdr *sctp_hdr(const struct sk_buff *skb)
+{
+	return (struct sctphdr *)skb_transport_header(skb);
+}
+#endif
+
 /* Section 3.2.  Chunk Field Descriptions. */
 typedef struct sctp_chunkhdr {
 	__u8 type;
diff --git a/include/linux/sdla_fr.h b/include/linux/sdla_fr.h
deleted file mode 100644
index cdfa77f..0000000
--- a/include/linux/sdla_fr.h
+++ /dev/null
@@ -1,638 +0,0 @@
-/*****************************************************************************
-* sdla_fr.h	Sangoma frame relay firmware API definitions.
-*
-* Author:       Gideon Hack  	
-*		Nenad Corbic <ncorbic@sangoma.com> 	
-*
-* Copyright:	(c) 1995-2000 Sangoma Technologies Inc.
-*
-*		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.
-* ============================================================================
-* Oct 04, 1999  Gideon Hack     Updated API structures
-* Jun 02, 1999  Gideon Hack 	Modifications for S514 support
-* Oct 12, 1997	Jaspreet Singh	Added FR_READ_DLCI_IB_MAPPING
-* Jul 21, 1997 	Jaspreet Singh	Changed FRRES_TOO_LONG and FRRES_TOO_MANY to 
-*				0x05 and 0x06 respectively.
-* Dec 23, 1996	Gene Kozin	v2.0
-* Apr 29, 1996	Gene Kozin	v1.0 (merged version S502 & S508 definitions).
-* Sep 26, 1995	Gene Kozin	Initial version.
-*****************************************************************************/
-#ifndef	_SDLA_FR_H
-#define	_SDLA_FR_H
-
-/*----------------------------------------------------------------------------
- * Notes:
- * ------
- * 1. All structures defined in this file are byte-alined.  
- *
- *	Compiler	Platform
- *	--------	--------
- *	GNU C		Linux
- */
-
-#ifndef	PACKED
-#    define	PACKED	__attribute__((packed))
-#endif	/* PACKED */
-
-/* Adapter memory layout */
-#define	FR_MB_VECTOR	0xE000	/* mailbox window vector */
-#define	FR502_RX_VECTOR	0xA000	/* S502 direct receive window vector */
-#define	FR502_MBOX_OFFS	0xF60	/* S502 mailbox offset */
-#define	FR508_MBOX_OFFS	0	/* S508 mailbox offset */
-#define	FR502_FLAG_OFFS	0x1FF0	/* S502 status flags offset */
-#define	FR508_FLAG_OFFS	0x1000	/* S508 status flags offset */
-#define	FR502_RXMB_OFFS	0x900	/* S502 direct receive mailbox offset */
-#define	FR508_TXBC_OFFS	0x1100	/* S508 Tx buffer info offset */
-#define	FR508_RXBC_OFFS	0x1120	/* S508 Rx buffer info offset */
-
-/* Important constants */
-#define FR502_MAX_DATA	4096	/* maximum data buffer length */
-#define FR508_MAX_DATA	4080	/* maximum data buffer length */
-#define MIN_LGTH_FR_DATA_CFG         300     /* min Information frame length
-(for configuration purposes) */
-#define FR_MAX_NO_DATA_BYTES_IN_FRAME  15354 	/* max Information frame length */
- 
-#define HIGHEST_VALID_DLCI	991
-
-/****** Data Structures *****************************************************/
-
-/*----------------------------------------------------------------------------
- * Frame relay command block.
- */
-typedef struct fr_cmd
-{
-	unsigned char  command	PACKED;	/* command code */
-	unsigned short length	PACKED;	/* length of data buffer */
-	unsigned char  result	PACKED;	/* return code */
-	unsigned short dlci	PACKED;	/* DLCI number */
-	unsigned char  attr	PACKED;	/* FECN, BECN, DE and C/R bits */
-	unsigned short rxlost1	PACKED;	/* frames discarded at int. level */
-	unsigned long  rxlost2	PACKED;	/* frames discarded at app. level */
-	unsigned char  rsrv[2]	PACKED;	/* reserved for future use */
-} fr_cmd_t;
-
-/* 'command' field defines */
-#define	FR_WRITE		0x01
-#define	FR_READ			0x02
-#define	FR_ISSUE_IS_FRAME	0x03
-#define FR_SET_CONFIG		0x10
-#define FR_READ_CONFIG		0x11
-#define FR_COMM_DISABLE		0x12
-#define FR_COMM_ENABLE		0x13
-#define FR_READ_STATUS		0x14
-#define FR_READ_STATISTICS	0x15
-#define FR_FLUSH_STATISTICS	0x16
-#define	FR_LIST_ACTIVE_DLCI	0x17
-#define FR_FLUSH_DATA_BUFFERS	0x18
-#define FR_READ_ADD_DLC_STATS	0x19
-#define	FR_ADD_DLCI		0x20
-#define	FR_DELETE_DLCI		0x21
-#define	FR_ACTIVATE_DLCI	0x22
-#define	FR_DEACTIVATE_DLCI	0x22
-#define FR_READ_MODEM_STATUS	0x30
-#define FR_SET_MODEM_STATUS	0x31
-#define FR_READ_ERROR_STATS	0x32
-#define FR_FLUSH_ERROR_STATS	0x33
-#define FR_READ_DLCI_IB_MAPPING 0x34
-#define FR_READ_CODE_VERSION	0x40
-#define	FR_SET_INTR_MODE	0x50
-#define	FR_READ_INTR_MODE	0x51
-#define FR_SET_TRACE_CONFIG	0x60
-#define FR_FT1_STATUS_CTRL 	0x80
-#define FR_SET_FT1_MODE		0x81
-
-/* Special UDP drivers management commands */
-#define FPIPE_ENABLE_TRACING          	0x41
-#define FPIPE_DISABLE_TRACING		0x42
-#define FPIPE_GET_TRACE_INFO            0x43
-#define FPIPE_FT1_READ_STATUS           0x44
-#define FPIPE_DRIVER_STAT_IFSEND        0x45
-#define FPIPE_DRIVER_STAT_INTR          0x46
-#define FPIPE_DRIVER_STAT_GEN           0x47
-#define FPIPE_FLUSH_DRIVER_STATS        0x48
-#define FPIPE_ROUTER_UP_TIME            0x49
-
-/* 'result' field defines */
-#define FRRES_OK		0x00	/* command executed successfully */
-#define	FRRES_DISABLED		0x01	/* communications not enabled */
-#define	FRRES_INOPERATIVE	0x02	/* channel inoperative */
-#define	FRRES_DLCI_INACTIVE	0x03	/* DLCI is inactive */
-#define	FRRES_DLCI_INVALID	0x04	/* DLCI is not configured */
-#define	FRRES_TOO_LONG		0x05
-#define	FRRES_TOO_MANY		0x06
-#define	FRRES_CIR_OVERFLOW	0x07	/* Tx throughput has exceeded CIR */
-#define	FRRES_BUFFER_OVERFLOW	0x08
-#define	FRRES_MODEM_FAILURE	0x10	/* DCD and/or CTS dropped */
-#define	FRRES_CHANNEL_DOWN	0x11	/* channel became inoperative */
-#define	FRRES_CHANNEL_UP	0x12	/* channel became operative */
-#define	FRRES_DLCI_CHANGE	0x13	/* DLCI status (or number) changed */
-#define	FRRES_DLCI_MISMATCH	0x14
-#define	FRRES_INVALID_CMD	0x1F	/* invalid command */
-
-/* 'attr' field defines */
-#define	FRATTR_
-
-/*----------------------------------------------------------------------------
- * Frame relay mailbox.
- *	This structure is located at offset FR50?_MBOX_OFFS into FR_MB_VECTOR.
- *	For S502 it is also located at offset FR502_RXMB_OFFS into
- *	FR502_RX_VECTOR.
- */
-typedef struct fr_mbox
-{
-	unsigned char opflag	PACKED;	/* 00h: execution flag */
-	fr_cmd_t cmd		PACKED;	/* 01h: command block */
-	unsigned char data[1]	PACKED;	/* 10h: variable length data buffer */
-} fr_mbox_t;
-
-/*----------------------------------------------------------------------------
- * S502 frame relay status flags.
- *	This structure is located at offset FR502_FLAG_OFFS into FR_MB_VECTOR.
- */
-typedef struct	fr502_flags
-{	
-	unsigned char rsrv1[1]	PACKED;	/* 00h: */
-	unsigned char tx_ready	PACKED;	/* 01h: Tx buffer available */
-	unsigned char rx_ready	PACKED;	/* 02h: Rx frame available */
-	unsigned char event	PACKED;	/* 03h: asynchronous event */
-	unsigned char mstatus	PACKED;	/* 04h: modem status */
-	unsigned char rsrv2[8]	PACKED;	/* 05h: */
-	unsigned char iflag	PACKED;	/* 0Dh: interrupt flag */
-	unsigned char imask	PACKED;	/* 0Eh: interrupt mask */
-} fr502_flags_t;
-
-/*----------------------------------------------------------------------------
- * S508 frame relay status flags.
- *	This structure is located at offset FR508_FLAG_OFFS into FR_MB_VECTOR.
- */
-typedef struct	fr508_flags
-{
-	unsigned char rsrv1[3]	PACKED;	/* 00h: reserved */
-	unsigned char event	PACKED;	/* 03h: asynchronous event */
-	unsigned char mstatus	PACKED;	/* 04h: modem status */
-	unsigned char rsrv2[11]	PACKED;	/* 05h: reserved */
-	unsigned char iflag	PACKED;	/* 10h: interrupt flag */
-	unsigned char imask	PACKED;	/* 11h: interrupt mask */
-	unsigned long tse_offs	PACKED;	/* 12h: Tx status element */
-	unsigned short dlci	PACKED; /* 16h: DLCI NUMBER */
-} fr508_flags_t;
-
-/* 'event' field defines */
-#define	FR_EVENT_STATUS		0x01	/* channel status change */
-#define	FR_EVENT_DLC_STATUS	0x02	/* DLC status change */
-#define	FR_EVENT_BAD_DLCI	0x04	/* FSR included wrong DLCI */
-#define	FR_EVENT_LINK_DOWN	0x40	/* DCD or CTS low */
-
-/* 'mstatus' field defines */
-#define	FR_MDM_DCD		0x08	/* mdm_status: DCD */
-#define	FR_MDM_CTS		0x20	/* mdm_status: CTS */
-
-/* 'iflag' & 'imask' fields defines */
-#define	FR_INTR_RXRDY		0x01	/* Rx ready */
-#define	FR_INTR_TXRDY		0x02	/* Tx ready */
-#define	FR_INTR_MODEM		0x04	/* modem status change (DCD, CTS) */
-#define	FR_INTR_READY		0x08	/* interface command completed */
-#define	FR_INTR_DLC		0x10	/* DLC status change */
-#define	FR_INTR_TIMER		0x20	/* millisecond timer */
-#define FR_INTR_TX_MULT_DLCIs	0x80	/* Tx interrupt on multiple DLCIs */
-
-
-/*----------------------------------------------------------------------------
- * Receive Buffer Configuration Info. S508 only!
- *	This structure is located at offset FR508_RXBC_OFFS into FR_MB_VECTOR.
- */
-typedef struct	fr_buf_info
-{
-	unsigned short rse_num	PACKED;	/* 00h: number of status elements */
-	unsigned long rse_base	PACKED;	/* 02h: receive status array base */
-	unsigned long rse_next	PACKED;	/* 06h: next status element */
-	unsigned long buf_base	PACKED;	/* 0Ah: rotational buffer base */
-	unsigned short reserved	PACKED;	/* 0Eh:  */
-	unsigned long buf_top	PACKED;	/* 10h: rotational buffer top */
-} fr_buf_info_t;
-
-/*----------------------------------------------------------------------------
- * Buffer Status Element. S508 only!
- *	Array of structures of this type is located at offset defined by the
- *	'rse_base' field of the frBufInfo_t structure into absolute adapter
- *	memory address space.
- */
-typedef struct	fr_rx_buf_ctl
-{
-	unsigned char flag	PACKED;	/* 00h: ready flag */
-	unsigned short length	PACKED;	/* 01h: frame length */
-	unsigned short dlci	PACKED;	/* 03h: DLCI */
-	unsigned char attr	PACKED;	/* 05h: FECN/BECN/DE/CR */
-	unsigned short tmstamp	PACKED;	/* 06h: time stamp */
-	unsigned short rsrv[2]	PACKED; /* 08h:  */
-	unsigned long offset	PACKED;	/* 0Ch: buffer absolute address */
-} fr_rx_buf_ctl_t;
-
-typedef struct  fr_tx_buf_ctl
-{
-        unsigned char flag      PACKED; /* 00h: ready flag */
-	unsigned short rsrv0[2]	PACKED;	/* 01h: */
-        unsigned short length   PACKED; /* 05h: frame length */
-        unsigned short dlci     PACKED; /* 07h: DLCI */
-        unsigned char attr      PACKED; /* 09h: FECN/BECN/DE/CR */
-        unsigned short rsrv1 	PACKED; /* 0Ah:  */
-        unsigned long offset    PACKED; /* 0Ch: buffer absolute address */
-} fr_tx_buf_ctl_t;
-
-/*----------------------------------------------------------------------------
- * Global Configuration Block. Passed to FR_SET_CONFIG command when dlci == 0.
- */
-typedef struct	fr_conf
-{
-	unsigned short station	PACKED;	/* 00h: CPE/Node */
-	unsigned short options	PACKED;	/* 02h: configuration options */
-	unsigned short kbps	PACKED;	/* 04h: baud rate in kbps */
-	unsigned short port	PACKED;	/* 06h: RS-232/V.35 */
-	unsigned short mtu	PACKED;	/* 08h: max. transmit length */
-	unsigned short t391	PACKED;	/* 0Ah:  */
-	unsigned short t392	PACKED;	/* 0Ch:  */
-	unsigned short n391	PACKED;	/* 0Eh:  */
-	unsigned short n392	PACKED;	/* 10h:  */
-	unsigned short n393	PACKED;	/* 12h:  */
-	unsigned short cir_fwd	PACKED;	/* 14h:  */
-	unsigned short bc_fwd	PACKED;	/* 16h:  */
-	unsigned short be_fwd	PACKED;	/* 18h:  */
-	unsigned short cir_bwd	PACKED;	/* 1Ah:  */
-	unsigned short bc_bwd	PACKED;	/* 1Ch:  */
-	unsigned short be_bwd	PACKED;	/* 1Eh:  */
-	unsigned short dlci[0]	PACKED;	/* 20h:  */
-} fr_conf_t;
-
-/* 'station_type' defines */
-#define	FRCFG_STATION_CPE	0
-#define	FRCFG_STATION_NODE	1
-
-/* 'conf_flags' defines */
-#define	FRCFG_IGNORE_TX_CIR	0x0001
-#define	FRCFG_IGNORE_RX_CIR	0x0002
-#define	FRCFG_DONT_RETRANSMIT	0x0004
-#define	FRCFG_IGNORE_CBS	0x0008
-#define	FRCFG_THROUGHPUT	0x0010	/* enable throughput calculation */
-#define	FRCFG_DIRECT_RX		0x0080	/* enable direct receive buffer */
-#define	FRCFG_AUTO_CONFIG	0x8000	/* enable  auto DLCI configuration */
-
-/* 'baud_rate' defines */
-#define	FRCFG_BAUD_1200		12
-#define	FRCFG_BAUD_2400		24
-#define	FRCFG_BAUD_4800		48
-#define	FRCFG_BAUD_9600		96
-#define	FRCFG_BAUD_19200	19
-#define	FRCFG_BAUD_38400	38
-#define	FRCFG_BAUD_56000	56
-#define	FRCFG_BAUD_64000	64
-#define	FRCFG_BAUD_128000	128
-
-/* 'port_mode' defines */
-#define	FRCFG_MODE_EXT_CLK	0x0000
-#define	FRCFG_MODE_INT_CLK	0x0001
-#define	FRCFG_MODE_V35		0x0000	/* S508 only */
-#define	FRCFG_MODE_RS232	0x0002	/* S508 only */
-
-/* defines for line tracing */
-
-/* the line trace status element presented by the frame relay code */
-typedef struct {
-        unsigned char flag      PACKED; /* ready flag */
-        unsigned short length   PACKED; /* trace length */
-        unsigned char rsrv0[2]  PACKED; /* reserved */
-        unsigned char attr      PACKED; /* trace attributes */
-        unsigned short tmstamp  PACKED; /* time stamp */
-        unsigned char rsrv1[4]  PACKED; /* reserved */
-        unsigned long offset    PACKED; /* buffer absolute address */
-} fr_trc_el_t;
-
-typedef struct {
-        unsigned char status    	PACKED; /* status flag */
-	unsigned char data_passed	PACKED;	/* 0 if no data passed, 1 if */
-						/* data passed */
-        unsigned short length   	PACKED; /* frame length */
-        unsigned short tmstamp  	PACKED; /* time stamp */
-} fpipemon_trc_hdr_t;
-
-typedef struct {
-	fpipemon_trc_hdr_t fpipemon_trc_hdr			PACKED;
-        unsigned char data[FR_MAX_NO_DATA_BYTES_IN_FRAME]	PACKED;
-} fpipemon_trc_t;
-
-/* bit settings for the 'status' byte  - note that bits 1, 2 and 3 are used */
-/* for returning the number of frames being passed to fpipemon */
-#define TRC_OUTGOING_FRM	0x01
-#define TRC_ABORT_ERROR         0x10
-#define TRC_CRC_ERROR           0x20
-#define TRC_OVERRUN_ERROR       0x40
-#define MORE_TRC_DATA		0x80
-
-#define MAX_FRMS_TRACED		0x07
-
-#define NO_TRC_ELEMENTS_OFF		0x9000
-#define BASE_TRC_ELEMENTS_OFF		0x9002
-#define TRC_ACTIVE			0x01
-#define FLUSH_TRC_BUFFERS 		0x02
-#define FLUSH_TRC_STATISTICS		0x04
-#define TRC_SIGNALLING_FRMS		0x10
-#define TRC_INFO_FRMS			0x20
-#define ACTIVATE_TRC	(TRC_ACTIVE | TRC_SIGNALLING_FRMS | TRC_INFO_FRMS)
-#define RESET_TRC	(FLUSH_TRC_BUFFERS | FLUSH_TRC_STATISTICS)
-
-/*----------------------------------------------------------------------------
- * Channel configuration.
- *	This structure is passed to the FR_SET_CONFIG command when dlci != 0.
- */
-typedef struct	fr_dlc_conf
-{
-	unsigned short conf_flags	PACKED;	/* 00h: configuration bits */
-	unsigned short cir_fwd		PACKED;	/* 02h:  */
-	unsigned short bc_fwd		PACKED;	/* 04h:  */
-	unsigned short be_fwd		PACKED;	/* 06h:  */
-	unsigned short cir_bwd		PACKED;	/* 08h:  */
-	unsigned short bc_bwd		PACKED;	/* 0Ah:  */
-	unsigned short be_bwd		PACKED;	/* 0Ch:  */
-} fr_dlc_conf_t;
-
-/*----------------------------------------------------------------------------
- * S502 interrupt mode control block.
- *	This structure is passed to the FR_SET_INTR_FLAGS and returned by the
- *	FR_READ_INTR_FLAGS commands.
- */
-typedef struct fr502_intr_ctl
-{
-	unsigned char mode	PACKED;	/* 00h: interrupt enable flags */
-	unsigned short tx_len	PACKED;	/* 01h: required Tx buffer size */
-} fr502_intr_ctl_t;
-
-/*----------------------------------------------------------------------------
- * S508 interrupt mode control block.
- *	This structure is passed to the FR_SET_INTR_FLAGS and returned by the
- *	FR_READ_INTR_FLAGS commands.
- */
-typedef struct fr508_intr_ctl
-{
-	unsigned char mode	PACKED;	/* 00h: interrupt enable flags */
-	unsigned short tx_len	PACKED;	/* 01h: required Tx buffer size */
-	unsigned char irq	PACKED;	/* 03h: IRQ level to activate */
-	unsigned char flags	PACKED;	/* 04h: ?? */
-	unsigned short timeout	PACKED;	/* 05h: ms, for timer interrupt */
-} fr508_intr_ctl_t;
-
-/*----------------------------------------------------------------------------
- * Channel status.
- *	This structure is returned by the FR_READ_STATUS command.
- */
-typedef struct	fr_dlc_Status
-{
-	unsigned char status		PACKED;	/* 00h: link/DLCI status */
-	struct
-	{
-		unsigned short dlci	PACKED;	/* 01h: DLCI number */
-		unsigned char status	PACKED;	/* 03h: DLCI status */
-	} circuit[1]			PACKED;
-} fr_dlc_status_t;
-
-/* 'status' defines */
-#define	FR_LINK_INOPER	0x00		/* for global status (DLCI == 0) */
-#define	FR_LINK_OPER	0x01
-#define	FR_DLCI_DELETED	0x01		/* for circuit status (DLCI != 0) */
-#define	FR_DLCI_ACTIVE	0x02
-#define	FR_DLCI_WAITING	0x04
-#define	FR_DLCI_NEW	0x08
-#define	FR_DLCI_REPORT	0x40
-
-/*----------------------------------------------------------------------------
- * Global Statistics Block.
- *	This structure is returned by the FR_READ_STATISTICS command when
- *	dcli == 0.
- */
-typedef struct	fr_link_stat
-{
-	unsigned short rx_too_long	PACKED;	/* 00h:  */
-	unsigned short rx_dropped	PACKED;	/* 02h:  */
-	unsigned short rx_dropped2	PACKED;	/* 04h:  */
-	unsigned short rx_bad_dlci	PACKED;	/* 06h:  */
-	unsigned short rx_bad_format	PACKED;	/* 08h:  */
-	unsigned short retransmitted	PACKED;	/* 0Ah:  */
-	unsigned short cpe_tx_FSE	PACKED;	/* 0Ch:  */
-	unsigned short cpe_tx_LIV	PACKED;	/* 0Eh:  */
-	unsigned short cpe_rx_FSR	PACKED;	/* 10h:  */
-	unsigned short cpe_rx_LIV	PACKED;	/* 12h:  */
-	unsigned short node_rx_FSE	PACKED;	/* 14h:  */
-	unsigned short node_rx_LIV	PACKED;	/* 16h:  */
-	unsigned short node_tx_FSR	PACKED;	/* 18h:  */
-	unsigned short node_tx_LIV	PACKED;	/* 1Ah:  */
-	unsigned short rx_ISF_err	PACKED;	/* 1Ch:  */
-	unsigned short rx_unsolicited	PACKED;	/* 1Eh:  */
-	unsigned short rx_SSN_err	PACKED;	/* 20h:  */
-	unsigned short rx_RSN_err	PACKED;	/* 22h:  */
-	unsigned short T391_timeouts	PACKED;	/* 24h:  */
-	unsigned short T392_timeouts	PACKED;	/* 26h:  */
-	unsigned short N392_reached	PACKED;	/* 28h:  */
-	unsigned short cpe_SSN_RSN	PACKED;	/* 2Ah:  */
-	unsigned short current_SSN	PACKED;	/* 2Ch:  */
-	unsigned short current_RSN	PACKED;	/* 2Eh:  */
-	unsigned short curreny_T391	PACKED;	/* 30h:  */
-	unsigned short current_T392	PACKED;	/* 32h:  */
-	unsigned short current_N392	PACKED;	/* 34h:  */
-	unsigned short current_N393	PACKED;	/* 36h:  */
-} fr_link_stat_t;
-
-/*----------------------------------------------------------------------------
- * DLCI statistics.
- *	This structure is returned by the FR_READ_STATISTICS command when
- *	dlci != 0.
- */
-typedef struct	fr_dlci_stat
-{
-	unsigned long tx_frames		PACKED;	/* 00h:  */
-	unsigned long tx_bytes		PACKED;	/* 04h:  */
-	unsigned long rx_frames		PACKED;	/* 08h:  */
-	unsigned long rx_bytes		PACKED;	/* 0Ch:  */
-	unsigned long rx_dropped	PACKED;	/* 10h:  */
-	unsigned long rx_inactive	PACKED;	/* 14h:  */
-	unsigned long rx_exceed_CIR	PACKED;	/* 18h:  */
-	unsigned long rx_DE_set		PACKED;	/* 1Ch:  */
-	unsigned long tx_throughput	PACKED;	/* 20h:  */
-	unsigned long tx_calc_timer	PACKED;	/* 24h:  */
-	unsigned long rx_throughput	PACKED;	/* 28h:  */
-	unsigned long rx_calc_timer	PACKED;	/* 2Ch:  */
-} fr_dlci_stat_t;
-
-/*----------------------------------------------------------------------------
- * Communications error statistics.
- *	This structure is returned by the FR_READ_ERROR_STATS command.
- */
-typedef struct	fr_comm_stat
-{
-	unsigned char rx_overruns	PACKED;	/* 00h:  */
-	unsigned char rx_bad_crc	PACKED;	/* 01h:  */
-	unsigned char rx_aborts		PACKED;	/* 02h:  */
-	unsigned char rx_too_long	PACKED;	/* 03h:  */
-	unsigned char tx_aborts		PACKED;	/* 04h:  */
-	unsigned char tx_underruns	PACKED;	/* 05h:  */
-	unsigned char tx_missed_undr	PACKED;	/* 06h:  */
-	unsigned char dcd_dropped	PACKED;	/* 07h:  */
-	unsigned char cts_dropped	PACKED;	/* 08h:  */
-} fr_comm_stat_t;
-
-/*----------------------------------------------------------------------------
- * Defines for the FR_ISSUE_IS_FRAME command.
- */
-#define	FR_ISF_LVE	2		/* issue Link Verification Enquiry */
-#define	FR_ISF_FSE	3		/* issue Full Status Enquiry */
-
-/*----------------------------------------------------------------------------
- * Frame Relay ARP Header -- Used for Dynamic route creation with InvARP 
- */
-
-typedef struct arphdr_fr
-	{
-	unsigned short ar_hrd PACKED;		/* format of hardware addr */
-	unsigned short ar_pro PACKED;		/* format of protocol addr */
-	unsigned char  ar_hln PACKED;		/* length of hardware addr */	
-	unsigned char  ar_pln PACKED;		/* length of protocol addr */
-	unsigned short ar_op  PACKED;		/* ARP opcode		   */
-	unsigned short ar_sha PACKED;		/* Sender DLCI addr 2 bytes */
-	unsigned long  ar_sip PACKED;		/* Sender IP   addr 4 bytes */
-	unsigned short ar_tha PACKED;		/* Target DLCI addr 2 bytes */
-	unsigned long  ar_tip PACKED;		/* Target IP   addr 4 bytes */
-	} arphdr_fr_t;
-
-/*----------------------------------------------------------------------------
- * Frame Relay RFC 1490 SNAP Header -- Used to check for ARP packets
- */
-typedef struct arphdr_1490
-	{
-	unsigned char control PACKED;		/* UI, etc...  */
-	unsigned char pad     PACKED;		/* Pad */
-	unsigned char NLPID   PACKED;		/* SNAP */
-	unsigned char OUI[3]  PACKED;		/* Ethertype, etc... */
-	unsigned short PID    PACKED;		/* ARP, IP, etc... */
-	}  arphdr_1490_t;
-
-/* UDP/IP packet (for UDP management) layout */
-
-/* The embedded control block for UDP mgmt
-   This is essentially a mailbox structure, without the large data field */
-
-typedef struct {
-        unsigned char  opp_flag PACKED; /* the opp flag */
-        unsigned char  command  PACKED; /* command code */
-        unsigned short length   PACKED; /* length of data buffer */
-        unsigned char  result   PACKED; /* return code */
-        unsigned short dlci     PACKED; /* DLCI number */
-        unsigned char  attr     PACKED; /* FECN, BECN, DE and C/R bits */
-        unsigned short rxlost1  PACKED; /* frames discarded at int. level */
-        unsigned long  rxlost2  PACKED; /* frames discarded at app. level */
-        unsigned char  rsrv[2]  PACKED; /* reserved for future use */
-} cblock_t;
-
-
-/* UDP management packet layout (data area of ip packet) */
-
-typedef struct {
-        unsigned char   control                 PACKED;
-        unsigned char   NLPID                   PACKED;
-} fr_encap_hdr_t;
-
-typedef struct {
-//	fr_encap_hdr_t 		fr_encap_hdr	PACKED;
-	ip_pkt_t 		ip_pkt		PACKED;
-	udp_pkt_t		udp_pkt		PACKED;
-	wp_mgmt_t 		wp_mgmt       	PACKED;
-        cblock_t                cblock          PACKED;
-        unsigned char           data[4080]      PACKED;
-} fr_udp_pkt_t;
-
-
-/* valid ip_protocol for UDP management */
-#define UDPMGMT_UDP_PROTOCOL 0x11
-
-#define UDPMGMT_FPIPE_SIGNATURE         "FPIPE8ND"
-#define UDPMGMT_DRVRSTATS_SIGNATURE     "DRVSTATS"
-
-/* values for request/reply byte */
-#define UDPMGMT_REQUEST	0x01
-#define UDPMGMT_REPLY	0x02
-#define UDP_OFFSET	12
-
-typedef struct {
-        unsigned long if_send_entry;
-        unsigned long if_send_skb_null;
-        unsigned long if_send_broadcast;
-        unsigned long if_send_multicast;
-        unsigned long if_send_critical_ISR;
-        unsigned long if_send_critical_non_ISR;
-        unsigned long if_send_busy;
-        unsigned long if_send_busy_timeout;
-	unsigned long if_send_DRVSTATS_request;
-        unsigned long if_send_FPIPE_request;
-        unsigned long if_send_wan_disconnected;
-        unsigned long if_send_dlci_disconnected;
-        unsigned long if_send_no_bfrs;
-        unsigned long if_send_adptr_bfrs_full;
-        unsigned long if_send_bfrs_passed_to_adptr;
-	unsigned long if_send_consec_send_fail;
-} drvstats_if_send_t; 
-
-typedef struct {
-        unsigned long rx_intr_no_socket;
-        unsigned long rx_intr_dev_not_started;
-        unsigned long rx_intr_DRVSTATS_request;
-        unsigned long rx_intr_FPIPE_request;
-        unsigned long rx_intr_bfr_not_passed_to_stack;
-        unsigned long rx_intr_bfr_passed_to_stack;
- } drvstats_rx_intr_t;
-
-typedef struct {
-        unsigned long UDP_FPIPE_mgmt_kmalloc_err;
-        unsigned long UDP_FPIPE_mgmt_direction_err;
-        unsigned long UDP_FPIPE_mgmt_adptr_type_err;
-        unsigned long UDP_FPIPE_mgmt_adptr_cmnd_OK;
-        unsigned long UDP_FPIPE_mgmt_adptr_cmnd_timeout;
-        unsigned long UDP_FPIPE_mgmt_adptr_send_passed;
-        unsigned long UDP_FPIPE_mgmt_adptr_send_failed;
-        unsigned long UDP_FPIPE_mgmt_not_passed_to_stack;
-        unsigned long UDP_FPIPE_mgmt_passed_to_stack;
-        unsigned long UDP_FPIPE_mgmt_no_socket;
-        unsigned long UDP_DRVSTATS_mgmt_kmalloc_err;
-        unsigned long UDP_DRVSTATS_mgmt_adptr_cmnd_OK;
-        unsigned long UDP_DRVSTATS_mgmt_adptr_cmnd_timeout;
-        unsigned long UDP_DRVSTATS_mgmt_adptr_send_passed;
-        unsigned long UDP_DRVSTATS_mgmt_adptr_send_failed;
-        unsigned long UDP_DRVSTATS_mgmt_not_passed_to_stack;
-        unsigned long UDP_DRVSTATS_mgmt_passed_to_stack;
-        unsigned long UDP_DRVSTATS_mgmt_no_socket;
-} drvstats_gen_t;
-
-typedef struct {
-        unsigned char   attr      	PACKED;
-        unsigned short  time_stamp      PACKED;
-        unsigned char   reserved[13]    PACKED;
-} api_rx_hdr_t;
-
-typedef struct {
-        api_rx_hdr_t    api_rx_hdr      PACKED;
-        void *          data            PACKED;
-} api_rx_element_t;
-
-typedef struct {
-        unsigned char   attr            PACKED;
-        unsigned char   reserved[15]    PACKED;
-} api_tx_hdr_t;
-
-typedef struct {
-        api_tx_hdr_t    api_tx_hdr      PACKED;
-        void *          data            PACKED;
-} api_tx_element_t;
-
-#ifdef		_MSC_
-#  pragma	pack()
-#endif
-#endif	/* _SDLA_FR_H */
-
diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h
index 52c9eb9..26e4925 100644
--- a/include/linux/seqlock.h
+++ b/include/linux/seqlock.h
@@ -61,10 +61,10 @@
 {
 	spin_lock(&sl->lock);
 	++sl->sequence;
-	smp_wmb();			
-}	
+	smp_wmb();
+}
 
-static inline void write_sequnlock(seqlock_t *sl) 
+static inline void write_sequnlock(seqlock_t *sl)
 {
 	smp_wmb();
 	sl->sequence++;
@@ -77,7 +77,7 @@
 
 	if (ret) {
 		++sl->sequence;
-		smp_wmb();			
+		smp_wmb();
 	}
 	return ret;
 }
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 5992f65..253a2b9 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -27,20 +27,24 @@
 #include <net/checksum.h>
 #include <linux/rcupdate.h>
 #include <linux/dmaengine.h>
+#include <linux/hrtimer.h>
 
 #define HAVE_ALLOC_SKB		/* For the drivers to know */
 #define HAVE_ALIGNABLE_SKB	/* Ditto 8)		   */
 
+/* Don't change this without changing skb_csum_unnecessary! */
 #define CHECKSUM_NONE 0
-#define CHECKSUM_PARTIAL 1
-#define CHECKSUM_UNNECESSARY 2
-#define CHECKSUM_COMPLETE 3
+#define CHECKSUM_UNNECESSARY 1
+#define CHECKSUM_COMPLETE 2
+#define CHECKSUM_PARTIAL 3
 
 #define SKB_DATA_ALIGN(X)	(((X) + (SMP_CACHE_BYTES - 1)) & \
 				 ~(SMP_CACHE_BYTES - 1))
-#define SKB_MAX_ORDER(X, ORDER)	(((PAGE_SIZE << (ORDER)) - (X) - \
-				  sizeof(struct skb_shared_info)) & \
-				  ~(SMP_CACHE_BYTES - 1))
+#define SKB_WITH_OVERHEAD(X)	\
+	(((X) - sizeof(struct skb_shared_info)) & \
+	 ~(SMP_CACHE_BYTES - 1))
+#define SKB_MAX_ORDER(X, ORDER) \
+	SKB_WITH_OVERHEAD((PAGE_SIZE << (ORDER)) - (X))
 #define SKB_MAX_HEAD(X)		(SKB_MAX_ORDER((X), 0))
 #define SKB_MAX_ALLOC		(SKB_MAX_ORDER(0, 2))
 
@@ -66,8 +70,8 @@
  *	NONE: skb is checksummed by protocol or csum is not required.
  *
  *	PARTIAL: device is required to csum packet as seen by hard_start_xmit
- *	from skb->h.raw to the end and to record the checksum
- *	at skb->h.raw+skb->csum.
+ *	from skb->transport_header to the end and to record the checksum
+ *	at skb->transport_header + skb->csum.
  *
  *	Device must show its capabilities in dev->features, set
  *	at device setup time.
@@ -83,12 +87,13 @@
  */
 
 struct net_device;
+struct scatterlist;
 
-#ifdef CONFIG_NETFILTER
+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
 struct nf_conntrack {
 	atomic_t use;
-	void (*destroy)(struct nf_conntrack *);
 };
+#endif
 
 #ifdef CONFIG_BRIDGE_NETFILTER
 struct nf_bridge_info {
@@ -103,8 +108,6 @@
 };
 #endif
 
-#endif
-
 struct sk_buff_head {
 	/* These two members must be first. */
 	struct sk_buff	*next;
@@ -156,11 +159,6 @@
 #define SKB_DATAREF_SHIFT 16
 #define SKB_DATAREF_MASK ((1 << SKB_DATAREF_SHIFT) - 1)
 
-struct skb_timeval {
-	u32	off_sec;
-	u32	off_usec;
-};
-
 
 enum {
 	SKB_FCLONE_UNAVAILABLE,
@@ -181,6 +179,16 @@
 	SKB_GSO_TCPV6 = 1 << 4,
 };
 
+#if BITS_PER_LONG > 32
+#define NET_SKBUFF_DATA_USES_OFFSET 1
+#endif
+
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+typedef unsigned int sk_buff_data_t;
+#else
+typedef unsigned char *sk_buff_data_t;
+#endif
+
 /** 
  *	struct sk_buff - socket buffer
  *	@next: Next buffer in list
@@ -190,15 +198,17 @@
  *	@dev: Device we arrived on/are leaving by
  *	@iif: ifindex of device we arrived on
  *	@h: Transport layer header
- *	@nh: Network layer header
- *	@mac: Link layer header
+ *	@network_header: Network layer header
+ *	@mac_header: Link layer header
  *	@dst: destination entry
  *	@sp: the security path, used for xfrm
  *	@cb: Control buffer. Free for use by every layer. Put private vars here
  *	@len: Length of actual data
  *	@data_len: Data length
  *	@mac_len: Length of link layer header
- *	@csum: Checksum
+ *	@csum: Checksum (must include start/offset pair)
+ *	@csum_start: Offset from skb->head where checksumming should start
+ *	@csum_offset: Offset from csum_start where checksum should be stored
  *	@local_df: allow local fragmentation
  *	@cloned: Head may be cloned (check refcnt to be sure)
  *	@nohdr: Payload reference only, must not modify header
@@ -233,32 +243,11 @@
 	struct sk_buff		*prev;
 
 	struct sock		*sk;
-	struct skb_timeval	tstamp;
+	ktime_t			tstamp;
 	struct net_device	*dev;
 	int			iif;
 	/* 4 byte hole on 64 bit*/
 
-	union {
-		struct tcphdr	*th;
-		struct udphdr	*uh;
-		struct icmphdr	*icmph;
-		struct igmphdr	*igmph;
-		struct iphdr	*ipiph;
-		struct ipv6hdr	*ipv6h;
-		unsigned char	*raw;
-	} h;
-
-	union {
-		struct iphdr	*iph;
-		struct ipv6hdr	*ipv6h;
-		struct arphdr	*arph;
-		unsigned char	*raw;
-	} nh;
-
-	union {
-	  	unsigned char 	*raw;
-	} mac;
-
 	struct  dst_entry	*dst;
 	struct	sec_path	*sp;
 
@@ -275,7 +264,10 @@
 				mac_len;
 	union {
 		__wsum		csum;
-		__u32		csum_offset;
+		struct {
+			__u16	csum_start;
+			__u16	csum_offset;
+		};
 	};
 	__u32			priority;
 	__u8			local_df:1,
@@ -289,15 +281,13 @@
 	__be16			protocol;
 
 	void			(*destructor)(struct sk_buff *skb);
-#ifdef CONFIG_NETFILTER
-	struct nf_conntrack	*nfct;
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+	struct nf_conntrack	*nfct;
 	struct sk_buff		*nfct_reasm;
 #endif
 #ifdef CONFIG_BRIDGE_NETFILTER
 	struct nf_bridge_info	*nf_bridge;
 #endif
-#endif /* CONFIG_NETFILTER */
 #ifdef CONFIG_NET_SCHED
 	__u16			tc_index;	/* traffic control index */
 #ifdef CONFIG_NET_CLS_ACT
@@ -313,13 +303,16 @@
 
 	__u32			mark;
 
+	sk_buff_data_t		transport_header;
+	sk_buff_data_t		network_header;
+	sk_buff_data_t		mac_header;
 	/* These elements must be at the end, see alloc_skb() for details.  */
+	sk_buff_data_t		tail;
+	sk_buff_data_t		end;
+	unsigned char		*head,
+				*data;
 	unsigned int		truesize;
 	atomic_t		users;
-	unsigned char		*head,
-				*data,
-				*tail,
-				*end;
 };
 
 #ifdef __KERNEL__
@@ -361,6 +354,11 @@
 extern struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
 				       int newheadroom, int newtailroom,
 				       gfp_t priority);
+extern int	       skb_to_sgvec(struct sk_buff *skb,
+				    struct scatterlist *sg, int offset,
+				    int len);
+extern int	       skb_cow_data(struct sk_buff *skb, int tailbits,
+				    struct sk_buff **trailer);
 extern int	       skb_pad(struct sk_buff *skb, int pad);
 #define dev_kfree_skb(a)	kfree_skb(a)
 extern void	      skb_over_panic(struct sk_buff *skb, int len,
@@ -402,8 +400,20 @@
 				    unsigned int to, struct ts_config *config,
 				    struct ts_state *state);
 
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+static inline unsigned char *skb_end_pointer(const struct sk_buff *skb)
+{
+	return skb->head + skb->end;
+}
+#else
+static inline unsigned char *skb_end_pointer(const struct sk_buff *skb)
+{
+	return skb->end;
+}
+#endif
+
 /* Internal */
-#define skb_shinfo(SKB)		((struct skb_shared_info *)((SKB)->end))
+#define skb_shinfo(SKB)	((struct skb_shared_info *)(skb_end_pointer(SKB)))
 
 /**
  *	skb_queue_empty - check if a queue is empty
@@ -822,12 +832,46 @@
 #define SKB_FRAG_ASSERT(skb) 	BUG_ON(skb_shinfo(skb)->frag_list)
 #define SKB_LINEAR_ASSERT(skb)  BUG_ON(skb_is_nonlinear(skb))
 
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb)
+{
+	return skb->head + skb->tail;
+}
+
+static inline void skb_reset_tail_pointer(struct sk_buff *skb)
+{
+	skb->tail = skb->data - skb->head;
+}
+
+static inline void skb_set_tail_pointer(struct sk_buff *skb, const int offset)
+{
+	skb_reset_tail_pointer(skb);
+	skb->tail += offset;
+}
+#else /* NET_SKBUFF_DATA_USES_OFFSET */
+static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb)
+{
+	return skb->tail;
+}
+
+static inline void skb_reset_tail_pointer(struct sk_buff *skb)
+{
+	skb->tail = skb->data;
+}
+
+static inline void skb_set_tail_pointer(struct sk_buff *skb, const int offset)
+{
+	skb->tail = skb->data + offset;
+}
+
+#endif /* NET_SKBUFF_DATA_USES_OFFSET */
+
 /*
  *	Add data to an sk_buff
  */
 static inline unsigned char *__skb_put(struct sk_buff *skb, unsigned int len)
 {
-	unsigned char *tmp = skb->tail;
+	unsigned char *tmp = skb_tail_pointer(skb);
 	SKB_LINEAR_ASSERT(skb);
 	skb->tail += len;
 	skb->len  += len;
@@ -845,11 +889,11 @@
  */
 static inline unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
 {
-	unsigned char *tmp = skb->tail;
+	unsigned char *tmp = skb_tail_pointer(skb);
 	SKB_LINEAR_ASSERT(skb);
 	skb->tail += len;
 	skb->len  += len;
-	if (unlikely(skb->tail>skb->end))
+	if (unlikely(skb->tail > skb->end))
 		skb_over_panic(skb, len, current_text_addr());
 	return tmp;
 }
@@ -962,6 +1006,130 @@
 	skb->tail += len;
 }
 
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
+{
+	return skb->head + skb->transport_header;
+}
+
+static inline void skb_reset_transport_header(struct sk_buff *skb)
+{
+	skb->transport_header = skb->data - skb->head;
+}
+
+static inline void skb_set_transport_header(struct sk_buff *skb,
+					    const int offset)
+{
+	skb_reset_transport_header(skb);
+	skb->transport_header += offset;
+}
+
+static inline unsigned char *skb_network_header(const struct sk_buff *skb)
+{
+	return skb->head + skb->network_header;
+}
+
+static inline void skb_reset_network_header(struct sk_buff *skb)
+{
+	skb->network_header = skb->data - skb->head;
+}
+
+static inline void skb_set_network_header(struct sk_buff *skb, const int offset)
+{
+	skb_reset_network_header(skb);
+	skb->network_header += offset;
+}
+
+static inline unsigned char *skb_mac_header(const struct sk_buff *skb)
+{
+	return skb->head + skb->mac_header;
+}
+
+static inline int skb_mac_header_was_set(const struct sk_buff *skb)
+{
+	return skb->mac_header != ~0U;
+}
+
+static inline void skb_reset_mac_header(struct sk_buff *skb)
+{
+	skb->mac_header = skb->data - skb->head;
+}
+
+static inline void skb_set_mac_header(struct sk_buff *skb, const int offset)
+{
+	skb_reset_mac_header(skb);
+	skb->mac_header += offset;
+}
+
+#else /* NET_SKBUFF_DATA_USES_OFFSET */
+
+static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
+{
+	return skb->transport_header;
+}
+
+static inline void skb_reset_transport_header(struct sk_buff *skb)
+{
+	skb->transport_header = skb->data;
+}
+
+static inline void skb_set_transport_header(struct sk_buff *skb,
+					    const int offset)
+{
+	skb->transport_header = skb->data + offset;
+}
+
+static inline unsigned char *skb_network_header(const struct sk_buff *skb)
+{
+	return skb->network_header;
+}
+
+static inline void skb_reset_network_header(struct sk_buff *skb)
+{
+	skb->network_header = skb->data;
+}
+
+static inline void skb_set_network_header(struct sk_buff *skb, const int offset)
+{
+	skb->network_header = skb->data + offset;
+}
+
+static inline unsigned char *skb_mac_header(const struct sk_buff *skb)
+{
+	return skb->mac_header;
+}
+
+static inline int skb_mac_header_was_set(const struct sk_buff *skb)
+{
+	return skb->mac_header != NULL;
+}
+
+static inline void skb_reset_mac_header(struct sk_buff *skb)
+{
+	skb->mac_header = skb->data;
+}
+
+static inline void skb_set_mac_header(struct sk_buff *skb, const int offset)
+{
+	skb->mac_header = skb->data + offset;
+}
+#endif /* NET_SKBUFF_DATA_USES_OFFSET */
+
+static inline int skb_transport_offset(const struct sk_buff *skb)
+{
+	return skb_transport_header(skb) - skb->data;
+}
+
+static inline u32 skb_network_header_len(const struct sk_buff *skb)
+{
+	return skb->transport_header - skb->network_header;
+}
+
+static inline int skb_network_offset(const struct sk_buff *skb)
+{
+	return skb_network_header(skb) - skb->data;
+}
+
 /*
  * CPUs often take a performance hit when accessing unaligned memory
  * locations. The actual performance hit varies, it can be small if the
@@ -1013,8 +1181,8 @@
 		WARN_ON(1);
 		return;
 	}
-	skb->len  = len;
-	skb->tail = skb->data + len;
+	skb->len = len;
+	skb_set_tail_pointer(skb, len);
 }
 
 /**
@@ -1303,6 +1471,11 @@
 		     prefetch(skb->next), (skb != (struct sk_buff *)(queue));	\
 		     skb = skb->next)
 
+#define skb_queue_walk_safe(queue, skb, tmp)					\
+		for (skb = (queue)->next, tmp = skb->next;			\
+		     skb != (struct sk_buff *)(queue);				\
+		     skb = tmp, tmp = skb->next)
+
 #define skb_queue_reverse_walk(queue, skb) \
 		for (skb = (queue)->prev;					\
 		     prefetch(skb->prev), (skb != (struct sk_buff *)(queue));	\
@@ -1326,8 +1499,8 @@
 				    int len, __wsum csum);
 extern int	       skb_copy_bits(const struct sk_buff *skb, int offset,
 				     void *to, int len);
-extern int	       skb_store_bits(const struct sk_buff *skb, int offset,
-				      void *from, int len);
+extern int	       skb_store_bits(struct sk_buff *skb, int offset,
+				      const void *from, int len);
 extern __wsum	       skb_copy_and_csum_bits(const struct sk_buff *skb,
 					      int offset, u8 *to, int len,
 					      __wsum csum);
@@ -1351,8 +1524,36 @@
 	return buffer;
 }
 
+static inline void skb_copy_from_linear_data(const struct sk_buff *skb,
+					     void *to,
+					     const unsigned int len)
+{
+	memcpy(to, skb->data, len);
+}
+
+static inline void skb_copy_from_linear_data_offset(const struct sk_buff *skb,
+						    const int offset, void *to,
+						    const unsigned int len)
+{
+	memcpy(to, skb->data + offset, len);
+}
+
+static inline void skb_copy_to_linear_data(struct sk_buff *skb,
+					   const void *from,
+					   const unsigned int len)
+{
+	memcpy(skb->data, from, len);
+}
+
+static inline void skb_copy_to_linear_data_offset(struct sk_buff *skb,
+						  const int offset,
+						  const void *from,
+						  const unsigned int len)
+{
+	memcpy(skb->data + offset, from, len);
+}
+
 extern void skb_init(void);
-extern void skb_add_mtu(int mtu);
 
 /**
  *	skb_get_timestamp - get timestamp from a skb
@@ -1365,29 +1566,28 @@
  */
 static inline void skb_get_timestamp(const struct sk_buff *skb, struct timeval *stamp)
 {
-	stamp->tv_sec  = skb->tstamp.off_sec;
-	stamp->tv_usec = skb->tstamp.off_usec;
+	*stamp = ktime_to_timeval(skb->tstamp);
 }
 
-/**
- * 	skb_set_timestamp - set timestamp of a skb
- *	@skb: skb to set stamp of
- *	@stamp: pointer to struct timeval to get stamp from
- *
- *	Timestamps are stored in the skb as offsets to a base timestamp.
- *	This function converts a struct timeval to an offset and stores
- *	it in the skb.
- */
-static inline void skb_set_timestamp(struct sk_buff *skb, const struct timeval *stamp)
+static inline void __net_timestamp(struct sk_buff *skb)
 {
-	skb->tstamp.off_sec  = stamp->tv_sec;
-	skb->tstamp.off_usec = stamp->tv_usec;
+	skb->tstamp = ktime_get_real();
 }
 
-extern void __net_timestamp(struct sk_buff *skb);
+static inline ktime_t net_timedelta(ktime_t t)
+{
+	return ktime_sub(ktime_get_real(), t);
+}
 
+
+extern __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len);
 extern __sum16 __skb_checksum_complete(struct sk_buff *skb);
 
+static inline int skb_csum_unnecessary(const struct sk_buff *skb)
+{
+	return skb->ip_summed & CHECKSUM_UNNECESSARY;
+}
+
 /**
  *	skb_checksum_complete - Calculate checksum of an entire packet
  *	@skb: packet to process
@@ -1406,22 +1606,22 @@
  */
 static inline unsigned int skb_checksum_complete(struct sk_buff *skb)
 {
-	return skb->ip_summed != CHECKSUM_UNNECESSARY &&
-		__skb_checksum_complete(skb);
+	return skb_csum_unnecessary(skb) ?
+	       0 : __skb_checksum_complete(skb);
 }
 
-#ifdef CONFIG_NETFILTER
+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+extern void nf_conntrack_destroy(struct nf_conntrack *nfct);
 static inline void nf_conntrack_put(struct nf_conntrack *nfct)
 {
 	if (nfct && atomic_dec_and_test(&nfct->use))
-		nfct->destroy(nfct);
+		nf_conntrack_destroy(nfct);
 }
 static inline void nf_conntrack_get(struct nf_conntrack *nfct)
 {
 	if (nfct)
 		atomic_inc(&nfct->use);
 }
-#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
 static inline void nf_conntrack_get_reasm(struct sk_buff *skb)
 {
 	if (skb)
@@ -1447,9 +1647,9 @@
 #endif /* CONFIG_BRIDGE_NETFILTER */
 static inline void nf_reset(struct sk_buff *skb)
 {
+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
 	nf_conntrack_put(skb->nfct);
 	skb->nfct = NULL;
-#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
 	nf_conntrack_put_reasm(skb->nfct_reasm);
 	skb->nfct_reasm = NULL;
 #endif
@@ -1459,9 +1659,33 @@
 #endif
 }
 
-#else /* CONFIG_NETFILTER */
-static inline void nf_reset(struct sk_buff *skb) {}
-#endif /* CONFIG_NETFILTER */
+/* Note: This doesn't put any conntrack and bridge info in dst. */
+static inline void __nf_copy(struct sk_buff *dst, const struct sk_buff *src)
+{
+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+	dst->nfct = src->nfct;
+	nf_conntrack_get(src->nfct);
+	dst->nfctinfo = src->nfctinfo;
+	dst->nfct_reasm = src->nfct_reasm;
+	nf_conntrack_get_reasm(src->nfct_reasm);
+#endif
+#ifdef CONFIG_BRIDGE_NETFILTER
+	dst->nf_bridge  = src->nf_bridge;
+	nf_bridge_get(src->nf_bridge);
+#endif
+}
+
+static inline void nf_copy(struct sk_buff *dst, const struct sk_buff *src)
+{
+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+	nf_conntrack_put(dst->nfct);
+	nf_conntrack_put_reasm(dst->nfct_reasm);
+#endif
+#ifdef CONFIG_BRIDGE_NETFILTER
+	nf_bridge_put(dst->nf_bridge);
+#endif
+	__nf_copy(dst, src);
+}
 
 #ifdef CONFIG_NETWORK_SECMARK
 static inline void skb_copy_secmark(struct sk_buff *to, const struct sk_buff *from)
@@ -1486,5 +1710,12 @@
 	return skb_shinfo(skb)->gso_size;
 }
 
+static inline void skb_forward_csum(struct sk_buff *skb)
+{
+	/* Unfortunately we don't support this one.  Any brave souls? */
+	if (skb->ip_summed == CHECKSUM_COMPLETE)
+		skb->ip_summed = CHECKSUM_NONE;
+}
+
 #endif	/* __KERNEL__ */
 #endif	/* _LINUX_SKBUFF_H */
diff --git a/include/linux/snmp.h b/include/linux/snmp.h
index 854aa6b..802b3a3 100644
--- a/include/linux/snmp.h
+++ b/include/linux/snmp.h
@@ -40,6 +40,8 @@
 	IPSTATS_MIB_FRAGCREATES,		/* FragCreates */
 	IPSTATS_MIB_INMCASTPKTS,		/* InMcastPkts */
 	IPSTATS_MIB_OUTMCASTPKTS,		/* OutMcastPkts */
+	IPSTATS_MIB_INBCASTPKTS,		/* InBcastPkts */
+	IPSTATS_MIB_OUTBCASTPKTS,		/* OutBcastPkts */
 	__IPSTATS_MIB_MAX
 };
 
diff --git a/include/linux/socket.h b/include/linux/socket.h
index fcd35a2..6e7c948 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -188,7 +188,8 @@
 #define AF_TIPC		30	/* TIPC sockets			*/
 #define AF_BLUETOOTH	31	/* Bluetooth sockets 		*/
 #define AF_IUCV		32	/* IUCV sockets			*/
-#define AF_MAX		33	/* For now.. */
+#define AF_RXRPC	33	/* RxRPC sockets 		*/
+#define AF_MAX		34	/* For now.. */
 
 /* Protocol families, same as address families. */
 #define PF_UNSPEC	AF_UNSPEC
@@ -222,6 +223,7 @@
 #define PF_TIPC		AF_TIPC
 #define PF_BLUETOOTH	AF_BLUETOOTH
 #define PF_IUCV		AF_IUCV
+#define PF_RXRPC	AF_RXRPC
 #define PF_MAX		AF_MAX
 
 /* Maximum queue length specifiable by listen.  */
@@ -284,6 +286,7 @@
 #define SOL_DCCP	269
 #define SOL_NETLINK	270
 #define SOL_TIPC	271
+#define SOL_RXRPC	272
 
 /* IPX options */
 #define IPX_TYPE	1
diff --git a/include/linux/sony-laptop.h b/include/linux/sony-laptop.h
new file mode 100644
index 0000000..e2e036d
--- /dev/null
+++ b/include/linux/sony-laptop.h
@@ -0,0 +1,34 @@
+#ifndef _SONYLAPTOP_H_
+#define _SONYLAPTOP_H_
+
+#include <linux/types.h>
+
+#ifdef __KERNEL__
+
+/* used only for communication between v4l and sony-laptop */
+
+#define SONY_PIC_COMMAND_GETCAMERA		 1	/* obsolete */
+#define SONY_PIC_COMMAND_SETCAMERA		 2
+#define SONY_PIC_COMMAND_GETCAMERABRIGHTNESS	 3	/* obsolete */
+#define SONY_PIC_COMMAND_SETCAMERABRIGHTNESS	 4
+#define SONY_PIC_COMMAND_GETCAMERACONTRAST	 5	/* obsolete */
+#define SONY_PIC_COMMAND_SETCAMERACONTRAST	 6
+#define SONY_PIC_COMMAND_GETCAMERAHUE		 7	/* obsolete */
+#define SONY_PIC_COMMAND_SETCAMERAHUE		 8
+#define SONY_PIC_COMMAND_GETCAMERACOLOR		 9	/* obsolete */
+#define SONY_PIC_COMMAND_SETCAMERACOLOR		10
+#define SONY_PIC_COMMAND_GETCAMERASHARPNESS	11	/* obsolete */
+#define SONY_PIC_COMMAND_SETCAMERASHARPNESS	12
+#define SONY_PIC_COMMAND_GETCAMERAPICTURE	13	/* obsolete */
+#define SONY_PIC_COMMAND_SETCAMERAPICTURE	14
+#define SONY_PIC_COMMAND_GETCAMERAAGC		15	/* obsolete */
+#define SONY_PIC_COMMAND_SETCAMERAAGC		16
+#define SONY_PIC_COMMAND_GETCAMERADIRECTION	17	/* obsolete */
+#define SONY_PIC_COMMAND_GETCAMERAROMVERSION	18	/* obsolete */
+#define SONY_PIC_COMMAND_GETCAMERAREVISION	19	/* obsolete */
+
+int sony_pic_camera_command(int command, u8 value);
+
+#endif	/* __KERNEL__ */
+
+#endif /* _SONYLAPTOP_H_ */
diff --git a/include/linux/string.h b/include/linux/string.h
index 4f69ef9..7f2eb6a 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -47,6 +47,12 @@
 #ifndef __HAVE_ARCH_STRNICMP
 extern int strnicmp(const char *, const char *, __kernel_size_t);
 #endif
+#ifndef __HAVE_ARCH_STRCASECMP
+extern int strcasecmp(const char *s1, const char *s2);
+#endif
+#ifndef __HAVE_ARCH_STRNCASECMP
+extern int strncasecmp(const char *s1, const char *s2, size_t n);
+#endif
 #ifndef __HAVE_ARCH_STRCHR
 extern char * strchr(const char *,int);
 #endif
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 9a8970b..47f1c53 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -290,6 +290,7 @@
 	NET_CORE_BUDGET=19,
 	NET_CORE_AEVENT_ETIME=20,
 	NET_CORE_AEVENT_RSEQTH=21,
+	NET_CORE_WARNINGS=22,
 };
 
 /* /proc/sys/net/ethernet */
@@ -438,6 +439,8 @@
 	NET_CIPSOV4_RBM_STRICTVALID=121,
 	NET_TCP_AVAIL_CONG_CONTROL=122,
 	NET_TCP_ALLOWED_CONG_CONTROL=123,
+	NET_TCP_MAX_SSTHRESH=124,
+	NET_TCP_FRTO_RESPONSE=125,
 };
 
 enum {
@@ -789,6 +792,7 @@
 	NET_BRIDGE_NF_CALL_IPTABLES = 2,
 	NET_BRIDGE_NF_CALL_IP6TABLES = 3,
 	NET_BRIDGE_NF_FILTER_VLAN_TAGGED = 4,
+	NET_BRIDGE_NF_FILTER_PPPOE_TAGGED = 5,
 };
 
 /* CTL_FS names: */
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h
index fea9a6b..7d5d1ec 100644
--- a/include/linux/sysfs.h
+++ b/include/linux/sysfs.h
@@ -80,7 +80,7 @@
 #ifdef CONFIG_SYSFS
 
 extern int sysfs_schedule_callback(struct kobject *kobj,
-		void (*func)(void *), void *data);
+		void (*func)(void *), void *data, struct module *owner);
 
 extern int __must_check
 sysfs_create_dir(struct kobject *, struct dentry *);
@@ -137,7 +137,7 @@
 #else /* CONFIG_SYSFS */
 
 static inline int sysfs_schedule_callback(struct kobject *kobj,
-		void (*func)(void *), void *data)
+		void (*func)(void *), void *data, struct module *owner)
 {
 	return -ENOSYS;
 }
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 29d3089..c6b9f92 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -178,6 +178,21 @@
 #include <net/inet_connection_sock.h>
 #include <net/inet_timewait_sock.h>
 
+static inline struct tcphdr *tcp_hdr(const struct sk_buff *skb)
+{
+	return (struct tcphdr *)skb_transport_header(skb);
+}
+
+static inline unsigned int tcp_hdrlen(const struct sk_buff *skb)
+{
+	return tcp_hdr(skb)->doff * 4;
+}
+
+static inline unsigned int tcp_optlen(const struct sk_buff *skb)
+{
+	return (tcp_hdr(skb)->doff - 5) * 4;
+}
+
 /* This defines a selective acknowledgement block. */
 struct tcp_sack_block_wire {
 	__be32	start_seq;
@@ -242,6 +257,8 @@
  *	See RFC793 and RFC1122. The RFC writes these in capitals.
  */
  	u32	rcv_nxt;	/* What we want to receive next 	*/
+	u32	copied_seq;	/* Head of yet unread data		*/
+	u32	rcv_wup;	/* rcv_nxt on last window update sent	*/
  	u32	snd_nxt;	/* Next sequence we send		*/
 
  	u32	snd_una;	/* First byte we want an ack for	*/
@@ -300,17 +317,15 @@
  	u32	snd_ssthresh;	/* Slow start size threshold		*/
  	u32	snd_cwnd;	/* Sending congestion window		*/
  	u16	snd_cwnd_cnt;	/* Linear increase counter		*/
-	u16	snd_cwnd_clamp; /* Do not allow snd_cwnd to grow above this */
+	u32	snd_cwnd_clamp; /* Do not allow snd_cwnd to grow above this */
 	u32	snd_cwnd_used;
 	u32	snd_cwnd_stamp;
 
 	struct sk_buff_head	out_of_order_queue; /* Out of order segments go here */
 
  	u32	rcv_wnd;	/* Current receiver window		*/
-	u32	rcv_wup;	/* rcv_nxt on last window update sent	*/
 	u32	write_seq;	/* Tail(+1) of data held in tcp send buffer */
 	u32	pushed_seq;	/* Last pushed seq, required to talk to windows */
-	u32	copied_seq;	/* Head of yet unread data		*/
 
 /*	SACKs data	*/
 	struct tcp_sack_block duplicate_sack[1]; /* D-SACK block */
diff --git a/include/linux/udp.h b/include/linux/udp.h
index 7e08c07..6de445c 100644
--- a/include/linux/udp.h
+++ b/include/linux/udp.h
@@ -26,6 +26,15 @@
 	__sum16	check;
 };
 
+#ifdef __KERNEL__
+#include <linux/skbuff.h>
+
+static inline struct udphdr *udp_hdr(const struct sk_buff *skb)
+{
+	return (struct udphdr *)skb_transport_header(skb);
+}
+#endif
+
 /* UDP socket options */
 #define UDP_CORK	1	/* Never send partially complete segments */
 #define UDP_ENCAP	100	/* Set the socket to accept encapsulated packets */
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 87dc75a..cfbd2bb 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -299,8 +299,9 @@
 	int bandwidth_int_reqs;		/* number of Interrupt requests */
 	int bandwidth_isoc_reqs;	/* number of Isoc. requests */
 
+#ifdef CONFIG_USB_DEVICEFS
 	struct dentry *usbfs_dentry;	/* usbfs dentry entry for the bus */
-
+#endif
 	struct class_device *class_dev;	/* class device for this bus */
 
 #if defined(CONFIG_USB_MON)
@@ -373,9 +374,12 @@
 	char *serial;			/* iSerialNumber string, if present */
 
 	struct list_head filelist;
-	struct device *usbfs_dev;
+#ifdef CONFIG_USB_DEVICE_CLASS
+	struct device *usb_classdev;
+#endif
+#ifdef CONFIG_USB_DEVICEFS
 	struct dentry *usbfs_dentry;	/* usbfs dentry entry for the device */
-
+#endif
 	/*
 	 * Child devices - these can be either new devices
 	 * (if this is a hub device), or different instances
@@ -394,10 +398,13 @@
 	struct delayed_work autosuspend; /* for delayed autosuspends */
 	struct mutex pm_mutex;		/* protects PM operations */
 
-	unsigned autosuspend_delay;	/* in jiffies */
+	unsigned long last_busy;	/* time of last use */
+	int autosuspend_delay;		/* in jiffies */
 
 	unsigned auto_pm:1;		/* autosuspend/resume in progress */
 	unsigned do_remote_wakeup:1;	/* remote wakeup should be enabled */
+	unsigned autosuspend_disabled:1; /* autosuspend and autoresume */
+	unsigned autoresume_disabled:1;  /*  disabled by the user */
 #endif
 };
 #define	to_usb_device(d) container_of(d, struct usb_device, dev)
@@ -437,6 +444,11 @@
 	usb_autopm_set_interface(intf);
 }
 
+static inline void usb_mark_last_busy(struct usb_device *udev)
+{
+	udev->last_busy = jiffies;
+}
+
 #else
 
 static inline int usb_autopm_set_interface(struct usb_interface *intf)
@@ -451,6 +463,8 @@
 { }
 static inline void usb_autopm_disable(struct usb_interface *intf)
 { }
+static inline void usb_mark_last_busy(struct usb_device *udev)
+{ }
 #endif
 
 /*-------------------------------------------------------------------------*/
diff --git a/include/linux/usb/cdc.h b/include/linux/usb/cdc.h
index 956edf3..2204ae2 100644
--- a/include/linux/usb/cdc.h
+++ b/include/linux/usb/cdc.h
@@ -91,6 +91,17 @@
 	/* ... and there could be other slave interfaces */
 } __attribute__ ((packed));
 
+/* "Country Selection Functional Descriptor" from CDC spec 5.2.3.9 */
+struct usb_cdc_country_functional_desc {
+	__u8	bLength;
+	__u8	bDescriptorType;
+	__u8	bDescriptorSubType;
+
+	__u8	iCountryCodeRelDate;
+	__le16	wCountyCode0;
+	/* ... and there can be a lot of country codes */
+} __attribute__ ((packed));
+
 /* "Network Channel Terminal Functional Descriptor" from CDC spec 5.2.3.11 */
 struct usb_cdc_network_terminal_desc {
 	__u8	bLength;
diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h
index 1122a6c..6169438 100644
--- a/include/linux/usb/ch9.h
+++ b/include/linux/usb/ch9.h
@@ -181,12 +181,15 @@
 #define USB_DT_WIRE_ADAPTER		0x21
 #define USB_DT_RPIPE			0x22
 
-/* conventional codes for class-specific descriptors */
-#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
+/* Conventional codes for class-specific descriptors.  The convention is
+ * defined in the USB "Common Class" Spec (3.11).  Individual class specs
+ * are authoritative for their usage, not the "common class" writeup.
+ */
+#define USB_DT_CS_DEVICE		(USB_TYPE_CLASS | USB_DT_DEVICE)
+#define USB_DT_CS_CONFIG		(USB_TYPE_CLASS | USB_DT_CONFIG)
+#define USB_DT_CS_STRING		(USB_TYPE_CLASS | USB_DT_STRING)
+#define USB_DT_CS_INTERFACE		(USB_TYPE_CLASS | USB_DT_INTERFACE)
+#define USB_DT_CS_ENDPOINT		(USB_TYPE_CLASS | USB_DT_ENDPOINT)
 
 /* All standard descriptors have these 2 fields at the beginning */
 struct usb_descriptor_header {
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 441b877..a25c2af 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -96,44 +96,60 @@
  *	E N U M S
  */
 enum v4l2_field {
-	V4L2_FIELD_ANY        = 0, /* driver can choose from none,
-				      top, bottom, interlaced
-				      depending on whatever it thinks
-				      is approximate ... */
-	V4L2_FIELD_NONE       = 1, /* this device has no fields ... */
-	V4L2_FIELD_TOP        = 2, /* top field only */
-	V4L2_FIELD_BOTTOM     = 3, /* bottom field only */
-	V4L2_FIELD_INTERLACED = 4, /* both fields interlaced */
-	V4L2_FIELD_SEQ_TB     = 5, /* both fields sequential into one
-				      buffer, top-bottom order */
-	V4L2_FIELD_SEQ_BT     = 6, /* same as above + bottom-top order */
-	V4L2_FIELD_ALTERNATE  = 7, /* both fields alternating into
-				      separate buffers */
+	V4L2_FIELD_ANY           = 0, /* driver can choose from none,
+					 top, bottom, interlaced
+					 depending on whatever it thinks
+					 is approximate ... */
+	V4L2_FIELD_NONE          = 1, /* this device has no fields ... */
+	V4L2_FIELD_TOP           = 2, /* top field only */
+	V4L2_FIELD_BOTTOM        = 3, /* bottom field only */
+	V4L2_FIELD_INTERLACED    = 4, /* both fields interlaced */
+	V4L2_FIELD_SEQ_TB        = 5, /* both fields sequential into one
+					 buffer, top-bottom order */
+	V4L2_FIELD_SEQ_BT        = 6, /* same as above + bottom-top order */
+	V4L2_FIELD_ALTERNATE     = 7, /* both fields alternating into
+					 separate buffers */
+	V4L2_FIELD_INTERLACED_TB = 8, /* both fields interlaced, top field
+					 first and the top field is
+					 transmitted first */
+	V4L2_FIELD_INTERLACED_BT = 9, /* both fields interlaced, top field
+					 first and the bottom field is
+					 transmitted first */
 };
 #define V4L2_FIELD_HAS_TOP(field)	\
 	((field) == V4L2_FIELD_TOP 	||\
 	 (field) == V4L2_FIELD_INTERLACED ||\
+	 (field) == V4L2_FIELD_INTERLACED_TB ||\
+	 (field) == V4L2_FIELD_INTERLACED_BT ||\
 	 (field) == V4L2_FIELD_SEQ_TB	||\
 	 (field) == V4L2_FIELD_SEQ_BT)
 #define V4L2_FIELD_HAS_BOTTOM(field)	\
 	((field) == V4L2_FIELD_BOTTOM 	||\
 	 (field) == V4L2_FIELD_INTERLACED ||\
+	 (field) == V4L2_FIELD_INTERLACED_TB ||\
+	 (field) == V4L2_FIELD_INTERLACED_BT ||\
 	 (field) == V4L2_FIELD_SEQ_TB	||\
 	 (field) == V4L2_FIELD_SEQ_BT)
 #define V4L2_FIELD_HAS_BOTH(field)	\
 	((field) == V4L2_FIELD_INTERLACED ||\
-	 (field) == V4L2_FIELD_SEQ_TB	||\
+	 (field) == V4L2_FIELD_INTERLACED_TB ||\
+	 (field) == V4L2_FIELD_INTERLACED_BT ||\
+	 (field) == V4L2_FIELD_SEQ_TB ||\
 	 (field) == V4L2_FIELD_SEQ_BT)
 
 enum v4l2_buf_type {
-	V4L2_BUF_TYPE_VIDEO_CAPTURE      = 1,
-	V4L2_BUF_TYPE_VIDEO_OUTPUT       = 2,
-	V4L2_BUF_TYPE_VIDEO_OVERLAY      = 3,
-	V4L2_BUF_TYPE_VBI_CAPTURE        = 4,
-	V4L2_BUF_TYPE_VBI_OUTPUT         = 5,
-	V4L2_BUF_TYPE_SLICED_VBI_CAPTURE = 6,
-	V4L2_BUF_TYPE_SLICED_VBI_OUTPUT  = 7,
-	V4L2_BUF_TYPE_PRIVATE            = 0x80,
+	V4L2_BUF_TYPE_VIDEO_CAPTURE        = 1,
+	V4L2_BUF_TYPE_VIDEO_OUTPUT         = 2,
+	V4L2_BUF_TYPE_VIDEO_OVERLAY        = 3,
+	V4L2_BUF_TYPE_VBI_CAPTURE          = 4,
+	V4L2_BUF_TYPE_VBI_OUTPUT           = 5,
+	V4L2_BUF_TYPE_SLICED_VBI_CAPTURE   = 6,
+	V4L2_BUF_TYPE_SLICED_VBI_OUTPUT    = 7,
+#if 1
+	/* Experimental */
+	V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8,
+#endif
+	V4L2_BUF_TYPE_PRIVATE              = 0x80,
 };
 
 enum v4l2_ctrl_type {
@@ -227,6 +243,8 @@
 #define V4L2_CAP_SLICED_VBI_CAPTURE	0x00000040  /* Is a sliced VBI capture device */
 #define V4L2_CAP_SLICED_VBI_OUTPUT	0x00000080  /* Is a sliced VBI output device */
 #define V4L2_CAP_RDS_CAPTURE		0x00000100  /* RDS data capture */
+#define V4L2_CAP_VIDEO_OUTPUT_POS       0x00000200  /* Video output can have x,y coords */
+#define V4L2_CAP_VIDEO_OUTPUT_OVERLAY	0x00000400  /* Can do video output overlay */
 
 #define V4L2_CAP_TUNER			0x00010000  /* has a tuner */
 #define V4L2_CAP_AUDIO			0x00020000  /* has audio support */
@@ -249,6 +267,8 @@
 	__u32          		sizeimage;
 	enum v4l2_colorspace	colorspace;
 	__u32			priv;		/* private data, depends on pixelformat */
+	__u32 			left;	/* only valid if V4L2_CAP_VIDEO_OUTPUT_POS is set */
+	__u32 			top;	/* only valid if V4L2_CAP_VIDEO_OUTPUT_POS is set */
 };
 
 /*      Pixel format         FOURCC                        depth  Description  */
@@ -596,10 +616,14 @@
 #define V4L2_FBUF_CAP_CHROMAKEY		0x0002
 #define V4L2_FBUF_CAP_LIST_CLIPPING     0x0004
 #define V4L2_FBUF_CAP_BITMAP_CLIPPING	0x0008
+#define V4L2_FBUF_CAP_LOCAL_ALPHA	0x0010
+#define V4L2_FBUF_CAP_GLOBAL_ALPHA	0x0020
 /*  Flags for the 'flags' field. */
 #define V4L2_FBUF_FLAG_PRIMARY		0x0001
 #define V4L2_FBUF_FLAG_OVERLAY		0x0002
 #define V4L2_FBUF_FLAG_CHROMAKEY	0x0004
+#define V4L2_FBUF_FLAG_LOCAL_ALPHA	0x0008
+#define V4L2_FBUF_FLAG_GLOBAL_ALPHA	0x0010
 
 struct v4l2_clip
 {
@@ -615,6 +639,7 @@
 	struct v4l2_clip	__user *clips;
 	__u32			clipcount;
 	void			__user *bitmap;
+	__u8                    global_alpha;
 };
 
 /*
@@ -1037,6 +1062,7 @@
 	V4L2_MPEG_AUDIO_CRC_NONE  = 0,
 	V4L2_MPEG_AUDIO_CRC_CRC16 = 1,
 };
+#define V4L2_CID_MPEG_AUDIO_MUTE 		(V4L2_CID_MPEG_BASE+109)
 
 /*  MPEG video */
 #define V4L2_CID_MPEG_VIDEO_ENCODING 		(V4L2_CID_MPEG_BASE+200)
@@ -1063,6 +1089,8 @@
 #define V4L2_CID_MPEG_VIDEO_BITRATE 		(V4L2_CID_MPEG_BASE+207)
 #define V4L2_CID_MPEG_VIDEO_BITRATE_PEAK 	(V4L2_CID_MPEG_BASE+208)
 #define V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION (V4L2_CID_MPEG_BASE+209)
+#define V4L2_CID_MPEG_VIDEO_MUTE 		(V4L2_CID_MPEG_BASE+210)
+#define V4L2_CID_MPEG_VIDEO_MUTE_YUV 		(V4L2_CID_MPEG_BASE+211)
 
 /*  MPEG-class control IDs specific to the CX2584x driver as defined by V4L2 */
 #define V4L2_CID_MPEG_CX2341X_BASE 				(V4L2_CTRL_CLASS_MPEG | 0x1000)
@@ -1103,6 +1131,7 @@
 #define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP 	(V4L2_CID_MPEG_CX2341X_BASE+8)
 #define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM	(V4L2_CID_MPEG_CX2341X_BASE+9)
 #define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP 	(V4L2_CID_MPEG_CX2341X_BASE+10)
+#define V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS 	(V4L2_CID_MPEG_CX2341X_BASE+11)
 
 /*
  *	T U N I N G
@@ -1369,6 +1398,14 @@
 	__u64 val;
 };
 
+/* VIDIOC_G_CHIP_IDENT */
+struct v4l2_chip_ident {
+	__u32 match_type;  /* Match type */
+	__u32 match_chip;  /* Match this chip, meaning determined by match_type */
+	__u32 ident;       /* chip identifier as specified in <media/v4l2-chip-ident.h> */
+	__u32 revision;    /* chip revision, chip specific */
+};
+
 /*
  *	I O C T L   C O D E S   F O R   V I D E O   D E V I C E S
  *
@@ -1442,6 +1479,8 @@
 /* Experimental, only implemented if CONFIG_VIDEO_ADV_DEBUG is defined */
 #define	VIDIOC_DBG_S_REGISTER 	_IOW  ('V', 79, struct v4l2_register)
 #define	VIDIOC_DBG_G_REGISTER 	_IOWR ('V', 80, struct v4l2_register)
+
+#define VIDIOC_G_CHIP_IDENT     _IOWR ('V', 81, struct v4l2_chip_ident)
 #endif
 
 #ifdef __OLD_VIDIOC_
diff --git a/include/linux/wireless.h b/include/linux/wireless.h
index 48759b2..0987aa7 100644
--- a/include/linux/wireless.h
+++ b/include/linux/wireless.h
@@ -186,7 +186,7 @@
  *	- Wireless Event capability in struct iw_range
  *	- Add support for relative TxPower (yick !)
  *
- * V17 to V18 (From Jouni Malinen <jkmaline@cc.hut.fi>)
+ * V17 to V18 (From Jouni Malinen <j@w1.fi>)
  * ----------
  *	- Add support for WPA/WPA2
  *	- Add extended encoding configuration (SIOCSIWENCODEEXT and
diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h
index 2a7b38d..b8abfc7 100644
--- a/include/linux/workqueue.h
+++ b/include/linux/workqueue.h
@@ -191,14 +191,15 @@
 
 /*
  * Kill off a pending schedule_delayed_work().  Note that the work callback
- * function may still be running on return from cancel_delayed_work().  Run
- * flush_scheduled_work() to wait on it.
+ * function may still be running on return from cancel_delayed_work(), unless
+ * it returns 1 and the work doesn't re-arm itself. Run flush_workqueue() or
+ * cancel_work_sync() to wait on it.
  */
 static inline int cancel_delayed_work(struct delayed_work *work)
 {
 	int ret;
 
-	ret = del_timer_sync(&work->timer);
+	ret = del_timer(&work->timer);
 	if (ret)
 		work_release(&work->work);
 	return ret;
diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h
index 15ca89e..a5d53e0 100644
--- a/include/linux/xfrm.h
+++ b/include/linux/xfrm.h
@@ -181,6 +181,15 @@
 	XFRM_MSG_MIGRATE,
 #define XFRM_MSG_MIGRATE XFRM_MSG_MIGRATE
 
+	XFRM_MSG_NEWSADINFO,
+#define XFRM_MSG_NEWSADINFO XFRM_MSG_NEWSADINFO
+	XFRM_MSG_GETSADINFO,
+#define XFRM_MSG_GETSADINFO XFRM_MSG_GETSADINFO
+
+	XFRM_MSG_NEWSPDINFO,
+#define XFRM_MSG_NEWSPDINFO XFRM_MSG_NEWSPDINFO
+	XFRM_MSG_GETSPDINFO,
+#define XFRM_MSG_GETSPDINFO XFRM_MSG_GETSPDINFO
 	__XFRM_MSG_MAX
 };
 #define XFRM_MSG_MAX (__XFRM_MSG_MAX - 1)
@@ -234,6 +243,17 @@
 #define XFRM_AE_MAX (__XFRM_AE_MAX - 1)
 };
 
+/* SAD Table filter flags  */
+enum xfrm_sad_ftype_t {
+	XFRM_SAD_UNSPEC,
+	XFRM_SAD_HMASK=1,
+	XFRM_SAD_HMAX=2,
+	XFRM_SAD_CNT=4,
+	__XFRM_SAD_MAX
+
+#define XFRM_SAD_MAX (__XFRM_SAD_MAX - 1)
+};
+
 struct xfrm_userpolicy_type {
 	__u8		type;
 	__u16		reserved1;
@@ -265,6 +285,46 @@
 #define XFRMA_MAX (__XFRMA_MAX - 1)
 };
 
+enum xfrm_sadattr_type_t {
+	XFRMA_SAD_UNSPEC,
+	XFRMA_SADHMASK,
+	XFRMA_SADHMAX,
+	XFRMA_SADCNT,
+	__XFRMA_SAD_MAX
+
+#define XFRMA_SAD_MAX (__XFRMA_SAD_MAX - 1)
+};
+
+/* SPD Table filter flags  */
+enum xfrm_spd_ftype_t {
+	XFRM_SPD_UNSPEC,
+	XFRM_SPD_HMASK=1,
+	XFRM_SPD_HMAX=2,
+	XFRM_SPD_ICNT=4,
+	XFRM_SPD_OCNT=8,
+	XFRM_SPD_FCNT=16,
+	XFRM_SPD_ISCNT=32,
+	XFRM_SPD_OSCNT=64,
+	XFRM_SPD_FSCNT=128,
+	__XFRM_SPD_MAX
+
+#define XFRM_SPD_MAX (__XFRM_SPD_MAX - 1)
+};
+enum xfrm_spdattr_type_t {
+	XFRMA_SPD_UNSPEC,
+	XFRMA_SPDHMASK,
+	XFRMA_SPDHMAX,
+	XFRMA_SPDICNT,
+	XFRMA_SPDOCNT,
+	XFRMA_SPDFCNT,
+	XFRMA_SPDISCNT,
+	XFRMA_SPDOSCNT,
+	XFRMA_SPDFSCNT,
+	__XFRMA_SPD_MAX
+
+#define XFRMA_SPD_MAX (__XFRMA_SPD_MAX - 1)
+};
+
 struct xfrm_usersa_info {
 	struct xfrm_selector		sel;
 	struct xfrm_id			id;
diff --git a/include/media/cx2341x.h b/include/media/cx2341x.h
index d758a52c..38c12fe 100644
--- a/include/media/cx2341x.h
+++ b/include/media/cx2341x.h
@@ -40,6 +40,7 @@
 	/* stream */
 	enum v4l2_mpeg_stream_type stream_type;
 	enum v4l2_mpeg_stream_vbi_fmt stream_vbi_fmt;
+	u16 stream_insert_nav_packets;
 
 	/* audio */
 	enum v4l2_mpeg_audio_sampling_freq audio_sampling_freq;
@@ -50,6 +51,7 @@
 	enum v4l2_mpeg_audio_emphasis audio_emphasis;
 	enum v4l2_mpeg_audio_crc audio_crc;
 	u16 audio_properties;
+	u16 audio_mute;
 
 	/* video */
 	enum v4l2_mpeg_video_encoding video_encoding;
@@ -61,6 +63,8 @@
 	u32 video_bitrate;
 	u32 video_bitrate_peak;
 	u16 video_temporal_decimation;
+	u16 video_mute;
+	u32 video_mute_yuv;
 
 	/* encoding filters */
 	enum v4l2_mpeg_cx2341x_video_spatial_filter_mode video_spatial_filter_mode;
@@ -162,7 +166,7 @@
 #define CX2341X_ENC_SET_PLACEHOLDER 		0xd7
 #define CX2341X_ENC_MUTE_VIDEO 			0xd9
 #define CX2341X_ENC_MUTE_AUDIO 			0xda
-#define CX2341X_ENC_UNKNOWN			0xdb
+#define CX2341X_ENC_SET_VERT_CROP_LINE		0xdb
 #define CX2341X_ENC_MISC 			0xdc
 
 /* OSD API, specific to the cx23415 */
diff --git a/include/media/ivtv.h b/include/media/ivtv.h
new file mode 100644
index 0000000..412b48e
--- /dev/null
+++ b/include/media/ivtv.h
@@ -0,0 +1,65 @@
+/*
+    Public ivtv API header
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _LINUX_IVTV_H
+#define _LINUX_IVTV_H
+
+/* ivtv knows several distinct output modes: MPEG streaming,
+   YUV streaming, YUV updates through user DMA and the passthrough
+   mode.
+
+   In order to clearly tell the driver that we are in user DMA
+   YUV mode you need to call IVTV_IOC_DMA_FRAME with y_source == NULL
+   first (althrough if you don't then the first time
+   DMA_FRAME is called the mode switch is done automatically).
+
+   When you close the file handle the user DMA mode is exited again.
+
+   While in one mode, you cannot use another mode (EBUSY is returned).
+
+   All this means that if you want to change the YUV interlacing
+   for the user DMA YUV mode you first need to do call IVTV_IOC_DMA_FRAME
+   with y_source == NULL before you can set the correct format using
+   VIDIOC_S_FMT.
+
+   Eventually all this should be replaced with a proper V4L2 API,
+   but for now we have to do it this way. */
+
+struct ivtv_dma_frame {
+	enum v4l2_buf_type type; /* V4L2_BUF_TYPE_VIDEO_OUTPUT */
+	__u32 pixelformat;	 /* 0 == same as destination */
+	void __user *y_source;   /* if NULL and type == V4L2_BUF_TYPE_VIDEO_OUTPUT,
+				    then just switch to user DMA YUV output mode */
+	void __user *uv_source;  /* Unused for RGB pixelformats */
+	struct v4l2_rect src;
+	struct v4l2_rect dst;
+	__u32 src_width;
+	__u32 src_height;
+};
+
+#define IVTV_IOC_DMA_FRAME  _IOW ('V', BASE_VIDIOC_PRIVATE+0, struct ivtv_dma_frame)
+
+/* These are the VBI types as they appear in the embedded VBI private packets. */
+#define IVTV_SLICED_TYPE_TELETEXT_B     (1)
+#define IVTV_SLICED_TYPE_CAPTION_525    (4)
+#define IVTV_SLICED_TYPE_WSS_625        (5)
+#define IVTV_SLICED_TYPE_VPS            (7)
+
+#endif /* _LINUX_IVTV_H */
diff --git a/include/media/tuner.h b/include/media/tuner.h
index 99acf84..a41ac411 100644
--- a/include/media/tuner.h
+++ b/include/media/tuner.h
@@ -177,6 +177,8 @@
 	unsigned short	addr; 	/* I2C address */
 	unsigned int	type;   /* Tuner type */
 	unsigned int	mode_mask;  /* Allowed tuner modes */
+	unsigned int	config; /* configuraion for more complex tuners */
+	int (*tuner_callback) (void *dev, int command,int arg);
 };
 
 struct tuner {
@@ -211,6 +213,9 @@
 	unsigned char tda827x_ver;
 	unsigned int sgIF;
 
+	unsigned int config;
+	int (*tuner_callback) (void *dev, int command,int arg);
+
 	/* function ptrs */
 	void (*set_tv_freq)(struct i2c_client *c, unsigned int freq);
 	void (*set_radio_freq)(struct i2c_client *c, unsigned int freq);
diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
new file mode 100644
index 0000000..09d16c4
--- /dev/null
+++ b/include/media/v4l2-chip-ident.h
@@ -0,0 +1,149 @@
+/*
+    v4l2 chip identifiers header
+
+    This header provides a list of chip identifiers that can be returned
+    through the VIDIOC_G_CHIP_IDENT ioctl.
+
+    Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef V4L2_CHIP_IDENT_H_
+#define V4L2_CHIP_IDENT_H_
+
+/* VIDIOC_G_CHIP_IDENT: identifies the actual chip installed on the board */
+enum {
+	/* general idents: reserved range 0-49 */
+	V4L2_IDENT_NONE      = 0,       /* No chip matched */
+	V4L2_IDENT_AMBIGUOUS = 1,       /* Match too general, multiple chips matched */
+	V4L2_IDENT_UNKNOWN   = 2,       /* Chip found, but cannot identify */
+
+	/* module tvaudio: reserved range 50-99 */
+	V4L2_IDENT_TVAUDIO = 50,	/* A tvaudio chip, unknown which it is exactly */
+
+	/* module saa7110: just ident 100 */
+	V4L2_IDENT_SAA7110 = 100,
+
+	/* module saa7111: just ident 101 */
+	V4L2_IDENT_SAA7111 = 101,
+
+	/* module saa7115: reserved range 102-149 */
+	V4L2_IDENT_SAA7113 = 103,
+	V4L2_IDENT_SAA7114 = 104,
+	V4L2_IDENT_SAA7115 = 105,
+	V4L2_IDENT_SAA7118 = 108,
+
+	/* module saa7127: reserved range 150-199 */
+	V4L2_IDENT_SAA7127 = 157,
+	V4L2_IDENT_SAA7129 = 159,
+
+	/* module cx25840: reserved range 200-249 */
+	V4L2_IDENT_CX25836 = 236,
+	V4L2_IDENT_CX25837 = 237,
+	V4L2_IDENT_CX25840 = 240,
+	V4L2_IDENT_CX25841 = 241,
+	V4L2_IDENT_CX25842 = 242,
+	V4L2_IDENT_CX25843 = 243,
+
+	/* OmniVision sensors: reserved range 250-299 */
+	V4L2_IDENT_OV7670 = 250,
+
+	/* Conexant MPEG encoder/decoders: reserved range 410-420 */
+	V4L2_IDENT_CX23415 = 415,
+	V4L2_IDENT_CX23416 = 416,
+
+	/* module wm8739: just ident 8739 */
+	V4L2_IDENT_WM8739 = 8739,
+
+	/* module wm8775: just ident 8775 */
+	V4L2_IDENT_WM8775 = 8775,
+
+	/* module cs53132a: just ident 53132 */
+	V4L2_IDENT_CS53l32A = 53132,
+
+	/* module upd64031a: just ident 64031 */
+	V4L2_IDENT_UPD64031A = 64031,
+
+	/* module upd64083: just ident 64083 */
+	V4L2_IDENT_UPD64083 = 64083,
+
+	/* module msp34xx: reserved range 34000-34999 */
+	V4L2_IDENT_MSP3400B = 34002,
+	V4L2_IDENT_MSP3410B = 34102,
+
+	V4L2_IDENT_MSP3400C = 34003,
+	V4L2_IDENT_MSP3410C = 34103,
+
+	V4L2_IDENT_MSP3400D = 34004,
+	V4L2_IDENT_MSP3410D = 34104,
+	V4L2_IDENT_MSP3405D = 34054,
+	V4L2_IDENT_MSP3415D = 34154,
+	V4L2_IDENT_MSP3407D = 34074,
+	V4L2_IDENT_MSP3417D = 34174,
+
+	V4L2_IDENT_MSP3400G = 34007,
+	V4L2_IDENT_MSP3410G = 34107,
+	V4L2_IDENT_MSP3420G = 34207,
+	V4L2_IDENT_MSP3430G = 34307,
+	V4L2_IDENT_MSP3440G = 34407,
+	V4L2_IDENT_MSP3450G = 34507,
+	V4L2_IDENT_MSP3460G = 34607,
+
+	V4L2_IDENT_MSP3401G = 34017,
+	V4L2_IDENT_MSP3411G = 34117,
+	V4L2_IDENT_MSP3421G = 34217,
+	V4L2_IDENT_MSP3431G = 34317,
+	V4L2_IDENT_MSP3441G = 34417,
+	V4L2_IDENT_MSP3451G = 34517,
+	V4L2_IDENT_MSP3461G = 34617,
+
+	V4L2_IDENT_MSP3402G = 34027,
+	V4L2_IDENT_MSP3412G = 34127,
+	V4L2_IDENT_MSP3422G = 34227,
+	V4L2_IDENT_MSP3442G = 34427,
+	V4L2_IDENT_MSP3452G = 34527,
+
+	V4L2_IDENT_MSP3405G = 34057,
+	V4L2_IDENT_MSP3415G = 34157,
+	V4L2_IDENT_MSP3425G = 34257,
+	V4L2_IDENT_MSP3435G = 34357,
+	V4L2_IDENT_MSP3445G = 34457,
+	V4L2_IDENT_MSP3455G = 34557,
+	V4L2_IDENT_MSP3465G = 34657,
+
+	V4L2_IDENT_MSP3407G = 34077,
+	V4L2_IDENT_MSP3417G = 34177,
+	V4L2_IDENT_MSP3427G = 34277,
+	V4L2_IDENT_MSP3437G = 34377,
+	V4L2_IDENT_MSP3447G = 34477,
+	V4L2_IDENT_MSP3457G = 34577,
+	V4L2_IDENT_MSP3467G = 34677,
+
+	/* module msp44xx: reserved range 44000-44999 */
+	V4L2_IDENT_MSP4400G = 44007,
+	V4L2_IDENT_MSP4410G = 44107,
+	V4L2_IDENT_MSP4420G = 44207,
+	V4L2_IDENT_MSP4440G = 44407,
+	V4L2_IDENT_MSP4450G = 44507,
+
+	V4L2_IDENT_MSP4408G = 44087,
+	V4L2_IDENT_MSP4418G = 44187,
+	V4L2_IDENT_MSP4428G = 44287,
+	V4L2_IDENT_MSP4448G = 44487,
+	V4L2_IDENT_MSP4458G = 44587,
+};
+
+#endif
diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index 6eaeec9..181a40c 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -98,6 +98,8 @@
 
 struct i2c_client; /* forward reference */
 int v4l2_chip_match_i2c_client(struct i2c_client *c, u32 id_type, u32 chip_id);
+int v4l2_chip_ident_i2c_client(struct i2c_client *c, struct v4l2_chip_ident *chip,
+		u32 ident, u32 revision);
 int v4l2_chip_match_host(u32 id_type, u32 chip_id);
 
 /* ------------------------------------------------------------------------- */
@@ -114,39 +116,6 @@
 	u32 type;		/* VBI service type (V4L2_SLICED_*). 0 if no service found */
 };
 
-/* VIDIOC_INT_G_CHIP_IDENT: identifies the actual chip installed on the board */
-enum v4l2_chip_ident {
-	/* general idents: reserved range 0-49 */
-	V4L2_IDENT_UNKNOWN = 0,
-
-	/* module saa7110: just ident= 100 */
-	V4L2_IDENT_SAA7110 = 100,
-
-	/* module saa7111: just ident= 101 */
-	V4L2_IDENT_SAA7111 = 101,
-
-	/* module saa7115: reserved range 102-149 */
-	V4L2_IDENT_SAA7113 = 103,
-	V4L2_IDENT_SAA7114 = 104,
-	V4L2_IDENT_SAA7115 = 105,
-	V4L2_IDENT_SAA7118 = 108,
-
-	/* module saa7127: reserved range 150-199 */
-	V4L2_IDENT_SAA7127 = 157,
-	V4L2_IDENT_SAA7129 = 159,
-
-	/* module cx25840: reserved range 200-249 */
-	V4L2_IDENT_CX25836 = 236,
-	V4L2_IDENT_CX25837 = 237,
-	V4L2_IDENT_CX25840 = 240,
-	V4L2_IDENT_CX25841 = 241,
-	V4L2_IDENT_CX25842 = 242,
-	V4L2_IDENT_CX25843 = 243,
-
-	/* OmniVision sensors - range 250-299 */
-	V4L2_IDENT_OV7670 = 250,
-};
-
 /* audio ioctls */
 
 /* v4l device was opened in Radio mode, to be replaced by VIDIOC_INT_S_TUNER_MODE */
@@ -208,10 +177,6 @@
    whether CC data from the first or second field should be obtained). */
 #define VIDIOC_INT_G_VBI_DATA 		_IOWR('d', 106, struct v4l2_sliced_vbi_data)
 
-/* Returns the chip identifier or V4L2_IDENT_UNKNOWN if no identification can
-   be made. */
-#define VIDIOC_INT_G_CHIP_IDENT		_IOR ('d', 107, enum v4l2_chip_ident)
-
 /* Sets I2S speed in bps. This is used to provide a standard way to select I2S
    clock used by driving digital audio streams at some board designs.
    Usual values for the frequency are 1024000 and 2048000.
@@ -254,4 +219,12 @@
    default values. */
 #define VIDIOC_INT_INIT			_IOW ('d', 114, u32)
 
+/* Set v4l2_std_id for video OUTPUT devices. This is ignored by
+   video input devices. */
+#define VIDIOC_INT_S_STD_OUTPUT		_IOW  ('d', 115, v4l2_std_id)
+
+/* Get v4l2_std_id for video OUTPUT devices. This is ignored by
+   video input devices. */
+#define VIDIOC_INT_G_STD_OUTPUT		_IOW  ('d', 116, v4l2_std_id)
+
 #endif /* V4L2_COMMON_H_ */
diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
index 1dd3d32..d62847f 100644
--- a/include/media/v4l2-dev.h
+++ b/include/media/v4l2-dev.h
@@ -127,6 +127,8 @@
 					    struct v4l2_fmtdesc *f);
 	int (*vidioc_enum_fmt_video_output)(struct file *file, void *fh,
 					    struct v4l2_fmtdesc *f);
+	int (*vidioc_enum_fmt_output_overlay) (struct file *file, void *fh,
+					    struct v4l2_fmtdesc *f);
 	int (*vidioc_enum_fmt_vbi_output)  (struct file *file, void *fh,
 					    struct v4l2_fmtdesc *f);
 	int (*vidioc_enum_fmt_type_private)(struct file *file, void *fh,
@@ -145,6 +147,8 @@
 					struct v4l2_format *f);
 	int (*vidioc_g_fmt_video_output)(struct file *file, void *fh,
 					struct v4l2_format *f);
+	int (*vidioc_g_fmt_output_overlay) (struct file *file, void *fh,
+					struct v4l2_format *f);
 	int (*vidioc_g_fmt_type_private)(struct file *file, void *fh,
 					struct v4l2_format *f);
 
@@ -162,6 +166,8 @@
 					struct v4l2_format *f);
 	int (*vidioc_s_fmt_video_output)(struct file *file, void *fh,
 					struct v4l2_format *f);
+	int (*vidioc_s_fmt_output_overlay) (struct file *file, void *fh,
+					struct v4l2_format *f);
 	int (*vidioc_s_fmt_type_private)(struct file *file, void *fh,
 					struct v4l2_format *f);
 
@@ -178,6 +184,8 @@
 					  struct v4l2_format *f);
 	int (*vidioc_try_fmt_video_output)(struct file *file, void *fh,
 					  struct v4l2_format *f);
+	int (*vidioc_try_fmt_output_overlay)(struct file *file, void *fh,
+					  struct v4l2_format *f);
 	int (*vidioc_try_fmt_type_private)(struct file *file, void *fh,
 					  struct v4l2_format *f);
 
@@ -309,6 +317,8 @@
 	int (*vidioc_s_register)       (struct file *file, void *fh,
 					struct v4l2_register *reg);
 #endif
+	int (*vidioc_g_chip_ident)     (struct file *file, void *fh,
+					struct v4l2_chip_ident *chip);
 
 
 #ifdef OBSOLETE_OWNER /* to be removed soon */
diff --git a/include/mtd/Kbuild b/include/mtd/Kbuild
index e0fe92b..4d46b3b 100644
--- a/include/mtd/Kbuild
+++ b/include/mtd/Kbuild
@@ -3,3 +3,5 @@
 header-y += mtd-abi.h
 header-y += mtd-user.h
 header-y += nftl-user.h
+header-y += ubi-header.h
+header-y += ubi-user.h
diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h
index 8e501a7..f71dac4 100644
--- a/include/mtd/mtd-abi.h
+++ b/include/mtd/mtd-abi.h
@@ -24,6 +24,7 @@
 #define MTD_NORFLASH		3
 #define MTD_NANDFLASH		4
 #define MTD_DATAFLASH		6
+#define MTD_UBIVOLUME		7
 
 #define MTD_WRITEABLE		0x400	/* Device is writeable */
 #define MTD_BIT_WRITEABLE	0x800	/* Single bits can be flipped */
diff --git a/include/mtd/ubi-header.h b/include/mtd/ubi-header.h
new file mode 100644
index 0000000..fa479c7
--- /dev/null
+++ b/include/mtd/ubi-header.h
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ *          Thomas Gleixner
+ *          Frank Haverkamp
+ *          Oliver Lohmann
+ *          Andreas Arnez
+ */
+
+/*
+ * This file defines the layout of UBI headers and all the other UBI on-flash
+ * data structures. May be included by user-space.
+ */
+
+#ifndef __UBI_HEADER_H__
+#define __UBI_HEADER_H__
+
+#include <asm/byteorder.h>
+
+/* The version of UBI images supported by this implementation */
+#define UBI_VERSION 1
+
+/* The highest erase counter value supported by this implementation */
+#define UBI_MAX_ERASECOUNTER 0x7FFFFFFF
+
+/* The initial CRC32 value used when calculating CRC checksums */
+#define UBI_CRC32_INIT 0xFFFFFFFFU
+
+/* Erase counter header magic number (ASCII "UBI#") */
+#define UBI_EC_HDR_MAGIC  0x55424923
+/* Volume identifier header magic number (ASCII "UBI!") */
+#define UBI_VID_HDR_MAGIC 0x55424921
+
+/*
+ * Volume type constants used in the volume identifier header.
+ *
+ * @UBI_VID_DYNAMIC: dynamic volume
+ * @UBI_VID_STATIC: static volume
+ */
+enum {
+	UBI_VID_DYNAMIC = 1,
+	UBI_VID_STATIC  = 2
+};
+
+/*
+ * Compatibility constants used by internal volumes.
+ *
+ * @UBI_COMPAT_DELETE: delete this internal volume before anything is written
+ * to the flash
+ * @UBI_COMPAT_RO: attach this device in read-only mode
+ * @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its
+ * physical eraseblocks, don't allow the wear-leveling unit to move them
+ * @UBI_COMPAT_REJECT: reject this UBI image
+ */
+enum {
+	UBI_COMPAT_DELETE   = 1,
+	UBI_COMPAT_RO       = 2,
+	UBI_COMPAT_PRESERVE = 4,
+	UBI_COMPAT_REJECT   = 5
+};
+
+/*
+ * ubi16_t/ubi32_t/ubi64_t - 16, 32, and 64-bit integers used in UBI on-flash
+ * data structures.
+ */
+typedef struct {
+	uint16_t int16;
+} __attribute__ ((packed)) ubi16_t;
+
+typedef struct {
+	uint32_t int32;
+} __attribute__ ((packed)) ubi32_t;
+
+typedef struct {
+	uint64_t int64;
+} __attribute__ ((packed)) ubi64_t;
+
+/*
+ * In this implementation of UBI uses the big-endian format for on-flash
+ * integers. The below are the corresponding conversion macros.
+ */
+#define cpu_to_ubi16(x) ((ubi16_t){__cpu_to_be16(x)})
+#define ubi16_to_cpu(x) ((uint16_t)__be16_to_cpu((x).int16))
+
+#define cpu_to_ubi32(x) ((ubi32_t){__cpu_to_be32(x)})
+#define ubi32_to_cpu(x) ((uint32_t)__be32_to_cpu((x).int32))
+
+#define cpu_to_ubi64(x) ((ubi64_t){__cpu_to_be64(x)})
+#define ubi64_to_cpu(x) ((uint64_t)__be64_to_cpu((x).int64))
+
+/* Sizes of UBI headers */
+#define UBI_EC_HDR_SIZE  sizeof(struct ubi_ec_hdr)
+#define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr)
+
+/* Sizes of UBI headers without the ending CRC */
+#define UBI_EC_HDR_SIZE_CRC  (UBI_EC_HDR_SIZE  - sizeof(ubi32_t))
+#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(ubi32_t))
+
+/**
+ * struct ubi_ec_hdr - UBI erase counter header.
+ * @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC)
+ * @version: version of UBI implementation which is supposed to accept this
+ * UBI image
+ * @padding1: reserved for future, zeroes
+ * @ec: the erase counter
+ * @vid_hdr_offset: where the VID header starts
+ * @data_offset: where the user data start
+ * @padding2: reserved for future, zeroes
+ * @hdr_crc: erase counter header CRC checksum
+ *
+ * The erase counter header takes 64 bytes and has a plenty of unused space for
+ * future usage. The unused fields are zeroed. The @version field is used to
+ * indicate the version of UBI implementation which is supposed to be able to
+ * work with this UBI image. If @version is greater then the current UBI
+ * version, the image is rejected. This may be useful in future if something
+ * is changed radically. This field is duplicated in the volume identifier
+ * header.
+ *
+ * The @vid_hdr_offset and @data_offset fields contain the offset of the the
+ * volume identifier header and user data, relative to the beginning of the
+ * physical eraseblock. These values have to be the same for all physical
+ * eraseblocks.
+ */
+struct ubi_ec_hdr {
+	ubi32_t magic;
+	uint8_t version;
+	uint8_t padding1[3];
+	ubi64_t ec; /* Warning: the current limit is 31-bit anyway! */
+	ubi32_t vid_hdr_offset;
+	ubi32_t data_offset;
+	uint8_t padding2[36];
+	ubi32_t hdr_crc;
+} __attribute__ ((packed));
+
+/**
+ * struct ubi_vid_hdr - on-flash UBI volume identifier header.
+ * @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC)
+ * @version: UBI implementation version which is supposed to accept this UBI
+ * image (%UBI_VERSION)
+ * @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC)
+ * @copy_flag: if this logical eraseblock was copied from another physical
+ * eraseblock (for wear-leveling reasons)
+ * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE,
+ * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT)
+ * @vol_id: ID of this volume
+ * @lnum: logical eraseblock number
+ * @leb_ver: version of this logical eraseblock (IMPORTANT: obsolete, to be
+ * removed, kept only for not breaking older UBI users)
+ * @data_size: how many bytes of data this logical eraseblock contains
+ * @used_ebs: total number of used logical eraseblocks in this volume
+ * @data_pad: how many bytes at the end of this physical eraseblock are not
+ * used
+ * @data_crc: CRC checksum of the data stored in this logical eraseblock
+ * @padding1: reserved for future, zeroes
+ * @sqnum: sequence number
+ * @padding2: reserved for future, zeroes
+ * @hdr_crc: volume identifier header CRC checksum
+ *
+ * The @sqnum is the value of the global sequence counter at the time when this
+ * VID header was created. The global sequence counter is incremented each time
+ * UBI writes a new VID header to the flash, i.e. when it maps a logical
+ * eraseblock to a new physical eraseblock. The global sequence counter is an
+ * unsigned 64-bit integer and we assume it never overflows. The @sqnum
+ * (sequence number) is used to distinguish between older and newer versions of
+ * logical eraseblocks.
+ *
+ * There are 2 situations when there may be more then one physical eraseblock
+ * corresponding to the same logical eraseblock, i.e., having the same @vol_id
+ * and @lnum values in the volume identifier header. Suppose we have a logical
+ * eraseblock L and it is mapped to the physical eraseblock P.
+ *
+ * 1. Because UBI may erase physical eraseblocks asynchronously, the following
+ * situation is possible: L is asynchronously erased, so P is scheduled for
+ * erasure, then L is written to,i.e. mapped to another physical eraseblock P1,
+ * so P1 is written to, then an unclean reboot happens. Result - there are 2
+ * physical eraseblocks P and P1 corresponding to the same logical eraseblock
+ * L. But P1 has greater sequence number, so UBI picks P1 when it attaches the
+ * flash.
+ *
+ * 2. From time to time UBI moves logical eraseblocks to other physical
+ * eraseblocks for wear-leveling reasons. If, for example, UBI moves L from P
+ * to P1, and an unclean reboot happens before P is physically erased, there
+ * are two physical eraseblocks P and P1 corresponding to L and UBI has to
+ * select one of them when the flash is attached. The @sqnum field says which
+ * PEB is the original (obviously P will have lower @sqnum) and the copy. But
+ * it is not enough to select the physical eraseblock with the higher sequence
+ * number, because the unclean reboot could have happen in the middle of the
+ * copying process, so the data in P is corrupted. It is also not enough to
+ * just select the physical eraseblock with lower sequence number, because the
+ * data there may be old (consider a case if more data was added to P1 after
+ * the copying). Moreover, the unclean reboot may happen when the erasure of P
+ * was just started, so it result in unstable P, which is "mostly" OK, but
+ * still has unstable bits.
+ *
+ * UBI uses the @copy_flag field to indicate that this logical eraseblock is a
+ * copy. UBI also calculates data CRC when the data is moved and stores it at
+ * the @data_crc field of the copy (P1). So when UBI needs to pick one physical
+ * eraseblock of two (P or P1), the @copy_flag of the newer one (P1) is
+ * examined. If it is cleared, the situation* is simple and the newer one is
+ * picked. If it is set, the data CRC of the copy (P1) is examined. If the CRC
+ * checksum is correct, this physical eraseblock is selected (P1). Otherwise
+ * the older one (P) is selected.
+ *
+ * Note, there is an obsolete @leb_ver field which was used instead of @sqnum
+ * in the past. But it is not used anymore and we keep it in order to be able
+ * to deal with old UBI images. It will be removed at some point.
+ *
+ * There are 2 sorts of volumes in UBI: user volumes and internal volumes.
+ * Internal volumes are not seen from outside and are used for various internal
+ * UBI purposes. In this implementation there is only one internal volume - the
+ * layout volume. Internal volumes are the main mechanism of UBI extensions.
+ * For example, in future one may introduce a journal internal volume. Internal
+ * volumes have their own reserved range of IDs.
+ *
+ * The @compat field is only used for internal volumes and contains the "degree
+ * of their compatibility". It is always zero for user volumes. This field
+ * provides a mechanism to introduce UBI extensions and to be still compatible
+ * with older UBI binaries. For example, if someone introduced a journal in
+ * future, he would probably use %UBI_COMPAT_DELETE compatibility for the
+ * journal volume.  And in this case, older UBI binaries, which know nothing
+ * about the journal volume, would just delete this volume and work perfectly
+ * fine. This is similar to what Ext2fs does when it is fed by an Ext3fs image
+ * - it just ignores the Ext3fs journal.
+ *
+ * The @data_crc field contains the CRC checksum of the contents of the logical
+ * eraseblock if this is a static volume. In case of dynamic volumes, it does
+ * not contain the CRC checksum as a rule. The only exception is when the
+ * data of the physical eraseblock was moved by the wear-leveling unit, then
+ * the wear-leveling unit calculates the data CRC and stores it in the
+ * @data_crc field. And of course, the @copy_flag is %in this case.
+ *
+ * The @data_size field is used only for static volumes because UBI has to know
+ * how many bytes of data are stored in this eraseblock. For dynamic volumes,
+ * this field usually contains zero. The only exception is when the data of the
+ * physical eraseblock was moved to another physical eraseblock for
+ * wear-leveling reasons. In this case, UBI calculates CRC checksum of the
+ * contents and uses both @data_crc and @data_size fields. In this case, the
+ * @data_size field contains data size.
+ *
+ * The @used_ebs field is used only for static volumes and indicates how many
+ * eraseblocks the data of the volume takes. For dynamic volumes this field is
+ * not used and always contains zero.
+ *
+ * The @data_pad is calculated when volumes are created using the alignment
+ * parameter. So, effectively, the @data_pad field reduces the size of logical
+ * eraseblocks of this volume. This is very handy when one uses block-oriented
+ * software (say, cramfs) on top of the UBI volume.
+ */
+struct ubi_vid_hdr {
+	ubi32_t magic;
+	uint8_t version;
+	uint8_t vol_type;
+	uint8_t copy_flag;
+	uint8_t compat;
+	ubi32_t vol_id;
+	ubi32_t lnum;
+	ubi32_t leb_ver; /* obsolete, to be removed, don't use */
+	ubi32_t data_size;
+	ubi32_t used_ebs;
+	ubi32_t data_pad;
+	ubi32_t data_crc;
+	uint8_t padding1[4];
+	ubi64_t sqnum;
+	uint8_t padding2[12];
+	ubi32_t hdr_crc;
+} __attribute__ ((packed));
+
+/* Internal UBI volumes count */
+#define UBI_INT_VOL_COUNT 1
+
+/*
+ * Starting ID of internal volumes. There is reserved room for 4096 internal
+ * volumes.
+ */
+#define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096)
+
+/* The layout volume contains the volume table */
+
+#define UBI_LAYOUT_VOL_ID        UBI_INTERNAL_VOL_START
+#define UBI_LAYOUT_VOLUME_EBS    2
+#define UBI_LAYOUT_VOLUME_NAME   "layout volume"
+#define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT
+
+/* The maximum number of volumes per one UBI device */
+#define UBI_MAX_VOLUMES 128
+
+/* The maximum volume name length */
+#define UBI_VOL_NAME_MAX 127
+
+/* Size of the volume table record */
+#define UBI_VTBL_RECORD_SIZE sizeof(struct ubi_vtbl_record)
+
+/* Size of the volume table record without the ending CRC */
+#define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(ubi32_t))
+
+/**
+ * struct ubi_vtbl_record - a record in the volume table.
+ * @reserved_pebs: how many physical eraseblocks are reserved for this volume
+ * @alignment: volume alignment
+ * @data_pad: how many bytes are unused at the end of the each physical
+ * eraseblock to satisfy the requested alignment
+ * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ * @upd_marker: if volume update was started but not finished
+ * @name_len: volume name length
+ * @name: the volume name
+ * @padding2: reserved, zeroes
+ * @crc: a CRC32 checksum of the record
+ *
+ * The volume table records are stored in the volume table, which is stored in
+ * the layout volume. The layout volume consists of 2 logical eraseblock, each
+ * of which contains a copy of the volume table (i.e., the volume table is
+ * duplicated). The volume table is an array of &struct ubi_vtbl_record
+ * objects indexed by the volume ID.
+ *
+ * If the size of the logical eraseblock is large enough to fit
+ * %UBI_MAX_VOLUMES records, the volume table contains %UBI_MAX_VOLUMES
+ * records. Otherwise, it contains as many records as it can fit (i.e., size of
+ * logical eraseblock divided by sizeof(struct ubi_vtbl_record)).
+ *
+ * The @upd_marker flag is used to implement volume update. It is set to %1
+ * before update and set to %0 after the update. So if the update operation was
+ * interrupted, UBI knows that the volume is corrupted.
+ *
+ * The @alignment field is specified when the volume is created and cannot be
+ * later changed. It may be useful, for example, when a block-oriented file
+ * system works on top of UBI. The @data_pad field is calculated using the
+ * logical eraseblock size and @alignment. The alignment must be multiple to the
+ * minimal flash I/O unit. If @alignment is 1, all the available space of
+ * the physical eraseblocks is used.
+ *
+ * Empty records contain all zeroes and the CRC checksum of those zeroes.
+ */
+struct ubi_vtbl_record {
+	ubi32_t reserved_pebs;
+	ubi32_t alignment;
+	ubi32_t data_pad;
+	uint8_t vol_type;
+	uint8_t upd_marker;
+	ubi16_t name_len;
+	uint8_t name[UBI_VOL_NAME_MAX+1];
+	uint8_t padding2[24];
+	ubi32_t crc;
+} __attribute__ ((packed));
+
+#endif /* !__UBI_HEADER_H__ */
diff --git a/include/mtd/ubi-user.h b/include/mtd/ubi-user.h
new file mode 100644
index 0000000..fe06ded
--- /dev/null
+++ b/include/mtd/ubi-user.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+#ifndef __UBI_USER_H__
+#define __UBI_USER_H__
+
+/*
+ * UBI volume creation
+ * ~~~~~~~~~~~~~~~~~~~
+ *
+ * UBI volumes are created via the %UBI_IOCMKVOL IOCTL command of UBI character
+ * device. A &struct ubi_mkvol_req object has to be properly filled and a
+ * pointer to it has to be passed to the IOCTL.
+ *
+ * UBI volume deletion
+ * ~~~~~~~~~~~~~~~~~~~
+ *
+ * To delete a volume, the %UBI_IOCRMVOL IOCTL command of the UBI character
+ * device should be used. A pointer to the 32-bit volume ID hast to be passed
+ * to the IOCTL.
+ *
+ * UBI volume re-size
+ * ~~~~~~~~~~~~~~~~~~
+ *
+ * To re-size a volume, the %UBI_IOCRSVOL IOCTL command of the UBI character
+ * device should be used. A &struct ubi_rsvol_req object has to be properly
+ * filled and a pointer to it has to be passed to the IOCTL.
+ *
+ * UBI volume update
+ * ~~~~~~~~~~~~~~~~~
+ *
+ * Volume update should be done via the %UBI_IOCVOLUP IOCTL command of the
+ * corresponding UBI volume character device. A pointer to a 64-bit update
+ * size should be passed to the IOCTL. After then, UBI expects user to write
+ * this number of bytes to the volume character device. The update is finished
+ * when the claimed number of bytes is passed. So, the volume update sequence
+ * is something like:
+ *
+ * fd = open("/dev/my_volume");
+ * ioctl(fd, UBI_IOCVOLUP, &image_size);
+ * write(fd, buf, image_size);
+ * close(fd);
+ */
+
+/*
+ * When a new volume is created, users may either specify the volume number they
+ * want to create or to let UBI automatically assign a volume number using this
+ * constant.
+ */
+#define UBI_VOL_NUM_AUTO (-1)
+
+/* Maximum volume name length */
+#define UBI_MAX_VOLUME_NAME 127
+
+/* IOCTL commands of UBI character devices */
+
+#define UBI_IOC_MAGIC 'o'
+
+/* Create an UBI volume */
+#define UBI_IOCMKVOL _IOW(UBI_IOC_MAGIC, 0, struct ubi_mkvol_req)
+/* Remove an UBI volume */
+#define UBI_IOCRMVOL _IOW(UBI_IOC_MAGIC, 1, int32_t)
+/* Re-size an UBI volume */
+#define UBI_IOCRSVOL _IOW(UBI_IOC_MAGIC, 2, struct ubi_rsvol_req)
+
+/* IOCTL commands of UBI volume character devices */
+
+#define UBI_VOL_IOC_MAGIC 'O'
+
+/* Start UBI volume update */
+#define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, int64_t)
+/* An eraseblock erasure command, used for debugging, disabled by default */
+#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t)
+
+/*
+ * UBI volume type constants.
+ *
+ * @UBI_DYNAMIC_VOLUME: dynamic volume
+ * @UBI_STATIC_VOLUME:  static volume
+ */
+enum {
+	UBI_DYNAMIC_VOLUME = 3,
+	UBI_STATIC_VOLUME = 4
+};
+
+/**
+ * struct ubi_mkvol_req - volume description data structure used in
+ * volume creation requests.
+ * @vol_id: volume number
+ * @alignment: volume alignment
+ * @bytes: volume size in bytes
+ * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ * @padding1: reserved for future, not used
+ * @name_len: volume name length
+ * @padding2: reserved for future, not used
+ * @name: volume name
+ *
+ * This structure is used by userspace programs when creating new volumes. The
+ * @used_bytes field is only necessary when creating static volumes.
+ *
+ * The @alignment field specifies the required alignment of the volume logical
+ * eraseblock. This means, that the size of logical eraseblocks will be aligned
+ * to this number, i.e.,
+ *	(UBI device logical eraseblock size) mod (@alignment) = 0.
+ *
+ * To put it differently, the logical eraseblock of this volume may be slightly
+ * shortened in order to make it properly aligned. The alignment has to be
+ * multiple of the flash minimal input/output unit, or %1 to utilize the entire
+ * available space of logical eraseblocks.
+ *
+ * The @alignment field may be useful, for example, when one wants to maintain
+ * a block device on top of an UBI volume. In this case, it is desirable to fit
+ * an integer number of blocks in logical eraseblocks of this UBI volume. With
+ * alignment it is possible to update this volume using plane UBI volume image
+ * BLOBs, without caring about how to properly align them.
+ */
+struct ubi_mkvol_req {
+	int32_t vol_id;
+	int32_t alignment;
+	int64_t bytes;
+	int8_t vol_type;
+	int8_t padding1;
+	int16_t name_len;
+	int8_t padding2[4];
+	char name[UBI_MAX_VOLUME_NAME+1];
+} __attribute__ ((packed));
+
+/**
+ * struct ubi_rsvol_req - a data structure used in volume re-size requests.
+ * @vol_id: ID of the volume to re-size
+ * @bytes: new size of the volume in bytes
+ *
+ * Re-sizing is possible for both dynamic and static volumes. But while dynamic
+ * volumes may be re-sized arbitrarily, static volumes cannot be made to be
+ * smaller then the number of bytes they bear. To arbitrarily shrink a static
+ * volume, it must be wiped out first (by means of volume update operation with
+ * zero number of bytes).
+ */
+struct ubi_rsvol_req {
+	int64_t bytes;
+	int32_t vol_id;
+} __attribute__ ((packed));
+
+#endif /* __UBI_USER_H__ */
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 88df8fc..f3531d0 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -73,7 +73,9 @@
 extern int			ipv6_dev_get_saddr(struct net_device *dev, 
 					       struct in6_addr *daddr,
 					       struct in6_addr *saddr);
-extern int			ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
+extern int			ipv6_get_lladdr(struct net_device *dev,
+						struct in6_addr *addr,
+						unsigned char banned_flags);
 extern int			ipv6_rcv_saddr_equal(const struct sock *sk, 
 						      const struct sock *sk2);
 extern void			addrconf_join_solict(struct net_device *dev,
diff --git a/include/net/af_rxrpc.h b/include/net/af_rxrpc.h
new file mode 100644
index 0000000..00c2eaa
--- /dev/null
+++ b/include/net/af_rxrpc.h
@@ -0,0 +1,57 @@
+/* RxRPC kernel service interface definitions
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#ifndef _NET_RXRPC_H
+#define _NET_RXRPC_H
+
+#ifdef __KERNEL__
+
+#include <linux/rxrpc.h>
+
+struct rxrpc_call;
+
+/*
+ * the mark applied to socket buffers that may be intercepted
+ */
+enum {
+	RXRPC_SKB_MARK_DATA,		/* data message */
+	RXRPC_SKB_MARK_FINAL_ACK,	/* final ACK received message */
+	RXRPC_SKB_MARK_BUSY,		/* server busy message */
+	RXRPC_SKB_MARK_REMOTE_ABORT,	/* remote abort message */
+	RXRPC_SKB_MARK_NET_ERROR,	/* network error message */
+	RXRPC_SKB_MARK_LOCAL_ERROR,	/* local error message */
+	RXRPC_SKB_MARK_NEW_CALL,	/* local error message */
+};
+
+typedef void (*rxrpc_interceptor_t)(struct sock *, unsigned long,
+				    struct sk_buff *);
+extern void rxrpc_kernel_intercept_rx_messages(struct socket *,
+					       rxrpc_interceptor_t);
+extern struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *,
+						  struct sockaddr_rxrpc *,
+						  struct key *,
+						  unsigned long,
+						  gfp_t);
+extern int rxrpc_kernel_send_data(struct rxrpc_call *, struct msghdr *,
+				  size_t);
+extern void rxrpc_kernel_abort_call(struct rxrpc_call *, u32);
+extern void rxrpc_kernel_end_call(struct rxrpc_call *);
+extern bool rxrpc_kernel_is_data_last(struct sk_buff *);
+extern u32 rxrpc_kernel_get_abort_code(struct sk_buff *);
+extern int rxrpc_kernel_get_error_number(struct sk_buff *);
+extern void rxrpc_kernel_data_delivered(struct sk_buff *);
+extern void rxrpc_kernel_free_skb(struct sk_buff *);
+extern struct rxrpc_call *rxrpc_kernel_accept_call(struct socket *,
+						   unsigned long);
+extern int rxrpc_kernel_reject_call(struct socket *);
+
+#endif /* __KERNEL__ */
+#endif /* _NET_RXRPC_H */
diff --git a/include/net/ax25.h b/include/net/ax25.h
index 47ff2f4..99a4e36 100644
--- a/include/net/ax25.h
+++ b/include/net/ax25.h
@@ -263,8 +263,8 @@
 static inline __be16 ax25_type_trans(struct sk_buff *skb, struct net_device *dev)
 {
 	skb->dev      = dev;
+	skb_reset_mac_header(skb);
 	skb->pkt_type = PACKET_HOST;
-	skb->mac.raw  = skb->data;
 	return htons(ETH_P_AX25);
 }
 
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 41456c1..93ce272 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -709,6 +709,24 @@
 	__u8 	dlen;
 } __attribute__ ((packed));
 
+#ifdef __KERNEL__
+#include <linux/skbuff.h>
+static inline struct hci_event_hdr *hci_event_hdr(const struct sk_buff *skb)
+{
+	return (struct hci_event_hdr *)skb->data;
+}
+
+static inline struct hci_acl_hdr *hci_acl_hdr(const struct sk_buff *skb)
+{
+	return (struct hci_acl_hdr *)skb->data;
+}
+
+static inline struct hci_sco_hdr *hci_sco_hdr(const struct sk_buff *skb)
+{
+	return (struct hci_sco_hdr *)skb->data;
+}
+#endif
+
 /* Command opcode pack/unpack */
 #define hci_opcode_pack(ogf, ocf)	(__u16) ((ocf & 0x03ff)|(ogf << 10))
 #define hci_opcode_ogf(op)		(op >> 10)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
new file mode 100644
index 0000000..88171f8
--- /dev/null
+++ b/include/net/cfg80211.h
@@ -0,0 +1,40 @@
+#ifndef __NET_CFG80211_H
+#define __NET_CFG80211_H
+
+#include <linux/netlink.h>
+#include <linux/skbuff.h>
+#include <net/genetlink.h>
+
+/*
+ * 802.11 configuration in-kernel interface
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ */
+
+/* from net/wireless.h */
+struct wiphy;
+
+/**
+ * struct cfg80211_ops - backend description for wireless configuration
+ *
+ * This struct is registered by fullmac card drivers and/or wireless stacks
+ * in order to handle configuration requests on their interfaces.
+ *
+ * All callbacks except where otherwise noted should return 0
+ * on success or a negative error code.
+ *
+ * All operations are currently invoked under rtnl for consistency with the
+ * wireless extensions but this is subject to reevaluation as soon as this
+ * code is used more widely and we have a first user without wext.
+ *
+ * @add_virtual_intf: create a new virtual interface with the given name
+ *
+ * @del_virtual_intf: remove the virtual interface determined by ifindex.
+ */
+struct cfg80211_ops {
+	int	(*add_virtual_intf)(struct wiphy *wiphy, char *name,
+				    unsigned int type);
+	int	(*del_virtual_intf)(struct wiphy *wiphy, int ifindex);
+};
+
+#endif /* __NET_CFG80211_H */
diff --git a/include/net/cipso_ipv4.h b/include/net/cipso_ipv4.h
index 4c9522c..4f90f55 100644
--- a/include/net/cipso_ipv4.h
+++ b/include/net/cipso_ipv4.h
@@ -120,7 +120,7 @@
  */
 
 #define CIPSO_V4_OPTEXIST(x) (IPCB(x)->opt.cipso != 0)
-#define CIPSO_V4_OPTPTR(x) ((x)->nh.raw + IPCB(x)->opt.cipso)
+#define CIPSO_V4_OPTPTR(x) (skb_network_header(x) + IPCB(x)->opt.cipso)
 
 /*
  * DOI List Functions
diff --git a/include/net/compat.h b/include/net/compat.h
index 9859b60..406db24 100644
--- a/include/net/compat.h
+++ b/include/net/compat.h
@@ -25,6 +25,7 @@
 };
 
 extern int compat_sock_get_timestamp(struct sock *, struct timeval __user *);
+extern int compat_sock_get_timestampns(struct sock *, struct timespec __user *);
 
 #else /* defined(CONFIG_COMPAT) */
 #define compat_msghdr	msghdr		/* to avoid compiler warnings */
diff --git a/include/net/dn_fib.h b/include/net/dn_fib.h
index f01626c..3012511 100644
--- a/include/net/dn_fib.h
+++ b/include/net/dn_fib.h
@@ -148,17 +148,8 @@
 extern unsigned dnet_addr_type(__le16 addr);
 extern int dn_fib_lookup(struct flowi *fl, struct dn_fib_res *res);
 
-/*
- * rtnetlink interface
- */
-extern int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
-extern int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
 extern int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb);
 
-extern int dn_fib_rtm_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
-extern int dn_fib_rtm_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
-extern int dn_fib_dump_rules(struct sk_buff *skb, struct netlink_callback *cb);
-
 extern void dn_fib_free_info(struct dn_fib_info *fi);
 
 static inline void dn_fib_info_put(struct dn_fib_info *fi)
diff --git a/include/net/dn_route.h b/include/net/dn_route.h
index a566944..c10e8e7 100644
--- a/include/net/dn_route.h
+++ b/include/net/dn_route.h
@@ -18,7 +18,6 @@
 extern struct sk_buff *dn_alloc_skb(struct sock *sk, int size, gfp_t pri);
 extern int dn_route_output_sock(struct dst_entry **pprt, struct flowi *, struct sock *sk, int flags);
 extern int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb);
-extern int dn_cache_getroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
 extern void dn_rt_cache_flush(int delay);
 
 /* Masks for flags field */
diff --git a/include/net/esp.h b/include/net/esp.h
index 713d039f..d05d8d2 100644
--- a/include/net/esp.h
+++ b/include/net/esp.h
@@ -40,8 +40,6 @@
 	} auth;
 };
 
-extern int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len);
-extern int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer);
 extern void *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len);
 
 static inline int esp_mac_digest(struct esp_data *esp, struct sk_buff *skb,
diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h
index d585ea9..ed3a887 100644
--- a/include/net/fib_rules.h
+++ b/include/net/fib_rules.h
@@ -5,7 +5,7 @@
 #include <linux/netdevice.h>
 #include <linux/fib_rules.h>
 #include <net/flow.h>
-#include <net/netlink.h>
+#include <net/rtnetlink.h>
 
 struct fib_rule
 {
@@ -19,6 +19,8 @@
 	u32			flags;
 	u32			table;
 	u8			action;
+	u32			target;
+	struct fib_rule *	ctarget;
 	struct rcu_head		rcu;
 };
 
@@ -35,6 +37,8 @@
 	struct list_head	list;
 	int			rule_size;
 	int			addr_size;
+	int			unresolved_rules;
+	int			nr_goto_rules;
 
 	int			(*action)(struct fib_rule *,
 					  struct flowi *, int,
@@ -55,6 +59,10 @@
 	u32			(*default_pref)(void);
 	size_t			(*nlmsg_payload)(struct fib_rule *);
 
+	/* Called after modifications to the rules set, must flush
+	 * the route cache if one exists. */
+	void			(*flush_cache)(void);
+
 	int			nlgroup;
 	struct nla_policy	*policy;
 	struct list_head	*rules_list;
@@ -66,7 +74,8 @@
 	[FRA_PRIORITY]	= { .type = NLA_U32 }, \
 	[FRA_FWMARK]	= { .type = NLA_U32 }, \
 	[FRA_FWMASK]	= { .type = NLA_U32 }, \
-	[FRA_TABLE]     = { .type = NLA_U32 }
+	[FRA_TABLE]     = { .type = NLA_U32 }, \
+	[FRA_GOTO]	= { .type = NLA_U32 }
 
 static inline void fib_rule_get(struct fib_rule *rule)
 {
@@ -98,11 +107,4 @@
 extern int			fib_rules_lookup(struct fib_rules_ops *,
 						 struct flowi *, int flags,
 						 struct fib_lookup_arg *);
-
-extern int			fib_nl_newrule(struct sk_buff *,
-					       struct nlmsghdr *, void *);
-extern int			fib_nl_delrule(struct sk_buff *,
-					       struct nlmsghdr *, void *);
-extern int			fib_rules_dump(struct sk_buff *,
-					       struct netlink_callback *, int);
 #endif
diff --git a/include/net/flow.h b/include/net/flow.h
index ce4b10d..f3cc1f8 100644
--- a/include/net/flow.h
+++ b/include/net/flow.h
@@ -97,4 +97,10 @@
 extern void flow_cache_flush(void);
 extern atomic_t flow_cache_genid;
 
+static inline int flow_cache_uli_match(struct flowi *fl1, struct flowi *fl2)
+{
+	return (fl1->proto == fl2->proto &&
+		!memcmp(&fl1->uli_u, &fl2->uli_u, sizeof(fl1->uli_u)));
+}
+
 #endif
diff --git a/include/net/ieee80211.h b/include/net/ieee80211.h
index e02d85f..d56b292 100644
--- a/include/net/ieee80211.h
+++ b/include/net/ieee80211.h
@@ -6,8 +6,8 @@
  * LAN access point) driver for Intersil Prism2/2.5/3.
  *
  * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
- * <jkmaline@cc.hut.fi>
- * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * <j@w1.fi>
+ * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
  *
  * Adaption to a generic IEEE 802.11 stack by James Ketrenos
  * <jketreno@linux.intel.com>
diff --git a/include/net/ieee80211_crypt.h b/include/net/ieee80211_crypt.h
index eb476414..b3d65e0 100644
--- a/include/net/ieee80211_crypt.h
+++ b/include/net/ieee80211_crypt.h
@@ -3,8 +3,8 @@
  * for Intersil Prism2/2.5/3.
  *
  * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
- * <jkmaline@cc.hut.fi>
- * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * <j@w1.fi>
+ * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
  *
  * Adaption to a generic IEEE 802.11 stack by James Ketrenos
  * <jketreno@linux.intel.com>
diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h
index 429b738..a0c2b41 100644
--- a/include/net/ieee80211_radiotap.h
+++ b/include/net/ieee80211_radiotap.h
@@ -66,7 +66,9 @@
  */
 #define IEEE80211_RADIOTAP_HDRLEN	64
 
-/* The radio capture header precedes the 802.11 header. */
+/* The radio capture header precedes the 802.11 header.
+ * All data in the header is little endian on all platforms.
+ */
 struct ieee80211_radiotap_header {
 	u8 it_version;		/* Version 0. Only increases
 				 * for drastic changes,
@@ -74,12 +76,12 @@
 				 * new fields does not count.
 				 */
 	u8 it_pad;
-	u16 it_len;		/* length of the whole
+	__le16 it_len;		/* length of the whole
 				 * header in bytes, including
 				 * it_version, it_pad,
 				 * it_len, and data fields.
 				 */
-	u32 it_present;		/* A bitmap telling which
+	__le32 it_present;	/* A bitmap telling which
 				 * fields are present. Set bit 31
 				 * (0x80000000) to extend the
 				 * bitmap by another 32 bits.
@@ -88,89 +90,102 @@
 				 */
 };
 
-/* Name                                 Data type       Units
- * ----                                 ---------       -----
+/* Name                                 Data type    Units
+ * ----                                 ---------    -----
  *
- * IEEE80211_RADIOTAP_TSFT              u64       microseconds
+ * IEEE80211_RADIOTAP_TSFT              __le64       microseconds
  *
  *      Value in microseconds of the MAC's 64-bit 802.11 Time
  *      Synchronization Function timer when the first bit of the
  *      MPDU arrived at the MAC. For received frames, only.
  *
- * IEEE80211_RADIOTAP_CHANNEL           2 x u16   MHz, bitmap
+ * IEEE80211_RADIOTAP_CHANNEL           2 x __le16   MHz, bitmap
  *
  *      Tx/Rx frequency in MHz, followed by flags (see below).
  *
- * IEEE80211_RADIOTAP_FHSS              u16       see below
+ * IEEE80211_RADIOTAP_FHSS              __le16       see below
  *
  *      For frequency-hopping radios, the hop set (first byte)
  *      and pattern (second byte).
  *
- * IEEE80211_RADIOTAP_RATE              u8        500kb/s
+ * IEEE80211_RADIOTAP_RATE              u8           500kb/s
  *
  *      Tx/Rx data rate
  *
- * IEEE80211_RADIOTAP_DBM_ANTSIGNAL     int8_t          decibels from
- *                                                      one milliwatt (dBm)
+ * IEEE80211_RADIOTAP_DBM_ANTSIGNAL     s8           decibels from
+ *                                                   one milliwatt (dBm)
  *
  *      RF signal power at the antenna, decibel difference from
  *      one milliwatt.
  *
- * IEEE80211_RADIOTAP_DBM_ANTNOISE      int8_t          decibels from
- *                                                      one milliwatt (dBm)
+ * IEEE80211_RADIOTAP_DBM_ANTNOISE      s8           decibels from
+ *                                                   one milliwatt (dBm)
  *
  *      RF noise power at the antenna, decibel difference from one
  *      milliwatt.
  *
- * IEEE80211_RADIOTAP_DB_ANTSIGNAL      u8        decibel (dB)
+ * IEEE80211_RADIOTAP_DB_ANTSIGNAL      u8           decibel (dB)
  *
  *      RF signal power at the antenna, decibel difference from an
  *      arbitrary, fixed reference.
  *
- * IEEE80211_RADIOTAP_DB_ANTNOISE       u8        decibel (dB)
+ * IEEE80211_RADIOTAP_DB_ANTNOISE       u8           decibel (dB)
  *
  *      RF noise power at the antenna, decibel difference from an
  *      arbitrary, fixed reference point.
  *
- * IEEE80211_RADIOTAP_LOCK_QUALITY      u16       unitless
+ * IEEE80211_RADIOTAP_LOCK_QUALITY      __le16       unitless
  *
  *      Quality of Barker code lock. Unitless. Monotonically
  *      nondecreasing with "better" lock strength. Called "Signal
  *      Quality" in datasheets.  (Is there a standard way to measure
  *      this?)
  *
- * IEEE80211_RADIOTAP_TX_ATTENUATION    u16       unitless
+ * IEEE80211_RADIOTAP_TX_ATTENUATION    __le16       unitless
  *
  *      Transmit power expressed as unitless distance from max
  *      power set at factory calibration.  0 is max power.
  *      Monotonically nondecreasing with lower power levels.
  *
- * IEEE80211_RADIOTAP_DB_TX_ATTENUATION u16       decibels (dB)
+ * IEEE80211_RADIOTAP_DB_TX_ATTENUATION __le16       decibels (dB)
  *
  *      Transmit power expressed as decibel distance from max power
  *      set at factory calibration.  0 is max power.  Monotonically
  *      nondecreasing with lower power levels.
  *
- * IEEE80211_RADIOTAP_DBM_TX_POWER      int8_t          decibels from
- *                                                      one milliwatt (dBm)
+ * IEEE80211_RADIOTAP_DBM_TX_POWER      s8           decibels from
+ *                                                   one milliwatt (dBm)
  *
  *      Transmit power expressed as dBm (decibels from a 1 milliwatt
  *      reference). This is the absolute power level measured at
  *      the antenna port.
  *
- * IEEE80211_RADIOTAP_FLAGS             u8        bitmap
+ * IEEE80211_RADIOTAP_FLAGS             u8           bitmap
  *
  *      Properties of transmitted and received frames. See flags
  *      defined below.
  *
- * IEEE80211_RADIOTAP_ANTENNA           u8        antenna index
+ * IEEE80211_RADIOTAP_ANTENNA           u8           antenna index
  *
  *      Unitless indication of the Rx/Tx antenna for this packet.
  *      The first antenna is antenna 0.
  *
- * IEEE80211_RADIOTAP_FCS           	u32       data
+ * IEEE80211_RADIOTAP_RX_FLAGS          __le16       bitmap
  *
- *	FCS from frame in network byte order.
+ *     Properties of received frames. See flags defined below.
+ *
+ * IEEE80211_RADIOTAP_TX_FLAGS          __le16       bitmap
+ *
+ *     Properties of transmitted frames. See flags defined below.
+ *
+ * IEEE80211_RADIOTAP_RTS_RETRIES       u8           data
+ *
+ *     Number of rts retries a transmitted frame used.
+ *
+ * IEEE80211_RADIOTAP_DATA_RETRIES      u8           data
+ *
+ *     Number of unicast retries a transmitted frame used.
+ *
  */
 enum ieee80211_radiotap_type {
 	IEEE80211_RADIOTAP_TSFT = 0,
@@ -187,7 +202,11 @@
 	IEEE80211_RADIOTAP_ANTENNA = 11,
 	IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12,
 	IEEE80211_RADIOTAP_DB_ANTNOISE = 13,
-	IEEE80211_RADIOTAP_EXT = 31,
+	IEEE80211_RADIOTAP_RX_FLAGS = 14,
+	IEEE80211_RADIOTAP_TX_FLAGS = 15,
+	IEEE80211_RADIOTAP_RTS_RETRIES = 16,
+	IEEE80211_RADIOTAP_DATA_RETRIES = 17,
+	IEEE80211_RADIOTAP_EXT = 31
 };
 
 /* Channel flags. */
@@ -219,6 +238,14 @@
 						 * 802.11 header and payload
 						 * (to 32-bit boundary)
 						 */
+/* For IEEE80211_RADIOTAP_RX_FLAGS */
+#define IEEE80211_RADIOTAP_F_RX_BADFCS	0x0001	/* frame failed crc check */
+
+/* For IEEE80211_RADIOTAP_TX_FLAGS */
+#define IEEE80211_RADIOTAP_F_TX_FAIL	0x0001	/* failed due to excessive
+						 * retries */
+#define IEEE80211_RADIOTAP_F_TX_CTS	0x0002	/* used cts 'protection' */
+#define IEEE80211_RADIOTAP_F_TX_RTS	0x0004	/* used rts/cts handshake */
 
 /* Ugly macro to convert literal channel numbers into their mhz equivalents
  * There are certianly some conditions that will break this (like feeding it '30')
diff --git a/include/net/inet6_hashtables.h b/include/net/inet6_hashtables.h
index c28e424..668056b 100644
--- a/include/net/inet6_hashtables.h
+++ b/include/net/inet6_hashtables.h
@@ -19,6 +19,9 @@
 #include <linux/in6.h>
 #include <linux/ipv6.h>
 #include <linux/types.h>
+#include <linux/jhash.h>
+
+#include <net/inet_sock.h>
 
 #include <net/ipv6.h>
 
@@ -28,12 +31,11 @@
 static inline unsigned int inet6_ehashfn(const struct in6_addr *laddr, const u16 lport,
 				const struct in6_addr *faddr, const __be16 fport)
 {
-	unsigned int hashent = (lport ^ (__force u16)fport);
+	u32 ports = (lport ^ (__force u16)fport);
 
-	hashent ^= (__force u32)(laddr->s6_addr32[3] ^ faddr->s6_addr32[3]);
-	hashent ^= hashent >> 16;
-	hashent ^= hashent >> 8;
-	return hashent;
+	return jhash_3words((__force u32)laddr->s6_addr32[3],
+			    (__force u32)faddr->s6_addr32[3],
+			    ports, inet_ehash_secret);
 }
 
 static inline int inet6_sk_ehashfn(const struct sock *sk)
diff --git a/include/net/inet_ecn.h b/include/net/inet_ecn.h
index 10117c8..de8399a 100644
--- a/include/net/inet_ecn.h
+++ b/include/net/inet_ecn.h
@@ -114,13 +114,13 @@
 {
 	switch (skb->protocol) {
 	case __constant_htons(ETH_P_IP):
-		if (skb->nh.raw + sizeof(struct iphdr) <= skb->tail)
-			return IP_ECN_set_ce(skb->nh.iph);
+		if (skb->network_header + sizeof(struct iphdr) <= skb->tail)
+			return IP_ECN_set_ce(ip_hdr(skb));
 		break;
 
 	case __constant_htons(ETH_P_IPV6):
-		if (skb->nh.raw + sizeof(struct ipv6hdr) <= skb->tail)
-			return IP6_ECN_set_ce(skb->nh.ipv6h);
+		if (skb->network_header + sizeof(struct ipv6hdr) <= skb->tail)
+			return IP6_ECN_set_ce(ipv6_hdr(skb));
 		break;
 	}
 
diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h
index ce6da97..62daf21 100644
--- a/include/net/inet_sock.h
+++ b/include/net/inet_sock.h
@@ -19,6 +19,7 @@
 
 #include <linux/string.h>
 #include <linux/types.h>
+#include <linux/jhash.h>
 
 #include <net/flow.h>
 #include <net/sock.h>
@@ -167,13 +168,15 @@
 
 extern int inet_sk_rebuild_header(struct sock *sk);
 
+extern u32 inet_ehash_secret;
+extern void build_ehash_secret(void);
+
 static inline unsigned int inet_ehashfn(const __be32 laddr, const __u16 lport,
 					const __be32 faddr, const __be16 fport)
 {
-	unsigned int h = ((__force __u32)laddr ^ lport) ^ ((__force __u32)faddr ^ (__force __u32)fport);
-	h ^= h >> 16;
-	h ^= h >> 8;
-	return h;
+	return jhash_2words((__force __u32) laddr ^ (__force __u32) faddr,
+			    ((__u32) lport) << 16 | (__force __u32)fport,
+			    inet_ehash_secret);
 }
 
 static inline int inet_sk_ehashfn(const struct sock *sk)
diff --git a/include/net/ip.h b/include/net/ip.h
index e79c3e3..bb207db 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -25,6 +25,7 @@
 #include <linux/types.h>
 #include <linux/ip.h>
 #include <linux/in.h>
+#include <linux/skbuff.h>
 
 #include <net/inet_sock.h>
 #include <net/snmp.h>
@@ -43,6 +44,11 @@
 #define IPSKB_REROUTED		16
 };
 
+static inline unsigned int ip_hdrlen(const struct sk_buff *skb)
+{
+	return ip_hdr(skb)->ihl * 4;
+}
+
 struct ipcm_cookie
 {
 	__be32			addr;
@@ -74,7 +80,6 @@
 struct net_device;
 struct packet_type;
 struct rtable;
-struct sk_buff;
 struct sockaddr;
 
 extern void		ip_mc_dropsocket(struct sock *);
@@ -161,6 +166,10 @@
 #define NET_ADD_STATS_BH(field, adnd)	SNMP_ADD_STATS_BH(net_statistics, field, adnd)
 #define NET_ADD_STATS_USER(field, adnd)	SNMP_ADD_STATS_USER(net_statistics, field, adnd)
 
+extern unsigned long snmp_fold_field(void *mib[], int offt);
+extern int snmp_mib_init(void *ptr[2], size_t mibsize, size_t mibalign);
+extern void snmp_mib_free(void *ptr[2]);
+
 extern int sysctl_local_port_range[2];
 extern int sysctl_ip_default_ttl;
 extern int sysctl_ip_nonlocal_bind;
diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
index cf355a3..c48ea87 100644
--- a/include/net/ip6_fib.h
+++ b/include/net/ip6_fib.h
@@ -219,8 +219,6 @@
 
 extern void			fib6_rules_init(void);
 extern void			fib6_rules_cleanup(void);
-extern int			fib6_rules_dump(struct sk_buff *,
-						struct netlink_callback *);
 
 #endif
 #endif
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index 4e927eb..5456fdd 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -116,12 +116,7 @@
 						   struct net_device *dev,
 						   u32 pmtu);
 
-struct nlmsghdr;
 struct netlink_callback;
-extern int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb);
-extern int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg);
-extern int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg);
-extern int inet6_rtm_getroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg);
 
 struct rt6_rtnl_dump_arg
 {
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index 36c635c..5a4a0366 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -215,10 +215,6 @@
 /* Exported by fib_frontend.c */
 extern struct nla_policy rtm_ipv4_policy[];
 extern void		ip_fib_init(void);
-extern int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg);
-extern int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg);
-extern int inet_rtm_getroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg);
-extern int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb);
 extern int fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif,
 			       struct net_device *dev, __be32 *spec_dst, u32 *itag);
 extern void fib_select_multipath(const struct flowi *flp, struct fib_result *res);
@@ -235,8 +231,6 @@
 extern struct fib_table *fib_hash_init(u32 id);
 
 #ifdef CONFIG_IP_MULTIPLE_TABLES
-extern int fib4_rules_dump(struct sk_buff *skb, struct netlink_callback *cb);
-
 extern void __init fib4_rules_init(void);
 
 #ifdef CONFIG_NET_CLS_ROUTE
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 00328b7..f70afef 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -166,13 +166,6 @@
 	if (is_udplite) SNMP_INC_STATS_USER(udplite_stats_in6, field);         \
 	else		SNMP_INC_STATS_USER(udp_stats_in6, field);    } while(0)
 
-int snmp6_register_dev(struct inet6_dev *idev);
-int snmp6_unregister_dev(struct inet6_dev *idev);
-int snmp6_alloc_dev(struct inet6_dev *idev);
-int snmp6_free_dev(struct inet6_dev *idev);
-int snmp6_mib_init(void *ptr[2], size_t mibsize, size_t mibalign);
-void snmp6_mib_free(void *ptr[2]);
-
 struct ip6_ra_chain
 {
 	struct ip6_ra_chain	*next;
@@ -605,8 +598,20 @@
 extern void udplite6_proc_exit(void);
 extern int  ipv6_misc_proc_init(void);
 extern void ipv6_misc_proc_exit(void);
+extern int snmp6_register_dev(struct inet6_dev *idev);
+extern int snmp6_unregister_dev(struct inet6_dev *idev);
 
 extern struct rt6_statistics rt6_stats;
+#else
+static inline int snmp6_register_dev(struct inet6_dev *idev)
+{
+	return 0;
+}
+
+static inline int snmp6_unregister_dev(struct inet6_dev *idev)
+{
+	return 0;
+}
 #endif
 
 #ifdef CONFIG_SYSCTL
diff --git a/include/net/ipx.h b/include/net/ipx.h
index c6b2ee6..4cc0b4e 100644
--- a/include/net/ipx.h
+++ b/include/net/ipx.h
@@ -43,7 +43,7 @@
 
 static __inline__ struct ipxhdr *ipx_hdr(struct sk_buff *skb)
 {
-	return (struct ipxhdr *)skb->h.raw;
+	return (struct ipxhdr *)skb_transport_header(skb);
 }
 
 struct ipx_interface {
diff --git a/include/net/iucv/iucv.h b/include/net/iucv/iucv.h
index 746e741..fd70adb 100644
--- a/include/net/iucv/iucv.h
+++ b/include/net/iucv/iucv.h
@@ -16,7 +16,7 @@
  * completed a register, it can exploit the other functions.
  * For furthur reference on all IUCV functionality, refer to the
  * CP Programming Services book, also available on the web thru
- * www.ibm.com/s390/vm/pubs, manual # SC24-5760
+ * www.vm.ibm.com/pubs, manual # SC24-6084
  *
  * Definition of Return Codes
  * - All positive return codes including zero are reflected back
diff --git a/include/net/iw_handler.h b/include/net/iw_handler.h
index 8a83018..f23d07c 100644
--- a/include/net/iw_handler.h
+++ b/include/net/iw_handler.h
@@ -431,26 +431,7 @@
  * Those may be called only within the kernel.
  */
 
-/* First : function strictly used inside the kernel */
-
-/* Handle /proc/net/wireless, called in net/code/dev.c */
-extern int dev_get_wireless_info(char * buffer, char **start, off_t offset,
-				 int length);
-
-/* Handle IOCTLs, called in net/core/dev.c */
-extern int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd);
-
-/* Handle RtNetlink requests, called in net/core/rtnetlink.c */
-extern int wireless_rtnetlink_set(struct net_device *	dev,
-				  char *		data,
-				  int			len);
-extern int wireless_rtnetlink_get(struct net_device *	dev,
-				  char *		data,
-				  int			len,
-				  char **		p_buf,
-				  int *			p_len);
-
-/* Second : functions that may be called by driver modules */
+/* functions that may be called by driver modules */
 
 /* Send a single event to user space */
 extern void wireless_send_event(struct net_device *	dev,
diff --git a/include/net/llc_pdu.h b/include/net/llc_pdu.h
index aa33a47..4a8f58b 100644
--- a/include/net/llc_pdu.h
+++ b/include/net/llc_pdu.h
@@ -203,7 +203,7 @@
 
 static inline struct llc_pdu_sn *llc_pdu_sn_hdr(struct sk_buff *skb)
 {
-	return (struct llc_pdu_sn *)skb->nh.raw;
+	return (struct llc_pdu_sn *)skb_network_header(skb);
 }
 
 /* Un-numbered PDU format (3 bytes in length) */
@@ -215,12 +215,7 @@
 
 static inline struct llc_pdu_un *llc_pdu_un_hdr(struct sk_buff *skb)
 {
-	return (struct llc_pdu_un *)skb->nh.raw;
-}
-
-static inline void *llc_set_pdu_hdr(struct sk_buff *skb, void *ptr)
-{
-	return skb->nh.raw = ptr;
+	return (struct llc_pdu_un *)skb_network_header(skb);
 }
 
 /**
@@ -237,7 +232,11 @@
 				       u8 ssap, u8 dsap, u8 cr)
 {
 	const int hlen = type == LLC_PDU_TYPE_U ? 3 : 4;
-	struct llc_pdu_un *pdu = llc_set_pdu_hdr(skb, skb_push(skb, hlen));
+	struct llc_pdu_un *pdu;
+
+	skb_push(skb, hlen);
+	skb_reset_network_header(skb);
+	pdu = llc_pdu_un_hdr(skb);
 	pdu->dsap = dsap;
 	pdu->ssap = ssap;
 	pdu->ssap |= cr;
diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index ad7fe11..a4f2618 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -24,6 +24,7 @@
 
 #include <linux/err.h>
 #include <linux/sysctl.h>
+#include <net/rtnetlink.h>
 
 #define NUD_IN_TIMER	(NUD_INCOMPLETE|NUD_REACHABLE|NUD_DELAY|NUD_PROBE)
 #define NUD_VALID	(NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY)
@@ -213,16 +214,7 @@
 extern struct pneigh_entry	*pneigh_lookup(struct neigh_table *tbl, const void *key, struct net_device *dev, int creat);
 extern int			pneigh_delete(struct neigh_table *tbl, const void *key, struct net_device *dev);
 
-struct netlink_callback;
-struct nlmsghdr;
-extern int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb);
-extern int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
-extern int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
 extern void neigh_app_ns(struct neighbour *n);
-
-extern int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb);
-extern int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
-
 extern void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void *), void *cookie);
 extern void __neigh_for_each_release(struct neigh_table *tbl, int (*cb)(struct neighbour *));
 extern void pneigh_for_each(struct neigh_table *tbl, void (*cb)(struct pneigh_entry *));
diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h
index 0e690e3..1c6b8bd 100644
--- a/include/net/netfilter/nf_conntrack.h
+++ b/include/net/netfilter/nf_conntrack.h
@@ -250,6 +250,11 @@
 	return test_bit(IPS_DYING_BIT, &ct->status);
 }
 
+static inline int nf_ct_is_untracked(const struct sk_buff *skb)
+{
+	return (skb->nfct == &nf_conntrack_untracked.ct_general);
+}
+
 extern unsigned int nf_conntrack_htable_size;
 extern int nf_conntrack_checksum;
 extern atomic_t nf_conntrack_count;
diff --git a/include/net/netfilter/nf_conntrack_compat.h b/include/net/netfilter/nf_conntrack_compat.h
deleted file mode 100644
index 6f84c1f..0000000
--- a/include/net/netfilter/nf_conntrack_compat.h
+++ /dev/null
@@ -1,145 +0,0 @@
-#ifndef _NF_CONNTRACK_COMPAT_H
-#define _NF_CONNTRACK_COMPAT_H
-
-#ifdef __KERNEL__
-
-#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE)
-
-#include <linux/netfilter_ipv4/ip_conntrack.h>
-#include <linux/socket.h>
-
-#ifdef CONFIG_IP_NF_CONNTRACK_MARK
-static inline u_int32_t *nf_ct_get_mark(const struct sk_buff *skb,
-					u_int32_t *ctinfo)
-{
-	struct ip_conntrack *ct = ip_conntrack_get(skb, ctinfo);
-
-	if (ct)
-		return &ct->mark;
-	else
-		return NULL;
-}
-#endif /* CONFIG_IP_NF_CONNTRACK_MARK */
-
-#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK
-static inline u_int32_t *nf_ct_get_secmark(const struct sk_buff *skb,
-					   u_int32_t *ctinfo)
-{
-	struct ip_conntrack *ct = ip_conntrack_get(skb, ctinfo);
-
-	if (ct)
-		return &ct->secmark;
-	else
-		return NULL;
-}
-#endif /* CONFIG_IP_NF_CONNTRACK_SECMARK */
-
-#ifdef CONFIG_IP_NF_CT_ACCT
-static inline struct ip_conntrack_counter *
-nf_ct_get_counters(const struct sk_buff *skb)
-{
-	enum ip_conntrack_info ctinfo;
-	struct ip_conntrack *ct = ip_conntrack_get(skb, &ctinfo);
-
-	if (ct)
-		return ct->counters;
-	else
-		return NULL;
-}
-#endif /* CONFIG_IP_NF_CT_ACCT */
-
-static inline int nf_ct_is_untracked(const struct sk_buff *skb)
-{
-	return (skb->nfct == &ip_conntrack_untracked.ct_general);
-}
-
-static inline void nf_ct_untrack(struct sk_buff *skb)
-{
-	skb->nfct = &ip_conntrack_untracked.ct_general;
-}
-
-static inline int nf_ct_get_ctinfo(const struct sk_buff *skb,
-				   enum ip_conntrack_info *ctinfo)
-{
-	struct ip_conntrack *ct = ip_conntrack_get(skb, ctinfo);
-	return (ct != NULL);
-}
-
-static inline int nf_ct_l3proto_try_module_get(unsigned short l3proto)
-{
-	need_conntrack();
-	return l3proto == PF_INET ? 0 : -1;
-}
-
-static inline void nf_ct_l3proto_module_put(unsigned short l3proto)
-{
-}
-
-#else /* CONFIG_IP_NF_CONNTRACK */
-
-#include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
-#include <net/netfilter/nf_conntrack.h>
-
-#ifdef CONFIG_NF_CONNTRACK_MARK
-
-static inline u_int32_t *nf_ct_get_mark(const struct sk_buff *skb,
-					u_int32_t *ctinfo)
-{
-	struct nf_conn *ct = nf_ct_get(skb, ctinfo);
-
-	if (ct)
-		return &ct->mark;
-	else
-		return NULL;
-}
-#endif /* CONFIG_NF_CONNTRACK_MARK */
-
-#ifdef CONFIG_NF_CONNTRACK_SECMARK
-static inline u_int32_t *nf_ct_get_secmark(const struct sk_buff *skb,
-					   u_int32_t *ctinfo)
-{
-	struct nf_conn *ct = nf_ct_get(skb, ctinfo);
-
-	if (ct)
-		return &ct->secmark;
-	else
-		return NULL;
-}
-#endif /* CONFIG_NF_CONNTRACK_MARK */
-
-#ifdef CONFIG_NF_CT_ACCT
-static inline struct ip_conntrack_counter *
-nf_ct_get_counters(const struct sk_buff *skb)
-{
-	enum ip_conntrack_info ctinfo;
-	struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
-
-	if (ct)
-		return ct->counters;
-	else
-		return NULL;
-}
-#endif /* CONFIG_NF_CT_ACCT */
-
-static inline int nf_ct_is_untracked(const struct sk_buff *skb)
-{
-	return (skb->nfct == &nf_conntrack_untracked.ct_general);
-}
-
-static inline void nf_ct_untrack(struct sk_buff *skb)
-{
-	skb->nfct = &nf_conntrack_untracked.ct_general;
-}
-
-static inline int nf_ct_get_ctinfo(const struct sk_buff *skb,
-				   enum ip_conntrack_info *ctinfo)
-{
-	struct nf_conn *ct = nf_ct_get(skb, ctinfo);
-	return (ct != NULL);
-}
-
-#endif /* CONFIG_IP_NF_CONNTRACK */
-
-#endif /* __KERNEL__ */
-
-#endif /* _NF_CONNTRACK_COMPAT_H */
diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h
index 85634e1..9fb9066 100644
--- a/include/net/netfilter/nf_conntrack_core.h
+++ b/include/net/netfilter/nf_conntrack_core.h
@@ -27,6 +27,9 @@
 extern int nf_conntrack_init(void);
 extern void nf_conntrack_cleanup(void);
 
+extern int nf_conntrack_proto_init(void);
+extern void nf_conntrack_proto_fini(void);
+
 struct nf_conntrack_l3proto;
 extern struct nf_conntrack_l3proto *nf_ct_find_l3proto(u_int16_t pf);
 /* Like above, but you already have conntrack read lock. */
diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h
index b62a8a9..811c907 100644
--- a/include/net/netfilter/nf_conntrack_ecache.h
+++ b/include/net/netfilter/nf_conntrack_ecache.h
@@ -20,30 +20,8 @@
 #define CONNTRACK_ECACHE(x)	(__get_cpu_var(nf_conntrack_ecache).x)
 
 extern struct atomic_notifier_head nf_conntrack_chain;
-extern struct atomic_notifier_head nf_conntrack_expect_chain;
-
-static inline int nf_conntrack_register_notifier(struct notifier_block *nb)
-{
-	return atomic_notifier_chain_register(&nf_conntrack_chain, nb);
-}
-
-static inline int nf_conntrack_unregister_notifier(struct notifier_block *nb)
-{
-	return atomic_notifier_chain_unregister(&nf_conntrack_chain, nb);
-}
-
-static inline int
-nf_conntrack_expect_register_notifier(struct notifier_block *nb)
-{
-	return atomic_notifier_chain_register(&nf_conntrack_expect_chain, nb);
-}
-
-static inline int
-nf_conntrack_expect_unregister_notifier(struct notifier_block *nb)
-{
-	return atomic_notifier_chain_unregister(&nf_conntrack_expect_chain,
-			nb);
-}
+extern int nf_conntrack_register_notifier(struct notifier_block *nb);
+extern int nf_conntrack_unregister_notifier(struct notifier_block *nb);
 
 extern void nf_ct_deliver_cached_events(const struct nf_conn *ct);
 extern void __nf_ct_event_cache_init(struct nf_conn *ct);
@@ -71,6 +49,10 @@
 		atomic_notifier_call_chain(&nf_conntrack_chain, event, ct);
 }
 
+extern struct atomic_notifier_head nf_conntrack_expect_chain;
+extern int nf_conntrack_expect_register_notifier(struct notifier_block *nb);
+extern int nf_conntrack_expect_unregister_notifier(struct notifier_block *nb);
+
 static inline void
 nf_conntrack_expect_event(enum ip_conntrack_expect_events event,
 			  struct nf_conntrack_expect *exp)
diff --git a/include/net/netfilter/nf_conntrack_l3proto.h b/include/net/netfilter/nf_conntrack_l3proto.h
index eb575cb..f32f714 100644
--- a/include/net/netfilter/nf_conntrack_l3proto.h
+++ b/include/net/netfilter/nf_conntrack_l3proto.h
@@ -90,10 +90,7 @@
 /* Protocol registration. */
 extern int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto);
 extern void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto);
-
-extern struct nf_conntrack_l3proto *
-nf_ct_l3proto_find_get(u_int16_t l3proto);
-
+extern struct nf_conntrack_l3proto *nf_ct_l3proto_find_get(u_int16_t l3proto);
 extern void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p);
 
 /* Existing built-in protocols */
diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h
index 8415182..f46cb93 100644
--- a/include/net/netfilter/nf_conntrack_l4proto.h
+++ b/include/net/netfilter/nf_conntrack_l4proto.h
@@ -97,7 +97,6 @@
 extern struct nf_conntrack_l4proto nf_conntrack_l4proto_generic;
 
 #define MAX_NF_CT_PROTO 256
-extern struct nf_conntrack_l4proto **nf_ct_protos[PF_MAX];
 
 extern struct nf_conntrack_l4proto *
 __nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto);
diff --git a/include/net/netfilter/nf_nat_rule.h b/include/net/netfilter/nf_nat_rule.h
index f191c67..e765654 100644
--- a/include/net/netfilter/nf_nat_rule.h
+++ b/include/net/netfilter/nf_nat_rule.h
@@ -4,16 +4,6 @@
 #include <net/netfilter/nf_nat.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
 
-/* Compatibility definitions for ipt_FOO modules */
-#define ip_nat_range			nf_nat_range
-#define ip_conntrack_tuple		nf_conntrack_tuple
-#define ip_conntrack_get		nf_ct_get
-#define ip_conntrack			nf_conn
-#define ip_nat_setup_info		nf_nat_setup_info
-#define ip_nat_multi_range_compat	nf_nat_multi_range_compat
-#define ip_ct_iterate_cleanup		nf_ct_iterate_cleanup
-#define	IP_NF_ASSERT			NF_CT_ASSERT
-
 extern int nf_nat_rule_init(void) __init;
 extern void nf_nat_rule_cleanup(void);
 extern int nf_nat_rule_find(struct sk_buff **pskb,
diff --git a/include/net/netlink.h b/include/net/netlink.h
index bcaf67b..0bf325c 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -171,6 +171,7 @@
 	NLA_MSECS,
 	NLA_NESTED,
 	NLA_NUL_STRING,
+	NLA_BINARY,
 	__NLA_TYPE_MAX,
 };
 
@@ -188,12 +189,13 @@
  *    NLA_STRING           Maximum length of string
  *    NLA_NUL_STRING       Maximum length of string (excluding NUL)
  *    NLA_FLAG             Unused
+ *    NLA_BINARY           Maximum length of attribute payload
  *    All other            Exact length of attribute payload
  *
  * Example:
  * static struct nla_policy my_policy[ATTR_MAX+1] __read_mostly = {
  * 	[ATTR_FOO] = { .type = NLA_U16 },
- *	[ATTR_BAR] = { .type = NLA_STRING, len = BARSIZ },
+ *	[ATTR_BAR] = { .type = NLA_STRING, .len = BARSIZ },
  *	[ATTR_BAZ] = { .len = sizeof(struct mystruct) },
  * };
  */
@@ -214,9 +216,7 @@
 
 extern void		netlink_run_queue(struct sock *sk, unsigned int *qlen,
 					  int (*cb)(struct sk_buff *,
-						    struct nlmsghdr *, int *));
-extern void		netlink_queue_skip(struct nlmsghdr *nlh,
-					   struct sk_buff *skb);
+						    struct nlmsghdr *));
 extern int		nlmsg_notify(struct sock *sk, struct sk_buff *skb,
 				     u32 pid, unsigned int group, int report,
 				     gfp_t flags);
@@ -525,7 +525,7 @@
  */
 static inline int nlmsg_end(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
-	nlh->nlmsg_len = skb->tail - (unsigned char *) nlh;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - (unsigned char *)nlh;
 
 	return skb->len;
 }
@@ -538,7 +538,7 @@
  */
 static inline void *nlmsg_get_pos(struct sk_buff *skb)
 {
-	return skb->tail;
+	return skb_tail_pointer(skb);
 }
 
 /**
@@ -548,7 +548,7 @@
  *
  * Trims the message to the provided mark. Returns -1.
  */
-static inline int nlmsg_trim(struct sk_buff *skb, void *mark)
+static inline int nlmsg_trim(struct sk_buff *skb, const void *mark)
 {
 	if (mark)
 		skb_trim(skb, (unsigned char *) mark - skb->data);
@@ -940,7 +940,7 @@
  */
 static inline struct nlattr *nla_nest_start(struct sk_buff *skb, int attrtype)
 {
-	struct nlattr *start = (struct nlattr *) skb->tail;
+	struct nlattr *start = (struct nlattr *)skb_tail_pointer(skb);
 
 	if (nla_put(skb, attrtype, 0, NULL) < 0)
 		return NULL;
@@ -960,7 +960,7 @@
  */
 static inline int nla_nest_end(struct sk_buff *skb, struct nlattr *start)
 {
-	start->nla_len = skb->tail - (unsigned char *) start;
+	start->nla_len = skb_tail_pointer(skb) - (unsigned char *)start;
 	return skb->len;
 }
 
diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
index 02647fe..4129df7 100644
--- a/include/net/pkt_cls.h
+++ b/include/net/pkt_cls.h
@@ -326,18 +326,18 @@
 		case TCF_LAYER_LINK:
 			return skb->data;
 		case TCF_LAYER_NETWORK:
-			return skb->nh.raw;
+			return skb_network_header(skb);
 		case TCF_LAYER_TRANSPORT:
-			return skb->h.raw;
+			return skb_transport_header(skb);
 	}
 
 	return NULL;
 }
 
-static inline int tcf_valid_offset(struct sk_buff *skb, unsigned char *ptr,
-				   int len)
+static inline int tcf_valid_offset(const struct sk_buff *skb,
+				   const unsigned char *ptr, const int len)
 {
-	return unlikely((ptr + len) < skb->tail && ptr > skb->head);
+	return unlikely((ptr + len) < skb_tail_pointer(skb) && ptr > skb->head);
 }
 
 #ifdef CONFIG_NET_CLS_IND
diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h
index f6afee7..5754d53 100644
--- a/include/net/pkt_sched.h
+++ b/include/net/pkt_sched.h
@@ -2,6 +2,7 @@
 #define __NET_PKT_SCHED_H
 
 #include <linux/jiffies.h>
+#include <linux/ktime.h>
 #include <net/sch_generic.h>
 
 struct qdisc_walker
@@ -12,8 +13,6 @@
 	int	(*fn)(struct Qdisc *, unsigned long cl, struct qdisc_walker *);
 };
 
-extern rwlock_t qdisc_tree_lock;
-
 #define QDISC_ALIGNTO		32
 #define QDISC_ALIGN(len)	(((len) + QDISC_ALIGNTO-1) & ~(QDISC_ALIGNTO-1))
 
@@ -37,175 +36,38 @@
    The things are not so bad, because we may use artifical
    clock evaluated by integration of network data flow
    in the most critical places.
-
-   Note: we do not use fastgettimeofday.
-   The reason is that, when it is not the same thing as
-   gettimeofday, it returns invalid timestamp, which is
-   not updated, when net_bh is active.
  */
 
-/* General note about internal clock.
-
-   Any clock source returns time intervals, measured in units
-   close to 1usec. With source CONFIG_NET_SCH_CLK_GETTIMEOFDAY it is precisely
-   microseconds, otherwise something close but different chosen to minimize
-   arithmetic cost. Ratio usec/internal untis in form nominator/denominator
-   may be read from /proc/net/psched.
- */
-
-
-#ifdef CONFIG_NET_SCH_CLK_GETTIMEOFDAY
-
-typedef struct timeval	psched_time_t;
-typedef long		psched_tdiff_t;
-
-#define PSCHED_GET_TIME(stamp) do_gettimeofday(&(stamp))
-#define PSCHED_US2JIFFIE(usecs) usecs_to_jiffies(usecs)
-#define PSCHED_JIFFIE2US(delay) jiffies_to_usecs(delay)
-
-#else /* !CONFIG_NET_SCH_CLK_GETTIMEOFDAY */
-
 typedef u64	psched_time_t;
 typedef long	psched_tdiff_t;
 
-#ifdef CONFIG_NET_SCH_CLK_JIFFIES
+/* Avoid doing 64 bit divide by 1000 */
+#define PSCHED_US2NS(x)			((s64)(x) << 10)
+#define PSCHED_NS2US(x)			((x) >> 10)
 
-#if HZ < 96
-#define PSCHED_JSCALE 14
-#elif HZ >= 96 && HZ < 192
-#define PSCHED_JSCALE 13
-#elif HZ >= 192 && HZ < 384
-#define PSCHED_JSCALE 12
-#elif HZ >= 384 && HZ < 768
-#define PSCHED_JSCALE 11
-#elif HZ >= 768
-#define PSCHED_JSCALE 10
-#endif
+#define PSCHED_TICKS_PER_SEC		PSCHED_NS2US(NSEC_PER_SEC)
+#define PSCHED_PASTPERFECT		0
 
-#define PSCHED_GET_TIME(stamp) ((stamp) = (get_jiffies_64()<<PSCHED_JSCALE))
-#define PSCHED_US2JIFFIE(delay) (((delay)+(1<<PSCHED_JSCALE)-1)>>PSCHED_JSCALE)
-#define PSCHED_JIFFIE2US(delay) ((delay)<<PSCHED_JSCALE)
-
-#endif /* CONFIG_NET_SCH_CLK_JIFFIES */
-#ifdef CONFIG_NET_SCH_CLK_CPU
-#include <asm/timex.h>
-
-extern psched_tdiff_t psched_clock_per_hz;
-extern int psched_clock_scale;
-extern psched_time_t psched_time_base;
-extern cycles_t psched_time_mark;
-
-#define PSCHED_GET_TIME(stamp)						\
-do {									\
-	cycles_t cur = get_cycles();					\
-	if (sizeof(cycles_t) == sizeof(u32)) {				\
-		if (cur <= psched_time_mark)				\
-			psched_time_base += 0x100000000ULL;		\
-		psched_time_mark = cur;					\
-		(stamp) = (psched_time_base + cur)>>psched_clock_scale;	\
-	} else {							\
-		(stamp) = cur>>psched_clock_scale;			\
-	}								\
-} while (0)
-#define PSCHED_US2JIFFIE(delay) (((delay)+psched_clock_per_hz-1)/psched_clock_per_hz)
-#define PSCHED_JIFFIE2US(delay) ((delay)*psched_clock_per_hz)
-
-#endif /* CONFIG_NET_SCH_CLK_CPU */
-
-#endif /* !CONFIG_NET_SCH_CLK_GETTIMEOFDAY */
-
-#ifdef CONFIG_NET_SCH_CLK_GETTIMEOFDAY
-#define PSCHED_TDIFF(tv1, tv2) \
-({ \
-	   int __delta_sec = (tv1).tv_sec - (tv2).tv_sec; \
-	   int __delta = (tv1).tv_usec - (tv2).tv_usec; \
-	   if (__delta_sec) { \
-	           switch (__delta_sec) { \
-		   default: \
-			   __delta = 0; \
-		   case 2: \
-			   __delta += USEC_PER_SEC; \
-		   case 1: \
-			   __delta += USEC_PER_SEC; \
-	           } \
-	   } \
-	   __delta; \
-})
-
-static inline int
-psched_tod_diff(int delta_sec, int bound)
+static inline psched_time_t psched_get_time(void)
 {
-	int delta;
-
-	if (bound <= USEC_PER_SEC || delta_sec > (0x7FFFFFFF/USEC_PER_SEC)-1)
-		return bound;
-	delta = delta_sec * USEC_PER_SEC;
-	if (delta > bound || delta < 0)
-		delta = bound;
-	return delta;
+	return PSCHED_NS2US(ktime_to_ns(ktime_get()));
 }
 
-#define PSCHED_TDIFF_SAFE(tv1, tv2, bound) \
-({ \
-	   int __delta_sec = (tv1).tv_sec - (tv2).tv_sec; \
-	   int __delta = (tv1).tv_usec - (tv2).tv_usec; \
-	   switch (__delta_sec) { \
-	   default: \
-		   __delta = psched_tod_diff(__delta_sec, bound);  break; \
-	   case 2: \
-		   __delta += USEC_PER_SEC; \
-	   case 1: \
-		   __delta += USEC_PER_SEC; \
-	   case 0: \
- 		   if (__delta > bound || __delta < 0) \
- 			__delta = bound; \
-	   } \
-	   __delta; \
-})
+static inline psched_tdiff_t
+psched_tdiff_bounded(psched_time_t tv1, psched_time_t tv2, psched_time_t bound)
+{
+	return min(tv1 - tv2, bound);
+}
 
-#define PSCHED_TLESS(tv1, tv2) (((tv1).tv_usec < (tv2).tv_usec && \
-				(tv1).tv_sec <= (tv2).tv_sec) || \
-				 (tv1).tv_sec < (tv2).tv_sec)
+struct qdisc_watchdog {
+	struct hrtimer	timer;
+	struct Qdisc	*qdisc;
+};
 
-#define PSCHED_TADD2(tv, delta, tv_res) \
-({ \
-	   int __delta = (tv).tv_usec + (delta); \
-	   (tv_res).tv_sec = (tv).tv_sec; \
-	   while (__delta >= USEC_PER_SEC) { (tv_res).tv_sec++; __delta -= USEC_PER_SEC; } \
-	   (tv_res).tv_usec = __delta; \
-})
-
-#define PSCHED_TADD(tv, delta) \
-({ \
-	   (tv).tv_usec += (delta); \
-	   while ((tv).tv_usec >= USEC_PER_SEC) { (tv).tv_sec++; \
-		 (tv).tv_usec -= USEC_PER_SEC; } \
-})
-
-/* Set/check that time is in the "past perfect";
-   it depends on concrete representation of system time
- */
-
-#define PSCHED_SET_PASTPERFECT(t)	((t).tv_sec = 0)
-#define PSCHED_IS_PASTPERFECT(t)	((t).tv_sec == 0)
-
-#define	PSCHED_AUDIT_TDIFF(t) ({ if ((t) > 2000000) (t) = 2000000; })
-
-#else /* !CONFIG_NET_SCH_CLK_GETTIMEOFDAY */
-
-#define PSCHED_TDIFF(tv1, tv2) (long)((tv1) - (tv2))
-#define PSCHED_TDIFF_SAFE(tv1, tv2, bound) \
-	min_t(long long, (tv1) - (tv2), bound)
-
-
-#define PSCHED_TLESS(tv1, tv2) ((tv1) < (tv2))
-#define PSCHED_TADD2(tv, delta, tv_res) ((tv_res) = (tv) + (delta))
-#define PSCHED_TADD(tv, delta) ((tv) += (delta))
-#define PSCHED_SET_PASTPERFECT(t)	((t) = 0)
-#define PSCHED_IS_PASTPERFECT(t)	((t) == 0)
-#define	PSCHED_AUDIT_TDIFF(t)
-
-#endif /* !CONFIG_NET_SCH_CLK_GETTIMEOFDAY */
+extern void qdisc_watchdog_init(struct qdisc_watchdog *wd, struct Qdisc *qdisc);
+extern void qdisc_watchdog_schedule(struct qdisc_watchdog *wd,
+				    psched_time_t expires);
+extern void qdisc_watchdog_cancel(struct qdisc_watchdog *wd);
 
 extern struct Qdisc_ops pfifo_qdisc_ops;
 extern struct Qdisc_ops bfifo_qdisc_ops;
diff --git a/include/net/red.h b/include/net/red.h
index a4eb379..3cf31d4 100644
--- a/include/net/red.h
+++ b/include/net/red.h
@@ -151,17 +151,17 @@
 
 static inline int red_is_idling(struct red_parms *p)
 {
-	return !PSCHED_IS_PASTPERFECT(p->qidlestart);
+	return p->qidlestart != PSCHED_PASTPERFECT;
 }
 
 static inline void red_start_of_idle_period(struct red_parms *p)
 {
-	PSCHED_GET_TIME(p->qidlestart);
+	p->qidlestart = psched_get_time();
 }
 
 static inline void red_end_of_idle_period(struct red_parms *p)
 {
-	PSCHED_SET_PASTPERFECT(p->qidlestart);
+	p->qidlestart = PSCHED_PASTPERFECT;
 }
 
 static inline void red_restart(struct red_parms *p)
@@ -177,8 +177,8 @@
 	long us_idle;
 	int  shift;
 
-	PSCHED_GET_TIME(now);
-	us_idle = PSCHED_TDIFF_SAFE(now, p->qidlestart, p->Scell_max);
+	now = psched_get_time();
+	us_idle = psched_tdiff_bounded(now, p->qidlestart, p->Scell_max);
 
 	/*
 	 * The problem: ideally, average length queue recalcultion should
diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h
new file mode 100644
index 0000000..3b3d474
--- /dev/null
+++ b/include/net/rtnetlink.h
@@ -0,0 +1,25 @@
+#ifndef __NET_RTNETLINK_H
+#define __NET_RTNETLINK_H
+
+#include <linux/rtnetlink.h>
+#include <net/netlink.h>
+
+typedef int (*rtnl_doit_func)(struct sk_buff *, struct nlmsghdr *, void *);
+typedef int (*rtnl_dumpit_func)(struct sk_buff *, struct netlink_callback *);
+
+extern int	__rtnl_register(int protocol, int msgtype,
+				rtnl_doit_func, rtnl_dumpit_func);
+extern void	rtnl_register(int protocol, int msgtype,
+			      rtnl_doit_func, rtnl_dumpit_func);
+extern int	rtnl_unregister(int protocol, int msgtype);
+extern void	rtnl_unregister_all(int protocol);
+
+static inline int rtnl_msg_family(struct nlmsghdr *nlh)
+{
+	if (nlmsg_len(nlh) >= sizeof(struct rtgenmsg))
+		return ((struct rtgenmsg *) nlmsg_data(nlh))->rtgen_family;
+	else
+		return AF_UNSPEC;
+}
+
+#endif
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index 8208639..1b8e351 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -5,10 +5,10 @@
 #include <linux/types.h>
 #include <linux/rcupdate.h>
 #include <linux/module.h>
-#include <linux/rtnetlink.h>
 #include <linux/pkt_sched.h>
 #include <linux/pkt_cls.h>
 #include <net/gen_stats.h>
+#include <net/rtnetlink.h>
 
 struct Qdisc_ops;
 struct qdisc_walker;
@@ -177,14 +177,8 @@
 extern struct Qdisc *qdisc_alloc(struct net_device *dev, struct Qdisc_ops *ops);
 extern struct Qdisc *qdisc_create_dflt(struct net_device *dev,
 				       struct Qdisc_ops *ops, u32 parentid);
-
-static inline void
-tcf_destroy(struct tcf_proto *tp)
-{
-	tp->ops->destroy(tp);
-	module_put(tp->ops->owner);
-	kfree(tp);
-}
+extern void tcf_destroy(struct tcf_proto *tp);
+extern void tcf_destroy_chain(struct tcf_proto *fl);
 
 static inline int __qdisc_enqueue_tail(struct sk_buff *skb, struct Qdisc *sch,
 				       struct sk_buff_head *list)
diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h
index 5ddb855..bb37724 100644
--- a/include/net/sctp/constants.h
+++ b/include/net/sctp/constants.h
@@ -283,7 +283,7 @@
 #define SCTP_RTO_BETA           2   /* 1/4 when converted to right shifts. */
 
 /* Maximum number of new data packets that can be sent in a burst.  */
-#define SCTP_MAX_BURST		4
+#define SCTP_DEFAULT_MAX_BURST		4
 
 #define SCTP_CLOCK_GRANULARITY	1	/* 1 jiffy */
 
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index f431acf..7b4fff9 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -276,6 +276,7 @@
 	__u32 default_context;
 	__u32 default_timetolive;
 	__u32 default_rcv_context;
+	int max_burst;
 
 	/* Heartbeat interval: The endpoint sends out a Heartbeat chunk to
 	 * the destination address every heartbeat interval. This value
@@ -304,10 +305,12 @@
 	__u32 autoclose;
 	__u8 nodelay;
 	__u8 disable_fragments;
-	__u8 pd_mode;
 	__u8 v4mapped;
+	__u8 frag_interleave;
 	__u32 adaptation_ind;
+	__u32 pd_point;
 
+	atomic_t pd_mode;
 	/* Receive to here while partial delivery is in effect. */
 	struct sk_buff_head pd_lobby;
 };
diff --git a/include/net/sctp/ulpevent.h b/include/net/sctp/ulpevent.h
index 2923e3d3..de88ed5 100644
--- a/include/net/sctp/ulpevent.h
+++ b/include/net/sctp/ulpevent.h
@@ -89,6 +89,7 @@
 	__u16 error,
 	__u16 outbound,
 	__u16 inbound,
+	struct sctp_chunk *chunk,
 	gfp_t gfp);
 
 struct sctp_ulpevent *sctp_ulpevent_make_peer_addr_change(
diff --git a/include/net/sctp/ulpqueue.h b/include/net/sctp/ulpqueue.h
index ab26ab3..39ea3f4 100644
--- a/include/net/sctp/ulpqueue.h
+++ b/include/net/sctp/ulpqueue.h
@@ -78,7 +78,7 @@
 void sctp_ulpq_abort_pd(struct sctp_ulpq *, gfp_t);
 
 /* Clear the partial data delivery condition on this socket. */
-int sctp_clear_pd(struct sock *sk);
+int sctp_clear_pd(struct sock *sk, struct sctp_association *asoc);
 
 /* Skip over an SSN. */
 void sctp_ulpq_skip(struct sctp_ulpq *ulpq, __u16 sid, __u16 ssn);
diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h
index 67a30eb..6d2b577 100644
--- a/include/net/sctp/user.h
+++ b/include/net/sctp/user.h
@@ -97,6 +97,12 @@
 #define SCTP_DELAYED_ACK_TIME SCTP_DELAYED_ACK_TIME
 	SCTP_CONTEXT,	/* Receive Context */
 #define SCTP_CONTEXT SCTP_CONTEXT
+	SCTP_FRAGMENT_INTERLEAVE,
+#define SCTP_FRAGMENT_INTERLEAVE SCTP_FRAGMENT_INTERLEAVE
+	SCTP_PARTIAL_DELIVERY_POINT,	/* Set/Get partial delivery point */
+#define SCTP_PARTIAL_DELIVERY_POINT SCTP_PARTIAL_DELIVERY_POINT
+	SCTP_MAX_BURST,		/* Set/Get max burst */
+#define SCTP_MAX_BURST SCTP_MAX_BURST
 
 	/* Internal Socket Options. Some of the sctp library functions are 
 	 * implemented using these socket options.
@@ -213,6 +219,7 @@
 	__u16 sac_outbound_streams;
 	__u16 sac_inbound_streams;
 	sctp_assoc_t sac_assoc_id;
+	__u8 sac_info[0];
 };
 
 /*
@@ -261,6 +268,7 @@
 	SCTP_ADDR_REMOVED,
 	SCTP_ADDR_ADDED,
 	SCTP_ADDR_MADE_PRIM,
+	SCTP_ADDR_CONFIRMED,
 };
 
 
@@ -508,16 +516,17 @@
  *   address's parameters:
  */
 enum  sctp_spp_flags {
-	SPP_HB_ENABLE = 1,		/*Enable heartbeats*/
-	SPP_HB_DISABLE = 2,		/*Disable heartbeats*/
+	SPP_HB_ENABLE = 1<<0,		/*Enable heartbeats*/
+	SPP_HB_DISABLE = 1<<1,		/*Disable heartbeats*/
 	SPP_HB = SPP_HB_ENABLE | SPP_HB_DISABLE,
-	SPP_HB_DEMAND = 4,		/*Send heartbeat immediately*/
-	SPP_PMTUD_ENABLE = 8,		/*Enable PMTU discovery*/
-	SPP_PMTUD_DISABLE = 16,		/*Disable PMTU discovery*/
+	SPP_HB_DEMAND = 1<<2,		/*Send heartbeat immediately*/
+	SPP_PMTUD_ENABLE = 1<<3,	/*Enable PMTU discovery*/
+	SPP_PMTUD_DISABLE = 1<<4,	/*Disable PMTU discovery*/
 	SPP_PMTUD = SPP_PMTUD_ENABLE | SPP_PMTUD_DISABLE,
-	SPP_SACKDELAY_ENABLE = 32,	/*Enable SACK*/
-	SPP_SACKDELAY_DISABLE = 64,	/*Disable SACK*/
+	SPP_SACKDELAY_ENABLE = 1<<5,	/*Enable SACK*/
+	SPP_SACKDELAY_DISABLE = 1<<6,	/*Disable SACK*/
 	SPP_SACKDELAY = SPP_SACKDELAY_ENABLE | SPP_SACKDELAY_DISABLE,
+	SPP_HB_TIME_IS_ZERO = 1<<7,	/* Set HB delay to 0 */
 };
 
 struct sctp_paddrparams {
@@ -530,7 +539,7 @@
 	__u32			spp_flags;
 } __attribute__((packed, aligned(4)));
 
-/* 7.1.24. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME)
+/* 7.1.23. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME)
  *
  *   This options will get or set the delayed ack timer.  The time is set
  *   in milliseconds.  If the assoc_id is 0, then this sets or gets the
diff --git a/include/net/sock.h b/include/net/sock.h
index 2c7d60c..25c37e3 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -202,6 +202,15 @@
 	unsigned short		sk_type;
 	int			sk_rcvbuf;
 	socket_lock_t		sk_lock;
+	/*
+	 * The backlog queue is special, it is always used with
+	 * the per-socket spinlock held and requires low latency
+	 * access. Therefore we special case it's implementation.
+	 */
+	struct {
+		struct sk_buff *head;
+		struct sk_buff *tail;
+	} sk_backlog;
 	wait_queue_head_t	*sk_sleep;
 	struct dst_entry	*sk_dst_cache;
 	struct xfrm_policy	*sk_policy[2];
@@ -221,15 +230,6 @@
 	int			sk_rcvlowat;
 	unsigned long 		sk_flags;
 	unsigned long	        sk_lingertime;
-	/*
-	 * The backlog queue is special, it is always used with
-	 * the per-socket spinlock held and requires low latency
-	 * access. Therefore we special case it's implementation.
-	 */
-	struct {
-		struct sk_buff *head;
-		struct sk_buff *tail;
-	} sk_backlog;
 	struct sk_buff_head	sk_error_queue;
 	struct proto		*sk_prot_creator;
 	rwlock_t		sk_callback_lock;
@@ -244,7 +244,7 @@
 	struct sk_filter      	*sk_filter;
 	void			*sk_protinfo;
 	struct timer_list	sk_timer;
-	struct timeval		sk_stamp;
+	ktime_t			sk_stamp;
 	struct socket		*sk_socket;
 	void			*sk_user_data;
 	struct page		*sk_sndmsg_page;
@@ -390,6 +390,7 @@
 	SOCK_USE_WRITE_QUEUE, /* whether to call sk->sk_write_space in sock_wfree */
 	SOCK_DBG, /* %SO_DEBUG setting */
 	SOCK_RCVTSTAMP, /* %SO_TIMESTAMP setting */
+	SOCK_RCVTSTAMPNS, /* %SO_TIMESTAMPNS setting */
 	SOCK_LOCALROUTE, /* route locally only, %SO_DONTROUTE setting */
 	SOCK_QUEUE_SHRUNK, /* write queue has been shrunk recently */
 };
@@ -710,15 +711,6 @@
 		__sk_stream_mem_reclaim(sk);
 }
 
-static inline void sk_stream_writequeue_purge(struct sock *sk)
-{
-	struct sk_buff *skb;
-
-	while ((skb = __skb_dequeue(&sk->sk_write_queue)) != NULL)
-		sk_stream_free_skb(sk, skb);
-	sk_stream_mem_reclaim(sk);
-}
-
 static inline int sk_stream_rmem_schedule(struct sock *sk, struct sk_buff *skb)
 {
 	return (int)skb->truesize <= sk->sk_forward_alloc ||
@@ -1083,19 +1075,7 @@
 	return net_gso_ok(sk->sk_route_caps, sk->sk_gso_type);
 }
 
-static inline void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
-{
-	__sk_dst_set(sk, dst);
-	sk->sk_route_caps = dst->dev->features;
-	if (sk->sk_route_caps & NETIF_F_GSO)
-		sk->sk_route_caps |= NETIF_F_GSO_MASK;
-	if (sk_can_gso(sk)) {
-		if (dst->header_len)
-			sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
-		else 
-			sk->sk_route_caps |= NETIF_F_SG | NETIF_F_HW_CSUM;
-	}
-}
+extern void sk_setup_caps(struct sock *sk, struct dst_entry *dst);
 
 static inline void sk_charge_skb(struct sock *sk, struct sk_buff *skb)
 {
@@ -1256,18 +1236,6 @@
 	return page;
 }
 
-#define sk_stream_for_retrans_queue(skb, sk)				\
-		for (skb = (sk)->sk_write_queue.next;			\
-		     (skb != (sk)->sk_send_head) &&			\
-		     (skb != (struct sk_buff *)&(sk)->sk_write_queue);	\
-		     skb = skb->next)
-
-/*from STCP for fast SACK Process*/
-#define sk_stream_for_retrans_queue_from(skb, sk)			\
-		for (; (skb != (sk)->sk_send_head) &&                   \
-		     (skb != (struct sk_buff *)&(sk)->sk_write_queue);	\
-		     skb = skb->next)
-
 /*
  *	Default write policy as shown to user space via poll/select/SIGIO
  */
@@ -1304,22 +1272,18 @@
 	return timeo == MAX_SCHEDULE_TIMEOUT ? -ERESTARTSYS : -EINTR;
 }
 
+extern void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
+	struct sk_buff *skb);
+
 static __inline__ void
 sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb)
 {
-	struct timeval stamp;
+	ktime_t kt = skb->tstamp;
 
-	skb_get_timestamp(skb, &stamp);
-	if (sock_flag(sk, SOCK_RCVTSTAMP)) {
-		/* Race occurred between timestamp enabling and packet
-		   receiving.  Fill in the current time for now. */
-		if (stamp.tv_sec == 0)
-			do_gettimeofday(&stamp);
-		skb_set_timestamp(skb, &stamp);
-		put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP, sizeof(struct timeval),
-			 &stamp);
-	} else
-		sk->sk_stamp = stamp;
+	if (sock_flag(sk, SOCK_RCVTSTAMP))
+		__sock_recv_timestamp(msg, sk, skb);
+	else
+		sk->sk_stamp = kt;
 }
 
 /**
@@ -1350,18 +1314,17 @@
 
 extern void sock_enable_timestamp(struct sock *sk);
 extern int sock_get_timestamp(struct sock *, struct timeval __user *);
+extern int sock_get_timestampns(struct sock *, struct timespec __user *);
 
 /* 
  *	Enable debug/info messages 
  */
+extern int net_msg_warn;
+#define NETDEBUG(fmt, args...) \
+	do { if (net_msg_warn) printk(fmt,##args); } while (0)
 
-#ifdef CONFIG_NETDEBUG
-#define NETDEBUG(fmt, args...)	printk(fmt,##args)
-#define LIMIT_NETDEBUG(fmt, args...) do { if (net_ratelimit()) printk(fmt,##args); } while(0)
-#else
-#define NETDEBUG(fmt, args...)	do { } while (0)
-#define LIMIT_NETDEBUG(fmt, args...) do { } while(0)
-#endif
+#define LIMIT_NETDEBUG(fmt, args...) \
+	do { if (net_msg_warn && net_ratelimit()) printk(fmt,##args); } while(0)
 
 /*
  * Macros for sleeping on a socket. Use them like this:
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 5c472f2..ef8f9d4 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -220,6 +220,7 @@
 extern int sysctl_tcp_adv_win_scale;
 extern int sysctl_tcp_tw_reuse;
 extern int sysctl_tcp_frto;
+extern int sysctl_tcp_frto_response;
 extern int sysctl_tcp_low_latency;
 extern int sysctl_tcp_dma_copybreak;
 extern int sysctl_tcp_nometrics_save;
@@ -230,6 +231,7 @@
 extern int sysctl_tcp_base_mss;
 extern int sysctl_tcp_workaround_signed_windows;
 extern int sysctl_tcp_slow_start_after_idle;
+extern int sysctl_tcp_max_ssthresh;
 
 extern atomic_t tcp_memory_allocated;
 extern atomic_t tcp_sockets_allocated;
@@ -341,6 +343,7 @@
 extern int			tcp_child_process(struct sock *parent,
 						  struct sock *child,
 						  struct sk_buff *skb);
+extern int			tcp_use_frto(struct sock *sk);
 extern void			tcp_enter_frto(struct sock *sk);
 extern void			tcp_enter_loss(struct sock *sk, int how);
 extern void			tcp_clear_retrans(struct tcp_sock *tp);
@@ -417,9 +420,9 @@
 
 /* tcp_output.c */
 
-extern void __tcp_push_pending_frames(struct sock *sk, struct tcp_sock *tp,
-				      unsigned int cur_mss, int nonagle);
-extern int tcp_may_send_now(struct sock *sk, struct tcp_sock *tp);
+extern void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss,
+				      int nonagle);
+extern int tcp_may_send_now(struct sock *sk);
 extern int tcp_retransmit_skb(struct sock *, struct sk_buff *);
 extern void tcp_xmit_retransmit_queue(struct sock *);
 extern void tcp_simple_retransmit(struct sock *);
@@ -476,8 +479,10 @@
 	__tcp_fast_path_on(tp, tp->snd_wnd >> tp->rx_opt.snd_wscale);
 }
 
-static inline void tcp_fast_path_check(struct sock *sk, struct tcp_sock *tp)
+static inline void tcp_fast_path_check(struct sock *sk)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
+
 	if (skb_queue_empty(&tp->out_of_order_queue) &&
 	    tp->rcv_wnd &&
 	    atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf &&
@@ -588,10 +593,10 @@
 	}
 }
 
-static inline void tcp_packets_out_inc(struct sock *sk, 
-				       struct tcp_sock *tp,
+static inline void tcp_packets_out_inc(struct sock *sk,
 				       const struct sk_buff *skb)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
 	int orig = tp->packets_out;
 
 	tp->packets_out += tcp_skb_pcount(skb);
@@ -624,9 +629,12 @@
 #define TCP_CA_MAX	128
 #define TCP_CA_BUF_MAX	(TCP_CA_NAME_MAX*TCP_CA_MAX)
 
+#define TCP_CONG_NON_RESTRICTED 0x1
+#define TCP_CONG_RTT_STAMP	0x2
+
 struct tcp_congestion_ops {
 	struct list_head	list;
-	int	non_restricted;
+	unsigned long flags;
 
 	/* initialize private data (optional) */
 	void (*init)(struct sock *sk);
@@ -640,8 +648,6 @@
 	/* do new cwnd calculation (required) */
 	void (*cong_avoid)(struct sock *sk, u32 ack,
 			   u32 rtt, u32 in_flight, int good_ack);
-	/* round trip time sample per acked packet (optional) */
-	void (*rtt_sample)(struct sock *sk, u32 usrtt);
 	/* call before changing ca_state (optional) */
 	void (*set_state)(struct sock *sk, u8 new_state);
 	/* call when cwnd event occurs (optional) */
@@ -649,7 +655,7 @@
 	/* new value of cwnd after loss (optional) */
 	u32  (*undo_cwnd)(struct sock *sk);
 	/* hook for packet ack accounting (optional) */
-	void (*pkts_acked)(struct sock *sk, u32 num_acked);
+	void (*pkts_acked)(struct sock *sk, u32 num_acked, ktime_t last);
 	/* get info for inet_diag (optional) */
 	void (*get_info)(struct sock *sk, u32 ext, struct sk_buff *skb);
 
@@ -730,13 +736,11 @@
 
 static inline void tcp_sync_left_out(struct tcp_sock *tp)
 {
-	if (tp->rx_opt.sack_ok &&
-	    (tp->sacked_out >= tp->packets_out - tp->lost_out))
-		tp->sacked_out = tp->packets_out - tp->lost_out;
+	BUG_ON(tp->sacked_out + tp->lost_out > tp->packets_out);
 	tp->left_out = tp->sacked_out + tp->lost_out;
 }
 
-extern void tcp_enter_cwr(struct sock *sk);
+extern void tcp_enter_cwr(struct sock *sk, const int set_ssthresh);
 extern __u32 tcp_init_cwnd(struct tcp_sock *tp, struct dst_entry *dst);
 
 /* Slow start with delack produces 3 packets of burst, so that
@@ -775,18 +779,21 @@
 		tp->snd_sml = TCP_SKB_CB(skb)->end_seq;
 }
 
-static inline void tcp_check_probe_timer(struct sock *sk, struct tcp_sock *tp)
+static inline void tcp_check_probe_timer(struct sock *sk)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
 	const struct inet_connection_sock *icsk = inet_csk(sk);
+
 	if (!tp->packets_out && !icsk->icsk_pending)
 		inet_csk_reset_xmit_timer(sk, ICSK_TIME_PROBE0,
 					  icsk->icsk_rto, TCP_RTO_MAX);
 }
 
-static inline void tcp_push_pending_frames(struct sock *sk,
-					   struct tcp_sock *tp)
+static inline void tcp_push_pending_frames(struct sock *sk)
 {
-	__tcp_push_pending_frames(sk, tp, tcp_current_mss(sk, 1), tp->nonagle);
+	struct tcp_sock *tp = tcp_sk(sk);
+
+	__tcp_push_pending_frames(sk, tcp_current_mss(sk, 1), tp->nonagle);
 }
 
 static inline void tcp_init_wl(struct tcp_sock *tp, u32 ack, u32 seq)
@@ -815,7 +822,7 @@
 
 static inline int tcp_checksum_complete(struct sk_buff *skb)
 {
-	return skb->ip_summed != CHECKSUM_UNNECESSARY &&
+	return !skb_csum_unnecessary(skb) &&
 		__tcp_checksum_complete(skb);
 }
 
@@ -918,21 +925,7 @@
 #endif	
 }
 
-static inline void tcp_done(struct sock *sk)
-{
-	if(sk->sk_state == TCP_SYN_SENT || sk->sk_state == TCP_SYN_RECV)
-		TCP_INC_STATS_BH(TCP_MIB_ATTEMPTFAILS);
-
-	tcp_set_state(sk, TCP_CLOSE);
-	tcp_clear_xmit_timers(sk);
-
-	sk->sk_shutdown = SHUTDOWN_MASK;
-
-	if (!sock_flag(sk, SOCK_DEAD))
-		sk->sk_state_change(sk);
-	else
-		inet_csk_destroy_sock(sk);
-}
+extern void tcp_done(struct sock *sk);
 
 static inline void tcp_sack_reset(struct tcp_options_received *rx_opt)
 {
@@ -981,7 +974,7 @@
 	ireq->wscale_ok = rx_opt->wscale_ok;
 	ireq->acked = 0;
 	ireq->ecn_ok = 0;
-	ireq->rmt_port = skb->h.th->source;
+	ireq->rmt_port = tcp_hdr(skb)->source;
 }
 
 extern void tcp_enter_memory_pressure(void);
@@ -1011,7 +1004,7 @@
 {
 	if ((s32)(rx_opt->rcv_tsval - rx_opt->ts_recent) >= 0)
 		return 0;
-	if (xtime.tv_sec >= rx_opt->ts_recent_stamp + TCP_PAWS_24DAYS)
+	if (get_seconds() >= rx_opt->ts_recent_stamp + TCP_PAWS_24DAYS)
 		return 0;
 
 	/* RST segments are not recommended to carry timestamp,
@@ -1026,26 +1019,13 @@
 
 	   However, we can relax time bounds for RST segments to MSL.
 	 */
-	if (rst && xtime.tv_sec >= rx_opt->ts_recent_stamp + TCP_PAWS_MSL)
+	if (rst && get_seconds() >= rx_opt->ts_recent_stamp + TCP_PAWS_MSL)
 		return 0;
 	return 1;
 }
 
 #define TCP_CHECK_TIMER(sk) do { } while (0)
 
-static inline int tcp_use_frto(const struct sock *sk)
-{
-	const struct tcp_sock *tp = tcp_sk(sk);
-	
-	/* F-RTO must be activated in sysctl and there must be some
-	 * unsent new data, and the advertised window should allow
-	 * sending it.
-	 */
-	return (sysctl_tcp_frto && sk->sk_send_head &&
-		!after(TCP_SKB_CB(sk->sk_send_head)->end_seq,
-		       tp->snd_una + tp->snd_wnd));
-}
-
 static inline void tcp_mib_init(void)
 {
 	/* See RFC 2012 */
@@ -1172,6 +1152,125 @@
 	put_cpu();
 }
 
+/* write queue abstraction */
+static inline void tcp_write_queue_purge(struct sock *sk)
+{
+	struct sk_buff *skb;
+
+	while ((skb = __skb_dequeue(&sk->sk_write_queue)) != NULL)
+		sk_stream_free_skb(sk, skb);
+	sk_stream_mem_reclaim(sk);
+}
+
+static inline struct sk_buff *tcp_write_queue_head(struct sock *sk)
+{
+	struct sk_buff *skb = sk->sk_write_queue.next;
+	if (skb == (struct sk_buff *) &sk->sk_write_queue)
+		return NULL;
+	return skb;
+}
+
+static inline struct sk_buff *tcp_write_queue_tail(struct sock *sk)
+{
+	struct sk_buff *skb = sk->sk_write_queue.prev;
+	if (skb == (struct sk_buff *) &sk->sk_write_queue)
+		return NULL;
+	return skb;
+}
+
+static inline struct sk_buff *tcp_write_queue_next(struct sock *sk, struct sk_buff *skb)
+{
+	return skb->next;
+}
+
+#define tcp_for_write_queue(skb, sk)					\
+		for (skb = (sk)->sk_write_queue.next;			\
+		     (skb != (struct sk_buff *)&(sk)->sk_write_queue);	\
+		     skb = skb->next)
+
+#define tcp_for_write_queue_from(skb, sk)				\
+		for (; (skb != (struct sk_buff *)&(sk)->sk_write_queue);\
+		     skb = skb->next)
+
+static inline struct sk_buff *tcp_send_head(struct sock *sk)
+{
+	return sk->sk_send_head;
+}
+
+static inline void tcp_advance_send_head(struct sock *sk, struct sk_buff *skb)
+{
+	struct tcp_sock *tp = tcp_sk(sk);
+
+	sk->sk_send_head = skb->next;
+	if (sk->sk_send_head == (struct sk_buff *)&sk->sk_write_queue)
+		sk->sk_send_head = NULL;
+	/* Don't override Nagle indefinately with F-RTO */
+	if (tp->frto_counter == 2)
+		tp->frto_counter = 3;
+}
+
+static inline void tcp_check_send_head(struct sock *sk, struct sk_buff *skb_unlinked)
+{
+	if (sk->sk_send_head == skb_unlinked)
+		sk->sk_send_head = NULL;
+}
+
+static inline void tcp_init_send_head(struct sock *sk)
+{
+	sk->sk_send_head = NULL;
+}
+
+static inline void __tcp_add_write_queue_tail(struct sock *sk, struct sk_buff *skb)
+{
+	__skb_queue_tail(&sk->sk_write_queue, skb);
+}
+
+static inline void tcp_add_write_queue_tail(struct sock *sk, struct sk_buff *skb)
+{
+	__tcp_add_write_queue_tail(sk, skb);
+
+	/* Queue it, remembering where we must start sending. */
+	if (sk->sk_send_head == NULL)
+		sk->sk_send_head = skb;
+}
+
+static inline void __tcp_add_write_queue_head(struct sock *sk, struct sk_buff *skb)
+{
+	__skb_queue_head(&sk->sk_write_queue, skb);
+}
+
+/* Insert buff after skb on the write queue of sk.  */
+static inline void tcp_insert_write_queue_after(struct sk_buff *skb,
+						struct sk_buff *buff,
+						struct sock *sk)
+{
+	__skb_append(skb, buff, &sk->sk_write_queue);
+}
+
+/* Insert skb between prev and next on the write queue of sk.  */
+static inline void tcp_insert_write_queue_before(struct sk_buff *new,
+						  struct sk_buff *skb,
+						  struct sock *sk)
+{
+	__skb_insert(new, skb->prev, skb, &sk->sk_write_queue);
+}
+
+static inline void tcp_unlink_write_queue(struct sk_buff *skb, struct sock *sk)
+{
+	__skb_unlink(skb, &sk->sk_write_queue);
+}
+
+static inline int tcp_skb_is_last(const struct sock *sk,
+				  const struct sk_buff *skb)
+{
+	return skb->next == (struct sk_buff *)&sk->sk_write_queue;
+}
+
+static inline int tcp_write_queue_empty(struct sock *sk)
+{
+	return skb_queue_empty(&sk->sk_write_queue);
+}
+
 /* /proc */
 enum tcp_seq_states {
 	TCP_SEQ_STATE_LISTENING,
diff --git a/include/net/tcp_ecn.h b/include/net/tcp_ecn.h
index 4629d77..89eb3e0 100644
--- a/include/net/tcp_ecn.h
+++ b/include/net/tcp_ecn.h
@@ -27,9 +27,10 @@
 		TCP_SKB_CB(skb)->flags &= ~TCPCB_FLAG_ECE;
 }
 
-static inline void TCP_ECN_send_syn(struct sock *sk, struct tcp_sock *tp,
-				    struct sk_buff *skb)
+static inline void TCP_ECN_send_syn(struct sock *sk, struct sk_buff *skb)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
+
 	tp->ecn_flags = 0;
 	if (sysctl_tcp_ecn) {
 		TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_ECE|TCPCB_FLAG_CWR;
@@ -44,9 +45,11 @@
 		th->ece = 1;
 }
 
-static inline void TCP_ECN_send(struct sock *sk, struct tcp_sock *tp,
-				struct sk_buff *skb, int tcp_header_len)
+static inline void TCP_ECN_send(struct sock *sk, struct sk_buff *skb,
+				int tcp_header_len)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
+
 	if (tp->ecn_flags & TCP_ECN_OK) {
 		/* Not-retransmitted data segment: set ECT and inject CWR. */
 		if (skb->len != tcp_header_len &&
@@ -54,7 +57,7 @@
 			INET_ECN_xmit(sk);
 			if (tp->ecn_flags&TCP_ECN_QUEUE_CWR) {
 				tp->ecn_flags &= ~TCP_ECN_QUEUE_CWR;
-				skb->h.th->cwr = 1;
+				tcp_hdr(skb)->cwr = 1;
 				skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN;
 			}
 		} else {
@@ -62,7 +65,7 @@
 			INET_ECN_dontxmit(sk);
 		}
 		if (tp->ecn_flags & TCP_ECN_DEMAND_CWR)
-			skb->h.th->ece = 1;
+			tcp_hdr(skb)->ece = 1;
 	}
 }
 
@@ -70,7 +73,7 @@
 
 static inline void TCP_ECN_accept_cwr(struct tcp_sock *tp, struct sk_buff *skb)
 {
-	if (skb->h.th->cwr)
+	if (tcp_hdr(skb)->cwr)
 		tp->ecn_flags &= ~TCP_ECN_DEMAND_CWR;
 }
 
diff --git a/include/net/udp.h b/include/net/udp.h
index 1b921fa..98755eb 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -72,15 +72,12 @@
  */
 static inline __sum16 __udp_lib_checksum_complete(struct sk_buff *skb)
 {
-	if (! UDP_SKB_CB(skb)->partial_cov)
-		return __skb_checksum_complete(skb);
-	return csum_fold(skb_checksum(skb, 0, UDP_SKB_CB(skb)->cscov,
-				      skb->csum));
+	return __skb_checksum_complete_head(skb, UDP_SKB_CB(skb)->cscov);
 }
 
 static inline int udp_lib_checksum_complete(struct sk_buff *skb)
 {
-	return skb->ip_summed != CHECKSUM_UNNECESSARY &&
+	return !skb_csum_unnecessary(skb) &&
 		__udp_lib_checksum_complete(skb);
 }
 
@@ -92,8 +89,8 @@
  */
 static inline __wsum udp_csum_outgoing(struct sock *sk, struct sk_buff *skb)
 {
-	__wsum csum = csum_partial(skb->h.raw, sizeof(struct udphdr), 0);
-
+	__wsum csum = csum_partial(skb_transport_header(skb),
+				   sizeof(struct udphdr), 0);
 	skb_queue_walk(&sk->sk_write_queue, skb) {
 		csum = csum_add(csum, skb->csum);
 	}
diff --git a/include/net/udplite.h b/include/net/udplite.h
index 67ac514..635b0ea 100644
--- a/include/net/udplite.h
+++ b/include/net/udplite.h
@@ -47,11 +47,10 @@
 		return 1;
 	}
 
-        UDP_SKB_CB(skb)->partial_cov = 0;
 	cscov = ntohs(uh->len);
 
 	if (cscov == 0)		 /* Indicates that full coverage is required. */
-		cscov = skb->len;
+		;
 	else if (cscov < 8  || cscov > skb->len) {
 		/*
 		 * Coverage length violates RFC 3828: log and discard silently.
@@ -60,42 +59,16 @@
 			       cscov, skb->len);
 		return 1;
 
-	} else if (cscov < skb->len)
+	} else if (cscov < skb->len) {
         	UDP_SKB_CB(skb)->partial_cov = 1;
-
-        UDP_SKB_CB(skb)->cscov = cscov;
-
-	/*
-	 * There is no known NIC manufacturer supporting UDP-Lite yet,
-	 * hence ip_summed is always (re-)set to CHECKSUM_NONE.
-	 */
-	skb->ip_summed = CHECKSUM_NONE;
+		UDP_SKB_CB(skb)->cscov = cscov;
+		if (skb->ip_summed == CHECKSUM_COMPLETE)
+			skb->ip_summed = CHECKSUM_NONE;
+        }
 
 	return 0;
 }
 
-static __inline__ int udplite4_csum_init(struct sk_buff *skb, struct udphdr *uh)
-{
-	int rc = udplite_checksum_init(skb, uh);
-
-	if (!rc)
-		skb->csum = csum_tcpudp_nofold(skb->nh.iph->saddr,
-					       skb->nh.iph->daddr,
-					       skb->len, IPPROTO_UDPLITE, 0);
-	return rc;
-}
-
-static __inline__ int udplite6_csum_init(struct sk_buff *skb, struct udphdr *uh)
-{
-	int rc = udplite_checksum_init(skb, uh);
-
-	if (!rc)
-		skb->csum = ~csum_unfold(csum_ipv6_magic(&skb->nh.ipv6h->saddr,
-					     &skb->nh.ipv6h->daddr,
-					     skb->len, IPPROTO_UDPLITE, 0));
-	return rc;
-}
-
 static inline int udplite_sender_cscov(struct udp_sock *up, struct udphdr *uh)
 {
 	int cscov = up->len;
@@ -128,14 +101,14 @@
 
 static inline __wsum udplite_csum_outgoing(struct sock *sk, struct sk_buff *skb)
 {
-	int off, len, cscov = udplite_sender_cscov(udp_sk(sk), skb->h.uh);
+	int cscov = udplite_sender_cscov(udp_sk(sk), udp_hdr(skb));
 	__wsum csum = 0;
 
 	skb->ip_summed = CHECKSUM_NONE;     /* no HW support for checksumming */
 
 	skb_queue_walk(&sk->sk_write_queue, skb) {
-		off = skb->h.raw - skb->data;
-		len = skb->len - off;
+		const int off = skb_transport_offset(skb);
+		const int len = skb->len - off;
 
 		csum = skb_checksum(skb, off, (cscov > len)? len : cscov, csum);
 
diff --git a/include/net/wext.h b/include/net/wext.h
new file mode 100644
index 0000000..c02b8de
--- /dev/null
+++ b/include/net/wext.h
@@ -0,0 +1,24 @@
+#ifndef __NET_WEXT_H
+#define __NET_WEXT_H
+
+/*
+ * wireless extensions interface to the core code
+ */
+
+#ifdef CONFIG_WIRELESS_EXT
+extern int wext_proc_init(void);
+extern int wext_handle_ioctl(struct ifreq *ifr, unsigned int cmd,
+			     void __user *arg);
+#else
+static inline int wext_proc_init(void)
+{
+	return 0;
+}
+static inline int wext_handle_ioctl(struct ifreq *ifr, unsigned int cmd,
+				    void __user *arg)
+{
+	return -EINVAL;
+}
+#endif
+
+#endif /* __NET_WEXT_H */
diff --git a/include/net/wireless.h b/include/net/wireless.h
new file mode 100644
index 0000000..d30c4ba
--- /dev/null
+++ b/include/net/wireless.h
@@ -0,0 +1,139 @@
+#ifndef __NET_WIRELESS_H
+#define __NET_WIRELESS_H
+
+/*
+ * 802.11 device management
+ *
+ * Copyright 2007	Johannes Berg <johannes@sipsolutions.net>
+ */
+
+#include <linux/netdevice.h>
+#include <linux/debugfs.h>
+#include <linux/list.h>
+#include <net/cfg80211.h>
+
+/**
+ * struct wiphy - wireless hardware description
+ * @idx: the wiphy index assigned to this item
+ * @class_dev: the class device representing /sys/class/ieee80211/<wiphy-name>
+ */
+struct wiphy {
+	/* assign these fields before you register the wiphy */
+
+	/* permanent MAC address */
+	u8 perm_addr[ETH_ALEN];
+
+	/* If multiple wiphys are registered and you're handed e.g.
+	 * a regular netdev with assigned ieee80211_ptr, you won't
+	 * know whether it points to a wiphy your driver has registered
+	 * or not. Assign this to something global to your driver to
+	 * help determine whether you own this wiphy or not. */
+	void *privid;
+
+	/* fields below are read-only, assigned by cfg80211 */
+
+	/* the item in /sys/class/ieee80211/ points to this,
+	 * you need use set_wiphy_dev() (see below) */
+	struct device dev;
+
+	/* dir in debugfs: ieee80211/<wiphyname> */
+	struct dentry *debugfsdir;
+
+	char priv[0] __attribute__((__aligned__(NETDEV_ALIGN)));
+};
+
+/** struct wireless_dev - wireless per-netdev state
+ *
+ * This structure must be allocated by the driver/stack
+ * that uses the ieee80211_ptr field in struct net_device
+ * (this is intentional so it can be allocated along with
+ * the netdev.)
+ *
+ * @wiphy: pointer to hardware description
+ */
+struct wireless_dev {
+	struct wiphy *wiphy;
+
+	/* private to the generic wireless code */
+	struct list_head list;
+	struct net_device *netdev;
+};
+
+/**
+ * wiphy_priv - return priv from wiphy
+ */
+static inline void *wiphy_priv(struct wiphy *wiphy)
+{
+	BUG_ON(!wiphy);
+	return &wiphy->priv;
+}
+
+/**
+ * set_wiphy_dev - set device pointer for wiphy
+ */
+static inline void set_wiphy_dev(struct wiphy *wiphy, struct device *dev)
+{
+	wiphy->dev.parent = dev;
+}
+
+/**
+ * wiphy_dev - get wiphy dev pointer
+ */
+static inline struct device *wiphy_dev(struct wiphy *wiphy)
+{
+	return wiphy->dev.parent;
+}
+
+/**
+ * wiphy_name - get wiphy name
+ */
+static inline char *wiphy_name(struct wiphy *wiphy)
+{
+	return wiphy->dev.bus_id;
+}
+
+/**
+ * wdev_priv - return wiphy priv from wireless_dev
+ */
+static inline void *wdev_priv(struct wireless_dev *wdev)
+{
+	BUG_ON(!wdev);
+	return wiphy_priv(wdev->wiphy);
+}
+
+/**
+ * wiphy_new - create a new wiphy for use with cfg80211
+ *
+ * create a new wiphy and associate the given operations with it.
+ * @sizeof_priv bytes are allocated for private use.
+ *
+ * the returned pointer must be assigned to each netdev's
+ * ieee80211_ptr for proper operation.
+ */
+struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv);
+
+/**
+ * wiphy_register - register a wiphy with cfg80211
+ *
+ * register the given wiphy
+ *
+ * Returns a non-negative wiphy index or a negative error code.
+ */
+extern int wiphy_register(struct wiphy *wiphy);
+
+/**
+ * wiphy_unregister - deregister a wiphy from cfg80211
+ *
+ * unregister a device with the given priv pointer.
+ * After this call, no more requests can be made with this priv
+ * pointer, but the call may sleep to wait for an outstanding
+ * request that is being handled.
+ */
+extern void wiphy_unregister(struct wiphy *wiphy);
+
+/**
+ * wiphy_free - free wiphy
+ */
+extern void wiphy_free(struct wiphy *wiphy);
+
+#endif /* __NET_WIRELESS_H */
diff --git a/include/net/x25device.h b/include/net/x25device.h
index 1d10c87..1415bcf 100644
--- a/include/net/x25device.h
+++ b/include/net/x25device.h
@@ -7,8 +7,8 @@
 
 static inline __be16 x25_type_trans(struct sk_buff *skb, struct net_device *dev)
 {
-	skb->mac.raw = skb->data;
 	skb->dev = dev;
+	skb_reset_mac_header(skb);
 	skb->pkt_type = PACKET_HOST;
 	
 	return htons(ETH_P_X25);
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 5a00aa8..66c2d3e 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -279,7 +279,7 @@
 	xfrm_address_t		*(*local_addr)(struct xfrm_state *, xfrm_address_t *);
 	xfrm_address_t		*(*remote_addr)(struct xfrm_state *, xfrm_address_t *);
 	/* Estimate maximal size of result of transformation of a dgram */
-	u32			(*get_max_size)(struct xfrm_state *, int size);
+	u32			(*get_mtu)(struct xfrm_state *, int size);
 };
 
 extern int xfrm_register_type(struct xfrm_type *type, unsigned short family);
@@ -416,6 +416,25 @@
 	u32	secid;
 };
 
+/* SAD metadata, add more later */
+struct xfrm_sadinfo
+{
+	u32 sadhcnt; /* current hash bkts */
+	u32 sadhmcnt; /* max allowed hash bkts */
+	u32 sadcnt; /* current running count */
+};
+
+struct xfrm_spdinfo
+{
+	u32 incnt;
+	u32 outcnt;
+	u32 fwdcnt;
+	u32 inscnt;
+	u32 outscnt;
+	u32 fwdscnt;
+	u32 spdhcnt;
+	u32 spdhmcnt;
+};
 #ifdef CONFIG_AUDITSYSCALL
 extern void xfrm_audit_log(uid_t auid, u32 secid, int type, int result,
 		    struct xfrm_policy *xp, struct xfrm_state *x);
@@ -584,6 +603,10 @@
 		struct rt6_info		rt6;
 	} u;
 	struct dst_entry *route;
+#ifdef CONFIG_XFRM_SUB_POLICY
+	struct flowi *origin;
+	struct xfrm_selector *partner;
+#endif
 	u32 genid;
 	u32 route_mtu_cached;
 	u32 child_mtu_cached;
@@ -596,6 +619,12 @@
 	dst_release(xdst->route);
 	if (likely(xdst->u.dst.xfrm))
 		xfrm_state_put(xdst->u.dst.xfrm);
+#ifdef CONFIG_XFRM_SUB_POLICY
+	kfree(xdst->origin);
+	xdst->origin = NULL;
+	kfree(xdst->partner);
+	xdst->partner = NULL;
+#endif
 }
 
 extern void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev);
@@ -938,6 +967,8 @@
 extern struct xfrm_state *xfrm_find_acq_byseq(u32 seq);
 extern int xfrm_state_delete(struct xfrm_state *x);
 extern void xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info);
+extern void xfrm_sad_getinfo(struct xfrm_sadinfo *si);
+extern void xfrm_spd_getinfo(struct xfrm_spdinfo *si);
 extern int xfrm_replay_check(struct xfrm_state *x, __be32 seq);
 extern void xfrm_replay_advance(struct xfrm_state *x, __be32 seq);
 extern void xfrm_replay_notify(struct xfrm_state *x, int event);
diff --git a/include/rxrpc/call.h b/include/rxrpc/call.h
deleted file mode 100644
index b86f837..0000000
--- a/include/rxrpc/call.h
+++ /dev/null
@@ -1,212 +0,0 @@
-/* call.h: Rx call record
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_RXRPC_CALL_H
-#define _LINUX_RXRPC_CALL_H
-
-#include <rxrpc/types.h>
-#include <rxrpc/rxrpc.h>
-#include <rxrpc/packet.h>
-#include <linux/timer.h>
-
-#define RXRPC_CALL_ACK_WINDOW_SIZE	16
-
-extern unsigned rxrpc_call_rcv_timeout;		/* receive activity timeout (secs) */
-
-/* application call state
- * - only state 0 and ffff are reserved, the state is set to 1 after an opid is received
- */
-enum rxrpc_app_cstate {
-	RXRPC_CSTATE_COMPLETE		= 0,	/* operation complete */
-	RXRPC_CSTATE_ERROR,			/* operation ICMP error or aborted */
-	RXRPC_CSTATE_SRVR_RCV_OPID,		/* [SERVER] receiving operation ID */
-	RXRPC_CSTATE_SRVR_RCV_ARGS,		/* [SERVER] receiving operation data */
-	RXRPC_CSTATE_SRVR_GOT_ARGS,		/* [SERVER] completely received operation data */
-	RXRPC_CSTATE_SRVR_SND_REPLY,		/* [SERVER] sending operation reply */
-	RXRPC_CSTATE_SRVR_RCV_FINAL_ACK,	/* [SERVER] receiving final ACK */
-	RXRPC_CSTATE_CLNT_SND_ARGS,		/* [CLIENT] sending operation args */
-	RXRPC_CSTATE_CLNT_RCV_REPLY,		/* [CLIENT] receiving operation reply */
-	RXRPC_CSTATE_CLNT_GOT_REPLY,		/* [CLIENT] completely received operation reply */
-} __attribute__((packed));
-
-extern const char *rxrpc_call_states[];
-
-enum rxrpc_app_estate {
-	RXRPC_ESTATE_NO_ERROR		= 0,	/* no error */
-	RXRPC_ESTATE_LOCAL_ABORT,		/* aborted locally by application layer */
-	RXRPC_ESTATE_PEER_ABORT,		/* aborted remotely by peer */
-	RXRPC_ESTATE_LOCAL_ERROR,		/* local ICMP network error */
-	RXRPC_ESTATE_REMOTE_ERROR,		/* remote ICMP network error */
-} __attribute__((packed));
-
-extern const char *rxrpc_call_error_states[];
-
-/*****************************************************************************/
-/*
- * Rx call record and application scratch buffer
- * - the call record occupies the bottom of a complete page
- * - the application scratch buffer occupies the rest
- */
-struct rxrpc_call
-{
-	atomic_t		usage;
-	struct rxrpc_connection	*conn;		/* connection upon which active */
-	spinlock_t		lock;		/* access lock */
-	struct module		*owner;		/* owner module */
-	wait_queue_head_t	waitq;		/* wait queue for events to happen */
-	struct list_head	link;		/* general internal list link */
-	struct list_head	call_link;	/* master call list link */
-	__be32			chan_ix;	/* connection channel index  */
-	__be32			call_id;	/* call ID on connection  */
-	unsigned long		cjif;		/* jiffies at call creation */
-	unsigned long		flags;		/* control flags */
-#define RXRPC_CALL_ACKS_TIMO	0x00000001	/* ACKS timeout reached */
-#define RXRPC_CALL_ACKR_TIMO	0x00000002	/* ACKR timeout reached */
-#define RXRPC_CALL_RCV_TIMO	0x00000004	/* RCV timeout reached */
-#define RXRPC_CALL_RCV_PKT	0x00000008	/* received packet */
-
-	/* transmission */
-	rxrpc_seq_t		snd_seq_count;	/* outgoing packet sequence number counter */
-	struct rxrpc_message	*snd_nextmsg;	/* next message being constructed for sending */
-	struct rxrpc_message	*snd_ping;	/* last ping message sent */
-	unsigned short		snd_resend_cnt;	/* count of resends since last ACK */
-
-	/* transmission ACK tracking */
-	struct list_head	acks_pendq;	/* messages pending ACK (ordered by seq) */
-	unsigned		acks_pend_cnt;	/* number of un-ACK'd packets */
-	rxrpc_seq_t		acks_dftv_seq;	/* highest definitively ACK'd msg seq */
-	struct timer_list	acks_timeout;	/* timeout on expected ACK */
-
-	/* reception */
-	struct list_head	rcv_receiveq;	/* messages pending reception (ordered by seq) */
-	struct list_head	rcv_krxiodq_lk;	/* krxiod queue for new inbound packets */
-	struct timer_list	rcv_timeout;	/* call receive activity timeout */
-
-	/* reception ACK'ing */
-	rxrpc_seq_t		ackr_win_bot;	/* bottom of ACK window */
-	rxrpc_seq_t		ackr_win_top;	/* top of ACK window */
-	rxrpc_seq_t		ackr_high_seq;	/* highest seqno yet received */
-	rxrpc_seq_net_t		ackr_prev_seq;	/* previous seqno received */
-	unsigned		ackr_pend_cnt;	/* number of pending ACKs */
-	struct timer_list	ackr_dfr_timo;	/* timeout on deferred ACK */
-	char			ackr_dfr_perm;	/* request for deferred ACKs permitted */
-	rxrpc_seq_t		ackr_dfr_seq;	/* seqno for deferred ACK */
-	struct rxrpc_ackpacket	ackr;		/* pending normal ACK packet */
-	uint8_t			ackr_array[RXRPC_CALL_ACK_WINDOW_SIZE];	/* ACK records */
-
-	/* presentation layer */
-	char			app_last_rcv;	/* T if received last packet from remote end */
-	enum rxrpc_app_cstate	app_call_state;	/* call state */
-	enum rxrpc_app_estate	app_err_state;	/* abort/error state */
-	struct list_head	app_readyq;	/* ordered ready received packet queue */
-	struct list_head	app_unreadyq;	/* ordered post-hole recv'd packet queue */
-	rxrpc_seq_t		app_ready_seq;	/* last seq number dropped into readyq */
-	size_t			app_ready_qty;	/* amount of data ready in readyq */
-	unsigned		app_opcode;	/* operation ID */
-	unsigned		app_abort_code;	/* abort code (when aborted) */
-	int			app_errno;	/* error number (when ICMP error received) */
-
-	/* statisics */
-	unsigned		pkt_rcv_count;	/* count of received packets on this call */
-	unsigned		pkt_snd_count;	/* count of sent packets on this call */
-	unsigned		app_read_count;	/* number of reads issued */
-
-	/* bits for the application to use */
-	rxrpc_call_attn_func_t	app_attn_func;	/* callback when attention required */
-	rxrpc_call_error_func_t	app_error_func;	/* callback when abort sent (cleanup and put) */
-	rxrpc_call_aemap_func_t	app_aemap_func;	/* callback to map abort code to/from errno */
-	void			*app_user;	/* application data */
-	struct list_head	app_link;	/* application list linkage */
-	struct list_head	app_attn_link;	/* application attention list linkage */
-	size_t			app_mark;	/* trigger callback when app_ready_qty>=app_mark */
-	char			app_async_read;	/* T if in async-read mode */
-	uint8_t			*app_read_buf;	/* application async read buffer (app_mark size) */
-	uint8_t			*app_scr_alloc;	/* application scratch allocation pointer */
-	void			*app_scr_ptr;	/* application pointer into scratch buffer */
-
-#define RXRPC_APP_MARK_EOF 0xFFFFFFFFU	/* mark at end of input */
-
-	/* application scratch buffer */
-	uint8_t			app_scratch[0] __attribute__((aligned(sizeof(long))));
-};
-
-#define RXRPC_CALL_SCRATCH_SIZE (PAGE_SIZE - sizeof(struct rxrpc_call))
-
-#define rxrpc_call_reset_scratch(CALL) \
-do { (CALL)->app_scr_alloc = (CALL)->app_scratch; } while(0)
-
-#define rxrpc_call_alloc_scratch(CALL,SIZE)						\
-({											\
-	void *ptr;									\
-	ptr = (CALL)->app_scr_alloc;							\
-	(CALL)->app_scr_alloc += (SIZE);						\
-	if ((SIZE)>RXRPC_CALL_SCRATCH_SIZE ||						\
-	    (size_t)((CALL)->app_scr_alloc - (u8*)(CALL)) > RXRPC_CALL_SCRATCH_SIZE) {	\
-		printk("rxrpc_call_alloc_scratch(%p,%Zu)\n",(CALL),(size_t)(SIZE));	\
-		BUG();									\
-	}										\
-	ptr;										\
-})
-
-#define rxrpc_call_alloc_scratch_s(CALL,TYPE)						\
-({											\
-	size_t size = sizeof(TYPE);							\
-	TYPE *ptr;									\
-	ptr = (TYPE*)(CALL)->app_scr_alloc;						\
-	(CALL)->app_scr_alloc += size;							\
-	if (size>RXRPC_CALL_SCRATCH_SIZE ||						\
-	    (size_t)((CALL)->app_scr_alloc - (u8*)(CALL)) > RXRPC_CALL_SCRATCH_SIZE) {	\
-		printk("rxrpc_call_alloc_scratch(%p,%Zu)\n",(CALL),size);		\
-		BUG();									\
-	}										\
-	ptr;										\
-})
-
-#define rxrpc_call_is_ack_pending(CALL) ((CALL)->ackr.reason != 0)
-
-extern int rxrpc_create_call(struct rxrpc_connection *conn,
-			     rxrpc_call_attn_func_t attn,
-			     rxrpc_call_error_func_t error,
-			     rxrpc_call_aemap_func_t aemap,
-			     struct rxrpc_call **_call);
-
-extern int rxrpc_incoming_call(struct rxrpc_connection *conn,
-			       struct rxrpc_message *msg,
-			       struct rxrpc_call **_call);
-
-static inline void rxrpc_get_call(struct rxrpc_call *call)
-{
-	BUG_ON(atomic_read(&call->usage)<=0);
-	atomic_inc(&call->usage);
-	/*printk("rxrpc_get_call(%p{u=%d})\n",(C),atomic_read(&(C)->usage));*/
-}
-
-extern void rxrpc_put_call(struct rxrpc_call *call);
-
-extern void rxrpc_call_do_stuff(struct rxrpc_call *call);
-
-extern int rxrpc_call_abort(struct rxrpc_call *call, int error);
-
-#define RXRPC_CALL_READ_BLOCK	0x0001	/* block if not enough data and not yet EOF */
-#define RXRPC_CALL_READ_ALL	0x0002	/* error if insufficient data received */
-extern int rxrpc_call_read_data(struct rxrpc_call *call, void *buffer, size_t size, int flags);
-
-extern int rxrpc_call_write_data(struct rxrpc_call *call,
-				 size_t sioc,
-				 struct kvec *siov,
-				 uint8_t rxhdr_flags,
-				 gfp_t alloc_flags,
-				 int dup_data,
-				 size_t *size_sent);
-
-extern void rxrpc_call_handle_error(struct rxrpc_call *conn, int local, int errno);
-
-#endif /* _LINUX_RXRPC_CALL_H */
diff --git a/include/rxrpc/connection.h b/include/rxrpc/connection.h
deleted file mode 100644
index 41e6781..0000000
--- a/include/rxrpc/connection.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/* connection.h: Rx connection record
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_RXRPC_CONNECTION_H
-#define _LINUX_RXRPC_CONNECTION_H
-
-#include <rxrpc/types.h>
-#include <rxrpc/krxtimod.h>
-
-struct sk_buff;
-
-/*****************************************************************************/
-/*
- * Rx connection
- * - connections are matched by (rmt_port,rmt_addr,service_id,conn_id,clientflag)
- * - connections only retain a refcount on the peer when they are active
- * - connections with refcount==0 are inactive and reside in the peer's graveyard
- */
-struct rxrpc_connection
-{
-	atomic_t		usage;
-	struct rxrpc_transport	*trans;		/* transport endpoint */
-	struct rxrpc_peer	*peer;		/* peer from/to which connected */
-	struct rxrpc_service	*service;	/* responsible service (inbound conns) */
-	struct rxrpc_timer	timeout;	/* decaching timer */
-	struct list_head	link;		/* link in peer's list */
-	struct list_head	proc_link;	/* link in proc list */
-	struct list_head	err_link;	/* link in ICMP error processing list */
-	struct list_head	id_link;	/* link in ID grant list */
-	struct sockaddr_in	addr;		/* remote address */
-	struct rxrpc_call	*channels[4];	/* channels (active calls) */
-	wait_queue_head_t	chanwait;	/* wait for channel to become available */
-	spinlock_t		lock;		/* access lock */
-	struct timeval		atime;		/* last access time */
-	size_t			mtu_size;	/* MTU size for outbound messages */
-	unsigned		call_counter;	/* call ID counter */
-	rxrpc_serial_t		serial_counter;	/* packet serial number counter */
-
-	/* the following should all be in net order */
-	__be32			in_epoch;	/* peer's epoch */
-	__be32			out_epoch;	/* my epoch */
-	__be32			conn_id;	/* connection ID, appropriately shifted */
-	__be16			service_id;	/* service ID */
-	uint8_t			security_ix;	/* security ID */
-	uint8_t			in_clientflag;	/* RXRPC_CLIENT_INITIATED if we are server */
-	uint8_t			out_clientflag;	/* RXRPC_CLIENT_INITIATED if we are client */
-};
-
-extern int rxrpc_create_connection(struct rxrpc_transport *trans,
-				   __be16 port,
-				   __be32 addr,
-				   uint16_t service_id,
-				   void *security,
-				   struct rxrpc_connection **_conn);
-
-extern int rxrpc_connection_lookup(struct rxrpc_peer *peer,
-				   struct rxrpc_message *msg,
-				   struct rxrpc_connection **_conn);
-
-static inline void rxrpc_get_connection(struct rxrpc_connection *conn)
-{
-	BUG_ON(atomic_read(&conn->usage)<0);
-	atomic_inc(&conn->usage);
-	//printk("rxrpc_get_conn(%p{u=%d})\n",conn,atomic_read(&conn->usage));
-}
-
-extern void rxrpc_put_connection(struct rxrpc_connection *conn);
-
-extern int rxrpc_conn_receive_call_packet(struct rxrpc_connection *conn,
-					  struct rxrpc_call *call,
-					  struct rxrpc_message *msg);
-
-extern void rxrpc_conn_handle_error(struct rxrpc_connection *conn, int local, int errno);
-
-#endif /* _LINUX_RXRPC_CONNECTION_H */
diff --git a/include/rxrpc/krxiod.h b/include/rxrpc/krxiod.h
deleted file mode 100644
index c0e0e82..0000000
--- a/include/rxrpc/krxiod.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* krxiod.h: Rx RPC I/O kernel thread interface
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_RXRPC_KRXIOD_H
-#define _LINUX_RXRPC_KRXIOD_H
-
-#include <rxrpc/types.h>
-
-extern int rxrpc_krxiod_init(void);
-extern void rxrpc_krxiod_kill(void);
-extern void rxrpc_krxiod_queue_transport(struct rxrpc_transport *trans);
-extern void rxrpc_krxiod_dequeue_transport(struct rxrpc_transport *trans);
-extern void rxrpc_krxiod_queue_peer(struct rxrpc_peer *peer);
-extern void rxrpc_krxiod_dequeue_peer(struct rxrpc_peer *peer);
-extern void rxrpc_krxiod_clear_peers(struct rxrpc_transport *trans);
-extern void rxrpc_krxiod_queue_call(struct rxrpc_call *call);
-extern void rxrpc_krxiod_dequeue_call(struct rxrpc_call *call);
-
-#endif /* _LINUX_RXRPC_KRXIOD_H */
diff --git a/include/rxrpc/krxsecd.h b/include/rxrpc/krxsecd.h
deleted file mode 100644
index 55ce43a..0000000
--- a/include/rxrpc/krxsecd.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* krxsecd.h: Rx RPC security kernel thread interface
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_RXRPC_KRXSECD_H
-#define _LINUX_RXRPC_KRXSECD_H
-
-#include <rxrpc/types.h>
-
-extern int rxrpc_krxsecd_init(void);
-extern void rxrpc_krxsecd_kill(void);
-extern void rxrpc_krxsecd_clear_transport(struct rxrpc_transport *trans);
-extern void rxrpc_krxsecd_queue_incoming_call(struct rxrpc_message *msg);
-
-#endif /* _LINUX_RXRPC_KRXSECD_H */
diff --git a/include/rxrpc/krxtimod.h b/include/rxrpc/krxtimod.h
deleted file mode 100644
index b3d298b..0000000
--- a/include/rxrpc/krxtimod.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* krxtimod.h: RxRPC timeout daemon
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_RXRPC_KRXTIMOD_H
-#define _LINUX_RXRPC_KRXTIMOD_H
-
-#include <rxrpc/types.h>
-
-struct rxrpc_timer_ops {
-	/* called when the front of the timer queue has timed out */
-	void (*timed_out)(struct rxrpc_timer *timer);
-};
-
-/*****************************************************************************/
-/*
- * RXRPC timer/timeout record
- */
-struct rxrpc_timer
-{
-	struct list_head		link;		/* link in timer queue */
-	unsigned long			timo_jif;	/* timeout time */
-	const struct rxrpc_timer_ops	*ops;		/* timeout expiry function */
-};
-
-static inline void rxrpc_timer_init(rxrpc_timer_t *timer, const struct rxrpc_timer_ops *ops)
-{
-	INIT_LIST_HEAD(&timer->link);
-	timer->ops = ops;
-}
-
-extern int rxrpc_krxtimod_start(void);
-extern void rxrpc_krxtimod_kill(void);
-
-extern void rxrpc_krxtimod_add_timer(rxrpc_timer_t *timer, unsigned long timeout);
-extern int rxrpc_krxtimod_del_timer(rxrpc_timer_t *timer);
-
-#endif /* _LINUX_RXRPC_KRXTIMOD_H */
diff --git a/include/rxrpc/message.h b/include/rxrpc/message.h
deleted file mode 100644
index b318f27..0000000
--- a/include/rxrpc/message.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/* message.h: Rx message caching
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_RXRPC_MESSAGE_H
-#define _LINUX_RXRPC_MESSAGE_H
-
-#include <rxrpc/packet.h>
-
-/*****************************************************************************/
-/*
- * Rx message record
- */
-struct rxrpc_message
-{
-	atomic_t		usage;
-	struct list_head	link;		/* list link */
-	struct timeval		stamp;		/* time received or last sent */
-	rxrpc_seq_t		seq;		/* message sequence number */
-
-	int			state;		/* the state the message is currently in */
-#define RXRPC_MSG_PREPARED	0
-#define RXRPC_MSG_SENT		1
-#define RXRPC_MSG_ACKED		2		/* provisionally ACK'd */
-#define RXRPC_MSG_DONE		3		/* definitively ACK'd (msg->seq<ack.firstPacket) */
-#define RXRPC_MSG_RECEIVED	4
-#define RXRPC_MSG_ERROR		-1
-	char			rttdone;	/* used for RTT */
-
-	struct rxrpc_transport	*trans;		/* transport received through */
-	struct rxrpc_connection	*conn;		/* connection received over */
-	struct sk_buff		*pkt;		/* received packet */
-	off_t			offset;		/* offset into pkt of next byte of data */
-
-	struct rxrpc_header	hdr;		/* message header */
-
-	int			dcount;		/* data part count */
-	size_t			dsize;		/* data size */
-#define RXRPC_MSG_MAX_IOCS 8
-	struct kvec		data[RXRPC_MSG_MAX_IOCS]; /* message data */
-	unsigned long		dfree;		/* bit mask indicating kfree(data[x]) if T */
-};
-
-#define rxrpc_get_message(M) do { atomic_inc(&(M)->usage); } while(0)
-
-extern void __rxrpc_put_message(struct rxrpc_message *msg);
-static inline void rxrpc_put_message(struct rxrpc_message *msg)
-{
-	BUG_ON(atomic_read(&msg->usage)<=0);
-	if (atomic_dec_and_test(&msg->usage))
-		__rxrpc_put_message(msg);
-}
-
-extern int rxrpc_conn_newmsg(struct rxrpc_connection *conn,
-			     struct rxrpc_call *call,
-			     uint8_t type,
-			     int count,
-			     struct kvec *diov,
-			     gfp_t alloc_flags,
-			     struct rxrpc_message **_msg);
-
-extern int rxrpc_conn_sendmsg(struct rxrpc_connection *conn, struct rxrpc_message *msg);
-
-#endif /* _LINUX_RXRPC_MESSAGE_H */
diff --git a/include/rxrpc/packet.h b/include/rxrpc/packet.h
index 1447f0a..b69e6e1 100644
--- a/include/rxrpc/packet.h
+++ b/include/rxrpc/packet.h
@@ -1,6 +1,6 @@
 /* packet.h: Rx packet layout and definitions
  *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
@@ -12,28 +12,25 @@
 #ifndef _LINUX_RXRPC_PACKET_H
 #define _LINUX_RXRPC_PACKET_H
 
-#include <rxrpc/types.h>
-
-#define RXRPC_IPUDP_SIZE		28
-extern size_t RXRPC_MAX_PACKET_SIZE;
-#define RXRPC_MAX_PACKET_DATA_SIZE	(RXRPC_MAX_PACKET_SIZE - sizeof(struct rxrpc_header))
-#define RXRPC_LOCAL_PACKET_SIZE		RXRPC_MAX_PACKET_SIZE
-#define RXRPC_REMOTE_PACKET_SIZE	(576 - RXRPC_IPUDP_SIZE)
+typedef u32	rxrpc_seq_t;	/* Rx message sequence number */
+typedef u32	rxrpc_serial_t;	/* Rx message serial number */
+typedef __be32	rxrpc_seq_net_t; /* on-the-wire Rx message sequence number */
+typedef __be32	rxrpc_serial_net_t; /* on-the-wire Rx message serial number */
 
 /*****************************************************************************/
 /*
  * on-the-wire Rx packet header
  * - all multibyte fields should be in network byte order
  */
-struct rxrpc_header
-{
+struct rxrpc_header {
 	__be32		epoch;		/* client boot timestamp */
 
 	__be32		cid;		/* connection and channel ID */
 #define RXRPC_MAXCALLS		4			/* max active calls per conn */
 #define RXRPC_CHANNELMASK	(RXRPC_MAXCALLS-1)	/* mask for channel ID */
 #define RXRPC_CIDMASK		(~RXRPC_CHANNELMASK)	/* mask for connection ID */
-#define RXRPC_CIDSHIFT		2			/* shift for connection ID */
+#define RXRPC_CIDSHIFT		ilog2(RXRPC_MAXCALLS)	/* shift for connection ID */
+#define RXRPC_CID_INC		(1 << RXRPC_CIDSHIFT)	/* connection ID increment */
 
 	__be32		callNumber;	/* call ID (0 for connection-level packets) */
 #define RXRPC_PROCESS_MAXCALLS	(1<<2)	/* maximum number of active calls per conn (power of 2) */
@@ -62,7 +59,10 @@
 
 	uint8_t		userStatus;	/* app-layer defined status */
 	uint8_t		securityIndex;	/* security protocol ID */
-	__be16		_rsvd;		/* reserved (used by kerberos security as cksum) */
+	union {
+		__be16	_rsvd;		/* reserved */
+		__be16	cksum;		/* kerberos security checksum */
+	};
 	__be16		serviceId;	/* service ID */
 
 } __attribute__((packed));
@@ -81,8 +81,7 @@
  *   - new__rsvd = j__rsvd
  *   - duplicating all other fields
  */
-struct rxrpc_jumbo_header
-{
+struct rxrpc_jumbo_header {
 	uint8_t		flags;		/* packet flags (as per rxrpc_header) */
 	uint8_t		pad;
 	__be16		_rsvd;		/* reserved (used by kerberos security as cksum) */
@@ -95,8 +94,7 @@
  * on-the-wire Rx ACK packet data payload
  * - all multibyte fields should be in network byte order
  */
-struct rxrpc_ackpacket
-{
+struct rxrpc_ackpacket {
 	__be16		bufferSpace;	/* number of packet buffers available */
 	__be16		maxSkew;	/* diff between serno being ACK'd and highest serial no
 					 * received */
@@ -124,4 +122,93 @@
 
 } __attribute__((packed));
 
+/*
+ * ACK packets can have a further piece of information tagged on the end
+ */
+struct rxrpc_ackinfo {
+	__be32		rxMTU;		/* maximum Rx MTU size (bytes) [AFS 3.3] */
+	__be32		maxMTU;		/* maximum interface MTU size (bytes) [AFS 3.3] */
+	__be32		rwind;		/* Rx window size (packets) [AFS 3.4] */
+	__be32		jumbo_max;	/* max packets to stick into a jumbo packet [AFS 3.5] */
+};
+
+/*****************************************************************************/
+/*
+ * Kerberos security type-2 challenge packet
+ */
+struct rxkad_challenge {
+	__be32		version;	/* version of this challenge type */
+	__be32		nonce;		/* encrypted random number */
+	__be32		min_level;	/* minimum security level */
+	__be32		__padding;	/* padding to 8-byte boundary */
+} __attribute__((packed));
+
+/*****************************************************************************/
+/*
+ * Kerberos security type-2 response packet
+ */
+struct rxkad_response {
+	__be32		version;	/* version of this reponse type */
+	__be32		__pad;
+
+	/* encrypted bit of the response */
+	struct {
+		__be32		epoch;		/* current epoch */
+		__be32		cid;		/* parent connection ID */
+		__be32		checksum;	/* checksum */
+		__be32		securityIndex;	/* security type */
+		__be32		call_id[4];	/* encrypted call IDs */
+		__be32		inc_nonce;	/* challenge nonce + 1 */
+		__be32		level;		/* desired level */
+	} encrypted;
+
+	__be32		kvno;		/* Kerberos key version number */
+	__be32		ticket_len;	/* Kerberos ticket length  */
+} __attribute__((packed));
+
+/*****************************************************************************/
+/*
+ * RxRPC-level abort codes
+ */
+#define RX_CALL_DEAD		-1	/* call/conn has been inactive and is shut down */
+#define RX_INVALID_OPERATION	-2	/* invalid operation requested / attempted */
+#define RX_CALL_TIMEOUT		-3	/* call timeout exceeded */
+#define RX_EOF			-4	/* unexpected end of data on read op */
+#define RX_PROTOCOL_ERROR	-5	/* low-level protocol error */
+#define RX_USER_ABORT		-6	/* generic user abort */
+#define RX_ADDRINUSE		-7	/* UDP port in use */
+#define RX_DEBUGI_BADTYPE	-8	/* bad debugging packet type */
+
+/*
+ * (un)marshalling abort codes (rxgen)
+ */
+#define	RXGEN_CC_MARSHAL    -450
+#define	RXGEN_CC_UNMARSHAL  -451
+#define	RXGEN_SS_MARSHAL    -452
+#define	RXGEN_SS_UNMARSHAL  -453
+#define	RXGEN_DECODE	    -454
+#define	RXGEN_OPCODE	    -455
+#define	RXGEN_SS_XDRFREE    -456
+#define	RXGEN_CC_XDRFREE    -457
+
+/*
+ * Rx kerberos security abort codes
+ * - unfortunately we have no generalised security abort codes to say things
+ *   like "unsupported security", so we have to use these instead and hope the
+ *   other side understands
+ */
+#define RXKADINCONSISTENCY	19270400	/* security module structure inconsistent */
+#define RXKADPACKETSHORT	19270401	/* packet too short for security challenge */
+#define RXKADLEVELFAIL		19270402	/* security level negotiation failed */
+#define RXKADTICKETLEN		19270403	/* ticket length too short or too long */
+#define RXKADOUTOFSEQUENCE	19270404	/* packet had bad sequence number */
+#define RXKADNOAUTH		19270405	/* caller not authorised */
+#define RXKADBADKEY		19270406	/* illegal key: bad parity or weak */
+#define RXKADBADTICKET		19270407	/* security object was passed a bad ticket */
+#define RXKADUNKNOWNKEY		19270408	/* ticket contained unknown key version number */
+#define RXKADEXPIRED		19270409	/* authentication expired */
+#define RXKADSEALEDINCON	19270410	/* sealed data inconsistent */
+#define RXKADDATALEN		19270411	/* user data too long */
+#define RXKADILLEGALLEVEL	19270412	/* caller not authorised to use encrypted conns */
+
 #endif /* _LINUX_RXRPC_PACKET_H */
diff --git a/include/rxrpc/peer.h b/include/rxrpc/peer.h
deleted file mode 100644
index 8b8fe97..0000000
--- a/include/rxrpc/peer.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/* peer.h: Rx RPC per-transport peer record
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_RXRPC_PEER_H
-#define _LINUX_RXRPC_PEER_H
-
-#include <linux/wait.h>
-#include <rxrpc/types.h>
-#include <rxrpc/krxtimod.h>
-
-struct rxrpc_peer_ops
-{
-	/* peer record being added */
-	int (*adding)(struct rxrpc_peer *peer);
-
-	/* peer record being discarded from graveyard */
-	void (*discarding)(struct rxrpc_peer *peer);
-
-	/* change of epoch detected on connection */
-	void (*change_of_epoch)(struct rxrpc_connection *conn);
-};
-
-/*****************************************************************************/
-/*
- * Rx RPC per-transport peer record
- * - peers only retain a refcount on the transport when they are active
- * - peers with refcount==0 are inactive and reside in the transport's graveyard
- */
-struct rxrpc_peer
-{
-	atomic_t		usage;
-	struct rxrpc_peer_ops	*ops;		/* operations on this peer */
-	struct rxrpc_transport	*trans;		/* owner transport */
-	struct rxrpc_timer	timeout;	/* timeout for grave destruction */
-	struct list_head	link;		/* link in transport's peer list */
-	struct list_head	proc_link;	/* link in /proc list */
-	rwlock_t		conn_idlock;	/* lock for connection IDs */
-	struct list_head	conn_idlist;	/* list of connections granted IDs */
-	uint32_t		conn_idcounter;	/* connection ID counter */
-	rwlock_t		conn_lock;	/* lock for active/dead connections */
-	struct list_head	conn_active;	/* active connections to/from this peer */
-	struct list_head	conn_graveyard;	/* graveyard for inactive connections */
-	spinlock_t		conn_gylock;	/* lock for conn_graveyard */
-	wait_queue_head_t	conn_gy_waitq;	/* wait queue hit when graveyard is empty */
-	atomic_t		conn_count;	/* number of attached connections */
-	struct in_addr		addr;		/* remote address */
-	size_t			if_mtu;		/* interface MTU for this peer */
-	spinlock_t		lock;		/* access lock */
-
-	void			*user;		/* application layer data */
-
-	/* calculated RTT cache */
-#define RXRPC_RTT_CACHE_SIZE 32
-	suseconds_t		rtt;		/* current RTT estimate (in uS) */
-	unsigned		rtt_point;	/* next entry at which to insert */
-	unsigned		rtt_usage;	/* amount of cache actually used */
-	suseconds_t		rtt_cache[RXRPC_RTT_CACHE_SIZE]; /* calculated RTT cache */
-};
-
-
-extern int rxrpc_peer_lookup(struct rxrpc_transport *trans,
-			     __be32 addr,
-			     struct rxrpc_peer **_peer);
-
-static inline void rxrpc_get_peer(struct rxrpc_peer *peer)
-{
-	BUG_ON(atomic_read(&peer->usage)<0);
-	atomic_inc(&peer->usage);
-	//printk("rxrpc_get_peer(%p{u=%d})\n",peer,atomic_read(&peer->usage));
-}
-
-extern void rxrpc_put_peer(struct rxrpc_peer *peer);
-
-#endif /* _LINUX_RXRPC_PEER_H */
diff --git a/include/rxrpc/rxrpc.h b/include/rxrpc/rxrpc.h
deleted file mode 100644
index 8d9874c..0000000
--- a/include/rxrpc/rxrpc.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* rx.h: Rx RPC interface
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_RXRPC_RXRPC_H
-#define _LINUX_RXRPC_RXRPC_H
-
-#ifdef __KERNEL__
-
-extern __be32 rxrpc_epoch;
-
-#ifdef CONFIG_SYSCTL
-extern int rxrpc_ktrace;
-extern int rxrpc_kdebug;
-extern int rxrpc_kproto;
-extern int rxrpc_knet;
-#else
-#define rxrpc_ktrace	0
-#define rxrpc_kdebug	0
-#define rxrpc_kproto	0
-#define rxrpc_knet	0
-#endif
-
-extern int rxrpc_sysctl_init(void);
-extern void rxrpc_sysctl_cleanup(void);
-
-#endif /* __KERNEL__ */
-
-#endif /* _LINUX_RXRPC_RXRPC_H */
diff --git a/include/rxrpc/transport.h b/include/rxrpc/transport.h
deleted file mode 100644
index 7c7b968..0000000
--- a/include/rxrpc/transport.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/* transport.h: Rx transport management
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#ifndef _LINUX_RXRPC_TRANSPORT_H
-#define _LINUX_RXRPC_TRANSPORT_H
-
-#include <rxrpc/types.h>
-#include <rxrpc/krxiod.h>
-#include <rxrpc/rxrpc.h>
-#include <linux/skbuff.h>
-#include <linux/rwsem.h>
-
-typedef int (*rxrpc_newcall_fnx_t)(struct rxrpc_call *call);
-
-extern wait_queue_head_t rxrpc_krxiod_wq;
-
-/*****************************************************************************/
-/*
- * Rx operation specification
- * - tables of these must be sorted by op ID so that they can be binary-chop searched
- */
-struct rxrpc_operation
-{
-	unsigned		id;		/* operation ID */
-	size_t			asize;		/* minimum size of argument block */
-	const char		*name;		/* name of operation */
-	void			*user;		/* initial user data */
-};
-
-/*****************************************************************************/
-/*
- * Rx transport service record
- */
-struct rxrpc_service
-{
-	struct list_head	link;		/* link in services list on transport */
-	struct module		*owner;		/* owner module */
-	rxrpc_newcall_fnx_t	new_call;	/* new call handler function */
-	const char		*name;		/* name of service */
-	unsigned short		service_id;	/* Rx service ID */
-	rxrpc_call_attn_func_t	attn_func;	/* call requires attention callback */
-	rxrpc_call_error_func_t	error_func;	/* call error callback */
-	rxrpc_call_aemap_func_t	aemap_func;	/* abort -> errno mapping callback */
-
-	const struct rxrpc_operation	*ops_begin;	/* beginning of operations table */
-	const struct rxrpc_operation	*ops_end;	/* end of operations table */
-};
-
-/*****************************************************************************/
-/*
- * Rx transport endpoint record
- */
-struct rxrpc_transport
-{
-	atomic_t		usage;
-	struct socket		*socket;	/* my UDP socket */
-	struct list_head	services;	/* services listening on this socket */
-	struct list_head	link;		/* link in transport list */
-	struct list_head	proc_link;	/* link in transport proc list */
-	struct list_head	krxiodq_link;	/* krxiod attention queue link */
-	spinlock_t		lock;		/* access lock */
-	struct list_head	peer_active;	/* active peers connected to over this socket */
-	struct list_head	peer_graveyard;	/* inactive peer list */
-	spinlock_t		peer_gylock;	/* peer graveyard lock */
-	wait_queue_head_t	peer_gy_waitq;	/* wait queue hit when peer graveyard is empty */
-	rwlock_t		peer_lock;	/* peer list access lock */
-	atomic_t		peer_count;	/* number of peers */
-	struct rxrpc_peer_ops	*peer_ops;	/* default peer operations */
-	unsigned short		port;		/* port upon which listening */
-	volatile char		error_rcvd;	/* T if received ICMP error outstanding */
-};
-
-extern int rxrpc_create_transport(unsigned short port,
-				  struct rxrpc_transport **_trans);
-
-static inline void rxrpc_get_transport(struct rxrpc_transport *trans)
-{
-	BUG_ON(atomic_read(&trans->usage) <= 0);
-	atomic_inc(&trans->usage);
-	//printk("rxrpc_get_transport(%p{u=%d})\n",
-	//       trans, atomic_read(&trans->usage));
-}
-
-extern void rxrpc_put_transport(struct rxrpc_transport *trans);
-
-extern int rxrpc_add_service(struct rxrpc_transport *trans,
-			     struct rxrpc_service *srv);
-
-extern void rxrpc_del_service(struct rxrpc_transport *trans,
-			      struct rxrpc_service *srv);
-
-extern void rxrpc_trans_receive_packet(struct rxrpc_transport *trans);
-
-extern int rxrpc_trans_immediate_abort(struct rxrpc_transport *trans,
-				       struct rxrpc_message *msg,
-				       int error);
-
-#endif /* _LINUX_RXRPC_TRANSPORT_H */
diff --git a/kernel/audit.c b/kernel/audit.c
index 76c9a11..4e9d208 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -151,7 +151,7 @@
 
 static void audit_set_pid(struct audit_buffer *ab, pid_t pid)
 {
-	struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data;
+	struct nlmsghdr *nlh = nlmsg_hdr(ab->skb);
 	nlh->nlmsg_pid = pid;
 }
 
@@ -750,7 +750,7 @@
 	u32		rlen;
 
 	while (skb->len >= NLMSG_SPACE(0)) {
-		nlh = (struct nlmsghdr *)skb->data;
+		nlh = nlmsg_hdr(skb);
 		if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
 			return;
 		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
@@ -795,7 +795,7 @@
 	printk(KERN_INFO "audit: initializing netlink socket (%s)\n",
 	       audit_default ? "enabled" : "disabled");
 	audit_sock = netlink_kernel_create(NETLINK_AUDIT, 0, audit_receive,
-					   THIS_MODULE);
+					   NULL, THIS_MODULE);
 	if (!audit_sock)
 		audit_panic("cannot initialize netlink socket");
 	else
@@ -1073,7 +1073,7 @@
 			goto out;
 	}
 	va_copy(args2, args);
-	len = vsnprintf(skb->tail, avail, fmt, args);
+	len = vsnprintf(skb_tail_pointer(skb), avail, fmt, args);
 	if (len >= avail) {
 		/* The printk buffer is 1024 bytes long, so if we get
 		 * here and AUDIT_BUFSIZ is at least 1024, then we can
@@ -1082,7 +1082,7 @@
 			max_t(unsigned, AUDIT_BUFSIZ, 1+len-avail));
 		if (!avail)
 			goto out;
-		len = vsnprintf(skb->tail, avail, fmt, args2);
+		len = vsnprintf(skb_tail_pointer(skb), avail, fmt, args2);
 	}
 	if (len > 0)
 		skb_put(skb, len);
@@ -1143,7 +1143,7 @@
 			return;
 	}
 
-	ptr = skb->tail;
+	ptr = skb_tail_pointer(skb);
 	for (i=0; i<len; i++) {
 		*ptr++ = hex[(buf[i] & 0xF0)>>4]; /* Upper nibble */
 		*ptr++ = hex[buf[i] & 0x0F];	  /* Lower nibble */
@@ -1175,7 +1175,7 @@
 		if (!avail)
 			return;
 	}
-	ptr = skb->tail;
+	ptr = skb_tail_pointer(skb);
 	*ptr++ = '"';
 	memcpy(ptr, string, slen);
 	ptr += slen;
@@ -1268,7 +1268,7 @@
 		audit_log_lost("rate limit exceeded");
 	} else {
 		if (audit_pid) {
-			struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data;
+			struct nlmsghdr *nlh = nlmsg_hdr(ab->skb);
 			nlh->nlmsg_len = ab->skb->len - NLMSG_SPACE(0);
 			skb_queue_tail(&audit_skb_queue, ab->skb);
 			ab->skb = NULL;
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c
index b74860a..1b30331 100644
--- a/kernel/hrtimer.c
+++ b/kernel/hrtimer.c
@@ -59,6 +59,7 @@
 
 	return timespec_to_ktime(now);
 }
+EXPORT_SYMBOL_GPL(ktime_get);
 
 /**
  * ktime_get_real - get the real (wall-) time in ktime_t format
@@ -278,6 +279,8 @@
 
 	return ktime_add(kt, tmp);
 }
+
+EXPORT_SYMBOL_GPL(ktime_add_ns);
 # endif /* !CONFIG_KTIME_SCALAR */
 
 /*
diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c
index 6f294ff..5a0de84 100644
--- a/kernel/kallsyms.c
+++ b/kernel/kallsyms.c
@@ -267,27 +267,33 @@
 	return NULL;
 }
 
-/* Replace "%s" in format with address, or returns -errno. */
-void __print_symbol(const char *fmt, unsigned long address)
+/* Look up a kernel symbol and return it in a text buffer. */
+int sprint_symbol(char *buffer, unsigned long address)
 {
 	char *modname;
 	const char *name;
 	unsigned long offset, size;
 	char namebuf[KSYM_NAME_LEN+1];
-	char buffer[sizeof("%s+%#lx/%#lx [%s]") + KSYM_NAME_LEN +
-		    2*(BITS_PER_LONG*3/10) + MODULE_NAME_LEN + 1];
 
 	name = kallsyms_lookup(address, &size, &offset, &modname, namebuf);
-
 	if (!name)
-		sprintf(buffer, "0x%lx", address);
+		return sprintf(buffer, "0x%lx", address);
 	else {
 		if (modname)
-			sprintf(buffer, "%s+%#lx/%#lx [%s]", name, offset,
+			return sprintf(buffer, "%s+%#lx/%#lx [%s]", name, offset,
 				size, modname);
 		else
-			sprintf(buffer, "%s+%#lx/%#lx", name, offset, size);
+			return sprintf(buffer, "%s+%#lx/%#lx", name, offset, size);
 	}
+}
+
+/* Look up a kernel symbol and print it to the kernel messages. */
+void __print_symbol(const char *fmt, unsigned long address)
+{
+	char buffer[KSYM_SYMBOL_LEN];
+
+	sprint_symbol(buffer, address);
+
 	printk(fmt, buffer);
 }
 
@@ -452,3 +458,4 @@
 __initcall(kallsyms_init);
 
 EXPORT_SYMBOL(__print_symbol);
+EXPORT_SYMBOL_GPL(sprint_symbol);
diff --git a/kernel/module.c b/kernel/module.c
index dcdb32b..9da5af6 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -1148,8 +1148,10 @@
 		goto out;
 
 	mod->holders_dir = kobject_add_dir(&mod->mkobj.kobj, "holders");
-	if (!mod->holders_dir)
+	if (!mod->holders_dir) {
+		err = -ENOMEM;
 		goto out_unreg;
+	}
 
 	err = module_param_sysfs_setup(mod, kparam, num_params);
 	if (err)
diff --git a/kernel/power/disk.c b/kernel/power/disk.c
index aec19b0..02e4fb69 100644
--- a/kernel/power/disk.c
+++ b/kernel/power/disk.c
@@ -39,7 +39,13 @@
 {
 	int error = 0;
 
-	if (pm_disk_mode == PM_DISK_PLATFORM) {
+	switch (pm_disk_mode) {
+	case PM_DISK_TEST:
+	case PM_DISK_TESTPROC:
+	case PM_DISK_SHUTDOWN:
+	case PM_DISK_REBOOT:
+		break;
+	default:
 		if (pm_ops && pm_ops->prepare)
 			error = pm_ops->prepare(PM_SUSPEND_DISK);
 	}
@@ -48,40 +54,48 @@
 
 /**
  *	power_down - Shut machine down for hibernate.
- *	@mode:		Suspend-to-disk mode
  *
- *	Use the platform driver, if configured so, and return gracefully if it
- *	fails.
- *	Otherwise, try to power off and reboot. If they fail, halt the machine,
- *	there ain't no turning back.
+ *	Use the platform driver, if configured so; otherwise try
+ *	to power off or reboot.
  */
 
-static void power_down(suspend_disk_method_t mode)
+static void power_down(void)
 {
-	switch(mode) {
-	case PM_DISK_PLATFORM:
-		if (pm_ops && pm_ops->enter) {
-			kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK);
-			pm_ops->enter(PM_SUSPEND_DISK);
-			break;
-		}
+	switch (pm_disk_mode) {
+	case PM_DISK_TEST:
+	case PM_DISK_TESTPROC:
+		break;
 	case PM_DISK_SHUTDOWN:
 		kernel_power_off();
 		break;
 	case PM_DISK_REBOOT:
 		kernel_restart(NULL);
 		break;
+	default:
+		if (pm_ops && pm_ops->enter) {
+			kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK);
+			pm_ops->enter(PM_SUSPEND_DISK);
+			break;
+		}
 	}
 	kernel_halt();
-	/* Valid image is on the disk, if we continue we risk serious data corruption
-	   after resume. */
+	/*
+	 * Valid image is on the disk, if we continue we risk serious data
+	 * corruption after resume.
+	 */
 	printk(KERN_CRIT "Please power me down manually\n");
 	while(1);
 }
 
 static inline void platform_finish(void)
 {
-	if (pm_disk_mode == PM_DISK_PLATFORM) {
+	switch (pm_disk_mode) {
+	case PM_DISK_TEST:
+	case PM_DISK_TESTPROC:
+	case PM_DISK_SHUTDOWN:
+	case PM_DISK_REBOOT:
+		break;
+	default:
 		if (pm_ops && pm_ops->finish)
 			pm_ops->finish(PM_SUSPEND_DISK);
 	}
@@ -108,8 +122,6 @@
 /**
  *	pm_suspend_disk - The granpappy of hibernation power management.
  *
- *	If we're going through the firmware, then get it over with quickly.
- *
  *	If not, then call swsusp to do its thing, then figure out how
  *	to power down the system.
  */
@@ -166,7 +178,7 @@
 		pr_debug("PM: writing image.\n");
 		error = swsusp_write();
 		if (!error)
-			power_down(pm_disk_mode);
+			power_down();
 		else {
 			swsusp_free();
 			goto Thaw;
@@ -278,7 +290,6 @@
 
 
 static const char * const pm_disk_modes[] = {
-	[PM_DISK_FIRMWARE]	= "firmware",
 	[PM_DISK_PLATFORM]	= "platform",
 	[PM_DISK_SHUTDOWN]	= "shutdown",
 	[PM_DISK_REBOOT]	= "reboot",
@@ -289,27 +300,25 @@
 /**
  *	disk - Control suspend-to-disk mode
  *
- *	Suspend-to-disk can be handled in several ways. The greatest
- *	distinction is who writes memory to disk - the firmware or the OS.
- *	If the firmware does it, we assume that it also handles suspending
- *	the system.
- *	If the OS does it, then we have three options for putting the system
- *	to sleep - using the platform driver (e.g. ACPI or other PM registers),
- *	powering off the system or rebooting the system (for testing).
+ *	Suspend-to-disk can be handled in several ways. We have a few options
+ *	for putting the system to sleep - using the platform driver (e.g. ACPI
+ *	or other pm_ops), powering off the system or rebooting the system
+ *	(for testing) as well as the two test modes.
  *
- *	The system will support either 'firmware' or 'platform', and that is
- *	known a priori (and encoded in pm_ops). But, the user may choose
- *	'shutdown' or 'reboot' as alternatives.
+ *	The system can support 'platform', and that is known a priori (and
+ *	encoded in pm_ops). However, the user may choose 'shutdown' or 'reboot'
+ *	as alternatives, as well as the test modes 'test' and 'testproc'.
  *
  *	show() will display what the mode is currently set to.
  *	store() will accept one of
  *
- *	'firmware'
  *	'platform'
  *	'shutdown'
  *	'reboot'
+ *	'test'
+ *	'testproc'
  *
- *	It will only change to 'firmware' or 'platform' if the system
+ *	It will only change to 'platform' if the system
  *	supports it (as determined from pm_ops->pm_disk_mode).
  */
 
@@ -331,17 +340,21 @@
 	len = p ? p - buf : n;
 
 	mutex_lock(&pm_mutex);
-	for (i = PM_DISK_FIRMWARE; i < PM_DISK_MAX; i++) {
+	for (i = PM_DISK_PLATFORM; i < PM_DISK_MAX; i++) {
 		if (!strncmp(buf, pm_disk_modes[i], len)) {
 			mode = i;
 			break;
 		}
 	}
 	if (mode) {
-		if (mode == PM_DISK_SHUTDOWN || mode == PM_DISK_REBOOT ||
-		     mode == PM_DISK_TEST || mode == PM_DISK_TESTPROC) {
+		switch (mode) {
+		case PM_DISK_SHUTDOWN:
+		case PM_DISK_REBOOT:
+		case PM_DISK_TEST:
+		case PM_DISK_TESTPROC:
 			pm_disk_mode = mode;
-		} else {
+			break;
+		default:
 			if (pm_ops && pm_ops->enter &&
 			    (mode == pm_ops->pm_disk_mode))
 				pm_disk_mode = mode;
diff --git a/kernel/power/main.c b/kernel/power/main.c
index a064dfd..72419a3 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -30,7 +30,7 @@
 DEFINE_MUTEX(pm_mutex);
 
 struct pm_ops *pm_ops;
-suspend_disk_method_t pm_disk_mode = PM_DISK_PLATFORM;
+suspend_disk_method_t pm_disk_mode = PM_DISK_SHUTDOWN;
 
 /**
  *	pm_set_ops - Set the global power method table. 
@@ -41,9 +41,26 @@
 {
 	mutex_lock(&pm_mutex);
 	pm_ops = ops;
+	if (ops && ops->pm_disk_mode != PM_DISK_INVALID) {
+		pm_disk_mode = ops->pm_disk_mode;
+	} else
+		pm_disk_mode = PM_DISK_SHUTDOWN;
 	mutex_unlock(&pm_mutex);
 }
 
+/**
+ * pm_valid_only_mem - generic memory-only valid callback
+ *
+ * pm_ops drivers that implement mem suspend only and only need
+ * to check for that in their .valid callback can use this instead
+ * of rolling their own .valid callback.
+ */
+int pm_valid_only_mem(suspend_state_t state)
+{
+	return state == PM_SUSPEND_MEM;
+}
+
+
 static inline void pm_finish(suspend_state_t state)
 {
 	if (pm_ops->finish)
@@ -111,13 +128,24 @@
 	return error;
 }
 
+/* default implementation */
+void __attribute__ ((weak)) arch_suspend_disable_irqs(void)
+{
+	local_irq_disable();
+}
+
+/* default implementation */
+void __attribute__ ((weak)) arch_suspend_enable_irqs(void)
+{
+	local_irq_enable();
+}
 
 int suspend_enter(suspend_state_t state)
 {
 	int error = 0;
-	unsigned long flags;
 
-	local_irq_save(flags);
+	arch_suspend_disable_irqs();
+	BUG_ON(!irqs_disabled());
 
 	if ((error = device_power_down(PMSG_SUSPEND))) {
 		printk(KERN_ERR "Some devices failed to power down\n");
@@ -126,7 +154,8 @@
 	error = pm_ops->enter(state);
 	device_power_up();
  Done:
-	local_irq_restore(flags);
+	arch_suspend_enable_irqs();
+	BUG_ON(irqs_disabled());
 	return error;
 }
 
@@ -169,8 +198,8 @@
 
 	/* all other states need lowlevel support and need to be
 	 * valid to the lowlevel implementation, no valid callback
-	 * implies that all are valid. */
-	if (!pm_ops || (pm_ops->valid && !pm_ops->valid(state)))
+	 * implies that none are valid. */
+	if (!pm_ops || !pm_ops->valid || !pm_ops->valid(state))
 		return 0;
 	return 1;
 }
diff --git a/kernel/resource.c b/kernel/resource.c
index bdb55a3..9bd14fd 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -213,27 +213,6 @@
 EXPORT_SYMBOL(request_resource);
 
 /**
- * ____request_resource - reserve a resource, with resource conflict returned
- * @root: root resource descriptor
- * @new: resource descriptor desired by caller
- *
- * Returns:
- * On success, NULL is returned.
- * On error, a pointer to the conflicting resource is returned.
- */
-struct resource *____request_resource(struct resource *root, struct resource *new)
-{
-	struct resource *conflict;
-
-	write_lock(&resource_lock);
-	conflict = __request_resource(root, new);
-	write_unlock(&resource_lock);
-	return conflict;
-}
-
-EXPORT_SYMBOL(____request_resource);
-
-/**
  * release_resource - release a previously reserved resource
  * @old: resource pointer
  */
diff --git a/kernel/sched.c b/kernel/sched.c
index b9a6837..960d7c5 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -4746,7 +4746,7 @@
 		 * console might take alot of time:
 		 */
 		touch_nmi_watchdog();
-		if (p->state & state_filter)
+		if (!state_filter || (p->state & state_filter))
 			show_task(p);
 	} while_each_thread(g, p);
 
diff --git a/kernel/taskstats.c b/kernel/taskstats.c
index 4c3476f..ad7d239 100644
--- a/kernel/taskstats.c
+++ b/kernel/taskstats.c
@@ -102,7 +102,7 @@
  */
 static int send_reply(struct sk_buff *skb, pid_t pid)
 {
-	struct genlmsghdr *genlhdr = nlmsg_data((struct nlmsghdr *)skb->data);
+	struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb));
 	void *reply = genlmsg_data(genlhdr);
 	int rc;
 
@@ -121,7 +121,7 @@
 static void send_cpu_listeners(struct sk_buff *skb,
 					struct listener_list *listeners)
 {
-	struct genlmsghdr *genlhdr = nlmsg_data((struct nlmsghdr *)skb->data);
+	struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb));
 	struct listener *s, *tmp;
 	struct sk_buff *skb_next, *skb_cur = skb;
 	void *reply = genlmsg_data(genlhdr);
diff --git a/kernel/time.c b/kernel/time.c
index 2f47888..ba18ec4 100644
--- a/kernel/time.c
+++ b/kernel/time.c
@@ -452,6 +452,7 @@
 
 	return ts;
 }
+EXPORT_SYMBOL(ns_to_timespec);
 
 /**
  * ns_to_timeval - Convert nanoseconds to timeval
@@ -469,6 +470,7 @@
 
 	return tv;
 }
+EXPORT_SYMBOL(ns_to_timeval);
 
 /*
  * Convert jiffies to milliseconds and back.
diff --git a/kernel/timer.c b/kernel/timer.c
index dd6c2c1..b22bd39 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -505,6 +505,8 @@
 	return ret;
 }
 
+EXPORT_SYMBOL(try_to_del_timer_sync);
+
 /**
  * del_timer_sync - deactivate a timer and wait for the handler to finish.
  * @timer: the timer to be deactivated
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 3f3e740..79afd00 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -261,7 +261,7 @@
 	bool
 	depends on DEBUG_KERNEL && TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT
 	select STACKTRACE
-	select FRAME_POINTER if !X86
+	select FRAME_POINTER if !X86 && !MIPS
 	select KALLSYMS
 	select KALLSYMS_ALL
 
diff --git a/lib/Makefile b/lib/Makefile
index 992a39e..ae57f35 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -4,7 +4,7 @@
 
 lib-y := ctype.o string.o vsprintf.o cmdline.o \
 	 rbtree.o radix-tree.o dump_stack.o \
-	 idr.o div64.o int_sqrt.o bitmap.o extable.o prio_tree.o \
+	 idr.o int_sqrt.o bitmap.o extable.o prio_tree.o \
 	 sha1.o irq_regs.o reciprocal_div.o
 
 lib-$(CONFIG_MMU) += ioremap.o
@@ -12,7 +12,8 @@
 
 lib-y	+= kobject.o kref.o kobject_uevent.o klist.o
 
-obj-y += sort.o parser.o halfmd4.o debug_locks.o random32.o bust_spinlocks.o
+obj-y += div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
+	 bust_spinlocks.o
 
 ifeq ($(CONFIG_DEBUG_KOBJECT),y)
 CFLAGS_kobject.o += -DDEBUG
diff --git a/lib/devres.c b/lib/devres.c
index eb38849..b1d336c 100644
--- a/lib/devres.c
+++ b/lib/devres.c
@@ -296,5 +296,31 @@
 	return rc;
 }
 EXPORT_SYMBOL(pcim_iomap_regions);
+
+/**
+ * pcim_iounmap_regions - Unmap and release PCI BARs
+ * @pdev: PCI device to map IO resources for
+ * @mask: Mask of BARs to unmap and release
+ *
+ * Unamp and release regions specified by @mask.
+ */
+void pcim_iounmap_regions(struct pci_dev *pdev, u16 mask)
+{
+	void __iomem * const *iomap;
+	int i;
+
+	iomap = pcim_iomap_table(pdev);
+	if (!iomap)
+		return;
+
+	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+		if (!(mask & (1 << i)))
+			continue;
+
+		pcim_iounmap(pdev, iomap[i]);
+		pci_release_region(pdev, i);
+	}
+}
+EXPORT_SYMBOL(pcim_iounmap_regions);
 #endif
 #endif
diff --git a/lib/div64.c b/lib/div64.c
index 365719f..b71cf93 100644
--- a/lib/div64.c
+++ b/lib/div64.c
@@ -23,7 +23,7 @@
 /* Not needed on 64bit architectures */
 #if BITS_PER_LONG == 32
 
-uint32_t __div64_32(uint64_t *n, uint32_t base)
+uint32_t __attribute__((weak)) __div64_32(uint64_t *n, uint32_t base)
 {
 	uint64_t rem = *n;
 	uint64_t b = base;
@@ -58,4 +58,24 @@
 
 EXPORT_SYMBOL(__div64_32);
 
+/* 64bit divisor, dividend and result. dynamic precision */
+uint64_t div64_64(uint64_t dividend, uint64_t divisor)
+{
+	uint32_t high, d;
+
+	high = divisor >> 32;
+	if (high) {
+		unsigned int shift = fls(high);
+
+		d = divisor >> shift;
+		dividend >>= shift;
+	} else
+		d = divisor;
+
+	do_div(dividend, d);
+
+	return dividend;
+}
+EXPORT_SYMBOL(div64_64);
+
 #endif /* BITS_PER_LONG == 32 */
diff --git a/lib/kobject.c b/lib/kobject.c
index 057921c..cecf2fb 100644
--- a/lib/kobject.c
+++ b/lib/kobject.c
@@ -157,7 +157,7 @@
 }
 
 /**
- *	kobject_add - add an object to the hierarchy.
+ *	kobject_shadow_add - add an object to the hierarchy.
  *	@kobj:	object.
  *	@shadow_parent: sysfs directory to add to.
  */
@@ -174,6 +174,7 @@
 	if (!*kobj->k_name) {
 		pr_debug("kobject attempted to be registered with no name!\n");
 		WARN_ON(1);
+		kobject_put(kobj);
 		return -EINVAL;
 	}
 	parent = kobject_get(kobj->parent);
@@ -190,8 +191,8 @@
 
 		list_add_tail(&kobj->entry,&kobj->kset->list);
 		spin_unlock(&kobj->kset->list_lock);
+		kobj->parent = parent;
 	}
-	kobj->parent = parent;
 
 	error = create_dir(kobj, shadow_parent);
 	if (error) {
@@ -311,13 +312,43 @@
 int kobject_rename(struct kobject * kobj, const char *new_name)
 {
 	int error = 0;
+	const char *devpath = NULL;
+	char *devpath_string = NULL;
+	char *envp[2];
 
 	kobj = kobject_get(kobj);
 	if (!kobj)
 		return -EINVAL;
 	if (!kobj->parent)
 		return -EINVAL;
+
+	devpath = kobject_get_path(kobj, GFP_KERNEL);
+	if (!devpath) {
+		error = -ENOMEM;
+		goto out;
+	}
+	devpath_string = kmalloc(strlen(devpath) + 15, GFP_KERNEL);
+	if (!devpath_string) {
+		error = -ENOMEM;
+		goto out;
+	}
+	sprintf(devpath_string, "DEVPATH_OLD=%s", devpath);
+	envp[0] = devpath_string;
+	envp[1] = NULL;
+	/* Note : if we want to send the new name alone, not the full path,
+	 * we could probably use kobject_name(kobj); */
+
 	error = sysfs_rename_dir(kobj, kobj->parent->dentry, new_name);
+
+	/* This function is mostly/only used for network interface.
+	 * Some hotplug package track interfaces by their name and
+	 * therefore want to know when the name is changed by the user. */
+	if (!error)
+		kobject_uevent_env(kobj, KOBJ_MOVE, envp);
+
+out:
+	kfree(devpath_string);
+	kfree(devpath);
 	kobject_put(kobj);
 
 	return error;
@@ -488,13 +519,15 @@
 };
 
 /**
- *	kobject_add_dir - add sub directory of object.
+ *	kobject_kset_add_dir - add sub directory of object.
+ *	@kset:		kset the directory is belongs to.
  *	@parent:	object in which a directory is created.
  *	@name:	directory name.
  *
  *	Add a plain directory object as child of given object.
  */
-struct kobject *kobject_add_dir(struct kobject *parent, const char *name)
+struct kobject *kobject_kset_add_dir(struct kset *kset,
+				     struct kobject *parent, const char *name)
 {
 	struct kobject *k;
 	int ret;
@@ -506,13 +539,14 @@
 	if (!k)
 		return NULL;
 
+	k->kset = kset;
 	k->parent = parent;
 	k->ktype = &dir_ktype;
 	kobject_set_name(k, name);
 	ret = kobject_register(k);
 	if (ret < 0) {
-		printk(KERN_WARNING "kobject_add_dir: "
-			"kobject_register error: %d\n", ret);
+		printk(KERN_WARNING "%s: kobject_register error: %d\n",
+			__func__, ret);
 		kobject_del(k);
 		return NULL;
 	}
@@ -521,6 +555,18 @@
 }
 
 /**
+ *	kobject_add_dir - add sub directory of object.
+ *	@parent:	object in which a directory is created.
+ *	@name:	directory name.
+ *
+ *	Add a plain directory object as child of given object.
+ */
+struct kobject *kobject_add_dir(struct kobject *parent, const char *name)
+{
+	return kobject_kset_add_dir(NULL, parent, name);
+}
+
+/**
  *	kset_init - initialize a kset for use
  *	@k:	kset 
  */
@@ -613,7 +659,6 @@
 
 void subsystem_init(struct subsystem * s)
 {
-	init_rwsem(&s->rwsem);
 	kset_init(&s->kset);
 }
 
@@ -622,8 +667,7 @@
  *	@s:	the subsystem we're registering.
  *
  *	Once we register the subsystem, we want to make sure that 
- *	the kset points back to this subsystem for correct usage of 
- *	the rwsem. 
+ *	the kset points back to this subsystem.
  */
 
 int subsystem_register(struct subsystem * s)
diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c
index 84272ed..12e311d 100644
--- a/lib/kobject_uevent.c
+++ b/lib/kobject_uevent.c
@@ -42,10 +42,6 @@
 		return "remove";
 	case KOBJ_CHANGE:
 		return "change";
-	case KOBJ_MOUNT:
-		return "mount";
-	case KOBJ_UMOUNT:
-		return "umount";
 	case KOBJ_OFFLINE:
 		return "offline";
 	case KOBJ_ONLINE:
@@ -95,10 +91,8 @@
 
 	/* search the kset we belong to */
 	top_kobj = kobj;
-	if (!top_kobj->kset && top_kobj->parent) {
-		do {
-			top_kobj = top_kobj->parent;
-		} while (!top_kobj->kset && top_kobj->parent);
+	while (!top_kobj->kset && top_kobj->parent) {
+		top_kobj = top_kobj->parent;
 	}
 	if (!top_kobj->kset) {
 		pr_debug("kobject attempted to send uevent without kset!\n");
@@ -115,6 +109,16 @@
 			return 0;
 		}
 
+	/* originating subsystem */
+	if (uevent_ops && uevent_ops->name)
+		subsystem = uevent_ops->name(kset, kobj);
+	else
+		subsystem = kobject_name(&kset->kobj);
+	if (!subsystem) {
+		pr_debug("unset subsytem caused the event to drop!\n");
+		return 0;
+	}
+
 	/* environment index */
 	envp = kzalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL);
 	if (!envp)
@@ -134,12 +138,6 @@
 		goto exit;
 	}
 
-	/* originating subsystem */
-	if (uevent_ops && uevent_ops->name)
-		subsystem = uevent_ops->name(kset, kobj);
-	else
-		subsystem = kobject_name(&kset->kobj);
-
 	/* event environemnt for helper process only */
 	envp[i++] = "HOME=/";
 	envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
@@ -293,7 +291,7 @@
 static int __init kobject_uevent_init(void)
 {
 	uevent_sock = netlink_kernel_create(NETLINK_KOBJECT_UEVENT, 1, NULL,
-					    THIS_MODULE);
+					    NULL, THIS_MODULE);
 
 	if (!uevent_sock) {
 		printk(KERN_ERR
diff --git a/lib/kref.c b/lib/kref.c
index 0d07cc3..a6dc3ec 100644
--- a/lib/kref.c
+++ b/lib/kref.c
@@ -21,6 +21,7 @@
 void kref_init(struct kref *kref)
 {
 	atomic_set(&kref->refcount,1);
+	smp_mb();
 }
 
 /**
@@ -31,6 +32,7 @@
 {
 	WARN_ON(!atomic_read(&kref->refcount));
 	atomic_inc(&kref->refcount);
+	smp_mb__after_atomic_inc();
 }
 
 /**
diff --git a/lib/string.c b/lib/string.c
index bab440f..5efafed 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -60,6 +60,34 @@
 EXPORT_SYMBOL(strnicmp);
 #endif
 
+#ifndef __HAVE_ARCH_STRCASECMP
+int strcasecmp(const char *s1, const char *s2)
+{
+	int c1, c2;
+
+	do {
+		c1 = tolower(*s1++);
+		c2 = tolower(*s2++);
+	} while (c1 == c2 && c1 != 0);
+	return c1 - c2;
+}
+EXPORT_SYMBOL(strcasecmp);
+#endif
+
+#ifndef __HAVE_ARCH_STRNCASECMP
+int strncasecmp(const char *s1, const char *s2, size_t n)
+{
+	int c1, c2;
+
+	do {
+		c1 = tolower(*s1++);
+		c2 = tolower(*s2++);
+	} while ((--n > 0) && c1 == c2 && c1 != 0);
+	return c1 - c2;
+}
+EXPORT_SYMBOL(strncasecmp);
+#endif
+
 #ifndef __HAVE_ARCH_STRCPY
 /**
  * strcpy - Copy a %NUL terminated string
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index b025864..cbab1df 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -851,23 +851,35 @@
 
 
 /* Simplified asprintf. */
-char *kasprintf(gfp_t gfp, const char *fmt, ...)
+char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap)
 {
-	va_list ap;
 	unsigned int len;
 	char *p;
+	va_list aq;
 
-	va_start(ap, fmt);
-	len = vsnprintf(NULL, 0, fmt, ap);
-	va_end(ap);
+	va_copy(aq, ap);
+	len = vsnprintf(NULL, 0, fmt, aq);
+	va_end(aq);
 
 	p = kmalloc(len+1, gfp);
 	if (!p)
 		return NULL;
-	va_start(ap, fmt);
+
 	vsnprintf(p, len+1, fmt, ap);
-	va_end(ap);
+
 	return p;
 }
+EXPORT_SYMBOL(kvasprintf);
 
+char *kasprintf(gfp_t gfp, const char *fmt, ...)
+{
+	va_list ap;
+	char *p;
+
+	va_start(ap, fmt);
+	p = kvasprintf(gfp, fmt, ap);
+	va_end(ap);
+
+	return p;
+}
 EXPORT_SYMBOL(kasprintf);
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index f469e3c..a794945 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -67,12 +67,12 @@
 /*
  * Start background writeback (via pdflush) at this percentage
  */
-int dirty_background_ratio = 10;
+int dirty_background_ratio = 5;
 
 /*
  * The generator of dirty data starts writeback at this percentage
  */
-int vm_dirty_ratio = 40;
+int vm_dirty_ratio = 10;
 
 /*
  * The interval between `kupdate'-style writebacks, in jiffies
diff --git a/mm/rmap.c b/mm/rmap.c
index b82146e..59da5b7 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -498,8 +498,10 @@
 		struct address_space *mapping = page_mapping(page);
 		if (mapping)
 			ret = page_mkclean_file(mapping, page);
-		if (page_test_and_clear_dirty(page))
+		if (page_test_dirty(page)) {
+			page_clear_dirty(page);
 			ret = 1;
+		}
 	}
 
 	return ret;
@@ -605,8 +607,10 @@
 		 * Leaving it set also helps swapoff to reinstate ptes
 		 * faster for those pages still in swapcache.
 		 */
-		if (page_test_and_clear_dirty(page))
+		if (page_test_dirty(page)) {
+			page_clear_dirty(page);
 			set_page_dirty(page);
+		}
 		__dec_zone_page_state(page,
 				PageAnon(page) ? NR_ANON_PAGES : NR_FILE_MAPPED);
 	}
diff --git a/net/802/fddi.c b/net/802/fddi.c
index ace6386..91dde41 100644
--- a/net/802/fddi.c
+++ b/net/802/fddi.c
@@ -100,7 +100,7 @@
 	struct fddihdr *fddi = (struct fddihdr *)skb->data;
 
 #ifdef CONFIG_INET
-	if (fddi->hdr.llc_snap.ethertype == __constant_htons(ETH_P_IP))
+	if (fddi->hdr.llc_snap.ethertype == htons(ETH_P_IP))
 		/* Try to get ARP to resolve the header and fill destination address */
 		return arp_find(fddi->daddr, skb);
 	else
@@ -130,12 +130,13 @@
 	 * to start of packet data.  Assume 802.2 SNAP frames for now.
 	 */
 
-	skb->mac.raw = skb->data;	/* point to frame control (FC) */
+	skb->dev = dev;
+	skb_reset_mac_header(skb);	/* point to frame control (FC) */
 
 	if(fddi->hdr.llc_8022_1.dsap==0xe0)
 	{
 		skb_pull(skb, FDDI_K_8022_HLEN-3);
-		type = __constant_htons(ETH_P_802_2);
+		type = htons(ETH_P_802_2);
 	}
 	else
 	{
diff --git a/net/802/hippi.c b/net/802/hippi.c
index 578f2a3..87ffc12 100644
--- a/net/802/hippi.c
+++ b/net/802/hippi.c
@@ -60,7 +60,7 @@
 	 * Due to the stupidity of the little endian byte-order we
 	 * have to set the fp field this way.
 	 */
-	hip->fp.fixed		= __constant_htonl(0x04800018);
+	hip->fp.fixed		= htonl(0x04800018);
 	hip->fp.d2_size		= htonl(len + 8);
 	hip->le.fc		= 0;
 	hip->le.double_wide	= 0;	/* only HIPPI 800 for the time being */
@@ -104,7 +104,7 @@
 	 * Only IP is currently supported
 	 */
 
-	if(hip->snap.ethertype != __constant_htons(ETH_P_IP))
+	if(hip->snap.ethertype != htons(ETH_P_IP))
 	{
 		printk(KERN_DEBUG "%s: unable to resolve type %X addresses.\n",skb->dev->name,ntohs(hip->snap.ethertype));
 		return 0;
@@ -126,14 +126,14 @@
 {
 	struct hippi_hdr *hip;
 
-	hip = (struct hippi_hdr *) skb->data;
-
 	/*
 	 * This is actually wrong ... question is if we really should
 	 * set the raw address here.
 	 */
-	 skb->mac.raw = skb->data;
-	 skb_pull(skb, HIPPI_HLEN);
+	skb->dev = dev;
+	skb_reset_mac_header(skb);
+	hip = (struct hippi_hdr *)skb_mac_header(skb);
+	skb_pull(skb, HIPPI_HLEN);
 
 	/*
 	 * No fancy promisc stuff here now.
diff --git a/net/802/psnap.c b/net/802/psnap.c
index 6e7c212..04ee43e 100644
--- a/net/802/psnap.c
+++ b/net/802/psnap.c
@@ -56,10 +56,10 @@
 	};
 
 	rcu_read_lock();
-	proto = find_snap_client(skb->h.raw);
+	proto = find_snap_client(skb_transport_header(skb));
 	if (proto) {
 		/* Pass the frame on. */
-		skb->h.raw  += 5;
+		skb->transport_header += 5;
 		skb_pull_rcsum(skb, 5);
 		rc = proto->rcvfunc(skb, dev, &snap_packet_type, orig_dev);
 	} else {
diff --git a/net/802/tr.c b/net/802/tr.c
index 96bd144..0ba1946 100644
--- a/net/802/tr.c
+++ b/net/802/tr.c
@@ -189,11 +189,13 @@
 __be16 tr_type_trans(struct sk_buff *skb, struct net_device *dev)
 {
 
-	struct trh_hdr *trh=(struct trh_hdr *)skb->data;
+	struct trh_hdr *trh;
 	struct trllc *trllc;
 	unsigned riflen=0;
 
-	skb->mac.raw = skb->data;
+	skb->dev = dev;
+	skb_reset_mac_header(skb);
+	trh = tr_hdr(skb);
 
 	if(trh->saddr[0] & TR_RII)
 		riflen = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8;
@@ -552,7 +554,8 @@
 					if(j==1) {
 						segment=ntohs(entry->rseg[j-1])>>4;
 						seq_printf(seq,"  %03X",segment);
-					};
+					}
+
 					segment=ntohs(entry->rseg[j])>>4;
 					brdgnmb=ntohs(entry->rseg[j-1])&0x00f;
 					seq_printf(seq,"-%01X-%03X",brdgnmb,segment);
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index eb1c71e..c0c7bb8 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -470,7 +470,7 @@
 		 */
 	default:
 		snprintf(name, IFNAMSIZ, "vlan%.4i", VLAN_ID);
-	};
+	}
 
 	new_dev = alloc_netdev(sizeof(struct vlan_dev_info), name,
 			       vlan_setup);
@@ -685,7 +685,7 @@
 				break;
 		}
 		break;
-	};
+	}
 
 out:
 	return NOTIFY_DONE;
@@ -819,7 +819,7 @@
 		printk(VLAN_DBG "%s: Unknown VLAN CMD: %x \n",
 			__FUNCTION__, args.cmd);
 		return -EINVAL;
-	};
+	}
 out:
 	return err;
 }
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index b6e0eea..ec46084 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -66,7 +66,7 @@
 
 		memcpy(veth->h_source, dev->dev_addr, ETH_ALEN);
 		break;
-	};
+	}
 
 	return 0;
 }
@@ -83,7 +83,7 @@
 			/* Lifted from Gleb's VLAN code... */
 			memmove(skb->data - ETH_HLEN,
 				skb->data - VLAN_ETH_HLEN, 12);
-			skb->mac.raw += VLAN_HLEN;
+			skb->mac_header += VLAN_HLEN;
 		}
 	}
 
@@ -219,7 +219,7 @@
 		break;
 	default:
 		break;
-	};
+	}
 
 	/*  Was a VLAN packet, grab the encapsulated protocol, which the layer
 	 * three protocols care about.
@@ -258,7 +258,7 @@
 	 * won't work for fault tolerant netware but does for the rest.
 	 */
 	if (*(unsigned short *)rawp == 0xFFFF) {
-		skb->protocol = __constant_htons(ETH_P_802_3);
+		skb->protocol = htons(ETH_P_802_3);
 		/* place it back on the queue to be handled by true layer 3 protocols.
 		 */
 
@@ -281,7 +281,7 @@
 	/*
 	 *	Real 802.2 LLC
 	 */
-	skb->protocol = __constant_htons(ETH_P_802_2);
+	skb->protocol = htons(ETH_P_802_2);
 	/* place it back on the queue to be handled by upper layer protocols.
 	 */
 
@@ -382,7 +382,7 @@
 		}
 
 		skb->protocol = htons(ETH_P_8021Q);
-		skb->nh.raw = skb->data;
+		skb_reset_network_header(skb);
 	}
 
 	/* Before delegating work to the lower layer, enter our MAC-address */
@@ -448,7 +448,7 @@
 	 * OTHER THINGS LIKE FDDI/TokenRing/802.3 SNAPs...
 	 */
 
-	if (veth->h_vlan_proto != __constant_htons(ETH_P_8021Q)) {
+	if (veth->h_vlan_proto != htons(ETH_P_8021Q)) {
 		int orig_headroom = skb_headroom(skb);
 		unsigned short veth_TCI;
 
diff --git a/net/Kconfig b/net/Kconfig
index 9156578..2fc8e77 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -27,13 +27,6 @@
 
 menu "Networking options"
 
-config NETDEBUG
-	bool "Network packet debugging"
-	help
-	  You can say Y here if you want to get additional messages useful in
-	  debugging bad packets, but can overwhelm logs under denial of service
-	  attacks.
-
 source "net/packet/Kconfig"
 source "net/unix/Kconfig"
 source "net/xfrm/Kconfig"
@@ -219,14 +212,18 @@
 source "net/ax25/Kconfig"
 source "net/irda/Kconfig"
 source "net/bluetooth/Kconfig"
-source "net/ieee80211/Kconfig"
-
-config WIRELESS_EXT
-	bool
+source "net/rxrpc/Kconfig"
 
 config FIB_RULES
 	bool
 
+menu "Wireless"
+
+source "net/wireless/Kconfig"
+source "net/ieee80211/Kconfig"
+
+endmenu
+
 endif   # if NET
 endmenu # Networking
 
diff --git a/net/Makefile b/net/Makefile
index 4854ac5..6b74d41 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -38,6 +38,7 @@
 obj-$(CONFIG_BT)		+= bluetooth/
 obj-$(CONFIG_SUNRPC)		+= sunrpc/
 obj-$(CONFIG_RXRPC)		+= rxrpc/
+obj-$(CONFIG_AF_RXRPC)		+= rxrpc/
 obj-$(CONFIG_ATM)		+= atm/
 obj-$(CONFIG_DECNET)		+= decnet/
 obj-$(CONFIG_ECONET)		+= econet/
@@ -52,3 +53,5 @@
 ifeq ($(CONFIG_NET),y)
 obj-$(CONFIG_SYSCTL)		+= sysctl_net.o
 endif
+
+obj-y				+= wireless/
diff --git a/net/appletalk/aarp.c b/net/appletalk/aarp.c
index d89d62f..5ef6a23 100644
--- a/net/appletalk/aarp.c
+++ b/net/appletalk/aarp.c
@@ -118,7 +118,9 @@
 
 	/* Set up the buffer */
 	skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length);
-	skb->nh.raw      = skb->h.raw = skb_put(skb, sizeof(*eah));
+	skb_reset_network_header(skb);
+	skb_reset_transport_header(skb);
+	skb_put(skb, sizeof(*eah));
 	skb->protocol    = htons(ETH_P_ATALK);
 	skb->dev	 = dev;
 	eah		 = aarp_hdr(skb);
@@ -163,7 +165,9 @@
 
 	/* Set up the buffer */
 	skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length);
-	skb->nh.raw      = skb->h.raw = skb_put(skb, sizeof(*eah));
+	skb_reset_network_header(skb);
+	skb_reset_transport_header(skb);
+	skb_put(skb, sizeof(*eah));
 	skb->protocol    = htons(ETH_P_ATALK);
 	skb->dev	 = dev;
 	eah		 = aarp_hdr(skb);
@@ -212,7 +216,9 @@
 
 	/* Set up the buffer */
 	skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length);
-	skb->nh.raw      = skb->h.raw = skb_put(skb, sizeof(*eah));
+	skb_reset_network_header(skb);
+	skb_reset_transport_header(skb);
+	skb_put(skb, sizeof(*eah));
 	skb->protocol    = htons(ETH_P_ATALK);
 	skb->dev	 = dev;
 	eah		 = aarp_hdr(skb);
@@ -539,7 +545,7 @@
 	int hash;
 	struct aarp_entry *a;
 
-	skb->nh.raw = skb->data;
+	skb_reset_network_header(skb);
 
 	/* Check for LocalTalk first */
 	if (dev->type == ARPHRD_LOCALTLK) {
diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c
index c8b7dc2..f6a92a0 100644
--- a/net/appletalk/ddp.c
+++ b/net/appletalk/ddp.c
@@ -1275,7 +1275,7 @@
 	skb->protocol = htons(ETH_P_IP);
 	skb_pull(skb, 13);
 	skb->dev   = dev;
-	skb->h.raw = skb->data;
+	skb_reset_transport_header(skb);
 
 	stats = dev->priv;
 	stats->rx_packets++;
@@ -1383,10 +1383,10 @@
  *	@pt - packet type
  *
  *	Receive a packet (in skb) from device dev. This has come from the SNAP
- *	decoder, and on entry skb->h.raw is the DDP header, skb->len is the DDP
- *	header, skb->len is the DDP length. The physical headers have been
- *	extracted. PPP should probably pass frames marked as for this layer.
- *	[ie ARPHRD_ETHERTALK]
+ *	decoder, and on entry skb->transport_header is the DDP header, skb->len
+ *	is the DDP header, skb->len is the DDP length. The physical headers
+ *	have been extracted. PPP should probably pass frames marked as for this
+ *	layer.  [ie ARPHRD_ETHERTALK]
  */
 static int atalk_rcv(struct sk_buff *skb, struct net_device *dev,
 		     struct packet_type *pt, struct net_device *orig_dev)
@@ -1484,7 +1484,7 @@
 		     struct packet_type *pt, struct net_device *orig_dev)
 {
 	/* Expand any short form frames */
-	if (skb->mac.raw[2] == 1) {
+	if (skb_mac_header(skb)[2] == 1) {
 		struct ddpehdr *ddp;
 		/* Find our address */
 		struct atalk_addr *ap = atalk_find_dev_addr(dev);
@@ -1510,8 +1510,8 @@
 		 * we write the network numbers !
 		 */
 
-		ddp->deh_dnode = skb->mac.raw[0];     /* From physical header */
-		ddp->deh_snode = skb->mac.raw[1];     /* From physical header */
+		ddp->deh_dnode = skb_mac_header(skb)[0];     /* From physical header */
+		ddp->deh_snode = skb_mac_header(skb)[1];     /* From physical header */
 
 		ddp->deh_dnet  = ap->s_net;	/* Network number */
 		ddp->deh_snet  = ap->s_net;
@@ -1522,7 +1522,7 @@
 		/* Non routable, so force a drop if we slip up later */
 		ddp->deh_len_hops = htons(skb->len + (DDP_MAXHOPS << 10));
 	}
-	skb->h.raw = skb->data;
+	skb_reset_transport_header(skb);
 
 	return atalk_rcv(skb, dev, pt, orig_dev);
 freeit:
@@ -1771,6 +1771,9 @@
 		case SIOCGSTAMP:
 			rc = sock_get_timestamp(sk, argp);
 			break;
+		case SIOCGSTAMPNS:
+			rc = sock_get_timestampns(sk, argp);
+			break;
 		/* Routing */
 		case SIOCADDRT:
 		case SIOCDELRT:
diff --git a/net/atm/br2684.c b/net/atm/br2684.c
index ec4ebd3..0e9f00c 100644
--- a/net/atm/br2684.c
+++ b/net/atm/br2684.c
@@ -173,7 +173,7 @@
 	}
 	skb_push(skb, minheadroom);
 	if (brvcc->encaps == e_llc)
-		memcpy(skb->data, llc_oui_pid_pad, 10);
+		skb_copy_to_linear_data(skb, llc_oui_pid_pad, 10);
 	else
 		memset(skb->data, 0, 2);
 #endif /* FASTER_VERSION */
@@ -375,11 +375,11 @@
 {
 	if (brvcc->filter.netmask == 0)
 		return 0;			/* no filter in place */
-	if (type == __constant_htons(ETH_P_IP) &&
+	if (type == htons(ETH_P_IP) &&
 	    (((struct iphdr *) (skb->data))->daddr & brvcc->filter.
 	     netmask) == brvcc->filter.prefix)
 		return 0;
-	if (type == __constant_htons(ETH_P_ARP))
+	if (type == htons(ETH_P_ARP))
 		return 0;
 	/* TODO: we should probably filter ARPs too.. don't want to have
 	 *   them returning values that don't make sense, or is that ok?
@@ -458,7 +458,7 @@
 	/* FIXME: tcpdump shows that pointer to mac header is 2 bytes earlier,
 	   than should be. What else should I set? */
 	skb_pull(skb, plen);
-	skb->mac.raw = ((char *) (skb->data)) - ETH_HLEN;
+	skb_set_mac_header(skb, -ETH_HLEN);
 	skb->pkt_type = PACKET_HOST;
 #ifdef CONFIG_BR2684_FAST_TRANS
 	skb->protocol = ((u16 *) skb->data)[-1];
diff --git a/net/atm/clip.c b/net/atm/clip.c
index 8c38258..876b77f 100644
--- a/net/atm/clip.c
+++ b/net/atm/clip.c
@@ -213,7 +213,7 @@
 		return;
 	}
 	ATM_SKB(skb)->vcc = vcc;
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	if (!clip_vcc->encap
 	    || skb->len < RFC1483LLC_LEN
 	    || memcmp(skb->data, llc_oui, sizeof (llc_oui)))
@@ -702,7 +702,7 @@
 	.ops =			&atmarpd_dev_ops,
 	.type =			"arpd",
 	.number = 		999,
-	.lock =			SPIN_LOCK_UNLOCKED
+	.lock =			__SPIN_LOCK_UNLOCKED(atmarpd_dev.lock)
 };
 
 
diff --git a/net/atm/ioctl.c b/net/atm/ioctl.c
index 8ccee45..7afd8e7 100644
--- a/net/atm/ioctl.c
+++ b/net/atm/ioctl.c
@@ -82,6 +82,9 @@
 		case SIOCGSTAMP: /* borrowed from IP */
 			error = sock_get_timestamp(sk, argp);
 			goto done;
+		case SIOCGSTAMPNS: /* borrowed from IP */
+			error = sock_get_timestampns(sk, argp);
+			goto done;
 		case ATM_SETSC:
 			printk(KERN_WARNING "ATM_SETSC is obsolete\n");
 			error = 0;
diff --git a/net/atm/lec.c b/net/atm/lec.c
index 3d804d6..4dc5f2b 100644
--- a/net/atm/lec.c
+++ b/net/atm/lec.c
@@ -283,8 +283,8 @@
 	}
 
 	DPRINTK("skbuff head:%lx data:%lx tail:%lx end:%lx\n",
-		(long)skb->head, (long)skb->data, (long)skb->tail,
-		(long)skb->end);
+		(long)skb->head, (long)skb->data, (long)skb_tail_pointer(skb),
+		(long)skb_end_pointer(skb));
 #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
 	if (memcmp(skb->data, bridge_ula_lec, sizeof(bridge_ula_lec)) == 0)
 		lec_handle_bridge(skb, dev);
@@ -576,8 +576,8 @@
 					break;
 				}
 				skb2->len = sizeof(struct atmlec_msg);
-				memcpy(skb2->data, mesg,
-				       sizeof(struct atmlec_msg));
+				skb_copy_to_linear_data(skb2, mesg,
+							sizeof(*mesg));
 				atm_force_charge(priv->lecd, skb2->truesize);
 				sk = sk_atm(priv->lecd);
 				skb_queue_tail(&sk->sk_receive_queue, skb2);
@@ -630,7 +630,7 @@
 	.ops = &lecdev_ops,
 	.type = "lec",
 	.number = 999,		/* dummy device number */
-	.lock = SPIN_LOCK_UNLOCKED
+	.lock = __SPIN_LOCK_UNLOCKED(lecatm_dev.lock)
 };
 
 /*
@@ -825,7 +825,6 @@
 		if (!hlist_empty(&priv->lec_arp_empty_ones)) {
 			lec_arp_check_empties(priv, vcc, skb);
 		}
-		skb->dev = dev;
 		skb_pull(skb, 2);	/* skip lec_id */
 #ifdef CONFIG_TR
 		if (priv->is_trdev)
@@ -1338,7 +1337,7 @@
 		if (skb == NULL)
 			return -1;
 		skb->len = *sizeoftlvs;
-		memcpy(skb->data, *tlvs, *sizeoftlvs);
+		skb_copy_to_linear_data(skb, *tlvs, *sizeoftlvs);
 		retval = send_to_lecd(priv, l_arp_xmt, dst_mac, NULL, skb);
 	}
 	return retval;
@@ -1372,7 +1371,7 @@
 	if (skb == NULL)
 		return 0;
 	skb->len = sizeoftlvs;
-	memcpy(skb->data, tlvs, sizeoftlvs);
+	skb_copy_to_linear_data(skb, tlvs, sizeoftlvs);
 	retval = send_to_lecd(priv, l_associate_req, NULL, NULL, skb);
 	if (retval != 0)
 		printk("lec.c: lane2_associate_req() failed\n");
diff --git a/net/atm/mpc.c b/net/atm/mpc.c
index cb3c004..7c85aa5 100644
--- a/net/atm/mpc.c
+++ b/net/atm/mpc.c
@@ -504,11 +504,13 @@
 		tagged_llc_snap_hdr.tag = entry->ctrl_info.tag;
 		skb_pull(skb, ETH_HLEN);                       /* get rid of Eth header */
 		skb_push(skb, sizeof(tagged_llc_snap_hdr));    /* add LLC/SNAP header   */
-		memcpy(skb->data, &tagged_llc_snap_hdr, sizeof(tagged_llc_snap_hdr));
+		skb_copy_to_linear_data(skb, &tagged_llc_snap_hdr,
+					sizeof(tagged_llc_snap_hdr));
 	} else {
 		skb_pull(skb, ETH_HLEN);                        /* get rid of Eth header */
 		skb_push(skb, sizeof(struct llc_snap_hdr));     /* add LLC/SNAP header + tag  */
-		memcpy(skb->data, &llc_snap_mpoa_data, sizeof(struct llc_snap_hdr));
+		skb_copy_to_linear_data(skb, &llc_snap_mpoa_data,
+					sizeof(struct llc_snap_hdr));
 	}
 
 	atomic_add(skb->truesize, &sk_atm(entry->shortcut)->sk_wmem_alloc);
@@ -711,11 +713,12 @@
 		return;
 	}
 	skb_push(new_skb, eg->ctrl_info.DH_length);     /* add MAC header */
-	memcpy(new_skb->data, eg->ctrl_info.DLL_header, eg->ctrl_info.DH_length);
+	skb_copy_to_linear_data(new_skb, eg->ctrl_info.DLL_header,
+				eg->ctrl_info.DH_length);
 	new_skb->protocol = eth_type_trans(new_skb, dev);
-	new_skb->nh.raw = new_skb->data;
+	skb_reset_network_header(new_skb);
 
-	eg->latest_ip_addr = new_skb->nh.iph->saddr;
+	eg->latest_ip_addr = ip_hdr(new_skb)->saddr;
 	eg->packets_rcvd++;
 	mpc->eg_ops->put(eg);
 
@@ -734,7 +737,7 @@
 	.ops	= &mpc_ops,
 	.type	= "mpc",
 	.number	= 42,
-	.lock	= SPIN_LOCK_UNLOCKED
+	.lock	= __SPIN_LOCK_UNLOCKED(mpc_dev.lock)
 	/* members not explicitly initialised will be 0 */
 };
 
@@ -936,7 +939,7 @@
 	if (skb == NULL)
 		return -ENOMEM;
 	skb_put(skb, sizeof(struct k_message));
-	memcpy(skb->data, mesg, sizeof(struct k_message));
+	skb_copy_to_linear_data(skb, mesg, sizeof(*mesg));
 	atm_force_charge(mpc->mpoad_vcc, skb->truesize);
 
 	sk = sk_atm(mpc->mpoad_vcc);
diff --git a/net/atm/signaling.c b/net/atm/signaling.c
index 31d98b5..d14baaf 100644
--- a/net/atm/signaling.c
+++ b/net/atm/signaling.c
@@ -256,7 +256,7 @@
 	.ops =		&sigd_dev_ops,
 	.type =		"sig",
 	.number =	999,
-	.lock =		SPIN_LOCK_UNLOCKED
+	.lock =		__SPIN_LOCK_UNLOCKED(sigd_dev.lock)
 };
 
 
diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c
index 1c07c6a..6ded952 100644
--- a/net/ax25/af_ax25.c
+++ b/net/ax25/af_ax25.c
@@ -1127,22 +1127,22 @@
 		switch (sk->sk_state) {
 		case TCP_SYN_SENT: /* still trying */
 			err = -EINPROGRESS;
-			goto out;
+			goto out_release;
 
 		case TCP_ESTABLISHED: /* connection established */
 			sock->state = SS_CONNECTED;
-			goto out;
+			goto out_release;
 
 		case TCP_CLOSE: /* connection refused */
 			sock->state = SS_UNCONNECTED;
 			err = -ECONNREFUSED;
-			goto out;
+			goto out_release;
 		}
 	}
 
 	if (sk->sk_state == TCP_ESTABLISHED && sk->sk_type == SOCK_SEQPACKET) {
 		err = -EISCONN;	/* No reconnect on a seqpacket socket */
-		goto out;
+		goto out_release;
 	}
 
 	sk->sk_state   = TCP_CLOSE;
@@ -1159,12 +1159,12 @@
 		/* Valid number of digipeaters ? */
 		if (fsa->fsa_ax25.sax25_ndigis < 1 || fsa->fsa_ax25.sax25_ndigis > AX25_MAX_DIGIS) {
 			err = -EINVAL;
-			goto out;
+			goto out_release;
 		}
 
 		if ((digi = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) {
 			err = -ENOBUFS;
-			goto out;
+			goto out_release;
 		}
 
 		digi->ndigi      = fsa->fsa_ax25.sax25_ndigis;
@@ -1194,7 +1194,7 @@
 			current->comm);
 		if ((err = ax25_rt_autobind(ax25, &fsa->fsa_ax25.sax25_call)) < 0) {
 			kfree(digi);
-			goto out;
+			goto out_release;
 		}
 
 		ax25_fillin_cb(ax25, ax25->ax25_dev);
@@ -1203,7 +1203,7 @@
 		if (ax25->ax25_dev == NULL) {
 			kfree(digi);
 			err = -EHOSTUNREACH;
-			goto out;
+			goto out_release;
 		}
 	}
 
@@ -1213,7 +1213,7 @@
 		kfree(digi);
 		err = -EADDRINUSE;		/* Already such a connection */
 		ax25_cb_put(ax25t);
-		goto out;
+		goto out_release;
 	}
 
 	ax25->dest_addr = fsa->fsa_ax25.sax25_call;
@@ -1223,7 +1223,7 @@
 	if (sk->sk_type != SOCK_SEQPACKET) {
 		sock->state = SS_CONNECTED;
 		sk->sk_state   = TCP_ESTABLISHED;
-		goto out;
+		goto out_release;
 	}
 
 	/* Move to connecting socket, ax.25 lapb WAIT_UA.. */
@@ -1255,55 +1255,53 @@
 	/* Now the loop */
 	if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) {
 		err = -EINPROGRESS;
-		goto out;
+		goto out_release;
 	}
 
 	if (sk->sk_state == TCP_SYN_SENT) {
-		struct task_struct *tsk = current;
-		DECLARE_WAITQUEUE(wait, tsk);
+		DEFINE_WAIT(wait);
 
-		add_wait_queue(sk->sk_sleep, &wait);
 		for (;;) {
+			prepare_to_wait(sk->sk_sleep, &wait,
+			                TASK_INTERRUPTIBLE);
 			if (sk->sk_state != TCP_SYN_SENT)
 				break;
-			set_current_state(TASK_INTERRUPTIBLE);
-			release_sock(sk);
-			if (!signal_pending(tsk)) {
+			if (!signal_pending(current)) {
+				release_sock(sk);
 				schedule();
 				lock_sock(sk);
 				continue;
 			}
-			current->state = TASK_RUNNING;
-			remove_wait_queue(sk->sk_sleep, &wait);
-			return -ERESTARTSYS;
+			err = -ERESTARTSYS;
+			break;
 		}
-		current->state = TASK_RUNNING;
-		remove_wait_queue(sk->sk_sleep, &wait);
+		finish_wait(sk->sk_sleep, &wait);
+
+		if (err)
+			goto out_release;
 	}
 
 	if (sk->sk_state != TCP_ESTABLISHED) {
 		/* Not in ABM, not in WAIT_UA -> failed */
 		sock->state = SS_UNCONNECTED;
 		err = sock_error(sk);	/* Always set at this point */
-		goto out;
+		goto out_release;
 	}
 
 	sock->state = SS_CONNECTED;
 
-	err=0;
-out:
+	err = 0;
+out_release:
 	release_sock(sk);
 
 	return err;
 }
 
-
 static int ax25_accept(struct socket *sock, struct socket *newsock, int flags)
 {
-	struct task_struct *tsk = current;
-	DECLARE_WAITQUEUE(wait, tsk);
 	struct sk_buff *skb;
 	struct sock *newsk;
+	DEFINE_WAIT(wait);
 	struct sock *sk;
 	int err = 0;
 
@@ -1328,30 +1326,29 @@
 	 *	The read queue this time is holding sockets ready to use
 	 *	hooked into the SABM we saved
 	 */
-	add_wait_queue(sk->sk_sleep, &wait);
 	for (;;) {
+		prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
 		skb = skb_dequeue(&sk->sk_receive_queue);
 		if (skb)
 			break;
 
-		release_sock(sk);
-		current->state = TASK_INTERRUPTIBLE;
 		if (flags & O_NONBLOCK) {
-			current->state = TASK_RUNNING;
-			remove_wait_queue(sk->sk_sleep, &wait);
-			return -EWOULDBLOCK;
+			err = -EWOULDBLOCK;
+			break;
 		}
-		if (!signal_pending(tsk)) {
+		if (!signal_pending(current)) {
+			release_sock(sk);
 			schedule();
 			lock_sock(sk);
 			continue;
 		}
-		current->state = TASK_RUNNING;
-		remove_wait_queue(sk->sk_sleep, &wait);
-		return -ERESTARTSYS;
+		err = -ERESTARTSYS;
+		break;
 	}
-	current->state = TASK_RUNNING;
-	remove_wait_queue(sk->sk_sleep, &wait);
+	finish_wait(sk->sk_sleep, &wait);
+
+	if (err)
+		goto out;
 
 	newsk		 = skb->sk;
 	newsk->sk_socket = newsock;
@@ -1425,7 +1422,6 @@
 	struct sockaddr_ax25 sax;
 	struct sk_buff *skb;
 	ax25_digi dtmp, *dp;
-	unsigned char *asmptr;
 	ax25_cb *ax25;
 	size_t size;
 	int lv, err, addr_len = msg->msg_namelen;
@@ -1548,13 +1544,11 @@
 		goto out;
 	}
 
-	skb->nh.raw = skb->data;
+	skb_reset_network_header(skb);
 
 	/* Add the PID if one is not supplied by the user in the skb */
-	if (!ax25->pidincl) {
-		asmptr  = skb_push(skb, 1);
-		*asmptr = sk->sk_protocol;
-	}
+	if (!ax25->pidincl)
+		*skb_push(skb, 1) = sk->sk_protocol;
 
 	SOCK_DEBUG(sk, "AX.25: Transmitting buffer\n");
 
@@ -1573,7 +1567,7 @@
 		goto out;
 	}
 
-	asmptr = skb_push(skb, 1 + ax25_addr_size(dp));
+	skb_push(skb, 1 + ax25_addr_size(dp));
 
 	SOCK_DEBUG(sk, "Building AX.25 Header (dp=%p).\n", dp);
 
@@ -1581,17 +1575,17 @@
 		SOCK_DEBUG(sk, "Num digipeaters=%d\n", dp->ndigi);
 
 	/* Build an AX.25 header */
-	asmptr += (lv = ax25_addr_build(asmptr, &ax25->source_addr,
-					&sax.sax25_call, dp,
-					AX25_COMMAND, AX25_MODULUS));
+	lv = ax25_addr_build(skb->data, &ax25->source_addr, &sax.sax25_call,
+			     dp, AX25_COMMAND, AX25_MODULUS);
 
 	SOCK_DEBUG(sk, "Built header (%d bytes)\n",lv);
 
-	skb->h.raw = asmptr;
+	skb_set_transport_header(skb, lv);
 
-	SOCK_DEBUG(sk, "base=%p pos=%p\n", skb->data, asmptr);
+	SOCK_DEBUG(sk, "base=%p pos=%p\n",
+		   skb->data, skb_transport_header(skb));
 
-	*asmptr = AX25_UI;
+	*skb_transport_header(skb) = AX25_UI;
 
 	/* Datagram frames go straight out of the door as UI */
 	ax25_queue_xmit(skb, ax25->ax25_dev->dev);
@@ -1631,8 +1625,8 @@
 	if (!ax25_sk(sk)->pidincl)
 		skb_pull(skb, 1);		/* Remove PID */
 
-	skb->h.raw = skb->data;
-	copied     = skb->len;
+	skb_reset_transport_header(skb);
+	copied = skb->len;
 
 	if (copied > size) {
 		copied = size;
@@ -1645,9 +1639,10 @@
 		struct sockaddr_ax25 *sax = (struct sockaddr_ax25 *)msg->msg_name;
 		ax25_digi digi;
 		ax25_address src;
+		const unsigned char *mac = skb_mac_header(skb);
 
-		ax25_addr_parse(skb->mac.raw+1, skb->data-skb->mac.raw-1, &src, NULL, &digi, NULL, NULL);
-
+		ax25_addr_parse(mac + 1, skb->data - mac - 1, &src, NULL,
+				&digi, NULL, NULL);
 		sax->sax25_family = AF_AX25;
 		/* We set this correctly, even though we may not let the
 		   application know the digi calls further down (because it
@@ -1711,6 +1706,10 @@
 		res = sock_get_timestamp(sk, argp);
 		break;
 
+	case SIOCGSTAMPNS:
+		res = sock_get_timestampns(sk, argp);
+		break;
+
 	case SIOCAX25ADDUID:	/* Add a uid to the uid/call map table */
 	case SIOCAX25DELUID:	/* Delete a uid from the uid/call map table */
 	case SIOCAX25GETUID: {
diff --git a/net/ax25/ax25_ds_subr.c b/net/ax25/ax25_ds_subr.c
index 9569dd3..a49773f 100644
--- a/net/ax25/ax25_ds_subr.c
+++ b/net/ax25/ax25_ds_subr.c
@@ -136,7 +136,7 @@
 	if ((skb = alloc_skb(2, GFP_ATOMIC)) == NULL)
 		return;
 
-	skb->nh.raw = skb->data;
+	skb_reset_network_header(skb);
 	p = skb_put(skb, 2);
 
 	*p++ = cmd;
diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c
index 4a6b26b..0ddaff0 100644
--- a/net/ax25/ax25_in.c
+++ b/net/ax25/ax25_in.c
@@ -61,12 +61,14 @@
 					skb_reserve(skbn, AX25_MAX_HEADER_LEN);
 
 					skbn->dev   = ax25->ax25_dev->dev;
-					skbn->h.raw = skbn->data;
-					skbn->nh.raw = skbn->data;
+					skb_reset_network_header(skbn);
+					skb_reset_transport_header(skbn);
 
 					/* Copy data from the fragments */
 					while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL) {
-						memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
+						skb_copy_from_linear_data(skbo,
+							  skb_put(skbn, skbo->len),
+									  skbo->len);
 						kfree_skb(skbo);
 					}
 
@@ -122,8 +124,8 @@
 		}
 
 		skb_pull(skb, 1);	/* Remove PID */
-		skb->mac.raw  = skb->nh.raw;
-		skb->nh.raw   = skb->data;
+		skb_reset_mac_header(skb);
+		skb_reset_network_header(skb);
 		skb->dev      = ax25->ax25_dev->dev;
 		skb->pkt_type = PACKET_HOST;
 		skb->protocol = htons(ETH_P_IP);
@@ -196,7 +198,7 @@
 	 *	Process the AX.25/LAPB frame.
 	 */
 
-	skb->h.raw = skb->data;
+	skb_reset_transport_header(skb);
 
 	if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) {
 		kfree_skb(skb);
@@ -233,7 +235,7 @@
 
 	/* UI frame - bypass LAPB processing */
 	if ((*skb->data & ~0x10) == AX25_UI && dp.lastrepeat + 1 == dp.ndigi) {
-		skb->h.raw = skb->data + 2;		/* skip control and pid */
+		skb_set_transport_header(skb, 2); /* skip control and pid */
 
 		ax25_send_to_raw(&dest, skb, skb->data[1]);
 
@@ -246,8 +248,8 @@
 		switch (skb->data[1]) {
 		case AX25_P_IP:
 			skb_pull(skb,2);		/* drop PID/CTRL */
-			skb->h.raw    = skb->data;
-			skb->nh.raw   = skb->data;
+			skb_reset_transport_header(skb);
+			skb_reset_network_header(skb);
 			skb->dev      = dev;
 			skb->pkt_type = PACKET_HOST;
 			skb->protocol = htons(ETH_P_IP);
@@ -256,8 +258,8 @@
 
 		case AX25_P_ARP:
 			skb_pull(skb,2);
-			skb->h.raw    = skb->data;
-			skb->nh.raw   = skb->data;
+			skb_reset_transport_header(skb);
+			skb_reset_network_header(skb);
 			skb->dev      = dev;
 			skb->pkt_type = PACKET_HOST;
 			skb->protocol = htons(ETH_P_ARP);
diff --git a/net/ax25/ax25_ip.c b/net/ax25/ax25_ip.c
index 7f818bb..930e491 100644
--- a/net/ax25/ax25_ip.c
+++ b/net/ax25/ax25_ip.c
@@ -121,7 +121,7 @@
 		digipeat = route->digipeat;
 		dev = route->dev;
 		ip_mode = route->ip_mode;
-	};
+	}
 
 	if (dev == NULL)
 		dev = skb->dev;
@@ -171,7 +171,7 @@
 			src_c = *(ax25_address *)(bp + 8);
 
 			skb_pull(ourskb, AX25_HEADER_LEN - 1);	/* Keep PID */
-			ourskb->nh.raw = ourskb->data;
+			skb_reset_network_header(ourskb);
 
 			ax25=ax25_send_frame(
 			    ourskb,
diff --git a/net/ax25/ax25_out.c b/net/ax25/ax25_out.c
index 2238350..92b517a 100644
--- a/net/ax25/ax25_out.c
+++ b/net/ax25/ax25_out.c
@@ -148,8 +148,9 @@
 
 			if (ka9qfrag == 1) {
 				skb_reserve(skbn, frontlen + 2);
-				skbn->nh.raw = skbn->data + (skb->nh.raw - skb->data);
-				memcpy(skb_put(skbn, len), skb->data, len);
+				skb_set_network_header(skbn,
+						      skb_network_offset(skb));
+				skb_copy_from_linear_data(skb, skb_put(skbn, len), len);
 				p = skb_push(skbn, 2);
 
 				*p++ = AX25_P_SEGMENT;
@@ -161,8 +162,9 @@
 				}
 			} else {
 				skb_reserve(skbn, frontlen + 1);
-				skbn->nh.raw = skbn->data + (skb->nh.raw - skb->data);
-				memcpy(skb_put(skbn, len), skb->data, len);
+				skb_set_network_header(skbn,
+						      skb_network_offset(skb));
+				skb_copy_from_linear_data(skb, skb_put(skbn, len), len);
 				p = skb_push(skbn, 1);
 				*p = AX25_P_TEXT;
 			}
@@ -205,7 +207,7 @@
 	if (skb == NULL)
 		return;
 
-	skb->nh.raw = skb->data;
+	skb_reset_network_header(skb);
 
 	if (ax25->modulus == AX25_MODULUS) {
 		frame = skb_push(skb, 1);
diff --git a/net/ax25/ax25_subr.c b/net/ax25/ax25_subr.c
index b6c577e..5fe9b2a 100644
--- a/net/ax25/ax25_subr.c
+++ b/net/ax25/ax25_subr.c
@@ -162,7 +162,7 @@
 
 	skb_reserve(skb, ax25->ax25_dev->dev->hard_header_len);
 
-	skb->nh.raw = skb->data;
+	skb_reset_network_header(skb);
 
 	/* Assume a response - address structure for DTE */
 	if (ax25->modulus == AX25_MODULUS) {
@@ -205,7 +205,7 @@
 		return;	/* Next SABM will get DM'd */
 
 	skb_reserve(skb, dev->hard_header_len);
-	skb->nh.raw = skb->data;
+	skb_reset_network_header(skb);
 
 	ax25_digi_invert(digi, &retdigi);
 
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index c7228cf..d942b94 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -221,7 +221,7 @@
 		copied = len;
 	}
 
-	skb->h.raw = skb->data;
+	skb_reset_transport_header(skb);
 	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 
 	skb_free_datagram(sk, skb);
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index b85d149..ab2db55 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -326,7 +326,7 @@
 		return 0;
 	}
 
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 
 	/* Verify and pull out header */
 	if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
@@ -364,26 +364,28 @@
 
 	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);
+		memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb), ETH_ALEN);
 		put_unaligned(s->eh.h_proto, (__be16 *) __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);
+		memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb),
+		       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);
+		memcpy(__skb_put(nskb, ETH_ALEN * 2), skb_mac_header(skb),
+		       ETH_ALEN * 2);
 		put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
 		break;
 	}
 
-	memcpy(__skb_put(nskb, skb->len), skb->data, skb->len);
+	skb_copy_from_linear_data(skb, __skb_put(nskb, skb->len), skb->len);
 	kfree_skb(skb);
 
 	s->stats.rx_packets++;
-	nskb->dev       = dev;
 	nskb->ip_summed = CHECKSUM_NONE;
 	nskb->protocol  = eth_type_trans(nskb, dev);
 	netif_rx_ni(nskb);
diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c
index 3933608..66bef1c 100644
--- a/net/bluetooth/cmtp/core.c
+++ b/net/bluetooth/cmtp/core.c
@@ -124,7 +124,7 @@
 	}
 
 	if (skb && (skb->len > 0))
-		memcpy(skb_put(nskb, skb->len), skb->data, skb->len);
+		skb_copy_from_linear_data(skb, skb_put(nskb, skb->len), skb->len);
 
 	memcpy(skb_put(nskb, count), buf, count);
 
@@ -256,7 +256,7 @@
 			hdr[2] = size >> 8;
 		}
 
-		memcpy(skb_put(nskb, size), skb->data, size);
+		skb_copy_from_linear_data(skb, skb_put(nskb, size), size);
 		skb_pull(skb, size);
 
 		if (skb->len > 0) {
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index f3403fd..63980bd 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -72,11 +72,11 @@
 			inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) {
 		cp.pscan_rep_mode = ie->data.pscan_rep_mode;
 		cp.pscan_mode     = ie->data.pscan_mode;
-		cp.clock_offset   = ie->data.clock_offset | __cpu_to_le16(0x8000);
+		cp.clock_offset   = ie->data.clock_offset | cpu_to_le16(0x8000);
 		memcpy(conn->dev_class, ie->data.dev_class, 3);
 	}
 
-	cp.pkt_type = __cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK);
+	cp.pkt_type = cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK);
 	if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER))
 		cp.role_switch	= 0x01;
 	else
@@ -107,7 +107,7 @@
 
 	conn->state = BT_DISCONN;
 
-	cp.handle = __cpu_to_le16(conn->handle);
+	cp.handle = cpu_to_le16(conn->handle);
 	cp.reason = reason;
 	hci_send_cmd(conn->hdev, OGF_LINK_CTL,
 				OCF_DISCONNECT, sizeof(cp), &cp);
@@ -123,8 +123,8 @@
 	conn->state = BT_CONNECT;
 	conn->out = 1;
 
-	cp.pkt_type = __cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK);
-	cp.handle   = __cpu_to_le16(handle);
+	cp.pkt_type = cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK);
+	cp.handle   = cpu_to_le16(handle);
 
 	hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ADD_SCO, sizeof(cp), &cp);
 }
@@ -348,7 +348,7 @@
 
 	if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
 		struct hci_cp_auth_requested cp;
-		cp.handle = __cpu_to_le16(conn->handle);
+		cp.handle = cpu_to_le16(conn->handle);
 		hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_AUTH_REQUESTED, sizeof(cp), &cp);
 	}
 	return 0;
@@ -368,7 +368,7 @@
 
 	if (hci_conn_auth(conn)) {
 		struct hci_cp_set_conn_encrypt cp;
-		cp.handle  = __cpu_to_le16(conn->handle);
+		cp.handle  = cpu_to_le16(conn->handle);
 		cp.encrypt = 1;
 		hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT, sizeof(cp), &cp);
 	}
@@ -383,7 +383,7 @@
 
 	if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
 		struct hci_cp_change_conn_link_key cp;
-		cp.handle = __cpu_to_le16(conn->handle);
+		cp.handle = cpu_to_le16(conn->handle);
 		hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_CHANGE_CONN_LINK_KEY, sizeof(cp), &cp);
 	}
 	return 0;
@@ -423,7 +423,7 @@
 
 	if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
 		struct hci_cp_exit_sniff_mode cp;
-		cp.handle = __cpu_to_le16(conn->handle);
+		cp.handle = cpu_to_le16(conn->handle);
 		hci_send_cmd(hdev, OGF_LINK_POLICY,
 				OCF_EXIT_SNIFF_MODE, sizeof(cp), &cp);
 	}
@@ -452,21 +452,21 @@
 
 	if (lmp_sniffsubr_capable(hdev) && lmp_sniffsubr_capable(conn)) {
 		struct hci_cp_sniff_subrate cp;
-		cp.handle             = __cpu_to_le16(conn->handle);
-		cp.max_latency        = __constant_cpu_to_le16(0);
-		cp.min_remote_timeout = __constant_cpu_to_le16(0);
-		cp.min_local_timeout  = __constant_cpu_to_le16(0);
+		cp.handle             = cpu_to_le16(conn->handle);
+		cp.max_latency        = cpu_to_le16(0);
+		cp.min_remote_timeout = cpu_to_le16(0);
+		cp.min_local_timeout  = cpu_to_le16(0);
 		hci_send_cmd(hdev, OGF_LINK_POLICY,
 				OCF_SNIFF_SUBRATE, sizeof(cp), &cp);
 	}
 
 	if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
 		struct hci_cp_sniff_mode cp;
-		cp.handle       = __cpu_to_le16(conn->handle);
-		cp.max_interval = __cpu_to_le16(hdev->sniff_max_interval);
-		cp.min_interval = __cpu_to_le16(hdev->sniff_min_interval);
-		cp.attempt      = __constant_cpu_to_le16(4);
-		cp.timeout      = __constant_cpu_to_le16(1);
+		cp.handle       = cpu_to_le16(conn->handle);
+		cp.max_interval = cpu_to_le16(hdev->sniff_max_interval);
+		cp.min_interval = cpu_to_le16(hdev->sniff_min_interval);
+		cp.attempt      = cpu_to_le16(4);
+		cp.timeout      = cpu_to_le16(1);
 		hci_send_cmd(hdev, OGF_LINK_POLICY,
 				OCF_SNIFF_MODE, sizeof(cp), &cp);
 	}
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 4917919..aa4b56a 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -149,7 +149,7 @@
 	default:
 		err = -ETIMEDOUT;
 		break;
-	};
+	}
 
 	hdev->req_status = hdev->req_result = 0;
 
@@ -216,10 +216,10 @@
 	/* Host buffer size */
 	{
 		struct hci_cp_host_buffer_size cp;
-		cp.acl_mtu = __cpu_to_le16(HCI_MAX_ACL_SIZE);
+		cp.acl_mtu = cpu_to_le16(HCI_MAX_ACL_SIZE);
 		cp.sco_mtu = HCI_MAX_SCO_SIZE;
-		cp.acl_max_pkt = __cpu_to_le16(0xffff);
-		cp.sco_max_pkt = __cpu_to_le16(0xffff);
+		cp.acl_max_pkt = cpu_to_le16(0xffff);
+		cp.sco_max_pkt = cpu_to_le16(0xffff);
 		hci_send_cmd(hdev, OGF_HOST_CTL, OCF_HOST_BUFFER_SIZE, sizeof(cp), &cp);
 	}
 #endif
@@ -240,11 +240,11 @@
 	}
 
 	/* Page timeout ~20 secs */
-	param = __cpu_to_le16(0x8000);
+	param = cpu_to_le16(0x8000);
 	hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_PG_TIMEOUT, 2, &param);
 
 	/* Connection accept timeout ~20 secs */
-	param = __cpu_to_le16(0x7d00);
+	param = cpu_to_le16(0x7d00);
 	hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_CA_TIMEOUT, 2, &param);
 }
 
@@ -1034,7 +1034,7 @@
 	}
 
 	hdr = (struct hci_command_hdr *) skb_put(skb, HCI_COMMAND_HDR_SIZE);
-	hdr->opcode = __cpu_to_le16(hci_opcode_pack(ogf, ocf));
+	hdr->opcode = cpu_to_le16(hci_opcode_pack(ogf, ocf));
 	hdr->plen   = plen;
 
 	if (plen)
@@ -1060,7 +1060,7 @@
 
 	hdr = (void *) hdev->sent_cmd->data;
 
-	if (hdr->opcode != __cpu_to_le16(hci_opcode_pack(ogf, ocf)))
+	if (hdr->opcode != cpu_to_le16(hci_opcode_pack(ogf, ocf)))
 		return NULL;
 
 	BT_DBG("%s ogf 0x%x ocf 0x%x", hdev->name, ogf, ocf);
@@ -1074,11 +1074,11 @@
 	struct hci_acl_hdr *hdr;
 	int len = skb->len;
 
-	hdr = (struct hci_acl_hdr *) skb_push(skb, HCI_ACL_HDR_SIZE);
-	hdr->handle = __cpu_to_le16(hci_handle_pack(handle, flags));
-	hdr->dlen   = __cpu_to_le16(len);
-
-	skb->h.raw = (void *) hdr;
+	skb_push(skb, HCI_ACL_HDR_SIZE);
+	skb_reset_transport_header(skb);
+	hdr = (struct hci_acl_hdr *)skb_transport_header(skb);
+	hdr->handle = cpu_to_le16(hci_handle_pack(handle, flags));
+	hdr->dlen   = cpu_to_le16(len);
 }
 
 int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)
@@ -1140,11 +1140,12 @@
 		return -EINVAL;
 	}
 
-	hdr.handle = __cpu_to_le16(conn->handle);
+	hdr.handle = cpu_to_le16(conn->handle);
 	hdr.dlen   = skb->len;
 
-	skb->h.raw = skb_push(skb, HCI_SCO_HDR_SIZE);
-	memcpy(skb->h.raw, &hdr, HCI_SCO_HDR_SIZE);
+	skb_push(skb, HCI_SCO_HDR_SIZE);
+	skb_reset_transport_header(skb);
+	memcpy(skb_transport_header(skb), &hdr, HCI_SCO_HDR_SIZE);
 
 	skb->dev = (void *) hdev;
 	bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
@@ -1387,7 +1388,7 @@
 			case HCI_SCODATA_PKT:
 				kfree_skb(skb);
 				continue;
-			};
+			}
 		}
 
 		/* Process frame */
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 936d3fc..447ba71 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -783,7 +783,7 @@
 		if (conn->type == ACL_LINK && hdev->link_policy) {
 			struct hci_cp_write_link_policy cp;
 			cp.handle = ev->handle;
-			cp.policy = __cpu_to_le16(hdev->link_policy);
+			cp.policy = cpu_to_le16(hdev->link_policy);
 			hci_send_cmd(hdev, OGF_LINK_POLICY,
 				OCF_WRITE_LINK_POLICY, sizeof(cp), &cp);
 		}
@@ -793,8 +793,8 @@
 			struct hci_cp_change_conn_ptype cp;
 			cp.handle = ev->handle;
 			cp.pkt_type = (conn->type == ACL_LINK) ?
-				__cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK):
-				__cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK);
+				cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK):
+				cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK);
 
 			hci_send_cmd(hdev, OGF_LINK_CTL,
 				OCF_CHANGE_CONN_PTYPE, sizeof(cp), &cp);
@@ -970,7 +970,7 @@
 		if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) {
 			if (!ev->status) {
 				struct hci_cp_set_conn_encrypt cp;
-				cp.handle  = __cpu_to_le16(conn->handle);
+				cp.handle  = cpu_to_le16(conn->handle);
 				cp.encrypt = 1;
 				hci_send_cmd(conn->hdev, OGF_LINK_CTL,
 					OCF_SET_CONN_ENCRYPT, sizeof(cp), &cp);
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 71f5cfb..832b5f4 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -375,7 +375,7 @@
 		copied = len;
 	}
 
-	skb->h.raw = skb->data;
+	skb_reset_transport_header(skb);
 	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 
 	hci_sock_cmsg(sk, msg, skb);
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index e83ee82..a586787 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -459,8 +459,8 @@
 			sk->sk_state = BT_DISCONN;
 			l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
 
-			req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
-			req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+			req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid);
+			req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
 			l2cap_send_cmd(conn, l2cap_get_ident(conn),
 					L2CAP_DISCONN_REQ, sizeof(req), &req);
 		} else {
@@ -652,7 +652,7 @@
 		if (sk->sk_type == SOCK_SEQPACKET) {
 			struct l2cap_conn_req req;
 			l2cap_pi(sk)->ident = l2cap_get_ident(conn);
-			req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+			req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
 			req.psm  = l2cap_pi(sk)->psm;
 			l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
 					L2CAP_CONN_REQ, sizeof(req), &req);
@@ -868,8 +868,8 @@
 
 	/* Create L2CAP header */
 	lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
-	lh->cid = __cpu_to_le16(l2cap_pi(sk)->dcid);
-	lh->len = __cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
+	lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid);
+	lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
 
 	if (sk->sk_type == SOCK_DGRAM)
 		put_unaligned(l2cap_pi(sk)->psm, (u16 *) skb_put(skb, 2));
@@ -1096,7 +1096,7 @@
 		} else if (sk->sk_state == BT_CONNECT) {
 			struct l2cap_conn_req req;
 			l2cap_pi(sk)->ident = l2cap_get_ident(conn);
-			req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+			req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
 			req.psm  = l2cap_pi(sk)->psm;
 			l2cap_send_cmd(conn, l2cap_pi(sk)->ident, L2CAP_CONN_REQ, sizeof(req), &req);
 		}
@@ -1192,13 +1192,13 @@
 		return NULL;
 
 	lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
-	lh->len = __cpu_to_le16(L2CAP_CMD_HDR_SIZE + dlen);
-	lh->cid = __cpu_to_le16(0x0001);
+	lh->len = cpu_to_le16(L2CAP_CMD_HDR_SIZE + dlen);
+	lh->cid = cpu_to_le16(0x0001);
 
 	cmd = (struct l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE);
 	cmd->code  = code;
 	cmd->ident = ident;
-	cmd->len   = __cpu_to_le16(dlen);
+	cmd->len   = cpu_to_le16(dlen);
 
 	if (dlen) {
 		count -= L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE;
@@ -1316,11 +1316,11 @@
 		break;
 
 	case 2:
-		*((u16 *) opt->val) = __cpu_to_le16(val);
+		*((u16 *) opt->val) = cpu_to_le16(val);
 		break;
 
 	case 4:
-		*((u32 *) opt->val) = __cpu_to_le32(val);
+		*((u32 *) opt->val) = cpu_to_le32(val);
 		break;
 
 	default:
@@ -1346,8 +1346,8 @@
 	//if (flush_to != L2CAP_DEFAULT_FLUSH_TO)
 	//   l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2, pi->flush_to);
 
-	req->dcid  = __cpu_to_le16(pi->dcid);
-	req->flags = __cpu_to_le16(0);
+	req->dcid  = cpu_to_le16(pi->dcid);
+	req->flags = cpu_to_le16(0);
 
 	return ptr - data;
 }
@@ -1383,9 +1383,9 @@
 	else
 		flags = 0x0001;
 
-	rsp->scid   = __cpu_to_le16(l2cap_pi(sk)->dcid);
-	rsp->result = __cpu_to_le16(result ? *result : 0);
-	rsp->flags  = __cpu_to_le16(flags);
+	rsp->scid   = cpu_to_le16(l2cap_pi(sk)->dcid);
+	rsp->result = cpu_to_le16(result ? *result : 0);
+	rsp->flags  = cpu_to_le16(flags);
 
 	return ptr - data;
 }
@@ -1470,10 +1470,10 @@
 	bh_unlock_sock(parent);
 
 sendresp:
-	rsp.scid   = __cpu_to_le16(scid);
-	rsp.dcid   = __cpu_to_le16(dcid);
-	rsp.result = __cpu_to_le16(result);
-	rsp.status = __cpu_to_le16(status);
+	rsp.scid   = cpu_to_le16(scid);
+	rsp.dcid   = cpu_to_le16(dcid);
+	rsp.result = cpu_to_le16(result);
+	rsp.status = cpu_to_le16(status);
 	l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp);
 	return 0;
 }
@@ -1613,8 +1613,8 @@
 		l2cap_sock_set_timer(sk, HZ * 5);
 		{
 			struct l2cap_disconn_req req;
-			req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
-			req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+			req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid);
+			req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
 			l2cap_send_cmd(conn, l2cap_get_ident(conn),
 					L2CAP_DISCONN_REQ, sizeof(req), &req);
 		}
@@ -1652,8 +1652,8 @@
 	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))
 		return 0;
 
-	rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid);
-	rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+	rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
+	rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
 	l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp);
 
 	sk->sk_shutdown = SHUTDOWN_MASK;
@@ -1696,8 +1696,8 @@
 
 	BT_DBG("type 0x%4.4x", type);
 
-	rsp.type   = __cpu_to_le16(type);
-	rsp.result = __cpu_to_le16(L2CAP_IR_NOTSUPP);
+	rsp.type   = cpu_to_le16(type);
+	rsp.result = cpu_to_le16(L2CAP_IR_NOTSUPP);
 	l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(rsp), &rsp);
 
 	return 0;
@@ -1794,7 +1794,7 @@
 			BT_DBG("error %d", err);
 
 			/* FIXME: Map err to a valid reason */
-			rej.reason = __cpu_to_le16(0);
+			rej.reason = cpu_to_le16(0);
 			l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej);
 		}
 
@@ -1993,10 +1993,10 @@
 			result = L2CAP_CR_SEC_BLOCK;
 		}
 
-		rsp.scid   = __cpu_to_le16(l2cap_pi(sk)->dcid);
-		rsp.dcid   = __cpu_to_le16(l2cap_pi(sk)->scid);
-		rsp.result = __cpu_to_le16(result);
-		rsp.status = __cpu_to_le16(0);
+		rsp.scid   = cpu_to_le16(l2cap_pi(sk)->dcid);
+		rsp.dcid   = cpu_to_le16(l2cap_pi(sk)->scid);
+		rsp.result = cpu_to_le16(result);
+		rsp.status = cpu_to_le16(0);
 		l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
 				L2CAP_CONN_RSP, sizeof(rsp), &rsp);
 
@@ -2041,10 +2041,10 @@
 			result = L2CAP_CR_SEC_BLOCK;
 		}
 
-		rsp.scid   = __cpu_to_le16(l2cap_pi(sk)->dcid);
-		rsp.dcid   = __cpu_to_le16(l2cap_pi(sk)->scid);
-		rsp.result = __cpu_to_le16(result);
-		rsp.status = __cpu_to_le16(0);
+		rsp.scid   = cpu_to_le16(l2cap_pi(sk)->dcid);
+		rsp.dcid   = cpu_to_le16(l2cap_pi(sk)->scid);
+		rsp.result = cpu_to_le16(result);
+		rsp.status = cpu_to_le16(0);
 		l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
 				L2CAP_CONN_RSP, sizeof(rsp), &rsp);
 
@@ -2107,7 +2107,8 @@
 		if (!(conn->rx_skb = bt_skb_alloc(len, GFP_ATOMIC)))
 			goto drop;
 
-		memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
+		skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
+			      skb->len);
 		conn->rx_len = len - skb->len;
 	} else {
 		BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
@@ -2128,7 +2129,8 @@
 			goto drop;
 		}
 
-		memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
+		skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
+			      skb->len);
 		conn->rx_len -= skb->len;
 
 		if (!conn->rx_len) {
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index 94f4573..fe7df90 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -1567,7 +1567,7 @@
 
 	/* Trim FCS */
 	skb->len--; skb->tail--;
-	fcs = *(u8 *) skb->tail;
+	fcs = *(u8 *)skb_tail_pointer(skb);
 
 	if (__check_fcs(skb->data, type, fcs)) {
 		BT_ERR("bad checksum in packet");
@@ -1851,18 +1851,18 @@
 	BT_DBG("");
 
 	while (!atomic_read(&terminate)) {
+		set_current_state(TASK_INTERRUPTIBLE);
 		if (!test_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event)) {
 			/* No pending events. Let's sleep.
 			 * Incoming connections and data will wake us up. */
-			set_current_state(TASK_INTERRUPTIBLE);
 			schedule();
 		}
+		set_current_state(TASK_RUNNING);
 
 		/* Process stuff */
 		clear_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event);
 		rfcomm_process_sessions();
 	}
-	set_current_state(TASK_RUNNING);
 	return;
 }
 
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index ae43914..3f5163e 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -393,7 +393,7 @@
 	default:
 		sock_set_flag(sk, SOCK_ZAPPED);
 		break;
-	};
+	}
 
 	release_sock(sk);
 
diff --git a/net/bridge/br.c b/net/bridge/br.c
index 2994387..848b8fa 100644
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -37,7 +37,9 @@
 		return -EADDRINUSE;
 	}
 
-	br_fdb_init();
+	err = br_fdb_init();
+	if (err)
+		goto err_out1;
 
 	err = br_netfilter_init();
 	if (err)
@@ -47,7 +49,10 @@
 	if (err)
 		goto err_out2;
 
-	br_netlink_init();
+	err = br_netlink_init();
+	if (err)
+		goto err_out3;
+
 	brioctl_set(br_ioctl_deviceless_stub);
 	br_handle_frame_hook = br_handle_frame;
 
@@ -55,7 +60,8 @@
 	br_fdb_put_hook = br_fdb_put;
 
 	return 0;
-
+err_out3:
+	unregister_netdevice_notifier(&br_device_notifier);
 err_out2:
 	br_netfilter_fini();
 err_out1:
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 905a39c..5e1892d 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -37,7 +37,7 @@
 	br->statistics.tx_packets++;
 	br->statistics.tx_bytes += skb->len;
 
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	skb_pull(skb, ETH_HLEN);
 
 	if (dest[0] & 1)
@@ -83,27 +83,21 @@
 	return 0;
 }
 
-/* Allow setting mac address of pseudo-bridge to be same as
- * any of the bound interfaces
- */
+/* Allow setting mac address to any valid ethernet address. */
 static int br_set_mac_address(struct net_device *dev, void *p)
 {
 	struct net_bridge *br = netdev_priv(dev);
 	struct sockaddr *addr = p;
-	struct net_bridge_port *port;
-	int err = -EADDRNOTAVAIL;
+
+	if (!is_valid_ether_addr(addr->sa_data))
+		return -EINVAL;
 
 	spin_lock_bh(&br->lock);
-	list_for_each_entry(port, &br->port_list, list) {
-		if (!compare_ether_addr(port->dev->dev_addr, addr->sa_data)) {
-			br_stp_change_bridge_id(br, addr->sa_data);
-			err = 0;
-			break;
-		}
-	}
+	memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+	br_stp_change_bridge_id(br, addr->sa_data);
 	spin_unlock_bh(&br->lock);
 
-	return err;
+	return 0;
 }
 
 static void br_getinfo(struct net_device *dev, struct ethtool_drvinfo *info)
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 8d566c1..91b0170 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -20,19 +20,28 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/jhash.h>
+#include <linux/random.h>
 #include <asm/atomic.h>
+#include <asm/unaligned.h>
 #include "br_private.h"
 
 static struct kmem_cache *br_fdb_cache __read_mostly;
 static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
 		      const unsigned char *addr);
 
-void __init br_fdb_init(void)
+static u32 fdb_salt __read_mostly;
+
+int __init br_fdb_init(void)
 {
 	br_fdb_cache = kmem_cache_create("bridge_fdb_cache",
 					 sizeof(struct net_bridge_fdb_entry),
 					 0,
 					 SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (!br_fdb_cache)
+		return -ENOMEM;
+
+	get_random_bytes(&fdb_salt, sizeof(fdb_salt));
+	return 0;
 }
 
 void __exit br_fdb_fini(void)
@@ -44,24 +53,26 @@
 /* if topology_changing then use forward_delay (default 15 sec)
  * otherwise keep longer (default 5 minutes)
  */
-static __inline__ unsigned long hold_time(const struct net_bridge *br)
+static inline unsigned long hold_time(const struct net_bridge *br)
 {
 	return br->topology_change ? br->forward_delay : br->ageing_time;
 }
 
-static __inline__ int has_expired(const struct net_bridge *br,
+static inline int has_expired(const struct net_bridge *br,
 				  const struct net_bridge_fdb_entry *fdb)
 {
 	return !fdb->is_static
 		&& time_before_eq(fdb->ageing_timer + hold_time(br), jiffies);
 }
 
-static __inline__ int br_mac_hash(const unsigned char *mac)
+static inline int br_mac_hash(const unsigned char *mac)
 {
-	return jhash(mac, ETH_ALEN, 0) & (BR_HASH_SIZE - 1);
+	/* use 1 byte of OUI cnd 3 bytes of NIC */
+	u32 key = get_unaligned((u32 *)(mac + 2));
+	return jhash_1word(key, fdb_salt) & (BR_HASH_SIZE - 1);
 }
 
-static __inline__ void fdb_delete(struct net_bridge_fdb_entry *f)
+static inline void fdb_delete(struct net_bridge_fdb_entry *f)
 {
 	hlist_del_rcu(&f->hlist);
 	br_fdb_put(f);
@@ -128,7 +139,26 @@
 	mod_timer(&br->gc_timer, jiffies + HZ/10);
 }
 
+/* Completely flush all dynamic entries in forwarding database.*/
+void br_fdb_flush(struct net_bridge *br)
+{
+	int i;
 
+	spin_lock_bh(&br->hash_lock);
+	for (i = 0; i < BR_HASH_SIZE; i++) {
+		struct net_bridge_fdb_entry *f;
+		struct hlist_node *h, *n;
+		hlist_for_each_entry_safe(f, h, n, &br->hash[i], hlist) {
+			if (!f->is_static)
+				fdb_delete(f);
+		}
+	}
+	spin_unlock_bh(&br->hash_lock);
+}
+
+/* Flush all entries refering to a specific port.
+ * if do_all is set also flush static entries
+ */
 void br_fdb_delete_by_port(struct net_bridge *br,
 			   const struct net_bridge_port *p,
 			   int do_all)
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index 3e45c1a..ada7f49 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -71,7 +71,7 @@
 
 	indev = skb->dev;
 	skb->dev = to->dev;
-	skb->ip_summed = CHECKSUM_NONE;
+	skb_forward_csum(skb);
 
 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
 			br_forward_finish);
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index f3a2e29..690573b 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -152,6 +152,8 @@
 	br_stp_disable_port(p);
 	spin_unlock_bh(&br->lock);
 
+	br_ifinfo_notify(RTM_DELLINK, p);
+
 	br_fdb_delete_by_port(br, p, 1);
 
 	list_del_rcu(&p->list);
@@ -203,7 +205,7 @@
 	memcpy(br->group_addr, br_group_address, ETH_ALEN);
 
 	br->feature_mask = dev->features;
-	br->stp_enabled = 0;
+	br->stp_enabled = BR_NO_STP;
 	br->designated_root = br->bridge_id;
 	br->root_path_cost = 0;
 	br->root_port = 0;
@@ -434,6 +436,8 @@
 		br_stp_enable_port(p);
 	spin_unlock_bh(&br->lock);
 
+	br_ifinfo_notify(RTM_NEWLINK, p);
+
 	dev_set_mtu(br->dev, br_min_mtu(br));
 
 	kobject_uevent(&p->kobj, KOBJ_ADD);
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 35b94f9..420bbb9 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -112,46 +112,59 @@
  */
 static inline int is_link_local(const unsigned char *dest)
 {
-	return memcmp(dest, br_group_address, 5) == 0 && (dest[5] & 0xf0) == 0;
+	const u16 *a = (const u16 *) dest;
+	static const u16 *const b = (const u16 *const ) br_group_address;
+	static const u16 m = __constant_cpu_to_be16(0xfff0);
+
+	return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | ((a[2] ^ b[2]) & m)) == 0;
 }
 
 /*
  * Called via br_handle_frame_hook.
- * Return 0 if *pskb should be processed furthur
- *	  1 if *pskb is handled
+ * Return NULL if skb is handled
  * note: already called with rcu_read_lock (preempt_disabled)
  */
-int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb)
+struct sk_buff *br_handle_frame(struct net_bridge_port *p, struct sk_buff *skb)
 {
-	struct sk_buff *skb = *pskb;
 	const unsigned char *dest = eth_hdr(skb)->h_dest;
 
 	if (!is_valid_ether_addr(eth_hdr(skb)->h_source))
-		goto err;
+		goto drop;
 
 	if (unlikely(is_link_local(dest))) {
-		skb->pkt_type = PACKET_HOST;
-		return NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
-			       NULL, br_handle_local_finish) != 0;
+		/* Pause frames shouldn't be passed up by driver anyway */
+		if (skb->protocol == htons(ETH_P_PAUSE))
+			goto drop;
+
+		/* Process STP BPDU's through normal netif_receive_skb() path */
+		if (p->br->stp_enabled != BR_NO_STP) {
+			if (NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
+				    NULL, br_handle_local_finish))
+				return NULL;
+			else
+				return skb;
+		}
 	}
 
-	if (p->state == BR_STATE_FORWARDING || p->state == BR_STATE_LEARNING) {
+	switch (p->state) {
+	case BR_STATE_FORWARDING:
+
 		if (br_should_route_hook) {
-			if (br_should_route_hook(pskb))
-				return 0;
-			skb = *pskb;
+			if (br_should_route_hook(&skb))
+				return skb;
 			dest = eth_hdr(skb)->h_dest;
 		}
-
+		/* fall through */
+	case BR_STATE_LEARNING:
 		if (!compare_ether_addr(p->br->dev->dev_addr, dest))
 			skb->pkt_type = PACKET_HOST;
 
 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
 			br_handle_frame_finish);
-		return 1;
+		break;
+	default:
+drop:
+		kfree_skb(skb);
 	}
-
-err:
-	kfree_skb(skb);
-	return 1;
+	return NULL;
 }
diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c
index 147015f..eda0fbf 100644
--- a/net/bridge/br_ioctl.c
+++ b/net/bridge/br_ioctl.c
@@ -137,7 +137,8 @@
 		b.topology_change = br->topology_change;
 		b.topology_change_detected = br->topology_change_detected;
 		b.root_port = br->root_port;
-		b.stp_enabled = br->stp_enabled;
+
+		b.stp_enabled = (br->stp_enabled != BR_NO_STP);
 		b.ageing_time = jiffies_to_clock_t(br->ageing_time);
 		b.hello_timer_value = br_timer_value(&br->hello_timer);
 		b.tcn_timer_value = br_timer_value(&br->tcn_timer);
@@ -251,7 +252,7 @@
 		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
 
-		br->stp_enabled = args[1]?1:0;
+		br_stp_set_enabled(br, args[1]);
 		return 0;
 
 	case BRCTL_SET_BRIDGE_PRIORITY:
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
index 5439a3c..9b2986b 100644
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -29,6 +29,8 @@
 #include <linux/if_arp.h>
 #include <linux/if_ether.h>
 #include <linux/if_vlan.h>
+#include <linux/if_pppox.h>
+#include <linux/ppp_defs.h>
 #include <linux/netfilter_bridge.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/netfilter_ipv6.h>
@@ -48,8 +50,8 @@
 
 #define skb_origaddr(skb)	 (((struct bridge_skb_cb *) \
 				 (skb->nf_bridge->data))->daddr.ipv4)
-#define store_orig_dstaddr(skb)	 (skb_origaddr(skb) = (skb)->nh.iph->daddr)
-#define dnat_took_place(skb)	 (skb_origaddr(skb) != (skb)->nh.iph->daddr)
+#define store_orig_dstaddr(skb)	 (skb_origaddr(skb) = ip_hdr(skb)->daddr)
+#define dnat_took_place(skb)	 (skb_origaddr(skb) != ip_hdr(skb)->daddr)
 
 #ifdef CONFIG_SYSCTL
 static struct ctl_table_header *brnf_sysctl_header;
@@ -57,8 +59,10 @@
 static int brnf_call_ip6tables __read_mostly = 1;
 static int brnf_call_arptables __read_mostly = 1;
 static int brnf_filter_vlan_tagged __read_mostly = 1;
+static int brnf_filter_pppoe_tagged __read_mostly = 1;
 #else
 #define brnf_filter_vlan_tagged 1
+#define brnf_filter_pppoe_tagged 1
 #endif
 
 static inline __be16 vlan_proto(const struct sk_buff *skb)
@@ -81,6 +85,22 @@
 	 vlan_proto(skb) == htons(ETH_P_ARP) &&	\
 	 brnf_filter_vlan_tagged)
 
+static inline __be16 pppoe_proto(const struct sk_buff *skb)
+{
+	return *((__be16 *)(skb_mac_header(skb) + ETH_HLEN +
+			    sizeof(struct pppoe_hdr)));
+}
+
+#define IS_PPPOE_IP(skb) \
+	(skb->protocol == htons(ETH_P_PPP_SES) && \
+	 pppoe_proto(skb) == htons(PPP_IP) && \
+	 brnf_filter_pppoe_tagged)
+
+#define IS_PPPOE_IPV6(skb) \
+	(skb->protocol == htons(ETH_P_PPP_SES) && \
+	 pppoe_proto(skb) == htons(PPP_IPV6) && \
+	 brnf_filter_pppoe_tagged)
+
 /* We need these fake structures to make netfilter happy --
  * lots of places assume that skb->dst != NULL, which isn't
  * all that unreasonable.
@@ -128,8 +148,11 @@
 
 	if (skb->protocol == htons(ETH_P_8021Q))
 		header_size += VLAN_HLEN;
+	else if (skb->protocol == htons(ETH_P_PPP_SES))
+		header_size += PPPOE_SES_HLEN;
 
-	memcpy(skb->nf_bridge->data, skb->data - header_size, header_size);
+	skb_copy_from_linear_data_offset(skb, -header_size,
+					 skb->nf_bridge->data, header_size);
 }
 
 /*
@@ -143,15 +166,20 @@
 
 	if (skb->protocol == htons(ETH_P_8021Q))
 		header_size += VLAN_HLEN;
+	else if (skb->protocol == htons(ETH_P_PPP_SES))
+		header_size += PPPOE_SES_HLEN;
 
 	err = skb_cow(skb, header_size);
 	if (err)
 		return err;
 
-	memcpy(skb->data - header_size, skb->nf_bridge->data, header_size);
+	skb_copy_to_linear_data_offset(skb, -header_size,
+				       skb->nf_bridge->data, header_size);
 
 	if (skb->protocol == htons(ETH_P_8021Q))
 		__skb_push(skb, VLAN_HLEN);
+	else if (skb->protocol == htons(ETH_P_PPP_SES))
+		__skb_push(skb, PPPOE_SES_HLEN);
 	return 0;
 }
 
@@ -174,7 +202,10 @@
 	skb->dev = nf_bridge->physindev;
 	if (skb->protocol == htons(ETH_P_8021Q)) {
 		skb_push(skb, VLAN_HLEN);
-		skb->nh.raw -= VLAN_HLEN;
+		skb->network_header -= VLAN_HLEN;
+	} else if (skb->protocol == htons(ETH_P_PPP_SES)) {
+		skb_push(skb, PPPOE_SES_HLEN);
+		skb->network_header -= PPPOE_SES_HLEN;
 	}
 	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
 		       br_handle_frame_finish, 1);
@@ -255,7 +286,10 @@
 	else {
 		if (skb->protocol == htons(ETH_P_8021Q)) {
 			skb_pull(skb, VLAN_HLEN);
-			skb->nh.raw += VLAN_HLEN;
+			skb->network_header += VLAN_HLEN;
+		} else if (skb->protocol == htons(ETH_P_PPP_SES)) {
+			skb_pull(skb, PPPOE_SES_HLEN);
+			skb->network_header += PPPOE_SES_HLEN;
 		}
 		skb->dst->output(skb);
 	}
@@ -265,7 +299,7 @@
 static int br_nf_pre_routing_finish(struct sk_buff *skb)
 {
 	struct net_device *dev = skb->dev;
-	struct iphdr *iph = skb->nh.iph;
+	struct iphdr *iph = ip_hdr(skb);
 	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
 	int err;
 
@@ -325,7 +359,11 @@
 				if (skb->protocol ==
 				    htons(ETH_P_8021Q)) {
 					skb_push(skb, VLAN_HLEN);
-					skb->nh.raw -= VLAN_HLEN;
+					skb->network_header -= VLAN_HLEN;
+				} else if(skb->protocol ==
+				    htons(ETH_P_PPP_SES)) {
+					skb_push(skb, PPPOE_SES_HLEN);
+					skb->network_header -= PPPOE_SES_HLEN;
 				}
 				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
 					       skb, skb->dev, NULL,
@@ -344,7 +382,10 @@
 	skb->dev = nf_bridge->physindev;
 	if (skb->protocol == htons(ETH_P_8021Q)) {
 		skb_push(skb, VLAN_HLEN);
-		skb->nh.raw -= VLAN_HLEN;
+		skb->network_header -= VLAN_HLEN;
+	} else if (skb->protocol == htons(ETH_P_PPP_SES)) {
+		skb_push(skb, PPPOE_SES_HLEN);
+		skb->network_header -= PPPOE_SES_HLEN;
 	}
 	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
 		       br_handle_frame_finish, 1);
@@ -372,9 +413,10 @@
 /* We only check the length. A bridge shouldn't do any hop-by-hop stuff anyway */
 static int check_hbh_len(struct sk_buff *skb)
 {
-	unsigned char *raw = (u8 *) (skb->nh.ipv6h + 1);
+	unsigned char *raw = (u8 *)(ipv6_hdr(skb) + 1);
 	u32 pkt_len;
-	int off = raw - skb->nh.raw;
+	const unsigned char *nh = skb_network_header(skb);
+	int off = raw - nh;
 	int len = (raw[1] + 1) << 3;
 
 	if ((raw + len) - skb->data > skb_headlen(skb))
@@ -384,9 +426,9 @@
 	len -= 2;
 
 	while (len > 0) {
-		int optlen = skb->nh.raw[off + 1] + 2;
+		int optlen = nh[off + 1] + 2;
 
-		switch (skb->nh.raw[off]) {
+		switch (nh[off]) {
 		case IPV6_TLV_PAD0:
 			optlen = 1;
 			break;
@@ -395,17 +437,18 @@
 			break;
 
 		case IPV6_TLV_JUMBO:
-			if (skb->nh.raw[off + 1] != 4 || (off & 3) != 2)
+			if (nh[off + 1] != 4 || (off & 3) != 2)
 				goto bad;
-			pkt_len = ntohl(*(__be32 *) (skb->nh.raw + off + 2));
+			pkt_len = ntohl(*(__be32 *) (nh + off + 2));
 			if (pkt_len <= IPV6_MAXPLEN ||
-			    skb->nh.ipv6h->payload_len)
+			    ipv6_hdr(skb)->payload_len)
 				goto bad;
 			if (pkt_len > skb->len - sizeof(struct ipv6hdr))
 				goto bad;
 			if (pskb_trim_rcsum(skb,
 					    pkt_len + sizeof(struct ipv6hdr)))
 				goto bad;
+			nh = skb_network_header(skb);
 			break;
 		default:
 			if (optlen > len)
@@ -439,7 +482,7 @@
 	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
 		goto inhdr_error;
 
-	hdr = skb->nh.ipv6h;
+	hdr = ipv6_hdr(skb);
 
 	if (hdr->version != 6)
 		goto inhdr_error;
@@ -485,7 +528,8 @@
 	__u32 len;
 	struct sk_buff *skb = *pskb;
 
-	if (skb->protocol == htons(ETH_P_IPV6) || IS_VLAN_IPV6(skb)) {
+	if (skb->protocol == htons(ETH_P_IPV6) || IS_VLAN_IPV6(skb) ||
+	    IS_PPPOE_IPV6(skb)) {
 #ifdef CONFIG_SYSCTL
 		if (!brnf_call_ip6tables)
 			return NF_ACCEPT;
@@ -495,7 +539,10 @@
 
 		if (skb->protocol == htons(ETH_P_8021Q)) {
 			skb_pull_rcsum(skb, VLAN_HLEN);
-			skb->nh.raw += VLAN_HLEN;
+			skb->network_header += VLAN_HLEN;
+		} else if (skb->protocol == htons(ETH_P_PPP_SES)) {
+			skb_pull_rcsum(skb, PPPOE_SES_HLEN);
+			skb->network_header += PPPOE_SES_HLEN;
 		}
 		return br_nf_pre_routing_ipv6(hook, skb, in, out, okfn);
 	}
@@ -504,7 +551,8 @@
 		return NF_ACCEPT;
 #endif
 
-	if (skb->protocol != htons(ETH_P_IP) && !IS_VLAN_IP(skb))
+	if (skb->protocol != htons(ETH_P_IP) && !IS_VLAN_IP(skb) &&
+	    !IS_PPPOE_IP(skb))
 		return NF_ACCEPT;
 
 	if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
@@ -512,20 +560,23 @@
 
 	if (skb->protocol == htons(ETH_P_8021Q)) {
 		skb_pull_rcsum(skb, VLAN_HLEN);
-		skb->nh.raw += VLAN_HLEN;
+		skb->network_header += VLAN_HLEN;
+	} else if (skb->protocol == htons(ETH_P_PPP_SES)) {
+		skb_pull_rcsum(skb, PPPOE_SES_HLEN);
+		skb->network_header += PPPOE_SES_HLEN;
 	}
 
 	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
 		goto inhdr_error;
 
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 	if (iph->ihl < 5 || iph->version != 4)
 		goto inhdr_error;
 
 	if (!pskb_may_pull(skb, 4 * iph->ihl))
 		goto inhdr_error;
 
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 	if (ip_fast_csum((__u8 *) iph, iph->ihl) != 0)
 		goto inhdr_error;
 
@@ -593,7 +644,10 @@
 	}
 	if (skb->protocol == htons(ETH_P_8021Q)) {
 		skb_push(skb, VLAN_HLEN);
-		skb->nh.raw -= VLAN_HLEN;
+		skb->network_header -= VLAN_HLEN;
+	} else if (skb->protocol == htons(ETH_P_PPP_SES)) {
+		skb_push(skb, PPPOE_SES_HLEN);
+		skb->network_header -= PPPOE_SES_HLEN;
 	}
 	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, in,
 		       skb->dev, br_forward_finish, 1);
@@ -622,14 +676,18 @@
 	if (!parent)
 		return NF_DROP;
 
-	if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb))
+	if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb) ||
+	    IS_PPPOE_IP(skb))
 		pf = PF_INET;
 	else
 		pf = PF_INET6;
 
 	if (skb->protocol == htons(ETH_P_8021Q)) {
 		skb_pull(*pskb, VLAN_HLEN);
-		(*pskb)->nh.raw += VLAN_HLEN;
+		(*pskb)->network_header += VLAN_HLEN;
+	} else if (skb->protocol == htons(ETH_P_PPP_SES)) {
+		skb_pull(*pskb, PPPOE_SES_HLEN);
+		(*pskb)->network_header += PPPOE_SES_HLEN;
 	}
 
 	nf_bridge = skb->nf_bridge;
@@ -665,13 +723,13 @@
 		if (!IS_VLAN_ARP(skb))
 			return NF_ACCEPT;
 		skb_pull(*pskb, VLAN_HLEN);
-		(*pskb)->nh.raw += VLAN_HLEN;
+		(*pskb)->network_header += VLAN_HLEN;
 	}
 
-	if (skb->nh.arph->ar_pln != 4) {
+	if (arp_hdr(skb)->ar_pln != 4) {
 		if (IS_VLAN_ARP(skb)) {
 			skb_push(*pskb, VLAN_HLEN);
-			(*pskb)->nh.raw -= VLAN_HLEN;
+			(*pskb)->network_header -= VLAN_HLEN;
 		}
 		return NF_ACCEPT;
 	}
@@ -721,7 +779,10 @@
 	}
 	if (skb->protocol == htons(ETH_P_8021Q)) {
 		skb_push(skb, VLAN_HLEN);
-		skb->nh.raw -= VLAN_HLEN;
+		skb->network_header -= VLAN_HLEN;
+	} else if (skb->protocol == htons(ETH_P_PPP_SES)) {
+		skb_push(skb, PPPOE_SES_HLEN);
+		skb->network_header -= PPPOE_SES_HLEN;
 	}
 
 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev, skb->dev,
@@ -753,7 +814,8 @@
 #ifdef CONFIG_NETFILTER_DEBUG
 	/* Be very paranoid. This probably won't happen anymore, but let's
 	 * keep the check just to be sure... */
-	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
+	if (skb_mac_header(skb) < skb->head ||
+	    skb_mac_header(skb) + ETH_HLEN > skb->data) {
 		printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: "
 		       "bad mac.raw pointer.\n");
 		goto print_error;
@@ -766,7 +828,8 @@
 	if (!realoutdev)
 		return NF_DROP;
 
-	if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb))
+	if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb) ||
+	    IS_PPPOE_IP(skb))
 		pf = PF_INET;
 	else
 		pf = PF_INET6;
@@ -787,7 +850,10 @@
 
 	if (skb->protocol == htons(ETH_P_8021Q)) {
 		skb_pull(skb, VLAN_HLEN);
-		skb->nh.raw += VLAN_HLEN;
+		skb->network_header += VLAN_HLEN;
+	} else if (skb->protocol == htons(ETH_P_PPP_SES)) {
+		skb_pull(skb, PPPOE_SES_HLEN);
+		skb->network_header += PPPOE_SES_HLEN;
 	}
 
 	nf_bridge_save_header(skb);
@@ -808,7 +874,7 @@
 		if (realoutdev)
 			printk("[%s]", realoutdev->name);
 	}
-	printk(" head:%p, raw:%p, data:%p\n", skb->head, skb->mac.raw,
+	printk(" head:%p, raw:%p, data:%p\n", skb->head, skb_mac_header(skb),
 	       skb->data);
 	dump_stack();
 	return NF_ACCEPT;
@@ -925,6 +991,14 @@
 		.mode		= 0644,
 		.proc_handler	= &brnf_sysctl_call_tables,
 	},
+	{
+		.ctl_name	= NET_BRIDGE_NF_FILTER_PPPOE_TAGGED,
+		.procname	= "bridge-nf-filter-pppoe-tagged",
+		.data		= &brnf_filter_pppoe_tagged,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &brnf_sysctl_call_tables,
+	},
 	{ .ctl_name = 0 }
 };
 
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 7d68b24..35facc0 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -11,8 +11,7 @@
  */
 
 #include <linux/kernel.h>
-#include <linux/rtnetlink.h>
-#include <net/netlink.h>
+#include <net/rtnetlink.h>
 #include "br_private.h"
 
 static inline size_t br_nlmsg_size(void)
@@ -110,7 +109,6 @@
 	struct net_device *dev;
 	int idx;
 
-	read_lock(&dev_base_lock);
 	for (dev = dev_base, idx = 0; dev; dev = dev->next) {
 		/* not a bridge port */
 		if (dev->br_port == NULL || idx < cb->args[0])
@@ -123,7 +121,6 @@
 skip:
 		++idx;
 	}
-	read_unlock(&dev_base_lock);
 
 	cb->args[0] = idx;
 
@@ -166,7 +163,7 @@
 		return -EINVAL;
 
 	/* if kernel STP is running, don't allow changes */
-	if (p->br->stp_enabled)
+	if (p->br->stp_enabled == BR_KERNEL_STP)
 		return -EBUSY;
 
 	if (!netif_running(dev) ||
@@ -179,18 +176,19 @@
 }
 
 
-static struct rtnetlink_link bridge_rtnetlink_table[RTM_NR_MSGTYPES] = {
-	[RTM_GETLINK - RTM_BASE] = { .dumpit	= br_dump_ifinfo, },
-	[RTM_SETLINK - RTM_BASE] = { .doit      = br_rtm_setlink, },
-};
-
-void __init br_netlink_init(void)
+int __init br_netlink_init(void)
 {
-	rtnetlink_links[PF_BRIDGE] = bridge_rtnetlink_table;
+	if (__rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, br_dump_ifinfo))
+		return -ENOBUFS;
+
+	/* Only the first call to __rtnl_register can fail */
+	__rtnl_register(PF_BRIDGE, RTM_SETLINK, br_rtm_setlink, NULL);
+
+	return 0;
 }
 
 void __exit br_netlink_fini(void)
 {
-	rtnetlink_links[PF_BRIDGE] = NULL;
+	rtnl_unregister_all(PF_BRIDGE);
 }
 
diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c
index 37357ed..c8451d3 100644
--- a/net/bridge/br_notify.c
+++ b/net/bridge/br_notify.c
@@ -50,7 +50,6 @@
 	case NETDEV_CHANGEADDR:
 		spin_lock_bh(&br->lock);
 		br_fdb_changeaddr(p, dev->dev_addr);
-		br_ifinfo_notify(RTM_NEWLINK, p);
 		br_stp_recalculate_bridge_id(br);
 		spin_unlock_bh(&br->lock);
 		break;
@@ -74,10 +73,11 @@
 		break;
 
 	case NETDEV_UP:
-		spin_lock_bh(&br->lock);
-		if (netif_carrier_ok(dev) && (br->dev->flags & IFF_UP))
+		if (netif_carrier_ok(dev) && (br->dev->flags & IFF_UP)) {
+			spin_lock_bh(&br->lock);
 			br_stp_enable_port(p);
-		spin_unlock_bh(&br->lock);
+			spin_unlock_bh(&br->lock);
+		}
 		break;
 
 	case NETDEV_UNREGISTER:
@@ -85,5 +85,10 @@
 		break;
 	}
 
+	/* Events that may cause spanning tree to refresh */
+	if (event == NETDEV_CHANGEADDR || event == NETDEV_UP ||
+	    event == NETDEV_CHANGE || event == NETDEV_DOWN)
+		br_ifinfo_notify(RTM_NEWLINK, p);
+
 	return NOTIFY_DONE;
 }
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index cc3f1c9..21bf3a9 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -26,7 +26,10 @@
 #define BR_PORT_BITS	10
 #define BR_MAX_PORTS	(1<<BR_PORT_BITS)
 
-#define BR_VERSION	"2.2"
+#define BR_VERSION	"2.3"
+
+/* Path to usermode spanning tree program */
+#define BR_STP_PROG	"/sbin/bridge-stp"
 
 typedef struct bridge_id bridge_id;
 typedef struct mac_addr mac_addr;
@@ -107,7 +110,13 @@
 
 	u8				group_addr[ETH_ALEN];
 	u16				root_port;
-	unsigned char			stp_enabled;
+
+	enum {
+		BR_NO_STP, 		/* no spanning tree */
+		BR_KERNEL_STP,		/* old STP in kernel */
+		BR_USER_STP,		/* new RSTP in userspace */
+	} stp_enabled;
+
 	unsigned char			topology_change;
 	unsigned char			topology_change_detected;
 
@@ -127,14 +136,14 @@
 	return !memcmp(&br->bridge_id, &br->designated_root, 8);
 }
 
-
 /* br_device.c */
 extern void br_dev_setup(struct net_device *dev);
 extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev);
 
 /* br_fdb.c */
-extern void br_fdb_init(void);
+extern int br_fdb_init(void);
 extern void br_fdb_fini(void);
+extern void br_fdb_flush(struct net_bridge *br);
 extern void br_fdb_changeaddr(struct net_bridge_port *p,
 			      const unsigned char *newaddr);
 extern void br_fdb_cleanup(unsigned long arg);
@@ -182,7 +191,8 @@
 
 /* br_input.c */
 extern int br_handle_frame_finish(struct sk_buff *skb);
-extern int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb);
+extern struct sk_buff *br_handle_frame(struct net_bridge_port *p,
+				       struct sk_buff *skb);
 
 /* br_ioctl.c */
 extern int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
@@ -207,6 +217,7 @@
 /* br_stp_if.c */
 extern void br_stp_enable_bridge(struct net_bridge *br);
 extern void br_stp_disable_bridge(struct net_bridge *br);
+extern void br_stp_set_enabled(struct net_bridge *br, unsigned long val);
 extern void br_stp_enable_port(struct net_bridge_port *p);
 extern void br_stp_disable_port(struct net_bridge_port *p);
 extern void br_stp_recalculate_bridge_id(struct net_bridge *br);
@@ -235,7 +246,7 @@
 
 
 /* br_netlink.c */
-extern void br_netlink_init(void);
+extern int br_netlink_init(void);
 extern void br_netlink_fini(void);
 extern void br_ifinfo_notify(int event, struct net_bridge_port *port);
 
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index f9ff4d5..ebb0861 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -370,11 +370,11 @@
 static void br_make_forwarding(struct net_bridge_port *p)
 {
 	if (p->state == BR_STATE_BLOCKING) {
-		if (p->br->stp_enabled) {
+		if (p->br->stp_enabled == BR_KERNEL_STP)
 			p->state = BR_STATE_LISTENING;
-		} else {
+		else
 			p->state = BR_STATE_LEARNING;
-		}
+
 		br_log_state(p);
 		mod_timer(&p->forward_delay_timer, jiffies + p->br->forward_delay);	}
 }
@@ -384,6 +384,10 @@
 {
 	struct net_bridge_port *p;
 
+	/* Don't change port states if userspace is handling STP */
+	if (br->stp_enabled == BR_USER_STP)
+		return;
+
 	list_for_each_entry(p, &br->port_list, list) {
 		if (p->state != BR_STATE_DISABLED) {
 			if (p->port_no == br->root_port) {
diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c
index b9fb0dc..60112bc 100644
--- a/net/bridge/br_stp_bpdu.c
+++ b/net/bridge/br_stp_bpdu.c
@@ -33,9 +33,6 @@
 {
 	struct sk_buff *skb;
 
-	if (!p->br->stp_enabled)
-		return;
-
 	skb = dev_alloc_skb(length+LLC_RESERVE);
 	if (!skb)
 		return;
@@ -75,6 +72,9 @@
 {
 	unsigned char buf[35];
 
+	if (p->br->stp_enabled != BR_KERNEL_STP)
+		return;
+
 	buf[0] = 0;
 	buf[1] = 0;
 	buf[2] = 0;
@@ -117,6 +117,9 @@
 {
 	unsigned char buf[4];
 
+	if (p->br->stp_enabled != BR_KERNEL_STP)
+		return;
+
 	buf[0] = 0;
 	buf[1] = 0;
 	buf[2] = 0;
@@ -157,9 +160,13 @@
 	br = p->br;
 	spin_lock(&br->lock);
 
-	if (p->state == BR_STATE_DISABLED
-	    || !br->stp_enabled
-	    || !(br->dev->flags & IFF_UP))
+	if (br->stp_enabled != BR_KERNEL_STP)
+		goto out;
+
+	if (!(br->dev->flags & IFF_UP))
+		goto out;
+
+	if (p->state == BR_STATE_DISABLED)
 		goto out;
 
 	if (compare_ether_addr(dest, br->group_addr) != 0)
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
index a285897..3e246b3 100644
--- a/net/bridge/br_stp_if.c
+++ b/net/bridge/br_stp_if.c
@@ -87,7 +87,6 @@
 void br_stp_enable_port(struct net_bridge_port *p)
 {
 	br_init_port(p);
-	br_ifinfo_notify(RTM_NEWLINK, p);
 	br_port_state_selection(p->br);
 }
 
@@ -101,8 +100,6 @@
 	printk(KERN_INFO "%s: port %i(%s) entering %s state\n",
 	       br->dev->name, p->port_no, p->dev->name, "disabled");
 
-	br_ifinfo_notify(RTM_DELLINK, p);
-
 	wasroot = br_is_root_bridge(br);
 	br_become_designated_port(p);
 	p->state = BR_STATE_DISABLED;
@@ -123,6 +120,62 @@
 		br_become_root_bridge(br);
 }
 
+static void br_stp_start(struct net_bridge *br)
+{
+	int r;
+	char *argv[] = { BR_STP_PROG, br->dev->name, "start", NULL };
+	char *envp[] = { NULL };
+
+	r = call_usermodehelper(BR_STP_PROG, argv, envp, 1);
+	if (r == 0) {
+		br->stp_enabled = BR_USER_STP;
+		printk(KERN_INFO "%s: userspace STP started\n", br->dev->name);
+	} else {
+		br->stp_enabled = BR_KERNEL_STP;
+		printk(KERN_INFO "%s: starting userspace STP failed, "
+				"staring kernel STP\n", br->dev->name);
+
+		/* To start timers on any ports left in blocking */
+		spin_lock_bh(&br->lock);
+		br_port_state_selection(br);
+		spin_unlock_bh(&br->lock);
+	}
+}
+
+static void br_stp_stop(struct net_bridge *br)
+{
+	int r;
+	char *argv[] = { BR_STP_PROG, br->dev->name, "stop", NULL };
+	char *envp[] = { NULL };
+
+	if (br->stp_enabled == BR_USER_STP) {
+		r = call_usermodehelper(BR_STP_PROG, argv, envp, 1);
+		printk(KERN_INFO "%s: userspace STP stopped, return code %d\n",
+			br->dev->name, r);
+
+
+		/* To start timers on any ports left in blocking */
+		spin_lock_bh(&br->lock);
+		br_port_state_selection(br);
+		spin_unlock_bh(&br->lock);
+	}
+
+	br->stp_enabled = BR_NO_STP;
+}
+
+void br_stp_set_enabled(struct net_bridge *br, unsigned long val)
+{
+	ASSERT_RTNL();
+
+	if (val) {
+		if (br->stp_enabled == BR_NO_STP)
+			br_stp_start(br);
+	} else {
+		if (br->stp_enabled != BR_NO_STP)
+			br_stp_stop(br);
+	}
+}
+
 /* called under bridge lock */
 void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *addr)
 {
diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
index 01a22ad..33c6c4a 100644
--- a/net/bridge/br_sysfs_br.c
+++ b/net/bridge/br_sysfs_br.c
@@ -149,7 +149,11 @@
 
 static void set_stp_state(struct net_bridge *br, unsigned long val)
 {
-	br->stp_enabled = val;
+	rtnl_lock();
+	spin_unlock_bh(&br->lock);
+	br_stp_set_enabled(br, val);
+	spin_lock_bh(&br->lock);
+	rtnl_unlock();
 }
 
 static ssize_t store_stp_state(struct device *d,
@@ -309,6 +313,19 @@
 static DEVICE_ATTR(group_addr, S_IRUGO | S_IWUSR,
 		   show_group_addr, store_group_addr);
 
+static ssize_t store_flush(struct device *d,
+			   struct device_attribute *attr,
+			   const char *buf, size_t len)
+{
+	struct net_bridge *br = to_bridge(d);
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	br_fdb_flush(br);
+	return len;
+}
+static DEVICE_ATTR(flush, S_IWUSR, NULL, store_flush);
 
 static struct attribute *bridge_attrs[] = {
 	&dev_attr_forward_delay.attr,
@@ -328,6 +345,7 @@
 	&dev_attr_topology_change_timer.attr,
 	&dev_attr_gc_timer.attr,
 	&dev_attr_group_addr.attr,
+	&dev_attr_flush.attr,
 	NULL
 };
 
diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
index 0bc2aef..2da2292 100644
--- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c
@@ -137,6 +137,13 @@
 }
 static BRPORT_ATTR(hold_timer, S_IRUGO, show_hold_timer, NULL);
 
+static ssize_t store_flush(struct net_bridge_port *p, unsigned long v)
+{
+	br_fdb_delete_by_port(p->br, p, 0); // Don't delete local entry
+	return 0;
+}
+static BRPORT_ATTR(flush, S_IWUSR, NULL, store_flush);
+
 static struct brport_attribute *brport_attrs[] = {
 	&brport_attr_path_cost,
 	&brport_attr_priority,
@@ -152,6 +159,7 @@
 	&brport_attr_message_age_timer,
 	&brport_attr_forward_delay_timer,
 	&brport_attr_hold_timer,
+	&brport_attr_flush,
 	NULL
 };
 
diff --git a/net/bridge/netfilter/ebt_arp.c b/net/bridge/netfilter/ebt_arp.c
index 9c59980..1a46952 100644
--- a/net/bridge/netfilter/ebt_arp.c
+++ b/net/bridge/netfilter/ebt_arp.c
@@ -35,40 +35,36 @@
 		return EBT_NOMATCH;
 
 	if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP)) {
-		__be32 _addr, *ap;
+		__be32 saddr, daddr, *sap, *dap;
 
-		/* IPv4 addresses are always 4 bytes */
-		if (ah->ar_pln != sizeof(__be32))
+		if (ah->ar_pln != sizeof(__be32) || ah->ar_pro != htons(ETH_P_IP))
 			return EBT_NOMATCH;
-		if (info->bitmask & EBT_ARP_SRC_IP) {
-			ap = skb_header_pointer(skb, sizeof(struct arphdr) +
-						ah->ar_hln, sizeof(_addr),
-						&_addr);
-			if (ap == NULL)
-				return EBT_NOMATCH;
-			if (FWINV(info->saddr != (*ap & info->smsk),
-			   EBT_ARP_SRC_IP))
-				return EBT_NOMATCH;
-		}
-
-		if (info->bitmask & EBT_ARP_DST_IP) {
-			ap = skb_header_pointer(skb, sizeof(struct arphdr) +
-						2*ah->ar_hln+sizeof(__be32),
-						sizeof(_addr), &_addr);
-			if (ap == NULL)
-				return EBT_NOMATCH;
-			if (FWINV(info->daddr != (*ap & info->dmsk),
-			   EBT_ARP_DST_IP))
-				return EBT_NOMATCH;
-		}
+		sap = skb_header_pointer(skb, sizeof(struct arphdr) +
+					ah->ar_hln, sizeof(saddr),
+					&saddr);
+		if (sap == NULL)
+			return EBT_NOMATCH;
+		dap = skb_header_pointer(skb, sizeof(struct arphdr) +
+					2*ah->ar_hln+sizeof(saddr),
+					sizeof(daddr), &daddr);
+		if (dap == NULL)
+			return EBT_NOMATCH;
+		if (info->bitmask & EBT_ARP_SRC_IP &&
+		    FWINV(info->saddr != (*sap & info->smsk), EBT_ARP_SRC_IP))
+			return EBT_NOMATCH;
+		if (info->bitmask & EBT_ARP_DST_IP &&
+		    FWINV(info->daddr != (*dap & info->dmsk), EBT_ARP_DST_IP))
+			return EBT_NOMATCH;
+		if (info->bitmask & EBT_ARP_GRAT &&
+		    FWINV(*dap != *sap, EBT_ARP_GRAT))
+			return EBT_NOMATCH;
 	}
 
 	if (info->bitmask & (EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC)) {
 		unsigned char _mac[ETH_ALEN], *mp;
 		uint8_t verdict, i;
 
-		/* MAC addresses are 6 bytes */
-		if (ah->ar_hln != ETH_ALEN)
+		if (ah->ar_hln != ETH_ALEN || ah->ar_hrd != htons(ARPHRD_ETHER))
 			return EBT_NOMATCH;
 		if (info->bitmask & EBT_ARP_SRC_MAC) {
 			mp = skb_header_pointer(skb, sizeof(struct arphdr),
diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c
index 45712ae..031bfa4 100644
--- a/net/bridge/netfilter/ebt_log.c
+++ b/net/bridge/netfilter/ebt_log.c
@@ -196,14 +196,10 @@
 	ret = ebt_register_watcher(&log);
 	if (ret < 0)
 		return ret;
-	if (nf_log_register(PF_BRIDGE, &ebt_log_logger) < 0) {
-		printk(KERN_WARNING "ebt_log: not logging via system console "
-		       "since somebody else already registered for PF_INET\n");
-		/* we cannot make module load fail here, since otherwise
-		 * ebtables userspace would abort */
-	}
-
-	return 0;
+	ret = nf_log_register(PF_BRIDGE, &ebt_log_logger);
+	if (ret < 0 && ret != -EEXIST)
+		ebt_unregister_watcher(&log);
+	return ret;
 }
 
 static void __exit ebt_log_fini(void)
diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c
index 8e15cc4..9411db62 100644
--- a/net/bridge/netfilter/ebt_ulog.c
+++ b/net/bridge/netfilter/ebt_ulog.c
@@ -130,6 +130,7 @@
 	unsigned int group = uloginfo->nlgroup;
 	ebt_ulog_buff_t *ub = &ulog_buffers[group];
 	spinlock_t *lock = &ub->lock;
+	ktime_t kt;
 
 	if ((uloginfo->cprange == 0) ||
 	    (uloginfo->cprange > skb->len + ETH_HLEN))
@@ -164,9 +165,10 @@
 
 	/* Fill in the ulog data */
 	pm->version = EBT_ULOG_VERSION;
-	do_gettimeofday(&pm->stamp);
+	kt = ktime_get_real();
+	pm->stamp = ktime_to_timeval(kt);
 	if (ub->qlen == 1)
-		skb_set_timestamp(ub->skb, &pm->stamp);
+		ub->skb->tstamp = kt;
 	pm->data_len = copy_len;
 	pm->mark = skb->mark;
 	pm->hook = hooknr;
@@ -295,14 +297,12 @@
 
 	/* initialize ulog_buffers */
 	for (i = 0; i < EBT_ULOG_MAXNLGROUPS; i++) {
-		init_timer(&ulog_buffers[i].timer);
-		ulog_buffers[i].timer.function = ulog_timer;
-		ulog_buffers[i].timer.data = i;
+		setup_timer(&ulog_buffers[i].timer, ulog_timer, i);
 		spin_lock_init(&ulog_buffers[i].lock);
 	}
 
 	ebtulognl = netlink_kernel_create(NETLINK_NFLOG, EBT_ULOG_MAXNLGROUPS,
-					  NULL, THIS_MODULE);
+					  NULL, NULL, THIS_MODULE);
 	if (!ebtulognl)
 		ret = -ENOMEM;
 	else if ((ret = ebt_register_watcher(&ulog)))
diff --git a/net/compat.c b/net/compat.c
index 1f32866..9a0f5f2 100644
--- a/net/compat.c
+++ b/net/compat.c
@@ -34,11 +34,11 @@
 {
 	int tot_len = 0;
 
-	while(niov > 0) {
+	while (niov > 0) {
 		compat_uptr_t buf;
 		compat_size_t len;
 
-		if(get_user(len, &uiov32->iov_len) ||
+		if (get_user(len, &uiov32->iov_len) ||
 		   get_user(buf, &uiov32->iov_base)) {
 			tot_len = -EFAULT;
 			break;
@@ -78,12 +78,12 @@
 {
 	int tot_len;
 
-	if(kern_msg->msg_namelen) {
-		if(mode==VERIFY_READ) {
+	if (kern_msg->msg_namelen) {
+		if (mode==VERIFY_READ) {
 			int err = move_addr_to_kernel(kern_msg->msg_name,
 						      kern_msg->msg_namelen,
 						      kern_address);
-			if(err < 0)
+			if (err < 0)
 				return err;
 		}
 		kern_msg->msg_name = kern_address;
@@ -93,7 +93,7 @@
 	tot_len = iov_from_user_compat_to_kern(kern_iov,
 					  (struct compat_iovec __user *)kern_msg->msg_iov,
 					  kern_msg->msg_iovlen);
-	if(tot_len >= 0)
+	if (tot_len >= 0)
 		kern_msg->msg_iov = kern_iov;
 
 	return tot_len;
@@ -146,8 +146,8 @@
 	kcmlen = 0;
 	kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf;
 	ucmsg = CMSG_COMPAT_FIRSTHDR(kmsg);
-	while(ucmsg != NULL) {
-		if(get_user(ucmlen, &ucmsg->cmsg_len))
+	while (ucmsg != NULL) {
+		if (get_user(ucmlen, &ucmsg->cmsg_len))
 			return -EFAULT;
 
 		/* Catch bogons. */
@@ -160,7 +160,7 @@
 		kcmlen += tmp;
 		ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen);
 	}
-	if(kcmlen == 0)
+	if (kcmlen == 0)
 		return -EINVAL;
 
 	/* The kcmlen holds the 64-bit version of the control length.
@@ -176,7 +176,7 @@
 	/* Now copy them over neatly. */
 	memset(kcmsg, 0, kcmlen);
 	ucmsg = CMSG_COMPAT_FIRSTHDR(kmsg);
-	while(ucmsg != NULL) {
+	while (ucmsg != NULL) {
 		if (__get_user(ucmlen, &ucmsg->cmsg_len))
 			goto Efault;
 		if (!CMSG_COMPAT_OK(ucmlen, ucmsg, kmsg))
@@ -215,11 +215,12 @@
 int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *data)
 {
 	struct compat_timeval ctv;
+	struct compat_timespec cts;
 	struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control;
 	struct compat_cmsghdr cmhdr;
 	int cmlen;
 
-	if(cm == NULL || kmsg->msg_controllen < sizeof(*cm)) {
+	if (cm == NULL || kmsg->msg_controllen < sizeof(*cm)) {
 		kmsg->msg_flags |= MSG_CTRUNC;
 		return 0; /* XXX: return error? check spec. */
 	}
@@ -229,11 +230,18 @@
 		ctv.tv_sec = tv->tv_sec;
 		ctv.tv_usec = tv->tv_usec;
 		data = &ctv;
-		len = sizeof(struct compat_timeval);
+		len = sizeof(ctv);
+	}
+	if (level == SOL_SOCKET && type == SO_TIMESTAMPNS) {
+		struct timespec *ts = (struct timespec *)data;
+		cts.tv_sec = ts->tv_sec;
+		cts.tv_nsec = ts->tv_nsec;
+		data = &cts;
+		len = sizeof(cts);
 	}
 
 	cmlen = CMSG_COMPAT_LEN(len);
-	if(kmsg->msg_controllen < cmlen) {
+	if (kmsg->msg_controllen < cmlen) {
 		kmsg->msg_flags |= MSG_CTRUNC;
 		cmlen = kmsg->msg_controllen;
 	}
@@ -241,9 +249,9 @@
 	cmhdr.cmsg_type = type;
 	cmhdr.cmsg_len = cmlen;
 
-	if(copy_to_user(cm, &cmhdr, sizeof cmhdr))
+	if (copy_to_user(cm, &cmhdr, sizeof cmhdr))
 		return -EFAULT;
-	if(copy_to_user(CMSG_COMPAT_DATA(cm), data, cmlen - sizeof(struct compat_cmsghdr)))
+	if (copy_to_user(CMSG_COMPAT_DATA(cm), data, cmlen - sizeof(struct compat_cmsghdr)))
 		return -EFAULT;
 	cmlen = CMSG_COMPAT_SPACE(len);
 	kmsg->msg_control += cmlen;
@@ -545,20 +553,49 @@
 	struct compat_timeval __user *ctv =
 			(struct compat_timeval __user*) userstamp;
 	int err = -ENOENT;
+	struct timeval tv;
 
 	if (!sock_flag(sk, SOCK_TIMESTAMP))
 		sock_enable_timestamp(sk);
-	if (sk->sk_stamp.tv_sec == -1)
+	tv = ktime_to_timeval(sk->sk_stamp);
+	if (tv.tv_sec == -1)
 		return err;
-	if (sk->sk_stamp.tv_sec == 0)
-		do_gettimeofday(&sk->sk_stamp);
-	if (put_user(sk->sk_stamp.tv_sec, &ctv->tv_sec) ||
-			put_user(sk->sk_stamp.tv_usec, &ctv->tv_usec))
+	if (tv.tv_sec == 0) {
+		sk->sk_stamp = ktime_get_real();
+		tv = ktime_to_timeval(sk->sk_stamp);
+	}
+	err = 0;
+	if (put_user(tv.tv_sec, &ctv->tv_sec) ||
+			put_user(tv.tv_usec, &ctv->tv_usec))
 		err = -EFAULT;
 	return err;
 }
 EXPORT_SYMBOL(compat_sock_get_timestamp);
 
+int compat_sock_get_timestampns(struct sock *sk, struct timespec __user *userstamp)
+{
+	struct compat_timespec __user *ctv =
+			(struct compat_timespec __user*) userstamp;
+	int err = -ENOENT;
+	struct timespec ts;
+
+	if (!sock_flag(sk, SOCK_TIMESTAMP))
+		sock_enable_timestamp(sk);
+	ts = ktime_to_timespec(sk->sk_stamp);
+	if (ts.tv_sec == -1)
+		return err;
+	if (ts.tv_sec == 0) {
+		sk->sk_stamp = ktime_get_real();
+		ts = ktime_to_timespec(sk->sk_stamp);
+	}
+	err = 0;
+	if (put_user(ts.tv_sec, &ctv->tv_sec) ||
+			put_user(ts.tv_nsec, &ctv->tv_nsec))
+		err = -EFAULT;
+	return err;
+}
+EXPORT_SYMBOL(compat_sock_get_timestampns);
+
 asmlinkage long compat_sys_getsockopt(int fd, int level, int optname,
 				char __user *optval, int __user *optlen)
 {
@@ -617,7 +654,7 @@
 	a0 = a[0];
 	a1 = a[1];
 
-	switch(call) {
+	switch (call) {
 	case SYS_SOCKET:
 		ret = sys_socket(a0, a1, a[2]);
 		break;
diff --git a/net/core/Makefile b/net/core/Makefile
index 73272d5..4751613 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -13,7 +13,6 @@
 obj-$(CONFIG_XFRM) += flow.o
 obj-$(CONFIG_SYSFS) += net-sysfs.o
 obj-$(CONFIG_NET_PKTGEN) += pktgen.o
-obj-$(CONFIG_WIRELESS_EXT) += wireless.o
 obj-$(CONFIG_NETPOLL) += netpoll.o
 obj-$(CONFIG_NET_DMA) += user_dma.o
 obj-$(CONFIG_FIB_RULES) += fib_rules.o
diff --git a/net/core/datagram.c b/net/core/datagram.c
index 186212b..cb056f4 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -411,11 +411,11 @@
 	return -EFAULT;
 }
 
-__sum16 __skb_checksum_complete(struct sk_buff *skb)
+__sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len)
 {
 	__sum16 sum;
 
-	sum = csum_fold(skb_checksum(skb, 0, skb->len, skb->csum));
+	sum = csum_fold(skb_checksum(skb, 0, len, skb->csum));
 	if (likely(!sum)) {
 		if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE))
 			netdev_rx_csum_fault(skb->dev);
@@ -423,6 +423,12 @@
 	}
 	return sum;
 }
+EXPORT_SYMBOL(__skb_checksum_complete_head);
+
+__sum16 __skb_checksum_complete(struct sk_buff *skb)
+{
+	return __skb_checksum_complete_head(skb, skb->len);
+}
 EXPORT_SYMBOL(__skb_checksum_complete);
 
 /**
diff --git a/net/core/dev.c b/net/core/dev.c
index 4dc93cc..eb99900 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -109,7 +109,7 @@
 #include <linux/netpoll.h>
 #include <linux/rcupdate.h>
 #include <linux/delay.h>
-#include <linux/wireless.h>
+#include <net/wext.h>
 #include <net/iw_handler.h>
 #include <asm/current.h>
 #include <linux/audit.h>
@@ -146,8 +146,8 @@
  */
 
 static DEFINE_SPINLOCK(ptype_lock);
-static struct list_head ptype_base[16];	/* 16 way hashed list */
-static struct list_head ptype_all;		/* Taps */
+static struct list_head ptype_base[16] __read_mostly;	/* 16 way hashed list */
+static struct list_head ptype_all __read_mostly;	/* Taps */
 
 #ifdef CONFIG_NET_DMA
 static struct dma_client *net_dma_client;
@@ -226,12 +226,6 @@
 *******************************************************************************/
 
 /*
- *	For efficiency
- */
-
-static int netdev_nit;
-
-/*
  *	Add a protocol ID to the list. Now that the input handler is
  *	smarter we can dispense with all the messy stuff that used to be
  *	here.
@@ -265,10 +259,9 @@
 	int hash;
 
 	spin_lock_bh(&ptype_lock);
-	if (pt->type == htons(ETH_P_ALL)) {
-		netdev_nit++;
+	if (pt->type == htons(ETH_P_ALL))
 		list_add_rcu(&pt->list, &ptype_all);
-	} else {
+	else {
 		hash = ntohs(pt->type) & 15;
 		list_add_rcu(&pt->list, &ptype_base[hash]);
 	}
@@ -295,10 +288,9 @@
 
 	spin_lock_bh(&ptype_lock);
 
-	if (pt->type == htons(ETH_P_ALL)) {
-		netdev_nit--;
+	if (pt->type == htons(ETH_P_ALL))
 		head = &ptype_all;
-	} else
+	else
 		head = &ptype_base[ntohs(pt->type) & 15];
 
 	list_for_each_entry(pt1, head, list) {
@@ -817,7 +809,6 @@
 	return 1;
 }
 
-
 /**
  *	dev_open	- prepare an interface for use.
  *	@dev:	device to open
@@ -1031,23 +1022,12 @@
 	atomic_dec(&netstamp_needed);
 }
 
-void __net_timestamp(struct sk_buff *skb)
-{
-	struct timeval tv;
-
-	do_gettimeofday(&tv);
-	skb_set_timestamp(skb, &tv);
-}
-EXPORT_SYMBOL(__net_timestamp);
-
 static inline void net_timestamp(struct sk_buff *skb)
 {
 	if (atomic_read(&netstamp_needed))
 		__net_timestamp(skb);
-	else {
-		skb->tstamp.off_sec = 0;
-		skb->tstamp.off_usec = 0;
-	}
+	else
+		skb->tstamp.tv64 = 0;
 }
 
 /*
@@ -1077,18 +1057,18 @@
 			   set by sender, so that the second statement is
 			   just protection against buggy protocols.
 			 */
-			skb2->mac.raw = skb2->data;
+			skb_reset_mac_header(skb2);
 
-			if (skb2->nh.raw < skb2->data ||
-			    skb2->nh.raw > skb2->tail) {
+			if (skb_network_header(skb2) < skb2->data ||
+			    skb2->network_header > skb2->tail) {
 				if (net_ratelimit())
 					printk(KERN_CRIT "protocol %04x is "
 					       "buggy, dev %s\n",
 					       skb2->protocol, dev->name);
-				skb2->nh.raw = skb2->data;
+				skb_reset_network_header(skb2);
 			}
 
-			skb2->h.raw = skb2->nh.raw;
+			skb2->transport_header = skb2->network_header;
 			skb2->pkt_type = PACKET_OUTGOING;
 			ptype->func(skb2, skb->dev, ptype, skb->dev);
 		}
@@ -1167,7 +1147,7 @@
 int skb_checksum_help(struct sk_buff *skb)
 {
 	__wsum csum;
-	int ret = 0, offset = skb->h.raw - skb->data;
+	int ret = 0, offset;
 
 	if (skb->ip_summed == CHECKSUM_COMPLETE)
 		goto out_set_summed;
@@ -1183,15 +1163,16 @@
 			goto out;
 	}
 
+	offset = skb->csum_start - skb_headroom(skb);
 	BUG_ON(offset > (int)skb->len);
 	csum = skb_checksum(skb, offset, skb->len-offset, 0);
 
-	offset = skb->tail - skb->h.raw;
+	offset = skb_headlen(skb) - offset;
 	BUG_ON(offset <= 0);
 	BUG_ON(skb->csum_offset + 2 > offset);
 
-	*(__sum16*)(skb->h.raw + skb->csum_offset) = csum_fold(csum);
-
+	*(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) =
+		csum_fold(csum);
 out_set_summed:
 	skb->ip_summed = CHECKSUM_NONE;
 out:
@@ -1217,11 +1198,11 @@
 
 	BUG_ON(skb_shinfo(skb)->frag_list);
 
-	skb->mac.raw = skb->data;
-	skb->mac_len = skb->nh.raw - skb->data;
+	skb_reset_mac_header(skb);
+	skb->mac_len = skb->network_header - skb->mac_header;
 	__skb_pull(skb, skb->mac_len);
 
-	if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
+	if (WARN_ON(skb->ip_summed != CHECKSUM_PARTIAL)) {
 		if (skb_header_cloned(skb) &&
 		    (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
 			return ERR_PTR(err);
@@ -1235,7 +1216,8 @@
 				segs = ERR_PTR(err);
 				if (err || skb_gso_ok(skb, features))
 					break;
-				__skb_push(skb, skb->data - skb->nh.raw);
+				__skb_push(skb, (skb->data -
+						 skb_network_header(skb)));
 			}
 			segs = ptype->gso_segment(skb, features);
 			break;
@@ -1243,7 +1225,7 @@
 	}
 	rcu_read_unlock();
 
-	__skb_push(skb, skb->data - skb->mac.raw);
+	__skb_push(skb, skb->data - skb_mac_header(skb));
 
 	return segs;
 }
@@ -1340,7 +1322,7 @@
 int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	if (likely(!skb->next)) {
-		if (netdev_nit)
+		if (!list_empty(&ptype_all))
 			dev_queue_xmit_nit(skb, dev);
 
 		if (netif_needs_gso(dev, skb)) {
@@ -1442,12 +1424,16 @@
 	/* If packet is not checksummed and device does not support
 	 * checksumming for this protocol, complete checksumming here.
 	 */
-	if (skb->ip_summed == CHECKSUM_PARTIAL &&
-	    (!(dev->features & NETIF_F_GEN_CSUM) &&
-	     (!(dev->features & NETIF_F_IP_CSUM) ||
-	      skb->protocol != htons(ETH_P_IP))))
-		if (skb_checksum_help(skb))
-			goto out_kfree_skb;
+	if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		skb_set_transport_header(skb, skb->csum_start -
+					      skb_headroom(skb));
+
+		if (!(dev->features & NETIF_F_GEN_CSUM) &&
+		    (!(dev->features & NETIF_F_IP_CSUM) ||
+		     skb->protocol != htons(ETH_P_IP)))
+			if (skb_checksum_help(skb))
+				goto out_kfree_skb;
+	}
 
 gso:
 	spin_lock_prefetch(&dev->queue_lock);
@@ -1543,9 +1529,9 @@
 			Receiver routines
   =======================================================================*/
 
-int netdev_max_backlog = 1000;
-int netdev_budget = 300;
-int weight_p = 64;            /* old backlog weight */
+int netdev_max_backlog __read_mostly = 1000;
+int netdev_budget __read_mostly = 300;
+int weight_p __read_mostly = 64;            /* old backlog weight */
 
 DEFINE_PER_CPU(struct netif_rx_stats, netdev_rx_stat) = { 0, };
 
@@ -1577,7 +1563,7 @@
 	if (netpoll_rx(skb))
 		return NET_RX_DROP;
 
-	if (!skb->tstamp.off_sec)
+	if (!skb->tstamp.tv64)
 		net_timestamp(skb);
 
 	/*
@@ -1684,40 +1670,46 @@
 	}
 }
 
-static __inline__ int deliver_skb(struct sk_buff *skb,
-				  struct packet_type *pt_prev,
-				  struct net_device *orig_dev)
+static inline int deliver_skb(struct sk_buff *skb,
+			      struct packet_type *pt_prev,
+			      struct net_device *orig_dev)
 {
 	atomic_inc(&skb->users);
 	return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
 }
 
 #if defined(CONFIG_BRIDGE) || defined (CONFIG_BRIDGE_MODULE)
-int (*br_handle_frame_hook)(struct net_bridge_port *p, struct sk_buff **pskb);
+/* These hooks defined here for ATM */
 struct net_bridge;
 struct net_bridge_fdb_entry *(*br_fdb_get_hook)(struct net_bridge *br,
 						unsigned char *addr);
-void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent);
+void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent) __read_mostly;
 
-static __inline__ int handle_bridge(struct sk_buff **pskb,
-				    struct packet_type **pt_prev, int *ret,
-				    struct net_device *orig_dev)
+/*
+ * If bridge module is loaded call bridging hook.
+ *  returns NULL if packet was consumed.
+ */
+struct sk_buff *(*br_handle_frame_hook)(struct net_bridge_port *p,
+					struct sk_buff *skb) __read_mostly;
+static inline struct sk_buff *handle_bridge(struct sk_buff *skb,
+					    struct packet_type **pt_prev, int *ret,
+					    struct net_device *orig_dev)
 {
 	struct net_bridge_port *port;
 
-	if ((*pskb)->pkt_type == PACKET_LOOPBACK ||
-	    (port = rcu_dereference((*pskb)->dev->br_port)) == NULL)
-		return 0;
+	if (skb->pkt_type == PACKET_LOOPBACK ||
+	    (port = rcu_dereference(skb->dev->br_port)) == NULL)
+		return skb;
 
 	if (*pt_prev) {
-		*ret = deliver_skb(*pskb, *pt_prev, orig_dev);
+		*ret = deliver_skb(skb, *pt_prev, orig_dev);
 		*pt_prev = NULL;
 	}
 
-	return br_handle_frame_hook(port, pskb);
+	return br_handle_frame_hook(port, skb);
 }
 #else
-#define handle_bridge(skb, pt_prev, ret, orig_dev)	(0)
+#define handle_bridge(skb, pt_prev, ret, orig_dev)	(skb)
 #endif
 
 #ifdef CONFIG_NET_CLS_ACT
@@ -1747,10 +1739,10 @@
 
 		skb->tc_verd = SET_TC_AT(skb->tc_verd,AT_INGRESS);
 
-		spin_lock(&dev->queue_lock);
+		spin_lock(&dev->ingress_lock);
 		if ((q = dev->qdisc_ingress) != NULL)
 			result = q->enqueue(skb, q);
-		spin_unlock(&dev->queue_lock);
+		spin_unlock(&dev->ingress_lock);
 
 	}
 
@@ -1769,7 +1761,7 @@
 	if (skb->dev->poll && netpoll_rx(skb))
 		return NET_RX_DROP;
 
-	if (!skb->tstamp.off_sec)
+	if (!skb->tstamp.tv64)
 		net_timestamp(skb);
 
 	if (!skb->iif)
@@ -1782,8 +1774,9 @@
 
 	__get_cpu_var(netdev_rx_stat).total++;
 
-	skb->h.raw = skb->nh.raw = skb->data;
-	skb->mac_len = skb->nh.raw - skb->mac.raw;
+	skb_reset_network_header(skb);
+	skb_reset_transport_header(skb);
+	skb->mac_len = skb->network_header - skb->mac_header;
 
 	pt_prev = NULL;
 
@@ -1823,7 +1816,8 @@
 ncls:
 #endif
 
-	if (handle_bridge(&skb, &pt_prev, &ret, orig_dev))
+	skb = handle_bridge(skb, &pt_prev, &ret, orig_dev);
+	if (!skb)
 		goto out;
 
 	type = skb->protocol;
@@ -2076,7 +2070,7 @@
  *	This is invoked by the /proc filesystem handler to display a device
  *	in detail.
  */
-static __inline__ struct net_device *dev_get_idx(loff_t pos)
+static struct net_device *dev_get_idx(loff_t pos)
 {
 	struct net_device *dev;
 	loff_t i;
@@ -2105,28 +2099,25 @@
 
 static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
 {
-	if (dev->get_stats) {
-		struct net_device_stats *stats = dev->get_stats(dev);
+	struct net_device_stats *stats = dev->get_stats(dev);
 
-		seq_printf(seq, "%6s:%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu "
-				"%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
-			   dev->name, stats->rx_bytes, stats->rx_packets,
-			   stats->rx_errors,
-			   stats->rx_dropped + stats->rx_missed_errors,
-			   stats->rx_fifo_errors,
-			   stats->rx_length_errors + stats->rx_over_errors +
-			     stats->rx_crc_errors + stats->rx_frame_errors,
-			   stats->rx_compressed, stats->multicast,
-			   stats->tx_bytes, stats->tx_packets,
-			   stats->tx_errors, stats->tx_dropped,
-			   stats->tx_fifo_errors, stats->collisions,
-			   stats->tx_carrier_errors +
-			     stats->tx_aborted_errors +
-			     stats->tx_window_errors +
-			     stats->tx_heartbeat_errors,
-			   stats->tx_compressed);
-	} else
-		seq_printf(seq, "%6s: No statistics available.\n", dev->name);
+	seq_printf(seq, "%6s:%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu "
+		   "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
+		   dev->name, stats->rx_bytes, stats->rx_packets,
+		   stats->rx_errors,
+		   stats->rx_dropped + stats->rx_missed_errors,
+		   stats->rx_fifo_errors,
+		   stats->rx_length_errors + stats->rx_over_errors +
+		    stats->rx_crc_errors + stats->rx_frame_errors,
+		   stats->rx_compressed, stats->multicast,
+		   stats->tx_bytes, stats->tx_packets,
+		   stats->tx_errors, stats->tx_dropped,
+		   stats->tx_fifo_errors, stats->collisions,
+		   stats->tx_carrier_errors +
+		    stats->tx_aborted_errors +
+		    stats->tx_window_errors +
+		    stats->tx_heartbeat_errors,
+		   stats->tx_compressed);
 }
 
 /*
@@ -2185,7 +2176,7 @@
 	return 0;
 }
 
-static struct seq_operations dev_seq_ops = {
+static const struct seq_operations dev_seq_ops = {
 	.start = dev_seq_start,
 	.next  = dev_seq_next,
 	.stop  = dev_seq_stop,
@@ -2205,7 +2196,7 @@
 	.release = seq_release,
 };
 
-static struct seq_operations softnet_seq_ops = {
+static const struct seq_operations softnet_seq_ops = {
 	.start = softnet_seq_start,
 	.next  = softnet_seq_next,
 	.stop  = softnet_seq_stop,
@@ -2225,12 +2216,135 @@
 	.release = seq_release,
 };
 
-#ifdef CONFIG_WIRELESS_EXT
-extern int wireless_proc_init(void);
-#else
-#define wireless_proc_init() 0
+static void *ptype_get_idx(loff_t pos)
+{
+	struct packet_type *pt = NULL;
+	loff_t i = 0;
+	int t;
+
+	list_for_each_entry_rcu(pt, &ptype_all, list) {
+		if (i == pos)
+			return pt;
+		++i;
+	}
+
+	for (t = 0; t < 16; t++) {
+		list_for_each_entry_rcu(pt, &ptype_base[t], list) {
+			if (i == pos)
+				return pt;
+			++i;
+		}
+	}
+	return NULL;
+}
+
+static void *ptype_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	rcu_read_lock();
+	return *pos ? ptype_get_idx(*pos - 1) : SEQ_START_TOKEN;
+}
+
+static void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct packet_type *pt;
+	struct list_head *nxt;
+	int hash;
+
+	++*pos;
+	if (v == SEQ_START_TOKEN)
+		return ptype_get_idx(0);
+
+	pt = v;
+	nxt = pt->list.next;
+	if (pt->type == htons(ETH_P_ALL)) {
+		if (nxt != &ptype_all)
+			goto found;
+		hash = 0;
+		nxt = ptype_base[0].next;
+	} else
+		hash = ntohs(pt->type) & 15;
+
+	while (nxt == &ptype_base[hash]) {
+		if (++hash >= 16)
+			return NULL;
+		nxt = ptype_base[hash].next;
+	}
+found:
+	return list_entry(nxt, struct packet_type, list);
+}
+
+static void ptype_seq_stop(struct seq_file *seq, void *v)
+{
+	rcu_read_unlock();
+}
+
+static void ptype_seq_decode(struct seq_file *seq, void *sym)
+{
+#ifdef CONFIG_KALLSYMS
+	unsigned long offset = 0, symsize;
+	const char *symname;
+	char *modname;
+	char namebuf[128];
+
+	symname = kallsyms_lookup((unsigned long)sym, &symsize, &offset,
+				  &modname, namebuf);
+
+	if (symname) {
+		char *delim = ":";
+
+		if (!modname)
+			modname = delim = "";
+		seq_printf(seq, "%s%s%s%s+0x%lx", delim, modname, delim,
+			   symname, offset);
+		return;
+	}
 #endif
 
+	seq_printf(seq, "[%p]", sym);
+}
+
+static int ptype_seq_show(struct seq_file *seq, void *v)
+{
+	struct packet_type *pt = v;
+
+	if (v == SEQ_START_TOKEN)
+		seq_puts(seq, "Type Device      Function\n");
+	else {
+		if (pt->type == htons(ETH_P_ALL))
+			seq_puts(seq, "ALL ");
+		else
+			seq_printf(seq, "%04x", ntohs(pt->type));
+
+		seq_printf(seq, " %-8s ",
+			   pt->dev ? pt->dev->name : "");
+		ptype_seq_decode(seq,  pt->func);
+		seq_putc(seq, '\n');
+	}
+
+	return 0;
+}
+
+static const struct seq_operations ptype_seq_ops = {
+	.start = ptype_seq_start,
+	.next  = ptype_seq_next,
+	.stop  = ptype_seq_stop,
+	.show  = ptype_seq_show,
+};
+
+static int ptype_seq_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &ptype_seq_ops);
+}
+
+static const struct file_operations ptype_seq_fops = {
+	.owner	 = THIS_MODULE,
+	.open    = ptype_seq_open,
+	.read    = seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
+
+
 static int __init dev_proc_init(void)
 {
 	int rc = -ENOMEM;
@@ -2239,13 +2353,18 @@
 		goto out;
 	if (!proc_net_fops_create("softnet_stat", S_IRUGO, &softnet_seq_fops))
 		goto out_dev;
-	if (wireless_proc_init())
+	if (!proc_net_fops_create("ptype", S_IRUGO, &ptype_seq_fops))
+		goto out_dev2;
+
+	if (wext_proc_init())
 		goto out_softnet;
 	rc = 0;
 out:
 	return rc;
 out_softnet:
 	proc_net_remove("softnet_stat");
+out_dev2:
+	proc_net_remove("ptype");
 out_dev:
 	proc_net_remove("dev");
 	goto out;
@@ -2795,29 +2914,9 @@
 					ret = -EFAULT;
 				return ret;
 			}
-#ifdef CONFIG_WIRELESS_EXT
 			/* Take care of Wireless Extensions */
-			if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
-				/* If command is `set a parameter', or
-				 * `get the encoding parameters', check if
-				 * the user has the right to do it */
-				if (IW_IS_SET(cmd) || cmd == SIOCGIWENCODE
-				    || cmd == SIOCGIWENCODEEXT) {
-					if (!capable(CAP_NET_ADMIN))
-						return -EPERM;
-				}
-				dev_load(ifr.ifr_name);
-				rtnl_lock();
-				/* Follow me in net/core/wireless.c */
-				ret = wireless_process_ioctl(&ifr, cmd);
-				rtnl_unlock();
-				if (IW_IS_GET(cmd) &&
-				    copy_to_user(arg, &ifr,
-						 sizeof(struct ifreq)))
-					ret = -EFAULT;
-				return ret;
-			}
-#endif	/* CONFIG_WIRELESS_EXT */
+			if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST)
+				return wext_handle_ioctl(&ifr, cmd, arg);
 			return -EINVAL;
 	}
 }
@@ -2847,7 +2946,7 @@
 static DEFINE_SPINLOCK(net_todo_list_lock);
 static struct list_head net_todo_list = LIST_HEAD_INIT(net_todo_list);
 
-static inline void net_set_todo(struct net_device *dev)
+static void net_set_todo(struct net_device *dev)
 {
 	spin_lock(&net_todo_list_lock);
 	list_add_tail(&dev->todo_list, &net_todo_list);
@@ -2888,9 +2987,7 @@
 	spin_lock_init(&dev->queue_lock);
 	spin_lock_init(&dev->_xmit_lock);
 	dev->xmit_lock_owner = -1;
-#ifdef CONFIG_NET_CLS_ACT
 	spin_lock_init(&dev->ingress_lock);
-#endif
 
 	dev->iflink = -1;
 
@@ -3002,7 +3099,7 @@
  *	chain. 0 is returned on success. A negative errno code is returned
  *	on a failure to set up the device, or if the name is a duplicate.
  *
- *	This is a wrapper around register_netdev that takes the rtnl semaphore
+ *	This is a wrapper around register_netdevice that takes the rtnl semaphore
  *	and expands the device name if you passed a format string to
  *	alloc_netdev.
  */
@@ -3157,6 +3254,11 @@
 	mutex_unlock(&net_todo_run_mutex);
 }
 
+static struct net_device_stats *internal_stats(struct net_device *dev)
+{
+	return &dev->stats;
+}
+
 /**
  *	alloc_netdev - allocate network device
  *	@sizeof_priv:	size of private data to allocate space for
@@ -3192,6 +3294,7 @@
 	if (sizeof_priv)
 		dev->priv = netdev_priv(dev);
 
+	dev->get_stats = internal_stats;
 	setup(dev);
 	strcpy(dev->name, name);
 	return dev;
diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c
index 56b310c..7d57bf7 100644
--- a/net/core/dev_mcast.c
+++ b/net/core/dev_mcast.c
@@ -264,7 +264,7 @@
 	return 0;
 }
 
-static struct seq_operations dev_mc_seq_ops = {
+static const struct seq_operations dev_mc_seq_ops = {
 	.start = dev_mc_seq_start,
 	.next  = dev_mc_seq_next,
 	.stop  = dev_mc_seq_stop,
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 6168edd..8d5e5a0 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -836,7 +836,7 @@
 			return -EPERM;
 	}
 
-	if(dev->ethtool_ops->begin)
+	if (dev->ethtool_ops->begin)
 		if ((rc = dev->ethtool_ops->begin(dev)) < 0)
 			return rc;
 
@@ -952,7 +952,7 @@
 		rc =  -EOPNOTSUPP;
 	}
 
-	if(dev->ethtool_ops->complete)
+	if (dev->ethtool_ops->complete)
 		dev->ethtool_ops->complete(dev);
 
 	if (old_features != dev->features)
diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c
index 7174ced..8c5474e 100644
--- a/net/core/fib_rules.c
+++ b/net/core/fib_rules.c
@@ -44,6 +44,12 @@
 		module_put(ops->owner);
 }
 
+static void flush_route_cache(struct fib_rules_ops *ops)
+{
+	if (ops->flush_cache)
+		ops->flush_cache();
+}
+
 int fib_rules_register(struct fib_rules_ops *ops)
 {
 	int err = -EEXIST;
@@ -132,10 +138,25 @@
 	rcu_read_lock();
 
 	list_for_each_entry_rcu(rule, ops->rules_list, list) {
+jumped:
 		if (!fib_rule_match(rule, ops, fl, flags))
 			continue;
 
-		err = ops->action(rule, fl, flags, arg);
+		if (rule->action == FR_ACT_GOTO) {
+			struct fib_rule *target;
+
+			target = rcu_dereference(rule->ctarget);
+			if (target == NULL) {
+				continue;
+			} else {
+				rule = target;
+				goto jumped;
+			}
+		} else if (rule->action == FR_ACT_NOP)
+			continue;
+		else
+			err = ops->action(rule, fl, flags, arg);
+
 		if (err != -EAGAIN) {
 			fib_rule_get(rule);
 			arg->rule = rule;
@@ -174,13 +195,13 @@
 	return err;
 }
 
-int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 {
 	struct fib_rule_hdr *frh = nlmsg_data(nlh);
 	struct fib_rules_ops *ops = NULL;
 	struct fib_rule *rule, *r, *last = NULL;
 	struct nlattr *tb[FRA_MAX+1];
-	int err = -EINVAL;
+	int err = -EINVAL, unresolved = 0;
 
 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh)))
 		goto errout;
@@ -237,6 +258,28 @@
 	if (!rule->pref && ops->default_pref)
 		rule->pref = ops->default_pref();
 
+	err = -EINVAL;
+	if (tb[FRA_GOTO]) {
+		if (rule->action != FR_ACT_GOTO)
+			goto errout_free;
+
+		rule->target = nla_get_u32(tb[FRA_GOTO]);
+		/* Backward jumps are prohibited to avoid endless loops */
+		if (rule->target <= rule->pref)
+			goto errout_free;
+
+		list_for_each_entry(r, ops->rules_list, list) {
+			if (r->pref == rule->target) {
+				rule->ctarget = r;
+				break;
+			}
+		}
+
+		if (rule->ctarget == NULL)
+			unresolved = 1;
+	} else if (rule->action == FR_ACT_GOTO)
+		goto errout_free;
+
 	err = ops->configure(rule, skb, nlh, frh, tb);
 	if (err < 0)
 		goto errout_free;
@@ -249,12 +292,35 @@
 
 	fib_rule_get(rule);
 
+	if (ops->unresolved_rules) {
+		/*
+		 * There are unresolved goto rules in the list, check if
+		 * any of them are pointing to this new rule.
+		 */
+		list_for_each_entry(r, ops->rules_list, list) {
+			if (r->action == FR_ACT_GOTO &&
+			    r->target == rule->pref) {
+				BUG_ON(r->ctarget != NULL);
+				rcu_assign_pointer(r->ctarget, rule);
+				if (--ops->unresolved_rules == 0)
+					break;
+			}
+		}
+	}
+
+	if (rule->action == FR_ACT_GOTO)
+		ops->nr_goto_rules++;
+
+	if (unresolved)
+		ops->unresolved_rules++;
+
 	if (last)
 		list_add_rcu(&rule->list, &last->list);
 	else
 		list_add_rcu(&rule->list, ops->rules_list);
 
 	notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).pid);
+	flush_route_cache(ops);
 	rules_ops_put(ops);
 	return 0;
 
@@ -265,11 +331,11 @@
 	return err;
 }
 
-int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 {
 	struct fib_rule_hdr *frh = nlmsg_data(nlh);
 	struct fib_rules_ops *ops = NULL;
-	struct fib_rule *rule;
+	struct fib_rule *rule, *tmp;
 	struct nlattr *tb[FRA_MAX+1];
 	int err = -EINVAL;
 
@@ -322,10 +388,30 @@
 		}
 
 		list_del_rcu(&rule->list);
+
+		if (rule->action == FR_ACT_GOTO)
+			ops->nr_goto_rules--;
+
+		/*
+		 * Check if this rule is a target to any of them. If so,
+		 * disable them. As this operation is eventually very
+		 * expensive, it is only performed if goto rules have
+		 * actually been added.
+		 */
+		if (ops->nr_goto_rules > 0) {
+			list_for_each_entry(tmp, ops->rules_list, list) {
+				if (tmp->ctarget == rule) {
+					rcu_assign_pointer(tmp->ctarget, NULL);
+					ops->unresolved_rules++;
+				}
+			}
+		}
+
 		synchronize_rcu();
 		notify_rule_change(RTM_DELRULE, rule, ops, nlh,
 				   NETLINK_CB(skb).pid);
 		fib_rule_put(rule);
+		flush_route_cache(ops);
 		rules_ops_put(ops);
 		return 0;
 	}
@@ -371,9 +457,16 @@
 	frh->action = rule->action;
 	frh->flags = rule->flags;
 
-	if (rule->ifname[0])
+	if (rule->action == FR_ACT_GOTO && rule->ctarget == NULL)
+		frh->flags |= FIB_RULE_UNRESOLVED;
+
+	if (rule->ifname[0]) {
 		NLA_PUT_STRING(skb, FRA_IFNAME, rule->ifname);
 
+		if (rule->ifindex == -1)
+			frh->flags |= FIB_RULE_DEV_DETACHED;
+	}
+
 	if (rule->pref)
 		NLA_PUT_U32(skb, FRA_PRIORITY, rule->pref);
 
@@ -383,6 +476,9 @@
 	if (rule->mark_mask || rule->mark)
 		NLA_PUT_U32(skb, FRA_FWMASK, rule->mark_mask);
 
+	if (rule->target)
+		NLA_PUT_U32(skb, FRA_GOTO, rule->target);
+
 	if (ops->fill(rule, skb, nlh, frh) < 0)
 		goto nla_put_failure;
 
@@ -393,19 +489,14 @@
 	return -EMSGSIZE;
 }
 
-int fib_rules_dump(struct sk_buff *skb, struct netlink_callback *cb, int family)
+static int dump_rules(struct sk_buff *skb, struct netlink_callback *cb,
+		      struct fib_rules_ops *ops)
 {
 	int idx = 0;
 	struct fib_rule *rule;
-	struct fib_rules_ops *ops;
 
-	ops = lookup_rules_ops(family);
-	if (ops == NULL)
-		return -EAFNOSUPPORT;
-
-	rcu_read_lock();
-	list_for_each_entry_rcu(rule, ops->rules_list, list) {
-		if (idx < cb->args[0])
+	list_for_each_entry(rule, ops->rules_list, list) {
+		if (idx < cb->args[1])
 			goto skip;
 
 		if (fib_nl_fill_rule(skb, rule, NETLINK_CB(cb->skb).pid,
@@ -415,14 +506,44 @@
 skip:
 		idx++;
 	}
-	rcu_read_unlock();
-	cb->args[0] = idx;
+	cb->args[1] = idx;
 	rules_ops_put(ops);
 
 	return skb->len;
 }
 
-EXPORT_SYMBOL_GPL(fib_rules_dump);
+static int fib_nl_dumprule(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct fib_rules_ops *ops;
+	int idx = 0, family;
+
+	family = rtnl_msg_family(cb->nlh);
+	if (family != AF_UNSPEC) {
+		/* Protocol specific dump request */
+		ops = lookup_rules_ops(family);
+		if (ops == NULL)
+			return -EAFNOSUPPORT;
+
+		return dump_rules(skb, cb, ops);
+	}
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(ops, &rules_ops, list) {
+		if (idx < cb->args[0] || !try_module_get(ops->owner))
+			goto skip;
+
+		if (dump_rules(skb, cb, ops) < 0)
+			break;
+
+		cb->args[1] = 0;
+	skip:
+		idx++;
+	}
+	rcu_read_unlock();
+	cb->args[0] = idx;
+
+	return skb->len;
+}
 
 static void notify_rule_change(int event, struct fib_rule *rule,
 			       struct fib_rules_ops *ops, struct nlmsghdr *nlh,
@@ -501,6 +622,10 @@
 
 static int __init fib_rules_init(void)
 {
+	rtnl_register(PF_UNSPEC, RTM_NEWRULE, fib_nl_newrule, NULL);
+	rtnl_register(PF_UNSPEC, RTM_DELRULE, fib_nl_delrule, NULL);
+	rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, fib_nl_dumprule);
+
 	return register_netdevice_notifier(&fib_rules_notifier);
 }
 
diff --git a/net/core/filter.c b/net/core/filter.c
index 8d185a08..bd903aa 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -42,11 +42,11 @@
 	u8 *ptr = NULL;
 
 	if (k >= SKF_NET_OFF)
-		ptr = skb->nh.raw + k - SKF_NET_OFF;
+		ptr = skb_network_header(skb) + k - SKF_NET_OFF;
 	else if (k >= SKF_LL_OFF)
-		ptr = skb->mac.raw + k - SKF_LL_OFF;
+		ptr = skb_mac_header(skb) + k - SKF_LL_OFF;
 
-	if (ptr >= skb->head && ptr < skb->tail)
+	if (ptr >= skb->head && ptr < skb_tail_pointer(skb))
 		return ptr;
 	return NULL;
 }
diff --git a/net/core/gen_stats.c b/net/core/gen_stats.c
index 259473d..bcc2559 100644
--- a/net/core/gen_stats.c
+++ b/net/core/gen_stats.c
@@ -61,7 +61,7 @@
 	spin_lock_bh(lock);
 	d->lock = lock;
 	if (type)
-		d->tail = (struct rtattr *) skb->tail;
+		d->tail = (struct rtattr *)skb_tail_pointer(skb);
 	d->skb = skb;
 	d->compat_tc_stats = tc_stats_type;
 	d->compat_xstats = xstats_type;
@@ -212,7 +212,7 @@
 gnet_stats_finish_copy(struct gnet_dump *d)
 {
 	if (d->tail)
-		d->tail->rta_len = d->skb->tail - (u8 *) d->tail;
+		d->tail->rta_len = skb_tail_pointer(d->skb) - (u8 *)d->tail;
 
 	if (d->compat_tc_stats)
 		if (gnet_stats_copy(d, d->compat_tc_stats, &d->tc_stats,
diff --git a/net/core/link_watch.c b/net/core/link_watch.c
index 8b45c9d..e3c26a9 100644
--- a/net/core/link_watch.c
+++ b/net/core/link_watch.c
@@ -79,7 +79,7 @@
 	case IF_LINK_MODE_DEFAULT:
 	default:
 		break;
-	};
+	}
 
 	dev->operstate = operstate;
 
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 841e3f3..6f3bb73 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -1125,7 +1125,7 @@
 {
 	struct net_device *dev = skb->dev;
 
-	__skb_pull(skb, skb->nh.raw - skb->data);
+	__skb_pull(skb, skb_network_offset(skb));
 
 	if (dev->hard_header &&
 	    dev->hard_header(skb, dev, ntohs(skb->protocol), NULL, NULL,
@@ -1147,7 +1147,7 @@
 	if (!dst || !(neigh = dst->neighbour))
 		goto discard;
 
-	__skb_pull(skb, skb->nh.raw - skb->data);
+	__skb_pull(skb, skb_network_offset(skb));
 
 	if (!neigh_event_send(neigh, skb)) {
 		int err;
@@ -1190,7 +1190,7 @@
 	struct neighbour *neigh = dst->neighbour;
 	struct net_device *dev = neigh->dev;
 
-	__skb_pull(skb, skb->nh.raw - skb->data);
+	__skb_pull(skb, skb_network_offset(skb));
 
 	read_lock_bh(&neigh->lock);
 	err = dev->hard_header(skb, dev, ntohs(skb->protocol),
@@ -1441,7 +1441,7 @@
 	return 0;
 }
 
-int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
 	struct ndmsg *ndm;
 	struct nlattr *dst_attr;
@@ -1506,7 +1506,7 @@
 	return err;
 }
 
-int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
 	struct ndmsg *ndm;
 	struct nlattr *tb[NDA_MAX+1];
@@ -1786,7 +1786,7 @@
 	[NDTPA_LOCKTIME]		= { .type = NLA_U64 },
 };
 
-int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
 	struct neigh_table *tbl;
 	struct ndtmsg *ndtmsg;
@@ -1910,7 +1910,7 @@
 	return err;
 }
 
-int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
+static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
 {
 	int family, tidx, nidx = 0;
 	int tbl_skip = cb->args[0];
@@ -2034,7 +2034,7 @@
 	return rc;
 }
 
-int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
+static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
 {
 	struct neigh_table *tbl;
 	int t, family, s_t;
@@ -2393,7 +2393,7 @@
 	return 0;
 }
 
-static struct seq_operations neigh_stat_seq_ops = {
+static const struct seq_operations neigh_stat_seq_ops = {
 	.start	= neigh_stat_seq_start,
 	.next	= neigh_stat_seq_next,
 	.stop	= neigh_stat_seq_stop,
@@ -2746,14 +2746,26 @@
 
 #endif	/* CONFIG_SYSCTL */
 
+static int __init neigh_init(void)
+{
+	rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL);
+	rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL);
+	rtnl_register(PF_UNSPEC, RTM_GETNEIGH, NULL, neigh_dump_info);
+
+	rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info);
+	rtnl_register(PF_UNSPEC, RTM_SETNEIGHTBL, neightbl_set, NULL);
+
+	return 0;
+}
+
+subsys_initcall(neigh_init);
+
 EXPORT_SYMBOL(__neigh_event_send);
 EXPORT_SYMBOL(neigh_changeaddr);
 EXPORT_SYMBOL(neigh_compat_output);
 EXPORT_SYMBOL(neigh_connected_output);
 EXPORT_SYMBOL(neigh_create);
-EXPORT_SYMBOL(neigh_delete);
 EXPORT_SYMBOL(neigh_destroy);
-EXPORT_SYMBOL(neigh_dump_info);
 EXPORT_SYMBOL(neigh_event_ns);
 EXPORT_SYMBOL(neigh_ifdown);
 EXPORT_SYMBOL(neigh_lookup);
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index 4cbb129..b21307b 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -352,8 +352,8 @@
 
 	read_lock(&dev_base_lock);
 	if (dev_isalive(dev)) {
-		if(dev->wireless_handlers &&
-		   dev->wireless_handlers->get_wireless_stats)
+		if (dev->wireless_handlers &&
+		    dev->wireless_handlers->get_wireless_stats)
 			iw = dev->wireless_handlers->get_wireless_stats(dev);
 		if (iw != NULL)
 			ret = (*format)(iw, buf);
@@ -412,20 +412,25 @@
 			 int num_envp, char *buf, int size)
 {
 	struct net_device *dev = to_net_dev(d);
-	int i = 0;
-	int n;
+	int retval, len = 0, i = 0;
 
 	/* pass interface to uevent. */
-	envp[i++] = buf;
-	n = snprintf(buf, size, "INTERFACE=%s", dev->name) + 1;
-	buf += n;
-	size -= n;
+	retval = add_uevent_var(envp, num_envp, &i,
+				buf, size, &len,
+				"INTERFACE=%s", dev->name);
+	if (retval)
+		goto exit;
 
-	if ((size <= 0) || (i >= num_envp))
-		return -ENOMEM;
+	/* pass ifindex to uevent.
+	 * ifindex is useful as it won't change (interface name may change)
+	 * and is what RtNetlink uses natively. */
+	retval = add_uevent_var(envp, num_envp, &i,
+				buf, size, &len,
+				"IFINDEX=%d", dev->ifindex);
 
+exit:
 	envp[i] = NULL;
-	return 0;
+	return retval;
 }
 #endif
 
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 4581ece..b316435 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -86,7 +86,7 @@
 {
 	__wsum psum;
 
-	if (uh->check == 0 || skb->ip_summed == CHECKSUM_UNNECESSARY)
+	if (uh->check == 0 || skb_csum_unnecessary(skb))
 		return 0;
 
 	psum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP, 0);
@@ -293,10 +293,12 @@
 	if (!skb)
 		return;
 
-	memcpy(skb->data, msg, len);
+	skb_copy_to_linear_data(skb, msg, len);
 	skb->len += len;
 
-	skb->h.uh = udph = (struct udphdr *) skb_push(skb, sizeof(*udph));
+	skb_push(skb, sizeof(*udph));
+	skb_reset_transport_header(skb);
+	udph = udp_hdr(skb);
 	udph->source = htons(np->local_port);
 	udph->dest = htons(np->remote_port);
 	udph->len = htons(udp_len);
@@ -308,7 +310,9 @@
 	if (udph->check == 0)
 		udph->check = CSUM_MANGLED_0;
 
-	skb->nh.iph = iph = (struct iphdr *)skb_push(skb, sizeof(*iph));
+	skb_push(skb, sizeof(*iph));
+	skb_reset_network_header(skb);
+	iph = ip_hdr(skb);
 
 	/* iph->version = 4; iph->ihl = 5; */
 	put_unaligned(0x45, (unsigned char *)iph);
@@ -324,7 +328,7 @@
 	iph->check    = ip_fast_csum((unsigned char *)iph, iph->ihl);
 
 	eth = (struct ethhdr *) skb_push(skb, ETH_HLEN);
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	skb->protocol = eth->h_proto = htons(ETH_P_IP);
 	memcpy(eth->h_source, np->local_mac, 6);
 	memcpy(eth->h_dest, np->remote_mac, 6);
@@ -359,8 +363,9 @@
 				 (2 * sizeof(u32)))))
 		return;
 
-	skb->h.raw = skb->nh.raw = skb->data;
-	arp = skb->nh.arph;
+	skb_reset_network_header(skb);
+	skb_reset_transport_header(skb);
+	arp = arp_hdr(skb);
 
 	if ((arp->ar_hrd != htons(ARPHRD_ETHER) &&
 	     arp->ar_hrd != htons(ARPHRD_IEEE802)) ||
@@ -389,7 +394,7 @@
 	if (!send_skb)
 		return;
 
-	send_skb->nh.raw = send_skb->data;
+	skb_reset_network_header(send_skb);
 	arp = (struct arphdr *) skb_put(send_skb, size);
 	send_skb->dev = skb->dev;
 	send_skb->protocol = htons(ETH_P_ARP);
@@ -443,7 +448,7 @@
 		goto out;
 
 	/* check if netpoll clients need ARP */
-	if (skb->protocol == __constant_htons(ETH_P_ARP) &&
+	if (skb->protocol == htons(ETH_P_ARP) &&
 	    atomic_read(&trapped)) {
 		skb_queue_tail(&npi->arp_tx, skb);
 		return 1;
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 4b01496..b92a322 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -164,14 +164,11 @@
 
 #define VERSION  "pktgen v2.68: Packet Generator for packet performance testing.\n"
 
-/* #define PG_DEBUG(a) a */
-#define PG_DEBUG(a)
-
 /* The buckets are exponential in 'width' */
 #define LAT_BUCKETS_MAX 32
 #define IP_NAME_SZ 32
 #define MAX_MPLS_LABELS 16 /* This is the max label stack depth */
-#define MPLS_STACK_BOTTOM __constant_htonl(0x00000100)
+#define MPLS_STACK_BOTTOM htonl(0x00000100)
 
 /* Device flag bits */
 #define F_IPSRC_RND   (1<<0)	/* IP-Src Random  */
@@ -214,15 +211,11 @@
 };
 
 struct pktgen_dev {
-
 	/*
 	 * Try to keep frequent/infrequent used vars. separated.
 	 */
-
-	char ifname[IFNAMSIZ];
-	char result[512];
-
-	struct pktgen_thread *pg_thread;	/* the owner */
+	struct proc_dir_entry *entry;	/* proc file */
+	struct pktgen_thread *pg_thread;/* the owner */
 	struct list_head list;		/* Used for chaining in the thread's run-queue */
 
 	int running;		/* if this changes to false, the test will stop */
@@ -349,6 +342,8 @@
 	unsigned cflows;	/* Concurrent flows (config) */
 	unsigned lflow;		/* Flow length  (config) */
 	unsigned nflows;	/* accumulated flows (stats) */
+
+	char result[512];
 };
 
 struct pktgen_hdr {
@@ -468,17 +463,6 @@
 	return tmp;
 }
 
-static inline u32 pktgen_random(void)
-{
-#if 0
-	__u32 n;
-	get_random_bytes(&n, 4);
-	return n;
-#else
-	return net_random();
-#endif
-}
-
 static inline __u64 getCurMs(void)
 {
 	struct timeval tv;
@@ -512,7 +496,7 @@
 static int pktgen_stop_device(struct pktgen_dev *pkt_dev);
 static void pktgen_stop(struct pktgen_thread *t);
 static void pktgen_clear_counters(struct pktgen_dev *pkt_dev);
-static int pktgen_mark_device(const char *ifname);
+
 static unsigned int scan_ip6(const char *s, char ip[16]);
 static unsigned int fmt_ip6(char *s, const char ip[16]);
 
@@ -606,7 +590,7 @@
 		   "     frags: %d  delay: %u  clone_skb: %d  ifname: %s\n",
 		   pkt_dev->nfrags,
 		   1000 * pkt_dev->delay_us + pkt_dev->delay_ns,
-		   pkt_dev->clone_skb, pkt_dev->ifname);
+		   pkt_dev->clone_skb, pkt_dev->odev->name);
 
 	seq_printf(seq, "     flows: %u flowlen: %u\n", pkt_dev->cflows,
 		   pkt_dev->lflow);
@@ -661,7 +645,7 @@
 	if (pkt_dev->nr_labels) {
 		unsigned i;
 		seq_printf(seq, "     mpls: ");
-		for(i = 0; i < pkt_dev->nr_labels; i++)
+		for (i = 0; i < pkt_dev->nr_labels; i++)
 			seq_printf(seq, "%08x%s", ntohl(pkt_dev->labels[i]),
 				   i == pkt_dev->nr_labels-1 ? "\n" : ", ");
 	}
@@ -766,7 +750,7 @@
 	int i = 0;
 	*num = 0;
 
-	for(; i < maxlen; i++) {
+	for (; i < maxlen; i++) {
 		char c;
 		*num <<= 4;
 		if (get_user(c, &user_buffer[i]))
@@ -802,7 +786,7 @@
 			break;
 		default:
 			goto done;
-		};
+		}
 	}
 done:
 	return i;
@@ -845,7 +829,7 @@
 			break;
 		default:
 			break;
-		};
+		}
 	}
 done_str:
 	return i;
@@ -874,7 +858,7 @@
 		n++;
 		if (n >= MAX_MPLS_LABELS)
 			return -E2BIG;
-	} while(c == ',');
+	} while (c == ',');
 
 	pkt_dev->nr_labels = n;
 	return i;
@@ -1503,7 +1487,7 @@
 		if (len < 0) { return len; }
 		i += len;
 		offset = sprintf(pg_result, "OK: mpls=");
-		for(n = 0; n < pkt_dev->nr_labels; n++)
+		for (n = 0; n < pkt_dev->nr_labels; n++)
 			offset += sprintf(pg_result + offset,
 					  "%08x%s", ntohl(pkt_dev->labels[n]),
 					  n == pkt_dev->nr_labels-1 ? "" : ",");
@@ -1697,13 +1681,13 @@
 	if_lock(t);
 	list_for_each_entry(pkt_dev, &t->if_list, list)
 		if (pkt_dev->running)
-			seq_printf(seq, "%s ", pkt_dev->ifname);
+			seq_printf(seq, "%s ", pkt_dev->odev->name);
 
 	seq_printf(seq, "\nStopped: ");
 
 	list_for_each_entry(pkt_dev, &t->if_list, list)
 		if (!pkt_dev->running)
-			seq_printf(seq, "%s ", pkt_dev->ifname);
+			seq_printf(seq, "%s ", pkt_dev->odev->name);
 
 	if (t->result[0])
 		seq_printf(seq, "\nResult: %s\n", t->result);
@@ -1849,16 +1833,14 @@
 /*
  * mark a device for removal
  */
-static int pktgen_mark_device(const char *ifname)
+static void pktgen_mark_device(const char *ifname)
 {
 	struct pktgen_dev *pkt_dev = NULL;
 	const int max_tries = 10, msec_per_try = 125;
 	int i = 0;
-	int ret = 0;
 
 	mutex_lock(&pktgen_thread_lock);
-	PG_DEBUG(printk("pktgen: pktgen_mark_device marking %s for removal\n",
-			ifname));
+	pr_debug("pktgen: pktgen_mark_device marking %s for removal\n", ifname);
 
 	while (1) {
 
@@ -1867,8 +1849,8 @@
 			break;	/* success */
 
 		mutex_unlock(&pktgen_thread_lock);
-		PG_DEBUG(printk("pktgen: pktgen_mark_device waiting for %s "
-				"to disappear....\n", ifname));
+		pr_debug("pktgen: pktgen_mark_device waiting for %s "
+				"to disappear....\n", ifname);
 		schedule_timeout_interruptible(msecs_to_jiffies(msec_per_try));
 		mutex_lock(&pktgen_thread_lock);
 
@@ -1876,79 +1858,91 @@
 			printk("pktgen_mark_device: timed out after waiting "
 			       "%d msec for device %s to be removed\n",
 			       msec_per_try * i, ifname);
-			ret = 1;
 			break;
 		}
 
 	}
 
 	mutex_unlock(&pktgen_thread_lock);
+}
 
-	return ret;
+static void pktgen_change_name(struct net_device *dev)
+{
+	struct pktgen_thread *t;
+
+	list_for_each_entry(t, &pktgen_threads, th_list) {
+		struct pktgen_dev *pkt_dev;
+
+		list_for_each_entry(pkt_dev, &t->if_list, list) {
+			if (pkt_dev->odev != dev)
+				continue;
+
+			remove_proc_entry(pkt_dev->entry->name, pg_proc_dir);
+
+			pkt_dev->entry = create_proc_entry(dev->name, 0600,
+							   pg_proc_dir);
+			if (!pkt_dev->entry)
+				printk(KERN_ERR "pktgen: can't move proc "
+				       " entry for '%s'\n", dev->name);
+			break;
+		}
+	}
 }
 
 static int pktgen_device_event(struct notifier_block *unused,
 			       unsigned long event, void *ptr)
 {
-	struct net_device *dev = (struct net_device *)(ptr);
+	struct net_device *dev = ptr;
 
 	/* It is OK that we do not hold the group lock right now,
 	 * as we run under the RTNL lock.
 	 */
 
 	switch (event) {
-	case NETDEV_CHANGEADDR:
-	case NETDEV_GOING_DOWN:
-	case NETDEV_DOWN:
-	case NETDEV_UP:
-		/* Ignore for now */
+	case NETDEV_CHANGENAME:
+		pktgen_change_name(dev);
 		break;
 
 	case NETDEV_UNREGISTER:
 		pktgen_mark_device(dev->name);
 		break;
-	};
+	}
 
 	return NOTIFY_DONE;
 }
 
 /* Associate pktgen_dev with a device. */
 
-static struct net_device *pktgen_setup_dev(struct pktgen_dev *pkt_dev)
+static int pktgen_setup_dev(struct pktgen_dev *pkt_dev, const char *ifname)
 {
 	struct net_device *odev;
+	int err;
 
 	/* Clean old setups */
-
 	if (pkt_dev->odev) {
 		dev_put(pkt_dev->odev);
 		pkt_dev->odev = NULL;
 	}
 
-	odev = dev_get_by_name(pkt_dev->ifname);
-
+	odev = dev_get_by_name(ifname);
 	if (!odev) {
-		printk("pktgen: no such netdevice: \"%s\"\n", pkt_dev->ifname);
-		goto out;
+		printk("pktgen: no such netdevice: \"%s\"\n", ifname);
+		return -ENODEV;
 	}
+
 	if (odev->type != ARPHRD_ETHER) {
-		printk("pktgen: not an ethernet device: \"%s\"\n",
-		       pkt_dev->ifname);
-		goto out_put;
+		printk("pktgen: not an ethernet device: \"%s\"\n", ifname);
+		err = -EINVAL;
+	} else if (!netif_running(odev)) {
+		printk("pktgen: device is down: \"%s\"\n", ifname);
+		err = -ENETDOWN;
+	} else {
+		pkt_dev->odev = odev;
+		return 0;
 	}
-	if (!netif_running(odev)) {
-		printk("pktgen: device is down: \"%s\"\n", pkt_dev->ifname);
-		goto out_put;
-	}
-	pkt_dev->odev = odev;
 
-	return pkt_dev->odev;
-
-out_put:
 	dev_put(odev);
-out:
-	return NULL;
-
+	return err;
 }
 
 /* Read pkt_dev from the interface and set up internal pktgen_dev
@@ -1956,10 +1950,6 @@
  */
 static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
 {
-	/* Try once more, just in case it works now. */
-	if (!pkt_dev->odev)
-		pktgen_setup_dev(pkt_dev);
-
 	if (!pkt_dev->odev) {
 		printk("pktgen: ERROR: pkt_dev->odev == NULL in setup_inject.\n");
 		sprintf(pkt_dev->result,
@@ -2096,7 +2086,7 @@
 	int flow = 0;
 
 	if (pkt_dev->cflows) {
-		flow = pktgen_random() % pkt_dev->cflows;
+		flow = random32() % pkt_dev->cflows;
 
 		if (pkt_dev->flows[flow].count > pkt_dev->lflow)
 			pkt_dev->flows[flow].count = 0;
@@ -2108,7 +2098,7 @@
 		__u32 tmp;
 
 		if (pkt_dev->flags & F_MACSRC_RND)
-			mc = pktgen_random() % (pkt_dev->src_mac_count);
+			mc = random32() % pkt_dev->src_mac_count;
 		else {
 			mc = pkt_dev->cur_src_mac_offset++;
 			if (pkt_dev->cur_src_mac_offset >
@@ -2134,7 +2124,7 @@
 		__u32 tmp;
 
 		if (pkt_dev->flags & F_MACDST_RND)
-			mc = pktgen_random() % (pkt_dev->dst_mac_count);
+			mc = random32() % pkt_dev->dst_mac_count;
 
 		else {
 			mc = pkt_dev->cur_dst_mac_offset++;
@@ -2158,27 +2148,26 @@
 
 	if (pkt_dev->flags & F_MPLS_RND) {
 		unsigned i;
-		for(i = 0; i < pkt_dev->nr_labels; i++)
+		for (i = 0; i < pkt_dev->nr_labels; i++)
 			if (pkt_dev->labels[i] & MPLS_STACK_BOTTOM)
 				pkt_dev->labels[i] = MPLS_STACK_BOTTOM |
-					     ((__force __be32)pktgen_random() &
+					     ((__force __be32)random32() &
 						      htonl(0x000fffff));
 	}
 
 	if ((pkt_dev->flags & F_VID_RND) && (pkt_dev->vlan_id != 0xffff)) {
-		pkt_dev->vlan_id = pktgen_random() % 4096;
+		pkt_dev->vlan_id = random32() & (4096-1);
 	}
 
 	if ((pkt_dev->flags & F_SVID_RND) && (pkt_dev->svlan_id != 0xffff)) {
-		pkt_dev->svlan_id = pktgen_random() % 4096;
+		pkt_dev->svlan_id = random32() & (4096 - 1);
 	}
 
 	if (pkt_dev->udp_src_min < pkt_dev->udp_src_max) {
 		if (pkt_dev->flags & F_UDPSRC_RND)
-			pkt_dev->cur_udp_src =
-			    ((pktgen_random() %
-			      (pkt_dev->udp_src_max - pkt_dev->udp_src_min)) +
-			     pkt_dev->udp_src_min);
+			pkt_dev->cur_udp_src = random32() %
+				(pkt_dev->udp_src_max - pkt_dev->udp_src_min)
+				+ pkt_dev->udp_src_min;
 
 		else {
 			pkt_dev->cur_udp_src++;
@@ -2189,10 +2178,9 @@
 
 	if (pkt_dev->udp_dst_min < pkt_dev->udp_dst_max) {
 		if (pkt_dev->flags & F_UDPDST_RND) {
-			pkt_dev->cur_udp_dst =
-			    ((pktgen_random() %
-			      (pkt_dev->udp_dst_max - pkt_dev->udp_dst_min)) +
-			     pkt_dev->udp_dst_min);
+			pkt_dev->cur_udp_dst = random32() %
+				(pkt_dev->udp_dst_max - pkt_dev->udp_dst_min)
+				+ pkt_dev->udp_dst_min;
 		} else {
 			pkt_dev->cur_udp_dst++;
 			if (pkt_dev->cur_udp_dst >= pkt_dev->udp_dst_max)
@@ -2207,7 +2195,7 @@
 							       saddr_max))) {
 			__u32 t;
 			if (pkt_dev->flags & F_IPSRC_RND)
-				t = ((pktgen_random() % (imx - imn)) + imn);
+				t = random32() % (imx - imn) + imn;
 			else {
 				t = ntohl(pkt_dev->cur_saddr);
 				t++;
@@ -2228,14 +2216,13 @@
 				__be32 s;
 				if (pkt_dev->flags & F_IPDST_RND) {
 
-					t = pktgen_random() % (imx - imn) + imn;
+					t = random32() % (imx - imn) + imn;
 					s = htonl(t);
 
 					while (LOOPBACK(s) || MULTICAST(s)
 					       || BADCLASS(s) || ZERONET(s)
 					       || LOCAL_MCAST(s)) {
-						t = (pktgen_random() %
-						      (imx - imn)) + imn;
+						t = random32() % (imx - imn) + imn;
 						s = htonl(t);
 					}
 					pkt_dev->cur_daddr = s;
@@ -2267,7 +2254,7 @@
 
 			for (i = 0; i < 4; i++) {
 				pkt_dev->cur_in6_daddr.s6_addr32[i] =
-				    (((__force __be32)pktgen_random() |
+				    (((__force __be32)random32() |
 				      pkt_dev->min_in6_daddr.s6_addr32[i]) &
 				     pkt_dev->max_in6_daddr.s6_addr32[i]);
 			}
@@ -2277,9 +2264,9 @@
 	if (pkt_dev->min_pkt_size < pkt_dev->max_pkt_size) {
 		__u32 t;
 		if (pkt_dev->flags & F_TXSIZE_RND) {
-			t = ((pktgen_random() %
-			      (pkt_dev->max_pkt_size - pkt_dev->min_pkt_size))
-			     + pkt_dev->min_pkt_size);
+			t = random32() %
+				(pkt_dev->max_pkt_size - pkt_dev->min_pkt_size)
+				+ pkt_dev->min_pkt_size;
 		} else {
 			t = pkt_dev->cur_pkt_size + 1;
 			if (t > pkt_dev->max_pkt_size)
@@ -2294,7 +2281,7 @@
 static void mpls_push(__be32 *mpls, struct pktgen_dev *pkt_dev)
 {
 	unsigned i;
-	for(i = 0; i < pkt_dev->nr_labels; i++) {
+	for (i = 0; i < pkt_dev->nr_labels; i++) {
 		*mpls++ = pkt_dev->labels[i] & ~MPLS_STACK_BOTTOM;
 	}
 	mpls--;
@@ -2316,7 +2303,7 @@
 	int datalen, iplen;
 	struct iphdr *iph;
 	struct pktgen_hdr *pgh = NULL;
-	__be16 protocol = __constant_htons(ETH_P_IP);
+	__be16 protocol = htons(ETH_P_IP);
 	__be32 *mpls;
 	__be16 *vlan_tci = NULL;                 /* Encapsulates priority and VLAN ID */
 	__be16 *vlan_encapsulated_proto = NULL;  /* packet type ID field (or len) for VLAN tag */
@@ -2325,10 +2312,10 @@
 
 
 	if (pkt_dev->nr_labels)
-		protocol = __constant_htons(ETH_P_MPLS_UC);
+		protocol = htons(ETH_P_MPLS_UC);
 
 	if (pkt_dev->vlan_id != 0xffff)
-		protocol = __constant_htons(ETH_P_8021Q);
+		protocol = htons(ETH_P_8021Q);
 
 	/* Update any of the values, used when we're incrementing various
 	 * fields.
@@ -2354,24 +2341,28 @@
 		mpls_push(mpls, pkt_dev);
 
 	if (pkt_dev->vlan_id != 0xffff) {
-		if(pkt_dev->svlan_id != 0xffff) {
+		if (pkt_dev->svlan_id != 0xffff) {
 			svlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
 			*svlan_tci = build_tci(pkt_dev->svlan_id,
 					       pkt_dev->svlan_cfi,
 					       pkt_dev->svlan_p);
 			svlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
-			*svlan_encapsulated_proto = __constant_htons(ETH_P_8021Q);
+			*svlan_encapsulated_proto = htons(ETH_P_8021Q);
 		}
 		vlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
 		*vlan_tci = build_tci(pkt_dev->vlan_id,
 				      pkt_dev->vlan_cfi,
 				      pkt_dev->vlan_p);
 		vlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
-		*vlan_encapsulated_proto = __constant_htons(ETH_P_IP);
+		*vlan_encapsulated_proto = htons(ETH_P_IP);
 	}
 
-	iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr));
-	udph = (struct udphdr *)skb_put(skb, sizeof(struct udphdr));
+	skb->network_header = skb->tail;
+	skb->transport_header = skb->network_header + sizeof(struct iphdr);
+	skb_put(skb, sizeof(struct iphdr) + sizeof(struct udphdr));
+
+	iph = ip_hdr(skb);
+	udph = udp_hdr(skb);
 
 	memcpy(eth, pkt_dev->hh, 12);
 	*(__be16 *) & eth[12] = protocol;
@@ -2400,12 +2391,11 @@
 	iph->check = 0;
 	iph->check = ip_fast_csum((void *)iph, iph->ihl);
 	skb->protocol = protocol;
-	skb->mac.raw = ((u8 *) iph) - 14 - pkt_dev->nr_labels*sizeof(u32) -
-		VLAN_TAG_SIZE(pkt_dev) - SVLAN_TAG_SIZE(pkt_dev);
+	skb->mac_header = (skb->network_header - ETH_HLEN -
+			   pkt_dev->nr_labels * sizeof(u32) -
+			   VLAN_TAG_SIZE(pkt_dev) - SVLAN_TAG_SIZE(pkt_dev));
 	skb->dev = odev;
 	skb->pkt_type = PACKET_HOST;
-	skb->nh.iph = iph;
-	skb->h.uh = udph;
 
 	if (pkt_dev->nfrags <= 0)
 		pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
@@ -2654,7 +2644,7 @@
 	int datalen;
 	struct ipv6hdr *iph;
 	struct pktgen_hdr *pgh = NULL;
-	__be16 protocol = __constant_htons(ETH_P_IPV6);
+	__be16 protocol = htons(ETH_P_IPV6);
 	__be32 *mpls;
 	__be16 *vlan_tci = NULL;                 /* Encapsulates priority and VLAN ID */
 	__be16 *vlan_encapsulated_proto = NULL;  /* packet type ID field (or len) for VLAN tag */
@@ -2662,10 +2652,10 @@
 	__be16 *svlan_encapsulated_proto = NULL; /* packet type ID field (or len) for SVLAN tag */
 
 	if (pkt_dev->nr_labels)
-		protocol = __constant_htons(ETH_P_MPLS_UC);
+		protocol = htons(ETH_P_MPLS_UC);
 
 	if (pkt_dev->vlan_id != 0xffff)
-		protocol = __constant_htons(ETH_P_8021Q);
+		protocol = htons(ETH_P_8021Q);
 
 	/* Update any of the values, used when we're incrementing various
 	 * fields.
@@ -2690,24 +2680,28 @@
 		mpls_push(mpls, pkt_dev);
 
 	if (pkt_dev->vlan_id != 0xffff) {
-		if(pkt_dev->svlan_id != 0xffff) {
+		if (pkt_dev->svlan_id != 0xffff) {
 			svlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
 			*svlan_tci = build_tci(pkt_dev->svlan_id,
 					       pkt_dev->svlan_cfi,
 					       pkt_dev->svlan_p);
 			svlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
-			*svlan_encapsulated_proto = __constant_htons(ETH_P_8021Q);
+			*svlan_encapsulated_proto = htons(ETH_P_8021Q);
 		}
 		vlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
 		*vlan_tci = build_tci(pkt_dev->vlan_id,
 				      pkt_dev->vlan_cfi,
 				      pkt_dev->vlan_p);
 		vlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
-		*vlan_encapsulated_proto = __constant_htons(ETH_P_IPV6);
+		*vlan_encapsulated_proto = htons(ETH_P_IPV6);
 	}
 
-	iph = (struct ipv6hdr *)skb_put(skb, sizeof(struct ipv6hdr));
-	udph = (struct udphdr *)skb_put(skb, sizeof(struct udphdr));
+	skb->network_header = skb->tail;
+	skb->transport_header = skb->network_header + sizeof(struct ipv6hdr);
+	skb_put(skb, sizeof(struct ipv6hdr) + sizeof(struct udphdr));
+
+	iph = ipv6_hdr(skb);
+	udph = udp_hdr(skb);
 
 	memcpy(eth, pkt_dev->hh, 12);
 	*(__be16 *) & eth[12] = protocol;
@@ -2729,7 +2723,7 @@
 	udph->len = htons(datalen + sizeof(struct udphdr));
 	udph->check = 0;	/* No checksum */
 
-	*(__be32 *) iph = __constant_htonl(0x60000000);	/* Version + flow */
+	*(__be32 *) iph = htonl(0x60000000);	/* Version + flow */
 
 	if (pkt_dev->traffic_class) {
 		/* Version + traffic class + flow (0) */
@@ -2744,13 +2738,12 @@
 	ipv6_addr_copy(&iph->daddr, &pkt_dev->cur_in6_daddr);
 	ipv6_addr_copy(&iph->saddr, &pkt_dev->cur_in6_saddr);
 
-	skb->mac.raw = ((u8 *) iph) - 14 - pkt_dev->nr_labels*sizeof(u32) -
-		VLAN_TAG_SIZE(pkt_dev) - SVLAN_TAG_SIZE(pkt_dev);
+	skb->mac_header = (skb->network_header - ETH_HLEN -
+			   pkt_dev->nr_labels * sizeof(u32) -
+			   VLAN_TAG_SIZE(pkt_dev) - SVLAN_TAG_SIZE(pkt_dev));
 	skb->protocol = protocol;
 	skb->dev = odev;
 	skb->pkt_type = PACKET_HOST;
-	skb->nh.ipv6h = iph;
-	skb->h.uh = udph;
 
 	if (pkt_dev->nfrags <= 0)
 		pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
@@ -2848,7 +2841,7 @@
 	struct pktgen_dev *pkt_dev;
 	int started = 0;
 
-	PG_DEBUG(printk("pktgen: entering pktgen_run. %p\n", t));
+	pr_debug("pktgen: entering pktgen_run. %p\n", t);
 
 	if_lock(t);
 	list_for_each_entry(pkt_dev, &t->if_list, list) {
@@ -2880,7 +2873,7 @@
 {
 	struct pktgen_thread *t;
 
-	PG_DEBUG(printk("pktgen: entering pktgen_stop_all_threads_ifs.\n"));
+	pr_debug("pktgen: entering pktgen_stop_all_threads_ifs.\n");
 
 	mutex_lock(&pktgen_thread_lock);
 
@@ -2948,7 +2941,7 @@
 {
 	struct pktgen_thread *t;
 
-	PG_DEBUG(printk("pktgen: entering pktgen_run_all_threads.\n"));
+	pr_debug("pktgen: entering pktgen_run_all_threads.\n");
 
 	mutex_lock(&pktgen_thread_lock);
 
@@ -3006,7 +2999,7 @@
 
 	if (!pkt_dev->running) {
 		printk("pktgen: interface: %s is already stopped\n",
-		       pkt_dev->ifname);
+		       pkt_dev->odev->name);
 		return -EINVAL;
 	}
 
@@ -3040,7 +3033,7 @@
 {
 	struct pktgen_dev *pkt_dev;
 
-	PG_DEBUG(printk("pktgen: entering pktgen_stop\n"));
+	pr_debug("pktgen: entering pktgen_stop\n");
 
 	if_lock(t);
 
@@ -3064,7 +3057,7 @@
 	struct list_head *q, *n;
 	struct pktgen_dev *cur;
 
-	PG_DEBUG(printk("pktgen: entering pktgen_rem_one_if\n"));
+	pr_debug("pktgen: entering pktgen_rem_one_if\n");
 
 	if_lock(t);
 
@@ -3093,7 +3086,7 @@
 
 	/* Remove all devices, free mem */
 
-	PG_DEBUG(printk("pktgen: entering pktgen_rem_all_ifs\n"));
+	pr_debug("pktgen: entering pktgen_rem_all_ifs\n");
 	if_lock(t);
 
 	list_for_each_safe(q, n, &t->if_list) {
@@ -3276,7 +3269,7 @@
 
 	t->pid = current->pid;
 
-	PG_DEBUG(printk("pktgen: starting pktgen/%d:  pid=%d\n", cpu, current->pid));
+	pr_debug("pktgen: starting pktgen/%d:  pid=%d\n", cpu, current->pid);
 
 	max_before_softirq = t->max_before_softirq;
 
@@ -3339,13 +3332,13 @@
 		set_current_state(TASK_INTERRUPTIBLE);
 	}
 
-	PG_DEBUG(printk("pktgen: %s stopping all device\n", t->tsk->comm));
+	pr_debug("pktgen: %s stopping all device\n", t->tsk->comm);
 	pktgen_stop(t);
 
-	PG_DEBUG(printk("pktgen: %s removing all device\n", t->tsk->comm));
+	pr_debug("pktgen: %s removing all device\n", t->tsk->comm);
 	pktgen_rem_all_ifs(t);
 
-	PG_DEBUG(printk("pktgen: %s removing thread.\n", t->tsk->comm));
+	pr_debug("pktgen: %s removing thread.\n", t->tsk->comm);
 	pktgen_rem_thread(t);
 
 	return 0;
@@ -3358,13 +3351,13 @@
 	if_lock(t);
 
 	list_for_each_entry(p, &t->if_list, list)
-		if (strncmp(p->ifname, ifname, IFNAMSIZ) == 0) {
+		if (strncmp(p->odev->name, ifname, IFNAMSIZ) == 0) {
 			pkt_dev = p;
 			break;
 		}
 
 	if_unlock(t);
-	PG_DEBUG(printk("pktgen: find_dev(%s) returning %p\n", ifname, pkt_dev));
+	pr_debug("pktgen: find_dev(%s) returning %p\n", ifname, pkt_dev);
 	return pkt_dev;
 }
 
@@ -3399,7 +3392,7 @@
 static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
 {
 	struct pktgen_dev *pkt_dev;
-	struct proc_dir_entry *pe;
+	int err;
 
 	/* We don't allow a device to be on several threads */
 
@@ -3441,29 +3434,28 @@
 	pkt_dev->svlan_cfi = 0;
 	pkt_dev->svlan_id = 0xffff;
 
-	strncpy(pkt_dev->ifname, ifname, IFNAMSIZ);
+	err = pktgen_setup_dev(pkt_dev, ifname);
+	if (err)
+		goto out1;
 
-	if (!pktgen_setup_dev(pkt_dev)) {
-		printk("pktgen: ERROR: pktgen_setup_dev failed.\n");
-		if (pkt_dev->flows)
-			vfree(pkt_dev->flows);
-		kfree(pkt_dev);
-		return -ENODEV;
-	}
-
-	pe = create_proc_entry(ifname, 0600, pg_proc_dir);
-	if (!pe) {
+	pkt_dev->entry = create_proc_entry(ifname, 0600, pg_proc_dir);
+	if (!pkt_dev->entry) {
 		printk("pktgen: cannot create %s/%s procfs entry.\n",
 		       PG_PROC_DIR, ifname);
-		if (pkt_dev->flows)
-			vfree(pkt_dev->flows);
-		kfree(pkt_dev);
-		return -EINVAL;
+		err = -EINVAL;
+		goto out2;
 	}
-	pe->proc_fops = &pktgen_if_fops;
-	pe->data = pkt_dev;
+	pkt_dev->entry->proc_fops = &pktgen_if_fops;
+	pkt_dev->entry->data = pkt_dev;
 
 	return add_dev_to_thread(t, pkt_dev);
+out2:
+	dev_put(pkt_dev->odev);
+out1:
+	if (pkt_dev->flows)
+		vfree(pkt_dev->flows);
+	kfree(pkt_dev);
+	return err;
 }
 
 static int __init pktgen_create_thread(int cpu)
@@ -3533,7 +3525,7 @@
 				struct pktgen_dev *pkt_dev)
 {
 
-	PG_DEBUG(printk("pktgen: remove_device pkt_dev=%p\n", pkt_dev));
+	pr_debug("pktgen: remove_device pkt_dev=%p\n", pkt_dev);
 
 	if (pkt_dev->running) {
 		printk("pktgen:WARNING: trying to remove a running interface, stopping it now.\n");
@@ -3551,9 +3543,8 @@
 
 	_rem_dev_from_if_list(t, pkt_dev);
 
-	/* Clean up proc file system */
-
-	remove_proc_entry(pkt_dev->ifname, pg_proc_dir);
+	if (pkt_dev->entry)
+		remove_proc_entry(pkt_dev->entry->name, pg_proc_dir);
 
 	if (pkt_dev->flows)
 		vfree(pkt_dev->flows);
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 33ea8ea..cec1111 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -50,11 +50,13 @@
 #include <net/sock.h>
 #include <net/pkt_sched.h>
 #include <net/fib_rules.h>
-#include <net/netlink.h>
-#ifdef CONFIG_NET_WIRELESS_RTNETLINK
-#include <linux/wireless.h>
-#include <net/iw_handler.h>
-#endif	/* CONFIG_NET_WIRELESS_RTNETLINK */
+#include <net/rtnetlink.h>
+
+struct rtnl_link
+{
+	rtnl_doit_func		doit;
+	rtnl_dumpit_func	dumpit;
+};
 
 static DEFINE_MUTEX(rtnl_mutex);
 static struct sock *rtnl;
@@ -95,7 +97,151 @@
 	return 0;
 }
 
-struct rtnetlink_link * rtnetlink_links[NPROTO];
+static struct rtnl_link *rtnl_msg_handlers[NPROTO];
+
+static inline int rtm_msgindex(int msgtype)
+{
+	int msgindex = msgtype - RTM_BASE;
+
+	/*
+	 * msgindex < 0 implies someone tried to register a netlink
+	 * control code. msgindex >= RTM_NR_MSGTYPES may indicate that
+	 * the message type has not been added to linux/rtnetlink.h
+	 */
+	BUG_ON(msgindex < 0 || msgindex >= RTM_NR_MSGTYPES);
+
+	return msgindex;
+}
+
+static rtnl_doit_func rtnl_get_doit(int protocol, int msgindex)
+{
+	struct rtnl_link *tab;
+
+	tab = rtnl_msg_handlers[protocol];
+	if (tab == NULL || tab[msgindex].doit == NULL)
+		tab = rtnl_msg_handlers[PF_UNSPEC];
+
+	return tab ? tab[msgindex].doit : NULL;
+}
+
+static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex)
+{
+	struct rtnl_link *tab;
+
+	tab = rtnl_msg_handlers[protocol];
+	if (tab == NULL || tab[msgindex].dumpit == NULL)
+		tab = rtnl_msg_handlers[PF_UNSPEC];
+
+	return tab ? tab[msgindex].dumpit : NULL;
+}
+
+/**
+ * __rtnl_register - Register a rtnetlink message type
+ * @protocol: Protocol family or PF_UNSPEC
+ * @msgtype: rtnetlink message type
+ * @doit: Function pointer called for each request message
+ * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message
+ *
+ * Registers the specified function pointers (at least one of them has
+ * to be non-NULL) to be called whenever a request message for the
+ * specified protocol family and message type is received.
+ *
+ * The special protocol family PF_UNSPEC may be used to define fallback
+ * function pointers for the case when no entry for the specific protocol
+ * family exists.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int __rtnl_register(int protocol, int msgtype,
+		    rtnl_doit_func doit, rtnl_dumpit_func dumpit)
+{
+	struct rtnl_link *tab;
+	int msgindex;
+
+	BUG_ON(protocol < 0 || protocol >= NPROTO);
+	msgindex = rtm_msgindex(msgtype);
+
+	tab = rtnl_msg_handlers[protocol];
+	if (tab == NULL) {
+		tab = kcalloc(RTM_NR_MSGTYPES, sizeof(*tab), GFP_KERNEL);
+		if (tab == NULL)
+			return -ENOBUFS;
+
+		rtnl_msg_handlers[protocol] = tab;
+	}
+
+	if (doit)
+		tab[msgindex].doit = doit;
+
+	if (dumpit)
+		tab[msgindex].dumpit = dumpit;
+
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(__rtnl_register);
+
+/**
+ * rtnl_register - Register a rtnetlink message type
+ *
+ * Identical to __rtnl_register() but panics on failure. This is useful
+ * as failure of this function is very unlikely, it can only happen due
+ * to lack of memory when allocating the chain to store all message
+ * handlers for a protocol. Meant for use in init functions where lack
+ * of memory implies no sense in continueing.
+ */
+void rtnl_register(int protocol, int msgtype,
+		   rtnl_doit_func doit, rtnl_dumpit_func dumpit)
+{
+	if (__rtnl_register(protocol, msgtype, doit, dumpit) < 0)
+		panic("Unable to register rtnetlink message handler, "
+		      "protocol = %d, message type = %d\n",
+		      protocol, msgtype);
+}
+
+EXPORT_SYMBOL_GPL(rtnl_register);
+
+/**
+ * rtnl_unregister - Unregister a rtnetlink message type
+ * @protocol: Protocol family or PF_UNSPEC
+ * @msgtype: rtnetlink message type
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int rtnl_unregister(int protocol, int msgtype)
+{
+	int msgindex;
+
+	BUG_ON(protocol < 0 || protocol >= NPROTO);
+	msgindex = rtm_msgindex(msgtype);
+
+	if (rtnl_msg_handlers[protocol] == NULL)
+		return -ENOENT;
+
+	rtnl_msg_handlers[protocol][msgindex].doit = NULL;
+	rtnl_msg_handlers[protocol][msgindex].dumpit = NULL;
+
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(rtnl_unregister);
+
+/**
+ * rtnl_unregister_all - Unregister all rtnetlink message type of a protocol
+ * @protocol : Protocol family or PF_UNSPEC
+ *
+ * Identical to calling rtnl_unregster() for all registered message types
+ * of a certain protocol family.
+ */
+void rtnl_unregister_all(int protocol)
+{
+	BUG_ON(protocol < 0 || protocol >= NPROTO);
+
+	kfree(rtnl_msg_handlers[protocol]);
+	rtnl_msg_handlers[protocol] = NULL;
+}
+
+EXPORT_SYMBOL_GPL(rtnl_unregister_all);
 
 static const int rtm_min[RTM_NR_FAMILIES] =
 {
@@ -249,7 +395,7 @@
 		    operstate == IF_OPER_UNKNOWN)
 			operstate = IF_OPER_DORMANT;
 		break;
-	};
+	}
 
 	if (dev->operstate != operstate) {
 		write_lock_bh(&dev_base_lock);
@@ -393,7 +539,6 @@
 	int s_idx = cb->args[0];
 	struct net_device *dev;
 
-	read_lock(&dev_base_lock);
 	for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
 		if (idx < s_idx)
 			continue;
@@ -402,7 +547,6 @@
 				     cb->nlh->nlmsg_seq, 0, NLM_F_MULTI) <= 0)
 			break;
 	}
-	read_unlock(&dev_base_lock);
 	cb->args[0] = idx;
 
 	return skb->len;
@@ -536,17 +680,6 @@
 		modified = 1;
 	}
 
-#ifdef CONFIG_NET_WIRELESS_RTNETLINK
-	if (tb[IFLA_WIRELESS]) {
-		/* Call Wireless Extensions.
-		 * Various stuff checked in there... */
-		err = wireless_rtnetlink_set(dev, nla_data(tb[IFLA_WIRELESS]),
-					     nla_len(tb[IFLA_WIRELESS]));
-		if (err < 0)
-			goto errout_dev;
-	}
-#endif	/* CONFIG_NET_WIRELESS_RTNETLINK */
-
 	if (tb[IFLA_BROADCAST]) {
 		nla_memcpy(dev->broadcast, tb[IFLA_BROADCAST], dev->addr_len);
 		send_addr_notify = 1;
@@ -610,22 +743,6 @@
 	} else
 		return -EINVAL;
 
-
-#ifdef CONFIG_NET_WIRELESS_RTNETLINK
-	if (tb[IFLA_WIRELESS]) {
-		/* Call Wireless Extensions. We need to know the size before
-		 * we can alloc. Various stuff checked in there... */
-		err = wireless_rtnetlink_get(dev, nla_data(tb[IFLA_WIRELESS]),
-					     nla_len(tb[IFLA_WIRELESS]),
-					     &iw_buf, &iw_buf_len);
-		if (err < 0)
-			goto errout;
-
-		/* Payload is at an offset in buffer */
-		iw = iw_buf + IW_EV_POINT_OFF;
-	}
-#endif	/* CONFIG_NET_WIRELESS_RTNETLINK */
-
 	nskb = nlmsg_new(if_nlmsg_size(iw_buf_len), GFP_KERNEL);
 	if (nskb == NULL) {
 		err = -ENOBUFS;
@@ -659,12 +776,12 @@
 		int type = cb->nlh->nlmsg_type-RTM_BASE;
 		if (idx < s_idx || idx == PF_PACKET)
 			continue;
-		if (rtnetlink_links[idx] == NULL ||
-		    rtnetlink_links[idx][type].dumpit == NULL)
+		if (rtnl_msg_handlers[idx] == NULL ||
+		    rtnl_msg_handlers[idx][type].dumpit == NULL)
 			continue;
 		if (idx > s_idx)
 			memset(&cb->args[0], 0, sizeof(cb->args));
-		if (rtnetlink_links[idx][type].dumpit(skb, cb))
+		if (rtnl_msg_handlers[idx][type].dumpit(skb, cb))
 			break;
 	}
 	cb->family = idx;
@@ -700,30 +817,18 @@
 
 /* Process one rtnetlink message. */
 
-static __inline__ int
-rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *errp)
+static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
-	struct rtnetlink_link *link;
-	struct rtnetlink_link *link_tab;
+	rtnl_doit_func doit;
 	int sz_idx, kind;
 	int min_len;
 	int family;
 	int type;
 	int err;
 
-	/* Only requests are handled by kernel now */
-	if (!(nlh->nlmsg_flags&NLM_F_REQUEST))
-		return 0;
-
 	type = nlh->nlmsg_type;
-
-	/* A control message: ignore them */
-	if (type < RTM_BASE)
-		return 0;
-
-	/* Unknown message: reply with EINVAL */
 	if (type > RTM_MAX)
-		goto err_inval;
+		return -EOPNOTSUPP;
 
 	type -= RTM_BASE;
 
@@ -732,45 +837,33 @@
 		return 0;
 
 	family = ((struct rtgenmsg*)NLMSG_DATA(nlh))->rtgen_family;
-	if (family >= NPROTO) {
-		*errp = -EAFNOSUPPORT;
-		return -1;
-	}
-
-	link_tab = rtnetlink_links[family];
-	if (link_tab == NULL)
-		link_tab = rtnetlink_links[PF_UNSPEC];
-	link = &link_tab[type];
+	if (family >= NPROTO)
+		return -EAFNOSUPPORT;
 
 	sz_idx = type>>2;
 	kind = type&3;
 
-	if (kind != 2 && security_netlink_recv(skb, CAP_NET_ADMIN)) {
-		*errp = -EPERM;
-		return -1;
-	}
+	if (kind != 2 && security_netlink_recv(skb, CAP_NET_ADMIN))
+		return -EPERM;
 
 	if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
-		if (link->dumpit == NULL)
-			link = &(rtnetlink_links[PF_UNSPEC][type]);
+		rtnl_dumpit_func dumpit;
 
-		if (link->dumpit == NULL)
-			goto err_inval;
+		dumpit = rtnl_get_dumpit(family, type);
+		if (dumpit == NULL)
+			return -EOPNOTSUPP;
 
-		if ((*errp = netlink_dump_start(rtnl, skb, nlh,
-						link->dumpit, NULL)) != 0) {
-			return -1;
-		}
-
-		netlink_queue_skip(nlh, skb);
-		return -1;
+		__rtnl_unlock();
+		err = netlink_dump_start(rtnl, skb, nlh, dumpit, NULL);
+		rtnl_lock();
+		return err;
 	}
 
 	memset(rta_buf, 0, (rtattr_max * sizeof(struct rtattr *)));
 
 	min_len = rtm_min[sz_idx];
 	if (nlh->nlmsg_len < min_len)
-		goto err_inval;
+		return -EINVAL;
 
 	if (nlh->nlmsg_len > min_len) {
 		int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
@@ -780,25 +873,18 @@
 			unsigned flavor = attr->rta_type;
 			if (flavor) {
 				if (flavor > rta_max[sz_idx])
-					goto err_inval;
+					return -EINVAL;
 				rta_buf[flavor-1] = attr;
 			}
 			attr = RTA_NEXT(attr, attrlen);
 		}
 	}
 
-	if (link->doit == NULL)
-		link = &(rtnetlink_links[PF_UNSPEC][type]);
-	if (link->doit == NULL)
-		goto err_inval;
-	err = link->doit(skb, nlh, (void *)&rta_buf[0]);
+	doit = rtnl_get_doit(family, type);
+	if (doit == NULL)
+		return -EOPNOTSUPP;
 
-	*errp = err;
-	return err;
-
-err_inval:
-	*errp = -EINVAL;
-	return -1;
+	return doit(skb, nlh, (void *)&rta_buf[0]);
 }
 
 static void rtnetlink_rcv(struct sock *sk, int len)
@@ -814,25 +900,6 @@
 	} while (qlen);
 }
 
-static struct rtnetlink_link link_rtnetlink_table[RTM_NR_MSGTYPES] =
-{
-	[RTM_GETLINK     - RTM_BASE] = { .doit   = rtnl_getlink,
-					 .dumpit = rtnl_dump_ifinfo	 },
-	[RTM_SETLINK     - RTM_BASE] = { .doit   = rtnl_setlink		 },
-	[RTM_GETADDR     - RTM_BASE] = { .dumpit = rtnl_dump_all	 },
-	[RTM_GETROUTE    - RTM_BASE] = { .dumpit = rtnl_dump_all	 },
-	[RTM_NEWNEIGH    - RTM_BASE] = { .doit   = neigh_add		 },
-	[RTM_DELNEIGH    - RTM_BASE] = { .doit   = neigh_delete		 },
-	[RTM_GETNEIGH    - RTM_BASE] = { .dumpit = neigh_dump_info	 },
-#ifdef CONFIG_FIB_RULES
-	[RTM_NEWRULE     - RTM_BASE] = { .doit   = fib_nl_newrule	 },
-	[RTM_DELRULE     - RTM_BASE] = { .doit   = fib_nl_delrule	 },
-#endif
-	[RTM_GETRULE     - RTM_BASE] = { .dumpit = rtnl_dump_all	 },
-	[RTM_GETNEIGHTBL - RTM_BASE] = { .dumpit = neightbl_dump_info	 },
-	[RTM_SETNEIGHTBL - RTM_BASE] = { .doit   = neightbl_set		 },
-};
-
 static int rtnetlink_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
 	struct net_device *dev = ptr;
@@ -874,19 +941,22 @@
 		panic("rtnetlink_init: cannot allocate rta_buf\n");
 
 	rtnl = netlink_kernel_create(NETLINK_ROUTE, RTNLGRP_MAX, rtnetlink_rcv,
-				     THIS_MODULE);
+				     &rtnl_mutex, THIS_MODULE);
 	if (rtnl == NULL)
 		panic("rtnetlink_init: cannot initialize rtnetlink\n");
 	netlink_set_nonroot(NETLINK_ROUTE, NL_NONROOT_RECV);
 	register_netdevice_notifier(&rtnetlink_dev_notifier);
-	rtnetlink_links[PF_UNSPEC] = link_rtnetlink_table;
-	rtnetlink_links[PF_PACKET] = link_rtnetlink_table;
+
+	rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, rtnl_dump_ifinfo);
+	rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL);
+
+	rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all);
+	rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all);
 }
 
 EXPORT_SYMBOL(__rta_fill);
 EXPORT_SYMBOL(rtattr_strlcpy);
 EXPORT_SYMBOL(rtattr_parse);
-EXPORT_SYMBOL(rtnetlink_links);
 EXPORT_SYMBOL(rtnetlink_put_metrics);
 EXPORT_SYMBOL(rtnl_lock);
 EXPORT_SYMBOL(rtnl_trylock);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 336958f..1422573 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -55,6 +55,7 @@
 #include <linux/cache.h>
 #include <linux/rtnetlink.h>
 #include <linux/init.h>
+#include <linux/scatterlist.h>
 
 #include <net/protocol.h>
 #include <net/dst.h>
@@ -87,8 +88,9 @@
 void skb_over_panic(struct sk_buff *skb, int sz, void *here)
 {
 	printk(KERN_EMERG "skb_over_panic: text:%p len:%d put:%d head:%p "
-			  "data:%p tail:%p end:%p dev:%s\n",
-	       here, skb->len, sz, skb->head, skb->data, skb->tail, skb->end,
+			  "data:%p tail:%#lx end:%#lx dev:%s\n",
+	       here, skb->len, sz, skb->head, skb->data,
+	       (unsigned long)skb->tail, (unsigned long)skb->end,
 	       skb->dev ? skb->dev->name : "<NULL>");
 	BUG();
 }
@@ -105,8 +107,9 @@
 void skb_under_panic(struct sk_buff *skb, int sz, void *here)
 {
 	printk(KERN_EMERG "skb_under_panic: text:%p len:%d put:%d head:%p "
-			  "data:%p tail:%p end:%p dev:%s\n",
-	       here, skb->len, sz, skb->head, skb->data, skb->tail, skb->end,
+			  "data:%p tail:%#lx end:%#lx dev:%s\n",
+	       here, skb->len, sz, skb->head, skb->data,
+	       (unsigned long)skb->tail, (unsigned long)skb->end,
 	       skb->dev ? skb->dev->name : "<NULL>");
 	BUG();
 }
@@ -155,20 +158,22 @@
 	if (!skb)
 		goto out;
 
-	/* Get the DATA. Size must match skb_add_mtu(). */
 	size = SKB_DATA_ALIGN(size);
 	data = kmalloc_node_track_caller(size + sizeof(struct skb_shared_info),
 			gfp_mask, node);
 	if (!data)
 		goto nodata;
 
-	memset(skb, 0, offsetof(struct sk_buff, truesize));
+	/*
+	 * See comment in sk_buff definition, just before the 'tail' member
+	 */
+	memset(skb, 0, offsetof(struct sk_buff, tail));
 	skb->truesize = size + sizeof(struct sk_buff);
 	atomic_set(&skb->users, 1);
 	skb->head = data;
 	skb->data = data;
-	skb->tail = data;
-	skb->end  = data + size;
+	skb_reset_tail_pointer(skb);
+	skb->end = skb->tail + size;
 	/* make sure we initialize shinfo sequentially */
 	shinfo = skb_shinfo(skb);
 	atomic_set(&shinfo->dataref, 1);
@@ -299,7 +304,7 @@
 		if (atomic_dec_and_test(fclone_ref))
 			kmem_cache_free(skbuff_fclone_cache, other);
 		break;
-	};
+	}
 }
 
 /**
@@ -321,15 +326,13 @@
 		WARN_ON(in_irq());
 		skb->destructor(skb);
 	}
-#ifdef CONFIG_NETFILTER
-	nf_conntrack_put(skb->nfct);
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+	nf_conntrack_put(skb->nfct);
 	nf_conntrack_put_reasm(skb->nfct_reasm);
 #endif
 #ifdef CONFIG_BRIDGE_NETFILTER
 	nf_bridge_put(skb->nf_bridge);
 #endif
-#endif
 /* XXX: IS this still necessary? - JHS */
 #ifdef CONFIG_NET_SCHED
 	skb->tc_index = 0;
@@ -396,9 +399,9 @@
 	n->sk = NULL;
 	C(tstamp);
 	C(dev);
-	C(h);
-	C(nh);
-	C(mac);
+	C(transport_header);
+	C(network_header);
+	C(mac_header);
 	C(dst);
 	dst_clone(skb->dst);
 	C(sp);
@@ -422,19 +425,7 @@
 	C(protocol);
 	n->destructor = NULL;
 	C(mark);
-#ifdef CONFIG_NETFILTER
-	C(nfct);
-	nf_conntrack_get(skb->nfct);
-	C(nfctinfo);
-#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
-	C(nfct_reasm);
-	nf_conntrack_get_reasm(skb->nfct_reasm);
-#endif
-#ifdef CONFIG_BRIDGE_NETFILTER
-	C(nf_bridge);
-	nf_bridge_get(skb->nf_bridge);
-#endif
-#endif /*CONFIG_NETFILTER*/
+	__nf_copy(n, skb);
 #ifdef CONFIG_NET_SCHED
 	C(tc_index);
 #ifdef CONFIG_NET_CLS_ACT
@@ -460,11 +451,12 @@
 
 static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
 {
+#ifndef NET_SKBUFF_DATA_USES_OFFSET
 	/*
 	 *	Shift between the two data areas in bytes
 	 */
 	unsigned long offset = new->data - old->data;
-
+#endif
 	new->sk		= NULL;
 	new->dev	= old->dev;
 	new->priority	= old->priority;
@@ -473,9 +465,15 @@
 #ifdef CONFIG_INET
 	new->sp		= secpath_get(old->sp);
 #endif
-	new->h.raw	= old->h.raw + offset;
-	new->nh.raw	= old->nh.raw + offset;
-	new->mac.raw	= old->mac.raw + offset;
+	new->transport_header = old->transport_header;
+	new->network_header   = old->network_header;
+	new->mac_header	      = old->mac_header;
+#ifndef NET_SKBUFF_DATA_USES_OFFSET
+	/* {transport,network,mac}_header are relative to skb->head */
+	new->transport_header += offset;
+	new->network_header   += offset;
+	new->mac_header	      += offset;
+#endif
 	memcpy(new->cb, old->cb, sizeof(old->cb));
 	new->local_df	= old->local_df;
 	new->fclone	= SKB_FCLONE_UNAVAILABLE;
@@ -483,22 +481,10 @@
 	new->tstamp	= old->tstamp;
 	new->destructor = NULL;
 	new->mark	= old->mark;
-#ifdef CONFIG_NETFILTER
-	new->nfct	= old->nfct;
-	nf_conntrack_get(old->nfct);
-	new->nfctinfo	= old->nfctinfo;
-#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
-	new->nfct_reasm = old->nfct_reasm;
-	nf_conntrack_get_reasm(old->nfct_reasm);
-#endif
+	__nf_copy(new, old);
 #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)
 	new->ipvs_property = old->ipvs_property;
 #endif
-#ifdef CONFIG_BRIDGE_NETFILTER
-	new->nf_bridge	= old->nf_bridge;
-	nf_bridge_get(old->nf_bridge);
-#endif
-#endif
 #ifdef CONFIG_NET_SCHED
 #ifdef CONFIG_NET_CLS_ACT
 	new->tc_verd = old->tc_verd;
@@ -535,8 +521,12 @@
 	/*
 	 *	Allocate the copy buffer
 	 */
-	struct sk_buff *n = alloc_skb(skb->end - skb->head + skb->data_len,
-				      gfp_mask);
+	struct sk_buff *n;
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+	n = alloc_skb(skb->end + skb->data_len, gfp_mask);
+#else
+	n = alloc_skb(skb->end - skb->head + skb->data_len, gfp_mask);
+#endif
 	if (!n)
 		return NULL;
 
@@ -573,8 +563,12 @@
 	/*
 	 *	Allocate the copy buffer
 	 */
-	struct sk_buff *n = alloc_skb(skb->end - skb->head, gfp_mask);
-
+	struct sk_buff *n;
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+	n = alloc_skb(skb->end, gfp_mask);
+#else
+	n = alloc_skb(skb->end - skb->head, gfp_mask);
+#endif
 	if (!n)
 		goto out;
 
@@ -583,7 +577,7 @@
 	/* Set the tail pointer and length */
 	skb_put(n, skb_headlen(skb));
 	/* Copy the bytes */
-	memcpy(n->data, skb->data, n->len);
+	skb_copy_from_linear_data(skb, n->data, n->len);
 	n->csum	     = skb->csum;
 	n->ip_summed = skb->ip_summed;
 
@@ -632,7 +626,11 @@
 {
 	int i;
 	u8 *data;
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+	int size = nhead + skb->end + ntail;
+#else
 	int size = nhead + (skb->end - skb->head) + ntail;
+#endif
 	long off;
 
 	if (skb_shared(skb))
@@ -646,8 +644,14 @@
 
 	/* Copy only real data... and, alas, header. This should be
 	 * optimized for the cases when header is void. */
-	memcpy(data + nhead, skb->head, skb->tail - skb->head);
-	memcpy(data + size, skb->end, sizeof(struct skb_shared_info));
+	memcpy(data + nhead, skb->head,
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+		skb->tail);
+#else
+		skb->tail - skb->head);
+#endif
+	memcpy(data + size, skb_end_pointer(skb),
+	       sizeof(struct skb_shared_info));
 
 	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
 		get_page(skb_shinfo(skb)->frags[i].page);
@@ -660,12 +664,18 @@
 	off = (data + nhead) - skb->head;
 
 	skb->head     = data;
-	skb->end      = data + size;
 	skb->data    += off;
-	skb->tail    += off;
-	skb->mac.raw += off;
-	skb->h.raw   += off;
-	skb->nh.raw  += off;
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+	skb->end      = size;
+	off           = nhead;
+#else
+	skb->end      = skb->head + size;
+#endif
+	/* {transport,network,mac}_header and tail are relative to skb->head */
+	skb->tail	      += off;
+	skb->transport_header += off;
+	skb->network_header   += off;
+	skb->mac_header	      += off;
 	skb->cloned   = 0;
 	skb->nohdr    = 0;
 	atomic_set(&skb_shinfo(skb)->dataref, 1);
@@ -726,7 +736,9 @@
 	 */
 	struct sk_buff *n = alloc_skb(newheadroom + skb->len + newtailroom,
 				      gfp_mask);
+	int oldheadroom = skb_headroom(skb);
 	int head_copy_len, head_copy_off;
+	int off = 0;
 
 	if (!n)
 		return NULL;
@@ -736,7 +748,7 @@
 	/* Set the tail pointer and length */
 	skb_put(n, skb->len);
 
-	head_copy_len = skb_headroom(skb);
+	head_copy_len = oldheadroom;
 	head_copy_off = 0;
 	if (newheadroom <= head_copy_len)
 		head_copy_len = newheadroom;
@@ -750,6 +762,13 @@
 
 	copy_skb_header(n, skb);
 
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+	off                  = newheadroom - oldheadroom;
+#endif
+	n->transport_header += off;
+	n->network_header   += off;
+	n->mac_header	    += off;
+
 	return n;
 }
 
@@ -877,7 +896,7 @@
 	} else {
 		skb->len       = len;
 		skb->data_len  = 0;
-		skb->tail      = skb->data + len;
+		skb_set_tail_pointer(skb, len);
 	}
 
 	return 0;
@@ -922,7 +941,7 @@
 			return NULL;
 	}
 
-	if (skb_copy_bits(skb, skb_headlen(skb), skb->tail, delta))
+	if (skb_copy_bits(skb, skb_headlen(skb), skb_tail_pointer(skb), delta))
 		BUG();
 
 	/* Optimization: no fragments, no reasons to preestimate
@@ -1018,7 +1037,7 @@
 	skb->tail     += delta;
 	skb->data_len -= delta;
 
-	return skb->tail;
+	return skb_tail_pointer(skb);
 }
 
 /* Copy some data bits from skb to kernel buffer. */
@@ -1035,7 +1054,7 @@
 	if ((copy = start - offset) > 0) {
 		if (copy > len)
 			copy = len;
-		memcpy(to, skb->data + offset, copy);
+		skb_copy_from_linear_data_offset(skb, offset, to, copy);
 		if ((len -= copy) == 0)
 			return 0;
 		offset += copy;
@@ -1110,7 +1129,7 @@
  *	traversing fragment lists and such.
  */
 
-int skb_store_bits(const struct sk_buff *skb, int offset, void *from, int len)
+int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len)
 {
 	int i, copy;
 	int start = skb_headlen(skb);
@@ -1121,7 +1140,7 @@
 	if ((copy = start - offset) > 0) {
 		if (copy > len)
 			copy = len;
-		memcpy(skb->data + offset, from, copy);
+		skb_copy_to_linear_data_offset(skb, offset, from, copy);
 		if ((len -= copy) == 0)
 			return 0;
 		offset += copy;
@@ -1348,13 +1367,13 @@
 	long csstart;
 
 	if (skb->ip_summed == CHECKSUM_PARTIAL)
-		csstart = skb->h.raw - skb->data;
+		csstart = skb->csum_start - skb_headroom(skb);
 	else
 		csstart = skb_headlen(skb);
 
 	BUG_ON(csstart > skb_headlen(skb));
 
-	memcpy(to, skb->data, csstart);
+	skb_copy_from_linear_data(skb, to, csstart);
 
 	csum = 0;
 	if (csstart != skb->len)
@@ -1522,27 +1541,14 @@
 	spin_unlock_irqrestore(&list->lock, flags);
 }
 
-#if 0
-/*
- * 	Tune the memory allocator for a new MTU size.
- */
-void skb_add_mtu(int mtu)
-{
-	/* Must match allocation in alloc_skb */
-	mtu = SKB_DATA_ALIGN(mtu) + sizeof(struct skb_shared_info);
-
-	kmem_add_cache_size(mtu);
-}
-#endif
-
 static inline void skb_split_inside_header(struct sk_buff *skb,
 					   struct sk_buff* skb1,
 					   const u32 len, const int pos)
 {
 	int i;
 
-	memcpy(skb_put(skb1, pos - len), skb->data + len, pos - len);
-
+	skb_copy_from_linear_data_offset(skb, len, skb_put(skb1, pos - len),
+					 pos - len);
 	/* And move data appendix as is. */
 	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
 		skb_shinfo(skb1)->frags[i] = skb_shinfo(skb)->frags[i];
@@ -1553,7 +1559,7 @@
 	skb1->len		   += skb1->data_len;
 	skb->data_len		   = 0;
 	skb->len		   = len;
-	skb->tail		   = skb->data + len;
+	skb_set_tail_pointer(skb, len);
 }
 
 static inline void skb_split_no_header(struct sk_buff *skb,
@@ -1878,7 +1884,7 @@
 	struct sk_buff *segs = NULL;
 	struct sk_buff *tail = NULL;
 	unsigned int mss = skb_shinfo(skb)->gso_size;
-	unsigned int doffset = skb->data - skb->mac.raw;
+	unsigned int doffset = skb->data - skb_mac_header(skb);
 	unsigned int offset = doffset;
 	unsigned int headroom;
 	unsigned int len;
@@ -1928,11 +1934,12 @@
 		nskb->mac_len = skb->mac_len;
 
 		skb_reserve(nskb, headroom);
-		nskb->mac.raw = nskb->data;
-		nskb->nh.raw = nskb->data + skb->mac_len;
-		nskb->h.raw = nskb->nh.raw + (skb->h.raw - skb->nh.raw);
-		memcpy(skb_put(nskb, doffset), skb->data, doffset);
-
+		skb_reset_mac_header(nskb);
+		skb_set_network_header(nskb, skb->mac_len);
+		nskb->transport_header = (nskb->network_header +
+					  skb_network_header_len(skb));
+		skb_copy_from_linear_data(skb, skb_put(nskb, doffset),
+					  doffset);
 		if (!sg) {
 			nskb->csum = skb_copy_and_csum_bits(skb, offset,
 							    skb_put(nskb, len),
@@ -1945,7 +1952,8 @@
 
 		nskb->ip_summed = CHECKSUM_PARTIAL;
 		nskb->csum = skb->csum;
-		memcpy(skb_put(nskb, hsize), skb->data + offset, hsize);
+		skb_copy_from_linear_data_offset(skb, offset,
+						 skb_put(nskb, hsize), hsize);
 
 		while (pos < offset + len) {
 			BUG_ON(i >= nfrags);
@@ -2005,6 +2013,190 @@
 						NULL, NULL);
 }
 
+/**
+ *	skb_to_sgvec - Fill a scatter-gather list from a socket buffer
+ *	@skb: Socket buffer containing the buffers to be mapped
+ *	@sg: The scatter-gather list to map into
+ *	@offset: The offset into the buffer's contents to start mapping
+ *	@len: Length of buffer space to be mapped
+ *
+ *	Fill the specified scatter-gather list with mappings/pointers into a
+ *	region of the buffer space attached to a socket buffer.
+ */
+int
+skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
+{
+	int start = skb_headlen(skb);
+	int i, copy = start - offset;
+	int elt = 0;
+
+	if (copy > 0) {
+		if (copy > len)
+			copy = len;
+		sg[elt].page = virt_to_page(skb->data + offset);
+		sg[elt].offset = (unsigned long)(skb->data + offset) % PAGE_SIZE;
+		sg[elt].length = copy;
+		elt++;
+		if ((len -= copy) == 0)
+			return elt;
+		offset += copy;
+	}
+
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+		int end;
+
+		BUG_TRAP(start <= offset + len);
+
+		end = start + skb_shinfo(skb)->frags[i].size;
+		if ((copy = end - offset) > 0) {
+			skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+			if (copy > len)
+				copy = len;
+			sg[elt].page = frag->page;
+			sg[elt].offset = frag->page_offset+offset-start;
+			sg[elt].length = copy;
+			elt++;
+			if (!(len -= copy))
+				return elt;
+			offset += copy;
+		}
+		start = end;
+	}
+
+	if (skb_shinfo(skb)->frag_list) {
+		struct sk_buff *list = skb_shinfo(skb)->frag_list;
+
+		for (; list; list = list->next) {
+			int end;
+
+			BUG_TRAP(start <= offset + len);
+
+			end = start + list->len;
+			if ((copy = end - offset) > 0) {
+				if (copy > len)
+					copy = len;
+				elt += skb_to_sgvec(list, sg+elt, offset - start, copy);
+				if ((len -= copy) == 0)
+					return elt;
+				offset += copy;
+			}
+			start = end;
+		}
+	}
+	BUG_ON(len);
+	return elt;
+}
+
+/**
+ *	skb_cow_data - Check that a socket buffer's data buffers are writable
+ *	@skb: The socket buffer to check.
+ *	@tailbits: Amount of trailing space to be added
+ *	@trailer: Returned pointer to the skb where the @tailbits space begins
+ *
+ *	Make sure that the data buffers attached to a socket buffer are
+ *	writable. If they are not, private copies are made of the data buffers
+ *	and the socket buffer is set to use these instead.
+ *
+ *	If @tailbits is given, make sure that there is space to write @tailbits
+ *	bytes of data beyond current end of socket buffer.  @trailer will be
+ *	set to point to the skb in which this space begins.
+ *
+ *	The number of scatterlist elements required to completely map the
+ *	COW'd and extended socket buffer will be returned.
+ */
+int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer)
+{
+	int copyflag;
+	int elt;
+	struct sk_buff *skb1, **skb_p;
+
+	/* If skb is cloned or its head is paged, reallocate
+	 * head pulling out all the pages (pages are considered not writable
+	 * at the moment even if they are anonymous).
+	 */
+	if ((skb_cloned(skb) || skb_shinfo(skb)->nr_frags) &&
+	    __pskb_pull_tail(skb, skb_pagelen(skb)-skb_headlen(skb)) == NULL)
+		return -ENOMEM;
+
+	/* Easy case. Most of packets will go this way. */
+	if (!skb_shinfo(skb)->frag_list) {
+		/* A little of trouble, not enough of space for trailer.
+		 * This should not happen, when stack is tuned to generate
+		 * good frames. OK, on miss we reallocate and reserve even more
+		 * space, 128 bytes is fair. */
+
+		if (skb_tailroom(skb) < tailbits &&
+		    pskb_expand_head(skb, 0, tailbits-skb_tailroom(skb)+128, GFP_ATOMIC))
+			return -ENOMEM;
+
+		/* Voila! */
+		*trailer = skb;
+		return 1;
+	}
+
+	/* Misery. We are in troubles, going to mincer fragments... */
+
+	elt = 1;
+	skb_p = &skb_shinfo(skb)->frag_list;
+	copyflag = 0;
+
+	while ((skb1 = *skb_p) != NULL) {
+		int ntail = 0;
+
+		/* The fragment is partially pulled by someone,
+		 * this can happen on input. Copy it and everything
+		 * after it. */
+
+		if (skb_shared(skb1))
+			copyflag = 1;
+
+		/* If the skb is the last, worry about trailer. */
+
+		if (skb1->next == NULL && tailbits) {
+			if (skb_shinfo(skb1)->nr_frags ||
+			    skb_shinfo(skb1)->frag_list ||
+			    skb_tailroom(skb1) < tailbits)
+				ntail = tailbits + 128;
+		}
+
+		if (copyflag ||
+		    skb_cloned(skb1) ||
+		    ntail ||
+		    skb_shinfo(skb1)->nr_frags ||
+		    skb_shinfo(skb1)->frag_list) {
+			struct sk_buff *skb2;
+
+			/* Fuck, we are miserable poor guys... */
+			if (ntail == 0)
+				skb2 = skb_copy(skb1, GFP_ATOMIC);
+			else
+				skb2 = skb_copy_expand(skb1,
+						       skb_headroom(skb1),
+						       ntail,
+						       GFP_ATOMIC);
+			if (unlikely(skb2 == NULL))
+				return -ENOMEM;
+
+			if (skb1->sk)
+				skb_set_owner_w(skb2, skb1->sk);
+
+			/* Looking around. Are we still alive?
+			 * OK, link new skb, drop old one */
+
+			skb2->next = skb1->next;
+			*skb_p = skb2;
+			kfree_skb(skb1);
+			skb1 = skb2;
+		}
+		elt++;
+		*trailer = skb1;
+		skb_p = &skb1->next;
+	}
+
+	return elt;
+}
+
 EXPORT_SYMBOL(___pskb_trim);
 EXPORT_SYMBOL(__kfree_skb);
 EXPORT_SYMBOL(kfree_skb);
@@ -2039,3 +2231,6 @@
 EXPORT_SYMBOL(skb_abort_seq_read);
 EXPORT_SYMBOL(skb_find_text);
 EXPORT_SYMBOL(skb_append_datato_frags);
+
+EXPORT_SYMBOL_GPL(skb_to_sgvec);
+EXPORT_SYMBOL_GPL(skb_cow_data);
diff --git a/net/core/sock.c b/net/core/sock.c
index 27c4f62..22183c2 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -154,7 +154,8 @@
   "sk_lock-21"       , "sk_lock-AF_SNA"      , "sk_lock-AF_IRDA"     ,
   "sk_lock-AF_PPPOX" , "sk_lock-AF_WANPIPE"  , "sk_lock-AF_LLC"      ,
   "sk_lock-27"       , "sk_lock-28"          , "sk_lock-29"          ,
-  "sk_lock-AF_TIPC"  , "sk_lock-AF_BLUETOOTH", "sk_lock-AF_MAX"
+  "sk_lock-AF_TIPC"  , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV"        ,
+  "sk_lock-AF_RXRPC" , "sk_lock-AF_MAX"
 };
 static const char *af_family_slock_key_strings[AF_MAX+1] = {
   "slock-AF_UNSPEC", "slock-AF_UNIX"     , "slock-AF_INET"     ,
@@ -167,7 +168,8 @@
   "slock-21"       , "slock-AF_SNA"      , "slock-AF_IRDA"     ,
   "slock-AF_PPPOX" , "slock-AF_WANPIPE"  , "slock-AF_LLC"      ,
   "slock-27"       , "slock-28"          , "slock-29"          ,
-  "slock-AF_TIPC"  , "slock-AF_BLUETOOTH", "slock-AF_MAX"
+  "slock-AF_TIPC"  , "slock-AF_BLUETOOTH", "slock-AF_IUCV"     ,
+  "slock-AF_RXRPC" , "slock-AF_MAX"
 };
 #endif
 
@@ -361,8 +363,8 @@
 	}
 #endif
 
-	if(optlen<sizeof(int))
-		return(-EINVAL);
+	if (optlen < sizeof(int))
+		return -EINVAL;
 
 	if (get_user(val, (int __user *)optval))
 		return -EFAULT;
@@ -371,265 +373,270 @@
 
 	lock_sock(sk);
 
-	switch(optname)
-	{
-		case SO_DEBUG:
-			if(val && !capable(CAP_NET_ADMIN))
-			{
-				ret = -EACCES;
-			}
-			else if (valbool)
-				sock_set_flag(sk, SOCK_DBG);
-			else
-				sock_reset_flag(sk, SOCK_DBG);
-			break;
-		case SO_REUSEADDR:
-			sk->sk_reuse = valbool;
-			break;
-		case SO_TYPE:
-		case SO_ERROR:
-			ret = -ENOPROTOOPT;
-			break;
-		case SO_DONTROUTE:
-			if (valbool)
-				sock_set_flag(sk, SOCK_LOCALROUTE);
-			else
-				sock_reset_flag(sk, SOCK_LOCALROUTE);
-			break;
-		case SO_BROADCAST:
-			sock_valbool_flag(sk, SOCK_BROADCAST, valbool);
-			break;
-		case SO_SNDBUF:
-			/* Don't error on this BSD doesn't and if you think
-			   about it this is right. Otherwise apps have to
-			   play 'guess the biggest size' games. RCVBUF/SNDBUF
-			   are treated in BSD as hints */
+	switch(optname) {
+	case SO_DEBUG:
+		if (val && !capable(CAP_NET_ADMIN)) {
+			ret = -EACCES;
+		}
+		else if (valbool)
+			sock_set_flag(sk, SOCK_DBG);
+		else
+			sock_reset_flag(sk, SOCK_DBG);
+		break;
+	case SO_REUSEADDR:
+		sk->sk_reuse = valbool;
+		break;
+	case SO_TYPE:
+	case SO_ERROR:
+		ret = -ENOPROTOOPT;
+		break;
+	case SO_DONTROUTE:
+		if (valbool)
+			sock_set_flag(sk, SOCK_LOCALROUTE);
+		else
+			sock_reset_flag(sk, SOCK_LOCALROUTE);
+		break;
+	case SO_BROADCAST:
+		sock_valbool_flag(sk, SOCK_BROADCAST, valbool);
+		break;
+	case SO_SNDBUF:
+		/* Don't error on this BSD doesn't and if you think
+		   about it this is right. Otherwise apps have to
+		   play 'guess the biggest size' games. RCVBUF/SNDBUF
+		   are treated in BSD as hints */
 
-			if (val > sysctl_wmem_max)
-				val = sysctl_wmem_max;
+		if (val > sysctl_wmem_max)
+			val = sysctl_wmem_max;
 set_sndbuf:
-			sk->sk_userlocks |= SOCK_SNDBUF_LOCK;
-			if ((val * 2) < SOCK_MIN_SNDBUF)
-				sk->sk_sndbuf = SOCK_MIN_SNDBUF;
-			else
-				sk->sk_sndbuf = val * 2;
+		sk->sk_userlocks |= SOCK_SNDBUF_LOCK;
+		if ((val * 2) < SOCK_MIN_SNDBUF)
+			sk->sk_sndbuf = SOCK_MIN_SNDBUF;
+		else
+			sk->sk_sndbuf = val * 2;
 
-			/*
-			 *	Wake up sending tasks if we
-			 *	upped the value.
-			 */
-			sk->sk_write_space(sk);
-			break;
+		/*
+		 *	Wake up sending tasks if we
+		 *	upped the value.
+		 */
+		sk->sk_write_space(sk);
+		break;
 
-		case SO_SNDBUFFORCE:
-			if (!capable(CAP_NET_ADMIN)) {
-				ret = -EPERM;
-				break;
-			}
-			goto set_sndbuf;
-
-		case SO_RCVBUF:
-			/* Don't error on this BSD doesn't and if you think
-			   about it this is right. Otherwise apps have to
-			   play 'guess the biggest size' games. RCVBUF/SNDBUF
-			   are treated in BSD as hints */
-
-			if (val > sysctl_rmem_max)
-				val = sysctl_rmem_max;
-set_rcvbuf:
-			sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
-			/*
-			 * We double it on the way in to account for
-			 * "struct sk_buff" etc. overhead.   Applications
-			 * assume that the SO_RCVBUF setting they make will
-			 * allow that much actual data to be received on that
-			 * socket.
-			 *
-			 * Applications are unaware that "struct sk_buff" and
-			 * other overheads allocate from the receive buffer
-			 * during socket buffer allocation.
-			 *
-			 * And after considering the possible alternatives,
-			 * returning the value we actually used in getsockopt
-			 * is the most desirable behavior.
-			 */
-			if ((val * 2) < SOCK_MIN_RCVBUF)
-				sk->sk_rcvbuf = SOCK_MIN_RCVBUF;
-			else
-				sk->sk_rcvbuf = val * 2;
-			break;
-
-		case SO_RCVBUFFORCE:
-			if (!capable(CAP_NET_ADMIN)) {
-				ret = -EPERM;
-				break;
-			}
-			goto set_rcvbuf;
-
-		case SO_KEEPALIVE:
-#ifdef CONFIG_INET
-			if (sk->sk_protocol == IPPROTO_TCP)
-				tcp_set_keepalive(sk, valbool);
-#endif
-			sock_valbool_flag(sk, SOCK_KEEPOPEN, valbool);
-			break;
-
-		case SO_OOBINLINE:
-			sock_valbool_flag(sk, SOCK_URGINLINE, valbool);
-			break;
-
-		case SO_NO_CHECK:
-			sk->sk_no_check = valbool;
-			break;
-
-		case SO_PRIORITY:
-			if ((val >= 0 && val <= 6) || capable(CAP_NET_ADMIN))
-				sk->sk_priority = val;
-			else
-				ret = -EPERM;
-			break;
-
-		case SO_LINGER:
-			if(optlen<sizeof(ling)) {
-				ret = -EINVAL;	/* 1003.1g */
-				break;
-			}
-			if (copy_from_user(&ling,optval,sizeof(ling))) {
-				ret = -EFAULT;
-				break;
-			}
-			if (!ling.l_onoff)
-				sock_reset_flag(sk, SOCK_LINGER);
-			else {
-#if (BITS_PER_LONG == 32)
-				if ((unsigned int)ling.l_linger >= MAX_SCHEDULE_TIMEOUT/HZ)
-					sk->sk_lingertime = MAX_SCHEDULE_TIMEOUT;
-				else
-#endif
-					sk->sk_lingertime = (unsigned int)ling.l_linger * HZ;
-				sock_set_flag(sk, SOCK_LINGER);
-			}
-			break;
-
-		case SO_BSDCOMPAT:
-			sock_warn_obsolete_bsdism("setsockopt");
-			break;
-
-		case SO_PASSCRED:
-			if (valbool)
-				set_bit(SOCK_PASSCRED, &sock->flags);
-			else
-				clear_bit(SOCK_PASSCRED, &sock->flags);
-			break;
-
-		case SO_TIMESTAMP:
-			if (valbool)  {
-				sock_set_flag(sk, SOCK_RCVTSTAMP);
-				sock_enable_timestamp(sk);
-			} else
-				sock_reset_flag(sk, SOCK_RCVTSTAMP);
-			break;
-
-		case SO_RCVLOWAT:
-			if (val < 0)
-				val = INT_MAX;
-			sk->sk_rcvlowat = val ? : 1;
-			break;
-
-		case SO_RCVTIMEO:
-			ret = sock_set_timeout(&sk->sk_rcvtimeo, optval, optlen);
-			break;
-
-		case SO_SNDTIMEO:
-			ret = sock_set_timeout(&sk->sk_sndtimeo, optval, optlen);
-			break;
-
-#ifdef CONFIG_NETDEVICES
-		case SO_BINDTODEVICE:
-		{
-			char devname[IFNAMSIZ];
-
-			/* Sorry... */
-			if (!capable(CAP_NET_RAW)) {
-				ret = -EPERM;
-				break;
-			}
-
-			/* Bind this socket to a particular device like "eth0",
-			 * as specified in the passed interface name. If the
-			 * name is "" or the option length is zero the socket
-			 * is not bound.
-			 */
-
-			if (!valbool) {
-				sk->sk_bound_dev_if = 0;
-			} else {
-				if (optlen > IFNAMSIZ - 1)
-					optlen = IFNAMSIZ - 1;
-				memset(devname, 0, sizeof(devname));
-				if (copy_from_user(devname, optval, optlen)) {
-					ret = -EFAULT;
-					break;
-				}
-
-				/* Remove any cached route for this socket. */
-				sk_dst_reset(sk);
-
-				if (devname[0] == '\0') {
-					sk->sk_bound_dev_if = 0;
-				} else {
-					struct net_device *dev = dev_get_by_name(devname);
-					if (!dev) {
-						ret = -ENODEV;
-						break;
-					}
-					sk->sk_bound_dev_if = dev->ifindex;
-					dev_put(dev);
-				}
-			}
+	case SO_SNDBUFFORCE:
+		if (!capable(CAP_NET_ADMIN)) {
+			ret = -EPERM;
 			break;
 		}
+		goto set_sndbuf;
+
+	case SO_RCVBUF:
+		/* Don't error on this BSD doesn't and if you think
+		   about it this is right. Otherwise apps have to
+		   play 'guess the biggest size' games. RCVBUF/SNDBUF
+		   are treated in BSD as hints */
+
+		if (val > sysctl_rmem_max)
+			val = sysctl_rmem_max;
+set_rcvbuf:
+		sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
+		/*
+		 * We double it on the way in to account for
+		 * "struct sk_buff" etc. overhead.   Applications
+		 * assume that the SO_RCVBUF setting they make will
+		 * allow that much actual data to be received on that
+		 * socket.
+		 *
+		 * Applications are unaware that "struct sk_buff" and
+		 * other overheads allocate from the receive buffer
+		 * during socket buffer allocation.
+		 *
+		 * And after considering the possible alternatives,
+		 * returning the value we actually used in getsockopt
+		 * is the most desirable behavior.
+		 */
+		if ((val * 2) < SOCK_MIN_RCVBUF)
+			sk->sk_rcvbuf = SOCK_MIN_RCVBUF;
+		else
+			sk->sk_rcvbuf = val * 2;
+		break;
+
+	case SO_RCVBUFFORCE:
+		if (!capable(CAP_NET_ADMIN)) {
+			ret = -EPERM;
+			break;
+		}
+		goto set_rcvbuf;
+
+	case SO_KEEPALIVE:
+#ifdef CONFIG_INET
+		if (sk->sk_protocol == IPPROTO_TCP)
+			tcp_set_keepalive(sk, valbool);
+#endif
+		sock_valbool_flag(sk, SOCK_KEEPOPEN, valbool);
+		break;
+
+	case SO_OOBINLINE:
+		sock_valbool_flag(sk, SOCK_URGINLINE, valbool);
+		break;
+
+	case SO_NO_CHECK:
+		sk->sk_no_check = valbool;
+		break;
+
+	case SO_PRIORITY:
+		if ((val >= 0 && val <= 6) || capable(CAP_NET_ADMIN))
+			sk->sk_priority = val;
+		else
+			ret = -EPERM;
+		break;
+
+	case SO_LINGER:
+		if (optlen < sizeof(ling)) {
+			ret = -EINVAL;	/* 1003.1g */
+			break;
+		}
+		if (copy_from_user(&ling,optval,sizeof(ling))) {
+			ret = -EFAULT;
+			break;
+		}
+		if (!ling.l_onoff)
+			sock_reset_flag(sk, SOCK_LINGER);
+		else {
+#if (BITS_PER_LONG == 32)
+			if ((unsigned int)ling.l_linger >= MAX_SCHEDULE_TIMEOUT/HZ)
+				sk->sk_lingertime = MAX_SCHEDULE_TIMEOUT;
+			else
+#endif
+				sk->sk_lingertime = (unsigned int)ling.l_linger * HZ;
+			sock_set_flag(sk, SOCK_LINGER);
+		}
+		break;
+
+	case SO_BSDCOMPAT:
+		sock_warn_obsolete_bsdism("setsockopt");
+		break;
+
+	case SO_PASSCRED:
+		if (valbool)
+			set_bit(SOCK_PASSCRED, &sock->flags);
+		else
+			clear_bit(SOCK_PASSCRED, &sock->flags);
+		break;
+
+	case SO_TIMESTAMP:
+	case SO_TIMESTAMPNS:
+		if (valbool)  {
+			if (optname == SO_TIMESTAMP)
+				sock_reset_flag(sk, SOCK_RCVTSTAMPNS);
+			else
+				sock_set_flag(sk, SOCK_RCVTSTAMPNS);
+			sock_set_flag(sk, SOCK_RCVTSTAMP);
+			sock_enable_timestamp(sk);
+		} else {
+			sock_reset_flag(sk, SOCK_RCVTSTAMP);
+			sock_reset_flag(sk, SOCK_RCVTSTAMPNS);
+		}
+		break;
+
+	case SO_RCVLOWAT:
+		if (val < 0)
+			val = INT_MAX;
+		sk->sk_rcvlowat = val ? : 1;
+		break;
+
+	case SO_RCVTIMEO:
+		ret = sock_set_timeout(&sk->sk_rcvtimeo, optval, optlen);
+		break;
+
+	case SO_SNDTIMEO:
+		ret = sock_set_timeout(&sk->sk_sndtimeo, optval, optlen);
+		break;
+
+#ifdef CONFIG_NETDEVICES
+	case SO_BINDTODEVICE:
+	{
+		char devname[IFNAMSIZ];
+
+		/* Sorry... */
+		if (!capable(CAP_NET_RAW)) {
+			ret = -EPERM;
+			break;
+		}
+
+		/* Bind this socket to a particular device like "eth0",
+		 * as specified in the passed interface name. If the
+		 * name is "" or the option length is zero the socket
+		 * is not bound.
+		 */
+
+		if (!valbool) {
+			sk->sk_bound_dev_if = 0;
+		} else {
+			if (optlen > IFNAMSIZ - 1)
+				optlen = IFNAMSIZ - 1;
+			memset(devname, 0, sizeof(devname));
+			if (copy_from_user(devname, optval, optlen)) {
+				ret = -EFAULT;
+				break;
+			}
+
+			/* Remove any cached route for this socket. */
+			sk_dst_reset(sk);
+
+			if (devname[0] == '\0') {
+				sk->sk_bound_dev_if = 0;
+			} else {
+				struct net_device *dev = dev_get_by_name(devname);
+				if (!dev) {
+					ret = -ENODEV;
+					break;
+				}
+				sk->sk_bound_dev_if = dev->ifindex;
+				dev_put(dev);
+			}
+		}
+		break;
+	}
 #endif
 
 
-		case SO_ATTACH_FILTER:
-			ret = -EINVAL;
-			if (optlen == sizeof(struct sock_fprog)) {
-				struct sock_fprog fprog;
+	case SO_ATTACH_FILTER:
+		ret = -EINVAL;
+		if (optlen == sizeof(struct sock_fprog)) {
+			struct sock_fprog fprog;
 
-				ret = -EFAULT;
-				if (copy_from_user(&fprog, optval, sizeof(fprog)))
-					break;
-
-				ret = sk_attach_filter(&fprog, sk);
-			}
-			break;
-
-		case SO_DETACH_FILTER:
-			rcu_read_lock_bh();
-			filter = rcu_dereference(sk->sk_filter);
-			if (filter) {
-				rcu_assign_pointer(sk->sk_filter, NULL);
-				sk_filter_release(sk, filter);
-				rcu_read_unlock_bh();
+			ret = -EFAULT;
+			if (copy_from_user(&fprog, optval, sizeof(fprog)))
 				break;
-			}
-			rcu_read_unlock_bh();
-			ret = -ENONET;
-			break;
 
-		case SO_PASSSEC:
-			if (valbool)
-				set_bit(SOCK_PASSSEC, &sock->flags);
-			else
-				clear_bit(SOCK_PASSSEC, &sock->flags);
+			ret = sk_attach_filter(&fprog, sk);
+		}
+		break;
+
+	case SO_DETACH_FILTER:
+		rcu_read_lock_bh();
+		filter = rcu_dereference(sk->sk_filter);
+		if (filter) {
+			rcu_assign_pointer(sk->sk_filter, NULL);
+			sk_filter_release(sk, filter);
+			rcu_read_unlock_bh();
 			break;
+		}
+		rcu_read_unlock_bh();
+		ret = -ENONET;
+		break;
+
+	case SO_PASSSEC:
+		if (valbool)
+			set_bit(SOCK_PASSSEC, &sock->flags);
+		else
+			clear_bit(SOCK_PASSSEC, &sock->flags);
+		break;
 
 		/* We implement the SO_SNDLOWAT etc to
 		   not be settable (1003.1g 5.3) */
-		default:
-			ret = -ENOPROTOOPT;
-			break;
+	default:
+		ret = -ENOPROTOOPT;
+		break;
 	}
 	release_sock(sk);
 	return ret;
@@ -641,8 +648,7 @@
 {
 	struct sock *sk = sock->sk;
 
-	union
-	{
+	union {
 		int val;
 		struct linger ling;
 		struct timeval tm;
@@ -651,148 +657,153 @@
 	unsigned int lv = sizeof(int);
 	int len;
 
-	if(get_user(len,optlen))
+	if (get_user(len, optlen))
 		return -EFAULT;
-	if(len < 0)
+	if (len < 0)
 		return -EINVAL;
 
-	switch(optname)
-	{
-		case SO_DEBUG:
-			v.val = sock_flag(sk, SOCK_DBG);
-			break;
+	switch(optname) {
+	case SO_DEBUG:
+		v.val = sock_flag(sk, SOCK_DBG);
+		break;
 
-		case SO_DONTROUTE:
-			v.val = sock_flag(sk, SOCK_LOCALROUTE);
-			break;
+	case SO_DONTROUTE:
+		v.val = sock_flag(sk, SOCK_LOCALROUTE);
+		break;
 
-		case SO_BROADCAST:
-			v.val = !!sock_flag(sk, SOCK_BROADCAST);
-			break;
+	case SO_BROADCAST:
+		v.val = !!sock_flag(sk, SOCK_BROADCAST);
+		break;
 
-		case SO_SNDBUF:
-			v.val = sk->sk_sndbuf;
-			break;
+	case SO_SNDBUF:
+		v.val = sk->sk_sndbuf;
+		break;
 
-		case SO_RCVBUF:
-			v.val = sk->sk_rcvbuf;
-			break;
+	case SO_RCVBUF:
+		v.val = sk->sk_rcvbuf;
+		break;
 
-		case SO_REUSEADDR:
-			v.val = sk->sk_reuse;
-			break;
+	case SO_REUSEADDR:
+		v.val = sk->sk_reuse;
+		break;
 
-		case SO_KEEPALIVE:
-			v.val = !!sock_flag(sk, SOCK_KEEPOPEN);
-			break;
+	case SO_KEEPALIVE:
+		v.val = !!sock_flag(sk, SOCK_KEEPOPEN);
+		break;
 
-		case SO_TYPE:
-			v.val = sk->sk_type;
-			break;
+	case SO_TYPE:
+		v.val = sk->sk_type;
+		break;
 
-		case SO_ERROR:
-			v.val = -sock_error(sk);
-			if(v.val==0)
-				v.val = xchg(&sk->sk_err_soft, 0);
-			break;
+	case SO_ERROR:
+		v.val = -sock_error(sk);
+		if (v.val==0)
+			v.val = xchg(&sk->sk_err_soft, 0);
+		break;
 
-		case SO_OOBINLINE:
-			v.val = !!sock_flag(sk, SOCK_URGINLINE);
-			break;
+	case SO_OOBINLINE:
+		v.val = !!sock_flag(sk, SOCK_URGINLINE);
+		break;
 
-		case SO_NO_CHECK:
-			v.val = sk->sk_no_check;
-			break;
+	case SO_NO_CHECK:
+		v.val = sk->sk_no_check;
+		break;
 
-		case SO_PRIORITY:
-			v.val = sk->sk_priority;
-			break;
+	case SO_PRIORITY:
+		v.val = sk->sk_priority;
+		break;
 
-		case SO_LINGER:
-			lv		= sizeof(v.ling);
-			v.ling.l_onoff	= !!sock_flag(sk, SOCK_LINGER);
-			v.ling.l_linger	= sk->sk_lingertime / HZ;
-			break;
+	case SO_LINGER:
+		lv		= sizeof(v.ling);
+		v.ling.l_onoff	= !!sock_flag(sk, SOCK_LINGER);
+		v.ling.l_linger	= sk->sk_lingertime / HZ;
+		break;
 
-		case SO_BSDCOMPAT:
-			sock_warn_obsolete_bsdism("getsockopt");
-			break;
+	case SO_BSDCOMPAT:
+		sock_warn_obsolete_bsdism("getsockopt");
+		break;
 
-		case SO_TIMESTAMP:
-			v.val = sock_flag(sk, SOCK_RCVTSTAMP);
-			break;
+	case SO_TIMESTAMP:
+		v.val = sock_flag(sk, SOCK_RCVTSTAMP) &&
+				!sock_flag(sk, SOCK_RCVTSTAMPNS);
+		break;
 
-		case SO_RCVTIMEO:
-			lv=sizeof(struct timeval);
-			if (sk->sk_rcvtimeo == MAX_SCHEDULE_TIMEOUT) {
-				v.tm.tv_sec = 0;
-				v.tm.tv_usec = 0;
-			} else {
-				v.tm.tv_sec = sk->sk_rcvtimeo / HZ;
-				v.tm.tv_usec = ((sk->sk_rcvtimeo % HZ) * 1000000) / HZ;
-			}
-			break;
+	case SO_TIMESTAMPNS:
+		v.val = sock_flag(sk, SOCK_RCVTSTAMPNS);
+		break;
 
-		case SO_SNDTIMEO:
-			lv=sizeof(struct timeval);
-			if (sk->sk_sndtimeo == MAX_SCHEDULE_TIMEOUT) {
-				v.tm.tv_sec = 0;
-				v.tm.tv_usec = 0;
-			} else {
-				v.tm.tv_sec = sk->sk_sndtimeo / HZ;
-				v.tm.tv_usec = ((sk->sk_sndtimeo % HZ) * 1000000) / HZ;
-			}
-			break;
-
-		case SO_RCVLOWAT:
-			v.val = sk->sk_rcvlowat;
-			break;
-
-		case SO_SNDLOWAT:
-			v.val=1;
-			break;
-
-		case SO_PASSCRED:
-			v.val = test_bit(SOCK_PASSCRED, &sock->flags) ? 1 : 0;
-			break;
-
-		case SO_PEERCRED:
-			if (len > sizeof(sk->sk_peercred))
-				len = sizeof(sk->sk_peercred);
-			if (copy_to_user(optval, &sk->sk_peercred, len))
-				return -EFAULT;
-			goto lenout;
-
-		case SO_PEERNAME:
-		{
-			char address[128];
-
-			if (sock->ops->getname(sock, (struct sockaddr *)address, &lv, 2))
-				return -ENOTCONN;
-			if (lv < len)
-				return -EINVAL;
-			if (copy_to_user(optval, address, len))
-				return -EFAULT;
-			goto lenout;
+	case SO_RCVTIMEO:
+		lv=sizeof(struct timeval);
+		if (sk->sk_rcvtimeo == MAX_SCHEDULE_TIMEOUT) {
+			v.tm.tv_sec = 0;
+			v.tm.tv_usec = 0;
+		} else {
+			v.tm.tv_sec = sk->sk_rcvtimeo / HZ;
+			v.tm.tv_usec = ((sk->sk_rcvtimeo % HZ) * 1000000) / HZ;
 		}
+		break;
 
-		/* Dubious BSD thing... Probably nobody even uses it, but
-		 * the UNIX standard wants it for whatever reason... -DaveM
-		 */
-		case SO_ACCEPTCONN:
-			v.val = sk->sk_state == TCP_LISTEN;
-			break;
+	case SO_SNDTIMEO:
+		lv=sizeof(struct timeval);
+		if (sk->sk_sndtimeo == MAX_SCHEDULE_TIMEOUT) {
+			v.tm.tv_sec = 0;
+			v.tm.tv_usec = 0;
+		} else {
+			v.tm.tv_sec = sk->sk_sndtimeo / HZ;
+			v.tm.tv_usec = ((sk->sk_sndtimeo % HZ) * 1000000) / HZ;
+		}
+		break;
 
-		case SO_PASSSEC:
-			v.val = test_bit(SOCK_PASSSEC, &sock->flags) ? 1 : 0;
-			break;
+	case SO_RCVLOWAT:
+		v.val = sk->sk_rcvlowat;
+		break;
 
-		case SO_PEERSEC:
-			return security_socket_getpeersec_stream(sock, optval, optlen, len);
+	case SO_SNDLOWAT:
+		v.val=1;
+		break;
 
-		default:
-			return(-ENOPROTOOPT);
+	case SO_PASSCRED:
+		v.val = test_bit(SOCK_PASSCRED, &sock->flags) ? 1 : 0;
+		break;
+
+	case SO_PEERCRED:
+		if (len > sizeof(sk->sk_peercred))
+			len = sizeof(sk->sk_peercred);
+		if (copy_to_user(optval, &sk->sk_peercred, len))
+			return -EFAULT;
+		goto lenout;
+
+	case SO_PEERNAME:
+	{
+		char address[128];
+
+		if (sock->ops->getname(sock, (struct sockaddr *)address, &lv, 2))
+			return -ENOTCONN;
+		if (lv < len)
+			return -EINVAL;
+		if (copy_to_user(optval, address, len))
+			return -EFAULT;
+		goto lenout;
 	}
+
+	/* Dubious BSD thing... Probably nobody even uses it, but
+	 * the UNIX standard wants it for whatever reason... -DaveM
+	 */
+	case SO_ACCEPTCONN:
+		v.val = sk->sk_state == TCP_LISTEN;
+		break;
+
+	case SO_PASSSEC:
+		v.val = test_bit(SOCK_PASSSEC, &sock->flags) ? 1 : 0;
+		break;
+
+	case SO_PEERSEC:
+		return security_socket_getpeersec_stream(sock, optval, optlen, len);
+
+	default:
+		return -ENOPROTOOPT;
+	}
+
 	if (len > lv)
 		len = lv;
 	if (copy_to_user(optval, &v, len))
@@ -904,6 +915,7 @@
 		sk_node_init(&newsk->sk_node);
 		sock_lock_init(newsk);
 		bh_lock_sock(newsk);
+		newsk->sk_backlog.head	= newsk->sk_backlog.tail = NULL;
 
 		atomic_set(&newsk->sk_rmem_alloc, 0);
 		atomic_set(&newsk->sk_wmem_alloc, 0);
@@ -923,7 +935,6 @@
 		newsk->sk_wmem_queued	= 0;
 		newsk->sk_forward_alloc = 0;
 		newsk->sk_send_head	= NULL;
-		newsk->sk_backlog.head	= newsk->sk_backlog.tail = NULL;
 		newsk->sk_userlocks	= sk->sk_userlocks & ~SOCK_BINDPORT_LOCK;
 
 		sock_reset_flag(newsk, SOCK_DONE);
@@ -970,6 +981,21 @@
 
 EXPORT_SYMBOL_GPL(sk_clone);
 
+void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
+{
+	__sk_dst_set(sk, dst);
+	sk->sk_route_caps = dst->dev->features;
+	if (sk->sk_route_caps & NETIF_F_GSO)
+		sk->sk_route_caps |= NETIF_F_GSO_MASK;
+	if (sk_can_gso(sk)) {
+		if (dst->header_len)
+			sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
+		else
+			sk->sk_route_caps |= NETIF_F_SG | NETIF_F_HW_CSUM;
+	}
+}
+EXPORT_SYMBOL_GPL(sk_setup_caps);
+
 void __init sk_init(void)
 {
 	if (num_physpages <= 4096) {
@@ -1220,13 +1246,13 @@
 {
 	DEFINE_WAIT(wait);
 
-	for(;;) {
+	for (;;) {
 		prepare_to_wait_exclusive(&sk->sk_lock.wq, &wait,
 					TASK_UNINTERRUPTIBLE);
 		spin_unlock_bh(&sk->sk_lock.slock);
 		schedule();
 		spin_lock_bh(&sk->sk_lock.slock);
-		if(!sock_owned_by_user(sk))
+		if (!sock_owned_by_user(sk))
 			break;
 	}
 	finish_wait(&sk->sk_lock.wq, &wait);
@@ -1258,7 +1284,7 @@
 		} while (skb != NULL);
 
 		bh_lock_sock(sk);
-	} while((skb = sk->sk_backlog.head) != NULL);
+	} while ((skb = sk->sk_backlog.head) != NULL);
 }
 
 /**
@@ -1420,7 +1446,7 @@
 	/* Do not wake up a writer until he can make "significant"
 	 * progress.  --DaveM
 	 */
-	if((atomic_read(&sk->sk_wmem_alloc) << 1) <= sk->sk_sndbuf) {
+	if ((atomic_read(&sk->sk_wmem_alloc) << 1) <= sk->sk_sndbuf) {
 		if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))
 			wake_up_interruptible(sk->sk_sleep);
 
@@ -1482,8 +1508,7 @@
 
 	sock_set_flag(sk, SOCK_ZAPPED);
 
-	if(sock)
-	{
+	if (sock) {
 		sk->sk_type	=	sock->type;
 		sk->sk_sleep	=	&sock->wait;
 		sock->sk	=	sk;
@@ -1512,8 +1537,7 @@
 	sk->sk_rcvtimeo		=	MAX_SCHEDULE_TIMEOUT;
 	sk->sk_sndtimeo		=	MAX_SCHEDULE_TIMEOUT;
 
-	sk->sk_stamp.tv_sec     = -1L;
-	sk->sk_stamp.tv_usec    = -1L;
+	sk->sk_stamp = ktime_set(-1L, -1L);
 
 	atomic_set(&sk->sk_refcnt, 1);
 }
@@ -1554,17 +1578,36 @@
 
 int sock_get_timestamp(struct sock *sk, struct timeval __user *userstamp)
 {
+	struct timeval tv;
 	if (!sock_flag(sk, SOCK_TIMESTAMP))
 		sock_enable_timestamp(sk);
-	if (sk->sk_stamp.tv_sec == -1)
+	tv = ktime_to_timeval(sk->sk_stamp);
+	if (tv.tv_sec == -1)
 		return -ENOENT;
-	if (sk->sk_stamp.tv_sec == 0)
-		do_gettimeofday(&sk->sk_stamp);
-	return copy_to_user(userstamp, &sk->sk_stamp, sizeof(struct timeval)) ?
-		-EFAULT : 0;
+	if (tv.tv_sec == 0) {
+		sk->sk_stamp = ktime_get_real();
+		tv = ktime_to_timeval(sk->sk_stamp);
+	}
+	return copy_to_user(userstamp, &tv, sizeof(tv)) ? -EFAULT : 0;
 }
 EXPORT_SYMBOL(sock_get_timestamp);
 
+int sock_get_timestampns(struct sock *sk, struct timespec __user *userstamp)
+{
+	struct timespec ts;
+	if (!sock_flag(sk, SOCK_TIMESTAMP))
+		sock_enable_timestamp(sk);
+	ts = ktime_to_timespec(sk->sk_stamp);
+	if (ts.tv_sec == -1)
+		return -ENOENT;
+	if (ts.tv_sec == 0) {
+		sk->sk_stamp = ktime_get_real();
+		ts = ktime_to_timespec(sk->sk_stamp);
+	}
+	return copy_to_user(userstamp, &ts, sizeof(ts)) ? -EFAULT : 0;
+}
+EXPORT_SYMBOL(sock_get_timestampns);
+
 void sock_enable_timestamp(struct sock *sk)
 {
 	if (!sock_flag(sk, SOCK_TIMESTAMP)) {
@@ -1899,7 +1942,7 @@
 	return 0;
 }
 
-static struct seq_operations proto_seq_ops = {
+static const struct seq_operations proto_seq_ops = {
 	.start  = proto_seq_start,
 	.next   = proto_seq_next,
 	.stop   = proto_seq_stop,
diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
index 1e75b15..b297120 100644
--- a/net/core/sysctl_net_core.c
+++ b/net/core/sysctl_net_core.c
@@ -136,6 +136,14 @@
 		.mode		= 0644,
 		.proc_handler	= &proc_dointvec
 	},
+	{
+		.ctl_name	= NET_CORE_WARNINGS,
+		.procname	= "warnings",
+		.data		= &net_msg_warn,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec
+	},
 	{ .ctl_name = 0 }
 };
 
diff --git a/net/core/utils.c b/net/core/utils.c
index 07236c1..adecfd2 100644
--- a/net/core/utils.c
+++ b/net/core/utils.c
@@ -30,8 +30,10 @@
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
-int net_msg_cost = 5*HZ;
-int net_msg_burst = 10;
+int net_msg_cost __read_mostly = 5*HZ;
+int net_msg_burst __read_mostly = 10;
+int net_msg_warn __read_mostly = 1;
+EXPORT_SYMBOL(net_msg_warn);
 
 /*
  * All net warning printk()s should be guarded by this function.
diff --git a/net/core/wireless.c b/net/core/wireless.c
deleted file mode 100644
index b07fe27..0000000
--- a/net/core/wireless.c
+++ /dev/null
@@ -1,2371 +0,0 @@
-/*
- * This file implement the Wireless Extensions APIs.
- *
- * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com>
- * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
- *
- * (As all part of the Linux kernel, this file is GPL)
- */
-
-/************************** DOCUMENTATION **************************/
-/*
- * API definition :
- * --------------
- * See <linux/wireless.h> for details of the APIs and the rest.
- *
- * History :
- * -------
- *
- * v1 - 5.12.01 - Jean II
- *	o Created this file.
- *
- * v2 - 13.12.01 - Jean II
- *	o Move /proc/net/wireless stuff from net/core/dev.c to here
- *	o Make Wireless Extension IOCTLs go through here
- *	o Added iw_handler handling ;-)
- *	o Added standard ioctl description
- *	o Initial dumb commit strategy based on orinoco.c
- *
- * v3 - 19.12.01 - Jean II
- *	o Make sure we don't go out of standard_ioctl[] in ioctl_standard_call
- *	o Add event dispatcher function
- *	o Add event description
- *	o Propagate events as rtnetlink IFLA_WIRELESS option
- *	o Generate event on selected SET requests
- *
- * v4 - 18.04.02 - Jean II
- *	o Fix stupid off by one in iw_ioctl_description : IW_ESSID_MAX_SIZE + 1
- *
- * v5 - 21.06.02 - Jean II
- *	o Add IW_PRIV_TYPE_ADDR in priv_type_size (+cleanup)
- *	o Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes
- *	o Add IWEVCUSTOM for driver specific event/scanning token
- *	o Turn on WE_STRICT_WRITE by default + kernel warning
- *	o Fix WE_STRICT_WRITE in ioctl_export_private() (32 => iw_num)
- *	o Fix off-by-one in test (extra_size <= IFNAMSIZ)
- *
- * v6 - 9.01.03 - Jean II
- *	o Add common spy support : iw_handler_set_spy(), wireless_spy_update()
- *	o Add enhanced spy support : iw_handler_set_thrspy() and event.
- *	o Add WIRELESS_EXT version display in /proc/net/wireless
- *
- * v6 - 18.06.04 - Jean II
- *	o Change get_spydata() method for added safety
- *	o Remove spy #ifdef, they are always on -> cleaner code
- *	o Allow any size GET request if user specifies length > max
- *		and if request has IW_DESCR_FLAG_NOMAX flag or is SIOCGIWPRIV
- *	o Start migrating get_wireless_stats to struct iw_handler_def
- *	o Add wmb() in iw_handler_set_spy() for non-coherent archs/cpus
- * Based on patch from Pavel Roskin <proski@gnu.org> :
- *	o Fix kernel data leak to user space in private handler handling
- *
- * v7 - 18.3.05 - Jean II
- *	o Remove (struct iw_point *)->pointer from events and streams
- *	o Remove spy_offset from struct iw_handler_def
- *	o Start deprecating dev->get_wireless_stats, output a warning
- *	o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless
- *	o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats)
- *
- * v8 - 17.02.06 - Jean II
- *	o RtNetlink requests support (SET/GET)
- *
- * v8b - 03.08.06 - Herbert Xu
- *	o Fix Wireless Event locking issues.
- *
- * v9 - 14.3.06 - Jean II
- *	o Change length in ESSID and NICK to strlen() instead of strlen()+1
- *	o Make standard_ioctl_num and standard_event_num unsigned
- *	o Remove (struct net_device *)->get_wireless_stats()
- *
- * v10 - 16.3.07 - Jean II
- *	o Prevent leaking of kernel space in stream on 64 bits.
- */
-
-/***************************** INCLUDES *****************************/
-
-#include <linux/module.h>
-#include <linux/types.h>		/* off_t */
-#include <linux/netdevice.h>		/* struct ifreq, dev_get_by_name() */
-#include <linux/proc_fs.h>
-#include <linux/rtnetlink.h>		/* rtnetlink stuff */
-#include <linux/seq_file.h>
-#include <linux/init.h>			/* for __init */
-#include <linux/if_arp.h>		/* ARPHRD_ETHER */
-#include <linux/etherdevice.h>		/* compare_ether_addr */
-#include <linux/interrupt.h>
-
-#include <linux/wireless.h>		/* Pretty obvious */
-#include <net/iw_handler.h>		/* New driver API */
-#include <net/netlink.h>
-
-#include <asm/uaccess.h>		/* copy_to_user() */
-
-/**************************** CONSTANTS ****************************/
-
-/* Debugging stuff */
-#undef WE_IOCTL_DEBUG		/* Debug IOCTL API */
-#undef WE_RTNETLINK_DEBUG	/* Debug RtNetlink API */
-#undef WE_EVENT_DEBUG		/* Debug Event dispatcher */
-#undef WE_SPY_DEBUG		/* Debug enhanced spy support */
-
-/* Options */
-//CONFIG_NET_WIRELESS_RTNETLINK	/* Wireless requests over RtNetlink */
-#define WE_EVENT_RTNETLINK	/* Propagate events using RtNetlink */
-#define WE_SET_EVENT		/* Generate an event on some set commands */
-
-/************************* GLOBAL VARIABLES *************************/
-/*
- * You should not use global variables, because of re-entrancy.
- * On our case, it's only const, so it's OK...
- */
-/*
- * Meta-data about all the standard Wireless Extension request we
- * know about.
- */
-static const struct iw_ioctl_description standard_ioctl[] = {
-	[SIOCSIWCOMMIT	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_NULL,
-	},
-	[SIOCGIWNAME	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_CHAR,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWNWID	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-		.flags		= IW_DESCR_FLAG_EVENT,
-	},
-	[SIOCGIWNWID	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWFREQ	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_FREQ,
-		.flags		= IW_DESCR_FLAG_EVENT,
-	},
-	[SIOCGIWFREQ	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_FREQ,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWMODE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_UINT,
-		.flags		= IW_DESCR_FLAG_EVENT,
-	},
-	[SIOCGIWMODE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_UINT,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWSENS	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWSENS	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWRANGE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_NULL,
-	},
-	[SIOCGIWRANGE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= sizeof(struct iw_range),
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWPRIV	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_NULL,
-	},
-	[SIOCGIWPRIV	- SIOCIWFIRST] = { /* (handled directly by us) */
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct iw_priv_args),
-		.max_tokens	= 16,
-		.flags		= IW_DESCR_FLAG_NOMAX,
-	},
-	[SIOCSIWSTATS	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_NULL,
-	},
-	[SIOCGIWSTATS	- SIOCIWFIRST] = { /* (handled directly by us) */
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= sizeof(struct iw_statistics),
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWSPY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct sockaddr),
-		.max_tokens	= IW_MAX_SPY,
-	},
-	[SIOCGIWSPY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct sockaddr) +
-				  sizeof(struct iw_quality),
-		.max_tokens	= IW_MAX_SPY,
-	},
-	[SIOCSIWTHRSPY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct iw_thrspy),
-		.min_tokens	= 1,
-		.max_tokens	= 1,
-	},
-	[SIOCGIWTHRSPY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct iw_thrspy),
-		.min_tokens	= 1,
-		.max_tokens	= 1,
-	},
-	[SIOCSIWAP	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_ADDR,
-	},
-	[SIOCGIWAP	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_ADDR,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWMLME	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.min_tokens	= sizeof(struct iw_mlme),
-		.max_tokens	= sizeof(struct iw_mlme),
-	},
-	[SIOCGIWAPLIST	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct sockaddr) +
-				  sizeof(struct iw_quality),
-		.max_tokens	= IW_MAX_AP,
-		.flags		= IW_DESCR_FLAG_NOMAX,
-	},
-	[SIOCSIWSCAN	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.min_tokens	= 0,
-		.max_tokens	= sizeof(struct iw_scan_req),
-	},
-	[SIOCGIWSCAN	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_SCAN_MAX_DATA,
-		.flags		= IW_DESCR_FLAG_NOMAX,
-	},
-	[SIOCSIWESSID	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ESSID_MAX_SIZE,
-		.flags		= IW_DESCR_FLAG_EVENT,
-	},
-	[SIOCGIWESSID	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ESSID_MAX_SIZE,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWNICKN	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ESSID_MAX_SIZE,
-	},
-	[SIOCGIWNICKN	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ESSID_MAX_SIZE,
-	},
-	[SIOCSIWRATE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWRATE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWRTS	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWRTS	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWFRAG	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWFRAG	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWTXPOW	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWTXPOW	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWRETRY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWRETRY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWENCODE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ENCODING_TOKEN_MAX,
-		.flags		= IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
-	},
-	[SIOCGIWENCODE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ENCODING_TOKEN_MAX,
-		.flags		= IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
-	},
-	[SIOCSIWPOWER	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWPOWER	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWGENIE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_GENERIC_IE_MAX,
-	},
-	[SIOCGIWGENIE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_GENERIC_IE_MAX,
-	},
-	[SIOCSIWAUTH	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWAUTH	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWENCODEEXT - SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.min_tokens	= sizeof(struct iw_encode_ext),
-		.max_tokens	= sizeof(struct iw_encode_ext) +
-				  IW_ENCODING_TOKEN_MAX,
-	},
-	[SIOCGIWENCODEEXT - SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.min_tokens	= sizeof(struct iw_encode_ext),
-		.max_tokens	= sizeof(struct iw_encode_ext) +
-				  IW_ENCODING_TOKEN_MAX,
-	},
-	[SIOCSIWPMKSA - SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.min_tokens	= sizeof(struct iw_pmksa),
-		.max_tokens	= sizeof(struct iw_pmksa),
-	},
-};
-static const unsigned standard_ioctl_num = (sizeof(standard_ioctl) /
-					    sizeof(struct iw_ioctl_description));
-
-/*
- * Meta-data about all the additional standard Wireless Extension events
- * we know about.
- */
-static const struct iw_ioctl_description standard_event[] = {
-	[IWEVTXDROP	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_ADDR,
-	},
-	[IWEVQUAL	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_QUAL,
-	},
-	[IWEVCUSTOM	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_CUSTOM_MAX,
-	},
-	[IWEVREGISTERED	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_ADDR,
-	},
-	[IWEVEXPIRED	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_ADDR,
-	},
-	[IWEVGENIE	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_GENERIC_IE_MAX,
-	},
-	[IWEVMICHAELMICFAILURE	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= sizeof(struct iw_michaelmicfailure),
-	},
-	[IWEVASSOCREQIE	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_GENERIC_IE_MAX,
-	},
-	[IWEVASSOCRESPIE	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_GENERIC_IE_MAX,
-	},
-	[IWEVPMKIDCAND	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= sizeof(struct iw_pmkid_cand),
-	},
-};
-static const unsigned standard_event_num = (sizeof(standard_event) /
-					    sizeof(struct iw_ioctl_description));
-
-/* Size (in bytes) of the various private data types */
-static const char iw_priv_type_size[] = {
-	0,				/* IW_PRIV_TYPE_NONE */
-	1,				/* IW_PRIV_TYPE_BYTE */
-	1,				/* IW_PRIV_TYPE_CHAR */
-	0,				/* Not defined */
-	sizeof(__u32),			/* IW_PRIV_TYPE_INT */
-	sizeof(struct iw_freq),		/* IW_PRIV_TYPE_FLOAT */
-	sizeof(struct sockaddr),	/* IW_PRIV_TYPE_ADDR */
-	0,				/* Not defined */
-};
-
-/* Size (in bytes) of various events */
-static const int event_type_size[] = {
-	IW_EV_LCP_LEN,			/* IW_HEADER_TYPE_NULL */
-	0,
-	IW_EV_CHAR_LEN,			/* IW_HEADER_TYPE_CHAR */
-	0,
-	IW_EV_UINT_LEN,			/* IW_HEADER_TYPE_UINT */
-	IW_EV_FREQ_LEN,			/* IW_HEADER_TYPE_FREQ */
-	IW_EV_ADDR_LEN,			/* IW_HEADER_TYPE_ADDR */
-	0,
-	IW_EV_POINT_LEN,		/* Without variable payload */
-	IW_EV_PARAM_LEN,		/* IW_HEADER_TYPE_PARAM */
-	IW_EV_QUAL_LEN,			/* IW_HEADER_TYPE_QUAL */
-};
-
-/* Size (in bytes) of various events, as packed */
-static const int event_type_pk_size[] = {
-	IW_EV_LCP_PK_LEN,		/* IW_HEADER_TYPE_NULL */
-	0,
-	IW_EV_CHAR_PK_LEN,		/* IW_HEADER_TYPE_CHAR */
-	0,
-	IW_EV_UINT_PK_LEN,		/* IW_HEADER_TYPE_UINT */
-	IW_EV_FREQ_PK_LEN,		/* IW_HEADER_TYPE_FREQ */
-	IW_EV_ADDR_PK_LEN,		/* IW_HEADER_TYPE_ADDR */
-	0,
-	IW_EV_POINT_PK_LEN,		/* Without variable payload */
-	IW_EV_PARAM_PK_LEN,		/* IW_HEADER_TYPE_PARAM */
-	IW_EV_QUAL_PK_LEN,		/* IW_HEADER_TYPE_QUAL */
-};
-
-/************************ COMMON SUBROUTINES ************************/
-/*
- * Stuff that may be used in various place or doesn't fit in one
- * of the section below.
- */
-
-/* ---------------------------------------------------------------- */
-/*
- * Return the driver handler associated with a specific Wireless Extension.
- * Called from various place, so make sure it remains efficient.
- */
-static inline iw_handler get_handler(struct net_device *dev,
-				     unsigned int cmd)
-{
-	/* Don't "optimise" the following variable, it will crash */
-	unsigned int	index;		/* *MUST* be unsigned */
-
-	/* Check if we have some wireless handlers defined */
-	if(dev->wireless_handlers == NULL)
-		return NULL;
-
-	/* Try as a standard command */
-	index = cmd - SIOCIWFIRST;
-	if(index < dev->wireless_handlers->num_standard)
-		return dev->wireless_handlers->standard[index];
-
-	/* Try as a private command */
-	index = cmd - SIOCIWFIRSTPRIV;
-	if(index < dev->wireless_handlers->num_private)
-		return dev->wireless_handlers->private[index];
-
-	/* Not found */
-	return NULL;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Get statistics out of the driver
- */
-static inline struct iw_statistics *get_wireless_stats(struct net_device *dev)
-{
-	/* New location */
-	if((dev->wireless_handlers != NULL) &&
-	   (dev->wireless_handlers->get_wireless_stats != NULL))
-		return dev->wireless_handlers->get_wireless_stats(dev);
-
-	/* Not found */
-	return (struct iw_statistics *) NULL;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Call the commit handler in the driver
- * (if exist and if conditions are right)
- *
- * Note : our current commit strategy is currently pretty dumb,
- * but we will be able to improve on that...
- * The goal is to try to agreagate as many changes as possible
- * before doing the commit. Drivers that will define a commit handler
- * are usually those that need a reset after changing parameters, so
- * we want to minimise the number of reset.
- * A cool idea is to use a timer : at each "set" command, we re-set the
- * timer, when the timer eventually fires, we call the driver.
- * Hopefully, more on that later.
- *
- * Also, I'm waiting to see how many people will complain about the
- * netif_running(dev) test. I'm open on that one...
- * Hopefully, the driver will remember to do a commit in "open()" ;-)
- */
-static inline int call_commit_handler(struct net_device *	dev)
-{
-	if((netif_running(dev)) &&
-	   (dev->wireless_handlers->standard[0] != NULL)) {
-		/* Call the commit handler on the driver */
-		return dev->wireless_handlers->standard[0](dev, NULL,
-							   NULL, NULL);
-	} else
-		return 0;		/* Command completed successfully */
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Calculate size of private arguments
- */
-static inline int get_priv_size(__u16	args)
-{
-	int	num = args & IW_PRIV_SIZE_MASK;
-	int	type = (args & IW_PRIV_TYPE_MASK) >> 12;
-
-	return num * iw_priv_type_size[type];
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Re-calculate the size of private arguments
- */
-static inline int adjust_priv_size(__u16		args,
-				   union iwreq_data *	wrqu)
-{
-	int	num = wrqu->data.length;
-	int	max = args & IW_PRIV_SIZE_MASK;
-	int	type = (args & IW_PRIV_TYPE_MASK) >> 12;
-
-	/* Make sure the driver doesn't goof up */
-	if (max < num)
-		num = max;
-
-	return num * iw_priv_type_size[type];
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Standard Wireless Handler : get wireless stats
- *	Allow programatic access to /proc/net/wireless even if /proc
- *	doesn't exist... Also more efficient...
- */
-static int iw_handler_get_iwstats(struct net_device *		dev,
-				  struct iw_request_info *	info,
-				  union iwreq_data *		wrqu,
-				  char *			extra)
-{
-	/* Get stats from the driver */
-	struct iw_statistics *stats;
-
-	stats = get_wireless_stats(dev);
-	if (stats != (struct iw_statistics *) NULL) {
-
-		/* Copy statistics to extra */
-		memcpy(extra, stats, sizeof(struct iw_statistics));
-		wrqu->data.length = sizeof(struct iw_statistics);
-
-		/* Check if we need to clear the updated flag */
-		if(wrqu->data.flags != 0)
-			stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
-		return 0;
-	} else
-		return -EOPNOTSUPP;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Standard Wireless Handler : get iwpriv definitions
- * Export the driver private handler definition
- * They will be picked up by tools like iwpriv...
- */
-static int iw_handler_get_private(struct net_device *		dev,
-				  struct iw_request_info *	info,
-				  union iwreq_data *		wrqu,
-				  char *			extra)
-{
-	/* Check if the driver has something to export */
-	if((dev->wireless_handlers->num_private_args == 0) ||
-	   (dev->wireless_handlers->private_args == NULL))
-		return -EOPNOTSUPP;
-
-	/* Check if there is enough buffer up there */
-	if(wrqu->data.length < dev->wireless_handlers->num_private_args) {
-		/* User space can't know in advance how large the buffer
-		 * needs to be. Give it a hint, so that we can support
-		 * any size buffer we want somewhat efficiently... */
-		wrqu->data.length = dev->wireless_handlers->num_private_args;
-		return -E2BIG;
-	}
-
-	/* Set the number of available ioctls. */
-	wrqu->data.length = dev->wireless_handlers->num_private_args;
-
-	/* Copy structure to the user buffer. */
-	memcpy(extra, dev->wireless_handlers->private_args,
-	       sizeof(struct iw_priv_args) * wrqu->data.length);
-
-	return 0;
-}
-
-
-/******************** /proc/net/wireless SUPPORT ********************/
-/*
- * The /proc/net/wireless file is a human readable user-space interface
- * exporting various wireless specific statistics from the wireless devices.
- * This is the most popular part of the Wireless Extensions ;-)
- *
- * This interface is a pure clone of /proc/net/dev (in net/core/dev.c).
- * The content of the file is basically the content of "struct iw_statistics".
- */
-
-#ifdef CONFIG_PROC_FS
-
-/* ---------------------------------------------------------------- */
-/*
- * Print one entry (line) of /proc/net/wireless
- */
-static __inline__ void wireless_seq_printf_stats(struct seq_file *seq,
-						 struct net_device *dev)
-{
-	/* Get stats from the driver */
-	struct iw_statistics *stats = get_wireless_stats(dev);
-
-	if (stats) {
-		seq_printf(seq, "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d "
-				"%6d %6d   %6d\n",
-			   dev->name, stats->status, stats->qual.qual,
-			   stats->qual.updated & IW_QUAL_QUAL_UPDATED
-			   ? '.' : ' ',
-			   ((__s32) stats->qual.level) -
-			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
-			   stats->qual.updated & IW_QUAL_LEVEL_UPDATED
-			   ? '.' : ' ',
-			   ((__s32) stats->qual.noise) -
-			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
-			   stats->qual.updated & IW_QUAL_NOISE_UPDATED
-			   ? '.' : ' ',
-			   stats->discard.nwid, stats->discard.code,
-			   stats->discard.fragment, stats->discard.retries,
-			   stats->discard.misc, stats->miss.beacon);
-		stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
-	}
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Print info for /proc/net/wireless (print all entries)
- */
-static int wireless_seq_show(struct seq_file *seq, void *v)
-{
-	if (v == SEQ_START_TOKEN)
-		seq_printf(seq, "Inter-| sta-|   Quality        |   Discarded "
-				"packets               | Missed | WE\n"
-				" face | tus | link level noise |  nwid  "
-				"crypt   frag  retry   misc | beacon | %d\n",
-			   WIRELESS_EXT);
-	else
-		wireless_seq_printf_stats(seq, v);
-	return 0;
-}
-
-static struct seq_operations wireless_seq_ops = {
-	.start = dev_seq_start,
-	.next  = dev_seq_next,
-	.stop  = dev_seq_stop,
-	.show  = wireless_seq_show,
-};
-
-static int wireless_seq_open(struct inode *inode, struct file *file)
-{
-	return seq_open(file, &wireless_seq_ops);
-}
-
-static const struct file_operations wireless_seq_fops = {
-	.owner	 = THIS_MODULE,
-	.open    = wireless_seq_open,
-	.read    = seq_read,
-	.llseek  = seq_lseek,
-	.release = seq_release,
-};
-
-int __init wireless_proc_init(void)
-{
-	/* Create /proc/net/wireless entry */
-	if (!proc_net_fops_create("wireless", S_IRUGO, &wireless_seq_fops))
-		return -ENOMEM;
-
-	return 0;
-}
-#endif	/* CONFIG_PROC_FS */
-
-/************************** IOCTL SUPPORT **************************/
-/*
- * The original user space API to configure all those Wireless Extensions
- * is through IOCTLs.
- * In there, we check if we need to call the new driver API (iw_handler)
- * or just call the driver ioctl handler.
- */
-
-/* ---------------------------------------------------------------- */
-/*
- * Wrapper to call a standard Wireless Extension handler.
- * We do various checks and also take care of moving data between
- * user space and kernel space.
- */
-static int ioctl_standard_call(struct net_device *	dev,
-			       struct ifreq *		ifr,
-			       unsigned int		cmd,
-			       iw_handler		handler)
-{
-	struct iwreq *				iwr = (struct iwreq *) ifr;
-	const struct iw_ioctl_description *	descr;
-	struct iw_request_info			info;
-	int					ret = -EINVAL;
-
-	/* Get the description of the IOCTL */
-	if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
-		return -EOPNOTSUPP;
-	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
-
-#ifdef WE_IOCTL_DEBUG
-	printk(KERN_DEBUG "%s (WE) : Found standard handler for 0x%04X\n",
-	       ifr->ifr_name, cmd);
-	printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
-#endif	/* WE_IOCTL_DEBUG */
-
-	/* Prepare the call */
-	info.cmd = cmd;
-	info.flags = 0;
-
-	/* Check if we have a pointer to user space data or not */
-	if(descr->header_type != IW_HEADER_TYPE_POINT) {
-
-		/* No extra arguments. Trivial to handle */
-		ret = handler(dev, &info, &(iwr->u), NULL);
-
-#ifdef WE_SET_EVENT
-		/* Generate an event to notify listeners of the change */
-		if((descr->flags & IW_DESCR_FLAG_EVENT) &&
-		   ((ret == 0) || (ret == -EIWCOMMIT)))
-			wireless_send_event(dev, cmd, &(iwr->u), NULL);
-#endif	/* WE_SET_EVENT */
-	} else {
-		char *	extra;
-		int	extra_size;
-		int	user_length = 0;
-		int	err;
-		int	essid_compat = 0;
-
-		/* Calculate space needed by arguments. Always allocate
-		 * for max space. Easier, and won't last long... */
-		extra_size = descr->max_tokens * descr->token_size;
-
-		/* Check need for ESSID compatibility for WE < 21 */
-		switch (cmd) {
-		case SIOCSIWESSID:
-		case SIOCGIWESSID:
-		case SIOCSIWNICKN:
-		case SIOCGIWNICKN:
-			if (iwr->u.data.length == descr->max_tokens + 1)
-				essid_compat = 1;
-			else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
-				char essid[IW_ESSID_MAX_SIZE + 1];
-
-				err = copy_from_user(essid, iwr->u.data.pointer,
-						     iwr->u.data.length *
-						     descr->token_size);
-				if (err)
-					return -EFAULT;
-
-				if (essid[iwr->u.data.length - 1] == '\0')
-					essid_compat = 1;
-			}
-			break;
-		default:
-			break;
-		}
-
-		iwr->u.data.length -= essid_compat;
-
-		/* Check what user space is giving us */
-		if(IW_IS_SET(cmd)) {
-			/* Check NULL pointer */
-			if((iwr->u.data.pointer == NULL) &&
-			   (iwr->u.data.length != 0))
-				return -EFAULT;
-			/* Check if number of token fits within bounds */
-			if(iwr->u.data.length > descr->max_tokens)
-				return -E2BIG;
-			if(iwr->u.data.length < descr->min_tokens)
-				return -EINVAL;
-		} else {
-			/* Check NULL pointer */
-			if(iwr->u.data.pointer == NULL)
-				return -EFAULT;
-			/* Save user space buffer size for checking */
-			user_length = iwr->u.data.length;
-
-			/* Don't check if user_length > max to allow forward
-			 * compatibility. The test user_length < min is
-			 * implied by the test at the end. */
-
-			/* Support for very large requests */
-			if((descr->flags & IW_DESCR_FLAG_NOMAX) &&
-			   (user_length > descr->max_tokens)) {
-				/* Allow userspace to GET more than max so
-				 * we can support any size GET requests.
-				 * There is still a limit : -ENOMEM. */
-				extra_size = user_length * descr->token_size;
-				/* Note : user_length is originally a __u16,
-				 * and token_size is controlled by us,
-				 * so extra_size won't get negative and
-				 * won't overflow... */
-			}
-		}
-
-#ifdef WE_IOCTL_DEBUG
-		printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
-		       dev->name, extra_size);
-#endif	/* WE_IOCTL_DEBUG */
-
-		/* Create the kernel buffer */
-		/*    kzalloc ensures NULL-termination for essid_compat */
-		extra = kzalloc(extra_size, GFP_KERNEL);
-		if (extra == NULL) {
-			return -ENOMEM;
-		}
-
-		/* If it is a SET, get all the extra data in here */
-		if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
-			err = copy_from_user(extra, iwr->u.data.pointer,
-					     iwr->u.data.length *
-					     descr->token_size);
-			if (err) {
-				kfree(extra);
-				return -EFAULT;
-			}
-#ifdef WE_IOCTL_DEBUG
-			printk(KERN_DEBUG "%s (WE) : Got %d bytes\n",
-			       dev->name,
-			       iwr->u.data.length * descr->token_size);
-#endif	/* WE_IOCTL_DEBUG */
-		}
-
-		/* Call the handler */
-		ret = handler(dev, &info, &(iwr->u), extra);
-
-		iwr->u.data.length += essid_compat;
-
-		/* If we have something to return to the user */
-		if (!ret && IW_IS_GET(cmd)) {
-			/* Check if there is enough buffer up there */
-			if(user_length < iwr->u.data.length) {
-				kfree(extra);
-				return -E2BIG;
-			}
-
-			err = copy_to_user(iwr->u.data.pointer, extra,
-					   iwr->u.data.length *
-					   descr->token_size);
-			if (err)
-				ret =  -EFAULT;
-#ifdef WE_IOCTL_DEBUG
-			printk(KERN_DEBUG "%s (WE) : Wrote %d bytes\n",
-			       dev->name,
-			       iwr->u.data.length * descr->token_size);
-#endif	/* WE_IOCTL_DEBUG */
-		}
-
-#ifdef WE_SET_EVENT
-		/* Generate an event to notify listeners of the change */
-		if((descr->flags & IW_DESCR_FLAG_EVENT) &&
-		   ((ret == 0) || (ret == -EIWCOMMIT))) {
-			if(descr->flags & IW_DESCR_FLAG_RESTRICT)
-				/* If the event is restricted, don't
-				 * export the payload */
-				wireless_send_event(dev, cmd, &(iwr->u), NULL);
-			else
-				wireless_send_event(dev, cmd, &(iwr->u),
-						    extra);
-		}
-#endif	/* WE_SET_EVENT */
-
-		/* Cleanup - I told you it wasn't that long ;-) */
-		kfree(extra);
-	}
-
-	/* Call commit handler if needed and defined */
-	if(ret == -EIWCOMMIT)
-		ret = call_commit_handler(dev);
-
-	/* Here, we will generate the appropriate event if needed */
-
-	return ret;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Wrapper to call a private Wireless Extension handler.
- * We do various checks and also take care of moving data between
- * user space and kernel space.
- * It's not as nice and slimline as the standard wrapper. The cause
- * is struct iw_priv_args, which was not really designed for the
- * job we are going here.
- *
- * IMPORTANT : This function prevent to set and get data on the same
- * IOCTL and enforce the SET/GET convention. Not doing it would be
- * far too hairy...
- * If you need to set and get data at the same time, please don't use
- * a iw_handler but process it in your ioctl handler (i.e. use the
- * old driver API).
- */
-static inline int ioctl_private_call(struct net_device *	dev,
-				     struct ifreq *		ifr,
-				     unsigned int		cmd,
-				     iw_handler		handler)
-{
-	struct iwreq *			iwr = (struct iwreq *) ifr;
-	const struct iw_priv_args *	descr = NULL;
-	struct iw_request_info		info;
-	int				extra_size = 0;
-	int				i;
-	int				ret = -EINVAL;
-
-	/* Get the description of the IOCTL */
-	for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
-		if(cmd == dev->wireless_handlers->private_args[i].cmd) {
-			descr = &(dev->wireless_handlers->private_args[i]);
-			break;
-		}
-
-#ifdef WE_IOCTL_DEBUG
-	printk(KERN_DEBUG "%s (WE) : Found private handler for 0x%04X\n",
-	       ifr->ifr_name, cmd);
-	if(descr) {
-		printk(KERN_DEBUG "%s (WE) : Name %s, set %X, get %X\n",
-		       dev->name, descr->name,
-		       descr->set_args, descr->get_args);
-	}
-#endif	/* WE_IOCTL_DEBUG */
-
-	/* Compute the size of the set/get arguments */
-	if(descr != NULL) {
-		if(IW_IS_SET(cmd)) {
-			int	offset = 0;	/* For sub-ioctls */
-			/* Check for sub-ioctl handler */
-			if(descr->name[0] == '\0')
-				/* Reserve one int for sub-ioctl index */
-				offset = sizeof(__u32);
-
-			/* Size of set arguments */
-			extra_size = get_priv_size(descr->set_args);
-
-			/* Does it fits in iwr ? */
-			if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
-			   ((extra_size + offset) <= IFNAMSIZ))
-				extra_size = 0;
-		} else {
-			/* Size of get arguments */
-			extra_size = get_priv_size(descr->get_args);
-
-			/* Does it fits in iwr ? */
-			if((descr->get_args & IW_PRIV_SIZE_FIXED) &&
-			   (extra_size <= IFNAMSIZ))
-				extra_size = 0;
-		}
-	}
-
-	/* Prepare the call */
-	info.cmd = cmd;
-	info.flags = 0;
-
-	/* Check if we have a pointer to user space data or not. */
-	if(extra_size == 0) {
-		/* No extra arguments. Trivial to handle */
-		ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u));
-	} else {
-		char *	extra;
-		int	err;
-
-		/* Check what user space is giving us */
-		if(IW_IS_SET(cmd)) {
-			/* Check NULL pointer */
-			if((iwr->u.data.pointer == NULL) &&
-			   (iwr->u.data.length != 0))
-				return -EFAULT;
-
-			/* Does it fits within bounds ? */
-			if(iwr->u.data.length > (descr->set_args &
-						 IW_PRIV_SIZE_MASK))
-				return -E2BIG;
-		} else {
-			/* Check NULL pointer */
-			if(iwr->u.data.pointer == NULL)
-				return -EFAULT;
-		}
-
-#ifdef WE_IOCTL_DEBUG
-		printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
-		       dev->name, extra_size);
-#endif	/* WE_IOCTL_DEBUG */
-
-		/* Always allocate for max space. Easier, and won't last
-		 * long... */
-		extra = kmalloc(extra_size, GFP_KERNEL);
-		if (extra == NULL) {
-			return -ENOMEM;
-		}
-
-		/* If it is a SET, get all the extra data in here */
-		if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
-			err = copy_from_user(extra, iwr->u.data.pointer,
-					     extra_size);
-			if (err) {
-				kfree(extra);
-				return -EFAULT;
-			}
-#ifdef WE_IOCTL_DEBUG
-			printk(KERN_DEBUG "%s (WE) : Got %d elem\n",
-			       dev->name, iwr->u.data.length);
-#endif	/* WE_IOCTL_DEBUG */
-		}
-
-		/* Call the handler */
-		ret = handler(dev, &info, &(iwr->u), extra);
-
-		/* If we have something to return to the user */
-		if (!ret && IW_IS_GET(cmd)) {
-
-			/* Adjust for the actual length if it's variable,
-			 * avoid leaking kernel bits outside. */
-			if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) {
-				extra_size = adjust_priv_size(descr->get_args,
-							      &(iwr->u));
-			}
-
-			err = copy_to_user(iwr->u.data.pointer, extra,
-					   extra_size);
-			if (err)
-				ret =  -EFAULT;
-#ifdef WE_IOCTL_DEBUG
-			printk(KERN_DEBUG "%s (WE) : Wrote %d elem\n",
-			       dev->name, iwr->u.data.length);
-#endif	/* WE_IOCTL_DEBUG */
-		}
-
-		/* Cleanup - I told you it wasn't that long ;-) */
-		kfree(extra);
-	}
-
-
-	/* Call commit handler if needed and defined */
-	if(ret == -EIWCOMMIT)
-		ret = call_commit_handler(dev);
-
-	return ret;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Main IOCTl dispatcher. Called from the main networking code
- * (dev_ioctl() in net/core/dev.c).
- * Check the type of IOCTL and call the appropriate wrapper...
- */
-int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
-{
-	struct net_device *dev;
-	iw_handler	handler;
-
-	/* Permissions are already checked in dev_ioctl() before calling us.
-	 * The copy_to/from_user() of ifr is also dealt with in there */
-
-	/* Make sure the device exist */
-	if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL)
-		return -ENODEV;
-
-	/* A bunch of special cases, then the generic case...
-	 * Note that 'cmd' is already filtered in dev_ioctl() with
-	 * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
-	switch(cmd)
-	{
-		case SIOCGIWSTATS:
-			/* Get Wireless Stats */
-			return ioctl_standard_call(dev,
-						   ifr,
-						   cmd,
-						   &iw_handler_get_iwstats);
-
-		case SIOCGIWPRIV:
-			/* Check if we have some wireless handlers defined */
-			if(dev->wireless_handlers != NULL) {
-				/* We export to user space the definition of
-				 * the private handler ourselves */
-				return ioctl_standard_call(dev,
-							   ifr,
-							   cmd,
-							   &iw_handler_get_private);
-			}
-			// ## Fall-through for old API ##
-		default:
-			/* Generic IOCTL */
-			/* Basic check */
-			if (!netif_device_present(dev))
-				return -ENODEV;
-			/* New driver API : try to find the handler */
-			handler = get_handler(dev, cmd);
-			if(handler != NULL) {
-				/* Standard and private are not the same */
-				if(cmd < SIOCIWFIRSTPRIV)
-					return ioctl_standard_call(dev,
-								   ifr,
-								   cmd,
-								   handler);
-				else
-					return ioctl_private_call(dev,
-								  ifr,
-								  cmd,
-								  handler);
-			}
-			/* Old driver API : call driver ioctl handler */
-			if (dev->do_ioctl) {
-				return dev->do_ioctl(dev, ifr, cmd);
-			}
-			return -EOPNOTSUPP;
-	}
-	/* Not reached */
-	return -EINVAL;
-}
-
-/********************** RTNETLINK REQUEST API **********************/
-/*
- * The alternate user space API to configure all those Wireless Extensions
- * is through RtNetlink.
- * This API support only the new driver API (iw_handler).
- *
- * This RtNetlink API use the same query/reply model as the ioctl API.
- * Maximum effort has been done to fit in the RtNetlink model, and
- * we support both RtNetlink Set and RtNelink Get operations.
- * On the other hand, we don't offer Dump operations because of the
- * following reasons :
- *	o Large number of parameters, most optional
- *	o Large size of some parameters (> 100 bytes)
- *	o Each parameters need to be extracted from hardware
- *	o Scan requests can take seconds and disable network activity.
- * Because of this high cost/overhead, we want to return only the
- * parameters the user application is really interested in.
- * We could offer partial Dump using the IW_DESCR_FLAG_DUMP flag.
- *
- * The API uses the standard RtNetlink socket. When the RtNetlink code
- * find a IFLA_WIRELESS field in a RtNetlink SET_LINK request,
- * it calls here.
- */
-
-#ifdef CONFIG_NET_WIRELESS_RTNETLINK
-/* ---------------------------------------------------------------- */
-/*
- * Wrapper to call a standard Wireless Extension GET handler.
- * We do various checks and call the handler with the proper args.
- */
-static int rtnetlink_standard_get(struct net_device *	dev,
-				  struct iw_event *	request,
-				  int			request_len,
-				  iw_handler		handler,
-				  char **		p_buf,
-				  int *			p_len)
-{
-	const struct iw_ioctl_description *	descr = NULL;
-	unsigned int				cmd;
-	union iwreq_data *			wrqu;
-	int					hdr_len;
-	struct iw_request_info			info;
-	char *					buffer = NULL;
-	int					buffer_size = 0;
-	int					ret = -EINVAL;
-
-	/* Get the description of the Request */
-	cmd = request->cmd;
-	if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
-		return -EOPNOTSUPP;
-	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
-
-#ifdef WE_RTNETLINK_DEBUG
-	printk(KERN_DEBUG "%s (WE.r) : Found standard handler for 0x%04X\n",
-	       dev->name, cmd);
-	printk(KERN_DEBUG "%s (WE.r) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
-#endif	/* WE_RTNETLINK_DEBUG */
-
-	/* Check if wrqu is complete */
-	hdr_len = event_type_size[descr->header_type];
-	if(request_len < hdr_len) {
-#ifdef WE_RTNETLINK_DEBUG
-		printk(KERN_DEBUG
-		       "%s (WE.r) : Wireless request too short (%d)\n",
-		       dev->name, request_len);
-#endif	/* WE_RTNETLINK_DEBUG */
-		return -EINVAL;
-	}
-
-	/* Prepare the call */
-	info.cmd = cmd;
-	info.flags = 0;
-
-	/* Check if we have extra data in the reply or not */
-	if(descr->header_type != IW_HEADER_TYPE_POINT) {
-
-		/* Create the kernel buffer that we will return.
-		 * It's at an offset to match the TYPE_POINT case... */
-		buffer_size = request_len + IW_EV_POINT_OFF;
-		buffer = kmalloc(buffer_size, GFP_KERNEL);
-		if (buffer == NULL) {
-			return -ENOMEM;
-		}
-		/* Copy event data */
-		memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
-		/* Use our own copy of wrqu */
-		wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
-					     + IW_EV_LCP_PK_LEN);
-
-		/* No extra arguments. Trivial to handle */
-		ret = handler(dev, &info, wrqu, NULL);
-
-	} else {
-		union iwreq_data	wrqu_point;
-		char *			extra = NULL;
-		int			extra_size = 0;
-
-		/* Get a temp copy of wrqu (skip pointer) */
-		memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
-		       ((char *) request) + IW_EV_LCP_PK_LEN,
-		       IW_EV_POINT_LEN - IW_EV_LCP_PK_LEN);
-
-		/* Calculate space needed by arguments. Always allocate
-		 * for max space. Easier, and won't last long... */
-		extra_size = descr->max_tokens * descr->token_size;
-		/* Support for very large requests */
-		if((descr->flags & IW_DESCR_FLAG_NOMAX) &&
-		   (wrqu_point.data.length > descr->max_tokens))
-			extra_size = (wrqu_point.data.length
-				      * descr->token_size);
-		buffer_size = extra_size + IW_EV_POINT_PK_LEN + IW_EV_POINT_OFF;
-#ifdef WE_RTNETLINK_DEBUG
-		printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
-		       dev->name, extra_size, buffer_size);
-#endif	/* WE_RTNETLINK_DEBUG */
-
-		/* Create the kernel buffer that we will return */
-		buffer = kmalloc(buffer_size, GFP_KERNEL);
-		if (buffer == NULL) {
-			return -ENOMEM;
-		}
-
-		/* Put wrqu in the right place (just before extra).
-		 * Leave space for IWE header and dummy pointer...
-		 * Note that IW_EV_LCP_PK_LEN==4 bytes, so it's still aligned.
-		 */
-		memcpy(buffer + IW_EV_LCP_PK_LEN + IW_EV_POINT_OFF,
-		       ((char *) &wrqu_point) + IW_EV_POINT_OFF,
-		       IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
-		wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_PK_LEN);
-
-		/* Extra comes logically after that. Offset +12 bytes. */
-		extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_PK_LEN;
-
-		/* Call the handler */
-		ret = handler(dev, &info, wrqu, extra);
-
-		/* Calculate real returned length */
-		extra_size = (wrqu->data.length * descr->token_size);
-		/* Re-adjust reply size */
-		request->len = extra_size + IW_EV_POINT_PK_LEN;
-
-		/* Put the iwe header where it should, i.e. scrap the
-		 * dummy pointer. */
-		memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_PK_LEN);
-
-#ifdef WE_RTNETLINK_DEBUG
-		printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size);
-#endif	/* WE_RTNETLINK_DEBUG */
-
-		/* Check if there is enough buffer up there */
-		if(wrqu_point.data.length < wrqu->data.length)
-			ret = -E2BIG;
-	}
-
-	/* Return the buffer to the caller */
-	if (!ret) {
-		*p_buf = buffer;
-		*p_len = request->len;
-	} else {
-		/* Cleanup */
-		if(buffer)
-			kfree(buffer);
-	}
-
-	return ret;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Wrapper to call a standard Wireless Extension SET handler.
- * We do various checks and call the handler with the proper args.
- */
-static inline int rtnetlink_standard_set(struct net_device *	dev,
-					 struct iw_event *	request,
-					 int			request_len,
-					 iw_handler		handler)
-{
-	const struct iw_ioctl_description *	descr = NULL;
-	unsigned int				cmd;
-	union iwreq_data *			wrqu;
-	union iwreq_data			wrqu_point;
-	int					hdr_len;
-	char *					extra = NULL;
-	int					extra_size = 0;
-	struct iw_request_info			info;
-	int					ret = -EINVAL;
-
-	/* Get the description of the Request */
-	cmd = request->cmd;
-	if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
-		return -EOPNOTSUPP;
-	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
-
-#ifdef WE_RTNETLINK_DEBUG
-	printk(KERN_DEBUG "%s (WE.r) : Found standard SET handler for 0x%04X\n",
-	       dev->name, cmd);
-	printk(KERN_DEBUG "%s (WE.r) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
-#endif	/* WE_RTNETLINK_DEBUG */
-
-	/* Extract fixed header from request. This is properly aligned. */
-	wrqu = (union iwreq_data *) (((char *) request) + IW_EV_LCP_PK_LEN);
-
-	/* Check if wrqu is complete */
-	hdr_len = event_type_pk_size[descr->header_type];
-	if(request_len < hdr_len) {
-#ifdef WE_RTNETLINK_DEBUG
-		printk(KERN_DEBUG
-		       "%s (WE.r) : Wireless request too short (%d)\n",
-		       dev->name, request_len);
-#endif	/* WE_RTNETLINK_DEBUG */
-		return -EINVAL;
-	}
-
-	/* Prepare the call */
-	info.cmd = cmd;
-	info.flags = 0;
-
-	/* Check if we have extra data in the request or not */
-	if(descr->header_type != IW_HEADER_TYPE_POINT) {
-
-		/* No extra arguments. Trivial to handle */
-		ret = handler(dev, &info, wrqu, NULL);
-
-	} else {
-		int	extra_len;
-
-		/* Put wrqu in the right place (skip pointer) */
-		memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
-		       wrqu, IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
-		/* Don't forget about the event code... */
-		wrqu = &wrqu_point;
-
-		/* Check if number of token fits within bounds */
-		if(wrqu_point.data.length > descr->max_tokens)
-			return -E2BIG;
-		if(wrqu_point.data.length < descr->min_tokens)
-			return -EINVAL;
-
-		/* Real length of payload */
-		extra_len = wrqu_point.data.length * descr->token_size;
-
-		/* Check if request is self consistent */
-		if((request_len - hdr_len) < extra_len) {
-#ifdef WE_RTNETLINK_DEBUG
-			printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n",
-			       dev->name, extra_size);
-#endif	/* WE_RTNETLINK_DEBUG */
-			return -EINVAL;
-		}
-
-#ifdef WE_RTNETLINK_DEBUG
-		printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n",
-		       dev->name, extra_size);
-#endif	/* WE_RTNETLINK_DEBUG */
-
-		/* Always allocate for max space. Easier, and won't last
-		 * long... */
-		extra_size = descr->max_tokens * descr->token_size;
-		extra = kmalloc(extra_size, GFP_KERNEL);
-		if (extra == NULL)
-			return -ENOMEM;
-
-		/* Copy extra in aligned buffer */
-		memcpy(extra, ((char *) request) + hdr_len, extra_len);
-
-		/* Call the handler */
-		ret = handler(dev, &info, &wrqu_point, extra);
-	}
-
-#ifdef WE_SET_EVENT
-	/* Generate an event to notify listeners of the change */
-	if((descr->flags & IW_DESCR_FLAG_EVENT) &&
-	   ((ret == 0) || (ret == -EIWCOMMIT))) {
-		if(descr->flags & IW_DESCR_FLAG_RESTRICT)
-			/* If the event is restricted, don't
-			 * export the payload */
-			wireless_send_event(dev, cmd, wrqu, NULL);
-		else
-			wireless_send_event(dev, cmd, wrqu, extra);
-	}
-#endif	/* WE_SET_EVENT */
-
-	/* Cleanup - I told you it wasn't that long ;-) */
-	if(extra)
-		kfree(extra);
-
-	/* Call commit handler if needed and defined */
-	if(ret == -EIWCOMMIT)
-		ret = call_commit_handler(dev);
-
-	return ret;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Wrapper to call a private Wireless Extension GET handler.
- * Same as above...
- * It's not as nice and slimline as the standard wrapper. The cause
- * is struct iw_priv_args, which was not really designed for the
- * job we are going here.
- *
- * IMPORTANT : This function prevent to set and get data on the same
- * IOCTL and enforce the SET/GET convention. Not doing it would be
- * far too hairy...
- * If you need to set and get data at the same time, please don't use
- * a iw_handler but process it in your ioctl handler (i.e. use the
- * old driver API).
- */
-static inline int rtnetlink_private_get(struct net_device *	dev,
-					struct iw_event *	request,
-					int			request_len,
-					iw_handler		handler,
-					char **			p_buf,
-					int *			p_len)
-{
-	const struct iw_priv_args *	descr = NULL;
-	unsigned int			cmd;
-	union iwreq_data *		wrqu;
-	int				hdr_len;
-	struct iw_request_info		info;
-	int				extra_size = 0;
-	int				i;
-	char *				buffer = NULL;
-	int				buffer_size = 0;
-	int				ret = -EINVAL;
-
-	/* Get the description of the Request */
-	cmd = request->cmd;
-	for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
-		if(cmd == dev->wireless_handlers->private_args[i].cmd) {
-			descr = &(dev->wireless_handlers->private_args[i]);
-			break;
-		}
-	if(descr == NULL)
-		return -EOPNOTSUPP;
-
-#ifdef WE_RTNETLINK_DEBUG
-	printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n",
-	       dev->name, cmd);
-	printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n",
-	       dev->name, descr->name, descr->set_args, descr->get_args);
-#endif	/* WE_RTNETLINK_DEBUG */
-
-	/* Compute the max size of the get arguments */
-	extra_size = get_priv_size(descr->get_args);
-
-	/* Does it fits in wrqu ? */
-	if((descr->get_args & IW_PRIV_SIZE_FIXED) &&
-	   (extra_size <= IFNAMSIZ)) {
-		hdr_len = extra_size;
-		extra_size = 0;
-	} else {
-		hdr_len = IW_EV_POINT_PK_LEN;
-	}
-
-	/* Check if wrqu is complete */
-	if(request_len < hdr_len) {
-#ifdef WE_RTNETLINK_DEBUG
-		printk(KERN_DEBUG
-		       "%s (WE.r) : Wireless request too short (%d)\n",
-		       dev->name, request_len);
-#endif	/* WE_RTNETLINK_DEBUG */
-		return -EINVAL;
-	}
-
-	/* Prepare the call */
-	info.cmd = cmd;
-	info.flags = 0;
-
-	/* Check if we have a pointer to user space data or not. */
-	if(extra_size == 0) {
-
-		/* Create the kernel buffer that we will return.
-		 * It's at an offset to match the TYPE_POINT case... */
-		buffer_size = request_len + IW_EV_POINT_OFF;
-		buffer = kmalloc(buffer_size, GFP_KERNEL);
-		if (buffer == NULL) {
-			return -ENOMEM;
-		}
-		/* Copy event data */
-		memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
-		/* Use our own copy of wrqu */
-		wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
-					     + IW_EV_LCP_PK_LEN);
-
-		/* No extra arguments. Trivial to handle */
-		ret = handler(dev, &info, wrqu, (char *) wrqu);
-
-	} else {
-		char *	extra;
-
-		/* Buffer for full reply */
-		buffer_size = extra_size + IW_EV_POINT_PK_LEN + IW_EV_POINT_OFF;
-
-#ifdef WE_RTNETLINK_DEBUG
-		printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
-		       dev->name, extra_size, buffer_size);
-#endif	/* WE_RTNETLINK_DEBUG */
-
-		/* Create the kernel buffer that we will return */
-		buffer = kmalloc(buffer_size, GFP_KERNEL);
-		if (buffer == NULL) {
-			return -ENOMEM;
-		}
-
-		/* Put wrqu in the right place (just before extra).
-		 * Leave space for IWE header and dummy pointer...
-		 * Note that IW_EV_LCP_PK_LEN==4 bytes, so it's still aligned.
-		 */
-		memcpy(buffer + IW_EV_LCP_PK_LEN + IW_EV_POINT_OFF,
-		       ((char *) request) + IW_EV_LCP_PK_LEN,
-		       IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
-		wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_PK_LEN);
-
-		/* Extra comes logically after that. Offset +12 bytes. */
-		extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_PK_LEN;
-
-		/* Call the handler */
-		ret = handler(dev, &info, wrqu, extra);
-
-		/* Adjust for the actual length if it's variable,
-		 * avoid leaking kernel bits outside. */
-		if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
-			extra_size = adjust_priv_size(descr->get_args, wrqu);
-		/* Re-adjust reply size */
-		request->len = extra_size + IW_EV_POINT_PK_LEN;
-
-		/* Put the iwe header where it should, i.e. scrap the
-		 * dummy pointer. */
-		memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_PK_LEN);
-
-#ifdef WE_RTNETLINK_DEBUG
-		printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size);
-#endif	/* WE_RTNETLINK_DEBUG */
-	}
-
-	/* Return the buffer to the caller */
-	if (!ret) {
-		*p_buf = buffer;
-		*p_len = request->len;
-	} else {
-		/* Cleanup */
-		if(buffer)
-			kfree(buffer);
-	}
-
-	return ret;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Wrapper to call a private Wireless Extension SET handler.
- * Same as above...
- * It's not as nice and slimline as the standard wrapper. The cause
- * is struct iw_priv_args, which was not really designed for the
- * job we are going here.
- *
- * IMPORTANT : This function prevent to set and get data on the same
- * IOCTL and enforce the SET/GET convention. Not doing it would be
- * far too hairy...
- * If you need to set and get data at the same time, please don't use
- * a iw_handler but process it in your ioctl handler (i.e. use the
- * old driver API).
- */
-static inline int rtnetlink_private_set(struct net_device *	dev,
-					struct iw_event *	request,
-					int			request_len,
-					iw_handler		handler)
-{
-	const struct iw_priv_args *	descr = NULL;
-	unsigned int			cmd;
-	union iwreq_data *		wrqu;
-	union iwreq_data		wrqu_point;
-	int				hdr_len;
-	char *				extra = NULL;
-	int				extra_size = 0;
-	int				offset = 0;	/* For sub-ioctls */
-	struct iw_request_info		info;
-	int				i;
-	int				ret = -EINVAL;
-
-	/* Get the description of the Request */
-	cmd = request->cmd;
-	for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
-		if(cmd == dev->wireless_handlers->private_args[i].cmd) {
-			descr = &(dev->wireless_handlers->private_args[i]);
-			break;
-		}
-	if(descr == NULL)
-		return -EOPNOTSUPP;
-
-#ifdef WE_RTNETLINK_DEBUG
-	printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n",
-	       ifr->ifr_name, cmd);
-	printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n",
-	       dev->name, descr->name, descr->set_args, descr->get_args);
-#endif	/* WE_RTNETLINK_DEBUG */
-
-	/* Compute the size of the set arguments */
-	/* Check for sub-ioctl handler */
-	if(descr->name[0] == '\0')
-		/* Reserve one int for sub-ioctl index */
-		offset = sizeof(__u32);
-
-	/* Size of set arguments */
-	extra_size = get_priv_size(descr->set_args);
-
-	/* Does it fits in wrqu ? */
-	if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
-	   (extra_size <= IFNAMSIZ)) {
-		hdr_len = IW_EV_LCP_PK_LEN + extra_size;
-		extra_size = 0;
-	} else {
-		hdr_len = IW_EV_POINT_PK_LEN;
-	}
-
-	/* Extract fixed header from request. This is properly aligned. */
-	wrqu = (union iwreq_data *) (((char *) request) + IW_EV_LCP_PK_LEN);
-
-	/* Check if wrqu is complete */
-	if(request_len < hdr_len) {
-#ifdef WE_RTNETLINK_DEBUG
-		printk(KERN_DEBUG
-		       "%s (WE.r) : Wireless request too short (%d)\n",
-		       dev->name, request_len);
-#endif	/* WE_RTNETLINK_DEBUG */
-		return -EINVAL;
-	}
-
-	/* Prepare the call */
-	info.cmd = cmd;
-	info.flags = 0;
-
-	/* Check if we have a pointer to user space data or not. */
-	if(extra_size == 0) {
-
-		/* No extra arguments. Trivial to handle */
-		ret = handler(dev, &info, wrqu, (char *) wrqu);
-
-	} else {
-		int	extra_len;
-
-		/* Put wrqu in the right place (skip pointer) */
-		memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
-		       wrqu, IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
-
-		/* Does it fits within bounds ? */
-		if(wrqu_point.data.length > (descr->set_args &
-					     IW_PRIV_SIZE_MASK))
-			return -E2BIG;
-
-		/* Real length of payload */
-		extra_len = adjust_priv_size(descr->set_args, &wrqu_point);
-
-		/* Check if request is self consistent */
-		if((request_len - hdr_len) < extra_len) {
-#ifdef WE_RTNETLINK_DEBUG
-			printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n",
-			       dev->name, extra_size);
-#endif	/* WE_RTNETLINK_DEBUG */
-			return -EINVAL;
-		}
-
-#ifdef WE_RTNETLINK_DEBUG
-		printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n",
-		       dev->name, extra_size);
-#endif	/* WE_RTNETLINK_DEBUG */
-
-		/* Always allocate for max space. Easier, and won't last
-		 * long... */
-		extra = kmalloc(extra_size, GFP_KERNEL);
-		if (extra == NULL)
-			return -ENOMEM;
-
-		/* Copy extra in aligned buffer */
-		memcpy(extra, ((char *) request) + hdr_len, extra_len);
-
-		/* Call the handler */
-		ret = handler(dev, &info, &wrqu_point, extra);
-
-		/* Cleanup - I told you it wasn't that long ;-) */
-		kfree(extra);
-	}
-
-	/* Call commit handler if needed and defined */
-	if(ret == -EIWCOMMIT)
-		ret = call_commit_handler(dev);
-
-	return ret;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Main RtNetlink dispatcher. Called from the main networking code
- * (do_getlink() in net/core/rtnetlink.c).
- * Check the type of Request and call the appropriate wrapper...
- */
-int wireless_rtnetlink_get(struct net_device *	dev,
-			   char *		data,
-			   int			len,
-			   char **		p_buf,
-			   int *		p_len)
-{
-	struct iw_event *	request = (struct iw_event *) data;
-	iw_handler		handler;
-
-	/* Check length */
-	if(len < IW_EV_LCP_PK_LEN) {
-		printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
-		       dev->name, len);
-		return -EINVAL;
-	}
-
-	/* ReCheck length (len may have padding) */
-	if(request->len > len) {
-		printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
-		       dev->name, request->len, len);
-		return -EINVAL;
-	}
-
-	/* Only accept GET requests in here */
-	if(!IW_IS_GET(request->cmd))
-		return -EOPNOTSUPP;
-
-	/* If command is `get the encoding parameters', check if
-	 * the user has the right to do it */
-	if (request->cmd == SIOCGIWENCODE ||
-	    request->cmd == SIOCGIWENCODEEXT) {
-		if (!capable(CAP_NET_ADMIN))
-			return -EPERM;
-	}
-
-	/* Special cases */
-	if(request->cmd == SIOCGIWSTATS)
-		/* Get Wireless Stats */
-		return rtnetlink_standard_get(dev,
-					      request,
-					      request->len,
-					      &iw_handler_get_iwstats,
-					      p_buf, p_len);
-	if(request->cmd == SIOCGIWPRIV) {
-		/* Check if we have some wireless handlers defined */
-		if(dev->wireless_handlers == NULL)
-			return -EOPNOTSUPP;
-		/* Get Wireless Stats */
-		return rtnetlink_standard_get(dev,
-					      request,
-					      request->len,
-					      &iw_handler_get_private,
-					      p_buf, p_len);
-	}
-
-	/* Basic check */
-	if (!netif_device_present(dev))
-		return -ENODEV;
-
-	/* Try to find the handler */
-	handler = get_handler(dev, request->cmd);
-	if(handler != NULL) {
-		/* Standard and private are not the same */
-		if(request->cmd < SIOCIWFIRSTPRIV)
-			return rtnetlink_standard_get(dev,
-						      request,
-						      request->len,
-						      handler,
-						      p_buf, p_len);
-		else
-			return rtnetlink_private_get(dev,
-						     request,
-						     request->len,
-						     handler,
-						     p_buf, p_len);
-	}
-
-	return -EOPNOTSUPP;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Main RtNetlink dispatcher. Called from the main networking code
- * (do_setlink() in net/core/rtnetlink.c).
- * Check the type of Request and call the appropriate wrapper...
- */
-int wireless_rtnetlink_set(struct net_device *	dev,
-			   char *		data,
-			   int			len)
-{
-	struct iw_event *	request = (struct iw_event *) data;
-	iw_handler		handler;
-
-	/* Check length */
-	if(len < IW_EV_LCP_PK_LEN) {
-		printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
-		       dev->name, len);
-		return -EINVAL;
-	}
-
-	/* ReCheck length (len may have padding) */
-	if(request->len > len) {
-		printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
-		       dev->name, request->len, len);
-		return -EINVAL;
-	}
-
-	/* Only accept SET requests in here */
-	if(!IW_IS_SET(request->cmd))
-		return -EOPNOTSUPP;
-
-	/* Basic check */
-	if (!netif_device_present(dev))
-		return -ENODEV;
-
-	/* New driver API : try to find the handler */
-	handler = get_handler(dev, request->cmd);
-	if(handler != NULL) {
-		/* Standard and private are not the same */
-		if(request->cmd < SIOCIWFIRSTPRIV)
-			return rtnetlink_standard_set(dev,
-						      request,
-						      request->len,
-						      handler);
-		else
-			return rtnetlink_private_set(dev,
-						     request,
-						     request->len,
-						     handler);
-	}
-
-	return -EOPNOTSUPP;
-}
-#endif	/* CONFIG_NET_WIRELESS_RTNETLINK */
-
-
-/************************* EVENT PROCESSING *************************/
-/*
- * Process events generated by the wireless layer or the driver.
- * Most often, the event will be propagated through rtnetlink
- */
-
-#ifdef WE_EVENT_RTNETLINK
-/* ---------------------------------------------------------------- */
-/*
- * Locking...
- * ----------
- *
- * Thanks to Herbert Xu <herbert@gondor.apana.org.au> for fixing
- * the locking issue in here and implementing this code !
- *
- * The issue : wireless_send_event() is often called in interrupt context,
- * while the Netlink layer can never be called in interrupt context.
- * The fully formed RtNetlink events are queued, and then a tasklet is run
- * to feed those to Netlink.
- * The skb_queue is interrupt safe, and its lock is not held while calling
- * Netlink, so there is no possibility of dealock.
- * Jean II
- */
-
-static struct sk_buff_head wireless_nlevent_queue;
-
-static int __init wireless_nlevent_init(void)
-{
-	skb_queue_head_init(&wireless_nlevent_queue);
-	return 0;
-}
-
-subsys_initcall(wireless_nlevent_init);
-
-static void wireless_nlevent_process(unsigned long data)
-{
-	struct sk_buff *skb;
-
-	while ((skb = skb_dequeue(&wireless_nlevent_queue)))
-		rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
-}
-
-static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0);
-
-/* ---------------------------------------------------------------- */
-/*
- * Fill a rtnetlink message with our event data.
- * Note that we propage only the specified event and don't dump the
- * current wireless config. Dumping the wireless config is far too
- * expensive (for each parameter, the driver need to query the hardware).
- */
-static inline int rtnetlink_fill_iwinfo(struct sk_buff *	skb,
-					struct net_device *	dev,
-					int			type,
-					char *			event,
-					int			event_len)
-{
-	struct ifinfomsg *r;
-	struct nlmsghdr  *nlh;
-	unsigned char	 *b = skb->tail;
-
-	nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r));
-	r = NLMSG_DATA(nlh);
-	r->ifi_family = AF_UNSPEC;
-	r->__ifi_pad = 0;
-	r->ifi_type = dev->type;
-	r->ifi_index = dev->ifindex;
-	r->ifi_flags = dev_get_flags(dev);
-	r->ifi_change = 0;	/* Wireless changes don't affect those flags */
-
-	/* Add the wireless events in the netlink packet */
-	RTA_PUT(skb, IFLA_WIRELESS, event_len, event);
-
-	nlh->nlmsg_len = skb->tail - b;
-	return skb->len;
-
-nlmsg_failure:
-rtattr_failure:
-	skb_trim(skb, b - skb->data);
-	return -1;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Create and broadcast and send it on the standard rtnetlink socket
- * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c
- * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field
- * within a RTM_NEWLINK event.
- */
-static inline void rtmsg_iwinfo(struct net_device *	dev,
-				char *			event,
-				int			event_len)
-{
-	struct sk_buff *skb;
-	int size = NLMSG_GOODSIZE;
-
-	skb = alloc_skb(size, GFP_ATOMIC);
-	if (!skb)
-		return;
-
-	if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK,
-				  event, event_len) < 0) {
-		kfree_skb(skb);
-		return;
-	}
-	NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
-	skb_queue_tail(&wireless_nlevent_queue, skb);
-	tasklet_schedule(&wireless_nlevent_tasklet);
-}
-
-#endif	/* WE_EVENT_RTNETLINK */
-
-/* ---------------------------------------------------------------- */
-/*
- * Main event dispatcher. Called from other parts and drivers.
- * Send the event on the appropriate channels.
- * May be called from interrupt context.
- */
-void wireless_send_event(struct net_device *	dev,
-			 unsigned int		cmd,
-			 union iwreq_data *	wrqu,
-			 char *			extra)
-{
-	const struct iw_ioctl_description *	descr = NULL;
-	int extra_len = 0;
-	struct iw_event  *event;		/* Mallocated whole event */
-	int event_len;				/* Its size */
-	int hdr_len;				/* Size of the event header */
-	int wrqu_off = 0;			/* Offset in wrqu */
-	/* Don't "optimise" the following variable, it will crash */
-	unsigned	cmd_index;		/* *MUST* be unsigned */
-
-	/* Get the description of the Event */
-	if(cmd <= SIOCIWLAST) {
-		cmd_index = cmd - SIOCIWFIRST;
-		if(cmd_index < standard_ioctl_num)
-			descr = &(standard_ioctl[cmd_index]);
-	} else {
-		cmd_index = cmd - IWEVFIRST;
-		if(cmd_index < standard_event_num)
-			descr = &(standard_event[cmd_index]);
-	}
-	/* Don't accept unknown events */
-	if(descr == NULL) {
-		/* Note : we don't return an error to the driver, because
-		 * the driver would not know what to do about it. It can't
-		 * return an error to the user, because the event is not
-		 * initiated by a user request.
-		 * The best the driver could do is to log an error message.
-		 * We will do it ourselves instead...
-		 */
-		printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n",
-		       dev->name, cmd);
-		return;
-	}
-#ifdef WE_EVENT_DEBUG
-	printk(KERN_DEBUG "%s (WE) : Got event 0x%04X\n",
-	       dev->name, cmd);
-	printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
-#endif	/* WE_EVENT_DEBUG */
-
-	/* Check extra parameters and set extra_len */
-	if(descr->header_type == IW_HEADER_TYPE_POINT) {
-		/* Check if number of token fits within bounds */
-		if(wrqu->data.length > descr->max_tokens) {
-			printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length);
-			return;
-		}
-		if(wrqu->data.length < descr->min_tokens) {
-			printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length);
-			return;
-		}
-		/* Calculate extra_len - extra is NULL for restricted events */
-		if(extra != NULL)
-			extra_len = wrqu->data.length * descr->token_size;
-		/* Always at an offset in wrqu */
-		wrqu_off = IW_EV_POINT_OFF;
-#ifdef WE_EVENT_DEBUG
-		printk(KERN_DEBUG "%s (WE) : Event 0x%04X, tokens %d, extra_len %d\n", dev->name, cmd, wrqu->data.length, extra_len);
-#endif	/* WE_EVENT_DEBUG */
-	}
-
-	/* Total length of the event */
-	hdr_len = event_type_size[descr->header_type];
-	event_len = hdr_len + extra_len;
-
-#ifdef WE_EVENT_DEBUG
-	printk(KERN_DEBUG "%s (WE) : Event 0x%04X, hdr_len %d, wrqu_off %d, event_len %d\n", dev->name, cmd, hdr_len, wrqu_off, event_len);
-#endif	/* WE_EVENT_DEBUG */
-
-	/* Create temporary buffer to hold the event */
-	event = kmalloc(event_len, GFP_ATOMIC);
-	if(event == NULL)
-		return;
-
-	/* Fill event */
-	event->len = event_len;
-	event->cmd = cmd;
-	memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);
-	if(extra != NULL)
-		memcpy(((char *) event) + hdr_len, extra, extra_len);
-
-#ifdef WE_EVENT_RTNETLINK
-	/* Send via the RtNetlink event channel */
-	rtmsg_iwinfo(dev, (char *) event, event_len);
-#endif	/* WE_EVENT_RTNETLINK */
-
-	/* Cleanup */
-	kfree(event);
-
-	return;		/* Always success, I guess ;-) */
-}
-
-/********************** ENHANCED IWSPY SUPPORT **********************/
-/*
- * In the old days, the driver was handling spy support all by itself.
- * Now, the driver can delegate this task to Wireless Extensions.
- * It needs to use those standard spy iw_handler in struct iw_handler_def,
- * push data to us via wireless_spy_update() and include struct iw_spy_data
- * in its private part (and export it in net_device->wireless_data->spy_data).
- * One of the main advantage of centralising spy support here is that
- * it becomes much easier to improve and extend it without having to touch
- * the drivers. One example is the addition of the Spy-Threshold events.
- */
-
-/* ---------------------------------------------------------------- */
-/*
- * Return the pointer to the spy data in the driver.
- * Because this is called on the Rx path via wireless_spy_update(),
- * we want it to be efficient...
- */
-static inline struct iw_spy_data * get_spydata(struct net_device *dev)
-{
-	/* This is the new way */
-	if(dev->wireless_data)
-		return(dev->wireless_data->spy_data);
-	return NULL;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Standard Wireless Handler : set Spy List
- */
-int iw_handler_set_spy(struct net_device *	dev,
-		       struct iw_request_info *	info,
-		       union iwreq_data *	wrqu,
-		       char *			extra)
-{
-	struct iw_spy_data *	spydata = get_spydata(dev);
-	struct sockaddr *	address = (struct sockaddr *) extra;
-
-	/* Make sure driver is not buggy or using the old API */
-	if(!spydata)
-		return -EOPNOTSUPP;
-
-	/* Disable spy collection while we copy the addresses.
-	 * While we copy addresses, any call to wireless_spy_update()
-	 * will NOP. This is OK, as anyway the addresses are changing. */
-	spydata->spy_number = 0;
-
-	/* We want to operate without locking, because wireless_spy_update()
-	 * most likely will happen in the interrupt handler, and therefore
-	 * have its own locking constraints and needs performance.
-	 * The rtnl_lock() make sure we don't race with the other iw_handlers.
-	 * This make sure wireless_spy_update() "see" that the spy list
-	 * is temporarily disabled. */
-	smp_wmb();
-
-	/* Are there are addresses to copy? */
-	if(wrqu->data.length > 0) {
-		int i;
-
-		/* Copy addresses */
-		for(i = 0; i < wrqu->data.length; i++)
-			memcpy(spydata->spy_address[i], address[i].sa_data,
-			       ETH_ALEN);
-		/* Reset stats */
-		memset(spydata->spy_stat, 0,
-		       sizeof(struct iw_quality) * IW_MAX_SPY);
-
-#ifdef WE_SPY_DEBUG
-		printk(KERN_DEBUG "iw_handler_set_spy() :  wireless_data %p, spydata %p, num %d\n", dev->wireless_data, spydata, wrqu->data.length);
-		for (i = 0; i < wrqu->data.length; i++)
-			printk(KERN_DEBUG
-			       "%02X:%02X:%02X:%02X:%02X:%02X \n",
-			       spydata->spy_address[i][0],
-			       spydata->spy_address[i][1],
-			       spydata->spy_address[i][2],
-			       spydata->spy_address[i][3],
-			       spydata->spy_address[i][4],
-			       spydata->spy_address[i][5]);
-#endif	/* WE_SPY_DEBUG */
-	}
-
-	/* Make sure above is updated before re-enabling */
-	smp_wmb();
-
-	/* Enable addresses */
-	spydata->spy_number = wrqu->data.length;
-
-	return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Standard Wireless Handler : get Spy List
- */
-int iw_handler_get_spy(struct net_device *	dev,
-		       struct iw_request_info *	info,
-		       union iwreq_data *	wrqu,
-		       char *			extra)
-{
-	struct iw_spy_data *	spydata = get_spydata(dev);
-	struct sockaddr *	address = (struct sockaddr *) extra;
-	int			i;
-
-	/* Make sure driver is not buggy or using the old API */
-	if(!spydata)
-		return -EOPNOTSUPP;
-
-	wrqu->data.length = spydata->spy_number;
-
-	/* Copy addresses. */
-	for(i = 0; i < spydata->spy_number; i++) 	{
-		memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
-		address[i].sa_family = AF_UNIX;
-	}
-	/* Copy stats to the user buffer (just after). */
-	if(spydata->spy_number > 0)
-		memcpy(extra  + (sizeof(struct sockaddr) *spydata->spy_number),
-		       spydata->spy_stat,
-		       sizeof(struct iw_quality) * spydata->spy_number);
-	/* Reset updated flags. */
-	for(i = 0; i < spydata->spy_number; i++)
-		spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
-	return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Standard Wireless Handler : set spy threshold
- */
-int iw_handler_set_thrspy(struct net_device *	dev,
-			  struct iw_request_info *info,
-			  union iwreq_data *	wrqu,
-			  char *		extra)
-{
-	struct iw_spy_data *	spydata = get_spydata(dev);
-	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra;
-
-	/* Make sure driver is not buggy or using the old API */
-	if(!spydata)
-		return -EOPNOTSUPP;
-
-	/* Just do it */
-	memcpy(&(spydata->spy_thr_low), &(threshold->low),
-	       2 * sizeof(struct iw_quality));
-
-	/* Clear flag */
-	memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
-
-#ifdef WE_SPY_DEBUG
-	printk(KERN_DEBUG "iw_handler_set_thrspy() :  low %d ; high %d\n", spydata->spy_thr_low.level, spydata->spy_thr_high.level);
-#endif	/* WE_SPY_DEBUG */
-
-	return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Standard Wireless Handler : get spy threshold
- */
-int iw_handler_get_thrspy(struct net_device *	dev,
-			  struct iw_request_info *info,
-			  union iwreq_data *	wrqu,
-			  char *		extra)
-{
-	struct iw_spy_data *	spydata = get_spydata(dev);
-	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra;
-
-	/* Make sure driver is not buggy or using the old API */
-	if(!spydata)
-		return -EOPNOTSUPP;
-
-	/* Just do it */
-	memcpy(&(threshold->low), &(spydata->spy_thr_low),
-	       2 * sizeof(struct iw_quality));
-
-	return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Prepare and send a Spy Threshold event
- */
-static void iw_send_thrspy_event(struct net_device *	dev,
-				 struct iw_spy_data *	spydata,
-				 unsigned char *	address,
-				 struct iw_quality *	wstats)
-{
-	union iwreq_data	wrqu;
-	struct iw_thrspy	threshold;
-
-	/* Init */
-	wrqu.data.length = 1;
-	wrqu.data.flags = 0;
-	/* Copy address */
-	memcpy(threshold.addr.sa_data, address, ETH_ALEN);
-	threshold.addr.sa_family = ARPHRD_ETHER;
-	/* Copy stats */
-	memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));
-	/* Copy also thresholds */
-	memcpy(&(threshold.low), &(spydata->spy_thr_low),
-	       2 * sizeof(struct iw_quality));
-
-#ifdef WE_SPY_DEBUG
-	printk(KERN_DEBUG "iw_send_thrspy_event() : address %02X:%02X:%02X:%02X:%02X:%02X, level %d, up = %d\n",
-	       threshold.addr.sa_data[0],
-	       threshold.addr.sa_data[1],
-	       threshold.addr.sa_data[2],
-	       threshold.addr.sa_data[3],
-	       threshold.addr.sa_data[4],
-	       threshold.addr.sa_data[5], threshold.qual.level);
-#endif	/* WE_SPY_DEBUG */
-
-	/* Send event to user space */
-	wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Call for the driver to update the spy data.
- * For now, the spy data is a simple array. As the size of the array is
- * small, this is good enough. If we wanted to support larger number of
- * spy addresses, we should use something more efficient...
- */
-void wireless_spy_update(struct net_device *	dev,
-			 unsigned char *	address,
-			 struct iw_quality *	wstats)
-{
-	struct iw_spy_data *	spydata = get_spydata(dev);
-	int			i;
-	int			match = -1;
-
-	/* Make sure driver is not buggy or using the old API */
-	if(!spydata)
-		return;
-
-#ifdef WE_SPY_DEBUG
-	printk(KERN_DEBUG "wireless_spy_update() :  wireless_data %p, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_data, spydata, address[0], address[1], address[2], address[3], address[4], address[5]);
-#endif	/* WE_SPY_DEBUG */
-
-	/* Update all records that match */
-	for(i = 0; i < spydata->spy_number; i++)
-		if(!compare_ether_addr(address, spydata->spy_address[i])) {
-			memcpy(&(spydata->spy_stat[i]), wstats,
-			       sizeof(struct iw_quality));
-			match = i;
-		}
-
-	/* Generate an event if we cross the spy threshold.
-	 * To avoid event storms, we have a simple hysteresis : we generate
-	 * event only when we go under the low threshold or above the
-	 * high threshold. */
-	if(match >= 0) {
-		if(spydata->spy_thr_under[match]) {
-			if(wstats->level > spydata->spy_thr_high.level) {
-				spydata->spy_thr_under[match] = 0;
-				iw_send_thrspy_event(dev, spydata,
-						     address, wstats);
-			}
-		} else {
-			if(wstats->level < spydata->spy_thr_low.level) {
-				spydata->spy_thr_under[match] = 1;
-				iw_send_thrspy_event(dev, spydata,
-						     address, wstats);
-			}
-		}
-	}
-}
-
-EXPORT_SYMBOL(iw_handler_get_spy);
-EXPORT_SYMBOL(iw_handler_get_thrspy);
-EXPORT_SYMBOL(iw_handler_set_spy);
-EXPORT_SYMBOL(iw_handler_set_thrspy);
-EXPORT_SYMBOL(wireless_send_event);
-EXPORT_SYMBOL(wireless_spy_update);
diff --git a/net/dccp/ackvec.c b/net/dccp/ackvec.c
index a086c63..01030f3 100644
--- a/net/dccp/ackvec.c
+++ b/net/dccp/ackvec.c
@@ -157,7 +157,7 @@
 
 	if (av != NULL) {
 		av->dccpav_buf_head	= DCCP_MAX_ACKVEC_LEN - 1;
-		av->dccpav_buf_ackno	= DCCP_MAX_SEQNO + 1;
+		av->dccpav_buf_ackno	= UINT48_MAX + 1;
 		av->dccpav_buf_nonce = av->dccpav_buf_nonce = 0;
 		av->dccpav_time.tv_sec	= 0;
 		av->dccpav_time.tv_usec	= 0;
diff --git a/net/dccp/ccids/ccid3.c b/net/dccp/ccids/ccid3.c
index 746f79d..d7d9ce7 100644
--- a/net/dccp/ccids/ccid3.c
+++ b/net/dccp/ccids/ccid3.c
@@ -33,7 +33,6 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-
 #include "../ccid.h"
 #include "../dccp.h"
 #include "lib/packet_history.h"
@@ -52,6 +51,9 @@
 static struct dccp_rx_hist *ccid3_rx_hist;
 static struct dccp_li_hist *ccid3_li_hist;
 
+/*
+ *	Transmitter Half-Connection Routines
+ */
 #ifdef CONFIG_IP_DCCP_CCID3_DEBUG
 static const char *ccid3_tx_state_name(enum ccid3_hc_tx_states state)
 {
@@ -80,23 +82,37 @@
 }
 
 /*
- * Recalculate scheduled nominal send time t_nom, inter-packet interval
- * t_ipi, and delta value. Should be called after each change to X.
+ * Compute the initial sending rate X_init according to RFC 3390:
+ *	w_init   =    min(4 * MSS, max(2 * MSS, 4380 bytes))
+ *	X_init   =    w_init / RTT
+ * For consistency with other parts of the code, X_init is scaled by 2^6.
  */
-static inline void ccid3_update_send_time(struct ccid3_hc_tx_sock *hctx)
+static inline u64 rfc3390_initial_rate(struct sock *sk)
 {
-	timeval_sub_usecs(&hctx->ccid3hctx_t_nom, hctx->ccid3hctx_t_ipi);
+	const struct dccp_sock *dp = dccp_sk(sk);
+	const __u32 w_init = min(4 * dp->dccps_mss_cache,
+				 max(2 * dp->dccps_mss_cache, 4380U));
 
+	return scaled_div(w_init << 6, ccid3_hc_tx_sk(sk)->ccid3hctx_rtt);
+}
+
+/*
+ * Recalculate t_ipi and delta (should be called whenever X changes)
+ */
+static inline void ccid3_update_send_interval(struct ccid3_hc_tx_sock *hctx)
+{
 	/* Calculate new t_ipi = s / X_inst (X_inst is in 64 * bytes/second) */
-	hctx->ccid3hctx_t_ipi = scaled_div(hctx->ccid3hctx_s,
-					   hctx->ccid3hctx_x >> 6);
-
-	/* Update nominal send time with regard to the new t_ipi */
-	timeval_add_usecs(&hctx->ccid3hctx_t_nom, hctx->ccid3hctx_t_ipi);
+	hctx->ccid3hctx_t_ipi = scaled_div32(((u64)hctx->ccid3hctx_s) << 6,
+					     hctx->ccid3hctx_x);
 
 	/* Calculate new delta by delta = min(t_ipi / 2, t_gran / 2) */
 	hctx->ccid3hctx_delta = min_t(u32, hctx->ccid3hctx_t_ipi / 2,
 					   TFRC_OPSYS_HALF_TIME_GRAN);
+
+	ccid3_pr_debug("t_ipi=%u, delta=%u, s=%u, X=%u\n",
+		       hctx->ccid3hctx_t_ipi, hctx->ccid3hctx_delta,
+		       hctx->ccid3hctx_s, (unsigned)(hctx->ccid3hctx_x >> 6));
+
 }
 /*
  * Update X by
@@ -112,19 +128,28 @@
  *       fine-grained resolution of sending rates. This requires scaling by 2^6
  *       throughout the code. Only X_calc is unscaled (in bytes/second).
  *
- * If X has changed, we also update the scheduled send time t_now,
- * the inter-packet interval t_ipi, and the delta value.
  */
 static void ccid3_hc_tx_update_x(struct sock *sk, struct timeval *now)
 
 {
 	struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk);
+	__u64 min_rate = 2 * hctx->ccid3hctx_x_recv;
 	const  __u64 old_x = hctx->ccid3hctx_x;
 
+	/*
+	 * Handle IDLE periods: do not reduce below RFC3390 initial sending rate
+	 * when idling [RFC 4342, 5.1]. See also draft-ietf-dccp-rfc3448bis.
+	 * For consistency with X and X_recv, min_rate is also scaled by 2^6.
+	 */
+	if (unlikely(hctx->ccid3hctx_idle)) {
+		min_rate = rfc3390_initial_rate(sk);
+		min_rate = max(min_rate, 2 * hctx->ccid3hctx_x_recv);
+	}
+
 	if (hctx->ccid3hctx_p > 0) {
 
 		hctx->ccid3hctx_x = min(((__u64)hctx->ccid3hctx_x_calc) << 6,
-					hctx->ccid3hctx_x_recv * 2);
+					min_rate);
 		hctx->ccid3hctx_x = max(hctx->ccid3hctx_x,
 					(((__u64)hctx->ccid3hctx_s) << 6) /
 								TFRC_T_MBI);
@@ -133,14 +158,21 @@
 			(suseconds_t)hctx->ccid3hctx_rtt >= 0) {
 
 		hctx->ccid3hctx_x =
-			max(2 * min(hctx->ccid3hctx_x, hctx->ccid3hctx_x_recv),
+			max(min(2 * hctx->ccid3hctx_x, min_rate),
 			    scaled_div(((__u64)hctx->ccid3hctx_s) << 6,
 				       hctx->ccid3hctx_rtt));
 		hctx->ccid3hctx_t_ld = *now;
 	}
 
-	if (hctx->ccid3hctx_x != old_x)
-		ccid3_update_send_time(hctx);
+	if (hctx->ccid3hctx_x != old_x) {
+		ccid3_pr_debug("X_prev=%u, X_now=%u, X_calc=%u, "
+			       "X_recv=%u\n", (unsigned)(old_x >> 6),
+			       (unsigned)(hctx->ccid3hctx_x >> 6),
+			       hctx->ccid3hctx_x_calc,
+			       (unsigned)(hctx->ccid3hctx_x_recv >> 6));
+
+		ccid3_update_send_interval(hctx);
+	}
 }
 
 /*
@@ -149,17 +181,12 @@
  */
 static inline void ccid3_hc_tx_update_s(struct ccid3_hc_tx_sock *hctx, int len)
 {
-	if (unlikely(len == 0))
-		ccid3_pr_debug("Packet payload length is 0 - not updating\n");
-	else
-		hctx->ccid3hctx_s = hctx->ccid3hctx_s == 0 ? len :
-				    (9 * hctx->ccid3hctx_s + len) / 10;
-	/*
-	 * Note: We could do a potential optimisation here - when `s' changes,
-	 *	 recalculate sending rate and consequently t_ipi, t_delta, and
-	 *	 t_now. This is however non-standard, and the benefits are not
-	 *	 clear, so it is currently left out.
-	 */
+	const u16 old_s = hctx->ccid3hctx_s;
+
+	hctx->ccid3hctx_s = old_s == 0 ? len : (9 * old_s + len) / 10;
+
+	if (hctx->ccid3hctx_s != old_s)
+		ccid3_update_send_interval(hctx);
 }
 
 /*
@@ -193,6 +220,7 @@
 {
 	struct sock *sk = (struct sock *)data;
 	struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk);
+	struct timeval now;
 	unsigned long t_nfb = USEC_PER_SEC / 5;
 
 	bh_lock_sock(sk);
@@ -205,6 +233,8 @@
 	ccid3_pr_debug("%s(%p, state=%s) - entry \n", dccp_role(sk), sk,
 		       ccid3_tx_state_name(hctx->ccid3hctx_state));
 
+	hctx->ccid3hctx_idle = 1;
+
 	switch (hctx->ccid3hctx_state) {
 	case TFRC_SSTATE_NO_FBACK:
 		/* RFC 3448, 4.4: Halve send rate directly */
@@ -219,53 +249,37 @@
 		/* The value of R is still undefined and so we can not recompute
 		 * the timout value. Keep initial value as per [RFC 4342, 5]. */
 		t_nfb = TFRC_INITIAL_TIMEOUT;
-		ccid3_update_send_time(hctx);
+		ccid3_update_send_interval(hctx);
 		break;
 	case TFRC_SSTATE_FBACK:
 		/*
-		 * Check if IDLE since last timeout and recv rate is less than
-		 * 4 packets (in units of 64*bytes/sec) per RTT
+		 *  Modify the cached value of X_recv [RFC 3448, 4.4]
+		 *
+		 *  If (p == 0 || X_calc > 2 * X_recv)
+		 *    X_recv = max(X_recv / 2, s / (2 * t_mbi));
+		 *  Else
+		 *    X_recv = X_calc / 4;
+		 *
+		 *  Note that X_recv is scaled by 2^6 while X_calc is not
 		 */
-		if (!hctx->ccid3hctx_idle ||
-		    (hctx->ccid3hctx_x_recv >= 4 *
-		     scaled_div(((__u64)hctx->ccid3hctx_s) << 6,
-				hctx->ccid3hctx_rtt))) {
-			struct timeval now;
+		BUG_ON(hctx->ccid3hctx_p && !hctx->ccid3hctx_x_calc);
 
-			ccid3_pr_debug("%s(%p, state=%s), not idle\n",
-				       dccp_role(sk), sk,
-				   ccid3_tx_state_name(hctx->ccid3hctx_state));
+		if (hctx->ccid3hctx_p == 0 ||
+		    (hctx->ccid3hctx_x_calc > (hctx->ccid3hctx_x_recv >> 5))) {
 
-			/*
-			 *  Modify the cached value of X_recv [RFC 3448, 4.4]
-			 *
-			 *  If (p == 0 || X_calc > 2 * X_recv)
-			 *    X_recv = max(X_recv / 2, s / (2 * t_mbi));
-			 *  Else
-			 *    X_recv = X_calc / 4;
-			 *
-			 *  Note that X_recv is scaled by 2^6 while X_calc is not
-			 */
-			BUG_ON(hctx->ccid3hctx_p && !hctx->ccid3hctx_x_calc);
+			hctx->ccid3hctx_x_recv =
+				max(hctx->ccid3hctx_x_recv / 2,
+				    (((__u64)hctx->ccid3hctx_s) << 6) /
+							      (2 * TFRC_T_MBI));
 
-			if (hctx->ccid3hctx_p  == 0 ||
-			    (hctx->ccid3hctx_x_calc >
-			     (hctx->ccid3hctx_x_recv >> 5))) {
-
-				hctx->ccid3hctx_x_recv =
-					max(hctx->ccid3hctx_x_recv / 2,
-					    (((__u64)hctx->ccid3hctx_s) << 6) /
-							  (2 * TFRC_T_MBI));
-
-				if (hctx->ccid3hctx_p == 0)
-					dccp_timestamp(sk, &now);
-			} else {
-				hctx->ccid3hctx_x_recv = hctx->ccid3hctx_x_calc;
-				hctx->ccid3hctx_x_recv <<= 4;
-			}
-			/* Now recalculate X [RFC 3448, 4.3, step (4)] */
-			ccid3_hc_tx_update_x(sk, &now);
+			if (hctx->ccid3hctx_p == 0)
+				dccp_timestamp(sk, &now);
+		} else {
+			hctx->ccid3hctx_x_recv = hctx->ccid3hctx_x_calc;
+			hctx->ccid3hctx_x_recv <<= 4;
 		}
+		/* Now recalculate X [RFC 3448, 4.3, step (4)] */
+		ccid3_hc_tx_update_x(sk, &now);
 		/*
 		 * Schedule no feedback timer to expire in
 		 * max(t_RTO, 2 * s/X)  =  max(t_RTO, 2 * t_ipi)
@@ -280,8 +294,6 @@
 		goto out;
 	}
 
-	hctx->ccid3hctx_idle = 1;
-
 restart_timer:
 	sk_reset_timer(sk, &hctx->ccid3hctx_no_feedback_timer,
 			   jiffies + usecs_to_jiffies(t_nfb));
@@ -322,24 +334,35 @@
 				usecs_to_jiffies(TFRC_INITIAL_TIMEOUT)));
 		hctx->ccid3hctx_last_win_count	 = 0;
 		hctx->ccid3hctx_t_last_win_count = now;
-		ccid3_hc_tx_set_state(sk, TFRC_SSTATE_NO_FBACK);
-
-		/* Set initial sending rate X/s to 1pps (X is scaled by 2^6) */
-		ccid3_hc_tx_update_s(hctx, skb->len);
-		hctx->ccid3hctx_x = hctx->ccid3hctx_s;
-		hctx->ccid3hctx_x <<= 6;
-
-		/* First timeout, according to [RFC 3448, 4.2], is 1 second */
-		hctx->ccid3hctx_t_ipi = USEC_PER_SEC;
-		/* Initial delta: minimum of 0.5 sec and t_gran/2 */
-		hctx->ccid3hctx_delta = TFRC_OPSYS_HALF_TIME_GRAN;
 
 		/* Set t_0 for initial packet */
 		hctx->ccid3hctx_t_nom = now;
+
+		hctx->ccid3hctx_s = skb->len;
+
+		/*
+		 * Use initial RTT sample when available: recommended by erratum
+		 * to RFC 4342. This implements the initialisation procedure of
+		 * draft rfc3448bis, section 4.2. Remember, X is scaled by 2^6.
+		 */
+		if (dp->dccps_syn_rtt) {
+			ccid3_pr_debug("SYN RTT = %uus\n", dp->dccps_syn_rtt);
+			hctx->ccid3hctx_rtt  = dp->dccps_syn_rtt;
+			hctx->ccid3hctx_x    = rfc3390_initial_rate(sk);
+			hctx->ccid3hctx_t_ld = now;
+		} else {
+			/* Sender does not have RTT sample: X = MSS/second */
+			hctx->ccid3hctx_x = dp->dccps_mss_cache;
+			hctx->ccid3hctx_x <<= 6;
+		}
+		ccid3_update_send_interval(hctx);
+
+		ccid3_hc_tx_set_state(sk, TFRC_SSTATE_NO_FBACK);
 		break;
 	case TFRC_SSTATE_NO_FBACK:
 	case TFRC_SSTATE_FBACK:
 		delay = timeval_delta(&hctx->ccid3hctx_t_nom, &now);
+		ccid3_pr_debug("delay=%ld\n", (long)delay);
 		/*
 		 *	Scheduling of packet transmissions [RFC 3448, 4.6]
 		 *
@@ -361,6 +384,7 @@
 	/* prepare to send now (add options etc.) */
 	dp->dccps_hc_tx_insert_options = 1;
 	DCCP_SKB_CB(skb)->dccpd_ccval = hctx->ccid3hctx_last_win_count;
+	hctx->ccid3hctx_idle = 0;
 
 	/* set the nominal send time for the next following packet */
 	timeval_add_usecs(&hctx->ccid3hctx_t_nom, hctx->ccid3hctx_t_ipi);
@@ -391,7 +415,6 @@
 	packet->dccphtx_seqno  = dccp_sk(sk)->dccps_gss;
 	packet->dccphtx_rtt    = hctx->ccid3hctx_rtt;
 	packet->dccphtx_sent   = 1;
-	hctx->ccid3hctx_idle   = 0;
 }
 
 static void ccid3_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
@@ -402,8 +425,7 @@
 	struct dccp_tx_hist_entry *packet;
 	struct timeval now;
 	unsigned long t_nfb;
-	u32 pinv;
-	suseconds_t r_sample, t_elapsed;
+	u32 pinv, r_sample;
 
 	BUG_ON(hctx == NULL);
 
@@ -445,18 +467,10 @@
 		 * Calculate new round trip sample as per [RFC 3448, 4.3] by
 		 *	R_sample  =  (now - t_recvdata) - t_elapsed
 		 */
-		r_sample  = timeval_delta(&now, &packet->dccphtx_tstamp);
-		t_elapsed = dp->dccps_options_received.dccpor_elapsed_time * 10;
+		r_sample = dccp_sample_rtt(sk, &now, &packet->dccphtx_tstamp);
 
-		DCCP_BUG_ON(r_sample < 0);
-		if (unlikely(r_sample <= t_elapsed))
-			DCCP_WARN("WARNING: r_sample=%dus <= t_elapsed=%dus\n",
-				  (int)r_sample, (int)t_elapsed);
-		else
-			r_sample -= t_elapsed;
-		CCID3_RTT_SANITY_CHECK(r_sample);
-
-		/* Update RTT estimate by
+		/*
+		 * Update RTT estimate by
 		 * If (No feedback recv)
 		 *    R = R_sample;
 		 * Else
@@ -467,27 +481,23 @@
 		if (hctx->ccid3hctx_state == TFRC_SSTATE_NO_FBACK) {
 			/*
 			 * Larger Initial Windows [RFC 4342, sec. 5]
-			 * We deviate in that we use `s' instead of `MSS'.
 			 */
-			__u64 w_init = min(4 * hctx->ccid3hctx_s,
-					   max(2 * hctx->ccid3hctx_s, 4380));
 			hctx->ccid3hctx_rtt  = r_sample;
-			hctx->ccid3hctx_x    = scaled_div(w_init << 6, r_sample);
+			hctx->ccid3hctx_x    = rfc3390_initial_rate(sk);
 			hctx->ccid3hctx_t_ld = now;
 
-			ccid3_update_send_time(hctx);
+			ccid3_update_send_interval(hctx);
 
-			ccid3_pr_debug("%s(%p), s=%u, w_init=%llu, "
-				       "R_sample=%dus, X=%u\n", dccp_role(sk),
+			ccid3_pr_debug("%s(%p), s=%u, MSS=%u, "
+				       "R_sample=%uus, X=%u\n", dccp_role(sk),
 				       sk, hctx->ccid3hctx_s,
-				       (unsigned long long)w_init,
-				       (int)r_sample,
+				       dp->dccps_mss_cache, r_sample,
 				       (unsigned)(hctx->ccid3hctx_x >> 6));
 
 			ccid3_hc_tx_set_state(sk, TFRC_SSTATE_FBACK);
 		} else {
 			hctx->ccid3hctx_rtt = (9 * hctx->ccid3hctx_rtt +
-						   (u32)r_sample) / 10;
+						   r_sample) / 10;
 
 			/* Update sending rate (step 4 of [RFC 3448, 4.3]) */
 			if (hctx->ccid3hctx_p > 0)
@@ -497,10 +507,10 @@
 						    hctx->ccid3hctx_p);
 			ccid3_hc_tx_update_x(sk, &now);
 
-			ccid3_pr_debug("%s(%p), RTT=%uus (sample=%dus), s=%u, "
+			ccid3_pr_debug("%s(%p), RTT=%uus (sample=%uus), s=%u, "
 				       "p=%u, X_calc=%u, X_recv=%u, X=%u\n",
 				       dccp_role(sk),
-				       sk, hctx->ccid3hctx_rtt, (int)r_sample,
+				       sk, hctx->ccid3hctx_rtt, r_sample,
 				       hctx->ccid3hctx_s, hctx->ccid3hctx_p,
 				       hctx->ccid3hctx_x_calc,
 				       (unsigned)(hctx->ccid3hctx_x_recv >> 6),
@@ -644,10 +654,50 @@
 	dccp_tx_hist_purge(ccid3_tx_hist, &hctx->ccid3hctx_hist);
 }
 
-/*
- * RX Half Connection methods
- */
+static void ccid3_hc_tx_get_info(struct sock *sk, struct tcp_info *info)
+{
+	const struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk);
 
+	/* Listen socks doesn't have a private CCID block */
+	if (sk->sk_state == DCCP_LISTEN)
+		return;
+
+	BUG_ON(hctx == NULL);
+
+	info->tcpi_rto = hctx->ccid3hctx_t_rto;
+	info->tcpi_rtt = hctx->ccid3hctx_rtt;
+}
+
+static int ccid3_hc_tx_getsockopt(struct sock *sk, const int optname, int len,
+				  u32 __user *optval, int __user *optlen)
+{
+	const struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk);
+	const void *val;
+
+	/* Listen socks doesn't have a private CCID block */
+	if (sk->sk_state == DCCP_LISTEN)
+		return -EINVAL;
+
+	switch (optname) {
+	case DCCP_SOCKOPT_CCID_TX_INFO:
+		if (len < sizeof(hctx->ccid3hctx_tfrc))
+			return -EINVAL;
+		len = sizeof(hctx->ccid3hctx_tfrc);
+		val = &hctx->ccid3hctx_tfrc;
+		break;
+	default:
+		return -ENOPROTOOPT;
+	}
+
+	if (put_user(len, optlen) || copy_to_user(optval, val, len))
+		return -EFAULT;
+
+	return 0;
+}
+
+/*
+ *	Receiver Half-Connection Routines
+ */
 #ifdef CONFIG_IP_DCCP_CCID3_DEBUG
 static const char *ccid3_rx_state_name(enum ccid3_hc_rx_states state)
 {
@@ -977,8 +1027,7 @@
 	const struct dccp_options_received *opt_recv;
 	struct dccp_rx_hist_entry *packet;
 	struct timeval now;
-	u32 p_prev, rtt_prev;
-	suseconds_t r_sample, t_elapsed;
+	u32 p_prev, r_sample, rtt_prev;
 	int loss, payload_size;
 
 	BUG_ON(hcrx == NULL);
@@ -994,17 +1043,7 @@
 			break;
 		rtt_prev = hcrx->ccid3hcrx_rtt;
 		dccp_timestamp(sk, &now);
-		timeval_sub_usecs(&now, opt_recv->dccpor_timestamp_echo * 10);
-		r_sample = timeval_usecs(&now);
-		t_elapsed = opt_recv->dccpor_elapsed_time * 10;
-
-		DCCP_BUG_ON(r_sample < 0);
-		if (unlikely(r_sample <= t_elapsed))
-			DCCP_WARN("r_sample=%ldus, t_elapsed=%ldus\n",
-				  (long)r_sample, (long)t_elapsed);
-		else
-			r_sample -= t_elapsed;
-		CCID3_RTT_SANITY_CHECK(r_sample);
+		r_sample = dccp_sample_rtt(sk, &now, NULL);
 
 		if (hcrx->ccid3hcrx_state == TFRC_RSTATE_NO_DATA)
 			hcrx->ccid3hcrx_rtt = r_sample;
@@ -1132,20 +1171,6 @@
 	info->tcpi_rcv_rtt  = hcrx->ccid3hcrx_rtt;
 }
 
-static void ccid3_hc_tx_get_info(struct sock *sk, struct tcp_info *info)
-{
-	const struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk);
-
-	/* Listen socks doesn't have a private CCID block */
-	if (sk->sk_state == DCCP_LISTEN)
-		return;
-
-	BUG_ON(hctx == NULL);
-
-	info->tcpi_rto = hctx->ccid3hctx_t_rto;
-	info->tcpi_rtt = hctx->ccid3hctx_rtt;
-}
-
 static int ccid3_hc_rx_getsockopt(struct sock *sk, const int optname, int len,
 				  u32 __user *optval, int __user *optlen)
 {
@@ -1173,33 +1198,6 @@
 	return 0;
 }
 
-static int ccid3_hc_tx_getsockopt(struct sock *sk, const int optname, int len,
-				  u32 __user *optval, int __user *optlen)
-{
-	const struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk);
-	const void *val;
-
-	/* Listen socks doesn't have a private CCID block */
-	if (sk->sk_state == DCCP_LISTEN)
-		return -EINVAL;
-
-	switch (optname) {
-	case DCCP_SOCKOPT_CCID_TX_INFO:
-		if (len < sizeof(hctx->ccid3hctx_tfrc))
-			return -EINVAL;
-		len = sizeof(hctx->ccid3hctx_tfrc);
-		val = &hctx->ccid3hctx_tfrc;
-		break;
-	default:
-		return -ENOPROTOOPT;
-	}
-
-	if (put_user(len, optlen) || copy_to_user(optval, val, len))
-		return -EFAULT;
-
-	return 0;
-}
-
 static struct ccid_operations ccid3 = {
 	.ccid_id		   = DCCPC_CCID3,
 	.ccid_name		   = "ccid3",
diff --git a/net/dccp/ccids/ccid3.h b/net/dccp/ccids/ccid3.h
index 15776a8..8d31b38 100644
--- a/net/dccp/ccids/ccid3.h
+++ b/net/dccp/ccids/ccid3.h
@@ -51,16 +51,6 @@
 /* Parameter t_mbi from [RFC 3448, 4.3]: backoff interval in seconds */
 #define TFRC_T_MBI		   64
 
-/* What we think is a reasonable upper limit on RTT values */
-#define CCID3_SANE_RTT_MAX	   ((suseconds_t)(4 * USEC_PER_SEC))
-
-#define CCID3_RTT_SANITY_CHECK(rtt) 			do {		   \
-		if (rtt > CCID3_SANE_RTT_MAX) {				   \
-			DCCP_CRIT("RTT (%d) too large, substituting %d",   \
-				  (int)rtt, (int)CCID3_SANE_RTT_MAX);	   \
-			rtt = CCID3_SANE_RTT_MAX;			   \
-		} 					} while (0)
-
 enum ccid3_options {
 	TFRC_OPT_LOSS_EVENT_RATE = 192,
 	TFRC_OPT_LOSS_INTERVALS	 = 193,
diff --git a/net/dccp/ccids/lib/loss_interval.c b/net/dccp/ccids/lib/loss_interval.c
index 0a0baef..372d7e7 100644
--- a/net/dccp/ccids/lib/loss_interval.c
+++ b/net/dccp/ccids/lib/loss_interval.c
@@ -91,7 +91,7 @@
 	u32 w_tot  = 0;
 
 	list_for_each_entry_safe(li_entry, li_next, list, dccplih_node) {
-		if (li_entry->dccplih_interval != ~0) {
+		if (li_entry->dccplih_interval != ~0U) {
 			i_tot0 += li_entry->dccplih_interval * dccp_li_hist_w[i];
 			w_tot  += dccp_li_hist_w[i];
 			if (i != 0)
diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h
index e33a9ed..d8ad27b 100644
--- a/net/dccp/dccp.h
+++ b/net/dccp/dccp.h
@@ -31,13 +31,9 @@
 					      __stringify(cond));          \
 			     } while (0)
 
-#ifdef MODULE
 #define DCCP_PRINTK(enable, fmt, args...)	do { if (enable)	     \
 							printk(fmt, ##args); \
 						} while(0)
-#else
-#define DCCP_PRINTK(enable, fmt, args...)	printk(fmt, ##args)
-#endif
 #define DCCP_PR_DEBUG(enable, fmt, a...)	DCCP_PRINTK(enable, KERN_DEBUG \
 						  "%s: " fmt, __FUNCTION__, ##a)
 
@@ -75,11 +71,15 @@
 /* RFC 1122, 4.2.3.1 initial RTO value */
 #define DCCP_TIMEOUT_INIT ((unsigned)(3 * HZ))
 
+#define DCCP_RTO_MAX ((unsigned)(120 * HZ)) /* FIXME: using TCP value */
+
+/* bounds for sampled RTT values from packet exchanges (in usec) */
+#define DCCP_SANE_RTT_MIN	100
+#define DCCP_SANE_RTT_MAX	(4 * USEC_PER_SEC)
+
 /* Maximal interval between probes for local resources.  */
 #define DCCP_RESOURCE_PROBE_INTERVAL ((unsigned)(HZ / 2U))
 
-#define DCCP_RTO_MAX ((unsigned)(120 * HZ)) /* FIXME: using TCP value */
-
 /* sysctl variables for DCCP */
 extern int  sysctl_dccp_request_retries;
 extern int  sysctl_dccp_retries1;
@@ -92,17 +92,43 @@
 extern int  sysctl_dccp_feat_send_ndp_count;
 extern int  sysctl_dccp_tx_qlen;
 
+/*
+ *	48-bit sequence number arithmetic (signed and unsigned)
+ */
+#define INT48_MIN	  0x800000000000LL		/* 2^47	    */
+#define UINT48_MAX	  0xFFFFFFFFFFFFLL		/* 2^48 - 1 */
+#define COMPLEMENT48(x)	 (0x1000000000000LL - (x))	/* 2^48 - x */
+#define TO_SIGNED48(x)	 (((x) < INT48_MIN)? (x) : -COMPLEMENT48( (x)))
+#define TO_UNSIGNED48(x) (((x) >= 0)?	     (x) :  COMPLEMENT48(-(x)))
+#define ADD48(a, b)	 (((a) + (b)) & UINT48_MAX)
+#define SUB48(a, b)	 ADD48((a), COMPLEMENT48(b))
+
+static inline void dccp_set_seqno(u64 *seqno, u64 value)
+{
+	*seqno = value & UINT48_MAX;
+}
+
+static inline void dccp_inc_seqno(u64 *seqno)
+{
+	*seqno = ADD48(*seqno, 1);
+}
+
+/* signed mod-2^48 distance: pos. if seqno1 < seqno2, neg. if seqno1 > seqno2 */
+static inline s64 dccp_delta_seqno(const u64 seqno1, const u64 seqno2)
+{
+	u64 delta = SUB48(seqno2, seqno1);
+
+	return TO_SIGNED48(delta);
+}
+
 /* is seq1 < seq2 ? */
 static inline int before48(const u64 seq1, const u64 seq2)
 {
-	return (s64)((seq1 << 16) - (seq2 << 16)) < 0;
+	return (s64)((seq2 << 16) - (seq1 << 16)) > 0;
 }
 
 /* is seq1 > seq2 ? */
-static inline int after48(const u64 seq1, const u64 seq2)
-{
-	return (s64)((seq2 << 16) - (seq1 << 16)) < 0;
-}
+#define after48(seq1, seq2)	before48(seq2, seq1)
 
 /* is seq2 <= seq1 <= seq3 ? */
 static inline int between48(const u64 seq1, const u64 seq2, const u64 seq3)
@@ -118,9 +144,7 @@
 /* is seq1 next seqno after seq2 */
 static inline int follows48(const u64 seq1, const u64 seq2)
 {
-	int diff = (seq1 & 0xFFFF) - (seq2 & 0xFFFF);
-
-	return diff==1;
+	return dccp_delta_seqno(seq2, seq1) == 1;
 }
 
 enum {
@@ -272,6 +296,8 @@
 extern int	   dccp_send_reset(struct sock *sk, enum dccp_reset_codes code);
 extern void	   dccp_send_close(struct sock *sk, const int active);
 extern int	   dccp_invalid_packet(struct sk_buff *skb);
+extern u32	   dccp_sample_rtt(struct sock *sk, struct timeval *t_recv,
+						    struct timeval *t_history);
 
 static inline int dccp_bad_service_code(const struct sock *sk,
 					const __be32 service)
@@ -313,26 +339,7 @@
 	return type == DCCP_PKT_DATA || type == DCCP_PKT_REQUEST;
 }
 
-#define DCCP_MAX_SEQNO ((((u64)1) << 48) - 1)
-#define DCCP_PKT_WITHOUT_ACK_SEQ (DCCP_MAX_SEQNO << 2)
-
-static inline void dccp_set_seqno(u64 *seqno, u64 value)
-{
-	if (value > DCCP_MAX_SEQNO)
-		value -= DCCP_MAX_SEQNO + 1;
-	*seqno = value;
-}
-
-static inline u64 dccp_delta_seqno(u64 seqno1, u64 seqno2)
-{
-	return ((seqno2 << 16) - (seqno1 << 16)) >> 16;
-}
-
-static inline void dccp_inc_seqno(u64 *seqno)
-{
-	if (++*seqno > DCCP_MAX_SEQNO)
-		*seqno = 0;
-}
+#define DCCP_PKT_WITHOUT_ACK_SEQ (UINT48_MAX << 2)
 
 static inline void dccp_hdr_set_seq(struct dccp_hdr *dh, const u64 gss)
 {
diff --git a/net/dccp/input.c b/net/dccp/input.c
index 78b043c..da6ec18 100644
--- a/net/dccp/input.c
+++ b/net/dccp/input.c
@@ -86,7 +86,8 @@
 	    dh->dccph_type == DCCP_PKT_SYNCACK) {
 		if (between48(DCCP_SKB_CB(skb)->dccpd_ack_seq,
 			      dp->dccps_awl, dp->dccps_awh) &&
-		    !before48(DCCP_SKB_CB(skb)->dccpd_seq, dp->dccps_swl))
+		    dccp_delta_seqno(dp->dccps_swl,
+				     DCCP_SKB_CB(skb)->dccpd_seq) >= 0)
 			dccp_update_gsr(sk, DCCP_SKB_CB(skb)->dccpd_seq);
 		else
 			return -1;
@@ -203,7 +204,8 @@
 		if (dp->dccps_role != DCCP_ROLE_CLIENT)
 			goto send_sync;
 check_seq:
-		if (!before48(DCCP_SKB_CB(skb)->dccpd_seq, dp->dccps_osr)) {
+		if (dccp_delta_seqno(dp->dccps_osr,
+				     DCCP_SKB_CB(skb)->dccpd_seq) >= 0) {
 send_sync:
 			dccp_send_sync(sk, DCCP_SKB_CB(skb)->dccpd_seq,
 				       DCCP_PKT_SYNC);
@@ -298,6 +300,14 @@
 		if (dccp_parse_options(sk, skb))
 			goto out_invalid_packet;
 
+		/* Obtain RTT sample from SYN exchange (used by CCID 3) */
+		if (dp->dccps_options_received.dccpor_timestamp_echo) {
+			struct timeval now;
+
+			dccp_timestamp(sk, &now);
+			dp->dccps_syn_rtt = dccp_sample_rtt(sk, &now, NULL);
+		}
+
 		if (dccp_msk(sk)->dccpms_send_ack_vector &&
 		    dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk,
 				    DCCP_SKB_CB(skb)->dccpd_seq,
@@ -575,3 +585,43 @@
 }
 
 EXPORT_SYMBOL_GPL(dccp_rcv_state_process);
+
+/**
+ * dccp_sample_rtt  -  Sample RTT from packet exchange
+ *
+ * @sk:     connected dccp_sock
+ * @t_recv: receive timestamp of packet with timestamp echo
+ * @t_hist: packet history timestamp or NULL
+ */
+u32 dccp_sample_rtt(struct sock *sk, struct timeval *t_recv,
+				     struct timeval *t_hist)
+{
+	struct dccp_sock *dp = dccp_sk(sk);
+	struct dccp_options_received *or = &dp->dccps_options_received;
+	suseconds_t delta;
+
+	if (t_hist == NULL) {
+		if (!or->dccpor_timestamp_echo) {
+			DCCP_WARN("packet without timestamp echo\n");
+			return DCCP_SANE_RTT_MAX;
+		}
+		timeval_sub_usecs(t_recv, or->dccpor_timestamp_echo * 10);
+		delta = timeval_usecs(t_recv);
+	} else
+		delta = timeval_delta(t_recv, t_hist);
+
+	delta -= or->dccpor_elapsed_time * 10;		/* either set or 0 */
+
+	if (unlikely(delta <= 0)) {
+		DCCP_WARN("unusable RTT sample %ld, using min\n", (long)delta);
+		return DCCP_SANE_RTT_MIN;
+	}
+	if (unlikely(delta - (suseconds_t)DCCP_SANE_RTT_MAX > 0)) {
+		DCCP_WARN("RTT sample %ld too large, using max\n", (long)delta);
+		return DCCP_SANE_RTT_MAX;
+	}
+
+	return delta;
+}
+
+EXPORT_SYMBOL_GPL(dccp_sample_rtt);
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index 4a83978..718f2fa 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -207,8 +207,8 @@
 							(iph->ihl << 2));
 	struct dccp_sock *dp;
 	struct inet_sock *inet;
-	const int type = skb->h.icmph->type;
-	const int code = skb->h.icmph->code;
+	const int type = icmp_hdr(skb)->type;
+	const int code = icmp_hdr(skb)->code;
 	struct sock *sk;
 	__u64 seq;
 	int err;
@@ -363,8 +363,8 @@
 
 static inline u64 dccp_v4_init_sequence(const struct sk_buff *skb)
 {
-	return secure_dccp_sequence_number(skb->nh.iph->daddr,
-					   skb->nh.iph->saddr,
+	return secure_dccp_sequence_number(ip_hdr(skb)->daddr,
+					   ip_hdr(skb)->saddr,
 					   dccp_hdr(skb)->dccph_dport,
 					   dccp_hdr(skb)->dccph_sport);
 }
@@ -405,7 +405,7 @@
 	newinet->opt	   = ireq->opt;
 	ireq->opt	   = NULL;
 	newinet->mc_index  = inet_iif(skb);
-	newinet->mc_ttl	   = skb->nh.iph->ttl;
+	newinet->mc_ttl	   = ip_hdr(skb)->ttl;
 	newinet->id	   = jiffies;
 
 	dccp_sync_mss(newsk, dst_mtu(dst));
@@ -428,7 +428,7 @@
 static struct sock *dccp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
 {
 	const struct dccp_hdr *dh = dccp_hdr(skb);
-	const struct iphdr *iph = skb->nh.iph;
+	const struct iphdr *iph = ip_hdr(skb);
 	struct sock *nsk;
 	struct request_sock **prev;
 	/* Find possible connection requests. */
@@ -460,8 +460,8 @@
 	struct rtable *rt;
 	struct flowi fl = { .oif = ((struct rtable *)skb->dst)->rt_iif,
 			    .nl_u = { .ip4_u =
-				      { .daddr = skb->nh.iph->saddr,
-					.saddr = skb->nh.iph->daddr,
+				      { .daddr = ip_hdr(skb)->saddr,
+					.saddr = ip_hdr(skb)->daddr,
 					.tos = RT_CONN_FLAGS(sk) } },
 			    .proto = sk->sk_protocol,
 			    .uli_u = { .ports =
@@ -513,6 +513,7 @@
 {
 	int err;
 	struct dccp_hdr *rxdh = dccp_hdr(rxskb), *dh;
+	const struct iphdr *rxiph;
 	const int dccp_hdr_reset_len = sizeof(struct dccp_hdr) +
 				       sizeof(struct dccp_hdr_ext) +
 				       sizeof(struct dccp_hdr_reset);
@@ -559,13 +560,13 @@
 	dccp_hdr_set_ack(dccp_hdr_ack_bits(skb), DCCP_SKB_CB(rxskb)->dccpd_seq);
 
 	dccp_csum_outgoing(skb);
-	dh->dccph_checksum = dccp_v4_csum_finish(skb, rxskb->nh.iph->saddr,
-						      rxskb->nh.iph->daddr);
+	rxiph = ip_hdr(rxskb);
+	dh->dccph_checksum = dccp_v4_csum_finish(skb, rxiph->saddr,
+						 rxiph->daddr);
 
 	bh_lock_sock(dccp_v4_ctl_socket->sk);
 	err = ip_build_and_send_pkt(skb, dccp_v4_ctl_socket->sk,
-				    rxskb->nh.iph->daddr,
-				    rxskb->nh.iph->saddr, NULL);
+				    rxiph->daddr, rxiph->saddr, NULL);
 	bh_unlock_sock(dccp_v4_ctl_socket->sk);
 
 	if (net_xmit_eval(err) == 0) {
@@ -640,8 +641,8 @@
 		goto drop_and_free;
 
 	ireq = inet_rsk(req);
-	ireq->loc_addr = skb->nh.iph->daddr;
-	ireq->rmt_addr = skb->nh.iph->saddr;
+	ireq->loc_addr = ip_hdr(skb)->daddr;
+	ireq->rmt_addr = ip_hdr(skb)->saddr;
 	ireq->opt	= NULL;
 
 	/*
@@ -809,6 +810,7 @@
 static int dccp_v4_rcv(struct sk_buff *skb)
 {
 	const struct dccp_hdr *dh;
+	const struct iphdr *iph;
 	struct sock *sk;
 	int min_cov;
 
@@ -817,8 +819,9 @@
 	if (dccp_invalid_packet(skb))
 		goto discard_it;
 
+	iph = ip_hdr(skb);
 	/* Step 1: If header checksum is incorrect, drop packet and return */
-	if (dccp_v4_csum_finish(skb, skb->nh.iph->saddr, skb->nh.iph->daddr)) {
+	if (dccp_v4_csum_finish(skb, iph->saddr, iph->daddr)) {
 		DCCP_WARN("dropped packet with invalid checksum\n");
 		goto discard_it;
 	}
@@ -832,8 +835,8 @@
 		      "src=%u.%u.%u.%u@%-5d "
 		      "dst=%u.%u.%u.%u@%-5d seq=%llu",
 		      dccp_packet_name(dh->dccph_type),
-		      NIPQUAD(skb->nh.iph->saddr), ntohs(dh->dccph_sport),
-		      NIPQUAD(skb->nh.iph->daddr), ntohs(dh->dccph_dport),
+		      NIPQUAD(iph->saddr), ntohs(dh->dccph_sport),
+		      NIPQUAD(iph->daddr), ntohs(dh->dccph_dport),
 		      (unsigned long long) DCCP_SKB_CB(skb)->dccpd_seq);
 
 	if (dccp_packet_without_ack(skb)) {
@@ -848,10 +851,8 @@
 	/* Step 2:
 	 *	Look up flow ID in table and get corresponding socket */
 	sk = __inet_lookup(&dccp_hashinfo,
-			   skb->nh.iph->saddr, dh->dccph_sport,
-			   skb->nh.iph->daddr, dh->dccph_dport,
-			   inet_iif(skb));
-
+			   iph->saddr, dh->dccph_sport,
+			   iph->daddr, dh->dccph_dport, inet_iif(skb));
 	/*
 	 * Step 2:
 	 *	If no socket ...
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index 7f51e8d..64eac25 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -84,8 +84,8 @@
 
 static inline __u32 dccp_v6_init_sequence(struct sk_buff *skb)
 {
-	return secure_dccpv6_sequence_number(skb->nh.ipv6h->daddr.s6_addr32,
-					     skb->nh.ipv6h->saddr.s6_addr32,
+	return secure_dccpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32,
+					     ipv6_hdr(skb)->saddr.s6_addr32,
 					     dccp_hdr(skb)->dccph_dport,
 					     dccp_hdr(skb)->dccph_sport     );
 
@@ -261,8 +261,8 @@
 
 			if (rxopt->srcrt)
 				opt = ipv6_invert_rthdr(sk,
-					(struct ipv6_rt_hdr *)(pktopts->nh.raw +
-							       rxopt->srcrt));
+			  (struct ipv6_rt_hdr *)(skb_network_header(pktopts) +
+						 rxopt->srcrt));
 		}
 
 		if (opt != NULL && opt->srcrt != NULL) {
@@ -313,6 +313,7 @@
 static void dccp_v6_ctl_send_reset(struct sock *sk, struct sk_buff *rxskb)
 {
 	struct dccp_hdr *rxdh = dccp_hdr(rxskb), *dh;
+	struct ipv6hdr *rxip6h;
 	const u32 dccp_hdr_reset_len = sizeof(struct dccp_hdr) +
 				       sizeof(struct dccp_hdr_ext) +
 				       sizeof(struct dccp_hdr_reset);
@@ -352,12 +353,13 @@
 	dccp_hdr_set_ack(dccp_hdr_ack_bits(skb), DCCP_SKB_CB(rxskb)->dccpd_seq);
 
 	dccp_csum_outgoing(skb);
-	dh->dccph_checksum = dccp_v6_csum_finish(skb, &rxskb->nh.ipv6h->saddr,
-						      &rxskb->nh.ipv6h->daddr);
+	rxip6h = ipv6_hdr(rxskb);
+	dh->dccph_checksum = dccp_v6_csum_finish(skb, &rxip6h->saddr,
+						      &rxip6h->daddr);
 
 	memset(&fl, 0, sizeof(fl));
-	ipv6_addr_copy(&fl.fl6_dst, &rxskb->nh.ipv6h->saddr);
-	ipv6_addr_copy(&fl.fl6_src, &rxskb->nh.ipv6h->daddr);
+	ipv6_addr_copy(&fl.fl6_dst, &rxip6h->saddr);
+	ipv6_addr_copy(&fl.fl6_src, &rxip6h->daddr);
 
 	fl.proto = IPPROTO_DCCP;
 	fl.oif = inet6_iif(rxskb);
@@ -390,7 +392,7 @@
 static struct sock *dccp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
 {
 	const struct dccp_hdr *dh = dccp_hdr(skb);
-	const struct ipv6hdr *iph = skb->nh.ipv6h;
+	const struct ipv6hdr *iph = ipv6_hdr(skb);
 	struct sock *nsk;
 	struct request_sock **prev;
 	/* Find possible connection requests. */
@@ -460,8 +462,8 @@
 		goto drop_and_free;
 
 	ireq6 = inet6_rsk(req);
-	ipv6_addr_copy(&ireq6->rmt_addr, &skb->nh.ipv6h->saddr);
-	ipv6_addr_copy(&ireq6->loc_addr, &skb->nh.ipv6h->daddr);
+	ipv6_addr_copy(&ireq6->rmt_addr, &ipv6_hdr(skb)->saddr);
+	ipv6_addr_copy(&ireq6->loc_addr, &ipv6_hdr(skb)->daddr);
 	ireq6->pktopts	= NULL;
 
 	if (ipv6_opt_accepted(sk, skb) ||
@@ -546,7 +548,7 @@
 		newnp->pktoptions  = NULL;
 		newnp->opt	   = NULL;
 		newnp->mcast_oif   = inet6_iif(skb);
-		newnp->mcast_hops  = skb->nh.ipv6h->hop_limit;
+		newnp->mcast_hops  = ipv6_hdr(skb)->hop_limit;
 
 		/*
 		 * No need to charge this sock to the relevant IPv6 refcnt debug socks count
@@ -573,8 +575,8 @@
 
 		if (rxopt->srcrt)
 			opt = ipv6_invert_rthdr(sk,
-				(struct ipv6_rt_hdr *)(ireq6->pktopts->nh.raw +
-						       rxopt->srcrt));
+		   (struct ipv6_rt_hdr *)(skb_network_header(ireq6->pktopts) +
+					  rxopt->srcrt));
 	}
 
 	if (dst == NULL) {
@@ -653,7 +655,7 @@
 	}
 	newnp->opt	  = NULL;
 	newnp->mcast_oif  = inet6_iif(skb);
-	newnp->mcast_hops = skb->nh.ipv6h->hop_limit;
+	newnp->mcast_hops = ipv6_hdr(skb)->hop_limit;
 
 	/*
 	 * Clone native IPv6 options from listening socket (if any)
@@ -826,8 +828,8 @@
 		goto discard_it;
 
 	/* Step 1: If header checksum is incorrect, drop packet and return. */
-	if (dccp_v6_csum_finish(skb, &skb->nh.ipv6h->saddr,
-				     &skb->nh.ipv6h->daddr)) {
+	if (dccp_v6_csum_finish(skb, &ipv6_hdr(skb)->saddr,
+				     &ipv6_hdr(skb)->daddr)) {
 		DCCP_WARN("dropped packet with invalid checksum\n");
 		goto discard_it;
 	}
@@ -844,9 +846,9 @@
 
 	/* Step 2:
 	 *	Look up flow ID in table and get corresponding socket */
-	sk = __inet6_lookup(&dccp_hashinfo, &skb->nh.ipv6h->saddr,
+	sk = __inet6_lookup(&dccp_hashinfo, &ipv6_hdr(skb)->saddr,
 			    dh->dccph_sport,
-			    &skb->nh.ipv6h->daddr, ntohs(dh->dccph_dport),
+			    &ipv6_hdr(skb)->daddr, ntohs(dh->dccph_dport),
 			    inet6_iif(skb));
 	/*
 	 * Step 2:
diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c
index 6d235b3..e18e249 100644
--- a/net/dccp/minisocks.c
+++ b/net/dccp/minisocks.c
@@ -27,7 +27,7 @@
 struct inet_timewait_death_row dccp_death_row = {
 	.sysctl_max_tw_buckets = NR_FILE * 2,
 	.period		= DCCP_TIMEWAIT_LEN / INET_TWDR_TWKILL_SLOTS,
-	.death_lock	= SPIN_LOCK_UNLOCKED,
+	.death_lock	= __SPIN_LOCK_UNLOCKED(dccp_death_row.death_lock),
 	.hashinfo	= &dccp_hashinfo,
 	.tw_timer	= TIMER_INITIALIZER(inet_twdr_hangman, 0,
 					    (unsigned long)&dccp_death_row),
diff --git a/net/dccp/options.c b/net/dccp/options.c
index ca13f77..34d536d 100644
--- a/net/dccp/options.c
+++ b/net/dccp/options.c
@@ -29,8 +29,6 @@
 int sysctl_dccp_feat_send_ack_vector = DCCPF_INITIAL_SEND_ACK_VECTOR;
 int sysctl_dccp_feat_send_ndp_count  = DCCPF_INITIAL_SEND_NDP_COUNT;
 
-EXPORT_SYMBOL_GPL(sysctl_dccp_feat_sequence_window);
-
 void dccp_minisock_init(struct dccp_minisock *dmsk)
 {
 	dmsk->dccpms_sequence_window = sysctl_dccp_feat_sequence_window;
@@ -174,21 +172,25 @@
 			opt_recv->dccpor_timestamp_echo = ntohl(*(__be32 *)value);
 
 			dccp_pr_debug("%s rx opt: TIMESTAMP_ECHO=%u, len=%d, "
-				      "ackno=%llu, ",  dccp_role(sk),
+				      "ackno=%llu", dccp_role(sk),
 				      opt_recv->dccpor_timestamp_echo,
 				      len + 2,
 				      (unsigned long long)
 				      DCCP_SKB_CB(skb)->dccpd_ack_seq);
 
 
-			if (len == 4)
+			if (len == 4) {
+				dccp_pr_debug_cat("\n");
 				break;
+			}
 
 			if (len == 6)
 				elapsed_time = ntohs(*(__be16 *)(value + 4));
 			else
 				elapsed_time = ntohl(*(__be32 *)(value + 4));
 
+			dccp_pr_debug_cat(", ELAPSED_TIME=%d\n", elapsed_time);
+
 			/* Give precedence to the biggest ELAPSED_TIME */
 			if (elapsed_time > opt_recv->dccpor_elapsed_time)
 				opt_recv->dccpor_elapsed_time = elapsed_time;
@@ -565,6 +567,14 @@
 	    dccp_insert_options_feat(sk, skb))
 		return -1;
 
+	/*
+	 * Obtain RTT sample from Request/Response exchange.
+	 * This is currently used in CCID 3 initialisation.
+	 */
+	if (DCCP_SKB_CB(skb)->dccpd_type == DCCP_PKT_REQUEST &&
+	    dccp_insert_option_timestamp(sk, skb))
+		return -1;
+
 	/* XXX: insert other options when appropriate */
 
 	if (DCCP_SKB_CB(skb)->dccpd_opt_len != 0) {
diff --git a/net/dccp/output.c b/net/dccp/output.c
index aa21cc4..c8d843e 100644
--- a/net/dccp/output.c
+++ b/net/dccp/output.c
@@ -194,6 +194,7 @@
 		rc = ccid_hc_tx_send_packet(dp->dccps_hc_tx_ccid, sk, skb);
 		if (rc <= 0)
 			break;
+		dccp_pr_debug("delayed send by %d msec\n", rc);
 		delay = msecs_to_jiffies(rc);
 		sk->sk_write_pending++;
 		release_sock(sk);
@@ -255,7 +256,7 @@
 				DCCP_BUG("err=%d after ccid_hc_tx_packet_sent",
 					 err);
 		} else {
-			dccp_pr_debug("packet discarded\n");
+			dccp_pr_debug("packet discarded due to err=%d\n", err);
 			kfree_skb(skb);
 		}
 	}
diff --git a/net/dccp/probe.c b/net/dccp/probe.c
index 3b1f509..1f5e3ba 100644
--- a/net/dccp/probe.c
+++ b/net/dccp/probe.c
@@ -90,15 +90,18 @@
 	if (port == 0 || ntohs(inet->dport) == port ||
 	    ntohs(inet->sport) == port) {
 		if (hctx)
-			printl("%d.%d.%d.%d:%u %d.%d.%d.%d:%u %d %d %d %d %d\n",
-			   NIPQUAD(inet->saddr), ntohs(inet->sport),
-			   NIPQUAD(inet->daddr), ntohs(inet->dport), size,
-			   hctx->ccid3hctx_s, hctx->ccid3hctx_rtt,
-			   hctx->ccid3hctx_p, hctx->ccid3hctx_t_ipi);
+			printl("%d.%d.%d.%d:%u %d.%d.%d.%d:%u %d %d %d %d %u "
+			       "%llu %llu %d\n",
+			       NIPQUAD(inet->saddr), ntohs(inet->sport),
+			       NIPQUAD(inet->daddr), ntohs(inet->dport), size,
+			       hctx->ccid3hctx_s, hctx->ccid3hctx_rtt,
+			       hctx->ccid3hctx_p, hctx->ccid3hctx_x_calc,
+			       hctx->ccid3hctx_x_recv >> 6,
+			       hctx->ccid3hctx_x >> 6, hctx->ccid3hctx_t_ipi);
 		else
 			printl("%d.%d.%d.%d:%u %d.%d.%d.%d:%u %d\n",
-			   NIPQUAD(inet->saddr), ntohs(inet->sport),
-			   NIPQUAD(inet->daddr), ntohs(inet->dport), size);
+			       NIPQUAD(inet->saddr), ntohs(inet->sport),
+			       NIPQUAD(inet->daddr), ntohs(inet->dport), size);
 	}
 
 	jprobe_return();
diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c
index c6568d6..a205eaa 100644
--- a/net/decnet/af_decnet.c
+++ b/net/decnet/af_decnet.c
@@ -2413,6 +2413,7 @@
 static void __exit decnet_exit(void)
 {
 	sock_unregister(AF_DECnet);
+	rtnl_unregister_all(PF_DECnet);
 	dev_remove_pack(&dn_dix_packet_type);
 
 	dn_unregister_sysctl();
diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c
index 060d725..5c2a995 100644
--- a/net/decnet/dn_dev.c
+++ b/net/decnet/dn_dev.c
@@ -799,7 +799,6 @@
 	skip_ndevs = cb->args[0];
 	skip_naddr = cb->args[1];
 
-	read_lock(&dev_base_lock);
 	for (dev = dev_base, idx = 0; dev; dev = dev->next, idx++) {
 		if (idx < skip_ndevs)
 			continue;
@@ -824,8 +823,6 @@
 		}
 	}
 done:
-	read_unlock(&dev_base_lock);
-
 	cb->args[0] = idx;
 	cb->args[1] = dn_idx;
 
@@ -913,7 +910,7 @@
 	pktlen = (__le16 *)skb_push(skb,2);
 	*pktlen = dn_htons(skb->len - 2);
 
-	skb->nh.raw = skb->data;
+	skb_reset_network_header(skb);
 
 	dn_rt_finish_output(skb, dn_rt_all_rt_mcast, msg->id);
 }
@@ -1005,7 +1002,7 @@
 	pktlen = (__le16 *)skb_push(skb, 2);
 	*pktlen = dn_htons(skb->len - 2);
 
-	skb->nh.raw = skb->data;
+	skb_reset_network_header(skb);
 
 	if (dn_am_i_a_router(dn, dn_db, ifa)) {
 		struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
@@ -1447,24 +1444,6 @@
 
 #endif /* CONFIG_PROC_FS */
 
-static struct rtnetlink_link dnet_rtnetlink_table[RTM_NR_MSGTYPES] =
-{
-	[RTM_NEWADDR  - RTM_BASE] = { .doit	= dn_nl_newaddr,	},
-	[RTM_DELADDR  - RTM_BASE] = { .doit	= dn_nl_deladdr,	},
-	[RTM_GETADDR  - RTM_BASE] = { .dumpit	= dn_nl_dump_ifaddr,	},
-#ifdef CONFIG_DECNET_ROUTER
-	[RTM_NEWROUTE - RTM_BASE] = { .doit	= dn_fib_rtm_newroute,	},
-	[RTM_DELROUTE - RTM_BASE] = { .doit	= dn_fib_rtm_delroute,	},
-	[RTM_GETROUTE - RTM_BASE] = { .doit	= dn_cache_getroute,
-				      .dumpit	= dn_fib_dump,		},
-	[RTM_GETRULE  - RTM_BASE] = { .dumpit	= dn_fib_dump_rules,	},
-#else
-	[RTM_GETROUTE - RTM_BASE] = { .doit	= dn_cache_getroute,
-				      .dumpit	= dn_cache_dump,	},
-#endif
-
-};
-
 static int __initdata addr[2];
 module_param_array(addr, int, NULL, 0444);
 MODULE_PARM_DESC(addr, "The DECnet address of this machine: area,node");
@@ -1485,7 +1464,9 @@
 
 	dn_dev_devices_on();
 
-	rtnetlink_links[PF_DECnet] = dnet_rtnetlink_table;
+	rtnl_register(PF_DECnet, RTM_NEWADDR, dn_nl_newaddr, NULL);
+	rtnl_register(PF_DECnet, RTM_DELADDR, dn_nl_deladdr, NULL);
+	rtnl_register(PF_DECnet, RTM_GETADDR, NULL, dn_nl_dump_ifaddr);
 
 	proc_net_fops_create("decnet_dev", S_IRUGO, &dn_dev_seq_fops);
 
@@ -1500,8 +1481,6 @@
 
 void __exit dn_dev_cleanup(void)
 {
-	rtnetlink_links[PF_DECnet] = NULL;
-
 #ifdef CONFIG_SYSCTL
 	{
 		int i;
diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c
index 82d58a9..310a862 100644
--- a/net/decnet/dn_fib.c
+++ b/net/decnet/dn_fib.c
@@ -504,7 +504,7 @@
 	return 0;
 }
 
-int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
 	struct dn_fib_table *tb;
 	struct rtattr **rta = arg;
@@ -520,7 +520,7 @@
 	return -ESRCH;
 }
 
-int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
 	struct dn_fib_table *tb;
 	struct rtattr **rta = arg;
@@ -748,11 +748,13 @@
 
 void __init dn_fib_init(void)
 {
-
 	dn_fib_table_init();
 	dn_fib_rules_init();
 
 	register_dnaddr_notifier(&dn_fib_dnaddr_notifier);
+
+	rtnl_register(PF_DECnet, RTM_NEWROUTE, dn_fib_rtm_newroute, NULL);
+	rtnl_register(PF_DECnet, RTM_DELROUTE, dn_fib_rtm_delroute, NULL);
 }
 
 
diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c
index bf701cf..4bf066c 100644
--- a/net/decnet/dn_neigh.c
+++ b/net/decnet/dn_neigh.c
@@ -261,7 +261,7 @@
 	lp->s_class  = 0;
 	lp->pt       = 0;
 
-	skb->nh.raw = skb->data;
+	skb_reset_network_header(skb);
 
 	return NF_HOOK(PF_DECnet, NF_DN_POST_ROUTING, skb, NULL, neigh->dev, dn_neigh_output_packet);
 }
@@ -300,7 +300,7 @@
 	sp->srcnode    = cb->src;
 	sp->forward    = cb->hops & 0x3f;
 
-	skb->nh.raw = skb->data;
+	skb_reset_network_header(skb);
 
 	return NF_HOOK(PF_DECnet, NF_DN_POST_ROUTING, skb, NULL, neigh->dev, dn_neigh_output_packet);
 }
@@ -342,7 +342,7 @@
 	sp->srcnode  = cb->src & dn_htons(0x03ff);
 	sp->forward  = cb->hops & 0x3f;
 
-	skb->nh.raw = skb->data;
+	skb_reset_network_header(skb);
 
 	return NF_HOOK(PF_DECnet, NF_DN_POST_ROUTING, skb, NULL, neigh->dev, dn_neigh_output_packet);
 }
diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c
index 9d20904..4074a6e 100644
--- a/net/decnet/dn_nsp_in.c
+++ b/net/decnet/dn_nsp_in.c
@@ -362,7 +362,8 @@
 			u16 dlen = *skb->data;
 			if ((dlen <= 16) && (dlen <= skb->len)) {
 				scp->conndata_in.opt_optl = dn_htons(dlen);
-				memcpy(scp->conndata_in.opt_data, skb->data + 1, dlen);
+				skb_copy_from_linear_data_offset(skb, 1,
+					      scp->conndata_in.opt_data, dlen);
 			}
 		}
 		dn_nsp_send_link(sk, DN_NOCHANGE, 0);
@@ -406,7 +407,7 @@
 		u16 dlen = *skb->data;
 		if ((dlen <= 16) && (dlen <= skb->len)) {
 			scp->discdata_in.opt_optl = dn_htons(dlen);
-			memcpy(scp->discdata_in.opt_data, skb->data + 1, dlen);
+			skb_copy_from_linear_data_offset(skb, 1, scp->discdata_in.opt_data, dlen);
 		}
 	}
 
@@ -725,7 +726,7 @@
 	if (!pskb_may_pull(skb, 2))
 		goto free_out;
 
-	skb->h.raw    = skb->data;
+	skb_reset_transport_header(skb);
 	cb->nsp_flags = *ptr++;
 
 	if (decnet_debug_level & 2)
diff --git a/net/decnet/dn_nsp_out.c b/net/decnet/dn_nsp_out.c
index 2d2cda8..7404653 100644
--- a/net/decnet/dn_nsp_out.c
+++ b/net/decnet/dn_nsp_out.c
@@ -79,7 +79,7 @@
 	struct dst_entry *dst;
 	struct flowi fl;
 
-	skb->h.raw = skb->data;
+	skb_reset_transport_header(skb);
 	scp->stamp = jiffies;
 
 	dst = sk_dst_check(sk, 0);
@@ -681,8 +681,10 @@
 	if (scp->peer.sdn_objnum)
 		type = 0;
 
-	skb_put(skb, dn_sockaddr2username(&scp->peer, skb->tail, type));
-	skb_put(skb, dn_sockaddr2username(&scp->addr, skb->tail, 2));
+	skb_put(skb, dn_sockaddr2username(&scp->peer,
+					  skb_tail_pointer(skb), type));
+	skb_put(skb, dn_sockaddr2username(&scp->addr,
+					  skb_tail_pointer(skb), 2));
 
 	menuver = DN_MENUVER_ACC | DN_MENUVER_USR;
 	if (scp->peer.sdn_flags & SDF_PROXY)
diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c
index c1b5502..5d7337b 100644
--- a/net/decnet/dn_route.c
+++ b/net/decnet/dn_route.c
@@ -77,6 +77,7 @@
 #include <linux/rcupdate.h>
 #include <linux/times.h>
 #include <asm/errno.h>
+#include <net/netlink.h>
 #include <net/neighbour.h>
 #include <net/dst.h>
 #include <net/flow.h>
@@ -386,7 +387,7 @@
 	__le16 tmp;
 
 	/* Add back headers */
-	skb_push(skb, skb->data - skb->nh.raw);
+	skb_push(skb, skb->data - skb_network_header(skb));
 
 	if ((skb = skb_unshare(skb, GFP_ATOMIC)) == NULL)
 		return NET_RX_DROP;
@@ -425,7 +426,7 @@
 	unsigned char tmp[ETH_ALEN];
 
 	/* Add back all headers */
-	skb_push(skb, skb->data - skb->nh.raw);
+	skb_push(skb, skb->data - skb_network_header(skb));
 
 	if ((skb = skb_unshare(skb, GFP_ATOMIC)) == NULL)
 		return NET_RX_DROP;
@@ -504,7 +505,7 @@
 		goto drop_it;
 
 	skb_pull(skb, 20);
-	skb->h.raw = skb->data;
+	skb_reset_transport_header(skb);
 
 	/* Destination info */
 	ptr += 2;
@@ -542,7 +543,7 @@
 		goto drop_it;
 
 	skb_pull(skb, 5);
-	skb->h.raw = skb->data;
+	skb_reset_transport_header(skb);
 
 	cb->dst = *(__le16 *)ptr;
 	ptr += 2;
@@ -615,7 +616,7 @@
 		flags = *skb->data;
 	}
 
-	skb->nh.raw = skb->data;
+	skb_reset_network_header(skb);
 
 	/*
 	 * Weed out future version DECnet
@@ -1468,7 +1469,7 @@
 	struct dn_route *rt = (struct dn_route *)skb->dst;
 	struct rtmsg *r;
 	struct nlmsghdr *nlh;
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	long expires;
 
 	nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*r), flags);
@@ -1509,19 +1510,19 @@
 	if (rt->fl.iif)
 		RTA_PUT(skb, RTA_IIF, sizeof(int), &rt->fl.iif);
 
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
 nlmsg_failure:
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
 /*
  * This is called by both endnodes and routers now.
  */
-int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg)
+static int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg)
 {
 	struct rtattr **rta = arg;
 	struct rtmsg *rtm = NLMSG_DATA(nlh);
@@ -1537,7 +1538,7 @@
 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
 	if (skb == NULL)
 		return -ENOBUFS;
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	cb = DN_SKB_CB(skb);
 
 	if (rta[RTA_SRC-1])
@@ -1812,6 +1813,13 @@
 	dn_dst_ops.gc_thresh = (dn_rt_hash_mask + 1);
 
 	proc_net_fops_create("decnet_cache", S_IRUGO, &dn_rt_cache_seq_fops);
+
+#ifdef CONFIG_DECNET_ROUTER
+	rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute, dn_fib_dump);
+#else
+	rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute,
+		      dn_cache_dump);
+#endif
 }
 
 void __exit dn_route_cleanup(void)
diff --git a/net/decnet/dn_rules.c b/net/decnet/dn_rules.c
index 5e86dd5..17a1932 100644
--- a/net/decnet/dn_rules.c
+++ b/net/decnet/dn_rules.c
@@ -31,6 +31,7 @@
 #include <net/dn_fib.h>
 #include <net/dn_neigh.h>
 #include <net/dn_dev.h>
+#include <net/dn_route.h>
 
 static struct fib_rules_ops dn_fib_rules_ops;
 
@@ -239,9 +240,9 @@
 	return 0;
 }
 
-int dn_fib_dump_rules(struct sk_buff *skb, struct netlink_callback *cb)
+static void dn_fib_rule_flush_cache(void)
 {
-	return fib_rules_dump(skb, cb, AF_DECnet);
+	dn_rt_cache_flush(-1);
 }
 
 static struct fib_rules_ops dn_fib_rules_ops = {
@@ -254,6 +255,7 @@
 	.compare	= dn_fib_rule_compare,
 	.fill		= dn_fib_rule_fill,
 	.default_pref	= dn_fib_rule_default_pref,
+	.flush_cache	= dn_fib_rule_flush_cache,
 	.nlgroup	= RTNLGRP_DECnet_RULE,
 	.policy		= dn_fib_rule_policy,
 	.rules_list	= &dn_fib_rules,
diff --git a/net/decnet/dn_table.c b/net/decnet/dn_table.c
index 780a141..d6615c9 100644
--- a/net/decnet/dn_table.c
+++ b/net/decnet/dn_table.c
@@ -28,6 +28,7 @@
 #include <asm/uaccess.h>
 #include <linux/route.h> /* RTF_xxx */
 #include <net/neighbour.h>
+#include <net/netlink.h>
 #include <net/dst.h>
 #include <net/flow.h>
 #include <net/fib_rules.h>
@@ -295,7 +296,7 @@
 {
 	struct rtmsg *rtm;
 	struct nlmsghdr *nlh;
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 
 	nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*rtm), flags);
 	rtm = NLMSG_DATA(nlh);
@@ -337,19 +338,19 @@
 			nhp->rtnh_ifindex = nh->nh_oif;
 			if (nh->nh_gw)
 				RTA_PUT(skb, RTA_GATEWAY, 2, &nh->nh_gw);
-			nhp->rtnh_len = skb->tail - (unsigned char *)nhp;
+			nhp->rtnh_len = skb_tail_pointer(skb) - (unsigned char *)nhp;
 		} endfor_nexthops(fi);
 		mp_head->rta_type = RTA_MULTIPATH;
-		mp_head->rta_len = skb->tail - (u8*)mp_head;
+		mp_head->rta_len = skb_tail_pointer(skb) - (u8 *)mp_head;
 	}
 
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
 
 nlmsg_failure:
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -EMSGSIZE;
 }
 
diff --git a/net/decnet/netfilter/dn_rtmsg.c b/net/decnet/netfilter/dn_rtmsg.c
index 0e62def..6962346 100644
--- a/net/decnet/netfilter/dn_rtmsg.c
+++ b/net/decnet/netfilter/dn_rtmsg.c
@@ -33,7 +33,7 @@
 {
 	struct sk_buff *skb = NULL;
 	size_t size;
-	unsigned char *old_tail;
+	sk_buff_data_t old_tail;
 	struct nlmsghdr *nlh;
 	unsigned char *ptr;
 	struct nf_dn_rtmsg *rtm;
@@ -48,7 +48,7 @@
 	rtm = (struct nf_dn_rtmsg *)NLMSG_DATA(nlh);
 	rtm->nfdn_ifindex = rt_skb->dev->ifindex;
 	ptr = NFDN_RTMSG(rtm);
-	memcpy(ptr, rt_skb->data, rt_skb->len);
+	skb_copy_from_linear_data(rt_skb, ptr, rt_skb->len);
 	nlh->nlmsg_len = skb->tail - old_tail;
 	return skb;
 
@@ -102,7 +102,7 @@
 
 static inline void dnrmg_receive_user_skb(struct sk_buff *skb)
 {
-	struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data;
+	struct nlmsghdr *nlh = nlmsg_hdr(skb);
 
 	if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
 		return;
@@ -138,7 +138,7 @@
 	int rv = 0;
 
 	dnrmg = netlink_kernel_create(NETLINK_DNRTMSG, DNRNG_NLGRP_MAX,
-				      dnrmg_receive_user_sk, THIS_MODULE);
+				      dnrmg_receive_user_sk, NULL, THIS_MODULE);
 	if (dnrmg == NULL) {
 		printk(KERN_ERR "dn_rtmsg: Cannot create netlink socket");
 		return -ENOMEM;
diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c
index bc12e36..b5524f3 100644
--- a/net/econet/af_econet.c
+++ b/net/econet/af_econet.c
@@ -162,7 +162,7 @@
 	err = memcpy_toiovec(msg->msg_iov, skb->data, copied);
 	if (err)
 		goto out_free;
-	skb_get_timestamp(skb, &sk->sk_stamp);
+	sk->sk_stamp = skb->tstamp;
 
 	if (msg->msg_name)
 		memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
@@ -345,7 +345,7 @@
 			goto out_unlock;
 
 		skb_reserve(skb, LL_RESERVED_SPACE(dev));
-		skb->nh.raw = skb->data;
+		skb_reset_network_header(skb);
 
 		eb = (struct ec_cb *)&skb->cb;
 
@@ -366,7 +366,7 @@
 			fh->cb = cb;
 			fh->port = port;
 			if (sock->type != SOCK_DGRAM) {
-				skb->tail = skb->data;
+				skb_reset_tail_pointer(skb);
 				skb->len = 0;
 			} else if (res < 0)
 				goto out_free;
@@ -727,6 +727,9 @@
 		case SIOCGSTAMP:
 			return sock_get_timestamp(sk, argp);
 
+		case SIOCGSTAMPNS:
+			return sock_get_timestampns(sk, argp);
+
 		case SIOCSIFADDR:
 		case SIOCGIFADDR:
 			return ec_dev_ioctl(sock, cmd, argp);
@@ -845,7 +848,7 @@
 
 static void aun_incoming(struct sk_buff *skb, struct aunhdr *ah, size_t len)
 {
-	struct iphdr *ip = skb->nh.iph;
+	struct iphdr *ip = ip_hdr(skb);
 	unsigned char stn = ntohl(ip->saddr) & 0xff;
 	struct sock *sk;
 	struct sk_buff *newskb;
@@ -940,10 +943,10 @@
 		printk(KERN_DEBUG "AUN: recvfrom() error %d\n", -err);
 	}
 
-	data = skb->h.raw + sizeof(struct udphdr);
+	data = skb_transport_header(skb) + sizeof(struct udphdr);
 	ah = (struct aunhdr *)data;
 	len = skb->len - sizeof(struct udphdr);
-	ip = skb->nh.iph;
+	ip = ip_hdr(skb);
 
 	switch (ah->code)
 	{
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index 7391f55..0ac2524 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -156,7 +156,8 @@
 	struct ethhdr *eth;
 	unsigned char *rawp;
 
-	skb->mac.raw = skb->data;
+	skb->dev = dev;
+	skb_reset_mac_header(skb);
 	skb_pull(skb, ETH_HLEN);
 	eth = eth_hdr(skb);
 
@@ -228,7 +229,7 @@
 	eth = (struct ethhdr *)
 	    (((u8 *) hh->hh_data) + (HH_DATA_OFF(sizeof(*eth))));
 
-	if (type == __constant_htons(ETH_P_802_3))
+	if (type == htons(ETH_P_802_3))
 		return -1;
 
 	eth->h_proto = type;
diff --git a/net/ieee80211/Kconfig b/net/ieee80211/Kconfig
index 6ef766e..1438aded 100644
--- a/net/ieee80211/Kconfig
+++ b/net/ieee80211/Kconfig
@@ -56,7 +56,8 @@
 
 config IEEE80211_CRYPT_TKIP
 	tristate "IEEE 802.11i TKIP encryption"
-	depends on IEEE80211 && NET_RADIO
+	depends on IEEE80211
+	select WIRELESS_EXT
 	select CRYPTO
 	select CRYPTO_MICHAEL_MIC
 	select CRYPTO_ECB
diff --git a/net/ieee80211/ieee80211_crypt.c b/net/ieee80211/ieee80211_crypt.c
index 5ed0a98..df5592c 100644
--- a/net/ieee80211/ieee80211_crypt.c
+++ b/net/ieee80211/ieee80211_crypt.c
@@ -1,7 +1,7 @@
 /*
  * Host AP crypto routines
  *
- * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
  * Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com>
  *
  * This program is free software; you can redistribute it and/or modify
diff --git a/net/ieee80211/ieee80211_crypt_ccmp.c b/net/ieee80211/ieee80211_crypt_ccmp.c
index 35aa342..b016b41 100644
--- a/net/ieee80211/ieee80211_crypt_ccmp.c
+++ b/net/ieee80211/ieee80211_crypt_ccmp.c
@@ -1,7 +1,7 @@
 /*
  * Host AP crypt: host-based CCMP encryption implementation for Host AP driver
  *
- * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
  *
  * 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
@@ -338,7 +338,7 @@
 
 	if (ccmp_replay_check(pn, key->rx_pn)) {
 		if (net_ratelimit()) {
-			printk(KERN_DEBUG "CCMP: replay detected: STA=" MAC_FMT
+			IEEE80211_DEBUG_DROP("CCMP: replay detected: STA=" MAC_FMT
 			       " previous PN %02x%02x%02x%02x%02x%02x "
 			       "received PN %02x%02x%02x%02x%02x%02x\n",
 			       MAC_ARG(hdr->addr2), MAC_ARG(key->rx_pn),
diff --git a/net/ieee80211/ieee80211_crypt_tkip.c b/net/ieee80211/ieee80211_crypt_tkip.c
index fc1f99a..5a48d8e 100644
--- a/net/ieee80211/ieee80211_crypt_tkip.c
+++ b/net/ieee80211/ieee80211_crypt_tkip.c
@@ -1,7 +1,7 @@
 /*
  * Host AP crypt: host-based TKIP encryption implementation for Host AP driver
  *
- * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
  *
  * 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
@@ -465,7 +465,7 @@
 
 	if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) {
 		if (net_ratelimit()) {
-			printk(KERN_DEBUG "TKIP: replay detected: STA=" MAC_FMT
+			IEEE80211_DEBUG_DROP("TKIP: replay detected: STA=" MAC_FMT
 			       " previous TSC %08x%04x received TSC "
 			       "%08x%04x\n", MAC_ARG(hdr->addr2),
 			       tkey->rx_iv32, tkey->rx_iv16, iv32, iv16);
@@ -507,7 +507,7 @@
 			tkey->rx_phase1_done = 0;
 		}
 		if (net_ratelimit()) {
-			printk(KERN_DEBUG "TKIP: ICV error detected: STA="
+			IEEE80211_DEBUG_DROP("TKIP: ICV error detected: STA="
 			       MAC_FMT "\n", MAC_ARG(hdr->addr2));
 		}
 		tkey->dot11RSNAStatsTKIPICVErrors++;
diff --git a/net/ieee80211/ieee80211_crypt_wep.c b/net/ieee80211/ieee80211_crypt_wep.c
index ec6d885..8d18245 100644
--- a/net/ieee80211/ieee80211_crypt_wep.c
+++ b/net/ieee80211/ieee80211_crypt_wep.c
@@ -1,7 +1,7 @@
 /*
  * Host AP crypt: host-based WEP encryption implementation for Host AP driver
  *
- * Copyright (c) 2002-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright (c) 2002-2004, Jouni Malinen <j@w1.fi>
  *
  * 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
@@ -152,7 +152,7 @@
 		return -1;
 
 	/* Copy the IV into the first 3 bytes of the key */
-	memcpy(key, skb->data + hdr_len, 3);
+	skb_copy_from_linear_data_offset(skb, hdr_len, key, 3);
 
 	/* Copy rest of the WEP key (the secret part) */
 	memcpy(key + 3, wep->key, wep->key_len);
diff --git a/net/ieee80211/ieee80211_module.c b/net/ieee80211/ieee80211_module.c
index b1c6d1f..7ec6610 100644
--- a/net/ieee80211/ieee80211_module.c
+++ b/net/ieee80211/ieee80211_module.c
@@ -5,8 +5,8 @@
   Portions of this file are based on the WEP enablement code provided by the
   Host AP project hostap-drivers v0.1.3
   Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
-  <jkmaline@cc.hut.fi>
-  Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+  <j@w1.fi>
+  Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
 
   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
@@ -229,6 +229,7 @@
 
 static int debug = 0;
 u32 ieee80211_debug_level = 0;
+EXPORT_SYMBOL_GPL(ieee80211_debug_level);
 static struct proc_dir_entry *ieee80211_proc = NULL;
 
 static int show_debug_level(char *page, char **start, off_t offset,
diff --git a/net/ieee80211/ieee80211_rx.c b/net/ieee80211/ieee80211_rx.c
index 4084909..f2de2e4 100644
--- a/net/ieee80211/ieee80211_rx.c
+++ b/net/ieee80211/ieee80211_rx.c
@@ -3,8 +3,8 @@
  * for Intersil Prism2/2.5/3 - hostap.o module, common routines
  *
  * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
- * <jkmaline@cc.hut.fi>
- * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * <j@w1.fi>
+ * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2004-2005, Intel Corporation
  *
  * This program is free software; you can redistribute it and/or modify
@@ -42,7 +42,7 @@
 	u16 fc = le16_to_cpu(hdr->frame_ctl);
 
 	skb->dev = ieee->dev;
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	skb_pull(skb, ieee80211_get_hdrlen(fc));
 	skb->pkt_type = PACKET_OTHERHOST;
 	skb->protocol = __constant_htons(ETH_P_80211_RAW);
@@ -606,12 +606,12 @@
 		if (frag == 0) {
 			/* copy first fragment (including full headers) into
 			 * beginning of the fragment cache skb */
-			memcpy(skb_put(frag_skb, flen), skb->data, flen);
+			skb_copy_from_linear_data(skb, skb_put(frag_skb, flen), flen);
 		} else {
 			/* append frame payload to the end of the fragment
 			 * cache skb */
-			memcpy(skb_put(frag_skb, flen), skb->data + hdrlen,
-			       flen);
+			skb_copy_from_linear_data_offset(skb, hdrlen,
+				      skb_put(frag_skb, flen), flen);
 		}
 		dev_kfree_skb_any(skb);
 		skb = NULL;
@@ -759,8 +759,9 @@
 		    IEEE80211_FCTL_TODS) && skb->len >= ETH_HLEN + ETH_ALEN) {
 		/* Non-standard frame: get addr4 from its bogus location after
 		 * the payload */
-		memcpy(skb->data + ETH_ALEN,
-		       skb->data + skb->len - ETH_ALEN, ETH_ALEN);
+		skb_copy_to_linear_data_offset(skb, ETH_ALEN,
+					       skb->data + skb->len - ETH_ALEN,
+					       ETH_ALEN);
 		skb_trim(skb, skb->len - ETH_ALEN);
 	}
 #endif
@@ -789,10 +790,11 @@
 
 	if (skb2 != NULL) {
 		/* send to wireless media */
-		skb2->protocol = __constant_htons(ETH_P_802_3);
-		skb2->mac.raw = skb2->nh.raw = skb2->data;
-		/* skb2->nh.raw = skb2->data + ETH_HLEN; */
 		skb2->dev = dev;
+		skb2->protocol = __constant_htons(ETH_P_802_3);
+		skb_reset_mac_header(skb2);
+		skb_reset_network_header(skb2);
+		/* skb2->network_header += ETH_HLEN; */
 		dev_queue_xmit(skb2);
 	}
 #endif
@@ -800,7 +802,6 @@
 	if (skb) {
 		skb->protocol = eth_type_trans(skb, dev);
 		memset(skb->cb, 0, sizeof(skb->cb));
-		skb->dev = dev;
 		skb->ip_summed = CHECKSUM_NONE;	/* 802.11 crc not sufficient */
 		if (netif_rx(skb) == NET_RX_DROP) {
 			/* netif_rx always succeeds, but it might drop
diff --git a/net/ieee80211/ieee80211_tx.c b/net/ieee80211/ieee80211_tx.c
index 0292d63..a4c3c51 100644
--- a/net/ieee80211/ieee80211_tx.c
+++ b/net/ieee80211/ieee80211_tx.c
@@ -225,10 +225,10 @@
 	struct iphdr *ip;
 
 	eth = (struct ethhdr *)skb->data;
-	if (eth->h_proto != __constant_htons(ETH_P_IP))
+	if (eth->h_proto != htons(ETH_P_IP))
 		return 0;
 
-	ip = skb->nh.iph;
+	ip = ip_hdr(skb);
 	switch (ip->tos & 0xfc) {
 	case 0x20:
 		return 2;
@@ -309,8 +309,8 @@
 	}
 
 	/* Save source and destination addresses */
-	memcpy(dest, skb->data, ETH_ALEN);
-	memcpy(src, skb->data + ETH_ALEN, ETH_ALEN);
+	skb_copy_from_linear_data(skb, dest, ETH_ALEN);
+	skb_copy_from_linear_data_offset(skb, ETH_ALEN, src, ETH_ALEN);
 
 	if (host_encrypt || host_build_iv)
 		fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA |
@@ -363,7 +363,7 @@
 		snapped = 1;
 		ieee80211_copy_snap(skb_put(skb_new, SNAP_SIZE + sizeof(u16)),
 				    ether_type);
-		memcpy(skb_put(skb_new, skb->len), skb->data, skb->len);
+		skb_copy_from_linear_data(skb, skb_put(skb_new, skb->len), skb->len);
 		res = crypt->ops->encrypt_msdu(skb_new, hdr_len, crypt->priv);
 		if (res < 0) {
 			IEEE80211_ERROR("msdu encryption failed\n");
@@ -492,7 +492,7 @@
 			bytes -= SNAP_SIZE + sizeof(u16);
 		}
 
-		memcpy(skb_put(skb_frag, bytes), skb->data, bytes);
+		skb_copy_from_linear_data(skb, skb_put(skb_frag, bytes), bytes);
 
 		/* Advance the SKB... */
 		skb_pull(skb, bytes);
diff --git a/net/ieee80211/ieee80211_wx.c b/net/ieee80211/ieee80211_wx.c
index 40d7a55..cee5e13 100644
--- a/net/ieee80211/ieee80211_wx.c
+++ b/net/ieee80211/ieee80211_wx.c
@@ -5,8 +5,8 @@
   Portions of this file are based on the WEP enablement code provided by the
   Host AP project hostap-drivers v0.1.3
   Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
-  <jkmaline@cc.hut.fi>
-  Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+  <j@w1.fi>
+  Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
 
   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
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index 9e8ef50..e62aee0 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -574,6 +574,33 @@
 	loss packets.
 	See http://www.ntu.edu.sg/home5/ZHOU0022/papers/CPFu03a.pdf
 
+config TCP_CONG_YEAH
+	tristate "YeAH TCP"
+	depends on EXPERIMENTAL
+	default n
+	---help---
+	YeAH-TCP is a sender-side high-speed enabled TCP congestion control
+	algorithm, which uses a mixed loss/delay approach to compute the
+	congestion window. It's design goals target high efficiency,
+	internal, RTT and Reno fairness, resilience to link loss while
+	keeping network elements load as low as possible.
+
+	For further details look here:
+	  http://wil.cs.caltech.edu/pfldnet2007/paper/YeAH_TCP.pdf
+
+config TCP_CONG_ILLINOIS
+	tristate "TCP Illinois"
+	depends on EXPERIMENTAL
+	default n
+	---help---
+	TCP-Illinois is a sender-side modificatio of TCP Reno for
+	high speed long delay links. It uses round-trip-time to
+	adjust the alpha and beta parameters to achieve a higher average
+	throughput and maintain fairness.
+
+	For further details see:
+	  http://www.ews.uiuc.edu/~shaoliu/tcpillinois/index.html
+
 choice
 	prompt "Default TCP congestion control"
 	default DEFAULT_CUBIC
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index 7a06862..4ff6c15 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -49,6 +49,8 @@
 obj-$(CONFIG_TCP_CONG_VENO) += tcp_veno.o
 obj-$(CONFIG_TCP_CONG_SCALABLE) += tcp_scalable.o
 obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o
+obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o
+obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o
 obj-$(CONFIG_NETLABEL) += cipso_ipv4.o
 
 obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index cf358c8..16aae8e 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -87,6 +87,7 @@
 #include <linux/init.h>
 #include <linux/poll.h>
 #include <linux/netfilter_ipv4.h>
+#include <linux/random.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -217,6 +218,26 @@
 	return err;
 }
 
+u32 inet_ehash_secret __read_mostly;
+EXPORT_SYMBOL(inet_ehash_secret);
+
+/*
+ * inet_ehash_secret must be set exactly once
+ * Instead of using a dedicated spinlock, we (ab)use inetsw_lock
+ */
+void build_ehash_secret(void)
+{
+	u32 rnd;
+	do {
+		get_random_bytes(&rnd, sizeof(rnd));
+	} while (rnd == 0);
+	spin_lock_bh(&inetsw_lock);
+	if (!inet_ehash_secret)
+		inet_ehash_secret = rnd;
+	spin_unlock_bh(&inetsw_lock);
+}
+EXPORT_SYMBOL(build_ehash_secret);
+
 /*
  *	Create an inet socket.
  */
@@ -233,6 +254,11 @@
 	int try_loading_module = 0;
 	int err;
 
+	if (sock->type != SOCK_RAW &&
+	    sock->type != SOCK_DGRAM &&
+	    !inet_ehash_secret)
+		build_ehash_secret();
+
 	sock->state = SS_UNCONNECTED;
 
 	/* Look for the requested type/protocol pair. */
@@ -755,6 +781,9 @@
 		case SIOCGSTAMP:
 			err = sock_get_timestamp(sk, (struct timeval __user *)arg);
 			break;
+		case SIOCGSTAMPNS:
+			err = sock_get_timestampns(sk, (struct timespec __user *)arg);
+			break;
 		case SIOCADDRT:
 		case SIOCDELRT:
 		case SIOCRTMSG:
@@ -1109,7 +1138,7 @@
 	if (unlikely(!pskb_may_pull(skb, sizeof(*iph))))
 		goto out;
 
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 	ihl = iph->ihl * 4;
 	if (ihl < sizeof(*iph))
 		goto out;
@@ -1117,8 +1146,9 @@
 	if (unlikely(!pskb_may_pull(skb, ihl)))
 		goto out;
 
-	skb->h.raw = __skb_pull(skb, ihl);
-	iph = skb->nh.iph;
+	__skb_pull(skb, ihl);
+	skb_reset_transport_header(skb);
+	iph = ip_hdr(skb);
 	proto = iph->protocol & (MAX_INET_PROTOS - 1);
 	err = -EPROTONOSUPPORT;
 
@@ -1152,7 +1182,7 @@
 	if (unlikely(!pskb_may_pull(skb, sizeof(*iph))))
 		goto out;
 
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 	ihl = iph->ihl * 4;
 	if (ihl < sizeof(*iph))
 		goto out;
@@ -1160,8 +1190,9 @@
 	if (unlikely(!pskb_may_pull(skb, ihl)))
 		goto out;
 
-	skb->h.raw = __skb_pull(skb, ihl);
-	iph = skb->nh.iph;
+	__skb_pull(skb, ihl);
+	skb_reset_transport_header(skb);
+	iph = ip_hdr(skb);
 	id = ntohs(iph->id);
 	proto = iph->protocol & (MAX_INET_PROTOS - 1);
 	segs = ERR_PTR(-EPROTONOSUPPORT);
@@ -1177,17 +1208,57 @@
 
 	skb = segs;
 	do {
-		iph = skb->nh.iph;
+		iph = ip_hdr(skb);
 		iph->id = htons(id++);
 		iph->tot_len = htons(skb->len - skb->mac_len);
 		iph->check = 0;
-		iph->check = ip_fast_csum(skb->nh.raw, iph->ihl);
+		iph->check = ip_fast_csum(skb_network_header(skb), iph->ihl);
 	} while ((skb = skb->next));
 
 out:
 	return segs;
 }
 
+unsigned long snmp_fold_field(void *mib[], int offt)
+{
+	unsigned long res = 0;
+	int i;
+
+	for_each_possible_cpu(i) {
+		res += *(((unsigned long *) per_cpu_ptr(mib[0], i)) + offt);
+		res += *(((unsigned long *) per_cpu_ptr(mib[1], i)) + offt);
+	}
+	return res;
+}
+EXPORT_SYMBOL_GPL(snmp_fold_field);
+
+int snmp_mib_init(void *ptr[2], size_t mibsize, size_t mibalign)
+{
+	BUG_ON(ptr == NULL);
+	ptr[0] = __alloc_percpu(mibsize);
+	if (!ptr[0])
+		goto err0;
+	ptr[1] = __alloc_percpu(mibsize);
+	if (!ptr[1])
+		goto err1;
+	return 0;
+err1:
+	free_percpu(ptr[0]);
+	ptr[0] = NULL;
+err0:
+	return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(snmp_mib_init);
+
+void snmp_mib_free(void *ptr[2])
+{
+	BUG_ON(ptr == NULL);
+	free_percpu(ptr[0]);
+	free_percpu(ptr[1]);
+	ptr[0] = ptr[1] = NULL;
+}
+EXPORT_SYMBOL_GPL(snmp_mib_free);
+
 #ifdef CONFIG_IP_MULTICAST
 static struct net_protocol igmp_protocol = {
 	.handler =	igmp_rcv,
@@ -1214,28 +1285,47 @@
 
 static int __init init_ipv4_mibs(void)
 {
-	net_statistics[0] = alloc_percpu(struct linux_mib);
-	net_statistics[1] = alloc_percpu(struct linux_mib);
-	ip_statistics[0] = alloc_percpu(struct ipstats_mib);
-	ip_statistics[1] = alloc_percpu(struct ipstats_mib);
-	icmp_statistics[0] = alloc_percpu(struct icmp_mib);
-	icmp_statistics[1] = alloc_percpu(struct icmp_mib);
-	tcp_statistics[0] = alloc_percpu(struct tcp_mib);
-	tcp_statistics[1] = alloc_percpu(struct tcp_mib);
-	udp_statistics[0] = alloc_percpu(struct udp_mib);
-	udp_statistics[1] = alloc_percpu(struct udp_mib);
-	udplite_statistics[0] = alloc_percpu(struct udp_mib);
-	udplite_statistics[1] = alloc_percpu(struct udp_mib);
-	if (!
-	    (net_statistics[0] && net_statistics[1] && ip_statistics[0]
-	     && ip_statistics[1] && tcp_statistics[0] && tcp_statistics[1]
-	     && udp_statistics[0] && udp_statistics[1]
-	     && udplite_statistics[0] && udplite_statistics[1]             ) )
-		return -ENOMEM;
+	if (snmp_mib_init((void **)net_statistics,
+			  sizeof(struct linux_mib),
+			  __alignof__(struct linux_mib)) < 0)
+		goto err_net_mib;
+	if (snmp_mib_init((void **)ip_statistics,
+			  sizeof(struct ipstats_mib),
+			  __alignof__(struct ipstats_mib)) < 0)
+		goto err_ip_mib;
+	if (snmp_mib_init((void **)icmp_statistics,
+			  sizeof(struct icmp_mib),
+			  __alignof__(struct icmp_mib)) < 0)
+		goto err_icmp_mib;
+	if (snmp_mib_init((void **)tcp_statistics,
+			  sizeof(struct tcp_mib),
+			  __alignof__(struct tcp_mib)) < 0)
+		goto err_tcp_mib;
+	if (snmp_mib_init((void **)udp_statistics,
+			  sizeof(struct udp_mib),
+			  __alignof__(struct udp_mib)) < 0)
+		goto err_udp_mib;
+	if (snmp_mib_init((void **)udplite_statistics,
+			  sizeof(struct udp_mib),
+			  __alignof__(struct udp_mib)) < 0)
+		goto err_udplite_mib;
 
-	(void) tcp_mib_init();
+	tcp_mib_init();
 
 	return 0;
+
+err_udplite_mib:
+	snmp_mib_free((void **)udp_statistics);
+err_udp_mib:
+	snmp_mib_free((void **)tcp_statistics);
+err_tcp_mib:
+	snmp_mib_free((void **)icmp_statistics);
+err_icmp_mib:
+	snmp_mib_free((void **)ip_statistics);
+err_ip_mib:
+	snmp_mib_free((void **)net_statistics);
+err_net_mib:
+	return -ENOMEM;
 }
 
 static int ipv4_proc_init(void);
@@ -1336,7 +1426,7 @@
 	 *	Initialise per-cpu ipv4 mibs
 	 */
 
-	if(init_ipv4_mibs())
+	if (init_ipv4_mibs())
 		printk(KERN_CRIT "inet_init: Cannot init ipv4 mibs\n"); ;
 
 	ipv4_proc_init();
diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c
index 7194eb4..6da8ff5 100644
--- a/net/ipv4/ah4.c
+++ b/net/ipv4/ah4.c
@@ -65,7 +65,7 @@
 		char 		buf[60];
 	} tmp_iph;
 
-	top_iph = skb->nh.iph;
+	top_iph = ip_hdr(skb);
 	iph = &tmp_iph.iph;
 
 	iph->tos = top_iph->tos;
@@ -152,9 +152,9 @@
 	skb->ip_summed = CHECKSUM_NONE;
 
 	ah = (struct ip_auth_hdr*)skb->data;
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 
-	ihl = skb->data - skb->nh.raw;
+	ihl = skb->data - skb_network_header(skb);
 	memcpy(work_buf, iph, ihl);
 
 	iph->ttl = 0;
@@ -181,7 +181,9 @@
 		}
 	}
 	((struct iphdr*)work_buf)->protocol = ah->nexthdr;
-	skb->h.raw = memcpy(skb->nh.raw += ah_hlen, work_buf, ihl);
+	skb->network_header += ah_hlen;
+	memcpy(skb_network_header(skb), work_buf, ihl);
+	skb->transport_header = skb->network_header;
 	__skb_pull(skb, ah_hlen + ihl);
 
 	return 0;
@@ -196,8 +198,8 @@
 	struct ip_auth_hdr *ah = (struct ip_auth_hdr*)(skb->data+(iph->ihl<<2));
 	struct xfrm_state *x;
 
-	if (skb->h.icmph->type != ICMP_DEST_UNREACH ||
-	    skb->h.icmph->code != ICMP_FRAG_NEEDED)
+	if (icmp_hdr(skb)->type != ICMP_DEST_UNREACH ||
+	    icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
 		return;
 
 	x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET);
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index 1a3488a..7110779 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -342,13 +342,13 @@
 	switch (IN_DEV_ARP_ANNOUNCE(in_dev)) {
 	default:
 	case 0:		/* By default announce any local IP */
-		if (skb && inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL)
-			saddr = skb->nh.iph->saddr;
+		if (skb && inet_addr_type(ip_hdr(skb)->saddr) == RTN_LOCAL)
+			saddr = ip_hdr(skb)->saddr;
 		break;
 	case 1:		/* Restrict announcements of saddr in same subnet */
 		if (!skb)
 			break;
-		saddr = skb->nh.iph->saddr;
+		saddr = ip_hdr(skb)->saddr;
 		if (inet_addr_type(saddr) == RTN_LOCAL) {
 			/* saddr should be known to target */
 			if (inet_addr_onlink(in_dev, target, saddr))
@@ -578,7 +578,7 @@
 		return NULL;
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
-	skb->nh.raw = skb->data;
+	skb_reset_network_header(skb);
 	arp = (struct arphdr *) skb_put(skb,sizeof(struct arphdr) + 2*(dev->addr_len+4));
 	skb->dev = dev;
 	skb->protocol = htons(ETH_P_ARP);
@@ -721,7 +721,7 @@
 	if (in_dev == NULL)
 		goto out;
 
-	arp = skb->nh.arph;
+	arp = arp_hdr(skb);
 
 	switch (dev_type) {
 	default:
@@ -937,7 +937,7 @@
 				 (2 * sizeof(u32)))))
 		goto freeskb;
 
-	arp = skb->nh.arph;
+	arp = arp_hdr(skb);
 	if (arp->ar_hln != dev->addr_len ||
 	    dev->flags & IFF_NOARP ||
 	    skb->pkt_type == PACKET_OTHERHOST ||
@@ -1178,7 +1178,7 @@
 		goto out;
 	}
 
-	switch(cmd) {
+	switch (cmd) {
 	case SIOCDARP:
 		err = arp_req_delete(&r, dev);
 		break;
@@ -1360,7 +1360,7 @@
 
 /* ------------------------------------------------------------------------ */
 
-static struct seq_operations arp_seq_ops = {
+static const struct seq_operations arp_seq_ops = {
 	.start  = arp_seq_start,
 	.next   = neigh_seq_next,
 	.stop   = neigh_seq_stop,
diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c
index 2ce5b69..e1f1848 100644
--- a/net/ipv4/cipso_ipv4.c
+++ b/net/ipv4/cipso_ipv4.c
@@ -92,6 +92,33 @@
 int cipso_v4_rbm_strictvalid = 1;
 
 /*
+ * Protocol Constants
+ */
+
+/* Maximum size of the CIPSO IP option, derived from the fact that the maximum
+ * IPv4 header size is 60 bytes and the base IPv4 header is 20 bytes long. */
+#define CIPSO_V4_OPT_LEN_MAX          40
+
+/* Length of the base CIPSO option, this includes the option type (1 byte), the
+ * option length (1 byte), and the DOI (4 bytes). */
+#define CIPSO_V4_HDR_LEN              6
+
+/* Base length of the restrictive category bitmap tag (tag #1). */
+#define CIPSO_V4_TAG_RBM_BLEN         4
+
+/* Base length of the enumerated category tag (tag #2). */
+#define CIPSO_V4_TAG_ENUM_BLEN        4
+
+/* Base length of the ranged categories bitmap tag (tag #5). */
+#define CIPSO_V4_TAG_RNG_BLEN         4
+/* The maximum number of category ranges permitted in the ranged category tag
+ * (tag #5).  You may note that the IETF draft states that the maximum number
+ * of category ranges is 7, but if the low end of the last category range is
+ * zero then it is possibile to fit 8 category ranges because the zero should
+ * be omitted. */
+#define CIPSO_V4_TAG_RNG_CAT_MAX      8
+
+/*
  * Helper Functions
  */
 
@@ -1109,16 +1136,15 @@
 				     unsigned char *net_cat,
 				     u32 net_cat_len)
 {
-	/* The constant '16' is not random, it is the maximum number of
-	 * high/low category range pairs as permitted by the CIPSO draft based
-	 * on a maximum IPv4 header length of 60 bytes - the BUG_ON() assertion
-	 * does a sanity check to make sure we don't overflow the array. */
 	int iter = -1;
-	u16 array[16];
+	u16 array[CIPSO_V4_TAG_RNG_CAT_MAX * 2];
 	u32 array_cnt = 0;
 	u32 cat_size = 0;
 
-	BUG_ON(net_cat_len > 30);
+	/* make sure we don't overflow the 'array[]' variable */
+	if (net_cat_len >
+	    (CIPSO_V4_OPT_LEN_MAX - CIPSO_V4_HDR_LEN - CIPSO_V4_TAG_RNG_BLEN))
+		return -ENOSPC;
 
 	for (;;) {
 		iter = netlbl_secattr_catmap_walk(secattr->mls_cat, iter + 1);
@@ -1174,7 +1200,7 @@
 	u16 cat_low;
 	u16 cat_high;
 
-	for(net_iter = 0; net_iter < net_cat_len; net_iter += 4) {
+	for (net_iter = 0; net_iter < net_cat_len; net_iter += 4) {
 		cat_high = ntohs(*((__be16 *)&net_cat[net_iter]));
 		if ((net_iter + 4) <= net_cat_len)
 			cat_low = ntohs(*((__be16 *)&net_cat[net_iter + 2]));
@@ -1196,9 +1222,6 @@
  * Protocol Handling Functions
  */
 
-#define CIPSO_V4_OPT_LEN_MAX          40
-#define CIPSO_V4_HDR_LEN              6
-
 /**
  * cipso_v4_gentag_hdr - Generate a CIPSO option header
  * @doi_def: the DOI definition
@@ -1676,7 +1699,7 @@
  */
 void cipso_v4_error(struct sk_buff *skb, int error, u32 gateway)
 {
-	if (skb->nh.iph->protocol == IPPROTO_ICMP || error != -EACCES)
+	if (ip_hdr(skb)->protocol == IPPROTO_ICMP || error != -EACCES)
 		return;
 
 	if (gateway)
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 98a00d0..088888d 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -48,7 +48,6 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
-#include <linux/rtnetlink.h>
 #include <linux/init.h>
 #include <linux/notifier.h>
 #include <linux/inetdevice.h>
@@ -62,7 +61,7 @@
 #include <net/ip.h>
 #include <net/route.h>
 #include <net/ip_fib.h>
-#include <net/netlink.h>
+#include <net/rtnetlink.h>
 
 struct ipv4_devconf ipv4_devconf = {
 	.accept_redirects = 1,
@@ -633,7 +632,7 @@
 	dev_load(ifr.ifr_name);
 #endif
 
-	switch(cmd) {
+	switch (cmd) {
 	case SIOCGIFADDR:	/* Get interface address */
 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
 	case SIOCGIFDSTADDR:	/* Get the destination address */
@@ -708,7 +707,7 @@
 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
 		goto done;
 
-	switch(cmd) {
+	switch (cmd) {
 	case SIOCGIFADDR:	/* Get interface address */
 		sin->sin_addr.s_addr = ifa->ifa_local;
 		goto rarok;
@@ -1183,17 +1182,13 @@
 	int s_ip_idx, s_idx = cb->args[0];
 
 	s_ip_idx = ip_idx = cb->args[1];
-	read_lock(&dev_base_lock);
 	for (dev = dev_base, idx = 0; dev; dev = dev->next, idx++) {
 		if (idx < s_idx)
 			continue;
 		if (idx > s_idx)
 			s_ip_idx = 0;
-		rcu_read_lock();
-		if ((in_dev = __in_dev_get_rcu(dev)) == NULL) {
-			rcu_read_unlock();
+		if ((in_dev = __in_dev_get_rtnl(dev)) == NULL)
 			continue;
-		}
 
 		for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
 		     ifa = ifa->ifa_next, ip_idx++) {
@@ -1201,16 +1196,12 @@
 				continue;
 			if (inet_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).pid,
 					     cb->nlh->nlmsg_seq,
-					     RTM_NEWADDR, NLM_F_MULTI) <= 0) {
-				rcu_read_unlock();
+					     RTM_NEWADDR, NLM_F_MULTI) <= 0)
 				goto done;
-			}
 		}
-		rcu_read_unlock();
 	}
 
 done:
-	read_unlock(&dev_base_lock);
 	cb->args[0] = idx;
 	cb->args[1] = ip_idx;
 
@@ -1241,19 +1232,6 @@
 		rtnl_set_sk_err(RTNLGRP_IPV4_IFADDR, err);
 }
 
-static struct rtnetlink_link inet_rtnetlink_table[RTM_NR_MSGTYPES] = {
-	[RTM_NEWADDR  - RTM_BASE] = { .doit	= inet_rtm_newaddr,	},
-	[RTM_DELADDR  - RTM_BASE] = { .doit	= inet_rtm_deladdr,	},
-	[RTM_GETADDR  - RTM_BASE] = { .dumpit	= inet_dump_ifaddr,	},
-	[RTM_NEWROUTE - RTM_BASE] = { .doit	= inet_rtm_newroute,	},
-	[RTM_DELROUTE - RTM_BASE] = { .doit	= inet_rtm_delroute,	},
-	[RTM_GETROUTE - RTM_BASE] = { .doit	= inet_rtm_getroute,
-				      .dumpit	= inet_dump_fib,	},
-#ifdef CONFIG_IP_MULTIPLE_TABLES
-	[RTM_GETRULE  - RTM_BASE] = { .dumpit	= fib4_rules_dump,	},
-#endif
-};
-
 #ifdef CONFIG_SYSCTL
 
 void inet_forward_change(void)
@@ -1636,7 +1614,10 @@
 {
 	register_gifconf(PF_INET, inet_gifconf);
 	register_netdevice_notifier(&ip_netdev_notifier);
-	rtnetlink_links[PF_INET] = inet_rtnetlink_table;
+
+	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL);
+	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL);
+	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr);
 #ifdef CONFIG_SYSCTL
 	devinet_sysctl.sysctl_header =
 		register_sysctl_table(devinet_sysctl.devinet_root_dir);
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index 3104112..47c95e8 100644
--- a/net/ipv4/esp4.c
+++ b/net/ipv4/esp4.c
@@ -21,13 +21,14 @@
 	struct blkcipher_desc desc;
 	struct esp_data *esp;
 	struct sk_buff *trailer;
+	u8 *tail;
 	int blksize;
 	int clen;
 	int alen;
 	int nfrags;
 
 	/* Strip IP+ESP header. */
-	__skb_pull(skb, skb->h.raw - skb->data);
+	__skb_pull(skb, skb_transport_offset(skb));
 	/* Now skb is pure payload to encrypt */
 
 	err = -ENOMEM;
@@ -49,19 +50,21 @@
 		goto error;
 
 	/* Fill padding... */
+	tail = skb_tail_pointer(trailer);
 	do {
 		int i;
 		for (i=0; i<clen-skb->len - 2; i++)
-			*(u8*)(trailer->tail + i) = i+1;
+			tail[i] = i + 1;
 	} while (0);
-	*(u8*)(trailer->tail + clen-skb->len - 2) = (clen - skb->len)-2;
+	tail[clen - skb->len - 2] = (clen - skb->len) - 2;
 	pskb_put(skb, trailer, clen - skb->len);
 
-	__skb_push(skb, skb->data - skb->nh.raw);
-	top_iph = skb->nh.iph;
-	esph = (struct ip_esp_hdr *)(skb->nh.raw + top_iph->ihl*4);
+	__skb_push(skb, skb->data - skb_network_header(skb));
+	top_iph = ip_hdr(skb);
+	esph = (struct ip_esp_hdr *)(skb_network_header(skb) +
+				     top_iph->ihl * 4);
 	top_iph->tot_len = htons(skb->len + alen);
-	*(u8*)(trailer->tail - 1) = top_iph->protocol;
+	*(skb_tail_pointer(trailer) - 1) = top_iph->protocol;
 
 	/* this is non-NULL only with UDP Encapsulation */
 	if (x->encap) {
@@ -217,12 +220,12 @@
 
 	/* ... check padding bits here. Silly. :-) */
 
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 	ihl = iph->ihl * 4;
 
 	if (x->encap) {
 		struct xfrm_encap_tmpl *encap = x->encap;
-		struct udphdr *uh = (void *)(skb->nh.raw + ihl);
+		struct udphdr *uh = (void *)(skb_network_header(skb) + ihl);
 
 		/*
 		 * 1) if the NAT-T peer's IP or port changed then
@@ -260,7 +263,8 @@
 
 	iph->protocol = nexthdr[1];
 	pskb_trim(skb, skb->len - alen - padlen - 2);
-	skb->h.raw = __skb_pull(skb, sizeof(*esph) + esp->conf.ivlen) - ihl;
+	__skb_pull(skb, sizeof(*esph) + esp->conf.ivlen);
+	skb_set_transport_header(skb, -ihl);
 
 	return 0;
 
@@ -268,32 +272,33 @@
 	return -EINVAL;
 }
 
-static u32 esp4_get_max_size(struct xfrm_state *x, int mtu)
+static u32 esp4_get_mtu(struct xfrm_state *x, int mtu)
 {
 	struct esp_data *esp = x->data;
 	u32 blksize = ALIGN(crypto_blkcipher_blocksize(esp->conf.tfm), 4);
-	int enclen = 0;
+	u32 align = max_t(u32, blksize, esp->conf.padlen);
+	u32 rem;
+
+	mtu -= x->props.header_len + esp->auth.icv_trunc_len;
+	rem = mtu & (align - 1);
+	mtu &= ~(align - 1);
 
 	switch (x->props.mode) {
 	case XFRM_MODE_TUNNEL:
-		mtu = ALIGN(mtu +2, blksize);
 		break;
 	default:
 	case XFRM_MODE_TRANSPORT:
 		/* The worst case */
-		mtu = ALIGN(mtu + 2, 4) + blksize - 4;
+		mtu -= blksize - 4;
+		mtu += min_t(u32, blksize - 4, rem);
 		break;
 	case XFRM_MODE_BEET:
 		/* The worst case. */
-		enclen = IPV4_BEET_PHMAXLEN;
-		mtu = ALIGN(mtu + enclen + 2, blksize);
+		mtu += min_t(u32, IPV4_BEET_PHMAXLEN, rem);
 		break;
 	}
 
-	if (esp->conf.padlen)
-		mtu = ALIGN(mtu, esp->conf.padlen);
-
-	return mtu + x->props.header_len + esp->auth.icv_trunc_len - enclen;
+	return mtu - 2;
 }
 
 static void esp4_err(struct sk_buff *skb, u32 info)
@@ -302,8 +307,8 @@
 	struct ip_esp_hdr *esph = (struct ip_esp_hdr*)(skb->data+(iph->ihl<<2));
 	struct xfrm_state *x;
 
-	if (skb->h.icmph->type != ICMP_DEST_UNREACH ||
-	    skb->h.icmph->code != ICMP_FRAG_NEEDED)
+	if (icmp_hdr(skb)->type != ICMP_DEST_UNREACH ||
+	    icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
 		return;
 
 	x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, esph->spi, IPPROTO_ESP, AF_INET);
@@ -336,6 +341,7 @@
 {
 	struct esp_data *esp = NULL;
 	struct crypto_blkcipher *tfm;
+	u32 align;
 
 	/* null auth and encryption can have zero length keys */
 	if (x->aalg) {
@@ -402,6 +408,8 @@
 	x->props.header_len = sizeof(struct ip_esp_hdr) + esp->conf.ivlen;
 	if (x->props.mode == XFRM_MODE_TUNNEL)
 		x->props.header_len += sizeof(struct iphdr);
+	else if (x->props.mode == XFRM_MODE_BEET)
+		x->props.header_len += IPV4_BEET_PHMAXLEN;
 	if (x->encap) {
 		struct xfrm_encap_tmpl *encap = x->encap;
 
@@ -417,7 +425,10 @@
 		}
 	}
 	x->data = esp;
-	x->props.trailer_len = esp4_get_max_size(x, 0) - x->props.header_len;
+	align = ALIGN(crypto_blkcipher_blocksize(esp->conf.tfm), 4);
+	if (esp->conf.padlen)
+		align = max_t(u32, align, esp->conf.padlen);
+	x->props.trailer_len = align + 1 + esp->auth.icv_trunc_len;
 	return 0;
 
 error:
@@ -434,7 +445,7 @@
 	.proto	     	= IPPROTO_ESP,
 	.init_state	= esp_init_state,
 	.destructor	= esp_destroy,
-	.get_max_size	= esp4_get_max_size,
+	.get_mtu	= esp4_get_mtu,
 	.input		= esp_input,
 	.output		= esp_output
 };
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index cac06c4..837f295 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -34,7 +34,6 @@
 #include <linux/if_addr.h>
 #include <linux/if_arp.h>
 #include <linux/skbuff.h>
-#include <linux/netlink.h>
 #include <linux/init.h>
 #include <linux/list.h>
 
@@ -46,6 +45,7 @@
 #include <net/icmp.h>
 #include <net/arp.h>
 #include <net/ip_fib.h>
+#include <net/rtnetlink.h>
 
 #define FFprint(a...) printk(KERN_DEBUG a)
 
@@ -540,7 +540,7 @@
 	return err;
 }
 
-int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+static int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 {
 	struct fib_config cfg;
 	struct fib_table *tb;
@@ -561,7 +561,7 @@
 	return err;
 }
 
-int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 {
 	struct fib_config cfg;
 	struct fib_table *tb;
@@ -582,7 +582,7 @@
 	return err;
 }
 
-int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
+static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
 {
 	unsigned int h, s_h;
 	unsigned int e = 0, s_e;
@@ -777,6 +777,10 @@
 							    .tos = frn->fl_tos,
 							    .scope = frn->fl_scope } } };
 
+#ifdef CONFIG_IP_MULTIPLE_TABLES
+	res.r = NULL;
+#endif
+
 	frn->err = -ENOENT;
 	if (tb) {
 		local_bh_disable();
@@ -807,7 +811,7 @@
 	if (skb == NULL)
 		return;
 
-	nlh = (struct nlmsghdr *)skb->data;
+	nlh = nlmsg_hdr(skb);
 	if (skb->len < NLMSG_SPACE(0) || skb->len < nlh->nlmsg_len ||
 	    nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*frn))) {
 		kfree_skb(skb);
@@ -827,7 +831,8 @@
 
 static void nl_fib_lookup_init(void)
 {
-      netlink_kernel_create(NETLINK_FIB_LOOKUP, 0, nl_fib_input, THIS_MODULE);
+      netlink_kernel_create(NETLINK_FIB_LOOKUP, 0, nl_fib_input, NULL,
+      			    THIS_MODULE);
 }
 
 static void fib_disable_ip(struct net_device *dev, int force)
@@ -925,6 +930,10 @@
 	register_netdevice_notifier(&fib_netdev_notifier);
 	register_inetaddr_notifier(&fib_inetaddr_notifier);
 	nl_fib_lookup_init();
+
+	rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL);
+	rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL);
+	rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib);
 }
 
 EXPORT_SYMBOL(inet_addr_type);
diff --git a/net/ipv4/fib_hash.c b/net/ipv4/fib_hash.c
index a4949f9..9cfecf1 100644
--- a/net/ipv4/fib_hash.c
+++ b/net/ipv4/fib_hash.c
@@ -1027,7 +1027,7 @@
 	return 0;
 }
 
-static struct seq_operations fib_seq_ops = {
+static const struct seq_operations fib_seq_ops = {
 	.start  = fib_seq_start,
 	.next   = fib_seq_next,
 	.stop   = fib_seq_stop,
diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c
index c660c07..33083ad 100644
--- a/net/ipv4/fib_rules.c
+++ b/net/ipv4/fib_rules.c
@@ -274,11 +274,6 @@
 	return -ENOBUFS;
 }
 
-int fib4_rules_dump(struct sk_buff *skb, struct netlink_callback *cb)
-{
-	return fib_rules_dump(skb, cb, AF_INET);
-}
-
 static u32 fib4_rule_default_pref(void)
 {
 	struct list_head *pos;
@@ -303,6 +298,11 @@
 	       + nla_total_size(4); /* flow */
 }
 
+static void fib4_rule_flush_cache(void)
+{
+	rt_cache_flush(-1);
+}
+
 static struct fib_rules_ops fib4_rules_ops = {
 	.family		= AF_INET,
 	.rule_size	= sizeof(struct fib4_rule),
@@ -314,6 +314,7 @@
 	.fill		= fib4_rule_fill,
 	.default_pref	= fib4_rule_default_pref,
 	.nlmsg_payload	= fib4_rule_nlmsg_payload,
+	.flush_cache	= fib4_rule_flush_cache,
 	.nlgroup	= RTNLGRP_IPV4_RULE,
 	.policy		= fib4_rule_policy,
 	.rules_list	= &fib4_rules,
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 3dad12e..406ea70 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -927,7 +927,7 @@
 			default:
 				printk(KERN_DEBUG "impossible 102\n");
 				return -EINVAL;
-			};
+			}
 		}
 		return err;
 	}
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index 214c347..9be7da7 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -50,7 +50,7 @@
  *		Patrick McHardy <kaber@trash.net>
  */
 
-#define VERSION "0.407"
+#define VERSION "0.408"
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -292,8 +292,8 @@
 
 static int halve_threshold = 25;
 static int inflate_threshold = 50;
-static int halve_threshold_root = 15;
-static int inflate_threshold_root = 25;
+static int halve_threshold_root = 8;
+static int inflate_threshold_root = 15;
 
 
 static void __alias_free_mem(struct rcu_head *head)
@@ -350,11 +350,10 @@
 
 static inline void tnode_free(struct tnode *tn)
 {
-	if(IS_LEAF(tn)) {
+	if (IS_LEAF(tn)) {
 		struct leaf *l = (struct leaf *) tn;
 		call_rcu_bh(&l->rcu, __leaf_free_rcu);
-	}
-	else
+	} else
 		call_rcu(&tn->rcu, __tnode_free_rcu);
 }
 
@@ -459,6 +458,7 @@
 	struct tnode *old_tn;
 	int inflate_threshold_use;
 	int halve_threshold_use;
+	int max_resize;
 
 	if (!tn)
 		return NULL;
@@ -553,13 +553,14 @@
 
 	/* Keep root node larger  */
 
-	if(!tn->parent)
+	if (!tn->parent)
 		inflate_threshold_use = inflate_threshold_root;
 	else
 		inflate_threshold_use = inflate_threshold;
 
 	err = 0;
-	while ((tn->full_children > 0 &&
+	max_resize = 10;
+	while ((tn->full_children > 0 &&  max_resize-- &&
 	       50 * (tn->full_children + tnode_child_length(tn) - tn->empty_children) >=
 				inflate_threshold_use * tnode_child_length(tn))) {
 
@@ -574,6 +575,15 @@
 		}
 	}
 
+	if (max_resize < 0) {
+		if (!tn->parent)
+			printk(KERN_WARNING "Fix inflate_threshold_root. Now=%d size=%d bits\n",
+			       inflate_threshold_root, tn->bits);
+		else
+			printk(KERN_WARNING "Fix inflate_threshold. Now=%d size=%d bits\n",
+			       inflate_threshold, tn->bits);
+	}
+
 	check_tnode(tn);
 
 	/*
@@ -584,13 +594,14 @@
 
 	/* Keep root node larger  */
 
-	if(!tn->parent)
+	if (!tn->parent)
 		halve_threshold_use = halve_threshold_root;
 	else
 		halve_threshold_use = halve_threshold;
 
 	err = 0;
-	while (tn->bits > 1 &&
+	max_resize = 10;
+	while (tn->bits > 1 &&  max_resize-- &&
 	       100 * (tnode_child_length(tn) - tn->empty_children) <
 	       halve_threshold_use * tnode_child_length(tn)) {
 
@@ -605,6 +616,14 @@
 		}
 	}
 
+	if (max_resize < 0) {
+		if (!tn->parent)
+			printk(KERN_WARNING "Fix halve_threshold_root. Now=%d size=%d bits\n",
+			       halve_threshold_root, tn->bits);
+		else
+			printk(KERN_WARNING "Fix halve_threshold. Now=%d size=%d bits\n",
+			       halve_threshold, tn->bits);
+	}
 
 	/* Only one child remains */
 	if (tn->empty_children == tnode_child_length(tn) - 1)
@@ -2039,12 +2058,12 @@
 {
 	struct node *n ;
 
-	if(!t)
+	if (!t)
 		return NULL;
 
 	n = rcu_dereference(t->trie);
 
-	if(!iter)
+	if (!iter)
 		return NULL;
 
 	if (n) {
@@ -2084,7 +2103,7 @@
 			int i;
 
 			s->tnodes++;
-			if(tn->bits < MAX_STAT_DEPTH)
+			if (tn->bits < MAX_STAT_DEPTH)
 				s->nodesizes[tn->bits]++;
 
 			for (i = 0; i < (1<<tn->bits); i++)
@@ -2250,7 +2269,7 @@
 {
 	static char buf[32];
 
-	switch(s) {
+	switch (s) {
 	case RT_SCOPE_UNIVERSE: return "universe";
 	case RT_SCOPE_SITE:	return "site";
 	case RT_SCOPE_LINK:	return "link";
@@ -2340,7 +2359,7 @@
 	return 0;
 }
 
-static struct seq_operations fib_trie_seq_ops = {
+static const struct seq_operations fib_trie_seq_ops = {
 	.start  = fib_trie_seq_start,
 	.next   = fib_trie_seq_next,
 	.stop   = fib_trie_seq_stop,
@@ -2461,7 +2480,7 @@
 	return 0;
 }
 
-static struct seq_operations fib_route_seq_ops = {
+static const struct seq_operations fib_route_seq_ops = {
 	.start  = fib_trie_seq_start,
 	.next   = fib_trie_seq_next,
 	.stop   = fib_trie_seq_stop,
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 4b7a0d9..d38cbba 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -355,7 +355,7 @@
 			   ipc, rt, MSG_DONTWAIT) < 0)
 		ip_flush_pending_frames(icmp_socket->sk);
 	else if ((skb = skb_peek(&icmp_socket->sk->sk_write_queue)) != NULL) {
-		struct icmphdr *icmph = skb->h.icmph;
+		struct icmphdr *icmph = icmp_hdr(skb);
 		__wsum csum = 0;
 		struct sk_buff *skb1;
 
@@ -392,7 +392,7 @@
 	icmp_param->data.icmph.checksum = 0;
 	icmp_out_count(icmp_param->data.icmph.type);
 
-	inet->tos = skb->nh.iph->tos;
+	inet->tos = ip_hdr(skb)->tos;
 	daddr = ipc.addr = rt->rt_src;
 	ipc.opt = NULL;
 	if (icmp_param->replyopts.optlen) {
@@ -404,7 +404,7 @@
 		struct flowi fl = { .nl_u = { .ip4_u =
 					      { .daddr = daddr,
 						.saddr = rt->rt_spec_dst,
-						.tos = RT_TOS(skb->nh.iph->tos) } },
+						.tos = RT_TOS(ip_hdr(skb)->tos) } },
 				    .proto = IPPROTO_ICMP };
 		security_skb_classify_flow(skb, &fl);
 		if (ip_route_output_key(&rt, &fl))
@@ -448,9 +448,10 @@
 	 *	Check this, icmp_send is called from the most obscure devices
 	 *	sometimes.
 	 */
-	iph = skb_in->nh.iph;
+	iph = ip_hdr(skb_in);
 
-	if ((u8 *)iph < skb_in->head || (u8 *)(iph + 1) > skb_in->tail)
+	if ((u8 *)iph < skb_in->head ||
+	    (skb_in->network_header + sizeof(*iph)) > skb_in->tail)
 		goto out;
 
 	/*
@@ -484,7 +485,7 @@
 			u8 _inner_type, *itp;
 
 			itp = skb_header_pointer(skb_in,
-						 skb_in->nh.raw +
+						 skb_network_header(skb_in) +
 						 (iph->ihl << 2) +
 						 offsetof(struct icmphdr,
 							  type) -
@@ -536,7 +537,7 @@
 	icmp_param.data.icmph.un.gateway = info;
 	icmp_param.data.icmph.checksum	 = 0;
 	icmp_param.skb	  = skb_in;
-	icmp_param.offset = skb_in->nh.raw - skb_in->data;
+	icmp_param.offset = skb_network_offset(skb_in);
 	icmp_out_count(icmp_param.data.icmph.type);
 	inet_sk(icmp_socket->sk)->tos = tos;
 	ipc.addr = iph->saddr;
@@ -613,7 +614,7 @@
 	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
 		goto out_err;
 
-	icmph = skb->h.icmph;
+	icmph = icmp_hdr(skb);
 	iph   = (struct iphdr *)skb->data;
 
 	if (iph->ihl < 5) /* Mangled header, drop. */
@@ -676,7 +677,7 @@
 			printk(KERN_WARNING "%u.%u.%u.%u sent an invalid ICMP "
 					    "type %u, code %u "
 					    "error to a broadcast: %u.%u.%u.%u on %s\n",
-			       NIPQUAD(skb->nh.iph->saddr),
+			       NIPQUAD(ip_hdr(skb)->saddr),
 			       icmph->type, icmph->code,
 			       NIPQUAD(iph->daddr),
 			       skb->dev->name);
@@ -743,7 +744,7 @@
 
 	iph = (struct iphdr *)skb->data;
 
-	switch (skb->h.icmph->code & 7) {
+	switch (icmp_hdr(skb)->code & 7) {
 	case ICMP_REDIR_NET:
 	case ICMP_REDIR_NETTOS:
 		/*
@@ -751,8 +752,8 @@
 		 */
 	case ICMP_REDIR_HOST:
 	case ICMP_REDIR_HOSTTOS:
-		ip_rt_redirect(skb->nh.iph->saddr, iph->daddr,
-			       skb->h.icmph->un.gateway,
+		ip_rt_redirect(ip_hdr(skb)->saddr, iph->daddr,
+			       icmp_hdr(skb)->un.gateway,
 			       iph->saddr, skb->dev);
 		break;
 	}
@@ -780,7 +781,7 @@
 	if (!sysctl_icmp_echo_ignore_all) {
 		struct icmp_bxm icmp_param;
 
-		icmp_param.data.icmph	   = *skb->h.icmph;
+		icmp_param.data.icmph	   = *icmp_hdr(skb);
 		icmp_param.data.icmph.type = ICMP_ECHOREPLY;
 		icmp_param.skb		   = skb;
 		icmp_param.offset	   = 0;
@@ -816,7 +817,7 @@
 	icmp_param.data.times[2] = icmp_param.data.times[1];
 	if (skb_copy_bits(skb, 0, &icmp_param.data.times[0], 4))
 		BUG();
-	icmp_param.data.icmph	   = *skb->h.icmph;
+	icmp_param.data.icmph	   = *icmp_hdr(skb);
 	icmp_param.data.icmph.type = ICMP_TIMESTAMPREPLY;
 	icmp_param.data.icmph.code = 0;
 	icmp_param.skb		   = skb;
@@ -943,7 +944,7 @@
 	if (!pskb_pull(skb, sizeof(struct icmphdr)))
 		goto error;
 
-	icmph = skb->h.icmph;
+	icmph = icmp_hdr(skb);
 
 	/*
 	 *	18 is the highest 'known' ICMP type. Anything else is a mystery
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 8cedb2a..2506021 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -314,7 +314,9 @@
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	skb->nh.iph = pip =(struct iphdr *)skb_put(skb, sizeof(struct iphdr)+4);
+	skb_reset_network_header(skb);
+	pip = ip_hdr(skb);
+	skb_put(skb, sizeof(struct iphdr) + 4);
 
 	pip->version  = 4;
 	pip->ihl      = (sizeof(struct iphdr)+4)>>2;
@@ -331,8 +333,9 @@
 	((u8*)&pip[1])[2] = 0;
 	((u8*)&pip[1])[3] = 0;
 
-	pig =(struct igmpv3_report *)skb_put(skb, sizeof(*pig));
-	skb->h.igmph = (struct igmphdr *)pig;
+	skb->transport_header = skb->network_header + sizeof(struct iphdr) + 4;
+	skb_put(skb, sizeof(*pig));
+	pig = igmpv3_report_hdr(skb);
 	pig->type = IGMPV3_HOST_MEMBERSHIP_REPORT;
 	pig->resv1 = 0;
 	pig->csum = 0;
@@ -343,16 +346,14 @@
 
 static int igmpv3_sendpack(struct sk_buff *skb)
 {
-	struct iphdr *pip = skb->nh.iph;
-	struct igmphdr *pig = skb->h.igmph;
-	int iplen, igmplen;
+	struct iphdr *pip = ip_hdr(skb);
+	struct igmphdr *pig = igmp_hdr(skb);
+	const int iplen = skb->tail - skb->network_header;
+	const int igmplen = skb->tail - skb->transport_header;
 
-	iplen = skb->tail - (unsigned char *)skb->nh.iph;
 	pip->tot_len = htons(iplen);
 	ip_send_check(pip);
-
-	igmplen = skb->tail - (unsigned char *)skb->h.igmph;
-	pig->csum = ip_compute_csum((void *)skb->h.igmph, igmplen);
+	pig->csum = ip_compute_csum(igmp_hdr(skb), igmplen);
 
 	return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, skb->dev,
 		       dst_output);
@@ -379,7 +380,7 @@
 	pgr->grec_auxwords = 0;
 	pgr->grec_nsrcs = 0;
 	pgr->grec_mca = pmc->multiaddr;
-	pih = (struct igmpv3_report *)skb->h.igmph;
+	pih = igmpv3_report_hdr(skb);
 	pih->ngrec = htons(ntohs(pih->ngrec)+1);
 	*ppgr = pgr;
 	return skb;
@@ -412,7 +413,7 @@
 	if (!*psf_list)
 		goto empty_source;
 
-	pih = skb ? (struct igmpv3_report *)skb->h.igmph : NULL;
+	pih = skb ? igmpv3_report_hdr(skb) : NULL;
 
 	/* EX and TO_EX get a fresh packet, if needed */
 	if (truncate) {
@@ -664,7 +665,9 @@
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	skb->nh.iph = iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr)+4);
+	skb_reset_network_header(skb);
+	iph = ip_hdr(skb);
+	skb_put(skb, sizeof(struct iphdr) + 4);
 
 	iph->version  = 4;
 	iph->ihl      = (sizeof(struct iphdr)+4)>>2;
@@ -827,8 +830,8 @@
 static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
 	int len)
 {
-	struct igmphdr 		*ih = skb->h.igmph;
-	struct igmpv3_query *ih3 = (struct igmpv3_query *)ih;
+	struct igmphdr 		*ih = igmp_hdr(skb);
+	struct igmpv3_query *ih3 = igmpv3_query_hdr(skb);
 	struct ip_mc_list	*im;
 	__be32			group = ih->group;
 	int			max_delay;
@@ -861,12 +864,12 @@
 		if (!pskb_may_pull(skb, sizeof(struct igmpv3_query)))
 			return;
 
-		ih3 = (struct igmpv3_query *) skb->h.raw;
+		ih3 = igmpv3_query_hdr(skb);
 		if (ih3->nsrcs) {
 			if (!pskb_may_pull(skb, sizeof(struct igmpv3_query)
 					   + ntohs(ih3->nsrcs)*sizeof(__be32)))
 				return;
-			ih3 = (struct igmpv3_query *) skb->h.raw;
+			ih3 = igmpv3_query_hdr(skb);
 		}
 
 		max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE);
@@ -943,7 +946,7 @@
 			goto drop;
 	}
 
-	ih = skb->h.igmph;
+	ih = igmp_hdr(skb);
 	switch (ih->type) {
 	case IGMP_HOST_MEMBERSHIP_QUERY:
 		igmp_heard_query(in_dev, skb, len);
@@ -2397,7 +2400,7 @@
 	return 0;
 }
 
-static struct seq_operations igmp_mc_seq_ops = {
+static const struct seq_operations igmp_mc_seq_ops = {
 	.start	=	igmp_mc_seq_start,
 	.next	=	igmp_mc_seq_next,
 	.stop	=	igmp_mc_seq_stop,
@@ -2571,7 +2574,7 @@
 	return 0;
 }
 
-static struct seq_operations igmp_mcf_seq_ops = {
+static const struct seq_operations igmp_mcf_seq_ops = {
 	.start	=	igmp_mcf_seq_start,
 	.next	=	igmp_mcf_seq_next,
 	.stop	=	igmp_mcf_seq_stop,
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
index 5df71cd..dbeacd8 100644
--- a/net/ipv4/inet_diag.c
+++ b/net/ipv4/inet_diag.c
@@ -27,6 +27,7 @@
 #include <net/inet_hashtables.h>
 #include <net/inet_timewait_sock.h>
 #include <net/inet6_hashtables.h>
+#include <net/netlink.h>
 
 #include <linux/inet.h>
 #include <linux/stddef.h>
@@ -60,7 +61,7 @@
 	struct nlmsghdr  *nlh;
 	void *info = NULL;
 	struct inet_diag_meminfo  *minfo = NULL;
-	unsigned char	 *b = skb->tail;
+	unsigned char	 *b = skb_tail_pointer(skb);
 	const struct inet_diag_handler *handler;
 
 	handler = inet_diag_table[unlh->nlmsg_type];
@@ -147,12 +148,12 @@
 	    icsk->icsk_ca_ops && icsk->icsk_ca_ops->get_info)
 		icsk->icsk_ca_ops->get_info(sk, ext, skb);
 
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
 rtattr_failure:
 nlmsg_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -EMSGSIZE;
 }
 
@@ -163,7 +164,7 @@
 {
 	long tmo;
 	struct inet_diag_msg *r;
-	const unsigned char *previous_tail = skb->tail;
+	const unsigned char *previous_tail = skb_tail_pointer(skb);
 	struct nlmsghdr *nlh = NLMSG_PUT(skb, pid, seq,
 					 unlh->nlmsg_type, sizeof(*r));
 
@@ -205,10 +206,10 @@
 			       &tw6->tw_v6_daddr);
 	}
 #endif
-	nlh->nlmsg_len = skb->tail - previous_tail;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - previous_tail;
 	return skb->len;
 nlmsg_failure:
-	skb_trim(skb, previous_tail - skb->data);
+	nlmsg_trim(skb, previous_tail);
 	return -EMSGSIZE;
 }
 
@@ -535,7 +536,7 @@
 {
 	const struct inet_request_sock *ireq = inet_rsk(req);
 	struct inet_sock *inet = inet_sk(sk);
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct inet_diag_msg *r;
 	struct nlmsghdr *nlh;
 	long tmo;
@@ -574,12 +575,12 @@
 			       &inet6_rsk(req)->rmt_addr);
 	}
 #endif
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 
 	return skb->len;
 
 nlmsg_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -805,68 +806,43 @@
 	return skb->len;
 }
 
-static inline int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+static int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
-	if (!(nlh->nlmsg_flags&NLM_F_REQUEST))
-		return 0;
+	int hdrlen = sizeof(struct inet_diag_req);
 
-	if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX)
-		goto err_inval;
+	if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX ||
+	    nlmsg_len(nlh) < hdrlen)
+		return -EINVAL;
 
 	if (inet_diag_table[nlh->nlmsg_type] == NULL)
 		return -ENOENT;
 
-	if (NLMSG_LENGTH(sizeof(struct inet_diag_req)) > skb->len)
-		goto err_inval;
+	if (nlh->nlmsg_flags & NLM_F_DUMP) {
+		if (nlmsg_attrlen(nlh, hdrlen)) {
+			struct nlattr *attr;
 
-	if (nlh->nlmsg_flags&NLM_F_DUMP) {
-		if (nlh->nlmsg_len >
-		    (4 + NLMSG_SPACE(sizeof(struct inet_diag_req)))) {
-			struct rtattr *rta = (void *)(NLMSG_DATA(nlh) +
-						 sizeof(struct inet_diag_req));
-			if (rta->rta_type != INET_DIAG_REQ_BYTECODE ||
-			    rta->rta_len < 8 ||
-			    rta->rta_len >
-			    (nlh->nlmsg_len -
-			     NLMSG_SPACE(sizeof(struct inet_diag_req))))
-				goto err_inval;
-			if (inet_diag_bc_audit(RTA_DATA(rta), RTA_PAYLOAD(rta)))
-				goto err_inval;
+			attr = nlmsg_find_attr(nlh, hdrlen,
+					       INET_DIAG_REQ_BYTECODE);
+			if (attr == NULL ||
+			    nla_len(attr) < sizeof(struct inet_diag_bc_op) ||
+			    inet_diag_bc_audit(nla_data(attr), nla_len(attr)))
+				return -EINVAL;
 		}
+
 		return netlink_dump_start(idiagnl, skb, nlh,
 					  inet_diag_dump, NULL);
-	} else
-		return inet_diag_get_exact(skb, nlh);
-
-err_inval:
-	return -EINVAL;
-}
-
-
-static inline void inet_diag_rcv_skb(struct sk_buff *skb)
-{
-	if (skb->len >= NLMSG_SPACE(0)) {
-		int err;
-		struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data;
-
-		if (nlh->nlmsg_len < sizeof(*nlh) ||
-		    skb->len < nlh->nlmsg_len)
-			return;
-		err = inet_diag_rcv_msg(skb, nlh);
-		if (err || nlh->nlmsg_flags & NLM_F_ACK)
-			netlink_ack(skb, nlh, err);
 	}
+
+	return inet_diag_get_exact(skb, nlh);
 }
 
 static void inet_diag_rcv(struct sock *sk, int len)
 {
-	struct sk_buff *skb;
-	unsigned int qlen = skb_queue_len(&sk->sk_receive_queue);
+	unsigned int qlen = 0;
 
-	while (qlen-- && (skb = skb_dequeue(&sk->sk_receive_queue))) {
-		inet_diag_rcv_skb(skb);
-		kfree_skb(skb);
-	}
+	do {
+		netlink_run_queue(sk, &qlen, &inet_diag_rcv_msg);
+	} while (qlen);
 }
 
 static DEFINE_SPINLOCK(inet_diag_register_lock);
@@ -917,7 +893,7 @@
 		goto out;
 
 	idiagnl = netlink_kernel_create(NETLINK_INET_DIAG, 0, inet_diag_rcv,
-					THIS_MODULE);
+					NULL, THIS_MODULE);
 	if (idiagnl == NULL)
 		goto out_free_table;
 	err = 0;
diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c
index db3ef96..2f44e61 100644
--- a/net/ipv4/inetpeer.c
+++ b/net/ipv4/inetpeer.c
@@ -87,10 +87,12 @@
 
 static int peer_total;
 /* Exported for sysctl_net_ipv4.  */
-int inet_peer_threshold = 65536 + 128;	/* start to throw entries more
+int inet_peer_threshold __read_mostly = 65536 + 128;	/* start to throw entries more
 					 * aggressively at this stage */
-int inet_peer_minttl = 120 * HZ;	/* TTL under high load: 120 sec */
-int inet_peer_maxttl = 10 * 60 * HZ;	/* usual time to live: 10 min */
+int inet_peer_minttl __read_mostly = 120 * HZ;	/* TTL under high load: 120 sec */
+int inet_peer_maxttl __read_mostly = 10 * 60 * HZ;	/* usual time to live: 10 min */
+int inet_peer_gc_mintime __read_mostly = 10 * HZ;
+int inet_peer_gc_maxtime __read_mostly = 120 * HZ;
 
 static struct inet_peer *inet_peer_unused_head;
 static struct inet_peer **inet_peer_unused_tailp = &inet_peer_unused_head;
@@ -99,9 +101,6 @@
 static void peer_check_expire(unsigned long dummy);
 static DEFINE_TIMER(peer_periodic_timer, peer_check_expire, 0, 0);
 
-/* Exported for sysctl_net_ipv4.  */
-int inet_peer_gc_mintime = 10 * HZ,
-    inet_peer_gc_maxtime = 120 * HZ;
 
 /* Called from ip_output.c:ip_init  */
 void __init inet_initpeers(void)
@@ -151,20 +150,27 @@
 	spin_unlock_bh(&inet_peer_unused_lock);
 }
 
-/* Called with local BH disabled and the pool lock held. */
-#define lookup(daddr) 						\
+/*
+ * Called with local BH disabled and the pool lock held.
+ * _stack is known to be NULL or not at compile time,
+ * so compiler will optimize the if (_stack) tests.
+ */
+#define lookup(_daddr,_stack) 					\
 ({								\
 	struct inet_peer *u, **v;				\
-	stackptr = stack;					\
-	*stackptr++ = &peer_root;				\
+	if (_stack) {						\
+		stackptr = _stack;				\
+		*stackptr++ = &peer_root;			\
+	}							\
 	for (u = peer_root; u != peer_avl_empty; ) {		\
-		if (daddr == u->v4daddr)			\
+		if (_daddr == u->v4daddr)			\
 			break;					\
-		if ((__force __u32)daddr < (__force __u32)u->v4daddr)	\
+		if ((__force __u32)_daddr < (__force __u32)u->v4daddr)	\
 			v = &u->avl_left;			\
 		else						\
 			v = &u->avl_right;			\
-		*stackptr++ = v;				\
+		if (_stack)					\
+			*stackptr++ = v;			\
 		u = *v;						\
 	}							\
 	u;							\
@@ -288,7 +294,7 @@
 	if (atomic_read(&p->refcnt) == 1) {
 		struct inet_peer **stack[PEER_MAXDEPTH];
 		struct inet_peer ***stackptr, ***delp;
-		if (lookup(p->v4daddr) != p)
+		if (lookup(p->v4daddr, stack) != p)
 			BUG();
 		delp = stackptr - 1; /* *delp[0] == p */
 		if (p->avl_left == peer_avl_empty) {
@@ -373,7 +379,7 @@
 
 	/* Look up for the address quickly. */
 	read_lock_bh(&peer_pool_lock);
-	p = lookup(daddr);
+	p = lookup(daddr, NULL);
 	if (p != peer_avl_empty)
 		atomic_inc(&p->refcnt);
 	read_unlock_bh(&peer_pool_lock);
@@ -400,7 +406,7 @@
 
 	write_lock_bh(&peer_pool_lock);
 	/* Check if an entry has suddenly appeared. */
-	p = lookup(daddr);
+	p = lookup(daddr, stack);
 	if (p != peer_avl_empty)
 		goto out_free;
 
diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c
index 369e721..9cb04df 100644
--- a/net/ipv4/ip_forward.c
+++ b/net/ipv4/ip_forward.c
@@ -67,14 +67,14 @@
 	if (skb->pkt_type != PACKET_HOST)
 		goto drop;
 
-	skb->ip_summed = CHECKSUM_NONE;
+	skb_forward_csum(skb);
 
 	/*
 	 *	According to the RFC, we must first decrease the TTL field. If
 	 *	that reaches zero, we must reply an ICMP control message telling
 	 *	that the packet's lifetime expired.
 	 */
-	if (skb->nh.iph->ttl <= 1)
+	if (ip_hdr(skb)->ttl <= 1)
 		goto too_many_hops;
 
 	if (!xfrm4_route_forward(skb))
@@ -85,10 +85,18 @@
 	if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway)
 		goto sr_failed;
 
+	if (unlikely(skb->len > dst_mtu(&rt->u.dst) &&
+	             (ip_hdr(skb)->frag_off & htons(IP_DF))) && !skb->local_df) {
+		IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);
+		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
+			  htonl(dst_mtu(&rt->u.dst)));
+		goto drop;
+	}
+
 	/* We are about to mangle packet. Copy it! */
 	if (skb_cow(skb, LL_RESERVED_SPACE(rt->u.dst.dev)+rt->u.dst.header_len))
 		goto drop;
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 
 	/* Decrease ttl after skb cow done */
 	ip_decrease_ttl(iph);
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index b6f0553..0231bdc 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -92,7 +92,7 @@
 	spinlock_t	lock;
 	atomic_t	refcnt;
 	struct timer_list timer;	/* when will this queue expire?		*/
-	struct timeval	stamp;
+	ktime_t		stamp;
 	int             iif;
 	unsigned int    rid;
 	struct inet_peer *peer;
@@ -184,7 +184,7 @@
 {
 	struct ipq *qp = kmalloc(sizeof(struct ipq), GFP_ATOMIC);
 
-	if(!qp)
+	if (!qp)
 		return NULL;
 	atomic_add(sizeof(struct ipq), &ip_frag_mem);
 	return qp;
@@ -321,11 +321,11 @@
 	 * promoted read lock to write lock.
 	 */
 	hlist_for_each_entry(qp, n, &ipq_hash[hash], list) {
-		if(qp->id == qp_in->id		&&
-		   qp->saddr == qp_in->saddr	&&
-		   qp->daddr == qp_in->daddr	&&
-		   qp->protocol == qp_in->protocol &&
-		   qp->user == qp_in->user) {
+		if (qp->id == qp_in->id		&&
+		    qp->saddr == qp_in->saddr	&&
+		    qp->daddr == qp_in->daddr	&&
+		    qp->protocol == qp_in->protocol &&
+		    qp->user == qp_in->user) {
 			atomic_inc(&qp->refcnt);
 			write_unlock(&ipfrag_lock);
 			qp_in->last_in |= COMPLETE;
@@ -398,11 +398,11 @@
 	read_lock(&ipfrag_lock);
 	hash = ipqhashfn(id, saddr, daddr, protocol);
 	hlist_for_each_entry(qp, n, &ipq_hash[hash], list) {
-		if(qp->id == id		&&
-		   qp->saddr == saddr	&&
-		   qp->daddr == daddr	&&
-		   qp->protocol == protocol &&
-		   qp->user == user) {
+		if (qp->id == id		&&
+		    qp->saddr == saddr	&&
+		    qp->daddr == daddr	&&
+		    qp->protocol == protocol &&
+		    qp->user == user) {
 			atomic_inc(&qp->refcnt);
 			read_unlock(&ipfrag_lock);
 			return qp;
@@ -479,11 +479,11 @@
 		goto err;
 	}
 
-	offset = ntohs(skb->nh.iph->frag_off);
+	offset = ntohs(ip_hdr(skb)->frag_off);
 	flags = offset & ~IP_OFFSET;
 	offset &= IP_OFFSET;
 	offset <<= 3;		/* offset is in 8-byte chunks */
-	ihl = skb->nh.iph->ihl * 4;
+	ihl = ip_hdrlen(skb);
 
 	/* Determine the position of this fragment. */
 	end = offset + skb->len - ihl;
@@ -524,7 +524,7 @@
 	 * this fragment, right?
 	 */
 	prev = NULL;
-	for(next = qp->fragments; next != NULL; next = next->next) {
+	for (next = qp->fragments; next != NULL; next = next->next) {
 		if (FRAG_CB(next)->offset >= offset)
 			break;	/* bingo! */
 		prev = next;
@@ -592,7 +592,7 @@
 	if (skb->dev)
 		qp->iif = skb->dev->ifindex;
 	skb->dev = NULL;
-	skb_get_timestamp(skb, &qp->stamp);
+	qp->stamp = skb->tstamp;
 	qp->meat += skb->len;
 	atomic_add(skb->truesize, &ip_frag_mem);
 	if (offset == 0)
@@ -624,10 +624,10 @@
 	BUG_TRAP(FRAG_CB(head)->offset == 0);
 
 	/* Allocate a new buffer for the datagram. */
-	ihlen = head->nh.iph->ihl*4;
+	ihlen = ip_hdrlen(head);
 	len = ihlen + qp->len;
 
-	if(len > 65535)
+	if (len > 65535)
 		goto out_oversize;
 
 	/* Head of list must not be cloned. */
@@ -658,7 +658,7 @@
 	}
 
 	skb_shinfo(head)->frag_list = head->next;
-	skb_push(head, head->data - head->nh.raw);
+	skb_push(head, head->data - skb_network_header(head));
 	atomic_sub(head->truesize, &ip_frag_mem);
 
 	for (fp=head->next; fp; fp = fp->next) {
@@ -674,9 +674,9 @@
 
 	head->next = NULL;
 	head->dev = dev;
-	skb_set_timestamp(head, &qp->stamp);
+	head->tstamp = qp->stamp;
 
-	iph = head->nh.iph;
+	iph = ip_hdr(head);
 	iph->frag_off = 0;
 	iph->tot_len = htons(len);
 	IP_INC_STATS_BH(IPSTATS_MIB_REASMOKS);
@@ -700,7 +700,6 @@
 /* Process an incoming IP datagram fragment. */
 struct sk_buff *ip_defrag(struct sk_buff *skb, u32 user)
 {
-	struct iphdr *iph = skb->nh.iph;
 	struct ipq *qp;
 	struct net_device *dev;
 
@@ -713,7 +712,7 @@
 	dev = skb->dev;
 
 	/* Lookup (or create) queue header */
-	if ((qp = ip_find(iph, user)) != NULL) {
+	if ((qp = ip_find(ip_hdr(skb), user)) != NULL) {
 		struct sk_buff *ret = NULL;
 
 		spin_lock(&qp->lock);
@@ -734,7 +733,7 @@
 	return NULL;
 }
 
-void ipfrag_init(void)
+void __init ipfrag_init(void)
 {
 	ipfrag_hash_rnd = (u32) ((num_physpages ^ (num_physpages>>7)) ^
 				 (jiffies ^ (jiffies >> 6)));
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 9151da6..6328293 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -191,11 +191,11 @@
 	return NULL;
 }
 
-static struct ip_tunnel **ipgre_bucket(struct ip_tunnel *t)
+static struct ip_tunnel **__ipgre_bucket(struct ip_tunnel_parm *parms)
 {
-	__be32 remote = t->parms.iph.daddr;
-	__be32 local = t->parms.iph.saddr;
-	__be32 key = t->parms.i_key;
+	__be32 remote = parms->iph.daddr;
+	__be32 local = parms->iph.saddr;
+	__be32 key = parms->i_key;
 	unsigned h = HASH(key);
 	int prio = 0;
 
@@ -209,6 +209,11 @@
 	return &tunnels[prio][h];
 }
 
+static inline struct ip_tunnel **ipgre_bucket(struct ip_tunnel *t)
+{
+	return __ipgre_bucket(&t->parms);
+}
+
 static void ipgre_tunnel_link(struct ip_tunnel *t)
 {
 	struct ip_tunnel **tp = ipgre_bucket(t);
@@ -240,17 +245,9 @@
 	__be32 key = parms->i_key;
 	struct ip_tunnel *t, **tp, *nt;
 	struct net_device *dev;
-	unsigned h = HASH(key);
-	int prio = 0;
 	char name[IFNAMSIZ];
 
-	if (local)
-		prio |= 1;
-	if (remote && !MULTICAST(remote)) {
-		prio |= 2;
-		h ^= HASH(remote);
-	}
-	for (tp = &tunnels[prio][h]; (t = *tp) != NULL; tp = &t->next) {
+	for (tp = __ipgre_bucket(parms); (t = *tp) != NULL; tp = &t->next) {
 		if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr) {
 			if (key == t->parms.i_key)
 				return t;
@@ -320,8 +317,8 @@
 	struct iphdr *iph = (struct iphdr*)skb->data;
 	__be16	     *p = (__be16*)(skb->data+(iph->ihl<<2));
 	int grehlen = (iph->ihl<<2) + 4;
-	int type = skb->h.icmph->type;
-	int code = skb->h.icmph->code;
+	const int type = icmp_hdr(skb)->type;
+	const int code = icmp_hdr(skb)->code;
 	struct ip_tunnel *t;
 	__be16 flags;
 
@@ -388,8 +385,8 @@
 	struct iphdr *iph = (struct iphdr*)dp;
 	struct iphdr *eiph;
 	__be16	     *p = (__be16*)(dp+(iph->ihl<<2));
-	int type = skb->h.icmph->type;
-	int code = skb->h.icmph->code;
+	const int type = icmp_hdr(skb)->type;
+	const int code = icmp_hdr(skb)->code;
 	int rel_type = 0;
 	int rel_code = 0;
 	__be32 rel_info = 0;
@@ -422,7 +419,7 @@
 	default:
 		return;
 	case ICMP_PARAMETERPROB:
-		n = ntohl(skb->h.icmph->un.gateway) >> 24;
+		n = ntohl(icmp_hdr(skb)->un.gateway) >> 24;
 		if (n < (iph->ihl<<2))
 			return;
 
@@ -442,7 +439,7 @@
 			return;
 		case ICMP_FRAG_NEEDED:
 			/* And it is the only really necessary thing :-) */
-			n = ntohs(skb->h.icmph->un.frag.mtu);
+			n = ntohs(icmp_hdr(skb)->un.frag.mtu);
 			if (n < grehlen+68)
 				return;
 			n -= grehlen;
@@ -474,7 +471,7 @@
 	dst_release(skb2->dst);
 	skb2->dst = NULL;
 	skb_pull(skb2, skb->data - (u8*)eiph);
-	skb2->nh.raw = skb2->data;
+	skb_reset_network_header(skb2);
 
 	/* Try to guess incoming interface */
 	memset(&fl, 0, sizeof(fl));
@@ -533,9 +530,9 @@
 {
 	if (INET_ECN_is_ce(iph->tos)) {
 		if (skb->protocol == htons(ETH_P_IP)) {
-			IP_ECN_set_ce(skb->nh.iph);
+			IP_ECN_set_ce(ip_hdr(skb));
 		} else if (skb->protocol == htons(ETH_P_IPV6)) {
-			IP6_ECN_set_ce(skb->nh.ipv6h);
+			IP6_ECN_set_ce(ipv6_hdr(skb));
 		}
 	}
 }
@@ -565,7 +562,7 @@
 	if (!pskb_may_pull(skb, 16))
 		goto drop_nolock;
 
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 	h = skb->data;
 	flags = *(__be16*)h;
 
@@ -616,9 +613,10 @@
 				offset += 4;
 		}
 
-		skb->mac.raw = skb->nh.raw;
-		skb->nh.raw = __pskb_pull(skb, offset);
-		skb_postpull_rcsum(skb, skb->h.raw, offset);
+		skb_reset_mac_header(skb);
+		__pskb_pull(skb, offset);
+		skb_reset_network_header(skb);
+		skb_postpull_rcsum(skb, skb_transport_header(skb), offset);
 		skb->pkt_type = PACKET_HOST;
 #ifdef CONFIG_NET_IPGRE_BROADCAST
 		if (MULTICAST(iph->daddr)) {
@@ -669,7 +667,7 @@
 {
 	struct ip_tunnel *tunnel = netdev_priv(dev);
 	struct net_device_stats *stats = &tunnel->stat;
-	struct iphdr  *old_iph = skb->nh.iph;
+	struct iphdr  *old_iph = ip_hdr(skb);
 	struct iphdr  *tiph;
 	u8     tos;
 	__be16 df;
@@ -720,7 +718,7 @@
 			addr_type = ipv6_addr_type(addr6);
 
 			if (addr_type == IPV6_ADDR_ANY) {
-				addr6 = &skb->nh.ipv6h->daddr;
+				addr6 = &ipv6_hdr(skb)->daddr;
 				addr_type = ipv6_addr_type(addr6);
 			}
 
@@ -824,11 +822,12 @@
 			skb_set_owner_w(new_skb, skb->sk);
 		dev_kfree_skb(skb);
 		skb = new_skb;
-		old_iph = skb->nh.iph;
+		old_iph = ip_hdr(skb);
 	}
 
-	skb->h.raw = skb->nh.raw;
-	skb->nh.raw = skb_push(skb, gre_hlen);
+	skb->transport_header = skb->network_header;
+	skb_push(skb, gre_hlen);
+	skb_reset_network_header(skb);
 	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
 	IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
 			      IPSKB_REROUTED);
@@ -839,7 +838,7 @@
 	 *	Push down and install the IPIP header.
 	 */
 
-	iph 			=	skb->nh.iph;
+	iph 			=	ip_hdr(skb);
 	iph->version		=	4;
 	iph->ihl		=	sizeof(struct iphdr) >> 2;
 	iph->frag_off		=	df;
diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c
index f38e976..9706939 100644
--- a/net/ipv4/ip_input.c
+++ b/net/ipv4/ip_input.c
@@ -158,7 +158,7 @@
 int ip_call_ra_chain(struct sk_buff *skb)
 {
 	struct ip_ra_chain *ra;
-	u8 protocol = skb->nh.iph->protocol;
+	u8 protocol = ip_hdr(skb)->protocol;
 	struct sock *last = NULL;
 
 	read_lock(&ip_ra_lock);
@@ -171,7 +171,7 @@
 		if (sk && inet_sk(sk)->num == protocol &&
 		    (!sk->sk_bound_dev_if ||
 		     sk->sk_bound_dev_if == skb->dev->ifindex)) {
-			if (skb->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
+			if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {
 				skb = ip_defrag(skb, IP_DEFRAG_CALL_RA_CHAIN);
 				if (skb == NULL) {
 					read_unlock(&ip_ra_lock);
@@ -198,17 +198,15 @@
 
 static inline int ip_local_deliver_finish(struct sk_buff *skb)
 {
-	int ihl = skb->nh.iph->ihl*4;
-
-	__skb_pull(skb, ihl);
+	__skb_pull(skb, ip_hdrlen(skb));
 
 	/* Point into the IP datagram, just past the header. */
-	skb->h.raw = skb->data;
+	skb_reset_transport_header(skb);
 
 	rcu_read_lock();
 	{
 		/* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
-		int protocol = skb->nh.iph->protocol;
+		int protocol = ip_hdr(skb)->protocol;
 		int hash;
 		struct sock *raw_sk;
 		struct net_protocol *ipprot;
@@ -220,7 +218,7 @@
 		/* If there maybe a raw socket we must check - if not we
 		 * don't care less
 		 */
-		if (raw_sk && !raw_v4_input(skb, skb->nh.iph, hash))
+		if (raw_sk && !raw_v4_input(skb, ip_hdr(skb), hash))
 			raw_sk = NULL;
 
 		if ((ipprot = rcu_dereference(inet_protos[hash])) != NULL) {
@@ -266,7 +264,7 @@
 	 *	Reassemble IP fragments.
 	 */
 
-	if (skb->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
+	if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {
 		skb = ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER);
 		if (!skb)
 			return 0;
@@ -294,7 +292,7 @@
 		goto drop;
 	}
 
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 
 	if (ip_options_compile(NULL, skb)) {
 		IP_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
@@ -330,7 +328,8 @@
 
 static inline int ip_rcv_finish(struct sk_buff *skb)
 {
-	struct iphdr *iph = skb->nh.iph;
+	const struct iphdr *iph = ip_hdr(skb);
+	struct rtable *rt;
 
 	/*
 	 *	Initialise the virtual path cache for the packet. It describes
@@ -342,6 +341,8 @@
 		if (unlikely(err)) {
 			if (err == -EHOSTUNREACH)
 				IP_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
+			else if (err == -ENETUNREACH)
+				IP_INC_STATS_BH(IPSTATS_MIB_INNOROUTES);
 			goto drop;
 		}
 	}
@@ -360,6 +361,12 @@
 	if (iph->ihl > 5 && ip_rcv_options(skb))
 		goto drop;
 
+	rt = (struct rtable*)skb->dst;
+	if (rt->rt_type == RTN_MULTICAST)
+		IP_INC_STATS_BH(IPSTATS_MIB_INMCASTPKTS);
+	else if (rt->rt_type == RTN_BROADCAST)
+		IP_INC_STATS_BH(IPSTATS_MIB_INBCASTPKTS);
+
 	return dst_input(skb);
 
 drop:
@@ -391,7 +398,7 @@
 	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
 		goto inhdr_error;
 
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 
 	/*
 	 *	RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum.
@@ -410,13 +417,16 @@
 	if (!pskb_may_pull(skb, iph->ihl*4))
 		goto inhdr_error;
 
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 
 	if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
 		goto inhdr_error;
 
 	len = ntohs(iph->tot_len);
-	if (skb->len < len || len < (iph->ihl*4))
+	if (skb->len < len) {
+		IP_INC_STATS_BH(IPSTATS_MIB_INTRUNCATEDPKTS);
+		goto drop;
+	} else if (len < (iph->ihl*4))
 		goto inhdr_error;
 
 	/* Our transport medium may have padded the buffer out. Now we know it
diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c
index f906a80..25134682 100644
--- a/net/ipv4/ip_options.c
+++ b/net/ipv4/ip_options.c
@@ -40,7 +40,7 @@
 void ip_options_build(struct sk_buff * skb, struct ip_options * opt,
 			    __be32 daddr, struct rtable *rt, int is_frag)
 {
-	unsigned char * iph = skb->nh.raw;
+	unsigned char *iph = skb_network_header(skb);
 
 	memcpy(&(IPCB(skb)->opt), opt, sizeof(struct ip_options));
 	memcpy(iph+sizeof(struct iphdr), opt->__data, opt->optlen);
@@ -104,13 +104,13 @@
 		return 0;
 	}
 
-	sptr = skb->nh.raw;
+	sptr = skb_network_header(skb);
 	dptr = dopt->__data;
 
 	if (skb->dst)
 		daddr = ((struct rtable*)skb->dst)->rt_spec_dst;
 	else
-		daddr = skb->nh.iph->daddr;
+		daddr = ip_hdr(skb)->daddr;
 
 	if (sopt->rr) {
 		optlen  = sptr[sopt->rr+1];
@@ -180,7 +180,8 @@
 			/*
 			 * RFC1812 requires to fix illegal source routes.
 			 */
-			if (memcmp(&skb->nh.iph->saddr, &start[soffset+3], 4) == 0)
+			if (memcmp(&ip_hdr(skb)->saddr,
+				   &start[soffset + 3], 4) == 0)
 				doffset -= 4;
 		}
 		if (doffset > 3) {
@@ -217,7 +218,7 @@
 
 void ip_options_fragment(struct sk_buff * skb)
 {
-	unsigned char * optptr = skb->nh.raw + sizeof(struct iphdr);
+	unsigned char *optptr = skb_network_header(skb) + sizeof(struct iphdr);
 	struct ip_options * opt = &(IPCB(skb)->opt);
 	int  l = opt->optlen;
 	int  optlen;
@@ -264,12 +265,13 @@
 
 	if (!opt) {
 		opt = &(IPCB(skb)->opt);
-		iph = skb->nh.raw;
+		iph = skb_network_header(skb);
 		opt->optlen = ((struct iphdr *)iph)->ihl*4 - sizeof(struct iphdr);
 		optptr = iph + sizeof(struct iphdr);
 		opt->is_data = 0;
 	} else {
-		optptr = opt->is_data ? opt->__data : (unsigned char*)&(skb->nh.iph[1]);
+		optptr = opt->is_data ? opt->__data :
+					(unsigned char *)&(ip_hdr(skb)[1]);
 		iph = optptr - sizeof(struct iphdr);
 	}
 
@@ -563,7 +565,7 @@
 	struct   ip_options * opt	= &(IPCB(skb)->opt);
 	unsigned char * optptr;
 	struct rtable *rt = (struct rtable*)skb->dst;
-	unsigned char *raw = skb->nh.raw;
+	unsigned char *raw = skb_network_header(skb);
 
 	if (opt->rr_needaddr) {
 		optptr = (unsigned char *)raw + opt->rr;
@@ -587,7 +589,7 @@
 		if (srrptr + 3 <= srrspace) {
 			opt->is_changed = 1;
 			ip_rt_get_source(&optptr[srrptr-1], rt);
-			skb->nh.iph->daddr = rt->rt_dst;
+			ip_hdr(skb)->daddr = rt->rt_dst;
 			optptr[2] = srrptr+4;
 		} else if (net_ratelimit())
 			printk(KERN_CRIT "ip_forward(): Argh! Destination lost!\n");
@@ -599,7 +601,7 @@
 	}
 	if (opt->is_changed) {
 		opt->is_changed = 0;
-		ip_send_check(skb->nh.iph);
+		ip_send_check(ip_hdr(skb));
 	}
 }
 
@@ -608,8 +610,8 @@
 	struct ip_options *opt = &(IPCB(skb)->opt);
 	int srrspace, srrptr;
 	__be32 nexthop;
-	struct iphdr *iph = skb->nh.iph;
-	unsigned char * optptr = skb->nh.raw + opt->srr;
+	struct iphdr *iph = ip_hdr(skb);
+	unsigned char *optptr = skb_network_header(skb) + opt->srr;
 	struct rtable *rt = (struct rtable*)skb->dst;
 	struct rtable *rt2;
 	int err;
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index d096332..d6427d9 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -95,8 +95,8 @@
 /* dev_loopback_xmit for use with netfilter. */
 static int ip_dev_loopback_xmit(struct sk_buff *newskb)
 {
-	newskb->mac.raw = newskb->data;
-	__skb_pull(newskb, newskb->nh.raw - newskb->data);
+	skb_reset_mac_header(newskb);
+	__skb_pull(newskb, skb_network_offset(newskb));
 	newskb->pkt_type = PACKET_LOOPBACK;
 	newskb->ip_summed = CHECKSUM_UNNECESSARY;
 	BUG_TRAP(newskb->dst);
@@ -125,11 +125,9 @@
 	struct iphdr *iph;
 
 	/* Build the IP header. */
-	if (opt)
-		iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr) + opt->optlen);
-	else
-		iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr));
-
+	skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));
+	skb_reset_network_header(skb);
+	iph = ip_hdr(skb);
 	iph->version  = 4;
 	iph->ihl      = 5;
 	iph->tos      = inet->tos;
@@ -143,7 +141,6 @@
 	iph->protocol = sk->sk_protocol;
 	iph->tot_len  = htons(skb->len);
 	ip_select_ident(iph, &rt->u.dst, sk);
-	skb->nh.iph   = iph;
 
 	if (opt && opt->optlen) {
 		iph->ihl += opt->optlen>>2;
@@ -163,9 +160,15 @@
 static inline int ip_finish_output2(struct sk_buff *skb)
 {
 	struct dst_entry *dst = skb->dst;
+	struct rtable *rt = (struct rtable *)dst;
 	struct net_device *dev = dst->dev;
 	int hh_len = LL_RESERVED_SPACE(dev);
 
+	if (rt->rt_type == RTN_MULTICAST)
+		IP_INC_STATS(IPSTATS_MIB_OUTMCASTPKTS);
+	else if (rt->rt_type == RTN_BROADCAST)
+		IP_INC_STATS(IPSTATS_MIB_OUTBCASTPKTS);
+
 	/* Be paranoid, rather than too clever. */
 	if (unlikely(skb_headroom(skb) < hh_len && dev->hard_header)) {
 		struct sk_buff *skb2;
@@ -192,6 +195,14 @@
 	return -EINVAL;
 }
 
+static inline int ip_skb_dst_mtu(struct sk_buff *skb)
+{
+	struct inet_sock *inet = skb->sk ? inet_sk(skb->sk) : NULL;
+
+	return (inet && inet->pmtudisc == IP_PMTUDISC_PROBE) ?
+	       skb->dst->dev->mtu : dst_mtu(skb->dst);
+}
+
 static inline int ip_finish_output(struct sk_buff *skb)
 {
 #if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
@@ -201,7 +212,7 @@
 		return dst_output(skb);
 	}
 #endif
-	if (skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb))
+	if (skb->len > ip_skb_dst_mtu(skb) && !skb_is_gso(skb))
 		return ip_fragment(skb, ip_finish_output2);
 	else
 		return ip_finish_output2(skb);
@@ -248,7 +259,7 @@
 
 		/* Multicasts with ttl 0 must not go beyond the host */
 
-		if (skb->nh.iph->ttl == 0) {
+		if (ip_hdr(skb)->ttl == 0) {
 			kfree_skb(skb);
 			return 0;
 		}
@@ -333,7 +344,9 @@
 		goto no_route;
 
 	/* OK, we know where to send it, allocate and build IP header. */
-	iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));
+	skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));
+	skb_reset_network_header(skb);
+	iph = ip_hdr(skb);
 	*((__be16 *)iph) = htons((4 << 12) | (5 << 8) | (inet->tos & 0xff));
 	iph->tot_len = htons(skb->len);
 	if (ip_dont_fragment(sk, &rt->u.dst) && !ipfragok)
@@ -344,7 +357,6 @@
 	iph->protocol = sk->sk_protocol;
 	iph->saddr    = rt->rt_src;
 	iph->daddr    = rt->rt_dst;
-	skb->nh.iph   = iph;
 	/* Transport layer set skb->h.foo itself. */
 
 	if (opt && opt->optlen) {
@@ -386,21 +398,10 @@
 #ifdef CONFIG_NET_SCHED
 	to->tc_index = from->tc_index;
 #endif
-#ifdef CONFIG_NETFILTER
-	/* Connection association is same as pre-frag packet */
-	nf_conntrack_put(to->nfct);
-	to->nfct = from->nfct;
-	nf_conntrack_get(to->nfct);
-	to->nfctinfo = from->nfctinfo;
+	nf_copy(to, from);
 #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)
 	to->ipvs_property = from->ipvs_property;
 #endif
-#ifdef CONFIG_BRIDGE_NETFILTER
-	nf_bridge_put(to->nf_bridge);
-	to->nf_bridge = from->nf_bridge;
-	nf_bridge_get(to->nf_bridge);
-#endif
-#endif
 	skb_copy_secmark(to, from);
 }
 
@@ -430,12 +431,12 @@
 	 *	Point into the IP datagram header.
 	 */
 
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 
 	if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) {
 		IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);
 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
-			  htonl(dst_mtu(&rt->u.dst)));
+			  htonl(ip_skb_dst_mtu(skb)));
 		kfree_skb(skb);
 		return -EMSGSIZE;
 	}
@@ -502,10 +503,11 @@
 			 * before previous one went down. */
 			if (frag) {
 				frag->ip_summed = CHECKSUM_NONE;
-				frag->h.raw = frag->data;
-				frag->nh.raw = __skb_push(frag, hlen);
-				memcpy(frag->nh.raw, iph, hlen);
-				iph = frag->nh.iph;
+				skb_reset_transport_header(frag);
+				__skb_push(frag, hlen);
+				skb_reset_network_header(frag);
+				memcpy(skb_network_header(frag), iph, hlen);
+				iph = ip_hdr(frag);
 				iph->tot_len = htons(frag->len);
 				ip_copy_metadata(frag, skb);
 				if (offset == 0)
@@ -566,7 +568,7 @@
 	 *	Keep copying data until we run out.
 	 */
 
-	while(left > 0)	{
+	while (left > 0) {
 		len = left;
 		/* IF: it doesn't fit, use 'mtu' - the data space left */
 		if (len > mtu)
@@ -593,8 +595,8 @@
 		ip_copy_metadata(skb2, skb);
 		skb_reserve(skb2, ll_rs);
 		skb_put(skb2, len + hlen);
-		skb2->nh.raw = skb2->data;
-		skb2->h.raw = skb2->data + hlen;
+		skb_reset_network_header(skb2);
+		skb2->transport_header = skb2->network_header + hlen;
 
 		/*
 		 *	Charge the memory for the fragment to any owner
@@ -608,19 +610,19 @@
 		 *	Copy the packet header into the new buffer.
 		 */
 
-		memcpy(skb2->nh.raw, skb->data, hlen);
+		skb_copy_from_linear_data(skb, skb_network_header(skb2), hlen);
 
 		/*
 		 *	Copy a block of the IP datagram.
 		 */
-		if (skb_copy_bits(skb, ptr, skb2->h.raw, len))
+		if (skb_copy_bits(skb, ptr, skb_transport_header(skb2), len))
 			BUG();
 		left -= len;
 
 		/*
 		 *	Fill in the new header fields.
 		 */
-		iph = skb2->nh.iph;
+		iph = ip_hdr(skb2);
 		iph->frag_off = htons((offset >> 3));
 
 		/* ANK: dirty, but effective trick. Upgrade options only if
@@ -722,10 +724,10 @@
 		skb_put(skb,fragheaderlen + transhdrlen);
 
 		/* initialize network header pointer */
-		skb->nh.raw = skb->data;
+		skb_reset_network_header(skb);
 
 		/* initialize protocol header pointer */
-		skb->h.raw = skb->data + fragheaderlen;
+		skb->transport_header = skb->network_header + fragheaderlen;
 
 		skb->ip_summed = CHECKSUM_PARTIAL;
 		skb->csum = 0;
@@ -799,7 +801,9 @@
 			inet->cork.addr = ipc->addr;
 		}
 		dst_hold(&rt->u.dst);
-		inet->cork.fragsize = mtu = dst_mtu(rt->u.dst.path);
+		inet->cork.fragsize = mtu = inet->pmtudisc == IP_PMTUDISC_PROBE ?
+					    rt->u.dst.dev->mtu :
+					    dst_mtu(rt->u.dst.path);
 		inet->cork.rt = rt;
 		inet->cork.length = 0;
 		sk->sk_sndmsg_page = NULL;
@@ -929,9 +933,10 @@
 			 *	Find where to start putting bytes.
 			 */
 			data = skb_put(skb, fraglen);
-			skb->nh.raw = data + exthdrlen;
+			skb_set_network_header(skb, exthdrlen);
+			skb->transport_header = (skb->network_header +
+						 fragheaderlen);
 			data += fragheaderlen;
-			skb->h.raw = data + exthdrlen;
 
 			if (fraggap) {
 				skb->csum = skb_copy_and_csum_bits(
@@ -1100,8 +1105,6 @@
 		}
 		if (len <= 0) {
 			struct sk_buff *skb_prev;
-			char *data;
-			struct iphdr *iph;
 			int alloclen;
 
 			skb_prev = skb;
@@ -1124,15 +1127,15 @@
 			/*
 			 *	Find where to start putting bytes.
 			 */
-			data = skb_put(skb, fragheaderlen + fraggap);
-			skb->nh.iph = iph = (struct iphdr *)data;
-			data += fragheaderlen;
-			skb->h.raw = data;
-
+			skb_put(skb, fragheaderlen + fraggap);
+			skb_reset_network_header(skb);
+			skb->transport_header = (skb->network_header +
+						 fragheaderlen);
 			if (fraggap) {
-				skb->csum = skb_copy_and_csum_bits(
-					skb_prev, maxfraglen,
-					data, fraggap, 0);
+				skb->csum = skb_copy_and_csum_bits(skb_prev,
+								   maxfraglen,
+						    skb_transport_header(skb),
+								   fraggap, 0);
 				skb_prev->csum = csum_sub(skb_prev->csum,
 							  skb->csum);
 				pskb_trim_unique(skb_prev, maxfraglen);
@@ -1198,10 +1201,10 @@
 	tail_skb = &(skb_shinfo(skb)->frag_list);
 
 	/* move skb->data to ip header from ext header */
-	if (skb->data < skb->nh.raw)
-		__skb_pull(skb, skb->nh.raw - skb->data);
+	if (skb->data < skb_network_header(skb))
+		__skb_pull(skb, skb_network_offset(skb));
 	while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) {
-		__skb_pull(tmp_skb, skb->h.raw - skb->nh.raw);
+		__skb_pull(tmp_skb, skb_network_header_len(skb));
 		*tail_skb = tmp_skb;
 		tail_skb = &(tmp_skb->next);
 		skb->len += tmp_skb->len;
@@ -1216,13 +1219,13 @@
 	 * to fragment the frame generated here. No matter, what transforms
 	 * how transforms change size of the packet, it will come out.
 	 */
-	if (inet->pmtudisc != IP_PMTUDISC_DO)
+	if (inet->pmtudisc < IP_PMTUDISC_DO)
 		skb->local_df = 1;
 
 	/* DF bit is set when we want to see DF on outgoing frames.
 	 * If local_df is set too, we still allow to fragment this frame
 	 * locally. */
-	if (inet->pmtudisc == IP_PMTUDISC_DO ||
+	if (inet->pmtudisc >= IP_PMTUDISC_DO ||
 	    (skb->len <= dst_mtu(&rt->u.dst) &&
 	     ip_dont_fragment(sk, &rt->u.dst)))
 		df = htons(IP_DF);
@@ -1352,11 +1355,11 @@
 		struct flowi fl = { .nl_u = { .ip4_u =
 					      { .daddr = daddr,
 						.saddr = rt->rt_spec_dst,
-						.tos = RT_TOS(skb->nh.iph->tos) } },
+						.tos = RT_TOS(ip_hdr(skb)->tos) } },
 				    /* Not quite clean, but right. */
 				    .uli_u = { .ports =
-					       { .sport = skb->h.th->dest,
-						 .dport = skb->h.th->source } },
+					       { .sport = tcp_hdr(skb)->dest,
+						 .dport = tcp_hdr(skb)->source } },
 				    .proto = sk->sk_protocol };
 		security_skb_classify_flow(skb, &fl);
 		if (ip_route_output_key(&rt, &fl))
@@ -1370,14 +1373,16 @@
 	   with locally disabled BH and that sk cannot be already spinlocked.
 	 */
 	bh_lock_sock(sk);
-	inet->tos = skb->nh.iph->tos;
+	inet->tos = ip_hdr(skb)->tos;
 	sk->sk_priority = skb->priority;
-	sk->sk_protocol = skb->nh.iph->protocol;
+	sk->sk_protocol = ip_hdr(skb)->protocol;
 	ip_append_data(sk, ip_reply_glue_bits, arg->iov->iov_base, len, 0,
 		       &ipc, rt, MSG_DONTWAIT);
 	if ((skb = skb_peek(&sk->sk_write_queue)) != NULL) {
 		if (arg->csumoffset >= 0)
-			*((__sum16 *)skb->h.raw + arg->csumoffset) = csum_fold(csum_add(skb->csum, arg->csum));
+			*((__sum16 *)skb_transport_header(skb) +
+			  arg->csumoffset) = csum_fold(csum_add(skb->csum,
+								arg->csum));
 		skb->ip_summed = CHECKSUM_NONE;
 		ip_push_pending_frames(sk);
 	}
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index 23048d9..4d54457 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -59,7 +59,7 @@
 	struct in_pktinfo info;
 	struct rtable *rt = (struct rtable *)skb->dst;
 
-	info.ipi_addr.s_addr = skb->nh.iph->daddr;
+	info.ipi_addr.s_addr = ip_hdr(skb)->daddr;
 	if (rt) {
 		info.ipi_ifindex = rt->rt_iif;
 		info.ipi_spec_dst.s_addr = rt->rt_spec_dst;
@@ -73,13 +73,13 @@
 
 static void ip_cmsg_recv_ttl(struct msghdr *msg, struct sk_buff *skb)
 {
-	int ttl = skb->nh.iph->ttl;
+	int ttl = ip_hdr(skb)->ttl;
 	put_cmsg(msg, SOL_IP, IP_TTL, sizeof(int), &ttl);
 }
 
 static void ip_cmsg_recv_tos(struct msghdr *msg, struct sk_buff *skb)
 {
-	put_cmsg(msg, SOL_IP, IP_TOS, 1, &skb->nh.iph->tos);
+	put_cmsg(msg, SOL_IP, IP_TOS, 1, &ip_hdr(skb)->tos);
 }
 
 static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb)
@@ -87,7 +87,8 @@
 	if (IPCB(skb)->opt.optlen == 0)
 		return;
 
-	put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen, skb->nh.iph+1);
+	put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen,
+		 ip_hdr(skb) + 1);
 }
 
 
@@ -268,18 +269,21 @@
 	serr = SKB_EXT_ERR(skb);
 	serr->ee.ee_errno = err;
 	serr->ee.ee_origin = SO_EE_ORIGIN_ICMP;
-	serr->ee.ee_type = skb->h.icmph->type;
-	serr->ee.ee_code = skb->h.icmph->code;
+	serr->ee.ee_type = icmp_hdr(skb)->type;
+	serr->ee.ee_code = icmp_hdr(skb)->code;
 	serr->ee.ee_pad = 0;
 	serr->ee.ee_info = info;
 	serr->ee.ee_data = 0;
-	serr->addr_offset = (u8*)&(((struct iphdr*)(skb->h.icmph+1))->daddr) - skb->nh.raw;
+	serr->addr_offset = (u8 *)&(((struct iphdr *)(icmp_hdr(skb) + 1))->daddr) -
+				   skb_network_header(skb);
 	serr->port = port;
 
-	skb->h.raw = payload;
-	if (!skb_pull(skb, payload - skb->data) ||
-	    sock_queue_err_skb(sk, skb))
-		kfree_skb(skb);
+	if (skb_pull(skb, payload - skb->data) != NULL) {
+		skb_reset_transport_header(skb);
+		if (sock_queue_err_skb(sk, skb) == 0)
+			return;
+	}
+	kfree_skb(skb);
 }
 
 void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info)
@@ -296,8 +300,9 @@
 	if (!skb)
 		return;
 
-	iph = (struct iphdr*)skb_put(skb, sizeof(struct iphdr));
-	skb->nh.iph = iph;
+	skb_put(skb, sizeof(struct iphdr));
+	skb_reset_network_header(skb);
+	iph = ip_hdr(skb);
 	iph->daddr = daddr;
 
 	serr = SKB_EXT_ERR(skb);
@@ -308,11 +313,11 @@
 	serr->ee.ee_pad = 0;
 	serr->ee.ee_info = info;
 	serr->ee.ee_data = 0;
-	serr->addr_offset = (u8*)&iph->daddr - skb->nh.raw;
+	serr->addr_offset = (u8 *)&iph->daddr - skb_network_header(skb);
 	serr->port = port;
 
-	skb->h.raw = skb->tail;
-	__skb_pull(skb, skb->tail - skb->data);
+	__skb_pull(skb, skb_tail_pointer(skb) - skb->data);
+	skb_reset_transport_header(skb);
 
 	if (sock_queue_err_skb(sk, skb))
 		kfree_skb(skb);
@@ -354,7 +359,8 @@
 	sin = (struct sockaddr_in *)msg->msg_name;
 	if (sin) {
 		sin->sin_family = AF_INET;
-		sin->sin_addr.s_addr = *(__be32*)(skb->nh.raw + serr->addr_offset);
+		sin->sin_addr.s_addr = *(__be32 *)(skb_network_header(skb) +
+						   serr->addr_offset);
 		sin->sin_port = serr->port;
 		memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
 	}
@@ -366,7 +372,7 @@
 		struct inet_sock *inet = inet_sk(sk);
 
 		sin->sin_family = AF_INET;
-		sin->sin_addr.s_addr = skb->nh.iph->saddr;
+		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
 		sin->sin_port = 0;
 		memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
 		if (inet->cmsg_flags)
@@ -403,20 +409,20 @@
  */
 
 static int do_ip_setsockopt(struct sock *sk, int level,
-		int optname, char __user *optval, int optlen)
+			    int optname, char __user *optval, int optlen)
 {
 	struct inet_sock *inet = inet_sk(sk);
 	int val=0,err;
 
 	if (((1<<optname) & ((1<<IP_PKTINFO) | (1<<IP_RECVTTL) |
-			    (1<<IP_RECVOPTS) | (1<<IP_RECVTOS) |
-			    (1<<IP_RETOPTS) | (1<<IP_TOS) |
-			    (1<<IP_TTL) | (1<<IP_HDRINCL) |
-			    (1<<IP_MTU_DISCOVER) | (1<<IP_RECVERR) |
-			    (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND) |
-			    (1<<IP_PASSSEC))) ||
-				optname == IP_MULTICAST_TTL ||
-				optname == IP_MULTICAST_LOOP) {
+			     (1<<IP_RECVOPTS) | (1<<IP_RECVTOS) |
+			     (1<<IP_RETOPTS) | (1<<IP_TOS) |
+			     (1<<IP_TTL) | (1<<IP_HDRINCL) |
+			     (1<<IP_MTU_DISCOVER) | (1<<IP_RECVERR) |
+			     (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND) |
+			     (1<<IP_PASSSEC))) ||
+	    optname == IP_MULTICAST_TTL ||
+	    optname == IP_MULTICAST_LOOP) {
 		if (optlen >= sizeof(int)) {
 			if (get_user(val, (int __user *) optval))
 				return -EFAULT;
@@ -440,444 +446,444 @@
 	lock_sock(sk);
 
 	switch (optname) {
-		case IP_OPTIONS:
-		{
-			struct ip_options * opt = NULL;
-			if (optlen > 40 || optlen < 0)
-				goto e_inval;
-			err = ip_options_get_from_user(&opt, optval, optlen);
-			if (err)
-				break;
-			if (inet->is_icsk) {
-				struct inet_connection_sock *icsk = inet_csk(sk);
+	case IP_OPTIONS:
+	{
+		struct ip_options * opt = NULL;
+		if (optlen > 40 || optlen < 0)
+			goto e_inval;
+		err = ip_options_get_from_user(&opt, optval, optlen);
+		if (err)
+			break;
+		if (inet->is_icsk) {
+			struct inet_connection_sock *icsk = inet_csk(sk);
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-				if (sk->sk_family == PF_INET ||
-				    (!((1 << sk->sk_state) &
-				       (TCPF_LISTEN | TCPF_CLOSE)) &&
-				     inet->daddr != LOOPBACK4_IPV6)) {
+			if (sk->sk_family == PF_INET ||
+			    (!((1 << sk->sk_state) &
+			       (TCPF_LISTEN | TCPF_CLOSE)) &&
+			     inet->daddr != LOOPBACK4_IPV6)) {
 #endif
-					if (inet->opt)
-						icsk->icsk_ext_hdr_len -= inet->opt->optlen;
-					if (opt)
-						icsk->icsk_ext_hdr_len += opt->optlen;
-					icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
+				if (inet->opt)
+					icsk->icsk_ext_hdr_len -= inet->opt->optlen;
+				if (opt)
+					icsk->icsk_ext_hdr_len += opt->optlen;
+				icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-				}
+			}
 #endif
-			}
-			opt = xchg(&inet->opt, opt);
-			kfree(opt);
-			break;
 		}
-		case IP_PKTINFO:
-			if (val)
-				inet->cmsg_flags |= IP_CMSG_PKTINFO;
-			else
-				inet->cmsg_flags &= ~IP_CMSG_PKTINFO;
-			break;
-		case IP_RECVTTL:
-			if (val)
-				inet->cmsg_flags |=  IP_CMSG_TTL;
-			else
-				inet->cmsg_flags &= ~IP_CMSG_TTL;
-			break;
-		case IP_RECVTOS:
-			if (val)
-				inet->cmsg_flags |=  IP_CMSG_TOS;
-			else
-				inet->cmsg_flags &= ~IP_CMSG_TOS;
-			break;
-		case IP_RECVOPTS:
-			if (val)
-				inet->cmsg_flags |=  IP_CMSG_RECVOPTS;
-			else
-				inet->cmsg_flags &= ~IP_CMSG_RECVOPTS;
-			break;
-		case IP_RETOPTS:
-			if (val)
-				inet->cmsg_flags |= IP_CMSG_RETOPTS;
-			else
-				inet->cmsg_flags &= ~IP_CMSG_RETOPTS;
-			break;
-		case IP_PASSSEC:
-			if (val)
-				inet->cmsg_flags |= IP_CMSG_PASSSEC;
-			else
-				inet->cmsg_flags &= ~IP_CMSG_PASSSEC;
-			break;
-		case IP_TOS:	/* This sets both TOS and Precedence */
-			if (sk->sk_type == SOCK_STREAM) {
-				val &= ~3;
-				val |= inet->tos & 3;
-			}
-			if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP &&
-			    !capable(CAP_NET_ADMIN)) {
-				err = -EPERM;
-				break;
-			}
-			if (inet->tos != val) {
-				inet->tos = val;
-				sk->sk_priority = rt_tos2priority(val);
-				sk_dst_reset(sk);
-			}
-			break;
-		case IP_TTL:
-			if (optlen<1)
-				goto e_inval;
-			if (val != -1 && (val < 1 || val>255))
-				goto e_inval;
-			inet->uc_ttl = val;
-			break;
-		case IP_HDRINCL:
-			if (sk->sk_type != SOCK_RAW) {
-				err = -ENOPROTOOPT;
-				break;
-			}
-			inet->hdrincl = val ? 1 : 0;
-			break;
-		case IP_MTU_DISCOVER:
-			if (val<0 || val>2)
-				goto e_inval;
-			inet->pmtudisc = val;
-			break;
-		case IP_RECVERR:
-			inet->recverr = !!val;
-			if (!val)
-				skb_queue_purge(&sk->sk_error_queue);
-			break;
-		case IP_MULTICAST_TTL:
-			if (sk->sk_type == SOCK_STREAM)
-				goto e_inval;
-			if (optlen<1)
-				goto e_inval;
-			if (val==-1)
-				val = 1;
-			if (val < 0 || val > 255)
-				goto e_inval;
-			inet->mc_ttl = val;
-			break;
-		case IP_MULTICAST_LOOP:
-			if (optlen<1)
-				goto e_inval;
-			inet->mc_loop = !!val;
-			break;
-		case IP_MULTICAST_IF:
-		{
-			struct ip_mreqn mreq;
-			struct net_device *dev = NULL;
-
-			if (sk->sk_type == SOCK_STREAM)
-				goto e_inval;
-			/*
-			 *	Check the arguments are allowable
-			 */
-
-			err = -EFAULT;
-			if (optlen >= sizeof(struct ip_mreqn)) {
-				if (copy_from_user(&mreq,optval,sizeof(mreq)))
-					break;
-			} else {
-				memset(&mreq, 0, sizeof(mreq));
-				if (optlen >= sizeof(struct in_addr) &&
-				    copy_from_user(&mreq.imr_address,optval,sizeof(struct in_addr)))
-					break;
-			}
-
-			if (!mreq.imr_ifindex) {
-				if (mreq.imr_address.s_addr == INADDR_ANY) {
-					inet->mc_index = 0;
-					inet->mc_addr  = 0;
-					err = 0;
-					break;
-				}
-				dev = ip_dev_find(mreq.imr_address.s_addr);
-				if (dev) {
-					mreq.imr_ifindex = dev->ifindex;
-					dev_put(dev);
-				}
-			} else
-				dev = __dev_get_by_index(mreq.imr_ifindex);
-
-
-			err = -EADDRNOTAVAIL;
-			if (!dev)
-				break;
-
-			err = -EINVAL;
-			if (sk->sk_bound_dev_if &&
-			    mreq.imr_ifindex != sk->sk_bound_dev_if)
-				break;
-
-			inet->mc_index = mreq.imr_ifindex;
-			inet->mc_addr  = mreq.imr_address.s_addr;
-			err = 0;
-			break;
+		opt = xchg(&inet->opt, opt);
+		kfree(opt);
+		break;
+	}
+	case IP_PKTINFO:
+		if (val)
+			inet->cmsg_flags |= IP_CMSG_PKTINFO;
+		else
+			inet->cmsg_flags &= ~IP_CMSG_PKTINFO;
+		break;
+	case IP_RECVTTL:
+		if (val)
+			inet->cmsg_flags |=  IP_CMSG_TTL;
+		else
+			inet->cmsg_flags &= ~IP_CMSG_TTL;
+		break;
+	case IP_RECVTOS:
+		if (val)
+			inet->cmsg_flags |=  IP_CMSG_TOS;
+		else
+			inet->cmsg_flags &= ~IP_CMSG_TOS;
+		break;
+	case IP_RECVOPTS:
+		if (val)
+			inet->cmsg_flags |=  IP_CMSG_RECVOPTS;
+		else
+			inet->cmsg_flags &= ~IP_CMSG_RECVOPTS;
+		break;
+	case IP_RETOPTS:
+		if (val)
+			inet->cmsg_flags |= IP_CMSG_RETOPTS;
+		else
+			inet->cmsg_flags &= ~IP_CMSG_RETOPTS;
+		break;
+	case IP_PASSSEC:
+		if (val)
+			inet->cmsg_flags |= IP_CMSG_PASSSEC;
+		else
+			inet->cmsg_flags &= ~IP_CMSG_PASSSEC;
+		break;
+	case IP_TOS:	/* This sets both TOS and Precedence */
+		if (sk->sk_type == SOCK_STREAM) {
+			val &= ~3;
+			val |= inet->tos & 3;
 		}
-
-		case IP_ADD_MEMBERSHIP:
-		case IP_DROP_MEMBERSHIP:
-		{
-			struct ip_mreqn mreq;
-
-			if (optlen < sizeof(struct ip_mreq))
-				goto e_inval;
-			err = -EFAULT;
-			if (optlen >= sizeof(struct ip_mreqn)) {
-				if(copy_from_user(&mreq,optval,sizeof(mreq)))
-					break;
-			} else {
-				memset(&mreq, 0, sizeof(mreq));
-				if (copy_from_user(&mreq,optval,sizeof(struct ip_mreq)))
-					break;
-			}
-
-			if (optname == IP_ADD_MEMBERSHIP)
-				err = ip_mc_join_group(sk, &mreq);
-			else
-				err = ip_mc_leave_group(sk, &mreq);
-			break;
-		}
-		case IP_MSFILTER:
-		{
-			extern int sysctl_igmp_max_msf;
-			struct ip_msfilter *msf;
-
-			if (optlen < IP_MSFILTER_SIZE(0))
-				goto e_inval;
-			if (optlen > sysctl_optmem_max) {
-				err = -ENOBUFS;
-				break;
-			}
-			msf = kmalloc(optlen, GFP_KERNEL);
-			if (msf == 0) {
-				err = -ENOBUFS;
-				break;
-			}
-			err = -EFAULT;
-			if (copy_from_user(msf, optval, optlen)) {
-				kfree(msf);
-				break;
-			}
-			/* numsrc >= (1G-4) overflow in 32 bits */
-			if (msf->imsf_numsrc >= 0x3ffffffcU ||
-			    msf->imsf_numsrc > sysctl_igmp_max_msf) {
-				kfree(msf);
-				err = -ENOBUFS;
-				break;
-			}
-			if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) {
-				kfree(msf);
-				err = -EINVAL;
-				break;
-			}
-			err = ip_mc_msfilter(sk, msf, 0);
-			kfree(msf);
-			break;
-		}
-		case IP_BLOCK_SOURCE:
-		case IP_UNBLOCK_SOURCE:
-		case IP_ADD_SOURCE_MEMBERSHIP:
-		case IP_DROP_SOURCE_MEMBERSHIP:
-		{
-			struct ip_mreq_source mreqs;
-			int omode, add;
-
-			if (optlen != sizeof(struct ip_mreq_source))
-				goto e_inval;
-			if (copy_from_user(&mreqs, optval, sizeof(mreqs))) {
-				err = -EFAULT;
-				break;
-			}
-			if (optname == IP_BLOCK_SOURCE) {
-				omode = MCAST_EXCLUDE;
-				add = 1;
-			} else if (optname == IP_UNBLOCK_SOURCE) {
-				omode = MCAST_EXCLUDE;
-				add = 0;
-			} else if (optname == IP_ADD_SOURCE_MEMBERSHIP) {
-				struct ip_mreqn mreq;
-
-				mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr;
-				mreq.imr_address.s_addr = mreqs.imr_interface;
-				mreq.imr_ifindex = 0;
-				err = ip_mc_join_group(sk, &mreq);
-				if (err && err != -EADDRINUSE)
-					break;
-				omode = MCAST_INCLUDE;
-				add = 1;
-			} else /* IP_DROP_SOURCE_MEMBERSHIP */ {
-				omode = MCAST_INCLUDE;
-				add = 0;
-			}
-			err = ip_mc_source(add, omode, sk, &mreqs, 0);
-			break;
-		}
-		case MCAST_JOIN_GROUP:
-		case MCAST_LEAVE_GROUP:
-		{
-			struct group_req greq;
-			struct sockaddr_in *psin;
-			struct ip_mreqn mreq;
-
-			if (optlen < sizeof(struct group_req))
-				goto e_inval;
-			err = -EFAULT;
-			if(copy_from_user(&greq, optval, sizeof(greq)))
-				break;
-			psin = (struct sockaddr_in *)&greq.gr_group;
-			if (psin->sin_family != AF_INET)
-				goto e_inval;
-			memset(&mreq, 0, sizeof(mreq));
-			mreq.imr_multiaddr = psin->sin_addr;
-			mreq.imr_ifindex = greq.gr_interface;
-
-			if (optname == MCAST_JOIN_GROUP)
-				err = ip_mc_join_group(sk, &mreq);
-			else
-				err = ip_mc_leave_group(sk, &mreq);
-			break;
-		}
-		case MCAST_JOIN_SOURCE_GROUP:
-		case MCAST_LEAVE_SOURCE_GROUP:
-		case MCAST_BLOCK_SOURCE:
-		case MCAST_UNBLOCK_SOURCE:
-		{
-			struct group_source_req greqs;
-			struct ip_mreq_source mreqs;
-			struct sockaddr_in *psin;
-			int omode, add;
-
-			if (optlen != sizeof(struct group_source_req))
-				goto e_inval;
-			if (copy_from_user(&greqs, optval, sizeof(greqs))) {
-				err = -EFAULT;
-				break;
-			}
-			if (greqs.gsr_group.ss_family != AF_INET ||
-			    greqs.gsr_source.ss_family != AF_INET) {
-				err = -EADDRNOTAVAIL;
-				break;
-			}
-			psin = (struct sockaddr_in *)&greqs.gsr_group;
-			mreqs.imr_multiaddr = psin->sin_addr.s_addr;
-			psin = (struct sockaddr_in *)&greqs.gsr_source;
-			mreqs.imr_sourceaddr = psin->sin_addr.s_addr;
-			mreqs.imr_interface = 0; /* use index for mc_source */
-
-			if (optname == MCAST_BLOCK_SOURCE) {
-				omode = MCAST_EXCLUDE;
-				add = 1;
-			} else if (optname == MCAST_UNBLOCK_SOURCE) {
-				omode = MCAST_EXCLUDE;
-				add = 0;
-			} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
-				struct ip_mreqn mreq;
-
-				psin = (struct sockaddr_in *)&greqs.gsr_group;
-				mreq.imr_multiaddr = psin->sin_addr;
-				mreq.imr_address.s_addr = 0;
-				mreq.imr_ifindex = greqs.gsr_interface;
-				err = ip_mc_join_group(sk, &mreq);
-				if (err && err != -EADDRINUSE)
-					break;
-				greqs.gsr_interface = mreq.imr_ifindex;
-				omode = MCAST_INCLUDE;
-				add = 1;
-			} else /* MCAST_LEAVE_SOURCE_GROUP */ {
-				omode = MCAST_INCLUDE;
-				add = 0;
-			}
-			err = ip_mc_source(add, omode, sk, &mreqs,
-				greqs.gsr_interface);
-			break;
-		}
-		case MCAST_MSFILTER:
-		{
-			extern int sysctl_igmp_max_msf;
-			struct sockaddr_in *psin;
-			struct ip_msfilter *msf = NULL;
-			struct group_filter *gsf = NULL;
-			int msize, i, ifindex;
-
-			if (optlen < GROUP_FILTER_SIZE(0))
-				goto e_inval;
-			if (optlen > sysctl_optmem_max) {
-				err = -ENOBUFS;
-				break;
-			}
-			gsf = kmalloc(optlen,GFP_KERNEL);
-			if (gsf == 0) {
-				err = -ENOBUFS;
-				break;
-			}
-			err = -EFAULT;
-			if (copy_from_user(gsf, optval, optlen)) {
-				goto mc_msf_out;
-			}
-			/* numsrc >= (4G-140)/128 overflow in 32 bits */
-			if (gsf->gf_numsrc >= 0x1ffffff ||
-			    gsf->gf_numsrc > sysctl_igmp_max_msf) {
-				err = -ENOBUFS;
-				goto mc_msf_out;
-			}
-			if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) {
-				err = -EINVAL;
-				goto mc_msf_out;
-			}
-			msize = IP_MSFILTER_SIZE(gsf->gf_numsrc);
-			msf = kmalloc(msize,GFP_KERNEL);
-			if (msf == 0) {
-				err = -ENOBUFS;
-				goto mc_msf_out;
-			}
-			ifindex = gsf->gf_interface;
-			psin = (struct sockaddr_in *)&gsf->gf_group;
-			if (psin->sin_family != AF_INET) {
-				err = -EADDRNOTAVAIL;
-				goto mc_msf_out;
-			}
-			msf->imsf_multiaddr = psin->sin_addr.s_addr;
-			msf->imsf_interface = 0;
-			msf->imsf_fmode = gsf->gf_fmode;
-			msf->imsf_numsrc = gsf->gf_numsrc;
-			err = -EADDRNOTAVAIL;
-			for (i=0; i<gsf->gf_numsrc; ++i) {
-				psin = (struct sockaddr_in *)&gsf->gf_slist[i];
-
-				if (psin->sin_family != AF_INET)
-					goto mc_msf_out;
-				msf->imsf_slist[i] = psin->sin_addr.s_addr;
-			}
-			kfree(gsf);
-			gsf = NULL;
-
-			err = ip_mc_msfilter(sk, msf, ifindex);
-mc_msf_out:
-			kfree(msf);
-			kfree(gsf);
-			break;
-		}
-		case IP_ROUTER_ALERT:
-			err = ip_ra_control(sk, val ? 1 : 0, NULL);
-			break;
-
-		case IP_FREEBIND:
-			if (optlen<1)
-				goto e_inval;
-			inet->freebind = !!val;
-			break;
-
-		case IP_IPSEC_POLICY:
-		case IP_XFRM_POLICY:
+		if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP &&
+		    !capable(CAP_NET_ADMIN)) {
 			err = -EPERM;
-			if (!capable(CAP_NET_ADMIN))
-				break;
-			err = xfrm_user_policy(sk, optname, optval, optlen);
 			break;
-
-		default:
+		}
+		if (inet->tos != val) {
+			inet->tos = val;
+			sk->sk_priority = rt_tos2priority(val);
+			sk_dst_reset(sk);
+		}
+		break;
+	case IP_TTL:
+		if (optlen<1)
+			goto e_inval;
+		if (val != -1 && (val < 1 || val>255))
+			goto e_inval;
+		inet->uc_ttl = val;
+		break;
+	case IP_HDRINCL:
+		if (sk->sk_type != SOCK_RAW) {
 			err = -ENOPROTOOPT;
 			break;
+		}
+		inet->hdrincl = val ? 1 : 0;
+		break;
+	case IP_MTU_DISCOVER:
+		if (val<0 || val>3)
+			goto e_inval;
+		inet->pmtudisc = val;
+		break;
+	case IP_RECVERR:
+		inet->recverr = !!val;
+		if (!val)
+			skb_queue_purge(&sk->sk_error_queue);
+		break;
+	case IP_MULTICAST_TTL:
+		if (sk->sk_type == SOCK_STREAM)
+			goto e_inval;
+		if (optlen<1)
+			goto e_inval;
+		if (val==-1)
+			val = 1;
+		if (val < 0 || val > 255)
+			goto e_inval;
+		inet->mc_ttl = val;
+		break;
+	case IP_MULTICAST_LOOP:
+		if (optlen<1)
+			goto e_inval;
+		inet->mc_loop = !!val;
+		break;
+	case IP_MULTICAST_IF:
+	{
+		struct ip_mreqn mreq;
+		struct net_device *dev = NULL;
+
+		if (sk->sk_type == SOCK_STREAM)
+			goto e_inval;
+		/*
+		 *	Check the arguments are allowable
+		 */
+
+		err = -EFAULT;
+		if (optlen >= sizeof(struct ip_mreqn)) {
+			if (copy_from_user(&mreq,optval,sizeof(mreq)))
+				break;
+		} else {
+			memset(&mreq, 0, sizeof(mreq));
+			if (optlen >= sizeof(struct in_addr) &&
+			    copy_from_user(&mreq.imr_address,optval,sizeof(struct in_addr)))
+				break;
+		}
+
+		if (!mreq.imr_ifindex) {
+			if (mreq.imr_address.s_addr == INADDR_ANY) {
+				inet->mc_index = 0;
+				inet->mc_addr  = 0;
+				err = 0;
+				break;
+			}
+			dev = ip_dev_find(mreq.imr_address.s_addr);
+			if (dev) {
+				mreq.imr_ifindex = dev->ifindex;
+				dev_put(dev);
+			}
+		} else
+			dev = __dev_get_by_index(mreq.imr_ifindex);
+
+
+		err = -EADDRNOTAVAIL;
+		if (!dev)
+			break;
+
+		err = -EINVAL;
+		if (sk->sk_bound_dev_if &&
+		    mreq.imr_ifindex != sk->sk_bound_dev_if)
+			break;
+
+		inet->mc_index = mreq.imr_ifindex;
+		inet->mc_addr  = mreq.imr_address.s_addr;
+		err = 0;
+		break;
+	}
+
+	case IP_ADD_MEMBERSHIP:
+	case IP_DROP_MEMBERSHIP:
+	{
+		struct ip_mreqn mreq;
+
+		if (optlen < sizeof(struct ip_mreq))
+			goto e_inval;
+		err = -EFAULT;
+		if (optlen >= sizeof(struct ip_mreqn)) {
+			if (copy_from_user(&mreq,optval,sizeof(mreq)))
+				break;
+		} else {
+			memset(&mreq, 0, sizeof(mreq));
+			if (copy_from_user(&mreq,optval,sizeof(struct ip_mreq)))
+				break;
+		}
+
+		if (optname == IP_ADD_MEMBERSHIP)
+			err = ip_mc_join_group(sk, &mreq);
+		else
+			err = ip_mc_leave_group(sk, &mreq);
+		break;
+	}
+	case IP_MSFILTER:
+	{
+		extern int sysctl_igmp_max_msf;
+		struct ip_msfilter *msf;
+
+		if (optlen < IP_MSFILTER_SIZE(0))
+			goto e_inval;
+		if (optlen > sysctl_optmem_max) {
+			err = -ENOBUFS;
+			break;
+		}
+		msf = kmalloc(optlen, GFP_KERNEL);
+		if (msf == 0) {
+			err = -ENOBUFS;
+			break;
+		}
+		err = -EFAULT;
+		if (copy_from_user(msf, optval, optlen)) {
+			kfree(msf);
+			break;
+		}
+		/* numsrc >= (1G-4) overflow in 32 bits */
+		if (msf->imsf_numsrc >= 0x3ffffffcU ||
+		    msf->imsf_numsrc > sysctl_igmp_max_msf) {
+			kfree(msf);
+			err = -ENOBUFS;
+			break;
+		}
+		if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) {
+			kfree(msf);
+			err = -EINVAL;
+			break;
+		}
+		err = ip_mc_msfilter(sk, msf, 0);
+		kfree(msf);
+		break;
+	}
+	case IP_BLOCK_SOURCE:
+	case IP_UNBLOCK_SOURCE:
+	case IP_ADD_SOURCE_MEMBERSHIP:
+	case IP_DROP_SOURCE_MEMBERSHIP:
+	{
+		struct ip_mreq_source mreqs;
+		int omode, add;
+
+		if (optlen != sizeof(struct ip_mreq_source))
+			goto e_inval;
+		if (copy_from_user(&mreqs, optval, sizeof(mreqs))) {
+			err = -EFAULT;
+			break;
+		}
+		if (optname == IP_BLOCK_SOURCE) {
+			omode = MCAST_EXCLUDE;
+			add = 1;
+		} else if (optname == IP_UNBLOCK_SOURCE) {
+			omode = MCAST_EXCLUDE;
+			add = 0;
+		} else if (optname == IP_ADD_SOURCE_MEMBERSHIP) {
+			struct ip_mreqn mreq;
+
+			mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr;
+			mreq.imr_address.s_addr = mreqs.imr_interface;
+			mreq.imr_ifindex = 0;
+			err = ip_mc_join_group(sk, &mreq);
+			if (err && err != -EADDRINUSE)
+				break;
+			omode = MCAST_INCLUDE;
+			add = 1;
+		} else /* IP_DROP_SOURCE_MEMBERSHIP */ {
+			omode = MCAST_INCLUDE;
+			add = 0;
+		}
+		err = ip_mc_source(add, omode, sk, &mreqs, 0);
+		break;
+	}
+	case MCAST_JOIN_GROUP:
+	case MCAST_LEAVE_GROUP:
+	{
+		struct group_req greq;
+		struct sockaddr_in *psin;
+		struct ip_mreqn mreq;
+
+		if (optlen < sizeof(struct group_req))
+			goto e_inval;
+		err = -EFAULT;
+		if (copy_from_user(&greq, optval, sizeof(greq)))
+			break;
+		psin = (struct sockaddr_in *)&greq.gr_group;
+		if (psin->sin_family != AF_INET)
+			goto e_inval;
+		memset(&mreq, 0, sizeof(mreq));
+		mreq.imr_multiaddr = psin->sin_addr;
+		mreq.imr_ifindex = greq.gr_interface;
+
+		if (optname == MCAST_JOIN_GROUP)
+			err = ip_mc_join_group(sk, &mreq);
+		else
+			err = ip_mc_leave_group(sk, &mreq);
+		break;
+	}
+	case MCAST_JOIN_SOURCE_GROUP:
+	case MCAST_LEAVE_SOURCE_GROUP:
+	case MCAST_BLOCK_SOURCE:
+	case MCAST_UNBLOCK_SOURCE:
+	{
+		struct group_source_req greqs;
+		struct ip_mreq_source mreqs;
+		struct sockaddr_in *psin;
+		int omode, add;
+
+		if (optlen != sizeof(struct group_source_req))
+			goto e_inval;
+		if (copy_from_user(&greqs, optval, sizeof(greqs))) {
+			err = -EFAULT;
+			break;
+		}
+		if (greqs.gsr_group.ss_family != AF_INET ||
+		    greqs.gsr_source.ss_family != AF_INET) {
+			err = -EADDRNOTAVAIL;
+			break;
+		}
+		psin = (struct sockaddr_in *)&greqs.gsr_group;
+		mreqs.imr_multiaddr = psin->sin_addr.s_addr;
+		psin = (struct sockaddr_in *)&greqs.gsr_source;
+		mreqs.imr_sourceaddr = psin->sin_addr.s_addr;
+		mreqs.imr_interface = 0; /* use index for mc_source */
+
+		if (optname == MCAST_BLOCK_SOURCE) {
+			omode = MCAST_EXCLUDE;
+			add = 1;
+		} else if (optname == MCAST_UNBLOCK_SOURCE) {
+			omode = MCAST_EXCLUDE;
+			add = 0;
+		} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
+			struct ip_mreqn mreq;
+
+			psin = (struct sockaddr_in *)&greqs.gsr_group;
+			mreq.imr_multiaddr = psin->sin_addr;
+			mreq.imr_address.s_addr = 0;
+			mreq.imr_ifindex = greqs.gsr_interface;
+			err = ip_mc_join_group(sk, &mreq);
+			if (err && err != -EADDRINUSE)
+				break;
+			greqs.gsr_interface = mreq.imr_ifindex;
+			omode = MCAST_INCLUDE;
+			add = 1;
+		} else /* MCAST_LEAVE_SOURCE_GROUP */ {
+			omode = MCAST_INCLUDE;
+			add = 0;
+		}
+		err = ip_mc_source(add, omode, sk, &mreqs,
+				   greqs.gsr_interface);
+		break;
+	}
+	case MCAST_MSFILTER:
+	{
+		extern int sysctl_igmp_max_msf;
+		struct sockaddr_in *psin;
+		struct ip_msfilter *msf = NULL;
+		struct group_filter *gsf = NULL;
+		int msize, i, ifindex;
+
+		if (optlen < GROUP_FILTER_SIZE(0))
+			goto e_inval;
+		if (optlen > sysctl_optmem_max) {
+			err = -ENOBUFS;
+			break;
+		}
+		gsf = kmalloc(optlen,GFP_KERNEL);
+		if (gsf == 0) {
+			err = -ENOBUFS;
+			break;
+		}
+		err = -EFAULT;
+		if (copy_from_user(gsf, optval, optlen)) {
+			goto mc_msf_out;
+		}
+		/* numsrc >= (4G-140)/128 overflow in 32 bits */
+		if (gsf->gf_numsrc >= 0x1ffffff ||
+		    gsf->gf_numsrc > sysctl_igmp_max_msf) {
+			err = -ENOBUFS;
+			goto mc_msf_out;
+		}
+		if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) {
+			err = -EINVAL;
+			goto mc_msf_out;
+		}
+		msize = IP_MSFILTER_SIZE(gsf->gf_numsrc);
+		msf = kmalloc(msize,GFP_KERNEL);
+		if (msf == 0) {
+			err = -ENOBUFS;
+			goto mc_msf_out;
+		}
+		ifindex = gsf->gf_interface;
+		psin = (struct sockaddr_in *)&gsf->gf_group;
+		if (psin->sin_family != AF_INET) {
+			err = -EADDRNOTAVAIL;
+			goto mc_msf_out;
+		}
+		msf->imsf_multiaddr = psin->sin_addr.s_addr;
+		msf->imsf_interface = 0;
+		msf->imsf_fmode = gsf->gf_fmode;
+		msf->imsf_numsrc = gsf->gf_numsrc;
+		err = -EADDRNOTAVAIL;
+		for (i=0; i<gsf->gf_numsrc; ++i) {
+			psin = (struct sockaddr_in *)&gsf->gf_slist[i];
+
+			if (psin->sin_family != AF_INET)
+				goto mc_msf_out;
+			msf->imsf_slist[i] = psin->sin_addr.s_addr;
+		}
+		kfree(gsf);
+		gsf = NULL;
+
+		err = ip_mc_msfilter(sk, msf, ifindex);
+	mc_msf_out:
+		kfree(msf);
+		kfree(gsf);
+		break;
+	}
+	case IP_ROUTER_ALERT:
+		err = ip_ra_control(sk, val ? 1 : 0, NULL);
+		break;
+
+	case IP_FREEBIND:
+		if (optlen<1)
+			goto e_inval;
+		inet->freebind = !!val;
+		break;
+
+	case IP_IPSEC_POLICY:
+	case IP_XFRM_POLICY:
+		err = -EPERM;
+		if (!capable(CAP_NET_ADMIN))
+			break;
+		err = xfrm_user_policy(sk, optname, optval, optlen);
+		break;
+
+	default:
+		err = -ENOPROTOOPT;
+		break;
 	}
 	release_sock(sk);
 	return err;
@@ -948,214 +954,213 @@
  */
 
 static int do_ip_getsockopt(struct sock *sk, int level, int optname,
-		char __user *optval, int __user *optlen)
+			    char __user *optval, int __user *optlen)
 {
 	struct inet_sock *inet = inet_sk(sk);
 	int val;
 	int len;
 
-	if(level!=SOL_IP)
+	if (level != SOL_IP)
 		return -EOPNOTSUPP;
 
 #ifdef CONFIG_IP_MROUTE
-	if(optname>=MRT_BASE && optname <=MRT_BASE+10)
-	{
+	if (optname >= MRT_BASE && optname <= MRT_BASE+10) {
 		return ip_mroute_getsockopt(sk,optname,optval,optlen);
 	}
 #endif
 
-	if(get_user(len,optlen))
+	if (get_user(len,optlen))
 		return -EFAULT;
-	if(len < 0)
+	if (len < 0)
 		return -EINVAL;
 
 	lock_sock(sk);
 
-	switch(optname)	{
-		case IP_OPTIONS:
-			{
-				unsigned char optbuf[sizeof(struct ip_options)+40];
-				struct ip_options * opt = (struct ip_options*)optbuf;
-				opt->optlen = 0;
-				if (inet->opt)
-					memcpy(optbuf, inet->opt,
-					       sizeof(struct ip_options)+
-					       inet->opt->optlen);
-				release_sock(sk);
+	switch (optname) {
+	case IP_OPTIONS:
+	{
+		unsigned char optbuf[sizeof(struct ip_options)+40];
+		struct ip_options * opt = (struct ip_options*)optbuf;
+		opt->optlen = 0;
+		if (inet->opt)
+			memcpy(optbuf, inet->opt,
+			       sizeof(struct ip_options)+
+			       inet->opt->optlen);
+		release_sock(sk);
 
-				if (opt->optlen == 0)
-					return put_user(0, optlen);
+		if (opt->optlen == 0)
+			return put_user(0, optlen);
 
-				ip_options_undo(opt);
+		ip_options_undo(opt);
 
-				len = min_t(unsigned int, len, opt->optlen);
-				if(put_user(len, optlen))
-					return -EFAULT;
-				if(copy_to_user(optval, opt->__data, len))
-					return -EFAULT;
-				return 0;
-			}
-		case IP_PKTINFO:
-			val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0;
-			break;
-		case IP_RECVTTL:
-			val = (inet->cmsg_flags & IP_CMSG_TTL) != 0;
-			break;
-		case IP_RECVTOS:
-			val = (inet->cmsg_flags & IP_CMSG_TOS) != 0;
-			break;
-		case IP_RECVOPTS:
-			val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0;
-			break;
-		case IP_RETOPTS:
-			val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0;
-			break;
-		case IP_PASSSEC:
-			val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0;
-			break;
-		case IP_TOS:
-			val = inet->tos;
-			break;
-		case IP_TTL:
-			val = (inet->uc_ttl == -1 ?
-			       sysctl_ip_default_ttl :
-			       inet->uc_ttl);
-			break;
-		case IP_HDRINCL:
-			val = inet->hdrincl;
-			break;
-		case IP_MTU_DISCOVER:
-			val = inet->pmtudisc;
-			break;
-		case IP_MTU:
-		{
-			struct dst_entry *dst;
-			val = 0;
-			dst = sk_dst_get(sk);
-			if (dst) {
-				val = dst_mtu(dst);
-				dst_release(dst);
-			}
-			if (!val) {
-				release_sock(sk);
-				return -ENOTCONN;
-			}
-			break;
+		len = min_t(unsigned int, len, opt->optlen);
+		if (put_user(len, optlen))
+			return -EFAULT;
+		if (copy_to_user(optval, opt->__data, len))
+			return -EFAULT;
+		return 0;
+	}
+	case IP_PKTINFO:
+		val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0;
+		break;
+	case IP_RECVTTL:
+		val = (inet->cmsg_flags & IP_CMSG_TTL) != 0;
+		break;
+	case IP_RECVTOS:
+		val = (inet->cmsg_flags & IP_CMSG_TOS) != 0;
+		break;
+	case IP_RECVOPTS:
+		val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0;
+		break;
+	case IP_RETOPTS:
+		val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0;
+		break;
+	case IP_PASSSEC:
+		val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0;
+		break;
+	case IP_TOS:
+		val = inet->tos;
+		break;
+	case IP_TTL:
+		val = (inet->uc_ttl == -1 ?
+		       sysctl_ip_default_ttl :
+		       inet->uc_ttl);
+		break;
+	case IP_HDRINCL:
+		val = inet->hdrincl;
+		break;
+	case IP_MTU_DISCOVER:
+		val = inet->pmtudisc;
+		break;
+	case IP_MTU:
+	{
+		struct dst_entry *dst;
+		val = 0;
+		dst = sk_dst_get(sk);
+		if (dst) {
+			val = dst_mtu(dst);
+			dst_release(dst);
 		}
-		case IP_RECVERR:
-			val = inet->recverr;
-			break;
-		case IP_MULTICAST_TTL:
-			val = inet->mc_ttl;
-			break;
-		case IP_MULTICAST_LOOP:
-			val = inet->mc_loop;
-			break;
-		case IP_MULTICAST_IF:
-		{
-			struct in_addr addr;
-			len = min_t(unsigned int, len, sizeof(struct in_addr));
-			addr.s_addr = inet->mc_addr;
+		if (!val) {
 			release_sock(sk);
-
-			if(put_user(len, optlen))
-				return -EFAULT;
-			if(copy_to_user(optval, &addr, len))
-				return -EFAULT;
-			return 0;
+			return -ENOTCONN;
 		}
-		case IP_MSFILTER:
-		{
-			struct ip_msfilter msf;
-			int err;
+		break;
+	}
+	case IP_RECVERR:
+		val = inet->recverr;
+		break;
+	case IP_MULTICAST_TTL:
+		val = inet->mc_ttl;
+		break;
+	case IP_MULTICAST_LOOP:
+		val = inet->mc_loop;
+		break;
+	case IP_MULTICAST_IF:
+	{
+		struct in_addr addr;
+		len = min_t(unsigned int, len, sizeof(struct in_addr));
+		addr.s_addr = inet->mc_addr;
+		release_sock(sk);
 
-			if (len < IP_MSFILTER_SIZE(0)) {
-				release_sock(sk);
-				return -EINVAL;
-			}
-			if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) {
-				release_sock(sk);
-				return -EFAULT;
-			}
-			err = ip_mc_msfget(sk, &msf,
-				(struct ip_msfilter __user *)optval, optlen);
+		if (put_user(len, optlen))
+			return -EFAULT;
+		if (copy_to_user(optval, &addr, len))
+			return -EFAULT;
+		return 0;
+	}
+	case IP_MSFILTER:
+	{
+		struct ip_msfilter msf;
+		int err;
+
+		if (len < IP_MSFILTER_SIZE(0)) {
 			release_sock(sk);
-			return err;
+			return -EINVAL;
 		}
-		case MCAST_MSFILTER:
-		{
-			struct group_filter gsf;
-			int err;
-
-			if (len < GROUP_FILTER_SIZE(0)) {
-				release_sock(sk);
-				return -EINVAL;
-			}
-			if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) {
-				release_sock(sk);
-				return -EFAULT;
-			}
-			err = ip_mc_gsfget(sk, &gsf,
-				(struct group_filter __user *)optval, optlen);
+		if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) {
 			release_sock(sk);
-			return err;
+			return -EFAULT;
 		}
-		case IP_PKTOPTIONS:
-		{
-			struct msghdr msg;
+		err = ip_mc_msfget(sk, &msf,
+				   (struct ip_msfilter __user *)optval, optlen);
+		release_sock(sk);
+		return err;
+	}
+	case MCAST_MSFILTER:
+	{
+		struct group_filter gsf;
+		int err;
 
+		if (len < GROUP_FILTER_SIZE(0)) {
 			release_sock(sk);
-
-			if (sk->sk_type != SOCK_STREAM)
-				return -ENOPROTOOPT;
-
-			msg.msg_control = optval;
-			msg.msg_controllen = len;
-			msg.msg_flags = 0;
-
-			if (inet->cmsg_flags & IP_CMSG_PKTINFO) {
-				struct in_pktinfo info;
-
-				info.ipi_addr.s_addr = inet->rcv_saddr;
-				info.ipi_spec_dst.s_addr = inet->rcv_saddr;
-				info.ipi_ifindex = inet->mc_index;
-				put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
-			}
-			if (inet->cmsg_flags & IP_CMSG_TTL) {
-				int hlim = inet->mc_ttl;
-				put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim);
-			}
-			len -= msg.msg_controllen;
-			return put_user(len, optlen);
+			return -EINVAL;
 		}
-		case IP_FREEBIND:
-			val = inet->freebind;
-			break;
-		default:
+		if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) {
 			release_sock(sk);
+			return -EFAULT;
+		}
+		err = ip_mc_gsfget(sk, &gsf,
+				   (struct group_filter __user *)optval, optlen);
+		release_sock(sk);
+		return err;
+	}
+	case IP_PKTOPTIONS:
+	{
+		struct msghdr msg;
+
+		release_sock(sk);
+
+		if (sk->sk_type != SOCK_STREAM)
 			return -ENOPROTOOPT;
+
+		msg.msg_control = optval;
+		msg.msg_controllen = len;
+		msg.msg_flags = 0;
+
+		if (inet->cmsg_flags & IP_CMSG_PKTINFO) {
+			struct in_pktinfo info;
+
+			info.ipi_addr.s_addr = inet->rcv_saddr;
+			info.ipi_spec_dst.s_addr = inet->rcv_saddr;
+			info.ipi_ifindex = inet->mc_index;
+			put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
+		}
+		if (inet->cmsg_flags & IP_CMSG_TTL) {
+			int hlim = inet->mc_ttl;
+			put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim);
+		}
+		len -= msg.msg_controllen;
+		return put_user(len, optlen);
+	}
+	case IP_FREEBIND:
+		val = inet->freebind;
+		break;
+	default:
+		release_sock(sk);
+		return -ENOPROTOOPT;
 	}
 	release_sock(sk);
 
 	if (len < sizeof(int) && len > 0 && val>=0 && val<255) {
 		unsigned char ucval = (unsigned char)val;
 		len = 1;
-		if(put_user(len, optlen))
+		if (put_user(len, optlen))
 			return -EFAULT;
-		if(copy_to_user(optval,&ucval,1))
+		if (copy_to_user(optval,&ucval,1))
 			return -EFAULT;
 	} else {
 		len = min_t(unsigned int, sizeof(int), len);
-		if(put_user(len, optlen))
+		if (put_user(len, optlen))
 			return -EFAULT;
-		if(copy_to_user(optval,&val,len))
+		if (copy_to_user(optval,&val,len))
 			return -EFAULT;
 	}
 	return 0;
 }
 
 int ip_getsockopt(struct sock *sk, int level,
-		int optname, char __user *optval, int __user *optlen)
+		  int optname, char __user *optval, int __user *optlen)
 {
 	int err;
 
@@ -1169,7 +1174,7 @@
 	   ) {
 		int len;
 
-		if(get_user(len,optlen))
+		if (get_user(len,optlen))
 			return -EFAULT;
 
 		lock_sock(sk);
diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c
index aa704b8..ab86137 100644
--- a/net/ipv4/ipcomp.c
+++ b/net/ipv4/ipcomp.c
@@ -43,21 +43,15 @@
 
 static int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb)
 {
-	int err, plen, dlen;
 	struct ipcomp_data *ipcd = x->data;
-	u8 *start, *scratch;
-	struct crypto_comp *tfm;
-	int cpu;
+	const int plen = skb->len;
+	int dlen = IPCOMP_SCRATCH_SIZE;
+	const u8 *start = skb->data;
+	const int cpu = get_cpu();
+	u8 *scratch = *per_cpu_ptr(ipcomp_scratches, cpu);
+	struct crypto_comp *tfm = *per_cpu_ptr(ipcd->tfms, cpu);
+	int err = crypto_comp_decompress(tfm, start, plen, scratch, &dlen);
 
-	plen = skb->len;
-	dlen = IPCOMP_SCRATCH_SIZE;
-	start = skb->data;
-
-	cpu = get_cpu();
-	scratch = *per_cpu_ptr(ipcomp_scratches, cpu);
-	tfm = *per_cpu_ptr(ipcd->tfms, cpu);
-
-	err = crypto_comp_decompress(tfm, start, plen, scratch, &dlen);
 	if (err)
 		goto out;
 
@@ -72,7 +66,7 @@
 
 	skb->truesize += dlen - plen;
 	__skb_put(skb, dlen - plen);
-	memcpy(skb->data, scratch, dlen);
+	skb_copy_to_linear_data(skb, scratch, dlen);
 out:
 	put_cpu();
 	return err;
@@ -90,10 +84,10 @@
 	skb->ip_summed = CHECKSUM_NONE;
 
 	/* Remove ipcomp header and decompress original payload */
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 	ipch = (void *)skb->data;
 	iph->protocol = ipch->nexthdr;
-	skb->h.raw = skb->nh.raw + sizeof(*ipch);
+	skb->transport_header = skb->network_header + sizeof(*ipch);
 	__skb_pull(skb, sizeof(*ipch));
 	err = ipcomp_decompress(x, skb);
 
@@ -103,23 +97,16 @@
 
 static int ipcomp_compress(struct xfrm_state *x, struct sk_buff *skb)
 {
-	int err, plen, dlen, ihlen;
-	struct iphdr *iph = skb->nh.iph;
 	struct ipcomp_data *ipcd = x->data;
-	u8 *start, *scratch;
-	struct crypto_comp *tfm;
-	int cpu;
+	const int ihlen = ip_hdrlen(skb);
+	const int plen = skb->len - ihlen;
+	int dlen = IPCOMP_SCRATCH_SIZE;
+	u8 *start = skb->data + ihlen;
+	const int cpu = get_cpu();
+	u8 *scratch = *per_cpu_ptr(ipcomp_scratches, cpu);
+	struct crypto_comp *tfm = *per_cpu_ptr(ipcd->tfms, cpu);
+	int err = crypto_comp_compress(tfm, start, plen, scratch, &dlen);
 
-	ihlen = iph->ihl * 4;
-	plen = skb->len - ihlen;
-	dlen = IPCOMP_SCRATCH_SIZE;
-	start = skb->data + ihlen;
-
-	cpu = get_cpu();
-	scratch = *per_cpu_ptr(ipcomp_scratches, cpu);
-	tfm = *per_cpu_ptr(ipcd->tfms, cpu);
-
-	err = crypto_comp_compress(tfm, start, plen, scratch, &dlen);
 	if (err)
 		goto out;
 
@@ -142,12 +129,11 @@
 static int ipcomp_output(struct xfrm_state *x, struct sk_buff *skb)
 {
 	int err;
-	struct iphdr *iph;
 	struct ip_comp_hdr *ipch;
 	struct ipcomp_data *ipcd = x->data;
 	int hdr_len = 0;
+	struct iphdr *iph = ip_hdr(skb);
 
-	iph = skb->nh.iph;
 	iph->tot_len = htons(skb->len);
 	hdr_len = iph->ihl * 4;
 	if ((skb->len - hdr_len) < ipcd->threshold) {
@@ -159,7 +145,7 @@
 		goto out_ok;
 
 	err = ipcomp_compress(x, skb);
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 
 	if (err) {
 		goto out_ok;
@@ -188,8 +174,8 @@
 	struct ip_comp_hdr *ipch = (struct ip_comp_hdr *)(skb->data+(iph->ihl<<2));
 	struct xfrm_state *x;
 
-	if (skb->h.icmph->type != ICMP_DEST_UNREACH ||
-	    skb->h.icmph->code != ICMP_FRAG_NEEDED)
+	if (icmp_hdr(skb)->type != ICMP_DEST_UNREACH ||
+	    icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
 		return;
 
 	spi = htonl(ntohs(ipch->cpi));
diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c
index cf49de1..597c800 100644
--- a/net/ipv4/ipconfig.c
+++ b/net/ipv4/ipconfig.c
@@ -432,7 +432,7 @@
 		goto drop;
 
 	/* Basic sanity checks can be done without the lock.  */
-	rarp = (struct arphdr *)skb->h.raw;
+	rarp = (struct arphdr *)skb_transport_header(skb);
 
 	/* If this test doesn't pass, it's not IP, or we should
 	 * ignore it anyway.
@@ -455,7 +455,7 @@
 		goto drop;
 
 	/* OK, it is all there and looks valid, process... */
-	rarp = (struct arphdr *)skb->h.raw;
+	rarp = (struct arphdr *)skb_transport_header(skb);
 	rarp_ptr = (unsigned char *) (rarp + 1);
 
 	/* One reply at a time, please. */
@@ -702,7 +702,8 @@
 	memset(b, 0, sizeof(struct bootp_pkt));
 
 	/* Construct IP header */
-	skb->nh.iph = h = &b->iph;
+	skb_reset_network_header(skb);
+	h = ip_hdr(skb);
 	h->version = 4;
 	h->ihl = 5;
 	h->tot_len = htons(sizeof(struct bootp_pkt));
@@ -782,7 +783,7 @@
 	u8 *c;
 
 	printk("DHCP/BOOTP: Got extension %d:",*ext);
-	for(c=ext+2; c<ext+2+ext[1]; c++)
+	for (c=ext+2; c<ext+2+ext[1]; c++)
 		printk(" %02x", *c);
 	printk("\n");
 #endif
@@ -845,7 +846,7 @@
 			   sizeof(struct udphdr)))
 		goto drop;
 
-	b = (struct bootp_pkt *) skb->nh.iph;
+	b = (struct bootp_pkt *)skb_network_header(skb);
 	h = &b->iph;
 
 	if (h->ihl != 5 || h->version != 4 || h->protocol != IPPROTO_UDP)
@@ -883,7 +884,7 @@
 	if (!pskb_may_pull(skb, skb->len))
 		goto drop;
 
-	b = (struct bootp_pkt *) skb->nh.iph;
+	b = (struct bootp_pkt *)skb_network_header(skb);
 	h = &b->iph;
 
 	/* One reply at a time, please. */
@@ -938,7 +939,7 @@
 					if (opt[1] >= 4)
 						memcpy(&server_id, opt + 2, 4);
 					break;
-				};
+				}
 			}
 
 #ifdef IPCONFIG_DEBUG
@@ -983,7 +984,7 @@
 				ic_myaddr = NONE;
 				ic_servaddr = NONE;
 				goto drop_unlock;
-			};
+			}
 
 			ic_dhcp_msgtype = mt;
 
@@ -1094,7 +1095,7 @@
 	retries = CONF_SEND_RETRIES;
 	get_random_bytes(&timeout, sizeof(timeout));
 	timeout = CONF_BASE_TIMEOUT + (timeout % (unsigned) CONF_TIMEOUT_RANDOM);
-	for(;;) {
+	for (;;) {
 #ifdef IPCONFIG_BOOTP
 		if (do_bootp && (d->able & IC_BOOTP))
 			ic_bootp_send_if(d, jiffies - start_jiffies);
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index 3ec5ce0..ebd2f2d 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -157,10 +157,10 @@
 	return NULL;
 }
 
-static struct ip_tunnel **ipip_bucket(struct ip_tunnel *t)
+static struct ip_tunnel **__ipip_bucket(struct ip_tunnel_parm *parms)
 {
-	__be32 remote = t->parms.iph.daddr;
-	__be32 local = t->parms.iph.saddr;
+	__be32 remote = parms->iph.daddr;
+	__be32 local = parms->iph.saddr;
 	unsigned h = 0;
 	int prio = 0;
 
@@ -175,6 +175,10 @@
 	return &tunnels[prio][h];
 }
 
+static inline struct ip_tunnel **ipip_bucket(struct ip_tunnel *t)
+{
+	return __ipip_bucket(&t->parms);
+}
 
 static void ipip_tunnel_unlink(struct ip_tunnel *t)
 {
@@ -206,19 +210,9 @@
 	__be32 local = parms->iph.saddr;
 	struct ip_tunnel *t, **tp, *nt;
 	struct net_device *dev;
-	unsigned h = 0;
-	int prio = 0;
 	char name[IFNAMSIZ];
 
-	if (remote) {
-		prio |= 2;
-		h ^= HASH(remote);
-	}
-	if (local) {
-		prio |= 1;
-		h ^= HASH(local);
-	}
-	for (tp = &tunnels[prio][h]; (t = *tp) != NULL; tp = &t->next) {
+	for (tp = __ipip_bucket(parms); (t = *tp) != NULL; tp = &t->next) {
 		if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr)
 			return t;
 	}
@@ -280,8 +274,8 @@
    ICMP in the real Internet is absolutely infeasible.
  */
 	struct iphdr *iph = (struct iphdr*)skb->data;
-	int type = skb->h.icmph->type;
-	int code = skb->h.icmph->code;
+	const int type = icmp_hdr(skb)->type;
+	const int code = icmp_hdr(skb)->code;
 	struct ip_tunnel *t;
 	int err;
 
@@ -336,8 +330,8 @@
 	struct iphdr *iph = (struct iphdr*)dp;
 	int hlen = iph->ihl<<2;
 	struct iphdr *eiph;
-	int type = skb->h.icmph->type;
-	int code = skb->h.icmph->code;
+	const int type = icmp_hdr(skb)->type;
+	const int code = icmp_hdr(skb)->code;
 	int rel_type = 0;
 	int rel_code = 0;
 	__be32 rel_info = 0;
@@ -354,7 +348,7 @@
 	default:
 		return 0;
 	case ICMP_PARAMETERPROB:
-		n = ntohl(skb->h.icmph->un.gateway) >> 24;
+		n = ntohl(icmp_hdr(skb)->un.gateway) >> 24;
 		if (n < hlen)
 			return 0;
 
@@ -373,7 +367,7 @@
 			return 0;
 		case ICMP_FRAG_NEEDED:
 			/* And it is the only really necessary thing :-) */
-			n = ntohs(skb->h.icmph->un.frag.mtu);
+			n = ntohs(icmp_hdr(skb)->un.frag.mtu);
 			if (n < hlen+68)
 				return 0;
 			n -= hlen;
@@ -405,7 +399,7 @@
 	dst_release(skb2->dst);
 	skb2->dst = NULL;
 	skb_pull(skb2, skb->data - (u8*)eiph);
-	skb2->nh.raw = skb2->data;
+	skb_reset_network_header(skb2);
 
 	/* Try to guess incoming interface */
 	memset(&fl, 0, sizeof(fl));
@@ -461,9 +455,10 @@
 #endif
 }
 
-static inline void ipip_ecn_decapsulate(struct iphdr *outer_iph, struct sk_buff *skb)
+static inline void ipip_ecn_decapsulate(const struct iphdr *outer_iph,
+					struct sk_buff *skb)
 {
-	struct iphdr *inner_iph = skb->nh.iph;
+	struct iphdr *inner_iph = ip_hdr(skb);
 
 	if (INET_ECN_is_ce(outer_iph->tos))
 		IP_ECN_set_ce(inner_iph);
@@ -471,10 +466,8 @@
 
 static int ipip_rcv(struct sk_buff *skb)
 {
-	struct iphdr *iph;
 	struct ip_tunnel *tunnel;
-
-	iph = skb->nh.iph;
+	const struct iphdr *iph = ip_hdr(skb);
 
 	read_lock(&ipip_lock);
 	if ((tunnel = ipip_tunnel_lookup(iph->saddr, iph->daddr)) != NULL) {
@@ -486,8 +479,8 @@
 
 		secpath_reset(skb);
 
-		skb->mac.raw = skb->nh.raw;
-		skb->nh.raw = skb->data;
+		skb->mac_header = skb->network_header;
+		skb_reset_network_header(skb);
 		skb->protocol = htons(ETH_P_IP);
 		skb->pkt_type = PACKET_HOST;
 
@@ -521,7 +514,7 @@
 	__be16 df = tiph->frag_off;
 	struct rtable *rt;     			/* Route to the other host */
 	struct net_device *tdev;			/* Device to other host */
-	struct iphdr  *old_iph = skb->nh.iph;
+	struct iphdr  *old_iph = ip_hdr(skb);
 	struct iphdr  *iph;			/* Our new IP header */
 	int    max_headroom;			/* The extra header space needed */
 	__be32 dst = tiph->daddr;
@@ -615,11 +608,12 @@
 			skb_set_owner_w(new_skb, skb->sk);
 		dev_kfree_skb(skb);
 		skb = new_skb;
-		old_iph = skb->nh.iph;
+		old_iph = ip_hdr(skb);
 	}
 
-	skb->h.raw = skb->nh.raw;
-	skb->nh.raw = skb_push(skb, sizeof(struct iphdr));
+	skb->transport_header = skb->network_header;
+	skb_push(skb, sizeof(struct iphdr));
+	skb_reset_network_header(skb);
 	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
 	IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
 			      IPSKB_REROUTED);
@@ -630,7 +624,7 @@
 	 *	Push down and install the IPIP header.
 	 */
 
-	iph 			=	skb->nh.iph;
+	iph 			=	ip_hdr(skb);
 	iph->version		=	4;
 	iph->ihl		=	sizeof(struct iphdr)>>2;
 	iph->frag_off		=	df;
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 601e3df..0ebae41 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -62,6 +62,7 @@
 #include <linux/netfilter_ipv4.h>
 #include <net/ipip.h>
 #include <net/checksum.h>
+#include <net/netlink.h>
 
 #if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
 #define CONFIG_IP_PIMSM	1
@@ -302,8 +303,8 @@
 
 	atomic_dec(&cache_resolve_queue_len);
 
-	while((skb=skb_dequeue(&c->mfc_un.unres.unresolved))) {
-		if (skb->nh.iph->version == 0) {
+	while ((skb=skb_dequeue(&c->mfc_un.unres.unresolved))) {
+		if (ip_hdr(skb)->version == 0) {
 			struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
 			nlh->nlmsg_type = NLMSG_ERROR;
 			nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
@@ -479,7 +480,7 @@
 static struct mfc_cache *ipmr_cache_alloc(void)
 {
 	struct mfc_cache *c=kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
-	if(c==NULL)
+	if (c==NULL)
 		return NULL;
 	c->mfc_un.res.minvif = MAXVIFS;
 	return c;
@@ -488,7 +489,7 @@
 static struct mfc_cache *ipmr_cache_alloc_unres(void)
 {
 	struct mfc_cache *c=kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC);
-	if(c==NULL)
+	if (c==NULL)
 		return NULL;
 	skb_queue_head_init(&c->mfc_un.unres.unresolved);
 	c->mfc_un.unres.expires = jiffies + 10*HZ;
@@ -508,12 +509,13 @@
 	 *	Play the pending entries through our router
 	 */
 
-	while((skb=__skb_dequeue(&uc->mfc_un.unres.unresolved))) {
-		if (skb->nh.iph->version == 0) {
+	while ((skb=__skb_dequeue(&uc->mfc_un.unres.unresolved))) {
+		if (ip_hdr(skb)->version == 0) {
 			struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
 
 			if (ipmr_fill_mroute(skb, c, NLMSG_DATA(nlh)) > 0) {
-				nlh->nlmsg_len = skb->tail - (u8*)nlh;
+				nlh->nlmsg_len = (skb_tail_pointer(skb) -
+						  (u8 *)nlh);
 			} else {
 				nlh->nlmsg_type = NLMSG_ERROR;
 				nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
@@ -539,7 +541,7 @@
 static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert)
 {
 	struct sk_buff *skb;
-	int ihl = pkt->nh.iph->ihl<<2;
+	const int ihl = ip_hdrlen(pkt);
 	struct igmphdr *igmp;
 	struct igmpmsg *msg;
 	int ret;
@@ -551,7 +553,7 @@
 #endif
 		skb = alloc_skb(128, GFP_ATOMIC);
 
-	if(!skb)
+	if (!skb)
 		return -ENOBUFS;
 
 #ifdef CONFIG_IP_PIMSM
@@ -561,14 +563,17 @@
 		   And all this only to mangle msg->im_msgtype and
 		   to set msg->im_mbz to "mbz" :-)
 		 */
-		msg = (struct igmpmsg*)skb_push(skb, sizeof(struct iphdr));
-		skb->nh.raw = skb->h.raw = (u8*)msg;
-		memcpy(msg, pkt->nh.raw, sizeof(struct iphdr));
+		skb_push(skb, sizeof(struct iphdr));
+		skb_reset_network_header(skb);
+		skb_reset_transport_header(skb);
+		msg = (struct igmpmsg *)skb_network_header(skb);
+		memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr));
 		msg->im_msgtype = IGMPMSG_WHOLEPKT;
 		msg->im_mbz = 0;
 		msg->im_vif = reg_vif_num;
-		skb->nh.iph->ihl = sizeof(struct iphdr) >> 2;
-		skb->nh.iph->tot_len = htons(ntohs(pkt->nh.iph->tot_len) + sizeof(struct iphdr));
+		ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2;
+		ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) +
+					     sizeof(struct iphdr));
 	} else
 #endif
 	{
@@ -577,10 +582,11 @@
 	 *	Copy the IP header
 	 */
 
-	skb->nh.iph = (struct iphdr *)skb_put(skb, ihl);
-	memcpy(skb->data,pkt->data,ihl);
-	skb->nh.iph->protocol = 0;			/* Flag to the kernel this is a route add */
-	msg = (struct igmpmsg*)skb->nh.iph;
+	skb->network_header = skb->tail;
+	skb_put(skb, ihl);
+	skb_copy_to_linear_data(skb, pkt->data, ihl);
+	ip_hdr(skb)->protocol = 0;			/* Flag to the kernel this is a route add */
+	msg = (struct igmpmsg *)skb_network_header(skb);
 	msg->im_vif = vifi;
 	skb->dst = dst_clone(pkt->dst);
 
@@ -592,8 +598,8 @@
 	igmp->type	=
 	msg->im_msgtype = assert;
 	igmp->code 	=	0;
-	skb->nh.iph->tot_len=htons(skb->len);			/* Fix the length */
-	skb->h.raw = skb->nh.raw;
+	ip_hdr(skb)->tot_len = htons(skb->len);			/* Fix the length */
+	skb->transport_header = skb->network_header;
 	}
 
 	if (mroute_socket == NULL) {
@@ -622,11 +628,12 @@
 {
 	int err;
 	struct mfc_cache *c;
+	const struct iphdr *iph = ip_hdr(skb);
 
 	spin_lock_bh(&mfc_unres_lock);
 	for (c=mfc_unres_queue; c; c=c->next) {
-		if (c->mfc_mcastgrp == skb->nh.iph->daddr &&
-		    c->mfc_origin == skb->nh.iph->saddr)
+		if (c->mfc_mcastgrp == iph->daddr &&
+		    c->mfc_origin == iph->saddr)
 			break;
 	}
 
@@ -646,9 +653,9 @@
 		/*
 		 *	Fill in the new cache entry
 		 */
-		c->mfc_parent=-1;
-		c->mfc_origin=skb->nh.iph->saddr;
-		c->mfc_mcastgrp=skb->nh.iph->daddr;
+		c->mfc_parent	= -1;
+		c->mfc_origin	= iph->saddr;
+		c->mfc_mcastgrp	= iph->daddr;
 
 		/*
 		 *	Reflect first query at mrouted.
@@ -734,7 +741,7 @@
 		return 0;
 	}
 
-	if(!MULTICAST(mfc->mfcc_mcastgrp.s_addr))
+	if (!MULTICAST(mfc->mfcc_mcastgrp.s_addr))
 		return -EINVAL;
 
 	c=ipmr_cache_alloc();
@@ -788,7 +795,7 @@
 	/*
 	 *	Shut down all active vif entries
 	 */
-	for(i=0; i<maxvif; i++) {
+	for (i=0; i<maxvif; i++) {
 		if (!(vif_table[i].flags&VIFF_STATIC))
 			vif_delete(i);
 	}
@@ -858,119 +865,117 @@
 	struct vifctl vif;
 	struct mfcctl mfc;
 
-	if(optname!=MRT_INIT)
-	{
-		if(sk!=mroute_socket && !capable(CAP_NET_ADMIN))
+	if (optname != MRT_INIT) {
+		if (sk != mroute_socket && !capable(CAP_NET_ADMIN))
 			return -EACCES;
 	}
 
-	switch(optname)
-	{
-		case MRT_INIT:
-			if (sk->sk_type != SOCK_RAW ||
-			    inet_sk(sk)->num != IPPROTO_IGMP)
-				return -EOPNOTSUPP;
-			if(optlen!=sizeof(int))
-				return -ENOPROTOOPT;
+	switch (optname) {
+	case MRT_INIT:
+		if (sk->sk_type != SOCK_RAW ||
+		    inet_sk(sk)->num != IPPROTO_IGMP)
+			return -EOPNOTSUPP;
+		if (optlen!=sizeof(int))
+			return -ENOPROTOOPT;
 
-			rtnl_lock();
-			if (mroute_socket) {
-				rtnl_unlock();
-				return -EADDRINUSE;
-			}
-
-			ret = ip_ra_control(sk, 1, mrtsock_destruct);
-			if (ret == 0) {
-				write_lock_bh(&mrt_lock);
-				mroute_socket=sk;
-				write_unlock_bh(&mrt_lock);
-
-				ipv4_devconf.mc_forwarding++;
-			}
+		rtnl_lock();
+		if (mroute_socket) {
 			rtnl_unlock();
-			return ret;
-		case MRT_DONE:
-			if (sk!=mroute_socket)
-				return -EACCES;
-			return ip_ra_control(sk, 0, NULL);
-		case MRT_ADD_VIF:
-		case MRT_DEL_VIF:
-			if(optlen!=sizeof(vif))
-				return -EINVAL;
-			if (copy_from_user(&vif,optval,sizeof(vif)))
-				return -EFAULT;
-			if(vif.vifc_vifi >= MAXVIFS)
-				return -ENFILE;
-			rtnl_lock();
-			if (optname==MRT_ADD_VIF) {
-				ret = vif_add(&vif, sk==mroute_socket);
-			} else {
-				ret = vif_delete(vif.vifc_vifi);
-			}
-			rtnl_unlock();
-			return ret;
+			return -EADDRINUSE;
+		}
+
+		ret = ip_ra_control(sk, 1, mrtsock_destruct);
+		if (ret == 0) {
+			write_lock_bh(&mrt_lock);
+			mroute_socket=sk;
+			write_unlock_bh(&mrt_lock);
+
+			ipv4_devconf.mc_forwarding++;
+		}
+		rtnl_unlock();
+		return ret;
+	case MRT_DONE:
+		if (sk!=mroute_socket)
+			return -EACCES;
+		return ip_ra_control(sk, 0, NULL);
+	case MRT_ADD_VIF:
+	case MRT_DEL_VIF:
+		if (optlen!=sizeof(vif))
+			return -EINVAL;
+		if (copy_from_user(&vif,optval,sizeof(vif)))
+			return -EFAULT;
+		if (vif.vifc_vifi >= MAXVIFS)
+			return -ENFILE;
+		rtnl_lock();
+		if (optname==MRT_ADD_VIF) {
+			ret = vif_add(&vif, sk==mroute_socket);
+		} else {
+			ret = vif_delete(vif.vifc_vifi);
+		}
+		rtnl_unlock();
+		return ret;
 
 		/*
 		 *	Manipulate the forwarding caches. These live
 		 *	in a sort of kernel/user symbiosis.
 		 */
-		case MRT_ADD_MFC:
-		case MRT_DEL_MFC:
-			if(optlen!=sizeof(mfc))
-				return -EINVAL;
-			if (copy_from_user(&mfc,optval, sizeof(mfc)))
-				return -EFAULT;
-			rtnl_lock();
-			if (optname==MRT_DEL_MFC)
-				ret = ipmr_mfc_delete(&mfc);
-			else
-				ret = ipmr_mfc_add(&mfc, sk==mroute_socket);
-			rtnl_unlock();
-			return ret;
+	case MRT_ADD_MFC:
+	case MRT_DEL_MFC:
+		if (optlen!=sizeof(mfc))
+			return -EINVAL;
+		if (copy_from_user(&mfc,optval, sizeof(mfc)))
+			return -EFAULT;
+		rtnl_lock();
+		if (optname==MRT_DEL_MFC)
+			ret = ipmr_mfc_delete(&mfc);
+		else
+			ret = ipmr_mfc_add(&mfc, sk==mroute_socket);
+		rtnl_unlock();
+		return ret;
 		/*
 		 *	Control PIM assert.
 		 */
-		case MRT_ASSERT:
-		{
-			int v;
-			if(get_user(v,(int __user *)optval))
-				return -EFAULT;
-			mroute_do_assert=(v)?1:0;
-			return 0;
-		}
+	case MRT_ASSERT:
+	{
+		int v;
+		if (get_user(v,(int __user *)optval))
+			return -EFAULT;
+		mroute_do_assert=(v)?1:0;
+		return 0;
+	}
 #ifdef CONFIG_IP_PIMSM
-		case MRT_PIM:
-		{
-			int v, ret;
-			if(get_user(v,(int __user *)optval))
-				return -EFAULT;
-			v = (v)?1:0;
-			rtnl_lock();
-			ret = 0;
-			if (v != mroute_do_pim) {
-				mroute_do_pim = v;
-				mroute_do_assert = v;
+	case MRT_PIM:
+	{
+		int v, ret;
+		if (get_user(v,(int __user *)optval))
+			return -EFAULT;
+		v = (v)?1:0;
+		rtnl_lock();
+		ret = 0;
+		if (v != mroute_do_pim) {
+			mroute_do_pim = v;
+			mroute_do_assert = v;
 #ifdef CONFIG_IP_PIMSM_V2
-				if (mroute_do_pim)
-					ret = inet_add_protocol(&pim_protocol,
-								IPPROTO_PIM);
-				else
-					ret = inet_del_protocol(&pim_protocol,
-								IPPROTO_PIM);
-				if (ret < 0)
-					ret = -EAGAIN;
+			if (mroute_do_pim)
+				ret = inet_add_protocol(&pim_protocol,
+							IPPROTO_PIM);
+			else
+				ret = inet_del_protocol(&pim_protocol,
+							IPPROTO_PIM);
+			if (ret < 0)
+				ret = -EAGAIN;
 #endif
-			}
-			rtnl_unlock();
-			return ret;
 		}
+		rtnl_unlock();
+		return ret;
+	}
 #endif
-		/*
-		 *	Spurious command, or MRT_VERSION which you cannot
-		 *	set.
-		 */
-		default:
-			return -ENOPROTOOPT;
+	/*
+	 *	Spurious command, or MRT_VERSION which you cannot
+	 *	set.
+	 */
+	default:
+		return -ENOPROTOOPT;
 	}
 }
 
@@ -983,7 +988,7 @@
 	int olr;
 	int val;
 
-	if(optname!=MRT_VERSION &&
+	if (optname!=MRT_VERSION &&
 #ifdef CONFIG_IP_PIMSM
 	   optname!=MRT_PIM &&
 #endif
@@ -997,17 +1002,17 @@
 	if (olr < 0)
 		return -EINVAL;
 
-	if(put_user(olr,optlen))
+	if (put_user(olr,optlen))
 		return -EFAULT;
-	if(optname==MRT_VERSION)
+	if (optname==MRT_VERSION)
 		val=0x0305;
 #ifdef CONFIG_IP_PIMSM
-	else if(optname==MRT_PIM)
+	else if (optname==MRT_PIM)
 		val=mroute_do_pim;
 #endif
 	else
 		val=mroute_do_assert;
-	if(copy_to_user(optval,&val,olr))
+	if (copy_to_user(optval,&val,olr))
 		return -EFAULT;
 	return 0;
 }
@@ -1023,48 +1028,47 @@
 	struct vif_device *vif;
 	struct mfc_cache *c;
 
-	switch(cmd)
-	{
-		case SIOCGETVIFCNT:
-			if (copy_from_user(&vr,arg,sizeof(vr)))
-				return -EFAULT;
-			if(vr.vifi>=maxvif)
-				return -EINVAL;
-			read_lock(&mrt_lock);
-			vif=&vif_table[vr.vifi];
-			if(VIF_EXISTS(vr.vifi))	{
-				vr.icount=vif->pkt_in;
-				vr.ocount=vif->pkt_out;
-				vr.ibytes=vif->bytes_in;
-				vr.obytes=vif->bytes_out;
-				read_unlock(&mrt_lock);
-
-				if (copy_to_user(arg,&vr,sizeof(vr)))
-					return -EFAULT;
-				return 0;
-			}
+	switch (cmd) {
+	case SIOCGETVIFCNT:
+		if (copy_from_user(&vr,arg,sizeof(vr)))
+			return -EFAULT;
+		if (vr.vifi>=maxvif)
+			return -EINVAL;
+		read_lock(&mrt_lock);
+		vif=&vif_table[vr.vifi];
+		if (VIF_EXISTS(vr.vifi))	{
+			vr.icount=vif->pkt_in;
+			vr.ocount=vif->pkt_out;
+			vr.ibytes=vif->bytes_in;
+			vr.obytes=vif->bytes_out;
 			read_unlock(&mrt_lock);
-			return -EADDRNOTAVAIL;
-		case SIOCGETSGCNT:
-			if (copy_from_user(&sr,arg,sizeof(sr)))
+
+			if (copy_to_user(arg,&vr,sizeof(vr)))
 				return -EFAULT;
+			return 0;
+		}
+		read_unlock(&mrt_lock);
+		return -EADDRNOTAVAIL;
+	case SIOCGETSGCNT:
+		if (copy_from_user(&sr,arg,sizeof(sr)))
+			return -EFAULT;
 
-			read_lock(&mrt_lock);
-			c = ipmr_cache_find(sr.src.s_addr, sr.grp.s_addr);
-			if (c) {
-				sr.pktcnt = c->mfc_un.res.pkt;
-				sr.bytecnt = c->mfc_un.res.bytes;
-				sr.wrong_if = c->mfc_un.res.wrong_if;
-				read_unlock(&mrt_lock);
-
-				if (copy_to_user(arg,&sr,sizeof(sr)))
-					return -EFAULT;
-				return 0;
-			}
+		read_lock(&mrt_lock);
+		c = ipmr_cache_find(sr.src.s_addr, sr.grp.s_addr);
+		if (c) {
+			sr.pktcnt = c->mfc_un.res.pkt;
+			sr.bytecnt = c->mfc_un.res.bytes;
+			sr.wrong_if = c->mfc_un.res.wrong_if;
 			read_unlock(&mrt_lock);
-			return -EADDRNOTAVAIL;
-		default:
-			return -ENOIOCTLCMD;
+
+			if (copy_to_user(arg,&sr,sizeof(sr)))
+				return -EFAULT;
+			return 0;
+		}
+		read_unlock(&mrt_lock);
+		return -EADDRNOTAVAIL;
+	default:
+		return -ENOIOCTLCMD;
 	}
 }
 
@@ -1076,7 +1080,7 @@
 	if (event != NETDEV_UNREGISTER)
 		return NOTIFY_DONE;
 	v=&vif_table[0];
-	for(ct=0;ct<maxvif;ct++,v++) {
+	for (ct=0;ct<maxvif;ct++,v++) {
 		if (v->dev==ptr)
 			vif_delete(ct);
 	}
@@ -1096,11 +1100,17 @@
 
 static void ip_encap(struct sk_buff *skb, __be32 saddr, __be32 daddr)
 {
-	struct iphdr *iph = (struct iphdr *)skb_push(skb,sizeof(struct iphdr));
+	struct iphdr *iph;
+	struct iphdr *old_iph = ip_hdr(skb);
+
+	skb_push(skb, sizeof(struct iphdr));
+	skb->transport_header = skb->network_header;
+	skb_reset_network_header(skb);
+	iph = ip_hdr(skb);
 
 	iph->version	= 	4;
-	iph->tos	=	skb->nh.iph->tos;
-	iph->ttl	=	skb->nh.iph->ttl;
+	iph->tos	=	old_iph->tos;
+	iph->ttl	=	old_iph->ttl;
 	iph->frag_off	=	0;
 	iph->daddr	=	daddr;
 	iph->saddr	=	saddr;
@@ -1110,8 +1120,6 @@
 	ip_select_ident(iph, skb->dst, NULL);
 	ip_send_check(iph);
 
-	skb->h.ipiph = skb->nh.iph;
-	skb->nh.iph = iph;
 	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
 	nf_reset(skb);
 }
@@ -1134,7 +1142,7 @@
 
 static void ipmr_queue_xmit(struct sk_buff *skb, struct mfc_cache *c, int vifi)
 {
-	struct iphdr *iph = skb->nh.iph;
+	const struct iphdr *iph = ip_hdr(skb);
 	struct vif_device *vif = &vif_table[vifi];
 	struct net_device *dev;
 	struct rtable *rt;
@@ -1200,8 +1208,7 @@
 
 	dst_release(skb->dst);
 	skb->dst = &rt->u.dst;
-	iph = skb->nh.iph;
-	ip_decrease_ttl(iph);
+	ip_decrease_ttl(ip_hdr(skb));
 
 	/* FIXME: forward and output firewalls used to be called here.
 	 * What do we do with netfilter? -- RR */
@@ -1301,7 +1308,7 @@
 	 *	Forward the frame
 	 */
 	for (ct = cache->mfc_un.res.maxvif-1; ct >= cache->mfc_un.res.minvif; ct--) {
-		if (skb->nh.iph->ttl > cache->mfc_un.res.ttls[ct]) {
+		if (ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) {
 			if (psend != -1) {
 				struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
 				if (skb2)
@@ -1347,7 +1354,7 @@
 		    if (IPCB(skb)->opt.router_alert) {
 			    if (ip_call_ra_chain(skb))
 				    return 0;
-		    } else if (skb->nh.iph->protocol == IPPROTO_IGMP){
+		    } else if (ip_hdr(skb)->protocol == IPPROTO_IGMP){
 			    /* IGMPv1 (and broken IGMPv2 implementations sort of
 			       Cisco IOS <= 11.2(8)) do not put router alert
 			       option to IGMP packets destined to routable
@@ -1366,7 +1373,7 @@
 	}
 
 	read_lock(&mrt_lock);
-	cache = ipmr_cache_find(skb->nh.iph->saddr, skb->nh.iph->daddr);
+	cache = ipmr_cache_find(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr);
 
 	/*
 	 *	No usable cache entry
@@ -1426,14 +1433,15 @@
 	if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap)))
 		goto drop;
 
-	pim = (struct igmphdr*)skb->h.raw;
+	pim = igmp_hdr(skb);
 
 	if (!mroute_do_pim ||
 	    skb->len < sizeof(*pim) + sizeof(*encap) ||
 	    pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER)
 		goto drop;
 
-	encap = (struct iphdr*)(skb->h.raw + sizeof(struct igmphdr));
+	encap = (struct iphdr *)(skb_transport_header(skb) +
+				 sizeof(struct igmphdr));
 	/*
 	   Check that:
 	   a. packet is really destinted to a multicast group
@@ -1455,9 +1463,9 @@
 	if (reg_dev == NULL)
 		goto drop;
 
-	skb->mac.raw = skb->nh.raw;
+	skb->mac_header = skb->network_header;
 	skb_pull(skb, (u8*)encap - skb->data);
-	skb->nh.iph = (struct iphdr *)skb->data;
+	skb_reset_network_header(skb);
 	skb->dev = reg_dev;
 	skb->protocol = htons(ETH_P_IP);
 	skb->ip_summed = 0;
@@ -1486,7 +1494,7 @@
 	if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap)))
 		goto drop;
 
-	pim = (struct pimreghdr*)skb->h.raw;
+	pim = (struct pimreghdr *)skb_transport_header(skb);
 	if (pim->type != ((PIM_VERSION<<4)|(PIM_REGISTER)) ||
 	    (pim->flags&PIM_NULL_REGISTER) ||
 	    (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 &&
@@ -1494,7 +1502,8 @@
 		goto drop;
 
 	/* check if the inner packet is destined to mcast group */
-	encap = (struct iphdr*)(skb->h.raw + sizeof(struct pimreghdr));
+	encap = (struct iphdr *)(skb_transport_header(skb) +
+				 sizeof(struct pimreghdr));
 	if (!MULTICAST(encap->daddr) ||
 	    encap->tot_len == 0 ||
 	    ntohs(encap->tot_len) + sizeof(*pim) > skb->len)
@@ -1510,9 +1519,9 @@
 	if (reg_dev == NULL)
 		goto drop;
 
-	skb->mac.raw = skb->nh.raw;
+	skb->mac_header = skb->network_header;
 	skb_pull(skb, (u8*)encap - skb->data);
-	skb->nh.iph = (struct iphdr *)skb->data;
+	skb_reset_network_header(skb);
 	skb->dev = reg_dev;
 	skb->protocol = htons(ETH_P_IP);
 	skb->ip_summed = 0;
@@ -1537,7 +1546,7 @@
 	int ct;
 	struct rtnexthop *nhp;
 	struct net_device *dev = vif_table[c->mfc_parent].dev;
-	u8 *b = skb->tail;
+	u8 *b = skb_tail_pointer(skb);
 	struct rtattr *mp_head;
 
 	if (dev)
@@ -1557,12 +1566,12 @@
 		}
 	}
 	mp_head->rta_type = RTA_MULTIPATH;
-	mp_head->rta_len = skb->tail - (u8*)mp_head;
+	mp_head->rta_len = skb_tail_pointer(skb) - (u8 *)mp_head;
 	rtm->rtm_type = RTN_MULTICAST;
 	return 1;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -EMSGSIZE;
 }
 
@@ -1577,6 +1586,7 @@
 
 	if (cache==NULL) {
 		struct sk_buff *skb2;
+		struct iphdr *iph;
 		struct net_device *dev;
 		int vif;
 
@@ -1596,11 +1606,13 @@
 			return -ENOMEM;
 		}
 
-		skb2->nh.raw = skb_push(skb2, sizeof(struct iphdr));
-		skb2->nh.iph->ihl = sizeof(struct iphdr)>>2;
-		skb2->nh.iph->saddr = rt->rt_src;
-		skb2->nh.iph->daddr = rt->rt_dst;
-		skb2->nh.iph->version = 0;
+		skb_push(skb2, sizeof(struct iphdr));
+		skb_reset_network_header(skb2);
+		iph = ip_hdr(skb2);
+		iph->ihl = sizeof(struct iphdr) >> 2;
+		iph->saddr = rt->rt_src;
+		iph->daddr = rt->rt_dst;
+		iph->version = 0;
 		err = ipmr_cache_unresolved(vif, skb2);
 		read_unlock(&mrt_lock);
 		return err;
@@ -1625,7 +1637,7 @@
 					   loff_t pos)
 {
 	for (iter->ct = 0; iter->ct < maxvif; ++iter->ct) {
-		if(!VIF_EXISTS(iter->ct))
+		if (!VIF_EXISTS(iter->ct))
 			continue;
 		if (pos-- == 0)
 			return &vif_table[iter->ct];
@@ -1649,7 +1661,7 @@
 		return ipmr_vif_seq_idx(iter, 0);
 
 	while (++iter->ct < maxvif) {
-		if(!VIF_EXISTS(iter->ct))
+		if (!VIF_EXISTS(iter->ct))
 			continue;
 		return &vif_table[iter->ct];
 	}
@@ -1680,7 +1692,7 @@
 	return 0;
 }
 
-static struct seq_operations ipmr_vif_seq_ops = {
+static const struct seq_operations ipmr_vif_seq_ops = {
 	.start = ipmr_vif_seq_start,
 	.next  = ipmr_vif_seq_next,
 	.stop  = ipmr_vif_seq_stop,
@@ -1732,14 +1744,14 @@
 	it->cache = mfc_cache_array;
 	read_lock(&mrt_lock);
 	for (it->ct = 0; it->ct < MFC_LINES; it->ct++)
-		for(mfc = mfc_cache_array[it->ct]; mfc; mfc = mfc->next)
+		for (mfc = mfc_cache_array[it->ct]; mfc; mfc = mfc->next)
 			if (pos-- == 0)
 				return mfc;
 	read_unlock(&mrt_lock);
 
 	it->cache = &mfc_unres_queue;
 	spin_lock_bh(&mfc_unres_lock);
-	for(mfc = mfc_unres_queue; mfc; mfc = mfc->next)
+	for (mfc = mfc_unres_queue; mfc; mfc = mfc->next)
 		if (pos-- == 0)
 			return mfc;
 	spin_unlock_bh(&mfc_unres_lock);
@@ -1829,9 +1841,9 @@
 			   mfc->mfc_un.res.wrong_if);
 
 		if (it->cache != &mfc_unres_queue) {
-			for(n = mfc->mfc_un.res.minvif;
-			    n < mfc->mfc_un.res.maxvif; n++ ) {
-				if(VIF_EXISTS(n)
+			for (n = mfc->mfc_un.res.minvif;
+			     n < mfc->mfc_un.res.maxvif; n++ ) {
+				if (VIF_EXISTS(n)
 				   && mfc->mfc_un.res.ttls[n] < 255)
 				seq_printf(seq,
 					   " %2d:%-3d",
@@ -1843,7 +1855,7 @@
 	return 0;
 }
 
-static struct seq_operations ipmr_mfc_seq_ops = {
+static const struct seq_operations ipmr_mfc_seq_ops = {
 	.start = ipmr_mfc_seq_start,
 	.next  = ipmr_mfc_seq_next,
 	.stop  = ipmr_mfc_seq_stop,
diff --git a/net/ipv4/ipvs/ip_vs_app.c b/net/ipv4/ipvs/ip_vs_app.c
index 22e104c..15ad5dd 100644
--- a/net/ipv4/ipvs/ip_vs_app.c
+++ b/net/ipv4/ipvs/ip_vs_app.c
@@ -331,14 +331,14 @@
 				  struct ip_vs_app *app)
 {
 	int diff;
-	unsigned int tcp_offset = (*pskb)->nh.iph->ihl*4;
+	const unsigned int tcp_offset = ip_hdrlen(*pskb);
 	struct tcphdr *th;
 	__u32 seq;
 
 	if (!ip_vs_make_skb_writable(pskb, tcp_offset + sizeof(*th)))
 		return 0;
 
-	th = (struct tcphdr *)((*pskb)->nh.raw + tcp_offset);
+	th = (struct tcphdr *)(skb_network_header(*pskb) + tcp_offset);
 
 	/*
 	 *	Remember seq number in case this pkt gets resized
@@ -406,14 +406,14 @@
 				 struct ip_vs_app *app)
 {
 	int diff;
-	unsigned int tcp_offset = (*pskb)->nh.iph->ihl*4;
+	const unsigned int tcp_offset = ip_hdrlen(*pskb);
 	struct tcphdr *th;
 	__u32 seq;
 
 	if (!ip_vs_make_skb_writable(pskb, tcp_offset + sizeof(*th)))
 		return 0;
 
-	th = (struct tcphdr *)((*pskb)->nh.raw + tcp_offset);
+	th = (struct tcphdr *)(skb_network_header(*pskb) + tcp_offset);
 
 	/*
 	 *	Remember seq number in case this pkt gets resized
@@ -577,7 +577,6 @@
 int ip_vs_skb_replace(struct sk_buff *skb, gfp_t pri,
 		      char *o_buf, int o_len, char *n_buf, int n_len)
 {
-	struct iphdr *iph;
 	int diff;
 	int o_offset;
 	int o_left;
@@ -603,12 +602,11 @@
 		skb_put(skb, diff);
 		memmove(skb->data + o_offset + n_len,
 			skb->data + o_offset + o_len, o_left);
-		memcpy(skb->data + o_offset, n_buf, n_len);
+		skb_copy_to_linear_data_offset(skb, o_offset, n_buf, n_len);
 	}
 
 	/* must update the iph total length here */
-	iph = skb->nh.iph;
-	iph->tot_len = htons(skb->len);
+	ip_hdr(skb)->tot_len = htons(skb->len);
 
 	LeaveFunction(9);
 	return 0;
diff --git a/net/ipv4/ipvs/ip_vs_core.c b/net/ipv4/ipvs/ip_vs_core.c
index 24d7b66..f005a2f 100644
--- a/net/ipv4/ipvs/ip_vs_core.c
+++ b/net/ipv4/ipvs/ip_vs_core.c
@@ -212,7 +212,7 @@
 		    __be16 ports[2])
 {
 	struct ip_vs_conn *cp = NULL;
-	struct iphdr *iph = skb->nh.iph;
+	struct iphdr *iph = ip_hdr(skb);
 	struct ip_vs_dest *dest;
 	struct ip_vs_conn *ct;
 	__be16  dport;	 /* destination port to forward */
@@ -381,7 +381,7 @@
 ip_vs_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
 {
 	struct ip_vs_conn *cp = NULL;
-	struct iphdr *iph = skb->nh.iph;
+	struct iphdr *iph = ip_hdr(skb);
 	struct ip_vs_dest *dest;
 	__be16 _ports[2], *pptr;
 
@@ -447,7 +447,7 @@
 		struct ip_vs_protocol *pp)
 {
 	__be16 _ports[2], *pptr;
-	struct iphdr *iph = skb->nh.iph;
+	struct iphdr *iph = ip_hdr(skb);
 
 	pptr = skb_header_pointer(skb, iph->ihl*4,
 				  sizeof(_ports), _ports);
@@ -546,7 +546,7 @@
 {
 	skb = ip_defrag(skb, user);
 	if (skb)
-		ip_send_check(skb->nh.iph);
+		ip_send_check(ip_hdr(skb));
 	return skb;
 }
 
@@ -557,9 +557,10 @@
 void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
 		    struct ip_vs_conn *cp, int inout)
 {
-	struct iphdr *iph	 = skb->nh.iph;
+	struct iphdr *iph	 = ip_hdr(skb);
 	unsigned int icmp_offset = iph->ihl*4;
-	struct icmphdr *icmph	 = (struct icmphdr *)(skb->nh.raw + icmp_offset);
+	struct icmphdr *icmph	 = (struct icmphdr *)(skb_network_header(skb) +
+						      icmp_offset);
 	struct iphdr *ciph	 = (struct iphdr *)(icmph + 1);
 
 	if (inout) {
@@ -617,14 +618,14 @@
 	*related = 1;
 
 	/* reassemble IP fragments */
-	if (skb->nh.iph->frag_off & __constant_htons(IP_MF|IP_OFFSET)) {
+	if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {
 		skb = ip_vs_gather_frags(skb, IP_DEFRAG_VS_OUT);
 		if (!skb)
 			return NF_STOLEN;
 		*pskb = skb;
 	}
 
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 	offset = ihl = iph->ihl * 4;
 	ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
 	if (ic == NULL)
@@ -659,7 +660,7 @@
 		return NF_ACCEPT;
 
 	/* Is the embedded protocol header present? */
-	if (unlikely(cih->frag_off & __constant_htons(IP_OFFSET) &&
+	if (unlikely(cih->frag_off & htons(IP_OFFSET) &&
 		     pp->dont_defrag))
 		return NF_ACCEPT;
 
@@ -680,8 +681,7 @@
 	}
 
 	/* Ensure the checksum is correct */
-	if (skb->ip_summed != CHECKSUM_UNNECESSARY &&
-	    ip_vs_checksum_complete(skb, ihl)) {
+	if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) {
 		/* Failed checksum! */
 		IP_VS_DBG(1, "Forward ICMP: failed checksum from %d.%d.%d.%d!\n",
 			  NIPQUAD(iph->saddr));
@@ -712,8 +712,7 @@
 {
 	struct tcphdr _tcph, *th;
 
-	th = skb_header_pointer(skb, skb->nh.iph->ihl * 4,
-				sizeof(_tcph), &_tcph);
+	th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph);
 	if (th == NULL)
 		return 0;
 	return th->rst;
@@ -740,14 +739,14 @@
 	if (skb->ipvs_property)
 		return NF_ACCEPT;
 
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 	if (unlikely(iph->protocol == IPPROTO_ICMP)) {
 		int related, verdict = ip_vs_out_icmp(pskb, &related);
 
 		if (related)
 			return verdict;
 		skb = *pskb;
-		iph = skb->nh.iph;
+		iph = ip_hdr(skb);
 	}
 
 	pp = ip_vs_proto_get(iph->protocol);
@@ -755,12 +754,12 @@
 		return NF_ACCEPT;
 
 	/* reassemble IP fragments */
-	if (unlikely(iph->frag_off & __constant_htons(IP_MF|IP_OFFSET) &&
+	if (unlikely(iph->frag_off & htons(IP_MF|IP_OFFSET) &&
 		     !pp->dont_defrag)) {
 		skb = ip_vs_gather_frags(skb, IP_DEFRAG_VS_OUT);
 		if (!skb)
 			return NF_STOLEN;
-		iph = skb->nh.iph;
+		iph = ip_hdr(skb);
 		*pskb = skb;
 	}
 
@@ -810,8 +809,8 @@
 	if (pp->snat_handler && !pp->snat_handler(pskb, pp, cp))
 		goto drop;
 	skb = *pskb;
-	skb->nh.iph->saddr = cp->vaddr;
-	ip_send_check(skb->nh.iph);
+	ip_hdr(skb)->saddr = cp->vaddr;
+	ip_send_check(ip_hdr(skb));
 
 	/* For policy routing, packets originating from this
 	 * machine itself may be routed differently to packets
@@ -861,7 +860,7 @@
 	*related = 1;
 
 	/* reassemble IP fragments */
-	if (skb->nh.iph->frag_off & __constant_htons(IP_MF|IP_OFFSET)) {
+	if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {
 		skb = ip_vs_gather_frags(skb,
 					 hooknum == NF_IP_LOCAL_IN ?
 					 IP_DEFRAG_VS_IN : IP_DEFRAG_VS_FWD);
@@ -870,7 +869,7 @@
 		*pskb = skb;
 	}
 
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 	offset = ihl = iph->ihl * 4;
 	ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
 	if (ic == NULL)
@@ -905,7 +904,7 @@
 		return NF_ACCEPT;
 
 	/* Is the embedded protocol header present? */
-	if (unlikely(cih->frag_off & __constant_htons(IP_OFFSET) &&
+	if (unlikely(cih->frag_off & htons(IP_OFFSET) &&
 		     pp->dont_defrag))
 		return NF_ACCEPT;
 
@@ -921,8 +920,7 @@
 	verdict = NF_DROP;
 
 	/* Ensure the checksum is correct */
-	if (skb->ip_summed != CHECKSUM_UNNECESSARY &&
-	    ip_vs_checksum_complete(skb, ihl)) {
+	if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) {
 		/* Failed checksum! */
 		IP_VS_DBG(1, "Incoming ICMP: failed checksum from %d.%d.%d.%d!\n",
 			  NIPQUAD(iph->saddr));
@@ -966,19 +964,19 @@
 		     || skb->dev == &loopback_dev || skb->sk)) {
 		IP_VS_DBG(12, "packet type=%d proto=%d daddr=%d.%d.%d.%d ignored\n",
 			  skb->pkt_type,
-			  skb->nh.iph->protocol,
-			  NIPQUAD(skb->nh.iph->daddr));
+			  ip_hdr(skb)->protocol,
+			  NIPQUAD(ip_hdr(skb)->daddr));
 		return NF_ACCEPT;
 	}
 
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 	if (unlikely(iph->protocol == IPPROTO_ICMP)) {
 		int related, verdict = ip_vs_in_icmp(pskb, &related, hooknum);
 
 		if (related)
 			return verdict;
 		skb = *pskb;
-		iph = skb->nh.iph;
+		iph = ip_hdr(skb);
 	}
 
 	/* Protocol supported? */
@@ -1064,7 +1062,7 @@
 {
 	int r;
 
-	if ((*pskb)->nh.iph->protocol != IPPROTO_ICMP)
+	if (ip_hdr(*pskb)->protocol != IPPROTO_ICMP)
 		return NF_ACCEPT;
 
 	return ip_vs_in_icmp(pskb, &r, hooknum);
diff --git a/net/ipv4/ipvs/ip_vs_dh.c b/net/ipv4/ipvs/ip_vs_dh.c
index 502111f..dcf5d46 100644
--- a/net/ipv4/ipvs/ip_vs_dh.c
+++ b/net/ipv4/ipvs/ip_vs_dh.c
@@ -204,7 +204,7 @@
 {
 	struct ip_vs_dest *dest;
 	struct ip_vs_dh_bucket *tbl;
-	struct iphdr *iph = skb->nh.iph;
+	struct iphdr *iph = ip_hdr(skb);
 
 	IP_VS_DBG(6, "ip_vs_dh_schedule(): Scheduling...\n");
 
diff --git a/net/ipv4/ipvs/ip_vs_ftp.c b/net/ipv4/ipvs/ip_vs_ftp.c
index 847c47a..344ddbb 100644
--- a/net/ipv4/ipvs/ip_vs_ftp.c
+++ b/net/ipv4/ipvs/ip_vs_ftp.c
@@ -159,10 +159,10 @@
 		return 0;
 
 	if (cp->app_data == &ip_vs_ftp_pasv) {
-		iph = (*pskb)->nh.iph;
+		iph = ip_hdr(*pskb);
 		th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
 		data = (char *)th + (th->doff << 2);
-		data_limit = (*pskb)->tail;
+		data_limit = skb_tail_pointer(*pskb);
 
 		if (ip_vs_ftp_get_addrport(data, data_limit,
 					   SERVER_STRING,
@@ -262,14 +262,14 @@
 	/*
 	 * Detecting whether it is passive
 	 */
-	iph = (*pskb)->nh.iph;
+	iph = ip_hdr(*pskb);
 	th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
 
 	/* Since there may be OPTIONS in the TCP packet and the HLEN is
 	   the length of the header in 32-bit multiples, it is accurate
 	   to calculate data address by th+HLEN*4 */
 	data = data_start = (char *)th + (th->doff << 2);
-	data_limit = (*pskb)->tail;
+	data_limit = skb_tail_pointer(*pskb);
 
 	while (data <= data_limit - 6) {
 		if (strnicmp(data, "PASV\r\n", 6) == 0) {
diff --git a/net/ipv4/ipvs/ip_vs_lblc.c b/net/ipv4/ipvs/ip_vs_lblc.c
index c801273..052f4ed 100644
--- a/net/ipv4/ipvs/ip_vs_lblc.c
+++ b/net/ipv4/ipvs/ip_vs_lblc.c
@@ -521,7 +521,7 @@
 	struct ip_vs_dest *dest;
 	struct ip_vs_lblc_table *tbl;
 	struct ip_vs_lblc_entry *en;
-	struct iphdr *iph = skb->nh.iph;
+	struct iphdr *iph = ip_hdr(skb);
 
 	IP_VS_DBG(6, "ip_vs_lblc_schedule(): Scheduling...\n");
 
diff --git a/net/ipv4/ipvs/ip_vs_lblcr.c b/net/ipv4/ipvs/ip_vs_lblcr.c
index 23f9b9e..6225aca 100644
--- a/net/ipv4/ipvs/ip_vs_lblcr.c
+++ b/net/ipv4/ipvs/ip_vs_lblcr.c
@@ -775,7 +775,7 @@
 	struct ip_vs_dest *dest;
 	struct ip_vs_lblcr_table *tbl;
 	struct ip_vs_lblcr_entry *en;
-	struct iphdr *iph = skb->nh.iph;
+	struct iphdr *iph = ip_hdr(skb);
 
 	IP_VS_DBG(6, "ip_vs_lblcr_schedule(): Scheduling...\n");
 
diff --git a/net/ipv4/ipvs/ip_vs_proto_ah.c b/net/ipv4/ipvs/ip_vs_proto_ah.c
index 8b0505b..a842676 100644
--- a/net/ipv4/ipvs/ip_vs_proto_ah.c
+++ b/net/ipv4/ipvs/ip_vs_proto_ah.c
@@ -52,15 +52,15 @@
 	if (likely(!inverse)) {
 		cp = ip_vs_conn_in_get(IPPROTO_UDP,
 				       iph->saddr,
-				       __constant_htons(PORT_ISAKMP),
+				       htons(PORT_ISAKMP),
 				       iph->daddr,
-				       __constant_htons(PORT_ISAKMP));
+				       htons(PORT_ISAKMP));
 	} else {
 		cp = ip_vs_conn_in_get(IPPROTO_UDP,
 				       iph->daddr,
-				       __constant_htons(PORT_ISAKMP),
+				       htons(PORT_ISAKMP),
 				       iph->saddr,
-				       __constant_htons(PORT_ISAKMP));
+				       htons(PORT_ISAKMP));
 	}
 
 	if (!cp) {
@@ -89,15 +89,15 @@
 	if (likely(!inverse)) {
 		cp = ip_vs_conn_out_get(IPPROTO_UDP,
 					iph->saddr,
-					__constant_htons(PORT_ISAKMP),
+					htons(PORT_ISAKMP),
 					iph->daddr,
-					__constant_htons(PORT_ISAKMP));
+					htons(PORT_ISAKMP));
 	} else {
 		cp = ip_vs_conn_out_get(IPPROTO_UDP,
 					iph->daddr,
-					__constant_htons(PORT_ISAKMP),
+					htons(PORT_ISAKMP),
 					iph->saddr,
-					__constant_htons(PORT_ISAKMP));
+					htons(PORT_ISAKMP));
 	}
 
 	if (!cp) {
diff --git a/net/ipv4/ipvs/ip_vs_proto_tcp.c b/net/ipv4/ipvs/ip_vs_proto_tcp.c
index 16a9ebe..e65577a 100644
--- a/net/ipv4/ipvs/ip_vs_proto_tcp.c
+++ b/net/ipv4/ipvs/ip_vs_proto_tcp.c
@@ -76,16 +76,15 @@
 	struct ip_vs_service *svc;
 	struct tcphdr _tcph, *th;
 
-	th = skb_header_pointer(skb, skb->nh.iph->ihl*4,
-				sizeof(_tcph), &_tcph);
+	th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph);
 	if (th == NULL) {
 		*verdict = NF_DROP;
 		return 0;
 	}
 
 	if (th->syn &&
-	    (svc = ip_vs_service_get(skb->mark, skb->nh.iph->protocol,
-				     skb->nh.iph->daddr, th->dest))) {
+	    (svc = ip_vs_service_get(skb->mark, ip_hdr(skb)->protocol,
+				     ip_hdr(skb)->daddr, th->dest))) {
 		if (ip_vs_todrop()) {
 			/*
 			 * It seems that we are very loaded.
@@ -127,7 +126,7 @@
 		 struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
 {
 	struct tcphdr *tcph;
-	unsigned int tcphoff = (*pskb)->nh.iph->ihl * 4;
+	const unsigned int tcphoff = ip_hdrlen(*pskb);
 
 	/* csum_check requires unshared skb */
 	if (!ip_vs_make_skb_writable(pskb, tcphoff+sizeof(*tcph)))
@@ -143,7 +142,7 @@
 			return 0;
 	}
 
-	tcph = (void *)(*pskb)->nh.iph + tcphoff;
+	tcph = (void *)ip_hdr(*pskb) + tcphoff;
 	tcph->source = cp->vport;
 
 	/* Adjust TCP checksums */
@@ -175,7 +174,7 @@
 		 struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
 {
 	struct tcphdr *tcph;
-	unsigned int tcphoff = (*pskb)->nh.iph->ihl * 4;
+	const unsigned int tcphoff = ip_hdrlen(*pskb);
 
 	/* csum_check requires unshared skb */
 	if (!ip_vs_make_skb_writable(pskb, tcphoff+sizeof(*tcph)))
@@ -194,7 +193,7 @@
 			return 0;
 	}
 
-	tcph = (void *)(*pskb)->nh.iph + tcphoff;
+	tcph = (void *)ip_hdr(*pskb) + tcphoff;
 	tcph->dest = cp->dport;
 
 	/*
@@ -224,15 +223,15 @@
 static int
 tcp_csum_check(struct sk_buff *skb, struct ip_vs_protocol *pp)
 {
-	unsigned int tcphoff = skb->nh.iph->ihl*4;
+	const unsigned int tcphoff = ip_hdrlen(skb);
 
 	switch (skb->ip_summed) {
 	case CHECKSUM_NONE:
 		skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0);
 	case CHECKSUM_COMPLETE:
-		if (csum_tcpudp_magic(skb->nh.iph->saddr, skb->nh.iph->daddr,
+		if (csum_tcpudp_magic(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr,
 				      skb->len - tcphoff,
-				      skb->nh.iph->protocol, skb->csum)) {
+				      ip_hdr(skb)->protocol, skb->csum)) {
 			IP_VS_DBG_RL_PKT(0, pp, skb, 0,
 					 "Failed checksum for");
 			return 0;
@@ -467,8 +466,7 @@
 {
 	struct tcphdr _tcph, *th;
 
-	th = skb_header_pointer(skb, skb->nh.iph->ihl*4,
-				sizeof(_tcph), &_tcph);
+	th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph);
 	if (th == NULL)
 		return 0;
 
diff --git a/net/ipv4/ipvs/ip_vs_proto_udp.c b/net/ipv4/ipvs/ip_vs_proto_udp.c
index 03f0a41..8ee5fe6 100644
--- a/net/ipv4/ipvs/ip_vs_proto_udp.c
+++ b/net/ipv4/ipvs/ip_vs_proto_udp.c
@@ -22,7 +22,7 @@
 #include <linux/udp.h>
 
 #include <net/ip_vs.h>
-
+#include <net/ip.h>
 
 static struct ip_vs_conn *
 udp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
@@ -56,7 +56,7 @@
 	struct ip_vs_conn *cp;
 	__be16 _ports[2], *pptr;
 
-	pptr = skb_header_pointer(skb, skb->nh.iph->ihl*4,
+	pptr = skb_header_pointer(skb, ip_hdrlen(skb),
 				  sizeof(_ports), _ports);
 	if (pptr == NULL)
 		return NULL;
@@ -82,15 +82,15 @@
 	struct ip_vs_service *svc;
 	struct udphdr _udph, *uh;
 
-	uh = skb_header_pointer(skb, skb->nh.iph->ihl*4,
+	uh = skb_header_pointer(skb, ip_hdrlen(skb),
 				sizeof(_udph), &_udph);
 	if (uh == NULL) {
 		*verdict = NF_DROP;
 		return 0;
 	}
 
-	if ((svc = ip_vs_service_get(skb->mark, skb->nh.iph->protocol,
-				     skb->nh.iph->daddr, uh->dest))) {
+	if ((svc = ip_vs_service_get(skb->mark, ip_hdr(skb)->protocol,
+				     ip_hdr(skb)->daddr, uh->dest))) {
 		if (ip_vs_todrop()) {
 			/*
 			 * It seems that we are very loaded.
@@ -133,7 +133,7 @@
 		 struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
 {
 	struct udphdr *udph;
-	unsigned int udphoff = (*pskb)->nh.iph->ihl * 4;
+	const unsigned int udphoff = ip_hdrlen(*pskb);
 
 	/* csum_check requires unshared skb */
 	if (!ip_vs_make_skb_writable(pskb, udphoff+sizeof(*udph)))
@@ -151,7 +151,7 @@
 			return 0;
 	}
 
-	udph = (void *)(*pskb)->nh.iph + udphoff;
+	udph = (void *)ip_hdr(*pskb) + udphoff;
 	udph->source = cp->vport;
 
 	/*
@@ -187,7 +187,7 @@
 		 struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
 {
 	struct udphdr *udph;
-	unsigned int udphoff = (*pskb)->nh.iph->ihl * 4;
+	unsigned int udphoff = ip_hdrlen(*pskb);
 
 	/* csum_check requires unshared skb */
 	if (!ip_vs_make_skb_writable(pskb, udphoff+sizeof(*udph)))
@@ -206,7 +206,7 @@
 			return 0;
 	}
 
-	udph = (void *)(*pskb)->nh.iph + udphoff;
+	udph = (void *)ip_hdr(*pskb) + udphoff;
 	udph->dest = cp->dport;
 
 	/*
@@ -239,7 +239,7 @@
 udp_csum_check(struct sk_buff *skb, struct ip_vs_protocol *pp)
 {
 	struct udphdr _udph, *uh;
-	unsigned int udphoff = skb->nh.iph->ihl*4;
+	const unsigned int udphoff = ip_hdrlen(skb);
 
 	uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph);
 	if (uh == NULL)
@@ -251,10 +251,10 @@
 			skb->csum = skb_checksum(skb, udphoff,
 						 skb->len - udphoff, 0);
 		case CHECKSUM_COMPLETE:
-			if (csum_tcpudp_magic(skb->nh.iph->saddr,
-					      skb->nh.iph->daddr,
+			if (csum_tcpudp_magic(ip_hdr(skb)->saddr,
+					      ip_hdr(skb)->daddr,
 					      skb->len - udphoff,
-					      skb->nh.iph->protocol,
+					      ip_hdr(skb)->protocol,
 					      skb->csum)) {
 				IP_VS_DBG_RL_PKT(0, pp, skb, 0,
 						 "Failed checksum for");
diff --git a/net/ipv4/ipvs/ip_vs_sh.c b/net/ipv4/ipvs/ip_vs_sh.c
index 338668f..1b25b00 100644
--- a/net/ipv4/ipvs/ip_vs_sh.c
+++ b/net/ipv4/ipvs/ip_vs_sh.c
@@ -201,7 +201,7 @@
 {
 	struct ip_vs_dest *dest;
 	struct ip_vs_sh_bucket *tbl;
-	struct iphdr *iph = skb->nh.iph;
+	struct iphdr *iph = ip_hdr(skb);
 
 	IP_VS_DBG(6, "ip_vs_sh_schedule(): Scheduling...\n");
 
diff --git a/net/ipv4/ipvs/ip_vs_xmit.c b/net/ipv4/ipvs/ip_vs_xmit.c
index e1f77bd..900ce29 100644
--- a/net/ipv4/ipvs/ip_vs_xmit.c
+++ b/net/ipv4/ipvs/ip_vs_xmit.c
@@ -156,7 +156,7 @@
 		  struct ip_vs_protocol *pp)
 {
 	struct rtable *rt;			/* Route to the other host */
-	struct iphdr  *iph = skb->nh.iph;
+	struct iphdr  *iph = ip_hdr(skb);
 	u8     tos = iph->tos;
 	int    mtu;
 	struct flowi fl = {
@@ -178,7 +178,7 @@
 
 	/* MTU checking */
 	mtu = dst_mtu(&rt->u.dst);
-	if ((skb->len > mtu) && (iph->frag_off&__constant_htons(IP_DF))) {
+	if ((skb->len > mtu) && (iph->frag_off & htons(IP_DF))) {
 		ip_rt_put(rt);
 		icmp_send(skb, ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED, htonl(mtu));
 		IP_VS_DBG_RL("ip_vs_bypass_xmit(): frag needed\n");
@@ -193,7 +193,7 @@
 		ip_rt_put(rt);
 		return NF_STOLEN;
 	}
-	ip_send_check(skb->nh.iph);
+	ip_send_check(ip_hdr(skb));
 
 	/* drop old route */
 	dst_release(skb->dst);
@@ -226,7 +226,7 @@
 {
 	struct rtable *rt;		/* Route to the other host */
 	int mtu;
-	struct iphdr *iph = skb->nh.iph;
+	struct iphdr *iph = ip_hdr(skb);
 
 	EnterFunction(10);
 
@@ -245,7 +245,7 @@
 
 	/* MTU checking */
 	mtu = dst_mtu(&rt->u.dst);
-	if ((skb->len > mtu) && (iph->frag_off&__constant_htons(IP_DF))) {
+	if ((skb->len > mtu) && (iph->frag_off & htons(IP_DF))) {
 		ip_rt_put(rt);
 		icmp_send(skb, ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED, htonl(mtu));
 		IP_VS_DBG_RL_PKT(0, pp, skb, 0, "ip_vs_nat_xmit(): frag needed for");
@@ -266,8 +266,8 @@
 	/* mangle the packet */
 	if (pp->dnat_handler && !pp->dnat_handler(&skb, pp, cp))
 		goto tx_error;
-	skb->nh.iph->daddr = cp->daddr;
-	ip_send_check(skb->nh.iph);
+	ip_hdr(skb)->daddr = cp->daddr;
+	ip_send_check(ip_hdr(skb));
 
 	IP_VS_DBG_PKT(10, pp, skb, 0, "After DNAT");
 
@@ -320,19 +320,20 @@
 {
 	struct rtable *rt;			/* Route to the other host */
 	struct net_device *tdev;		/* Device to other host */
-	struct iphdr  *old_iph = skb->nh.iph;
+	struct iphdr  *old_iph = ip_hdr(skb);
 	u8     tos = old_iph->tos;
 	__be16 df = old_iph->frag_off;
+	sk_buff_data_t old_transport_header = skb->transport_header;
 	struct iphdr  *iph;			/* Our new IP header */
 	int    max_headroom;			/* The extra header space needed */
 	int    mtu;
 
 	EnterFunction(10);
 
-	if (skb->protocol != __constant_htons(ETH_P_IP)) {
+	if (skb->protocol != htons(ETH_P_IP)) {
 		IP_VS_DBG_RL("ip_vs_tunnel_xmit(): protocol error, "
 			     "ETH_P_IP: %d, skb protocol: %d\n",
-			     __constant_htons(ETH_P_IP), skb->protocol);
+			     htons(ETH_P_IP), skb->protocol);
 		goto tx_error;
 	}
 
@@ -350,9 +351,9 @@
 	if (skb->dst)
 		skb->dst->ops->update_pmtu(skb->dst, 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);
@@ -377,15 +378,16 @@
 		}
 		kfree_skb(skb);
 		skb = new_skb;
-		old_iph = skb->nh.iph;
+		old_iph = ip_hdr(skb);
 	}
 
-	skb->h.raw = (void *) old_iph;
+	skb->transport_header = old_transport_header;
 
 	/* fix old IP header checksum */
 	ip_send_check(old_iph);
 
-	skb->nh.raw = skb_push(skb, sizeof(struct iphdr));
+	skb_push(skb, sizeof(struct iphdr));
+	skb_reset_network_header(skb);
 	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
 
 	/* drop old route */
@@ -395,7 +397,7 @@
 	/*
 	 *	Push down and install the IPIP header.
 	 */
-	iph			=	skb->nh.iph;
+	iph			=	ip_hdr(skb);
 	iph->version		=	4;
 	iph->ihl		=	sizeof(struct iphdr)>>2;
 	iph->frag_off		=	df;
@@ -435,7 +437,7 @@
 	      struct ip_vs_protocol *pp)
 {
 	struct rtable *rt;			/* Route to the other host */
-	struct iphdr  *iph = skb->nh.iph;
+	struct iphdr  *iph = ip_hdr(skb);
 	int    mtu;
 
 	EnterFunction(10);
@@ -445,7 +447,7 @@
 
 	/* MTU checking */
 	mtu = dst_mtu(&rt->u.dst);
-	if ((iph->frag_off&__constant_htons(IP_DF)) && skb->len > mtu) {
+	if ((iph->frag_off & htons(IP_DF)) && skb->len > mtu) {
 		icmp_send(skb, ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED, htonl(mtu));
 		ip_rt_put(rt);
 		IP_VS_DBG_RL("ip_vs_dr_xmit(): frag needed\n");
@@ -460,7 +462,7 @@
 		ip_rt_put(rt);
 		return NF_STOLEN;
 	}
-	ip_send_check(skb->nh.iph);
+	ip_send_check(ip_hdr(skb));
 
 	/* drop old route */
 	dst_release(skb->dst);
@@ -514,12 +516,12 @@
 	 * mangle and send the packet here (only for VS/NAT)
 	 */
 
-	if (!(rt = __ip_vs_get_out_rt(cp, RT_TOS(skb->nh.iph->tos))))
+	if (!(rt = __ip_vs_get_out_rt(cp, RT_TOS(ip_hdr(skb)->tos))))
 		goto tx_error_icmp;
 
 	/* MTU checking */
 	mtu = dst_mtu(&rt->u.dst);
-	if ((skb->len > mtu) && (skb->nh.iph->frag_off&__constant_htons(IP_DF))) {
+	if ((skb->len > mtu) && (ip_hdr(skb)->frag_off & htons(IP_DF))) {
 		ip_rt_put(rt);
 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
 		IP_VS_DBG_RL("ip_vs_in_icmp(): frag needed\n");
diff --git a/net/ipv4/multipath_drr.c b/net/ipv4/multipath_drr.c
index 574c735..b03c5ca 100644
--- a/net/ipv4/multipath_drr.c
+++ b/net/ipv4/multipath_drr.c
@@ -100,7 +100,7 @@
 
 		spin_unlock_bh(&state_lock);
 		break;
-	};
+	}
 
 	return NOTIFY_DONE;
 }
diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c
index 6069a11..b441929 100644
--- a/net/ipv4/netfilter.c
+++ b/net/ipv4/netfilter.c
@@ -10,7 +10,7 @@
 /* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue */
 int ip_route_me_harder(struct sk_buff **pskb, unsigned addr_type)
 {
-	struct iphdr *iph = (*pskb)->nh.iph;
+	const struct iphdr *iph = ip_hdr(*pskb);
 	struct rtable *rt;
 	struct flowi fl = {};
 	struct dst_entry *odst;
@@ -142,7 +142,7 @@
 	struct ip_rt_info *rt_info = nf_info_reroute(info);
 
 	if (info->hook == NF_IP_LOCAL_OUT) {
-		const struct iphdr *iph = skb->nh.iph;
+		const struct iphdr *iph = ip_hdr(skb);
 
 		rt_info->tos = iph->tos;
 		rt_info->daddr = iph->daddr;
@@ -155,7 +155,7 @@
 	const struct ip_rt_info *rt_info = nf_info_reroute(info);
 
 	if (info->hook == NF_IP_LOCAL_OUT) {
-		struct iphdr *iph = (*pskb)->nh.iph;
+		const struct iphdr *iph = ip_hdr(*pskb);
 
 		if (!(iph->tos == rt_info->tos
 		      && iph->daddr == rt_info->daddr
@@ -168,7 +168,7 @@
 __sum16 nf_ip_checksum(struct sk_buff *skb, unsigned int hook,
 			    unsigned int dataoff, u_int8_t protocol)
 {
-	struct iphdr *iph = skb->nh.iph;
+	const struct iphdr *iph = ip_hdr(skb);
 	__sum16 csum = 0;
 
 	switch (skb->ip_summed) {
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
index 601808c..46509fa 100644
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
@@ -30,188 +30,6 @@
 
 	  If unsure, say Y.
 
-# connection tracking, helpers and protocols
-config IP_NF_CT_ACCT
-	bool "Connection tracking flow accounting"
-	depends on IP_NF_CONNTRACK
-	help
-	  If this option is enabled, the connection tracking code will
-	  keep per-flow packet and byte counters.
-
-	  Those counters can be used for flow-based accounting or the
-	  `connbytes' match.
-
-	  If unsure, say `N'.
-
-config IP_NF_CONNTRACK_MARK
-	bool  'Connection mark tracking support'
-	depends on IP_NF_CONNTRACK
-	help
-	  This option enables support for connection marks, used by the
-	  `CONNMARK' target and `connmark' match. Similar to the mark value
-	  of packets, but this mark value is kept in the conntrack session
-	  instead of the individual packets.
-	
-config IP_NF_CONNTRACK_SECMARK
-	bool  'Connection tracking security mark support'
-	depends on IP_NF_CONNTRACK && NETWORK_SECMARK
-	help
-	  This option enables security markings to be applied to
-	  connections.  Typically they are copied to connections from
-	  packets using the CONNSECMARK target and copied back from
-	  connections to packets with the same target, with the packets
-	  being originally labeled via SECMARK.
-
-	  If unsure, say 'N'.
-
-config IP_NF_CONNTRACK_EVENTS
-	bool "Connection tracking events (EXPERIMENTAL)"
-	depends on EXPERIMENTAL && IP_NF_CONNTRACK
-	help
-	  If this option is enabled, the connection tracking code will
-	  provide a notifier chain that can be used by other kernel code
-	  to get notified about changes in the connection tracking state.
-	  
-	  IF unsure, say `N'.
-
-config IP_NF_CONNTRACK_NETLINK
-	tristate 'Connection tracking netlink interface (EXPERIMENTAL)'
-	depends on EXPERIMENTAL && IP_NF_CONNTRACK && NETFILTER_NETLINK
-	depends on IP_NF_CONNTRACK!=y || NETFILTER_NETLINK!=m
-	depends on IP_NF_NAT=n || IP_NF_NAT
-	help
-	  This option enables support for a netlink-based userspace interface
-
-
-config IP_NF_CT_PROTO_SCTP
-	tristate  'SCTP protocol connection tracking support (EXPERIMENTAL)'
-	depends on IP_NF_CONNTRACK && EXPERIMENTAL
-	help
-	  With this option enabled, the connection tracking code will
-	  be able to do state tracking on SCTP connections.
-
-	  If you want to compile it as a module, say M here and read
-	  <file:Documentation/modules.txt>.  If unsure, say `N'.
-
-config IP_NF_FTP
-	tristate "FTP protocol support"
-	depends on IP_NF_CONNTRACK
-	help
-	  Tracking FTP connections is problematic: special helpers are
-	  required for tracking them, and doing masquerading and other forms
-	  of Network Address Translation on them.
-
-	  To compile it as a module, choose M here.  If unsure, say Y.
-
-config IP_NF_IRC
-	tristate "IRC protocol support"
-	depends on IP_NF_CONNTRACK
-	---help---
-	  There is a commonly-used extension to IRC called
-	  Direct Client-to-Client Protocol (DCC).  This enables users to send
-	  files to each other, and also chat to each other without the need
-	  of a server.  DCC Sending is used anywhere you send files over IRC,
-	  and DCC Chat is most commonly used by Eggdrop bots.  If you are
-	  using NAT, this extension will enable you to send files and initiate
-	  chats.  Note that you do NOT need this extension to get files or
-	  have others initiate chats, or everything else in IRC.
-
-	  To compile it as a module, choose M here.  If unsure, say Y.
-
-config IP_NF_NETBIOS_NS
-	tristate "NetBIOS name service protocol support (EXPERIMENTAL)"
-	depends on IP_NF_CONNTRACK && EXPERIMENTAL
-	help
-	  NetBIOS name service requests are sent as broadcast messages from an
-	  unprivileged port and responded to with unicast messages to the
-	  same port. This make them hard to firewall properly because connection
-	  tracking doesn't deal with broadcasts. This helper tracks locally
-	  originating NetBIOS name service requests and the corresponding
-	  responses. It relies on correct IP address configuration, specifically
-	  netmask and broadcast address. When properly configured, the output
-	  of "ip address show" should look similar to this:
-
-	  $ ip -4 address show eth0
-	  4: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast qlen 1000
-	      inet 172.16.2.252/24 brd 172.16.2.255 scope global eth0
-	  
-	  To compile it as a module, choose M here.  If unsure, say N.
-
-config IP_NF_TFTP
-	tristate "TFTP protocol support"
-	depends on IP_NF_CONNTRACK
-	help
-	  TFTP connection tracking helper, this is required depending
-	  on how restrictive your ruleset is.
-	  If you are using a tftp client behind -j SNAT or -j MASQUERADING
-	  you will need this.
-
-	  To compile it as a module, choose M here.  If unsure, say Y.
-
-config IP_NF_AMANDA
-	tristate "Amanda backup protocol support"
-	depends on IP_NF_CONNTRACK
-	select TEXTSEARCH
-	select TEXTSEARCH_KMP
-	help
-	  If you are running the Amanda backup package <http://www.amanda.org/>
-	  on this machine or machines that will be MASQUERADED through this
-	  machine, then you may want to enable this feature.  This allows the
-	  connection tracking and natting code to allow the sub-channels that
-	  Amanda requires for communication of the backup data, messages and
-	  index.
-
-	  To compile it as a module, choose M here.  If unsure, say Y.
-
-config IP_NF_PPTP
-	tristate  'PPTP protocol support'
-	depends on IP_NF_CONNTRACK
-	help
-	  This module adds support for PPTP (Point to Point Tunnelling
-	  Protocol, RFC2637) connection tracking and NAT. 
-	
-	  If you are running PPTP sessions over a stateful firewall or NAT
-	  box, you may want to enable this feature.  
-	
-	  Please note that not all PPTP modes of operation are supported yet.
-	  For more info, read top of the file
-	  net/ipv4/netfilter/ip_conntrack_pptp.c
-	
-	  If you want to compile it as a module, say M here and read
-	  Documentation/modules.txt.  If unsure, say `N'.
-
-config IP_NF_H323
-	tristate  'H.323 protocol support (EXPERIMENTAL)'
-	depends on IP_NF_CONNTRACK && EXPERIMENTAL
-	help
-	  H.323 is a VoIP signalling protocol from ITU-T. As one of the most
-	  important VoIP protocols, it is widely used by voice hardware and
-	  software including voice gateways, IP phones, Netmeeting, OpenPhone,
-	  Gnomemeeting, etc.
-
-	  With this module you can support H.323 on a connection tracking/NAT
-	  firewall.
-
-	  This module supports RAS, Fast Start, H.245 Tunnelling, Call
-	  Forwarding, RTP/RTCP and T.120 based audio, video, fax, chat,
-	  whiteboard, file transfer, etc. For more information, please
-	  visit http://nath323.sourceforge.net/.
-
-	  If you want to compile it as a module, say 'M' here and read
-	  Documentation/modules.txt.  If unsure, say 'N'.
-
-config IP_NF_SIP
-	tristate "SIP protocol support (EXPERIMENTAL)"
-	depends on IP_NF_CONNTRACK && EXPERIMENTAL
-	help
-	  SIP is an application-layer control protocol that can establish,
-	  modify, and terminate multimedia sessions (conferences) such as
-	  Internet telephony calls. With the ip_conntrack_sip and
-	  the ip_nat_sip modules you can support the protocol on a connection
-	  tracking/NATing firewall.
-
-	  To compile it as a module, choose M here.  If unsure, say Y.
-
 config IP_NF_QUEUE
 	tristate "IP Userspace queueing via NETLINK (OBSOLETE)"
 	help
@@ -361,17 +179,6 @@
 
 	  To compile it as a module, choose M here.  If unsure, say N.
 
-# NAT + specific targets: ip_conntrack
-config IP_NF_NAT
-	tristate "Full NAT"
-	depends on IP_NF_IPTABLES && IP_NF_CONNTRACK
-	help
-	  The Full NAT option allows masquerading, port forwarding and other
-	  forms of full Network Address Port Translation.  It is controlled by
-	  the `nat' table in iptables: see the man page for iptables(8).
-
-	  To compile it as a module, choose M here.  If unsure, say N.
-
 # NAT + specific targets: nf_conntrack
 config NF_NAT
 	tristate "Full NAT"
@@ -383,11 +190,6 @@
 
 	  To compile it as a module, choose M here.  If unsure, say N.
 
-config IP_NF_NAT_NEEDED
-	bool
-	depends on IP_NF_NAT
-	default y
-
 config NF_NAT_NEEDED
 	bool
 	depends on NF_NAT
@@ -395,7 +197,7 @@
 
 config IP_NF_TARGET_MASQUERADE
 	tristate "MASQUERADE target support"
-	depends on (NF_NAT || IP_NF_NAT)
+	depends on NF_NAT
 	help
 	  Masquerading is a special case of NAT: all outgoing connections are
 	  changed to seem to come from a particular interface's address, and
@@ -407,7 +209,7 @@
 
 config IP_NF_TARGET_REDIRECT
 	tristate "REDIRECT target support"
-	depends on (NF_NAT || IP_NF_NAT)
+	depends on NF_NAT
 	help
 	  REDIRECT is a special case of NAT: all incoming connections are
 	  mapped onto the incoming interface's address, causing the packets to
@@ -418,7 +220,7 @@
 
 config IP_NF_TARGET_NETMAP
 	tristate "NETMAP target support"
-	depends on (NF_NAT || IP_NF_NAT)
+	depends on NF_NAT
 	help
 	  NETMAP is an implementation of static 1:1 NAT mapping of network
 	  addresses. It maps the network address part, while keeping the host
@@ -429,28 +231,13 @@
 
 config IP_NF_TARGET_SAME
 	tristate "SAME target support"
-	depends on (NF_NAT || IP_NF_NAT)
+	depends on NF_NAT
 	help
 	  This option adds a `SAME' target, which works like the standard SNAT
 	  target, but attempts to give clients the same IP for all connections.
 
 	  To compile it as a module, choose M here.  If unsure, say N.
 
-config IP_NF_NAT_SNMP_BASIC
-	tristate "Basic SNMP-ALG support (EXPERIMENTAL)"
-	depends on EXPERIMENTAL && IP_NF_NAT
-	---help---
-
-	  This module implements an Application Layer Gateway (ALG) for
-	  SNMP payloads.  In conjunction with NAT, it allows a network
-	  management system to access multiple private networks with
-	  conflicting addresses.  It works by modifying IP addresses
-	  inside SNMP payloads to match IP-layer NAT mapping.
-
-	  This is the "basic" form of SNMP-ALG, as described in RFC 2962
-
-	  To compile it as a module, choose M here.  If unsure, say N.
-
 config NF_NAT_SNMP_BASIC
 	tristate "Basic SNMP-ALG support (EXPERIMENTAL)"
 	depends on EXPERIMENTAL && NF_NAT
@@ -477,78 +264,37 @@
 	tristate
 	depends on NF_NAT && NF_CT_PROTO_GRE
 
-config IP_NF_NAT_FTP
-	tristate
-	depends on IP_NF_IPTABLES && IP_NF_CONNTRACK && IP_NF_NAT
-	default IP_NF_NAT && IP_NF_FTP
-
 config NF_NAT_FTP
 	tristate
 	depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT
 	default NF_NAT && NF_CONNTRACK_FTP
 
-config IP_NF_NAT_IRC
-	tristate
-	depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n
-	default IP_NF_NAT if IP_NF_IRC=y
-	default m if IP_NF_IRC=m
-
 config NF_NAT_IRC
 	tristate
 	depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT
 	default NF_NAT && NF_CONNTRACK_IRC
 
-config IP_NF_NAT_TFTP
-	tristate
-	depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n
-	default IP_NF_NAT if IP_NF_TFTP=y
-	default m if IP_NF_TFTP=m
-
 config NF_NAT_TFTP
 	tristate
 	depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT
 	default NF_NAT && NF_CONNTRACK_TFTP
 
-config IP_NF_NAT_AMANDA
-	tristate
-	depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n
-	default IP_NF_NAT if IP_NF_AMANDA=y
-	default m if IP_NF_AMANDA=m
-
 config NF_NAT_AMANDA
 	tristate
 	depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT
 	default NF_NAT && NF_CONNTRACK_AMANDA
 
-config IP_NF_NAT_PPTP
-	tristate
-	depends on IP_NF_NAT!=n && IP_NF_PPTP!=n
-	default IP_NF_NAT if IP_NF_PPTP=y
-	default m if IP_NF_PPTP=m
-
 config NF_NAT_PPTP
 	tristate
 	depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT
 	default NF_NAT && NF_CONNTRACK_PPTP
 	select NF_NAT_PROTO_GRE
 
-config IP_NF_NAT_H323
-	tristate
-	depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n
-	default IP_NF_NAT if IP_NF_H323=y
-	default m if IP_NF_H323=m
-
 config NF_NAT_H323
 	tristate
 	depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT
 	default NF_NAT && NF_CONNTRACK_H323
 
-config IP_NF_NAT_SIP
-	tristate
-	depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n
-	default IP_NF_NAT if IP_NF_SIP=y
-	default m if IP_NF_SIP=m
-
 config NF_NAT_SIP
 	tristate
 	depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT
@@ -606,9 +352,8 @@
 config IP_NF_TARGET_CLUSTERIP
 	tristate "CLUSTERIP target support (EXPERIMENTAL)"
 	depends on IP_NF_MANGLE && EXPERIMENTAL
-	depends on IP_NF_CONNTRACK || NF_CONNTRACK_IPV4
-	select IP_NF_CONNTRACK_MARK if IP_NF_CONNTRACK
-	select NF_CONNTRACK_MARK if NF_CONNTRACK_IPV4
+	depends on NF_CONNTRACK_IPV4
+	select NF_CONNTRACK_MARK
 	help
 	  The CLUSTERIP target allows you to build load-balancing clusters of
 	  network servers without having a dedicated load-balancing
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
index 6625ec6..409d273 100644
--- a/net/ipv4/netfilter/Makefile
+++ b/net/ipv4/netfilter/Makefile
@@ -2,8 +2,6 @@
 # Makefile for the netfilter modules on top of IPv4.
 #
 
-# objects for the standalone - connection tracking / NAT
-ip_conntrack-objs	:= ip_conntrack_standalone.o ip_conntrack_core.o ip_conntrack_proto_generic.o ip_conntrack_proto_tcp.o ip_conntrack_proto_udp.o ip_conntrack_proto_icmp.o
 # objects for l3 independent conntrack
 nf_conntrack_ipv4-objs  :=  nf_conntrack_l3proto_ipv4.o nf_conntrack_proto_icmp.o
 ifeq ($(CONFIG_NF_CONNTRACK_PROC_COMPAT),y)
@@ -12,53 +10,14 @@
 endif
 endif
 
-ip_nat-objs	:= ip_nat_core.o ip_nat_helper.o ip_nat_proto_unknown.o ip_nat_proto_tcp.o ip_nat_proto_udp.o ip_nat_proto_icmp.o
-nf_nat-objs	:= nf_nat_core.o nf_nat_helper.o nf_nat_proto_unknown.o nf_nat_proto_tcp.o nf_nat_proto_udp.o nf_nat_proto_icmp.o
-ifneq ($(CONFIG_NF_NAT),)
+nf_nat-objs		:= nf_nat_core.o nf_nat_helper.o nf_nat_proto_unknown.o nf_nat_proto_tcp.o nf_nat_proto_udp.o nf_nat_proto_icmp.o
 iptable_nat-objs	:= nf_nat_rule.o nf_nat_standalone.o
-else
-iptable_nat-objs	:= ip_nat_rule.o ip_nat_standalone.o
-endif
-
-ip_conntrack_pptp-objs	:= ip_conntrack_helper_pptp.o ip_conntrack_proto_gre.o
-ip_nat_pptp-objs	:= ip_nat_helper_pptp.o ip_nat_proto_gre.o
-
-ip_conntrack_h323-objs := ip_conntrack_helper_h323.o ../../netfilter/nf_conntrack_h323_asn1.o
-ip_nat_h323-objs := ip_nat_helper_h323.o
 
 # connection tracking
-obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o
 obj-$(CONFIG_NF_CONNTRACK_IPV4) += nf_conntrack_ipv4.o
 
-obj-$(CONFIG_IP_NF_NAT) += ip_nat.o
 obj-$(CONFIG_NF_NAT) += nf_nat.o
 
-# conntrack netlink interface
-obj-$(CONFIG_IP_NF_CONNTRACK_NETLINK) += ip_conntrack_netlink.o
-
-
-# SCTP protocol connection tracking
-obj-$(CONFIG_IP_NF_CT_PROTO_SCTP) += ip_conntrack_proto_sctp.o
-
-# connection tracking helpers
-obj-$(CONFIG_IP_NF_H323) += ip_conntrack_h323.o
-obj-$(CONFIG_IP_NF_PPTP) += ip_conntrack_pptp.o
-obj-$(CONFIG_IP_NF_AMANDA) += ip_conntrack_amanda.o
-obj-$(CONFIG_IP_NF_TFTP) += ip_conntrack_tftp.o
-obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o
-obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o
-obj-$(CONFIG_IP_NF_SIP) += ip_conntrack_sip.o
-obj-$(CONFIG_IP_NF_NETBIOS_NS) += ip_conntrack_netbios_ns.o
-
-# NAT helpers (ip_conntrack)
-obj-$(CONFIG_IP_NF_NAT_H323) += ip_nat_h323.o
-obj-$(CONFIG_IP_NF_NAT_PPTP) += ip_nat_pptp.o
-obj-$(CONFIG_IP_NF_NAT_AMANDA) += ip_nat_amanda.o
-obj-$(CONFIG_IP_NF_NAT_TFTP) += ip_nat_tftp.o
-obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o
-obj-$(CONFIG_IP_NF_NAT_IRC) += ip_nat_irc.o
-obj-$(CONFIG_IP_NF_NAT_SIP) += ip_nat_sip.o
-
 # NAT helpers (nf_conntrack)
 obj-$(CONFIG_NF_NAT_AMANDA) += nf_nat_amanda.o
 obj-$(CONFIG_NF_NAT_FTP) += nf_nat_ftp.o
@@ -78,7 +37,6 @@
 # the three instances of ip_tables
 obj-$(CONFIG_IP_NF_FILTER) += iptable_filter.o
 obj-$(CONFIG_IP_NF_MANGLE) += iptable_mangle.o
-obj-$(CONFIG_IP_NF_NAT) += iptable_nat.o
 obj-$(CONFIG_NF_NAT) += iptable_nat.o
 obj-$(CONFIG_IP_NF_RAW) += iptable_raw.o
 
@@ -100,7 +58,6 @@
 obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o
 obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o
 obj-$(CONFIG_IP_NF_TARGET_SAME) += ipt_SAME.o
-obj-$(CONFIG_IP_NF_NAT_SNMP_BASIC) += ip_nat_snmp_basic.o
 obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o
 obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o
 obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
index 57b0221..cae4121 100644
--- a/net/ipv4/netfilter/arp_tables.c
+++ b/net/ipv4/netfilter/arp_tables.c
@@ -245,7 +245,7 @@
 	e = get_entry(table_base, private->hook_entry[hook]);
 	back = get_entry(table_base, private->underflow[hook]);
 
-	arp = (*pskb)->nh.arph;
+	arp = arp_hdr(*pskb);
 	do {
 		if (arp_packet_match(arp, (*pskb)->dev, indev, outdev, &e->arp)) {
 			struct arpt_entry_target *t;
@@ -297,7 +297,7 @@
 								     t->data);
 
 				/* Target might have changed stuff. */
-				arp = (*pskb)->nh.arph;
+				arp = arp_hdr(*pskb);
 
 				if (verdict == ARPT_CONTINUE)
 					e = (void *)e + e->next_offset;
diff --git a/net/ipv4/netfilter/arpt_mangle.c b/net/ipv4/netfilter/arpt_mangle.c
index 709db4d..6298d40 100644
--- a/net/ipv4/netfilter/arpt_mangle.c
+++ b/net/ipv4/netfilter/arpt_mangle.c
@@ -30,35 +30,35 @@
 		*pskb = nskb;
 	}
 
-	arp = (*pskb)->nh.arph;
-	arpptr = (*pskb)->nh.raw + sizeof(*arp);
+	arp = arp_hdr(*pskb);
+	arpptr = skb_network_header(*pskb) + sizeof(*arp);
 	pln = arp->ar_pln;
 	hln = arp->ar_hln;
 	/* We assume that pln and hln were checked in the match */
 	if (mangle->flags & ARPT_MANGLE_SDEV) {
 		if (ARPT_DEV_ADDR_LEN_MAX < hln ||
-		   (arpptr + hln > (**pskb).tail))
+		   (arpptr + hln > skb_tail_pointer(*pskb)))
 			return NF_DROP;
 		memcpy(arpptr, mangle->src_devaddr, hln);
 	}
 	arpptr += hln;
 	if (mangle->flags & ARPT_MANGLE_SIP) {
 		if (ARPT_MANGLE_ADDR_LEN_MAX < pln ||
-		   (arpptr + pln > (**pskb).tail))
+		   (arpptr + pln > skb_tail_pointer(*pskb)))
 			return NF_DROP;
 		memcpy(arpptr, &mangle->u_s.src_ip, pln);
 	}
 	arpptr += pln;
 	if (mangle->flags & ARPT_MANGLE_TDEV) {
 		if (ARPT_DEV_ADDR_LEN_MAX < hln ||
-		   (arpptr + hln > (**pskb).tail))
+		   (arpptr + hln > skb_tail_pointer(*pskb)))
 			return NF_DROP;
 		memcpy(arpptr, mangle->tgt_devaddr, hln);
 	}
 	arpptr += hln;
 	if (mangle->flags & ARPT_MANGLE_TIP) {
 		if (ARPT_MANGLE_ADDR_LEN_MAX < pln ||
-		   (arpptr + pln > (**pskb).tail))
+		   (arpptr + pln > skb_tail_pointer(*pskb)))
 			return NF_DROP;
 		memcpy(arpptr, &mangle->u_t.tgt_ip, pln);
 	}
diff --git a/net/ipv4/netfilter/ip_conntrack_amanda.c b/net/ipv4/netfilter/ip_conntrack_amanda.c
deleted file mode 100644
index 4f561f5..0000000
--- a/net/ipv4/netfilter/ip_conntrack_amanda.c
+++ /dev/null
@@ -1,229 +0,0 @@
-/* Amanda extension for IP connection tracking, Version 0.2
- * (C) 2002 by Brian J. Murrell <netfilter@interlinx.bc.ca>
- * based on HW's ip_conntrack_irc.c as well as other modules
- *
- *      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.
- *
- *	Module load syntax:
- * 	insmod ip_conntrack_amanda.o [master_timeout=n]
- *
- *	Where master_timeout is the timeout (in seconds) of the master
- *	connection (port 10080).  This defaults to 5 minutes but if
- *	your clients take longer than 5 minutes to do their work
- *	before getting back to the Amanda server, you can increase
- *	this value.
- *
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/textsearch.h>
-#include <linux/skbuff.h>
-#include <linux/in.h>
-#include <linux/ip.h>
-#include <linux/udp.h>
-
-#include <linux/netfilter.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-#include <linux/netfilter_ipv4/ip_conntrack_amanda.h>
-
-static unsigned int master_timeout = 300;
-static char *ts_algo = "kmp";
-
-MODULE_AUTHOR("Brian J. Murrell <netfilter@interlinx.bc.ca>");
-MODULE_DESCRIPTION("Amanda connection tracking module");
-MODULE_LICENSE("GPL");
-module_param(master_timeout, uint, 0600);
-MODULE_PARM_DESC(master_timeout, "timeout for the master connection");
-module_param(ts_algo, charp, 0400);
-MODULE_PARM_DESC(ts_algo, "textsearch algorithm to use (default kmp)");
-
-unsigned int (*ip_nat_amanda_hook)(struct sk_buff **pskb,
-				   enum ip_conntrack_info ctinfo,
-				   unsigned int matchoff,
-				   unsigned int matchlen,
-				   struct ip_conntrack_expect *exp);
-EXPORT_SYMBOL_GPL(ip_nat_amanda_hook);
-
-enum amanda_strings {
-	SEARCH_CONNECT,
-	SEARCH_NEWLINE,
-	SEARCH_DATA,
-	SEARCH_MESG,
-	SEARCH_INDEX,
-};
-
-static struct {
-	char			*string;
-	size_t			len;
-	struct ts_config	*ts;
-} search[] = {
-	[SEARCH_CONNECT] = {
-		.string	= "CONNECT ",
-		.len	= 8,
-	},
-	[SEARCH_NEWLINE] = {
-		.string	= "\n",
-		.len	= 1,
-	},
-	[SEARCH_DATA] = {
-		.string	= "DATA ",
-		.len	= 5,
-	},
-	[SEARCH_MESG] = {
-		.string	= "MESG ",
-		.len	= 5,
-	},
-	[SEARCH_INDEX] = {
-		.string = "INDEX ",
-		.len	= 6,
-	},
-};
-
-static int help(struct sk_buff **pskb,
-		struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
-{
-	struct ts_state ts;
-	struct ip_conntrack_expect *exp;
-	unsigned int dataoff, start, stop, off, i;
-	char pbuf[sizeof("65535")], *tmp;
-	u_int16_t port, len;
-	int ret = NF_ACCEPT;
-	typeof(ip_nat_amanda_hook) ip_nat_amanda;
-
-	/* Only look at packets from the Amanda server */
-	if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL)
-		return NF_ACCEPT;
-
-	/* increase the UDP timeout of the master connection as replies from
-	 * Amanda clients to the server can be quite delayed */
-	ip_ct_refresh(ct, *pskb, master_timeout * HZ);
-
-	/* No data? */
-	dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
-	if (dataoff >= (*pskb)->len) {
-		if (net_ratelimit())
-			printk("amanda_help: skblen = %u\n", (*pskb)->len);
-		return NF_ACCEPT;
-	}
-
-	memset(&ts, 0, sizeof(ts));
-	start = skb_find_text(*pskb, dataoff, (*pskb)->len,
-			      search[SEARCH_CONNECT].ts, &ts);
-	if (start == UINT_MAX)
-		goto out;
-	start += dataoff + search[SEARCH_CONNECT].len;
-
-	memset(&ts, 0, sizeof(ts));
-	stop = skb_find_text(*pskb, start, (*pskb)->len,
-			     search[SEARCH_NEWLINE].ts, &ts);
-	if (stop == UINT_MAX)
-		goto out;
-	stop += start;
-
-	for (i = SEARCH_DATA; i <= SEARCH_INDEX; i++) {
-		memset(&ts, 0, sizeof(ts));
-		off = skb_find_text(*pskb, start, stop, search[i].ts, &ts);
-		if (off == UINT_MAX)
-			continue;
-		off += start + search[i].len;
-
-		len = min_t(unsigned int, sizeof(pbuf) - 1, stop - off);
-		if (skb_copy_bits(*pskb, off, pbuf, len))
-			break;
-		pbuf[len] = '\0';
-
-		port = simple_strtoul(pbuf, &tmp, 10);
-		len = tmp - pbuf;
-		if (port == 0 || len > 5)
-			break;
-
-		exp = ip_conntrack_expect_alloc(ct);
-		if (exp == NULL) {
-			ret = NF_DROP;
-			goto out;
-		}
-
-		exp->expectfn = NULL;
-		exp->flags = 0;
-
-		exp->tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
-		exp->tuple.src.u.tcp.port = 0;
-		exp->tuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
-		exp->tuple.dst.protonum = IPPROTO_TCP;
-		exp->tuple.dst.u.tcp.port = htons(port);
-
-		exp->mask.src.ip = htonl(0xFFFFFFFF);
-		exp->mask.src.u.tcp.port = 0;
-		exp->mask.dst.ip = htonl(0xFFFFFFFF);
-		exp->mask.dst.protonum = 0xFF;
-		exp->mask.dst.u.tcp.port = htons(0xFFFF);
-
-		/* RCU read locked by nf_hook_slow */
-		ip_nat_amanda = rcu_dereference(ip_nat_amanda_hook);
-		if (ip_nat_amanda)
-			ret = ip_nat_amanda(pskb, ctinfo, off - dataoff,
-					    len, exp);
-		else if (ip_conntrack_expect_related(exp) != 0)
-			ret = NF_DROP;
-		ip_conntrack_expect_put(exp);
-	}
-
-out:
-	return ret;
-}
-
-static struct ip_conntrack_helper amanda_helper = {
-	.max_expected = 3,
-	.timeout = 180,
-	.me = THIS_MODULE,
-	.help = help,
-	.name = "amanda",
-
-	.tuple = { .src = { .u = { .udp = {.port = __constant_htons(10080) } } },
-		   .dst = { .protonum = IPPROTO_UDP },
-	},
-	.mask = { .src = { .u = { 0xFFFF } },
-		 .dst = { .protonum = 0xFF },
-	},
-};
-
-static void __exit ip_conntrack_amanda_fini(void)
-{
-	int i;
-
-	ip_conntrack_helper_unregister(&amanda_helper);
-	for (i = 0; i < ARRAY_SIZE(search); i++)
-		textsearch_destroy(search[i].ts);
-}
-
-static int __init ip_conntrack_amanda_init(void)
-{
-	int ret, i;
-
-	ret = -ENOMEM;
-	for (i = 0; i < ARRAY_SIZE(search); i++) {
-		search[i].ts = textsearch_prepare(ts_algo, search[i].string,
-						  search[i].len,
-						  GFP_KERNEL, TS_AUTOLOAD);
-		if (search[i].ts == NULL)
-			goto err;
-	}
-	ret = ip_conntrack_helper_register(&amanda_helper);
-	if (ret < 0)
-		goto err;
-	return 0;
-
-err:
-	for (; i >= 0; i--) {
-		if (search[i].ts)
-			textsearch_destroy(search[i].ts);
-	}
-	return ret;
-}
-
-module_init(ip_conntrack_amanda_init);
-module_exit(ip_conntrack_amanda_fini);
diff --git a/net/ipv4/netfilter/ip_conntrack_core.c b/net/ipv4/netfilter/ip_conntrack_core.c
deleted file mode 100644
index 23b99ae..0000000
--- a/net/ipv4/netfilter/ip_conntrack_core.c
+++ /dev/null
@@ -1,1550 +0,0 @@
-/* Connection state tracking for netfilter.  This is separated from,
-   but required by, the NAT layer; it can also be used by an iptables
-   extension. */
-
-/* (C) 1999-2001 Paul `Rusty' Russell
- * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.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.
- *
- * 23 Apr 2001: Harald Welte <laforge@gnumonks.org>
- * 	- new API and handling of conntrack/nat helpers
- * 	- now capable of multiple expectations for one master
- * 16 Jul 2002: Harald Welte <laforge@gnumonks.org>
- * 	- add usage/reference counts to ip_conntrack_expect
- *	- export ip_conntrack[_expect]_{find_get,put} functions
- * */
-
-#include <linux/types.h>
-#include <linux/icmp.h>
-#include <linux/ip.h>
-#include <linux/netfilter.h>
-#include <linux/netfilter_ipv4.h>
-#include <linux/module.h>
-#include <linux/skbuff.h>
-#include <linux/proc_fs.h>
-#include <linux/vmalloc.h>
-#include <net/checksum.h>
-#include <net/ip.h>
-#include <linux/stddef.h>
-#include <linux/sysctl.h>
-#include <linux/slab.h>
-#include <linux/random.h>
-#include <linux/jhash.h>
-#include <linux/err.h>
-#include <linux/percpu.h>
-#include <linux/moduleparam.h>
-#include <linux/notifier.h>
-
-/* ip_conntrack_lock protects the main hash table, protocol/helper/expected
-   registrations, conntrack timers*/
-#include <linux/netfilter_ipv4/ip_conntrack.h>
-#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-#include <linux/netfilter_ipv4/ip_conntrack_core.h>
-
-#define IP_CONNTRACK_VERSION	"2.4"
-
-#if 0
-#define DEBUGP printk
-#else
-#define DEBUGP(format, args...)
-#endif
-
-DEFINE_RWLOCK(ip_conntrack_lock);
-
-/* ip_conntrack_standalone needs this */
-atomic_t ip_conntrack_count = ATOMIC_INIT(0);
-
-void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack) = NULL;
-LIST_HEAD(ip_conntrack_expect_list);
-struct ip_conntrack_protocol *ip_ct_protos[MAX_IP_CT_PROTO] __read_mostly;
-static LIST_HEAD(helpers);
-unsigned int ip_conntrack_htable_size __read_mostly = 0;
-int ip_conntrack_max __read_mostly;
-struct list_head *ip_conntrack_hash __read_mostly;
-static struct kmem_cache *ip_conntrack_cachep __read_mostly;
-static struct kmem_cache *ip_conntrack_expect_cachep __read_mostly;
-struct ip_conntrack ip_conntrack_untracked;
-unsigned int ip_ct_log_invalid __read_mostly;
-static LIST_HEAD(unconfirmed);
-static int ip_conntrack_vmalloc __read_mostly;
-
-static unsigned int ip_conntrack_next_id;
-static unsigned int ip_conntrack_expect_next_id;
-#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS
-ATOMIC_NOTIFIER_HEAD(ip_conntrack_chain);
-ATOMIC_NOTIFIER_HEAD(ip_conntrack_expect_chain);
-
-DEFINE_PER_CPU(struct ip_conntrack_ecache, ip_conntrack_ecache);
-
-/* deliver cached events and clear cache entry - must be called with locally
- * disabled softirqs */
-static inline void
-__ip_ct_deliver_cached_events(struct ip_conntrack_ecache *ecache)
-{
-	DEBUGP("ecache: delivering events for %p\n", ecache->ct);
-	if (is_confirmed(ecache->ct) && !is_dying(ecache->ct) && ecache->events)
-		atomic_notifier_call_chain(&ip_conntrack_chain, ecache->events,
-				    ecache->ct);
-	ecache->events = 0;
-	ip_conntrack_put(ecache->ct);
-	ecache->ct = NULL;
-}
-
-/* Deliver all cached events for a particular conntrack. This is called
- * by code prior to async packet handling or freeing the skb */
-void ip_ct_deliver_cached_events(const struct ip_conntrack *ct)
-{
-	struct ip_conntrack_ecache *ecache;
-
-	local_bh_disable();
-	ecache = &__get_cpu_var(ip_conntrack_ecache);
-	if (ecache->ct == ct)
-		__ip_ct_deliver_cached_events(ecache);
-	local_bh_enable();
-}
-
-void __ip_ct_event_cache_init(struct ip_conntrack *ct)
-{
-	struct ip_conntrack_ecache *ecache;
-
-	/* take care of delivering potentially old events */
-	ecache = &__get_cpu_var(ip_conntrack_ecache);
-	BUG_ON(ecache->ct == ct);
-	if (ecache->ct)
-		__ip_ct_deliver_cached_events(ecache);
-	/* initialize for this conntrack/packet */
-	ecache->ct = ct;
-	nf_conntrack_get(&ct->ct_general);
-}
-
-/* flush the event cache - touches other CPU's data and must not be called while
- * packets are still passing through the code */
-static void ip_ct_event_cache_flush(void)
-{
-	struct ip_conntrack_ecache *ecache;
-	int cpu;
-
-	for_each_possible_cpu(cpu) {
-		ecache = &per_cpu(ip_conntrack_ecache, cpu);
-		if (ecache->ct)
-			ip_conntrack_put(ecache->ct);
-	}
-}
-#else
-static inline void ip_ct_event_cache_flush(void) {}
-#endif /* CONFIG_IP_NF_CONNTRACK_EVENTS */
-
-DEFINE_PER_CPU(struct ip_conntrack_stat, ip_conntrack_stat);
-
-static int ip_conntrack_hash_rnd_initted;
-static unsigned int ip_conntrack_hash_rnd;
-
-static u_int32_t __hash_conntrack(const struct ip_conntrack_tuple *tuple,
-			    unsigned int size, unsigned int rnd)
-{
-	return (jhash_3words((__force u32)tuple->src.ip,
-			     ((__force u32)tuple->dst.ip ^ tuple->dst.protonum),
-			     (tuple->src.u.all | (tuple->dst.u.all << 16)),
-			     rnd) % size);
-}
-
-static u_int32_t
-hash_conntrack(const struct ip_conntrack_tuple *tuple)
-{
-	return __hash_conntrack(tuple, ip_conntrack_htable_size,
-				ip_conntrack_hash_rnd);
-}
-
-int
-ip_ct_get_tuple(const struct iphdr *iph,
-		const struct sk_buff *skb,
-		unsigned int dataoff,
-		struct ip_conntrack_tuple *tuple,
-		const struct ip_conntrack_protocol *protocol)
-{
-	/* Never happen */
-	if (iph->frag_off & htons(IP_OFFSET)) {
-		printk("ip_conntrack_core: Frag of proto %u.\n",
-		       iph->protocol);
-		return 0;
-	}
-
-	tuple->src.ip = iph->saddr;
-	tuple->dst.ip = iph->daddr;
-	tuple->dst.protonum = iph->protocol;
-	tuple->dst.dir = IP_CT_DIR_ORIGINAL;
-
-	return protocol->pkt_to_tuple(skb, dataoff, tuple);
-}
-
-int
-ip_ct_invert_tuple(struct ip_conntrack_tuple *inverse,
-		   const struct ip_conntrack_tuple *orig,
-		   const struct ip_conntrack_protocol *protocol)
-{
-	inverse->src.ip = orig->dst.ip;
-	inverse->dst.ip = orig->src.ip;
-	inverse->dst.protonum = orig->dst.protonum;
-	inverse->dst.dir = !orig->dst.dir;
-
-	return protocol->invert_tuple(inverse, orig);
-}
-
-
-/* ip_conntrack_expect helper functions */
-void ip_ct_unlink_expect(struct ip_conntrack_expect *exp)
-{
-	IP_NF_ASSERT(!timer_pending(&exp->timeout));
-	list_del(&exp->list);
-	CONNTRACK_STAT_INC(expect_delete);
-	exp->master->expecting--;
-	ip_conntrack_expect_put(exp);
-}
-
-static void expectation_timed_out(unsigned long ul_expect)
-{
-	struct ip_conntrack_expect *exp = (void *)ul_expect;
-
-	write_lock_bh(&ip_conntrack_lock);
-	ip_ct_unlink_expect(exp);
-	write_unlock_bh(&ip_conntrack_lock);
-	ip_conntrack_expect_put(exp);
-}
-
-struct ip_conntrack_expect *
-__ip_conntrack_expect_find(const struct ip_conntrack_tuple *tuple)
-{
-	struct ip_conntrack_expect *i;
-
-	list_for_each_entry(i, &ip_conntrack_expect_list, list) {
-		if (ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask))
-			return i;
-	}
-	return NULL;
-}
-
-/* Just find a expectation corresponding to a tuple. */
-struct ip_conntrack_expect *
-ip_conntrack_expect_find_get(const struct ip_conntrack_tuple *tuple)
-{
-	struct ip_conntrack_expect *i;
-
-	read_lock_bh(&ip_conntrack_lock);
-	i = __ip_conntrack_expect_find(tuple);
-	if (i)
-		atomic_inc(&i->use);
-	read_unlock_bh(&ip_conntrack_lock);
-
-	return i;
-}
-
-/* If an expectation for this connection is found, it gets delete from
- * global list then returned. */
-static struct ip_conntrack_expect *
-find_expectation(const struct ip_conntrack_tuple *tuple)
-{
-	struct ip_conntrack_expect *i;
-
-	list_for_each_entry(i, &ip_conntrack_expect_list, list) {
-		/* If master is not in hash table yet (ie. packet hasn't left
-		   this machine yet), how can other end know about expected?
-		   Hence these are not the droids you are looking for (if
-		   master ct never got confirmed, we'd hold a reference to it
-		   and weird things would happen to future packets). */
-		if (ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask)
-		    && is_confirmed(i->master)) {
-			if (i->flags & IP_CT_EXPECT_PERMANENT) {
-				atomic_inc(&i->use);
-				return i;
-			} else if (del_timer(&i->timeout)) {
-				ip_ct_unlink_expect(i);
-				return i;
-			}
-		}
-	}
-	return NULL;
-}
-
-/* delete all expectations for this conntrack */
-void ip_ct_remove_expectations(struct ip_conntrack *ct)
-{
-	struct ip_conntrack_expect *i, *tmp;
-
-	/* Optimization: most connection never expect any others. */
-	if (ct->expecting == 0)
-		return;
-
-	list_for_each_entry_safe(i, tmp, &ip_conntrack_expect_list, list) {
-		if (i->master == ct && del_timer(&i->timeout)) {
-			ip_ct_unlink_expect(i);
-			ip_conntrack_expect_put(i);
-		}
-	}
-}
-
-static void
-clean_from_lists(struct ip_conntrack *ct)
-{
-	DEBUGP("clean_from_lists(%p)\n", ct);
-	list_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list);
-	list_del(&ct->tuplehash[IP_CT_DIR_REPLY].list);
-
-	/* Destroy all pending expectations */
-	ip_ct_remove_expectations(ct);
-}
-
-static void
-destroy_conntrack(struct nf_conntrack *nfct)
-{
-	struct ip_conntrack *ct = (struct ip_conntrack *)nfct;
-	struct ip_conntrack_protocol *proto;
-	struct ip_conntrack_helper *helper;
-	typeof(ip_conntrack_destroyed) destroyed;
-
-	DEBUGP("destroy_conntrack(%p)\n", ct);
-	IP_NF_ASSERT(atomic_read(&nfct->use) == 0);
-	IP_NF_ASSERT(!timer_pending(&ct->timeout));
-
-	ip_conntrack_event(IPCT_DESTROY, ct);
-	set_bit(IPS_DYING_BIT, &ct->status);
-
-	helper = ct->helper;
-	if (helper && helper->destroy)
-		helper->destroy(ct);
-
-	/* To make sure we don't get any weird locking issues here:
-	 * destroy_conntrack() MUST NOT be called with a write lock
-	 * to ip_conntrack_lock!!! -HW */
-	rcu_read_lock();
-	proto = __ip_conntrack_proto_find(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum);
-	if (proto && proto->destroy)
-		proto->destroy(ct);
-
-	destroyed = rcu_dereference(ip_conntrack_destroyed);
-	if (destroyed)
-		destroyed(ct);
-
-	rcu_read_unlock();
-
-	write_lock_bh(&ip_conntrack_lock);
-	/* Expectations will have been removed in clean_from_lists,
-	 * except TFTP can create an expectation on the first packet,
-	 * before connection is in the list, so we need to clean here,
-	 * too. */
-	ip_ct_remove_expectations(ct);
-
-	/* We overload first tuple to link into unconfirmed list. */
-	if (!is_confirmed(ct)) {
-		BUG_ON(list_empty(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list));
-		list_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list);
-	}
-
-	CONNTRACK_STAT_INC(delete);
-	write_unlock_bh(&ip_conntrack_lock);
-
-	if (ct->master)
-		ip_conntrack_put(ct->master);
-
-	DEBUGP("destroy_conntrack: returning ct=%p to slab\n", ct);
-	ip_conntrack_free(ct);
-}
-
-static void death_by_timeout(unsigned long ul_conntrack)
-{
-	struct ip_conntrack *ct = (void *)ul_conntrack;
-
-	write_lock_bh(&ip_conntrack_lock);
-	/* Inside lock so preempt is disabled on module removal path.
-	 * Otherwise we can get spurious warnings. */
-	CONNTRACK_STAT_INC(delete_list);
-	clean_from_lists(ct);
-	write_unlock_bh(&ip_conntrack_lock);
-	ip_conntrack_put(ct);
-}
-
-struct ip_conntrack_tuple_hash *
-__ip_conntrack_find(const struct ip_conntrack_tuple *tuple,
-		    const struct ip_conntrack *ignored_conntrack)
-{
-	struct ip_conntrack_tuple_hash *h;
-	unsigned int hash = hash_conntrack(tuple);
-
-	list_for_each_entry(h, &ip_conntrack_hash[hash], list) {
-		if (tuplehash_to_ctrack(h) != ignored_conntrack &&
-		    ip_ct_tuple_equal(tuple, &h->tuple)) {
-			CONNTRACK_STAT_INC(found);
-			return h;
-		}
-		CONNTRACK_STAT_INC(searched);
-	}
-
-	return NULL;
-}
-
-/* Find a connection corresponding to a tuple. */
-struct ip_conntrack_tuple_hash *
-ip_conntrack_find_get(const struct ip_conntrack_tuple *tuple,
-		      const struct ip_conntrack *ignored_conntrack)
-{
-	struct ip_conntrack_tuple_hash *h;
-
-	read_lock_bh(&ip_conntrack_lock);
-	h = __ip_conntrack_find(tuple, ignored_conntrack);
-	if (h)
-		atomic_inc(&tuplehash_to_ctrack(h)->ct_general.use);
-	read_unlock_bh(&ip_conntrack_lock);
-
-	return h;
-}
-
-static void __ip_conntrack_hash_insert(struct ip_conntrack *ct,
-					unsigned int hash,
-					unsigned int repl_hash)
-{
-	ct->id = ++ip_conntrack_next_id;
-	list_add(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list,
-		 &ip_conntrack_hash[hash]);
-	list_add(&ct->tuplehash[IP_CT_DIR_REPLY].list,
-		 &ip_conntrack_hash[repl_hash]);
-}
-
-void ip_conntrack_hash_insert(struct ip_conntrack *ct)
-{
-	unsigned int hash, repl_hash;
-
-	hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
-	repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
-
-	write_lock_bh(&ip_conntrack_lock);
-	__ip_conntrack_hash_insert(ct, hash, repl_hash);
-	write_unlock_bh(&ip_conntrack_lock);
-}
-
-/* Confirm a connection given skb; places it in hash table */
-int
-__ip_conntrack_confirm(struct sk_buff **pskb)
-{
-	unsigned int hash, repl_hash;
-	struct ip_conntrack_tuple_hash *h;
-	struct ip_conntrack *ct;
-	enum ip_conntrack_info ctinfo;
-
-	ct = ip_conntrack_get(*pskb, &ctinfo);
-
-	/* ipt_REJECT uses ip_conntrack_attach to attach related
-	   ICMP/TCP RST packets in other direction.  Actual packet
-	   which created connection will be IP_CT_NEW or for an
-	   expected connection, IP_CT_RELATED. */
-	if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL)
-		return NF_ACCEPT;
-
-	hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
-	repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
-
-	/* We're not in hash table, and we refuse to set up related
-	   connections for unconfirmed conns.  But packet copies and
-	   REJECT will give spurious warnings here. */
-	/* IP_NF_ASSERT(atomic_read(&ct->ct_general.use) == 1); */
-
-	/* No external references means noone else could have
-	   confirmed us. */
-	IP_NF_ASSERT(!is_confirmed(ct));
-	DEBUGP("Confirming conntrack %p\n", ct);
-
-	write_lock_bh(&ip_conntrack_lock);
-
-	/* See if there's one in the list already, including reverse:
-	   NAT could have grabbed it without realizing, since we're
-	   not in the hash.  If there is, we lost race. */
-	list_for_each_entry(h, &ip_conntrack_hash[hash], list)
-		if (ip_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
-				      &h->tuple))
-			goto out;
-	list_for_each_entry(h, &ip_conntrack_hash[repl_hash], list)
-		if (ip_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_REPLY].tuple,
-				      &h->tuple))
-			goto out;
-
-	/* Remove from unconfirmed list */
-	list_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list);
-
-	__ip_conntrack_hash_insert(ct, hash, repl_hash);
-	/* Timer relative to confirmation time, not original
-	   setting time, otherwise we'd get timer wrap in
-	   weird delay cases. */
-	ct->timeout.expires += jiffies;
-	add_timer(&ct->timeout);
-	atomic_inc(&ct->ct_general.use);
-	set_bit(IPS_CONFIRMED_BIT, &ct->status);
-	CONNTRACK_STAT_INC(insert);
-	write_unlock_bh(&ip_conntrack_lock);
-	if (ct->helper)
-		ip_conntrack_event_cache(IPCT_HELPER, *pskb);
-#ifdef CONFIG_IP_NF_NAT_NEEDED
-	if (test_bit(IPS_SRC_NAT_DONE_BIT, &ct->status) ||
-	    test_bit(IPS_DST_NAT_DONE_BIT, &ct->status))
-		ip_conntrack_event_cache(IPCT_NATINFO, *pskb);
-#endif
-	ip_conntrack_event_cache(master_ct(ct) ?
-				 IPCT_RELATED : IPCT_NEW, *pskb);
-
-	return NF_ACCEPT;
-
-out:
-	CONNTRACK_STAT_INC(insert_failed);
-	write_unlock_bh(&ip_conntrack_lock);
-	return NF_DROP;
-}
-
-/* Returns true if a connection correspondings to the tuple (required
-   for NAT). */
-int
-ip_conntrack_tuple_taken(const struct ip_conntrack_tuple *tuple,
-			 const struct ip_conntrack *ignored_conntrack)
-{
-	struct ip_conntrack_tuple_hash *h;
-
-	read_lock_bh(&ip_conntrack_lock);
-	h = __ip_conntrack_find(tuple, ignored_conntrack);
-	read_unlock_bh(&ip_conntrack_lock);
-
-	return h != NULL;
-}
-
-/* There's a small race here where we may free a just-assured
-   connection.  Too bad: we're in trouble anyway. */
-static int early_drop(struct list_head *chain)
-{
-	/* Traverse backwards: gives us oldest, which is roughly LRU */
-	struct ip_conntrack_tuple_hash *h;
-	struct ip_conntrack *ct = NULL, *tmp;
-	int dropped = 0;
-
-	read_lock_bh(&ip_conntrack_lock);
-	list_for_each_entry_reverse(h, chain, list) {
-		tmp = tuplehash_to_ctrack(h);
-		if (!test_bit(IPS_ASSURED_BIT, &tmp->status)) {
-			ct = tmp;
-			atomic_inc(&ct->ct_general.use);
-			break;
-		}
-	}
-	read_unlock_bh(&ip_conntrack_lock);
-
-	if (!ct)
-		return dropped;
-
-	if (del_timer(&ct->timeout)) {
-		death_by_timeout((unsigned long)ct);
-		dropped = 1;
-		CONNTRACK_STAT_INC_ATOMIC(early_drop);
-	}
-	ip_conntrack_put(ct);
-	return dropped;
-}
-
-static struct ip_conntrack_helper *
-__ip_conntrack_helper_find( const struct ip_conntrack_tuple *tuple)
-{
-	struct ip_conntrack_helper *h;
-
-	list_for_each_entry(h, &helpers, list) {
-		if (ip_ct_tuple_mask_cmp(tuple, &h->tuple, &h->mask))
-			return h;
-	}
-	return NULL;
-}
-
-struct ip_conntrack_helper *
-ip_conntrack_helper_find_get( const struct ip_conntrack_tuple *tuple)
-{
-	struct ip_conntrack_helper *helper;
-
-	/* need ip_conntrack_lock to assure that helper exists until
-	 * try_module_get() is called */
-	read_lock_bh(&ip_conntrack_lock);
-
-	helper = __ip_conntrack_helper_find(tuple);
-	if (helper) {
-		/* need to increase module usage count to assure helper will
-		 * not go away while the caller is e.g. busy putting a
-		 * conntrack in the hash that uses the helper */
-		if (!try_module_get(helper->me))
-			helper = NULL;
-	}
-
-	read_unlock_bh(&ip_conntrack_lock);
-
-	return helper;
-}
-
-void ip_conntrack_helper_put(struct ip_conntrack_helper *helper)
-{
-	module_put(helper->me);
-}
-
-struct ip_conntrack_protocol *
-__ip_conntrack_proto_find(u_int8_t protocol)
-{
-	return ip_ct_protos[protocol];
-}
-
-/* this is guaranteed to always return a valid protocol helper, since
- * it falls back to generic_protocol */
-struct ip_conntrack_protocol *
-ip_conntrack_proto_find_get(u_int8_t protocol)
-{
-	struct ip_conntrack_protocol *p;
-
-	rcu_read_lock();
-	p = __ip_conntrack_proto_find(protocol);
-	if (p) {
-		if (!try_module_get(p->me))
-			p = &ip_conntrack_generic_protocol;
-	}
-	rcu_read_unlock();
-
-	return p;
-}
-
-void ip_conntrack_proto_put(struct ip_conntrack_protocol *p)
-{
-	module_put(p->me);
-}
-
-struct ip_conntrack *ip_conntrack_alloc(struct ip_conntrack_tuple *orig,
-					struct ip_conntrack_tuple *repl)
-{
-	struct ip_conntrack *conntrack;
-
-	if (!ip_conntrack_hash_rnd_initted) {
-		get_random_bytes(&ip_conntrack_hash_rnd, 4);
-		ip_conntrack_hash_rnd_initted = 1;
-	}
-
-	/* We don't want any race condition at early drop stage */
-	atomic_inc(&ip_conntrack_count);
-
-	if (ip_conntrack_max
-	    && atomic_read(&ip_conntrack_count) > ip_conntrack_max) {
-		unsigned int hash = hash_conntrack(orig);
-		/* Try dropping from this hash chain. */
-		if (!early_drop(&ip_conntrack_hash[hash])) {
-			atomic_dec(&ip_conntrack_count);
-			if (net_ratelimit())
-				printk(KERN_WARNING
-				       "ip_conntrack: table full, dropping"
-				       " packet.\n");
-			return ERR_PTR(-ENOMEM);
-		}
-	}
-
-	conntrack = kmem_cache_zalloc(ip_conntrack_cachep, GFP_ATOMIC);
-	if (!conntrack) {
-		DEBUGP("Can't allocate conntrack.\n");
-		atomic_dec(&ip_conntrack_count);
-		return ERR_PTR(-ENOMEM);
-	}
-
-	atomic_set(&conntrack->ct_general.use, 1);
-	conntrack->ct_general.destroy = destroy_conntrack;
-	conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *orig;
-	conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *repl;
-	/* Don't set timer yet: wait for confirmation */
-	init_timer(&conntrack->timeout);
-	conntrack->timeout.data = (unsigned long)conntrack;
-	conntrack->timeout.function = death_by_timeout;
-
-	return conntrack;
-}
-
-void
-ip_conntrack_free(struct ip_conntrack *conntrack)
-{
-	atomic_dec(&ip_conntrack_count);
-	kmem_cache_free(ip_conntrack_cachep, conntrack);
-}
-
-/* Allocate a new conntrack: we return -ENOMEM if classification
- * failed due to stress.   Otherwise it really is unclassifiable */
-static struct ip_conntrack_tuple_hash *
-init_conntrack(struct ip_conntrack_tuple *tuple,
-	       struct ip_conntrack_protocol *protocol,
-	       struct sk_buff *skb)
-{
-	struct ip_conntrack *conntrack;
-	struct ip_conntrack_tuple repl_tuple;
-	struct ip_conntrack_expect *exp;
-
-	if (!ip_ct_invert_tuple(&repl_tuple, tuple, protocol)) {
-		DEBUGP("Can't invert tuple.\n");
-		return NULL;
-	}
-
-	conntrack = ip_conntrack_alloc(tuple, &repl_tuple);
-	if (conntrack == NULL || IS_ERR(conntrack))
-		return (struct ip_conntrack_tuple_hash *)conntrack;
-
-	if (!protocol->new(conntrack, skb)) {
-		ip_conntrack_free(conntrack);
-		return NULL;
-	}
-
-	write_lock_bh(&ip_conntrack_lock);
-	exp = find_expectation(tuple);
-
-	if (exp) {
-		DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n",
-			conntrack, exp);
-		/* Welcome, Mr. Bond.  We've been expecting you... */
-		__set_bit(IPS_EXPECTED_BIT, &conntrack->status);
-		conntrack->master = exp->master;
-#ifdef CONFIG_IP_NF_CONNTRACK_MARK
-		conntrack->mark = exp->master->mark;
-#endif
-#if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \
-    defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE)
-		/* this is ugly, but there is no other place where to put it */
-		conntrack->nat.masq_index = exp->master->nat.masq_index;
-#endif
-#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK
-		conntrack->secmark = exp->master->secmark;
-#endif
-		nf_conntrack_get(&conntrack->master->ct_general);
-		CONNTRACK_STAT_INC(expect_new);
-	} else {
-		conntrack->helper = __ip_conntrack_helper_find(&repl_tuple);
-
-		CONNTRACK_STAT_INC(new);
-	}
-
-	/* Overload tuple linked list to put us in unconfirmed list. */
-	list_add(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL].list, &unconfirmed);
-
-	write_unlock_bh(&ip_conntrack_lock);
-
-	if (exp) {
-		if (exp->expectfn)
-			exp->expectfn(conntrack, exp);
-		ip_conntrack_expect_put(exp);
-	}
-
-	return &conntrack->tuplehash[IP_CT_DIR_ORIGINAL];
-}
-
-/* On success, returns conntrack ptr, sets skb->nfct and ctinfo */
-static inline struct ip_conntrack *
-resolve_normal_ct(struct sk_buff *skb,
-		  struct ip_conntrack_protocol *proto,
-		  int *set_reply,
-		  unsigned int hooknum,
-		  enum ip_conntrack_info *ctinfo)
-{
-	struct ip_conntrack_tuple tuple;
-	struct ip_conntrack_tuple_hash *h;
-	struct ip_conntrack *ct;
-
-	IP_NF_ASSERT((skb->nh.iph->frag_off & htons(IP_OFFSET)) == 0);
-
-	if (!ip_ct_get_tuple(skb->nh.iph, skb, skb->nh.iph->ihl*4,
-				&tuple,proto))
-		return NULL;
-
-	/* look for tuple match */
-	h = ip_conntrack_find_get(&tuple, NULL);
-	if (!h) {
-		h = init_conntrack(&tuple, proto, skb);
-		if (!h)
-			return NULL;
-		if (IS_ERR(h))
-			return (void *)h;
-	}
-	ct = tuplehash_to_ctrack(h);
-
-	/* It exists; we have (non-exclusive) reference. */
-	if (DIRECTION(h) == IP_CT_DIR_REPLY) {
-		*ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY;
-		/* Please set reply bit if this packet OK */
-		*set_reply = 1;
-	} else {
-		/* Once we've had two way comms, always ESTABLISHED. */
-		if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
-			DEBUGP("ip_conntrack_in: normal packet for %p\n",
-			       ct);
-			*ctinfo = IP_CT_ESTABLISHED;
-		} else if (test_bit(IPS_EXPECTED_BIT, &ct->status)) {
-			DEBUGP("ip_conntrack_in: related packet for %p\n",
-			       ct);
-			*ctinfo = IP_CT_RELATED;
-		} else {
-			DEBUGP("ip_conntrack_in: new packet for %p\n",
-			       ct);
-			*ctinfo = IP_CT_NEW;
-		}
-		*set_reply = 0;
-	}
-	skb->nfct = &ct->ct_general;
-	skb->nfctinfo = *ctinfo;
-	return ct;
-}
-
-/* Netfilter hook itself. */
-unsigned int ip_conntrack_in(unsigned int hooknum,
-			     struct sk_buff **pskb,
-			     const struct net_device *in,
-			     const struct net_device *out,
-			     int (*okfn)(struct sk_buff *))
-{
-	struct ip_conntrack *ct;
-	enum ip_conntrack_info ctinfo;
-	struct ip_conntrack_protocol *proto;
-	int set_reply = 0;
-	int ret;
-
-	/* Previously seen (loopback or untracked)?  Ignore. */
-	if ((*pskb)->nfct) {
-		CONNTRACK_STAT_INC_ATOMIC(ignore);
-		return NF_ACCEPT;
-	}
-
-	/* Never happen */
-	if ((*pskb)->nh.iph->frag_off & htons(IP_OFFSET)) {
-		if (net_ratelimit()) {
-		printk(KERN_ERR "ip_conntrack_in: Frag of proto %u (hook=%u)\n",
-		       (*pskb)->nh.iph->protocol, hooknum);
-		}
-		return NF_DROP;
-	}
-
-/* Doesn't cover locally-generated broadcast, so not worth it. */
-#if 0
-	/* Ignore broadcast: no `connection'. */
-	if ((*pskb)->pkt_type == PACKET_BROADCAST) {
-		printk("Broadcast packet!\n");
-		return NF_ACCEPT;
-	} else if (((*pskb)->nh.iph->daddr & htonl(0x000000FF))
-		   == htonl(0x000000FF)) {
-		printk("Should bcast: %u.%u.%u.%u->%u.%u.%u.%u (sk=%p, ptype=%u)\n",
-		       NIPQUAD((*pskb)->nh.iph->saddr),
-		       NIPQUAD((*pskb)->nh.iph->daddr),
-		       (*pskb)->sk, (*pskb)->pkt_type);
-	}
-#endif
-
-	/* rcu_read_lock()ed by nf_hook_slow */
-	proto = __ip_conntrack_proto_find((*pskb)->nh.iph->protocol);
-
-	/* It may be an special packet, error, unclean...
-	 * inverse of the return code tells to the netfilter
-	 * core what to do with the packet. */
-	if (proto->error != NULL
-	    && (ret = proto->error(*pskb, &ctinfo, hooknum)) <= 0) {
-		CONNTRACK_STAT_INC_ATOMIC(error);
-		CONNTRACK_STAT_INC_ATOMIC(invalid);
-		return -ret;
-	}
-
-	if (!(ct = resolve_normal_ct(*pskb, proto,&set_reply,hooknum,&ctinfo))) {
-		/* Not valid part of a connection */
-		CONNTRACK_STAT_INC_ATOMIC(invalid);
-		return NF_ACCEPT;
-	}
-
-	if (IS_ERR(ct)) {
-		/* Too stressed to deal. */
-		CONNTRACK_STAT_INC_ATOMIC(drop);
-		return NF_DROP;
-	}
-
-	IP_NF_ASSERT((*pskb)->nfct);
-
-	ret = proto->packet(ct, *pskb, ctinfo);
-	if (ret < 0) {
-		/* Invalid: inverse of the return code tells
-		 * the netfilter core what to do*/
-		nf_conntrack_put((*pskb)->nfct);
-		(*pskb)->nfct = NULL;
-		CONNTRACK_STAT_INC_ATOMIC(invalid);
-		return -ret;
-	}
-
-	if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status))
-		ip_conntrack_event_cache(IPCT_STATUS, *pskb);
-
-	return ret;
-}
-
-int invert_tuplepr(struct ip_conntrack_tuple *inverse,
-		   const struct ip_conntrack_tuple *orig)
-{
-	struct ip_conntrack_protocol *proto;
-	int ret;
-
-	rcu_read_lock();
-	proto = __ip_conntrack_proto_find(orig->dst.protonum);
-	ret = ip_ct_invert_tuple(inverse, orig, proto);
-	rcu_read_unlock();
-
-	return ret;
-}
-
-/* Would two expected things clash? */
-static inline int expect_clash(const struct ip_conntrack_expect *a,
-			       const struct ip_conntrack_expect *b)
-{
-	/* Part covered by intersection of masks must be unequal,
-	   otherwise they clash */
-	struct ip_conntrack_tuple intersect_mask
-		= { { a->mask.src.ip & b->mask.src.ip,
-		      { a->mask.src.u.all & b->mask.src.u.all } },
-		    { a->mask.dst.ip & b->mask.dst.ip,
-		      { a->mask.dst.u.all & b->mask.dst.u.all },
-		      a->mask.dst.protonum & b->mask.dst.protonum } };
-
-	return ip_ct_tuple_mask_cmp(&a->tuple, &b->tuple, &intersect_mask);
-}
-
-static inline int expect_matches(const struct ip_conntrack_expect *a,
-				 const struct ip_conntrack_expect *b)
-{
-	return a->master == b->master
-		&& ip_ct_tuple_equal(&a->tuple, &b->tuple)
-		&& ip_ct_tuple_equal(&a->mask, &b->mask);
-}
-
-/* Generally a bad idea to call this: could have matched already. */
-void ip_conntrack_unexpect_related(struct ip_conntrack_expect *exp)
-{
-	struct ip_conntrack_expect *i;
-
-	write_lock_bh(&ip_conntrack_lock);
-	/* choose the the oldest expectation to evict */
-	list_for_each_entry_reverse(i, &ip_conntrack_expect_list, list) {
-		if (expect_matches(i, exp) && del_timer(&i->timeout)) {
-			ip_ct_unlink_expect(i);
-			write_unlock_bh(&ip_conntrack_lock);
-			ip_conntrack_expect_put(i);
-			return;
-		}
-	}
-	write_unlock_bh(&ip_conntrack_lock);
-}
-
-/* We don't increase the master conntrack refcount for non-fulfilled
- * conntracks. During the conntrack destruction, the expectations are
- * always killed before the conntrack itself */
-struct ip_conntrack_expect *ip_conntrack_expect_alloc(struct ip_conntrack *me)
-{
-	struct ip_conntrack_expect *new;
-
-	new = kmem_cache_alloc(ip_conntrack_expect_cachep, GFP_ATOMIC);
-	if (!new) {
-		DEBUGP("expect_related: OOM allocating expect\n");
-		return NULL;
-	}
-	new->master = me;
-	atomic_set(&new->use, 1);
-	return new;
-}
-
-void ip_conntrack_expect_put(struct ip_conntrack_expect *exp)
-{
-	if (atomic_dec_and_test(&exp->use))
-		kmem_cache_free(ip_conntrack_expect_cachep, exp);
-}
-
-static void ip_conntrack_expect_insert(struct ip_conntrack_expect *exp)
-{
-	atomic_inc(&exp->use);
-	exp->master->expecting++;
-	list_add(&exp->list, &ip_conntrack_expect_list);
-
-	init_timer(&exp->timeout);
-	exp->timeout.data = (unsigned long)exp;
-	exp->timeout.function = expectation_timed_out;
-	exp->timeout.expires = jiffies + exp->master->helper->timeout * HZ;
-	add_timer(&exp->timeout);
-
-	exp->id = ++ip_conntrack_expect_next_id;
-	atomic_inc(&exp->use);
-	CONNTRACK_STAT_INC(expect_create);
-}
-
-/* Race with expectations being used means we could have none to find; OK. */
-static void evict_oldest_expect(struct ip_conntrack *master)
-{
-	struct ip_conntrack_expect *i;
-
-	list_for_each_entry_reverse(i, &ip_conntrack_expect_list, list) {
-		if (i->master == master) {
-			if (del_timer(&i->timeout)) {
-				ip_ct_unlink_expect(i);
-				ip_conntrack_expect_put(i);
-			}
-			break;
-		}
-	}
-}
-
-static inline int refresh_timer(struct ip_conntrack_expect *i)
-{
-	if (!del_timer(&i->timeout))
-		return 0;
-
-	i->timeout.expires = jiffies + i->master->helper->timeout*HZ;
-	add_timer(&i->timeout);
-	return 1;
-}
-
-int ip_conntrack_expect_related(struct ip_conntrack_expect *expect)
-{
-	struct ip_conntrack_expect *i;
-	int ret;
-
-	DEBUGP("ip_conntrack_expect_related %p\n", related_to);
-	DEBUGP("tuple: "); DUMP_TUPLE(&expect->tuple);
-	DEBUGP("mask:  "); DUMP_TUPLE(&expect->mask);
-
-	write_lock_bh(&ip_conntrack_lock);
-	list_for_each_entry(i, &ip_conntrack_expect_list, list) {
-		if (expect_matches(i, expect)) {
-			/* Refresh timer: if it's dying, ignore.. */
-			if (refresh_timer(i)) {
-				ret = 0;
-				goto out;
-			}
-		} else if (expect_clash(i, expect)) {
-			ret = -EBUSY;
-			goto out;
-		}
-	}
-
-	/* Will be over limit? */
-	if (expect->master->helper->max_expected &&
-	    expect->master->expecting >= expect->master->helper->max_expected)
-		evict_oldest_expect(expect->master);
-
-	ip_conntrack_expect_insert(expect);
-	ip_conntrack_expect_event(IPEXP_NEW, expect);
-	ret = 0;
-out:
-	write_unlock_bh(&ip_conntrack_lock);
-	return ret;
-}
-
-/* Alter reply tuple (maybe alter helper).  This is for NAT, and is
-   implicitly racy: see __ip_conntrack_confirm */
-void ip_conntrack_alter_reply(struct ip_conntrack *conntrack,
-			      const struct ip_conntrack_tuple *newreply)
-{
-	write_lock_bh(&ip_conntrack_lock);
-	/* Should be unconfirmed, so not in hash table yet */
-	IP_NF_ASSERT(!is_confirmed(conntrack));
-
-	DEBUGP("Altering reply tuple of %p to ", conntrack);
-	DUMP_TUPLE(newreply);
-
-	conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply;
-	if (!conntrack->master && conntrack->expecting == 0)
-		conntrack->helper = __ip_conntrack_helper_find(newreply);
-	write_unlock_bh(&ip_conntrack_lock);
-}
-
-int ip_conntrack_helper_register(struct ip_conntrack_helper *me)
-{
-	BUG_ON(me->timeout == 0);
-	write_lock_bh(&ip_conntrack_lock);
-	list_add(&me->list, &helpers);
-	write_unlock_bh(&ip_conntrack_lock);
-
-	return 0;
-}
-
-struct ip_conntrack_helper *
-__ip_conntrack_helper_find_byname(const char *name)
-{
-	struct ip_conntrack_helper *h;
-
-	list_for_each_entry(h, &helpers, list) {
-		if (!strcmp(h->name, name))
-			return h;
-	}
-
-	return NULL;
-}
-
-static inline void unhelp(struct ip_conntrack_tuple_hash *i,
-			  const struct ip_conntrack_helper *me)
-{
-	if (tuplehash_to_ctrack(i)->helper == me) {
-		ip_conntrack_event(IPCT_HELPER, tuplehash_to_ctrack(i));
-		tuplehash_to_ctrack(i)->helper = NULL;
-	}
-}
-
-void ip_conntrack_helper_unregister(struct ip_conntrack_helper *me)
-{
-	unsigned int i;
-	struct ip_conntrack_tuple_hash *h;
-	struct ip_conntrack_expect *exp, *tmp;
-
-	/* Need write lock here, to delete helper. */
-	write_lock_bh(&ip_conntrack_lock);
-	list_del(&me->list);
-
-	/* Get rid of expectations */
-	list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list, list) {
-		if (exp->master->helper == me && del_timer(&exp->timeout)) {
-			ip_ct_unlink_expect(exp);
-			ip_conntrack_expect_put(exp);
-		}
-	}
-	/* Get rid of expecteds, set helpers to NULL. */
-	list_for_each_entry(h, &unconfirmed, list)
-		unhelp(h, me);
-	for (i = 0; i < ip_conntrack_htable_size; i++) {
-		list_for_each_entry(h, &ip_conntrack_hash[i], list)
-			unhelp(h, me);
-	}
-	write_unlock_bh(&ip_conntrack_lock);
-
-	/* Someone could be still looking at the helper in a bh. */
-	synchronize_net();
-}
-
-/* Refresh conntrack for this many jiffies and do accounting if do_acct is 1 */
-void __ip_ct_refresh_acct(struct ip_conntrack *ct,
-			enum ip_conntrack_info ctinfo,
-			const struct sk_buff *skb,
-			unsigned long extra_jiffies,
-			int do_acct)
-{
-	int event = 0;
-
-	IP_NF_ASSERT(ct->timeout.data == (unsigned long)ct);
-	IP_NF_ASSERT(skb);
-
-	write_lock_bh(&ip_conntrack_lock);
-
-	/* Only update if this is not a fixed timeout */
-	if (test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status)) {
-		write_unlock_bh(&ip_conntrack_lock);
-		return;
-	}
-
-	/* If not in hash table, timer will not be active yet */
-	if (!is_confirmed(ct)) {
-		ct->timeout.expires = extra_jiffies;
-		event = IPCT_REFRESH;
-	} else {
-		/* Need del_timer for race avoidance (may already be dying). */
-		if (del_timer(&ct->timeout)) {
-			ct->timeout.expires = jiffies + extra_jiffies;
-			add_timer(&ct->timeout);
-			event = IPCT_REFRESH;
-		}
-	}
-
-#ifdef CONFIG_IP_NF_CT_ACCT
-	if (do_acct) {
-		ct->counters[CTINFO2DIR(ctinfo)].packets++;
-		ct->counters[CTINFO2DIR(ctinfo)].bytes +=
-						ntohs(skb->nh.iph->tot_len);
-		if ((ct->counters[CTINFO2DIR(ctinfo)].packets & 0x80000000)
-		    || (ct->counters[CTINFO2DIR(ctinfo)].bytes & 0x80000000))
-			event |= IPCT_COUNTER_FILLING;
-	}
-#endif
-
-	write_unlock_bh(&ip_conntrack_lock);
-
-	/* must be unlocked when calling event cache */
-	if (event)
-		ip_conntrack_event_cache(event, skb);
-}
-
-#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
-    defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
-/* Generic function for tcp/udp/sctp/dccp and alike. This needs to be
- * in ip_conntrack_core, since we don't want the protocols to autoload
- * or depend on ctnetlink */
-int ip_ct_port_tuple_to_nfattr(struct sk_buff *skb,
-			       const struct ip_conntrack_tuple *tuple)
-{
-	NFA_PUT(skb, CTA_PROTO_SRC_PORT, sizeof(__be16),
-		&tuple->src.u.tcp.port);
-	NFA_PUT(skb, CTA_PROTO_DST_PORT, sizeof(__be16),
-		&tuple->dst.u.tcp.port);
-	return 0;
-
-nfattr_failure:
-	return -1;
-}
-
-int ip_ct_port_nfattr_to_tuple(struct nfattr *tb[],
-			       struct ip_conntrack_tuple *t)
-{
-	if (!tb[CTA_PROTO_SRC_PORT-1] || !tb[CTA_PROTO_DST_PORT-1])
-		return -EINVAL;
-
-	t->src.u.tcp.port =
-		*(__be16 *)NFA_DATA(tb[CTA_PROTO_SRC_PORT-1]);
-	t->dst.u.tcp.port =
-		*(__be16 *)NFA_DATA(tb[CTA_PROTO_DST_PORT-1]);
-
-	return 0;
-}
-#endif
-
-/* Returns new sk_buff, or NULL */
-struct sk_buff *
-ip_ct_gather_frags(struct sk_buff *skb, u_int32_t user)
-{
-	skb_orphan(skb);
-
-	local_bh_disable();
-	skb = ip_defrag(skb, user);
-	local_bh_enable();
-
-	if (skb)
-		ip_send_check(skb->nh.iph);
-	return skb;
-}
-
-/* Used by ipt_REJECT. */
-static void ip_conntrack_attach(struct sk_buff *nskb, struct sk_buff *skb)
-{
-	struct ip_conntrack *ct;
-	enum ip_conntrack_info ctinfo;
-
-	/* This ICMP is in reverse direction to the packet which caused it */
-	ct = ip_conntrack_get(skb, &ctinfo);
-
-	if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL)
-		ctinfo = IP_CT_RELATED + IP_CT_IS_REPLY;
-	else
-		ctinfo = IP_CT_RELATED;
-
-	/* Attach to new skbuff, and increment count */
-	nskb->nfct = &ct->ct_general;
-	nskb->nfctinfo = ctinfo;
-	nf_conntrack_get(nskb->nfct);
-}
-
-/* Bring out ya dead! */
-static struct ip_conntrack *
-get_next_corpse(int (*iter)(struct ip_conntrack *i, void *data),
-		void *data, unsigned int *bucket)
-{
-	struct ip_conntrack_tuple_hash *h;
-	struct ip_conntrack *ct;
-
-	write_lock_bh(&ip_conntrack_lock);
-	for (; *bucket < ip_conntrack_htable_size; (*bucket)++) {
-		list_for_each_entry(h, &ip_conntrack_hash[*bucket], list) {
-			ct = tuplehash_to_ctrack(h);
-			if (iter(ct, data))
-				goto found;
-		}
-	}
-	list_for_each_entry(h, &unconfirmed, list) {
-		ct = tuplehash_to_ctrack(h);
-		if (iter(ct, data))
-			set_bit(IPS_DYING_BIT, &ct->status);
-	}
-	write_unlock_bh(&ip_conntrack_lock);
-	return NULL;
-
-found:
-	atomic_inc(&ct->ct_general.use);
-	write_unlock_bh(&ip_conntrack_lock);
-	return ct;
-}
-
-void
-ip_ct_iterate_cleanup(int (*iter)(struct ip_conntrack *i, void *), void *data)
-{
-	struct ip_conntrack *ct;
-	unsigned int bucket = 0;
-
-	while ((ct = get_next_corpse(iter, data, &bucket)) != NULL) {
-		/* Time to push up daises... */
-		if (del_timer(&ct->timeout))
-			death_by_timeout((unsigned long)ct);
-		/* ... else the timer will get him soon. */
-
-		ip_conntrack_put(ct);
-	}
-}
-
-/* Fast function for those who don't want to parse /proc (and I don't
-   blame them). */
-/* Reversing the socket's dst/src point of view gives us the reply
-   mapping. */
-static int
-getorigdst(struct sock *sk, int optval, void __user *user, int *len)
-{
-	struct inet_sock *inet = inet_sk(sk);
-	struct ip_conntrack_tuple_hash *h;
-	struct ip_conntrack_tuple tuple;
-
-	IP_CT_TUPLE_U_BLANK(&tuple);
-	tuple.src.ip = inet->rcv_saddr;
-	tuple.src.u.tcp.port = inet->sport;
-	tuple.dst.ip = inet->daddr;
-	tuple.dst.u.tcp.port = inet->dport;
-	tuple.dst.protonum = IPPROTO_TCP;
-
-	/* We only do TCP at the moment: is there a better way? */
-	if (strcmp(sk->sk_prot->name, "TCP")) {
-		DEBUGP("SO_ORIGINAL_DST: Not a TCP socket\n");
-		return -ENOPROTOOPT;
-	}
-
-	if ((unsigned int) *len < sizeof(struct sockaddr_in)) {
-		DEBUGP("SO_ORIGINAL_DST: len %u not %u\n",
-		       *len, sizeof(struct sockaddr_in));
-		return -EINVAL;
-	}
-
-	h = ip_conntrack_find_get(&tuple, NULL);
-	if (h) {
-		struct sockaddr_in sin;
-		struct ip_conntrack *ct = tuplehash_to_ctrack(h);
-
-		sin.sin_family = AF_INET;
-		sin.sin_port = ct->tuplehash[IP_CT_DIR_ORIGINAL]
-			.tuple.dst.u.tcp.port;
-		sin.sin_addr.s_addr = ct->tuplehash[IP_CT_DIR_ORIGINAL]
-			.tuple.dst.ip;
-		memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
-
-		DEBUGP("SO_ORIGINAL_DST: %u.%u.%u.%u %u\n",
-		       NIPQUAD(sin.sin_addr.s_addr), ntohs(sin.sin_port));
-		ip_conntrack_put(ct);
-		if (copy_to_user(user, &sin, sizeof(sin)) != 0)
-			return -EFAULT;
-		else
-			return 0;
-	}
-	DEBUGP("SO_ORIGINAL_DST: Can't find %u.%u.%u.%u/%u-%u.%u.%u.%u/%u.\n",
-	       NIPQUAD(tuple.src.ip), ntohs(tuple.src.u.tcp.port),
-	       NIPQUAD(tuple.dst.ip), ntohs(tuple.dst.u.tcp.port));
-	return -ENOENT;
-}
-
-static struct nf_sockopt_ops so_getorigdst = {
-	.pf		= PF_INET,
-	.get_optmin	= SO_ORIGINAL_DST,
-	.get_optmax	= SO_ORIGINAL_DST+1,
-	.get		= &getorigdst,
-};
-
-static int kill_all(struct ip_conntrack *i, void *data)
-{
-	return 1;
-}
-
-void ip_conntrack_flush(void)
-{
-	ip_ct_iterate_cleanup(kill_all, NULL);
-}
-
-static void free_conntrack_hash(struct list_head *hash, int vmalloced,int size)
-{
-	if (vmalloced)
-		vfree(hash);
-	else
-		free_pages((unsigned long)hash,
-			   get_order(sizeof(struct list_head) * size));
-}
-
-/* Mishearing the voices in his head, our hero wonders how he's
-   supposed to kill the mall. */
-void ip_conntrack_cleanup(void)
-{
-	rcu_assign_pointer(ip_ct_attach, NULL);
-
-	/* This makes sure all current packets have passed through
-	   netfilter framework.  Roll on, two-stage module
-	   delete... */
-	synchronize_net();
-
-	ip_ct_event_cache_flush();
- i_see_dead_people:
-	ip_conntrack_flush();
-	if (atomic_read(&ip_conntrack_count) != 0) {
-		schedule();
-		goto i_see_dead_people;
-	}
-	/* wait until all references to ip_conntrack_untracked are dropped */
-	while (atomic_read(&ip_conntrack_untracked.ct_general.use) > 1)
-		schedule();
-
-	kmem_cache_destroy(ip_conntrack_cachep);
-	kmem_cache_destroy(ip_conntrack_expect_cachep);
-	free_conntrack_hash(ip_conntrack_hash, ip_conntrack_vmalloc,
-			    ip_conntrack_htable_size);
-	nf_unregister_sockopt(&so_getorigdst);
-}
-
-static struct list_head *alloc_hashtable(int size, int *vmalloced)
-{
-	struct list_head *hash;
-	unsigned int i;
-
-	*vmalloced = 0;
-	hash = (void*)__get_free_pages(GFP_KERNEL,
-				       get_order(sizeof(struct list_head)
-						 * size));
-	if (!hash) {
-		*vmalloced = 1;
-		printk(KERN_WARNING"ip_conntrack: falling back to vmalloc.\n");
-		hash = vmalloc(sizeof(struct list_head) * size);
-	}
-
-	if (hash)
-		for (i = 0; i < size; i++)
-			INIT_LIST_HEAD(&hash[i]);
-
-	return hash;
-}
-
-static int set_hashsize(const char *val, struct kernel_param *kp)
-{
-	int i, bucket, hashsize, vmalloced;
-	int old_vmalloced, old_size;
-	int rnd;
-	struct list_head *hash, *old_hash;
-	struct ip_conntrack_tuple_hash *h;
-
-	/* On boot, we can set this without any fancy locking. */
-	if (!ip_conntrack_htable_size)
-		return param_set_int(val, kp);
-
-	hashsize = simple_strtol(val, NULL, 0);
-	if (!hashsize)
-		return -EINVAL;
-
-	hash = alloc_hashtable(hashsize, &vmalloced);
-	if (!hash)
-		return -ENOMEM;
-
-	/* We have to rehash for the new table anyway, so we also can
-	 * use a new random seed */
-	get_random_bytes(&rnd, 4);
-
-	write_lock_bh(&ip_conntrack_lock);
-	for (i = 0; i < ip_conntrack_htable_size; i++) {
-		while (!list_empty(&ip_conntrack_hash[i])) {
-			h = list_entry(ip_conntrack_hash[i].next,
-				       struct ip_conntrack_tuple_hash, list);
-			list_del(&h->list);
-			bucket = __hash_conntrack(&h->tuple, hashsize, rnd);
-			list_add_tail(&h->list, &hash[bucket]);
-		}
-	}
-	old_size = ip_conntrack_htable_size;
-	old_vmalloced = ip_conntrack_vmalloc;
-	old_hash = ip_conntrack_hash;
-
-	ip_conntrack_htable_size = hashsize;
-	ip_conntrack_vmalloc = vmalloced;
-	ip_conntrack_hash = hash;
-	ip_conntrack_hash_rnd = rnd;
-	write_unlock_bh(&ip_conntrack_lock);
-
-	free_conntrack_hash(old_hash, old_vmalloced, old_size);
-	return 0;
-}
-
-module_param_call(hashsize, set_hashsize, param_get_uint,
-		  &ip_conntrack_htable_size, 0600);
-
-int __init ip_conntrack_init(void)
-{
-	unsigned int i;
-	int ret;
-
-	/* Idea from tcp.c: use 1/16384 of memory.  On i386: 32MB
-	 * machine has 256 buckets.  >= 1GB machines have 8192 buckets. */
-	if (!ip_conntrack_htable_size) {
-		ip_conntrack_htable_size
-			= (((num_physpages << PAGE_SHIFT) / 16384)
-			   / sizeof(struct list_head));
-		if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE))
-			ip_conntrack_htable_size = 8192;
-		if (ip_conntrack_htable_size < 16)
-			ip_conntrack_htable_size = 16;
-	}
-	ip_conntrack_max = 8 * ip_conntrack_htable_size;
-
-	printk("ip_conntrack version %s (%u buckets, %d max)"
-	       " - %Zd bytes per conntrack\n", IP_CONNTRACK_VERSION,
-	       ip_conntrack_htable_size, ip_conntrack_max,
-	       sizeof(struct ip_conntrack));
-
-	ret = nf_register_sockopt(&so_getorigdst);
-	if (ret != 0) {
-		printk(KERN_ERR "Unable to register netfilter socket option\n");
-		return ret;
-	}
-
-	ip_conntrack_hash = alloc_hashtable(ip_conntrack_htable_size,
-					    &ip_conntrack_vmalloc);
-	if (!ip_conntrack_hash) {
-		printk(KERN_ERR "Unable to create ip_conntrack_hash\n");
-		goto err_unreg_sockopt;
-	}
-
-	ip_conntrack_cachep = kmem_cache_create("ip_conntrack",
-						sizeof(struct ip_conntrack), 0,
-						0, NULL, NULL);
-	if (!ip_conntrack_cachep) {
-		printk(KERN_ERR "Unable to create ip_conntrack slab cache\n");
-		goto err_free_hash;
-	}
-
-	ip_conntrack_expect_cachep = kmem_cache_create("ip_conntrack_expect",
-					sizeof(struct ip_conntrack_expect),
-					0, 0, NULL, NULL);
-	if (!ip_conntrack_expect_cachep) {
-		printk(KERN_ERR "Unable to create ip_expect slab cache\n");
-		goto err_free_conntrack_slab;
-	}
-
-	/* Don't NEED lock here, but good form anyway. */
-	write_lock_bh(&ip_conntrack_lock);
-	for (i = 0; i < MAX_IP_CT_PROTO; i++)
-		rcu_assign_pointer(ip_ct_protos[i], &ip_conntrack_generic_protocol);
-	/* Sew in builtin protocols. */
-	rcu_assign_pointer(ip_ct_protos[IPPROTO_TCP], &ip_conntrack_protocol_tcp);
-	rcu_assign_pointer(ip_ct_protos[IPPROTO_UDP], &ip_conntrack_protocol_udp);
-	rcu_assign_pointer(ip_ct_protos[IPPROTO_ICMP], &ip_conntrack_protocol_icmp);
-	write_unlock_bh(&ip_conntrack_lock);
-
-	/* For use by ipt_REJECT */
-	rcu_assign_pointer(ip_ct_attach, ip_conntrack_attach);
-
-	/* Set up fake conntrack:
-	    - to never be deleted, not in any hashes */
-	atomic_set(&ip_conntrack_untracked.ct_general.use, 1);
-	/*  - and look it like as a confirmed connection */
-	set_bit(IPS_CONFIRMED_BIT, &ip_conntrack_untracked.status);
-
-	return ret;
-
-err_free_conntrack_slab:
-	kmem_cache_destroy(ip_conntrack_cachep);
-err_free_hash:
-	free_conntrack_hash(ip_conntrack_hash, ip_conntrack_vmalloc,
-			    ip_conntrack_htable_size);
-err_unreg_sockopt:
-	nf_unregister_sockopt(&so_getorigdst);
-
-	return -ENOMEM;
-}
diff --git a/net/ipv4/netfilter/ip_conntrack_ftp.c b/net/ipv4/netfilter/ip_conntrack_ftp.c
deleted file mode 100644
index 1faa68a..0000000
--- a/net/ipv4/netfilter/ip_conntrack_ftp.c
+++ /dev/null
@@ -1,520 +0,0 @@
-/* FTP extension for IP connection tracking. */
-
-/* (C) 1999-2001 Paul `Rusty' Russell
- * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.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.
- */
-
-#include <linux/module.h>
-#include <linux/netfilter.h>
-#include <linux/ip.h>
-#include <linux/ctype.h>
-#include <net/checksum.h>
-#include <net/tcp.h>
-
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-#include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
-#include <linux/moduleparam.h>
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>");
-MODULE_DESCRIPTION("ftp connection tracking helper");
-
-/* This is slow, but it's simple. --RR */
-static char *ftp_buffer;
-static DEFINE_SPINLOCK(ip_ftp_lock);
-
-#define MAX_PORTS 8
-static unsigned short ports[MAX_PORTS];
-static int ports_c;
-module_param_array(ports, ushort, &ports_c, 0400);
-
-static int loose;
-module_param(loose, bool, 0600);
-
-unsigned int (*ip_nat_ftp_hook)(struct sk_buff **pskb,
-				enum ip_conntrack_info ctinfo,
-				enum ip_ct_ftp_type type,
-				unsigned int matchoff,
-				unsigned int matchlen,
-				struct ip_conntrack_expect *exp,
-				u32 *seq);
-EXPORT_SYMBOL_GPL(ip_nat_ftp_hook);
-
-#if 0
-#define DEBUGP printk
-#else
-#define DEBUGP(format, args...)
-#endif
-
-static int try_rfc959(const char *, size_t, u_int32_t [], char);
-static int try_eprt(const char *, size_t, u_int32_t [], char);
-static int try_epsv_response(const char *, size_t, u_int32_t [], char);
-
-static const struct ftp_search {
-	const char *pattern;
-	size_t plen;
-	char skip;
-	char term;
-	enum ip_ct_ftp_type ftptype;
-	int (*getnum)(const char *, size_t, u_int32_t[], char);
-} search[IP_CT_DIR_MAX][2] = {
-	[IP_CT_DIR_ORIGINAL] = {
-		{
-			.pattern	=  "PORT",
-			.plen		= sizeof("PORT") - 1,
-			.skip		= ' ',
-			.term		= '\r',
-			.ftptype	= IP_CT_FTP_PORT,
-			.getnum		= try_rfc959,
-		},
-		{
-			.pattern	= "EPRT",
-			.plen		= sizeof("EPRT") - 1,
-			.skip		= ' ',
-			.term		= '\r',
-			.ftptype	= IP_CT_FTP_EPRT,
-			.getnum		= try_eprt,
-		},
-	},
-	[IP_CT_DIR_REPLY] = {
-		{
-			.pattern	= "227 ",
-			.plen		= sizeof("227 ") - 1,
-			.skip		= '(',
-			.term		= ')',
-			.ftptype	= IP_CT_FTP_PASV,
-			.getnum		= try_rfc959,
-		},
-		{
-			.pattern	= "229 ",
-			.plen		= sizeof("229 ") - 1,
-			.skip		= '(',
-			.term		= ')',
-			.ftptype	= IP_CT_FTP_EPSV,
-			.getnum		= try_epsv_response,
-		},
-	},
-};
-
-static int try_number(const char *data, size_t dlen, u_int32_t array[],
-		      int array_size, char sep, char term)
-{
-	u_int32_t i, len;
-
-	memset(array, 0, sizeof(array[0])*array_size);
-
-	/* Keep data pointing at next char. */
-	for (i = 0, len = 0; len < dlen && i < array_size; len++, data++) {
-		if (*data >= '0' && *data <= '9') {
-			array[i] = array[i]*10 + *data - '0';
-		}
-		else if (*data == sep)
-			i++;
-		else {
-			/* Unexpected character; true if it's the
-			   terminator and we're finished. */
-			if (*data == term && i == array_size - 1)
-				return len;
-
-			DEBUGP("Char %u (got %u nums) `%u' unexpected\n",
-			       len, i, *data);
-			return 0;
-		}
-	}
-	DEBUGP("Failed to fill %u numbers separated by %c\n", array_size, sep);
-
-	return 0;
-}
-
-/* Returns 0, or length of numbers: 192,168,1,1,5,6 */
-static int try_rfc959(const char *data, size_t dlen, u_int32_t array[6],
-		       char term)
-{
-	return try_number(data, dlen, array, 6, ',', term);
-}
-
-/* Grab port: number up to delimiter */
-static int get_port(const char *data, int start, size_t dlen, char delim,
-		    u_int32_t array[2])
-{
-	u_int16_t port = 0;
-	int i;
-
-	for (i = start; i < dlen; i++) {
-		/* Finished? */
-		if (data[i] == delim) {
-			if (port == 0)
-				break;
-			array[0] = port >> 8;
-			array[1] = port;
-			return i + 1;
-		}
-		else if (data[i] >= '0' && data[i] <= '9')
-			port = port*10 + data[i] - '0';
-		else /* Some other crap */
-			break;
-	}
-	return 0;
-}
-
-/* Returns 0, or length of numbers: |1|132.235.1.2|6275| */
-static int try_eprt(const char *data, size_t dlen, u_int32_t array[6],
-		    char term)
-{
-	char delim;
-	int length;
-
-	/* First character is delimiter, then "1" for IPv4, then
-	   delimiter again. */
-	if (dlen <= 3) return 0;
-	delim = data[0];
-	if (isdigit(delim) || delim < 33 || delim > 126
-	    || data[1] != '1' || data[2] != delim)
-		return 0;
-
-	DEBUGP("EPRT: Got |1|!\n");
-	/* Now we have IP address. */
-	length = try_number(data + 3, dlen - 3, array, 4, '.', delim);
-	if (length == 0)
-		return 0;
-
-	DEBUGP("EPRT: Got IP address!\n");
-	/* Start offset includes initial "|1|", and trailing delimiter */
-	return get_port(data, 3 + length + 1, dlen, delim, array+4);
-}
-
-/* Returns 0, or length of numbers: |||6446| */
-static int try_epsv_response(const char *data, size_t dlen, u_int32_t array[6],
-			     char term)
-{
-	char delim;
-
-	/* Three delimiters. */
-	if (dlen <= 3) return 0;
-	delim = data[0];
-	if (isdigit(delim) || delim < 33 || delim > 126
-	    || data[1] != delim || data[2] != delim)
-		return 0;
-
-	return get_port(data, 3, dlen, delim, array+4);
-}
-
-/* Return 1 for match, 0 for accept, -1 for partial. */
-static int find_pattern(const char *data, size_t dlen,
-			const char *pattern, size_t plen,
-			char skip, char term,
-			unsigned int *numoff,
-			unsigned int *numlen,
-			u_int32_t array[6],
-			int (*getnum)(const char *, size_t, u_int32_t[], char))
-{
-	size_t i;
-
-	DEBUGP("find_pattern `%s': dlen = %u\n", pattern, dlen);
-	if (dlen == 0)
-		return 0;
-
-	if (dlen <= plen) {
-		/* Short packet: try for partial? */
-		if (strnicmp(data, pattern, dlen) == 0)
-			return -1;
-		else return 0;
-	}
-
-	if (strnicmp(data, pattern, plen) != 0) {
-#if 0
-		size_t i;
-
-		DEBUGP("ftp: string mismatch\n");
-		for (i = 0; i < plen; i++) {
-			DEBUGP("ftp:char %u `%c'(%u) vs `%c'(%u)\n",
-				i, data[i], data[i],
-				pattern[i], pattern[i]);
-		}
-#endif
-		return 0;
-	}
-
-	DEBUGP("Pattern matches!\n");
-	/* Now we've found the constant string, try to skip
-	   to the 'skip' character */
-	for (i = plen; data[i] != skip; i++)
-		if (i == dlen - 1) return -1;
-
-	/* Skip over the last character */
-	i++;
-
-	DEBUGP("Skipped up to `%c'!\n", skip);
-
-	*numoff = i;
-	*numlen = getnum(data + i, dlen - i, array, term);
-	if (!*numlen)
-		return -1;
-
-	DEBUGP("Match succeeded!\n");
-	return 1;
-}
-
-/* Look up to see if we're just after a \n. */
-static int find_nl_seq(u32 seq, const struct ip_ct_ftp_master *info, int dir)
-{
-	unsigned int i;
-
-	for (i = 0; i < info->seq_aft_nl_num[dir]; i++)
-		if (info->seq_aft_nl[dir][i] == seq)
-			return 1;
-	return 0;
-}
-
-/* We don't update if it's older than what we have. */
-static void update_nl_seq(u32 nl_seq, struct ip_ct_ftp_master *info, int dir,
-			  struct sk_buff *skb)
-{
-	unsigned int i, oldest = NUM_SEQ_TO_REMEMBER;
-
-	/* Look for oldest: if we find exact match, we're done. */
-	for (i = 0; i < info->seq_aft_nl_num[dir]; i++) {
-		if (info->seq_aft_nl[dir][i] == nl_seq)
-			return;
-
-		if (oldest == info->seq_aft_nl_num[dir]
-		    || before(info->seq_aft_nl[dir][i], oldest))
-			oldest = i;
-	}
-
-	if (info->seq_aft_nl_num[dir] < NUM_SEQ_TO_REMEMBER) {
-		info->seq_aft_nl[dir][info->seq_aft_nl_num[dir]++] = nl_seq;
-		ip_conntrack_event_cache(IPCT_HELPINFO_VOLATILE, skb);
-	} else if (oldest != NUM_SEQ_TO_REMEMBER) {
-		info->seq_aft_nl[dir][oldest] = nl_seq;
-		ip_conntrack_event_cache(IPCT_HELPINFO_VOLATILE, skb);
-	}
-}
-
-static int help(struct sk_buff **pskb,
-		struct ip_conntrack *ct,
-		enum ip_conntrack_info ctinfo)
-{
-	unsigned int dataoff, datalen;
-	struct tcphdr _tcph, *th;
-	char *fb_ptr;
-	int ret;
-	u32 seq, array[6] = { 0 };
-	int dir = CTINFO2DIR(ctinfo);
-	unsigned int matchlen, matchoff;
-	struct ip_ct_ftp_master *ct_ftp_info = &ct->help.ct_ftp_info;
-	struct ip_conntrack_expect *exp;
-	unsigned int i;
-	int found = 0, ends_in_nl;
-	typeof(ip_nat_ftp_hook) ip_nat_ftp;
-
-	/* Until there's been traffic both ways, don't look in packets. */
-	if (ctinfo != IP_CT_ESTABLISHED
-	    && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) {
-		DEBUGP("ftp: Conntrackinfo = %u\n", ctinfo);
-		return NF_ACCEPT;
-	}
-
-	th = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4,
-				sizeof(_tcph), &_tcph);
-	if (th == NULL)
-		return NF_ACCEPT;
-
-	dataoff = (*pskb)->nh.iph->ihl*4 + th->doff*4;
-	/* No data? */
-	if (dataoff >= (*pskb)->len) {
-		DEBUGP("ftp: pskblen = %u\n", (*pskb)->len);
-		return NF_ACCEPT;
-	}
-	datalen = (*pskb)->len - dataoff;
-
-	spin_lock_bh(&ip_ftp_lock);
-	fb_ptr = skb_header_pointer(*pskb, dataoff,
-				    (*pskb)->len - dataoff, ftp_buffer);
-	BUG_ON(fb_ptr == NULL);
-
-	ends_in_nl = (fb_ptr[datalen - 1] == '\n');
-	seq = ntohl(th->seq) + datalen;
-
-	/* Look up to see if we're just after a \n. */
-	if (!find_nl_seq(ntohl(th->seq), ct_ftp_info, dir)) {
-		/* Now if this ends in \n, update ftp info. */
-		DEBUGP("ip_conntrack_ftp_help: wrong seq pos %s(%u) or %s(%u)\n",
-		       ct_ftp_info->seq_aft_nl[0][dir]
-		       old_seq_aft_nl_set ? "":"(UNSET) ", old_seq_aft_nl);
-		ret = NF_ACCEPT;
-		goto out_update_nl;
-	}
-
-	/* Initialize IP array to expected address (it's not mentioned
-	   in EPSV responses) */
-	array[0] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 24) & 0xFF;
-	array[1] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 16) & 0xFF;
-	array[2] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 8) & 0xFF;
-	array[3] = ntohl(ct->tuplehash[dir].tuple.src.ip) & 0xFF;
-
-	for (i = 0; i < ARRAY_SIZE(search[dir]); i++) {
-		found = find_pattern(fb_ptr, (*pskb)->len - dataoff,
-				     search[dir][i].pattern,
-				     search[dir][i].plen,
-				     search[dir][i].skip,
-				     search[dir][i].term,
-				     &matchoff, &matchlen,
-				     array,
-				     search[dir][i].getnum);
-		if (found) break;
-	}
-	if (found == -1) {
-		/* We don't usually drop packets.  After all, this is
-		   connection tracking, not packet filtering.
-		   However, it is necessary for accurate tracking in
-		   this case. */
-		if (net_ratelimit())
-			printk("conntrack_ftp: partial %s %u+%u\n",
-			       search[dir][i].pattern,
-			       ntohl(th->seq), datalen);
-		ret = NF_DROP;
-		goto out;
-	} else if (found == 0) { /* No match */
-		ret = NF_ACCEPT;
-		goto out_update_nl;
-	}
-
-	DEBUGP("conntrack_ftp: match `%s' (%u bytes at %u)\n",
-	       fb_ptr + matchoff, matchlen, ntohl(th->seq) + matchoff);
-
-	/* Allocate expectation which will be inserted */
-	exp = ip_conntrack_expect_alloc(ct);
-	if (exp == NULL) {
-		ret = NF_DROP;
-		goto out;
-	}
-
-	/* We refer to the reverse direction ("!dir") tuples here,
-	 * because we're expecting something in the other direction.
-	 * Doesn't matter unless NAT is happening.  */
-	exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip;
-
-	if (htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3])
-	    != ct->tuplehash[dir].tuple.src.ip) {
-		/* Enrico Scholz's passive FTP to partially RNAT'd ftp
-		   server: it really wants us to connect to a
-		   different IP address.  Simply don't record it for
-		   NAT. */
-		DEBUGP("conntrack_ftp: NOT RECORDING: %u,%u,%u,%u != %u.%u.%u.%u\n",
-		       array[0], array[1], array[2], array[3],
-		       NIPQUAD(ct->tuplehash[dir].tuple.src.ip));
-
-		/* Thanks to Cristiano Lincoln Mattos
-		   <lincoln@cesar.org.br> for reporting this potential
-		   problem (DMZ machines opening holes to internal
-		   networks, or the packet filter itself). */
-		if (!loose) {
-			ret = NF_ACCEPT;
-			goto out_put_expect;
-		}
-		exp->tuple.dst.ip = htonl((array[0] << 24) | (array[1] << 16)
-					 | (array[2] << 8) | array[3]);
-	}
-
-	exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip;
-	exp->tuple.dst.u.tcp.port = htons(array[4] << 8 | array[5]);
-	exp->tuple.src.u.tcp.port = 0; /* Don't care. */
-	exp->tuple.dst.protonum = IPPROTO_TCP;
-	exp->mask = ((struct ip_conntrack_tuple)
-		{ { htonl(0xFFFFFFFF), { 0 } },
-		  { htonl(0xFFFFFFFF), { .tcp = { htons(0xFFFF) } }, 0xFF }});
-
-	exp->expectfn = NULL;
-	exp->flags = 0;
-
-	/* Now, NAT might want to mangle the packet, and register the
-	 * (possibly changed) expectation itself. */
-	ip_nat_ftp = rcu_dereference(ip_nat_ftp_hook);
-	if (ip_nat_ftp)
-		ret = ip_nat_ftp(pskb, ctinfo, search[dir][i].ftptype,
-				 matchoff, matchlen, exp, &seq);
-	else {
-		/* Can't expect this?  Best to drop packet now. */
-		if (ip_conntrack_expect_related(exp) != 0)
-			ret = NF_DROP;
-		else
-			ret = NF_ACCEPT;
-	}
-
-out_put_expect:
-	ip_conntrack_expect_put(exp);
-
-out_update_nl:
-	/* Now if this ends in \n, update ftp info.  Seq may have been
-	 * adjusted by NAT code. */
-	if (ends_in_nl)
-		update_nl_seq(seq, ct_ftp_info,dir, *pskb);
- out:
-	spin_unlock_bh(&ip_ftp_lock);
-	return ret;
-}
-
-static struct ip_conntrack_helper ftp[MAX_PORTS];
-static char ftp_names[MAX_PORTS][sizeof("ftp-65535")];
-
-/* Not __exit: called from init() */
-static void ip_conntrack_ftp_fini(void)
-{
-	int i;
-	for (i = 0; i < ports_c; i++) {
-		DEBUGP("ip_ct_ftp: unregistering helper for port %d\n",
-				ports[i]);
-		ip_conntrack_helper_unregister(&ftp[i]);
-	}
-
-	kfree(ftp_buffer);
-}
-
-static int __init ip_conntrack_ftp_init(void)
-{
-	int i, ret;
-	char *tmpname;
-
-	ftp_buffer = kmalloc(65536, GFP_KERNEL);
-	if (!ftp_buffer)
-		return -ENOMEM;
-
-	if (ports_c == 0)
-		ports[ports_c++] = FTP_PORT;
-
-	for (i = 0; i < ports_c; i++) {
-		ftp[i].tuple.src.u.tcp.port = htons(ports[i]);
-		ftp[i].tuple.dst.protonum = IPPROTO_TCP;
-		ftp[i].mask.src.u.tcp.port = htons(0xFFFF);
-		ftp[i].mask.dst.protonum = 0xFF;
-		ftp[i].max_expected = 1;
-		ftp[i].timeout = 5 * 60; /* 5 minutes */
-		ftp[i].me = THIS_MODULE;
-		ftp[i].help = help;
-
-		tmpname = &ftp_names[i][0];
-		if (ports[i] == FTP_PORT)
-			sprintf(tmpname, "ftp");
-		else
-			sprintf(tmpname, "ftp-%d", ports[i]);
-		ftp[i].name = tmpname;
-
-		DEBUGP("ip_ct_ftp: registering helper for port %d\n",
-				ports[i]);
-		ret = ip_conntrack_helper_register(&ftp[i]);
-
-		if (ret) {
-			ip_conntrack_ftp_fini();
-			return ret;
-		}
-	}
-	return 0;
-}
-
-module_init(ip_conntrack_ftp_init);
-module_exit(ip_conntrack_ftp_fini);
diff --git a/net/ipv4/netfilter/ip_conntrack_helper_h323.c b/net/ipv4/netfilter/ip_conntrack_helper_h323.c
deleted file mode 100644
index 53eb365..0000000
--- a/net/ipv4/netfilter/ip_conntrack_helper_h323.c
+++ /dev/null
@@ -1,1841 +0,0 @@
-/*
- * H.323 connection tracking helper
- *
- * Copyright (c) 2006 Jing Min Zhao <zhaojingmin@users.sourceforge.net>
- *
- * This source code is licensed under General Public License version 2.
- *
- * Based on the 'brute force' H.323 connection tracking module by
- * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
- *
- * For more information, please see http://nath323.sourceforge.net/
- */
-
-#include <linux/module.h>
-#include <linux/netfilter.h>
-#include <linux/ip.h>
-#include <net/tcp.h>
-#include <linux/netfilter_ipv4/ip_conntrack.h>
-#include <linux/netfilter_ipv4/ip_conntrack_core.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
-#include <linux/netfilter_ipv4/ip_conntrack_h323.h>
-#include <linux/moduleparam.h>
-#include <linux/ctype.h>
-#include <linux/inet.h>
-
-#if 0
-#define DEBUGP printk
-#else
-#define DEBUGP(format, args...)
-#endif
-
-/* Parameters */
-static unsigned int default_rrq_ttl = 300;
-module_param(default_rrq_ttl, uint, 0600);
-MODULE_PARM_DESC(default_rrq_ttl, "use this TTL if it's missing in RRQ");
-
-static int gkrouted_only = 1;
-module_param(gkrouted_only, int, 0600);
-MODULE_PARM_DESC(gkrouted_only, "only accept calls from gatekeeper");
-
-static int callforward_filter = 1;
-module_param(callforward_filter, bool, 0600);
-MODULE_PARM_DESC(callforward_filter, "only create call forwarding expectations "
-				     "if both endpoints are on different sides "
-				     "(determined by routing information)");
-
-/* Hooks for NAT */
-int (*set_h245_addr_hook) (struct sk_buff ** pskb,
-			   unsigned char **data, int dataoff,
-			   H245_TransportAddress * addr,
-			   __be32 ip, u_int16_t port);
-int (*set_h225_addr_hook) (struct sk_buff ** pskb,
-			   unsigned char **data, int dataoff,
-			   TransportAddress * addr,
-			   __be32 ip, u_int16_t port);
-int (*set_sig_addr_hook) (struct sk_buff ** pskb,
-			  struct ip_conntrack * ct,
-			  enum ip_conntrack_info ctinfo,
-			  unsigned char **data,
-			  TransportAddress * addr, int count);
-int (*set_ras_addr_hook) (struct sk_buff ** pskb,
-			  struct ip_conntrack * ct,
-			  enum ip_conntrack_info ctinfo,
-			  unsigned char **data,
-			  TransportAddress * addr, int count);
-int (*nat_rtp_rtcp_hook) (struct sk_buff ** pskb,
-			  struct ip_conntrack * ct,
-			  enum ip_conntrack_info ctinfo,
-			  unsigned char **data, int dataoff,
-			  H245_TransportAddress * addr,
-			  u_int16_t port, u_int16_t rtp_port,
-			  struct ip_conntrack_expect * rtp_exp,
-			  struct ip_conntrack_expect * rtcp_exp);
-int (*nat_t120_hook) (struct sk_buff ** pskb,
-		      struct ip_conntrack * ct,
-		      enum ip_conntrack_info ctinfo,
-		      unsigned char **data, int dataoff,
-		      H245_TransportAddress * addr, u_int16_t port,
-		      struct ip_conntrack_expect * exp);
-int (*nat_h245_hook) (struct sk_buff ** pskb,
-		      struct ip_conntrack * ct,
-		      enum ip_conntrack_info ctinfo,
-		      unsigned char **data, int dataoff,
-		      TransportAddress * addr, u_int16_t port,
-		      struct ip_conntrack_expect * exp);
-int (*nat_callforwarding_hook) (struct sk_buff ** pskb,
-				struct ip_conntrack * ct,
-				enum ip_conntrack_info ctinfo,
-				unsigned char **data, int dataoff,
-				TransportAddress * addr, u_int16_t port,
-				struct ip_conntrack_expect * exp);
-int (*nat_q931_hook) (struct sk_buff ** pskb,
-		      struct ip_conntrack * ct,
-		      enum ip_conntrack_info ctinfo,
-		      unsigned char **data, TransportAddress * addr, int idx,
-		      u_int16_t port, struct ip_conntrack_expect * exp);
-
-
-static DEFINE_SPINLOCK(ip_h323_lock);
-static char *h323_buffer;
-
-/****************************************************************************/
-static int get_tpkt_data(struct sk_buff **pskb, struct ip_conntrack *ct,
-			 enum ip_conntrack_info ctinfo,
-			 unsigned char **data, int *datalen, int *dataoff)
-{
-	struct ip_ct_h323_master *info = &ct->help.ct_h323_info;
-	int dir = CTINFO2DIR(ctinfo);
-	struct tcphdr _tcph, *th;
-	int tcpdatalen;
-	int tcpdataoff;
-	unsigned char *tpkt;
-	int tpktlen;
-	int tpktoff;
-
-	/* Get TCP header */
-	th = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl * 4,
-				sizeof(_tcph), &_tcph);
-	if (th == NULL)
-		return 0;
-
-	/* Get TCP data offset */
-	tcpdataoff = (*pskb)->nh.iph->ihl * 4 + th->doff * 4;
-
-	/* Get TCP data length */
-	tcpdatalen = (*pskb)->len - tcpdataoff;
-	if (tcpdatalen <= 0)	/* No TCP data */
-		goto clear_out;
-
-	if (*data == NULL) {	/* first TPKT */
-		/* Get first TPKT pointer */
-		tpkt = skb_header_pointer(*pskb, tcpdataoff, tcpdatalen,
-					  h323_buffer);
-		BUG_ON(tpkt == NULL);
-
-		/* Validate TPKT identifier */
-		if (tcpdatalen < 4 || tpkt[0] != 0x03 || tpkt[1] != 0) {
-			/* Netmeeting sends TPKT header and data separately */
-			if (info->tpkt_len[dir] > 0) {
-				DEBUGP("ip_ct_h323: previous packet "
-				       "indicated separate TPKT data of %hu "
-				       "bytes\n", info->tpkt_len[dir]);
-				if (info->tpkt_len[dir] <= tcpdatalen) {
-					/* Yes, there was a TPKT header
-					 * received */
-					*data = tpkt;
-					*datalen = info->tpkt_len[dir];
-					*dataoff = 0;
-					goto out;
-				}
-
-				/* Fragmented TPKT */
-				if (net_ratelimit())
-					printk("ip_ct_h323: "
-					       "fragmented TPKT\n");
-				goto clear_out;
-			}
-
-			/* It is not even a TPKT */
-			return 0;
-		}
-		tpktoff = 0;
-	} else {		/* Next TPKT */
-		tpktoff = *dataoff + *datalen;
-		tcpdatalen -= tpktoff;
-		if (tcpdatalen <= 4)	/* No more TPKT */
-			goto clear_out;
-		tpkt = *data + *datalen;
-
-		/* Validate TPKT identifier */
-		if (tpkt[0] != 0x03 || tpkt[1] != 0)
-			goto clear_out;
-	}
-
-	/* Validate TPKT length */
-	tpktlen = tpkt[2] * 256 + tpkt[3];
-	if (tpktlen < 4)
-		goto clear_out;
-	if (tpktlen > tcpdatalen) {
-		if (tcpdatalen == 4) {	/* Separate TPKT header */
-			/* Netmeeting sends TPKT header and data separately */
-			DEBUGP("ip_ct_h323: separate TPKT header indicates "
-			       "there will be TPKT data of %hu bytes\n",
-			       tpktlen - 4);
-			info->tpkt_len[dir] = tpktlen - 4;
-			return 0;
-		}
-
-		if (net_ratelimit())
-			printk("ip_ct_h323: incomplete TPKT (fragmented?)\n");
-		goto clear_out;
-	}
-
-	/* This is the encapsulated data */
-	*data = tpkt + 4;
-	*datalen = tpktlen - 4;
-	*dataoff = tpktoff + 4;
-
-      out:
-	/* Clear TPKT length */
-	info->tpkt_len[dir] = 0;
-	return 1;
-
-      clear_out:
-	info->tpkt_len[dir] = 0;
-	return 0;
-}
-
-/****************************************************************************/
-static int get_h245_addr(unsigned char *data, H245_TransportAddress * addr,
-			 __be32 * ip, u_int16_t * port)
-{
-	unsigned char *p;
-
-	if (addr->choice != eH245_TransportAddress_unicastAddress ||
-	    addr->unicastAddress.choice != eUnicastAddress_iPAddress)
-		return 0;
-
-	p = data + addr->unicastAddress.iPAddress.network;
-	*ip = htonl((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]));
-	*port = (p[4] << 8) | (p[5]);
-
-	return 1;
-}
-
-/****************************************************************************/
-static int expect_rtp_rtcp(struct sk_buff **pskb, struct ip_conntrack *ct,
-			   enum ip_conntrack_info ctinfo,
-			   unsigned char **data, int dataoff,
-			   H245_TransportAddress * addr)
-{
-	int dir = CTINFO2DIR(ctinfo);
-	int ret = 0;
-	__be32 ip;
-	u_int16_t port;
-	u_int16_t rtp_port;
-	struct ip_conntrack_expect *rtp_exp;
-	struct ip_conntrack_expect *rtcp_exp;
-	typeof(nat_rtp_rtcp_hook) nat_rtp_rtcp;
-
-	/* Read RTP or RTCP address */
-	if (!get_h245_addr(*data, addr, &ip, &port) ||
-	    ip != ct->tuplehash[dir].tuple.src.ip || port == 0)
-		return 0;
-
-	/* RTP port is even */
-	rtp_port = port & (~1);
-
-	/* Create expect for RTP */
-	if ((rtp_exp = ip_conntrack_expect_alloc(ct)) == NULL)
-		return -1;
-	rtp_exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip;
-	rtp_exp->tuple.src.u.udp.port = 0;
-	rtp_exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip;
-	rtp_exp->tuple.dst.u.udp.port = htons(rtp_port);
-	rtp_exp->tuple.dst.protonum = IPPROTO_UDP;
-	rtp_exp->mask.src.ip = htonl(0xFFFFFFFF);
-	rtp_exp->mask.src.u.udp.port = 0;
-	rtp_exp->mask.dst.ip = htonl(0xFFFFFFFF);
-	rtp_exp->mask.dst.u.udp.port = htons(0xFFFF);
-	rtp_exp->mask.dst.protonum = 0xFF;
-	rtp_exp->flags = 0;
-
-	/* Create expect for RTCP */
-	if ((rtcp_exp = ip_conntrack_expect_alloc(ct)) == NULL) {
-		ip_conntrack_expect_put(rtp_exp);
-		return -1;
-	}
-	rtcp_exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip;
-	rtcp_exp->tuple.src.u.udp.port = 0;
-	rtcp_exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip;
-	rtcp_exp->tuple.dst.u.udp.port = htons(rtp_port + 1);
-	rtcp_exp->tuple.dst.protonum = IPPROTO_UDP;
-	rtcp_exp->mask.src.ip = htonl(0xFFFFFFFF);
-	rtcp_exp->mask.src.u.udp.port = 0;
-	rtcp_exp->mask.dst.ip = htonl(0xFFFFFFFF);
-	rtcp_exp->mask.dst.u.udp.port = htons(0xFFFF);
-	rtcp_exp->mask.dst.protonum = 0xFF;
-	rtcp_exp->flags = 0;
-
-	if (ct->tuplehash[dir].tuple.src.ip !=
-	    ct->tuplehash[!dir].tuple.dst.ip &&
-	    (nat_rtp_rtcp = rcu_dereference(nat_rtp_rtcp_hook))) {
-		/* NAT needed */
-		ret = nat_rtp_rtcp(pskb, ct, ctinfo, data, dataoff,
-				   addr, port, rtp_port, rtp_exp, rtcp_exp);
-	} else {		/* Conntrack only */
-		rtp_exp->expectfn = NULL;
-		rtcp_exp->expectfn = NULL;
-
-		if (ip_conntrack_expect_related(rtp_exp) == 0) {
-			if (ip_conntrack_expect_related(rtcp_exp) == 0) {
-				DEBUGP("ip_ct_h323: expect RTP "
-				       "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
-				       NIPQUAD(rtp_exp->tuple.src.ip),
-				       ntohs(rtp_exp->tuple.src.u.udp.port),
-				       NIPQUAD(rtp_exp->tuple.dst.ip),
-				       ntohs(rtp_exp->tuple.dst.u.udp.port));
-				DEBUGP("ip_ct_h323: expect RTCP "
-				       "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
-				       NIPQUAD(rtcp_exp->tuple.src.ip),
-				       ntohs(rtcp_exp->tuple.src.u.udp.port),
-				       NIPQUAD(rtcp_exp->tuple.dst.ip),
-				       ntohs(rtcp_exp->tuple.dst.u.udp.port));
-			} else {
-				ip_conntrack_unexpect_related(rtp_exp);
-				ret = -1;
-			}
-		} else
-			ret = -1;
-	}
-
-	ip_conntrack_expect_put(rtp_exp);
-	ip_conntrack_expect_put(rtcp_exp);
-
-	return ret;
-}
-
-/****************************************************************************/
-static int expect_t120(struct sk_buff **pskb,
-		       struct ip_conntrack *ct,
-		       enum ip_conntrack_info ctinfo,
-		       unsigned char **data, int dataoff,
-		       H245_TransportAddress * addr)
-{
-	int dir = CTINFO2DIR(ctinfo);
-	int ret = 0;
-	__be32 ip;
-	u_int16_t port;
-	struct ip_conntrack_expect *exp = NULL;
-	typeof(nat_t120_hook) nat_t120;
-
-	/* Read T.120 address */
-	if (!get_h245_addr(*data, addr, &ip, &port) ||
-	    ip != ct->tuplehash[dir].tuple.src.ip || port == 0)
-		return 0;
-
-	/* Create expect for T.120 connections */
-	if ((exp = ip_conntrack_expect_alloc(ct)) == NULL)
-		return -1;
-	exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip;
-	exp->tuple.src.u.tcp.port = 0;
-	exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip;
-	exp->tuple.dst.u.tcp.port = htons(port);
-	exp->tuple.dst.protonum = IPPROTO_TCP;
-	exp->mask.src.ip = htonl(0xFFFFFFFF);
-	exp->mask.src.u.tcp.port = 0;
-	exp->mask.dst.ip = htonl(0xFFFFFFFF);
-	exp->mask.dst.u.tcp.port = htons(0xFFFF);
-	exp->mask.dst.protonum = 0xFF;
-	exp->flags = IP_CT_EXPECT_PERMANENT;	/* Accept multiple channels */
-
-	if (ct->tuplehash[dir].tuple.src.ip !=
-	    ct->tuplehash[!dir].tuple.dst.ip &&
-	    (nat_t120 = rcu_dereference(nat_t120_hook))) {
-		/* NAT needed */
-		ret = nat_t120(pskb, ct, ctinfo, data, dataoff, addr,
-			       port, exp);
-	} else {		/* Conntrack only */
-		exp->expectfn = NULL;
-		if (ip_conntrack_expect_related(exp) == 0) {
-			DEBUGP("ip_ct_h323: expect T.120 "
-			       "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
-			       NIPQUAD(exp->tuple.src.ip),
-			       ntohs(exp->tuple.src.u.tcp.port),
-			       NIPQUAD(exp->tuple.dst.ip),
-			       ntohs(exp->tuple.dst.u.tcp.port));
-		} else
-			ret = -1;
-	}
-
-	ip_conntrack_expect_put(exp);
-
-	return ret;
-}
-
-/****************************************************************************/
-static int process_h245_channel(struct sk_buff **pskb,
-				struct ip_conntrack *ct,
-				enum ip_conntrack_info ctinfo,
-				unsigned char **data, int dataoff,
-				H2250LogicalChannelParameters * channel)
-{
-	int ret;
-
-	if (channel->options & eH2250LogicalChannelParameters_mediaChannel) {
-		/* RTP */
-		ret = expect_rtp_rtcp(pskb, ct, ctinfo, data, dataoff,
-				      &channel->mediaChannel);
-		if (ret < 0)
-			return -1;
-	}
-
-	if (channel->
-	    options & eH2250LogicalChannelParameters_mediaControlChannel) {
-		/* RTCP */
-		ret = expect_rtp_rtcp(pskb, ct, ctinfo, data, dataoff,
-				      &channel->mediaControlChannel);
-		if (ret < 0)
-			return -1;
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-static int process_olc(struct sk_buff **pskb, struct ip_conntrack *ct,
-		       enum ip_conntrack_info ctinfo,
-		       unsigned char **data, int dataoff,
-		       OpenLogicalChannel * olc)
-{
-	int ret;
-
-	DEBUGP("ip_ct_h323: OpenLogicalChannel\n");
-
-	if (olc->forwardLogicalChannelParameters.multiplexParameters.choice ==
-	    eOpenLogicalChannel_forwardLogicalChannelParameters_multiplexParameters_h2250LogicalChannelParameters)
-	{
-		ret = process_h245_channel(pskb, ct, ctinfo, data, dataoff,
-					   &olc->
-					   forwardLogicalChannelParameters.
-					   multiplexParameters.
-					   h2250LogicalChannelParameters);
-		if (ret < 0)
-			return -1;
-	}
-
-	if ((olc->options &
-	     eOpenLogicalChannel_reverseLogicalChannelParameters) &&
-	    (olc->reverseLogicalChannelParameters.options &
-	     eOpenLogicalChannel_reverseLogicalChannelParameters_multiplexParameters)
-	    && (olc->reverseLogicalChannelParameters.multiplexParameters.
-		choice ==
-		eOpenLogicalChannel_reverseLogicalChannelParameters_multiplexParameters_h2250LogicalChannelParameters))
-	{
-		ret =
-		    process_h245_channel(pskb, ct, ctinfo, data, dataoff,
-					 &olc->
-					 reverseLogicalChannelParameters.
-					 multiplexParameters.
-					 h2250LogicalChannelParameters);
-		if (ret < 0)
-			return -1;
-	}
-
-	if ((olc->options & eOpenLogicalChannel_separateStack) &&
-	    olc->forwardLogicalChannelParameters.dataType.choice ==
-	    eDataType_data &&
-	    olc->forwardLogicalChannelParameters.dataType.data.application.
-	    choice == eDataApplicationCapability_application_t120 &&
-	    olc->forwardLogicalChannelParameters.dataType.data.application.
-	    t120.choice == eDataProtocolCapability_separateLANStack &&
-	    olc->separateStack.networkAddress.choice ==
-	    eNetworkAccessParameters_networkAddress_localAreaAddress) {
-		ret = expect_t120(pskb, ct, ctinfo, data, dataoff,
-				  &olc->separateStack.networkAddress.
-				  localAreaAddress);
-		if (ret < 0)
-			return -1;
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-static int process_olca(struct sk_buff **pskb, struct ip_conntrack *ct,
-			enum ip_conntrack_info ctinfo,
-			unsigned char **data, int dataoff,
-			OpenLogicalChannelAck * olca)
-{
-	H2250LogicalChannelAckParameters *ack;
-	int ret;
-
-	DEBUGP("ip_ct_h323: OpenLogicalChannelAck\n");
-
-	if ((olca->options &
-	     eOpenLogicalChannelAck_reverseLogicalChannelParameters) &&
-	    (olca->reverseLogicalChannelParameters.options &
-	     eOpenLogicalChannelAck_reverseLogicalChannelParameters_multiplexParameters)
-	    && (olca->reverseLogicalChannelParameters.multiplexParameters.
-		choice ==
-		eOpenLogicalChannelAck_reverseLogicalChannelParameters_multiplexParameters_h2250LogicalChannelParameters))
-	{
-		ret = process_h245_channel(pskb, ct, ctinfo, data, dataoff,
-					   &olca->
-					   reverseLogicalChannelParameters.
-					   multiplexParameters.
-					   h2250LogicalChannelParameters);
-		if (ret < 0)
-			return -1;
-	}
-
-	if ((olca->options &
-	     eOpenLogicalChannelAck_forwardMultiplexAckParameters) &&
-	    (olca->forwardMultiplexAckParameters.choice ==
-	     eOpenLogicalChannelAck_forwardMultiplexAckParameters_h2250LogicalChannelAckParameters))
-	{
-		ack = &olca->forwardMultiplexAckParameters.
-		    h2250LogicalChannelAckParameters;
-		if (ack->options &
-		    eH2250LogicalChannelAckParameters_mediaChannel) {
-			/* RTP */
-			ret = expect_rtp_rtcp(pskb, ct, ctinfo, data, dataoff,
-					      &ack->mediaChannel);
-			if (ret < 0)
-				return -1;
-		}
-
-		if (ack->options &
-		    eH2250LogicalChannelAckParameters_mediaControlChannel) {
-			/* RTCP */
-			ret = expect_rtp_rtcp(pskb, ct, ctinfo, data, dataoff,
-					      &ack->mediaControlChannel);
-			if (ret < 0)
-				return -1;
-		}
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-static int process_h245(struct sk_buff **pskb, struct ip_conntrack *ct,
-			enum ip_conntrack_info ctinfo,
-			unsigned char **data, int dataoff,
-			MultimediaSystemControlMessage * mscm)
-{
-	switch (mscm->choice) {
-	case eMultimediaSystemControlMessage_request:
-		if (mscm->request.choice ==
-		    eRequestMessage_openLogicalChannel) {
-			return process_olc(pskb, ct, ctinfo, data, dataoff,
-					   &mscm->request.openLogicalChannel);
-		}
-		DEBUGP("ip_ct_h323: H.245 Request %d\n",
-		       mscm->request.choice);
-		break;
-	case eMultimediaSystemControlMessage_response:
-		if (mscm->response.choice ==
-		    eResponseMessage_openLogicalChannelAck) {
-			return process_olca(pskb, ct, ctinfo, data, dataoff,
-					    &mscm->response.
-					    openLogicalChannelAck);
-		}
-		DEBUGP("ip_ct_h323: H.245 Response %d\n",
-		       mscm->response.choice);
-		break;
-	default:
-		DEBUGP("ip_ct_h323: H.245 signal %d\n", mscm->choice);
-		break;
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-static int h245_help(struct sk_buff **pskb, struct ip_conntrack *ct,
-		     enum ip_conntrack_info ctinfo)
-{
-	static MultimediaSystemControlMessage mscm;
-	unsigned char *data = NULL;
-	int datalen;
-	int dataoff;
-	int ret;
-
-	/* Until there's been traffic both ways, don't look in packets. */
-	if (ctinfo != IP_CT_ESTABLISHED
-	    && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
-		return NF_ACCEPT;
-	}
-	DEBUGP("ip_ct_h245: skblen = %u\n", (*pskb)->len);
-
-	spin_lock_bh(&ip_h323_lock);
-
-	/* Process each TPKT */
-	while (get_tpkt_data(pskb, ct, ctinfo, &data, &datalen, &dataoff)) {
-		DEBUGP("ip_ct_h245: TPKT %u.%u.%u.%u->%u.%u.%u.%u, len=%d\n",
-		       NIPQUAD((*pskb)->nh.iph->saddr),
-		       NIPQUAD((*pskb)->nh.iph->daddr), datalen);
-
-		/* Decode H.245 signal */
-		ret = DecodeMultimediaSystemControlMessage(data, datalen,
-							   &mscm);
-		if (ret < 0) {
-			if (net_ratelimit())
-				printk("ip_ct_h245: decoding error: %s\n",
-				       ret == H323_ERROR_BOUND ?
-				       "out of bound" : "out of range");
-			/* We don't drop when decoding error */
-			break;
-		}
-
-		/* Process H.245 signal */
-		if (process_h245(pskb, ct, ctinfo, &data, dataoff, &mscm) < 0)
-			goto drop;
-	}
-
-	spin_unlock_bh(&ip_h323_lock);
-	return NF_ACCEPT;
-
-      drop:
-	spin_unlock_bh(&ip_h323_lock);
-	if (net_ratelimit())
-		printk("ip_ct_h245: packet dropped\n");
-	return NF_DROP;
-}
-
-/****************************************************************************/
-static struct ip_conntrack_helper ip_conntrack_helper_h245 = {
-	.name = "H.245",
-	.me = THIS_MODULE,
-	.max_expected = H323_RTP_CHANNEL_MAX * 4 + 2 /* T.120 */ ,
-	.timeout = 240,
-	.tuple = {.dst = {.protonum = IPPROTO_TCP}},
-	.mask = {.src = {.u = {0xFFFF}},
-		 .dst = {.protonum = 0xFF}},
-	.help = h245_help
-};
-
-/****************************************************************************/
-void ip_conntrack_h245_expect(struct ip_conntrack *new,
-			      struct ip_conntrack_expect *this)
-{
-	write_lock_bh(&ip_conntrack_lock);
-	new->helper = &ip_conntrack_helper_h245;
-	write_unlock_bh(&ip_conntrack_lock);
-}
-
-/****************************************************************************/
-int get_h225_addr(unsigned char *data, TransportAddress * addr,
-		  __be32 * ip, u_int16_t * port)
-{
-	unsigned char *p;
-
-	if (addr->choice != eTransportAddress_ipAddress)
-		return 0;
-
-	p = data + addr->ipAddress.ip;
-	*ip = htonl((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]));
-	*port = (p[4] << 8) | (p[5]);
-
-	return 1;
-}
-
-/****************************************************************************/
-static int expect_h245(struct sk_buff **pskb, struct ip_conntrack *ct,
-		       enum ip_conntrack_info ctinfo,
-		       unsigned char **data, int dataoff,
-		       TransportAddress * addr)
-{
-	int dir = CTINFO2DIR(ctinfo);
-	int ret = 0;
-	__be32 ip;
-	u_int16_t port;
-	struct ip_conntrack_expect *exp = NULL;
-	typeof(nat_h245_hook) nat_h245;
-
-	/* Read h245Address */
-	if (!get_h225_addr(*data, addr, &ip, &port) ||
-	    ip != ct->tuplehash[dir].tuple.src.ip || port == 0)
-		return 0;
-
-	/* Create expect for h245 connection */
-	if ((exp = ip_conntrack_expect_alloc(ct)) == NULL)
-		return -1;
-	exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip;
-	exp->tuple.src.u.tcp.port = 0;
-	exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip;
-	exp->tuple.dst.u.tcp.port = htons(port);
-	exp->tuple.dst.protonum = IPPROTO_TCP;
-	exp->mask.src.ip = htonl(0xFFFFFFFF);
-	exp->mask.src.u.tcp.port = 0;
-	exp->mask.dst.ip = htonl(0xFFFFFFFF);
-	exp->mask.dst.u.tcp.port = htons(0xFFFF);
-	exp->mask.dst.protonum = 0xFF;
-	exp->flags = 0;
-
-	if (ct->tuplehash[dir].tuple.src.ip !=
-	    ct->tuplehash[!dir].tuple.dst.ip &&
-	    (nat_h245 = rcu_dereference(nat_h245_hook))) {
-		/* NAT needed */
-		ret = nat_h245(pskb, ct, ctinfo, data, dataoff, addr,
-			       port, exp);
-	} else {		/* Conntrack only */
-		exp->expectfn = ip_conntrack_h245_expect;
-
-		if (ip_conntrack_expect_related(exp) == 0) {
-			DEBUGP("ip_ct_q931: expect H.245 "
-			       "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
-			       NIPQUAD(exp->tuple.src.ip),
-			       ntohs(exp->tuple.src.u.tcp.port),
-			       NIPQUAD(exp->tuple.dst.ip),
-			       ntohs(exp->tuple.dst.u.tcp.port));
-		} else
-			ret = -1;
-	}
-
-	ip_conntrack_expect_put(exp);
-
-	return ret;
-}
-
-/* Forwarding declaration */
-void ip_conntrack_q931_expect(struct ip_conntrack *new,
-			      struct ip_conntrack_expect *this);
-
-/****************************************************************************/
-static int expect_callforwarding(struct sk_buff **pskb,
-				 struct ip_conntrack *ct,
-				 enum ip_conntrack_info ctinfo,
-				 unsigned char **data, int dataoff,
-				 TransportAddress * addr)
-{
-	int dir = CTINFO2DIR(ctinfo);
-	int ret = 0;
-	__be32 ip;
-	u_int16_t port;
-	struct ip_conntrack_expect *exp = NULL;
-	typeof(nat_callforwarding_hook) nat_callforwarding;
-
-	/* Read alternativeAddress */
-	if (!get_h225_addr(*data, addr, &ip, &port) || port == 0)
-		return 0;
-
-	/* If the calling party is on the same side of the forward-to party,
-	 * we don't need to track the second call */
-	if (callforward_filter) {
-		struct rtable *rt1, *rt2;
-		struct flowi fl1 = {
-			.fl4_dst = ip,
-		};
-		struct flowi fl2 = {
-			.fl4_dst = ct->tuplehash[!dir].tuple.src.ip,
-		};
-
-		if (ip_route_output_key(&rt1, &fl1) == 0) {
-			if (ip_route_output_key(&rt2, &fl2) == 0) {
-				if (rt1->rt_gateway == rt2->rt_gateway &&
-				    rt1->u.dst.dev  == rt2->u.dst.dev)
-					ret = 1;
-				dst_release(&rt2->u.dst);
-			}
-			dst_release(&rt1->u.dst);
-		}
-		if (ret) {
-			DEBUGP("ip_ct_q931: Call Forwarding not tracked\n");
-			return 0;
-		}
-	}
-
-	/* Create expect for the second call leg */
-	if ((exp = ip_conntrack_expect_alloc(ct)) == NULL)
-		return -1;
-	exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip;
-	exp->tuple.src.u.tcp.port = 0;
-	exp->tuple.dst.ip = ip;
-	exp->tuple.dst.u.tcp.port = htons(port);
-	exp->tuple.dst.protonum = IPPROTO_TCP;
-	exp->mask.src.ip = htonl(0xFFFFFFFF);
-	exp->mask.src.u.tcp.port = 0;
-	exp->mask.dst.ip = htonl(0xFFFFFFFF);
-	exp->mask.dst.u.tcp.port = htons(0xFFFF);
-	exp->mask.dst.protonum = 0xFF;
-	exp->flags = 0;
-
-	if (ct->tuplehash[dir].tuple.src.ip !=
-	    ct->tuplehash[!dir].tuple.dst.ip &&
-	    (nat_callforwarding = rcu_dereference(nat_callforwarding_hook))) {
-		/* Need NAT */
-		ret = nat_callforwarding(pskb, ct, ctinfo, data, dataoff,
-					 addr, port, exp);
-	} else {		/* Conntrack only */
-		exp->expectfn = ip_conntrack_q931_expect;
-
-		if (ip_conntrack_expect_related(exp) == 0) {
-			DEBUGP("ip_ct_q931: expect Call Forwarding "
-			       "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
-			       NIPQUAD(exp->tuple.src.ip),
-			       ntohs(exp->tuple.src.u.tcp.port),
-			       NIPQUAD(exp->tuple.dst.ip),
-			       ntohs(exp->tuple.dst.u.tcp.port));
-		} else
-			ret = -1;
-	}
-
-	ip_conntrack_expect_put(exp);
-
-	return ret;
-}
-
-/****************************************************************************/
-static int process_setup(struct sk_buff **pskb, struct ip_conntrack *ct,
-			 enum ip_conntrack_info ctinfo,
-			 unsigned char **data, int dataoff,
-			 Setup_UUIE * setup)
-{
-	int dir = CTINFO2DIR(ctinfo);
-	int ret;
-	int i;
-	__be32 ip;
-	u_int16_t port;
-	typeof(set_h225_addr_hook) set_h225_addr;
-
-	DEBUGP("ip_ct_q931: Setup\n");
-
-	if (setup->options & eSetup_UUIE_h245Address) {
-		ret = expect_h245(pskb, ct, ctinfo, data, dataoff,
-				  &setup->h245Address);
-		if (ret < 0)
-			return -1;
-	}
-
-	set_h225_addr = rcu_dereference(set_h225_addr_hook);
-
-	if ((setup->options & eSetup_UUIE_destCallSignalAddress) &&
-	    (set_h225_addr) &&
-	    get_h225_addr(*data, &setup->destCallSignalAddress, &ip, &port) &&
-	    ip != ct->tuplehash[!dir].tuple.src.ip) {
-		DEBUGP("ip_ct_q931: set destCallSignalAddress "
-		       "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
-		       NIPQUAD(ip), port,
-		       NIPQUAD(ct->tuplehash[!dir].tuple.src.ip),
-		       ntohs(ct->tuplehash[!dir].tuple.src.u.tcp.port));
-		ret = set_h225_addr(pskb, data, dataoff,
-				    &setup->destCallSignalAddress,
-				    ct->tuplehash[!dir].tuple.src.ip,
-				    ntohs(ct->tuplehash[!dir].tuple.src.
-					  u.tcp.port));
-		if (ret < 0)
-			return -1;
-	}
-
-	if ((setup->options & eSetup_UUIE_sourceCallSignalAddress) &&
-	    (set_h225_addr) &&
-	    get_h225_addr(*data, &setup->sourceCallSignalAddress, &ip, &port)
-	    && ip != ct->tuplehash[!dir].tuple.dst.ip) {
-		DEBUGP("ip_ct_q931: set sourceCallSignalAddress "
-		       "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
-		       NIPQUAD(ip), port,
-		       NIPQUAD(ct->tuplehash[!dir].tuple.dst.ip),
-		       ntohs(ct->tuplehash[!dir].tuple.dst.u.tcp.port));
-		ret = set_h225_addr(pskb, data, dataoff,
-				    &setup->sourceCallSignalAddress,
-				    ct->tuplehash[!dir].tuple.dst.ip,
-				    ntohs(ct->tuplehash[!dir].tuple.dst.
-					  u.tcp.port));
-		if (ret < 0)
-			return -1;
-	}
-
-	if (setup->options & eSetup_UUIE_fastStart) {
-		for (i = 0; i < setup->fastStart.count; i++) {
-			ret = process_olc(pskb, ct, ctinfo, data, dataoff,
-					  &setup->fastStart.item[i]);
-			if (ret < 0)
-				return -1;
-		}
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-static int process_callproceeding(struct sk_buff **pskb,
-				  struct ip_conntrack *ct,
-				  enum ip_conntrack_info ctinfo,
-				  unsigned char **data, int dataoff,
-				  CallProceeding_UUIE * callproc)
-{
-	int ret;
-	int i;
-
-	DEBUGP("ip_ct_q931: CallProceeding\n");
-
-	if (callproc->options & eCallProceeding_UUIE_h245Address) {
-		ret = expect_h245(pskb, ct, ctinfo, data, dataoff,
-				  &callproc->h245Address);
-		if (ret < 0)
-			return -1;
-	}
-
-	if (callproc->options & eCallProceeding_UUIE_fastStart) {
-		for (i = 0; i < callproc->fastStart.count; i++) {
-			ret = process_olc(pskb, ct, ctinfo, data, dataoff,
-					  &callproc->fastStart.item[i]);
-			if (ret < 0)
-				return -1;
-		}
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-static int process_connect(struct sk_buff **pskb, struct ip_conntrack *ct,
-			   enum ip_conntrack_info ctinfo,
-			   unsigned char **data, int dataoff,
-			   Connect_UUIE * connect)
-{
-	int ret;
-	int i;
-
-	DEBUGP("ip_ct_q931: Connect\n");
-
-	if (connect->options & eConnect_UUIE_h245Address) {
-		ret = expect_h245(pskb, ct, ctinfo, data, dataoff,
-				  &connect->h245Address);
-		if (ret < 0)
-			return -1;
-	}
-
-	if (connect->options & eConnect_UUIE_fastStart) {
-		for (i = 0; i < connect->fastStart.count; i++) {
-			ret = process_olc(pskb, ct, ctinfo, data, dataoff,
-					  &connect->fastStart.item[i]);
-			if (ret < 0)
-				return -1;
-		}
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-static int process_alerting(struct sk_buff **pskb, struct ip_conntrack *ct,
-			    enum ip_conntrack_info ctinfo,
-			    unsigned char **data, int dataoff,
-			    Alerting_UUIE * alert)
-{
-	int ret;
-	int i;
-
-	DEBUGP("ip_ct_q931: Alerting\n");
-
-	if (alert->options & eAlerting_UUIE_h245Address) {
-		ret = expect_h245(pskb, ct, ctinfo, data, dataoff,
-				  &alert->h245Address);
-		if (ret < 0)
-			return -1;
-	}
-
-	if (alert->options & eAlerting_UUIE_fastStart) {
-		for (i = 0; i < alert->fastStart.count; i++) {
-			ret = process_olc(pskb, ct, ctinfo, data, dataoff,
-					  &alert->fastStart.item[i]);
-			if (ret < 0)
-				return -1;
-		}
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-static int process_information(struct sk_buff **pskb,
-			       struct ip_conntrack *ct,
-			       enum ip_conntrack_info ctinfo,
-			       unsigned char **data, int dataoff,
-			       Information_UUIE * info)
-{
-	int ret;
-	int i;
-
-	DEBUGP("ip_ct_q931: Information\n");
-
-	if (info->options & eInformation_UUIE_fastStart) {
-		for (i = 0; i < info->fastStart.count; i++) {
-			ret = process_olc(pskb, ct, ctinfo, data, dataoff,
-					  &info->fastStart.item[i]);
-			if (ret < 0)
-				return -1;
-		}
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-static int process_facility(struct sk_buff **pskb, struct ip_conntrack *ct,
-			    enum ip_conntrack_info ctinfo,
-			    unsigned char **data, int dataoff,
-			    Facility_UUIE * facility)
-{
-	int ret;
-	int i;
-
-	DEBUGP("ip_ct_q931: Facility\n");
-
-	if (facility->reason.choice == eFacilityReason_callForwarded) {
-		if (facility->options & eFacility_UUIE_alternativeAddress)
-			return expect_callforwarding(pskb, ct, ctinfo, data,
-						     dataoff,
-						     &facility->
-						     alternativeAddress);
-		return 0;
-	}
-
-	if (facility->options & eFacility_UUIE_h245Address) {
-		ret = expect_h245(pskb, ct, ctinfo, data, dataoff,
-				  &facility->h245Address);
-		if (ret < 0)
-			return -1;
-	}
-
-	if (facility->options & eFacility_UUIE_fastStart) {
-		for (i = 0; i < facility->fastStart.count; i++) {
-			ret = process_olc(pskb, ct, ctinfo, data, dataoff,
-					  &facility->fastStart.item[i]);
-			if (ret < 0)
-				return -1;
-		}
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-static int process_progress(struct sk_buff **pskb, struct ip_conntrack *ct,
-			    enum ip_conntrack_info ctinfo,
-			    unsigned char **data, int dataoff,
-			    Progress_UUIE * progress)
-{
-	int ret;
-	int i;
-
-	DEBUGP("ip_ct_q931: Progress\n");
-
-	if (progress->options & eProgress_UUIE_h245Address) {
-		ret = expect_h245(pskb, ct, ctinfo, data, dataoff,
-				  &progress->h245Address);
-		if (ret < 0)
-			return -1;
-	}
-
-	if (progress->options & eProgress_UUIE_fastStart) {
-		for (i = 0; i < progress->fastStart.count; i++) {
-			ret = process_olc(pskb, ct, ctinfo, data, dataoff,
-					  &progress->fastStart.item[i]);
-			if (ret < 0)
-				return -1;
-		}
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-static int process_q931(struct sk_buff **pskb, struct ip_conntrack *ct,
-			enum ip_conntrack_info ctinfo,
-			unsigned char **data, int dataoff, Q931 * q931)
-{
-	H323_UU_PDU *pdu = &q931->UUIE.h323_uu_pdu;
-	int i;
-	int ret = 0;
-
-	switch (pdu->h323_message_body.choice) {
-	case eH323_UU_PDU_h323_message_body_setup:
-		ret = process_setup(pskb, ct, ctinfo, data, dataoff,
-				    &pdu->h323_message_body.setup);
-		break;
-	case eH323_UU_PDU_h323_message_body_callProceeding:
-		ret = process_callproceeding(pskb, ct, ctinfo, data, dataoff,
-					     &pdu->h323_message_body.
-					     callProceeding);
-		break;
-	case eH323_UU_PDU_h323_message_body_connect:
-		ret = process_connect(pskb, ct, ctinfo, data, dataoff,
-				      &pdu->h323_message_body.connect);
-		break;
-	case eH323_UU_PDU_h323_message_body_alerting:
-		ret = process_alerting(pskb, ct, ctinfo, data, dataoff,
-				       &pdu->h323_message_body.alerting);
-		break;
-	case eH323_UU_PDU_h323_message_body_information:
-		ret = process_information(pskb, ct, ctinfo, data, dataoff,
-					  &pdu->h323_message_body.
-					  information);
-		break;
-	case eH323_UU_PDU_h323_message_body_facility:
-		ret = process_facility(pskb, ct, ctinfo, data, dataoff,
-				       &pdu->h323_message_body.facility);
-		break;
-	case eH323_UU_PDU_h323_message_body_progress:
-		ret = process_progress(pskb, ct, ctinfo, data, dataoff,
-				       &pdu->h323_message_body.progress);
-		break;
-	default:
-		DEBUGP("ip_ct_q931: Q.931 signal %d\n",
-		       pdu->h323_message_body.choice);
-		break;
-	}
-
-	if (ret < 0)
-		return -1;
-
-	if (pdu->options & eH323_UU_PDU_h245Control) {
-		for (i = 0; i < pdu->h245Control.count; i++) {
-			ret = process_h245(pskb, ct, ctinfo, data, dataoff,
-					   &pdu->h245Control.item[i]);
-			if (ret < 0)
-				return -1;
-		}
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-static int q931_help(struct sk_buff **pskb, struct ip_conntrack *ct,
-		     enum ip_conntrack_info ctinfo)
-{
-	static Q931 q931;
-	unsigned char *data = NULL;
-	int datalen;
-	int dataoff;
-	int ret;
-
-	/* Until there's been traffic both ways, don't look in packets. */
-	if (ctinfo != IP_CT_ESTABLISHED
-	    && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
-		return NF_ACCEPT;
-	}
-	DEBUGP("ip_ct_q931: skblen = %u\n", (*pskb)->len);
-
-	spin_lock_bh(&ip_h323_lock);
-
-	/* Process each TPKT */
-	while (get_tpkt_data(pskb, ct, ctinfo, &data, &datalen, &dataoff)) {
-		DEBUGP("ip_ct_q931: TPKT %u.%u.%u.%u->%u.%u.%u.%u, len=%d\n",
-		       NIPQUAD((*pskb)->nh.iph->saddr),
-		       NIPQUAD((*pskb)->nh.iph->daddr), datalen);
-
-		/* Decode Q.931 signal */
-		ret = DecodeQ931(data, datalen, &q931);
-		if (ret < 0) {
-			if (net_ratelimit())
-				printk("ip_ct_q931: decoding error: %s\n",
-				       ret == H323_ERROR_BOUND ?
-				       "out of bound" : "out of range");
-			/* We don't drop when decoding error */
-			break;
-		}
-
-		/* Process Q.931 signal */
-		if (process_q931(pskb, ct, ctinfo, &data, dataoff, &q931) < 0)
-			goto drop;
-	}
-
-	spin_unlock_bh(&ip_h323_lock);
-	return NF_ACCEPT;
-
-      drop:
-	spin_unlock_bh(&ip_h323_lock);
-	if (net_ratelimit())
-		printk("ip_ct_q931: packet dropped\n");
-	return NF_DROP;
-}
-
-/****************************************************************************/
-static struct ip_conntrack_helper ip_conntrack_helper_q931 = {
-	.name = "Q.931",
-	.me = THIS_MODULE,
-	.max_expected = H323_RTP_CHANNEL_MAX * 4 + 4 /* T.120 and H.245 */ ,
-	.timeout = 240,
-	.tuple = {.src = {.u = {.tcp = {.port = __constant_htons(Q931_PORT)}}},
-		  .dst = {.protonum = IPPROTO_TCP}},
-	.mask = {.src = {.u = {0xFFFF}},
-		 .dst = {.protonum = 0xFF}},
-	.help = q931_help
-};
-
-/****************************************************************************/
-void ip_conntrack_q931_expect(struct ip_conntrack *new,
-			      struct ip_conntrack_expect *this)
-{
-	write_lock_bh(&ip_conntrack_lock);
-	new->helper = &ip_conntrack_helper_q931;
-	write_unlock_bh(&ip_conntrack_lock);
-}
-
-/****************************************************************************/
-static unsigned char *get_udp_data(struct sk_buff **pskb, int *datalen)
-{
-	struct udphdr _uh, *uh;
-	int dataoff;
-
-	uh = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl * 4, sizeof(_uh),
-				&_uh);
-	if (uh == NULL)
-		return NULL;
-	dataoff = (*pskb)->nh.iph->ihl * 4 + sizeof(_uh);
-	if (dataoff >= (*pskb)->len)
-		return NULL;
-	*datalen = (*pskb)->len - dataoff;
-	return skb_header_pointer(*pskb, dataoff, *datalen, h323_buffer);
-}
-
-/****************************************************************************/
-static struct ip_conntrack_expect *find_expect(struct ip_conntrack *ct,
-					       __be32 ip, u_int16_t port)
-{
-	struct ip_conntrack_expect *exp;
-	struct ip_conntrack_tuple tuple;
-
-	tuple.src.ip = 0;
-	tuple.src.u.tcp.port = 0;
-	tuple.dst.ip = ip;
-	tuple.dst.u.tcp.port = htons(port);
-	tuple.dst.protonum = IPPROTO_TCP;
-
-	exp = __ip_conntrack_expect_find(&tuple);
-	if (exp && exp->master == ct)
-		return exp;
-	return NULL;
-}
-
-/****************************************************************************/
-static int set_expect_timeout(struct ip_conntrack_expect *exp,
-			      unsigned timeout)
-{
-	if (!exp || !del_timer(&exp->timeout))
-		return 0;
-
-	exp->timeout.expires = jiffies + timeout * HZ;
-	add_timer(&exp->timeout);
-
-	return 1;
-}
-
-/****************************************************************************/
-static int expect_q931(struct sk_buff **pskb, struct ip_conntrack *ct,
-		       enum ip_conntrack_info ctinfo,
-		       unsigned char **data,
-		       TransportAddress * addr, int count)
-{
-	struct ip_ct_h323_master *info = &ct->help.ct_h323_info;
-	int dir = CTINFO2DIR(ctinfo);
-	int ret = 0;
-	int i;
-	__be32 ip;
-	u_int16_t port;
-	struct ip_conntrack_expect *exp;
-	typeof(nat_q931_hook) nat_q931;
-
-	/* Look for the first related address */
-	for (i = 0; i < count; i++) {
-		if (get_h225_addr(*data, &addr[i], &ip, &port) &&
-		    ip == ct->tuplehash[dir].tuple.src.ip && port != 0)
-			break;
-	}
-
-	if (i >= count)		/* Not found */
-		return 0;
-
-	/* Create expect for Q.931 */
-	if ((exp = ip_conntrack_expect_alloc(ct)) == NULL)
-		return -1;
-	exp->tuple.src.ip = gkrouted_only ?	/* only accept calls from GK? */
-	    ct->tuplehash[!dir].tuple.src.ip : 0;
-	exp->tuple.src.u.tcp.port = 0;
-	exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip;
-	exp->tuple.dst.u.tcp.port = htons(port);
-	exp->tuple.dst.protonum = IPPROTO_TCP;
-	exp->mask.src.ip = gkrouted_only ? htonl(0xFFFFFFFF) : 0;
-	exp->mask.src.u.tcp.port = 0;
-	exp->mask.dst.ip = htonl(0xFFFFFFFF);
-	exp->mask.dst.u.tcp.port = htons(0xFFFF);
-	exp->mask.dst.protonum = 0xFF;
-	exp->flags = IP_CT_EXPECT_PERMANENT;	/* Accept multiple calls */
-
-	nat_q931 = rcu_dereference(nat_q931_hook);
-	if (nat_q931) {	/* Need NAT */
-		ret = nat_q931(pskb, ct, ctinfo, data, addr, i, port, exp);
-	} else {		/* Conntrack only */
-		exp->expectfn = ip_conntrack_q931_expect;
-
-		if (ip_conntrack_expect_related(exp) == 0) {
-			DEBUGP("ip_ct_ras: expect Q.931 "
-			       "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
-			       NIPQUAD(exp->tuple.src.ip),
-			       ntohs(exp->tuple.src.u.tcp.port),
-			       NIPQUAD(exp->tuple.dst.ip),
-			       ntohs(exp->tuple.dst.u.tcp.port));
-
-			/* Save port for looking up expect in processing RCF */
-			info->sig_port[dir] = port;
-		} else
-			ret = -1;
-	}
-
-	ip_conntrack_expect_put(exp);
-
-	return ret;
-}
-
-/****************************************************************************/
-static int process_grq(struct sk_buff **pskb, struct ip_conntrack *ct,
-		       enum ip_conntrack_info ctinfo,
-		       unsigned char **data, GatekeeperRequest * grq)
-{
-	typeof(set_ras_addr_hook) set_ras_addr;
-
-	DEBUGP("ip_ct_ras: GRQ\n");
-
-	set_ras_addr = rcu_dereference(set_ras_addr_hook);
-	if (set_ras_addr)	/* NATed */
-		return set_ras_addr(pskb, ct, ctinfo, data,
-				    &grq->rasAddress, 1);
-	return 0;
-}
-
-/* Declare before using */
-static void ip_conntrack_ras_expect(struct ip_conntrack *new,
-				    struct ip_conntrack_expect *this);
-
-/****************************************************************************/
-static int process_gcf(struct sk_buff **pskb, struct ip_conntrack *ct,
-		       enum ip_conntrack_info ctinfo,
-		       unsigned char **data, GatekeeperConfirm * gcf)
-{
-	int dir = CTINFO2DIR(ctinfo);
-	int ret = 0;
-	__be32 ip;
-	u_int16_t port;
-	struct ip_conntrack_expect *exp;
-
-	DEBUGP("ip_ct_ras: GCF\n");
-
-	if (!get_h225_addr(*data, &gcf->rasAddress, &ip, &port))
-		return 0;
-
-	/* Registration port is the same as discovery port */
-	if (ip == ct->tuplehash[dir].tuple.src.ip &&
-	    port == ntohs(ct->tuplehash[dir].tuple.src.u.udp.port))
-		return 0;
-
-	/* Avoid RAS expectation loops. A GCF is never expected. */
-	if (test_bit(IPS_EXPECTED_BIT, &ct->status))
-		return 0;
-
-	/* Need new expect */
-	if ((exp = ip_conntrack_expect_alloc(ct)) == NULL)
-		return -1;
-	exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip;
-	exp->tuple.src.u.tcp.port = 0;
-	exp->tuple.dst.ip = ip;
-	exp->tuple.dst.u.tcp.port = htons(port);
-	exp->tuple.dst.protonum = IPPROTO_UDP;
-	exp->mask.src.ip = htonl(0xFFFFFFFF);
-	exp->mask.src.u.tcp.port = 0;
-	exp->mask.dst.ip = htonl(0xFFFFFFFF);
-	exp->mask.dst.u.tcp.port = htons(0xFFFF);
-	exp->mask.dst.protonum = 0xFF;
-	exp->flags = 0;
-	exp->expectfn = ip_conntrack_ras_expect;
-	if (ip_conntrack_expect_related(exp) == 0) {
-		DEBUGP("ip_ct_ras: expect RAS "
-		       "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
-		       NIPQUAD(exp->tuple.src.ip),
-		       ntohs(exp->tuple.src.u.tcp.port),
-		       NIPQUAD(exp->tuple.dst.ip),
-		       ntohs(exp->tuple.dst.u.tcp.port));
-	} else
-		ret = -1;
-
-	ip_conntrack_expect_put(exp);
-
-	return ret;
-}
-
-/****************************************************************************/
-static int process_rrq(struct sk_buff **pskb, struct ip_conntrack *ct,
-		       enum ip_conntrack_info ctinfo,
-		       unsigned char **data, RegistrationRequest * rrq)
-{
-	struct ip_ct_h323_master *info = &ct->help.ct_h323_info;
-	int ret;
-	typeof(set_ras_addr_hook) set_ras_addr;
-
-	DEBUGP("ip_ct_ras: RRQ\n");
-
-	ret = expect_q931(pskb, ct, ctinfo, data,
-			  rrq->callSignalAddress.item,
-			  rrq->callSignalAddress.count);
-	if (ret < 0)
-		return -1;
-
-	set_ras_addr = rcu_dereference(set_ras_addr_hook);
-	if (set_ras_addr) {
-		ret = set_ras_addr(pskb, ct, ctinfo, data,
-				   rrq->rasAddress.item,
-				   rrq->rasAddress.count);
-		if (ret < 0)
-			return -1;
-	}
-
-	if (rrq->options & eRegistrationRequest_timeToLive) {
-		DEBUGP("ip_ct_ras: RRQ TTL = %u seconds\n", rrq->timeToLive);
-		info->timeout = rrq->timeToLive;
-	} else
-		info->timeout = default_rrq_ttl;
-
-	return 0;
-}
-
-/****************************************************************************/
-static int process_rcf(struct sk_buff **pskb, struct ip_conntrack *ct,
-		       enum ip_conntrack_info ctinfo,
-		       unsigned char **data, RegistrationConfirm * rcf)
-{
-	struct ip_ct_h323_master *info = &ct->help.ct_h323_info;
-	int dir = CTINFO2DIR(ctinfo);
-	int ret;
-	struct ip_conntrack_expect *exp;
-	typeof(set_sig_addr_hook) set_sig_addr;
-
-	DEBUGP("ip_ct_ras: RCF\n");
-
-	set_sig_addr = rcu_dereference(set_sig_addr_hook);
-	if (set_sig_addr) {
-		ret = set_sig_addr(pskb, ct, ctinfo, data,
-				   rcf->callSignalAddress.item,
-				   rcf->callSignalAddress.count);
-		if (ret < 0)
-			return -1;
-	}
-
-	if (rcf->options & eRegistrationConfirm_timeToLive) {
-		DEBUGP("ip_ct_ras: RCF TTL = %u seconds\n", rcf->timeToLive);
-		info->timeout = rcf->timeToLive;
-	}
-
-	if (info->timeout > 0) {
-		DEBUGP
-		    ("ip_ct_ras: set RAS connection timeout to %u seconds\n",
-		     info->timeout);
-		ip_ct_refresh(ct, *pskb, info->timeout * HZ);
-
-		/* Set expect timeout */
-		read_lock_bh(&ip_conntrack_lock);
-		exp = find_expect(ct, ct->tuplehash[dir].tuple.dst.ip,
-				  info->sig_port[!dir]);
-		if (exp) {
-			DEBUGP("ip_ct_ras: set Q.931 expect "
-			       "(%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu) "
-			       "timeout to %u seconds\n",
-			       NIPQUAD(exp->tuple.src.ip),
-			       ntohs(exp->tuple.src.u.tcp.port),
-			       NIPQUAD(exp->tuple.dst.ip),
-			       ntohs(exp->tuple.dst.u.tcp.port),
-			       info->timeout);
-			set_expect_timeout(exp, info->timeout);
-		}
-		read_unlock_bh(&ip_conntrack_lock);
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-static int process_urq(struct sk_buff **pskb, struct ip_conntrack *ct,
-		       enum ip_conntrack_info ctinfo,
-		       unsigned char **data, UnregistrationRequest * urq)
-{
-	struct ip_ct_h323_master *info = &ct->help.ct_h323_info;
-	int dir = CTINFO2DIR(ctinfo);
-	int ret;
-	typeof(set_sig_addr_hook) set_sig_addr;
-
-	DEBUGP("ip_ct_ras: URQ\n");
-
-	set_sig_addr = rcu_dereference(set_sig_addr_hook);
-	if (set_sig_addr) {
-		ret = set_sig_addr(pskb, ct, ctinfo, data,
-				   urq->callSignalAddress.item,
-				   urq->callSignalAddress.count);
-		if (ret < 0)
-			return -1;
-	}
-
-	/* Clear old expect */
-	ip_ct_remove_expectations(ct);
-	info->sig_port[dir] = 0;
-	info->sig_port[!dir] = 0;
-
-	/* Give it 30 seconds for UCF or URJ */
-	ip_ct_refresh(ct, *pskb, 30 * HZ);
-
-	return 0;
-}
-
-/****************************************************************************/
-static int process_arq(struct sk_buff **pskb, struct ip_conntrack *ct,
-		       enum ip_conntrack_info ctinfo,
-		       unsigned char **data, AdmissionRequest * arq)
-{
-	struct ip_ct_h323_master *info = &ct->help.ct_h323_info;
-	int dir = CTINFO2DIR(ctinfo);
-	__be32 ip;
-	u_int16_t port;
-	typeof(set_h225_addr_hook) set_h225_addr;
-
-	DEBUGP("ip_ct_ras: ARQ\n");
-
-	set_h225_addr = rcu_dereference(set_h225_addr_hook);
-	if ((arq->options & eAdmissionRequest_destCallSignalAddress) &&
-	    get_h225_addr(*data, &arq->destCallSignalAddress, &ip, &port) &&
-	    ip == ct->tuplehash[dir].tuple.src.ip &&
-	    port == info->sig_port[dir] && set_h225_addr) {
-		/* Answering ARQ */
-		return set_h225_addr(pskb, data, 0,
-				     &arq->destCallSignalAddress,
-				     ct->tuplehash[!dir].tuple.dst.ip,
-				     info->sig_port[!dir]);
-	}
-
-	if ((arq->options & eAdmissionRequest_srcCallSignalAddress) &&
-	    get_h225_addr(*data, &arq->srcCallSignalAddress, &ip, &port) &&
-	    ip == ct->tuplehash[dir].tuple.src.ip && set_h225_addr) {
-		/* Calling ARQ */
-		return set_h225_addr(pskb, data, 0,
-				     &arq->srcCallSignalAddress,
-				     ct->tuplehash[!dir].tuple.dst.ip,
-				     port);
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-static int process_acf(struct sk_buff **pskb, struct ip_conntrack *ct,
-		       enum ip_conntrack_info ctinfo,
-		       unsigned char **data, AdmissionConfirm * acf)
-{
-	int dir = CTINFO2DIR(ctinfo);
-	int ret = 0;
-	__be32 ip;
-	u_int16_t port;
-	struct ip_conntrack_expect *exp;
-	typeof(set_sig_addr_hook) set_sig_addr;
-
-	DEBUGP("ip_ct_ras: ACF\n");
-
-	if (!get_h225_addr(*data, &acf->destCallSignalAddress, &ip, &port))
-		return 0;
-
-	if (ip == ct->tuplehash[dir].tuple.dst.ip) {	/* Answering ACF */
-		set_sig_addr = rcu_dereference(set_sig_addr_hook);
-		if (set_sig_addr)
-			return set_sig_addr(pskb, ct, ctinfo, data,
-					    &acf->destCallSignalAddress, 1);
-		return 0;
-	}
-
-	/* Need new expect */
-	if ((exp = ip_conntrack_expect_alloc(ct)) == NULL)
-		return -1;
-	exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip;
-	exp->tuple.src.u.tcp.port = 0;
-	exp->tuple.dst.ip = ip;
-	exp->tuple.dst.u.tcp.port = htons(port);
-	exp->tuple.dst.protonum = IPPROTO_TCP;
-	exp->mask.src.ip = htonl(0xFFFFFFFF);
-	exp->mask.src.u.tcp.port = 0;
-	exp->mask.dst.ip = htonl(0xFFFFFFFF);
-	exp->mask.dst.u.tcp.port = htons(0xFFFF);
-	exp->mask.dst.protonum = 0xFF;
-	exp->flags = IP_CT_EXPECT_PERMANENT;
-	exp->expectfn = ip_conntrack_q931_expect;
-
-	if (ip_conntrack_expect_related(exp) == 0) {
-		DEBUGP("ip_ct_ras: expect Q.931 "
-		       "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
-		       NIPQUAD(exp->tuple.src.ip),
-		       ntohs(exp->tuple.src.u.tcp.port),
-		       NIPQUAD(exp->tuple.dst.ip),
-		       ntohs(exp->tuple.dst.u.tcp.port));
-	} else
-		ret = -1;
-
-	ip_conntrack_expect_put(exp);
-
-	return ret;
-}
-
-/****************************************************************************/
-static int process_lrq(struct sk_buff **pskb, struct ip_conntrack *ct,
-		       enum ip_conntrack_info ctinfo,
-		       unsigned char **data, LocationRequest * lrq)
-{
-	typeof(set_ras_addr_hook) set_ras_addr;
-
-	DEBUGP("ip_ct_ras: LRQ\n");
-
-	set_ras_addr = rcu_dereference(set_ras_addr_hook);
-	if (set_ras_addr)
-		return set_ras_addr(pskb, ct, ctinfo, data,
-				    &lrq->replyAddress, 1);
-	return 0;
-}
-
-/****************************************************************************/
-static int process_lcf(struct sk_buff **pskb, struct ip_conntrack *ct,
-		       enum ip_conntrack_info ctinfo,
-		       unsigned char **data, LocationConfirm * lcf)
-{
-	int dir = CTINFO2DIR(ctinfo);
-	int ret = 0;
-	__be32 ip;
-	u_int16_t port;
-	struct ip_conntrack_expect *exp = NULL;
-
-	DEBUGP("ip_ct_ras: LCF\n");
-
-	if (!get_h225_addr(*data, &lcf->callSignalAddress, &ip, &port))
-		return 0;
-
-	/* Need new expect for call signal */
-	if ((exp = ip_conntrack_expect_alloc(ct)) == NULL)
-		return -1;
-	exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip;
-	exp->tuple.src.u.tcp.port = 0;
-	exp->tuple.dst.ip = ip;
-	exp->tuple.dst.u.tcp.port = htons(port);
-	exp->tuple.dst.protonum = IPPROTO_TCP;
-	exp->mask.src.ip = htonl(0xFFFFFFFF);
-	exp->mask.src.u.tcp.port = 0;
-	exp->mask.dst.ip = htonl(0xFFFFFFFF);
-	exp->mask.dst.u.tcp.port = htons(0xFFFF);
-	exp->mask.dst.protonum = 0xFF;
-	exp->flags = IP_CT_EXPECT_PERMANENT;
-	exp->expectfn = ip_conntrack_q931_expect;
-
-	if (ip_conntrack_expect_related(exp) == 0) {
-		DEBUGP("ip_ct_ras: expect Q.931 "
-		       "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
-		       NIPQUAD(exp->tuple.src.ip),
-		       ntohs(exp->tuple.src.u.tcp.port),
-		       NIPQUAD(exp->tuple.dst.ip),
-		       ntohs(exp->tuple.dst.u.tcp.port));
-	} else
-		ret = -1;
-
-	ip_conntrack_expect_put(exp);
-
-	/* Ignore rasAddress */
-
-	return ret;
-}
-
-/****************************************************************************/
-static int process_irr(struct sk_buff **pskb, struct ip_conntrack *ct,
-		       enum ip_conntrack_info ctinfo,
-		       unsigned char **data, InfoRequestResponse * irr)
-{
-	int ret;
-	typeof(set_ras_addr_hook) set_ras_addr;
-	typeof(set_sig_addr_hook) set_sig_addr;
-
-	DEBUGP("ip_ct_ras: IRR\n");
-
-	set_ras_addr = rcu_dereference(set_ras_addr_hook);
-	if (set_ras_addr) {
-		ret = set_ras_addr(pskb, ct, ctinfo, data,
-				   &irr->rasAddress, 1);
-		if (ret < 0)
-			return -1;
-	}
-
-	set_sig_addr = rcu_dereference(set_sig_addr_hook);
-	if (set_sig_addr) {
-		ret = set_sig_addr(pskb, ct, ctinfo, data,
-				   irr->callSignalAddress.item,
-				   irr->callSignalAddress.count);
-		if (ret < 0)
-			return -1;
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-static int process_ras(struct sk_buff **pskb, struct ip_conntrack *ct,
-		       enum ip_conntrack_info ctinfo,
-		       unsigned char **data, RasMessage * ras)
-{
-	switch (ras->choice) {
-	case eRasMessage_gatekeeperRequest:
-		return process_grq(pskb, ct, ctinfo, data,
-				   &ras->gatekeeperRequest);
-	case eRasMessage_gatekeeperConfirm:
-		return process_gcf(pskb, ct, ctinfo, data,
-				   &ras->gatekeeperConfirm);
-	case eRasMessage_registrationRequest:
-		return process_rrq(pskb, ct, ctinfo, data,
-				   &ras->registrationRequest);
-	case eRasMessage_registrationConfirm:
-		return process_rcf(pskb, ct, ctinfo, data,
-				   &ras->registrationConfirm);
-	case eRasMessage_unregistrationRequest:
-		return process_urq(pskb, ct, ctinfo, data,
-				   &ras->unregistrationRequest);
-	case eRasMessage_admissionRequest:
-		return process_arq(pskb, ct, ctinfo, data,
-				   &ras->admissionRequest);
-	case eRasMessage_admissionConfirm:
-		return process_acf(pskb, ct, ctinfo, data,
-				   &ras->admissionConfirm);
-	case eRasMessage_locationRequest:
-		return process_lrq(pskb, ct, ctinfo, data,
-				   &ras->locationRequest);
-	case eRasMessage_locationConfirm:
-		return process_lcf(pskb, ct, ctinfo, data,
-				   &ras->locationConfirm);
-	case eRasMessage_infoRequestResponse:
-		return process_irr(pskb, ct, ctinfo, data,
-				   &ras->infoRequestResponse);
-	default:
-		DEBUGP("ip_ct_ras: RAS message %d\n", ras->choice);
-		break;
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-static int ras_help(struct sk_buff **pskb, struct ip_conntrack *ct,
-		    enum ip_conntrack_info ctinfo)
-{
-	static RasMessage ras;
-	unsigned char *data;
-	int datalen = 0;
-	int ret;
-
-	DEBUGP("ip_ct_ras: skblen = %u\n", (*pskb)->len);
-
-	spin_lock_bh(&ip_h323_lock);
-
-	/* Get UDP data */
-	data = get_udp_data(pskb, &datalen);
-	if (data == NULL)
-		goto accept;
-	DEBUGP("ip_ct_ras: RAS message %u.%u.%u.%u->%u.%u.%u.%u, len=%d\n",
-	       NIPQUAD((*pskb)->nh.iph->saddr),
-	       NIPQUAD((*pskb)->nh.iph->daddr), datalen);
-
-	/* Decode RAS message */
-	ret = DecodeRasMessage(data, datalen, &ras);
-	if (ret < 0) {
-		if (net_ratelimit())
-			printk("ip_ct_ras: decoding error: %s\n",
-			       ret == H323_ERROR_BOUND ?
-			       "out of bound" : "out of range");
-		goto accept;
-	}
-
-	/* Process RAS message */
-	if (process_ras(pskb, ct, ctinfo, &data, &ras) < 0)
-		goto drop;
-
-      accept:
-	spin_unlock_bh(&ip_h323_lock);
-	return NF_ACCEPT;
-
-      drop:
-	spin_unlock_bh(&ip_h323_lock);
-	if (net_ratelimit())
-		printk("ip_ct_ras: packet dropped\n");
-	return NF_DROP;
-}
-
-/****************************************************************************/
-static struct ip_conntrack_helper ip_conntrack_helper_ras = {
-	.name = "RAS",
-	.me = THIS_MODULE,
-	.max_expected = 32,
-	.timeout = 240,
-	.tuple = {.src = {.u = {.tcp = {.port = __constant_htons(RAS_PORT)}}},
-		  .dst = {.protonum = IPPROTO_UDP}},
-	.mask = {.src = {.u = {0xFFFE}},
-		 .dst = {.protonum = 0xFF}},
-	.help = ras_help,
-};
-
-/****************************************************************************/
-static void ip_conntrack_ras_expect(struct ip_conntrack *new,
-				    struct ip_conntrack_expect *this)
-{
-	write_lock_bh(&ip_conntrack_lock);
-	new->helper = &ip_conntrack_helper_ras;
-	write_unlock_bh(&ip_conntrack_lock);
-}
-
-/****************************************************************************/
-/* Not __exit - called from init() */
-static void fini(void)
-{
-	ip_conntrack_helper_unregister(&ip_conntrack_helper_ras);
-	ip_conntrack_helper_unregister(&ip_conntrack_helper_q931);
-	kfree(h323_buffer);
-	DEBUGP("ip_ct_h323: fini\n");
-}
-
-/****************************************************************************/
-static int __init init(void)
-{
-	int ret;
-
-	h323_buffer = kmalloc(65536, GFP_KERNEL);
-	if (!h323_buffer)
-		return -ENOMEM;
-	if ((ret = ip_conntrack_helper_register(&ip_conntrack_helper_q931)) ||
-	    (ret = ip_conntrack_helper_register(&ip_conntrack_helper_ras))) {
-		fini();
-		return ret;
-	}
-	DEBUGP("ip_ct_h323: init success\n");
-	return 0;
-}
-
-/****************************************************************************/
-module_init(init);
-module_exit(fini);
-
-EXPORT_SYMBOL_GPL(get_h225_addr);
-EXPORT_SYMBOL_GPL(ip_conntrack_h245_expect);
-EXPORT_SYMBOL_GPL(ip_conntrack_q931_expect);
-EXPORT_SYMBOL_GPL(set_h245_addr_hook);
-EXPORT_SYMBOL_GPL(set_h225_addr_hook);
-EXPORT_SYMBOL_GPL(set_sig_addr_hook);
-EXPORT_SYMBOL_GPL(set_ras_addr_hook);
-EXPORT_SYMBOL_GPL(nat_rtp_rtcp_hook);
-EXPORT_SYMBOL_GPL(nat_t120_hook);
-EXPORT_SYMBOL_GPL(nat_h245_hook);
-EXPORT_SYMBOL_GPL(nat_callforwarding_hook);
-EXPORT_SYMBOL_GPL(nat_q931_hook);
-
-MODULE_AUTHOR("Jing Min Zhao <zhaojingmin@users.sourceforge.net>");
-MODULE_DESCRIPTION("H.323 connection tracking helper");
-MODULE_LICENSE("GPL");
diff --git a/net/ipv4/netfilter/ip_conntrack_helper_pptp.c b/net/ipv4/netfilter/ip_conntrack_helper_pptp.c
deleted file mode 100644
index 2b760c5..0000000
--- a/net/ipv4/netfilter/ip_conntrack_helper_pptp.c
+++ /dev/null
@@ -1,684 +0,0 @@
-/*
- * ip_conntrack_pptp.c	- Version 3.0
- *
- * Connection tracking support for PPTP (Point to Point Tunneling Protocol).
- * PPTP is a a protocol for creating virtual private networks.
- * It is a specification defined by Microsoft and some vendors
- * working with Microsoft.  PPTP is built on top of a modified
- * version of the Internet Generic Routing Encapsulation Protocol.
- * GRE is defined in RFC 1701 and RFC 1702.  Documentation of
- * PPTP can be found in RFC 2637
- *
- * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org>
- *
- * Development of this code funded by Astaro AG (http://www.astaro.com/)
- *
- * Limitations:
- * 	 - We blindly assume that control connections are always
- * 	   established in PNS->PAC direction.  This is a violation
- * 	   of RFFC2673
- * 	 - We can only support one single call within each session
- *
- * TODO:
- *	 - testing of incoming PPTP calls
- *
- * Changes:
- * 	2002-02-05 - Version 1.3
- * 	  - Call ip_conntrack_unexpect_related() from
- * 	    pptp_destroy_siblings() to destroy expectations in case
- * 	    CALL_DISCONNECT_NOTIFY or tcp fin packet was seen
- * 	    (Philip Craig <philipc@snapgear.com>)
- * 	  - Add Version information at module loadtime
- * 	2002-02-10 - Version 1.6
- * 	  - move to C99 style initializers
- * 	  - remove second expectation if first arrives
- * 	2004-10-22 - Version 2.0
- * 	  - merge Mandrake's 2.6.x port with recent 2.6.x API changes
- * 	  - fix lots of linear skb assumptions from Mandrake's port
- * 	2005-06-10 - Version 2.1
- * 	  - use ip_conntrack_expect_free() instead of kfree() on the
- * 	    expect's (which are from the slab for quite some time)
- * 	2005-06-10 - Version 3.0
- * 	  - port helper to post-2.6.11 API changes,
- * 	    funded by Oxcoda NetBox Blue (http://www.netboxblue.com/)
- * 	2005-07-30 - Version 3.1
- * 	  - port helper to 2.6.13 API changes
- *
- */
-
-#include <linux/module.h>
-#include <linux/netfilter.h>
-#include <linux/ip.h>
-#include <net/checksum.h>
-#include <net/tcp.h>
-
-#include <linux/netfilter_ipv4/ip_conntrack.h>
-#include <linux/netfilter_ipv4/ip_conntrack_core.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
-#include <linux/netfilter_ipv4/ip_conntrack_pptp.h>
-
-#define IP_CT_PPTP_VERSION "3.1"
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
-MODULE_DESCRIPTION("Netfilter connection tracking helper module for PPTP");
-
-static DEFINE_SPINLOCK(ip_pptp_lock);
-
-int
-(*ip_nat_pptp_hook_outbound)(struct sk_buff **pskb,
-			  struct ip_conntrack *ct,
-			  enum ip_conntrack_info ctinfo,
-			  struct PptpControlHeader *ctlh,
-			  union pptp_ctrl_union *pptpReq);
-
-int
-(*ip_nat_pptp_hook_inbound)(struct sk_buff **pskb,
-			  struct ip_conntrack *ct,
-			  enum ip_conntrack_info ctinfo,
-			  struct PptpControlHeader *ctlh,
-			  union pptp_ctrl_union *pptpReq);
-
-void
-(*ip_nat_pptp_hook_exp_gre)(struct ip_conntrack_expect *expect_orig,
-			    struct ip_conntrack_expect *expect_reply);
-
-void
-(*ip_nat_pptp_hook_expectfn)(struct ip_conntrack *ct,
-			     struct ip_conntrack_expect *exp);
-
-#if 0
-/* PptpControlMessageType names */
-const char *pptp_msg_name[] = {
-	"UNKNOWN_MESSAGE",
-	"START_SESSION_REQUEST",
-	"START_SESSION_REPLY",
-	"STOP_SESSION_REQUEST",
-	"STOP_SESSION_REPLY",
-	"ECHO_REQUEST",
-	"ECHO_REPLY",
-	"OUT_CALL_REQUEST",
-	"OUT_CALL_REPLY",
-	"IN_CALL_REQUEST",
-	"IN_CALL_REPLY",
-	"IN_CALL_CONNECT",
-	"CALL_CLEAR_REQUEST",
-	"CALL_DISCONNECT_NOTIFY",
-	"WAN_ERROR_NOTIFY",
-	"SET_LINK_INFO"
-};
-EXPORT_SYMBOL(pptp_msg_name);
-#define DEBUGP(format, args...)	printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args)
-#else
-#define DEBUGP(format, args...)
-#endif
-
-#define SECS *HZ
-#define MINS * 60 SECS
-#define HOURS * 60 MINS
-
-#define PPTP_GRE_TIMEOUT 		(10 MINS)
-#define PPTP_GRE_STREAM_TIMEOUT 	(5 HOURS)
-
-static void pptp_expectfn(struct ip_conntrack *ct,
-			 struct ip_conntrack_expect *exp)
-{
-	typeof(ip_nat_pptp_hook_expectfn) ip_nat_pptp_expectfn;
-
-	DEBUGP("increasing timeouts\n");
-
-	/* increase timeout of GRE data channel conntrack entry */
-	ct->proto.gre.timeout = PPTP_GRE_TIMEOUT;
-	ct->proto.gre.stream_timeout = PPTP_GRE_STREAM_TIMEOUT;
-
-	/* Can you see how rusty this code is, compared with the pre-2.6.11
-	 * one? That's what happened to my shiny newnat of 2002 ;( -HW */
-
-	rcu_read_lock();
-	ip_nat_pptp_expectfn = rcu_dereference(ip_nat_pptp_hook_expectfn);
-	if (!ip_nat_pptp_expectfn) {
-		struct ip_conntrack_tuple inv_t;
-		struct ip_conntrack_expect *exp_other;
-
-		/* obviously this tuple inversion only works until you do NAT */
-		invert_tuplepr(&inv_t, &exp->tuple);
-		DEBUGP("trying to unexpect other dir: ");
-		DUMP_TUPLE(&inv_t);
-
-		exp_other = ip_conntrack_expect_find_get(&inv_t);
-		if (exp_other) {
-			/* delete other expectation.  */
-			DEBUGP("found\n");
-			ip_conntrack_unexpect_related(exp_other);
-			ip_conntrack_expect_put(exp_other);
-		} else {
-			DEBUGP("not found\n");
-		}
-	} else {
-		/* we need more than simple inversion */
-		ip_nat_pptp_expectfn(ct, exp);
-	}
-	rcu_read_unlock();
-}
-
-static int destroy_sibling_or_exp(const struct ip_conntrack_tuple *t)
-{
-	struct ip_conntrack_tuple_hash *h;
-	struct ip_conntrack_expect *exp;
-
-	DEBUGP("trying to timeout ct or exp for tuple ");
-	DUMP_TUPLE(t);
-
-	h = ip_conntrack_find_get(t, NULL);
-	if (h)  {
-		struct ip_conntrack *sibling = tuplehash_to_ctrack(h);
-		DEBUGP("setting timeout of conntrack %p to 0\n", sibling);
-		sibling->proto.gre.timeout = 0;
-		sibling->proto.gre.stream_timeout = 0;
-		if (del_timer(&sibling->timeout))
-			sibling->timeout.function((unsigned long)sibling);
-		ip_conntrack_put(sibling);
-		return 1;
-	} else {
-		exp = ip_conntrack_expect_find_get(t);
-		if (exp) {
-			DEBUGP("unexpect_related of expect %p\n", exp);
-			ip_conntrack_unexpect_related(exp);
-			ip_conntrack_expect_put(exp);
-			return 1;
-		}
-	}
-
-	return 0;
-}
-
-
-/* timeout GRE data connections */
-static void pptp_destroy_siblings(struct ip_conntrack *ct)
-{
-	struct ip_conntrack_tuple t;
-
-	ip_ct_gre_keymap_destroy(ct);
-	/* Since ct->sibling_list has literally rusted away in 2.6.11,
-	 * we now need another way to find out about our sibling
-	 * contrack and expects... -HW */
-
-	/* try original (pns->pac) tuple */
-	memcpy(&t, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(t));
-	t.dst.protonum = IPPROTO_GRE;
-	t.src.u.gre.key = ct->help.ct_pptp_info.pns_call_id;
-	t.dst.u.gre.key = ct->help.ct_pptp_info.pac_call_id;
-
-	if (!destroy_sibling_or_exp(&t))
-		DEBUGP("failed to timeout original pns->pac ct/exp\n");
-
-	/* try reply (pac->pns) tuple */
-	memcpy(&t, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, sizeof(t));
-	t.dst.protonum = IPPROTO_GRE;
-	t.src.u.gre.key = ct->help.ct_pptp_info.pac_call_id;
-	t.dst.u.gre.key = ct->help.ct_pptp_info.pns_call_id;
-
-	if (!destroy_sibling_or_exp(&t))
-		DEBUGP("failed to timeout reply pac->pns ct/exp\n");
-}
-
-/* expect GRE connections (PNS->PAC and PAC->PNS direction) */
-static inline int
-exp_gre(struct ip_conntrack *ct,
-	__be16 callid,
-	__be16 peer_callid)
-{
-	struct ip_conntrack_expect *exp_orig, *exp_reply;
-	int ret = 1;
-	typeof(ip_nat_pptp_hook_exp_gre) ip_nat_pptp_exp_gre;
-
-	exp_orig = ip_conntrack_expect_alloc(ct);
-	if (exp_orig == NULL)
-		goto out;
-
-	exp_reply = ip_conntrack_expect_alloc(ct);
-	if (exp_reply == NULL)
-		goto out_put_orig;
-
-	/* original direction, PNS->PAC */
-	exp_orig->tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
-	exp_orig->tuple.src.u.gre.key = peer_callid;
-	exp_orig->tuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
-	exp_orig->tuple.dst.u.gre.key = callid;
-	exp_orig->tuple.dst.protonum = IPPROTO_GRE;
-
-	exp_orig->mask.src.ip = htonl(0xffffffff);
-	exp_orig->mask.src.u.all = 0;
-	exp_orig->mask.dst.u.gre.key = htons(0xffff);
-	exp_orig->mask.dst.ip = htonl(0xffffffff);
-	exp_orig->mask.dst.protonum = 0xff;
-
-	exp_orig->master = ct;
-	exp_orig->expectfn = pptp_expectfn;
-	exp_orig->flags = 0;
-
-	/* both expectations are identical apart from tuple */
-	memcpy(exp_reply, exp_orig, sizeof(*exp_reply));
-
-	/* reply direction, PAC->PNS */
-	exp_reply->tuple.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
-	exp_reply->tuple.src.u.gre.key = callid;
-	exp_reply->tuple.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
-	exp_reply->tuple.dst.u.gre.key = peer_callid;
-	exp_reply->tuple.dst.protonum = IPPROTO_GRE;
-
-	ip_nat_pptp_exp_gre = rcu_dereference(ip_nat_pptp_hook_exp_gre);
-	if (ip_nat_pptp_exp_gre)
-		ip_nat_pptp_exp_gre(exp_orig, exp_reply);
-	if (ip_conntrack_expect_related(exp_orig) != 0)
-		goto out_put_both;
-	if (ip_conntrack_expect_related(exp_reply) != 0)
-		goto out_unexpect_orig;
-
-	/* Add GRE keymap entries */
-	if (ip_ct_gre_keymap_add(ct, &exp_orig->tuple, 0) != 0)
-		goto out_unexpect_both;
-	if (ip_ct_gre_keymap_add(ct, &exp_reply->tuple, 1) != 0) {
-		ip_ct_gre_keymap_destroy(ct);
-		goto out_unexpect_both;
-	}
-	ret = 0;
-
-out_put_both:
-	ip_conntrack_expect_put(exp_reply);
-out_put_orig:
-	ip_conntrack_expect_put(exp_orig);
-out:
-	return ret;
-
-out_unexpect_both:
-	ip_conntrack_unexpect_related(exp_reply);
-out_unexpect_orig:
-	ip_conntrack_unexpect_related(exp_orig);
-	goto out_put_both;
-}
-
-static inline int
-pptp_inbound_pkt(struct sk_buff **pskb,
-		 struct PptpControlHeader *ctlh,
-		 union pptp_ctrl_union *pptpReq,
-		 unsigned int reqlen,
-		 struct ip_conntrack *ct,
-		 enum ip_conntrack_info ctinfo)
-{
-	struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
-	u_int16_t msg;
-	__be16 cid = 0, pcid = 0;
-	typeof(ip_nat_pptp_hook_inbound) ip_nat_pptp_inbound;
-
-	msg = ntohs(ctlh->messageType);
-	DEBUGP("inbound control message %s\n", pptp_msg_name[msg]);
-
-	switch (msg) {
-	case PPTP_START_SESSION_REPLY:
-		/* server confirms new control session */
-		if (info->sstate < PPTP_SESSION_REQUESTED)
-			goto invalid;
-		if (pptpReq->srep.resultCode == PPTP_START_OK)
-			info->sstate = PPTP_SESSION_CONFIRMED;
-		else
-			info->sstate = PPTP_SESSION_ERROR;
-		break;
-
-	case PPTP_STOP_SESSION_REPLY:
-		/* server confirms end of control session */
-		if (info->sstate > PPTP_SESSION_STOPREQ)
-			goto invalid;
-		if (pptpReq->strep.resultCode == PPTP_STOP_OK)
-			info->sstate = PPTP_SESSION_NONE;
-		else
-			info->sstate = PPTP_SESSION_ERROR;
-		break;
-
-	case PPTP_OUT_CALL_REPLY:
-		/* server accepted call, we now expect GRE frames */
-		if (info->sstate != PPTP_SESSION_CONFIRMED)
-			goto invalid;
-		if (info->cstate != PPTP_CALL_OUT_REQ &&
-		    info->cstate != PPTP_CALL_OUT_CONF)
-			goto invalid;
-
-		cid = pptpReq->ocack.callID;
-		pcid = pptpReq->ocack.peersCallID;
-		if (info->pns_call_id != pcid)
-			goto invalid;
-		DEBUGP("%s, CID=%X, PCID=%X\n", pptp_msg_name[msg],
-			ntohs(cid), ntohs(pcid));
-
-		if (pptpReq->ocack.resultCode == PPTP_OUTCALL_CONNECT) {
-			info->cstate = PPTP_CALL_OUT_CONF;
-			info->pac_call_id = cid;
-			exp_gre(ct, cid, pcid);
-		} else
-			info->cstate = PPTP_CALL_NONE;
-		break;
-
-	case PPTP_IN_CALL_REQUEST:
-		/* server tells us about incoming call request */
-		if (info->sstate != PPTP_SESSION_CONFIRMED)
-			goto invalid;
-
-		cid = pptpReq->icreq.callID;
-		DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid));
-		info->cstate = PPTP_CALL_IN_REQ;
-		info->pac_call_id = cid;
-		break;
-
-	case PPTP_IN_CALL_CONNECT:
-		/* server tells us about incoming call established */
-		if (info->sstate != PPTP_SESSION_CONFIRMED)
-			goto invalid;
-		if (info->cstate != PPTP_CALL_IN_REP &&
-		    info->cstate != PPTP_CALL_IN_CONF)
-			goto invalid;
-
-		pcid = pptpReq->iccon.peersCallID;
-		cid = info->pac_call_id;
-
-		if (info->pns_call_id != pcid)
-			goto invalid;
-
-		DEBUGP("%s, PCID=%X\n", pptp_msg_name[msg], ntohs(pcid));
-		info->cstate = PPTP_CALL_IN_CONF;
-
-		/* we expect a GRE connection from PAC to PNS */
-		exp_gre(ct, cid, pcid);
-		break;
-
-	case PPTP_CALL_DISCONNECT_NOTIFY:
-		/* server confirms disconnect */
-		cid = pptpReq->disc.callID;
-		DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid));
-		info->cstate = PPTP_CALL_NONE;
-
-		/* untrack this call id, unexpect GRE packets */
-		pptp_destroy_siblings(ct);
-		break;
-
-	case PPTP_WAN_ERROR_NOTIFY:
-	case PPTP_ECHO_REQUEST:
-	case PPTP_ECHO_REPLY:
-		/* I don't have to explain these ;) */
-		break;
-	default:
-		goto invalid;
-	}
-
-	ip_nat_pptp_inbound = rcu_dereference(ip_nat_pptp_hook_inbound);
-	if (ip_nat_pptp_inbound)
-		return ip_nat_pptp_inbound(pskb, ct, ctinfo, ctlh, pptpReq);
-	return NF_ACCEPT;
-
-invalid:
-	DEBUGP("invalid %s: type=%d cid=%u pcid=%u "
-	       "cstate=%d sstate=%d pns_cid=%u pac_cid=%u\n",
-	       msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : pptp_msg_name[0],
-	       msg, ntohs(cid), ntohs(pcid),  info->cstate, info->sstate,
-	       ntohs(info->pns_call_id), ntohs(info->pac_call_id));
-	return NF_ACCEPT;
-}
-
-static inline int
-pptp_outbound_pkt(struct sk_buff **pskb,
-		  struct PptpControlHeader *ctlh,
-		  union pptp_ctrl_union *pptpReq,
-		  unsigned int reqlen,
-		  struct ip_conntrack *ct,
-		  enum ip_conntrack_info ctinfo)
-{
-	struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
-	u_int16_t msg;
-	__be16 cid = 0, pcid = 0;
-	typeof(ip_nat_pptp_hook_outbound) ip_nat_pptp_outbound;
-
-	msg = ntohs(ctlh->messageType);
-	DEBUGP("outbound control message %s\n", pptp_msg_name[msg]);
-
-	switch (msg) {
-	case PPTP_START_SESSION_REQUEST:
-		/* client requests for new control session */
-		if (info->sstate != PPTP_SESSION_NONE)
-			goto invalid;
-		info->sstate = PPTP_SESSION_REQUESTED;
-		break;
-	case PPTP_STOP_SESSION_REQUEST:
-		/* client requests end of control session */
-		info->sstate = PPTP_SESSION_STOPREQ;
-		break;
-
-	case PPTP_OUT_CALL_REQUEST:
-		/* client initiating connection to server */
-		if (info->sstate != PPTP_SESSION_CONFIRMED)
-			goto invalid;
-		info->cstate = PPTP_CALL_OUT_REQ;
-		/* track PNS call id */
-		cid = pptpReq->ocreq.callID;
-		DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid));
-		info->pns_call_id = cid;
-		break;
-	case PPTP_IN_CALL_REPLY:
-		/* client answers incoming call */
-		if (info->cstate != PPTP_CALL_IN_REQ &&
-		    info->cstate != PPTP_CALL_IN_REP)
-			goto invalid;
-
-		cid = pptpReq->icack.callID;
-		pcid = pptpReq->icack.peersCallID;
-		if (info->pac_call_id != pcid)
-			goto invalid;
-		DEBUGP("%s, CID=%X PCID=%X\n", pptp_msg_name[msg],
-		       ntohs(cid), ntohs(pcid));
-
-		if (pptpReq->icack.resultCode == PPTP_INCALL_ACCEPT) {
-			/* part two of the three-way handshake */
-			info->cstate = PPTP_CALL_IN_REP;
-			info->pns_call_id = cid;
-		} else
-			info->cstate = PPTP_CALL_NONE;
-		break;
-
-	case PPTP_CALL_CLEAR_REQUEST:
-		/* client requests hangup of call */
-		if (info->sstate != PPTP_SESSION_CONFIRMED)
-			goto invalid;
-		/* FUTURE: iterate over all calls and check if
-		 * call ID is valid.  We don't do this without newnat,
-		 * because we only know about last call */
-		info->cstate = PPTP_CALL_CLEAR_REQ;
-		break;
-	case PPTP_SET_LINK_INFO:
-	case PPTP_ECHO_REQUEST:
-	case PPTP_ECHO_REPLY:
-		/* I don't have to explain these ;) */
-		break;
-	default:
-		goto invalid;
-	}
-
-	ip_nat_pptp_outbound = rcu_dereference(ip_nat_pptp_hook_outbound);
-	if (ip_nat_pptp_outbound)
-		return ip_nat_pptp_outbound(pskb, ct, ctinfo, ctlh, pptpReq);
-	return NF_ACCEPT;
-
-invalid:
-	DEBUGP("invalid %s: type=%d cid=%u pcid=%u "
-	       "cstate=%d sstate=%d pns_cid=%u pac_cid=%u\n",
-	       msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : pptp_msg_name[0],
-	       msg, ntohs(cid), ntohs(pcid),  info->cstate, info->sstate,
-	       ntohs(info->pns_call_id), ntohs(info->pac_call_id));
-	return NF_ACCEPT;
-}
-
-static const unsigned int pptp_msg_size[] = {
-	[PPTP_START_SESSION_REQUEST]  = sizeof(struct PptpStartSessionRequest),
-	[PPTP_START_SESSION_REPLY]    = sizeof(struct PptpStartSessionReply),
-	[PPTP_STOP_SESSION_REQUEST]   = sizeof(struct PptpStopSessionRequest),
-	[PPTP_STOP_SESSION_REPLY]     = sizeof(struct PptpStopSessionReply),
-	[PPTP_OUT_CALL_REQUEST]       = sizeof(struct PptpOutCallRequest),
-	[PPTP_OUT_CALL_REPLY]	      = sizeof(struct PptpOutCallReply),
-	[PPTP_IN_CALL_REQUEST]	      = sizeof(struct PptpInCallRequest),
-	[PPTP_IN_CALL_REPLY]	      = sizeof(struct PptpInCallReply),
-	[PPTP_IN_CALL_CONNECT]	      = sizeof(struct PptpInCallConnected),
-	[PPTP_CALL_CLEAR_REQUEST]     = sizeof(struct PptpClearCallRequest),
-	[PPTP_CALL_DISCONNECT_NOTIFY] = sizeof(struct PptpCallDisconnectNotify),
-	[PPTP_WAN_ERROR_NOTIFY]	      = sizeof(struct PptpWanErrorNotify),
-	[PPTP_SET_LINK_INFO]	      = sizeof(struct PptpSetLinkInfo),
-};
-
-/* track caller id inside control connection, call expect_related */
-static int
-conntrack_pptp_help(struct sk_buff **pskb,
-		    struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
-
-{
-	int dir = CTINFO2DIR(ctinfo);
-	struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
-	struct tcphdr _tcph, *tcph;
-	struct pptp_pkt_hdr _pptph, *pptph;
-	struct PptpControlHeader _ctlh, *ctlh;
-	union pptp_ctrl_union _pptpReq, *pptpReq;
-	unsigned int tcplen = (*pskb)->len - (*pskb)->nh.iph->ihl * 4;
-	unsigned int datalen, reqlen, nexthdr_off;
-	int oldsstate, oldcstate;
-	int ret;
-	u_int16_t msg;
-
-	/* don't do any tracking before tcp handshake complete */
-	if (ctinfo != IP_CT_ESTABLISHED
-	    && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) {
-		DEBUGP("ctinfo = %u, skipping\n", ctinfo);
-		return NF_ACCEPT;
-	}
-
-	nexthdr_off = (*pskb)->nh.iph->ihl*4;
-	tcph = skb_header_pointer(*pskb, nexthdr_off, sizeof(_tcph), &_tcph);
-	BUG_ON(!tcph);
-	nexthdr_off += tcph->doff * 4;
-	datalen = tcplen - tcph->doff * 4;
-
-	pptph = skb_header_pointer(*pskb, nexthdr_off, sizeof(_pptph), &_pptph);
-	if (!pptph) {
-		DEBUGP("no full PPTP header, can't track\n");
-		return NF_ACCEPT;
-	}
-	nexthdr_off += sizeof(_pptph);
-	datalen -= sizeof(_pptph);
-
-	/* if it's not a control message we can't do anything with it */
-	if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL ||
-	    ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) {
-		DEBUGP("not a control packet\n");
-		return NF_ACCEPT;
-	}
-
-	ctlh = skb_header_pointer(*pskb, nexthdr_off, sizeof(_ctlh), &_ctlh);
-	if (!ctlh)
-		return NF_ACCEPT;
-	nexthdr_off += sizeof(_ctlh);
-	datalen -= sizeof(_ctlh);
-
-	reqlen = datalen;
-	msg = ntohs(ctlh->messageType);
-	if (msg > 0 && msg <= PPTP_MSG_MAX && reqlen < pptp_msg_size[msg])
-		return NF_ACCEPT;
-	if (reqlen > sizeof(*pptpReq))
-		reqlen = sizeof(*pptpReq);
-
-	pptpReq = skb_header_pointer(*pskb, nexthdr_off, reqlen, &_pptpReq);
-	if (!pptpReq)
-		return NF_ACCEPT;
-
-	oldsstate = info->sstate;
-	oldcstate = info->cstate;
-
-	spin_lock_bh(&ip_pptp_lock);
-
-	/* FIXME: We just blindly assume that the control connection is always
-	 * established from PNS->PAC.  However, RFC makes no guarantee */
-	if (dir == IP_CT_DIR_ORIGINAL)
-		/* client -> server (PNS -> PAC) */
-		ret = pptp_outbound_pkt(pskb, ctlh, pptpReq, reqlen, ct,
-					ctinfo);
-	else
-		/* server -> client (PAC -> PNS) */
-		ret = pptp_inbound_pkt(pskb, ctlh, pptpReq, reqlen, ct,
-				       ctinfo);
-	DEBUGP("sstate: %d->%d, cstate: %d->%d\n",
-		oldsstate, info->sstate, oldcstate, info->cstate);
-	spin_unlock_bh(&ip_pptp_lock);
-
-	return ret;
-}
-
-/* control protocol helper */
-static struct ip_conntrack_helper pptp = {
-	.list = { NULL, NULL },
-	.name = "pptp",
-	.me = THIS_MODULE,
-	.max_expected = 2,
-	.timeout = 5 * 60,
-	.tuple = { .src = { .ip = 0,
-			    .u = { .tcp = { .port =
-				    __constant_htons(PPTP_CONTROL_PORT) } }
-			  },
-		   .dst = { .ip = 0,
-			    .u = { .all = 0 },
-			    .protonum = IPPROTO_TCP
-			  }
-		 },
-	.mask = { .src = { .ip = 0,
-			   .u = { .tcp = { .port = __constant_htons(0xffff) } }
-			 },
-		  .dst = { .ip = 0,
-			   .u = { .all = 0 },
-			   .protonum = 0xff
-			 }
-		},
-	.help = conntrack_pptp_help,
-	.destroy = pptp_destroy_siblings,
-};
-
-extern void ip_ct_proto_gre_fini(void);
-extern int __init ip_ct_proto_gre_init(void);
-
-/* ip_conntrack_pptp initialization */
-static int __init ip_conntrack_helper_pptp_init(void)
-{
-	int retcode;
-
-	retcode = ip_ct_proto_gre_init();
-	if (retcode < 0)
-		return retcode;
-
-	DEBUGP(" registering helper\n");
-	if ((retcode = ip_conntrack_helper_register(&pptp))) {
-		printk(KERN_ERR "Unable to register conntrack application "
-				"helper for pptp: %d\n", retcode);
-		ip_ct_proto_gre_fini();
-		return retcode;
-	}
-
-	printk("ip_conntrack_pptp version %s loaded\n", IP_CT_PPTP_VERSION);
-	return 0;
-}
-
-static void __exit ip_conntrack_helper_pptp_fini(void)
-{
-	ip_conntrack_helper_unregister(&pptp);
-	ip_ct_proto_gre_fini();
-	printk("ip_conntrack_pptp version %s unloaded\n", IP_CT_PPTP_VERSION);
-}
-
-module_init(ip_conntrack_helper_pptp_init);
-module_exit(ip_conntrack_helper_pptp_fini);
-
-EXPORT_SYMBOL(ip_nat_pptp_hook_outbound);
-EXPORT_SYMBOL(ip_nat_pptp_hook_inbound);
-EXPORT_SYMBOL(ip_nat_pptp_hook_exp_gre);
-EXPORT_SYMBOL(ip_nat_pptp_hook_expectfn);
diff --git a/net/ipv4/netfilter/ip_conntrack_irc.c b/net/ipv4/netfilter/ip_conntrack_irc.c
deleted file mode 100644
index 053e591..0000000
--- a/net/ipv4/netfilter/ip_conntrack_irc.c
+++ /dev/null
@@ -1,314 +0,0 @@
-/* IRC extension for IP connection tracking, Version 1.21
- * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
- * based on RR's ip_conntrack_ftp.c
- *
- * ip_conntrack_irc.c,v 1.21 2002/02/05 14:49:26 laforge Exp
- *
- *      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.
- **
- *	Module load syntax:
- * 	insmod ip_conntrack_irc.o ports=port1,port2,...port<MAX_PORTS>
- *			    max_dcc_channels=n dcc_timeout=secs
- *
- * 	please give the ports of all IRC servers You wish to connect to.
- *	If You don't specify ports, the default will be port 6667.
- *	With max_dcc_channels you can define the maximum number of not
- *	yet answered DCC channels per IRC session (default 8).
- *	With dcc_timeout you can specify how long the system waits for
- *	an expected DCC channel (default 300 seconds).
- *
- */
-
-#include <linux/module.h>
-#include <linux/netfilter.h>
-#include <linux/ip.h>
-#include <net/checksum.h>
-#include <net/tcp.h>
-
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-#include <linux/netfilter_ipv4/ip_conntrack_irc.h>
-#include <linux/moduleparam.h>
-
-#define MAX_PORTS 8
-static unsigned short ports[MAX_PORTS];
-static int ports_c;
-static unsigned int max_dcc_channels = 8;
-static unsigned int dcc_timeout = 300;
-/* This is slow, but it's simple. --RR */
-static char *irc_buffer;
-static DEFINE_SPINLOCK(irc_buffer_lock);
-
-unsigned int (*ip_nat_irc_hook)(struct sk_buff **pskb,
-				enum ip_conntrack_info ctinfo,
-				unsigned int matchoff,
-				unsigned int matchlen,
-				struct ip_conntrack_expect *exp);
-EXPORT_SYMBOL_GPL(ip_nat_irc_hook);
-
-MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
-MODULE_DESCRIPTION("IRC (DCC) connection tracking helper");
-MODULE_LICENSE("GPL");
-module_param_array(ports, ushort, &ports_c, 0400);
-MODULE_PARM_DESC(ports, "port numbers of IRC servers");
-module_param(max_dcc_channels, uint, 0400);
-MODULE_PARM_DESC(max_dcc_channels, "max number of expected DCC channels per IRC session");
-module_param(dcc_timeout, uint, 0400);
-MODULE_PARM_DESC(dcc_timeout, "timeout on for unestablished DCC channels");
-
-static const char *dccprotos[] = { "SEND ", "CHAT ", "MOVE ", "TSEND ", "SCHAT " };
-#define MINMATCHLEN	5
-
-#if 0
-#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s:" format, \
-				       __FILE__, __FUNCTION__ , ## args)
-#else
-#define DEBUGP(format, args...)
-#endif
-
-static int parse_dcc(char *data, char *data_end, u_int32_t *ip,
-		     u_int16_t *port, char **ad_beg_p, char **ad_end_p)
-/* tries to get the ip_addr and port out of a dcc command
-   return value: -1 on failure, 0 on success
-	data		pointer to first byte of DCC command data
-	data_end	pointer to last byte of dcc command data
-	ip		returns parsed ip of dcc command
-	port		returns parsed port of dcc command
-	ad_beg_p	returns pointer to first byte of addr data
-	ad_end_p	returns pointer to last byte of addr data */
-{
-
-	/* at least 12: "AAAAAAAA P\1\n" */
-	while (*data++ != ' ')
-		if (data > data_end - 12)
-			return -1;
-
-	*ad_beg_p = data;
-	*ip = simple_strtoul(data, &data, 10);
-
-	/* skip blanks between ip and port */
-	while (*data == ' ') {
-		if (data >= data_end)
-			return -1;
-		data++;
-	}
-
-	*port = simple_strtoul(data, &data, 10);
-	*ad_end_p = data;
-
-	return 0;
-}
-
-static int help(struct sk_buff **pskb,
-		struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
-{
-	unsigned int dataoff;
-	struct tcphdr _tcph, *th;
-	char *data, *data_limit, *ib_ptr;
-	int dir = CTINFO2DIR(ctinfo);
-	struct ip_conntrack_expect *exp;
-	u32 seq;
-	u_int32_t dcc_ip;
-	u_int16_t dcc_port;
-	int i, ret = NF_ACCEPT;
-	char *addr_beg_p, *addr_end_p;
-	typeof(ip_nat_irc_hook) ip_nat_irc;
-
-	DEBUGP("entered\n");
-
-	/* If packet is coming from IRC server */
-	if (dir == IP_CT_DIR_REPLY)
-		return NF_ACCEPT;
-
-	/* Until there's been traffic both ways, don't look in packets. */
-	if (ctinfo != IP_CT_ESTABLISHED
-	    && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
-		DEBUGP("Conntrackinfo = %u\n", ctinfo);
-		return NF_ACCEPT;
-	}
-
-	/* Not a full tcp header? */
-	th = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4,
-				sizeof(_tcph), &_tcph);
-	if (th == NULL)
-		return NF_ACCEPT;
-
-	/* No data? */
-	dataoff = (*pskb)->nh.iph->ihl*4 + th->doff*4;
-	if (dataoff >= (*pskb)->len)
-		return NF_ACCEPT;
-
-	spin_lock_bh(&irc_buffer_lock);
-	ib_ptr = skb_header_pointer(*pskb, dataoff,
-				    (*pskb)->len - dataoff, irc_buffer);
-	BUG_ON(ib_ptr == NULL);
-
-	data = ib_ptr;
-	data_limit = ib_ptr + (*pskb)->len - dataoff;
-
-	/* strlen("\1DCC SENT t AAAAAAAA P\1\n")=24
-	 * 5+MINMATCHLEN+strlen("t AAAAAAAA P\1\n")=14 */
-	while (data < (data_limit - (19 + MINMATCHLEN))) {
-		if (memcmp(data, "\1DCC ", 5)) {
-			data++;
-			continue;
-		}
-
-		data += 5;
-		/* we have at least (19+MINMATCHLEN)-5 bytes valid data left */
-
-		DEBUGP("DCC found in master %u.%u.%u.%u:%u %u.%u.%u.%u:%u...\n",
-			NIPQUAD(iph->saddr), ntohs(th->source),
-			NIPQUAD(iph->daddr), ntohs(th->dest));
-
-		for (i = 0; i < ARRAY_SIZE(dccprotos); i++) {
-			if (memcmp(data, dccprotos[i], strlen(dccprotos[i]))) {
-				/* no match */
-				continue;
-			}
-
-			DEBUGP("DCC %s detected\n", dccprotos[i]);
-			data += strlen(dccprotos[i]);
-			/* we have at least
-			 * (19+MINMATCHLEN)-5-dccprotos[i].matchlen bytes valid
-			 * data left (== 14/13 bytes) */
-			if (parse_dcc((char *)data, data_limit, &dcc_ip,
-				       &dcc_port, &addr_beg_p, &addr_end_p)) {
-				/* unable to parse */
-				DEBUGP("unable to parse dcc command\n");
-				continue;
-			}
-			DEBUGP("DCC bound ip/port: %u.%u.%u.%u:%u\n",
-				HIPQUAD(dcc_ip), dcc_port);
-
-			/* dcc_ip can be the internal OR external (NAT'ed) IP
-			 * Tiago Sousa <mirage@kaotik.org> */
-			if (ct->tuplehash[dir].tuple.src.ip != htonl(dcc_ip)
-			    && ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip != htonl(dcc_ip)) {
-				if (net_ratelimit())
-					printk(KERN_WARNING
-						"Forged DCC command from "
-						"%u.%u.%u.%u: %u.%u.%u.%u:%u\n",
-				NIPQUAD(ct->tuplehash[dir].tuple.src.ip),
-						HIPQUAD(dcc_ip), dcc_port);
-
-				continue;
-			}
-
-			exp = ip_conntrack_expect_alloc(ct);
-			if (exp == NULL) {
-				ret = NF_DROP;
-				goto out;
-			}
-
-			/* save position of address in dcc string,
-			 * necessary for NAT */
-			DEBUGP("tcph->seq = %u\n", th->seq);
-			seq = ntohl(th->seq) + (addr_beg_p - ib_ptr);
-
-			/* We refer to the reverse direction ("!dir")
-			 * tuples here, because we're expecting
-			 * something in the other * direction.
-			 * Doesn't matter unless NAT is happening.  */
-			exp->tuple = ((struct ip_conntrack_tuple)
-				{ { 0, { 0 } },
-				  { ct->tuplehash[!dir].tuple.dst.ip,
-				    { .tcp = { htons(dcc_port) } },
-				    IPPROTO_TCP }});
-			exp->mask = ((struct ip_conntrack_tuple)
-				{ { 0, { 0 } },
-				  { htonl(0xFFFFFFFF),
-					{ .tcp = { htons(0xFFFF) } }, 0xFF }});
-			exp->expectfn = NULL;
-			exp->flags = 0;
-			ip_nat_irc = rcu_dereference(ip_nat_irc_hook);
-			if (ip_nat_irc)
-				ret = ip_nat_irc(pskb, ctinfo,
-						 addr_beg_p - ib_ptr,
-						 addr_end_p - addr_beg_p,
-						 exp);
-			else if (ip_conntrack_expect_related(exp) != 0)
-				ret = NF_DROP;
-			ip_conntrack_expect_put(exp);
-			goto out;
-		} /* for .. NUM_DCCPROTO */
-	} /* while data < ... */
-
- out:
-	spin_unlock_bh(&irc_buffer_lock);
-	return ret;
-}
-
-static struct ip_conntrack_helper irc_helpers[MAX_PORTS];
-static char irc_names[MAX_PORTS][sizeof("irc-65535")];
-
-static void ip_conntrack_irc_fini(void);
-
-static int __init ip_conntrack_irc_init(void)
-{
-	int i, ret;
-	struct ip_conntrack_helper *hlpr;
-	char *tmpname;
-
-	if (max_dcc_channels < 1) {
-		printk("ip_conntrack_irc: max_dcc_channels must be a positive integer\n");
-		return -EBUSY;
-	}
-
-	irc_buffer = kmalloc(65536, GFP_KERNEL);
-	if (!irc_buffer)
-		return -ENOMEM;
-
-	/* If no port given, default to standard irc port */
-	if (ports_c == 0)
-		ports[ports_c++] = IRC_PORT;
-
-	for (i = 0; i < ports_c; i++) {
-		hlpr = &irc_helpers[i];
-		hlpr->tuple.src.u.tcp.port = htons(ports[i]);
-		hlpr->tuple.dst.protonum = IPPROTO_TCP;
-		hlpr->mask.src.u.tcp.port = htons(0xFFFF);
-		hlpr->mask.dst.protonum = 0xFF;
-		hlpr->max_expected = max_dcc_channels;
-		hlpr->timeout = dcc_timeout;
-		hlpr->me = THIS_MODULE;
-		hlpr->help = help;
-
-		tmpname = &irc_names[i][0];
-		if (ports[i] == IRC_PORT)
-			sprintf(tmpname, "irc");
-		else
-			sprintf(tmpname, "irc-%d", i);
-		hlpr->name = tmpname;
-
-		DEBUGP("port #%d: %d\n", i, ports[i]);
-
-		ret = ip_conntrack_helper_register(hlpr);
-
-		if (ret) {
-			printk("ip_conntrack_irc: ERROR registering port %d\n",
-				ports[i]);
-			ip_conntrack_irc_fini();
-			return -EBUSY;
-		}
-	}
-	return 0;
-}
-
-/* This function is intentionally _NOT_ defined as __exit, because
- * it is needed by the init function */
-static void ip_conntrack_irc_fini(void)
-{
-	int i;
-	for (i = 0; i < ports_c; i++) {
-		DEBUGP("unregistering port %d\n",
-		       ports[i]);
-		ip_conntrack_helper_unregister(&irc_helpers[i]);
-	}
-	kfree(irc_buffer);
-}
-
-module_init(ip_conntrack_irc_init);
-module_exit(ip_conntrack_irc_fini);
diff --git a/net/ipv4/netfilter/ip_conntrack_netbios_ns.c b/net/ipv4/netfilter/ip_conntrack_netbios_ns.c
deleted file mode 100644
index cc6dd49..0000000
--- a/net/ipv4/netfilter/ip_conntrack_netbios_ns.c
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- *      NetBIOS name service broadcast connection tracking helper
- *
- *      (c) 2005 Patrick McHardy <kaber@trash.net>
- *
- *      This program is free software; you can redistribute it and/or
- *      modify it under the terms of the GNU General Public License
- *      as published by the Free Software Foundation; either version
- *      2 of the License, or (at your option) any later version.
- */
-/*
- *      This helper tracks locally originating NetBIOS name service
- *      requests by issuing permanent expectations (valid until
- *      timing out) matching all reply connections from the
- *      destination network. The only NetBIOS specific thing is
- *      actually the port number.
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/inetdevice.h>
-#include <linux/if_addr.h>
-#include <linux/in.h>
-#include <linux/ip.h>
-#include <net/route.h>
-
-#include <linux/netfilter.h>
-#include <linux/netfilter_ipv4.h>
-#include <linux/netfilter_ipv4/ip_conntrack.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-
-#define NMBD_PORT	137
-
-MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
-MODULE_DESCRIPTION("NetBIOS name service broadcast connection tracking helper");
-MODULE_LICENSE("GPL");
-
-static unsigned int timeout = 3;
-module_param(timeout, uint, 0400);
-MODULE_PARM_DESC(timeout, "timeout for master connection/replies in seconds");
-
-static int help(struct sk_buff **pskb,
-		struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
-{
-	struct ip_conntrack_expect *exp;
-	struct iphdr *iph = (*pskb)->nh.iph;
-	struct rtable *rt = (struct rtable *)(*pskb)->dst;
-	struct in_device *in_dev;
-	__be32 mask = 0;
-
-	/* we're only interested in locally generated packets */
-	if ((*pskb)->sk == NULL)
-		goto out;
-	if (rt == NULL || !(rt->rt_flags & RTCF_BROADCAST))
-		goto out;
-	if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL)
-		goto out;
-
-	rcu_read_lock();
-	in_dev = __in_dev_get_rcu(rt->u.dst.dev);
-	if (in_dev != NULL) {
-		for_primary_ifa(in_dev) {
-			if (ifa->ifa_broadcast == iph->daddr) {
-				mask = ifa->ifa_mask;
-				break;
-			}
-		} endfor_ifa(in_dev);
-	}
-	rcu_read_unlock();
-
-	if (mask == 0)
-		goto out;
-
-	exp = ip_conntrack_expect_alloc(ct);
-	if (exp == NULL)
-		goto out;
-
-	exp->tuple                = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
-	exp->tuple.src.u.udp.port = htons(NMBD_PORT);
-
-	exp->mask.src.ip          = mask;
-	exp->mask.src.u.udp.port  = htons(0xFFFF);
-	exp->mask.dst.ip          = htonl(0xFFFFFFFF);
-	exp->mask.dst.u.udp.port  = htons(0xFFFF);
-	exp->mask.dst.protonum    = 0xFF;
-
-	exp->expectfn             = NULL;
-	exp->flags                = IP_CT_EXPECT_PERMANENT;
-
-	ip_conntrack_expect_related(exp);
-	ip_conntrack_expect_put(exp);
-
-	ip_ct_refresh(ct, *pskb, timeout * HZ);
-out:
-	return NF_ACCEPT;
-}
-
-static struct ip_conntrack_helper helper = {
-	.name			= "netbios-ns",
-	.tuple = {
-		.src = {
-			.u = {
-				.udp = {
-					.port	= __constant_htons(NMBD_PORT),
-				}
-			}
-		},
-		.dst = {
-			.protonum	= IPPROTO_UDP,
-		},
-	},
-	.mask = {
-		.src = {
-			.u = {
-				.udp = {
-					.port	= __constant_htons(0xFFFF),
-				}
-			}
-		},
-		.dst = {
-			.protonum	= 0xFF,
-		},
-	},
-	.max_expected		= 1,
-	.me			= THIS_MODULE,
-	.help			= help,
-};
-
-static int __init ip_conntrack_netbios_ns_init(void)
-{
-	helper.timeout = timeout;
-	return ip_conntrack_helper_register(&helper);
-}
-
-static void __exit ip_conntrack_netbios_ns_fini(void)
-{
-	ip_conntrack_helper_unregister(&helper);
-}
-
-module_init(ip_conntrack_netbios_ns_init);
-module_exit(ip_conntrack_netbios_ns_fini);
diff --git a/net/ipv4/netfilter/ip_conntrack_netlink.c b/net/ipv4/netfilter/ip_conntrack_netlink.c
deleted file mode 100644
index 9228b76c..0000000
--- a/net/ipv4/netfilter/ip_conntrack_netlink.c
+++ /dev/null
@@ -1,1577 +0,0 @@
-/* Connection tracking via netlink socket. Allows for user space
- * protocol helpers and general trouble making from userspace.
- *
- * (C) 2001 by Jay Schulist <jschlst@samba.org>
- * (C) 2002-2005 by Harald Welte <laforge@gnumonks.org>
- * (C) 2003 by Patrick Mchardy <kaber@trash.net>
- * (C) 2005-2006 by Pablo Neira Ayuso <pablo@eurodev.net>
- *
- * I've reworked this stuff to use attributes instead of conntrack
- * structures. 5.44 am. I need more tea. --pablo 05/07/11.
- *
- * Initial connection tracking via netlink development funded and
- * generally made possible by Network Robots, Inc. (www.networkrobots.com)
- *
- * Further development of this code funded by Astaro AG (http://www.astaro.com)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/timer.h>
-#include <linux/skbuff.h>
-#include <linux/errno.h>
-#include <linux/netlink.h>
-#include <linux/spinlock.h>
-#include <linux/interrupt.h>
-#include <linux/notifier.h>
-
-#include <linux/netfilter.h>
-#include <linux/netfilter_ipv4/ip_conntrack.h>
-#include <linux/netfilter_ipv4/ip_conntrack_core.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
-#include <linux/netfilter_ipv4/ip_nat_protocol.h>
-
-#include <linux/netfilter/nfnetlink.h>
-#include <linux/netfilter/nfnetlink_conntrack.h>
-
-MODULE_LICENSE("GPL");
-
-static char __initdata version[] = "0.90";
-
-static inline int
-ctnetlink_dump_tuples_proto(struct sk_buff *skb,
-			    const struct ip_conntrack_tuple *tuple,
-			    struct ip_conntrack_protocol *proto)
-{
-	int ret = 0;
-	struct nfattr *nest_parms = NFA_NEST(skb, CTA_TUPLE_PROTO);
-
-	NFA_PUT(skb, CTA_PROTO_NUM, sizeof(u_int8_t), &tuple->dst.protonum);
-
-	if (likely(proto->tuple_to_nfattr))
-		ret = proto->tuple_to_nfattr(skb, tuple);
-
-	NFA_NEST_END(skb, nest_parms);
-
-	return ret;
-
-nfattr_failure:
-	return -1;
-}
-
-static inline int
-ctnetlink_dump_tuples_ip(struct sk_buff *skb,
-			 const struct ip_conntrack_tuple *tuple)
-{
-	struct nfattr *nest_parms = NFA_NEST(skb, CTA_TUPLE_IP);
-
-	NFA_PUT(skb, CTA_IP_V4_SRC, sizeof(__be32), &tuple->src.ip);
-	NFA_PUT(skb, CTA_IP_V4_DST, sizeof(__be32), &tuple->dst.ip);
-
-	NFA_NEST_END(skb, nest_parms);
-
-	return 0;
-
-nfattr_failure:
-	return -1;
-}
-
-static inline int
-ctnetlink_dump_tuples(struct sk_buff *skb,
-		      const struct ip_conntrack_tuple *tuple)
-{
-	int ret;
-	struct ip_conntrack_protocol *proto;
-
-	ret = ctnetlink_dump_tuples_ip(skb, tuple);
-	if (unlikely(ret < 0))
-		return ret;
-
-	proto = ip_conntrack_proto_find_get(tuple->dst.protonum);
-	ret = ctnetlink_dump_tuples_proto(skb, tuple, proto);
-	ip_conntrack_proto_put(proto);
-
-	return ret;
-}
-
-static inline int
-ctnetlink_dump_status(struct sk_buff *skb, const struct ip_conntrack *ct)
-{
-	__be32 status = htonl((u_int32_t) ct->status);
-	NFA_PUT(skb, CTA_STATUS, sizeof(status), &status);
-	return 0;
-
-nfattr_failure:
-	return -1;
-}
-
-static inline int
-ctnetlink_dump_timeout(struct sk_buff *skb, const struct ip_conntrack *ct)
-{
-	long timeout_l = ct->timeout.expires - jiffies;
-	__be32 timeout;
-
-	if (timeout_l < 0)
-		timeout = 0;
-	else
-		timeout = htonl(timeout_l / HZ);
-
-	NFA_PUT(skb, CTA_TIMEOUT, sizeof(timeout), &timeout);
-	return 0;
-
-nfattr_failure:
-	return -1;
-}
-
-static inline int
-ctnetlink_dump_protoinfo(struct sk_buff *skb, const struct ip_conntrack *ct)
-{
-	struct ip_conntrack_protocol *proto = ip_conntrack_proto_find_get(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum);
-
-	struct nfattr *nest_proto;
-	int ret;
-
-	if (!proto->to_nfattr) {
-		ip_conntrack_proto_put(proto);
-		return 0;
-	}
-
-	nest_proto = NFA_NEST(skb, CTA_PROTOINFO);
-
-	ret = proto->to_nfattr(skb, nest_proto, ct);
-
-	ip_conntrack_proto_put(proto);
-
-	NFA_NEST_END(skb, nest_proto);
-
-	return ret;
-
-nfattr_failure:
-	ip_conntrack_proto_put(proto);
-	return -1;
-}
-
-static inline int
-ctnetlink_dump_helpinfo(struct sk_buff *skb, const struct ip_conntrack *ct)
-{
-	struct nfattr *nest_helper;
-
-	if (!ct->helper)
-		return 0;
-
-	nest_helper = NFA_NEST(skb, CTA_HELP);
-	NFA_PUT(skb, CTA_HELP_NAME, strlen(ct->helper->name), ct->helper->name);
-
-	if (ct->helper->to_nfattr)
-		ct->helper->to_nfattr(skb, ct);
-
-	NFA_NEST_END(skb, nest_helper);
-
-	return 0;
-
-nfattr_failure:
-	return -1;
-}
-
-#ifdef CONFIG_IP_NF_CT_ACCT
-static inline int
-ctnetlink_dump_counters(struct sk_buff *skb, const struct ip_conntrack *ct,
-			enum ip_conntrack_dir dir)
-{
-	enum ctattr_type type = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG;
-	struct nfattr *nest_count = NFA_NEST(skb, type);
-	__be32 tmp;
-
-	tmp = htonl(ct->counters[dir].packets);
-	NFA_PUT(skb, CTA_COUNTERS32_PACKETS, sizeof(__be32), &tmp);
-
-	tmp = htonl(ct->counters[dir].bytes);
-	NFA_PUT(skb, CTA_COUNTERS32_BYTES, sizeof(__be32), &tmp);
-
-	NFA_NEST_END(skb, nest_count);
-
-	return 0;
-
-nfattr_failure:
-	return -1;
-}
-#else
-#define ctnetlink_dump_counters(a, b, c) (0)
-#endif
-
-#ifdef CONFIG_IP_NF_CONNTRACK_MARK
-static inline int
-ctnetlink_dump_mark(struct sk_buff *skb, const struct ip_conntrack *ct)
-{
-	__be32 mark = htonl(ct->mark);
-
-	NFA_PUT(skb, CTA_MARK, sizeof(__be32), &mark);
-	return 0;
-
-nfattr_failure:
-	return -1;
-}
-#else
-#define ctnetlink_dump_mark(a, b) (0)
-#endif
-
-static inline int
-ctnetlink_dump_id(struct sk_buff *skb, const struct ip_conntrack *ct)
-{
-	__be32 id = htonl(ct->id);
-	NFA_PUT(skb, CTA_ID, sizeof(__be32), &id);
-	return 0;
-
-nfattr_failure:
-	return -1;
-}
-
-static inline int
-ctnetlink_dump_use(struct sk_buff *skb, const struct ip_conntrack *ct)
-{
-	__be32 use = htonl(atomic_read(&ct->ct_general.use));
-
-	NFA_PUT(skb, CTA_USE, sizeof(__be32), &use);
-	return 0;
-
-nfattr_failure:
-	return -1;
-}
-
-#define tuple(ct, dir) (&(ct)->tuplehash[dir].tuple)
-
-static int
-ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
-		    int event, int nowait,
-		    const struct ip_conntrack *ct)
-{
-	struct nlmsghdr *nlh;
-	struct nfgenmsg *nfmsg;
-	struct nfattr *nest_parms;
-	unsigned char *b;
-
-	b = skb->tail;
-
-	event |= NFNL_SUBSYS_CTNETLINK << 8;
-	nlh    = NLMSG_PUT(skb, pid, seq, event, sizeof(struct nfgenmsg));
-	nfmsg  = NLMSG_DATA(nlh);
-
-	nlh->nlmsg_flags    = (nowait && pid) ? NLM_F_MULTI : 0;
-	nfmsg->nfgen_family = AF_INET;
-	nfmsg->version      = NFNETLINK_V0;
-	nfmsg->res_id	    = 0;
-
-	nest_parms = NFA_NEST(skb, CTA_TUPLE_ORIG);
-	if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_ORIGINAL)) < 0)
-		goto nfattr_failure;
-	NFA_NEST_END(skb, nest_parms);
-
-	nest_parms = NFA_NEST(skb, CTA_TUPLE_REPLY);
-	if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_REPLY)) < 0)
-		goto nfattr_failure;
-	NFA_NEST_END(skb, nest_parms);
-
-	if (ctnetlink_dump_status(skb, ct) < 0 ||
-	    ctnetlink_dump_timeout(skb, ct) < 0 ||
-	    ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 ||
-	    ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0 ||
-	    ctnetlink_dump_protoinfo(skb, ct) < 0 ||
-	    ctnetlink_dump_helpinfo(skb, ct) < 0 ||
-	    ctnetlink_dump_mark(skb, ct) < 0 ||
-	    ctnetlink_dump_id(skb, ct) < 0 ||
-	    ctnetlink_dump_use(skb, ct) < 0)
-		goto nfattr_failure;
-
-	nlh->nlmsg_len = skb->tail - b;
-	return skb->len;
-
-nlmsg_failure:
-nfattr_failure:
-	skb_trim(skb, b - skb->data);
-	return -1;
-}
-
-#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS
-static int ctnetlink_conntrack_event(struct notifier_block *this,
-				     unsigned long events, void *ptr)
-{
-	struct nlmsghdr *nlh;
-	struct nfgenmsg *nfmsg;
-	struct nfattr *nest_parms;
-	struct ip_conntrack *ct = (struct ip_conntrack *)ptr;
-	struct sk_buff *skb;
-	unsigned int type;
-	unsigned char *b;
-	unsigned int flags = 0, group;
-
-	/* ignore our fake conntrack entry */
-	if (ct == &ip_conntrack_untracked)
-		return NOTIFY_DONE;
-
-	if (events & IPCT_DESTROY) {
-		type = IPCTNL_MSG_CT_DELETE;
-		group = NFNLGRP_CONNTRACK_DESTROY;
-	} else if (events & (IPCT_NEW | IPCT_RELATED)) {
-		type = IPCTNL_MSG_CT_NEW;
-		flags = NLM_F_CREATE|NLM_F_EXCL;
-		group = NFNLGRP_CONNTRACK_NEW;
-	} else if (events & (IPCT_STATUS | IPCT_PROTOINFO)) {
-		type = IPCTNL_MSG_CT_NEW;
-		group = NFNLGRP_CONNTRACK_UPDATE;
-	} else
-		return NOTIFY_DONE;
-
-	if (!nfnetlink_has_listeners(group))
-		return NOTIFY_DONE;
-
-	skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
-	if (!skb)
-		return NOTIFY_DONE;
-
-	b = skb->tail;
-
-	type |= NFNL_SUBSYS_CTNETLINK << 8;
-	nlh   = NLMSG_PUT(skb, 0, 0, type, sizeof(struct nfgenmsg));
-	nfmsg = NLMSG_DATA(nlh);
-
-	nlh->nlmsg_flags    = flags;
-	nfmsg->nfgen_family = AF_INET;
-	nfmsg->version	= NFNETLINK_V0;
-	nfmsg->res_id	= 0;
-
-	nest_parms = NFA_NEST(skb, CTA_TUPLE_ORIG);
-	if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_ORIGINAL)) < 0)
-		goto nfattr_failure;
-	NFA_NEST_END(skb, nest_parms);
-
-	nest_parms = NFA_NEST(skb, CTA_TUPLE_REPLY);
-	if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_REPLY)) < 0)
-		goto nfattr_failure;
-	NFA_NEST_END(skb, nest_parms);
-
-	if (events & IPCT_DESTROY) {
-		if (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 ||
-		    ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0)
-			goto nfattr_failure;
-	} else {
-		if (ctnetlink_dump_status(skb, ct) < 0)
-			goto nfattr_failure;
-
-		if (ctnetlink_dump_timeout(skb, ct) < 0)
-			goto nfattr_failure;
-
-		if (events & IPCT_PROTOINFO
-		    && ctnetlink_dump_protoinfo(skb, ct) < 0)
-			goto nfattr_failure;
-
-		if ((events & IPCT_HELPER || ct->helper)
-		    && ctnetlink_dump_helpinfo(skb, ct) < 0)
-			goto nfattr_failure;
-
-#ifdef CONFIG_IP_NF_CONNTRACK_MARK
-		if ((events & IPCT_MARK || ct->mark)
-		    && ctnetlink_dump_mark(skb, ct) < 0)
-			goto nfattr_failure;
-#endif
-
-		if (events & IPCT_COUNTER_FILLING &&
-		    (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 ||
-		     ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0))
-			goto nfattr_failure;
-	}
-
-	nlh->nlmsg_len = skb->tail - b;
-	nfnetlink_send(skb, 0, group, 0);
-	return NOTIFY_DONE;
-
-nlmsg_failure:
-nfattr_failure:
-	kfree_skb(skb);
-	return NOTIFY_DONE;
-}
-#endif /* CONFIG_IP_NF_CONNTRACK_EVENTS */
-
-static int ctnetlink_done(struct netlink_callback *cb)
-{
-	if (cb->args[1])
-		ip_conntrack_put((struct ip_conntrack *)cb->args[1]);
-	return 0;
-}
-
-static int
-ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
-{
-	struct ip_conntrack *ct, *last;
-	struct ip_conntrack_tuple_hash *h;
-	struct list_head *i;
-
-	read_lock_bh(&ip_conntrack_lock);
-	last = (struct ip_conntrack *)cb->args[1];
-	for (; cb->args[0] < ip_conntrack_htable_size; cb->args[0]++) {
-restart:
-		list_for_each_prev(i, &ip_conntrack_hash[cb->args[0]]) {
-			h = (struct ip_conntrack_tuple_hash *) i;
-			if (DIRECTION(h) != IP_CT_DIR_ORIGINAL)
-				continue;
-			ct = tuplehash_to_ctrack(h);
-			if (cb->args[1]) {
-				if (ct != last)
-					continue;
-				cb->args[1] = 0;
-			}
-			if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid,
-						cb->nlh->nlmsg_seq,
-						IPCTNL_MSG_CT_NEW,
-						1, ct) < 0) {
-				nf_conntrack_get(&ct->ct_general);
-				cb->args[1] = (unsigned long)ct;
-				goto out;
-			}
-#ifdef CONFIG_NF_CT_ACCT
-			if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) ==
-						IPCTNL_MSG_CT_GET_CTRZERO)
-				memset(&ct->counters, 0, sizeof(ct->counters));
-#endif
-		}
-		if (cb->args[1]) {
-			cb->args[1] = 0;
-			goto restart;
-		}
-	}
-out:
-	read_unlock_bh(&ip_conntrack_lock);
-	if (last)
-		ip_conntrack_put(last);
-
-	return skb->len;
-}
-
-static const size_t cta_min_ip[CTA_IP_MAX] = {
-	[CTA_IP_V4_SRC-1]	= sizeof(__be32),
-	[CTA_IP_V4_DST-1]	= sizeof(__be32),
-};
-
-static inline int
-ctnetlink_parse_tuple_ip(struct nfattr *attr, struct ip_conntrack_tuple *tuple)
-{
-	struct nfattr *tb[CTA_IP_MAX];
-
-	nfattr_parse_nested(tb, CTA_IP_MAX, attr);
-
-	if (nfattr_bad_size(tb, CTA_IP_MAX, cta_min_ip))
-		return -EINVAL;
-
-	if (!tb[CTA_IP_V4_SRC-1])
-		return -EINVAL;
-	tuple->src.ip = *(__be32 *)NFA_DATA(tb[CTA_IP_V4_SRC-1]);
-
-	if (!tb[CTA_IP_V4_DST-1])
-		return -EINVAL;
-	tuple->dst.ip = *(__be32 *)NFA_DATA(tb[CTA_IP_V4_DST-1]);
-
-	return 0;
-}
-
-static const size_t cta_min_proto[CTA_PROTO_MAX] = {
-	[CTA_PROTO_NUM-1]	= sizeof(u_int8_t),
-	[CTA_PROTO_SRC_PORT-1]	= sizeof(u_int16_t),
-	[CTA_PROTO_DST_PORT-1]	= sizeof(u_int16_t),
-	[CTA_PROTO_ICMP_TYPE-1]	= sizeof(u_int8_t),
-	[CTA_PROTO_ICMP_CODE-1]	= sizeof(u_int8_t),
-	[CTA_PROTO_ICMP_ID-1]	= sizeof(u_int16_t),
-};
-
-static inline int
-ctnetlink_parse_tuple_proto(struct nfattr *attr,
-			    struct ip_conntrack_tuple *tuple)
-{
-	struct nfattr *tb[CTA_PROTO_MAX];
-	struct ip_conntrack_protocol *proto;
-	int ret = 0;
-
-	nfattr_parse_nested(tb, CTA_PROTO_MAX, attr);
-
-	if (nfattr_bad_size(tb, CTA_PROTO_MAX, cta_min_proto))
-		return -EINVAL;
-
-	if (!tb[CTA_PROTO_NUM-1])
-		return -EINVAL;
-	tuple->dst.protonum = *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_NUM-1]);
-
-	proto = ip_conntrack_proto_find_get(tuple->dst.protonum);
-
-	if (likely(proto->nfattr_to_tuple))
-		ret = proto->nfattr_to_tuple(tb, tuple);
-
-	ip_conntrack_proto_put(proto);
-
-	return ret;
-}
-
-static inline int
-ctnetlink_parse_tuple(struct nfattr *cda[], struct ip_conntrack_tuple *tuple,
-		      enum ctattr_tuple type)
-{
-	struct nfattr *tb[CTA_TUPLE_MAX];
-	int err;
-
-	memset(tuple, 0, sizeof(*tuple));
-
-	nfattr_parse_nested(tb, CTA_TUPLE_MAX, cda[type-1]);
-
-	if (!tb[CTA_TUPLE_IP-1])
-		return -EINVAL;
-
-	err = ctnetlink_parse_tuple_ip(tb[CTA_TUPLE_IP-1], tuple);
-	if (err < 0)
-		return err;
-
-	if (!tb[CTA_TUPLE_PROTO-1])
-		return -EINVAL;
-
-	err = ctnetlink_parse_tuple_proto(tb[CTA_TUPLE_PROTO-1], tuple);
-	if (err < 0)
-		return err;
-
-	/* orig and expect tuples get DIR_ORIGINAL */
-	if (type == CTA_TUPLE_REPLY)
-		tuple->dst.dir = IP_CT_DIR_REPLY;
-	else
-		tuple->dst.dir = IP_CT_DIR_ORIGINAL;
-
-	return 0;
-}
-
-#ifdef CONFIG_IP_NF_NAT_NEEDED
-static const size_t cta_min_protonat[CTA_PROTONAT_MAX] = {
-	[CTA_PROTONAT_PORT_MIN-1]	= sizeof(u_int16_t),
-	[CTA_PROTONAT_PORT_MAX-1]	= sizeof(u_int16_t),
-};
-
-static int ctnetlink_parse_nat_proto(struct nfattr *attr,
-				     const struct ip_conntrack *ct,
-				     struct ip_nat_range *range)
-{
-	struct nfattr *tb[CTA_PROTONAT_MAX];
-	struct ip_nat_protocol *npt;
-
-	nfattr_parse_nested(tb, CTA_PROTONAT_MAX, attr);
-
-	if (nfattr_bad_size(tb, CTA_PROTONAT_MAX, cta_min_protonat))
-		return -EINVAL;
-
-	npt = ip_nat_proto_find_get(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum);
-
-	if (!npt->nfattr_to_range) {
-		ip_nat_proto_put(npt);
-		return 0;
-	}
-
-	/* nfattr_to_range returns 1 if it parsed, 0 if not, neg. on error */
-	if (npt->nfattr_to_range(tb, range) > 0)
-		range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
-
-	ip_nat_proto_put(npt);
-
-	return 0;
-}
-
-static const size_t cta_min_nat[CTA_NAT_MAX] = {
-	[CTA_NAT_MINIP-1]       = sizeof(__be32),
-	[CTA_NAT_MAXIP-1]       = sizeof(__be32),
-};
-
-static inline int
-ctnetlink_parse_nat(struct nfattr *nat,
-		    const struct ip_conntrack *ct, struct ip_nat_range *range)
-{
-	struct nfattr *tb[CTA_NAT_MAX];
-	int err;
-
-	memset(range, 0, sizeof(*range));
-
-	nfattr_parse_nested(tb, CTA_NAT_MAX, nat);
-
-	if (nfattr_bad_size(tb, CTA_NAT_MAX, cta_min_nat))
-		return -EINVAL;
-
-	if (tb[CTA_NAT_MINIP-1])
-		range->min_ip = *(__be32 *)NFA_DATA(tb[CTA_NAT_MINIP-1]);
-
-	if (!tb[CTA_NAT_MAXIP-1])
-		range->max_ip = range->min_ip;
-	else
-		range->max_ip = *(__be32 *)NFA_DATA(tb[CTA_NAT_MAXIP-1]);
-
-	if (range->min_ip)
-		range->flags |= IP_NAT_RANGE_MAP_IPS;
-
-	if (!tb[CTA_NAT_PROTO-1])
-		return 0;
-
-	err = ctnetlink_parse_nat_proto(tb[CTA_NAT_PROTO-1], ct, range);
-	if (err < 0)
-		return err;
-
-	return 0;
-}
-#endif
-
-static inline int
-ctnetlink_parse_help(struct nfattr *attr, char **helper_name)
-{
-	struct nfattr *tb[CTA_HELP_MAX];
-
-	nfattr_parse_nested(tb, CTA_HELP_MAX, attr);
-
-	if (!tb[CTA_HELP_NAME-1])
-		return -EINVAL;
-
-	*helper_name = NFA_DATA(tb[CTA_HELP_NAME-1]);
-
-	return 0;
-}
-
-static const size_t cta_min[CTA_MAX] = {
-	[CTA_STATUS-1] 		= sizeof(__be32),
-	[CTA_TIMEOUT-1] 	= sizeof(__be32),
-	[CTA_MARK-1]		= sizeof(__be32),
-	[CTA_USE-1]		= sizeof(__be32),
-	[CTA_ID-1]		= sizeof(__be32)
-};
-
-static int
-ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
-			struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
-{
-	struct ip_conntrack_tuple_hash *h;
-	struct ip_conntrack_tuple tuple;
-	struct ip_conntrack *ct;
-	int err = 0;
-
-	if (nfattr_bad_size(cda, CTA_MAX, cta_min))
-		return -EINVAL;
-
-	if (cda[CTA_TUPLE_ORIG-1])
-		err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG);
-	else if (cda[CTA_TUPLE_REPLY-1])
-		err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY);
-	else {
-		/* Flush the whole table */
-		ip_conntrack_flush();
-		return 0;
-	}
-
-	if (err < 0)
-		return err;
-
-	h = ip_conntrack_find_get(&tuple, NULL);
-	if (!h)
-		return -ENOENT;
-
-	ct = tuplehash_to_ctrack(h);
-
-	if (cda[CTA_ID-1]) {
-		u_int32_t id = ntohl(*(__be32 *)NFA_DATA(cda[CTA_ID-1]));
-		if (ct->id != id) {
-			ip_conntrack_put(ct);
-			return -ENOENT;
-		}
-	}
-	if (del_timer(&ct->timeout))
-		ct->timeout.function((unsigned long)ct);
-
-	ip_conntrack_put(ct);
-
-	return 0;
-}
-
-static int
-ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
-			struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
-{
-	struct ip_conntrack_tuple_hash *h;
-	struct ip_conntrack_tuple tuple;
-	struct ip_conntrack *ct;
-	struct sk_buff *skb2 = NULL;
-	int err = 0;
-
-	if (nlh->nlmsg_flags & NLM_F_DUMP) {
-		struct nfgenmsg *msg = NLMSG_DATA(nlh);
-		u32 rlen;
-
-		if (msg->nfgen_family != AF_INET)
-			return -EAFNOSUPPORT;
-
-#ifndef CONFIG_IP_NF_CT_ACCT
-		if (NFNL_MSG_TYPE(nlh->nlmsg_type) == IPCTNL_MSG_CT_GET_CTRZERO)
-			return -ENOTSUPP;
-#endif
-		if ((*errp = netlink_dump_start(ctnl, skb, nlh,
-						ctnetlink_dump_table,
-						ctnetlink_done)) != 0)
-			return -EINVAL;
-
-		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
-		if (rlen > skb->len)
-			rlen = skb->len;
-		skb_pull(skb, rlen);
-		return 0;
-	}
-
-	if (nfattr_bad_size(cda, CTA_MAX, cta_min))
-		return -EINVAL;
-
-	if (cda[CTA_TUPLE_ORIG-1])
-		err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG);
-	else if (cda[CTA_TUPLE_REPLY-1])
-		err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY);
-	else
-		return -EINVAL;
-
-	if (err < 0)
-		return err;
-
-	h = ip_conntrack_find_get(&tuple, NULL);
-	if (!h)
-		return -ENOENT;
-
-	ct = tuplehash_to_ctrack(h);
-
-	err = -ENOMEM;
-	skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
-	if (!skb2) {
-		ip_conntrack_put(ct);
-		return -ENOMEM;
-	}
-
-	err = ctnetlink_fill_info(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq,
-				  IPCTNL_MSG_CT_NEW, 1, ct);
-	ip_conntrack_put(ct);
-	if (err <= 0)
-		goto free;
-
-	err = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT);
-	if (err < 0)
-		goto out;
-
-	return 0;
-
-free:
-	kfree_skb(skb2);
-out:
-	return err;
-}
-
-static inline int
-ctnetlink_change_status(struct ip_conntrack *ct, struct nfattr *cda[])
-{
-	unsigned long d;
-	unsigned status = ntohl(*(__be32 *)NFA_DATA(cda[CTA_STATUS-1]));
-	d = ct->status ^ status;
-
-	if (d & (IPS_EXPECTED|IPS_CONFIRMED|IPS_DYING))
-		/* unchangeable */
-		return -EINVAL;
-
-	if (d & IPS_SEEN_REPLY && !(status & IPS_SEEN_REPLY))
-		/* SEEN_REPLY bit can only be set */
-		return -EINVAL;
-
-
-	if (d & IPS_ASSURED && !(status & IPS_ASSURED))
-		/* ASSURED bit can only be set */
-		return -EINVAL;
-
-	if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) {
-#ifndef CONFIG_IP_NF_NAT_NEEDED
-		return -EINVAL;
-#else
-		struct ip_nat_range range;
-
-		if (cda[CTA_NAT_DST-1]) {
-			if (ctnetlink_parse_nat(cda[CTA_NAT_DST-1], ct,
-						&range) < 0)
-				return -EINVAL;
-			if (ip_nat_initialized(ct,
-					       HOOK2MANIP(NF_IP_PRE_ROUTING)))
-				return -EEXIST;
-			ip_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING);
-		}
-		if (cda[CTA_NAT_SRC-1]) {
-			if (ctnetlink_parse_nat(cda[CTA_NAT_SRC-1], ct,
-						&range) < 0)
-				return -EINVAL;
-			if (ip_nat_initialized(ct,
-					       HOOK2MANIP(NF_IP_POST_ROUTING)))
-				return -EEXIST;
-			ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING);
-		}
-#endif
-	}
-
-	/* Be careful here, modifying NAT bits can screw up things,
-	 * so don't let users modify them directly if they don't pass
-	 * ip_nat_range. */
-	ct->status |= status & ~(IPS_NAT_DONE_MASK | IPS_NAT_MASK);
-	return 0;
-}
-
-
-static inline int
-ctnetlink_change_helper(struct ip_conntrack *ct, struct nfattr *cda[])
-{
-	struct ip_conntrack_helper *helper;
-	char *helpname;
-	int err;
-
-	/* don't change helper of sibling connections */
-	if (ct->master)
-		return -EINVAL;
-
-	err = ctnetlink_parse_help(cda[CTA_HELP-1], &helpname);
-	if (err < 0)
-		return err;
-
-	helper = __ip_conntrack_helper_find_byname(helpname);
-	if (!helper) {
-		if (!strcmp(helpname, ""))
-			helper = NULL;
-		else
-			return -EINVAL;
-	}
-
-	if (ct->helper) {
-		if (!helper) {
-			/* we had a helper before ... */
-			ip_ct_remove_expectations(ct);
-			ct->helper = NULL;
-		} else {
-			/* need to zero data of old helper */
-			memset(&ct->help, 0, sizeof(ct->help));
-		}
-	}
-
-	ct->helper = helper;
-
-	return 0;
-}
-
-static inline int
-ctnetlink_change_timeout(struct ip_conntrack *ct, struct nfattr *cda[])
-{
-	u_int32_t timeout = ntohl(*(__be32 *)NFA_DATA(cda[CTA_TIMEOUT-1]));
-
-	if (!del_timer(&ct->timeout))
-		return -ETIME;
-
-	ct->timeout.expires = jiffies + timeout * HZ;
-	add_timer(&ct->timeout);
-
-	return 0;
-}
-
-static inline int
-ctnetlink_change_protoinfo(struct ip_conntrack *ct, struct nfattr *cda[])
-{
-	struct nfattr *tb[CTA_PROTOINFO_MAX], *attr = cda[CTA_PROTOINFO-1];
-	struct ip_conntrack_protocol *proto;
-	u_int16_t npt = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum;
-	int err = 0;
-
-	nfattr_parse_nested(tb, CTA_PROTOINFO_MAX, attr);
-
-	proto = ip_conntrack_proto_find_get(npt);
-
-	if (proto->from_nfattr)
-		err = proto->from_nfattr(tb, ct);
-	ip_conntrack_proto_put(proto);
-
-	return err;
-}
-
-static int
-ctnetlink_change_conntrack(struct ip_conntrack *ct, struct nfattr *cda[])
-{
-	int err;
-
-	if (cda[CTA_HELP-1]) {
-		err = ctnetlink_change_helper(ct, cda);
-		if (err < 0)
-			return err;
-	}
-
-	if (cda[CTA_TIMEOUT-1]) {
-		err = ctnetlink_change_timeout(ct, cda);
-		if (err < 0)
-			return err;
-	}
-
-	if (cda[CTA_STATUS-1]) {
-		err = ctnetlink_change_status(ct, cda);
-		if (err < 0)
-			return err;
-	}
-
-	if (cda[CTA_PROTOINFO-1]) {
-		err = ctnetlink_change_protoinfo(ct, cda);
-		if (err < 0)
-			return err;
-	}
-
-#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
-	if (cda[CTA_MARK-1])
-		ct->mark = ntohl(*(__be32 *)NFA_DATA(cda[CTA_MARK-1]));
-#endif
-
-	return 0;
-}
-
-static int
-ctnetlink_create_conntrack(struct nfattr *cda[],
-			   struct ip_conntrack_tuple *otuple,
-			   struct ip_conntrack_tuple *rtuple)
-{
-	struct ip_conntrack *ct;
-	int err = -EINVAL;
-
-	ct = ip_conntrack_alloc(otuple, rtuple);
-	if (ct == NULL || IS_ERR(ct))
-		return -ENOMEM;
-
-	if (!cda[CTA_TIMEOUT-1])
-		goto err;
-	ct->timeout.expires = ntohl(*(__be32 *)NFA_DATA(cda[CTA_TIMEOUT-1]));
-
-	ct->timeout.expires = jiffies + ct->timeout.expires * HZ;
-	ct->status |= IPS_CONFIRMED;
-
-	if (cda[CTA_STATUS-1]) {
-		err = ctnetlink_change_status(ct, cda);
-		if (err < 0)
-			goto err;
-	}
-
-	if (cda[CTA_PROTOINFO-1]) {
-		err = ctnetlink_change_protoinfo(ct, cda);
-		if (err < 0)
-			goto err;
-	}
-
-#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
-	if (cda[CTA_MARK-1])
-		ct->mark = ntohl(*(__be32 *)NFA_DATA(cda[CTA_MARK-1]));
-#endif
-
-	ct->helper = ip_conntrack_helper_find_get(rtuple);
-
-	add_timer(&ct->timeout);
-	ip_conntrack_hash_insert(ct);
-
-	if (ct->helper)
-		ip_conntrack_helper_put(ct->helper);
-
-	return 0;
-
-err:
-	ip_conntrack_free(ct);
-	return err;
-}
-
-static int
-ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
-			struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
-{
-	struct ip_conntrack_tuple otuple, rtuple;
-	struct ip_conntrack_tuple_hash *h = NULL;
-	int err = 0;
-
-	if (nfattr_bad_size(cda, CTA_MAX, cta_min))
-		return -EINVAL;
-
-	if (cda[CTA_TUPLE_ORIG-1]) {
-		err = ctnetlink_parse_tuple(cda, &otuple, CTA_TUPLE_ORIG);
-		if (err < 0)
-			return err;
-	}
-
-	if (cda[CTA_TUPLE_REPLY-1]) {
-		err = ctnetlink_parse_tuple(cda, &rtuple, CTA_TUPLE_REPLY);
-		if (err < 0)
-			return err;
-	}
-
-	write_lock_bh(&ip_conntrack_lock);
-	if (cda[CTA_TUPLE_ORIG-1])
-		h = __ip_conntrack_find(&otuple, NULL);
-	else if (cda[CTA_TUPLE_REPLY-1])
-		h = __ip_conntrack_find(&rtuple, NULL);
-
-	if (h == NULL) {
-		write_unlock_bh(&ip_conntrack_lock);
-		err = -ENOENT;
-		if (nlh->nlmsg_flags & NLM_F_CREATE)
-			err = ctnetlink_create_conntrack(cda, &otuple, &rtuple);
-		return err;
-	}
-	/* implicit 'else' */
-
-	/* we only allow nat config for new conntracks */
-	if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) {
-		err = -EINVAL;
-		goto out_unlock;
-	}
-
-	/* We manipulate the conntrack inside the global conntrack table lock,
-	 * so there's no need to increase the refcount */
-	err = -EEXIST;
-	if (!(nlh->nlmsg_flags & NLM_F_EXCL))
-		err = ctnetlink_change_conntrack(tuplehash_to_ctrack(h), cda);
-
-out_unlock:
-	write_unlock_bh(&ip_conntrack_lock);
-	return err;
-}
-
-/***********************************************************************
- * EXPECT
- ***********************************************************************/
-
-static inline int
-ctnetlink_exp_dump_tuple(struct sk_buff *skb,
-			 const struct ip_conntrack_tuple *tuple,
-			 enum ctattr_expect type)
-{
-	struct nfattr *nest_parms = NFA_NEST(skb, type);
-
-	if (ctnetlink_dump_tuples(skb, tuple) < 0)
-		goto nfattr_failure;
-
-	NFA_NEST_END(skb, nest_parms);
-
-	return 0;
-
-nfattr_failure:
-	return -1;
-}
-
-static inline int
-ctnetlink_exp_dump_mask(struct sk_buff *skb,
-			const struct ip_conntrack_tuple *tuple,
-			const struct ip_conntrack_tuple *mask)
-{
-	int ret;
-	struct ip_conntrack_protocol *proto;
-	struct nfattr *nest_parms = NFA_NEST(skb, CTA_EXPECT_MASK);
-
-	ret = ctnetlink_dump_tuples_ip(skb, mask);
-	if (unlikely(ret < 0))
-		goto nfattr_failure;
-
-	proto = ip_conntrack_proto_find_get(tuple->dst.protonum);
-	ret = ctnetlink_dump_tuples_proto(skb, mask, proto);
-	ip_conntrack_proto_put(proto);
-	if (unlikely(ret < 0))
-		goto nfattr_failure;
-
-	NFA_NEST_END(skb, nest_parms);
-
-	return 0;
-
-nfattr_failure:
-	return -1;
-}
-
-static inline int
-ctnetlink_exp_dump_expect(struct sk_buff *skb,
-			  const struct ip_conntrack_expect *exp)
-{
-	struct ip_conntrack *master = exp->master;
-	__be32 timeout = htonl((exp->timeout.expires - jiffies) / HZ);
-	__be32 id = htonl(exp->id);
-
-	if (ctnetlink_exp_dump_tuple(skb, &exp->tuple, CTA_EXPECT_TUPLE) < 0)
-		goto nfattr_failure;
-	if (ctnetlink_exp_dump_mask(skb, &exp->tuple, &exp->mask) < 0)
-		goto nfattr_failure;
-	if (ctnetlink_exp_dump_tuple(skb,
-				 &master->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
-				 CTA_EXPECT_MASTER) < 0)
-		goto nfattr_failure;
-
-	NFA_PUT(skb, CTA_EXPECT_TIMEOUT, sizeof(__be32), &timeout);
-	NFA_PUT(skb, CTA_EXPECT_ID, sizeof(__be32), &id);
-
-	return 0;
-
-nfattr_failure:
-	return -1;
-}
-
-static int
-ctnetlink_exp_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
-		    int event,
-		    int nowait,
-		    const struct ip_conntrack_expect *exp)
-{
-	struct nlmsghdr *nlh;
-	struct nfgenmsg *nfmsg;
-	unsigned char *b;
-
-	b = skb->tail;
-
-	event |= NFNL_SUBSYS_CTNETLINK_EXP << 8;
-	nlh    = NLMSG_PUT(skb, pid, seq, event, sizeof(struct nfgenmsg));
-	nfmsg  = NLMSG_DATA(nlh);
-
-	nlh->nlmsg_flags    = (nowait && pid) ? NLM_F_MULTI : 0;
-	nfmsg->nfgen_family = AF_INET;
-	nfmsg->version	    = NFNETLINK_V0;
-	nfmsg->res_id	    = 0;
-
-	if (ctnetlink_exp_dump_expect(skb, exp) < 0)
-		goto nfattr_failure;
-
-	nlh->nlmsg_len = skb->tail - b;
-	return skb->len;
-
-nlmsg_failure:
-nfattr_failure:
-	skb_trim(skb, b - skb->data);
-	return -1;
-}
-
-#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS
-static int ctnetlink_expect_event(struct notifier_block *this,
-				  unsigned long events, void *ptr)
-{
-	struct nlmsghdr *nlh;
-	struct nfgenmsg *nfmsg;
-	struct ip_conntrack_expect *exp = (struct ip_conntrack_expect *)ptr;
-	struct sk_buff *skb;
-	unsigned int type;
-	unsigned char *b;
-	int flags = 0;
-
-	if (events & IPEXP_NEW) {
-		type = IPCTNL_MSG_EXP_NEW;
-		flags = NLM_F_CREATE|NLM_F_EXCL;
-	} else
-		return NOTIFY_DONE;
-
-	if (!nfnetlink_has_listeners(NFNLGRP_CONNTRACK_EXP_NEW))
-		return NOTIFY_DONE;
-
-	skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
-	if (!skb)
-		return NOTIFY_DONE;
-
-	b = skb->tail;
-
-	type |= NFNL_SUBSYS_CTNETLINK_EXP << 8;
-	nlh   = NLMSG_PUT(skb, 0, 0, type, sizeof(struct nfgenmsg));
-	nfmsg = NLMSG_DATA(nlh);
-
-	nlh->nlmsg_flags    = flags;
-	nfmsg->nfgen_family = AF_INET;
-	nfmsg->version	    = NFNETLINK_V0;
-	nfmsg->res_id	    = 0;
-
-	if (ctnetlink_exp_dump_expect(skb, exp) < 0)
-		goto nfattr_failure;
-
-	nlh->nlmsg_len = skb->tail - b;
-	nfnetlink_send(skb, 0, NFNLGRP_CONNTRACK_EXP_NEW, 0);
-	return NOTIFY_DONE;
-
-nlmsg_failure:
-nfattr_failure:
-	kfree_skb(skb);
-	return NOTIFY_DONE;
-}
-#endif
-
-static int
-ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
-{
-	struct ip_conntrack_expect *exp = NULL;
-	struct list_head *i;
-	u_int32_t *id = (u_int32_t *) &cb->args[0];
-
-	read_lock_bh(&ip_conntrack_lock);
-	list_for_each_prev(i, &ip_conntrack_expect_list) {
-		exp = (struct ip_conntrack_expect *) i;
-		if (exp->id <= *id)
-			continue;
-		if (ctnetlink_exp_fill_info(skb, NETLINK_CB(cb->skb).pid,
-					    cb->nlh->nlmsg_seq,
-					    IPCTNL_MSG_EXP_NEW,
-					    1, exp) < 0)
-			goto out;
-		*id = exp->id;
-	}
-out:
-	read_unlock_bh(&ip_conntrack_lock);
-
-	return skb->len;
-}
-
-static const size_t cta_min_exp[CTA_EXPECT_MAX] = {
-	[CTA_EXPECT_TIMEOUT-1]          = sizeof(__be32),
-	[CTA_EXPECT_ID-1]               = sizeof(__be32)
-};
-
-static int
-ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
-		     struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
-{
-	struct ip_conntrack_tuple tuple;
-	struct ip_conntrack_expect *exp;
-	struct sk_buff *skb2;
-	int err = 0;
-
-	if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp))
-		return -EINVAL;
-
-	if (nlh->nlmsg_flags & NLM_F_DUMP) {
-		struct nfgenmsg *msg = NLMSG_DATA(nlh);
-		u32 rlen;
-
-		if (msg->nfgen_family != AF_INET)
-			return -EAFNOSUPPORT;
-
-		if ((*errp = netlink_dump_start(ctnl, skb, nlh,
-						ctnetlink_exp_dump_table,
-						ctnetlink_done)) != 0)
-			return -EINVAL;
-		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
-		if (rlen > skb->len)
-			rlen = skb->len;
-		skb_pull(skb, rlen);
-		return 0;
-	}
-
-	if (cda[CTA_EXPECT_MASTER-1])
-		err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_MASTER);
-	else
-		return -EINVAL;
-
-	if (err < 0)
-		return err;
-
-	exp = ip_conntrack_expect_find_get(&tuple);
-	if (!exp)
-		return -ENOENT;
-
-	if (cda[CTA_EXPECT_ID-1]) {
-		__be32 id = *(__be32 *)NFA_DATA(cda[CTA_EXPECT_ID-1]);
-		if (exp->id != ntohl(id)) {
-			ip_conntrack_expect_put(exp);
-			return -ENOENT;
-		}
-	}
-
-	err = -ENOMEM;
-	skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
-	if (!skb2)
-		goto out;
-
-	err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).pid,
-				      nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW,
-				      1, exp);
-	if (err <= 0)
-		goto free;
-
-	ip_conntrack_expect_put(exp);
-
-	return netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT);
-
-free:
-	kfree_skb(skb2);
-out:
-	ip_conntrack_expect_put(exp);
-	return err;
-}
-
-static int
-ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
-		     struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
-{
-	struct ip_conntrack_expect *exp, *tmp;
-	struct ip_conntrack_tuple tuple;
-	struct ip_conntrack_helper *h;
-	int err;
-
-	if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp))
-		return -EINVAL;
-
-	if (cda[CTA_EXPECT_TUPLE-1]) {
-		/* delete a single expect by tuple */
-		err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE);
-		if (err < 0)
-			return err;
-
-		/* bump usage count to 2 */
-		exp = ip_conntrack_expect_find_get(&tuple);
-		if (!exp)
-			return -ENOENT;
-
-		if (cda[CTA_EXPECT_ID-1]) {
-			__be32 id =
-				*(__be32 *)NFA_DATA(cda[CTA_EXPECT_ID-1]);
-			if (exp->id != ntohl(id)) {
-				ip_conntrack_expect_put(exp);
-				return -ENOENT;
-			}
-		}
-
-		/* after list removal, usage count == 1 */
-		ip_conntrack_unexpect_related(exp);
-		/* have to put what we 'get' above.
-		 * after this line usage count == 0 */
-		ip_conntrack_expect_put(exp);
-	} else if (cda[CTA_EXPECT_HELP_NAME-1]) {
-		char *name = NFA_DATA(cda[CTA_EXPECT_HELP_NAME-1]);
-
-		/* delete all expectations for this helper */
-		write_lock_bh(&ip_conntrack_lock);
-		h = __ip_conntrack_helper_find_byname(name);
-		if (!h) {
-			write_unlock_bh(&ip_conntrack_lock);
-			return -EINVAL;
-		}
-		list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list,
-					 list) {
-			if (exp->master->helper == h
-			    && del_timer(&exp->timeout)) {
-				ip_ct_unlink_expect(exp);
-				ip_conntrack_expect_put(exp);
-			}
-		}
-		write_unlock_bh(&ip_conntrack_lock);
-	} else {
-		/* This basically means we have to flush everything*/
-		write_lock_bh(&ip_conntrack_lock);
-		list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list,
-					 list) {
-			if (del_timer(&exp->timeout)) {
-				ip_ct_unlink_expect(exp);
-				ip_conntrack_expect_put(exp);
-			}
-		}
-		write_unlock_bh(&ip_conntrack_lock);
-	}
-
-	return 0;
-}
-static int
-ctnetlink_change_expect(struct ip_conntrack_expect *x, struct nfattr *cda[])
-{
-	return -EOPNOTSUPP;
-}
-
-static int
-ctnetlink_create_expect(struct nfattr *cda[])
-{
-	struct ip_conntrack_tuple tuple, mask, master_tuple;
-	struct ip_conntrack_tuple_hash *h = NULL;
-	struct ip_conntrack_expect *exp;
-	struct ip_conntrack *ct;
-	int err = 0;
-
-	/* caller guarantees that those three CTA_EXPECT_* exist */
-	err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE);
-	if (err < 0)
-		return err;
-	err = ctnetlink_parse_tuple(cda, &mask, CTA_EXPECT_MASK);
-	if (err < 0)
-		return err;
-	err = ctnetlink_parse_tuple(cda, &master_tuple, CTA_EXPECT_MASTER);
-	if (err < 0)
-		return err;
-
-	/* Look for master conntrack of this expectation */
-	h = ip_conntrack_find_get(&master_tuple, NULL);
-	if (!h)
-		return -ENOENT;
-	ct = tuplehash_to_ctrack(h);
-
-	if (!ct->helper) {
-		/* such conntrack hasn't got any helper, abort */
-		err = -EINVAL;
-		goto out;
-	}
-
-	exp = ip_conntrack_expect_alloc(ct);
-	if (!exp) {
-		err = -ENOMEM;
-		goto out;
-	}
-
-	exp->expectfn = NULL;
-	exp->flags = 0;
-	exp->master = ct;
-	memcpy(&exp->tuple, &tuple, sizeof(struct ip_conntrack_tuple));
-	memcpy(&exp->mask, &mask, sizeof(struct ip_conntrack_tuple));
-
-	err = ip_conntrack_expect_related(exp);
-	ip_conntrack_expect_put(exp);
-
-out:
-	ip_conntrack_put(tuplehash_to_ctrack(h));
-	return err;
-}
-
-static int
-ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb,
-		     struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
-{
-	struct ip_conntrack_tuple tuple;
-	struct ip_conntrack_expect *exp;
-	int err = 0;
-
-	if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp))
-		return -EINVAL;
-
-	if (!cda[CTA_EXPECT_TUPLE-1]
-	    || !cda[CTA_EXPECT_MASK-1]
-	    || !cda[CTA_EXPECT_MASTER-1])
-		return -EINVAL;
-
-	err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE);
-	if (err < 0)
-		return err;
-
-	write_lock_bh(&ip_conntrack_lock);
-	exp = __ip_conntrack_expect_find(&tuple);
-
-	if (!exp) {
-		write_unlock_bh(&ip_conntrack_lock);
-		err = -ENOENT;
-		if (nlh->nlmsg_flags & NLM_F_CREATE)
-			err = ctnetlink_create_expect(cda);
-		return err;
-	}
-
-	err = -EEXIST;
-	if (!(nlh->nlmsg_flags & NLM_F_EXCL))
-		err = ctnetlink_change_expect(exp, cda);
-	write_unlock_bh(&ip_conntrack_lock);
-
-	return err;
-}
-
-#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS
-static struct notifier_block ctnl_notifier = {
-	.notifier_call	= ctnetlink_conntrack_event,
-};
-
-static struct notifier_block ctnl_notifier_exp = {
-	.notifier_call	= ctnetlink_expect_event,
-};
-#endif
-
-static struct nfnl_callback ctnl_cb[IPCTNL_MSG_MAX] = {
-	[IPCTNL_MSG_CT_NEW]		= { .call = ctnetlink_new_conntrack,
-					    .attr_count = CTA_MAX, },
-	[IPCTNL_MSG_CT_GET] 		= { .call = ctnetlink_get_conntrack,
-					    .attr_count = CTA_MAX, },
-	[IPCTNL_MSG_CT_DELETE]  	= { .call = ctnetlink_del_conntrack,
-					    .attr_count = CTA_MAX, },
-	[IPCTNL_MSG_CT_GET_CTRZERO] 	= { .call = ctnetlink_get_conntrack,
-					    .attr_count = CTA_MAX, },
-};
-
-static struct nfnl_callback ctnl_exp_cb[IPCTNL_MSG_EXP_MAX] = {
-	[IPCTNL_MSG_EXP_GET]		= { .call = ctnetlink_get_expect,
-					    .attr_count = CTA_EXPECT_MAX, },
-	[IPCTNL_MSG_EXP_NEW]		= { .call = ctnetlink_new_expect,
-					    .attr_count = CTA_EXPECT_MAX, },
-	[IPCTNL_MSG_EXP_DELETE]		= { .call = ctnetlink_del_expect,
-					    .attr_count = CTA_EXPECT_MAX, },
-};
-
-static struct nfnetlink_subsystem ctnl_subsys = {
-	.name				= "conntrack",
-	.subsys_id			= NFNL_SUBSYS_CTNETLINK,
-	.cb_count			= IPCTNL_MSG_MAX,
-	.cb				= ctnl_cb,
-};
-
-static struct nfnetlink_subsystem ctnl_exp_subsys = {
-	.name				= "conntrack_expect",
-	.subsys_id			= NFNL_SUBSYS_CTNETLINK_EXP,
-	.cb_count			= IPCTNL_MSG_EXP_MAX,
-	.cb				= ctnl_exp_cb,
-};
-
-MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK);
-MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK_EXP);
-
-static int __init ctnetlink_init(void)
-{
-	int ret;
-
-	printk("ctnetlink v%s: registering with nfnetlink.\n", version);
-	ret = nfnetlink_subsys_register(&ctnl_subsys);
-	if (ret < 0) {
-		printk("ctnetlink_init: cannot register with nfnetlink.\n");
-		goto err_out;
-	}
-
-	ret = nfnetlink_subsys_register(&ctnl_exp_subsys);
-	if (ret < 0) {
-		printk("ctnetlink_init: cannot register exp with nfnetlink.\n");
-		goto err_unreg_subsys;
-	}
-
-#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS
-	ret = ip_conntrack_register_notifier(&ctnl_notifier);
-	if (ret < 0) {
-		printk("ctnetlink_init: cannot register notifier.\n");
-		goto err_unreg_exp_subsys;
-	}
-
-	ret = ip_conntrack_expect_register_notifier(&ctnl_notifier_exp);
-	if (ret < 0) {
-		printk("ctnetlink_init: cannot expect register notifier.\n");
-		goto err_unreg_notifier;
-	}
-#endif
-
-	return 0;
-
-#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS
-err_unreg_notifier:
-	ip_conntrack_unregister_notifier(&ctnl_notifier);
-err_unreg_exp_subsys:
-	nfnetlink_subsys_unregister(&ctnl_exp_subsys);
-#endif
-err_unreg_subsys:
-	nfnetlink_subsys_unregister(&ctnl_subsys);
-err_out:
-	return ret;
-}
-
-static void __exit ctnetlink_exit(void)
-{
-	printk("ctnetlink: unregistering from nfnetlink.\n");
-
-#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS
-	ip_conntrack_expect_unregister_notifier(&ctnl_notifier_exp);
-	ip_conntrack_unregister_notifier(&ctnl_notifier);
-#endif
-
-	nfnetlink_subsys_unregister(&ctnl_exp_subsys);
-	nfnetlink_subsys_unregister(&ctnl_subsys);
-	return;
-}
-
-module_init(ctnetlink_init);
-module_exit(ctnetlink_exit);
diff --git a/net/ipv4/netfilter/ip_conntrack_proto_generic.c b/net/ipv4/netfilter/ip_conntrack_proto_generic.c
deleted file mode 100644
index 88af82e..0000000
--- a/net/ipv4/netfilter/ip_conntrack_proto_generic.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/* (C) 1999-2001 Paul `Rusty' Russell
- * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.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.
- */
-
-#include <linux/types.h>
-#include <linux/timer.h>
-#include <linux/netfilter.h>
-#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
-
-unsigned int ip_ct_generic_timeout __read_mostly = 600*HZ;
-
-static int generic_pkt_to_tuple(const struct sk_buff *skb,
-				unsigned int dataoff,
-				struct ip_conntrack_tuple *tuple)
-{
-	tuple->src.u.all = 0;
-	tuple->dst.u.all = 0;
-
-	return 1;
-}
-
-static int generic_invert_tuple(struct ip_conntrack_tuple *tuple,
-				const struct ip_conntrack_tuple *orig)
-{
-	tuple->src.u.all = 0;
-	tuple->dst.u.all = 0;
-
-	return 1;
-}
-
-/* Print out the per-protocol part of the tuple. */
-static int generic_print_tuple(struct seq_file *s,
-			       const struct ip_conntrack_tuple *tuple)
-{
-	return 0;
-}
-
-/* Print out the private part of the conntrack. */
-static int generic_print_conntrack(struct seq_file *s,
-				   const struct ip_conntrack *state)
-{
-	return 0;
-}
-
-/* Returns verdict for packet, or -1 for invalid. */
-static int packet(struct ip_conntrack *conntrack,
-		  const struct sk_buff *skb,
-		  enum ip_conntrack_info ctinfo)
-{
-	ip_ct_refresh_acct(conntrack, ctinfo, skb, ip_ct_generic_timeout);
-	return NF_ACCEPT;
-}
-
-/* Called when a new connection for this protocol found. */
-static int new(struct ip_conntrack *conntrack, const struct sk_buff *skb)
-{
-	return 1;
-}
-
-struct ip_conntrack_protocol ip_conntrack_generic_protocol =
-{
-	.proto			= 0,
-	.name			= "unknown",
-	.pkt_to_tuple		= generic_pkt_to_tuple,
-	.invert_tuple		= generic_invert_tuple,
-	.print_tuple		= generic_print_tuple,
-	.print_conntrack	= generic_print_conntrack,
-	.packet			= packet,
-	.new			= new,
-};
diff --git a/net/ipv4/netfilter/ip_conntrack_proto_gre.c b/net/ipv4/netfilter/ip_conntrack_proto_gre.c
deleted file mode 100644
index ac1c49e..0000000
--- a/net/ipv4/netfilter/ip_conntrack_proto_gre.c
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * ip_conntrack_proto_gre.c - Version 3.0
- *
- * Connection tracking protocol helper module for GRE.
- *
- * GRE is a generic encapsulation protocol, which is generally not very
- * suited for NAT, as it has no protocol-specific part as port numbers.
- *
- * It has an optional key field, which may help us distinguishing two
- * connections between the same two hosts.
- *
- * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784
- *
- * PPTP is built on top of a modified version of GRE, and has a mandatory
- * field called "CallID", which serves us for the same purpose as the key
- * field in plain GRE.
- *
- * Documentation about PPTP can be found in RFC 2637
- *
- * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org>
- *
- * Development of this code funded by Astaro AG (http://www.astaro.com/)
- *
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/timer.h>
-#include <linux/netfilter.h>
-#include <linux/ip.h>
-#include <linux/in.h>
-#include <linux/list.h>
-#include <linux/seq_file.h>
-#include <linux/interrupt.h>
-
-static DEFINE_RWLOCK(ip_ct_gre_lock);
-
-#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-#include <linux/netfilter_ipv4/ip_conntrack_core.h>
-
-#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
-#include <linux/netfilter_ipv4/ip_conntrack_pptp.h>
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
-MODULE_DESCRIPTION("netfilter connection tracking protocol helper for GRE");
-
-/* shamelessly stolen from ip_conntrack_proto_udp.c */
-#define GRE_TIMEOUT		(30*HZ)
-#define GRE_STREAM_TIMEOUT	(180*HZ)
-
-#if 0
-#define DEBUGP(format, args...)	printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args)
-#define DUMP_TUPLE_GRE(x) printk("%u.%u.%u.%u:0x%x -> %u.%u.%u.%u:0x%x\n", \
-			NIPQUAD((x)->src.ip), ntohs((x)->src.u.gre.key), \
-			NIPQUAD((x)->dst.ip), ntohs((x)->dst.u.gre.key))
-#else
-#define DEBUGP(x, args...)
-#define DUMP_TUPLE_GRE(x)
-#endif
-
-/* GRE KEYMAP HANDLING FUNCTIONS */
-static LIST_HEAD(gre_keymap_list);
-
-static inline int gre_key_cmpfn(const struct ip_ct_gre_keymap *km,
-				const struct ip_conntrack_tuple *t)
-{
-	return ((km->tuple.src.ip == t->src.ip) &&
-		(km->tuple.dst.ip == t->dst.ip) &&
-		(km->tuple.dst.protonum == t->dst.protonum) &&
-		(km->tuple.dst.u.all == t->dst.u.all));
-}
-
-/* look up the source key for a given tuple */
-static __be16 gre_keymap_lookup(struct ip_conntrack_tuple *t)
-{
-	struct ip_ct_gre_keymap *km;
-	__be16 key = 0;
-
-	read_lock_bh(&ip_ct_gre_lock);
-	list_for_each_entry(km, &gre_keymap_list, list) {
-		if (gre_key_cmpfn(km, t)) {
-			key = km->tuple.src.u.gre.key;
-			break;
-		}
-	}
-	read_unlock_bh(&ip_ct_gre_lock);
-
-	DEBUGP("lookup src key 0x%x up key for ", key);
-	DUMP_TUPLE_GRE(t);
-
-	return key;
-}
-
-/* add a single keymap entry, associate with specified master ct */
-int
-ip_ct_gre_keymap_add(struct ip_conntrack *ct,
-		     struct ip_conntrack_tuple *t, int reply)
-{
-	struct ip_ct_gre_keymap **exist_km, *km;
-
-	if (!ct->helper || strcmp(ct->helper->name, "pptp")) {
-		DEBUGP("refusing to add GRE keymap to non-pptp session\n");
-		return -1;
-	}
-
-	if (!reply)
-		exist_km = &ct->help.ct_pptp_info.keymap_orig;
-	else
-		exist_km = &ct->help.ct_pptp_info.keymap_reply;
-
-	if (*exist_km) {
-		/* check whether it's a retransmission */
-		list_for_each_entry(km, &gre_keymap_list, list) {
-			if (gre_key_cmpfn(km, t) && km == *exist_km)
-				return 0;
-		}
-		DEBUGP("trying to override keymap_%s for ct %p\n",
-			reply? "reply":"orig", ct);
-		return -EEXIST;
-	}
-
-	km = kmalloc(sizeof(*km), GFP_ATOMIC);
-	if (!km)
-		return -ENOMEM;
-
-	memcpy(&km->tuple, t, sizeof(*t));
-	*exist_km = km;
-
-	DEBUGP("adding new entry %p: ", km);
-	DUMP_TUPLE_GRE(&km->tuple);
-
-	write_lock_bh(&ip_ct_gre_lock);
-	list_add_tail(&km->list, &gre_keymap_list);
-	write_unlock_bh(&ip_ct_gre_lock);
-
-	return 0;
-}
-
-/* destroy the keymap entries associated with specified master ct */
-void ip_ct_gre_keymap_destroy(struct ip_conntrack *ct)
-{
-	DEBUGP("entering for ct %p\n", ct);
-
-	if (!ct->helper || strcmp(ct->helper->name, "pptp")) {
-		DEBUGP("refusing to destroy GRE keymap to non-pptp session\n");
-		return;
-	}
-
-	write_lock_bh(&ip_ct_gre_lock);
-	if (ct->help.ct_pptp_info.keymap_orig) {
-		DEBUGP("removing %p from list\n",
-			ct->help.ct_pptp_info.keymap_orig);
-		list_del(&ct->help.ct_pptp_info.keymap_orig->list);
-		kfree(ct->help.ct_pptp_info.keymap_orig);
-		ct->help.ct_pptp_info.keymap_orig = NULL;
-	}
-	if (ct->help.ct_pptp_info.keymap_reply) {
-		DEBUGP("removing %p from list\n",
-			ct->help.ct_pptp_info.keymap_reply);
-		list_del(&ct->help.ct_pptp_info.keymap_reply->list);
-		kfree(ct->help.ct_pptp_info.keymap_reply);
-		ct->help.ct_pptp_info.keymap_reply = NULL;
-	}
-	write_unlock_bh(&ip_ct_gre_lock);
-}
-
-
-/* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */
-
-/* invert gre part of tuple */
-static int gre_invert_tuple(struct ip_conntrack_tuple *tuple,
-			    const struct ip_conntrack_tuple *orig)
-{
-	tuple->dst.u.gre.key = orig->src.u.gre.key;
-	tuple->src.u.gre.key = orig->dst.u.gre.key;
-
-	return 1;
-}
-
-/* gre hdr info to tuple */
-static int gre_pkt_to_tuple(const struct sk_buff *skb,
-			   unsigned int dataoff,
-			   struct ip_conntrack_tuple *tuple)
-{
-	struct gre_hdr_pptp _pgrehdr, *pgrehdr;
-	__be16 srckey;
-	struct gre_hdr _grehdr, *grehdr;
-
-	/* first only delinearize old RFC1701 GRE header */
-	grehdr = skb_header_pointer(skb, dataoff, sizeof(_grehdr), &_grehdr);
-	if (!grehdr || grehdr->version != GRE_VERSION_PPTP) {
-		/* try to behave like "ip_conntrack_proto_generic" */
-		tuple->src.u.all = 0;
-		tuple->dst.u.all = 0;
-		return 1;
-	}
-
-	/* PPTP header is variable length, only need up to the call_id field */
-	pgrehdr = skb_header_pointer(skb, dataoff, 8, &_pgrehdr);
-	if (!pgrehdr)
-		return 1;
-
-	if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) {
-		DEBUGP("GRE_VERSION_PPTP but unknown proto\n");
-		return 0;
-	}
-
-	tuple->dst.u.gre.key = pgrehdr->call_id;
-	srckey = gre_keymap_lookup(tuple);
-	tuple->src.u.gre.key = srckey;
-
-	return 1;
-}
-
-/* print gre part of tuple */
-static int gre_print_tuple(struct seq_file *s,
-			   const struct ip_conntrack_tuple *tuple)
-{
-	return seq_printf(s, "srckey=0x%x dstkey=0x%x ",
-			  ntohs(tuple->src.u.gre.key),
-			  ntohs(tuple->dst.u.gre.key));
-}
-
-/* print private data for conntrack */
-static int gre_print_conntrack(struct seq_file *s,
-			       const struct ip_conntrack *ct)
-{
-	return seq_printf(s, "timeout=%u, stream_timeout=%u ",
-			  (ct->proto.gre.timeout / HZ),
-			  (ct->proto.gre.stream_timeout / HZ));
-}
-
-/* Returns verdict for packet, and may modify conntrack */
-static int gre_packet(struct ip_conntrack *ct,
-		      const struct sk_buff *skb,
-		      enum ip_conntrack_info conntrackinfo)
-{
-	/* If we've seen traffic both ways, this is a GRE connection.
-	 * Extend timeout. */
-	if (ct->status & IPS_SEEN_REPLY) {
-		ip_ct_refresh_acct(ct, conntrackinfo, skb,
-				   ct->proto.gre.stream_timeout);
-		/* Also, more likely to be important, and not a probe. */
-		set_bit(IPS_ASSURED_BIT, &ct->status);
-		ip_conntrack_event_cache(IPCT_STATUS, skb);
-	} else
-		ip_ct_refresh_acct(ct, conntrackinfo, skb,
-				   ct->proto.gre.timeout);
-
-	return NF_ACCEPT;
-}
-
-/* Called when a new connection for this protocol found. */
-static int gre_new(struct ip_conntrack *ct,
-		   const struct sk_buff *skb)
-{
-	DEBUGP(": ");
-	DUMP_TUPLE_GRE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
-
-	/* initialize to sane value.  Ideally a conntrack helper
-	 * (e.g. in case of pptp) is increasing them */
-	ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT;
-	ct->proto.gre.timeout = GRE_TIMEOUT;
-
-	return 1;
-}
-
-/* Called when a conntrack entry has already been removed from the hashes
- * and is about to be deleted from memory */
-static void gre_destroy(struct ip_conntrack *ct)
-{
-	struct ip_conntrack *master = ct->master;
-	DEBUGP(" entering\n");
-
-	if (!master)
-		DEBUGP("no master !?!\n");
-	else
-		ip_ct_gre_keymap_destroy(master);
-}
-
-/* protocol helper struct */
-static struct ip_conntrack_protocol gre = {
-	.proto		 = IPPROTO_GRE,
-	.name		 = "gre",
-	.pkt_to_tuple	 = gre_pkt_to_tuple,
-	.invert_tuple	 = gre_invert_tuple,
-	.print_tuple	 = gre_print_tuple,
-	.print_conntrack = gre_print_conntrack,
-	.packet		 = gre_packet,
-	.new		 = gre_new,
-	.destroy	 = gre_destroy,
-	.me 		 = THIS_MODULE,
-#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
-    defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
-	.tuple_to_nfattr = ip_ct_port_tuple_to_nfattr,
-	.nfattr_to_tuple = ip_ct_port_nfattr_to_tuple,
-#endif
-};
-
-/* ip_conntrack_proto_gre initialization */
-int __init ip_ct_proto_gre_init(void)
-{
-	return ip_conntrack_protocol_register(&gre);
-}
-
-/* This cannot be __exit, as it is invoked from ip_conntrack_helper_pptp.c's
- * init() code on errors.
- */
-void ip_ct_proto_gre_fini(void)
-{
-	struct list_head *pos, *n;
-
-	/* delete all keymap entries */
-	write_lock_bh(&ip_ct_gre_lock);
-	list_for_each_safe(pos, n, &gre_keymap_list) {
-		DEBUGP("deleting keymap %p at module unload time\n", pos);
-		list_del(pos);
-		kfree(pos);
-	}
-	write_unlock_bh(&ip_ct_gre_lock);
-
-	ip_conntrack_protocol_unregister(&gre);
-}
-
-EXPORT_SYMBOL(ip_ct_gre_keymap_add);
-EXPORT_SYMBOL(ip_ct_gre_keymap_destroy);
diff --git a/net/ipv4/netfilter/ip_conntrack_proto_icmp.c b/net/ipv4/netfilter/ip_conntrack_proto_icmp.c
deleted file mode 100644
index ad70c81..0000000
--- a/net/ipv4/netfilter/ip_conntrack_proto_icmp.c
+++ /dev/null
@@ -1,315 +0,0 @@
-/* (C) 1999-2001 Paul `Rusty' Russell
- * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.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.
- */
-
-#include <linux/types.h>
-#include <linux/timer.h>
-#include <linux/netfilter.h>
-#include <linux/in.h>
-#include <linux/icmp.h>
-#include <linux/seq_file.h>
-#include <linux/skbuff.h>
-#include <net/ip.h>
-#include <net/checksum.h>
-#include <linux/netfilter_ipv4.h>
-#include <linux/netfilter_ipv4/ip_conntrack.h>
-#include <linux/netfilter_ipv4/ip_conntrack_core.h>
-#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
-
-unsigned int ip_ct_icmp_timeout __read_mostly = 30*HZ;
-
-#if 0
-#define DEBUGP printk
-#else
-#define DEBUGP(format, args...)
-#endif
-
-static int icmp_pkt_to_tuple(const struct sk_buff *skb,
-			     unsigned int dataoff,
-			     struct ip_conntrack_tuple *tuple)
-{
-	struct icmphdr _hdr, *hp;
-
-	hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
-	if (hp == NULL)
-		return 0;
-
-	tuple->dst.u.icmp.type = hp->type;
-	tuple->src.u.icmp.id = hp->un.echo.id;
-	tuple->dst.u.icmp.code = hp->code;
-
-	return 1;
-}
-
-/* Add 1; spaces filled with 0. */
-static const u_int8_t invmap[] = {
-	[ICMP_ECHO] = ICMP_ECHOREPLY + 1,
-	[ICMP_ECHOREPLY] = ICMP_ECHO + 1,
-	[ICMP_TIMESTAMP] = ICMP_TIMESTAMPREPLY + 1,
-	[ICMP_TIMESTAMPREPLY] = ICMP_TIMESTAMP + 1,
-	[ICMP_INFO_REQUEST] = ICMP_INFO_REPLY + 1,
-	[ICMP_INFO_REPLY] = ICMP_INFO_REQUEST + 1,
-	[ICMP_ADDRESS] = ICMP_ADDRESSREPLY + 1,
-	[ICMP_ADDRESSREPLY] = ICMP_ADDRESS + 1
-};
-
-static int icmp_invert_tuple(struct ip_conntrack_tuple *tuple,
-			     const struct ip_conntrack_tuple *orig)
-{
-	if (orig->dst.u.icmp.type >= sizeof(invmap)
-	    || !invmap[orig->dst.u.icmp.type])
-		return 0;
-
-	tuple->src.u.icmp.id = orig->src.u.icmp.id;
-	tuple->dst.u.icmp.type = invmap[orig->dst.u.icmp.type] - 1;
-	tuple->dst.u.icmp.code = orig->dst.u.icmp.code;
-	return 1;
-}
-
-/* Print out the per-protocol part of the tuple. */
-static int icmp_print_tuple(struct seq_file *s,
-			    const struct ip_conntrack_tuple *tuple)
-{
-	return seq_printf(s, "type=%u code=%u id=%u ",
-			  tuple->dst.u.icmp.type,
-			  tuple->dst.u.icmp.code,
-			  ntohs(tuple->src.u.icmp.id));
-}
-
-/* Print out the private part of the conntrack. */
-static int icmp_print_conntrack(struct seq_file *s,
-				const struct ip_conntrack *conntrack)
-{
-	return 0;
-}
-
-/* Returns verdict for packet, or -1 for invalid. */
-static int icmp_packet(struct ip_conntrack *ct,
-		       const struct sk_buff *skb,
-		       enum ip_conntrack_info ctinfo)
-{
-	/* Try to delete connection immediately after all replies:
-	   won't actually vanish as we still have skb, and del_timer
-	   means this will only run once even if count hits zero twice
-	   (theoretically possible with SMP) */
-	if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
-		if (atomic_dec_and_test(&ct->proto.icmp.count)
-		    && del_timer(&ct->timeout))
-			ct->timeout.function((unsigned long)ct);
-	} else {
-		atomic_inc(&ct->proto.icmp.count);
-		ip_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb);
-		ip_ct_refresh_acct(ct, ctinfo, skb, ip_ct_icmp_timeout);
-	}
-
-	return NF_ACCEPT;
-}
-
-/* Called when a new connection for this protocol found. */
-static int icmp_new(struct ip_conntrack *conntrack,
-		    const struct sk_buff *skb)
-{
-	static const u_int8_t valid_new[] = {
-		[ICMP_ECHO] = 1,
-		[ICMP_TIMESTAMP] = 1,
-		[ICMP_INFO_REQUEST] = 1,
-		[ICMP_ADDRESS] = 1
-	};
-
-	if (conntrack->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new)
-	    || !valid_new[conntrack->tuplehash[0].tuple.dst.u.icmp.type]) {
-		/* Can't create a new ICMP `conn' with this. */
-		DEBUGP("icmp: can't create new conn with type %u\n",
-		       conntrack->tuplehash[0].tuple.dst.u.icmp.type);
-		DUMP_TUPLE(&conntrack->tuplehash[0].tuple);
-		return 0;
-	}
-	atomic_set(&conntrack->proto.icmp.count, 0);
-	return 1;
-}
-
-static int
-icmp_error_message(struct sk_buff *skb,
-		   enum ip_conntrack_info *ctinfo,
-		   unsigned int hooknum)
-{
-	struct ip_conntrack_tuple innertuple, origtuple;
-	struct {
-		struct icmphdr icmp;
-		struct iphdr ip;
-	} _in, *inside;
-	struct ip_conntrack_protocol *innerproto;
-	struct ip_conntrack_tuple_hash *h;
-	int dataoff;
-
-	IP_NF_ASSERT(skb->nfct == NULL);
-
-	/* Not enough header? */
-	inside = skb_header_pointer(skb, skb->nh.iph->ihl*4, sizeof(_in), &_in);
-	if (inside == NULL)
-		return -NF_ACCEPT;
-
-	/* Ignore ICMP's containing fragments (shouldn't happen) */
-	if (inside->ip.frag_off & htons(IP_OFFSET)) {
-		DEBUGP("icmp_error_track: fragment of proto %u\n",
-		       inside->ip.protocol);
-		return -NF_ACCEPT;
-	}
-
-	innerproto = ip_conntrack_proto_find_get(inside->ip.protocol);
-	dataoff = skb->nh.iph->ihl*4 + sizeof(inside->icmp) + inside->ip.ihl*4;
-	/* Are they talking about one of our connections? */
-	if (!ip_ct_get_tuple(&inside->ip, skb, dataoff, &origtuple, innerproto)) {
-		DEBUGP("icmp_error: ! get_tuple p=%u", inside->ip.protocol);
-		ip_conntrack_proto_put(innerproto);
-		return -NF_ACCEPT;
-	}
-
-	/* Ordinarily, we'd expect the inverted tupleproto, but it's
-	   been preserved inside the ICMP. */
-	if (!ip_ct_invert_tuple(&innertuple, &origtuple, innerproto)) {
-		DEBUGP("icmp_error_track: Can't invert tuple\n");
-		ip_conntrack_proto_put(innerproto);
-		return -NF_ACCEPT;
-	}
-	ip_conntrack_proto_put(innerproto);
-
-	*ctinfo = IP_CT_RELATED;
-
-	h = ip_conntrack_find_get(&innertuple, NULL);
-	if (!h) {
-		/* Locally generated ICMPs will match inverted if they
-		   haven't been SNAT'ed yet */
-		/* FIXME: NAT code has to handle half-done double NAT --RR */
-		if (hooknum == NF_IP_LOCAL_OUT)
-			h = ip_conntrack_find_get(&origtuple, NULL);
-
-		if (!h) {
-			DEBUGP("icmp_error_track: no match\n");
-			return -NF_ACCEPT;
-		}
-		/* Reverse direction from that found */
-		if (DIRECTION(h) != IP_CT_DIR_REPLY)
-			*ctinfo += IP_CT_IS_REPLY;
-	} else {
-		if (DIRECTION(h) == IP_CT_DIR_REPLY)
-			*ctinfo += IP_CT_IS_REPLY;
-	}
-
-	/* Update skb to refer to this connection */
-	skb->nfct = &tuplehash_to_ctrack(h)->ct_general;
-	skb->nfctinfo = *ctinfo;
-	return -NF_ACCEPT;
-}
-
-/* Small and modified version of icmp_rcv */
-static int
-icmp_error(struct sk_buff *skb, enum ip_conntrack_info *ctinfo,
-	   unsigned int hooknum)
-{
-	struct icmphdr _ih, *icmph;
-
-	/* Not enough header? */
-	icmph = skb_header_pointer(skb, skb->nh.iph->ihl*4, sizeof(_ih), &_ih);
-	if (icmph == NULL) {
-		if (LOG_INVALID(IPPROTO_ICMP))
-			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
-				      "ip_ct_icmp: short packet ");
-		return -NF_ACCEPT;
-	}
-
-	/* See ip_conntrack_proto_tcp.c */
-	if (ip_conntrack_checksum && hooknum == NF_IP_PRE_ROUTING &&
-	    nf_ip_checksum(skb, hooknum, skb->nh.iph->ihl * 4, 0)) {
-		if (LOG_INVALID(IPPROTO_ICMP))
-			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
-				      "ip_ct_icmp: bad ICMP checksum ");
-		return -NF_ACCEPT;
-	}
-
-	/*
-	 *	18 is the highest 'known' ICMP type. Anything else is a mystery
-	 *
-	 *	RFC 1122: 3.2.2  Unknown ICMP messages types MUST be silently
-	 *		  discarded.
-	 */
-	if (icmph->type > NR_ICMP_TYPES) {
-		if (LOG_INVALID(IPPROTO_ICMP))
-			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
-				      "ip_ct_icmp: invalid ICMP type ");
-		return -NF_ACCEPT;
-	}
-
-	/* Need to track icmp error message? */
-	if (icmph->type != ICMP_DEST_UNREACH
-	    && icmph->type != ICMP_SOURCE_QUENCH
-	    && icmph->type != ICMP_TIME_EXCEEDED
-	    && icmph->type != ICMP_PARAMETERPROB
-	    && icmph->type != ICMP_REDIRECT)
-		return NF_ACCEPT;
-
-	return icmp_error_message(skb, ctinfo, hooknum);
-}
-
-#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
-    defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
-static int icmp_tuple_to_nfattr(struct sk_buff *skb,
-				const struct ip_conntrack_tuple *t)
-{
-	NFA_PUT(skb, CTA_PROTO_ICMP_ID, sizeof(__be16),
-		&t->src.u.icmp.id);
-	NFA_PUT(skb, CTA_PROTO_ICMP_TYPE, sizeof(u_int8_t),
-		&t->dst.u.icmp.type);
-	NFA_PUT(skb, CTA_PROTO_ICMP_CODE, sizeof(u_int8_t),
-		&t->dst.u.icmp.code);
-
-	return 0;
-
-nfattr_failure:
-	return -1;
-}
-
-static int icmp_nfattr_to_tuple(struct nfattr *tb[],
-				struct ip_conntrack_tuple *tuple)
-{
-	if (!tb[CTA_PROTO_ICMP_TYPE-1]
-	    || !tb[CTA_PROTO_ICMP_CODE-1]
-	    || !tb[CTA_PROTO_ICMP_ID-1])
-		return -EINVAL;
-
-	tuple->dst.u.icmp.type =
-			*(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_TYPE-1]);
-	tuple->dst.u.icmp.code =
-			*(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_CODE-1]);
-	tuple->src.u.icmp.id =
-			*(__be16 *)NFA_DATA(tb[CTA_PROTO_ICMP_ID-1]);
-
-	if (tuple->dst.u.icmp.type >= sizeof(invmap)
-	    || !invmap[tuple->dst.u.icmp.type])
-		return -EINVAL;
-
-	return 0;
-}
-#endif
-
-struct ip_conntrack_protocol ip_conntrack_protocol_icmp =
-{
-	.proto 			= IPPROTO_ICMP,
-	.name 			= "icmp",
-	.pkt_to_tuple		= icmp_pkt_to_tuple,
-	.invert_tuple		= icmp_invert_tuple,
-	.print_tuple		= icmp_print_tuple,
-	.print_conntrack	= icmp_print_conntrack,
-	.packet			= icmp_packet,
-	.new			= icmp_new,
-	.error			= icmp_error,
-#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
-    defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
-	.tuple_to_nfattr	= icmp_tuple_to_nfattr,
-	.nfattr_to_tuple	= icmp_nfattr_to_tuple,
-#endif
-};
diff --git a/net/ipv4/netfilter/ip_conntrack_proto_sctp.c b/net/ipv4/netfilter/ip_conntrack_proto_sctp.c
deleted file mode 100644
index e694299..0000000
--- a/net/ipv4/netfilter/ip_conntrack_proto_sctp.c
+++ /dev/null
@@ -1,659 +0,0 @@
-/*
- * Connection tracking protocol helper module for SCTP.
- *
- * SCTP is defined in RFC 2960. References to various sections in this code
- * are to this RFC.
- *
- * 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.
- */
-
-/*
- * Added support for proc manipulation of timeouts.
- */
-
-#include <linux/types.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/netfilter.h>
-#include <linux/module.h>
-#include <linux/in.h>
-#include <linux/ip.h>
-#include <linux/sctp.h>
-#include <linux/string.h>
-#include <linux/seq_file.h>
-
-#include <linux/netfilter_ipv4/ip_conntrack.h>
-#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
-
-#if 0
-#define DEBUGP(format, ...) printk(format, ## __VA_ARGS__)
-#else
-#define DEBUGP(format, args...)
-#endif
-
-/* Protects conntrack->proto.sctp */
-static DEFINE_RWLOCK(sctp_lock);
-
-/* FIXME: Examine ipfilter's timeouts and conntrack transitions more
-   closely.  They're more complex. --RR
-
-   And so for me for SCTP :D -Kiran */
-
-static const char *sctp_conntrack_names[] = {
-	"NONE",
-	"CLOSED",
-	"COOKIE_WAIT",
-	"COOKIE_ECHOED",
-	"ESTABLISHED",
-	"SHUTDOWN_SENT",
-	"SHUTDOWN_RECD",
-	"SHUTDOWN_ACK_SENT",
-};
-
-#define SECS  * HZ
-#define MINS  * 60 SECS
-#define HOURS * 60 MINS
-#define DAYS  * 24 HOURS
-
-static unsigned int ip_ct_sctp_timeout_closed __read_mostly           = 10 SECS;
-static unsigned int ip_ct_sctp_timeout_cookie_wait __read_mostly      =  3 SECS;
-static unsigned int ip_ct_sctp_timeout_cookie_echoed __read_mostly    =  3 SECS;
-static unsigned int ip_ct_sctp_timeout_established __read_mostly      =  5 DAYS;
-static unsigned int ip_ct_sctp_timeout_shutdown_sent __read_mostly    = 300 SECS / 1000;
-static unsigned int ip_ct_sctp_timeout_shutdown_recd __read_mostly    = 300 SECS / 1000;
-static unsigned int ip_ct_sctp_timeout_shutdown_ack_sent __read_mostly = 3 SECS;
-
-static const unsigned int * sctp_timeouts[]
-= { NULL,                                  /* SCTP_CONNTRACK_NONE  */
-    &ip_ct_sctp_timeout_closed,	           /* SCTP_CONNTRACK_CLOSED */
-    &ip_ct_sctp_timeout_cookie_wait,       /* SCTP_CONNTRACK_COOKIE_WAIT */
-    &ip_ct_sctp_timeout_cookie_echoed,     /* SCTP_CONNTRACK_COOKIE_ECHOED */
-    &ip_ct_sctp_timeout_established,       /* SCTP_CONNTRACK_ESTABLISHED */
-    &ip_ct_sctp_timeout_shutdown_sent,     /* SCTP_CONNTRACK_SHUTDOWN_SENT */
-    &ip_ct_sctp_timeout_shutdown_recd,     /* SCTP_CONNTRACK_SHUTDOWN_RECD */
-    &ip_ct_sctp_timeout_shutdown_ack_sent  /* SCTP_CONNTRACK_SHUTDOWN_ACK_SENT */
- };
-
-#define sNO SCTP_CONNTRACK_NONE
-#define	sCL SCTP_CONNTRACK_CLOSED
-#define	sCW SCTP_CONNTRACK_COOKIE_WAIT
-#define	sCE SCTP_CONNTRACK_COOKIE_ECHOED
-#define	sES SCTP_CONNTRACK_ESTABLISHED
-#define	sSS SCTP_CONNTRACK_SHUTDOWN_SENT
-#define	sSR SCTP_CONNTRACK_SHUTDOWN_RECD
-#define	sSA SCTP_CONNTRACK_SHUTDOWN_ACK_SENT
-#define	sIV SCTP_CONNTRACK_MAX
-
-/*
-	These are the descriptions of the states:
-
-NOTE: These state names are tantalizingly similar to the states of an
-SCTP endpoint. But the interpretation of the states is a little different,
-considering that these are the states of the connection and not of an end
-point. Please note the subtleties. -Kiran
-
-NONE              - Nothing so far.
-COOKIE WAIT       - We have seen an INIT chunk in the original direction, or also
-		    an INIT_ACK chunk in the reply direction.
-COOKIE ECHOED     - We have seen a COOKIE_ECHO chunk in the original direction.
-ESTABLISHED       - We have seen a COOKIE_ACK in the reply direction.
-SHUTDOWN_SENT     - We have seen a SHUTDOWN chunk in the original direction.
-SHUTDOWN_RECD     - We have seen a SHUTDOWN chunk in the reply directoin.
-SHUTDOWN_ACK_SENT - We have seen a SHUTDOWN_ACK chunk in the direction opposite
-		    to that of the SHUTDOWN chunk.
-CLOSED            - We have seen a SHUTDOWN_COMPLETE chunk in the direction of
-		    the SHUTDOWN chunk. Connection is closed.
-*/
-
-/* TODO
- - I have assumed that the first INIT is in the original direction.
- This messes things when an INIT comes in the reply direction in CLOSED
- state.
- - Check the error type in the reply dir before transitioning from
-cookie echoed to closed.
- - Sec 5.2.4 of RFC 2960
- - Multi Homing support.
-*/
-
-/* SCTP conntrack state transitions */
-static const enum sctp_conntrack sctp_conntracks[2][9][SCTP_CONNTRACK_MAX] = {
-	{
-/*	ORIGINAL	*/
-/*                  sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA */
-/* init         */ {sCW, sCW, sCW, sCE, sES, sSS, sSR, sSA},
-/* init_ack     */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA},
-/* abort        */ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL},
-/* shutdown     */ {sCL, sCL, sCW, sCE, sSS, sSS, sSR, sSA},
-/* shutdown_ack */ {sSA, sCL, sCW, sCE, sES, sSA, sSA, sSA},
-/* error        */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant have Stale cookie*/
-/* cookie_echo  */ {sCL, sCL, sCE, sCE, sES, sSS, sSR, sSA},/* 5.2.4 - Big TODO */
-/* cookie_ack   */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant come in orig dir */
-/* shutdown_comp*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sCL}
-	},
-	{
-/*	REPLY	*/
-/*                  sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA */
-/* init         */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* INIT in sCL Big TODO */
-/* init_ack     */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA},
-/* abort        */ {sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL},
-/* shutdown     */ {sIV, sCL, sCW, sCE, sSR, sSS, sSR, sSA},
-/* shutdown_ack */ {sIV, sCL, sCW, sCE, sES, sSA, sSA, sSA},
-/* error        */ {sIV, sCL, sCW, sCL, sES, sSS, sSR, sSA},
-/* cookie_echo  */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant come in reply dir */
-/* cookie_ack   */ {sIV, sCL, sCW, sES, sES, sSS, sSR, sSA},
-/* shutdown_comp*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sCL}
-	}
-};
-
-static int sctp_pkt_to_tuple(const struct sk_buff *skb,
-			     unsigned int dataoff,
-			     struct ip_conntrack_tuple *tuple)
-{
-	sctp_sctphdr_t _hdr, *hp;
-
-	DEBUGP(__FUNCTION__);
-	DEBUGP("\n");
-
-	/* Actually only need first 8 bytes. */
-	hp = skb_header_pointer(skb, dataoff, 8, &_hdr);
-	if (hp == NULL)
-		return 0;
-
-	tuple->src.u.sctp.port = hp->source;
-	tuple->dst.u.sctp.port = hp->dest;
-	return 1;
-}
-
-static int sctp_invert_tuple(struct ip_conntrack_tuple *tuple,
-			     const struct ip_conntrack_tuple *orig)
-{
-	DEBUGP(__FUNCTION__);
-	DEBUGP("\n");
-
-	tuple->src.u.sctp.port = orig->dst.u.sctp.port;
-	tuple->dst.u.sctp.port = orig->src.u.sctp.port;
-	return 1;
-}
-
-/* Print out the per-protocol part of the tuple. */
-static int sctp_print_tuple(struct seq_file *s,
-			    const struct ip_conntrack_tuple *tuple)
-{
-	DEBUGP(__FUNCTION__);
-	DEBUGP("\n");
-
-	return seq_printf(s, "sport=%hu dport=%hu ",
-			  ntohs(tuple->src.u.sctp.port),
-			  ntohs(tuple->dst.u.sctp.port));
-}
-
-/* Print out the private part of the conntrack. */
-static int sctp_print_conntrack(struct seq_file *s,
-				const struct ip_conntrack *conntrack)
-{
-	enum sctp_conntrack state;
-
-	DEBUGP(__FUNCTION__);
-	DEBUGP("\n");
-
-	read_lock_bh(&sctp_lock);
-	state = conntrack->proto.sctp.state;
-	read_unlock_bh(&sctp_lock);
-
-	return seq_printf(s, "%s ", sctp_conntrack_names[state]);
-}
-
-#define for_each_sctp_chunk(skb, sch, _sch, offset, count)		\
-for (offset = skb->nh.iph->ihl * 4 + sizeof(sctp_sctphdr_t), count = 0;	\
-	offset < skb->len &&						\
-	(sch = skb_header_pointer(skb, offset, sizeof(_sch), &_sch));	\
-	offset += (ntohs(sch->length) + 3) & ~3, count++)
-
-/* Some validity checks to make sure the chunks are fine */
-static int do_basic_checks(struct ip_conntrack *conntrack,
-			   const struct sk_buff *skb,
-			   char *map)
-{
-	u_int32_t offset, count;
-	sctp_chunkhdr_t _sch, *sch;
-	int flag;
-
-	DEBUGP(__FUNCTION__);
-	DEBUGP("\n");
-
-	flag = 0;
-
-	for_each_sctp_chunk (skb, sch, _sch, offset, count) {
-		DEBUGP("Chunk Num: %d  Type: %d\n", count, sch->type);
-
-		if (sch->type == SCTP_CID_INIT
-			|| sch->type == SCTP_CID_INIT_ACK
-			|| sch->type == SCTP_CID_SHUTDOWN_COMPLETE) {
-			flag = 1;
-		}
-
-		/*
-		 * Cookie Ack/Echo chunks not the first OR
-		 * Init / Init Ack / Shutdown compl chunks not the only chunks
-		 * OR zero-length.
-		 */
-		if (((sch->type == SCTP_CID_COOKIE_ACK
-			|| sch->type == SCTP_CID_COOKIE_ECHO
-			|| flag)
-		      && count !=0) || !sch->length) {
-			DEBUGP("Basic checks failed\n");
-			return 1;
-		}
-
-		if (map) {
-			set_bit(sch->type, (void *)map);
-		}
-	}
-
-	DEBUGP("Basic checks passed\n");
-	return count == 0;
-}
-
-static int new_state(enum ip_conntrack_dir dir,
-		     enum sctp_conntrack cur_state,
-		     int chunk_type)
-{
-	int i;
-
-	DEBUGP(__FUNCTION__);
-	DEBUGP("\n");
-
-	DEBUGP("Chunk type: %d\n", chunk_type);
-
-	switch (chunk_type) {
-		case SCTP_CID_INIT:
-			DEBUGP("SCTP_CID_INIT\n");
-			i = 0; break;
-		case SCTP_CID_INIT_ACK:
-			DEBUGP("SCTP_CID_INIT_ACK\n");
-			i = 1; break;
-		case SCTP_CID_ABORT:
-			DEBUGP("SCTP_CID_ABORT\n");
-			i = 2; break;
-		case SCTP_CID_SHUTDOWN:
-			DEBUGP("SCTP_CID_SHUTDOWN\n");
-			i = 3; break;
-		case SCTP_CID_SHUTDOWN_ACK:
-			DEBUGP("SCTP_CID_SHUTDOWN_ACK\n");
-			i = 4; break;
-		case SCTP_CID_ERROR:
-			DEBUGP("SCTP_CID_ERROR\n");
-			i = 5; break;
-		case SCTP_CID_COOKIE_ECHO:
-			DEBUGP("SCTP_CID_COOKIE_ECHO\n");
-			i = 6; break;
-		case SCTP_CID_COOKIE_ACK:
-			DEBUGP("SCTP_CID_COOKIE_ACK\n");
-			i = 7; break;
-		case SCTP_CID_SHUTDOWN_COMPLETE:
-			DEBUGP("SCTP_CID_SHUTDOWN_COMPLETE\n");
-			i = 8; break;
-		default:
-			/* Other chunks like DATA, SACK, HEARTBEAT and
-			its ACK do not cause a change in state */
-			DEBUGP("Unknown chunk type, Will stay in %s\n",
-						sctp_conntrack_names[cur_state]);
-			return cur_state;
-	}
-
-	DEBUGP("dir: %d   cur_state: %s  chunk_type: %d  new_state: %s\n",
-			dir, sctp_conntrack_names[cur_state], chunk_type,
-			sctp_conntrack_names[sctp_conntracks[dir][i][cur_state]]);
-
-	return sctp_conntracks[dir][i][cur_state];
-}
-
-/* Returns verdict for packet, or -1 for invalid. */
-static int sctp_packet(struct ip_conntrack *conntrack,
-		       const struct sk_buff *skb,
-		       enum ip_conntrack_info ctinfo)
-{
-	enum sctp_conntrack newconntrack, oldsctpstate;
-	struct iphdr *iph = skb->nh.iph;
-	sctp_sctphdr_t _sctph, *sh;
-	sctp_chunkhdr_t _sch, *sch;
-	u_int32_t offset, count;
-	char map[256 / sizeof (char)] = {0};
-
-	DEBUGP(__FUNCTION__);
-	DEBUGP("\n");
-
-	sh = skb_header_pointer(skb, iph->ihl * 4, sizeof(_sctph), &_sctph);
-	if (sh == NULL)
-		return -1;
-
-	if (do_basic_checks(conntrack, skb, map) != 0)
-		return -1;
-
-	/* Check the verification tag (Sec 8.5) */
-	if (!test_bit(SCTP_CID_INIT, (void *)map)
-		&& !test_bit(SCTP_CID_SHUTDOWN_COMPLETE, (void *)map)
-		&& !test_bit(SCTP_CID_COOKIE_ECHO, (void *)map)
-		&& !test_bit(SCTP_CID_ABORT, (void *)map)
-		&& !test_bit(SCTP_CID_SHUTDOWN_ACK, (void *)map)
-		&& (sh->vtag != conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)])) {
-		DEBUGP("Verification tag check failed\n");
-		return -1;
-	}
-
-	oldsctpstate = newconntrack = SCTP_CONNTRACK_MAX;
-	for_each_sctp_chunk (skb, sch, _sch, offset, count) {
-		write_lock_bh(&sctp_lock);
-
-		/* Special cases of Verification tag check (Sec 8.5.1) */
-		if (sch->type == SCTP_CID_INIT) {
-			/* Sec 8.5.1 (A) */
-			if (sh->vtag != 0) {
-				write_unlock_bh(&sctp_lock);
-				return -1;
-			}
-		} else if (sch->type == SCTP_CID_ABORT) {
-			/* Sec 8.5.1 (B) */
-			if (!(sh->vtag == conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)])
-				&& !(sh->vtag == conntrack->proto.sctp.vtag
-							[1 - CTINFO2DIR(ctinfo)])) {
-				write_unlock_bh(&sctp_lock);
-				return -1;
-			}
-		} else if (sch->type == SCTP_CID_SHUTDOWN_COMPLETE) {
-			/* Sec 8.5.1 (C) */
-			if (!(sh->vtag == conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)])
-				&& !(sh->vtag == conntrack->proto.sctp.vtag
-							[1 - CTINFO2DIR(ctinfo)]
-					&& (sch->flags & 1))) {
-				write_unlock_bh(&sctp_lock);
-				return -1;
-			}
-		} else if (sch->type == SCTP_CID_COOKIE_ECHO) {
-			/* Sec 8.5.1 (D) */
-			if (!(sh->vtag == conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)])) {
-				write_unlock_bh(&sctp_lock);
-				return -1;
-			}
-		}
-
-		oldsctpstate = conntrack->proto.sctp.state;
-		newconntrack = new_state(CTINFO2DIR(ctinfo), oldsctpstate, sch->type);
-
-		/* Invalid */
-		if (newconntrack == SCTP_CONNTRACK_MAX) {
-			DEBUGP("ip_conntrack_sctp: Invalid dir=%i ctype=%u conntrack=%u\n",
-			       CTINFO2DIR(ctinfo), sch->type, oldsctpstate);
-			write_unlock_bh(&sctp_lock);
-			return -1;
-		}
-
-		/* If it is an INIT or an INIT ACK note down the vtag */
-		if (sch->type == SCTP_CID_INIT
-			|| sch->type == SCTP_CID_INIT_ACK) {
-			sctp_inithdr_t _inithdr, *ih;
-
-			ih = skb_header_pointer(skb, offset + sizeof(sctp_chunkhdr_t),
-						sizeof(_inithdr), &_inithdr);
-			if (ih == NULL) {
-					write_unlock_bh(&sctp_lock);
-					return -1;
-			}
-			DEBUGP("Setting vtag %x for dir %d\n",
-					ih->init_tag, !CTINFO2DIR(ctinfo));
-			conntrack->proto.sctp.vtag[!CTINFO2DIR(ctinfo)] = ih->init_tag;
-		}
-
-		conntrack->proto.sctp.state = newconntrack;
-		if (oldsctpstate != newconntrack)
-			ip_conntrack_event_cache(IPCT_PROTOINFO, skb);
-		write_unlock_bh(&sctp_lock);
-	}
-
-	ip_ct_refresh_acct(conntrack, ctinfo, skb, *sctp_timeouts[newconntrack]);
-
-	if (oldsctpstate == SCTP_CONNTRACK_COOKIE_ECHOED
-		&& CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY
-		&& newconntrack == SCTP_CONNTRACK_ESTABLISHED) {
-		DEBUGP("Setting assured bit\n");
-		set_bit(IPS_ASSURED_BIT, &conntrack->status);
-		ip_conntrack_event_cache(IPCT_STATUS, skb);
-	}
-
-	return NF_ACCEPT;
-}
-
-/* Called when a new connection for this protocol found. */
-static int sctp_new(struct ip_conntrack *conntrack,
-		    const struct sk_buff *skb)
-{
-	enum sctp_conntrack newconntrack;
-	struct iphdr *iph = skb->nh.iph;
-	sctp_sctphdr_t _sctph, *sh;
-	sctp_chunkhdr_t _sch, *sch;
-	u_int32_t offset, count;
-	char map[256 / sizeof (char)] = {0};
-
-	DEBUGP(__FUNCTION__);
-	DEBUGP("\n");
-
-	sh = skb_header_pointer(skb, iph->ihl * 4, sizeof(_sctph), &_sctph);
-	if (sh == NULL)
-		return 0;
-
-	if (do_basic_checks(conntrack, skb, map) != 0)
-		return 0;
-
-	/* If an OOTB packet has any of these chunks discard (Sec 8.4) */
-	if ((test_bit (SCTP_CID_ABORT, (void *)map))
-		|| (test_bit (SCTP_CID_SHUTDOWN_COMPLETE, (void *)map))
-		|| (test_bit (SCTP_CID_COOKIE_ACK, (void *)map))) {
-		return 0;
-	}
-
-	newconntrack = SCTP_CONNTRACK_MAX;
-	for_each_sctp_chunk (skb, sch, _sch, offset, count) {
-		/* Don't need lock here: this conntrack not in circulation yet */
-		newconntrack = new_state (IP_CT_DIR_ORIGINAL,
-						SCTP_CONNTRACK_NONE, sch->type);
-
-		/* Invalid: delete conntrack */
-		if (newconntrack == SCTP_CONNTRACK_MAX) {
-			DEBUGP("ip_conntrack_sctp: invalid new deleting.\n");
-			return 0;
-		}
-
-		/* Copy the vtag into the state info */
-		if (sch->type == SCTP_CID_INIT) {
-			if (sh->vtag == 0) {
-				sctp_inithdr_t _inithdr, *ih;
-
-				ih = skb_header_pointer(skb, offset + sizeof(sctp_chunkhdr_t),
-							sizeof(_inithdr), &_inithdr);
-				if (ih == NULL)
-					return 0;
-
-				DEBUGP("Setting vtag %x for new conn\n",
-					ih->init_tag);
-
-				conntrack->proto.sctp.vtag[IP_CT_DIR_REPLY] =
-								ih->init_tag;
-			} else {
-				/* Sec 8.5.1 (A) */
-				return 0;
-			}
-		}
-		/* If it is a shutdown ack OOTB packet, we expect a return
-		   shutdown complete, otherwise an ABORT Sec 8.4 (5) and (8) */
-		else {
-			DEBUGP("Setting vtag %x for new conn OOTB\n",
-				sh->vtag);
-			conntrack->proto.sctp.vtag[IP_CT_DIR_REPLY] = sh->vtag;
-		}
-
-		conntrack->proto.sctp.state = newconntrack;
-	}
-
-	return 1;
-}
-
-static struct ip_conntrack_protocol ip_conntrack_protocol_sctp = {
-	.proto 		 = IPPROTO_SCTP,
-	.name 		 = "sctp",
-	.pkt_to_tuple 	 = sctp_pkt_to_tuple,
-	.invert_tuple 	 = sctp_invert_tuple,
-	.print_tuple 	 = sctp_print_tuple,
-	.print_conntrack = sctp_print_conntrack,
-	.packet 	 = sctp_packet,
-	.new 		 = sctp_new,
-	.destroy 	 = NULL,
-	.me 		 = THIS_MODULE,
-#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
-    defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
-	.tuple_to_nfattr = ip_ct_port_tuple_to_nfattr,
-	.nfattr_to_tuple = ip_ct_port_nfattr_to_tuple,
-#endif
-};
-
-#ifdef CONFIG_SYSCTL
-static ctl_table ip_ct_sysctl_table[] = {
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_CLOSED,
-		.procname	= "ip_conntrack_sctp_timeout_closed",
-		.data		= &ip_ct_sctp_timeout_closed,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_jiffies,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_COOKIE_WAIT,
-		.procname	= "ip_conntrack_sctp_timeout_cookie_wait",
-		.data		= &ip_ct_sctp_timeout_cookie_wait,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_jiffies,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_COOKIE_ECHOED,
-		.procname	= "ip_conntrack_sctp_timeout_cookie_echoed",
-		.data		= &ip_ct_sctp_timeout_cookie_echoed,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_jiffies,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_ESTABLISHED,
-		.procname	= "ip_conntrack_sctp_timeout_established",
-		.data		= &ip_ct_sctp_timeout_established,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_jiffies,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_SENT,
-		.procname	= "ip_conntrack_sctp_timeout_shutdown_sent",
-		.data		= &ip_ct_sctp_timeout_shutdown_sent,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_jiffies,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_RECD,
-		.procname	= "ip_conntrack_sctp_timeout_shutdown_recd",
-		.data		= &ip_ct_sctp_timeout_shutdown_recd,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_jiffies,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_ACK_SENT,
-		.procname	= "ip_conntrack_sctp_timeout_shutdown_ack_sent",
-		.data		= &ip_ct_sctp_timeout_shutdown_ack_sent,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_jiffies,
-	},
-	{ .ctl_name = 0 }
-};
-
-static ctl_table ip_ct_netfilter_table[] = {
-	{
-		.ctl_name	= NET_IPV4_NETFILTER,
-		.procname	= "netfilter",
-		.mode		= 0555,
-		.child		= ip_ct_sysctl_table,
-	},
-	{ .ctl_name = 0 }
-};
-
-static ctl_table ip_ct_ipv4_table[] = {
-	{
-		.ctl_name	= NET_IPV4,
-		.procname	= "ipv4",
-		.mode		= 0555,
-		.child		= ip_ct_netfilter_table,
-	},
-	{ .ctl_name = 0 }
-};
-
-static ctl_table ip_ct_net_table[] = {
-	{
-		.ctl_name	= CTL_NET,
-		.procname	= "net",
-		.mode		= 0555,
-		.child		= ip_ct_ipv4_table,
-	},
-	{ .ctl_name = 0 }
-};
-
-static struct ctl_table_header *ip_ct_sysctl_header;
-#endif
-
-static int __init ip_conntrack_proto_sctp_init(void)
-{
-	int ret;
-
-	ret = ip_conntrack_protocol_register(&ip_conntrack_protocol_sctp);
-	if (ret) {
-		printk("ip_conntrack_proto_sctp: protocol register failed\n");
-		goto out;
-	}
-
-#ifdef CONFIG_SYSCTL
-	ip_ct_sysctl_header = register_sysctl_table(ip_ct_net_table);
-	if (ip_ct_sysctl_header == NULL) {
-		ret = -ENOMEM;
-		printk("ip_conntrack_proto_sctp: can't register to sysctl.\n");
-		goto cleanup;
-	}
-#endif
-
-	return ret;
-
-#ifdef CONFIG_SYSCTL
- cleanup:
-	ip_conntrack_protocol_unregister(&ip_conntrack_protocol_sctp);
-#endif
- out:
-	DEBUGP("SCTP conntrack module loading %s\n",
-					ret ? "failed": "succeeded");
-	return ret;
-}
-
-static void __exit ip_conntrack_proto_sctp_fini(void)
-{
-	ip_conntrack_protocol_unregister(&ip_conntrack_protocol_sctp);
-#ifdef CONFIG_SYSCTL
-	unregister_sysctl_table(ip_ct_sysctl_header);
-#endif
-	DEBUGP("SCTP conntrack module unloaded\n");
-}
-
-module_init(ip_conntrack_proto_sctp_init);
-module_exit(ip_conntrack_proto_sctp_fini);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Kiran Kumar Immidi");
-MODULE_DESCRIPTION("Netfilter connection tracking protocol helper for SCTP");
diff --git a/net/ipv4/netfilter/ip_conntrack_proto_tcp.c b/net/ipv4/netfilter/ip_conntrack_proto_tcp.c
deleted file mode 100644
index 0a72eab..0000000
--- a/net/ipv4/netfilter/ip_conntrack_proto_tcp.c
+++ /dev/null
@@ -1,1164 +0,0 @@
-/* (C) 1999-2001 Paul `Rusty' Russell
- * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.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.
- *
- * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>:
- *	- Real stateful connection tracking
- *	- Modified state transitions table
- *	- Window scaling support added
- *	- SACK support added
- *
- * Willy Tarreau:
- *	- State table bugfixes
- *	- More robust state changes
- *	- Tuning timer parameters
- *
- * version 2.2
- */
-
-#include <linux/types.h>
-#include <linux/timer.h>
-#include <linux/netfilter.h>
-#include <linux/module.h>
-#include <linux/in.h>
-#include <linux/ip.h>
-#include <linux/tcp.h>
-#include <linux/spinlock.h>
-
-#include <net/tcp.h>
-
-#include <linux/netfilter_ipv4.h>
-#include <linux/netfilter_ipv4/ip_conntrack.h>
-#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
-
-#if 0
-#define DEBUGP printk
-#define DEBUGP_VARS
-#else
-#define DEBUGP(format, args...)
-#endif
-
-/* Protects conntrack->proto.tcp */
-static DEFINE_RWLOCK(tcp_lock);
-
-/* "Be conservative in what you do,
-    be liberal in what you accept from others."
-    If it's non-zero, we mark only out of window RST segments as INVALID. */
-int ip_ct_tcp_be_liberal __read_mostly = 0;
-
-/* If it is set to zero, we disable picking up already established
-   connections. */
-int ip_ct_tcp_loose __read_mostly = 1;
-
-/* Max number of the retransmitted packets without receiving an (acceptable)
-   ACK from the destination. If this number is reached, a shorter timer
-   will be started. */
-int ip_ct_tcp_max_retrans __read_mostly = 3;
-
-  /* FIXME: Examine ipfilter's timeouts and conntrack transitions more
-     closely.  They're more complex. --RR */
-
-static const char *tcp_conntrack_names[] = {
-	"NONE",
-	"SYN_SENT",
-	"SYN_RECV",
-	"ESTABLISHED",
-	"FIN_WAIT",
-	"CLOSE_WAIT",
-	"LAST_ACK",
-	"TIME_WAIT",
-	"CLOSE",
-	"LISTEN"
-};
-
-#define SECS * HZ
-#define MINS * 60 SECS
-#define HOURS * 60 MINS
-#define DAYS * 24 HOURS
-
-unsigned int ip_ct_tcp_timeout_syn_sent __read_mostly =      2 MINS;
-unsigned int ip_ct_tcp_timeout_syn_recv __read_mostly =     60 SECS;
-unsigned int ip_ct_tcp_timeout_established __read_mostly =   5 DAYS;
-unsigned int ip_ct_tcp_timeout_fin_wait __read_mostly =      2 MINS;
-unsigned int ip_ct_tcp_timeout_close_wait __read_mostly =   60 SECS;
-unsigned int ip_ct_tcp_timeout_last_ack __read_mostly =     30 SECS;
-unsigned int ip_ct_tcp_timeout_time_wait __read_mostly =     2 MINS;
-unsigned int ip_ct_tcp_timeout_close __read_mostly =        10 SECS;
-
-/* RFC1122 says the R2 limit should be at least 100 seconds.
-   Linux uses 15 packets as limit, which corresponds
-   to ~13-30min depending on RTO. */
-unsigned int ip_ct_tcp_timeout_max_retrans __read_mostly =   5 MINS;
-
-static const unsigned int * tcp_timeouts[]
-= { NULL,                              /*      TCP_CONNTRACK_NONE */
-    &ip_ct_tcp_timeout_syn_sent,       /*      TCP_CONNTRACK_SYN_SENT, */
-    &ip_ct_tcp_timeout_syn_recv,       /*      TCP_CONNTRACK_SYN_RECV, */
-    &ip_ct_tcp_timeout_established,    /*      TCP_CONNTRACK_ESTABLISHED,      */
-    &ip_ct_tcp_timeout_fin_wait,       /*      TCP_CONNTRACK_FIN_WAIT, */
-    &ip_ct_tcp_timeout_close_wait,     /*      TCP_CONNTRACK_CLOSE_WAIT,       */
-    &ip_ct_tcp_timeout_last_ack,       /*      TCP_CONNTRACK_LAST_ACK, */
-    &ip_ct_tcp_timeout_time_wait,      /*      TCP_CONNTRACK_TIME_WAIT,        */
-    &ip_ct_tcp_timeout_close,          /*      TCP_CONNTRACK_CLOSE,    */
-    NULL,                              /*      TCP_CONNTRACK_LISTEN */
- };
-
-#define sNO TCP_CONNTRACK_NONE
-#define sSS TCP_CONNTRACK_SYN_SENT
-#define sSR TCP_CONNTRACK_SYN_RECV
-#define sES TCP_CONNTRACK_ESTABLISHED
-#define sFW TCP_CONNTRACK_FIN_WAIT
-#define sCW TCP_CONNTRACK_CLOSE_WAIT
-#define sLA TCP_CONNTRACK_LAST_ACK
-#define sTW TCP_CONNTRACK_TIME_WAIT
-#define sCL TCP_CONNTRACK_CLOSE
-#define sLI TCP_CONNTRACK_LISTEN
-#define sIV TCP_CONNTRACK_MAX
-#define sIG TCP_CONNTRACK_IGNORE
-
-/* What TCP flags are set from RST/SYN/FIN/ACK. */
-enum tcp_bit_set {
-	TCP_SYN_SET,
-	TCP_SYNACK_SET,
-	TCP_FIN_SET,
-	TCP_ACK_SET,
-	TCP_RST_SET,
-	TCP_NONE_SET,
-};
-
-/*
- * The TCP state transition table needs a few words...
- *
- * We are the man in the middle. All the packets go through us
- * but might get lost in transit to the destination.
- * It is assumed that the destinations can't receive segments
- * we haven't seen.
- *
- * The checked segment is in window, but our windows are *not*
- * equivalent with the ones of the sender/receiver. We always
- * try to guess the state of the current sender.
- *
- * The meaning of the states are:
- *
- * NONE:	initial state
- * SYN_SENT:	SYN-only packet seen
- * SYN_RECV:	SYN-ACK packet seen
- * ESTABLISHED:	ACK packet seen
- * FIN_WAIT:	FIN packet seen
- * CLOSE_WAIT:	ACK seen (after FIN)
- * LAST_ACK:	FIN seen (after FIN)
- * TIME_WAIT:	last ACK seen
- * CLOSE:	closed connection
- *
- * LISTEN state is not used.
- *
- * Packets marked as IGNORED (sIG):
- *	if they may be either invalid or valid
- *	and the receiver may send back a connection
- *	closing RST or a SYN/ACK.
- *
- * Packets marked as INVALID (sIV):
- *	if they are invalid
- *	or we do not support the request (simultaneous open)
- */
-static const enum tcp_conntrack tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {
-	{
-/* ORIGINAL */
-/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
-/*syn*/	   { sSS, sSS, sIG, sIG, sIG, sIG, sIG, sSS, sSS, sIV },
-/*
- *	sNO -> sSS	Initialize a new connection
- *	sSS -> sSS	Retransmitted SYN
- *	sSR -> sIG	Late retransmitted SYN?
- *	sES -> sIG	Error: SYNs in window outside the SYN_SENT state
- *			are errors. Receiver will reply with RST
- *			and close the connection.
- *			Or we are not in sync and hold a dead connection.
- *	sFW -> sIG
- *	sCW -> sIG
- *	sLA -> sIG
- *	sTW -> sSS	Reopened connection (RFC 1122).
- *	sCL -> sSS
- */
-/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
-/*synack*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV },
-/*
- * A SYN/ACK from the client is always invalid:
- *	- either it tries to set up a simultaneous open, which is
- *	  not supported;
- *	- or the firewall has just been inserted between the two hosts
- *	  during the session set-up. The SYN will be retransmitted
- *	  by the true client (or it'll time out).
- */
-/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
-/*fin*/    { sIV, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV },
-/*
- *	sNO -> sIV	Too late and no reason to do anything...
- *	sSS -> sIV	Client migth not send FIN in this state:
- *			we enforce waiting for a SYN/ACK reply first.
- *	sSR -> sFW	Close started.
- *	sES -> sFW
- *	sFW -> sLA	FIN seen in both directions, waiting for
- *			the last ACK.
- *			Migth be a retransmitted FIN as well...
- *	sCW -> sLA
- *	sLA -> sLA	Retransmitted FIN. Remain in the same state.
- *	sTW -> sTW
- *	sCL -> sCL
- */
-/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
-/*ack*/	   { sES, sIV, sES, sES, sCW, sCW, sTW, sTW, sCL, sIV },
-/*
- *	sNO -> sES	Assumed.
- *	sSS -> sIV	ACK is invalid: we haven't seen a SYN/ACK yet.
- *	sSR -> sES	Established state is reached.
- *	sES -> sES	:-)
- *	sFW -> sCW	Normal close request answered by ACK.
- *	sCW -> sCW
- *	sLA -> sTW	Last ACK detected.
- *	sTW -> sTW	Retransmitted last ACK. Remain in the same state.
- *	sCL -> sCL
- */
-/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
-/*rst*/    { sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV },
-/*none*/   { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
-	},
-	{
-/* REPLY */
-/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
-/*syn*/	   { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV },
-/*
- *	sNO -> sIV	Never reached.
- *	sSS -> sIV	Simultaneous open, not supported
- *	sSR -> sIV	Simultaneous open, not supported.
- *	sES -> sIV	Server may not initiate a connection.
- *	sFW -> sIV
- *	sCW -> sIV
- *	sLA -> sIV
- *	sTW -> sIV	Reopened connection, but server may not do it.
- *	sCL -> sIV
- */
-/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
-/*synack*/ { sIV, sSR, sSR, sIG, sIG, sIG, sIG, sIG, sIG, sIV },
-/*
- *	sSS -> sSR	Standard open.
- *	sSR -> sSR	Retransmitted SYN/ACK.
- *	sES -> sIG	Late retransmitted SYN/ACK?
- *	sFW -> sIG	Might be SYN/ACK answering ignored SYN
- *	sCW -> sIG
- *	sLA -> sIG
- *	sTW -> sIG
- *	sCL -> sIG
- */
-/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
-/*fin*/    { sIV, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV },
-/*
- *	sSS -> sIV	Server might not send FIN in this state.
- *	sSR -> sFW	Close started.
- *	sES -> sFW
- *	sFW -> sLA	FIN seen in both directions.
- *	sCW -> sLA
- *	sLA -> sLA	Retransmitted FIN.
- *	sTW -> sTW
- *	sCL -> sCL
- */
-/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
-/*ack*/	   { sIV, sIG, sSR, sES, sCW, sCW, sTW, sTW, sCL, sIV },
-/*
- *	sSS -> sIG	Might be a half-open connection.
- *	sSR -> sSR	Might answer late resent SYN.
- *	sES -> sES	:-)
- *	sFW -> sCW	Normal close request answered by ACK.
- *	sCW -> sCW
- *	sLA -> sTW	Last ACK detected.
- *	sTW -> sTW	Retransmitted last ACK.
- *	sCL -> sCL
- */
-/* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI	*/
-/*rst*/    { sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV },
-/*none*/   { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
-	}
-};
-
-static int tcp_pkt_to_tuple(const struct sk_buff *skb,
-			    unsigned int dataoff,
-			    struct ip_conntrack_tuple *tuple)
-{
-	struct tcphdr _hdr, *hp;
-
-	/* Actually only need first 8 bytes. */
-	hp = skb_header_pointer(skb, dataoff, 8, &_hdr);
-	if (hp == NULL)
-		return 0;
-
-	tuple->src.u.tcp.port = hp->source;
-	tuple->dst.u.tcp.port = hp->dest;
-
-	return 1;
-}
-
-static int tcp_invert_tuple(struct ip_conntrack_tuple *tuple,
-			    const struct ip_conntrack_tuple *orig)
-{
-	tuple->src.u.tcp.port = orig->dst.u.tcp.port;
-	tuple->dst.u.tcp.port = orig->src.u.tcp.port;
-	return 1;
-}
-
-/* Print out the per-protocol part of the tuple. */
-static int tcp_print_tuple(struct seq_file *s,
-			   const struct ip_conntrack_tuple *tuple)
-{
-	return seq_printf(s, "sport=%hu dport=%hu ",
-			  ntohs(tuple->src.u.tcp.port),
-			  ntohs(tuple->dst.u.tcp.port));
-}
-
-/* Print out the private part of the conntrack. */
-static int tcp_print_conntrack(struct seq_file *s,
-			       const struct ip_conntrack *conntrack)
-{
-	enum tcp_conntrack state;
-
-	read_lock_bh(&tcp_lock);
-	state = conntrack->proto.tcp.state;
-	read_unlock_bh(&tcp_lock);
-
-	return seq_printf(s, "%s ", tcp_conntrack_names[state]);
-}
-
-#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
-    defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
-static int tcp_to_nfattr(struct sk_buff *skb, struct nfattr *nfa,
-			 const struct ip_conntrack *ct)
-{
-	struct nfattr *nest_parms;
-
-	read_lock_bh(&tcp_lock);
-	nest_parms = NFA_NEST(skb, CTA_PROTOINFO_TCP);
-	NFA_PUT(skb, CTA_PROTOINFO_TCP_STATE, sizeof(u_int8_t),
-		&ct->proto.tcp.state);
-	read_unlock_bh(&tcp_lock);
-
-	NFA_NEST_END(skb, nest_parms);
-
-	return 0;
-
-nfattr_failure:
-	read_unlock_bh(&tcp_lock);
-	return -1;
-}
-
-static const size_t cta_min_tcp[CTA_PROTOINFO_TCP_MAX] = {
-	[CTA_PROTOINFO_TCP_STATE-1]	= sizeof(u_int8_t),
-};
-
-static int nfattr_to_tcp(struct nfattr *cda[], struct ip_conntrack *ct)
-{
-	struct nfattr *attr = cda[CTA_PROTOINFO_TCP-1];
-	struct nfattr *tb[CTA_PROTOINFO_TCP_MAX];
-
-	/* updates could not contain anything about the private
-	 * protocol info, in that case skip the parsing */
-	if (!attr)
-		return 0;
-
-	nfattr_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, attr);
-
-	if (nfattr_bad_size(tb, CTA_PROTOINFO_TCP_MAX, cta_min_tcp))
-		return -EINVAL;
-
-	if (!tb[CTA_PROTOINFO_TCP_STATE-1])
-		return -EINVAL;
-
-	write_lock_bh(&tcp_lock);
-	ct->proto.tcp.state =
-		*(u_int8_t *)NFA_DATA(tb[CTA_PROTOINFO_TCP_STATE-1]);
-	write_unlock_bh(&tcp_lock);
-
-	return 0;
-}
-#endif
-
-static unsigned int get_conntrack_index(const struct tcphdr *tcph)
-{
-	if (tcph->rst) return TCP_RST_SET;
-	else if (tcph->syn) return (tcph->ack ? TCP_SYNACK_SET : TCP_SYN_SET);
-	else if (tcph->fin) return TCP_FIN_SET;
-	else if (tcph->ack) return TCP_ACK_SET;
-	else return TCP_NONE_SET;
-}
-
-/* TCP connection tracking based on 'Real Stateful TCP Packet Filtering
-   in IP Filter' by Guido van Rooij.
-
-   http://www.nluug.nl/events/sane2000/papers.html
-   http://www.iae.nl/users/guido/papers/tcp_filtering.ps.gz
-
-   The boundaries and the conditions are changed according to RFC793:
-   the packet must intersect the window (i.e. segments may be
-   after the right or before the left edge) and thus receivers may ACK
-   segments after the right edge of the window.
-
-	td_maxend = max(sack + max(win,1)) seen in reply packets
-	td_maxwin = max(max(win, 1)) + (sack - ack) seen in sent packets
-	td_maxwin += seq + len - sender.td_maxend
-			if seq + len > sender.td_maxend
-	td_end    = max(seq + len) seen in sent packets
-
-   I.   Upper bound for valid data:	seq <= sender.td_maxend
-   II.  Lower bound for valid data:	seq + len >= sender.td_end - receiver.td_maxwin
-   III.	Upper bound for valid ack:      sack <= receiver.td_end
-   IV.	Lower bound for valid ack:	ack >= receiver.td_end - MAXACKWINDOW
-
-   where sack is the highest right edge of sack block found in the packet.
-
-   The upper bound limit for a valid ack is not ignored -
-   we doesn't have to deal with fragments.
-*/
-
-static inline __u32 segment_seq_plus_len(__u32 seq,
-					 size_t len,
-					 struct iphdr *iph,
-					 struct tcphdr *tcph)
-{
-	return (seq + len - (iph->ihl + tcph->doff)*4
-		+ (tcph->syn ? 1 : 0) + (tcph->fin ? 1 : 0));
-}
-
-/* Fixme: what about big packets? */
-#define MAXACKWINCONST			66000
-#define MAXACKWINDOW(sender)						\
-	((sender)->td_maxwin > MAXACKWINCONST ? (sender)->td_maxwin	\
-					      : MAXACKWINCONST)
-
-/*
- * Simplified tcp_parse_options routine from tcp_input.c
- */
-static void tcp_options(const struct sk_buff *skb,
-			struct iphdr *iph,
-			struct tcphdr *tcph,
-			struct ip_ct_tcp_state *state)
-{
-	unsigned char buff[(15 * 4) - sizeof(struct tcphdr)];
-	unsigned char *ptr;
-	int length = (tcph->doff*4) - sizeof(struct tcphdr);
-
-	if (!length)
-		return;
-
-	ptr = skb_header_pointer(skb,
-				 (iph->ihl * 4) + sizeof(struct tcphdr),
-				 length, buff);
-	BUG_ON(ptr == NULL);
-
-	state->td_scale =
-	state->flags = 0;
-
-	while (length > 0) {
-		int opcode=*ptr++;
-		int opsize;
-
-		switch (opcode) {
-		case TCPOPT_EOL:
-			return;
-		case TCPOPT_NOP:	/* Ref: RFC 793 section 3.1 */
-			length--;
-			continue;
-		default:
-			opsize=*ptr++;
-			if (opsize < 2) /* "silly options" */
-				return;
-			if (opsize > length)
-				break;	/* don't parse partial options */
-
-			if (opcode == TCPOPT_SACK_PERM
-			    && opsize == TCPOLEN_SACK_PERM)
-				state->flags |= IP_CT_TCP_FLAG_SACK_PERM;
-			else if (opcode == TCPOPT_WINDOW
-				 && opsize == TCPOLEN_WINDOW) {
-				state->td_scale = *(u_int8_t *)ptr;
-
-				if (state->td_scale > 14) {
-					/* See RFC1323 */
-					state->td_scale = 14;
-				}
-				state->flags |=
-					IP_CT_TCP_FLAG_WINDOW_SCALE;
-			}
-			ptr += opsize - 2;
-			length -= opsize;
-		}
-	}
-}
-
-static void tcp_sack(const struct sk_buff *skb,
-		     struct iphdr *iph,
-		     struct tcphdr *tcph,
-		     __u32 *sack)
-{
-	unsigned char buff[(15 * 4) - sizeof(struct tcphdr)];
-	unsigned char *ptr;
-	int length = (tcph->doff*4) - sizeof(struct tcphdr);
-	__u32 tmp;
-
-	if (!length)
-		return;
-
-	ptr = skb_header_pointer(skb,
-				 (iph->ihl * 4) + sizeof(struct tcphdr),
-				 length, buff);
-	BUG_ON(ptr == NULL);
-
-	/* Fast path for timestamp-only option */
-	if (length == TCPOLEN_TSTAMP_ALIGNED*4
-	    && *(__be32 *)ptr ==
-		__constant_htonl((TCPOPT_NOP << 24)
-				 | (TCPOPT_NOP << 16)
-				 | (TCPOPT_TIMESTAMP << 8)
-				 | TCPOLEN_TIMESTAMP))
-		return;
-
-	while (length > 0) {
-		int opcode=*ptr++;
-		int opsize, i;
-
-		switch (opcode) {
-		case TCPOPT_EOL:
-			return;
-		case TCPOPT_NOP:	/* Ref: RFC 793 section 3.1 */
-			length--;
-			continue;
-		default:
-			opsize=*ptr++;
-			if (opsize < 2) /* "silly options" */
-				return;
-			if (opsize > length)
-				break;	/* don't parse partial options */
-
-			if (opcode == TCPOPT_SACK
-			    && opsize >= (TCPOLEN_SACK_BASE
-					  + TCPOLEN_SACK_PERBLOCK)
-			    && !((opsize - TCPOLEN_SACK_BASE)
-				 % TCPOLEN_SACK_PERBLOCK)) {
-				for (i = 0;
-				     i < (opsize - TCPOLEN_SACK_BASE);
-				     i += TCPOLEN_SACK_PERBLOCK) {
-					tmp = ntohl(*((__be32 *)(ptr+i)+1));
-
-					if (after(tmp, *sack))
-						*sack = tmp;
-				}
-				return;
-			}
-			ptr += opsize - 2;
-			length -= opsize;
-		}
-	}
-}
-
-static int tcp_in_window(struct ip_ct_tcp *state,
-			 enum ip_conntrack_dir dir,
-			 unsigned int index,
-			 const struct sk_buff *skb,
-			 struct iphdr *iph,
-			 struct tcphdr *tcph)
-{
-	struct ip_ct_tcp_state *sender = &state->seen[dir];
-	struct ip_ct_tcp_state *receiver = &state->seen[!dir];
-	__u32 seq, ack, sack, end, win, swin;
-	int res;
-
-	/*
-	 * Get the required data from the packet.
-	 */
-	seq = ntohl(tcph->seq);
-	ack = sack = ntohl(tcph->ack_seq);
-	win = ntohs(tcph->window);
-	end = segment_seq_plus_len(seq, skb->len, iph, tcph);
-
-	if (receiver->flags & IP_CT_TCP_FLAG_SACK_PERM)
-		tcp_sack(skb, iph, tcph, &sack);
-
-	DEBUGP("tcp_in_window: START\n");
-	DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu "
-	       "seq=%u ack=%u sack=%u win=%u end=%u\n",
-		NIPQUAD(iph->saddr), ntohs(tcph->source),
-		NIPQUAD(iph->daddr), ntohs(tcph->dest),
-		seq, ack, sack, win, end);
-	DEBUGP("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i "
-	       "receiver end=%u maxend=%u maxwin=%u scale=%i\n",
-		sender->td_end, sender->td_maxend, sender->td_maxwin,
-		sender->td_scale,
-		receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
-		receiver->td_scale);
-
-	if (sender->td_end == 0) {
-		/*
-		 * Initialize sender data.
-		 */
-		if (tcph->syn && tcph->ack) {
-			/*
-			 * Outgoing SYN-ACK in reply to a SYN.
-			 */
-			sender->td_end =
-			sender->td_maxend = end;
-			sender->td_maxwin = (win == 0 ? 1 : win);
-
-			tcp_options(skb, iph, tcph, sender);
-			/*
-			 * RFC 1323:
-			 * Both sides must send the Window Scale option
-			 * to enable window scaling in either direction.
-			 */
-			if (!(sender->flags & IP_CT_TCP_FLAG_WINDOW_SCALE
-			      && receiver->flags & IP_CT_TCP_FLAG_WINDOW_SCALE))
-				sender->td_scale =
-				receiver->td_scale = 0;
-		} else {
-			/*
-			 * We are in the middle of a connection,
-			 * its history is lost for us.
-			 * Let's try to use the data from the packet.
-			 */
-			sender->td_end = end;
-			sender->td_maxwin = (win == 0 ? 1 : win);
-			sender->td_maxend = end + sender->td_maxwin;
-		}
-	} else if (((state->state == TCP_CONNTRACK_SYN_SENT
-		     && dir == IP_CT_DIR_ORIGINAL)
-		    || (state->state == TCP_CONNTRACK_SYN_RECV
-			&& dir == IP_CT_DIR_REPLY))
-		    && after(end, sender->td_end)) {
-		/*
-		 * RFC 793: "if a TCP is reinitialized ... then it need
-		 * not wait at all; it must only be sure to use sequence
-		 * numbers larger than those recently used."
-		 */
-		sender->td_end =
-		sender->td_maxend = end;
-		sender->td_maxwin = (win == 0 ? 1 : win);
-
-		tcp_options(skb, iph, tcph, sender);
-	}
-
-	if (!(tcph->ack)) {
-		/*
-		 * If there is no ACK, just pretend it was set and OK.
-		 */
-		ack = sack = receiver->td_end;
-	} else if (((tcp_flag_word(tcph) & (TCP_FLAG_ACK|TCP_FLAG_RST)) ==
-		    (TCP_FLAG_ACK|TCP_FLAG_RST))
-		   && (ack == 0)) {
-		/*
-		 * Broken TCP stacks, that set ACK in RST packets as well
-		 * with zero ack value.
-		 */
-		ack = sack = receiver->td_end;
-	}
-
-	if (seq == end
-	    && (!tcph->rst
-		|| (seq == 0 && state->state == TCP_CONNTRACK_SYN_SENT)))
-		/*
-		 * Packets contains no data: we assume it is valid
-		 * and check the ack value only.
-		 * However RST segments are always validated by their
-		 * SEQ number, except when seq == 0 (reset sent answering
-		 * SYN.
-		 */
-		seq = end = sender->td_end;
-
-	DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu "
-	       "seq=%u ack=%u sack =%u win=%u end=%u\n",
-		NIPQUAD(iph->saddr), ntohs(tcph->source),
-		NIPQUAD(iph->daddr), ntohs(tcph->dest),
-		seq, ack, sack, win, end);
-	DEBUGP("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i "
-	       "receiver end=%u maxend=%u maxwin=%u scale=%i\n",
-		sender->td_end, sender->td_maxend, sender->td_maxwin,
-		sender->td_scale,
-		receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
-		receiver->td_scale);
-
-	DEBUGP("tcp_in_window: I=%i II=%i III=%i IV=%i\n",
-		before(seq, sender->td_maxend + 1),
-		after(end, sender->td_end - receiver->td_maxwin - 1),
-		before(sack, receiver->td_end + 1),
-		after(ack, receiver->td_end - MAXACKWINDOW(sender)));
-
-	if (before(seq, sender->td_maxend + 1) &&
-	    after(end, sender->td_end - receiver->td_maxwin - 1) &&
-	    before(sack, receiver->td_end + 1) &&
-	    after(ack, receiver->td_end - MAXACKWINDOW(sender))) {
-		/*
-		 * Take into account window scaling (RFC 1323).
-		 */
-		if (!tcph->syn)
-			win <<= sender->td_scale;
-
-		/*
-		 * Update sender data.
-		 */
-		swin = win + (sack - ack);
-		if (sender->td_maxwin < swin)
-			sender->td_maxwin = swin;
-		if (after(end, sender->td_end))
-			sender->td_end = end;
-		/*
-		 * Update receiver data.
-		 */
-		if (after(end, sender->td_maxend))
-			receiver->td_maxwin += end - sender->td_maxend;
-		if (after(sack + win, receiver->td_maxend - 1)) {
-			receiver->td_maxend = sack + win;
-			if (win == 0)
-				receiver->td_maxend++;
-		}
-
-		/*
-		 * Check retransmissions.
-		 */
-		if (index == TCP_ACK_SET) {
-			if (state->last_dir == dir
-			    && state->last_seq == seq
-			    && state->last_ack == ack
-			    && state->last_end == end
-			    && state->last_win == win)
-				state->retrans++;
-			else {
-				state->last_dir = dir;
-				state->last_seq = seq;
-				state->last_ack = ack;
-				state->last_end = end;
-				state->last_win = win;
-				state->retrans = 0;
-			}
-		}
-		res = 1;
-	} else {
-		res = 0;
-		if (sender->flags & IP_CT_TCP_FLAG_BE_LIBERAL ||
-		    ip_ct_tcp_be_liberal)
-			res = 1;
-		if (!res && LOG_INVALID(IPPROTO_TCP))
-			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
-			"ip_ct_tcp: %s ",
-			before(seq, sender->td_maxend + 1) ?
-			after(end, sender->td_end - receiver->td_maxwin - 1) ?
-			before(sack, receiver->td_end + 1) ?
-			after(ack, receiver->td_end - MAXACKWINDOW(sender)) ? "BUG"
-			: "ACK is under the lower bound (possible overly delayed ACK)"
-			: "ACK is over the upper bound (ACKed data not seen yet)"
-			: "SEQ is under the lower bound (already ACKed data retransmitted)"
-			: "SEQ is over the upper bound (over the window of the receiver)");
-	}
-
-	DEBUGP("tcp_in_window: res=%i sender end=%u maxend=%u maxwin=%u "
-	       "receiver end=%u maxend=%u maxwin=%u\n",
-		res, sender->td_end, sender->td_maxend, sender->td_maxwin,
-		receiver->td_end, receiver->td_maxend, receiver->td_maxwin);
-
-	return res;
-}
-
-#ifdef CONFIG_IP_NF_NAT_NEEDED
-/* Update sender->td_end after NAT successfully mangled the packet */
-void ip_conntrack_tcp_update(struct sk_buff *skb,
-			     struct ip_conntrack *conntrack,
-			     enum ip_conntrack_dir dir)
-{
-	struct iphdr *iph = skb->nh.iph;
-	struct tcphdr *tcph = (void *)skb->nh.iph + skb->nh.iph->ihl*4;
-	__u32 end;
-#ifdef DEBUGP_VARS
-	struct ip_ct_tcp_state *sender = &conntrack->proto.tcp.seen[dir];
-	struct ip_ct_tcp_state *receiver = &conntrack->proto.tcp.seen[!dir];
-#endif
-
-	end = segment_seq_plus_len(ntohl(tcph->seq), skb->len, iph, tcph);
-
-	write_lock_bh(&tcp_lock);
-	/*
-	 * We have to worry for the ack in the reply packet only...
-	 */
-	if (after(end, conntrack->proto.tcp.seen[dir].td_end))
-		conntrack->proto.tcp.seen[dir].td_end = end;
-	conntrack->proto.tcp.last_end = end;
-	write_unlock_bh(&tcp_lock);
-	DEBUGP("tcp_update: sender end=%u maxend=%u maxwin=%u scale=%i "
-	       "receiver end=%u maxend=%u maxwin=%u scale=%i\n",
-		sender->td_end, sender->td_maxend, sender->td_maxwin,
-		sender->td_scale,
-		receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
-		receiver->td_scale);
-}
-
-#endif
-
-#define	TH_FIN	0x01
-#define	TH_SYN	0x02
-#define	TH_RST	0x04
-#define	TH_PUSH	0x08
-#define	TH_ACK	0x10
-#define	TH_URG	0x20
-#define	TH_ECE	0x40
-#define	TH_CWR	0x80
-
-/* table of valid flag combinations - ECE and CWR are always valid */
-static const u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG) + 1] =
-{
-	[TH_SYN]			= 1,
-	[TH_SYN|TH_PUSH]		= 1,
-	[TH_SYN|TH_URG]			= 1,
-	[TH_SYN|TH_PUSH|TH_URG]		= 1,
-	[TH_SYN|TH_ACK]			= 1,
-	[TH_SYN|TH_ACK|TH_PUSH]		= 1,
-	[TH_RST]			= 1,
-	[TH_RST|TH_ACK]			= 1,
-	[TH_RST|TH_ACK|TH_PUSH]		= 1,
-	[TH_FIN|TH_ACK]			= 1,
-	[TH_ACK]			= 1,
-	[TH_ACK|TH_PUSH]		= 1,
-	[TH_ACK|TH_URG]			= 1,
-	[TH_ACK|TH_URG|TH_PUSH]		= 1,
-	[TH_FIN|TH_ACK|TH_PUSH]		= 1,
-	[TH_FIN|TH_ACK|TH_URG]		= 1,
-	[TH_FIN|TH_ACK|TH_URG|TH_PUSH]	= 1,
-};
-
-/* Protect conntrack agaist broken packets. Code taken from ipt_unclean.c.  */
-static int tcp_error(struct sk_buff *skb,
-		     enum ip_conntrack_info *ctinfo,
-		     unsigned int hooknum)
-{
-	struct iphdr *iph = skb->nh.iph;
-	struct tcphdr _tcph, *th;
-	unsigned int tcplen = skb->len - iph->ihl * 4;
-	u_int8_t tcpflags;
-
-	/* Smaller that minimal TCP header? */
-	th = skb_header_pointer(skb, iph->ihl * 4,
-				sizeof(_tcph), &_tcph);
-	if (th == NULL) {
-		if (LOG_INVALID(IPPROTO_TCP))
-			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
-				"ip_ct_tcp: short packet ");
-		return -NF_ACCEPT;
-	}
-
-	/* Not whole TCP header or malformed packet */
-	if (th->doff*4 < sizeof(struct tcphdr) || tcplen < th->doff*4) {
-		if (LOG_INVALID(IPPROTO_TCP))
-			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
-				"ip_ct_tcp: truncated/malformed packet ");
-		return -NF_ACCEPT;
-	}
-
-	/* Checksum invalid? Ignore.
-	 * We skip checking packets on the outgoing path
-	 * because it is assumed to be correct.
-	 */
-	/* FIXME: Source route IP option packets --RR */
-	if (ip_conntrack_checksum && hooknum == NF_IP_PRE_ROUTING &&
-	    nf_ip_checksum(skb, hooknum, iph->ihl * 4, IPPROTO_TCP)) {
-		if (LOG_INVALID(IPPROTO_TCP))
-			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
-				  "ip_ct_tcp: bad TCP checksum ");
-		return -NF_ACCEPT;
-	}
-
-	/* Check TCP flags. */
-	tcpflags = (((u_int8_t *)th)[13] & ~(TH_ECE|TH_CWR));
-	if (!tcp_valid_flags[tcpflags]) {
-		if (LOG_INVALID(IPPROTO_TCP))
-			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
-				  "ip_ct_tcp: invalid TCP flag combination ");
-		return -NF_ACCEPT;
-	}
-
-	return NF_ACCEPT;
-}
-
-/* Returns verdict for packet, or -1 for invalid. */
-static int tcp_packet(struct ip_conntrack *conntrack,
-		      const struct sk_buff *skb,
-		      enum ip_conntrack_info ctinfo)
-{
-	enum tcp_conntrack new_state, old_state;
-	enum ip_conntrack_dir dir;
-	struct iphdr *iph = skb->nh.iph;
-	struct tcphdr *th, _tcph;
-	unsigned long timeout;
-	unsigned int index;
-
-	th = skb_header_pointer(skb, iph->ihl * 4,
-				sizeof(_tcph), &_tcph);
-	BUG_ON(th == NULL);
-
-	write_lock_bh(&tcp_lock);
-	old_state = conntrack->proto.tcp.state;
-	dir = CTINFO2DIR(ctinfo);
-	index = get_conntrack_index(th);
-	new_state = tcp_conntracks[dir][index][old_state];
-
-	switch (new_state) {
-	case TCP_CONNTRACK_IGNORE:
-		/* Ignored packets:
-		 *
-		 * a) SYN in ORIGINAL
-		 * b) SYN/ACK in REPLY
-		 * c) ACK in reply direction after initial SYN in original.
-		 */
-		if (index == TCP_SYNACK_SET
-		    && conntrack->proto.tcp.last_index == TCP_SYN_SET
-		    && conntrack->proto.tcp.last_dir != dir
-		    && ntohl(th->ack_seq) ==
-			     conntrack->proto.tcp.last_end) {
-			/* This SYN/ACK acknowledges a SYN that we earlier
-			 * ignored as invalid. This means that the client and
-			 * the server are both in sync, while the firewall is
-			 * not. We kill this session and block the SYN/ACK so
-			 * that the client cannot but retransmit its SYN and
-			 * thus initiate a clean new session.
-			 */
-			write_unlock_bh(&tcp_lock);
-			if (LOG_INVALID(IPPROTO_TCP))
-				nf_log_packet(PF_INET, 0, skb, NULL, NULL,
-					      NULL, "ip_ct_tcp: "
-					      "killing out of sync session ");
-			if (del_timer(&conntrack->timeout))
-				conntrack->timeout.function((unsigned long)
-							    conntrack);
-			return -NF_DROP;
-		}
-		conntrack->proto.tcp.last_index = index;
-		conntrack->proto.tcp.last_dir = dir;
-		conntrack->proto.tcp.last_seq = ntohl(th->seq);
-		conntrack->proto.tcp.last_end =
-		    segment_seq_plus_len(ntohl(th->seq), skb->len, iph, th);
-
-		write_unlock_bh(&tcp_lock);
-		if (LOG_INVALID(IPPROTO_TCP))
-			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
-				  "ip_ct_tcp: invalid packet ignored ");
-		return NF_ACCEPT;
-	case TCP_CONNTRACK_MAX:
-		/* Invalid packet */
-		DEBUGP("ip_ct_tcp: Invalid dir=%i index=%u ostate=%u\n",
-		       dir, get_conntrack_index(th),
-		       old_state);
-		write_unlock_bh(&tcp_lock);
-		if (LOG_INVALID(IPPROTO_TCP))
-			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
-				  "ip_ct_tcp: invalid state ");
-		return -NF_ACCEPT;
-	case TCP_CONNTRACK_SYN_SENT:
-		if (old_state < TCP_CONNTRACK_TIME_WAIT)
-			break;
-		if ((conntrack->proto.tcp.seen[dir].flags &
-			 IP_CT_TCP_FLAG_CLOSE_INIT)
-		    || after(ntohl(th->seq),
-			     conntrack->proto.tcp.seen[dir].td_end)) {
-			/* Attempt to reopen a closed connection.
-			* Delete this connection and look up again. */
-			write_unlock_bh(&tcp_lock);
-			if (del_timer(&conntrack->timeout))
-				conntrack->timeout.function((unsigned long)
-							    conntrack);
-			return -NF_REPEAT;
-		} else {
-			write_unlock_bh(&tcp_lock);
-			if (LOG_INVALID(IPPROTO_TCP))
-				nf_log_packet(PF_INET, 0, skb, NULL, NULL,
-					      NULL, "ip_ct_tcp: invalid SYN");
-			return -NF_ACCEPT;
-		}
-	case TCP_CONNTRACK_CLOSE:
-		if (index == TCP_RST_SET
-		    && ((test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)
-			 && conntrack->proto.tcp.last_index == TCP_SYN_SET)
-			|| (!test_bit(IPS_ASSURED_BIT, &conntrack->status)
-			    && conntrack->proto.tcp.last_index == TCP_ACK_SET))
-		    && ntohl(th->ack_seq) == conntrack->proto.tcp.last_end) {
-			/* RST sent to invalid SYN or ACK we had let through
-			 * at a) and c) above:
-			 *
-			 * a) SYN was in window then
-			 * c) we hold a half-open connection.
-			 *
-			 * Delete our connection entry.
-			 * We skip window checking, because packet might ACK
-			 * segments we ignored. */
-			goto in_window;
-		}
-		/* Just fall through */
-	default:
-		/* Keep compilers happy. */
-		break;
-	}
-
-	if (!tcp_in_window(&conntrack->proto.tcp, dir, index,
-			   skb, iph, th)) {
-		write_unlock_bh(&tcp_lock);
-		return -NF_ACCEPT;
-	}
-    in_window:
-	/* From now on we have got in-window packets */
-	conntrack->proto.tcp.last_index = index;
-
-	DEBUGP("tcp_conntracks: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu "
-	       "syn=%i ack=%i fin=%i rst=%i old=%i new=%i\n",
-		NIPQUAD(iph->saddr), ntohs(th->source),
-		NIPQUAD(iph->daddr), ntohs(th->dest),
-		(th->syn ? 1 : 0), (th->ack ? 1 : 0),
-		(th->fin ? 1 : 0), (th->rst ? 1 : 0),
-		old_state, new_state);
-
-	conntrack->proto.tcp.state = new_state;
-	if (old_state != new_state
-	    && (new_state == TCP_CONNTRACK_FIN_WAIT
-		|| new_state == TCP_CONNTRACK_CLOSE))
-		conntrack->proto.tcp.seen[dir].flags |= IP_CT_TCP_FLAG_CLOSE_INIT;
-	timeout = conntrack->proto.tcp.retrans >= ip_ct_tcp_max_retrans
-		  && *tcp_timeouts[new_state] > ip_ct_tcp_timeout_max_retrans
-		  ? ip_ct_tcp_timeout_max_retrans : *tcp_timeouts[new_state];
-	write_unlock_bh(&tcp_lock);
-
-	ip_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb);
-	if (new_state != old_state)
-		ip_conntrack_event_cache(IPCT_PROTOINFO, skb);
-
-	if (!test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) {
-		/* If only reply is a RST, we can consider ourselves not to
-		   have an established connection: this is a fairly common
-		   problem case, so we can delete the conntrack
-		   immediately.  --RR */
-		if (th->rst) {
-			if (del_timer(&conntrack->timeout))
-				conntrack->timeout.function((unsigned long)
-							    conntrack);
-			return NF_ACCEPT;
-		}
-	} else if (!test_bit(IPS_ASSURED_BIT, &conntrack->status)
-		   && (old_state == TCP_CONNTRACK_SYN_RECV
-		       || old_state == TCP_CONNTRACK_ESTABLISHED)
-		   && new_state == TCP_CONNTRACK_ESTABLISHED) {
-		/* Set ASSURED if we see see valid ack in ESTABLISHED
-		   after SYN_RECV or a valid answer for a picked up
-		   connection. */
-		set_bit(IPS_ASSURED_BIT, &conntrack->status);
-		ip_conntrack_event_cache(IPCT_STATUS, skb);
-	}
-	ip_ct_refresh_acct(conntrack, ctinfo, skb, timeout);
-
-	return NF_ACCEPT;
-}
-
-/* Called when a new connection for this protocol found. */
-static int tcp_new(struct ip_conntrack *conntrack,
-		   const struct sk_buff *skb)
-{
-	enum tcp_conntrack new_state;
-	struct iphdr *iph = skb->nh.iph;
-	struct tcphdr *th, _tcph;
-#ifdef DEBUGP_VARS
-	struct ip_ct_tcp_state *sender = &conntrack->proto.tcp.seen[0];
-	struct ip_ct_tcp_state *receiver = &conntrack->proto.tcp.seen[1];
-#endif
-
-	th = skb_header_pointer(skb, iph->ihl * 4,
-				sizeof(_tcph), &_tcph);
-	BUG_ON(th == NULL);
-
-	/* Don't need lock here: this conntrack not in circulation yet */
-	new_state
-		= tcp_conntracks[0][get_conntrack_index(th)]
-		[TCP_CONNTRACK_NONE];
-
-	/* Invalid: delete conntrack */
-	if (new_state >= TCP_CONNTRACK_MAX) {
-		DEBUGP("ip_ct_tcp: invalid new deleting.\n");
-		return 0;
-	}
-
-	if (new_state == TCP_CONNTRACK_SYN_SENT) {
-		/* SYN packet */
-		conntrack->proto.tcp.seen[0].td_end =
-			segment_seq_plus_len(ntohl(th->seq), skb->len,
-					     iph, th);
-		conntrack->proto.tcp.seen[0].td_maxwin = ntohs(th->window);
-		if (conntrack->proto.tcp.seen[0].td_maxwin == 0)
-			conntrack->proto.tcp.seen[0].td_maxwin = 1;
-		conntrack->proto.tcp.seen[0].td_maxend =
-			conntrack->proto.tcp.seen[0].td_end;
-
-		tcp_options(skb, iph, th, &conntrack->proto.tcp.seen[0]);
-		conntrack->proto.tcp.seen[1].flags = 0;
-	} else if (ip_ct_tcp_loose == 0) {
-		/* Don't try to pick up connections. */
-		return 0;
-	} else {
-		/*
-		 * We are in the middle of a connection,
-		 * its history is lost for us.
-		 * Let's try to use the data from the packet.
-		 */
-		conntrack->proto.tcp.seen[0].td_end =
-			segment_seq_plus_len(ntohl(th->seq), skb->len,
-					     iph, th);
-		conntrack->proto.tcp.seen[0].td_maxwin = ntohs(th->window);
-		if (conntrack->proto.tcp.seen[0].td_maxwin == 0)
-			conntrack->proto.tcp.seen[0].td_maxwin = 1;
-		conntrack->proto.tcp.seen[0].td_maxend =
-			conntrack->proto.tcp.seen[0].td_end +
-			conntrack->proto.tcp.seen[0].td_maxwin;
-		conntrack->proto.tcp.seen[0].td_scale = 0;
-
-		/* We assume SACK and liberal window checking to handle
-		 * window scaling */
-		conntrack->proto.tcp.seen[0].flags =
-		conntrack->proto.tcp.seen[1].flags = IP_CT_TCP_FLAG_SACK_PERM |
-						     IP_CT_TCP_FLAG_BE_LIBERAL;
-	}
-
-	conntrack->proto.tcp.seen[1].td_end = 0;
-	conntrack->proto.tcp.seen[1].td_maxend = 0;
-	conntrack->proto.tcp.seen[1].td_maxwin = 1;
-	conntrack->proto.tcp.seen[1].td_scale = 0;
-
-	/* tcp_packet will set them */
-	conntrack->proto.tcp.state = TCP_CONNTRACK_NONE;
-	conntrack->proto.tcp.last_index = TCP_NONE_SET;
-
-	DEBUGP("tcp_new: sender end=%u maxend=%u maxwin=%u scale=%i "
-	       "receiver end=%u maxend=%u maxwin=%u scale=%i\n",
-		sender->td_end, sender->td_maxend, sender->td_maxwin,
-		sender->td_scale,
-		receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
-		receiver->td_scale);
-	return 1;
-}
-
-struct ip_conntrack_protocol ip_conntrack_protocol_tcp =
-{
-	.proto 			= IPPROTO_TCP,
-	.name 			= "tcp",
-	.pkt_to_tuple 		= tcp_pkt_to_tuple,
-	.invert_tuple 		= tcp_invert_tuple,
-	.print_tuple 		= tcp_print_tuple,
-	.print_conntrack 	= tcp_print_conntrack,
-	.packet 		= tcp_packet,
-	.new 			= tcp_new,
-	.error			= tcp_error,
-#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
-    defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
-	.to_nfattr		= tcp_to_nfattr,
-	.from_nfattr		= nfattr_to_tcp,
-	.tuple_to_nfattr	= ip_ct_port_tuple_to_nfattr,
-	.nfattr_to_tuple	= ip_ct_port_nfattr_to_tuple,
-#endif
-};
diff --git a/net/ipv4/netfilter/ip_conntrack_proto_udp.c b/net/ipv4/netfilter/ip_conntrack_proto_udp.c
deleted file mode 100644
index 14c30c6..0000000
--- a/net/ipv4/netfilter/ip_conntrack_proto_udp.c
+++ /dev/null
@@ -1,148 +0,0 @@
-/* (C) 1999-2001 Paul `Rusty' Russell
- * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.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.
- */
-
-#include <linux/types.h>
-#include <linux/timer.h>
-#include <linux/netfilter.h>
-#include <linux/in.h>
-#include <linux/ip.h>
-#include <linux/udp.h>
-#include <linux/seq_file.h>
-#include <net/checksum.h>
-#include <linux/netfilter_ipv4.h>
-#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
-
-unsigned int ip_ct_udp_timeout __read_mostly = 30*HZ;
-unsigned int ip_ct_udp_timeout_stream __read_mostly = 180*HZ;
-
-static int udp_pkt_to_tuple(const struct sk_buff *skb,
-			     unsigned int dataoff,
-			     struct ip_conntrack_tuple *tuple)
-{
-	struct udphdr _hdr, *hp;
-
-	/* Actually only need first 8 bytes. */
-	hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
-	if (hp == NULL)
-		return 0;
-
-	tuple->src.u.udp.port = hp->source;
-	tuple->dst.u.udp.port = hp->dest;
-
-	return 1;
-}
-
-static int udp_invert_tuple(struct ip_conntrack_tuple *tuple,
-			    const struct ip_conntrack_tuple *orig)
-{
-	tuple->src.u.udp.port = orig->dst.u.udp.port;
-	tuple->dst.u.udp.port = orig->src.u.udp.port;
-	return 1;
-}
-
-/* Print out the per-protocol part of the tuple. */
-static int udp_print_tuple(struct seq_file *s,
-			   const struct ip_conntrack_tuple *tuple)
-{
-	return seq_printf(s, "sport=%hu dport=%hu ",
-			  ntohs(tuple->src.u.udp.port),
-			  ntohs(tuple->dst.u.udp.port));
-}
-
-/* Print out the private part of the conntrack. */
-static int udp_print_conntrack(struct seq_file *s,
-			       const struct ip_conntrack *conntrack)
-{
-	return 0;
-}
-
-/* Returns verdict for packet, and may modify conntracktype */
-static int udp_packet(struct ip_conntrack *conntrack,
-		      const struct sk_buff *skb,
-		      enum ip_conntrack_info ctinfo)
-{
-	/* If we've seen traffic both ways, this is some kind of UDP
-	   stream.  Extend timeout. */
-	if (test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) {
-		ip_ct_refresh_acct(conntrack, ctinfo, skb,
-				   ip_ct_udp_timeout_stream);
-		/* Also, more likely to be important, and not a probe */
-		if (!test_and_set_bit(IPS_ASSURED_BIT, &conntrack->status))
-			ip_conntrack_event_cache(IPCT_STATUS, skb);
-	} else
-		ip_ct_refresh_acct(conntrack, ctinfo, skb, ip_ct_udp_timeout);
-
-	return NF_ACCEPT;
-}
-
-/* Called when a new connection for this protocol found. */
-static int udp_new(struct ip_conntrack *conntrack, const struct sk_buff *skb)
-{
-	return 1;
-}
-
-static int udp_error(struct sk_buff *skb, enum ip_conntrack_info *ctinfo,
-		     unsigned int hooknum)
-{
-	struct iphdr *iph = skb->nh.iph;
-	unsigned int udplen = skb->len - iph->ihl * 4;
-	struct udphdr _hdr, *hdr;
-
-	/* Header is too small? */
-	hdr = skb_header_pointer(skb, iph->ihl*4, sizeof(_hdr), &_hdr);
-	if (hdr == NULL) {
-		if (LOG_INVALID(IPPROTO_UDP))
-			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
-				  "ip_ct_udp: short packet ");
-		return -NF_ACCEPT;
-	}
-
-	/* Truncated/malformed packets */
-	if (ntohs(hdr->len) > udplen || ntohs(hdr->len) < sizeof(*hdr)) {
-		if (LOG_INVALID(IPPROTO_UDP))
-			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
-				  "ip_ct_udp: truncated/malformed packet ");
-		return -NF_ACCEPT;
-	}
-
-	/* Packet with no checksum */
-	if (!hdr->check)
-		return NF_ACCEPT;
-
-	/* Checksum invalid? Ignore.
-	 * We skip checking packets on the outgoing path
-	 * because the checksum is assumed to be correct.
-	 * FIXME: Source route IP option packets --RR */
-	if (ip_conntrack_checksum && hooknum == NF_IP_PRE_ROUTING &&
-	    nf_ip_checksum(skb, hooknum, iph->ihl * 4, IPPROTO_UDP)) {
-		if (LOG_INVALID(IPPROTO_UDP))
-			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
-				  "ip_ct_udp: bad UDP checksum ");
-		return -NF_ACCEPT;
-	}
-
-	return NF_ACCEPT;
-}
-
-struct ip_conntrack_protocol ip_conntrack_protocol_udp =
-{
-	.proto 			= IPPROTO_UDP,
-	.name			= "udp",
-	.pkt_to_tuple		= udp_pkt_to_tuple,
-	.invert_tuple		= udp_invert_tuple,
-	.print_tuple		= udp_print_tuple,
-	.print_conntrack	= udp_print_conntrack,
-	.packet			= udp_packet,
-	.new			= udp_new,
-	.error			= udp_error,
-#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
-    defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
-	.tuple_to_nfattr	= ip_ct_port_tuple_to_nfattr,
-	.nfattr_to_tuple	= ip_ct_port_nfattr_to_tuple,
-#endif
-};
diff --git a/net/ipv4/netfilter/ip_conntrack_sip.c b/net/ipv4/netfilter/ip_conntrack_sip.c
deleted file mode 100644
index c59a962..0000000
--- a/net/ipv4/netfilter/ip_conntrack_sip.c
+++ /dev/null
@@ -1,520 +0,0 @@
-/* SIP extension for IP connection tracking.
- *
- * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar>
- * based on RR's ip_conntrack_ftp.c and other modules.
- *
- * 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.
- */
-
-#include <linux/module.h>
-#include <linux/ctype.h>
-#include <linux/skbuff.h>
-#include <linux/in.h>
-#include <linux/ip.h>
-#include <linux/udp.h>
-
-#include <linux/netfilter.h>
-#include <linux/netfilter_ipv4.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-#include <linux/netfilter_ipv4/ip_conntrack_sip.h>
-
-#if 0
-#define DEBUGP printk
-#else
-#define DEBUGP(format, args...)
-#endif
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");
-MODULE_DESCRIPTION("SIP connection tracking helper");
-
-#define MAX_PORTS	8
-static unsigned short ports[MAX_PORTS];
-static int ports_c;
-module_param_array(ports, ushort, &ports_c, 0400);
-MODULE_PARM_DESC(ports, "port numbers of sip servers");
-
-static unsigned int sip_timeout = SIP_TIMEOUT;
-module_param(sip_timeout, uint, 0600);
-MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session");
-
-unsigned int (*ip_nat_sip_hook)(struct sk_buff **pskb,
-				enum ip_conntrack_info ctinfo,
-				struct ip_conntrack *ct,
-				const char **dptr);
-EXPORT_SYMBOL_GPL(ip_nat_sip_hook);
-
-unsigned int (*ip_nat_sdp_hook)(struct sk_buff **pskb,
-				enum ip_conntrack_info ctinfo,
-				struct ip_conntrack_expect *exp,
-				const char *dptr);
-EXPORT_SYMBOL_GPL(ip_nat_sdp_hook);
-
-static int digits_len(const char *dptr, const char *limit, int *shift);
-static int epaddr_len(const char *dptr, const char *limit, int *shift);
-static int skp_digits_len(const char *dptr, const char *limit, int *shift);
-static int skp_epaddr_len(const char *dptr, const char *limit, int *shift);
-
-struct sip_header_nfo {
-	const char	*lname;
-	const char	*sname;
-	const char	*ln_str;
-	size_t		lnlen;
-	size_t		snlen;
-	size_t		ln_strlen;
-	int		case_sensitive;
-	int		(*match_len)(const char *, const char *, int *);
-};
-
-static struct sip_header_nfo ct_sip_hdrs[] = {
-	[POS_REG_REQ_URI] = { 	/* SIP REGISTER request URI */
-		.lname		= "sip:",
-		.lnlen		= sizeof("sip:") - 1,
-		.ln_str		= ":",
-		.ln_strlen	= sizeof(":") - 1,
-		.match_len	= epaddr_len
-	},
-	[POS_REQ_URI] = { 	/* SIP request URI */
-		.lname		= "sip:",
-		.lnlen		= sizeof("sip:") - 1,
-		.ln_str		= "@",
-		.ln_strlen	= sizeof("@") - 1,
-		.match_len	= epaddr_len
-	},
-	[POS_FROM] = {		/* SIP From header */
-		.lname		= "From:",
-		.lnlen		= sizeof("From:") - 1,
-		.sname		= "\r\nf:",
-		.snlen		= sizeof("\r\nf:") - 1,
-		.ln_str		= "sip:",
-		.ln_strlen	= sizeof("sip:") - 1,
-		.match_len	= skp_epaddr_len,
-	},
-	[POS_TO] = {		/* SIP To header */
-		.lname		= "To:",
-		.lnlen		= sizeof("To:") - 1,
-		.sname		= "\r\nt:",
-		.snlen		= sizeof("\r\nt:") - 1,
-		.ln_str		= "sip:",
-		.ln_strlen	= sizeof("sip:") - 1,
-		.match_len	= skp_epaddr_len,
-	},
-	[POS_VIA] = { 		/* SIP Via header */
-		.lname		= "Via:",
-		.lnlen		= sizeof("Via:") - 1,
-		.sname		= "\r\nv:",
-		.snlen		= sizeof("\r\nv:") - 1, /* rfc3261 "\r\n" */
-		.ln_str		= "UDP ",
-		.ln_strlen	= sizeof("UDP ") - 1,
-		.match_len	= epaddr_len,
-	},
-	[POS_CONTACT] = { 	/* SIP Contact header */
-		.lname		= "Contact:",
-		.lnlen		= sizeof("Contact:") - 1,
-		.sname		= "\r\nm:",
-		.snlen		= sizeof("\r\nm:") - 1,
-		.ln_str		= "sip:",
-		.ln_strlen	= sizeof("sip:") - 1,
-		.match_len	= skp_epaddr_len
-	},
-	[POS_CONTENT] = { 	/* SIP Content length header */
-		.lname		= "Content-Length:",
-		.lnlen		= sizeof("Content-Length:") - 1,
-		.sname		= "\r\nl:",
-		.snlen		= sizeof("\r\nl:") - 1,
-		.ln_str		= ":",
-		.ln_strlen	= sizeof(":") - 1,
-		.match_len	= skp_digits_len
-	},
-	[POS_MEDIA] = {		/* SDP media info */
-		.case_sensitive	= 1,
-		.lname		= "\nm=",
-		.lnlen		= sizeof("\nm=") - 1,
-		.sname		= "\rm=",
-		.snlen		= sizeof("\rm=") - 1,
-		.ln_str		= "audio ",
-		.ln_strlen	= sizeof("audio ") - 1,
-		.match_len	= digits_len
-	},
-	[POS_OWNER] = { 	/* SDP owner address*/
-		.case_sensitive	= 1,
-		.lname		= "\no=",
-		.lnlen		= sizeof("\no=") - 1,
-		.sname		= "\ro=",
-		.snlen		= sizeof("\ro=") - 1,
-		.ln_str		= "IN IP4 ",
-		.ln_strlen	= sizeof("IN IP4 ") - 1,
-		.match_len	= epaddr_len
-	},
-	[POS_CONNECTION] = { 	/* SDP connection info */
-		.case_sensitive	= 1,
-		.lname		= "\nc=",
-		.lnlen		= sizeof("\nc=") - 1,
-		.sname		= "\rc=",
-		.snlen		= sizeof("\rc=") - 1,
-		.ln_str		= "IN IP4 ",
-		.ln_strlen	= sizeof("IN IP4 ") - 1,
-		.match_len	= epaddr_len
-	},
-	[POS_SDP_HEADER] = { 	/* SDP version header */
-		.case_sensitive	= 1,
-		.lname		= "\nv=",
-		.lnlen		= sizeof("\nv=") - 1,
-		.sname		= "\rv=",
-		.snlen		= sizeof("\rv=") - 1,
-		.ln_str		= "=",
-		.ln_strlen	= sizeof("=") - 1,
-		.match_len	= digits_len
-	}
-};
-
-/* get line lenght until first CR or LF seen. */
-int ct_sip_lnlen(const char *line, const char *limit)
-{
-	const char *k = line;
-
-	while ((line <= limit) && (*line == '\r' || *line == '\n'))
-		line++;
-
-	while (line <= limit) {
-		if (*line == '\r' || *line == '\n')
-			break;
-		line++;
-	}
-	return line - k;
-}
-EXPORT_SYMBOL_GPL(ct_sip_lnlen);
-
-/* Linear string search, case sensitive. */
-const char *ct_sip_search(const char *needle, const char *haystack,
-			  size_t needle_len, size_t haystack_len,
-			  int case_sensitive)
-{
-	const char *limit = haystack + (haystack_len - needle_len);
-
-	while (haystack <= limit) {
-		if (case_sensitive) {
-			if (strncmp(haystack, needle, needle_len) == 0)
-				return haystack;
-		} else {
-			if (strnicmp(haystack, needle, needle_len) == 0)
-				return haystack;
-		}
-		haystack++;
-	}
-	return NULL;
-}
-EXPORT_SYMBOL_GPL(ct_sip_search);
-
-static int digits_len(const char *dptr, const char *limit, int *shift)
-{
-	int len = 0;
-	while (dptr <= limit && isdigit(*dptr)) {
-		dptr++;
-		len++;
-	}
-	return len;
-}
-
-/* get digits lenght, skiping blank spaces. */
-static int skp_digits_len(const char *dptr, const char *limit, int *shift)
-{
-	for (; dptr <= limit && *dptr == ' '; dptr++)
-		(*shift)++;
-
-	return digits_len(dptr, limit, shift);
-}
-
-/* Simple ipaddr parser.. */
-static int parse_ipaddr(const char *cp,	const char **endp,
-			__be32 *ipaddr, const char *limit)
-{
-	unsigned long int val;
-	int i, digit = 0;
-
-	for (i = 0, *ipaddr = 0; cp <= limit && i < 4; i++) {
-		digit = 0;
-		if (!isdigit(*cp))
-			break;
-
-		val = simple_strtoul(cp, (char **)&cp, 10);
-		if (val > 0xFF)
-			return -1;
-
-		((u_int8_t *)ipaddr)[i] = val;
-		digit = 1;
-
-		if (*cp != '.')
-			break;
-		cp++;
-	}
-	if (!digit)
-		return -1;
-
-	if (endp)
-		*endp = cp;
-
-	return 0;
-}
-
-/* skip ip address. returns it lenght. */
-static int epaddr_len(const char *dptr, const char *limit, int *shift)
-{
-	const char *aux = dptr;
-	__be32 ip;
-
-	if (parse_ipaddr(dptr, &dptr, &ip, limit) < 0) {
-		DEBUGP("ip: %s parse failed.!\n", dptr);
-		return 0;
-	}
-
-	/* Port number */
-	if (*dptr == ':') {
-		dptr++;
-		dptr += digits_len(dptr, limit, shift);
-	}
-	return dptr - aux;
-}
-
-/* get address length, skiping user info. */
-static int skp_epaddr_len(const char *dptr, const char *limit, int *shift)
-{
-	int s = *shift;
-
-	/* Search for @, but stop at the end of the line.
-	 * We are inside a sip: URI, so we don't need to worry about
-	 * continuation lines. */
-	while (dptr <= limit &&
-	       *dptr != '@' && *dptr != '\r' && *dptr != '\n') {
-		(*shift)++;
-		dptr++;
-	}
-
-	if (dptr <= limit && *dptr == '@') {
-		dptr++;
-		(*shift)++;
-	} else
-		*shift = s;
-
-	return epaddr_len(dptr, limit, shift);
-}
-
-/* Returns 0 if not found, -1 error parsing. */
-int ct_sip_get_info(const char *dptr, size_t dlen,
-		    unsigned int *matchoff,
-		    unsigned int *matchlen,
-		    enum sip_header_pos pos)
-{
-	struct sip_header_nfo *hnfo = &ct_sip_hdrs[pos];
-	const char *limit, *aux, *k = dptr;
-	int shift = 0;
-
-	limit = dptr + (dlen - hnfo->lnlen);
-
-	while (dptr <= limit) {
-		if ((strncmp(dptr, hnfo->lname, hnfo->lnlen) != 0) &&
-		    (hnfo->sname == NULL ||
-		     strncmp(dptr, hnfo->sname, hnfo->snlen) != 0)) {
-			dptr++;
-			continue;
-		}
-		aux = ct_sip_search(hnfo->ln_str, dptr, hnfo->ln_strlen,
-				    ct_sip_lnlen(dptr, limit),
-				    hnfo->case_sensitive);
-		if (!aux) {
-			DEBUGP("'%s' not found in '%s'.\n", hnfo->ln_str,
-			       hnfo->lname);
-			return -1;
-		}
-		aux += hnfo->ln_strlen;
-
-		*matchlen = hnfo->match_len(aux, limit, &shift);
-		if (!*matchlen)
-			return -1;
-
-		*matchoff = (aux - k) + shift;
-
-		DEBUGP("%s match succeeded! - len: %u\n", hnfo->lname,
-		       *matchlen);
-		return 1;
-	}
-	DEBUGP("%s header not found.\n", hnfo->lname);
-	return 0;
-}
-EXPORT_SYMBOL_GPL(ct_sip_get_info);
-
-static int set_expected_rtp(struct sk_buff **pskb,
-			    struct ip_conntrack *ct,
-			    enum ip_conntrack_info ctinfo,
-			    __be32 ipaddr, u_int16_t port,
-			    const char *dptr)
-{
-	struct ip_conntrack_expect *exp;
-	enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
-	int ret;
-	typeof(ip_nat_sdp_hook) ip_nat_sdp;
-
-	exp = ip_conntrack_expect_alloc(ct);
-	if (exp == NULL)
-		return NF_DROP;
-
-	exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip;
-	exp->tuple.src.u.udp.port = 0;
-	exp->tuple.dst.ip = ipaddr;
-	exp->tuple.dst.u.udp.port = htons(port);
-	exp->tuple.dst.protonum = IPPROTO_UDP;
-
-	exp->mask.src.ip = htonl(0xFFFFFFFF);
-	exp->mask.src.u.udp.port = 0;
-	exp->mask.dst.ip = htonl(0xFFFFFFFF);
-	exp->mask.dst.u.udp.port = htons(0xFFFF);
-	exp->mask.dst.protonum = 0xFF;
-
-	exp->expectfn = NULL;
-	exp->flags = 0;
-
-	ip_nat_sdp = rcu_dereference(ip_nat_sdp_hook);
-	if (ip_nat_sdp)
-		ret = ip_nat_sdp(pskb, ctinfo, exp, dptr);
-	else {
-		if (ip_conntrack_expect_related(exp) != 0)
-			ret = NF_DROP;
-		else
-			ret = NF_ACCEPT;
-	}
-	ip_conntrack_expect_put(exp);
-
-	return ret;
-}
-
-static int sip_help(struct sk_buff **pskb,
-		    struct ip_conntrack *ct,
-		    enum ip_conntrack_info ctinfo)
-{
-	unsigned int dataoff, datalen;
-	const char *dptr;
-	int ret = NF_ACCEPT;
-	int matchoff, matchlen;
-	__be32 ipaddr;
-	u_int16_t port;
-	typeof(ip_nat_sip_hook) ip_nat_sip;
-
-	/* No Data ? */
-	dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
-	if (dataoff >= (*pskb)->len) {
-		DEBUGP("skb->len = %u\n", (*pskb)->len);
-		return NF_ACCEPT;
-	}
-
-	ip_ct_refresh(ct, *pskb, sip_timeout * HZ);
-
-	if (!skb_is_nonlinear(*pskb))
-		dptr = (*pskb)->data + dataoff;
-	else {
-		DEBUGP("Copy of skbuff not supported yet.\n");
-		goto out;
-	}
-
-	ip_nat_sip = rcu_dereference(ip_nat_sip_hook);
-	if (ip_nat_sip) {
-		if (!ip_nat_sip(pskb, ctinfo, ct, &dptr)) {
-			ret = NF_DROP;
-			goto out;
-		}
-	}
-
-	/* After this point NAT, could have mangled skb, so
-	   we need to recalculate payload lenght. */
-	datalen = (*pskb)->len - dataoff;
-
-	if (datalen < (sizeof("SIP/2.0 200") - 1))
-		goto out;
-
-	/* RTP info only in some SDP pkts */
-	if (memcmp(dptr, "INVITE", sizeof("INVITE") - 1) != 0 &&
-	    memcmp(dptr, "SIP/2.0 200", sizeof("SIP/2.0 200") - 1) != 0) {
-		goto out;
-	}
-	/* Get ip and port address from SDP packet. */
-	if (ct_sip_get_info(dptr, datalen, &matchoff, &matchlen,
-			    POS_CONNECTION) > 0) {
-
-		/* We'll drop only if there are parse problems. */
-		if (parse_ipaddr(dptr + matchoff, NULL, &ipaddr,
-				 dptr + datalen) < 0) {
-			ret = NF_DROP;
-			goto out;
-		}
-		if (ct_sip_get_info(dptr, datalen, &matchoff, &matchlen,
-				    POS_MEDIA) > 0) {
-
-			port = simple_strtoul(dptr + matchoff, NULL, 10);
-			if (port < 1024) {
-				ret = NF_DROP;
-				goto out;
-			}
-			ret = set_expected_rtp(pskb, ct, ctinfo,
-					       ipaddr, port, dptr);
-		}
-	}
-out:
-	return ret;
-}
-
-static struct ip_conntrack_helper sip[MAX_PORTS];
-static char sip_names[MAX_PORTS][10];
-
-static void fini(void)
-{
-	int i;
-	for (i = 0; i < ports_c; i++) {
-		DEBUGP("unregistering helper for port %d\n", ports[i]);
-		ip_conntrack_helper_unregister(&sip[i]);
-	}
-}
-
-static int __init init(void)
-{
-	int i, ret;
-	char *tmpname;
-
-	if (ports_c == 0)
-		ports[ports_c++] = SIP_PORT;
-
-	for (i = 0; i < ports_c; i++) {
-		/* Create helper structure */
-		memset(&sip[i], 0, sizeof(struct ip_conntrack_helper));
-
-		sip[i].tuple.dst.protonum = IPPROTO_UDP;
-		sip[i].tuple.src.u.udp.port = htons(ports[i]);
-		sip[i].mask.src.u.udp.port = htons(0xFFFF);
-		sip[i].mask.dst.protonum = 0xFF;
-		sip[i].max_expected = 2;
-		sip[i].timeout = 3 * 60; /* 3 minutes */
-		sip[i].me = THIS_MODULE;
-		sip[i].help = sip_help;
-
-		tmpname = &sip_names[i][0];
-		if (ports[i] == SIP_PORT)
-			sprintf(tmpname, "sip");
-		else
-			sprintf(tmpname, "sip-%d", i);
-		sip[i].name = tmpname;
-
-		DEBUGP("port #%d: %d\n", i, ports[i]);
-
-		ret = ip_conntrack_helper_register(&sip[i]);
-		if (ret) {
-			printk("ERROR registering helper for port %d\n",
-				ports[i]);
-			fini();
-			return ret;
-		}
-	}
-	return 0;
-}
-
-module_init(init);
-module_exit(fini);
diff --git a/net/ipv4/netfilter/ip_conntrack_standalone.c b/net/ipv4/netfilter/ip_conntrack_standalone.c
deleted file mode 100644
index 56b2f75..0000000
--- a/net/ipv4/netfilter/ip_conntrack_standalone.c
+++ /dev/null
@@ -1,962 +0,0 @@
-/* This file contains all the functions required for the standalone
-   ip_conntrack module.
-
-   These are not required by the compatibility layer.
-*/
-
-/* (C) 1999-2001 Paul `Rusty' Russell
- * (C) 2002-2005 Netfilter Core Team <coreteam@netfilter.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.
- */
-
-#include <linux/types.h>
-#include <linux/ip.h>
-#include <linux/netfilter.h>
-#include <linux/netfilter_ipv4.h>
-#include <linux/module.h>
-#include <linux/skbuff.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/percpu.h>
-#ifdef CONFIG_SYSCTL
-#include <linux/sysctl.h>
-#endif
-#include <net/checksum.h>
-#include <net/ip.h>
-#include <net/route.h>
-
-#include <linux/netfilter_ipv4/ip_conntrack.h>
-#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
-#include <linux/netfilter_ipv4/ip_conntrack_core.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-
-#if 0
-#define DEBUGP printk
-#else
-#define DEBUGP(format, args...)
-#endif
-
-MODULE_LICENSE("GPL");
-
-extern atomic_t ip_conntrack_count;
-DECLARE_PER_CPU(struct ip_conntrack_stat, ip_conntrack_stat);
-
-static int kill_proto(struct ip_conntrack *i, void *data)
-{
-	return (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum ==
-			*((u_int8_t *) data));
-}
-
-#ifdef CONFIG_PROC_FS
-static int
-print_tuple(struct seq_file *s, const struct ip_conntrack_tuple *tuple,
-	    struct ip_conntrack_protocol *proto)
-{
-	seq_printf(s, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
-		   NIPQUAD(tuple->src.ip), NIPQUAD(tuple->dst.ip));
-	return proto->print_tuple(s, tuple);
-}
-
-#ifdef CONFIG_IP_NF_CT_ACCT
-static unsigned int
-seq_print_counters(struct seq_file *s,
-		   const struct ip_conntrack_counter *counter)
-{
-	return seq_printf(s, "packets=%llu bytes=%llu ",
-			  (unsigned long long)counter->packets,
-			  (unsigned long long)counter->bytes);
-}
-#else
-#define seq_print_counters(x, y)	0
-#endif
-
-struct ct_iter_state {
-	unsigned int bucket;
-};
-
-static struct list_head *ct_get_first(struct seq_file *seq)
-{
-	struct ct_iter_state *st = seq->private;
-
-	for (st->bucket = 0;
-	     st->bucket < ip_conntrack_htable_size;
-	     st->bucket++) {
-		if (!list_empty(&ip_conntrack_hash[st->bucket]))
-			return ip_conntrack_hash[st->bucket].next;
-	}
-	return NULL;
-}
-
-static struct list_head *ct_get_next(struct seq_file *seq, struct list_head *head)
-{
-	struct ct_iter_state *st = seq->private;
-
-	head = head->next;
-	while (head == &ip_conntrack_hash[st->bucket]) {
-		if (++st->bucket >= ip_conntrack_htable_size)
-			return NULL;
-		head = ip_conntrack_hash[st->bucket].next;
-	}
-	return head;
-}
-
-static struct list_head *ct_get_idx(struct seq_file *seq, loff_t pos)
-{
-	struct list_head *head = ct_get_first(seq);
-
-	if (head)
-		while (pos && (head = ct_get_next(seq, head)))
-			pos--;
-	return pos ? NULL : head;
-}
-
-static void *ct_seq_start(struct seq_file *seq, loff_t *pos)
-{
-	read_lock_bh(&ip_conntrack_lock);
-	return ct_get_idx(seq, *pos);
-}
-
-static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
-{
-	(*pos)++;
-	return ct_get_next(s, v);
-}
-
-static void ct_seq_stop(struct seq_file *s, void *v)
-{
-	read_unlock_bh(&ip_conntrack_lock);
-}
-
-static int ct_seq_show(struct seq_file *s, void *v)
-{
-	const struct ip_conntrack_tuple_hash *hash = v;
-	const struct ip_conntrack *conntrack = tuplehash_to_ctrack(hash);
-	struct ip_conntrack_protocol *proto;
-
-	IP_NF_ASSERT(conntrack);
-
-	/* we only want to print DIR_ORIGINAL */
-	if (DIRECTION(hash))
-		return 0;
-
-	proto = __ip_conntrack_proto_find(conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum);
-	IP_NF_ASSERT(proto);
-
-	if (seq_printf(s, "%-8s %u %ld ",
-		      proto->name,
-		      conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum,
-		      timer_pending(&conntrack->timeout)
-		      ? (long)(conntrack->timeout.expires - jiffies)/HZ
-		      : 0) != 0)
-		return -ENOSPC;
-
-	if (proto->print_conntrack(s, conntrack))
-		return -ENOSPC;
-
-	if (print_tuple(s, &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
-			proto))
-		return -ENOSPC;
-
-	if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_ORIGINAL]))
-		return -ENOSPC;
-
-	if (!(test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)))
-		if (seq_printf(s, "[UNREPLIED] "))
-			return -ENOSPC;
-
-	if (print_tuple(s, &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple,
-			proto))
-		return -ENOSPC;
-
-	if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_REPLY]))
-		return -ENOSPC;
-
-	if (test_bit(IPS_ASSURED_BIT, &conntrack->status))
-		if (seq_printf(s, "[ASSURED] "))
-			return -ENOSPC;
-
-#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
-	if (seq_printf(s, "mark=%u ", conntrack->mark))
-		return -ENOSPC;
-#endif
-
-#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK
-	if (seq_printf(s, "secmark=%u ", conntrack->secmark))
-		return -ENOSPC;
-#endif
-
-	if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use)))
-		return -ENOSPC;
-
-	return 0;
-}
-
-static struct seq_operations ct_seq_ops = {
-	.start = ct_seq_start,
-	.next  = ct_seq_next,
-	.stop  = ct_seq_stop,
-	.show  = ct_seq_show
-};
-
-static int ct_open(struct inode *inode, struct file *file)
-{
-	struct seq_file *seq;
-	struct ct_iter_state *st;
-	int ret;
-
-	st = kmalloc(sizeof(struct ct_iter_state), GFP_KERNEL);
-	if (st == NULL)
-		return -ENOMEM;
-	ret = seq_open(file, &ct_seq_ops);
-	if (ret)
-		goto out_free;
-	seq          = file->private_data;
-	seq->private = st;
-	memset(st, 0, sizeof(struct ct_iter_state));
-	return ret;
-out_free:
-	kfree(st);
-	return ret;
-}
-
-static const struct file_operations ct_file_ops = {
-	.owner   = THIS_MODULE,
-	.open    = ct_open,
-	.read    = seq_read,
-	.llseek  = seq_lseek,
-	.release = seq_release_private,
-};
-
-/* expects */
-static void *exp_seq_start(struct seq_file *s, loff_t *pos)
-{
-	struct list_head *e = &ip_conntrack_expect_list;
-	loff_t i;
-
-	/* strange seq_file api calls stop even if we fail,
-	 * thus we need to grab lock since stop unlocks */
-	read_lock_bh(&ip_conntrack_lock);
-
-	if (list_empty(e))
-		return NULL;
-
-	for (i = 0; i <= *pos; i++) {
-		e = e->next;
-		if (e == &ip_conntrack_expect_list)
-			return NULL;
-	}
-	return e;
-}
-
-static void *exp_seq_next(struct seq_file *s, void *v, loff_t *pos)
-{
-	struct list_head *e = v;
-
-	++*pos;
-	e = e->next;
-
-	if (e == &ip_conntrack_expect_list)
-		return NULL;
-
-	return e;
-}
-
-static void exp_seq_stop(struct seq_file *s, void *v)
-{
-	read_unlock_bh(&ip_conntrack_lock);
-}
-
-static int exp_seq_show(struct seq_file *s, void *v)
-{
-	struct ip_conntrack_expect *expect = v;
-
-	if (expect->timeout.function)
-		seq_printf(s, "%ld ", timer_pending(&expect->timeout)
-			   ? (long)(expect->timeout.expires - jiffies)/HZ : 0);
-	else
-		seq_printf(s, "- ");
-
-	seq_printf(s, "proto=%u ", expect->tuple.dst.protonum);
-
-	print_tuple(s, &expect->tuple,
-		    __ip_conntrack_proto_find(expect->tuple.dst.protonum));
-	return seq_putc(s, '\n');
-}
-
-static struct seq_operations exp_seq_ops = {
-	.start = exp_seq_start,
-	.next = exp_seq_next,
-	.stop = exp_seq_stop,
-	.show = exp_seq_show
-};
-
-static int exp_open(struct inode *inode, struct file *file)
-{
-	return seq_open(file, &exp_seq_ops);
-}
-
-static const struct file_operations exp_file_ops = {
-	.owner   = THIS_MODULE,
-	.open    = exp_open,
-	.read    = seq_read,
-	.llseek  = seq_lseek,
-	.release = seq_release
-};
-
-static void *ct_cpu_seq_start(struct seq_file *seq, loff_t *pos)
-{
-	int cpu;
-
-	if (*pos == 0)
-		return SEQ_START_TOKEN;
-
-	for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) {
-		if (!cpu_possible(cpu))
-			continue;
-		*pos = cpu+1;
-		return &per_cpu(ip_conntrack_stat, cpu);
-	}
-
-	return NULL;
-}
-
-static void *ct_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
-	int cpu;
-
-	for (cpu = *pos; cpu < NR_CPUS; ++cpu) {
-		if (!cpu_possible(cpu))
-			continue;
-		*pos = cpu+1;
-		return &per_cpu(ip_conntrack_stat, cpu);
-	}
-
-	return NULL;
-}
-
-static void ct_cpu_seq_stop(struct seq_file *seq, void *v)
-{
-}
-
-static int ct_cpu_seq_show(struct seq_file *seq, void *v)
-{
-	unsigned int nr_conntracks = atomic_read(&ip_conntrack_count);
-	struct ip_conntrack_stat *st = v;
-
-	if (v == SEQ_START_TOKEN) {
-		seq_printf(seq, "entries  searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error  expect_new expect_create expect_delete\n");
-		return 0;
-	}
-
-	seq_printf(seq, "%08x  %08x %08x %08x %08x %08x %08x %08x "
-			"%08x %08x %08x %08x %08x  %08x %08x %08x \n",
-		   nr_conntracks,
-		   st->searched,
-		   st->found,
-		   st->new,
-		   st->invalid,
-		   st->ignore,
-		   st->delete,
-		   st->delete_list,
-		   st->insert,
-		   st->insert_failed,
-		   st->drop,
-		   st->early_drop,
-		   st->error,
-
-		   st->expect_new,
-		   st->expect_create,
-		   st->expect_delete
-		);
-	return 0;
-}
-
-static struct seq_operations ct_cpu_seq_ops = {
-	.start  = ct_cpu_seq_start,
-	.next   = ct_cpu_seq_next,
-	.stop   = ct_cpu_seq_stop,
-	.show   = ct_cpu_seq_show,
-};
-
-static int ct_cpu_seq_open(struct inode *inode, struct file *file)
-{
-	return seq_open(file, &ct_cpu_seq_ops);
-}
-
-static const struct file_operations ct_cpu_seq_fops = {
-	.owner   = THIS_MODULE,
-	.open    = ct_cpu_seq_open,
-	.read    = seq_read,
-	.llseek  = seq_lseek,
-	.release = seq_release_private,
-};
-#endif
-
-static unsigned int ip_confirm(unsigned int hooknum,
-			       struct sk_buff **pskb,
-			       const struct net_device *in,
-			       const struct net_device *out,
-			       int (*okfn)(struct sk_buff *))
-{
-	/* We've seen it coming out the other side: confirm it */
-	return ip_conntrack_confirm(pskb);
-}
-
-static unsigned int ip_conntrack_help(unsigned int hooknum,
-				      struct sk_buff **pskb,
-				      const struct net_device *in,
-				      const struct net_device *out,
-				      int (*okfn)(struct sk_buff *))
-{
-	struct ip_conntrack *ct;
-	enum ip_conntrack_info ctinfo;
-
-	/* This is where we call the helper: as the packet goes out. */
-	ct = ip_conntrack_get(*pskb, &ctinfo);
-	if (ct && ct->helper && ctinfo != IP_CT_RELATED + IP_CT_IS_REPLY) {
-		unsigned int ret;
-		ret = ct->helper->help(pskb, ct, ctinfo);
-		if (ret != NF_ACCEPT)
-			return ret;
-	}
-	return NF_ACCEPT;
-}
-
-static unsigned int ip_conntrack_defrag(unsigned int hooknum,
-					struct sk_buff **pskb,
-					const struct net_device *in,
-					const struct net_device *out,
-					int (*okfn)(struct sk_buff *))
-{
-#if !defined(CONFIG_IP_NF_NAT) && !defined(CONFIG_IP_NF_NAT_MODULE)
-	/* Previously seen (loopback)?  Ignore.  Do this before
-	   fragment check. */
-	if ((*pskb)->nfct)
-		return NF_ACCEPT;
-#endif
-
-	/* Gather fragments. */
-	if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
-		*pskb = ip_ct_gather_frags(*pskb,
-					   hooknum == NF_IP_PRE_ROUTING ?
-					   IP_DEFRAG_CONNTRACK_IN :
-					   IP_DEFRAG_CONNTRACK_OUT);
-		if (!*pskb)
-			return NF_STOLEN;
-	}
-	return NF_ACCEPT;
-}
-
-static unsigned int ip_conntrack_local(unsigned int hooknum,
-				       struct sk_buff **pskb,
-				       const struct net_device *in,
-				       const struct net_device *out,
-				       int (*okfn)(struct sk_buff *))
-{
-	/* root is playing with raw sockets. */
-	if ((*pskb)->len < sizeof(struct iphdr)
-	    || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) {
-		if (net_ratelimit())
-			printk("ipt_hook: happy cracking.\n");
-		return NF_ACCEPT;
-	}
-	return ip_conntrack_in(hooknum, pskb, in, out, okfn);
-}
-
-/* Connection tracking may drop packets, but never alters them, so
-   make it the first hook. */
-static struct nf_hook_ops ip_conntrack_ops[] = {
-	{
-		.hook		= ip_conntrack_defrag,
-		.owner		= THIS_MODULE,
-		.pf		= PF_INET,
-		.hooknum	= NF_IP_PRE_ROUTING,
-		.priority	= NF_IP_PRI_CONNTRACK_DEFRAG,
-	},
-	{
-		.hook		= ip_conntrack_in,
-		.owner		= THIS_MODULE,
-		.pf		= PF_INET,
-		.hooknum	= NF_IP_PRE_ROUTING,
-		.priority	= NF_IP_PRI_CONNTRACK,
-	},
-	{
-		.hook		= ip_conntrack_defrag,
-		.owner		= THIS_MODULE,
-		.pf		= PF_INET,
-		.hooknum	= NF_IP_LOCAL_OUT,
-		.priority	= NF_IP_PRI_CONNTRACK_DEFRAG,
-	},
-	{
-		.hook		= ip_conntrack_local,
-		.owner		= THIS_MODULE,
-		.pf		= PF_INET,
-		.hooknum	= NF_IP_LOCAL_OUT,
-		.priority	= NF_IP_PRI_CONNTRACK,
-	},
-	{
-		.hook		= ip_conntrack_help,
-		.owner		= THIS_MODULE,
-		.pf		= PF_INET,
-		.hooknum	= NF_IP_POST_ROUTING,
-		.priority	= NF_IP_PRI_CONNTRACK_HELPER,
-	},
-	{
-		.hook		= ip_conntrack_help,
-		.owner		= THIS_MODULE,
-		.pf		= PF_INET,
-		.hooknum	= NF_IP_LOCAL_IN,
-		.priority	= NF_IP_PRI_CONNTRACK_HELPER,
-	},
-	{
-		.hook		= ip_confirm,
-		.owner		= THIS_MODULE,
-		.pf		= PF_INET,
-		.hooknum	= NF_IP_POST_ROUTING,
-		.priority	= NF_IP_PRI_CONNTRACK_CONFIRM,
-	},
-	{
-		.hook		= ip_confirm,
-		.owner		= THIS_MODULE,
-		.pf		= PF_INET,
-		.hooknum	= NF_IP_LOCAL_IN,
-		.priority	= NF_IP_PRI_CONNTRACK_CONFIRM,
-	},
-};
-
-/* Sysctl support */
-
-int ip_conntrack_checksum __read_mostly = 1;
-
-#ifdef CONFIG_SYSCTL
-
-/* From ip_conntrack_core.c */
-extern int ip_conntrack_max;
-extern unsigned int ip_conntrack_htable_size;
-
-/* From ip_conntrack_proto_tcp.c */
-extern unsigned int ip_ct_tcp_timeout_syn_sent;
-extern unsigned int ip_ct_tcp_timeout_syn_recv;
-extern unsigned int ip_ct_tcp_timeout_established;
-extern unsigned int ip_ct_tcp_timeout_fin_wait;
-extern unsigned int ip_ct_tcp_timeout_close_wait;
-extern unsigned int ip_ct_tcp_timeout_last_ack;
-extern unsigned int ip_ct_tcp_timeout_time_wait;
-extern unsigned int ip_ct_tcp_timeout_close;
-extern unsigned int ip_ct_tcp_timeout_max_retrans;
-extern int ip_ct_tcp_loose;
-extern int ip_ct_tcp_be_liberal;
-extern int ip_ct_tcp_max_retrans;
-
-/* From ip_conntrack_proto_udp.c */
-extern unsigned int ip_ct_udp_timeout;
-extern unsigned int ip_ct_udp_timeout_stream;
-
-/* From ip_conntrack_proto_icmp.c */
-extern unsigned int ip_ct_icmp_timeout;
-
-/* From ip_conntrack_proto_generic.c */
-extern unsigned int ip_ct_generic_timeout;
-
-/* Log invalid packets of a given protocol */
-static int log_invalid_proto_min = 0;
-static int log_invalid_proto_max = 255;
-
-static struct ctl_table_header *ip_ct_sysctl_header;
-
-static ctl_table ip_ct_sysctl_table[] = {
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_MAX,
-		.procname	= "ip_conntrack_max",
-		.data		= &ip_conntrack_max,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_COUNT,
-		.procname	= "ip_conntrack_count",
-		.data		= &ip_conntrack_count,
-		.maxlen		= sizeof(int),
-		.mode		= 0444,
-		.proc_handler	= &proc_dointvec,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_BUCKETS,
-		.procname	= "ip_conntrack_buckets",
-		.data		= &ip_conntrack_htable_size,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0444,
-		.proc_handler	= &proc_dointvec,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_CHECKSUM,
-		.procname	= "ip_conntrack_checksum",
-		.data		= &ip_conntrack_checksum,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_SENT,
-		.procname	= "ip_conntrack_tcp_timeout_syn_sent",
-		.data		= &ip_ct_tcp_timeout_syn_sent,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_jiffies,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_RECV,
-		.procname	= "ip_conntrack_tcp_timeout_syn_recv",
-		.data		= &ip_ct_tcp_timeout_syn_recv,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_jiffies,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED,
-		.procname	= "ip_conntrack_tcp_timeout_established",
-		.data		= &ip_ct_tcp_timeout_established,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_jiffies,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_FIN_WAIT,
-		.procname	= "ip_conntrack_tcp_timeout_fin_wait",
-		.data		= &ip_ct_tcp_timeout_fin_wait,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_jiffies,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_CLOSE_WAIT,
-		.procname	= "ip_conntrack_tcp_timeout_close_wait",
-		.data		= &ip_ct_tcp_timeout_close_wait,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_jiffies,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_LAST_ACK,
-		.procname	= "ip_conntrack_tcp_timeout_last_ack",
-		.data		= &ip_ct_tcp_timeout_last_ack,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_jiffies,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_TIME_WAIT,
-		.procname	= "ip_conntrack_tcp_timeout_time_wait",
-		.data		= &ip_ct_tcp_timeout_time_wait,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_jiffies,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_CLOSE,
-		.procname	= "ip_conntrack_tcp_timeout_close",
-		.data		= &ip_ct_tcp_timeout_close,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_jiffies,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_UDP_TIMEOUT,
-		.procname	= "ip_conntrack_udp_timeout",
-		.data		= &ip_ct_udp_timeout,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_jiffies,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_UDP_TIMEOUT_STREAM,
-		.procname	= "ip_conntrack_udp_timeout_stream",
-		.data		= &ip_ct_udp_timeout_stream,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_jiffies,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_ICMP_TIMEOUT,
-		.procname	= "ip_conntrack_icmp_timeout",
-		.data		= &ip_ct_icmp_timeout,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_jiffies,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_GENERIC_TIMEOUT,
-		.procname	= "ip_conntrack_generic_timeout",
-		.data		= &ip_ct_generic_timeout,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_jiffies,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_LOG_INVALID,
-		.procname	= "ip_conntrack_log_invalid",
-		.data		= &ip_ct_log_invalid,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_minmax,
-		.strategy	= &sysctl_intvec,
-		.extra1		= &log_invalid_proto_min,
-		.extra2		= &log_invalid_proto_max,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_MAX_RETRANS,
-		.procname	= "ip_conntrack_tcp_timeout_max_retrans",
-		.data		= &ip_ct_tcp_timeout_max_retrans,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_jiffies,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_TCP_LOOSE,
-		.procname	= "ip_conntrack_tcp_loose",
-		.data		= &ip_ct_tcp_loose,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_TCP_BE_LIBERAL,
-		.procname	= "ip_conntrack_tcp_be_liberal",
-		.data		= &ip_ct_tcp_be_liberal,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec,
-	},
-	{
-		.ctl_name	= NET_IPV4_NF_CONNTRACK_TCP_MAX_RETRANS,
-		.procname	= "ip_conntrack_tcp_max_retrans",
-		.data		= &ip_ct_tcp_max_retrans,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec,
-	},
-	{ .ctl_name = 0 }
-};
-
-#define NET_IP_CONNTRACK_MAX 2089
-
-static ctl_table ip_ct_netfilter_table[] = {
-	{
-		.ctl_name	= NET_IPV4_NETFILTER,
-		.procname	= "netfilter",
-		.mode		= 0555,
-		.child		= ip_ct_sysctl_table,
-	},
-	{
-		.ctl_name	= NET_IP_CONNTRACK_MAX,
-		.procname	= "ip_conntrack_max",
-		.data		= &ip_conntrack_max,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec
-	},
-	{ .ctl_name = 0 }
-};
-
-static ctl_table ip_ct_ipv4_table[] = {
-	{
-		.ctl_name	= NET_IPV4,
-		.procname	= "ipv4",
-		.mode		= 0555,
-		.child		= ip_ct_netfilter_table,
-	},
-	{ .ctl_name = 0 }
-};
-
-static ctl_table ip_ct_net_table[] = {
-	{
-		.ctl_name	= CTL_NET,
-		.procname	= "net",
-		.mode		= 0555,
-		.child		= ip_ct_ipv4_table,
-	},
-	{ .ctl_name = 0 }
-};
-
-EXPORT_SYMBOL(ip_ct_log_invalid);
-#endif /* CONFIG_SYSCTL */
-
-/* FIXME: Allow NULL functions and sub in pointers to generic for
-   them. --RR */
-int ip_conntrack_protocol_register(struct ip_conntrack_protocol *proto)
-{
-	int ret = 0;
-
-	write_lock_bh(&ip_conntrack_lock);
-	if (ip_ct_protos[proto->proto] != &ip_conntrack_generic_protocol) {
-		ret = -EBUSY;
-		goto out;
-	}
-	rcu_assign_pointer(ip_ct_protos[proto->proto], proto);
- out:
-	write_unlock_bh(&ip_conntrack_lock);
-	return ret;
-}
-
-void ip_conntrack_protocol_unregister(struct ip_conntrack_protocol *proto)
-{
-	write_lock_bh(&ip_conntrack_lock);
-	rcu_assign_pointer(ip_ct_protos[proto->proto],
-			   &ip_conntrack_generic_protocol);
-	write_unlock_bh(&ip_conntrack_lock);
-	synchronize_rcu();
-
-	/* Remove all contrack entries for this protocol */
-	ip_ct_iterate_cleanup(kill_proto, &proto->proto);
-}
-
-static int __init ip_conntrack_standalone_init(void)
-{
-#ifdef CONFIG_PROC_FS
-	struct proc_dir_entry *proc, *proc_exp, *proc_stat;
-#endif
-	int ret = 0;
-
-	ret = ip_conntrack_init();
-	if (ret < 0)
-		return ret;
-
-#ifdef CONFIG_PROC_FS
-	ret = -ENOMEM;
-	proc = proc_net_fops_create("ip_conntrack", 0440, &ct_file_ops);
-	if (!proc) goto cleanup_init;
-
-	proc_exp = proc_net_fops_create("ip_conntrack_expect", 0440,
-					&exp_file_ops);
-	if (!proc_exp) goto cleanup_proc;
-
-	proc_stat = create_proc_entry("ip_conntrack", S_IRUGO, proc_net_stat);
-	if (!proc_stat)
-		goto cleanup_proc_exp;
-
-	proc_stat->proc_fops = &ct_cpu_seq_fops;
-	proc_stat->owner = THIS_MODULE;
-#endif
-
-	ret = nf_register_hooks(ip_conntrack_ops, ARRAY_SIZE(ip_conntrack_ops));
-	if (ret < 0) {
-		printk("ip_conntrack: can't register hooks.\n");
-		goto cleanup_proc_stat;
-	}
-#ifdef CONFIG_SYSCTL
-	ip_ct_sysctl_header = register_sysctl_table(ip_ct_net_table);
-	if (ip_ct_sysctl_header == NULL) {
-		printk("ip_conntrack: can't register to sysctl.\n");
-		ret = -ENOMEM;
-		goto cleanup_hooks;
-	}
-#endif
-	return ret;
-
-#ifdef CONFIG_SYSCTL
- cleanup_hooks:
-	nf_unregister_hooks(ip_conntrack_ops, ARRAY_SIZE(ip_conntrack_ops));
-#endif
- cleanup_proc_stat:
-#ifdef CONFIG_PROC_FS
-	remove_proc_entry("ip_conntrack", proc_net_stat);
- cleanup_proc_exp:
-	proc_net_remove("ip_conntrack_expect");
- cleanup_proc:
-	proc_net_remove("ip_conntrack");
- cleanup_init:
-#endif /* CONFIG_PROC_FS */
-	ip_conntrack_cleanup();
-	return ret;
-}
-
-static void __exit ip_conntrack_standalone_fini(void)
-{
-	synchronize_net();
-#ifdef CONFIG_SYSCTL
-	unregister_sysctl_table(ip_ct_sysctl_header);
-#endif
-	nf_unregister_hooks(ip_conntrack_ops, ARRAY_SIZE(ip_conntrack_ops));
-#ifdef CONFIG_PROC_FS
-	remove_proc_entry("ip_conntrack", proc_net_stat);
-	proc_net_remove("ip_conntrack_expect");
-	proc_net_remove("ip_conntrack");
-#endif /* CONFIG_PROC_FS */
-	ip_conntrack_cleanup();
-}
-
-module_init(ip_conntrack_standalone_init);
-module_exit(ip_conntrack_standalone_fini);
-
-/* Some modules need us, but don't depend directly on any symbol.
-   They should call this. */
-void need_conntrack(void)
-{
-}
-
-#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS
-EXPORT_SYMBOL_GPL(ip_conntrack_chain);
-EXPORT_SYMBOL_GPL(ip_conntrack_expect_chain);
-EXPORT_SYMBOL_GPL(ip_conntrack_register_notifier);
-EXPORT_SYMBOL_GPL(ip_conntrack_unregister_notifier);
-EXPORT_SYMBOL_GPL(__ip_ct_event_cache_init);
-EXPORT_PER_CPU_SYMBOL_GPL(ip_conntrack_ecache);
-#endif
-EXPORT_SYMBOL(ip_conntrack_protocol_register);
-EXPORT_SYMBOL(ip_conntrack_protocol_unregister);
-EXPORT_SYMBOL(ip_ct_get_tuple);
-EXPORT_SYMBOL(invert_tuplepr);
-EXPORT_SYMBOL(ip_conntrack_alter_reply);
-EXPORT_SYMBOL(ip_conntrack_destroyed);
-EXPORT_SYMBOL(need_conntrack);
-EXPORT_SYMBOL(ip_conntrack_helper_register);
-EXPORT_SYMBOL(ip_conntrack_helper_unregister);
-EXPORT_SYMBOL(ip_ct_iterate_cleanup);
-EXPORT_SYMBOL(__ip_ct_refresh_acct);
-
-EXPORT_SYMBOL(ip_conntrack_expect_alloc);
-EXPORT_SYMBOL(ip_conntrack_expect_put);
-EXPORT_SYMBOL_GPL(__ip_conntrack_expect_find);
-EXPORT_SYMBOL_GPL(ip_conntrack_expect_find_get);
-EXPORT_SYMBOL(ip_conntrack_expect_related);
-EXPORT_SYMBOL(ip_conntrack_unexpect_related);
-EXPORT_SYMBOL_GPL(ip_conntrack_expect_list);
-EXPORT_SYMBOL_GPL(ip_ct_unlink_expect);
-
-EXPORT_SYMBOL(ip_conntrack_tuple_taken);
-EXPORT_SYMBOL(ip_ct_gather_frags);
-EXPORT_SYMBOL(ip_conntrack_htable_size);
-EXPORT_SYMBOL(ip_conntrack_lock);
-EXPORT_SYMBOL(ip_conntrack_hash);
-EXPORT_SYMBOL(ip_conntrack_untracked);
-EXPORT_SYMBOL_GPL(ip_conntrack_find_get);
-#ifdef CONFIG_IP_NF_NAT_NEEDED
-EXPORT_SYMBOL(ip_conntrack_tcp_update);
-#endif
-
-EXPORT_SYMBOL_GPL(ip_conntrack_flush);
-EXPORT_SYMBOL_GPL(__ip_conntrack_find);
-
-EXPORT_SYMBOL_GPL(ip_conntrack_alloc);
-EXPORT_SYMBOL_GPL(ip_conntrack_free);
-EXPORT_SYMBOL_GPL(ip_conntrack_hash_insert);
-
-EXPORT_SYMBOL_GPL(ip_ct_remove_expectations);
-
-EXPORT_SYMBOL_GPL(ip_conntrack_helper_find_get);
-EXPORT_SYMBOL_GPL(ip_conntrack_helper_put);
-EXPORT_SYMBOL_GPL(__ip_conntrack_helper_find_byname);
-
-EXPORT_SYMBOL_GPL(ip_conntrack_proto_find_get);
-EXPORT_SYMBOL_GPL(ip_conntrack_proto_put);
-EXPORT_SYMBOL_GPL(__ip_conntrack_proto_find);
-EXPORT_SYMBOL_GPL(ip_conntrack_checksum);
-#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
-    defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
-EXPORT_SYMBOL_GPL(ip_ct_port_tuple_to_nfattr);
-EXPORT_SYMBOL_GPL(ip_ct_port_nfattr_to_tuple);
-#endif
diff --git a/net/ipv4/netfilter/ip_conntrack_tftp.c b/net/ipv4/netfilter/ip_conntrack_tftp.c
deleted file mode 100644
index 76e175e..0000000
--- a/net/ipv4/netfilter/ip_conntrack_tftp.c
+++ /dev/null
@@ -1,161 +0,0 @@
-/* (C) 2001-2002 Magnus Boden <mb@ozaba.mine.nu>
- *
- * 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.
- *
- * Version: 0.0.7
- *
- * Thu 21 Mar 2002 Harald Welte <laforge@gnumonks.org>
- * 	- port to newnat API
- *
- */
-
-#include <linux/module.h>
-#include <linux/ip.h>
-#include <linux/udp.h>
-
-#include <linux/netfilter.h>
-#include <linux/netfilter_ipv4/ip_tables.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-#include <linux/netfilter_ipv4/ip_conntrack_tftp.h>
-#include <linux/moduleparam.h>
-
-MODULE_AUTHOR("Magnus Boden <mb@ozaba.mine.nu>");
-MODULE_DESCRIPTION("tftp connection tracking helper");
-MODULE_LICENSE("GPL");
-
-#define MAX_PORTS 8
-static unsigned short ports[MAX_PORTS];
-static int ports_c;
-module_param_array(ports, ushort, &ports_c, 0400);
-MODULE_PARM_DESC(ports, "port numbers of tftp servers");
-
-#if 0
-#define DEBUGP(format, args...) printk("%s:%s:" format, \
-				       __FILE__, __FUNCTION__ , ## args)
-#else
-#define DEBUGP(format, args...)
-#endif
-
-unsigned int (*ip_nat_tftp_hook)(struct sk_buff **pskb,
-				 enum ip_conntrack_info ctinfo,
-				 struct ip_conntrack_expect *exp);
-EXPORT_SYMBOL_GPL(ip_nat_tftp_hook);
-
-static int tftp_help(struct sk_buff **pskb,
-		     struct ip_conntrack *ct,
-		     enum ip_conntrack_info ctinfo)
-{
-	struct tftphdr _tftph, *tfh;
-	struct ip_conntrack_expect *exp;
-	unsigned int ret = NF_ACCEPT;
-	typeof(ip_nat_tftp_hook) ip_nat_tftp;
-
-	tfh = skb_header_pointer(*pskb,
-				 (*pskb)->nh.iph->ihl*4+sizeof(struct udphdr),
-				 sizeof(_tftph), &_tftph);
-	if (tfh == NULL)
-		return NF_ACCEPT;
-
-	switch (ntohs(tfh->opcode)) {
-	/* RRQ and WRQ works the same way */
-	case TFTP_OPCODE_READ:
-	case TFTP_OPCODE_WRITE:
-		DEBUGP("");
-		DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
-		DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
-
-		exp = ip_conntrack_expect_alloc(ct);
-		if (exp == NULL)
-			return NF_DROP;
-
-		exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
-		exp->mask.src.ip = htonl(0xffffffff);
-		exp->mask.src.u.udp.port = 0;
-		exp->mask.dst.ip = htonl(0xffffffff);
-		exp->mask.dst.u.udp.port = htons(0xffff);
-		exp->mask.dst.protonum = 0xff;
-		exp->expectfn = NULL;
-		exp->flags = 0;
-
-		DEBUGP("expect: ");
-		DUMP_TUPLE(&exp->tuple);
-		DUMP_TUPLE(&exp->mask);
-		ip_nat_tftp = rcu_dereference(ip_nat_tftp_hook);
-		if (ip_nat_tftp)
-			ret = ip_nat_tftp(pskb, ctinfo, exp);
-		else if (ip_conntrack_expect_related(exp) != 0)
-			ret = NF_DROP;
-		ip_conntrack_expect_put(exp);
-		break;
-	case TFTP_OPCODE_DATA:
-	case TFTP_OPCODE_ACK:
-		DEBUGP("Data/ACK opcode\n");
-		break;
-	case TFTP_OPCODE_ERROR:
-		DEBUGP("Error opcode\n");
-		break;
-	default:
-		DEBUGP("Unknown opcode\n");
-	}
-	return NF_ACCEPT;
-}
-
-static struct ip_conntrack_helper tftp[MAX_PORTS];
-static char tftp_names[MAX_PORTS][sizeof("tftp-65535")];
-
-static void ip_conntrack_tftp_fini(void)
-{
-	int i;
-
-	for (i = 0 ; i < ports_c; i++) {
-		DEBUGP("unregistering helper for port %d\n",
-			ports[i]);
-		ip_conntrack_helper_unregister(&tftp[i]);
-	}
-}
-
-static int __init ip_conntrack_tftp_init(void)
-{
-	int i, ret;
-	char *tmpname;
-
-	if (ports_c == 0)
-		ports[ports_c++] = TFTP_PORT;
-
-	for (i = 0; i < ports_c; i++) {
-		/* Create helper structure */
-		memset(&tftp[i], 0, sizeof(struct ip_conntrack_helper));
-
-		tftp[i].tuple.dst.protonum = IPPROTO_UDP;
-		tftp[i].tuple.src.u.udp.port = htons(ports[i]);
-		tftp[i].mask.dst.protonum = 0xFF;
-		tftp[i].mask.src.u.udp.port = htons(0xFFFF);
-		tftp[i].max_expected = 1;
-		tftp[i].timeout = 5 * 60; /* 5 minutes */
-		tftp[i].me = THIS_MODULE;
-		tftp[i].help = tftp_help;
-
-		tmpname = &tftp_names[i][0];
-		if (ports[i] == TFTP_PORT)
-			sprintf(tmpname, "tftp");
-		else
-			sprintf(tmpname, "tftp-%d", i);
-		tftp[i].name = tmpname;
-
-		DEBUGP("port #%d: %d\n", i, ports[i]);
-
-		ret=ip_conntrack_helper_register(&tftp[i]);
-		if (ret) {
-			printk("ERROR registering helper for port %d\n",
-				ports[i]);
-			ip_conntrack_tftp_fini();
-			return(ret);
-		}
-	}
-	return(0);
-}
-
-module_init(ip_conntrack_tftp_init);
-module_exit(ip_conntrack_tftp_fini);
diff --git a/net/ipv4/netfilter/ip_nat_amanda.c b/net/ipv4/netfilter/ip_nat_amanda.c
deleted file mode 100644
index 85df1a9..0000000
--- a/net/ipv4/netfilter/ip_nat_amanda.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/* Amanda extension for TCP NAT alteration.
- * (C) 2002 by Brian J. Murrell <netfilter@interlinx.bc.ca>
- * based on a copy of HW's ip_nat_irc.c as well as other modules
- *
- *      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.
- *
- *	Module load syntax:
- * 	insmod ip_nat_amanda.o
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/netfilter.h>
-#include <linux/skbuff.h>
-#include <linux/ip.h>
-#include <linux/udp.h>
-#include <net/tcp.h>
-#include <net/udp.h>
-
-#include <linux/netfilter_ipv4.h>
-#include <linux/netfilter_ipv4/ip_nat.h>
-#include <linux/netfilter_ipv4/ip_nat_helper.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-#include <linux/netfilter_ipv4/ip_conntrack_amanda.h>
-
-
-MODULE_AUTHOR("Brian J. Murrell <netfilter@interlinx.bc.ca>");
-MODULE_DESCRIPTION("Amanda NAT helper");
-MODULE_LICENSE("GPL");
-
-static unsigned int help(struct sk_buff **pskb,
-			 enum ip_conntrack_info ctinfo,
-			 unsigned int matchoff,
-			 unsigned int matchlen,
-			 struct ip_conntrack_expect *exp)
-{
-	char buffer[sizeof("65535")];
-	u_int16_t port;
-	unsigned int ret;
-
-	/* Connection comes from client. */
-	exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
-	exp->dir = IP_CT_DIR_ORIGINAL;
-
-	/* When you see the packet, we need to NAT it the same as the
-	 * this one (ie. same IP: it will be TCP and master is UDP). */
-	exp->expectfn = ip_nat_follow_master;
-
-	/* Try to get same port: if not, try to change it. */
-	for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) {
-		exp->tuple.dst.u.tcp.port = htons(port);
-		if (ip_conntrack_expect_related(exp) == 0)
-			break;
-	}
-
-	if (port == 0)
-		return NF_DROP;
-
-	sprintf(buffer, "%u", port);
-	ret = ip_nat_mangle_udp_packet(pskb, exp->master, ctinfo,
-				       matchoff, matchlen,
-				       buffer, strlen(buffer));
-	if (ret != NF_ACCEPT)
-		ip_conntrack_unexpect_related(exp);
-	return ret;
-}
-
-static void __exit ip_nat_amanda_fini(void)
-{
-	rcu_assign_pointer(ip_nat_amanda_hook, NULL);
-	synchronize_rcu();
-}
-
-static int __init ip_nat_amanda_init(void)
-{
-	BUG_ON(rcu_dereference(ip_nat_amanda_hook));
-	rcu_assign_pointer(ip_nat_amanda_hook, help);
-	return 0;
-}
-
-module_init(ip_nat_amanda_init);
-module_exit(ip_nat_amanda_fini);
diff --git a/net/ipv4/netfilter/ip_nat_core.c b/net/ipv4/netfilter/ip_nat_core.c
deleted file mode 100644
index 40737fd..0000000
--- a/net/ipv4/netfilter/ip_nat_core.c
+++ /dev/null
@@ -1,634 +0,0 @@
-/* NAT for netfilter; shared with compatibility layer. */
-
-/* (C) 1999-2001 Paul `Rusty' Russell
- * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.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.
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/timer.h>
-#include <linux/skbuff.h>
-#include <linux/netfilter_ipv4.h>
-#include <linux/vmalloc.h>
-#include <net/checksum.h>
-#include <net/icmp.h>
-#include <net/ip.h>
-#include <net/tcp.h>  /* For tcp_prot in getorigdst */
-#include <linux/icmp.h>
-#include <linux/udp.h>
-#include <linux/jhash.h>
-
-#include <linux/netfilter_ipv4/ip_conntrack.h>
-#include <linux/netfilter_ipv4/ip_conntrack_core.h>
-#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
-#include <linux/netfilter_ipv4/ip_nat.h>
-#include <linux/netfilter_ipv4/ip_nat_protocol.h>
-#include <linux/netfilter_ipv4/ip_nat_core.h>
-#include <linux/netfilter_ipv4/ip_nat_helper.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-
-#if 0
-#define DEBUGP printk
-#else
-#define DEBUGP(format, args...)
-#endif
-
-DEFINE_RWLOCK(ip_nat_lock);
-
-/* Calculated at init based on memory size */
-static unsigned int ip_nat_htable_size;
-
-static struct list_head *bysource;
-
-#define MAX_IP_NAT_PROTO 256
-static struct ip_nat_protocol *ip_nat_protos[MAX_IP_NAT_PROTO];
-
-static inline struct ip_nat_protocol *
-__ip_nat_proto_find(u_int8_t protonum)
-{
-	return rcu_dereference(ip_nat_protos[protonum]);
-}
-
-struct ip_nat_protocol *
-ip_nat_proto_find_get(u_int8_t protonum)
-{
-	struct ip_nat_protocol *p;
-
-	rcu_read_lock();
-	p = __ip_nat_proto_find(protonum);
-	if (!try_module_get(p->me))
-		p = &ip_nat_unknown_protocol;
-	rcu_read_unlock();
-
-	return p;
-}
-EXPORT_SYMBOL_GPL(ip_nat_proto_find_get);
-
-void
-ip_nat_proto_put(struct ip_nat_protocol *p)
-{
-	module_put(p->me);
-}
-EXPORT_SYMBOL_GPL(ip_nat_proto_put);
-
-/* We keep an extra hash for each conntrack, for fast searching. */
-static inline unsigned int
-hash_by_src(const struct ip_conntrack_tuple *tuple)
-{
-	/* Original src, to ensure we map it consistently if poss. */
-	return jhash_3words((__force u32)tuple->src.ip, tuple->src.u.all,
-			    tuple->dst.protonum, 0) % ip_nat_htable_size;
-}
-
-/* Noone using conntrack by the time this called. */
-static void ip_nat_cleanup_conntrack(struct ip_conntrack *conn)
-{
-	if (!(conn->status & IPS_NAT_DONE_MASK))
-		return;
-
-	write_lock_bh(&ip_nat_lock);
-	list_del(&conn->nat.info.bysource);
-	write_unlock_bh(&ip_nat_lock);
-}
-
-/* Is this tuple already taken? (not by us) */
-int
-ip_nat_used_tuple(const struct ip_conntrack_tuple *tuple,
-		  const struct ip_conntrack *ignored_conntrack)
-{
-	/* Conntrack tracking doesn't keep track of outgoing tuples; only
-	   incoming ones.  NAT means they don't have a fixed mapping,
-	   so we invert the tuple and look for the incoming reply.
-
-	   We could keep a separate hash if this proves too slow. */
-	struct ip_conntrack_tuple reply;
-
-	invert_tuplepr(&reply, tuple);
-	return ip_conntrack_tuple_taken(&reply, ignored_conntrack);
-}
-EXPORT_SYMBOL(ip_nat_used_tuple);
-
-/* If we source map this tuple so reply looks like reply_tuple, will
- * that meet the constraints of range. */
-static int
-in_range(const struct ip_conntrack_tuple *tuple,
-	 const struct ip_nat_range *range)
-{
-	struct ip_nat_protocol *proto;
-	int ret = 0;
-
-	/* If we are supposed to map IPs, then we must be in the
-	   range specified, otherwise let this drag us onto a new src IP. */
-	if (range->flags & IP_NAT_RANGE_MAP_IPS) {
-		if (ntohl(tuple->src.ip) < ntohl(range->min_ip)
-		    || ntohl(tuple->src.ip) > ntohl(range->max_ip))
-			return 0;
-	}
-
-	rcu_read_lock();
-	proto = __ip_nat_proto_find(tuple->dst.protonum);
-	if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)
-	    || proto->in_range(tuple, IP_NAT_MANIP_SRC,
-			       &range->min, &range->max))
-		ret = 1;
-	rcu_read_unlock();
-
-	return ret;
-}
-
-static inline int
-same_src(const struct ip_conntrack *ct,
-	 const struct ip_conntrack_tuple *tuple)
-{
-	return (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum
-		== tuple->dst.protonum
-		&& ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip
-		== tuple->src.ip
-		&& ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.all
-		== tuple->src.u.all);
-}
-
-/* Only called for SRC manip */
-static int
-find_appropriate_src(const struct ip_conntrack_tuple *tuple,
-		     struct ip_conntrack_tuple *result,
-		     const struct ip_nat_range *range)
-{
-	unsigned int h = hash_by_src(tuple);
-	struct ip_conntrack *ct;
-
-	read_lock_bh(&ip_nat_lock);
-	list_for_each_entry(ct, &bysource[h], nat.info.bysource) {
-		if (same_src(ct, tuple)) {
-			/* Copy source part from reply tuple. */
-			invert_tuplepr(result,
-				       &ct->tuplehash[IP_CT_DIR_REPLY].tuple);
-			result->dst = tuple->dst;
-
-			if (in_range(result, range)) {
-				read_unlock_bh(&ip_nat_lock);
-				return 1;
-			}
-		}
-	}
-	read_unlock_bh(&ip_nat_lock);
-	return 0;
-}
-
-/* For [FUTURE] fragmentation handling, we want the least-used
-   src-ip/dst-ip/proto triple.  Fairness doesn't come into it.  Thus
-   if the range specifies 1.2.3.4 ports 10000-10005 and 1.2.3.5 ports
-   1-65535, we don't do pro-rata allocation based on ports; we choose
-   the ip with the lowest src-ip/dst-ip/proto usage.
-*/
-static void
-find_best_ips_proto(struct ip_conntrack_tuple *tuple,
-		    const struct ip_nat_range *range,
-		    const struct ip_conntrack *conntrack,
-		    enum ip_nat_manip_type maniptype)
-{
-	__be32 *var_ipp;
-	/* Host order */
-	u_int32_t minip, maxip, j;
-
-	/* No IP mapping?  Do nothing. */
-	if (!(range->flags & IP_NAT_RANGE_MAP_IPS))
-		return;
-
-	if (maniptype == IP_NAT_MANIP_SRC)
-		var_ipp = &tuple->src.ip;
-	else
-		var_ipp = &tuple->dst.ip;
-
-	/* Fast path: only one choice. */
-	if (range->min_ip == range->max_ip) {
-		*var_ipp = range->min_ip;
-		return;
-	}
-
-	/* Hashing source and destination IPs gives a fairly even
-	 * spread in practice (if there are a small number of IPs
-	 * involved, there usually aren't that many connections
-	 * anyway).  The consistency means that servers see the same
-	 * client coming from the same IP (some Internet Banking sites
-	 * like this), even across reboots. */
-	minip = ntohl(range->min_ip);
-	maxip = ntohl(range->max_ip);
-	j = jhash_2words((__force u32)tuple->src.ip, (__force u32)tuple->dst.ip, 0);
-	*var_ipp = htonl(minip + j % (maxip - minip + 1));
-}
-
-/* Manipulate the tuple into the range given.  For NF_IP_POST_ROUTING,
- * we change the source to map into the range.  For NF_IP_PRE_ROUTING
- * and NF_IP_LOCAL_OUT, we change the destination to map into the
- * range.  It might not be possible to get a unique tuple, but we try.
- * At worst (or if we race), we will end up with a final duplicate in
- * __ip_conntrack_confirm and drop the packet. */
-static void
-get_unique_tuple(struct ip_conntrack_tuple *tuple,
-		 const struct ip_conntrack_tuple *orig_tuple,
-		 const struct ip_nat_range *range,
-		 struct ip_conntrack *conntrack,
-		 enum ip_nat_manip_type maniptype)
-{
-	struct ip_nat_protocol *proto;
-
-	/* 1) If this srcip/proto/src-proto-part is currently mapped,
-	   and that same mapping gives a unique tuple within the given
-	   range, use that.
-
-	   This is only required for source (ie. NAT/masq) mappings.
-	   So far, we don't do local source mappings, so multiple
-	   manips not an issue.  */
-	if (maniptype == IP_NAT_MANIP_SRC) {
-		if (find_appropriate_src(orig_tuple, tuple, range)) {
-			DEBUGP("get_unique_tuple: Found current src map\n");
-			if (!(range->flags & IP_NAT_RANGE_PROTO_RANDOM))
-				if (!ip_nat_used_tuple(tuple, conntrack))
-					return;
-		}
-	}
-
-	/* 2) Select the least-used IP/proto combination in the given
-	   range. */
-	*tuple = *orig_tuple;
-	find_best_ips_proto(tuple, range, conntrack, maniptype);
-
-	/* 3) The per-protocol part of the manip is made to map into
-	   the range to make a unique tuple. */
-
-	rcu_read_lock();
-	proto = __ip_nat_proto_find(orig_tuple->dst.protonum);
-
-	/* Change protocol info to have some randomization */
-	if (range->flags & IP_NAT_RANGE_PROTO_RANDOM) {
-		proto->unique_tuple(tuple, range, maniptype, conntrack);
-		goto out;
-	}
-
-	/* Only bother mapping if it's not already in range and unique */
-	if ((!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)
-	     || proto->in_range(tuple, maniptype, &range->min, &range->max))
-	    && !ip_nat_used_tuple(tuple, conntrack))
-		goto out;
-
-	/* Last change: get protocol to try to obtain unique tuple. */
-	proto->unique_tuple(tuple, range, maniptype, conntrack);
-out:
-	rcu_read_unlock();
-}
-
-unsigned int
-ip_nat_setup_info(struct ip_conntrack *conntrack,
-		  const struct ip_nat_range *range,
-		  unsigned int hooknum)
-{
-	struct ip_conntrack_tuple curr_tuple, new_tuple;
-	struct ip_nat_info *info = &conntrack->nat.info;
-	int have_to_hash = !(conntrack->status & IPS_NAT_DONE_MASK);
-	enum ip_nat_manip_type maniptype = HOOK2MANIP(hooknum);
-
-	IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING
-		     || hooknum == NF_IP_POST_ROUTING
-		     || hooknum == NF_IP_LOCAL_IN
-		     || hooknum == NF_IP_LOCAL_OUT);
-	BUG_ON(ip_nat_initialized(conntrack, maniptype));
-
-	/* What we've got will look like inverse of reply. Normally
-	   this is what is in the conntrack, except for prior
-	   manipulations (future optimization: if num_manips == 0,
-	   orig_tp =
-	   conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple) */
-	invert_tuplepr(&curr_tuple,
-		       &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple);
-
-	get_unique_tuple(&new_tuple, &curr_tuple, range, conntrack, maniptype);
-
-	if (!ip_ct_tuple_equal(&new_tuple, &curr_tuple)) {
-		struct ip_conntrack_tuple reply;
-
-		/* Alter conntrack table so will recognize replies. */
-		invert_tuplepr(&reply, &new_tuple);
-		ip_conntrack_alter_reply(conntrack, &reply);
-
-		/* Non-atomic: we own this at the moment. */
-		if (maniptype == IP_NAT_MANIP_SRC)
-			conntrack->status |= IPS_SRC_NAT;
-		else
-			conntrack->status |= IPS_DST_NAT;
-	}
-
-	/* Place in source hash if this is the first time. */
-	if (have_to_hash) {
-		unsigned int srchash
-			= hash_by_src(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
-				      .tuple);
-		write_lock_bh(&ip_nat_lock);
-		list_add(&info->bysource, &bysource[srchash]);
-		write_unlock_bh(&ip_nat_lock);
-	}
-
-	/* It's done. */
-	if (maniptype == IP_NAT_MANIP_DST)
-		set_bit(IPS_DST_NAT_DONE_BIT, &conntrack->status);
-	else
-		set_bit(IPS_SRC_NAT_DONE_BIT, &conntrack->status);
-
-	return NF_ACCEPT;
-}
-EXPORT_SYMBOL(ip_nat_setup_info);
-
-/* Returns true if succeeded. */
-static int
-manip_pkt(u_int16_t proto,
-	  struct sk_buff **pskb,
-	  unsigned int iphdroff,
-	  const struct ip_conntrack_tuple *target,
-	  enum ip_nat_manip_type maniptype)
-{
-	struct iphdr *iph;
-	struct ip_nat_protocol *p;
-
-	if (!skb_make_writable(pskb, iphdroff + sizeof(*iph)))
-		return 0;
-
-	iph = (void *)(*pskb)->data + iphdroff;
-
-	/* Manipulate protcol part. */
-
-	/* rcu_read_lock()ed by nf_hook_slow */
-	p = __ip_nat_proto_find(proto);
-	if (!p->manip_pkt(pskb, iphdroff, target, maniptype))
-		return 0;
-
-	iph = (void *)(*pskb)->data + iphdroff;
-
-	if (maniptype == IP_NAT_MANIP_SRC) {
-		nf_csum_replace4(&iph->check, iph->saddr, target->src.ip);
-		iph->saddr = target->src.ip;
-	} else {
-		nf_csum_replace4(&iph->check, iph->daddr, target->dst.ip);
-		iph->daddr = target->dst.ip;
-	}
-	return 1;
-}
-
-/* Do packet manipulations according to ip_nat_setup_info. */
-unsigned int ip_nat_packet(struct ip_conntrack *ct,
-			   enum ip_conntrack_info ctinfo,
-			   unsigned int hooknum,
-			   struct sk_buff **pskb)
-{
-	enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
-	unsigned long statusbit;
-	enum ip_nat_manip_type mtype = HOOK2MANIP(hooknum);
-
-	if (mtype == IP_NAT_MANIP_SRC)
-		statusbit = IPS_SRC_NAT;
-	else
-		statusbit = IPS_DST_NAT;
-
-	/* Invert if this is reply dir. */
-	if (dir == IP_CT_DIR_REPLY)
-		statusbit ^= IPS_NAT_MASK;
-
-	/* Non-atomic: these bits don't change. */
-	if (ct->status & statusbit) {
-		struct ip_conntrack_tuple target;
-
-		/* We are aiming to look like inverse of other direction. */
-		invert_tuplepr(&target, &ct->tuplehash[!dir].tuple);
-
-		if (!manip_pkt(target.dst.protonum, pskb, 0, &target, mtype))
-			return NF_DROP;
-	}
-	return NF_ACCEPT;
-}
-EXPORT_SYMBOL_GPL(ip_nat_packet);
-
-/* Dir is direction ICMP is coming from (opposite to packet it contains) */
-int ip_nat_icmp_reply_translation(struct ip_conntrack *ct,
-				  enum ip_conntrack_info ctinfo,
-				  unsigned int hooknum,
-				  struct sk_buff **pskb)
-{
-	struct {
-		struct icmphdr icmp;
-		struct iphdr ip;
-	} *inside;
-	struct ip_conntrack_protocol *proto;
-	struct ip_conntrack_tuple inner, target;
-	int hdrlen = (*pskb)->nh.iph->ihl * 4;
-	enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
-	unsigned long statusbit;
-	enum ip_nat_manip_type manip = HOOK2MANIP(hooknum);
-
-	if (!skb_make_writable(pskb, hdrlen + sizeof(*inside)))
-		return 0;
-
-	inside = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4;
-
-	/* We're actually going to mangle it beyond trivial checksum
-	   adjustment, so make sure the current checksum is correct. */
-	if (nf_ip_checksum(*pskb, hooknum, hdrlen, 0))
-		return 0;
-
-	/* Must be RELATED */
-	IP_NF_ASSERT((*pskb)->nfctinfo == IP_CT_RELATED ||
-		     (*pskb)->nfctinfo == IP_CT_RELATED+IP_CT_IS_REPLY);
-
-	/* Redirects on non-null nats must be dropped, else they'll
-	   start talking to each other without our translation, and be
-	   confused... --RR */
-	if (inside->icmp.type == ICMP_REDIRECT) {
-		/* If NAT isn't finished, assume it and drop. */
-		if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK)
-			return 0;
-
-		if (ct->status & IPS_NAT_MASK)
-			return 0;
-	}
-
-	DEBUGP("icmp_reply_translation: translating error %p manp %u dir %s\n",
-	       *pskb, manip, dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY");
-
-	/* rcu_read_lock()ed by nf_hook_slow */
-	proto = __ip_conntrack_proto_find(inside->ip.protocol);
-	if (!ip_ct_get_tuple(&inside->ip, *pskb, (*pskb)->nh.iph->ihl*4 +
-			     sizeof(struct icmphdr) + inside->ip.ihl*4,
-			     &inner, proto))
-		return 0;
-
-	/* Change inner back to look like incoming packet.  We do the
-	   opposite manip on this hook to normal, because it might not
-	   pass all hooks (locally-generated ICMP).  Consider incoming
-	   packet: PREROUTING (DST manip), routing produces ICMP, goes
-	   through POSTROUTING (which must correct the DST manip). */
-	if (!manip_pkt(inside->ip.protocol, pskb,
-		       (*pskb)->nh.iph->ihl*4
-		       + sizeof(inside->icmp),
-		       &ct->tuplehash[!dir].tuple,
-		       !manip))
-		return 0;
-
-	if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) {
-		/* Reloading "inside" here since manip_pkt inner. */
-		inside = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4;
-		inside->icmp.checksum = 0;
-		inside->icmp.checksum = csum_fold(skb_checksum(*pskb, hdrlen,
-							       (*pskb)->len - hdrlen,
-							       0));
-	}
-
-	/* Change outer to look the reply to an incoming packet
-	 * (proto 0 means don't invert per-proto part). */
-	if (manip == IP_NAT_MANIP_SRC)
-		statusbit = IPS_SRC_NAT;
-	else
-		statusbit = IPS_DST_NAT;
-
-	/* Invert if this is reply dir. */
-	if (dir == IP_CT_DIR_REPLY)
-		statusbit ^= IPS_NAT_MASK;
-
-	if (ct->status & statusbit) {
-		invert_tuplepr(&target, &ct->tuplehash[!dir].tuple);
-		if (!manip_pkt(0, pskb, 0, &target, manip))
-			return 0;
-	}
-
-	return 1;
-}
-EXPORT_SYMBOL_GPL(ip_nat_icmp_reply_translation);
-
-/* Protocol registration. */
-int ip_nat_protocol_register(struct ip_nat_protocol *proto)
-{
-	int ret = 0;
-
-	write_lock_bh(&ip_nat_lock);
-	if (ip_nat_protos[proto->protonum] != &ip_nat_unknown_protocol) {
-		ret = -EBUSY;
-		goto out;
-	}
-	rcu_assign_pointer(ip_nat_protos[proto->protonum], proto);
- out:
-	write_unlock_bh(&ip_nat_lock);
-	return ret;
-}
-EXPORT_SYMBOL(ip_nat_protocol_register);
-
-/* Noone stores the protocol anywhere; simply delete it. */
-void ip_nat_protocol_unregister(struct ip_nat_protocol *proto)
-{
-	write_lock_bh(&ip_nat_lock);
-	rcu_assign_pointer(ip_nat_protos[proto->protonum],
-			   &ip_nat_unknown_protocol);
-	write_unlock_bh(&ip_nat_lock);
-	synchronize_rcu();
-}
-EXPORT_SYMBOL(ip_nat_protocol_unregister);
-
-#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
-    defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
-int
-ip_nat_port_range_to_nfattr(struct sk_buff *skb,
-			    const struct ip_nat_range *range)
-{
-	NFA_PUT(skb, CTA_PROTONAT_PORT_MIN, sizeof(__be16),
-		&range->min.tcp.port);
-	NFA_PUT(skb, CTA_PROTONAT_PORT_MAX, sizeof(__be16),
-		&range->max.tcp.port);
-
-	return 0;
-
-nfattr_failure:
-	return -1;
-}
-
-int
-ip_nat_port_nfattr_to_range(struct nfattr *tb[], struct ip_nat_range *range)
-{
-	int ret = 0;
-
-	/* we have to return whether we actually parsed something or not */
-
-	if (tb[CTA_PROTONAT_PORT_MIN-1]) {
-		ret = 1;
-		range->min.tcp.port =
-			*(__be16 *)NFA_DATA(tb[CTA_PROTONAT_PORT_MIN-1]);
-	}
-
-	if (!tb[CTA_PROTONAT_PORT_MAX-1]) {
-		if (ret)
-			range->max.tcp.port = range->min.tcp.port;
-	} else {
-		ret = 1;
-		range->max.tcp.port =
-			*(__be16 *)NFA_DATA(tb[CTA_PROTONAT_PORT_MAX-1]);
-	}
-
-	return ret;
-}
-EXPORT_SYMBOL_GPL(ip_nat_port_nfattr_to_range);
-EXPORT_SYMBOL_GPL(ip_nat_port_range_to_nfattr);
-#endif
-
-static int __init ip_nat_init(void)
-{
-	size_t i;
-
-	/* Leave them the same for the moment. */
-	ip_nat_htable_size = ip_conntrack_htable_size;
-
-	/* One vmalloc for both hash tables */
-	bysource = vmalloc(sizeof(struct list_head) * ip_nat_htable_size);
-	if (!bysource)
-		return -ENOMEM;
-
-	/* Sew in builtin protocols. */
-	write_lock_bh(&ip_nat_lock);
-	for (i = 0; i < MAX_IP_NAT_PROTO; i++)
-		rcu_assign_pointer(ip_nat_protos[i], &ip_nat_unknown_protocol);
-	rcu_assign_pointer(ip_nat_protos[IPPROTO_TCP], &ip_nat_protocol_tcp);
-	rcu_assign_pointer(ip_nat_protos[IPPROTO_UDP], &ip_nat_protocol_udp);
-	rcu_assign_pointer(ip_nat_protos[IPPROTO_ICMP], &ip_nat_protocol_icmp);
-	write_unlock_bh(&ip_nat_lock);
-
-	for (i = 0; i < ip_nat_htable_size; i++) {
-		INIT_LIST_HEAD(&bysource[i]);
-	}
-
-	/* FIXME: Man, this is a hack.  <SIGH> */
-	IP_NF_ASSERT(rcu_dereference(ip_conntrack_destroyed) == NULL);
-	rcu_assign_pointer(ip_conntrack_destroyed, ip_nat_cleanup_conntrack);
-
-	/* Initialize fake conntrack so that NAT will skip it */
-	ip_conntrack_untracked.status |= IPS_NAT_DONE_MASK;
-	return 0;
-}
-
-/* Clear NAT section of all conntracks, in case we're loaded again. */
-static int clean_nat(struct ip_conntrack *i, void *data)
-{
-	memset(&i->nat, 0, sizeof(i->nat));
-	i->status &= ~(IPS_NAT_MASK | IPS_NAT_DONE_MASK | IPS_SEQ_ADJUST);
-	return 0;
-}
-
-static void __exit ip_nat_cleanup(void)
-{
-	ip_ct_iterate_cleanup(&clean_nat, NULL);
-	rcu_assign_pointer(ip_conntrack_destroyed, NULL);
-	synchronize_rcu();
-	vfree(bysource);
-}
-
-MODULE_LICENSE("GPL");
-
-module_init(ip_nat_init);
-module_exit(ip_nat_cleanup);
diff --git a/net/ipv4/netfilter/ip_nat_ftp.c b/net/ipv4/netfilter/ip_nat_ftp.c
deleted file mode 100644
index 32e01d8..0000000
--- a/net/ipv4/netfilter/ip_nat_ftp.c
+++ /dev/null
@@ -1,180 +0,0 @@
-/* FTP extension for TCP NAT alteration. */
-
-/* (C) 1999-2001 Paul `Rusty' Russell
- * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.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.
- */
-
-#include <linux/module.h>
-#include <linux/netfilter_ipv4.h>
-#include <linux/ip.h>
-#include <linux/tcp.h>
-#include <linux/moduleparam.h>
-#include <net/tcp.h>
-#include <linux/netfilter_ipv4/ip_nat.h>
-#include <linux/netfilter_ipv4/ip_nat_helper.h>
-#include <linux/netfilter_ipv4/ip_nat_rule.h>
-#include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>");
-MODULE_DESCRIPTION("ftp NAT helper");
-
-#if 0
-#define DEBUGP printk
-#else
-#define DEBUGP(format, args...)
-#endif
-
-/* FIXME: Time out? --RR */
-
-static int
-mangle_rfc959_packet(struct sk_buff **pskb,
-		     __be32 newip,
-		     u_int16_t port,
-		     unsigned int matchoff,
-		     unsigned int matchlen,
-		     struct ip_conntrack *ct,
-		     enum ip_conntrack_info ctinfo,
-		     u32 *seq)
-{
-	char buffer[sizeof("nnn,nnn,nnn,nnn,nnn,nnn")];
-
-	sprintf(buffer, "%u,%u,%u,%u,%u,%u",
-		NIPQUAD(newip), port>>8, port&0xFF);
-
-	DEBUGP("calling ip_nat_mangle_tcp_packet\n");
-
-	*seq += strlen(buffer) - matchlen;
-	return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff,
-					matchlen, buffer, strlen(buffer));
-}
-
-/* |1|132.235.1.2|6275| */
-static int
-mangle_eprt_packet(struct sk_buff **pskb,
-		   __be32 newip,
-		   u_int16_t port,
-		   unsigned int matchoff,
-		   unsigned int matchlen,
-		   struct ip_conntrack *ct,
-		   enum ip_conntrack_info ctinfo,
-		   u32 *seq)
-{
-	char buffer[sizeof("|1|255.255.255.255|65535|")];
-
-	sprintf(buffer, "|1|%u.%u.%u.%u|%u|", NIPQUAD(newip), port);
-
-	DEBUGP("calling ip_nat_mangle_tcp_packet\n");
-
-	*seq += strlen(buffer) - matchlen;
-	return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff,
-					matchlen, buffer, strlen(buffer));
-}
-
-/* |1|132.235.1.2|6275| */
-static int
-mangle_epsv_packet(struct sk_buff **pskb,
-		   __be32 newip,
-		   u_int16_t port,
-		   unsigned int matchoff,
-		   unsigned int matchlen,
-		   struct ip_conntrack *ct,
-		   enum ip_conntrack_info ctinfo,
-		   u32 *seq)
-{
-	char buffer[sizeof("|||65535|")];
-
-	sprintf(buffer, "|||%u|", port);
-
-	DEBUGP("calling ip_nat_mangle_tcp_packet\n");
-
-	*seq += strlen(buffer) - matchlen;
-	return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff,
-					matchlen, buffer, strlen(buffer));
-}
-
-static int (*mangle[])(struct sk_buff **, __be32, u_int16_t,
-		     unsigned int,
-		     unsigned int,
-		     struct ip_conntrack *,
-		     enum ip_conntrack_info,
-		     u32 *seq)
-= { [IP_CT_FTP_PORT] = mangle_rfc959_packet,
-    [IP_CT_FTP_PASV] = mangle_rfc959_packet,
-    [IP_CT_FTP_EPRT] = mangle_eprt_packet,
-    [IP_CT_FTP_EPSV] = mangle_epsv_packet
-};
-
-/* So, this packet has hit the connection tracking matching code.
-   Mangle it, and change the expectation to match the new version. */
-static unsigned int ip_nat_ftp(struct sk_buff **pskb,
-			       enum ip_conntrack_info ctinfo,
-			       enum ip_ct_ftp_type type,
-			       unsigned int matchoff,
-			       unsigned int matchlen,
-			       struct ip_conntrack_expect *exp,
-			       u32 *seq)
-{
-	__be32 newip;
-	u_int16_t port;
-	int dir = CTINFO2DIR(ctinfo);
-	struct ip_conntrack *ct = exp->master;
-
-	DEBUGP("FTP_NAT: type %i, off %u len %u\n", type, matchoff, matchlen);
-
-	/* Connection will come from wherever this packet goes, hence !dir */
-	newip = ct->tuplehash[!dir].tuple.dst.ip;
-	exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
-	exp->dir = !dir;
-
-	/* When you see the packet, we need to NAT it the same as the
-	 * this one. */
-	exp->expectfn = ip_nat_follow_master;
-
-	/* Try to get same port: if not, try to change it. */
-	for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) {
-		exp->tuple.dst.u.tcp.port = htons(port);
-		if (ip_conntrack_expect_related(exp) == 0)
-			break;
-	}
-
-	if (port == 0)
-		return NF_DROP;
-
-	if (!mangle[type](pskb, newip, port, matchoff, matchlen, ct, ctinfo,
-			  seq)) {
-		ip_conntrack_unexpect_related(exp);
-		return NF_DROP;
-	}
-	return NF_ACCEPT;
-}
-
-static void __exit ip_nat_ftp_fini(void)
-{
-	rcu_assign_pointer(ip_nat_ftp_hook, NULL);
-	synchronize_rcu();
-}
-
-static int __init ip_nat_ftp_init(void)
-{
-	BUG_ON(rcu_dereference(ip_nat_ftp_hook));
-	rcu_assign_pointer(ip_nat_ftp_hook, ip_nat_ftp);
-	return 0;
-}
-
-/* Prior to 2.6.11, we had a ports param.  No longer, but don't break users. */
-static int warn_set(const char *val, struct kernel_param *kp)
-{
-	printk(KERN_INFO KBUILD_MODNAME
-	       ": kernel >= 2.6.10 only uses 'ports' for conntrack modules\n");
-	return 0;
-}
-module_param_call(ports, warn_set, NULL, NULL, 0);
-
-module_init(ip_nat_ftp_init);
-module_exit(ip_nat_ftp_fini);
diff --git a/net/ipv4/netfilter/ip_nat_helper.c b/net/ipv4/netfilter/ip_nat_helper.c
deleted file mode 100644
index dc778cf..0000000
--- a/net/ipv4/netfilter/ip_nat_helper.c
+++ /dev/null
@@ -1,436 +0,0 @@
-/* ip_nat_helper.c - generic support functions for NAT helpers
- *
- * (C) 2000-2002 Harald Welte <laforge@netfilter.org>
- * (C) 2003-2004 Netfilter Core Team <coreteam@netfilter.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.
- *
- * 	14 Jan 2002 Harald Welte <laforge@gnumonks.org>:
- *		- add support for SACK adjustment
- *	14 Mar 2002 Harald Welte <laforge@gnumonks.org>:
- *		- merge SACK support into newnat API
- *	16 Aug 2002 Brian J. Murrell <netfilter@interlinx.bc.ca>:
- *		- make ip_nat_resize_packet more generic (TCP and UDP)
- *		- add ip_nat_mangle_udp_packet
- */
-#include <linux/module.h>
-#include <linux/kmod.h>
-#include <linux/types.h>
-#include <linux/timer.h>
-#include <linux/skbuff.h>
-#include <linux/netfilter_ipv4.h>
-#include <net/checksum.h>
-#include <net/icmp.h>
-#include <net/ip.h>
-#include <net/tcp.h>
-#include <net/udp.h>
-
-#include <linux/netfilter_ipv4/ip_conntrack.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-#include <linux/netfilter_ipv4/ip_nat.h>
-#include <linux/netfilter_ipv4/ip_nat_protocol.h>
-#include <linux/netfilter_ipv4/ip_nat_core.h>
-#include <linux/netfilter_ipv4/ip_nat_helper.h>
-
-#if 0
-#define DEBUGP printk
-#define DUMP_OFFSET(x)	printk("offset_before=%d, offset_after=%d, correction_pos=%u\n", x->offset_before, x->offset_after, x->correction_pos);
-#else
-#define DEBUGP(format, args...)
-#define DUMP_OFFSET(x)
-#endif
-
-static DEFINE_SPINLOCK(ip_nat_seqofs_lock);
-
-/* Setup TCP sequence correction given this change at this sequence */
-static inline void
-adjust_tcp_sequence(u32 seq,
-		    int sizediff,
-		    struct ip_conntrack *ct,
-		    enum ip_conntrack_info ctinfo)
-{
-	int dir;
-	struct ip_nat_seq *this_way, *other_way;
-
-	DEBUGP("ip_nat_resize_packet: old_size = %u, new_size = %u\n",
-		(*skb)->len, new_size);
-
-	dir = CTINFO2DIR(ctinfo);
-
-	this_way = &ct->nat.info.seq[dir];
-	other_way = &ct->nat.info.seq[!dir];
-
-	DEBUGP("ip_nat_resize_packet: Seq_offset before: ");
-	DUMP_OFFSET(this_way);
-
-	spin_lock_bh(&ip_nat_seqofs_lock);
-
-	/* SYN adjust. If it's uninitialized, or this is after last
-	 * correction, record it: we don't handle more than one
-	 * adjustment in the window, but do deal with common case of a
-	 * retransmit */
-	if (this_way->offset_before == this_way->offset_after
-	    || before(this_way->correction_pos, seq)) {
-		    this_way->correction_pos = seq;
-		    this_way->offset_before = this_way->offset_after;
-		    this_way->offset_after += sizediff;
-	}
-	spin_unlock_bh(&ip_nat_seqofs_lock);
-
-	DEBUGP("ip_nat_resize_packet: Seq_offset after: ");
-	DUMP_OFFSET(this_way);
-}
-
-/* Frobs data inside this packet, which is linear. */
-static void mangle_contents(struct sk_buff *skb,
-			    unsigned int dataoff,
-			    unsigned int match_offset,
-			    unsigned int match_len,
-			    const char *rep_buffer,
-			    unsigned int rep_len)
-{
-	unsigned char *data;
-
-	BUG_ON(skb_is_nonlinear(skb));
-	data = (unsigned char *)skb->nh.iph + dataoff;
-
-	/* move post-replacement */
-	memmove(data + match_offset + rep_len,
-		data + match_offset + match_len,
-		skb->tail - (data + match_offset + match_len));
-
-	/* insert data from buffer */
-	memcpy(data + match_offset, rep_buffer, rep_len);
-
-	/* update skb info */
-	if (rep_len > match_len) {
-		DEBUGP("ip_nat_mangle_packet: Extending packet by "
-			"%u from %u bytes\n", rep_len - match_len,
-		       skb->len);
-		skb_put(skb, rep_len - match_len);
-	} else {
-		DEBUGP("ip_nat_mangle_packet: Shrinking packet from "
-			"%u from %u bytes\n", match_len - rep_len,
-		       skb->len);
-		__skb_trim(skb, skb->len + rep_len - match_len);
-	}
-
-	/* fix IP hdr checksum information */
-	skb->nh.iph->tot_len = htons(skb->len);
-	ip_send_check(skb->nh.iph);
-}
-
-/* Unusual, but possible case. */
-static int enlarge_skb(struct sk_buff **pskb, unsigned int extra)
-{
-	struct sk_buff *nskb;
-
-	if ((*pskb)->len + extra > 65535)
-		return 0;
-
-	nskb = skb_copy_expand(*pskb, skb_headroom(*pskb), extra, GFP_ATOMIC);
-	if (!nskb)
-		return 0;
-
-	/* Transfer socket to new skb. */
-	if ((*pskb)->sk)
-		skb_set_owner_w(nskb, (*pskb)->sk);
-	kfree_skb(*pskb);
-	*pskb = nskb;
-	return 1;
-}
-
-/* Generic function for mangling variable-length address changes inside
- * NATed TCP connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX
- * command in FTP).
- *
- * Takes care about all the nasty sequence number changes, checksumming,
- * skb enlargement, ...
- *
- * */
-int
-ip_nat_mangle_tcp_packet(struct sk_buff **pskb,
-			 struct ip_conntrack *ct,
-			 enum ip_conntrack_info ctinfo,
-			 unsigned int match_offset,
-			 unsigned int match_len,
-			 const char *rep_buffer,
-			 unsigned int rep_len)
-{
-	struct iphdr *iph;
-	struct tcphdr *tcph;
-	int oldlen, datalen;
-
-	if (!skb_make_writable(pskb, (*pskb)->len))
-		return 0;
-
-	if (rep_len > match_len
-	    && rep_len - match_len > skb_tailroom(*pskb)
-	    && !enlarge_skb(pskb, rep_len - match_len))
-		return 0;
-
-	SKB_LINEAR_ASSERT(*pskb);
-
-	iph = (*pskb)->nh.iph;
-	tcph = (void *)iph + iph->ihl*4;
-
-	oldlen = (*pskb)->len - iph->ihl*4;
-	mangle_contents(*pskb, iph->ihl*4 + tcph->doff*4,
-			match_offset, match_len, rep_buffer, rep_len);
-
-	datalen = (*pskb)->len - iph->ihl*4;
-	if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) {
-		tcph->check = 0;
-		tcph->check = tcp_v4_check(datalen,
-					   iph->saddr, iph->daddr,
-					   csum_partial((char *)tcph,
-							datalen, 0));
-	} else
-		nf_proto_csum_replace2(&tcph->check, *pskb,
-					htons(oldlen), htons(datalen), 1);
-
-	if (rep_len != match_len) {
-		set_bit(IPS_SEQ_ADJUST_BIT, &ct->status);
-		adjust_tcp_sequence(ntohl(tcph->seq),
-				    (int)rep_len - (int)match_len,
-				    ct, ctinfo);
-		/* Tell TCP window tracking about seq change */
-		ip_conntrack_tcp_update(*pskb, ct, CTINFO2DIR(ctinfo));
-	}
-	return 1;
-}
-EXPORT_SYMBOL(ip_nat_mangle_tcp_packet);
-
-/* Generic function for mangling variable-length address changes inside
- * NATed UDP connections (like the CONNECT DATA XXXXX MESG XXXXX INDEX XXXXX
- * command in the Amanda protocol)
- *
- * Takes care about all the nasty sequence number changes, checksumming,
- * skb enlargement, ...
- *
- * XXX - This function could be merged with ip_nat_mangle_tcp_packet which
- *       should be fairly easy to do.
- */
-int
-ip_nat_mangle_udp_packet(struct sk_buff **pskb,
-			 struct ip_conntrack *ct,
-			 enum ip_conntrack_info ctinfo,
-			 unsigned int match_offset,
-			 unsigned int match_len,
-			 const char *rep_buffer,
-			 unsigned int rep_len)
-{
-	struct iphdr *iph;
-	struct udphdr *udph;
-	int datalen, oldlen;
-
-	/* UDP helpers might accidentally mangle the wrong packet */
-	iph = (*pskb)->nh.iph;
-	if ((*pskb)->len < iph->ihl*4 + sizeof(*udph) +
-			       match_offset + match_len)
-		return 0;
-
-	if (!skb_make_writable(pskb, (*pskb)->len))
-		return 0;
-
-	if (rep_len > match_len
-	    && rep_len - match_len > skb_tailroom(*pskb)
-	    && !enlarge_skb(pskb, rep_len - match_len))
-		return 0;
-
-	iph = (*pskb)->nh.iph;
-	udph = (void *)iph + iph->ihl*4;
-
-	oldlen = (*pskb)->len - iph->ihl*4;
-	mangle_contents(*pskb, iph->ihl*4 + sizeof(*udph),
-			match_offset, match_len, rep_buffer, rep_len);
-
-	/* update the length of the UDP packet */
-	datalen = (*pskb)->len - iph->ihl*4;
-	udph->len = htons(datalen);
-
-	/* fix udp checksum if udp checksum was previously calculated */
-	if (!udph->check && (*pskb)->ip_summed != CHECKSUM_PARTIAL)
-		return 1;
-
-	if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) {
-		udph->check = 0;
-		udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
-						datalen, IPPROTO_UDP,
-						csum_partial((char *)udph,
-							     datalen, 0));
-		if (!udph->check)
-			udph->check = CSUM_MANGLED_0;
-	} else
-		nf_proto_csum_replace2(&udph->check, *pskb,
-					htons(oldlen), htons(datalen), 1);
-	return 1;
-}
-EXPORT_SYMBOL(ip_nat_mangle_udp_packet);
-
-/* Adjust one found SACK option including checksum correction */
-static void
-sack_adjust(struct sk_buff *skb,
-	    struct tcphdr *tcph,
-	    unsigned int sackoff,
-	    unsigned int sackend,
-	    struct ip_nat_seq *natseq)
-{
-	while (sackoff < sackend) {
-		struct tcp_sack_block_wire *sack;
-		__be32 new_start_seq, new_end_seq;
-
-		sack = (void *)skb->data + sackoff;
-		if (after(ntohl(sack->start_seq) - natseq->offset_before,
-			  natseq->correction_pos))
-			new_start_seq = htonl(ntohl(sack->start_seq)
-					- natseq->offset_after);
-		else
-			new_start_seq = htonl(ntohl(sack->start_seq)
-					- natseq->offset_before);
-
-		if (after(ntohl(sack->end_seq) - natseq->offset_before,
-			  natseq->correction_pos))
-			new_end_seq = htonl(ntohl(sack->end_seq)
-				      - natseq->offset_after);
-		else
-			new_end_seq = htonl(ntohl(sack->end_seq)
-				      - natseq->offset_before);
-
-		DEBUGP("sack_adjust: start_seq: %d->%d, end_seq: %d->%d\n",
-			ntohl(sack->start_seq), new_start_seq,
-			ntohl(sack->end_seq), new_end_seq);
-
-		nf_proto_csum_replace4(&tcph->check, skb,
-					sack->start_seq, new_start_seq, 0);
-		nf_proto_csum_replace4(&tcph->check, skb,
-					sack->end_seq, new_end_seq, 0);
-		sack->start_seq = new_start_seq;
-		sack->end_seq = new_end_seq;
-		sackoff += sizeof(*sack);
-	}
-}
-
-/* TCP SACK sequence number adjustment */
-static inline unsigned int
-ip_nat_sack_adjust(struct sk_buff **pskb,
-		   struct tcphdr *tcph,
-		   struct ip_conntrack *ct,
-		   enum ip_conntrack_info ctinfo)
-{
-	unsigned int dir, optoff, optend;
-
-	optoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct tcphdr);
-	optend = (*pskb)->nh.iph->ihl*4 + tcph->doff*4;
-
-	if (!skb_make_writable(pskb, optend))
-		return 0;
-
-	dir = CTINFO2DIR(ctinfo);
-
-	while (optoff < optend) {
-		/* Usually: option, length. */
-		unsigned char *op = (*pskb)->data + optoff;
-
-		switch (op[0]) {
-		case TCPOPT_EOL:
-			return 1;
-		case TCPOPT_NOP:
-			optoff++;
-			continue;
-		default:
-			/* no partial options */
-			if (optoff + 1 == optend
-			    || optoff + op[1] > optend
-			    || op[1] < 2)
-				return 0;
-			if (op[0] == TCPOPT_SACK
-			    && op[1] >= 2+TCPOLEN_SACK_PERBLOCK
-			    && ((op[1] - 2) % TCPOLEN_SACK_PERBLOCK) == 0)
-				sack_adjust(*pskb, tcph, optoff+2,
-					    optoff+op[1],
-					    &ct->nat.info.seq[!dir]);
-			optoff += op[1];
-		}
-	}
-	return 1;
-}
-
-/* TCP sequence number adjustment.  Returns 1 on success, 0 on failure */
-int
-ip_nat_seq_adjust(struct sk_buff **pskb,
-		  struct ip_conntrack *ct,
-		  enum ip_conntrack_info ctinfo)
-{
-	struct tcphdr *tcph;
-	int dir;
-	__be32 newseq, newack;
-	struct ip_nat_seq *this_way, *other_way;
-
-	dir = CTINFO2DIR(ctinfo);
-
-	this_way = &ct->nat.info.seq[dir];
-	other_way = &ct->nat.info.seq[!dir];
-
-	if (!skb_make_writable(pskb, (*pskb)->nh.iph->ihl*4+sizeof(*tcph)))
-		return 0;
-
-	tcph = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4;
-	if (after(ntohl(tcph->seq), this_way->correction_pos))
-		newseq = htonl(ntohl(tcph->seq) + this_way->offset_after);
-	else
-		newseq = htonl(ntohl(tcph->seq) + this_way->offset_before);
-
-	if (after(ntohl(tcph->ack_seq) - other_way->offset_before,
-		  other_way->correction_pos))
-		newack = htonl(ntohl(tcph->ack_seq) - other_way->offset_after);
-	else
-		newack = htonl(ntohl(tcph->ack_seq) - other_way->offset_before);
-
-	nf_proto_csum_replace4(&tcph->check, *pskb, tcph->seq, newseq, 0);
-	nf_proto_csum_replace4(&tcph->check, *pskb, tcph->ack_seq, newack, 0);
-
-	DEBUGP("Adjusting sequence number from %u->%u, ack from %u->%u\n",
-		ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq),
-		ntohl(newack));
-
-	tcph->seq = newseq;
-	tcph->ack_seq = newack;
-
-	if (!ip_nat_sack_adjust(pskb, tcph, ct, ctinfo))
-		return 0;
-
-	ip_conntrack_tcp_update(*pskb, ct, dir);
-
-	return 1;
-}
-EXPORT_SYMBOL(ip_nat_seq_adjust);
-
-/* Setup NAT on this expected conntrack so it follows master. */
-/* If we fail to get a free NAT slot, we'll get dropped on confirm */
-void ip_nat_follow_master(struct ip_conntrack *ct,
-			  struct ip_conntrack_expect *exp)
-{
-	struct ip_nat_range range;
-
-	/* This must be a fresh one. */
-	BUG_ON(ct->status & IPS_NAT_DONE_MASK);
-
-	/* Change src to where master sends to */
-	range.flags = IP_NAT_RANGE_MAP_IPS;
-	range.min_ip = range.max_ip
-		= ct->master->tuplehash[!exp->dir].tuple.dst.ip;
-	/* hook doesn't matter, but it has to do source manip */
-	ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING);
-
-	/* For DST manip, map port here to where it's expected. */
-	range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED);
-	range.min = range.max = exp->saved_proto;
-	range.min_ip = range.max_ip
-		= ct->master->tuplehash[!exp->dir].tuple.src.ip;
-	/* hook doesn't matter, but it has to do destination manip */
-	ip_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING);
-}
-EXPORT_SYMBOL(ip_nat_follow_master);
diff --git a/net/ipv4/netfilter/ip_nat_helper_h323.c b/net/ipv4/netfilter/ip_nat_helper_h323.c
deleted file mode 100644
index bdc99ef..0000000
--- a/net/ipv4/netfilter/ip_nat_helper_h323.c
+++ /dev/null
@@ -1,611 +0,0 @@
-/*
- * H.323 extension for NAT alteration.
- *
- * Copyright (c) 2006 Jing Min Zhao <zhaojingmin@users.sourceforge.net>
- *
- * This source code is licensed under General Public License version 2.
- *
- * Based on the 'brute force' H.323 NAT module by
- * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
- */
-
-#include <linux/module.h>
-#include <linux/netfilter_ipv4.h>
-#include <linux/netfilter.h>
-#include <linux/ip.h>
-#include <linux/tcp.h>
-#include <linux/moduleparam.h>
-#include <net/tcp.h>
-#include <linux/netfilter_ipv4/ip_nat.h>
-#include <linux/netfilter_ipv4/ip_nat_helper.h>
-#include <linux/netfilter_ipv4/ip_nat_rule.h>
-#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
-#include <linux/netfilter_ipv4/ip_conntrack_h323.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-
-#if 0
-#define DEBUGP printk
-#else
-#define DEBUGP(format, args...)
-#endif
-
-/****************************************************************************/
-static int set_addr(struct sk_buff **pskb,
-		    unsigned char **data, int dataoff,
-		    unsigned int addroff, __be32 ip, u_int16_t port)
-{
-	enum ip_conntrack_info ctinfo;
-	struct ip_conntrack *ct = ip_conntrack_get(*pskb, &ctinfo);
-	struct {
-		__be32 ip;
-		__be16 port;
-	} __attribute__ ((__packed__)) buf;
-	struct tcphdr _tcph, *th;
-
-	buf.ip = ip;
-	buf.port = htons(port);
-	addroff += dataoff;
-
-	if ((*pskb)->nh.iph->protocol == IPPROTO_TCP) {
-		if (!ip_nat_mangle_tcp_packet(pskb, ct, ctinfo,
-					      addroff, sizeof(buf),
-					      (char *) &buf, sizeof(buf))) {
-			if (net_ratelimit())
-				printk("ip_nat_h323: ip_nat_mangle_tcp_packet"
-				       " error\n");
-			return -1;
-		}
-
-		/* Relocate data pointer */
-		th = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl * 4,
-					sizeof(_tcph), &_tcph);
-		if (th == NULL)
-			return -1;
-		*data = (*pskb)->data + (*pskb)->nh.iph->ihl * 4 +
-		    th->doff * 4 + dataoff;
-	} else {
-		if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
-					      addroff, sizeof(buf),
-					      (char *) &buf, sizeof(buf))) {
-			if (net_ratelimit())
-				printk("ip_nat_h323: ip_nat_mangle_udp_packet"
-				       " error\n");
-			return -1;
-		}
-		/* ip_nat_mangle_udp_packet uses skb_make_writable() to copy
-		 * or pull everything in a linear buffer, so we can safely
-		 * use the skb pointers now */
-		*data = (*pskb)->data + (*pskb)->nh.iph->ihl * 4 +
-		    sizeof(struct udphdr);
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-static int set_h225_addr(struct sk_buff **pskb,
-			 unsigned char **data, int dataoff,
-			 TransportAddress * addr,
-			 __be32 ip, u_int16_t port)
-{
-	return set_addr(pskb, data, dataoff, addr->ipAddress.ip, ip, port);
-}
-
-/****************************************************************************/
-static int set_h245_addr(struct sk_buff **pskb,
-			 unsigned char **data, int dataoff,
-			 H245_TransportAddress * addr,
-			 __be32 ip, u_int16_t port)
-{
-	return set_addr(pskb, data, dataoff,
-			addr->unicastAddress.iPAddress.network, ip, port);
-}
-
-/****************************************************************************/
-static int set_sig_addr(struct sk_buff **pskb, struct ip_conntrack *ct,
-			enum ip_conntrack_info ctinfo,
-			unsigned char **data,
-			TransportAddress * addr, int count)
-{
-	struct ip_ct_h323_master *info = &ct->help.ct_h323_info;
-	int dir = CTINFO2DIR(ctinfo);
-	int i;
-	__be32 ip;
-	u_int16_t port;
-
-	for (i = 0; i < count; i++) {
-		if (get_h225_addr(*data, &addr[i], &ip, &port)) {
-			if (ip == ct->tuplehash[dir].tuple.src.ip &&
-			    port == info->sig_port[dir]) {
-				/* GW->GK */
-
-				/* Fix for Gnomemeeting */
-				if (i > 0 &&
-				    get_h225_addr(*data, &addr[0],
-						  &ip, &port) &&
-				    (ntohl(ip) & 0xff000000) == 0x7f000000)
-					i = 0;
-
-				DEBUGP
-				    ("ip_nat_ras: set signal address "
-				     "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
-				     NIPQUAD(ip), port,
-				     NIPQUAD(ct->tuplehash[!dir].tuple.dst.
-					     ip), info->sig_port[!dir]);
-				return set_h225_addr(pskb, data, 0, &addr[i],
-						     ct->tuplehash[!dir].
-						     tuple.dst.ip,
-						     info->sig_port[!dir]);
-			} else if (ip == ct->tuplehash[dir].tuple.dst.ip &&
-				   port == info->sig_port[dir]) {
-				/* GK->GW */
-				DEBUGP
-				    ("ip_nat_ras: set signal address "
-				     "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
-				     NIPQUAD(ip), port,
-				     NIPQUAD(ct->tuplehash[!dir].tuple.src.
-					     ip), info->sig_port[!dir]);
-				return set_h225_addr(pskb, data, 0, &addr[i],
-						     ct->tuplehash[!dir].
-						     tuple.src.ip,
-						     info->sig_port[!dir]);
-			}
-		}
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-static int set_ras_addr(struct sk_buff **pskb, struct ip_conntrack *ct,
-			enum ip_conntrack_info ctinfo,
-			unsigned char **data,
-			TransportAddress * addr, int count)
-{
-	int dir = CTINFO2DIR(ctinfo);
-	int i;
-	__be32 ip;
-	u_int16_t port;
-
-	for (i = 0; i < count; i++) {
-		if (get_h225_addr(*data, &addr[i], &ip, &port) &&
-		    ip == ct->tuplehash[dir].tuple.src.ip &&
-		    port == ntohs(ct->tuplehash[dir].tuple.src.u.udp.port)) {
-			DEBUGP("ip_nat_ras: set rasAddress "
-			       "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
-			       NIPQUAD(ip), port,
-			       NIPQUAD(ct->tuplehash[!dir].tuple.dst.ip),
-			       ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.
-				     port));
-			return set_h225_addr(pskb, data, 0, &addr[i],
-					     ct->tuplehash[!dir].tuple.dst.ip,
-					     ntohs(ct->tuplehash[!dir].tuple.
-						   dst.u.udp.port));
-		}
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-static int nat_rtp_rtcp(struct sk_buff **pskb, struct ip_conntrack *ct,
-			enum ip_conntrack_info ctinfo,
-			unsigned char **data, int dataoff,
-			H245_TransportAddress * addr,
-			u_int16_t port, u_int16_t rtp_port,
-			struct ip_conntrack_expect *rtp_exp,
-			struct ip_conntrack_expect *rtcp_exp)
-{
-	struct ip_ct_h323_master *info = &ct->help.ct_h323_info;
-	int dir = CTINFO2DIR(ctinfo);
-	int i;
-	u_int16_t nated_port;
-
-	/* Set expectations for NAT */
-	rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port;
-	rtp_exp->expectfn = ip_nat_follow_master;
-	rtp_exp->dir = !dir;
-	rtcp_exp->saved_proto.udp.port = rtcp_exp->tuple.dst.u.udp.port;
-	rtcp_exp->expectfn = ip_nat_follow_master;
-	rtcp_exp->dir = !dir;
-
-	/* Lookup existing expects */
-	for (i = 0; i < H323_RTP_CHANNEL_MAX; i++) {
-		if (info->rtp_port[i][dir] == rtp_port) {
-			/* Expected */
-
-			/* Use allocated ports first. This will refresh
-			 * the expects */
-			rtp_exp->tuple.dst.u.udp.port =
-			    htons(info->rtp_port[i][dir]);
-			rtcp_exp->tuple.dst.u.udp.port =
-			    htons(info->rtp_port[i][dir] + 1);
-			break;
-		} else if (info->rtp_port[i][dir] == 0) {
-			/* Not expected */
-			break;
-		}
-	}
-
-	/* Run out of expectations */
-	if (i >= H323_RTP_CHANNEL_MAX) {
-		if (net_ratelimit())
-			printk("ip_nat_h323: out of expectations\n");
-		return 0;
-	}
-
-	/* Try to get a pair of ports. */
-	for (nated_port = ntohs(rtp_exp->tuple.dst.u.udp.port);
-	     nated_port != 0; nated_port += 2) {
-		rtp_exp->tuple.dst.u.udp.port = htons(nated_port);
-		if (ip_conntrack_expect_related(rtp_exp) == 0) {
-			rtcp_exp->tuple.dst.u.udp.port =
-			    htons(nated_port + 1);
-			if (ip_conntrack_expect_related(rtcp_exp) == 0)
-				break;
-			ip_conntrack_unexpect_related(rtp_exp);
-		}
-	}
-
-	if (nated_port == 0) {	/* No port available */
-		if (net_ratelimit())
-			printk("ip_nat_h323: out of RTP ports\n");
-		return 0;
-	}
-
-	/* Modify signal */
-	if (set_h245_addr(pskb, data, dataoff, addr,
-			  ct->tuplehash[!dir].tuple.dst.ip,
-			  (port & 1) ? nated_port + 1 : nated_port) == 0) {
-		/* Save ports */
-		info->rtp_port[i][dir] = rtp_port;
-		info->rtp_port[i][!dir] = nated_port;
-	} else {
-		ip_conntrack_unexpect_related(rtp_exp);
-		ip_conntrack_unexpect_related(rtcp_exp);
-		return -1;
-	}
-
-	/* Success */
-	DEBUGP("ip_nat_h323: expect RTP %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
-	       NIPQUAD(rtp_exp->tuple.src.ip),
-	       ntohs(rtp_exp->tuple.src.u.udp.port),
-	       NIPQUAD(rtp_exp->tuple.dst.ip),
-	       ntohs(rtp_exp->tuple.dst.u.udp.port));
-	DEBUGP("ip_nat_h323: expect RTCP %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
-	       NIPQUAD(rtcp_exp->tuple.src.ip),
-	       ntohs(rtcp_exp->tuple.src.u.udp.port),
-	       NIPQUAD(rtcp_exp->tuple.dst.ip),
-	       ntohs(rtcp_exp->tuple.dst.u.udp.port));
-
-	return 0;
-}
-
-/****************************************************************************/
-static int nat_t120(struct sk_buff **pskb, struct ip_conntrack *ct,
-		    enum ip_conntrack_info ctinfo,
-		    unsigned char **data, int dataoff,
-		    H245_TransportAddress * addr, u_int16_t port,
-		    struct ip_conntrack_expect *exp)
-{
-	int dir = CTINFO2DIR(ctinfo);
-	u_int16_t nated_port = port;
-
-	/* Set expectations for NAT */
-	exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
-	exp->expectfn = ip_nat_follow_master;
-	exp->dir = !dir;
-
-	/* Try to get same port: if not, try to change it. */
-	for (; nated_port != 0; nated_port++) {
-		exp->tuple.dst.u.tcp.port = htons(nated_port);
-		if (ip_conntrack_expect_related(exp) == 0)
-			break;
-	}
-
-	if (nated_port == 0) {	/* No port available */
-		if (net_ratelimit())
-			printk("ip_nat_h323: out of TCP ports\n");
-		return 0;
-	}
-
-	/* Modify signal */
-	if (set_h245_addr(pskb, data, dataoff, addr,
-			  ct->tuplehash[!dir].tuple.dst.ip, nated_port) < 0) {
-		ip_conntrack_unexpect_related(exp);
-		return -1;
-	}
-
-	DEBUGP("ip_nat_h323: expect T.120 %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
-	       NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port),
-	       NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port));
-
-	return 0;
-}
-
-/****************************************************************************
- * This conntrack expect function replaces ip_conntrack_h245_expect()
- * which was set by ip_conntrack_helper_h323.c. It calls both
- * ip_nat_follow_master() and ip_conntrack_h245_expect()
- ****************************************************************************/
-static void ip_nat_h245_expect(struct ip_conntrack *new,
-			       struct ip_conntrack_expect *this)
-{
-	ip_nat_follow_master(new, this);
-	ip_conntrack_h245_expect(new, this);
-}
-
-/****************************************************************************/
-static int nat_h245(struct sk_buff **pskb, struct ip_conntrack *ct,
-		    enum ip_conntrack_info ctinfo,
-		    unsigned char **data, int dataoff,
-		    TransportAddress * addr, u_int16_t port,
-		    struct ip_conntrack_expect *exp)
-{
-	struct ip_ct_h323_master *info = &ct->help.ct_h323_info;
-	int dir = CTINFO2DIR(ctinfo);
-	u_int16_t nated_port = port;
-
-	/* Set expectations for NAT */
-	exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
-	exp->expectfn = ip_nat_h245_expect;
-	exp->dir = !dir;
-
-	/* Check existing expects */
-	if (info->sig_port[dir] == port)
-		nated_port = info->sig_port[!dir];
-
-	/* Try to get same port: if not, try to change it. */
-	for (; nated_port != 0; nated_port++) {
-		exp->tuple.dst.u.tcp.port = htons(nated_port);
-		if (ip_conntrack_expect_related(exp) == 0)
-			break;
-	}
-
-	if (nated_port == 0) {	/* No port available */
-		if (net_ratelimit())
-			printk("ip_nat_q931: out of TCP ports\n");
-		return 0;
-	}
-
-	/* Modify signal */
-	if (set_h225_addr(pskb, data, dataoff, addr,
-			  ct->tuplehash[!dir].tuple.dst.ip,
-			  nated_port) == 0) {
-		/* Save ports */
-		info->sig_port[dir] = port;
-		info->sig_port[!dir] = nated_port;
-	} else {
-		ip_conntrack_unexpect_related(exp);
-		return -1;
-	}
-
-	DEBUGP("ip_nat_q931: expect H.245 %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
-	       NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port),
-	       NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port));
-
-	return 0;
-}
-
-/****************************************************************************
- * This conntrack expect function replaces ip_conntrack_q931_expect()
- * which was set by ip_conntrack_helper_h323.c.
- ****************************************************************************/
-static void ip_nat_q931_expect(struct ip_conntrack *new,
-			       struct ip_conntrack_expect *this)
-{
-	struct ip_nat_range range;
-
-	if (this->tuple.src.ip != 0) {	/* Only accept calls from GK */
-		ip_nat_follow_master(new, this);
-		goto out;
-	}
-
-	/* This must be a fresh one. */
-	BUG_ON(new->status & IPS_NAT_DONE_MASK);
-
-	/* Change src to where master sends to */
-	range.flags = IP_NAT_RANGE_MAP_IPS;
-	range.min_ip = range.max_ip = new->tuplehash[!this->dir].tuple.src.ip;
-
-	/* hook doesn't matter, but it has to do source manip */
-	ip_nat_setup_info(new, &range, NF_IP_POST_ROUTING);
-
-	/* For DST manip, map port here to where it's expected. */
-	range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED);
-	range.min = range.max = this->saved_proto;
-	range.min_ip = range.max_ip =
-	    new->master->tuplehash[!this->dir].tuple.src.ip;
-
-	/* hook doesn't matter, but it has to do destination manip */
-	ip_nat_setup_info(new, &range, NF_IP_PRE_ROUTING);
-
-      out:
-	ip_conntrack_q931_expect(new, this);
-}
-
-/****************************************************************************/
-static int nat_q931(struct sk_buff **pskb, struct ip_conntrack *ct,
-		    enum ip_conntrack_info ctinfo,
-		    unsigned char **data, TransportAddress * addr, int idx,
-		    u_int16_t port, struct ip_conntrack_expect *exp)
-{
-	struct ip_ct_h323_master *info = &ct->help.ct_h323_info;
-	int dir = CTINFO2DIR(ctinfo);
-	u_int16_t nated_port = port;
-	__be32 ip;
-
-	/* Set expectations for NAT */
-	exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
-	exp->expectfn = ip_nat_q931_expect;
-	exp->dir = !dir;
-
-	/* Check existing expects */
-	if (info->sig_port[dir] == port)
-		nated_port = info->sig_port[!dir];
-
-	/* Try to get same port: if not, try to change it. */
-	for (; nated_port != 0; nated_port++) {
-		exp->tuple.dst.u.tcp.port = htons(nated_port);
-		if (ip_conntrack_expect_related(exp) == 0)
-			break;
-	}
-
-	if (nated_port == 0) {	/* No port available */
-		if (net_ratelimit())
-			printk("ip_nat_ras: out of TCP ports\n");
-		return 0;
-	}
-
-	/* Modify signal */
-	if (set_h225_addr(pskb, data, 0, &addr[idx],
-			  ct->tuplehash[!dir].tuple.dst.ip,
-			  nated_port) == 0) {
-		/* Save ports */
-		info->sig_port[dir] = port;
-		info->sig_port[!dir] = nated_port;
-
-		/* Fix for Gnomemeeting */
-		if (idx > 0 &&
-		    get_h225_addr(*data, &addr[0], &ip, &port) &&
-		    (ntohl(ip) & 0xff000000) == 0x7f000000) {
-			set_h225_addr_hook(pskb, data, 0, &addr[0],
-					   ct->tuplehash[!dir].tuple.dst.ip,
-					   info->sig_port[!dir]);
-		}
-	} else {
-		ip_conntrack_unexpect_related(exp);
-		return -1;
-	}
-
-	/* Success */
-	DEBUGP("ip_nat_ras: expect Q.931 %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
-	       NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port),
-	       NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port));
-
-	return 0;
-}
-
-/****************************************************************************/
-static void ip_nat_callforwarding_expect(struct ip_conntrack *new,
-					 struct ip_conntrack_expect *this)
-{
-	struct ip_nat_range range;
-
-	/* This must be a fresh one. */
-	BUG_ON(new->status & IPS_NAT_DONE_MASK);
-
-	/* Change src to where master sends to */
-	range.flags = IP_NAT_RANGE_MAP_IPS;
-	range.min_ip = range.max_ip = new->tuplehash[!this->dir].tuple.src.ip;
-
-	/* hook doesn't matter, but it has to do source manip */
-	ip_nat_setup_info(new, &range, NF_IP_POST_ROUTING);
-
-	/* For DST manip, map port here to where it's expected. */
-	range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED);
-	range.min = range.max = this->saved_proto;
-	range.min_ip = range.max_ip = this->saved_ip;
-
-	/* hook doesn't matter, but it has to do destination manip */
-	ip_nat_setup_info(new, &range, NF_IP_PRE_ROUTING);
-
-	ip_conntrack_q931_expect(new, this);
-}
-
-/****************************************************************************/
-static int nat_callforwarding(struct sk_buff **pskb, struct ip_conntrack *ct,
-			      enum ip_conntrack_info ctinfo,
-			      unsigned char **data, int dataoff,
-			      TransportAddress * addr, u_int16_t port,
-			      struct ip_conntrack_expect *exp)
-{
-	int dir = CTINFO2DIR(ctinfo);
-	u_int16_t nated_port;
-
-	/* Set expectations for NAT */
-	exp->saved_ip = exp->tuple.dst.ip;
-	exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip;
-	exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
-	exp->expectfn = ip_nat_callforwarding_expect;
-	exp->dir = !dir;
-
-	/* Try to get same port: if not, try to change it. */
-	for (nated_port = port; nated_port != 0; nated_port++) {
-		exp->tuple.dst.u.tcp.port = htons(nated_port);
-		if (ip_conntrack_expect_related(exp) == 0)
-			break;
-	}
-
-	if (nated_port == 0) {	/* No port available */
-		if (net_ratelimit())
-			printk("ip_nat_q931: out of TCP ports\n");
-		return 0;
-	}
-
-	/* Modify signal */
-	if (!set_h225_addr(pskb, data, dataoff, addr,
-			   ct->tuplehash[!dir].tuple.dst.ip,
-			   nated_port) == 0) {
-		ip_conntrack_unexpect_related(exp);
-		return -1;
-	}
-
-	/* Success */
-	DEBUGP("ip_nat_q931: expect Call Forwarding "
-	       "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
-	       NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port),
-	       NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port));
-
-	return 0;
-}
-
-/****************************************************************************/
-static int __init init(void)
-{
-	BUG_ON(rcu_dereference(set_h245_addr_hook) != NULL);
-	BUG_ON(rcu_dereference(set_h225_addr_hook) != NULL);
-	BUG_ON(rcu_dereference(set_sig_addr_hook) != NULL);
-	BUG_ON(rcu_dereference(set_ras_addr_hook) != NULL);
-	BUG_ON(rcu_dereference(nat_rtp_rtcp_hook) != NULL);
-	BUG_ON(rcu_dereference(nat_t120_hook) != NULL);
-	BUG_ON(rcu_dereference(nat_h245_hook) != NULL);
-	BUG_ON(rcu_dereference(nat_callforwarding_hook) != NULL);
-	BUG_ON(rcu_dereference(nat_q931_hook) != NULL);
-
-	rcu_assign_pointer(set_h245_addr_hook, set_h245_addr);
-	rcu_assign_pointer(set_h225_addr_hook, set_h225_addr);
-	rcu_assign_pointer(set_sig_addr_hook, set_sig_addr);
-	rcu_assign_pointer(set_ras_addr_hook, set_ras_addr);
-	rcu_assign_pointer(nat_rtp_rtcp_hook, nat_rtp_rtcp);
-	rcu_assign_pointer(nat_t120_hook, nat_t120);
-	rcu_assign_pointer(nat_h245_hook, nat_h245);
-	rcu_assign_pointer(nat_callforwarding_hook, nat_callforwarding);
-	rcu_assign_pointer(nat_q931_hook, nat_q931);
-
-	DEBUGP("ip_nat_h323: init success\n");
-	return 0;
-}
-
-/****************************************************************************/
-static void __exit fini(void)
-{
-	rcu_assign_pointer(set_h245_addr_hook, NULL);
-	rcu_assign_pointer(set_h225_addr_hook, NULL);
-	rcu_assign_pointer(set_sig_addr_hook, NULL);
-	rcu_assign_pointer(set_ras_addr_hook, NULL);
-	rcu_assign_pointer(nat_rtp_rtcp_hook, NULL);
-	rcu_assign_pointer(nat_t120_hook, NULL);
-	rcu_assign_pointer(nat_h245_hook, NULL);
-	rcu_assign_pointer(nat_callforwarding_hook, NULL);
-	rcu_assign_pointer(nat_q931_hook, NULL);
-	synchronize_rcu();
-}
-
-/****************************************************************************/
-module_init(init);
-module_exit(fini);
-
-MODULE_AUTHOR("Jing Min Zhao <zhaojingmin@users.sourceforge.net>");
-MODULE_DESCRIPTION("H.323 NAT helper");
-MODULE_LICENSE("GPL");
diff --git a/net/ipv4/netfilter/ip_nat_helper_pptp.c b/net/ipv4/netfilter/ip_nat_helper_pptp.c
deleted file mode 100644
index 24ce4a5..0000000
--- a/net/ipv4/netfilter/ip_nat_helper_pptp.c
+++ /dev/null
@@ -1,350 +0,0 @@
-/*
- * ip_nat_pptp.c	- Version 3.0
- *
- * NAT support for PPTP (Point to Point Tunneling Protocol).
- * PPTP is a a protocol for creating virtual private networks.
- * It is a specification defined by Microsoft and some vendors
- * working with Microsoft.  PPTP is built on top of a modified
- * version of the Internet Generic Routing Encapsulation Protocol.
- * GRE is defined in RFC 1701 and RFC 1702.  Documentation of
- * PPTP can be found in RFC 2637
- *
- * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org>
- *
- * Development of this code funded by Astaro AG (http://www.astaro.com/)
- *
- * TODO: - NAT to a unique tuple, not to TCP source port
- * 	   (needs netfilter tuple reservation)
- *
- * Changes:
- *     2002-02-10 - Version 1.3
- *       - Use ip_nat_mangle_tcp_packet() because of cloned skb's
- *	   in local connections (Philip Craig <philipc@snapgear.com>)
- *       - add checks for magicCookie and pptp version
- *       - make argument list of pptp_{out,in}bound_packet() shorter
- *       - move to C99 style initializers
- *       - print version number at module loadtime
- *     2003-09-22 - Version 1.5
- *       - use SNATed tcp sourceport as callid, since we get called before
- *	   TCP header is mangled (Philip Craig <philipc@snapgear.com>)
- *     2004-10-22 - Version 2.0
- *       - kernel 2.6.x version
- *     2005-06-10 - Version 3.0
- *       - kernel >= 2.6.11 version,
- *	   funded by Oxcoda NetBox Blue (http://www.netboxblue.com/)
- *
- */
-
-#include <linux/module.h>
-#include <linux/ip.h>
-#include <linux/tcp.h>
-#include <net/tcp.h>
-
-#include <linux/netfilter_ipv4/ip_nat.h>
-#include <linux/netfilter_ipv4/ip_nat_rule.h>
-#include <linux/netfilter_ipv4/ip_nat_helper.h>
-#include <linux/netfilter_ipv4/ip_nat_pptp.h>
-#include <linux/netfilter_ipv4/ip_conntrack_core.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
-#include <linux/netfilter_ipv4/ip_conntrack_pptp.h>
-
-#define IP_NAT_PPTP_VERSION "3.0"
-
-#define REQ_CID(req, off)		(*(__be16 *)((char *)(req) + (off)))
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
-MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP");
-
-
-#if 0
-extern const char *pptp_msg_name[];
-#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, \
-				       __FUNCTION__, ## args)
-#else
-#define DEBUGP(format, args...)
-#endif
-
-static void pptp_nat_expected(struct ip_conntrack *ct,
-			      struct ip_conntrack_expect *exp)
-{
-	struct ip_conntrack *master = ct->master;
-	struct ip_conntrack_expect *other_exp;
-	struct ip_conntrack_tuple t;
-	struct ip_ct_pptp_master *ct_pptp_info;
-	struct ip_nat_pptp *nat_pptp_info;
-	struct ip_nat_range range;
-
-	ct_pptp_info = &master->help.ct_pptp_info;
-	nat_pptp_info = &master->nat.help.nat_pptp_info;
-
-	/* And here goes the grand finale of corrosion... */
-
-	if (exp->dir == IP_CT_DIR_ORIGINAL) {
-		DEBUGP("we are PNS->PAC\n");
-		/* therefore, build tuple for PAC->PNS */
-		t.src.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
-		t.src.u.gre.key = master->help.ct_pptp_info.pac_call_id;
-		t.dst.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
-		t.dst.u.gre.key = master->help.ct_pptp_info.pns_call_id;
-		t.dst.protonum = IPPROTO_GRE;
-	} else {
-		DEBUGP("we are PAC->PNS\n");
-		/* build tuple for PNS->PAC */
-		t.src.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
-		t.src.u.gre.key = master->nat.help.nat_pptp_info.pns_call_id;
-		t.dst.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
-		t.dst.u.gre.key = master->nat.help.nat_pptp_info.pac_call_id;
-		t.dst.protonum = IPPROTO_GRE;
-	}
-
-	DEBUGP("trying to unexpect other dir: ");
-	DUMP_TUPLE(&t);
-	other_exp = ip_conntrack_expect_find_get(&t);
-	if (other_exp) {
-		ip_conntrack_unexpect_related(other_exp);
-		ip_conntrack_expect_put(other_exp);
-		DEBUGP("success\n");
-	} else {
-		DEBUGP("not found!\n");
-	}
-
-	/* This must be a fresh one. */
-	BUG_ON(ct->status & IPS_NAT_DONE_MASK);
-
-	/* Change src to where master sends to */
-	range.flags = IP_NAT_RANGE_MAP_IPS;
-	range.min_ip = range.max_ip
-		= ct->master->tuplehash[!exp->dir].tuple.dst.ip;
-	if (exp->dir == IP_CT_DIR_ORIGINAL) {
-		range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
-		range.min = range.max = exp->saved_proto;
-	}
-	/* hook doesn't matter, but it has to do source manip */
-	ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING);
-
-	/* For DST manip, map port here to where it's expected. */
-	range.flags = IP_NAT_RANGE_MAP_IPS;
-	range.min_ip = range.max_ip
-		= ct->master->tuplehash[!exp->dir].tuple.src.ip;
-	if (exp->dir == IP_CT_DIR_REPLY) {
-		range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
-		range.min = range.max = exp->saved_proto;
-	}
-	/* hook doesn't matter, but it has to do destination manip */
-	ip_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING);
-}
-
-/* outbound packets == from PNS to PAC */
-static int
-pptp_outbound_pkt(struct sk_buff **pskb,
-		  struct ip_conntrack *ct,
-		  enum ip_conntrack_info ctinfo,
-		  struct PptpControlHeader *ctlh,
-		  union pptp_ctrl_union *pptpReq)
-
-{
-	struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info;
-	struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info;
-	u_int16_t msg;
-	__be16 new_callid;
-	unsigned int cid_off;
-
-	new_callid = ct_pptp_info->pns_call_id;
-
-	switch (msg = ntohs(ctlh->messageType)) {
-	case PPTP_OUT_CALL_REQUEST:
-		cid_off = offsetof(union pptp_ctrl_union, ocreq.callID);
-		/* FIXME: ideally we would want to reserve a call ID
-		 * here.  current netfilter NAT core is not able to do
-		 * this :( For now we use TCP source port. This breaks
-		 * multiple calls within one control session */
-
-		/* save original call ID in nat_info */
-		nat_pptp_info->pns_call_id = ct_pptp_info->pns_call_id;
-
-		/* don't use tcph->source since we are at a DSTmanip
-		 * hook (e.g. PREROUTING) and pkt is not mangled yet */
-		new_callid = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.tcp.port;
-
-		/* save new call ID in ct info */
-		ct_pptp_info->pns_call_id = new_callid;
-		break;
-	case PPTP_IN_CALL_REPLY:
-		cid_off = offsetof(union pptp_ctrl_union, icack.callID);
-		break;
-	case PPTP_CALL_CLEAR_REQUEST:
-		cid_off = offsetof(union pptp_ctrl_union, clrreq.callID);
-		break;
-	default:
-		DEBUGP("unknown outbound packet 0x%04x:%s\n", msg,
-		      (msg <= PPTP_MSG_MAX)?
-		      pptp_msg_name[msg]:pptp_msg_name[0]);
-		/* fall through */
-
-	case PPTP_SET_LINK_INFO:
-		/* only need to NAT in case PAC is behind NAT box */
-	case PPTP_START_SESSION_REQUEST:
-	case PPTP_START_SESSION_REPLY:
-	case PPTP_STOP_SESSION_REQUEST:
-	case PPTP_STOP_SESSION_REPLY:
-	case PPTP_ECHO_REQUEST:
-	case PPTP_ECHO_REPLY:
-		/* no need to alter packet */
-		return NF_ACCEPT;
-	}
-
-	/* only OUT_CALL_REQUEST, IN_CALL_REPLY, CALL_CLEAR_REQUEST pass
-	 * down to here */
-	DEBUGP("altering call id from 0x%04x to 0x%04x\n",
-		ntohs(REQ_CID(pptpReq, cid_off)), ntohs(new_callid));
-
-	/* mangle packet */
-	if (ip_nat_mangle_tcp_packet(pskb, ct, ctinfo,
-				     cid_off + sizeof(struct pptp_pkt_hdr) +
-				     sizeof(struct PptpControlHeader),
-				     sizeof(new_callid), (char *)&new_callid,
-				     sizeof(new_callid)) == 0)
-		return NF_DROP;
-
-	return NF_ACCEPT;
-}
-
-static void
-pptp_exp_gre(struct ip_conntrack_expect *expect_orig,
-	     struct ip_conntrack_expect *expect_reply)
-{
-	struct ip_conntrack *ct = expect_orig->master;
-	struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info;
-	struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info;
-
-	/* save original PAC call ID in nat_info */
-	nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id;
-
-	/* alter expectation for PNS->PAC direction */
-	expect_orig->saved_proto.gre.key = ct_pptp_info->pns_call_id;
-	expect_orig->tuple.src.u.gre.key = nat_pptp_info->pns_call_id;
-	expect_orig->tuple.dst.u.gre.key = ct_pptp_info->pac_call_id;
-	expect_orig->dir = IP_CT_DIR_ORIGINAL;
-
-	/* alter expectation for PAC->PNS direction */
-	expect_reply->saved_proto.gre.key = nat_pptp_info->pns_call_id;
-	expect_reply->tuple.src.u.gre.key = nat_pptp_info->pac_call_id;
-	expect_reply->tuple.dst.u.gre.key = ct_pptp_info->pns_call_id;
-	expect_reply->dir = IP_CT_DIR_REPLY;
-}
-
-/* inbound packets == from PAC to PNS */
-static int
-pptp_inbound_pkt(struct sk_buff **pskb,
-		 struct ip_conntrack *ct,
-		 enum ip_conntrack_info ctinfo,
-		 struct PptpControlHeader *ctlh,
-		 union pptp_ctrl_union *pptpReq)
-{
-	struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info;
-	u_int16_t msg;
-	__be16 new_pcid;
-	unsigned int pcid_off;
-
-	new_pcid = nat_pptp_info->pns_call_id;
-
-	switch (msg = ntohs(ctlh->messageType)) {
-	case PPTP_OUT_CALL_REPLY:
-		pcid_off = offsetof(union pptp_ctrl_union, ocack.peersCallID);
-		break;
-	case PPTP_IN_CALL_CONNECT:
-		pcid_off = offsetof(union pptp_ctrl_union, iccon.peersCallID);
-		break;
-	case PPTP_IN_CALL_REQUEST:
-		/* only need to nat in case PAC is behind NAT box */
-		return NF_ACCEPT;
-	case PPTP_WAN_ERROR_NOTIFY:
-		pcid_off = offsetof(union pptp_ctrl_union, wanerr.peersCallID);
-		break;
-	case PPTP_CALL_DISCONNECT_NOTIFY:
-		pcid_off = offsetof(union pptp_ctrl_union, disc.callID);
-		break;
-	case PPTP_SET_LINK_INFO:
-		pcid_off = offsetof(union pptp_ctrl_union, setlink.peersCallID);
-		break;
-
-	default:
-		DEBUGP("unknown inbound packet %s\n", (msg <= PPTP_MSG_MAX)?
-			pptp_msg_name[msg]:pptp_msg_name[0]);
-		/* fall through */
-
-	case PPTP_START_SESSION_REQUEST:
-	case PPTP_START_SESSION_REPLY:
-	case PPTP_STOP_SESSION_REQUEST:
-	case PPTP_STOP_SESSION_REPLY:
-	case PPTP_ECHO_REQUEST:
-	case PPTP_ECHO_REPLY:
-		/* no need to alter packet */
-		return NF_ACCEPT;
-	}
-
-	/* only OUT_CALL_REPLY, IN_CALL_CONNECT, IN_CALL_REQUEST,
-	 * WAN_ERROR_NOTIFY, CALL_DISCONNECT_NOTIFY pass down here */
-
-	/* mangle packet */
-	DEBUGP("altering peer call id from 0x%04x to 0x%04x\n",
-		ntohs(REQ_CID(pptpReq, pcid_off)), ntohs(new_pcid));
-
-	if (ip_nat_mangle_tcp_packet(pskb, ct, ctinfo,
-				     pcid_off + sizeof(struct pptp_pkt_hdr) +
-				     sizeof(struct PptpControlHeader),
-				     sizeof(new_pcid), (char *)&new_pcid,
-				     sizeof(new_pcid)) == 0)
-		return NF_DROP;
-	return NF_ACCEPT;
-}
-
-
-extern int __init ip_nat_proto_gre_init(void);
-extern void __exit ip_nat_proto_gre_fini(void);
-
-static int __init ip_nat_helper_pptp_init(void)
-{
-	int ret;
-
-	DEBUGP("%s: registering NAT helper\n", __FILE__);
-
-	ret = ip_nat_proto_gre_init();
-	if (ret < 0)
-		return ret;
-
-	BUG_ON(rcu_dereference(ip_nat_pptp_hook_outbound));
-	rcu_assign_pointer(ip_nat_pptp_hook_outbound, pptp_outbound_pkt);
-
-	BUG_ON(rcu_dereference(ip_nat_pptp_hook_inbound));
-	rcu_assign_pointer(ip_nat_pptp_hook_inbound, pptp_inbound_pkt);
-
-	BUG_ON(rcu_dereference(ip_nat_pptp_hook_exp_gre));
-	rcu_assign_pointer(ip_nat_pptp_hook_exp_gre, pptp_exp_gre);
-
-	BUG_ON(rcu_dereference(ip_nat_pptp_hook_expectfn));
-	rcu_assign_pointer(ip_nat_pptp_hook_expectfn, pptp_nat_expected);
-
-	printk("ip_nat_pptp version %s loaded\n", IP_NAT_PPTP_VERSION);
-	return 0;
-}
-
-static void __exit ip_nat_helper_pptp_fini(void)
-{
-	DEBUGP("cleanup_module\n" );
-
-	rcu_assign_pointer(ip_nat_pptp_hook_expectfn, NULL);
-	rcu_assign_pointer(ip_nat_pptp_hook_exp_gre, NULL);
-	rcu_assign_pointer(ip_nat_pptp_hook_inbound, NULL);
-	rcu_assign_pointer(ip_nat_pptp_hook_outbound, NULL);
-	synchronize_rcu();
-
-	ip_nat_proto_gre_fini();
-
-	printk("ip_nat_pptp version %s unloaded\n", IP_NAT_PPTP_VERSION);
-}
-
-module_init(ip_nat_helper_pptp_init);
-module_exit(ip_nat_helper_pptp_fini);
diff --git a/net/ipv4/netfilter/ip_nat_irc.c b/net/ipv4/netfilter/ip_nat_irc.c
deleted file mode 100644
index cfaeea3..0000000
--- a/net/ipv4/netfilter/ip_nat_irc.c
+++ /dev/null
@@ -1,122 +0,0 @@
-/* IRC extension for TCP NAT alteration.
- * (C) 2000-2001 by Harald Welte <laforge@gnumonks.org>
- * (C) 2004 Rusty Russell <rusty@rustcorp.com.au> IBM Corporation
- * based on a copy of RR's ip_nat_ftp.c
- *
- * ip_nat_irc.c,v 1.16 2001/12/06 07:42:10 laforge Exp
- *
- *      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.
- */
-
-#include <linux/module.h>
-#include <linux/netfilter_ipv4.h>
-#include <linux/ip.h>
-#include <linux/tcp.h>
-#include <linux/kernel.h>
-#include <net/tcp.h>
-#include <linux/netfilter_ipv4/ip_nat.h>
-#include <linux/netfilter_ipv4/ip_nat_helper.h>
-#include <linux/netfilter_ipv4/ip_nat_rule.h>
-#include <linux/netfilter_ipv4/ip_conntrack_irc.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-#include <linux/moduleparam.h>
-
-#if 0
-#define DEBUGP printk
-#else
-#define DEBUGP(format, args...)
-#endif
-
-MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
-MODULE_DESCRIPTION("IRC (DCC) NAT helper");
-MODULE_LICENSE("GPL");
-
-static unsigned int help(struct sk_buff **pskb,
-			 enum ip_conntrack_info ctinfo,
-			 unsigned int matchoff,
-			 unsigned int matchlen,
-			 struct ip_conntrack_expect *exp)
-{
-	u_int16_t port;
-	unsigned int ret;
-
-	/* "4294967296 65635 " */
-	char buffer[18];
-
-	DEBUGP("IRC_NAT: info (seq %u + %u) in %u\n",
-	       expect->seq, exp_irc_info->len,
-	       ntohl(tcph->seq));
-
-	/* Reply comes from server. */
-	exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
-	exp->dir = IP_CT_DIR_REPLY;
-
-	/* When you see the packet, we need to NAT it the same as the
-	 * this one. */
-	exp->expectfn = ip_nat_follow_master;
-
-	/* Try to get same port: if not, try to change it. */
-	for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) {
-		exp->tuple.dst.u.tcp.port = htons(port);
-		if (ip_conntrack_expect_related(exp) == 0)
-			break;
-	}
-
-	if (port == 0)
-		return NF_DROP;
-
-	/*      strlen("\1DCC CHAT chat AAAAAAAA P\1\n")=27
-	 *      strlen("\1DCC SCHAT chat AAAAAAAA P\1\n")=28
-	 *      strlen("\1DCC SEND F AAAAAAAA P S\1\n")=26
-	 *      strlen("\1DCC MOVE F AAAAAAAA P S\1\n")=26
-	 *      strlen("\1DCC TSEND F AAAAAAAA P S\1\n")=27
-	 *              AAAAAAAAA: bound addr (1.0.0.0==16777216, min 8 digits,
-	 *                      255.255.255.255==4294967296, 10 digits)
-	 *              P:         bound port (min 1 d, max 5d (65635))
-	 *              F:         filename   (min 1 d )
-	 *              S:         size       (min 1 d )
-	 *              0x01, \n:  terminators
-	 */
-
-	/* AAA = "us", ie. where server normally talks to. */
-	sprintf(buffer, "%u %u",
-		ntohl(exp->master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip),
-		port);
-	DEBUGP("ip_nat_irc: Inserting '%s' == %u.%u.%u.%u, port %u\n",
-	       buffer, NIPQUAD(exp->tuple.src.ip), port);
-
-	ret = ip_nat_mangle_tcp_packet(pskb, exp->master, ctinfo,
-				       matchoff, matchlen, buffer,
-				       strlen(buffer));
-	if (ret != NF_ACCEPT)
-		ip_conntrack_unexpect_related(exp);
-	return ret;
-}
-
-static void __exit ip_nat_irc_fini(void)
-{
-	rcu_assign_pointer(ip_nat_irc_hook, NULL);
-	synchronize_rcu();
-}
-
-static int __init ip_nat_irc_init(void)
-{
-	BUG_ON(rcu_dereference(ip_nat_irc_hook));
-	rcu_assign_pointer(ip_nat_irc_hook, help);
-	return 0;
-}
-
-/* Prior to 2.6.11, we had a ports param.  No longer, but don't break users. */
-static int warn_set(const char *val, struct kernel_param *kp)
-{
-	printk(KERN_INFO KBUILD_MODNAME
-	       ": kernel >= 2.6.10 only uses 'ports' for conntrack modules\n");
-	return 0;
-}
-module_param_call(ports, warn_set, NULL, NULL, 0);
-
-module_init(ip_nat_irc_init);
-module_exit(ip_nat_irc_fini);
diff --git a/net/ipv4/netfilter/ip_nat_proto_gre.c b/net/ipv4/netfilter/ip_nat_proto_gre.c
deleted file mode 100644
index 9581020..0000000
--- a/net/ipv4/netfilter/ip_nat_proto_gre.c
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * ip_nat_proto_gre.c - Version 2.0
- *
- * NAT protocol helper module for GRE.
- *
- * GRE is a generic encapsulation protocol, which is generally not very
- * suited for NAT, as it has no protocol-specific part as port numbers.
- *
- * It has an optional key field, which may help us distinguishing two
- * connections between the same two hosts.
- *
- * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784
- *
- * PPTP is built on top of a modified version of GRE, and has a mandatory
- * field called "CallID", which serves us for the same purpose as the key
- * field in plain GRE.
- *
- * Documentation about PPTP can be found in RFC 2637
- *
- * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org>
- *
- * Development of this code funded by Astaro AG (http://www.astaro.com/)
- *
- */
-
-#include <linux/module.h>
-#include <linux/ip.h>
-#include <linux/netfilter_ipv4/ip_nat.h>
-#include <linux/netfilter_ipv4/ip_nat_rule.h>
-#include <linux/netfilter_ipv4/ip_nat_protocol.h>
-#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
-MODULE_DESCRIPTION("Netfilter NAT protocol helper module for GRE");
-
-#if 0
-#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, \
-				       __FUNCTION__, ## args)
-#else
-#define DEBUGP(x, args...)
-#endif
-
-/* is key in given range between min and max */
-static int
-gre_in_range(const struct ip_conntrack_tuple *tuple,
-	     enum ip_nat_manip_type maniptype,
-	     const union ip_conntrack_manip_proto *min,
-	     const union ip_conntrack_manip_proto *max)
-{
-	__be16 key;
-
-	if (maniptype == IP_NAT_MANIP_SRC)
-		key = tuple->src.u.gre.key;
-	else
-		key = tuple->dst.u.gre.key;
-
-	return ntohs(key) >= ntohs(min->gre.key)
-		&& ntohs(key) <= ntohs(max->gre.key);
-}
-
-/* generate unique tuple ... */
-static int
-gre_unique_tuple(struct ip_conntrack_tuple *tuple,
-		 const struct ip_nat_range *range,
-		 enum ip_nat_manip_type maniptype,
-		 const struct ip_conntrack *conntrack)
-{
-	static u_int16_t key;
-	__be16 *keyptr;
-	unsigned int min, i, range_size;
-
-	if (maniptype == IP_NAT_MANIP_SRC)
-		keyptr = &tuple->src.u.gre.key;
-	else
-		keyptr = &tuple->dst.u.gre.key;
-
-	if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) {
-		DEBUGP("%p: NATing GRE PPTP\n", conntrack);
-		min = 1;
-		range_size = 0xffff;
-	} else {
-		min = ntohs(range->min.gre.key);
-		range_size = ntohs(range->max.gre.key) - min + 1;
-	}
-
-	DEBUGP("min = %u, range_size = %u\n", min, range_size);
-
-	for (i = 0; i < range_size; i++, key++) {
-		*keyptr = htons(min + key % range_size);
-		if (!ip_nat_used_tuple(tuple, conntrack))
-			return 1;
-	}
-
-	DEBUGP("%p: no NAT mapping\n", conntrack);
-
-	return 0;
-}
-
-/* manipulate a GRE packet according to maniptype */
-static int
-gre_manip_pkt(struct sk_buff **pskb,
-	      unsigned int iphdroff,
-	      const struct ip_conntrack_tuple *tuple,
-	      enum ip_nat_manip_type maniptype)
-{
-	struct gre_hdr *greh;
-	struct gre_hdr_pptp *pgreh;
-	struct iphdr *iph = (struct iphdr *)((*pskb)->data + iphdroff);
-	unsigned int hdroff = iphdroff + iph->ihl*4;
-
-	/* pgreh includes two optional 32bit fields which are not required
-	 * to be there.  That's where the magic '8' comes from */
-	if (!skb_make_writable(pskb, hdroff + sizeof(*pgreh)-8))
-		return 0;
-
-	greh = (void *)(*pskb)->data + hdroff;
-	pgreh = (struct gre_hdr_pptp *) greh;
-
-	/* we only have destination manip of a packet, since 'source key'
-	 * is not present in the packet itself */
-	if (maniptype == IP_NAT_MANIP_DST) {
-		/* key manipulation is always dest */
-		switch (greh->version) {
-		case 0:
-			if (!greh->key) {
-				DEBUGP("can't nat GRE w/o key\n");
-				break;
-			}
-			if (greh->csum) {
-				/* FIXME: Never tested this code... */
-				nf_proto_csum_replace4(gre_csum(greh), *pskb,
-							*(gre_key(greh)),
-							tuple->dst.u.gre.key, 0);
-			}
-			*(gre_key(greh)) = tuple->dst.u.gre.key;
-			break;
-		case GRE_VERSION_PPTP:
-			DEBUGP("call_id -> 0x%04x\n",
-				ntohs(tuple->dst.u.gre.key));
-			pgreh->call_id = tuple->dst.u.gre.key;
-			break;
-		default:
-			DEBUGP("can't nat unknown GRE version\n");
-			return 0;
-			break;
-		}
-	}
-	return 1;
-}
-
-/* nat helper struct */
-static struct ip_nat_protocol gre = {
-	.name		= "GRE",
-	.protonum	= IPPROTO_GRE,
-	.manip_pkt	= gre_manip_pkt,
-	.in_range	= gre_in_range,
-	.unique_tuple	= gre_unique_tuple,
-#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
-    defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
-	.range_to_nfattr	= ip_nat_port_range_to_nfattr,
-	.nfattr_to_range	= ip_nat_port_nfattr_to_range,
-#endif
-};
-
-int __init ip_nat_proto_gre_init(void)
-{
-	return ip_nat_protocol_register(&gre);
-}
-
-void __exit ip_nat_proto_gre_fini(void)
-{
-	ip_nat_protocol_unregister(&gre);
-}
diff --git a/net/ipv4/netfilter/ip_nat_proto_icmp.c b/net/ipv4/netfilter/ip_nat_proto_icmp.c
deleted file mode 100644
index 22a528a..0000000
--- a/net/ipv4/netfilter/ip_nat_proto_icmp.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/* (C) 1999-2001 Paul `Rusty' Russell
- * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.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.
- */
-
-#include <linux/types.h>
-#include <linux/init.h>
-#include <linux/netfilter.h>
-#include <linux/ip.h>
-#include <linux/icmp.h>
-#include <linux/if.h>
-
-#include <linux/netfilter_ipv4/ip_nat.h>
-#include <linux/netfilter_ipv4/ip_nat_core.h>
-#include <linux/netfilter_ipv4/ip_nat_rule.h>
-#include <linux/netfilter_ipv4/ip_nat_protocol.h>
-
-static int
-icmp_in_range(const struct ip_conntrack_tuple *tuple,
-	      enum ip_nat_manip_type maniptype,
-	      const union ip_conntrack_manip_proto *min,
-	      const union ip_conntrack_manip_proto *max)
-{
-	return ntohs(tuple->src.u.icmp.id) >= ntohs(min->icmp.id) &&
-	       ntohs(tuple->src.u.icmp.id) <= ntohs(max->icmp.id);
-}
-
-static int
-icmp_unique_tuple(struct ip_conntrack_tuple *tuple,
-		  const struct ip_nat_range *range,
-		  enum ip_nat_manip_type maniptype,
-		  const struct ip_conntrack *conntrack)
-{
-	static u_int16_t id;
-	unsigned int range_size;
-	unsigned int i;
-
-	range_size = ntohs(range->max.icmp.id) - ntohs(range->min.icmp.id) + 1;
-	/* If no range specified... */
-	if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED))
-		range_size = 0xFFFF;
-
-	for (i = 0; i < range_size; i++, id++) {
-		tuple->src.u.icmp.id = htons(ntohs(range->min.icmp.id) +
-					     (id % range_size));
-		if (!ip_nat_used_tuple(tuple, conntrack))
-			return 1;
-	}
-	return 0;
-}
-
-static int
-icmp_manip_pkt(struct sk_buff **pskb,
-	       unsigned int iphdroff,
-	       const struct ip_conntrack_tuple *tuple,
-	       enum ip_nat_manip_type maniptype)
-{
-	struct iphdr *iph = (struct iphdr *)((*pskb)->data + iphdroff);
-	struct icmphdr *hdr;
-	unsigned int hdroff = iphdroff + iph->ihl*4;
-
-	if (!skb_make_writable(pskb, hdroff + sizeof(*hdr)))
-		return 0;
-
-	hdr = (struct icmphdr *)((*pskb)->data + hdroff);
-	nf_proto_csum_replace2(&hdr->checksum, *pskb,
-			       hdr->un.echo.id, tuple->src.u.icmp.id, 0);
-	hdr->un.echo.id = tuple->src.u.icmp.id;
-	return 1;
-}
-
-struct ip_nat_protocol ip_nat_protocol_icmp = {
-	.name			= "ICMP",
-	.protonum		= IPPROTO_ICMP,
-	.me			= THIS_MODULE,
-	.manip_pkt		= icmp_manip_pkt,
-	.in_range		= icmp_in_range,
-	.unique_tuple		= icmp_unique_tuple,
-#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
-    defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
-	.range_to_nfattr	= ip_nat_port_range_to_nfattr,
-	.nfattr_to_range	= ip_nat_port_nfattr_to_range,
-#endif
-};
diff --git a/net/ipv4/netfilter/ip_nat_proto_tcp.c b/net/ipv4/netfilter/ip_nat_proto_tcp.c
deleted file mode 100644
index 14ff24f..0000000
--- a/net/ipv4/netfilter/ip_nat_proto_tcp.c
+++ /dev/null
@@ -1,154 +0,0 @@
-/* (C) 1999-2001 Paul `Rusty' Russell
- * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.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.
- */
-
-#include <linux/types.h>
-#include <linux/init.h>
-#include <linux/random.h>
-#include <linux/netfilter.h>
-#include <linux/ip.h>
-#include <linux/tcp.h>
-#include <linux/if.h>
-#include <linux/netfilter/nfnetlink_conntrack.h>
-#include <linux/netfilter_ipv4/ip_nat.h>
-#include <linux/netfilter_ipv4/ip_nat_rule.h>
-#include <linux/netfilter_ipv4/ip_nat_protocol.h>
-#include <linux/netfilter_ipv4/ip_nat_core.h>
-
-static int
-tcp_in_range(const struct ip_conntrack_tuple *tuple,
-	     enum ip_nat_manip_type maniptype,
-	     const union ip_conntrack_manip_proto *min,
-	     const union ip_conntrack_manip_proto *max)
-{
-	__be16 port;
-
-	if (maniptype == IP_NAT_MANIP_SRC)
-		port = tuple->src.u.tcp.port;
-	else
-		port = tuple->dst.u.tcp.port;
-
-	return ntohs(port) >= ntohs(min->tcp.port)
-		&& ntohs(port) <= ntohs(max->tcp.port);
-}
-
-static int
-tcp_unique_tuple(struct ip_conntrack_tuple *tuple,
-		 const struct ip_nat_range *range,
-		 enum ip_nat_manip_type maniptype,
-		 const struct ip_conntrack *conntrack)
-{
-	static u_int16_t port;
-	__be16 *portptr;
-	unsigned int range_size, min, i;
-
-	if (maniptype == IP_NAT_MANIP_SRC)
-		portptr = &tuple->src.u.tcp.port;
-	else
-		portptr = &tuple->dst.u.tcp.port;
-
-	/* If no range specified... */
-	if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) {
-		/* If it's dst rewrite, can't change port */
-		if (maniptype == IP_NAT_MANIP_DST)
-			return 0;
-
-		/* Map privileged onto privileged. */
-		if (ntohs(*portptr) < 1024) {
-			/* Loose convention: >> 512 is credential passing */
-			if (ntohs(*portptr)<512) {
-				min = 1;
-				range_size = 511 - min + 1;
-			} else {
-				min = 600;
-				range_size = 1023 - min + 1;
-			}
-		} else {
-			min = 1024;
-			range_size = 65535 - 1024 + 1;
-		}
-	} else {
-		min = ntohs(range->min.tcp.port);
-		range_size = ntohs(range->max.tcp.port) - min + 1;
-	}
-
-	/* Start from random port to avoid prediction */
-	if (range->flags & IP_NAT_RANGE_PROTO_RANDOM)
-		port =  net_random();
-
-	for (i = 0; i < range_size; i++, port++) {
-		*portptr = htons(min + port % range_size);
-		if (!ip_nat_used_tuple(tuple, conntrack)) {
-			return 1;
-		}
-	}
-	return 0;
-}
-
-static int
-tcp_manip_pkt(struct sk_buff **pskb,
-	      unsigned int iphdroff,
-	      const struct ip_conntrack_tuple *tuple,
-	      enum ip_nat_manip_type maniptype)
-{
-	struct iphdr *iph = (struct iphdr *)((*pskb)->data + iphdroff);
-	struct tcphdr *hdr;
-	unsigned int hdroff = iphdroff + iph->ihl*4;
-	__be32 oldip, newip;
-	__be16 *portptr, newport, oldport;
-	int hdrsize = 8; /* TCP connection tracking guarantees this much */
-
-	/* this could be a inner header returned in icmp packet; in such
-	   cases we cannot update the checksum field since it is outside of
-	   the 8 bytes of transport layer headers we are guaranteed */
-	if ((*pskb)->len >= hdroff + sizeof(struct tcphdr))
-		hdrsize = sizeof(struct tcphdr);
-
-	if (!skb_make_writable(pskb, hdroff + hdrsize))
-		return 0;
-
-	iph = (struct iphdr *)((*pskb)->data + iphdroff);
-	hdr = (struct tcphdr *)((*pskb)->data + hdroff);
-
-	if (maniptype == IP_NAT_MANIP_SRC) {
-		/* Get rid of src ip and src pt */
-		oldip = iph->saddr;
-		newip = tuple->src.ip;
-		newport = tuple->src.u.tcp.port;
-		portptr = &hdr->source;
-	} else {
-		/* Get rid of dst ip and dst pt */
-		oldip = iph->daddr;
-		newip = tuple->dst.ip;
-		newport = tuple->dst.u.tcp.port;
-		portptr = &hdr->dest;
-	}
-
-	oldport = *portptr;
-	*portptr = newport;
-
-	if (hdrsize < sizeof(*hdr))
-		return 1;
-
-	nf_proto_csum_replace4(&hdr->check, *pskb, oldip, newip, 1);
-	nf_proto_csum_replace2(&hdr->check, *pskb, oldport, newport, 0);
-	return 1;
-}
-
-struct ip_nat_protocol ip_nat_protocol_tcp = {
-	.name			= "TCP",
-	.protonum		= IPPROTO_TCP,
-	.me			= THIS_MODULE,
-	.manip_pkt		= tcp_manip_pkt,
-	.in_range		= tcp_in_range,
-	.unique_tuple		= tcp_unique_tuple,
-#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
-    defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
-	.range_to_nfattr	= ip_nat_port_range_to_nfattr,
-	.nfattr_to_range	= ip_nat_port_nfattr_to_range,
-#endif
-};
diff --git a/net/ipv4/netfilter/ip_nat_proto_udp.c b/net/ipv4/netfilter/ip_nat_proto_udp.c
deleted file mode 100644
index dfd5216..0000000
--- a/net/ipv4/netfilter/ip_nat_proto_udp.c
+++ /dev/null
@@ -1,144 +0,0 @@
-/* (C) 1999-2001 Paul `Rusty' Russell
- * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.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.
- */
-
-#include <linux/types.h>
-#include <linux/init.h>
-#include <linux/random.h>
-#include <linux/netfilter.h>
-#include <linux/ip.h>
-#include <linux/udp.h>
-#include <linux/if.h>
-
-#include <linux/netfilter_ipv4/ip_nat.h>
-#include <linux/netfilter_ipv4/ip_nat_core.h>
-#include <linux/netfilter_ipv4/ip_nat_rule.h>
-#include <linux/netfilter_ipv4/ip_nat_protocol.h>
-
-static int
-udp_in_range(const struct ip_conntrack_tuple *tuple,
-	     enum ip_nat_manip_type maniptype,
-	     const union ip_conntrack_manip_proto *min,
-	     const union ip_conntrack_manip_proto *max)
-{
-	__be16 port;
-
-	if (maniptype == IP_NAT_MANIP_SRC)
-		port = tuple->src.u.udp.port;
-	else
-		port = tuple->dst.u.udp.port;
-
-	return ntohs(port) >= ntohs(min->udp.port)
-		&& ntohs(port) <= ntohs(max->udp.port);
-}
-
-static int
-udp_unique_tuple(struct ip_conntrack_tuple *tuple,
-		 const struct ip_nat_range *range,
-		 enum ip_nat_manip_type maniptype,
-		 const struct ip_conntrack *conntrack)
-{
-	static u_int16_t port;
-	__be16 *portptr;
-	unsigned int range_size, min, i;
-
-	if (maniptype == IP_NAT_MANIP_SRC)
-		portptr = &tuple->src.u.udp.port;
-	else
-		portptr = &tuple->dst.u.udp.port;
-
-	/* If no range specified... */
-	if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) {
-		/* If it's dst rewrite, can't change port */
-		if (maniptype == IP_NAT_MANIP_DST)
-			return 0;
-
-		if (ntohs(*portptr) < 1024) {
-			/* Loose convention: >> 512 is credential passing */
-			if (ntohs(*portptr)<512) {
-				min = 1;
-				range_size = 511 - min + 1;
-			} else {
-				min = 600;
-				range_size = 1023 - min + 1;
-			}
-		} else {
-			min = 1024;
-			range_size = 65535 - 1024 + 1;
-		}
-	} else {
-		min = ntohs(range->min.udp.port);
-		range_size = ntohs(range->max.udp.port) - min + 1;
-	}
-
-	/* Start from random port to avoid prediction */
-	if (range->flags & IP_NAT_RANGE_PROTO_RANDOM)
-		port = net_random();
-
-	for (i = 0; i < range_size; i++, port++) {
-		*portptr = htons(min + port % range_size);
-		if (!ip_nat_used_tuple(tuple, conntrack))
-			return 1;
-	}
-	return 0;
-}
-
-static int
-udp_manip_pkt(struct sk_buff **pskb,
-	      unsigned int iphdroff,
-	      const struct ip_conntrack_tuple *tuple,
-	      enum ip_nat_manip_type maniptype)
-{
-	struct iphdr *iph = (struct iphdr *)((*pskb)->data + iphdroff);
-	struct udphdr *hdr;
-	unsigned int hdroff = iphdroff + iph->ihl*4;
-	__be32 oldip, newip;
-	__be16 *portptr, newport;
-
-	if (!skb_make_writable(pskb, hdroff + sizeof(*hdr)))
-		return 0;
-
-	iph = (struct iphdr *)((*pskb)->data + iphdroff);
-	hdr = (struct udphdr *)((*pskb)->data + hdroff);
-
-	if (maniptype == IP_NAT_MANIP_SRC) {
-		/* Get rid of src ip and src pt */
-		oldip = iph->saddr;
-		newip = tuple->src.ip;
-		newport = tuple->src.u.udp.port;
-		portptr = &hdr->source;
-	} else {
-		/* Get rid of dst ip and dst pt */
-		oldip = iph->daddr;
-		newip = tuple->dst.ip;
-		newport = tuple->dst.u.udp.port;
-		portptr = &hdr->dest;
-	}
-
-	if (hdr->check || (*pskb)->ip_summed == CHECKSUM_PARTIAL) {
-		nf_proto_csum_replace4(&hdr->check, *pskb, oldip, newip, 1);
-		nf_proto_csum_replace2(&hdr->check, *pskb, *portptr, newport, 0);
-		if (!hdr->check)
-			hdr->check = CSUM_MANGLED_0;
-	}
-	*portptr = newport;
-	return 1;
-}
-
-struct ip_nat_protocol ip_nat_protocol_udp = {
-	.name			= "UDP",
-	.protonum		= IPPROTO_UDP,
-	.me			= THIS_MODULE,
-	.manip_pkt		= udp_manip_pkt,
-	.in_range		= udp_in_range,
-	.unique_tuple		= udp_unique_tuple,
-#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
-    defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
-	.range_to_nfattr	= ip_nat_port_range_to_nfattr,
-	.nfattr_to_range	= ip_nat_port_nfattr_to_range,
-#endif
-};
diff --git a/net/ipv4/netfilter/ip_nat_proto_unknown.c b/net/ipv4/netfilter/ip_nat_proto_unknown.c
deleted file mode 100644
index 3bf0495..0000000
--- a/net/ipv4/netfilter/ip_nat_proto_unknown.c
+++ /dev/null
@@ -1,55 +0,0 @@
-/* The "unknown" protocol.  This is what is used for protocols we
- * don't understand.  It's returned by ip_ct_find_proto().
- */
-
-/* (C) 1999-2001 Paul `Rusty' Russell
- * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.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.
- */
-
-#include <linux/types.h>
-#include <linux/init.h>
-#include <linux/netfilter.h>
-#include <linux/if.h>
-
-#include <linux/netfilter_ipv4/ip_nat.h>
-#include <linux/netfilter_ipv4/ip_nat_rule.h>
-#include <linux/netfilter_ipv4/ip_nat_protocol.h>
-
-static int unknown_in_range(const struct ip_conntrack_tuple *tuple,
-			    enum ip_nat_manip_type manip_type,
-			    const union ip_conntrack_manip_proto *min,
-			    const union ip_conntrack_manip_proto *max)
-{
-	return 1;
-}
-
-static int unknown_unique_tuple(struct ip_conntrack_tuple *tuple,
-				const struct ip_nat_range *range,
-				enum ip_nat_manip_type maniptype,
-				const struct ip_conntrack *conntrack)
-{
-	/* Sorry: we can't help you; if it's not unique, we can't frob
-	   anything. */
-	return 0;
-}
-
-static int
-unknown_manip_pkt(struct sk_buff **pskb,
-		  unsigned int iphdroff,
-		  const struct ip_conntrack_tuple *tuple,
-		  enum ip_nat_manip_type maniptype)
-{
-	return 1;
-}
-
-struct ip_nat_protocol ip_nat_unknown_protocol = {
-	.name			= "unknown",
-	/* .me isn't set: getting a ref to this cannot fail. */
-	.manip_pkt		= unknown_manip_pkt,
-	.in_range		= unknown_in_range,
-	.unique_tuple		= unknown_unique_tuple,
-};
diff --git a/net/ipv4/netfilter/ip_nat_rule.c b/net/ipv4/netfilter/ip_nat_rule.c
deleted file mode 100644
index 080eb1d..0000000
--- a/net/ipv4/netfilter/ip_nat_rule.c
+++ /dev/null
@@ -1,314 +0,0 @@
-/* (C) 1999-2001 Paul `Rusty' Russell
- * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.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.
- */
-
-/* Everything about the rules for NAT. */
-#include <linux/types.h>
-#include <linux/ip.h>
-#include <linux/netfilter.h>
-#include <linux/netfilter_ipv4.h>
-#include <linux/module.h>
-#include <linux/kmod.h>
-#include <linux/skbuff.h>
-#include <linux/proc_fs.h>
-#include <net/checksum.h>
-#include <net/route.h>
-#include <linux/bitops.h>
-
-#include <linux/netfilter_ipv4/ip_tables.h>
-#include <linux/netfilter_ipv4/ip_nat.h>
-#include <linux/netfilter_ipv4/ip_nat_core.h>
-#include <linux/netfilter_ipv4/ip_nat_rule.h>
-
-#if 0
-#define DEBUGP printk
-#else
-#define DEBUGP(format, args...)
-#endif
-
-#define NAT_VALID_HOOKS ((1<<NF_IP_PRE_ROUTING) | (1<<NF_IP_POST_ROUTING) | (1<<NF_IP_LOCAL_OUT))
-
-static struct
-{
-	struct ipt_replace repl;
-	struct ipt_standard entries[3];
-	struct ipt_error term;
-} nat_initial_table __initdata
-= { { "nat", NAT_VALID_HOOKS, 4,
-      sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error),
-      { [NF_IP_PRE_ROUTING] = 0,
-	[NF_IP_POST_ROUTING] = sizeof(struct ipt_standard),
-	[NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 2 },
-      { [NF_IP_PRE_ROUTING] = 0,
-	[NF_IP_POST_ROUTING] = sizeof(struct ipt_standard),
-	[NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 2 },
-      0, NULL, { } },
-    {
-	    /* PRE_ROUTING */
-	    { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
-		0,
-		sizeof(struct ipt_entry),
-		sizeof(struct ipt_standard),
-		0, { 0, 0 }, { } },
-	      { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },
-		-NF_ACCEPT - 1 } },
-	    /* POST_ROUTING */
-	    { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
-		0,
-		sizeof(struct ipt_entry),
-		sizeof(struct ipt_standard),
-		0, { 0, 0 }, { } },
-	      { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },
-		-NF_ACCEPT - 1 } },
-	    /* LOCAL_OUT */
-	    { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
-		0,
-		sizeof(struct ipt_entry),
-		sizeof(struct ipt_standard),
-		0, { 0, 0 }, { } },
-	      { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },
-		-NF_ACCEPT - 1 } }
-    },
-    /* ERROR */
-    { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
-	0,
-	sizeof(struct ipt_entry),
-	sizeof(struct ipt_error),
-	0, { 0, 0 }, { } },
-      { { { { IPT_ALIGN(sizeof(struct ipt_error_target)), IPT_ERROR_TARGET } },
-	  { } },
-	"ERROR"
-      }
-    }
-};
-
-static struct xt_table nat_table = {
-	.name		= "nat",
-	.valid_hooks	= NAT_VALID_HOOKS,
-	.lock		= RW_LOCK_UNLOCKED,
-	.me		= THIS_MODULE,
-	.af		= AF_INET,
-};
-
-/* Source NAT */
-static unsigned int ipt_snat_target(struct sk_buff **pskb,
-				    const struct net_device *in,
-				    const struct net_device *out,
-				    unsigned int hooknum,
-				    const struct xt_target *target,
-				    const void *targinfo)
-{
-	struct ip_conntrack *ct;
-	enum ip_conntrack_info ctinfo;
-	const struct ip_nat_multi_range_compat *mr = targinfo;
-
-	IP_NF_ASSERT(hooknum == NF_IP_POST_ROUTING);
-
-	ct = ip_conntrack_get(*pskb, &ctinfo);
-
-	/* Connection must be valid and new. */
-	IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED
-			    || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY));
-	IP_NF_ASSERT(out);
-
-	return ip_nat_setup_info(ct, &mr->range[0], hooknum);
-}
-
-/* Before 2.6.11 we did implicit source NAT if required. Warn about change. */
-static void warn_if_extra_mangle(__be32 dstip, __be32 srcip)
-{
-	static int warned = 0;
-	struct flowi fl = { .nl_u = { .ip4_u = { .daddr = dstip } } };
-	struct rtable *rt;
-
-	if (ip_route_output_key(&rt, &fl) != 0)
-		return;
-
-	if (rt->rt_src != srcip && !warned) {
-		printk("NAT: no longer support implicit source local NAT\n");
-		printk("NAT: packet src %u.%u.%u.%u -> dst %u.%u.%u.%u\n",
-		       NIPQUAD(srcip), NIPQUAD(dstip));
-		warned = 1;
-	}
-	ip_rt_put(rt);
-}
-
-static unsigned int ipt_dnat_target(struct sk_buff **pskb,
-				    const struct net_device *in,
-				    const struct net_device *out,
-				    unsigned int hooknum,
-				    const struct xt_target *target,
-				    const void *targinfo)
-{
-	struct ip_conntrack *ct;
-	enum ip_conntrack_info ctinfo;
-	const struct ip_nat_multi_range_compat *mr = targinfo;
-
-	IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING
-		     || hooknum == NF_IP_LOCAL_OUT);
-
-	ct = ip_conntrack_get(*pskb, &ctinfo);
-
-	/* Connection must be valid and new. */
-	IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
-
-	if (hooknum == NF_IP_LOCAL_OUT
-	    && mr->range[0].flags & IP_NAT_RANGE_MAP_IPS)
-		warn_if_extra_mangle((*pskb)->nh.iph->daddr,
-				     mr->range[0].min_ip);
-
-	return ip_nat_setup_info(ct, &mr->range[0], hooknum);
-}
-
-static int ipt_snat_checkentry(const char *tablename,
-			       const void *entry,
-			       const struct xt_target *target,
-			       void *targinfo,
-			       unsigned int hook_mask)
-{
-	struct ip_nat_multi_range_compat *mr = targinfo;
-
-	/* Must be a valid range */
-	if (mr->rangesize != 1) {
-		printk("SNAT: multiple ranges no longer supported\n");
-		return 0;
-	}
-	return 1;
-}
-
-static int ipt_dnat_checkentry(const char *tablename,
-			       const void *entry,
-			       const struct xt_target *target,
-			       void *targinfo,
-			       unsigned int hook_mask)
-{
-	struct ip_nat_multi_range_compat *mr = targinfo;
-
-	/* Must be a valid range */
-	if (mr->rangesize != 1) {
-		printk("DNAT: multiple ranges no longer supported\n");
-		return 0;
-	}
-	if (mr->range[0].flags & IP_NAT_RANGE_PROTO_RANDOM) {
-		printk("DNAT: port randomization not supported\n");
-		return 0;
-	}
-	return 1;
-}
-
-inline unsigned int
-alloc_null_binding(struct ip_conntrack *conntrack,
-		   struct ip_nat_info *info,
-		   unsigned int hooknum)
-{
-	/* Force range to this IP; let proto decide mapping for
-	   per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED).
-	   Use reply in case it's already been mangled (eg local packet).
-	*/
-	__be32 ip
-		= (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC
-		   ? conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip
-		   : conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip);
-	struct ip_nat_range range
-		= { IP_NAT_RANGE_MAP_IPS, ip, ip, { 0 }, { 0 } };
-
-	DEBUGP("Allocating NULL binding for %p (%u.%u.%u.%u)\n", conntrack,
-	       NIPQUAD(ip));
-	return ip_nat_setup_info(conntrack, &range, hooknum);
-}
-
-unsigned int
-alloc_null_binding_confirmed(struct ip_conntrack *conntrack,
-			     struct ip_nat_info *info,
-			     unsigned int hooknum)
-{
-	__be32 ip
-		= (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC
-		   ? conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip
-		   : conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip);
-	u_int16_t all
-		= (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC
-		   ? conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.all
-		   : conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.all);
-	struct ip_nat_range range
-		= { IP_NAT_RANGE_MAP_IPS, ip, ip, { all }, { all } };
-
-	DEBUGP("Allocating NULL binding for confirmed %p (%u.%u.%u.%u)\n",
-	       conntrack, NIPQUAD(ip));
-	return ip_nat_setup_info(conntrack, &range, hooknum);
-}
-
-int ip_nat_rule_find(struct sk_buff **pskb,
-		     unsigned int hooknum,
-		     const struct net_device *in,
-		     const struct net_device *out,
-		     struct ip_conntrack *ct,
-		     struct ip_nat_info *info)
-{
-	int ret;
-
-	ret = ipt_do_table(pskb, hooknum, in, out, &nat_table);
-
-	if (ret == NF_ACCEPT) {
-		if (!ip_nat_initialized(ct, HOOK2MANIP(hooknum)))
-			/* NUL mapping */
-			ret = alloc_null_binding(ct, info, hooknum);
-	}
-	return ret;
-}
-
-static struct xt_target ipt_snat_reg = {
-	.name		= "SNAT",
-	.family		= AF_INET,
-	.target		= ipt_snat_target,
-	.targetsize	= sizeof(struct ip_nat_multi_range_compat),
-	.table		= "nat",
-	.hooks		= 1 << NF_IP_POST_ROUTING,
-	.checkentry	= ipt_snat_checkentry,
-};
-
-static struct xt_target ipt_dnat_reg = {
-	.name		= "DNAT",
-	.family		= AF_INET,
-	.target		= ipt_dnat_target,
-	.targetsize	= sizeof(struct ip_nat_multi_range_compat),
-	.table		= "nat",
-	.hooks		= (1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_OUT),
-	.checkentry	= ipt_dnat_checkentry,
-};
-
-int __init ip_nat_rule_init(void)
-{
-	int ret;
-
-	ret = ipt_register_table(&nat_table, &nat_initial_table.repl);
-	if (ret != 0)
-		return ret;
-	ret = xt_register_target(&ipt_snat_reg);
-	if (ret != 0)
-		goto unregister_table;
-
-	ret = xt_register_target(&ipt_dnat_reg);
-	if (ret != 0)
-		goto unregister_snat;
-
-	return ret;
-
- unregister_snat:
-	xt_unregister_target(&ipt_snat_reg);
- unregister_table:
-	xt_unregister_table(&nat_table);
-
-	return ret;
-}
-
-void ip_nat_rule_cleanup(void)
-{
-	xt_unregister_target(&ipt_dnat_reg);
-	xt_unregister_target(&ipt_snat_reg);
-	ipt_unregister_table(&nat_table);
-}
diff --git a/net/ipv4/netfilter/ip_nat_sip.c b/net/ipv4/netfilter/ip_nat_sip.c
deleted file mode 100644
index 325c5a9..0000000
--- a/net/ipv4/netfilter/ip_nat_sip.c
+++ /dev/null
@@ -1,282 +0,0 @@
-/* SIP extension for UDP NAT alteration.
- *
- * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar>
- * based on RR's ip_nat_ftp.c and other modules.
- *
- * 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.
- */
-
-#include <linux/module.h>
-#include <linux/skbuff.h>
-#include <linux/ip.h>
-#include <linux/udp.h>
-
-#include <linux/netfilter_ipv4.h>
-#include <linux/netfilter_ipv4/ip_nat.h>
-#include <linux/netfilter_ipv4/ip_nat_helper.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-#include <linux/netfilter_ipv4/ip_conntrack_sip.h>
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");
-MODULE_DESCRIPTION("SIP NAT helper");
-
-#if 0
-#define DEBUGP printk
-#else
-#define DEBUGP(format, args...)
-#endif
-
-struct addr_map {
-	struct {
-		char		src[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
-		char		dst[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
-		unsigned int	srclen, srciplen;
-		unsigned int	dstlen, dstiplen;
-	} addr[IP_CT_DIR_MAX];
-};
-
-static void addr_map_init(struct ip_conntrack *ct, struct addr_map *map)
-{
-	struct ip_conntrack_tuple *t;
-	enum ip_conntrack_dir dir;
-	unsigned int n;
-
-	for (dir = 0; dir < IP_CT_DIR_MAX; dir++) {
-		t = &ct->tuplehash[dir].tuple;
-
-		n = sprintf(map->addr[dir].src, "%u.%u.%u.%u",
-			    NIPQUAD(t->src.ip));
-		map->addr[dir].srciplen = n;
-		n += sprintf(map->addr[dir].src + n, ":%u",
-			     ntohs(t->src.u.udp.port));
-		map->addr[dir].srclen = n;
-
-		n = sprintf(map->addr[dir].dst, "%u.%u.%u.%u",
-			    NIPQUAD(t->dst.ip));
-		map->addr[dir].dstiplen = n;
-		n += sprintf(map->addr[dir].dst + n, ":%u",
-			     ntohs(t->dst.u.udp.port));
-		map->addr[dir].dstlen = n;
-	}
-}
-
-static int map_sip_addr(struct sk_buff **pskb, enum ip_conntrack_info ctinfo,
-			struct ip_conntrack *ct, const char **dptr, size_t dlen,
-			enum sip_header_pos pos, struct addr_map *map)
-{
-	enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
-	unsigned int matchlen, matchoff, addrlen;
-	char *addr;
-
-	if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, pos) <= 0)
-		return 1;
-
-	if ((matchlen == map->addr[dir].srciplen ||
-	     matchlen == map->addr[dir].srclen) &&
-	    memcmp(*dptr + matchoff, map->addr[dir].src, matchlen) == 0) {
-		addr    = map->addr[!dir].dst;
-		addrlen = map->addr[!dir].dstlen;
-	} else if ((matchlen == map->addr[dir].dstiplen ||
-		    matchlen == map->addr[dir].dstlen) &&
-		   memcmp(*dptr + matchoff, map->addr[dir].dst, matchlen) == 0) {
-		addr    = map->addr[!dir].src;
-		addrlen = map->addr[!dir].srclen;
-	} else
-		return 1;
-
-	if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
-				      matchoff, matchlen, addr, addrlen))
-		return 0;
-	*dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
-	return 1;
-
-}
-
-static unsigned int ip_nat_sip(struct sk_buff **pskb,
-			       enum ip_conntrack_info ctinfo,
-			       struct ip_conntrack *ct,
-			       const char **dptr)
-{
-	enum sip_header_pos pos;
-	struct addr_map map;
-	int dataoff, datalen;
-
-	dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
-	datalen = (*pskb)->len - dataoff;
-	if (datalen < sizeof("SIP/2.0") - 1)
-		return NF_DROP;
-
-	addr_map_init(ct, &map);
-
-	/* Basic rules: requests and responses. */
-	if (strncmp(*dptr, "SIP/2.0", sizeof("SIP/2.0") - 1) != 0) {
-		/* 10.2: Constructing the REGISTER Request:
-		 *
-		 * The "userinfo" and "@" components of the SIP URI MUST NOT
-		 * be present.
-		 */
-		if (datalen >= sizeof("REGISTER") - 1 &&
-		    strncmp(*dptr, "REGISTER", sizeof("REGISTER") - 1) == 0)
-			pos = POS_REG_REQ_URI;
-		else
-			pos = POS_REQ_URI;
-
-		if (!map_sip_addr(pskb, ctinfo, ct, dptr, datalen, pos, &map))
-			return NF_DROP;
-	}
-
-	if (!map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_FROM, &map) ||
-	    !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_TO, &map) ||
-	    !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_VIA, &map) ||
-	    !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_CONTACT, &map))
-		return NF_DROP;
-	return NF_ACCEPT;
-}
-
-static unsigned int mangle_sip_packet(struct sk_buff **pskb,
-				      enum ip_conntrack_info ctinfo,
-				      struct ip_conntrack *ct,
-				      const char **dptr, size_t dlen,
-				      char *buffer, int bufflen,
-				      enum sip_header_pos pos)
-{
-	unsigned int matchlen, matchoff;
-
-	if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, pos) <= 0)
-		return 0;
-
-	if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
-				      matchoff, matchlen, buffer, bufflen))
-		return 0;
-
-	/* We need to reload this. Thanks Patrick. */
-	*dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
-	return 1;
-}
-
-static int mangle_content_len(struct sk_buff **pskb,
-			      enum ip_conntrack_info ctinfo,
-			      struct ip_conntrack *ct,
-			      const char *dptr)
-{
-	unsigned int dataoff, matchoff, matchlen;
-	char buffer[sizeof("65536")];
-	int bufflen;
-
-	dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
-
-	/* Get actual SDP lenght */
-	if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff,
-			    &matchlen, POS_SDP_HEADER) > 0) {
-
-		/* since ct_sip_get_info() give us a pointer passing 'v='
-		   we need to add 2 bytes in this count. */
-		int c_len = (*pskb)->len - dataoff - matchoff + 2;
-
-		/* Now, update SDP lenght */
-		if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff,
-				    &matchlen, POS_CONTENT) > 0) {
-
-			bufflen = sprintf(buffer, "%u", c_len);
-
-			return ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
-							matchoff, matchlen,
-							buffer, bufflen);
-		}
-	}
-	return 0;
-}
-
-static unsigned int mangle_sdp(struct sk_buff **pskb,
-			       enum ip_conntrack_info ctinfo,
-			       struct ip_conntrack *ct,
-			       __be32 newip, u_int16_t port,
-			       const char *dptr)
-{
-	char buffer[sizeof("nnn.nnn.nnn.nnn")];
-	unsigned int dataoff, bufflen;
-
-	dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
-
-	/* Mangle owner and contact info. */
-	bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip));
-	if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,
-			       buffer, bufflen, POS_OWNER))
-		return 0;
-
-	if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,
-			       buffer, bufflen, POS_CONNECTION))
-		return 0;
-
-	/* Mangle media port. */
-	bufflen = sprintf(buffer, "%u", port);
-	if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,
-			       buffer, bufflen, POS_MEDIA))
-		return 0;
-
-	return mangle_content_len(pskb, ctinfo, ct, dptr);
-}
-
-/* So, this packet has hit the connection tracking matching code.
-   Mangle it, and change the expectation to match the new version. */
-static unsigned int ip_nat_sdp(struct sk_buff **pskb,
-			       enum ip_conntrack_info ctinfo,
-			       struct ip_conntrack_expect *exp,
-			       const char *dptr)
-{
-	struct ip_conntrack *ct = exp->master;
-	enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
-	__be32 newip;
-	u_int16_t port;
-
-	DEBUGP("ip_nat_sdp():\n");
-
-	/* Connection will come from reply */
-	newip = ct->tuplehash[!dir].tuple.dst.ip;
-
-	exp->tuple.dst.ip = newip;
-	exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port;
-	exp->dir = !dir;
-
-	/* When you see the packet, we need to NAT it the same as the
-	   this one. */
-	exp->expectfn = ip_nat_follow_master;
-
-	/* Try to get same port: if not, try to change it. */
-	for (port = ntohs(exp->saved_proto.udp.port); port != 0; port++) {
-		exp->tuple.dst.u.udp.port = htons(port);
-		if (ip_conntrack_expect_related(exp) == 0)
-			break;
-	}
-
-	if (port == 0)
-		return NF_DROP;
-
-	if (!mangle_sdp(pskb, ctinfo, ct, newip, port, dptr)) {
-		ip_conntrack_unexpect_related(exp);
-		return NF_DROP;
-	}
-	return NF_ACCEPT;
-}
-
-static void __exit fini(void)
-{
-	rcu_assign_pointer(ip_nat_sip_hook, NULL);
-	rcu_assign_pointer(ip_nat_sdp_hook, NULL);
-	synchronize_rcu();
-}
-
-static int __init init(void)
-{
-	BUG_ON(rcu_dereference(ip_nat_sip_hook));
-	BUG_ON(rcu_dereference(ip_nat_sdp_hook));
-	rcu_assign_pointer(ip_nat_sip_hook, ip_nat_sip);
-	rcu_assign_pointer(ip_nat_sdp_hook, ip_nat_sdp);
-	return 0;
-}
-
-module_init(init);
-module_exit(fini);
diff --git a/net/ipv4/netfilter/ip_nat_snmp_basic.c b/net/ipv4/netfilter/ip_nat_snmp_basic.c
deleted file mode 100644
index e41d0ef..0000000
--- a/net/ipv4/netfilter/ip_nat_snmp_basic.c
+++ /dev/null
@@ -1,1333 +0,0 @@
-/*
- * ip_nat_snmp_basic.c
- *
- * Basic SNMP Application Layer Gateway
- *
- * This IP NAT module is intended for use with SNMP network
- * discovery and monitoring applications where target networks use
- * conflicting private address realms.
- *
- * Static NAT is used to remap the networks from the view of the network
- * management system at the IP layer, and this module remaps some application
- * layer addresses to match.
- *
- * The simplest form of ALG is performed, where only tagged IP addresses
- * are modified.  The module does not need to be MIB aware and only scans
- * messages at the ASN.1/BER level.
- *
- * Currently, only SNMPv1 and SNMPv2 are supported.
- *
- * More information on ALG and associated issues can be found in
- * RFC 2962
- *
- * The ASB.1/BER parsing code is derived from the gxsnmp package by Gregory
- * McLean & Jochen Friedrich, stripped down for use in the kernel.
- *
- * Copyright (c) 2000 RP Internet (www.rpi.net.au).
- *
- * 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
- *
- * Author: James Morris <jmorris@intercode.com.au>
- *
- * Updates:
- * 2000-08-06: Convert to new helper API (Harald Welte).
- *
- */
-#include <linux/in.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/moduleparam.h>
-#include <linux/netfilter_ipv4.h>
-#include <linux/netfilter_ipv4/ip_nat.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-#include <linux/netfilter_ipv4/ip_nat_helper.h>
-#include <linux/ip.h>
-#include <linux/udp.h>
-#include <net/checksum.h>
-#include <net/udp.h>
-#include <asm/uaccess.h>
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>");
-MODULE_DESCRIPTION("Basic SNMP Application Layer Gateway");
-
-#define SNMP_PORT 161
-#define SNMP_TRAP_PORT 162
-#define NOCT1(n) (*(u8 *)n)
-
-static int debug;
-static DEFINE_SPINLOCK(snmp_lock);
-
-/*
- * Application layer address mapping mimics the NAT mapping, but
- * only for the first octet in this case (a more flexible system
- * can be implemented if needed).
- */
-struct oct1_map
-{
-	u_int8_t from;
-	u_int8_t to;
-};
-
-
-/*****************************************************************************
- *
- * Basic ASN.1 decoding routines (gxsnmp author Dirk Wisse)
- *
- *****************************************************************************/
-
-/* Class */
-#define ASN1_UNI	0	/* Universal */
-#define ASN1_APL	1	/* Application */
-#define ASN1_CTX	2	/* Context */
-#define ASN1_PRV	3	/* Private */
-
-/* Tag */
-#define ASN1_EOC	0	/* End Of Contents */
-#define ASN1_BOL	1	/* Boolean */
-#define ASN1_INT	2	/* Integer */
-#define ASN1_BTS	3	/* Bit String */
-#define ASN1_OTS	4	/* Octet String */
-#define ASN1_NUL	5	/* Null */
-#define ASN1_OJI	6	/* Object Identifier  */
-#define ASN1_OJD	7	/* Object Description */
-#define ASN1_EXT	8	/* External */
-#define ASN1_SEQ	16	/* Sequence */
-#define ASN1_SET	17	/* Set */
-#define ASN1_NUMSTR	18	/* Numerical String */
-#define ASN1_PRNSTR	19	/* Printable String */
-#define ASN1_TEXSTR	20	/* Teletext String */
-#define ASN1_VIDSTR	21	/* Video String */
-#define ASN1_IA5STR	22	/* IA5 String */
-#define ASN1_UNITIM	23	/* Universal Time */
-#define ASN1_GENTIM	24	/* General Time */
-#define ASN1_GRASTR	25	/* Graphical String */
-#define ASN1_VISSTR	26	/* Visible String */
-#define ASN1_GENSTR	27	/* General String */
-
-/* Primitive / Constructed methods*/
-#define ASN1_PRI	0	/* Primitive */
-#define ASN1_CON	1	/* Constructed */
-
-/*
- * Error codes.
- */
-#define ASN1_ERR_NOERROR		0
-#define ASN1_ERR_DEC_EMPTY		2
-#define ASN1_ERR_DEC_EOC_MISMATCH	3
-#define ASN1_ERR_DEC_LENGTH_MISMATCH	4
-#define ASN1_ERR_DEC_BADVALUE		5
-
-/*
- * ASN.1 context.
- */
-struct asn1_ctx
-{
-	int error;			/* Error condition */
-	unsigned char *pointer;		/* Octet just to be decoded */
-	unsigned char *begin;		/* First octet */
-	unsigned char *end;		/* Octet after last octet */
-};
-
-/*
- * Octet string (not null terminated)
- */
-struct asn1_octstr
-{
-	unsigned char *data;
-	unsigned int len;
-};
-
-static void asn1_open(struct asn1_ctx *ctx,
-		      unsigned char *buf,
-		      unsigned int len)
-{
-	ctx->begin = buf;
-	ctx->end = buf + len;
-	ctx->pointer = buf;
-	ctx->error = ASN1_ERR_NOERROR;
-}
-
-static unsigned char asn1_octet_decode(struct asn1_ctx *ctx, unsigned char *ch)
-{
-	if (ctx->pointer >= ctx->end) {
-		ctx->error = ASN1_ERR_DEC_EMPTY;
-		return 0;
-	}
-	*ch = *(ctx->pointer)++;
-	return 1;
-}
-
-static unsigned char asn1_tag_decode(struct asn1_ctx *ctx, unsigned int *tag)
-{
-	unsigned char ch;
-
-	*tag = 0;
-
-	do
-	{
-		if (!asn1_octet_decode(ctx, &ch))
-			return 0;
-		*tag <<= 7;
-		*tag |= ch & 0x7F;
-	} while ((ch & 0x80) == 0x80);
-	return 1;
-}
-
-static unsigned char asn1_id_decode(struct asn1_ctx *ctx,
-				    unsigned int *cls,
-				    unsigned int *con,
-				    unsigned int *tag)
-{
-	unsigned char ch;
-
-	if (!asn1_octet_decode(ctx, &ch))
-		return 0;
-
-	*cls = (ch & 0xC0) >> 6;
-	*con = (ch & 0x20) >> 5;
-	*tag = (ch & 0x1F);
-
-	if (*tag == 0x1F) {
-		if (!asn1_tag_decode(ctx, tag))
-			return 0;
-	}
-	return 1;
-}
-
-static unsigned char asn1_length_decode(struct asn1_ctx *ctx,
-					unsigned int *def,
-					unsigned int *len)
-{
-	unsigned char ch, cnt;
-
-	if (!asn1_octet_decode(ctx, &ch))
-		return 0;
-
-	if (ch == 0x80)
-		*def = 0;
-	else {
-		*def = 1;
-
-		if (ch < 0x80)
-			*len = ch;
-		else {
-			cnt = (unsigned char) (ch & 0x7F);
-			*len = 0;
-
-			while (cnt > 0) {
-				if (!asn1_octet_decode(ctx, &ch))
-					return 0;
-				*len <<= 8;
-				*len |= ch;
-				cnt--;
-			}
-		}
-	}
-	return 1;
-}
-
-static unsigned char asn1_header_decode(struct asn1_ctx *ctx,
-					unsigned char **eoc,
-					unsigned int *cls,
-					unsigned int *con,
-					unsigned int *tag)
-{
-	unsigned int def, len;
-
-	if (!asn1_id_decode(ctx, cls, con, tag))
-		return 0;
-
-	def = len = 0;
-	if (!asn1_length_decode(ctx, &def, &len))
-		return 0;
-
-	if (def)
-		*eoc = ctx->pointer + len;
-	else
-		*eoc = NULL;
-	return 1;
-}
-
-static unsigned char asn1_eoc_decode(struct asn1_ctx *ctx, unsigned char *eoc)
-{
-	unsigned char ch;
-
-	if (eoc == 0) {
-		if (!asn1_octet_decode(ctx, &ch))
-			return 0;
-
-		if (ch != 0x00) {
-			ctx->error = ASN1_ERR_DEC_EOC_MISMATCH;
-			return 0;
-		}
-
-		if (!asn1_octet_decode(ctx, &ch))
-			return 0;
-
-		if (ch != 0x00) {
-			ctx->error = ASN1_ERR_DEC_EOC_MISMATCH;
-			return 0;
-		}
-		return 1;
-	} else {
-		if (ctx->pointer != eoc) {
-			ctx->error = ASN1_ERR_DEC_LENGTH_MISMATCH;
-			return 0;
-		}
-		return 1;
-	}
-}
-
-static unsigned char asn1_null_decode(struct asn1_ctx *ctx, unsigned char *eoc)
-{
-	ctx->pointer = eoc;
-	return 1;
-}
-
-static unsigned char asn1_long_decode(struct asn1_ctx *ctx,
-				      unsigned char *eoc,
-				      long *integer)
-{
-	unsigned char ch;
-	unsigned int  len;
-
-	if (!asn1_octet_decode(ctx, &ch))
-		return 0;
-
-	*integer = (signed char) ch;
-	len = 1;
-
-	while (ctx->pointer < eoc) {
-		if (++len > sizeof (long)) {
-			ctx->error = ASN1_ERR_DEC_BADVALUE;
-			return 0;
-		}
-
-		if (!asn1_octet_decode(ctx, &ch))
-			return 0;
-
-		*integer <<= 8;
-		*integer |= ch;
-	}
-	return 1;
-}
-
-static unsigned char asn1_uint_decode(struct asn1_ctx *ctx,
-				      unsigned char *eoc,
-				      unsigned int *integer)
-{
-	unsigned char ch;
-	unsigned int  len;
-
-	if (!asn1_octet_decode(ctx, &ch))
-		return 0;
-
-	*integer = ch;
-	if (ch == 0) len = 0;
-	else len = 1;
-
-	while (ctx->pointer < eoc) {
-		if (++len > sizeof (unsigned int)) {
-			ctx->error = ASN1_ERR_DEC_BADVALUE;
-			return 0;
-		}
-
-		if (!asn1_octet_decode(ctx, &ch))
-			return 0;
-
-		*integer <<= 8;
-		*integer |= ch;
-	}
-	return 1;
-}
-
-static unsigned char asn1_ulong_decode(struct asn1_ctx *ctx,
-				       unsigned char *eoc,
-				       unsigned long *integer)
-{
-	unsigned char ch;
-	unsigned int  len;
-
-	if (!asn1_octet_decode(ctx, &ch))
-		return 0;
-
-	*integer = ch;
-	if (ch == 0) len = 0;
-	else len = 1;
-
-	while (ctx->pointer < eoc) {
-		if (++len > sizeof (unsigned long)) {
-			ctx->error = ASN1_ERR_DEC_BADVALUE;
-			return 0;
-		}
-
-		if (!asn1_octet_decode(ctx, &ch))
-			return 0;
-
-		*integer <<= 8;
-		*integer |= ch;
-	}
-	return 1;
-}
-
-static unsigned char asn1_octets_decode(struct asn1_ctx *ctx,
-					unsigned char *eoc,
-					unsigned char **octets,
-					unsigned int *len)
-{
-	unsigned char *ptr;
-
-	*len = 0;
-
-	*octets = kmalloc(eoc - ctx->pointer, GFP_ATOMIC);
-	if (*octets == NULL) {
-		if (net_ratelimit())
-			printk("OOM in bsalg (%d)\n", __LINE__);
-		return 0;
-	}
-
-	ptr = *octets;
-	while (ctx->pointer < eoc) {
-		if (!asn1_octet_decode(ctx, (unsigned char *)ptr++)) {
-			kfree(*octets);
-			*octets = NULL;
-			return 0;
-		}
-		(*len)++;
-	}
-	return 1;
-}
-
-static unsigned char asn1_subid_decode(struct asn1_ctx *ctx,
-				       unsigned long *subid)
-{
-	unsigned char ch;
-
-	*subid = 0;
-
-	do {
-		if (!asn1_octet_decode(ctx, &ch))
-			return 0;
-
-		*subid <<= 7;
-		*subid |= ch & 0x7F;
-	} while ((ch & 0x80) == 0x80);
-	return 1;
-}
-
-static unsigned char asn1_oid_decode(struct asn1_ctx *ctx,
-				     unsigned char *eoc,
-				     unsigned long **oid,
-				     unsigned int *len)
-{
-	unsigned long subid;
-	unsigned int  size;
-	unsigned long *optr;
-
-	size = eoc - ctx->pointer + 1;
-	*oid = kmalloc(size * sizeof(unsigned long), GFP_ATOMIC);
-	if (*oid == NULL) {
-		if (net_ratelimit())
-			printk("OOM in bsalg (%d)\n", __LINE__);
-		return 0;
-	}
-
-	optr = *oid;
-
-	if (!asn1_subid_decode(ctx, &subid)) {
-		kfree(*oid);
-		*oid = NULL;
-		return 0;
-	}
-
-	if (subid < 40) {
-		optr [0] = 0;
-		optr [1] = subid;
-	} else if (subid < 80) {
-		optr [0] = 1;
-		optr [1] = subid - 40;
-	} else {
-		optr [0] = 2;
-		optr [1] = subid - 80;
-	}
-
-	*len = 2;
-	optr += 2;
-
-	while (ctx->pointer < eoc) {
-		if (++(*len) > size) {
-			ctx->error = ASN1_ERR_DEC_BADVALUE;
-			kfree(*oid);
-			*oid = NULL;
-			return 0;
-		}
-
-		if (!asn1_subid_decode(ctx, optr++)) {
-			kfree(*oid);
-			*oid = NULL;
-			return 0;
-		}
-	}
-	return 1;
-}
-
-/*****************************************************************************
- *
- * SNMP decoding routines (gxsnmp author Dirk Wisse)
- *
- *****************************************************************************/
-
-/* SNMP Versions */
-#define SNMP_V1				0
-#define SNMP_V2C			1
-#define SNMP_V2				2
-#define SNMP_V3				3
-
-/* Default Sizes */
-#define SNMP_SIZE_COMM			256
-#define SNMP_SIZE_OBJECTID		128
-#define SNMP_SIZE_BUFCHR		256
-#define SNMP_SIZE_BUFINT		128
-#define SNMP_SIZE_SMALLOBJECTID		16
-
-/* Requests */
-#define SNMP_PDU_GET			0
-#define SNMP_PDU_NEXT			1
-#define SNMP_PDU_RESPONSE		2
-#define SNMP_PDU_SET			3
-#define SNMP_PDU_TRAP1			4
-#define SNMP_PDU_BULK			5
-#define SNMP_PDU_INFORM			6
-#define SNMP_PDU_TRAP2			7
-
-/* Errors */
-#define SNMP_NOERROR			0
-#define SNMP_TOOBIG			1
-#define SNMP_NOSUCHNAME			2
-#define SNMP_BADVALUE			3
-#define SNMP_READONLY			4
-#define SNMP_GENERROR			5
-#define SNMP_NOACCESS			6
-#define SNMP_WRONGTYPE			7
-#define SNMP_WRONGLENGTH		8
-#define SNMP_WRONGENCODING		9
-#define SNMP_WRONGVALUE			10
-#define SNMP_NOCREATION			11
-#define SNMP_INCONSISTENTVALUE		12
-#define SNMP_RESOURCEUNAVAILABLE	13
-#define SNMP_COMMITFAILED		14
-#define SNMP_UNDOFAILED			15
-#define SNMP_AUTHORIZATIONERROR		16
-#define SNMP_NOTWRITABLE		17
-#define SNMP_INCONSISTENTNAME		18
-
-/* General SNMP V1 Traps */
-#define SNMP_TRAP_COLDSTART		0
-#define SNMP_TRAP_WARMSTART		1
-#define SNMP_TRAP_LINKDOWN		2
-#define SNMP_TRAP_LINKUP		3
-#define SNMP_TRAP_AUTFAILURE		4
-#define SNMP_TRAP_EQPNEIGHBORLOSS	5
-#define SNMP_TRAP_ENTSPECIFIC		6
-
-/* SNMPv1 Types */
-#define SNMP_NULL                0
-#define SNMP_INTEGER             1    /* l  */
-#define SNMP_OCTETSTR            2    /* c  */
-#define SNMP_DISPLAYSTR          2    /* c  */
-#define SNMP_OBJECTID            3    /* ul */
-#define SNMP_IPADDR              4    /* uc */
-#define SNMP_COUNTER             5    /* ul */
-#define SNMP_GAUGE               6    /* ul */
-#define SNMP_TIMETICKS           7    /* ul */
-#define SNMP_OPAQUE              8    /* c  */
-
-/* Additional SNMPv2 Types */
-#define SNMP_UINTEGER            5    /* ul */
-#define SNMP_BITSTR              9    /* uc */
-#define SNMP_NSAP               10    /* uc */
-#define SNMP_COUNTER64          11    /* ul */
-#define SNMP_NOSUCHOBJECT       12
-#define SNMP_NOSUCHINSTANCE     13
-#define SNMP_ENDOFMIBVIEW       14
-
-union snmp_syntax
-{
-	unsigned char uc[0];	/* 8 bit unsigned */
-	char c[0];		/* 8 bit signed */
-	unsigned long ul[0];	/* 32 bit unsigned */
-	long l[0];		/* 32 bit signed */
-};
-
-struct snmp_object
-{
-	unsigned long *id;
-	unsigned int id_len;
-	unsigned short type;
-	unsigned int syntax_len;
-	union snmp_syntax syntax;
-};
-
-struct snmp_request
-{
-	unsigned long id;
-	unsigned int error_status;
-	unsigned int error_index;
-};
-
-struct snmp_v1_trap
-{
-	unsigned long *id;
-	unsigned int id_len;
-	unsigned long ip_address;	/* pointer  */
-	unsigned int general;
-	unsigned int specific;
-	unsigned long time;
-};
-
-/* SNMP types */
-#define SNMP_IPA    0
-#define SNMP_CNT    1
-#define SNMP_GGE    2
-#define SNMP_TIT    3
-#define SNMP_OPQ    4
-#define SNMP_C64    6
-
-/* SNMP errors */
-#define SERR_NSO    0
-#define SERR_NSI    1
-#define SERR_EOM    2
-
-static inline void mangle_address(unsigned char *begin,
-				  unsigned char *addr,
-				  const struct oct1_map *map,
-				  __sum16 *check);
-struct snmp_cnv
-{
-	unsigned int class;
-	unsigned int tag;
-	int syntax;
-};
-
-static struct snmp_cnv snmp_conv [] =
-{
-	{ASN1_UNI, ASN1_NUL, SNMP_NULL},
-	{ASN1_UNI, ASN1_INT, SNMP_INTEGER},
-	{ASN1_UNI, ASN1_OTS, SNMP_OCTETSTR},
-	{ASN1_UNI, ASN1_OTS, SNMP_DISPLAYSTR},
-	{ASN1_UNI, ASN1_OJI, SNMP_OBJECTID},
-	{ASN1_APL, SNMP_IPA, SNMP_IPADDR},
-	{ASN1_APL, SNMP_CNT, SNMP_COUNTER},	/* Counter32 */
-	{ASN1_APL, SNMP_GGE, SNMP_GAUGE},	/* Gauge32 == Unsigned32  */
-	{ASN1_APL, SNMP_TIT, SNMP_TIMETICKS},
-	{ASN1_APL, SNMP_OPQ, SNMP_OPAQUE},
-
-	/* SNMPv2 data types and errors */
-	{ASN1_UNI, ASN1_BTS, SNMP_BITSTR},
-	{ASN1_APL, SNMP_C64, SNMP_COUNTER64},
-	{ASN1_CTX, SERR_NSO, SNMP_NOSUCHOBJECT},
-	{ASN1_CTX, SERR_NSI, SNMP_NOSUCHINSTANCE},
-	{ASN1_CTX, SERR_EOM, SNMP_ENDOFMIBVIEW},
-	{0,       0,       -1}
-};
-
-static unsigned char snmp_tag_cls2syntax(unsigned int tag,
-					 unsigned int cls,
-					 unsigned short *syntax)
-{
-	struct snmp_cnv *cnv;
-
-	cnv = snmp_conv;
-
-	while (cnv->syntax != -1) {
-		if (cnv->tag == tag && cnv->class == cls) {
-			*syntax = cnv->syntax;
-			return 1;
-		}
-		cnv++;
-	}
-	return 0;
-}
-
-static unsigned char snmp_object_decode(struct asn1_ctx *ctx,
-					struct snmp_object **obj)
-{
-	unsigned int cls, con, tag, len, idlen;
-	unsigned short type;
-	unsigned char *eoc, *end, *p;
-	unsigned long *lp, *id;
-	unsigned long ul;
-	long l;
-
-	*obj = NULL;
-	id = NULL;
-
-	if (!asn1_header_decode(ctx, &eoc, &cls, &con, &tag))
-		return 0;
-
-	if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SEQ)
-		return 0;
-
-	if (!asn1_header_decode(ctx, &end, &cls, &con, &tag))
-		return 0;
-
-	if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_OJI)
-		return 0;
-
-	if (!asn1_oid_decode(ctx, end, &id, &idlen))
-		return 0;
-
-	if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) {
-		kfree(id);
-		return 0;
-	}
-
-	if (con != ASN1_PRI) {
-		kfree(id);
-		return 0;
-	}
-
-	type = 0;
-	if (!snmp_tag_cls2syntax(tag, cls, &type)) {
-		kfree(id);
-		return 0;
-	}
-
-	l = 0;
-	switch (type) {
-		case SNMP_INTEGER:
-			len = sizeof(long);
-			if (!asn1_long_decode(ctx, end, &l)) {
-				kfree(id);
-				return 0;
-			}
-			*obj = kmalloc(sizeof(struct snmp_object) + len,
-				       GFP_ATOMIC);
-			if (*obj == NULL) {
-				kfree(id);
-				if (net_ratelimit())
-					printk("OOM in bsalg (%d)\n", __LINE__);
-				return 0;
-			}
-			(*obj)->syntax.l[0] = l;
-			break;
-		case SNMP_OCTETSTR:
-		case SNMP_OPAQUE:
-			if (!asn1_octets_decode(ctx, end, &p, &len)) {
-				kfree(id);
-				return 0;
-			}
-			*obj = kmalloc(sizeof(struct snmp_object) + len,
-				       GFP_ATOMIC);
-			if (*obj == NULL) {
-				kfree(id);
-				if (net_ratelimit())
-					printk("OOM in bsalg (%d)\n", __LINE__);
-				return 0;
-			}
-			memcpy((*obj)->syntax.c, p, len);
-			kfree(p);
-			break;
-		case SNMP_NULL:
-		case SNMP_NOSUCHOBJECT:
-		case SNMP_NOSUCHINSTANCE:
-		case SNMP_ENDOFMIBVIEW:
-			len = 0;
-			*obj = kmalloc(sizeof(struct snmp_object), GFP_ATOMIC);
-			if (*obj == NULL) {
-				kfree(id);
-				if (net_ratelimit())
-					printk("OOM in bsalg (%d)\n", __LINE__);
-				return 0;
-			}
-			if (!asn1_null_decode(ctx, end)) {
-				kfree(id);
-				kfree(*obj);
-				*obj = NULL;
-				return 0;
-			}
-			break;
-		case SNMP_OBJECTID:
-			if (!asn1_oid_decode(ctx, end, (unsigned long **)&lp, &len)) {
-				kfree(id);
-				return 0;
-			}
-			len *= sizeof(unsigned long);
-			*obj = kmalloc(sizeof(struct snmp_object) + len, GFP_ATOMIC);
-			if (*obj == NULL) {
-				kfree(lp);
-				kfree(id);
-				if (net_ratelimit())
-					printk("OOM in bsalg (%d)\n", __LINE__);
-				return 0;
-			}
-			memcpy((*obj)->syntax.ul, lp, len);
-			kfree(lp);
-			break;
-		case SNMP_IPADDR:
-			if (!asn1_octets_decode(ctx, end, &p, &len)) {
-				kfree(id);
-				return 0;
-			}
-			if (len != 4) {
-				kfree(p);
-				kfree(id);
-				return 0;
-			}
-			*obj = kmalloc(sizeof(struct snmp_object) + len, GFP_ATOMIC);
-			if (*obj == NULL) {
-				kfree(p);
-				kfree(id);
-				if (net_ratelimit())
-					printk("OOM in bsalg (%d)\n", __LINE__);
-				return 0;
-			}
-			memcpy((*obj)->syntax.uc, p, len);
-			kfree(p);
-			break;
-		case SNMP_COUNTER:
-		case SNMP_GAUGE:
-		case SNMP_TIMETICKS:
-			len = sizeof(unsigned long);
-			if (!asn1_ulong_decode(ctx, end, &ul)) {
-				kfree(id);
-				return 0;
-			}
-			*obj = kmalloc(sizeof(struct snmp_object) + len, GFP_ATOMIC);
-			if (*obj == NULL) {
-				kfree(id);
-				if (net_ratelimit())
-					printk("OOM in bsalg (%d)\n", __LINE__);
-				return 0;
-			}
-			(*obj)->syntax.ul[0] = ul;
-			break;
-		default:
-			kfree(id);
-			return 0;
-	}
-
-	(*obj)->syntax_len = len;
-	(*obj)->type = type;
-	(*obj)->id = id;
-	(*obj)->id_len = idlen;
-
-	if (!asn1_eoc_decode(ctx, eoc)) {
-		kfree(id);
-		kfree(*obj);
-		*obj = NULL;
-		return 0;
-	}
-	return 1;
-}
-
-static unsigned char snmp_request_decode(struct asn1_ctx *ctx,
-					 struct snmp_request *request)
-{
-	unsigned int cls, con, tag;
-	unsigned char *end;
-
-	if (!asn1_header_decode(ctx, &end, &cls, &con, &tag))
-		return 0;
-
-	if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT)
-		return 0;
-
-	if (!asn1_ulong_decode(ctx, end, &request->id))
-		return 0;
-
-	if (!asn1_header_decode(ctx, &end, &cls, &con, &tag))
-		return 0;
-
-	if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT)
-		return 0;
-
-	if (!asn1_uint_decode(ctx, end, &request->error_status))
-		return 0;
-
-	if (!asn1_header_decode(ctx, &end, &cls, &con, &tag))
-		return 0;
-
-	if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT)
-		return 0;
-
-	if (!asn1_uint_decode(ctx, end, &request->error_index))
-		return 0;
-
-	return 1;
-}
-
-/*
- * Fast checksum update for possibly oddly-aligned UDP byte, from the
- * code example in the draft.
- */
-static void fast_csum(__sum16 *csum,
-		      const unsigned char *optr,
-		      const unsigned char *nptr,
-		      int offset)
-{
-	unsigned char s[4];
-
-	if (offset & 1) {
-		s[0] = s[2] = 0;
-		s[1] = ~*optr;
-		s[3] = *nptr;
-	} else {
-		s[1] = s[3] = 0;
-		s[0] = ~*optr;
-		s[2] = *nptr;
-	}
-
-	*csum = csum_fold(csum_partial(s, 4, ~csum_unfold(*csum)));
-}
-
-/*
- * Mangle IP address.
- * 	- begin points to the start of the snmp messgae
- *      - addr points to the start of the address
- */
-static inline void mangle_address(unsigned char *begin,
-				  unsigned char *addr,
-				  const struct oct1_map *map,
-				  __sum16 *check)
-{
-	if (map->from == NOCT1(addr)) {
-		u_int32_t old;
-
-		if (debug)
-			memcpy(&old, (unsigned char *)addr, sizeof(old));
-
-		*addr = map->to;
-
-		/* Update UDP checksum if being used */
-		if (*check) {
-			fast_csum(check,
-				  &map->from, &map->to, addr - begin);
-		}
-
-		if (debug)
-			printk(KERN_DEBUG "bsalg: mapped %u.%u.%u.%u to "
-			       "%u.%u.%u.%u\n", NIPQUAD(old), NIPQUAD(*addr));
-	}
-}
-
-static unsigned char snmp_trap_decode(struct asn1_ctx *ctx,
-				      struct snmp_v1_trap *trap,
-				      const struct oct1_map *map,
-				      __sum16 *check)
-{
-	unsigned int cls, con, tag, len;
-	unsigned char *end;
-
-	if (!asn1_header_decode(ctx, &end, &cls, &con, &tag))
-		return 0;
-
-	if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_OJI)
-		return 0;
-
-	if (!asn1_oid_decode(ctx, end, &trap->id, &trap->id_len))
-		return 0;
-
-	if (!asn1_header_decode(ctx, &end, &cls, &con, &tag))
-		goto err_id_free;
-
-	if (!((cls == ASN1_APL && con == ASN1_PRI && tag == SNMP_IPA) ||
-	      (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS)))
-		goto err_id_free;
-
-	if (!asn1_octets_decode(ctx, end, (unsigned char **)&trap->ip_address, &len))
-		goto err_id_free;
-
-	/* IPv4 only */
-	if (len != 4)
-		goto err_addr_free;
-
-	mangle_address(ctx->begin, ctx->pointer - 4, map, check);
-
-	if (!asn1_header_decode(ctx, &end, &cls, &con, &tag))
-		goto err_addr_free;
-
-	if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT)
-		goto err_addr_free;
-
-	if (!asn1_uint_decode(ctx, end, &trap->general))
-		goto err_addr_free;
-
-	if (!asn1_header_decode(ctx, &end, &cls, &con, &tag))
-		goto err_addr_free;
-
-	if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT)
-		goto err_addr_free;
-
-	if (!asn1_uint_decode(ctx, end, &trap->specific))
-		goto err_addr_free;
-
-	if (!asn1_header_decode(ctx, &end, &cls, &con, &tag))
-		goto err_addr_free;
-
-	if (!((cls == ASN1_APL && con == ASN1_PRI && tag == SNMP_TIT) ||
-	      (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_INT)))
-		goto err_addr_free;
-
-	if (!asn1_ulong_decode(ctx, end, &trap->time))
-		goto err_addr_free;
-
-	return 1;
-
-err_addr_free:
-	kfree((unsigned long *)trap->ip_address);
-
-err_id_free:
-	kfree(trap->id);
-
-	return 0;
-}
-
-/*****************************************************************************
- *
- * Misc. routines
- *
- *****************************************************************************/
-
-static void hex_dump(unsigned char *buf, size_t len)
-{
-	size_t i;
-
-	for (i = 0; i < len; i++) {
-		if (i && !(i % 16))
-			printk("\n");
-		printk("%02x ", *(buf + i));
-	}
-	printk("\n");
-}
-
-/*
- * Parse and mangle SNMP message according to mapping.
- * (And this is the fucking 'basic' method).
- */
-static int snmp_parse_mangle(unsigned char *msg,
-			     u_int16_t len,
-			     const struct oct1_map *map,
-			     __sum16 *check)
-{
-	unsigned char *eoc, *end;
-	unsigned int cls, con, tag, vers, pdutype;
-	struct asn1_ctx ctx;
-	struct asn1_octstr comm;
-	struct snmp_object **obj;
-
-	if (debug > 1)
-		hex_dump(msg, len);
-
-	asn1_open(&ctx, msg, len);
-
-	/*
-	 * Start of SNMP message.
-	 */
-	if (!asn1_header_decode(&ctx, &eoc, &cls, &con, &tag))
-		return 0;
-	if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SEQ)
-		return 0;
-
-	/*
-	 * Version 1 or 2 handled.
-	 */
-	if (!asn1_header_decode(&ctx, &end, &cls, &con, &tag))
-		return 0;
-	if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT)
-		return 0;
-	if (!asn1_uint_decode (&ctx, end, &vers))
-		return 0;
-	if (debug > 1)
-		printk(KERN_DEBUG "bsalg: snmp version: %u\n", vers + 1);
-	if (vers > 1)
-		return 1;
-
-	/*
-	 * Community.
-	 */
-	if (!asn1_header_decode (&ctx, &end, &cls, &con, &tag))
-		return 0;
-	if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_OTS)
-		return 0;
-	if (!asn1_octets_decode(&ctx, end, &comm.data, &comm.len))
-		return 0;
-	if (debug > 1) {
-		unsigned int i;
-
-		printk(KERN_DEBUG "bsalg: community: ");
-		for (i = 0; i < comm.len; i++)
-			printk("%c", comm.data[i]);
-		printk("\n");
-	}
-	kfree(comm.data);
-
-	/*
-	 * PDU type
-	 */
-	if (!asn1_header_decode(&ctx, &eoc, &cls, &con, &pdutype))
-		return 0;
-	if (cls != ASN1_CTX || con != ASN1_CON)
-		return 0;
-	if (debug > 1) {
-		unsigned char *pdus[] = {
-			[SNMP_PDU_GET] = "get",
-			[SNMP_PDU_NEXT] = "get-next",
-			[SNMP_PDU_RESPONSE] = "response",
-			[SNMP_PDU_SET] = "set",
-			[SNMP_PDU_TRAP1] = "trapv1",
-			[SNMP_PDU_BULK] = "bulk",
-			[SNMP_PDU_INFORM] = "inform",
-			[SNMP_PDU_TRAP2] = "trapv2"
-		};
-
-		if (pdutype > SNMP_PDU_TRAP2)
-			printk(KERN_DEBUG "bsalg: bad pdu type %u\n", pdutype);
-		else
-			printk(KERN_DEBUG "bsalg: pdu: %s\n", pdus[pdutype]);
-	}
-	if (pdutype != SNMP_PDU_RESPONSE &&
-	    pdutype != SNMP_PDU_TRAP1 && pdutype != SNMP_PDU_TRAP2)
-		return 1;
-
-	/*
-	 * Request header or v1 trap
-	 */
-	if (pdutype == SNMP_PDU_TRAP1) {
-		struct snmp_v1_trap trap;
-		unsigned char ret = snmp_trap_decode(&ctx, &trap, map, check);
-
-		if (ret) {
-			kfree(trap.id);
-			kfree((unsigned long *)trap.ip_address);
-		} else
-			return ret;
-
-	} else {
-		struct snmp_request req;
-
-		if (!snmp_request_decode(&ctx, &req))
-			return 0;
-
-		if (debug > 1)
-			printk(KERN_DEBUG "bsalg: request: id=0x%lx error_status=%u "
-			"error_index=%u\n", req.id, req.error_status,
-			req.error_index);
-	}
-
-	/*
-	 * Loop through objects, look for IP addresses to mangle.
-	 */
-	if (!asn1_header_decode(&ctx, &eoc, &cls, &con, &tag))
-		return 0;
-
-	if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SEQ)
-		return 0;
-
-	obj = kmalloc(sizeof(struct snmp_object), GFP_ATOMIC);
-	if (obj == NULL) {
-		if (net_ratelimit())
-			printk(KERN_WARNING "OOM in bsalg(%d)\n", __LINE__);
-		return 0;
-	}
-
-	while (!asn1_eoc_decode(&ctx, eoc)) {
-		unsigned int i;
-
-		if (!snmp_object_decode(&ctx, obj)) {
-			if (*obj) {
-				kfree((*obj)->id);
-				kfree(*obj);
-			}
-			kfree(obj);
-			return 0;
-		}
-
-		if (debug > 1) {
-			printk(KERN_DEBUG "bsalg: object: ");
-			for (i = 0; i < (*obj)->id_len; i++) {
-				if (i > 0)
-					printk(".");
-				printk("%lu", (*obj)->id[i]);
-			}
-			printk(": type=%u\n", (*obj)->type);
-
-		}
-
-		if ((*obj)->type == SNMP_IPADDR)
-			mangle_address(ctx.begin, ctx.pointer - 4 , map, check);
-
-		kfree((*obj)->id);
-		kfree(*obj);
-	}
-	kfree(obj);
-
-	if (!asn1_eoc_decode(&ctx, eoc))
-		return 0;
-
-	return 1;
-}
-
-/*****************************************************************************
- *
- * NAT routines.
- *
- *****************************************************************************/
-
-/*
- * SNMP translation routine.
- */
-static int snmp_translate(struct ip_conntrack *ct,
-			  enum ip_conntrack_info ctinfo,
-			  struct sk_buff **pskb)
-{
-	struct iphdr *iph = (*pskb)->nh.iph;
-	struct udphdr *udph = (struct udphdr *)((__be32 *)iph + iph->ihl);
-	u_int16_t udplen = ntohs(udph->len);
-	u_int16_t paylen = udplen - sizeof(struct udphdr);
-	int dir = CTINFO2DIR(ctinfo);
-	struct oct1_map map;
-
-	/*
-	 * Determine mappping for application layer addresses based
-	 * on NAT manipulations for the packet.
-	 */
-	if (dir == IP_CT_DIR_ORIGINAL) {
-		/* SNAT traps */
-		map.from = NOCT1(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip);
-		map.to = NOCT1(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip);
-	} else {
-		/* DNAT replies */
-		map.from = NOCT1(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip);
-		map.to = NOCT1(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip);
-	}
-
-	if (map.from == map.to)
-		return NF_ACCEPT;
-
-	if (!snmp_parse_mangle((unsigned char *)udph + sizeof(struct udphdr),
-			       paylen, &map, &udph->check)) {
-		if (net_ratelimit())
-			printk(KERN_WARNING "bsalg: parser failed\n");
-		return NF_DROP;
-	}
-	return NF_ACCEPT;
-}
-
-/* We don't actually set up expectations, just adjust internal IP
- * addresses if this is being NATted */
-static int help(struct sk_buff **pskb,
-		struct ip_conntrack *ct,
-		enum ip_conntrack_info ctinfo)
-{
-	int dir = CTINFO2DIR(ctinfo);
-	unsigned int ret;
-	struct iphdr *iph = (*pskb)->nh.iph;
-	struct udphdr *udph = (struct udphdr *)((u_int32_t *)iph + iph->ihl);
-
-	/* SNMP replies and originating SNMP traps get mangled */
-	if (udph->source == htons(SNMP_PORT) && dir != IP_CT_DIR_REPLY)
-		return NF_ACCEPT;
-	if (udph->dest == htons(SNMP_TRAP_PORT) && dir != IP_CT_DIR_ORIGINAL)
-		return NF_ACCEPT;
-
-	/* No NAT? */
-	if (!(ct->status & IPS_NAT_MASK))
-		return NF_ACCEPT;
-
-	/*
-	 * Make sure the packet length is ok.  So far, we were only guaranteed
-	 * to have a valid length IP header plus 8 bytes, which means we have
-	 * enough room for a UDP header.  Just verify the UDP length field so we
-	 * can mess around with the payload.
-	 */
-	if (ntohs(udph->len) != (*pskb)->len - (iph->ihl << 2)) {
-		 if (net_ratelimit())
-			 printk(KERN_WARNING "SNMP: dropping malformed packet "
-				"src=%u.%u.%u.%u dst=%u.%u.%u.%u\n",
-				NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
-		 return NF_DROP;
-	}
-
-	if (!skb_make_writable(pskb, (*pskb)->len))
-		return NF_DROP;
-
-	spin_lock_bh(&snmp_lock);
-	ret = snmp_translate(ct, ctinfo, pskb);
-	spin_unlock_bh(&snmp_lock);
-	return ret;
-}
-
-static struct ip_conntrack_helper snmp_helper = {
-	.max_expected = 0,
-	.timeout = 180,
-	.me = THIS_MODULE,
-	.help = help,
-	.name = "snmp",
-
-	.tuple = {.src = {.u = {.udp = {.port = __constant_htons(SNMP_PORT)}}},
-		  .dst = {.protonum = IPPROTO_UDP},
-	},
-	.mask = {.src = {.u = {0xFFFF}},
-		 .dst = {.protonum = 0xFF},
-	},
-};
-
-static struct ip_conntrack_helper snmp_trap_helper = {
-	.max_expected = 0,
-	.timeout = 180,
-	.me = THIS_MODULE,
-	.help = help,
-	.name = "snmp_trap",
-
-	.tuple = {.src = {.u = {.udp = {.port = __constant_htons(SNMP_TRAP_PORT)}}},
-		  .dst = {.protonum = IPPROTO_UDP},
-	},
-	.mask = {.src = {.u = {0xFFFF}},
-		 .dst = {.protonum = 0xFF},
-	},
-};
-
-/*****************************************************************************
- *
- * Module stuff.
- *
- *****************************************************************************/
-
-static int __init ip_nat_snmp_basic_init(void)
-{
-	int ret = 0;
-
-	ret = ip_conntrack_helper_register(&snmp_helper);
-	if (ret < 0)
-		return ret;
-	ret = ip_conntrack_helper_register(&snmp_trap_helper);
-	if (ret < 0) {
-		ip_conntrack_helper_unregister(&snmp_helper);
-		return ret;
-	}
-	return ret;
-}
-
-static void __exit ip_nat_snmp_basic_fini(void)
-{
-	ip_conntrack_helper_unregister(&snmp_helper);
-	ip_conntrack_helper_unregister(&snmp_trap_helper);
-}
-
-module_init(ip_nat_snmp_basic_init);
-module_exit(ip_nat_snmp_basic_fini);
-
-module_param(debug, int, 0600);
diff --git a/net/ipv4/netfilter/ip_nat_standalone.c b/net/ipv4/netfilter/ip_nat_standalone.c
deleted file mode 100644
index 6bcfdf6..0000000
--- a/net/ipv4/netfilter/ip_nat_standalone.c
+++ /dev/null
@@ -1,388 +0,0 @@
-/* This file contains all the functions required for the standalone
-   ip_nat module.
-
-   These are not required by the compatibility layer.
-*/
-
-/* (C) 1999-2001 Paul `Rusty' Russell
- * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.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.
- */
-
-/*
- * 23 Apr 2001: Harald Welte <laforge@gnumonks.org>
- * 	- new API and handling of conntrack/nat helpers
- * 	- now capable of multiple expectations for one master
- * */
-
-#include <linux/types.h>
-#include <linux/icmp.h>
-#include <linux/ip.h>
-#include <linux/netfilter.h>
-#include <linux/netfilter_ipv4.h>
-#include <linux/module.h>
-#include <linux/skbuff.h>
-#include <linux/proc_fs.h>
-#include <net/ip.h>
-#include <net/checksum.h>
-#include <linux/spinlock.h>
-
-#include <linux/netfilter_ipv4/ip_nat.h>
-#include <linux/netfilter_ipv4/ip_nat_rule.h>
-#include <linux/netfilter_ipv4/ip_nat_protocol.h>
-#include <linux/netfilter_ipv4/ip_nat_core.h>
-#include <linux/netfilter_ipv4/ip_nat_helper.h>
-#include <linux/netfilter_ipv4/ip_tables.h>
-#include <linux/netfilter_ipv4/ip_conntrack_core.h>
-
-#if 0
-#define DEBUGP printk
-#else
-#define DEBUGP(format, args...)
-#endif
-
-#ifdef CONFIG_XFRM
-static void nat_decode_session(struct sk_buff *skb, struct flowi *fl)
-{
-	struct ip_conntrack *ct;
-	struct ip_conntrack_tuple *t;
-	enum ip_conntrack_info ctinfo;
-	enum ip_conntrack_dir dir;
-	unsigned long statusbit;
-
-	ct = ip_conntrack_get(skb, &ctinfo);
-	if (ct == NULL)
-		return;
-	dir = CTINFO2DIR(ctinfo);
-	t = &ct->tuplehash[dir].tuple;
-
-	if (dir == IP_CT_DIR_ORIGINAL)
-		statusbit = IPS_DST_NAT;
-	else
-		statusbit = IPS_SRC_NAT;
-
-	if (ct->status & statusbit) {
-		fl->fl4_dst = t->dst.ip;
-		if (t->dst.protonum == IPPROTO_TCP ||
-		    t->dst.protonum == IPPROTO_UDP)
-			fl->fl_ip_dport = t->dst.u.tcp.port;
-	}
-
-	statusbit ^= IPS_NAT_MASK;
-
-	if (ct->status & statusbit) {
-		fl->fl4_src = t->src.ip;
-		if (t->dst.protonum == IPPROTO_TCP ||
-		    t->dst.protonum == IPPROTO_UDP)
-			fl->fl_ip_sport = t->src.u.tcp.port;
-	}
-}
-#endif
-
-static unsigned int
-ip_nat_fn(unsigned int hooknum,
-	  struct sk_buff **pskb,
-	  const struct net_device *in,
-	  const struct net_device *out,
-	  int (*okfn)(struct sk_buff *))
-{
-	struct ip_conntrack *ct;
-	enum ip_conntrack_info ctinfo;
-	struct ip_nat_info *info;
-	/* maniptype == SRC for postrouting. */
-	enum ip_nat_manip_type maniptype = HOOK2MANIP(hooknum);
-
-	/* 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
-		       & htons(IP_MF|IP_OFFSET)));
-
-	ct = ip_conntrack_get(*pskb, &ctinfo);
-	/* Can't track?  It's not due to stress, or conntrack would
-	   have dropped it.  Hence it's the user's responsibilty to
-	   packet filter it out, or implement conntrack/NAT for that
-	   protocol. 8) --RR */
-	if (!ct) {
-		/* Exception: ICMP redirect to new connection (not in
-		   hash table yet).  We must not let this through, in
-		   case we're doing NAT to the same network. */
-		if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) {
-			struct icmphdr _hdr, *hp;
-
-			hp = skb_header_pointer(*pskb,
-						(*pskb)->nh.iph->ihl*4,
-						sizeof(_hdr), &_hdr);
-			if (hp != NULL &&
-			    hp->type == ICMP_REDIRECT)
-				return NF_DROP;
-		}
-		return NF_ACCEPT;
-	}
-
-	/* Don't try to NAT if this packet is not conntracked */
-	if (ct == &ip_conntrack_untracked)
-		return NF_ACCEPT;
-
-	switch (ctinfo) {
-	case IP_CT_RELATED:
-	case IP_CT_RELATED+IP_CT_IS_REPLY:
-		if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) {
-			if (!ip_nat_icmp_reply_translation(ct, ctinfo,
-							   hooknum, pskb))
-				return NF_DROP;
-			else
-				return NF_ACCEPT;
-		}
-		/* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */
-	case IP_CT_NEW:
-		info = &ct->nat.info;
-
-		/* Seen it before?  This can happen for loopback, retrans,
-		   or local packets.. */
-		if (!ip_nat_initialized(ct, maniptype)) {
-			unsigned int ret;
-
-			if (unlikely(is_confirmed(ct)))
-				/* NAT module was loaded late */
-				ret = alloc_null_binding_confirmed(ct, info,
-								   hooknum);
-			else if (hooknum == NF_IP_LOCAL_IN)
-				/* LOCAL_IN hook doesn't have a chain!  */
-				ret = alloc_null_binding(ct, info, hooknum);
-			else
-				ret = ip_nat_rule_find(pskb, hooknum,
-						       in, out, ct,
-						       info);
-
-			if (ret != NF_ACCEPT) {
-				return ret;
-			}
-		} else
-			DEBUGP("Already setup manip %s for ct %p\n",
-			       maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST",
-			       ct);
-		break;
-
-	default:
-		/* ESTABLISHED */
-		IP_NF_ASSERT(ctinfo == IP_CT_ESTABLISHED
-			     || ctinfo == (IP_CT_ESTABLISHED+IP_CT_IS_REPLY));
-		info = &ct->nat.info;
-	}
-
-	IP_NF_ASSERT(info);
-	return ip_nat_packet(ct, ctinfo, hooknum, pskb);
-}
-
-static unsigned int
-ip_nat_in(unsigned int hooknum,
-	  struct sk_buff **pskb,
-	  const struct net_device *in,
-	  const struct net_device *out,
-	  int (*okfn)(struct sk_buff *))
-{
-	unsigned int ret;
-	__be32 daddr = (*pskb)->nh.iph->daddr;
-
-	ret = ip_nat_fn(hooknum, pskb, in, out, okfn);
-	if (ret != NF_DROP && ret != NF_STOLEN
-	    && daddr != (*pskb)->nh.iph->daddr) {
-		dst_release((*pskb)->dst);
-		(*pskb)->dst = NULL;
-	}
-	return ret;
-}
-
-static unsigned int
-ip_nat_out(unsigned int hooknum,
-	   struct sk_buff **pskb,
-	   const struct net_device *in,
-	   const struct net_device *out,
-	   int (*okfn)(struct sk_buff *))
-{
-#ifdef CONFIG_XFRM
-	struct ip_conntrack *ct;
-	enum ip_conntrack_info ctinfo;
-#endif
-	unsigned int ret;
-
-	/* root is playing with raw sockets. */
-	if ((*pskb)->len < sizeof(struct iphdr)
-	    || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr))
-		return NF_ACCEPT;
-
-	ret = ip_nat_fn(hooknum, pskb, in, out, okfn);
-#ifdef CONFIG_XFRM
-	if (ret != NF_DROP && ret != NF_STOLEN
-	    && (ct = ip_conntrack_get(*pskb, &ctinfo)) != NULL) {
-		enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
-
-		if (ct->tuplehash[dir].tuple.src.ip !=
-		    ct->tuplehash[!dir].tuple.dst.ip
-		    || ct->tuplehash[dir].tuple.src.u.all !=
-		       ct->tuplehash[!dir].tuple.dst.u.all
-		    )
-			return ip_xfrm_me_harder(pskb) == 0 ? ret : NF_DROP;
-	}
-#endif
-	return ret;
-}
-
-static unsigned int
-ip_nat_local_fn(unsigned int hooknum,
-		struct sk_buff **pskb,
-		const struct net_device *in,
-		const struct net_device *out,
-		int (*okfn)(struct sk_buff *))
-{
-	struct ip_conntrack *ct;
-	enum ip_conntrack_info ctinfo;
-	unsigned int ret;
-
-	/* root is playing with raw sockets. */
-	if ((*pskb)->len < sizeof(struct iphdr)
-	    || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr))
-		return NF_ACCEPT;
-
-	ret = ip_nat_fn(hooknum, pskb, in, out, okfn);
-	if (ret != NF_DROP && ret != NF_STOLEN
-	    && (ct = ip_conntrack_get(*pskb, &ctinfo)) != NULL) {
-		enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
-
-		if (ct->tuplehash[dir].tuple.dst.ip !=
-		    ct->tuplehash[!dir].tuple.src.ip) {
-			if (ip_route_me_harder(pskb, RTN_UNSPEC))
-				ret = NF_DROP;
-		}
-#ifdef CONFIG_XFRM
-		else if (ct->tuplehash[dir].tuple.dst.u.all !=
-			 ct->tuplehash[!dir].tuple.src.u.all)
-			if (ip_xfrm_me_harder(pskb))
-				ret = NF_DROP;
-#endif
-
-	}
-	return ret;
-}
-
-static unsigned int
-ip_nat_adjust(unsigned int hooknum,
-	      struct sk_buff **pskb,
-	      const struct net_device *in,
-	      const struct net_device *out,
-	      int (*okfn)(struct sk_buff *))
-{
-	struct ip_conntrack *ct;
-	enum ip_conntrack_info ctinfo;
-
-	ct = ip_conntrack_get(*pskb, &ctinfo);
-	if (ct && test_bit(IPS_SEQ_ADJUST_BIT, &ct->status)) {
-		DEBUGP("ip_nat_standalone: adjusting sequence number\n");
-		if (!ip_nat_seq_adjust(pskb, ct, ctinfo))
-			return NF_DROP;
-	}
-	return NF_ACCEPT;
-}
-
-/* We must be after connection tracking and before packet filtering. */
-
-static struct nf_hook_ops ip_nat_ops[] = {
-	/* Before packet filtering, change destination */
-	{
-		.hook		= ip_nat_in,
-		.owner		= THIS_MODULE,
-		.pf		= PF_INET,
-		.hooknum	= NF_IP_PRE_ROUTING,
-		.priority	= NF_IP_PRI_NAT_DST,
-	},
-	/* After packet filtering, change source */
-	{
-		.hook		= ip_nat_out,
-		.owner		= THIS_MODULE,
-		.pf		= PF_INET,
-		.hooknum	= NF_IP_POST_ROUTING,
-		.priority	= NF_IP_PRI_NAT_SRC,
-	},
-	/* After conntrack, adjust sequence number */
-	{
-		.hook		= ip_nat_adjust,
-		.owner		= THIS_MODULE,
-		.pf		= PF_INET,
-		.hooknum	= NF_IP_POST_ROUTING,
-		.priority	= NF_IP_PRI_NAT_SEQ_ADJUST,
-	},
-	/* Before packet filtering, change destination */
-	{
-		.hook		= ip_nat_local_fn,
-		.owner		= THIS_MODULE,
-		.pf		= PF_INET,
-		.hooknum	= NF_IP_LOCAL_OUT,
-		.priority	= NF_IP_PRI_NAT_DST,
-	},
-	/* After packet filtering, change source */
-	{
-		.hook		= ip_nat_fn,
-		.owner		= THIS_MODULE,
-		.pf		= PF_INET,
-		.hooknum	= NF_IP_LOCAL_IN,
-		.priority	= NF_IP_PRI_NAT_SRC,
-	},
-	/* After conntrack, adjust sequence number */
-	{
-		.hook		= ip_nat_adjust,
-		.owner		= THIS_MODULE,
-		.pf		= PF_INET,
-		.hooknum	= NF_IP_LOCAL_IN,
-		.priority	= NF_IP_PRI_NAT_SEQ_ADJUST,
-	},
-};
-
-static int __init ip_nat_standalone_init(void)
-{
-	int ret = 0;
-
-	need_conntrack();
-
-#ifdef CONFIG_XFRM
-	BUG_ON(ip_nat_decode_session != NULL);
-	ip_nat_decode_session = nat_decode_session;
-#endif
-	ret = ip_nat_rule_init();
-	if (ret < 0) {
-		printk("ip_nat_init: can't setup rules.\n");
-		goto cleanup_decode_session;
-	}
-	ret = nf_register_hooks(ip_nat_ops, ARRAY_SIZE(ip_nat_ops));
-	if (ret < 0) {
-		printk("ip_nat_init: can't register hooks.\n");
-		goto cleanup_rule_init;
-	}
-	return ret;
-
- cleanup_rule_init:
-	ip_nat_rule_cleanup();
- cleanup_decode_session:
-#ifdef CONFIG_XFRM
-	ip_nat_decode_session = NULL;
-	synchronize_net();
-#endif
-	return ret;
-}
-
-static void __exit ip_nat_standalone_fini(void)
-{
-	nf_unregister_hooks(ip_nat_ops, ARRAY_SIZE(ip_nat_ops));
-	ip_nat_rule_cleanup();
-#ifdef CONFIG_XFRM
-	ip_nat_decode_session = NULL;
-	synchronize_net();
-#endif
-}
-
-module_init(ip_nat_standalone_init);
-module_exit(ip_nat_standalone_fini);
-
-MODULE_LICENSE("GPL");
diff --git a/net/ipv4/netfilter/ip_nat_tftp.c b/net/ipv4/netfilter/ip_nat_tftp.c
deleted file mode 100644
index 6047935..0000000
--- a/net/ipv4/netfilter/ip_nat_tftp.c
+++ /dev/null
@@ -1,70 +0,0 @@
-/* (C) 2001-2002 Magnus Boden <mb@ozaba.mine.nu>
- *
- * 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.
- *
- * Version: 0.0.7
- *
- * Thu 21 Mar 2002 Harald Welte <laforge@gnumonks.org>
- * 	- Port to newnat API
- *
- * This module currently supports DNAT:
- * iptables -t nat -A PREROUTING -d x.x.x.x -j DNAT --to-dest x.x.x.y
- *
- * and SNAT:
- * iptables -t nat -A POSTROUTING { -j MASQUERADE , -j SNAT --to-source x.x.x.x }
- *
- * It has not been tested with
- * -j SNAT --to-source x.x.x.x-x.x.x.y since I only have one external ip
- * If you do test this please let me know if it works or not.
- *
- */
-
-#include <linux/module.h>
-#include <linux/netfilter_ipv4.h>
-#include <linux/ip.h>
-#include <linux/udp.h>
-
-#include <linux/netfilter.h>
-#include <linux/netfilter_ipv4/ip_tables.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-#include <linux/netfilter_ipv4/ip_conntrack_tftp.h>
-#include <linux/netfilter_ipv4/ip_nat_helper.h>
-#include <linux/netfilter_ipv4/ip_nat_rule.h>
-#include <linux/moduleparam.h>
-
-MODULE_AUTHOR("Magnus Boden <mb@ozaba.mine.nu>");
-MODULE_DESCRIPTION("tftp NAT helper");
-MODULE_LICENSE("GPL");
-
-static unsigned int help(struct sk_buff **pskb,
-			 enum ip_conntrack_info ctinfo,
-			 struct ip_conntrack_expect *exp)
-{
-	struct ip_conntrack *ct = exp->master;
-
-	exp->saved_proto.udp.port
-		= ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port;
-	exp->dir = IP_CT_DIR_REPLY;
-	exp->expectfn = ip_nat_follow_master;
-	if (ip_conntrack_expect_related(exp) != 0)
-		return NF_DROP;
-	return NF_ACCEPT;
-}
-
-static void __exit ip_nat_tftp_fini(void)
-{
-	rcu_assign_pointer(ip_nat_tftp_hook, NULL);
-	synchronize_rcu();
-}
-
-static int __init ip_nat_tftp_init(void)
-{
-	BUG_ON(rcu_dereference(ip_nat_tftp_hook));
-	rcu_assign_pointer(ip_nat_tftp_hook, help);
-	return 0;
-}
-
-module_init(ip_nat_tftp_init);
-module_exit(ip_nat_tftp_fini);
diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c
index a14798a..702d94d 100644
--- a/net/ipv4/netfilter/ip_queue.c
+++ b/net/ipv4/netfilter/ip_queue.c
@@ -8,18 +8,6 @@
  * 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.
- *
- * 2000-03-27: Simplified code (thanks to Andi Kleen for clues).
- * 2000-05-20: Fixed notifier problems (following Miguel Freitas' report).
- * 2000-06-19: Fixed so nfmark is copied to metadata (reported by Sebastian
- *             Zander).
- * 2000-08-01: Added Nick Williams' MAC support.
- * 2002-06-25: Code cleanup.
- * 2005-01-10: Added /proc counter for dropped packets; fixed so
- *             packets aren't delivered to user space if they're going
- *             to be dropped.
- * 2005-05-26: local_bh_{disable,enable} around nf_reinject (Harald Welte)
- *
  */
 #include <linux/module.h>
 #include <linux/skbuff.h>
@@ -191,12 +179,13 @@
 static struct sk_buff *
 ipq_build_packet_message(struct ipq_queue_entry *entry, int *errp)
 {
-	unsigned char *old_tail;
+	sk_buff_data_t old_tail;
 	size_t size = 0;
 	size_t data_len = 0;
 	struct sk_buff *skb;
 	struct ipq_packet_msg *pmsg;
 	struct nlmsghdr *nlh;
+	struct timeval tv;
 
 	read_lock_bh(&queue_lock);
 
@@ -234,15 +223,16 @@
 	if (!skb)
 		goto nlmsg_failure;
 
-	old_tail= skb->tail;
+	old_tail = skb->tail;
 	nlh = NLMSG_PUT(skb, 0, 0, IPQM_PACKET, size - sizeof(*nlh));
 	pmsg = NLMSG_DATA(nlh);
 	memset(pmsg, 0, sizeof(*pmsg));
 
 	pmsg->packet_id       = (unsigned long )entry;
 	pmsg->data_len        = data_len;
-	pmsg->timestamp_sec   = entry->skb->tstamp.off_sec;
-	pmsg->timestamp_usec  = entry->skb->tstamp.off_usec;
+	tv = ktime_to_timeval(entry->skb->tstamp);
+	pmsg->timestamp_sec   = tv.tv_sec;
+	pmsg->timestamp_usec  = tv.tv_usec;
 	pmsg->mark            = entry->skb->mark;
 	pmsg->hook            = entry->info->hook;
 	pmsg->hw_protocol     = entry->skb->protocol;
@@ -378,7 +368,7 @@
 	}
 	if (!skb_make_writable(&e->skb, v->data_len))
 		return -ENOMEM;
-	memcpy(e->skb->data, v->payload, v->data_len);
+	skb_copy_to_linear_data(e->skb, v->payload, v->data_len);
 	e->skb->ip_summed = CHECKSUM_NONE;
 
 	return 0;
@@ -495,7 +485,7 @@
 	if (skblen < sizeof(*nlh))
 		return;
 
-	nlh = (struct nlmsghdr *)skb->data;
+	nlh = nlmsg_hdr(skb);
 	nlmsglen = nlh->nlmsg_len;
 	if (nlmsglen < sizeof(*nlh) || skblen < nlmsglen)
 		return;
@@ -678,7 +668,7 @@
 
 	netlink_register_notifier(&ipq_nl_notifier);
 	ipqnl = netlink_kernel_create(NETLINK_FIREWALL, 0, ipq_rcv_sk,
-				      THIS_MODULE);
+				      NULL, THIS_MODULE);
 	if (ipqnl == NULL) {
 		printk(KERN_ERR "ip_queue: failed to create netlink socket\n");
 		goto cleanup_netlink_notifier;
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
index 50cc4b9..e3f83bf 100644
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -7,12 +7,6 @@
  * 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.
- *
- * 19 Jan 2002 Harald Welte <laforge@gnumonks.org>
- * 	- increase module usage count as soon as we have rules inside
- * 	  a table
- * 08 Oct 2005 Harald Welte <lafore@netfilter.org>
- * 	- Generalize into "x_tables" layer and "{ip,ip6,arp}_tables"
  */
 #include <linux/cache.h>
 #include <linux/capability.h>
@@ -198,7 +192,7 @@
 {
 	/* Stop iteration if it doesn't match */
 	if (!m->u.kernel.match->match(skb, in, out, m->u.kernel.match, m->data,
-				      offset, skb->nh.iph->ihl*4, hotdrop))
+				      offset, ip_hdrlen(skb), hotdrop))
 		return 1;
 	else
 		return 0;
@@ -231,7 +225,7 @@
 	struct xt_table_info *private;
 
 	/* Initialization */
-	ip = (*pskb)->nh.iph;
+	ip = ip_hdr(*pskb);
 	datalen = (*pskb)->len - ip->ihl * 4;
 	indev = in ? in->name : nulldevname;
 	outdev = out ? out->name : nulldevname;
@@ -320,7 +314,7 @@
 					= 0x57acc001;
 #endif
 				/* Target might have changed stuff. */
-				ip = (*pskb)->nh.iph;
+				ip = ip_hdr(*pskb);
 				datalen = (*pskb)->len - ip->ihl * 4;
 
 				if (verdict == IPT_CONTINUE)
diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c
index 42b0802..40e2734 100644
--- a/net/ipv4/netfilter/ipt_CLUSTERIP.c
+++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c
@@ -21,15 +21,12 @@
 #include <linux/if_arp.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
-
-#include <net/checksum.h>
-
 #include <linux/netfilter_arp.h>
-
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
 #include <linux/netfilter_ipv4/ipt_CLUSTERIP.h>
-#include <net/netfilter/nf_conntrack_compat.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/checksum.h>
 
 #define CLUSTERIP_VERSION "0.8"
 
@@ -240,7 +237,7 @@
 static inline u_int32_t
 clusterip_hashfn(struct sk_buff *skb, struct clusterip_config *config)
 {
-	struct iphdr *iph = skb->nh.iph;
+	struct iphdr *iph = ip_hdr(skb);
 	unsigned long hashval;
 	u_int16_t sport, dport;
 	u_int16_t *ports;
@@ -310,15 +307,16 @@
        const void *targinfo)
 {
 	const struct ipt_clusterip_tgt_info *cipinfo = targinfo;
+	struct nf_conn *ct;
 	enum ip_conntrack_info ctinfo;
-	u_int32_t *mark, hash;
+	u_int32_t hash;
 
 	/* don't need to clusterip_config_get() here, since refcount
 	 * is only decremented by destroy() - and ip_tables guarantees
 	 * that the ->target() function isn't called after ->destroy() */
 
-	mark = nf_ct_get_mark((*pskb), &ctinfo);
-	if (mark == NULL) {
+	ct = nf_ct_get(*pskb, &ctinfo);
+	if (ct == NULL) {
 		printk(KERN_ERR "CLUSTERIP: no conntrack!\n");
 			/* FIXME: need to drop invalid ones, since replies
 			 * to outgoing connections of other nodes will be
@@ -328,7 +326,7 @@
 
 	/* special case: ICMP error handling. conntrack distinguishes between
 	 * error messages (RELATED) and information requests (see below) */
-	if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP
+	if (ip_hdr(*pskb)->protocol == IPPROTO_ICMP
 	    && (ctinfo == IP_CT_RELATED
 		|| ctinfo == IP_CT_RELATED+IP_CT_IS_REPLY))
 		return XT_CONTINUE;
@@ -341,7 +339,7 @@
 
 	switch (ctinfo) {
 		case IP_CT_NEW:
-			*mark = hash;
+			ct->mark = hash;
 			break;
 		case IP_CT_RELATED:
 		case IP_CT_RELATED+IP_CT_IS_REPLY:
@@ -358,7 +356,7 @@
 #ifdef DEBUG_CLUSTERP
 	DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
 #endif
-	DEBUGP("hash=%u ct_hash=%u ", hash, *mark);
+	DEBUGP("hash=%u ct_hash=%u ", hash, ct->mark);
 	if (!clusterip_responsible(cipinfo->config, hash)) {
 		DEBUGP("not responsible\n");
 		return NF_DROP;
@@ -521,7 +519,7 @@
 	   const struct net_device *out,
 	   int (*okfn)(struct sk_buff *))
 {
-	struct arphdr *arp = (*pskb)->nh.arph;
+	struct arphdr *arp = arp_hdr(*pskb);
 	struct arp_payload *payload;
 	struct clusterip_config *c;
 
diff --git a/net/ipv4/netfilter/ipt_ECN.c b/net/ipv4/netfilter/ipt_ECN.c
index 4f56563..918ca92 100644
--- a/net/ipv4/netfilter/ipt_ECN.c
+++ b/net/ipv4/netfilter/ipt_ECN.c
@@ -5,14 +5,13 @@
  * 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.
- *
- * ipt_ECN.c,v 1.5 2002/08/18 19:36:51 laforge Exp
 */
 
 #include <linux/in.h>
 #include <linux/module.h>
 #include <linux/skbuff.h>
 #include <linux/ip.h>
+#include <net/ip.h>
 #include <linux/tcp.h>
 #include <net/checksum.h>
 
@@ -29,13 +28,13 @@
 static inline int
 set_ect_ip(struct sk_buff **pskb, const struct ipt_ECN_info *einfo)
 {
-	struct iphdr *iph = (*pskb)->nh.iph;
+	struct iphdr *iph = ip_hdr(*pskb);
 
 	if ((iph->tos & IPT_ECN_IP_MASK) != (einfo->ip_ect & IPT_ECN_IP_MASK)) {
 		__u8 oldtos;
 		if (!skb_make_writable(pskb, sizeof(struct iphdr)))
 			return 0;
-		iph = (*pskb)->nh.iph;
+		iph = ip_hdr(*pskb);
 		oldtos = iph->tos;
 		iph->tos &= ~IPT_ECN_IP_MASK;
 		iph->tos |= (einfo->ip_ect & IPT_ECN_IP_MASK);
@@ -52,7 +51,7 @@
 	__be16 oldval;
 
 	/* Not enought header? */
-	tcph = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4,
+	tcph = skb_header_pointer(*pskb, ip_hdrlen(*pskb),
 				  sizeof(_tcph), &_tcph);
 	if (!tcph)
 		return 0;
@@ -63,9 +62,9 @@
 	     tcph->cwr == einfo->proto.tcp.cwr)))
 		return 1;
 
-	if (!skb_make_writable(pskb, (*pskb)->nh.iph->ihl*4+sizeof(*tcph)))
+	if (!skb_make_writable(pskb, ip_hdrlen(*pskb) + sizeof(*tcph)))
 		return 0;
-	tcph = (void *)(*pskb)->nh.iph + (*pskb)->nh.iph->ihl*4;
+	tcph = (void *)ip_hdr(*pskb) + ip_hdrlen(*pskb);
 
 	oldval = ((__be16 *)tcph)[6];
 	if (einfo->operation & IPT_ECN_OP_SET_ECE)
@@ -93,7 +92,7 @@
 			return NF_DROP;
 
 	if (einfo->operation & (IPT_ECN_OP_SET_ECE | IPT_ECN_OP_SET_CWR)
-	    && (*pskb)->nh.iph->protocol == IPPROTO_TCP)
+	    && ip_hdr(*pskb)->protocol == IPPROTO_TCP)
 		if (!set_ect_tcp(pskb, einfo))
 			return NF_DROP;
 
diff --git a/net/ipv4/netfilter/ipt_LOG.c b/net/ipv4/netfilter/ipt_LOG.c
index d9c37fd..a42c5cd 100644
--- a/net/ipv4/netfilter/ipt_LOG.c
+++ b/net/ipv4/netfilter/ipt_LOG.c
@@ -399,9 +399,9 @@
 		/* MAC logging for input chain only. */
 		printk("MAC=");
 		if (skb->dev && skb->dev->hard_header_len
-		    && skb->mac.raw != (void*)skb->nh.iph) {
+		    && skb->mac_header != skb->network_header) {
 			int i;
-			unsigned char *p = skb->mac.raw;
+			const unsigned char *p = skb_mac_header(skb);
 			for (i = 0; i < skb->dev->hard_header_len; i++,p++)
 				printk("%02x%c", *p,
 				       i==skb->dev->hard_header_len - 1
@@ -477,14 +477,10 @@
 	ret = xt_register_target(&ipt_log_reg);
 	if (ret < 0)
 		return ret;
-	if (nf_log_register(PF_INET, &ipt_log_logger) < 0) {
-		printk(KERN_WARNING "ipt_LOG: not logging via system console "
-		       "since somebody else already registered for PF_INET\n");
-		/* we cannot make module load fail here, since otherwise
-		 * iptables userspace would abort */
-	}
-
-	return 0;
+	ret = nf_log_register(PF_INET, &ipt_log_logger);
+	if (ret < 0 && ret != -EEXIST)
+		xt_unregister_target(&ipt_log_reg);
+	return ret;
 }
 
 static void __exit ipt_log_fini(void)
diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c
index b5955f3..d4f2d77 100644
--- a/net/ipv4/netfilter/ipt_MASQUERADE.c
+++ b/net/ipv4/netfilter/ipt_MASQUERADE.c
@@ -19,12 +19,8 @@
 #include <net/ip.h>
 #include <net/checksum.h>
 #include <net/route.h>
-#include <linux/netfilter_ipv4.h>
-#ifdef CONFIG_NF_NAT_NEEDED
 #include <net/netfilter/nf_nat_rule.h>
-#else
-#include <linux/netfilter_ipv4/ip_nat_rule.h>
-#endif
+#include <linux/netfilter_ipv4.h>
 #include <linux/netfilter/x_tables.h>
 
 MODULE_LICENSE("GPL");
@@ -48,7 +44,7 @@
 		 void *targinfo,
 		 unsigned int hook_mask)
 {
-	const struct ip_nat_multi_range_compat *mr = targinfo;
+	const struct nf_nat_multi_range_compat *mr = targinfo;
 
 	if (mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) {
 		DEBUGP("masquerade_check: bad MAP_IPS.\n");
@@ -69,33 +65,26 @@
 		  const struct xt_target *target,
 		  const void *targinfo)
 {
-#ifdef CONFIG_NF_NAT_NEEDED
+	struct nf_conn *ct;
 	struct nf_conn_nat *nat;
-#endif
-	struct ip_conntrack *ct;
 	enum ip_conntrack_info ctinfo;
-	struct ip_nat_range newrange;
-	const struct ip_nat_multi_range_compat *mr;
+	struct nf_nat_range newrange;
+	const struct nf_nat_multi_range_compat *mr;
 	struct rtable *rt;
 	__be32 newsrc;
 
-	IP_NF_ASSERT(hooknum == NF_IP_POST_ROUTING);
+	NF_CT_ASSERT(hooknum == NF_IP_POST_ROUTING);
 
-	ct = ip_conntrack_get(*pskb, &ctinfo);
-#ifdef CONFIG_NF_NAT_NEEDED
+	ct = nf_ct_get(*pskb, &ctinfo);
 	nat = nfct_nat(ct);
-#endif
-	IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED
+
+	NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED
 			    || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY));
 
 	/* Source address is 0.0.0.0 - locally generated packet that is
 	 * probably not supposed to be masqueraded.
 	 */
-#ifdef CONFIG_NF_NAT_NEEDED
 	if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip == 0)
-#else
-	if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip == 0)
-#endif
 		return NF_ACCEPT;
 
 	mr = targinfo;
@@ -107,40 +96,30 @@
 	}
 
 	write_lock_bh(&masq_lock);
-#ifdef CONFIG_NF_NAT_NEEDED
 	nat->masq_index = out->ifindex;
-#else
-	ct->nat.masq_index = out->ifindex;
-#endif
 	write_unlock_bh(&masq_lock);
 
 	/* Transfer from original range. */
-	newrange = ((struct ip_nat_range)
+	newrange = ((struct nf_nat_range)
 		{ mr->range[0].flags | IP_NAT_RANGE_MAP_IPS,
 		  newsrc, newsrc,
 		  mr->range[0].min, mr->range[0].max });
 
 	/* Hand modified range to generic setup. */
-	return ip_nat_setup_info(ct, &newrange, hooknum);
+	return nf_nat_setup_info(ct, &newrange, hooknum);
 }
 
 static inline int
-device_cmp(struct ip_conntrack *i, void *ifindex)
+device_cmp(struct nf_conn *i, void *ifindex)
 {
-	int ret;
-#ifdef CONFIG_NF_NAT_NEEDED
 	struct nf_conn_nat *nat = nfct_nat(i);
+	int ret;
 
 	if (!nat)
 		return 0;
-#endif
 
 	read_lock_bh(&masq_lock);
-#ifdef CONFIG_NF_NAT_NEEDED
 	ret = (nat->masq_index == (int)(long)ifindex);
-#else
-	ret = (i->nat.masq_index == (int)(long)ifindex);
-#endif
 	read_unlock_bh(&masq_lock);
 
 	return ret;
@@ -156,9 +135,9 @@
 		/* Device was downed.  Search entire table for
 		   conntracks which were associated with that device,
 		   and forget them. */
-		IP_NF_ASSERT(dev->ifindex != 0);
+		NF_CT_ASSERT(dev->ifindex != 0);
 
-		ip_ct_iterate_cleanup(device_cmp, (void *)(long)dev->ifindex);
+		nf_ct_iterate_cleanup(device_cmp, (void *)(long)dev->ifindex);
 	}
 
 	return NOTIFY_DONE;
@@ -174,9 +153,9 @@
 		/* IP address was deleted.  Search entire table for
 		   conntracks which were associated with that device,
 		   and forget them. */
-		IP_NF_ASSERT(dev->ifindex != 0);
+		NF_CT_ASSERT(dev->ifindex != 0);
 
-		ip_ct_iterate_cleanup(device_cmp, (void *)(long)dev->ifindex);
+		nf_ct_iterate_cleanup(device_cmp, (void *)(long)dev->ifindex);
 	}
 
 	return NOTIFY_DONE;
@@ -194,7 +173,7 @@
 	.name		= "MASQUERADE",
 	.family		= AF_INET,
 	.target		= masquerade_target,
-	.targetsize	= sizeof(struct ip_nat_multi_range_compat),
+	.targetsize	= sizeof(struct nf_nat_multi_range_compat),
 	.table		= "nat",
 	.hooks		= 1 << NF_IP_POST_ROUTING,
 	.checkentry	= masquerade_check,
diff --git a/net/ipv4/netfilter/ipt_NETMAP.c b/net/ipv4/netfilter/ipt_NETMAP.c
index fd7aaa3..068c69b 100644
--- a/net/ipv4/netfilter/ipt_NETMAP.c
+++ b/net/ipv4/netfilter/ipt_NETMAP.c
@@ -16,11 +16,7 @@
 #include <linux/netfilter.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/netfilter/x_tables.h>
-#ifdef CONFIG_NF_NAT_NEEDED
 #include <net/netfilter/nf_nat_rule.h>
-#else
-#include <linux/netfilter_ipv4/ip_nat_rule.h>
-#endif
 
 #define MODULENAME "NETMAP"
 MODULE_LICENSE("GPL");
@@ -40,7 +36,7 @@
       void *targinfo,
       unsigned int hook_mask)
 {
-	const struct ip_nat_multi_range_compat *mr = targinfo;
+	const struct nf_nat_multi_range_compat *mr = targinfo;
 
 	if (!(mr->range[0].flags & IP_NAT_RANGE_MAP_IPS)) {
 		DEBUGP(MODULENAME":check: bad MAP_IPS.\n");
@@ -61,39 +57,39 @@
        const struct xt_target *target,
        const void *targinfo)
 {
-	struct ip_conntrack *ct;
+	struct nf_conn *ct;
 	enum ip_conntrack_info ctinfo;
 	__be32 new_ip, netmask;
-	const struct ip_nat_multi_range_compat *mr = targinfo;
-	struct ip_nat_range newrange;
+	const struct nf_nat_multi_range_compat *mr = targinfo;
+	struct nf_nat_range newrange;
 
-	IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING
+	NF_CT_ASSERT(hooknum == NF_IP_PRE_ROUTING
 		     || hooknum == NF_IP_POST_ROUTING
 		     || hooknum == NF_IP_LOCAL_OUT);
-	ct = ip_conntrack_get(*pskb, &ctinfo);
+	ct = nf_ct_get(*pskb, &ctinfo);
 
 	netmask = ~(mr->range[0].min_ip ^ mr->range[0].max_ip);
 
 	if (hooknum == NF_IP_PRE_ROUTING || hooknum == NF_IP_LOCAL_OUT)
-		new_ip = (*pskb)->nh.iph->daddr & ~netmask;
+		new_ip = ip_hdr(*pskb)->daddr & ~netmask;
 	else
-		new_ip = (*pskb)->nh.iph->saddr & ~netmask;
+		new_ip = ip_hdr(*pskb)->saddr & ~netmask;
 	new_ip |= mr->range[0].min_ip & netmask;
 
-	newrange = ((struct ip_nat_range)
+	newrange = ((struct nf_nat_range)
 		{ mr->range[0].flags | IP_NAT_RANGE_MAP_IPS,
 		  new_ip, new_ip,
 		  mr->range[0].min, mr->range[0].max });
 
 	/* Hand modified range to generic setup. */
-	return ip_nat_setup_info(ct, &newrange, hooknum);
+	return nf_nat_setup_info(ct, &newrange, hooknum);
 }
 
 static struct xt_target target_module = {
 	.name 		= MODULENAME,
 	.family		= AF_INET,
 	.target 	= target,
-	.targetsize	= sizeof(struct ip_nat_multi_range_compat),
+	.targetsize	= sizeof(struct nf_nat_multi_range_compat),
 	.table		= "nat",
 	.hooks		= (1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_POST_ROUTING) |
 			  (1 << NF_IP_LOCAL_OUT),
diff --git a/net/ipv4/netfilter/ipt_REDIRECT.c b/net/ipv4/netfilter/ipt_REDIRECT.c
index c2b6b80..68cc76a1 100644
--- a/net/ipv4/netfilter/ipt_REDIRECT.c
+++ b/net/ipv4/netfilter/ipt_REDIRECT.c
@@ -19,11 +19,7 @@
 #include <net/checksum.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/netfilter/x_tables.h>
-#ifdef CONFIG_NF_NAT_NEEDED
 #include <net/netfilter/nf_nat_rule.h>
-#else
-#include <linux/netfilter_ipv4/ip_nat_rule.h>
-#endif
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
@@ -43,7 +39,7 @@
 	       void *targinfo,
 	       unsigned int hook_mask)
 {
-	const struct ip_nat_multi_range_compat *mr = targinfo;
+	const struct nf_nat_multi_range_compat *mr = targinfo;
 
 	if (mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) {
 		DEBUGP("redirect_check: bad MAP_IPS.\n");
@@ -64,17 +60,17 @@
 		const struct xt_target *target,
 		const void *targinfo)
 {
-	struct ip_conntrack *ct;
+	struct nf_conn *ct;
 	enum ip_conntrack_info ctinfo;
 	__be32 newdst;
-	const struct ip_nat_multi_range_compat *mr = targinfo;
-	struct ip_nat_range newrange;
+	const struct nf_nat_multi_range_compat *mr = targinfo;
+	struct nf_nat_range newrange;
 
-	IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING
+	NF_CT_ASSERT(hooknum == NF_IP_PRE_ROUTING
 		     || hooknum == NF_IP_LOCAL_OUT);
 
-	ct = ip_conntrack_get(*pskb, &ctinfo);
-	IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
+	ct = nf_ct_get(*pskb, &ctinfo);
+	NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
 
 	/* Local packets: make them go to loopback */
 	if (hooknum == NF_IP_LOCAL_OUT)
@@ -96,20 +92,20 @@
 	}
 
 	/* Transfer from original range. */
-	newrange = ((struct ip_nat_range)
+	newrange = ((struct nf_nat_range)
 		{ mr->range[0].flags | IP_NAT_RANGE_MAP_IPS,
 		  newdst, newdst,
 		  mr->range[0].min, mr->range[0].max });
 
 	/* Hand modified range to generic setup. */
-	return ip_nat_setup_info(ct, &newrange, hooknum);
+	return nf_nat_setup_info(ct, &newrange, hooknum);
 }
 
 static struct xt_target redirect_reg = {
 	.name		= "REDIRECT",
 	.family		= AF_INET,
 	.target		= redirect_target,
-	.targetsize	= sizeof(struct ip_nat_multi_range_compat),
+	.targetsize	= sizeof(struct nf_nat_multi_range_compat),
 	.table		= "nat",
 	.hooks		= (1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_OUT),
 	.checkentry	= redirect_check,
diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c
index 80f739e..9041e07 100644
--- a/net/ipv4/netfilter/ipt_REJECT.c
+++ b/net/ipv4/netfilter/ipt_REJECT.c
@@ -1,7 +1,5 @@
 /*
  * This is a module which is used for rejecting packets.
- * Added support for customized reject packets (Jozsef Kadlecsik).
- * Added support for ICMP type-3-code-13 (Maciej Soltysiak). [RFC 1812]
  */
 
 /* (C) 1999-2001 Paul `Rusty' Russell
@@ -43,7 +41,7 @@
 static void send_reset(struct sk_buff *oldskb, int hook)
 {
 	struct sk_buff *nskb;
-	struct iphdr *iph = oldskb->nh.iph;
+	struct iphdr *niph;
 	struct tcphdr _otcph, *oth, *tcph;
 	__be16 tmp_port;
 	__be32 tmp_addr;
@@ -51,10 +49,10 @@
 	unsigned int addr_type;
 
 	/* IP header checks: fragment. */
-	if (oldskb->nh.iph->frag_off & htons(IP_OFFSET))
+	if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET))
 		return;
 
-	oth = skb_header_pointer(oldskb, oldskb->nh.iph->ihl * 4,
+	oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb),
 				 sizeof(_otcph), &_otcph);
 	if (oth == NULL)
 		return;
@@ -64,7 +62,7 @@
 		return;
 
 	/* Check checksum */
-	if (nf_ip_checksum(oldskb, hook, iph->ihl * 4, IPPROTO_TCP))
+	if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP))
 		return;
 
 	/* We need a linear, writeable skb.  We also need to expand
@@ -84,20 +82,21 @@
 	skb_shinfo(nskb)->gso_segs = 0;
 	skb_shinfo(nskb)->gso_type = 0;
 
-	tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl);
+	tcph = (struct tcphdr *)(skb_network_header(nskb) + ip_hdrlen(nskb));
 
 	/* Swap source and dest */
-	tmp_addr = nskb->nh.iph->saddr;
-	nskb->nh.iph->saddr = nskb->nh.iph->daddr;
-	nskb->nh.iph->daddr = tmp_addr;
+	niph = ip_hdr(nskb);
+	tmp_addr = niph->saddr;
+	niph->saddr = niph->daddr;
+	niph->daddr = tmp_addr;
 	tmp_port = tcph->source;
 	tcph->source = tcph->dest;
 	tcph->dest = tmp_port;
 
 	/* Truncate to length (no data) */
 	tcph->doff = sizeof(struct tcphdr)/4;
-	skb_trim(nskb, nskb->nh.iph->ihl*4 + sizeof(struct tcphdr));
-	nskb->nh.iph->tot_len = htons(nskb->len);
+	skb_trim(nskb, ip_hdrlen(nskb) + sizeof(struct tcphdr));
+	niph->tot_len = htons(nskb->len);
 
 	if (tcph->ack) {
 		needs_ack = 0;
@@ -105,9 +104,9 @@
 		tcph->ack_seq = 0;
 	} else {
 		needs_ack = 1;
-		tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin
-				      + oldskb->len - oldskb->nh.iph->ihl*4
-				      - (oth->doff<<2));
+		tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin +
+				      oldskb->len - ip_hdrlen(oldskb) -
+				      (oth->doff << 2));
 		tcph->seq = 0;
 	}
 
@@ -122,14 +121,13 @@
 	/* Adjust TCP checksum */
 	tcph->check = 0;
 	tcph->check = tcp_v4_check(sizeof(struct tcphdr),
-				   nskb->nh.iph->saddr,
-				   nskb->nh.iph->daddr,
+				   niph->saddr, niph->daddr,
 				   csum_partial((char *)tcph,
 						sizeof(struct tcphdr), 0));
 
 	/* Set DF, id = 0 */
-	nskb->nh.iph->frag_off = htons(IP_DF);
-	nskb->nh.iph->id = 0;
+	niph->frag_off = htons(IP_DF);
+	niph->id = 0;
 
 	addr_type = RTN_UNSPEC;
 	if (hook != NF_IP_FORWARD
@@ -145,12 +143,11 @@
 	nskb->ip_summed = CHECKSUM_NONE;
 
 	/* Adjust IP TTL */
-	nskb->nh.iph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT);
+	niph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT);
 
 	/* Adjust IP checksum */
-	nskb->nh.iph->check = 0;
-	nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph,
-					   nskb->nh.iph->ihl);
+	niph->check = 0;
+	niph->check = ip_fast_csum(skb_network_header(nskb), niph->ihl);
 
 	/* "Never happens" */
 	if (nskb->len > dst_mtu(nskb->dst))
@@ -182,7 +179,7 @@
 
 	/* Our naive response construction doesn't deal with IP
 	   options, and probably shouldn't try. */
-	if ((*pskb)->nh.iph->ihl<<2 != sizeof(struct iphdr))
+	if (ip_hdrlen(*pskb) != sizeof(struct iphdr))
 		return NF_DROP;
 
 	/* WARNING: This code causes reentry within iptables.
diff --git a/net/ipv4/netfilter/ipt_SAME.c b/net/ipv4/netfilter/ipt_SAME.c
index bd4404e..511e5ff 100644
--- a/net/ipv4/netfilter/ipt_SAME.c
+++ b/net/ipv4/netfilter/ipt_SAME.c
@@ -7,21 +7,6 @@
  * 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.
- *
- * 010320 Martin Josefsson <gandalf@wlug.westbo.se>
- * 	* copied ipt_BALANCE.c to ipt_SAME.c and changed a few things.
- * 010728 Martin Josefsson <gandalf@wlug.westbo.se>
- * 	* added --nodst to not include destination-ip in new source
- * 	  calculations.
- *	* added some more sanity-checks.
- * 010729 Martin Josefsson <gandalf@wlug.westbo.se>
- * 	* fixed a buggy if-statement in same_check(), should have
- * 	  used ntohl() but didn't.
- * 	* added support for multiple ranges. IPT_SAME_MAX_RANGE is
- * 	  defined in linux/include/linux/netfilter_ipv4/ipt_SAME.h
- * 	  and is currently set to 10.
- * 	* added support for 1-address range, nice to have now that
- * 	  we have multiple ranges.
  */
 #include <linux/types.h>
 #include <linux/ip.h>
@@ -35,11 +20,7 @@
 #include <net/checksum.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/netfilter/x_tables.h>
-#ifdef CONFIG_NF_NAT_NEEDED
 #include <net/netfilter/nf_nat_rule.h>
-#else
-#include <linux/netfilter_ipv4/ip_nat_rule.h>
-#endif
 #include <linux/netfilter_ipv4/ipt_SAME.h>
 
 MODULE_LICENSE("GPL");
@@ -138,17 +119,17 @@
 		const struct xt_target *target,
 		const void *targinfo)
 {
-	struct ip_conntrack *ct;
+	struct nf_conn *ct;
 	enum ip_conntrack_info ctinfo;
 	u_int32_t tmpip, aindex;
 	__be32 new_ip;
 	const struct ipt_same_info *same = targinfo;
-	struct ip_nat_range newrange;
-	const struct ip_conntrack_tuple *t;
+	struct nf_nat_range newrange;
+	const struct nf_conntrack_tuple *t;
 
-	IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING ||
+	NF_CT_ASSERT(hooknum == NF_IP_PRE_ROUTING ||
 			hooknum == NF_IP_POST_ROUTING);
-	ct = ip_conntrack_get(*pskb, &ctinfo);
+	ct = nf_ct_get(*pskb, &ctinfo);
 
 	t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
 
@@ -157,17 +138,10 @@
 	   Here we calculate the index in same->iparray which
 	   holds the ipaddress we should use */
 
-#ifdef CONFIG_NF_NAT_NEEDED
 	tmpip = ntohl(t->src.u3.ip);
 
 	if (!(same->info & IPT_SAME_NODST))
 		tmpip += ntohl(t->dst.u3.ip);
-#else
-	tmpip = ntohl(t->src.ip);
-
-	if (!(same->info & IPT_SAME_NODST))
-		tmpip += ntohl(t->dst.ip);
-#endif
 	aindex = tmpip % same->ipnum;
 
 	new_ip = htonl(same->iparray[aindex]);
@@ -178,13 +152,13 @@
 			NIPQUAD(new_ip));
 
 	/* Transfer from original range. */
-	newrange = ((struct ip_nat_range)
+	newrange = ((struct nf_nat_range)
 		{ same->range[0].flags, new_ip, new_ip,
 		  /* FIXME: Use ports from correct range! */
 		  same->range[0].min, same->range[0].max });
 
 	/* Hand modified range to generic setup. */
-	return ip_nat_setup_info(ct, &newrange, hooknum);
+	return nf_nat_setup_info(ct, &newrange, hooknum);
 }
 
 static struct xt_target same_reg = {
diff --git a/net/ipv4/netfilter/ipt_TOS.c b/net/ipv4/netfilter/ipt_TOS.c
index cedf9f7..0ad02f2 100644
--- a/net/ipv4/netfilter/ipt_TOS.c
+++ b/net/ipv4/netfilter/ipt_TOS.c
@@ -29,13 +29,13 @@
        const void *targinfo)
 {
 	const struct ipt_tos_target_info *tosinfo = targinfo;
-	struct iphdr *iph = (*pskb)->nh.iph;
+	struct iphdr *iph = ip_hdr(*pskb);
 
 	if ((iph->tos & IPTOS_TOS_MASK) != tosinfo->tos) {
 		__u8 oldtos;
 		if (!skb_make_writable(pskb, sizeof(struct iphdr)))
 			return NF_DROP;
-		iph = (*pskb)->nh.iph;
+		iph = ip_hdr(*pskb);
 		oldtos = iph->tos;
 		iph->tos = (iph->tos & IPTOS_PREC_MASK) | tosinfo->tos;
 		nf_csum_replace2(&iph->check, htons(oldtos), htons(iph->tos));
diff --git a/net/ipv4/netfilter/ipt_TTL.c b/net/ipv4/netfilter/ipt_TTL.c
index 64be31c..a991ec7 100644
--- a/net/ipv4/netfilter/ipt_TTL.c
+++ b/net/ipv4/netfilter/ipt_TTL.c
@@ -32,7 +32,7 @@
 	if (!skb_make_writable(pskb, (*pskb)->len))
 		return NF_DROP;
 
-	iph = (*pskb)->nh.iph;
+	iph = ip_hdr(*pskb);
 
 	switch (info->mode) {
 		case IPT_TTL_SET:
diff --git a/net/ipv4/netfilter/ipt_ULOG.c b/net/ipv4/netfilter/ipt_ULOG.c
index 9acc018..23b607b 100644
--- a/net/ipv4/netfilter/ipt_ULOG.c
+++ b/net/ipv4/netfilter/ipt_ULOG.c
@@ -2,20 +2,6 @@
  * netfilter module for userspace packet logging daemons
  *
  * (C) 2000-2004 by Harald Welte <laforge@netfilter.org>
- *
- * 2000/09/22 ulog-cprange feature added
- * 2001/01/04 in-kernel queue as proposed by Sebastian Zander
- * 						<zander@fokus.gmd.de>
- * 2001/01/30 per-rule nlgroup conflicts with global queue.
- *            nlgroup now global (sysctl)
- * 2001/04/19 ulog-queue reworked, now fixed buffer size specified at
- * 	      module loadtime -HW
- * 2002/07/07 remove broken nflog_rcv() function -HW
- * 2002/08/29 fix shifted/unshifted nlgroup bug -HW
- * 2002/10/30 fix uninitialized mac_len field - <Anders K. Pedersen>
- * 2004/10/25 fix erroneous calculation of 'len' parameter to NLMSG_PUT
- *	      resulting in bogus 'error during NLMSG_PUT' messages.
- *
  * (C) 1999-2001 Paul `Rusty' Russell
  * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
  *
@@ -42,8 +28,6 @@
  * flushtimeout:
  *   Specify, after how many hundredths of a second the queue should be
  *   flushed even if it is not full yet.
- *
- * ipt_ULOG.c,v 1.22 2002/10/30 09:07:31 laforge Exp
  */
 
 #include <linux/module.h>
@@ -187,6 +171,7 @@
 	ulog_packet_msg_t *pm;
 	size_t size, copy_len;
 	struct nlmsghdr *nlh;
+	struct timeval tv;
 
 	/* ffs == find first bit set, necessary because userspace
 	 * is already shifting groupnumber, but we need unshifted.
@@ -232,13 +217,14 @@
 	pm = NLMSG_DATA(nlh);
 
 	/* We might not have a timestamp, get one */
-	if (skb->tstamp.off_sec == 0)
+	if (skb->tstamp.tv64 == 0)
 		__net_timestamp((struct sk_buff *)skb);
 
 	/* copy hook, prefix, timestamp, payload, etc. */
 	pm->data_len = copy_len;
-	put_unaligned(skb->tstamp.off_sec, &pm->timestamp_sec);
-	put_unaligned(skb->tstamp.off_usec, &pm->timestamp_usec);
+	tv = ktime_to_timeval(skb->tstamp);
+	put_unaligned(tv.tv_sec, &pm->timestamp_sec);
+	put_unaligned(tv.tv_usec, &pm->timestamp_usec);
 	put_unaligned(skb->mark, &pm->mark);
 	pm->hook = hooknum;
 	if (prefix != NULL)
@@ -249,9 +235,9 @@
 		*(pm->prefix) = '\0';
 
 	if (in && in->hard_header_len > 0
-	    && skb->mac.raw != (void *) skb->nh.iph
+	    && skb->mac_header != skb->network_header
 	    && in->hard_header_len <= ULOG_MAC_LEN) {
-		memcpy(pm->mac, skb->mac.raw, in->hard_header_len);
+		memcpy(pm->mac, skb_mac_header(skb), in->hard_header_len);
 		pm->mac_len = in->hard_header_len;
 	} else
 		pm->mac_len = 0;
@@ -363,12 +349,52 @@
 	return 1;
 }
 
+#ifdef CONFIG_COMPAT
+struct compat_ipt_ulog_info {
+	compat_uint_t	nl_group;
+	compat_size_t	copy_range;
+	compat_size_t	qthreshold;
+	char		prefix[ULOG_PREFIX_LEN];
+};
+
+static void compat_from_user(void *dst, void *src)
+{
+	struct compat_ipt_ulog_info *cl = src;
+	struct ipt_ulog_info l = {
+		.nl_group	= cl->nl_group,
+		.copy_range	= cl->copy_range,
+		.qthreshold	= cl->qthreshold,
+	};
+
+	memcpy(l.prefix, cl->prefix, sizeof(l.prefix));
+	memcpy(dst, &l, sizeof(l));
+}
+
+static int compat_to_user(void __user *dst, void *src)
+{
+	struct ipt_ulog_info *l = src;
+	struct compat_ipt_ulog_info cl = {
+		.nl_group	= l->nl_group,
+		.copy_range	= l->copy_range,
+		.qthreshold	= l->qthreshold,
+	};
+
+	memcpy(cl.prefix, l->prefix, sizeof(cl.prefix));
+	return copy_to_user(dst, &cl, sizeof(cl)) ? -EFAULT : 0;
+}
+#endif /* CONFIG_COMPAT */
+
 static struct xt_target ipt_ulog_reg = {
 	.name		= "ULOG",
 	.family		= AF_INET,
 	.target		= ipt_ulog_target,
 	.targetsize	= sizeof(struct ipt_ulog_info),
 	.checkentry	= ipt_ulog_checkentry,
+#ifdef CONFIG_COMPAT
+	.compatsize	= sizeof(struct compat_ipt_ulog_info),
+	.compat_from_user = compat_from_user,
+	.compat_to_user	= compat_to_user,
+#endif
 	.me		= THIS_MODULE,
 };
 
@@ -390,14 +416,11 @@
 	}
 
 	/* initialize ulog_buffers */
-	for (i = 0; i < ULOG_MAXNLGROUPS; i++) {
-		init_timer(&ulog_buffers[i].timer);
-		ulog_buffers[i].timer.function = ulog_timer;
-		ulog_buffers[i].timer.data = i;
-	}
+	for (i = 0; i < ULOG_MAXNLGROUPS; i++)
+		setup_timer(&ulog_buffers[i].timer, ulog_timer, i);
 
 	nflognl = netlink_kernel_create(NETLINK_NFLOG, ULOG_MAXNLGROUPS, NULL,
-					THIS_MODULE);
+					NULL, THIS_MODULE);
 	if (!nflognl)
 		return -ENOMEM;
 
diff --git a/net/ipv4/netfilter/ipt_addrtype.c b/net/ipv4/netfilter/ipt_addrtype.c
index cfa0472..a652a14 100644
--- a/net/ipv4/netfilter/ipt_addrtype.c
+++ b/net/ipv4/netfilter/ipt_addrtype.c
@@ -33,7 +33,7 @@
 		 int offset, unsigned int protoff, int *hotdrop)
 {
 	const struct ipt_addrtype_info *info = matchinfo;
-	const struct iphdr *iph = skb->nh.iph;
+	const struct iphdr *iph = ip_hdr(skb);
 	int ret = 1;
 
 	if (info->source)
diff --git a/net/ipv4/netfilter/ipt_ecn.c b/net/ipv4/netfilter/ipt_ecn.c
index 37508b2..2621812 100644
--- a/net/ipv4/netfilter/ipt_ecn.c
+++ b/net/ipv4/netfilter/ipt_ecn.c
@@ -1,7 +1,5 @@
 /* IP tables module for matching the value of the IPv4 and TCP ECN bits
  *
- * ipt_ecn.c,v 1.3 2002/05/29 15:09:00 laforge Exp
- *
  * (C) 2002 by Harald Welte <laforge@gnumonks.org>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -11,6 +9,7 @@
 
 #include <linux/in.h>
 #include <linux/ip.h>
+#include <net/ip.h>
 #include <linux/module.h>
 #include <linux/skbuff.h>
 #include <linux/tcp.h>
@@ -26,7 +25,7 @@
 static inline int match_ip(const struct sk_buff *skb,
 			   const struct ipt_ecn_info *einfo)
 {
-	return ((skb->nh.iph->tos&IPT_ECN_IP_MASK) == einfo->ip_ect);
+	return (ip_hdr(skb)->tos & IPT_ECN_IP_MASK) == einfo->ip_ect;
 }
 
 static inline int match_tcp(const struct sk_buff *skb,
@@ -38,8 +37,7 @@
 	/* In practice, TCP match does this, so can't fail.  But let's
 	 * be good citizens.
 	 */
-	th = skb_header_pointer(skb, skb->nh.iph->ihl * 4,
-				sizeof(_tcph), &_tcph);
+	th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph);
 	if (th == NULL) {
 		*hotdrop = 0;
 		return 0;
@@ -80,7 +78,7 @@
 			return 0;
 
 	if (info->operation & (IPT_ECN_OP_MATCH_ECE|IPT_ECN_OP_MATCH_CWR)) {
-		if (skb->nh.iph->protocol != IPPROTO_TCP)
+		if (ip_hdr(skb)->protocol != IPPROTO_TCP)
 			return 0;
 		if (!match_tcp(skb, info, hotdrop))
 			return 0;
diff --git a/net/ipv4/netfilter/ipt_iprange.c b/net/ipv4/netfilter/ipt_iprange.c
index bc5d5e6..33af9e9 100644
--- a/net/ipv4/netfilter/ipt_iprange.c
+++ b/net/ipv4/netfilter/ipt_iprange.c
@@ -32,7 +32,7 @@
       int offset, unsigned int protoff, int *hotdrop)
 {
 	const struct ipt_iprange_info *info = matchinfo;
-	const struct iphdr *iph = skb->nh.iph;
+	const struct iphdr *iph = ip_hdr(skb);
 
 	if (info->flags & IPRANGE_SRC) {
 		if (((ntohl(iph->saddr) < ntohl(info->src.min_ip))
diff --git a/net/ipv4/netfilter/ipt_recent.c b/net/ipv4/netfilter/ipt_recent.c
index aecb9c4..15a9e8b 100644
--- a/net/ipv4/netfilter/ipt_recent.c
+++ b/net/ipv4/netfilter/ipt_recent.c
@@ -183,11 +183,11 @@
 	int ret = info->invert;
 
 	if (info->side == IPT_RECENT_DEST)
-		addr = skb->nh.iph->daddr;
+		addr = ip_hdr(skb)->daddr;
 	else
-		addr = skb->nh.iph->saddr;
+		addr = ip_hdr(skb)->saddr;
 
-	ttl = skb->nh.iph->ttl;
+	ttl = ip_hdr(skb)->ttl;
 	/* use TTL as seen before forwarding */
 	if (out && !skb->sk)
 		ttl++;
diff --git a/net/ipv4/netfilter/ipt_tos.c b/net/ipv4/netfilter/ipt_tos.c
index 5d33b51..d314844 100644
--- a/net/ipv4/netfilter/ipt_tos.c
+++ b/net/ipv4/netfilter/ipt_tos.c
@@ -30,7 +30,7 @@
 {
 	const struct ipt_tos_info *info = matchinfo;
 
-	return (skb->nh.iph->tos == info->tos) ^ info->invert;
+	return (ip_hdr(skb)->tos == info->tos) ^ info->invert;
 }
 
 static struct xt_match tos_match = {
diff --git a/net/ipv4/netfilter/ipt_ttl.c b/net/ipv4/netfilter/ipt_ttl.c
index 1eca9f4..ab02d9e 100644
--- a/net/ipv4/netfilter/ipt_ttl.c
+++ b/net/ipv4/netfilter/ipt_ttl.c
@@ -1,7 +1,5 @@
 /* IP tables module for matching the value of the TTL
  *
- * ipt_ttl.c,v 1.5 2000/11/13 11:16:08 laforge Exp
- *
  * (C) 2000,2001 by Harald Welte <laforge@netfilter.org>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -26,19 +24,20 @@
 		 int offset, unsigned int protoff, int *hotdrop)
 {
 	const struct ipt_ttl_info *info = matchinfo;
+	const u8 ttl = ip_hdr(skb)->ttl;
 
 	switch (info->mode) {
 		case IPT_TTL_EQ:
-			return (skb->nh.iph->ttl == info->ttl);
+			return (ttl == info->ttl);
 			break;
 		case IPT_TTL_NE:
-			return (!(skb->nh.iph->ttl == info->ttl));
+			return (!(ttl == info->ttl));
 			break;
 		case IPT_TTL_LT:
-			return (skb->nh.iph->ttl < info->ttl);
+			return (ttl < info->ttl);
 			break;
 		case IPT_TTL_GT:
-			return (skb->nh.iph->ttl > info->ttl);
+			return (ttl > info->ttl);
 			break;
 		default:
 			printk(KERN_WARNING "ipt_ttl: unknown mode %d\n",
diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c
index d1d61e9..4272890 100644
--- a/net/ipv4/netfilter/iptable_filter.c
+++ b/net/ipv4/netfilter/iptable_filter.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
+#include <net/ip.h>
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
@@ -102,7 +103,7 @@
 {
 	/* root is playing with raw sockets. */
 	if ((*pskb)->len < sizeof(struct iphdr)
-	    || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) {
+	    || ip_hdrlen(*pskb) < sizeof(struct iphdr)) {
 		if (net_ratelimit())
 			printk("ipt_hook: happy cracking.\n");
 		return NF_ACCEPT;
diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c
index 98b66ef..9278802 100644
--- a/net/ipv4/netfilter/iptable_mangle.c
+++ b/net/ipv4/netfilter/iptable_mangle.c
@@ -7,8 +7,6 @@
  * 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.
- *
- * Extended to all five netfilter hooks by Brad Chapman & Harald Welte
  */
 #include <linux/module.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
@@ -17,6 +15,7 @@
 #include <net/sock.h>
 #include <net/route.h>
 #include <linux/ip.h>
+#include <net/ip.h>
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
@@ -130,13 +129,14 @@
 		   int (*okfn)(struct sk_buff *))
 {
 	unsigned int ret;
+	const struct iphdr *iph;
 	u_int8_t tos;
 	__be32 saddr, daddr;
 	u_int32_t mark;
 
 	/* root is playing with raw sockets. */
 	if ((*pskb)->len < sizeof(struct iphdr)
-	    || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) {
+	    || ip_hdrlen(*pskb) < sizeof(struct iphdr)) {
 		if (net_ratelimit())
 			printk("ipt_hook: happy cracking.\n");
 		return NF_ACCEPT;
@@ -144,19 +144,23 @@
 
 	/* Save things which could affect route */
 	mark = (*pskb)->mark;
-	saddr = (*pskb)->nh.iph->saddr;
-	daddr = (*pskb)->nh.iph->daddr;
-	tos = (*pskb)->nh.iph->tos;
+	iph = ip_hdr(*pskb);
+	saddr = iph->saddr;
+	daddr = iph->daddr;
+	tos = iph->tos;
 
 	ret = ipt_do_table(pskb, hook, in, out, &packet_mangler);
 	/* Reroute for ANY change. */
-	if (ret != NF_DROP && ret != NF_STOLEN && ret != NF_QUEUE
-	    && ((*pskb)->nh.iph->saddr != saddr
-		|| (*pskb)->nh.iph->daddr != daddr
-		|| (*pskb)->mark != mark
-		|| (*pskb)->nh.iph->tos != tos))
-		if (ip_route_me_harder(pskb, RTN_UNSPEC))
-			ret = NF_DROP;
+	if (ret != NF_DROP && ret != NF_STOLEN && ret != NF_QUEUE) {
+		iph = ip_hdr(*pskb);
+
+		if (iph->saddr != saddr ||
+		    iph->daddr != daddr ||
+		    (*pskb)->mark != mark ||
+		    iph->tos != tos)
+			if (ip_route_me_harder(pskb, RTN_UNSPEC))
+				ret = NF_DROP;
+	}
 
 	return ret;
 }
diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
index 8f3e92d..0654eaa 100644
--- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
+++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
@@ -4,14 +4,6 @@
  * 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.
- *
- * 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
- *	- move L3 protocol dependent part to this file.
- * 23 Mar 2004: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
- *	- add get_features() to support various size of conntrack
- *	  structures.
- *
- * Derived from net/ipv4/netfilter/ip_conntrack_standalone.c
  */
 
 #include <linux/types.h>
@@ -87,7 +79,7 @@
 	local_bh_enable();
 
 	if (skb)
-		ip_send_check(skb->nh.iph);
+		ip_send_check(ip_hdr(skb));
 
 	return skb;
 }
@@ -97,16 +89,16 @@
 	     u_int8_t *protonum)
 {
 	/* Never happen */
-	if ((*pskb)->nh.iph->frag_off & htons(IP_OFFSET)) {
+	if (ip_hdr(*pskb)->frag_off & htons(IP_OFFSET)) {
 		if (net_ratelimit()) {
 			printk(KERN_ERR "ipv4_prepare: Frag of proto %u (hook=%u)\n",
-			(*pskb)->nh.iph->protocol, hooknum);
+			ip_hdr(*pskb)->protocol, hooknum);
 		}
 		return -NF_DROP;
 	}
 
-	*dataoff = (*pskb)->nh.raw - (*pskb)->data + (*pskb)->nh.iph->ihl*4;
-	*protonum = (*pskb)->nh.iph->protocol;
+	*dataoff = skb_network_offset(*pskb) + ip_hdrlen(*pskb);
+	*protonum = ip_hdr(*pskb)->protocol;
 
 	return NF_ACCEPT;
 }
@@ -152,9 +144,8 @@
 		return NF_ACCEPT;
 
 	return help->helper->help(pskb,
-			       (*pskb)->nh.raw - (*pskb)->data
-					       + (*pskb)->nh.iph->ihl*4,
-			       ct, ctinfo);
+				  skb_network_offset(*pskb) + ip_hdrlen(*pskb),
+				  ct, ctinfo);
 }
 
 static unsigned int ipv4_conntrack_defrag(unsigned int hooknum,
@@ -171,7 +162,7 @@
 #endif
 
 	/* Gather fragments. */
-	if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
+	if (ip_hdr(*pskb)->frag_off & htons(IP_MF | IP_OFFSET)) {
 		*pskb = nf_ct_ipv4_gather_frags(*pskb,
 						hooknum == NF_IP_PRE_ROUTING ?
 						IP_DEFRAG_CONNTRACK_IN :
@@ -199,7 +190,7 @@
 {
 	/* root is playing with raw sockets. */
 	if ((*pskb)->len < sizeof(struct iphdr)
-	    || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) {
+	    || ip_hdrlen(*pskb) < sizeof(struct iphdr)) {
 		if (net_ratelimit())
 			printk("ipt_hook: happy cracking.\n");
 		return NF_ACCEPT;
diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c
index 5fd1e53..f4fc657 100644
--- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c
+++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c
@@ -4,11 +4,6 @@
  * 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.
- *
- * 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
- *	- enable working with Layer 3 protocol independent connection tracking.
- *
- * Derived from net/ipv4/netfilter/ip_conntrack_proto_icmp.c
  */
 
 #include <linux/types.h>
@@ -158,7 +153,7 @@
 	NF_CT_ASSERT(skb->nfct == NULL);
 
 	/* Not enough header? */
-	inside = skb_header_pointer(skb, skb->nh.iph->ihl*4, sizeof(_in), &_in);
+	inside = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_in), &_in);
 	if (inside == NULL)
 		return -NF_ACCEPT;
 
@@ -172,7 +167,7 @@
 	/* rcu_read_lock()ed by nf_hook_slow */
 	innerproto = __nf_ct_l4proto_find(PF_INET, inside->ip.protocol);
 
-	dataoff = skb->nh.iph->ihl*4 + sizeof(inside->icmp);
+	dataoff = ip_hdrlen(skb) + sizeof(inside->icmp);
 	/* Are they talking about one of our connections? */
 	if (!nf_ct_get_tuple(skb, dataoff, dataoff + inside->ip.ihl*4, PF_INET,
 			     inside->ip.protocol, &origtuple,
@@ -227,7 +222,7 @@
 	struct icmphdr _ih, *icmph;
 
 	/* Not enough header? */
-	icmph = skb_header_pointer(skb, skb->nh.iph->ihl*4, sizeof(_ih), &_ih);
+	icmph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_ih), &_ih);
 	if (icmph == NULL) {
 		if (LOG_INVALID(IPPROTO_ICMP))
 			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c
index 452e9d3..ea02f00 100644
--- a/net/ipv4/netfilter/nf_nat_core.c
+++ b/net/ipv4/netfilter/nf_nat_core.c
@@ -431,7 +431,7 @@
 	} *inside;
 	struct nf_conntrack_l4proto *l4proto;
 	struct nf_conntrack_tuple inner, target;
-	int hdrlen = (*pskb)->nh.iph->ihl * 4;
+	int hdrlen = ip_hdrlen(*pskb);
 	enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
 	unsigned long statusbit;
 	enum nf_nat_manip_type manip = HOOK2MANIP(hooknum);
@@ -439,7 +439,7 @@
 	if (!skb_make_writable(pskb, hdrlen + sizeof(*inside)))
 		return 0;
 
-	inside = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4;
+	inside = (void *)(*pskb)->data + ip_hdrlen(*pskb);
 
 	/* We're actually going to mangle it beyond trivial checksum
 	   adjustment, so make sure the current checksum is correct. */
@@ -469,9 +469,9 @@
 	l4proto = __nf_ct_l4proto_find(PF_INET, inside->ip.protocol);
 
 	if (!nf_ct_get_tuple(*pskb,
-			     (*pskb)->nh.iph->ihl*4 + sizeof(struct icmphdr),
-			     (*pskb)->nh.iph->ihl*4 +
-			     sizeof(struct icmphdr) + inside->ip.ihl*4,
+			     ip_hdrlen(*pskb) + sizeof(struct icmphdr),
+			     (ip_hdrlen(*pskb) +
+			      sizeof(struct icmphdr) + inside->ip.ihl * 4),
 			     (u_int16_t)AF_INET,
 			     inside->ip.protocol,
 			     &inner, l3proto, l4proto))
@@ -483,14 +483,14 @@
 	   packet: PREROUTING (DST manip), routing produces ICMP, goes
 	   through POSTROUTING (which must correct the DST manip). */
 	if (!manip_pkt(inside->ip.protocol, pskb,
-		       (*pskb)->nh.iph->ihl*4 + sizeof(inside->icmp),
+		       ip_hdrlen(*pskb) + sizeof(inside->icmp),
 		       &ct->tuplehash[!dir].tuple,
 		       !manip))
 		return 0;
 
 	if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) {
 		/* Reloading "inside" here since manip_pkt inner. */
-		inside = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4;
+		inside = (void *)(*pskb)->data + ip_hdrlen(*pskb);
 		inside->icmp.checksum = 0;
 		inside->icmp.checksum =
 			csum_fold(skb_checksum(*pskb, hdrlen,
diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c
index 9cbf3f9..fcebc96 100644
--- a/net/ipv4/netfilter/nf_nat_h323.c
+++ b/net/ipv4/netfilter/nf_nat_h323.c
@@ -33,7 +33,7 @@
 		    unsigned int addroff, __be32 ip, __be16 port)
 {
 	enum ip_conntrack_info ctinfo;
-	struct nf_conn *ct = ip_conntrack_get(*pskb, &ctinfo);
+	struct nf_conn *ct = nf_ct_get(*pskb, &ctinfo);
 	struct {
 		__be32 ip;
 		__be16 port;
@@ -44,7 +44,7 @@
 	buf.port = port;
 	addroff += dataoff;
 
-	if ((*pskb)->nh.iph->protocol == IPPROTO_TCP) {
+	if (ip_hdr(*pskb)->protocol == IPPROTO_TCP) {
 		if (!nf_nat_mangle_tcp_packet(pskb, ct, ctinfo,
 					      addroff, sizeof(buf),
 					      (char *) &buf, sizeof(buf))) {
@@ -55,11 +55,11 @@
 		}
 
 		/* Relocate data pointer */
-		th = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl * 4,
+		th = skb_header_pointer(*pskb, ip_hdrlen(*pskb),
 					sizeof(_tcph), &_tcph);
 		if (th == NULL)
 			return -1;
-		*data = (*pskb)->data + (*pskb)->nh.iph->ihl * 4 +
+		*data = (*pskb)->data + ip_hdrlen(*pskb) +
 		    th->doff * 4 + dataoff;
 	} else {
 		if (!nf_nat_mangle_udp_packet(pskb, ct, ctinfo,
@@ -73,8 +73,8 @@
 		/* nf_nat_mangle_udp_packet uses skb_make_writable() to copy
 		 * or pull everything in a linear buffer, so we can safely
 		 * use the skb pointers now */
-		*data = (*pskb)->data + (*pskb)->nh.iph->ihl * 4 +
-		    sizeof(struct udphdr);
+		*data = ((*pskb)->data + ip_hdrlen(*pskb) +
+			 sizeof(struct udphdr));
 	}
 
 	return 0;
@@ -383,7 +383,7 @@
 static void ip_nat_q931_expect(struct nf_conn *new,
 			       struct nf_conntrack_expect *this)
 {
-	struct ip_nat_range range;
+	struct nf_nat_range range;
 
 	if (this->tuple.src.u3.ip != 0) {	/* Only accept calls from GK */
 		nf_nat_follow_master(new, this);
diff --git a/net/ipv4/netfilter/nf_nat_helper.c b/net/ipv4/netfilter/nf_nat_helper.c
index 49a90c3..15b6e5c 100644
--- a/net/ipv4/netfilter/nf_nat_helper.c
+++ b/net/ipv4/netfilter/nf_nat_helper.c
@@ -87,12 +87,13 @@
 	unsigned char *data;
 
 	BUG_ON(skb_is_nonlinear(skb));
-	data = (unsigned char *)skb->nh.iph + dataoff;
+	data = skb_network_header(skb) + dataoff;
 
 	/* move post-replacement */
 	memmove(data + match_offset + rep_len,
 		data + match_offset + match_len,
-		skb->tail - (data + match_offset + match_len));
+		skb->tail - (skb->network_header + dataoff +
+			     match_offset + match_len));
 
 	/* insert data from buffer */
 	memcpy(data + match_offset, rep_buffer, rep_len);
@@ -111,8 +112,8 @@
 	}
 
 	/* fix IP hdr checksum information */
-	skb->nh.iph->tot_len = htons(skb->len);
-	ip_send_check(skb->nh.iph);
+	ip_hdr(skb)->tot_len = htons(skb->len);
+	ip_send_check(ip_hdr(skb));
 }
 
 /* Unusual, but possible case. */
@@ -152,6 +153,7 @@
 			 const char *rep_buffer,
 			 unsigned int rep_len)
 {
+	struct rtable *rt = (struct rtable *)(*pskb)->dst;
 	struct iphdr *iph;
 	struct tcphdr *tcph;
 	int oldlen, datalen;
@@ -166,7 +168,7 @@
 
 	SKB_LINEAR_ASSERT(*pskb);
 
-	iph = (*pskb)->nh.iph;
+	iph = ip_hdr(*pskb);
 	tcph = (void *)iph + iph->ihl*4;
 
 	oldlen = (*pskb)->len - iph->ihl*4;
@@ -175,11 +177,22 @@
 
 	datalen = (*pskb)->len - iph->ihl*4;
 	if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) {
-		tcph->check = 0;
-		tcph->check = tcp_v4_check(datalen,
-					   iph->saddr, iph->daddr,
-					   csum_partial((char *)tcph,
-							datalen, 0));
+		if (!(rt->rt_flags & RTCF_LOCAL) &&
+		    (*pskb)->dev->features & NETIF_F_ALL_CSUM) {
+			(*pskb)->ip_summed = CHECKSUM_PARTIAL;
+			(*pskb)->csum_start = skb_headroom(*pskb) +
+					      skb_network_offset(*pskb) +
+					      iph->ihl * 4;
+			(*pskb)->csum_offset = offsetof(struct tcphdr, check);
+			tcph->check = ~tcp_v4_check(datalen,
+						    iph->saddr, iph->daddr, 0);
+		} else {
+			tcph->check = 0;
+			tcph->check = tcp_v4_check(datalen,
+						   iph->saddr, iph->daddr,
+						   csum_partial((char *)tcph,
+								datalen, 0));
+		}
 	} else
 		nf_proto_csum_replace2(&tcph->check, *pskb,
 				       htons(oldlen), htons(datalen), 1);
@@ -190,7 +203,7 @@
 				    (int)rep_len - (int)match_len,
 				    ct, ctinfo);
 		/* Tell TCP window tracking about seq change */
-		nf_conntrack_tcp_update(*pskb, (*pskb)->nh.iph->ihl*4,
+		nf_conntrack_tcp_update(*pskb, ip_hdrlen(*pskb),
 					ct, CTINFO2DIR(ctinfo));
 	}
 	return 1;
@@ -216,12 +229,13 @@
 			 const char *rep_buffer,
 			 unsigned int rep_len)
 {
+	struct rtable *rt = (struct rtable *)(*pskb)->dst;
 	struct iphdr *iph;
 	struct udphdr *udph;
 	int datalen, oldlen;
 
 	/* UDP helpers might accidentally mangle the wrong packet */
-	iph = (*pskb)->nh.iph;
+	iph = ip_hdr(*pskb);
 	if ((*pskb)->len < iph->ihl*4 + sizeof(*udph) +
 			       match_offset + match_len)
 		return 0;
@@ -234,7 +248,7 @@
 	    !enlarge_skb(pskb, rep_len - match_len))
 		return 0;
 
-	iph = (*pskb)->nh.iph;
+	iph = ip_hdr(*pskb);
 	udph = (void *)iph + iph->ihl*4;
 
 	oldlen = (*pskb)->len - iph->ihl*4;
@@ -250,13 +264,25 @@
 		return 1;
 
 	if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) {
-		udph->check = 0;
-		udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
-						datalen, IPPROTO_UDP,
-						csum_partial((char *)udph,
-							     datalen, 0));
-		if (!udph->check)
-			udph->check = CSUM_MANGLED_0;
+		if (!(rt->rt_flags & RTCF_LOCAL) &&
+		    (*pskb)->dev->features & NETIF_F_ALL_CSUM) {
+			(*pskb)->ip_summed = CHECKSUM_PARTIAL;
+			(*pskb)->csum_start = skb_headroom(*pskb) +
+					      skb_network_offset(*pskb) +
+					      iph->ihl * 4;
+			(*pskb)->csum_offset = offsetof(struct udphdr, check);
+			udph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
+							 datalen, IPPROTO_UDP,
+							 0);
+		} else {
+			udph->check = 0;
+			udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
+							datalen, IPPROTO_UDP,
+							csum_partial((char *)udph,
+								     datalen, 0));
+			if (!udph->check)
+				udph->check = CSUM_MANGLED_0;
+		}
 	} else
 		nf_proto_csum_replace2(&udph->check, *pskb,
 				       htons(oldlen), htons(datalen), 1);
@@ -318,8 +344,8 @@
 	unsigned int dir, optoff, optend;
 	struct nf_conn_nat *nat = nfct_nat(ct);
 
-	optoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct tcphdr);
-	optend = (*pskb)->nh.iph->ihl*4 + tcph->doff*4;
+	optoff = ip_hdrlen(*pskb) + sizeof(struct tcphdr);
+	optend = ip_hdrlen(*pskb) + tcph->doff * 4;
 
 	if (!skb_make_writable(pskb, optend))
 		return 0;
@@ -371,10 +397,10 @@
 	this_way = &nat->info.seq[dir];
 	other_way = &nat->info.seq[!dir];
 
-	if (!skb_make_writable(pskb, (*pskb)->nh.iph->ihl*4+sizeof(*tcph)))
+	if (!skb_make_writable(pskb, ip_hdrlen(*pskb) + sizeof(*tcph)))
 		return 0;
 
-	tcph = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4;
+	tcph = (void *)(*pskb)->data + ip_hdrlen(*pskb);
 	if (after(ntohl(tcph->seq), this_way->correction_pos))
 		newseq = htonl(ntohl(tcph->seq) + this_way->offset_after);
 	else
@@ -399,7 +425,7 @@
 	if (!nf_nat_sack_adjust(pskb, tcph, ct, ctinfo))
 		return 0;
 
-	nf_conntrack_tcp_update(*pskb, (*pskb)->nh.iph->ihl*4, ct, dir);
+	nf_conntrack_tcp_update(*pskb, ip_hdrlen(*pskb), ct, dir);
 
 	return 1;
 }
diff --git a/net/ipv4/netfilter/nf_nat_pptp.c b/net/ipv4/netfilter/nf_nat_pptp.c
index 7ba341c..a668887 100644
--- a/net/ipv4/netfilter/nf_nat_pptp.c
+++ b/net/ipv4/netfilter/nf_nat_pptp.c
@@ -53,7 +53,7 @@
 	struct nf_conntrack_tuple t;
 	struct nf_ct_pptp_master *ct_pptp_info;
 	struct nf_nat_pptp *nat_pptp_info;
-	struct ip_nat_range range;
+	struct nf_nat_range range;
 
 	ct_pptp_info = &nfct_help(master)->help.ct_pptp_info;
 	nat_pptp_info = &nfct_nat(master)->help.nat_pptp_info;
diff --git a/net/ipv4/netfilter/nf_nat_rule.c b/net/ipv4/netfilter/nf_nat_rule.c
index 147a437..2a28339 100644
--- a/net/ipv4/netfilter/nf_nat_rule.c
+++ b/net/ipv4/netfilter/nf_nat_rule.c
@@ -191,7 +191,7 @@
 
 	if (hooknum == NF_IP_LOCAL_OUT &&
 	    mr->range[0].flags & IP_NAT_RANGE_MAP_IPS)
-		warn_if_extra_mangle((*pskb)->nh.iph->daddr,
+		warn_if_extra_mangle(ip_hdr(*pskb)->daddr,
 				     mr->range[0].min_ip);
 
 	return nf_nat_setup_info(ct, &mr->range[0], hooknum);
diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c
index b12cd7c..bfd88e4 100644
--- a/net/ipv4/netfilter/nf_nat_sip.c
+++ b/net/ipv4/netfilter/nf_nat_sip.c
@@ -11,6 +11,7 @@
 #include <linux/module.h>
 #include <linux/skbuff.h>
 #include <linux/ip.h>
+#include <net/ip.h>
 #include <linux/udp.h>
 
 #include <net/netfilter/nf_nat.h>
@@ -92,7 +93,7 @@
 	if (!nf_nat_mangle_udp_packet(pskb, ct, ctinfo,
 				      matchoff, matchlen, addr, addrlen))
 		return 0;
-	*dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
+	*dptr = (*pskb)->data + ip_hdrlen(*pskb) + sizeof(struct udphdr);
 	return 1;
 
 }
@@ -106,7 +107,7 @@
 	struct addr_map map;
 	int dataoff, datalen;
 
-	dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
+	dataoff = ip_hdrlen(*pskb) + sizeof(struct udphdr);
 	datalen = (*pskb)->len - dataoff;
 	if (datalen < sizeof("SIP/2.0") - 1)
 		return NF_DROP;
@@ -155,7 +156,7 @@
 		return 0;
 
 	/* We need to reload this. Thanks Patrick. */
-	*dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
+	*dptr = (*pskb)->data + ip_hdrlen(*pskb) + sizeof(struct udphdr);
 	return 1;
 }
 
@@ -168,7 +169,7 @@
 	char buffer[sizeof("65536")];
 	int bufflen;
 
-	dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
+	dataoff = ip_hdrlen(*pskb) + sizeof(struct udphdr);
 
 	/* Get actual SDP lenght */
 	if (ct_sip_get_info(ct, dptr, (*pskb)->len - dataoff, &matchoff,
@@ -200,7 +201,7 @@
 	char buffer[sizeof("nnn.nnn.nnn.nnn")];
 	unsigned int dataoff, bufflen;
 
-	dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
+	dataoff = ip_hdrlen(*pskb) + sizeof(struct udphdr);
 
 	/* Mangle owner and contact info. */
 	bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip));
diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic.c b/net/ipv4/netfilter/nf_nat_snmp_basic.c
index ce5c493..6e88505 100644
--- a/net/ipv4/netfilter/nf_nat_snmp_basic.c
+++ b/net/ipv4/netfilter/nf_nat_snmp_basic.c
@@ -38,10 +38,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  *
  * Author: James Morris <jmorris@intercode.com.au>
- *
- * Updates:
- * 2000-08-06: Convert to new helper API (Harald Welte).
- *
  */
 #include <linux/module.h>
 #include <linux/moduleparam.h>
@@ -1194,7 +1190,7 @@
 			  enum ip_conntrack_info ctinfo,
 			  struct sk_buff **pskb)
 {
-	struct iphdr *iph = (*pskb)->nh.iph;
+	struct iphdr *iph = ip_hdr(*pskb);
 	struct udphdr *udph = (struct udphdr *)((__be32 *)iph + iph->ihl);
 	u_int16_t udplen = ntohs(udph->len);
 	u_int16_t paylen = udplen - sizeof(struct udphdr);
@@ -1235,7 +1231,7 @@
 {
 	int dir = CTINFO2DIR(ctinfo);
 	unsigned int ret;
-	struct iphdr *iph = (*pskb)->nh.iph;
+	struct iphdr *iph = ip_hdr(*pskb);
 	struct udphdr *udph = (struct udphdr *)((u_int32_t *)iph + iph->ihl);
 
 	/* SNMP replies and originating SNMP traps get mangled */
diff --git a/net/ipv4/netfilter/nf_nat_standalone.c b/net/ipv4/netfilter/nf_nat_standalone.c
index 15aa3db..64bbed2 100644
--- a/net/ipv4/netfilter/nf_nat_standalone.c
+++ b/net/ipv4/netfilter/nf_nat_standalone.c
@@ -86,8 +86,7 @@
 
 	/* We never see fragments: conntrack defrags on pre-routing
 	   and local-out, and nf_nat_out protects post-routing. */
-	NF_CT_ASSERT(!((*pskb)->nh.iph->frag_off
-		       & htons(IP_MF|IP_OFFSET)));
+	NF_CT_ASSERT(!(ip_hdr(*pskb)->frag_off & htons(IP_MF | IP_OFFSET)));
 
 	ct = nf_ct_get(*pskb, &ctinfo);
 	/* Can't track?  It's not due to stress, or conntrack would
@@ -98,11 +97,10 @@
 		/* Exception: ICMP redirect to new connection (not in
 		   hash table yet).  We must not let this through, in
 		   case we're doing NAT to the same network. */
-		if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) {
+		if (ip_hdr(*pskb)->protocol == IPPROTO_ICMP) {
 			struct icmphdr _hdr, *hp;
 
-			hp = skb_header_pointer(*pskb,
-						(*pskb)->nh.iph->ihl*4,
+			hp = skb_header_pointer(*pskb, ip_hdrlen(*pskb),
 						sizeof(_hdr), &_hdr);
 			if (hp != NULL &&
 			    hp->type == ICMP_REDIRECT)
@@ -122,7 +120,7 @@
 	switch (ctinfo) {
 	case IP_CT_RELATED:
 	case IP_CT_RELATED+IP_CT_IS_REPLY:
-		if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) {
+		if (ip_hdr(*pskb)->protocol == IPPROTO_ICMP) {
 			if (!nf_nat_icmp_reply_translation(ct, ctinfo,
 							   hooknum, pskb))
 				return NF_DROP;
@@ -177,11 +175,11 @@
 	  int (*okfn)(struct sk_buff *))
 {
 	unsigned int ret;
-	__be32 daddr = (*pskb)->nh.iph->daddr;
+	__be32 daddr = ip_hdr(*pskb)->daddr;
 
 	ret = nf_nat_fn(hooknum, pskb, in, out, okfn);
 	if (ret != NF_DROP && ret != NF_STOLEN &&
-	    daddr != (*pskb)->nh.iph->daddr) {
+	    daddr != ip_hdr(*pskb)->daddr) {
 		dst_release((*pskb)->dst);
 		(*pskb)->dst = NULL;
 	}
@@ -203,7 +201,7 @@
 
 	/* root is playing with raw sockets. */
 	if ((*pskb)->len < sizeof(struct iphdr) ||
-	    (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr))
+	    ip_hdrlen(*pskb) < sizeof(struct iphdr))
 		return NF_ACCEPT;
 
 	ret = nf_nat_fn(hooknum, pskb, in, out, okfn);
@@ -236,7 +234,7 @@
 
 	/* root is playing with raw sockets. */
 	if ((*pskb)->len < sizeof(struct iphdr) ||
-	    (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr))
+	    ip_hdrlen(*pskb) < sizeof(struct iphdr))
 		return NF_ACCEPT;
 
 	ret = nf_nat_fn(hooknum, pskb, in, out, okfn);
diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c
index ae68a69..37ab580 100644
--- a/net/ipv4/proc.c
+++ b/net/ipv4/proc.c
@@ -87,19 +87,6 @@
 	.release = single_release,
 };
 
-static unsigned long
-fold_field(void *mib[], int offt)
-{
-	unsigned long res = 0;
-	int i;
-
-	for_each_possible_cpu(i) {
-		res += *(((unsigned long *) per_cpu_ptr(mib[0], i)) + offt);
-		res += *(((unsigned long *) per_cpu_ptr(mib[1], i)) + offt);
-	}
-	return res;
-}
-
 /* snmp items */
 static const struct snmp_mib snmp4_ipstats_list[] = {
 	SNMP_MIB_ITEM("InReceives", IPSTATS_MIB_INRECEIVES),
@@ -266,8 +253,8 @@
 
 	for (i = 0; snmp4_ipstats_list[i].name != NULL; i++)
 		seq_printf(seq, " %lu",
-			   fold_field((void **) ip_statistics,
-				      snmp4_ipstats_list[i].entry));
+			   snmp_fold_field((void **)ip_statistics,
+					   snmp4_ipstats_list[i].entry));
 
 	seq_puts(seq, "\nIcmp:");
 	for (i = 0; snmp4_icmp_list[i].name != NULL; i++)
@@ -276,8 +263,8 @@
 	seq_puts(seq, "\nIcmp:");
 	for (i = 0; snmp4_icmp_list[i].name != NULL; i++)
 		seq_printf(seq, " %lu",
-			   fold_field((void **) icmp_statistics,
-				      snmp4_icmp_list[i].entry));
+			   snmp_fold_field((void **)icmp_statistics,
+					   snmp4_icmp_list[i].entry));
 
 	seq_puts(seq, "\nTcp:");
 	for (i = 0; snmp4_tcp_list[i].name != NULL; i++)
@@ -288,12 +275,12 @@
 		/* MaxConn field is signed, RFC 2012 */
 		if (snmp4_tcp_list[i].entry == TCP_MIB_MAXCONN)
 			seq_printf(seq, " %ld",
-				   fold_field((void **) tcp_statistics,
-					      snmp4_tcp_list[i].entry));
+				   snmp_fold_field((void **)tcp_statistics,
+						   snmp4_tcp_list[i].entry));
 		else
 			seq_printf(seq, " %lu",
-				   fold_field((void **) tcp_statistics,
-					      snmp4_tcp_list[i].entry));
+				   snmp_fold_field((void **)tcp_statistics,
+						   snmp4_tcp_list[i].entry));
 	}
 
 	seq_puts(seq, "\nUdp:");
@@ -303,8 +290,8 @@
 	seq_puts(seq, "\nUdp:");
 	for (i = 0; snmp4_udp_list[i].name != NULL; i++)
 		seq_printf(seq, " %lu",
-			   fold_field((void **) udp_statistics,
-				      snmp4_udp_list[i].entry));
+			   snmp_fold_field((void **)udp_statistics,
+					   snmp4_udp_list[i].entry));
 
 	/* the UDP and UDP-Lite MIBs are the same */
 	seq_puts(seq, "\nUdpLite:");
@@ -314,8 +301,8 @@
 	seq_puts(seq, "\nUdpLite:");
 	for (i = 0; snmp4_udp_list[i].name != NULL; i++)
 		seq_printf(seq, " %lu",
-			   fold_field((void **) udplite_statistics,
-				      snmp4_udp_list[i].entry)     );
+			   snmp_fold_field((void **)udplite_statistics,
+					   snmp4_udp_list[i].entry));
 
 	seq_putc(seq, '\n');
 	return 0;
@@ -348,8 +335,8 @@
 	seq_puts(seq, "\nTcpExt:");
 	for (i = 0; snmp4_net_list[i].name != NULL; i++)
 		seq_printf(seq, " %lu",
-			   fold_field((void **) net_statistics,
-				      snmp4_net_list[i].entry));
+			   snmp_fold_field((void **)net_statistics,
+					   snmp4_net_list[i].entry));
 
 	seq_putc(seq, '\n');
 	return 0;
diff --git a/net/ipv4/protocol.c b/net/ipv4/protocol.c
index da70fef..971ab93 100644
--- a/net/ipv4/protocol.c
+++ b/net/ipv4/protocol.c
@@ -45,7 +45,7 @@
 #include <net/ipip.h>
 #include <linux/igmp.h>
 
-struct net_protocol *inet_protos[MAX_INET_PROTOS];
+struct net_protocol *inet_protos[MAX_INET_PROTOS] ____cacheline_aligned_in_smp;
 static DEFINE_SPINLOCK(inet_proto_lock);
 
 /*
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 87e9c16..24d7c9f 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -132,7 +132,7 @@
 	if (!pskb_may_pull(skb, sizeof(struct icmphdr)))
 		return 1;
 
-	type = skb->h.icmph->type;
+	type = icmp_hdr(skb)->type;
 	if (type < 32) {
 		__u32 data = raw_sk(sk)->filter.data;
 
@@ -184,8 +184,8 @@
 void raw_err (struct sock *sk, struct sk_buff *skb, u32 info)
 {
 	struct inet_sock *inet = inet_sk(sk);
-	int type = skb->h.icmph->type;
-	int code = skb->h.icmph->code;
+	const int type = icmp_hdr(skb)->type;
+	const int code = icmp_hdr(skb)->code;
 	int err = 0;
 	int harderr = 0;
 
@@ -256,7 +256,7 @@
 	}
 	nf_reset(skb);
 
-	skb_push(skb, skb->data - skb->nh.raw);
+	skb_push(skb, skb->data - skb_network_header(skb));
 
 	raw_rcv_skb(sk, skb);
 	return 0;
@@ -291,11 +291,13 @@
 	skb->priority = sk->sk_priority;
 	skb->dst = dst_clone(&rt->u.dst);
 
-	skb->nh.iph = iph = (struct iphdr *)skb_put(skb, length);
+	skb_reset_network_header(skb);
+	iph = ip_hdr(skb);
+	skb_put(skb, length);
 
 	skb->ip_summed = CHECKSUM_NONE;
 
-	skb->h.raw = skb->nh.raw;
+	skb->transport_header = skb->network_header;
 	err = memcpy_fromiovecend((void *)iph, from, 0, length);
 	if (err)
 		goto error_fault;
@@ -613,7 +615,7 @@
 	/* Copy the address. */
 	if (sin) {
 		sin->sin_family = AF_INET;
-		sin->sin_addr.s_addr = skb->nh.iph->saddr;
+		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
 		sin->sin_port = 0;
 		memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
 	}
@@ -887,7 +889,7 @@
 	return 0;
 }
 
-static struct seq_operations raw_seq_ops = {
+static const struct seq_operations raw_seq_ops = {
 	.start = raw_seq_start,
 	.next  = raw_seq_next,
 	.stop  = raw_seq_stop,
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 37e0d4d..cb76e3c 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -82,7 +82,6 @@
 #include <linux/proc_fs.h>
 #include <linux/init.h>
 #include <linux/skbuff.h>
-#include <linux/rtnetlink.h>
 #include <linux/inetdevice.h>
 #include <linux/igmp.h>
 #include <linux/pkt_sched.h>
@@ -104,6 +103,7 @@
 #include <net/xfrm.h>
 #include <net/ip_mp_alg.h>
 #include <net/netevent.h>
+#include <net/rtnetlink.h>
 #ifdef CONFIG_SYSCTL
 #include <linux/sysctl.h>
 #endif
@@ -364,7 +364,7 @@
 	return 0;
 }
 
-static struct seq_operations rt_cache_seq_ops = {
+static const struct seq_operations rt_cache_seq_ops = {
 	.start  = rt_cache_seq_start,
 	.next   = rt_cache_seq_next,
 	.stop   = rt_cache_seq_stop,
@@ -470,7 +470,7 @@
 	return 0;
 }
 
-static struct seq_operations rt_cpu_seq_ops = {
+static const struct seq_operations rt_cpu_seq_ops = {
 	.start  = rt_cpu_seq_start,
 	.next   = rt_cpu_seq_next,
 	.stop   = rt_cpu_seq_stop,
@@ -1519,7 +1519,7 @@
 static int ip_rt_bug(struct sk_buff *skb)
 {
 	printk(KERN_DEBUG "ip_rt_bug: %u.%u.%u.%u -> %u.%u.%u.%u, %s\n",
-		NIPQUAD(skb->nh.iph->saddr), NIPQUAD(skb->nh.iph->daddr),
+		NIPQUAD(ip_hdr(skb)->saddr), NIPQUAD(ip_hdr(skb)->daddr),
 		skb->dev ? skb->dev->name : "?");
 	kfree_skb(skb);
 	return 0;
@@ -1698,9 +1698,9 @@
 		printk(KERN_WARNING "martian source %u.%u.%u.%u from "
 			"%u.%u.%u.%u, on dev %s\n",
 			NIPQUAD(daddr), NIPQUAD(saddr), dev->name);
-		if (dev->hard_header_len && skb->mac.raw) {
+		if (dev->hard_header_len && skb_mac_header_was_set(skb)) {
 			int i;
-			unsigned char *p = skb->mac.raw;
+			const unsigned char *p = skb_mac_header(skb);
 			printk(KERN_WARNING "ll header: ");
 			for (i = 0; i < dev->hard_header_len; i++, p++) {
 				printk("%02x", *p);
@@ -2134,7 +2134,7 @@
 		rcu_read_lock();
 		if ((in_dev = __in_dev_get_rcu(dev)) != NULL) {
 			int our = ip_check_mc(in_dev, daddr, saddr,
-				skb->nh.iph->protocol);
+				ip_hdr(skb)->protocol);
 			if (our
 #ifdef CONFIG_IP_MROUTE
 			    || (!LOCAL_MCAST(daddr) && IN_DEV_MFORWARD(in_dev))
@@ -2396,7 +2396,7 @@
 
 		/* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */
 		dev_out = ip_dev_find(oldflp->fl4_src);
-		if (dev_out == NULL)
+		if ((dev_out == NULL) && !(sysctl_ip_nonlocal_bind))
 			goto out;
 
 		/* I removed check for oif == dev_out->oif here.
@@ -2407,7 +2407,7 @@
 		      of another iface. --ANK
 		 */
 
-		if (oldflp->oif == 0
+		if (dev_out && oldflp->oif == 0
 		    && (MULTICAST(oldflp->fl4_dst) || oldflp->fl4_dst == htonl(0xFFFFFFFF))) {
 			/* Special hack: user can direct multicasts
 			   and limited broadcast via necessary interface
@@ -2683,7 +2683,7 @@
 		id = rt->peer->ip_id_count;
 		if (rt->peer->tcp_ts_stamp) {
 			ts = rt->peer->tcp_ts;
-			tsage = xtime.tv_sec - rt->peer->tcp_ts_stamp;
+			tsage = get_seconds() - rt->peer->tcp_ts_stamp;
 		}
 	}
 
@@ -2721,7 +2721,7 @@
 	return -EMSGSIZE;
 }
 
-int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
+static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
 {
 	struct rtmsg *rtm;
 	struct nlattr *tb[RTA_MAX+1];
@@ -2747,10 +2747,11 @@
 	/* Reserve room for dummy headers, this skb can pass
 	   through good chunk of routing engine.
 	 */
-	skb->mac.raw = skb->nh.raw = skb->data;
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
 
 	/* Bugfix: need to give ip_route_input enough of an IP header to not gag. */
-	skb->nh.iph->protocol = IPPROTO_ICMP;
+	ip_hdr(skb)->protocol = IPPROTO_ICMP;
 	skb_reserve(skb, MAX_HEADER + sizeof(struct iphdr));
 
 	src = tb[RTA_SRC] ? nla_get_be32(tb[RTA_SRC]) : 0;
@@ -3193,6 +3194,8 @@
 	xfrm_init();
 	xfrm4_init();
 #endif
+	rtnl_register(PF_INET, RTM_GETROUTE, inet_rtm_getroute, NULL);
+
 	return rc;
 }
 
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index 33016cc..2da1be0 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -125,10 +125,11 @@
 __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp)
 {
 	struct tcp_sock *tp = tcp_sk(sk);
+	const struct iphdr *iph = ip_hdr(skb);
+	const struct tcphdr *th = tcp_hdr(skb);
 	int mssind;
 	const __u16 mss = *mssp;
 
-
 	tp->last_synq_overflow = jiffies;
 
 	/* XXX sort msstab[] by probability?  Binary search? */
@@ -138,9 +139,8 @@
 
 	NET_INC_STATS_BH(LINUX_MIB_SYNCOOKIESSENT);
 
-	return secure_tcp_syn_cookie(skb->nh.iph->saddr, skb->nh.iph->daddr,
-				     skb->h.th->source, skb->h.th->dest,
-				     ntohl(skb->h.th->seq),
+	return secure_tcp_syn_cookie(iph->saddr, iph->daddr,
+				     th->source, th->dest, ntohl(th->seq),
 				     jiffies / (HZ * 60), mssind);
 }
 
@@ -157,14 +157,13 @@
  */
 static inline int cookie_check(struct sk_buff *skb, __u32 cookie)
 {
-	__u32 seq;
-	__u32 mssind;
-
-	seq = ntohl(skb->h.th->seq)-1;
-	mssind = check_tcp_syn_cookie(cookie,
-				      skb->nh.iph->saddr, skb->nh.iph->daddr,
-				      skb->h.th->source, skb->h.th->dest,
-				      seq, jiffies / (HZ * 60), COUNTER_TRIES);
+	const struct iphdr *iph = ip_hdr(skb);
+	const struct tcphdr *th = tcp_hdr(skb);
+	__u32 seq = ntohl(th->seq) - 1;
+	__u32 mssind = check_tcp_syn_cookie(cookie, iph->saddr, iph->daddr,
+					    th->source, th->dest, seq,
+					    jiffies / (HZ * 60),
+					    COUNTER_TRIES);
 
 	return mssind < NUM_MSS ? msstab[mssind] + 1 : 0;
 }
@@ -191,14 +190,15 @@
 	struct inet_request_sock *ireq;
 	struct tcp_request_sock *treq;
 	struct tcp_sock *tp = tcp_sk(sk);
-	__u32 cookie = ntohl(skb->h.th->ack_seq) - 1;
+	const struct tcphdr *th = tcp_hdr(skb);
+	__u32 cookie = ntohl(th->ack_seq) - 1;
 	struct sock *ret = sk;
 	struct request_sock *req;
 	int mss;
 	struct rtable *rt;
 	__u8 rcv_wscale;
 
-	if (!sysctl_tcp_syncookies || !skb->h.th->ack)
+	if (!sysctl_tcp_syncookies || !th->ack)
 		goto out;
 
 	if (time_after(jiffies, tp->last_synq_overflow + TCP_TIMEOUT_INIT) ||
@@ -220,12 +220,12 @@
 	}
 	ireq = inet_rsk(req);
 	treq = tcp_rsk(req);
-	treq->rcv_isn		= ntohl(skb->h.th->seq) - 1;
+	treq->rcv_isn		= ntohl(th->seq) - 1;
 	treq->snt_isn		= cookie;
 	req->mss		= mss;
-	ireq->rmt_port		= skb->h.th->source;
-	ireq->loc_addr		= skb->nh.iph->daddr;
-	ireq->rmt_addr		= skb->nh.iph->saddr;
+	ireq->rmt_port		= th->source;
+	ireq->loc_addr		= ip_hdr(skb)->daddr;
+	ireq->rmt_addr		= ip_hdr(skb)->saddr;
 	ireq->opt		= NULL;
 
 	/* We throwed the options of the initial SYN away, so we hope
@@ -261,8 +261,8 @@
 						.tos = RT_CONN_FLAGS(sk) } },
 				    .proto = IPPROTO_TCP,
 				    .uli_u = { .ports =
-					       { .sport = skb->h.th->dest,
-						 .dport = skb->h.th->source } } };
+					       { .sport = th->dest,
+						 .dport = th->source } } };
 		security_req_classify_flow(req, &fl);
 		if (ip_route_output_key(&rt, &fl)) {
 			reqsk_free(req);
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 0aa3047..6817d64 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -647,6 +647,14 @@
 		.proc_handler	= &proc_dointvec
 	},
 	{
+		.ctl_name	= NET_TCP_FRTO_RESPONSE,
+		.procname	= "tcp_frto_response",
+		.data		= &sysctl_tcp_frto_response,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec
+	},
+	{
 		.ctl_name	= NET_TCP_LOW_LATENCY,
 		.procname	= "tcp_low_latency",
 		.data		= &sysctl_tcp_low_latency,
@@ -803,6 +811,14 @@
 		.proc_handler   = &proc_allowed_congestion_control,
 		.strategy	= &strategy_allowed_congestion_control,
 	},
+	{
+		.ctl_name	= NET_TCP_MAX_SSTHRESH,
+		.procname	= "tcp_max_ssthresh",
+		.data		= &sysctl_tcp_max_ssthresh,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
 	{ .ctl_name = 0 }
 };
 
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 3834b10..d6e4886 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -297,7 +297,7 @@
  * All the sk_stream_mem_schedule() is of this nature: accounting
  * is strict, actions are advisory and have some latency.
  */
-int tcp_memory_pressure;
+int tcp_memory_pressure __read_mostly;
 
 EXPORT_SYMBOL(tcp_memory_pressure);
 
@@ -425,7 +425,7 @@
 			/* Subtract 1, if FIN is in queue. */
 			if (answ && !skb_queue_empty(&sk->sk_receive_queue))
 				answ -=
-		       ((struct sk_buff *)sk->sk_receive_queue.prev)->h.th->fin;
+		       tcp_hdr((struct sk_buff *)sk->sk_receive_queue.prev)->fin;
 		} else
 			answ = tp->urg_seq - tp->copied_seq;
 		release_sock(sk);
@@ -444,7 +444,7 @@
 		break;
 	default:
 		return -ENOIOCTLCMD;
-	};
+	}
 
 	return put_user(answ, (int __user *)arg);
 }
@@ -460,9 +460,9 @@
 	return after(tp->write_seq, tp->pushed_seq + (tp->max_window >> 1));
 }
 
-static inline void skb_entail(struct sock *sk, struct tcp_sock *tp,
-			      struct sk_buff *skb)
+static inline void skb_entail(struct sock *sk, struct sk_buff *skb)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
 	struct tcp_skb_cb *tcb = TCP_SKB_CB(skb);
 
 	skb->csum    = 0;
@@ -470,10 +470,8 @@
 	tcb->flags   = TCPCB_FLAG_ACK;
 	tcb->sacked  = 0;
 	skb_header_release(skb);
-	__skb_queue_tail(&sk->sk_write_queue, skb);
+	tcp_add_write_queue_tail(sk, skb);
 	sk_charge_skb(sk, skb);
-	if (!sk->sk_send_head)
-		sk->sk_send_head = skb;
 	if (tp->nonagle & TCP_NAGLE_PUSH)
 		tp->nonagle &= ~TCP_NAGLE_PUSH;
 }
@@ -488,15 +486,17 @@
 	}
 }
 
-static inline void tcp_push(struct sock *sk, struct tcp_sock *tp, int flags,
-			    int mss_now, int nonagle)
+static inline void tcp_push(struct sock *sk, int flags, int mss_now,
+			    int nonagle)
 {
-	if (sk->sk_send_head) {
-		struct sk_buff *skb = sk->sk_write_queue.prev;
+	struct tcp_sock *tp = tcp_sk(sk);
+
+	if (tcp_send_head(sk)) {
+		struct sk_buff *skb = tcp_write_queue_tail(sk);
 		if (!(flags & MSG_MORE) || forced_push(tp))
 			tcp_mark_push(tp, skb);
 		tcp_mark_urg(tp, flags, skb);
-		__tcp_push_pending_frames(sk, tp, mss_now,
+		__tcp_push_pending_frames(sk, mss_now,
 					  (flags & MSG_MORE) ? TCP_NAGLE_CORK : nonagle);
 	}
 }
@@ -526,13 +526,13 @@
 		goto do_error;
 
 	while (psize > 0) {
-		struct sk_buff *skb = sk->sk_write_queue.prev;
+		struct sk_buff *skb = tcp_write_queue_tail(sk);
 		struct page *page = pages[poffset / PAGE_SIZE];
 		int copy, i, can_coalesce;
 		int offset = poffset % PAGE_SIZE;
 		int size = min_t(size_t, psize, PAGE_SIZE - offset);
 
-		if (!sk->sk_send_head || (copy = size_goal - skb->len) <= 0) {
+		if (!tcp_send_head(sk) || (copy = size_goal - skb->len) <= 0) {
 new_segment:
 			if (!sk_stream_memory_free(sk))
 				goto wait_for_sndbuf;
@@ -542,7 +542,7 @@
 			if (!skb)
 				goto wait_for_memory;
 
-			skb_entail(sk, tp, skb);
+			skb_entail(sk, skb);
 			copy = size_goal;
 		}
 
@@ -588,8 +588,8 @@
 
 		if (forced_push(tp)) {
 			tcp_mark_push(tp, skb);
-			__tcp_push_pending_frames(sk, tp, mss_now, TCP_NAGLE_PUSH);
-		} else if (skb == sk->sk_send_head)
+			__tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_PUSH);
+		} else if (skb == tcp_send_head(sk))
 			tcp_push_one(sk, mss_now);
 		continue;
 
@@ -597,7 +597,7 @@
 		set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
 wait_for_memory:
 		if (copied)
-			tcp_push(sk, tp, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
+			tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
 
 		if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
 			goto do_error;
@@ -608,7 +608,7 @@
 
 out:
 	if (copied)
-		tcp_push(sk, tp, flags, mss_now, tp->nonagle);
+		tcp_push(sk, flags, mss_now, tp->nonagle);
 	return copied;
 
 do_error:
@@ -639,8 +639,9 @@
 #define TCP_PAGE(sk)	(sk->sk_sndmsg_page)
 #define TCP_OFF(sk)	(sk->sk_sndmsg_off)
 
-static inline int select_size(struct sock *sk, struct tcp_sock *tp)
+static inline int select_size(struct sock *sk)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
 	int tmp = tp->mss_cache;
 
 	if (sk->sk_route_caps & NETIF_F_SG) {
@@ -704,9 +705,9 @@
 		while (seglen > 0) {
 			int copy;
 
-			skb = sk->sk_write_queue.prev;
+			skb = tcp_write_queue_tail(sk);
 
-			if (!sk->sk_send_head ||
+			if (!tcp_send_head(sk) ||
 			    (copy = size_goal - skb->len) <= 0) {
 
 new_segment:
@@ -716,7 +717,7 @@
 				if (!sk_stream_memory_free(sk))
 					goto wait_for_sndbuf;
 
-				skb = sk_stream_alloc_pskb(sk, select_size(sk, tp),
+				skb = sk_stream_alloc_pskb(sk, select_size(sk),
 							   0, sk->sk_allocation);
 				if (!skb)
 					goto wait_for_memory;
@@ -727,7 +728,7 @@
 				if (sk->sk_route_caps & NETIF_F_ALL_CSUM)
 					skb->ip_summed = CHECKSUM_PARTIAL;
 
-				skb_entail(sk, tp, skb);
+				skb_entail(sk, skb);
 				copy = size_goal;
 			}
 
@@ -832,8 +833,8 @@
 
 			if (forced_push(tp)) {
 				tcp_mark_push(tp, skb);
-				__tcp_push_pending_frames(sk, tp, mss_now, TCP_NAGLE_PUSH);
-			} else if (skb == sk->sk_send_head)
+				__tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_PUSH);
+			} else if (skb == tcp_send_head(sk))
 				tcp_push_one(sk, mss_now);
 			continue;
 
@@ -841,7 +842,7 @@
 			set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
 wait_for_memory:
 			if (copied)
-				tcp_push(sk, tp, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
+				tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
 
 			if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
 				goto do_error;
@@ -853,16 +854,18 @@
 
 out:
 	if (copied)
-		tcp_push(sk, tp, flags, mss_now, tp->nonagle);
+		tcp_push(sk, flags, mss_now, tp->nonagle);
 	TCP_CHECK_TIMER(sk);
 	release_sock(sk);
 	return copied;
 
 do_fault:
 	if (!skb->len) {
-		if (sk->sk_send_head == skb)
-			sk->sk_send_head = NULL;
-		__skb_unlink(skb, &sk->sk_write_queue);
+		tcp_unlink_write_queue(skb, sk);
+		/* It is the one place in all of TCP, except connection
+		 * reset, where we can be unlinking the send_head.
+		 */
+		tcp_check_send_head(sk, skb);
 		sk_stream_free_skb(sk, skb);
 	}
 
@@ -1016,9 +1019,9 @@
 
 	skb_queue_walk(&sk->sk_receive_queue, skb) {
 		offset = seq - TCP_SKB_CB(skb)->seq;
-		if (skb->h.th->syn)
+		if (tcp_hdr(skb)->syn)
 			offset--;
-		if (offset < skb->len || skb->h.th->fin) {
+		if (offset < skb->len || tcp_hdr(skb)->fin) {
 			*off = offset;
 			return skb;
 		}
@@ -1070,7 +1073,7 @@
 			if (offset != skb->len)
 				break;
 		}
-		if (skb->h.th->fin) {
+		if (tcp_hdr(skb)->fin) {
 			sk_eat_skb(sk, skb, 0);
 			++seq;
 			break;
@@ -1174,11 +1177,11 @@
 				break;
 			}
 			offset = *seq - TCP_SKB_CB(skb)->seq;
-			if (skb->h.th->syn)
+			if (tcp_hdr(skb)->syn)
 				offset--;
 			if (offset < skb->len)
 				goto found_ok_skb;
-			if (skb->h.th->fin)
+			if (tcp_hdr(skb)->fin)
 				goto found_fin_ok;
 			BUG_TRAP(flags & MSG_PEEK);
 			skb = skb->next;
@@ -1389,12 +1392,12 @@
 skip_copy:
 		if (tp->urg_data && after(tp->copied_seq, tp->urg_seq)) {
 			tp->urg_data = 0;
-			tcp_fast_path_check(sk, tp);
+			tcp_fast_path_check(sk);
 		}
 		if (used + offset < skb->len)
 			continue;
 
-		if (skb->h.th->fin)
+		if (tcp_hdr(skb)->fin)
 			goto found_fin_ok;
 		if (!(flags & MSG_PEEK)) {
 			sk_eat_skb(sk, skb, copied_early);
@@ -1563,21 +1566,19 @@
 	 */
 	while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) {
 		u32 len = TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq -
-			  skb->h.th->fin;
+			  tcp_hdr(skb)->fin;
 		data_was_unread += len;
 		__kfree_skb(skb);
 	}
 
 	sk_stream_mem_reclaim(sk);
 
-	/* As outlined in draft-ietf-tcpimpl-prob-03.txt, section
-	 * 3.10, we send a RST here because data was lost.  To
-	 * witness the awful effects of the old behavior of always
-	 * doing a FIN, run an older 2.1.x kernel or 2.0.x, start
-	 * a bulk GET in an FTP client, suspend the process, wait
-	 * for the client to advertise a zero window, then kill -9
-	 * the FTP client, wheee...  Note: timeout is always zero
-	 * in such a case.
+	/* As outlined in RFC 2525, section 2.17, we send a RST here because
+	 * data was lost. To witness the awful effects of the old behavior of
+	 * always doing a FIN, run an older 2.1.x kernel or 2.0.x, start a bulk
+	 * GET in an FTP client, suspend the process, wait for the client to
+	 * advertise a zero window, then kill -9 the FTP client, wheee...
+	 * Note: timeout is always zero in such a case.
 	 */
 	if (data_was_unread) {
 		/* Unread data was tossed, zap the connection. */
@@ -1732,7 +1733,7 @@
 
 	tcp_clear_xmit_timers(sk);
 	__skb_queue_purge(&sk->sk_receive_queue);
-	sk_stream_writequeue_purge(sk);
+	tcp_write_queue_purge(sk);
 	__skb_queue_purge(&tp->out_of_order_queue);
 #ifdef CONFIG_NET_DMA
 	__skb_queue_purge(&sk->sk_async_wait_queue);
@@ -1758,7 +1759,7 @@
 	tcp_set_ca_state(sk, TCP_CA_Open);
 	tcp_clear_retrans(tp);
 	inet_csk_delack_init(sk);
-	sk->sk_send_head = NULL;
+	tcp_init_send_head(sk);
 	tp->rx_opt.saw_tstamp = 0;
 	tcp_sack_reset(&tp->rx_opt);
 	__sk_dst_reset(sk);
@@ -1830,7 +1831,7 @@
 			 * for currently queued segments.
 			 */
 			tp->nonagle |= TCP_NAGLE_OFF|TCP_NAGLE_PUSH;
-			tcp_push_pending_frames(sk, tp);
+			tcp_push_pending_frames(sk);
 		} else {
 			tp->nonagle &= ~TCP_NAGLE_OFF;
 		}
@@ -1854,7 +1855,7 @@
 			tp->nonagle &= ~TCP_NAGLE_CORK;
 			if (tp->nonagle&TCP_NAGLE_OFF)
 				tp->nonagle |= TCP_NAGLE_PUSH;
-			tcp_push_pending_frames(sk, tp);
+			tcp_push_pending_frames(sk);
 		}
 		break;
 
@@ -1954,7 +1955,8 @@
 	default:
 		err = -ENOPROTOOPT;
 		break;
-	};
+	}
+
 	release_sock(sk);
 	return err;
 }
@@ -2124,7 +2126,7 @@
 		return 0;
 	default:
 		return -ENOPROTOOPT;
-	};
+	}
 
 	if (put_user(len, optlen))
 		return -EFAULT;
@@ -2170,7 +2172,7 @@
 	if (!pskb_may_pull(skb, sizeof(*th)))
 		goto out;
 
-	th = skb->h.th;
+	th = tcp_hdr(skb);
 	thlen = th->doff * 4;
 	if (thlen < sizeof(*th))
 		goto out;
@@ -2210,7 +2212,7 @@
 	delta = htonl(oldlen + (thlen + len));
 
 	skb = segs;
-	th = skb->h.th;
+	th = tcp_hdr(skb);
 	seq = ntohl(th->seq);
 
 	do {
@@ -2219,23 +2221,25 @@
 		th->check = ~csum_fold((__force __wsum)((__force u32)th->check +
 				       (__force u32)delta));
 		if (skb->ip_summed != CHECKSUM_PARTIAL)
-			th->check = csum_fold(csum_partial(skb->h.raw, thlen,
-							   skb->csum));
+			th->check =
+			     csum_fold(csum_partial(skb_transport_header(skb),
+						    thlen, skb->csum));
 
 		seq += len;
 		skb = skb->next;
-		th = skb->h.th;
+		th = tcp_hdr(skb);
 
 		th->seq = htonl(seq);
 		th->cwr = 0;
 	} while (skb->next);
 
-	delta = htonl(oldlen + (skb->tail - skb->h.raw) + skb->data_len);
+	delta = htonl(oldlen + (skb->tail - skb->transport_header) +
+		      skb->data_len);
 	th->check = ~csum_fold((__force __wsum)((__force u32)th->check +
 				(__force u32)delta));
 	if (skb->ip_summed != CHECKSUM_PARTIAL)
-		th->check = csum_fold(csum_partial(skb->h.raw, thlen,
-						   skb->csum));
+		th->check = csum_fold(csum_partial(skb_transport_header(skb),
+						   thlen, skb->csum));
 
 out:
 	return segs;
@@ -2372,6 +2376,23 @@
 EXPORT_SYMBOL(__tcp_put_md5sig_pool);
 #endif
 
+void tcp_done(struct sock *sk)
+{
+	if(sk->sk_state == TCP_SYN_SENT || sk->sk_state == TCP_SYN_RECV)
+		TCP_INC_STATS_BH(TCP_MIB_ATTEMPTFAILS);
+
+	tcp_set_state(sk, TCP_CLOSE);
+	tcp_clear_xmit_timers(sk);
+
+	sk->sk_shutdown = SHUTDOWN_MASK;
+
+	if (!sock_flag(sk, SOCK_DEAD))
+		sk->sk_state_change(sk);
+	else
+		inet_csk_destroy_sock(sk);
+}
+EXPORT_SYMBOL_GPL(tcp_done);
+
 extern void __skb_cb_too_small_for_tcp(int, int);
 extern struct tcp_congestion_ops tcp_reno;
 
diff --git a/net/ipv4/tcp_bic.c b/net/ipv4/tcp_bic.c
index 5730333..281c9f9 100644
--- a/net/ipv4/tcp_bic.c
+++ b/net/ipv4/tcp_bic.c
@@ -206,7 +206,7 @@
 /* Track delayed acknowledgment ratio using sliding window
  * ratio = (15*ratio + sample) / 16
  */
-static void bictcp_acked(struct sock *sk, u32 cnt)
+static void bictcp_acked(struct sock *sk, u32 cnt, ktime_t last)
 {
 	const struct inet_connection_sock *icsk = inet_csk(sk);
 
diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c
index 34ae3f1..86b2653 100644
--- a/net/ipv4/tcp_cong.c
+++ b/net/ipv4/tcp_cong.c
@@ -12,6 +12,8 @@
 #include <linux/list.h>
 #include <net/tcp.h>
 
+int sysctl_tcp_max_ssthresh = 0;
+
 static DEFINE_SPINLOCK(tcp_cong_list_lock);
 static LIST_HEAD(tcp_cong_list);
 
@@ -124,7 +126,7 @@
 #endif
 
 	if (ca) {
-		ca->non_restricted = 1;	/* default is always allowed */
+		ca->flags |= TCP_CONG_NON_RESTRICTED;	/* default is always allowed */
 		list_move(&ca->list, &tcp_cong_list);
 		ret = 0;
 	}
@@ -179,7 +181,7 @@
 	*buf = '\0';
 	rcu_read_lock();
 	list_for_each_entry_rcu(ca, &tcp_cong_list, list) {
-		if (!ca->non_restricted)
+		if (!(ca->flags & TCP_CONG_NON_RESTRICTED))
 			continue;
 		offs += snprintf(buf + offs, maxlen - offs,
 				 "%s%s",
@@ -210,16 +212,16 @@
 		}
 	}
 
-	/* pass 2 clear */
+	/* pass 2 clear old values */
 	list_for_each_entry_rcu(ca, &tcp_cong_list, list)
-		ca->non_restricted = 0;
+		ca->flags &= ~TCP_CONG_NON_RESTRICTED;
 
 	/* pass 3 mark as allowed */
 	while ((name = strsep(&val, " ")) && *name) {
 		ca = tcp_ca_find(name);
 		WARN_ON(!ca);
 		if (ca)
-			ca->non_restricted = 1;
+			ca->flags |= TCP_CONG_NON_RESTRICTED;
 	}
 out:
 	spin_unlock(&tcp_cong_list_lock);
@@ -254,7 +256,7 @@
 	if (!ca)
 		err = -ENOENT;
 
-	else if (!(ca->non_restricted || capable(CAP_NET_ADMIN)))
+	else if (!((ca->flags & TCP_CONG_NON_RESTRICTED) || capable(CAP_NET_ADMIN)))
 		err = -EPERM;
 
 	else if (!try_module_get(ca->owner))
@@ -274,10 +276,13 @@
 
 
 /*
- * Linear increase during slow start
+ * Slow start (exponential increase) with
+ * RFC3742 Limited Slow Start (fast linear increase) support.
  */
 void tcp_slow_start(struct tcp_sock *tp)
 {
+	int cnt = 0;
+
 	if (sysctl_tcp_abc) {
 		/* RFC3465: Slow Start
 		 * TCP sender SHOULD increase cwnd by the number of
@@ -286,17 +291,25 @@
 		 */
 		if (tp->bytes_acked < tp->mss_cache)
 			return;
-
-		/* We MAY increase by 2 if discovered delayed ack */
-		if (sysctl_tcp_abc > 1 && tp->bytes_acked >= 2*tp->mss_cache) {
-			if (tp->snd_cwnd < tp->snd_cwnd_clamp)
-				tp->snd_cwnd++;
-		}
 	}
+
+	if (sysctl_tcp_max_ssthresh > 0 &&
+	    tp->snd_cwnd > sysctl_tcp_max_ssthresh)
+		cnt += sysctl_tcp_max_ssthresh>>1;
+	else
+		cnt += tp->snd_cwnd;
+
+	/* RFC3465: We MAY increase by 2 if discovered delayed ack */
+	if (sysctl_tcp_abc > 1 && tp->bytes_acked >= 2*tp->mss_cache)
+		cnt <<= 1;
 	tp->bytes_acked = 0;
 
-	if (tp->snd_cwnd < tp->snd_cwnd_clamp)
-		tp->snd_cwnd++;
+	tp->snd_cwnd_cnt += cnt;
+	while (tp->snd_cwnd_cnt >= tp->snd_cwnd) {
+		tp->snd_cwnd_cnt -= tp->snd_cwnd;
+		if (tp->snd_cwnd < tp->snd_cwnd_clamp)
+			tp->snd_cwnd++;
+	}
 }
 EXPORT_SYMBOL_GPL(tcp_slow_start);
 
@@ -358,8 +371,8 @@
 EXPORT_SYMBOL_GPL(tcp_reno_min_cwnd);
 
 struct tcp_congestion_ops tcp_reno = {
+	.flags		= TCP_CONG_NON_RESTRICTED,
 	.name		= "reno",
-	.non_restricted = 1,
 	.owner		= THIS_MODULE,
 	.ssthresh	= tcp_reno_ssthresh,
 	.cong_avoid	= tcp_reno_cong_avoid,
diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c
index 9a582fb..1422448 100644
--- a/net/ipv4/tcp_cubic.c
+++ b/net/ipv4/tcp_cubic.c
@@ -1,5 +1,5 @@
 /*
- * TCP CUBIC: Binary Increase Congestion control for TCP v2.0
+ * TCP CUBIC: Binary Increase Congestion control for TCP v2.1
  *
  * This is from the implementation of CUBIC TCP in
  * Injong Rhee, Lisong Xu.
@@ -51,8 +51,6 @@
 module_param(tcp_friendliness, int, 0644);
 MODULE_PARM_DESC(tcp_friendliness, "turn on/off tcp friendliness");
 
-#include <asm/div64.h>
-
 /* BIC TCP Parameters */
 struct bictcp {
 	u32	cnt;		/* increase cwnd by 1 after ACKs */
@@ -93,50 +91,51 @@
 		tcp_sk(sk)->snd_ssthresh = initial_ssthresh;
 }
 
-/* 64bit divisor, dividend and result. dynamic precision */
-static inline u_int64_t div64_64(u_int64_t dividend, u_int64_t divisor)
-{
-	u_int32_t d = divisor;
-
-	if (divisor > 0xffffffffULL) {
-		unsigned int shift = fls(divisor >> 32);
-
-		d = divisor >> shift;
-		dividend >>= shift;
-	}
-
-	/* avoid 64 bit division if possible */
-	if (dividend >> 32)
-		do_div(dividend, d);
-	else
-		dividend = (uint32_t) dividend / d;
-
-	return dividend;
-}
-
-/*
- * calculate the cubic root of x using Newton-Raphson
+/* calculate the cubic root of x using a table lookup followed by one
+ * Newton-Raphson iteration.
+ * Avg err ~= 0.195%
  */
 static u32 cubic_root(u64 a)
 {
-	u32 x, x1;
-
-	/* Initial estimate is based on:
-	 * cbrt(x) = exp(log(x) / 3)
+	u32 x, b, shift;
+	/*
+	 * cbrt(x) MSB values for x MSB values in [0..63].
+	 * Precomputed then refined by hand - Willy Tarreau
+	 *
+	 * For x in [0..63],
+	 *   v = cbrt(x << 18) - 1
+	 *   cbrt(x) = (v[x] + 10) >> 6
 	 */
-	x = 1u << (fls64(a)/3);
+	static const u8 v[] = {
+		/* 0x00 */    0,   54,   54,   54,  118,  118,  118,  118,
+		/* 0x08 */  123,  129,  134,  138,  143,  147,  151,  156,
+		/* 0x10 */  157,  161,  164,  168,  170,  173,  176,  179,
+		/* 0x18 */  181,  185,  187,  190,  192,  194,  197,  199,
+		/* 0x20 */  200,  202,  204,  206,  209,  211,  213,  215,
+		/* 0x28 */  217,  219,  221,  222,  224,  225,  227,  229,
+		/* 0x30 */  231,  232,  234,  236,  237,  239,  240,  242,
+		/* 0x38 */  244,  245,  246,  248,  250,  251,  252,  254,
+	};
+
+	b = fls64(a);
+	if (b < 7) {
+		/* a in [0..63] */
+		return ((u32)v[(u32)a] + 35) >> 6;
+	}
+
+	b = ((b * 84) >> 8) - 1;
+	shift = (a >> (b * 3));
+
+	x = ((u32)(((u32)v[shift] + 10) << b)) >> 6;
 
 	/*
-	 * Iteration based on:
+	 * Newton-Raphson iteration
 	 *                         2
 	 * x    = ( 2 * x  +  a / x  ) / 3
 	 *  k+1          k         k
 	 */
-	do {
-		x1 = x;
-		x = (2 * x + (uint32_t) div64_64(a, x*x)) / 3;
-	} while (abs(x1 - x) > 1);
-
+	x = (2 * x + (u32)div64_64(a, (u64)x * (u64)(x - 1)));
+	x = ((x * 341) >> 10);
 	return x;
 }
 
@@ -215,7 +214,9 @@
 	if (ca->delay_min > 0) {
 		/* max increment = Smax * rtt / 0.1  */
 		min_cnt = (cwnd * HZ * 8)/(10 * max_increment * ca->delay_min);
-		if (ca->cnt < min_cnt)
+
+		/* use concave growth when the target is above the origin */
+		if (ca->cnt < min_cnt && t >= ca->bic_K)
 			ca->cnt = min_cnt;
 	}
 
@@ -333,7 +334,7 @@
 /* Track delayed acknowledgment ratio using sliding window
  * ratio = (15*ratio + sample) / 16
  */
-static void bictcp_acked(struct sock *sk, u32 cnt)
+static void bictcp_acked(struct sock *sk, u32 cnt, ktime_t last)
 {
 	const struct inet_connection_sock *icsk = inet_csk(sk);
 
@@ -401,4 +402,4 @@
 MODULE_AUTHOR("Sangtae Ha, Stephen Hemminger");
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("CUBIC TCP");
-MODULE_VERSION("2.0");
+MODULE_VERSION("2.1");
diff --git a/net/ipv4/tcp_htcp.c b/net/ipv4/tcp_htcp.c
index 1020eb4..4ba4a7a 100644
--- a/net/ipv4/tcp_htcp.c
+++ b/net/ipv4/tcp_htcp.c
@@ -98,7 +98,7 @@
 	}
 }
 
-static void measure_achieved_throughput(struct sock *sk, u32 pkts_acked)
+static void measure_achieved_throughput(struct sock *sk, u32 pkts_acked, ktime_t last)
 {
 	const struct inet_connection_sock *icsk = inet_csk(sk);
 	const struct tcp_sock *tp = tcp_sk(sk);
diff --git a/net/ipv4/tcp_hybla.c b/net/ipv4/tcp_hybla.c
index 59e691d..e5be351 100644
--- a/net/ipv4/tcp_hybla.c
+++ b/net/ipv4/tcp_hybla.c
@@ -144,7 +144,7 @@
 	ca->snd_cwnd_cents += odd;
 
 	/* check when fractions goes >=128 and increase cwnd by 1. */
-	while(ca->snd_cwnd_cents >= 128) {
+	while (ca->snd_cwnd_cents >= 128) {
 		tp->snd_cwnd++;
 		ca->snd_cwnd_cents -= 128;
 		tp->snd_cwnd_cnt = 0;
diff --git a/net/ipv4/tcp_illinois.c b/net/ipv4/tcp_illinois.c
new file mode 100644
index 0000000..4adc47c
--- /dev/null
+++ b/net/ipv4/tcp_illinois.c
@@ -0,0 +1,356 @@
+/*
+ * TCP Illinois congestion control.
+ * Home page:
+ *	http://www.ews.uiuc.edu/~shaoliu/tcpillinois/index.html
+ *
+ * The algorithm is described in:
+ * "TCP-Illinois: A Loss and Delay-Based Congestion Control Algorithm
+ *  for High-Speed Networks"
+ * http://www.ews.uiuc.edu/~shaoliu/papersandslides/liubassri06perf.pdf
+ *
+ * Implemented from description in paper and ns-2 simulation.
+ * Copyright (C) 2007 Stephen Hemminger <shemminger@linux-foundation.org>
+ */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/inet_diag.h>
+#include <asm/div64.h>
+#include <net/tcp.h>
+
+#define ALPHA_SHIFT	7
+#define ALPHA_SCALE	(1u<<ALPHA_SHIFT)
+#define ALPHA_MIN	((3*ALPHA_SCALE)/10)	/* ~0.3 */
+#define ALPHA_MAX	(10*ALPHA_SCALE)	/* 10.0 */
+#define ALPHA_BASE	ALPHA_SCALE		/* 1.0 */
+#define U32_MAX		((u32)~0U)
+#define RTT_MAX		(U32_MAX / ALPHA_MAX)	/* 3.3 secs */
+
+#define BETA_SHIFT	6
+#define BETA_SCALE	(1u<<BETA_SHIFT)
+#define BETA_MIN	(BETA_SCALE/8)		/* 0.125 */
+#define BETA_MAX	(BETA_SCALE/2)		/* 0.5 */
+#define BETA_BASE	BETA_MAX
+
+static int win_thresh __read_mostly = 15;
+module_param(win_thresh, int, 0);
+MODULE_PARM_DESC(win_thresh, "Window threshold for starting adaptive sizing");
+
+static int theta __read_mostly = 5;
+module_param(theta, int, 0);
+MODULE_PARM_DESC(theta, "# of fast RTT's before full growth");
+
+/* TCP Illinois Parameters */
+struct illinois {
+	u64	sum_rtt;	/* sum of rtt's measured within last rtt */
+	u16	cnt_rtt;	/* # of rtts measured within last rtt */
+	u32	base_rtt;	/* min of all rtt in usec */
+	u32	max_rtt;	/* max of all rtt in usec */
+	u32	end_seq;	/* right edge of current RTT */
+	u32	alpha;		/* Additive increase */
+	u32	beta;		/* Muliplicative decrease */
+	u16	acked;		/* # packets acked by current ACK */
+	u8	rtt_above;	/* average rtt has gone above threshold */
+	u8	rtt_low;	/* # of rtts measurements below threshold */
+};
+
+static void rtt_reset(struct sock *sk)
+{
+	struct tcp_sock *tp = tcp_sk(sk);
+	struct illinois *ca = inet_csk_ca(sk);
+
+	ca->end_seq = tp->snd_nxt;
+	ca->cnt_rtt = 0;
+	ca->sum_rtt = 0;
+
+	/* TODO: age max_rtt? */
+}
+
+static void tcp_illinois_init(struct sock *sk)
+{
+	struct illinois *ca = inet_csk_ca(sk);
+
+	ca->alpha = ALPHA_MAX;
+	ca->beta = BETA_BASE;
+	ca->base_rtt = 0x7fffffff;
+	ca->max_rtt = 0;
+
+	ca->acked = 0;
+	ca->rtt_low = 0;
+	ca->rtt_above = 0;
+
+	rtt_reset(sk);
+}
+
+/* Measure RTT for each ack. */
+static void tcp_illinois_acked(struct sock *sk, u32 pkts_acked, ktime_t last)
+{
+	struct illinois *ca = inet_csk_ca(sk);
+	u32 rtt;
+
+	ca->acked = pkts_acked;
+
+	rtt = ktime_to_us(net_timedelta(last));
+
+	/* ignore bogus values, this prevents wraparound in alpha math */
+	if (rtt > RTT_MAX)
+		rtt = RTT_MAX;
+
+	/* keep track of minimum RTT seen so far */
+	if (ca->base_rtt > rtt)
+		ca->base_rtt = rtt;
+
+	/* and max */
+	if (ca->max_rtt < rtt)
+		ca->max_rtt = rtt;
+
+	++ca->cnt_rtt;
+	ca->sum_rtt += rtt;
+}
+
+/* Maximum queuing delay */
+static inline u32 max_delay(const struct illinois *ca)
+{
+	return ca->max_rtt - ca->base_rtt;
+}
+
+/* Average queuing delay */
+static inline u32 avg_delay(const struct illinois *ca)
+{
+	u64 t = ca->sum_rtt;
+
+	do_div(t, ca->cnt_rtt);
+	return t - ca->base_rtt;
+}
+
+/*
+ * Compute value of alpha used for additive increase.
+ * If small window then use 1.0, equivalent to Reno.
+ *
+ * For larger windows, adjust based on average delay.
+ * A. If average delay is at minimum (we are uncongested),
+ *    then use large alpha (10.0) to increase faster.
+ * B. If average delay is at maximum (getting congested)
+ *    then use small alpha (0.3)
+ *
+ * The result is a convex window growth curve.
+ */
+static u32 alpha(struct illinois *ca, u32 da, u32 dm)
+{
+	u32 d1 = dm / 100;	/* Low threshold */
+
+	if (da <= d1) {
+		/* If never got out of low delay zone, then use max */
+		if (!ca->rtt_above)
+			return ALPHA_MAX;
+
+		/* Wait for 5 good RTT's before allowing alpha to go alpha max.
+		 * This prevents one good RTT from causing sudden window increase.
+		 */
+		if (++ca->rtt_low < theta)
+			return ca->alpha;
+
+		ca->rtt_low = 0;
+		ca->rtt_above = 0;
+		return ALPHA_MAX;
+	}
+
+	ca->rtt_above = 1;
+
+	/*
+	 * Based on:
+	 *
+	 *      (dm - d1) amin amax
+	 * k1 = -------------------
+	 *         amax - amin
+	 *
+	 *       (dm - d1) amin
+	 * k2 = ----------------  - d1
+	 *        amax - amin
+	 *
+	 *             k1
+	 * alpha = ----------
+	 *          k2 + da
+	 */
+
+	dm -= d1;
+	da -= d1;
+	return (dm * ALPHA_MAX) /
+		(dm + (da  * (ALPHA_MAX - ALPHA_MIN)) / ALPHA_MIN);
+}
+
+/*
+ * Beta used for multiplicative decrease.
+ * For small window sizes returns same value as Reno (0.5)
+ *
+ * If delay is small (10% of max) then beta = 1/8
+ * If delay is up to 80% of max then beta = 1/2
+ * In between is a linear function
+ */
+static u32 beta(u32 da, u32 dm)
+{
+	u32 d2, d3;
+
+	d2 = dm / 10;
+	if (da <= d2)
+		return BETA_MIN;
+
+	d3 = (8 * dm) / 10;
+	if (da >= d3 || d3 <= d2)
+		return BETA_MAX;
+
+	/*
+	 * Based on:
+	 *
+	 *       bmin d3 - bmax d2
+	 * k3 = -------------------
+	 *         d3 - d2
+	 *
+	 *       bmax - bmin
+	 * k4 = -------------
+	 *         d3 - d2
+	 *
+	 * b = k3 + k4 da
+	 */
+	return (BETA_MIN * d3 - BETA_MAX * d2 + (BETA_MAX - BETA_MIN) * da)
+		/ (d3 - d2);
+}
+
+/* Update alpha and beta values once per RTT */
+static void update_params(struct sock *sk)
+{
+	struct tcp_sock *tp = tcp_sk(sk);
+	struct illinois *ca = inet_csk_ca(sk);
+
+	if (tp->snd_cwnd < win_thresh) {
+		ca->alpha = ALPHA_BASE;
+		ca->beta = BETA_BASE;
+	} else if (ca->cnt_rtt > 0) {
+		u32 dm = max_delay(ca);
+		u32 da = avg_delay(ca);
+
+		ca->alpha = alpha(ca, da, dm);
+		ca->beta = beta(da, dm);
+	}
+
+	rtt_reset(sk);
+}
+
+/*
+ * In case of loss, reset to default values
+ */
+static void tcp_illinois_state(struct sock *sk, u8 new_state)
+{
+	struct illinois *ca = inet_csk_ca(sk);
+
+	if (new_state == TCP_CA_Loss) {
+		ca->alpha = ALPHA_BASE;
+		ca->beta = BETA_BASE;
+		ca->rtt_low = 0;
+		ca->rtt_above = 0;
+		rtt_reset(sk);
+	}
+}
+
+/*
+ * Increase window in response to successful acknowledgment.
+ */
+static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 rtt,
+				    u32 in_flight, int flag)
+{
+	struct tcp_sock *tp = tcp_sk(sk);
+	struct illinois *ca = inet_csk_ca(sk);
+
+	if (after(ack, ca->end_seq))
+		update_params(sk);
+
+	/* RFC2861 only increase cwnd if fully utilized */
+	if (!tcp_is_cwnd_limited(sk, in_flight))
+		return;
+
+	/* In slow start */
+	if (tp->snd_cwnd <= tp->snd_ssthresh)
+		tcp_slow_start(tp);
+
+	else {
+		u32 delta;
+
+		/* snd_cwnd_cnt is # of packets since last cwnd increment */
+		tp->snd_cwnd_cnt += ca->acked;
+		ca->acked = 1;
+
+		/* This is close approximation of:
+		 * tp->snd_cwnd += alpha/tp->snd_cwnd
+		*/
+		delta = (tp->snd_cwnd_cnt * ca->alpha) >> ALPHA_SHIFT;
+		if (delta >= tp->snd_cwnd) {
+			tp->snd_cwnd = min(tp->snd_cwnd + delta / tp->snd_cwnd,
+					   (u32) tp->snd_cwnd_clamp);
+			tp->snd_cwnd_cnt = 0;
+		}
+	}
+}
+
+static u32 tcp_illinois_ssthresh(struct sock *sk)
+{
+	struct tcp_sock *tp = tcp_sk(sk);
+	struct illinois *ca = inet_csk_ca(sk);
+
+	/* Multiplicative decrease */
+	return max((tp->snd_cwnd * ca->beta) >> BETA_SHIFT, 2U);
+}
+
+
+/* Extract info for Tcp socket info provided via netlink. */
+static void tcp_illinois_info(struct sock *sk, u32 ext,
+			      struct sk_buff *skb)
+{
+	const struct illinois *ca = inet_csk_ca(sk);
+
+	if (ext & (1 << (INET_DIAG_VEGASINFO - 1))) {
+		struct tcpvegas_info info = {
+			.tcpv_enabled = 1,
+			.tcpv_rttcnt = ca->cnt_rtt,
+			.tcpv_minrtt = ca->base_rtt,
+		};
+		u64 t = ca->sum_rtt;
+
+		do_div(t, ca->cnt_rtt);
+		info.tcpv_rtt = t;
+
+		nla_put(skb, INET_DIAG_VEGASINFO, sizeof(info), &info);
+	}
+}
+
+static struct tcp_congestion_ops tcp_illinois = {
+	.flags		= TCP_CONG_RTT_STAMP,
+	.init		= tcp_illinois_init,
+	.ssthresh	= tcp_illinois_ssthresh,
+	.min_cwnd	= tcp_reno_min_cwnd,
+	.cong_avoid	= tcp_illinois_cong_avoid,
+	.set_state	= tcp_illinois_state,
+	.get_info	= tcp_illinois_info,
+	.pkts_acked	= tcp_illinois_acked,
+
+	.owner		= THIS_MODULE,
+	.name		= "illinois",
+};
+
+static int __init tcp_illinois_register(void)
+{
+	BUILD_BUG_ON(sizeof(struct illinois) > ICSK_CA_PRIV_SIZE);
+	return tcp_register_congestion_control(&tcp_illinois);
+}
+
+static void __exit tcp_illinois_unregister(void)
+{
+	tcp_unregister_congestion_control(&tcp_illinois);
+}
+
+module_init(tcp_illinois_register);
+module_exit(tcp_illinois_unregister);
+
+MODULE_AUTHOR("Stephen Hemminger, Shao Liu");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("TCP Illinois");
+MODULE_VERSION("1.0");
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 1a14191..7641b27 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -86,6 +86,7 @@
 int sysctl_tcp_rfc1337 __read_mostly;
 int sysctl_tcp_max_orphans __read_mostly = NR_FILE;
 int sysctl_tcp_frto __read_mostly;
+int sysctl_tcp_frto_response __read_mostly;
 int sysctl_tcp_nometrics_save __read_mostly;
 
 int sysctl_tcp_moderate_rcvbuf __read_mostly = 1;
@@ -100,6 +101,7 @@
 #define FLAG_ECE		0x40 /* ECE in this ACK				*/
 #define FLAG_DATA_LOST		0x80 /* SACK detected data lossage.		*/
 #define FLAG_SLOWPATH		0x100 /* Do not skip RFC checks for window update.*/
+#define FLAG_ONLY_ORIG_SACKED	0x200 /* SACKs only non-rexmit sent before RTO */
 
 #define FLAG_ACKED		(FLAG_DATA_ACKED|FLAG_SYN_ACKED)
 #define FLAG_NOT_DUP		(FLAG_DATA|FLAG_WIN_UPDATE|FLAG_ACKED)
@@ -110,6 +112,8 @@
 #define IsFack(tp) ((tp)->rx_opt.sack_ok & 2)
 #define IsDSack(tp) ((tp)->rx_opt.sack_ok & 4)
 
+#define IsSackFrto() (sysctl_tcp_frto == 0x2)
+
 #define TCP_REMNANT (TCP_FLAG_FIN|TCP_FLAG_URG|TCP_FLAG_SYN|TCP_FLAG_PSH)
 
 /* Adapt the MSS value used to make delayed ack decision to the
@@ -136,7 +140,7 @@
 		 *
 		 * "len" is invariant segment length, including TCP header.
 		 */
-		len += skb->data - skb->h.raw;
+		len += skb->data - skb_transport_header(skb);
 		if (len >= TCP_MIN_RCVMSS + sizeof(struct tcphdr) ||
 		    /* If PSH is not set, packet should be
 		     * full sized, provided peer TCP is not badly broken.
@@ -144,7 +148,7 @@
 		     * to handle super-low mtu links fairly.
 		     */
 		    (len >= TCP_MIN_MSS + sizeof(struct tcphdr) &&
-		     !(tcp_flag_word(skb->h.th)&TCP_REMNANT))) {
+		     !(tcp_flag_word(tcp_hdr(skb)) & TCP_REMNANT))) {
 			/* Subtract also invariant (if peer is RFC compliant),
 			 * tcp header plus fixed timestamp option length.
 			 * Resulting "len" is MSS free of SACK jitter.
@@ -231,9 +235,9 @@
  */
 
 /* Slow part of check#2. */
-static int __tcp_grow_window(const struct sock *sk, struct tcp_sock *tp,
-			     const struct sk_buff *skb)
+static int __tcp_grow_window(const struct sock *sk, const struct sk_buff *skb)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
 	/* Optimize this! */
 	int truesize = tcp_win_from_space(skb->truesize)/2;
 	int window = tcp_win_from_space(sysctl_tcp_rmem[2])/2;
@@ -248,9 +252,11 @@
 	return 0;
 }
 
-static void tcp_grow_window(struct sock *sk, struct tcp_sock *tp,
+static void tcp_grow_window(struct sock *sk,
 			    struct sk_buff *skb)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
+
 	/* Check #1 */
 	if (tp->rcv_ssthresh < tp->window_clamp &&
 	    (int)tp->rcv_ssthresh < tcp_space(sk) &&
@@ -263,7 +269,7 @@
 		if (tcp_win_from_space(skb->truesize) <= skb->len)
 			incr = 2*tp->advmss;
 		else
-			incr = __tcp_grow_window(sk, tp, skb);
+			incr = __tcp_grow_window(sk, skb);
 
 		if (incr) {
 			tp->rcv_ssthresh = min(tp->rcv_ssthresh + incr, tp->window_clamp);
@@ -326,8 +332,9 @@
 }
 
 /* 5. Recalculate window clamp after socket hit its memory bounds. */
-static void tcp_clamp_window(struct sock *sk, struct tcp_sock *tp)
+static void tcp_clamp_window(struct sock *sk)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
 	struct inet_connection_sock *icsk = inet_csk(sk);
 
 	icsk->icsk_ack.quick = 0;
@@ -499,8 +506,9 @@
  * each ACK we send, he increments snd_cwnd and transmits more of his
  * queue.  -DaveM
  */
-static void tcp_event_data_recv(struct sock *sk, struct tcp_sock *tp, struct sk_buff *skb)
+static void tcp_event_data_recv(struct sock *sk, struct sk_buff *skb)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
 	struct inet_connection_sock *icsk = inet_csk(sk);
 	u32 now;
 
@@ -541,7 +549,7 @@
 	TCP_ECN_check_ce(tp, skb);
 
 	if (skb->len >= 128)
-		tcp_grow_window(sk, tp, skb);
+		tcp_grow_window(sk, skb);
 }
 
 /* Called to compute a smoothed rtt estimate. The data fed to this
@@ -574,7 +582,7 @@
 	 * does not matter how to _calculate_ it. Seems, it was trap
 	 * that VJ failed to avoid. 8)
 	 */
-	if(m == 0)
+	if (m == 0)
 		m = 1;
 	if (tp->srtt != 0) {
 		m -= (tp->srtt >> 3);	/* m is now error in rtt est */
@@ -759,15 +767,17 @@
 }
 
 /* Set slow start threshold and cwnd not falling to slow start */
-void tcp_enter_cwr(struct sock *sk)
+void tcp_enter_cwr(struct sock *sk, const int set_ssthresh)
 {
 	struct tcp_sock *tp = tcp_sk(sk);
+	const struct inet_connection_sock *icsk = inet_csk(sk);
 
 	tp->prior_ssthresh = 0;
 	tp->bytes_acked = 0;
-	if (inet_csk(sk)->icsk_ca_state < TCP_CA_CWR) {
+	if (icsk->icsk_ca_state < TCP_CA_CWR) {
 		tp->undo_marker = 0;
-		tp->snd_ssthresh = inet_csk(sk)->icsk_ca_ops->ssthresh(sk);
+		if (set_ssthresh)
+			tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk);
 		tp->snd_cwnd = min(tp->snd_cwnd,
 				   tcp_packets_in_flight(tp) + 1U);
 		tp->snd_cwnd_cnt = 0;
@@ -934,7 +944,8 @@
 {
 	const struct inet_connection_sock *icsk = inet_csk(sk);
 	struct tcp_sock *tp = tcp_sk(sk);
-	unsigned char *ptr = ack_skb->h.raw + TCP_SKB_CB(ack_skb)->sacked;
+	unsigned char *ptr = (skb_transport_header(ack_skb) +
+			      TCP_SKB_CB(ack_skb)->sacked);
 	struct tcp_sack_block_wire *sp = (struct tcp_sack_block_wire *)(ptr+2);
 	struct sk_buff *cached_skb;
 	int num_sacks = (ptr[1] - TCPOLEN_SACK_BASE)>>3;
@@ -1038,7 +1049,7 @@
 	cached_skb = tp->fastpath_skb_hint;
 	cached_fack_count = tp->fastpath_cnt_hint;
 	if (!cached_skb) {
-		cached_skb = sk->sk_write_queue.next;
+		cached_skb = tcp_write_queue_head(sk);
 		cached_fack_count = 0;
 	}
 
@@ -1055,10 +1066,13 @@
 		if (after(end_seq, tp->high_seq))
 			flag |= FLAG_DATA_LOST;
 
-		sk_stream_for_retrans_queue_from(skb, sk) {
+		tcp_for_write_queue_from(skb, sk) {
 			int in_sack, pcount;
 			u8 sacked;
 
+			if (skb == tcp_send_head(sk))
+				break;
+
 			cached_skb = skb;
 			cached_fack_count = fack_count;
 			if (i == first_sack_index) {
@@ -1159,6 +1173,18 @@
 						/* clear lost hint */
 						tp->retransmit_skb_hint = NULL;
 					}
+					/* SACK enhanced F-RTO detection.
+					 * Set flag if and only if non-rexmitted
+					 * segments below frto_highmark are
+					 * SACKed (RFC4138; Appendix B).
+					 * Clearing correct due to in-order walk
+					 */
+					if (after(end_seq, tp->frto_highmark)) {
+						flag &= ~FLAG_ONLY_ORIG_SACKED;
+					} else {
+						if (!(sacked & TCPCB_RETRANS))
+							flag |= FLAG_ONLY_ORIG_SACKED;
+					}
 				}
 
 				TCP_SKB_CB(skb)->sacked |= TCPCB_SACKED_ACKED;
@@ -1195,7 +1221,9 @@
 	if (lost_retrans && icsk->icsk_ca_state == TCP_CA_Recovery) {
 		struct sk_buff *skb;
 
-		sk_stream_for_retrans_queue(skb, sk) {
+		tcp_for_write_queue(skb, sk) {
+			if (skb == tcp_send_head(sk))
+				break;
 			if (after(TCP_SKB_CB(skb)->seq, lost_retrans))
 				break;
 			if (!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una))
@@ -1224,7 +1252,8 @@
 
 	tp->left_out = tp->sacked_out + tp->lost_out;
 
-	if ((reord < tp->fackets_out) && icsk->icsk_ca_state != TCP_CA_Loss)
+	if ((reord < tp->fackets_out) && icsk->icsk_ca_state != TCP_CA_Loss &&
+	    (!tp->frto_highmark || after(tp->snd_una, tp->frto_highmark)))
 		tcp_update_reordering(sk, ((tp->fackets_out + 1) - reord), 0);
 
 #if FASTRETRANS_DEBUG > 0
@@ -1236,9 +1265,49 @@
 	return flag;
 }
 
-/* RTO occurred, but do not yet enter loss state. Instead, transmit two new
- * segments to see from the next ACKs whether any data was really missing.
- * If the RTO was spurious, new ACKs should arrive.
+/* F-RTO can only be used if TCP has never retransmitted anything other than
+ * head (SACK enhanced variant from Appendix B of RFC4138 is more robust here)
+ */
+int tcp_use_frto(struct sock *sk)
+{
+	const struct tcp_sock *tp = tcp_sk(sk);
+	struct sk_buff *skb;
+
+	if (!sysctl_tcp_frto)
+		return 0;
+
+	if (IsSackFrto())
+		return 1;
+
+	/* Avoid expensive walking of rexmit queue if possible */
+	if (tp->retrans_out > 1)
+		return 0;
+
+	skb = tcp_write_queue_head(sk);
+	skb = tcp_write_queue_next(sk, skb);	/* Skips head */
+	tcp_for_write_queue_from(skb, sk) {
+		if (skb == tcp_send_head(sk))
+			break;
+		if (TCP_SKB_CB(skb)->sacked&TCPCB_RETRANS)
+			return 0;
+		/* Short-circuit when first non-SACKed skb has been checked */
+		if (!(TCP_SKB_CB(skb)->sacked&TCPCB_SACKED_ACKED))
+			break;
+	}
+	return 1;
+}
+
+/* RTO occurred, but do not yet enter Loss state. Instead, defer RTO
+ * recovery a bit and use heuristics in tcp_process_frto() to detect if
+ * the RTO was spurious. Only clear SACKED_RETRANS of the head here to
+ * keep retrans_out counting accurate (with SACK F-RTO, other than head
+ * may still have that bit set); TCPCB_LOST and remaining SACKED_RETRANS
+ * bits are handled if the Loss state is really to be entered (in
+ * tcp_enter_frto_loss).
+ *
+ * Do like tcp_enter_loss() would; when RTO expires the second time it
+ * does:
+ *  "Reduce ssthresh if it has not yet been made inside this window."
  */
 void tcp_enter_frto(struct sock *sk)
 {
@@ -1246,39 +1315,69 @@
 	struct tcp_sock *tp = tcp_sk(sk);
 	struct sk_buff *skb;
 
-	tp->frto_counter = 1;
-
-	if (icsk->icsk_ca_state <= TCP_CA_Disorder ||
+	if ((!tp->frto_counter && icsk->icsk_ca_state <= TCP_CA_Disorder) ||
 	    tp->snd_una == tp->high_seq ||
-	    (icsk->icsk_ca_state == TCP_CA_Loss && !icsk->icsk_retransmits)) {
+	    ((icsk->icsk_ca_state == TCP_CA_Loss || tp->frto_counter) &&
+	     !icsk->icsk_retransmits)) {
 		tp->prior_ssthresh = tcp_current_ssthresh(sk);
-		tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk);
+		/* Our state is too optimistic in ssthresh() call because cwnd
+		 * is not reduced until tcp_enter_frto_loss() when previous FRTO
+		 * recovery has not yet completed. Pattern would be this: RTO,
+		 * Cumulative ACK, RTO (2xRTO for the same segment does not end
+		 * up here twice).
+		 * RFC4138 should be more specific on what to do, even though
+		 * RTO is quite unlikely to occur after the first Cumulative ACK
+		 * due to back-off and complexity of triggering events ...
+		 */
+		if (tp->frto_counter) {
+			u32 stored_cwnd;
+			stored_cwnd = tp->snd_cwnd;
+			tp->snd_cwnd = 2;
+			tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk);
+			tp->snd_cwnd = stored_cwnd;
+		} else {
+			tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk);
+		}
+		/* ... in theory, cong.control module could do "any tricks" in
+		 * ssthresh(), which means that ca_state, lost bits and lost_out
+		 * counter would have to be faked before the call occurs. We
+		 * consider that too expensive, unlikely and hacky, so modules
+		 * using these in ssthresh() must deal these incompatibility
+		 * issues if they receives CA_EVENT_FRTO and frto_counter != 0
+		 */
 		tcp_ca_event(sk, CA_EVENT_FRTO);
 	}
 
-	/* Have to clear retransmission markers here to keep the bookkeeping
-	 * in shape, even though we are not yet in Loss state.
-	 * If something was really lost, it is eventually caught up
-	 * in tcp_enter_frto_loss.
-	 */
-	tp->retrans_out = 0;
 	tp->undo_marker = tp->snd_una;
 	tp->undo_retrans = 0;
 
-	sk_stream_for_retrans_queue(skb, sk) {
-		TCP_SKB_CB(skb)->sacked &= ~TCPCB_RETRANS;
+	skb = tcp_write_queue_head(sk);
+	if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS) {
+		TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS;
+		tp->retrans_out -= tcp_skb_pcount(skb);
 	}
 	tcp_sync_left_out(tp);
 
-	tcp_set_ca_state(sk, TCP_CA_Open);
-	tp->frto_highmark = tp->snd_nxt;
+	/* Earlier loss recovery underway (see RFC4138; Appendix B).
+	 * The last condition is necessary at least in tp->frto_counter case.
+	 */
+	if (IsSackFrto() && (tp->frto_counter ||
+	    ((1 << icsk->icsk_ca_state) & (TCPF_CA_Recovery|TCPF_CA_Loss))) &&
+	    after(tp->high_seq, tp->snd_una)) {
+		tp->frto_highmark = tp->high_seq;
+	} else {
+		tp->frto_highmark = tp->snd_nxt;
+	}
+	tcp_set_ca_state(sk, TCP_CA_Disorder);
+	tp->high_seq = tp->snd_nxt;
+	tp->frto_counter = 1;
 }
 
 /* Enter Loss state after F-RTO was applied. Dupack arrived after RTO,
  * which indicates that we should follow the traditional RTO recovery,
  * i.e. mark everything lost and do go-back-N retransmission.
  */
-static void tcp_enter_frto_loss(struct sock *sk)
+static void tcp_enter_frto_loss(struct sock *sk, int allowed_segments, int flag)
 {
 	struct tcp_sock *tp = tcp_sk(sk);
 	struct sk_buff *skb;
@@ -1287,10 +1386,23 @@
 	tp->sacked_out = 0;
 	tp->lost_out = 0;
 	tp->fackets_out = 0;
+	tp->retrans_out = 0;
 
-	sk_stream_for_retrans_queue(skb, sk) {
+	tcp_for_write_queue(skb, sk) {
+		if (skb == tcp_send_head(sk))
+			break;
 		cnt += tcp_skb_pcount(skb);
-		TCP_SKB_CB(skb)->sacked &= ~TCPCB_LOST;
+		/*
+		 * Count the retransmission made on RTO correctly (only when
+		 * waiting for the first ACK and did not get it)...
+		 */
+		if ((tp->frto_counter == 1) && !(flag&FLAG_DATA_ACKED)) {
+			tp->retrans_out += tcp_skb_pcount(skb);
+			/* ...enter this if branch just for the first segment */
+			flag |= FLAG_DATA_ACKED;
+		} else {
+			TCP_SKB_CB(skb)->sacked &= ~(TCPCB_LOST|TCPCB_SACKED_RETRANS);
+		}
 		if (!(TCP_SKB_CB(skb)->sacked&TCPCB_SACKED_ACKED)) {
 
 			/* Do not mark those segments lost that were
@@ -1308,7 +1420,7 @@
 	}
 	tcp_sync_left_out(tp);
 
-	tp->snd_cwnd = tp->frto_counter + tcp_packets_in_flight(tp)+1;
+	tp->snd_cwnd = tcp_packets_in_flight(tp) + allowed_segments;
 	tp->snd_cwnd_cnt = 0;
 	tp->snd_cwnd_stamp = tcp_time_stamp;
 	tp->undo_marker = 0;
@@ -1366,7 +1478,9 @@
 	if (!how)
 		tp->undo_marker = tp->snd_una;
 
-	sk_stream_for_retrans_queue(skb, sk) {
+	tcp_for_write_queue(skb, sk) {
+		if (skb == tcp_send_head(sk))
+			break;
 		cnt += tcp_skb_pcount(skb);
 		if (TCP_SKB_CB(skb)->sacked&TCPCB_RETRANS)
 			tp->undo_marker = 0;
@@ -1401,14 +1515,14 @@
 	 * receiver _host_ is heavily congested (or buggy).
 	 * Do processing similar to RTO timeout.
 	 */
-	if ((skb = skb_peek(&sk->sk_write_queue)) != NULL &&
+	if ((skb = tcp_write_queue_head(sk)) != NULL &&
 	    (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED)) {
 		struct inet_connection_sock *icsk = inet_csk(sk);
 		NET_INC_STATS_BH(LINUX_MIB_TCPSACKRENEGING);
 
 		tcp_enter_loss(sk, 1);
 		icsk->icsk_retransmits++;
-		tcp_retransmit_skb(sk, skb_peek(&sk->sk_write_queue));
+		tcp_retransmit_skb(sk, tcp_write_queue_head(sk));
 		inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
 					  icsk->icsk_rto, TCP_RTO_MAX);
 		return 1;
@@ -1426,10 +1540,12 @@
 	return (tcp_time_stamp - TCP_SKB_CB(skb)->when > inet_csk(sk)->icsk_rto);
 }
 
-static inline int tcp_head_timedout(struct sock *sk, struct tcp_sock *tp)
+static inline int tcp_head_timedout(struct sock *sk)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
+
 	return tp->packets_out &&
-	       tcp_skb_timedout(sk, skb_peek(&sk->sk_write_queue));
+	       tcp_skb_timedout(sk, tcp_write_queue_head(sk));
 }
 
 /* Linux NewReno/SACK/FACK/ECN state machine.
@@ -1525,10 +1641,15 @@
  * Main question: may we further continue forward transmission
  * with the same cwnd?
  */
-static int tcp_time_to_recover(struct sock *sk, struct tcp_sock *tp)
+static int tcp_time_to_recover(struct sock *sk)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
 	__u32 packets_out;
 
+	/* Do not perform any recovery during FRTO algorithm */
+	if (tp->frto_counter)
+		return 0;
+
 	/* Trick#1: The loss is proven. */
 	if (tp->lost_out)
 		return 1;
@@ -1540,7 +1661,7 @@
 	/* Trick#3 : when we use RFC2988 timer restart, fast
 	 * retransmit can be triggered by timeout of queue head.
 	 */
-	if (tcp_head_timedout(sk, tp))
+	if (tcp_head_timedout(sk))
 		return 1;
 
 	/* Trick#4: It is still not OK... But will it be useful to delay
@@ -1549,7 +1670,7 @@
 	packets_out = tp->packets_out;
 	if (packets_out <= tp->reordering &&
 	    tp->sacked_out >= max_t(__u32, packets_out/2, sysctl_tcp_reordering) &&
-	    !tcp_may_send_now(sk, tp)) {
+	    !tcp_may_send_now(sk)) {
 		/* We have nothing to send. This connection is limited
 		 * either by receiver window or by application.
 		 */
@@ -1589,8 +1710,10 @@
 
 /* Account for ACK, ACKing some data in Reno Recovery phase. */
 
-static void tcp_remove_reno_sacks(struct sock *sk, struct tcp_sock *tp, int acked)
+static void tcp_remove_reno_sacks(struct sock *sk, int acked)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
+
 	if (acked > 0) {
 		/* One ACK acked hole. The rest eat duplicate ACKs. */
 		if (acked-1 >= tp->sacked_out)
@@ -1609,9 +1732,10 @@
 }
 
 /* Mark head of queue up as lost. */
-static void tcp_mark_head_lost(struct sock *sk, struct tcp_sock *tp,
+static void tcp_mark_head_lost(struct sock *sk,
 			       int packets, u32 high_seq)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
 	struct sk_buff *skb;
 	int cnt;
 
@@ -1620,11 +1744,13 @@
 		skb = tp->lost_skb_hint;
 		cnt = tp->lost_cnt_hint;
 	} else {
-		skb = sk->sk_write_queue.next;
+		skb = tcp_write_queue_head(sk);
 		cnt = 0;
 	}
 
-	sk_stream_for_retrans_queue_from(skb, sk) {
+	tcp_for_write_queue_from(skb, sk) {
+		if (skb == tcp_send_head(sk))
+			break;
 		/* TODO: do this better */
 		/* this is not the most efficient way to do this... */
 		tp->lost_skb_hint = skb;
@@ -1638,12 +1764,11 @@
 
 			/* clear xmit_retransmit_queue hints
 			 *  if this is beyond hint */
-			if(tp->retransmit_skb_hint != NULL &&
-			   before(TCP_SKB_CB(skb)->seq,
-				  TCP_SKB_CB(tp->retransmit_skb_hint)->seq)) {
-
+			if (tp->retransmit_skb_hint != NULL &&
+			    before(TCP_SKB_CB(skb)->seq,
+				   TCP_SKB_CB(tp->retransmit_skb_hint)->seq))
 				tp->retransmit_skb_hint = NULL;
-			}
+
 		}
 	}
 	tcp_sync_left_out(tp);
@@ -1651,15 +1776,17 @@
 
 /* Account newly detected lost packet(s) */
 
-static void tcp_update_scoreboard(struct sock *sk, struct tcp_sock *tp)
+static void tcp_update_scoreboard(struct sock *sk)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
+
 	if (IsFack(tp)) {
 		int lost = tp->fackets_out - tp->reordering;
 		if (lost <= 0)
 			lost = 1;
-		tcp_mark_head_lost(sk, tp, lost, tp->high_seq);
+		tcp_mark_head_lost(sk, lost, tp->high_seq);
 	} else {
-		tcp_mark_head_lost(sk, tp, 1, tp->high_seq);
+		tcp_mark_head_lost(sk, 1, tp->high_seq);
 	}
 
 	/* New heuristics: it is possible only after we switched
@@ -1667,13 +1794,15 @@
 	 * Hence, we can detect timed out packets during fast
 	 * retransmit without falling to slow start.
 	 */
-	if (!IsReno(tp) && tcp_head_timedout(sk, tp)) {
+	if (!IsReno(tp) && tcp_head_timedout(sk)) {
 		struct sk_buff *skb;
 
 		skb = tp->scoreboard_skb_hint ? tp->scoreboard_skb_hint
-			: sk->sk_write_queue.next;
+			: tcp_write_queue_head(sk);
 
-		sk_stream_for_retrans_queue_from(skb, sk) {
+		tcp_for_write_queue_from(skb, sk) {
+			if (skb == tcp_send_head(sk))
+				break;
 			if (!tcp_skb_timedout(sk, skb))
 				break;
 
@@ -1745,9 +1874,11 @@
 /* Undo procedures. */
 
 #if FASTRETRANS_DEBUG > 1
-static void DBGUNDO(struct sock *sk, struct tcp_sock *tp, const char *msg)
+static void DBGUNDO(struct sock *sk, const char *msg)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
 	struct inet_sock *inet = inet_sk(sk);
+
 	printk(KERN_DEBUG "Undo %s %u.%u.%u.%u/%u c%u l%u ss%u/%u p%u\n",
 	       msg,
 	       NIPQUAD(inet->daddr), ntohs(inet->dport),
@@ -1793,13 +1924,15 @@
 }
 
 /* People celebrate: "We love our President!" */
-static int tcp_try_undo_recovery(struct sock *sk, struct tcp_sock *tp)
+static int tcp_try_undo_recovery(struct sock *sk)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
+
 	if (tcp_may_undo(tp)) {
 		/* Happy end! We did not retransmit anything
 		 * or our original transmission succeeded.
 		 */
-		DBGUNDO(sk, tp, inet_csk(sk)->icsk_ca_state == TCP_CA_Loss ? "loss" : "retrans");
+		DBGUNDO(sk, inet_csk(sk)->icsk_ca_state == TCP_CA_Loss ? "loss" : "retrans");
 		tcp_undo_cwr(sk, 1);
 		if (inet_csk(sk)->icsk_ca_state == TCP_CA_Loss)
 			NET_INC_STATS_BH(LINUX_MIB_TCPLOSSUNDO);
@@ -1819,10 +1952,12 @@
 }
 
 /* Try to undo cwnd reduction, because D-SACKs acked all retransmitted data */
-static void tcp_try_undo_dsack(struct sock *sk, struct tcp_sock *tp)
+static void tcp_try_undo_dsack(struct sock *sk)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
+
 	if (tp->undo_marker && !tp->undo_retrans) {
-		DBGUNDO(sk, tp, "D-SACK");
+		DBGUNDO(sk, "D-SACK");
 		tcp_undo_cwr(sk, 1);
 		tp->undo_marker = 0;
 		NET_INC_STATS_BH(LINUX_MIB_TCPDSACKUNDO);
@@ -1831,9 +1966,9 @@
 
 /* Undo during fast recovery after partial ACK. */
 
-static int tcp_try_undo_partial(struct sock *sk, struct tcp_sock *tp,
-				int acked)
+static int tcp_try_undo_partial(struct sock *sk, int acked)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
 	/* Partial ACK arrived. Force Hoe's retransmit. */
 	int failed = IsReno(tp) || tp->fackets_out>tp->reordering;
 
@@ -1846,7 +1981,7 @@
 
 		tcp_update_reordering(sk, tcp_fackets_out(tp) + acked, 1);
 
-		DBGUNDO(sk, tp, "Hoe");
+		DBGUNDO(sk, "Hoe");
 		tcp_undo_cwr(sk, 0);
 		NET_INC_STATS_BH(LINUX_MIB_TCPPARTIALUNDO);
 
@@ -1860,17 +1995,21 @@
 }
 
 /* Undo during loss recovery after partial ACK. */
-static int tcp_try_undo_loss(struct sock *sk, struct tcp_sock *tp)
+static int tcp_try_undo_loss(struct sock *sk)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
+
 	if (tcp_may_undo(tp)) {
 		struct sk_buff *skb;
-		sk_stream_for_retrans_queue(skb, sk) {
+		tcp_for_write_queue(skb, sk) {
+			if (skb == tcp_send_head(sk))
+				break;
 			TCP_SKB_CB(skb)->sacked &= ~TCPCB_LOST;
 		}
 
 		clear_all_retrans_hints(tp);
 
-		DBGUNDO(sk, tp, "partial loss");
+		DBGUNDO(sk, "partial loss");
 		tp->lost_out = 0;
 		tp->left_out = tp->sacked_out;
 		tcp_undo_cwr(sk, 1);
@@ -1892,15 +2031,17 @@
 	tcp_ca_event(sk, CA_EVENT_COMPLETE_CWR);
 }
 
-static void tcp_try_to_open(struct sock *sk, struct tcp_sock *tp, int flag)
+static void tcp_try_to_open(struct sock *sk, int flag)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
+
 	tp->left_out = tp->sacked_out;
 
 	if (tp->retrans_out == 0)
 		tp->retrans_stamp = 0;
 
 	if (flag&FLAG_ECE)
-		tcp_enter_cwr(sk);
+		tcp_enter_cwr(sk, 1);
 
 	if (inet_csk(sk)->icsk_ca_state != TCP_CA_CWR) {
 		int state = TCP_CA_Open;
@@ -1987,7 +2128,7 @@
 	    before(tp->snd_una, tp->high_seq) &&
 	    icsk->icsk_ca_state != TCP_CA_Open &&
 	    tp->fackets_out > tp->reordering) {
-		tcp_mark_head_lost(sk, tp, tp->fackets_out-tp->reordering, tp->high_seq);
+		tcp_mark_head_lost(sk, tp->fackets_out-tp->reordering, tp->high_seq);
 		NET_INC_STATS_BH(LINUX_MIB_TCPLOSS);
 	}
 
@@ -1997,14 +2138,13 @@
 	/* E. Check state exit conditions. State can be terminated
 	 *    when high_seq is ACKed. */
 	if (icsk->icsk_ca_state == TCP_CA_Open) {
-		if (!sysctl_tcp_frto)
-			BUG_TRAP(tp->retrans_out == 0);
+		BUG_TRAP(tp->retrans_out == 0);
 		tp->retrans_stamp = 0;
 	} else if (!before(tp->snd_una, tp->high_seq)) {
 		switch (icsk->icsk_ca_state) {
 		case TCP_CA_Loss:
 			icsk->icsk_retransmits = 0;
-			if (tcp_try_undo_recovery(sk, tp))
+			if (tcp_try_undo_recovery(sk))
 				return;
 			break;
 
@@ -2018,7 +2158,7 @@
 			break;
 
 		case TCP_CA_Disorder:
-			tcp_try_undo_dsack(sk, tp);
+			tcp_try_undo_dsack(sk);
 			if (!tp->undo_marker ||
 			    /* For SACK case do not Open to allow to undo
 			     * catching for all duplicate ACKs. */
@@ -2031,7 +2171,7 @@
 		case TCP_CA_Recovery:
 			if (IsReno(tp))
 				tcp_reset_reno_sack(tp);
-			if (tcp_try_undo_recovery(sk, tp))
+			if (tcp_try_undo_recovery(sk))
 				return;
 			tcp_complete_cwr(sk);
 			break;
@@ -2047,14 +2187,14 @@
 		} else {
 			int acked = prior_packets - tp->packets_out;
 			if (IsReno(tp))
-				tcp_remove_reno_sacks(sk, tp, acked);
-			is_dupack = tcp_try_undo_partial(sk, tp, acked);
+				tcp_remove_reno_sacks(sk, acked);
+			is_dupack = tcp_try_undo_partial(sk, acked);
 		}
 		break;
 	case TCP_CA_Loss:
 		if (flag&FLAG_DATA_ACKED)
 			icsk->icsk_retransmits = 0;
-		if (!tcp_try_undo_loss(sk, tp)) {
+		if (!tcp_try_undo_loss(sk)) {
 			tcp_moderate_cwnd(tp);
 			tcp_xmit_retransmit_queue(sk);
 			return;
@@ -2071,10 +2211,10 @@
 		}
 
 		if (icsk->icsk_ca_state == TCP_CA_Disorder)
-			tcp_try_undo_dsack(sk, tp);
+			tcp_try_undo_dsack(sk);
 
-		if (!tcp_time_to_recover(sk, tp)) {
-			tcp_try_to_open(sk, tp, flag);
+		if (!tcp_time_to_recover(sk)) {
+			tcp_try_to_open(sk, flag);
 			return;
 		}
 
@@ -2113,8 +2253,8 @@
 		tcp_set_ca_state(sk, TCP_CA_Recovery);
 	}
 
-	if (is_dupack || tcp_head_timedout(sk, tp))
-		tcp_update_scoreboard(sk, tp);
+	if (is_dupack || tcp_head_timedout(sk))
+		tcp_update_scoreboard(sk);
 	tcp_cwnd_down(sk);
 	tcp_xmit_retransmit_queue(sk);
 }
@@ -2190,8 +2330,10 @@
  * RFC2988 recommends to restart timer to now+rto.
  */
 
-static void tcp_ack_packets_out(struct sock *sk, struct tcp_sock *tp)
+static void tcp_ack_packets_out(struct sock *sk)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
+
 	if (!tp->packets_out) {
 		inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS);
 	} else {
@@ -2255,14 +2397,6 @@
 	return acked;
 }
 
-static u32 tcp_usrtt(struct timeval *tv)
-{
-	struct timeval now;
-
-	do_gettimeofday(&now);
-	return (now.tv_sec - tv->tv_sec) * 1000000 + (now.tv_usec - tv->tv_usec);
-}
-
 /* Remove acknowledged frames from the retransmission queue. */
 static int tcp_clean_rtx_queue(struct sock *sk, __s32 *seq_rtt_p)
 {
@@ -2273,12 +2407,10 @@
 	int acked = 0;
 	__s32 seq_rtt = -1;
 	u32 pkts_acked = 0;
-	void (*rtt_sample)(struct sock *sk, u32 usrtt)
-		= icsk->icsk_ca_ops->rtt_sample;
-	struct timeval tv = { .tv_sec = 0, .tv_usec = 0 };
+	ktime_t last_ackt = ktime_set(0,0);
 
-	while ((skb = skb_peek(&sk->sk_write_queue)) &&
-	       skb != sk->sk_send_head) {
+	while ((skb = tcp_write_queue_head(sk)) &&
+	       skb != tcp_send_head(sk)) {
 		struct tcp_skb_cb *scb = TCP_SKB_CB(skb);
 		__u8 sacked = scb->sacked;
 
@@ -2318,13 +2450,13 @@
 
 		if (sacked) {
 			if (sacked & TCPCB_RETRANS) {
-				if(sacked & TCPCB_SACKED_RETRANS)
+				if (sacked & TCPCB_SACKED_RETRANS)
 					tp->retrans_out -= tcp_skb_pcount(skb);
 				acked |= FLAG_RETRANS_DATA_ACKED;
 				seq_rtt = -1;
 			} else if (seq_rtt < 0) {
 				seq_rtt = now - scb->when;
-				skb_get_timestamp(skb, &tv);
+				last_ackt = skb->tstamp;
 			}
 			if (sacked & TCPCB_SACKED_ACKED)
 				tp->sacked_out -= tcp_skb_pcount(skb);
@@ -2337,23 +2469,24 @@
 			}
 		} else if (seq_rtt < 0) {
 			seq_rtt = now - scb->when;
-			skb_get_timestamp(skb, &tv);
+			last_ackt = skb->tstamp;
 		}
 		tcp_dec_pcount_approx(&tp->fackets_out, skb);
 		tcp_packets_out_dec(tp, skb);
-		__skb_unlink(skb, &sk->sk_write_queue);
+		tcp_unlink_write_queue(skb, sk);
 		sk_stream_free_skb(sk, skb);
 		clear_all_retrans_hints(tp);
 	}
 
 	if (acked&FLAG_ACKED) {
-		tcp_ack_update_rtt(sk, acked, seq_rtt);
-		tcp_ack_packets_out(sk, tp);
-		if (rtt_sample && !(acked & FLAG_RETRANS_DATA_ACKED))
-			(*rtt_sample)(sk, tcp_usrtt(&tv));
+		const struct tcp_congestion_ops *ca_ops
+			= inet_csk(sk)->icsk_ca_ops;
 
-		if (icsk->icsk_ca_ops->pkts_acked)
-			icsk->icsk_ca_ops->pkts_acked(sk, pkts_acked);
+		tcp_ack_update_rtt(sk, acked, seq_rtt);
+		tcp_ack_packets_out(sk);
+
+		if (ca_ops->pkts_acked)
+			ca_ops->pkts_acked(sk, pkts_acked, last_ackt);
 	}
 
 #if FASTRETRANS_DEBUG > 0
@@ -2390,7 +2523,7 @@
 
 	/* Was it a usable window open? */
 
-	if (!after(TCP_SKB_CB(sk->sk_send_head)->end_seq,
+	if (!after(TCP_SKB_CB(tcp_send_head(sk))->end_seq,
 		   tp->snd_una + tp->snd_wnd)) {
 		icsk->icsk_backoff = 0;
 		inet_csk_clear_xmit_timer(sk, ICSK_TIME_PROBE0);
@@ -2433,13 +2566,14 @@
  * Window update algorithm, described in RFC793/RFC1122 (used in linux-2.2
  * and in FreeBSD. NetBSD's one is even worse.) is wrong.
  */
-static int tcp_ack_update_window(struct sock *sk, struct tcp_sock *tp,
-				 struct sk_buff *skb, u32 ack, u32 ack_seq)
+static int tcp_ack_update_window(struct sock *sk, struct sk_buff *skb, u32 ack,
+				 u32 ack_seq)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
 	int flag = 0;
-	u32 nwin = ntohs(skb->h.th->window);
+	u32 nwin = ntohs(tcp_hdr(skb)->window);
 
-	if (likely(!skb->h.th->syn))
+	if (likely(!tcp_hdr(skb)->syn))
 		nwin <<= tp->rx_opt.snd_wscale;
 
 	if (tcp_may_update_window(tp, ack, ack_seq, nwin)) {
@@ -2453,7 +2587,7 @@
 			 * fast path is recovered for sending TCP.
 			 */
 			tp->pred_flags = 0;
-			tcp_fast_path_check(sk, tp);
+			tcp_fast_path_check(sk);
 
 			if (nwin > tp->max_window) {
 				tp->max_window = nwin;
@@ -2467,39 +2601,139 @@
 	return flag;
 }
 
-static void tcp_process_frto(struct sock *sk, u32 prior_snd_una)
+/* A very conservative spurious RTO response algorithm: reduce cwnd and
+ * continue in congestion avoidance.
+ */
+static void tcp_conservative_spur_to_response(struct tcp_sock *tp)
+{
+	tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh);
+	tp->snd_cwnd_cnt = 0;
+	tcp_moderate_cwnd(tp);
+}
+
+/* A conservative spurious RTO response algorithm: reduce cwnd using
+ * rate halving and continue in congestion avoidance.
+ */
+static void tcp_ratehalving_spur_to_response(struct sock *sk)
+{
+	tcp_enter_cwr(sk, 0);
+}
+
+static void tcp_undo_spur_to_response(struct sock *sk, int flag)
+{
+	if (flag&FLAG_ECE)
+		tcp_ratehalving_spur_to_response(sk);
+	else
+		tcp_undo_cwr(sk, 1);
+}
+
+/* F-RTO spurious RTO detection algorithm (RFC4138)
+ *
+ * F-RTO affects during two new ACKs following RTO (well, almost, see inline
+ * comments). State (ACK number) is kept in frto_counter. When ACK advances
+ * window (but not to or beyond highest sequence sent before RTO):
+ *   On First ACK,  send two new segments out.
+ *   On Second ACK, RTO was likely spurious. Do spurious response (response
+ *                  algorithm is not part of the F-RTO detection algorithm
+ *                  given in RFC4138 but can be selected separately).
+ * Otherwise (basically on duplicate ACK), RTO was (likely) caused by a loss
+ * and TCP falls back to conventional RTO recovery. F-RTO allows overriding
+ * of Nagle, this is done using frto_counter states 2 and 3, when a new data
+ * segment of any size sent during F-RTO, state 2 is upgraded to 3.
+ *
+ * Rationale: if the RTO was spurious, new ACKs should arrive from the
+ * original window even after we transmit two new data segments.
+ *
+ * SACK version:
+ *   on first step, wait until first cumulative ACK arrives, then move to
+ *   the second step. In second step, the next ACK decides.
+ *
+ * F-RTO is implemented (mainly) in four functions:
+ *   - tcp_use_frto() is used to determine if TCP is can use F-RTO
+ *   - tcp_enter_frto() prepares TCP state on RTO if F-RTO is used, it is
+ *     called when tcp_use_frto() showed green light
+ *   - tcp_process_frto() handles incoming ACKs during F-RTO algorithm
+ *   - tcp_enter_frto_loss() is called if there is not enough evidence
+ *     to prove that the RTO is indeed spurious. It transfers the control
+ *     from F-RTO to the conventional RTO recovery
+ */
+static int tcp_process_frto(struct sock *sk, u32 prior_snd_una, int flag)
 {
 	struct tcp_sock *tp = tcp_sk(sk);
 
 	tcp_sync_left_out(tp);
 
-	if (tp->snd_una == prior_snd_una ||
-	    !before(tp->snd_una, tp->frto_highmark)) {
-		/* RTO was caused by loss, start retransmitting in
-		 * go-back-N slow start
+	/* Duplicate the behavior from Loss state (fastretrans_alert) */
+	if (flag&FLAG_DATA_ACKED)
+		inet_csk(sk)->icsk_retransmits = 0;
+
+	if (!before(tp->snd_una, tp->frto_highmark)) {
+		tcp_enter_frto_loss(sk, (tp->frto_counter == 1 ? 2 : 3), flag);
+		return 1;
+	}
+
+	if (!IsSackFrto() || IsReno(tp)) {
+		/* RFC4138 shortcoming in step 2; should also have case c):
+		 * ACK isn't duplicate nor advances window, e.g., opposite dir
+		 * data, winupdate
 		 */
-		tcp_enter_frto_loss(sk);
-		return;
+		if ((tp->snd_una == prior_snd_una) && (flag&FLAG_NOT_DUP) &&
+		    !(flag&FLAG_FORWARD_PROGRESS))
+			return 1;
+
+		if (!(flag&FLAG_DATA_ACKED)) {
+			tcp_enter_frto_loss(sk, (tp->frto_counter == 1 ? 0 : 3),
+					    flag);
+			return 1;
+		}
+	} else {
+		if (!(flag&FLAG_DATA_ACKED) && (tp->frto_counter == 1)) {
+			/* Prevent sending of new data. */
+			tp->snd_cwnd = min(tp->snd_cwnd,
+					   tcp_packets_in_flight(tp));
+			return 1;
+		}
+
+		if ((tp->frto_counter >= 2) &&
+		    (!(flag&FLAG_FORWARD_PROGRESS) ||
+		     ((flag&FLAG_DATA_SACKED) && !(flag&FLAG_ONLY_ORIG_SACKED)))) {
+			/* RFC4138 shortcoming (see comment above) */
+			if (!(flag&FLAG_FORWARD_PROGRESS) && (flag&FLAG_NOT_DUP))
+				return 1;
+
+			tcp_enter_frto_loss(sk, 3, flag);
+			return 1;
+		}
 	}
 
 	if (tp->frto_counter == 1) {
-		/* First ACK after RTO advances the window: allow two new
-		 * segments out.
-		 */
-		tp->snd_cwnd = tcp_packets_in_flight(tp) + 2;
-	} else {
-		/* Also the second ACK after RTO advances the window.
-		 * The RTO was likely spurious. Reduce cwnd and continue
-		 * in congestion avoidance
-		 */
-		tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh);
-		tcp_moderate_cwnd(tp);
-	}
+		/* Sending of the next skb must be allowed or no FRTO */
+		if (!tcp_send_head(sk) ||
+		    after(TCP_SKB_CB(tcp_send_head(sk))->end_seq,
+				     tp->snd_una + tp->snd_wnd)) {
+			tcp_enter_frto_loss(sk, (tp->frto_counter == 1 ? 2 : 3),
+					    flag);
+			return 1;
+		}
 
-	/* F-RTO affects on two new ACKs following RTO.
-	 * At latest on third ACK the TCP behavior is back to normal.
-	 */
-	tp->frto_counter = (tp->frto_counter + 1) % 3;
+		tp->snd_cwnd = tcp_packets_in_flight(tp) + 2;
+		tp->frto_counter = 2;
+		return 1;
+	} else {
+		switch (sysctl_tcp_frto_response) {
+		case 2:
+			tcp_undo_spur_to_response(sk, flag);
+			break;
+		case 1:
+			tcp_conservative_spur_to_response(tp);
+			break;
+		default:
+			tcp_ratehalving_spur_to_response(sk);
+			break;
+		}
+		tp->frto_counter = 0;
+	}
+	return 0;
 }
 
 /* This routine deals with incoming acks, but not outgoing ones. */
@@ -2513,6 +2747,7 @@
 	u32 prior_in_flight;
 	s32 seq_rtt;
 	int prior_packets;
+	int frto_cwnd = 0;
 
 	/* If the ack is newer than sent or older than previous acks
 	 * then we can probably ignore it.
@@ -2549,12 +2784,12 @@
 		else
 			NET_INC_STATS_BH(LINUX_MIB_TCPPUREACKS);
 
-		flag |= tcp_ack_update_window(sk, tp, skb, ack, ack_seq);
+		flag |= tcp_ack_update_window(sk, skb, ack, ack_seq);
 
 		if (TCP_SKB_CB(skb)->sacked)
 			flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una);
 
-		if (TCP_ECN_rcv_ecn_echo(tp, skb->h.th))
+		if (TCP_ECN_rcv_ecn_echo(tp, tcp_hdr(skb)))
 			flag |= FLAG_ECE;
 
 		tcp_ca_event(sk, CA_EVENT_SLOW_ACK);
@@ -2575,15 +2810,16 @@
 	flag |= tcp_clean_rtx_queue(sk, &seq_rtt);
 
 	if (tp->frto_counter)
-		tcp_process_frto(sk, prior_snd_una);
+		frto_cwnd = tcp_process_frto(sk, prior_snd_una, flag);
 
 	if (tcp_ack_is_dubious(sk, flag)) {
 		/* Advance CWND, if state allows this. */
-		if ((flag & FLAG_DATA_ACKED) && tcp_may_raise_cwnd(sk, flag))
+		if ((flag & FLAG_DATA_ACKED) && !frto_cwnd &&
+		    tcp_may_raise_cwnd(sk, flag))
 			tcp_cong_avoid(sk, ack,  seq_rtt, prior_in_flight, 0);
 		tcp_fastretrans_alert(sk, prior_snd_una, prior_packets, flag);
 	} else {
-		if ((flag & FLAG_DATA_ACKED))
+		if ((flag & FLAG_DATA_ACKED) && !frto_cwnd)
 			tcp_cong_avoid(sk, ack, seq_rtt, prior_in_flight, 1);
 	}
 
@@ -2599,7 +2835,7 @@
 	 * being used to time the probes, and is probably far higher than
 	 * it needs to be for normal retransmission.
 	 */
-	if (sk->sk_send_head)
+	if (tcp_send_head(sk))
 		tcp_ack_probe(sk);
 	return 1;
 
@@ -2620,13 +2856,13 @@
 void tcp_parse_options(struct sk_buff *skb, struct tcp_options_received *opt_rx, int estab)
 {
 	unsigned char *ptr;
-	struct tcphdr *th = skb->h.th;
+	struct tcphdr *th = tcp_hdr(skb);
 	int length=(th->doff*4)-sizeof(struct tcphdr);
 
 	ptr = (unsigned char *)(th + 1);
 	opt_rx->saw_tstamp = 0;
 
-	while(length>0) {
+	while (length > 0) {
 		int opcode=*ptr++;
 		int opsize;
 
@@ -2642,9 +2878,9 @@
 					return;
 				if (opsize > length)
 					return;	/* don't parse partial options */
-				switch(opcode) {
+				switch (opcode) {
 				case TCPOPT_MSS:
-					if(opsize==TCPOLEN_MSS && th->syn && !estab) {
+					if (opsize==TCPOLEN_MSS && th->syn && !estab) {
 						u16 in_mss = ntohs(get_unaligned((__be16 *)ptr));
 						if (in_mss) {
 							if (opt_rx->user_mss && opt_rx->user_mss < in_mss)
@@ -2654,12 +2890,12 @@
 					}
 					break;
 				case TCPOPT_WINDOW:
-					if(opsize==TCPOLEN_WINDOW && th->syn && !estab)
+					if (opsize==TCPOLEN_WINDOW && th->syn && !estab)
 						if (sysctl_tcp_window_scaling) {
 							__u8 snd_wscale = *(__u8 *) ptr;
 							opt_rx->wscale_ok = 1;
 							if (snd_wscale > 14) {
-								if(net_ratelimit())
+								if (net_ratelimit())
 									printk(KERN_INFO "tcp_parse_options: Illegal window "
 									       "scaling value %d >14 received.\n",
 									       snd_wscale);
@@ -2669,7 +2905,7 @@
 						}
 					break;
 				case TCPOPT_TIMESTAMP:
-					if(opsize==TCPOLEN_TIMESTAMP) {
+					if (opsize==TCPOLEN_TIMESTAMP) {
 						if ((estab && opt_rx->tstamp_ok) ||
 						    (!estab && sysctl_tcp_timestamps)) {
 							opt_rx->saw_tstamp = 1;
@@ -2679,7 +2915,7 @@
 					}
 					break;
 				case TCPOPT_SACK_PERM:
-					if(opsize==TCPOLEN_SACK_PERM && th->syn && !estab) {
+					if (opsize==TCPOLEN_SACK_PERM && th->syn && !estab) {
 						if (sysctl_tcp_sack) {
 							opt_rx->sack_ok = 1;
 							tcp_sack_reset(opt_rx);
@@ -2688,7 +2924,7 @@
 					break;
 
 				case TCPOPT_SACK:
-					if((opsize >= (TCPOLEN_SACK_BASE + TCPOLEN_SACK_PERBLOCK)) &&
+					if ((opsize >= (TCPOLEN_SACK_BASE + TCPOLEN_SACK_PERBLOCK)) &&
 					   !((opsize - TCPOLEN_SACK_BASE) % TCPOLEN_SACK_PERBLOCK) &&
 					   opt_rx->sack_ok) {
 						TCP_SKB_CB(skb)->sacked = (ptr - 2) - (unsigned char *)th;
@@ -2701,10 +2937,11 @@
 					 */
 					break;
 #endif
-				};
+				}
+
 				ptr+=opsize-2;
 				length-=opsize;
-		};
+		}
 	}
 }
 
@@ -2737,7 +2974,7 @@
 static inline void tcp_store_ts_recent(struct tcp_sock *tp)
 {
 	tp->rx_opt.ts_recent = tp->rx_opt.rcv_tsval;
-	tp->rx_opt.ts_recent_stamp = xtime.tv_sec;
+	tp->rx_opt.ts_recent_stamp = get_seconds();
 }
 
 static inline void tcp_replace_ts_recent(struct tcp_sock *tp, u32 seq)
@@ -2750,8 +2987,8 @@
 		 * Not only, also it occurs for expired timestamps.
 		 */
 
-		if((s32)(tp->rx_opt.rcv_tsval - tp->rx_opt.ts_recent) >= 0 ||
-		   xtime.tv_sec >= tp->rx_opt.ts_recent_stamp + TCP_PAWS_24DAYS)
+		if ((s32)(tp->rx_opt.rcv_tsval - tp->rx_opt.ts_recent) >= 0 ||
+		   get_seconds() >= tp->rx_opt.ts_recent_stamp + TCP_PAWS_24DAYS)
 			tcp_store_ts_recent(tp);
 	}
 }
@@ -2782,7 +3019,7 @@
 static int tcp_disordered_ack(const struct sock *sk, const struct sk_buff *skb)
 {
 	struct tcp_sock *tp = tcp_sk(sk);
-	struct tcphdr *th = skb->h.th;
+	struct tcphdr *th = tcp_hdr(skb);
 	u32 seq = TCP_SKB_CB(skb)->seq;
 	u32 ack = TCP_SKB_CB(skb)->ack_seq;
 
@@ -2803,7 +3040,7 @@
 {
 	const struct tcp_sock *tp = tcp_sk(sk);
 	return ((s32)(tp->rx_opt.ts_recent - tp->rx_opt.rcv_tsval) > TCP_PAWS_WINDOW &&
-		xtime.tv_sec < tp->rx_opt.ts_recent_stamp + TCP_PAWS_24DAYS &&
+		get_seconds() < tp->rx_opt.ts_recent_stamp + TCP_PAWS_24DAYS &&
 		!tcp_disordered_ack(sk, skb));
 }
 
@@ -2910,7 +3147,7 @@
 			printk(KERN_ERR "%s: Impossible, sk->sk_state=%d\n",
 			       __FUNCTION__, sk->sk_state);
 			break;
-	};
+	}
 
 	/* It _is_ possible, that we have something out-of-order _after_ FIN.
 	 * Probably, we should reset in this case. For now drop them.
@@ -3009,7 +3246,7 @@
 			 */
 			tp->rx_opt.num_sacks--;
 			tp->rx_opt.eff_sacks = min(tp->rx_opt.num_sacks + tp->rx_opt.dsack, 4 - tp->rx_opt.tstamp_ok);
-			for(i=this_sack; i < tp->rx_opt.num_sacks; i++)
+			for (i=this_sack; i < tp->rx_opt.num_sacks; i++)
 				sp[i] = sp[i+1];
 			continue;
 		}
@@ -3062,7 +3299,7 @@
 		tp->rx_opt.num_sacks--;
 		sp--;
 	}
-	for(; this_sack > 0; this_sack--, sp--)
+	for (; this_sack > 0; this_sack--, sp--)
 		*sp = *(sp-1);
 
 new_sack:
@@ -3088,7 +3325,7 @@
 		return;
 	}
 
-	for(this_sack = 0; this_sack < num_sacks; ) {
+	for (this_sack = 0; this_sack < num_sacks; ) {
 		/* Check if the start of the sack is covered by RCV.NXT. */
 		if (!before(tp->rcv_nxt, sp->start_seq)) {
 			int i;
@@ -3144,8 +3381,8 @@
 		__skb_unlink(skb, &tp->out_of_order_queue);
 		__skb_queue_tail(&sk->sk_receive_queue, skb);
 		tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
-		if(skb->h.th->fin)
-			tcp_fin(skb, sk, skb->h.th);
+		if (tcp_hdr(skb)->fin)
+			tcp_fin(skb, sk, tcp_hdr(skb));
 	}
 }
 
@@ -3153,7 +3390,7 @@
 
 static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
 {
-	struct tcphdr *th = skb->h.th;
+	struct tcphdr *th = tcp_hdr(skb);
 	struct tcp_sock *tp = tcp_sk(sk);
 	int eaten = -1;
 
@@ -3210,9 +3447,9 @@
 			__skb_queue_tail(&sk->sk_receive_queue, skb);
 		}
 		tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
-		if(skb->len)
-			tcp_event_data_recv(sk, tp, skb);
-		if(th->fin)
+		if (skb->len)
+			tcp_event_data_recv(sk, skb);
+		if (th->fin)
 			tcp_fin(skb, sk, th);
 
 		if (!skb_queue_empty(&tp->out_of_order_queue)) {
@@ -3228,7 +3465,7 @@
 		if (tp->rx_opt.num_sacks)
 			tcp_sack_remove(tp);
 
-		tcp_fast_path_check(sk, tp);
+		tcp_fast_path_check(sk);
 
 		if (eaten > 0)
 			__kfree_skb(skb);
@@ -3392,7 +3629,7 @@
 		 * - bloated or contains data before "start" or
 		 *   overlaps to the next one.
 		 */
-		if (!skb->h.th->syn && !skb->h.th->fin &&
+		if (!tcp_hdr(skb)->syn && !tcp_hdr(skb)->fin &&
 		    (tcp_win_from_space(skb->truesize) > skb->len ||
 		     before(TCP_SKB_CB(skb)->seq, start) ||
 		     (skb->next != tail &&
@@ -3403,7 +3640,7 @@
 		start = TCP_SKB_CB(skb)->end_seq;
 		skb = skb->next;
 	}
-	if (skb == tail || skb->h.th->syn || skb->h.th->fin)
+	if (skb == tail || tcp_hdr(skb)->syn || tcp_hdr(skb)->fin)
 		return;
 
 	while (before(start, end)) {
@@ -3419,11 +3656,14 @@
 		nskb = alloc_skb(copy+header, GFP_ATOMIC);
 		if (!nskb)
 			return;
+
+		skb_set_mac_header(nskb, skb_mac_header(skb) - skb->head);
+		skb_set_network_header(nskb, (skb_network_header(skb) -
+					      skb->head));
+		skb_set_transport_header(nskb, (skb_transport_header(skb) -
+						skb->head));
 		skb_reserve(nskb, header);
 		memcpy(nskb->head, skb->head, header);
-		nskb->nh.raw = nskb->head + (skb->nh.raw-skb->head);
-		nskb->h.raw = nskb->head + (skb->h.raw-skb->head);
-		nskb->mac.raw = nskb->head + (skb->mac.raw-skb->head);
 		memcpy(nskb->cb, skb->cb, sizeof(skb->cb));
 		TCP_SKB_CB(nskb)->seq = TCP_SKB_CB(nskb)->end_seq = start;
 		__skb_insert(nskb, skb->prev, skb, list);
@@ -3449,7 +3689,9 @@
 				__kfree_skb(skb);
 				NET_INC_STATS_BH(LINUX_MIB_TCPRCVCOLLAPSED);
 				skb = next;
-				if (skb == tail || skb->h.th->syn || skb->h.th->fin)
+				if (skb == tail ||
+				    tcp_hdr(skb)->syn ||
+				    tcp_hdr(skb)->fin)
 					return;
 			}
 		}
@@ -3514,7 +3756,7 @@
 	NET_INC_STATS_BH(LINUX_MIB_PRUNECALLED);
 
 	if (atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf)
-		tcp_clamp_window(sk, tp);
+		tcp_clamp_window(sk);
 	else if (tcp_memory_pressure)
 		tp->rcv_ssthresh = min(tp->rcv_ssthresh, 4U * tp->advmss);
 
@@ -3583,8 +3825,10 @@
 	tp->snd_cwnd_stamp = tcp_time_stamp;
 }
 
-static int tcp_should_expand_sndbuf(struct sock *sk, struct tcp_sock *tp)
+static int tcp_should_expand_sndbuf(struct sock *sk)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
+
 	/* If the user specified a specific send buffer setting, do
 	 * not modify it.
 	 */
@@ -3616,7 +3860,7 @@
 {
 	struct tcp_sock *tp = tcp_sk(sk);
 
-	if (tcp_should_expand_sndbuf(sk, tp)) {
+	if (tcp_should_expand_sndbuf(sk)) {
 		int sndmem = max_t(u32, tp->rx_opt.mss_clamp, tp->mss_cache) +
 			MAX_TCP_HEADER + 16 + sizeof(struct sk_buff),
 		    demanded = max_t(unsigned int, tp->snd_cwnd,
@@ -3640,9 +3884,9 @@
 	}
 }
 
-static inline void tcp_data_snd_check(struct sock *sk, struct tcp_sock *tp)
+static inline void tcp_data_snd_check(struct sock *sk)
 {
-	tcp_push_pending_frames(sk, tp);
+	tcp_push_pending_frames(sk);
 	tcp_check_space(sk);
 }
 
@@ -3790,7 +4034,7 @@
 	int err;
 
 	local_bh_enable();
-	if (skb->ip_summed==CHECKSUM_UNNECESSARY)
+	if (skb_csum_unnecessary(skb))
 		err = skb_copy_datagram_iovec(skb, hlen, tp->ucopy.iov, chunk);
 	else
 		err = skb_copy_and_csum_datagram_iovec(skb, hlen,
@@ -3822,7 +4066,7 @@
 
 static inline int tcp_checksum_complete_user(struct sock *sk, struct sk_buff *skb)
 {
-	return skb->ip_summed != CHECKSUM_UNNECESSARY &&
+	return !skb_csum_unnecessary(skb) &&
 		__tcp_checksum_complete_user(sk, skb);
 }
 
@@ -3840,7 +4084,7 @@
 	if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list)
 		tp->ucopy.dma_chan = get_softnet_dma();
 
-	if (tp->ucopy.dma_chan && skb->ip_summed == CHECKSUM_UNNECESSARY) {
+	if (tp->ucopy.dma_chan && skb_csum_unnecessary(skb)) {
 
 		dma_cookie = dma_skb_copy_datagram_iovec(tp->ucopy.dma_chan,
 			skb, hlen, tp->ucopy.iov, chunk, tp->ucopy.pinned_list);
@@ -3856,7 +4100,7 @@
 		tcp_rcv_space_adjust(sk);
 
 		if ((tp->ucopy.len == 0) ||
-		    (tcp_flag_word(skb->h.th) & TCP_FLAG_PSH) ||
+		    (tcp_flag_word(tcp_hdr(skb)) & TCP_FLAG_PSH) ||
 		    (atomic_read(&sk->sk_rmem_alloc) > (sk->sk_rcvbuf >> 1))) {
 			tp->ucopy.wakeup = 1;
 			sk->sk_data_ready(sk, 0);
@@ -3976,7 +4220,7 @@
 				 */
 				tcp_ack(sk, skb, 0);
 				__kfree_skb(skb);
-				tcp_data_snd_check(sk, tp);
+				tcp_data_snd_check(sk);
 				return 0;
 			} else { /* Header too small */
 				TCP_INC_STATS_BH(TCP_MIB_INERRS);
@@ -4047,12 +4291,12 @@
 				tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
 			}
 
-			tcp_event_data_recv(sk, tp, skb);
+			tcp_event_data_recv(sk, skb);
 
 			if (TCP_SKB_CB(skb)->ack_seq != tp->snd_una) {
 				/* Well, only one small jumplet in fast path... */
 				tcp_ack(sk, skb, FLAG_DATA);
-				tcp_data_snd_check(sk, tp);
+				tcp_data_snd_check(sk);
 				if (!inet_csk_ack_scheduled(sk))
 					goto no_ack;
 			}
@@ -4109,7 +4353,7 @@
 		goto discard;
 	}
 
-	if(th->rst) {
+	if (th->rst) {
 		tcp_reset(sk);
 		goto discard;
 	}
@@ -4124,7 +4368,7 @@
 	}
 
 step5:
-	if(th->ack)
+	if (th->ack)
 		tcp_ack(sk, skb, FLAG_SLOWPATH);
 
 	tcp_rcv_rtt_measure_ts(sk, skb);
@@ -4135,7 +4379,7 @@
 	/* step 7: process the segment text */
 	tcp_data_queue(sk, skb);
 
-	tcp_data_snd_check(sk, tp);
+	tcp_data_snd_check(sk);
 	tcp_ack_snd_check(sk);
 	return 0;
 
@@ -4412,13 +4656,13 @@
 		goto discard;
 
 	case TCP_LISTEN:
-		if(th->ack)
+		if (th->ack)
 			return 1;
 
-		if(th->rst)
+		if (th->rst)
 			goto discard;
 
-		if(th->syn) {
+		if (th->syn) {
 			if (icsk->icsk_af_ops->conn_request(sk, skb) < 0)
 				return 1;
 
@@ -4452,7 +4696,7 @@
 		/* Do step6 onward by hand. */
 		tcp_urg(sk, skb, th);
 		__kfree_skb(skb);
-		tcp_data_snd_check(sk, tp);
+		tcp_data_snd_check(sk);
 		return 0;
 	}
 
@@ -4474,7 +4718,7 @@
 	}
 
 	/* step 2: check RST bit */
-	if(th->rst) {
+	if (th->rst) {
 		tcp_reset(sk);
 		goto discard;
 	}
@@ -4497,7 +4741,7 @@
 	if (th->ack) {
 		int acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH);
 
-		switch(sk->sk_state) {
+		switch (sk->sk_state) {
 		case TCP_SYN_RECV:
 			if (acceptable) {
 				tp->copied_seq = tp->rcv_nxt;
@@ -4644,7 +4888,7 @@
 
 	/* tcp_data could move socket to TIME-WAIT */
 	if (sk->sk_state != TCP_CLOSE) {
-		tcp_data_snd_check(sk, tp);
+		tcp_data_snd_check(sk);
 		tcp_ack_snd_check(sk);
 	}
 
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 0ba74bb..5a3e7f8 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -88,7 +88,7 @@
 #define ICMP_MIN_LENGTH 8
 
 /* Socket used for sending RSTs */
-static struct socket *tcp_socket;
+static struct socket *tcp_socket __read_mostly;
 
 void tcp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb);
 
@@ -125,10 +125,10 @@
 
 static inline __u32 tcp_v4_init_sequence(struct sk_buff *skb)
 {
-	return secure_tcp_sequence_number(skb->nh.iph->daddr,
-					  skb->nh.iph->saddr,
-					  skb->h.th->dest,
-					  skb->h.th->source);
+	return secure_tcp_sequence_number(ip_hdr(skb)->daddr,
+					  ip_hdr(skb)->saddr,
+					  tcp_hdr(skb)->dest,
+					  tcp_hdr(skb)->source);
 }
 
 int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
@@ -149,7 +149,7 @@
 	 */
 	if (tcptw->tw_ts_recent_stamp &&
 	    (twp == NULL || (sysctl_tcp_tw_reuse &&
-			     xtime.tv_sec - tcptw->tw_ts_recent_stamp > 1))) {
+			     get_seconds() - tcptw->tw_ts_recent_stamp > 1))) {
 		tp->write_seq = tcptw->tw_snd_nxt + 65535 + 2;
 		if (tp->write_seq == 0)
 			tp->write_seq = 1;
@@ -224,7 +224,7 @@
 		 * when trying new connection.
 		 */
 		if (peer != NULL &&
-		    peer->tcp_ts_stamp + TCP_PAWS_MSL >= xtime.tv_sec) {
+		    peer->tcp_ts_stamp + TCP_PAWS_MSL >= get_seconds()) {
 			tp->rx_opt.ts_recent_stamp = peer->tcp_ts_stamp;
 			tp->rx_opt.ts_recent = peer->tcp_ts;
 		}
@@ -354,8 +354,8 @@
 	struct tcphdr *th = (struct tcphdr *)(skb->data + (iph->ihl << 2));
 	struct tcp_sock *tp;
 	struct inet_sock *inet;
-	int type = skb->h.icmph->type;
-	int code = skb->h.icmph->code;
+	const int type = icmp_hdr(skb)->type;
+	const int code = icmp_hdr(skb)->code;
 	struct sock *sk;
 	__u32 seq;
 	int err;
@@ -499,11 +499,12 @@
 void tcp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb)
 {
 	struct inet_sock *inet = inet_sk(sk);
-	struct tcphdr *th = skb->h.th;
+	struct tcphdr *th = tcp_hdr(skb);
 
 	if (skb->ip_summed == CHECKSUM_PARTIAL) {
 		th->check = ~tcp_v4_check(len, inet->saddr,
 					  inet->daddr, 0);
+		skb->csum_start = skb_transport_header(skb) - skb->head;
 		skb->csum_offset = offsetof(struct tcphdr, check);
 	} else {
 		th->check = tcp_v4_check(len, inet->saddr, inet->daddr,
@@ -515,17 +516,18 @@
 
 int tcp_v4_gso_send_check(struct sk_buff *skb)
 {
-	struct iphdr *iph;
+	const struct iphdr *iph;
 	struct tcphdr *th;
 
 	if (!pskb_may_pull(skb, sizeof(*th)))
 		return -EINVAL;
 
-	iph = skb->nh.iph;
-	th = skb->h.th;
+	iph = ip_hdr(skb);
+	th = tcp_hdr(skb);
 
 	th->check = 0;
 	th->check = ~tcp_v4_check(skb->len, iph->saddr, iph->daddr, 0);
+	skb->csum_start = skb_transport_header(skb) - skb->head;
 	skb->csum_offset = offsetof(struct tcphdr, check);
 	skb->ip_summed = CHECKSUM_PARTIAL;
 	return 0;
@@ -546,7 +548,7 @@
 
 static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
 {
-	struct tcphdr *th = skb->h.th;
+	struct tcphdr *th = tcp_hdr(skb);
 	struct {
 		struct tcphdr th;
 #ifdef CONFIG_TCP_MD5SIG
@@ -585,7 +587,7 @@
 	arg.iov[0].iov_len  = sizeof(rep.th);
 
 #ifdef CONFIG_TCP_MD5SIG
-	key = sk ? tcp_v4_md5_do_lookup(sk, skb->nh.iph->daddr) : NULL;
+	key = sk ? tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->daddr) : NULL;
 	if (key) {
 		rep.opt[0] = htonl((TCPOPT_NOP << 24) |
 				   (TCPOPT_NOP << 16) |
@@ -597,14 +599,14 @@
 
 		tcp_v4_do_calc_md5_hash((__u8 *)&rep.opt[1],
 					key,
-					skb->nh.iph->daddr,
-					skb->nh.iph->saddr,
+					ip_hdr(skb)->daddr,
+					ip_hdr(skb)->saddr,
 					&rep.th, IPPROTO_TCP,
 					arg.iov[0].iov_len);
 	}
 #endif
-	arg.csum = csum_tcpudp_nofold(skb->nh.iph->daddr,
-				      skb->nh.iph->saddr, /* XXX */
+	arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,
+				      ip_hdr(skb)->saddr, /* XXX */
 				      sizeof(struct tcphdr), IPPROTO_TCP, 0);
 	arg.csumoffset = offsetof(struct tcphdr, check) / 2;
 
@@ -622,7 +624,7 @@
 			    struct sk_buff *skb, u32 seq, u32 ack,
 			    u32 win, u32 ts)
 {
-	struct tcphdr *th = skb->h.th;
+	struct tcphdr *th = tcp_hdr(skb);
 	struct {
 		struct tcphdr th;
 		__be32 opt[(TCPOLEN_TSTAMP_ALIGNED >> 2)
@@ -670,7 +672,7 @@
 	 * skb->sk) holds true, but we program defensively.
 	 */
 	if (!twsk && skb->sk) {
-		key = tcp_v4_md5_do_lookup(skb->sk, skb->nh.iph->daddr);
+		key = tcp_v4_md5_do_lookup(skb->sk, ip_hdr(skb)->daddr);
 	} else if (twsk && twsk->tw_md5_keylen) {
 		tw_key.key = twsk->tw_md5_key;
 		tw_key.keylen = twsk->tw_md5_keylen;
@@ -690,14 +692,14 @@
 
 		tcp_v4_do_calc_md5_hash((__u8 *)&rep.opt[offset],
 					key,
-					skb->nh.iph->daddr,
-					skb->nh.iph->saddr,
+					ip_hdr(skb)->daddr,
+					ip_hdr(skb)->saddr,
 					&rep.th, IPPROTO_TCP,
 					arg.iov[0].iov_len);
 	}
 #endif
-	arg.csum = csum_tcpudp_nofold(skb->nh.iph->daddr,
-				      skb->nh.iph->saddr, /* XXX */
+	arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,
+				      ip_hdr(skb)->saddr, /* XXX */
 				      arg.iov[0].iov_len, IPPROTO_TCP, 0);
 	arg.csumoffset = offsetof(struct tcphdr, check) / 2;
 
@@ -745,7 +747,7 @@
 	skb = tcp_make_synack(sk, dst, req);
 
 	if (skb) {
-		struct tcphdr *th = skb->h.th;
+		struct tcphdr *th = tcp_hdr(skb);
 
 		th->check = tcp_v4_check(skb->len,
 					 ireq->loc_addr,
@@ -781,7 +783,7 @@
 		warntime = jiffies;
 		printk(KERN_INFO
 		       "possible SYN flooding on port %d. Sending cookies.\n",
-		       ntohs(skb->h.th->dest));
+		       ntohs(tcp_hdr(skb)->dest));
 	}
 }
 #endif
@@ -1133,8 +1135,8 @@
 	 */
 	__u8 *hash_location = NULL;
 	struct tcp_md5sig_key *hash_expected;
-	struct iphdr *iph = skb->nh.iph;
-	struct tcphdr *th = skb->h.th;
+	const struct iphdr *iph = ip_hdr(skb);
+	struct tcphdr *th = tcp_hdr(skb);
 	int length = (th->doff << 2) - sizeof(struct tcphdr);
 	int genhash;
 	unsigned char *ptr;
@@ -1251,8 +1253,8 @@
 	struct inet_request_sock *ireq;
 	struct tcp_options_received tmp_opt;
 	struct request_sock *req;
-	__be32 saddr = skb->nh.iph->saddr;
-	__be32 daddr = skb->nh.iph->daddr;
+	__be32 saddr = ip_hdr(skb)->saddr;
+	__be32 daddr = ip_hdr(skb)->daddr;
 	__u32 isn = TCP_SKB_CB(skb)->when;
 	struct dst_entry *dst = NULL;
 #ifdef CONFIG_SYN_COOKIES
@@ -1327,7 +1329,7 @@
 	ireq->rmt_addr = saddr;
 	ireq->opt = tcp_v4_save_options(sk, skb);
 	if (!want_cookie)
-		TCP_ECN_create_request(req, skb->h.th);
+		TCP_ECN_create_request(req, tcp_hdr(skb));
 
 	if (want_cookie) {
 #ifdef CONFIG_SYN_COOKIES
@@ -1351,7 +1353,7 @@
 		    (dst = inet_csk_route_req(sk, req)) != NULL &&
 		    (peer = rt_get_peer((struct rtable *)dst)) != NULL &&
 		    peer->v4daddr == saddr) {
-			if (xtime.tv_sec < peer->tcp_ts_stamp + TCP_PAWS_MSL &&
+			if (get_seconds() < peer->tcp_ts_stamp + TCP_PAWS_MSL &&
 			    (s32)(peer->tcp_ts - req->ts_recent) >
 							TCP_PAWS_WINDOW) {
 				NET_INC_STATS_BH(LINUX_MIB_PAWSPASSIVEREJECTED);
@@ -1375,7 +1377,7 @@
 			LIMIT_NETDEBUG(KERN_DEBUG "TCP: drop open "
 				       "request from %u.%u.%u.%u/%u\n",
 				       NIPQUAD(saddr),
-				       ntohs(skb->h.th->source));
+				       ntohs(tcp_hdr(skb)->source));
 			dst_release(dst);
 			goto drop_and_free;
 		}
@@ -1439,7 +1441,7 @@
 	newinet->opt	      = ireq->opt;
 	ireq->opt	      = NULL;
 	newinet->mc_index     = inet_iif(skb);
-	newinet->mc_ttl	      = skb->nh.iph->ttl;
+	newinet->mc_ttl	      = ip_hdr(skb)->ttl;
 	inet_csk(newsk)->icsk_ext_hdr_len = 0;
 	if (newinet->opt)
 		inet_csk(newsk)->icsk_ext_hdr_len = newinet->opt->optlen;
@@ -1481,8 +1483,8 @@
 
 static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
 {
-	struct tcphdr *th = skb->h.th;
-	struct iphdr *iph = skb->nh.iph;
+	struct tcphdr *th = tcp_hdr(skb);
+	const struct iphdr *iph = ip_hdr(skb);
 	struct sock *nsk;
 	struct request_sock **prev;
 	/* Find possible connection requests. */
@@ -1491,9 +1493,8 @@
 	if (req)
 		return tcp_check_req(sk, skb, req, prev);
 
-	nsk = inet_lookup_established(&tcp_hashinfo, skb->nh.iph->saddr,
-				      th->source, skb->nh.iph->daddr,
-				      th->dest, inet_iif(skb));
+	nsk = inet_lookup_established(&tcp_hashinfo, iph->saddr, th->source,
+				      iph->daddr, th->dest, inet_iif(skb));
 
 	if (nsk) {
 		if (nsk->sk_state != TCP_TIME_WAIT) {
@@ -1513,15 +1514,17 @@
 
 static __sum16 tcp_v4_checksum_init(struct sk_buff *skb)
 {
+	const struct iphdr *iph = ip_hdr(skb);
+
 	if (skb->ip_summed == CHECKSUM_COMPLETE) {
-		if (!tcp_v4_check(skb->len, skb->nh.iph->saddr,
-				  skb->nh.iph->daddr, skb->csum)) {
+		if (!tcp_v4_check(skb->len, iph->saddr,
+				  iph->daddr, skb->csum)) {
 			skb->ip_summed = CHECKSUM_UNNECESSARY;
 			return 0;
 		}
 	}
 
-	skb->csum = csum_tcpudp_nofold(skb->nh.iph->saddr, skb->nh.iph->daddr,
+	skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
 				       skb->len, IPPROTO_TCP, 0);
 
 	if (skb->len <= 76) {
@@ -1555,7 +1558,7 @@
 
 	if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
 		TCP_CHECK_TIMER(sk);
-		if (tcp_rcv_established(sk, skb, skb->h.th, skb->len)) {
+		if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) {
 			rsk = sk;
 			goto reset;
 		}
@@ -1563,7 +1566,7 @@
 		return 0;
 	}
 
-	if (skb->len < (skb->h.th->doff << 2) || tcp_checksum_complete(skb))
+	if (skb->len < tcp_hdrlen(skb) || tcp_checksum_complete(skb))
 		goto csum_err;
 
 	if (sk->sk_state == TCP_LISTEN) {
@@ -1581,7 +1584,7 @@
 	}
 
 	TCP_CHECK_TIMER(sk);
-	if (tcp_rcv_state_process(sk, skb, skb->h.th, skb->len)) {
+	if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) {
 		rsk = sk;
 		goto reset;
 	}
@@ -1610,6 +1613,7 @@
 
 int tcp_v4_rcv(struct sk_buff *skb)
 {
+	const struct iphdr *iph;
 	struct tcphdr *th;
 	struct sock *sk;
 	int ret;
@@ -1623,7 +1627,7 @@
 	if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
 		goto discard_it;
 
-	th = skb->h.th;
+	th = tcp_hdr(skb);
 
 	if (th->doff < sizeof(struct tcphdr) / 4)
 		goto bad_packet;
@@ -1634,23 +1638,21 @@
 	 * Packet length and doff are validated by header prediction,
 	 * provided case of th->doff==0 is eliminated.
 	 * So, we defer the checks. */
-	if ((skb->ip_summed != CHECKSUM_UNNECESSARY &&
-	     tcp_v4_checksum_init(skb)))
+	if (!skb_csum_unnecessary(skb) && tcp_v4_checksum_init(skb))
 		goto bad_packet;
 
-	th = skb->h.th;
+	th = tcp_hdr(skb);
+	iph = ip_hdr(skb);
 	TCP_SKB_CB(skb)->seq = ntohl(th->seq);
 	TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
 				    skb->len - th->doff * 4);
 	TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
 	TCP_SKB_CB(skb)->when	 = 0;
-	TCP_SKB_CB(skb)->flags	 = skb->nh.iph->tos;
+	TCP_SKB_CB(skb)->flags	 = iph->tos;
 	TCP_SKB_CB(skb)->sacked	 = 0;
 
-	sk = __inet_lookup(&tcp_hashinfo, skb->nh.iph->saddr, th->source,
-			   skb->nh.iph->daddr, th->dest,
-			   inet_iif(skb));
-
+	sk = __inet_lookup(&tcp_hashinfo, iph->saddr, th->source,
+			   iph->daddr, th->dest, inet_iif(skb));
 	if (!sk)
 		goto no_tcp_socket;
 
@@ -1724,8 +1726,7 @@
 	switch (tcp_timewait_state_process(inet_twsk(sk), skb, th)) {
 	case TCP_TW_SYN: {
 		struct sock *sk2 = inet_lookup_listener(&tcp_hashinfo,
-							skb->nh.iph->daddr,
-							th->dest,
+							iph->daddr, th->dest,
 							inet_iif(skb));
 		if (sk2) {
 			inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row);
@@ -1770,7 +1771,7 @@
 
 	if (peer) {
 		if ((s32)(peer->tcp_ts - tp->rx_opt.ts_recent) <= 0 ||
-		    (peer->tcp_ts_stamp + TCP_PAWS_MSL < xtime.tv_sec &&
+		    (peer->tcp_ts_stamp + TCP_PAWS_MSL < get_seconds() &&
 		     peer->tcp_ts_stamp <= tp->rx_opt.ts_recent_stamp)) {
 			peer->tcp_ts_stamp = tp->rx_opt.ts_recent_stamp;
 			peer->tcp_ts = tp->rx_opt.ts_recent;
@@ -1791,7 +1792,7 @@
 		const struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
 
 		if ((s32)(peer->tcp_ts - tcptw->tw_ts_recent) <= 0 ||
-		    (peer->tcp_ts_stamp + TCP_PAWS_MSL < xtime.tv_sec &&
+		    (peer->tcp_ts_stamp + TCP_PAWS_MSL < get_seconds() &&
 		     peer->tcp_ts_stamp <= tcptw->tw_ts_recent_stamp)) {
 			peer->tcp_ts_stamp = tcptw->tw_ts_recent_stamp;
 			peer->tcp_ts	   = tcptw->tw_ts_recent;
@@ -1890,7 +1891,7 @@
 	tcp_cleanup_congestion_control(sk);
 
 	/* Cleanup up the write buffer. */
-	sk_stream_writequeue_purge(sk);
+	tcp_write_queue_purge(sk);
 
 	/* Cleans up our, hopefully empty, out_of_order_queue. */
 	__skb_queue_purge(&tp->out_of_order_queue);
@@ -2293,13 +2294,13 @@
 		req);
 }
 
-static void get_tcp4_sock(struct sock *sp, char *tmpbuf, int i)
+static void get_tcp4_sock(struct sock *sk, char *tmpbuf, int i)
 {
 	int timer_active;
 	unsigned long timer_expires;
-	struct tcp_sock *tp = tcp_sk(sp);
-	const struct inet_connection_sock *icsk = inet_csk(sp);
-	struct inet_sock *inet = inet_sk(sp);
+	struct tcp_sock *tp = tcp_sk(sk);
+	const struct inet_connection_sock *icsk = inet_csk(sk);
+	struct inet_sock *inet = inet_sk(sk);
 	__be32 dest = inet->daddr;
 	__be32 src = inet->rcv_saddr;
 	__u16 destp = ntohs(inet->dport);
@@ -2311,9 +2312,9 @@
 	} else if (icsk->icsk_pending == ICSK_TIME_PROBE0) {
 		timer_active	= 4;
 		timer_expires	= icsk->icsk_timeout;
-	} else if (timer_pending(&sp->sk_timer)) {
+	} else if (timer_pending(&sk->sk_timer)) {
 		timer_active	= 2;
-		timer_expires	= sp->sk_timer.expires;
+		timer_expires	= sk->sk_timer.expires;
 	} else {
 		timer_active	= 0;
 		timer_expires = jiffies;
@@ -2321,17 +2322,17 @@
 
 	sprintf(tmpbuf, "%4d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08lX "
 			"%08X %5d %8d %lu %d %p %u %u %u %u %d",
-		i, src, srcp, dest, destp, sp->sk_state,
+		i, src, srcp, dest, destp, sk->sk_state,
 		tp->write_seq - tp->snd_una,
-		sp->sk_state == TCP_LISTEN ? sp->sk_ack_backlog :
+		sk->sk_state == TCP_LISTEN ? sk->sk_ack_backlog :
 					     (tp->rcv_nxt - tp->copied_seq),
 		timer_active,
 		jiffies_to_clock_t(timer_expires - jiffies),
 		icsk->icsk_retransmits,
-		sock_i_uid(sp),
+		sock_i_uid(sk),
 		icsk->icsk_probes_out,
-		sock_i_ino(sp),
-		atomic_read(&sp->sk_refcnt), sp,
+		sock_i_ino(sk),
+		atomic_read(&sk->sk_refcnt), sk,
 		icsk->icsk_rto,
 		icsk->icsk_ack.ato,
 		(icsk->icsk_ack.quick << 1) | icsk->icsk_ack.pingpong,
diff --git a/net/ipv4/tcp_lp.c b/net/ipv4/tcp_lp.c
index f0ebaf0..43294ad 100644
--- a/net/ipv4/tcp_lp.c
+++ b/net/ipv4/tcp_lp.c
@@ -218,7 +218,7 @@
  *   3. calc smoothed OWD (SOWD).
  * Most ideas come from the original TCP-LP implementation.
  */
-static void tcp_lp_rtt_sample(struct sock *sk, u32 usrtt)
+static void tcp_lp_rtt_sample(struct sock *sk, u32 rtt)
 {
 	struct lp *lp = inet_csk_ca(sk);
 	s64 mowd = tcp_lp_owd_calculator(sk);
@@ -261,11 +261,13 @@
  * newReno in increase case.
  * We work it out by following the idea from TCP-LP's paper directly
  */
-static void tcp_lp_pkts_acked(struct sock *sk, u32 num_acked)
+static void tcp_lp_pkts_acked(struct sock *sk, u32 num_acked, ktime_t last)
 {
 	struct tcp_sock *tp = tcp_sk(sk);
 	struct lp *lp = inet_csk_ca(sk);
 
+	tcp_lp_rtt_sample(sk,  ktime_to_us(net_timedelta(last)));
+
 	/* calc inference */
 	if (tcp_time_stamp > tp->rx_opt.rcv_tsecr)
 		lp->inference = 3 * (tcp_time_stamp - tp->rx_opt.rcv_tsecr);
@@ -312,11 +314,11 @@
 }
 
 static struct tcp_congestion_ops tcp_lp = {
+	.flags = TCP_CONG_RTT_STAMP,
 	.init = tcp_lp_init,
 	.ssthresh = tcp_reno_ssthresh,
 	.cong_avoid = tcp_lp_cong_avoid,
 	.min_cwnd = tcp_reno_min_cwnd,
-	.rtt_sample = tcp_lp_rtt_sample,
 	.pkts_acked = tcp_lp_pkts_acked,
 
 	.owner = THIS_MODULE,
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 6b5c64f..a12b08f 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -149,7 +149,7 @@
 		tw->tw_substate	  = TCP_TIME_WAIT;
 		tcptw->tw_rcv_nxt = TCP_SKB_CB(skb)->end_seq;
 		if (tmp_opt.saw_tstamp) {
-			tcptw->tw_ts_recent_stamp = xtime.tv_sec;
+			tcptw->tw_ts_recent_stamp = get_seconds();
 			tcptw->tw_ts_recent	  = tmp_opt.rcv_tsval;
 		}
 
@@ -208,7 +208,7 @@
 
 		if (tmp_opt.saw_tstamp) {
 			tcptw->tw_ts_recent	  = tmp_opt.rcv_tsval;
-			tcptw->tw_ts_recent_stamp = xtime.tv_sec;
+			tcptw->tw_ts_recent_stamp = get_seconds();
 		}
 
 		inet_twsk_put(tw);
@@ -246,7 +246,7 @@
 	if (paws_reject)
 		NET_INC_STATS_BH(LINUX_MIB_PAWSESTABREJECTED);
 
-	if(!th->rst) {
+	if (!th->rst) {
 		/* In this case we must reset the TIMEWAIT timer.
 		 *
 		 * If it is ACKless SYN it may be both old duplicate
@@ -324,7 +324,7 @@
 				if (tcp_alloc_md5sig_pool() == NULL)
 					BUG();
 			}
-		} while(0);
+		} while (0);
 #endif
 
 		/* Linkage updates. */
@@ -387,8 +387,8 @@
 		/* Now setup tcp_sock */
 		newtp = tcp_sk(newsk);
 		newtp->pred_flags = 0;
-		newtp->rcv_nxt = treq->rcv_isn + 1;
-		newtp->snd_nxt = newtp->snd_una = newtp->snd_sml = treq->snt_isn + 1;
+		newtp->rcv_wup = newtp->copied_seq = newtp->rcv_nxt = treq->rcv_isn + 1;
+		newtp->snd_sml = newtp->snd_una = newtp->snd_nxt = treq->snt_isn + 1;
 
 		tcp_prequeue_init(newtp);
 
@@ -422,10 +422,8 @@
 		tcp_set_ca_state(newsk, TCP_CA_Open);
 		tcp_init_xmit_timers(newsk);
 		skb_queue_head_init(&newtp->out_of_order_queue);
-		newtp->rcv_wup = treq->rcv_isn + 1;
 		newtp->write_seq = treq->snt_isn + 1;
 		newtp->pushed_seq = newtp->write_seq;
-		newtp->copied_seq = treq->rcv_isn + 1;
 
 		newtp->rx_opt.saw_tstamp = 0;
 
@@ -440,7 +438,7 @@
 						       keepalive_time_when(newtp));
 
 		newtp->rx_opt.tstamp_ok = ireq->tstamp_ok;
-		if((newtp->rx_opt.sack_ok = ireq->sack_ok) != 0) {
+		if ((newtp->rx_opt.sack_ok = ireq->sack_ok) != 0) {
 			if (sysctl_tcp_fack)
 				newtp->rx_opt.sack_ok |= 2;
 		}
@@ -455,12 +453,13 @@
 			newtp->rx_opt.snd_wscale = newtp->rx_opt.rcv_wscale = 0;
 			newtp->window_clamp = min(newtp->window_clamp, 65535U);
 		}
-		newtp->snd_wnd = ntohs(skb->h.th->window) << newtp->rx_opt.snd_wscale;
+		newtp->snd_wnd = (ntohs(tcp_hdr(skb)->window) <<
+				  newtp->rx_opt.snd_wscale);
 		newtp->max_window = newtp->snd_wnd;
 
 		if (newtp->rx_opt.tstamp_ok) {
 			newtp->rx_opt.ts_recent = req->ts_recent;
-			newtp->rx_opt.ts_recent_stamp = xtime.tv_sec;
+			newtp->rx_opt.ts_recent_stamp = get_seconds();
 			newtp->tcp_header_len = sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED;
 		} else {
 			newtp->rx_opt.ts_recent_stamp = 0;
@@ -490,7 +489,7 @@
 			   struct request_sock *req,
 			   struct request_sock **prev)
 {
-	struct tcphdr *th = skb->h.th;
+	const struct tcphdr *th = tcp_hdr(skb);
 	__be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK);
 	int paws_reject = 0;
 	struct tcp_options_received tmp_opt;
@@ -506,7 +505,7 @@
 			 * it can be estimated (approximately)
 			 * from another data.
 			 */
-			tmp_opt.ts_recent_stamp = xtime.tv_sec - ((TCP_TIMEOUT_INIT/HZ)<<req->retrans);
+			tmp_opt.ts_recent_stamp = get_seconds() - ((TCP_TIMEOUT_INIT/HZ)<<req->retrans);
 			paws_reject = tcp_paws_check(&tmp_opt, th->rst);
 		}
 	}
@@ -712,8 +711,8 @@
 	int state = child->sk_state;
 
 	if (!sock_owned_by_user(child)) {
-		ret = tcp_rcv_state_process(child, skb, skb->h.th, skb->len);
-
+		ret = tcp_rcv_state_process(child, skb, tcp_hdr(skb),
+					    skb->len);
 		/* Wakeup parent, send SIGIO */
 		if (state == TCP_SYN_RECV && child->sk_state != state)
 			parent->sk_data_ready(parent, 0);
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 3c24881..0faacf9 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -62,14 +62,13 @@
 /* By default, RFC2861 behavior.  */
 int sysctl_tcp_slow_start_after_idle __read_mostly = 1;
 
-static void update_send_head(struct sock *sk, struct tcp_sock *tp,
-			     struct sk_buff *skb)
+static void update_send_head(struct sock *sk, struct sk_buff *skb)
 {
-	sk->sk_send_head = skb->next;
-	if (sk->sk_send_head == (struct sk_buff *)&sk->sk_write_queue)
-		sk->sk_send_head = NULL;
+	struct tcp_sock *tp = tcp_sk(sk);
+
+	tcp_advance_send_head(sk, skb);
 	tp->snd_nxt = TCP_SKB_CB(skb)->end_seq;
-	tcp_packets_out_inc(sk, tp, skb);
+	tcp_packets_out_inc(sk, skb);
 }
 
 /* SND.NXT, if window was not shrunk.
@@ -78,8 +77,10 @@
  * Anything in between SND.UNA...SND.UNA+SND.WND also can be already
  * invalid. OK, let's make this for now:
  */
-static inline __u32 tcp_acceptable_seq(struct sock *sk, struct tcp_sock *tp)
+static inline __u32 tcp_acceptable_seq(struct sock *sk)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
+
 	if (!before(tp->snd_una+tp->snd_wnd, tp->snd_nxt))
 		return tp->snd_nxt;
 	else
@@ -238,7 +239,7 @@
 	u32 new_win = __tcp_select_window(sk);
 
 	/* Never shrink the offered window */
-	if(new_win < cur_win) {
+	if (new_win < cur_win) {
 		/* Danger Will Robinson!
 		 * Don't update rcv_wup/rcv_wnd here or else
 		 * we will not be able to advertise a zero
@@ -289,10 +290,12 @@
 			       (TCPOPT_SACK <<  8) |
 			       (TCPOLEN_SACK_BASE + (tp->rx_opt.eff_sacks *
 						     TCPOLEN_SACK_PERBLOCK)));
-		for(this_sack = 0; this_sack < tp->rx_opt.eff_sacks; this_sack++) {
+
+		for (this_sack = 0; this_sack < tp->rx_opt.eff_sacks; this_sack++) {
 			*ptr++ = htonl(sp[this_sack].start_seq);
 			*ptr++ = htonl(sp[this_sack].end_seq);
 		}
+
 		if (tp->rx_opt.dsack) {
 			tp->rx_opt.dsack = 0;
 			tp->rx_opt.eff_sacks--;
@@ -337,7 +340,7 @@
 	 */
 	*ptr++ = htonl((TCPOPT_MSS << 24) | (TCPOLEN_MSS << 16) | mss);
 	if (ts) {
-		if(sack)
+		if (sack)
 			*ptr++ = htonl((TCPOPT_SACK_PERM << 24) |
 				       (TCPOLEN_SACK_PERM << 16) |
 				       (TCPOPT_TIMESTAMP << 8) |
@@ -349,7 +352,7 @@
 				       TCPOLEN_TIMESTAMP);
 		*ptr++ = htonl(tstamp);		/* TSVAL */
 		*ptr++ = htonl(ts_recent);	/* TSECR */
-	} else if(sack)
+	} else if (sack)
 		*ptr++ = htonl((TCPOPT_NOP << 24) |
 			       (TCPOPT_NOP << 16) |
 			       (TCPOPT_SACK_PERM << 8) |
@@ -406,7 +409,7 @@
 	/* If congestion control is doing timestamping, we must
 	 * take such a timestamp before we potentially clone/copy.
 	 */
-	if (icsk->icsk_ca_ops->rtt_sample)
+	if (icsk->icsk_ca_ops->flags & TCP_CONG_RTT_STAMP)
 		__net_timestamp(skb);
 
 	if (likely(clone_it)) {
@@ -430,7 +433,7 @@
 	sysctl_flags = 0;
 	if (unlikely(tcb->flags & TCPCB_FLAG_SYN)) {
 		tcp_header_size = sizeof(struct tcphdr) + TCPOLEN_MSS;
-		if(sysctl_tcp_timestamps) {
+		if (sysctl_tcp_timestamps) {
 			tcp_header_size += TCPOLEN_TSTAMP_ALIGNED;
 			sysctl_flags |= SYSCTL_FLAG_TSTAMPS;
 		}
@@ -465,11 +468,12 @@
 		tcp_header_size += TCPOLEN_MD5SIG_ALIGNED;
 #endif
 
-	th = (struct tcphdr *) skb_push(skb, tcp_header_size);
-	skb->h.th = th;
+	skb_push(skb, tcp_header_size);
+	skb_reset_transport_header(skb);
 	skb_set_owner_w(skb, sk);
 
 	/* Build TCP header and checksum it. */
+	th = tcp_hdr(skb);
 	th->source		= inet->sport;
 	th->dest		= inet->dport;
 	th->seq			= htonl(tcb->seq);
@@ -515,7 +519,7 @@
 					     md5 ? &md5_hash_location :
 #endif
 					     NULL);
-		TCP_ECN_send(sk, tp, skb, tcp_header_size);
+		TCP_ECN_send(sk, skb, tcp_header_size);
 	}
 
 #ifdef CONFIG_TCP_MD5SIG
@@ -524,7 +528,7 @@
 		tp->af_specific->calc_md5_hash(md5_hash_location,
 					       md5,
 					       sk, NULL, NULL,
-					       skb->h.th,
+					       tcp_hdr(skb),
 					       sk->sk_protocol,
 					       skb->len);
 	}
@@ -545,7 +549,7 @@
 	if (likely(err <= 0))
 		return err;
 
-	tcp_enter_cwr(sk);
+	tcp_enter_cwr(sk, 1);
 
 	return net_xmit_eval(err);
 
@@ -567,12 +571,8 @@
 	/* Advance write_seq and place onto the write_queue. */
 	tp->write_seq = TCP_SKB_CB(skb)->end_seq;
 	skb_header_release(skb);
-	__skb_queue_tail(&sk->sk_write_queue, skb);
+	tcp_add_write_queue_tail(sk, skb);
 	sk_charge_skb(sk, skb);
-
-	/* Queue it, remembering where we must start sending. */
-	if (sk->sk_send_head == NULL)
-		sk->sk_send_head = skb;
 }
 
 static void tcp_set_skb_tso_segs(struct sock *sk, struct sk_buff *skb, unsigned int mss_now)
@@ -705,7 +705,7 @@
 
 	/* Link BUFF into the send queue. */
 	skb_header_release(buff);
-	__skb_append(skb, buff, &sk->sk_write_queue);
+	tcp_insert_write_queue_after(skb, buff, sk);
 
 	return 0;
 }
@@ -736,7 +736,7 @@
 	}
 	skb_shinfo(skb)->nr_frags = k;
 
-	skb->tail = skb->data;
+	skb_reset_tail_pointer(skb);
 	skb->data_len -= len;
 	skb->len = skb->data_len;
 }
@@ -930,8 +930,9 @@
 
 /* Congestion window validation. (RFC2861) */
 
-static void tcp_cwnd_validate(struct sock *sk, struct tcp_sock *tp)
+static void tcp_cwnd_validate(struct sock *sk)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
 	__u32 packets_out = tp->packets_out;
 
 	if (packets_out >= tp->snd_cwnd) {
@@ -1034,8 +1035,10 @@
 	if (nonagle & TCP_NAGLE_PUSH)
 		return 1;
 
-	/* Don't use the nagle rule for urgent data (or for the final FIN).  */
-	if (tp->urg_mode ||
+	/* Don't use the nagle rule for urgent data (or for the final FIN).
+	 * Nagle can be ignored during F-RTO too (see RFC4138).
+	 */
+	if (tp->urg_mode || (tp->frto_counter == 2) ||
 	    (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN))
 		return 1;
 
@@ -1056,7 +1059,7 @@
 	return !after(end_seq, tp->snd_una + tp->snd_wnd);
 }
 
-/* This checks if the data bearing packet SKB (usually sk->sk_send_head)
+/* This checks if the data bearing packet SKB (usually tcp_send_head(sk))
  * should be put on the wire right now.  If so, it returns the number of
  * packets allowed by the congestion window.
  */
@@ -1079,15 +1082,10 @@
 	return cwnd_quota;
 }
 
-static inline int tcp_skb_is_last(const struct sock *sk,
-				  const struct sk_buff *skb)
+int tcp_may_send_now(struct sock *sk)
 {
-	return skb->next == (struct sk_buff *)&sk->sk_write_queue;
-}
-
-int tcp_may_send_now(struct sock *sk, struct tcp_sock *tp)
-{
-	struct sk_buff *skb = sk->sk_send_head;
+	struct tcp_sock *tp = tcp_sk(sk);
+	struct sk_buff *skb = tcp_send_head(sk);
 
 	return (skb &&
 		tcp_snd_test(sk, skb, tcp_current_mss(sk, 1),
@@ -1143,7 +1141,7 @@
 
 	/* Link BUFF into the send queue. */
 	skb_header_release(buff);
-	__skb_append(skb, buff, &sk->sk_write_queue);
+	tcp_insert_write_queue_after(skb, buff, sk);
 
 	return 0;
 }
@@ -1153,8 +1151,9 @@
  *
  * This algorithm is from John Heffner.
  */
-static int tcp_tso_should_defer(struct sock *sk, struct tcp_sock *tp, struct sk_buff *skb)
+static int tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
 	const struct inet_connection_sock *icsk = inet_csk(sk);
 	u32 send_win, cong_win, limit, in_flight;
 
@@ -1249,10 +1248,10 @@
 
 	/* Have enough data in the send queue to probe? */
 	len = 0;
-	if ((skb = sk->sk_send_head) == NULL)
+	if ((skb = tcp_send_head(sk)) == NULL)
 		return -1;
 	while ((len += skb->len) < probe_size && !tcp_skb_is_last(sk, skb))
-		skb = skb->next;
+		skb = tcp_write_queue_next(sk, skb);
 	if (len < probe_size)
 		return -1;
 
@@ -1279,9 +1278,9 @@
 		return -1;
 	sk_charge_skb(sk, nskb);
 
-	skb = sk->sk_send_head;
-	__skb_insert(nskb, skb->prev, skb, &sk->sk_write_queue);
-	sk->sk_send_head = nskb;
+	skb = tcp_send_head(sk);
+	tcp_insert_write_queue_before(nskb, skb, sk);
+	tcp_advance_send_head(sk, skb);
 
 	TCP_SKB_CB(nskb)->seq = TCP_SKB_CB(skb)->seq;
 	TCP_SKB_CB(nskb)->end_seq = TCP_SKB_CB(skb)->seq + probe_size;
@@ -1292,7 +1291,7 @@
 
 	len = 0;
 	while (len < probe_size) {
-		next = skb->next;
+		next = tcp_write_queue_next(sk, skb);
 
 		copy = min_t(int, skb->len, probe_size - len);
 		if (nskb->ip_summed)
@@ -1305,7 +1304,7 @@
 			/* We've eaten all the data from this skb.
 			 * Throw it away. */
 			TCP_SKB_CB(nskb)->flags |= TCP_SKB_CB(skb)->flags;
-			__skb_unlink(skb, &sk->sk_write_queue);
+			tcp_unlink_write_queue(skb, sk);
 			sk_stream_free_skb(sk, skb);
 		} else {
 			TCP_SKB_CB(nskb)->flags |= TCP_SKB_CB(skb)->flags &
@@ -1333,7 +1332,7 @@
 		/* Decrement cwnd here because we are sending
 		* effectively two packets. */
 		tp->snd_cwnd--;
-		update_send_head(sk, tp, nskb);
+		update_send_head(sk, nskb);
 
 		icsk->icsk_mtup.probe_size = tcp_mss_to_mtu(sk, nskb->len);
 		tp->mtu_probe.probe_seq_start = TCP_SKB_CB(nskb)->seq;
@@ -1377,7 +1376,7 @@
 		sent_pkts = 1;
 	}
 
-	while ((skb = sk->sk_send_head)) {
+	while ((skb = tcp_send_head(sk))) {
 		unsigned int limit;
 
 		tso_segs = tcp_init_tso_segs(sk, skb, mss_now);
@@ -1396,7 +1395,7 @@
 						      nonagle : TCP_NAGLE_PUSH))))
 				break;
 		} else {
-			if (tcp_tso_should_defer(sk, tp, skb))
+			if (tcp_tso_should_defer(sk, skb))
 				break;
 		}
 
@@ -1425,31 +1424,31 @@
 		/* Advance the send_head.  This one is sent out.
 		 * This call will increment packets_out.
 		 */
-		update_send_head(sk, tp, skb);
+		update_send_head(sk, skb);
 
 		tcp_minshall_update(tp, mss_now, skb);
 		sent_pkts++;
 	}
 
 	if (likely(sent_pkts)) {
-		tcp_cwnd_validate(sk, tp);
+		tcp_cwnd_validate(sk);
 		return 0;
 	}
-	return !tp->packets_out && sk->sk_send_head;
+	return !tp->packets_out && tcp_send_head(sk);
 }
 
 /* Push out any pending frames which were held back due to
  * TCP_CORK or attempt at coalescing tiny packets.
  * The socket must be locked by the caller.
  */
-void __tcp_push_pending_frames(struct sock *sk, struct tcp_sock *tp,
-			       unsigned int cur_mss, int nonagle)
+void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss,
+			       int nonagle)
 {
-	struct sk_buff *skb = sk->sk_send_head;
+	struct sk_buff *skb = tcp_send_head(sk);
 
 	if (skb) {
 		if (tcp_write_xmit(sk, cur_mss, nonagle))
-			tcp_check_probe_timer(sk, tp);
+			tcp_check_probe_timer(sk);
 	}
 }
 
@@ -1459,7 +1458,7 @@
 void tcp_push_one(struct sock *sk, unsigned int mss_now)
 {
 	struct tcp_sock *tp = tcp_sk(sk);
-	struct sk_buff *skb = sk->sk_send_head;
+	struct sk_buff *skb = tcp_send_head(sk);
 	unsigned int tso_segs, cwnd_quota;
 
 	BUG_ON(!skb || skb->len < mss_now);
@@ -1493,8 +1492,8 @@
 		TCP_SKB_CB(skb)->when = tcp_time_stamp;
 
 		if (likely(!tcp_transmit_skb(sk, skb, 1, sk->sk_allocation))) {
-			update_send_head(sk, tp, skb);
-			tcp_cwnd_validate(sk, tp);
+			update_send_head(sk, skb);
+			tcp_cwnd_validate(sk);
 			return;
 		}
 	}
@@ -1620,7 +1619,7 @@
 static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *skb, int mss_now)
 {
 	struct tcp_sock *tp = tcp_sk(sk);
-	struct sk_buff *next_skb = skb->next;
+	struct sk_buff *next_skb = tcp_write_queue_next(sk, skb);
 
 	/* The first test we must make is that neither of these two
 	 * SKB's are still referenced by someone else.
@@ -1630,7 +1629,7 @@
 		u16 flags = TCP_SKB_CB(skb)->flags;
 
 		/* Also punt if next skb has been SACK'd. */
-		if(TCP_SKB_CB(next_skb)->sacked & TCPCB_SACKED_ACKED)
+		if (TCP_SKB_CB(next_skb)->sacked & TCPCB_SACKED_ACKED)
 			return;
 
 		/* Next skb is out of window. */
@@ -1652,9 +1651,11 @@
 		clear_all_retrans_hints(tp);
 
 		/* Ok.	We will be able to collapse the packet. */
-		__skb_unlink(next_skb, &sk->sk_write_queue);
+		tcp_unlink_write_queue(next_skb, sk);
 
-		memcpy(skb_put(skb, next_skb_size), next_skb->data, next_skb_size);
+		skb_copy_from_linear_data(next_skb,
+					  skb_put(skb, next_skb_size),
+					  next_skb_size);
 
 		if (next_skb->ip_summed == CHECKSUM_PARTIAL)
 			skb->ip_summed = CHECKSUM_PARTIAL;
@@ -1706,7 +1707,9 @@
 	unsigned int mss = tcp_current_mss(sk, 0);
 	int lost = 0;
 
-	sk_stream_for_retrans_queue(skb, sk) {
+	tcp_for_write_queue(skb, sk) {
+		if (skb == tcp_send_head(sk))
+			break;
 		if (skb->len > mss &&
 		    !(TCP_SKB_CB(skb)->sacked&TCPCB_SACKED_ACKED)) {
 			if (TCP_SKB_CB(skb)->sacked&TCPCB_SACKED_RETRANS) {
@@ -1788,13 +1791,13 @@
 	}
 
 	/* Collapse two adjacent packets if worthwhile and we can. */
-	if(!(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_SYN) &&
-	   (skb->len < (cur_mss >> 1)) &&
-	   (skb->next != sk->sk_send_head) &&
-	   (skb->next != (struct sk_buff *)&sk->sk_write_queue) &&
-	   (skb_shinfo(skb)->nr_frags == 0 && skb_shinfo(skb->next)->nr_frags == 0) &&
-	   (tcp_skb_pcount(skb) == 1 && tcp_skb_pcount(skb->next) == 1) &&
-	   (sysctl_tcp_retrans_collapse != 0))
+	if (!(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_SYN) &&
+	    (skb->len < (cur_mss >> 1)) &&
+	    (tcp_write_queue_next(sk, skb) != tcp_send_head(sk)) &&
+	    (!tcp_skb_is_last(sk, skb)) &&
+	    (skb_shinfo(skb)->nr_frags == 0 && skb_shinfo(tcp_write_queue_next(sk, skb))->nr_frags == 0) &&
+	    (tcp_skb_pcount(skb) == 1 && tcp_skb_pcount(tcp_write_queue_next(sk, skb)) == 1) &&
+	    (sysctl_tcp_retrans_collapse != 0))
 		tcp_retrans_try_collapse(sk, skb, cur_mss);
 
 	if (inet_csk(sk)->icsk_af_ops->rebuild_header(sk))
@@ -1804,9 +1807,9 @@
 	 * retransmit when old data is attached.  So strip it off
 	 * since it is cheap to do so and saves bytes on the network.
 	 */
-	if(skb->len > 0 &&
-	   (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN) &&
-	   tp->snd_una == (TCP_SKB_CB(skb)->end_seq - 1)) {
+	if (skb->len > 0 &&
+	    (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN) &&
+	    tp->snd_una == (TCP_SKB_CB(skb)->end_seq - 1)) {
 		if (!pskb_trim(skb, 0)) {
 			TCP_SKB_CB(skb)->seq = TCP_SKB_CB(skb)->end_seq - 1;
 			skb_shinfo(skb)->gso_segs = 1;
@@ -1872,15 +1875,17 @@
 		skb = tp->retransmit_skb_hint;
 		packet_cnt = tp->retransmit_cnt_hint;
 	}else{
-		skb = sk->sk_write_queue.next;
+		skb = tcp_write_queue_head(sk);
 		packet_cnt = 0;
 	}
 
 	/* First pass: retransmit lost packets. */
 	if (tp->lost_out) {
-		sk_stream_for_retrans_queue_from(skb, sk) {
+		tcp_for_write_queue_from(skb, sk) {
 			__u8 sacked = TCP_SKB_CB(skb)->sacked;
 
+			if (skb == tcp_send_head(sk))
+				break;
 			/* we could do better than to assign each time */
 			tp->retransmit_skb_hint = skb;
 			tp->retransmit_cnt_hint = packet_cnt;
@@ -1906,8 +1911,7 @@
 					else
 						NET_INC_STATS_BH(LINUX_MIB_TCPSLOWSTARTRETRANS);
 
-					if (skb ==
-					    skb_peek(&sk->sk_write_queue))
+					if (skb == tcp_write_queue_head(sk))
 						inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
 									  inet_csk(sk)->icsk_rto,
 									  TCP_RTO_MAX);
@@ -1937,18 +1941,20 @@
 	 * segments to send.
 	 */
 
-	if (tcp_may_send_now(sk, tp))
+	if (tcp_may_send_now(sk))
 		return;
 
 	if (tp->forward_skb_hint) {
 		skb = tp->forward_skb_hint;
 		packet_cnt = tp->forward_cnt_hint;
 	} else{
-		skb = sk->sk_write_queue.next;
+		skb = tcp_write_queue_head(sk);
 		packet_cnt = 0;
 	}
 
-	sk_stream_for_retrans_queue_from(skb, sk) {
+	tcp_for_write_queue_from(skb, sk) {
+		if (skb == tcp_send_head(sk))
+			break;
 		tp->forward_cnt_hint = packet_cnt;
 		tp->forward_skb_hint = skb;
 
@@ -1973,7 +1979,7 @@
 			break;
 		}
 
-		if (skb == skb_peek(&sk->sk_write_queue))
+		if (skb == tcp_write_queue_head(sk))
 			inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
 						  inet_csk(sk)->icsk_rto,
 						  TCP_RTO_MAX);
@@ -1989,7 +1995,7 @@
 void tcp_send_fin(struct sock *sk)
 {
 	struct tcp_sock *tp = tcp_sk(sk);
-	struct sk_buff *skb = skb_peek_tail(&sk->sk_write_queue);
+	struct sk_buff *skb = tcp_write_queue_tail(sk);
 	int mss_now;
 
 	/* Optimization, tack on the FIN if we have a queue of
@@ -1998,7 +2004,7 @@
 	 */
 	mss_now = tcp_current_mss(sk, 1);
 
-	if (sk->sk_send_head != NULL) {
+	if (tcp_send_head(sk) != NULL) {
 		TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_FIN;
 		TCP_SKB_CB(skb)->end_seq++;
 		tp->write_seq++;
@@ -2025,17 +2031,16 @@
 		TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq + 1;
 		tcp_queue_skb(sk, skb);
 	}
-	__tcp_push_pending_frames(sk, tp, mss_now, TCP_NAGLE_OFF);
+	__tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_OFF);
 }
 
 /* We get here when a process closes a file descriptor (either due to
  * an explicit close() or as a byproduct of exit()'ing) and there
  * was unread data in the receive queue.  This behavior is recommended
- * by draft-ietf-tcpimpl-prob-03.txt section 3.10.  -DaveM
+ * by RFC 2525, section 2.17.  -DaveM
  */
 void tcp_send_active_reset(struct sock *sk, gfp_t priority)
 {
-	struct tcp_sock *tp = tcp_sk(sk);
 	struct sk_buff *skb;
 
 	/* NOTE: No TCP options attached and we never retransmit this. */
@@ -2055,7 +2060,7 @@
 	skb_shinfo(skb)->gso_type = 0;
 
 	/* Send it off. */
-	TCP_SKB_CB(skb)->seq = tcp_acceptable_seq(sk, tp);
+	TCP_SKB_CB(skb)->seq = tcp_acceptable_seq(sk);
 	TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq;
 	TCP_SKB_CB(skb)->when = tcp_time_stamp;
 	if (tcp_transmit_skb(sk, skb, 0, priority))
@@ -2071,7 +2076,7 @@
 {
 	struct sk_buff* skb;
 
-	skb = skb_peek(&sk->sk_write_queue);
+	skb = tcp_write_queue_head(sk);
 	if (skb == NULL || !(TCP_SKB_CB(skb)->flags&TCPCB_FLAG_SYN)) {
 		printk(KERN_DEBUG "tcp_send_synack: wrong queue state\n");
 		return -EFAULT;
@@ -2081,9 +2086,9 @@
 			struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
 			if (nskb == NULL)
 				return -ENOMEM;
-			__skb_unlink(skb, &sk->sk_write_queue);
+			tcp_unlink_write_queue(skb, sk);
 			skb_header_release(nskb);
-			__skb_queue_head(&sk->sk_write_queue, nskb);
+			__tcp_add_write_queue_head(sk, nskb);
 			sk_stream_free_skb(sk, skb);
 			sk_charge_skb(sk, nskb);
 			skb = nskb;
@@ -2133,8 +2138,10 @@
 	if (md5)
 		tcp_header_size += TCPOLEN_MD5SIG_ALIGNED;
 #endif
-	skb->h.th = th = (struct tcphdr *) skb_push(skb, tcp_header_size);
+	skb_push(skb, tcp_header_size);
+	skb_reset_transport_header(skb);
 
+	th = tcp_hdr(skb);
 	memset(th, 0, sizeof(struct tcphdr));
 	th->syn = 1;
 	th->ack = 1;
@@ -2188,7 +2195,7 @@
 		tp->af_specific->calc_md5_hash(md5_hash_location,
 					       md5,
 					       NULL, dst, req,
-					       skb->h.th, sk->sk_protocol,
+					       tcp_hdr(skb), sk->sk_protocol,
 					       skb->len);
 	}
 #endif
@@ -2271,7 +2278,7 @@
 	skb_reserve(buff, MAX_TCP_HEADER);
 
 	TCP_SKB_CB(buff)->flags = TCPCB_FLAG_SYN;
-	TCP_ECN_send_syn(sk, tp, buff);
+	TCP_ECN_send_syn(sk, buff);
 	TCP_SKB_CB(buff)->sacked = 0;
 	skb_shinfo(buff)->gso_segs = 1;
 	skb_shinfo(buff)->gso_size = 0;
@@ -2285,7 +2292,7 @@
 	TCP_SKB_CB(buff)->when = tcp_time_stamp;
 	tp->retrans_stamp = TCP_SKB_CB(buff)->when;
 	skb_header_release(buff);
-	__skb_queue_tail(&sk->sk_write_queue, buff);
+	__tcp_add_write_queue_tail(sk, buff);
 	sk_charge_skb(sk, buff);
 	tp->packets_out += tcp_skb_pcount(buff);
 	tcp_transmit_skb(sk, buff, 1, GFP_KERNEL);
@@ -2363,7 +2370,6 @@
 {
 	/* If we have been reset, we may not send again. */
 	if (sk->sk_state != TCP_CLOSE) {
-		struct tcp_sock *tp = tcp_sk(sk);
 		struct sk_buff *buff;
 
 		/* We are not putting this on the write queue, so
@@ -2389,7 +2395,7 @@
 		skb_shinfo(buff)->gso_type = 0;
 
 		/* Send it off, this clears delayed acks for us. */
-		TCP_SKB_CB(buff)->seq = TCP_SKB_CB(buff)->end_seq = tcp_acceptable_seq(sk, tp);
+		TCP_SKB_CB(buff)->seq = TCP_SKB_CB(buff)->end_seq = tcp_acceptable_seq(sk);
 		TCP_SKB_CB(buff)->when = tcp_time_stamp;
 		tcp_transmit_skb(sk, buff, 0, GFP_ATOMIC);
 	}
@@ -2441,7 +2447,7 @@
 		struct tcp_sock *tp = tcp_sk(sk);
 		struct sk_buff *skb;
 
-		if ((skb = sk->sk_send_head) != NULL &&
+		if ((skb = tcp_send_head(sk)) != NULL &&
 		    before(TCP_SKB_CB(skb)->seq, tp->snd_una+tp->snd_wnd)) {
 			int err;
 			unsigned int mss = tcp_current_mss(sk, 0);
@@ -2467,7 +2473,7 @@
 			TCP_SKB_CB(skb)->when = tcp_time_stamp;
 			err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC);
 			if (!err) {
-				update_send_head(sk, tp, skb);
+				update_send_head(sk, skb);
 			}
 			return err;
 		} else {
@@ -2491,7 +2497,7 @@
 
 	err = tcp_write_wakeup(sk);
 
-	if (tp->packets_out || !sk->sk_send_head) {
+	if (tp->packets_out || !tcp_send_head(sk)) {
 		/* Cancel probe timer, if it is not required. */
 		icsk->icsk_probes_out = 0;
 		icsk->icsk_backoff = 0;
diff --git a/net/ipv4/tcp_probe.c b/net/ipv4/tcp_probe.c
index 61f406f2..3938d5d 100644
--- a/net/ipv4/tcp_probe.c
+++ b/net/ipv4/tcp_probe.c
@@ -26,6 +26,8 @@
 #include <linux/proc_fs.h>
 #include <linux/module.h>
 #include <linux/kfifo.h>
+#include <linux/ktime.h>
+#include <linux/time.h>
 #include <linux/vmalloc.h>
 
 #include <net/tcp.h>
@@ -34,43 +36,45 @@
 MODULE_DESCRIPTION("TCP cwnd snooper");
 MODULE_LICENSE("GPL");
 
-static int port = 0;
+static int port __read_mostly = 0;
 MODULE_PARM_DESC(port, "Port to match (0=all)");
 module_param(port, int, 0);
 
-static int bufsize = 64*1024;
+static int bufsize __read_mostly = 64*1024;
 MODULE_PARM_DESC(bufsize, "Log buffer size (default 64k)");
 module_param(bufsize, int, 0);
 
+static int full __read_mostly;
+MODULE_PARM_DESC(full, "Full log (1=every ack packet received,  0=only cwnd changes)");
+module_param(full, int, 0);
+
 static const char procname[] = "tcpprobe";
 
 struct {
-	struct kfifo  *fifo;
-	spinlock_t    lock;
+	struct kfifo	*fifo;
+	spinlock_t	lock;
 	wait_queue_head_t wait;
-	struct timeval tstart;
+	ktime_t		start;
+	u32		lastcwnd;
 } tcpw;
 
+/*
+ * Print to log with timestamps.
+ * FIXME: causes an extra copy
+ */
 static void printl(const char *fmt, ...)
 {
 	va_list args;
 	int len;
-	struct timeval now;
+	struct timespec tv;
 	char tbuf[256];
 
 	va_start(args, fmt);
-	do_gettimeofday(&now);
+	/* want monotonic time since start of tcp_probe */
+	tv = ktime_to_timespec(ktime_sub(ktime_get(), tcpw.start));
 
-	now.tv_sec -= tcpw.tstart.tv_sec;
-	now.tv_usec -= tcpw.tstart.tv_usec;
-	if (now.tv_usec < 0) {
-		--now.tv_sec;
-		now.tv_usec += 1000000;
-	}
-
-	len = sprintf(tbuf, "%lu.%06lu ",
-		      (unsigned long) now.tv_sec,
-		      (unsigned long) now.tv_usec);
+	len = sprintf(tbuf, "%lu.%09lu ",
+		      (unsigned long) tv.tv_sec, (unsigned long) tv.tv_nsec);
 	len += vscnprintf(tbuf+len, sizeof(tbuf)-len, fmt, args);
 	va_end(args);
 
@@ -78,38 +82,44 @@
 	wake_up(&tcpw.wait);
 }
 
-static int jtcp_sendmsg(struct kiocb *iocb, struct sock *sk,
-			struct msghdr *msg, size_t size)
+/*
+ * Hook inserted to be called before each receive packet.
+ * Note: arguments must match tcp_rcv_established()!
+ */
+static int jtcp_rcv_established(struct sock *sk, struct sk_buff *skb,
+			       struct tcphdr *th, unsigned len)
 {
 	const struct tcp_sock *tp = tcp_sk(sk);
 	const struct inet_sock *inet = inet_sk(sk);
 
-	if (port == 0 || ntohs(inet->dport) == port ||
-	    ntohs(inet->sport) == port) {
+	/* Only update if port matches */
+	if ((port == 0 || ntohs(inet->dport) == port || ntohs(inet->sport) == port)
+	    && (full || tp->snd_cwnd != tcpw.lastcwnd)) {
 		printl("%d.%d.%d.%d:%u %d.%d.%d.%d:%u %d %#x %#x %u %u %u\n",
 		       NIPQUAD(inet->saddr), ntohs(inet->sport),
 		       NIPQUAD(inet->daddr), ntohs(inet->dport),
-		       size, tp->snd_nxt, tp->snd_una,
+		       skb->len, tp->snd_nxt, tp->snd_una,
 		       tp->snd_cwnd, tcp_current_ssthresh(sk),
-		       tp->snd_wnd);
+		       tp->snd_wnd, tp->srtt >> 3);
+		tcpw.lastcwnd = tp->snd_cwnd;
 	}
 
 	jprobe_return();
 	return 0;
 }
 
-static struct jprobe tcp_send_probe = {
+static struct jprobe tcp_probe = {
 	.kp = {
-		.symbol_name	= "tcp_sendmsg",
+		.symbol_name	= "tcp_rcv_established",
 	},
-	.entry	= JPROBE_ENTRY(jtcp_sendmsg),
+	.entry	= JPROBE_ENTRY(jtcp_rcv_established),
 };
 
 
 static int tcpprobe_open(struct inode * inode, struct file * file)
 {
 	kfifo_reset(tcpw.fifo);
-	do_gettimeofday(&tcpw.tstart);
+	tcpw.start = ktime_get();
 	return 0;
 }
 
@@ -162,7 +172,7 @@
 	if (!proc_net_fops_create(procname, S_IRUSR, &tcpprobe_fops))
 		goto err0;
 
-	ret = register_jprobe(&tcp_send_probe);
+	ret = register_jprobe(&tcp_probe);
 	if (ret)
 		goto err1;
 
@@ -180,7 +190,7 @@
 {
 	kfifo_free(tcpw.fifo);
 	proc_net_remove(procname);
-	unregister_jprobe(&tcp_send_probe);
+	unregister_jprobe(&tcp_probe);
 
 }
 module_exit(tcpprobe_exit);
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index a9243cf..2ca97b2 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -233,7 +233,7 @@
 	struct tcp_sock *tp = tcp_sk(sk);
 	int max_probes;
 
-	if (tp->packets_out || !sk->sk_send_head) {
+	if (tp->packets_out || !tcp_send_head(sk)) {
 		icsk->icsk_probes_out = 0;
 		return;
 	}
@@ -284,7 +284,7 @@
 	if (!tp->packets_out)
 		goto out;
 
-	BUG_TRAP(!skb_queue_empty(&sk->sk_write_queue));
+	BUG_TRAP(!tcp_write_queue_empty(sk));
 
 	if (!tp->snd_wnd && !sock_flag(sk, SOCK_DEAD) &&
 	    !((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV))) {
@@ -306,7 +306,7 @@
 			goto out;
 		}
 		tcp_enter_loss(sk, 0);
-		tcp_retransmit_skb(sk, skb_peek(&sk->sk_write_queue));
+		tcp_retransmit_skb(sk, tcp_write_queue_head(sk));
 		__sk_dst_reset(sk);
 		goto out_reset_timer;
 	}
@@ -341,7 +341,7 @@
 		tcp_enter_loss(sk, 0);
 	}
 
-	if (tcp_retransmit_skb(sk, skb_peek(&sk->sk_write_queue)) > 0) {
+	if (tcp_retransmit_skb(sk, tcp_write_queue_head(sk)) > 0) {
 		/* Retransmission failed because of local congestion,
 		 * do not backoff.
 		 */
@@ -482,7 +482,7 @@
 	elapsed = keepalive_time_when(tp);
 
 	/* It is alive without keepalive 8) */
-	if (tp->packets_out || sk->sk_send_head)
+	if (tp->packets_out || tcp_send_head(sk))
 		goto resched;
 
 	elapsed = tcp_time_stamp - tp->rcv_tstamp;
diff --git a/net/ipv4/tcp_vegas.c b/net/ipv4/tcp_vegas.c
index 5c484dc..73e19cf 100644
--- a/net/ipv4/tcp_vegas.c
+++ b/net/ipv4/tcp_vegas.c
@@ -38,6 +38,8 @@
 
 #include <net/tcp.h>
 
+#include "tcp_vegas.h"
+
 /* Default values of the Vegas variables, in fixed-point representation
  * with V_PARAM_SHIFT bits to the right of the binary point.
  */
@@ -54,17 +56,6 @@
 MODULE_PARM_DESC(gamma, "limit on increase (scale by 2)");
 
 
-/* Vegas variables */
-struct vegas {
-	u32	beg_snd_nxt;	/* right edge during last RTT */
-	u32	beg_snd_una;	/* left edge  during last RTT */
-	u32	beg_snd_cwnd;	/* saves the size of the cwnd */
-	u8	doing_vegas_now;/* if true, do vegas for this RTT */
-	u16	cntRTT;		/* # of RTTs measured within last RTT */
-	u32	minRTT;		/* min of RTTs measured within last RTT (in usec) */
-	u32	baseRTT;	/* the min of all Vegas RTT measurements seen (in usec) */
-};
-
 /* There are several situations when we must "re-start" Vegas:
  *
  *  o when a connection is established
@@ -81,7 +72,7 @@
  * Instead we must wait until the completion of an RTT during
  * which we actually receive ACKs.
  */
-static inline void vegas_enable(struct sock *sk)
+static void vegas_enable(struct sock *sk)
 {
 	const struct tcp_sock *tp = tcp_sk(sk);
 	struct vegas *vegas = inet_csk_ca(sk);
@@ -104,13 +95,14 @@
 	vegas->doing_vegas_now = 0;
 }
 
-static void tcp_vegas_init(struct sock *sk)
+void tcp_vegas_init(struct sock *sk)
 {
 	struct vegas *vegas = inet_csk_ca(sk);
 
 	vegas->baseRTT = 0x7fffffff;
 	vegas_enable(sk);
 }
+EXPORT_SYMBOL_GPL(tcp_vegas_init);
 
 /* Do RTT sampling needed for Vegas.
  * Basically we:
@@ -120,10 +112,13 @@
  *   o min-filter RTT samples from a much longer window (forever for now)
  *     to find the propagation delay (baseRTT)
  */
-static void tcp_vegas_rtt_calc(struct sock *sk, u32 usrtt)
+void tcp_vegas_pkts_acked(struct sock *sk, u32 cnt, ktime_t last)
 {
 	struct vegas *vegas = inet_csk_ca(sk);
-	u32 vrtt = usrtt + 1; /* Never allow zero rtt or baseRTT */
+	u32 vrtt;
+
+	/* Never allow zero rtt or baseRTT */
+	vrtt = ktime_to_us(net_timedelta(last)) + 1;
 
 	/* Filter to find propagation delay: */
 	if (vrtt < vegas->baseRTT)
@@ -135,8 +130,9 @@
 	vegas->minRTT = min(vegas->minRTT, vrtt);
 	vegas->cntRTT++;
 }
+EXPORT_SYMBOL_GPL(tcp_vegas_pkts_acked);
 
-static void tcp_vegas_state(struct sock *sk, u8 ca_state)
+void tcp_vegas_state(struct sock *sk, u8 ca_state)
 {
 
 	if (ca_state == TCP_CA_Open)
@@ -144,6 +140,7 @@
 	else
 		vegas_disable(sk);
 }
+EXPORT_SYMBOL_GPL(tcp_vegas_state);
 
 /*
  * If the connection is idle and we are restarting,
@@ -154,12 +151,13 @@
  * packets, _then_ we can make Vegas calculations
  * again.
  */
-static void tcp_vegas_cwnd_event(struct sock *sk, enum tcp_ca_event event)
+void tcp_vegas_cwnd_event(struct sock *sk, enum tcp_ca_event event)
 {
 	if (event == CA_EVENT_CWND_RESTART ||
 	    event == CA_EVENT_TX_START)
 		tcp_vegas_init(sk);
 }
+EXPORT_SYMBOL_GPL(tcp_vegas_cwnd_event);
 
 static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack,
 				 u32 seq_rtt, u32 in_flight, int flag)
@@ -336,30 +334,29 @@
 }
 
 /* Extract info for Tcp socket info provided via netlink. */
-static void tcp_vegas_get_info(struct sock *sk, u32 ext,
-			       struct sk_buff *skb)
+void tcp_vegas_get_info(struct sock *sk, u32 ext, struct sk_buff *skb)
 {
 	const struct vegas *ca = inet_csk_ca(sk);
 	if (ext & (1 << (INET_DIAG_VEGASINFO - 1))) {
-		struct tcpvegas_info *info;
+		struct tcpvegas_info info = {
+			.tcpv_enabled = ca->doing_vegas_now,
+			.tcpv_rttcnt = ca->cntRTT,
+			.tcpv_rtt = ca->baseRTT,
+			.tcpv_minrtt = ca->minRTT,
+		};
 
-		info = RTA_DATA(__RTA_PUT(skb, INET_DIAG_VEGASINFO,
-					  sizeof(*info)));
-
-		info->tcpv_enabled = ca->doing_vegas_now;
-		info->tcpv_rttcnt = ca->cntRTT;
-		info->tcpv_rtt = ca->baseRTT;
-		info->tcpv_minrtt = ca->minRTT;
-	rtattr_failure:	;
+		nla_put(skb, INET_DIAG_VEGASINFO, sizeof(info), &info);
 	}
 }
+EXPORT_SYMBOL_GPL(tcp_vegas_get_info);
 
 static struct tcp_congestion_ops tcp_vegas = {
+	.flags		= TCP_CONG_RTT_STAMP,
 	.init		= tcp_vegas_init,
 	.ssthresh	= tcp_reno_ssthresh,
 	.cong_avoid	= tcp_vegas_cong_avoid,
 	.min_cwnd	= tcp_reno_min_cwnd,
-	.rtt_sample	= tcp_vegas_rtt_calc,
+	.pkts_acked	= tcp_vegas_pkts_acked,
 	.set_state	= tcp_vegas_state,
 	.cwnd_event	= tcp_vegas_cwnd_event,
 	.get_info	= tcp_vegas_get_info,
diff --git a/net/ipv4/tcp_vegas.h b/net/ipv4/tcp_vegas.h
new file mode 100644
index 0000000..502fa81
--- /dev/null
+++ b/net/ipv4/tcp_vegas.h
@@ -0,0 +1,24 @@
+/*
+ * TCP Vegas congestion control interface
+ */
+#ifndef __TCP_VEGAS_H
+#define __TCP_VEGAS_H 1
+
+/* Vegas variables */
+struct vegas {
+	u32	beg_snd_nxt;	/* right edge during last RTT */
+	u32	beg_snd_una;	/* left edge  during last RTT */
+	u32	beg_snd_cwnd;	/* saves the size of the cwnd */
+	u8	doing_vegas_now;/* if true, do vegas for this RTT */
+	u16	cntRTT;		/* # of RTTs measured within last RTT */
+	u32	minRTT;		/* min of RTTs measured within last RTT (in usec) */
+	u32	baseRTT;	/* the min of all Vegas RTT measurements seen (in usec) */
+};
+
+extern void tcp_vegas_init(struct sock *sk);
+extern void tcp_vegas_state(struct sock *sk, u8 ca_state);
+extern void tcp_vegas_pkts_acked(struct sock *sk, u32 cnt, ktime_t last);
+extern void tcp_vegas_cwnd_event(struct sock *sk, enum tcp_ca_event event);
+extern void tcp_vegas_get_info(struct sock *sk, u32 ext, struct sk_buff *skb);
+
+#endif	/* __TCP_VEGAS_H */
diff --git a/net/ipv4/tcp_veno.c b/net/ipv4/tcp_veno.c
index ce57bf3..9edb340 100644
--- a/net/ipv4/tcp_veno.c
+++ b/net/ipv4/tcp_veno.c
@@ -69,10 +69,13 @@
 }
 
 /* Do rtt sampling needed for Veno. */
-static void tcp_veno_rtt_calc(struct sock *sk, u32 usrtt)
+static void tcp_veno_pkts_acked(struct sock *sk, u32 cnt, ktime_t last)
 {
 	struct veno *veno = inet_csk_ca(sk);
-	u32 vrtt = usrtt + 1;	/* Never allow zero rtt or basertt */
+	u32 vrtt;
+
+	/* Never allow zero rtt or baseRTT */
+	vrtt = ktime_to_us(net_timedelta(last)) + 1;
 
 	/* Filter to find propagation delay: */
 	if (vrtt < veno->basertt)
@@ -199,10 +202,11 @@
 }
 
 static struct tcp_congestion_ops tcp_veno = {
+	.flags		= TCP_CONG_RTT_STAMP,
 	.init		= tcp_veno_init,
 	.ssthresh	= tcp_veno_ssthresh,
 	.cong_avoid	= tcp_veno_cong_avoid,
-	.rtt_sample	= tcp_veno_rtt_calc,
+	.pkts_acked	= tcp_veno_pkts_acked,
 	.set_state	= tcp_veno_state,
 	.cwnd_event	= tcp_veno_cwnd_event,
 
diff --git a/net/ipv4/tcp_westwood.c b/net/ipv4/tcp_westwood.c
index 4e1b610..e61e09d 100644
--- a/net/ipv4/tcp_westwood.c
+++ b/net/ipv4/tcp_westwood.c
@@ -100,7 +100,7 @@
  * Called after processing group of packets.
  * but all westwood needs is the last sample of srtt.
  */
-static void tcp_westwood_pkts_acked(struct sock *sk, u32 cnt)
+static void tcp_westwood_pkts_acked(struct sock *sk, u32 cnt, ktime_t last)
 {
 	struct westwood *w = inet_csk_ca(sk);
 	if (cnt > 0)
@@ -226,7 +226,7 @@
 	struct tcp_sock *tp = tcp_sk(sk);
 	struct westwood *w = inet_csk_ca(sk);
 
-	switch(event) {
+	switch (event) {
 	case CA_EVENT_FAST_ACK:
 		westwood_fast_bw(sk);
 		break;
@@ -260,16 +260,13 @@
 {
 	const struct westwood *ca = inet_csk_ca(sk);
 	if (ext & (1 << (INET_DIAG_VEGASINFO - 1))) {
-		struct rtattr *rta;
-		struct tcpvegas_info *info;
+		struct tcpvegas_info info = {
+			.tcpv_enabled = 1,
+			.tcpv_rtt = jiffies_to_usecs(ca->rtt),
+			.tcpv_minrtt = jiffies_to_usecs(ca->rtt_min),
+		};
 
-		rta = __RTA_PUT(skb, INET_DIAG_VEGASINFO, sizeof(*info));
-		info = RTA_DATA(rta);
-		info->tcpv_enabled = 1;
-		info->tcpv_rttcnt = 0;
-		info->tcpv_rtt = jiffies_to_usecs(ca->rtt);
-		info->tcpv_minrtt = jiffies_to_usecs(ca->rtt_min);
-	rtattr_failure:	;
+		nla_put(skb, INET_DIAG_VEGASINFO, sizeof(info), &info);
 	}
 }
 
diff --git a/net/ipv4/tcp_yeah.c b/net/ipv4/tcp_yeah.c
new file mode 100644
index 0000000..545ed23
--- /dev/null
+++ b/net/ipv4/tcp_yeah.c
@@ -0,0 +1,268 @@
+/*
+ *
+ *   YeAH TCP
+ *
+ * For further details look at:
+ *    http://wil.cs.caltech.edu/pfldnet2007/paper/YeAH_TCP.pdf
+ *
+ */
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/inet_diag.h>
+
+#include <net/tcp.h>
+
+#include "tcp_vegas.h"
+
+#define TCP_YEAH_ALPHA       80 //lin number of packets queued at the bottleneck
+#define TCP_YEAH_GAMMA        1 //lin fraction of queue to be removed per rtt
+#define TCP_YEAH_DELTA        3 //log minimum fraction of cwnd to be removed on loss
+#define TCP_YEAH_EPSILON      1 //log maximum fraction to be removed on early decongestion
+#define TCP_YEAH_PHY          8 //lin maximum delta from base
+#define TCP_YEAH_RHO         16 //lin minumum number of consecutive rtt to consider competition on loss
+#define TCP_YEAH_ZETA        50 //lin minimum number of state switchs to reset reno_count
+
+#define TCP_SCALABLE_AI_CNT	 100U
+
+/* YeAH variables */
+struct yeah {
+	struct vegas vegas;	/* must be first */
+
+	/* YeAH */
+	u32 lastQ;
+	u32 doing_reno_now;
+
+	u32 reno_count;
+	u32 fast_count;
+
+	u32 pkts_acked;
+};
+
+static void tcp_yeah_init(struct sock *sk)
+{
+	struct tcp_sock *tp = tcp_sk(sk);
+	struct yeah *yeah = inet_csk_ca(sk);
+
+	tcp_vegas_init(sk);
+
+	yeah->doing_reno_now = 0;
+	yeah->lastQ = 0;
+
+	yeah->reno_count = 2;
+
+	/* Ensure the MD arithmetic works.  This is somewhat pedantic,
+	 * since I don't think we will see a cwnd this large. :) */
+	tp->snd_cwnd_clamp = min_t(u32, tp->snd_cwnd_clamp, 0xffffffff/128);
+
+}
+
+
+static void tcp_yeah_pkts_acked(struct sock *sk, u32 pkts_acked, ktime_t last)
+{
+	const struct inet_connection_sock *icsk = inet_csk(sk);
+	struct yeah *yeah = inet_csk_ca(sk);
+
+	if (icsk->icsk_ca_state == TCP_CA_Open)
+		yeah->pkts_acked = pkts_acked;
+
+	tcp_vegas_pkts_acked(sk, pkts_acked, last);
+}
+
+static void tcp_yeah_cong_avoid(struct sock *sk, u32 ack,
+				u32 seq_rtt, u32 in_flight, int flag)
+{
+	struct tcp_sock *tp = tcp_sk(sk);
+	struct yeah *yeah = inet_csk_ca(sk);
+
+	if (!tcp_is_cwnd_limited(sk, in_flight))
+		return;
+
+	if (tp->snd_cwnd <= tp->snd_ssthresh)
+		tcp_slow_start(tp);
+
+	else if (!yeah->doing_reno_now) {
+		/* Scalable */
+
+		tp->snd_cwnd_cnt+=yeah->pkts_acked;
+		if (tp->snd_cwnd_cnt > min(tp->snd_cwnd, TCP_SCALABLE_AI_CNT)){
+			if (tp->snd_cwnd < tp->snd_cwnd_clamp)
+				tp->snd_cwnd++;
+			tp->snd_cwnd_cnt = 0;
+		}
+
+		yeah->pkts_acked = 1;
+
+	} else {
+		/* Reno */
+
+		if (tp->snd_cwnd_cnt < tp->snd_cwnd)
+			tp->snd_cwnd_cnt++;
+
+		if (tp->snd_cwnd_cnt >= tp->snd_cwnd) {
+			tp->snd_cwnd++;
+			tp->snd_cwnd_cnt = 0;
+		}
+	}
+
+	/* The key players are v_vegas.beg_snd_una and v_beg_snd_nxt.
+	 *
+	 * These are so named because they represent the approximate values
+	 * of snd_una and snd_nxt at the beginning of the current RTT. More
+	 * precisely, they represent the amount of data sent during the RTT.
+	 * At the end of the RTT, when we receive an ACK for v_beg_snd_nxt,
+	 * we will calculate that (v_beg_snd_nxt - v_vegas.beg_snd_una) outstanding
+	 * bytes of data have been ACKed during the course of the RTT, giving
+	 * an "actual" rate of:
+	 *
+	 *     (v_beg_snd_nxt - v_vegas.beg_snd_una) / (rtt duration)
+	 *
+	 * Unfortunately, v_vegas.beg_snd_una is not exactly equal to snd_una,
+	 * because delayed ACKs can cover more than one segment, so they
+	 * don't line up yeahly with the boundaries of RTTs.
+	 *
+	 * Another unfortunate fact of life is that delayed ACKs delay the
+	 * advance of the left edge of our send window, so that the number
+	 * of bytes we send in an RTT is often less than our cwnd will allow.
+	 * So we keep track of our cwnd separately, in v_beg_snd_cwnd.
+	 */
+
+	if (after(ack, yeah->vegas.beg_snd_nxt)) {
+
+		/* We do the Vegas calculations only if we got enough RTT
+		 * samples that we can be reasonably sure that we got
+		 * at least one RTT sample that wasn't from a delayed ACK.
+		 * If we only had 2 samples total,
+		 * then that means we're getting only 1 ACK per RTT, which
+		 * means they're almost certainly delayed ACKs.
+		 * If  we have 3 samples, we should be OK.
+		 */
+
+		if (yeah->vegas.cntRTT > 2) {
+			u32 rtt, queue;
+			u64 bw;
+
+			/* We have enough RTT samples, so, using the Vegas
+			 * algorithm, we determine if we should increase or
+			 * decrease cwnd, and by how much.
+			 */
+
+			/* Pluck out the RTT we are using for the Vegas
+			 * calculations. This is the min RTT seen during the
+			 * last RTT. Taking the min filters out the effects
+			 * of delayed ACKs, at the cost of noticing congestion
+			 * a bit later.
+			 */
+			rtt = yeah->vegas.minRTT;
+
+			/* Compute excess number of packets above bandwidth
+			 * Avoid doing full 64 bit divide.
+			 */
+			bw = tp->snd_cwnd;
+			bw *= rtt - yeah->vegas.baseRTT;
+			do_div(bw, rtt);
+			queue = bw;
+
+			if (queue > TCP_YEAH_ALPHA ||
+			    rtt - yeah->vegas.baseRTT > (yeah->vegas.baseRTT / TCP_YEAH_PHY)) {
+				if (queue > TCP_YEAH_ALPHA
+				    && tp->snd_cwnd > yeah->reno_count) {
+					u32 reduction = min(queue / TCP_YEAH_GAMMA ,
+							    tp->snd_cwnd >> TCP_YEAH_EPSILON);
+
+					tp->snd_cwnd -= reduction;
+
+					tp->snd_cwnd = max(tp->snd_cwnd,
+							   yeah->reno_count);
+
+					tp->snd_ssthresh = tp->snd_cwnd;
+				}
+
+				if (yeah->reno_count <= 2)
+					yeah->reno_count = max(tp->snd_cwnd>>1, 2U);
+				else
+					yeah->reno_count++;
+
+				yeah->doing_reno_now = min(yeah->doing_reno_now + 1,
+							   0xffffffU);
+			} else {
+				yeah->fast_count++;
+
+				if (yeah->fast_count > TCP_YEAH_ZETA) {
+					yeah->reno_count = 2;
+					yeah->fast_count = 0;
+				}
+
+				yeah->doing_reno_now = 0;
+			}
+
+			yeah->lastQ = queue;
+
+		}
+
+		/* Save the extent of the current window so we can use this
+		 * at the end of the next RTT.
+		 */
+		yeah->vegas.beg_snd_una  = yeah->vegas.beg_snd_nxt;
+		yeah->vegas.beg_snd_nxt  = tp->snd_nxt;
+		yeah->vegas.beg_snd_cwnd = tp->snd_cwnd;
+
+		/* Wipe the slate clean for the next RTT. */
+		yeah->vegas.cntRTT = 0;
+		yeah->vegas.minRTT = 0x7fffffff;
+	}
+}
+
+static u32 tcp_yeah_ssthresh(struct sock *sk) {
+	const struct tcp_sock *tp = tcp_sk(sk);
+	struct yeah *yeah = inet_csk_ca(sk);
+	u32 reduction;
+
+	if (yeah->doing_reno_now < TCP_YEAH_RHO) {
+		reduction = yeah->lastQ;
+
+		reduction = min( reduction, max(tp->snd_cwnd>>1, 2U) );
+
+		reduction = max( reduction, tp->snd_cwnd >> TCP_YEAH_DELTA);
+	} else
+		reduction = max(tp->snd_cwnd>>1,2U);
+
+	yeah->fast_count = 0;
+	yeah->reno_count = max(yeah->reno_count>>1, 2U);
+
+	return tp->snd_cwnd - reduction;
+}
+
+static struct tcp_congestion_ops tcp_yeah = {
+	.flags		= TCP_CONG_RTT_STAMP,
+	.init		= tcp_yeah_init,
+	.ssthresh	= tcp_yeah_ssthresh,
+	.cong_avoid	= tcp_yeah_cong_avoid,
+	.min_cwnd	= tcp_reno_min_cwnd,
+	.set_state	= tcp_vegas_state,
+	.cwnd_event	= tcp_vegas_cwnd_event,
+	.get_info	= tcp_vegas_get_info,
+	.pkts_acked	= tcp_yeah_pkts_acked,
+
+	.owner		= THIS_MODULE,
+	.name		= "yeah",
+};
+
+static int __init tcp_yeah_register(void)
+{
+	BUG_ON(sizeof(struct yeah) > ICSK_CA_PRIV_SIZE);
+	tcp_register_congestion_control(&tcp_yeah);
+	return 0;
+}
+
+static void __exit tcp_yeah_unregister(void)
+{
+	tcp_unregister_congestion_control(&tcp_yeah);
+}
+
+module_init(tcp_yeah_register);
+module_exit(tcp_yeah_unregister);
+
+MODULE_AUTHOR("Angelo P. Castellani");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("YeAH TCP");
diff --git a/net/ipv4/tcp_yeah.h b/net/ipv4/tcp_yeah.h
new file mode 100644
index 0000000..ed3b719
--- /dev/null
+++ b/net/ipv4/tcp_yeah.h
@@ -0,0 +1,7 @@
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/inet_diag.h>
+#include <asm/div64.h>
+
+#include <net/tcp.h>
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index fc620a7..113e0c4 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -114,14 +114,33 @@
 
 static int udp_port_rover;
 
-static inline int __udp_lib_lport_inuse(__u16 num, struct hlist_head udptable[])
+/*
+ * Note about this hash function :
+ * Typical use is probably daddr = 0, only dport is going to vary hash
+ */
+static inline unsigned int hash_port_and_addr(__u16 port, __be32 addr)
+{
+	addr ^= addr >> 16;
+	addr ^= addr >> 8;
+	return port ^ addr;
+}
+
+static inline int __udp_lib_port_inuse(unsigned int hash, int port,
+	__be32 daddr, struct hlist_head udptable[])
 {
 	struct sock *sk;
 	struct hlist_node *node;
+	struct inet_sock *inet;
 
-	sk_for_each(sk, node, &udptable[num & (UDP_HTABLE_SIZE - 1)])
-		if (sk->sk_hash == num)
+	sk_for_each(sk, node, &udptable[hash & (UDP_HTABLE_SIZE - 1)]) {
+		if (sk->sk_hash != hash)
+			continue;
+		inet = inet_sk(sk);
+		if (inet->num != port)
+			continue;
+		if (inet->rcv_saddr == daddr)
 			return 1;
+	}
 	return 0;
 }
 
@@ -142,6 +161,7 @@
 	struct hlist_node *node;
 	struct hlist_head *head;
 	struct sock *sk2;
+	unsigned int hash;
 	int    error = 1;
 
 	write_lock_bh(&udp_hash_lock);
@@ -156,7 +176,9 @@
 		for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) {
 			int size;
 
-			head = &udptable[result & (UDP_HTABLE_SIZE - 1)];
+			hash = hash_port_and_addr(result,
+					inet_sk(sk)->rcv_saddr);
+			head = &udptable[hash & (UDP_HTABLE_SIZE - 1)];
 			if (hlist_empty(head)) {
 				if (result > sysctl_local_port_range[1])
 					result = sysctl_local_port_range[0] +
@@ -175,12 +197,23 @@
 			;
 		}
 		result = best;
-		for(i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++, result += UDP_HTABLE_SIZE) {
+		for (i = 0; i < (1 << 16) / UDP_HTABLE_SIZE;
+		     i++, result += UDP_HTABLE_SIZE) {
 			if (result > sysctl_local_port_range[1])
 				result = sysctl_local_port_range[0]
 					+ ((result - sysctl_local_port_range[0]) &
 					   (UDP_HTABLE_SIZE - 1));
-			if (! __udp_lib_lport_inuse(result, udptable))
+			hash = hash_port_and_addr(result, 0);
+			if (__udp_lib_port_inuse(hash, result,
+						 0, udptable))
+				continue;
+			if (!inet_sk(sk)->rcv_saddr)
+				break;
+
+			hash = hash_port_and_addr(result,
+					inet_sk(sk)->rcv_saddr);
+			if (! __udp_lib_port_inuse(hash, result,
+				inet_sk(sk)->rcv_saddr, udptable))
 				break;
 		}
 		if (i >= (1 << 16) / UDP_HTABLE_SIZE)
@@ -188,21 +221,41 @@
 gotit:
 		*port_rover = snum = result;
 	} else {
-		head = &udptable[snum & (UDP_HTABLE_SIZE - 1)];
+		hash = hash_port_and_addr(snum, 0);
+		head = &udptable[hash & (UDP_HTABLE_SIZE - 1)];
 
 		sk_for_each(sk2, node, head)
-			if (sk2->sk_hash == snum                             &&
-			    sk2 != sk                                        &&
-			    (!sk2->sk_reuse        || !sk->sk_reuse)         &&
-			    (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if
-			     || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
-			    (*saddr_comp)(sk, sk2)                             )
+			if (sk2->sk_hash == hash &&
+			    sk2 != sk &&
+			    inet_sk(sk2)->num == snum &&
+			    (!sk2->sk_reuse || !sk->sk_reuse) &&
+			    (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if ||
+			     sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
+			    (*saddr_comp)(sk, sk2))
 				goto fail;
+
+		if (inet_sk(sk)->rcv_saddr) {
+			hash = hash_port_and_addr(snum,
+						  inet_sk(sk)->rcv_saddr);
+			head = &udptable[hash & (UDP_HTABLE_SIZE - 1)];
+
+			sk_for_each(sk2, node, head)
+				if (sk2->sk_hash == hash &&
+				    sk2 != sk &&
+				    inet_sk(sk2)->num == snum &&
+				    (!sk2->sk_reuse || !sk->sk_reuse) &&
+				    (!sk2->sk_bound_dev_if ||
+				     !sk->sk_bound_dev_if ||
+				     sk2->sk_bound_dev_if ==
+				     sk->sk_bound_dev_if) &&
+				    (*saddr_comp)(sk, sk2))
+					goto fail;
+		}
 	}
 	inet_sk(sk)->num = snum;
-	sk->sk_hash = snum;
+	sk->sk_hash = hash;
 	if (sk_unhashed(sk)) {
-		head = &udptable[snum & (UDP_HTABLE_SIZE - 1)];
+		head = &udptable[hash & (UDP_HTABLE_SIZE - 1)];
 		sk_add_node(sk, head);
 		sock_prot_inc_use(sk->sk_prot);
 	}
@@ -212,13 +265,13 @@
 	return error;
 }
 
-__inline__ int udp_get_port(struct sock *sk, unsigned short snum,
+int udp_get_port(struct sock *sk, unsigned short snum,
 			int (*scmp)(const struct sock *, const struct sock *))
 {
 	return  __udp_lib_get_port(sk, snum, udp_hash, &udp_port_rover, scmp);
 }
 
-inline int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2)
+int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2)
 {
 	struct inet_sock *inet1 = inet_sk(sk1), *inet2 = inet_sk(sk2);
 
@@ -241,63 +294,77 @@
 {
 	struct sock *sk, *result = NULL;
 	struct hlist_node *node;
-	unsigned short hnum = ntohs(dport);
-	int badness = -1;
+	unsigned int hash, hashwild;
+	int score, best = -1, hport = ntohs(dport);
+
+ 	hash = hash_port_and_addr(hport, daddr);
+ 	hashwild = hash_port_and_addr(hport, 0);
 
 	read_lock(&udp_hash_lock);
-	sk_for_each(sk, node, &udptable[hnum & (UDP_HTABLE_SIZE - 1)]) {
+
+lookup:
+
+	sk_for_each(sk, node, &udptable[hash & (UDP_HTABLE_SIZE - 1)]) {
 		struct inet_sock *inet = inet_sk(sk);
 
-		if (sk->sk_hash == hnum && !ipv6_only_sock(sk)) {
-			int score = (sk->sk_family == PF_INET ? 1 : 0);
-			if (inet->rcv_saddr) {
-				if (inet->rcv_saddr != daddr)
-					continue;
-				score+=2;
-			}
-			if (inet->daddr) {
-				if (inet->daddr != saddr)
-					continue;
-				score+=2;
-			}
-			if (inet->dport) {
-				if (inet->dport != sport)
-					continue;
-				score+=2;
-			}
-			if (sk->sk_bound_dev_if) {
-				if (sk->sk_bound_dev_if != dif)
-					continue;
-				score+=2;
-			}
-			if(score == 9) {
-				result = sk;
-				break;
-			} else if(score > badness) {
-				result = sk;
-				badness = score;
-			}
+		if (sk->sk_hash != hash || ipv6_only_sock(sk) ||
+			inet->num != hport)
+			continue;
+
+		score = (sk->sk_family == PF_INET ? 1 : 0);
+		if (inet->rcv_saddr) {
+			if (inet->rcv_saddr != daddr)
+				continue;
+			score+=2;
+		}
+		if (inet->daddr) {
+			if (inet->daddr != saddr)
+				continue;
+			score+=2;
+		}
+		if (inet->dport) {
+			if (inet->dport != sport)
+				continue;
+			score+=2;
+		}
+		if (sk->sk_bound_dev_if) {
+			if (sk->sk_bound_dev_if != dif)
+				continue;
+			score+=2;
+		}
+		if (score == 9) {
+			result = sk;
+			goto found;
+		} else if (score > best) {
+			result = sk;
+			best = score;
 		}
 	}
+
+	if (hash != hashwild) {
+		hash = hashwild;
+		goto lookup;
+	}
+found:
 	if (result)
 		sock_hold(result);
 	read_unlock(&udp_hash_lock);
 	return result;
 }
 
-static inline struct sock *udp_v4_mcast_next(struct sock *sk,
-					     __be16 loc_port, __be32 loc_addr,
+static inline struct sock *udp_v4_mcast_next(struct sock *sk, unsigned int hnum,
+					     int hport, __be32 loc_addr,
 					     __be16 rmt_port, __be32 rmt_addr,
 					     int dif)
 {
 	struct hlist_node *node;
 	struct sock *s = sk;
-	unsigned short hnum = ntohs(loc_port);
 
 	sk_for_each_from(s, node) {
 		struct inet_sock *inet = inet_sk(s);
 
 		if (s->sk_hash != hnum					||
+		    inet->num != hport					||
 		    (inet->daddr && inet->daddr != rmt_addr)		||
 		    (inet->dport != rmt_port && inet->dport)		||
 		    (inet->rcv_saddr && inet->rcv_saddr != loc_addr)	||
@@ -329,8 +396,8 @@
 	struct inet_sock *inet;
 	struct iphdr *iph = (struct iphdr*)skb->data;
 	struct udphdr *uh = (struct udphdr*)(skb->data+(iph->ihl<<2));
-	int type = skb->h.icmph->type;
-	int code = skb->h.icmph->code;
+	const int type = icmp_hdr(skb)->type;
+	const int code = icmp_hdr(skb)->code;
 	struct sock *sk;
 	int harderr;
 	int err;
@@ -390,7 +457,7 @@
 	sock_put(sk);
 }
 
-__inline__ void udp_err(struct sk_buff *skb, u32 info)
+void udp_err(struct sk_buff *skb, u32 info)
 {
 	return __udp4_lib_err(skb, info, udp_hash);
 }
@@ -419,13 +486,14 @@
 				 __be32 src, __be32 dst, int len      )
 {
 	unsigned int offset;
-	struct udphdr *uh = skb->h.uh;
+	struct udphdr *uh = udp_hdr(skb);
 	__wsum csum = 0;
 
 	if (skb_queue_len(&sk->sk_write_queue) == 1) {
 		/*
 		 * Only one fragment on the socket.
 		 */
+		skb->csum_start = skb_transport_header(skb) - skb->head;
 		skb->csum_offset = offsetof(struct udphdr, check);
 		uh->check = ~csum_tcpudp_magic(src, dst, len, IPPROTO_UDP, 0);
 	} else {
@@ -434,7 +502,7 @@
 		 * fragments on the socket so that all csums of sk_buffs
 		 * should be together
 		 */
-		offset = skb->h.raw - skb->data;
+		offset = skb_transport_offset(skb);
 		skb->csum = skb_checksum(skb, offset, skb->len - offset, 0);
 
 		skb->ip_summed = CHECKSUM_NONE;
@@ -469,7 +537,7 @@
 	/*
 	 * Create a UDP header
 	 */
-	uh = skb->h.uh;
+	uh = udp_hdr(skb);
 	uh->source = fl->fl_ip_sport;
 	uh->dest = fl->fl_ip_dport;
 	uh->len = htons(up->len);
@@ -765,38 +833,38 @@
 
 int udp_ioctl(struct sock *sk, int cmd, unsigned long arg)
 {
-	switch(cmd)
+	switch (cmd) {
+	case SIOCOUTQ:
 	{
-		case SIOCOUTQ:
-		{
-			int amount = atomic_read(&sk->sk_wmem_alloc);
-			return put_user(amount, (int __user *)arg);
-		}
-
-		case SIOCINQ:
-		{
-			struct sk_buff *skb;
-			unsigned long amount;
-
-			amount = 0;
-			spin_lock_bh(&sk->sk_receive_queue.lock);
-			skb = skb_peek(&sk->sk_receive_queue);
-			if (skb != NULL) {
-				/*
-				 * We will only return the amount
-				 * of this packet since that is all
-				 * that will be read.
-				 */
-				amount = skb->len - sizeof(struct udphdr);
-			}
-			spin_unlock_bh(&sk->sk_receive_queue.lock);
-			return put_user(amount, (int __user *)arg);
-		}
-
-		default:
-			return -ENOIOCTLCMD;
+		int amount = atomic_read(&sk->sk_wmem_alloc);
+		return put_user(amount, (int __user *)arg);
 	}
-	return(0);
+
+	case SIOCINQ:
+	{
+		struct sk_buff *skb;
+		unsigned long amount;
+
+		amount = 0;
+		spin_lock_bh(&sk->sk_receive_queue.lock);
+		skb = skb_peek(&sk->sk_receive_queue);
+		if (skb != NULL) {
+			/*
+			 * We will only return the amount
+			 * of this packet since that is all
+			 * that will be read.
+			 */
+			amount = skb->len - sizeof(struct udphdr);
+		}
+		spin_unlock_bh(&sk->sk_receive_queue.lock);
+		return put_user(amount, (int __user *)arg);
+	}
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+
+	return 0;
 }
 
 /*
@@ -810,7 +878,9 @@
 	struct inet_sock *inet = inet_sk(sk);
 	struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
 	struct sk_buff *skb;
-	int copied, err, copy_only, is_udplite = IS_UDPLITE(sk);
+	unsigned int ulen, copied;
+	int err;
+	int is_udplite = IS_UDPLITE(sk);
 
 	/*
 	 *	Check any passed addresses
@@ -826,28 +896,25 @@
 	if (!skb)
 		goto out;
 
-	copied = skb->len - sizeof(struct udphdr);
-	if (copied > len) {
-		copied = len;
+	ulen = skb->len - sizeof(struct udphdr);
+	copied = len;
+	if (copied > ulen)
+		copied = ulen;
+	else if (copied < ulen)
 		msg->msg_flags |= MSG_TRUNC;
-	}
 
 	/*
-	 * 	Decide whether to checksum and/or copy data.
-	 *
-	 * 	UDP:      checksum may have been computed in HW,
-	 * 	          (re-)compute it if message is truncated.
-	 * 	UDP-Lite: always needs to checksum, no HW support.
+	 * If checksum is needed at all, try to do it while copying the
+	 * data.  If the data is truncated, or if we only want a partial
+	 * coverage checksum (UDP-Lite), do it before the copy.
 	 */
-	copy_only = (skb->ip_summed==CHECKSUM_UNNECESSARY);
 
-	if (is_udplite  ||  (!copy_only  &&  msg->msg_flags&MSG_TRUNC)) {
-		if (__udp_lib_checksum_complete(skb))
+	if (copied < ulen || UDP_SKB_CB(skb)->partial_cov) {
+		if (udp_lib_checksum_complete(skb))
 			goto csum_copy_err;
-		copy_only = 1;
 	}
 
-	if (copy_only)
+	if (skb_csum_unnecessary(skb))
 		err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr),
 					      msg->msg_iov, copied       );
 	else {
@@ -866,8 +933,8 @@
 	if (sin)
 	{
 		sin->sin_family = AF_INET;
-		sin->sin_port = skb->h.uh->source;
-		sin->sin_addr.s_addr = skb->nh.iph->saddr;
+		sin->sin_port = udp_hdr(skb)->source;
+		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
 		memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
 	}
 	if (inet->cmsg_flags)
@@ -875,7 +942,7 @@
 
 	err = copied;
 	if (flags & MSG_TRUNC)
-		err = skb->len - sizeof(struct udphdr);
+		err = ulen;
 
 out_free:
 	skb_free_datagram(sk, skb);
@@ -949,7 +1016,7 @@
 		return 1;
 
 	/* Now we can get the pointers */
-	uh = skb->h.uh;
+	uh = udp_hdr(skb);
 	udpdata = (__u8 *)uh + sizeof(struct udphdr);
 	udpdata32 = (__be32 *)udpdata;
 
@@ -959,7 +1026,7 @@
 		/* Check if this is a keepalive packet.  If so, eat it. */
 		if (len == 1 && udpdata[0] == 0xff) {
 			return 0;
-		} else if (len > sizeof(struct ip_esp_hdr) && udpdata32[0] != 0 ) {
+		} else if (len > sizeof(struct ip_esp_hdr) && udpdata32[0] != 0) {
 			/* ESP Packet without Non-ESP header */
 			len = sizeof(struct udphdr);
 		} else
@@ -990,7 +1057,7 @@
 		return 0;
 
 	/* Now we can update and verify the packet length... */
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 	iphlen = iph->ihl << 2;
 	iph->tot_len = htons(ntohs(iph->tot_len) - len);
 	if (skb->len < iphlen + len) {
@@ -1002,7 +1069,8 @@
 	 * transport header to point to ESP.  Keep UDP on the stack
 	 * for later.
 	 */
-	skb->h.raw = skb_pull(skb, len);
+	__skb_pull(skb, len);
+	skb_reset_transport_header(skb);
 
 	/* modify the protocol (it's ESP!) */
 	iph->protocol = IPPROTO_ESP;
@@ -1095,10 +1163,9 @@
 		}
 	}
 
-	if (sk->sk_filter && skb->ip_summed != CHECKSUM_UNNECESSARY) {
-		if (__udp_lib_checksum_complete(skb))
+	if (sk->sk_filter) {
+		if (udp_lib_checksum_complete(skb))
 			goto drop;
-		skb->ip_summed = CHECKSUM_UNNECESSARY;
 	}
 
 	if ((rc = sock_queue_rcv_skb(sk,skb)) < 0) {
@@ -1128,33 +1195,49 @@
 				    __be32 saddr, __be32 daddr,
 				    struct hlist_head udptable[])
 {
-	struct sock *sk;
+	struct sock *sk, *skw, *sknext;
 	int dif;
+	int hport = ntohs(uh->dest);
+	unsigned int hash = hash_port_and_addr(hport, daddr);
+	unsigned int hashwild = hash_port_and_addr(hport, 0);
+
+	dif = skb->dev->ifindex;
 
 	read_lock(&udp_hash_lock);
-	sk = sk_head(&udptable[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)]);
-	dif = skb->dev->ifindex;
-	sk = udp_v4_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif);
-	if (sk) {
-		struct sock *sknext = NULL;
 
+	sk = sk_head(&udptable[hash & (UDP_HTABLE_SIZE - 1)]);
+	skw = sk_head(&udptable[hashwild & (UDP_HTABLE_SIZE - 1)]);
+
+	sk = udp_v4_mcast_next(sk, hash, hport, daddr, uh->source, saddr, dif);
+	if (!sk) {
+		hash = hashwild;
+		sk = udp_v4_mcast_next(skw, hash, hport, daddr, uh->source,
+			saddr, dif);
+	}
+	if (sk) {
 		do {
 			struct sk_buff *skb1 = skb;
-
-			sknext = udp_v4_mcast_next(sk_next(sk), uh->dest, daddr,
-						   uh->source, saddr, dif);
-			if(sknext)
+			sknext = udp_v4_mcast_next(sk_next(sk), hash, hport,
+						daddr, uh->source, saddr, dif);
+			if (!sknext && hash != hashwild) {
+				hash = hashwild;
+				sknext = udp_v4_mcast_next(skw, hash, hport,
+					daddr, uh->source, saddr, dif);
+			}
+			if (sknext)
 				skb1 = skb_clone(skb, GFP_ATOMIC);
 
-			if(skb1) {
+			if (skb1) {
 				int ret = udp_queue_rcv_skb(sk, skb1);
 				if (ret > 0)
-					/* we should probably re-process instead
-					 * of dropping packets here. */
+					/*
+					 * we should probably re-process
+					 * instead of dropping packets here.
+					 */
 					kfree_skb(skb1);
 			}
 			sk = sknext;
-		} while(sknext);
+		} while (sknext);
 	} else
 		kfree_skb(skb);
 	read_unlock(&udp_hash_lock);
@@ -1166,25 +1249,37 @@
  * Otherwise, csum completion requires chacksumming packet body,
  * including udp header and folding it to skb->csum.
  */
-static inline void udp4_csum_init(struct sk_buff *skb, struct udphdr *uh)
+static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh,
+				 int proto)
 {
+	const struct iphdr *iph;
+	int err;
+
+	UDP_SKB_CB(skb)->partial_cov = 0;
+	UDP_SKB_CB(skb)->cscov = skb->len;
+
+	if (proto == IPPROTO_UDPLITE) {
+		err = udplite_checksum_init(skb, uh);
+		if (err)
+			return err;
+	}
+
+	iph = ip_hdr(skb);
 	if (uh->check == 0) {
 		skb->ip_summed = CHECKSUM_UNNECESSARY;
 	} else if (skb->ip_summed == CHECKSUM_COMPLETE) {
-	       if (!csum_tcpudp_magic(skb->nh.iph->saddr, skb->nh.iph->daddr,
-				      skb->len, IPPROTO_UDP, skb->csum       ))
+	       if (!csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len,
+				      proto, skb->csum))
 			skb->ip_summed = CHECKSUM_UNNECESSARY;
 	}
-	if (skb->ip_summed != CHECKSUM_UNNECESSARY)
-		skb->csum = csum_tcpudp_nofold(skb->nh.iph->saddr,
-					       skb->nh.iph->daddr,
-					       skb->len, IPPROTO_UDP, 0);
+	if (!skb_csum_unnecessary(skb))
+		skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
+					       skb->len, proto, 0);
 	/* Probably, we should checksum udp header (it should be in cache
 	 * in any case) and data in tiny packets (< rx copybreak).
 	 */
 
-	/* UDP = UDP-Lite with a non-partial checksum coverage */
-	UDP_SKB_CB(skb)->partial_cov = 0;
+	return 0;
 }
 
 /*
@@ -1192,14 +1287,14 @@
  */
 
 int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[],
-		   int is_udplite)
+		   int proto)
 {
 	struct sock *sk;
-	struct udphdr *uh = skb->h.uh;
+	struct udphdr *uh = udp_hdr(skb);
 	unsigned short ulen;
 	struct rtable *rt = (struct rtable*)skb->dst;
-	__be32 saddr = skb->nh.iph->saddr;
-	__be32 daddr = skb->nh.iph->daddr;
+	__be32 saddr = ip_hdr(skb)->saddr;
+	__be32 daddr = ip_hdr(skb)->daddr;
 
 	/*
 	 *  Validate the packet.
@@ -1211,24 +1306,21 @@
 	if (ulen > skb->len)
 		goto short_packet;
 
-	if(! is_udplite ) {		/* UDP validates ulen. */
-
+	if (proto == IPPROTO_UDP) {
+		/* UDP validates ulen. */
 		if (ulen < sizeof(*uh) || pskb_trim_rcsum(skb, ulen))
 			goto short_packet;
-		uh = skb->h.uh;
-
-		udp4_csum_init(skb, uh);
-
-	} else 	{			/* UDP-Lite validates cscov. */
-		if (udplite4_csum_init(skb, uh))
-			goto csum_error;
+		uh = udp_hdr(skb);
 	}
 
-	if(rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
+	if (udp4_csum_init(skb, uh, proto))
+		goto csum_error;
+
+	if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
 		return __udp4_lib_mcast_deliver(skb, uh, saddr, daddr, udptable);
 
 	sk = __udp4_lib_lookup(saddr, uh->source, daddr, uh->dest,
-			       skb->dev->ifindex, udptable        );
+			       skb->dev->ifindex, udptable);
 
 	if (sk != NULL) {
 		int ret = udp_queue_rcv_skb(sk, skb);
@@ -1250,7 +1342,7 @@
 	if (udp_lib_checksum_complete(skb))
 		goto csum_error;
 
-	UDP_INC_STATS_BH(UDP_MIB_NOPORTS, is_udplite);
+	UDP_INC_STATS_BH(UDP_MIB_NOPORTS, proto == IPPROTO_UDPLITE);
 	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
 
 	/*
@@ -1258,11 +1350,11 @@
 	 * don't wanna listen.  Ignore it.
 	 */
 	kfree_skb(skb);
-	return(0);
+	return 0;
 
 short_packet:
 	LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: short packet: From %u.%u.%u.%u:%u %d/%d to %u.%u.%u.%u:%u\n",
-		       is_udplite? "-Lite" : "",
+		       proto == IPPROTO_UDPLITE ? "-Lite" : "",
 		       NIPQUAD(saddr),
 		       ntohs(uh->source),
 		       ulen,
@@ -1277,21 +1369,21 @@
 	 * the network is concerned, anyway) as per 4.1.3.4 (MUST).
 	 */
 	LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: bad checksum. From %d.%d.%d.%d:%d to %d.%d.%d.%d:%d ulen %d\n",
-		       is_udplite? "-Lite" : "",
+		       proto == IPPROTO_UDPLITE ? "-Lite" : "",
 		       NIPQUAD(saddr),
 		       ntohs(uh->source),
 		       NIPQUAD(daddr),
 		       ntohs(uh->dest),
 		       ulen);
 drop:
-	UDP_INC_STATS_BH(UDP_MIB_INERRORS, is_udplite);
+	UDP_INC_STATS_BH(UDP_MIB_INERRORS, proto == IPPROTO_UDPLITE);
 	kfree_skb(skb);
-	return(0);
+	return 0;
 }
 
-__inline__ int udp_rcv(struct sk_buff *skb)
+int udp_rcv(struct sk_buff *skb)
 {
-	return __udp4_lib_rcv(skb, udp_hash, 0);
+	return __udp4_lib_rcv(skb, udp_hash, IPPROTO_UDP);
 }
 
 int udp_destroy_sock(struct sock *sk)
@@ -1313,13 +1405,13 @@
 	int val;
 	int err = 0;
 
-	if(optlen<sizeof(int))
+	if (optlen<sizeof(int))
 		return -EINVAL;
 
 	if (get_user(val, (int __user *)optval))
 		return -EFAULT;
 
-	switch(optname) {
+	switch (optname) {
 	case UDP_CORK:
 		if (val != 0) {
 			up->corkflag = 1;
@@ -1373,7 +1465,7 @@
 	default:
 		err = -ENOPROTOOPT;
 		break;
-	};
+	}
 
 	return err;
 }
@@ -1404,15 +1496,15 @@
 	struct udp_sock *up = udp_sk(sk);
 	int val, len;
 
-	if(get_user(len,optlen))
+	if (get_user(len,optlen))
 		return -EFAULT;
 
 	len = min_t(unsigned int, len, sizeof(int));
 
-	if(len < 0)
+	if (len < 0)
 		return -EINVAL;
 
-	switch(optname) {
+	switch (optname) {
 	case UDP_CORK:
 		val = up->corkflag;
 		break;
@@ -1433,11 +1525,11 @@
 
 	default:
 		return -ENOPROTOOPT;
-	};
+	}
 
-	if(put_user(len, optlen))
+	if (put_user(len, optlen))
 		return -EFAULT;
-	if(copy_to_user(optval, &val,len))
+	if (copy_to_user(optval, &val,len))
 		return -EFAULT;
 	return 0;
 }
@@ -1486,15 +1578,11 @@
 		struct sk_buff *skb;
 
 		spin_lock_bh(&rcvq->lock);
-		while ((skb = skb_peek(rcvq)) != NULL) {
-			if (udp_lib_checksum_complete(skb)) {
-				UDP_INC_STATS_BH(UDP_MIB_INERRORS, is_lite);
-				__skb_unlink(skb, rcvq);
-				kfree_skb(skb);
-			} else {
-				skb->ip_summed = CHECKSUM_UNNECESSARY;
-				break;
-			}
+		while ((skb = skb_peek(rcvq)) != NULL &&
+		       udp_lib_checksum_complete(skb)) {
+			UDP_INC_STATS_BH(UDP_MIB_INERRORS, is_lite);
+			__skb_unlink(skb, rcvq);
+			kfree_skb(skb);
 		}
 		spin_unlock_bh(&rcvq->lock);
 
@@ -1573,7 +1661,7 @@
 	struct sock *sk = udp_get_first(seq);
 
 	if (sk)
-		while(pos && (sk = udp_get_next(seq, sk)) != NULL)
+		while (pos && (sk = udp_get_next(seq, sk)) != NULL)
 			--pos;
 	return pos ? NULL : sk;
 }
diff --git a/net/ipv4/udplite.c b/net/ipv4/udplite.c
index b28fe1e..f34fd68 100644
--- a/net/ipv4/udplite.c
+++ b/net/ipv4/udplite.c
@@ -31,7 +31,7 @@
 
 static int udplite_rcv(struct sk_buff *skb)
 {
-	return __udp4_lib_rcv(skb, udplite_hash, 1);
+	return __udp4_lib_rcv(skb, udplite_hash, IPPROTO_UDPLITE);
 }
 
 static void udplite_err(struct sk_buff *skb, u32 info)
diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c
index 78e80de..5ceca95 100644
--- a/net/ipv4/xfrm4_input.c
+++ b/net/ipv4/xfrm4_input.c
@@ -28,7 +28,7 @@
 	switch (nexthdr) {
 	case IPPROTO_IPIP:
 	case IPPROTO_IPV6:
-		*spi = skb->nh.iph->saddr;
+		*spi = ip_hdr(skb)->saddr;
 		*seq = 0;
 		return 0;
 	}
@@ -39,9 +39,9 @@
 #ifdef CONFIG_NETFILTER
 static inline int xfrm4_rcv_encap_finish(struct sk_buff *skb)
 {
-	struct iphdr *iph = skb->nh.iph;
-
 	if (skb->dst == NULL) {
+		const struct iphdr *iph = ip_hdr(skb);
+
 		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
 				   skb->dev))
 			goto drop;
@@ -55,18 +55,18 @@
 
 int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type)
 {
-	int err;
 	__be32 spi, seq;
 	struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH];
 	struct xfrm_state *x;
 	int xfrm_nr = 0;
 	int decaps = 0;
+	int err = xfrm4_parse_spi(skb, ip_hdr(skb)->protocol, &spi, &seq);
 
-	if ((err = xfrm4_parse_spi(skb, skb->nh.iph->protocol, &spi, &seq)) != 0)
+	if (err != 0)
 		goto drop;
 
 	do {
-		struct iphdr *iph = skb->nh.iph;
+		const struct iphdr *iph = ip_hdr(skb);
 
 		if (xfrm_nr == XFRM_MAX_DEPTH)
 			goto drop;
@@ -113,7 +113,8 @@
 			break;
 		}
 
-		if ((err = xfrm_parse_spi(skb, skb->nh.iph->protocol, &spi, &seq)) < 0)
+		err = xfrm_parse_spi(skb, ip_hdr(skb)->protocol, &spi, &seq);
+		if (err < 0)
 			goto drop;
 	} while (!err);
 
@@ -146,15 +147,15 @@
 		return 0;
 	} else {
 #ifdef CONFIG_NETFILTER
-		__skb_push(skb, skb->data - skb->nh.raw);
-		skb->nh.iph->tot_len = htons(skb->len);
-		ip_send_check(skb->nh.iph);
+		__skb_push(skb, skb->data - skb_network_header(skb));
+		ip_hdr(skb)->tot_len = htons(skb->len);
+		ip_send_check(ip_hdr(skb));
 
 		NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
 			xfrm4_rcv_encap_finish);
 		return 0;
 #else
-		return -skb->nh.iph->protocol;
+		return -ip_hdr(skb)->protocol;
 #endif
 	}
 
diff --git a/net/ipv4/xfrm4_mode_beet.c b/net/ipv4/xfrm4_mode_beet.c
index d419e15..a73e710 100644
--- a/net/ipv4/xfrm4_mode_beet.c
+++ b/net/ipv4/xfrm4_mode_beet.c
@@ -29,20 +29,21 @@
  */
 static int xfrm4_beet_output(struct xfrm_state *x, struct sk_buff *skb)
 {
-	struct iphdr *iph, *top_iph = NULL;
+	struct iphdr *iph, *top_iph;
 	int hdrlen, optlen;
 
-	iph = skb->nh.iph;
-	skb->h.ipiph = iph;
+	iph = ip_hdr(skb);
+	skb->transport_header = skb->network_header;
 
 	hdrlen = 0;
 	optlen = iph->ihl * 4 - sizeof(*iph);
 	if (unlikely(optlen))
 		hdrlen += IPV4_BEET_PHMAXLEN - (optlen & 4);
 
-	skb->nh.raw = skb_push(skb, x->props.header_len + hdrlen);
-	top_iph = skb->nh.iph;
-	skb->h.raw += sizeof(*iph) - hdrlen;
+	skb_push(skb, x->props.header_len - IPV4_BEET_PHMAXLEN + hdrlen);
+	skb_reset_network_header(skb);
+	top_iph = ip_hdr(skb);
+	skb->transport_header += sizeof(*iph) - hdrlen;
 
 	memmove(top_iph, iph, sizeof(*iph));
 	if (unlikely(optlen)) {
@@ -50,7 +51,7 @@
 
 		BUG_ON(optlen < 0);
 
-		ph = (struct ip_beet_phdr *)skb->h.raw;
+		ph = (struct ip_beet_phdr *)skb_transport_header(skb);
 		ph->padlen = 4 - (optlen & 4);
 		ph->hdrlen = optlen / 8;
 		ph->nexthdr = top_iph->protocol;
@@ -69,20 +70,18 @@
 
 static int xfrm4_beet_input(struct xfrm_state *x, struct sk_buff *skb)
 {
-	struct iphdr *iph = skb->nh.iph;
+	struct iphdr *iph = ip_hdr(skb);
 	int phlen = 0;
 	int optlen = 0;
-	__u8 ph_nexthdr = 0, protocol = 0;
+	u8 ph_nexthdr = 0;
 	int err = -EINVAL;
 
-	protocol = iph->protocol;
-
 	if (unlikely(iph->protocol == IPPROTO_BEETPH)) {
 		struct ip_beet_phdr *ph;
 
 		if (!pskb_may_pull(skb, sizeof(*ph)))
 			goto out;
-		ph = (struct ip_beet_phdr *)(skb->h.ipiph + 1);
+		ph = (struct ip_beet_phdr *)(ipip_hdr(skb) + 1);
 
 		phlen = sizeof(*ph) + ph->padlen;
 		optlen = ph->hdrlen * 8 + (IPV4_BEET_PHMAXLEN - phlen);
@@ -96,22 +95,20 @@
 		ph_nexthdr = ph->nexthdr;
 	}
 
-	skb->nh.raw = skb->data + (phlen - sizeof(*iph));
-	memmove(skb->nh.raw, iph, sizeof(*iph));
-	skb->h.raw = skb->data + (phlen + optlen);
-	skb->data = skb->h.raw;
+	skb_set_network_header(skb, phlen - sizeof(*iph));
+	memmove(skb_network_header(skb), iph, sizeof(*iph));
+	skb_set_transport_header(skb, phlen + optlen);
+	skb->data = skb_transport_header(skb);
 
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 	iph->ihl = (sizeof(*iph) + optlen) / 4;
 	iph->tot_len = htons(skb->len + iph->ihl * 4);
 	iph->daddr = x->sel.daddr.a4;
 	iph->saddr = x->sel.saddr.a4;
 	if (ph_nexthdr)
 		iph->protocol = ph_nexthdr;
-	else
-		iph->protocol = protocol;
 	iph->check = 0;
-	iph->check = ip_fast_csum(skb->nh.raw, iph->ihl);
+	iph->check = ip_fast_csum(skb_network_header(skb), iph->ihl);
 	err = 0;
 out:
 	return err;
diff --git a/net/ipv4/xfrm4_mode_transport.c b/net/ipv4/xfrm4_mode_transport.c
index 92676b7..6010471 100644
--- a/net/ipv4/xfrm4_mode_transport.c
+++ b/net/ipv4/xfrm4_mode_transport.c
@@ -23,16 +23,13 @@
  */
 static int xfrm4_transport_output(struct xfrm_state *x, struct sk_buff *skb)
 {
-	struct iphdr *iph;
-	int ihl;
+	struct iphdr *iph = ip_hdr(skb);
+	int ihl = iph->ihl * 4;
 
-	iph = skb->nh.iph;
-	skb->h.ipiph = iph;
-
-	ihl = iph->ihl * 4;
-	skb->h.raw += ihl;
-
-	skb->nh.raw = memmove(skb_push(skb, x->props.header_len), iph, ihl);
+	skb->transport_header = skb->network_header + ihl;
+	skb_push(skb, x->props.header_len);
+	skb_reset_network_header(skb);
+	memmove(skb_network_header(skb), iph, ihl);
 	return 0;
 }
 
@@ -46,12 +43,15 @@
  */
 static int xfrm4_transport_input(struct xfrm_state *x, struct sk_buff *skb)
 {
-	int ihl = skb->data - skb->h.raw;
+	int ihl = skb->data - skb_transport_header(skb);
 
-	if (skb->h.raw != skb->nh.raw)
-		skb->nh.raw = memmove(skb->h.raw, skb->nh.raw, ihl);
-	skb->nh.iph->tot_len = htons(skb->len + ihl);
-	skb->h.raw = skb->data;
+	if (skb->transport_header != skb->network_header) {
+		memmove(skb_transport_header(skb),
+			skb_network_header(skb), ihl);
+		skb->network_header = skb->transport_header;
+	}
+	ip_hdr(skb)->tot_len = htons(skb->len + ihl);
+	skb_reset_transport_header(skb);
 	return 0;
 }
 
diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c
index ceb4376..a2f2e6a 100644
--- a/net/ipv4/xfrm4_mode_tunnel.c
+++ b/net/ipv4/xfrm4_mode_tunnel.c
@@ -16,8 +16,8 @@
 
 static inline void ipip_ecn_decapsulate(struct sk_buff *skb)
 {
-	struct iphdr *outer_iph = skb->nh.iph;
-	struct iphdr *inner_iph = skb->h.ipiph;
+	struct iphdr *outer_iph = ip_hdr(skb);
+	struct iphdr *inner_iph = ipip_hdr(skb);
 
 	if (INET_ECN_is_ce(outer_iph->tos))
 		IP_ECN_set_ce(inner_iph);
@@ -26,7 +26,7 @@
 static inline void ipip6_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb)
 {
 	if (INET_ECN_is_ce(iph->tos))
-		IP6_ECN_set_ce(skb->nh.ipv6h);
+		IP6_ECN_set_ce(ipv6_hdr(skb));
 }
 
 /* Add encapsulation header.
@@ -46,11 +46,12 @@
 	struct iphdr *iph, *top_iph;
 	int flags;
 
-	iph = skb->nh.iph;
-	skb->h.ipiph = iph;
+	iph = ip_hdr(skb);
+	skb->transport_header = skb->network_header;
 
-	skb->nh.raw = skb_push(skb, x->props.header_len);
-	top_iph = skb->nh.iph;
+	skb_push(skb, x->props.header_len);
+	skb_reset_network_header(skb);
+	top_iph = ip_hdr(skb);
 
 	top_iph->ihl = 5;
 	top_iph->version = 4;
@@ -90,10 +91,11 @@
 
 static int xfrm4_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
 {
-	struct iphdr *iph = skb->nh.iph;
+	struct iphdr *iph = ip_hdr(skb);
+	const unsigned char *old_mac;
 	int err = -EINVAL;
 
-	switch(iph->protocol){
+	switch (iph->protocol){
 		case IPPROTO_IPIP:
 			break;
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
@@ -111,10 +113,10 @@
 	    (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
 		goto out;
 
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 	if (iph->protocol == IPPROTO_IPIP) {
 		if (x->props.flags & XFRM_STATE_DECAP_DSCP)
-			ipv4_copy_dscp(iph, skb->h.ipiph);
+			ipv4_copy_dscp(iph, ipip_hdr(skb));
 		if (!(x->props.flags & XFRM_STATE_NOECN))
 			ipip_ecn_decapsulate(skb);
 	}
@@ -125,9 +127,10 @@
 		skb->protocol = htons(ETH_P_IPV6);
 	}
 #endif
-	skb->mac.raw = memmove(skb->data - skb->mac_len,
-			       skb->mac.raw, skb->mac_len);
-	skb->nh.raw = skb->data;
+	old_mac = skb_mac_header(skb);
+	skb_set_mac_header(skb, -skb->mac_len);
+	memmove(skb_mac_header(skb), old_mac, skb->mac_len);
+	skb_reset_network_header(skb);
 	err = 0;
 
 out:
diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c
index 038ca16..44ef208 100644
--- a/net/ipv4/xfrm4_output.c
+++ b/net/ipv4/xfrm4_output.c
@@ -22,14 +22,13 @@
 {
 	int mtu, ret = 0;
 	struct dst_entry *dst;
-	struct iphdr *iph = skb->nh.iph;
 
 	if (IPCB(skb)->flags & IPSKB_XFRM_TUNNEL_SIZE)
 		goto out;
 
 	IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE;
 
-	if (!(iph->frag_off & htons(IP_DF)) || skb->local_df)
+	if (!(ip_hdr(skb)->frag_off & htons(IP_DF)) || skb->local_df)
 		goto out;
 
 	dst = skb->dst;
diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
index 5d51a2a..4ff8ed3 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -119,7 +119,7 @@
 
 		if (xfrm[i]->props.mode == XFRM_MODE_TUNNEL) {
 			unsigned short encap_family = xfrm[i]->props.family;
-			switch(encap_family) {
+			switch (encap_family) {
 			case AF_INET:
 				fl_tunnel.fl4_dst = xfrm[i]->id.daddr.a4;
 				fl_tunnel.fl4_src = xfrm[i]->props.saddr.a4;
@@ -209,8 +209,8 @@
 static void
 _decode_session4(struct sk_buff *skb, struct flowi *fl)
 {
-	struct iphdr *iph = skb->nh.iph;
-	u8 *xprth = skb->nh.raw + iph->ihl*4;
+	struct iphdr *iph = ip_hdr(skb);
+	u8 *xprth = skb_network_header(skb) + iph->ihl * 4;
 
 	memset(fl, 0, sizeof(struct flowi));
 	if (!(iph->frag_off & htons(IP_MF | IP_OFFSET))) {
@@ -263,7 +263,7 @@
 		default:
 			fl->fl_ipsec_spi = 0;
 			break;
-		};
+		}
 	}
 	fl->proto = iph->protocol;
 	fl->fl4_dst = iph->daddr;
diff --git a/net/ipv4/xfrm4_tunnel.c b/net/ipv4/xfrm4_tunnel.c
index 3eef064..5685103 100644
--- a/net/ipv4/xfrm4_tunnel.c
+++ b/net/ipv4/xfrm4_tunnel.c
@@ -12,9 +12,8 @@
 
 static int ipip_output(struct xfrm_state *x, struct sk_buff *skb)
 {
-	struct iphdr *iph;
+	struct iphdr *iph = ip_hdr(skb);
 
-	iph = skb->nh.iph;
 	iph->tot_len = htons(skb->len);
 	ip_send_check(iph);
 
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index 79682ef..8e5d54f 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -57,6 +57,16 @@
 
 	  If unsure, say N.
 
+config IPV6_OPTIMISTIC_DAD
+	bool "IPv6: Enable RFC 4429 Optimistic DAD (EXPERIMENTAL)"
+	depends on IPV6 && EXPERIMENTAL
+	---help---
+	  This is experimental support for optimistic Duplicate
+	  Address Detection.  It allows for autoconfigured addresses
+	  to be used more quickly.
+
+	  If unsure, say N.
+
 config INET6_AH
 	tristate "IPv6: AH transformation"
 	depends on IPV6
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index d460017..bb3330904 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -7,14 +7,15 @@
 ipv6-objs :=	af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \
 		route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \
 		raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
-		exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \
-		ip6_flowlabel.o ipv6_syms.o inet6_connection_sock.o
+		exthdrs.o sysctl_net_ipv6.o datagram.o \
+		ip6_flowlabel.o inet6_connection_sock.o
 
 ipv6-$(CONFIG_XFRM) += xfrm6_policy.o xfrm6_state.o xfrm6_input.o \
 	xfrm6_output.o
 ipv6-$(CONFIG_NETFILTER) += netfilter.o
 ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o
 ipv6-$(CONFIG_IPV6_MIP6) += mip6.o
+ipv6-$(CONFIG_PROC_FS) += proc.o
 
 ipv6-objs += $(ipv6-y)
 
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 452a82c..3452433 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -81,6 +81,7 @@
 #endif
 
 #include <asm/uaccess.h>
+#include <asm/unaligned.h>
 
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
@@ -208,9 +209,7 @@
 };
 
 /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
-#if 0
 const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
-#endif
 const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
 
 static void addrconf_del_timer(struct inet6_ifaddr *ifp)
@@ -246,6 +245,37 @@
 	add_timer(&ifp->timer);
 }
 
+static int snmp6_alloc_dev(struct inet6_dev *idev)
+{
+	int err = -ENOMEM;
+
+	if (!idev || !idev->dev)
+		return -EINVAL;
+
+	if (snmp_mib_init((void **)idev->stats.ipv6,
+			  sizeof(struct ipstats_mib),
+			  __alignof__(struct ipstats_mib)) < 0)
+		goto err_ip;
+	if (snmp_mib_init((void **)idev->stats.icmpv6,
+			  sizeof(struct icmpv6_mib),
+			  __alignof__(struct icmpv6_mib)) < 0)
+		goto err_icmp;
+
+	return 0;
+
+err_icmp:
+	snmp_mib_free((void **)idev->stats.ipv6);
+err_ip:
+	return err;
+}
+
+static int snmp6_free_dev(struct inet6_dev *idev)
+{
+	snmp_mib_free((void **)idev->stats.icmpv6);
+	snmp_mib_free((void **)idev->stats.ipv6);
+	return 0;
+}
+
 /* Nobody refers to this device, we may destroy it. */
 
 static void in6_dev_finish_destroy_rcu(struct rcu_head *head)
@@ -271,6 +301,8 @@
 	call_rcu(&idev->rcu, in6_dev_finish_destroy_rcu);
 }
 
+EXPORT_SYMBOL(in6_dev_finish_destroy);
+
 static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
 {
 	struct inet6_dev *ndev;
@@ -528,6 +560,16 @@
 
 	ifa->rt = rt;
 
+	/*
+	 * part one of RFC 4429, section 3.3
+	 * We should not configure an address as
+	 * optimistic if we do not yet know the link
+	 * layer address of our nexhop router
+	 */
+
+	if (rt->rt6i_nexthop == NULL)
+		ifa->flags &= ~IFA_F_OPTIMISTIC;
+
 	ifa->idev = idev;
 	in6_dev_hold(idev);
 	/* For caller */
@@ -704,6 +746,7 @@
 	int tmp_plen;
 	int ret = 0;
 	int max_addresses;
+	u32 addr_flags;
 
 	write_lock(&idev->lock);
 	if (ift) {
@@ -761,10 +804,17 @@
 	spin_unlock_bh(&ifp->lock);
 
 	write_unlock(&idev->lock);
+
+	addr_flags = IFA_F_TEMPORARY;
+	/* set in addrconf_prefix_rcv() */
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		addr_flags |= IFA_F_OPTIMISTIC;
+
 	ift = !max_addresses ||
 	      ipv6_count_addresses(idev) < max_addresses ?
 		ipv6_add_addr(idev, &addr, tmp_plen,
-			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
+			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK,
+			      addr_flags) : NULL;
 	if (!ift || IS_ERR(ift)) {
 		in6_ifa_put(ifp);
 		in6_dev_put(idev);
@@ -896,13 +946,14 @@
 			 * - Tentative Address (RFC2462 section 5.4)
 			 *  - A tentative address is not considered
 			 *    "assigned to an interface" in the traditional
-			 *    sense.
+			 *    sense, unless it is also flagged as optimistic.
 			 * - Candidate Source Address (section 4)
 			 *  - In any case, anycast addresses, multicast
 			 *    addresses, and the unspecified address MUST
 			 *    NOT be included in a candidate set.
 			 */
-			if (ifa->flags & IFA_F_TENTATIVE)
+			if ((ifa->flags & IFA_F_TENTATIVE) &&
+			    (!(ifa->flags & IFA_F_OPTIMISTIC)))
 				continue;
 			if (unlikely(score.addr_type == IPV6_ADDR_ANY ||
 				     score.addr_type & IPV6_ADDR_MULTICAST)) {
@@ -961,15 +1012,17 @@
 				}
 			}
 
-			/* Rule 3: Avoid deprecated address */
+			/* Rule 3: Avoid deprecated and optimistic addresses */
 			if (hiscore.rule < 3) {
 				if (ipv6_saddr_preferred(hiscore.addr_type) ||
-				    !(ifa_result->flags & IFA_F_DEPRECATED))
+				   (((ifa_result->flags &
+				    (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0)))
 					hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED;
 				hiscore.rule++;
 			}
 			if (ipv6_saddr_preferred(score.addr_type) ||
-			    !(ifa->flags & IFA_F_DEPRECATED)) {
+			   (((ifa_result->flags &
+			    (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0))) {
 				score.attrs |= IPV6_SADDR_SCORE_PREFERRED;
 				if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) {
 					score.rule = 3;
@@ -1107,8 +1160,10 @@
 	return ipv6_dev_get_saddr(dst ? ip6_dst_idev(dst)->dev : NULL, daddr, saddr);
 }
 
+EXPORT_SYMBOL(ipv6_get_saddr);
 
-int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
+int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
+		    unsigned char banned_flags)
 {
 	struct inet6_dev *idev;
 	int err = -EADDRNOTAVAIL;
@@ -1119,7 +1174,7 @@
 
 		read_lock_bh(&idev->lock);
 		for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
-			if (ifp->scope == IFA_LINK && !(ifp->flags&IFA_F_TENTATIVE)) {
+			if (ifp->scope == IFA_LINK && !(ifp->flags & banned_flags)) {
 				ipv6_addr_copy(addr, &ifp->addr);
 				err = 0;
 				break;
@@ -1161,6 +1216,8 @@
 	return ifp != NULL;
 }
 
+EXPORT_SYMBOL(ipv6_chk_addr);
+
 static
 int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev)
 {
@@ -1669,6 +1726,13 @@
 
 		if (ifp == NULL && valid_lft) {
 			int max_addresses = in6_dev->cnf.max_addresses;
+			u32 addr_flags = 0;
+
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+			if (in6_dev->cnf.optimistic_dad &&
+			    !ipv6_devconf.forwarding)
+				addr_flags = IFA_F_OPTIMISTIC;
+#endif
 
 			/* Do not allow to create too much of autoconfigured
 			 * addresses; this would be too easy way to crash kernel.
@@ -1676,7 +1740,8 @@
 			if (!max_addresses ||
 			    ipv6_count_addresses(in6_dev) < max_addresses)
 				ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len,
-						    addr_type&IPV6_ADDR_SCOPE_MASK, 0);
+						    addr_type&IPV6_ADDR_SCOPE_MASK,
+						    addr_flags);
 
 			if (!ifp || IS_ERR(ifp)) {
 				in6_dev_put(in6_dev);
@@ -1884,6 +1949,11 @@
 
 		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev,
 				      jiffies_to_clock_t(valid_lft * HZ), flags);
+		/*
+		 * Note that section 3.1 of RFC 4429 indicates
+		 * that the Optimistic flag should not be set for
+		 * manually configured addresses
+		 */
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
 		addrconf_verify(0);
@@ -2060,8 +2130,16 @@
 static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr)
 {
 	struct inet6_ifaddr * ifp;
+	u32 addr_flags = IFA_F_PERMANENT;
 
-	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	if (idev->cnf.optimistic_dad &&
+	    !ipv6_devconf.forwarding)
+		addr_flags |= IFA_F_OPTIMISTIC;
+#endif
+
+
+	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, addr_flags);
 	if (!IS_ERR(ifp)) {
 		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, idev->dev, 0, 0);
 		addrconf_dad_start(ifp, 0);
@@ -2129,7 +2207,7 @@
 {
 	struct in6_addr lladdr;
 
-	if (!ipv6_get_lladdr(link_dev, &lladdr)) {
+	if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
 		addrconf_add_linklocal(idev, &lladdr);
 		return 0;
 	}
@@ -2240,7 +2318,7 @@
 		default:
 			addrconf_dev_config(dev);
 			break;
-		};
+		}
 		if (idev) {
 			if (run_pending)
 				addrconf_dad_run(idev);
@@ -2281,8 +2359,9 @@
 		break;
 
 	case NETDEV_CHANGENAME:
-#ifdef CONFIG_SYSCTL
 		if (idev) {
+			snmp6_unregister_dev(idev);
+#ifdef CONFIG_SYSCTL
 			addrconf_sysctl_unregister(&idev->cnf);
 			neigh_sysctl_unregister(idev->nd_parms);
 			neigh_sysctl_register(dev, idev->nd_parms,
@@ -2290,10 +2369,11 @@
 					      &ndisc_ifinfo_sysctl_change,
 					      NULL);
 			addrconf_sysctl_register(idev, &idev->cnf);
-		}
 #endif
+			snmp6_register_dev(idev);
+		}
 		break;
-	};
+	}
 
 	return NOTIFY_OK;
 }
@@ -2474,7 +2554,11 @@
 	unsigned long rand_num;
 	struct inet6_dev *idev = ifp->idev;
 
-	rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		rand_num = 0;
+	else
+		rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+
 	ifp->probes = idev->cnf.dad_transmits;
 	addrconf_mod_timer(ifp, AC_DAD, rand_num);
 }
@@ -2496,7 +2580,7 @@
 	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
 	    !(ifp->flags&IFA_F_TENTATIVE) ||
 	    ifp->flags & IFA_F_NODAD) {
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -2516,6 +2600,14 @@
 		addrconf_dad_stop(ifp);
 		return;
 	}
+
+	/*
+	 * Optimistic nodes can start receiving
+	 * Frames right away
+	 */
+	if(ifp->flags & IFA_F_OPTIMISTIC)
+		ip6_ins_rt(ifp->rt);
+
 	addrconf_dad_kick(ifp);
 	spin_unlock_bh(&ifp->lock);
 out:
@@ -2540,7 +2632,7 @@
 		 * DAD was successful
 		 */
 
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -3164,7 +3256,6 @@
 
 	s_idx = cb->args[0];
 	s_ip_idx = ip_idx = cb->args[1];
-	read_lock(&dev_base_lock);
 
 	for (dev = dev_base, idx = 0; dev; dev = dev->next, idx++) {
 		if (idx < s_idx)
@@ -3226,7 +3317,6 @@
 		read_unlock_bh(&idev->lock);
 		in6_dev_put(idev);
 	}
-	read_unlock(&dev_base_lock);
 	cb->args[0] = idx;
 	cb->args[1] = ip_idx;
 	return skb->len;
@@ -3359,6 +3449,9 @@
 #endif
 	array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp;
 	array[DEVCONF_ACCEPT_SOURCE_ROUTE] = cnf->accept_source_route;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad;
+#endif
 }
 
 static inline size_t inet6_if_nlmsg_size(void)
@@ -3372,14 +3465,44 @@
 			nla_total_size(4) /* IFLA_INET6_FLAGS */
 			+ nla_total_size(sizeof(struct ifla_cacheinfo))
 			+ nla_total_size(DEVCONF_MAX * 4) /* IFLA_INET6_CONF */
+			+ nla_total_size(IPSTATS_MIB_MAX * 8) /* IFLA_INET6_STATS */
+			+ nla_total_size(ICMP6_MIB_MAX * 8) /* IFLA_INET6_ICMP6STATS */
 		 );
 }
 
+static inline void __snmp6_fill_stats(u64 *stats, void **mib, int items,
+				      int bytes)
+{
+	int i;
+	int pad = bytes - sizeof(u64) * items;
+	BUG_ON(pad < 0);
+
+	/* Use put_unaligned() because stats may not be aligned for u64. */
+	put_unaligned(items, &stats[0]);
+	for (i = 1; i < items; i++)
+		put_unaligned(snmp_fold_field(mib, i), &stats[i]);
+
+	memset(&stats[items], 0, pad);
+}
+
+static void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype,
+			     int bytes)
+{
+	switch(attrtype) {
+	case IFLA_INET6_STATS:
+		__snmp6_fill_stats(stats, (void **)idev->stats.ipv6, IPSTATS_MIB_MAX, bytes);
+		break;
+	case IFLA_INET6_ICMP6STATS:
+		__snmp6_fill_stats(stats, (void **)idev->stats.icmpv6, ICMP6_MIB_MAX, bytes);
+		break;
+	}
+}
+
 static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,
 			     u32 pid, u32 seq, int event, unsigned int flags)
 {
 	struct net_device *dev = idev->dev;
-	struct nlattr *conf;
+	struct nlattr *nla;
 	struct ifinfomsg *hdr;
 	struct nlmsghdr *nlh;
 	void *protoinfo;
@@ -3419,12 +3542,22 @@
 	ci.retrans_time = idev->nd_parms->retrans_time;
 	NLA_PUT(skb, IFLA_INET6_CACHEINFO, sizeof(ci), &ci);
 
-	conf = nla_reserve(skb, IFLA_INET6_CONF, DEVCONF_MAX * sizeof(s32));
-	if (conf == NULL)
+	nla = nla_reserve(skb, IFLA_INET6_CONF, DEVCONF_MAX * sizeof(s32));
+	if (nla == NULL)
 		goto nla_put_failure;
-	ipv6_store_devconf(&idev->cnf, nla_data(conf), nla_len(conf));
+	ipv6_store_devconf(&idev->cnf, nla_data(nla), nla_len(nla));
 
-	/* XXX - Statistics/MC not implemented */
+	/* XXX - MC not implemented */
+
+	nla = nla_reserve(skb, IFLA_INET6_STATS, IPSTATS_MIB_MAX * sizeof(u64));
+	if (nla == NULL)
+		goto nla_put_failure;
+	snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_STATS, nla_len(nla));
+
+	nla = nla_reserve(skb, IFLA_INET6_ICMP6STATS, ICMP6_MIB_MAX * sizeof(u64));
+	if (nla == NULL)
+		goto nla_put_failure;
+	snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_ICMP6STATS, nla_len(nla));
 
 	nla_nest_end(skb, protoinfo);
 	return nlmsg_end(skb, nlh);
@@ -3550,30 +3683,20 @@
 		rtnl_set_sk_err(RTNLGRP_IPV6_PREFIX, err);
 }
 
-static struct rtnetlink_link inet6_rtnetlink_table[RTM_NR_MSGTYPES] = {
-	[RTM_GETLINK - RTM_BASE] = { .dumpit	= inet6_dump_ifinfo, },
-	[RTM_NEWADDR - RTM_BASE] = { .doit	= inet6_rtm_newaddr, },
-	[RTM_DELADDR - RTM_BASE] = { .doit	= inet6_rtm_deladdr, },
-	[RTM_GETADDR - RTM_BASE] = { .doit	= inet6_rtm_getaddr,
-				     .dumpit	= inet6_dump_ifaddr, },
-	[RTM_GETMULTICAST - RTM_BASE] = { .dumpit = inet6_dump_ifmcaddr, },
-	[RTM_GETANYCAST - RTM_BASE] = { .dumpit	= inet6_dump_ifacaddr, },
-	[RTM_NEWROUTE - RTM_BASE] = { .doit	= inet6_rtm_newroute, },
-	[RTM_DELROUTE - RTM_BASE] = { .doit	= inet6_rtm_delroute, },
-	[RTM_GETROUTE - RTM_BASE] = { .doit	= inet6_rtm_getroute,
-				      .dumpit	= inet6_dump_fib, },
-#ifdef CONFIG_IPV6_MULTIPLE_TABLES
-	[RTM_GETRULE  - RTM_BASE] = { .dumpit   = fib6_rules_dump,   },
-#endif
-};
-
 static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
 {
 	inet6_ifa_notify(event ? : RTM_NEWADDR, ifp);
 
 	switch (event) {
 	case RTM_NEWADDR:
-		ip6_ins_rt(ifp->rt);
+		/*
+		 * If the address was optimistic
+		 * we inserted the route at the start of
+		 * our DAD process, so we don't need
+		 * to do it again
+		 */
+		if (!(ifp->rt->rt6i_node))
+			ip6_ins_rt(ifp->rt);
 		if (ifp->idev->cnf.forwarding)
 			addrconf_join_anycast(ifp);
 		break;
@@ -3894,6 +4017,17 @@
 			.mode		=	0644,
 			.proc_handler	=	&proc_dointvec,
 		},
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+		{
+			.ctl_name	=	CTL_UNNUMBERED,
+			.procname       =       "optimistic_dad",
+			.data           =       &ipv6_devconf.optimistic_dad,
+			.maxlen         =       sizeof(int),
+			.mode           =       0644,
+			.proc_handler   =       &proc_dointvec,
+
+		},
+#endif
 		{
 			.ctl_name	=	0,	/* sentinel */
 		}
@@ -4021,11 +4155,15 @@
 	return atomic_notifier_chain_register(&inet6addr_chain, nb);
 }
 
+EXPORT_SYMBOL(register_inet6addr_notifier);
+
 int unregister_inet6addr_notifier(struct notifier_block *nb)
 {
 	return atomic_notifier_chain_unregister(&inet6addr_chain,nb);
 }
 
+EXPORT_SYMBOL(unregister_inet6addr_notifier);
+
 /*
  *	Init / cleanup code
  */
@@ -4064,7 +4202,18 @@
 	register_netdevice_notifier(&ipv6_dev_notf);
 
 	addrconf_verify(0);
-	rtnetlink_links[PF_INET6] = inet6_rtnetlink_table;
+
+	err = __rtnl_register(PF_INET6, RTM_GETLINK, NULL, inet6_dump_ifinfo);
+	if (err < 0)
+		goto errout;
+
+	/* Only the first call to __rtnl_register can fail */
+	__rtnl_register(PF_INET6, RTM_NEWADDR, inet6_rtm_newaddr, NULL);
+	__rtnl_register(PF_INET6, RTM_DELADDR, inet6_rtm_deladdr, NULL);
+	__rtnl_register(PF_INET6, RTM_GETADDR, inet6_rtm_getaddr, inet6_dump_ifaddr);
+	__rtnl_register(PF_INET6, RTM_GETMULTICAST, NULL, inet6_dump_ifmcaddr);
+	__rtnl_register(PF_INET6, RTM_GETANYCAST, NULL, inet6_dump_ifacaddr);
+
 #ifdef CONFIG_SYSCTL
 	addrconf_sysctl.sysctl_header =
 		register_sysctl_table(addrconf_sysctl.addrconf_root_dir);
@@ -4072,6 +4221,10 @@
 #endif
 
 	return 0;
+errout:
+	unregister_netdevice_notifier(&ipv6_dev_notf);
+
+	return err;
 }
 
 void __exit addrconf_cleanup(void)
@@ -4083,7 +4236,6 @@
 
 	unregister_netdevice_notifier(&ipv6_dev_notf);
 
-	rtnetlink_links[PF_INET6] = NULL;
 #ifdef CONFIG_SYSCTL
 	addrconf_sysctl_unregister(&ipv6_devconf_dflt);
 	addrconf_sysctl_unregister(&ipv6_devconf);
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 5cac14a..18cb928 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -98,6 +98,11 @@
 	int try_loading_module = 0;
 	int err;
 
+	if (sock->type != SOCK_RAW &&
+	    sock->type != SOCK_DGRAM &&
+	    !inet_ehash_secret)
+		build_ehash_secret();
+
 	/* Look for the requested type/protocol pair. */
 	answer = NULL;
 lookup_protocol:
@@ -349,6 +354,8 @@
 	return err;
 }
 
+EXPORT_SYMBOL(inet6_bind);
+
 int inet6_release(struct socket *sock)
 {
 	struct sock *sk = sock->sk;
@@ -365,6 +372,8 @@
 	return inet_release(sock);
 }
 
+EXPORT_SYMBOL(inet6_release);
+
 int inet6_destroy_sock(struct sock *sk)
 {
 	struct ipv6_pinfo *np = inet6_sk(sk);
@@ -428,6 +437,8 @@
 	return(0);
 }
 
+EXPORT_SYMBOL(inet6_getname);
+
 int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
 	struct sock *sk = sock->sk;
@@ -437,6 +448,9 @@
 	case SIOCGSTAMP:
 		return sock_get_timestamp(sk, (struct timeval __user *)arg);
 
+	case SIOCGSTAMPNS:
+		return sock_get_timestampns(sk, (struct timespec __user *)arg);
+
 	case SIOCADDRT:
 	case SIOCDELRT:
 
@@ -457,6 +471,8 @@
 	return(0);
 }
 
+EXPORT_SYMBOL(inet6_ioctl);
+
 const struct proto_ops inet6_stream_ops = {
 	.family		   = PF_INET6,
 	.owner		   = THIS_MODULE,
@@ -603,6 +619,8 @@
 	goto out;
 }
 
+EXPORT_SYMBOL(inet6_register_protosw);
+
 void
 inet6_unregister_protosw(struct inet_protosw *p)
 {
@@ -619,6 +637,8 @@
 	}
 }
 
+EXPORT_SYMBOL(inet6_unregister_protosw);
+
 int inet6_sk_rebuild_header(struct sock *sk)
 {
 	int err;
@@ -678,7 +698,8 @@
 	if (np->rxopt.all) {
 		if ((opt->hop && (np->rxopt.bits.hopopts ||
 				  np->rxopt.bits.ohopopts)) ||
-		    ((IPV6_FLOWINFO_MASK & *(__be32*)skb->nh.raw) &&
+		    ((IPV6_FLOWINFO_MASK &
+		      *(__be32 *)skb_network_header(skb)) &&
 		     np->rxopt.bits.rxflow) ||
 		    (opt->srcrt && (np->rxopt.bits.srcrt ||
 		     np->rxopt.bits.osrcrt)) ||
@@ -691,61 +712,28 @@
 
 EXPORT_SYMBOL_GPL(ipv6_opt_accepted);
 
-int
-snmp6_mib_init(void *ptr[2], size_t mibsize, size_t mibalign)
-{
-	if (ptr == NULL)
-		return -EINVAL;
-
-	ptr[0] = __alloc_percpu(mibsize);
-	if (!ptr[0])
-		goto err0;
-
-	ptr[1] = __alloc_percpu(mibsize);
-	if (!ptr[1])
-		goto err1;
-
-	return 0;
-
-err1:
-	free_percpu(ptr[0]);
-	ptr[0] = NULL;
-err0:
-	return -ENOMEM;
-}
-
-void
-snmp6_mib_free(void *ptr[2])
-{
-	if (ptr == NULL)
-		return;
-	free_percpu(ptr[0]);
-	free_percpu(ptr[1]);
-	ptr[0] = ptr[1] = NULL;
-}
-
 static int __init init_ipv6_mibs(void)
 {
-	if (snmp6_mib_init((void **)ipv6_statistics, sizeof (struct ipstats_mib),
-			   __alignof__(struct ipstats_mib)) < 0)
+	if (snmp_mib_init((void **)ipv6_statistics, sizeof (struct ipstats_mib),
+			  __alignof__(struct ipstats_mib)) < 0)
 		goto err_ip_mib;
-	if (snmp6_mib_init((void **)icmpv6_statistics, sizeof (struct icmpv6_mib),
-			   __alignof__(struct icmpv6_mib)) < 0)
+	if (snmp_mib_init((void **)icmpv6_statistics, sizeof (struct icmpv6_mib),
+			  __alignof__(struct icmpv6_mib)) < 0)
 		goto err_icmp_mib;
-	if (snmp6_mib_init((void **)udp_stats_in6, sizeof (struct udp_mib),
-			   __alignof__(struct udp_mib)) < 0)
+	if (snmp_mib_init((void **)udp_stats_in6, sizeof (struct udp_mib),
+			  __alignof__(struct udp_mib)) < 0)
 		goto err_udp_mib;
-	if (snmp6_mib_init((void **)udplite_stats_in6, sizeof (struct udp_mib),
-			   __alignof__(struct udp_mib)) < 0)
+	if (snmp_mib_init((void **)udplite_stats_in6, sizeof (struct udp_mib),
+			  __alignof__(struct udp_mib)) < 0)
 		goto err_udplite_mib;
 	return 0;
 
 err_udplite_mib:
-	snmp6_mib_free((void **)udp_stats_in6);
+	snmp_mib_free((void **)udp_stats_in6);
 err_udp_mib:
-	snmp6_mib_free((void **)icmpv6_statistics);
+	snmp_mib_free((void **)icmpv6_statistics);
 err_icmp_mib:
-	snmp6_mib_free((void **)ipv6_statistics);
+	snmp_mib_free((void **)ipv6_statistics);
 err_ip_mib:
 	return -ENOMEM;
 
@@ -753,10 +741,10 @@
 
 static void cleanup_ipv6_mibs(void)
 {
-	snmp6_mib_free((void **)ipv6_statistics);
-	snmp6_mib_free((void **)icmpv6_statistics);
-	snmp6_mib_free((void **)udp_stats_in6);
-	snmp6_mib_free((void **)udplite_stats_in6);
+	snmp_mib_free((void **)ipv6_statistics);
+	snmp_mib_free((void **)icmpv6_statistics);
+	snmp_mib_free((void **)udp_stats_in6);
+	snmp_mib_free((void **)udplite_stats_in6);
 }
 
 static int __init inet6_init(void)
@@ -929,6 +917,8 @@
 {
 	/* First of all disallow new sockets creation. */
 	sock_unregister(PF_INET6);
+	/* Disallow any further netlink messages */
+	rtnl_unregister_all(PF_INET6);
 
 	/* Cleanup code parts. */
 	ipv6_packet_cleanup();
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c
index dc68b72..b696c84 100644
--- a/net/ipv6/ah6.c
+++ b/net/ipv6/ah6.c
@@ -238,8 +238,8 @@
 	top_iph = (struct ipv6hdr *)skb->data;
 	top_iph->payload_len = htons(skb->len - sizeof(*top_iph));
 
-	nexthdr = *skb->nh.raw;
-	*skb->nh.raw = IPPROTO_AH;
+	nexthdr = *skb_network_header(skb);
+	*skb_network_header(skb) = IPPROTO_AH;
 
 	/* When there are no extension headers, we only need to save the first
 	 * 8 bytes of the base IP header.
@@ -247,7 +247,7 @@
 	memcpy(tmp_base, top_iph, sizeof(tmp_base));
 
 	tmp_ext = NULL;
-	extlen = skb->h.raw - (unsigned char *)(top_iph + 1);
+	extlen = skb_transport_offset(skb) + sizeof(struct ipv6hdr);
 	if (extlen) {
 		extlen += sizeof(*tmp_ext);
 		tmp_ext = kmalloc(extlen, GFP_ATOMIC);
@@ -268,7 +268,7 @@
 			goto error_free_iph;
 	}
 
-	ah = (struct ip_auth_hdr *)skb->h.raw;
+	ah = (struct ip_auth_hdr *)skb_transport_header(skb);
 	ah->nexthdr = nexthdr;
 
 	top_iph->priority    = 0;
@@ -316,8 +316,8 @@
 	 *
 	 * To erase AH:
 	 * Keeping copy of cleared headers. After AH processing,
-	 * Moving the pointer of skb->nh.raw by using skb_pull as long as AH
-	 * header length. Then copy back the copy as long as hdr_len
+	 * Moving the pointer of skb->network_header by using skb_pull as long
+	 * as AH header length. Then copy back the copy as long as hdr_len
 	 * If destination header following AH exists, copy it into after [Ext2].
 	 *
 	 * |<>|[IPv6][Ext1][Ext2][Dest][Payload]
@@ -325,6 +325,7 @@
 	 */
 
 	struct ipv6_auth_hdr *ah;
+	struct ipv6hdr *ip6h;
 	struct ah_data *ahp;
 	unsigned char *tmp_hdr = NULL;
 	u16 hdr_len;
@@ -341,7 +342,7 @@
 	    pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
 		goto out;
 
-	hdr_len = skb->data - skb->nh.raw;
+	hdr_len = skb->data - skb_network_header(skb);
 	ah = (struct ipv6_auth_hdr*)skb->data;
 	ahp = x->data;
 	nexthdr = ah->nexthdr;
@@ -354,16 +355,17 @@
 	if (!pskb_may_pull(skb, ah_hlen))
 		goto out;
 
-	tmp_hdr = kmemdup(skb->nh.raw, hdr_len, GFP_ATOMIC);
+	tmp_hdr = kmemdup(skb_network_header(skb), hdr_len, GFP_ATOMIC);
 	if (!tmp_hdr)
 		goto out;
-	if (ipv6_clear_mutable_options(skb->nh.ipv6h, hdr_len, XFRM_POLICY_IN))
+	ip6h = ipv6_hdr(skb);
+	if (ipv6_clear_mutable_options(ip6h, hdr_len, XFRM_POLICY_IN))
 		goto free_out;
-	skb->nh.ipv6h->priority    = 0;
-	skb->nh.ipv6h->flow_lbl[0] = 0;
-	skb->nh.ipv6h->flow_lbl[1] = 0;
-	skb->nh.ipv6h->flow_lbl[2] = 0;
-	skb->nh.ipv6h->hop_limit   = 0;
+	ip6h->priority    = 0;
+	ip6h->flow_lbl[0] = 0;
+	ip6h->flow_lbl[1] = 0;
+	ip6h->flow_lbl[2] = 0;
+	ip6h->hop_limit   = 0;
 
 	{
 		u8 auth_data[MAX_AH_AUTH_LEN];
@@ -382,7 +384,9 @@
 		}
 	}
 
-	skb->h.raw = memcpy(skb->nh.raw += ah_hlen, tmp_hdr, hdr_len);
+	skb->network_header += ah_hlen;
+	memcpy(skb_network_header(skb), tmp_hdr, hdr_len);
+	skb->transport_header = skb->network_header;
 	__skb_pull(skb, ah_hlen + hdr_len);
 
 	kfree(tmp_hdr);
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 3b4e8dc..403eee6 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -209,7 +209,7 @@
 		     __be16 port, u32 info, u8 *payload)
 {
 	struct ipv6_pinfo *np  = inet6_sk(sk);
-	struct icmp6hdr *icmph = (struct icmp6hdr *)skb->h.raw;
+	struct icmp6hdr *icmph = icmp6_hdr(skb);
 	struct sock_exterr_skb *serr;
 
 	if (!np->recverr)
@@ -227,11 +227,12 @@
 	serr->ee.ee_pad = 0;
 	serr->ee.ee_info = info;
 	serr->ee.ee_data = 0;
-	serr->addr_offset = (u8*)&(((struct ipv6hdr*)(icmph+1))->daddr) - skb->nh.raw;
+	serr->addr_offset = (u8 *)&(((struct ipv6hdr *)(icmph + 1))->daddr) -
+				  skb_network_header(skb);
 	serr->port = port;
 
-	skb->h.raw = payload;
 	__skb_pull(skb, payload - skb->data);
+	skb_reset_transport_header(skb);
 
 	if (sock_queue_err_skb(sk, skb))
 		kfree_skb(skb);
@@ -251,8 +252,9 @@
 	if (!skb)
 		return;
 
-	iph = (struct ipv6hdr*)skb_put(skb, sizeof(struct ipv6hdr));
-	skb->nh.ipv6h = iph;
+	skb_put(skb, sizeof(struct ipv6hdr));
+	skb_reset_network_header(skb);
+	iph = ipv6_hdr(skb);
 	ipv6_addr_copy(&iph->daddr, &fl->fl6_dst);
 
 	serr = SKB_EXT_ERR(skb);
@@ -263,11 +265,11 @@
 	serr->ee.ee_pad = 0;
 	serr->ee.ee_info = info;
 	serr->ee.ee_data = 0;
-	serr->addr_offset = (u8*)&iph->daddr - skb->nh.raw;
+	serr->addr_offset = (u8 *)&iph->daddr - skb_network_header(skb);
 	serr->port = fl->fl_ip_dport;
 
-	skb->h.raw = skb->tail;
-	__skb_pull(skb, skb->tail - skb->data);
+	__skb_pull(skb, skb_tail_pointer(skb) - skb->data);
+	skb_reset_transport_header(skb);
 
 	if (sock_queue_err_skb(sk, skb))
 		kfree_skb(skb);
@@ -309,21 +311,24 @@
 
 	sin = (struct sockaddr_in6 *)msg->msg_name;
 	if (sin) {
+		const unsigned char *nh = skb_network_header(skb);
 		sin->sin6_family = AF_INET6;
 		sin->sin6_flowinfo = 0;
 		sin->sin6_port = serr->port;
 		sin->sin6_scope_id = 0;
 		if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6) {
 			ipv6_addr_copy(&sin->sin6_addr,
-			  (struct in6_addr *)(skb->nh.raw + serr->addr_offset));
+				  (struct in6_addr *)(nh + serr->addr_offset));
 			if (np->sndflow)
-				sin->sin6_flowinfo = *(__be32*)(skb->nh.raw + serr->addr_offset - 24) & IPV6_FLOWINFO_MASK;
+				sin->sin6_flowinfo =
+					(*(__be32 *)(nh + serr->addr_offset - 24) &
+					 IPV6_FLOWINFO_MASK);
 			if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL)
 				sin->sin6_scope_id = IP6CB(skb)->iif;
 		} else {
 			ipv6_addr_set(&sin->sin6_addr, 0, 0,
 				      htonl(0xffff),
-				      *(__be32*)(skb->nh.raw + serr->addr_offset));
+				      *(__be32 *)(nh + serr->addr_offset));
 		}
 	}
 
@@ -335,7 +340,7 @@
 		sin->sin6_flowinfo = 0;
 		sin->sin6_scope_id = 0;
 		if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6) {
-			ipv6_addr_copy(&sin->sin6_addr, &skb->nh.ipv6h->saddr);
+			ipv6_addr_copy(&sin->sin6_addr, &ipv6_hdr(skb)->saddr);
 			if (np->rxopt.all)
 				datagram_recv_ctl(sk, msg, skb);
 			if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL)
@@ -344,8 +349,7 @@
 			struct inet_sock *inet = inet_sk(sk);
 
 			ipv6_addr_set(&sin->sin6_addr, 0, 0,
-				      htonl(0xffff),
-				      skb->nh.iph->saddr);
+				      htonl(0xffff), ip_hdr(skb)->saddr);
 			if (inet->cmsg_flags)
 				ip_cmsg_recv(msg, skb);
 		}
@@ -381,33 +385,34 @@
 {
 	struct ipv6_pinfo *np = inet6_sk(sk);
 	struct inet6_skb_parm *opt = IP6CB(skb);
+	unsigned char *nh = skb_network_header(skb);
 
 	if (np->rxopt.bits.rxinfo) {
 		struct in6_pktinfo src_info;
 
 		src_info.ipi6_ifindex = opt->iif;
-		ipv6_addr_copy(&src_info.ipi6_addr, &skb->nh.ipv6h->daddr);
+		ipv6_addr_copy(&src_info.ipi6_addr, &ipv6_hdr(skb)->daddr);
 		put_cmsg(msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info);
 	}
 
 	if (np->rxopt.bits.rxhlim) {
-		int hlim = skb->nh.ipv6h->hop_limit;
+		int hlim = ipv6_hdr(skb)->hop_limit;
 		put_cmsg(msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
 	}
 
 	if (np->rxopt.bits.rxtclass) {
-		int tclass = (ntohl(*(__be32 *)skb->nh.ipv6h) >> 20) & 0xff;
+		int tclass = (ntohl(*(__be32 *)ipv6_hdr(skb)) >> 20) & 0xff;
 		put_cmsg(msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass);
 	}
 
-	if (np->rxopt.bits.rxflow && (*(__be32*)skb->nh.raw & IPV6_FLOWINFO_MASK)) {
-		__be32 flowinfo = *(__be32*)skb->nh.raw & IPV6_FLOWINFO_MASK;
+	if (np->rxopt.bits.rxflow && (*(__be32 *)nh & IPV6_FLOWINFO_MASK)) {
+		__be32 flowinfo = *(__be32 *)nh & IPV6_FLOWINFO_MASK;
 		put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo);
 	}
 
 	/* HbH is allowed only once */
 	if (np->rxopt.bits.hopopts && opt->hop) {
-		u8 *ptr = skb->nh.raw + opt->hop;
+		u8 *ptr = nh + opt->hop;
 		put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr);
 	}
 
@@ -423,11 +428,11 @@
 		 * IPV6_RECVDSTOPTS is more generic. --yoshfuji
 		 */
 		unsigned int off = sizeof(struct ipv6hdr);
-		u8 nexthdr = skb->nh.ipv6h->nexthdr;
+		u8 nexthdr = ipv6_hdr(skb)->nexthdr;
 
 		while (off <= opt->lastopt) {
 			unsigned len;
-			u8 *ptr = skb->nh.raw + off;
+			u8 *ptr = nh + off;
 
 			switch(nexthdr) {
 			case IPPROTO_DSTOPTS:
@@ -461,27 +466,27 @@
 		struct in6_pktinfo src_info;
 
 		src_info.ipi6_ifindex = opt->iif;
-		ipv6_addr_copy(&src_info.ipi6_addr, &skb->nh.ipv6h->daddr);
+		ipv6_addr_copy(&src_info.ipi6_addr, &ipv6_hdr(skb)->daddr);
 		put_cmsg(msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
 	}
 	if (np->rxopt.bits.rxohlim) {
-		int hlim = skb->nh.ipv6h->hop_limit;
+		int hlim = ipv6_hdr(skb)->hop_limit;
 		put_cmsg(msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim);
 	}
 	if (np->rxopt.bits.ohopopts && opt->hop) {
-		u8 *ptr = skb->nh.raw + opt->hop;
+		u8 *ptr = nh + opt->hop;
 		put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, (ptr[1]+1)<<3, ptr);
 	}
 	if (np->rxopt.bits.odstopts && opt->dst0) {
-		u8 *ptr = skb->nh.raw + opt->dst0;
+		u8 *ptr = nh + opt->dst0;
 		put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
 	}
 	if (np->rxopt.bits.osrcrt && opt->srcrt) {
-		struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(skb->nh.raw + opt->srcrt);
+		struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(nh + opt->srcrt);
 		put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, (rthdr->hdrlen+1) << 3, rthdr);
 	}
 	if (np->rxopt.bits.odstopts && opt->dst1) {
-		u8 *ptr = skb->nh.raw + opt->dst1;
+		u8 *ptr = nh + opt->dst1;
 		put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
 	}
 	return 0;
@@ -718,7 +723,7 @@
 				       cmsg->cmsg_type);
 			err = -EINVAL;
 			break;
-		};
+		}
 	}
 
 exit_f:
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index 363e63f..7107bb7 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -42,21 +42,19 @@
 static int esp6_output(struct xfrm_state *x, struct sk_buff *skb)
 {
 	int err;
-	int hdr_len;
 	struct ipv6hdr *top_iph;
 	struct ipv6_esp_hdr *esph;
 	struct crypto_blkcipher *tfm;
 	struct blkcipher_desc desc;
-	struct esp_data *esp;
 	struct sk_buff *trailer;
 	int blksize;
 	int clen;
 	int alen;
 	int nfrags;
-
-	esp = x->data;
-	hdr_len = skb->h.raw - skb->data +
-		  sizeof(*esph) + esp->conf.ivlen;
+	u8 *tail;
+	struct esp_data *esp = x->data;
+	int hdr_len = (skb_transport_offset(skb) +
+		       sizeof(*esph) + esp->conf.ivlen);
 
 	/* Strip IP+ESP header. */
 	__skb_pull(skb, hdr_len);
@@ -81,19 +79,20 @@
 	}
 
 	/* Fill padding... */
+	tail = skb_tail_pointer(trailer);
 	do {
 		int i;
 		for (i=0; i<clen-skb->len - 2; i++)
-			*(u8*)(trailer->tail + i) = i+1;
+			tail[i] = i + 1;
 	} while (0);
-	*(u8*)(trailer->tail + clen-skb->len - 2) = (clen - skb->len)-2;
+	tail[clen-skb->len - 2] = (clen - skb->len) - 2;
 	pskb_put(skb, trailer, clen - skb->len);
 
 	top_iph = (struct ipv6hdr *)__skb_push(skb, hdr_len);
-	esph = (struct ipv6_esp_hdr *)skb->h.raw;
+	esph = (struct ipv6_esp_hdr *)skb_transport_header(skb);
 	top_iph->payload_len = htons(skb->len + alen - sizeof(*top_iph));
-	*(u8*)(trailer->tail - 1) = *skb->nh.raw;
-	*skb->nh.raw = IPPROTO_ESP;
+	*(skb_tail_pointer(trailer) - 1) = *skb_network_header(skb);
+	*skb_network_header(skb) = IPPROTO_ESP;
 
 	esph->spi = x->id.spi;
 	esph->seq_no = htonl(++x->replay.oseq);
@@ -150,8 +149,7 @@
 	int blksize = ALIGN(crypto_blkcipher_blocksize(tfm), 4);
 	int alen = esp->auth.icv_trunc_len;
 	int elen = skb->len - sizeof(struct ipv6_esp_hdr) - esp->conf.ivlen - alen;
-
-	int hdr_len = skb->h.raw - skb->nh.raw;
+	int hdr_len = skb_network_header_len(skb);
 	int nfrags;
 	int ret = 0;
 
@@ -191,7 +189,7 @@
 	skb->ip_summed = CHECKSUM_NONE;
 
 	esph = (struct ipv6_esp_hdr*)skb->data;
-	iph = skb->nh.ipv6h;
+	iph = ipv6_hdr(skb);
 
 	/* Get ivec. This can be wrong, check against another impls. */
 	if (esp->conf.ivlen)
@@ -231,28 +229,30 @@
 		ret = nexthdr[1];
 	}
 
-	skb->h.raw = __skb_pull(skb, sizeof(*esph) + esp->conf.ivlen) - hdr_len;
-
+	__skb_pull(skb, sizeof(*esph) + esp->conf.ivlen);
+	skb_set_transport_header(skb, -hdr_len);
 out:
 	return ret;
 }
 
-static u32 esp6_get_max_size(struct xfrm_state *x, int mtu)
+static u32 esp6_get_mtu(struct xfrm_state *x, int mtu)
 {
 	struct esp_data *esp = x->data;
 	u32 blksize = ALIGN(crypto_blkcipher_blocksize(esp->conf.tfm), 4);
+	u32 align = max_t(u32, blksize, esp->conf.padlen);
+	u32 rem;
 
-	if (x->props.mode == XFRM_MODE_TUNNEL) {
-		mtu = ALIGN(mtu + 2, blksize);
-	} else {
-		/* The worst case. */
+	mtu -= x->props.header_len + esp->auth.icv_trunc_len;
+	rem = mtu & (align - 1);
+	mtu &= ~(align - 1);
+
+	if (x->props.mode != XFRM_MODE_TUNNEL) {
 		u32 padsize = ((blksize - 1) & 7) + 1;
-		mtu = ALIGN(mtu + 2, padsize) + blksize - padsize;
+		mtu -= blksize - padsize;
+		mtu += min_t(u32, blksize - padsize, rem);
 	}
-	if (esp->conf.padlen)
-		mtu = ALIGN(mtu, esp->conf.padlen);
 
-	return mtu + x->props.header_len + esp->auth.icv_trunc_len;
+	return mtu - 2;
 }
 
 static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
@@ -382,7 +382,7 @@
 	.proto	     	= IPPROTO_ESP,
 	.init_state	= esp6_init_state,
 	.destructor	= esp6_destroy,
-	.get_max_size	= esp6_get_max_size,
+	.get_mtu	= esp6_get_mtu,
 	.input		= esp6_input,
 	.output		= esp6_output,
 	.hdr_offset	= xfrm6_find_1stfragopt,
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index fb39604..6d8e4ac 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -50,13 +50,14 @@
 
 int ipv6_find_tlv(struct sk_buff *skb, int offset, int type)
 {
-	int packet_len = skb->tail - skb->nh.raw;
+	const unsigned char *nh = skb_network_header(skb);
+	int packet_len = skb->tail - skb->network_header;
 	struct ipv6_opt_hdr *hdr;
 	int len;
 
 	if (offset + 2 > packet_len)
 		goto bad;
-	hdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
+	hdr = (struct ipv6_opt_hdr *)(nh + offset);
 	len = ((hdr->hdrlen + 1) << 3);
 
 	if (offset + len > packet_len)
@@ -66,7 +67,7 @@
 	len -= 2;
 
 	while (len > 0) {
-		int opttype = skb->nh.raw[offset];
+		int opttype = nh[offset];
 		int optlen;
 
 		if (opttype == type)
@@ -77,7 +78,7 @@
 			optlen = 1;
 			break;
 		default:
-			optlen = skb->nh.raw[offset + 1] + 2;
+			optlen = nh[offset + 1] + 2;
 			if (optlen > len)
 				goto bad;
 			break;
@@ -113,7 +114,7 @@
 {
 	struct sk_buff *skb = *skbp;
 
-	switch ((skb->nh.raw[optoff] & 0xC0) >> 6) {
+	switch ((skb_network_header(skb)[optoff] & 0xC0) >> 6) {
 	case 0: /* ignore */
 		return 1;
 
@@ -124,12 +125,12 @@
 		/* Actually, it is redundant check. icmp_send
 		   will recheck in any case.
 		 */
-		if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr))
+		if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr))
 			break;
 	case 2: /* send ICMP PARM PROB regardless and drop packet */
 		icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff);
 		return 0;
-	};
+	}
 
 	kfree_skb(skb);
 	return 0;
@@ -141,19 +142,20 @@
 {
 	struct sk_buff *skb = *skbp;
 	struct tlvtype_proc *curr;
-	int off = skb->h.raw - skb->nh.raw;
-	int len = ((skb->h.raw[1]+1)<<3);
+	const unsigned char *nh = skb_network_header(skb);
+	int off = skb_network_header_len(skb);
+	int len = (skb_transport_header(skb)[1] + 1) << 3;
 
-	if ((skb->h.raw + len) - skb->data > skb_headlen(skb))
+	if (skb_transport_offset(skb) + len > skb_headlen(skb))
 		goto bad;
 
 	off += 2;
 	len -= 2;
 
 	while (len > 0) {
-		int optlen = skb->nh.raw[off+1]+2;
+		int optlen = nh[off + 1] + 2;
 
-		switch (skb->nh.raw[off]) {
+		switch (nh[off]) {
 		case IPV6_TLV_PAD0:
 			optlen = 1;
 			break;
@@ -165,7 +167,7 @@
 			if (optlen > len)
 				goto bad;
 			for (curr=procs; curr->type >= 0; curr++) {
-				if (curr->type == skb->nh.raw[off]) {
+				if (curr->type == nh[off]) {
 					/* type specific length/alignment
 					   checks will be performed in the
 					   func(). */
@@ -200,7 +202,7 @@
 	struct sk_buff *skb = *skbp;
 	struct ipv6_destopt_hao *hao;
 	struct inet6_skb_parm *opt = IP6CB(skb);
-	struct ipv6hdr *ipv6h = (struct ipv6hdr *)skb->nh.raw;
+	struct ipv6hdr *ipv6h = ipv6_hdr(skb);
 	struct in6_addr tmp_addr;
 	int ret;
 
@@ -211,7 +213,7 @@
 	opt->dsthao = opt->dst1;
 	opt->dst1 = 0;
 
-	hao = (struct ipv6_destopt_hao *)(skb->nh.raw + optoff);
+	hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff);
 
 	if (hao->length != 16) {
 		LIMIT_NETDEBUG(
@@ -244,8 +246,9 @@
 
 		/* update all variable using below by copied skbuff */
 		*skbp = skb = skb2;
-		hao = (struct ipv6_destopt_hao *)(skb2->nh.raw + optoff);
-		ipv6h = (struct ipv6hdr *)skb2->nh.raw;
+		hao = (struct ipv6_destopt_hao *)(skb_network_header(skb2) +
+						  optoff);
+		ipv6h = ipv6_hdr(skb2);
 	}
 
 	if (skb->ip_summed == CHECKSUM_COMPLETE)
@@ -255,7 +258,7 @@
 	ipv6_addr_copy(&ipv6h->saddr, &hao->addr);
 	ipv6_addr_copy(&hao->addr, &tmp_addr);
 
-	if (skb->tstamp.off_sec == 0)
+	if (skb->tstamp.tv64 == 0)
 		__net_timestamp(skb);
 
 	return 1;
@@ -285,16 +288,16 @@
 #endif
 	struct dst_entry *dst;
 
-	if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
-	    !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
+	if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||
+	    !pskb_may_pull(skb, (skb_transport_offset(skb) +
+				 ((skb_transport_header(skb)[1] + 1) << 3)))) {
 		IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
 				 IPSTATS_MIB_INHDRERRORS);
 		kfree_skb(skb);
 		return -1;
 	}
 
-	opt->lastopt = skb->h.raw - skb->nh.raw;
-	opt->dst1 = skb->h.raw - skb->nh.raw;
+	opt->lastopt = opt->dst1 = skb_network_header_len(skb);
 #ifdef CONFIG_IPV6_MIP6
 	dstbuf = opt->dst1;
 #endif
@@ -303,7 +306,7 @@
 	if (ip6_parse_tlv(tlvprocdestopt_lst, skbp)) {
 		dst_release(dst);
 		skb = *skbp;
-		skb->h.raw += ((skb->h.raw[1]+1)<<3);
+		skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;
 		opt = IP6CB(skb);
 #ifdef CONFIG_IPV6_MIP6
 		opt->nhoff = dstbuf;
@@ -384,18 +387,20 @@
 
 	in6_dev_put(idev);
 
-	if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
-	    !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
+	if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||
+	    !pskb_may_pull(skb, (skb_transport_offset(skb) +
+				 ((skb_transport_header(skb)[1] + 1) << 3)))) {
 		IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
 				 IPSTATS_MIB_INHDRERRORS);
 		kfree_skb(skb);
 		return -1;
 	}
 
-	hdr = (struct ipv6_rt_hdr *) skb->h.raw;
+	hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb);
 
 	switch (hdr->type) {
 #ifdef CONFIG_IPV6_MIP6
+	case IPV6_SRCRT_TYPE_2:
 		break;
 #endif
 	case IPV6_SRCRT_TYPE_0:
@@ -406,11 +411,12 @@
 	default:
 		IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
 				 IPSTATS_MIB_INHDRERRORS);
-		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw);
+		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
+				  (&hdr->type) - skb_network_header(skb));
 		return -1;
 	}
 
-	if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr) ||
+	if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) ||
 	    skb->pkt_type != PACKET_HOST) {
 		IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
 				 IPSTATS_MIB_INADDRERRORS);
@@ -438,12 +444,11 @@
 			break;
 		}
 
-		opt->lastopt = skb->h.raw - skb->nh.raw;
-		opt->srcrt = skb->h.raw - skb->nh.raw;
-		skb->h.raw += (hdr->hdrlen + 1) << 3;
+		opt->lastopt = opt->srcrt = skb_network_header_len(skb);
+		skb->transport_header += (hdr->hdrlen + 1) << 3;
 		opt->dst0 = opt->dst1;
 		opt->dst1 = 0;
-		opt->nhoff = (&hdr->nexthdr) - skb->nh.raw;
+		opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb);
 		return 1;
 	}
 
@@ -452,7 +457,9 @@
 		if (hdr->hdrlen & 0x01) {
 			IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
 					 IPSTATS_MIB_INHDRERRORS);
-			icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->hdrlen) - skb->nh.raw);
+			icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
+					  ((&hdr->hdrlen) -
+					   skb_network_header(skb)));
 			return -1;
 		}
 		break;
@@ -479,7 +486,9 @@
 	if (hdr->segments_left > n) {
 		IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
 				 IPSTATS_MIB_INHDRERRORS);
-		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->segments_left) - skb->nh.raw);
+		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
+				  ((&hdr->segments_left) -
+				   skb_network_header(skb)));
 		return -1;
 	}
 
@@ -498,7 +507,7 @@
 		kfree_skb(skb);
 		*skbp = skb = skb2;
 		opt = IP6CB(skb2);
-		hdr = (struct ipv6_rt_hdr *) skb2->h.raw;
+		hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb2);
 	}
 
 	if (skb->ip_summed == CHECKSUM_COMPLETE)
@@ -514,7 +523,7 @@
 #ifdef CONFIG_IPV6_MIP6
 	case IPV6_SRCRT_TYPE_2:
 		if (xfrm6_input_addr(skb, (xfrm_address_t *)addr,
-				     (xfrm_address_t *)&skb->nh.ipv6h->saddr,
+				     (xfrm_address_t *)&ipv6_hdr(skb)->saddr,
 				     IPPROTO_ROUTING) < 0) {
 			IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
 					 IPSTATS_MIB_INADDRERRORS);
@@ -541,19 +550,19 @@
 	}
 
 	ipv6_addr_copy(&daddr, addr);
-	ipv6_addr_copy(addr, &skb->nh.ipv6h->daddr);
-	ipv6_addr_copy(&skb->nh.ipv6h->daddr, &daddr);
+	ipv6_addr_copy(addr, &ipv6_hdr(skb)->daddr);
+	ipv6_addr_copy(&ipv6_hdr(skb)->daddr, &daddr);
 
 	dst_release(xchg(&skb->dst, NULL));
 	ip6_route_input(skb);
 	if (skb->dst->error) {
-		skb_push(skb, skb->data - skb->nh.raw);
+		skb_push(skb, skb->data - skb_network_header(skb));
 		dst_input(skb);
 		return -1;
 	}
 
 	if (skb->dst->dev->flags&IFF_LOOPBACK) {
-		if (skb->nh.ipv6h->hop_limit <= 1) {
+		if (ipv6_hdr(skb)->hop_limit <= 1) {
 			IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
 					 IPSTATS_MIB_INHDRERRORS);
 			icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
@@ -561,11 +570,11 @@
 			kfree_skb(skb);
 			return -1;
 		}
-		skb->nh.ipv6h->hop_limit--;
+		ipv6_hdr(skb)->hop_limit--;
 		goto looped_back;
 	}
 
-	skb_push(skb, skb->data - skb->nh.raw);
+	skb_push(skb, skb->data - skb_network_header(skb));
 	dst_input(skb);
 	return -1;
 }
@@ -656,13 +665,14 @@
 static int ipv6_hop_ra(struct sk_buff **skbp, int optoff)
 {
 	struct sk_buff *skb = *skbp;
+	const unsigned char *nh = skb_network_header(skb);
 
-	if (skb->nh.raw[optoff+1] == 2) {
+	if (nh[optoff + 1] == 2) {
 		IP6CB(skb)->ra = optoff;
 		return 1;
 	}
 	LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n",
-		       skb->nh.raw[optoff+1]);
+		       nh[optoff + 1]);
 	kfree_skb(skb);
 	return 0;
 }
@@ -672,23 +682,24 @@
 static int ipv6_hop_jumbo(struct sk_buff **skbp, int optoff)
 {
 	struct sk_buff *skb = *skbp;
+	const unsigned char *nh = skb_network_header(skb);
 	u32 pkt_len;
 
-	if (skb->nh.raw[optoff+1] != 4 || (optoff&3) != 2) {
+	if (nh[optoff + 1] != 4 || (optoff & 3) != 2) {
 		LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n",
-			       skb->nh.raw[optoff+1]);
+			       nh[optoff+1]);
 		IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
 				 IPSTATS_MIB_INHDRERRORS);
 		goto drop;
 	}
 
-	pkt_len = ntohl(*(__be32*)(skb->nh.raw+optoff+2));
+	pkt_len = ntohl(*(__be32 *)(nh + optoff + 2));
 	if (pkt_len <= IPV6_MAXPLEN) {
 		IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS);
 		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
 		return 0;
 	}
-	if (skb->nh.ipv6h->payload_len) {
+	if (ipv6_hdr(skb)->payload_len) {
 		IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS);
 		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
 		return 0;
@@ -727,13 +738,14 @@
 	struct inet6_skb_parm *opt = IP6CB(skb);
 
 	/*
-	 * skb->nh.raw is equal to skb->data, and
-	 * skb->h.raw - skb->nh.raw is always equal to
+	 * skb_network_header(skb) is equal to skb->data, and
+	 * skb_network_header_len(skb) is always equal to
 	 * sizeof(struct ipv6hdr) by definition of
 	 * hop-by-hop options.
 	 */
 	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) ||
-	    !pskb_may_pull(skb, sizeof(struct ipv6hdr) + ((skb->h.raw[1] + 1) << 3))) {
+	    !pskb_may_pull(skb, (sizeof(struct ipv6hdr) +
+				 ((skb_transport_header(skb)[1] + 1) << 3)))) {
 		kfree_skb(skb);
 		return -1;
 	}
@@ -741,7 +753,7 @@
 	opt->hop = sizeof(struct ipv6hdr);
 	if (ip6_parse_tlv(tlvprochopopt_lst, skbp)) {
 		skb = *skbp;
-		skb->h.raw += (skb->h.raw[1]+1)<<3;
+		skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;
 		opt = IP6CB(skb);
 		opt->nhoff = sizeof(struct ipv6hdr);
 		return 1;
@@ -810,6 +822,8 @@
 		ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
 }
 
+EXPORT_SYMBOL(ipv6_push_nfrag_opts);
+
 void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
 {
 	if (opt->dst1opt)
diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c
index ea3035b..fc3882c 100644
--- a/net/ipv6/fib6_rules.c
+++ b/net/ipv6/fib6_rules.c
@@ -17,6 +17,7 @@
 
 #include <net/fib_rules.h>
 #include <net/ipv6.h>
+#include <net/addrconf.h>
 #include <net/ip6_route.h>
 #include <net/netlink.h>
 
@@ -95,8 +96,27 @@
 	if (table)
 		rt = lookup(table, flp, flags);
 
-	if (rt != &ip6_null_entry)
+	if (rt != &ip6_null_entry) {
+		struct fib6_rule *r = (struct fib6_rule *)rule;
+
+		/*
+		 * If we need to find a source address for this traffic,
+		 * we check the result if it meets requirement of the rule.
+		 */
+		if ((rule->flags & FIB_RULE_FIND_SADDR) &&
+		    r->src.plen && !(flags & RT6_LOOKUP_F_HAS_SADDR)) {
+			struct in6_addr saddr;
+			if (ipv6_get_saddr(&rt->u.dst, &flp->fl6_dst,
+					   &saddr))
+				goto again;
+			if (!ipv6_prefix_equal(&saddr, &r->src.addr,
+					       r->src.plen))
+				goto again;
+			ipv6_addr_copy(&flp->fl6_src, &saddr);
+		}
 		goto out;
+	}
+again:
 	dst_release(&rt->u.dst);
 	rt = NULL;
 	goto out;
@@ -117,9 +137,17 @@
 	    !ipv6_prefix_equal(&fl->fl6_dst, &r->dst.addr, r->dst.plen))
 		return 0;
 
+	/*
+	 * If FIB_RULE_FIND_SADDR is set and we do not have a
+	 * source address for the traffic, we defer check for
+	 * source address.
+	 */
 	if (r->src.plen) {
-		if (!(flags & RT6_LOOKUP_F_HAS_SADDR) ||
-		    !ipv6_prefix_equal(&fl->fl6_src, &r->src.addr, r->src.plen))
+		if (flags & RT6_LOOKUP_F_HAS_SADDR) {
+			if (!ipv6_prefix_equal(&fl->fl6_src, &r->src.addr,
+					       r->src.plen))
+				return 0;
+		} else if (!(r->common.flags & FIB_RULE_FIND_SADDR))
 			return 0;
 	}
 
@@ -216,11 +244,6 @@
 	return -ENOBUFS;
 }
 
-int fib6_rules_dump(struct sk_buff *skb, struct netlink_callback *cb)
-{
-	return fib_rules_dump(skb, cb, AF_INET6);
-}
-
 static u32 fib6_rule_default_pref(void)
 {
 	return 0x3FFF;
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index edfe98b..e9bcce9 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -68,6 +68,7 @@
 #include <asm/system.h>
 
 DEFINE_SNMP_STAT(struct icmpv6_mib, icmpv6_statistics) __read_mostly;
+EXPORT_SYMBOL(icmpv6_statistics);
 
 /*
  *	The ICMP socket(s). This is the most convenient way to flow control
@@ -128,9 +129,9 @@
 
 static int is_ineligible(struct sk_buff *skb)
 {
-	int ptr = (u8*)(skb->nh.ipv6h+1) - skb->data;
+	int ptr = (u8 *)(ipv6_hdr(skb) + 1) - skb->data;
 	int len = skb->len - ptr;
-	__u8 nexthdr = skb->nh.ipv6h->nexthdr;
+	__u8 nexthdr = ipv6_hdr(skb)->nexthdr;
 
 	if (len < 0)
 		return 1;
@@ -205,7 +206,7 @@
 {
 	u8 _optval, *op;
 
-	offset += skb->nh.raw - skb->data;
+	offset += skb_network_offset(skb);
 	op = skb_header_pointer(skb, offset, sizeof(_optval), &_optval);
 	if (op == NULL)
 		return 1;
@@ -221,7 +222,7 @@
 	if ((skb = skb_peek(&sk->sk_write_queue)) == NULL)
 		goto out;
 
-	icmp6h = (struct icmp6hdr*) skb->h.raw;
+	icmp6h = icmp6_hdr(skb);
 	memcpy(icmp6h, thdr, sizeof(struct icmp6hdr));
 	icmp6h->icmp6_cksum = 0;
 
@@ -274,7 +275,7 @@
 #ifdef CONFIG_IPV6_MIP6
 static void mip6_addr_swap(struct sk_buff *skb)
 {
-	struct ipv6hdr *iph = skb->nh.ipv6h;
+	struct ipv6hdr *iph = ipv6_hdr(skb);
 	struct inet6_skb_parm *opt = IP6CB(skb);
 	struct ipv6_destopt_hao *hao;
 	struct in6_addr tmp;
@@ -283,7 +284,8 @@
 	if (opt->dsthao) {
 		off = ipv6_find_tlv(skb, opt->dsthao, IPV6_TLV_HAO);
 		if (likely(off >= 0)) {
-			hao = (struct ipv6_destopt_hao *)(skb->nh.raw + off);
+			hao = (struct ipv6_destopt_hao *)
+					(skb_network_header(skb) + off);
 			ipv6_addr_copy(&tmp, &iph->saddr);
 			ipv6_addr_copy(&iph->saddr, &hao->addr);
 			ipv6_addr_copy(&hao->addr, &tmp);
@@ -301,7 +303,7 @@
 		 struct net_device *dev)
 {
 	struct inet6_dev *idev = NULL;
-	struct ipv6hdr *hdr = skb->nh.ipv6h;
+	struct ipv6hdr *hdr = ipv6_hdr(skb);
 	struct sock *sk;
 	struct ipv6_pinfo *np;
 	struct in6_addr *saddr = NULL;
@@ -315,7 +317,8 @@
 	int hlimit, tclass;
 	int err = 0;
 
-	if ((u8*)hdr < skb->head || (u8*)(hdr+1) > skb->tail)
+	if ((u8 *)hdr < skb->head ||
+	    (skb->network_header + sizeof(*hdr)) > skb->tail)
 		return;
 
 	/*
@@ -430,7 +433,7 @@
 		tclass = 0;
 
 	msg.skb = skb;
-	msg.offset = skb->nh.raw - skb->data;
+	msg.offset = skb_network_offset(skb);
 	msg.type = type;
 
 	len = skb->len - msg.offset;
@@ -466,13 +469,15 @@
 	icmpv6_xmit_unlock();
 }
 
+EXPORT_SYMBOL(icmpv6_send);
+
 static void icmpv6_echo_reply(struct sk_buff *skb)
 {
 	struct sock *sk;
 	struct inet6_dev *idev;
 	struct ipv6_pinfo *np;
 	struct in6_addr *saddr = NULL;
-	struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw;
+	struct icmp6hdr *icmph = icmp6_hdr(skb);
 	struct icmp6hdr tmp_hdr;
 	struct flowi fl;
 	struct icmpv6_msg msg;
@@ -481,7 +486,7 @@
 	int hlimit;
 	int tclass;
 
-	saddr = &skb->nh.ipv6h->daddr;
+	saddr = &ipv6_hdr(skb)->daddr;
 
 	if (!ipv6_unicast_destination(skb))
 		saddr = NULL;
@@ -491,7 +496,7 @@
 
 	memset(&fl, 0, sizeof(fl));
 	fl.proto = IPPROTO_ICMPV6;
-	ipv6_addr_copy(&fl.fl6_dst, &skb->nh.ipv6h->saddr);
+	ipv6_addr_copy(&fl.fl6_dst, &ipv6_hdr(skb)->saddr);
 	if (saddr)
 		ipv6_addr_copy(&fl.fl6_src, saddr);
 	fl.oif = skb->dev->ifindex;
@@ -579,8 +584,8 @@
 	if (!pskb_may_pull(skb, inner_offset+8))
 		return;
 
-	saddr = &skb->nh.ipv6h->saddr;
-	daddr = &skb->nh.ipv6h->daddr;
+	saddr = &ipv6_hdr(skb)->saddr;
+	daddr = &ipv6_hdr(skb)->daddr;
 
 	/* BUGGG_FUTURE: we should try to parse exthdrs in this packet.
 	   Without this we will not able f.e. to make source routed
@@ -624,8 +629,8 @@
 
 	ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INMSGS);
 
-	saddr = &skb->nh.ipv6h->saddr;
-	daddr = &skb->nh.ipv6h->daddr;
+	saddr = &ipv6_hdr(skb)->saddr;
+	daddr = &ipv6_hdr(skb)->daddr;
 
 	/* Perform checksum. */
 	switch (skb->ip_summed) {
@@ -647,7 +652,7 @@
 	if (!pskb_pull(skb, sizeof(struct icmp6hdr)))
 		goto discard_it;
 
-	hdr = (struct icmp6hdr *) skb->h.raw;
+	hdr = icmp6_hdr(skb);
 
 	type = hdr->icmp6_type;
 
@@ -673,7 +678,7 @@
 		 */
 		if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
 			goto discard_it;
-		hdr = (struct icmp6hdr *) skb->h.raw;
+		hdr = icmp6_hdr(skb);
 		orig_hdr = (struct ipv6hdr *) (hdr + 1);
 		rt6_pmtu_discovery(&orig_hdr->daddr, &orig_hdr->saddr, dev,
 				   ntohl(hdr->icmp6_mtu));
@@ -727,7 +732,8 @@
 		 */
 
 		icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu);
-	};
+	}
+
 	kfree_skb(skb);
 	return 0;
 
@@ -860,11 +866,13 @@
 	case ICMPV6_TIME_EXCEED:
 		*err = EHOSTUNREACH;
 		break;
-	};
+	}
 
 	return fatal;
 }
 
+EXPORT_SYMBOL(icmpv6_err_convert);
+
 #ifdef CONFIG_SYSCTL
 ctl_table ipv6_icmp_table[] = {
 	{
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 268f476..ca08ee8 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -359,7 +359,7 @@
 	return res;
 }
 
-int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
+static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
 {
 	unsigned int h, s_h;
 	unsigned int e = 0, s_e;
@@ -1486,6 +1486,8 @@
 					   NULL, NULL);
 
 	fib6_tables_init();
+
+	__rtnl_register(PF_INET6, RTM_GETROUTE, NULL, inet6_dump_fib);
 }
 
 void fib6_gc_cleanup(void)
diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c
index 61e7a6c..be0ee8a 100644
--- a/net/ipv6/ip6_input.c
+++ b/net/ipv6/ip6_input.c
@@ -96,12 +96,12 @@
 	if (unlikely(!pskb_may_pull(skb, sizeof(*hdr))))
 		goto err;
 
-	hdr = skb->nh.ipv6h;
+	hdr = ipv6_hdr(skb);
 
 	if (hdr->version != 6)
 		goto err;
 
-	skb->h.raw = (u8 *)(hdr + 1);
+	skb->transport_header = skb->network_header + sizeof(*hdr);
 	IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
 
 	pkt_len = ntohs(hdr->payload_len);
@@ -116,7 +116,7 @@
 			IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
 			goto drop;
 		}
-		hdr = skb->nh.ipv6h;
+		hdr = ipv6_hdr(skb);
 	}
 
 	if (hdr->nexthdr == NEXTHDR_HOP) {
@@ -160,10 +160,10 @@
 	rcu_read_lock();
 resubmit:
 	idev = ip6_dst_idev(skb->dst);
-	if (!pskb_pull(skb, skb->h.raw - skb->data))
+	if (!pskb_pull(skb, skb_transport_offset(skb)))
 		goto discard;
 	nhoff = IP6CB(skb)->nhoff;
-	nexthdr = skb->nh.raw[nhoff];
+	nexthdr = skb_network_header(skb)[nhoff];
 
 	raw_sk = sk_head(&raw_v6_htable[nexthdr & (MAX_INET_PROTOS - 1)]);
 	if (raw_sk && !ipv6_raw_deliver(skb, nexthdr))
@@ -181,9 +181,9 @@
 			   indefinitely. */
 			nf_reset(skb);
 
-			skb_postpull_rcsum(skb, skb->nh.raw,
-					   skb->h.raw - skb->nh.raw);
-			hdr = skb->nh.ipv6h;
+			skb_postpull_rcsum(skb, skb_network_header(skb),
+					   skb_network_header_len(skb));
+			hdr = ipv6_hdr(skb);
 			if (ipv6_addr_is_multicast(&hdr->daddr) &&
 			    !ipv6_chk_mcast_addr(skb->dev, &hdr->daddr,
 			    &hdr->saddr) &&
@@ -234,7 +234,7 @@
 
 	IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INMCASTPKTS);
 
-	hdr = skb->nh.ipv6h;
+	hdr = ipv6_hdr(skb);
 	deliver = likely(!(skb->dev->flags & (IFF_PROMISC|IFF_ALLMULTI))) ||
 	    ipv6_chk_mcast_addr(skb->dev, &hdr->daddr, NULL);
 
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 3055169..f508171 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -88,8 +88,8 @@
 /* dev_loopback_xmit for use with netfilter. */
 static int ip6_dev_loopback_xmit(struct sk_buff *newskb)
 {
-	newskb->mac.raw = newskb->data;
-	__skb_pull(newskb, newskb->nh.raw - newskb->data);
+	skb_reset_mac_header(newskb);
+	__skb_pull(newskb, skb_network_offset(newskb));
 	newskb->pkt_type = PACKET_LOOPBACK;
 	newskb->ip_summed = CHECKSUM_UNNECESSARY;
 	BUG_TRAP(newskb->dst);
@@ -107,13 +107,13 @@
 	skb->protocol = htons(ETH_P_IPV6);
 	skb->dev = dev;
 
-	if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr)) {
+	if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) {
 		struct ipv6_pinfo* np = skb->sk ? inet6_sk(skb->sk) : NULL;
 		struct inet6_dev *idev = ip6_dst_idev(skb->dst);
 
 		if (!(dev->flags & IFF_LOOPBACK) && (!np || np->mc_loop) &&
-		    ipv6_chk_mcast_addr(dev, &skb->nh.ipv6h->daddr,
-				&skb->nh.ipv6h->saddr)) {
+		    ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr,
+					&ipv6_hdr(skb)->saddr)) {
 			struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
 
 			/* Do not check for IFF_ALLMULTI; multicast routing
@@ -124,7 +124,7 @@
 					newskb->dev,
 					ip6_dev_loopback_xmit);
 
-			if (skb->nh.ipv6h->hop_limit == 0) {
+			if (ipv6_hdr(skb)->hop_limit == 0) {
 				IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS);
 				kfree_skb(skb);
 				return 0;
@@ -137,9 +137,17 @@
 	return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb,NULL, skb->dev,ip6_output_finish);
 }
 
+static inline int ip6_skb_dst_mtu(struct sk_buff *skb)
+{
+	struct ipv6_pinfo *np = skb->sk ? inet6_sk(skb->sk) : NULL;
+
+	return (np && np->pmtudisc == IPV6_PMTUDISC_PROBE) ?
+	       skb->dst->dev->mtu : dst_mtu(skb->dst);
+}
+
 int ip6_output(struct sk_buff *skb)
 {
-	if ((skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb)) ||
+	if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
 				dst_allfrag(skb->dst))
 		return ip6_fragment(skb, ip6_output2);
 	else
@@ -191,7 +199,9 @@
 			ipv6_push_nfrag_opts(skb, opt, &proto, &first_hop);
 	}
 
-	hdr = skb->nh.ipv6h = (struct ipv6hdr*)skb_push(skb, sizeof(struct ipv6hdr));
+	skb_push(skb, sizeof(struct ipv6hdr));
+	skb_reset_network_header(skb);
+	hdr = ipv6_hdr(skb);
 
 	/*
 	 *	Fill in the IPv6 header
@@ -239,6 +249,8 @@
 	return -EMSGSIZE;
 }
 
+EXPORT_SYMBOL(ip6_xmit);
+
 /*
  *	To avoid extra problems ND packets are send through this
  *	routine. It's code duplication but I really want to avoid
@@ -259,8 +271,9 @@
 
 	totlen = len + sizeof(struct ipv6hdr);
 
-	hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
-	skb->nh.ipv6h = hdr;
+	skb_reset_network_header(skb);
+	skb_put(skb, sizeof(struct ipv6hdr));
+	hdr = ipv6_hdr(skb);
 
 	*(__be32*)hdr = htonl(0x60000000);
 
@@ -305,7 +318,7 @@
 
 static int ip6_forward_proxy_check(struct sk_buff *skb)
 {
-	struct ipv6hdr *hdr = skb->nh.ipv6h;
+	struct ipv6hdr *hdr = ipv6_hdr(skb);
 	u8 nexthdr = hdr->nexthdr;
 	int offset;
 
@@ -319,10 +332,11 @@
 	if (nexthdr == IPPROTO_ICMPV6) {
 		struct icmp6hdr *icmp6;
 
-		if (!pskb_may_pull(skb, skb->nh.raw + offset + 1 - skb->data))
+		if (!pskb_may_pull(skb, (skb_network_header(skb) +
+					 offset + 1 - skb->data)))
 			return 0;
 
-		icmp6 = (struct icmp6hdr *)(skb->nh.raw + offset);
+		icmp6 = (struct icmp6hdr *)(skb_network_header(skb) + offset);
 
 		switch (icmp6->icmp6_type) {
 		case NDISC_ROUTER_SOLICITATION:
@@ -361,7 +375,7 @@
 int ip6_forward(struct sk_buff *skb)
 {
 	struct dst_entry *dst = skb->dst;
-	struct ipv6hdr *hdr = skb->nh.ipv6h;
+	struct ipv6hdr *hdr = ipv6_hdr(skb);
 	struct inet6_skb_parm *opt = IP6CB(skb);
 
 	if (ipv6_devconf.forwarding == 0)
@@ -372,7 +386,7 @@
 		goto drop;
 	}
 
-	skb->ip_summed = CHECKSUM_NONE;
+	skb_forward_csum(skb);
 
 	/*
 	 *	We DO NOT make any processing on
@@ -388,7 +402,7 @@
 	 *	that different fragments will go along one path. --ANK
 	 */
 	if (opt->ra) {
-		u8 *ptr = skb->nh.raw + opt->ra;
+		u8 *ptr = skb_network_header(skb) + opt->ra;
 		if (ip6_call_ra_chain(skb, (ptr[2]<<8) + ptr[3]))
 			return 0;
 	}
@@ -470,7 +484,7 @@
 		goto drop;
 	}
 
-	hdr = skb->nh.ipv6h;
+	hdr = ipv6_hdr(skb);
 
 	/* Mangling hops number delayed to point after skb COW */
 
@@ -499,33 +513,18 @@
 #ifdef CONFIG_NET_SCHED
 	to->tc_index = from->tc_index;
 #endif
-#ifdef CONFIG_NETFILTER
-	/* Connection association is same as pre-frag packet */
-	nf_conntrack_put(to->nfct);
-	to->nfct = from->nfct;
-	nf_conntrack_get(to->nfct);
-	to->nfctinfo = from->nfctinfo;
-#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
-	nf_conntrack_put_reasm(to->nfct_reasm);
-	to->nfct_reasm = from->nfct_reasm;
-	nf_conntrack_get_reasm(to->nfct_reasm);
-#endif
-#ifdef CONFIG_BRIDGE_NETFILTER
-	nf_bridge_put(to->nf_bridge);
-	to->nf_bridge = from->nf_bridge;
-	nf_bridge_get(to->nf_bridge);
-#endif
-#endif
+	nf_copy(to, from);
 	skb_copy_secmark(to, from);
 }
 
 int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
 {
 	u16 offset = sizeof(struct ipv6hdr);
-	struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.ipv6h + 1);
-	unsigned int packet_len = skb->tail - skb->nh.raw;
+	struct ipv6_opt_hdr *exthdr =
+				(struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1);
+	unsigned int packet_len = skb->tail - skb->network_header;
 	int found_rhdr = 0;
-	*nexthdr = &skb->nh.ipv6h->nexthdr;
+	*nexthdr = &ipv6_hdr(skb)->nexthdr;
 
 	while (offset + 1 <= packet_len) {
 
@@ -550,7 +549,8 @@
 
 		offset += ipv6_optlen(exthdr);
 		*nexthdr = &exthdr->nexthdr;
-		exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
+		exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) +
+						 offset);
 	}
 
 	return offset;
@@ -574,7 +574,20 @@
 	hlen = ip6_find_1stfragopt(skb, &prevhdr);
 	nexthdr = *prevhdr;
 
-	mtu = dst_mtu(&rt->u.dst);
+	mtu = ip6_skb_dst_mtu(skb);
+
+	/* We must not fragment if the socket is set to force MTU discovery
+	 * or if the skb it not generated by a local socket.  (This last
+	 * check should be redundant, but it's free.)
+	 */
+	if (!np || np->pmtudisc >= IPV6_PMTUDISC_DO) {
+		skb->dev = skb->dst->dev;
+		icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev);
+		IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_FRAGFAILS);
+		kfree_skb(skb);
+		return -EMSGSIZE;
+	}
+
 	if (np && np->frag_size < mtu) {
 		if (np->frag_size)
 			mtu = np->frag_size;
@@ -616,7 +629,7 @@
 		/* BUILD HEADER */
 
 		*prevhdr = NEXTHDR_FRAGMENT;
-		tmp_hdr = kmemdup(skb->nh.raw, hlen, GFP_ATOMIC);
+		tmp_hdr = kmemdup(skb_network_header(skb), hlen, GFP_ATOMIC);
 		if (!tmp_hdr) {
 			IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_FRAGFAILS);
 			return -ENOMEM;
@@ -624,8 +637,9 @@
 
 		__skb_pull(skb, hlen);
 		fh = (struct frag_hdr*)__skb_push(skb, sizeof(struct frag_hdr));
-		skb->nh.raw = __skb_push(skb, hlen);
-		memcpy(skb->nh.raw, tmp_hdr, hlen);
+		__skb_push(skb, hlen);
+		skb_reset_network_header(skb);
+		memcpy(skb_network_header(skb), tmp_hdr, hlen);
 
 		ipv6_select_ident(skb, fh);
 		fh->nexthdr = nexthdr;
@@ -636,7 +650,8 @@
 		first_len = skb_pagelen(skb);
 		skb->data_len = first_len - skb_headlen(skb);
 		skb->len = first_len;
-		skb->nh.ipv6h->payload_len = htons(first_len - sizeof(struct ipv6hdr));
+		ipv6_hdr(skb)->payload_len = htons(first_len -
+						   sizeof(struct ipv6hdr));
 
 		dst_hold(&rt->u.dst);
 
@@ -645,10 +660,12 @@
 			 * before previous one went down. */
 			if (frag) {
 				frag->ip_summed = CHECKSUM_NONE;
-				frag->h.raw = frag->data;
+				skb_reset_transport_header(frag);
 				fh = (struct frag_hdr*)__skb_push(frag, sizeof(struct frag_hdr));
-				frag->nh.raw = __skb_push(frag, hlen);
-				memcpy(frag->nh.raw, tmp_hdr, hlen);
+				__skb_push(frag, hlen);
+				skb_reset_network_header(frag);
+				memcpy(skb_network_header(frag), tmp_hdr,
+				       hlen);
 				offset += skb->len - hlen - sizeof(struct frag_hdr);
 				fh->nexthdr = nexthdr;
 				fh->reserved = 0;
@@ -656,7 +673,9 @@
 				if (frag->next != NULL)
 					fh->frag_off |= htons(IP6_MF);
 				fh->identification = frag_id;
-				frag->nh.ipv6h->payload_len = htons(frag->len - sizeof(struct ipv6hdr));
+				ipv6_hdr(frag)->payload_len =
+						htons(frag->len -
+						      sizeof(struct ipv6hdr));
 				ip6_copy_metadata(frag, skb);
 			}
 
@@ -733,9 +752,10 @@
 		ip6_copy_metadata(frag, skb);
 		skb_reserve(frag, LL_RESERVED_SPACE(rt->u.dst.dev));
 		skb_put(frag, len + hlen + sizeof(struct frag_hdr));
-		frag->nh.raw = frag->data;
-		fh = (struct frag_hdr*)(frag->data + hlen);
-		frag->h.raw = frag->data + hlen + sizeof(struct frag_hdr);
+		skb_reset_network_header(frag);
+		fh = (struct frag_hdr *)(skb_network_header(frag) + hlen);
+		frag->transport_header = (frag->network_header + hlen +
+					  sizeof(struct frag_hdr));
 
 		/*
 		 *	Charge the memory for the fragment to any owner
@@ -747,7 +767,7 @@
 		/*
 		 *	Copy the packet header into the new buffer.
 		 */
-		memcpy(frag->nh.raw, skb->data, hlen);
+		skb_copy_from_linear_data(skb, skb_network_header(frag), hlen);
 
 		/*
 		 *	Build fragment header.
@@ -763,14 +783,15 @@
 		/*
 		 *	Copy a block of the IP datagram.
 		 */
-		if (skb_copy_bits(skb, ptr, frag->h.raw, len))
+		if (skb_copy_bits(skb, ptr, skb_transport_header(skb), len))
 			BUG();
 		left -= len;
 
 		fh->frag_off = htons(offset);
 		if (left > 0)
 			fh->frag_off |= htons(IP6_MF);
-		frag->nh.ipv6h->payload_len = htons(frag->len - sizeof(struct ipv6hdr));
+		ipv6_hdr(frag)->payload_len = htons(frag->len -
+						    sizeof(struct ipv6hdr));
 
 		ptr += len;
 		offset += len;
@@ -861,6 +882,41 @@
 			goto out_err_release;
 	}
 
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+		/*
+		 * Here if the dst entry we've looked up
+		 * has a neighbour entry that is in the INCOMPLETE
+		 * state and the src address from the flow is
+		 * marked as OPTIMISTIC, we release the found
+		 * dst entry and replace it instead with the
+		 * dst entry of the nexthop router
+		 */
+		if (!((*dst)->neighbour->nud_state & NUD_VALID)) {
+			struct inet6_ifaddr *ifp;
+			struct flowi fl_gw;
+			int redirect;
+
+			ifp = ipv6_get_ifaddr(&fl->fl6_src, (*dst)->dev, 1);
+
+			redirect = (ifp && ifp->flags & IFA_F_OPTIMISTIC);
+			if (ifp)
+				in6_ifa_put(ifp);
+
+			if (redirect) {
+				/*
+				 * We need to get the dst entry for the
+				 * default router instead
+				 */
+				dst_release(*dst);
+				memcpy(&fl_gw, fl, sizeof(struct flowi));
+				memset(&fl_gw.fl6_dst, 0, sizeof(struct in6_addr));
+				*dst = ip6_route_output(sk, &fl_gw);
+				if ((err = (*dst)->error))
+					goto out_err_release;
+			}
+		}
+#endif
+
 	return 0;
 
 out_err_release:
@@ -939,10 +995,10 @@
 		skb_put(skb,fragheaderlen + transhdrlen);
 
 		/* initialize network header pointer */
-		skb->nh.raw = skb->data;
+		skb_reset_network_header(skb);
 
 		/* initialize protocol header pointer */
-		skb->h.raw = skb->data + fragheaderlen;
+		skb->transport_header = skb->network_header + fragheaderlen;
 
 		skb->ip_summed = CHECKSUM_PARTIAL;
 		skb->csum = 0;
@@ -1015,7 +1071,8 @@
 		inet->cork.fl = *fl;
 		np->cork.hop_limit = hlimit;
 		np->cork.tclass = tclass;
-		mtu = dst_mtu(rt->u.dst.path);
+		mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
+		      rt->u.dst.dev->mtu : dst_mtu(rt->u.dst.path);
 		if (np->frag_size < mtu) {
 			if (np->frag_size)
 				mtu = np->frag_size;
@@ -1162,10 +1219,10 @@
 			 *	Find where to start putting bytes
 			 */
 			data = skb_put(skb, fraglen);
-			skb->nh.raw = data + exthdrlen;
+			skb_set_network_header(skb, exthdrlen);
 			data += fragheaderlen;
-			skb->h.raw = data + exthdrlen;
-
+			skb->transport_header = (skb->network_header +
+						 fragheaderlen);
 			if (fraggap) {
 				skb->csum = skb_copy_and_csum_bits(
 					skb_prev, maxfraglen,
@@ -1288,10 +1345,10 @@
 	tail_skb = &(skb_shinfo(skb)->frag_list);
 
 	/* move skb->data to ip header from ext header */
-	if (skb->data < skb->nh.raw)
-		__skb_pull(skb, skb->nh.raw - skb->data);
+	if (skb->data < skb_network_header(skb))
+		__skb_pull(skb, skb_network_offset(skb));
 	while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) {
-		__skb_pull(tmp_skb, skb->h.raw - skb->nh.raw);
+		__skb_pull(tmp_skb, skb_network_header_len(skb));
 		*tail_skb = tmp_skb;
 		tail_skb = &(tmp_skb->next);
 		skb->len += tmp_skb->len;
@@ -1303,13 +1360,15 @@
 	}
 
 	ipv6_addr_copy(final_dst, &fl->fl6_dst);
-	__skb_pull(skb, skb->h.raw - skb->nh.raw);
+	__skb_pull(skb, skb_network_header_len(skb));
 	if (opt && opt->opt_flen)
 		ipv6_push_frag_opts(skb, opt, &proto);
 	if (opt && opt->opt_nflen)
 		ipv6_push_nfrag_opts(skb, opt, &proto, &final_dst);
 
-	skb->nh.ipv6h = hdr = (struct ipv6hdr*) skb_push(skb, sizeof(struct ipv6hdr));
+	skb_push(skb, sizeof(struct ipv6hdr));
+	skb_reset_network_header(skb);
+	hdr = ipv6_hdr(skb);
 
 	*(__be32*)hdr = fl->fl6_flowlabel |
 		     htonl(0x60000000 | ((int)np->cork.tclass << 20));
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 08d9442..a0902fb 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -1,14 +1,15 @@
 /*
- *	IPv6 over IPv6 tunnel device
+ *	IPv6 tunneling device
  *	Linux INET6 implementation
  *
  *	Authors:
  *	Ville Nuorvala		<vnuorval@tcs.hut.fi>
+ *	Yasuyuki Kozakai	<kozakai@linux-ipv6.org>
  *
  *	$Id$
  *
  *      Based on:
- *      linux/net/ipv6/sit.c
+ *      linux/net/ipv6/sit.c and linux/net/ipv4/ipip.c
  *
  *      RFC 2473
  *
@@ -24,6 +25,7 @@
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/sockios.h>
+#include <linux/icmp.h>
 #include <linux/if.h>
 #include <linux/in.h>
 #include <linux/ip.h>
@@ -41,6 +43,7 @@
 #include <asm/uaccess.h>
 #include <asm/atomic.h>
 
+#include <net/icmp.h>
 #include <net/ip.h>
 #include <net/ipv6.h>
 #include <net/ip6_route.h>
@@ -51,7 +54,7 @@
 #include <net/inet_ecn.h>
 
 MODULE_AUTHOR("Ville Nuorvala");
-MODULE_DESCRIPTION("IPv6-in-IPv6 tunnel");
+MODULE_DESCRIPTION("IPv6 tunneling device");
 MODULE_LICENSE("GPL");
 
 #define IPV6_TLV_TEL_DST_SIZE 8
@@ -63,6 +66,7 @@
 #endif
 
 #define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK)
+#define IPV6_TCLASS_SHIFT 20
 
 #define HASH_SIZE  32
 
@@ -70,12 +74,12 @@
 		     (addr)->s6_addr32[2] ^ (addr)->s6_addr32[3]) & \
 		    (HASH_SIZE - 1))
 
-static int ip6ip6_fb_tnl_dev_init(struct net_device *dev);
-static int ip6ip6_tnl_dev_init(struct net_device *dev);
-static void ip6ip6_tnl_dev_setup(struct net_device *dev);
+static int ip6_fb_tnl_dev_init(struct net_device *dev);
+static int ip6_tnl_dev_init(struct net_device *dev);
+static void ip6_tnl_dev_setup(struct net_device *dev);
 
 /* the IPv6 tunnel fallback device */
-static struct net_device *ip6ip6_fb_tnl_dev;
+static struct net_device *ip6_fb_tnl_dev;
 
 
 /* lists for storing tunnels in use */
@@ -84,7 +88,7 @@
 static struct ip6_tnl **tnls[2] = { tnls_wc, tnls_r_l };
 
 /* lock for the tunnel lists */
-static DEFINE_RWLOCK(ip6ip6_lock);
+static DEFINE_RWLOCK(ip6_tnl_lock);
 
 static inline struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t)
 {
@@ -115,7 +119,7 @@
 }
 
 /**
- * ip6ip6_tnl_lookup - fetch tunnel matching the end-point addresses
+ * ip6_tnl_lookup - fetch tunnel matching the end-point addresses
  *   @remote: the address of the tunnel exit-point
  *   @local: the address of the tunnel entry-point
  *
@@ -126,7 +130,7 @@
  **/
 
 static struct ip6_tnl *
-ip6ip6_tnl_lookup(struct in6_addr *remote, struct in6_addr *local)
+ip6_tnl_lookup(struct in6_addr *remote, struct in6_addr *local)
 {
 	unsigned h0 = HASH(remote);
 	unsigned h1 = HASH(local);
@@ -145,18 +149,18 @@
 }
 
 /**
- * ip6ip6_bucket - get head of list matching given tunnel parameters
+ * ip6_tnl_bucket - get head of list matching given tunnel parameters
  *   @p: parameters containing tunnel end-points
  *
  * Description:
- *   ip6ip6_bucket() returns the head of the list matching the
+ *   ip6_tnl_bucket() returns the head of the list matching the
  *   &struct in6_addr entries laddr and raddr in @p.
  *
  * Return: head of IPv6 tunnel list
  **/
 
 static struct ip6_tnl **
-ip6ip6_bucket(struct ip6_tnl_parm *p)
+ip6_tnl_bucket(struct ip6_tnl_parm *p)
 {
 	struct in6_addr *remote = &p->raddr;
 	struct in6_addr *local = &p->laddr;
@@ -171,36 +175,36 @@
 }
 
 /**
- * ip6ip6_tnl_link - add tunnel to hash table
+ * ip6_tnl_link - add tunnel to hash table
  *   @t: tunnel to be added
  **/
 
 static void
-ip6ip6_tnl_link(struct ip6_tnl *t)
+ip6_tnl_link(struct ip6_tnl *t)
 {
-	struct ip6_tnl **tp = ip6ip6_bucket(&t->parms);
+	struct ip6_tnl **tp = ip6_tnl_bucket(&t->parms);
 
 	t->next = *tp;
-	write_lock_bh(&ip6ip6_lock);
+	write_lock_bh(&ip6_tnl_lock);
 	*tp = t;
-	write_unlock_bh(&ip6ip6_lock);
+	write_unlock_bh(&ip6_tnl_lock);
 }
 
 /**
- * ip6ip6_tnl_unlink - remove tunnel from hash table
+ * ip6_tnl_unlink - remove tunnel from hash table
  *   @t: tunnel to be removed
  **/
 
 static void
-ip6ip6_tnl_unlink(struct ip6_tnl *t)
+ip6_tnl_unlink(struct ip6_tnl *t)
 {
 	struct ip6_tnl **tp;
 
-	for (tp = ip6ip6_bucket(&t->parms); *tp; tp = &(*tp)->next) {
+	for (tp = ip6_tnl_bucket(&t->parms); *tp; tp = &(*tp)->next) {
 		if (t == *tp) {
-			write_lock_bh(&ip6ip6_lock);
+			write_lock_bh(&ip6_tnl_lock);
 			*tp = t->next;
-			write_unlock_bh(&ip6ip6_lock);
+			write_unlock_bh(&ip6_tnl_lock);
 			break;
 		}
 	}
@@ -237,12 +241,12 @@
 		if (i == IP6_TNL_MAX)
 			goto failed;
 	}
-	dev = alloc_netdev(sizeof (*t), name, ip6ip6_tnl_dev_setup);
+	dev = alloc_netdev(sizeof (*t), name, ip6_tnl_dev_setup);
 	if (dev == NULL)
 		goto failed;
 
 	t = netdev_priv(dev);
-	dev->init = ip6ip6_tnl_dev_init;
+	dev->init = ip6_tnl_dev_init;
 	t->parms = *p;
 
 	if ((err = register_netdevice(dev)) < 0) {
@@ -250,19 +254,19 @@
 		goto failed;
 	}
 	dev_hold(dev);
-	ip6ip6_tnl_link(t);
+	ip6_tnl_link(t);
 	return t;
 failed:
 	return NULL;
 }
 
 /**
- * ip6ip6_tnl_locate - find or create tunnel matching given parameters
+ * ip6_tnl_locate - find or create tunnel matching given parameters
  *   @p: tunnel parameters
  *   @create: != 0 if allowed to create new tunnel if no match found
  *
  * Description:
- *   ip6ip6_tnl_locate() first tries to locate an existing tunnel
+ *   ip6_tnl_locate() first tries to locate an existing tunnel
  *   based on @parms. If this is unsuccessful, but @create is set a new
  *   tunnel device is created and registered for use.
  *
@@ -270,13 +274,13 @@
  *   matching tunnel or NULL
  **/
 
-static struct ip6_tnl *ip6ip6_tnl_locate(struct ip6_tnl_parm *p, int create)
+static struct ip6_tnl *ip6_tnl_locate(struct ip6_tnl_parm *p, int create)
 {
 	struct in6_addr *remote = &p->raddr;
 	struct in6_addr *local = &p->laddr;
 	struct ip6_tnl *t;
 
-	for (t = *ip6ip6_bucket(p); t; t = t->next) {
+	for (t = *ip6_tnl_bucket(p); t; t = t->next) {
 		if (ipv6_addr_equal(local, &t->parms.laddr) &&
 		    ipv6_addr_equal(remote, &t->parms.raddr))
 			return t;
@@ -287,24 +291,24 @@
 }
 
 /**
- * ip6ip6_tnl_dev_uninit - tunnel device uninitializer
+ * ip6_tnl_dev_uninit - tunnel device uninitializer
  *   @dev: the device to be destroyed
  *
  * Description:
- *   ip6ip6_tnl_dev_uninit() removes tunnel from its list
+ *   ip6_tnl_dev_uninit() removes tunnel from its list
  **/
 
 static void
-ip6ip6_tnl_dev_uninit(struct net_device *dev)
+ip6_tnl_dev_uninit(struct net_device *dev)
 {
 	struct ip6_tnl *t = netdev_priv(dev);
 
-	if (dev == ip6ip6_fb_tnl_dev) {
-		write_lock_bh(&ip6ip6_lock);
+	if (dev == ip6_fb_tnl_dev) {
+		write_lock_bh(&ip6_tnl_lock);
 		tnls_wc[0] = NULL;
-		write_unlock_bh(&ip6ip6_lock);
+		write_unlock_bh(&ip6_tnl_lock);
 	} else {
-		ip6ip6_tnl_unlink(t);
+		ip6_tnl_unlink(t);
 	}
 	ip6_tnl_dst_reset(t);
 	dev_put(dev);
@@ -372,16 +376,16 @@
 }
 
 /**
- * ip6ip6_err - tunnel error handler
+ * ip6_tnl_err - tunnel error handler
  *
  * Description:
- *   ip6ip6_err() should handle errors in the tunnel according
+ *   ip6_tnl_err() should handle errors in the tunnel according
  *   to the specifications in RFC 2473.
  **/
 
 static int
-ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
-	   int type, int code, int offset, __be32 info)
+ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt,
+	    int *type, int *code, int *msg, __be32 *info, int offset)
 {
 	struct ipv6hdr *ipv6h = (struct ipv6hdr *) skb->data;
 	struct ip6_tnl *t;
@@ -396,13 +400,16 @@
 	   in trouble since we might need the source address for further
 	   processing of the error. */
 
-	read_lock(&ip6ip6_lock);
-	if ((t = ip6ip6_tnl_lookup(&ipv6h->daddr, &ipv6h->saddr)) == NULL)
+	read_lock(&ip6_tnl_lock);
+	if ((t = ip6_tnl_lookup(&ipv6h->daddr, &ipv6h->saddr)) == NULL)
+		goto out;
+
+	if (t->parms.proto != ipproto && t->parms.proto != 0)
 		goto out;
 
 	err = 0;
 
-	switch (type) {
+	switch (*type) {
 		__u32 teli;
 		struct ipv6_tlv_tnl_enc_lim *tel;
 		__u32 mtu;
@@ -414,7 +421,7 @@
 		rel_msg = 1;
 		break;
 	case ICMPV6_TIME_EXCEED:
-		if (code == ICMPV6_EXC_HOPLIMIT) {
+		if ((*code) == ICMPV6_EXC_HOPLIMIT) {
 			if (net_ratelimit())
 				printk(KERN_WARNING
 				       "%s: Too small hop limit or "
@@ -425,10 +432,10 @@
 		break;
 	case ICMPV6_PARAMPROB:
 		teli = 0;
-		if (code == ICMPV6_HDR_FIELD)
+		if ((*code) == ICMPV6_HDR_FIELD)
 			teli = parse_tlv_tnl_enc_lim(skb, skb->data);
 
-		if (teli && teli == ntohl(info) - 2) {
+		if (teli && teli == ntohl(*info) - 2) {
 			tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli];
 			if (tel->encap_limit == 0) {
 				if (net_ratelimit())
@@ -445,7 +452,7 @@
 		}
 		break;
 	case ICMPV6_PKT_TOOBIG:
-		mtu = ntohl(info) - offset;
+		mtu = ntohl(*info) - offset;
 		if (mtu < IPV6_MIN_MTU)
 			mtu = IPV6_MIN_MTU;
 		t->dev->mtu = mtu;
@@ -458,20 +465,144 @@
 		}
 		break;
 	}
-	if (rel_msg &&  pskb_may_pull(skb, offset + sizeof (*ipv6h))) {
+
+	*type = rel_type;
+	*code = rel_code;
+	*info = rel_info;
+	*msg = rel_msg;
+
+out:
+	read_unlock(&ip6_tnl_lock);
+	return err;
+}
+
+static int
+ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+	   int type, int code, int offset, __u32 info)
+{
+	int rel_msg = 0;
+	int rel_type = type;
+	int rel_code = code;
+	__u32 rel_info = info;
+	int err;
+	struct sk_buff *skb2;
+	struct iphdr *eiph;
+	struct flowi fl;
+	struct rtable *rt;
+
+	err = ip6_tnl_err(skb, IPPROTO_IPIP, opt, &rel_type, &rel_code,
+			  &rel_msg, &rel_info, offset);
+	if (err < 0)
+		return err;
+
+	if (rel_msg == 0)
+		return 0;
+
+	switch (rel_type) {
+	case ICMPV6_DEST_UNREACH:
+		if (rel_code != ICMPV6_ADDR_UNREACH)
+			return 0;
+		rel_type = ICMP_DEST_UNREACH;
+		rel_code = ICMP_HOST_UNREACH;
+		break;
+	case ICMPV6_PKT_TOOBIG:
+		if (rel_code != 0)
+			return 0;
+		rel_type = ICMP_DEST_UNREACH;
+		rel_code = ICMP_FRAG_NEEDED;
+		break;
+	default:
+		return 0;
+	}
+
+	if (!pskb_may_pull(skb, offset + sizeof(struct iphdr)))
+		return 0;
+
+	skb2 = skb_clone(skb, GFP_ATOMIC);
+	if (!skb2)
+		return 0;
+
+	dst_release(skb2->dst);
+	skb2->dst = NULL;
+	skb_pull(skb2, offset);
+	skb_reset_network_header(skb2);
+	eiph = ip_hdr(skb2);
+
+	/* Try to guess incoming interface */
+	memset(&fl, 0, sizeof(fl));
+	fl.fl4_dst = eiph->saddr;
+	fl.fl4_tos = RT_TOS(eiph->tos);
+	fl.proto = IPPROTO_IPIP;
+	if (ip_route_output_key(&rt, &fl))
+		goto out;
+
+	skb2->dev = rt->u.dst.dev;
+
+	/* route "incoming" packet */
+	if (rt->rt_flags & RTCF_LOCAL) {
+		ip_rt_put(rt);
+		rt = NULL;
+		fl.fl4_dst = eiph->daddr;
+		fl.fl4_src = eiph->saddr;
+		fl.fl4_tos = eiph->tos;
+		if (ip_route_output_key(&rt, &fl) ||
+		    rt->u.dst.dev->type != ARPHRD_TUNNEL) {
+			ip_rt_put(rt);
+			goto out;
+		}
+	} else {
+		ip_rt_put(rt);
+		if (ip_route_input(skb2, eiph->daddr, eiph->saddr, eiph->tos,
+				   skb2->dev) ||
+		    skb2->dst->dev->type != ARPHRD_TUNNEL)
+			goto out;
+	}
+
+	/* change mtu on this route */
+	if (rel_type == ICMP_DEST_UNREACH && rel_code == ICMP_FRAG_NEEDED) {
+		if (rel_info > dst_mtu(skb2->dst))
+			goto out;
+
+		skb2->dst->ops->update_pmtu(skb2->dst, rel_info);
+		rel_info = htonl(rel_info);
+	}
+
+	icmp_send(skb2, rel_type, rel_code, rel_info);
+
+out:
+	kfree_skb(skb2);
+	return 0;
+}
+
+static int
+ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+	   int type, int code, int offset, __u32 info)
+{
+	int rel_msg = 0;
+	int rel_type = type;
+	int rel_code = code;
+	__u32 rel_info = info;
+	int err;
+
+	err = ip6_tnl_err(skb, IPPROTO_IPV6, opt, &rel_type, &rel_code,
+			  &rel_msg, &rel_info, offset);
+	if (err < 0)
+		return err;
+
+	if (rel_msg && pskb_may_pull(skb, offset + sizeof(struct ipv6hdr))) {
 		struct rt6_info *rt;
 		struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
 
 		if (!skb2)
-			goto out;
+			return 0;
 
 		dst_release(skb2->dst);
 		skb2->dst = NULL;
 		skb_pull(skb2, offset);
-		skb2->nh.raw = skb2->data;
+		skb_reset_network_header(skb2);
 
 		/* Try to guess incoming interface */
-		rt = rt6_lookup(&skb2->nh.ipv6h->saddr, NULL, 0, 0);
+		rt = rt6_lookup(&ipv6_hdr(skb2)->saddr, NULL, 0, 0);
 
 		if (rt && rt->rt6i_dev)
 			skb2->dev = rt->rt6i_dev;
@@ -483,19 +614,34 @@
 
 		kfree_skb(skb2);
 	}
-out:
-	read_unlock(&ip6ip6_lock);
-	return err;
+
+	return 0;
 }
 
-static inline void ip6ip6_ecn_decapsulate(struct ipv6hdr *outer_iph,
-					  struct sk_buff *skb)
+static void ip4ip6_dscp_ecn_decapsulate(struct ip6_tnl *t,
+					struct ipv6hdr *ipv6h,
+					struct sk_buff *skb)
 {
-	struct ipv6hdr *inner_iph = skb->nh.ipv6h;
+	__u8 dsfield = ipv6_get_dsfield(ipv6h) & ~INET_ECN_MASK;
 
-	if (INET_ECN_is_ce(ipv6_get_dsfield(outer_iph)))
-		IP6_ECN_set_ce(inner_iph);
+	if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY)
+		ipv4_change_dsfield(ip_hdr(skb), INET_ECN_MASK, dsfield);
+
+	if (INET_ECN_is_ce(dsfield))
+		IP_ECN_set_ce(ip_hdr(skb));
 }
+
+static void ip6ip6_dscp_ecn_decapsulate(struct ip6_tnl *t,
+					struct ipv6hdr *ipv6h,
+					struct sk_buff *skb)
+{
+	if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY)
+		ipv6_copy_dscp(ipv6h, ipv6_hdr(skb));
+
+	if (INET_ECN_is_ce(ipv6_get_dsfield(ipv6h)))
+		IP6_ECN_set_ce(ipv6_hdr(skb));
+}
+
 static inline int ip6_tnl_rcv_ctl(struct ip6_tnl *t)
 {
 	struct ip6_tnl_parm *p = &t->parms;
@@ -519,53 +665,61 @@
 }
 
 /**
- * ip6ip6_rcv - decapsulate IPv6 packet and retransmit it locally
+ * ip6_tnl_rcv - decapsulate IPv6 packet and retransmit it locally
  *   @skb: received socket buffer
+ *   @protocol: ethernet protocol ID
+ *   @dscp_ecn_decapsulate: the function to decapsulate DSCP code and ECN
  *
  * Return: 0
  **/
 
-static int
-ip6ip6_rcv(struct sk_buff *skb)
+static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol,
+		       __u8 ipproto,
+		       void (*dscp_ecn_decapsulate)(struct ip6_tnl *t,
+						    struct ipv6hdr *ipv6h,
+						    struct sk_buff *skb))
 {
-	struct ipv6hdr *ipv6h;
 	struct ip6_tnl *t;
+	struct ipv6hdr *ipv6h = ipv6_hdr(skb);
 
-	ipv6h = skb->nh.ipv6h;
+	read_lock(&ip6_tnl_lock);
 
-	read_lock(&ip6ip6_lock);
+	if ((t = ip6_tnl_lookup(&ipv6h->saddr, &ipv6h->daddr)) != NULL) {
+		if (t->parms.proto != ipproto && t->parms.proto != 0) {
+			read_unlock(&ip6_tnl_lock);
+			goto discard;
+		}
 
-	if ((t = ip6ip6_tnl_lookup(&ipv6h->saddr, &ipv6h->daddr)) != NULL) {
 		if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
-			read_unlock(&ip6ip6_lock);
+			read_unlock(&ip6_tnl_lock);
 			goto discard;
 		}
 
 		if (!ip6_tnl_rcv_ctl(t)) {
 			t->stat.rx_dropped++;
-			read_unlock(&ip6ip6_lock);
+			read_unlock(&ip6_tnl_lock);
 			goto discard;
 		}
 		secpath_reset(skb);
-		skb->mac.raw = skb->nh.raw;
-		skb->nh.raw = skb->data;
-		skb->protocol = htons(ETH_P_IPV6);
+		skb->mac_header = skb->network_header;
+		skb_reset_network_header(skb);
+		skb->protocol = htons(protocol);
 		skb->pkt_type = PACKET_HOST;
 		memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
 		skb->dev = t->dev;
 		dst_release(skb->dst);
 		skb->dst = NULL;
 		nf_reset(skb);
-		if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY)
-			ipv6_copy_dscp(ipv6h, skb->nh.ipv6h);
-		ip6ip6_ecn_decapsulate(ipv6h, skb);
+
+		dscp_ecn_decapsulate(t, ipv6h, skb);
+
 		t->stat.rx_packets++;
 		t->stat.rx_bytes += skb->len;
 		netif_rx(skb);
-		read_unlock(&ip6ip6_lock);
+		read_unlock(&ip6_tnl_lock);
 		return 0;
 	}
-	read_unlock(&ip6ip6_lock);
+	read_unlock(&ip6_tnl_lock);
 	return 1;
 
 discard:
@@ -573,6 +727,18 @@
 	return 0;
 }
 
+static int ip4ip6_rcv(struct sk_buff *skb)
+{
+	return ip6_tnl_rcv(skb, ETH_P_IP, IPPROTO_IPIP,
+			   ip4ip6_dscp_ecn_decapsulate);
+}
+
+static int ip6ip6_rcv(struct sk_buff *skb)
+{
+	return ip6_tnl_rcv(skb, ETH_P_IPV6, IPPROTO_IPV6,
+			   ip6ip6_dscp_ecn_decapsulate);
+}
+
 struct ipv6_tel_txoption {
 	struct ipv6_txoptions ops;
 	__u8 dst_opt[8];
@@ -593,7 +759,7 @@
 }
 
 /**
- * ip6ip6_tnl_addr_conflict - compare packet addresses to tunnel's own
+ * ip6_tnl_addr_conflict - compare packet addresses to tunnel's own
  *   @t: the outgoing tunnel device
  *   @hdr: IPv6 header from the incoming packet
  *
@@ -607,7 +773,7 @@
  **/
 
 static inline int
-ip6ip6_tnl_addr_conflict(struct ip6_tnl *t, struct ipv6hdr *hdr)
+ip6_tnl_addr_conflict(struct ip6_tnl *t, struct ipv6hdr *hdr)
 {
 	return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr);
 }
@@ -641,72 +807,49 @@
 	return ret;
 }
 /**
- * ip6ip6_tnl_xmit - encapsulate packet and send
+ * ip6_tnl_xmit2 - encapsulate packet and send
  *   @skb: the outgoing socket buffer
  *   @dev: the outgoing tunnel device
+ *   @dsfield: dscp code for outer header
+ *   @fl: flow of tunneled packet
+ *   @encap_limit: encapsulation limit
+ *   @pmtu: Path MTU is stored if packet is too big
  *
  * Description:
  *   Build new header and do some sanity checks on the packet before sending
  *   it.
  *
  * Return:
- *   0
+ *   0 on success
+ *   -1 fail
+ *   %-EMSGSIZE message too big. return mtu in this case.
  **/
 
-static int
-ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
+static int ip6_tnl_xmit2(struct sk_buff *skb,
+			 struct net_device *dev,
+			 __u8 dsfield,
+			 struct flowi *fl,
+			 int encap_limit,
+			 __u32 *pmtu)
 {
 	struct ip6_tnl *t = netdev_priv(dev);
 	struct net_device_stats *stats = &t->stat;
-	struct ipv6hdr *ipv6h = skb->nh.ipv6h;
-	int encap_limit = -1;
+	struct ipv6hdr *ipv6h = ipv6_hdr(skb);
 	struct ipv6_tel_txoption opt;
-	__u16 offset;
-	struct flowi fl;
 	struct dst_entry *dst;
 	struct net_device *tdev;
 	int mtu;
 	int max_headroom = sizeof(struct ipv6hdr);
 	u8 proto;
-	int err;
+	int err = -1;
 	int pkt_len;
-	int dsfield;
-
-	if (t->recursion++) {
-		stats->collisions++;
-		goto tx_err;
-	}
-	if (skb->protocol != htons(ETH_P_IPV6) ||
-	    !ip6_tnl_xmit_ctl(t) || ip6ip6_tnl_addr_conflict(t, ipv6h))
-		goto tx_err;
-
-	if ((offset = parse_tlv_tnl_enc_lim(skb, skb->nh.raw)) > 0) {
-		struct ipv6_tlv_tnl_enc_lim *tel;
-		tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->nh.raw[offset];
-		if (tel->encap_limit == 0) {
-			icmpv6_send(skb, ICMPV6_PARAMPROB,
-				    ICMPV6_HDR_FIELD, offset + 2, skb->dev);
-			goto tx_err;
-		}
-		encap_limit = tel->encap_limit - 1;
-	} else if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
-		encap_limit = t->parms.encap_limit;
-
-	memcpy(&fl, &t->fl, sizeof (fl));
-	proto = fl.proto;
-
-	dsfield = ipv6_get_dsfield(ipv6h);
-	if ((t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS))
-		fl.fl6_flowlabel |= (*(__be32 *) ipv6h & IPV6_TCLASS_MASK);
-	if ((t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL))
-		fl.fl6_flowlabel |= (*(__be32 *) ipv6h & IPV6_FLOWLABEL_MASK);
 
 	if ((dst = ip6_tnl_dst_check(t)) != NULL)
 		dst_hold(dst);
 	else {
-		dst = ip6_route_output(NULL, &fl);
+		dst = ip6_route_output(NULL, fl);
 
-		if (dst->error || xfrm_lookup(&dst, &fl, NULL, 0) < 0)
+		if (dst->error || xfrm_lookup(&dst, fl, NULL, 0) < 0)
 			goto tx_err_link_failure;
 	}
 
@@ -730,7 +873,8 @@
 	if (skb->dst)
 		skb->dst->ops->update_pmtu(skb->dst, mtu);
 	if (skb->len > mtu) {
-		icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev);
+		*pmtu = mtu;
+		err = -EMSGSIZE;
 		goto tx_err_dst_release;
 	}
 
@@ -754,22 +898,24 @@
 	dst_release(skb->dst);
 	skb->dst = dst_clone(dst);
 
-	skb->h.raw = skb->nh.raw;
+	skb->transport_header = skb->network_header;
 
+	proto = fl->proto;
 	if (encap_limit >= 0) {
 		init_tel_txopt(&opt, encap_limit);
 		ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL);
 	}
-	skb->nh.raw = skb_push(skb, sizeof(struct ipv6hdr));
-	ipv6h = skb->nh.ipv6h;
-	*(__be32*)ipv6h = fl.fl6_flowlabel | htonl(0x60000000);
+	skb_push(skb, sizeof(struct ipv6hdr));
+	skb_reset_network_header(skb);
+	ipv6h = ipv6_hdr(skb);
+	*(__be32*)ipv6h = fl->fl6_flowlabel | htonl(0x60000000);
 	dsfield = INET_ECN_encapsulate(0, dsfield);
 	ipv6_change_dsfield(ipv6h, ~INET_ECN_MASK, dsfield);
 	ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
 	ipv6h->hop_limit = t->parms.hop_limit;
 	ipv6h->nexthdr = proto;
-	ipv6_addr_copy(&ipv6h->saddr, &fl.fl6_src);
-	ipv6_addr_copy(&ipv6h->daddr, &fl.fl6_dst);
+	ipv6_addr_copy(&ipv6h->saddr, &fl->fl6_src);
+	ipv6_addr_copy(&ipv6h->daddr, &fl->fl6_dst);
 	nf_reset(skb);
 	pkt_len = skb->len;
 	err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL,
@@ -783,13 +929,131 @@
 		stats->tx_aborted_errors++;
 	}
 	ip6_tnl_dst_store(t, dst);
-	t->recursion--;
 	return 0;
 tx_err_link_failure:
 	stats->tx_carrier_errors++;
 	dst_link_failure(skb);
 tx_err_dst_release:
 	dst_release(dst);
+	return err;
+}
+
+static inline int
+ip4ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ip6_tnl *t = netdev_priv(dev);
+	struct iphdr  *iph = ip_hdr(skb);
+	int encap_limit = -1;
+	struct flowi fl;
+	__u8 dsfield;
+	__u32 mtu;
+	int err;
+
+	if ((t->parms.proto != IPPROTO_IPIP && t->parms.proto != 0) ||
+	    !ip6_tnl_xmit_ctl(t))
+		return -1;
+
+	if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
+		encap_limit = t->parms.encap_limit;
+
+	memcpy(&fl, &t->fl, sizeof (fl));
+	fl.proto = IPPROTO_IPIP;
+
+	dsfield = ipv4_get_dsfield(iph);
+
+	if ((t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS))
+		fl.fl6_flowlabel |= ntohl(((__u32)iph->tos << IPV6_TCLASS_SHIFT)
+					  & IPV6_TCLASS_MASK);
+
+	err = ip6_tnl_xmit2(skb, dev, dsfield, &fl, encap_limit, &mtu);
+	if (err != 0) {
+		/* XXX: send ICMP error even if DF is not set. */
+		if (err == -EMSGSIZE)
+			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
+				  htonl(mtu));
+		return -1;
+	}
+
+	return 0;
+}
+
+static inline int
+ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ip6_tnl *t = netdev_priv(dev);
+	struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+	int encap_limit = -1;
+	__u16 offset;
+	struct flowi fl;
+	__u8 dsfield;
+	__u32 mtu;
+	int err;
+
+	if ((t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) ||
+	    !ip6_tnl_xmit_ctl(t) || ip6_tnl_addr_conflict(t, ipv6h))
+		return -1;
+
+	offset = parse_tlv_tnl_enc_lim(skb, skb_network_header(skb));
+	if (offset > 0) {
+		struct ipv6_tlv_tnl_enc_lim *tel;
+		tel = (struct ipv6_tlv_tnl_enc_lim *)&skb_network_header(skb)[offset];
+		if (tel->encap_limit == 0) {
+			icmpv6_send(skb, ICMPV6_PARAMPROB,
+				    ICMPV6_HDR_FIELD, offset + 2, skb->dev);
+			return -1;
+		}
+		encap_limit = tel->encap_limit - 1;
+	} else if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
+		encap_limit = t->parms.encap_limit;
+
+	memcpy(&fl, &t->fl, sizeof (fl));
+	fl.proto = IPPROTO_IPV6;
+
+	dsfield = ipv6_get_dsfield(ipv6h);
+	if ((t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS))
+		fl.fl6_flowlabel |= (*(__be32 *) ipv6h & IPV6_TCLASS_MASK);
+	if ((t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL))
+		fl.fl6_flowlabel |= (*(__be32 *) ipv6h & IPV6_FLOWLABEL_MASK);
+
+	err = ip6_tnl_xmit2(skb, dev, dsfield, &fl, encap_limit, &mtu);
+	if (err != 0) {
+		if (err == -EMSGSIZE)
+			icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ip6_tnl *t = netdev_priv(dev);
+	struct net_device_stats *stats = &t->stat;
+	int ret;
+
+	if (t->recursion++) {
+		t->stat.collisions++;
+		goto tx_err;
+	}
+
+	switch (skb->protocol) {
+	case __constant_htons(ETH_P_IP):
+		ret = ip4ip6_tnl_xmit(skb, dev);
+		break;
+	case __constant_htons(ETH_P_IPV6):
+		ret = ip6ip6_tnl_xmit(skb, dev);
+		break;
+	default:
+		goto tx_err;
+	}
+
+	if (ret < 0)
+		goto tx_err;
+
+	t->recursion--;
+	return 0;
+
 tx_err:
 	stats->tx_errors++;
 	stats->tx_dropped++;
@@ -817,7 +1081,7 @@
 	}
 }
 
-static void ip6ip6_tnl_link_config(struct ip6_tnl *t)
+static void ip6_tnl_link_config(struct ip6_tnl *t)
 {
 	struct net_device *dev = t->dev;
 	struct ip6_tnl_parm *p = &t->parms;
@@ -870,17 +1134,17 @@
 }
 
 /**
- * ip6ip6_tnl_change - update the tunnel parameters
+ * ip6_tnl_change - update the tunnel parameters
  *   @t: tunnel to be changed
  *   @p: tunnel configuration parameters
  *   @active: != 0 if tunnel is ready for use
  *
  * Description:
- *   ip6ip6_tnl_change() updates the tunnel parameters
+ *   ip6_tnl_change() updates the tunnel parameters
  **/
 
 static int
-ip6ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p)
+ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p)
 {
 	ipv6_addr_copy(&t->parms.laddr, &p->laddr);
 	ipv6_addr_copy(&t->parms.raddr, &p->raddr);
@@ -889,19 +1153,20 @@
 	t->parms.encap_limit = p->encap_limit;
 	t->parms.flowinfo = p->flowinfo;
 	t->parms.link = p->link;
+	t->parms.proto = p->proto;
 	ip6_tnl_dst_reset(t);
-	ip6ip6_tnl_link_config(t);
+	ip6_tnl_link_config(t);
 	return 0;
 }
 
 /**
- * ip6ip6_tnl_ioctl - configure ipv6 tunnels from userspace
+ * ip6_tnl_ioctl - configure ipv6 tunnels from userspace
  *   @dev: virtual device associated with tunnel
  *   @ifr: parameters passed from userspace
  *   @cmd: command to be performed
  *
  * Description:
- *   ip6ip6_tnl_ioctl() is used for managing IPv6 tunnels
+ *   ip6_tnl_ioctl() is used for managing IPv6 tunnels
  *   from userspace.
  *
  *   The possible commands are the following:
@@ -923,7 +1188,7 @@
  **/
 
 static int
-ip6ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
 	int err = 0;
 	struct ip6_tnl_parm p;
@@ -931,12 +1196,12 @@
 
 	switch (cmd) {
 	case SIOCGETTUNNEL:
-		if (dev == ip6ip6_fb_tnl_dev) {
+		if (dev == ip6_fb_tnl_dev) {
 			if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) {
 				err = -EFAULT;
 				break;
 			}
-			t = ip6ip6_tnl_locate(&p, 0);
+			t = ip6_tnl_locate(&p, 0);
 		}
 		if (t == NULL)
 			t = netdev_priv(dev);
@@ -954,10 +1219,11 @@
 		if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p)))
 			break;
 		err = -EINVAL;
-		if (p.proto != IPPROTO_IPV6)
+		if (p.proto != IPPROTO_IPV6 && p.proto != IPPROTO_IPIP &&
+		    p.proto != 0)
 			break;
-		t = ip6ip6_tnl_locate(&p, cmd == SIOCADDTUNNEL);
-		if (dev != ip6ip6_fb_tnl_dev && cmd == SIOCCHGTUNNEL) {
+		t = ip6_tnl_locate(&p, cmd == SIOCADDTUNNEL);
+		if (dev != ip6_fb_tnl_dev && cmd == SIOCCHGTUNNEL) {
 			if (t != NULL) {
 				if (t->dev != dev) {
 					err = -EEXIST;
@@ -966,9 +1232,9 @@
 			} else
 				t = netdev_priv(dev);
 
-			ip6ip6_tnl_unlink(t);
-			err = ip6ip6_tnl_change(t, &p);
-			ip6ip6_tnl_link(t);
+			ip6_tnl_unlink(t);
+			err = ip6_tnl_change(t, &p);
+			ip6_tnl_link(t);
 			netdev_state_change(dev);
 		}
 		if (t) {
@@ -984,15 +1250,15 @@
 		if (!capable(CAP_NET_ADMIN))
 			break;
 
-		if (dev == ip6ip6_fb_tnl_dev) {
+		if (dev == ip6_fb_tnl_dev) {
 			err = -EFAULT;
 			if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p)))
 				break;
 			err = -ENOENT;
-			if ((t = ip6ip6_tnl_locate(&p, 0)) == NULL)
+			if ((t = ip6_tnl_locate(&p, 0)) == NULL)
 				break;
 			err = -EPERM;
-			if (t->dev == ip6ip6_fb_tnl_dev)
+			if (t->dev == ip6_fb_tnl_dev)
 				break;
 			dev = t->dev;
 		}
@@ -1006,20 +1272,20 @@
 }
 
 /**
- * ip6ip6_tnl_get_stats - return the stats for tunnel device
+ * ip6_tnl_get_stats - return the stats for tunnel device
  *   @dev: virtual device associated with tunnel
  *
  * Return: stats for device
  **/
 
 static struct net_device_stats *
-ip6ip6_tnl_get_stats(struct net_device *dev)
+ip6_tnl_get_stats(struct net_device *dev)
 {
 	return &(((struct ip6_tnl *)netdev_priv(dev))->stat);
 }
 
 /**
- * ip6ip6_tnl_change_mtu - change mtu manually for tunnel device
+ * ip6_tnl_change_mtu - change mtu manually for tunnel device
  *   @dev: virtual device associated with tunnel
  *   @new_mtu: the new mtu
  *
@@ -1029,7 +1295,7 @@
  **/
 
 static int
-ip6ip6_tnl_change_mtu(struct net_device *dev, int new_mtu)
+ip6_tnl_change_mtu(struct net_device *dev, int new_mtu)
 {
 	if (new_mtu < IPV6_MIN_MTU) {
 		return -EINVAL;
@@ -1039,22 +1305,22 @@
 }
 
 /**
- * ip6ip6_tnl_dev_setup - setup virtual tunnel device
+ * ip6_tnl_dev_setup - setup virtual tunnel device
  *   @dev: virtual device associated with tunnel
  *
  * Description:
  *   Initialize function pointers and device parameters
  **/
 
-static void ip6ip6_tnl_dev_setup(struct net_device *dev)
+static void ip6_tnl_dev_setup(struct net_device *dev)
 {
 	SET_MODULE_OWNER(dev);
-	dev->uninit = ip6ip6_tnl_dev_uninit;
+	dev->uninit = ip6_tnl_dev_uninit;
 	dev->destructor = free_netdev;
-	dev->hard_start_xmit = ip6ip6_tnl_xmit;
-	dev->get_stats = ip6ip6_tnl_get_stats;
-	dev->do_ioctl = ip6ip6_tnl_ioctl;
-	dev->change_mtu = ip6ip6_tnl_change_mtu;
+	dev->hard_start_xmit = ip6_tnl_xmit;
+	dev->get_stats = ip6_tnl_get_stats;
+	dev->do_ioctl = ip6_tnl_ioctl;
+	dev->change_mtu = ip6_tnl_change_mtu;
 
 	dev->type = ARPHRD_TUNNEL6;
 	dev->hard_header_len = LL_MAX_HEADER + sizeof (struct ipv6hdr);
@@ -1065,50 +1331,56 @@
 
 
 /**
- * ip6ip6_tnl_dev_init_gen - general initializer for all tunnel devices
+ * ip6_tnl_dev_init_gen - general initializer for all tunnel devices
  *   @dev: virtual device associated with tunnel
  **/
 
 static inline void
-ip6ip6_tnl_dev_init_gen(struct net_device *dev)
+ip6_tnl_dev_init_gen(struct net_device *dev)
 {
 	struct ip6_tnl *t = netdev_priv(dev);
-	t->fl.proto = IPPROTO_IPV6;
 	t->dev = dev;
 	strcpy(t->parms.name, dev->name);
 }
 
 /**
- * ip6ip6_tnl_dev_init - initializer for all non fallback tunnel devices
+ * ip6_tnl_dev_init - initializer for all non fallback tunnel devices
  *   @dev: virtual device associated with tunnel
  **/
 
 static int
-ip6ip6_tnl_dev_init(struct net_device *dev)
+ip6_tnl_dev_init(struct net_device *dev)
 {
 	struct ip6_tnl *t = netdev_priv(dev);
-	ip6ip6_tnl_dev_init_gen(dev);
-	ip6ip6_tnl_link_config(t);
+	ip6_tnl_dev_init_gen(dev);
+	ip6_tnl_link_config(t);
 	return 0;
 }
 
 /**
- * ip6ip6_fb_tnl_dev_init - initializer for fallback tunnel device
+ * ip6_fb_tnl_dev_init - initializer for fallback tunnel device
  *   @dev: fallback device
  *
  * Return: 0
  **/
 
 static int
-ip6ip6_fb_tnl_dev_init(struct net_device *dev)
+ip6_fb_tnl_dev_init(struct net_device *dev)
 {
 	struct ip6_tnl *t = netdev_priv(dev);
-	ip6ip6_tnl_dev_init_gen(dev);
+	ip6_tnl_dev_init_gen(dev);
+	t->parms.proto = IPPROTO_IPV6;
 	dev_hold(dev);
 	tnls_wc[0] = t;
 	return 0;
 }
 
+static struct xfrm6_tunnel ip4ip6_handler = {
+	.handler	= ip4ip6_rcv,
+	.err_handler	= ip4ip6_err,
+	.priority	=	1,
+};
+
 static struct xfrm6_tunnel ip6ip6_handler = {
 	.handler	= ip6ip6_rcv,
 	.err_handler	= ip6ip6_err,
@@ -1125,30 +1397,40 @@
 {
 	int  err;
 
-	if (xfrm6_tunnel_register(&ip6ip6_handler, AF_INET6)) {
-		printk(KERN_ERR "ip6ip6 init: can't register tunnel\n");
-		return -EAGAIN;
+	if (xfrm6_tunnel_register(&ip4ip6_handler, AF_INET)) {
+		printk(KERN_ERR "ip6_tunnel init: can't register ip4ip6\n");
+		err = -EAGAIN;
+		goto out;
 	}
-	ip6ip6_fb_tnl_dev = alloc_netdev(sizeof(struct ip6_tnl), "ip6tnl0",
-					 ip6ip6_tnl_dev_setup);
 
-	if (!ip6ip6_fb_tnl_dev) {
+	if (xfrm6_tunnel_register(&ip6ip6_handler, AF_INET6)) {
+		printk(KERN_ERR "ip6_tunnel init: can't register ip6ip6\n");
+		err = -EAGAIN;
+		goto unreg_ip4ip6;
+	}
+	ip6_fb_tnl_dev = alloc_netdev(sizeof(struct ip6_tnl), "ip6tnl0",
+				      ip6_tnl_dev_setup);
+
+	if (!ip6_fb_tnl_dev) {
 		err = -ENOMEM;
 		goto fail;
 	}
-	ip6ip6_fb_tnl_dev->init = ip6ip6_fb_tnl_dev_init;
+	ip6_fb_tnl_dev->init = ip6_fb_tnl_dev_init;
 
-	if ((err = register_netdev(ip6ip6_fb_tnl_dev))) {
-		free_netdev(ip6ip6_fb_tnl_dev);
+	if ((err = register_netdev(ip6_fb_tnl_dev))) {
+		free_netdev(ip6_fb_tnl_dev);
 		goto fail;
 	}
 	return 0;
 fail:
 	xfrm6_tunnel_deregister(&ip6ip6_handler, AF_INET6);
+unreg_ip4ip6:
+	xfrm6_tunnel_deregister(&ip4ip6_handler, AF_INET);
+out:
 	return err;
 }
 
-static void __exit ip6ip6_destroy_tunnels(void)
+static void __exit ip6_tnl_destroy_tunnels(void)
 {
 	int h;
 	struct ip6_tnl *t;
@@ -1168,11 +1450,14 @@
 
 static void __exit ip6_tunnel_cleanup(void)
 {
+	if (xfrm6_tunnel_deregister(&ip4ip6_handler, AF_INET))
+		printk(KERN_INFO "ip6_tunnel close: can't deregister ip4ip6\n");
+
 	if (xfrm6_tunnel_deregister(&ip6ip6_handler, AF_INET6))
-		printk(KERN_INFO "ip6ip6 close: can't deregister tunnel\n");
+		printk(KERN_INFO "ip6_tunnel close: can't deregister ip6ip6\n");
 
 	rtnl_lock();
-	ip6ip6_destroy_tunnels();
+	ip6_tnl_destroy_tunnels();
 	rtnl_unlock();
 }
 
diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c
index 5724ba9..1ee50b5 100644
--- a/net/ipv6/ipcomp6.c
+++ b/net/ipv6/ipcomp6.c
@@ -79,9 +79,9 @@
 	skb->ip_summed = CHECKSUM_NONE;
 
 	/* Remove ipcomp header and decompress original payload */
-	iph = skb->nh.ipv6h;
+	iph = ipv6_hdr(skb);
 	ipch = (void *)skb->data;
-	skb->h.raw = skb->nh.raw + sizeof(*ipch);
+	skb->transport_header = skb->network_header + sizeof(*ipch);
 	__skb_pull(skb, sizeof(*ipch));
 
 	/* decompression */
@@ -111,7 +111,7 @@
 
 	skb->truesize += dlen - plen;
 	__skb_put(skb, dlen - plen);
-	memcpy(skb->data, scratch, dlen);
+	skb_copy_to_linear_data(skb, scratch, dlen);
 	err = ipch->nexthdr;
 
 out_put_cpu:
@@ -124,15 +124,13 @@
 {
 	int err;
 	struct ipv6hdr *top_iph;
-	int hdr_len;
 	struct ipv6_comp_hdr *ipch;
 	struct ipcomp_data *ipcd = x->data;
 	int plen, dlen;
 	u8 *start, *scratch;
 	struct crypto_comp *tfm;
 	int cpu;
-
-	hdr_len = skb->h.raw - skb->data;
+	int hdr_len = skb_transport_offset(skb);
 
 	/* check whether datagram len is larger than threshold */
 	if ((skb->len - hdr_len) < ipcd->threshold) {
@@ -145,7 +143,7 @@
 	/* compression */
 	plen = skb->len - hdr_len;
 	dlen = IPCOMP_SCRATCH_SIZE;
-	start = skb->h.raw;
+	start = skb_transport_header(skb);
 
 	cpu = get_cpu();
 	scratch = *per_cpu_ptr(ipcomp6_scratches, cpu);
@@ -166,10 +164,10 @@
 	top_iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
 
 	ipch = (struct ipv6_comp_hdr *)start;
-	ipch->nexthdr = *skb->nh.raw;
+	ipch->nexthdr = *skb_network_header(skb);
 	ipch->flags = 0;
 	ipch->cpi = htons((u16 )ntohl(x->id.spi));
-	*skb->nh.raw = IPPROTO_COMP;
+	*skb_network_header(skb) = IPPROTO_COMP;
 
 out_ok:
 	return 0;
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index f5f9582..aa3d07c 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -101,14 +101,14 @@
 	if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
 		goto out;
 
-	ipv6h = skb->nh.ipv6h;
+	ipv6h = ipv6_hdr(skb);
 	__skb_pull(skb, sizeof(*ipv6h));
 	err = -EPROTONOSUPPORT;
 
 	rcu_read_lock();
 	ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr);
 	if (likely(ops && ops->gso_send_check)) {
-		skb->h.raw = skb->data;
+		skb_reset_transport_header(skb);
 		err = ops->gso_send_check(skb);
 	}
 	rcu_read_unlock();
@@ -137,14 +137,14 @@
 	if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
 		goto out;
 
-	ipv6h = skb->nh.ipv6h;
+	ipv6h = ipv6_hdr(skb);
 	__skb_pull(skb, sizeof(*ipv6h));
 	segs = ERR_PTR(-EPROTONOSUPPORT);
 
 	rcu_read_lock();
 	ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr);
 	if (likely(ops && ops->gso_segment)) {
-		skb->h.raw = skb->data;
+		skb_reset_transport_header(skb);
 		segs = ops->gso_segment(skb, features);
 	}
 	rcu_read_unlock();
@@ -153,7 +153,7 @@
 		goto out;
 
 	for (skb = segs; skb; skb = skb->next) {
-		ipv6h = skb->nh.ipv6h;
+		ipv6h = ipv6_hdr(skb);
 		ipv6h->payload_len = htons(skb->len - skb->mac_len -
 					   sizeof(*ipv6h));
 	}
@@ -694,7 +694,7 @@
 		retv = ip6_ra_control(sk, val, NULL);
 		break;
 	case IPV6_MTU_DISCOVER:
-		if (val<0 || val>2)
+		if (val<0 || val>3)
 			goto e_inval;
 		np->pmtudisc = val;
 		retv = 0;
@@ -761,6 +761,7 @@
 	return err;
 }
 
+EXPORT_SYMBOL(ipv6_setsockopt);
 
 #ifdef CONFIG_COMPAT
 int compat_ipv6_setsockopt(struct sock *sk, int level, int optname,
@@ -796,18 +797,37 @@
 #endif
 
 static int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_txoptions *opt,
-				  char __user *optval, int len)
+				  int optname, char __user *optval, int len)
 {
 	struct ipv6_opt_hdr *hdr;
 
-	if (!opt || !opt->hopopt)
+	if (!opt)
 		return 0;
-	hdr = opt->hopopt;
+
+	switch(optname) {
+	case IPV6_HOPOPTS:
+		hdr = opt->hopopt;
+		break;
+	case IPV6_RTHDRDSTOPTS:
+		hdr = opt->dst0opt;
+		break;
+	case IPV6_RTHDR:
+		hdr = (struct ipv6_opt_hdr *)opt->srcrt;
+		break;
+	case IPV6_DSTOPTS:
+		hdr = opt->dst1opt;
+		break;
+	default:
+		return -EINVAL;	/* should not happen */
+	}
+
+	if (!hdr)
+		return 0;
 
 	len = min_t(unsigned int, len, ipv6_optlen(hdr));
-	if (copy_to_user(optval, hdr, ipv6_optlen(hdr)))
+	if (copy_to_user(optval, hdr, len));
 		return -EFAULT;
-	return len;
+	return ipv6_optlen(hdr);
 }
 
 static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
@@ -945,7 +965,7 @@
 
 		lock_sock(sk);
 		len = ipv6_getsockopt_sticky(sk, np->opt,
-					     optval, len);
+					     optname, optval, len);
 		release_sock(sk);
 		return put_user(len, optlen);
 	}
@@ -1066,6 +1086,8 @@
 	return err;
 }
 
+EXPORT_SYMBOL(ipv6_getsockopt);
+
 #ifdef CONFIG_COMPAT
 int compat_ipv6_getsockopt(struct sock *sk, int level, int optname,
 			   char __user *optval, int __user *optlen)
diff --git a/net/ipv6/ipv6_syms.c b/net/ipv6/ipv6_syms.c
deleted file mode 100644
index e12e3d4..0000000
--- a/net/ipv6/ipv6_syms.c
+++ /dev/null
@@ -1,36 +0,0 @@
-
-#include <linux/module.h>
-#include <net/protocol.h>
-#include <net/ipv6.h>
-#include <net/addrconf.h>
-#include <net/ip6_route.h>
-#include <net/xfrm.h>
-
-EXPORT_SYMBOL(icmpv6_send);
-EXPORT_SYMBOL(icmpv6_statistics);
-EXPORT_SYMBOL(icmpv6_err_convert);
-EXPORT_SYMBOL(ndisc_mc_map);
-EXPORT_SYMBOL(register_inet6addr_notifier);
-EXPORT_SYMBOL(unregister_inet6addr_notifier);
-EXPORT_SYMBOL(ip6_route_output);
-EXPORT_SYMBOL(ipv6_setsockopt);
-EXPORT_SYMBOL(ipv6_getsockopt);
-EXPORT_SYMBOL(inet6_register_protosw);
-EXPORT_SYMBOL(inet6_unregister_protosw);
-EXPORT_SYMBOL(inet6_add_protocol);
-EXPORT_SYMBOL(inet6_del_protocol);
-EXPORT_SYMBOL(ip6_xmit);
-EXPORT_SYMBOL(inet6_release);
-EXPORT_SYMBOL(inet6_bind);
-EXPORT_SYMBOL(inet6_getname);
-EXPORT_SYMBOL(inet6_ioctl);
-EXPORT_SYMBOL(ipv6_get_saddr);
-EXPORT_SYMBOL(ipv6_chk_addr);
-EXPORT_SYMBOL(in6_dev_finish_destroy);
-#ifdef CONFIG_XFRM
-EXPORT_SYMBOL(xfrm6_rcv);
-EXPORT_SYMBOL(xfrm6_input_addr);
-EXPORT_SYMBOL(xfrm6_find_1stfragopt);
-#endif
-EXPORT_SYMBOL(rt6_lookup);
-EXPORT_SYMBOL(ipv6_push_nfrag_opts);
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index a8d6625..6c27589 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -988,7 +988,7 @@
 	if (!pskb_may_pull(skb, sizeof(struct icmp6hdr)))
 		return 0;
 
-	pic = (struct icmp6hdr *)skb->h.raw;
+	pic = icmp6_hdr(skb);
 
 	switch (pic->icmp6_type) {
 	case ICMPV6_MGM_QUERY:
@@ -1167,11 +1167,11 @@
 		return -EINVAL;
 
 	/* compute payload length excluding extension headers */
-	len = ntohs(skb->nh.ipv6h->payload_len) + sizeof(struct ipv6hdr);
-	len -= (char *)skb->h.raw - (char *)skb->nh.ipv6h;
+	len = ntohs(ipv6_hdr(skb)->payload_len) + sizeof(struct ipv6hdr);
+	len -= skb_network_header_len(skb);
 
 	/* Drop queries with not link local source */
-	if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr)&IPV6_ADDR_LINKLOCAL))
+	if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL))
 		return -EINVAL;
 
 	idev = in6_dev_get(skb->dev);
@@ -1179,7 +1179,7 @@
 	if (idev == NULL)
 		return 0;
 
-	hdr = (struct icmp6hdr *) skb->h.raw;
+	hdr = icmp6_hdr(skb);
 	group = (struct in6_addr *) (hdr + 1);
 	group_type = ipv6_addr_type(group);
 
@@ -1212,7 +1212,7 @@
 			in6_dev_put(idev);
 			return -EINVAL;
 		}
-		mlh2 = (struct mld2_query *) skb->h.raw;
+		mlh2 = (struct mld2_query *)skb_transport_header(skb);
 		max_delay = (MLDV2_MRC(ntohs(mlh2->mrc))*HZ)/1000;
 		if (!max_delay)
 			max_delay = 1;
@@ -1235,7 +1235,7 @@
 				in6_dev_put(idev);
 				return -EINVAL;
 			}
-			mlh2 = (struct mld2_query *) skb->h.raw;
+			mlh2 = (struct mld2_query *)skb_transport_header(skb);
 			mark = 1;
 		}
 	} else {
@@ -1300,10 +1300,10 @@
 	if (!pskb_may_pull(skb, sizeof(struct in6_addr)))
 		return -EINVAL;
 
-	hdr = (struct icmp6hdr*) skb->h.raw;
+	hdr = icmp6_hdr(skb);
 
 	/* Drop reports with not link local source */
-	addr_type = ipv6_addr_type(&skb->nh.ipv6h->saddr);
+	addr_type = ipv6_addr_type(&ipv6_hdr(skb)->saddr);
 	if (addr_type != IPV6_ADDR_ANY &&
 	    !(addr_type&IPV6_ADDR_LINKLOCAL))
 		return -EINVAL;
@@ -1411,7 +1411,7 @@
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address
 		 * when a valid link-local address is not available.
@@ -1423,8 +1423,9 @@
 
 	memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra));
 
-	pmr =(struct mld2_report *)skb_put(skb, sizeof(*pmr));
-	skb->h.raw = (unsigned char *)pmr;
+	skb_set_transport_header(skb, skb_tail_pointer(skb) - skb->data);
+	skb_put(skb, sizeof(*pmr));
+	pmr = (struct mld2_report *)skb_transport_header(skb);
 	pmr->type = ICMPV6_MLD2_REPORT;
 	pmr->resv1 = 0;
 	pmr->csum = 0;
@@ -1441,7 +1442,7 @@
 		unsigned char ha[MAX_ADDR_LEN];
 		int err;
 
-		ndisc_mc_map(&skb->nh.ipv6h->daddr, ha, dev, 1);
+		ndisc_mc_map(&ipv6_hdr(skb)->daddr, ha, dev, 1);
 		err = dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, skb->len);
 		if (err < 0) {
 			kfree_skb(skb);
@@ -1459,20 +1460,21 @@
 
 static void mld_sendpack(struct sk_buff *skb)
 {
-	struct ipv6hdr *pip6 = skb->nh.ipv6h;
-	struct mld2_report *pmr = (struct mld2_report *)skb->h.raw;
+	struct ipv6hdr *pip6 = ipv6_hdr(skb);
+	struct mld2_report *pmr =
+			      (struct mld2_report *)skb_transport_header(skb);
 	int payload_len, mldlen;
 	struct inet6_dev *idev = in6_dev_get(skb->dev);
 	int err;
 
 	IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
-	payload_len = skb->tail - (unsigned char *)skb->nh.ipv6h -
-		sizeof(struct ipv6hdr);
-	mldlen = skb->tail - skb->h.raw;
+	payload_len = (skb->tail - skb->network_header) - sizeof(*pip6);
+	mldlen = skb->tail - skb->transport_header;
 	pip6->payload_len = htons(payload_len);
 
 	pmr->csum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen,
-		IPPROTO_ICMPV6, csum_partial(skb->h.raw, mldlen, 0));
+		IPPROTO_ICMPV6, csum_partial(skb_transport_header(skb),
+					     mldlen, 0));
 	err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dev,
 		mld_dev_queue_xmit);
 	if (!err) {
@@ -1506,7 +1508,7 @@
 	pgr->grec_auxwords = 0;
 	pgr->grec_nsrcs = 0;
 	pgr->grec_mca = pmc->mca_addr;	/* structure copy */
-	pmr = (struct mld2_report *)skb->h.raw;
+	pmr = (struct mld2_report *)skb_transport_header(skb);
 	pmr->ngrec = htons(ntohs(pmr->ngrec)+1);
 	*ppgr = pgr;
 	return skb;
@@ -1539,7 +1541,7 @@
 	if (!*psf_list)
 		goto empty_source;
 
-	pmr = skb ? (struct mld2_report *)skb->h.raw : NULL;
+	pmr = skb ? (struct mld2_report *)skb_transport_header(skb) : NULL;
 
 	/* EX and TO_EX get a fresh packet, if needed */
 	if (truncate) {
@@ -1791,7 +1793,7 @@
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address
 		 * when a valid link-local address is not available.
diff --git a/net/ipv6/mip6.c b/net/ipv6/mip6.c
index 0afcabd..13b7160 100644
--- a/net/ipv6/mip6.c
+++ b/net/ipv6/mip6.c
@@ -90,23 +90,26 @@
 {
 	struct ip6_mh *mh;
 
-	if (!pskb_may_pull(skb, (skb->h.raw - skb->data) + 8) ||
-	    !pskb_may_pull(skb, (skb->h.raw - skb->data) + ((skb->h.raw[1] + 1) << 3)))
+	if (!pskb_may_pull(skb, (skb_transport_offset(skb)) + 8) ||
+	    !pskb_may_pull(skb, (skb_transport_offset(skb) +
+				 ((skb_transport_header(skb)[1] + 1) << 3))))
 		return -1;
 
-	mh = (struct ip6_mh *)skb->h.raw;
+	mh = (struct ip6_mh *)skb_transport_header(skb);
 
 	if (mh->ip6mh_hdrlen < mip6_mh_len(mh->ip6mh_type)) {
 		LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH message too short: %d vs >=%d\n",
 			       mh->ip6mh_hdrlen, mip6_mh_len(mh->ip6mh_type));
-		mip6_param_prob(skb, 0, (&mh->ip6mh_hdrlen) - skb->nh.raw);
+		mip6_param_prob(skb, 0, ((&mh->ip6mh_hdrlen) -
+					 skb_network_header(skb)));
 		return -1;
 	}
 
 	if (mh->ip6mh_proto != IPPROTO_NONE) {
 		LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH invalid payload proto = %d\n",
 			       mh->ip6mh_proto);
-		mip6_param_prob(skb, 0, (&mh->ip6mh_proto) - skb->nh.raw);
+		mip6_param_prob(skb, 0, ((&mh->ip6mh_proto) -
+					 skb_network_header(skb)));
 		return -1;
 	}
 
@@ -122,12 +125,12 @@
 };
 
 static struct mip6_report_rate_limiter mip6_report_rl = {
-	.lock = SPIN_LOCK_UNLOCKED
+	.lock = __SPIN_LOCK_UNLOCKED(mip6_report_rl.lock)
 };
 
 static int mip6_destopt_input(struct xfrm_state *x, struct sk_buff *skb)
 {
-	struct ipv6hdr *iph = skb->nh.ipv6h;
+	struct ipv6hdr *iph = ipv6_hdr(skb);
 	struct ipv6_destopt_hdr *destopt = (struct ipv6_destopt_hdr *)skb->data;
 
 	if (!ipv6_addr_equal(&iph->saddr, (struct in6_addr *)x->coaddr) &&
@@ -152,10 +155,10 @@
 	iph = (struct ipv6hdr *)skb->data;
 	iph->payload_len = htons(skb->len - sizeof(*iph));
 
-	nexthdr = *skb->nh.raw;
-	*skb->nh.raw = IPPROTO_DSTOPTS;
+	nexthdr = *skb_network_header(skb);
+	*skb_network_header(skb) = IPPROTO_DSTOPTS;
 
-	dstopt = (struct ipv6_destopt_hdr *)skb->h.raw;
+	dstopt = (struct ipv6_destopt_hdr *)skb_transport_header(skb);
 	dstopt->nexthdr = nexthdr;
 
 	hao = mip6_padn((char *)(dstopt + 1),
@@ -215,21 +218,22 @@
 	if (likely(opt->dsthao)) {
 		offset = ipv6_find_tlv(skb, opt->dsthao, IPV6_TLV_HAO);
 		if (likely(offset >= 0))
-			hao = (struct ipv6_destopt_hao *)(skb->nh.raw + offset);
+			hao = (struct ipv6_destopt_hao *)
+					(skb_network_header(skb) + offset);
 	}
 
 	skb_get_timestamp(skb, &stamp);
 
-	if (!mip6_report_rl_allow(&stamp, &skb->nh.ipv6h->daddr,
-				  hao ? &hao->addr : &skb->nh.ipv6h->saddr,
+	if (!mip6_report_rl_allow(&stamp, &ipv6_hdr(skb)->daddr,
+				  hao ? &hao->addr : &ipv6_hdr(skb)->saddr,
 				  opt->iif))
 		goto out;
 
 	memset(&sel, 0, sizeof(sel));
-	memcpy(&sel.daddr, (xfrm_address_t *)&skb->nh.ipv6h->daddr,
+	memcpy(&sel.daddr, (xfrm_address_t *)&ipv6_hdr(skb)->daddr,
 	       sizeof(sel.daddr));
 	sel.prefixlen_d = 128;
-	memcpy(&sel.saddr, (xfrm_address_t *)&skb->nh.ipv6h->saddr,
+	memcpy(&sel.saddr, (xfrm_address_t *)&ipv6_hdr(skb)->saddr,
 	       sizeof(sel.saddr));
 	sel.prefixlen_s = 128;
 	sel.family = AF_INET6;
@@ -253,11 +257,13 @@
 			       u8 **nexthdr)
 {
 	u16 offset = sizeof(struct ipv6hdr);
-	struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.ipv6h + 1);
-	unsigned int packet_len = skb->tail - skb->nh.raw;
+	struct ipv6_opt_hdr *exthdr =
+				   (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1);
+	const unsigned char *nh = skb_network_header(skb);
+	unsigned int packet_len = skb->tail - skb->network_header;
 	int found_rhdr = 0;
 
-	*nexthdr = &skb->nh.ipv6h->nexthdr;
+	*nexthdr = &ipv6_hdr(skb)->nexthdr;
 
 	while (offset + 1 <= packet_len) {
 
@@ -288,7 +294,7 @@
 
 		offset += ipv6_optlen(exthdr);
 		*nexthdr = &exthdr->nexthdr;
-		exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
+		exthdr = (struct ipv6_opt_hdr *)(nh + offset);
 	}
 
 	return offset;
@@ -361,10 +367,10 @@
 	iph = (struct ipv6hdr *)skb->data;
 	iph->payload_len = htons(skb->len - sizeof(*iph));
 
-	nexthdr = *skb->nh.raw;
-	*skb->nh.raw = IPPROTO_ROUTING;
+	nexthdr = *skb_network_header(skb);
+	*skb_network_header(skb) = IPPROTO_ROUTING;
 
-	rt2 = (struct rt2_hdr *)skb->h.raw;
+	rt2 = (struct rt2_hdr *)skb_transport_header(skb);
 	rt2->rt_hdr.nexthdr = nexthdr;
 	rt2->rt_hdr.hdrlen = (x->props.header_len >> 3) - 1;
 	rt2->rt_hdr.type = IPV6_SRCRT_TYPE_2;
@@ -383,11 +389,13 @@
 			     u8 **nexthdr)
 {
 	u16 offset = sizeof(struct ipv6hdr);
-	struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.ipv6h + 1);
-	unsigned int packet_len = skb->tail - skb->nh.raw;
+	struct ipv6_opt_hdr *exthdr =
+				   (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1);
+	const unsigned char *nh = skb_network_header(skb);
+	unsigned int packet_len = skb->tail - skb->network_header;
 	int found_rhdr = 0;
 
-	*nexthdr = &skb->nh.ipv6h->nexthdr;
+	*nexthdr = &ipv6_hdr(skb)->nexthdr;
 
 	while (offset + 1 <= packet_len) {
 
@@ -397,7 +405,7 @@
 		case NEXTHDR_ROUTING:
 			if (offset + 3 <= packet_len) {
 				struct ipv6_rt_hdr *rt;
-				rt = (struct ipv6_rt_hdr *)(skb->nh.raw + offset);
+				rt = (struct ipv6_rt_hdr *)(nh + offset);
 				if (rt->type != 0)
 					return offset;
 			}
@@ -417,7 +425,7 @@
 
 		offset += ipv6_optlen(exthdr);
 		*nexthdr = &exthdr->nexthdr;
-		exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
+		exthdr = (struct ipv6_opt_hdr *)(nh + offset);
 	}
 
 	return offset;
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 121f31c..d8b3645 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -319,6 +319,8 @@
 	return -EINVAL;
 }
 
+EXPORT_SYMBOL(ndisc_mc_map);
+
 static u32 ndisc_hash(const void *pkey, const struct net_device *dev)
 {
 	const u32 *p32 = pkey;
@@ -425,28 +427,108 @@
 	security_sk_classify_flow(ndisc_socket->sk, fl);
 }
 
+static void __ndisc_send(struct net_device *dev,
+			 struct neighbour *neigh,
+			 struct in6_addr *daddr, struct in6_addr *saddr,
+			 struct icmp6hdr *icmp6h, struct in6_addr *target,
+			 int llinfo, int icmp6_mib_outnd)
+{
+	struct flowi fl;
+	struct dst_entry *dst;
+	struct sock *sk = ndisc_socket->sk;
+	struct sk_buff *skb;
+	struct icmp6hdr *hdr;
+	struct inet6_dev *idev;
+	int len;
+	int err;
+	u8 *opt;
+
+	ndisc_flow_init(&fl, icmp6h->icmp6_type, saddr, daddr,
+			dev->ifindex);
+
+	dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output);
+	if (!dst)
+		return;
+
+	err = xfrm_lookup(&dst, &fl, NULL, 0);
+	if (err < 0)
+		return;
+
+	if (!dev->addr_len)
+		llinfo = 0;
+
+	len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0);
+	if (llinfo)
+		len += ndisc_opt_addr_space(dev);
+
+	skb = sock_alloc_send_skb(sk,
+				  (MAX_HEADER + sizeof(struct ipv6hdr) +
+				   len + LL_RESERVED_SPACE(dev)),
+				  1, &err);
+	if (!skb) {
+		ND_PRINTK0(KERN_ERR
+			   "ICMPv6 ND: %s() failed to allocate an skb.\n",
+			   __FUNCTION__);
+		dst_release(dst);
+		return;
+	}
+
+	skb_reserve(skb, LL_RESERVED_SPACE(dev));
+	ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
+
+	skb->transport_header = skb->tail;
+	skb_put(skb, len);
+
+	hdr = (struct icmp6hdr *)skb_transport_header(skb);
+	memcpy(hdr, icmp6h, sizeof(*hdr));
+
+	opt = skb_transport_header(skb) + sizeof(struct icmp6hdr);
+	if (target) {
+		ipv6_addr_copy((struct in6_addr *)opt, target);
+		opt += sizeof(*target);
+	}
+
+	if (llinfo)
+		ndisc_fill_addr_option(opt, llinfo, dev->dev_addr,
+				       dev->addr_len, dev->type);
+
+	hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len,
+					   IPPROTO_ICMPV6,
+					   csum_partial((__u8 *) hdr,
+							len, 0));
+
+	skb->dst = dst;
+
+	idev = in6_dev_get(dst->dev);
+	IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
+
+	err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output);
+	if (!err) {
+		ICMP6_INC_STATS(idev, icmp6_mib_outnd);
+		ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);
+	}
+
+	if (likely(idev != NULL))
+		in6_dev_put(idev);
+}
+
 static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
 		   struct in6_addr *daddr, struct in6_addr *solicited_addr,
 		   int router, int solicited, int override, int inc_opt)
 {
 	struct in6_addr tmpaddr;
 	struct inet6_ifaddr *ifp;
-	struct inet6_dev *idev;
-	struct flowi fl;
-	struct dst_entry* dst;
-	struct sock *sk = ndisc_socket->sk;
 	struct in6_addr *src_addr;
-	struct nd_msg *msg;
-	int len;
-	struct sk_buff *skb;
-	int err;
-
-	len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
+	struct icmp6hdr icmp6h = {
+		.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
+	};
 
 	/* for anycast or proxy, solicited_addr != src_addr */
 	ifp = ipv6_get_ifaddr(solicited_addr, dev, 1);
 	if (ifp) {
 		src_addr = solicited_addr;
+		if (ifp->flags & IFA_F_OPTIMISTIC)
+			override = 0;
 		in6_ifa_put(ifp);
 	} else {
 		if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr))
@@ -454,237 +536,71 @@
 		src_addr = &tmpaddr;
 	}
 
-	ndisc_flow_init(&fl, NDISC_NEIGHBOUR_ADVERTISEMENT, src_addr, daddr,
-			dev->ifindex);
+	icmp6h.icmp6_router = router;
+	icmp6h.icmp6_solicited = solicited;
+	icmp6h.icmp6_override = override;
 
-	dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output);
-	if (!dst)
-		return;
-
-	err = xfrm_lookup(&dst, &fl, NULL, 0);
-	if (err < 0)
-		return;
-
-	if (inc_opt) {
-		if (dev->addr_len)
-			len += ndisc_opt_addr_space(dev);
-		else
-			inc_opt = 0;
-	}
-
-	skb = sock_alloc_send_skb(sk,
-				  (MAX_HEADER + sizeof(struct ipv6hdr) +
-				   len + LL_RESERVED_SPACE(dev)),
-				  1, &err);
-
-	if (skb == NULL) {
-		ND_PRINTK0(KERN_ERR
-			   "ICMPv6 NA: %s() failed to allocate an skb.\n",
-			   __FUNCTION__);
-		dst_release(dst);
-		return;
-	}
-
-	skb_reserve(skb, LL_RESERVED_SPACE(dev));
-	ip6_nd_hdr(sk, skb, dev, src_addr, daddr, IPPROTO_ICMPV6, len);
-
-	msg = (struct nd_msg *)skb_put(skb, len);
-	skb->h.raw = (unsigned char*)msg;
-
-	msg->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
-	msg->icmph.icmp6_code = 0;
-	msg->icmph.icmp6_cksum = 0;
-
-	msg->icmph.icmp6_unused = 0;
-	msg->icmph.icmp6_router    = router;
-	msg->icmph.icmp6_solicited = solicited;
-	msg->icmph.icmp6_override  = override;
-
-	/* Set the target address. */
-	ipv6_addr_copy(&msg->target, solicited_addr);
-
-	if (inc_opt)
-		ndisc_fill_addr_option(msg->opt, ND_OPT_TARGET_LL_ADDR, dev->dev_addr,
-				       dev->addr_len, dev->type);
-
-	/* checksum */
-	msg->icmph.icmp6_cksum = csum_ipv6_magic(src_addr, daddr, len,
-						 IPPROTO_ICMPV6,
-						 csum_partial((__u8 *) msg,
-							      len, 0));
-
-	skb->dst = dst;
-	idev = in6_dev_get(dst->dev);
-	IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
-	err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output);
-	if (!err) {
-		ICMP6_INC_STATS(idev, ICMP6_MIB_OUTNEIGHBORADVERTISEMENTS);
-		ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);
-	}
-
-	if (likely(idev != NULL))
-		in6_dev_put(idev);
+	__ndisc_send(dev, neigh, daddr, src_addr,
+		     &icmp6h, solicited_addr,
+		     inc_opt ? ND_OPT_TARGET_LL_ADDR : 0,
+		     ICMP6_MIB_OUTNEIGHBORADVERTISEMENTS);
 }
 
 void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
 		   struct in6_addr *solicit,
 		   struct in6_addr *daddr, struct in6_addr *saddr)
 {
-	struct flowi fl;
-	struct dst_entry* dst;
-	struct inet6_dev *idev;
-	struct sock *sk = ndisc_socket->sk;
-	struct sk_buff *skb;
-	struct nd_msg *msg;
 	struct in6_addr addr_buf;
-	int len;
-	int err;
-	int send_llinfo;
+	struct icmp6hdr icmp6h = {
+		.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
+	};
 
 	if (saddr == NULL) {
-		if (ipv6_get_lladdr(dev, &addr_buf))
+		if (ipv6_get_lladdr(dev, &addr_buf,
+				   (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
 			return;
 		saddr = &addr_buf;
 	}
 
-	ndisc_flow_init(&fl, NDISC_NEIGHBOUR_SOLICITATION, saddr, daddr,
-			dev->ifindex);
-
-	dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output);
-	if (!dst)
-		return;
-
-	err = xfrm_lookup(&dst, &fl, NULL, 0);
-	if (err < 0)
-		return;
-
-	len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
-	send_llinfo = dev->addr_len && !ipv6_addr_any(saddr);
-	if (send_llinfo)
-		len += ndisc_opt_addr_space(dev);
-
-	skb = sock_alloc_send_skb(sk,
-				  (MAX_HEADER + sizeof(struct ipv6hdr) +
-				   len + LL_RESERVED_SPACE(dev)),
-				  1, &err);
-	if (skb == NULL) {
-		ND_PRINTK0(KERN_ERR
-			   "ICMPv6 NA: %s() failed to allocate an skb.\n",
-			   __FUNCTION__);
-		dst_release(dst);
-		return;
-	}
-
-	skb_reserve(skb, LL_RESERVED_SPACE(dev));
-	ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
-
-	msg = (struct nd_msg *)skb_put(skb, len);
-	skb->h.raw = (unsigned char*)msg;
-	msg->icmph.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION;
-	msg->icmph.icmp6_code = 0;
-	msg->icmph.icmp6_cksum = 0;
-	msg->icmph.icmp6_unused = 0;
-
-	/* Set the target address. */
-	ipv6_addr_copy(&msg->target, solicit);
-
-	if (send_llinfo)
-		ndisc_fill_addr_option(msg->opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr,
-				       dev->addr_len, dev->type);
-
-	/* checksum */
-	msg->icmph.icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr,
-						 daddr, len,
-						 IPPROTO_ICMPV6,
-						 csum_partial((__u8 *) msg,
-							      len, 0));
-	/* send it! */
-	skb->dst = dst;
-	idev = in6_dev_get(dst->dev);
-	IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
-	err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output);
-	if (!err) {
-		ICMP6_INC_STATS(idev, ICMP6_MIB_OUTNEIGHBORSOLICITS);
-		ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);
-	}
-
-	if (likely(idev != NULL))
-		in6_dev_put(idev);
+	__ndisc_send(dev, neigh, daddr, saddr,
+		     &icmp6h, solicit,
+		     !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0,
+		     ICMP6_MIB_OUTNEIGHBORSOLICITS);
 }
 
 void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
 		   struct in6_addr *daddr)
 {
-	struct flowi fl;
-	struct dst_entry* dst;
-	struct inet6_dev *idev;
-	struct sock *sk = ndisc_socket->sk;
-	struct sk_buff *skb;
-	struct icmp6hdr *hdr;
-	__u8 * opt;
-	int len;
-	int err;
+	struct icmp6hdr icmp6h = {
+		.icmp6_type = NDISC_ROUTER_SOLICITATION,
+	};
+	int send_sllao = dev->addr_len;
 
-	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
-			dev->ifindex);
-
-	dst = ndisc_dst_alloc(dev, NULL, daddr, ip6_output);
-	if (!dst)
-		return;
-
-	err = xfrm_lookup(&dst, &fl, NULL, 0);
-	if (err < 0)
-		return;
-
-	len = sizeof(struct icmp6hdr);
-	if (dev->addr_len)
-		len += ndisc_opt_addr_space(dev);
-
-	skb = sock_alloc_send_skb(sk,
-				  (MAX_HEADER + sizeof(struct ipv6hdr) +
-				   len + LL_RESERVED_SPACE(dev)),
-				  1, &err);
-	if (skb == NULL) {
-		ND_PRINTK0(KERN_ERR
-			   "ICMPv6 RS: %s() failed to allocate an skb.\n",
-			   __FUNCTION__);
-		dst_release(dst);
-		return;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	/*
+	 * According to section 2.2 of RFC 4429, we must not
+	 * send router solicitations with a sllao from
+	 * optimistic addresses, but we may send the solicitation
+	 * if we don't include the sllao.  So here we check
+	 * if our address is optimistic, and if so, we
+	 * supress the inclusion of the sllao.
+	 */
+	if (send_sllao) {
+		struct inet6_ifaddr *ifp = ipv6_get_ifaddr(saddr, dev, 1);
+		if (ifp) {
+			if (ifp->flags & IFA_F_OPTIMISTIC)  {
+				send_sllao = 0;
+			}
+			in6_ifa_put(ifp);
+		} else {
+			send_sllao = 0;
+		}
 	}
-
-	skb_reserve(skb, LL_RESERVED_SPACE(dev));
-	ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
-
-	hdr = (struct icmp6hdr *)skb_put(skb, len);
-	skb->h.raw = (unsigned char*)hdr;
-	hdr->icmp6_type = NDISC_ROUTER_SOLICITATION;
-	hdr->icmp6_code = 0;
-	hdr->icmp6_cksum = 0;
-	hdr->icmp6_unused = 0;
-
-	opt = (u8*) (hdr + 1);
-
-	if (dev->addr_len)
-		ndisc_fill_addr_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr,
-				       dev->addr_len, dev->type);
-
-	/* checksum */
-	hdr->icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr, daddr, len,
-					   IPPROTO_ICMPV6,
-					   csum_partial((__u8 *) hdr, len, 0));
-
-	/* send it! */
-	skb->dst = dst;
-	idev = in6_dev_get(dst->dev);
-	IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
-	err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output);
-	if (!err) {
-		ICMP6_INC_STATS(idev, ICMP6_MIB_OUTROUTERSOLICITS);
-		ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);
-	}
-
-	if (likely(idev != NULL))
-		in6_dev_put(idev);
+#endif
+	__ndisc_send(dev, NULL, daddr, saddr,
+		     &icmp6h, NULL,
+		     send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0,
+		     ICMP6_MIB_OUTROUTERSOLICITS);
 }
 
 
@@ -708,8 +624,8 @@
 	struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
 	int probes = atomic_read(&neigh->probes);
 
-	if (skb && ipv6_chk_addr(&skb->nh.ipv6h->saddr, dev, 1))
-		saddr = &skb->nh.ipv6h->saddr;
+	if (skb && ipv6_chk_addr(&ipv6_hdr(skb)->saddr, dev, 1))
+		saddr = &ipv6_hdr(skb)->saddr;
 
 	if ((probes -= neigh->parms->ucast_probes) < 0) {
 		if (!(neigh->nud_state & NUD_VALID)) {
@@ -732,11 +648,12 @@
 
 static void ndisc_recv_ns(struct sk_buff *skb)
 {
-	struct nd_msg *msg = (struct nd_msg *)skb->h.raw;
-	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
-	struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
+	struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
+	struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
+	struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
 	u8 *lladdr = NULL;
-	u32 ndoptlen = skb->tail - msg->opt;
+	u32 ndoptlen = skb->tail - (skb->transport_header +
+				    offsetof(struct nd_msg, opt));
 	struct ndisc_options ndopts;
 	struct net_device *dev = skb->dev;
 	struct inet6_ifaddr *ifp;
@@ -796,28 +713,40 @@
 	inc = ipv6_addr_is_multicast(daddr);
 
 	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
-		if (ifp->flags & IFA_F_TENTATIVE) {
-			/* Address is tentative. If the source
-			   is unspecified address, it is someone
-			   does DAD, otherwise we ignore solicitations
-			   until DAD timer expires.
-			 */
-			if (!dad)
-				goto out;
-			if (dev->type == ARPHRD_IEEE802_TR) {
-				unsigned char *sadr = skb->mac.raw;
-				if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
-				    sadr[9] == dev->dev_addr[1] &&
-				    sadr[10] == dev->dev_addr[2] &&
-				    sadr[11] == dev->dev_addr[3] &&
-				    sadr[12] == dev->dev_addr[4] &&
-				    sadr[13] == dev->dev_addr[5]) {
-					/* looped-back to us */
-					goto out;
+
+		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
+			if (dad) {
+				if (dev->type == ARPHRD_IEEE802_TR) {
+					const unsigned char *sadr;
+					sadr = skb_mac_header(skb);
+					if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
+					    sadr[9] == dev->dev_addr[1] &&
+					    sadr[10] == dev->dev_addr[2] &&
+					    sadr[11] == dev->dev_addr[3] &&
+					    sadr[12] == dev->dev_addr[4] &&
+					    sadr[13] == dev->dev_addr[5]) {
+						/* looped-back to us */
+						goto out;
+					}
 				}
+
+				/*
+				 * We are colliding with another node
+				 * who is doing DAD
+				 * so fail our DAD process
+				 */
+				addrconf_dad_failure(ifp);
+				goto out;
+			} else {
+				/*
+				 * This is not a dad solicitation.
+				 * If we are an optimistic node,
+				 * we should respond.
+				 * Otherwise, we should ignore it.
+				 */
+				if (!(ifp->flags & IFA_F_OPTIMISTIC))
+					goto out;
 			}
-			addrconf_dad_failure(ifp);
-			return;
 		}
 
 		idev = ifp->idev;
@@ -898,11 +827,12 @@
 
 static void ndisc_recv_na(struct sk_buff *skb)
 {
-	struct nd_msg *msg = (struct nd_msg *)skb->h.raw;
-	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
-	struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
+	struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
+	struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
+	struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
 	u8 *lladdr = NULL;
-	u32 ndoptlen = skb->tail - msg->opt;
+	u32 ndoptlen = skb->tail - (skb->transport_header +
+				    offsetof(struct nd_msg, opt));
 	struct ndisc_options ndopts;
 	struct net_device *dev = skb->dev;
 	struct inet6_ifaddr *ifp;
@@ -1000,11 +930,11 @@
 
 static void ndisc_recv_rs(struct sk_buff *skb)
 {
-	struct rs_msg *rs_msg = (struct rs_msg *) skb->h.raw;
+	struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
 	unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
 	struct neighbour *neigh;
 	struct inet6_dev *idev;
-	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
+	struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
 	struct ndisc_options ndopts;
 	u8 *lladdr = NULL;
 
@@ -1057,7 +987,7 @@
 
 static void ndisc_router_discovery(struct sk_buff *skb)
 {
-	struct ra_msg *ra_msg = (struct ra_msg *) skb->h.raw;
+	struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
 	struct neighbour *neigh = NULL;
 	struct inet6_dev *in6_dev;
 	struct rt6_info *rt = NULL;
@@ -1068,9 +998,9 @@
 
 	__u8 * opt = (__u8 *)(ra_msg + 1);
 
-	optlen = (skb->tail - skb->h.raw) - sizeof(struct ra_msg);
+	optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);
 
-	if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL)) {
+	if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
 		ND_PRINTK2(KERN_WARNING
 			   "ICMPv6 RA: source address is not link-local.\n");
 		return;
@@ -1136,7 +1066,7 @@
 		pref = ICMPV6_ROUTER_PREF_MEDIUM;
 #endif
 
-	rt = rt6_get_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
+	rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
 
 	if (rt)
 		neigh = rt->rt6i_nexthop;
@@ -1151,7 +1081,7 @@
 		ND_PRINTK3(KERN_DEBUG
 			   "ICMPv6 RA: adding default router.\n");
 
-		rt = rt6_add_dflt_router(&skb->nh.ipv6h->saddr, skb->dev, pref);
+		rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
 		if (rt == NULL) {
 			ND_PRINTK0(KERN_ERR
 				   "ICMPv6 RA: %s() failed to add default route.\n",
@@ -1223,7 +1153,7 @@
 	 */
 
 	if (!neigh)
-		neigh = __neigh_lookup(&nd_tbl, &skb->nh.ipv6h->saddr,
+		neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
 				       skb->dev, 1);
 	if (neigh) {
 		u8 *lladdr = NULL;
@@ -1252,7 +1182,7 @@
 			if (((struct route_info *)p)->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
 				continue;
 			rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
-				      &skb->nh.ipv6h->saddr);
+				      &ipv6_hdr(skb)->saddr);
 		}
 	}
 #endif
@@ -1311,13 +1241,13 @@
 	int optlen;
 	u8 *lladdr = NULL;
 
-	if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL)) {
+	if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
 		ND_PRINTK2(KERN_WARNING
 			   "ICMPv6 Redirect: source address is not link-local.\n");
 		return;
 	}
 
-	optlen = skb->tail - skb->h.raw;
+	optlen = skb->tail - skb->transport_header;
 	optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
 
 	if (optlen < 0) {
@@ -1326,7 +1256,7 @@
 		return;
 	}
 
-	icmph = (struct icmp6hdr *) skb->h.raw;
+	icmph = icmp6_hdr(skb);
 	target = (struct in6_addr *) (icmph + 1);
 	dest = target + 1;
 
@@ -1376,8 +1306,8 @@
 
 	neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
 	if (neigh) {
-		rt6_redirect(dest, &skb->nh.ipv6h->daddr,
-			     &skb->nh.ipv6h->saddr, neigh, lladdr,
+		rt6_redirect(dest, &ipv6_hdr(skb)->daddr,
+			     &ipv6_hdr(skb)->saddr, neigh, lladdr,
 			     on_link);
 		neigh_release(neigh);
 	}
@@ -1406,21 +1336,21 @@
 
 	dev = skb->dev;
 
-	if (ipv6_get_lladdr(dev, &saddr_buf)) {
+	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
 		ND_PRINTK2(KERN_WARNING
 			   "ICMPv6 Redirect: no link-local address on %s\n",
 			   dev->name);
 		return;
 	}
 
-	if (!ipv6_addr_equal(&skb->nh.ipv6h->daddr, target) &&
+	if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
 	    !(ipv6_addr_type(target) & IPV6_ADDR_LINKLOCAL)) {
 		ND_PRINTK2(KERN_WARNING
 			"ICMPv6 Redirect: target address is not link-local.\n");
 		return;
 	}
 
-	ndisc_flow_init(&fl, NDISC_REDIRECT, &saddr_buf, &skb->nh.ipv6h->saddr,
+	ndisc_flow_init(&fl, NDISC_REDIRECT, &saddr_buf, &ipv6_hdr(skb)->saddr,
 			dev->ifindex);
 
 	dst = ip6_route_output(NULL, &fl);
@@ -1475,11 +1405,12 @@
 	hlen = 0;
 
 	skb_reserve(buff, LL_RESERVED_SPACE(dev));
-	ip6_nd_hdr(sk, buff, dev, &saddr_buf, &skb->nh.ipv6h->saddr,
+	ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,
 		   IPPROTO_ICMPV6, len);
 
-	icmph = (struct icmp6hdr *)skb_put(buff, len);
-	buff->h.raw = (unsigned char*)icmph;
+	skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data);
+	skb_put(buff, len);
+	icmph = icmp6_hdr(buff);
 
 	memset(icmph, 0, sizeof(struct icmp6hdr));
 	icmph->icmp6_type = NDISC_REDIRECT;
@@ -1491,7 +1422,7 @@
 	addrp = (struct in6_addr *)(icmph + 1);
 	ipv6_addr_copy(addrp, target);
 	addrp++;
-	ipv6_addr_copy(addrp, &skb->nh.ipv6h->daddr);
+	ipv6_addr_copy(addrp, &ipv6_hdr(skb)->daddr);
 
 	opt = (u8*) (addrp + 1);
 
@@ -1512,9 +1443,9 @@
 	*(opt++) = (rd_len >> 3);
 	opt += 6;
 
-	memcpy(opt, skb->nh.ipv6h, rd_len - 8);
+	memcpy(opt, ipv6_hdr(skb), rd_len - 8);
 
-	icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &skb->nh.ipv6h->saddr,
+	icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
 					     len, IPPROTO_ICMPV6,
 					     csum_partial((u8 *) icmph, len, 0));
 
@@ -1544,14 +1475,14 @@
 	if (!pskb_may_pull(skb, skb->len))
 		return 0;
 
-	msg = (struct nd_msg *) skb->h.raw;
+	msg = (struct nd_msg *)skb_transport_header(skb);
 
-	__skb_push(skb, skb->data-skb->h.raw);
+	__skb_push(skb, skb->data - skb_transport_header(skb));
 
-	if (skb->nh.ipv6h->hop_limit != 255) {
+	if (ipv6_hdr(skb)->hop_limit != 255) {
 		ND_PRINTK2(KERN_WARNING
 			   "ICMPv6 NDISC: invalid hop-limit: %d\n",
-			   skb->nh.ipv6h->hop_limit);
+			   ipv6_hdr(skb)->hop_limit);
 		return 0;
 	}
 
@@ -1584,7 +1515,7 @@
 	case NDISC_REDIRECT:
 		ndisc_redirect_rcv(skb);
 		break;
-	};
+	}
 
 	return 0;
 }
diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c
index 1c405dd..38b1496 100644
--- a/net/ipv6/netfilter.c
+++ b/net/ipv6/netfilter.c
@@ -11,7 +11,7 @@
 
 int ip6_route_me_harder(struct sk_buff *skb)
 {
-	struct ipv6hdr *iph = skb->nh.ipv6h;
+	struct ipv6hdr *iph = ipv6_hdr(skb);
 	struct dst_entry *dst;
 	struct flowi fl = {
 		.oif = skb->sk ? skb->sk->sk_bound_dev_if : 0,
@@ -61,7 +61,7 @@
 	struct ip6_rt_info *rt_info = nf_info_reroute(info);
 
 	if (info->hook == NF_IP6_LOCAL_OUT) {
-		struct ipv6hdr *iph = skb->nh.ipv6h;
+		struct ipv6hdr *iph = ipv6_hdr(skb);
 
 		rt_info->daddr = iph->daddr;
 		rt_info->saddr = iph->saddr;
@@ -73,7 +73,7 @@
 	struct ip6_rt_info *rt_info = nf_info_reroute(info);
 
 	if (info->hook == NF_IP6_LOCAL_OUT) {
-		struct ipv6hdr *iph = (*pskb)->nh.ipv6h;
+		struct ipv6hdr *iph = ipv6_hdr(*pskb);
 		if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) ||
 		    !ipv6_addr_equal(&iph->saddr, &rt_info->saddr))
 			return ip6_route_me_harder(*pskb);
@@ -84,7 +84,7 @@
 __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook,
 			     unsigned int dataoff, u_int8_t protocol)
 {
-	struct ipv6hdr *ip6h = skb->nh.ipv6h;
+	struct ipv6hdr *ip6h = ipv6_hdr(skb);
 	__sum16 csum = 0;
 
 	switch (skb->ip_summed) {
diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c
index fdb30a5..0004db3 100644
--- a/net/ipv6/netfilter/ip6_queue.c
+++ b/net/ipv6/netfilter/ip6_queue.c
@@ -11,18 +11,6 @@
  * 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.
- *
- * 2001-11-06: First try. Working with ip_queue.c for IPv4 and trying
- *             to adapt it to IPv6
- *             HEAVILY based in ipqueue.c by James Morris. It's just
- *             a little modified version of it, so he's nearly the
- *             real coder of this.
- *             Few changes needed, mainly the hard_routing code and
- *             the netlink socket protocol (we're NETLINK_IP6_FW).
- * 2002-06-25: Code cleanup. [JM: ported cleanup over from ip_queue.c]
- * 2005-02-04: Added /proc counter for dropped packets; fixed so
- *             packets aren't delivered to user space if they're going
- *             to be dropped.
  */
 #include <linux/module.h>
 #include <linux/skbuff.h>
@@ -189,12 +177,13 @@
 static struct sk_buff *
 ipq_build_packet_message(struct ipq_queue_entry *entry, int *errp)
 {
-	unsigned char *old_tail;
+	sk_buff_data_t old_tail;
 	size_t size = 0;
 	size_t data_len = 0;
 	struct sk_buff *skb;
 	struct ipq_packet_msg *pmsg;
 	struct nlmsghdr *nlh;
+	struct timeval tv;
 
 	read_lock_bh(&queue_lock);
 
@@ -232,15 +221,16 @@
 	if (!skb)
 		goto nlmsg_failure;
 
-	old_tail= skb->tail;
+	old_tail = skb->tail;
 	nlh = NLMSG_PUT(skb, 0, 0, IPQM_PACKET, size - sizeof(*nlh));
 	pmsg = NLMSG_DATA(nlh);
 	memset(pmsg, 0, sizeof(*pmsg));
 
 	pmsg->packet_id       = (unsigned long )entry;
 	pmsg->data_len        = data_len;
-	pmsg->timestamp_sec   = entry->skb->tstamp.off_sec;
-	pmsg->timestamp_usec  = entry->skb->tstamp.off_usec;
+	tv = ktime_to_timeval(entry->skb->tstamp);
+	pmsg->timestamp_sec   = tv.tv_sec;
+	pmsg->timestamp_usec  = tv.tv_usec;
 	pmsg->mark            = entry->skb->mark;
 	pmsg->hook            = entry->info->hook;
 	pmsg->hw_protocol     = entry->skb->protocol;
@@ -376,7 +366,7 @@
 	}
 	if (!skb_make_writable(&e->skb, v->data_len))
 		return -ENOMEM;
-	memcpy(e->skb->data, v->payload, v->data_len);
+	skb_copy_to_linear_data(e->skb, v->payload, v->data_len);
 	e->skb->ip_summed = CHECKSUM_NONE;
 
 	return 0;
@@ -485,7 +475,7 @@
 	if (skblen < sizeof(*nlh))
 		return;
 
-	nlh = (struct nlmsghdr *)skb->data;
+	nlh = nlmsg_hdr(skb);
 	nlmsglen = nlh->nlmsg_len;
 	if (nlmsglen < sizeof(*nlh) || skblen < nlmsglen)
 		return;
@@ -667,7 +657,7 @@
 	struct proc_dir_entry *proc;
 
 	netlink_register_notifier(&ipq_nl_notifier);
-	ipqnl = netlink_kernel_create(NETLINK_IP6_FW, 0, ipq_rcv_sk,
+	ipqnl = netlink_kernel_create(NETLINK_IP6_FW, 0, ipq_rcv_sk, NULL,
 				      THIS_MODULE);
 	if (ipqnl == NULL) {
 		printk(KERN_ERR "ip6_queue: failed to create netlink socket\n");
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index 7c512e1..9aa6240 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -7,15 +7,6 @@
  * 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.
- *
- * 19 Jan 2002 Harald Welte <laforge@gnumonks.org>
- * 	- increase module usage count as soon as we have rules inside
- * 	  a table
- * 06 Jun 2002 Andras Kis-Szabo <kisza@sch.bme.hu>
- *      - new extension header parser code
- * 15 Oct 2005 Harald Welte <laforge@netfilter.org>
- * 	- Unification of {ip,ip6}_tables into x_tables
- * 	- Removed tcp and udp code, since it's not ipv6 specific
  */
 
 #include <linux/capability.h>
@@ -115,7 +106,7 @@
 {
 	size_t i;
 	unsigned long ret;
-	const struct ipv6hdr *ipv6 = skb->nh.ipv6h;
+	const struct ipv6hdr *ipv6 = ipv6_hdr(skb);
 
 #define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
 
@@ -301,7 +292,7 @@
 				goto no_match;
 
 			ADD_COUNTER(e->counters,
-				    ntohs((*pskb)->nh.ipv6h->payload_len)
+				    ntohs(ipv6_hdr(*pskb)->payload_len)
 				    + IPV6_HDR_LEN,
 				    1);
 
@@ -1448,8 +1439,8 @@
 int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
 		  int target, unsigned short *fragoff)
 {
-	unsigned int start = (u8*)(skb->nh.ipv6h + 1) - skb->data;
-	u8 nexthdr = skb->nh.ipv6h->nexthdr;
+	unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
+	u8 nexthdr = ipv6_hdr(skb)->nexthdr;
 	unsigned int len = skb->len - start;
 
 	if (fragoff)
diff --git a/net/ipv6/netfilter/ip6t_HL.c b/net/ipv6/netfilter/ip6t_HL.c
index ccbab66..4115a57 100644
--- a/net/ipv6/netfilter/ip6t_HL.c
+++ b/net/ipv6/netfilter/ip6t_HL.c
@@ -32,7 +32,7 @@
 	if (!skb_make_writable(pskb, (*pskb)->len))
 		return NF_DROP;
 
-	ip6h = (*pskb)->nh.ipv6h;
+	ip6h = ipv6_hdr(*pskb);
 
 	switch (info->mode) {
 		case IP6T_HL_SET:
diff --git a/net/ipv6/netfilter/ip6t_LOG.c b/net/ipv6/netfilter/ip6t_LOG.c
index afaa039..5bb9cd3 100644
--- a/net/ipv6/netfilter/ip6t_LOG.c
+++ b/net/ipv6/netfilter/ip6t_LOG.c
@@ -396,8 +396,8 @@
 		/* MAC logging for input chain only. */
 		printk("MAC=");
 		if (skb->dev && (len = skb->dev->hard_header_len) &&
-		    skb->mac.raw != skb->nh.raw) {
-			unsigned char *p = skb->mac.raw;
+		    skb->mac_header != skb->network_header) {
+			const unsigned char *p = skb_mac_header(skb);
 			int i;
 
 			if (skb->dev->type == ARPHRD_SIT &&
@@ -412,7 +412,8 @@
 			printk(" ");
 
 			if (skb->dev->type == ARPHRD_SIT) {
-				struct iphdr *iph = (struct iphdr *)skb->mac.raw;
+				const struct iphdr *iph =
+					(struct iphdr *)skb_mac_header(skb);
 				printk("TUNNEL=%u.%u.%u.%u->%u.%u.%u.%u ",
 				       NIPQUAD(iph->saddr),
 				       NIPQUAD(iph->daddr));
@@ -421,7 +422,7 @@
 			printk(" ");
 	}
 
-	dump_packet(loginfo, skb, (u8*)skb->nh.ipv6h - skb->data, 1);
+	dump_packet(loginfo, skb, skb_network_offset(skb), 1);
 	printk("\n");
 	spin_unlock_bh(&log_lock);
 }
@@ -489,14 +490,10 @@
 	ret = xt_register_target(&ip6t_log_reg);
 	if (ret < 0)
 		return ret;
-	if (nf_log_register(PF_INET6, &ip6t_logger) < 0) {
-		printk(KERN_WARNING "ip6t_LOG: not logging via system console "
-		       "since somebody else already registered for PF_INET6\n");
-		/* we cannot make module load fail here, since otherwise
-		 * ip6tables userspace would abort */
-	}
-
-	return 0;
+	ret = nf_log_register(PF_INET6, &ip6t_logger);
+	if (ret < 0 && ret != -EEXIST)
+		xt_unregister_target(&ip6t_log_reg);
+	return ret;
 }
 
 static void __exit ip6t_log_fini(void)
diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c
index 6abee94..cb3d241 100644
--- a/net/ipv6/netfilter/ip6t_REJECT.c
+++ b/net/ipv6/netfilter/ip6t_REJECT.c
@@ -47,7 +47,7 @@
 	struct tcphdr otcph, *tcph;
 	unsigned int otcplen, hh_len;
 	int tcphoff, needs_ack;
-	struct ipv6hdr *oip6h = oldskb->nh.ipv6h, *ip6h;
+	struct ipv6hdr *oip6h = ipv6_hdr(oldskb), *ip6h;
 	struct dst_entry *dst = NULL;
 	u8 proto;
 	struct flowi fl;
@@ -120,8 +120,9 @@
 
 	skb_reserve(nskb, hh_len + dst->header_len);
 
-	ip6h = nskb->nh.ipv6h = (struct ipv6hdr *)
-					skb_put(nskb, sizeof(struct ipv6hdr));
+	skb_put(nskb, sizeof(struct ipv6hdr));
+	skb_reset_network_header(nskb);
+	ip6h = ipv6_hdr(nskb);
 	ip6h->version = 6;
 	ip6h->hop_limit = dst_metric(dst, RTAX_HOPLIMIT);
 	ip6h->nexthdr = IPPROTO_TCP;
@@ -155,8 +156,8 @@
 	tcph->check = 0;
 
 	/* Adjust TCP checksum */
-	tcph->check = csum_ipv6_magic(&nskb->nh.ipv6h->saddr,
-				      &nskb->nh.ipv6h->daddr,
+	tcph->check = csum_ipv6_magic(&ipv6_hdr(nskb)->saddr,
+				      &ipv6_hdr(nskb)->daddr,
 				      sizeof(struct tcphdr), IPPROTO_TCP,
 				      csum_partial((char *)tcph,
 						   sizeof(struct tcphdr), 0));
diff --git a/net/ipv6/netfilter/ip6t_eui64.c b/net/ipv6/netfilter/ip6t_eui64.c
index 967bed7..0f3dd93 100644
--- a/net/ipv6/netfilter/ip6t_eui64.c
+++ b/net/ipv6/netfilter/ip6t_eui64.c
@@ -32,8 +32,8 @@
 	unsigned char eui64[8];
 	int i = 0;
 
-	if (!(skb->mac.raw >= skb->head &&
-	      (skb->mac.raw + ETH_HLEN) <= skb->data) &&
+	if (!(skb_mac_header(skb) >= skb->head &&
+	      (skb_mac_header(skb) + ETH_HLEN) <= skb->data) &&
 	    offset != 0) {
 		*hotdrop = 1;
 		return 0;
@@ -42,7 +42,7 @@
 	memset(eui64, 0, sizeof(eui64));
 
 	if (eth_hdr(skb)->h_proto == htons(ETH_P_IPV6)) {
-		if (skb->nh.ipv6h->version == 0x6) {
+		if (ipv6_hdr(skb)->version == 0x6) {
 			memcpy(eui64, eth_hdr(skb)->h_source, 3);
 			memcpy(eui64 + 5, eth_hdr(skb)->h_source + 3, 3);
 			eui64[3] = 0xff;
@@ -50,7 +50,7 @@
 			eui64[0] |= 0x02;
 
 			i = 0;
-			while ((skb->nh.ipv6h->saddr.s6_addr[8+i] == eui64[i])
+			while ((ipv6_hdr(skb)->saddr.s6_addr[8 + i] == eui64[i])
 			       && (i < 8))
 				i++;
 
diff --git a/net/ipv6/netfilter/ip6t_hl.c b/net/ipv6/netfilter/ip6t_hl.c
index 37c8a4d..d606c0e 100644
--- a/net/ipv6/netfilter/ip6t_hl.c
+++ b/net/ipv6/netfilter/ip6t_hl.c
@@ -25,7 +25,7 @@
 		 int offset, unsigned int protoff, int *hotdrop)
 {
 	const struct ip6t_hl_info *info = matchinfo;
-	const struct ipv6hdr *ip6h = skb->nh.ipv6h;
+	const struct ipv6hdr *ip6h = ipv6_hdr(skb);
 
 	switch (info->mode) {
 		case IP6T_HL_EQ:
diff --git a/net/ipv6/netfilter/ip6t_ipv6header.c b/net/ipv6/netfilter/ip6t_ipv6header.c
index 700a11d..fd6a086 100644
--- a/net/ipv6/netfilter/ip6t_ipv6header.c
+++ b/net/ipv6/netfilter/ip6t_ipv6header.c
@@ -45,7 +45,7 @@
 	/* Make sure this isn't an evil packet */
 
 	/* type of the 1st exthdr */
-	nexthdr = skb->nh.ipv6h->nexthdr;
+	nexthdr = ipv6_hdr(skb)->nexthdr;
 	/* pointer to the 1st exthdr */
 	ptr = sizeof(struct ipv6hdr);
 	/* available length */
diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c
index 112a21d..76f0cf6 100644
--- a/net/ipv6/netfilter/ip6table_filter.c
+++ b/net/ipv6/netfilter/ip6table_filter.c
@@ -102,7 +102,7 @@
 #if 0
 	/* root is playing with raw sockets. */
 	if ((*pskb)->len < sizeof(struct iphdr)
-	    || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) {
+	    || ip_hdrlen(*pskb) < sizeof(struct iphdr)) {
 		if (net_ratelimit())
 			printk("ip6t_hook: happy cracking.\n");
 		return NF_ACCEPT;
diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c
index 0c468d3..a9f10e3 100644
--- a/net/ipv6/netfilter/ip6table_mangle.c
+++ b/net/ipv6/netfilter/ip6table_mangle.c
@@ -7,8 +7,6 @@
  * 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.
- *
- * Extended to all five netfilter hooks by Brad Chapman & Harald Welte
  */
 #include <linux/module.h>
 #include <linux/netfilter_ipv6/ip6_tables.h>
@@ -138,7 +136,7 @@
 #if 0
 	/* root is playing with raw sockets. */
 	if ((*pskb)->len < sizeof(struct iphdr)
-	    || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) {
+	    || ip_hdrlen(*pskb) < sizeof(struct iphdr)) {
 		if (net_ratelimit())
 			printk("ip6t_hook: happy cracking.\n");
 		return NF_ACCEPT;
@@ -146,21 +144,21 @@
 #endif
 
 	/* save source/dest address, mark, hoplimit, flowlabel, priority,  */
-	memcpy(&saddr, &(*pskb)->nh.ipv6h->saddr, sizeof(saddr));
-	memcpy(&daddr, &(*pskb)->nh.ipv6h->daddr, sizeof(daddr));
+	memcpy(&saddr, &ipv6_hdr(*pskb)->saddr, sizeof(saddr));
+	memcpy(&daddr, &ipv6_hdr(*pskb)->daddr, sizeof(daddr));
 	mark = (*pskb)->mark;
-	hop_limit = (*pskb)->nh.ipv6h->hop_limit;
+	hop_limit = ipv6_hdr(*pskb)->hop_limit;
 
 	/* flowlabel and prio (includes version, which shouldn't change either */
-	flowlabel = *((u_int32_t *) (*pskb)->nh.ipv6h);
+	flowlabel = *((u_int32_t *)ipv6_hdr(*pskb));
 
 	ret = ip6t_do_table(pskb, hook, in, out, &packet_mangler);
 
 	if (ret != NF_DROP && ret != NF_STOLEN
-		&& (memcmp(&(*pskb)->nh.ipv6h->saddr, &saddr, sizeof(saddr))
-		    || memcmp(&(*pskb)->nh.ipv6h->daddr, &daddr, sizeof(daddr))
+		&& (memcmp(&ipv6_hdr(*pskb)->saddr, &saddr, sizeof(saddr))
+		    || memcmp(&ipv6_hdr(*pskb)->daddr, &daddr, sizeof(daddr))
 		    || (*pskb)->mark != mark
-		    || (*pskb)->nh.ipv6h->hop_limit != hop_limit))
+		    || ipv6_hdr(*pskb)->hop_limit != hop_limit))
 		return ip6_route_me_harder(*pskb) == 0 ? ret : NF_DROP;
 
 	return ret;
diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
index d110245..6d2a082 100644
--- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
+++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
@@ -7,17 +7,6 @@
  *
  * Author:
  *	Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
- *
- * 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
- *	- support Layer 3 protocol independent connection tracking.
- *	  Based on the original ip_conntrack code which	had the following
- *	  copyright information:
- *		(C) 1999-2001 Paul `Rusty' Russell
- *		(C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
- *
- * 23 Mar 2004: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
- *	- add get_features() to support various size of conntrack
- *	  structures.
  */
 
 #include <linux/types.h>
@@ -138,16 +127,10 @@
 ipv6_prepare(struct sk_buff **pskb, unsigned int hooknum, unsigned int *dataoff,
 	     u_int8_t *protonum)
 {
-	unsigned int extoff;
-	unsigned char pnum;
-	int protoff;
-
-	extoff = (u8*)((*pskb)->nh.ipv6h + 1) - (*pskb)->data;
-	pnum = (*pskb)->nh.ipv6h->nexthdr;
-
-	protoff = nf_ct_ipv6_skip_exthdr(*pskb, extoff, &pnum,
-					 (*pskb)->len - extoff);
-
+	unsigned int extoff = (u8 *)(ipv6_hdr(*pskb) + 1) - (*pskb)->data;
+	unsigned char pnum = ipv6_hdr(*pskb)->nexthdr;
+	int protoff = nf_ct_ipv6_skip_exthdr(*pskb, extoff, &pnum,
+					     (*pskb)->len - extoff);
 	/*
 	 * (protoff == (*pskb)->len) mean that the packet doesn't have no data
 	 * except of IPv6 & ext headers. but it's tracked anyway. - YK
@@ -179,9 +162,8 @@
 	struct nf_conn_help *help;
 	enum ip_conntrack_info ctinfo;
 	unsigned int ret, protoff;
-	unsigned int extoff = (u8*)((*pskb)->nh.ipv6h + 1)
-			      - (*pskb)->data;
-	unsigned char pnum = (*pskb)->nh.ipv6h->nexthdr;
+	unsigned int extoff = (u8 *)(ipv6_hdr(*pskb) + 1) - (*pskb)->data;
+	unsigned char pnum = ipv6_hdr(*pskb)->nexthdr;
 
 
 	/* This is where we call the helper: as the packet goes out. */
diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
index 075da4f..0be790d 100644
--- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
+++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
@@ -7,13 +7,6 @@
  *
  * Author:
  *	Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
- *
- * 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
- *	- ICMPv6 tracking support. Derived from the original ip_conntrack code
- *	  net/ipv4/netfilter/ip_conntrack_proto_icmp.c which had the following
- *	  copyright information:
- *		(C) 1999-2001 Paul `Rusty' Russell
- *		(C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
  */
 
 #include <linux/types.h>
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index 15ab1e3..347ab76 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -82,7 +82,7 @@
 	struct sk_buff		*fragments;
 	int			len;
 	int			meat;
-	struct timeval		stamp;
+	ktime_t			stamp;
 	unsigned int		csum;
 	__u8			last_in;	/* has first/last segment arrived? */
 #define COMPLETE		4
@@ -353,9 +353,7 @@
 	ipv6_addr_copy(&fq->saddr, src);
 	ipv6_addr_copy(&fq->daddr, dst);
 
-	init_timer(&fq->timer);
-	fq->timer.function = nf_ct_frag6_expire;
-	fq->timer.data = (long) fq;
+	setup_timer(&fq->timer, nf_ct_frag6_expire, (unsigned long)fq);
 	spin_lock_init(&fq->lock);
 	atomic_set(&fq->refcnt, 1);
 
@@ -400,19 +398,20 @@
 	}
 
 	offset = ntohs(fhdr->frag_off) & ~0x7;
-	end = offset + (ntohs(skb->nh.ipv6h->payload_len) -
-			((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1)));
+	end = offset + (ntohs(ipv6_hdr(skb)->payload_len) -
+			((u8 *)(fhdr + 1) - (u8 *)(ipv6_hdr(skb) + 1)));
 
 	if ((unsigned int)end > IPV6_MAXPLEN) {
 		DEBUGP("offset is too large.\n");
 		return -1;
 	}
 
-	if (skb->ip_summed == CHECKSUM_COMPLETE)
+	if (skb->ip_summed == CHECKSUM_COMPLETE) {
+		const unsigned char *nh = skb_network_header(skb);
 		skb->csum = csum_sub(skb->csum,
-				     csum_partial(skb->nh.raw,
-						  (u8*)(fhdr + 1) - skb->nh.raw,
+				     csum_partial(nh, (u8 *)(fhdr + 1) - nh,
 						  0));
+	}
 
 	/* Is this the final fragment? */
 	if (!(fhdr->frag_off & htons(IP6_MF))) {
@@ -542,7 +541,7 @@
 		fq->fragments = skb;
 
 	skb->dev = NULL;
-	skb_get_timestamp(skb, &fq->stamp);
+	fq->stamp = skb->tstamp;
 	fq->meat += skb->len;
 	atomic_add(skb->truesize, &nf_ct_frag6_mem);
 
@@ -583,7 +582,9 @@
 	BUG_TRAP(NFCT_FRAG6_CB(head)->offset == 0);
 
 	/* Unfragmented part is taken from the first segment. */
-	payload_len = (head->data - head->nh.raw) - sizeof(struct ipv6hdr) + fq->len - sizeof(struct frag_hdr);
+	payload_len = ((head->data - skb_network_header(head)) -
+		       sizeof(struct ipv6hdr) + fq->len -
+		       sizeof(struct frag_hdr));
 	if (payload_len > IPV6_MAXPLEN) {
 		DEBUGP("payload len is too large.\n");
 		goto out_oversize;
@@ -624,15 +625,15 @@
 
 	/* We have to remove fragment header from datagram and to relocate
 	 * header in order to calculate ICV correctly. */
-	head->nh.raw[fq->nhoffset] = head->h.raw[0];
+	skb_network_header(head)[fq->nhoffset] = skb_transport_header(head)[0];
 	memmove(head->head + sizeof(struct frag_hdr), head->head,
 		(head->data - head->head) - sizeof(struct frag_hdr));
-	head->mac.raw += sizeof(struct frag_hdr);
-	head->nh.raw += sizeof(struct frag_hdr);
+	head->mac_header += sizeof(struct frag_hdr);
+	head->network_header += sizeof(struct frag_hdr);
 
 	skb_shinfo(head)->frag_list = head->next;
-	head->h.raw = head->data;
-	skb_push(head, head->data - head->nh.raw);
+	skb_reset_transport_header(head);
+	skb_push(head, head->data - skb_network_header(head));
 	atomic_sub(head->truesize, &nf_ct_frag6_mem);
 
 	for (fp=head->next; fp; fp = fp->next) {
@@ -648,12 +649,14 @@
 
 	head->next = NULL;
 	head->dev = dev;
-	skb_set_timestamp(head, &fq->stamp);
-	head->nh.ipv6h->payload_len = htons(payload_len);
+	head->tstamp = fq->stamp;
+	ipv6_hdr(head)->payload_len = htons(payload_len);
 
 	/* Yes, and fold redundant checksum back. 8) */
 	if (head->ip_summed == CHECKSUM_COMPLETE)
-		head->csum = csum_partial(head->nh.raw, head->h.raw-head->nh.raw, head->csum);
+		head->csum = csum_partial(skb_network_header(head),
+					  skb_network_header_len(head),
+					  head->csum);
 
 	fq->fragments = NULL;
 
@@ -701,9 +704,10 @@
 static int
 find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff)
 {
-	u8 nexthdr = skb->nh.ipv6h->nexthdr;
-	u8 prev_nhoff = (u8 *)&skb->nh.ipv6h->nexthdr - skb->data;
-	int start = (u8 *)(skb->nh.ipv6h+1) - skb->data;
+	u8 nexthdr = ipv6_hdr(skb)->nexthdr;
+	const int netoff = skb_network_offset(skb);
+	u8 prev_nhoff = netoff + offsetof(struct ipv6hdr, nexthdr);
+	int start = netoff + sizeof(struct ipv6hdr);
 	int len = skb->len - start;
 	u8 prevhdr = NEXTHDR_IPV6;
 
@@ -759,7 +763,7 @@
 	struct sk_buff *ret_skb = NULL;
 
 	/* Jumbo payload inhibits frag. header */
-	if (skb->nh.ipv6h->payload_len == 0) {
+	if (ipv6_hdr(skb)->payload_len == 0) {
 		DEBUGP("payload len = 0\n");
 		return skb;
 	}
@@ -780,9 +784,9 @@
 		goto ret_orig;
 	}
 
-	clone->h.raw = clone->data + fhoff;
-	hdr = clone->nh.ipv6h;
-	fhdr = (struct frag_hdr *)clone->h.raw;
+	skb_set_transport_header(clone, fhoff);
+	hdr = ipv6_hdr(clone);
+	fhdr = (struct frag_hdr *)skb_transport_header(clone);
 
 	if (!(fhdr->frag_off & htons(0xFFF9))) {
 		DEBUGP("Invalid fragment offset\n");
@@ -864,8 +868,7 @@
 	nf_ct_frag6_hash_rnd = (u32) ((num_physpages ^ (num_physpages>>7)) ^
 				   (jiffies ^ (jiffies >> 6)));
 
-	init_timer(&nf_ct_frag6_secret_timer);
-	nf_ct_frag6_secret_timer.function = nf_ct_frag6_secret_rebuild;
+	setup_timer(&nf_ct_frag6_secret_timer, nf_ct_frag6_secret_rebuild, 0);
 	nf_ct_frag6_secret_timer.expires = jiffies
 					   + nf_ct_frag6_secret_interval;
 	add_timer(&nf_ct_frag6_secret_timer);
diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c
index fa3fb50..920dc9c 100644
--- a/net/ipv6/proc.c
+++ b/net/ipv6/proc.c
@@ -23,12 +23,12 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/stddef.h>
+#include <net/ip.h>
 #include <net/sock.h>
 #include <net/tcp.h>
 #include <net/transp_v6.h>
 #include <net/ipv6.h>
 
-#ifdef CONFIG_PROC_FS
 static struct proc_dir_entry *proc_net_devsnmp6;
 
 static int fold_prot_inuse(struct proto *proto)
@@ -142,26 +142,13 @@
 	SNMP_MIB_SENTINEL
 };
 
-static unsigned long
-fold_field(void *mib[], int offt)
-{
-	unsigned long res = 0;
-	int i;
-
-	for_each_possible_cpu(i) {
-		res += *(((unsigned long *)per_cpu_ptr(mib[0], i)) + offt);
-		res += *(((unsigned long *)per_cpu_ptr(mib[1], i)) + offt);
-	}
-	return res;
-}
-
 static inline void
 snmp6_seq_show_item(struct seq_file *seq, void **mib, struct snmp_mib *itemlist)
 {
 	int i;
 	for (i=0; itemlist[i].name; i++)
 		seq_printf(seq, "%-32s\t%lu\n", itemlist[i].name,
-				fold_field(mib, itemlist[i].entry));
+			   snmp_fold_field(mib, itemlist[i].entry));
 }
 
 static int snmp6_seq_show(struct seq_file *seq, void *v)
@@ -236,6 +223,7 @@
 		return -EINVAL;
 	remove_proc_entry(idev->stats.proc_dir_entry->name,
 			  proc_net_devsnmp6);
+	idev->stats.proc_dir_entry = NULL;
 	return 0;
 }
 
@@ -271,47 +259,3 @@
 	proc_net_remove("snmp6");
 }
 
-#else	/* CONFIG_PROC_FS */
-
-
-int snmp6_register_dev(struct inet6_dev *idev)
-{
-	return 0;
-}
-
-int snmp6_unregister_dev(struct inet6_dev *idev)
-{
-	return 0;
-}
-#endif	/* CONFIG_PROC_FS */
-
-int snmp6_alloc_dev(struct inet6_dev *idev)
-{
-	int err = -ENOMEM;
-
-	if (!idev || !idev->dev)
-		return -EINVAL;
-
-	if (snmp6_mib_init((void **)idev->stats.ipv6, sizeof(struct ipstats_mib),
-			   __alignof__(struct ipstats_mib)) < 0)
-		goto err_ip;
-	if (snmp6_mib_init((void **)idev->stats.icmpv6, sizeof(struct icmpv6_mib),
-			   __alignof__(struct icmpv6_mib)) < 0)
-		goto err_icmp;
-
-	return 0;
-
-err_icmp:
-	snmp6_mib_free((void **)idev->stats.ipv6);
-err_ip:
-	return err;
-}
-
-int snmp6_free_dev(struct inet6_dev *idev)
-{
-	snmp6_mib_free((void **)idev->stats.icmpv6);
-	snmp6_mib_free((void **)idev->stats.ipv6);
-	return 0;
-}
-
-
diff --git a/net/ipv6/protocol.c b/net/ipv6/protocol.c
index ef43bd5..f929f47 100644
--- a/net/ipv6/protocol.c
+++ b/net/ipv6/protocol.c
@@ -60,6 +60,8 @@
 	return ret;
 }
 
+EXPORT_SYMBOL(inet6_add_protocol);
+
 /*
  *	Remove a protocol from the hash tables.
  */
@@ -83,3 +85,5 @@
 
 	return ret;
 }
+
+EXPORT_SYMBOL(inet6_del_protocol);
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 203e069..009a104 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -152,7 +152,7 @@
 	int delivered = 0;
 	__u8 hash;
 
-	saddr = &skb->nh.ipv6h->saddr;
+	saddr = &ipv6_hdr(skb)->saddr;
 	daddr = saddr + 1;
 
 	hash = nexthdr & (MAX_INET_PROTOS - 1);
@@ -361,17 +361,18 @@
 		skb->ip_summed = CHECKSUM_UNNECESSARY;
 
 	if (skb->ip_summed == CHECKSUM_COMPLETE) {
-		skb_postpull_rcsum(skb, skb->nh.raw,
-				   skb->h.raw - skb->nh.raw);
-		if (!csum_ipv6_magic(&skb->nh.ipv6h->saddr,
-				     &skb->nh.ipv6h->daddr,
+		skb_postpull_rcsum(skb, skb_network_header(skb),
+				   skb_network_header_len(skb));
+		if (!csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+				     &ipv6_hdr(skb)->daddr,
 				     skb->len, inet->num, skb->csum))
 			skb->ip_summed = CHECKSUM_UNNECESSARY;
 	}
-	if (skb->ip_summed != CHECKSUM_UNNECESSARY)
-		skb->csum = ~csum_unfold(csum_ipv6_magic(&skb->nh.ipv6h->saddr,
-					     &skb->nh.ipv6h->daddr,
-					     skb->len, inet->num, 0));
+	if (!skb_csum_unnecessary(skb))
+		skb->csum = ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+							 &ipv6_hdr(skb)->daddr,
+							 skb->len,
+							 inet->num, 0));
 
 	if (inet->hdrincl) {
 		if (skb_checksum_complete(skb)) {
@@ -420,7 +421,7 @@
 		msg->msg_flags |= MSG_TRUNC;
 	}
 
-	if (skb->ip_summed==CHECKSUM_UNNECESSARY) {
+	if (skb_csum_unnecessary(skb)) {
 		err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 	} else if (msg->msg_flags&MSG_TRUNC) {
 		if (__skb_checksum_complete(skb))
@@ -438,7 +439,7 @@
 	if (sin6) {
 		sin6->sin6_family = AF_INET6;
 		sin6->sin6_port = 0;
-		ipv6_addr_copy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr);
+		ipv6_addr_copy(&sin6->sin6_addr, &ipv6_hdr(skb)->saddr);
 		sin6->sin6_flowinfo = 0;
 		sin6->sin6_scope_id = 0;
 		if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)
@@ -488,7 +489,8 @@
 		goto out;
 
 	offset = rp->offset;
-	total_len = inet_sk(sk)->cork.length - (skb->nh.raw - skb->data);
+	total_len = inet_sk(sk)->cork.length - (skb_network_header(skb) -
+						skb->data);
 	if (offset >= total_len - 1) {
 		err = -EINVAL;
 		ip6_flush_pending_frames(sk);
@@ -511,7 +513,7 @@
 			if (csum_skb)
 				continue;
 
-			len = skb->len - (skb->h.raw - skb->data);
+			len = skb->len - skb_transport_offset(skb);
 			if (offset >= len) {
 				offset -= len;
 				continue;
@@ -523,7 +525,7 @@
 		skb = csum_skb;
 	}
 
-	offset += skb->h.raw - skb->data;
+	offset += skb_transport_offset(skb);
 	if (skb_copy_bits(skb, offset, &csum, 2))
 		BUG();
 
@@ -575,11 +577,13 @@
 	skb->priority = sk->sk_priority;
 	skb->dst = dst_clone(&rt->u.dst);
 
-	skb->nh.ipv6h = iph = (struct ipv6hdr *)skb_put(skb, length);
+	skb_put(skb, length);
+	skb_reset_network_header(skb);
+	iph = ipv6_hdr(skb);
 
 	skb->ip_summed = CHECKSUM_NONE;
 
-	skb->h.raw = skb->nh.raw;
+	skb->transport_header = skb->network_header;
 	err = memcpy_fromiovecend((void *)iph, from, 0, length);
 	if (err)
 		goto error_fault;
@@ -878,7 +882,7 @@
 		return 0;
 	default:
 		return -ENOPROTOOPT;
-	};
+	}
 
 	return 0;
 }
@@ -903,7 +907,7 @@
 		return 0;
 	default:
 		return -ENOPROTOOPT;
-	};
+	}
 
 	return 0;
 }
@@ -957,7 +961,8 @@
 		default:
 			return ipv6_setsockopt(sk, level, optname, optval,
 					       optlen);
-	};
+	}
+
 	return do_rawv6_setsockopt(sk, level, optname, optval, optlen);
 }
 
@@ -978,7 +983,7 @@
 	default:
 		return compat_ipv6_setsockopt(sk, level, optname,
 					      optval, optlen);
-	};
+	}
 	return do_rawv6_setsockopt(sk, level, optname, optval, optlen);
 }
 #endif
@@ -1031,7 +1036,8 @@
 		default:
 			return ipv6_getsockopt(sk, level, optname, optval,
 					       optlen);
-	};
+	}
+
 	return do_rawv6_getsockopt(sk, level, optname, optval, optlen);
 }
 
@@ -1052,7 +1058,7 @@
 	default:
 		return compat_ipv6_getsockopt(sk, level, optname,
 					      optval, optlen);
-	};
+	}
 	return do_rawv6_getsockopt(sk, level, optname, optval, optlen);
 }
 #endif
@@ -1073,7 +1079,7 @@
 			spin_lock_bh(&sk->sk_receive_queue.lock);
 			skb = skb_peek(&sk->sk_receive_queue);
 			if (skb != NULL)
-				amount = skb->tail - skb->h.raw;
+				amount = skb->tail - skb->transport_header;
 			spin_unlock_bh(&sk->sk_receive_queue.lock);
 			return put_user(amount, (int __user *)arg);
 		}
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index 7034c54..de795c0 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -88,7 +88,7 @@
 	int			len;
 	int			meat;
 	int			iif;
-	struct timeval		stamp;
+	ktime_t			stamp;
 	unsigned int		csum;
 	__u8			last_in;	/* has first/last segment arrived? */
 #define COMPLETE		4
@@ -430,19 +430,24 @@
 		goto err;
 
 	offset = ntohs(fhdr->frag_off) & ~0x7;
-	end = offset + (ntohs(skb->nh.ipv6h->payload_len) -
-			((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1)));
+	end = offset + (ntohs(ipv6_hdr(skb)->payload_len) -
+			((u8 *)(fhdr + 1) - (u8 *)(ipv6_hdr(skb) + 1)));
 
 	if ((unsigned int)end > IPV6_MAXPLEN) {
 		IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
 				 IPSTATS_MIB_INHDRERRORS);
-		icmpv6_param_prob(skb,ICMPV6_HDR_FIELD, (u8*)&fhdr->frag_off - skb->nh.raw);
+		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
+				  ((u8 *)&fhdr->frag_off -
+				   skb_network_header(skb)));
 		return;
 	}
 
-	if (skb->ip_summed == CHECKSUM_COMPLETE)
+	if (skb->ip_summed == CHECKSUM_COMPLETE) {
+		const unsigned char *nh = skb_network_header(skb);
 		skb->csum = csum_sub(skb->csum,
-				     csum_partial(skb->nh.raw, (u8*)(fhdr+1)-skb->nh.raw, 0));
+				     csum_partial(nh, (u8 *)(fhdr + 1) - nh,
+						  0));
+	}
 
 	/* Is this the final fragment? */
 	if (!(fhdr->frag_off & htons(IP6_MF))) {
@@ -562,7 +567,7 @@
 	if (skb->dev)
 		fq->iif = skb->dev->ifindex;
 	skb->dev = NULL;
-	skb_get_timestamp(skb, &fq->stamp);
+	fq->stamp = skb->tstamp;
 	fq->meat += skb->len;
 	atomic_add(skb->truesize, &ip6_frag_mem);
 
@@ -605,7 +610,9 @@
 	BUG_TRAP(FRAG6_CB(head)->offset == 0);
 
 	/* Unfragmented part is taken from the first segment. */
-	payload_len = (head->data - head->nh.raw) - sizeof(struct ipv6hdr) + fq->len - sizeof(struct frag_hdr);
+	payload_len = ((head->data - skb_network_header(head)) -
+		       sizeof(struct ipv6hdr) + fq->len -
+		       sizeof(struct frag_hdr));
 	if (payload_len > IPV6_MAXPLEN)
 		goto out_oversize;
 
@@ -639,15 +646,15 @@
 	/* We have to remove fragment header from datagram and to relocate
 	 * header in order to calculate ICV correctly. */
 	nhoff = fq->nhoffset;
-	head->nh.raw[nhoff] = head->h.raw[0];
+	skb_network_header(head)[nhoff] = skb_transport_header(head)[0];
 	memmove(head->head + sizeof(struct frag_hdr), head->head,
 		(head->data - head->head) - sizeof(struct frag_hdr));
-	head->mac.raw += sizeof(struct frag_hdr);
-	head->nh.raw += sizeof(struct frag_hdr);
+	head->mac_header += sizeof(struct frag_hdr);
+	head->network_header += sizeof(struct frag_hdr);
 
 	skb_shinfo(head)->frag_list = head->next;
-	head->h.raw = head->data;
-	skb_push(head, head->data - head->nh.raw);
+	skb_reset_transport_header(head);
+	skb_push(head, head->data - skb_network_header(head));
 	atomic_sub(head->truesize, &ip6_frag_mem);
 
 	for (fp=head->next; fp; fp = fp->next) {
@@ -663,15 +670,17 @@
 
 	head->next = NULL;
 	head->dev = dev;
-	skb_set_timestamp(head, &fq->stamp);
-	head->nh.ipv6h->payload_len = htons(payload_len);
+	head->tstamp = fq->stamp;
+	ipv6_hdr(head)->payload_len = htons(payload_len);
 	IP6CB(head)->nhoff = nhoff;
 
 	*skb_in = head;
 
 	/* Yes, and fold redundant checksum back. 8) */
 	if (head->ip_summed == CHECKSUM_COMPLETE)
-		head->csum = csum_partial(head->nh.raw, head->h.raw-head->nh.raw, head->csum);
+		head->csum = csum_partial(skb_network_header(head),
+					  skb_network_header_len(head),
+					  head->csum);
 
 	rcu_read_lock();
 	IP6_INC_STATS_BH(__in6_dev_get(dev), IPSTATS_MIB_REASMOKS);
@@ -699,33 +708,34 @@
 	struct net_device *dev = skb->dev;
 	struct frag_hdr *fhdr;
 	struct frag_queue *fq;
-	struct ipv6hdr *hdr;
-
-	hdr = skb->nh.ipv6h;
+	struct ipv6hdr *hdr = ipv6_hdr(skb);
 
 	IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_REASMREQDS);
 
 	/* Jumbo payload inhibits frag. header */
 	if (hdr->payload_len==0) {
 		IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS);
-		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw-skb->nh.raw);
+		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
+				  skb_network_header_len(skb));
 		return -1;
 	}
-	if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+sizeof(struct frag_hdr))) {
+	if (!pskb_may_pull(skb, (skb_transport_offset(skb) +
+				 sizeof(struct frag_hdr)))) {
 		IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS);
-		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw-skb->nh.raw);
+		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
+				  skb_network_header_len(skb));
 		return -1;
 	}
 
-	hdr = skb->nh.ipv6h;
-	fhdr = (struct frag_hdr *)skb->h.raw;
+	hdr = ipv6_hdr(skb);
+	fhdr = (struct frag_hdr *)skb_transport_header(skb);
 
 	if (!(fhdr->frag_off & htons(0xFFF9))) {
 		/* It is not a fragmented frame */
-		skb->h.raw += sizeof(struct frag_hdr);
+		skb->transport_header += sizeof(struct frag_hdr);
 		IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_REASMOKS);
 
-		IP6CB(skb)->nhoff = (u8*)fhdr - skb->nh.raw;
+		IP6CB(skb)->nhoff = (u8 *)fhdr - skb_network_header(skb);
 		return 1;
 	}
 
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index aebb4e2..b46ad53 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -575,6 +575,8 @@
 	return NULL;
 }
 
+EXPORT_SYMBOL(rt6_lookup);
+
 /* ip6_ins_rt is called with FREE table->tb6_lock.
    It takes new route entry, the addition fails by any reason the
    route is freed. In any case, if caller does not hold it, it may
@@ -724,7 +726,7 @@
 
 void ip6_route_input(struct sk_buff *skb)
 {
-	struct ipv6hdr *iph = skb->nh.ipv6h;
+	struct ipv6hdr *iph = ipv6_hdr(skb);
 	int flags = RT6_LOOKUP_F_HAS_SADDR;
 	struct flowi fl = {
 		.iif = skb->dev->ifindex,
@@ -829,6 +831,7 @@
 	return fib6_rule_lookup(fl, flags, ip6_pol_route_output);
 }
 
+EXPORT_SYMBOL(ip6_route_output);
 
 /*
  *	Destination cache support functions
@@ -1757,7 +1760,7 @@
 		rtnl_unlock();
 
 		return err;
-	};
+	}
 
 	return -EINVAL;
 }
@@ -1772,7 +1775,7 @@
 	int type;
 	switch (ipstats_mib_noroutes) {
 	case IPSTATS_MIB_INNOROUTES:
-		type = ipv6_addr_type(&skb->nh.ipv6h->daddr);
+		type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
 		if (type == IPV6_ADDR_ANY || type == IPV6_ADDR_RESERVED) {
 			IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_INADDRERRORS);
 			break;
@@ -2012,7 +2015,7 @@
 	return err;
 }
 
-int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 {
 	struct fib6_config cfg;
 	int err;
@@ -2024,7 +2027,7 @@
 	return ip6_route_del(&cfg);
 }
 
-int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 {
 	struct fib6_config cfg;
 	int err;
@@ -2161,7 +2164,7 @@
 		     prefix, NLM_F_MULTI);
 }
 
-int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
+static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
 {
 	struct nlattr *tb[RTA_MAX+1];
 	struct rt6_info *rt;
@@ -2215,7 +2218,7 @@
 	/* Reserve room for dummy headers, this skb can pass
 	   through good chunk of routing engine.
 	 */
-	skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
 	skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
 
 	rt = (struct rt6_info*) ip6_route_output(NULL, &fl);
@@ -2486,8 +2489,9 @@
 
 void __init ip6_route_init(void)
 {
+#ifdef 	CONFIG_PROC_FS
 	struct proc_dir_entry *p;
-
+#endif
 	ip6_dst_ops.kmem_cachep =
 		kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
 				  SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
@@ -2505,6 +2509,10 @@
 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
 	fib6_rules_init();
 #endif
+
+	__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL);
+	__rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL);
+	__rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL);
 }
 
 void ip6_route_cleanup(void)
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index 08d6ed3..1efa95a 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -99,10 +99,10 @@
 	return NULL;
 }
 
-static struct ip_tunnel ** ipip6_bucket(struct ip_tunnel *t)
+static struct ip_tunnel **__ipip6_bucket(struct ip_tunnel_parm *parms)
 {
-	__be32 remote = t->parms.iph.daddr;
-	__be32 local = t->parms.iph.saddr;
+	__be32 remote = parms->iph.daddr;
+	__be32 local = parms->iph.saddr;
 	unsigned h = 0;
 	int prio = 0;
 
@@ -117,6 +117,11 @@
 	return &tunnels[prio][h];
 }
 
+static inline struct ip_tunnel **ipip6_bucket(struct ip_tunnel *t)
+{
+	return __ipip6_bucket(&t->parms);
+}
+
 static void ipip6_tunnel_unlink(struct ip_tunnel *t)
 {
 	struct ip_tunnel **tp;
@@ -147,19 +152,9 @@
 	__be32 local = parms->iph.saddr;
 	struct ip_tunnel *t, **tp, *nt;
 	struct net_device *dev;
-	unsigned h = 0;
-	int prio = 0;
 	char name[IFNAMSIZ];
 
-	if (remote) {
-		prio |= 2;
-		h ^= HASH(remote);
-	}
-	if (local) {
-		prio |= 1;
-		h ^= HASH(local);
-	}
-	for (tp = &tunnels[prio][h]; (t = *tp) != NULL; tp = &t->next) {
+	for (tp = __ipip6_bucket(parms); (t = *tp) != NULL; tp = &t->next) {
 		if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr)
 			return t;
 	}
@@ -224,8 +219,8 @@
    ICMP in the real Internet is absolutely infeasible.
  */
 	struct iphdr *iph = (struct iphdr*)skb->data;
-	int type = skb->h.icmph->type;
-	int code = skb->h.icmph->code;
+	const int type = icmp_hdr(skb)->type;
+	const int code = icmp_hdr(skb)->code;
 	struct ip_tunnel *t;
 	int err;
 
@@ -280,8 +275,8 @@
 	struct iphdr *iph = (struct iphdr*)dp;
 	int hlen = iph->ihl<<2;
 	struct ipv6hdr *iph6;
-	int type = skb->h.icmph->type;
-	int code = skb->h.icmph->code;
+	const int type = icmp_hdr(skb)->type;
+	const int code = icmp_hdr(skb)->code;
 	int rel_type = 0;
 	int rel_code = 0;
 	int rel_info = 0;
@@ -296,14 +291,14 @@
 	default:
 		return;
 	case ICMP_PARAMETERPROB:
-		if (skb->h.icmph->un.gateway < hlen)
+		if (icmp_hdr(skb)->un.gateway < hlen)
 			return;
 
 		/* So... This guy found something strange INSIDE encapsulated
 		   packet. Well, he is fool, but what can we do ?
 		 */
 		rel_type = ICMPV6_PARAMPROB;
-		rel_info = skb->h.icmph->un.gateway - hlen;
+		rel_info = icmp_hdr(skb)->un.gateway - hlen;
 		break;
 
 	case ICMP_DEST_UNREACH:
@@ -340,7 +335,7 @@
 	dst_release(skb2->dst);
 	skb2->dst = NULL;
 	skb_pull(skb2, skb->data - (u8*)iph6);
-	skb2->nh.raw = skb2->data;
+	skb_reset_network_header(skb2);
 
 	/* Try to guess incoming interface */
 	rt6i = rt6_lookup(&iph6->saddr, NULL, NULL, 0);
@@ -366,7 +361,7 @@
 static inline void ipip6_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb)
 {
 	if (INET_ECN_is_ce(iph->tos))
-		IP6_ECN_set_ce(skb->nh.ipv6h);
+		IP6_ECN_set_ce(ipv6_hdr(skb));
 }
 
 static int ipip6_rcv(struct sk_buff *skb)
@@ -377,13 +372,13 @@
 	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
 		goto out;
 
-	iph = skb->nh.iph;
+	iph = ip_hdr(skb);
 
 	read_lock(&ipip6_lock);
 	if ((tunnel = ipip6_tunnel_lookup(iph->saddr, iph->daddr)) != NULL) {
 		secpath_reset(skb);
-		skb->mac.raw = skb->nh.raw;
-		skb->nh.raw = skb->data;
+		skb->mac_header = skb->network_header;
+		skb_reset_network_header(skb);
 		IPCB(skb)->flags = 0;
 		skb->protocol = htons(ETH_P_IPV6);
 		skb->pkt_type = PACKET_HOST;
@@ -430,7 +425,7 @@
 	struct ip_tunnel *tunnel = netdev_priv(dev);
 	struct net_device_stats *stats = &tunnel->stat;
 	struct iphdr  *tiph = &tunnel->parms.iph;
-	struct ipv6hdr *iph6 = skb->nh.ipv6h;
+	struct ipv6hdr *iph6 = ipv6_hdr(skb);
 	u8     tos = tunnel->parms.iph.tos;
 	struct rtable *rt;     			/* Route to the other host */
 	struct net_device *tdev;			/* Device to other host */
@@ -468,7 +463,7 @@
 		addr_type = ipv6_addr_type(addr6);
 
 		if (addr_type == IPV6_ADDR_ANY) {
-			addr6 = &skb->nh.ipv6h->daddr;
+			addr6 = &ipv6_hdr(skb)->daddr;
 			addr_type = ipv6_addr_type(addr6);
 		}
 
@@ -550,11 +545,12 @@
 			skb_set_owner_w(new_skb, skb->sk);
 		dev_kfree_skb(skb);
 		skb = new_skb;
-		iph6 = skb->nh.ipv6h;
+		iph6 = ipv6_hdr(skb);
 	}
 
-	skb->h.raw = skb->nh.raw;
-	skb->nh.raw = skb_push(skb, sizeof(struct iphdr));
+	skb->transport_header = skb->network_header;
+	skb_push(skb, sizeof(struct iphdr));
+	skb_reset_network_header(skb);
 	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
 	IPCB(skb)->flags = 0;
 	dst_release(skb->dst);
@@ -564,7 +560,7 @@
 	 *	Push down and install the IPIP header.
 	 */
 
-	iph 			=	skb->nh.iph;
+	iph 			=	ip_hdr(skb);
 	iph->version		=	4;
 	iph->ihl		=	sizeof(struct iphdr)>>2;
 	if (mtu > IPV6_MIN_MTU)
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 92f9992..e2f25ea 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -115,10 +115,10 @@
 
 static __u32 tcp_v6_init_sequence(struct sk_buff *skb)
 {
-	return secure_tcpv6_sequence_number(skb->nh.ipv6h->daddr.s6_addr32,
-					    skb->nh.ipv6h->saddr.s6_addr32,
-					    skb->h.th->dest,
-					    skb->h.th->source);
+	return secure_tcpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32,
+					    ipv6_hdr(skb)->saddr.s6_addr32,
+					    tcp_hdr(skb)->dest,
+					    tcp_hdr(skb)->source);
 }
 
 static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
@@ -486,7 +486,9 @@
 			struct sk_buff *pktopts = treq->pktopts;
 			struct inet6_skb_parm *rxopt = IP6CB(pktopts);
 			if (rxopt->srcrt)
-				opt = ipv6_invert_rthdr(sk, (struct ipv6_rt_hdr*)(pktopts->nh.raw + rxopt->srcrt));
+				opt = ipv6_invert_rthdr(sk,
+			  (struct ipv6_rt_hdr *)(skb_network_header(pktopts) +
+						 rxopt->srcrt));
 		}
 
 		if (opt && opt->srcrt) {
@@ -507,7 +509,7 @@
 
 	skb = tcp_make_synack(sk, dst, req);
 	if (skb) {
-		struct tcphdr *th = skb->h.th;
+		struct tcphdr *th = tcp_hdr(skb);
 
 		th->check = tcp_v6_check(th, skb->len,
 					 &treq->loc_addr, &treq->rmt_addr,
@@ -835,8 +837,8 @@
 {
 	__u8 *hash_location = NULL;
 	struct tcp_md5sig_key *hash_expected;
-	struct ipv6hdr *ip6h = skb->nh.ipv6h;
-	struct tcphdr *th = skb->h.th;
+	struct ipv6hdr *ip6h = ipv6_hdr(skb);
+	struct tcphdr *th = tcp_hdr(skb);
 	int length = (th->doff << 2) - sizeof (*th);
 	int genhash;
 	u8 *ptr;
@@ -944,10 +946,11 @@
 static void tcp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb)
 {
 	struct ipv6_pinfo *np = inet6_sk(sk);
-	struct tcphdr *th = skb->h.th;
+	struct tcphdr *th = tcp_hdr(skb);
 
 	if (skb->ip_summed == CHECKSUM_PARTIAL) {
 		th->check = ~csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP,  0);
+		skb->csum_start = skb_transport_header(skb) - skb->head;
 		skb->csum_offset = offsetof(struct tcphdr, check);
 	} else {
 		th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP,
@@ -964,12 +967,13 @@
 	if (!pskb_may_pull(skb, sizeof(*th)))
 		return -EINVAL;
 
-	ipv6h = skb->nh.ipv6h;
-	th = skb->h.th;
+	ipv6h = ipv6_hdr(skb);
+	th = tcp_hdr(skb);
 
 	th->check = 0;
 	th->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len,
 				     IPPROTO_TCP, 0);
+	skb->csum_start = skb_transport_header(skb) - skb->head;
 	skb->csum_offset = offsetof(struct tcphdr, check);
 	skb->ip_summed = CHECKSUM_PARTIAL;
 	return 0;
@@ -977,7 +981,7 @@
 
 static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb)
 {
-	struct tcphdr *th = skb->h.th, *t1;
+	struct tcphdr *th = tcp_hdr(skb), *t1;
 	struct sk_buff *buff;
 	struct flowi fl;
 	int tot_len = sizeof(*th);
@@ -993,7 +997,7 @@
 
 #ifdef CONFIG_TCP_MD5SIG
 	if (sk)
-		key = tcp_v6_md5_do_lookup(sk, &skb->nh.ipv6h->daddr);
+		key = tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr);
 	else
 		key = NULL;
 
@@ -1037,20 +1041,18 @@
 			       (TCPOPT_NOP << 16) |
 			       (TCPOPT_MD5SIG << 8) |
 			       TCPOLEN_MD5SIG);
-		tcp_v6_do_calc_md5_hash((__u8*)&opt[1],
-					key,
-					&skb->nh.ipv6h->daddr,
-					&skb->nh.ipv6h->saddr,
-					t1, IPPROTO_TCP,
-					tot_len);
+		tcp_v6_do_calc_md5_hash((__u8 *)&opt[1], key,
+					&ipv6_hdr(skb)->daddr,
+					&ipv6_hdr(skb)->saddr,
+					t1, IPPROTO_TCP, tot_len);
 	}
 #endif
 
 	buff->csum = csum_partial((char *)t1, sizeof(*t1), 0);
 
 	memset(&fl, 0, sizeof(fl));
-	ipv6_addr_copy(&fl.fl6_dst, &skb->nh.ipv6h->saddr);
-	ipv6_addr_copy(&fl.fl6_src, &skb->nh.ipv6h->daddr);
+	ipv6_addr_copy(&fl.fl6_dst, &ipv6_hdr(skb)->saddr);
+	ipv6_addr_copy(&fl.fl6_src, &ipv6_hdr(skb)->daddr);
 
 	t1->check = csum_ipv6_magic(&fl.fl6_src, &fl.fl6_dst,
 				    sizeof(*t1), IPPROTO_TCP,
@@ -1079,7 +1081,7 @@
 static void tcp_v6_send_ack(struct tcp_timewait_sock *tw,
 			    struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts)
 {
-	struct tcphdr *th = skb->h.th, *t1;
+	struct tcphdr *th = tcp_hdr(skb), *t1;
 	struct sk_buff *buff;
 	struct flowi fl;
 	int tot_len = sizeof(struct tcphdr);
@@ -1091,7 +1093,7 @@
 
 #ifdef CONFIG_TCP_MD5SIG
 	if (!tw && skb->sk) {
-		key = tcp_v6_md5_do_lookup(skb->sk, &skb->nh.ipv6h->daddr);
+		key = tcp_v6_md5_do_lookup(skb->sk, &ipv6_hdr(skb)->daddr);
 	} else if (tw && tw->tw_md5_keylen) {
 		tw_key.key = tw->tw_md5_key;
 		tw_key.keylen = tw->tw_md5_keylen;
@@ -1140,20 +1142,18 @@
 	if (key) {
 		*topt++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
 				(TCPOPT_MD5SIG << 8) | TCPOLEN_MD5SIG);
-		tcp_v6_do_calc_md5_hash((__u8 *)topt,
-					key,
-					&skb->nh.ipv6h->daddr,
-					&skb->nh.ipv6h->saddr,
-					t1, IPPROTO_TCP,
-					tot_len);
+		tcp_v6_do_calc_md5_hash((__u8 *)topt, key,
+					&ipv6_hdr(skb)->daddr,
+					&ipv6_hdr(skb)->saddr,
+					t1, IPPROTO_TCP, tot_len);
 	}
 #endif
 
 	buff->csum = csum_partial((char *)t1, tot_len, 0);
 
 	memset(&fl, 0, sizeof(fl));
-	ipv6_addr_copy(&fl.fl6_dst, &skb->nh.ipv6h->saddr);
-	ipv6_addr_copy(&fl.fl6_src, &skb->nh.ipv6h->daddr);
+	ipv6_addr_copy(&fl.fl6_dst, &ipv6_hdr(skb)->saddr);
+	ipv6_addr_copy(&fl.fl6_src, &ipv6_hdr(skb)->daddr);
 
 	t1->check = csum_ipv6_magic(&fl.fl6_src, &fl.fl6_dst,
 				    tot_len, IPPROTO_TCP,
@@ -1197,18 +1197,18 @@
 static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
 {
 	struct request_sock *req, **prev;
-	const struct tcphdr *th = skb->h.th;
+	const struct tcphdr *th = tcp_hdr(skb);
 	struct sock *nsk;
 
 	/* Find possible connection requests. */
 	req = inet6_csk_search_req(sk, &prev, th->source,
-				   &skb->nh.ipv6h->saddr,
-				   &skb->nh.ipv6h->daddr, inet6_iif(skb));
+				   &ipv6_hdr(skb)->saddr,
+				   &ipv6_hdr(skb)->daddr, inet6_iif(skb));
 	if (req)
 		return tcp_check_req(sk, skb, req, prev);
 
-	nsk = __inet6_lookup_established(&tcp_hashinfo, &skb->nh.ipv6h->saddr,
-					 th->source, &skb->nh.ipv6h->daddr,
+	nsk = __inet6_lookup_established(&tcp_hashinfo, &ipv6_hdr(skb)->saddr,
+					 th->source, &ipv6_hdr(skb)->daddr,
 					 ntohs(th->dest), inet6_iif(skb));
 
 	if (nsk) {
@@ -1275,9 +1275,9 @@
 	tcp_openreq_init(req, &tmp_opt, skb);
 
 	treq = inet6_rsk(req);
-	ipv6_addr_copy(&treq->rmt_addr, &skb->nh.ipv6h->saddr);
-	ipv6_addr_copy(&treq->loc_addr, &skb->nh.ipv6h->daddr);
-	TCP_ECN_create_request(req, skb->h.th);
+	ipv6_addr_copy(&treq->rmt_addr, &ipv6_hdr(skb)->saddr);
+	ipv6_addr_copy(&treq->loc_addr, &ipv6_hdr(skb)->daddr);
+	TCP_ECN_create_request(req, tcp_hdr(skb));
 	treq->pktopts = NULL;
 	if (ipv6_opt_accepted(sk, skb) ||
 	    np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
@@ -1363,7 +1363,7 @@
 		newnp->pktoptions  = NULL;
 		newnp->opt	   = NULL;
 		newnp->mcast_oif   = inet6_iif(skb);
-		newnp->mcast_hops  = skb->nh.ipv6h->hop_limit;
+		newnp->mcast_hops  = ipv6_hdr(skb)->hop_limit;
 
 		/*
 		 * No need to charge this sock to the relevant IPv6 refcnt debug socks count
@@ -1389,7 +1389,9 @@
 	    opt == NULL && treq->pktopts) {
 		struct inet6_skb_parm *rxopt = IP6CB(treq->pktopts);
 		if (rxopt->srcrt)
-			opt = ipv6_invert_rthdr(sk, (struct ipv6_rt_hdr *)(treq->pktopts->nh.raw + rxopt->srcrt));
+			opt = ipv6_invert_rthdr(sk,
+		   (struct ipv6_rt_hdr *)(skb_network_header(treq->pktopts) +
+					  rxopt->srcrt));
 	}
 
 	if (dst == NULL) {
@@ -1469,7 +1471,7 @@
 	}
 	newnp->opt	  = NULL;
 	newnp->mcast_oif  = inet6_iif(skb);
-	newnp->mcast_hops = skb->nh.ipv6h->hop_limit;
+	newnp->mcast_hops = ipv6_hdr(skb)->hop_limit;
 
 	/* Clone native IPv6 options from listening socket (if any)
 
@@ -1528,15 +1530,16 @@
 static __sum16 tcp_v6_checksum_init(struct sk_buff *skb)
 {
 	if (skb->ip_summed == CHECKSUM_COMPLETE) {
-		if (!tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr,
-				  &skb->nh.ipv6h->daddr,skb->csum)) {
+		if (!tcp_v6_check(tcp_hdr(skb), skb->len, &ipv6_hdr(skb)->saddr,
+				  &ipv6_hdr(skb)->daddr, skb->csum)) {
 			skb->ip_summed = CHECKSUM_UNNECESSARY;
 			return 0;
 		}
 	}
 
-	skb->csum = ~csum_unfold(tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr,
-				  &skb->nh.ipv6h->daddr, 0));
+	skb->csum = ~csum_unfold(tcp_v6_check(tcp_hdr(skb), skb->len,
+					      &ipv6_hdr(skb)->saddr,
+					      &ipv6_hdr(skb)->daddr, 0));
 
 	if (skb->len <= 76) {
 		return __skb_checksum_complete(skb);
@@ -1600,7 +1603,7 @@
 
 	if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
 		TCP_CHECK_TIMER(sk);
-		if (tcp_rcv_established(sk, skb, skb->h.th, skb->len))
+		if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len))
 			goto reset;
 		TCP_CHECK_TIMER(sk);
 		if (opt_skb)
@@ -1608,7 +1611,7 @@
 		return 0;
 	}
 
-	if (skb->len < (skb->h.th->doff<<2) || tcp_checksum_complete(skb))
+	if (skb->len < tcp_hdrlen(skb) || tcp_checksum_complete(skb))
 		goto csum_err;
 
 	if (sk->sk_state == TCP_LISTEN) {
@@ -1631,7 +1634,7 @@
 	}
 
 	TCP_CHECK_TIMER(sk);
-	if (tcp_rcv_state_process(sk, skb, skb->h.th, skb->len))
+	if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len))
 		goto reset;
 	TCP_CHECK_TIMER(sk);
 	if (opt_skb)
@@ -1664,7 +1667,7 @@
 		if (np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo)
 			np->mcast_oif = inet6_iif(opt_skb);
 		if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim)
-			np->mcast_hops = opt_skb->nh.ipv6h->hop_limit;
+			np->mcast_hops = ipv6_hdr(opt_skb)->hop_limit;
 		if (ipv6_opt_accepted(sk, opt_skb)) {
 			skb_set_owner_r(opt_skb, sk);
 			opt_skb = xchg(&np->pktoptions, opt_skb);
@@ -1697,28 +1700,27 @@
 	if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
 		goto discard_it;
 
-	th = skb->h.th;
+	th = tcp_hdr(skb);
 
 	if (th->doff < sizeof(struct tcphdr)/4)
 		goto bad_packet;
 	if (!pskb_may_pull(skb, th->doff*4))
 		goto discard_it;
 
-	if ((skb->ip_summed != CHECKSUM_UNNECESSARY &&
-	     tcp_v6_checksum_init(skb)))
+	if (!skb_csum_unnecessary(skb) && tcp_v6_checksum_init(skb))
 		goto bad_packet;
 
-	th = skb->h.th;
+	th = tcp_hdr(skb);
 	TCP_SKB_CB(skb)->seq = ntohl(th->seq);
 	TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
 				    skb->len - th->doff*4);
 	TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
 	TCP_SKB_CB(skb)->when = 0;
-	TCP_SKB_CB(skb)->flags = ipv6_get_dsfield(skb->nh.ipv6h);
+	TCP_SKB_CB(skb)->flags = ipv6_get_dsfield(ipv6_hdr(skb));
 	TCP_SKB_CB(skb)->sacked = 0;
 
-	sk = __inet6_lookup(&tcp_hashinfo, &skb->nh.ipv6h->saddr, th->source,
-			    &skb->nh.ipv6h->daddr, ntohs(th->dest),
+	sk = __inet6_lookup(&tcp_hashinfo, &ipv6_hdr(skb)->saddr, th->source,
+			    &ipv6_hdr(skb)->daddr, ntohs(th->dest),
 			    inet6_iif(skb));
 
 	if (!sk)
@@ -1798,7 +1800,7 @@
 		struct sock *sk2;
 
 		sk2 = inet6_lookup_listener(&tcp_hashinfo,
-					    &skb->nh.ipv6h->daddr,
+					    &ipv6_hdr(skb)->daddr,
 					    ntohs(th->dest), inet6_iif(skb));
 		if (sk2 != NULL) {
 			struct inet_timewait_sock *tw = inet_twsk(sk);
@@ -1945,6 +1947,7 @@
 	return inet6_destroy_sock(sk);
 }
 
+#ifdef CONFIG_PROC_FS
 /* Proc filesystem TCPv6 sock list dumping. */
 static void get_openreq6(struct seq_file *seq,
 			 struct sock *sk, struct request_sock *req, int i, int uid)
@@ -2061,7 +2064,6 @@
 		   atomic_read(&tw->tw_refcnt), tw);
 }
 
-#ifdef CONFIG_PROC_FS
 static int tcp6_seq_show(struct seq_file *seq, void *v)
 {
 	struct tcp_iter_state *st;
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index f590db5..b083c09 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -93,10 +93,10 @@
 					continue;
 				score++;
 			}
-			if(score == 4) {
+			if (score == 4) {
 				result = sk;
 				break;
-			} else if(score > badness) {
+			} else if (score > badness) {
 				result = sk;
 				badness = score;
 			}
@@ -120,8 +120,9 @@
 	struct ipv6_pinfo *np = inet6_sk(sk);
 	struct inet_sock *inet = inet_sk(sk);
 	struct sk_buff *skb;
-	size_t copied;
-	int err, copy_only, is_udplite = IS_UDPLITE(sk);
+	unsigned int ulen, copied;
+	int err;
+	int is_udplite = IS_UDPLITE(sk);
 
 	if (addr_len)
 		*addr_len=sizeof(struct sockaddr_in6);
@@ -134,24 +135,25 @@
 	if (!skb)
 		goto out;
 
-	copied = skb->len - sizeof(struct udphdr);
-	if (copied > len) {
-		copied = len;
+	ulen = skb->len - sizeof(struct udphdr);
+	copied = len;
+	if (copied > ulen)
+		copied = ulen;
+	else if (copied < ulen)
 		msg->msg_flags |= MSG_TRUNC;
-	}
 
 	/*
-	 * 	Decide whether to checksum and/or copy data.
+	 * If checksum is needed at all, try to do it while copying the
+	 * data.  If the data is truncated, or if we only want a partial
+	 * coverage checksum (UDP-Lite), do it before the copy.
 	 */
-	copy_only = (skb->ip_summed==CHECKSUM_UNNECESSARY);
 
-	if (is_udplite  ||  (!copy_only  &&  msg->msg_flags&MSG_TRUNC)) {
-		if (__udp_lib_checksum_complete(skb))
+	if (copied < ulen || UDP_SKB_CB(skb)->partial_cov) {
+		if (udp_lib_checksum_complete(skb))
 			goto csum_copy_err;
-		copy_only = 1;
 	}
 
-	if (copy_only)
+	if (skb_csum_unnecessary(skb))
 		err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr),
 					      msg->msg_iov, copied       );
 	else {
@@ -170,15 +172,16 @@
 
 		sin6 = (struct sockaddr_in6 *) msg->msg_name;
 		sin6->sin6_family = AF_INET6;
-		sin6->sin6_port = skb->h.uh->source;
+		sin6->sin6_port = udp_hdr(skb)->source;
 		sin6->sin6_flowinfo = 0;
 		sin6->sin6_scope_id = 0;
 
 		if (skb->protocol == htons(ETH_P_IP))
 			ipv6_addr_set(&sin6->sin6_addr, 0, 0,
-				      htonl(0xffff), skb->nh.iph->saddr);
+				      htonl(0xffff), ip_hdr(skb)->saddr);
 		else {
-			ipv6_addr_copy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr);
+			ipv6_addr_copy(&sin6->sin6_addr,
+				       &ipv6_hdr(skb)->saddr);
 			if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)
 				sin6->sin6_scope_id = IP6CB(skb)->iif;
 		}
@@ -194,7 +197,7 @@
 
 	err = copied;
 	if (flags & MSG_TRUNC)
-		err = skb->len - sizeof(struct udphdr);
+		err = ulen;
 
 out_free:
 	skb_free_datagram(sk, skb);
@@ -279,8 +282,10 @@
 		}
 	}
 
-	if (udp_lib_checksum_complete(skb))
-		goto drop;
+	if (sk->sk_filter) {
+		if (udp_lib_checksum_complete(skb))
+			goto drop;
+	}
 
 	if ((rc = sock_queue_rcv_skb(sk,skb)) < 0) {
 		/* Note that an ENOMEM error is charged twice */
@@ -325,7 +330,7 @@
 				if (!ipv6_addr_equal(&np->rcv_saddr, loc_addr))
 					continue;
 			}
-			if(!inet6_mc_check(s, loc_addr, rmt_addr))
+			if (!inet6_mc_check(s, loc_addr, rmt_addr))
 				continue;
 			return s;
 		}
@@ -341,7 +346,7 @@
 			   struct in6_addr *daddr, struct hlist_head udptable[])
 {
 	struct sock *sk, *sk2;
-	const struct udphdr *uh = skb->h.uh;
+	const struct udphdr *uh = udp_hdr(skb);
 	int dif;
 
 	read_lock(&udp_hash_lock);
@@ -366,9 +371,20 @@
 	return 0;
 }
 
-static inline int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh)
-
+static inline int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh,
+				 int proto)
 {
+	int err;
+
+	UDP_SKB_CB(skb)->partial_cov = 0;
+	UDP_SKB_CB(skb)->cscov = skb->len;
+
+	if (proto == IPPROTO_UDPLITE) {
+		err = udplite_checksum_init(skb, uh);
+		if (err)
+			return err;
+	}
+
 	if (uh->check == 0) {
 		/* RFC 2460 section 8.1 says that we SHOULD log
 		   this error. Well, it is reasonable.
@@ -377,21 +393,20 @@
 		return 1;
 	}
 	if (skb->ip_summed == CHECKSUM_COMPLETE &&
-	    !csum_ipv6_magic(&skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr,
-			     skb->len, IPPROTO_UDP, skb->csum             ))
+	    !csum_ipv6_magic(&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
+			     skb->len, proto, skb->csum))
 		skb->ip_summed = CHECKSUM_UNNECESSARY;
 
-	if (skb->ip_summed != CHECKSUM_UNNECESSARY)
-		skb->csum = ~csum_unfold(csum_ipv6_magic(&skb->nh.ipv6h->saddr,
-							 &skb->nh.ipv6h->daddr,
-							 skb->len, IPPROTO_UDP,
-							 0));
+	if (!skb_csum_unnecessary(skb))
+		skb->csum = ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+							 &ipv6_hdr(skb)->daddr,
+							 skb->len, proto, 0));
 
-	return (UDP_SKB_CB(skb)->partial_cov = 0);
+	return 0;
 }
 
 int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[],
-		   int is_udplite)
+		   int proto)
 {
 	struct sk_buff *skb = *pskb;
 	struct sock *sk;
@@ -403,15 +418,16 @@
 	if (!pskb_may_pull(skb, sizeof(struct udphdr)))
 		goto short_packet;
 
-	saddr = &skb->nh.ipv6h->saddr;
-	daddr = &skb->nh.ipv6h->daddr;
-	uh = skb->h.uh;
+	saddr = &ipv6_hdr(skb)->saddr;
+	daddr = &ipv6_hdr(skb)->daddr;
+	uh = udp_hdr(skb);
 
 	ulen = ntohs(uh->len);
 	if (ulen > skb->len)
 		goto short_packet;
 
-	if(! is_udplite ) {		/* UDP validates ulen. */
+	if (proto == IPPROTO_UDP) {
+		/* UDP validates ulen. */
 
 		/* Check for jumbo payload */
 		if (ulen == 0)
@@ -423,19 +439,15 @@
 		if (ulen < skb->len) {
 			if (pskb_trim_rcsum(skb, ulen))
 				goto short_packet;
-			saddr = &skb->nh.ipv6h->saddr;
-			daddr = &skb->nh.ipv6h->daddr;
-			uh = skb->h.uh;
+			saddr = &ipv6_hdr(skb)->saddr;
+			daddr = &ipv6_hdr(skb)->daddr;
+			uh = udp_hdr(skb);
 		}
-
-		if (udp6_csum_init(skb, uh))
-			goto discard;
-
-	} else 	{			/* UDP-Lite validates cscov. */
-		if (udplite6_csum_init(skb, uh))
-			goto discard;
 	}
 
+	if (udp6_csum_init(skb, uh, proto))
+		goto discard;
+
 	/*
 	 *	Multicast receive code
 	 */
@@ -457,33 +469,34 @@
 
 		if (udp_lib_checksum_complete(skb))
 			goto discard;
-		UDP6_INC_STATS_BH(UDP_MIB_NOPORTS, is_udplite);
+		UDP6_INC_STATS_BH(UDP_MIB_NOPORTS, proto == IPPROTO_UDPLITE);
 
 		icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, dev);
 
 		kfree_skb(skb);
-		return(0);
+		return 0;
 	}
 
 	/* deliver */
 
 	udpv6_queue_rcv_skb(sk, skb);
 	sock_put(sk);
-	return(0);
+	return 0;
 
 short_packet:
 	LIMIT_NETDEBUG(KERN_DEBUG "UDP%sv6: short packet: %d/%u\n",
-		       is_udplite? "-Lite" : "",  ulen, skb->len);
+		       proto == IPPROTO_UDPLITE ? "-Lite" : "",
+		       ulen, skb->len);
 
 discard:
-	UDP6_INC_STATS_BH(UDP_MIB_INERRORS, is_udplite);
+	UDP6_INC_STATS_BH(UDP_MIB_INERRORS, proto == IPPROTO_UDPLITE);
 	kfree_skb(skb);
-	return(0);
+	return 0;
 }
 
 static __inline__ int udpv6_rcv(struct sk_buff **pskb)
 {
-	return __udp6_lib_rcv(pskb, udp_hash, 0);
+	return __udp6_lib_rcv(pskb, udp_hash, IPPROTO_UDP);
 }
 
 /*
@@ -521,7 +534,7 @@
 	/*
 	 * Create a UDP header
 	 */
-	uh = skb->h.uh;
+	uh = udp_hdr(skb);
 	uh->source = fl->fl_ip_sport;
 	uh->dest = fl->fl_ip_dport;
 	uh->len = htons(up->len);
diff --git a/net/ipv6/udplite.c b/net/ipv6/udplite.c
index 629f971..f54016a 100644
--- a/net/ipv6/udplite.c
+++ b/net/ipv6/udplite.c
@@ -19,7 +19,7 @@
 
 static int udplitev6_rcv(struct sk_buff **pskb)
 {
-	return __udp6_lib_rcv(pskb, udplite_hash, 1);
+	return __udp6_lib_rcv(pskb, udplite_hash, IPPROTO_UDPLITE);
 }
 
 static void udplitev6_err(struct sk_buff *skb,
diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c
index 31f651f..d7ed8aa 100644
--- a/net/ipv6/xfrm6_input.c
+++ b/net/ipv6/xfrm6_input.c
@@ -28,14 +28,14 @@
 	unsigned int nhoff;
 
 	nhoff = IP6CB(skb)->nhoff;
-	nexthdr = skb->nh.raw[nhoff];
+	nexthdr = skb_network_header(skb)[nhoff];
 
 	seq = 0;
 	if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0)
 		goto drop;
 
 	do {
-		struct ipv6hdr *iph = skb->nh.ipv6h;
+		struct ipv6hdr *iph = ipv6_hdr(skb);
 
 		if (xfrm_nr == XFRM_MAX_DEPTH)
 			goto drop;
@@ -58,7 +58,7 @@
 		if (nexthdr <= 0)
 			goto drop_unlock;
 
-		skb->nh.raw[nhoff] = nexthdr;
+		skb_network_header(skb)[nhoff] = nexthdr;
 
 		if (x->props.replay_window)
 			xfrm_replay_advance(x, seq);
@@ -112,8 +112,8 @@
 		return -1;
 	} else {
 #ifdef CONFIG_NETFILTER
-		skb->nh.ipv6h->payload_len = htons(skb->len);
-		__skb_push(skb, skb->data - skb->nh.raw);
+		ipv6_hdr(skb)->payload_len = htons(skb->len);
+		__skb_push(skb, skb->data - skb_network_header(skb));
 
 		NF_HOOK(PF_INET6, NF_IP6_PRE_ROUTING, skb, skb->dev, NULL,
 			ip6_rcv_finish);
@@ -140,19 +140,19 @@
 	return xfrm6_rcv_spi(*pskb, 0);
 }
 
+EXPORT_SYMBOL(xfrm6_rcv);
+
 int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
 		     xfrm_address_t *saddr, u8 proto)
 {
 	struct xfrm_state *x = NULL;
 	int wildcard = 0;
-	struct in6_addr any;
 	xfrm_address_t *xany;
 	struct xfrm_state *xfrm_vec_one = NULL;
 	int nh = 0;
 	int i = 0;
 
-	ipv6_addr_set(&any, 0, 0, 0, 0);
-	xany = (xfrm_address_t *)&any;
+	xany = (xfrm_address_t *)&in6addr_any;
 
 	for (i = 0; i < 3; i++) {
 		xfrm_address_t *dst, *src;
@@ -247,3 +247,5 @@
 		xfrm_state_put(xfrm_vec_one);
 	return -1;
 }
+
+EXPORT_SYMBOL(xfrm6_input_addr);
diff --git a/net/ipv6/xfrm6_mode_beet.c b/net/ipv6/xfrm6_mode_beet.c
index edcfffa..2e61d6d 100644
--- a/net/ipv6/xfrm6_mode_beet.c
+++ b/net/ipv6/xfrm6_mode_beet.c
@@ -38,17 +38,18 @@
 	int hdr_len;
 
 	skb_push(skb, x->props.header_len);
-	iph = skb->nh.ipv6h;
+	iph = ipv6_hdr(skb);
 
 	hdr_len = ip6_find_1stfragopt(skb, &prevhdr);
-	skb->nh.raw = prevhdr - x->props.header_len;
-	skb->h.raw = skb->data + hdr_len;
+	skb_set_network_header(skb,
+			       (prevhdr - x->props.header_len) - skb->data);
+	skb_set_transport_header(skb, hdr_len);
 	memmove(skb->data, iph, hdr_len);
 
-	skb->nh.raw = skb->data;
-	top_iph = skb->nh.ipv6h;
-	skb->nh.raw = &top_iph->nexthdr;
-	skb->h.ipv6h = top_iph + 1;
+	skb_reset_network_header(skb);
+	top_iph = ipv6_hdr(skb);
+	skb->transport_header = skb->network_header + sizeof(struct ipv6hdr);
+	skb->network_header += offsetof(struct ipv6hdr, nexthdr);
 
 	ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr);
 	ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr);
@@ -59,6 +60,7 @@
 static int xfrm6_beet_input(struct xfrm_state *x, struct sk_buff *skb)
 {
 	struct ipv6hdr *ip6h;
+	const unsigned char *old_mac;
 	int size = sizeof(struct ipv6hdr);
 	int err = -EINVAL;
 
@@ -66,13 +68,14 @@
 		goto out;
 
 	skb_push(skb, size);
-	memmove(skb->data, skb->nh.raw, size);
-	skb->nh.raw = skb->data;
+	memmove(skb->data, skb_network_header(skb), size);
+	skb_reset_network_header(skb);
 
-	skb->mac.raw = memmove(skb->data - skb->mac_len,
-			       skb->mac.raw, skb->mac_len);
+	old_mac = skb_mac_header(skb);
+	skb_set_mac_header(skb, -skb->mac_len);
+	memmove(skb_mac_header(skb), old_mac, skb->mac_len);
 
-	ip6h = skb->nh.ipv6h;
+	ip6h = ipv6_hdr(skb);
 	ip6h->payload_len = htons(skb->len - size);
 	ipv6_addr_copy(&ip6h->daddr, (struct in6_addr *) &x->sel.daddr.a6);
 	ipv6_addr_copy(&ip6h->saddr, (struct in6_addr *) &x->sel.saddr.a6);
diff --git a/net/ipv6/xfrm6_mode_ro.c b/net/ipv6/xfrm6_mode_ro.c
index 6031c16..6ad6d7a 100644
--- a/net/ipv6/xfrm6_mode_ro.c
+++ b/net/ipv6/xfrm6_mode_ro.c
@@ -50,11 +50,12 @@
 	int hdr_len;
 
 	skb_push(skb, x->props.header_len);
-	iph = skb->nh.ipv6h;
+	iph = ipv6_hdr(skb);
 
 	hdr_len = x->type->hdr_offset(x, skb, &prevhdr);
-	skb->nh.raw = prevhdr - x->props.header_len;
-	skb->h.raw = skb->data + hdr_len;
+	skb_set_network_header(skb,
+			       (prevhdr - x->props.header_len) - skb->data);
+	skb_set_transport_header(skb, hdr_len);
 	memmove(skb->data, iph, hdr_len);
 	return 0;
 }
diff --git a/net/ipv6/xfrm6_mode_transport.c b/net/ipv6/xfrm6_mode_transport.c
index 3a4b39b..c026bfe 100644
--- a/net/ipv6/xfrm6_mode_transport.c
+++ b/net/ipv6/xfrm6_mode_transport.c
@@ -32,11 +32,12 @@
 	int hdr_len;
 
 	skb_push(skb, x->props.header_len);
-	iph = skb->nh.ipv6h;
+	iph = ipv6_hdr(skb);
 
 	hdr_len = x->type->hdr_offset(x, skb, &prevhdr);
-	skb->nh.raw = prevhdr - x->props.header_len;
-	skb->h.raw = skb->data + hdr_len;
+	skb_set_network_header(skb,
+			       (prevhdr - x->props.header_len) - skb->data);
+	skb_set_transport_header(skb, hdr_len);
 	memmove(skb->data, iph, hdr_len);
 	return 0;
 }
@@ -51,13 +52,16 @@
  */
 static int xfrm6_transport_input(struct xfrm_state *x, struct sk_buff *skb)
 {
-	int ihl = skb->data - skb->h.raw;
+	int ihl = skb->data - skb_transport_header(skb);
 
-	if (skb->h.raw != skb->nh.raw)
-		skb->nh.raw = memmove(skb->h.raw, skb->nh.raw, ihl);
-	skb->nh.ipv6h->payload_len = htons(skb->len + ihl -
+	if (skb->transport_header != skb->network_header) {
+		memmove(skb_transport_header(skb),
+			skb_network_header(skb), ihl);
+		skb->network_header = skb->transport_header;
+	}
+	ipv6_hdr(skb)->payload_len = htons(skb->len + ihl -
 					   sizeof(struct ipv6hdr));
-	skb->h.raw = skb->data;
+	skb_reset_transport_header(skb);
 	return 0;
 }
 
diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c
index 0bc866c..a6c0cdf 100644
--- a/net/ipv6/xfrm6_mode_tunnel.c
+++ b/net/ipv6/xfrm6_mode_tunnel.c
@@ -18,8 +18,8 @@
 
 static inline void ipip6_ecn_decapsulate(struct sk_buff *skb)
 {
-	struct ipv6hdr *outer_iph = skb->nh.ipv6h;
-	struct ipv6hdr *inner_iph = skb->h.ipv6h;
+	struct ipv6hdr *outer_iph = ipv6_hdr(skb);
+	struct ipv6hdr *inner_iph = ipipv6_hdr(skb);
 
 	if (INET_ECN_is_ce(ipv6_get_dsfield(outer_iph)))
 		IP6_ECN_set_ce(inner_iph);
@@ -27,8 +27,8 @@
 
 static inline void ip6ip_ecn_decapsulate(struct sk_buff *skb)
 {
-	if (INET_ECN_is_ce(ipv6_get_dsfield(skb->nh.ipv6h)))
-			IP_ECN_set_ce(skb->h.ipiph);
+	if (INET_ECN_is_ce(ipv6_get_dsfield(ipv6_hdr(skb))))
+			IP_ECN_set_ce(ipip_hdr(skb));
 }
 
 /* Add encapsulation header.
@@ -51,12 +51,12 @@
 	int dsfield;
 
 	skb_push(skb, x->props.header_len);
-	iph = skb->nh.ipv6h;
+	iph = ipv6_hdr(skb);
 
-	skb->nh.raw = skb->data;
-	top_iph = skb->nh.ipv6h;
-	skb->nh.raw = &top_iph->nexthdr;
-	skb->h.ipv6h = top_iph + 1;
+	skb_reset_network_header(skb);
+	top_iph = ipv6_hdr(skb);
+	skb->transport_header = skb->network_header + sizeof(struct ipv6hdr);
+	skb->network_header   += offsetof(struct ipv6hdr, nexthdr);
 
 	top_iph->version = 6;
 	if (xdst->route->ops->family == AF_INET6) {
@@ -86,9 +86,11 @@
 static int xfrm6_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
 {
 	int err = -EINVAL;
+	const unsigned char *old_mac;
+	const unsigned char *nh = skb_network_header(skb);
 
-	if (skb->nh.raw[IP6CB(skb)->nhoff] != IPPROTO_IPV6
-	    && skb->nh.raw[IP6CB(skb)->nhoff] != IPPROTO_IPIP)
+	if (nh[IP6CB(skb)->nhoff] != IPPROTO_IPV6 &&
+	    nh[IP6CB(skb)->nhoff] != IPPROTO_IPIP)
 		goto out;
 	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
 		goto out;
@@ -97,9 +99,10 @@
 	    (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
 		goto out;
 
-	if (skb->nh.raw[IP6CB(skb)->nhoff] == IPPROTO_IPV6) {
+	nh = skb_network_header(skb);
+	if (nh[IP6CB(skb)->nhoff] == IPPROTO_IPV6) {
 		if (x->props.flags & XFRM_STATE_DECAP_DSCP)
-			ipv6_copy_dscp(skb->nh.ipv6h, skb->h.ipv6h);
+			ipv6_copy_dscp(ipv6_hdr(skb), ipipv6_hdr(skb));
 		if (!(x->props.flags & XFRM_STATE_NOECN))
 			ipip6_ecn_decapsulate(skb);
 	} else {
@@ -107,9 +110,10 @@
 			ip6ip_ecn_decapsulate(skb);
 		skb->protocol = htons(ETH_P_IP);
 	}
-	skb->mac.raw = memmove(skb->data - skb->mac_len,
-			       skb->mac.raw, skb->mac_len);
-	skb->nh.raw = skb->data;
+	old_mac = skb_mac_header(skb);
+	skb_set_mac_header(skb, -skb->mac_len);
+	memmove(skb_mac_header(skb), old_mac, skb->mac_len);
+	skb_reset_network_header(skb);
 	err = 0;
 
 out:
diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c
index d6d786b..56364a5 100644
--- a/net/ipv6/xfrm6_output.c
+++ b/net/ipv6/xfrm6_output.c
@@ -23,6 +23,8 @@
 	return ip6_find_1stfragopt(skb, prevhdr);
 }
 
+EXPORT_SYMBOL(xfrm6_find_1stfragopt);
+
 static int xfrm6_tunnel_check_size(struct sk_buff *skb)
 {
 	int mtu, ret = 0;
@@ -76,11 +78,11 @@
 		x->curlft.bytes += skb->len;
 		x->curlft.packets++;
 		if (x->props.mode == XFRM_MODE_ROUTEOPTIMIZATION)
-			x->lastused = (u64)xtime.tv_sec;
+			x->lastused = get_seconds();
 
 		spin_unlock_bh(&x->lock);
 
-		skb->nh.raw = skb->data;
+		skb_reset_network_header(skb);
 
 		if (!(skb->dst = dst_pop(dst))) {
 			err = -EHOSTUNREACH;
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index d8a585b..1faa2ea 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -240,7 +240,8 @@
 		if (!afinfo) {
 			dst = *dst_p;
 			goto error;
-		};
+		}
+
 		dst_prev->output = afinfo->output;
 		xfrm_state_put_afinfo(afinfo);
 		/* Sheit... I remember I did this right. Apparently,
@@ -270,17 +271,19 @@
 static inline void
 _decode_session6(struct sk_buff *skb, struct flowi *fl)
 {
-	u16 offset = skb->h.raw - skb->nh.raw;
-	struct ipv6hdr *hdr = skb->nh.ipv6h;
+	u16 offset = skb_network_header_len(skb);
+	struct ipv6hdr *hdr = ipv6_hdr(skb);
 	struct ipv6_opt_hdr *exthdr;
-	u8 nexthdr = skb->nh.raw[IP6CB(skb)->nhoff];
+	const unsigned char *nh = skb_network_header(skb);
+	u8 nexthdr = nh[IP6CB(skb)->nhoff];
 
 	memset(fl, 0, sizeof(struct flowi));
 	ipv6_addr_copy(&fl->fl6_dst, &hdr->daddr);
 	ipv6_addr_copy(&fl->fl6_src, &hdr->saddr);
 
-	while (pskb_may_pull(skb, skb->nh.raw + offset + 1 - skb->data)) {
-		exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
+	while (pskb_may_pull(skb, nh + offset + 1 - skb->data)) {
+		nh = skb_network_header(skb);
+		exthdr = (struct ipv6_opt_hdr *)(nh + offset);
 
 		switch (nexthdr) {
 		case NEXTHDR_ROUTING:
@@ -288,7 +291,7 @@
 		case NEXTHDR_DEST:
 			offset += ipv6_optlen(exthdr);
 			nexthdr = exthdr->nexthdr;
-			exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
+			exthdr = (struct ipv6_opt_hdr *)(nh + offset);
 			break;
 
 		case IPPROTO_UDP:
@@ -296,7 +299,7 @@
 		case IPPROTO_TCP:
 		case IPPROTO_SCTP:
 		case IPPROTO_DCCP:
-			if (pskb_may_pull(skb, skb->nh.raw + offset + 4 - skb->data)) {
+			if (pskb_may_pull(skb, nh + offset + 4 - skb->data)) {
 				__be16 *ports = (__be16 *)exthdr;
 
 				fl->fl_ip_sport = ports[0];
@@ -306,7 +309,7 @@
 			return;
 
 		case IPPROTO_ICMPV6:
-			if (pskb_may_pull(skb, skb->nh.raw + offset + 2 - skb->data)) {
+			if (pskb_may_pull(skb, nh + offset + 2 - skb->data)) {
 				u8 *icmp = (u8 *)exthdr;
 
 				fl->fl_icmp_type = icmp[0];
@@ -317,7 +320,7 @@
 
 #ifdef CONFIG_IPV6_MIP6
 		case IPPROTO_MH:
-			if (pskb_may_pull(skb, skb->nh.raw + offset + 3 - skb->data)) {
+			if (pskb_may_pull(skb, nh + offset + 3 - skb->data)) {
 				struct ip6_mh *mh;
 				mh = (struct ip6_mh *)exthdr;
 
@@ -335,7 +338,7 @@
 			fl->fl_ipsec_spi = 0;
 			fl->proto = nexthdr;
 			return;
-		};
+		}
 	}
 }
 
diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c
index 93c4223..5502cc94 100644
--- a/net/ipv6/xfrm6_tunnel.c
+++ b/net/ipv6/xfrm6_tunnel.c
@@ -257,11 +257,11 @@
 
 static int xfrm6_tunnel_rcv(struct sk_buff *skb)
 {
-	struct ipv6hdr *iph = skb->nh.ipv6h;
+	struct ipv6hdr *iph = ipv6_hdr(skb);
 	__be32 spi;
 
 	spi = xfrm6_tunnel_spi_lookup((xfrm_address_t *)&iph->saddr);
-	return xfrm6_rcv_spi(skb, spi);
+	return xfrm6_rcv_spi(skb, spi) > 0 ? : 0;
 }
 
 static int xfrm6_tunnel_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c
index cac35a7..392f8bc 100644
--- a/net/ipx/af_ipx.c
+++ b/net/ipx/af_ipx.c
@@ -576,7 +576,9 @@
 	skb2 = alloc_skb(len, GFP_ATOMIC);
 	if (skb2) {
 		skb_reserve(skb2, out_offset);
-		skb2->nh.raw = skb2->h.raw = skb_put(skb2, skb->len);
+		skb_reset_network_header(skb2);
+		skb_reset_transport_header(skb2);
+		skb_put(skb2, skb->len);
 		memcpy(ipx_hdr(skb2), ipx_hdr(skb), skb->len);
 		memcpy(skb2->cb, skb->cb, sizeof(skb->cb));
 	}
@@ -1807,8 +1809,8 @@
 				     copied);
 	if (rc)
 		goto out_free;
-	if (skb->tstamp.off_sec)
-		skb_get_timestamp(skb, &sk->sk_stamp);
+	if (skb->tstamp.tv64)
+		sk->sk_stamp = skb->tstamp;
 
 	msg->msg_namelen = sizeof(*sipx);
 
diff --git a/net/ipx/ipx_route.c b/net/ipx/ipx_route.c
index 8e1cad9..e16c114 100644
--- a/net/ipx/ipx_route.c
+++ b/net/ipx/ipx_route.c
@@ -203,7 +203,9 @@
 	skb->sk = sk;
 
 	/* Fill in IPX header */
-	skb->h.raw = skb->nh.raw = skb_put(skb, sizeof(struct ipxhdr));
+	skb_reset_network_header(skb);
+	skb_reset_transport_header(skb);
+	skb_put(skb, sizeof(struct ipxhdr));
 	ipx = ipx_hdr(skb);
 	ipx->ipx_pktsize = htons(len + sizeof(struct ipxhdr));
 	IPX_SKB_CB(skb)->ipx_tctrl = 0;
diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c
index 0eb7d59..06c97c6 100644
--- a/net/irda/af_irda.c
+++ b/net/irda/af_irda.c
@@ -89,7 +89,6 @@
 
 	self = instance;
 	sk = instance;
-	IRDA_ASSERT(sk != NULL, return -1;);
 
 	err = sock_queue_rcv_skb(sk, skb);
 	if (err) {
@@ -131,14 +130,12 @@
 	}
 
 	/* Prevent race conditions with irda_release() and irda_shutdown() */
+	bh_lock_sock(sk);
 	if (!sock_flag(sk, SOCK_DEAD) && sk->sk_state != TCP_CLOSE) {
-		lock_sock(sk);
 		sk->sk_state     = TCP_CLOSE;
-		sk->sk_err       = ECONNRESET;
 		sk->sk_shutdown |= SEND_SHUTDOWN;
 
 		sk->sk_state_change(sk);
-		release_sock(sk);
 
 		/* Close our TSAP.
 		 * If we leave it open, IrLMP put it back into the list of
@@ -158,6 +155,7 @@
 			self->tsap = NULL;
 		}
 	}
+	bh_unlock_sock(sk);
 
 	/* Note : once we are there, there is not much you want to do
 	 * with the socket anymore, apart from closing it.
@@ -220,7 +218,7 @@
 		break;
 	default:
 		self->max_data_size = irttp_get_max_seg_size(self->tsap);
-	};
+	}
 
 	IRDA_DEBUG(2, "%s(), max_data_size=%d\n", __FUNCTION__,
 		   self->max_data_size);
@@ -283,7 +281,7 @@
 		break;
 	default:
 		self->max_data_size = irttp_get_max_seg_size(self->tsap);
-	};
+	}
 
 	IRDA_DEBUG(2, "%s(), max_data_size=%d\n", __FUNCTION__,
 		   self->max_data_size);
@@ -306,8 +304,6 @@
 
 	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
-	IRDA_ASSERT(self != NULL, return;);
-
 	skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER,
 			GFP_ATOMIC);
 	if (skb == NULL) {
@@ -337,7 +333,7 @@
 
 	self = instance;
 	sk = instance;
-	IRDA_ASSERT(sk != NULL, return;);
+	BUG_ON(sk == NULL);
 
 	switch (flow) {
 	case FLOW_STOP:
@@ -449,7 +445,7 @@
 	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
 	self = (struct irda_sock *) priv;
-	IRDA_ASSERT(self != NULL, return;);
+	BUG_ON(self == NULL);
 
 	/* Nothing for the caller */
 	self->cachelog = NULL;
@@ -546,8 +542,6 @@
 {
 	IRDA_DEBUG(2, "%s(%p, %s)\n", __FUNCTION__, self, name);
 
-	IRDA_ASSERT(self != NULL, return -1;);
-
 	if (self->iriap) {
 		IRDA_WARNING("%s(): busy with a previous query\n",
 			     __FUNCTION__);
@@ -635,8 +629,6 @@
 
 	IRDA_DEBUG(2, "%s(), name=%s\n", __FUNCTION__, name);
 
-	IRDA_ASSERT(self != NULL, return -1;);
-
 	/* Ask lmp for the current discovery log
 	 * Note : we have to use irlmp_get_discoveries(), as opposed
 	 * to play with the cachelog directly, because while we are
@@ -784,8 +776,6 @@
 	struct irda_sock *self = irda_sk(sk);
 	int err;
 
-	IRDA_ASSERT(self != NULL, return -1;);
-
 	IRDA_DEBUG(2, "%s(%p)\n", __FUNCTION__, self);
 
 	if (addr_len != sizeof(struct sockaddr_irda))
@@ -841,8 +831,6 @@
 
 	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
-	IRDA_ASSERT(self != NULL, return -1;);
-
 	err = irda_create(newsock, sk->sk_protocol);
 	if (err)
 		return err;
@@ -873,44 +861,28 @@
 	 * calling us, the data is waiting for us ;-)
 	 * Jean II
 	 */
-	skb = skb_dequeue(&sk->sk_receive_queue);
-	if (skb == NULL) {
-		int ret = 0;
-		DECLARE_WAITQUEUE(waitq, current);
+	while (1) {
+		skb = skb_dequeue(&sk->sk_receive_queue);
+		if (skb)
+			break;
 
 		/* Non blocking operation */
 		if (flags & O_NONBLOCK)
 			return -EWOULDBLOCK;
 
-		/* The following code is a cut'n'paste of the
-		 * wait_event_interruptible() macro.
-		 * We don't us the macro because the condition has
-		 * side effects : we want to make sure that only one
-		 * skb get dequeued - Jean II */
-		add_wait_queue(sk->sk_sleep, &waitq);
-		for (;;) {
-			set_current_state(TASK_INTERRUPTIBLE);
-			skb = skb_dequeue(&sk->sk_receive_queue);
-			if (skb != NULL)
-				break;
-			if (!signal_pending(current)) {
-				schedule();
-				continue;
-			}
-			ret = -ERESTARTSYS;
-			break;
-		}
-		current->state = TASK_RUNNING;
-		remove_wait_queue(sk->sk_sleep, &waitq);
-		if(ret)
-			return -ERESTARTSYS;
+		err = wait_event_interruptible(*(sk->sk_sleep),
+					skb_peek(&sk->sk_receive_queue));
+		if (err)
+			return err;
 	}
 
 	newsk = newsock->sk;
+	if (newsk == NULL)
+		return -EIO;
+
 	newsk->sk_state = TCP_ESTABLISHED;
 
 	new = irda_sk(newsk);
-	IRDA_ASSERT(new != NULL, return -1;);
 
 	/* Now attach up the new socket */
 	new->tsap = irttp_dup(self->tsap, new);
@@ -1061,7 +1033,8 @@
 
 	if (sk->sk_state != TCP_ESTABLISHED) {
 		sock->state = SS_UNCONNECTED;
-		return sock_error(sk);	/* Always set at this point */
+		err = sock_error(sk);
+		return err? err : -ECONNRESET;
 	}
 
 	sock->state = SS_CONNECTED;
@@ -1171,8 +1144,6 @@
 {
 	IRDA_DEBUG(2, "%s(%p)\n", __FUNCTION__, self);
 
-	IRDA_ASSERT(self != NULL, return;);
-
 	/* Unregister with IrLMP */
 	irlmp_unregister_client(self->ckey);
 	irlmp_unregister_service(self->skey);
@@ -1274,7 +1245,6 @@
 	struct sock *sk = sock->sk;
 	struct irda_sock *self;
 	struct sk_buff *skb;
-	unsigned char *asmptr;
 	int err;
 
 	IRDA_DEBUG(4, "%s(), len=%zd\n", __FUNCTION__, len);
@@ -1292,7 +1262,6 @@
 		return -ENOTCONN;
 
 	self = irda_sk(sk);
-	IRDA_ASSERT(self != NULL, return -1;);
 
 	/* Check if IrTTP is wants us to slow down */
 
@@ -1317,9 +1286,9 @@
 		return -ENOBUFS;
 
 	skb_reserve(skb, self->max_header_size + 16);
-
-	asmptr = skb->h.raw = skb_put(skb, len);
-	err = memcpy_fromiovec(asmptr, msg->msg_iov, len);
+	skb_reset_transport_header(skb);
+	skb_put(skb, len);
+	err = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len);
 	if (err) {
 		kfree_skb(skb);
 		return err;
@@ -1355,16 +1324,16 @@
 
 	IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
 
-	IRDA_ASSERT(self != NULL, return -1;);
-	IRDA_ASSERT(!sock_error(sk), return -1;);
+	if ((err = sock_error(sk)) < 0)
+		return err;
 
 	skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
 				flags & MSG_DONTWAIT, &err);
 	if (!skb)
 		return err;
 
-	skb->h.raw = skb->data;
-	copied     = skb->len;
+	skb_reset_transport_header(skb);
+	copied = skb->len;
 
 	if (copied > size) {
 		IRDA_DEBUG(2, "%s(), Received truncated frame (%zd < %zd)!\n",
@@ -1403,13 +1372,13 @@
 	struct irda_sock *self = irda_sk(sk);
 	int noblock = flags & MSG_DONTWAIT;
 	size_t copied = 0;
-	int target = 1;
-	DECLARE_WAITQUEUE(waitq, current);
+	int target, err;
+	long timeo;
 
 	IRDA_DEBUG(3, "%s()\n", __FUNCTION__);
 
-	IRDA_ASSERT(self != NULL, return -1;);
-	IRDA_ASSERT(!sock_error(sk), return -1;);
+	if ((err = sock_error(sk)) < 0)
+		return err;
 
 	if (sock->flags & __SO_ACCEPTCON)
 		return(-EINVAL);
@@ -1417,8 +1386,8 @@
 	if (flags & MSG_OOB)
 		return -EOPNOTSUPP;
 
-	if (flags & MSG_WAITALL)
-		target = size;
+	target = sock_rcvlowat(sk, flags & MSG_WAITALL, size);
+	timeo = sock_rcvtimeo(sk, noblock);
 
 	msg->msg_namelen = 0;
 
@@ -1426,19 +1395,14 @@
 		int chunk;
 		struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue);
 
-		if (skb==NULL) {
+		if (skb == NULL) {
+			DEFINE_WAIT(wait);
 			int ret = 0;
 
 			if (copied >= target)
 				break;
 
-			/* The following code is a cut'n'paste of the
-			 * wait_event_interruptible() macro.
-			 * We don't us the macro because the test condition
-			 * is messy. - Jean II */
-			set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
-			add_wait_queue(sk->sk_sleep, &waitq);
-			set_current_state(TASK_INTERRUPTIBLE);
+			prepare_to_wait_exclusive(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
 
 			/*
 			 *	POSIX 1003.1g mandates this order.
@@ -1451,17 +1415,17 @@
 			else if (noblock)
 				ret = -EAGAIN;
 			else if (signal_pending(current))
-				ret = -ERESTARTSYS;
+				ret = sock_intr_errno(timeo);
+			else if (sk->sk_state != TCP_ESTABLISHED)
+				ret = -ENOTCONN;
 			else if (skb_peek(&sk->sk_receive_queue) == NULL)
 				/* Wait process until data arrives */
 				schedule();
 
-			current->state = TASK_RUNNING;
-			remove_wait_queue(sk->sk_sleep, &waitq);
-			clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+			finish_wait(sk->sk_sleep, &wait);
 
-			if(ret)
-				return(ret);
+			if (ret)
+				return ret;
 			if (sk->sk_shutdown & RCV_SHUTDOWN)
 				break;
 
@@ -1530,7 +1494,6 @@
 	struct sock *sk = sock->sk;
 	struct irda_sock *self;
 	struct sk_buff *skb;
-	unsigned char *asmptr;
 	int err;
 
 	IRDA_DEBUG(4, "%s(), len=%zd\n", __FUNCTION__, len);
@@ -1547,7 +1510,6 @@
 		return -ENOTCONN;
 
 	self = irda_sk(sk);
-	IRDA_ASSERT(self != NULL, return -1;);
 
 	/*
 	 * Check that we don't send out too big frames. This is an unreliable
@@ -1566,10 +1528,11 @@
 		return -ENOBUFS;
 
 	skb_reserve(skb, self->max_header_size);
+	skb_reset_transport_header(skb);
 
 	IRDA_DEBUG(4, "%s(), appending user data\n", __FUNCTION__);
-	asmptr = skb->h.raw = skb_put(skb, len);
-	err = memcpy_fromiovec(asmptr, msg->msg_iov, len);
+	skb_put(skb, len);
+	err = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len);
 	if (err) {
 		kfree_skb(skb);
 		return err;
@@ -1602,7 +1565,6 @@
 	__u8 pid = 0;
 	int bound = 0;
 	struct sk_buff *skb;
-	unsigned char *asmptr;
 	int err;
 
 	IRDA_DEBUG(4, "%s(), len=%zd\n", __FUNCTION__, len);
@@ -1616,7 +1578,6 @@
 	}
 
 	self = irda_sk(sk);
-	IRDA_ASSERT(self != NULL, return -1;);
 
 	/* Check if an address was specified with sendto. Jean II */
 	if (msg->msg_name) {
@@ -1662,10 +1623,11 @@
 		return -ENOBUFS;
 
 	skb_reserve(skb, self->max_header_size);
+	skb_reset_transport_header(skb);
 
 	IRDA_DEBUG(4, "%s(), appending user data\n", __FUNCTION__);
-	asmptr = skb->h.raw = skb_put(skb, len);
-	err = memcpy_fromiovec(asmptr, msg->msg_iov, len);
+	skb_put(skb, len);
+	err = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len);
 	if (err) {
 		kfree_skb(skb);
 		return err;
@@ -1689,8 +1651,6 @@
 	struct sock *sk = sock->sk;
 	struct irda_sock *self = irda_sk(sk);
 
-	IRDA_ASSERT(self != NULL, return -1;);
-
 	IRDA_DEBUG(1, "%s(%p)\n", __FUNCTION__, self);
 
 	sk->sk_state       = TCP_CLOSE;
@@ -1863,8 +1823,6 @@
 	struct ias_attrib *	ias_attr;	/* Attribute in IAS object */
 	int opt;
 
-	IRDA_ASSERT(self != NULL, return -1;);
-
 	IRDA_DEBUG(2, "%s(%p)\n", __FUNCTION__, self);
 
 	if (level != SOL_IRLMP)
diff --git a/net/irda/ircomm/ircomm_param.c b/net/irda/ircomm/ircomm_param.c
index 01d7c9c..e5e4792 100644
--- a/net/irda/ircomm/ircomm_param.c
+++ b/net/irda/ircomm/ircomm_param.c
@@ -133,8 +133,8 @@
 	 * Inserting is a little bit tricky since we don't know how much
 	 * room we will need. But this should hopefully work OK
 	 */
-	count = irda_param_insert(self, pi, skb->tail, skb_tailroom(skb),
-				  &ircomm_param_info);
+	count = irda_param_insert(self, pi, skb_tail_pointer(skb),
+				  skb_tailroom(skb), &ircomm_param_info);
 	if (count < 0) {
 		IRDA_WARNING("%s(), no room for parameter!\n", __FUNCTION__);
 		spin_unlock_irqrestore(&self->spinlock, flags);
diff --git a/net/irda/irda_device.c b/net/irda/irda_device.c
index e717801..7b5def1 100644
--- a/net/irda/irda_device.c
+++ b/net/irda/irda_device.c
@@ -375,7 +375,7 @@
 dongle_t *irda_device_dongle_init(struct net_device *dev, int type)
 {
 	struct dongle_reg *reg;
-	dongle_t *dongle = NULL;
+	dongle_t *dongle = kzalloc(sizeof(dongle_t), GFP_KERNEL);
 
 	might_sleep();
 
@@ -397,19 +397,14 @@
 	if (!reg || !try_module_get(reg->owner) ) {
 		IRDA_ERROR("IrDA: Unable to find requested dongle type %x\n",
 			   type);
-		goto out;
+		kfree(dongle);
+		dongle = NULL;
 	}
-
-	/* Allocate dongle info for this instance */
-	dongle = kzalloc(sizeof(dongle_t), GFP_KERNEL);
-	if (!dongle)
-		goto out;
-
-	/* Bind the registration info to this particular instance */
-	dongle->issue = reg;
-	dongle->dev = dev;
-
- out:
+	if (dongle) {
+		/* Bind the registration info to this particular instance */
+		dongle->issue = reg;
+		dongle->dev = dev;
+	}
 	spin_unlock(&dongles->hb_spinlock);
 	return dongle;
 }
diff --git a/net/irda/irlan/irlan_common.c b/net/irda/irlan/irlan_common.c
index fcf9d65..ed69773 100644
--- a/net/irda/irlan/irlan_common.c
+++ b/net/irda/irlan/irlan_common.c
@@ -1039,7 +1039,7 @@
 	}
 
 	/* Insert at end of sk-buffer */
-	frame = skb->tail;
+	frame = skb_tail_pointer(skb);
 
 	/* Make space for data */
 	if (skb_tailroom(skb) < (param_len+value_len+3)) {
diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c
index 672ab3f..c421521 100644
--- a/net/irda/irlan/irlan_eth.c
+++ b/net/irda/irlan/irlan_eth.c
@@ -234,8 +234,7 @@
 	 * might have been previously set by the low level IrDA network
 	 * device driver
 	 */
-	skb->dev = self->dev;
-	skb->protocol=eth_type_trans(skb, skb->dev); /* Remove eth header */
+	skb->protocol = eth_type_trans(skb, self->dev); /* Remove eth header */
 
 	self->stats.rx_packets++;
 	self->stats.rx_bytes += skb->len;
diff --git a/net/irda/irlap_event.c b/net/irda/irlap_event.c
index 7b6433f..0b02073 100644
--- a/net/irda/irlap_event.c
+++ b/net/irda/irlap_event.c
@@ -590,7 +590,7 @@
 		if (!self->discovery_log) {
 			IRDA_WARNING("%s: discovery log is gone! "
 				     "maybe the discovery timeout has been set"
-				     " to short?\n", __FUNCTION__);
+				     " too short?\n", __FUNCTION__);
 			break;
 		}
 		hashbin_insert(self->discovery_log,
diff --git a/net/irda/irlap_frame.c b/net/irda/irlap_frame.c
index 0b04603..3c5a68e 100644
--- a/net/irda/irlap_frame.c
+++ b/net/irda/irlap_frame.c
@@ -93,7 +93,9 @@
 {
 	/* Some common init stuff */
 	skb->dev = self->netdev;
-	skb->h.raw = skb->nh.raw = skb->mac.raw = skb->data;
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
+	skb_reset_transport_header(skb);
 	skb->protocol = htons(ETH_P_IRDA);
 	skb->priority = TC_PRIO_BESTEFFORT;
 
@@ -411,7 +413,7 @@
 	IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
 
 	if (!pskb_may_pull(skb, sizeof(struct xid_frame))) {
-		IRDA_ERROR("%s: frame to short!\n", __FUNCTION__);
+		IRDA_ERROR("%s: frame too short!\n", __FUNCTION__);
 		return;
 	}
 
@@ -482,7 +484,7 @@
 	char *text;
 
 	if (!pskb_may_pull(skb, sizeof(struct xid_frame))) {
-		IRDA_ERROR("%s: frame to short!\n", __FUNCTION__);
+		IRDA_ERROR("%s: frame too short!\n", __FUNCTION__);
 		return;
 	}
 
@@ -526,7 +528,7 @@
 		/* Check if things are sane at this point... */
 		if((discovery_info == NULL) ||
 		   !pskb_may_pull(skb, 3)) {
-			IRDA_ERROR("%s: discovery frame to short!\n",
+			IRDA_ERROR("%s: discovery frame too short!\n",
 				   __FUNCTION__);
 			return;
 		}
@@ -1171,7 +1173,7 @@
 	IRDA_ASSERT(info != NULL, return;);
 
 	if (!pskb_may_pull(skb, 4)) {
-		IRDA_ERROR("%s: frame to short!\n", __FUNCTION__);
+		IRDA_ERROR("%s: frame too short!\n", __FUNCTION__);
 		return;
 	}
 
@@ -1260,7 +1262,7 @@
 	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
 	if (!pskb_may_pull(skb, sizeof(*frame))) {
-		IRDA_ERROR("%s: frame to short!\n", __FUNCTION__);
+		IRDA_ERROR("%s: frame too short!\n", __FUNCTION__);
 		return;
 	}
 	frame = (struct test_frame *) skb->data;
@@ -1268,7 +1270,7 @@
 	/* Broadcast frames must carry saddr and daddr fields */
 	if (info->caddr == CBROADCAST) {
 		if (skb->len < sizeof(struct test_frame)) {
-			IRDA_DEBUG(0, "%s() test frame to short!\n",
+			IRDA_DEBUG(0, "%s() test frame too short!\n",
 				   __FUNCTION__);
 			return;
 		}
@@ -1334,7 +1336,7 @@
 
 	/* Check if frame is large enough for parsing */
 	if (!pskb_may_pull(skb, 2)) {
-		IRDA_ERROR("%s: frame to short!\n", __FUNCTION__);
+		IRDA_ERROR("%s: frame too short!\n", __FUNCTION__);
 		dev_kfree_skb(skb);
 		return -1;
 	}
diff --git a/net/irda/irqueue.c b/net/irda/irqueue.c
index 9266233..d058b46 100644
--- a/net/irda/irqueue.c
+++ b/net/irda/irqueue.c
@@ -384,6 +384,9 @@
  *    for deallocating this structure if it's complex. If not the user can
  *    just supply kfree, which should take care of the job.
  */
+#ifdef CONFIG_LOCKDEP
+static int hashbin_lock_depth = 0;
+#endif
 int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func)
 {
 	irda_queue_t* queue;
@@ -395,7 +398,8 @@
 
 	/* Synchronize */
 	if ( hashbin->hb_type & HB_LOCK ) {
-		spin_lock_irqsave(&hashbin->hb_spinlock, flags);
+		spin_lock_irqsave_nested(&hashbin->hb_spinlock, flags,
+					 hashbin_lock_depth++);
 	}
 
 	/*
@@ -419,6 +423,9 @@
 	/* Release lock */
 	if ( hashbin->hb_type & HB_LOCK) {
 		spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
+#ifdef CONFIG_LOCKDEP
+		hashbin_lock_depth--;
+#endif
 	}
 
 	/*
diff --git a/net/irda/irttp.c b/net/irda/irttp.c
index da3f2bc..7069e4a 100644
--- a/net/irda/irttp.c
+++ b/net/irda/irttp.c
@@ -256,7 +256,7 @@
 	 *  Copy all fragments to a new buffer
 	 */
 	while ((frag = skb_dequeue(&self->rx_fragments)) != NULL) {
-		memcpy(skb->data+n, frag->data, frag->len);
+		skb_copy_to_linear_data_offset(skb, n, frag->data, frag->len);
 		n += frag->len;
 
 		dev_kfree_skb(frag);
@@ -314,8 +314,8 @@
 		skb_reserve(frag, self->max_header_size);
 
 		/* Copy data from the original skb into this fragment. */
-		memcpy(skb_put(frag, self->max_seg_size), skb->data,
-		       self->max_seg_size);
+		skb_copy_from_linear_data(skb, skb_put(frag, self->max_seg_size),
+			      self->max_seg_size);
 
 		/* Insert TTP header, with the more bit set */
 		frame = skb_push(frag, TTP_HEADER);
@@ -551,7 +551,7 @@
 	}
 
 	if (skb->len > self->max_seg_size) {
-		IRDA_DEBUG(1, "%s(), UData is to large for IrLAP!\n",
+		IRDA_DEBUG(1, "%s(), UData is too large for IrLAP!\n",
 			   __FUNCTION__);
 		goto err;
 	}
@@ -598,7 +598,7 @@
 	 *  inside an IrLAP frame
 	 */
 	if ((self->tx_max_sdu_size == 0) && (skb->len > self->max_seg_size)) {
-		IRDA_ERROR("%s: SAR disabled, and data is to large for IrLAP!\n",
+		IRDA_ERROR("%s: SAR disabled, and data is too large for IrLAP!\n",
 			   __FUNCTION__);
 		ret = -EMSGSIZE;
 		goto err;
diff --git a/net/irda/parameters.c b/net/irda/parameters.c
index 75a72d2..2627dad 100644
--- a/net/irda/parameters.c
+++ b/net/irda/parameters.c
@@ -160,7 +160,7 @@
 	}
 	/* Check if buffer is long enough for insertion */
 	if (len < (2+p.pl)) {
-		IRDA_WARNING("%s: buffer to short for insertion!\n",
+		IRDA_WARNING("%s: buffer too short for insertion!\n",
 			     __FUNCTION__);
 		return -1;
 	}
@@ -216,7 +216,7 @@
 
 	/* Check if buffer is long enough for parsing */
 	if (len < (2+p.pl)) {
-		IRDA_WARNING("%s: buffer to short for parsing! "
+		IRDA_WARNING("%s: buffer too short for parsing! "
 			     "Need %d bytes, but len is only %d\n",
 			     __FUNCTION__, p.pl, len);
 		return -1;
@@ -304,7 +304,7 @@
 
 	/* Check if buffer is long enough for parsing */
 	if (len < (2+p.pl)) {
-		IRDA_WARNING("%s: buffer to short for parsing! "
+		IRDA_WARNING("%s: buffer too short for parsing! "
 			     "Need %d bytes, but len is only %d\n",
 			     __FUNCTION__, p.pl, len);
 		return -1;
@@ -343,7 +343,7 @@
 
 	/* Check if buffer is long enough for parsing */
 	if (len < (2+p.pl)) {
-		IRDA_WARNING("%s: buffer to short for parsing! "
+		IRDA_WARNING("%s: buffer too short for parsing! "
 			     "Need %d bytes, but len is only %d\n",
 			     __FUNCTION__, p.pl, len);
 		return -1;
diff --git a/net/irda/qos.c b/net/irda/qos.c
index 349012c..aeb18cf 100644
--- a/net/irda/qos.c
+++ b/net/irda/qos.c
@@ -469,49 +469,49 @@
 	int ret;
 
 	/* Insert data rate */
-	ret = irda_param_insert(self, PI_BAUD_RATE, skb->tail,
+	ret = irda_param_insert(self, PI_BAUD_RATE, skb_tail_pointer(skb),
 				skb_tailroom(skb), &irlap_param_info);
 	if (ret < 0)
 		return ret;
 	skb_put(skb, ret);
 
 	/* Insert max turnaround time */
-	ret = irda_param_insert(self, PI_MAX_TURN_TIME, skb->tail,
+	ret = irda_param_insert(self, PI_MAX_TURN_TIME, skb_tail_pointer(skb),
 				skb_tailroom(skb), &irlap_param_info);
 	if (ret < 0)
 		return ret;
 	skb_put(skb, ret);
 
 	/* Insert data size */
-	ret = irda_param_insert(self, PI_DATA_SIZE, skb->tail,
+	ret = irda_param_insert(self, PI_DATA_SIZE, skb_tail_pointer(skb),
 				skb_tailroom(skb), &irlap_param_info);
 	if (ret < 0)
 		return ret;
 	skb_put(skb, ret);
 
 	/* Insert window size */
-	ret = irda_param_insert(self, PI_WINDOW_SIZE, skb->tail,
+	ret = irda_param_insert(self, PI_WINDOW_SIZE, skb_tail_pointer(skb),
 				skb_tailroom(skb), &irlap_param_info);
 	if (ret < 0)
 		return ret;
 	skb_put(skb, ret);
 
 	/* Insert additional BOFs */
-	ret = irda_param_insert(self, PI_ADD_BOFS, skb->tail,
+	ret = irda_param_insert(self, PI_ADD_BOFS, skb_tail_pointer(skb),
 				skb_tailroom(skb), &irlap_param_info);
 	if (ret < 0)
 		return ret;
 	skb_put(skb, ret);
 
 	/* Insert minimum turnaround time */
-	ret = irda_param_insert(self, PI_MIN_TURN_TIME, skb->tail,
+	ret = irda_param_insert(self, PI_MIN_TURN_TIME, skb_tail_pointer(skb),
 				skb_tailroom(skb), &irlap_param_info);
 	if (ret < 0)
 		return ret;
 	skb_put(skb, ret);
 
 	/* Insert link disconnect/threshold time */
-	ret = irda_param_insert(self, PI_LINK_DISC, skb->tail,
+	ret = irda_param_insert(self, PI_LINK_DISC, skb_tail_pointer(skb),
 				skb_tailroom(skb), &irlap_param_info);
 	if (ret < 0)
 		return ret;
diff --git a/net/irda/wrapper.c b/net/irda/wrapper.c
index 5abfb71..a7a7f19 100644
--- a/net/irda/wrapper.c
+++ b/net/irda/wrapper.c
@@ -239,7 +239,8 @@
 
 	if(docopy) {
 		/* Copy data without CRC (lenght already checked) */
-		memcpy(newskb->data, rx_buff->data, rx_buff->len - 2);
+		skb_copy_to_linear_data(newskb, rx_buff->data,
+					rx_buff->len - 2);
 		/* Deliver this skb */
 		dataskb = newskb;
 	} else {
@@ -256,7 +257,7 @@
 
 	/* Feed it to IrLAP layer */
 	dataskb->dev = dev;
-	dataskb->mac.raw  = dataskb->data;
+	skb_reset_mac_header(dataskb);
 	dataskb->protocol = htons(ETH_P_IRDA);
 
 	netif_rx(dataskb);
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index acc9421..e84c924 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -181,7 +181,7 @@
 	default:
 		sock_set_flag(sk, SOCK_ZAPPED);
 		break;
-	};
+	}
 
 	release_sock(sk);
 	iucv_sock_kill(sk);
@@ -953,8 +953,8 @@
 			return;
 		}
 
-		skb->h.raw = skb->data;
-		skb->nh.raw = skb->data;
+		skb_reset_transport_header(skb);
+		skb_reset_network_header(skb);
 		skb->len = msg->length;
 	}
 
diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c
index 1b10d57..903bdb6 100644
--- a/net/iucv/iucv.c
+++ b/net/iucv/iucv.c
@@ -90,20 +90,43 @@
 	u32 res2[8];
 };
 
-struct iucv_work {
+struct iucv_irq_list {
 	struct list_head list;
 	struct iucv_irq_data data;
 };
 
-static LIST_HEAD(iucv_work_queue);
-static DEFINE_SPINLOCK(iucv_work_lock);
-
 static struct iucv_irq_data *iucv_irq_data;
 static cpumask_t iucv_buffer_cpumask = CPU_MASK_NONE;
 static cpumask_t iucv_irq_cpumask = CPU_MASK_NONE;
 
-static void iucv_tasklet_handler(unsigned long);
-static DECLARE_TASKLET(iucv_tasklet, iucv_tasklet_handler,0);
+/*
+ * Queue of interrupt buffers lock for delivery via the tasklet
+ * (fast but can't call smp_call_function).
+ */
+static LIST_HEAD(iucv_task_queue);
+
+/*
+ * The tasklet for fast delivery of iucv interrupts.
+ */
+static void iucv_tasklet_fn(unsigned long);
+static DECLARE_TASKLET(iucv_tasklet, iucv_tasklet_fn,0);
+
+/*
+ * Queue of interrupt buffers for delivery via a work queue
+ * (slower but can call smp_call_function).
+ */
+static LIST_HEAD(iucv_work_queue);
+
+/*
+ * The work element to deliver path pending interrupts.
+ */
+static void iucv_work_fn(struct work_struct *work);
+static DECLARE_WORK(iucv_work, iucv_work_fn);
+
+/*
+ * Spinlock protecting task and work queue.
+ */
+static DEFINE_SPINLOCK(iucv_queue_lock);
 
 enum iucv_command_codes {
 	IUCV_QUERY = 0,
@@ -147,10 +170,10 @@
 static DEFINE_SPINLOCK(iucv_table_lock);
 
 /*
- * iucv_tasklet_cpu: contains the number of the cpu executing the tasklet.
- * Needed for iucv_path_sever called from tasklet.
+ * iucv_active_cpu: contains the number of the cpu executing the tasklet
+ * or the work handler. Needed for iucv_path_sever called from tasklet.
  */
-static int iucv_tasklet_cpu = -1;
+static int iucv_active_cpu = -1;
 
 /*
  * Mutex and wait queue for iucv_register/iucv_unregister.
@@ -449,17 +472,19 @@
 {
 	int cpu;
 
+	preempt_disable();
 	for_each_online_cpu(cpu)
 		/* Enable all cpus with a declared buffer. */
 		if (cpu_isset(cpu, iucv_buffer_cpumask) &&
 		    !cpu_isset(cpu, iucv_irq_cpumask))
 			smp_call_function_on(iucv_allow_cpu, NULL, 0, 1, cpu);
+	preempt_enable();
 }
 
 /**
  * iucv_setmask_up
  *
- * Allow iucv interrupts on a single cpus.
+ * Allow iucv interrupts on a single cpu.
  */
 static void iucv_setmask_up(void)
 {
@@ -493,8 +518,10 @@
 		goto out;
 	/* Declare per cpu buffers. */
 	rc = -EIO;
+	preempt_disable();
 	for_each_online_cpu(cpu)
 		smp_call_function_on(iucv_declare_cpu, NULL, 0, 1, cpu);
+	preempt_enable();
 	if (cpus_empty(iucv_buffer_cpumask))
 		/* No cpu could declare an iucv buffer. */
 		goto out_path;
@@ -519,7 +546,6 @@
 	kfree(iucv_path_table);
 }
 
-#ifdef CONFIG_HOTPLUG_CPU
 static int __cpuinit iucv_cpu_notify(struct notifier_block *self,
 				     unsigned long action, void *hcpu)
 {
@@ -565,7 +591,6 @@
 static struct notifier_block iucv_cpu_notifier = {
 	.notifier_call = iucv_cpu_notify,
 };
-#endif
 
 /**
  * iucv_sever_pathid
@@ -586,48 +611,49 @@
 	return iucv_call_b2f0(IUCV_SEVER, parm);
 }
 
+#ifdef CONFIG_SMP
 /**
- * __iucv_cleanup_pathid
+ * __iucv_cleanup_queue
  * @dummy: unused dummy argument
  *
  * Nop function called via smp_call_function to force work items from
  * pending external iucv interrupts to the work queue.
  */
-static void __iucv_cleanup_pathid(void *dummy)
+static void __iucv_cleanup_queue(void *dummy)
 {
 }
+#endif
 
 /**
- * iucv_cleanup_pathid
- * @pathid: 16 bit pathid
+ * iucv_cleanup_queue
  *
  * Function called after a path has been severed to find all remaining
  * work items for the now stale pathid. The caller needs to hold the
  * iucv_table_lock.
  */
-static void iucv_cleanup_pathid(u16 pathid)
+static void iucv_cleanup_queue(void)
 {
-	struct iucv_work *p, *n;
+	struct iucv_irq_list *p, *n;
 
 	/*
-	 * Path is severed, the pathid can be reused immediatly on
-	 * a iucv connect or a connection pending interrupt.
-	 * iucv_path_connect and connection pending interrupt will
-	 * wait until the iucv_table_lock is released before the
-	 * recycled pathid enters the system.
-	 * Force remaining interrupts to the work queue, then
-	 * scan the work queue for items of this path.
+	 * When a path is severed, the pathid can be reused immediatly
+	 * on a iucv connect or a connection pending interrupt. Remove
+	 * all entries from the task queue that refer to a stale pathid
+	 * (iucv_path_table[ix] == NULL). Only then do the iucv connect
+	 * or deliver the connection pending interrupt. To get all the
+	 * pending interrupts force them to the work queue by calling
+	 * an empty function on all cpus.
 	 */
-	smp_call_function(__iucv_cleanup_pathid, NULL, 0, 1);
-	spin_lock_irq(&iucv_work_lock);
-	list_for_each_entry_safe(p, n, &iucv_work_queue, list) {
-		/* Remove work items for pathid except connection pending */
-		if (p->data.ippathid == pathid && p->data.iptype != 0x01) {
+	smp_call_function(__iucv_cleanup_queue, NULL, 0, 1);
+	spin_lock_irq(&iucv_queue_lock);
+	list_for_each_entry_safe(p, n, &iucv_task_queue, list) {
+		/* Remove stale work items from the task queue. */
+		if (iucv_path_table[p->data.ippathid] == NULL) {
 			list_del(&p->list);
 			kfree(p);
 		}
 	}
-	spin_unlock_irq(&iucv_work_lock);
+	spin_unlock_irq(&iucv_queue_lock);
 }
 
 /**
@@ -686,7 +712,6 @@
 		iucv_sever_pathid(p->pathid, NULL);
 		iucv_path_table[p->pathid] = NULL;
 		list_del(&p->list);
-		iucv_cleanup_pathid(p->pathid);
 		iucv_path_free(p);
 	}
 	spin_unlock_bh(&iucv_table_lock);
@@ -759,9 +784,9 @@
 	union iucv_param *parm;
 	int rc;
 
-	preempt_disable();
-	if (iucv_tasklet_cpu != smp_processor_id())
-		spin_lock_bh(&iucv_table_lock);
+	BUG_ON(in_atomic());
+	spin_lock_bh(&iucv_table_lock);
+	iucv_cleanup_queue();
 	parm = percpu_ptr(iucv_param, smp_processor_id());
 	memset(parm, 0, sizeof(union iucv_param));
 	parm->ctrl.ipmsglim = path->msglim;
@@ -796,9 +821,7 @@
 			rc = -EIO;
 		}
 	}
-	if (iucv_tasklet_cpu != smp_processor_id())
-		spin_unlock_bh(&iucv_table_lock);
-	preempt_enable();
+	spin_unlock_bh(&iucv_table_lock);
 	return rc;
 }
 
@@ -869,15 +892,14 @@
 
 
 	preempt_disable();
-	if (iucv_tasklet_cpu != smp_processor_id())
+	if (iucv_active_cpu != smp_processor_id())
 		spin_lock_bh(&iucv_table_lock);
 	rc = iucv_sever_pathid(path->pathid, userdata);
 	if (!rc) {
 		iucv_path_table[path->pathid] = NULL;
 		list_del_init(&path->list);
-		iucv_cleanup_pathid(path->pathid);
 	}
-	if (iucv_tasklet_cpu != smp_processor_id())
+	if (iucv_active_cpu != smp_processor_id())
 		spin_unlock_bh(&iucv_table_lock);
 	preempt_enable();
 	return rc;
@@ -1246,8 +1268,7 @@
 	struct iucv_path_complete *ipc = (void *) data;
 	struct iucv_path *path = iucv_path_table[ipc->ippathid];
 
-	BUG_ON(!path || !path->handler);
-	if (path->handler->path_complete)
+	if (path && path->handler && path->handler->path_complete)
 		path->handler->path_complete(path, ipc->ipuser);
 }
 
@@ -1275,14 +1296,14 @@
 	struct iucv_path_severed *ips = (void *) data;
 	struct iucv_path *path = iucv_path_table[ips->ippathid];
 
-	BUG_ON(!path || !path->handler);
+	if (!path || !path->handler)	/* Already severed */
+		return;
 	if (path->handler->path_severed)
 		path->handler->path_severed(path, ips->ipuser);
 	else {
 		iucv_sever_pathid(path->pathid, NULL);
 		iucv_path_table[path->pathid] = NULL;
 		list_del_init(&path->list);
-		iucv_cleanup_pathid(path->pathid);
 		iucv_path_free(path);
 	}
 }
@@ -1311,8 +1332,7 @@
 	struct iucv_path_quiesced *ipq = (void *) data;
 	struct iucv_path *path = iucv_path_table[ipq->ippathid];
 
-	BUG_ON(!path || !path->handler);
-	if (path->handler->path_quiesced)
+	if (path && path->handler && path->handler->path_quiesced)
 		path->handler->path_quiesced(path, ipq->ipuser);
 }
 
@@ -1340,8 +1360,7 @@
 	struct iucv_path_resumed *ipr = (void *) data;
 	struct iucv_path *path = iucv_path_table[ipr->ippathid];
 
-	BUG_ON(!path || !path->handler);
-	if (path->handler->path_resumed)
+	if (path && path->handler && path->handler->path_resumed)
 		path->handler->path_resumed(path, ipr->ipuser);
 }
 
@@ -1373,8 +1392,7 @@
 	struct iucv_path *path = iucv_path_table[imc->ippathid];
 	struct iucv_message msg;
 
-	BUG_ON(!path || !path->handler);
-	if (path->handler->message_complete) {
+	if (path && path->handler && path->handler->message_complete) {
 		msg.flags = imc->ipflags1;
 		msg.id = imc->ipmsgid;
 		msg.audit = imc->ipaudit;
@@ -1419,8 +1437,7 @@
 	struct iucv_path *path = iucv_path_table[imp->ippathid];
 	struct iucv_message msg;
 
-	BUG_ON(!path || !path->handler);
-	if (path->handler->message_pending) {
+	if (path && path->handler && path->handler->message_pending) {
 		msg.flags = imp->ipflags1;
 		msg.id = imp->ipmsgid;
 		msg.class = imp->iptrgcls;
@@ -1435,17 +1452,16 @@
 }
 
 /**
- * iucv_tasklet_handler:
+ * iucv_tasklet_fn:
  *
  * This tasklet loops over the queue of irq buffers created by
  * iucv_external_interrupt, calls the appropriate action handler
  * and then frees the buffer.
  */
-static void iucv_tasklet_handler(unsigned long ignored)
+static void iucv_tasklet_fn(unsigned long ignored)
 {
 	typedef void iucv_irq_fn(struct iucv_irq_data *);
 	static iucv_irq_fn *irq_fn[] = {
-		[0x01] = iucv_path_pending,
 		[0x02] = iucv_path_complete,
 		[0x03] = iucv_path_severed,
 		[0x04] = iucv_path_quiesced,
@@ -1455,38 +1471,70 @@
 		[0x08] = iucv_message_pending,
 		[0x09] = iucv_message_pending,
 	};
-	struct iucv_work *p;
+	struct list_head task_queue = LIST_HEAD_INIT(task_queue);
+	struct iucv_irq_list *p, *n;
 
 	/* Serialize tasklet, iucv_path_sever and iucv_path_connect. */
 	spin_lock(&iucv_table_lock);
-	iucv_tasklet_cpu = smp_processor_id();
+	iucv_active_cpu = smp_processor_id();
 
-	spin_lock_irq(&iucv_work_lock);
-	while (!list_empty(&iucv_work_queue)) {
-		p = list_entry(iucv_work_queue.next, struct iucv_work, list);
+	spin_lock_irq(&iucv_queue_lock);
+	list_splice_init(&iucv_task_queue, &task_queue);
+	spin_unlock_irq(&iucv_queue_lock);
+
+	list_for_each_entry_safe(p, n, &task_queue, list) {
 		list_del_init(&p->list);
-		spin_unlock_irq(&iucv_work_lock);
 		irq_fn[p->data.iptype](&p->data);
 		kfree(p);
-		spin_lock_irq(&iucv_work_lock);
 	}
-	spin_unlock_irq(&iucv_work_lock);
 
-	iucv_tasklet_cpu = -1;
+	iucv_active_cpu = -1;
 	spin_unlock(&iucv_table_lock);
 }
 
 /**
+ * iucv_work_fn:
+ *
+ * This work function loops over the queue of path pending irq blocks
+ * created by iucv_external_interrupt, calls the appropriate action
+ * handler and then frees the buffer.
+ */
+static void iucv_work_fn(struct work_struct *work)
+{
+	typedef void iucv_irq_fn(struct iucv_irq_data *);
+	struct list_head work_queue = LIST_HEAD_INIT(work_queue);
+	struct iucv_irq_list *p, *n;
+
+	/* Serialize tasklet, iucv_path_sever and iucv_path_connect. */
+	spin_lock_bh(&iucv_table_lock);
+	iucv_active_cpu = smp_processor_id();
+
+	spin_lock_irq(&iucv_queue_lock);
+	list_splice_init(&iucv_work_queue, &work_queue);
+	spin_unlock_irq(&iucv_queue_lock);
+
+	iucv_cleanup_queue();
+	list_for_each_entry_safe(p, n, &work_queue, list) {
+		list_del_init(&p->list);
+		iucv_path_pending(&p->data);
+		kfree(p);
+	}
+
+	iucv_active_cpu = -1;
+	spin_unlock_bh(&iucv_table_lock);
+}
+
+/**
  * iucv_external_interrupt
  * @code: irq code
  *
  * Handles external interrupts coming in from CP.
- * Places the interrupt buffer on a queue and schedules iucv_tasklet_handler().
+ * Places the interrupt buffer on a queue and schedules iucv_tasklet_fn().
  */
 static void iucv_external_interrupt(u16 code)
 {
 	struct iucv_irq_data *p;
-	struct iucv_work *work;
+	struct iucv_irq_list *work;
 
 	p = percpu_ptr(iucv_irq_data, smp_processor_id());
 	if (p->ippathid >= iucv_max_pathid) {
@@ -1500,16 +1548,23 @@
 		printk(KERN_ERR "iucv_do_int: unknown iucv interrupt\n");
 		return;
 	}
-	work = kmalloc(sizeof(struct iucv_work), GFP_ATOMIC);
+	work = kmalloc(sizeof(struct iucv_irq_list), GFP_ATOMIC);
 	if (!work) {
 		printk(KERN_WARNING "iucv_external_interrupt: out of memory\n");
 		return;
 	}
 	memcpy(&work->data, p, sizeof(work->data));
-	spin_lock(&iucv_work_lock);
-	list_add_tail(&work->list, &iucv_work_queue);
-	spin_unlock(&iucv_work_lock);
-	tasklet_schedule(&iucv_tasklet);
+	spin_lock(&iucv_queue_lock);
+	if (p->iptype == 0x01) {
+		/* Path pending interrupt. */
+		list_add_tail(&work->list, &iucv_work_queue);
+		schedule_work(&iucv_work);
+	} else {
+		/* The other interrupts. */
+		list_add_tail(&work->list, &iucv_task_queue);
+		tasklet_schedule(&iucv_tasklet);
+	}
+	spin_unlock(&iucv_queue_lock);
 }
 
 /**
@@ -1579,12 +1634,14 @@
  */
 static void iucv_exit(void)
 {
-	struct iucv_work *p, *n;
+	struct iucv_irq_list *p, *n;
 
-	spin_lock_irq(&iucv_work_lock);
+	spin_lock_irq(&iucv_queue_lock);
+	list_for_each_entry_safe(p, n, &iucv_task_queue, list)
+		kfree(p);
 	list_for_each_entry_safe(p, n, &iucv_work_queue, list)
 		kfree(p);
-	spin_unlock_irq(&iucv_work_lock);
+	spin_unlock_irq(&iucv_queue_lock);
 	unregister_hotcpu_notifier(&iucv_cpu_notifier);
 	percpu_free(iucv_param);
 	percpu_free(iucv_irq_data);
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 3450193..a994441 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -379,7 +379,7 @@
 		 */
 		return -EINVAL;
 		break;
-	};
+	}
 
 	return 0;
 }
@@ -3667,7 +3667,7 @@
 		copied = len;
 	}
 
-	skb->h.raw = skb->data;
+	skb_reset_transport_header(skb);
 	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 	if (err)
 		goto out_free;
diff --git a/net/llc/llc_input.c b/net/llc/llc_input.c
index b3f65d1..099ed8f 100644
--- a/net/llc/llc_input.c
+++ b/net/llc/llc_input.c
@@ -112,7 +112,7 @@
 	if (unlikely(!pskb_may_pull(skb, llc_len)))
 		return 0;
 
-	skb->h.raw += llc_len;
+	skb->transport_header += llc_len;
 	skb_pull(skb, llc_len);
 	if (skb->protocol == htons(ETH_P_802_2)) {
 		__be16 pdulen = eth_hdr(skb)->h_proto;
diff --git a/net/llc/llc_output.c b/net/llc/llc_output.c
index f4291f3..754f4fe 100644
--- a/net/llc/llc_output.c
+++ b/net/llc/llc_output.c
@@ -41,7 +41,8 @@
 		struct net_device *dev = skb->dev;
 		struct trh_hdr *trh;
 
-		skb->mac.raw = skb_push(skb, sizeof(*trh));
+		skb_push(skb, sizeof(*trh));
+		skb_reset_mac_header(skb);
 		trh = tr_hdr(skb);
 		trh->ac = AC;
 		trh->fc = LLC_FRAME;
@@ -52,7 +53,7 @@
 		if (da) {
 			memcpy(trh->daddr, da, dev->addr_len);
 			tr_source_route(skb, trh, dev);
-			skb->mac.raw = skb->data;
+			skb_reset_mac_header(skb);
 		}
 		break;
 	}
@@ -62,7 +63,8 @@
 		unsigned short len = skb->len;
 		struct ethhdr *eth;
 
-		skb->mac.raw = skb_push(skb, sizeof(*eth));
+		skb_push(skb, sizeof(*eth));
+		skb_reset_mac_header(skb);
 		eth = eth_hdr(skb);
 		eth->h_proto = htons(len);
 		memcpy(eth->h_dest, da, ETH_ALEN);
diff --git a/net/llc/llc_sap.c b/net/llc/llc_sap.c
index 2615dc8..2525165 100644
--- a/net/llc/llc_sap.c
+++ b/net/llc/llc_sap.c
@@ -36,11 +36,12 @@
 	struct sk_buff *skb = alloc_skb(128, GFP_ATOMIC);
 
 	if (skb) {
+		skb_reset_mac_header(skb);
 		skb_reserve(skb, 50);
-		skb->nh.raw   = skb->h.raw = skb->data;
+		skb_reset_network_header(skb);
+		skb_reset_transport_header(skb);
 		skb->protocol = htons(ETH_P_802_2);
 		skb->dev      = dev;
-		skb->mac.raw  = skb->head;
 		if (sk != NULL)
 			skb_set_owner_w(skb, sk);
 	}
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index 54698af..c558f32 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -25,6 +25,7 @@
 	  and is also scheduled to replace the old syslog-based ipt_LOG
 	  and ip6t_LOG modules.
 
+# Rename this to NF_CONNTRACK in a 2.6.25
 config NF_CONNTRACK_ENABLED
 	tristate "Netfilter connection tracking support"
 	help
@@ -39,42 +40,9 @@
 
 	  To compile it as a module, choose M here.  If unsure, say N.
 
-choice
-	prompt "Netfilter connection tracking support"
-	depends on NF_CONNTRACK_ENABLED
-
-config NF_CONNTRACK_SUPPORT
-	bool "Layer 3 Independent Connection tracking"
-	help
-	  Layer 3 independent connection tracking is experimental scheme
-	  which generalize ip_conntrack to support other layer 3 protocols.
-
-	  This is required to do Masquerading or other kinds of Network
-	  Address Translation (except for Fast NAT).  It can also be used to
-	  enhance packet filtering (see `Connection state match support'
-	  below).
-
-config IP_NF_CONNTRACK_SUPPORT
-	bool "Layer 3 Dependent Connection tracking (OBSOLETE)"
-	help
-	  The old, Layer 3 dependent ip_conntrack subsystem of netfilter.
-
-	  This is required to do Masquerading or other kinds of Network
-	  Address Translation (except for Fast NAT).  It can also be used to
-	  enhance packet filtering (see `Connection state match support'
-	  below).
-
-endchoice
-
 config NF_CONNTRACK
 	tristate
-	default m if NF_CONNTRACK_SUPPORT && NF_CONNTRACK_ENABLED=m
-	default y if NF_CONNTRACK_SUPPORT && NF_CONNTRACK_ENABLED=y
-
-config IP_NF_CONNTRACK
-	tristate
-	default m if IP_NF_CONNTRACK_SUPPORT && NF_CONNTRACK_ENABLED=m
-	default y if IP_NF_CONNTRACK_SUPPORT && NF_CONNTRACK_ENABLED=y
+	default NF_CONNTRACK_ENABLED
 
 config NF_CT_ACCT
 	bool "Connection tracking flow accounting"
@@ -303,9 +271,8 @@
 	tristate  '"CONNMARK" target support'
 	depends on NETFILTER_XTABLES
 	depends on IP_NF_MANGLE || IP6_NF_MANGLE
-	depends on IP_NF_CONNTRACK || NF_CONNTRACK
-	select IP_NF_CONNTRACK_MARK if IP_NF_CONNTRACK
-	select NF_CONNTRACK_MARK if NF_CONNTRACK
+	depends on NF_CONNTRACK
+	select NF_CONNTRACK_MARK
 	help
 	  This option adds a `CONNMARK' target, which allows one to manipulate
 	  the connection mark value.  Similar to the MARK target, but
@@ -366,7 +333,7 @@
 	tristate  '"NOTRACK" target support'
 	depends on NETFILTER_XTABLES
 	depends on IP_NF_RAW || IP6_NF_RAW
-	depends on IP_NF_CONNTRACK || NF_CONNTRACK
+	depends on NF_CONNTRACK
 	help
 	  The NOTRACK target allows a select rule to specify
 	  which packets *not* to enter the conntrack/NAT
@@ -387,9 +354,7 @@
 
 config NETFILTER_XT_TARGET_CONNSECMARK
 	tristate '"CONNSECMARK" target support'
-	depends on NETFILTER_XTABLES && \
-		   ((NF_CONNTRACK && NF_CONNTRACK_SECMARK) || \
-		    (IP_NF_CONNTRACK && IP_NF_CONNTRACK_SECMARK))
+	depends on NETFILTER_XTABLES && NF_CONNTRACK && NF_CONNTRACK_SECMARK
 	help
 	  The CONNSECMARK target copies security markings from packets
 	  to connections, and restores security markings from connections
@@ -437,9 +402,8 @@
 config NETFILTER_XT_MATCH_CONNBYTES
 	tristate  '"connbytes" per-connection counter match support'
 	depends on NETFILTER_XTABLES
-	depends on IP_NF_CONNTRACK || NF_CONNTRACK
-	select IP_NF_CT_ACCT if IP_NF_CONNTRACK
-	select NF_CT_ACCT if NF_CONNTRACK
+	depends on NF_CONNTRACK
+	select NF_CT_ACCT
 	help
 	  This option adds a `connbytes' match, which allows you to match the
 	  number of bytes and/or packets for each direction within a connection.
@@ -450,9 +414,8 @@
 config NETFILTER_XT_MATCH_CONNMARK
 	tristate  '"connmark" connection mark match support'
 	depends on NETFILTER_XTABLES
-	depends on IP_NF_CONNTRACK || NF_CONNTRACK
-	select IP_NF_CONNTRACK_MARK if IP_NF_CONNTRACK
-	select NF_CONNTRACK_MARK if NF_CONNTRACK
+	depends on NF_CONNTRACK
+	select NF_CONNTRACK_MARK
 	help
 	  This option adds a `connmark' match, which allows you to match the
 	  connection mark value previously set for the session by `CONNMARK'. 
@@ -464,7 +427,7 @@
 config NETFILTER_XT_MATCH_CONNTRACK
 	tristate '"conntrack" connection tracking match support'
 	depends on NETFILTER_XTABLES
-	depends on IP_NF_CONNTRACK || NF_CONNTRACK
+	depends on NF_CONNTRACK
 	help
 	  This is a general conntrack match module, a superset of the state match.
 
@@ -508,7 +471,7 @@
 config NETFILTER_XT_MATCH_HELPER
 	tristate '"helper" match support'
 	depends on NETFILTER_XTABLES
-	depends on IP_NF_CONNTRACK || NF_CONNTRACK
+	depends on NF_CONNTRACK
 	help
 	  Helper matching allows you to match packets in dynamic connections
 	  tracked by a conntrack-helper, ie. ip_conntrack_ftp
@@ -632,7 +595,7 @@
 config NETFILTER_XT_MATCH_STATE
 	tristate '"state" match support'
 	depends on NETFILTER_XTABLES
-	depends on IP_NF_CONNTRACK || NF_CONNTRACK
+	depends on NF_CONNTRACK
 	help
 	  Connection state matching allows you to match packets based on their
 	  relationship to a tracked connection (ie. previous packets).  This
diff --git a/net/netfilter/core.c b/net/netfilter/core.c
index c3ebdbd..a84478e 100644
--- a/net/netfilter/core.c
+++ b/net/netfilter/core.c
@@ -5,10 +5,6 @@
  * way.
  *
  * Rusty Russell (C)2000 -- This code is GPL.
- *
- * February 2000: Modified by James Morris to have 1 queue per protocol.
- * 15-Mar-2000:   Added NF_REPEAT --RR.
- * 08-May-2003:	  Internal logging interface added by Jozsef Kadlecsik.
  */
 #include <linux/kernel.h>
 #include <linux/netfilter.h>
@@ -244,6 +240,7 @@
 }
 EXPORT_SYMBOL(nf_proto_csum_replace4);
 
+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
 /* This does not belong here, but locally generated errors need it if connection
    tracking in use: without this, connection may not be in hash table, and hence
    manufactured ICMP or RST packets will not be associated with it. */
@@ -264,6 +261,22 @@
 }
 EXPORT_SYMBOL(nf_ct_attach);
 
+void (*nf_ct_destroy)(struct nf_conntrack *);
+EXPORT_SYMBOL(nf_ct_destroy);
+
+void nf_conntrack_destroy(struct nf_conntrack *nfct)
+{
+	void (*destroy)(struct nf_conntrack *);
+
+	rcu_read_lock();
+	destroy = rcu_dereference(nf_ct_destroy);
+	BUG_ON(destroy == NULL);
+	destroy(nfct);
+	rcu_read_unlock();
+}
+EXPORT_SYMBOL(nf_conntrack_destroy);
+#endif /* CONFIG_NF_CONNTRACK */
+
 #ifdef CONFIG_PROC_FS
 struct proc_dir_entry *proc_net_netfilter;
 EXPORT_SYMBOL(proc_net_netfilter);
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index b3a70eb..e132c8a 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -9,24 +9,6 @@
  * 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.
- *
- * 23 Apr 2001: Harald Welte <laforge@gnumonks.org>
- *	- new API and handling of conntrack/nat helpers
- *	- now capable of multiple expectations for one master
- * 16 Jul 2002: Harald Welte <laforge@gnumonks.org>
- *	- add usage/reference counts to ip_conntrack_expect
- *	- export ip_conntrack[_expect]_{find_get,put} functions
- * 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
- *	- generalize L3 protocol denendent part.
- * 23 Mar 2004: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
- *	- add support various size of conntrack structures.
- * 26 Jan 2006: Harald Welte <laforge@netfilter.org>
- * 	- restructure nf_conn (introduce nf_conn_help)
- * 	- redesign 'features' how they were originally intended
- * 26 Feb 2006: Pablo Neira Ayuso <pablo@eurodev.net>
- * 	- add support for L3 protocol module load on demand.
- *
- * Derived from net/ipv4/netfilter/ip_conntrack_core.c
  */
 
 #include <linux/types.h>
@@ -128,10 +110,11 @@
 				  unsigned int size, unsigned int rnd)
 {
 	unsigned int a, b;
-	a = jhash((void *)tuple->src.u3.all, sizeof(tuple->src.u3.all),
-		  ((tuple->src.l3num) << 16) | tuple->dst.protonum);
-	b = jhash((void *)tuple->dst.u3.all, sizeof(tuple->dst.u3.all),
-			(tuple->src.u.all << 16) | tuple->dst.u.all);
+
+	a = jhash2(tuple->src.u3.all, ARRAY_SIZE(tuple->src.u3.all),
+		   (tuple->src.l3num << 16) | tuple->dst.protonum);
+	b = jhash2(tuple->dst.u3.all, ARRAY_SIZE(tuple->dst.u3.all),
+		   (tuple->src.u.all << 16) | tuple->dst.u.all);
 
 	return jhash_2words(a, b, rnd) % size;
 }
@@ -633,13 +616,11 @@
 	memset(conntrack, 0, nf_ct_cache[features].size);
 	conntrack->features = features;
 	atomic_set(&conntrack->ct_general.use, 1);
-	conntrack->ct_general.destroy = destroy_conntrack;
 	conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *orig;
 	conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *repl;
 	/* Don't set timer yet: wait for confirmation */
-	init_timer(&conntrack->timeout);
-	conntrack->timeout.data = (unsigned long)conntrack;
-	conntrack->timeout.function = death_by_timeout;
+	setup_timer(&conntrack->timeout, death_by_timeout,
+		    (unsigned long)conntrack);
 	read_unlock_bh(&nf_ct_cache_lock);
 
 	return conntrack;
@@ -768,7 +749,7 @@
 	struct nf_conntrack_tuple_hash *h;
 	struct nf_conn *ct;
 
-	if (!nf_ct_get_tuple(skb, (unsigned int)(skb->nh.raw - skb->data),
+	if (!nf_ct_get_tuple(skb, skb_network_offset(skb),
 			     dataoff, l3num, protonum, &tuple, l3proto,
 			     l4proto)) {
 		DEBUGP("resolve_normal_ct: Can't get tuple\n");
@@ -960,7 +941,7 @@
 	if (do_acct) {
 		ct->counters[CTINFO2DIR(ctinfo)].packets++;
 		ct->counters[CTINFO2DIR(ctinfo)].bytes +=
-			skb->len - (unsigned int)(skb->nh.raw - skb->data);
+			skb->len - skb_network_offset(skb);
 
 		if ((ct->counters[CTINFO2DIR(ctinfo)].packets & 0x80000000)
 		    || (ct->counters[CTINFO2DIR(ctinfo)].bytes & 0x80000000))
@@ -1140,6 +1121,8 @@
 	while (atomic_read(&nf_conntrack_untracked.ct_general.use) > 1)
 		schedule();
 
+	rcu_assign_pointer(nf_ct_destroy, NULL);
+
 	for (i = 0; i < NF_CT_F_NUM; i++) {
 		if (nf_ct_cache[i].use == 0)
 			continue;
@@ -1152,14 +1135,7 @@
 	free_conntrack_hash(nf_conntrack_hash, nf_conntrack_vmalloc,
 			    nf_conntrack_htable_size);
 
-	nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_generic);
-
-	/* free l3proto protocol tables */
-	for (i = 0; i < PF_MAX; i++)
-		if (nf_ct_protos[i]) {
-			kfree(nf_ct_protos[i]);
-			nf_ct_protos[i] = NULL;
-		}
+	nf_conntrack_proto_fini();
 }
 
 static struct list_head *alloc_hashtable(int size, int *vmalloced)
@@ -1237,7 +1213,6 @@
 
 int __init nf_conntrack_init(void)
 {
-	unsigned int i;
 	int ret;
 
 	/* Idea from tcp.c: use 1/16384 of memory.  On i386: 32MB
@@ -1279,18 +1254,13 @@
 		goto err_free_conntrack_slab;
 	}
 
-	ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_generic);
+	ret = nf_conntrack_proto_init();
 	if (ret < 0)
 		goto out_free_expect_slab;
 
-	/* Don't NEED lock here, but good form anyway. */
-	write_lock_bh(&nf_conntrack_lock);
-	for (i = 0; i < AF_MAX; i++)
-		nf_ct_l3protos[i] = &nf_conntrack_l3proto_generic;
-	write_unlock_bh(&nf_conntrack_lock);
-
 	/* For use by REJECT target */
 	rcu_assign_pointer(ip_ct_attach, __nf_conntrack_attach);
+	rcu_assign_pointer(nf_ct_destroy, destroy_conntrack);
 
 	/* Set up fake conntrack:
 	    - to never be deleted, not in any hashes */
diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c
index 1a223e0..6bd421d 100644
--- a/net/netfilter/nf_conntrack_ecache.c
+++ b/net/netfilter/nf_conntrack_ecache.c
@@ -91,3 +91,26 @@
 	}
 }
 
+int nf_conntrack_register_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_register(&nf_conntrack_chain, nb);
+}
+EXPORT_SYMBOL_GPL(nf_conntrack_register_notifier);
+
+int nf_conntrack_unregister_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_unregister(&nf_conntrack_chain, nb);
+}
+EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier);
+
+int nf_conntrack_expect_register_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_register(&nf_conntrack_expect_chain, nb);
+}
+EXPORT_SYMBOL_GPL(nf_conntrack_expect_register_notifier);
+
+int nf_conntrack_expect_unregister_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_unregister(&nf_conntrack_expect_chain, nb);
+}
+EXPORT_SYMBOL_GPL(nf_conntrack_expect_unregister_notifier);
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index ce70a6f..c31af29 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -290,9 +290,7 @@
 	master_help->expecting++;
 	list_add(&exp->list, &nf_conntrack_expect_list);
 
-	init_timer(&exp->timeout);
-	exp->timeout.data = (unsigned long)exp;
-	exp->timeout.function = expectation_timed_out;
+	setup_timer(&exp->timeout, expectation_timed_out, (unsigned long)exp);
 	exp->timeout.expires = jiffies + master_help->helper->timeout * HZ;
 	add_timer(&exp->timeout);
 
diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c
index 3089dfc..a186799 100644
--- a/net/netfilter/nf_conntrack_ftp.c
+++ b/net/netfilter/nf_conntrack_ftp.c
@@ -7,12 +7,6 @@
  * 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.
- *
- * 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
- *	- enable working with Layer 3 protocol independent connection tracking.
- *	- track EPRT and EPSV commands with IPv6 address.
- *
- * Derived from net/ipv4/netfilter/ip_conntrack_ftp.c
  */
 
 #include <linux/module.h>
diff --git a/net/netfilter/nf_conntrack_netbios_ns.c b/net/netfilter/nf_conntrack_netbios_ns.c
index bb26a65..1093478 100644
--- a/net/netfilter/nf_conntrack_netbios_ns.c
+++ b/net/netfilter/nf_conntrack_netbios_ns.c
@@ -46,7 +46,7 @@
 		struct nf_conn *ct, enum ip_conntrack_info ctinfo)
 {
 	struct nf_conntrack_expect *exp;
-	struct iphdr *iph = (*pskb)->nh.iph;
+	struct iphdr *iph = ip_hdr(*pskb);
 	struct rtable *rt = (struct rtable *)(*pskb)->dst;
 	struct in_device *in_dev;
 	__be32 mask = 0;
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 48f0531..aa1a97ee 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -6,9 +6,6 @@
  * (C) 2003 by Patrick Mchardy <kaber@trash.net>
  * (C) 2005-2006 by Pablo Neira Ayuso <pablo@eurodev.net>
  *
- * I've reworked this stuff to use attributes instead of conntrack
- * structures. 5.44 am. I need more tea. --pablo 05/07/11.
- *
  * Initial connection tracking via netlink development funded and
  * generally made possible by Network Robots, Inc. (www.networkrobots.com)
  *
@@ -16,8 +13,6 @@
  *
  * This software may be used and distributed according to the terms
  * of the GNU General Public License, incorporated herein by reference.
- *
- * Derived from ip_conntrack_netlink.c: Port by Pablo Neira Ayuso (05/11/14)
  */
 
 #include <linux/init.h>
@@ -33,6 +28,7 @@
 #include <linux/notifier.h>
 
 #include <linux/netfilter.h>
+#include <net/netlink.h>
 #include <net/netfilter/nf_conntrack.h>
 #include <net/netfilter/nf_conntrack_core.h>
 #include <net/netfilter/nf_conntrack_expect.h>
@@ -268,9 +264,7 @@
 	struct nlmsghdr *nlh;
 	struct nfgenmsg *nfmsg;
 	struct nfattr *nest_parms;
-	unsigned char *b;
-
-	b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 
 	event |= NFNL_SUBSYS_CTNETLINK << 8;
 	nlh    = NLMSG_PUT(skb, pid, seq, event, sizeof(struct nfgenmsg));
@@ -303,12 +297,12 @@
 	    ctnetlink_dump_use(skb, ct) < 0)
 		goto nfattr_failure;
 
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
 nlmsg_failure:
 nfattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -322,7 +316,7 @@
 	struct nf_conn *ct = (struct nf_conn *)ptr;
 	struct sk_buff *skb;
 	unsigned int type;
-	unsigned char *b;
+	sk_buff_data_t b;
 	unsigned int flags = 0, group;
 
 	/* ignore our fake conntrack entry */
@@ -662,7 +656,7 @@
 
 static int
 ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
-			struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
+			struct nlmsghdr *nlh, struct nfattr *cda[])
 {
 	struct nf_conntrack_tuple_hash *h;
 	struct nf_conntrack_tuple tuple;
@@ -710,7 +704,7 @@
 
 static int
 ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
-			struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
+			struct nlmsghdr *nlh, struct nfattr *cda[])
 {
 	struct nf_conntrack_tuple_hash *h;
 	struct nf_conntrack_tuple tuple;
@@ -721,22 +715,12 @@
 	int err = 0;
 
 	if (nlh->nlmsg_flags & NLM_F_DUMP) {
-		u32 rlen;
-
 #ifndef CONFIG_NF_CT_ACCT
 		if (NFNL_MSG_TYPE(nlh->nlmsg_type) == IPCTNL_MSG_CT_GET_CTRZERO)
 			return -ENOTSUPP;
 #endif
-		if ((*errp = netlink_dump_start(ctnl, skb, nlh,
-						ctnetlink_dump_table,
-						ctnetlink_done)) != 0)
-			return -EINVAL;
-
-		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
-		if (rlen > skb->len)
-			rlen = skb->len;
-		skb_pull(skb, rlen);
-		return 0;
+		return netlink_dump_start(ctnl, skb, nlh, ctnetlink_dump_table,
+					  ctnetlink_done);
 	}
 
 	if (nfattr_bad_size(cda, CTA_MAX, cta_min))
@@ -1010,7 +994,7 @@
 
 static int
 ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
-			struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
+			struct nlmsghdr *nlh, struct nfattr *cda[])
 {
 	struct nf_conntrack_tuple otuple, rtuple;
 	struct nf_conntrack_tuple_hash *h = NULL;
@@ -1152,9 +1136,7 @@
 {
 	struct nlmsghdr *nlh;
 	struct nfgenmsg *nfmsg;
-	unsigned char *b;
-
-	b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 
 	event |= NFNL_SUBSYS_CTNETLINK_EXP << 8;
 	nlh    = NLMSG_PUT(skb, pid, seq, event, sizeof(struct nfgenmsg));
@@ -1168,12 +1150,12 @@
 	if (ctnetlink_exp_dump_expect(skb, exp) < 0)
 		goto nfattr_failure;
 
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
 nlmsg_failure:
 nfattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -1186,7 +1168,7 @@
 	struct nf_conntrack_expect *exp = (struct nf_conntrack_expect *)ptr;
 	struct sk_buff *skb;
 	unsigned int type;
-	unsigned char *b;
+	sk_buff_data_t b;
 	int flags = 0;
 
 	if (events & IPEXP_NEW) {
@@ -1263,7 +1245,7 @@
 
 static int
 ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
-		     struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
+		     struct nlmsghdr *nlh, struct nfattr *cda[])
 {
 	struct nf_conntrack_tuple tuple;
 	struct nf_conntrack_expect *exp;
@@ -1276,17 +1258,9 @@
 		return -EINVAL;
 
 	if (nlh->nlmsg_flags & NLM_F_DUMP) {
-		u32 rlen;
-
-		if ((*errp = netlink_dump_start(ctnl, skb, nlh,
-						ctnetlink_exp_dump_table,
-						ctnetlink_done)) != 0)
-			return -EINVAL;
-		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
-		if (rlen > skb->len)
-			rlen = skb->len;
-		skb_pull(skb, rlen);
-		return 0;
+		return netlink_dump_start(ctnl, skb, nlh,
+					  ctnetlink_exp_dump_table,
+					  ctnetlink_done);
 	}
 
 	if (cda[CTA_EXPECT_MASTER-1])
@@ -1333,7 +1307,7 @@
 
 static int
 ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
-		     struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
+		     struct nlmsghdr *nlh, struct nfattr *cda[])
 {
 	struct nf_conntrack_expect *exp, *tmp;
 	struct nf_conntrack_tuple tuple;
@@ -1467,7 +1441,7 @@
 
 static int
 ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb,
-		     struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
+		     struct nlmsghdr *nlh, struct nfattr *cda[])
 {
 	struct nf_conntrack_tuple tuple;
 	struct nf_conntrack_expect *exp;
diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c
index 456155f..6d94706 100644
--- a/net/netfilter/nf_conntrack_proto.c
+++ b/net/netfilter/nf_conntrack_proto.c
@@ -28,13 +28,13 @@
 #include <net/netfilter/nf_conntrack_l4proto.h>
 #include <net/netfilter/nf_conntrack_core.h>
 
-struct nf_conntrack_l4proto **nf_ct_protos[PF_MAX] __read_mostly;
+static struct nf_conntrack_l4proto **nf_ct_protos[PF_MAX] __read_mostly;
 struct nf_conntrack_l3proto *nf_ct_l3protos[AF_MAX] __read_mostly;
 EXPORT_SYMBOL_GPL(nf_ct_l3protos);
 
-#ifdef CONFIG_SYSCTL
-static DEFINE_MUTEX(nf_ct_proto_sysctl_mutex);
+static DEFINE_MUTEX(nf_ct_proto_mutex);
 
+#ifdef CONFIG_SYSCTL
 static int
 nf_ct_register_sysctl(struct ctl_table_header **header, struct ctl_table *path,
 		      struct ctl_table *table, unsigned int *users)
@@ -164,13 +164,11 @@
 	int err = 0;
 
 #ifdef CONFIG_SYSCTL
-	mutex_lock(&nf_ct_proto_sysctl_mutex);
 	if (l3proto->ctl_table != NULL) {
 		err = nf_ct_register_sysctl(&l3proto->ctl_table_header,
 					    l3proto->ctl_table_path,
 					    l3proto->ctl_table, NULL);
 	}
-	mutex_unlock(&nf_ct_proto_sysctl_mutex);
 #endif
 	return err;
 }
@@ -178,11 +176,9 @@
 static void nf_ct_l3proto_unregister_sysctl(struct nf_conntrack_l3proto *l3proto)
 {
 #ifdef CONFIG_SYSCTL
-	mutex_lock(&nf_ct_proto_sysctl_mutex);
 	if (l3proto->ctl_table_header != NULL)
 		nf_ct_unregister_sysctl(&l3proto->ctl_table_header,
 					l3proto->ctl_table, NULL);
-	mutex_unlock(&nf_ct_proto_sysctl_mutex);
 #endif
 }
 
@@ -190,27 +186,23 @@
 {
 	int ret = 0;
 
-	if (proto->l3proto >= AF_MAX) {
-		ret = -EBUSY;
-		goto out;
-	}
+	if (proto->l3proto >= AF_MAX)
+		return -EBUSY;
 
-	write_lock_bh(&nf_conntrack_lock);
+	mutex_lock(&nf_ct_proto_mutex);
 	if (nf_ct_l3protos[proto->l3proto] != &nf_conntrack_l3proto_generic) {
 		ret = -EBUSY;
 		goto out_unlock;
 	}
-	rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto);
-	write_unlock_bh(&nf_conntrack_lock);
 
 	ret = nf_ct_l3proto_register_sysctl(proto);
 	if (ret < 0)
-		nf_conntrack_l3proto_unregister(proto);
-	return ret;
+		goto out_unlock;
+
+	rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto);
 
 out_unlock:
-	write_unlock_bh(&nf_conntrack_lock);
-out:
+	mutex_unlock(&nf_ct_proto_mutex);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_register);
@@ -219,14 +211,14 @@
 {
 	BUG_ON(proto->l3proto >= AF_MAX);
 
-	write_lock_bh(&nf_conntrack_lock);
+	mutex_lock(&nf_ct_proto_mutex);
 	BUG_ON(nf_ct_l3protos[proto->l3proto] != proto);
 	rcu_assign_pointer(nf_ct_l3protos[proto->l3proto],
 			   &nf_conntrack_l3proto_generic);
-	write_unlock_bh(&nf_conntrack_lock);
-	synchronize_rcu();
-
 	nf_ct_l3proto_unregister_sysctl(proto);
+	mutex_unlock(&nf_ct_proto_mutex);
+
+	synchronize_rcu();
 
 	/* Remove all contrack entries for this protocol */
 	nf_ct_iterate_cleanup(kill_l3proto, proto);
@@ -238,7 +230,6 @@
 	int err = 0;
 
 #ifdef CONFIG_SYSCTL
-	mutex_lock(&nf_ct_proto_sysctl_mutex);
 	if (l4proto->ctl_table != NULL) {
 		err = nf_ct_register_sysctl(l4proto->ctl_table_header,
 					    nf_net_netfilter_sysctl_path,
@@ -260,7 +251,6 @@
 	}
 #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
 out:
-	mutex_unlock(&nf_ct_proto_sysctl_mutex);
 #endif /* CONFIG_SYSCTL */
 	return err;
 }
@@ -268,7 +258,6 @@
 static void nf_ct_l4proto_unregister_sysctl(struct nf_conntrack_l4proto *l4proto)
 {
 #ifdef CONFIG_SYSCTL
-	mutex_lock(&nf_ct_proto_sysctl_mutex);
 	if (l4proto->ctl_table_header != NULL &&
 	    *l4proto->ctl_table_header != NULL)
 		nf_ct_unregister_sysctl(l4proto->ctl_table_header,
@@ -279,7 +268,6 @@
 		nf_ct_unregister_sysctl(&l4proto->ctl_compat_table_header,
 					l4proto->ctl_compat_table, NULL);
 #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
-	mutex_unlock(&nf_ct_proto_sysctl_mutex);
 #endif /* CONFIG_SYSCTL */
 }
 
@@ -289,68 +277,41 @@
 {
 	int ret = 0;
 
-	if (l4proto->l3proto >= PF_MAX) {
-		ret = -EBUSY;
-		goto out;
-	}
+	if (l4proto->l3proto >= PF_MAX)
+		return -EBUSY;
 
-	if (l4proto == &nf_conntrack_l4proto_generic)
-		return nf_ct_l4proto_register_sysctl(l4proto);
-
-retry:
-	write_lock_bh(&nf_conntrack_lock);
-	if (nf_ct_protos[l4proto->l3proto]) {
-		if (nf_ct_protos[l4proto->l3proto][l4proto->l4proto]
-				!= &nf_conntrack_l4proto_generic) {
-			ret = -EBUSY;
-			goto out_unlock;
-		}
-	} else {
+	mutex_lock(&nf_ct_proto_mutex);
+	if (!nf_ct_protos[l4proto->l3proto]) {
 		/* l3proto may be loaded latter. */
 		struct nf_conntrack_l4proto **proto_array;
 		int i;
 
-		write_unlock_bh(&nf_conntrack_lock);
-
-		proto_array = (struct nf_conntrack_l4proto **)
-				kmalloc(MAX_NF_CT_PROTO *
-					 sizeof(struct nf_conntrack_l4proto *),
-					GFP_KERNEL);
+		proto_array = kmalloc(MAX_NF_CT_PROTO *
+				      sizeof(struct nf_conntrack_l4proto *),
+				      GFP_KERNEL);
 		if (proto_array == NULL) {
 			ret = -ENOMEM;
-			goto out;
+			goto out_unlock;
 		}
+
 		for (i = 0; i < MAX_NF_CT_PROTO; i++)
 			proto_array[i] = &nf_conntrack_l4proto_generic;
-
-		write_lock_bh(&nf_conntrack_lock);
-		if (nf_ct_protos[l4proto->l3proto]) {
-			/* bad timing, but no problem */
-			write_unlock_bh(&nf_conntrack_lock);
-			kfree(proto_array);
-		} else {
-			nf_ct_protos[l4proto->l3proto] = proto_array;
-			write_unlock_bh(&nf_conntrack_lock);
-		}
-
-		/*
-		 * Just once because array is never freed until unloading
-		 * nf_conntrack.ko
-		 */
-		goto retry;
+		nf_ct_protos[l4proto->l3proto] = proto_array;
+	} else if (nf_ct_protos[l4proto->l3proto][l4proto->l4proto] !=
+					&nf_conntrack_l4proto_generic) {
+		ret = -EBUSY;
+		goto out_unlock;
 	}
 
-	rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], l4proto);
-	write_unlock_bh(&nf_conntrack_lock);
-
 	ret = nf_ct_l4proto_register_sysctl(l4proto);
 	if (ret < 0)
-		nf_conntrack_l4proto_unregister(l4proto);
-	return ret;
+		goto out_unlock;
+
+	rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
+			   l4proto);
 
 out_unlock:
-	write_unlock_bh(&nf_conntrack_lock);
-out:
+	mutex_unlock(&nf_ct_proto_mutex);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_register);
@@ -359,21 +320,42 @@
 {
 	BUG_ON(l4proto->l3proto >= PF_MAX);
 
-	if (l4proto == &nf_conntrack_l4proto_generic) {
-		nf_ct_l4proto_unregister_sysctl(l4proto);
-		return;
-	}
-
-	write_lock_bh(&nf_conntrack_lock);
+	mutex_lock(&nf_ct_proto_mutex);
 	BUG_ON(nf_ct_protos[l4proto->l3proto][l4proto->l4proto] != l4proto);
 	rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
 			   &nf_conntrack_l4proto_generic);
-	write_unlock_bh(&nf_conntrack_lock);
-	synchronize_rcu();
-
 	nf_ct_l4proto_unregister_sysctl(l4proto);
+	mutex_unlock(&nf_ct_proto_mutex);
+
+	synchronize_rcu();
 
 	/* Remove all contrack entries for this protocol */
 	nf_ct_iterate_cleanup(kill_l4proto, l4proto);
 }
 EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister);
+
+int nf_conntrack_proto_init(void)
+{
+	unsigned int i;
+	int err;
+
+	err = nf_ct_l4proto_register_sysctl(&nf_conntrack_l4proto_generic);
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < AF_MAX; i++)
+		rcu_assign_pointer(nf_ct_l3protos[i],
+				   &nf_conntrack_l3proto_generic);
+	return 0;
+}
+
+void nf_conntrack_proto_fini(void)
+{
+	unsigned int i;
+
+	nf_ct_l4proto_unregister_sysctl(&nf_conntrack_l4proto_generic);
+
+	/* free l3proto protocol tables */
+	for (i = 0; i < PF_MAX; i++)
+		kfree(nf_ct_protos[i]);
+}
diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c
index 7c06993..6faf1be 100644
--- a/net/netfilter/nf_conntrack_proto_generic.c
+++ b/net/netfilter/nf_conntrack_proto_generic.c
@@ -4,11 +4,6 @@
  * 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.
- *
- * 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
- *	- enable working with L3 protocol independent connection tracking.
- *
- * Derived from net/ipv4/netfilter/ip_conntrack_proto_generic.c
  */
 
 #include <linux/types.h>
diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c
index 3c80558..0d3254b 100644
--- a/net/netfilter/nf_conntrack_proto_sctp.c
+++ b/net/netfilter/nf_conntrack_proto_sctp.c
@@ -7,15 +7,6 @@
  * 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.
- *
- * 17 Oct 2004: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
- *	- enable working with L3 protocol independent connection tracking.
- *
- * Derived from net/ipv4/ip_conntrack_sctp.c
- */
-
-/*
- * Added support for proc manipulation of timeouts.
  */
 
 #include <linux/types.h>
diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c
index 153d661..ccdd5d2 100644
--- a/net/netfilter/nf_conntrack_proto_tcp.c
+++ b/net/netfilter/nf_conntrack_proto_tcp.c
@@ -4,24 +4,6 @@
  * 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.
- *
- * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>:
- *	- Real stateful connection tracking
- *	- Modified state transitions table
- *	- Window scaling support added
- *	- SACK support added
- *
- * Willy Tarreau:
- *	- State table bugfixes
- *	- More robust state changes
- *	- Tuning timer parameters
- *
- * 27 Oct 2004: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
- *	- genelized Layer 3 protocol part.
- *
- * Derived from net/ipv4/netfilter/ip_conntrack_proto_tcp.c
- *
- * version 2.2
  */
 
 #include <linux/types.h>
@@ -470,11 +452,10 @@
 
 	/* Fast path for timestamp-only option */
 	if (length == TCPOLEN_TSTAMP_ALIGNED*4
-	    && *(__be32 *)ptr ==
-		__constant_htonl((TCPOPT_NOP << 24)
-				 | (TCPOPT_NOP << 16)
-				 | (TCPOPT_TIMESTAMP << 8)
-				 | TCPOLEN_TIMESTAMP))
+	    && *(__be32 *)ptr == htonl((TCPOPT_NOP << 24)
+				       | (TCPOPT_NOP << 16)
+				       | (TCPOPT_TIMESTAMP << 8)
+				       | TCPOLEN_TIMESTAMP))
 		return;
 
 	while (length > 0) {
@@ -765,26 +746,18 @@
 #define	TH_ECE	0x40
 #define	TH_CWR	0x80
 
-/* table of valid flag combinations - ECE and CWR are always valid */
-static u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG) + 1] =
+/* table of valid flag combinations - PUSH, ECE and CWR are always valid */
+static u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG) + 1] =
 {
 	[TH_SYN]			= 1,
-	[TH_SYN|TH_PUSH]		= 1,
 	[TH_SYN|TH_URG]			= 1,
-	[TH_SYN|TH_PUSH|TH_URG]		= 1,
 	[TH_SYN|TH_ACK]			= 1,
-	[TH_SYN|TH_ACK|TH_PUSH]		= 1,
 	[TH_RST]			= 1,
 	[TH_RST|TH_ACK]			= 1,
-	[TH_RST|TH_ACK|TH_PUSH]		= 1,
 	[TH_FIN|TH_ACK]			= 1,
-	[TH_ACK]			= 1,
-	[TH_ACK|TH_PUSH]		= 1,
-	[TH_ACK|TH_URG]			= 1,
-	[TH_ACK|TH_URG|TH_PUSH]		= 1,
-	[TH_FIN|TH_ACK|TH_PUSH]		= 1,
 	[TH_FIN|TH_ACK|TH_URG]		= 1,
-	[TH_FIN|TH_ACK|TH_URG|TH_PUSH]	= 1,
+	[TH_ACK]			= 1,
+	[TH_ACK|TH_URG]			= 1,
 };
 
 /* Protect conntrack agaist broken packets. Code taken from ipt_unclean.c.  */
@@ -831,7 +804,7 @@
 	}
 
 	/* Check TCP flags. */
-	tcpflags = (((u_int8_t *)th)[13] & ~(TH_ECE|TH_CWR));
+	tcpflags = (((u_int8_t *)th)[13] & ~(TH_ECE|TH_CWR|TH_PUSH));
 	if (!tcp_valid_flags[tcpflags]) {
 		if (LOG_INVALID(IPPROTO_TCP))
 			nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
@@ -1110,11 +1083,26 @@
 			 const struct nf_conn *ct)
 {
 	struct nfattr *nest_parms;
+	struct nf_ct_tcp_flags tmp = {};
 
 	read_lock_bh(&tcp_lock);
 	nest_parms = NFA_NEST(skb, CTA_PROTOINFO_TCP);
 	NFA_PUT(skb, CTA_PROTOINFO_TCP_STATE, sizeof(u_int8_t),
 		&ct->proto.tcp.state);
+
+	NFA_PUT(skb, CTA_PROTOINFO_TCP_WSCALE_ORIGINAL, sizeof(u_int8_t),
+		&ct->proto.tcp.seen[0].td_scale);
+
+	NFA_PUT(skb, CTA_PROTOINFO_TCP_WSCALE_REPLY, sizeof(u_int8_t),
+		&ct->proto.tcp.seen[1].td_scale);
+
+	tmp.flags = ct->proto.tcp.seen[0].flags;
+	NFA_PUT(skb, CTA_PROTOINFO_TCP_FLAGS_ORIGINAL,
+		sizeof(struct nf_ct_tcp_flags), &tmp);
+
+	tmp.flags = ct->proto.tcp.seen[1].flags;
+	NFA_PUT(skb, CTA_PROTOINFO_TCP_FLAGS_REPLY,
+		sizeof(struct nf_ct_tcp_flags), &tmp);
 	read_unlock_bh(&tcp_lock);
 
 	NFA_NEST_END(skb, nest_parms);
@@ -1127,7 +1115,11 @@
 }
 
 static const size_t cta_min_tcp[CTA_PROTOINFO_TCP_MAX] = {
-	[CTA_PROTOINFO_TCP_STATE-1]	= sizeof(u_int8_t),
+	[CTA_PROTOINFO_TCP_STATE-1]	      = sizeof(u_int8_t),
+	[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL-1] = sizeof(u_int8_t),
+	[CTA_PROTOINFO_TCP_WSCALE_REPLY-1]    = sizeof(u_int8_t),
+	[CTA_PROTOINFO_TCP_FLAGS_ORIGINAL-1]  = sizeof(struct nf_ct_tcp_flags),
+	[CTA_PROTOINFO_TCP_FLAGS_REPLY-1]     = sizeof(struct nf_ct_tcp_flags)
 };
 
 static int nfattr_to_tcp(struct nfattr *cda[], struct nf_conn *ct)
@@ -1151,6 +1143,30 @@
 	write_lock_bh(&tcp_lock);
 	ct->proto.tcp.state =
 		*(u_int8_t *)NFA_DATA(tb[CTA_PROTOINFO_TCP_STATE-1]);
+
+	if (tb[CTA_PROTOINFO_TCP_FLAGS_ORIGINAL-1]) {
+		struct nf_ct_tcp_flags *attr =
+			NFA_DATA(tb[CTA_PROTOINFO_TCP_FLAGS_ORIGINAL-1]);
+		ct->proto.tcp.seen[0].flags &= ~attr->mask;
+		ct->proto.tcp.seen[0].flags |= attr->flags & attr->mask;
+	}
+
+	if (tb[CTA_PROTOINFO_TCP_FLAGS_REPLY-1]) {
+		struct nf_ct_tcp_flags *attr =
+			NFA_DATA(tb[CTA_PROTOINFO_TCP_FLAGS_REPLY-1]);
+		ct->proto.tcp.seen[1].flags &= ~attr->mask;
+		ct->proto.tcp.seen[1].flags |= attr->flags & attr->mask;
+	}
+
+	if (tb[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL-1] &&
+	    tb[CTA_PROTOINFO_TCP_WSCALE_REPLY-1] &&
+	    ct->proto.tcp.seen[0].flags & IP_CT_TCP_FLAG_WINDOW_SCALE &&
+	    ct->proto.tcp.seen[1].flags & IP_CT_TCP_FLAG_WINDOW_SCALE) {
+		ct->proto.tcp.seen[0].td_scale = *(u_int8_t *)
+			NFA_DATA(tb[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL-1]);
+		ct->proto.tcp.seen[1].td_scale = *(u_int8_t *)
+			NFA_DATA(tb[CTA_PROTOINFO_TCP_WSCALE_REPLY-1]);
+	}
 	write_unlock_bh(&tcp_lock);
 
 	return 0;
diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c
index a5e5726..3620ecc 100644
--- a/net/netfilter/nf_conntrack_proto_udp.c
+++ b/net/netfilter/nf_conntrack_proto_udp.c
@@ -4,11 +4,6 @@
  * 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.
- *
- * 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
- *	- enable working with Layer 3 protocol independent connection tracking.
- *
- * Derived from net/ipv4/netfilter/ip_conntrack_proto_udp.c
  */
 
 #include <linux/types.h>
diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c
index b858636..45baeb0 100644
--- a/net/netfilter/nf_conntrack_standalone.c
+++ b/net/netfilter/nf_conntrack_standalone.c
@@ -1,20 +1,9 @@
-/* This file contains all the functions required for the standalone
-   nf_conntrack module.
-
-   These are not required by the compatibility layer.
-*/
-
 /* (C) 1999-2001 Paul `Rusty' Russell
  * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.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.
- *
- * 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
- *	- generalize L3 protocol dependent part.
- *
- * Derived from net/ipv4/netfilter/ip_conntrack_standalone.c
  */
 
 #include <linux/types.h>
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c
index bf23e48..8797e69 100644
--- a/net/netfilter/nfnetlink.c
+++ b/net/netfilter/nfnetlink.c
@@ -3,7 +3,7 @@
  *
  * (C) 2001 by Jay Schulist <jschlst@samba.org>,
  * (C) 2002-2005 by Harald Welte <laforge@gnumonks.org>
- * (C) 2005 by Pablo Neira Ayuso <pablo@eurodev.net>
+ * (C) 2005,2007 by Pablo Neira Ayuso <pablo@netfilter.org>
  *
  * Initial netfilter messages via netlink development funded and
  * generally made possible by Network Robots, Inc. (www.networkrobots.com)
@@ -28,10 +28,9 @@
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <net/sock.h>
+#include <net/netlink.h>
 #include <linux/init.h>
-#include <linux/spinlock.h>
 
-#include <linux/netfilter.h>
 #include <linux/netlink.h>
 #include <linux/netfilter/nfnetlink.h>
 
@@ -41,32 +40,34 @@
 
 static char __initdata nfversion[] = "0.30";
 
-#if 0
-#define DEBUGP(format, args...)	\
-		printk(KERN_DEBUG "%s(%d):%s(): " format, __FILE__, \
-			__LINE__, __FUNCTION__, ## args)
-#else
-#define DEBUGP(format, args...)
-#endif
-
 static struct sock *nfnl = NULL;
 static struct nfnetlink_subsystem *subsys_table[NFNL_SUBSYS_COUNT];
-DECLARE_MUTEX(nfnl_sem);
+static DEFINE_MUTEX(nfnl_mutex);
 
-void nfnl_lock(void)
+static void nfnl_lock(void)
 {
-	nfnl_shlock();
+	mutex_lock(&nfnl_mutex);
 }
 
-void nfnl_unlock(void)
+static int nfnl_trylock(void)
 {
-	nfnl_shunlock();
+	return !mutex_trylock(&nfnl_mutex);
+}
+
+static void __nfnl_unlock(void)
+{
+	mutex_unlock(&nfnl_mutex);
+}
+
+static void nfnl_unlock(void)
+{
+	mutex_unlock(&nfnl_mutex);
+	if (nfnl->sk_receive_queue.qlen)
+		nfnl->sk_data_ready(nfnl, 0);
 }
 
 int nfnetlink_subsys_register(struct nfnetlink_subsystem *n)
 {
-	DEBUGP("registering subsystem ID %u\n", n->subsys_id);
-
 	nfnl_lock();
 	if (subsys_table[n->subsys_id]) {
 		nfnl_unlock();
@@ -77,24 +78,23 @@
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(nfnetlink_subsys_register);
 
 int nfnetlink_subsys_unregister(struct nfnetlink_subsystem *n)
 {
-	DEBUGP("unregistering subsystem ID %u\n", n->subsys_id);
-
 	nfnl_lock();
 	subsys_table[n->subsys_id] = NULL;
 	nfnl_unlock();
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister);
 
 static inline struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t type)
 {
 	u_int8_t subsys_id = NFNL_SUBSYS_ID(type);
 
-	if (subsys_id >= NFNL_SUBSYS_COUNT
-	    || subsys_table[subsys_id] == NULL)
+	if (subsys_id >= NFNL_SUBSYS_COUNT)
 		return NULL;
 
 	return subsys_table[subsys_id];
@@ -105,10 +105,8 @@
 {
 	u_int8_t cb_id = NFNL_MSG_TYPE(type);
 
-	if (cb_id >= ss->cb_count) {
-		DEBUGP("msgtype %u >= %u, returning\n", type, ss->cb_count);
+	if (cb_id >= ss->cb_count)
 		return NULL;
-	}
 
 	return &ss->cb[cb_id];
 }
@@ -125,6 +123,7 @@
 	memcpy(NFA_DATA(nfa), data, attrlen);
 	memset(NFA_DATA(nfa) + attrlen, 0, NFA_ALIGN(size) - size);
 }
+EXPORT_SYMBOL_GPL(__nfa_fill);
 
 void nfattr_parse(struct nfattr *tb[], int maxattr, struct nfattr *nfa, int len)
 {
@@ -137,6 +136,7 @@
 		nfa = NFA_NEXT(nfa, len);
 	}
 }
+EXPORT_SYMBOL_GPL(nfattr_parse);
 
 /**
  * nfnetlink_check_attributes - check and parse nfnetlink attributes
@@ -150,37 +150,15 @@
 nfnetlink_check_attributes(struct nfnetlink_subsystem *subsys,
 			   struct nlmsghdr *nlh, struct nfattr *cda[])
 {
-	int min_len;
-	u_int16_t attr_count;
+	int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
 	u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
-
-	if (unlikely(cb_id >= subsys->cb_count)) {
-		DEBUGP("msgtype %u >= %u, returning\n",
-			cb_id, subsys->cb_count);
-		return -EINVAL;
-	}
-
-	min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
-	if (unlikely(nlh->nlmsg_len < min_len))
-		return -EINVAL;
-
-	attr_count = subsys->cb[cb_id].attr_count;
-	memset(cda, 0, sizeof(struct nfattr *) * attr_count);
+	u_int16_t attr_count = subsys->cb[cb_id].attr_count;
 
 	/* check attribute lengths. */
 	if (likely(nlh->nlmsg_len > min_len)) {
 		struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh));
 		int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
-
-		while (NFA_OK(attr, attrlen)) {
-			unsigned flavor = NFA_TYPE(attr);
-			if (flavor) {
-				if (flavor > attr_count)
-					return -EINVAL;
-				cda[flavor - 1] = attr;
-			}
-			attr = NFA_NEXT(attr, attrlen);
-		}
+		nfattr_parse(cda, attr_count, attr, attrlen);
 	}
 
 	/* implicit: if nlmsg_len == min_len, we return 0, and an empty
@@ -208,62 +186,46 @@
 
 	return err;
 }
+EXPORT_SYMBOL_GPL(nfnetlink_send);
 
 int nfnetlink_unicast(struct sk_buff *skb, u_int32_t pid, int flags)
 {
 	return netlink_unicast(nfnl, skb, pid, flags);
 }
+EXPORT_SYMBOL_GPL(nfnetlink_unicast);
 
 /* Process one complete nfnetlink message. */
-static int nfnetlink_rcv_msg(struct sk_buff *skb,
-				    struct nlmsghdr *nlh, int *errp)
+static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
 	struct nfnl_callback *nc;
 	struct nfnetlink_subsystem *ss;
-	int type, err = 0;
+	int type, err;
 
-	DEBUGP("entered; subsys=%u, msgtype=%u\n",
-		 NFNL_SUBSYS_ID(nlh->nlmsg_type),
-		 NFNL_MSG_TYPE(nlh->nlmsg_type));
-
-	if (security_netlink_recv(skb, CAP_NET_ADMIN)) {
-		DEBUGP("missing CAP_NET_ADMIN\n");
-		*errp = -EPERM;
-		return -1;
-	}
-
-	/* Only requests are handled by kernel now. */
-	if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) {
-		DEBUGP("received non-request message\n");
-		return 0;
-	}
+	if (security_netlink_recv(skb, CAP_NET_ADMIN))
+		return -EPERM;
 
 	/* All the messages must at least contain nfgenmsg */
-	if (nlh->nlmsg_len < NLMSG_SPACE(sizeof(struct nfgenmsg))) {
-		DEBUGP("received message was too short\n");
+	if (nlh->nlmsg_len < NLMSG_SPACE(sizeof(struct nfgenmsg)))
 		return 0;
-	}
 
 	type = nlh->nlmsg_type;
 	ss = nfnetlink_get_subsys(type);
 	if (!ss) {
 #ifdef CONFIG_KMOD
-		/* don't call nfnl_shunlock, since it would reenter
+		/* don't call nfnl_unlock, since it would reenter
 		 * with further packet processing */
-		up(&nfnl_sem);
+		__nfnl_unlock();
 		request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type));
-		nfnl_shlock();
+		nfnl_lock();
 		ss = nfnetlink_get_subsys(type);
 		if (!ss)
 #endif
-			goto err_inval;
+			return -EINVAL;
 	}
 
 	nc = nfnetlink_find_client(type, ss);
-	if (!nc) {
-		DEBUGP("unable to find client for type %d\n", type);
-		goto err_inval;
-	}
+	if (!nc)
+		return -EINVAL;
 
 	{
 		u_int16_t attr_count =
@@ -274,73 +236,21 @@
 
 		err = nfnetlink_check_attributes(ss, nlh, cda);
 		if (err < 0)
-			goto err_inval;
-
-		DEBUGP("calling handler\n");
-		err = nc->call(nfnl, skb, nlh, cda, errp);
-		*errp = err;
-		return err;
+			return err;
+		return nc->call(nfnl, skb, nlh, cda);
 	}
-
-err_inval:
-	DEBUGP("returning -EINVAL\n");
-	*errp = -EINVAL;
-	return -1;
-}
-
-/* Process one packet of messages. */
-static inline int nfnetlink_rcv_skb(struct sk_buff *skb)
-{
-	int err;
-	struct nlmsghdr *nlh;
-
-	while (skb->len >= NLMSG_SPACE(0)) {
-		u32 rlen;
-
-		nlh = (struct nlmsghdr *)skb->data;
-		if (nlh->nlmsg_len < sizeof(struct nlmsghdr)
-		    || skb->len < nlh->nlmsg_len)
-			return 0;
-		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
-		if (rlen > skb->len)
-			rlen = skb->len;
-		if (nfnetlink_rcv_msg(skb, nlh, &err)) {
-			if (!err)
-				return -1;
-			netlink_ack(skb, nlh, err);
-		} else
-			if (nlh->nlmsg_flags & NLM_F_ACK)
-				netlink_ack(skb, nlh, 0);
-		skb_pull(skb, rlen);
-	}
-
-	return 0;
 }
 
 static void nfnetlink_rcv(struct sock *sk, int len)
 {
+	unsigned int qlen = 0;
+
 	do {
-		struct sk_buff *skb;
-
-		if (nfnl_shlock_nowait())
+		if (nfnl_trylock())
 			return;
-
-		while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
-			if (nfnetlink_rcv_skb(skb)) {
-				if (skb->len)
-					skb_queue_head(&sk->sk_receive_queue,
-						       skb);
-				else
-					kfree_skb(skb);
-				break;
-			}
-			kfree_skb(skb);
-		}
-
-		/* don't call nfnl_shunlock, since it would reenter
-		 * with further packet processing */
-		up(&nfnl_sem);
-	} while(nfnl && nfnl->sk_receive_queue.qlen);
+		netlink_run_queue(sk, &qlen, nfnetlink_rcv_msg);
+		__nfnl_unlock();
+	} while (qlen);
 }
 
 static void __exit nfnetlink_exit(void)
@@ -355,7 +265,7 @@
 	printk("Netfilter messages via NETLINK v%s.\n", nfversion);
 
 	nfnl = netlink_kernel_create(NETLINK_NETFILTER, NFNLGRP_MAX,
-				     nfnetlink_rcv, THIS_MODULE);
+				     nfnetlink_rcv, NULL, THIS_MODULE);
 	if (!nfnl) {
 		printk(KERN_ERR "cannot initialize nfnetlink!\n");
 		return -1;
@@ -366,10 +276,3 @@
 
 module_init(nfnetlink_init);
 module_exit(nfnetlink_exit);
-
-EXPORT_SYMBOL_GPL(nfnetlink_subsys_register);
-EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister);
-EXPORT_SYMBOL_GPL(nfnetlink_send);
-EXPORT_SYMBOL_GPL(nfnetlink_unicast);
-EXPORT_SYMBOL_GPL(nfattr_parse);
-EXPORT_SYMBOL_GPL(__nfa_fill);
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
index 5cb30eb..e32e30e 100644
--- a/net/netfilter/nfnetlink_log.c
+++ b/net/netfilter/nfnetlink_log.c
@@ -10,11 +10,6 @@
  * 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.
- *
- * 2006-01-26 Harald Welte <laforge@netfilter.org>
- * 	- Add optional local and global sequence number to detect lost
- * 	  events from userspace
- *
  */
 #include <linux/module.h>
 #include <linux/skbuff.h>
@@ -163,10 +158,7 @@
 	/* needs to be two, since we _put() after creation */
 	atomic_set(&inst->use, 2);
 
-	init_timer(&inst->timer);
-	inst->timer.function = nfulnl_timer;
-	inst->timer.data = (unsigned long)inst;
-	/* don't start timer yet. (re)start it  with every packet */
+	setup_timer(&inst->timer, nfulnl_timer, (unsigned long)inst);
 
 	inst->peer_pid = pid;
 	inst->group_num = group_num;
@@ -200,20 +192,14 @@
 static int __nfulnl_send(struct nfulnl_instance *inst);
 
 static void
-_instance_destroy2(struct nfulnl_instance *inst, int lock)
+__instance_destroy(struct nfulnl_instance *inst)
 {
 	/* first pull it out of the global list */
-	if (lock)
-		write_lock_bh(&instances_lock);
-
 	UDEBUG("removing instance %p (queuenum=%u) from hash\n",
 		inst, inst->group_num);
 
 	hlist_del(&inst->hlist);
 
-	if (lock)
-		write_unlock_bh(&instances_lock);
-
 	/* then flush all pending packets from skb */
 
 	spin_lock_bh(&inst->lock);
@@ -235,15 +221,11 @@
 }
 
 static inline void
-__instance_destroy(struct nfulnl_instance *inst)
-{
-	_instance_destroy2(inst, 0);
-}
-
-static inline void
 instance_destroy(struct nfulnl_instance *inst)
 {
-	_instance_destroy2(inst, 1);
+	write_lock_bh(&instances_lock);
+	__instance_destroy(inst);
+	write_unlock_bh(&instances_lock);
 }
 
 static int
@@ -365,9 +347,6 @@
 {
 	int status;
 
-	if (!inst->skb)
-		return 0;
-
 	if (inst->qlen > 1)
 		inst->lastnlh->nlmsg_type = NLMSG_DONE;
 
@@ -391,7 +370,8 @@
 	UDEBUG("timer function called, flushing buffer\n");
 
 	spin_lock_bh(&inst->lock);
-	__nfulnl_send(inst);
+	if (inst->skb)
+		__nfulnl_send(inst);
 	spin_unlock_bh(&inst->lock);
 	instance_put(inst);
 }
@@ -409,15 +389,14 @@
 			const struct nf_loginfo *li,
 			const char *prefix, unsigned int plen)
 {
-	unsigned char *old_tail;
 	struct nfulnl_msg_packet_hdr pmsg;
 	struct nlmsghdr *nlh;
 	struct nfgenmsg *nfmsg;
 	__be32 tmp_uint;
+	sk_buff_data_t old_tail = inst->skb->tail;
 
 	UDEBUG("entered\n");
 
-	old_tail = inst->skb->tail;
 	nlh = NLMSG_PUT(inst->skb, 0, 0,
 			NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET,
 			sizeof(struct nfgenmsg));
@@ -509,11 +488,11 @@
 		NFA_PUT(inst->skb, NFULA_HWADDR, sizeof(phw), &phw);
 	}
 
-	if (skb->tstamp.off_sec) {
+	if (skb->tstamp.tv64) {
 		struct nfulnl_msg_packet_timestamp ts;
-
-		ts.sec = cpu_to_be64(skb->tstamp.off_sec);
-		ts.usec = cpu_to_be64(skb->tstamp.off_usec);
+		struct timeval tv = ktime_to_timeval(skb->tstamp);
+		ts.sec = cpu_to_be64(tv.tv_sec);
+		ts.usec = cpu_to_be64(tv.tv_usec);
 
 		NFA_PUT(inst->skb, NFULA_TIMESTAMP, sizeof(ts), &ts);
 	}
@@ -596,7 +575,6 @@
 	struct nfulnl_instance *inst;
 	const struct nf_loginfo *li;
 	unsigned int qthreshold;
-	unsigned int nlbufsiz;
 	unsigned int plen;
 
 	if (li_user && li_user->type == NF_LOG_TYPE_ULOG)
@@ -606,12 +584,7 @@
 
 	inst = instance_lookup_get(li->u.ulog.group);
 	if (!inst)
-		inst = instance_lookup_get(0);
-	if (!inst) {
-		PRINTR("nfnetlink_log: trying to log packet, "
-			"but no instance for group %u\n", li->u.ulog.group);
 		return;
-	}
 
 	plen = 0;
 	if (prefix)
@@ -667,24 +640,11 @@
 		break;
 
 	default:
-		spin_unlock_bh(&inst->lock);
-		instance_put(inst);
-		return;
+		goto unlock_and_release;
 	}
 
-	if (size > inst->nlbufsiz)
-		nlbufsiz = size;
-	else
-		nlbufsiz = inst->nlbufsiz;
-
-	if (!inst->skb) {
-		if (!(inst->skb = nfulnl_alloc_skb(nlbufsiz, size))) {
-			UDEBUG("error in nfulnl_alloc_skb(%u, %u)\n",
-				inst->nlbufsiz, size);
-			goto alloc_failure;
-		}
-	} else if (inst->qlen >= qthreshold ||
-		   size > skb_tailroom(inst->skb)) {
+	if (inst->qlen >= qthreshold ||
+	    (inst->skb && size > skb_tailroom(inst->skb))) {
 		/* either the queue len is too high or we don't have
 		 * enough room in the skb left. flush to userspace. */
 		UDEBUG("flushing old skb\n");
@@ -693,12 +653,12 @@
 		if (del_timer(&inst->timer))
 			instance_put(inst);
 		__nfulnl_send(inst);
+	}
 
-		if (!(inst->skb = nfulnl_alloc_skb(nlbufsiz, size))) {
-			UDEBUG("error in nfulnl_alloc_skb(%u, %u)\n",
-				inst->nlbufsiz, size);
+	if (!inst->skb) {
+		inst->skb = nfulnl_alloc_skb(inst->nlbufsiz, size);
+		if (!inst->skb)
 			goto alloc_failure;
-		}
 	}
 
 	UDEBUG("qlen %d, qthreshold %d\n", inst->qlen, qthreshold);
@@ -760,7 +720,7 @@
 
 static int
 nfulnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb,
-		  struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp)
+		  struct nlmsghdr *nlh, struct nfattr *nfqa[])
 {
 	return -ENOTSUPP;
 }
@@ -798,7 +758,7 @@
 
 static int
 nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
-		   struct nlmsghdr *nlh, struct nfattr *nfula[], int *errp)
+		   struct nlmsghdr *nlh, struct nfattr *nfula[])
 {
 	struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
 	u_int16_t group_num = ntohs(nfmsg->res_id);
@@ -830,13 +790,13 @@
 					       NETLINK_CB(skb).pid);
 			if (!inst) {
 				ret = -EINVAL;
-				goto out_put;
+				goto out;
 			}
 			break;
 		case NFULNL_CFG_CMD_UNBIND:
 			if (!inst) {
 				ret = -ENODEV;
-				goto out_put;
+				goto out;
 			}
 
 			if (inst->peer_pid != NETLINK_CB(skb).pid) {
@@ -845,7 +805,7 @@
 			}
 
 			instance_destroy(inst);
-			break;
+			goto out;
 		case NFULNL_CFG_CMD_PF_BIND:
 			UDEBUG("registering log handler for pf=%u\n", pf);
 			ret = nf_log_register(pf, &nfulnl_logger);
@@ -869,7 +829,7 @@
 				"group=%u pid=%u =>ENOENT\n",
 				group_num, NETLINK_CB(skb).pid);
 			ret = -ENOENT;
-			goto out_put;
+			goto out;
 		}
 
 		if (inst->peer_pid != NETLINK_CB(skb).pid) {
@@ -939,10 +899,8 @@
 	unsigned int bucket;
 };
 
-static struct hlist_node *get_first(struct seq_file *seq)
+static struct hlist_node *get_first(struct iter_state *st)
 {
-	struct iter_state *st = seq->private;
-
 	if (!st)
 		return NULL;
 
@@ -953,10 +911,8 @@
 	return NULL;
 }
 
-static struct hlist_node *get_next(struct seq_file *seq, struct hlist_node *h)
+static struct hlist_node *get_next(struct iter_state *st, struct hlist_node *h)
 {
-	struct iter_state *st = seq->private;
-
 	h = h->next;
 	while (!h) {
 		if (++st->bucket >= INSTANCE_BUCKETS)
@@ -967,13 +923,13 @@
 	return h;
 }
 
-static struct hlist_node *get_idx(struct seq_file *seq, loff_t pos)
+static struct hlist_node *get_idx(struct iter_state *st, loff_t pos)
 {
 	struct hlist_node *head;
-	head = get_first(seq);
+	head = get_first(st);
 
 	if (head)
-		while (pos && (head = get_next(seq, head)))
+		while (pos && (head = get_next(st, head)))
 			pos--;
 	return pos ? NULL : head;
 }
@@ -981,13 +937,13 @@
 static void *seq_start(struct seq_file *seq, loff_t *pos)
 {
 	read_lock_bh(&instances_lock);
-	return get_idx(seq, *pos);
+	return get_idx(seq->private, *pos);
 }
 
 static void *seq_next(struct seq_file *s, void *v, loff_t *pos)
 {
 	(*pos)++;
-	return get_next(s, v);
+	return get_next(s->private, v);
 }
 
 static void seq_stop(struct seq_file *s, void *v)
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
index d9ce4a7..7a97bec 100644
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
@@ -338,7 +338,7 @@
 nfqnl_build_packet_message(struct nfqnl_instance *queue,
 			   struct nfqnl_queue_entry *entry, int *errp)
 {
-	unsigned char *old_tail;
+	sk_buff_data_t old_tail;
 	size_t size;
 	size_t data_len = 0;
 	struct sk_buff *skb;
@@ -404,7 +404,7 @@
 	if (!skb)
 		goto nlmsg_failure;
 
-	old_tail= skb->tail;
+	old_tail = skb->tail;
 	nlh = NLMSG_PUT(skb, 0, 0,
 			NFNL_SUBSYS_QUEUE << 8 | NFQNL_MSG_PACKET,
 			sizeof(struct nfgenmsg));
@@ -495,11 +495,11 @@
 		NFA_PUT(skb, NFQA_HWADDR, sizeof(phw), &phw);
 	}
 
-	if (entskb->tstamp.off_sec) {
+	if (entskb->tstamp.tv64) {
 		struct nfqnl_msg_packet_timestamp ts;
-
-		ts.sec = cpu_to_be64(entskb->tstamp.off_sec);
-		ts.usec = cpu_to_be64(entskb->tstamp.off_usec);
+		struct timeval tv = ktime_to_timeval(entskb->tstamp);
+		ts.sec = cpu_to_be64(tv.tv_sec);
+		ts.usec = cpu_to_be64(tv.tv_usec);
 
 		NFA_PUT(skb, NFQA_TIMESTAMP, sizeof(ts), &ts);
 	}
@@ -648,7 +648,7 @@
 	}
 	if (!skb_make_writable(&e->skb, data_len))
 		return -ENOMEM;
-	memcpy(e->skb->data, data, data_len);
+	skb_copy_to_linear_data(e->skb, data, data_len);
 	e->skb->ip_summed = CHECKSUM_NONE;
 	return 0;
 }
@@ -783,7 +783,7 @@
 
 static int
 nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
-		   struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp)
+		   struct nlmsghdr *nlh, struct nfattr *nfqa[])
 {
 	struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
 	u_int16_t queue_num = ntohs(nfmsg->res_id);
@@ -848,7 +848,7 @@
 
 static int
 nfqnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb,
-		  struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp)
+		  struct nlmsghdr *nlh, struct nfattr *nfqa[])
 {
 	return -ENOTSUPP;
 }
@@ -865,7 +865,7 @@
 
 static int
 nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
-		  struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp)
+		  struct nlmsghdr *nlh, struct nfattr *nfqa[])
 {
 	struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
 	u_int16_t queue_num = ntohs(nfmsg->res_id);
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c
index ec607a4..0eb2504 100644
--- a/net/netfilter/x_tables.c
+++ b/net/netfilter/x_tables.c
@@ -56,8 +56,8 @@
 };
 
 static const char *xt_prefix[NPROTO] = {
-	[AF_INET] 	= "ip",
-	[AF_INET6] 	= "ip6",
+	[AF_INET]	= "ip",
+	[AF_INET6]	= "ip6",
 	[NF_ARP]	= "arp",
 };
 
@@ -651,12 +651,6 @@
 EXPORT_SYMBOL_GPL(xt_unregister_table);
 
 #ifdef CONFIG_PROC_FS
-static char *xt_proto_prefix[NPROTO] = {
-	[AF_INET]	= "ip",
-	[AF_INET6]	= "ip6",
-	[NF_ARP]	= "arp",
-};
-
 static struct list_head *xt_get_idx(struct list_head *list, struct seq_file *seq, loff_t pos)
 {
 	struct list_head *head = list->next;
@@ -798,7 +792,7 @@
 
 
 #ifdef CONFIG_PROC_FS
-	strlcpy(buf, xt_proto_prefix[af], sizeof(buf));
+	strlcpy(buf, xt_prefix[af], sizeof(buf));
 	strlcat(buf, FORMAT_TABLES, sizeof(buf));
 	proc = proc_net_fops_create(buf, 0440, &xt_file_ops);
 	if (!proc)
@@ -806,14 +800,14 @@
 	proc->data = (void *) ((unsigned long) af | (TABLE << 16));
 
 
-	strlcpy(buf, xt_proto_prefix[af], sizeof(buf));
+	strlcpy(buf, xt_prefix[af], sizeof(buf));
 	strlcat(buf, FORMAT_MATCHES, sizeof(buf));
 	proc = proc_net_fops_create(buf, 0440, &xt_file_ops);
 	if (!proc)
 		goto out_remove_tables;
 	proc->data = (void *) ((unsigned long) af | (MATCH << 16));
 
-	strlcpy(buf, xt_proto_prefix[af], sizeof(buf));
+	strlcpy(buf, xt_prefix[af], sizeof(buf));
 	strlcat(buf, FORMAT_TARGETS, sizeof(buf));
 	proc = proc_net_fops_create(buf, 0440, &xt_file_ops);
 	if (!proc)
@@ -825,12 +819,12 @@
 
 #ifdef CONFIG_PROC_FS
 out_remove_matches:
-	strlcpy(buf, xt_proto_prefix[af], sizeof(buf));
+	strlcpy(buf, xt_prefix[af], sizeof(buf));
 	strlcat(buf, FORMAT_MATCHES, sizeof(buf));
 	proc_net_remove(buf);
 
 out_remove_tables:
-	strlcpy(buf, xt_proto_prefix[af], sizeof(buf));
+	strlcpy(buf, xt_prefix[af], sizeof(buf));
 	strlcat(buf, FORMAT_TABLES, sizeof(buf));
 	proc_net_remove(buf);
 out:
@@ -844,15 +838,15 @@
 #ifdef CONFIG_PROC_FS
 	char buf[XT_FUNCTION_MAXNAMELEN];
 
-	strlcpy(buf, xt_proto_prefix[af], sizeof(buf));
+	strlcpy(buf, xt_prefix[af], sizeof(buf));
 	strlcat(buf, FORMAT_TABLES, sizeof(buf));
 	proc_net_remove(buf);
 
-	strlcpy(buf, xt_proto_prefix[af], sizeof(buf));
+	strlcpy(buf, xt_prefix[af], sizeof(buf));
 	strlcat(buf, FORMAT_TARGETS, sizeof(buf));
 	proc_net_remove(buf);
 
-	strlcpy(buf, xt_proto_prefix[af], sizeof(buf));
+	strlcpy(buf, xt_prefix[af], sizeof(buf));
 	strlcat(buf, FORMAT_MATCHES, sizeof(buf));
 	proc_net_remove(buf);
 #endif /*CONFIG_PROC_FS*/
diff --git a/net/netfilter/xt_CONNMARK.c b/net/netfilter/xt_CONNMARK.c
index 795c058..b03ce00 100644
--- a/net/netfilter/xt_CONNMARK.c
+++ b/net/netfilter/xt_CONNMARK.c
@@ -30,10 +30,7 @@
 
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/xt_CONNMARK.h>
-#include <net/netfilter/nf_conntrack_compat.h>
-#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
 #include <net/netfilter/nf_conntrack_ecache.h>
-#endif
 
 static unsigned int
 target(struct sk_buff **pskb,
@@ -44,40 +41,33 @@
        const void *targinfo)
 {
 	const struct xt_connmark_target_info *markinfo = targinfo;
+	struct nf_conn *ct;
+	enum ip_conntrack_info ctinfo;
 	u_int32_t diff;
 	u_int32_t mark;
 	u_int32_t newmark;
-	u_int32_t ctinfo;
-	u_int32_t *ctmark = nf_ct_get_mark(*pskb, &ctinfo);
 
-	if (ctmark) {
+	ct = nf_ct_get(*pskb, &ctinfo);
+	if (ct) {
 		switch(markinfo->mode) {
 		case XT_CONNMARK_SET:
-			newmark = (*ctmark & ~markinfo->mask) | markinfo->mark;
-			if (newmark != *ctmark) {
-				*ctmark = newmark;
-#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE)
-				ip_conntrack_event_cache(IPCT_MARK, *pskb);
-#else
+			newmark = (ct->mark & ~markinfo->mask) | markinfo->mark;
+			if (newmark != ct->mark) {
+				ct->mark = newmark;
 				nf_conntrack_event_cache(IPCT_MARK, *pskb);
-#endif
 			}
 			break;
 		case XT_CONNMARK_SAVE:
-			newmark = (*ctmark & ~markinfo->mask) |
+			newmark = (ct->mark & ~markinfo->mask) |
 				  ((*pskb)->mark & markinfo->mask);
-			if (*ctmark != newmark) {
-				*ctmark = newmark;
-#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE)
-				ip_conntrack_event_cache(IPCT_MARK, *pskb);
-#else
+			if (ct->mark != newmark) {
+				ct->mark = newmark;
 				nf_conntrack_event_cache(IPCT_MARK, *pskb);
-#endif
 			}
 			break;
 		case XT_CONNMARK_RESTORE:
 			mark = (*pskb)->mark;
-			diff = (*ctmark ^ mark) & markinfo->mask;
+			diff = (ct->mark ^ mark) & markinfo->mask;
 			(*pskb)->mark = mark ^ diff;
 			break;
 		}
diff --git a/net/netfilter/xt_CONNSECMARK.c b/net/netfilter/xt_CONNSECMARK.c
index 1ab0db6..81c0c58 100644
--- a/net/netfilter/xt_CONNSECMARK.c
+++ b/net/netfilter/xt_CONNSECMARK.c
@@ -19,7 +19,7 @@
 #include <linux/skbuff.h>
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/xt_CONNSECMARK.h>
-#include <net/netfilter/nf_conntrack_compat.h>
+#include <net/netfilter/nf_conntrack.h>
 
 #define PFX "CONNSECMARK: "
 
@@ -36,12 +36,12 @@
 static void secmark_save(struct sk_buff *skb)
 {
 	if (skb->secmark) {
-		u32 *connsecmark;
+		struct nf_conn *ct;
 		enum ip_conntrack_info ctinfo;
 
-		connsecmark = nf_ct_get_secmark(skb, &ctinfo);
-		if (connsecmark && !*connsecmark)
-			*connsecmark = skb->secmark;
+		ct = nf_ct_get(skb, &ctinfo);
+		if (ct && !ct->secmark)
+			ct->secmark = skb->secmark;
 	}
 }
 
@@ -52,12 +52,12 @@
 static void secmark_restore(struct sk_buff *skb)
 {
 	if (!skb->secmark) {
-		u32 *connsecmark;
+		struct nf_conn *ct;
 		enum ip_conntrack_info ctinfo;
 
-		connsecmark = nf_ct_get_secmark(skb, &ctinfo);
-		if (connsecmark && *connsecmark)
-			skb->secmark = *connsecmark;
+		ct = nf_ct_get(skb, &ctinfo);
+		if (ct && ct->secmark)
+			skb->secmark = ct->secmark;
 	}
 }
 
diff --git a/net/netfilter/xt_DSCP.c b/net/netfilter/xt_DSCP.c
index a7cc75a..9f2f220 100644
--- a/net/netfilter/xt_DSCP.c
+++ b/net/netfilter/xt_DSCP.c
@@ -8,8 +8,6 @@
  * published by the Free Software Foundation.
  *
  * See RFC2474 for a description of the DSCP field within the IP Header.
- *
- * xt_DSCP.c,v 1.8 2002/08/06 18:41:57 laforge Exp
 */
 
 #include <linux/module.h>
@@ -35,13 +33,13 @@
 			   const void *targinfo)
 {
 	const struct xt_DSCP_info *dinfo = targinfo;
-	u_int8_t dscp = ipv4_get_dsfield((*pskb)->nh.iph) >> XT_DSCP_SHIFT;
+	u_int8_t dscp = ipv4_get_dsfield(ip_hdr(*pskb)) >> XT_DSCP_SHIFT;
 
 	if (dscp != dinfo->dscp) {
 		if (!skb_make_writable(pskb, sizeof(struct iphdr)))
 			return NF_DROP;
 
-		ipv4_change_dsfield((*pskb)->nh.iph, (__u8)(~XT_DSCP_MASK),
+		ipv4_change_dsfield(ip_hdr(*pskb), (__u8)(~XT_DSCP_MASK),
 				    dinfo->dscp << XT_DSCP_SHIFT);
 
 	}
@@ -56,13 +54,13 @@
 			    const void *targinfo)
 {
 	const struct xt_DSCP_info *dinfo = targinfo;
-	u_int8_t dscp = ipv6_get_dsfield((*pskb)->nh.ipv6h) >> XT_DSCP_SHIFT;
+	u_int8_t dscp = ipv6_get_dsfield(ipv6_hdr(*pskb)) >> XT_DSCP_SHIFT;
 
 	if (dscp != dinfo->dscp) {
 		if (!skb_make_writable(pskb, sizeof(struct ipv6hdr)))
 			return NF_DROP;
 
-		ipv6_change_dsfield((*pskb)->nh.ipv6h, (__u8)(~XT_DSCP_MASK),
+		ipv6_change_dsfield(ipv6_hdr(*pskb), (__u8)(~XT_DSCP_MASK),
 				    dinfo->dscp << XT_DSCP_SHIFT);
 	}
 	return XT_CONTINUE;
diff --git a/net/netfilter/xt_NOTRACK.c b/net/netfilter/xt_NOTRACK.c
index b874a20..5085fb3 100644
--- a/net/netfilter/xt_NOTRACK.c
+++ b/net/netfilter/xt_NOTRACK.c
@@ -5,7 +5,7 @@
 #include <linux/skbuff.h>
 
 #include <linux/netfilter/x_tables.h>
-#include <net/netfilter/nf_conntrack_compat.h>
+#include <net/netfilter/nf_conntrack.h>
 
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("ipt_NOTRACK");
@@ -26,7 +26,7 @@
 	   If there is a real ct entry correspondig to this packet,
 	   it'll hang aroun till timing out. We don't deal with it
 	   for performance reasons. JK */
-	nf_ct_untrack(*pskb);
+	(*pskb)->nfct = &nf_conntrack_untracked.ct_general;
 	(*pskb)->nfctinfo = IP_CT_NEW;
 	nf_conntrack_get((*pskb)->nfct);
 
diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c
index db7e38c..15fe8f6 100644
--- a/net/netfilter/xt_TCPMSS.c
+++ b/net/netfilter/xt_TCPMSS.c
@@ -54,7 +54,7 @@
 		return -1;
 
 	tcplen = (*pskb)->len - tcphoff;
-	tcph = (struct tcphdr *)((*pskb)->nh.raw + tcphoff);
+	tcph = (struct tcphdr *)(skb_network_header(*pskb) + tcphoff);
 
 	/* Since it passed flags test in tcp match, we know it is is
 	   not a fragment, and has data >= tcp header length.  SYN
@@ -113,7 +113,7 @@
 			return -1;
 		kfree_skb(*pskb);
 		*pskb = newskb;
-		tcph = (struct tcphdr *)((*pskb)->nh.raw + tcphoff);
+		tcph = (struct tcphdr *)(skb_network_header(*pskb) + tcphoff);
 	}
 
 	skb_put((*pskb), TCPOLEN_MSS);
@@ -145,7 +145,7 @@
 		  const struct xt_target *target,
 		  const void *targinfo)
 {
-	struct iphdr *iph = (*pskb)->nh.iph;
+	struct iphdr *iph = ip_hdr(*pskb);
 	__be16 newlen;
 	int ret;
 
@@ -154,7 +154,7 @@
 	if (ret < 0)
 		return NF_DROP;
 	if (ret > 0) {
-		iph = (*pskb)->nh.iph;
+		iph = ip_hdr(*pskb);
 		newlen = htons(ntohs(iph->tot_len) + ret);
 		nf_csum_replace2(&iph->check, iph->tot_len, newlen);
 		iph->tot_len = newlen;
@@ -171,7 +171,7 @@
 		  const struct xt_target *target,
 		  const void *targinfo)
 {
-	struct ipv6hdr *ipv6h = (*pskb)->nh.ipv6h;
+	struct ipv6hdr *ipv6h = ipv6_hdr(*pskb);
 	u8 nexthdr;
 	int tcphoff;
 	int ret;
@@ -187,7 +187,7 @@
 	if (ret < 0)
 		return NF_DROP;
 	if (ret > 0) {
-		ipv6h = (*pskb)->nh.ipv6h;
+		ipv6h = ipv6_hdr(*pskb);
 		ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) + ret);
 	}
 	return XT_CONTINUE;
diff --git a/net/netfilter/xt_connbytes.c b/net/netfilter/xt_connbytes.c
index 5e32dfa..804afe5 100644
--- a/net/netfilter/xt_connbytes.c
+++ b/net/netfilter/xt_connbytes.c
@@ -1,20 +1,11 @@
 /* Kernel module to match connection tracking byte counter.
  * GPL (C) 2002 Martin Devera (devik@cdi.cz).
- *
- * 2004-07-20 Harald Welte <laforge@netfilter.org>
- * 	- reimplemented to use per-connection accounting counters
- * 	- add functionality to match number of packets
- * 	- add functionality to match average packet size
- * 	- add support to match directions seperately
- * 2005-10-16 Harald Welte <laforge@netfilter.org>
- * 	- Port to x_tables
- *
  */
 #include <linux/module.h>
 #include <linux/skbuff.h>
-#include <net/netfilter/nf_conntrack_compat.h>
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/xt_connbytes.h>
+#include <net/netfilter/nf_conntrack.h>
 
 #include <asm/div64.h>
 #include <asm/bitops.h>
@@ -24,22 +15,6 @@
 MODULE_DESCRIPTION("iptables match for matching number of pkts/bytes per connection");
 MODULE_ALIAS("ipt_connbytes");
 
-/* 64bit divisor, dividend and result. dynamic precision */
-static u_int64_t div64_64(u_int64_t dividend, u_int64_t divisor)
-{
-	u_int32_t d = divisor;
-
-	if (divisor > 0xffffffffULL) {
-		unsigned int shift = fls(divisor >> 32);
-
-		d = divisor >> shift;
-		dividend >>= shift;
-	}
-
-	do_div(dividend, d);
-	return dividend;
-}
-
 static int
 match(const struct sk_buff *skb,
       const struct net_device *in,
@@ -51,13 +26,17 @@
       int *hotdrop)
 {
 	const struct xt_connbytes_info *sinfo = matchinfo;
+	struct nf_conn *ct;
+	enum ip_conntrack_info ctinfo;
 	u_int64_t what = 0;	/* initialize to make gcc happy */
 	u_int64_t bytes = 0;
 	u_int64_t pkts = 0;
 	const struct ip_conntrack_counter *counters;
 
-	if (!(counters = nf_ct_get_counters(skb)))
-		return 0; /* no match */
+	ct = nf_ct_get(skb, &ctinfo);
+	if (!ct)
+		return 0;
+	counters = ct->counters;
 
 	switch (sinfo->what) {
 	case XT_CONNBYTES_PKTS:
diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c
index 36c2def..e180325 100644
--- a/net/netfilter/xt_connmark.c
+++ b/net/netfilter/xt_connmark.c
@@ -21,16 +21,15 @@
 
 #include <linux/module.h>
 #include <linux/skbuff.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_connmark.h>
 
 MODULE_AUTHOR("Henrik Nordstrom <hno@marasytems.com>");
 MODULE_DESCRIPTION("IP tables connmark match module");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("ipt_connmark");
 
-#include <linux/netfilter/x_tables.h>
-#include <linux/netfilter/xt_connmark.h>
-#include <net/netfilter/nf_conntrack_compat.h>
-
 static int
 match(const struct sk_buff *skb,
       const struct net_device *in,
@@ -42,12 +41,14 @@
       int *hotdrop)
 {
 	const struct xt_connmark_info *info = matchinfo;
-	u_int32_t ctinfo;
-	const u_int32_t *ctmark = nf_ct_get_mark(skb, &ctinfo);
-	if (!ctmark)
+	struct nf_conn *ct;
+	enum ip_conntrack_info ctinfo;
+
+	ct = nf_ct_get(skb, &ctinfo);
+	if (!ct)
 		return 0;
 
-	return (((*ctmark) & info->mask) == info->mark) ^ info->invert;
+	return (((ct->mark) & info->mask) == info->mark) ^ info->invert;
 }
 
 static int
diff --git a/net/netfilter/xt_conntrack.c b/net/netfilter/xt_conntrack.c
index 2885c37..f4ea8fe 100644
--- a/net/netfilter/xt_conntrack.c
+++ b/net/netfilter/xt_conntrack.c
@@ -10,121 +10,15 @@
 
 #include <linux/module.h>
 #include <linux/skbuff.h>
-
-#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE)
-#include <linux/netfilter_ipv4/ip_conntrack.h>
-#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
-#else
-#include <net/netfilter/nf_conntrack.h>
-#endif
-
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/xt_conntrack.h>
-#include <net/netfilter/nf_conntrack_compat.h>
+#include <net/netfilter/nf_conntrack.h>
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
 MODULE_DESCRIPTION("iptables connection tracking match module");
 MODULE_ALIAS("ipt_conntrack");
 
-#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE)
-
-static int
-match(const struct sk_buff *skb,
-      const struct net_device *in,
-      const struct net_device *out,
-      const struct xt_match *match,
-      const void *matchinfo,
-      int offset,
-      unsigned int protoff,
-      int *hotdrop)
-{
-	const struct xt_conntrack_info *sinfo = matchinfo;
-	struct ip_conntrack *ct;
-	enum ip_conntrack_info ctinfo;
-	unsigned int statebit;
-
-	ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo);
-
-#define FWINV(bool, invflg) ((bool) ^ !!(sinfo->invflags & invflg))
-
-	if (ct == &ip_conntrack_untracked)
-		statebit = XT_CONNTRACK_STATE_UNTRACKED;
-	else if (ct)
-		statebit = XT_CONNTRACK_STATE_BIT(ctinfo);
-	else
-		statebit = XT_CONNTRACK_STATE_INVALID;
-
-	if (sinfo->flags & XT_CONNTRACK_STATE) {
-		if (ct) {
-			if (test_bit(IPS_SRC_NAT_BIT, &ct->status))
-				statebit |= XT_CONNTRACK_STATE_SNAT;
-			if (test_bit(IPS_DST_NAT_BIT, &ct->status))
-				statebit |= XT_CONNTRACK_STATE_DNAT;
-		}
-		if (FWINV((statebit & sinfo->statemask) == 0,
-			  XT_CONNTRACK_STATE))
-			return 0;
-	}
-
-	if (ct == NULL) {
-		if (sinfo->flags & ~XT_CONNTRACK_STATE)
-			return 0;
-		return 1;
-	}
-
-	if (sinfo->flags & XT_CONNTRACK_PROTO &&
-	    FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum !=
-		  sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum,
-		  XT_CONNTRACK_PROTO))
-		return 0;
-
-	if (sinfo->flags & XT_CONNTRACK_ORIGSRC &&
-	    FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip &
-		   sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) !=
-		  sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
-		  XT_CONNTRACK_ORIGSRC))
-		return 0;
-
-	if (sinfo->flags & XT_CONNTRACK_ORIGDST &&
-	    FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip &
-		   sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) !=
-		  sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
-		  XT_CONNTRACK_ORIGDST))
-		return 0;
-
-	if (sinfo->flags & XT_CONNTRACK_REPLSRC &&
-	    FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip &
-		   sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) !=
-		  sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
-		  XT_CONNTRACK_REPLSRC))
-		return 0;
-
-	if (sinfo->flags & XT_CONNTRACK_REPLDST &&
-	    FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip &
-		   sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) !=
-		  sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
-		  XT_CONNTRACK_REPLDST))
-		return 0;
-
-	if (sinfo->flags & XT_CONNTRACK_STATUS &&
-	    FWINV((ct->status & sinfo->statusmask) == 0,
-		  XT_CONNTRACK_STATUS))
-		return 0;
-
-	if (sinfo->flags & XT_CONNTRACK_EXPIRES) {
-		unsigned long expires = timer_pending(&ct->timeout) ?
-					(ct->timeout.expires - jiffies)/HZ : 0;
-
-		if (FWINV(!(expires >= sinfo->expires_min &&
-			    expires <= sinfo->expires_max),
-			  XT_CONNTRACK_EXPIRES))
-			return 0;
-	}
-	return 1;
-}
-
-#else /* CONFIG_IP_NF_CONNTRACK */
 static int
 match(const struct sk_buff *skb,
       const struct net_device *in,
@@ -220,8 +114,6 @@
 	return 1;
 }
 
-#endif /* CONFIG_NF_IP_CONNTRACK */
-
 static int
 checkentry(const char *tablename,
 	   const void *ip,
diff --git a/net/netfilter/xt_dscp.c b/net/netfilter/xt_dscp.c
index 26c7f4a..56b247e 100644
--- a/net/netfilter/xt_dscp.c
+++ b/net/netfilter/xt_dscp.c
@@ -1,7 +1,5 @@
 /* IP tables module for matching the value of the IPv4/IPv6 DSCP field
  *
- * xt_dscp.c,v 1.3 2002/08/05 19:00:21 laforge Exp
- *
  * (C) 2002 by Harald Welte <laforge@netfilter.org>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -34,7 +32,7 @@
 		 int *hotdrop)
 {
 	const struct xt_dscp_info *info = matchinfo;
-	u_int8_t dscp = ipv4_get_dsfield(skb->nh.iph) >> XT_DSCP_SHIFT;
+	u_int8_t dscp = ipv4_get_dsfield(ip_hdr(skb)) >> XT_DSCP_SHIFT;
 
 	return (dscp == info->dscp) ^ !!info->invert;
 }
@@ -49,7 +47,7 @@
 		  int *hotdrop)
 {
 	const struct xt_dscp_info *info = matchinfo;
-	u_int8_t dscp = ipv6_get_dsfield(skb->nh.ipv6h) >> XT_DSCP_SHIFT;
+	u_int8_t dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> XT_DSCP_SHIFT;
 
 	return (dscp == info->dscp) ^ !!info->invert;
 }
diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c
index 9f37d59..d3043fa 100644
--- a/net/netfilter/xt_hashlimit.c
+++ b/net/netfilter/xt_hashlimit.c
@@ -216,10 +216,8 @@
 	hinfo->pde->proc_fops = &dl_file_ops;
 	hinfo->pde->data = hinfo;
 
-	init_timer(&hinfo->timer);
+	setup_timer(&hinfo->timer, htable_gc, (unsigned long )hinfo);
 	hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval);
-	hinfo->timer.data = (unsigned long )hinfo;
-	hinfo->timer.function = htable_gc;
 	add_timer(&hinfo->timer);
 
 	spin_lock_bh(&hashlimit_lock);
@@ -380,22 +378,22 @@
 	switch (hinfo->family) {
 	case AF_INET:
 		if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_DIP)
-			dst->addr.ip.dst = skb->nh.iph->daddr;
+			dst->addr.ip.dst = ip_hdr(skb)->daddr;
 		if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_SIP)
-			dst->addr.ip.src = skb->nh.iph->saddr;
+			dst->addr.ip.src = ip_hdr(skb)->saddr;
 
 		if (!(hinfo->cfg.mode &
 		      (XT_HASHLIMIT_HASH_DPT | XT_HASHLIMIT_HASH_SPT)))
 			return 0;
-		nexthdr = skb->nh.iph->protocol;
+		nexthdr = ip_hdr(skb)->protocol;
 		break;
 #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
 	case AF_INET6:
 		if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_DIP)
-			memcpy(&dst->addr.ip6.dst, &skb->nh.ipv6h->daddr,
+			memcpy(&dst->addr.ip6.dst, &ipv6_hdr(skb)->daddr,
 			       sizeof(dst->addr.ip6.dst));
 		if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_SIP)
-			memcpy(&dst->addr.ip6.src, &skb->nh.ipv6h->saddr,
+			memcpy(&dst->addr.ip6.src, &ipv6_hdr(skb)->saddr,
 			       sizeof(dst->addr.ip6.src));
 
 		if (!(hinfo->cfg.mode &
diff --git a/net/netfilter/xt_helper.c b/net/netfilter/xt_helper.c
index 407d1d5..c139b2f 100644
--- a/net/netfilter/xt_helper.c
+++ b/net/netfilter/xt_helper.c
@@ -5,26 +5,16 @@
  * 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.
- *
- *   19 Mar 2002 Harald Welte <laforge@gnumonks.org>:
- *   		 - Port to newnat infrastructure
  */
 
 #include <linux/module.h>
 #include <linux/skbuff.h>
 #include <linux/netfilter.h>
-#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE)
-#include <linux/netfilter_ipv4/ip_conntrack.h>
-#include <linux/netfilter_ipv4/ip_conntrack_core.h>
-#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-#else
 #include <net/netfilter/nf_conntrack.h>
 #include <net/netfilter/nf_conntrack_core.h>
 #include <net/netfilter/nf_conntrack_helper.h>
-#endif
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/xt_helper.h>
-#include <net/netfilter/nf_conntrack_compat.h>
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Martin Josefsson <gandalf@netfilter.org>");
@@ -38,55 +28,6 @@
 #define DEBUGP(format, args...)
 #endif
 
-#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE)
-static int
-match(const struct sk_buff *skb,
-      const struct net_device *in,
-      const struct net_device *out,
-      const struct xt_match *match,
-      const void *matchinfo,
-      int offset,
-      unsigned int protoff,
-      int *hotdrop)
-{
-	const struct xt_helper_info *info = matchinfo;
-	struct ip_conntrack *ct;
-	enum ip_conntrack_info ctinfo;
-	int ret = info->invert;
-
-	ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo);
-	if (!ct) {
-		DEBUGP("xt_helper: Eek! invalid conntrack?\n");
-		return ret;
-	}
-
-	if (!ct->master) {
-		DEBUGP("xt_helper: conntrack %p has no master\n", ct);
-		return ret;
-	}
-
-	read_lock_bh(&ip_conntrack_lock);
-	if (!ct->master->helper) {
-		DEBUGP("xt_helper: master ct %p has no helper\n",
-			exp->expectant);
-		goto out_unlock;
-	}
-
-	DEBUGP("master's name = %s , info->name = %s\n",
-		ct->master->helper->name, info->name);
-
-	if (info->name[0] == '\0')
-		ret ^= 1;
-	else
-		ret ^= !strncmp(ct->master->helper->name, info->name,
-				strlen(ct->master->helper->name));
-out_unlock:
-	read_unlock_bh(&ip_conntrack_lock);
-	return ret;
-}
-
-#else /* CONFIG_IP_NF_CONNTRACK */
-
 static int
 match(const struct sk_buff *skb,
       const struct net_device *in,
@@ -134,7 +75,6 @@
 	read_unlock_bh(&nf_conntrack_lock);
 	return ret;
 }
-#endif
 
 static int check(const char *tablename,
 		 const void *inf,
diff --git a/net/netfilter/xt_length.c b/net/netfilter/xt_length.c
index 32fb998..77288c5 100644
--- a/net/netfilter/xt_length.c
+++ b/net/netfilter/xt_length.c
@@ -31,7 +31,7 @@
       int *hotdrop)
 {
 	const struct xt_length_info *info = matchinfo;
-	u_int16_t pktlen = ntohs(skb->nh.iph->tot_len);
+	u_int16_t pktlen = ntohs(ip_hdr(skb)->tot_len);
 
 	return (pktlen >= info->min && pktlen <= info->max) ^ info->invert;
 }
@@ -47,7 +47,8 @@
        int *hotdrop)
 {
 	const struct xt_length_info *info = matchinfo;
-	u_int16_t pktlen = ntohs(skb->nh.ipv6h->payload_len) + sizeof(struct ipv6hdr);
+	const u_int16_t pktlen = (ntohs(ipv6_hdr(skb)->payload_len) +
+				  sizeof(struct ipv6hdr));
 
 	return (pktlen >= info->min && pktlen <= info->max) ^ info->invert;
 }
diff --git a/net/netfilter/xt_limit.c b/net/netfilter/xt_limit.c
index 6fd8347..571a72a 100644
--- a/net/netfilter/xt_limit.c
+++ b/net/netfilter/xt_limit.c
@@ -1,10 +1,3 @@
-/* Kernel module to control the rate
- *
- * 2 September 1999: Changed from the target RATE to the match
- *                   `limit', removed logging.  Did I mention that
- *                   Alexey is a fucking genius?
- *                   Rusty Russell (rusty@rustcorp.com.au).  */
-
 /* (C) 1999 Jérôme de Vivie <devivie@info.enserb.u-bordeaux.fr>
  * (C) 1999 Hervé Eychenne <eychenne@info.enserb.u-bordeaux.fr>
  *
diff --git a/net/netfilter/xt_mac.c b/net/netfilter/xt_mac.c
index d430d90..1d3a1d9 100644
--- a/net/netfilter/xt_mac.c
+++ b/net/netfilter/xt_mac.c
@@ -37,8 +37,8 @@
     const struct xt_mac_info *info = matchinfo;
 
     /* Is mac pointer valid? */
-    return (skb->mac.raw >= skb->head
-	    && (skb->mac.raw + ETH_HLEN) <= skb->data
+    return (skb_mac_header(skb) >= skb->head &&
+	    (skb_mac_header(skb) + ETH_HLEN) <= skb->data
 	    /* If so, compare... */
 	    && ((!compare_ether_addr(eth_hdr(skb)->h_source, info->srcaddr))
 		^ info->invert));
diff --git a/net/netfilter/xt_pkttype.c b/net/netfilter/xt_pkttype.c
index 16e7b08..e1409fc 100644
--- a/net/netfilter/xt_pkttype.c
+++ b/net/netfilter/xt_pkttype.c
@@ -34,7 +34,7 @@
 	const struct xt_pkttype_info *info = matchinfo;
 
 	if (skb->pkt_type == PACKET_LOOPBACK)
-		type = (MULTICAST(skb->nh.iph->daddr)
+		type = (MULTICAST(ip_hdr(skb)->daddr)
 			? PACKET_MULTICAST
 			: PACKET_BROADCAST);
 	else
diff --git a/net/netfilter/xt_realm.c b/net/netfilter/xt_realm.c
index 97ffc2f..c2017f8 100644
--- a/net/netfilter/xt_realm.c
+++ b/net/netfilter/xt_realm.c
@@ -1,7 +1,5 @@
 /* IP tables module for matching the routing realm
  *
- * $Id: ipt_realm.c,v 1.3 2004/03/05 13:25:40 laforge Exp $
- *
  * (C) 2003 by Sampsa Ranta <sampsa@netsonic.fi>
  *
  * This program is free software; you can redistribute it and/or modify
diff --git a/net/netfilter/xt_state.c b/net/netfilter/xt_state.c
index df37b91..149294f 100644
--- a/net/netfilter/xt_state.c
+++ b/net/netfilter/xt_state.c
@@ -10,7 +10,7 @@
 
 #include <linux/module.h>
 #include <linux/skbuff.h>
-#include <net/netfilter/nf_conntrack_compat.h>
+#include <net/netfilter/nf_conntrack.h>
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/xt_state.h>
 
@@ -36,7 +36,7 @@
 
 	if (nf_ct_is_untracked(skb))
 		statebit = XT_STATE_UNTRACKED;
-	else if (!nf_ct_get_ctinfo(skb, &ctinfo))
+	else if (!nf_ct_get(skb, &ctinfo))
 		statebit = XT_STATE_INVALID;
 	else
 		statebit = XT_STATE_BIT(ctinfo);
diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c
index e03a328..f2535e7 100644
--- a/net/netlabel/netlabel_kapi.c
+++ b/net/netlabel/netlabel_kapi.c
@@ -263,9 +263,6 @@
 	int ret_val = -ENOENT;
 	struct netlbl_dom_map *dom_entry;
 
-	if ((secattr->flags & NETLBL_SECATTR_DOMAIN) == 0)
-		return -ENOENT;
-
 	rcu_read_lock();
 	dom_entry = netlbl_domhsh_getentry(secattr->domain);
 	if (dom_entry == NULL)
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index c48b0f49..42d2fb9 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -56,6 +56,7 @@
 #include <linux/types.h>
 #include <linux/audit.h>
 #include <linux/selinux.h>
+#include <linux/mutex.h>
 
 #include <net/sock.h>
 #include <net/scm.h>
@@ -76,7 +77,8 @@
 	unsigned long		state;
 	wait_queue_head_t	wait;
 	struct netlink_callback	*cb;
-	spinlock_t		cb_lock;
+	struct mutex		*cb_mutex;
+	struct mutex		cb_def_mutex;
 	void			(*data_ready)(struct sock *sk, int bytes);
 	struct module		*module;
 };
@@ -108,6 +110,7 @@
 	unsigned long *listeners;
 	unsigned int nl_nonroot;
 	unsigned int groups;
+	struct mutex *cb_mutex;
 	struct module *module;
 	int registered;
 };
@@ -118,6 +121,7 @@
 
 static int netlink_dump(struct sock *sk);
 static void netlink_destroy_callback(struct netlink_callback *cb);
+static void netlink_queue_skip(struct nlmsghdr *nlh, struct sk_buff *skb);
 
 static DEFINE_RWLOCK(nl_table_lock);
 static atomic_t nl_table_users = ATOMIC_INIT(0);
@@ -370,7 +374,8 @@
 	.obj_size = sizeof(struct netlink_sock),
 };
 
-static int __netlink_create(struct socket *sock, int protocol)
+static int __netlink_create(struct socket *sock, struct mutex *cb_mutex,
+			    int protocol)
 {
 	struct sock *sk;
 	struct netlink_sock *nlk;
@@ -384,7 +389,12 @@
 	sock_init_data(sock, sk);
 
 	nlk = nlk_sk(sk);
-	spin_lock_init(&nlk->cb_lock);
+	if (cb_mutex)
+		nlk->cb_mutex = cb_mutex;
+	else {
+		nlk->cb_mutex = &nlk->cb_def_mutex;
+		mutex_init(nlk->cb_mutex);
+	}
 	init_waitqueue_head(&nlk->wait);
 
 	sk->sk_destruct = netlink_sock_destruct;
@@ -395,8 +405,8 @@
 static int netlink_create(struct socket *sock, int protocol)
 {
 	struct module *module = NULL;
+	struct mutex *cb_mutex;
 	struct netlink_sock *nlk;
-	unsigned int groups;
 	int err = 0;
 
 	sock->state = SS_UNCONNECTED;
@@ -418,10 +428,10 @@
 	if (nl_table[protocol].registered &&
 	    try_module_get(nl_table[protocol].module))
 		module = nl_table[protocol].module;
-	groups = nl_table[protocol].groups;
+	cb_mutex = nl_table[protocol].cb_mutex;
 	netlink_unlock_table();
 
-	if ((err = __netlink_create(sock, protocol)) < 0)
+	if ((err = __netlink_create(sock, cb_mutex, protocol)) < 0)
 		goto out_module;
 
 	nlk = nlk_sk(sock->sk);
@@ -446,14 +456,14 @@
 	sock_orphan(sk);
 	nlk = nlk_sk(sk);
 
-	spin_lock(&nlk->cb_lock);
+	mutex_lock(nlk->cb_mutex);
 	if (nlk->cb) {
 		if (nlk->cb->done)
 			nlk->cb->done(nlk->cb);
 		netlink_destroy_callback(nlk->cb);
 		nlk->cb = NULL;
 	}
-	spin_unlock(&nlk->cb_lock);
+	mutex_unlock(nlk->cb_mutex);
 
 	/* OK. Socket is unlinked, and, therefore,
 	   no new packets will arrive */
@@ -1215,7 +1225,7 @@
 		copied = len;
 	}
 
-	skb->h.raw = skb->data;
+	skb_reset_transport_header(skb);
 	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 
 	if (msg->msg_name) {
@@ -1242,6 +1252,9 @@
 
 	scm_recv(sock, msg, siocb->scm, flags);
 
+	if (flags & MSG_TRUNC)
+		copied = skb->len;
+
 out:
 	netlink_rcv_wake(sk);
 	return err ? : copied;
@@ -1265,7 +1278,7 @@
 struct sock *
 netlink_kernel_create(int unit, unsigned int groups,
 		      void (*input)(struct sock *sk, int len),
-		      struct module *module)
+		      struct mutex *cb_mutex, struct module *module)
 {
 	struct socket *sock;
 	struct sock *sk;
@@ -1280,7 +1293,7 @@
 	if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock))
 		return NULL;
 
-	if (__netlink_create(sock, unit) < 0)
+	if (__netlink_create(sock, cb_mutex, unit) < 0)
 		goto out_sock_release;
 
 	if (groups < 32)
@@ -1304,6 +1317,7 @@
 	netlink_table_grab();
 	nl_table[unit].groups = groups;
 	nl_table[unit].listeners = listeners;
+	nl_table[unit].cb_mutex = cb_mutex;
 	nl_table[unit].module = module;
 	nl_table[unit].registered = 1;
 	netlink_table_ungrab();
@@ -1346,7 +1360,7 @@
 	if (!skb)
 		goto errout;
 
-	spin_lock(&nlk->cb_lock);
+	mutex_lock(nlk->cb_mutex);
 
 	cb = nlk->cb;
 	if (cb == NULL) {
@@ -1357,7 +1371,7 @@
 	len = cb->dump(skb, cb);
 
 	if (len > 0) {
-		spin_unlock(&nlk->cb_lock);
+		mutex_unlock(nlk->cb_mutex);
 		skb_queue_tail(&sk->sk_receive_queue, skb);
 		sk->sk_data_ready(sk, len);
 		return 0;
@@ -1375,13 +1389,13 @@
 	if (cb->done)
 		cb->done(cb);
 	nlk->cb = NULL;
-	spin_unlock(&nlk->cb_lock);
+	mutex_unlock(nlk->cb_mutex);
 
 	netlink_destroy_callback(cb);
 	return 0;
 
 errout_skb:
-	spin_unlock(&nlk->cb_lock);
+	mutex_unlock(nlk->cb_mutex);
 	kfree_skb(skb);
 errout:
 	return err;
@@ -1413,19 +1427,24 @@
 	}
 	nlk = nlk_sk(sk);
 	/* A dump or destruction is in progress... */
-	spin_lock(&nlk->cb_lock);
+	mutex_lock(nlk->cb_mutex);
 	if (nlk->cb || sock_flag(sk, SOCK_DEAD)) {
-		spin_unlock(&nlk->cb_lock);
+		mutex_unlock(nlk->cb_mutex);
 		netlink_destroy_callback(cb);
 		sock_put(sk);
 		return -EBUSY;
 	}
 	nlk->cb = cb;
-	spin_unlock(&nlk->cb_lock);
+	mutex_unlock(nlk->cb_mutex);
 
 	netlink_dump(sk);
 	sock_put(sk);
-	return 0;
+
+	/* We successfully started a dump, by returning -EINTR we
+	 * signal the queue mangement to interrupt processing of
+	 * any netlink messages so userspace gets a chance to read
+	 * the results. */
+	return -EINTR;
 }
 
 void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err)
@@ -1462,27 +1481,35 @@
 }
 
 static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *,
-						     struct nlmsghdr *, int *))
+						     struct nlmsghdr *))
 {
 	struct nlmsghdr *nlh;
 	int err;
 
 	while (skb->len >= nlmsg_total_size(0)) {
-		nlh = (struct nlmsghdr *) skb->data;
+		nlh = nlmsg_hdr(skb);
+		err = 0;
 
 		if (nlh->nlmsg_len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len)
 			return 0;
 
-		if (cb(skb, nlh, &err) < 0) {
-			/* Not an error, but we have to interrupt processing
-			 * here. Note: that in this case we do not pull
-			 * message from skb, it will be processed later.
-			 */
-			if (err == 0)
-				return -1;
+		/* Only requests are handled by the kernel */
+		if (!(nlh->nlmsg_flags & NLM_F_REQUEST))
+			goto skip;
+
+		/* Skip control messages */
+		if (nlh->nlmsg_type < NLMSG_MIN_TYPE)
+			goto skip;
+
+		err = cb(skb, nlh);
+		if (err == -EINTR) {
+			/* Not an error, but we interrupt processing */
+			netlink_queue_skip(nlh, skb);
+			return err;
+		}
+skip:
+		if (nlh->nlmsg_flags & NLM_F_ACK || err)
 			netlink_ack(skb, nlh, err);
-		} else if (nlh->nlmsg_flags & NLM_F_ACK)
-			netlink_ack(skb, nlh, 0);
 
 		netlink_queue_skip(nlh, skb);
 	}
@@ -1504,9 +1531,14 @@
  *
  * qlen must be initialized to 0 before the initial entry, afterwards
  * the function may be called repeatedly until qlen reaches 0.
+ *
+ * The callback function may return -EINTR to signal that processing
+ * of netlink messages shall be interrupted. In this case the message
+ * currently being processed will NOT be requeued onto the receive
+ * queue.
  */
 void netlink_run_queue(struct sock *sk, unsigned int *qlen,
-		       int (*cb)(struct sk_buff *, struct nlmsghdr *, int *))
+		       int (*cb)(struct sk_buff *, struct nlmsghdr *))
 {
 	struct sk_buff *skb;
 
@@ -1537,7 +1569,7 @@
  * Pulls the given netlink message off the socket buffer so the next
  * call to netlink_queue_run() will not reconsider the message.
  */
-void netlink_queue_skip(struct nlmsghdr *nlh, struct sk_buff *skb)
+static void netlink_queue_skip(struct nlmsghdr *nlh, struct sk_buff *skb)
 {
 	int msglen = NLMSG_ALIGN(nlh->nlmsg_len);
 
@@ -1820,12 +1852,10 @@
 
 EXPORT_SYMBOL(netlink_ack);
 EXPORT_SYMBOL(netlink_run_queue);
-EXPORT_SYMBOL(netlink_queue_skip);
 EXPORT_SYMBOL(netlink_broadcast);
 EXPORT_SYMBOL(netlink_dump_start);
 EXPORT_SYMBOL(netlink_kernel_create);
 EXPORT_SYMBOL(netlink_register_notifier);
-EXPORT_SYMBOL(netlink_set_err);
 EXPORT_SYMBOL(netlink_set_nonroot);
 EXPORT_SYMBOL(netlink_unicast);
 EXPORT_SYMBOL(netlink_unregister_notifier);
diff --git a/net/netlink/attr.c b/net/netlink/attr.c
index 0041395..df5f820 100644
--- a/net/netlink/attr.c
+++ b/net/netlink/attr.c
@@ -67,6 +67,11 @@
 		}
 		break;
 
+	case NLA_BINARY:
+		if (pt->len && attrlen > pt->len)
+			return -ERANGE;
+		break;
+
 	default:
 		if (pt->len)
 			minlen = pt->len;
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index c299679..6e31234 100644
--- a/net/netlink/genetlink.c
+++ b/net/netlink/genetlink.c
@@ -295,66 +295,46 @@
 	return -ENOENT;
 }
 
-static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
-			       int *errp)
+static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
 	struct genl_ops *ops;
 	struct genl_family *family;
 	struct genl_info info;
 	struct genlmsghdr *hdr = nlmsg_data(nlh);
-	int hdrlen, err = -EINVAL;
-
-	if (!(nlh->nlmsg_flags & NLM_F_REQUEST))
-		goto ignore;
-
-	if (nlh->nlmsg_type < NLMSG_MIN_TYPE)
-		goto ignore;
+	int hdrlen, err;
 
 	family = genl_family_find_byid(nlh->nlmsg_type);
-	if (family == NULL) {
-		err = -ENOENT;
-		goto errout;
-	}
+	if (family == NULL)
+		return -ENOENT;
 
 	hdrlen = GENL_HDRLEN + family->hdrsize;
 	if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
-		goto errout;
+		return -EINVAL;
 
 	ops = genl_get_cmd(hdr->cmd, family);
-	if (ops == NULL) {
-		err = -EOPNOTSUPP;
-		goto errout;
-	}
+	if (ops == NULL)
+		return -EOPNOTSUPP;
 
-	if ((ops->flags & GENL_ADMIN_PERM) && security_netlink_recv(skb, CAP_NET_ADMIN)) {
-		err = -EPERM;
-		goto errout;
-	}
+	if ((ops->flags & GENL_ADMIN_PERM) &&
+	    security_netlink_recv(skb, CAP_NET_ADMIN))
+		return -EPERM;
 
 	if (nlh->nlmsg_flags & NLM_F_DUMP) {
-		if (ops->dumpit == NULL) {
-			err = -EOPNOTSUPP;
-			goto errout;
-		}
+		if (ops->dumpit == NULL)
+			return -EOPNOTSUPP;
 
-		*errp = err = netlink_dump_start(genl_sock, skb, nlh,
-						 ops->dumpit, ops->done);
-		if (err == 0)
-			skb_pull(skb, min(NLMSG_ALIGN(nlh->nlmsg_len),
-					  skb->len));
-		return -1;
+		return netlink_dump_start(genl_sock, skb, nlh,
+					  ops->dumpit, ops->done);
 	}
 
-	if (ops->doit == NULL) {
-		err = -EOPNOTSUPP;
-		goto errout;
-	}
+	if (ops->doit == NULL)
+		return -EOPNOTSUPP;
 
 	if (family->attrbuf) {
 		err = nlmsg_parse(nlh, hdrlen, family->attrbuf, family->maxattr,
 				  ops->policy);
 		if (err < 0)
-			goto errout;
+			return err;
 	}
 
 	info.snd_seq = nlh->nlmsg_seq;
@@ -364,15 +344,7 @@
 	info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN;
 	info.attrs = family->attrbuf;
 
-	*errp = err = ops->doit(skb, &info);
-	return err;
-
-ignore:
-	return 0;
-
-errout:
-	*errp = err;
-	return -1;
+	return ops->doit(skb, &info);
 }
 
 static void genl_rcv(struct sock *sk, int len)
@@ -586,7 +558,7 @@
 
 	netlink_set_nonroot(NETLINK_GENERIC, NL_NONROOT_RECV);
 	genl_sock = netlink_kernel_create(NETLINK_GENERIC, GENL_MAX_ID,
-					  genl_rcv, THIS_MODULE);
+					  genl_rcv, NULL, THIS_MODULE);
 	if (genl_sock == NULL)
 		panic("GENL: Cannot initialize generic netlink\n");
 
diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c
index bf9837d..5d4a26c 100644
--- a/net/netrom/af_netrom.c
+++ b/net/netrom/af_netrom.c
@@ -625,42 +625,42 @@
 	ax25_address *source = NULL;
 	ax25_uid_assoc *user;
 	struct net_device *dev;
+	int err = 0;
 
 	lock_sock(sk);
 	if (sk->sk_state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
 		sock->state = SS_CONNECTED;
-		release_sock(sk);
-		return 0;	/* Connect completed during a ERESTARTSYS event */
+		goto out_release;	/* Connect completed during a ERESTARTSYS event */
 	}
 
 	if (sk->sk_state == TCP_CLOSE && sock->state == SS_CONNECTING) {
 		sock->state = SS_UNCONNECTED;
-		release_sock(sk);
-		return -ECONNREFUSED;
+		err = -ECONNREFUSED;
+		goto out_release;
 	}
 
 	if (sk->sk_state == TCP_ESTABLISHED) {
-		release_sock(sk);
-		return -EISCONN;	/* No reconnect on a seqpacket socket */
+		err = -EISCONN;	/* No reconnect on a seqpacket socket */
+		goto out_release;
 	}
 
 	sk->sk_state   = TCP_CLOSE;
 	sock->state = SS_UNCONNECTED;
 
 	if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25)) {
-		release_sock(sk);
-		return -EINVAL;
+		err = -EINVAL;
+		goto out_release;
 	}
 	if (addr->sax25_family != AF_NETROM) {
-		release_sock(sk);
-		return -EINVAL;
+		err = -EINVAL;
+		goto out_release;
 	}
 	if (sock_flag(sk, SOCK_ZAPPED)) {	/* Must bind first - autobinding in this may or may not work */
 		sock_reset_flag(sk, SOCK_ZAPPED);
 
 		if ((dev = nr_dev_first()) == NULL) {
-			release_sock(sk);
-			return -ENETUNREACH;
+			err = -ENETUNREACH;
+			goto out_release;
 		}
 		source = (ax25_address *)dev->dev_addr;
 
@@ -671,8 +671,8 @@
 		} else {
 			if (ax25_uid_policy && !capable(CAP_NET_ADMIN)) {
 				dev_put(dev);
-				release_sock(sk);
-				return -EPERM;
+				err = -EPERM;
+				goto out_release;
 			}
 			nr->user_addr   = *source;
 		}
@@ -707,8 +707,8 @@
 
 	/* Now the loop */
 	if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) {
-		release_sock(sk);
-		return -EINPROGRESS;
+		err = -EINPROGRESS;
+		goto out_release;
 	}
 
 	/*
@@ -716,46 +716,46 @@
 	 * closed.
 	 */
 	if (sk->sk_state == TCP_SYN_SENT) {
-		struct task_struct *tsk = current;
-		DECLARE_WAITQUEUE(wait, tsk);
+		DEFINE_WAIT(wait);
 
-		add_wait_queue(sk->sk_sleep, &wait);
 		for (;;) {
-			set_current_state(TASK_INTERRUPTIBLE);
+			prepare_to_wait(sk->sk_sleep, &wait,
+			                TASK_INTERRUPTIBLE);
 			if (sk->sk_state != TCP_SYN_SENT)
 				break;
-			release_sock(sk);
-			if (!signal_pending(tsk)) {
+			if (!signal_pending(current)) {
+				release_sock(sk);
 				schedule();
 				lock_sock(sk);
 				continue;
 			}
-			current->state = TASK_RUNNING;
-			remove_wait_queue(sk->sk_sleep, &wait);
-			return -ERESTARTSYS;
+			err = -ERESTARTSYS;
+			break;
 		}
-		current->state = TASK_RUNNING;
-		remove_wait_queue(sk->sk_sleep, &wait);
+		finish_wait(sk->sk_sleep, &wait);
+		if (err)
+			goto out_release;
 	}
 
 	if (sk->sk_state != TCP_ESTABLISHED) {
 		sock->state = SS_UNCONNECTED;
-		release_sock(sk);
-		return sock_error(sk);	/* Always set at this point */
+		err = sock_error(sk);	/* Always set at this point */
+		goto out_release;
 	}
 
 	sock->state = SS_CONNECTED;
+
+out_release:
 	release_sock(sk);
 
-	return 0;
+	return err;
 }
 
 static int nr_accept(struct socket *sock, struct socket *newsock, int flags)
 {
-	struct task_struct *tsk = current;
-	DECLARE_WAITQUEUE(wait, tsk);
 	struct sk_buff *skb;
 	struct sock *newsk;
+	DEFINE_WAIT(wait);
 	struct sock *sk;
 	int err = 0;
 
@@ -765,42 +765,40 @@
 	lock_sock(sk);
 	if (sk->sk_type != SOCK_SEQPACKET) {
 		err = -EOPNOTSUPP;
-		goto out;
+		goto out_release;
 	}
 
 	if (sk->sk_state != TCP_LISTEN) {
 		err = -EINVAL;
-		goto out;
+		goto out_release;
 	}
 
 	/*
 	 *	The write queue this time is holding sockets ready to use
 	 *	hooked into the SABM we saved
 	 */
-	add_wait_queue(sk->sk_sleep, &wait);
 	for (;;) {
+		prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
 		skb = skb_dequeue(&sk->sk_receive_queue);
 		if (skb)
 			break;
 
-		current->state = TASK_INTERRUPTIBLE;
-		release_sock(sk);
 		if (flags & O_NONBLOCK) {
-			current->state = TASK_RUNNING;
-			remove_wait_queue(sk->sk_sleep, &wait);
-			return -EWOULDBLOCK;
+			err = -EWOULDBLOCK;
+			break;
 		}
-		if (!signal_pending(tsk)) {
+		if (!signal_pending(current)) {
+			release_sock(sk);
 			schedule();
 			lock_sock(sk);
 			continue;
 		}
-		current->state = TASK_RUNNING;
-		remove_wait_queue(sk->sk_sleep, &wait);
-		return -ERESTARTSYS;
+		err = -ERESTARTSYS;
+		break;
 	}
-	current->state = TASK_RUNNING;
-	remove_wait_queue(sk->sk_sleep, &wait);
+	finish_wait(sk->sk_sleep, &wait);
+	if (err)
+		goto out_release;
 
 	newsk = skb->sk;
 	newsk->sk_socket = newsock;
@@ -811,8 +809,9 @@
 	sk_acceptq_removed(sk);
 	newsock->sk = newsk;
 
-out:
+out_release:
 	release_sock(sk);
+
 	return err;
 }
 
@@ -878,7 +877,7 @@
 	if (frametype == NR_PROTOEXT &&
 	    circuit_index == NR_PROTO_IP && circuit_id == NR_PROTO_IP) {
 		skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
-		skb->h.raw = skb->data;
+		skb_reset_transport_header(skb);
 
 		return nr_rx_ip(skb, dev);
 	}
@@ -904,7 +903,7 @@
 	}
 
 	if (sk != NULL) {
-		skb->h.raw = skb->data;
+		skb_reset_transport_header(skb);
 
 		if (frametype == NR_CONNACK && skb->len == 22)
 			nr_sk(sk)->bpqext = 1;
@@ -1074,6 +1073,7 @@
 		goto out;
 
 	skb_reserve(skb, size - len);
+	skb_reset_transport_header(skb);
 
 	/*
 	 *	Push down the NET/ROM header
@@ -1094,14 +1094,12 @@
 	/*
 	 *	Put the data on the end
 	 */
+	skb_put(skb, len);
 
-	skb->h.raw = skb_put(skb, len);
-
-	asmptr = skb->h.raw;
 	SOCK_DEBUG(sk, "NET/ROM: Appending user data\n");
 
 	/* User data follows immediately after the NET/ROM transport header */
-	if (memcpy_fromiovec(asmptr, msg->msg_iov, len)) {
+	if (memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len)) {
 		kfree_skb(skb);
 		err = -EFAULT;
 		goto out;
@@ -1149,7 +1147,7 @@
 		return er;
 	}
 
-	skb->h.raw = skb->data;
+	skb_reset_transport_header(skb);
 	copied     = skb->len;
 
 	if (copied > size) {
@@ -1161,7 +1159,8 @@
 
 	if (sax != NULL) {
 		sax->sax25_family = AF_NETROM;
-		memcpy(sax->sax25_call.ax25_call, skb->data + 7, AX25_ADDR_LEN);
+		skb_copy_from_linear_data_offset(skb, 7, sax->sax25_call.ax25_call,
+			      AX25_ADDR_LEN);
 	}
 
 	msg->msg_namelen = sizeof(*sax);
@@ -1209,6 +1208,12 @@
 		release_sock(sk);
 		return ret;
 
+	case SIOCGSTAMPNS:
+		lock_sock(sk);
+		ret = sock_get_timestampns(sk, argp);
+		release_sock(sk);
+		return ret;
+
 	case SIOCGIFADDR:
 	case SIOCSIFADDR:
 	case SIOCGIFDSTADDR:
diff --git a/net/netrom/nr_dev.c b/net/netrom/nr_dev.c
index 9a97ed6..c7b5d93 100644
--- a/net/netrom/nr_dev.c
+++ b/net/netrom/nr_dev.c
@@ -56,8 +56,8 @@
 
 	/* Spoof incoming device */
 	skb->dev      = dev;
-	skb->mac.raw  = skb->nh.raw;
-	skb->nh.raw   = skb->data;
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
 	skb->pkt_type = PACKET_HOST;
 
 	netif_rx(skb);
diff --git a/net/netrom/nr_in.c b/net/netrom/nr_in.c
index 5560acb..6817648 100644
--- a/net/netrom/nr_in.c
+++ b/net/netrom/nr_in.c
@@ -51,10 +51,12 @@
 		if ((skbn = alloc_skb(nr->fraglen, GFP_ATOMIC)) == NULL)
 			return 1;
 
-		skbn->h.raw = skbn->data;
+		skb_reset_transport_header(skbn);
 
 		while ((skbo = skb_dequeue(&nr->frag_queue)) != NULL) {
-			memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
+			skb_copy_from_linear_data(skbo,
+						  skb_put(skbn, skbo->len),
+						  skbo->len);
 			kfree_skb(skbo);
 		}
 
diff --git a/net/netrom/nr_loopback.c b/net/netrom/nr_loopback.c
index e856ae1..f324d5d 100644
--- a/net/netrom/nr_loopback.c
+++ b/net/netrom/nr_loopback.c
@@ -34,8 +34,8 @@
 	struct sk_buff *skbn;
 
 	if ((skbn = alloc_skb(skb->len, GFP_ATOMIC)) != NULL) {
-		memcpy(skb_put(skbn, skb->len), skb->data, skb->len);
-		skbn->h.raw = skbn->data;
+		skb_copy_from_linear_data(skb, skb_put(skbn, skb->len), skb->len);
+		skb_reset_transport_header(skbn);
 
 		skb_queue_tail(&loopback_queue, skbn);
 
diff --git a/net/netrom/nr_out.c b/net/netrom/nr_out.c
index 0cbfb61..e3e6c44 100644
--- a/net/netrom/nr_out.c
+++ b/net/netrom/nr_out.c
@@ -40,7 +40,7 @@
 
 	if (skb->len - NR_TRANSPORT_LEN > NR_MAX_PACKET_SIZE) {
 		/* Save a copy of the Transport Header */
-		memcpy(transport, skb->data, NR_TRANSPORT_LEN);
+		skb_copy_from_linear_data(skb, transport, NR_TRANSPORT_LEN);
 		skb_pull(skb, NR_TRANSPORT_LEN);
 
 		frontlen = skb_headroom(skb);
@@ -54,13 +54,13 @@
 			len = (NR_MAX_PACKET_SIZE > skb->len) ? skb->len : NR_MAX_PACKET_SIZE;
 
 			/* Copy the user data */
-			memcpy(skb_put(skbn, len), skb->data, len);
+			skb_copy_from_linear_data(skb, skb_put(skbn, len), len);
 			skb_pull(skb, len);
 
 			/* Duplicate the Transport Header */
 			skb_push(skbn, NR_TRANSPORT_LEN);
-			memcpy(skbn->data, transport, NR_TRANSPORT_LEN);
-
+			skb_copy_to_linear_data(skbn, transport,
+						NR_TRANSPORT_LEN);
 			if (skb->len > 0)
 				skbn->data[4] |= NR_MORE_FLAG;
 
diff --git a/net/netrom/nr_subr.c b/net/netrom/nr_subr.c
index 07b694d..04e7d0d2 100644
--- a/net/netrom/nr_subr.c
+++ b/net/netrom/nr_subr.c
@@ -226,13 +226,13 @@
 
 	dptr = skb_put(skbn, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
 
-	memcpy(dptr, skb->data + 7, AX25_ADDR_LEN);
+	skb_copy_from_linear_data_offset(skb, 7, dptr, AX25_ADDR_LEN);
 	dptr[6] &= ~AX25_CBIT;
 	dptr[6] &= ~AX25_EBIT;
 	dptr[6] |= AX25_SSSID_SPARE;
 	dptr += AX25_ADDR_LEN;
 
-	memcpy(dptr, skb->data + 0, AX25_ADDR_LEN);
+	skb_copy_from_linear_data(skb, dptr, AX25_ADDR_LEN);
 	dptr[6] &= ~AX25_CBIT;
 	dptr[6] |= AX25_EBIT;
 	dptr[6] |= AX25_SSSID_SPARE;
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 28d47e8..02e401c 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -114,22 +114,22 @@
 -----------
 
 Incoming, dev->hard_header!=NULL
-   mac.raw -> ll header
-   data    -> data
+   mac_header -> ll header
+   data       -> data
 
 Outgoing, dev->hard_header!=NULL
-   mac.raw -> ll header
-   data    -> ll header
+   mac_header -> ll header
+   data       -> ll header
 
 Incoming, dev->hard_header==NULL
-   mac.raw -> UNKNOWN position. It is very likely, that it points to ll header.
-	      PPP makes it, that is wrong, because introduce assymetry
-	      between rx and tx paths.
-   data    -> data
+   mac_header -> UNKNOWN position. It is very likely, that it points to ll
+		 header.  PPP makes it, that is wrong, because introduce
+                 assymetry between rx and tx paths.
+   data       -> data
 
 Outgoing, dev->hard_header==NULL
-   mac.raw -> data. ll header is still not built!
-   data    -> data
+   mac_header -> data. ll header is still not built!
+   data       -> data
 
 Resume
   If dev->hard_header==NULL we are unlikely to restore sensible ll header.
@@ -139,12 +139,12 @@
 ------------
 
 dev->hard_header != NULL
-   mac.raw -> ll header
-   data    -> ll header
+   mac_header -> ll header
+   data       -> ll header
 
 dev->hard_header == NULL (ll header is added by device, we cannot control it)
-   mac.raw -> data
-   data -> data
+   mac_header -> data
+   data       -> data
 
    We should set nh.raw on output to correct posistion,
    packet classifier depends on it.
@@ -201,7 +201,8 @@
 	struct packet_type	prot_hook;
 	spinlock_t		bind_lock;
 	unsigned int		running:1,	/* prot_hook is attached*/
-				auxdata:1;
+				auxdata:1,
+				origdev:1;
 	int			ifindex;	/* bound device		*/
 	__be16			num;
 #ifdef CONFIG_PACKET_MULTICAST
@@ -284,7 +285,7 @@
 	 *	Incoming packets have ll header pulled,
 	 *	push it back.
 	 *
-	 *	For outgoing ones skb->data == skb->mac.raw
+	 *	For outgoing ones skb->data == skb_mac_header(skb)
 	 *	so that this procedure is noop.
 	 */
 
@@ -303,7 +304,7 @@
 
 	spkt = &PACKET_SKB_CB(skb)->sa.pkt;
 
-	skb_push(skb, skb->data-skb->mac.raw);
+	skb_push(skb, skb->data - skb_mac_header(skb));
 
 	/*
 	 *	The SOCK_PACKET socket receives _all_ frames.
@@ -401,14 +402,14 @@
 	 * notable one here. This should really be fixed at the driver level.
 	 */
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
-	skb->nh.raw = skb->data;
+	skb_reset_network_header(skb);
 
 	/* Try to align data part correctly */
 	if (dev->hard_header) {
 		skb->data -= dev->hard_header_len;
 		skb->tail -= dev->hard_header_len;
 		if (len < dev->hard_header_len)
-			skb->nh.raw = skb->data;
+			skb_reset_network_header(skb);
 	}
 
 	/* Returns -EFAULT on error */
@@ -488,10 +489,10 @@
 		   never delivered to user.
 		 */
 		if (sk->sk_type != SOCK_DGRAM)
-			skb_push(skb, skb->data - skb->mac.raw);
+			skb_push(skb, skb->data - skb_mac_header(skb));
 		else if (skb->pkt_type == PACKET_OUTGOING) {
 			/* Special case: outgoing packets have ll header at head */
-			skb_pull(skb, skb->nh.raw - skb->data);
+			skb_pull(skb, skb_network_offset(skb));
 		}
 	}
 
@@ -528,7 +529,10 @@
 	sll->sll_hatype = dev->type;
 	sll->sll_protocol = skb->protocol;
 	sll->sll_pkttype = skb->pkt_type;
-	sll->sll_ifindex = dev->ifindex;
+	if (unlikely(po->origdev) && skb->pkt_type == PACKET_HOST)
+		sll->sll_ifindex = orig_dev->ifindex;
+	else
+		sll->sll_ifindex = dev->ifindex;
 	sll->sll_halen = 0;
 
 	if (dev->hard_header_parse)
@@ -582,6 +586,7 @@
 	unsigned long status = TP_STATUS_LOSING|TP_STATUS_USER;
 	unsigned short macoff, netoff;
 	struct sk_buff *copy_skb = NULL;
+	struct timeval tv;
 
 	if (skb->pkt_type == PACKET_LOOPBACK)
 		goto drop;
@@ -591,10 +596,10 @@
 
 	if (dev->hard_header) {
 		if (sk->sk_type != SOCK_DGRAM)
-			skb_push(skb, skb->data - skb->mac.raw);
+			skb_push(skb, skb->data - skb_mac_header(skb));
 		else if (skb->pkt_type == PACKET_OUTGOING) {
 			/* Special case: outgoing packets have ll header at head */
-			skb_pull(skb, skb->nh.raw - skb->data);
+			skb_pull(skb, skb_network_offset(skb));
 		}
 	}
 
@@ -612,7 +617,7 @@
 	if (sk->sk_type == SOCK_DGRAM) {
 		macoff = netoff = TPACKET_ALIGN(TPACKET_HDRLEN) + 16;
 	} else {
-		unsigned maclen = skb->nh.raw - skb->data;
+		unsigned maclen = skb_network_offset(skb);
 		netoff = TPACKET_ALIGN(TPACKET_HDRLEN + (maclen < 16 ? 16 : maclen));
 		macoff = netoff - maclen;
 	}
@@ -656,12 +661,13 @@
 	h->tp_snaplen = snaplen;
 	h->tp_mac = macoff;
 	h->tp_net = netoff;
-	if (skb->tstamp.off_sec == 0) {
+	if (skb->tstamp.tv64 == 0) {
 		__net_timestamp(skb);
 		sock_enable_timestamp(sk);
 	}
-	h->tp_sec = skb->tstamp.off_sec;
-	h->tp_usec = skb->tstamp.off_usec;
+	tv = ktime_to_timeval(skb->tstamp);
+	h->tp_sec = tv.tv_sec;
+	h->tp_usec = tv.tv_usec;
 
 	sll = (struct sockaddr_ll*)((u8*)h + TPACKET_ALIGN(sizeof(*h)));
 	sll->sll_halen = 0;
@@ -671,7 +677,10 @@
 	sll->sll_hatype = dev->type;
 	sll->sll_protocol = skb->protocol;
 	sll->sll_pkttype = skb->pkt_type;
-	sll->sll_ifindex = dev->ifindex;
+	if (unlikely(po->origdev) && skb->pkt_type == PACKET_HOST)
+		sll->sll_ifindex = orig_dev->ifindex;
+	else
+		sll->sll_ifindex = dev->ifindex;
 
 	h->tp_status = status;
 	smp_mb();
@@ -766,14 +775,14 @@
 		goto out_unlock;
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
-	skb->nh.raw = skb->data;
+	skb_reset_network_header(skb);
 
 	if (dev->hard_header) {
 		int res;
 		err = -EINVAL;
 		res = dev->hard_header(skb, dev, ntohs(proto), addr, NULL, len);
 		if (sock->type != SOCK_DGRAM) {
-			skb->tail = skb->data;
+			skb_reset_tail_pointer(skb);
 			skb->len = 0;
 		} else if (res < 0)
 			goto out_free;
@@ -1143,7 +1152,7 @@
 		aux.tp_len = PACKET_SKB_CB(skb)->origlen;
 		aux.tp_snaplen = skb->len;
 		aux.tp_mac = 0;
-		aux.tp_net = skb->nh.raw - skb->data;
+		aux.tp_net = skb_network_offset(skb);
 
 		put_cmsg(msg, SOL_PACKET, PACKET_AUXDATA, sizeof(aux), &aux);
 	}
@@ -1411,6 +1420,18 @@
 		po->auxdata = !!val;
 		return 0;
 	}
+	case PACKET_ORIGDEV:
+	{
+		int val;
+
+		if (optlen < sizeof(val))
+			return -EINVAL;
+		if (copy_from_user(&val, optval, sizeof(val)))
+			return -EFAULT;
+
+		po->origdev = !!val;
+		return 0;
+	}
 	default:
 		return -ENOPROTOOPT;
 	}
@@ -1454,6 +1475,13 @@
 
 		data = &val;
 		break;
+	case PACKET_ORIGDEV:
+		if (len > sizeof(int))
+			len = sizeof(int);
+		val = po->origdev;
+
+		data = &val;
+		break;
 	default:
 		return -ENOPROTOOPT;
 	}
@@ -1543,6 +1571,8 @@
 		}
 		case SIOCGSTAMP:
 			return sock_get_timestamp(sk, (struct timeval __user *)arg);
+		case SIOCGSTAMPNS:
+			return sock_get_timestampns(sk, (struct timespec __user *)arg);
 
 #ifdef CONFIG_INET
 		case SIOCADDRT:
diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c
index f92d531..d476c43 100644
--- a/net/rose/af_rose.c
+++ b/net/rose/af_rose.c
@@ -812,26 +812,26 @@
 	 * closed.
 	 */
 	if (sk->sk_state == TCP_SYN_SENT) {
-		struct task_struct *tsk = current;
-		DECLARE_WAITQUEUE(wait, tsk);
+		DEFINE_WAIT(wait);
 
-		add_wait_queue(sk->sk_sleep, &wait);
 		for (;;) {
-			set_current_state(TASK_INTERRUPTIBLE);
+			prepare_to_wait(sk->sk_sleep, &wait,
+			                TASK_INTERRUPTIBLE);
 			if (sk->sk_state != TCP_SYN_SENT)
 				break;
-			release_sock(sk);
-			if (!signal_pending(tsk)) {
+			if (!signal_pending(current)) {
+				release_sock(sk);
 				schedule();
 				lock_sock(sk);
 				continue;
 			}
-			current->state = TASK_RUNNING;
-			remove_wait_queue(sk->sk_sleep, &wait);
-			return -ERESTARTSYS;
+			err = -ERESTARTSYS;
+			break;
 		}
-		current->state = TASK_RUNNING;
-		remove_wait_queue(sk->sk_sleep, &wait);
+		finish_wait(sk->sk_sleep, &wait);
+
+		if (err)
+			goto out_release;
 	}
 
 	if (sk->sk_state != TCP_ESTABLISHED) {
@@ -856,10 +856,9 @@
 
 static int rose_accept(struct socket *sock, struct socket *newsock, int flags)
 {
-	struct task_struct *tsk = current;
-	DECLARE_WAITQUEUE(wait, tsk);
 	struct sk_buff *skb;
 	struct sock *newsk;
+	DEFINE_WAIT(wait);
 	struct sock *sk;
 	int err = 0;
 
@@ -869,42 +868,41 @@
 	lock_sock(sk);
 	if (sk->sk_type != SOCK_SEQPACKET) {
 		err = -EOPNOTSUPP;
-		goto out;
+		goto out_release;
 	}
 
 	if (sk->sk_state != TCP_LISTEN) {
 		err = -EINVAL;
-		goto out;
+		goto out_release;
 	}
 
 	/*
 	 *	The write queue this time is holding sockets ready to use
 	 *	hooked into the SABM we saved
 	 */
-	add_wait_queue(sk->sk_sleep, &wait);
 	for (;;) {
+		prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
+
 		skb = skb_dequeue(&sk->sk_receive_queue);
 		if (skb)
 			break;
 
-		current->state = TASK_INTERRUPTIBLE;
-		release_sock(sk);
 		if (flags & O_NONBLOCK) {
-			current->state = TASK_RUNNING;
-			remove_wait_queue(sk->sk_sleep, &wait);
-			return -EWOULDBLOCK;
+			err = -EWOULDBLOCK;
+			break;
 		}
-		if (!signal_pending(tsk)) {
+		if (!signal_pending(current)) {
+			release_sock(sk);
 			schedule();
 			lock_sock(sk);
 			continue;
 		}
-		current->state = TASK_RUNNING;
-		remove_wait_queue(sk->sk_sleep, &wait);
-		return -ERESTARTSYS;
+		err = -ERESTARTSYS;
+		break;
 	}
-	current->state = TASK_RUNNING;
-	remove_wait_queue(sk->sk_sleep, &wait);
+	finish_wait(sk->sk_sleep, &wait);
+	if (err)
+		goto out_release;
 
 	newsk = skb->sk;
 	newsk->sk_socket = newsock;
@@ -916,7 +914,7 @@
 	sk->sk_ack_backlog--;
 	newsock->sk = newsk;
 
-out:
+out_release:
 	release_sock(sk);
 
 	return err;
@@ -1105,9 +1103,10 @@
 	 */
 	SOCK_DEBUG(sk, "ROSE: Appending user data\n");
 
-	asmptr = skb->h.raw = skb_put(skb, len);
+	skb_reset_transport_header(skb);
+	skb_put(skb, len);
 
-	err = memcpy_fromiovec(asmptr, msg->msg_iov, len);
+	err = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len);
 	if (err) {
 		kfree_skb(skb);
 		return err;
@@ -1155,7 +1154,7 @@
 		int lg;
 
 		/* Save a copy of the Header */
-		memcpy(header, skb->data, ROSE_MIN_LEN);
+		skb_copy_from_linear_data(skb, header, ROSE_MIN_LEN);
 		skb_pull(skb, ROSE_MIN_LEN);
 
 		frontlen = skb_headroom(skb);
@@ -1175,12 +1174,12 @@
 			lg = (ROSE_PACLEN > skb->len) ? skb->len : ROSE_PACLEN;
 
 			/* Copy the user data */
-			memcpy(skb_put(skbn, lg), skb->data, lg);
+			skb_copy_from_linear_data(skb, skb_put(skbn, lg), lg);
 			skb_pull(skb, lg);
 
 			/* Duplicate the Header */
 			skb_push(skbn, ROSE_MIN_LEN);
-			memcpy(skbn->data, header, ROSE_MIN_LEN);
+			skb_copy_to_linear_data(skbn, header, ROSE_MIN_LEN);
 
 			if (skb->len > 0)
 				skbn->data[2] |= M_BIT;
@@ -1234,7 +1233,7 @@
 		*asmptr = qbit;
 	}
 
-	skb->h.raw = skb->data;
+	skb_reset_transport_header(skb);
 	copied     = skb->len;
 
 	if (copied > size) {
@@ -1296,6 +1295,9 @@
 	case SIOCGSTAMP:
 		return sock_get_timestamp(sk, (struct timeval __user *) argp);
 
+	case SIOCGSTAMPNS:
+		return sock_get_timestampns(sk, (struct timespec __user *) argp);
+
 	case SIOCGIFADDR:
 	case SIOCSIFADDR:
 	case SIOCGIFDSTADDR:
diff --git a/net/rose/rose_loopback.c b/net/rose/rose_loopback.c
index 3e41bd9..cd01642 100644
--- a/net/rose/rose_loopback.c
+++ b/net/rose/rose_loopback.c
@@ -77,7 +77,7 @@
 		dest      = (rose_address *)(skb->data + 4);
 		lci_o     = 0xFFF - lci_i;
 
-		skb->h.raw = skb->data;
+		skb_reset_transport_header(skb);
 
 		sk = rose_find_socket(lci_o, &rose_loopback_neigh);
 		if (sk) {
diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c
index a1233e1..1f9aefd 100644
--- a/net/rose/rose_route.c
+++ b/net/rose/rose_route.c
@@ -906,7 +906,7 @@
 			}
 		}
 		else {
-			skb->h.raw = skb->data;
+			skb_reset_transport_header(skb);
 			res = rose_process_rx_frame(sk, skb);
 			goto out;
 		}
diff --git a/net/rxrpc/Kconfig b/net/rxrpc/Kconfig
new file mode 100644
index 0000000..8750f6d
--- /dev/null
+++ b/net/rxrpc/Kconfig
@@ -0,0 +1,42 @@
+#
+# RxRPC session sockets
+#
+
+config AF_RXRPC
+	tristate "RxRPC session sockets"
+	depends on EXPERIMENTAL
+	help
+	  Say Y or M here to include support for RxRPC session sockets (just
+	  the transport part, not the presentation part: (un)marshalling is
+	  left to the application).
+
+	  These are used for AFS kernel filesystem and userspace utilities.
+
+	  This module at the moment only supports client operations and is
+	  currently incomplete.
+
+	  See Documentation/networking/rxrpc.txt.
+
+
+config AF_RXRPC_DEBUG
+	bool "RxRPC dynamic debugging"
+	depends on AF_RXRPC
+	help
+	  Say Y here to make runtime controllable debugging messages appear.
+
+	  See Documentation/networking/rxrpc.txt.
+
+
+config RXKAD
+	tristate "RxRPC Kerberos security"
+	depends on AF_RXRPC && KEYS
+	select CRYPTO
+	select CRYPTO_MANAGER
+	select CRYPTO_BLKCIPHER
+	select CRYPTO_PCBC
+	select CRYPTO_FCRYPT
+	help
+	  Provide kerberos 4 and AFS kaserver security handling for AF_RXRPC
+	  through the use of the key retention service.
+
+	  See Documentation/networking/rxrpc.txt.
diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile
index 6efcb6f..c46867c 100644
--- a/net/rxrpc/Makefile
+++ b/net/rxrpc/Makefile
@@ -1,25 +1,29 @@
 #
-# Makefile for Linux kernel Rx RPC
+# Makefile for Linux kernel RxRPC
 #
 
-#CFLAGS += -finstrument-functions
-
-rxrpc-objs := \
-	call.o \
-	connection.o \
-	krxiod.o \
-	krxsecd.o \
-	krxtimod.o \
-	main.o \
-	peer.o \
-	rxrpc_syms.o \
-	transport.o
+af-rxrpc-objs := \
+	af_rxrpc.o \
+	ar-accept.o \
+	ar-ack.o \
+	ar-call.o \
+	ar-connection.o \
+	ar-connevent.o \
+	ar-error.o \
+	ar-input.o \
+	ar-key.o \
+	ar-local.o \
+	ar-output.o \
+	ar-peer.o \
+	ar-recvmsg.o \
+	ar-security.o \
+	ar-skbuff.o \
+	ar-transport.o
 
 ifeq ($(CONFIG_PROC_FS),y)
-rxrpc-objs += proc.o
-endif
-ifeq ($(CONFIG_SYSCTL),y)
-rxrpc-objs += sysctl.o
+af-rxrpc-objs += ar-proc.o
 endif
 
-obj-$(CONFIG_RXRPC) := rxrpc.o
+obj-$(CONFIG_AF_RXRPC) += af-rxrpc.o
+
+obj-$(CONFIG_RXKAD) += rxkad.o
diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c
new file mode 100644
index 0000000..2c57df9
--- /dev/null
+++ b/net/rxrpc/af_rxrpc.c
@@ -0,0 +1,879 @@
+/* AF_RXRPC implementation
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <net/sock.h>
+#include <net/af_rxrpc.h>
+#include "ar-internal.h"
+
+MODULE_DESCRIPTION("RxRPC network protocol");
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NETPROTO(PF_RXRPC);
+
+unsigned rxrpc_debug; // = RXRPC_DEBUG_KPROTO;
+module_param_named(debug, rxrpc_debug, uint, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(rxrpc_debug, "RxRPC debugging mask");
+
+static int sysctl_rxrpc_max_qlen __read_mostly = 10;
+
+static struct proto rxrpc_proto;
+static const struct proto_ops rxrpc_rpc_ops;
+
+/* local epoch for detecting local-end reset */
+__be32 rxrpc_epoch;
+
+/* current debugging ID */
+atomic_t rxrpc_debug_id;
+
+/* count of skbs currently in use */
+atomic_t rxrpc_n_skbs;
+
+struct workqueue_struct *rxrpc_workqueue;
+
+static void rxrpc_sock_destructor(struct sock *);
+
+/*
+ * see if an RxRPC socket is currently writable
+ */
+static inline int rxrpc_writable(struct sock *sk)
+{
+	return atomic_read(&sk->sk_wmem_alloc) < (size_t) sk->sk_sndbuf;
+}
+
+/*
+ * wait for write bufferage to become available
+ */
+static void rxrpc_write_space(struct sock *sk)
+{
+	_enter("%p", sk);
+	read_lock(&sk->sk_callback_lock);
+	if (rxrpc_writable(sk)) {
+		if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))
+			wake_up_interruptible(sk->sk_sleep);
+		sk_wake_async(sk, 2, POLL_OUT);
+	}
+	read_unlock(&sk->sk_callback_lock);
+}
+
+/*
+ * validate an RxRPC address
+ */
+static int rxrpc_validate_address(struct rxrpc_sock *rx,
+				  struct sockaddr_rxrpc *srx,
+				  int len)
+{
+	if (len < sizeof(struct sockaddr_rxrpc))
+		return -EINVAL;
+
+	if (srx->srx_family != AF_RXRPC)
+		return -EAFNOSUPPORT;
+
+	if (srx->transport_type != SOCK_DGRAM)
+		return -ESOCKTNOSUPPORT;
+
+	len -= offsetof(struct sockaddr_rxrpc, transport);
+	if (srx->transport_len < sizeof(sa_family_t) ||
+	    srx->transport_len > len)
+		return -EINVAL;
+
+	if (srx->transport.family != rx->proto)
+		return -EAFNOSUPPORT;
+
+	switch (srx->transport.family) {
+	case AF_INET:
+		_debug("INET: %x @ %u.%u.%u.%u",
+		       ntohs(srx->transport.sin.sin_port),
+		       NIPQUAD(srx->transport.sin.sin_addr));
+		if (srx->transport_len > 8)
+			memset((void *)&srx->transport + 8, 0,
+			       srx->transport_len - 8);
+		break;
+
+	case AF_INET6:
+	default:
+		return -EAFNOSUPPORT;
+	}
+
+	return 0;
+}
+
+/*
+ * bind a local address to an RxRPC socket
+ */
+static int rxrpc_bind(struct socket *sock, struct sockaddr *saddr, int len)
+{
+	struct sockaddr_rxrpc *srx = (struct sockaddr_rxrpc *) saddr;
+	struct sock *sk = sock->sk;
+	struct rxrpc_local *local;
+	struct rxrpc_sock *rx = rxrpc_sk(sk), *prx;
+	__be16 service_id;
+	int ret;
+
+	_enter("%p,%p,%d", rx, saddr, len);
+
+	ret = rxrpc_validate_address(rx, srx, len);
+	if (ret < 0)
+		goto error;
+
+	lock_sock(&rx->sk);
+
+	if (rx->sk.sk_state != RXRPC_UNCONNECTED) {
+		ret = -EINVAL;
+		goto error_unlock;
+	}
+
+	memcpy(&rx->srx, srx, sizeof(rx->srx));
+
+	/* find a local transport endpoint if we don't have one already */
+	local = rxrpc_lookup_local(&rx->srx);
+	if (IS_ERR(local)) {
+		ret = PTR_ERR(local);
+		goto error_unlock;
+	}
+
+	rx->local = local;
+	if (srx->srx_service) {
+		service_id = htons(srx->srx_service);
+		write_lock_bh(&local->services_lock);
+		list_for_each_entry(prx, &local->services, listen_link) {
+			if (prx->service_id == service_id)
+				goto service_in_use;
+		}
+
+		rx->service_id = service_id;
+		list_add_tail(&rx->listen_link, &local->services);
+		write_unlock_bh(&local->services_lock);
+
+		rx->sk.sk_state = RXRPC_SERVER_BOUND;
+	} else {
+		rx->sk.sk_state = RXRPC_CLIENT_BOUND;
+	}
+
+	release_sock(&rx->sk);
+	_leave(" = 0");
+	return 0;
+
+service_in_use:
+	ret = -EADDRINUSE;
+	write_unlock_bh(&local->services_lock);
+error_unlock:
+	release_sock(&rx->sk);
+error:
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * set the number of pending calls permitted on a listening socket
+ */
+static int rxrpc_listen(struct socket *sock, int backlog)
+{
+	struct sock *sk = sock->sk;
+	struct rxrpc_sock *rx = rxrpc_sk(sk);
+	int ret;
+
+	_enter("%p,%d", rx, backlog);
+
+	lock_sock(&rx->sk);
+
+	switch (rx->sk.sk_state) {
+	case RXRPC_UNCONNECTED:
+		ret = -EADDRNOTAVAIL;
+		break;
+	case RXRPC_CLIENT_BOUND:
+	case RXRPC_CLIENT_CONNECTED:
+	default:
+		ret = -EBUSY;
+		break;
+	case RXRPC_SERVER_BOUND:
+		ASSERT(rx->local != NULL);
+		sk->sk_max_ack_backlog = backlog;
+		rx->sk.sk_state = RXRPC_SERVER_LISTENING;
+		ret = 0;
+		break;
+	}
+
+	release_sock(&rx->sk);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * find a transport by address
+ */
+static struct rxrpc_transport *rxrpc_name_to_transport(struct socket *sock,
+						       struct sockaddr *addr,
+						       int addr_len, int flags,
+						       gfp_t gfp)
+{
+	struct sockaddr_rxrpc *srx = (struct sockaddr_rxrpc *) addr;
+	struct rxrpc_transport *trans;
+	struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
+	struct rxrpc_peer *peer;
+
+	_enter("%p,%p,%d,%d", rx, addr, addr_len, flags);
+
+	ASSERT(rx->local != NULL);
+	ASSERT(rx->sk.sk_state > RXRPC_UNCONNECTED);
+
+	if (rx->srx.transport_type != srx->transport_type)
+		return ERR_PTR(-ESOCKTNOSUPPORT);
+	if (rx->srx.transport.family != srx->transport.family)
+		return ERR_PTR(-EAFNOSUPPORT);
+
+	/* find a remote transport endpoint from the local one */
+	peer = rxrpc_get_peer(srx, gfp);
+	if (IS_ERR(peer))
+		return ERR_PTR(PTR_ERR(peer));
+
+	/* find a transport */
+	trans = rxrpc_get_transport(rx->local, peer, gfp);
+	rxrpc_put_peer(peer);
+	_leave(" = %p", trans);
+	return trans;
+}
+
+/**
+ * rxrpc_kernel_begin_call - Allow a kernel service to begin a call
+ * @sock: The socket on which to make the call
+ * @srx: The address of the peer to contact (defaults to socket setting)
+ * @key: The security context to use (defaults to socket setting)
+ * @user_call_ID: The ID to use
+ *
+ * Allow a kernel service to begin a call on the nominated socket.  This just
+ * sets up all the internal tracking structures and allocates connection and
+ * call IDs as appropriate.  The call to be used is returned.
+ *
+ * The default socket destination address and security may be overridden by
+ * supplying @srx and @key.
+ */
+struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock,
+					   struct sockaddr_rxrpc *srx,
+					   struct key *key,
+					   unsigned long user_call_ID,
+					   gfp_t gfp)
+{
+	struct rxrpc_conn_bundle *bundle;
+	struct rxrpc_transport *trans;
+	struct rxrpc_call *call;
+	struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
+	__be16 service_id;
+
+	_enter(",,%x,%lx", key_serial(key), user_call_ID);
+
+	lock_sock(&rx->sk);
+
+	if (srx) {
+		trans = rxrpc_name_to_transport(sock, (struct sockaddr *) srx,
+						sizeof(*srx), 0, gfp);
+		if (IS_ERR(trans)) {
+			call = ERR_PTR(PTR_ERR(trans));
+			trans = NULL;
+			goto out;
+		}
+	} else {
+		trans = rx->trans;
+		if (!trans) {
+			call = ERR_PTR(-ENOTCONN);
+			goto out;
+		}
+		atomic_inc(&trans->usage);
+	}
+
+	service_id = rx->service_id;
+	if (srx)
+		service_id = htons(srx->srx_service);
+
+	if (!key)
+		key = rx->key;
+	if (key && !key->payload.data)
+		key = NULL; /* a no-security key */
+
+	bundle = rxrpc_get_bundle(rx, trans, key, service_id, gfp);
+	if (IS_ERR(bundle)) {
+		call = ERR_PTR(PTR_ERR(bundle));
+		goto out;
+	}
+
+	call = rxrpc_get_client_call(rx, trans, bundle, user_call_ID, true,
+				     gfp);
+	rxrpc_put_bundle(trans, bundle);
+out:
+	rxrpc_put_transport(trans);
+	release_sock(&rx->sk);
+	_leave(" = %p", call);
+	return call;
+}
+
+EXPORT_SYMBOL(rxrpc_kernel_begin_call);
+
+/**
+ * rxrpc_kernel_end_call - Allow a kernel service to end a call it was using
+ * @call: The call to end
+ *
+ * Allow a kernel service to end a call it was using.  The call must be
+ * complete before this is called (the call should be aborted if necessary).
+ */
+void rxrpc_kernel_end_call(struct rxrpc_call *call)
+{
+	_enter("%d{%d}", call->debug_id, atomic_read(&call->usage));
+	rxrpc_remove_user_ID(call->socket, call);
+	rxrpc_put_call(call);
+}
+
+EXPORT_SYMBOL(rxrpc_kernel_end_call);
+
+/**
+ * rxrpc_kernel_intercept_rx_messages - Intercept received RxRPC messages
+ * @sock: The socket to intercept received messages on
+ * @interceptor: The function to pass the messages to
+ *
+ * Allow a kernel service to intercept messages heading for the Rx queue on an
+ * RxRPC socket.  They get passed to the specified function instead.
+ * @interceptor should free the socket buffers it is given.  @interceptor is
+ * called with the socket receive queue spinlock held and softirqs disabled -
+ * this ensures that the messages will be delivered in the right order.
+ */
+void rxrpc_kernel_intercept_rx_messages(struct socket *sock,
+					rxrpc_interceptor_t interceptor)
+{
+	struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
+
+	_enter("");
+	rx->interceptor = interceptor;
+}
+
+EXPORT_SYMBOL(rxrpc_kernel_intercept_rx_messages);
+
+/*
+ * connect an RxRPC socket
+ * - this just targets it at a specific destination; no actual connection
+ *   negotiation takes place
+ */
+static int rxrpc_connect(struct socket *sock, struct sockaddr *addr,
+			 int addr_len, int flags)
+{
+	struct sockaddr_rxrpc *srx = (struct sockaddr_rxrpc *) addr;
+	struct sock *sk = sock->sk;
+	struct rxrpc_transport *trans;
+	struct rxrpc_local *local;
+	struct rxrpc_sock *rx = rxrpc_sk(sk);
+	int ret;
+
+	_enter("%p,%p,%d,%d", rx, addr, addr_len, flags);
+
+	ret = rxrpc_validate_address(rx, srx, addr_len);
+	if (ret < 0) {
+		_leave(" = %d [bad addr]", ret);
+		return ret;
+	}
+
+	lock_sock(&rx->sk);
+
+	switch (rx->sk.sk_state) {
+	case RXRPC_UNCONNECTED:
+		/* find a local transport endpoint if we don't have one already */
+		ASSERTCMP(rx->local, ==, NULL);
+		rx->srx.srx_family = AF_RXRPC;
+		rx->srx.srx_service = 0;
+		rx->srx.transport_type = srx->transport_type;
+		rx->srx.transport_len = sizeof(sa_family_t);
+		rx->srx.transport.family = srx->transport.family;
+		local = rxrpc_lookup_local(&rx->srx);
+		if (IS_ERR(local)) {
+			release_sock(&rx->sk);
+			return PTR_ERR(local);
+		}
+		rx->local = local;
+		rx->sk.sk_state = RXRPC_CLIENT_BOUND;
+	case RXRPC_CLIENT_BOUND:
+		break;
+	case RXRPC_CLIENT_CONNECTED:
+		release_sock(&rx->sk);
+		return -EISCONN;
+	default:
+		release_sock(&rx->sk);
+		return -EBUSY; /* server sockets can't connect as well */
+	}
+
+	trans = rxrpc_name_to_transport(sock, addr, addr_len, flags,
+					GFP_KERNEL);
+	if (IS_ERR(trans)) {
+		release_sock(&rx->sk);
+		_leave(" = %ld", PTR_ERR(trans));
+		return PTR_ERR(trans);
+	}
+
+	rx->trans = trans;
+	rx->service_id = htons(srx->srx_service);
+	rx->sk.sk_state = RXRPC_CLIENT_CONNECTED;
+
+	release_sock(&rx->sk);
+	return 0;
+}
+
+/*
+ * send a message through an RxRPC socket
+ * - in a client this does a number of things:
+ *   - finds/sets up a connection for the security specified (if any)
+ *   - initiates a call (ID in control data)
+ *   - ends the request phase of a call (if MSG_MORE is not set)
+ *   - sends a call data packet
+ *   - may send an abort (abort code in control data)
+ */
+static int rxrpc_sendmsg(struct kiocb *iocb, struct socket *sock,
+			 struct msghdr *m, size_t len)
+{
+	struct rxrpc_transport *trans;
+	struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
+	int ret;
+
+	_enter(",{%d},,%zu", rx->sk.sk_state, len);
+
+	if (m->msg_flags & MSG_OOB)
+		return -EOPNOTSUPP;
+
+	if (m->msg_name) {
+		ret = rxrpc_validate_address(rx, m->msg_name, m->msg_namelen);
+		if (ret < 0) {
+			_leave(" = %d [bad addr]", ret);
+			return ret;
+		}
+	}
+
+	trans = NULL;
+	lock_sock(&rx->sk);
+
+	if (m->msg_name) {
+		ret = -EISCONN;
+		trans = rxrpc_name_to_transport(sock, m->msg_name,
+						m->msg_namelen, 0, GFP_KERNEL);
+		if (IS_ERR(trans)) {
+			ret = PTR_ERR(trans);
+			trans = NULL;
+			goto out;
+		}
+	} else {
+		trans = rx->trans;
+		if (trans)
+			atomic_inc(&trans->usage);
+	}
+
+	switch (rx->sk.sk_state) {
+	case RXRPC_SERVER_LISTENING:
+		if (!m->msg_name) {
+			ret = rxrpc_server_sendmsg(iocb, rx, m, len);
+			break;
+		}
+	case RXRPC_SERVER_BOUND:
+	case RXRPC_CLIENT_BOUND:
+		if (!m->msg_name) {
+			ret = -ENOTCONN;
+			break;
+		}
+	case RXRPC_CLIENT_CONNECTED:
+		ret = rxrpc_client_sendmsg(iocb, rx, trans, m, len);
+		break;
+	default:
+		ret = -ENOTCONN;
+		break;
+	}
+
+out:
+	release_sock(&rx->sk);
+	if (trans)
+		rxrpc_put_transport(trans);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * set RxRPC socket options
+ */
+static int rxrpc_setsockopt(struct socket *sock, int level, int optname,
+			    char __user *optval, int optlen)
+{
+	struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
+	unsigned min_sec_level;
+	int ret;
+
+	_enter(",%d,%d,,%d", level, optname, optlen);
+
+	lock_sock(&rx->sk);
+	ret = -EOPNOTSUPP;
+
+	if (level == SOL_RXRPC) {
+		switch (optname) {
+		case RXRPC_EXCLUSIVE_CONNECTION:
+			ret = -EINVAL;
+			if (optlen != 0)
+				goto error;
+			ret = -EISCONN;
+			if (rx->sk.sk_state != RXRPC_UNCONNECTED)
+				goto error;
+			set_bit(RXRPC_SOCK_EXCLUSIVE_CONN, &rx->flags);
+			goto success;
+
+		case RXRPC_SECURITY_KEY:
+			ret = -EINVAL;
+			if (rx->key)
+				goto error;
+			ret = -EISCONN;
+			if (rx->sk.sk_state != RXRPC_UNCONNECTED)
+				goto error;
+			ret = rxrpc_request_key(rx, optval, optlen);
+			goto error;
+
+		case RXRPC_SECURITY_KEYRING:
+			ret = -EINVAL;
+			if (rx->key)
+				goto error;
+			ret = -EISCONN;
+			if (rx->sk.sk_state != RXRPC_UNCONNECTED)
+				goto error;
+			ret = rxrpc_server_keyring(rx, optval, optlen);
+			goto error;
+
+		case RXRPC_MIN_SECURITY_LEVEL:
+			ret = -EINVAL;
+			if (optlen != sizeof(unsigned))
+				goto error;
+			ret = -EISCONN;
+			if (rx->sk.sk_state != RXRPC_UNCONNECTED)
+				goto error;
+			ret = get_user(min_sec_level,
+				       (unsigned __user *) optval);
+			if (ret < 0)
+				goto error;
+			ret = -EINVAL;
+			if (min_sec_level > RXRPC_SECURITY_MAX)
+				goto error;
+			rx->min_sec_level = min_sec_level;
+			goto success;
+
+		default:
+			break;
+		}
+	}
+
+success:
+	ret = 0;
+error:
+	release_sock(&rx->sk);
+	return ret;
+}
+
+/*
+ * permit an RxRPC socket to be polled
+ */
+static unsigned int rxrpc_poll(struct file *file, struct socket *sock,
+			       poll_table *wait)
+{
+	unsigned int mask;
+	struct sock *sk = sock->sk;
+
+	poll_wait(file, sk->sk_sleep, wait);
+	mask = 0;
+
+	/* the socket is readable if there are any messages waiting on the Rx
+	 * queue */
+	if (!skb_queue_empty(&sk->sk_receive_queue))
+		mask |= POLLIN | POLLRDNORM;
+
+	/* the socket is writable if there is space to add new data to the
+	 * socket; there is no guarantee that any particular call in progress
+	 * on the socket may have space in the Tx ACK window */
+	if (rxrpc_writable(sk))
+		mask |= POLLOUT | POLLWRNORM;
+
+	return mask;
+}
+
+/*
+ * create an RxRPC socket
+ */
+static int rxrpc_create(struct socket *sock, int protocol)
+{
+	struct rxrpc_sock *rx;
+	struct sock *sk;
+
+	_enter("%p,%d", sock, protocol);
+
+	/* we support transport protocol UDP only */
+	if (protocol != PF_INET)
+		return -EPROTONOSUPPORT;
+
+	if (sock->type != SOCK_DGRAM)
+		return -ESOCKTNOSUPPORT;
+
+	sock->ops = &rxrpc_rpc_ops;
+	sock->state = SS_UNCONNECTED;
+
+	sk = sk_alloc(PF_RXRPC, GFP_KERNEL, &rxrpc_proto, 1);
+	if (!sk)
+		return -ENOMEM;
+
+	sock_init_data(sock, sk);
+	sk->sk_state		= RXRPC_UNCONNECTED;
+	sk->sk_write_space	= rxrpc_write_space;
+	sk->sk_max_ack_backlog	= sysctl_rxrpc_max_qlen;
+	sk->sk_destruct		= rxrpc_sock_destructor;
+
+	rx = rxrpc_sk(sk);
+	rx->proto = protocol;
+	rx->calls = RB_ROOT;
+
+	INIT_LIST_HEAD(&rx->listen_link);
+	INIT_LIST_HEAD(&rx->secureq);
+	INIT_LIST_HEAD(&rx->acceptq);
+	rwlock_init(&rx->call_lock);
+	memset(&rx->srx, 0, sizeof(rx->srx));
+
+	_leave(" = 0 [%p]", rx);
+	return 0;
+}
+
+/*
+ * RxRPC socket destructor
+ */
+static void rxrpc_sock_destructor(struct sock *sk)
+{
+	_enter("%p", sk);
+
+	rxrpc_purge_queue(&sk->sk_receive_queue);
+
+	BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
+	BUG_TRAP(sk_unhashed(sk));
+	BUG_TRAP(!sk->sk_socket);
+
+	if (!sock_flag(sk, SOCK_DEAD)) {
+		printk("Attempt to release alive rxrpc socket: %p\n", sk);
+		return;
+	}
+}
+
+/*
+ * release an RxRPC socket
+ */
+static int rxrpc_release_sock(struct sock *sk)
+{
+	struct rxrpc_sock *rx = rxrpc_sk(sk);
+
+	_enter("%p{%d,%d}", sk, sk->sk_state, atomic_read(&sk->sk_refcnt));
+
+	/* declare the socket closed for business */
+	sock_orphan(sk);
+	sk->sk_shutdown = SHUTDOWN_MASK;
+
+	spin_lock_bh(&sk->sk_receive_queue.lock);
+	sk->sk_state = RXRPC_CLOSE;
+	spin_unlock_bh(&sk->sk_receive_queue.lock);
+
+	ASSERTCMP(rx->listen_link.next, !=, LIST_POISON1);
+
+	if (!list_empty(&rx->listen_link)) {
+		write_lock_bh(&rx->local->services_lock);
+		list_del(&rx->listen_link);
+		write_unlock_bh(&rx->local->services_lock);
+	}
+
+	/* try to flush out this socket */
+	rxrpc_release_calls_on_socket(rx);
+	flush_workqueue(rxrpc_workqueue);
+	rxrpc_purge_queue(&sk->sk_receive_queue);
+
+	if (rx->conn) {
+		rxrpc_put_connection(rx->conn);
+		rx->conn = NULL;
+	}
+
+	if (rx->bundle) {
+		rxrpc_put_bundle(rx->trans, rx->bundle);
+		rx->bundle = NULL;
+	}
+	if (rx->trans) {
+		rxrpc_put_transport(rx->trans);
+		rx->trans = NULL;
+	}
+	if (rx->local) {
+		rxrpc_put_local(rx->local);
+		rx->local = NULL;
+	}
+
+	key_put(rx->key);
+	rx->key = NULL;
+	key_put(rx->securities);
+	rx->securities = NULL;
+	sock_put(sk);
+
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * release an RxRPC BSD socket on close() or equivalent
+ */
+static int rxrpc_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	_enter("%p{%p}", sock, sk);
+
+	if (!sk)
+		return 0;
+
+	sock->sk = NULL;
+
+	return rxrpc_release_sock(sk);
+}
+
+/*
+ * RxRPC network protocol
+ */
+static const struct proto_ops rxrpc_rpc_ops = {
+	.family		= PF_UNIX,
+	.owner		= THIS_MODULE,
+	.release	= rxrpc_release,
+	.bind		= rxrpc_bind,
+	.connect	= rxrpc_connect,
+	.socketpair	= sock_no_socketpair,
+	.accept		= sock_no_accept,
+	.getname	= sock_no_getname,
+	.poll		= rxrpc_poll,
+	.ioctl		= sock_no_ioctl,
+	.listen		= rxrpc_listen,
+	.shutdown	= sock_no_shutdown,
+	.setsockopt	= rxrpc_setsockopt,
+	.getsockopt	= sock_no_getsockopt,
+	.sendmsg	= rxrpc_sendmsg,
+	.recvmsg	= rxrpc_recvmsg,
+	.mmap		= sock_no_mmap,
+	.sendpage	= sock_no_sendpage,
+};
+
+static struct proto rxrpc_proto = {
+	.name		= "RXRPC",
+	.owner		= THIS_MODULE,
+	.obj_size	= sizeof(struct rxrpc_sock),
+	.max_header	= sizeof(struct rxrpc_header),
+};
+
+static struct net_proto_family rxrpc_family_ops = {
+	.family	= PF_RXRPC,
+	.create = rxrpc_create,
+	.owner	= THIS_MODULE,
+};
+
+/*
+ * initialise and register the RxRPC protocol
+ */
+static int __init af_rxrpc_init(void)
+{
+	struct sk_buff *dummy_skb;
+	int ret = -1;
+
+	BUILD_BUG_ON(sizeof(struct rxrpc_skb_priv) > sizeof(dummy_skb->cb));
+
+	rxrpc_epoch = htonl(xtime.tv_sec);
+
+	ret = -ENOMEM;
+	rxrpc_call_jar = kmem_cache_create(
+		"rxrpc_call_jar", sizeof(struct rxrpc_call), 0,
+		SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (!rxrpc_call_jar) {
+		printk(KERN_NOTICE "RxRPC: Failed to allocate call jar\n");
+		goto error_call_jar;
+	}
+
+	rxrpc_workqueue = create_workqueue("krxrpcd");
+	if (!rxrpc_workqueue) {
+		printk(KERN_NOTICE "RxRPC: Failed to allocate work queue\n");
+		goto error_work_queue;
+	}
+
+	ret = proto_register(&rxrpc_proto, 1);
+        if (ret < 0) {
+                printk(KERN_CRIT "RxRPC: Cannot register protocol\n");
+		goto error_proto;
+	}
+
+	ret = sock_register(&rxrpc_family_ops);
+	if (ret < 0) {
+                printk(KERN_CRIT "RxRPC: Cannot register socket family\n");
+		goto error_sock;
+	}
+
+	ret = register_key_type(&key_type_rxrpc);
+	if (ret < 0) {
+                printk(KERN_CRIT "RxRPC: Cannot register client key type\n");
+		goto error_key_type;
+	}
+
+	ret = register_key_type(&key_type_rxrpc_s);
+	if (ret < 0) {
+                printk(KERN_CRIT "RxRPC: Cannot register server key type\n");
+		goto error_key_type_s;
+	}
+
+#ifdef CONFIG_PROC_FS
+	proc_net_fops_create("rxrpc_calls", 0, &rxrpc_call_seq_fops);
+	proc_net_fops_create("rxrpc_conns", 0, &rxrpc_connection_seq_fops);
+#endif
+	return 0;
+
+error_key_type_s:
+	unregister_key_type(&key_type_rxrpc);
+error_key_type:
+	sock_unregister(PF_RXRPC);
+error_sock:
+	proto_unregister(&rxrpc_proto);
+error_proto:
+	destroy_workqueue(rxrpc_workqueue);
+error_work_queue:
+	kmem_cache_destroy(rxrpc_call_jar);
+error_call_jar:
+	return ret;
+}
+
+/*
+ * unregister the RxRPC protocol
+ */
+static void __exit af_rxrpc_exit(void)
+{
+	_enter("");
+	unregister_key_type(&key_type_rxrpc_s);
+	unregister_key_type(&key_type_rxrpc);
+	sock_unregister(PF_RXRPC);
+	proto_unregister(&rxrpc_proto);
+	rxrpc_destroy_all_calls();
+	rxrpc_destroy_all_connections();
+	rxrpc_destroy_all_transports();
+	rxrpc_destroy_all_peers();
+	rxrpc_destroy_all_locals();
+
+	ASSERTCMP(atomic_read(&rxrpc_n_skbs), ==, 0);
+
+	_debug("flush scheduled work");
+	flush_workqueue(rxrpc_workqueue);
+	proc_net_remove("rxrpc_conns");
+	proc_net_remove("rxrpc_calls");
+	destroy_workqueue(rxrpc_workqueue);
+	kmem_cache_destroy(rxrpc_call_jar);
+	_leave("");
+}
+
+module_init(af_rxrpc_init);
+module_exit(af_rxrpc_exit);
diff --git a/net/rxrpc/ar-accept.c b/net/rxrpc/ar-accept.c
new file mode 100644
index 0000000..92a87fd
--- /dev/null
+++ b/net/rxrpc/ar-accept.c
@@ -0,0 +1,504 @@
+/* incoming call handling
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/errqueue.h>
+#include <linux/udp.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/icmp.h>
+#include <net/sock.h>
+#include <net/af_rxrpc.h>
+#include <net/ip.h>
+#include "ar-internal.h"
+
+/*
+ * generate a connection-level abort
+ */
+static int rxrpc_busy(struct rxrpc_local *local, struct sockaddr_rxrpc *srx,
+		      struct rxrpc_header *hdr)
+{
+	struct msghdr msg;
+	struct kvec iov[1];
+	size_t len;
+	int ret;
+
+	_enter("%d,,", local->debug_id);
+
+	msg.msg_name	= &srx->transport.sin;
+	msg.msg_namelen	= sizeof(srx->transport.sin);
+	msg.msg_control	= NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags	= 0;
+
+	hdr->seq	= 0;
+	hdr->type	= RXRPC_PACKET_TYPE_BUSY;
+	hdr->flags	= 0;
+	hdr->userStatus	= 0;
+	hdr->_rsvd	= 0;
+
+	iov[0].iov_base	= hdr;
+	iov[0].iov_len	= sizeof(*hdr);
+
+	len = iov[0].iov_len;
+
+	hdr->serial = htonl(1);
+	_proto("Tx BUSY %%%u", ntohl(hdr->serial));
+
+	ret = kernel_sendmsg(local->socket, &msg, iov, 1, len);
+	if (ret < 0) {
+		_leave(" = -EAGAIN [sendmsg failed: %d]", ret);
+		return -EAGAIN;
+	}
+
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * accept an incoming call that needs peer, transport and/or connection setting
+ * up
+ */
+static int rxrpc_accept_incoming_call(struct rxrpc_local *local,
+				      struct rxrpc_sock *rx,
+				      struct sk_buff *skb,
+				      struct sockaddr_rxrpc *srx)
+{
+	struct rxrpc_connection *conn;
+	struct rxrpc_transport *trans;
+	struct rxrpc_skb_priv *sp, *nsp;
+	struct rxrpc_peer *peer;
+	struct rxrpc_call *call;
+	struct sk_buff *notification;
+	int ret;
+
+	_enter("");
+
+	sp = rxrpc_skb(skb);
+
+	/* get a notification message to send to the server app */
+	notification = alloc_skb(0, GFP_NOFS);
+	rxrpc_new_skb(notification);
+	notification->mark = RXRPC_SKB_MARK_NEW_CALL;
+
+	peer = rxrpc_get_peer(srx, GFP_NOIO);
+	if (IS_ERR(peer)) {
+		_debug("no peer");
+		ret = -EBUSY;
+		goto error;
+	}
+
+	trans = rxrpc_get_transport(local, peer, GFP_NOIO);
+	rxrpc_put_peer(peer);
+	if (!trans) {
+		_debug("no trans");
+		ret = -EBUSY;
+		goto error;
+	}
+
+	conn = rxrpc_incoming_connection(trans, &sp->hdr, GFP_NOIO);
+	rxrpc_put_transport(trans);
+	if (IS_ERR(conn)) {
+		_debug("no conn");
+		ret = PTR_ERR(conn);
+		goto error;
+	}
+
+	call = rxrpc_incoming_call(rx, conn, &sp->hdr, GFP_NOIO);
+	rxrpc_put_connection(conn);
+	if (IS_ERR(call)) {
+		_debug("no call");
+		ret = PTR_ERR(call);
+		goto error;
+	}
+
+	/* attach the call to the socket */
+	read_lock_bh(&local->services_lock);
+	if (rx->sk.sk_state == RXRPC_CLOSE)
+		goto invalid_service;
+
+	write_lock(&rx->call_lock);
+	if (!test_and_set_bit(RXRPC_CALL_INIT_ACCEPT, &call->flags)) {
+		rxrpc_get_call(call);
+
+		spin_lock(&call->conn->state_lock);
+		if (sp->hdr.securityIndex > 0 &&
+		    call->conn->state == RXRPC_CONN_SERVER_UNSECURED) {
+			_debug("await conn sec");
+			list_add_tail(&call->accept_link, &rx->secureq);
+			call->conn->state = RXRPC_CONN_SERVER_CHALLENGING;
+			atomic_inc(&call->conn->usage);
+			set_bit(RXRPC_CONN_CHALLENGE, &call->conn->events);
+			rxrpc_queue_conn(call->conn);
+		} else {
+			_debug("conn ready");
+			call->state = RXRPC_CALL_SERVER_ACCEPTING;
+			list_add_tail(&call->accept_link, &rx->acceptq);
+			rxrpc_get_call(call);
+			nsp = rxrpc_skb(notification);
+			nsp->call = call;
+
+			ASSERTCMP(atomic_read(&call->usage), >=, 3);
+
+			_debug("notify");
+			spin_lock(&call->lock);
+			ret = rxrpc_queue_rcv_skb(call, notification, true,
+						  false);
+			spin_unlock(&call->lock);
+			notification = NULL;
+			if (ret < 0)
+				BUG();
+		}
+		spin_unlock(&call->conn->state_lock);
+
+		_debug("queued");
+	}
+	write_unlock(&rx->call_lock);
+
+	_debug("process");
+	rxrpc_fast_process_packet(call, skb);
+
+	_debug("done");
+	read_unlock_bh(&local->services_lock);
+	rxrpc_free_skb(notification);
+	rxrpc_put_call(call);
+	_leave(" = 0");
+	return 0;
+
+invalid_service:
+	_debug("invalid");
+	read_unlock_bh(&local->services_lock);
+
+	read_lock_bh(&call->state_lock);
+	if (!test_bit(RXRPC_CALL_RELEASE, &call->flags) &&
+	    !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events)) {
+		rxrpc_get_call(call);
+		rxrpc_queue_call(call);
+	}
+	read_unlock_bh(&call->state_lock);
+	rxrpc_put_call(call);
+	ret = -ECONNREFUSED;
+error:
+	rxrpc_free_skb(notification);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * accept incoming calls that need peer, transport and/or connection setting up
+ * - the packets we get are all incoming client DATA packets that have seq == 1
+ */
+void rxrpc_accept_incoming_calls(struct work_struct *work)
+{
+	struct rxrpc_local *local =
+		container_of(work, struct rxrpc_local, acceptor);
+	struct rxrpc_skb_priv *sp;
+	struct sockaddr_rxrpc srx;
+	struct rxrpc_sock *rx;
+	struct sk_buff *skb;
+	__be16 service_id;
+	int ret;
+
+	_enter("%d", local->debug_id);
+
+	read_lock_bh(&rxrpc_local_lock);
+	if (atomic_read(&local->usage) > 0)
+		rxrpc_get_local(local);
+	else
+		local = NULL;
+	read_unlock_bh(&rxrpc_local_lock);
+	if (!local) {
+		_leave(" [local dead]");
+		return;
+	}
+
+process_next_packet:
+	skb = skb_dequeue(&local->accept_queue);
+	if (!skb) {
+		rxrpc_put_local(local);
+		_leave("\n");
+		return;
+	}
+
+	_net("incoming call skb %p", skb);
+
+	sp = rxrpc_skb(skb);
+
+	/* determine the remote address */
+	memset(&srx, 0, sizeof(srx));
+	srx.srx_family = AF_RXRPC;
+	srx.transport.family = local->srx.transport.family;
+	srx.transport_type = local->srx.transport_type;
+	switch (srx.transport.family) {
+	case AF_INET:
+		srx.transport_len = sizeof(struct sockaddr_in);
+		srx.transport.sin.sin_port = udp_hdr(skb)->source;
+		srx.transport.sin.sin_addr.s_addr = ip_hdr(skb)->saddr;
+		break;
+	default:
+		goto busy;
+	}
+
+	/* get the socket providing the service */
+	service_id = sp->hdr.serviceId;
+	read_lock_bh(&local->services_lock);
+	list_for_each_entry(rx, &local->services, listen_link) {
+		if (rx->service_id == service_id &&
+		    rx->sk.sk_state != RXRPC_CLOSE)
+			goto found_service;
+	}
+	read_unlock_bh(&local->services_lock);
+	goto invalid_service;
+
+found_service:
+	_debug("found service %hd", ntohs(rx->service_id));
+	if (sk_acceptq_is_full(&rx->sk))
+		goto backlog_full;
+	sk_acceptq_added(&rx->sk);
+	sock_hold(&rx->sk);
+	read_unlock_bh(&local->services_lock);
+
+	ret = rxrpc_accept_incoming_call(local, rx, skb, &srx);
+	if (ret < 0)
+		sk_acceptq_removed(&rx->sk);
+	sock_put(&rx->sk);
+	switch (ret) {
+	case -ECONNRESET: /* old calls are ignored */
+	case -ECONNABORTED: /* aborted calls are reaborted or ignored */
+	case 0:
+		goto process_next_packet;
+	case -ECONNREFUSED:
+		goto invalid_service;
+	case -EBUSY:
+		goto busy;
+	case -EKEYREJECTED:
+		goto security_mismatch;
+	default:
+		BUG();
+	}
+
+backlog_full:
+	read_unlock_bh(&local->services_lock);
+busy:
+	rxrpc_busy(local, &srx, &sp->hdr);
+	rxrpc_free_skb(skb);
+	goto process_next_packet;
+
+invalid_service:
+	skb->priority = RX_INVALID_OPERATION;
+	rxrpc_reject_packet(local, skb);
+	goto process_next_packet;
+
+	/* can't change connection security type mid-flow */
+security_mismatch:
+	skb->priority = RX_PROTOCOL_ERROR;
+	rxrpc_reject_packet(local, skb);
+	goto process_next_packet;
+}
+
+/*
+ * handle acceptance of a call by userspace
+ * - assign the user call ID to the call at the front of the queue
+ */
+struct rxrpc_call *rxrpc_accept_call(struct rxrpc_sock *rx,
+				     unsigned long user_call_ID)
+{
+	struct rxrpc_call *call;
+	struct rb_node *parent, **pp;
+	int ret;
+
+	_enter(",%lx", user_call_ID);
+
+	ASSERT(!irqs_disabled());
+
+	write_lock(&rx->call_lock);
+
+	ret = -ENODATA;
+	if (list_empty(&rx->acceptq))
+		goto out;
+
+	/* check the user ID isn't already in use */
+	ret = -EBADSLT;
+	pp = &rx->calls.rb_node;
+	parent = NULL;
+	while (*pp) {
+		parent = *pp;
+		call = rb_entry(parent, struct rxrpc_call, sock_node);
+
+		if (user_call_ID < call->user_call_ID)
+			pp = &(*pp)->rb_left;
+		else if (user_call_ID > call->user_call_ID)
+			pp = &(*pp)->rb_right;
+		else
+			goto out;
+	}
+
+	/* dequeue the first call and check it's still valid */
+	call = list_entry(rx->acceptq.next, struct rxrpc_call, accept_link);
+	list_del_init(&call->accept_link);
+	sk_acceptq_removed(&rx->sk);
+
+	write_lock_bh(&call->state_lock);
+	switch (call->state) {
+	case RXRPC_CALL_SERVER_ACCEPTING:
+		call->state = RXRPC_CALL_SERVER_RECV_REQUEST;
+		break;
+	case RXRPC_CALL_REMOTELY_ABORTED:
+	case RXRPC_CALL_LOCALLY_ABORTED:
+		ret = -ECONNABORTED;
+		goto out_release;
+	case RXRPC_CALL_NETWORK_ERROR:
+		ret = call->conn->error;
+		goto out_release;
+	case RXRPC_CALL_DEAD:
+		ret = -ETIME;
+		goto out_discard;
+	default:
+		BUG();
+	}
+
+	/* formalise the acceptance */
+	call->user_call_ID = user_call_ID;
+	rb_link_node(&call->sock_node, parent, pp);
+	rb_insert_color(&call->sock_node, &rx->calls);
+	if (test_and_set_bit(RXRPC_CALL_HAS_USERID, &call->flags))
+		BUG();
+	if (test_and_set_bit(RXRPC_CALL_ACCEPTED, &call->events))
+		BUG();
+	rxrpc_queue_call(call);
+
+	rxrpc_get_call(call);
+	write_unlock_bh(&call->state_lock);
+	write_unlock(&rx->call_lock);
+	_leave(" = %p{%d}", call, call->debug_id);
+	return call;
+
+	/* if the call is already dying or dead, then we leave the socket's ref
+	 * on it to be released by rxrpc_dead_call_expired() as induced by
+	 * rxrpc_release_call() */
+out_release:
+	_debug("release %p", call);
+	if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) &&
+	    !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events))
+		rxrpc_queue_call(call);
+out_discard:
+	write_unlock_bh(&call->state_lock);
+	_debug("discard %p", call);
+out:
+	write_unlock(&rx->call_lock);
+	_leave(" = %d", ret);
+	return ERR_PTR(ret);
+}
+
+/*
+ * handle rejectance of a call by userspace
+ * - reject the call at the front of the queue
+ */
+int rxrpc_reject_call(struct rxrpc_sock *rx)
+{
+	struct rxrpc_call *call;
+	int ret;
+
+	_enter("");
+
+	ASSERT(!irqs_disabled());
+
+	write_lock(&rx->call_lock);
+
+	ret = -ENODATA;
+	if (list_empty(&rx->acceptq))
+		goto out;
+
+	/* dequeue the first call and check it's still valid */
+	call = list_entry(rx->acceptq.next, struct rxrpc_call, accept_link);
+	list_del_init(&call->accept_link);
+	sk_acceptq_removed(&rx->sk);
+
+	write_lock_bh(&call->state_lock);
+	switch (call->state) {
+	case RXRPC_CALL_SERVER_ACCEPTING:
+		call->state = RXRPC_CALL_SERVER_BUSY;
+		if (test_and_set_bit(RXRPC_CALL_REJECT_BUSY, &call->events))
+			rxrpc_queue_call(call);
+		ret = 0;
+		goto out_release;
+	case RXRPC_CALL_REMOTELY_ABORTED:
+	case RXRPC_CALL_LOCALLY_ABORTED:
+		ret = -ECONNABORTED;
+		goto out_release;
+	case RXRPC_CALL_NETWORK_ERROR:
+		ret = call->conn->error;
+		goto out_release;
+	case RXRPC_CALL_DEAD:
+		ret = -ETIME;
+		goto out_discard;
+	default:
+		BUG();
+	}
+
+	/* if the call is already dying or dead, then we leave the socket's ref
+	 * on it to be released by rxrpc_dead_call_expired() as induced by
+	 * rxrpc_release_call() */
+out_release:
+	_debug("release %p", call);
+	if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) &&
+	    !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events))
+		rxrpc_queue_call(call);
+out_discard:
+	write_unlock_bh(&call->state_lock);
+	_debug("discard %p", call);
+out:
+	write_unlock(&rx->call_lock);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/**
+ * rxrpc_kernel_accept_call - Allow a kernel service to accept an incoming call
+ * @sock: The socket on which the impending call is waiting
+ * @user_call_ID: The tag to attach to the call
+ *
+ * Allow a kernel service to accept an incoming call, assuming the incoming
+ * call is still valid.
+ */
+struct rxrpc_call *rxrpc_kernel_accept_call(struct socket *sock,
+					    unsigned long user_call_ID)
+{
+	struct rxrpc_call *call;
+
+	_enter(",%lx", user_call_ID);
+	call = rxrpc_accept_call(rxrpc_sk(sock->sk), user_call_ID);
+	_leave(" = %p", call);
+	return call;
+}
+
+EXPORT_SYMBOL(rxrpc_kernel_accept_call);
+
+/**
+ * rxrpc_kernel_reject_call - Allow a kernel service to reject an incoming call
+ * @sock: The socket on which the impending call is waiting
+ *
+ * Allow a kernel service to reject an incoming call with a BUSY message,
+ * assuming the incoming call is still valid.
+ */
+int rxrpc_kernel_reject_call(struct socket *sock)
+{
+	int ret;
+
+	_enter("");
+	ret = rxrpc_reject_call(rxrpc_sk(sock->sk));
+	_leave(" = %d", ret);
+	return ret;
+}
+
+EXPORT_SYMBOL(rxrpc_kernel_reject_call);
diff --git a/net/rxrpc/ar-ack.c b/net/rxrpc/ar-ack.c
new file mode 100644
index 0000000..fc07a92
--- /dev/null
+++ b/net/rxrpc/ar-ack.c
@@ -0,0 +1,1250 @@
+/* Management of Tx window, Tx resend, ACKs and out-of-sequence reception
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#include <linux/module.h>
+#include <linux/circ_buf.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/udp.h>
+#include <net/sock.h>
+#include <net/af_rxrpc.h>
+#include "ar-internal.h"
+
+static unsigned rxrpc_ack_defer = 1;
+
+static const char *rxrpc_acks[] = {
+	"---", "REQ", "DUP", "OOS", "WIN", "MEM", "PNG", "PNR", "DLY", "IDL",
+	"-?-"
+};
+
+static const s8 rxrpc_ack_priority[] = {
+	[0]				= 0,
+	[RXRPC_ACK_DELAY]		= 1,
+	[RXRPC_ACK_REQUESTED]		= 2,
+	[RXRPC_ACK_IDLE]		= 3,
+	[RXRPC_ACK_PING_RESPONSE]	= 4,
+	[RXRPC_ACK_DUPLICATE]		= 5,
+	[RXRPC_ACK_OUT_OF_SEQUENCE]	= 6,
+	[RXRPC_ACK_EXCEEDS_WINDOW]	= 7,
+	[RXRPC_ACK_NOSPACE]		= 8,
+};
+
+/*
+ * propose an ACK be sent
+ */
+void __rxrpc_propose_ACK(struct rxrpc_call *call, uint8_t ack_reason,
+			 __be32 serial, bool immediate)
+{
+	unsigned long expiry;
+	s8 prior = rxrpc_ack_priority[ack_reason];
+
+	ASSERTCMP(prior, >, 0);
+
+	_enter("{%d},%s,%%%x,%u",
+	       call->debug_id, rxrpc_acks[ack_reason], ntohl(serial),
+	       immediate);
+
+	if (prior < rxrpc_ack_priority[call->ackr_reason]) {
+		if (immediate)
+			goto cancel_timer;
+		return;
+	}
+
+	/* update DELAY, IDLE, REQUESTED and PING_RESPONSE ACK serial
+	 * numbers */
+	if (prior == rxrpc_ack_priority[call->ackr_reason]) {
+		if (prior <= 4)
+			call->ackr_serial = serial;
+		if (immediate)
+			goto cancel_timer;
+		return;
+	}
+
+	call->ackr_reason = ack_reason;
+	call->ackr_serial = serial;
+
+	switch (ack_reason) {
+	case RXRPC_ACK_DELAY:
+		_debug("run delay timer");
+		call->ack_timer.expires = jiffies + rxrpc_ack_timeout * HZ;
+		add_timer(&call->ack_timer);
+		return;
+
+	case RXRPC_ACK_IDLE:
+		if (!immediate) {
+			_debug("run defer timer");
+			expiry = 1;
+			goto run_timer;
+		}
+		goto cancel_timer;
+
+	case RXRPC_ACK_REQUESTED:
+		if (!rxrpc_ack_defer)
+			goto cancel_timer;
+		if (!immediate || serial == cpu_to_be32(1)) {
+			_debug("run defer timer");
+			expiry = rxrpc_ack_defer;
+			goto run_timer;
+		}
+
+	default:
+		_debug("immediate ACK");
+		goto cancel_timer;
+	}
+
+run_timer:
+	expiry += jiffies;
+	if (!timer_pending(&call->ack_timer) ||
+	    time_after(call->ack_timer.expires, expiry))
+		mod_timer(&call->ack_timer, expiry);
+	return;
+
+cancel_timer:
+	_debug("cancel timer %%%u", ntohl(serial));
+	try_to_del_timer_sync(&call->ack_timer);
+	read_lock_bh(&call->state_lock);
+	if (call->state <= RXRPC_CALL_COMPLETE &&
+	    !test_and_set_bit(RXRPC_CALL_ACK, &call->events))
+		rxrpc_queue_call(call);
+	read_unlock_bh(&call->state_lock);
+}
+
+/*
+ * propose an ACK be sent, locking the call structure
+ */
+void rxrpc_propose_ACK(struct rxrpc_call *call, uint8_t ack_reason,
+		       __be32 serial, bool immediate)
+{
+	s8 prior = rxrpc_ack_priority[ack_reason];
+
+	if (prior > rxrpc_ack_priority[call->ackr_reason]) {
+		spin_lock_bh(&call->lock);
+		__rxrpc_propose_ACK(call, ack_reason, serial, immediate);
+		spin_unlock_bh(&call->lock);
+	}
+}
+
+/*
+ * set the resend timer
+ */
+static void rxrpc_set_resend(struct rxrpc_call *call, u8 resend,
+			     unsigned long resend_at)
+{
+	read_lock_bh(&call->state_lock);
+	if (call->state >= RXRPC_CALL_COMPLETE)
+		resend = 0;
+
+	if (resend & 1) {
+		_debug("SET RESEND");
+		set_bit(RXRPC_CALL_RESEND, &call->events);
+	}
+
+	if (resend & 2) {
+		_debug("MODIFY RESEND TIMER");
+		set_bit(RXRPC_CALL_RUN_RTIMER, &call->flags);
+		mod_timer(&call->resend_timer, resend_at);
+	} else {
+		_debug("KILL RESEND TIMER");
+		del_timer_sync(&call->resend_timer);
+		clear_bit(RXRPC_CALL_RESEND_TIMER, &call->events);
+		clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags);
+	}
+	read_unlock_bh(&call->state_lock);
+}
+
+/*
+ * resend packets
+ */
+static void rxrpc_resend(struct rxrpc_call *call)
+{
+	struct rxrpc_skb_priv *sp;
+	struct rxrpc_header *hdr;
+	struct sk_buff *txb;
+	unsigned long *p_txb, resend_at;
+	int loop, stop;
+	u8 resend;
+
+	_enter("{%d,%d,%d,%d},",
+	       call->acks_hard, call->acks_unacked,
+	       atomic_read(&call->sequence),
+	       CIRC_CNT(call->acks_head, call->acks_tail, call->acks_winsz));
+
+	stop = 0;
+	resend = 0;
+	resend_at = 0;
+
+	for (loop = call->acks_tail;
+	     loop != call->acks_head || stop;
+	     loop = (loop + 1) &  (call->acks_winsz - 1)
+	     ) {
+		p_txb = call->acks_window + loop;
+		smp_read_barrier_depends();
+		if (*p_txb & 1)
+			continue;
+
+		txb = (struct sk_buff *) *p_txb;
+		sp = rxrpc_skb(txb);
+
+		if (sp->need_resend) {
+			sp->need_resend = 0;
+
+			/* each Tx packet has a new serial number */
+			sp->hdr.serial =
+				htonl(atomic_inc_return(&call->conn->serial));
+
+			hdr = (struct rxrpc_header *) txb->head;
+			hdr->serial = sp->hdr.serial;
+
+			_proto("Tx DATA %%%u { #%d }",
+			       ntohl(sp->hdr.serial), ntohl(sp->hdr.seq));
+			if (rxrpc_send_packet(call->conn->trans, txb) < 0) {
+				stop = 0;
+				sp->resend_at = jiffies + 3;
+			} else {
+				sp->resend_at =
+					jiffies + rxrpc_resend_timeout * HZ;
+			}
+		}
+
+		if (time_after_eq(jiffies + 1, sp->resend_at)) {
+			sp->need_resend = 1;
+			resend |= 1;
+		} else if (resend & 2) {
+			if (time_before(sp->resend_at, resend_at))
+				resend_at = sp->resend_at;
+		} else {
+			resend_at = sp->resend_at;
+			resend |= 2;
+		}
+	}
+
+	rxrpc_set_resend(call, resend, resend_at);
+	_leave("");
+}
+
+/*
+ * handle resend timer expiry
+ */
+static void rxrpc_resend_timer(struct rxrpc_call *call)
+{
+	struct rxrpc_skb_priv *sp;
+	struct sk_buff *txb;
+	unsigned long *p_txb, resend_at;
+	int loop;
+	u8 resend;
+
+	_enter("%d,%d,%d",
+	       call->acks_tail, call->acks_unacked, call->acks_head);
+
+	resend = 0;
+	resend_at = 0;
+
+	for (loop = call->acks_unacked;
+	     loop != call->acks_head;
+	     loop = (loop + 1) &  (call->acks_winsz - 1)
+	     ) {
+		p_txb = call->acks_window + loop;
+		smp_read_barrier_depends();
+		txb = (struct sk_buff *) (*p_txb & ~1);
+		sp = rxrpc_skb(txb);
+
+		ASSERT(!(*p_txb & 1));
+
+		if (sp->need_resend) {
+			;
+		} else if (time_after_eq(jiffies + 1, sp->resend_at)) {
+			sp->need_resend = 1;
+			resend |= 1;
+		} else if (resend & 2) {
+			if (time_before(sp->resend_at, resend_at))
+				resend_at = sp->resend_at;
+		} else {
+			resend_at = sp->resend_at;
+			resend |= 2;
+		}
+	}
+
+	rxrpc_set_resend(call, resend, resend_at);
+	_leave("");
+}
+
+/*
+ * process soft ACKs of our transmitted packets
+ * - these indicate packets the peer has or has not received, but hasn't yet
+ *   given to the consumer, and so can still be discarded and re-requested
+ */
+static int rxrpc_process_soft_ACKs(struct rxrpc_call *call,
+				   struct rxrpc_ackpacket *ack,
+				   struct sk_buff *skb)
+{
+	struct rxrpc_skb_priv *sp;
+	struct sk_buff *txb;
+	unsigned long *p_txb, resend_at;
+	int loop;
+	u8 sacks[RXRPC_MAXACKS], resend;
+
+	_enter("{%d,%d},{%d},",
+	       call->acks_hard,
+	       CIRC_CNT(call->acks_head, call->acks_tail, call->acks_winsz),
+	       ack->nAcks);
+
+	if (skb_copy_bits(skb, 0, sacks, ack->nAcks) < 0)
+		goto protocol_error;
+
+	resend = 0;
+	resend_at = 0;
+	for (loop = 0; loop < ack->nAcks; loop++) {
+		p_txb = call->acks_window;
+		p_txb += (call->acks_tail + loop) & (call->acks_winsz - 1);
+		smp_read_barrier_depends();
+		txb = (struct sk_buff *) (*p_txb & ~1);
+		sp = rxrpc_skb(txb);
+
+		switch (sacks[loop]) {
+		case RXRPC_ACK_TYPE_ACK:
+			sp->need_resend = 0;
+			*p_txb |= 1;
+			break;
+		case RXRPC_ACK_TYPE_NACK:
+			sp->need_resend = 1;
+			*p_txb &= ~1;
+			resend = 1;
+			break;
+		default:
+			_debug("Unsupported ACK type %d", sacks[loop]);
+			goto protocol_error;
+		}
+	}
+
+	smp_mb();
+	call->acks_unacked = (call->acks_tail + loop) & (call->acks_winsz - 1);
+
+	/* anything not explicitly ACK'd is implicitly NACK'd, but may just not
+	 * have been received or processed yet by the far end */
+	for (loop = call->acks_unacked;
+	     loop != call->acks_head;
+	     loop = (loop + 1) &  (call->acks_winsz - 1)
+	     ) {
+		p_txb = call->acks_window + loop;
+		smp_read_barrier_depends();
+		txb = (struct sk_buff *) (*p_txb & ~1);
+		sp = rxrpc_skb(txb);
+
+		if (*p_txb & 1) {
+			/* packet must have been discarded */
+			sp->need_resend = 1;
+			*p_txb &= ~1;
+			resend |= 1;
+		} else if (sp->need_resend) {
+			;
+		} else if (time_after_eq(jiffies + 1, sp->resend_at)) {
+			sp->need_resend = 1;
+			resend |= 1;
+		} else if (resend & 2) {
+			if (time_before(sp->resend_at, resend_at))
+				resend_at = sp->resend_at;
+		} else {
+			resend_at = sp->resend_at;
+			resend |= 2;
+		}
+	}
+
+	rxrpc_set_resend(call, resend, resend_at);
+	_leave(" = 0");
+	return 0;
+
+protocol_error:
+	_leave(" = -EPROTO");
+	return -EPROTO;
+}
+
+/*
+ * discard hard-ACK'd packets from the Tx window
+ */
+static void rxrpc_rotate_tx_window(struct rxrpc_call *call, u32 hard)
+{
+	struct rxrpc_skb_priv *sp;
+	unsigned long _skb;
+	int tail = call->acks_tail, old_tail;
+	int win = CIRC_CNT(call->acks_head, tail, call->acks_winsz);
+
+	_enter("{%u,%u},%u", call->acks_hard, win, hard);
+
+	ASSERTCMP(hard - call->acks_hard, <=, win);
+
+	while (call->acks_hard < hard) {
+		smp_read_barrier_depends();
+		_skb = call->acks_window[tail] & ~1;
+		sp = rxrpc_skb((struct sk_buff *) _skb);
+		rxrpc_free_skb((struct sk_buff *) _skb);
+		old_tail = tail;
+		tail = (tail + 1) & (call->acks_winsz - 1);
+		call->acks_tail = tail;
+		if (call->acks_unacked == old_tail)
+			call->acks_unacked = tail;
+		call->acks_hard++;
+	}
+
+	wake_up(&call->tx_waitq);
+}
+
+/*
+ * clear the Tx window in the event of a failure
+ */
+static void rxrpc_clear_tx_window(struct rxrpc_call *call)
+{
+	rxrpc_rotate_tx_window(call, atomic_read(&call->sequence));
+}
+
+/*
+ * drain the out of sequence received packet queue into the packet Rx queue
+ */
+static int rxrpc_drain_rx_oos_queue(struct rxrpc_call *call)
+{
+	struct rxrpc_skb_priv *sp;
+	struct sk_buff *skb;
+	bool terminal;
+	int ret;
+
+	_enter("{%d,%d}", call->rx_data_post, call->rx_first_oos);
+
+	spin_lock_bh(&call->lock);
+
+	ret = -ECONNRESET;
+	if (test_bit(RXRPC_CALL_RELEASED, &call->flags))
+		goto socket_unavailable;
+
+	skb = skb_dequeue(&call->rx_oos_queue);
+	if (skb) {
+		sp = rxrpc_skb(skb);
+
+		_debug("drain OOS packet %d [%d]",
+		       ntohl(sp->hdr.seq), call->rx_first_oos);
+
+		if (ntohl(sp->hdr.seq) != call->rx_first_oos) {
+			skb_queue_head(&call->rx_oos_queue, skb);
+			call->rx_first_oos = ntohl(rxrpc_skb(skb)->hdr.seq);
+			_debug("requeue %p {%u}", skb, call->rx_first_oos);
+		} else {
+			skb->mark = RXRPC_SKB_MARK_DATA;
+			terminal = ((sp->hdr.flags & RXRPC_LAST_PACKET) &&
+				!(sp->hdr.flags & RXRPC_CLIENT_INITIATED));
+			ret = rxrpc_queue_rcv_skb(call, skb, true, terminal);
+			BUG_ON(ret < 0);
+			_debug("drain #%u", call->rx_data_post);
+			call->rx_data_post++;
+
+			/* find out what the next packet is */
+			skb = skb_peek(&call->rx_oos_queue);
+			if (skb)
+				call->rx_first_oos =
+					ntohl(rxrpc_skb(skb)->hdr.seq);
+			else
+				call->rx_first_oos = 0;
+			_debug("peek %p {%u}", skb, call->rx_first_oos);
+		}
+	}
+
+	ret = 0;
+socket_unavailable:
+	spin_unlock_bh(&call->lock);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * insert an out of sequence packet into the buffer
+ */
+static void rxrpc_insert_oos_packet(struct rxrpc_call *call,
+				    struct sk_buff *skb)
+{
+	struct rxrpc_skb_priv *sp, *psp;
+	struct sk_buff *p;
+	u32 seq;
+
+	sp = rxrpc_skb(skb);
+	seq = ntohl(sp->hdr.seq);
+	_enter(",,{%u}", seq);
+
+	skb->destructor = rxrpc_packet_destructor;
+	ASSERTCMP(sp->call, ==, NULL);
+	sp->call = call;
+	rxrpc_get_call(call);
+
+	/* insert into the buffer in sequence order */
+	spin_lock_bh(&call->lock);
+
+	skb_queue_walk(&call->rx_oos_queue, p) {
+		psp = rxrpc_skb(p);
+		if (ntohl(psp->hdr.seq) > seq) {
+			_debug("insert oos #%u before #%u",
+			       seq, ntohl(psp->hdr.seq));
+			skb_insert(p, skb, &call->rx_oos_queue);
+			goto inserted;
+		}
+	}
+
+	_debug("append oos #%u", seq);
+	skb_queue_tail(&call->rx_oos_queue, skb);
+inserted:
+
+	/* we might now have a new front to the queue */
+	if (call->rx_first_oos == 0 || seq < call->rx_first_oos)
+		call->rx_first_oos = seq;
+
+	read_lock(&call->state_lock);
+	if (call->state < RXRPC_CALL_COMPLETE &&
+	    call->rx_data_post == call->rx_first_oos) {
+		_debug("drain rx oos now");
+		set_bit(RXRPC_CALL_DRAIN_RX_OOS, &call->events);
+	}
+	read_unlock(&call->state_lock);
+
+	spin_unlock_bh(&call->lock);
+	_leave(" [stored #%u]", call->rx_first_oos);
+}
+
+/*
+ * clear the Tx window on final ACK reception
+ */
+static void rxrpc_zap_tx_window(struct rxrpc_call *call)
+{
+	struct rxrpc_skb_priv *sp;
+	struct sk_buff *skb;
+	unsigned long _skb, *acks_window;
+	uint8_t winsz = call->acks_winsz;
+	int tail;
+
+	acks_window = call->acks_window;
+	call->acks_window = NULL;
+
+	while (CIRC_CNT(call->acks_head, call->acks_tail, winsz) > 0) {
+		tail = call->acks_tail;
+		smp_read_barrier_depends();
+		_skb = acks_window[tail] & ~1;
+		smp_mb();
+		call->acks_tail = (call->acks_tail + 1) & (winsz - 1);
+
+		skb = (struct sk_buff *) _skb;
+		sp = rxrpc_skb(skb);
+		_debug("+++ clear Tx %u", ntohl(sp->hdr.seq));
+		rxrpc_free_skb(skb);
+	}
+
+	kfree(acks_window);
+}
+
+/*
+ * process packets in the reception queue
+ */
+static int rxrpc_process_rx_queue(struct rxrpc_call *call,
+				  u32 *_abort_code)
+{
+	struct rxrpc_ackpacket ack;
+	struct rxrpc_skb_priv *sp;
+	struct sk_buff *skb;
+	bool post_ACK;
+	int latest;
+	u32 hard, tx;
+
+	_enter("");
+
+process_further:
+	skb = skb_dequeue(&call->rx_queue);
+	if (!skb)
+		return -EAGAIN;
+
+	_net("deferred skb %p", skb);
+
+	sp = rxrpc_skb(skb);
+
+	_debug("process %s [st %d]", rxrpc_pkts[sp->hdr.type], call->state);
+
+	post_ACK = false;
+
+	switch (sp->hdr.type) {
+		/* data packets that wind up here have been received out of
+		 * order, need security processing or are jumbo packets */
+	case RXRPC_PACKET_TYPE_DATA:
+		_proto("OOSQ DATA %%%u { #%u }",
+		       ntohl(sp->hdr.serial), ntohl(sp->hdr.seq));
+
+		/* secured packets must be verified and possibly decrypted */
+		if (rxrpc_verify_packet(call, skb, _abort_code) < 0)
+			goto protocol_error;
+
+		rxrpc_insert_oos_packet(call, skb);
+		goto process_further;
+
+		/* partial ACK to process */
+	case RXRPC_PACKET_TYPE_ACK:
+		if (skb_copy_bits(skb, 0, &ack, sizeof(ack)) < 0) {
+			_debug("extraction failure");
+			goto protocol_error;
+		}
+		if (!skb_pull(skb, sizeof(ack)))
+			BUG();
+
+		latest = ntohl(sp->hdr.serial);
+		hard = ntohl(ack.firstPacket);
+		tx = atomic_read(&call->sequence);
+
+		_proto("Rx ACK %%%u { m=%hu f=#%u p=#%u s=%%%u r=%s n=%u }",
+		       latest,
+		       ntohs(ack.maxSkew),
+		       hard,
+		       ntohl(ack.previousPacket),
+		       ntohl(ack.serial),
+		       rxrpc_acks[ack.reason],
+		       ack.nAcks);
+
+		if (ack.reason == RXRPC_ACK_PING) {
+			_proto("Rx ACK %%%u PING Request", latest);
+			rxrpc_propose_ACK(call, RXRPC_ACK_PING_RESPONSE,
+					  sp->hdr.serial, true);
+		}
+
+		/* discard any out-of-order or duplicate ACKs */
+		if (latest - call->acks_latest <= 0) {
+			_debug("discard ACK %d <= %d",
+			       latest, call->acks_latest);
+			goto discard;
+		}
+		call->acks_latest = latest;
+
+		if (call->state != RXRPC_CALL_CLIENT_SEND_REQUEST &&
+		    call->state != RXRPC_CALL_CLIENT_AWAIT_REPLY &&
+		    call->state != RXRPC_CALL_SERVER_SEND_REPLY &&
+		    call->state != RXRPC_CALL_SERVER_AWAIT_ACK)
+			goto discard;
+
+		_debug("Tx=%d H=%u S=%d", tx, call->acks_hard, call->state);
+
+		if (hard > 0) {
+			if (hard - 1 > tx) {
+				_debug("hard-ACK'd packet %d not transmitted"
+				       " (%d top)",
+				       hard - 1, tx);
+				goto protocol_error;
+			}
+
+			if ((call->state == RXRPC_CALL_CLIENT_AWAIT_REPLY ||
+			     call->state == RXRPC_CALL_SERVER_AWAIT_ACK) &&
+			    hard > tx)
+				goto all_acked;
+
+			smp_rmb();
+			rxrpc_rotate_tx_window(call, hard - 1);
+		}
+
+		if (ack.nAcks > 0) {
+			if (hard - 1 + ack.nAcks > tx) {
+				_debug("soft-ACK'd packet %d+%d not"
+				       " transmitted (%d top)",
+				       hard - 1, ack.nAcks, tx);
+				goto protocol_error;
+			}
+
+			if (rxrpc_process_soft_ACKs(call, &ack, skb) < 0)
+				goto protocol_error;
+		}
+		goto discard;
+
+		/* complete ACK to process */
+	case RXRPC_PACKET_TYPE_ACKALL:
+		goto all_acked;
+
+		/* abort and busy are handled elsewhere */
+	case RXRPC_PACKET_TYPE_BUSY:
+	case RXRPC_PACKET_TYPE_ABORT:
+		BUG();
+
+		/* connection level events - also handled elsewhere */
+	case RXRPC_PACKET_TYPE_CHALLENGE:
+	case RXRPC_PACKET_TYPE_RESPONSE:
+	case RXRPC_PACKET_TYPE_DEBUG:
+		BUG();
+	}
+
+	/* if we've had a hard ACK that covers all the packets we've sent, then
+	 * that ends that phase of the operation */
+all_acked:
+	write_lock_bh(&call->state_lock);
+	_debug("ack all %d", call->state);
+
+	switch (call->state) {
+	case RXRPC_CALL_CLIENT_AWAIT_REPLY:
+		call->state = RXRPC_CALL_CLIENT_RECV_REPLY;
+		break;
+	case RXRPC_CALL_SERVER_AWAIT_ACK:
+		_debug("srv complete");
+		call->state = RXRPC_CALL_COMPLETE;
+		post_ACK = true;
+		break;
+	case RXRPC_CALL_CLIENT_SEND_REQUEST:
+	case RXRPC_CALL_SERVER_RECV_REQUEST:
+		goto protocol_error_unlock; /* can't occur yet */
+	default:
+		write_unlock_bh(&call->state_lock);
+		goto discard; /* assume packet left over from earlier phase */
+	}
+
+	write_unlock_bh(&call->state_lock);
+
+	/* if all the packets we sent are hard-ACK'd, then we can discard
+	 * whatever we've got left */
+	_debug("clear Tx %d",
+	       CIRC_CNT(call->acks_head, call->acks_tail, call->acks_winsz));
+
+	del_timer_sync(&call->resend_timer);
+	clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags);
+	clear_bit(RXRPC_CALL_RESEND_TIMER, &call->events);
+
+	if (call->acks_window)
+		rxrpc_zap_tx_window(call);
+
+	if (post_ACK) {
+		/* post the final ACK message for userspace to pick up */
+		_debug("post ACK");
+		skb->mark = RXRPC_SKB_MARK_FINAL_ACK;
+		sp->call = call;
+		rxrpc_get_call(call);
+		spin_lock_bh(&call->lock);
+		if (rxrpc_queue_rcv_skb(call, skb, true, true) < 0)
+			BUG();
+		spin_unlock_bh(&call->lock);
+		goto process_further;
+	}
+
+discard:
+	rxrpc_free_skb(skb);
+	goto process_further;
+
+protocol_error_unlock:
+	write_unlock_bh(&call->state_lock);
+protocol_error:
+	rxrpc_free_skb(skb);
+	_leave(" = -EPROTO");
+	return -EPROTO;
+}
+
+/*
+ * post a message to the socket Rx queue for recvmsg() to pick up
+ */
+static int rxrpc_post_message(struct rxrpc_call *call, u32 mark, u32 error,
+			      bool fatal)
+{
+	struct rxrpc_skb_priv *sp;
+	struct sk_buff *skb;
+	int ret;
+
+	_enter("{%d,%lx},%u,%u,%d",
+	       call->debug_id, call->flags, mark, error, fatal);
+
+	/* remove timers and things for fatal messages */
+	if (fatal) {
+		del_timer_sync(&call->resend_timer);
+		del_timer_sync(&call->ack_timer);
+		clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags);
+	}
+
+	if (mark != RXRPC_SKB_MARK_NEW_CALL &&
+	    !test_bit(RXRPC_CALL_HAS_USERID, &call->flags)) {
+		_leave("[no userid]");
+		return 0;
+	}
+
+	if (!test_bit(RXRPC_CALL_TERMINAL_MSG, &call->flags)) {
+		skb = alloc_skb(0, GFP_NOFS);
+		if (!skb)
+			return -ENOMEM;
+
+		rxrpc_new_skb(skb);
+
+		skb->mark = mark;
+
+		sp = rxrpc_skb(skb);
+		memset(sp, 0, sizeof(*sp));
+		sp->error = error;
+		sp->call = call;
+		rxrpc_get_call(call);
+
+		spin_lock_bh(&call->lock);
+		ret = rxrpc_queue_rcv_skb(call, skb, true, fatal);
+		spin_unlock_bh(&call->lock);
+		if (ret < 0)
+			BUG();
+	}
+
+	return 0;
+}
+
+/*
+ * handle background processing of incoming call packets and ACK / abort
+ * generation
+ */
+void rxrpc_process_call(struct work_struct *work)
+{
+	struct rxrpc_call *call =
+		container_of(work, struct rxrpc_call, processor);
+	struct rxrpc_ackpacket ack;
+	struct rxrpc_ackinfo ackinfo;
+	struct rxrpc_header hdr;
+	struct msghdr msg;
+	struct kvec iov[5];
+	unsigned long bits;
+	__be32 data;
+	size_t len;
+	int genbit, loop, nbit, ioc, ret;
+	u32 abort_code = RX_PROTOCOL_ERROR;
+	u8 *acks = NULL;
+
+	//printk("\n--------------------\n");
+	_enter("{%d,%s,%lx} [%lu]",
+	       call->debug_id, rxrpc_call_states[call->state], call->events,
+	       (jiffies - call->creation_jif) / (HZ / 10));
+
+	if (test_and_set_bit(RXRPC_CALL_PROC_BUSY, &call->flags)) {
+		_debug("XXXXXXXXXXXXX RUNNING ON MULTIPLE CPUS XXXXXXXXXXXXX");
+		return;
+	}
+
+	/* there's a good chance we're going to have to send a message, so set
+	 * one up in advance */
+	msg.msg_name	= &call->conn->trans->peer->srx.transport.sin;
+	msg.msg_namelen	= sizeof(call->conn->trans->peer->srx.transport.sin);
+	msg.msg_control	= NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags	= 0;
+
+	hdr.epoch	= call->conn->epoch;
+	hdr.cid		= call->cid;
+	hdr.callNumber	= call->call_id;
+	hdr.seq		= 0;
+	hdr.type	= RXRPC_PACKET_TYPE_ACK;
+	hdr.flags	= call->conn->out_clientflag;
+	hdr.userStatus	= 0;
+	hdr.securityIndex = call->conn->security_ix;
+	hdr._rsvd	= 0;
+	hdr.serviceId	= call->conn->service_id;
+
+	memset(iov, 0, sizeof(iov));
+	iov[0].iov_base	= &hdr;
+	iov[0].iov_len	= sizeof(hdr);
+
+	/* deal with events of a final nature */
+	if (test_bit(RXRPC_CALL_RELEASE, &call->events)) {
+		rxrpc_release_call(call);
+		clear_bit(RXRPC_CALL_RELEASE, &call->events);
+	}
+
+	if (test_bit(RXRPC_CALL_RCVD_ERROR, &call->events)) {
+		int error;
+
+		clear_bit(RXRPC_CALL_CONN_ABORT, &call->events);
+		clear_bit(RXRPC_CALL_REJECT_BUSY, &call->events);
+		clear_bit(RXRPC_CALL_ABORT, &call->events);
+
+		error = call->conn->trans->peer->net_error;
+		_debug("post net error %d", error);
+
+		if (rxrpc_post_message(call, RXRPC_SKB_MARK_NET_ERROR,
+				       error, true) < 0)
+			goto no_mem;
+		clear_bit(RXRPC_CALL_RCVD_ERROR, &call->events);
+		goto kill_ACKs;
+	}
+
+	if (test_bit(RXRPC_CALL_CONN_ABORT, &call->events)) {
+		ASSERTCMP(call->state, >, RXRPC_CALL_COMPLETE);
+
+		clear_bit(RXRPC_CALL_REJECT_BUSY, &call->events);
+		clear_bit(RXRPC_CALL_ABORT, &call->events);
+
+		_debug("post conn abort");
+
+		if (rxrpc_post_message(call, RXRPC_SKB_MARK_LOCAL_ERROR,
+				       call->conn->error, true) < 0)
+			goto no_mem;
+		clear_bit(RXRPC_CALL_CONN_ABORT, &call->events);
+		goto kill_ACKs;
+	}
+
+	if (test_bit(RXRPC_CALL_REJECT_BUSY, &call->events)) {
+		hdr.type = RXRPC_PACKET_TYPE_BUSY;
+		genbit = RXRPC_CALL_REJECT_BUSY;
+		goto send_message;
+	}
+
+	if (test_bit(RXRPC_CALL_ABORT, &call->events)) {
+		ASSERTCMP(call->state, >, RXRPC_CALL_COMPLETE);
+
+		if (rxrpc_post_message(call, RXRPC_SKB_MARK_LOCAL_ERROR,
+				       ECONNABORTED, true) < 0)
+			goto no_mem;
+		hdr.type = RXRPC_PACKET_TYPE_ABORT;
+		data = htonl(call->abort_code);
+		iov[1].iov_base = &data;
+		iov[1].iov_len = sizeof(data);
+		genbit = RXRPC_CALL_ABORT;
+		goto send_message;
+	}
+
+	if (test_bit(RXRPC_CALL_ACK_FINAL, &call->events)) {
+		hdr.type = RXRPC_PACKET_TYPE_ACKALL;
+		genbit = RXRPC_CALL_ACK_FINAL;
+		goto send_message;
+	}
+
+	if (call->events & ((1 << RXRPC_CALL_RCVD_BUSY) |
+			    (1 << RXRPC_CALL_RCVD_ABORT))
+	    ) {
+		u32 mark;
+
+		if (test_bit(RXRPC_CALL_RCVD_ABORT, &call->events))
+			mark = RXRPC_SKB_MARK_REMOTE_ABORT;
+		else
+			mark = RXRPC_SKB_MARK_BUSY;
+
+		_debug("post abort/busy");
+		rxrpc_clear_tx_window(call);
+		if (rxrpc_post_message(call, mark, ECONNABORTED, true) < 0)
+			goto no_mem;
+
+		clear_bit(RXRPC_CALL_RCVD_BUSY, &call->events);
+		clear_bit(RXRPC_CALL_RCVD_ABORT, &call->events);
+		goto kill_ACKs;
+	}
+
+	if (test_and_clear_bit(RXRPC_CALL_RCVD_ACKALL, &call->events)) {
+		_debug("do implicit ackall");
+		rxrpc_clear_tx_window(call);
+	}
+
+	if (test_bit(RXRPC_CALL_LIFE_TIMER, &call->events)) {
+		write_lock_bh(&call->state_lock);
+		if (call->state <= RXRPC_CALL_COMPLETE) {
+			call->state = RXRPC_CALL_LOCALLY_ABORTED;
+			call->abort_code = RX_CALL_TIMEOUT;
+			set_bit(RXRPC_CALL_ABORT, &call->events);
+		}
+		write_unlock_bh(&call->state_lock);
+
+		_debug("post timeout");
+		if (rxrpc_post_message(call, RXRPC_SKB_MARK_LOCAL_ERROR,
+				       ETIME, true) < 0)
+			goto no_mem;
+
+		clear_bit(RXRPC_CALL_LIFE_TIMER, &call->events);
+		goto kill_ACKs;
+	}
+
+	/* deal with assorted inbound messages */
+	if (!skb_queue_empty(&call->rx_queue)) {
+		switch (rxrpc_process_rx_queue(call, &abort_code)) {
+		case 0:
+		case -EAGAIN:
+			break;
+		case -ENOMEM:
+			goto no_mem;
+		case -EKEYEXPIRED:
+		case -EKEYREJECTED:
+		case -EPROTO:
+			rxrpc_abort_call(call, abort_code);
+			goto kill_ACKs;
+		}
+	}
+
+	/* handle resending */
+	if (test_and_clear_bit(RXRPC_CALL_RESEND_TIMER, &call->events))
+		rxrpc_resend_timer(call);
+	if (test_and_clear_bit(RXRPC_CALL_RESEND, &call->events))
+		rxrpc_resend(call);
+
+	/* consider sending an ordinary ACK */
+	if (test_bit(RXRPC_CALL_ACK, &call->events)) {
+		__be32 pad;
+
+		_debug("send ACK: window: %d - %d { %lx }",
+		       call->rx_data_eaten, call->ackr_win_top,
+		       call->ackr_window[0]);
+
+		if (call->state > RXRPC_CALL_SERVER_ACK_REQUEST &&
+		    call->ackr_reason != RXRPC_ACK_PING_RESPONSE) {
+			/* ACK by sending reply DATA packet in this state */
+			clear_bit(RXRPC_CALL_ACK, &call->events);
+			goto maybe_reschedule;
+		}
+
+		genbit = RXRPC_CALL_ACK;
+
+		acks = kzalloc(call->ackr_win_top - call->rx_data_eaten,
+			       GFP_NOFS);
+		if (!acks)
+			goto no_mem;
+
+		//hdr.flags	= RXRPC_SLOW_START_OK;
+		ack.bufferSpace	= htons(8);
+		ack.maxSkew	= 0;
+		ack.serial	= 0;
+		ack.reason	= 0;
+
+		ackinfo.rxMTU	= htonl(5692);
+//		ackinfo.rxMTU	= htonl(call->conn->trans->peer->maxdata);
+		ackinfo.maxMTU	= htonl(call->conn->trans->peer->maxdata);
+		ackinfo.rwind	= htonl(32);
+		ackinfo.jumbo_max = htonl(4);
+
+		spin_lock_bh(&call->lock);
+		ack.reason = call->ackr_reason;
+		ack.serial = call->ackr_serial;
+		ack.previousPacket = call->ackr_prev_seq;
+		ack.firstPacket = htonl(call->rx_data_eaten + 1);
+
+		ack.nAcks = 0;
+		for (loop = 0; loop < RXRPC_ACKR_WINDOW_ASZ; loop++) {
+			nbit = loop * BITS_PER_LONG;
+			for (bits = call->ackr_window[loop]; bits; bits >>= 1
+			     ) {
+				_debug("- l=%d n=%d b=%lx", loop, nbit, bits);
+				if (bits & 1) {
+					acks[nbit] = RXRPC_ACK_TYPE_ACK;
+					ack.nAcks = nbit + 1;
+				}
+				nbit++;
+			}
+		}
+		call->ackr_reason = 0;
+		spin_unlock_bh(&call->lock);
+
+		pad = 0;
+
+		iov[1].iov_base = &ack;
+		iov[1].iov_len	= sizeof(ack);
+		iov[2].iov_base = acks;
+		iov[2].iov_len	= ack.nAcks;
+		iov[3].iov_base = &pad;
+		iov[3].iov_len	= 3;
+		iov[4].iov_base = &ackinfo;
+		iov[4].iov_len	= sizeof(ackinfo);
+
+		switch (ack.reason) {
+		case RXRPC_ACK_REQUESTED:
+		case RXRPC_ACK_DUPLICATE:
+		case RXRPC_ACK_OUT_OF_SEQUENCE:
+		case RXRPC_ACK_EXCEEDS_WINDOW:
+		case RXRPC_ACK_NOSPACE:
+		case RXRPC_ACK_PING:
+		case RXRPC_ACK_PING_RESPONSE:
+			goto send_ACK_with_skew;
+		case RXRPC_ACK_DELAY:
+		case RXRPC_ACK_IDLE:
+			goto send_ACK;
+		}
+	}
+
+	/* handle completion of security negotiations on an incoming
+	 * connection */
+	if (test_and_clear_bit(RXRPC_CALL_SECURED, &call->events)) {
+		_debug("secured");
+		spin_lock_bh(&call->lock);
+
+		if (call->state == RXRPC_CALL_SERVER_SECURING) {
+			_debug("securing");
+			write_lock(&call->conn->lock);
+			if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) &&
+			    !test_bit(RXRPC_CALL_RELEASE, &call->events)) {
+				_debug("not released");
+				call->state = RXRPC_CALL_SERVER_ACCEPTING;
+				list_move_tail(&call->accept_link,
+					       &call->socket->acceptq);
+			}
+			write_unlock(&call->conn->lock);
+			read_lock(&call->state_lock);
+			if (call->state < RXRPC_CALL_COMPLETE)
+				set_bit(RXRPC_CALL_POST_ACCEPT, &call->events);
+			read_unlock(&call->state_lock);
+		}
+
+		spin_unlock_bh(&call->lock);
+		if (!test_bit(RXRPC_CALL_POST_ACCEPT, &call->events))
+			goto maybe_reschedule;
+	}
+
+	/* post a notification of an acceptable connection to the app */
+	if (test_bit(RXRPC_CALL_POST_ACCEPT, &call->events)) {
+		_debug("post accept");
+		if (rxrpc_post_message(call, RXRPC_SKB_MARK_NEW_CALL,
+				       0, false) < 0)
+			goto no_mem;
+		clear_bit(RXRPC_CALL_POST_ACCEPT, &call->events);
+		goto maybe_reschedule;
+	}
+
+	/* handle incoming call acceptance */
+	if (test_and_clear_bit(RXRPC_CALL_ACCEPTED, &call->events)) {
+		_debug("accepted");
+		ASSERTCMP(call->rx_data_post, ==, 0);
+		call->rx_data_post = 1;
+		read_lock_bh(&call->state_lock);
+		if (call->state < RXRPC_CALL_COMPLETE)
+			set_bit(RXRPC_CALL_DRAIN_RX_OOS, &call->events);
+		read_unlock_bh(&call->state_lock);
+	}
+
+	/* drain the out of sequence received packet queue into the packet Rx
+	 * queue */
+	if (test_and_clear_bit(RXRPC_CALL_DRAIN_RX_OOS, &call->events)) {
+		while (call->rx_data_post == call->rx_first_oos)
+			if (rxrpc_drain_rx_oos_queue(call) < 0)
+				break;
+		goto maybe_reschedule;
+	}
+
+	/* other events may have been raised since we started checking */
+	goto maybe_reschedule;
+
+send_ACK_with_skew:
+	ack.maxSkew = htons(atomic_read(&call->conn->hi_serial) -
+			    ntohl(ack.serial));
+send_ACK:
+	hdr.serial = htonl(atomic_inc_return(&call->conn->serial));
+	_proto("Tx ACK %%%u { m=%hu f=#%u p=#%u s=%%%u r=%s n=%u }",
+	       ntohl(hdr.serial),
+	       ntohs(ack.maxSkew),
+	       ntohl(ack.firstPacket),
+	       ntohl(ack.previousPacket),
+	       ntohl(ack.serial),
+	       rxrpc_acks[ack.reason],
+	       ack.nAcks);
+
+	del_timer_sync(&call->ack_timer);
+	if (ack.nAcks > 0)
+		set_bit(RXRPC_CALL_TX_SOFT_ACK, &call->flags);
+	goto send_message_2;
+
+send_message:
+	_debug("send message");
+
+	hdr.serial = htonl(atomic_inc_return(&call->conn->serial));
+	_proto("Tx %s %%%u", rxrpc_pkts[hdr.type], ntohl(hdr.serial));
+send_message_2:
+
+	len = iov[0].iov_len;
+	ioc = 1;
+	if (iov[4].iov_len) {
+		ioc = 5;
+		len += iov[4].iov_len;
+		len += iov[3].iov_len;
+		len += iov[2].iov_len;
+		len += iov[1].iov_len;
+	} else if (iov[3].iov_len) {
+		ioc = 4;
+		len += iov[3].iov_len;
+		len += iov[2].iov_len;
+		len += iov[1].iov_len;
+	} else if (iov[2].iov_len) {
+		ioc = 3;
+		len += iov[2].iov_len;
+		len += iov[1].iov_len;
+	} else if (iov[1].iov_len) {
+		ioc = 2;
+		len += iov[1].iov_len;
+	}
+
+	ret = kernel_sendmsg(call->conn->trans->local->socket,
+			     &msg, iov, ioc, len);
+	if (ret < 0) {
+		_debug("sendmsg failed: %d", ret);
+		read_lock_bh(&call->state_lock);
+		if (call->state < RXRPC_CALL_DEAD)
+			rxrpc_queue_call(call);
+		read_unlock_bh(&call->state_lock);
+		goto error;
+	}
+
+	switch (genbit) {
+	case RXRPC_CALL_ABORT:
+		clear_bit(genbit, &call->events);
+		clear_bit(RXRPC_CALL_RCVD_ABORT, &call->events);
+		goto kill_ACKs;
+
+	case RXRPC_CALL_ACK_FINAL:
+		write_lock_bh(&call->state_lock);
+		if (call->state == RXRPC_CALL_CLIENT_FINAL_ACK)
+			call->state = RXRPC_CALL_COMPLETE;
+		write_unlock_bh(&call->state_lock);
+		goto kill_ACKs;
+
+	default:
+		clear_bit(genbit, &call->events);
+		switch (call->state) {
+		case RXRPC_CALL_CLIENT_AWAIT_REPLY:
+		case RXRPC_CALL_CLIENT_RECV_REPLY:
+		case RXRPC_CALL_SERVER_RECV_REQUEST:
+		case RXRPC_CALL_SERVER_ACK_REQUEST:
+			_debug("start ACK timer");
+			rxrpc_propose_ACK(call, RXRPC_ACK_DELAY,
+					  call->ackr_serial, false);
+		default:
+			break;
+		}
+		goto maybe_reschedule;
+	}
+
+kill_ACKs:
+	del_timer_sync(&call->ack_timer);
+	if (test_and_clear_bit(RXRPC_CALL_ACK_FINAL, &call->events))
+		rxrpc_put_call(call);
+	clear_bit(RXRPC_CALL_ACK, &call->events);
+
+maybe_reschedule:
+	if (call->events || !skb_queue_empty(&call->rx_queue)) {
+		read_lock_bh(&call->state_lock);
+		if (call->state < RXRPC_CALL_DEAD)
+			rxrpc_queue_call(call);
+		read_unlock_bh(&call->state_lock);
+	}
+
+	/* don't leave aborted connections on the accept queue */
+	if (call->state >= RXRPC_CALL_COMPLETE &&
+	    !list_empty(&call->accept_link)) {
+		_debug("X unlinking once-pending call %p { e=%lx f=%lx c=%x }",
+		       call, call->events, call->flags,
+		       ntohl(call->conn->cid));
+
+		read_lock_bh(&call->state_lock);
+		if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) &&
+		    !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events))
+			rxrpc_queue_call(call);
+		read_unlock_bh(&call->state_lock);
+	}
+
+error:
+	clear_bit(RXRPC_CALL_PROC_BUSY, &call->flags);
+	kfree(acks);
+
+	/* because we don't want two CPUs both processing the work item for one
+	 * call at the same time, we use a flag to note when it's busy; however
+	 * this means there's a race between clearing the flag and setting the
+	 * work pending bit and the work item being processed again */
+	if (call->events && !work_pending(&call->processor)) {
+		_debug("jumpstart %x", ntohl(call->conn->cid));
+		rxrpc_queue_call(call);
+	}
+
+	_leave("");
+	return;
+
+no_mem:
+	_debug("out of memory");
+	goto maybe_reschedule;
+}
diff --git a/net/rxrpc/ar-call.c b/net/rxrpc/ar-call.c
new file mode 100644
index 0000000..4d92d88
--- /dev/null
+++ b/net/rxrpc/ar-call.c
@@ -0,0 +1,804 @@
+/* RxRPC individual remote procedure call handling
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#include <linux/module.h>
+#include <linux/circ_buf.h>
+#include <net/sock.h>
+#include <net/af_rxrpc.h>
+#include "ar-internal.h"
+
+struct kmem_cache *rxrpc_call_jar;
+LIST_HEAD(rxrpc_calls);
+DEFINE_RWLOCK(rxrpc_call_lock);
+static unsigned rxrpc_call_max_lifetime = 60;
+static unsigned rxrpc_dead_call_timeout = 2;
+
+static void rxrpc_destroy_call(struct work_struct *work);
+static void rxrpc_call_life_expired(unsigned long _call);
+static void rxrpc_dead_call_expired(unsigned long _call);
+static void rxrpc_ack_time_expired(unsigned long _call);
+static void rxrpc_resend_time_expired(unsigned long _call);
+
+/*
+ * allocate a new call
+ */
+static struct rxrpc_call *rxrpc_alloc_call(gfp_t gfp)
+{
+	struct rxrpc_call *call;
+
+	call = kmem_cache_zalloc(rxrpc_call_jar, gfp);
+	if (!call)
+		return NULL;
+
+	call->acks_winsz = 16;
+	call->acks_window = kmalloc(call->acks_winsz * sizeof(unsigned long),
+				    gfp);
+	if (!call->acks_window) {
+		kmem_cache_free(rxrpc_call_jar, call);
+		return NULL;
+	}
+
+	setup_timer(&call->lifetimer, &rxrpc_call_life_expired,
+		    (unsigned long) call);
+	setup_timer(&call->deadspan, &rxrpc_dead_call_expired,
+		    (unsigned long) call);
+	setup_timer(&call->ack_timer, &rxrpc_ack_time_expired,
+		    (unsigned long) call);
+	setup_timer(&call->resend_timer, &rxrpc_resend_time_expired,
+		    (unsigned long) call);
+	INIT_WORK(&call->destroyer, &rxrpc_destroy_call);
+	INIT_WORK(&call->processor, &rxrpc_process_call);
+	INIT_LIST_HEAD(&call->accept_link);
+	skb_queue_head_init(&call->rx_queue);
+	skb_queue_head_init(&call->rx_oos_queue);
+	init_waitqueue_head(&call->tx_waitq);
+	spin_lock_init(&call->lock);
+	rwlock_init(&call->state_lock);
+	atomic_set(&call->usage, 1);
+	call->debug_id = atomic_inc_return(&rxrpc_debug_id);
+	call->state = RXRPC_CALL_CLIENT_SEND_REQUEST;
+
+	memset(&call->sock_node, 0xed, sizeof(call->sock_node));
+
+	call->rx_data_expect = 1;
+	call->rx_data_eaten = 0;
+	call->rx_first_oos = 0;
+	call->ackr_win_top = call->rx_data_eaten + 1 + RXRPC_MAXACKS;
+	call->creation_jif = jiffies;
+	return call;
+}
+
+/*
+ * allocate a new client call and attempt to to get a connection slot for it
+ */
+static struct rxrpc_call *rxrpc_alloc_client_call(
+	struct rxrpc_sock *rx,
+	struct rxrpc_transport *trans,
+	struct rxrpc_conn_bundle *bundle,
+	gfp_t gfp)
+{
+	struct rxrpc_call *call;
+	int ret;
+
+	_enter("");
+
+	ASSERT(rx != NULL);
+	ASSERT(trans != NULL);
+	ASSERT(bundle != NULL);
+
+	call = rxrpc_alloc_call(gfp);
+	if (!call)
+		return ERR_PTR(-ENOMEM);
+
+	sock_hold(&rx->sk);
+	call->socket = rx;
+	call->rx_data_post = 1;
+
+	ret = rxrpc_connect_call(rx, trans, bundle, call, gfp);
+	if (ret < 0) {
+		kmem_cache_free(rxrpc_call_jar, call);
+		return ERR_PTR(ret);
+	}
+
+	spin_lock(&call->conn->trans->peer->lock);
+	list_add(&call->error_link, &call->conn->trans->peer->error_targets);
+	spin_unlock(&call->conn->trans->peer->lock);
+
+	call->lifetimer.expires = jiffies + rxrpc_call_max_lifetime * HZ;
+	add_timer(&call->lifetimer);
+
+	_leave(" = %p", call);
+	return call;
+}
+
+/*
+ * set up a call for the given data
+ * - called in process context with IRQs enabled
+ */
+struct rxrpc_call *rxrpc_get_client_call(struct rxrpc_sock *rx,
+					 struct rxrpc_transport *trans,
+					 struct rxrpc_conn_bundle *bundle,
+					 unsigned long user_call_ID,
+					 int create,
+					 gfp_t gfp)
+{
+	struct rxrpc_call *call, *candidate;
+	struct rb_node *p, *parent, **pp;
+
+	_enter("%p,%d,%d,%lx,%d",
+	       rx, trans ? trans->debug_id : -1, bundle ? bundle->debug_id : -1,
+	       user_call_ID, create);
+
+	/* search the extant calls first for one that matches the specified
+	 * user ID */
+	read_lock(&rx->call_lock);
+
+	p = rx->calls.rb_node;
+	while (p) {
+		call = rb_entry(p, struct rxrpc_call, sock_node);
+
+		if (user_call_ID < call->user_call_ID)
+			p = p->rb_left;
+		else if (user_call_ID > call->user_call_ID)
+			p = p->rb_right;
+		else
+			goto found_extant_call;
+	}
+
+	read_unlock(&rx->call_lock);
+
+	if (!create || !trans)
+		return ERR_PTR(-EBADSLT);
+
+	/* not yet present - create a candidate for a new record and then
+	 * redo the search */
+	candidate = rxrpc_alloc_client_call(rx, trans, bundle, gfp);
+	if (IS_ERR(candidate)) {
+		_leave(" = %ld", PTR_ERR(candidate));
+		return candidate;
+	}
+
+	candidate->user_call_ID = user_call_ID;
+	__set_bit(RXRPC_CALL_HAS_USERID, &candidate->flags);
+
+	write_lock(&rx->call_lock);
+
+	pp = &rx->calls.rb_node;
+	parent = NULL;
+	while (*pp) {
+		parent = *pp;
+		call = rb_entry(parent, struct rxrpc_call, sock_node);
+
+		if (user_call_ID < call->user_call_ID)
+			pp = &(*pp)->rb_left;
+		else if (user_call_ID > call->user_call_ID)
+			pp = &(*pp)->rb_right;
+		else
+			goto found_extant_second;
+	}
+
+	/* second search also failed; add the new call */
+	call = candidate;
+	candidate = NULL;
+	rxrpc_get_call(call);
+
+	rb_link_node(&call->sock_node, parent, pp);
+	rb_insert_color(&call->sock_node, &rx->calls);
+	write_unlock(&rx->call_lock);
+
+	write_lock_bh(&rxrpc_call_lock);
+	list_add_tail(&call->link, &rxrpc_calls);
+	write_unlock_bh(&rxrpc_call_lock);
+
+	_net("CALL new %d on CONN %d", call->debug_id, call->conn->debug_id);
+
+	_leave(" = %p [new]", call);
+	return call;
+
+	/* we found the call in the list immediately */
+found_extant_call:
+	rxrpc_get_call(call);
+	read_unlock(&rx->call_lock);
+	_leave(" = %p [extant %d]", call, atomic_read(&call->usage));
+	return call;
+
+	/* we found the call on the second time through the list */
+found_extant_second:
+	rxrpc_get_call(call);
+	write_unlock(&rx->call_lock);
+	rxrpc_put_call(candidate);
+	_leave(" = %p [second %d]", call, atomic_read(&call->usage));
+	return call;
+}
+
+/*
+ * set up an incoming call
+ * - called in process context with IRQs enabled
+ */
+struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx,
+				       struct rxrpc_connection *conn,
+				       struct rxrpc_header *hdr,
+				       gfp_t gfp)
+{
+	struct rxrpc_call *call, *candidate;
+	struct rb_node **p, *parent;
+	__be32 call_id;
+
+	_enter(",%d,,%x", conn->debug_id, gfp);
+
+	ASSERT(rx != NULL);
+
+	candidate = rxrpc_alloc_call(gfp);
+	if (!candidate)
+		return ERR_PTR(-EBUSY);
+
+	candidate->socket = rx;
+	candidate->conn = conn;
+	candidate->cid = hdr->cid;
+	candidate->call_id = hdr->callNumber;
+	candidate->channel = ntohl(hdr->cid) & RXRPC_CHANNELMASK;
+	candidate->rx_data_post = 0;
+	candidate->state = RXRPC_CALL_SERVER_ACCEPTING;
+	if (conn->security_ix > 0)
+		candidate->state = RXRPC_CALL_SERVER_SECURING;
+
+	write_lock_bh(&conn->lock);
+
+	/* set the channel for this call */
+	call = conn->channels[candidate->channel];
+	_debug("channel[%u] is %p", candidate->channel, call);
+	if (call && call->call_id == hdr->callNumber) {
+		/* already set; must've been a duplicate packet */
+		_debug("extant call [%d]", call->state);
+		ASSERTCMP(call->conn, ==, conn);
+
+		read_lock(&call->state_lock);
+		switch (call->state) {
+		case RXRPC_CALL_LOCALLY_ABORTED:
+			if (!test_and_set_bit(RXRPC_CALL_ABORT, &call->events))
+				rxrpc_queue_call(call);
+		case RXRPC_CALL_REMOTELY_ABORTED:
+			read_unlock(&call->state_lock);
+			goto aborted_call;
+		default:
+			rxrpc_get_call(call);
+			read_unlock(&call->state_lock);
+			goto extant_call;
+		}
+	}
+
+	if (call) {
+		/* it seems the channel is still in use from the previous call
+		 * - ditch the old binding if its call is now complete */
+		_debug("CALL: %u { %s }",
+		       call->debug_id, rxrpc_call_states[call->state]);
+
+		if (call->state >= RXRPC_CALL_COMPLETE) {
+			conn->channels[call->channel] = NULL;
+		} else {
+			write_unlock_bh(&conn->lock);
+			kmem_cache_free(rxrpc_call_jar, candidate);
+			_leave(" = -EBUSY");
+			return ERR_PTR(-EBUSY);
+		}
+	}
+
+	/* check the call number isn't duplicate */
+	_debug("check dup");
+	call_id = hdr->callNumber;
+	p = &conn->calls.rb_node;
+	parent = NULL;
+	while (*p) {
+		parent = *p;
+		call = rb_entry(parent, struct rxrpc_call, conn_node);
+
+		if (call_id < call->call_id)
+			p = &(*p)->rb_left;
+		else if (call_id > call->call_id)
+			p = &(*p)->rb_right;
+		else
+			goto old_call;
+	}
+
+	/* make the call available */
+	_debug("new call");
+	call = candidate;
+	candidate = NULL;
+	rb_link_node(&call->conn_node, parent, p);
+	rb_insert_color(&call->conn_node, &conn->calls);
+	conn->channels[call->channel] = call;
+	sock_hold(&rx->sk);
+	atomic_inc(&conn->usage);
+	write_unlock_bh(&conn->lock);
+
+	spin_lock(&conn->trans->peer->lock);
+	list_add(&call->error_link, &conn->trans->peer->error_targets);
+	spin_unlock(&conn->trans->peer->lock);
+
+	write_lock_bh(&rxrpc_call_lock);
+	list_add_tail(&call->link, &rxrpc_calls);
+	write_unlock_bh(&rxrpc_call_lock);
+
+	_net("CALL incoming %d on CONN %d", call->debug_id, call->conn->debug_id);
+
+	call->lifetimer.expires = jiffies + rxrpc_call_max_lifetime * HZ;
+	add_timer(&call->lifetimer);
+	_leave(" = %p {%d} [new]", call, call->debug_id);
+	return call;
+
+extant_call:
+	write_unlock_bh(&conn->lock);
+	kmem_cache_free(rxrpc_call_jar, candidate);
+	_leave(" = %p {%d} [extant]", call, call ? call->debug_id : -1);
+	return call;
+
+aborted_call:
+	write_unlock_bh(&conn->lock);
+	kmem_cache_free(rxrpc_call_jar, candidate);
+	_leave(" = -ECONNABORTED");
+	return ERR_PTR(-ECONNABORTED);
+
+old_call:
+	write_unlock_bh(&conn->lock);
+	kmem_cache_free(rxrpc_call_jar, candidate);
+	_leave(" = -ECONNRESET [old]");
+	return ERR_PTR(-ECONNRESET);
+}
+
+/*
+ * find an extant server call
+ * - called in process context with IRQs enabled
+ */
+struct rxrpc_call *rxrpc_find_server_call(struct rxrpc_sock *rx,
+					  unsigned long user_call_ID)
+{
+	struct rxrpc_call *call;
+	struct rb_node *p;
+
+	_enter("%p,%lx", rx, user_call_ID);
+
+	/* search the extant calls for one that matches the specified user
+	 * ID */
+	read_lock(&rx->call_lock);
+
+	p = rx->calls.rb_node;
+	while (p) {
+		call = rb_entry(p, struct rxrpc_call, sock_node);
+
+		if (user_call_ID < call->user_call_ID)
+			p = p->rb_left;
+		else if (user_call_ID > call->user_call_ID)
+			p = p->rb_right;
+		else
+			goto found_extant_call;
+	}
+
+	read_unlock(&rx->call_lock);
+	_leave(" = NULL");
+	return NULL;
+
+	/* we found the call in the list immediately */
+found_extant_call:
+	rxrpc_get_call(call);
+	read_unlock(&rx->call_lock);
+	_leave(" = %p [%d]", call, atomic_read(&call->usage));
+	return call;
+}
+
+/*
+ * detach a call from a socket and set up for release
+ */
+void rxrpc_release_call(struct rxrpc_call *call)
+{
+	struct rxrpc_connection *conn = call->conn;
+	struct rxrpc_sock *rx = call->socket;
+
+	_enter("{%d,%d,%d,%d}",
+	       call->debug_id, atomic_read(&call->usage),
+	       atomic_read(&call->ackr_not_idle),
+	       call->rx_first_oos);
+
+	spin_lock_bh(&call->lock);
+	if (test_and_set_bit(RXRPC_CALL_RELEASED, &call->flags))
+		BUG();
+	spin_unlock_bh(&call->lock);
+
+	/* dissociate from the socket
+	 * - the socket's ref on the call is passed to the death timer
+	 */
+	_debug("RELEASE CALL %p (%d CONN %p)", call, call->debug_id, conn);
+
+	write_lock_bh(&rx->call_lock);
+	if (!list_empty(&call->accept_link)) {
+		_debug("unlinking once-pending call %p { e=%lx f=%lx }",
+		       call, call->events, call->flags);
+		ASSERT(!test_bit(RXRPC_CALL_HAS_USERID, &call->flags));
+		list_del_init(&call->accept_link);
+		sk_acceptq_removed(&rx->sk);
+	} else if (test_bit(RXRPC_CALL_HAS_USERID, &call->flags)) {
+		rb_erase(&call->sock_node, &rx->calls);
+		memset(&call->sock_node, 0xdd, sizeof(call->sock_node));
+		clear_bit(RXRPC_CALL_HAS_USERID, &call->flags);
+	}
+	write_unlock_bh(&rx->call_lock);
+
+	/* free up the channel for reuse */
+	spin_lock(&conn->trans->client_lock);
+	write_lock_bh(&conn->lock);
+	write_lock(&call->state_lock);
+
+	if (conn->channels[call->channel] == call)
+		conn->channels[call->channel] = NULL;
+
+	if (conn->out_clientflag && conn->bundle) {
+		conn->avail_calls++;
+		switch (conn->avail_calls) {
+		case 1:
+			list_move_tail(&conn->bundle_link,
+				       &conn->bundle->avail_conns);
+		case 2 ... RXRPC_MAXCALLS - 1:
+			ASSERT(conn->channels[0] == NULL ||
+			       conn->channels[1] == NULL ||
+			       conn->channels[2] == NULL ||
+			       conn->channels[3] == NULL);
+			break;
+		case RXRPC_MAXCALLS:
+			list_move_tail(&conn->bundle_link,
+				       &conn->bundle->unused_conns);
+			ASSERT(conn->channels[0] == NULL &&
+			       conn->channels[1] == NULL &&
+			       conn->channels[2] == NULL &&
+			       conn->channels[3] == NULL);
+			break;
+		default:
+			printk(KERN_ERR "RxRPC: conn->avail_calls=%d\n",
+			       conn->avail_calls);
+			BUG();
+		}
+	}
+
+	spin_unlock(&conn->trans->client_lock);
+
+	if (call->state < RXRPC_CALL_COMPLETE &&
+	    call->state != RXRPC_CALL_CLIENT_FINAL_ACK) {
+		_debug("+++ ABORTING STATE %d +++\n", call->state);
+		call->state = RXRPC_CALL_LOCALLY_ABORTED;
+		call->abort_code = RX_CALL_DEAD;
+		set_bit(RXRPC_CALL_ABORT, &call->events);
+		rxrpc_queue_call(call);
+	}
+	write_unlock(&call->state_lock);
+	write_unlock_bh(&conn->lock);
+
+	/* clean up the Rx queue */
+	if (!skb_queue_empty(&call->rx_queue) ||
+	    !skb_queue_empty(&call->rx_oos_queue)) {
+		struct rxrpc_skb_priv *sp;
+		struct sk_buff *skb;
+
+		_debug("purge Rx queues");
+
+		spin_lock_bh(&call->lock);
+		while ((skb = skb_dequeue(&call->rx_queue)) ||
+		       (skb = skb_dequeue(&call->rx_oos_queue))) {
+			sp = rxrpc_skb(skb);
+			if (sp->call) {
+				ASSERTCMP(sp->call, ==, call);
+				rxrpc_put_call(call);
+				sp->call = NULL;
+			}
+			skb->destructor = NULL;
+			spin_unlock_bh(&call->lock);
+
+			_debug("- zap %s %%%u #%u",
+			       rxrpc_pkts[sp->hdr.type],
+			       ntohl(sp->hdr.serial),
+			       ntohl(sp->hdr.seq));
+			rxrpc_free_skb(skb);
+			spin_lock_bh(&call->lock);
+		}
+		spin_unlock_bh(&call->lock);
+
+		ASSERTCMP(call->state, !=, RXRPC_CALL_COMPLETE);
+	}
+
+	del_timer_sync(&call->resend_timer);
+	del_timer_sync(&call->ack_timer);
+	del_timer_sync(&call->lifetimer);
+	call->deadspan.expires = jiffies + rxrpc_dead_call_timeout * HZ;
+	add_timer(&call->deadspan);
+
+	_leave("");
+}
+
+/*
+ * handle a dead call being ready for reaping
+ */
+static void rxrpc_dead_call_expired(unsigned long _call)
+{
+	struct rxrpc_call *call = (struct rxrpc_call *) _call;
+
+	_enter("{%d}", call->debug_id);
+
+	write_lock_bh(&call->state_lock);
+	call->state = RXRPC_CALL_DEAD;
+	write_unlock_bh(&call->state_lock);
+	rxrpc_put_call(call);
+}
+
+/*
+ * mark a call as to be released, aborting it if it's still in progress
+ * - called with softirqs disabled
+ */
+static void rxrpc_mark_call_released(struct rxrpc_call *call)
+{
+	bool sched;
+
+	write_lock(&call->state_lock);
+	if (call->state < RXRPC_CALL_DEAD) {
+		sched = false;
+		if (call->state < RXRPC_CALL_COMPLETE) {
+			_debug("abort call %p", call);
+			call->state = RXRPC_CALL_LOCALLY_ABORTED;
+			call->abort_code = RX_CALL_DEAD;
+			if (!test_and_set_bit(RXRPC_CALL_ABORT, &call->events))
+				sched = true;
+		}
+		if (!test_and_set_bit(RXRPC_CALL_RELEASE, &call->events))
+			sched = true;
+		if (sched)
+			rxrpc_queue_call(call);
+	}
+	write_unlock(&call->state_lock);
+}
+
+/*
+ * release all the calls associated with a socket
+ */
+void rxrpc_release_calls_on_socket(struct rxrpc_sock *rx)
+{
+	struct rxrpc_call *call;
+	struct rb_node *p;
+
+	_enter("%p", rx);
+
+	read_lock_bh(&rx->call_lock);
+
+	/* mark all the calls as no longer wanting incoming packets */
+	for (p = rb_first(&rx->calls); p; p = rb_next(p)) {
+		call = rb_entry(p, struct rxrpc_call, sock_node);
+		rxrpc_mark_call_released(call);
+	}
+
+	/* kill the not-yet-accepted incoming calls */
+	list_for_each_entry(call, &rx->secureq, accept_link) {
+		rxrpc_mark_call_released(call);
+	}
+
+	list_for_each_entry(call, &rx->acceptq, accept_link) {
+		rxrpc_mark_call_released(call);
+	}
+
+	read_unlock_bh(&rx->call_lock);
+	_leave("");
+}
+
+/*
+ * release a call
+ */
+void __rxrpc_put_call(struct rxrpc_call *call)
+{
+	ASSERT(call != NULL);
+
+	_enter("%p{u=%d}", call, atomic_read(&call->usage));
+
+	ASSERTCMP(atomic_read(&call->usage), >, 0);
+
+	if (atomic_dec_and_test(&call->usage)) {
+		_debug("call %d dead", call->debug_id);
+		ASSERTCMP(call->state, ==, RXRPC_CALL_DEAD);
+		rxrpc_queue_work(&call->destroyer);
+	}
+	_leave("");
+}
+
+/*
+ * clean up a call
+ */
+static void rxrpc_cleanup_call(struct rxrpc_call *call)
+{
+	_net("DESTROY CALL %d", call->debug_id);
+
+	ASSERT(call->socket);
+
+	memset(&call->sock_node, 0xcd, sizeof(call->sock_node));
+
+	del_timer_sync(&call->lifetimer);
+	del_timer_sync(&call->deadspan);
+	del_timer_sync(&call->ack_timer);
+	del_timer_sync(&call->resend_timer);
+
+	ASSERT(test_bit(RXRPC_CALL_RELEASED, &call->flags));
+	ASSERTCMP(call->events, ==, 0);
+	if (work_pending(&call->processor)) {
+		_debug("defer destroy");
+		rxrpc_queue_work(&call->destroyer);
+		return;
+	}
+
+	if (call->conn) {
+		spin_lock(&call->conn->trans->peer->lock);
+		list_del(&call->error_link);
+		spin_unlock(&call->conn->trans->peer->lock);
+
+		write_lock_bh(&call->conn->lock);
+		rb_erase(&call->conn_node, &call->conn->calls);
+		write_unlock_bh(&call->conn->lock);
+		rxrpc_put_connection(call->conn);
+	}
+
+	if (call->acks_window) {
+		_debug("kill Tx window %d",
+		       CIRC_CNT(call->acks_head, call->acks_tail,
+				call->acks_winsz));
+		smp_mb();
+		while (CIRC_CNT(call->acks_head, call->acks_tail,
+				call->acks_winsz) > 0) {
+			struct rxrpc_skb_priv *sp;
+			unsigned long _skb;
+
+			_skb = call->acks_window[call->acks_tail] & ~1;
+			sp = rxrpc_skb((struct sk_buff *) _skb);
+			_debug("+++ clear Tx %u", ntohl(sp->hdr.seq));
+			rxrpc_free_skb((struct sk_buff *) _skb);
+			call->acks_tail =
+				(call->acks_tail + 1) & (call->acks_winsz - 1);
+		}
+
+		kfree(call->acks_window);
+	}
+
+	rxrpc_free_skb(call->tx_pending);
+
+	rxrpc_purge_queue(&call->rx_queue);
+	ASSERT(skb_queue_empty(&call->rx_oos_queue));
+	sock_put(&call->socket->sk);
+	kmem_cache_free(rxrpc_call_jar, call);
+}
+
+/*
+ * destroy a call
+ */
+static void rxrpc_destroy_call(struct work_struct *work)
+{
+	struct rxrpc_call *call =
+		container_of(work, struct rxrpc_call, destroyer);
+
+	_enter("%p{%d,%d,%p}",
+	       call, atomic_read(&call->usage), call->channel, call->conn);
+
+	ASSERTCMP(call->state, ==, RXRPC_CALL_DEAD);
+
+	write_lock_bh(&rxrpc_call_lock);
+	list_del_init(&call->link);
+	write_unlock_bh(&rxrpc_call_lock);
+
+	rxrpc_cleanup_call(call);
+	_leave("");
+}
+
+/*
+ * preemptively destroy all the call records from a transport endpoint rather
+ * than waiting for them to time out
+ */
+void __exit rxrpc_destroy_all_calls(void)
+{
+	struct rxrpc_call *call;
+
+	_enter("");
+	write_lock_bh(&rxrpc_call_lock);
+
+	while (!list_empty(&rxrpc_calls)) {
+		call = list_entry(rxrpc_calls.next, struct rxrpc_call, link);
+		_debug("Zapping call %p", call);
+
+		list_del_init(&call->link);
+
+		switch (atomic_read(&call->usage)) {
+		case 0:
+			ASSERTCMP(call->state, ==, RXRPC_CALL_DEAD);
+			break;
+		case 1:
+			if (del_timer_sync(&call->deadspan) != 0 &&
+			    call->state != RXRPC_CALL_DEAD)
+				rxrpc_dead_call_expired((unsigned long) call);
+			if (call->state != RXRPC_CALL_DEAD)
+				break;
+		default:
+			printk(KERN_ERR "RXRPC:"
+			       " Call %p still in use (%d,%d,%s,%lx,%lx)!\n",
+			       call, atomic_read(&call->usage),
+			       atomic_read(&call->ackr_not_idle),
+			       rxrpc_call_states[call->state],
+			       call->flags, call->events);
+			if (!skb_queue_empty(&call->rx_queue))
+				printk(KERN_ERR"RXRPC: Rx queue occupied\n");
+			if (!skb_queue_empty(&call->rx_oos_queue))
+				printk(KERN_ERR"RXRPC: OOS queue occupied\n");
+			break;
+		}
+
+		write_unlock_bh(&rxrpc_call_lock);
+		cond_resched();
+		write_lock_bh(&rxrpc_call_lock);
+	}
+
+	write_unlock_bh(&rxrpc_call_lock);
+	_leave("");
+}
+
+/*
+ * handle call lifetime being exceeded
+ */
+static void rxrpc_call_life_expired(unsigned long _call)
+{
+	struct rxrpc_call *call = (struct rxrpc_call *) _call;
+
+	if (call->state >= RXRPC_CALL_COMPLETE)
+		return;
+
+	_enter("{%d}", call->debug_id);
+	read_lock_bh(&call->state_lock);
+	if (call->state < RXRPC_CALL_COMPLETE) {
+		set_bit(RXRPC_CALL_LIFE_TIMER, &call->events);
+		rxrpc_queue_call(call);
+	}
+	read_unlock_bh(&call->state_lock);
+}
+
+/*
+ * handle resend timer expiry
+ */
+static void rxrpc_resend_time_expired(unsigned long _call)
+{
+	struct rxrpc_call *call = (struct rxrpc_call *) _call;
+
+	_enter("{%d}", call->debug_id);
+
+	if (call->state >= RXRPC_CALL_COMPLETE)
+		return;
+
+	read_lock_bh(&call->state_lock);
+	clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags);
+	if (call->state < RXRPC_CALL_COMPLETE &&
+	    !test_and_set_bit(RXRPC_CALL_RESEND_TIMER, &call->events))
+		rxrpc_queue_call(call);
+	read_unlock_bh(&call->state_lock);
+}
+
+/*
+ * handle ACK timer expiry
+ */
+static void rxrpc_ack_time_expired(unsigned long _call)
+{
+	struct rxrpc_call *call = (struct rxrpc_call *) _call;
+
+	_enter("{%d}", call->debug_id);
+
+	if (call->state >= RXRPC_CALL_COMPLETE)
+		return;
+
+	read_lock_bh(&call->state_lock);
+	if (call->state < RXRPC_CALL_COMPLETE &&
+	    !test_and_set_bit(RXRPC_CALL_ACK, &call->events))
+		rxrpc_queue_call(call);
+	read_unlock_bh(&call->state_lock);
+}
diff --git a/net/rxrpc/ar-connection.c b/net/rxrpc/ar-connection.c
new file mode 100644
index 0000000..43cb3e0
--- /dev/null
+++ b/net/rxrpc/ar-connection.c
@@ -0,0 +1,911 @@
+/* RxRPC virtual connection handler
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/crypto.h>
+#include <net/sock.h>
+#include <net/af_rxrpc.h>
+#include "ar-internal.h"
+
+static void rxrpc_connection_reaper(struct work_struct *work);
+
+LIST_HEAD(rxrpc_connections);
+DEFINE_RWLOCK(rxrpc_connection_lock);
+static unsigned long rxrpc_connection_timeout = 10 * 60;
+static DECLARE_DELAYED_WORK(rxrpc_connection_reap, rxrpc_connection_reaper);
+
+/*
+ * allocate a new client connection bundle
+ */
+static struct rxrpc_conn_bundle *rxrpc_alloc_bundle(gfp_t gfp)
+{
+	struct rxrpc_conn_bundle *bundle;
+
+	_enter("");
+
+	bundle = kzalloc(sizeof(struct rxrpc_conn_bundle), gfp);
+	if (bundle) {
+		INIT_LIST_HEAD(&bundle->unused_conns);
+		INIT_LIST_HEAD(&bundle->avail_conns);
+		INIT_LIST_HEAD(&bundle->busy_conns);
+		init_waitqueue_head(&bundle->chanwait);
+		atomic_set(&bundle->usage, 1);
+	}
+
+	_leave(" = %p", bundle);
+	return bundle;
+}
+
+/*
+ * compare bundle parameters with what we're looking for
+ * - return -ve, 0 or +ve
+ */
+static inline
+int rxrpc_cmp_bundle(const struct rxrpc_conn_bundle *bundle,
+		     struct key *key, __be16 service_id)
+{
+	return (bundle->service_id - service_id) ?:
+		((unsigned long) bundle->key - (unsigned long) key);
+}
+
+/*
+ * get bundle of client connections that a client socket can make use of
+ */
+struct rxrpc_conn_bundle *rxrpc_get_bundle(struct rxrpc_sock *rx,
+					   struct rxrpc_transport *trans,
+					   struct key *key,
+					   __be16 service_id,
+					   gfp_t gfp)
+{
+	struct rxrpc_conn_bundle *bundle, *candidate;
+	struct rb_node *p, *parent, **pp;
+
+	_enter("%p{%x},%x,%hx,",
+	       rx, key_serial(key), trans->debug_id, ntohl(service_id));
+
+	if (rx->trans == trans && rx->bundle) {
+		atomic_inc(&rx->bundle->usage);
+		return rx->bundle;
+	}
+
+	/* search the extant bundles first for one that matches the specified
+	 * user ID */
+	spin_lock(&trans->client_lock);
+
+	p = trans->bundles.rb_node;
+	while (p) {
+		bundle = rb_entry(p, struct rxrpc_conn_bundle, node);
+
+		if (rxrpc_cmp_bundle(bundle, key, service_id) < 0)
+			p = p->rb_left;
+		else if (rxrpc_cmp_bundle(bundle, key, service_id) > 0)
+			p = p->rb_right;
+		else
+			goto found_extant_bundle;
+	}
+
+	spin_unlock(&trans->client_lock);
+
+	/* not yet present - create a candidate for a new record and then
+	 * redo the search */
+	candidate = rxrpc_alloc_bundle(gfp);
+	if (!candidate) {
+		_leave(" = -ENOMEM");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	candidate->key = key_get(key);
+	candidate->service_id = service_id;
+
+	spin_lock(&trans->client_lock);
+
+	pp = &trans->bundles.rb_node;
+	parent = NULL;
+	while (*pp) {
+		parent = *pp;
+		bundle = rb_entry(parent, struct rxrpc_conn_bundle, node);
+
+		if (rxrpc_cmp_bundle(bundle, key, service_id) < 0)
+			pp = &(*pp)->rb_left;
+		else if (rxrpc_cmp_bundle(bundle, key, service_id) > 0)
+			pp = &(*pp)->rb_right;
+		else
+			goto found_extant_second;
+	}
+
+	/* second search also failed; add the new bundle */
+	bundle = candidate;
+	candidate = NULL;
+
+	rb_link_node(&bundle->node, parent, pp);
+	rb_insert_color(&bundle->node, &trans->bundles);
+	spin_unlock(&trans->client_lock);
+	_net("BUNDLE new on trans %d", trans->debug_id);
+	if (!rx->bundle && rx->sk.sk_state == RXRPC_CLIENT_CONNECTED) {
+		atomic_inc(&bundle->usage);
+		rx->bundle = bundle;
+	}
+	_leave(" = %p [new]", bundle);
+	return bundle;
+
+	/* we found the bundle in the list immediately */
+found_extant_bundle:
+	atomic_inc(&bundle->usage);
+	spin_unlock(&trans->client_lock);
+	_net("BUNDLE old on trans %d", trans->debug_id);
+	if (!rx->bundle && rx->sk.sk_state == RXRPC_CLIENT_CONNECTED) {
+		atomic_inc(&bundle->usage);
+		rx->bundle = bundle;
+	}
+	_leave(" = %p [extant %d]", bundle, atomic_read(&bundle->usage));
+	return bundle;
+
+	/* we found the bundle on the second time through the list */
+found_extant_second:
+	atomic_inc(&bundle->usage);
+	spin_unlock(&trans->client_lock);
+	kfree(candidate);
+	_net("BUNDLE old2 on trans %d", trans->debug_id);
+	if (!rx->bundle && rx->sk.sk_state == RXRPC_CLIENT_CONNECTED) {
+		atomic_inc(&bundle->usage);
+		rx->bundle = bundle;
+	}
+	_leave(" = %p [second %d]", bundle, atomic_read(&bundle->usage));
+	return bundle;
+}
+
+/*
+ * release a bundle
+ */
+void rxrpc_put_bundle(struct rxrpc_transport *trans,
+		      struct rxrpc_conn_bundle *bundle)
+{
+	_enter("%p,%p{%d}",trans, bundle, atomic_read(&bundle->usage));
+
+	if (atomic_dec_and_lock(&bundle->usage, &trans->client_lock)) {
+		_debug("Destroy bundle");
+		rb_erase(&bundle->node, &trans->bundles);
+		spin_unlock(&trans->client_lock);
+		ASSERT(list_empty(&bundle->unused_conns));
+		ASSERT(list_empty(&bundle->avail_conns));
+		ASSERT(list_empty(&bundle->busy_conns));
+		ASSERTCMP(bundle->num_conns, ==, 0);
+		key_put(bundle->key);
+		kfree(bundle);
+	}
+
+	_leave("");
+}
+
+/*
+ * allocate a new connection
+ */
+static struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp)
+{
+	struct rxrpc_connection *conn;
+
+	_enter("");
+
+	conn = kzalloc(sizeof(struct rxrpc_connection), gfp);
+	if (conn) {
+		INIT_WORK(&conn->processor, &rxrpc_process_connection);
+		INIT_LIST_HEAD(&conn->bundle_link);
+		conn->calls = RB_ROOT;
+		skb_queue_head_init(&conn->rx_queue);
+		rwlock_init(&conn->lock);
+		spin_lock_init(&conn->state_lock);
+		atomic_set(&conn->usage, 1);
+		conn->debug_id = atomic_inc_return(&rxrpc_debug_id);
+		conn->avail_calls = RXRPC_MAXCALLS;
+		conn->size_align = 4;
+		conn->header_size = sizeof(struct rxrpc_header);
+	}
+
+	_leave(" = %p{%d}", conn, conn->debug_id);
+	return conn;
+}
+
+/*
+ * assign a connection ID to a connection and add it to the transport's
+ * connection lookup tree
+ * - called with transport client lock held
+ */
+static void rxrpc_assign_connection_id(struct rxrpc_connection *conn)
+{
+	struct rxrpc_connection *xconn;
+	struct rb_node *parent, **p;
+	__be32 epoch;
+	u32 real_conn_id;
+
+	_enter("");
+
+	epoch = conn->epoch;
+
+	write_lock_bh(&conn->trans->conn_lock);
+
+	conn->trans->conn_idcounter += RXRPC_CID_INC;
+	if (conn->trans->conn_idcounter < RXRPC_CID_INC)
+		conn->trans->conn_idcounter = RXRPC_CID_INC;
+	real_conn_id = conn->trans->conn_idcounter;
+
+attempt_insertion:
+	parent = NULL;
+	p = &conn->trans->client_conns.rb_node;
+
+	while (*p) {
+		parent = *p;
+		xconn = rb_entry(parent, struct rxrpc_connection, node);
+
+		if (epoch < xconn->epoch)
+			p = &(*p)->rb_left;
+		else if (epoch > xconn->epoch)
+			p = &(*p)->rb_right;
+		else if (real_conn_id < xconn->real_conn_id)
+			p = &(*p)->rb_left;
+		else if (real_conn_id > xconn->real_conn_id)
+			p = &(*p)->rb_right;
+		else
+			goto id_exists;
+	}
+
+	/* we've found a suitable hole - arrange for this connection to occupy
+	 * it */
+	rb_link_node(&conn->node, parent, p);
+	rb_insert_color(&conn->node, &conn->trans->client_conns);
+
+	conn->real_conn_id = real_conn_id;
+	conn->cid = htonl(real_conn_id);
+	write_unlock_bh(&conn->trans->conn_lock);
+	_leave(" [CONNID %x CID %x]", real_conn_id, ntohl(conn->cid));
+	return;
+
+	/* we found a connection with the proposed ID - walk the tree from that
+	 * point looking for the next unused ID */
+id_exists:
+	for (;;) {
+		real_conn_id += RXRPC_CID_INC;
+		if (real_conn_id < RXRPC_CID_INC) {
+			real_conn_id = RXRPC_CID_INC;
+			conn->trans->conn_idcounter = real_conn_id;
+			goto attempt_insertion;
+		}
+
+		parent = rb_next(parent);
+		if (!parent)
+			goto attempt_insertion;
+
+		xconn = rb_entry(parent, struct rxrpc_connection, node);
+		if (epoch < xconn->epoch ||
+		    real_conn_id < xconn->real_conn_id)
+			goto attempt_insertion;
+	}
+}
+
+/*
+ * add a call to a connection's call-by-ID tree
+ */
+static void rxrpc_add_call_ID_to_conn(struct rxrpc_connection *conn,
+				      struct rxrpc_call *call)
+{
+	struct rxrpc_call *xcall;
+	struct rb_node *parent, **p;
+	__be32 call_id;
+
+	write_lock_bh(&conn->lock);
+
+	call_id = call->call_id;
+	p = &conn->calls.rb_node;
+	parent = NULL;
+	while (*p) {
+		parent = *p;
+		xcall = rb_entry(parent, struct rxrpc_call, conn_node);
+
+		if (call_id < xcall->call_id)
+			p = &(*p)->rb_left;
+		else if (call_id > xcall->call_id)
+			p = &(*p)->rb_right;
+		else
+			BUG();
+	}
+
+	rb_link_node(&call->conn_node, parent, p);
+	rb_insert_color(&call->conn_node, &conn->calls);
+
+	write_unlock_bh(&conn->lock);
+}
+
+/*
+ * connect a call on an exclusive connection
+ */
+static int rxrpc_connect_exclusive(struct rxrpc_sock *rx,
+				   struct rxrpc_transport *trans,
+				   __be16 service_id,
+				   struct rxrpc_call *call,
+				   gfp_t gfp)
+{
+	struct rxrpc_connection *conn;
+	int chan, ret;
+
+	_enter("");
+
+	conn = rx->conn;
+	if (!conn) {
+		/* not yet present - create a candidate for a new connection
+		 * and then redo the check */
+		conn = rxrpc_alloc_connection(gfp);
+		if (IS_ERR(conn)) {
+			_leave(" = %ld", PTR_ERR(conn));
+			return PTR_ERR(conn);
+		}
+
+		conn->trans = trans;
+		conn->bundle = NULL;
+		conn->service_id = service_id;
+		conn->epoch = rxrpc_epoch;
+		conn->in_clientflag = 0;
+		conn->out_clientflag = RXRPC_CLIENT_INITIATED;
+		conn->cid = 0;
+		conn->state = RXRPC_CONN_CLIENT;
+		conn->avail_calls = RXRPC_MAXCALLS - 1;
+		conn->security_level = rx->min_sec_level;
+		conn->key = key_get(rx->key);
+
+		ret = rxrpc_init_client_conn_security(conn);
+		if (ret < 0) {
+			key_put(conn->key);
+			kfree(conn);
+			_leave(" = %d [key]", ret);
+			return ret;
+		}
+
+		write_lock_bh(&rxrpc_connection_lock);
+		list_add_tail(&conn->link, &rxrpc_connections);
+		write_unlock_bh(&rxrpc_connection_lock);
+
+		spin_lock(&trans->client_lock);
+		atomic_inc(&trans->usage);
+
+		_net("CONNECT EXCL new %d on TRANS %d",
+		     conn->debug_id, conn->trans->debug_id);
+
+		rxrpc_assign_connection_id(conn);
+		rx->conn = conn;
+	}
+
+	/* we've got a connection with a free channel and we can now attach the
+	 * call to it
+	 * - we're holding the transport's client lock
+	 * - we're holding a reference on the connection
+	 */
+	for (chan = 0; chan < RXRPC_MAXCALLS; chan++)
+		if (!conn->channels[chan])
+			goto found_channel;
+	goto no_free_channels;
+
+found_channel:
+	atomic_inc(&conn->usage);
+	conn->channels[chan] = call;
+	call->conn = conn;
+	call->channel = chan;
+	call->cid = conn->cid | htonl(chan);
+	call->call_id = htonl(++conn->call_counter);
+
+	_net("CONNECT client on conn %d chan %d as call %x",
+	     conn->debug_id, chan, ntohl(call->call_id));
+
+	spin_unlock(&trans->client_lock);
+
+	rxrpc_add_call_ID_to_conn(conn, call);
+	_leave(" = 0");
+	return 0;
+
+no_free_channels:
+	spin_unlock(&trans->client_lock);
+	_leave(" = -ENOSR");
+	return -ENOSR;
+}
+
+/*
+ * find a connection for a call
+ * - called in process context with IRQs enabled
+ */
+int rxrpc_connect_call(struct rxrpc_sock *rx,
+		       struct rxrpc_transport *trans,
+		       struct rxrpc_conn_bundle *bundle,
+		       struct rxrpc_call *call,
+		       gfp_t gfp)
+{
+	struct rxrpc_connection *conn, *candidate;
+	int chan, ret;
+
+	DECLARE_WAITQUEUE(myself, current);
+
+	_enter("%p,%lx,", rx, call->user_call_ID);
+
+	if (test_bit(RXRPC_SOCK_EXCLUSIVE_CONN, &rx->flags))
+		return rxrpc_connect_exclusive(rx, trans, bundle->service_id,
+					       call, gfp);
+
+	spin_lock(&trans->client_lock);
+	for (;;) {
+		/* see if the bundle has a call slot available */
+		if (!list_empty(&bundle->avail_conns)) {
+			_debug("avail");
+			conn = list_entry(bundle->avail_conns.next,
+					  struct rxrpc_connection,
+					  bundle_link);
+			if (--conn->avail_calls == 0)
+				list_move(&conn->bundle_link,
+					  &bundle->busy_conns);
+			ASSERTCMP(conn->avail_calls, <, RXRPC_MAXCALLS);
+			ASSERT(conn->channels[0] == NULL ||
+			       conn->channels[1] == NULL ||
+			       conn->channels[2] == NULL ||
+			       conn->channels[3] == NULL);
+			atomic_inc(&conn->usage);
+			break;
+		}
+
+		if (!list_empty(&bundle->unused_conns)) {
+			_debug("unused");
+			conn = list_entry(bundle->unused_conns.next,
+					  struct rxrpc_connection,
+					  bundle_link);
+			ASSERTCMP(conn->avail_calls, ==, RXRPC_MAXCALLS);
+			conn->avail_calls = RXRPC_MAXCALLS - 1;
+			ASSERT(conn->channels[0] == NULL &&
+			       conn->channels[1] == NULL &&
+			       conn->channels[2] == NULL &&
+			       conn->channels[3] == NULL);
+			atomic_inc(&conn->usage);
+			list_move(&conn->bundle_link, &bundle->avail_conns);
+			break;
+		}
+
+		/* need to allocate a new connection */
+		_debug("get new conn [%d]", bundle->num_conns);
+
+		spin_unlock(&trans->client_lock);
+
+		if (signal_pending(current))
+			goto interrupted;
+
+		if (bundle->num_conns >= 20) {
+			_debug("too many conns");
+
+			if (!(gfp & __GFP_WAIT)) {
+				_leave(" = -EAGAIN");
+				return -EAGAIN;
+			}
+
+			add_wait_queue(&bundle->chanwait, &myself);
+			for (;;) {
+				set_current_state(TASK_INTERRUPTIBLE);
+				if (bundle->num_conns < 20 ||
+				    !list_empty(&bundle->unused_conns) ||
+				    !list_empty(&bundle->avail_conns))
+					break;
+				if (signal_pending(current))
+					goto interrupted_dequeue;
+				schedule();
+			}
+			remove_wait_queue(&bundle->chanwait, &myself);
+			__set_current_state(TASK_RUNNING);
+			spin_lock(&trans->client_lock);
+			continue;
+		}
+
+		/* not yet present - create a candidate for a new connection and then
+		 * redo the check */
+		candidate = rxrpc_alloc_connection(gfp);
+		if (IS_ERR(candidate)) {
+			_leave(" = %ld", PTR_ERR(candidate));
+			return PTR_ERR(candidate);
+		}
+
+		candidate->trans = trans;
+		candidate->bundle = bundle;
+		candidate->service_id = bundle->service_id;
+		candidate->epoch = rxrpc_epoch;
+		candidate->in_clientflag = 0;
+		candidate->out_clientflag = RXRPC_CLIENT_INITIATED;
+		candidate->cid = 0;
+		candidate->state = RXRPC_CONN_CLIENT;
+		candidate->avail_calls = RXRPC_MAXCALLS;
+		candidate->security_level = rx->min_sec_level;
+		candidate->key = key_get(bundle->key);
+
+		ret = rxrpc_init_client_conn_security(candidate);
+		if (ret < 0) {
+			key_put(candidate->key);
+			kfree(candidate);
+			_leave(" = %d [key]", ret);
+			return ret;
+		}
+
+		write_lock_bh(&rxrpc_connection_lock);
+		list_add_tail(&candidate->link, &rxrpc_connections);
+		write_unlock_bh(&rxrpc_connection_lock);
+
+		spin_lock(&trans->client_lock);
+
+		list_add(&candidate->bundle_link, &bundle->unused_conns);
+		bundle->num_conns++;
+		atomic_inc(&bundle->usage);
+		atomic_inc(&trans->usage);
+
+		_net("CONNECT new %d on TRANS %d",
+		     candidate->debug_id, candidate->trans->debug_id);
+
+		rxrpc_assign_connection_id(candidate);
+		if (candidate->security)
+			candidate->security->prime_packet_security(candidate);
+
+		/* leave the candidate lurking in zombie mode attached to the
+		 * bundle until we're ready for it */
+		rxrpc_put_connection(candidate);
+		candidate = NULL;
+	}
+
+	/* we've got a connection with a free channel and we can now attach the
+	 * call to it
+	 * - we're holding the transport's client lock
+	 * - we're holding a reference on the connection
+	 * - we're holding a reference on the bundle
+	 */
+	for (chan = 0; chan < RXRPC_MAXCALLS; chan++)
+		if (!conn->channels[chan])
+			goto found_channel;
+	ASSERT(conn->channels[0] == NULL ||
+	       conn->channels[1] == NULL ||
+	       conn->channels[2] == NULL ||
+	       conn->channels[3] == NULL);
+	BUG();
+
+found_channel:
+	conn->channels[chan] = call;
+	call->conn = conn;
+	call->channel = chan;
+	call->cid = conn->cid | htonl(chan);
+	call->call_id = htonl(++conn->call_counter);
+
+	_net("CONNECT client on conn %d chan %d as call %x",
+	     conn->debug_id, chan, ntohl(call->call_id));
+
+	ASSERTCMP(conn->avail_calls, <, RXRPC_MAXCALLS);
+	spin_unlock(&trans->client_lock);
+
+	rxrpc_add_call_ID_to_conn(conn, call);
+
+	_leave(" = 0");
+	return 0;
+
+interrupted_dequeue:
+	remove_wait_queue(&bundle->chanwait, &myself);
+	__set_current_state(TASK_RUNNING);
+interrupted:
+	_leave(" = -ERESTARTSYS");
+	return -ERESTARTSYS;
+}
+
+/*
+ * get a record of an incoming connection
+ */
+struct rxrpc_connection *
+rxrpc_incoming_connection(struct rxrpc_transport *trans,
+			  struct rxrpc_header *hdr,
+			  gfp_t gfp)
+{
+	struct rxrpc_connection *conn, *candidate = NULL;
+	struct rb_node *p, **pp;
+	const char *new = "old";
+	__be32 epoch;
+	u32 conn_id;
+
+	_enter("");
+
+	ASSERT(hdr->flags & RXRPC_CLIENT_INITIATED);
+
+	epoch = hdr->epoch;
+	conn_id = ntohl(hdr->cid) & RXRPC_CIDMASK;
+
+	/* search the connection list first */
+	read_lock_bh(&trans->conn_lock);
+
+	p = trans->server_conns.rb_node;
+	while (p) {
+		conn = rb_entry(p, struct rxrpc_connection, node);
+
+		_debug("maybe %x", conn->real_conn_id);
+
+		if (epoch < conn->epoch)
+			p = p->rb_left;
+		else if (epoch > conn->epoch)
+			p = p->rb_right;
+		else if (conn_id < conn->real_conn_id)
+			p = p->rb_left;
+		else if (conn_id > conn->real_conn_id)
+			p = p->rb_right;
+		else
+			goto found_extant_connection;
+	}
+	read_unlock_bh(&trans->conn_lock);
+
+	/* not yet present - create a candidate for a new record and then
+	 * redo the search */
+	candidate = rxrpc_alloc_connection(gfp);
+	if (!candidate) {
+		_leave(" = -ENOMEM");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	candidate->trans = trans;
+	candidate->epoch = hdr->epoch;
+	candidate->cid = hdr->cid & __constant_cpu_to_be32(RXRPC_CIDMASK);
+	candidate->service_id = hdr->serviceId;
+	candidate->security_ix = hdr->securityIndex;
+	candidate->in_clientflag = RXRPC_CLIENT_INITIATED;
+	candidate->out_clientflag = 0;
+	candidate->real_conn_id = conn_id;
+	candidate->state = RXRPC_CONN_SERVER;
+	if (candidate->service_id)
+		candidate->state = RXRPC_CONN_SERVER_UNSECURED;
+
+	write_lock_bh(&trans->conn_lock);
+
+	pp = &trans->server_conns.rb_node;
+	p = NULL;
+	while (*pp) {
+		p = *pp;
+		conn = rb_entry(p, struct rxrpc_connection, node);
+
+		if (epoch < conn->epoch)
+			pp = &(*pp)->rb_left;
+		else if (epoch > conn->epoch)
+			pp = &(*pp)->rb_right;
+		else if (conn_id < conn->real_conn_id)
+			pp = &(*pp)->rb_left;
+		else if (conn_id > conn->real_conn_id)
+			pp = &(*pp)->rb_right;
+		else
+			goto found_extant_second;
+	}
+
+	/* we can now add the new candidate to the list */
+	conn = candidate;
+	candidate = NULL;
+	rb_link_node(&conn->node, p, pp);
+	rb_insert_color(&conn->node, &trans->server_conns);
+	atomic_inc(&conn->trans->usage);
+
+	write_unlock_bh(&trans->conn_lock);
+
+	write_lock_bh(&rxrpc_connection_lock);
+	list_add_tail(&conn->link, &rxrpc_connections);
+	write_unlock_bh(&rxrpc_connection_lock);
+
+	new = "new";
+
+success:
+	_net("CONNECTION %s %d {%x}", new, conn->debug_id, conn->real_conn_id);
+
+	_leave(" = %p {u=%d}", conn, atomic_read(&conn->usage));
+	return conn;
+
+	/* we found the connection in the list immediately */
+found_extant_connection:
+	if (hdr->securityIndex != conn->security_ix) {
+		read_unlock_bh(&trans->conn_lock);
+		goto security_mismatch;
+	}
+	atomic_inc(&conn->usage);
+	read_unlock_bh(&trans->conn_lock);
+	goto success;
+
+	/* we found the connection on the second time through the list */
+found_extant_second:
+	if (hdr->securityIndex != conn->security_ix) {
+		write_unlock_bh(&trans->conn_lock);
+		goto security_mismatch;
+	}
+	atomic_inc(&conn->usage);
+	write_unlock_bh(&trans->conn_lock);
+	kfree(candidate);
+	goto success;
+
+security_mismatch:
+	kfree(candidate);
+	_leave(" = -EKEYREJECTED");
+	return ERR_PTR(-EKEYREJECTED);
+}
+
+/*
+ * find a connection based on transport and RxRPC connection ID for an incoming
+ * packet
+ */
+struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_transport *trans,
+					       struct rxrpc_header *hdr)
+{
+	struct rxrpc_connection *conn;
+	struct rb_node *p;
+	__be32 epoch;
+	u32 conn_id;
+
+	_enter(",{%x,%x}", ntohl(hdr->cid), hdr->flags);
+
+	read_lock_bh(&trans->conn_lock);
+
+	conn_id = ntohl(hdr->cid) & RXRPC_CIDMASK;
+	epoch = hdr->epoch;
+
+	if (hdr->flags & RXRPC_CLIENT_INITIATED)
+		p = trans->server_conns.rb_node;
+	else
+		p = trans->client_conns.rb_node;
+
+	while (p) {
+		conn = rb_entry(p, struct rxrpc_connection, node);
+
+		_debug("maybe %x", conn->real_conn_id);
+
+		if (epoch < conn->epoch)
+			p = p->rb_left;
+		else if (epoch > conn->epoch)
+			p = p->rb_right;
+		else if (conn_id < conn->real_conn_id)
+			p = p->rb_left;
+		else if (conn_id > conn->real_conn_id)
+			p = p->rb_right;
+		else
+			goto found;
+	}
+
+	read_unlock_bh(&trans->conn_lock);
+	_leave(" = NULL");
+	return NULL;
+
+found:
+	atomic_inc(&conn->usage);
+	read_unlock_bh(&trans->conn_lock);
+	_leave(" = %p", conn);
+	return conn;
+}
+
+/*
+ * release a virtual connection
+ */
+void rxrpc_put_connection(struct rxrpc_connection *conn)
+{
+	_enter("%p{u=%d,d=%d}",
+	       conn, atomic_read(&conn->usage), conn->debug_id);
+
+	ASSERTCMP(atomic_read(&conn->usage), >, 0);
+
+	conn->put_time = xtime.tv_sec;
+	if (atomic_dec_and_test(&conn->usage)) {
+		_debug("zombie");
+		rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0);
+	}
+
+	_leave("");
+}
+
+/*
+ * destroy a virtual connection
+ */
+static void rxrpc_destroy_connection(struct rxrpc_connection *conn)
+{
+	_enter("%p{%d}", conn, atomic_read(&conn->usage));
+
+	ASSERTCMP(atomic_read(&conn->usage), ==, 0);
+
+	_net("DESTROY CONN %d", conn->debug_id);
+
+	if (conn->bundle)
+		rxrpc_put_bundle(conn->trans, conn->bundle);
+
+	ASSERT(RB_EMPTY_ROOT(&conn->calls));
+	rxrpc_purge_queue(&conn->rx_queue);
+
+	rxrpc_clear_conn_security(conn);
+	rxrpc_put_transport(conn->trans);
+	kfree(conn);
+	_leave("");
+}
+
+/*
+ * reap dead connections
+ */
+void rxrpc_connection_reaper(struct work_struct *work)
+{
+	struct rxrpc_connection *conn, *_p;
+	unsigned long now, earliest, reap_time;
+
+	LIST_HEAD(graveyard);
+
+	_enter("");
+
+	now = xtime.tv_sec;
+	earliest = ULONG_MAX;
+
+	write_lock_bh(&rxrpc_connection_lock);
+	list_for_each_entry_safe(conn, _p, &rxrpc_connections, link) {
+		_debug("reap CONN %d { u=%d,t=%ld }",
+		       conn->debug_id, atomic_read(&conn->usage),
+		       (long) now - (long) conn->put_time);
+
+		if (likely(atomic_read(&conn->usage) > 0))
+			continue;
+
+		spin_lock(&conn->trans->client_lock);
+		write_lock(&conn->trans->conn_lock);
+		reap_time = conn->put_time + rxrpc_connection_timeout;
+
+		if (atomic_read(&conn->usage) > 0) {
+			;
+		} else if (reap_time <= now) {
+			list_move_tail(&conn->link, &graveyard);
+			if (conn->out_clientflag)
+				rb_erase(&conn->node,
+					 &conn->trans->client_conns);
+			else
+				rb_erase(&conn->node,
+					 &conn->trans->server_conns);
+			if (conn->bundle) {
+				list_del_init(&conn->bundle_link);
+				conn->bundle->num_conns--;
+			}
+
+		} else if (reap_time < earliest) {
+			earliest = reap_time;
+		}
+
+		write_unlock(&conn->trans->conn_lock);
+		spin_unlock(&conn->trans->client_lock);
+	}
+	write_unlock_bh(&rxrpc_connection_lock);
+
+	if (earliest != ULONG_MAX) {
+		_debug("reschedule reaper %ld", (long) earliest - now);
+		ASSERTCMP(earliest, >, now);
+		rxrpc_queue_delayed_work(&rxrpc_connection_reap,
+					 (earliest - now) * HZ);
+	}
+
+	/* then destroy all those pulled out */
+	while (!list_empty(&graveyard)) {
+		conn = list_entry(graveyard.next, struct rxrpc_connection,
+				  link);
+		list_del_init(&conn->link);
+
+		ASSERTCMP(atomic_read(&conn->usage), ==, 0);
+		rxrpc_destroy_connection(conn);
+	}
+
+	_leave("");
+}
+
+/*
+ * preemptively destroy all the connection records rather than waiting for them
+ * to time out
+ */
+void __exit rxrpc_destroy_all_connections(void)
+{
+	_enter("");
+
+	rxrpc_connection_timeout = 0;
+	cancel_delayed_work(&rxrpc_connection_reap);
+	rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0);
+
+	_leave("");
+}
diff --git a/net/rxrpc/ar-connevent.c b/net/rxrpc/ar-connevent.c
new file mode 100644
index 0000000..1ada43d
--- /dev/null
+++ b/net/rxrpc/ar-connevent.c
@@ -0,0 +1,403 @@
+/* connection-level event handling
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/errqueue.h>
+#include <linux/udp.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/icmp.h>
+#include <net/sock.h>
+#include <net/af_rxrpc.h>
+#include <net/ip.h>
+#include "ar-internal.h"
+
+/*
+ * pass a connection-level abort onto all calls on that connection
+ */
+static void rxrpc_abort_calls(struct rxrpc_connection *conn, int state,
+			      u32 abort_code)
+{
+	struct rxrpc_call *call;
+	struct rb_node *p;
+
+	_enter("{%d},%x", conn->debug_id, abort_code);
+
+	read_lock_bh(&conn->lock);
+
+	for (p = rb_first(&conn->calls); p; p = rb_next(p)) {
+		call = rb_entry(p, struct rxrpc_call, conn_node);
+		write_lock(&call->state_lock);
+		if (call->state <= RXRPC_CALL_COMPLETE) {
+			call->state = state;
+			call->abort_code = abort_code;
+			if (state == RXRPC_CALL_LOCALLY_ABORTED)
+				set_bit(RXRPC_CALL_CONN_ABORT, &call->events);
+			else
+				set_bit(RXRPC_CALL_RCVD_ABORT, &call->events);
+			rxrpc_queue_call(call);
+		}
+		write_unlock(&call->state_lock);
+	}
+
+	read_unlock_bh(&conn->lock);
+	_leave("");
+}
+
+/*
+ * generate a connection-level abort
+ */
+static int rxrpc_abort_connection(struct rxrpc_connection *conn,
+				  u32 error, u32 abort_code)
+{
+	struct rxrpc_header hdr;
+	struct msghdr msg;
+	struct kvec iov[2];
+	__be32 word;
+	size_t len;
+	int ret;
+
+	_enter("%d,,%u,%u", conn->debug_id, error, abort_code);
+
+	/* generate a connection-level abort */
+	spin_lock_bh(&conn->state_lock);
+	if (conn->state < RXRPC_CONN_REMOTELY_ABORTED) {
+		conn->state = RXRPC_CONN_LOCALLY_ABORTED;
+		conn->error = error;
+		spin_unlock_bh(&conn->state_lock);
+	} else {
+		spin_unlock_bh(&conn->state_lock);
+		_leave(" = 0 [already dead]");
+		return 0;
+	}
+
+	rxrpc_abort_calls(conn, RXRPC_CALL_LOCALLY_ABORTED, abort_code);
+
+	msg.msg_name	= &conn->trans->peer->srx.transport.sin;
+	msg.msg_namelen	= sizeof(conn->trans->peer->srx.transport.sin);
+	msg.msg_control	= NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags	= 0;
+
+	hdr.epoch	= conn->epoch;
+	hdr.cid		= conn->cid;
+	hdr.callNumber	= 0;
+	hdr.seq		= 0;
+	hdr.type	= RXRPC_PACKET_TYPE_ABORT;
+	hdr.flags	= conn->out_clientflag;
+	hdr.userStatus	= 0;
+	hdr.securityIndex = conn->security_ix;
+	hdr._rsvd	= 0;
+	hdr.serviceId	= conn->service_id;
+
+	word = htonl(abort_code);
+
+	iov[0].iov_base	= &hdr;
+	iov[0].iov_len	= sizeof(hdr);
+	iov[1].iov_base	= &word;
+	iov[1].iov_len	= sizeof(word);
+
+	len = iov[0].iov_len + iov[1].iov_len;
+
+	hdr.serial = htonl(atomic_inc_return(&conn->serial));
+	_proto("Tx CONN ABORT %%%u { %d }", ntohl(hdr.serial), abort_code);
+
+	ret = kernel_sendmsg(conn->trans->local->socket, &msg, iov, 2, len);
+	if (ret < 0) {
+		_debug("sendmsg failed: %d", ret);
+		return -EAGAIN;
+	}
+
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * mark a call as being on a now-secured channel
+ * - must be called with softirqs disabled
+ */
+void rxrpc_call_is_secure(struct rxrpc_call *call)
+{
+	_enter("%p", call);
+	if (call) {
+		read_lock(&call->state_lock);
+		if (call->state < RXRPC_CALL_COMPLETE &&
+		    !test_and_set_bit(RXRPC_CALL_SECURED, &call->events))
+			rxrpc_queue_call(call);
+		read_unlock(&call->state_lock);
+	}
+}
+
+/*
+ * connection-level Rx packet processor
+ */
+static int rxrpc_process_event(struct rxrpc_connection *conn,
+			       struct sk_buff *skb,
+			       u32 *_abort_code)
+{
+	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+	__be32 tmp;
+	u32 serial;
+	int loop, ret;
+
+	if (conn->state >= RXRPC_CONN_REMOTELY_ABORTED)
+		return -ECONNABORTED;
+
+	serial = ntohl(sp->hdr.serial);
+
+	switch (sp->hdr.type) {
+	case RXRPC_PACKET_TYPE_ABORT:
+		if (skb_copy_bits(skb, 0, &tmp, sizeof(tmp)) < 0)
+			return -EPROTO;
+		_proto("Rx ABORT %%%u { ac=%d }", serial, ntohl(tmp));
+
+		conn->state = RXRPC_CONN_REMOTELY_ABORTED;
+		rxrpc_abort_calls(conn, RXRPC_CALL_REMOTELY_ABORTED,
+				  ntohl(tmp));
+		return -ECONNABORTED;
+
+	case RXRPC_PACKET_TYPE_CHALLENGE:
+		if (conn->security)
+			return conn->security->respond_to_challenge(
+				conn, skb, _abort_code);
+		return -EPROTO;
+
+	case RXRPC_PACKET_TYPE_RESPONSE:
+		if (!conn->security)
+			return -EPROTO;
+
+		ret = conn->security->verify_response(conn, skb, _abort_code);
+		if (ret < 0)
+			return ret;
+
+		ret = conn->security->init_connection_security(conn);
+		if (ret < 0)
+			return ret;
+
+		conn->security->prime_packet_security(conn);
+		read_lock_bh(&conn->lock);
+		spin_lock(&conn->state_lock);
+
+		if (conn->state == RXRPC_CONN_SERVER_CHALLENGING) {
+			conn->state = RXRPC_CONN_SERVER;
+			for (loop = 0; loop < RXRPC_MAXCALLS; loop++)
+				rxrpc_call_is_secure(conn->channels[loop]);
+		}
+
+		spin_unlock(&conn->state_lock);
+		read_unlock_bh(&conn->lock);
+		return 0;
+
+	default:
+		return -EPROTO;
+	}
+}
+
+/*
+ * set up security and issue a challenge
+ */
+static void rxrpc_secure_connection(struct rxrpc_connection *conn)
+{
+	u32 abort_code;
+	int ret;
+
+	_enter("{%d}", conn->debug_id);
+
+	ASSERT(conn->security_ix != 0);
+
+	if (!conn->key) {
+		_debug("set up security");
+		ret = rxrpc_init_server_conn_security(conn);
+		switch (ret) {
+		case 0:
+			break;
+		case -ENOENT:
+			abort_code = RX_CALL_DEAD;
+			goto abort;
+		default:
+			abort_code = RXKADNOAUTH;
+			goto abort;
+		}
+	}
+
+	ASSERT(conn->security != NULL);
+
+	if (conn->security->issue_challenge(conn) < 0) {
+		abort_code = RX_CALL_DEAD;
+		ret = -ENOMEM;
+		goto abort;
+	}
+
+	_leave("");
+	return;
+
+abort:
+	_debug("abort %d, %d", ret, abort_code);
+	rxrpc_abort_connection(conn, -ret, abort_code);
+	_leave(" [aborted]");
+}
+
+/*
+ * connection-level event processor
+ */
+void rxrpc_process_connection(struct work_struct *work)
+{
+	struct rxrpc_connection *conn =
+		container_of(work, struct rxrpc_connection, processor);
+	struct rxrpc_skb_priv *sp;
+	struct sk_buff *skb;
+	u32 abort_code = RX_PROTOCOL_ERROR;
+	int ret;
+
+	_enter("{%d}", conn->debug_id);
+
+	atomic_inc(&conn->usage);
+
+	if (test_and_clear_bit(RXRPC_CONN_CHALLENGE, &conn->events)) {
+		rxrpc_secure_connection(conn);
+		rxrpc_put_connection(conn);
+	}
+
+	/* go through the conn-level event packets, releasing the ref on this
+	 * connection that each one has when we've finished with it */
+	while ((skb = skb_dequeue(&conn->rx_queue))) {
+		sp = rxrpc_skb(skb);
+
+		ret = rxrpc_process_event(conn, skb, &abort_code);
+		switch (ret) {
+		case -EPROTO:
+		case -EKEYEXPIRED:
+		case -EKEYREJECTED:
+			goto protocol_error;
+		case -EAGAIN:
+			goto requeue_and_leave;
+		case -ECONNABORTED:
+		default:
+			rxrpc_put_connection(conn);
+			rxrpc_free_skb(skb);
+			break;
+		}
+	}
+
+out:
+	rxrpc_put_connection(conn);
+	_leave("");
+	return;
+
+requeue_and_leave:
+	skb_queue_head(&conn->rx_queue, skb);
+	goto out;
+
+protocol_error:
+	if (rxrpc_abort_connection(conn, -ret, abort_code) < 0)
+		goto requeue_and_leave;
+	rxrpc_put_connection(conn);
+	rxrpc_free_skb(skb);
+	_leave(" [EPROTO]");
+	goto out;
+}
+
+/*
+ * put a packet up for transport-level abort
+ */
+void rxrpc_reject_packet(struct rxrpc_local *local, struct sk_buff *skb)
+{
+	CHECK_SLAB_OKAY(&local->usage);
+
+	if (!atomic_inc_not_zero(&local->usage)) {
+		printk("resurrected on reject\n");
+		BUG();
+	}
+
+	skb_queue_tail(&local->reject_queue, skb);
+	rxrpc_queue_work(&local->rejecter);
+}
+
+/*
+ * reject packets through the local endpoint
+ */
+void rxrpc_reject_packets(struct work_struct *work)
+{
+	union {
+		struct sockaddr sa;
+		struct sockaddr_in sin;
+	} sa;
+	struct rxrpc_skb_priv *sp;
+	struct rxrpc_header hdr;
+	struct rxrpc_local *local;
+	struct sk_buff *skb;
+	struct msghdr msg;
+	struct kvec iov[2];
+	size_t size;
+	__be32 code;
+
+	local = container_of(work, struct rxrpc_local, rejecter);
+	rxrpc_get_local(local);
+
+	_enter("%d", local->debug_id);
+
+	iov[0].iov_base = &hdr;
+	iov[0].iov_len = sizeof(hdr);
+	iov[1].iov_base = &code;
+	iov[1].iov_len = sizeof(code);
+	size = sizeof(hdr) + sizeof(code);
+
+	msg.msg_name = &sa;
+	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags = 0;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa.sa_family = local->srx.transport.family;
+	switch (sa.sa.sa_family) {
+	case AF_INET:
+		msg.msg_namelen = sizeof(sa.sin);
+		break;
+	default:
+		msg.msg_namelen = 0;
+		break;
+	}
+
+	memset(&hdr, 0, sizeof(hdr));
+	hdr.type = RXRPC_PACKET_TYPE_ABORT;
+
+	while ((skb = skb_dequeue(&local->reject_queue))) {
+		sp = rxrpc_skb(skb);
+		switch (sa.sa.sa_family) {
+		case AF_INET:
+			sa.sin.sin_port = udp_hdr(skb)->source;
+			sa.sin.sin_addr.s_addr = ip_hdr(skb)->saddr;
+			code = htonl(skb->priority);
+
+			hdr.epoch = sp->hdr.epoch;
+			hdr.cid = sp->hdr.cid;
+			hdr.callNumber = sp->hdr.callNumber;
+			hdr.serviceId = sp->hdr.serviceId;
+			hdr.flags = sp->hdr.flags;
+			hdr.flags ^= RXRPC_CLIENT_INITIATED;
+			hdr.flags &= RXRPC_CLIENT_INITIATED;
+
+			kernel_sendmsg(local->socket, &msg, iov, 2, size);
+			break;
+
+		default:
+			break;
+		}
+
+		rxrpc_free_skb(skb);
+		rxrpc_put_local(local);
+	}
+
+	rxrpc_put_local(local);
+	_leave("");
+}
diff --git a/net/rxrpc/ar-error.c b/net/rxrpc/ar-error.c
new file mode 100644
index 0000000..2c27df1
--- /dev/null
+++ b/net/rxrpc/ar-error.c
@@ -0,0 +1,253 @@
+/* Error message handling (ICMP)
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/errqueue.h>
+#include <linux/udp.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/icmp.h>
+#include <net/sock.h>
+#include <net/af_rxrpc.h>
+#include <net/ip.h>
+#include "ar-internal.h"
+
+/*
+ * handle an error received on the local endpoint
+ */
+void rxrpc_UDP_error_report(struct sock *sk)
+{
+	struct sock_exterr_skb *serr;
+	struct rxrpc_transport *trans;
+	struct rxrpc_local *local = sk->sk_user_data;
+	struct rxrpc_peer *peer;
+	struct sk_buff *skb;
+	__be32 addr;
+	__be16 port;
+
+	_enter("%p{%d}", sk, local->debug_id);
+
+	skb = skb_dequeue(&sk->sk_error_queue);
+	if (!skb) {
+		_leave("UDP socket errqueue empty");
+		return;
+	}
+
+	rxrpc_new_skb(skb);
+
+	serr = SKB_EXT_ERR(skb);
+	addr = *(__be32 *)(skb_network_header(skb) + serr->addr_offset);
+	port = serr->port;
+
+	_net("Rx UDP Error from "NIPQUAD_FMT":%hu",
+	     NIPQUAD(addr), ntohs(port));
+	_debug("Msg l:%d d:%d", skb->len, skb->data_len);
+
+	peer = rxrpc_find_peer(local, addr, port);
+	if (IS_ERR(peer)) {
+		rxrpc_free_skb(skb);
+		_leave(" [no peer]");
+		return;
+	}
+
+	trans = rxrpc_find_transport(local, peer);
+	if (!trans) {
+		rxrpc_put_peer(peer);
+		rxrpc_free_skb(skb);
+		_leave(" [no trans]");
+		return;
+	}
+
+	if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP &&
+	    serr->ee.ee_type == ICMP_DEST_UNREACH &&
+	    serr->ee.ee_code == ICMP_FRAG_NEEDED
+	    ) {
+		u32 mtu = serr->ee.ee_info;
+
+		_net("Rx Received ICMP Fragmentation Needed (%d)", mtu);
+
+		/* wind down the local interface MTU */
+		if (mtu > 0 && peer->if_mtu == 65535 && mtu < peer->if_mtu) {
+			peer->if_mtu = mtu;
+			_net("I/F MTU %u", mtu);
+		}
+
+		/* ip_rt_frag_needed() may have eaten the info */
+		if (mtu == 0)
+			mtu = ntohs(icmp_hdr(skb)->un.frag.mtu);
+
+		if (mtu == 0) {
+			/* they didn't give us a size, estimate one */
+			if (mtu > 1500) {
+				mtu >>= 1;
+				if (mtu < 1500)
+					mtu = 1500;
+			} else {
+				mtu -= 100;
+				if (mtu < peer->hdrsize)
+					mtu = peer->hdrsize + 4;
+			}
+		}
+
+		if (mtu < peer->mtu) {
+			peer->mtu = mtu;
+			peer->maxdata = peer->mtu - peer->hdrsize;
+			_net("Net MTU %u (maxdata %u)",
+			     peer->mtu, peer->maxdata);
+		}
+	}
+
+	rxrpc_put_peer(peer);
+
+	/* pass the transport ref to error_handler to release */
+	skb_queue_tail(&trans->error_queue, skb);
+	rxrpc_queue_work(&trans->error_handler);
+
+	/* reset and regenerate socket error */
+	spin_lock_bh(&sk->sk_error_queue.lock);
+	sk->sk_err = 0;
+	skb = skb_peek(&sk->sk_error_queue);
+	if (skb) {
+		sk->sk_err = SKB_EXT_ERR(skb)->ee.ee_errno;
+		spin_unlock_bh(&sk->sk_error_queue.lock);
+		sk->sk_error_report(sk);
+	} else {
+		spin_unlock_bh(&sk->sk_error_queue.lock);
+	}
+
+	_leave("");
+}
+
+/*
+ * deal with UDP error messages
+ */
+void rxrpc_UDP_error_handler(struct work_struct *work)
+{
+	struct sock_extended_err *ee;
+	struct sock_exterr_skb *serr;
+	struct rxrpc_transport *trans =
+		container_of(work, struct rxrpc_transport, error_handler);
+	struct sk_buff *skb;
+	int local, err;
+
+	_enter("");
+
+	skb = skb_dequeue(&trans->error_queue);
+	if (!skb)
+		return;
+
+	serr = SKB_EXT_ERR(skb);
+	ee = &serr->ee;
+
+	_net("Rx Error o=%d t=%d c=%d e=%d",
+	     ee->ee_origin, ee->ee_type, ee->ee_code, ee->ee_errno);
+
+	err = ee->ee_errno;
+
+	switch (ee->ee_origin) {
+	case SO_EE_ORIGIN_ICMP:
+		local = 0;
+		switch (ee->ee_type) {
+		case ICMP_DEST_UNREACH:
+			switch (ee->ee_code) {
+			case ICMP_NET_UNREACH:
+				_net("Rx Received ICMP Network Unreachable");
+				err = ENETUNREACH;
+				break;
+			case ICMP_HOST_UNREACH:
+				_net("Rx Received ICMP Host Unreachable");
+				err = EHOSTUNREACH;
+				break;
+			case ICMP_PORT_UNREACH:
+				_net("Rx Received ICMP Port Unreachable");
+				err = ECONNREFUSED;
+				break;
+			case ICMP_FRAG_NEEDED:
+				_net("Rx Received ICMP Fragmentation Needed (%d)",
+				     ee->ee_info);
+				err = 0; /* dealt with elsewhere */
+				break;
+			case ICMP_NET_UNKNOWN:
+				_net("Rx Received ICMP Unknown Network");
+				err = ENETUNREACH;
+				break;
+			case ICMP_HOST_UNKNOWN:
+				_net("Rx Received ICMP Unknown Host");
+				err = EHOSTUNREACH;
+				break;
+			default:
+				_net("Rx Received ICMP DestUnreach code=%u",
+				     ee->ee_code);
+				break;
+			}
+			break;
+
+		case ICMP_TIME_EXCEEDED:
+			_net("Rx Received ICMP TTL Exceeded");
+			break;
+
+		default:
+			_proto("Rx Received ICMP error { type=%u code=%u }",
+			       ee->ee_type, ee->ee_code);
+			break;
+		}
+		break;
+
+	case SO_EE_ORIGIN_LOCAL:
+		_proto("Rx Received local error { error=%d }",
+		       ee->ee_errno);
+		local = 1;
+		break;
+
+	case SO_EE_ORIGIN_NONE:
+	case SO_EE_ORIGIN_ICMP6:
+	default:
+		_proto("Rx Received error report { orig=%u }",
+		       ee->ee_origin);
+		local = 0;
+		break;
+	}
+
+	/* terminate all the affected calls if there's an unrecoverable
+	 * error */
+	if (err) {
+		struct rxrpc_call *call, *_n;
+
+		_debug("ISSUE ERROR %d", err);
+
+		spin_lock_bh(&trans->peer->lock);
+		trans->peer->net_error = err;
+
+		list_for_each_entry_safe(call, _n, &trans->peer->error_targets,
+					 error_link) {
+			write_lock(&call->state_lock);
+			if (call->state != RXRPC_CALL_COMPLETE &&
+			    call->state < RXRPC_CALL_NETWORK_ERROR) {
+				call->state = RXRPC_CALL_NETWORK_ERROR;
+				set_bit(RXRPC_CALL_RCVD_ERROR, &call->events);
+				rxrpc_queue_call(call);
+			}
+			write_unlock(&call->state_lock);
+			list_del_init(&call->error_link);
+		}
+
+		spin_unlock_bh(&trans->peer->lock);
+	}
+
+	if (!skb_queue_empty(&trans->error_queue))
+		rxrpc_queue_work(&trans->error_handler);
+
+	rxrpc_free_skb(skb);
+	rxrpc_put_transport(trans);
+	_leave("");
+}
diff --git a/net/rxrpc/ar-input.c b/net/rxrpc/ar-input.c
new file mode 100644
index 0000000..91b5bbb
--- /dev/null
+++ b/net/rxrpc/ar-input.c
@@ -0,0 +1,797 @@
+/* RxRPC packet reception
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/errqueue.h>
+#include <linux/udp.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/icmp.h>
+#include <net/sock.h>
+#include <net/af_rxrpc.h>
+#include <net/ip.h>
+#include "ar-internal.h"
+
+unsigned long rxrpc_ack_timeout = 1;
+
+const char *rxrpc_pkts[] = {
+	"?00",
+	"DATA", "ACK", "BUSY", "ABORT", "ACKALL", "CHALL", "RESP", "DEBUG",
+	"?09", "?10", "?11", "?12", "?13", "?14", "?15"
+};
+
+/*
+ * queue a packet for recvmsg to pass to userspace
+ * - the caller must hold a lock on call->lock
+ * - must not be called with interrupts disabled (sk_filter() disables BH's)
+ * - eats the packet whether successful or not
+ * - there must be just one reference to the packet, which the caller passes to
+ *   this function
+ */
+int rxrpc_queue_rcv_skb(struct rxrpc_call *call, struct sk_buff *skb,
+			bool force, bool terminal)
+{
+	struct rxrpc_skb_priv *sp;
+	struct rxrpc_sock *rx = call->socket;
+	struct sock *sk;
+	int skb_len, ret;
+
+	_enter(",,%d,%d", force, terminal);
+
+	ASSERT(!irqs_disabled());
+
+	sp = rxrpc_skb(skb);
+	ASSERTCMP(sp->call, ==, call);
+
+	/* if we've already posted the terminal message for a call, then we
+	 * don't post any more */
+	if (test_bit(RXRPC_CALL_TERMINAL_MSG, &call->flags)) {
+		_debug("already terminated");
+		ASSERTCMP(call->state, >=, RXRPC_CALL_COMPLETE);
+		skb->destructor = NULL;
+		sp->call = NULL;
+		rxrpc_put_call(call);
+		rxrpc_free_skb(skb);
+		return 0;
+	}
+
+	sk = &rx->sk;
+
+	if (!force) {
+		/* cast skb->rcvbuf to unsigned...  It's pointless, but
+		 * reduces number of warnings when compiling with -W
+		 * --ANK */
+//		ret = -ENOBUFS;
+//		if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >=
+//		    (unsigned) sk->sk_rcvbuf)
+//			goto out;
+
+		ret = sk_filter(sk, skb);
+		if (ret < 0)
+			goto out;
+	}
+
+	spin_lock_bh(&sk->sk_receive_queue.lock);
+	if (!test_bit(RXRPC_CALL_TERMINAL_MSG, &call->flags) &&
+	    !test_bit(RXRPC_CALL_RELEASED, &call->flags) &&
+	    call->socket->sk.sk_state != RXRPC_CLOSE) {
+		skb->destructor = rxrpc_packet_destructor;
+		skb->dev = NULL;
+		skb->sk = sk;
+		atomic_add(skb->truesize, &sk->sk_rmem_alloc);
+
+		if (terminal) {
+			_debug("<<<< TERMINAL MESSAGE >>>>");
+			set_bit(RXRPC_CALL_TERMINAL_MSG, &call->flags);
+		}
+
+		/* allow interception by a kernel service */
+		if (rx->interceptor) {
+			rx->interceptor(sk, call->user_call_ID, skb);
+			spin_unlock_bh(&sk->sk_receive_queue.lock);
+		} else {
+
+			/* Cache the SKB length before we tack it onto the
+			 * receive queue.  Once it is added it no longer
+			 * belongs to us and may be freed by other threads of
+			 * control pulling packets from the queue */
+			skb_len = skb->len;
+
+			_net("post skb %p", skb);
+			__skb_queue_tail(&sk->sk_receive_queue, skb);
+			spin_unlock_bh(&sk->sk_receive_queue.lock);
+
+			if (!sock_flag(sk, SOCK_DEAD))
+				sk->sk_data_ready(sk, skb_len);
+		}
+		skb = NULL;
+	} else {
+		spin_unlock_bh(&sk->sk_receive_queue.lock);
+	}
+	ret = 0;
+
+out:
+	/* release the socket buffer */
+	if (skb) {
+		skb->destructor = NULL;
+		sp->call = NULL;
+		rxrpc_put_call(call);
+		rxrpc_free_skb(skb);
+	}
+
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * process a DATA packet, posting the packet to the appropriate queue
+ * - eats the packet if successful
+ */
+static int rxrpc_fast_process_data(struct rxrpc_call *call,
+				   struct sk_buff *skb, u32 seq)
+{
+	struct rxrpc_skb_priv *sp;
+	bool terminal;
+	int ret, ackbit, ack;
+
+	_enter("{%u,%u},,{%u}", call->rx_data_post, call->rx_first_oos, seq);
+
+	sp = rxrpc_skb(skb);
+	ASSERTCMP(sp->call, ==, NULL);
+
+	spin_lock(&call->lock);
+
+	if (call->state > RXRPC_CALL_COMPLETE)
+		goto discard;
+
+	ASSERTCMP(call->rx_data_expect, >=, call->rx_data_post);
+	ASSERTCMP(call->rx_data_post, >=, call->rx_data_recv);
+	ASSERTCMP(call->rx_data_recv, >=, call->rx_data_eaten);
+
+	if (seq < call->rx_data_post) {
+		_debug("dup #%u [-%u]", seq, call->rx_data_post);
+		ack = RXRPC_ACK_DUPLICATE;
+		ret = -ENOBUFS;
+		goto discard_and_ack;
+	}
+
+	/* we may already have the packet in the out of sequence queue */
+	ackbit = seq - (call->rx_data_eaten + 1);
+	ASSERTCMP(ackbit, >=, 0);
+	if (__test_and_set_bit(ackbit, call->ackr_window)) {
+		_debug("dup oos #%u [%u,%u]",
+		       seq, call->rx_data_eaten, call->rx_data_post);
+		ack = RXRPC_ACK_DUPLICATE;
+		goto discard_and_ack;
+	}
+
+	if (seq >= call->ackr_win_top) {
+		_debug("exceed #%u [%u]", seq, call->ackr_win_top);
+		__clear_bit(ackbit, call->ackr_window);
+		ack = RXRPC_ACK_EXCEEDS_WINDOW;
+		goto discard_and_ack;
+	}
+
+	if (seq == call->rx_data_expect) {
+		clear_bit(RXRPC_CALL_EXPECT_OOS, &call->flags);
+		call->rx_data_expect++;
+	} else if (seq > call->rx_data_expect) {
+		_debug("oos #%u [%u]", seq, call->rx_data_expect);
+		call->rx_data_expect = seq + 1;
+		if (test_and_set_bit(RXRPC_CALL_EXPECT_OOS, &call->flags)) {
+			ack = RXRPC_ACK_OUT_OF_SEQUENCE;
+			goto enqueue_and_ack;
+		}
+		goto enqueue_packet;
+	}
+
+	if (seq != call->rx_data_post) {
+		_debug("ahead #%u [%u]", seq, call->rx_data_post);
+		goto enqueue_packet;
+	}
+
+	if (test_bit(RXRPC_CALL_RCVD_LAST, &call->flags))
+		goto protocol_error;
+
+	/* if the packet need security things doing to it, then it goes down
+	 * the slow path */
+	if (call->conn->security)
+		goto enqueue_packet;
+
+	sp->call = call;
+	rxrpc_get_call(call);
+	terminal = ((sp->hdr.flags & RXRPC_LAST_PACKET) &&
+		    !(sp->hdr.flags & RXRPC_CLIENT_INITIATED));
+	ret = rxrpc_queue_rcv_skb(call, skb, false, terminal);
+	if (ret < 0) {
+		if (ret == -ENOMEM || ret == -ENOBUFS) {
+			__clear_bit(ackbit, call->ackr_window);
+			ack = RXRPC_ACK_NOSPACE;
+			goto discard_and_ack;
+		}
+		goto out;
+	}
+
+	skb = NULL;
+
+	_debug("post #%u", seq);
+	ASSERTCMP(call->rx_data_post, ==, seq);
+	call->rx_data_post++;
+
+	if (sp->hdr.flags & RXRPC_LAST_PACKET)
+		set_bit(RXRPC_CALL_RCVD_LAST, &call->flags);
+
+	/* if we've reached an out of sequence packet then we need to drain
+	 * that queue into the socket Rx queue now */
+	if (call->rx_data_post == call->rx_first_oos) {
+		_debug("drain rx oos now");
+		read_lock(&call->state_lock);
+		if (call->state < RXRPC_CALL_COMPLETE &&
+		    !test_and_set_bit(RXRPC_CALL_DRAIN_RX_OOS, &call->events))
+			rxrpc_queue_call(call);
+		read_unlock(&call->state_lock);
+	}
+
+	spin_unlock(&call->lock);
+	atomic_inc(&call->ackr_not_idle);
+	rxrpc_propose_ACK(call, RXRPC_ACK_DELAY, sp->hdr.serial, false);
+	_leave(" = 0 [posted]");
+	return 0;
+
+protocol_error:
+	ret = -EBADMSG;
+out:
+	spin_unlock(&call->lock);
+	_leave(" = %d", ret);
+	return ret;
+
+discard_and_ack:
+	_debug("discard and ACK packet %p", skb);
+	__rxrpc_propose_ACK(call, ack, sp->hdr.serial, true);
+discard:
+	spin_unlock(&call->lock);
+	rxrpc_free_skb(skb);
+	_leave(" = 0 [discarded]");
+	return 0;
+
+enqueue_and_ack:
+	__rxrpc_propose_ACK(call, ack, sp->hdr.serial, true);
+enqueue_packet:
+	_net("defer skb %p", skb);
+	spin_unlock(&call->lock);
+	skb_queue_tail(&call->rx_queue, skb);
+	atomic_inc(&call->ackr_not_idle);
+	read_lock(&call->state_lock);
+	if (call->state < RXRPC_CALL_DEAD)
+		rxrpc_queue_call(call);
+	read_unlock(&call->state_lock);
+	_leave(" = 0 [queued]");
+	return 0;
+}
+
+/*
+ * assume an implicit ACKALL of the transmission phase of a client socket upon
+ * reception of the first reply packet
+ */
+static void rxrpc_assume_implicit_ackall(struct rxrpc_call *call, u32 serial)
+{
+	write_lock_bh(&call->state_lock);
+
+	switch (call->state) {
+	case RXRPC_CALL_CLIENT_AWAIT_REPLY:
+		call->state = RXRPC_CALL_CLIENT_RECV_REPLY;
+		call->acks_latest = serial;
+
+		_debug("implicit ACKALL %%%u", call->acks_latest);
+		set_bit(RXRPC_CALL_RCVD_ACKALL, &call->events);
+		write_unlock_bh(&call->state_lock);
+
+		if (try_to_del_timer_sync(&call->resend_timer) >= 0) {
+			clear_bit(RXRPC_CALL_RESEND_TIMER, &call->events);
+			clear_bit(RXRPC_CALL_RESEND, &call->events);
+			clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags);
+		}
+		break;
+
+	default:
+		write_unlock_bh(&call->state_lock);
+		break;
+	}
+}
+
+/*
+ * post an incoming packet to the nominated call to deal with
+ * - must get rid of the sk_buff, either by freeing it or by queuing it
+ */
+void rxrpc_fast_process_packet(struct rxrpc_call *call, struct sk_buff *skb)
+{
+	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+	__be32 _abort_code;
+	u32 serial, hi_serial, seq, abort_code;
+
+	_enter("%p,%p", call, skb);
+
+	ASSERT(!irqs_disabled());
+
+#if 0 // INJECT RX ERROR
+	if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA) {
+		static int skip = 0;
+		if (++skip == 3) {
+			printk("DROPPED 3RD PACKET!!!!!!!!!!!!!\n");
+			skip = 0;
+			goto free_packet;
+		}
+	}
+#endif
+
+	/* track the latest serial number on this connection for ACK packet
+	 * information */
+	serial = ntohl(sp->hdr.serial);
+	hi_serial = atomic_read(&call->conn->hi_serial);
+	while (serial > hi_serial)
+		hi_serial = atomic_cmpxchg(&call->conn->hi_serial, hi_serial,
+					   serial);
+
+	/* request ACK generation for any ACK or DATA packet that requests
+	 * it */
+	if (sp->hdr.flags & RXRPC_REQUEST_ACK) {
+		_proto("ACK Requested on %%%u", serial);
+		rxrpc_propose_ACK(call, RXRPC_ACK_REQUESTED, sp->hdr.serial,
+				  !(sp->hdr.flags & RXRPC_MORE_PACKETS));
+	}
+
+	switch (sp->hdr.type) {
+	case RXRPC_PACKET_TYPE_ABORT:
+		_debug("abort");
+
+		if (skb_copy_bits(skb, 0, &_abort_code,
+				  sizeof(_abort_code)) < 0)
+			goto protocol_error;
+
+		abort_code = ntohl(_abort_code);
+		_proto("Rx ABORT %%%u { %x }", serial, abort_code);
+
+		write_lock_bh(&call->state_lock);
+		if (call->state < RXRPC_CALL_COMPLETE) {
+			call->state = RXRPC_CALL_REMOTELY_ABORTED;
+			call->abort_code = abort_code;
+			set_bit(RXRPC_CALL_RCVD_ABORT, &call->events);
+			rxrpc_queue_call(call);
+		}
+		goto free_packet_unlock;
+
+	case RXRPC_PACKET_TYPE_BUSY:
+		_proto("Rx BUSY %%%u", serial);
+
+		if (call->conn->out_clientflag)
+			goto protocol_error;
+
+		write_lock_bh(&call->state_lock);
+		switch (call->state) {
+		case RXRPC_CALL_CLIENT_SEND_REQUEST:
+			call->state = RXRPC_CALL_SERVER_BUSY;
+			set_bit(RXRPC_CALL_RCVD_BUSY, &call->events);
+			rxrpc_queue_call(call);
+		case RXRPC_CALL_SERVER_BUSY:
+			goto free_packet_unlock;
+		default:
+			goto protocol_error_locked;
+		}
+
+	default:
+		_proto("Rx %s %%%u", rxrpc_pkts[sp->hdr.type], serial);
+		goto protocol_error;
+
+	case RXRPC_PACKET_TYPE_DATA:
+		seq = ntohl(sp->hdr.seq);
+
+		_proto("Rx DATA %%%u { #%u }", serial, seq);
+
+		if (seq == 0)
+			goto protocol_error;
+
+		call->ackr_prev_seq = sp->hdr.seq;
+
+		/* received data implicitly ACKs all of the request packets we
+		 * sent when we're acting as a client */
+		if (call->state == RXRPC_CALL_CLIENT_AWAIT_REPLY)
+			rxrpc_assume_implicit_ackall(call, serial);
+
+		switch (rxrpc_fast_process_data(call, skb, seq)) {
+		case 0:
+			skb = NULL;
+			goto done;
+
+		default:
+			BUG();
+
+			/* data packet received beyond the last packet */
+		case -EBADMSG:
+			goto protocol_error;
+		}
+
+	case RXRPC_PACKET_TYPE_ACK:
+		/* ACK processing is done in process context */
+		read_lock_bh(&call->state_lock);
+		if (call->state < RXRPC_CALL_DEAD) {
+			skb_queue_tail(&call->rx_queue, skb);
+			rxrpc_queue_call(call);
+			skb = NULL;
+		}
+		read_unlock_bh(&call->state_lock);
+		goto free_packet;
+	}
+
+protocol_error:
+	_debug("protocol error");
+	write_lock_bh(&call->state_lock);
+protocol_error_locked:
+	if (call->state <= RXRPC_CALL_COMPLETE) {
+		call->state = RXRPC_CALL_LOCALLY_ABORTED;
+		call->abort_code = RX_PROTOCOL_ERROR;
+		set_bit(RXRPC_CALL_ABORT, &call->events);
+		rxrpc_queue_call(call);
+	}
+free_packet_unlock:
+	write_unlock_bh(&call->state_lock);
+free_packet:
+	rxrpc_free_skb(skb);
+done:
+	_leave("");
+}
+
+/*
+ * split up a jumbo data packet
+ */
+static void rxrpc_process_jumbo_packet(struct rxrpc_call *call,
+				       struct sk_buff *jumbo)
+{
+	struct rxrpc_jumbo_header jhdr;
+	struct rxrpc_skb_priv *sp;
+	struct sk_buff *part;
+
+	_enter(",{%u,%u}", jumbo->data_len, jumbo->len);
+
+	sp = rxrpc_skb(jumbo);
+
+	do {
+		sp->hdr.flags &= ~RXRPC_JUMBO_PACKET;
+
+		/* make a clone to represent the first subpacket in what's left
+		 * of the jumbo packet */
+		part = skb_clone(jumbo, GFP_ATOMIC);
+		if (!part) {
+			/* simply ditch the tail in the event of ENOMEM */
+			pskb_trim(jumbo, RXRPC_JUMBO_DATALEN);
+			break;
+		}
+		rxrpc_new_skb(part);
+
+		pskb_trim(part, RXRPC_JUMBO_DATALEN);
+
+		if (!pskb_pull(jumbo, RXRPC_JUMBO_DATALEN))
+			goto protocol_error;
+
+		if (skb_copy_bits(jumbo, 0, &jhdr, sizeof(jhdr)) < 0)
+			goto protocol_error;
+		if (!pskb_pull(jumbo, sizeof(jhdr)))
+			BUG();
+
+		sp->hdr.seq	= htonl(ntohl(sp->hdr.seq) + 1);
+		sp->hdr.serial	= htonl(ntohl(sp->hdr.serial) + 1);
+		sp->hdr.flags	= jhdr.flags;
+		sp->hdr._rsvd	= jhdr._rsvd;
+
+		_proto("Rx DATA Jumbo %%%u", ntohl(sp->hdr.serial) - 1);
+
+		rxrpc_fast_process_packet(call, part);
+		part = NULL;
+
+	} while (sp->hdr.flags & RXRPC_JUMBO_PACKET);
+
+	rxrpc_fast_process_packet(call, jumbo);
+	_leave("");
+	return;
+
+protocol_error:
+	_debug("protocol error");
+	rxrpc_free_skb(part);
+	rxrpc_free_skb(jumbo);
+	write_lock_bh(&call->state_lock);
+	if (call->state <= RXRPC_CALL_COMPLETE) {
+		call->state = RXRPC_CALL_LOCALLY_ABORTED;
+		call->abort_code = RX_PROTOCOL_ERROR;
+		set_bit(RXRPC_CALL_ABORT, &call->events);
+		rxrpc_queue_call(call);
+	}
+	write_unlock_bh(&call->state_lock);
+	_leave("");
+}
+
+/*
+ * post an incoming packet to the appropriate call/socket to deal with
+ * - must get rid of the sk_buff, either by freeing it or by queuing it
+ */
+static void rxrpc_post_packet_to_call(struct rxrpc_connection *conn,
+				      struct sk_buff *skb)
+{
+	struct rxrpc_skb_priv *sp;
+	struct rxrpc_call *call;
+	struct rb_node *p;
+	__be32 call_id;
+
+	_enter("%p,%p", conn, skb);
+
+	read_lock_bh(&conn->lock);
+
+	sp = rxrpc_skb(skb);
+
+	/* look at extant calls by channel number first */
+	call = conn->channels[ntohl(sp->hdr.cid) & RXRPC_CHANNELMASK];
+	if (!call || call->call_id != sp->hdr.callNumber)
+		goto call_not_extant;
+
+	_debug("extant call [%d]", call->state);
+	ASSERTCMP(call->conn, ==, conn);
+
+	read_lock(&call->state_lock);
+	switch (call->state) {
+	case RXRPC_CALL_LOCALLY_ABORTED:
+		if (!test_and_set_bit(RXRPC_CALL_ABORT, &call->events))
+			rxrpc_queue_call(call);
+	case RXRPC_CALL_REMOTELY_ABORTED:
+	case RXRPC_CALL_NETWORK_ERROR:
+	case RXRPC_CALL_DEAD:
+		goto free_unlock;
+	default:
+		break;
+	}
+
+	read_unlock(&call->state_lock);
+	rxrpc_get_call(call);
+	read_unlock_bh(&conn->lock);
+
+	if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA &&
+	    sp->hdr.flags & RXRPC_JUMBO_PACKET)
+		rxrpc_process_jumbo_packet(call, skb);
+	else
+		rxrpc_fast_process_packet(call, skb);
+
+	rxrpc_put_call(call);
+	goto done;
+
+call_not_extant:
+	/* search the completed calls in case what we're dealing with is
+	 * there */
+	_debug("call not extant");
+
+	call_id = sp->hdr.callNumber;
+	p = conn->calls.rb_node;
+	while (p) {
+		call = rb_entry(p, struct rxrpc_call, conn_node);
+
+		if (call_id < call->call_id)
+			p = p->rb_left;
+		else if (call_id > call->call_id)
+			p = p->rb_right;
+		else
+			goto found_completed_call;
+	}
+
+dead_call:
+	/* it's a either a really old call that we no longer remember or its a
+	 * new incoming call */
+	read_unlock_bh(&conn->lock);
+
+	if (sp->hdr.flags & RXRPC_CLIENT_INITIATED &&
+	    sp->hdr.seq == __constant_cpu_to_be32(1)) {
+		_debug("incoming call");
+		skb_queue_tail(&conn->trans->local->accept_queue, skb);
+		rxrpc_queue_work(&conn->trans->local->acceptor);
+		goto done;
+	}
+
+	_debug("dead call");
+	skb->priority = RX_CALL_DEAD;
+	rxrpc_reject_packet(conn->trans->local, skb);
+	goto done;
+
+	/* resend last packet of a completed call
+	 * - client calls may have been aborted or ACK'd
+	 * - server calls may have been aborted
+	 */
+found_completed_call:
+	_debug("completed call");
+
+	if (atomic_read(&call->usage) == 0)
+		goto dead_call;
+
+	/* synchronise any state changes */
+	read_lock(&call->state_lock);
+	ASSERTIFCMP(call->state != RXRPC_CALL_CLIENT_FINAL_ACK,
+		    call->state, >=, RXRPC_CALL_COMPLETE);
+
+	if (call->state == RXRPC_CALL_LOCALLY_ABORTED ||
+	    call->state == RXRPC_CALL_REMOTELY_ABORTED ||
+	    call->state == RXRPC_CALL_DEAD) {
+		read_unlock(&call->state_lock);
+		goto dead_call;
+	}
+
+	if (call->conn->in_clientflag) {
+		read_unlock(&call->state_lock);
+		goto dead_call; /* complete server call */
+	}
+
+	_debug("final ack again");
+	rxrpc_get_call(call);
+	set_bit(RXRPC_CALL_ACK_FINAL, &call->events);
+	rxrpc_queue_call(call);
+
+free_unlock:
+	read_unlock(&call->state_lock);
+	read_unlock_bh(&conn->lock);
+	rxrpc_free_skb(skb);
+done:
+	_leave("");
+}
+
+/*
+ * post connection-level events to the connection
+ * - this includes challenges, responses and some aborts
+ */
+static void rxrpc_post_packet_to_conn(struct rxrpc_connection *conn,
+				      struct sk_buff *skb)
+{
+	_enter("%p,%p", conn, skb);
+
+	atomic_inc(&conn->usage);
+	skb_queue_tail(&conn->rx_queue, skb);
+	rxrpc_queue_conn(conn);
+}
+
+/*
+ * handle data received on the local endpoint
+ * - may be called in interrupt context
+ */
+void rxrpc_data_ready(struct sock *sk, int count)
+{
+	struct rxrpc_connection *conn;
+	struct rxrpc_transport *trans;
+	struct rxrpc_skb_priv *sp;
+	struct rxrpc_local *local;
+	struct rxrpc_peer *peer;
+	struct sk_buff *skb;
+	int ret;
+
+	_enter("%p, %d", sk, count);
+
+	ASSERT(!irqs_disabled());
+
+	read_lock_bh(&rxrpc_local_lock);
+	local = sk->sk_user_data;
+	if (local && atomic_read(&local->usage) > 0)
+		rxrpc_get_local(local);
+	else
+		local = NULL;
+	read_unlock_bh(&rxrpc_local_lock);
+	if (!local) {
+		_leave(" [local dead]");
+		return;
+	}
+
+	skb = skb_recv_datagram(sk, 0, 1, &ret);
+	if (!skb) {
+		rxrpc_put_local(local);
+		if (ret == -EAGAIN)
+			return;
+		_debug("UDP socket error %d", ret);
+		return;
+	}
+
+	rxrpc_new_skb(skb);
+
+	_net("recv skb %p", skb);
+
+	/* we'll probably need to checksum it (didn't call sock_recvmsg) */
+	if (skb_checksum_complete(skb)) {
+		rxrpc_free_skb(skb);
+		rxrpc_put_local(local);
+		_leave(" [CSUM failed]");
+		return;
+	}
+
+	/* the socket buffer we have is owned by UDP, with UDP's data all over
+	 * it, but we really want our own */
+	skb_orphan(skb);
+	sp = rxrpc_skb(skb);
+	memset(sp, 0, sizeof(*sp));
+
+	_net("Rx UDP packet from %08x:%04hu",
+	     ntohl(ip_hdr(skb)->saddr), ntohs(udp_hdr(skb)->source));
+
+	/* dig out the RxRPC connection details */
+	if (skb_copy_bits(skb, sizeof(struct udphdr), &sp->hdr,
+			  sizeof(sp->hdr)) < 0)
+		goto bad_message;
+	if (!pskb_pull(skb, sizeof(struct udphdr) + sizeof(sp->hdr)))
+		BUG();
+
+	_net("Rx RxRPC %s ep=%x call=%x:%x",
+	     sp->hdr.flags & RXRPC_CLIENT_INITIATED ? "ToServer" : "ToClient",
+	     ntohl(sp->hdr.epoch),
+	     ntohl(sp->hdr.cid),
+	     ntohl(sp->hdr.callNumber));
+
+	if (sp->hdr.type == 0 || sp->hdr.type >= RXRPC_N_PACKET_TYPES) {
+		_proto("Rx Bad Packet Type %u", sp->hdr.type);
+		goto bad_message;
+	}
+
+	if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA &&
+	    (sp->hdr.callNumber == 0 || sp->hdr.seq == 0))
+		goto bad_message;
+
+	peer = rxrpc_find_peer(local, ip_hdr(skb)->saddr, udp_hdr(skb)->source);
+	if (IS_ERR(peer))
+		goto cant_route_call;
+
+	trans = rxrpc_find_transport(local, peer);
+	rxrpc_put_peer(peer);
+	if (!trans)
+		goto cant_route_call;
+
+	conn = rxrpc_find_connection(trans, &sp->hdr);
+	rxrpc_put_transport(trans);
+	if (!conn)
+		goto cant_route_call;
+
+	_debug("CONN %p {%d}", conn, conn->debug_id);
+
+	if (sp->hdr.callNumber == 0)
+		rxrpc_post_packet_to_conn(conn, skb);
+	else
+		rxrpc_post_packet_to_call(conn, skb);
+	rxrpc_put_connection(conn);
+	rxrpc_put_local(local);
+	return;
+
+cant_route_call:
+	_debug("can't route call");
+	if (sp->hdr.flags & RXRPC_CLIENT_INITIATED &&
+	    sp->hdr.type == RXRPC_PACKET_TYPE_DATA) {
+		if (sp->hdr.seq == __constant_cpu_to_be32(1)) {
+			_debug("first packet");
+			skb_queue_tail(&local->accept_queue, skb);
+			rxrpc_queue_work(&local->acceptor);
+			rxrpc_put_local(local);
+			_leave(" [incoming]");
+			return;
+		}
+		skb->priority = RX_INVALID_OPERATION;
+	} else {
+		skb->priority = RX_CALL_DEAD;
+	}
+
+	_debug("reject");
+	rxrpc_reject_packet(local, skb);
+	rxrpc_put_local(local);
+	_leave(" [no call]");
+	return;
+
+bad_message:
+	skb->priority = RX_PROTOCOL_ERROR;
+	rxrpc_reject_packet(local, skb);
+	rxrpc_put_local(local);
+	_leave(" [badmsg]");
+}
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
new file mode 100644
index 0000000..58aaf89
--- /dev/null
+++ b/net/rxrpc/ar-internal.h
@@ -0,0 +1,808 @@
+/* AF_RXRPC internal definitions
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#include <rxrpc/packet.h>
+
+#if 0
+#define CHECK_SLAB_OKAY(X)				     \
+	BUG_ON(atomic_read((X)) >> (sizeof(atomic_t) - 2) == \
+	       (POISON_FREE << 8 | POISON_FREE))
+#else
+#define CHECK_SLAB_OKAY(X) do {} while(0)
+#endif
+
+#define FCRYPT_BSIZE 8
+struct rxrpc_crypt {
+	union {
+		u8	x[FCRYPT_BSIZE];
+		u32	n[2];
+	};
+} __attribute__((aligned(8)));
+
+#define rxrpc_queue_work(WS)	queue_work(rxrpc_workqueue, (WS))
+#define rxrpc_queue_delayed_work(WS,D)	\
+	queue_delayed_work(rxrpc_workqueue, (WS), (D))
+
+#define rxrpc_queue_call(CALL)	rxrpc_queue_work(&(CALL)->processor)
+#define rxrpc_queue_conn(CONN)	rxrpc_queue_work(&(CONN)->processor)
+
+/*
+ * sk_state for RxRPC sockets
+ */
+enum {
+	RXRPC_UNCONNECTED = 0,
+	RXRPC_CLIENT_BOUND,		/* client local address bound */
+	RXRPC_CLIENT_CONNECTED,		/* client is connected */
+	RXRPC_SERVER_BOUND,		/* server local address bound */
+	RXRPC_SERVER_LISTENING,		/* server listening for connections */
+	RXRPC_CLOSE,			/* socket is being closed */
+};
+
+/*
+ * RxRPC socket definition
+ */
+struct rxrpc_sock {
+	/* WARNING: sk has to be the first member */
+	struct sock		sk;
+	rxrpc_interceptor_t	interceptor;	/* kernel service Rx interceptor function */
+	struct rxrpc_local	*local;		/* local endpoint */
+	struct rxrpc_transport	*trans;		/* transport handler */
+	struct rxrpc_conn_bundle *bundle;	/* virtual connection bundle */
+	struct rxrpc_connection	*conn;		/* exclusive virtual connection */
+	struct list_head	listen_link;	/* link in the local endpoint's listen list */
+	struct list_head	secureq;	/* calls awaiting connection security clearance */
+	struct list_head	acceptq;	/* calls awaiting acceptance */
+	struct key		*key;		/* security for this socket */
+	struct key		*securities;	/* list of server security descriptors */
+	struct rb_root		calls;		/* outstanding calls on this socket */
+	unsigned long		flags;
+#define RXRPC_SOCK_EXCLUSIVE_CONN	1	/* exclusive connection for a client socket */
+	rwlock_t		call_lock;	/* lock for calls */
+	u32			min_sec_level;	/* minimum security level */
+#define RXRPC_SECURITY_MAX	RXRPC_SECURITY_ENCRYPT
+	struct sockaddr_rxrpc	srx;		/* local address */
+	sa_family_t		proto;		/* protocol created with */
+	__be16			service_id;	/* service ID of local/remote service */
+};
+
+#define rxrpc_sk(__sk) container_of((__sk), struct rxrpc_sock, sk)
+
+/*
+ * RxRPC socket buffer private variables
+ * - max 48 bytes (struct sk_buff::cb)
+ */
+struct rxrpc_skb_priv {
+	struct rxrpc_call	*call;		/* call with which associated */
+	unsigned long		resend_at;	/* time in jiffies at which to resend */
+	union {
+		unsigned	offset;		/* offset into buffer of next read */
+		int		remain;		/* amount of space remaining for next write */
+		u32		error;		/* network error code */
+		bool		need_resend;	/* T if needs resending */
+	};
+
+	struct rxrpc_header	hdr;		/* RxRPC packet header from this packet */
+};
+
+#define rxrpc_skb(__skb) ((struct rxrpc_skb_priv *) &(__skb)->cb)
+
+enum rxrpc_command {
+	RXRPC_CMD_SEND_DATA,		/* send data message */
+	RXRPC_CMD_SEND_ABORT,		/* request abort generation */
+	RXRPC_CMD_ACCEPT,		/* [server] accept incoming call */
+	RXRPC_CMD_REJECT_BUSY,		/* [server] reject a call as busy */
+};
+
+/*
+ * RxRPC security module interface
+ */
+struct rxrpc_security {
+	struct module		*owner;		/* providing module */
+	struct list_head	link;		/* link in master list */
+	const char		*name;		/* name of this service */
+	u8			security_index;	/* security type provided */
+
+	/* initialise a connection's security */
+	int (*init_connection_security)(struct rxrpc_connection *);
+
+	/* prime a connection's packet security */
+	void (*prime_packet_security)(struct rxrpc_connection *);
+
+	/* impose security on a packet */
+	int (*secure_packet)(const struct rxrpc_call *,
+			     struct sk_buff *,
+			     size_t,
+			     void *);
+
+	/* verify the security on a received packet */
+	int (*verify_packet)(const struct rxrpc_call *, struct sk_buff *,
+			     u32 *);
+
+	/* issue a challenge */
+	int (*issue_challenge)(struct rxrpc_connection *);
+
+	/* respond to a challenge */
+	int (*respond_to_challenge)(struct rxrpc_connection *,
+				    struct sk_buff *,
+				    u32 *);
+
+	/* verify a response */
+	int (*verify_response)(struct rxrpc_connection *,
+			       struct sk_buff *,
+			       u32 *);
+
+	/* clear connection security */
+	void (*clear)(struct rxrpc_connection *);
+};
+
+/*
+ * RxRPC local transport endpoint definition
+ * - matched by local port, address and protocol type
+ */
+struct rxrpc_local {
+	struct socket		*socket;	/* my UDP socket */
+	struct work_struct	destroyer;	/* endpoint destroyer */
+	struct work_struct	acceptor;	/* incoming call processor */
+	struct work_struct	rejecter;	/* packet reject writer */
+	struct list_head	services;	/* services listening on this endpoint */
+	struct list_head	link;		/* link in endpoint list */
+	struct rw_semaphore	defrag_sem;	/* control re-enablement of IP DF bit */
+	struct sk_buff_head	accept_queue;	/* incoming calls awaiting acceptance */
+	struct sk_buff_head	reject_queue;	/* packets awaiting rejection */
+	spinlock_t		lock;		/* access lock */
+	rwlock_t		services_lock;	/* lock for services list */
+	atomic_t		usage;
+	int			debug_id;	/* debug ID for printks */
+	volatile char		error_rcvd;	/* T if received ICMP error outstanding */
+	struct sockaddr_rxrpc	srx;		/* local address */
+};
+
+/*
+ * RxRPC remote transport endpoint definition
+ * - matched by remote port, address and protocol type
+ * - holds the connection ID counter for connections between the two endpoints
+ */
+struct rxrpc_peer {
+	struct work_struct	destroyer;	/* peer destroyer */
+	struct list_head	link;		/* link in master peer list */
+	struct list_head	error_targets;	/* targets for net error distribution */
+	spinlock_t		lock;		/* access lock */
+	atomic_t		usage;
+	unsigned		if_mtu;		/* interface MTU for this peer */
+	unsigned		mtu;		/* network MTU for this peer */
+	unsigned		maxdata;	/* data size (MTU - hdrsize) */
+	unsigned short		hdrsize;	/* header size (IP + UDP + RxRPC) */
+	int			debug_id;	/* debug ID for printks */
+	int			net_error;	/* network error distributed */
+	struct sockaddr_rxrpc	srx;		/* remote address */
+
+	/* calculated RTT cache */
+#define RXRPC_RTT_CACHE_SIZE 32
+	suseconds_t		rtt;		/* current RTT estimate (in uS) */
+	unsigned		rtt_point;	/* next entry at which to insert */
+	unsigned		rtt_usage;	/* amount of cache actually used */
+	suseconds_t		rtt_cache[RXRPC_RTT_CACHE_SIZE]; /* calculated RTT cache */
+};
+
+/*
+ * RxRPC point-to-point transport / connection manager definition
+ * - handles a bundle of connections between two endpoints
+ * - matched by { local, peer }
+ */
+struct rxrpc_transport {
+	struct rxrpc_local	*local;		/* local transport endpoint */
+	struct rxrpc_peer	*peer;		/* remote transport endpoint */
+	struct work_struct	error_handler;	/* network error distributor */
+	struct rb_root		bundles;	/* client connection bundles on this transport */
+	struct rb_root		client_conns;	/* client connections on this transport */
+	struct rb_root		server_conns;	/* server connections on this transport */
+	struct list_head	link;		/* link in master session list */
+	struct sk_buff_head	error_queue;	/* error packets awaiting processing */
+	time_t			put_time;	/* time at which to reap */
+	spinlock_t		client_lock;	/* client connection allocation lock */
+	rwlock_t		conn_lock;	/* lock for active/dead connections */
+	atomic_t		usage;
+	int			debug_id;	/* debug ID for printks */
+	unsigned int		conn_idcounter;	/* connection ID counter (client) */
+};
+
+/*
+ * RxRPC client connection bundle
+ * - matched by { transport, service_id, key }
+ */
+struct rxrpc_conn_bundle {
+	struct rb_node		node;		/* node in transport's lookup tree */
+	struct list_head	unused_conns;	/* unused connections in this bundle */
+	struct list_head	avail_conns;	/* available connections in this bundle */
+	struct list_head	busy_conns;	/* busy connections in this bundle */
+	struct key		*key;		/* security for this bundle */
+	wait_queue_head_t	chanwait;	/* wait for channel to become available */
+	atomic_t		usage;
+	int			debug_id;	/* debug ID for printks */
+	unsigned short		num_conns;	/* number of connections in this bundle */
+	__be16			service_id;	/* service ID */
+	uint8_t			security_ix;	/* security type */
+};
+
+/*
+ * RxRPC connection definition
+ * - matched by { transport, service_id, conn_id, direction, key }
+ * - each connection can only handle four simultaneous calls
+ */
+struct rxrpc_connection {
+	struct rxrpc_transport	*trans;		/* transport session */
+	struct rxrpc_conn_bundle *bundle;	/* connection bundle (client) */
+	struct work_struct	processor;	/* connection event processor */
+	struct rb_node		node;		/* node in transport's lookup tree */
+	struct list_head	link;		/* link in master connection list */
+	struct list_head	bundle_link;	/* link in bundle */
+	struct rb_root		calls;		/* calls on this connection */
+	struct sk_buff_head	rx_queue;	/* received conn-level packets */
+	struct rxrpc_call	*channels[RXRPC_MAXCALLS]; /* channels (active calls) */
+	struct rxrpc_security	*security;	/* applied security module */
+	struct key		*key;		/* security for this connection (client) */
+	struct key		*server_key;	/* security for this service */
+	struct crypto_blkcipher	*cipher;	/* encryption handle */
+	struct rxrpc_crypt	csum_iv;	/* packet checksum base */
+	unsigned long		events;
+#define RXRPC_CONN_CHALLENGE	0		/* send challenge packet */
+	time_t			put_time;	/* time at which to reap */
+	rwlock_t		lock;		/* access lock */
+	spinlock_t		state_lock;	/* state-change lock */
+	atomic_t		usage;
+	u32			real_conn_id;	/* connection ID (host-endian) */
+	enum {					/* current state of connection */
+		RXRPC_CONN_UNUSED,		/* - connection not yet attempted */
+		RXRPC_CONN_CLIENT,		/* - client connection */
+		RXRPC_CONN_SERVER_UNSECURED,	/* - server unsecured connection */
+		RXRPC_CONN_SERVER_CHALLENGING,	/* - server challenging for security */
+		RXRPC_CONN_SERVER,		/* - server secured connection */
+		RXRPC_CONN_REMOTELY_ABORTED,	/* - conn aborted by peer */
+		RXRPC_CONN_LOCALLY_ABORTED,	/* - conn aborted locally */
+		RXRPC_CONN_NETWORK_ERROR,	/* - conn terminated by network error */
+	} state;
+	int			error;		/* error code for local abort */
+	int			debug_id;	/* debug ID for printks */
+	unsigned		call_counter;	/* call ID counter */
+	atomic_t		serial;		/* packet serial number counter */
+	atomic_t		hi_serial;	/* highest serial number received */
+	u8			avail_calls;	/* number of calls available */
+	u8			size_align;	/* data size alignment (for security) */
+	u8			header_size;	/* rxrpc + security header size */
+	u8			security_size;	/* security header size */
+	u32			security_level;	/* security level negotiated */
+	u32			security_nonce;	/* response re-use preventer */
+
+	/* the following are all in net order */
+	__be32			epoch;		/* epoch of this connection */
+	__be32			cid;		/* connection ID */
+	__be16			service_id;	/* service ID */
+	u8			security_ix;	/* security type */
+	u8			in_clientflag;	/* RXRPC_CLIENT_INITIATED if we are server */
+	u8			out_clientflag;	/* RXRPC_CLIENT_INITIATED if we are client */
+};
+
+/*
+ * RxRPC call definition
+ * - matched by { connection, call_id }
+ */
+struct rxrpc_call {
+	struct rxrpc_connection	*conn;		/* connection carrying call */
+	struct rxrpc_sock	*socket;	/* socket responsible */
+	struct timer_list	lifetimer;	/* lifetime remaining on call */
+	struct timer_list	deadspan;	/* reap timer for re-ACK'ing, etc  */
+	struct timer_list	ack_timer;	/* ACK generation timer */
+	struct timer_list	resend_timer;	/* Tx resend timer */
+	struct work_struct	destroyer;	/* call destroyer */
+	struct work_struct	processor;	/* packet processor and ACK generator */
+	struct list_head	link;		/* link in master call list */
+	struct list_head	error_link;	/* link in error distribution list */
+	struct list_head	accept_link;	/* calls awaiting acceptance */
+	struct rb_node		sock_node;	/* node in socket call tree */
+	struct rb_node		conn_node;	/* node in connection call tree */
+	struct sk_buff_head	rx_queue;	/* received packets */
+	struct sk_buff_head	rx_oos_queue;	/* packets received out of sequence */
+	struct sk_buff		*tx_pending;	/* Tx socket buffer being filled */
+	wait_queue_head_t	tx_waitq;	/* wait for Tx window space to become available */
+	unsigned long		user_call_ID;	/* user-defined call ID */
+	unsigned long		creation_jif;	/* time of call creation */
+	unsigned long		flags;
+#define RXRPC_CALL_RELEASED	0	/* call has been released - no more message to userspace */
+#define RXRPC_CALL_TERMINAL_MSG	1	/* call has given the socket its final message */
+#define RXRPC_CALL_RCVD_LAST	2	/* all packets received */
+#define RXRPC_CALL_RUN_RTIMER	3	/* Tx resend timer started */
+#define RXRPC_CALL_TX_SOFT_ACK	4	/* sent some soft ACKs */
+#define RXRPC_CALL_PROC_BUSY	5	/* the processor is busy */
+#define RXRPC_CALL_INIT_ACCEPT	6	/* acceptance was initiated */
+#define RXRPC_CALL_HAS_USERID	7	/* has a user ID attached */
+#define RXRPC_CALL_EXPECT_OOS	8	/* expect out of sequence packets */
+	unsigned long		events;
+#define RXRPC_CALL_RCVD_ACKALL	0	/* ACKALL or reply received */
+#define RXRPC_CALL_RCVD_BUSY	1	/* busy packet received */
+#define RXRPC_CALL_RCVD_ABORT	2	/* abort packet received */
+#define RXRPC_CALL_RCVD_ERROR	3	/* network error received */
+#define RXRPC_CALL_ACK_FINAL	4	/* need to generate final ACK (and release call) */
+#define RXRPC_CALL_ACK		5	/* need to generate ACK */
+#define RXRPC_CALL_REJECT_BUSY	6	/* need to generate busy message */
+#define RXRPC_CALL_ABORT	7	/* need to generate abort */
+#define RXRPC_CALL_CONN_ABORT	8	/* local connection abort generated */
+#define RXRPC_CALL_RESEND_TIMER	9	/* Tx resend timer expired */
+#define RXRPC_CALL_RESEND	10	/* Tx resend required */
+#define RXRPC_CALL_DRAIN_RX_OOS	11	/* drain the Rx out of sequence queue */
+#define RXRPC_CALL_LIFE_TIMER	12	/* call's lifetimer ran out */
+#define RXRPC_CALL_ACCEPTED	13	/* incoming call accepted by userspace app */
+#define RXRPC_CALL_SECURED	14	/* incoming call's connection is now secure */
+#define RXRPC_CALL_POST_ACCEPT	15	/* need to post an "accept?" message to the app */
+#define RXRPC_CALL_RELEASE	16	/* need to release the call's resources */
+
+	spinlock_t		lock;
+	rwlock_t		state_lock;	/* lock for state transition */
+	atomic_t		usage;
+	atomic_t		sequence;	/* Tx data packet sequence counter */
+	u32			abort_code;	/* local/remote abort code */
+	enum {					/* current state of call */
+		RXRPC_CALL_CLIENT_SEND_REQUEST,	/* - client sending request phase */
+		RXRPC_CALL_CLIENT_AWAIT_REPLY,	/* - client awaiting reply */
+		RXRPC_CALL_CLIENT_RECV_REPLY,	/* - client receiving reply phase */
+		RXRPC_CALL_CLIENT_FINAL_ACK,	/* - client sending final ACK phase */
+		RXRPC_CALL_SERVER_SECURING,	/* - server securing request connection */
+		RXRPC_CALL_SERVER_ACCEPTING,	/* - server accepting request */
+		RXRPC_CALL_SERVER_RECV_REQUEST,	/* - server receiving request */
+		RXRPC_CALL_SERVER_ACK_REQUEST,	/* - server pending ACK of request */
+		RXRPC_CALL_SERVER_SEND_REPLY,	/* - server sending reply */
+		RXRPC_CALL_SERVER_AWAIT_ACK,	/* - server awaiting final ACK */
+		RXRPC_CALL_COMPLETE,		/* - call completed */
+		RXRPC_CALL_SERVER_BUSY,		/* - call rejected by busy server */
+		RXRPC_CALL_REMOTELY_ABORTED,	/* - call aborted by peer */
+		RXRPC_CALL_LOCALLY_ABORTED,	/* - call aborted locally on error or close */
+		RXRPC_CALL_NETWORK_ERROR,	/* - call terminated by network error */
+		RXRPC_CALL_DEAD,		/* - call is dead */
+	} state;
+	int			debug_id;	/* debug ID for printks */
+	u8			channel;	/* connection channel occupied by this call */
+
+	/* transmission-phase ACK management */
+	uint8_t			acks_head;	/* offset into window of first entry */
+	uint8_t			acks_tail;	/* offset into window of last entry */
+	uint8_t			acks_winsz;	/* size of un-ACK'd window */
+	uint8_t			acks_unacked;	/* lowest unacked packet in last ACK received */
+	int			acks_latest;	/* serial number of latest ACK received */
+	rxrpc_seq_t		acks_hard;	/* highest definitively ACK'd msg seq */
+	unsigned long		*acks_window;	/* sent packet window
+						 * - elements are pointers with LSB set if ACK'd
+						 */
+
+	/* receive-phase ACK management */
+	rxrpc_seq_t		rx_data_expect;	/* next data seq ID expected to be received */
+	rxrpc_seq_t		rx_data_post;	/* next data seq ID expected to be posted */
+	rxrpc_seq_t		rx_data_recv;	/* last data seq ID encountered by recvmsg */
+	rxrpc_seq_t		rx_data_eaten;	/* last data seq ID consumed by recvmsg */
+	rxrpc_seq_t		rx_first_oos;	/* first packet in rx_oos_queue (or 0) */
+	rxrpc_seq_t		ackr_win_top;	/* top of ACK window (rx_data_eaten is bottom) */
+	rxrpc_seq_net_t		ackr_prev_seq;	/* previous sequence number received */
+	uint8_t			ackr_reason;	/* reason to ACK */
+	__be32			ackr_serial;	/* serial of packet being ACK'd */
+	atomic_t		ackr_not_idle;	/* number of packets in Rx queue */
+
+	/* received packet records, 1 bit per record */
+#define RXRPC_ACKR_WINDOW_ASZ DIV_ROUND_UP(RXRPC_MAXACKS, BITS_PER_LONG)
+	unsigned long		ackr_window[RXRPC_ACKR_WINDOW_ASZ + 1];
+
+	/* the following should all be in net order */
+	__be32			cid;		/* connection ID + channel index  */
+	__be32			call_id;	/* call ID on connection  */
+};
+
+/*
+ * RxRPC key for Kerberos (type-2 security)
+ */
+struct rxkad_key {
+	u16	security_index;		/* RxRPC header security index */
+	u16	ticket_len;		/* length of ticket[] */
+	u32	expiry;			/* time at which expires */
+	u32	kvno;			/* key version number */
+	u8	session_key[8];		/* DES session key */
+	u8	ticket[0];		/* the encrypted ticket */
+};
+
+struct rxrpc_key_payload {
+	struct rxkad_key k;
+};
+
+/*
+ * locally abort an RxRPC call
+ */
+static inline void rxrpc_abort_call(struct rxrpc_call *call, u32 abort_code)
+{
+	write_lock_bh(&call->state_lock);
+	if (call->state < RXRPC_CALL_COMPLETE) {
+		call->abort_code = abort_code;
+		call->state = RXRPC_CALL_LOCALLY_ABORTED;
+		set_bit(RXRPC_CALL_ABORT, &call->events);
+	}
+	write_unlock_bh(&call->state_lock);
+}
+
+/*
+ * af_rxrpc.c
+ */
+extern atomic_t rxrpc_n_skbs;
+extern __be32 rxrpc_epoch;
+extern atomic_t rxrpc_debug_id;
+extern struct workqueue_struct *rxrpc_workqueue;
+
+/*
+ * ar-accept.c
+ */
+extern void rxrpc_accept_incoming_calls(struct work_struct *);
+extern struct rxrpc_call *rxrpc_accept_call(struct rxrpc_sock *,
+					    unsigned long);
+extern int rxrpc_reject_call(struct rxrpc_sock *);
+
+/*
+ * ar-ack.c
+ */
+extern void __rxrpc_propose_ACK(struct rxrpc_call *, uint8_t, __be32, bool);
+extern void rxrpc_propose_ACK(struct rxrpc_call *, uint8_t, __be32, bool);
+extern void rxrpc_process_call(struct work_struct *);
+
+/*
+ * ar-call.c
+ */
+extern struct kmem_cache *rxrpc_call_jar;
+extern struct list_head rxrpc_calls;
+extern rwlock_t rxrpc_call_lock;
+
+extern struct rxrpc_call *rxrpc_get_client_call(struct rxrpc_sock *,
+						struct rxrpc_transport *,
+						struct rxrpc_conn_bundle *,
+						unsigned long, int, gfp_t);
+extern struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *,
+					      struct rxrpc_connection *,
+					      struct rxrpc_header *, gfp_t);
+extern struct rxrpc_call *rxrpc_find_server_call(struct rxrpc_sock *,
+						 unsigned long);
+extern void rxrpc_release_call(struct rxrpc_call *);
+extern void rxrpc_release_calls_on_socket(struct rxrpc_sock *);
+extern void __rxrpc_put_call(struct rxrpc_call *);
+extern void __exit rxrpc_destroy_all_calls(void);
+
+/*
+ * ar-connection.c
+ */
+extern struct list_head rxrpc_connections;
+extern rwlock_t rxrpc_connection_lock;
+
+extern struct rxrpc_conn_bundle *rxrpc_get_bundle(struct rxrpc_sock *,
+						  struct rxrpc_transport *,
+						  struct key *,
+						  __be16, gfp_t);
+extern void rxrpc_put_bundle(struct rxrpc_transport *,
+			     struct rxrpc_conn_bundle *);
+extern int rxrpc_connect_call(struct rxrpc_sock *, struct rxrpc_transport *,
+			      struct rxrpc_conn_bundle *, struct rxrpc_call *,
+			      gfp_t);
+extern void rxrpc_put_connection(struct rxrpc_connection *);
+extern void __exit rxrpc_destroy_all_connections(void);
+extern struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_transport *,
+						      struct rxrpc_header *);
+extern struct rxrpc_connection *
+rxrpc_incoming_connection(struct rxrpc_transport *, struct rxrpc_header *,
+			  gfp_t);
+
+/*
+ * ar-connevent.c
+ */
+extern void rxrpc_process_connection(struct work_struct *);
+extern void rxrpc_reject_packet(struct rxrpc_local *, struct sk_buff *);
+extern void rxrpc_reject_packets(struct work_struct *);
+
+/*
+ * ar-error.c
+ */
+extern void rxrpc_UDP_error_report(struct sock *);
+extern void rxrpc_UDP_error_handler(struct work_struct *);
+
+/*
+ * ar-input.c
+ */
+extern unsigned long rxrpc_ack_timeout;
+extern const char *rxrpc_pkts[];
+
+extern void rxrpc_data_ready(struct sock *, int);
+extern int rxrpc_queue_rcv_skb(struct rxrpc_call *, struct sk_buff *, bool,
+			       bool);
+extern void rxrpc_fast_process_packet(struct rxrpc_call *, struct sk_buff *);
+
+/*
+ * ar-local.c
+ */
+extern rwlock_t rxrpc_local_lock;
+extern struct rxrpc_local *rxrpc_lookup_local(struct sockaddr_rxrpc *);
+extern void rxrpc_put_local(struct rxrpc_local *);
+extern void __exit rxrpc_destroy_all_locals(void);
+
+/*
+ * ar-key.c
+ */
+extern struct key_type key_type_rxrpc;
+extern struct key_type key_type_rxrpc_s;
+
+extern int rxrpc_request_key(struct rxrpc_sock *, char __user *, int);
+extern int rxrpc_server_keyring(struct rxrpc_sock *, char __user *, int);
+extern int rxrpc_get_server_data_key(struct rxrpc_connection *, const void *,
+				     time_t, u32);
+
+/*
+ * ar-output.c
+ */
+extern int rxrpc_resend_timeout;
+
+extern int rxrpc_send_packet(struct rxrpc_transport *, struct sk_buff *);
+extern int rxrpc_client_sendmsg(struct kiocb *, struct rxrpc_sock *,
+				struct rxrpc_transport *, struct msghdr *,
+				size_t);
+extern int rxrpc_server_sendmsg(struct kiocb *, struct rxrpc_sock *,
+				struct msghdr *, size_t);
+
+/*
+ * ar-peer.c
+ */
+extern struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *, gfp_t);
+extern void rxrpc_put_peer(struct rxrpc_peer *);
+extern struct rxrpc_peer *rxrpc_find_peer(struct rxrpc_local *,
+					  __be32, __be16);
+extern void __exit rxrpc_destroy_all_peers(void);
+
+/*
+ * ar-proc.c
+ */
+extern const char *rxrpc_call_states[];
+extern struct file_operations rxrpc_call_seq_fops;
+extern struct file_operations rxrpc_connection_seq_fops;
+
+/*
+ * ar-recvmsg.c
+ */
+extern void rxrpc_remove_user_ID(struct rxrpc_sock *, struct rxrpc_call *);
+extern int rxrpc_recvmsg(struct kiocb *, struct socket *, struct msghdr *,
+			 size_t, int);
+
+/*
+ * ar-security.c
+ */
+extern int rxrpc_register_security(struct rxrpc_security *);
+extern void rxrpc_unregister_security(struct rxrpc_security *);
+extern int rxrpc_init_client_conn_security(struct rxrpc_connection *);
+extern int rxrpc_init_server_conn_security(struct rxrpc_connection *);
+extern int rxrpc_secure_packet(const struct rxrpc_call *, struct sk_buff *,
+			       size_t, void *);
+extern int rxrpc_verify_packet(const struct rxrpc_call *, struct sk_buff *,
+			       u32 *);
+extern void rxrpc_clear_conn_security(struct rxrpc_connection *);
+
+/*
+ * ar-skbuff.c
+ */
+extern void rxrpc_packet_destructor(struct sk_buff *);
+
+/*
+ * ar-transport.c
+ */
+extern struct rxrpc_transport *rxrpc_get_transport(struct rxrpc_local *,
+						   struct rxrpc_peer *,
+						   gfp_t);
+extern void rxrpc_put_transport(struct rxrpc_transport *);
+extern void __exit rxrpc_destroy_all_transports(void);
+extern struct rxrpc_transport *rxrpc_find_transport(struct rxrpc_local *,
+						    struct rxrpc_peer *);
+
+/*
+ * debug tracing
+ */
+extern unsigned rxrpc_debug;
+
+#define dbgprintk(FMT,...) \
+	printk("[%x%-6.6s] "FMT"\n", smp_processor_id(), current->comm ,##__VA_ARGS__)
+
+/* make sure we maintain the format strings, even when debugging is disabled */
+static inline __attribute__((format(printf,1,2)))
+void _dbprintk(const char *fmt, ...)
+{
+}
+
+#define kenter(FMT,...)	dbgprintk("==> %s("FMT")",__FUNCTION__ ,##__VA_ARGS__)
+#define kleave(FMT,...)	dbgprintk("<== %s()"FMT"",__FUNCTION__ ,##__VA_ARGS__)
+#define kdebug(FMT,...)	dbgprintk("    "FMT ,##__VA_ARGS__)
+#define kproto(FMT,...)	dbgprintk("### "FMT ,##__VA_ARGS__)
+#define knet(FMT,...)	dbgprintk("@@@ "FMT ,##__VA_ARGS__)
+
+
+#if defined(__KDEBUG)
+#define _enter(FMT,...)	kenter(FMT,##__VA_ARGS__)
+#define _leave(FMT,...)	kleave(FMT,##__VA_ARGS__)
+#define _debug(FMT,...)	kdebug(FMT,##__VA_ARGS__)
+#define _proto(FMT,...)	kproto(FMT,##__VA_ARGS__)
+#define _net(FMT,...)	knet(FMT,##__VA_ARGS__)
+
+#elif defined(CONFIG_AF_RXRPC_DEBUG)
+#define RXRPC_DEBUG_KENTER	0x01
+#define RXRPC_DEBUG_KLEAVE	0x02
+#define RXRPC_DEBUG_KDEBUG	0x04
+#define RXRPC_DEBUG_KPROTO	0x08
+#define RXRPC_DEBUG_KNET	0x10
+
+#define _enter(FMT,...)					\
+do {							\
+	if (unlikely(rxrpc_debug & RXRPC_DEBUG_KENTER))	\
+		kenter(FMT,##__VA_ARGS__);		\
+} while (0)
+
+#define _leave(FMT,...)					\
+do {							\
+	if (unlikely(rxrpc_debug & RXRPC_DEBUG_KLEAVE))	\
+		kleave(FMT,##__VA_ARGS__);		\
+} while (0)
+
+#define _debug(FMT,...)					\
+do {							\
+	if (unlikely(rxrpc_debug & RXRPC_DEBUG_KDEBUG))	\
+		kdebug(FMT,##__VA_ARGS__);		\
+} while (0)
+
+#define _proto(FMT,...)					\
+do {							\
+	if (unlikely(rxrpc_debug & RXRPC_DEBUG_KPROTO))	\
+		kproto(FMT,##__VA_ARGS__);		\
+} while (0)
+
+#define _net(FMT,...)					\
+do {							\
+	if (unlikely(rxrpc_debug & RXRPC_DEBUG_KNET))	\
+		knet(FMT,##__VA_ARGS__);		\
+} while (0)
+
+#else
+#define _enter(FMT,...)	_dbprintk("==> %s("FMT")",__FUNCTION__ ,##__VA_ARGS__)
+#define _leave(FMT,...)	_dbprintk("<== %s()"FMT"",__FUNCTION__ ,##__VA_ARGS__)
+#define _debug(FMT,...)	_dbprintk("    "FMT ,##__VA_ARGS__)
+#define _proto(FMT,...)	_dbprintk("### "FMT ,##__VA_ARGS__)
+#define _net(FMT,...)	_dbprintk("@@@ "FMT ,##__VA_ARGS__)
+#endif
+
+/*
+ * debug assertion checking
+ */
+#if 1 // defined(__KDEBUGALL)
+
+#define ASSERT(X)						\
+do {								\
+	if (unlikely(!(X))) {					\
+		printk(KERN_ERR "\n");				\
+		printk(KERN_ERR "RxRPC: Assertion failed\n");	\
+		BUG();						\
+	}							\
+} while(0)
+
+#define ASSERTCMP(X, OP, Y)						\
+do {									\
+	if (unlikely(!((X) OP (Y)))) {					\
+		printk(KERN_ERR "\n");					\
+		printk(KERN_ERR "RxRPC: Assertion failed\n");		\
+		printk(KERN_ERR "%lu " #OP " %lu is false\n",		\
+		       (unsigned long)(X), (unsigned long)(Y));		\
+		printk(KERN_ERR "0x%lx " #OP " 0x%lx is false\n",	\
+		       (unsigned long)(X), (unsigned long)(Y));		\
+		BUG();							\
+	}								\
+} while(0)
+
+#define ASSERTIF(C, X)						\
+do {								\
+	if (unlikely((C) && !(X))) {				\
+		printk(KERN_ERR "\n");				\
+		printk(KERN_ERR "RxRPC: Assertion failed\n");	\
+		BUG();						\
+	}							\
+} while(0)
+
+#define ASSERTIFCMP(C, X, OP, Y)					\
+do {									\
+	if (unlikely((C) && !((X) OP (Y)))) {				\
+		printk(KERN_ERR "\n");					\
+		printk(KERN_ERR "RxRPC: Assertion failed\n");		\
+		printk(KERN_ERR "%lu " #OP " %lu is false\n",		\
+		       (unsigned long)(X), (unsigned long)(Y));		\
+		printk(KERN_ERR "0x%lx " #OP " 0x%lx is false\n",	\
+		       (unsigned long)(X), (unsigned long)(Y));		\
+		BUG();							\
+	}								\
+} while(0)
+
+#else
+
+#define ASSERT(X)				\
+do {						\
+} while(0)
+
+#define ASSERTCMP(X, OP, Y)			\
+do {						\
+} while(0)
+
+#define ASSERTIF(C, X)				\
+do {						\
+} while(0)
+
+#define ASSERTIFCMP(C, X, OP, Y)		\
+do {						\
+} while(0)
+
+#endif /* __KDEBUGALL */
+
+/*
+ * socket buffer accounting / leak finding
+ */
+static inline void __rxrpc_new_skb(struct sk_buff *skb, const char *fn)
+{
+	//_net("new skb %p %s [%d]", skb, fn, atomic_read(&rxrpc_n_skbs));
+	//atomic_inc(&rxrpc_n_skbs);
+}
+
+#define rxrpc_new_skb(skb) __rxrpc_new_skb((skb), __func__)
+
+static inline void __rxrpc_kill_skb(struct sk_buff *skb, const char *fn)
+{
+	//_net("kill skb %p %s [%d]", skb, fn, atomic_read(&rxrpc_n_skbs));
+	//atomic_dec(&rxrpc_n_skbs);
+}
+
+#define rxrpc_kill_skb(skb) __rxrpc_kill_skb((skb), __func__)
+
+static inline void __rxrpc_free_skb(struct sk_buff *skb, const char *fn)
+{
+	if (skb) {
+		CHECK_SLAB_OKAY(&skb->users);
+		//_net("free skb %p %s [%d]",
+		//     skb, fn, atomic_read(&rxrpc_n_skbs));
+		//atomic_dec(&rxrpc_n_skbs);
+		kfree_skb(skb);
+	}
+}
+
+#define rxrpc_free_skb(skb) __rxrpc_free_skb((skb), __func__)
+
+static inline void rxrpc_purge_queue(struct sk_buff_head *list)
+{
+	struct sk_buff *skb;
+	while ((skb = skb_dequeue((list))) != NULL)
+		rxrpc_free_skb(skb);
+}
+
+static inline void __rxrpc_get_local(struct rxrpc_local *local, const char *f)
+{
+	CHECK_SLAB_OKAY(&local->usage);
+	if (atomic_inc_return(&local->usage) == 1)
+		printk("resurrected (%s)\n", f);
+}
+
+#define rxrpc_get_local(LOCAL) __rxrpc_get_local((LOCAL), __func__)
+
+#define rxrpc_get_call(CALL)				\
+do {							\
+	CHECK_SLAB_OKAY(&(CALL)->usage);		\
+	if (atomic_inc_return(&(CALL)->usage) == 1)	\
+		BUG();					\
+} while(0)
+
+#define rxrpc_put_call(CALL)				\
+do {							\
+	__rxrpc_put_call(CALL);				\
+} while(0)
diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c
new file mode 100644
index 0000000..7e049ff
--- /dev/null
+++ b/net/rxrpc/ar-key.c
@@ -0,0 +1,334 @@
+/* RxRPC key management
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ *
+ * RxRPC keys should have a description of describing their purpose:
+ *	"afs@CAMBRIDGE.REDHAT.COM>
+ */
+
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/key.h>
+#include <linux/crypto.h>
+#include <net/sock.h>
+#include <net/af_rxrpc.h>
+#include <keys/rxrpc-type.h>
+#include <keys/user-type.h>
+#include "ar-internal.h"
+
+static int rxrpc_instantiate(struct key *, const void *, size_t);
+static int rxrpc_instantiate_s(struct key *, const void *, size_t);
+static void rxrpc_destroy(struct key *);
+static void rxrpc_destroy_s(struct key *);
+static void rxrpc_describe(const struct key *, struct seq_file *);
+
+/*
+ * rxrpc defined keys take an arbitrary string as the description and an
+ * arbitrary blob of data as the payload
+ */
+struct key_type key_type_rxrpc = {
+	.name		= "rxrpc",
+	.instantiate	= rxrpc_instantiate,
+	.match		= user_match,
+	.destroy	= rxrpc_destroy,
+	.describe	= rxrpc_describe,
+};
+
+EXPORT_SYMBOL(key_type_rxrpc);
+
+/*
+ * rxrpc server defined keys take "<serviceId>:<securityIndex>" as the
+ * description and an 8-byte decryption key as the payload
+ */
+struct key_type key_type_rxrpc_s = {
+	.name		= "rxrpc_s",
+	.instantiate	= rxrpc_instantiate_s,
+	.match		= user_match,
+	.destroy	= rxrpc_destroy_s,
+	.describe	= rxrpc_describe,
+};
+
+/*
+ * instantiate an rxrpc defined key
+ * data should be of the form:
+ *	OFFSET	LEN	CONTENT
+ *	0	4	key interface version number
+ *	4	2	security index (type)
+ *	6	2	ticket length
+ *	8	4	key expiry time (time_t)
+ *	12	4	kvno
+ *	16	8	session key
+ *	24	[len]	ticket
+ *
+ * if no data is provided, then a no-security key is made
+ */
+static int rxrpc_instantiate(struct key *key, const void *data, size_t datalen)
+{
+	const struct rxkad_key *tsec;
+	struct rxrpc_key_payload *upayload;
+	size_t plen;
+	u32 kver;
+	int ret;
+
+	_enter("{%x},,%zu", key_serial(key), datalen);
+
+	/* handle a no-security key */
+	if (!data && datalen == 0)
+		return 0;
+
+	/* get the key interface version number */
+	ret = -EINVAL;
+	if (datalen <= 4 || !data)
+		goto error;
+	memcpy(&kver, data, sizeof(kver));
+	data += sizeof(kver);
+	datalen -= sizeof(kver);
+
+	_debug("KEY I/F VERSION: %u", kver);
+
+	ret = -EKEYREJECTED;
+	if (kver != 1)
+		goto error;
+
+	/* deal with a version 1 key */
+	ret = -EINVAL;
+	if (datalen < sizeof(*tsec))
+		goto error;
+
+	tsec = data;
+	if (datalen != sizeof(*tsec) + tsec->ticket_len)
+		goto error;
+
+	_debug("SCIX: %u", tsec->security_index);
+	_debug("TLEN: %u", tsec->ticket_len);
+	_debug("EXPY: %x", tsec->expiry);
+	_debug("KVNO: %u", tsec->kvno);
+	_debug("SKEY: %02x%02x%02x%02x%02x%02x%02x%02x",
+	       tsec->session_key[0], tsec->session_key[1],
+	       tsec->session_key[2], tsec->session_key[3],
+	       tsec->session_key[4], tsec->session_key[5],
+	       tsec->session_key[6], tsec->session_key[7]);
+	if (tsec->ticket_len >= 8)
+		_debug("TCKT: %02x%02x%02x%02x%02x%02x%02x%02x",
+		       tsec->ticket[0], tsec->ticket[1],
+		       tsec->ticket[2], tsec->ticket[3],
+		       tsec->ticket[4], tsec->ticket[5],
+		       tsec->ticket[6], tsec->ticket[7]);
+
+	ret = -EPROTONOSUPPORT;
+	if (tsec->security_index != 2)
+		goto error;
+
+	key->type_data.x[0] = tsec->security_index;
+
+	plen = sizeof(*upayload) + tsec->ticket_len;
+	ret = key_payload_reserve(key, plen);
+	if (ret < 0)
+		goto error;
+
+	ret = -ENOMEM;
+	upayload = kmalloc(plen, GFP_KERNEL);
+	if (!upayload)
+		goto error;
+
+	/* attach the data */
+	memcpy(&upayload->k, tsec, sizeof(*tsec));
+	memcpy(&upayload->k.ticket, (void *)tsec + sizeof(*tsec),
+	       tsec->ticket_len);
+	key->payload.data = upayload;
+	key->expiry = tsec->expiry;
+	ret = 0;
+
+error:
+	return ret;
+}
+
+/*
+ * instantiate a server secret key
+ * data should be a pointer to the 8-byte secret key
+ */
+static int rxrpc_instantiate_s(struct key *key, const void *data,
+			       size_t datalen)
+{
+	struct crypto_blkcipher *ci;
+
+	_enter("{%x},,%zu", key_serial(key), datalen);
+
+	if (datalen != 8)
+		return -EINVAL;
+
+	memcpy(&key->type_data, data, 8);
+
+	ci = crypto_alloc_blkcipher("pcbc(des)", 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(ci)) {
+		_leave(" = %ld", PTR_ERR(ci));
+		return PTR_ERR(ci);
+	}
+
+	if (crypto_blkcipher_setkey(ci, data, 8) < 0)
+		BUG();
+
+	key->payload.data = ci;
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * dispose of the data dangling from the corpse of a rxrpc key
+ */
+static void rxrpc_destroy(struct key *key)
+{
+	kfree(key->payload.data);
+}
+
+/*
+ * dispose of the data dangling from the corpse of a rxrpc key
+ */
+static void rxrpc_destroy_s(struct key *key)
+{
+	if (key->payload.data) {
+		crypto_free_blkcipher(key->payload.data);
+		key->payload.data = NULL;
+	}
+}
+
+/*
+ * describe the rxrpc key
+ */
+static void rxrpc_describe(const struct key *key, struct seq_file *m)
+{
+	seq_puts(m, key->description);
+}
+
+/*
+ * grab the security key for a socket
+ */
+int rxrpc_request_key(struct rxrpc_sock *rx, char __user *optval, int optlen)
+{
+	struct key *key;
+	char *description;
+
+	_enter("");
+
+	if (optlen <= 0 || optlen > PAGE_SIZE - 1)
+		return -EINVAL;
+
+	description = kmalloc(optlen + 1, GFP_KERNEL);
+	if (!description)
+		return -ENOMEM;
+
+	if (copy_from_user(description, optval, optlen)) {
+		kfree(description);
+		return -EFAULT;
+	}
+	description[optlen] = 0;
+
+	key = request_key(&key_type_rxrpc, description, NULL);
+	if (IS_ERR(key)) {
+		kfree(description);
+		_leave(" = %ld", PTR_ERR(key));
+		return PTR_ERR(key);
+	}
+
+	rx->key = key;
+	kfree(description);
+	_leave(" = 0 [key %x]", key->serial);
+	return 0;
+}
+
+/*
+ * grab the security keyring for a server socket
+ */
+int rxrpc_server_keyring(struct rxrpc_sock *rx, char __user *optval,
+			 int optlen)
+{
+	struct key *key;
+	char *description;
+
+	_enter("");
+
+	if (optlen <= 0 || optlen > PAGE_SIZE - 1)
+		return -EINVAL;
+
+	description = kmalloc(optlen + 1, GFP_KERNEL);
+	if (!description)
+		return -ENOMEM;
+
+	if (copy_from_user(description, optval, optlen)) {
+		kfree(description);
+		return -EFAULT;
+	}
+	description[optlen] = 0;
+
+	key = request_key(&key_type_keyring, description, NULL);
+	if (IS_ERR(key)) {
+		kfree(description);
+		_leave(" = %ld", PTR_ERR(key));
+		return PTR_ERR(key);
+	}
+
+	rx->securities = key;
+	kfree(description);
+	_leave(" = 0 [key %x]", key->serial);
+	return 0;
+}
+
+/*
+ * generate a server data key
+ */
+int rxrpc_get_server_data_key(struct rxrpc_connection *conn,
+			      const void *session_key,
+			      time_t expiry,
+			      u32 kvno)
+{
+	struct key *key;
+	int ret;
+
+	struct {
+		u32 kver;
+		struct rxkad_key tsec;
+	} data;
+
+	_enter("");
+
+	key = key_alloc(&key_type_rxrpc, "x", 0, 0, current, 0,
+			KEY_ALLOC_NOT_IN_QUOTA);
+	if (IS_ERR(key)) {
+		_leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key));
+		return -ENOMEM;
+	}
+
+	_debug("key %d", key_serial(key));
+
+	data.kver = 1;
+	data.tsec.security_index = 2;
+	data.tsec.ticket_len = 0;
+	data.tsec.expiry = expiry;
+	data.tsec.kvno = 0;
+
+	memcpy(&data.tsec.session_key, session_key,
+	       sizeof(data.tsec.session_key));
+
+	ret = key_instantiate_and_link(key, &data, sizeof(data), NULL, NULL);
+	if (ret < 0)
+		goto error;
+
+	conn->key = key;
+	_leave(" = 0 [%d]", key_serial(key));
+	return 0;
+
+error:
+	key_revoke(key);
+	key_put(key);
+	_leave(" = -ENOMEM [ins %d]", ret);
+	return -ENOMEM;
+}
+
+EXPORT_SYMBOL(rxrpc_get_server_data_key);
diff --git a/net/rxrpc/ar-local.c b/net/rxrpc/ar-local.c
new file mode 100644
index 0000000..fe03f71
--- /dev/null
+++ b/net/rxrpc/ar-local.c
@@ -0,0 +1,309 @@
+/* AF_RXRPC local endpoint management
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/af_rxrpc.h>
+#include "ar-internal.h"
+
+static LIST_HEAD(rxrpc_locals);
+DEFINE_RWLOCK(rxrpc_local_lock);
+static DECLARE_RWSEM(rxrpc_local_sem);
+static DECLARE_WAIT_QUEUE_HEAD(rxrpc_local_wq);
+
+static void rxrpc_destroy_local(struct work_struct *work);
+
+/*
+ * allocate a new local
+ */
+static
+struct rxrpc_local *rxrpc_alloc_local(struct sockaddr_rxrpc *srx)
+{
+	struct rxrpc_local *local;
+
+	local = kzalloc(sizeof(struct rxrpc_local), GFP_KERNEL);
+	if (local) {
+		INIT_WORK(&local->destroyer, &rxrpc_destroy_local);
+		INIT_WORK(&local->acceptor, &rxrpc_accept_incoming_calls);
+		INIT_WORK(&local->rejecter, &rxrpc_reject_packets);
+		INIT_LIST_HEAD(&local->services);
+		INIT_LIST_HEAD(&local->link);
+		init_rwsem(&local->defrag_sem);
+		skb_queue_head_init(&local->accept_queue);
+		skb_queue_head_init(&local->reject_queue);
+		spin_lock_init(&local->lock);
+		rwlock_init(&local->services_lock);
+		atomic_set(&local->usage, 1);
+		local->debug_id = atomic_inc_return(&rxrpc_debug_id);
+		memcpy(&local->srx, srx, sizeof(*srx));
+	}
+
+	_leave(" = %p", local);
+	return local;
+}
+
+/*
+ * create the local socket
+ * - must be called with rxrpc_local_sem writelocked
+ */
+static int rxrpc_create_local(struct rxrpc_local *local)
+{
+	struct sock *sock;
+	int ret, opt;
+
+	_enter("%p{%d}", local, local->srx.transport_type);
+
+	/* create a socket to represent the local endpoint */
+	ret = sock_create_kern(PF_INET, local->srx.transport_type, IPPROTO_UDP,
+			       &local->socket);
+	if (ret < 0) {
+		_leave(" = %d [socket]", ret);
+		return ret;
+	}
+
+	/* if a local address was supplied then bind it */
+	if (local->srx.transport_len > sizeof(sa_family_t)) {
+		_debug("bind");
+		ret = kernel_bind(local->socket,
+				  (struct sockaddr *) &local->srx.transport,
+				  local->srx.transport_len);
+		if (ret < 0) {
+			_debug("bind failed");
+			goto error;
+		}
+	}
+
+	/* we want to receive ICMP errors */
+	opt = 1;
+	ret = kernel_setsockopt(local->socket, SOL_IP, IP_RECVERR,
+				(char *) &opt, sizeof(opt));
+	if (ret < 0) {
+		_debug("setsockopt failed");
+		goto error;
+	}
+
+	/* we want to set the don't fragment bit */
+	opt = IP_PMTUDISC_DO;
+	ret = kernel_setsockopt(local->socket, SOL_IP, IP_MTU_DISCOVER,
+				(char *) &opt, sizeof(opt));
+	if (ret < 0) {
+		_debug("setsockopt failed");
+		goto error;
+	}
+
+	write_lock_bh(&rxrpc_local_lock);
+	list_add(&local->link, &rxrpc_locals);
+	write_unlock_bh(&rxrpc_local_lock);
+
+	/* set the socket up */
+	sock = local->socket->sk;
+	sock->sk_user_data	= local;
+	sock->sk_data_ready	= rxrpc_data_ready;
+	sock->sk_error_report	= rxrpc_UDP_error_report;
+	_leave(" = 0");
+	return 0;
+
+error:
+	local->socket->ops->shutdown(local->socket, 2);
+	local->socket->sk->sk_user_data = NULL;
+	sock_release(local->socket);
+	local->socket = NULL;
+
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * create a new local endpoint using the specified UDP address
+ */
+struct rxrpc_local *rxrpc_lookup_local(struct sockaddr_rxrpc *srx)
+{
+	struct rxrpc_local *local;
+	int ret;
+
+	_enter("{%d,%u,%u.%u.%u.%u+%hu}",
+	       srx->transport_type,
+	       srx->transport.family,
+	       NIPQUAD(srx->transport.sin.sin_addr),
+	       ntohs(srx->transport.sin.sin_port));
+
+	down_write(&rxrpc_local_sem);
+
+	/* see if we have a suitable local local endpoint already */
+	read_lock_bh(&rxrpc_local_lock);
+
+	list_for_each_entry(local, &rxrpc_locals, link) {
+		_debug("CMP {%d,%u,%u.%u.%u.%u+%hu}",
+		       local->srx.transport_type,
+		       local->srx.transport.family,
+		       NIPQUAD(local->srx.transport.sin.sin_addr),
+		       ntohs(local->srx.transport.sin.sin_port));
+
+		if (local->srx.transport_type != srx->transport_type ||
+		    local->srx.transport.family != srx->transport.family)
+			continue;
+
+		switch (srx->transport.family) {
+		case AF_INET:
+			if (local->srx.transport.sin.sin_port !=
+			    srx->transport.sin.sin_port)
+				continue;
+			if (memcmp(&local->srx.transport.sin.sin_addr,
+				   &srx->transport.sin.sin_addr,
+				   sizeof(struct in_addr)) != 0)
+				continue;
+			goto found_local;
+
+		default:
+			BUG();
+		}
+	}
+
+	read_unlock_bh(&rxrpc_local_lock);
+
+	/* we didn't find one, so we need to create one */
+	local = rxrpc_alloc_local(srx);
+	if (!local) {
+		up_write(&rxrpc_local_sem);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	ret = rxrpc_create_local(local);
+	if (ret < 0) {
+		up_write(&rxrpc_local_sem);
+		kfree(local);
+		_leave(" = %d", ret);
+		return ERR_PTR(ret);
+	}
+
+	up_write(&rxrpc_local_sem);
+
+	_net("LOCAL new %d {%d,%u,%u.%u.%u.%u+%hu}",
+	     local->debug_id,
+	     local->srx.transport_type,
+	     local->srx.transport.family,
+	     NIPQUAD(local->srx.transport.sin.sin_addr),
+	     ntohs(local->srx.transport.sin.sin_port));
+
+	_leave(" = %p [new]", local);
+	return local;
+
+found_local:
+	rxrpc_get_local(local);
+	read_unlock_bh(&rxrpc_local_lock);
+	up_write(&rxrpc_local_sem);
+
+	_net("LOCAL old %d {%d,%u,%u.%u.%u.%u+%hu}",
+	     local->debug_id,
+	     local->srx.transport_type,
+	     local->srx.transport.family,
+	     NIPQUAD(local->srx.transport.sin.sin_addr),
+	     ntohs(local->srx.transport.sin.sin_port));
+
+	_leave(" = %p [reuse]", local);
+	return local;
+}
+
+/*
+ * release a local endpoint
+ */
+void rxrpc_put_local(struct rxrpc_local *local)
+{
+	_enter("%p{u=%d}", local, atomic_read(&local->usage));
+
+	ASSERTCMP(atomic_read(&local->usage), >, 0);
+
+	/* to prevent a race, the decrement and the dequeue must be effectively
+	 * atomic */
+	write_lock_bh(&rxrpc_local_lock);
+	if (unlikely(atomic_dec_and_test(&local->usage))) {
+		_debug("destroy local");
+		rxrpc_queue_work(&local->destroyer);
+	}
+	write_unlock_bh(&rxrpc_local_lock);
+	_leave("");
+}
+
+/*
+ * destroy a local endpoint
+ */
+static void rxrpc_destroy_local(struct work_struct *work)
+{
+	struct rxrpc_local *local =
+		container_of(work, struct rxrpc_local, destroyer);
+
+	_enter("%p{%d}", local, atomic_read(&local->usage));
+
+	down_write(&rxrpc_local_sem);
+
+	write_lock_bh(&rxrpc_local_lock);
+	if (atomic_read(&local->usage) > 0) {
+		write_unlock_bh(&rxrpc_local_lock);
+		up_read(&rxrpc_local_sem);
+		_leave(" [resurrected]");
+		return;
+	}
+
+	list_del(&local->link);
+	local->socket->sk->sk_user_data = NULL;
+	write_unlock_bh(&rxrpc_local_lock);
+
+	downgrade_write(&rxrpc_local_sem);
+
+	ASSERT(list_empty(&local->services));
+	ASSERT(!work_pending(&local->acceptor));
+	ASSERT(!work_pending(&local->rejecter));
+
+	/* finish cleaning up the local descriptor */
+	rxrpc_purge_queue(&local->accept_queue);
+	rxrpc_purge_queue(&local->reject_queue);
+	local->socket->ops->shutdown(local->socket, 2);
+	sock_release(local->socket);
+
+	up_read(&rxrpc_local_sem);
+
+	_net("DESTROY LOCAL %d", local->debug_id);
+	kfree(local);
+
+	if (list_empty(&rxrpc_locals))
+		wake_up_all(&rxrpc_local_wq);
+
+	_leave("");
+}
+
+/*
+ * preemptively destroy all local local endpoint rather than waiting for
+ * them to be destroyed
+ */
+void __exit rxrpc_destroy_all_locals(void)
+{
+	DECLARE_WAITQUEUE(myself,current);
+
+	_enter("");
+
+	/* we simply have to wait for them to go away */
+	if (!list_empty(&rxrpc_locals)) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		add_wait_queue(&rxrpc_local_wq, &myself);
+
+		while (!list_empty(&rxrpc_locals)) {
+			schedule();
+			set_current_state(TASK_UNINTERRUPTIBLE);
+		}
+
+		remove_wait_queue(&rxrpc_local_wq, &myself);
+		set_current_state(TASK_RUNNING);
+	}
+
+	_leave("");
+}
diff --git a/net/rxrpc/ar-output.c b/net/rxrpc/ar-output.c
new file mode 100644
index 0000000..5cdde4a
--- /dev/null
+++ b/net/rxrpc/ar-output.c
@@ -0,0 +1,734 @@
+/* RxRPC packet transmission
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/circ_buf.h>
+#include <net/sock.h>
+#include <net/af_rxrpc.h>
+#include "ar-internal.h"
+
+int rxrpc_resend_timeout = 4;
+
+static int rxrpc_send_data(struct kiocb *iocb,
+			   struct rxrpc_sock *rx,
+			   struct rxrpc_call *call,
+			   struct msghdr *msg, size_t len);
+
+/*
+ * extract control messages from the sendmsg() control buffer
+ */
+static int rxrpc_sendmsg_cmsg(struct rxrpc_sock *rx, struct msghdr *msg,
+			      unsigned long *user_call_ID,
+			      enum rxrpc_command *command,
+			      u32 *abort_code,
+			      bool server)
+{
+	struct cmsghdr *cmsg;
+	int len;
+
+	*command = RXRPC_CMD_SEND_DATA;
+
+	if (msg->msg_controllen == 0)
+		return -EINVAL;
+
+	for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+		if (!CMSG_OK(msg, cmsg))
+			return -EINVAL;
+
+		len = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr));
+		_debug("CMSG %d, %d, %d",
+		       cmsg->cmsg_level, cmsg->cmsg_type, len);
+
+		if (cmsg->cmsg_level != SOL_RXRPC)
+			continue;
+
+		switch (cmsg->cmsg_type) {
+		case RXRPC_USER_CALL_ID:
+			if (msg->msg_flags & MSG_CMSG_COMPAT) {
+				if (len != sizeof(u32))
+					return -EINVAL;
+				*user_call_ID = *(u32 *) CMSG_DATA(cmsg);
+			} else {
+				if (len != sizeof(unsigned long))
+					return -EINVAL;
+				*user_call_ID = *(unsigned long *)
+					CMSG_DATA(cmsg);
+			}
+			_debug("User Call ID %lx", *user_call_ID);
+			break;
+
+		case RXRPC_ABORT:
+			if (*command != RXRPC_CMD_SEND_DATA)
+				return -EINVAL;
+			*command = RXRPC_CMD_SEND_ABORT;
+			if (len != sizeof(*abort_code))
+				return -EINVAL;
+			*abort_code = *(unsigned int *) CMSG_DATA(cmsg);
+			_debug("Abort %x", *abort_code);
+			if (*abort_code == 0)
+				return -EINVAL;
+			break;
+
+		case RXRPC_ACCEPT:
+			if (*command != RXRPC_CMD_SEND_DATA)
+				return -EINVAL;
+			*command = RXRPC_CMD_ACCEPT;
+			if (len != 0)
+				return -EINVAL;
+			if (!server)
+				return -EISCONN;
+			break;
+
+		default:
+			return -EINVAL;
+		}
+	}
+
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * abort a call, sending an ABORT packet to the peer
+ */
+static void rxrpc_send_abort(struct rxrpc_call *call, u32 abort_code)
+{
+	write_lock_bh(&call->state_lock);
+
+	if (call->state <= RXRPC_CALL_COMPLETE) {
+		call->state = RXRPC_CALL_LOCALLY_ABORTED;
+		call->abort_code = abort_code;
+		set_bit(RXRPC_CALL_ABORT, &call->events);
+		del_timer_sync(&call->resend_timer);
+		del_timer_sync(&call->ack_timer);
+		clear_bit(RXRPC_CALL_RESEND_TIMER, &call->events);
+		clear_bit(RXRPC_CALL_ACK, &call->events);
+		clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags);
+		rxrpc_queue_call(call);
+	}
+
+	write_unlock_bh(&call->state_lock);
+}
+
+/*
+ * send a message forming part of a client call through an RxRPC socket
+ * - caller holds the socket locked
+ * - the socket may be either a client socket or a server socket
+ */
+int rxrpc_client_sendmsg(struct kiocb *iocb, struct rxrpc_sock *rx,
+			 struct rxrpc_transport *trans, struct msghdr *msg,
+			 size_t len)
+{
+	struct rxrpc_conn_bundle *bundle;
+	enum rxrpc_command cmd;
+	struct rxrpc_call *call;
+	unsigned long user_call_ID = 0;
+	struct key *key;
+	__be16 service_id;
+	u32 abort_code = 0;
+	int ret;
+
+	_enter("");
+
+	ASSERT(trans != NULL);
+
+	ret = rxrpc_sendmsg_cmsg(rx, msg, &user_call_ID, &cmd, &abort_code,
+				 false);
+	if (ret < 0)
+		return ret;
+
+	bundle = NULL;
+	if (trans) {
+		service_id = rx->service_id;
+		if (msg->msg_name) {
+			struct sockaddr_rxrpc *srx =
+				(struct sockaddr_rxrpc *) msg->msg_name;
+			service_id = htons(srx->srx_service);
+		}
+		key = rx->key;
+		if (key && !rx->key->payload.data)
+			key = NULL;
+		bundle = rxrpc_get_bundle(rx, trans, key, service_id,
+					  GFP_KERNEL);
+		if (IS_ERR(bundle))
+			return PTR_ERR(bundle);
+	}
+
+	call = rxrpc_get_client_call(rx, trans, bundle, user_call_ID,
+				     abort_code == 0, GFP_KERNEL);
+	if (trans)
+		rxrpc_put_bundle(trans, bundle);
+	if (IS_ERR(call)) {
+		_leave(" = %ld", PTR_ERR(call));
+		return PTR_ERR(call);
+	}
+
+	_debug("CALL %d USR %lx ST %d on CONN %p",
+	       call->debug_id, call->user_call_ID, call->state, call->conn);
+
+	if (call->state >= RXRPC_CALL_COMPLETE) {
+		/* it's too late for this call */
+		ret = -ESHUTDOWN;
+	} else if (cmd == RXRPC_CMD_SEND_ABORT) {
+		rxrpc_send_abort(call, abort_code);
+	} else if (cmd != RXRPC_CMD_SEND_DATA) {
+		ret = -EINVAL;
+	} else if (call->state != RXRPC_CALL_CLIENT_SEND_REQUEST) {
+		/* request phase complete for this client call */
+		ret = -EPROTO;
+	} else {
+		ret = rxrpc_send_data(iocb, rx, call, msg, len);
+	}
+
+	rxrpc_put_call(call);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/**
+ * rxrpc_kernel_send_data - Allow a kernel service to send data on a call
+ * @call: The call to send data through
+ * @msg: The data to send
+ * @len: The amount of data to send
+ *
+ * Allow a kernel service to send data on a call.  The call must be in an state
+ * appropriate to sending data.  No control data should be supplied in @msg,
+ * nor should an address be supplied.  MSG_MORE should be flagged if there's
+ * more data to come, otherwise this data will end the transmission phase.
+ */
+int rxrpc_kernel_send_data(struct rxrpc_call *call, struct msghdr *msg,
+			   size_t len)
+{
+	int ret;
+
+	_enter("{%d,%s},", call->debug_id, rxrpc_call_states[call->state]);
+
+	ASSERTCMP(msg->msg_name, ==, NULL);
+	ASSERTCMP(msg->msg_control, ==, NULL);
+
+	lock_sock(&call->socket->sk);
+
+	_debug("CALL %d USR %lx ST %d on CONN %p",
+	       call->debug_id, call->user_call_ID, call->state, call->conn);
+
+	if (call->state >= RXRPC_CALL_COMPLETE) {
+		ret = -ESHUTDOWN; /* it's too late for this call */
+	} else if (call->state != RXRPC_CALL_CLIENT_SEND_REQUEST &&
+		   call->state != RXRPC_CALL_SERVER_ACK_REQUEST &&
+		   call->state != RXRPC_CALL_SERVER_SEND_REPLY) {
+		ret = -EPROTO; /* request phase complete for this client call */
+	} else {
+		mm_segment_t oldfs = get_fs();
+		set_fs(KERNEL_DS);
+		ret = rxrpc_send_data(NULL, call->socket, call, msg, len);
+		set_fs(oldfs);
+	}
+
+	release_sock(&call->socket->sk);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+EXPORT_SYMBOL(rxrpc_kernel_send_data);
+
+/*
+ * rxrpc_kernel_abort_call - Allow a kernel service to abort a call
+ * @call: The call to be aborted
+ * @abort_code: The abort code to stick into the ABORT packet
+ *
+ * Allow a kernel service to abort a call, if it's still in an abortable state.
+ */
+void rxrpc_kernel_abort_call(struct rxrpc_call *call, u32 abort_code)
+{
+	_enter("{%d},%d", call->debug_id, abort_code);
+
+	lock_sock(&call->socket->sk);
+
+	_debug("CALL %d USR %lx ST %d on CONN %p",
+	       call->debug_id, call->user_call_ID, call->state, call->conn);
+
+	if (call->state < RXRPC_CALL_COMPLETE)
+		rxrpc_send_abort(call, abort_code);
+
+	release_sock(&call->socket->sk);
+	_leave("");
+}
+
+EXPORT_SYMBOL(rxrpc_kernel_abort_call);
+
+/*
+ * send a message through a server socket
+ * - caller holds the socket locked
+ */
+int rxrpc_server_sendmsg(struct kiocb *iocb, struct rxrpc_sock *rx,
+			 struct msghdr *msg, size_t len)
+{
+	enum rxrpc_command cmd;
+	struct rxrpc_call *call;
+	unsigned long user_call_ID = 0;
+	u32 abort_code = 0;
+	int ret;
+
+	_enter("");
+
+	ret = rxrpc_sendmsg_cmsg(rx, msg, &user_call_ID, &cmd, &abort_code,
+				 true);
+	if (ret < 0)
+		return ret;
+
+	if (cmd == RXRPC_CMD_ACCEPT) {
+		call = rxrpc_accept_call(rx, user_call_ID);
+		if (IS_ERR(call))
+			return PTR_ERR(call);
+		rxrpc_put_call(call);
+		return 0;
+	}
+
+	call = rxrpc_find_server_call(rx, user_call_ID);
+	if (!call)
+		return -EBADSLT;
+	if (call->state >= RXRPC_CALL_COMPLETE) {
+		ret = -ESHUTDOWN;
+		goto out;
+	}
+
+	switch (cmd) {
+	case RXRPC_CMD_SEND_DATA:
+		if (call->state != RXRPC_CALL_CLIENT_SEND_REQUEST &&
+		    call->state != RXRPC_CALL_SERVER_ACK_REQUEST &&
+		    call->state != RXRPC_CALL_SERVER_SEND_REPLY) {
+			/* Tx phase not yet begun for this call */
+			ret = -EPROTO;
+			break;
+		}
+
+		ret = rxrpc_send_data(iocb, rx, call, msg, len);
+		break;
+
+	case RXRPC_CMD_SEND_ABORT:
+		rxrpc_send_abort(call, abort_code);
+		break;
+	default:
+		BUG();
+	}
+
+	out:
+	rxrpc_put_call(call);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * send a packet through the transport endpoint
+ */
+int rxrpc_send_packet(struct rxrpc_transport *trans, struct sk_buff *skb)
+{
+	struct kvec iov[1];
+	struct msghdr msg;
+	int ret, opt;
+
+	_enter(",{%d}", skb->len);
+
+	iov[0].iov_base = skb->head;
+	iov[0].iov_len = skb->len;
+
+	msg.msg_name = &trans->peer->srx.transport.sin;
+	msg.msg_namelen = sizeof(trans->peer->srx.transport.sin);
+	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags = 0;
+
+	/* send the packet with the don't fragment bit set if we currently
+	 * think it's small enough */
+	if (skb->len - sizeof(struct rxrpc_header) < trans->peer->maxdata) {
+		down_read(&trans->local->defrag_sem);
+		/* send the packet by UDP
+		 * - returns -EMSGSIZE if UDP would have to fragment the packet
+		 *   to go out of the interface
+		 *   - in which case, we'll have processed the ICMP error
+		 *     message and update the peer record
+		 */
+		ret = kernel_sendmsg(trans->local->socket, &msg, iov, 1,
+				     iov[0].iov_len);
+
+		up_read(&trans->local->defrag_sem);
+		if (ret == -EMSGSIZE)
+			goto send_fragmentable;
+
+		_leave(" = %d [%u]", ret, trans->peer->maxdata);
+		return ret;
+	}
+
+send_fragmentable:
+	/* attempt to send this message with fragmentation enabled */
+	_debug("send fragment");
+
+	down_write(&trans->local->defrag_sem);
+	opt = IP_PMTUDISC_DONT;
+	ret = kernel_setsockopt(trans->local->socket, SOL_IP, IP_MTU_DISCOVER,
+				(char *) &opt, sizeof(opt));
+	if (ret == 0) {
+		ret = kernel_sendmsg(trans->local->socket, &msg, iov, 1,
+				     iov[0].iov_len);
+
+		opt = IP_PMTUDISC_DO;
+		kernel_setsockopt(trans->local->socket, SOL_IP,
+				  IP_MTU_DISCOVER, (char *) &opt, sizeof(opt));
+	}
+
+	up_write(&trans->local->defrag_sem);
+	_leave(" = %d [frag %u]", ret, trans->peer->maxdata);
+	return ret;
+}
+
+/*
+ * wait for space to appear in the transmit/ACK window
+ * - caller holds the socket locked
+ */
+static int rxrpc_wait_for_tx_window(struct rxrpc_sock *rx,
+				    struct rxrpc_call *call,
+				    long *timeo)
+{
+	DECLARE_WAITQUEUE(myself, current);
+	int ret;
+
+	_enter(",{%d},%ld",
+	       CIRC_SPACE(call->acks_head, call->acks_tail, call->acks_winsz),
+	       *timeo);
+
+	add_wait_queue(&call->tx_waitq, &myself);
+
+	for (;;) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		ret = 0;
+		if (CIRC_SPACE(call->acks_head, call->acks_tail,
+			       call->acks_winsz) > 0)
+			break;
+		if (signal_pending(current)) {
+			ret = sock_intr_errno(*timeo);
+			break;
+		}
+
+		release_sock(&rx->sk);
+		*timeo = schedule_timeout(*timeo);
+		lock_sock(&rx->sk);
+	}
+
+	remove_wait_queue(&call->tx_waitq, &myself);
+	set_current_state(TASK_RUNNING);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * attempt to schedule an instant Tx resend
+ */
+static inline void rxrpc_instant_resend(struct rxrpc_call *call)
+{
+	read_lock_bh(&call->state_lock);
+	if (try_to_del_timer_sync(&call->resend_timer) >= 0) {
+		clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags);
+		if (call->state < RXRPC_CALL_COMPLETE &&
+		    !test_and_set_bit(RXRPC_CALL_RESEND_TIMER, &call->events))
+			rxrpc_queue_call(call);
+	}
+	read_unlock_bh(&call->state_lock);
+}
+
+/*
+ * queue a packet for transmission, set the resend timer and attempt
+ * to send the packet immediately
+ */
+static void rxrpc_queue_packet(struct rxrpc_call *call, struct sk_buff *skb,
+			       bool last)
+{
+	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+	int ret;
+
+	_net("queue skb %p [%d]", skb, call->acks_head);
+
+	ASSERT(call->acks_window != NULL);
+	call->acks_window[call->acks_head] = (unsigned long) skb;
+	smp_wmb();
+	call->acks_head = (call->acks_head + 1) & (call->acks_winsz - 1);
+
+	if (last || call->state == RXRPC_CALL_SERVER_ACK_REQUEST) {
+		_debug("________awaiting reply/ACK__________");
+		write_lock_bh(&call->state_lock);
+		switch (call->state) {
+		case RXRPC_CALL_CLIENT_SEND_REQUEST:
+			call->state = RXRPC_CALL_CLIENT_AWAIT_REPLY;
+			break;
+		case RXRPC_CALL_SERVER_ACK_REQUEST:
+			call->state = RXRPC_CALL_SERVER_SEND_REPLY;
+			if (!last)
+				break;
+		case RXRPC_CALL_SERVER_SEND_REPLY:
+			call->state = RXRPC_CALL_SERVER_AWAIT_ACK;
+			break;
+		default:
+			break;
+		}
+		write_unlock_bh(&call->state_lock);
+	}
+
+	_proto("Tx DATA %%%u { #%u }",
+	       ntohl(sp->hdr.serial), ntohl(sp->hdr.seq));
+
+	sp->need_resend = 0;
+	sp->resend_at = jiffies + rxrpc_resend_timeout * HZ;
+	if (!test_and_set_bit(RXRPC_CALL_RUN_RTIMER, &call->flags)) {
+		_debug("run timer");
+		call->resend_timer.expires = sp->resend_at;
+		add_timer(&call->resend_timer);
+	}
+
+	/* attempt to cancel the rx-ACK timer, deferring reply transmission if
+	 * we're ACK'ing the request phase of an incoming call */
+	ret = -EAGAIN;
+	if (try_to_del_timer_sync(&call->ack_timer) >= 0) {
+		/* the packet may be freed by rxrpc_process_call() before this
+		 * returns */
+		ret = rxrpc_send_packet(call->conn->trans, skb);
+		_net("sent skb %p", skb);
+	} else {
+		_debug("failed to delete ACK timer");
+	}
+
+	if (ret < 0) {
+		_debug("need instant resend %d", ret);
+		sp->need_resend = 1;
+		rxrpc_instant_resend(call);
+	}
+
+	_leave("");
+}
+
+/*
+ * send data through a socket
+ * - must be called in process context
+ * - caller holds the socket locked
+ */
+static int rxrpc_send_data(struct kiocb *iocb,
+			   struct rxrpc_sock *rx,
+			   struct rxrpc_call *call,
+			   struct msghdr *msg, size_t len)
+{
+	struct rxrpc_skb_priv *sp;
+	unsigned char __user *from;
+	struct sk_buff *skb;
+	struct iovec *iov;
+	struct sock *sk = &rx->sk;
+	long timeo;
+	bool more;
+	int ret, ioc, segment, copied;
+
+	_enter(",,,{%zu},%zu", msg->msg_iovlen, len);
+
+	timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
+
+	/* this should be in poll */
+	clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+
+	if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
+		return -EPIPE;
+
+	iov = msg->msg_iov;
+	ioc = msg->msg_iovlen - 1;
+	from = iov->iov_base;
+	segment = iov->iov_len;
+	iov++;
+	more = msg->msg_flags & MSG_MORE;
+
+	skb = call->tx_pending;
+	call->tx_pending = NULL;
+
+	copied = 0;
+	do {
+		int copy;
+
+		if (segment > len)
+			segment = len;
+
+		_debug("SEGMENT %d @%p", segment, from);
+
+		if (!skb) {
+			size_t size, chunk, max, space;
+
+			_debug("alloc");
+
+			if (CIRC_SPACE(call->acks_head, call->acks_tail,
+				       call->acks_winsz) <= 0) {
+				ret = -EAGAIN;
+				if (msg->msg_flags & MSG_DONTWAIT)
+					goto maybe_error;
+				ret = rxrpc_wait_for_tx_window(rx, call,
+							       &timeo);
+				if (ret < 0)
+					goto maybe_error;
+			}
+
+			max = call->conn->trans->peer->maxdata;
+			max -= call->conn->security_size;
+			max &= ~(call->conn->size_align - 1UL);
+
+			chunk = max;
+			if (chunk > len)
+				chunk = len;
+
+			space = chunk + call->conn->size_align;
+			space &= ~(call->conn->size_align - 1UL);
+
+			size = space + call->conn->header_size;
+
+			_debug("SIZE: %zu/%zu/%zu", chunk, space, size);
+
+			/* create a buffer that we can retain until it's ACK'd */
+			skb = sock_alloc_send_skb(
+				sk, size, msg->msg_flags & MSG_DONTWAIT, &ret);
+			if (!skb)
+				goto maybe_error;
+
+			rxrpc_new_skb(skb);
+
+			_debug("ALLOC SEND %p", skb);
+
+			ASSERTCMP(skb->mark, ==, 0);
+
+			_debug("HS: %u", call->conn->header_size);
+			skb_reserve(skb, call->conn->header_size);
+			skb->len += call->conn->header_size;
+
+			sp = rxrpc_skb(skb);
+			sp->remain = chunk;
+			if (sp->remain > skb_tailroom(skb))
+				sp->remain = skb_tailroom(skb);
+
+			_net("skb: hr %d, tr %d, hl %d, rm %d",
+			       skb_headroom(skb),
+			       skb_tailroom(skb),
+			       skb_headlen(skb),
+			       sp->remain);
+
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+		}
+
+		_debug("append");
+		sp = rxrpc_skb(skb);
+
+		/* append next segment of data to the current buffer */
+		copy = skb_tailroom(skb);
+		ASSERTCMP(copy, >, 0);
+		if (copy > segment)
+			copy = segment;
+		if (copy > sp->remain)
+			copy = sp->remain;
+
+		_debug("add");
+		ret = skb_add_data(skb, from, copy);
+		_debug("added");
+		if (ret < 0)
+			goto efault;
+		sp->remain -= copy;
+		skb->mark += copy;
+
+		len -= copy;
+		segment -= copy;
+		from += copy;
+		while (segment == 0 && ioc > 0) {
+			from = iov->iov_base;
+			segment = iov->iov_len;
+			iov++;
+			ioc--;
+		}
+		if (len == 0) {
+			segment = 0;
+			ioc = 0;
+		}
+
+		/* check for the far side aborting the call or a network error
+		 * occurring */
+		if (call->state > RXRPC_CALL_COMPLETE)
+			goto call_aborted;
+
+		/* add the packet to the send queue if it's now full */
+		if (sp->remain <= 0 || (segment == 0 && !more)) {
+			struct rxrpc_connection *conn = call->conn;
+			size_t pad;
+
+			/* pad out if we're using security */
+			if (conn->security) {
+				pad = conn->security_size + skb->mark;
+				pad = conn->size_align - pad;
+				pad &= conn->size_align - 1;
+				_debug("pad %zu", pad);
+				if (pad)
+					memset(skb_put(skb, pad), 0, pad);
+			}
+
+			sp->hdr.epoch = conn->epoch;
+			sp->hdr.cid = call->cid;
+			sp->hdr.callNumber = call->call_id;
+			sp->hdr.seq =
+				htonl(atomic_inc_return(&call->sequence));
+			sp->hdr.serial =
+				htonl(atomic_inc_return(&conn->serial));
+			sp->hdr.type = RXRPC_PACKET_TYPE_DATA;
+			sp->hdr.userStatus = 0;
+			sp->hdr.securityIndex = conn->security_ix;
+			sp->hdr._rsvd = 0;
+			sp->hdr.serviceId = conn->service_id;
+
+			sp->hdr.flags = conn->out_clientflag;
+			if (len == 0 && !more)
+				sp->hdr.flags |= RXRPC_LAST_PACKET;
+			else if (CIRC_SPACE(call->acks_head, call->acks_tail,
+					    call->acks_winsz) > 1)
+				sp->hdr.flags |= RXRPC_MORE_PACKETS;
+
+			ret = rxrpc_secure_packet(
+				call, skb, skb->mark,
+				skb->head + sizeof(struct rxrpc_header));
+			if (ret < 0)
+				goto out;
+
+			memcpy(skb->head, &sp->hdr,
+			       sizeof(struct rxrpc_header));
+			rxrpc_queue_packet(call, skb, segment == 0 && !more);
+			skb = NULL;
+		}
+
+	} while (segment > 0);
+
+out:
+	call->tx_pending = skb;
+	_leave(" = %d", ret);
+	return ret;
+
+call_aborted:
+	rxrpc_free_skb(skb);
+	if (call->state == RXRPC_CALL_NETWORK_ERROR)
+		ret = call->conn->trans->peer->net_error;
+	else
+		ret = -ECONNABORTED;
+	_leave(" = %d", ret);
+	return ret;
+
+maybe_error:
+	if (copied)
+		ret = copied;
+	goto out;
+
+efault:
+	ret = -EFAULT;
+	goto out;
+}
diff --git a/net/rxrpc/ar-peer.c b/net/rxrpc/ar-peer.c
new file mode 100644
index 0000000..d399de4
--- /dev/null
+++ b/net/rxrpc/ar-peer.c
@@ -0,0 +1,273 @@
+/* RxRPC remote transport endpoint management
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/udp.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/icmp.h>
+#include <net/sock.h>
+#include <net/af_rxrpc.h>
+#include <net/ip.h>
+#include "ar-internal.h"
+
+static LIST_HEAD(rxrpc_peers);
+static DEFINE_RWLOCK(rxrpc_peer_lock);
+static DECLARE_WAIT_QUEUE_HEAD(rxrpc_peer_wq);
+
+static void rxrpc_destroy_peer(struct work_struct *work);
+
+/*
+ * allocate a new peer
+ */
+static struct rxrpc_peer *rxrpc_alloc_peer(struct sockaddr_rxrpc *srx,
+					   gfp_t gfp)
+{
+	struct rxrpc_peer *peer;
+
+	_enter("");
+
+	peer = kzalloc(sizeof(struct rxrpc_peer), gfp);
+	if (peer) {
+		INIT_WORK(&peer->destroyer, &rxrpc_destroy_peer);
+		INIT_LIST_HEAD(&peer->link);
+		INIT_LIST_HEAD(&peer->error_targets);
+		spin_lock_init(&peer->lock);
+		atomic_set(&peer->usage, 1);
+		peer->debug_id = atomic_inc_return(&rxrpc_debug_id);
+		memcpy(&peer->srx, srx, sizeof(*srx));
+
+		peer->mtu = peer->if_mtu = 65535;
+
+		if (srx->transport.family == AF_INET) {
+			peer->hdrsize = sizeof(struct iphdr);
+			switch (srx->transport_type) {
+			case SOCK_DGRAM:
+				peer->hdrsize += sizeof(struct udphdr);
+				break;
+			default:
+				BUG();
+				break;
+			}
+		} else {
+			BUG();
+		}
+
+		peer->hdrsize += sizeof(struct rxrpc_header);
+		peer->maxdata = peer->mtu - peer->hdrsize;
+	}
+
+	_leave(" = %p", peer);
+	return peer;
+}
+
+/*
+ * obtain a remote transport endpoint for the specified address
+ */
+struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *srx, gfp_t gfp)
+{
+	struct rxrpc_peer *peer, *candidate;
+	const char *new = "old";
+	int usage;
+
+	_enter("{%d,%d,%u.%u.%u.%u+%hu}",
+	       srx->transport_type,
+	       srx->transport_len,
+	       NIPQUAD(srx->transport.sin.sin_addr),
+	       ntohs(srx->transport.sin.sin_port));
+
+	/* search the peer list first */
+	read_lock_bh(&rxrpc_peer_lock);
+	list_for_each_entry(peer, &rxrpc_peers, link) {
+		_debug("check PEER %d { u=%d t=%d l=%d }",
+		       peer->debug_id,
+		       atomic_read(&peer->usage),
+		       peer->srx.transport_type,
+		       peer->srx.transport_len);
+
+		if (atomic_read(&peer->usage) > 0 &&
+		    peer->srx.transport_type == srx->transport_type &&
+		    peer->srx.transport_len == srx->transport_len &&
+		    memcmp(&peer->srx.transport,
+			   &srx->transport,
+			   srx->transport_len) == 0)
+			goto found_extant_peer;
+	}
+	read_unlock_bh(&rxrpc_peer_lock);
+
+	/* not yet present - create a candidate for a new record and then
+	 * redo the search */
+	candidate = rxrpc_alloc_peer(srx, gfp);
+	if (!candidate) {
+		_leave(" = -ENOMEM");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	write_lock_bh(&rxrpc_peer_lock);
+
+	list_for_each_entry(peer, &rxrpc_peers, link) {
+		if (atomic_read(&peer->usage) > 0 &&
+		    peer->srx.transport_type == srx->transport_type &&
+		    peer->srx.transport_len == srx->transport_len &&
+		    memcmp(&peer->srx.transport,
+			   &srx->transport,
+			   srx->transport_len) == 0)
+			goto found_extant_second;
+	}
+
+	/* we can now add the new candidate to the list */
+	peer = candidate;
+	candidate = NULL;
+
+	list_add_tail(&peer->link, &rxrpc_peers);
+	write_unlock_bh(&rxrpc_peer_lock);
+	new = "new";
+
+success:
+	_net("PEER %s %d {%d,%u,%u.%u.%u.%u+%hu}",
+	     new,
+	     peer->debug_id,
+	     peer->srx.transport_type,
+	     peer->srx.transport.family,
+	     NIPQUAD(peer->srx.transport.sin.sin_addr),
+	     ntohs(peer->srx.transport.sin.sin_port));
+
+	_leave(" = %p {u=%d}", peer, atomic_read(&peer->usage));
+	return peer;
+
+	/* we found the peer in the list immediately */
+found_extant_peer:
+	usage = atomic_inc_return(&peer->usage);
+	read_unlock_bh(&rxrpc_peer_lock);
+	goto success;
+
+	/* we found the peer on the second time through the list */
+found_extant_second:
+	usage = atomic_inc_return(&peer->usage);
+	write_unlock_bh(&rxrpc_peer_lock);
+	kfree(candidate);
+	goto success;
+}
+
+/*
+ * find the peer associated with a packet
+ */
+struct rxrpc_peer *rxrpc_find_peer(struct rxrpc_local *local,
+				   __be32 addr, __be16 port)
+{
+	struct rxrpc_peer *peer;
+
+	_enter("");
+
+	/* search the peer list */
+	read_lock_bh(&rxrpc_peer_lock);
+
+	if (local->srx.transport.family == AF_INET &&
+	    local->srx.transport_type == SOCK_DGRAM
+	    ) {
+		list_for_each_entry(peer, &rxrpc_peers, link) {
+			if (atomic_read(&peer->usage) > 0 &&
+			    peer->srx.transport_type == SOCK_DGRAM &&
+			    peer->srx.transport.family == AF_INET &&
+			    peer->srx.transport.sin.sin_port == port &&
+			    peer->srx.transport.sin.sin_addr.s_addr == addr)
+				goto found_UDP_peer;
+		}
+
+		goto new_UDP_peer;
+	}
+
+	read_unlock_bh(&rxrpc_peer_lock);
+	_leave(" = -EAFNOSUPPORT");
+	return ERR_PTR(-EAFNOSUPPORT);
+
+found_UDP_peer:
+	_net("Rx UDP DGRAM from peer %d", peer->debug_id);
+	atomic_inc(&peer->usage);
+	read_unlock_bh(&rxrpc_peer_lock);
+	_leave(" = %p", peer);
+	return peer;
+
+new_UDP_peer:
+	_net("Rx UDP DGRAM from NEW peer %d", peer->debug_id);
+	read_unlock_bh(&rxrpc_peer_lock);
+	_leave(" = -EBUSY [new]");
+	return ERR_PTR(-EBUSY);
+}
+
+/*
+ * release a remote transport endpoint
+ */
+void rxrpc_put_peer(struct rxrpc_peer *peer)
+{
+	_enter("%p{u=%d}", peer, atomic_read(&peer->usage));
+
+	ASSERTCMP(atomic_read(&peer->usage), >, 0);
+
+	if (likely(!atomic_dec_and_test(&peer->usage))) {
+		_leave(" [in use]");
+		return;
+	}
+
+	rxrpc_queue_work(&peer->destroyer);
+	_leave("");
+}
+
+/*
+ * destroy a remote transport endpoint
+ */
+static void rxrpc_destroy_peer(struct work_struct *work)
+{
+	struct rxrpc_peer *peer =
+		container_of(work, struct rxrpc_peer, destroyer);
+
+	_enter("%p{%d}", peer, atomic_read(&peer->usage));
+
+	write_lock_bh(&rxrpc_peer_lock);
+	list_del(&peer->link);
+	write_unlock_bh(&rxrpc_peer_lock);
+
+	_net("DESTROY PEER %d", peer->debug_id);
+	kfree(peer);
+
+	if (list_empty(&rxrpc_peers))
+		wake_up_all(&rxrpc_peer_wq);
+	_leave("");
+}
+
+/*
+ * preemptively destroy all the peer records from a transport endpoint rather
+ * than waiting for them to time out
+ */
+void __exit rxrpc_destroy_all_peers(void)
+{
+	DECLARE_WAITQUEUE(myself,current);
+
+	_enter("");
+
+	/* we simply have to wait for them to go away */
+	if (!list_empty(&rxrpc_peers)) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		add_wait_queue(&rxrpc_peer_wq, &myself);
+
+		while (!list_empty(&rxrpc_peers)) {
+			schedule();
+			set_current_state(TASK_UNINTERRUPTIBLE);
+		}
+
+		remove_wait_queue(&rxrpc_peer_wq, &myself);
+		set_current_state(TASK_RUNNING);
+	}
+
+	_leave("");
+}
diff --git a/net/rxrpc/ar-proc.c b/net/rxrpc/ar-proc.c
new file mode 100644
index 0000000..58f4b4e
--- /dev/null
+++ b/net/rxrpc/ar-proc.c
@@ -0,0 +1,247 @@
+/* /proc/net/ support for AF_RXRPC
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#include <linux/module.h>
+#include <net/sock.h>
+#include <net/af_rxrpc.h>
+#include "ar-internal.h"
+
+static const char *rxrpc_conn_states[] = {
+	[RXRPC_CONN_UNUSED]		= "Unused  ",
+	[RXRPC_CONN_CLIENT]		= "Client  ",
+	[RXRPC_CONN_SERVER_UNSECURED]	= "SvUnsec ",
+	[RXRPC_CONN_SERVER_CHALLENGING]	= "SvChall ",
+	[RXRPC_CONN_SERVER]		= "SvSecure",
+	[RXRPC_CONN_REMOTELY_ABORTED]	= "RmtAbort",
+	[RXRPC_CONN_LOCALLY_ABORTED]	= "LocAbort",
+	[RXRPC_CONN_NETWORK_ERROR]	= "NetError",
+};
+
+const char *rxrpc_call_states[] = {
+	[RXRPC_CALL_CLIENT_SEND_REQUEST]	= "ClSndReq",
+	[RXRPC_CALL_CLIENT_AWAIT_REPLY]		= "ClAwtRpl",
+	[RXRPC_CALL_CLIENT_RECV_REPLY]		= "ClRcvRpl",
+	[RXRPC_CALL_CLIENT_FINAL_ACK]		= "ClFnlACK",
+	[RXRPC_CALL_SERVER_SECURING]		= "SvSecure",
+	[RXRPC_CALL_SERVER_ACCEPTING]		= "SvAccept",
+	[RXRPC_CALL_SERVER_RECV_REQUEST]	= "SvRcvReq",
+	[RXRPC_CALL_SERVER_ACK_REQUEST]		= "SvAckReq",
+	[RXRPC_CALL_SERVER_SEND_REPLY]		= "SvSndRpl",
+	[RXRPC_CALL_SERVER_AWAIT_ACK]		= "SvAwtACK",
+	[RXRPC_CALL_COMPLETE]			= "Complete",
+	[RXRPC_CALL_SERVER_BUSY]		= "SvBusy  ",
+	[RXRPC_CALL_REMOTELY_ABORTED]		= "RmtAbort",
+	[RXRPC_CALL_LOCALLY_ABORTED]		= "LocAbort",
+	[RXRPC_CALL_NETWORK_ERROR]		= "NetError",
+	[RXRPC_CALL_DEAD]			= "Dead    ",
+};
+
+/*
+ * generate a list of extant and dead calls in /proc/net/rxrpc_calls
+ */
+static void *rxrpc_call_seq_start(struct seq_file *seq, loff_t *_pos)
+{
+	struct list_head *_p;
+	loff_t pos = *_pos;
+
+	read_lock(&rxrpc_call_lock);
+	if (!pos)
+		return SEQ_START_TOKEN;
+	pos--;
+
+	list_for_each(_p, &rxrpc_calls)
+		if (!pos--)
+			break;
+
+	return _p != &rxrpc_calls ? _p : NULL;
+}
+
+static void *rxrpc_call_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct list_head *_p;
+
+	(*pos)++;
+
+	_p = v;
+	_p = (v == SEQ_START_TOKEN) ? rxrpc_calls.next : _p->next;
+
+	return _p != &rxrpc_calls ? _p : NULL;
+}
+
+static void rxrpc_call_seq_stop(struct seq_file *seq, void *v)
+{
+	read_unlock(&rxrpc_call_lock);
+}
+
+static int rxrpc_call_seq_show(struct seq_file *seq, void *v)
+{
+	struct rxrpc_transport *trans;
+	struct rxrpc_call *call;
+	char lbuff[4 + 4 + 4 + 4 + 5 + 1], rbuff[4 + 4 + 4 + 4 + 5 + 1];
+
+	if (v == SEQ_START_TOKEN) {
+		seq_puts(seq,
+			 "Proto Local                  Remote                "
+			 " SvID ConnID   CallID   End Use State    Abort   "
+			 " UserID\n");
+		return 0;
+	}
+
+	call = list_entry(v, struct rxrpc_call, link);
+	trans = call->conn->trans;
+
+	sprintf(lbuff, NIPQUAD_FMT":%u",
+		NIPQUAD(trans->local->srx.transport.sin.sin_addr),
+		ntohs(trans->local->srx.transport.sin.sin_port));
+
+	sprintf(rbuff, NIPQUAD_FMT":%u",
+		NIPQUAD(trans->peer->srx.transport.sin.sin_addr),
+		ntohs(trans->peer->srx.transport.sin.sin_port));
+
+	seq_printf(seq,
+		   "UDP   %-22.22s %-22.22s %4x %08x %08x %s %3u"
+		   " %-8.8s %08x %lx\n",
+		   lbuff,
+		   rbuff,
+		   ntohs(call->conn->service_id),
+		   ntohl(call->conn->cid),
+		   ntohl(call->call_id),
+		   call->conn->in_clientflag ? "Svc" : "Clt",
+		   atomic_read(&call->usage),
+		   rxrpc_call_states[call->state],
+		   call->abort_code,
+		   call->user_call_ID);
+
+	return 0;
+}
+
+static struct seq_operations rxrpc_call_seq_ops = {
+	.start  = rxrpc_call_seq_start,
+	.next   = rxrpc_call_seq_next,
+	.stop   = rxrpc_call_seq_stop,
+	.show   = rxrpc_call_seq_show,
+};
+
+static int rxrpc_call_seq_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &rxrpc_call_seq_ops);
+}
+
+struct file_operations rxrpc_call_seq_fops = {
+	.owner		= THIS_MODULE,
+	.open		= rxrpc_call_seq_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release_private,
+};
+
+/*
+ * generate a list of extant virtual connections in /proc/net/rxrpc_conns
+ */
+static void *rxrpc_connection_seq_start(struct seq_file *seq, loff_t *_pos)
+{
+	struct list_head *_p;
+	loff_t pos = *_pos;
+
+	read_lock(&rxrpc_connection_lock);
+	if (!pos)
+		return SEQ_START_TOKEN;
+	pos--;
+
+	list_for_each(_p, &rxrpc_connections)
+		if (!pos--)
+			break;
+
+	return _p != &rxrpc_connections ? _p : NULL;
+}
+
+static void *rxrpc_connection_seq_next(struct seq_file *seq, void *v,
+				       loff_t *pos)
+{
+	struct list_head *_p;
+
+	(*pos)++;
+
+	_p = v;
+	_p = (v == SEQ_START_TOKEN) ? rxrpc_connections.next : _p->next;
+
+	return _p != &rxrpc_connections ? _p : NULL;
+}
+
+static void rxrpc_connection_seq_stop(struct seq_file *seq, void *v)
+{
+	read_unlock(&rxrpc_connection_lock);
+}
+
+static int rxrpc_connection_seq_show(struct seq_file *seq, void *v)
+{
+	struct rxrpc_connection *conn;
+	struct rxrpc_transport *trans;
+	char lbuff[4 + 4 + 4 + 4 + 5 + 1], rbuff[4 + 4 + 4 + 4 + 5 + 1];
+
+	if (v == SEQ_START_TOKEN) {
+		seq_puts(seq,
+			 "Proto Local                  Remote                "
+			 " SvID ConnID   Calls    End Use State    Key     "
+			 " Serial   ISerial\n"
+			 );
+		return 0;
+	}
+
+	conn = list_entry(v, struct rxrpc_connection, link);
+	trans = conn->trans;
+
+	sprintf(lbuff, NIPQUAD_FMT":%u",
+		NIPQUAD(trans->local->srx.transport.sin.sin_addr),
+		ntohs(trans->local->srx.transport.sin.sin_port));
+
+	sprintf(rbuff, NIPQUAD_FMT":%u",
+		NIPQUAD(trans->peer->srx.transport.sin.sin_addr),
+		ntohs(trans->peer->srx.transport.sin.sin_port));
+
+	seq_printf(seq,
+		   "UDP   %-22.22s %-22.22s %4x %08x %08x %s %3u"
+		   " %s %08x %08x %08x\n",
+		   lbuff,
+		   rbuff,
+		   ntohs(conn->service_id),
+		   ntohl(conn->cid),
+		   conn->call_counter,
+		   conn->in_clientflag ? "Svc" : "Clt",
+		   atomic_read(&conn->usage),
+		   rxrpc_conn_states[conn->state],
+		   key_serial(conn->key),
+		   atomic_read(&conn->serial),
+		   atomic_read(&conn->hi_serial));
+
+	return 0;
+}
+
+static struct seq_operations rxrpc_connection_seq_ops = {
+	.start  = rxrpc_connection_seq_start,
+	.next   = rxrpc_connection_seq_next,
+	.stop   = rxrpc_connection_seq_stop,
+	.show   = rxrpc_connection_seq_show,
+};
+
+
+static int rxrpc_connection_seq_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &rxrpc_connection_seq_ops);
+}
+
+struct file_operations rxrpc_connection_seq_fops = {
+	.owner		= THIS_MODULE,
+	.open		= rxrpc_connection_seq_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release_private,
+};
diff --git a/net/rxrpc/ar-recvmsg.c b/net/rxrpc/ar-recvmsg.c
new file mode 100644
index 0000000..f19121d
--- /dev/null
+++ b/net/rxrpc/ar-recvmsg.c
@@ -0,0 +1,437 @@
+/* RxRPC recvmsg() implementation
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/af_rxrpc.h>
+#include "ar-internal.h"
+
+/*
+ * removal a call's user ID from the socket tree to make the user ID available
+ * again and so that it won't be seen again in association with that call
+ */
+void rxrpc_remove_user_ID(struct rxrpc_sock *rx, struct rxrpc_call *call)
+{
+	_debug("RELEASE CALL %d", call->debug_id);
+
+	if (test_bit(RXRPC_CALL_HAS_USERID, &call->flags)) {
+		write_lock_bh(&rx->call_lock);
+		rb_erase(&call->sock_node, &call->socket->calls);
+		clear_bit(RXRPC_CALL_HAS_USERID, &call->flags);
+		write_unlock_bh(&rx->call_lock);
+	}
+
+	read_lock_bh(&call->state_lock);
+	if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) &&
+	    !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events))
+		rxrpc_queue_call(call);
+	read_unlock_bh(&call->state_lock);
+}
+
+/*
+ * receive a message from an RxRPC socket
+ * - we need to be careful about two or more threads calling recvmsg
+ *   simultaneously
+ */
+int rxrpc_recvmsg(struct kiocb *iocb, struct socket *sock,
+		  struct msghdr *msg, size_t len, int flags)
+{
+	struct rxrpc_skb_priv *sp;
+	struct rxrpc_call *call = NULL, *continue_call = NULL;
+	struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
+	struct sk_buff *skb;
+	long timeo;
+	int copy, ret, ullen, offset, copied = 0;
+	u32 abort_code;
+
+	DEFINE_WAIT(wait);
+
+	_enter(",,,%zu,%d", len, flags);
+
+	if (flags & (MSG_OOB | MSG_TRUNC))
+		return -EOPNOTSUPP;
+
+	ullen = msg->msg_flags & MSG_CMSG_COMPAT ? 4 : sizeof(unsigned long);
+
+	timeo = sock_rcvtimeo(&rx->sk, flags & MSG_DONTWAIT);
+	msg->msg_flags |= MSG_MORE;
+
+	lock_sock(&rx->sk);
+
+	for (;;) {
+		/* return immediately if a client socket has no outstanding
+		 * calls */
+		if (RB_EMPTY_ROOT(&rx->calls)) {
+			if (copied)
+				goto out;
+			if (rx->sk.sk_state != RXRPC_SERVER_LISTENING) {
+				release_sock(&rx->sk);
+				if (continue_call)
+					rxrpc_put_call(continue_call);
+				return -ENODATA;
+			}
+		}
+
+		/* get the next message on the Rx queue */
+		skb = skb_peek(&rx->sk.sk_receive_queue);
+		if (!skb) {
+			/* nothing remains on the queue */
+			if (copied &&
+			    (msg->msg_flags & MSG_PEEK || timeo == 0))
+				goto out;
+
+			/* wait for a message to turn up */
+			release_sock(&rx->sk);
+			prepare_to_wait_exclusive(rx->sk.sk_sleep, &wait,
+						  TASK_INTERRUPTIBLE);
+			ret = sock_error(&rx->sk);
+			if (ret)
+				goto wait_error;
+
+			if (skb_queue_empty(&rx->sk.sk_receive_queue)) {
+				if (signal_pending(current))
+					goto wait_interrupted;
+				timeo = schedule_timeout(timeo);
+			}
+			finish_wait(rx->sk.sk_sleep, &wait);
+			lock_sock(&rx->sk);
+			continue;
+		}
+
+	peek_next_packet:
+		sp = rxrpc_skb(skb);
+		call = sp->call;
+		ASSERT(call != NULL);
+
+		_debug("next pkt %s", rxrpc_pkts[sp->hdr.type]);
+
+		/* make sure we wait for the state to be updated in this call */
+		spin_lock_bh(&call->lock);
+		spin_unlock_bh(&call->lock);
+
+		if (test_bit(RXRPC_CALL_RELEASED, &call->flags)) {
+			_debug("packet from released call");
+			if (skb_dequeue(&rx->sk.sk_receive_queue) != skb)
+				BUG();
+			rxrpc_free_skb(skb);
+			continue;
+		}
+
+		/* determine whether to continue last data receive */
+		if (continue_call) {
+			_debug("maybe cont");
+			if (call != continue_call ||
+			    skb->mark != RXRPC_SKB_MARK_DATA) {
+				release_sock(&rx->sk);
+				rxrpc_put_call(continue_call);
+				_leave(" = %d [noncont]", copied);
+				return copied;
+			}
+		}
+
+		rxrpc_get_call(call);
+
+		/* copy the peer address and timestamp */
+		if (!continue_call) {
+			if (msg->msg_name && msg->msg_namelen > 0)
+				memcpy(&msg->msg_name, &call->conn->trans->peer->srx,
+				       sizeof(call->conn->trans->peer->srx));
+			sock_recv_timestamp(msg, &rx->sk, skb);
+		}
+
+		/* receive the message */
+		if (skb->mark != RXRPC_SKB_MARK_DATA)
+			goto receive_non_data_message;
+
+		_debug("recvmsg DATA #%u { %d, %d }",
+		       ntohl(sp->hdr.seq), skb->len, sp->offset);
+
+		if (!continue_call) {
+			/* only set the control data once per recvmsg() */
+			ret = put_cmsg(msg, SOL_RXRPC, RXRPC_USER_CALL_ID,
+				       ullen, &call->user_call_ID);
+			if (ret < 0)
+				goto copy_error;
+			ASSERT(test_bit(RXRPC_CALL_HAS_USERID, &call->flags));
+		}
+
+		ASSERTCMP(ntohl(sp->hdr.seq), >=, call->rx_data_recv);
+		ASSERTCMP(ntohl(sp->hdr.seq), <=, call->rx_data_recv + 1);
+		call->rx_data_recv = ntohl(sp->hdr.seq);
+
+		ASSERTCMP(ntohl(sp->hdr.seq), >, call->rx_data_eaten);
+
+		offset = sp->offset;
+		copy = skb->len - offset;
+		if (copy > len - copied)
+			copy = len - copied;
+
+		if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
+			ret = skb_copy_datagram_iovec(skb, offset,
+						      msg->msg_iov, copy);
+		} else {
+			ret = skb_copy_and_csum_datagram_iovec(skb, offset,
+							       msg->msg_iov);
+			if (ret == -EINVAL)
+				goto csum_copy_error;
+		}
+
+		if (ret < 0)
+			goto copy_error;
+
+		/* handle piecemeal consumption of data packets */
+		_debug("copied %d+%d", copy, copied);
+
+		offset += copy;
+		copied += copy;
+
+		if (!(flags & MSG_PEEK))
+			sp->offset = offset;
+
+		if (sp->offset < skb->len) {
+			_debug("buffer full");
+			ASSERTCMP(copied, ==, len);
+			break;
+		}
+
+		/* we transferred the whole data packet */
+		if (sp->hdr.flags & RXRPC_LAST_PACKET) {
+			_debug("last");
+			if (call->conn->out_clientflag) {
+				 /* last byte of reply received */
+				ret = copied;
+				goto terminal_message;
+			}
+
+			/* last bit of request received */
+			if (!(flags & MSG_PEEK)) {
+				_debug("eat packet");
+				if (skb_dequeue(&rx->sk.sk_receive_queue) !=
+				    skb)
+					BUG();
+				rxrpc_free_skb(skb);
+			}
+			msg->msg_flags &= ~MSG_MORE;
+			break;
+		}
+
+		/* move on to the next data message */
+		_debug("next");
+		if (!continue_call)
+			continue_call = sp->call;
+		else
+			rxrpc_put_call(call);
+		call = NULL;
+
+		if (flags & MSG_PEEK) {
+			_debug("peek next");
+			skb = skb->next;
+			if (skb == (struct sk_buff *) &rx->sk.sk_receive_queue)
+				break;
+			goto peek_next_packet;
+		}
+
+		_debug("eat packet");
+		if (skb_dequeue(&rx->sk.sk_receive_queue) != skb)
+			BUG();
+		rxrpc_free_skb(skb);
+	}
+
+	/* end of non-terminal data packet reception for the moment */
+	_debug("end rcv data");
+out:
+	release_sock(&rx->sk);
+	if (call)
+		rxrpc_put_call(call);
+	if (continue_call)
+		rxrpc_put_call(continue_call);
+	_leave(" = %d [data]", copied);
+	return copied;
+
+	/* handle non-DATA messages such as aborts, incoming connections and
+	 * final ACKs */
+receive_non_data_message:
+	_debug("non-data");
+
+	if (skb->mark == RXRPC_SKB_MARK_NEW_CALL) {
+		_debug("RECV NEW CALL");
+		ret = put_cmsg(msg, SOL_RXRPC, RXRPC_NEW_CALL, 0, &abort_code);
+		if (ret < 0)
+			goto copy_error;
+		if (!(flags & MSG_PEEK)) {
+			if (skb_dequeue(&rx->sk.sk_receive_queue) != skb)
+				BUG();
+			rxrpc_free_skb(skb);
+		}
+		goto out;
+	}
+
+	ret = put_cmsg(msg, SOL_RXRPC, RXRPC_USER_CALL_ID,
+		       ullen, &call->user_call_ID);
+	if (ret < 0)
+		goto copy_error;
+	ASSERT(test_bit(RXRPC_CALL_HAS_USERID, &call->flags));
+
+	switch (skb->mark) {
+	case RXRPC_SKB_MARK_DATA:
+		BUG();
+	case RXRPC_SKB_MARK_FINAL_ACK:
+		ret = put_cmsg(msg, SOL_RXRPC, RXRPC_ACK, 0, &abort_code);
+		break;
+	case RXRPC_SKB_MARK_BUSY:
+		ret = put_cmsg(msg, SOL_RXRPC, RXRPC_BUSY, 0, &abort_code);
+		break;
+	case RXRPC_SKB_MARK_REMOTE_ABORT:
+		abort_code = call->abort_code;
+		ret = put_cmsg(msg, SOL_RXRPC, RXRPC_ABORT, 4, &abort_code);
+		break;
+	case RXRPC_SKB_MARK_NET_ERROR:
+		_debug("RECV NET ERROR %d", sp->error);
+		abort_code = sp->error;
+		ret = put_cmsg(msg, SOL_RXRPC, RXRPC_NET_ERROR, 4, &abort_code);
+		break;
+	case RXRPC_SKB_MARK_LOCAL_ERROR:
+		_debug("RECV LOCAL ERROR %d", sp->error);
+		abort_code = sp->error;
+		ret = put_cmsg(msg, SOL_RXRPC, RXRPC_LOCAL_ERROR, 4,
+			       &abort_code);
+		break;
+	default:
+		BUG();
+		break;
+	}
+
+	if (ret < 0)
+		goto copy_error;
+
+terminal_message:
+	_debug("terminal");
+	msg->msg_flags &= ~MSG_MORE;
+	msg->msg_flags |= MSG_EOR;
+
+	if (!(flags & MSG_PEEK)) {
+		_net("free terminal skb %p", skb);
+		if (skb_dequeue(&rx->sk.sk_receive_queue) != skb)
+			BUG();
+		rxrpc_free_skb(skb);
+		rxrpc_remove_user_ID(rx, call);
+	}
+
+	release_sock(&rx->sk);
+	rxrpc_put_call(call);
+	if (continue_call)
+		rxrpc_put_call(continue_call);
+	_leave(" = %d", ret);
+	return ret;
+
+copy_error:
+	_debug("copy error");
+	release_sock(&rx->sk);
+	rxrpc_put_call(call);
+	if (continue_call)
+		rxrpc_put_call(continue_call);
+	_leave(" = %d", ret);
+	return ret;
+
+csum_copy_error:
+	_debug("csum error");
+	release_sock(&rx->sk);
+	if (continue_call)
+		rxrpc_put_call(continue_call);
+	rxrpc_kill_skb(skb);
+	skb_kill_datagram(&rx->sk, skb, flags);
+	rxrpc_put_call(call);
+	return -EAGAIN;
+
+wait_interrupted:
+	ret = sock_intr_errno(timeo);
+wait_error:
+	finish_wait(rx->sk.sk_sleep, &wait);
+	if (continue_call)
+		rxrpc_put_call(continue_call);
+	if (copied)
+		copied = ret;
+	_leave(" = %d [waitfail %d]", copied, ret);
+	return copied;
+
+}
+
+/**
+ * rxrpc_kernel_data_delivered - Record delivery of data message
+ * @skb: Message holding data
+ *
+ * Record the delivery of a data message.  This permits RxRPC to keep its
+ * tracking correct.  The socket buffer will be deleted.
+ */
+void rxrpc_kernel_data_delivered(struct sk_buff *skb)
+{
+	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+	struct rxrpc_call *call = sp->call;
+
+	ASSERTCMP(ntohl(sp->hdr.seq), >=, call->rx_data_recv);
+	ASSERTCMP(ntohl(sp->hdr.seq), <=, call->rx_data_recv + 1);
+	call->rx_data_recv = ntohl(sp->hdr.seq);
+
+	ASSERTCMP(ntohl(sp->hdr.seq), >, call->rx_data_eaten);
+	rxrpc_free_skb(skb);
+}
+
+EXPORT_SYMBOL(rxrpc_kernel_data_delivered);
+
+/**
+ * rxrpc_kernel_is_data_last - Determine if data message is last one
+ * @skb: Message holding data
+ *
+ * Determine if data message is last one for the parent call.
+ */
+bool rxrpc_kernel_is_data_last(struct sk_buff *skb)
+{
+	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+
+	ASSERTCMP(skb->mark, ==, RXRPC_SKB_MARK_DATA);
+
+	return sp->hdr.flags & RXRPC_LAST_PACKET;
+}
+
+EXPORT_SYMBOL(rxrpc_kernel_is_data_last);
+
+/**
+ * rxrpc_kernel_get_abort_code - Get the abort code from an RxRPC abort message
+ * @skb: Message indicating an abort
+ *
+ * Get the abort code from an RxRPC abort message.
+ */
+u32 rxrpc_kernel_get_abort_code(struct sk_buff *skb)
+{
+	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+
+	ASSERTCMP(skb->mark, ==, RXRPC_SKB_MARK_REMOTE_ABORT);
+
+	return sp->call->abort_code;
+}
+
+EXPORT_SYMBOL(rxrpc_kernel_get_abort_code);
+
+/**
+ * rxrpc_kernel_get_error - Get the error number from an RxRPC error message
+ * @skb: Message indicating an error
+ *
+ * Get the error number from an RxRPC error message.
+ */
+int rxrpc_kernel_get_error_number(struct sk_buff *skb)
+{
+	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+
+	return sp->error;
+}
+
+EXPORT_SYMBOL(rxrpc_kernel_get_error_number);
diff --git a/net/rxrpc/ar-security.c b/net/rxrpc/ar-security.c
new file mode 100644
index 0000000..60d1d36
--- /dev/null
+++ b/net/rxrpc/ar-security.c
@@ -0,0 +1,258 @@
+/* RxRPC security handling
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/udp.h>
+#include <linux/crypto.h>
+#include <net/sock.h>
+#include <net/af_rxrpc.h>
+#include "ar-internal.h"
+
+static LIST_HEAD(rxrpc_security_methods);
+static DECLARE_RWSEM(rxrpc_security_sem);
+
+/*
+ * get an RxRPC security module
+ */
+static struct rxrpc_security *rxrpc_security_get(struct rxrpc_security *sec)
+{
+	return try_module_get(sec->owner) ? sec : NULL;
+}
+
+/*
+ * release an RxRPC security module
+ */
+static void rxrpc_security_put(struct rxrpc_security *sec)
+{
+	module_put(sec->owner);
+}
+
+/*
+ * look up an rxrpc security module
+ */
+struct rxrpc_security *rxrpc_security_lookup(u8 security_index)
+{
+	struct rxrpc_security *sec = NULL;
+
+	_enter("");
+
+	down_read(&rxrpc_security_sem);
+
+	list_for_each_entry(sec, &rxrpc_security_methods, link) {
+		if (sec->security_index == security_index) {
+			if (unlikely(!rxrpc_security_get(sec)))
+				break;
+			goto out;
+		}
+	}
+
+	sec = NULL;
+out:
+	up_read(&rxrpc_security_sem);
+	_leave(" = %p [%s]", sec, sec ? sec->name : "");
+	return sec;
+}
+
+/**
+ * rxrpc_register_security - register an RxRPC security handler
+ * @sec: security module
+ *
+ * register an RxRPC security handler for use by RxRPC
+ */
+int rxrpc_register_security(struct rxrpc_security *sec)
+{
+	struct rxrpc_security *psec;
+	int ret;
+
+	_enter("");
+	down_write(&rxrpc_security_sem);
+
+	ret = -EEXIST;
+	list_for_each_entry(psec, &rxrpc_security_methods, link) {
+		if (psec->security_index == sec->security_index)
+			goto out;
+	}
+
+	list_add(&sec->link, &rxrpc_security_methods);
+
+	printk(KERN_NOTICE "RxRPC: Registered security type %d '%s'\n",
+	       sec->security_index, sec->name);
+	ret = 0;
+
+out:
+	up_write(&rxrpc_security_sem);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+EXPORT_SYMBOL_GPL(rxrpc_register_security);
+
+/**
+ * rxrpc_unregister_security - unregister an RxRPC security handler
+ * @sec: security module
+ *
+ * unregister an RxRPC security handler
+ */
+void rxrpc_unregister_security(struct rxrpc_security *sec)
+{
+
+	_enter("");
+	down_write(&rxrpc_security_sem);
+	list_del_init(&sec->link);
+	up_write(&rxrpc_security_sem);
+
+	printk(KERN_NOTICE "RxRPC: Unregistered security type %d '%s'\n",
+	       sec->security_index, sec->name);
+}
+
+EXPORT_SYMBOL_GPL(rxrpc_unregister_security);
+
+/*
+ * initialise the security on a client connection
+ */
+int rxrpc_init_client_conn_security(struct rxrpc_connection *conn)
+{
+	struct rxrpc_security *sec;
+	struct key *key = conn->key;
+	int ret;
+
+	_enter("{%d},{%x}", conn->debug_id, key_serial(key));
+
+	if (!key)
+		return 0;
+
+	ret = key_validate(key);
+	if (ret < 0)
+		return ret;
+
+	sec = rxrpc_security_lookup(key->type_data.x[0]);
+	if (!sec)
+		return -EKEYREJECTED;
+	conn->security = sec;
+
+	ret = conn->security->init_connection_security(conn);
+	if (ret < 0) {
+		rxrpc_security_put(conn->security);
+		conn->security = NULL;
+		return ret;
+	}
+
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * initialise the security on a server connection
+ */
+int rxrpc_init_server_conn_security(struct rxrpc_connection *conn)
+{
+	struct rxrpc_security *sec;
+	struct rxrpc_local *local = conn->trans->local;
+	struct rxrpc_sock *rx;
+	struct key *key;
+	key_ref_t kref;
+	char kdesc[5+1+3+1];
+
+	_enter("");
+
+	sprintf(kdesc, "%u:%u", ntohs(conn->service_id), conn->security_ix);
+
+	sec = rxrpc_security_lookup(conn->security_ix);
+	if (!sec) {
+		_leave(" = -ENOKEY [lookup]");
+		return -ENOKEY;
+	}
+
+	/* find the service */
+	read_lock_bh(&local->services_lock);
+	list_for_each_entry(rx, &local->services, listen_link) {
+		if (rx->service_id == conn->service_id)
+			goto found_service;
+	}
+
+	/* the service appears to have died */
+	read_unlock_bh(&local->services_lock);
+	rxrpc_security_put(sec);
+	_leave(" = -ENOENT");
+	return -ENOENT;
+
+found_service:
+	if (!rx->securities) {
+		read_unlock_bh(&local->services_lock);
+		rxrpc_security_put(sec);
+		_leave(" = -ENOKEY");
+		return -ENOKEY;
+	}
+
+	/* look through the service's keyring */
+	kref = keyring_search(make_key_ref(rx->securities, 1UL),
+			      &key_type_rxrpc_s, kdesc);
+	if (IS_ERR(kref)) {
+		read_unlock_bh(&local->services_lock);
+		rxrpc_security_put(sec);
+		_leave(" = %ld [search]", PTR_ERR(kref));
+		return PTR_ERR(kref);
+	}
+
+	key = key_ref_to_ptr(kref);
+	read_unlock_bh(&local->services_lock);
+
+	conn->server_key = key;
+	conn->security = sec;
+
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * secure a packet prior to transmission
+ */
+int rxrpc_secure_packet(const struct rxrpc_call *call,
+			struct sk_buff *skb,
+			size_t data_size,
+			void *sechdr)
+{
+	if (call->conn->security)
+		return call->conn->security->secure_packet(
+			call, skb, data_size, sechdr);
+	return 0;
+}
+
+/*
+ * secure a packet prior to transmission
+ */
+int rxrpc_verify_packet(const struct rxrpc_call *call, struct sk_buff *skb,
+			u32 *_abort_code)
+{
+	if (call->conn->security)
+		return call->conn->security->verify_packet(
+			call, skb, _abort_code);
+	return 0;
+}
+
+/*
+ * clear connection security
+ */
+void rxrpc_clear_conn_security(struct rxrpc_connection *conn)
+{
+	_enter("{%d}", conn->debug_id);
+
+	if (conn->security) {
+		conn->security->clear(conn);
+		rxrpc_security_put(conn->security);
+		conn->security = NULL;
+	}
+
+	key_put(conn->key);
+	key_put(conn->server_key);
+}
diff --git a/net/rxrpc/ar-skbuff.c b/net/rxrpc/ar-skbuff.c
new file mode 100644
index 0000000..de755e0
--- /dev/null
+++ b/net/rxrpc/ar-skbuff.c
@@ -0,0 +1,132 @@
+/* ar-skbuff.c: socket buffer destruction handling
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/af_rxrpc.h>
+#include "ar-internal.h"
+
+/*
+ * set up for the ACK at the end of the receive phase when we discard the final
+ * receive phase data packet
+ * - called with softirqs disabled
+ */
+static void rxrpc_request_final_ACK(struct rxrpc_call *call)
+{
+	/* the call may be aborted before we have a chance to ACK it */
+	write_lock(&call->state_lock);
+
+	switch (call->state) {
+	case RXRPC_CALL_CLIENT_RECV_REPLY:
+		call->state = RXRPC_CALL_CLIENT_FINAL_ACK;
+		_debug("request final ACK");
+
+		/* get an extra ref on the call for the final-ACK generator to
+		 * release */
+		rxrpc_get_call(call);
+		set_bit(RXRPC_CALL_ACK_FINAL, &call->events);
+		if (try_to_del_timer_sync(&call->ack_timer) >= 0)
+			rxrpc_queue_call(call);
+		break;
+
+	case RXRPC_CALL_SERVER_RECV_REQUEST:
+		call->state = RXRPC_CALL_SERVER_ACK_REQUEST;
+	default:
+		break;
+	}
+
+	write_unlock(&call->state_lock);
+}
+
+/*
+ * drop the bottom ACK off of the call ACK window and advance the window
+ */
+static void rxrpc_hard_ACK_data(struct rxrpc_call *call,
+				struct rxrpc_skb_priv *sp)
+{
+	int loop;
+	u32 seq;
+
+	spin_lock_bh(&call->lock);
+
+	_debug("hard ACK #%u", ntohl(sp->hdr.seq));
+
+	for (loop = 0; loop < RXRPC_ACKR_WINDOW_ASZ; loop++) {
+		call->ackr_window[loop] >>= 1;
+		call->ackr_window[loop] |=
+			call->ackr_window[loop + 1] << (BITS_PER_LONG - 1);
+	}
+
+	seq = ntohl(sp->hdr.seq);
+	ASSERTCMP(seq, ==, call->rx_data_eaten + 1);
+	call->rx_data_eaten = seq;
+
+	if (call->ackr_win_top < UINT_MAX)
+		call->ackr_win_top++;
+
+	ASSERTIFCMP(call->state <= RXRPC_CALL_COMPLETE,
+		    call->rx_data_post, >=, call->rx_data_recv);
+	ASSERTIFCMP(call->state <= RXRPC_CALL_COMPLETE,
+		    call->rx_data_recv, >=, call->rx_data_eaten);
+
+	if (sp->hdr.flags & RXRPC_LAST_PACKET) {
+		rxrpc_request_final_ACK(call);
+	} else if (atomic_dec_and_test(&call->ackr_not_idle) &&
+		   test_and_clear_bit(RXRPC_CALL_TX_SOFT_ACK, &call->flags)) {
+		_debug("send Rx idle ACK");
+		__rxrpc_propose_ACK(call, RXRPC_ACK_IDLE, sp->hdr.serial,
+				    true);
+	}
+
+	spin_unlock_bh(&call->lock);
+}
+
+/*
+ * destroy a packet that has an RxRPC control buffer
+ * - advance the hard-ACK state of the parent call (done here in case something
+ *   in the kernel bypasses recvmsg() and steals the packet directly off of the
+ *   socket receive queue)
+ */
+void rxrpc_packet_destructor(struct sk_buff *skb)
+{
+	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+	struct rxrpc_call *call = sp->call;
+
+	_enter("%p{%p}", skb, call);
+
+	if (call) {
+		/* send the final ACK on a client call */
+		if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA)
+			rxrpc_hard_ACK_data(call, sp);
+		rxrpc_put_call(call);
+		sp->call = NULL;
+	}
+
+	if (skb->sk)
+		sock_rfree(skb);
+	_leave("");
+}
+
+/**
+ * rxrpc_kernel_free_skb - Free an RxRPC socket buffer
+ * @skb: The socket buffer to be freed
+ *
+ * Let RxRPC free its own socket buffer, permitting it to maintain debug
+ * accounting.
+ */
+void rxrpc_kernel_free_skb(struct sk_buff *skb)
+{
+	rxrpc_free_skb(skb);
+}
+
+EXPORT_SYMBOL(rxrpc_kernel_free_skb);
diff --git a/net/rxrpc/ar-transport.c b/net/rxrpc/ar-transport.c
new file mode 100644
index 0000000..d43d78f
--- /dev/null
+++ b/net/rxrpc/ar-transport.c
@@ -0,0 +1,276 @@
+/* RxRPC point-to-point transport session management
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/af_rxrpc.h>
+#include "ar-internal.h"
+
+static void rxrpc_transport_reaper(struct work_struct *work);
+
+static LIST_HEAD(rxrpc_transports);
+static DEFINE_RWLOCK(rxrpc_transport_lock);
+static unsigned long rxrpc_transport_timeout = 3600 * 24;
+static DECLARE_DELAYED_WORK(rxrpc_transport_reap, rxrpc_transport_reaper);
+
+/*
+ * allocate a new transport session manager
+ */
+static struct rxrpc_transport *rxrpc_alloc_transport(struct rxrpc_local *local,
+						     struct rxrpc_peer *peer,
+						     gfp_t gfp)
+{
+	struct rxrpc_transport *trans;
+
+	_enter("");
+
+	trans = kzalloc(sizeof(struct rxrpc_transport), gfp);
+	if (trans) {
+		trans->local = local;
+		trans->peer = peer;
+		INIT_LIST_HEAD(&trans->link);
+		trans->bundles = RB_ROOT;
+		trans->client_conns = RB_ROOT;
+		trans->server_conns = RB_ROOT;
+		skb_queue_head_init(&trans->error_queue);
+		spin_lock_init(&trans->client_lock);
+		rwlock_init(&trans->conn_lock);
+		atomic_set(&trans->usage, 1);
+		trans->debug_id = atomic_inc_return(&rxrpc_debug_id);
+
+		if (peer->srx.transport.family == AF_INET) {
+			switch (peer->srx.transport_type) {
+			case SOCK_DGRAM:
+				INIT_WORK(&trans->error_handler,
+					  rxrpc_UDP_error_handler);
+				break;
+			default:
+				BUG();
+				break;
+			}
+		} else {
+			BUG();
+		}
+	}
+
+	_leave(" = %p", trans);
+	return trans;
+}
+
+/*
+ * obtain a transport session for the nominated endpoints
+ */
+struct rxrpc_transport *rxrpc_get_transport(struct rxrpc_local *local,
+					    struct rxrpc_peer *peer,
+					    gfp_t gfp)
+{
+	struct rxrpc_transport *trans, *candidate;
+	const char *new = "old";
+	int usage;
+
+	_enter("{%u.%u.%u.%u+%hu},{%u.%u.%u.%u+%hu},",
+	       NIPQUAD(local->srx.transport.sin.sin_addr),
+	       ntohs(local->srx.transport.sin.sin_port),
+	       NIPQUAD(peer->srx.transport.sin.sin_addr),
+	       ntohs(peer->srx.transport.sin.sin_port));
+
+	/* search the transport list first */
+	read_lock_bh(&rxrpc_transport_lock);
+	list_for_each_entry(trans, &rxrpc_transports, link) {
+		if (trans->local == local && trans->peer == peer)
+			goto found_extant_transport;
+	}
+	read_unlock_bh(&rxrpc_transport_lock);
+
+	/* not yet present - create a candidate for a new record and then
+	 * redo the search */
+	candidate = rxrpc_alloc_transport(local, peer, gfp);
+	if (!candidate) {
+		_leave(" = -ENOMEM");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	write_lock_bh(&rxrpc_transport_lock);
+
+	list_for_each_entry(trans, &rxrpc_transports, link) {
+		if (trans->local == local && trans->peer == peer)
+			goto found_extant_second;
+	}
+
+	/* we can now add the new candidate to the list */
+	trans = candidate;
+	candidate = NULL;
+
+	rxrpc_get_local(trans->local);
+	atomic_inc(&trans->peer->usage);
+	list_add_tail(&trans->link, &rxrpc_transports);
+	write_unlock_bh(&rxrpc_transport_lock);
+	new = "new";
+
+success:
+	_net("TRANSPORT %s %d local %d -> peer %d",
+	     new,
+	     trans->debug_id,
+	     trans->local->debug_id,
+	     trans->peer->debug_id);
+
+	_leave(" = %p {u=%d}", trans, atomic_read(&trans->usage));
+	return trans;
+
+	/* we found the transport in the list immediately */
+found_extant_transport:
+	usage = atomic_inc_return(&trans->usage);
+	read_unlock_bh(&rxrpc_transport_lock);
+	goto success;
+
+	/* we found the transport on the second time through the list */
+found_extant_second:
+	usage = atomic_inc_return(&trans->usage);
+	write_unlock_bh(&rxrpc_transport_lock);
+	kfree(candidate);
+	goto success;
+}
+
+/*
+ * find the transport connecting two endpoints
+ */
+struct rxrpc_transport *rxrpc_find_transport(struct rxrpc_local *local,
+					     struct rxrpc_peer *peer)
+{
+	struct rxrpc_transport *trans;
+
+	_enter("{%u.%u.%u.%u+%hu},{%u.%u.%u.%u+%hu},",
+	       NIPQUAD(local->srx.transport.sin.sin_addr),
+	       ntohs(local->srx.transport.sin.sin_port),
+	       NIPQUAD(peer->srx.transport.sin.sin_addr),
+	       ntohs(peer->srx.transport.sin.sin_port));
+
+	/* search the transport list */
+	read_lock_bh(&rxrpc_transport_lock);
+
+	list_for_each_entry(trans, &rxrpc_transports, link) {
+		if (trans->local == local && trans->peer == peer)
+			goto found_extant_transport;
+	}
+
+	read_unlock_bh(&rxrpc_transport_lock);
+	_leave(" = NULL");
+	return NULL;
+
+found_extant_transport:
+	atomic_inc(&trans->usage);
+	read_unlock_bh(&rxrpc_transport_lock);
+	_leave(" = %p", trans);
+	return trans;
+}
+
+/*
+ * release a transport session
+ */
+void rxrpc_put_transport(struct rxrpc_transport *trans)
+{
+	_enter("%p{u=%d}", trans, atomic_read(&trans->usage));
+
+	ASSERTCMP(atomic_read(&trans->usage), >, 0);
+
+	trans->put_time = xtime.tv_sec;
+	if (unlikely(atomic_dec_and_test(&trans->usage)))
+		_debug("zombie");
+		/* let the reaper determine the timeout to avoid a race with
+		 * overextending the timeout if the reaper is running at the
+		 * same time */
+		rxrpc_queue_delayed_work(&rxrpc_transport_reap, 0);
+	_leave("");
+}
+
+/*
+ * clean up a transport session
+ */
+static void rxrpc_cleanup_transport(struct rxrpc_transport *trans)
+{
+	_net("DESTROY TRANS %d", trans->debug_id);
+
+	rxrpc_purge_queue(&trans->error_queue);
+
+	rxrpc_put_local(trans->local);
+	rxrpc_put_peer(trans->peer);
+	kfree(trans);
+}
+
+/*
+ * reap dead transports that have passed their expiry date
+ */
+static void rxrpc_transport_reaper(struct work_struct *work)
+{
+	struct rxrpc_transport *trans, *_p;
+	unsigned long now, earliest, reap_time;
+
+	LIST_HEAD(graveyard);
+
+	_enter("");
+
+	now = xtime.tv_sec;
+	earliest = ULONG_MAX;
+
+	/* extract all the transports that have been dead too long */
+	write_lock_bh(&rxrpc_transport_lock);
+	list_for_each_entry_safe(trans, _p, &rxrpc_transports, link) {
+		_debug("reap TRANS %d { u=%d t=%ld }",
+		       trans->debug_id, atomic_read(&trans->usage),
+		       (long) now - (long) trans->put_time);
+
+		if (likely(atomic_read(&trans->usage) > 0))
+			continue;
+
+		reap_time = trans->put_time + rxrpc_transport_timeout;
+		if (reap_time <= now)
+			list_move_tail(&trans->link, &graveyard);
+		else if (reap_time < earliest)
+			earliest = reap_time;
+	}
+	write_unlock_bh(&rxrpc_transport_lock);
+
+	if (earliest != ULONG_MAX) {
+		_debug("reschedule reaper %ld", (long) earliest - now);
+		ASSERTCMP(earliest, >, now);
+		rxrpc_queue_delayed_work(&rxrpc_transport_reap,
+					 (earliest - now) * HZ);
+	}
+
+	/* then destroy all those pulled out */
+	while (!list_empty(&graveyard)) {
+		trans = list_entry(graveyard.next, struct rxrpc_transport,
+				   link);
+		list_del_init(&trans->link);
+
+		ASSERTCMP(atomic_read(&trans->usage), ==, 0);
+		rxrpc_cleanup_transport(trans);
+	}
+
+	_leave("");
+}
+
+/*
+ * preemptively destroy all the transport session records rather than waiting
+ * for them to time out
+ */
+void __exit rxrpc_destroy_all_transports(void)
+{
+	_enter("");
+
+	rxrpc_transport_timeout = 0;
+	cancel_delayed_work(&rxrpc_transport_reap);
+	rxrpc_queue_delayed_work(&rxrpc_transport_reap, 0);
+
+	_leave("");
+}
diff --git a/net/rxrpc/call.c b/net/rxrpc/call.c
deleted file mode 100644
index d07122b..0000000
--- a/net/rxrpc/call.c
+++ /dev/null
@@ -1,2277 +0,0 @@
-/* call.c: Rx call routines
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <rxrpc/rxrpc.h>
-#include <rxrpc/transport.h>
-#include <rxrpc/peer.h>
-#include <rxrpc/connection.h>
-#include <rxrpc/call.h>
-#include <rxrpc/message.h>
-#include "internal.h"
-
-__RXACCT_DECL(atomic_t rxrpc_call_count);
-__RXACCT_DECL(atomic_t rxrpc_message_count);
-
-LIST_HEAD(rxrpc_calls);
-DECLARE_RWSEM(rxrpc_calls_sem);
-
-unsigned rxrpc_call_rcv_timeout			= HZ/3;
-static unsigned rxrpc_call_acks_timeout		= HZ/3;
-static unsigned rxrpc_call_dfr_ack_timeout	= HZ/20;
-static unsigned short rxrpc_call_max_resend	= HZ/10;
-
-const char *rxrpc_call_states[] = {
-	"COMPLETE",
-	"ERROR",
-	"SRVR_RCV_OPID",
-	"SRVR_RCV_ARGS",
-	"SRVR_GOT_ARGS",
-	"SRVR_SND_REPLY",
-	"SRVR_RCV_FINAL_ACK",
-	"CLNT_SND_ARGS",
-	"CLNT_RCV_REPLY",
-	"CLNT_GOT_REPLY"
-};
-
-const char *rxrpc_call_error_states[] = {
-	"NO_ERROR",
-	"LOCAL_ABORT",
-	"PEER_ABORT",
-	"LOCAL_ERROR",
-	"REMOTE_ERROR"
-};
-
-const char *rxrpc_pkts[] = {
-	"?00",
-	"data", "ack", "busy", "abort", "ackall", "chall", "resp", "debug",
-	"?09", "?10", "?11", "?12", "?13", "?14", "?15"
-};
-
-static const char *rxrpc_acks[] = {
-	"---", "REQ", "DUP", "SEQ", "WIN", "MEM", "PNG", "PNR", "DLY", "IDL",
-	"-?-"
-};
-
-static const char _acktype[] = "NA-";
-
-static void rxrpc_call_receive_packet(struct rxrpc_call *call);
-static void rxrpc_call_receive_data_packet(struct rxrpc_call *call,
-					   struct rxrpc_message *msg);
-static void rxrpc_call_receive_ack_packet(struct rxrpc_call *call,
-					  struct rxrpc_message *msg);
-static void rxrpc_call_definitively_ACK(struct rxrpc_call *call,
-					rxrpc_seq_t higest);
-static void rxrpc_call_resend(struct rxrpc_call *call, rxrpc_seq_t highest);
-static int __rxrpc_call_read_data(struct rxrpc_call *call);
-
-static int rxrpc_call_record_ACK(struct rxrpc_call *call,
-				 struct rxrpc_message *msg,
-				 rxrpc_seq_t seq,
-				 size_t count);
-
-static int rxrpc_call_flush(struct rxrpc_call *call);
-
-#define _state(call) \
-	_debug("[[[ state %s ]]]", rxrpc_call_states[call->app_call_state]);
-
-static void rxrpc_call_default_attn_func(struct rxrpc_call *call)
-{
-	wake_up(&call->waitq);
-}
-
-static void rxrpc_call_default_error_func(struct rxrpc_call *call)
-{
-	wake_up(&call->waitq);
-}
-
-static void rxrpc_call_default_aemap_func(struct rxrpc_call *call)
-{
-	switch (call->app_err_state) {
-	case RXRPC_ESTATE_LOCAL_ABORT:
-		call->app_abort_code = -call->app_errno;
-	case RXRPC_ESTATE_PEER_ABORT:
-		call->app_errno = -ECONNABORTED;
-	default:
-		break;
-	}
-}
-
-static void __rxrpc_call_acks_timeout(unsigned long _call)
-{
-	struct rxrpc_call *call = (struct rxrpc_call *) _call;
-
-	_debug("ACKS TIMEOUT %05lu", jiffies - call->cjif);
-
-	call->flags |= RXRPC_CALL_ACKS_TIMO;
-	rxrpc_krxiod_queue_call(call);
-}
-
-static void __rxrpc_call_rcv_timeout(unsigned long _call)
-{
-	struct rxrpc_call *call = (struct rxrpc_call *) _call;
-
-	_debug("RCV TIMEOUT %05lu", jiffies - call->cjif);
-
-	call->flags |= RXRPC_CALL_RCV_TIMO;
-	rxrpc_krxiod_queue_call(call);
-}
-
-static void __rxrpc_call_ackr_timeout(unsigned long _call)
-{
-	struct rxrpc_call *call = (struct rxrpc_call *) _call;
-
-	_debug("ACKR TIMEOUT %05lu",jiffies - call->cjif);
-
-	call->flags |= RXRPC_CALL_ACKR_TIMO;
-	rxrpc_krxiod_queue_call(call);
-}
-
-/*****************************************************************************/
-/*
- * calculate a timeout based on an RTT value
- */
-static inline unsigned long __rxrpc_rtt_based_timeout(struct rxrpc_call *call,
-						      unsigned long val)
-{
-	unsigned long expiry = call->conn->peer->rtt / (1000000 / HZ);
-
-	expiry += 10;
-	if (expiry < HZ / 25)
-		expiry = HZ / 25;
-	if (expiry > HZ)
-		expiry = HZ;
-
-	_leave(" = %lu jiffies", expiry);
-	return jiffies + expiry;
-} /* end __rxrpc_rtt_based_timeout() */
-
-/*****************************************************************************/
-/*
- * create a new call record
- */
-static inline int __rxrpc_create_call(struct rxrpc_connection *conn,
-				      struct rxrpc_call **_call)
-{
-	struct rxrpc_call *call;
-
-	_enter("%p", conn);
-
-	/* allocate and initialise a call record */
-	call = (struct rxrpc_call *) get_zeroed_page(GFP_KERNEL);
-	if (!call) {
-		_leave(" ENOMEM");
-		return -ENOMEM;
-	}
-
-	atomic_set(&call->usage, 1);
-
-	init_waitqueue_head(&call->waitq);
-	spin_lock_init(&call->lock);
-	INIT_LIST_HEAD(&call->link);
-	INIT_LIST_HEAD(&call->acks_pendq);
-	INIT_LIST_HEAD(&call->rcv_receiveq);
-	INIT_LIST_HEAD(&call->rcv_krxiodq_lk);
-	INIT_LIST_HEAD(&call->app_readyq);
-	INIT_LIST_HEAD(&call->app_unreadyq);
-	INIT_LIST_HEAD(&call->app_link);
-	INIT_LIST_HEAD(&call->app_attn_link);
-
-	init_timer(&call->acks_timeout);
-	call->acks_timeout.data = (unsigned long) call;
-	call->acks_timeout.function = __rxrpc_call_acks_timeout;
-
-	init_timer(&call->rcv_timeout);
-	call->rcv_timeout.data = (unsigned long) call;
-	call->rcv_timeout.function = __rxrpc_call_rcv_timeout;
-
-	init_timer(&call->ackr_dfr_timo);
-	call->ackr_dfr_timo.data = (unsigned long) call;
-	call->ackr_dfr_timo.function = __rxrpc_call_ackr_timeout;
-
-	call->conn = conn;
-	call->ackr_win_bot = 1;
-	call->ackr_win_top = call->ackr_win_bot + RXRPC_CALL_ACK_WINDOW_SIZE - 1;
-	call->ackr_prev_seq = 0;
-	call->app_mark = RXRPC_APP_MARK_EOF;
-	call->app_attn_func = rxrpc_call_default_attn_func;
-	call->app_error_func = rxrpc_call_default_error_func;
-	call->app_aemap_func = rxrpc_call_default_aemap_func;
-	call->app_scr_alloc = call->app_scratch;
-
-	call->cjif = jiffies;
-
-	_leave(" = 0 (%p)", call);
-
-	*_call = call;
-
-	return 0;
-} /* end __rxrpc_create_call() */
-
-/*****************************************************************************/
-/*
- * create a new call record for outgoing calls
- */
-int rxrpc_create_call(struct rxrpc_connection *conn,
-		      rxrpc_call_attn_func_t attn,
-		      rxrpc_call_error_func_t error,
-		      rxrpc_call_aemap_func_t aemap,
-		      struct rxrpc_call **_call)
-{
-	DECLARE_WAITQUEUE(myself, current);
-
-	struct rxrpc_call *call;
-	int ret, cix, loop;
-
-	_enter("%p", conn);
-
-	/* allocate and initialise a call record */
-	ret = __rxrpc_create_call(conn, &call);
-	if (ret < 0) {
-		_leave(" = %d", ret);
-		return ret;
-	}
-
-	call->app_call_state = RXRPC_CSTATE_CLNT_SND_ARGS;
-	if (attn)
-		call->app_attn_func = attn;
-	if (error)
-		call->app_error_func = error;
-	if (aemap)
-		call->app_aemap_func = aemap;
-
-	_state(call);
-
-	spin_lock(&conn->lock);
-	set_current_state(TASK_INTERRUPTIBLE);
-	add_wait_queue(&conn->chanwait, &myself);
-
- try_again:
-	/* try to find an unused channel */
-	for (cix = 0; cix < 4; cix++)
-		if (!conn->channels[cix])
-			goto obtained_chan;
-
-	/* no free channels - wait for one to become available */
-	ret = -EINTR;
-	if (signal_pending(current))
-		goto error_unwait;
-
-	spin_unlock(&conn->lock);
-
-	schedule();
-	set_current_state(TASK_INTERRUPTIBLE);
-
-	spin_lock(&conn->lock);
-	goto try_again;
-
-	/* got a channel - now attach to the connection */
- obtained_chan:
-	remove_wait_queue(&conn->chanwait, &myself);
-	set_current_state(TASK_RUNNING);
-
-	/* concoct a unique call number */
- next_callid:
-	call->call_id = htonl(++conn->call_counter);
-	for (loop = 0; loop < 4; loop++)
-		if (conn->channels[loop] &&
-		    conn->channels[loop]->call_id == call->call_id)
-			goto next_callid;
-
-	rxrpc_get_connection(conn);
-	conn->channels[cix] = call; /* assign _after_ done callid check loop */
-	do_gettimeofday(&conn->atime);
-	call->chan_ix = htonl(cix);
-
-	spin_unlock(&conn->lock);
-
-	down_write(&rxrpc_calls_sem);
-	list_add_tail(&call->call_link, &rxrpc_calls);
-	up_write(&rxrpc_calls_sem);
-
-	__RXACCT(atomic_inc(&rxrpc_call_count));
-	*_call = call;
-
-	_leave(" = 0 (call=%p cix=%u)", call, cix);
-	return 0;
-
- error_unwait:
-	remove_wait_queue(&conn->chanwait, &myself);
-	set_current_state(TASK_RUNNING);
-	spin_unlock(&conn->lock);
-
-	free_page((unsigned long) call);
-	_leave(" = %d", ret);
-	return ret;
-} /* end rxrpc_create_call() */
-
-/*****************************************************************************/
-/*
- * create a new call record for incoming calls
- */
-int rxrpc_incoming_call(struct rxrpc_connection *conn,
-			struct rxrpc_message *msg,
-			struct rxrpc_call **_call)
-{
-	struct rxrpc_call *call;
-	unsigned cix;
-	int ret;
-
-	cix = ntohl(msg->hdr.cid) & RXRPC_CHANNELMASK;
-
-	_enter("%p,%u,%u", conn, ntohl(msg->hdr.callNumber), cix);
-
-	/* allocate and initialise a call record */
-	ret = __rxrpc_create_call(conn, &call);
-	if (ret < 0) {
-		_leave(" = %d", ret);
-		return ret;
-	}
-
-	call->pkt_rcv_count = 1;
-	call->app_call_state = RXRPC_CSTATE_SRVR_RCV_OPID;
-	call->app_mark = sizeof(uint32_t);
-
-	_state(call);
-
-	/* attach to the connection */
-	ret = -EBUSY;
-	call->chan_ix = htonl(cix);
-	call->call_id = msg->hdr.callNumber;
-
-	spin_lock(&conn->lock);
-
-	if (!conn->channels[cix] ||
-	    conn->channels[cix]->app_call_state == RXRPC_CSTATE_COMPLETE ||
-	    conn->channels[cix]->app_call_state == RXRPC_CSTATE_ERROR
-	    ) {
-		conn->channels[cix] = call;
-		rxrpc_get_connection(conn);
-		ret = 0;
-	}
-
-	spin_unlock(&conn->lock);
-
-	if (ret < 0) {
-		free_page((unsigned long) call);
-		call = NULL;
-	}
-
-	if (ret == 0) {
-		down_write(&rxrpc_calls_sem);
-		list_add_tail(&call->call_link, &rxrpc_calls);
-		up_write(&rxrpc_calls_sem);
-		__RXACCT(atomic_inc(&rxrpc_call_count));
-		*_call = call;
-	}
-
-	_leave(" = %d [%p]", ret, call);
-	return ret;
-} /* end rxrpc_incoming_call() */
-
-/*****************************************************************************/
-/*
- * free a call record
- */
-void rxrpc_put_call(struct rxrpc_call *call)
-{
-	struct rxrpc_connection *conn = call->conn;
-	struct rxrpc_message *msg;
-
-	_enter("%p{u=%d}",call,atomic_read(&call->usage));
-
-	/* sanity check */
-	if (atomic_read(&call->usage) <= 0)
-		BUG();
-
-	/* to prevent a race, the decrement and the de-list must be effectively
-	 * atomic */
-	spin_lock(&conn->lock);
-	if (likely(!atomic_dec_and_test(&call->usage))) {
-		spin_unlock(&conn->lock);
-		_leave("");
-		return;
-	}
-
-	if (conn->channels[ntohl(call->chan_ix)] == call)
-		conn->channels[ntohl(call->chan_ix)] = NULL;
-
-	spin_unlock(&conn->lock);
-
-	wake_up(&conn->chanwait);
-
-	rxrpc_put_connection(conn);
-
-	/* clear the timers and dequeue from krxiod */
-	del_timer_sync(&call->acks_timeout);
-	del_timer_sync(&call->rcv_timeout);
-	del_timer_sync(&call->ackr_dfr_timo);
-
-	rxrpc_krxiod_dequeue_call(call);
-
-	/* clean up the contents of the struct */
-	if (call->snd_nextmsg)
-		rxrpc_put_message(call->snd_nextmsg);
-
-	if (call->snd_ping)
-		rxrpc_put_message(call->snd_ping);
-
-	while (!list_empty(&call->acks_pendq)) {
-		msg = list_entry(call->acks_pendq.next,
-				 struct rxrpc_message, link);
-		list_del(&msg->link);
-		rxrpc_put_message(msg);
-	}
-
-	while (!list_empty(&call->rcv_receiveq)) {
-		msg = list_entry(call->rcv_receiveq.next,
-				 struct rxrpc_message, link);
-		list_del(&msg->link);
-		rxrpc_put_message(msg);
-	}
-
-	while (!list_empty(&call->app_readyq)) {
-		msg = list_entry(call->app_readyq.next,
-				 struct rxrpc_message, link);
-		list_del(&msg->link);
-		rxrpc_put_message(msg);
-	}
-
-	while (!list_empty(&call->app_unreadyq)) {
-		msg = list_entry(call->app_unreadyq.next,
-				 struct rxrpc_message, link);
-		list_del(&msg->link);
-		rxrpc_put_message(msg);
-	}
-
-	module_put(call->owner);
-
-	down_write(&rxrpc_calls_sem);
-	list_del(&call->call_link);
-	up_write(&rxrpc_calls_sem);
-
-	__RXACCT(atomic_dec(&rxrpc_call_count));
-	free_page((unsigned long) call);
-
-	_leave(" [destroyed]");
-} /* end rxrpc_put_call() */
-
-/*****************************************************************************/
-/*
- * actually generate a normal ACK
- */
-static inline int __rxrpc_call_gen_normal_ACK(struct rxrpc_call *call,
-					      rxrpc_seq_t seq)
-{
-	struct rxrpc_message *msg;
-	struct kvec diov[3];
-	__be32 aux[4];
-	int delta, ret;
-
-	/* ACKs default to DELAY */
-	if (!call->ackr.reason)
-		call->ackr.reason = RXRPC_ACK_DELAY;
-
-	_proto("Rx %05lu Sending ACK { m=%hu f=#%u p=#%u s=%%%u r=%s n=%u }",
-	       jiffies - call->cjif,
-	       ntohs(call->ackr.maxSkew),
-	       ntohl(call->ackr.firstPacket),
-	       ntohl(call->ackr.previousPacket),
-	       ntohl(call->ackr.serial),
-	       rxrpc_acks[call->ackr.reason],
-	       call->ackr.nAcks);
-
-	aux[0] = htonl(call->conn->peer->if_mtu);	/* interface MTU */
-	aux[1] = htonl(1444);				/* max MTU */
-	aux[2] = htonl(16);				/* rwind */
-	aux[3] = htonl(4);				/* max packets */
-
-	diov[0].iov_len  = sizeof(struct rxrpc_ackpacket);
-	diov[0].iov_base = &call->ackr;
-	diov[1].iov_len  = call->ackr_pend_cnt + 3;
-	diov[1].iov_base = call->ackr_array;
-	diov[2].iov_len  = sizeof(aux);
-	diov[2].iov_base = &aux;
-
-	/* build and send the message */
-	ret = rxrpc_conn_newmsg(call->conn,call, RXRPC_PACKET_TYPE_ACK,
-				3, diov, GFP_KERNEL, &msg);
-	if (ret < 0)
-		goto out;
-
-	msg->seq = seq;
-	msg->hdr.seq = htonl(seq);
-	msg->hdr.flags |= RXRPC_SLOW_START_OK;
-
-	ret = rxrpc_conn_sendmsg(call->conn, msg);
-	rxrpc_put_message(msg);
-	if (ret < 0)
-		goto out;
-	call->pkt_snd_count++;
-
-	/* count how many actual ACKs there were at the front */
-	for (delta = 0; delta < call->ackr_pend_cnt; delta++)
-		if (call->ackr_array[delta] != RXRPC_ACK_TYPE_ACK)
-			break;
-
-	call->ackr_pend_cnt -= delta; /* all ACK'd to this point */
-
-	/* crank the ACK window around */
-	if (delta == 0) {
-		/* un-ACK'd window */
-	}
-	else if (delta < RXRPC_CALL_ACK_WINDOW_SIZE) {
-		/* partially ACK'd window
-		 * - shuffle down to avoid losing out-of-sequence packets
-		 */
-		call->ackr_win_bot += delta;
-		call->ackr_win_top += delta;
-
-		memmove(&call->ackr_array[0],
-			&call->ackr_array[delta],
-			call->ackr_pend_cnt);
-
-		memset(&call->ackr_array[call->ackr_pend_cnt],
-		       RXRPC_ACK_TYPE_NACK,
-		       sizeof(call->ackr_array) - call->ackr_pend_cnt);
-	}
-	else {
-		/* fully ACK'd window
-		 * - just clear the whole thing
-		 */
-		memset(&call->ackr_array,
-		       RXRPC_ACK_TYPE_NACK,
-		       sizeof(call->ackr_array));
-	}
-
-	/* clear this ACK */
-	memset(&call->ackr, 0, sizeof(call->ackr));
-
- out:
-	if (!call->app_call_state)
-		printk("___ STATE 0 ___\n");
-	return ret;
-} /* end __rxrpc_call_gen_normal_ACK() */
-
-/*****************************************************************************/
-/*
- * note the reception of a packet in the call's ACK records and generate an
- * appropriate ACK packet if necessary
- * - returns 0 if packet should be processed, 1 if packet should be ignored
- *   and -ve on an error
- */
-static int rxrpc_call_generate_ACK(struct rxrpc_call *call,
-				   struct rxrpc_header *hdr,
-				   struct rxrpc_ackpacket *ack)
-{
-	struct rxrpc_message *msg;
-	rxrpc_seq_t seq;
-	unsigned offset;
-	int ret = 0, err;
-	u8 special_ACK, do_ACK, force;
-
-	_enter("%p,%p { seq=%d tp=%d fl=%02x }",
-	       call, hdr, ntohl(hdr->seq), hdr->type, hdr->flags);
-
-	seq = ntohl(hdr->seq);
-	offset = seq - call->ackr_win_bot;
-	do_ACK = RXRPC_ACK_DELAY;
-	special_ACK = 0;
-	force = (seq == 1);
-
-	if (call->ackr_high_seq < seq)
-		call->ackr_high_seq = seq;
-
-	/* deal with generation of obvious special ACKs first */
-	if (ack && ack->reason == RXRPC_ACK_PING) {
-		special_ACK = RXRPC_ACK_PING_RESPONSE;
-		ret = 1;
-		goto gen_ACK;
-	}
-
-	if (seq < call->ackr_win_bot) {
-		special_ACK = RXRPC_ACK_DUPLICATE;
-		ret = 1;
-		goto gen_ACK;
-	}
-
-	if (seq >= call->ackr_win_top) {
-		special_ACK = RXRPC_ACK_EXCEEDS_WINDOW;
-		ret = 1;
-		goto gen_ACK;
-	}
-
-	if (call->ackr_array[offset] != RXRPC_ACK_TYPE_NACK) {
-		special_ACK = RXRPC_ACK_DUPLICATE;
-		ret = 1;
-		goto gen_ACK;
-	}
-
-	/* okay... it's a normal data packet inside the ACK window */
-	call->ackr_array[offset] = RXRPC_ACK_TYPE_ACK;
-
-	if (offset < call->ackr_pend_cnt) {
-	}
-	else if (offset > call->ackr_pend_cnt) {
-		do_ACK = RXRPC_ACK_OUT_OF_SEQUENCE;
-		call->ackr_pend_cnt = offset;
-		goto gen_ACK;
-	}
-
-	if (hdr->flags & RXRPC_REQUEST_ACK) {
-		do_ACK = RXRPC_ACK_REQUESTED;
-	}
-
-	/* generate an ACK on the final packet of a reply just received */
-	if (hdr->flags & RXRPC_LAST_PACKET) {
-		if (call->conn->out_clientflag)
-			force = 1;
-	}
-	else if (!(hdr->flags & RXRPC_MORE_PACKETS)) {
-		do_ACK = RXRPC_ACK_REQUESTED;
-	}
-
-	/* re-ACK packets previously received out-of-order */
-	for (offset++; offset < RXRPC_CALL_ACK_WINDOW_SIZE; offset++)
-		if (call->ackr_array[offset] != RXRPC_ACK_TYPE_ACK)
-			break;
-
-	call->ackr_pend_cnt = offset;
-
-	/* generate an ACK if we fill up the window */
-	if (call->ackr_pend_cnt >= RXRPC_CALL_ACK_WINDOW_SIZE)
-		force = 1;
-
- gen_ACK:
-	_debug("%05lu ACKs pend=%u norm=%s special=%s%s",
-	       jiffies - call->cjif,
-	       call->ackr_pend_cnt,
-	       rxrpc_acks[do_ACK],
-	       rxrpc_acks[special_ACK],
-	       force ? " immediate" :
-	       do_ACK == RXRPC_ACK_REQUESTED ? " merge-req" :
-	       hdr->flags & RXRPC_LAST_PACKET ? " finalise" :
-	       " defer"
-	       );
-
-	/* send any pending normal ACKs if need be */
-	if (call->ackr_pend_cnt > 0) {
-		/* fill out the appropriate form */
-		call->ackr.bufferSpace	= htons(RXRPC_CALL_ACK_WINDOW_SIZE);
-		call->ackr.maxSkew	= htons(min(call->ackr_high_seq - seq,
-						    65535U));
-		call->ackr.firstPacket	= htonl(call->ackr_win_bot);
-		call->ackr.previousPacket = call->ackr_prev_seq;
-		call->ackr.serial	= hdr->serial;
-		call->ackr.nAcks	= call->ackr_pend_cnt;
-
-		if (do_ACK == RXRPC_ACK_REQUESTED)
-			call->ackr.reason = do_ACK;
-
-		/* generate the ACK immediately if necessary */
-		if (special_ACK || force) {
-			err = __rxrpc_call_gen_normal_ACK(
-				call, do_ACK == RXRPC_ACK_DELAY ? 0 : seq);
-			if (err < 0) {
-				ret = err;
-				goto out;
-			}
-		}
-	}
-
-	if (call->ackr.reason == RXRPC_ACK_REQUESTED)
-		call->ackr_dfr_seq = seq;
-
-	/* start the ACK timer if not running if there are any pending deferred
-	 * ACKs */
-	if (call->ackr_pend_cnt > 0 &&
-	    call->ackr.reason != RXRPC_ACK_REQUESTED &&
-	    !timer_pending(&call->ackr_dfr_timo)
-	    ) {
-		unsigned long timo;
-
-		timo = rxrpc_call_dfr_ack_timeout + jiffies;
-
-		_debug("START ACKR TIMER for cj=%lu", timo - call->cjif);
-
-		spin_lock(&call->lock);
-		mod_timer(&call->ackr_dfr_timo, timo);
-		spin_unlock(&call->lock);
-	}
-	else if ((call->ackr_pend_cnt == 0 ||
-		  call->ackr.reason == RXRPC_ACK_REQUESTED) &&
-		 timer_pending(&call->ackr_dfr_timo)
-		 ) {
-		/* stop timer if no pending ACKs */
-		_debug("CLEAR ACKR TIMER");
-		del_timer_sync(&call->ackr_dfr_timo);
-	}
-
-	/* send a special ACK if one is required */
-	if (special_ACK) {
-		struct rxrpc_ackpacket ack;
-		struct kvec diov[2];
-		uint8_t acks[1] = { RXRPC_ACK_TYPE_ACK };
-
-		/* fill out the appropriate form */
-		ack.bufferSpace	= htons(RXRPC_CALL_ACK_WINDOW_SIZE);
-		ack.maxSkew	= htons(min(call->ackr_high_seq - seq,
-					    65535U));
-		ack.firstPacket	= htonl(call->ackr_win_bot);
-		ack.previousPacket = call->ackr_prev_seq;
-		ack.serial	= hdr->serial;
-		ack.reason	= special_ACK;
-		ack.nAcks	= 0;
-
-		_proto("Rx Sending s-ACK"
-		       " { m=%hu f=#%u p=#%u s=%%%u r=%s n=%u }",
-		       ntohs(ack.maxSkew),
-		       ntohl(ack.firstPacket),
-		       ntohl(ack.previousPacket),
-		       ntohl(ack.serial),
-		       rxrpc_acks[ack.reason],
-		       ack.nAcks);
-
-		diov[0].iov_len  = sizeof(struct rxrpc_ackpacket);
-		diov[0].iov_base = &ack;
-		diov[1].iov_len  = sizeof(acks);
-		diov[1].iov_base = acks;
-
-		/* build and send the message */
-		err = rxrpc_conn_newmsg(call->conn,call, RXRPC_PACKET_TYPE_ACK,
-					hdr->seq ? 2 : 1, diov,
-					GFP_KERNEL,
-					&msg);
-		if (err < 0) {
-			ret = err;
-			goto out;
-		}
-
-		msg->seq = seq;
-		msg->hdr.seq = htonl(seq);
-		msg->hdr.flags |= RXRPC_SLOW_START_OK;
-
-		err = rxrpc_conn_sendmsg(call->conn, msg);
-		rxrpc_put_message(msg);
-		if (err < 0) {
-			ret = err;
-			goto out;
-		}
-		call->pkt_snd_count++;
-	}
-
- out:
-	if (hdr->seq)
-		call->ackr_prev_seq = hdr->seq;
-
-	_leave(" = %d", ret);
-	return ret;
-} /* end rxrpc_call_generate_ACK() */
-
-/*****************************************************************************/
-/*
- * handle work to be done on a call
- * - includes packet reception and timeout processing
- */
-void rxrpc_call_do_stuff(struct rxrpc_call *call)
-{
-	_enter("%p{flags=%lx}", call, call->flags);
-
-	/* handle packet reception */
-	if (call->flags & RXRPC_CALL_RCV_PKT) {
-		_debug("- receive packet");
-		call->flags &= ~RXRPC_CALL_RCV_PKT;
-		rxrpc_call_receive_packet(call);
-	}
-
-	/* handle overdue ACKs */
-	if (call->flags & RXRPC_CALL_ACKS_TIMO) {
-		_debug("- overdue ACK timeout");
-		call->flags &= ~RXRPC_CALL_ACKS_TIMO;
-		rxrpc_call_resend(call, call->snd_seq_count);
-	}
-
-	/* handle lack of reception */
-	if (call->flags & RXRPC_CALL_RCV_TIMO) {
-		_debug("- reception timeout");
-		call->flags &= ~RXRPC_CALL_RCV_TIMO;
-		rxrpc_call_abort(call, -EIO);
-	}
-
-	/* handle deferred ACKs */
-	if (call->flags & RXRPC_CALL_ACKR_TIMO ||
-	    (call->ackr.nAcks > 0 && call->ackr.reason == RXRPC_ACK_REQUESTED)
-	    ) {
-		_debug("- deferred ACK timeout: cj=%05lu r=%s n=%u",
-		       jiffies - call->cjif,
-		       rxrpc_acks[call->ackr.reason],
-		       call->ackr.nAcks);
-
-		call->flags &= ~RXRPC_CALL_ACKR_TIMO;
-
-		if (call->ackr.nAcks > 0 &&
-		    call->app_call_state != RXRPC_CSTATE_ERROR) {
-			/* generate ACK */
-			__rxrpc_call_gen_normal_ACK(call, call->ackr_dfr_seq);
-			call->ackr_dfr_seq = 0;
-		}
-	}
-
-	_leave("");
-
-} /* end rxrpc_call_do_stuff() */
-
-/*****************************************************************************/
-/*
- * send an abort message at call or connection level
- * - must be called with call->lock held
- * - the supplied error code is sent as the packet data
- */
-static int __rxrpc_call_abort(struct rxrpc_call *call, int errno)
-{
-	struct rxrpc_connection *conn = call->conn;
-	struct rxrpc_message *msg;
-	struct kvec diov[1];
-	int ret;
-	__be32 _error;
-
-	_enter("%p{%08x},%p{%d},%d",
-	       conn, ntohl(conn->conn_id), call, ntohl(call->call_id), errno);
-
-	/* if this call is already aborted, then just wake up any waiters */
-	if (call->app_call_state == RXRPC_CSTATE_ERROR) {
-		spin_unlock(&call->lock);
-		call->app_error_func(call);
-		_leave(" = 0");
-		return 0;
-	}
-
-	rxrpc_get_call(call);
-
-	/* change the state _with_ the lock still held */
-	call->app_call_state	= RXRPC_CSTATE_ERROR;
-	call->app_err_state	= RXRPC_ESTATE_LOCAL_ABORT;
-	call->app_errno		= errno;
-	call->app_mark		= RXRPC_APP_MARK_EOF;
-	call->app_read_buf	= NULL;
-	call->app_async_read	= 0;
-
-	_state(call);
-
-	/* ask the app to translate the error code */
-	call->app_aemap_func(call);
-
-	spin_unlock(&call->lock);
-
-	/* flush any outstanding ACKs */
-	del_timer_sync(&call->acks_timeout);
-	del_timer_sync(&call->rcv_timeout);
-	del_timer_sync(&call->ackr_dfr_timo);
-
-	if (rxrpc_call_is_ack_pending(call))
-		__rxrpc_call_gen_normal_ACK(call, 0);
-
-	/* send the abort packet only if we actually traded some other
-	 * packets */
-	ret = 0;
-	if (call->pkt_snd_count || call->pkt_rcv_count) {
-		/* actually send the abort */
-		_proto("Rx Sending Call ABORT { data=%d }",
-		       call->app_abort_code);
-
-		_error = htonl(call->app_abort_code);
-
-		diov[0].iov_len  = sizeof(_error);
-		diov[0].iov_base = &_error;
-
-		ret = rxrpc_conn_newmsg(conn, call, RXRPC_PACKET_TYPE_ABORT,
-					1, diov, GFP_KERNEL, &msg);
-		if (ret == 0) {
-			ret = rxrpc_conn_sendmsg(conn, msg);
-			rxrpc_put_message(msg);
-		}
-	}
-
-	/* tell the app layer to let go */
-	call->app_error_func(call);
-
-	rxrpc_put_call(call);
-
-	_leave(" = %d", ret);
-	return ret;
-} /* end __rxrpc_call_abort() */
-
-/*****************************************************************************/
-/*
- * send an abort message at call or connection level
- * - the supplied error code is sent as the packet data
- */
-int rxrpc_call_abort(struct rxrpc_call *call, int error)
-{
-	spin_lock(&call->lock);
-
-	return __rxrpc_call_abort(call, error);
-
-} /* end rxrpc_call_abort() */
-
-/*****************************************************************************/
-/*
- * process packets waiting for this call
- */
-static void rxrpc_call_receive_packet(struct rxrpc_call *call)
-{
-	struct rxrpc_message *msg;
-	struct list_head *_p;
-
-	_enter("%p", call);
-
-	rxrpc_get_call(call); /* must not go away too soon if aborted by
-			       * app-layer */
-
-	while (!list_empty(&call->rcv_receiveq)) {
-		/* try to get next packet */
-		_p = NULL;
-		spin_lock(&call->lock);
-		if (!list_empty(&call->rcv_receiveq)) {
-			_p = call->rcv_receiveq.next;
-			list_del_init(_p);
-		}
-		spin_unlock(&call->lock);
-
-		if (!_p)
-			break;
-
-		msg = list_entry(_p, struct rxrpc_message, link);
-
-		_proto("Rx %05lu Received %s packet (%%%u,#%u,%c%c%c%c%c)",
-		       jiffies - call->cjif,
-		       rxrpc_pkts[msg->hdr.type],
-		       ntohl(msg->hdr.serial),
-		       msg->seq,
-		       msg->hdr.flags & RXRPC_JUMBO_PACKET	? 'j' : '-',
-		       msg->hdr.flags & RXRPC_MORE_PACKETS	? 'm' : '-',
-		       msg->hdr.flags & RXRPC_LAST_PACKET	? 'l' : '-',
-		       msg->hdr.flags & RXRPC_REQUEST_ACK	? 'r' : '-',
-		       msg->hdr.flags & RXRPC_CLIENT_INITIATED	? 'C' : 'S'
-		       );
-
-		switch (msg->hdr.type) {
-			/* deal with data packets */
-		case RXRPC_PACKET_TYPE_DATA:
-			/* ACK the packet if necessary */
-			switch (rxrpc_call_generate_ACK(call, &msg->hdr,
-							NULL)) {
-			case 0: /* useful packet */
-				rxrpc_call_receive_data_packet(call, msg);
-				break;
-			case 1: /* duplicate or out-of-window packet */
-				break;
-			default:
-				rxrpc_put_message(msg);
-				goto out;
-			}
-			break;
-
-			/* deal with ACK packets */
-		case RXRPC_PACKET_TYPE_ACK:
-			rxrpc_call_receive_ack_packet(call, msg);
-			break;
-
-			/* deal with abort packets */
-		case RXRPC_PACKET_TYPE_ABORT: {
-			__be32 _dbuf, *dp;
-
-			dp = skb_header_pointer(msg->pkt, msg->offset,
-						sizeof(_dbuf), &_dbuf);
-			if (dp == NULL)
-				printk("Rx Received short ABORT packet\n");
-
-			_proto("Rx Received Call ABORT { data=%d }",
-			       (dp ? ntohl(*dp) : 0));
-
-			spin_lock(&call->lock);
-			call->app_call_state	= RXRPC_CSTATE_ERROR;
-			call->app_err_state	= RXRPC_ESTATE_PEER_ABORT;
-			call->app_abort_code	= (dp ? ntohl(*dp) : 0);
-			call->app_errno		= -ECONNABORTED;
-			call->app_mark		= RXRPC_APP_MARK_EOF;
-			call->app_read_buf	= NULL;
-			call->app_async_read	= 0;
-
-			/* ask the app to translate the error code */
-			call->app_aemap_func(call);
-			_state(call);
-			spin_unlock(&call->lock);
-			call->app_error_func(call);
-			break;
-		}
-		default:
-			/* deal with other packet types */
-			_proto("Rx Unsupported packet type %u (#%u)",
-			       msg->hdr.type, msg->seq);
-			break;
-		}
-
-		rxrpc_put_message(msg);
-	}
-
- out:
-	rxrpc_put_call(call);
-	_leave("");
-} /* end rxrpc_call_receive_packet() */
-
-/*****************************************************************************/
-/*
- * process next data packet
- * - as the next data packet arrives:
- *   - it is queued on app_readyq _if_ it is the next one expected
- *     (app_ready_seq+1)
- *   - it is queued on app_unreadyq _if_ it is not the next one expected
- *   - if a packet placed on app_readyq completely fills a hole leading up to
- *     the first packet on app_unreadyq, then packets now in sequence are
- *     tranferred to app_readyq
- * - the application layer can only see packets on app_readyq
- *   (app_ready_qty bytes)
- * - the application layer is prodded every time a new packet arrives
- */
-static void rxrpc_call_receive_data_packet(struct rxrpc_call *call,
-					   struct rxrpc_message *msg)
-{
-	const struct rxrpc_operation *optbl, *op;
-	struct rxrpc_message *pmsg;
-	struct list_head *_p;
-	int ret, lo, hi, rmtimo;
-	__be32 opid;
-
-	_enter("%p{%u},%p{%u}", call, ntohl(call->call_id), msg, msg->seq);
-
-	rxrpc_get_message(msg);
-
-	/* add to the unready queue if we'd have to create a hole in the ready
-	 * queue otherwise */
-	if (msg->seq != call->app_ready_seq + 1) {
-		_debug("Call add packet %d to unreadyq", msg->seq);
-
-		/* insert in seq order */
-		list_for_each(_p, &call->app_unreadyq) {
-			pmsg = list_entry(_p, struct rxrpc_message, link);
-			if (pmsg->seq > msg->seq)
-				break;
-		}
-
-		list_add_tail(&msg->link, _p);
-
-		_leave(" [unreadyq]");
-		return;
-	}
-
-	/* next in sequence - simply append into the call's ready queue */
-	_debug("Call add packet %d to readyq (+%Zd => %Zd bytes)",
-	       msg->seq, msg->dsize, call->app_ready_qty);
-
-	spin_lock(&call->lock);
-	call->app_ready_seq = msg->seq;
-	call->app_ready_qty += msg->dsize;
-	list_add_tail(&msg->link, &call->app_readyq);
-
-	/* move unready packets to the readyq if we got rid of a hole */
-	while (!list_empty(&call->app_unreadyq)) {
-		pmsg = list_entry(call->app_unreadyq.next,
-				  struct rxrpc_message, link);
-
-		if (pmsg->seq != call->app_ready_seq + 1)
-			break;
-
-		/* next in sequence - just move list-to-list */
-		_debug("Call transfer packet %d to readyq (+%Zd => %Zd bytes)",
-		       pmsg->seq, pmsg->dsize, call->app_ready_qty);
-
-		call->app_ready_seq = pmsg->seq;
-		call->app_ready_qty += pmsg->dsize;
-		list_move_tail(&pmsg->link, &call->app_readyq);
-	}
-
-	/* see if we've got the last packet yet */
-	if (!list_empty(&call->app_readyq)) {
-		pmsg = list_entry(call->app_readyq.prev,
-				  struct rxrpc_message, link);
-		if (pmsg->hdr.flags & RXRPC_LAST_PACKET) {
-			call->app_last_rcv = 1;
-			_debug("Last packet on readyq");
-		}
-	}
-
-	switch (call->app_call_state) {
-		/* do nothing if call already aborted */
-	case RXRPC_CSTATE_ERROR:
-		spin_unlock(&call->lock);
-		_leave(" [error]");
-		return;
-
-		/* extract the operation ID from an incoming call if that's not
-		 * yet been done */
-	case RXRPC_CSTATE_SRVR_RCV_OPID:
-		spin_unlock(&call->lock);
-
-		/* handle as yet insufficient data for the operation ID */
-		if (call->app_ready_qty < 4) {
-			if (call->app_last_rcv)
-				/* trouble - last packet seen */
-				rxrpc_call_abort(call, -EINVAL);
-
-			_leave("");
-			return;
-		}
-
-		/* pull the operation ID out of the buffer */
-		ret = rxrpc_call_read_data(call, &opid, sizeof(opid), 0);
-		if (ret < 0) {
-			printk("Unexpected error from read-data: %d\n", ret);
-			if (call->app_call_state != RXRPC_CSTATE_ERROR)
-				rxrpc_call_abort(call, ret);
-			_leave("");
-			return;
-		}
-		call->app_opcode = ntohl(opid);
-
-		/* locate the operation in the available ops table */
-		optbl = call->conn->service->ops_begin;
-		lo = 0;
-		hi = call->conn->service->ops_end - optbl;
-
-		while (lo < hi) {
-			int mid = (hi + lo) / 2;
-			op = &optbl[mid];
-			if (call->app_opcode == op->id)
-				goto found_op;
-			if (call->app_opcode > op->id)
-				lo = mid + 1;
-			else
-				hi = mid;
-		}
-
-		/* search failed */
-		kproto("Rx Client requested operation %d from %s service",
-		       call->app_opcode, call->conn->service->name);
-		rxrpc_call_abort(call, -EINVAL);
-		_leave(" [inval]");
-		return;
-
-	found_op:
-		_proto("Rx Client requested operation %s from %s service",
-		       op->name, call->conn->service->name);
-
-		/* we're now waiting for the argument block (unless the call
-		 * was aborted) */
-		spin_lock(&call->lock);
-		if (call->app_call_state == RXRPC_CSTATE_SRVR_RCV_OPID ||
-		    call->app_call_state == RXRPC_CSTATE_SRVR_SND_REPLY) {
-			if (!call->app_last_rcv)
-				call->app_call_state =
-					RXRPC_CSTATE_SRVR_RCV_ARGS;
-			else if (call->app_ready_qty > 0)
-				call->app_call_state =
-					RXRPC_CSTATE_SRVR_GOT_ARGS;
-			else
-				call->app_call_state =
-					RXRPC_CSTATE_SRVR_SND_REPLY;
-			call->app_mark = op->asize;
-			call->app_user = op->user;
-		}
-		spin_unlock(&call->lock);
-
-		_state(call);
-		break;
-
-	case RXRPC_CSTATE_SRVR_RCV_ARGS:
-		/* change state if just received last packet of arg block */
-		if (call->app_last_rcv)
-			call->app_call_state = RXRPC_CSTATE_SRVR_GOT_ARGS;
-		spin_unlock(&call->lock);
-
-		_state(call);
-		break;
-
-	case RXRPC_CSTATE_CLNT_RCV_REPLY:
-		/* change state if just received last packet of reply block */
-		rmtimo = 0;
-		if (call->app_last_rcv) {
-			call->app_call_state = RXRPC_CSTATE_CLNT_GOT_REPLY;
-			rmtimo = 1;
-		}
-		spin_unlock(&call->lock);
-
-		if (rmtimo) {
-			del_timer_sync(&call->acks_timeout);
-			del_timer_sync(&call->rcv_timeout);
-			del_timer_sync(&call->ackr_dfr_timo);
-		}
-
-		_state(call);
-		break;
-
-	default:
-		/* deal with data reception in an unexpected state */
-		printk("Unexpected state [[[ %u ]]]\n", call->app_call_state);
-		__rxrpc_call_abort(call, -EBADMSG);
-		_leave("");
-		return;
-	}
-
-	if (call->app_call_state == RXRPC_CSTATE_CLNT_RCV_REPLY &&
-	    call->app_last_rcv)
-		BUG();
-
-	/* otherwise just invoke the data function whenever we can satisfy its desire for more
-	 * data
-	 */
-	_proto("Rx Received Op Data: st=%u qty=%Zu mk=%Zu%s",
-	       call->app_call_state, call->app_ready_qty, call->app_mark,
-	       call->app_last_rcv ? " last-rcvd" : "");
-
-	spin_lock(&call->lock);
-
-	ret = __rxrpc_call_read_data(call);
-	switch (ret) {
-	case 0:
-		spin_unlock(&call->lock);
-		call->app_attn_func(call);
-		break;
-	case -EAGAIN:
-		spin_unlock(&call->lock);
-		break;
-	case -ECONNABORTED:
-		spin_unlock(&call->lock);
-		break;
-	default:
-		__rxrpc_call_abort(call, ret);
-		break;
-	}
-
-	_state(call);
-
-	_leave("");
-
-} /* end rxrpc_call_receive_data_packet() */
-
-/*****************************************************************************/
-/*
- * received an ACK packet
- */
-static void rxrpc_call_receive_ack_packet(struct rxrpc_call *call,
-					  struct rxrpc_message *msg)
-{
-	struct rxrpc_ackpacket _ack, *ap;
-	rxrpc_serial_net_t serial;
-	rxrpc_seq_t seq;
-	int ret;
-
-	_enter("%p{%u},%p{%u}", call, ntohl(call->call_id), msg, msg->seq);
-
-	/* extract the basic ACK record */
-	ap = skb_header_pointer(msg->pkt, msg->offset, sizeof(_ack), &_ack);
-	if (ap == NULL) {
-		printk("Rx Received short ACK packet\n");
-		return;
-	}
-	msg->offset += sizeof(_ack);
-
-	serial = ap->serial;
-	seq = ntohl(ap->firstPacket);
-
-	_proto("Rx Received ACK %%%d { b=%hu m=%hu f=%u p=%u s=%u r=%s n=%u }",
-	       ntohl(msg->hdr.serial),
-	       ntohs(ap->bufferSpace),
-	       ntohs(ap->maxSkew),
-	       seq,
-	       ntohl(ap->previousPacket),
-	       ntohl(serial),
-	       rxrpc_acks[ap->reason],
-	       call->ackr.nAcks
-	       );
-
-	/* check the other side isn't ACK'ing a sequence number I haven't sent
-	 * yet */
-	if (ap->nAcks > 0 &&
-	    (seq > call->snd_seq_count ||
-	     seq + ap->nAcks - 1 > call->snd_seq_count)) {
-		printk("Received ACK (#%u-#%u) for unsent packet\n",
-		       seq, seq + ap->nAcks - 1);
-		rxrpc_call_abort(call, -EINVAL);
-		_leave("");
-		return;
-	}
-
-	/* deal with RTT calculation */
-	if (serial) {
-		struct rxrpc_message *rttmsg;
-
-		/* find the prompting packet */
-		spin_lock(&call->lock);
-		if (call->snd_ping && call->snd_ping->hdr.serial == serial) {
-			/* it was a ping packet */
-			rttmsg = call->snd_ping;
-			call->snd_ping = NULL;
-			spin_unlock(&call->lock);
-
-			if (rttmsg) {
-				rttmsg->rttdone = 1;
-				rxrpc_peer_calculate_rtt(call->conn->peer,
-							 rttmsg, msg);
-				rxrpc_put_message(rttmsg);
-			}
-		}
-		else {
-			struct list_head *_p;
-
-			/* it ought to be a data packet - look in the pending
-			 * ACK list */
-			list_for_each(_p, &call->acks_pendq) {
-				rttmsg = list_entry(_p, struct rxrpc_message,
-						    link);
-				if (rttmsg->hdr.serial == serial) {
-					if (rttmsg->rttdone)
-						/* never do RTT twice without
-						 * resending */
-						break;
-
-					rttmsg->rttdone = 1;
-					rxrpc_peer_calculate_rtt(
-						call->conn->peer, rttmsg, msg);
-					break;
-				}
-			}
-			spin_unlock(&call->lock);
-		}
-	}
-
-	switch (ap->reason) {
-		/* deal with negative/positive acknowledgement of data
-		 * packets */
-	case RXRPC_ACK_REQUESTED:
-	case RXRPC_ACK_DELAY:
-	case RXRPC_ACK_IDLE:
-		rxrpc_call_definitively_ACK(call, seq - 1);
-
-	case RXRPC_ACK_DUPLICATE:
-	case RXRPC_ACK_OUT_OF_SEQUENCE:
-	case RXRPC_ACK_EXCEEDS_WINDOW:
-		call->snd_resend_cnt = 0;
-		ret = rxrpc_call_record_ACK(call, msg, seq, ap->nAcks);
-		if (ret < 0)
-			rxrpc_call_abort(call, ret);
-		break;
-
-		/* respond to ping packets immediately */
-	case RXRPC_ACK_PING:
-		rxrpc_call_generate_ACK(call, &msg->hdr, ap);
-		break;
-
-		/* only record RTT on ping response packets */
-	case RXRPC_ACK_PING_RESPONSE:
-		if (call->snd_ping) {
-			struct rxrpc_message *rttmsg;
-
-			/* only do RTT stuff if the response matches the
-			 * retained ping */
-			rttmsg = NULL;
-			spin_lock(&call->lock);
-			if (call->snd_ping &&
-			    call->snd_ping->hdr.serial == ap->serial) {
-				rttmsg = call->snd_ping;
-				call->snd_ping = NULL;
-			}
-			spin_unlock(&call->lock);
-
-			if (rttmsg) {
-				rttmsg->rttdone = 1;
-				rxrpc_peer_calculate_rtt(call->conn->peer,
-							 rttmsg, msg);
-				rxrpc_put_message(rttmsg);
-			}
-		}
-		break;
-
-	default:
-		printk("Unsupported ACK reason %u\n", ap->reason);
-		break;
-	}
-
-	_leave("");
-} /* end rxrpc_call_receive_ack_packet() */
-
-/*****************************************************************************/
-/*
- * record definitive ACKs for all messages up to and including the one with the
- * 'highest' seq
- */
-static void rxrpc_call_definitively_ACK(struct rxrpc_call *call,
-					rxrpc_seq_t highest)
-{
-	struct rxrpc_message *msg;
-	int now_complete;
-
-	_enter("%p{ads=%u},%u", call, call->acks_dftv_seq, highest);
-
-	while (call->acks_dftv_seq < highest) {
-		call->acks_dftv_seq++;
-
-		_proto("Definitive ACK on packet #%u", call->acks_dftv_seq);
-
-		/* discard those at front of queue until message with highest
-		 * ACK is found */
-		spin_lock(&call->lock);
-		msg = NULL;
-		if (!list_empty(&call->acks_pendq)) {
-			msg = list_entry(call->acks_pendq.next,
-					 struct rxrpc_message, link);
-			list_del_init(&msg->link); /* dequeue */
-			if (msg->state == RXRPC_MSG_SENT)
-				call->acks_pend_cnt--;
-		}
-		spin_unlock(&call->lock);
-
-		/* insanity check */
-		if (!msg)
-			panic("%s(): acks_pendq unexpectedly empty\n",
-			      __FUNCTION__);
-
-		if (msg->seq != call->acks_dftv_seq)
-			panic("%s(): Packet #%u expected at front of acks_pendq"
-			      " (#%u found)\n",
-			      __FUNCTION__, call->acks_dftv_seq, msg->seq);
-
-		/* discard the message */
-		msg->state = RXRPC_MSG_DONE;
-		rxrpc_put_message(msg);
-	}
-
-	/* if all sent packets are definitively ACK'd then prod any sleepers just in case */
-	now_complete = 0;
-	spin_lock(&call->lock);
-	if (call->acks_dftv_seq == call->snd_seq_count) {
-		if (call->app_call_state != RXRPC_CSTATE_COMPLETE) {
-			call->app_call_state = RXRPC_CSTATE_COMPLETE;
-			_state(call);
-			now_complete = 1;
-		}
-	}
-	spin_unlock(&call->lock);
-
-	if (now_complete) {
-		del_timer_sync(&call->acks_timeout);
-		del_timer_sync(&call->rcv_timeout);
-		del_timer_sync(&call->ackr_dfr_timo);
-		call->app_attn_func(call);
-	}
-
-	_leave("");
-} /* end rxrpc_call_definitively_ACK() */
-
-/*****************************************************************************/
-/*
- * record the specified amount of ACKs/NAKs
- */
-static int rxrpc_call_record_ACK(struct rxrpc_call *call,
-				 struct rxrpc_message *msg,
-				 rxrpc_seq_t seq,
-				 size_t count)
-{
-	struct rxrpc_message *dmsg;
-	struct list_head *_p;
-	rxrpc_seq_t highest;
-	unsigned ix;
-	size_t chunk;
-	char resend, now_complete;
-	u8 acks[16];
-
-	_enter("%p{apc=%u ads=%u},%p,%u,%Zu",
-	       call, call->acks_pend_cnt, call->acks_dftv_seq,
-	       msg, seq, count);
-
-	/* handle re-ACK'ing of definitively ACK'd packets (may be out-of-order
-	 * ACKs) */
-	if (seq <= call->acks_dftv_seq) {
-		unsigned delta = call->acks_dftv_seq - seq;
-
-		if (count <= delta) {
-			_leave(" = 0 [all definitively ACK'd]");
-			return 0;
-		}
-
-		seq += delta;
-		count -= delta;
-		msg->offset += delta;
-	}
-
-	highest = seq + count - 1;
-	resend = 0;
-	while (count > 0) {
-		/* extract up to 16 ACK slots at a time */
-		chunk = min(count, sizeof(acks));
-		count -= chunk;
-
-		memset(acks, 2, sizeof(acks));
-
-		if (skb_copy_bits(msg->pkt, msg->offset, &acks, chunk) < 0) {
-			printk("Rx Received short ACK packet\n");
-			_leave(" = -EINVAL");
-			return -EINVAL;
-		}
-		msg->offset += chunk;
-
-		/* check that the ACK set is valid */
-		for (ix = 0; ix < chunk; ix++) {
-			switch (acks[ix]) {
-			case RXRPC_ACK_TYPE_ACK:
-				break;
-			case RXRPC_ACK_TYPE_NACK:
-				resend = 1;
-				break;
-			default:
-				printk("Rx Received unsupported ACK state"
-				       " %u\n", acks[ix]);
-				_leave(" = -EINVAL");
-				return -EINVAL;
-			}
-		}
-
-		_proto("Rx ACK of packets #%u-#%u "
-		       "[%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c] (pend=%u)",
-		       seq, (unsigned) (seq + chunk - 1),
-		       _acktype[acks[0x0]],
-		       _acktype[acks[0x1]],
-		       _acktype[acks[0x2]],
-		       _acktype[acks[0x3]],
-		       _acktype[acks[0x4]],
-		       _acktype[acks[0x5]],
-		       _acktype[acks[0x6]],
-		       _acktype[acks[0x7]],
-		       _acktype[acks[0x8]],
-		       _acktype[acks[0x9]],
-		       _acktype[acks[0xA]],
-		       _acktype[acks[0xB]],
-		       _acktype[acks[0xC]],
-		       _acktype[acks[0xD]],
-		       _acktype[acks[0xE]],
-		       _acktype[acks[0xF]],
-		       call->acks_pend_cnt
-		       );
-
-		/* mark the packets in the ACK queue as being provisionally
-		 * ACK'd */
-		ix = 0;
-		spin_lock(&call->lock);
-
-		/* find the first packet ACK'd/NAK'd here */
-		list_for_each(_p, &call->acks_pendq) {
-			dmsg = list_entry(_p, struct rxrpc_message, link);
-			if (dmsg->seq == seq)
-				goto found_first;
-			_debug("- %u: skipping #%u", ix, dmsg->seq);
-		}
-		goto bad_queue;
-
-	found_first:
-		do {
-			_debug("- %u: processing #%u (%c) apc=%u",
-			       ix, dmsg->seq, _acktype[acks[ix]],
-			       call->acks_pend_cnt);
-
-			if (acks[ix] == RXRPC_ACK_TYPE_ACK) {
-				if (dmsg->state == RXRPC_MSG_SENT)
-					call->acks_pend_cnt--;
-				dmsg->state = RXRPC_MSG_ACKED;
-			}
-			else {
-				if (dmsg->state == RXRPC_MSG_ACKED)
-					call->acks_pend_cnt++;
-				dmsg->state = RXRPC_MSG_SENT;
-			}
-			ix++;
-			seq++;
-
-			_p = dmsg->link.next;
-			dmsg = list_entry(_p, struct rxrpc_message, link);
-		} while(ix < chunk &&
-			_p != &call->acks_pendq &&
-			dmsg->seq == seq);
-
-		if (ix < chunk)
-			goto bad_queue;
-
-		spin_unlock(&call->lock);
-	}
-
-	if (resend)
-		rxrpc_call_resend(call, highest);
-
-	/* if all packets are provisionally ACK'd, then wake up anyone who's
-	 * waiting for that */
-	now_complete = 0;
-	spin_lock(&call->lock);
-	if (call->acks_pend_cnt == 0) {
-		if (call->app_call_state == RXRPC_CSTATE_SRVR_RCV_FINAL_ACK) {
-			call->app_call_state = RXRPC_CSTATE_COMPLETE;
-			_state(call);
-		}
-		now_complete = 1;
-	}
-	spin_unlock(&call->lock);
-
-	if (now_complete) {
-		_debug("- wake up waiters");
-		del_timer_sync(&call->acks_timeout);
-		del_timer_sync(&call->rcv_timeout);
-		del_timer_sync(&call->ackr_dfr_timo);
-		call->app_attn_func(call);
-	}
-
-	_leave(" = 0 (apc=%u)", call->acks_pend_cnt);
-	return 0;
-
- bad_queue:
-	panic("%s(): acks_pendq in bad state (packet #%u absent)\n",
-	      __FUNCTION__, seq);
-
-} /* end rxrpc_call_record_ACK() */
-
-/*****************************************************************************/
-/*
- * transfer data from the ready packet queue to the asynchronous read buffer
- * - since this func is the only one going to look at packets queued on
- *   app_readyq, we don't need a lock to modify or access them, only to modify
- *   the queue pointers
- * - called with call->lock held
- * - the buffer must be in kernel space
- * - returns:
- *	0 if buffer filled
- *	-EAGAIN if buffer not filled and more data to come
- *	-EBADMSG if last packet received and insufficient data left
- *	-ECONNABORTED if the call has in an error state
- */
-static int __rxrpc_call_read_data(struct rxrpc_call *call)
-{
-	struct rxrpc_message *msg;
-	size_t qty;
-	int ret;
-
-	_enter("%p{as=%d buf=%p qty=%Zu/%Zu}",
-	       call,
-	       call->app_async_read, call->app_read_buf,
-	       call->app_ready_qty, call->app_mark);
-
-	/* check the state */
-	switch (call->app_call_state) {
-	case RXRPC_CSTATE_SRVR_RCV_ARGS:
-	case RXRPC_CSTATE_CLNT_RCV_REPLY:
-		if (call->app_last_rcv) {
-			printk("%s(%p,%p,%Zd):"
-			       " Inconsistent call state (%s, last pkt)",
-			       __FUNCTION__,
-			       call, call->app_read_buf, call->app_mark,
-			       rxrpc_call_states[call->app_call_state]);
-			BUG();
-		}
-		break;
-
-	case RXRPC_CSTATE_SRVR_RCV_OPID:
-	case RXRPC_CSTATE_SRVR_GOT_ARGS:
-	case RXRPC_CSTATE_CLNT_GOT_REPLY:
-		break;
-
-	case RXRPC_CSTATE_SRVR_SND_REPLY:
-		if (!call->app_last_rcv) {
-			printk("%s(%p,%p,%Zd):"
-			       " Inconsistent call state (%s, not last pkt)",
-			       __FUNCTION__,
-			       call, call->app_read_buf, call->app_mark,
-			       rxrpc_call_states[call->app_call_state]);
-			BUG();
-		}
-		_debug("Trying to read data from call in SND_REPLY state");
-		break;
-
-	case RXRPC_CSTATE_ERROR:
-		_leave(" = -ECONNABORTED");
-		return -ECONNABORTED;
-
-	default:
-		printk("reading in unexpected state [[[ %u ]]]\n",
-		       call->app_call_state);
-		BUG();
-	}
-
-	/* handle the case of not having an async buffer */
-	if (!call->app_async_read) {
-		if (call->app_mark == RXRPC_APP_MARK_EOF) {
-			ret = call->app_last_rcv ? 0 : -EAGAIN;
-		}
-		else {
-			if (call->app_mark >= call->app_ready_qty) {
-				call->app_mark = RXRPC_APP_MARK_EOF;
-				ret = 0;
-			}
-			else {
-				ret = call->app_last_rcv ? -EBADMSG : -EAGAIN;
-			}
-		}
-
-		_leave(" = %d [no buf]", ret);
-		return 0;
-	}
-
-	while (!list_empty(&call->app_readyq) && call->app_mark > 0) {
-		msg = list_entry(call->app_readyq.next,
-				 struct rxrpc_message, link);
-
-		/* drag as much data as we need out of this packet */
-		qty = min(call->app_mark, msg->dsize);
-
-		_debug("reading %Zu from skb=%p off=%lu",
-		       qty, msg->pkt, msg->offset);
-
-		if (call->app_read_buf)
-			if (skb_copy_bits(msg->pkt, msg->offset,
-					  call->app_read_buf, qty) < 0)
-				panic("%s: Failed to copy data from packet:"
-				      " (%p,%p,%Zd)",
-				      __FUNCTION__,
-				      call, call->app_read_buf, qty);
-
-		/* if that packet is now empty, discard it */
-		call->app_ready_qty -= qty;
-		msg->dsize -= qty;
-
-		if (msg->dsize == 0) {
-			list_del_init(&msg->link);
-			rxrpc_put_message(msg);
-		}
-		else {
-			msg->offset += qty;
-		}
-
-		call->app_mark -= qty;
-		if (call->app_read_buf)
-			call->app_read_buf += qty;
-	}
-
-	if (call->app_mark == 0) {
-		call->app_async_read = 0;
-		call->app_mark = RXRPC_APP_MARK_EOF;
-		call->app_read_buf = NULL;
-
-		/* adjust the state if used up all packets */
-		if (list_empty(&call->app_readyq) && call->app_last_rcv) {
-			switch (call->app_call_state) {
-			case RXRPC_CSTATE_SRVR_RCV_OPID:
-				call->app_call_state = RXRPC_CSTATE_SRVR_SND_REPLY;
-				call->app_mark = RXRPC_APP_MARK_EOF;
-				_state(call);
-				del_timer_sync(&call->rcv_timeout);
-				break;
-			case RXRPC_CSTATE_SRVR_GOT_ARGS:
-				call->app_call_state = RXRPC_CSTATE_SRVR_SND_REPLY;
-				_state(call);
-				del_timer_sync(&call->rcv_timeout);
-				break;
-			default:
-				call->app_call_state = RXRPC_CSTATE_COMPLETE;
-				_state(call);
-				del_timer_sync(&call->acks_timeout);
-				del_timer_sync(&call->ackr_dfr_timo);
-				del_timer_sync(&call->rcv_timeout);
-				break;
-			}
-		}
-
-		_leave(" = 0");
-		return 0;
-	}
-
-	if (call->app_last_rcv) {
-		_debug("Insufficient data (%Zu/%Zu)",
-		       call->app_ready_qty, call->app_mark);
-		call->app_async_read = 0;
-		call->app_mark = RXRPC_APP_MARK_EOF;
-		call->app_read_buf = NULL;
-
-		_leave(" = -EBADMSG");
-		return -EBADMSG;
-	}
-
-	_leave(" = -EAGAIN");
-	return -EAGAIN;
-} /* end __rxrpc_call_read_data() */
-
-/*****************************************************************************/
-/*
- * attempt to read the specified amount of data from the call's ready queue
- * into the buffer provided
- * - since this func is the only one going to look at packets queued on
- *   app_readyq, we don't need a lock to modify or access them, only to modify
- *   the queue pointers
- * - if the buffer pointer is NULL, then data is merely drained, not copied
- * - if flags&RXRPC_CALL_READ_BLOCK, then the function will wait until there is
- *   enough data or an error will be generated
- *   - note that the caller must have added the calling task to the call's wait
- *     queue beforehand
- * - if flags&RXRPC_CALL_READ_ALL, then an error will be generated if this
- *   function doesn't read all available data
- */
-int rxrpc_call_read_data(struct rxrpc_call *call,
-			 void *buffer, size_t size, int flags)
-{
-	int ret;
-
-	_enter("%p{arq=%Zu},%p,%Zd,%x",
-	       call, call->app_ready_qty, buffer, size, flags);
-
-	spin_lock(&call->lock);
-
-	if (unlikely(!!call->app_read_buf)) {
-		spin_unlock(&call->lock);
-		_leave(" = -EBUSY");
-		return -EBUSY;
-	}
-
-	call->app_mark = size;
-	call->app_read_buf = buffer;
-	call->app_async_read = 1;
-	call->app_read_count++;
-
-	/* read as much data as possible */
-	ret = __rxrpc_call_read_data(call);
-	switch (ret) {
-	case 0:
-		if (flags & RXRPC_CALL_READ_ALL &&
-		    (!call->app_last_rcv || call->app_ready_qty > 0)) {
-			_leave(" = -EBADMSG");
-			__rxrpc_call_abort(call, -EBADMSG);
-			return -EBADMSG;
-		}
-
-		spin_unlock(&call->lock);
-		call->app_attn_func(call);
-		_leave(" = 0");
-		return ret;
-
-	case -ECONNABORTED:
-		spin_unlock(&call->lock);
-		_leave(" = %d [aborted]", ret);
-		return ret;
-
-	default:
-		__rxrpc_call_abort(call, ret);
-		_leave(" = %d", ret);
-		return ret;
-
-	case -EAGAIN:
-		spin_unlock(&call->lock);
-
-		if (!(flags & RXRPC_CALL_READ_BLOCK)) {
-			_leave(" = -EAGAIN");
-			return -EAGAIN;
-		}
-
-		/* wait for the data to arrive */
-		_debug("blocking for data arrival");
-
-		for (;;) {
-			set_current_state(TASK_INTERRUPTIBLE);
-			if (!call->app_async_read || signal_pending(current))
-				break;
-			schedule();
-		}
-		set_current_state(TASK_RUNNING);
-
-		if (signal_pending(current)) {
-			_leave(" = -EINTR");
-			return -EINTR;
-		}
-
-		if (call->app_call_state == RXRPC_CSTATE_ERROR) {
-			_leave(" = -ECONNABORTED");
-			return -ECONNABORTED;
-		}
-
-		_leave(" = 0");
-		return 0;
-	}
-
-} /* end rxrpc_call_read_data() */
-
-/*****************************************************************************/
-/*
- * write data to a call
- * - the data may not be sent immediately if it doesn't fill a buffer
- * - if we can't queue all the data for buffering now, siov[] will have been
- *   adjusted to take account of what has been sent
- */
-int rxrpc_call_write_data(struct rxrpc_call *call,
-			  size_t sioc,
-			  struct kvec *siov,
-			  u8 rxhdr_flags,
-			  gfp_t alloc_flags,
-			  int dup_data,
-			  size_t *size_sent)
-{
-	struct rxrpc_message *msg;
-	struct kvec *sptr;
-	size_t space, size, chunk, tmp;
-	char *buf;
-	int ret;
-
-	_enter("%p,%Zu,%p,%02x,%x,%d,%p",
-	       call, sioc, siov, rxhdr_flags, alloc_flags, dup_data,
-	       size_sent);
-
-	*size_sent = 0;
-	size = 0;
-	ret = -EINVAL;
-
-	/* can't send more if we've sent last packet from this end */
-	switch (call->app_call_state) {
-	case RXRPC_CSTATE_SRVR_SND_REPLY:
-	case RXRPC_CSTATE_CLNT_SND_ARGS:
-		break;
-	case RXRPC_CSTATE_ERROR:
-		ret = call->app_errno;
-	default:
-		goto out;
-	}
-
-	/* calculate how much data we've been given */
-	sptr = siov;
-	for (; sioc > 0; sptr++, sioc--) {
-		if (!sptr->iov_len)
-			continue;
-
-		if (!sptr->iov_base)
-			goto out;
-
-		size += sptr->iov_len;
-	}
-
-	_debug("- size=%Zu mtu=%Zu", size, call->conn->mtu_size);
-
-	do {
-		/* make sure there's a message under construction */
-		if (!call->snd_nextmsg) {
-			/* no - allocate a message with no data yet attached */
-			ret = rxrpc_conn_newmsg(call->conn, call,
-						RXRPC_PACKET_TYPE_DATA,
-						0, NULL, alloc_flags,
-						&call->snd_nextmsg);
-			if (ret < 0)
-				goto out;
-			_debug("- allocated new message [ds=%Zu]",
-			       call->snd_nextmsg->dsize);
-		}
-
-		msg = call->snd_nextmsg;
-		msg->hdr.flags |= rxhdr_flags;
-
-		/* deal with zero-length terminal packet */
-		if (size == 0) {
-			if (rxhdr_flags & RXRPC_LAST_PACKET) {
-				ret = rxrpc_call_flush(call);
-				if (ret < 0)
-					goto out;
-			}
-			break;
-		}
-
-		/* work out how much space current packet has available */
-		space = call->conn->mtu_size - msg->dsize;
-		chunk = min(space, size);
-
-		_debug("- [before] space=%Zu chunk=%Zu", space, chunk);
-
-		while (!siov->iov_len)
-			siov++;
-
-		/* if we are going to have to duplicate the data then coalesce
-		 * it too */
-		if (dup_data) {
-			/* don't allocate more that 1 page at a time */
-			if (chunk > PAGE_SIZE)
-				chunk = PAGE_SIZE;
-
-			/* allocate a data buffer and attach to the message */
-			buf = kmalloc(chunk, alloc_flags);
-			if (unlikely(!buf)) {
-				if (msg->dsize ==
-				    sizeof(struct rxrpc_header)) {
-					/* discard an empty msg and wind back
-					 * the seq counter */
-					rxrpc_put_message(msg);
-					call->snd_nextmsg = NULL;
-					call->snd_seq_count--;
-				}
-
-				ret = -ENOMEM;
-				goto out;
-			}
-
-			tmp = msg->dcount++;
-			set_bit(tmp, &msg->dfree);
-			msg->data[tmp].iov_base = buf;
-			msg->data[tmp].iov_len = chunk;
-			msg->dsize += chunk;
-			*size_sent += chunk;
-			size -= chunk;
-
-			/* load the buffer with data */
-			while (chunk > 0) {
-				tmp = min(chunk, siov->iov_len);
-				memcpy(buf, siov->iov_base, tmp);
-				buf += tmp;
-				siov->iov_base += tmp;
-				siov->iov_len -= tmp;
-				if (!siov->iov_len)
-					siov++;
-				chunk -= tmp;
-			}
-		}
-		else {
-			/* we want to attach the supplied buffers directly */
-			while (chunk > 0 &&
-			       msg->dcount < RXRPC_MSG_MAX_IOCS) {
-				tmp = msg->dcount++;
-				msg->data[tmp].iov_base = siov->iov_base;
-				msg->data[tmp].iov_len = siov->iov_len;
-				msg->dsize += siov->iov_len;
-				*size_sent += siov->iov_len;
-				size -= siov->iov_len;
-				chunk -= siov->iov_len;
-				siov++;
-			}
-		}
-
-		_debug("- [loaded] chunk=%Zu size=%Zu", chunk, size);
-
-		/* dispatch the message when full, final or requesting ACK */
-		if (msg->dsize >= call->conn->mtu_size || rxhdr_flags) {
-			ret = rxrpc_call_flush(call);
-			if (ret < 0)
-				goto out;
-		}
-
-	} while(size > 0);
-
-	ret = 0;
- out:
-	_leave(" = %d (%Zd queued, %Zd rem)", ret, *size_sent, size);
-	return ret;
-
-} /* end rxrpc_call_write_data() */
-
-/*****************************************************************************/
-/*
- * flush outstanding packets to the network
- */
-static int rxrpc_call_flush(struct rxrpc_call *call)
-{
-	struct rxrpc_message *msg;
-	int ret = 0;
-
-	_enter("%p", call);
-
-	rxrpc_get_call(call);
-
-	/* if there's a packet under construction, then dispatch it now */
-	if (call->snd_nextmsg) {
-		msg = call->snd_nextmsg;
-		call->snd_nextmsg = NULL;
-
-		if (msg->hdr.flags & RXRPC_LAST_PACKET) {
-			msg->hdr.flags &= ~RXRPC_MORE_PACKETS;
-			if (call->app_call_state != RXRPC_CSTATE_CLNT_SND_ARGS)
-				msg->hdr.flags |= RXRPC_REQUEST_ACK;
-		}
-		else {
-			msg->hdr.flags |= RXRPC_MORE_PACKETS;
-		}
-
-		_proto("Sending DATA message { ds=%Zu dc=%u df=%02lu }",
-		       msg->dsize, msg->dcount, msg->dfree);
-
-		/* queue and adjust call state */
-		spin_lock(&call->lock);
-		list_add_tail(&msg->link, &call->acks_pendq);
-
-		/* decide what to do depending on current state and if this is
-		 * the last packet */
-		ret = -EINVAL;
-		switch (call->app_call_state) {
-		case RXRPC_CSTATE_SRVR_SND_REPLY:
-			if (msg->hdr.flags & RXRPC_LAST_PACKET) {
-				call->app_call_state =
-					RXRPC_CSTATE_SRVR_RCV_FINAL_ACK;
-				_state(call);
-			}
-			break;
-
-		case RXRPC_CSTATE_CLNT_SND_ARGS:
-			if (msg->hdr.flags & RXRPC_LAST_PACKET) {
-				call->app_call_state =
-					RXRPC_CSTATE_CLNT_RCV_REPLY;
-				_state(call);
-			}
-			break;
-
-		case RXRPC_CSTATE_ERROR:
-			ret = call->app_errno;
-		default:
-			spin_unlock(&call->lock);
-			goto out;
-		}
-
-		call->acks_pend_cnt++;
-
-		mod_timer(&call->acks_timeout,
-			  __rxrpc_rtt_based_timeout(call,
-						    rxrpc_call_acks_timeout));
-
-		spin_unlock(&call->lock);
-
-		ret = rxrpc_conn_sendmsg(call->conn, msg);
-		if (ret == 0)
-			call->pkt_snd_count++;
-	}
-
- out:
-	rxrpc_put_call(call);
-
-	_leave(" = %d", ret);
-	return ret;
-
-} /* end rxrpc_call_flush() */
-
-/*****************************************************************************/
-/*
- * resend NAK'd or unacknowledged packets up to the highest one specified
- */
-static void rxrpc_call_resend(struct rxrpc_call *call, rxrpc_seq_t highest)
-{
-	struct rxrpc_message *msg;
-	struct list_head *_p;
-	rxrpc_seq_t seq = 0;
-
-	_enter("%p,%u", call, highest);
-
-	_proto("Rx Resend required");
-
-	/* handle too many resends */
-	if (call->snd_resend_cnt >= rxrpc_call_max_resend) {
-		_debug("Aborting due to too many resends (rcv=%d)",
-		       call->pkt_rcv_count);
-		rxrpc_call_abort(call,
-				 call->pkt_rcv_count > 0 ? -EIO : -ETIMEDOUT);
-		_leave("");
-		return;
-	}
-
-	spin_lock(&call->lock);
-	call->snd_resend_cnt++;
-	for (;;) {
-		/* determine which the next packet we might need to ACK is */
-		if (seq <= call->acks_dftv_seq)
-			seq = call->acks_dftv_seq;
-		seq++;
-
-		if (seq > highest)
-			break;
-
-		/* look for the packet in the pending-ACK queue */
-		list_for_each(_p, &call->acks_pendq) {
-			msg = list_entry(_p, struct rxrpc_message, link);
-			if (msg->seq == seq)
-				goto found_msg;
-		}
-
-		panic("%s(%p,%d):"
-		      " Inconsistent pending-ACK queue (ds=%u sc=%u sq=%u)\n",
-		      __FUNCTION__, call, highest,
-		      call->acks_dftv_seq, call->snd_seq_count, seq);
-
-	found_msg:
-		if (msg->state != RXRPC_MSG_SENT)
-			continue; /* only un-ACK'd packets */
-
-		rxrpc_get_message(msg);
-		spin_unlock(&call->lock);
-
-		/* send each message again (and ignore any errors we might
-		 * incur) */
-		_proto("Resending DATA message { ds=%Zu dc=%u df=%02lu }",
-		       msg->dsize, msg->dcount, msg->dfree);
-
-		if (rxrpc_conn_sendmsg(call->conn, msg) == 0)
-			call->pkt_snd_count++;
-
-		rxrpc_put_message(msg);
-
-		spin_lock(&call->lock);
-	}
-
-	/* reset the timeout */
-	mod_timer(&call->acks_timeout,
-		  __rxrpc_rtt_based_timeout(call, rxrpc_call_acks_timeout));
-
-	spin_unlock(&call->lock);
-
-	_leave("");
-} /* end rxrpc_call_resend() */
-
-/*****************************************************************************/
-/*
- * handle an ICMP error being applied to a call
- */
-void rxrpc_call_handle_error(struct rxrpc_call *call, int local, int errno)
-{
-	_enter("%p{%u},%d", call, ntohl(call->call_id), errno);
-
-	/* if this call is already aborted, then just wake up any waiters */
-	if (call->app_call_state == RXRPC_CSTATE_ERROR) {
-		call->app_error_func(call);
-	}
-	else {
-		/* tell the app layer what happened */
-		spin_lock(&call->lock);
-		call->app_call_state = RXRPC_CSTATE_ERROR;
-		_state(call);
-		if (local)
-			call->app_err_state = RXRPC_ESTATE_LOCAL_ERROR;
-		else
-			call->app_err_state = RXRPC_ESTATE_REMOTE_ERROR;
-		call->app_errno		= errno;
-		call->app_mark		= RXRPC_APP_MARK_EOF;
-		call->app_read_buf	= NULL;
-		call->app_async_read	= 0;
-
-		/* map the error */
-		call->app_aemap_func(call);
-
-		del_timer_sync(&call->acks_timeout);
-		del_timer_sync(&call->rcv_timeout);
-		del_timer_sync(&call->ackr_dfr_timo);
-
-		spin_unlock(&call->lock);
-
-		call->app_error_func(call);
-	}
-
-	_leave("");
-} /* end rxrpc_call_handle_error() */
diff --git a/net/rxrpc/connection.c b/net/rxrpc/connection.c
deleted file mode 100644
index a7c929a..0000000
--- a/net/rxrpc/connection.c
+++ /dev/null
@@ -1,777 +0,0 @@
-/* connection.c: Rx connection routines
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <rxrpc/rxrpc.h>
-#include <rxrpc/transport.h>
-#include <rxrpc/peer.h>
-#include <rxrpc/connection.h>
-#include <rxrpc/call.h>
-#include <rxrpc/message.h>
-#include <linux/udp.h>
-#include <linux/ip.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include "internal.h"
-
-__RXACCT_DECL(atomic_t rxrpc_connection_count);
-
-LIST_HEAD(rxrpc_conns);
-DECLARE_RWSEM(rxrpc_conns_sem);
-unsigned long rxrpc_conn_timeout = 60 * 60;
-
-static void rxrpc_conn_do_timeout(struct rxrpc_connection *conn);
-
-static void __rxrpc_conn_timeout(rxrpc_timer_t *timer)
-{
-	struct rxrpc_connection *conn =
-		list_entry(timer, struct rxrpc_connection, timeout);
-
-	_debug("Rx CONN TIMEOUT [%p{u=%d}]", conn, atomic_read(&conn->usage));
-
-	rxrpc_conn_do_timeout(conn);
-}
-
-static const struct rxrpc_timer_ops rxrpc_conn_timer_ops = {
-	.timed_out	= __rxrpc_conn_timeout,
-};
-
-/*****************************************************************************/
-/*
- * create a new connection record
- */
-static inline int __rxrpc_create_connection(struct rxrpc_peer *peer,
-					    struct rxrpc_connection **_conn)
-{
-	struct rxrpc_connection *conn;
-
-	_enter("%p",peer);
-
-	/* allocate and initialise a connection record */
-	conn = kzalloc(sizeof(struct rxrpc_connection), GFP_KERNEL);
-	if (!conn) {
-		_leave(" = -ENOMEM");
-		return -ENOMEM;
-	}
-
-	atomic_set(&conn->usage, 1);
-
-	INIT_LIST_HEAD(&conn->link);
-	INIT_LIST_HEAD(&conn->id_link);
-	init_waitqueue_head(&conn->chanwait);
-	spin_lock_init(&conn->lock);
-	rxrpc_timer_init(&conn->timeout, &rxrpc_conn_timer_ops);
-
-	do_gettimeofday(&conn->atime);
-	conn->mtu_size = 1024;
-	conn->peer = peer;
-	conn->trans = peer->trans;
-
-	__RXACCT(atomic_inc(&rxrpc_connection_count));
-	*_conn = conn;
-	_leave(" = 0 (%p)", conn);
-
-	return 0;
-} /* end __rxrpc_create_connection() */
-
-/*****************************************************************************/
-/*
- * create a new connection record for outgoing connections
- */
-int rxrpc_create_connection(struct rxrpc_transport *trans,
-			    __be16 port,
-			    __be32 addr,
-			    uint16_t service_id,
-			    void *security,
-			    struct rxrpc_connection **_conn)
-{
-	struct rxrpc_connection *candidate, *conn;
-	struct rxrpc_peer *peer;
-	struct list_head *_p;
-	__be32 connid;
-	int ret;
-
-	_enter("%p{%hu},%u,%hu", trans, trans->port, ntohs(port), service_id);
-
-	/* get a peer record */
-	ret = rxrpc_peer_lookup(trans, addr, &peer);
-	if (ret < 0) {
-		_leave(" = %d", ret);
-		return ret;
-	}
-
-	/* allocate and initialise a connection record */
-	ret = __rxrpc_create_connection(peer, &candidate);
-	if (ret < 0) {
-		rxrpc_put_peer(peer);
-		_leave(" = %d", ret);
-		return ret;
-	}
-
-	/* fill in the specific bits */
-	candidate->addr.sin_family	= AF_INET;
-	candidate->addr.sin_port	= port;
-	candidate->addr.sin_addr.s_addr	= addr;
-
-	candidate->in_epoch		= rxrpc_epoch;
-	candidate->out_epoch		= rxrpc_epoch;
-	candidate->in_clientflag	= 0;
-	candidate->out_clientflag	= RXRPC_CLIENT_INITIATED;
-	candidate->service_id		= htons(service_id);
-
-	/* invent a unique connection ID */
-	write_lock(&peer->conn_idlock);
-
- try_next_id:
-	connid = htonl(peer->conn_idcounter & RXRPC_CIDMASK);
-	peer->conn_idcounter += RXRPC_MAXCALLS;
-
-	list_for_each(_p, &peer->conn_idlist) {
-		conn = list_entry(_p, struct rxrpc_connection, id_link);
-		if (connid == conn->conn_id)
-			goto try_next_id;
-		if (connid > conn->conn_id)
-			break;
-	}
-
-	_debug("selected candidate conn ID %x.%u",
-	       ntohl(peer->addr.s_addr), ntohl(connid));
-
-	candidate->conn_id = connid;
-	list_add_tail(&candidate->id_link, _p);
-
-	write_unlock(&peer->conn_idlock);
-
-	/* attach to peer */
-	candidate->peer = peer;
-
-	write_lock(&peer->conn_lock);
-
-	/* search the peer's transport graveyard list */
-	spin_lock(&peer->conn_gylock);
-	list_for_each(_p, &peer->conn_graveyard) {
-		conn = list_entry(_p, struct rxrpc_connection, link);
-		if (conn->addr.sin_port	== candidate->addr.sin_port	&&
-		    conn->security_ix	== candidate->security_ix	&&
-		    conn->service_id	== candidate->service_id	&&
-		    conn->in_clientflag	== 0)
-			goto found_in_graveyard;
-	}
-	spin_unlock(&peer->conn_gylock);
-
-	/* pick the new candidate */
-	_debug("created connection: {%08x} [out]", ntohl(candidate->conn_id));
-	atomic_inc(&peer->conn_count);
-	conn = candidate;
-	candidate = NULL;
-
- make_active:
-	list_add_tail(&conn->link, &peer->conn_active);
-	write_unlock(&peer->conn_lock);
-
-	if (candidate) {
-		write_lock(&peer->conn_idlock);
-		list_del(&candidate->id_link);
-		write_unlock(&peer->conn_idlock);
-
-		__RXACCT(atomic_dec(&rxrpc_connection_count));
-		kfree(candidate);
-	}
-	else {
-		down_write(&rxrpc_conns_sem);
-		list_add_tail(&conn->proc_link, &rxrpc_conns);
-		up_write(&rxrpc_conns_sem);
-	}
-
-	*_conn = conn;
-	_leave(" = 0 (%p)", conn);
-
-	return 0;
-
-	/* handle resurrecting a connection from the graveyard */
- found_in_graveyard:
-	_debug("resurrecting connection: {%08x} [out]", ntohl(conn->conn_id));
-	rxrpc_get_connection(conn);
-	rxrpc_krxtimod_del_timer(&conn->timeout);
-	list_del_init(&conn->link);
-	spin_unlock(&peer->conn_gylock);
-	goto make_active;
-} /* end rxrpc_create_connection() */
-
-/*****************************************************************************/
-/*
- * lookup the connection for an incoming packet
- * - create a new connection record for unrecorded incoming connections
- */
-int rxrpc_connection_lookup(struct rxrpc_peer *peer,
-			    struct rxrpc_message *msg,
-			    struct rxrpc_connection **_conn)
-{
-	struct rxrpc_connection *conn, *candidate = NULL;
-	struct list_head *_p;
-	struct sk_buff *pkt = msg->pkt;
-	int ret, fresh = 0;
-	__be32 x_epoch, x_connid;
-	__be16 x_port, x_servid;
-	__u32 x_secix;
-	u8 x_clflag;
-
-	_enter("%p{{%hu}},%u,%hu",
-	       peer,
-	       peer->trans->port,
-	       ntohs(pkt->h.uh->source),
-	       ntohs(msg->hdr.serviceId));
-
-	x_port		= pkt->h.uh->source;
-	x_epoch		= msg->hdr.epoch;
-	x_clflag	= msg->hdr.flags & RXRPC_CLIENT_INITIATED;
-	x_connid	= htonl(ntohl(msg->hdr.cid) & RXRPC_CIDMASK);
-	x_servid	= msg->hdr.serviceId;
-	x_secix		= msg->hdr.securityIndex;
-
-	/* [common case] search the transport's active list first */
-	read_lock(&peer->conn_lock);
-	list_for_each(_p, &peer->conn_active) {
-		conn = list_entry(_p, struct rxrpc_connection, link);
-		if (conn->addr.sin_port		== x_port	&&
-		    conn->in_epoch		== x_epoch	&&
-		    conn->conn_id		== x_connid	&&
-		    conn->security_ix		== x_secix	&&
-		    conn->service_id		== x_servid	&&
-		    conn->in_clientflag		== x_clflag)
-			goto found_active;
-	}
-	read_unlock(&peer->conn_lock);
-
-	/* [uncommon case] not active
-	 * - create a candidate for a new record if an inbound connection
-	 * - only examine the graveyard for an outbound connection
-	 */
-	if (x_clflag) {
-		ret = __rxrpc_create_connection(peer, &candidate);
-		if (ret < 0) {
-			_leave(" = %d", ret);
-			return ret;
-		}
-
-		/* fill in the specifics */
-		candidate->addr.sin_family	= AF_INET;
-		candidate->addr.sin_port	= x_port;
-		candidate->addr.sin_addr.s_addr = pkt->nh.iph->saddr;
-		candidate->in_epoch		= x_epoch;
-		candidate->out_epoch		= x_epoch;
-		candidate->in_clientflag	= RXRPC_CLIENT_INITIATED;
-		candidate->out_clientflag	= 0;
-		candidate->conn_id		= x_connid;
-		candidate->service_id		= x_servid;
-		candidate->security_ix		= x_secix;
-	}
-
-	/* search the active list again, just in case it appeared whilst we
-	 * were busy */
-	write_lock(&peer->conn_lock);
-	list_for_each(_p, &peer->conn_active) {
-		conn = list_entry(_p, struct rxrpc_connection, link);
-		if (conn->addr.sin_port		== x_port	&&
-		    conn->in_epoch		== x_epoch	&&
-		    conn->conn_id		== x_connid	&&
-		    conn->security_ix		== x_secix	&&
-		    conn->service_id		== x_servid	&&
-		    conn->in_clientflag		== x_clflag)
-			goto found_active_second_chance;
-	}
-
-	/* search the transport's graveyard list */
-	spin_lock(&peer->conn_gylock);
-	list_for_each(_p, &peer->conn_graveyard) {
-		conn = list_entry(_p, struct rxrpc_connection, link);
-		if (conn->addr.sin_port		== x_port	&&
-		    conn->in_epoch		== x_epoch	&&
-		    conn->conn_id		== x_connid	&&
-		    conn->security_ix		== x_secix	&&
-		    conn->service_id		== x_servid	&&
-		    conn->in_clientflag		== x_clflag)
-			goto found_in_graveyard;
-	}
-	spin_unlock(&peer->conn_gylock);
-
-	/* outbound connections aren't created here */
-	if (!x_clflag) {
-		write_unlock(&peer->conn_lock);
-		_leave(" = -ENOENT");
-		return -ENOENT;
-	}
-
-	/* we can now add the new candidate to the list */
-	_debug("created connection: {%08x} [in]", ntohl(candidate->conn_id));
-	rxrpc_get_peer(peer);
-	conn = candidate;
-	candidate = NULL;
-	atomic_inc(&peer->conn_count);
-	fresh = 1;
-
- make_active:
-	list_add_tail(&conn->link, &peer->conn_active);
-
- success_uwfree:
-	write_unlock(&peer->conn_lock);
-
-	if (candidate) {
-		write_lock(&peer->conn_idlock);
-		list_del(&candidate->id_link);
-		write_unlock(&peer->conn_idlock);
-
-		__RXACCT(atomic_dec(&rxrpc_connection_count));
-		kfree(candidate);
-	}
-
-	if (fresh) {
-		down_write(&rxrpc_conns_sem);
-		list_add_tail(&conn->proc_link, &rxrpc_conns);
-		up_write(&rxrpc_conns_sem);
-	}
-
- success:
-	*_conn = conn;
-	_leave(" = 0 (%p)", conn);
-	return 0;
-
-	/* handle the connection being found in the active list straight off */
- found_active:
-	rxrpc_get_connection(conn);
-	read_unlock(&peer->conn_lock);
-	goto success;
-
-	/* handle resurrecting a connection from the graveyard */
- found_in_graveyard:
-	_debug("resurrecting connection: {%08x} [in]", ntohl(conn->conn_id));
-	rxrpc_get_peer(peer);
-	rxrpc_get_connection(conn);
-	rxrpc_krxtimod_del_timer(&conn->timeout);
-	list_del_init(&conn->link);
-	spin_unlock(&peer->conn_gylock);
-	goto make_active;
-
-	/* handle finding the connection on the second time through the active
-	 * list */
- found_active_second_chance:
-	rxrpc_get_connection(conn);
-	goto success_uwfree;
-
-} /* end rxrpc_connection_lookup() */
-
-/*****************************************************************************/
-/*
- * finish using a connection record
- * - it will be transferred to the peer's connection graveyard when refcount
- *   reaches 0
- */
-void rxrpc_put_connection(struct rxrpc_connection *conn)
-{
-	struct rxrpc_peer *peer;
-
-	if (!conn)
-		return;
-
-	_enter("%p{u=%d p=%hu}",
-	       conn, atomic_read(&conn->usage), ntohs(conn->addr.sin_port));
-
-	peer = conn->peer;
-	spin_lock(&peer->conn_gylock);
-
-	/* sanity check */
-	if (atomic_read(&conn->usage) <= 0)
-		BUG();
-
-	if (likely(!atomic_dec_and_test(&conn->usage))) {
-		spin_unlock(&peer->conn_gylock);
-		_leave("");
-		return;
-	}
-
-	/* move to graveyard queue */
-	_debug("burying connection: {%08x}", ntohl(conn->conn_id));
-	list_move_tail(&conn->link, &peer->conn_graveyard);
-
-	rxrpc_krxtimod_add_timer(&conn->timeout, rxrpc_conn_timeout * HZ);
-
-	spin_unlock(&peer->conn_gylock);
-
-	rxrpc_put_peer(conn->peer);
-
-	_leave(" [killed]");
-} /* end rxrpc_put_connection() */
-
-/*****************************************************************************/
-/*
- * free a connection record
- */
-static void rxrpc_conn_do_timeout(struct rxrpc_connection *conn)
-{
-	struct rxrpc_peer *peer;
-
-	_enter("%p{u=%d p=%hu}",
-	       conn, atomic_read(&conn->usage), ntohs(conn->addr.sin_port));
-
-	peer = conn->peer;
-
-	if (atomic_read(&conn->usage) < 0)
-		BUG();
-
-	/* remove from graveyard if still dead */
-	spin_lock(&peer->conn_gylock);
-	if (atomic_read(&conn->usage) == 0) {
-		list_del_init(&conn->link);
-	}
-	else {
-		conn = NULL;
-	}
-	spin_unlock(&peer->conn_gylock);
-
-	if (!conn) {
-		_leave("");
-		return; /* resurrected */
-	}
-
-	_debug("--- Destroying Connection %p{%08x} ---",
-	       conn, ntohl(conn->conn_id));
-
-	down_write(&rxrpc_conns_sem);
-	list_del(&conn->proc_link);
-	up_write(&rxrpc_conns_sem);
-
-	write_lock(&peer->conn_idlock);
-	list_del(&conn->id_link);
-	write_unlock(&peer->conn_idlock);
-
-	__RXACCT(atomic_dec(&rxrpc_connection_count));
-	kfree(conn);
-
-	/* if the graveyard is now empty, wake up anyone waiting for that */
-	if (atomic_dec_and_test(&peer->conn_count))
-		wake_up(&peer->conn_gy_waitq);
-
-	_leave(" [destroyed]");
-} /* end rxrpc_conn_do_timeout() */
-
-/*****************************************************************************/
-/*
- * clear all connection records from a peer endpoint
- */
-void rxrpc_conn_clearall(struct rxrpc_peer *peer)
-{
-	DECLARE_WAITQUEUE(myself, current);
-
-	struct rxrpc_connection *conn;
-	int err;
-
-	_enter("%p", peer);
-
-	/* there shouldn't be any active conns remaining */
-	if (!list_empty(&peer->conn_active))
-		BUG();
-
-	/* manually timeout all conns in the graveyard */
-	spin_lock(&peer->conn_gylock);
-	while (!list_empty(&peer->conn_graveyard)) {
-		conn = list_entry(peer->conn_graveyard.next,
-				  struct rxrpc_connection, link);
-		err = rxrpc_krxtimod_del_timer(&conn->timeout);
-		spin_unlock(&peer->conn_gylock);
-
-		if (err == 0)
-			rxrpc_conn_do_timeout(conn);
-
-		spin_lock(&peer->conn_gylock);
-	}
-	spin_unlock(&peer->conn_gylock);
-
-	/* wait for the the conn graveyard to be completely cleared */
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	add_wait_queue(&peer->conn_gy_waitq, &myself);
-
-	while (atomic_read(&peer->conn_count) != 0) {
-		schedule();
-		set_current_state(TASK_UNINTERRUPTIBLE);
-	}
-
-	remove_wait_queue(&peer->conn_gy_waitq, &myself);
-	set_current_state(TASK_RUNNING);
-
-	_leave("");
-} /* end rxrpc_conn_clearall() */
-
-/*****************************************************************************/
-/*
- * allocate and prepare a message for sending out through the transport
- * endpoint
- */
-int rxrpc_conn_newmsg(struct rxrpc_connection *conn,
-		      struct rxrpc_call *call,
-		      uint8_t type,
-		      int dcount,
-		      struct kvec diov[],
-		      gfp_t alloc_flags,
-		      struct rxrpc_message **_msg)
-{
-	struct rxrpc_message *msg;
-	int loop;
-
-	_enter("%p{%d},%p,%u", conn, ntohs(conn->addr.sin_port), call, type);
-
-	if (dcount > 3) {
-		_leave(" = -EINVAL");
-		return -EINVAL;
-	}
-
-	msg = kzalloc(sizeof(struct rxrpc_message), alloc_flags);
-	if (!msg) {
-		_leave(" = -ENOMEM");
-		return -ENOMEM;
-	}
-
-	atomic_set(&msg->usage, 1);
-
-	INIT_LIST_HEAD(&msg->link);
-
-	msg->state = RXRPC_MSG_PREPARED;
-
-	msg->hdr.epoch		= conn->out_epoch;
-	msg->hdr.cid		= conn->conn_id | (call ? call->chan_ix : 0);
-	msg->hdr.callNumber	= call ? call->call_id : 0;
-	msg->hdr.type		= type;
-	msg->hdr.flags		= conn->out_clientflag;
-	msg->hdr.securityIndex	= conn->security_ix;
-	msg->hdr.serviceId	= conn->service_id;
-
-	/* generate sequence numbers for data packets */
-	if (call) {
-		switch (type) {
-		case RXRPC_PACKET_TYPE_DATA:
-			msg->seq = ++call->snd_seq_count;
-			msg->hdr.seq = htonl(msg->seq);
-			break;
-		case RXRPC_PACKET_TYPE_ACK:
-			/* ACK sequence numbers are complicated. The following
-			 * may be wrong:
-			 * - jumbo packet ACKs should have a seq number
-			 * - normal ACKs should not
-			 */
-		default:
-			break;
-		}
-	}
-
-	msg->dcount = dcount + 1;
-	msg->dsize = sizeof(msg->hdr);
-	msg->data[0].iov_len = sizeof(msg->hdr);
-	msg->data[0].iov_base = &msg->hdr;
-
-	for (loop=0; loop < dcount; loop++) {
-		msg->dsize += diov[loop].iov_len;
-		msg->data[loop+1].iov_len  = diov[loop].iov_len;
-		msg->data[loop+1].iov_base = diov[loop].iov_base;
-	}
-
-	__RXACCT(atomic_inc(&rxrpc_message_count));
-	*_msg = msg;
-	_leave(" = 0 (%p) #%d", msg, atomic_read(&rxrpc_message_count));
-	return 0;
-} /* end rxrpc_conn_newmsg() */
-
-/*****************************************************************************/
-/*
- * free a message
- */
-void __rxrpc_put_message(struct rxrpc_message *msg)
-{
-	int loop;
-
-	_enter("%p #%d", msg, atomic_read(&rxrpc_message_count));
-
-	if (msg->pkt)
-		kfree_skb(msg->pkt);
-	rxrpc_put_connection(msg->conn);
-
-	for (loop = 0; loop < 8; loop++)
-		if (test_bit(loop, &msg->dfree))
-			kfree(msg->data[loop].iov_base);
-
-	__RXACCT(atomic_dec(&rxrpc_message_count));
-	kfree(msg);
-
-	_leave("");
-} /* end __rxrpc_put_message() */
-
-/*****************************************************************************/
-/*
- * send a message out through the transport endpoint
- */
-int rxrpc_conn_sendmsg(struct rxrpc_connection *conn,
-		       struct rxrpc_message *msg)
-{
-	struct msghdr msghdr;
-	int ret;
-
-	_enter("%p{%d}", conn, ntohs(conn->addr.sin_port));
-
-	/* fill in some fields in the header */
-	spin_lock(&conn->lock);
-	msg->hdr.serial = htonl(++conn->serial_counter);
-	msg->rttdone = 0;
-	spin_unlock(&conn->lock);
-
-	/* set up the message to be transmitted */
-	msghdr.msg_name		= &conn->addr;
-	msghdr.msg_namelen	= sizeof(conn->addr);
-	msghdr.msg_control	= NULL;
-	msghdr.msg_controllen	= 0;
-	msghdr.msg_flags	= MSG_CONFIRM | MSG_DONTWAIT;
-
-	_net("Sending message type %d of %Zd bytes to %08x:%d",
-	     msg->hdr.type,
-	     msg->dsize,
-	     ntohl(conn->addr.sin_addr.s_addr),
-	     ntohs(conn->addr.sin_port));
-
-	/* send the message */
-	ret = kernel_sendmsg(conn->trans->socket, &msghdr,
-			     msg->data, msg->dcount, msg->dsize);
-	if (ret < 0) {
-		msg->state = RXRPC_MSG_ERROR;
-	} else {
-		msg->state = RXRPC_MSG_SENT;
-		ret = 0;
-
-		spin_lock(&conn->lock);
-		do_gettimeofday(&conn->atime);
-		msg->stamp = conn->atime;
-		spin_unlock(&conn->lock);
-	}
-
-	_leave(" = %d", ret);
-
-	return ret;
-} /* end rxrpc_conn_sendmsg() */
-
-/*****************************************************************************/
-/*
- * deal with a subsequent call packet
- */
-int rxrpc_conn_receive_call_packet(struct rxrpc_connection *conn,
-				   struct rxrpc_call *call,
-				   struct rxrpc_message *msg)
-{
-	struct rxrpc_message *pmsg;
-	struct dst_entry *dst;
-	struct list_head *_p;
-	unsigned cix, seq;
-	int ret = 0;
-
-	_enter("%p,%p,%p", conn, call, msg);
-
-	if (!call) {
-		cix = ntohl(msg->hdr.cid) & RXRPC_CHANNELMASK;
-
-		spin_lock(&conn->lock);
-		call = conn->channels[cix];
-
-		if (!call || call->call_id != msg->hdr.callNumber) {
-			spin_unlock(&conn->lock);
-			rxrpc_trans_immediate_abort(conn->trans, msg, -ENOENT);
-			goto out;
-		}
-		else {
-			rxrpc_get_call(call);
-			spin_unlock(&conn->lock);
-		}
-	}
-	else {
-		rxrpc_get_call(call);
-	}
-
-	_proto("Received packet %%%u [%u] on call %hu:%u:%u",
-	       ntohl(msg->hdr.serial),
-	       ntohl(msg->hdr.seq),
-	       ntohs(msg->hdr.serviceId),
-	       ntohl(conn->conn_id),
-	       ntohl(call->call_id));
-
-	call->pkt_rcv_count++;
-
-	dst = msg->pkt->dst;
-	if (dst && dst->dev)
-		conn->peer->if_mtu =
-			dst->dev->mtu - dst->dev->hard_header_len;
-
-	/* queue on the call in seq order */
-	rxrpc_get_message(msg);
-	seq = msg->seq;
-
-	spin_lock(&call->lock);
-	list_for_each(_p, &call->rcv_receiveq) {
-		pmsg = list_entry(_p, struct rxrpc_message, link);
-		if (pmsg->seq > seq)
-			break;
-	}
-	list_add_tail(&msg->link, _p);
-
-	/* reset the activity timeout */
-	call->flags |= RXRPC_CALL_RCV_PKT;
-	mod_timer(&call->rcv_timeout,jiffies + rxrpc_call_rcv_timeout * HZ);
-
-	spin_unlock(&call->lock);
-
-	rxrpc_krxiod_queue_call(call);
-
-	rxrpc_put_call(call);
- out:
-	_leave(" = %d", ret);
-	return ret;
-} /* end rxrpc_conn_receive_call_packet() */
-
-/*****************************************************************************/
-/*
- * handle an ICMP error being applied to a connection
- */
-void rxrpc_conn_handle_error(struct rxrpc_connection *conn,
-			     int local, int errno)
-{
-	struct rxrpc_call *calls[4];
-	int loop;
-
-	_enter("%p{%d},%d", conn, ntohs(conn->addr.sin_port), errno);
-
-	/* get a ref to all my calls in one go */
-	memset(calls, 0, sizeof(calls));
-	spin_lock(&conn->lock);
-
-	for (loop = 3; loop >= 0; loop--) {
-		if (conn->channels[loop]) {
-			calls[loop] = conn->channels[loop];
-			rxrpc_get_call(calls[loop]);
-		}
-	}
-
-	spin_unlock(&conn->lock);
-
-	/* now kick them all */
-	for (loop = 3; loop >= 0; loop--) {
-		if (calls[loop]) {
-			rxrpc_call_handle_error(calls[loop], local, errno);
-			rxrpc_put_call(calls[loop]);
-		}
-	}
-
-	_leave("");
-} /* end rxrpc_conn_handle_error() */
diff --git a/net/rxrpc/internal.h b/net/rxrpc/internal.h
deleted file mode 100644
index cc0c579..0000000
--- a/net/rxrpc/internal.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/* internal.h: internal Rx RPC stuff
- *
- * Copyright (c) 2002   David Howells (dhowells@redhat.com).
- */
-
-#ifndef RXRPC_INTERNAL_H
-#define RXRPC_INTERNAL_H
-
-#include <linux/compiler.h>
-#include <linux/kernel.h>
-
-/*
- * debug accounting
- */
-#if 1
-#define __RXACCT_DECL(X) X
-#define __RXACCT(X) do { X; } while(0)
-#else
-#define __RXACCT_DECL(X)
-#define __RXACCT(X) do { } while(0)
-#endif
-
-__RXACCT_DECL(extern atomic_t rxrpc_transport_count);
-__RXACCT_DECL(extern atomic_t rxrpc_peer_count);
-__RXACCT_DECL(extern atomic_t rxrpc_connection_count);
-__RXACCT_DECL(extern atomic_t rxrpc_call_count);
-__RXACCT_DECL(extern atomic_t rxrpc_message_count);
-
-/*
- * debug tracing
- */
-#define kenter(FMT, a...)	printk("==> %s("FMT")\n",__FUNCTION__ , ##a)
-#define kleave(FMT, a...)	printk("<== %s()"FMT"\n",__FUNCTION__ , ##a)
-#define kdebug(FMT, a...)	printk("    "FMT"\n" , ##a)
-#define kproto(FMT, a...)	printk("### "FMT"\n" , ##a)
-#define knet(FMT, a...)		printk("    "FMT"\n" , ##a)
-
-#if 0
-#define _enter(FMT, a...)	kenter(FMT , ##a)
-#define _leave(FMT, a...)	kleave(FMT , ##a)
-#define _debug(FMT, a...)	kdebug(FMT , ##a)
-#define _proto(FMT, a...)	kproto(FMT , ##a)
-#define _net(FMT, a...)		knet(FMT , ##a)
-#else
-#define _enter(FMT, a...)	do { if (rxrpc_ktrace) kenter(FMT , ##a); } while(0)
-#define _leave(FMT, a...)	do { if (rxrpc_ktrace) kleave(FMT , ##a); } while(0)
-#define _debug(FMT, a...)	do { if (rxrpc_kdebug) kdebug(FMT , ##a); } while(0)
-#define _proto(FMT, a...)	do { if (rxrpc_kproto) kproto(FMT , ##a); } while(0)
-#define _net(FMT, a...)		do { if (rxrpc_knet)   knet  (FMT , ##a); } while(0)
-#endif
-
-static inline void rxrpc_discard_my_signals(void)
-{
-	while (signal_pending(current)) {
-		siginfo_t sinfo;
-
-		spin_lock_irq(&current->sighand->siglock);
-		dequeue_signal(current, &current->blocked, &sinfo);
-		spin_unlock_irq(&current->sighand->siglock);
-	}
-}
-
-/*
- * call.c
- */
-extern struct list_head rxrpc_calls;
-extern struct rw_semaphore rxrpc_calls_sem;
-
-/*
- * connection.c
- */
-extern struct list_head rxrpc_conns;
-extern struct rw_semaphore rxrpc_conns_sem;
-extern unsigned long rxrpc_conn_timeout;
-
-extern void rxrpc_conn_clearall(struct rxrpc_peer *peer);
-
-/*
- * peer.c
- */
-extern struct list_head rxrpc_peers;
-extern struct rw_semaphore rxrpc_peers_sem;
-extern unsigned long rxrpc_peer_timeout;
-
-extern void rxrpc_peer_calculate_rtt(struct rxrpc_peer *peer,
-				     struct rxrpc_message *msg,
-				     struct rxrpc_message *resp);
-
-extern void rxrpc_peer_clearall(struct rxrpc_transport *trans);
-
-
-/*
- * proc.c
- */
-#ifdef CONFIG_PROC_FS
-extern int rxrpc_proc_init(void);
-extern void rxrpc_proc_cleanup(void);
-#endif
-
-/*
- * transport.c
- */
-extern struct list_head rxrpc_proc_transports;
-extern struct rw_semaphore rxrpc_proc_transports_sem;
-
-#endif /* RXRPC_INTERNAL_H */
diff --git a/net/rxrpc/krxiod.c b/net/rxrpc/krxiod.c
deleted file mode 100644
index bbbcd6c..0000000
--- a/net/rxrpc/krxiod.c
+++ /dev/null
@@ -1,262 +0,0 @@
-/* krxiod.c: Rx I/O daemon
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#include <linux/sched.h>
-#include <linux/completion.h>
-#include <linux/spinlock.h>
-#include <linux/init.h>
-#include <linux/freezer.h>
-#include <rxrpc/krxiod.h>
-#include <rxrpc/transport.h>
-#include <rxrpc/peer.h>
-#include <rxrpc/call.h>
-#include "internal.h"
-
-static DECLARE_WAIT_QUEUE_HEAD(rxrpc_krxiod_sleepq);
-static DECLARE_COMPLETION(rxrpc_krxiod_dead);
-
-static atomic_t rxrpc_krxiod_qcount = ATOMIC_INIT(0);
-
-static LIST_HEAD(rxrpc_krxiod_transportq);
-static DEFINE_SPINLOCK(rxrpc_krxiod_transportq_lock);
-
-static LIST_HEAD(rxrpc_krxiod_callq);
-static DEFINE_SPINLOCK(rxrpc_krxiod_callq_lock);
-
-static volatile int rxrpc_krxiod_die;
-
-/*****************************************************************************/
-/*
- * Rx I/O daemon
- */
-static int rxrpc_krxiod(void *arg)
-{
-	DECLARE_WAITQUEUE(krxiod,current);
-
-	printk("Started krxiod %d\n",current->pid);
-
-	daemonize("krxiod");
-
-	/* loop around waiting for work to do */
-	do {
-		/* wait for work or to be told to exit */
-		_debug("### Begin Wait");
-		if (!atomic_read(&rxrpc_krxiod_qcount)) {
-			set_current_state(TASK_INTERRUPTIBLE);
-
-			add_wait_queue(&rxrpc_krxiod_sleepq, &krxiod);
-
-			for (;;) {
-				set_current_state(TASK_INTERRUPTIBLE);
-				if (atomic_read(&rxrpc_krxiod_qcount) ||
-				    rxrpc_krxiod_die ||
-				    signal_pending(current))
-					break;
-
-				schedule();
-			}
-
-			remove_wait_queue(&rxrpc_krxiod_sleepq, &krxiod);
-			set_current_state(TASK_RUNNING);
-		}
-		_debug("### End Wait");
-
-		/* do work if been given some to do */
-		_debug("### Begin Work");
-
-		/* see if there's a transport in need of attention */
-		if (!list_empty(&rxrpc_krxiod_transportq)) {
-			struct rxrpc_transport *trans = NULL;
-
-			spin_lock_irq(&rxrpc_krxiod_transportq_lock);
-
-			if (!list_empty(&rxrpc_krxiod_transportq)) {
-				trans = list_entry(
-					rxrpc_krxiod_transportq.next,
-					struct rxrpc_transport,
-					krxiodq_link);
-
-				list_del_init(&trans->krxiodq_link);
-				atomic_dec(&rxrpc_krxiod_qcount);
-
-				/* make sure it hasn't gone away and doesn't go
-				 * away */
-				if (atomic_read(&trans->usage)>0)
-					rxrpc_get_transport(trans);
-				else
-					trans = NULL;
-			}
-
-			spin_unlock_irq(&rxrpc_krxiod_transportq_lock);
-
-			if (trans) {
-				rxrpc_trans_receive_packet(trans);
-				rxrpc_put_transport(trans);
-			}
-		}
-
-		/* see if there's a call in need of attention */
-		if (!list_empty(&rxrpc_krxiod_callq)) {
-			struct rxrpc_call *call = NULL;
-
-			spin_lock_irq(&rxrpc_krxiod_callq_lock);
-
-			if (!list_empty(&rxrpc_krxiod_callq)) {
-				call = list_entry(rxrpc_krxiod_callq.next,
-						  struct rxrpc_call,
-						  rcv_krxiodq_lk);
-				list_del_init(&call->rcv_krxiodq_lk);
-				atomic_dec(&rxrpc_krxiod_qcount);
-
-				/* make sure it hasn't gone away and doesn't go
-				 * away */
-				if (atomic_read(&call->usage) > 0) {
-					_debug("@@@ KRXIOD"
-					       " Begin Attend Call %p", call);
-					rxrpc_get_call(call);
-				}
-				else {
-					call = NULL;
-				}
-			}
-
-			spin_unlock_irq(&rxrpc_krxiod_callq_lock);
-
-			if (call) {
-				rxrpc_call_do_stuff(call);
-				rxrpc_put_call(call);
-				_debug("@@@ KRXIOD End Attend Call %p", call);
-			}
-		}
-
-		_debug("### End Work");
-
-		try_to_freeze();
-
-		/* discard pending signals */
-		rxrpc_discard_my_signals();
-
-	} while (!rxrpc_krxiod_die);
-
-	/* and that's all */
-	complete_and_exit(&rxrpc_krxiod_dead, 0);
-
-} /* end rxrpc_krxiod() */
-
-/*****************************************************************************/
-/*
- * start up a krxiod daemon
- */
-int __init rxrpc_krxiod_init(void)
-{
-	return kernel_thread(rxrpc_krxiod, NULL, 0);
-
-} /* end rxrpc_krxiod_init() */
-
-/*****************************************************************************/
-/*
- * kill the krxiod daemon and wait for it to complete
- */
-void rxrpc_krxiod_kill(void)
-{
-	rxrpc_krxiod_die = 1;
-	wake_up_all(&rxrpc_krxiod_sleepq);
-	wait_for_completion(&rxrpc_krxiod_dead);
-
-} /* end rxrpc_krxiod_kill() */
-
-/*****************************************************************************/
-/*
- * queue a transport for attention by krxiod
- */
-void rxrpc_krxiod_queue_transport(struct rxrpc_transport *trans)
-{
-	unsigned long flags;
-
-	_enter("");
-
-	if (list_empty(&trans->krxiodq_link)) {
-		spin_lock_irqsave(&rxrpc_krxiod_transportq_lock, flags);
-
-		if (list_empty(&trans->krxiodq_link)) {
-			if (atomic_read(&trans->usage) > 0) {
-				list_add_tail(&trans->krxiodq_link,
-					      &rxrpc_krxiod_transportq);
-				atomic_inc(&rxrpc_krxiod_qcount);
-			}
-		}
-
-		spin_unlock_irqrestore(&rxrpc_krxiod_transportq_lock, flags);
-		wake_up_all(&rxrpc_krxiod_sleepq);
-	}
-
-	_leave("");
-
-} /* end rxrpc_krxiod_queue_transport() */
-
-/*****************************************************************************/
-/*
- * dequeue a transport from krxiod's attention queue
- */
-void rxrpc_krxiod_dequeue_transport(struct rxrpc_transport *trans)
-{
-	unsigned long flags;
-
-	_enter("");
-
-	spin_lock_irqsave(&rxrpc_krxiod_transportq_lock, flags);
-	if (!list_empty(&trans->krxiodq_link)) {
-		list_del_init(&trans->krxiodq_link);
-		atomic_dec(&rxrpc_krxiod_qcount);
-	}
-	spin_unlock_irqrestore(&rxrpc_krxiod_transportq_lock, flags);
-
-	_leave("");
-
-} /* end rxrpc_krxiod_dequeue_transport() */
-
-/*****************************************************************************/
-/*
- * queue a call for attention by krxiod
- */
-void rxrpc_krxiod_queue_call(struct rxrpc_call *call)
-{
-	unsigned long flags;
-
-	if (list_empty(&call->rcv_krxiodq_lk)) {
-		spin_lock_irqsave(&rxrpc_krxiod_callq_lock, flags);
-		if (atomic_read(&call->usage) > 0) {
-			list_add_tail(&call->rcv_krxiodq_lk,
-				      &rxrpc_krxiod_callq);
-			atomic_inc(&rxrpc_krxiod_qcount);
-		}
-		spin_unlock_irqrestore(&rxrpc_krxiod_callq_lock, flags);
-	}
-	wake_up_all(&rxrpc_krxiod_sleepq);
-
-} /* end rxrpc_krxiod_queue_call() */
-
-/*****************************************************************************/
-/*
- * dequeue a call from krxiod's attention queue
- */
-void rxrpc_krxiod_dequeue_call(struct rxrpc_call *call)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&rxrpc_krxiod_callq_lock, flags);
-	if (!list_empty(&call->rcv_krxiodq_lk)) {
-		list_del_init(&call->rcv_krxiodq_lk);
-		atomic_dec(&rxrpc_krxiod_qcount);
-	}
-	spin_unlock_irqrestore(&rxrpc_krxiod_callq_lock, flags);
-
-} /* end rxrpc_krxiod_dequeue_call() */
diff --git a/net/rxrpc/krxsecd.c b/net/rxrpc/krxsecd.c
deleted file mode 100644
index 9a1e7f5..0000000
--- a/net/rxrpc/krxsecd.c
+++ /dev/null
@@ -1,270 +0,0 @@
-/* krxsecd.c: Rx security daemon
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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 daemon deals with:
- * - consulting the application as to whether inbound peers and calls should be authorised
- * - generating security challenges for inbound connections
- * - responding to security challenges on outbound connections
- */
-
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/completion.h>
-#include <linux/spinlock.h>
-#include <linux/init.h>
-#include <rxrpc/krxsecd.h>
-#include <rxrpc/transport.h>
-#include <rxrpc/connection.h>
-#include <rxrpc/message.h>
-#include <rxrpc/peer.h>
-#include <rxrpc/call.h>
-#include <linux/udp.h>
-#include <linux/ip.h>
-#include <linux/freezer.h>
-#include <net/sock.h>
-#include "internal.h"
-
-static DECLARE_WAIT_QUEUE_HEAD(rxrpc_krxsecd_sleepq);
-static DECLARE_COMPLETION(rxrpc_krxsecd_dead);
-static volatile int rxrpc_krxsecd_die;
-
-static atomic_t rxrpc_krxsecd_qcount;
-
-/* queue of unprocessed inbound messages with seqno #1 and
- * RXRPC_CLIENT_INITIATED flag set */
-static LIST_HEAD(rxrpc_krxsecd_initmsgq);
-static DEFINE_SPINLOCK(rxrpc_krxsecd_initmsgq_lock);
-
-static void rxrpc_krxsecd_process_incoming_call(struct rxrpc_message *msg);
-
-/*****************************************************************************/
-/*
- * Rx security daemon
- */
-static int rxrpc_krxsecd(void *arg)
-{
-	DECLARE_WAITQUEUE(krxsecd, current);
-
-	int die;
-
-	printk("Started krxsecd %d\n", current->pid);
-
-	daemonize("krxsecd");
-
-	/* loop around waiting for work to do */
-	do {
-		/* wait for work or to be told to exit */
-		_debug("### Begin Wait");
-		if (!atomic_read(&rxrpc_krxsecd_qcount)) {
-			set_current_state(TASK_INTERRUPTIBLE);
-
-			add_wait_queue(&rxrpc_krxsecd_sleepq, &krxsecd);
-
-			for (;;) {
-				set_current_state(TASK_INTERRUPTIBLE);
-				if (atomic_read(&rxrpc_krxsecd_qcount) ||
-				    rxrpc_krxsecd_die ||
-				    signal_pending(current))
-					break;
-
-				schedule();
-			}
-
-			remove_wait_queue(&rxrpc_krxsecd_sleepq, &krxsecd);
-			set_current_state(TASK_RUNNING);
-		}
-		die = rxrpc_krxsecd_die;
-		_debug("### End Wait");
-
-		/* see if there're incoming calls in need of authenticating */
-		_debug("### Begin Inbound Calls");
-
-		if (!list_empty(&rxrpc_krxsecd_initmsgq)) {
-			struct rxrpc_message *msg = NULL;
-
-			spin_lock(&rxrpc_krxsecd_initmsgq_lock);
-
-			if (!list_empty(&rxrpc_krxsecd_initmsgq)) {
-				msg = list_entry(rxrpc_krxsecd_initmsgq.next,
-						 struct rxrpc_message, link);
-				list_del_init(&msg->link);
-				atomic_dec(&rxrpc_krxsecd_qcount);
-			}
-
-			spin_unlock(&rxrpc_krxsecd_initmsgq_lock);
-
-			if (msg) {
-				rxrpc_krxsecd_process_incoming_call(msg);
-				rxrpc_put_message(msg);
-			}
-		}
-
-		_debug("### End Inbound Calls");
-
-		try_to_freeze();
-
-		/* discard pending signals */
-		rxrpc_discard_my_signals();
-
-	} while (!die);
-
-	/* and that's all */
-	complete_and_exit(&rxrpc_krxsecd_dead, 0);
-
-} /* end rxrpc_krxsecd() */
-
-/*****************************************************************************/
-/*
- * start up a krxsecd daemon
- */
-int __init rxrpc_krxsecd_init(void)
-{
-	return kernel_thread(rxrpc_krxsecd, NULL, 0);
-
-} /* end rxrpc_krxsecd_init() */
-
-/*****************************************************************************/
-/*
- * kill the krxsecd daemon and wait for it to complete
- */
-void rxrpc_krxsecd_kill(void)
-{
-	rxrpc_krxsecd_die = 1;
-	wake_up_all(&rxrpc_krxsecd_sleepq);
-	wait_for_completion(&rxrpc_krxsecd_dead);
-
-} /* end rxrpc_krxsecd_kill() */
-
-/*****************************************************************************/
-/*
- * clear all pending incoming calls for the specified transport
- */
-void rxrpc_krxsecd_clear_transport(struct rxrpc_transport *trans)
-{
-	LIST_HEAD(tmp);
-
-	struct rxrpc_message *msg;
-	struct list_head *_p, *_n;
-
-	_enter("%p",trans);
-
-	/* move all the messages for this transport onto a temp list */
-	spin_lock(&rxrpc_krxsecd_initmsgq_lock);
-
-	list_for_each_safe(_p, _n, &rxrpc_krxsecd_initmsgq) {
-		msg = list_entry(_p, struct rxrpc_message, link);
-		if (msg->trans == trans) {
-			list_move_tail(&msg->link, &tmp);
-			atomic_dec(&rxrpc_krxsecd_qcount);
-		}
-	}
-
-	spin_unlock(&rxrpc_krxsecd_initmsgq_lock);
-
-	/* zap all messages on the temp list */
-	while (!list_empty(&tmp)) {
-		msg = list_entry(tmp.next, struct rxrpc_message, link);
-		list_del_init(&msg->link);
-		rxrpc_put_message(msg);
-	}
-
-	_leave("");
-} /* end rxrpc_krxsecd_clear_transport() */
-
-/*****************************************************************************/
-/*
- * queue a message on the incoming calls list
- */
-void rxrpc_krxsecd_queue_incoming_call(struct rxrpc_message *msg)
-{
-	_enter("%p", msg);
-
-	/* queue for processing by krxsecd */
-	spin_lock(&rxrpc_krxsecd_initmsgq_lock);
-
-	if (!rxrpc_krxsecd_die) {
-		rxrpc_get_message(msg);
-		list_add_tail(&msg->link, &rxrpc_krxsecd_initmsgq);
-		atomic_inc(&rxrpc_krxsecd_qcount);
-	}
-
-	spin_unlock(&rxrpc_krxsecd_initmsgq_lock);
-
-	wake_up(&rxrpc_krxsecd_sleepq);
-
-	_leave("");
-} /* end rxrpc_krxsecd_queue_incoming_call() */
-
-/*****************************************************************************/
-/*
- * process the initial message of an incoming call
- */
-void rxrpc_krxsecd_process_incoming_call(struct rxrpc_message *msg)
-{
-	struct rxrpc_transport *trans = msg->trans;
-	struct rxrpc_service *srv;
-	struct rxrpc_call *call;
-	struct list_head *_p;
-	unsigned short sid;
-	int ret;
-
-	_enter("%p{tr=%p}", msg, trans);
-
-	ret = rxrpc_incoming_call(msg->conn, msg, &call);
-	if (ret < 0)
-		goto out;
-
-	/* find the matching service on the transport */
-	sid = ntohs(msg->hdr.serviceId);
-	srv = NULL;
-
-	spin_lock(&trans->lock);
-	list_for_each(_p, &trans->services) {
-		srv = list_entry(_p, struct rxrpc_service, link);
-		if (srv->service_id == sid && try_module_get(srv->owner)) {
-			/* found a match (made sure it won't vanish) */
-			_debug("found service '%s'", srv->name);
-			call->owner = srv->owner;
-			break;
-		}
-	}
-	spin_unlock(&trans->lock);
-
-	/* report the new connection
-	 * - the func must inc the call's usage count to keep it
-	 */
-	ret = -ENOENT;
-	if (_p != &trans->services) {
-		/* attempt to accept the call */
-		call->conn->service = srv;
-		call->app_attn_func = srv->attn_func;
-		call->app_error_func = srv->error_func;
-		call->app_aemap_func = srv->aemap_func;
-
-		ret = srv->new_call(call);
-
-		/* send an abort if an error occurred */
-		if (ret < 0) {
-			rxrpc_call_abort(call, ret);
-		}
-		else {
-			/* formally receive and ACK the new packet */
-			ret = rxrpc_conn_receive_call_packet(call->conn,
-							     call, msg);
-		}
-	}
-
-	rxrpc_put_call(call);
- out:
-	if (ret < 0)
-		rxrpc_trans_immediate_abort(trans, msg, ret);
-
-	_leave(" (%d)", ret);
-} /* end rxrpc_krxsecd_process_incoming_call() */
diff --git a/net/rxrpc/krxtimod.c b/net/rxrpc/krxtimod.c
deleted file mode 100644
index 9a9b613..0000000
--- a/net/rxrpc/krxtimod.c
+++ /dev/null
@@ -1,204 +0,0 @@
-/* krxtimod.c: RXRPC timeout daemon
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/completion.h>
-#include <linux/freezer.h>
-#include <rxrpc/rxrpc.h>
-#include <rxrpc/krxtimod.h>
-#include <asm/errno.h>
-#include "internal.h"
-
-static DECLARE_COMPLETION(krxtimod_alive);
-static DECLARE_COMPLETION(krxtimod_dead);
-static DECLARE_WAIT_QUEUE_HEAD(krxtimod_sleepq);
-static int krxtimod_die;
-
-static LIST_HEAD(krxtimod_list);
-static DEFINE_SPINLOCK(krxtimod_lock);
-
-static int krxtimod(void *arg);
-
-/*****************************************************************************/
-/*
- * start the timeout daemon
- */
-int rxrpc_krxtimod_start(void)
-{
-	int ret;
-
-	ret = kernel_thread(krxtimod, NULL, 0);
-	if (ret < 0)
-		return ret;
-
-	wait_for_completion(&krxtimod_alive);
-
-	return ret;
-} /* end rxrpc_krxtimod_start() */
-
-/*****************************************************************************/
-/*
- * stop the timeout daemon
- */
-void rxrpc_krxtimod_kill(void)
-{
-	/* get rid of my daemon */
-	krxtimod_die = 1;
-	wake_up(&krxtimod_sleepq);
-	wait_for_completion(&krxtimod_dead);
-
-} /* end rxrpc_krxtimod_kill() */
-
-/*****************************************************************************/
-/*
- * timeout processing daemon
- */
-static int krxtimod(void *arg)
-{
-	DECLARE_WAITQUEUE(myself, current);
-
-	rxrpc_timer_t *timer;
-
-	printk("Started krxtimod %d\n", current->pid);
-
-	daemonize("krxtimod");
-
-	complete(&krxtimod_alive);
-
-	/* loop around looking for things to attend to */
- loop:
-	set_current_state(TASK_INTERRUPTIBLE);
-	add_wait_queue(&krxtimod_sleepq, &myself);
-
-	for (;;) {
-		unsigned long jif;
-		long timeout;
-
-		/* deal with the server being asked to die */
-		if (krxtimod_die) {
-			remove_wait_queue(&krxtimod_sleepq, &myself);
-			_leave("");
-			complete_and_exit(&krxtimod_dead, 0);
-		}
-
-		try_to_freeze();
-
-		/* discard pending signals */
-		rxrpc_discard_my_signals();
-
-		/* work out the time to elapse before the next event */
-		spin_lock(&krxtimod_lock);
-		if (list_empty(&krxtimod_list)) {
-			timeout = MAX_SCHEDULE_TIMEOUT;
-		}
-		else {
-			timer = list_entry(krxtimod_list.next,
-					   rxrpc_timer_t, link);
-			timeout = timer->timo_jif;
-			jif = jiffies;
-
-			if (time_before_eq((unsigned long) timeout, jif))
-				goto immediate;
-
-			else {
-				timeout = (long) timeout - (long) jiffies;
-			}
-		}
-		spin_unlock(&krxtimod_lock);
-
-		schedule_timeout(timeout);
-
-		set_current_state(TASK_INTERRUPTIBLE);
-	}
-
-	/* the thing on the front of the queue needs processing
-	 * - we come here with the lock held and timer pointing to the expired
-	 *   entry
-	 */
- immediate:
-	remove_wait_queue(&krxtimod_sleepq, &myself);
-	set_current_state(TASK_RUNNING);
-
-	_debug("@@@ Begin Timeout of %p", timer);
-
-	/* dequeue the timer */
-	list_del_init(&timer->link);
-	spin_unlock(&krxtimod_lock);
-
-	/* call the timeout function */
-	timer->ops->timed_out(timer);
-
-	_debug("@@@ End Timeout");
-	goto loop;
-
-} /* end krxtimod() */
-
-/*****************************************************************************/
-/*
- * (re-)queue a timer
- */
-void rxrpc_krxtimod_add_timer(rxrpc_timer_t *timer, unsigned long timeout)
-{
-	struct list_head *_p;
-	rxrpc_timer_t *ptimer;
-
-	_enter("%p,%lu", timer, timeout);
-
-	spin_lock(&krxtimod_lock);
-
-	list_del(&timer->link);
-
-	/* the timer was deferred or reset - put it back in the queue at the
-	 * right place */
-	timer->timo_jif = jiffies + timeout;
-
-	list_for_each(_p, &krxtimod_list) {
-		ptimer = list_entry(_p, rxrpc_timer_t, link);
-		if (time_before(timer->timo_jif, ptimer->timo_jif))
-			break;
-	}
-
-	list_add_tail(&timer->link, _p); /* insert before stopping point */
-
-	spin_unlock(&krxtimod_lock);
-
-	wake_up(&krxtimod_sleepq);
-
-	_leave("");
-} /* end rxrpc_krxtimod_add_timer() */
-
-/*****************************************************************************/
-/*
- * dequeue a timer
- * - returns 0 if the timer was deleted or -ENOENT if it wasn't queued
- */
-int rxrpc_krxtimod_del_timer(rxrpc_timer_t *timer)
-{
-	int ret = 0;
-
-	_enter("%p", timer);
-
-	spin_lock(&krxtimod_lock);
-
-	if (list_empty(&timer->link))
-		ret = -ENOENT;
-	else
-		list_del_init(&timer->link);
-
-	spin_unlock(&krxtimod_lock);
-
-	wake_up(&krxtimod_sleepq);
-
-	_leave(" = %d", ret);
-	return ret;
-} /* end rxrpc_krxtimod_del_timer() */
diff --git a/net/rxrpc/main.c b/net/rxrpc/main.c
deleted file mode 100644
index baec1f7..0000000
--- a/net/rxrpc/main.c
+++ /dev/null
@@ -1,180 +0,0 @@
-/* main.c: Rx RPC interface
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <rxrpc/rxrpc.h>
-#include <rxrpc/krxiod.h>
-#include <rxrpc/krxsecd.h>
-#include <rxrpc/krxtimod.h>
-#include <rxrpc/transport.h>
-#include <rxrpc/connection.h>
-#include <rxrpc/call.h>
-#include <rxrpc/message.h>
-#include "internal.h"
-
-MODULE_DESCRIPTION("Rx RPC implementation");
-MODULE_AUTHOR("Red Hat, Inc.");
-MODULE_LICENSE("GPL");
-
-__be32 rxrpc_epoch;
-
-/*****************************************************************************/
-/*
- * initialise the Rx module
- */
-static int __init rxrpc_initialise(void)
-{
-	int ret;
-
-	/* my epoch value */
-	rxrpc_epoch = htonl(xtime.tv_sec);
-
-	/* register the /proc interface */
-#ifdef CONFIG_PROC_FS
-	ret = rxrpc_proc_init();
-	if (ret<0)
-		return ret;
-#endif
-
-	/* register the sysctl files */
-#ifdef CONFIG_SYSCTL
-	ret = rxrpc_sysctl_init();
-	if (ret<0)
-		goto error_proc;
-#endif
-
-	/* start the krxtimod daemon */
-	ret = rxrpc_krxtimod_start();
-	if (ret<0)
-		goto error_sysctl;
-
-	/* start the krxiod daemon */
-	ret = rxrpc_krxiod_init();
-	if (ret<0)
-		goto error_krxtimod;
-
-	/* start the krxsecd daemon */
-	ret = rxrpc_krxsecd_init();
-	if (ret<0)
-		goto error_krxiod;
-
-	kdebug("\n\n");
-
-	return 0;
-
- error_krxiod:
-	rxrpc_krxiod_kill();
- error_krxtimod:
-	rxrpc_krxtimod_kill();
- error_sysctl:
-#ifdef CONFIG_SYSCTL
-	rxrpc_sysctl_cleanup();
- error_proc:
-#endif
-#ifdef CONFIG_PROC_FS
-	rxrpc_proc_cleanup();
-#endif
-	return ret;
-} /* end rxrpc_initialise() */
-
-module_init(rxrpc_initialise);
-
-/*****************************************************************************/
-/*
- * clean up the Rx module
- */
-static void __exit rxrpc_cleanup(void)
-{
-	kenter("");
-
-	__RXACCT(printk("Outstanding Messages   : %d\n",
-			atomic_read(&rxrpc_message_count)));
-	__RXACCT(printk("Outstanding Calls      : %d\n",
-			atomic_read(&rxrpc_call_count)));
-	__RXACCT(printk("Outstanding Connections: %d\n",
-			atomic_read(&rxrpc_connection_count)));
-	__RXACCT(printk("Outstanding Peers      : %d\n",
-			atomic_read(&rxrpc_peer_count)));
-	__RXACCT(printk("Outstanding Transports : %d\n",
-			atomic_read(&rxrpc_transport_count)));
-
-	rxrpc_krxsecd_kill();
-	rxrpc_krxiod_kill();
-	rxrpc_krxtimod_kill();
-#ifdef CONFIG_SYSCTL
-	rxrpc_sysctl_cleanup();
-#endif
-#ifdef CONFIG_PROC_FS
-	rxrpc_proc_cleanup();
-#endif
-
-	__RXACCT(printk("Outstanding Messages   : %d\n",
-			atomic_read(&rxrpc_message_count)));
-	__RXACCT(printk("Outstanding Calls      : %d\n",
-			atomic_read(&rxrpc_call_count)));
-	__RXACCT(printk("Outstanding Connections: %d\n",
-			atomic_read(&rxrpc_connection_count)));
-	__RXACCT(printk("Outstanding Peers      : %d\n",
-			atomic_read(&rxrpc_peer_count)));
-	__RXACCT(printk("Outstanding Transports : %d\n",
-			atomic_read(&rxrpc_transport_count)));
-
-	kleave("");
-} /* end rxrpc_cleanup() */
-
-module_exit(rxrpc_cleanup);
-
-/*****************************************************************************/
-/*
- * clear the dead space between task_struct and kernel stack
- * - called by supplying -finstrument-functions to gcc
- */
-#if 0
-void __cyg_profile_func_enter (void *this_fn, void *call_site)
-__attribute__((no_instrument_function));
-
-void __cyg_profile_func_enter (void *this_fn, void *call_site)
-{
-       asm volatile("  movl    %%esp,%%edi     \n"
-		    "  andl    %0,%%edi        \n"
-		    "  addl    %1,%%edi        \n"
-		    "  movl    %%esp,%%ecx     \n"
-		    "  subl    %%edi,%%ecx     \n"
-		    "  shrl    $2,%%ecx        \n"
-		    "  movl    $0xedededed,%%eax     \n"
-		    "  rep stosl               \n"
-		    :
-		    : "i"(~(THREAD_SIZE-1)), "i"(sizeof(struct thread_info))
-		    : "eax", "ecx", "edi", "memory", "cc"
-		    );
-}
-
-void __cyg_profile_func_exit(void *this_fn, void *call_site)
-__attribute__((no_instrument_function));
-
-void __cyg_profile_func_exit(void *this_fn, void *call_site)
-{
-       asm volatile("  movl    %%esp,%%edi     \n"
-		    "  andl    %0,%%edi        \n"
-		    "  addl    %1,%%edi        \n"
-		    "  movl    %%esp,%%ecx     \n"
-		    "  subl    %%edi,%%ecx     \n"
-		    "  shrl    $2,%%ecx        \n"
-		    "  movl    $0xdadadada,%%eax     \n"
-		    "  rep stosl               \n"
-		    :
-		    : "i"(~(THREAD_SIZE-1)), "i"(sizeof(struct thread_info))
-		    : "eax", "ecx", "edi", "memory", "cc"
-		    );
-}
-#endif
diff --git a/net/rxrpc/peer.c b/net/rxrpc/peer.c
deleted file mode 100644
index 8a27515..0000000
--- a/net/rxrpc/peer.c
+++ /dev/null
@@ -1,398 +0,0 @@
-/* peer.c: Rx RPC peer management
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <rxrpc/rxrpc.h>
-#include <rxrpc/transport.h>
-#include <rxrpc/peer.h>
-#include <rxrpc/connection.h>
-#include <rxrpc/call.h>
-#include <rxrpc/message.h>
-#include <linux/udp.h>
-#include <linux/ip.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/div64.h>
-#include "internal.h"
-
-__RXACCT_DECL(atomic_t rxrpc_peer_count);
-LIST_HEAD(rxrpc_peers);
-DECLARE_RWSEM(rxrpc_peers_sem);
-unsigned long rxrpc_peer_timeout = 12 * 60 * 60;
-
-static void rxrpc_peer_do_timeout(struct rxrpc_peer *peer);
-
-static void __rxrpc_peer_timeout(rxrpc_timer_t *timer)
-{
-	struct rxrpc_peer *peer =
-		list_entry(timer, struct rxrpc_peer, timeout);
-
-	_debug("Rx PEER TIMEOUT [%p{u=%d}]", peer, atomic_read(&peer->usage));
-
-	rxrpc_peer_do_timeout(peer);
-}
-
-static const struct rxrpc_timer_ops rxrpc_peer_timer_ops = {
-	.timed_out	= __rxrpc_peer_timeout,
-};
-
-/*****************************************************************************/
-/*
- * create a peer record
- */
-static int __rxrpc_create_peer(struct rxrpc_transport *trans, __be32 addr,
-			       struct rxrpc_peer **_peer)
-{
-	struct rxrpc_peer *peer;
-
-	_enter("%p,%08x", trans, ntohl(addr));
-
-	/* allocate and initialise a peer record */
-	peer = kzalloc(sizeof(struct rxrpc_peer), GFP_KERNEL);
-	if (!peer) {
-		_leave(" = -ENOMEM");
-		return -ENOMEM;
-	}
-
-	atomic_set(&peer->usage, 1);
-
-	INIT_LIST_HEAD(&peer->link);
-	INIT_LIST_HEAD(&peer->proc_link);
-	INIT_LIST_HEAD(&peer->conn_idlist);
-	INIT_LIST_HEAD(&peer->conn_active);
-	INIT_LIST_HEAD(&peer->conn_graveyard);
-	spin_lock_init(&peer->conn_gylock);
-	init_waitqueue_head(&peer->conn_gy_waitq);
-	rwlock_init(&peer->conn_idlock);
-	rwlock_init(&peer->conn_lock);
-	atomic_set(&peer->conn_count, 0);
-	spin_lock_init(&peer->lock);
-	rxrpc_timer_init(&peer->timeout, &rxrpc_peer_timer_ops);
-
-	peer->addr.s_addr = addr;
-
-	peer->trans = trans;
-	peer->ops = trans->peer_ops;
-
-	__RXACCT(atomic_inc(&rxrpc_peer_count));
-	*_peer = peer;
-	_leave(" = 0 (%p)", peer);
-
-	return 0;
-} /* end __rxrpc_create_peer() */
-
-/*****************************************************************************/
-/*
- * find a peer record on the specified transport
- * - returns (if successful) with peer record usage incremented
- * - resurrects it from the graveyard if found there
- */
-int rxrpc_peer_lookup(struct rxrpc_transport *trans, __be32 addr,
-		      struct rxrpc_peer **_peer)
-{
-	struct rxrpc_peer *peer, *candidate = NULL;
-	struct list_head *_p;
-	int ret;
-
-	_enter("%p{%hu},%08x", trans, trans->port, ntohl(addr));
-
-	/* [common case] search the transport's active list first */
-	read_lock(&trans->peer_lock);
-	list_for_each(_p, &trans->peer_active) {
-		peer = list_entry(_p, struct rxrpc_peer, link);
-		if (peer->addr.s_addr == addr)
-			goto found_active;
-	}
-	read_unlock(&trans->peer_lock);
-
-	/* [uncommon case] not active - create a candidate for a new record */
-	ret = __rxrpc_create_peer(trans, addr, &candidate);
-	if (ret < 0) {
-		_leave(" = %d", ret);
-		return ret;
-	}
-
-	/* search the active list again, just in case it appeared whilst we
-	 * were busy */
-	write_lock(&trans->peer_lock);
-	list_for_each(_p, &trans->peer_active) {
-		peer = list_entry(_p, struct rxrpc_peer, link);
-		if (peer->addr.s_addr == addr)
-			goto found_active_second_chance;
-	}
-
-	/* search the transport's graveyard list */
-	spin_lock(&trans->peer_gylock);
-	list_for_each(_p, &trans->peer_graveyard) {
-		peer = list_entry(_p, struct rxrpc_peer, link);
-		if (peer->addr.s_addr == addr)
-			goto found_in_graveyard;
-	}
-	spin_unlock(&trans->peer_gylock);
-
-	/* we can now add the new candidate to the list
-	 * - tell the application layer that this peer has been added
-	 */
-	rxrpc_get_transport(trans);
-	peer = candidate;
-	candidate = NULL;
-
-	if (peer->ops && peer->ops->adding) {
-		ret = peer->ops->adding(peer);
-		if (ret < 0) {
-			write_unlock(&trans->peer_lock);
-			__RXACCT(atomic_dec(&rxrpc_peer_count));
-			kfree(peer);
-			rxrpc_put_transport(trans);
-			_leave(" = %d", ret);
-			return ret;
-		}
-	}
-
-	atomic_inc(&trans->peer_count);
-
- make_active:
-	list_add_tail(&peer->link, &trans->peer_active);
-
- success_uwfree:
-	write_unlock(&trans->peer_lock);
-
-	if (candidate) {
-		__RXACCT(atomic_dec(&rxrpc_peer_count));
-		kfree(candidate);
-	}
-
-	if (list_empty(&peer->proc_link)) {
-		down_write(&rxrpc_peers_sem);
-		list_add_tail(&peer->proc_link, &rxrpc_peers);
-		up_write(&rxrpc_peers_sem);
-	}
-
- success:
-	*_peer = peer;
-
-	_leave(" = 0 (%p{u=%d cc=%d})",
-	       peer,
-	       atomic_read(&peer->usage),
-	       atomic_read(&peer->conn_count));
-	return 0;
-
-	/* handle the peer being found in the active list straight off */
- found_active:
-	rxrpc_get_peer(peer);
-	read_unlock(&trans->peer_lock);
-	goto success;
-
-	/* handle resurrecting a peer from the graveyard */
- found_in_graveyard:
-	rxrpc_get_peer(peer);
-	rxrpc_get_transport(peer->trans);
-	rxrpc_krxtimod_del_timer(&peer->timeout);
-	list_del_init(&peer->link);
-	spin_unlock(&trans->peer_gylock);
-	goto make_active;
-
-	/* handle finding the peer on the second time through the active
-	 * list */
- found_active_second_chance:
-	rxrpc_get_peer(peer);
-	goto success_uwfree;
-
-} /* end rxrpc_peer_lookup() */
-
-/*****************************************************************************/
-/*
- * finish with a peer record
- * - it gets sent to the graveyard from where it can be resurrected or timed
- *   out
- */
-void rxrpc_put_peer(struct rxrpc_peer *peer)
-{
-	struct rxrpc_transport *trans = peer->trans;
-
-	_enter("%p{cc=%d a=%08x}",
-	       peer,
-	       atomic_read(&peer->conn_count),
-	       ntohl(peer->addr.s_addr));
-
-	/* sanity check */
-	if (atomic_read(&peer->usage) <= 0)
-		BUG();
-
-	write_lock(&trans->peer_lock);
-	spin_lock(&trans->peer_gylock);
-	if (likely(!atomic_dec_and_test(&peer->usage))) {
-		spin_unlock(&trans->peer_gylock);
-		write_unlock(&trans->peer_lock);
-		_leave("");
-		return;
-	}
-
-	/* move to graveyard queue */
-	list_del(&peer->link);
-	write_unlock(&trans->peer_lock);
-
-	list_add_tail(&peer->link, &trans->peer_graveyard);
-
-	BUG_ON(!list_empty(&peer->conn_active));
-
-	rxrpc_krxtimod_add_timer(&peer->timeout, rxrpc_peer_timeout * HZ);
-
-	spin_unlock(&trans->peer_gylock);
-
-	rxrpc_put_transport(trans);
-
-	_leave(" [killed]");
-} /* end rxrpc_put_peer() */
-
-/*****************************************************************************/
-/*
- * handle a peer timing out in the graveyard
- * - called from krxtimod
- */
-static void rxrpc_peer_do_timeout(struct rxrpc_peer *peer)
-{
-	struct rxrpc_transport *trans = peer->trans;
-
-	_enter("%p{u=%d cc=%d a=%08x}",
-	       peer,
-	       atomic_read(&peer->usage),
-	       atomic_read(&peer->conn_count),
-	       ntohl(peer->addr.s_addr));
-
-	BUG_ON(atomic_read(&peer->usage) < 0);
-
-	/* remove from graveyard if still dead */
-	spin_lock(&trans->peer_gylock);
-	if (atomic_read(&peer->usage) == 0)
-		list_del_init(&peer->link);
-	else
-		peer = NULL;
-	spin_unlock(&trans->peer_gylock);
-
-	if (!peer) {
-		_leave("");
-		return; /* resurrected */
-	}
-
-	/* clear all connections on this peer */
-	rxrpc_conn_clearall(peer);
-
-	BUG_ON(!list_empty(&peer->conn_active));
-	BUG_ON(!list_empty(&peer->conn_graveyard));
-
-	/* inform the application layer */
-	if (peer->ops && peer->ops->discarding)
-		peer->ops->discarding(peer);
-
-	if (!list_empty(&peer->proc_link)) {
-		down_write(&rxrpc_peers_sem);
-		list_del(&peer->proc_link);
-		up_write(&rxrpc_peers_sem);
-	}
-
-	__RXACCT(atomic_dec(&rxrpc_peer_count));
-	kfree(peer);
-
-	/* if the graveyard is now empty, wake up anyone waiting for that */
-	if (atomic_dec_and_test(&trans->peer_count))
-		wake_up(&trans->peer_gy_waitq);
-
-	_leave(" [destroyed]");
-} /* end rxrpc_peer_do_timeout() */
-
-/*****************************************************************************/
-/*
- * clear all peer records from a transport endpoint
- */
-void rxrpc_peer_clearall(struct rxrpc_transport *trans)
-{
-	DECLARE_WAITQUEUE(myself,current);
-
-	struct rxrpc_peer *peer;
-	int err;
-
-	_enter("%p",trans);
-
-	/* there shouldn't be any active peers remaining */
-	BUG_ON(!list_empty(&trans->peer_active));
-
-	/* manually timeout all peers in the graveyard */
-	spin_lock(&trans->peer_gylock);
-	while (!list_empty(&trans->peer_graveyard)) {
-		peer = list_entry(trans->peer_graveyard.next,
-				  struct rxrpc_peer, link);
-		_debug("Clearing peer %p\n", peer);
-		err = rxrpc_krxtimod_del_timer(&peer->timeout);
-		spin_unlock(&trans->peer_gylock);
-
-		if (err == 0)
-			rxrpc_peer_do_timeout(peer);
-
-		spin_lock(&trans->peer_gylock);
-	}
-	spin_unlock(&trans->peer_gylock);
-
-	/* wait for the the peer graveyard to be completely cleared */
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	add_wait_queue(&trans->peer_gy_waitq, &myself);
-
-	while (atomic_read(&trans->peer_count) != 0) {
-		schedule();
-		set_current_state(TASK_UNINTERRUPTIBLE);
-	}
-
-	remove_wait_queue(&trans->peer_gy_waitq, &myself);
-	set_current_state(TASK_RUNNING);
-
-	_leave("");
-} /* end rxrpc_peer_clearall() */
-
-/*****************************************************************************/
-/*
- * calculate and cache the Round-Trip-Time for a message and its response
- */
-void rxrpc_peer_calculate_rtt(struct rxrpc_peer *peer,
-			      struct rxrpc_message *msg,
-			      struct rxrpc_message *resp)
-{
-	unsigned long long rtt;
-	int loop;
-
-	_enter("%p,%p,%p", peer, msg, resp);
-
-	/* calculate the latest RTT */
-	rtt = resp->stamp.tv_sec - msg->stamp.tv_sec;
-	rtt *= 1000000UL;
-	rtt += resp->stamp.tv_usec - msg->stamp.tv_usec;
-
-	/* add to cache */
-	peer->rtt_cache[peer->rtt_point] = rtt;
-	peer->rtt_point++;
-	peer->rtt_point %= RXRPC_RTT_CACHE_SIZE;
-
-	if (peer->rtt_usage < RXRPC_RTT_CACHE_SIZE)
-		peer->rtt_usage++;
-
-	/* recalculate RTT */
-	rtt = 0;
-	for (loop = peer->rtt_usage - 1; loop >= 0; loop--)
-		rtt += peer->rtt_cache[loop];
-
-	do_div(rtt, peer->rtt_usage);
-	peer->rtt = rtt;
-
-	_leave(" RTT=%lu.%lums",
-	       (long) (peer->rtt / 1000), (long) (peer->rtt % 1000));
-
-} /* end rxrpc_peer_calculate_rtt() */
diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c
deleted file mode 100644
index 8551c87..0000000
--- a/net/rxrpc/proc.c
+++ /dev/null
@@ -1,617 +0,0 @@
-/* proc.c: /proc interface for RxRPC
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <rxrpc/rxrpc.h>
-#include <rxrpc/transport.h>
-#include <rxrpc/peer.h>
-#include <rxrpc/connection.h>
-#include <rxrpc/call.h>
-#include <rxrpc/message.h>
-#include "internal.h"
-
-static struct proc_dir_entry *proc_rxrpc;
-
-static int rxrpc_proc_transports_open(struct inode *inode, struct file *file);
-static void *rxrpc_proc_transports_start(struct seq_file *p, loff_t *pos);
-static void *rxrpc_proc_transports_next(struct seq_file *p, void *v, loff_t *pos);
-static void rxrpc_proc_transports_stop(struct seq_file *p, void *v);
-static int rxrpc_proc_transports_show(struct seq_file *m, void *v);
-
-static struct seq_operations rxrpc_proc_transports_ops = {
-	.start	= rxrpc_proc_transports_start,
-	.next	= rxrpc_proc_transports_next,
-	.stop	= rxrpc_proc_transports_stop,
-	.show	= rxrpc_proc_transports_show,
-};
-
-static const struct file_operations rxrpc_proc_transports_fops = {
-	.open		= rxrpc_proc_transports_open,
-	.read		= seq_read,
-	.llseek		= seq_lseek,
-	.release	= seq_release,
-};
-
-static int rxrpc_proc_peers_open(struct inode *inode, struct file *file);
-static void *rxrpc_proc_peers_start(struct seq_file *p, loff_t *pos);
-static void *rxrpc_proc_peers_next(struct seq_file *p, void *v, loff_t *pos);
-static void rxrpc_proc_peers_stop(struct seq_file *p, void *v);
-static int rxrpc_proc_peers_show(struct seq_file *m, void *v);
-
-static struct seq_operations rxrpc_proc_peers_ops = {
-	.start	= rxrpc_proc_peers_start,
-	.next	= rxrpc_proc_peers_next,
-	.stop	= rxrpc_proc_peers_stop,
-	.show	= rxrpc_proc_peers_show,
-};
-
-static const struct file_operations rxrpc_proc_peers_fops = {
-	.open		= rxrpc_proc_peers_open,
-	.read		= seq_read,
-	.llseek		= seq_lseek,
-	.release	= seq_release,
-};
-
-static int rxrpc_proc_conns_open(struct inode *inode, struct file *file);
-static void *rxrpc_proc_conns_start(struct seq_file *p, loff_t *pos);
-static void *rxrpc_proc_conns_next(struct seq_file *p, void *v, loff_t *pos);
-static void rxrpc_proc_conns_stop(struct seq_file *p, void *v);
-static int rxrpc_proc_conns_show(struct seq_file *m, void *v);
-
-static struct seq_operations rxrpc_proc_conns_ops = {
-	.start	= rxrpc_proc_conns_start,
-	.next	= rxrpc_proc_conns_next,
-	.stop	= rxrpc_proc_conns_stop,
-	.show	= rxrpc_proc_conns_show,
-};
-
-static const struct file_operations rxrpc_proc_conns_fops = {
-	.open		= rxrpc_proc_conns_open,
-	.read		= seq_read,
-	.llseek		= seq_lseek,
-	.release	= seq_release,
-};
-
-static int rxrpc_proc_calls_open(struct inode *inode, struct file *file);
-static void *rxrpc_proc_calls_start(struct seq_file *p, loff_t *pos);
-static void *rxrpc_proc_calls_next(struct seq_file *p, void *v, loff_t *pos);
-static void rxrpc_proc_calls_stop(struct seq_file *p, void *v);
-static int rxrpc_proc_calls_show(struct seq_file *m, void *v);
-
-static struct seq_operations rxrpc_proc_calls_ops = {
-	.start	= rxrpc_proc_calls_start,
-	.next	= rxrpc_proc_calls_next,
-	.stop	= rxrpc_proc_calls_stop,
-	.show	= rxrpc_proc_calls_show,
-};
-
-static const struct file_operations rxrpc_proc_calls_fops = {
-	.open		= rxrpc_proc_calls_open,
-	.read		= seq_read,
-	.llseek		= seq_lseek,
-	.release	= seq_release,
-};
-
-static const char *rxrpc_call_states7[] = {
-	"complet",
-	"error  ",
-	"rcv_op ",
-	"rcv_arg",
-	"got_arg",
-	"snd_rpl",
-	"fin_ack",
-	"snd_arg",
-	"rcv_rpl",
-	"got_rpl"
-};
-
-static const char *rxrpc_call_error_states7[] = {
-	"no_err ",
-	"loc_abt",
-	"rmt_abt",
-	"loc_err",
-	"rmt_err"
-};
-
-/*****************************************************************************/
-/*
- * initialise the /proc/net/rxrpc/ directory
- */
-int rxrpc_proc_init(void)
-{
-	struct proc_dir_entry *p;
-
-	proc_rxrpc = proc_mkdir("rxrpc", proc_net);
-	if (!proc_rxrpc)
-		goto error;
-	proc_rxrpc->owner = THIS_MODULE;
-
-	p = create_proc_entry("calls", 0, proc_rxrpc);
-	if (!p)
-		goto error_proc;
-	p->proc_fops = &rxrpc_proc_calls_fops;
-	p->owner = THIS_MODULE;
-
-	p = create_proc_entry("connections", 0, proc_rxrpc);
-	if (!p)
-		goto error_calls;
-	p->proc_fops = &rxrpc_proc_conns_fops;
-	p->owner = THIS_MODULE;
-
-	p = create_proc_entry("peers", 0, proc_rxrpc);
-	if (!p)
-		goto error_calls;
-	p->proc_fops = &rxrpc_proc_peers_fops;
-	p->owner = THIS_MODULE;
-
-	p = create_proc_entry("transports", 0, proc_rxrpc);
-	if (!p)
-		goto error_conns;
-	p->proc_fops = &rxrpc_proc_transports_fops;
-	p->owner = THIS_MODULE;
-
-	return 0;
-
- error_conns:
-	remove_proc_entry("connections", proc_rxrpc);
- error_calls:
-	remove_proc_entry("calls", proc_rxrpc);
- error_proc:
-	remove_proc_entry("rxrpc", proc_net);
- error:
-	return -ENOMEM;
-} /* end rxrpc_proc_init() */
-
-/*****************************************************************************/
-/*
- * clean up the /proc/net/rxrpc/ directory
- */
-void rxrpc_proc_cleanup(void)
-{
-	remove_proc_entry("transports", proc_rxrpc);
-	remove_proc_entry("peers", proc_rxrpc);
-	remove_proc_entry("connections", proc_rxrpc);
-	remove_proc_entry("calls", proc_rxrpc);
-
-	remove_proc_entry("rxrpc", proc_net);
-
-} /* end rxrpc_proc_cleanup() */
-
-/*****************************************************************************/
-/*
- * open "/proc/net/rxrpc/transports" which provides a summary of extant transports
- */
-static int rxrpc_proc_transports_open(struct inode *inode, struct file *file)
-{
-	struct seq_file *m;
-	int ret;
-
-	ret = seq_open(file, &rxrpc_proc_transports_ops);
-	if (ret < 0)
-		return ret;
-
-	m = file->private_data;
-	m->private = PDE(inode)->data;
-
-	return 0;
-} /* end rxrpc_proc_transports_open() */
-
-/*****************************************************************************/
-/*
- * set up the iterator to start reading from the transports list and return the first item
- */
-static void *rxrpc_proc_transports_start(struct seq_file *m, loff_t *_pos)
-{
-	struct list_head *_p;
-	loff_t pos = *_pos;
-
-	/* lock the list against modification */
-	down_read(&rxrpc_proc_transports_sem);
-
-	/* allow for the header line */
-	if (!pos)
-		return SEQ_START_TOKEN;
-	pos--;
-
-	/* find the n'th element in the list */
-	list_for_each(_p, &rxrpc_proc_transports)
-		if (!pos--)
-			break;
-
-	return _p != &rxrpc_proc_transports ? _p : NULL;
-} /* end rxrpc_proc_transports_start() */
-
-/*****************************************************************************/
-/*
- * move to next call in transports list
- */
-static void *rxrpc_proc_transports_next(struct seq_file *p, void *v, loff_t *pos)
-{
-	struct list_head *_p;
-
-	(*pos)++;
-
-	_p = v;
-	_p = (v == SEQ_START_TOKEN) ? rxrpc_proc_transports.next : _p->next;
-
-	return _p != &rxrpc_proc_transports ? _p : NULL;
-} /* end rxrpc_proc_transports_next() */
-
-/*****************************************************************************/
-/*
- * clean up after reading from the transports list
- */
-static void rxrpc_proc_transports_stop(struct seq_file *p, void *v)
-{
-	up_read(&rxrpc_proc_transports_sem);
-
-} /* end rxrpc_proc_transports_stop() */
-
-/*****************************************************************************/
-/*
- * display a header line followed by a load of call lines
- */
-static int rxrpc_proc_transports_show(struct seq_file *m, void *v)
-{
-	struct rxrpc_transport *trans =
-		list_entry(v, struct rxrpc_transport, proc_link);
-
-	/* display header on line 1 */
-	if (v == SEQ_START_TOKEN) {
-		seq_puts(m, "LOCAL USE\n");
-		return 0;
-	}
-
-	/* display one transport per line on subsequent lines */
-	seq_printf(m, "%5hu %3d\n",
-		   trans->port,
-		   atomic_read(&trans->usage)
-		   );
-
-	return 0;
-} /* end rxrpc_proc_transports_show() */
-
-/*****************************************************************************/
-/*
- * open "/proc/net/rxrpc/peers" which provides a summary of extant peers
- */
-static int rxrpc_proc_peers_open(struct inode *inode, struct file *file)
-{
-	struct seq_file *m;
-	int ret;
-
-	ret = seq_open(file, &rxrpc_proc_peers_ops);
-	if (ret < 0)
-		return ret;
-
-	m = file->private_data;
-	m->private = PDE(inode)->data;
-
-	return 0;
-} /* end rxrpc_proc_peers_open() */
-
-/*****************************************************************************/
-/*
- * set up the iterator to start reading from the peers list and return the
- * first item
- */
-static void *rxrpc_proc_peers_start(struct seq_file *m, loff_t *_pos)
-{
-	struct list_head *_p;
-	loff_t pos = *_pos;
-
-	/* lock the list against modification */
-	down_read(&rxrpc_peers_sem);
-
-	/* allow for the header line */
-	if (!pos)
-		return SEQ_START_TOKEN;
-	pos--;
-
-	/* find the n'th element in the list */
-	list_for_each(_p, &rxrpc_peers)
-		if (!pos--)
-			break;
-
-	return _p != &rxrpc_peers ? _p : NULL;
-} /* end rxrpc_proc_peers_start() */
-
-/*****************************************************************************/
-/*
- * move to next conn in peers list
- */
-static void *rxrpc_proc_peers_next(struct seq_file *p, void *v, loff_t *pos)
-{
-	struct list_head *_p;
-
-	(*pos)++;
-
-	_p = v;
-	_p = (v == SEQ_START_TOKEN) ? rxrpc_peers.next : _p->next;
-
-	return _p != &rxrpc_peers ? _p : NULL;
-} /* end rxrpc_proc_peers_next() */
-
-/*****************************************************************************/
-/*
- * clean up after reading from the peers list
- */
-static void rxrpc_proc_peers_stop(struct seq_file *p, void *v)
-{
-	up_read(&rxrpc_peers_sem);
-
-} /* end rxrpc_proc_peers_stop() */
-
-/*****************************************************************************/
-/*
- * display a header line followed by a load of conn lines
- */
-static int rxrpc_proc_peers_show(struct seq_file *m, void *v)
-{
-	struct rxrpc_peer *peer = list_entry(v, struct rxrpc_peer, proc_link);
-	long timeout;
-
-	/* display header on line 1 */
-	if (v == SEQ_START_TOKEN) {
-		seq_puts(m, "LOCAL REMOTE   USAGE CONNS  TIMEOUT"
-			 "   MTU RTT(uS)\n");
-		return 0;
-	}
-
-	/* display one peer per line on subsequent lines */
-	timeout = 0;
-	if (!list_empty(&peer->timeout.link))
-		timeout = (long) peer->timeout.timo_jif -
-			(long) jiffies;
-
-	seq_printf(m, "%5hu %08x %5d %5d %8ld %5Zu %7lu\n",
-		   peer->trans->port,
-		   ntohl(peer->addr.s_addr),
-		   atomic_read(&peer->usage),
-		   atomic_read(&peer->conn_count),
-		   timeout,
-		   peer->if_mtu,
-		   (long) peer->rtt
-		   );
-
-	return 0;
-} /* end rxrpc_proc_peers_show() */
-
-/*****************************************************************************/
-/*
- * open "/proc/net/rxrpc/connections" which provides a summary of extant
- * connections
- */
-static int rxrpc_proc_conns_open(struct inode *inode, struct file *file)
-{
-	struct seq_file *m;
-	int ret;
-
-	ret = seq_open(file, &rxrpc_proc_conns_ops);
-	if (ret < 0)
-		return ret;
-
-	m = file->private_data;
-	m->private = PDE(inode)->data;
-
-	return 0;
-} /* end rxrpc_proc_conns_open() */
-
-/*****************************************************************************/
-/*
- * set up the iterator to start reading from the conns list and return the
- * first item
- */
-static void *rxrpc_proc_conns_start(struct seq_file *m, loff_t *_pos)
-{
-	struct list_head *_p;
-	loff_t pos = *_pos;
-
-	/* lock the list against modification */
-	down_read(&rxrpc_conns_sem);
-
-	/* allow for the header line */
-	if (!pos)
-		return SEQ_START_TOKEN;
-	pos--;
-
-	/* find the n'th element in the list */
-	list_for_each(_p, &rxrpc_conns)
-		if (!pos--)
-			break;
-
-	return _p != &rxrpc_conns ? _p : NULL;
-} /* end rxrpc_proc_conns_start() */
-
-/*****************************************************************************/
-/*
- * move to next conn in conns list
- */
-static void *rxrpc_proc_conns_next(struct seq_file *p, void *v, loff_t *pos)
-{
-	struct list_head *_p;
-
-	(*pos)++;
-
-	_p = v;
-	_p = (v == SEQ_START_TOKEN) ? rxrpc_conns.next : _p->next;
-
-	return _p != &rxrpc_conns ? _p : NULL;
-} /* end rxrpc_proc_conns_next() */
-
-/*****************************************************************************/
-/*
- * clean up after reading from the conns list
- */
-static void rxrpc_proc_conns_stop(struct seq_file *p, void *v)
-{
-	up_read(&rxrpc_conns_sem);
-
-} /* end rxrpc_proc_conns_stop() */
-
-/*****************************************************************************/
-/*
- * display a header line followed by a load of conn lines
- */
-static int rxrpc_proc_conns_show(struct seq_file *m, void *v)
-{
-	struct rxrpc_connection *conn;
-	long timeout;
-
-	conn = list_entry(v, struct rxrpc_connection, proc_link);
-
-	/* display header on line 1 */
-	if (v == SEQ_START_TOKEN) {
-		seq_puts(m,
-			 "LOCAL REMOTE   RPORT SRVC CONN     END SERIALNO "
-			 "CALLNO     MTU  TIMEOUT"
-			 "\n");
-		return 0;
-	}
-
-	/* display one conn per line on subsequent lines */
-	timeout = 0;
-	if (!list_empty(&conn->timeout.link))
-		timeout = (long) conn->timeout.timo_jif -
-			(long) jiffies;
-
-	seq_printf(m,
-		   "%5hu %08x %5hu %04hx %08x %-3.3s %08x %08x %5Zu %8ld\n",
-		   conn->trans->port,
-		   ntohl(conn->addr.sin_addr.s_addr),
-		   ntohs(conn->addr.sin_port),
-		   ntohs(conn->service_id),
-		   ntohl(conn->conn_id),
-		   conn->out_clientflag ? "CLT" : "SRV",
-		   conn->serial_counter,
-		   conn->call_counter,
-		   conn->mtu_size,
-		   timeout
-		   );
-
-	return 0;
-} /* end rxrpc_proc_conns_show() */
-
-/*****************************************************************************/
-/*
- * open "/proc/net/rxrpc/calls" which provides a summary of extant calls
- */
-static int rxrpc_proc_calls_open(struct inode *inode, struct file *file)
-{
-	struct seq_file *m;
-	int ret;
-
-	ret = seq_open(file, &rxrpc_proc_calls_ops);
-	if (ret < 0)
-		return ret;
-
-	m = file->private_data;
-	m->private = PDE(inode)->data;
-
-	return 0;
-} /* end rxrpc_proc_calls_open() */
-
-/*****************************************************************************/
-/*
- * set up the iterator to start reading from the calls list and return the
- * first item
- */
-static void *rxrpc_proc_calls_start(struct seq_file *m, loff_t *_pos)
-{
-	struct list_head *_p;
-	loff_t pos = *_pos;
-
-	/* lock the list against modification */
-	down_read(&rxrpc_calls_sem);
-
-	/* allow for the header line */
-	if (!pos)
-		return SEQ_START_TOKEN;
-	pos--;
-
-	/* find the n'th element in the list */
-	list_for_each(_p, &rxrpc_calls)
-		if (!pos--)
-			break;
-
-	return _p != &rxrpc_calls ? _p : NULL;
-} /* end rxrpc_proc_calls_start() */
-
-/*****************************************************************************/
-/*
- * move to next call in calls list
- */
-static void *rxrpc_proc_calls_next(struct seq_file *p, void *v, loff_t *pos)
-{
-	struct list_head *_p;
-
-	(*pos)++;
-
-	_p = v;
-	_p = (v == SEQ_START_TOKEN) ? rxrpc_calls.next : _p->next;
-
-	return _p != &rxrpc_calls ? _p : NULL;
-} /* end rxrpc_proc_calls_next() */
-
-/*****************************************************************************/
-/*
- * clean up after reading from the calls list
- */
-static void rxrpc_proc_calls_stop(struct seq_file *p, void *v)
-{
-	up_read(&rxrpc_calls_sem);
-
-} /* end rxrpc_proc_calls_stop() */
-
-/*****************************************************************************/
-/*
- * display a header line followed by a load of call lines
- */
-static int rxrpc_proc_calls_show(struct seq_file *m, void *v)
-{
-	struct rxrpc_call *call = list_entry(v, struct rxrpc_call, call_link);
-
-	/* display header on line 1 */
-	if (v == SEQ_START_TOKEN) {
-		seq_puts(m,
-			 "LOCAL REMOT SRVC CONN     CALL     DIR USE "
-			 " L STATE   OPCODE ABORT    ERRNO\n"
-			 );
-		return 0;
-	}
-
-	/* display one call per line on subsequent lines */
-	seq_printf(m,
-		   "%5hu %5hu %04hx %08x %08x %s %3u%c"
-		   " %c %-7.7s %6d %08x %5d\n",
-		   call->conn->trans->port,
-		   ntohs(call->conn->addr.sin_port),
-		   ntohs(call->conn->service_id),
-		   ntohl(call->conn->conn_id),
-		   ntohl(call->call_id),
-		   call->conn->service ? "SVC" : "CLT",
-		   atomic_read(&call->usage),
-		   waitqueue_active(&call->waitq) ? 'w' : ' ',
-		   call->app_last_rcv ? 'Y' : '-',
-		   (call->app_call_state!=RXRPC_CSTATE_ERROR ?
-		    rxrpc_call_states7[call->app_call_state] :
-		    rxrpc_call_error_states7[call->app_err_state]),
-		   call->app_opcode,
-		   call->app_abort_code,
-		   call->app_errno
-		   );
-
-	return 0;
-} /* end rxrpc_proc_calls_show() */
diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c
new file mode 100644
index 0000000..5ec7051
--- /dev/null
+++ b/net/rxrpc/rxkad.c
@@ -0,0 +1,1154 @@
+/* Kerberos-based RxRPC security
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.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.
+ */
+
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/udp.h>
+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+#include <linux/ctype.h>
+#include <net/sock.h>
+#include <net/af_rxrpc.h>
+#define rxrpc_debug rxkad_debug
+#include "ar-internal.h"
+
+#define RXKAD_VERSION			2
+#define MAXKRB5TICKETLEN		1024
+#define RXKAD_TKT_TYPE_KERBEROS_V5	256
+#define ANAME_SZ			40	/* size of authentication name */
+#define INST_SZ				40	/* size of principal's instance */
+#define REALM_SZ			40	/* size of principal's auth domain */
+#define SNAME_SZ			40	/* size of service name */
+
+unsigned rxrpc_debug;
+module_param_named(debug, rxrpc_debug, uint, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(rxrpc_debug, "rxkad debugging mask");
+
+struct rxkad_level1_hdr {
+	__be32	data_size;	/* true data size (excluding padding) */
+};
+
+struct rxkad_level2_hdr {
+	__be32	data_size;	/* true data size (excluding padding) */
+	__be32	checksum;	/* decrypted data checksum */
+};
+
+MODULE_DESCRIPTION("RxRPC network protocol type-2 security (Kerberos)");
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL");
+
+/*
+ * this holds a pinned cipher so that keventd doesn't get called by the cipher
+ * alloc routine, but since we have it to hand, we use it to decrypt RESPONSE
+ * packets
+ */
+static struct crypto_blkcipher *rxkad_ci;
+static DEFINE_MUTEX(rxkad_ci_mutex);
+
+/*
+ * initialise connection security
+ */
+static int rxkad_init_connection_security(struct rxrpc_connection *conn)
+{
+	struct rxrpc_key_payload *payload;
+	struct crypto_blkcipher *ci;
+	int ret;
+
+	_enter("{%d},{%x}", conn->debug_id, key_serial(conn->key));
+
+	payload = conn->key->payload.data;
+	conn->security_ix = payload->k.security_index;
+
+	ci = crypto_alloc_blkcipher("pcbc(fcrypt)", 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(ci)) {
+		_debug("no cipher");
+		ret = PTR_ERR(ci);
+		goto error;
+	}
+
+	if (crypto_blkcipher_setkey(ci, payload->k.session_key,
+				    sizeof(payload->k.session_key)) < 0)
+		BUG();
+
+	switch (conn->security_level) {
+	case RXRPC_SECURITY_PLAIN:
+		break;
+	case RXRPC_SECURITY_AUTH:
+		conn->size_align = 8;
+		conn->security_size = sizeof(struct rxkad_level1_hdr);
+		conn->header_size += sizeof(struct rxkad_level1_hdr);
+		break;
+	case RXRPC_SECURITY_ENCRYPT:
+		conn->size_align = 8;
+		conn->security_size = sizeof(struct rxkad_level2_hdr);
+		conn->header_size += sizeof(struct rxkad_level2_hdr);
+		break;
+	default:
+		ret = -EKEYREJECTED;
+		goto error;
+	}
+
+	conn->cipher = ci;
+	ret = 0;
+error:
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * prime the encryption state with the invariant parts of a connection's
+ * description
+ */
+static void rxkad_prime_packet_security(struct rxrpc_connection *conn)
+{
+	struct rxrpc_key_payload *payload;
+	struct blkcipher_desc desc;
+	struct scatterlist sg[2];
+	struct rxrpc_crypt iv;
+	struct {
+		__be32 x[4];
+	} tmpbuf __attribute__((aligned(16))); /* must all be in same page */
+
+	_enter("");
+
+	if (!conn->key)
+		return;
+
+	payload = conn->key->payload.data;
+	memcpy(&iv, payload->k.session_key, sizeof(iv));
+
+	desc.tfm = conn->cipher;
+	desc.info = iv.x;
+	desc.flags = 0;
+
+	tmpbuf.x[0] = conn->epoch;
+	tmpbuf.x[1] = conn->cid;
+	tmpbuf.x[2] = 0;
+	tmpbuf.x[3] = htonl(conn->security_ix);
+
+	memset(sg, 0, sizeof(sg));
+	sg_set_buf(&sg[0], &tmpbuf, sizeof(tmpbuf));
+	sg_set_buf(&sg[1], &tmpbuf, sizeof(tmpbuf));
+	crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(tmpbuf));
+
+	memcpy(&conn->csum_iv, &tmpbuf.x[2], sizeof(conn->csum_iv));
+	ASSERTCMP(conn->csum_iv.n[0], ==, tmpbuf.x[2]);
+
+	_leave("");
+}
+
+/*
+ * partially encrypt a packet (level 1 security)
+ */
+static int rxkad_secure_packet_auth(const struct rxrpc_call *call,
+				    struct sk_buff *skb,
+				    u32 data_size,
+				    void *sechdr)
+{
+	struct rxrpc_skb_priv *sp;
+	struct blkcipher_desc desc;
+	struct rxrpc_crypt iv;
+	struct scatterlist sg[2];
+	struct {
+		struct rxkad_level1_hdr hdr;
+		__be32	first;	/* first four bytes of data and padding */
+	} tmpbuf __attribute__((aligned(8))); /* must all be in same page */
+	u16 check;
+
+	sp = rxrpc_skb(skb);
+
+	_enter("");
+
+	check = ntohl(sp->hdr.seq ^ sp->hdr.callNumber);
+	data_size |= (u32) check << 16;
+
+	tmpbuf.hdr.data_size = htonl(data_size);
+	memcpy(&tmpbuf.first, sechdr + 4, sizeof(tmpbuf.first));
+
+	/* start the encryption afresh */
+	memset(&iv, 0, sizeof(iv));
+	desc.tfm = call->conn->cipher;
+	desc.info = iv.x;
+	desc.flags = 0;
+
+	memset(sg, 0, sizeof(sg));
+	sg_set_buf(&sg[0], &tmpbuf, sizeof(tmpbuf));
+	sg_set_buf(&sg[1], &tmpbuf, sizeof(tmpbuf));
+	crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(tmpbuf));
+
+	memcpy(sechdr, &tmpbuf, sizeof(tmpbuf));
+
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * wholly encrypt a packet (level 2 security)
+ */
+static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call,
+					struct sk_buff *skb,
+					u32 data_size,
+					void *sechdr)
+{
+	const struct rxrpc_key_payload *payload;
+	struct rxkad_level2_hdr rxkhdr
+		__attribute__((aligned(8))); /* must be all on one page */
+	struct rxrpc_skb_priv *sp;
+	struct blkcipher_desc desc;
+	struct rxrpc_crypt iv;
+	struct scatterlist sg[16];
+	struct sk_buff *trailer;
+	unsigned len;
+	u16 check;
+	int nsg;
+
+	sp = rxrpc_skb(skb);
+
+	_enter("");
+
+	check = ntohl(sp->hdr.seq ^ sp->hdr.callNumber);
+
+	rxkhdr.data_size = htonl(data_size | (u32) check << 16);
+	rxkhdr.checksum = 0;
+
+	/* encrypt from the session key */
+	payload = call->conn->key->payload.data;
+	memcpy(&iv, payload->k.session_key, sizeof(iv));
+	desc.tfm = call->conn->cipher;
+	desc.info = iv.x;
+	desc.flags = 0;
+
+	memset(sg, 0, sizeof(sg[0]) * 2);
+	sg_set_buf(&sg[0], sechdr, sizeof(rxkhdr));
+	sg_set_buf(&sg[1], &rxkhdr, sizeof(rxkhdr));
+	crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(rxkhdr));
+
+	/* we want to encrypt the skbuff in-place */
+	nsg = skb_cow_data(skb, 0, &trailer);
+	if (nsg < 0 || nsg > 16)
+		return -ENOMEM;
+
+	len = data_size + call->conn->size_align - 1;
+	len &= ~(call->conn->size_align - 1);
+
+	skb_to_sgvec(skb, sg, 0, len);
+	crypto_blkcipher_encrypt_iv(&desc, sg, sg, len);
+
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * checksum an RxRPC packet header
+ */
+static int rxkad_secure_packet(const struct rxrpc_call *call,
+				struct sk_buff *skb,
+				size_t data_size,
+				void *sechdr)
+{
+	struct rxrpc_skb_priv *sp;
+	struct blkcipher_desc desc;
+	struct rxrpc_crypt iv;
+	struct scatterlist sg[2];
+	struct {
+		__be32 x[2];
+	} tmpbuf __attribute__((aligned(8))); /* must all be in same page */
+	__be32 x;
+	int ret;
+
+	sp = rxrpc_skb(skb);
+
+	_enter("{%d{%x}},{#%u},%zu,",
+	       call->debug_id, key_serial(call->conn->key), ntohl(sp->hdr.seq),
+	       data_size);
+
+	if (!call->conn->cipher)
+		return 0;
+
+	ret = key_validate(call->conn->key);
+	if (ret < 0)
+		return ret;
+
+	/* continue encrypting from where we left off */
+	memcpy(&iv, call->conn->csum_iv.x, sizeof(iv));
+	desc.tfm = call->conn->cipher;
+	desc.info = iv.x;
+	desc.flags = 0;
+
+	/* calculate the security checksum */
+	x = htonl(call->channel << (32 - RXRPC_CIDSHIFT));
+	x |= sp->hdr.seq & __constant_cpu_to_be32(0x3fffffff);
+	tmpbuf.x[0] = sp->hdr.callNumber;
+	tmpbuf.x[1] = x;
+
+	memset(&sg, 0, sizeof(sg));
+	sg_set_buf(&sg[0], &tmpbuf, sizeof(tmpbuf));
+	sg_set_buf(&sg[1], &tmpbuf, sizeof(tmpbuf));
+	crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(tmpbuf));
+
+	x = ntohl(tmpbuf.x[1]);
+	x = (x >> 16) & 0xffff;
+	if (x == 0)
+		x = 1; /* zero checksums are not permitted */
+	sp->hdr.cksum = htons(x);
+
+	switch (call->conn->security_level) {
+	case RXRPC_SECURITY_PLAIN:
+		ret = 0;
+		break;
+	case RXRPC_SECURITY_AUTH:
+		ret = rxkad_secure_packet_auth(call, skb, data_size, sechdr);
+		break;
+	case RXRPC_SECURITY_ENCRYPT:
+		ret = rxkad_secure_packet_encrypt(call, skb, data_size,
+						  sechdr);
+		break;
+	default:
+		ret = -EPERM;
+		break;
+	}
+
+	_leave(" = %d [set %hx]", ret, x);
+	return ret;
+}
+
+/*
+ * decrypt partial encryption on a packet (level 1 security)
+ */
+static int rxkad_verify_packet_auth(const struct rxrpc_call *call,
+				    struct sk_buff *skb,
+				    u32 *_abort_code)
+{
+	struct rxkad_level1_hdr sechdr;
+	struct rxrpc_skb_priv *sp;
+	struct blkcipher_desc desc;
+	struct rxrpc_crypt iv;
+	struct scatterlist sg[2];
+	struct sk_buff *trailer;
+	u32 data_size, buf;
+	u16 check;
+
+	_enter("");
+
+	sp = rxrpc_skb(skb);
+
+	/* we want to decrypt the skbuff in-place */
+	if (skb_cow_data(skb, 0, &trailer) < 0)
+		goto nomem;
+
+	skb_to_sgvec(skb, sg, 0, 8);
+
+	/* start the decryption afresh */
+	memset(&iv, 0, sizeof(iv));
+	desc.tfm = call->conn->cipher;
+	desc.info = iv.x;
+	desc.flags = 0;
+
+	crypto_blkcipher_decrypt_iv(&desc, sg, sg, 8);
+
+	/* remove the decrypted packet length */
+	if (skb_copy_bits(skb, 0, &sechdr, sizeof(sechdr)) < 0)
+		goto datalen_error;
+	if (!skb_pull(skb, sizeof(sechdr)))
+		BUG();
+
+	buf = ntohl(sechdr.data_size);
+	data_size = buf & 0xffff;
+
+	check = buf >> 16;
+	check ^= ntohl(sp->hdr.seq ^ sp->hdr.callNumber);
+	check &= 0xffff;
+	if (check != 0) {
+		*_abort_code = RXKADSEALEDINCON;
+		goto protocol_error;
+	}
+
+	/* shorten the packet to remove the padding */
+	if (data_size > skb->len)
+		goto datalen_error;
+	else if (data_size < skb->len)
+		skb->len = data_size;
+
+	_leave(" = 0 [dlen=%x]", data_size);
+	return 0;
+
+datalen_error:
+	*_abort_code = RXKADDATALEN;
+protocol_error:
+	_leave(" = -EPROTO");
+	return -EPROTO;
+
+nomem:
+	_leave(" = -ENOMEM");
+	return -ENOMEM;
+}
+
+/*
+ * wholly decrypt a packet (level 2 security)
+ */
+static int rxkad_verify_packet_encrypt(const struct rxrpc_call *call,
+				       struct sk_buff *skb,
+				       u32 *_abort_code)
+{
+	const struct rxrpc_key_payload *payload;
+	struct rxkad_level2_hdr sechdr;
+	struct rxrpc_skb_priv *sp;
+	struct blkcipher_desc desc;
+	struct rxrpc_crypt iv;
+	struct scatterlist _sg[4], *sg;
+	struct sk_buff *trailer;
+	u32 data_size, buf;
+	u16 check;
+	int nsg;
+
+	_enter(",{%d}", skb->len);
+
+	sp = rxrpc_skb(skb);
+
+	/* we want to decrypt the skbuff in-place */
+	nsg = skb_cow_data(skb, 0, &trailer);
+	if (nsg < 0)
+		goto nomem;
+
+	sg = _sg;
+	if (unlikely(nsg > 4)) {
+		sg = kmalloc(sizeof(*sg) * nsg, GFP_NOIO);
+		if (!sg)
+			goto nomem;
+	}
+
+	skb_to_sgvec(skb, sg, 0, skb->len);
+
+	/* decrypt from the session key */
+	payload = call->conn->key->payload.data;
+	memcpy(&iv, payload->k.session_key, sizeof(iv));
+	desc.tfm = call->conn->cipher;
+	desc.info = iv.x;
+	desc.flags = 0;
+
+	crypto_blkcipher_decrypt_iv(&desc, sg, sg, skb->len);
+	if (sg != _sg)
+		kfree(sg);
+
+	/* remove the decrypted packet length */
+	if (skb_copy_bits(skb, 0, &sechdr, sizeof(sechdr)) < 0)
+		goto datalen_error;
+	if (!skb_pull(skb, sizeof(sechdr)))
+		BUG();
+
+	buf = ntohl(sechdr.data_size);
+	data_size = buf & 0xffff;
+
+	check = buf >> 16;
+	check ^= ntohl(sp->hdr.seq ^ sp->hdr.callNumber);
+	check &= 0xffff;
+	if (check != 0) {
+		*_abort_code = RXKADSEALEDINCON;
+		goto protocol_error;
+	}
+
+	/* shorten the packet to remove the padding */
+	if (data_size > skb->len)
+		goto datalen_error;
+	else if (data_size < skb->len)
+		skb->len = data_size;
+
+	_leave(" = 0 [dlen=%x]", data_size);
+	return 0;
+
+datalen_error:
+	*_abort_code = RXKADDATALEN;
+protocol_error:
+	_leave(" = -EPROTO");
+	return -EPROTO;
+
+nomem:
+	_leave(" = -ENOMEM");
+	return -ENOMEM;
+}
+
+/*
+ * verify the security on a received packet
+ */
+static int rxkad_verify_packet(const struct rxrpc_call *call,
+			       struct sk_buff *skb,
+			       u32 *_abort_code)
+{
+	struct blkcipher_desc desc;
+	struct rxrpc_skb_priv *sp;
+	struct rxrpc_crypt iv;
+	struct scatterlist sg[2];
+	struct {
+		__be32 x[2];
+	} tmpbuf __attribute__((aligned(8))); /* must all be in same page */
+	__be32 x;
+	__be16 cksum;
+	int ret;
+
+	sp = rxrpc_skb(skb);
+
+	_enter("{%d{%x}},{#%u}",
+	       call->debug_id, key_serial(call->conn->key),
+	       ntohl(sp->hdr.seq));
+
+	if (!call->conn->cipher)
+		return 0;
+
+	if (sp->hdr.securityIndex != 2) {
+		*_abort_code = RXKADINCONSISTENCY;
+		_leave(" = -EPROTO [not rxkad]");
+		return -EPROTO;
+	}
+
+	/* continue encrypting from where we left off */
+	memcpy(&iv, call->conn->csum_iv.x, sizeof(iv));
+	desc.tfm = call->conn->cipher;
+	desc.info = iv.x;
+	desc.flags = 0;
+
+	/* validate the security checksum */
+	x = htonl(call->channel << (32 - RXRPC_CIDSHIFT));
+	x |= sp->hdr.seq & __constant_cpu_to_be32(0x3fffffff);
+	tmpbuf.x[0] = call->call_id;
+	tmpbuf.x[1] = x;
+
+	memset(&sg, 0, sizeof(sg));
+	sg_set_buf(&sg[0], &tmpbuf, sizeof(tmpbuf));
+	sg_set_buf(&sg[1], &tmpbuf, sizeof(tmpbuf));
+	crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(tmpbuf));
+
+	x = ntohl(tmpbuf.x[1]);
+	x = (x >> 16) & 0xffff;
+	if (x == 0)
+		x = 1; /* zero checksums are not permitted */
+
+	cksum = htons(x);
+	if (sp->hdr.cksum != cksum) {
+		*_abort_code = RXKADSEALEDINCON;
+		_leave(" = -EPROTO [csum failed]");
+		return -EPROTO;
+	}
+
+	switch (call->conn->security_level) {
+	case RXRPC_SECURITY_PLAIN:
+		ret = 0;
+		break;
+	case RXRPC_SECURITY_AUTH:
+		ret = rxkad_verify_packet_auth(call, skb, _abort_code);
+		break;
+	case RXRPC_SECURITY_ENCRYPT:
+		ret = rxkad_verify_packet_encrypt(call, skb, _abort_code);
+		break;
+	default:
+		ret = -ENOANO;
+		break;
+	}
+
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * issue a challenge
+ */
+static int rxkad_issue_challenge(struct rxrpc_connection *conn)
+{
+	struct rxkad_challenge challenge;
+	struct rxrpc_header hdr;
+	struct msghdr msg;
+	struct kvec iov[2];
+	size_t len;
+	int ret;
+
+	_enter("{%d,%x}", conn->debug_id, key_serial(conn->key));
+
+	ret = key_validate(conn->key);
+	if (ret < 0)
+		return ret;
+
+	get_random_bytes(&conn->security_nonce, sizeof(conn->security_nonce));
+
+	challenge.version	= htonl(2);
+	challenge.nonce		= htonl(conn->security_nonce);
+	challenge.min_level	= htonl(0);
+	challenge.__padding	= 0;
+
+	msg.msg_name	= &conn->trans->peer->srx.transport.sin;
+	msg.msg_namelen	= sizeof(conn->trans->peer->srx.transport.sin);
+	msg.msg_control	= NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags	= 0;
+
+	hdr.epoch	= conn->epoch;
+	hdr.cid		= conn->cid;
+	hdr.callNumber	= 0;
+	hdr.seq		= 0;
+	hdr.type	= RXRPC_PACKET_TYPE_CHALLENGE;
+	hdr.flags	= conn->out_clientflag;
+	hdr.userStatus	= 0;
+	hdr.securityIndex = conn->security_ix;
+	hdr._rsvd	= 0;
+	hdr.serviceId	= conn->service_id;
+
+	iov[0].iov_base	= &hdr;
+	iov[0].iov_len	= sizeof(hdr);
+	iov[1].iov_base	= &challenge;
+	iov[1].iov_len	= sizeof(challenge);
+
+	len = iov[0].iov_len + iov[1].iov_len;
+
+	hdr.serial = htonl(atomic_inc_return(&conn->serial));
+	_proto("Tx CHALLENGE %%%u", ntohl(hdr.serial));
+
+	ret = kernel_sendmsg(conn->trans->local->socket, &msg, iov, 2, len);
+	if (ret < 0) {
+		_debug("sendmsg failed: %d", ret);
+		return -EAGAIN;
+	}
+
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * send a Kerberos security response
+ */
+static int rxkad_send_response(struct rxrpc_connection *conn,
+			       struct rxrpc_header *hdr,
+			       struct rxkad_response *resp,
+			       const struct rxkad_key *s2)
+{
+	struct msghdr msg;
+	struct kvec iov[3];
+	size_t len;
+	int ret;
+
+	_enter("");
+
+	msg.msg_name	= &conn->trans->peer->srx.transport.sin;
+	msg.msg_namelen	= sizeof(conn->trans->peer->srx.transport.sin);
+	msg.msg_control	= NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags	= 0;
+
+	hdr->epoch	= conn->epoch;
+	hdr->seq	= 0;
+	hdr->type	= RXRPC_PACKET_TYPE_RESPONSE;
+	hdr->flags	= conn->out_clientflag;
+	hdr->userStatus	= 0;
+	hdr->_rsvd	= 0;
+
+	iov[0].iov_base	= hdr;
+	iov[0].iov_len	= sizeof(*hdr);
+	iov[1].iov_base	= resp;
+	iov[1].iov_len	= sizeof(*resp);
+	iov[2].iov_base	= (void *) s2->ticket;
+	iov[2].iov_len	= s2->ticket_len;
+
+	len = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len;
+
+	hdr->serial = htonl(atomic_inc_return(&conn->serial));
+	_proto("Tx RESPONSE %%%u", ntohl(hdr->serial));
+
+	ret = kernel_sendmsg(conn->trans->local->socket, &msg, iov, 3, len);
+	if (ret < 0) {
+		_debug("sendmsg failed: %d", ret);
+		return -EAGAIN;
+	}
+
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * calculate the response checksum
+ */
+static void rxkad_calc_response_checksum(struct rxkad_response *response)
+{
+	u32 csum = 1000003;
+	int loop;
+	u8 *p = (u8 *) response;
+
+	for (loop = sizeof(*response); loop > 0; loop--)
+		csum = csum * 0x10204081 + *p++;
+
+	response->encrypted.checksum = htonl(csum);
+}
+
+/*
+ * load a scatterlist with a potentially split-page buffer
+ */
+static void rxkad_sg_set_buf2(struct scatterlist sg[2],
+			      void *buf, size_t buflen)
+{
+
+	memset(sg, 0, sizeof(sg));
+
+	sg_set_buf(&sg[0], buf, buflen);
+	if (sg[0].offset + buflen > PAGE_SIZE) {
+		/* the buffer was split over two pages */
+		sg[0].length = PAGE_SIZE - sg[0].offset;
+		sg_set_buf(&sg[1], buf + sg[0].length, buflen - sg[0].length);
+	}
+
+	ASSERTCMP(sg[0].length + sg[1].length, ==, buflen);
+}
+
+/*
+ * encrypt the response packet
+ */
+static void rxkad_encrypt_response(struct rxrpc_connection *conn,
+				   struct rxkad_response *resp,
+				   const struct rxkad_key *s2)
+{
+	struct blkcipher_desc desc;
+	struct rxrpc_crypt iv;
+	struct scatterlist ssg[2], dsg[2];
+
+	/* continue encrypting from where we left off */
+	memcpy(&iv, s2->session_key, sizeof(iv));
+	desc.tfm = conn->cipher;
+	desc.info = iv.x;
+	desc.flags = 0;
+
+	rxkad_sg_set_buf2(ssg, &resp->encrypted, sizeof(resp->encrypted));
+	memcpy(dsg, ssg, sizeof(dsg));
+	crypto_blkcipher_encrypt_iv(&desc, dsg, ssg, sizeof(resp->encrypted));
+}
+
+/*
+ * respond to a challenge packet
+ */
+static int rxkad_respond_to_challenge(struct rxrpc_connection *conn,
+				      struct sk_buff *skb,
+				      u32 *_abort_code)
+{
+	const struct rxrpc_key_payload *payload;
+	struct rxkad_challenge challenge;
+	struct rxkad_response resp
+		__attribute__((aligned(8))); /* must be aligned for crypto */
+	struct rxrpc_skb_priv *sp;
+	u32 version, nonce, min_level, abort_code;
+	int ret;
+
+	_enter("{%d,%x}", conn->debug_id, key_serial(conn->key));
+
+	if (!conn->key) {
+		_leave(" = -EPROTO [no key]");
+		return -EPROTO;
+	}
+
+	ret = key_validate(conn->key);
+	if (ret < 0) {
+		*_abort_code = RXKADEXPIRED;
+		return ret;
+	}
+
+	abort_code = RXKADPACKETSHORT;
+	sp = rxrpc_skb(skb);
+	if (skb_copy_bits(skb, 0, &challenge, sizeof(challenge)) < 0)
+		goto protocol_error;
+
+	version = ntohl(challenge.version);
+	nonce = ntohl(challenge.nonce);
+	min_level = ntohl(challenge.min_level);
+
+	_proto("Rx CHALLENGE %%%u { v=%u n=%u ml=%u }",
+	       ntohl(sp->hdr.serial), version, nonce, min_level);
+
+	abort_code = RXKADINCONSISTENCY;
+	if (version != RXKAD_VERSION)
+		goto protocol_error;
+
+	abort_code = RXKADLEVELFAIL;
+	if (conn->security_level < min_level)
+		goto protocol_error;
+
+	payload = conn->key->payload.data;
+
+	/* build the response packet */
+	memset(&resp, 0, sizeof(resp));
+
+	resp.version = RXKAD_VERSION;
+	resp.encrypted.epoch = conn->epoch;
+	resp.encrypted.cid = conn->cid;
+	resp.encrypted.securityIndex = htonl(conn->security_ix);
+	resp.encrypted.call_id[0] =
+		(conn->channels[0] ? conn->channels[0]->call_id : 0);
+	resp.encrypted.call_id[1] =
+		(conn->channels[1] ? conn->channels[1]->call_id : 0);
+	resp.encrypted.call_id[2] =
+		(conn->channels[2] ? conn->channels[2]->call_id : 0);
+	resp.encrypted.call_id[3] =
+		(conn->channels[3] ? conn->channels[3]->call_id : 0);
+	resp.encrypted.inc_nonce = htonl(nonce + 1);
+	resp.encrypted.level = htonl(conn->security_level);
+	resp.kvno = htonl(payload->k.kvno);
+	resp.ticket_len = htonl(payload->k.ticket_len);
+
+	/* calculate the response checksum and then do the encryption */
+	rxkad_calc_response_checksum(&resp);
+	rxkad_encrypt_response(conn, &resp, &payload->k);
+	return rxkad_send_response(conn, &sp->hdr, &resp, &payload->k);
+
+protocol_error:
+	*_abort_code = abort_code;
+	_leave(" = -EPROTO [%d]", abort_code);
+	return -EPROTO;
+}
+
+/*
+ * decrypt the kerberos IV ticket in the response
+ */
+static int rxkad_decrypt_ticket(struct rxrpc_connection *conn,
+				void *ticket, size_t ticket_len,
+				struct rxrpc_crypt *_session_key,
+				time_t *_expiry,
+				u32 *_abort_code)
+{
+	struct blkcipher_desc desc;
+	struct rxrpc_crypt iv, key;
+	struct scatterlist ssg[1], dsg[1];
+	struct in_addr addr;
+	unsigned life;
+	time_t issue, now;
+	bool little_endian;
+	int ret;
+	u8 *p, *q, *name, *end;
+
+	_enter("{%d},{%x}", conn->debug_id, key_serial(conn->server_key));
+
+	*_expiry = 0;
+
+	ret = key_validate(conn->server_key);
+	if (ret < 0) {
+		switch (ret) {
+		case -EKEYEXPIRED:
+			*_abort_code = RXKADEXPIRED;
+			goto error;
+		default:
+			*_abort_code = RXKADNOAUTH;
+			goto error;
+		}
+	}
+
+	ASSERT(conn->server_key->payload.data != NULL);
+	ASSERTCMP((unsigned long) ticket & 7UL, ==, 0);
+
+	memcpy(&iv, &conn->server_key->type_data, sizeof(iv));
+
+	desc.tfm = conn->server_key->payload.data;
+	desc.info = iv.x;
+	desc.flags = 0;
+
+	sg_init_one(&ssg[0], ticket, ticket_len);
+	memcpy(dsg, ssg, sizeof(dsg));
+	crypto_blkcipher_decrypt_iv(&desc, dsg, ssg, ticket_len);
+
+	p = ticket;
+	end = p + ticket_len;
+
+#define Z(size)						\
+	({						\
+		u8 *__str = p;				\
+		q = memchr(p, 0, end - p);		\
+		if (!q || q - p > (size))		\
+			goto bad_ticket;		\
+		for (; p < q; p++)			\
+			if (!isprint(*p))		\
+				goto bad_ticket;	\
+		p++;					\
+		__str;					\
+	})
+
+	/* extract the ticket flags */
+	_debug("KIV FLAGS: %x", *p);
+	little_endian = *p & 1;
+	p++;
+
+	/* extract the authentication name */
+	name = Z(ANAME_SZ);
+	_debug("KIV ANAME: %s", name);
+
+	/* extract the principal's instance */
+	name = Z(INST_SZ);
+	_debug("KIV INST : %s", name);
+
+	/* extract the principal's authentication domain */
+	name = Z(REALM_SZ);
+	_debug("KIV REALM: %s", name);
+
+	if (end - p < 4 + 8 + 4 + 2)
+		goto bad_ticket;
+
+	/* get the IPv4 address of the entity that requested the ticket */
+	memcpy(&addr, p, sizeof(addr));
+	p += 4;
+	_debug("KIV ADDR : "NIPQUAD_FMT, NIPQUAD(addr));
+
+	/* get the session key from the ticket */
+	memcpy(&key, p, sizeof(key));
+	p += 8;
+	_debug("KIV KEY  : %08x %08x", ntohl(key.n[0]), ntohl(key.n[1]));
+	memcpy(_session_key, &key, sizeof(key));
+
+	/* get the ticket's lifetime */
+	life = *p++ * 5 * 60;
+	_debug("KIV LIFE : %u", life);
+
+	/* get the issue time of the ticket */
+	if (little_endian) {
+		__le32 stamp;
+		memcpy(&stamp, p, 4);
+		issue = le32_to_cpu(stamp);
+	} else {
+		__be32 stamp;
+		memcpy(&stamp, p, 4);
+		issue = be32_to_cpu(stamp);
+	}
+	p += 4;
+	now = xtime.tv_sec;
+	_debug("KIV ISSUE: %lx [%lx]", issue, now);
+
+	/* check the ticket is in date */
+	if (issue > now) {
+		*_abort_code = RXKADNOAUTH;
+		ret = -EKEYREJECTED;
+		goto error;
+	}
+
+	if (issue < now - life) {
+		*_abort_code = RXKADEXPIRED;
+		ret = -EKEYEXPIRED;
+		goto error;
+	}
+
+	*_expiry = issue + life;
+
+	/* get the service name */
+	name = Z(SNAME_SZ);
+	_debug("KIV SNAME: %s", name);
+
+	/* get the service instance name */
+	name = Z(INST_SZ);
+	_debug("KIV SINST: %s", name);
+
+	ret = 0;
+error:
+	_leave(" = %d", ret);
+	return ret;
+
+bad_ticket:
+	*_abort_code = RXKADBADTICKET;
+	ret = -EBADMSG;
+	goto error;
+}
+
+/*
+ * decrypt the response packet
+ */
+static void rxkad_decrypt_response(struct rxrpc_connection *conn,
+				   struct rxkad_response *resp,
+				   const struct rxrpc_crypt *session_key)
+{
+	struct blkcipher_desc desc;
+	struct scatterlist ssg[2], dsg[2];
+	struct rxrpc_crypt iv;
+
+	_enter(",,%08x%08x",
+	       ntohl(session_key->n[0]), ntohl(session_key->n[1]));
+
+	ASSERT(rxkad_ci != NULL);
+
+	mutex_lock(&rxkad_ci_mutex);
+	if (crypto_blkcipher_setkey(rxkad_ci, session_key->x,
+				    sizeof(*session_key)) < 0)
+		BUG();
+
+	memcpy(&iv, session_key, sizeof(iv));
+	desc.tfm = rxkad_ci;
+	desc.info = iv.x;
+	desc.flags = 0;
+
+	rxkad_sg_set_buf2(ssg, &resp->encrypted, sizeof(resp->encrypted));
+	memcpy(dsg, ssg, sizeof(dsg));
+	crypto_blkcipher_decrypt_iv(&desc, dsg, ssg, sizeof(resp->encrypted));
+	mutex_unlock(&rxkad_ci_mutex);
+
+	_leave("");
+}
+
+/*
+ * verify a response
+ */
+static int rxkad_verify_response(struct rxrpc_connection *conn,
+				 struct sk_buff *skb,
+				 u32 *_abort_code)
+{
+	struct rxkad_response response
+		__attribute__((aligned(8))); /* must be aligned for crypto */
+	struct rxrpc_skb_priv *sp;
+	struct rxrpc_crypt session_key;
+	time_t expiry;
+	void *ticket;
+	u32 abort_code, version, kvno, ticket_len, csum, level;
+	int ret;
+
+	_enter("{%d,%x}", conn->debug_id, key_serial(conn->server_key));
+
+	abort_code = RXKADPACKETSHORT;
+	if (skb_copy_bits(skb, 0, &response, sizeof(response)) < 0)
+		goto protocol_error;
+	if (!pskb_pull(skb, sizeof(response)))
+		BUG();
+
+	version = ntohl(response.version);
+	ticket_len = ntohl(response.ticket_len);
+	kvno = ntohl(response.kvno);
+	sp = rxrpc_skb(skb);
+	_proto("Rx RESPONSE %%%u { v=%u kv=%u tl=%u }",
+	       ntohl(sp->hdr.serial), version, kvno, ticket_len);
+
+	abort_code = RXKADINCONSISTENCY;
+	if (version != RXKAD_VERSION)
+
+	abort_code = RXKADTICKETLEN;
+	if (ticket_len < 4 || ticket_len > MAXKRB5TICKETLEN)
+		goto protocol_error;
+
+	abort_code = RXKADUNKNOWNKEY;
+	if (kvno >= RXKAD_TKT_TYPE_KERBEROS_V5)
+		goto protocol_error;
+
+	/* extract the kerberos ticket and decrypt and decode it */
+	ticket = kmalloc(ticket_len, GFP_NOFS);
+	if (!ticket)
+		return -ENOMEM;
+
+	abort_code = RXKADPACKETSHORT;
+	if (skb_copy_bits(skb, 0, ticket, ticket_len) < 0)
+		goto protocol_error_free;
+
+	ret = rxkad_decrypt_ticket(conn, ticket, ticket_len, &session_key,
+				   &expiry, &abort_code);
+	if (ret < 0) {
+		*_abort_code = abort_code;
+		kfree(ticket);
+		return ret;
+	}
+
+	/* use the session key from inside the ticket to decrypt the
+	 * response */
+	rxkad_decrypt_response(conn, &response, &session_key);
+
+	abort_code = RXKADSEALEDINCON;
+	if (response.encrypted.epoch != conn->epoch)
+		goto protocol_error_free;
+	if (response.encrypted.cid != conn->cid)
+		goto protocol_error_free;
+	if (ntohl(response.encrypted.securityIndex) != conn->security_ix)
+		goto protocol_error_free;
+	csum = response.encrypted.checksum;
+	response.encrypted.checksum = 0;
+	rxkad_calc_response_checksum(&response);
+	if (response.encrypted.checksum != csum)
+		goto protocol_error_free;
+
+	if (ntohl(response.encrypted.call_id[0]) > INT_MAX ||
+	    ntohl(response.encrypted.call_id[1]) > INT_MAX ||
+	    ntohl(response.encrypted.call_id[2]) > INT_MAX ||
+	    ntohl(response.encrypted.call_id[3]) > INT_MAX)
+		goto protocol_error_free;
+
+	abort_code = RXKADOUTOFSEQUENCE;
+	if (response.encrypted.inc_nonce != htonl(conn->security_nonce + 1))
+		goto protocol_error_free;
+
+	abort_code = RXKADLEVELFAIL;
+	level = ntohl(response.encrypted.level);
+	if (level > RXRPC_SECURITY_ENCRYPT)
+		goto protocol_error_free;
+	conn->security_level = level;
+
+	/* create a key to hold the security data and expiration time - after
+	 * this the connection security can be handled in exactly the same way
+	 * as for a client connection */
+	ret = rxrpc_get_server_data_key(conn, &session_key, expiry, kvno);
+	if (ret < 0) {
+		kfree(ticket);
+		return ret;
+	}
+
+	kfree(ticket);
+	_leave(" = 0");
+	return 0;
+
+protocol_error_free:
+	kfree(ticket);
+protocol_error:
+	*_abort_code = abort_code;
+	_leave(" = -EPROTO [%d]", abort_code);
+	return -EPROTO;
+}
+
+/*
+ * clear the connection security
+ */
+static void rxkad_clear(struct rxrpc_connection *conn)
+{
+	_enter("");
+
+	if (conn->cipher)
+		crypto_free_blkcipher(conn->cipher);
+}
+
+/*
+ * RxRPC Kerberos-based security
+ */
+static struct rxrpc_security rxkad = {
+	.owner				= THIS_MODULE,
+	.name				= "rxkad",
+	.security_index			= RXKAD_VERSION,
+	.init_connection_security	= rxkad_init_connection_security,
+	.prime_packet_security		= rxkad_prime_packet_security,
+	.secure_packet			= rxkad_secure_packet,
+	.verify_packet			= rxkad_verify_packet,
+	.issue_challenge		= rxkad_issue_challenge,
+	.respond_to_challenge		= rxkad_respond_to_challenge,
+	.verify_response		= rxkad_verify_response,
+	.clear				= rxkad_clear,
+};
+
+static __init int rxkad_init(void)
+{
+	_enter("");
+
+	/* pin the cipher we need so that the crypto layer doesn't invoke
+	 * keventd to go get it */
+	rxkad_ci = crypto_alloc_blkcipher("pcbc(fcrypt)", 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(rxkad_ci))
+		return PTR_ERR(rxkad_ci);
+
+	return rxrpc_register_security(&rxkad);
+}
+
+module_init(rxkad_init);
+
+static __exit void rxkad_exit(void)
+{
+	_enter("");
+
+	rxrpc_unregister_security(&rxkad);
+	crypto_free_blkcipher(rxkad_ci);
+}
+
+module_exit(rxkad_exit);
diff --git a/net/rxrpc/rxrpc_syms.c b/net/rxrpc/rxrpc_syms.c
deleted file mode 100644
index 9896fd8..0000000
--- a/net/rxrpc/rxrpc_syms.c
+++ /dev/null
@@ -1,34 +0,0 @@
-/* rxrpc_syms.c: exported Rx RPC layer interface symbols
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#include <linux/module.h>
-
-#include <rxrpc/transport.h>
-#include <rxrpc/connection.h>
-#include <rxrpc/call.h>
-#include <rxrpc/krxiod.h>
-
-/* call.c */
-EXPORT_SYMBOL(rxrpc_create_call);
-EXPORT_SYMBOL(rxrpc_put_call);
-EXPORT_SYMBOL(rxrpc_call_abort);
-EXPORT_SYMBOL(rxrpc_call_read_data);
-EXPORT_SYMBOL(rxrpc_call_write_data);
-
-/* connection.c */
-EXPORT_SYMBOL(rxrpc_create_connection);
-EXPORT_SYMBOL(rxrpc_put_connection);
-
-/* transport.c */
-EXPORT_SYMBOL(rxrpc_create_transport);
-EXPORT_SYMBOL(rxrpc_put_transport);
-EXPORT_SYMBOL(rxrpc_add_service);
-EXPORT_SYMBOL(rxrpc_del_service);
diff --git a/net/rxrpc/sysctl.c b/net/rxrpc/sysctl.c
deleted file mode 100644
index 8842907..0000000
--- a/net/rxrpc/sysctl.c
+++ /dev/null
@@ -1,121 +0,0 @@
-/* sysctl.c: Rx RPC control
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/sysctl.h>
-#include <rxrpc/types.h>
-#include <rxrpc/rxrpc.h>
-#include <asm/errno.h>
-#include "internal.h"
-
-int rxrpc_ktrace;
-int rxrpc_kdebug;
-int rxrpc_kproto;
-int rxrpc_knet;
-
-#ifdef CONFIG_SYSCTL
-static struct ctl_table_header *rxrpc_sysctl = NULL;
-
-static ctl_table rxrpc_sysctl_table[] = {
-	{
-		.ctl_name	= 1,
-		.procname	= "kdebug",
-		.data		= &rxrpc_kdebug,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec
-	},
-	{
-		.ctl_name	= 2,
-		.procname	= "ktrace",
-		.data		= &rxrpc_ktrace,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec
-	},
-	{
-		.ctl_name	= 3,
-		.procname	= "kproto",
-		.data		= &rxrpc_kproto,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec
-	},
-	{
-		.ctl_name	= 4,
-		.procname	= "knet",
-		.data		= &rxrpc_knet,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec
-	},
-	{
-		.ctl_name	= 5,
-		.procname	= "peertimo",
-		.data		= &rxrpc_peer_timeout,
-		.maxlen		= sizeof(unsigned long),
-		.mode		= 0644,
-		.proc_handler	= &proc_doulongvec_minmax
-	},
-	{
-		.ctl_name	= 6,
-		.procname	= "conntimo",
-		.data		= &rxrpc_conn_timeout,
-		.maxlen		= sizeof(unsigned long),
-		.mode		= 0644,
-		.proc_handler	= &proc_doulongvec_minmax
-	},
-	{ .ctl_name = 0 }
-};
-
-static ctl_table rxrpc_dir_sysctl_table[] = {
-	{
-		.ctl_name	= 1,
-		.procname	= "rxrpc",
-		.maxlen		= 0,
-		.mode		= 0555,
-		.child		= rxrpc_sysctl_table
-	},
-	{ .ctl_name = 0 }
-};
-#endif /* CONFIG_SYSCTL */
-
-/*****************************************************************************/
-/*
- * initialise the sysctl stuff for Rx RPC
- */
-int rxrpc_sysctl_init(void)
-{
-#ifdef CONFIG_SYSCTL
-	rxrpc_sysctl = register_sysctl_table(rxrpc_dir_sysctl_table);
-	if (!rxrpc_sysctl)
-		return -ENOMEM;
-#endif /* CONFIG_SYSCTL */
-
-	return 0;
-} /* end rxrpc_sysctl_init() */
-
-/*****************************************************************************/
-/*
- * clean up the sysctl stuff for Rx RPC
- */
-void rxrpc_sysctl_cleanup(void)
-{
-#ifdef CONFIG_SYSCTL
-	if (rxrpc_sysctl) {
-		unregister_sysctl_table(rxrpc_sysctl);
-		rxrpc_sysctl = NULL;
-	}
-#endif /* CONFIG_SYSCTL */
-
-} /* end rxrpc_sysctl_cleanup() */
diff --git a/net/rxrpc/transport.c b/net/rxrpc/transport.c
deleted file mode 100644
index 8e57be2..0000000
--- a/net/rxrpc/transport.c
+++ /dev/null
@@ -1,846 +0,0 @@
-/* transport.c: Rx Transport routines
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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.
- */
-
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <rxrpc/transport.h>
-#include <rxrpc/peer.h>
-#include <rxrpc/connection.h>
-#include <rxrpc/call.h>
-#include <rxrpc/message.h>
-#include <rxrpc/krxiod.h>
-#include <rxrpc/krxsecd.h>
-#include <linux/udp.h>
-#include <linux/in.h>
-#include <linux/in6.h>
-#include <linux/icmp.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <net/ip.h>
-#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-#include <linux/ipv6.h>	/* this should _really_ be in errqueue.h.. */
-#endif
-#include <linux/errqueue.h>
-#include <asm/uaccess.h>
-#include "internal.h"
-
-struct errormsg {
-	struct cmsghdr			cmsg;		/* control message header */
-	struct sock_extended_err	ee;		/* extended error information */
-	struct sockaddr_in		icmp_src;	/* ICMP packet source address */
-};
-
-static DEFINE_SPINLOCK(rxrpc_transports_lock);
-static struct list_head rxrpc_transports = LIST_HEAD_INIT(rxrpc_transports);
-
-__RXACCT_DECL(atomic_t rxrpc_transport_count);
-LIST_HEAD(rxrpc_proc_transports);
-DECLARE_RWSEM(rxrpc_proc_transports_sem);
-
-static void rxrpc_data_ready(struct sock *sk, int count);
-static void rxrpc_error_report(struct sock *sk);
-static int rxrpc_trans_receive_new_call(struct rxrpc_transport *trans,
-					struct list_head *msgq);
-static void rxrpc_trans_receive_error_report(struct rxrpc_transport *trans);
-
-/*****************************************************************************/
-/*
- * create a new transport endpoint using the specified UDP port
- */
-int rxrpc_create_transport(unsigned short port,
-			   struct rxrpc_transport **_trans)
-{
-	struct rxrpc_transport *trans;
-	struct sockaddr_in sin;
-	mm_segment_t oldfs;
-	struct sock *sock;
-	int ret, opt;
-
-	_enter("%hu", port);
-
-	trans = kzalloc(sizeof(struct rxrpc_transport), GFP_KERNEL);
-	if (!trans)
-		return -ENOMEM;
-
-	atomic_set(&trans->usage, 1);
-	INIT_LIST_HEAD(&trans->services);
-	INIT_LIST_HEAD(&trans->link);
-	INIT_LIST_HEAD(&trans->krxiodq_link);
-	spin_lock_init(&trans->lock);
-	INIT_LIST_HEAD(&trans->peer_active);
-	INIT_LIST_HEAD(&trans->peer_graveyard);
-	spin_lock_init(&trans->peer_gylock);
-	init_waitqueue_head(&trans->peer_gy_waitq);
-	rwlock_init(&trans->peer_lock);
-	atomic_set(&trans->peer_count, 0);
-	trans->port = port;
-
-	/* create a UDP socket to be my actual transport endpoint */
-	ret = sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &trans->socket);
-	if (ret < 0)
-		goto error;
-
-	/* use the specified port */
-	if (port) {
-		memset(&sin, 0, sizeof(sin));
-		sin.sin_family = AF_INET;
-		sin.sin_port = htons(port);
-		ret = trans->socket->ops->bind(trans->socket,
-					       (struct sockaddr *) &sin,
-					       sizeof(sin));
-		if (ret < 0)
-			goto error;
-	}
-
-	opt = 1;
-	oldfs = get_fs();
-	set_fs(KERNEL_DS);
-	ret = trans->socket->ops->setsockopt(trans->socket, SOL_IP, IP_RECVERR,
-					     (char *) &opt, sizeof(opt));
-	set_fs(oldfs);
-
-	spin_lock(&rxrpc_transports_lock);
-	list_add(&trans->link, &rxrpc_transports);
-	spin_unlock(&rxrpc_transports_lock);
-
-	/* set the socket up */
-	sock = trans->socket->sk;
-	sock->sk_user_data	= trans;
-	sock->sk_data_ready	= rxrpc_data_ready;
-	sock->sk_error_report	= rxrpc_error_report;
-
-	down_write(&rxrpc_proc_transports_sem);
-	list_add_tail(&trans->proc_link, &rxrpc_proc_transports);
-	up_write(&rxrpc_proc_transports_sem);
-
-	__RXACCT(atomic_inc(&rxrpc_transport_count));
-
-	*_trans = trans;
-	_leave(" = 0 (%p)", trans);
-	return 0;
-
- error:
-	/* finish cleaning up the transport (not really needed here, but...) */
-	if (trans->socket)
-		trans->socket->ops->shutdown(trans->socket, 2);
-
-	/* close the socket */
-	if (trans->socket) {
-		trans->socket->sk->sk_user_data = NULL;
-		sock_release(trans->socket);
-		trans->socket = NULL;
-	}
-
-	kfree(trans);
-
-
-	_leave(" = %d", ret);
-	return ret;
-} /* end rxrpc_create_transport() */
-
-/*****************************************************************************/
-/*
- * destroy a transport endpoint
- */
-void rxrpc_put_transport(struct rxrpc_transport *trans)
-{
-	_enter("%p{u=%d p=%hu}",
-	       trans, atomic_read(&trans->usage), trans->port);
-
-	BUG_ON(atomic_read(&trans->usage) <= 0);
-
-	/* to prevent a race, the decrement and the dequeue must be
-	 * effectively atomic */
-	spin_lock(&rxrpc_transports_lock);
-	if (likely(!atomic_dec_and_test(&trans->usage))) {
-		spin_unlock(&rxrpc_transports_lock);
-		_leave("");
-		return;
-	}
-
-	list_del(&trans->link);
-	spin_unlock(&rxrpc_transports_lock);
-
-	/* finish cleaning up the transport */
-	if (trans->socket)
-		trans->socket->ops->shutdown(trans->socket, 2);
-
-	rxrpc_krxsecd_clear_transport(trans);
-	rxrpc_krxiod_dequeue_transport(trans);
-
-	/* discard all peer information */
-	rxrpc_peer_clearall(trans);
-
-	down_write(&rxrpc_proc_transports_sem);
-	list_del(&trans->proc_link);
-	up_write(&rxrpc_proc_transports_sem);
-	__RXACCT(atomic_dec(&rxrpc_transport_count));
-
-	/* close the socket */
-	if (trans->socket) {
-		trans->socket->sk->sk_user_data = NULL;
-		sock_release(trans->socket);
-		trans->socket = NULL;
-	}
-
-	kfree(trans);
-
-	_leave("");
-} /* end rxrpc_put_transport() */
-
-/*****************************************************************************/
-/*
- * add a service to a transport to be listened upon
- */
-int rxrpc_add_service(struct rxrpc_transport *trans,
-		      struct rxrpc_service *newsrv)
-{
-	struct rxrpc_service *srv;
-	struct list_head *_p;
-	int ret = -EEXIST;
-
-	_enter("%p{%hu},%p{%hu}",
-	       trans, trans->port, newsrv, newsrv->service_id);
-
-	/* verify that the service ID is not already present */
-	spin_lock(&trans->lock);
-
-	list_for_each(_p, &trans->services) {
-		srv = list_entry(_p, struct rxrpc_service, link);
-		if (srv->service_id == newsrv->service_id)
-			goto out;
-	}
-
-	/* okay - add the transport to the list */
-	list_add_tail(&newsrv->link, &trans->services);
-	rxrpc_get_transport(trans);
-	ret = 0;
-
- out:
-	spin_unlock(&trans->lock);
-
-	_leave("= %d", ret);
-	return ret;
-} /* end rxrpc_add_service() */
-
-/*****************************************************************************/
-/*
- * remove a service from a transport
- */
-void rxrpc_del_service(struct rxrpc_transport *trans, struct rxrpc_service *srv)
-{
-	_enter("%p{%hu},%p{%hu}", trans, trans->port, srv, srv->service_id);
-
-	spin_lock(&trans->lock);
-	list_del(&srv->link);
-	spin_unlock(&trans->lock);
-
-	rxrpc_put_transport(trans);
-
-	_leave("");
-} /* end rxrpc_del_service() */
-
-/*****************************************************************************/
-/*
- * INET callback when data has been received on the socket.
- */
-static void rxrpc_data_ready(struct sock *sk, int count)
-{
-	struct rxrpc_transport *trans;
-
-	_enter("%p{t=%p},%d", sk, sk->sk_user_data, count);
-
-	/* queue the transport for attention by krxiod */
-	trans = (struct rxrpc_transport *) sk->sk_user_data;
-	if (trans)
-		rxrpc_krxiod_queue_transport(trans);
-
-	/* wake up anyone waiting on the socket */
-	if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))
-		wake_up_interruptible(sk->sk_sleep);
-
-	_leave("");
-} /* end rxrpc_data_ready() */
-
-/*****************************************************************************/
-/*
- * INET callback when an ICMP error packet is received
- * - sk->err is error (EHOSTUNREACH, EPROTO or EMSGSIZE)
- */
-static void rxrpc_error_report(struct sock *sk)
-{
-	struct rxrpc_transport *trans;
-
-	_enter("%p{t=%p}", sk, sk->sk_user_data);
-
-	/* queue the transport for attention by krxiod */
-	trans = (struct rxrpc_transport *) sk->sk_user_data;
-	if (trans) {
-		trans->error_rcvd = 1;
-		rxrpc_krxiod_queue_transport(trans);
-	}
-
-	/* wake up anyone waiting on the socket */
-	if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))
-		wake_up_interruptible(sk->sk_sleep);
-
-	_leave("");
-} /* end rxrpc_error_report() */
-
-/*****************************************************************************/
-/*
- * split a message up, allocating message records and filling them in
- * from the contents of a socket buffer
- */
-static int rxrpc_incoming_msg(struct rxrpc_transport *trans,
-			      struct sk_buff *pkt,
-			      struct list_head *msgq)
-{
-	struct rxrpc_message *msg;
-	int ret;
-
-	_enter("");
-
-	msg = kzalloc(sizeof(struct rxrpc_message), GFP_KERNEL);
-	if (!msg) {
-		_leave(" = -ENOMEM");
-		return -ENOMEM;
-	}
-
-	atomic_set(&msg->usage, 1);
-	list_add_tail(&msg->link,msgq);
-
-	/* dig out the Rx routing parameters */
-	if (skb_copy_bits(pkt, sizeof(struct udphdr),
-			  &msg->hdr, sizeof(msg->hdr)) < 0) {
-		ret = -EBADMSG;
-		goto error;
-	}
-
-	msg->trans = trans;
-	msg->state = RXRPC_MSG_RECEIVED;
-	skb_get_timestamp(pkt, &msg->stamp);
-	if (msg->stamp.tv_sec == 0) {
-		do_gettimeofday(&msg->stamp);
-		if (pkt->sk)
-			sock_enable_timestamp(pkt->sk);
-	}
-	msg->seq = ntohl(msg->hdr.seq);
-
-	/* attach the packet */
-	skb_get(pkt);
-	msg->pkt = pkt;
-
-	msg->offset = sizeof(struct udphdr) + sizeof(struct rxrpc_header);
-	msg->dsize = msg->pkt->len - msg->offset;
-
-	_net("Rx Received packet from %s (%08x;%08x,%1x,%d,%s,%02x,%d,%d)",
-	     msg->hdr.flags & RXRPC_CLIENT_INITIATED ? "client" : "server",
-	     ntohl(msg->hdr.epoch),
-	     (ntohl(msg->hdr.cid) & RXRPC_CIDMASK) >> RXRPC_CIDSHIFT,
-	     ntohl(msg->hdr.cid) & RXRPC_CHANNELMASK,
-	     ntohl(msg->hdr.callNumber),
-	     rxrpc_pkts[msg->hdr.type],
-	     msg->hdr.flags,
-	     ntohs(msg->hdr.serviceId),
-	     msg->hdr.securityIndex);
-
-	__RXACCT(atomic_inc(&rxrpc_message_count));
-
-	/* split off jumbo packets */
-	while (msg->hdr.type == RXRPC_PACKET_TYPE_DATA &&
-	       msg->hdr.flags & RXRPC_JUMBO_PACKET
-	       ) {
-		struct rxrpc_jumbo_header jumbo;
-		struct rxrpc_message *jumbomsg = msg;
-
-		_debug("split jumbo packet");
-
-		/* quick sanity check */
-		ret = -EBADMSG;
-		if (msg->dsize <
-		    RXRPC_JUMBO_DATALEN + sizeof(struct rxrpc_jumbo_header))
-			goto error;
-		if (msg->hdr.flags & RXRPC_LAST_PACKET)
-			goto error;
-
-		/* dig out the secondary header */
-		if (skb_copy_bits(pkt, msg->offset + RXRPC_JUMBO_DATALEN,
-				  &jumbo, sizeof(jumbo)) < 0)
-			goto error;
-
-		/* allocate a new message record */
-		ret = -ENOMEM;
-		msg = kmemdup(jumbomsg, sizeof(struct rxrpc_message), GFP_KERNEL);
-		if (!msg)
-			goto error;
-
-		list_add_tail(&msg->link, msgq);
-
-		/* adjust the jumbo packet */
-		jumbomsg->dsize = RXRPC_JUMBO_DATALEN;
-
-		/* attach the packet here too */
-		skb_get(pkt);
-
-		/* adjust the parameters */
-		msg->seq++;
-		msg->hdr.seq = htonl(msg->seq);
-		msg->hdr.serial = htonl(ntohl(msg->hdr.serial) + 1);
-		msg->offset += RXRPC_JUMBO_DATALEN +
-			sizeof(struct rxrpc_jumbo_header);
-		msg->dsize -= RXRPC_JUMBO_DATALEN +
-			sizeof(struct rxrpc_jumbo_header);
-		msg->hdr.flags = jumbo.flags;
-		msg->hdr._rsvd = jumbo._rsvd;
-
-		_net("Rx Split jumbo packet from %s"
-		     " (%08x;%08x,%1x,%d,%s,%02x,%d,%d)",
-		     msg->hdr.flags & RXRPC_CLIENT_INITIATED ? "client" : "server",
-		     ntohl(msg->hdr.epoch),
-		     (ntohl(msg->hdr.cid) & RXRPC_CIDMASK) >> RXRPC_CIDSHIFT,
-		     ntohl(msg->hdr.cid) & RXRPC_CHANNELMASK,
-		     ntohl(msg->hdr.callNumber),
-		     rxrpc_pkts[msg->hdr.type],
-		     msg->hdr.flags,
-		     ntohs(msg->hdr.serviceId),
-		     msg->hdr.securityIndex);
-
-		__RXACCT(atomic_inc(&rxrpc_message_count));
-	}
-
-	_leave(" = 0 #%d", atomic_read(&rxrpc_message_count));
-	return 0;
-
- error:
-	while (!list_empty(msgq)) {
-		msg = list_entry(msgq->next, struct rxrpc_message, link);
-		list_del_init(&msg->link);
-
-		rxrpc_put_message(msg);
-	}
-
-	_leave(" = %d", ret);
-	return ret;
-} /* end rxrpc_incoming_msg() */
-
-/*****************************************************************************/
-/*
- * accept a new call
- * - called from krxiod in process context
- */
-void rxrpc_trans_receive_packet(struct rxrpc_transport *trans)
-{
-	struct rxrpc_message *msg;
-	struct rxrpc_peer *peer;
-	struct sk_buff *pkt;
-	int ret;
-	__be32 addr;
-	__be16 port;
-
-	LIST_HEAD(msgq);
-
-	_enter("%p{%d}", trans, trans->port);
-
-	for (;;) {
-		/* deal with outstanting errors first */
-		if (trans->error_rcvd)
-			rxrpc_trans_receive_error_report(trans);
-
-		/* attempt to receive a packet */
-		pkt = skb_recv_datagram(trans->socket->sk, 0, 1, &ret);
-		if (!pkt) {
-			if (ret == -EAGAIN) {
-				_leave(" EAGAIN");
-				return;
-			}
-
-			/* an icmp error may have occurred */
-			rxrpc_krxiod_queue_transport(trans);
-			_leave(" error %d\n", ret);
-			return;
-		}
-
-		/* we'll probably need to checksum it (didn't call
-		 * sock_recvmsg) */
-		if (skb_checksum_complete(pkt)) {
-			kfree_skb(pkt);
-			rxrpc_krxiod_queue_transport(trans);
-			_leave(" CSUM failed");
-			return;
-		}
-
-		addr = pkt->nh.iph->saddr;
-		port = pkt->h.uh->source;
-
-		_net("Rx Received UDP packet from %08x:%04hu",
-		     ntohl(addr), ntohs(port));
-
-		/* unmarshall the Rx parameters and split jumbo packets */
-		ret = rxrpc_incoming_msg(trans, pkt, &msgq);
-		if (ret < 0) {
-			kfree_skb(pkt);
-			rxrpc_krxiod_queue_transport(trans);
-			_leave(" bad packet");
-			return;
-		}
-
-		BUG_ON(list_empty(&msgq));
-
-		msg = list_entry(msgq.next, struct rxrpc_message, link);
-
-		/* locate the record for the peer from which it
-		 * originated */
-		ret = rxrpc_peer_lookup(trans, addr, &peer);
-		if (ret < 0) {
-			kdebug("Rx No connections from that peer");
-			rxrpc_trans_immediate_abort(trans, msg, -EINVAL);
-			goto finished_msg;
-		}
-
-		/* try and find a matching connection */
-		ret = rxrpc_connection_lookup(peer, msg, &msg->conn);
-		if (ret < 0) {
-			kdebug("Rx Unknown Connection");
-			rxrpc_trans_immediate_abort(trans, msg, -EINVAL);
-			rxrpc_put_peer(peer);
-			goto finished_msg;
-		}
-		rxrpc_put_peer(peer);
-
-		/* deal with the first packet of a new call */
-		if (msg->hdr.flags & RXRPC_CLIENT_INITIATED &&
-		    msg->hdr.type == RXRPC_PACKET_TYPE_DATA &&
-		    ntohl(msg->hdr.seq) == 1
-		    ) {
-			_debug("Rx New server call");
-			rxrpc_trans_receive_new_call(trans, &msgq);
-			goto finished_msg;
-		}
-
-		/* deal with subsequent packet(s) of call */
-		_debug("Rx Call packet");
-		while (!list_empty(&msgq)) {
-			msg = list_entry(msgq.next, struct rxrpc_message, link);
-			list_del_init(&msg->link);
-
-			ret = rxrpc_conn_receive_call_packet(msg->conn, NULL, msg);
-			if (ret < 0) {
-				rxrpc_trans_immediate_abort(trans, msg, ret);
-				rxrpc_put_message(msg);
-				goto finished_msg;
-			}
-
-			rxrpc_put_message(msg);
-		}
-
-		goto finished_msg;
-
-		/* dispose of the packets */
-	finished_msg:
-		while (!list_empty(&msgq)) {
-			msg = list_entry(msgq.next, struct rxrpc_message, link);
-			list_del_init(&msg->link);
-
-			rxrpc_put_message(msg);
-		}
-		kfree_skb(pkt);
-	}
-
-	_leave("");
-
-} /* end rxrpc_trans_receive_packet() */
-
-/*****************************************************************************/
-/*
- * accept a new call from a client trying to connect to one of my services
- * - called in process context
- */
-static int rxrpc_trans_receive_new_call(struct rxrpc_transport *trans,
-					struct list_head *msgq)
-{
-	struct rxrpc_message *msg;
-
-	_enter("");
-
-	/* only bother with the first packet */
-	msg = list_entry(msgq->next, struct rxrpc_message, link);
-	list_del_init(&msg->link);
-	rxrpc_krxsecd_queue_incoming_call(msg);
-	rxrpc_put_message(msg);
-
-	_leave(" = 0");
-
-	return 0;
-} /* end rxrpc_trans_receive_new_call() */
-
-/*****************************************************************************/
-/*
- * perform an immediate abort without connection or call structures
- */
-int rxrpc_trans_immediate_abort(struct rxrpc_transport *trans,
-				struct rxrpc_message *msg,
-				int error)
-{
-	struct rxrpc_header ahdr;
-	struct sockaddr_in sin;
-	struct msghdr msghdr;
-	struct kvec iov[2];
-	__be32 _error;
-	int len, ret;
-
-	_enter("%p,%p,%d", trans, msg, error);
-
-	/* don't abort an abort packet */
-	if (msg->hdr.type == RXRPC_PACKET_TYPE_ABORT) {
-		_leave(" = 0");
-		return 0;
-	}
-
-	_error = htonl(-error);
-
-	/* set up the message to be transmitted */
-	memcpy(&ahdr, &msg->hdr, sizeof(ahdr));
-	ahdr.epoch	= msg->hdr.epoch;
-	ahdr.serial	= htonl(1);
-	ahdr.seq	= 0;
-	ahdr.type	= RXRPC_PACKET_TYPE_ABORT;
-	ahdr.flags	= RXRPC_LAST_PACKET;
-	ahdr.flags	|= ~msg->hdr.flags & RXRPC_CLIENT_INITIATED;
-
-	iov[0].iov_len	= sizeof(ahdr);
-	iov[0].iov_base	= &ahdr;
-	iov[1].iov_len	= sizeof(_error);
-	iov[1].iov_base	= &_error;
-
-	len = sizeof(ahdr) + sizeof(_error);
-
-	memset(&sin,0,sizeof(sin));
-	sin.sin_family		= AF_INET;
-	sin.sin_port		= msg->pkt->h.uh->source;
-	sin.sin_addr.s_addr	= msg->pkt->nh.iph->saddr;
-
-	msghdr.msg_name		= &sin;
-	msghdr.msg_namelen	= sizeof(sin);
-	msghdr.msg_control	= NULL;
-	msghdr.msg_controllen	= 0;
-	msghdr.msg_flags	= MSG_DONTWAIT;
-
-	_net("Sending message type %d of %d bytes to %08x:%d",
-	     ahdr.type,
-	     len,
-	     ntohl(sin.sin_addr.s_addr),
-	     ntohs(sin.sin_port));
-
-	/* send the message */
-	ret = kernel_sendmsg(trans->socket, &msghdr, iov, 2, len);
-
-	_leave(" = %d", ret);
-	return ret;
-} /* end rxrpc_trans_immediate_abort() */
-
-/*****************************************************************************/
-/*
- * receive an ICMP error report and percolate it to all connections
- * heading to the affected host or port
- */
-static void rxrpc_trans_receive_error_report(struct rxrpc_transport *trans)
-{
-	struct rxrpc_connection *conn;
-	struct sockaddr_in sin;
-	struct rxrpc_peer *peer;
-	struct list_head connq, *_p;
-	struct errormsg emsg;
-	struct msghdr msg;
-	__be16 port;
-	int local, err;
-
-	_enter("%p", trans);
-
-	for (;;) {
-		trans->error_rcvd = 0;
-
-		/* try and receive an error message */
-		msg.msg_name	= &sin;
-		msg.msg_namelen	= sizeof(sin);
-		msg.msg_control	= &emsg;
-		msg.msg_controllen = sizeof(emsg);
-		msg.msg_flags	= 0;
-
-		err = kernel_recvmsg(trans->socket, &msg, NULL, 0, 0,
-				   MSG_ERRQUEUE | MSG_DONTWAIT | MSG_TRUNC);
-
-		if (err == -EAGAIN) {
-			_leave("");
-			return;
-		}
-
-		if (err < 0) {
-			printk("%s: unable to recv an error report: %d\n",
-			       __FUNCTION__, err);
-			_leave("");
-			return;
-		}
-
-		msg.msg_controllen = (char *) msg.msg_control - (char *) &emsg;
-
-		if (msg.msg_controllen < sizeof(emsg.cmsg) ||
-		    msg.msg_namelen < sizeof(sin)) {
-			printk("%s: short control message"
-			       " (nlen=%u clen=%Zu fl=%x)\n",
-			       __FUNCTION__,
-			       msg.msg_namelen,
-			       msg.msg_controllen,
-			       msg.msg_flags);
-			continue;
-		}
-
-		_net("Rx Received control message"
-		     " { len=%Zu level=%u type=%u }",
-		     emsg.cmsg.cmsg_len,
-		     emsg.cmsg.cmsg_level,
-		     emsg.cmsg.cmsg_type);
-
-		if (sin.sin_family != AF_INET) {
-			printk("Rx Ignoring error report with non-INET address"
-			       " (fam=%u)",
-			       sin.sin_family);
-			continue;
-		}
-
-		_net("Rx Received message pertaining to host addr=%x port=%hu",
-		     ntohl(sin.sin_addr.s_addr), ntohs(sin.sin_port));
-
-		if (emsg.cmsg.cmsg_level != SOL_IP ||
-		    emsg.cmsg.cmsg_type != IP_RECVERR) {
-			printk("Rx Ignoring unknown error report"
-			       " { level=%u type=%u }",
-			       emsg.cmsg.cmsg_level,
-			       emsg.cmsg.cmsg_type);
-			continue;
-		}
-
-		if (msg.msg_controllen < sizeof(emsg.cmsg) + sizeof(emsg.ee)) {
-			printk("%s: short error message (%Zu)\n",
-			       __FUNCTION__, msg.msg_controllen);
-			_leave("");
-			return;
-		}
-
-		port = sin.sin_port;
-
-		switch (emsg.ee.ee_origin) {
-		case SO_EE_ORIGIN_ICMP:
-			local = 0;
-			switch (emsg.ee.ee_type) {
-			case ICMP_DEST_UNREACH:
-				switch (emsg.ee.ee_code) {
-				case ICMP_NET_UNREACH:
-					_net("Rx Received ICMP Network Unreachable");
-					port = 0;
-					err = -ENETUNREACH;
-					break;
-				case ICMP_HOST_UNREACH:
-					_net("Rx Received ICMP Host Unreachable");
-					port = 0;
-					err = -EHOSTUNREACH;
-					break;
-				case ICMP_PORT_UNREACH:
-					_net("Rx Received ICMP Port Unreachable");
-					err = -ECONNREFUSED;
-					break;
-				case ICMP_NET_UNKNOWN:
-					_net("Rx Received ICMP Unknown Network");
-					port = 0;
-					err = -ENETUNREACH;
-					break;
-				case ICMP_HOST_UNKNOWN:
-					_net("Rx Received ICMP Unknown Host");
-					port = 0;
-					err = -EHOSTUNREACH;
-					break;
-				default:
-					_net("Rx Received ICMP DestUnreach { code=%u }",
-					     emsg.ee.ee_code);
-					err = emsg.ee.ee_errno;
-					break;
-				}
-				break;
-
-			case ICMP_TIME_EXCEEDED:
-				_net("Rx Received ICMP TTL Exceeded");
-				err = emsg.ee.ee_errno;
-				break;
-
-			default:
-				_proto("Rx Received ICMP error { type=%u code=%u }",
-				       emsg.ee.ee_type, emsg.ee.ee_code);
-				err = emsg.ee.ee_errno;
-				break;
-			}
-			break;
-
-		case SO_EE_ORIGIN_LOCAL:
-			_proto("Rx Received local error { error=%d }",
-			       emsg.ee.ee_errno);
-			local = 1;
-			err = emsg.ee.ee_errno;
-			break;
-
-		case SO_EE_ORIGIN_NONE:
-		case SO_EE_ORIGIN_ICMP6:
-		default:
-			_proto("Rx Received error report { orig=%u }",
-			       emsg.ee.ee_origin);
-			local = 0;
-			err = emsg.ee.ee_errno;
-			break;
-		}
-
-		/* find all the connections between this transport and the
-		 * affected destination */
-		INIT_LIST_HEAD(&connq);
-
-		if (rxrpc_peer_lookup(trans, sin.sin_addr.s_addr,
-				      &peer) == 0) {
-			read_lock(&peer->conn_lock);
-			list_for_each(_p, &peer->conn_active) {
-				conn = list_entry(_p, struct rxrpc_connection,
-						  link);
-				if (port && conn->addr.sin_port != port)
-					continue;
-				if (!list_empty(&conn->err_link))
-					continue;
-
-				rxrpc_get_connection(conn);
-				list_add_tail(&conn->err_link, &connq);
-			}
-			read_unlock(&peer->conn_lock);
-
-			/* service all those connections */
-			while (!list_empty(&connq)) {
-				conn = list_entry(connq.next,
-						  struct rxrpc_connection,
-						  err_link);
-				list_del(&conn->err_link);
-
-				rxrpc_conn_handle_error(conn, local, err);
-
-				rxrpc_put_connection(conn);
-			}
-
-			rxrpc_put_peer(peer);
-		}
-	}
-
-	_leave("");
-	return;
-} /* end rxrpc_trans_receive_error_report() */
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index f4544dd..475df84 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -46,62 +46,6 @@
 
 if NET_SCHED
 
-choice
-	prompt "Packet scheduler clock source"
-	default NET_SCH_CLK_GETTIMEOFDAY
-	---help---
-	  Packet schedulers need a monotonic clock that increments at a static
-	  rate. The kernel provides several suitable interfaces, each with
-	  different properties:
-	  
-	  - high resolution (us or better)
-	  - fast to read (minimal locking, no i/o access)
-	  - synchronized on all processors
-	  - handles cpu clock frequency changes
-
-	  but nothing provides all of the above.
-
-config NET_SCH_CLK_JIFFIES
-	bool "Timer interrupt"
-	---help---
-	  Say Y here if you want to use the timer interrupt (jiffies) as clock
-	  source. This clock source is fast, synchronized on all processors and
-	  handles cpu clock frequency changes, but its resolution is too low
-	  for accurate shaping except at very low speed.
-
-config NET_SCH_CLK_GETTIMEOFDAY
-	bool "gettimeofday"
-	---help---
-	  Say Y here if you want to use gettimeofday as clock source. This clock
-	  source has high resolution, is synchronized on all processors and
-	  handles cpu clock frequency changes, but it is slow.
-
-	  Choose this if you need a high resolution clock source but can't use
-	  the CPU's cycle counter.
-
-# don't allow on SMP x86 because they can have unsynchronized TSCs.
-# gettimeofday is a good alternative
-config NET_SCH_CLK_CPU
-	bool "CPU cycle counter"
-	depends on ((X86_TSC || X86_64) && !SMP) || ALPHA || SPARC64 || PPC64 || IA64
-	---help---
-	  Say Y here if you want to use the CPU's cycle counter as clock source.
-	  This is a cheap and high resolution clock source, but on some
-	  architectures it is not synchronized on all processors and doesn't
-	  handle cpu clock frequency changes.
-
-	  The useable cycle counters are:
-
-	  	x86/x86_64	- Timestamp Counter
-		alpha		- Cycle Counter
-		sparc64		- %ticks register
-		ppc64		- Time base
-		ia64		- Interval Time Counter
-
-	  Choose this if your CPU's cycle counter is working properly.
-
-endchoice
-
 comment "Queueing/Scheduling"
 
 config NET_SCH_CBQ
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index cb21617..711dd26 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -25,12 +25,12 @@
 #include <linux/interrupt.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
-#include <linux/rtnetlink.h>
 #include <linux/init.h>
 #include <linux/kmod.h>
 #include <net/sock.h>
 #include <net/sch_generic.h>
 #include <net/act_api.h>
+#include <net/netlink.h>
 
 void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo)
 {
@@ -93,15 +93,15 @@
 				continue;
 			a->priv = p;
 			a->order = n_i;
-			r = (struct rtattr*) skb->tail;
+			r = (struct rtattr *)skb_tail_pointer(skb);
 			RTA_PUT(skb, a->order, 0, NULL);
 			err = tcf_action_dump_1(skb, a, 0, 0);
 			if (err < 0) {
 				index--;
-				skb_trim(skb, (u8*)r - skb->data);
+				nlmsg_trim(skb, r);
 				goto done;
 			}
-			r->rta_len = skb->tail - (u8*)r;
+			r->rta_len = skb_tail_pointer(skb) - (u8 *)r;
 			n_i++;
 			if (n_i >= TCA_ACT_MAX_PRIO)
 				goto done;
@@ -114,7 +114,7 @@
 	return n_i;
 
 rtattr_failure:
-	skb_trim(skb, (u8*)r - skb->data);
+	nlmsg_trim(skb, r);
 	goto done;
 }
 
@@ -125,7 +125,7 @@
 	struct rtattr *r ;
 	int i= 0, n_i = 0;
 
-	r = (struct rtattr*) skb->tail;
+	r = (struct rtattr *)skb_tail_pointer(skb);
 	RTA_PUT(skb, a->order, 0, NULL);
 	RTA_PUT(skb, TCA_KIND, IFNAMSIZ, a->ops->kind);
 	for (i = 0; i < (hinfo->hmask + 1); i++) {
@@ -140,11 +140,11 @@
 		}
 	}
 	RTA_PUT(skb, TCA_FCNT, 4, &n_i);
-	r->rta_len = skb->tail - (u8*)r;
+	r->rta_len = skb_tail_pointer(skb) - (u8 *)r;
 
 	return n_i;
 rtattr_failure:
-	skb_trim(skb, (u8*)r - skb->data);
+	nlmsg_trim(skb, r);
 	return -EINVAL;
 }
 
@@ -423,7 +423,7 @@
 tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
 {
 	int err = -EINVAL;
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct rtattr *r;
 
 	if (a->ops == NULL || a->ops->dump == NULL)
@@ -432,15 +432,15 @@
 	RTA_PUT(skb, TCA_KIND, IFNAMSIZ, a->ops->kind);
 	if (tcf_action_copy_stats(skb, a, 0))
 		goto rtattr_failure;
-	r = (struct rtattr*) skb->tail;
+	r = (struct rtattr *)skb_tail_pointer(skb);
 	RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
 	if ((err = tcf_action_dump_old(skb, a, bind, ref)) > 0) {
-		r->rta_len = skb->tail - (u8*)r;
+		r->rta_len = skb_tail_pointer(skb) - (u8 *)r;
 		return err;
 	}
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -449,17 +449,17 @@
 {
 	struct tc_action *a;
 	int err = -EINVAL;
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct rtattr *r ;
 
 	while ((a = act) != NULL) {
-		r = (struct rtattr*) skb->tail;
+		r = (struct rtattr *)skb_tail_pointer(skb);
 		act = a->next;
 		RTA_PUT(skb, a->order, 0, NULL);
 		err = tcf_action_dump_1(skb, a, bind, ref);
 		if (err < 0)
 			goto errout;
-		r->rta_len = skb->tail - (u8*)r;
+		r->rta_len = skb_tail_pointer(skb) - (u8 *)r;
 	}
 
 	return 0;
@@ -467,7 +467,7 @@
 rtattr_failure:
 	err = -EINVAL;
 errout:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return err;
 }
 
@@ -635,7 +635,7 @@
 {
 	struct tcamsg *t;
 	struct nlmsghdr *nlh;
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct rtattr *x;
 
 	nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*t), flags);
@@ -645,20 +645,20 @@
 	t->tca__pad1 = 0;
 	t->tca__pad2 = 0;
 
-	x = (struct rtattr*) skb->tail;
+	x = (struct rtattr *)skb_tail_pointer(skb);
 	RTA_PUT(skb, TCA_ACT_TAB, 0, NULL);
 
 	if (tcf_action_dump(skb, a, bind, ref) < 0)
 		goto rtattr_failure;
 
-	x->rta_len = skb->tail - (u8*)x;
+	x->rta_len = skb_tail_pointer(skb) - (u8 *)x;
 
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
 rtattr_failure:
 nlmsg_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -767,7 +767,7 @@
 		return -ENOBUFS;
 	}
 
-	b = (unsigned char *)skb->tail;
+	b = skb_tail_pointer(skb);
 
 	if (rtattr_parse_nested(tb, TCA_ACT_MAX, rta) < 0)
 		goto err_out;
@@ -783,16 +783,16 @@
 	t->tca__pad1 = 0;
 	t->tca__pad2 = 0;
 
-	x = (struct rtattr *) skb->tail;
+	x = (struct rtattr *)skb_tail_pointer(skb);
 	RTA_PUT(skb, TCA_ACT_TAB, 0, NULL);
 
 	err = a->ops->walk(skb, &dcb, RTM_DELACTION, a);
 	if (err < 0)
 		goto rtattr_failure;
 
-	x->rta_len = skb->tail - (u8 *) x;
+	x->rta_len = skb_tail_pointer(skb) - (u8 *)x;
 
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 	nlh->nlmsg_flags |= NLM_F_ROOT;
 	module_put(a->ops->owner);
 	kfree(a);
@@ -884,7 +884,7 @@
 	if (!skb)
 		return -ENOBUFS;
 
-	b = (unsigned char *)skb->tail;
+	b = skb_tail_pointer(skb);
 
 	nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*t), flags);
 	t = NLMSG_DATA(nlh);
@@ -892,15 +892,15 @@
 	t->tca__pad1 = 0;
 	t->tca__pad2 = 0;
 
-	x = (struct rtattr*) skb->tail;
+	x = (struct rtattr *)skb_tail_pointer(skb);
 	RTA_PUT(skb, TCA_ACT_TAB, 0, NULL);
 
 	if (tcf_action_dump(skb, a, 0, 0) < 0)
 		goto rtattr_failure;
 
-	x->rta_len = skb->tail - (u8*)x;
+	x->rta_len = skb_tail_pointer(skb) - (u8 *)x;
 
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 	NETLINK_CB(skb).dst_group = RTNLGRP_TC;
 
 	err = rtnetlink_send(skb, pid, RTNLGRP_TC, flags&NLM_F_ECHO);
@@ -1015,7 +1015,7 @@
 tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb)
 {
 	struct nlmsghdr *nlh;
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct rtattr *x;
 	struct tc_action_ops *a_o;
 	struct tc_action a;
@@ -1048,7 +1048,7 @@
 	t->tca__pad1 = 0;
 	t->tca__pad2 = 0;
 
-	x = (struct rtattr *) skb->tail;
+	x = (struct rtattr *)skb_tail_pointer(skb);
 	RTA_PUT(skb, TCA_ACT_TAB, 0, NULL);
 
 	ret = a_o->walk(skb, cb, RTM_GETACTION, &a);
@@ -1056,12 +1056,12 @@
 		goto rtattr_failure;
 
 	if (ret > 0) {
-		x->rta_len = skb->tail - (u8 *) x;
+		x->rta_len = skb_tail_pointer(skb) - (u8 *)x;
 		ret = skb->len;
 	} else
-		skb_trim(skb, (u8*)x - skb->data);
+		nlmsg_trim(skb, x);
 
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 	if (NETLINK_CB(cb->skb).pid && ret)
 		nlh->nlmsg_flags |= NLM_F_MULTI;
 	module_put(a_o->owner);
@@ -1070,20 +1070,15 @@
 rtattr_failure:
 nlmsg_failure:
 	module_put(a_o->owner);
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return skb->len;
 }
 
 static int __init tc_action_init(void)
 {
-	struct rtnetlink_link *link_p = rtnetlink_links[PF_UNSPEC];
-
-	if (link_p) {
-		link_p[RTM_NEWACTION-RTM_BASE].doit = tc_ctl_action;
-		link_p[RTM_DELACTION-RTM_BASE].doit = tc_ctl_action;
-		link_p[RTM_GETACTION-RTM_BASE].doit = tc_ctl_action;
-		link_p[RTM_GETACTION-RTM_BASE].dumpit = tc_dump_action;
-	}
+	rtnl_register(PF_UNSPEC, RTM_NEWACTION, tc_ctl_action, NULL);
+	rtnl_register(PF_UNSPEC, RTM_DELACTION, tc_ctl_action, NULL);
+	rtnl_register(PF_UNSPEC, RTM_GETACTION, tc_ctl_action, tc_dump_action);
 
 	return 0;
 }
diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c
index 87d0faf..7517f37 100644
--- a/net/sched/act_gact.c
+++ b/net/sched/act_gact.c
@@ -28,6 +28,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/proc_fs.h>
+#include <net/netlink.h>
 #include <net/sock.h>
 #include <net/pkt_sched.h>
 #include <linux/tc_act/tc_gact.h>
@@ -155,7 +156,7 @@
 
 static int tcf_gact_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
 {
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct tc_gact opt;
 	struct tcf_gact *gact = a->priv;
 	struct tcf_t t;
@@ -181,7 +182,7 @@
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c
index 47f0b13..00b05f4 100644
--- a/net/sched/act_ipt.c
+++ b/net/sched/act_ipt.c
@@ -30,6 +30,7 @@
 #include <linux/init.h>
 #include <linux/proc_fs.h>
 #include <linux/kmod.h>
+#include <net/netlink.h>
 #include <net/sock.h>
 #include <net/pkt_sched.h>
 #include <linux/tc_act/tc_ipt.h>
@@ -245,7 +246,7 @@
 
 static int tcf_ipt_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
 {
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct tcf_ipt *ipt = a->priv;
 	struct ipt_entry_target *t;
 	struct tcf_t tm;
@@ -277,7 +278,7 @@
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	kfree(t);
 	return -1;
 }
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
index 3e93683..de21c92 100644
--- a/net/sched/act_mirred.c
+++ b/net/sched/act_mirred.c
@@ -30,6 +30,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/proc_fs.h>
+#include <net/netlink.h>
 #include <net/sock.h>
 #include <net/pkt_sched.h>
 #include <linux/tc_act/tc_mirred.h>
@@ -206,7 +207,7 @@
 
 static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
 {
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct tcf_mirred *m = a->priv;
 	struct tc_mirred opt;
 	struct tcf_t t;
@@ -225,7 +226,7 @@
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
index 3d6a2fc..45b3cda 100644
--- a/net/sched/act_pedit.c
+++ b/net/sched/act_pedit.c
@@ -27,6 +27,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/proc_fs.h>
+#include <net/netlink.h>
 #include <net/sock.h>
 #include <net/pkt_sched.h>
 #include <linux/tc_act/tc_pedit.h>
@@ -136,7 +137,7 @@
 		}
 	}
 
-	pptr = skb->nh.raw;
+	pptr = skb_network_header(skb);
 
 	spin_lock(&p->tcf_lock);
 
@@ -195,7 +196,7 @@
 static int tcf_pedit_dump(struct sk_buff *skb, struct tc_action *a,
 			  int bind, int ref)
 {
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct tcf_pedit *p = a->priv;
 	struct tc_pedit *opt;
 	struct tcf_t t;
@@ -226,7 +227,7 @@
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	kfree(opt);
 	return -1;
 }
diff --git a/net/sched/act_police.c b/net/sched/act_police.c
index 10a5a5c..616f465 100644
--- a/net/sched/act_police.c
+++ b/net/sched/act_police.c
@@ -30,6 +30,7 @@
 #include <linux/init.h>
 #include <net/sock.h>
 #include <net/act_api.h>
+#include <net/netlink.h>
 
 #define L2T(p,L)   ((p)->tcfp_R_tab->data[(L)>>(p)->tcfp_R_tab->rate.cell_log])
 #define L2T_P(p,L) ((p)->tcfp_P_tab->data[(L)>>(p)->tcfp_P_tab->rate.cell_log])
@@ -80,7 +81,7 @@
 				continue;
 			a->priv = p;
 			a->order = index;
-			r = (struct rtattr*) skb->tail;
+			r = (struct rtattr *)skb_tail_pointer(skb);
 			RTA_PUT(skb, a->order, 0, NULL);
 			if (type == RTM_DELACTION)
 				err = tcf_action_dump_1(skb, a, 0, 1);
@@ -88,10 +89,10 @@
 				err = tcf_action_dump_1(skb, a, 0, 0);
 			if (err < 0) {
 				index--;
-				skb_trim(skb, (u8*)r - skb->data);
+				nlmsg_trim(skb, r);
 				goto done;
 			}
-			r->rta_len = skb->tail - (u8*)r;
+			r->rta_len = skb_tail_pointer(skb) - (u8 *)r;
 			n_i++;
 		}
 	}
@@ -102,7 +103,7 @@
 	return n_i;
 
 rtattr_failure:
-	skb_trim(skb, (u8*)r - skb->data);
+	nlmsg_trim(skb, r);
 	goto done;
 }
 #endif
@@ -240,7 +241,7 @@
 	if (ret != ACT_P_CREATED)
 		return ret;
 
-	PSCHED_GET_TIME(police->tcfp_t_c);
+	police->tcfp_t_c = psched_get_time();
 	police->tcf_index = parm->index ? parm->index :
 		tcf_hash_new_index(&police_idx_gen, &police_hash_info);
 	h = tcf_hash(police->tcf_index, POL_TAB_MASK);
@@ -295,10 +296,9 @@
 			return police->tcfp_result;
 		}
 
-		PSCHED_GET_TIME(now);
-
-		toks = PSCHED_TDIFF_SAFE(now, police->tcfp_t_c,
-					 police->tcfp_burst);
+		now = psched_get_time();
+		toks = psched_tdiff_bounded(now, police->tcfp_t_c,
+					    police->tcfp_burst);
 		if (police->tcfp_P_tab) {
 			ptoks = toks + police->tcfp_ptoks;
 			if (ptoks > (long)L2T_P(police, police->tcfp_mtu))
@@ -326,7 +326,7 @@
 static int
 tcf_act_police_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
 {
-	unsigned char	 *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct tcf_police *police = a->priv;
 	struct tc_police opt;
 
@@ -355,7 +355,7 @@
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -494,7 +494,7 @@
 	}
 	if (police->tcfp_P_tab)
 		police->tcfp_ptoks = L2T_P(police, police->tcfp_mtu);
-	PSCHED_GET_TIME(police->tcfp_t_c);
+	police->tcfp_t_c = psched_get_time();
 	police->tcf_index = parm->index ? parm->index :
 		tcf_police_new_index();
 	police->tcf_action = parm->action;
@@ -542,9 +542,9 @@
 			return police->tcfp_result;
 		}
 
-		PSCHED_GET_TIME(now);
-		toks = PSCHED_TDIFF_SAFE(now, police->tcfp_t_c,
-					 police->tcfp_burst);
+		now = psched_get_time();
+		toks = psched_tdiff_bounded(now, police->tcfp_t_c,
+					    police->tcfp_burst);
 		if (police->tcfp_P_tab) {
 			ptoks = toks + police->tcfp_ptoks;
 			if (ptoks > (long)L2T_P(police, police->tcfp_mtu))
@@ -572,7 +572,7 @@
 
 int tcf_police_dump(struct sk_buff *skb, struct tcf_police *police)
 {
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct tc_police opt;
 
 	opt.index = police->tcf_index;
@@ -598,7 +598,7 @@
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c
index c797118..36e1eda 100644
--- a/net/sched/act_simple.c
+++ b/net/sched/act_simple.c
@@ -16,6 +16,7 @@
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
 #include <linux/rtnetlink.h>
+#include <net/netlink.h>
 #include <net/pkt_sched.h>
 
 #define TCA_ACT_SIMP 22
@@ -155,7 +156,7 @@
 static inline int tcf_simp_dump(struct sk_buff *skb, struct tc_action *a,
 				int bind, int ref)
 {
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct tcf_defact *d = a->priv;
 	struct tc_defact opt;
 	struct tcf_t t;
@@ -173,7 +174,7 @@
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index 5c6ffdb..ebf94ed 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -29,9 +29,10 @@
 #include <linux/interrupt.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
-#include <linux/rtnetlink.h>
 #include <linux/init.h>
 #include <linux/kmod.h>
+#include <linux/netlink.h>
+#include <net/netlink.h>
 #include <net/sock.h>
 #include <net/pkt_sched.h>
 #include <net/pkt_cls.h>
@@ -323,7 +324,7 @@
 {
 	struct tcmsg *tcm;
 	struct nlmsghdr  *nlh;
-	unsigned char	 *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 
 	nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*tcm), flags);
 	tcm = NLMSG_DATA(nlh);
@@ -340,12 +341,12 @@
 		if (tp->ops->dump && tp->ops->dump(tp, fh, skb, tcm) < 0)
 			goto rtattr_failure;
 	}
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
 nlmsg_failure:
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -399,7 +400,6 @@
 	if ((dev = dev_get_by_index(tcm->tcm_ifindex)) == NULL)
 		return skb->len;
 
-	read_lock(&qdisc_tree_lock);
 	if (!tcm->tcm_parent)
 		q = dev->qdisc_sleeping;
 	else
@@ -456,7 +456,6 @@
 	if (cl)
 		cops->put(q, cl);
 out:
-	read_unlock(&qdisc_tree_lock);
 	dev_put(dev);
 	return skb->len;
 }
@@ -563,30 +562,30 @@
 		 * to work with both old and new modes of entering
 		 * tc data even if iproute2  was newer - jhs
 		 */
-		struct rtattr * p_rta = (struct rtattr*) skb->tail;
+		struct rtattr *p_rta = (struct rtattr *)skb_tail_pointer(skb);
 
 		if (exts->action->type != TCA_OLD_COMPAT) {
 			RTA_PUT(skb, map->action, 0, NULL);
 			if (tcf_action_dump(skb, exts->action, 0, 0) < 0)
 				goto rtattr_failure;
-			p_rta->rta_len = skb->tail - (u8*)p_rta;
+			p_rta->rta_len = skb_tail_pointer(skb) - (u8 *)p_rta;
 		} else if (map->police) {
 			RTA_PUT(skb, map->police, 0, NULL);
 			if (tcf_action_dump_old(skb, exts->action, 0, 0) < 0)
 				goto rtattr_failure;
-			p_rta->rta_len = skb->tail - (u8*)p_rta;
+			p_rta->rta_len = skb_tail_pointer(skb) - (u8 *)p_rta;
 		}
 	}
 #elif defined CONFIG_NET_CLS_POLICE
 	if (map->police && exts->police) {
-		struct rtattr * p_rta = (struct rtattr*) skb->tail;
+		struct rtattr *p_rta = (struct rtattr *)skb_tail_pointer(skb);
 
 		RTA_PUT(skb, map->police, 0, NULL);
 
 		if (tcf_police_dump(skb, exts->police) < 0)
 			goto rtattr_failure;
 
-		p_rta->rta_len = skb->tail - (u8*)p_rta;
+		p_rta->rta_len = skb_tail_pointer(skb) - (u8 *)p_rta;
 	}
 #endif
 	return 0;
@@ -614,18 +613,11 @@
 
 static int __init tc_filter_init(void)
 {
-	struct rtnetlink_link *link_p = rtnetlink_links[PF_UNSPEC];
+	rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_ctl_tfilter, NULL);
+	rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_ctl_tfilter, NULL);
+	rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_ctl_tfilter,
+						 tc_dump_tfilter);
 
-	/* Setup rtnetlink links. It is made here to avoid
-	   exporting large number of public symbols.
-	 */
-
-	if (link_p) {
-		link_p[RTM_NEWTFILTER-RTM_BASE].doit = tc_ctl_tfilter;
-		link_p[RTM_DELTFILTER-RTM_BASE].doit = tc_ctl_tfilter;
-		link_p[RTM_GETTFILTER-RTM_BASE].doit = tc_ctl_tfilter;
-		link_p[RTM_GETTFILTER-RTM_BASE].dumpit = tc_dump_tfilter;
-	}
 	return 0;
 }
 
diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c
index 4a91f08..c885412 100644
--- a/net/sched/cls_basic.c
+++ b/net/sched/cls_basic.c
@@ -17,6 +17,7 @@
 #include <linux/errno.h>
 #include <linux/rtnetlink.h>
 #include <linux/skbuff.h>
+#include <net/netlink.h>
 #include <net/act_api.h>
 #include <net/pkt_cls.h>
 
@@ -245,7 +246,7 @@
 		      struct sk_buff *skb, struct tcmsg *t)
 {
 	struct basic_filter *f = (struct basic_filter *) fh;
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct rtattr *rta;
 
 	if (f == NULL)
@@ -263,11 +264,11 @@
 	    tcf_em_tree_dump(skb, &f->ematches, TCA_BASIC_EMATCHES) < 0)
 		goto rtattr_failure;
 
-	rta->rta_len = (skb->tail - b);
+	rta->rta_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c
index 5dbb9d4..bbec4a0 100644
--- a/net/sched/cls_fw.c
+++ b/net/sched/cls_fw.c
@@ -38,6 +38,7 @@
 #include <linux/notifier.h>
 #include <linux/netfilter.h>
 #include <net/ip.h>
+#include <net/netlink.h>
 #include <net/route.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
@@ -348,7 +349,7 @@
 {
 	struct fw_head *head = (struct fw_head *)tp->root;
 	struct fw_filter *f = (struct fw_filter*)fh;
-	unsigned char	 *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct rtattr *rta;
 
 	if (f == NULL)
@@ -374,7 +375,7 @@
 	if (tcf_exts_dump(skb, &f->exts, &fw_ext_map) < 0)
 		goto rtattr_failure;
 
-	rta->rta_len = skb->tail - b;
+	rta->rta_len = skb_tail_pointer(skb) - b;
 
 	if (tcf_exts_dump_stats(skb, &f->exts, &fw_ext_map) < 0)
 		goto rtattr_failure;
@@ -382,7 +383,7 @@
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c
index abc47cc..cc941d0 100644
--- a/net/sched/cls_route.c
+++ b/net/sched/cls_route.c
@@ -28,6 +28,7 @@
 #include <linux/etherdevice.h>
 #include <linux/notifier.h>
 #include <net/ip.h>
+#include <net/netlink.h>
 #include <net/route.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
@@ -88,9 +89,9 @@
 static inline
 void route4_reset_fastmap(struct net_device *dev, struct route4_head *head, u32 id)
 {
-	spin_lock_bh(&dev->queue_lock);
+	qdisc_lock_tree(dev);
 	memset(head->fastmap, 0, sizeof(head->fastmap));
-	spin_unlock_bh(&dev->queue_lock);
+	qdisc_unlock_tree(dev);
 }
 
 static inline void
@@ -562,7 +563,7 @@
 		       struct sk_buff *skb, struct tcmsg *t)
 {
 	struct route4_filter *f = (struct route4_filter*)fh;
-	unsigned char	 *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct rtattr *rta;
 	u32 id;
 
@@ -591,7 +592,7 @@
 	if (tcf_exts_dump(skb, &f->exts, &route_ext_map) < 0)
 		goto rtattr_failure;
 
-	rta->rta_len = skb->tail - b;
+	rta->rta_len = skb_tail_pointer(skb) - b;
 
 	if (tcf_exts_dump_stats(skb, &f->exts, &route_ext_map) < 0)
 		goto rtattr_failure;
@@ -599,7 +600,7 @@
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
diff --git a/net/sched/cls_rsvp.c b/net/sched/cls_rsvp.c
index 1d4a1fb..0a683c0 100644
--- a/net/sched/cls_rsvp.c
+++ b/net/sched/cls_rsvp.c
@@ -31,6 +31,7 @@
 #include <net/route.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
+#include <net/netlink.h>
 #include <net/act_api.h>
 #include <net/pkt_cls.h>
 
diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h
index 7853621..22f9ede 100644
--- a/net/sched/cls_rsvp.h
+++ b/net/sched/cls_rsvp.h
@@ -143,9 +143,9 @@
 	u8 tunnelid = 0;
 	u8 *xprt;
 #if RSVP_DST_LEN == 4
-	struct ipv6hdr *nhptr = skb->nh.ipv6h;
+	struct ipv6hdr *nhptr = ipv6_hdr(skb);
 #else
-	struct iphdr *nhptr = skb->nh.iph;
+	struct iphdr *nhptr = ip_hdr(skb);
 #endif
 
 restart:
@@ -160,7 +160,7 @@
 	dst = &nhptr->daddr;
 	protocol = nhptr->protocol;
 	xprt = ((u8*)nhptr) + (nhptr->ihl<<2);
-	if (nhptr->frag_off&__constant_htons(IP_MF|IP_OFFSET))
+	if (nhptr->frag_off & htons(IP_MF|IP_OFFSET))
 		return -1;
 #endif
 
@@ -593,7 +593,7 @@
 {
 	struct rsvp_filter *f = (struct rsvp_filter*)fh;
 	struct rsvp_session *s;
-	unsigned char	 *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct rtattr *rta;
 	struct tc_rsvp_pinfo pinfo;
 
@@ -623,14 +623,14 @@
 	if (tcf_exts_dump(skb, &f->exts, &rsvp_ext_map) < 0)
 		goto rtattr_failure;
 
-	rta->rta_len = skb->tail - b;
+	rta->rta_len = skb_tail_pointer(skb) - b;
 
 	if (tcf_exts_dump_stats(skb, &f->exts, &rsvp_ext_map) < 0)
 		goto rtattr_failure;
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
diff --git a/net/sched/cls_rsvp6.c b/net/sched/cls_rsvp6.c
index a2979d8..93b6abe 100644
--- a/net/sched/cls_rsvp6.c
+++ b/net/sched/cls_rsvp6.c
@@ -34,6 +34,7 @@
 #include <net/sock.h>
 #include <net/act_api.h>
 #include <net/pkt_cls.h>
+#include <net/netlink.h>
 
 #define RSVP_DST_LEN	4
 #define RSVP_ID		"rsvp6"
diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c
index 7563fdc..47ac0c5 100644
--- a/net/sched/cls_tcindex.c
+++ b/net/sched/cls_tcindex.c
@@ -12,6 +12,7 @@
 #include <linux/netdevice.h>
 #include <net/ip.h>
 #include <net/act_api.h>
+#include <net/netlink.h>
 #include <net/pkt_cls.h>
 #include <net/route.h>
 
@@ -448,7 +449,7 @@
 {
 	struct tcindex_data *p = PRIV(tp);
 	struct tcindex_filter_result *r = (struct tcindex_filter_result *) fh;
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct rtattr *rta;
 
 	DPRINTK("tcindex_dump(tp %p,fh 0x%lx,skb %p,t %p),p %p,r %p,b %p\n",
@@ -463,7 +464,7 @@
 		RTA_PUT(skb,TCA_TCINDEX_SHIFT,sizeof(p->shift),&p->shift);
 		RTA_PUT(skb,TCA_TCINDEX_FALL_THROUGH,sizeof(p->fall_through),
 		    &p->fall_through);
-		rta->rta_len = skb->tail-b;
+		rta->rta_len = skb_tail_pointer(skb) - b;
 	} else {
 		if (p->perfect) {
 			t->tcm_handle = r-p->perfect;
@@ -486,7 +487,7 @@
 
 		if (tcf_exts_dump(skb, &r->exts, &tcindex_ext_map) < 0)
 			goto rtattr_failure;
-		rta->rta_len = skb->tail-b;
+		rta->rta_len = skb_tail_pointer(skb) - b;
 
 		if (tcf_exts_dump_stats(skb, &r->exts, &tcindex_ext_map) < 0)
 			goto rtattr_failure;
@@ -495,7 +496,7 @@
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c
index 0bcb169..c7a347b 100644
--- a/net/sched/cls_u32.c
+++ b/net/sched/cls_u32.c
@@ -50,6 +50,7 @@
 #include <linux/notifier.h>
 #include <linux/rtnetlink.h>
 #include <net/ip.h>
+#include <net/netlink.h>
 #include <net/route.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
@@ -119,7 +120,7 @@
 	} stack[TC_U32_MAXDEPTH];
 
 	struct tc_u_hnode *ht = (struct tc_u_hnode*)tp->root;
-	u8 *ptr = skb->nh.raw;
+	u8 *ptr = skb_network_header(skb);
 	struct tc_u_knode *n;
 	int sdepth = 0;
 	int off2 = 0;
@@ -213,7 +214,7 @@
 			off2 = 0;
 		}
 
-		if (ptr < skb->tail)
+		if (ptr < skb_tail_pointer(skb))
 			goto next_ht;
 	}
 
@@ -435,7 +436,7 @@
 			BUG_TRAP(ht->refcnt == 0);
 
 			kfree(ht);
-		};
+		}
 
 		kfree(tp_c);
 	}
@@ -718,7 +719,7 @@
 		     struct sk_buff *skb, struct tcmsg *t)
 {
 	struct tc_u_knode *n = (struct tc_u_knode*)fh;
-	unsigned char	 *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct rtattr *rta;
 
 	if (n == NULL)
@@ -765,14 +766,14 @@
 #endif
 	}
 
-	rta->rta_len = skb->tail - b;
+	rta->rta_len = skb_tail_pointer(skb) - b;
 	if (TC_U32_KEY(n->handle))
 		if (tcf_exts_dump_stats(skb, &n->exts, &u32_ext_map) < 0)
 			goto rtattr_failure;
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
diff --git a/net/sched/em_u32.c b/net/sched/em_u32.c
index cd0600c..0a2a7fe 100644
--- a/net/sched/em_u32.c
+++ b/net/sched/em_u32.c
@@ -22,7 +22,7 @@
 			struct tcf_pkt_info *info)
 {
 	struct tc_u32_key *key = (struct tc_u32_key *) em->data;
-	unsigned char *ptr = skb->nh.raw;
+	const unsigned char *ptr = skb_network_header(skb);
 
 	if (info) {
 		if (info->ptr)
diff --git a/net/sched/ematch.c b/net/sched/ematch.c
index 959c306..63146d3 100644
--- a/net/sched/ematch.c
+++ b/net/sched/ematch.c
@@ -418,17 +418,19 @@
 int tcf_em_tree_dump(struct sk_buff *skb, struct tcf_ematch_tree *tree, int tlv)
 {
 	int i;
-	struct rtattr * top_start = (struct rtattr*) skb->tail;
-	struct rtattr * list_start;
+	u8 *tail;
+	struct rtattr *top_start = (struct rtattr *)skb_tail_pointer(skb);
+	struct rtattr *list_start;
 
 	RTA_PUT(skb, tlv, 0, NULL);
 	RTA_PUT(skb, TCA_EMATCH_TREE_HDR, sizeof(tree->hdr), &tree->hdr);
 
-	list_start = (struct rtattr *) skb->tail;
+	list_start = (struct rtattr *)skb_tail_pointer(skb);
 	RTA_PUT(skb, TCA_EMATCH_TREE_LIST, 0, NULL);
 
+	tail = skb_tail_pointer(skb);
 	for (i = 0; i < tree->hdr.nmatches; i++) {
-		struct rtattr *match_start = (struct rtattr*) skb->tail;
+		struct rtattr *match_start = (struct rtattr *)tail;
 		struct tcf_ematch *em = tcf_em_get_match(tree, i);
 		struct tcf_ematch_hdr em_hdr = {
 			.kind = em->ops ? em->ops->kind : TCF_EM_CONTAINER,
@@ -447,11 +449,12 @@
 		} else if (em->datalen > 0)
 			RTA_PUT_NOHDR(skb, em->datalen, (void *) em->data);
 
-		match_start->rta_len = skb->tail - (u8*) match_start;
+		tail = skb_tail_pointer(skb);
+		match_start->rta_len = tail - (u8 *)match_start;
 	}
 
-	list_start->rta_len = skb->tail - (u8 *) list_start;
-	top_start->rta_len = skb->tail - (u8 *) top_start;
+	list_start->rta_len = tail - (u8 *)list_start;
+	top_start->rta_len = tail - (u8 *)top_start;
 
 	return 0;
 
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index ecc988a..8699e70 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -27,14 +27,15 @@
 #include <linux/interrupt.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
-#include <linux/rtnetlink.h>
 #include <linux/init.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/kmod.h>
 #include <linux/list.h>
 #include <linux/bitops.h>
+#include <linux/hrtimer.h>
 
+#include <net/netlink.h>
 #include <net/sock.h>
 #include <net/pkt_sched.h>
 
@@ -190,7 +191,7 @@
    (root qdisc, all its children, children of children etc.)
  */
 
-static struct Qdisc *__qdisc_lookup(struct net_device *dev, u32 handle)
+struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle)
 {
 	struct Qdisc *q;
 
@@ -201,16 +202,6 @@
 	return NULL;
 }
 
-struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle)
-{
-	struct Qdisc *q;
-
-	read_lock(&qdisc_tree_lock);
-	q = __qdisc_lookup(dev, handle);
-	read_unlock(&qdisc_tree_lock);
-	return q;
-}
-
 static struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid)
 {
 	unsigned long cl;
@@ -291,6 +282,48 @@
 	}
 }
 
+static enum hrtimer_restart qdisc_watchdog(struct hrtimer *timer)
+{
+	struct qdisc_watchdog *wd = container_of(timer, struct qdisc_watchdog,
+						 timer);
+	struct net_device *dev = wd->qdisc->dev;
+
+	wd->qdisc->flags &= ~TCQ_F_THROTTLED;
+	smp_wmb();
+	if (spin_trylock(&dev->queue_lock)) {
+		qdisc_run(dev);
+		spin_unlock(&dev->queue_lock);
+	} else
+		netif_schedule(dev);
+
+	return HRTIMER_NORESTART;
+}
+
+void qdisc_watchdog_init(struct qdisc_watchdog *wd, struct Qdisc *qdisc)
+{
+	hrtimer_init(&wd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+	wd->timer.function = qdisc_watchdog;
+	wd->qdisc = qdisc;
+}
+EXPORT_SYMBOL(qdisc_watchdog_init);
+
+void qdisc_watchdog_schedule(struct qdisc_watchdog *wd, psched_time_t expires)
+{
+	ktime_t time;
+
+	wd->qdisc->flags |= TCQ_F_THROTTLED;
+	time = ktime_set(0, 0);
+	time = ktime_add_ns(time, PSCHED_US2NS(expires));
+	hrtimer_start(&wd->timer, time, HRTIMER_MODE_ABS);
+}
+EXPORT_SYMBOL(qdisc_watchdog_schedule);
+
+void qdisc_watchdog_cancel(struct qdisc_watchdog *wd)
+{
+	hrtimer_cancel(&wd->timer);
+	wd->qdisc->flags &= ~TCQ_F_THROTTLED;
+}
+EXPORT_SYMBOL(qdisc_watchdog_cancel);
 
 /* Allocate an unique handle from space managed by kernel */
 
@@ -362,7 +395,7 @@
 	if (n == 0)
 		return;
 	while ((parentid = sch->parent)) {
-		sch = __qdisc_lookup(sch->dev, TC_H_MAJ(parentid));
+		sch = qdisc_lookup(sch->dev, TC_H_MAJ(parentid));
 		cops = sch->ops->cl_ops;
 		if (cops->qlen_notify) {
 			cl = cops->get(sch, parentid);
@@ -467,12 +500,16 @@
 
 	if (handle == TC_H_INGRESS) {
 		sch->flags |= TCQ_F_INGRESS;
+		sch->stats_lock = &dev->ingress_lock;
 		handle = TC_H_MAKE(TC_H_INGRESS, 0);
-	} else if (handle == 0) {
-		handle = qdisc_alloc_handle(dev);
-		err = -ENOMEM;
-		if (handle == 0)
-			goto err_out3;
+	} else {
+		sch->stats_lock = &dev->queue_lock;
+		if (handle == 0) {
+			handle = qdisc_alloc_handle(dev);
+			err = -ENOMEM;
+			if (handle == 0)
+				goto err_out3;
+		}
 	}
 
 	sch->handle = handle;
@@ -621,9 +658,9 @@
 			return err;
 		if (q) {
 			qdisc_notify(skb, n, clid, q, NULL);
-			spin_lock_bh(&dev->queue_lock);
+			qdisc_lock_tree(dev);
 			qdisc_destroy(q);
-			spin_unlock_bh(&dev->queue_lock);
+			qdisc_unlock_tree(dev);
 		}
 	} else {
 		qdisc_notify(skb, n, clid, NULL, q);
@@ -756,17 +793,17 @@
 		err = qdisc_graft(dev, p, clid, q, &old_q);
 		if (err) {
 			if (q) {
-				spin_lock_bh(&dev->queue_lock);
+				qdisc_lock_tree(dev);
 				qdisc_destroy(q);
-				spin_unlock_bh(&dev->queue_lock);
+				qdisc_unlock_tree(dev);
 			}
 			return err;
 		}
 		qdisc_notify(skb, n, clid, old_q, q);
 		if (old_q) {
-			spin_lock_bh(&dev->queue_lock);
+			qdisc_lock_tree(dev);
 			qdisc_destroy(old_q);
-			spin_unlock_bh(&dev->queue_lock);
+			qdisc_unlock_tree(dev);
 		}
 	}
 	return 0;
@@ -777,7 +814,7 @@
 {
 	struct tcmsg *tcm;
 	struct nlmsghdr  *nlh;
-	unsigned char	 *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct gnet_dump d;
 
 	nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*tcm), flags);
@@ -811,12 +848,12 @@
 	if (gnet_stats_finish_copy(&d) < 0)
 		goto rtattr_failure;
 
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
 nlmsg_failure:
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -862,7 +899,6 @@
 			continue;
 		if (idx > s_idx)
 			s_q_idx = 0;
-		read_lock(&qdisc_tree_lock);
 		q_idx = 0;
 		list_for_each_entry(q, &dev->qdisc_list, list) {
 			if (q_idx < s_q_idx) {
@@ -870,13 +906,10 @@
 				continue;
 			}
 			if (tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).pid,
-					  cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0) {
-				read_unlock(&qdisc_tree_lock);
+					  cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0)
 				goto done;
-			}
 			q_idx++;
 		}
-		read_unlock(&qdisc_tree_lock);
 	}
 
 done:
@@ -1015,7 +1048,7 @@
 {
 	struct tcmsg *tcm;
 	struct nlmsghdr  *nlh;
-	unsigned char	 *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct gnet_dump d;
 	struct Qdisc_class_ops *cl_ops = q->ops->cl_ops;
 
@@ -1040,12 +1073,12 @@
 	if (gnet_stats_finish_copy(&d) < 0)
 		goto rtattr_failure;
 
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
 nlmsg_failure:
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -1099,7 +1132,6 @@
 	s_t = cb->args[0];
 	t = 0;
 
-	read_lock(&qdisc_tree_lock);
 	list_for_each_entry(q, &dev->qdisc_list, list) {
 		if (t < s_t || !q->ops->cl_ops ||
 		    (tcm->tcm_parent &&
@@ -1121,7 +1153,6 @@
 			break;
 		t++;
 	}
-	read_unlock(&qdisc_tree_lock);
 
 	cb->args[0] = t;
 
@@ -1146,7 +1177,7 @@
 
 	for ( ; tp; tp = tp->next) {
 		if ((tp->protocol == protocol ||
-			tp->protocol == __constant_htons(ETH_P_ALL)) &&
+			tp->protocol == htons(ETH_P_ALL)) &&
 			(err = tp->classify(skb, tp, res)) >= 0) {
 #ifdef CONFIG_NET_CLS_ACT
 			if ( TC_ACT_RECLASSIFY == err) {
@@ -1175,15 +1206,31 @@
 	return -1;
 }
 
-static int psched_us_per_tick = 1;
-static int psched_tick_per_us = 1;
+void tcf_destroy(struct tcf_proto *tp)
+{
+	tp->ops->destroy(tp);
+	module_put(tp->ops->owner);
+	kfree(tp);
+}
+
+void tcf_destroy_chain(struct tcf_proto *fl)
+{
+	struct tcf_proto *tp;
+
+	while ((tp = fl) != NULL) {
+		fl = tp->next;
+		tcf_destroy(tp);
+	}
+}
+EXPORT_SYMBOL(tcf_destroy_chain);
 
 #ifdef CONFIG_PROC_FS
 static int psched_show(struct seq_file *seq, void *v)
 {
 	seq_printf(seq, "%08x %08x %08x %08x\n",
-		      psched_tick_per_us, psched_us_per_tick,
-		      1000000, HZ);
+		   (u32)NSEC_PER_USEC, (u32)PSCHED_US2NS(1),
+		   1000000,
+		   (u32)NSEC_PER_SEC/(u32)ktime_to_ns(KTIME_MONOTONIC_RES));
 
 	return 0;
 }
@@ -1202,101 +1249,19 @@
 };
 #endif
 
-#ifdef CONFIG_NET_SCH_CLK_CPU
-psched_tdiff_t psched_clock_per_hz;
-int psched_clock_scale;
-EXPORT_SYMBOL(psched_clock_per_hz);
-EXPORT_SYMBOL(psched_clock_scale);
-
-psched_time_t psched_time_base;
-cycles_t psched_time_mark;
-EXPORT_SYMBOL(psched_time_mark);
-EXPORT_SYMBOL(psched_time_base);
-
-/*
- * Periodically adjust psched_time_base to avoid overflow
- * with 32-bit get_cycles(). Safe up to 4GHz CPU.
- */
-static void psched_tick(unsigned long);
-static DEFINE_TIMER(psched_timer, psched_tick, 0, 0);
-
-static void psched_tick(unsigned long dummy)
-{
-	if (sizeof(cycles_t) == sizeof(u32)) {
-		psched_time_t dummy_stamp;
-		PSCHED_GET_TIME(dummy_stamp);
-		psched_timer.expires = jiffies + 1*HZ;
-		add_timer(&psched_timer);
-	}
-}
-
-int __init psched_calibrate_clock(void)
-{
-	psched_time_t stamp, stamp1;
-	struct timeval tv, tv1;
-	psched_tdiff_t delay;
-	long rdelay;
-	unsigned long stop;
-
-	psched_tick(0);
-	stop = jiffies + HZ/10;
-	PSCHED_GET_TIME(stamp);
-	do_gettimeofday(&tv);
-	while (time_before(jiffies, stop)) {
-		barrier();
-		cpu_relax();
-	}
-	PSCHED_GET_TIME(stamp1);
-	do_gettimeofday(&tv1);
-
-	delay = PSCHED_TDIFF(stamp1, stamp);
-	rdelay = tv1.tv_usec - tv.tv_usec;
-	rdelay += (tv1.tv_sec - tv.tv_sec)*1000000;
-	if (rdelay > delay)
-		return -1;
-	delay /= rdelay;
-	psched_tick_per_us = delay;
-	while ((delay>>=1) != 0)
-		psched_clock_scale++;
-	psched_us_per_tick = 1<<psched_clock_scale;
-	psched_clock_per_hz = (psched_tick_per_us*(1000000/HZ))>>psched_clock_scale;
-	return 0;
-}
-#endif
-
 static int __init pktsched_init(void)
 {
-	struct rtnetlink_link *link_p;
-
-#ifdef CONFIG_NET_SCH_CLK_CPU
-	if (psched_calibrate_clock() < 0)
-		return -1;
-#elif defined(CONFIG_NET_SCH_CLK_JIFFIES)
-	psched_tick_per_us = HZ<<PSCHED_JSCALE;
-	psched_us_per_tick = 1000000;
-#endif
-
-	link_p = rtnetlink_links[PF_UNSPEC];
-
-	/* Setup rtnetlink links. It is made here to avoid
-	   exporting large number of public symbols.
-	 */
-
-	if (link_p) {
-		link_p[RTM_NEWQDISC-RTM_BASE].doit = tc_modify_qdisc;
-		link_p[RTM_DELQDISC-RTM_BASE].doit = tc_get_qdisc;
-		link_p[RTM_GETQDISC-RTM_BASE].doit = tc_get_qdisc;
-		link_p[RTM_GETQDISC-RTM_BASE].dumpit = tc_dump_qdisc;
-		link_p[RTM_NEWTCLASS-RTM_BASE].doit = tc_ctl_tclass;
-		link_p[RTM_DELTCLASS-RTM_BASE].doit = tc_ctl_tclass;
-		link_p[RTM_GETTCLASS-RTM_BASE].doit = tc_ctl_tclass;
-		link_p[RTM_GETTCLASS-RTM_BASE].dumpit = tc_dump_tclass;
-	}
-
 	register_qdisc(&pfifo_qdisc_ops);
 	register_qdisc(&bfifo_qdisc_ops);
 	proc_net_fops_create("psched", 0, &psched_fops);
 
+	rtnl_register(PF_UNSPEC, RTM_NEWQDISC, tc_modify_qdisc, NULL);
+	rtnl_register(PF_UNSPEC, RTM_DELQDISC, tc_get_qdisc, NULL);
+	rtnl_register(PF_UNSPEC, RTM_GETQDISC, tc_get_qdisc, tc_dump_qdisc);
+	rtnl_register(PF_UNSPEC, RTM_NEWTCLASS, tc_ctl_tclass, NULL);
+	rtnl_register(PF_UNSPEC, RTM_DELTCLASS, tc_ctl_tclass, NULL);
+	rtnl_register(PF_UNSPEC, RTM_GETTCLASS, tc_ctl_tclass, tc_dump_tclass);
+
 	return 0;
 }
 
diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c
index afb3bbd..be7d299 100644
--- a/net/sched/sch_atm.c
+++ b/net/sched/sch_atm.c
@@ -14,6 +14,7 @@
 #include <linux/netdevice.h>
 #include <linux/rtnetlink.h>
 #include <linux/file.h> /* for fput */
+#include <net/netlink.h>
 #include <net/pkt_sched.h>
 #include <net/sock.h>
 
@@ -157,19 +158,6 @@
 	return atm_tc_get(sch,classid);
 }
 
-
-static void destroy_filters(struct atm_flow_data *flow)
-{
-	struct tcf_proto *filter;
-
-	while ((filter = flow->filter_list)) {
-		DPRINTK("destroy_filters: destroying filter %p\n",filter);
-		flow->filter_list = filter->next;
-		tcf_destroy(filter);
-	}
-}
-
-
 /*
  * atm_tc_put handles all destructions, including the ones that are explicitly
  * requested (atm_tc_destroy, etc.). The assumption here is that we never drop
@@ -194,7 +182,7 @@
 	*prev = flow->next;
 	DPRINTK("atm_tc_put: qdisc %p\n",flow->q);
 	qdisc_destroy(flow->q);
-	destroy_filters(flow);
+	tcf_destroy_chain(flow->filter_list);
 	if (flow->sock) {
 		DPRINTK("atm_tc_put: f_count %d\n",
 		    file_count(flow->sock->file));
@@ -503,7 +491,7 @@
 			}
 			D2PRINTK("atm_tc_dequeue: sending on class %p\n",flow);
 			/* remove any LL header somebody else has attached */
-			skb_pull(skb,(char *) skb->nh.iph-(char *) skb->data);
+			skb_pull(skb, skb_network_offset(skb));
 			if (skb_headroom(skb) < flow->hdr_len) {
 				struct sk_buff *new;
 
@@ -513,7 +501,7 @@
 				skb = new;
 			}
 			D2PRINTK("sch_atm_dequeue: ip %p, data %p\n",
-			    skb->nh.iph,skb->data);
+				 skb_network_header(skb), skb->data);
 			ATM_SKB(skb)->vcc = flow->vcc;
 			memcpy(skb_push(skb,flow->hdr_len),flow->hdr,
 			    flow->hdr_len);
@@ -610,7 +598,7 @@
 	DPRINTK("atm_tc_destroy(sch %p,[qdisc %p])\n",sch,p);
 	/* races ? */
 	while ((flow = p->flows)) {
-		destroy_filters(flow);
+		tcf_destroy_chain(flow->filter_list);
 		if (flow->ref > 1)
 			printk(KERN_ERR "atm_destroy: %p->ref = %d\n",flow,
 			    flow->ref);
@@ -631,7 +619,7 @@
 {
 	struct atm_qdisc_data *p = PRIV(sch);
 	struct atm_flow_data *flow = (struct atm_flow_data *) cl;
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct rtattr *rta;
 
 	DPRINTK("atm_tc_dump_class(sch %p,[qdisc %p],flow %p,skb %p,tcm %p)\n",
@@ -661,11 +649,11 @@
 
 		RTA_PUT(skb,TCA_ATM_EXCESS,sizeof(zero),&zero);
 	}
-	rta->rta_len = skb->tail-b;
+	rta->rta_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb,b-skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 static int
diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c
index 76c92e7..a294542 100644
--- a/net/sched/sch_cbq.c
+++ b/net/sched/sch_cbq.c
@@ -29,6 +29,7 @@
 #include <linux/etherdevice.h>
 #include <linux/notifier.h>
 #include <net/ip.h>
+#include <net/netlink.h>
 #include <net/route.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
@@ -112,7 +113,7 @@
 
 	/* Overlimit strategy parameters */
 	void			(*overlimit)(struct cbq_class *cl);
-	long			penalty;
+	psched_tdiff_t		penalty;
 
 	/* General scheduler (WRR) parameters */
 	long			allot;
@@ -143,7 +144,7 @@
 	psched_time_t		undertime;
 	long			avgidle;
 	long			deficit;	/* Saved deficit for WRR */
-	unsigned long		penalized;
+	psched_time_t		penalized;
 	struct gnet_stats_basic bstats;
 	struct gnet_stats_queue qstats;
 	struct gnet_stats_rate_est rate_est;
@@ -180,12 +181,12 @@
 	psched_time_t		now_rt;		/* Cached real time */
 	unsigned		pmask;
 
-	struct timer_list	delay_timer;
-	struct timer_list	wd_timer;	/* Watchdog timer,
+	struct hrtimer		delay_timer;
+	struct qdisc_watchdog	watchdog;	/* Watchdog timer,
 						   started when CBQ has
 						   backlog, but cannot
 						   transmit just now */
-	long			wd_expires;
+	psched_tdiff_t		wd_expires;
 	int			toplevel;
 	u32			hgenerator;
 };
@@ -384,12 +385,12 @@
 		psched_time_t now;
 		psched_tdiff_t incr;
 
-		PSCHED_GET_TIME(now);
-		incr = PSCHED_TDIFF(now, q->now_rt);
-		PSCHED_TADD2(q->now, incr, now);
+		now = psched_get_time();
+		incr = now - q->now_rt;
+		now = q->now + incr;
 
 		do {
-			if (PSCHED_TLESS(cl->undertime, now)) {
+			if (cl->undertime < now) {
 				q->toplevel = cl->level;
 				return;
 			}
@@ -473,7 +474,7 @@
 static void cbq_ovl_classic(struct cbq_class *cl)
 {
 	struct cbq_sched_data *q = qdisc_priv(cl->qdisc);
-	psched_tdiff_t delay = PSCHED_TDIFF(cl->undertime, q->now);
+	psched_tdiff_t delay = cl->undertime - q->now;
 
 	if (!cl->delayed) {
 		delay += cl->offtime;
@@ -491,7 +492,7 @@
 			cl->avgidle = cl->minidle;
 		if (delay <= 0)
 			delay = 1;
-		PSCHED_TADD2(q->now, delay, cl->undertime);
+		cl->undertime = q->now + delay;
 
 		cl->xstats.overactions++;
 		cl->delayed = 1;
@@ -508,7 +509,7 @@
 		psched_tdiff_t base_delay = q->wd_expires;
 
 		for (b = cl->borrow; b; b = b->borrow) {
-			delay = PSCHED_TDIFF(b->undertime, q->now);
+			delay = b->undertime - q->now;
 			if (delay < base_delay) {
 				if (delay <= 0)
 					delay = 1;
@@ -546,27 +547,32 @@
 static void cbq_ovl_delay(struct cbq_class *cl)
 {
 	struct cbq_sched_data *q = qdisc_priv(cl->qdisc);
-	psched_tdiff_t delay = PSCHED_TDIFF(cl->undertime, q->now);
+	psched_tdiff_t delay = cl->undertime - q->now;
 
 	if (!cl->delayed) {
-		unsigned long sched = jiffies;
+		psched_time_t sched = q->now;
+		ktime_t expires;
 
 		delay += cl->offtime;
 		if (cl->avgidle < 0)
 			delay -= (-cl->avgidle) - ((-cl->avgidle) >> cl->ewma_log);
 		if (cl->avgidle < cl->minidle)
 			cl->avgidle = cl->minidle;
-		PSCHED_TADD2(q->now, delay, cl->undertime);
+		cl->undertime = q->now + delay;
 
 		if (delay > 0) {
-			sched += PSCHED_US2JIFFIE(delay) + cl->penalty;
+			sched += delay + cl->penalty;
 			cl->penalized = sched;
 			cl->cpriority = TC_CBQ_MAXPRIO;
 			q->pmask |= (1<<TC_CBQ_MAXPRIO);
-			if (del_timer(&q->delay_timer) &&
-			    (long)(q->delay_timer.expires - sched) > 0)
-				q->delay_timer.expires = sched;
-			add_timer(&q->delay_timer);
+
+			expires = ktime_set(0, 0);
+			expires = ktime_add_ns(expires, PSCHED_US2NS(sched));
+			if (hrtimer_try_to_cancel(&q->delay_timer) &&
+			    ktime_to_ns(ktime_sub(q->delay_timer.expires,
+						  expires)) > 0)
+				q->delay_timer.expires = expires;
+			hrtimer_restart(&q->delay_timer);
 			cl->delayed = 1;
 			cl->xstats.overactions++;
 			return;
@@ -583,7 +589,7 @@
 {
 	struct cbq_sched_data *q = qdisc_priv(cl->qdisc);
 
-	cl->penalized = jiffies + cl->penalty;
+	cl->penalized = q->now + cl->penalty;
 
 	if (cl->cpriority != cl->priority2) {
 		cl->cpriority = cl->priority2;
@@ -604,27 +610,19 @@
 	cbq_ovl_classic(cl);
 }
 
-static void cbq_watchdog(unsigned long arg)
-{
-	struct Qdisc *sch = (struct Qdisc*)arg;
-
-	sch->flags &= ~TCQ_F_THROTTLED;
-	netif_schedule(sch->dev);
-}
-
-static unsigned long cbq_undelay_prio(struct cbq_sched_data *q, int prio)
+static psched_tdiff_t cbq_undelay_prio(struct cbq_sched_data *q, int prio,
+				       psched_time_t now)
 {
 	struct cbq_class *cl;
 	struct cbq_class *cl_prev = q->active[prio];
-	unsigned long now = jiffies;
-	unsigned long sched = now;
+	psched_time_t sched = now;
 
 	if (cl_prev == NULL)
-		return now;
+		return 0;
 
 	do {
 		cl = cl_prev->next_alive;
-		if ((long)(now - cl->penalized) > 0) {
+		if (now - cl->penalized > 0) {
 			cl_prev->next_alive = cl->next_alive;
 			cl->next_alive = NULL;
 			cl->cpriority = cl->priority;
@@ -640,30 +638,34 @@
 			}
 
 			cl = cl_prev->next_alive;
-		} else if ((long)(sched - cl->penalized) > 0)
+		} else if (sched - cl->penalized > 0)
 			sched = cl->penalized;
 	} while ((cl_prev = cl) != q->active[prio]);
 
-	return (long)(sched - now);
+	return sched - now;
 }
 
-static void cbq_undelay(unsigned long arg)
+static enum hrtimer_restart cbq_undelay(struct hrtimer *timer)
 {
-	struct Qdisc *sch = (struct Qdisc*)arg;
-	struct cbq_sched_data *q = qdisc_priv(sch);
-	long delay = 0;
+	struct cbq_sched_data *q = container_of(timer, struct cbq_sched_data,
+						delay_timer);
+	struct Qdisc *sch = q->watchdog.qdisc;
+	psched_time_t now;
+	psched_tdiff_t delay = 0;
 	unsigned pmask;
 
+	now = psched_get_time();
+
 	pmask = q->pmask;
 	q->pmask = 0;
 
 	while (pmask) {
 		int prio = ffz(~pmask);
-		long tmp;
+		psched_tdiff_t tmp;
 
 		pmask &= ~(1<<prio);
 
-		tmp = cbq_undelay_prio(q, prio);
+		tmp = cbq_undelay_prio(q, prio, now);
 		if (tmp > 0) {
 			q->pmask |= 1<<prio;
 			if (tmp < delay || delay == 0)
@@ -672,12 +674,16 @@
 	}
 
 	if (delay) {
-		q->delay_timer.expires = jiffies + delay;
-		add_timer(&q->delay_timer);
+		ktime_t time;
+
+		time = ktime_set(0, 0);
+		time = ktime_add_ns(time, PSCHED_US2NS(now + delay));
+		hrtimer_start(&q->delay_timer, time, HRTIMER_MODE_ABS);
 	}
 
 	sch->flags &= ~TCQ_F_THROTTLED;
 	netif_schedule(sch->dev);
+	return HRTIMER_NORESTART;
 }
 
 
@@ -732,7 +738,7 @@
 	if (cl && q->toplevel >= borrowed->level) {
 		if (cl->q->q.qlen > 1) {
 			do {
-				if (PSCHED_IS_PASTPERFECT(borrowed->undertime)) {
+				if (borrowed->undertime == PSCHED_PASTPERFECT) {
 					q->toplevel = borrowed->level;
 					return;
 				}
@@ -770,7 +776,7 @@
 			 idle = (now - last) - last_pktlen/rate
 		 */
 
-		idle = PSCHED_TDIFF(q->now, cl->last);
+		idle = q->now - cl->last;
 		if ((unsigned long)idle > 128*1024*1024) {
 			avgidle = cl->maxidle;
 		} else {
@@ -814,13 +820,11 @@
 			idle -= L2T(&q->link, len);
 			idle += L2T(cl, len);
 
-			PSCHED_AUDIT_TDIFF(idle);
-
-			PSCHED_TADD2(q->now, idle, cl->undertime);
+			cl->undertime = q->now + idle;
 		} else {
 			/* Underlimit */
 
-			PSCHED_SET_PASTPERFECT(cl->undertime);
+			cl->undertime = PSCHED_PASTPERFECT;
 			if (avgidle > cl->maxidle)
 				cl->avgidle = cl->maxidle;
 			else
@@ -841,8 +845,7 @@
 	if (cl->tparent == NULL)
 		return cl;
 
-	if (PSCHED_IS_PASTPERFECT(cl->undertime) ||
-	    !PSCHED_TLESS(q->now, cl->undertime)) {
+	if (cl->undertime == PSCHED_PASTPERFECT || q->now >= cl->undertime) {
 		cl->delayed = 0;
 		return cl;
 	}
@@ -865,8 +868,7 @@
 		}
 		if (cl->level > q->toplevel)
 			return NULL;
-	} while (!PSCHED_IS_PASTPERFECT(cl->undertime) &&
-		 PSCHED_TLESS(q->now, cl->undertime));
+	} while (cl->undertime != PSCHED_PASTPERFECT && q->now < cl->undertime);
 
 	cl->delayed = 0;
 	return cl;
@@ -1001,8 +1003,8 @@
 	psched_time_t now;
 	psched_tdiff_t incr;
 
-	PSCHED_GET_TIME(now);
-	incr = PSCHED_TDIFF(now, q->now_rt);
+	now = psched_get_time();
+	incr = now - q->now_rt;
 
 	if (q->tx_class) {
 		psched_tdiff_t incr2;
@@ -1014,12 +1016,12 @@
 		   cbq_time = max(real_time, work);
 		 */
 		incr2 = L2T(&q->link, q->tx_len);
-		PSCHED_TADD(q->now, incr2);
+		q->now += incr2;
 		cbq_update(q);
 		if ((incr -= incr2) < 0)
 			incr = 0;
 	}
-	PSCHED_TADD(q->now, incr);
+	q->now += incr;
 	q->now_rt = now;
 
 	for (;;) {
@@ -1051,11 +1053,11 @@
 		*/
 
 		if (q->toplevel == TC_CBQ_MAXLEVEL &&
-		    PSCHED_IS_PASTPERFECT(q->link.undertime))
+		    q->link.undertime == PSCHED_PASTPERFECT)
 			break;
 
 		q->toplevel = TC_CBQ_MAXLEVEL;
-		PSCHED_SET_PASTPERFECT(q->link.undertime);
+		q->link.undertime = PSCHED_PASTPERFECT;
 	}
 
 	/* No packets in scheduler or nobody wants to give them to us :-(
@@ -1063,13 +1065,9 @@
 
 	if (sch->q.qlen) {
 		sch->qstats.overlimits++;
-		if (q->wd_expires) {
-			long delay = PSCHED_US2JIFFIE(q->wd_expires);
-			if (delay <= 0)
-				delay = 1;
-			mod_timer(&q->wd_timer, jiffies + delay);
-			sch->flags |= TCQ_F_THROTTLED;
-		}
+		if (q->wd_expires)
+			qdisc_watchdog_schedule(&q->watchdog,
+						now + q->wd_expires);
 	}
 	return NULL;
 }
@@ -1276,10 +1274,10 @@
 	q->pmask = 0;
 	q->tx_class = NULL;
 	q->tx_borrowed = NULL;
-	del_timer(&q->wd_timer);
-	del_timer(&q->delay_timer);
+	qdisc_watchdog_cancel(&q->watchdog);
+	hrtimer_cancel(&q->delay_timer);
 	q->toplevel = TC_CBQ_MAXLEVEL;
-	PSCHED_GET_TIME(q->now);
+	q->now = psched_get_time();
 	q->now_rt = q->now;
 
 	for (prio = 0; prio <= TC_CBQ_MAXPRIO; prio++)
@@ -1290,7 +1288,7 @@
 			qdisc_reset(cl->q);
 
 			cl->next_alive = NULL;
-			PSCHED_SET_PASTPERFECT(cl->undertime);
+			cl->undertime = PSCHED_PASTPERFECT;
 			cl->avgidle = cl->maxidle;
 			cl->deficit = cl->quantum;
 			cl->cpriority = cl->priority;
@@ -1379,7 +1377,7 @@
 	default:
 		return -EINVAL;
 	}
-	cl->penalty = (ovl->penalty*HZ)/1000;
+	cl->penalty = ovl->penalty;
 	return 0;
 }
 
@@ -1446,14 +1444,11 @@
 	q->link.minidle = -0x7FFFFFFF;
 	q->link.stats_lock = &sch->dev->queue_lock;
 
-	init_timer(&q->wd_timer);
-	q->wd_timer.data = (unsigned long)sch;
-	q->wd_timer.function = cbq_watchdog;
-	init_timer(&q->delay_timer);
-	q->delay_timer.data = (unsigned long)sch;
+	qdisc_watchdog_init(&q->watchdog, sch);
+	hrtimer_init(&q->delay_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
 	q->delay_timer.function = cbq_undelay;
 	q->toplevel = TC_CBQ_MAXLEVEL;
-	PSCHED_GET_TIME(q->now);
+	q->now = psched_get_time();
 	q->now_rt = q->now;
 
 	cbq_link_class(&q->link);
@@ -1467,19 +1462,19 @@
 
 static __inline__ int cbq_dump_rate(struct sk_buff *skb, struct cbq_class *cl)
 {
-	unsigned char	 *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 
 	RTA_PUT(skb, TCA_CBQ_RATE, sizeof(cl->R_tab->rate), &cl->R_tab->rate);
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
 static __inline__ int cbq_dump_lss(struct sk_buff *skb, struct cbq_class *cl)
 {
-	unsigned char	 *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct tc_cbq_lssopt opt;
 
 	opt.flags = 0;
@@ -1498,13 +1493,13 @@
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
 static __inline__ int cbq_dump_wrr(struct sk_buff *skb, struct cbq_class *cl)
 {
-	unsigned char	 *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct tc_cbq_wrropt opt;
 
 	opt.flags = 0;
@@ -1516,30 +1511,30 @@
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
 static __inline__ int cbq_dump_ovl(struct sk_buff *skb, struct cbq_class *cl)
 {
-	unsigned char	 *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct tc_cbq_ovl opt;
 
 	opt.strategy = cl->ovl_strategy;
 	opt.priority2 = cl->priority2+1;
 	opt.pad = 0;
-	opt.penalty = (cl->penalty*1000)/HZ;
+	opt.penalty = cl->penalty;
 	RTA_PUT(skb, TCA_CBQ_OVL_STRATEGY, sizeof(opt), &opt);
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
 static __inline__ int cbq_dump_fopt(struct sk_buff *skb, struct cbq_class *cl)
 {
-	unsigned char	 *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct tc_cbq_fopt opt;
 
 	if (cl->split || cl->defmap) {
@@ -1551,14 +1546,14 @@
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
 #ifdef CONFIG_NET_CLS_POLICE
 static __inline__ int cbq_dump_police(struct sk_buff *skb, struct cbq_class *cl)
 {
-	unsigned char	 *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct tc_cbq_police opt;
 
 	if (cl->police) {
@@ -1570,7 +1565,7 @@
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 #endif
@@ -1592,18 +1587,18 @@
 static int cbq_dump(struct Qdisc *sch, struct sk_buff *skb)
 {
 	struct cbq_sched_data *q = qdisc_priv(sch);
-	unsigned char	 *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct rtattr *rta;
 
 	rta = (struct rtattr*)b;
 	RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
 	if (cbq_dump_attr(skb, &q->link) < 0)
 		goto rtattr_failure;
-	rta->rta_len = skb->tail - b;
+	rta->rta_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -1621,7 +1616,7 @@
 	       struct sk_buff *skb, struct tcmsg *tcm)
 {
 	struct cbq_class *cl = (struct cbq_class*)arg;
-	unsigned char	 *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct rtattr *rta;
 
 	if (cl->tparent)
@@ -1635,11 +1630,11 @@
 	RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
 	if (cbq_dump_attr(skb, cl) < 0)
 		goto rtattr_failure;
-	rta->rta_len = skb->tail - b;
+	rta->rta_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -1654,8 +1649,8 @@
 	cl->xstats.avgidle = cl->avgidle;
 	cl->xstats.undertime = 0;
 
-	if (!PSCHED_IS_PASTPERFECT(cl->undertime))
-		cl->xstats.undertime = PSCHED_TDIFF(cl->undertime, q->now);
+	if (cl->undertime != PSCHED_PASTPERFECT)
+		cl->xstats.undertime = cl->undertime - q->now;
 
 	if (gnet_stats_copy_basic(d, &cl->bstats) < 0 ||
 #ifdef CONFIG_NET_ESTIMATOR
@@ -1722,23 +1717,13 @@
 	return 0;
 }
 
-static void cbq_destroy_filters(struct cbq_class *cl)
-{
-	struct tcf_proto *tp;
-
-	while ((tp = cl->filter_list) != NULL) {
-		cl->filter_list = tp->next;
-		tcf_destroy(tp);
-	}
-}
-
 static void cbq_destroy_class(struct Qdisc *sch, struct cbq_class *cl)
 {
 	struct cbq_sched_data *q = qdisc_priv(sch);
 
 	BUG_TRAP(!cl->filters);
 
-	cbq_destroy_filters(cl);
+	tcf_destroy_chain(cl->filter_list);
 	qdisc_destroy(cl->q);
 	qdisc_put_rtab(cl->R_tab);
 #ifdef CONFIG_NET_ESTIMATOR
@@ -1765,7 +1750,7 @@
 	 */
 	for (h = 0; h < 16; h++)
 		for (cl = q->classes[h]; cl; cl = cl->next)
-			cbq_destroy_filters(cl);
+			tcf_destroy_chain(cl->filter_list);
 
 	for (h = 0; h < 16; h++) {
 		struct cbq_class *next;
diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c
index 96324cf..3c6fd18 100644
--- a/net/sched/sch_dsmark.c
+++ b/net/sched/sch_dsmark.c
@@ -216,17 +216,17 @@
 		/* FIXME: Safe with non-linear skbs? --RR */
 		switch (skb->protocol) {
 			case __constant_htons(ETH_P_IP):
-				skb->tc_index = ipv4_get_dsfield(skb->nh.iph)
+				skb->tc_index = ipv4_get_dsfield(ip_hdr(skb))
 					& ~INET_ECN_MASK;
 				break;
 			case __constant_htons(ETH_P_IPV6):
-				skb->tc_index = ipv6_get_dsfield(skb->nh.ipv6h)
+				skb->tc_index = ipv6_get_dsfield(ipv6_hdr(skb))
 					& ~INET_ECN_MASK;
 				break;
 			default:
 				skb->tc_index = 0;
 				break;
-		};
+		}
 	}
 
 	if (TC_H_MAJ(skb->priority) == sch->handle)
@@ -257,7 +257,7 @@
 				if (p->default_index != NO_DEFAULT_INDEX)
 					skb->tc_index = p->default_index;
 				break;
-		};
+		}
 	}
 
 	err = p->q->enqueue(skb,p->q);
@@ -292,11 +292,11 @@
 
 	switch (skb->protocol) {
 		case __constant_htons(ETH_P_IP):
-			ipv4_change_dsfield(skb->nh.iph, p->mask[index],
+			ipv4_change_dsfield(ip_hdr(skb), p->mask[index],
 					    p->value[index]);
 			break;
 		case __constant_htons(ETH_P_IPV6):
-			ipv6_change_dsfield(skb->nh.ipv6h, p->mask[index],
+			ipv6_change_dsfield(ipv6_hdr(skb), p->mask[index],
 					    p->value[index]);
 			break;
 		default:
@@ -310,7 +310,7 @@
 				       "unsupported protocol %d\n",
 				       ntohs(skb->protocol));
 			break;
-	};
+	}
 
 	return skb;
 }
@@ -412,16 +412,10 @@
 static void dsmark_destroy(struct Qdisc *sch)
 {
 	struct dsmark_qdisc_data *p = PRIV(sch);
-	struct tcf_proto *tp;
 
 	DPRINTK("dsmark_destroy(sch %p,[qdisc %p])\n", sch, p);
 
-	while (p->filter_list) {
-		tp = p->filter_list;
-		p->filter_list = tp->next;
-		tcf_destroy(tp);
-	}
-
+	tcf_destroy_chain(p->filter_list);
 	qdisc_destroy(p->q);
 	kfree(p->mask);
 }
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index 52eb343..3385ee5 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -36,34 +36,27 @@
 
 /* Main transmission queue. */
 
-/* Main qdisc structure lock.
-
-   However, modifications
-   to data, participating in scheduling must be additionally
-   protected with dev->queue_lock spinlock.
-
-   The idea is the following:
-   - enqueue, dequeue are serialized via top level device
-     spinlock dev->queue_lock.
-   - tree walking is protected by read_lock(qdisc_tree_lock)
-     and this lock is used only in process context.
-   - updates to tree are made only under rtnl semaphore,
-     hence this lock may be made without local bh disabling.
-
-   qdisc_tree_lock must be grabbed BEFORE dev->queue_lock!
+/* Modifications to data participating in scheduling must be protected with
+ * dev->queue_lock spinlock.
+ *
+ * The idea is the following:
+ * - enqueue, dequeue are serialized via top level device
+ *   spinlock dev->queue_lock.
+ * - ingress filtering is serialized via top level device
+ *   spinlock dev->ingress_lock.
+ * - updates to tree and tree walking are only done under the rtnl mutex.
  */
-DEFINE_RWLOCK(qdisc_tree_lock);
 
 void qdisc_lock_tree(struct net_device *dev)
 {
-	write_lock(&qdisc_tree_lock);
 	spin_lock_bh(&dev->queue_lock);
+	spin_lock(&dev->ingress_lock);
 }
 
 void qdisc_unlock_tree(struct net_device *dev)
 {
+	spin_unlock(&dev->ingress_lock);
 	spin_unlock_bh(&dev->queue_lock);
-	write_unlock(&qdisc_tree_lock);
 }
 
 /*
@@ -442,7 +435,6 @@
 	sch->dequeue = ops->dequeue;
 	sch->dev = dev;
 	dev_hold(dev);
-	sch->stats_lock = &dev->queue_lock;
 	atomic_set(&sch->refcnt, 1);
 
 	return sch;
@@ -458,6 +450,7 @@
 	sch = qdisc_alloc(dev, ops);
 	if (IS_ERR(sch))
 		goto errout;
+	sch->stats_lock = &dev->queue_lock;
 	sch->parent = parentid;
 
 	if (!ops->init || ops->init(sch, NULL) == 0)
@@ -528,15 +521,11 @@
 				printk(KERN_INFO "%s: activation failed\n", dev->name);
 				return;
 			}
-			write_lock(&qdisc_tree_lock);
 			list_add_tail(&qdisc->list, &dev->qdisc_list);
-			write_unlock(&qdisc_tree_lock);
 		} else {
 			qdisc =  &noqueue_qdisc;
 		}
-		write_lock(&qdisc_tree_lock);
 		dev->qdisc_sleeping = qdisc;
-		write_unlock(&qdisc_tree_lock);
 	}
 
 	if (!netif_carrier_ok(dev))
diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c
index 407c6fb..9d124c4 100644
--- a/net/sched/sch_hfsc.c
+++ b/net/sched/sch_hfsc.c
@@ -59,13 +59,13 @@
 #include <linux/skbuff.h>
 #include <linux/string.h>
 #include <linux/slab.h>
-#include <linux/timer.h>
 #include <linux/list.h>
 #include <linux/rbtree.h>
 #include <linux/init.h>
 #include <linux/netdevice.h>
 #include <linux/rtnetlink.h>
 #include <linux/pkt_sched.h>
+#include <net/netlink.h>
 #include <net/pkt_sched.h>
 #include <net/pkt_cls.h>
 #include <asm/system.h>
@@ -192,23 +192,9 @@
 	struct list_head droplist;		/* active leaf class list (for
 						   dropping) */
 	struct sk_buff_head requeue;		/* requeued packet */
-	struct timer_list wd_timer;		/* watchdog timer */
+	struct qdisc_watchdog watchdog;		/* watchdog timer */
 };
 
-/*
- * macros
- */
-#ifdef CONFIG_NET_SCH_CLK_GETTIMEOFDAY
-#include <linux/time.h>
-#undef PSCHED_GET_TIME
-#define PSCHED_GET_TIME(stamp)						\
-do {									\
-	struct timeval tv;						\
-	do_gettimeofday(&tv);						\
-	(stamp) = 1ULL * USEC_PER_SEC * tv.tv_sec + tv.tv_usec;		\
-} while (0)
-#endif
-
 #define	HT_INFINITY	0xffffffffffffffffULL	/* infinite time value */
 
 
@@ -394,28 +380,17 @@
  *	ism: (psched_us/byte) << ISM_SHIFT
  *	dx: psched_us
  *
- * Clock source resolution (CONFIG_NET_SCH_CLK_*)
- *  JIFFIES: for 48<=HZ<=1534 resolution is between 0.63us and 1.27us.
- *  CPU: resolution is between 0.5us and 1us.
- *  GETTIMEOFDAY: resolution is exactly 1us.
+ * The clock source resolution with ktime is 1.024us.
  *
  * sm and ism are scaled in order to keep effective digits.
  * SM_SHIFT and ISM_SHIFT are selected to keep at least 4 effective
  * digits in decimal using the following table.
  *
- * Note: We can afford the additional accuracy (altq hfsc keeps at most
- * 3 effective digits) thanks to the fact that linux clock is bounded
- * much more tightly.
- *
  *  bits/sec      100Kbps     1Mbps     10Mbps     100Mbps    1Gbps
  *  ------------+-------------------------------------------------------
- *  bytes/0.5us   6.25e-3    62.5e-3    625e-3     6250e-e    62500e-3
- *  bytes/us      12.5e-3    125e-3     1250e-3    12500e-3   125000e-3
- *  bytes/1.27us  15.875e-3  158.75e-3  1587.5e-3  15875e-3   158750e-3
+ *  bytes/1.024us 12.8e-3    128e-3     1280e-3    12800e-3   128000e-3
  *
- *  0.5us/byte    160        16         1.6        0.16       0.016
- *  us/byte       80         8          0.8        0.08       0.008
- *  1.27us/byte   63         6.3        0.63       0.063      0.0063
+ *  1.024us/byte  78.125     7.8125     0.78125    0.078125   0.0078125
  */
 #define	SM_SHIFT	20
 #define	ISM_SHIFT	18
@@ -460,8 +435,8 @@
 	u64 sm;
 
 	sm = ((u64)m << SM_SHIFT);
-	sm += PSCHED_JIFFIE2US(HZ) - 1;
-	do_div(sm, PSCHED_JIFFIE2US(HZ));
+	sm += PSCHED_TICKS_PER_SEC - 1;
+	do_div(sm, PSCHED_TICKS_PER_SEC);
 	return sm;
 }
 
@@ -474,7 +449,7 @@
 	if (m == 0)
 		ism = HT_INFINITY;
 	else {
-		ism = ((u64)PSCHED_JIFFIE2US(HZ) << ISM_SHIFT);
+		ism = ((u64)PSCHED_TICKS_PER_SEC << ISM_SHIFT);
 		ism += m - 1;
 		do_div(ism, m);
 	}
@@ -487,7 +462,7 @@
 {
 	u64 dx;
 
-	dx = ((u64)d * PSCHED_JIFFIE2US(HZ));
+	dx = ((u64)d * PSCHED_TICKS_PER_SEC);
 	dx += USEC_PER_SEC - 1;
 	do_div(dx, USEC_PER_SEC);
 	return dx;
@@ -499,7 +474,7 @@
 {
 	u64 m;
 
-	m = (sm * PSCHED_JIFFIE2US(HZ)) >> SM_SHIFT;
+	m = (sm * PSCHED_TICKS_PER_SEC) >> SM_SHIFT;
 	return (u32)m;
 }
 
@@ -510,7 +485,7 @@
 	u64 d;
 
 	d = dx * USEC_PER_SEC;
-	do_div(d, PSCHED_JIFFIE2US(HZ));
+	do_div(d, PSCHED_TICKS_PER_SEC);
 	return (u32)d;
 }
 
@@ -654,9 +629,7 @@
 static void
 init_ed(struct hfsc_class *cl, unsigned int next_len)
 {
-	u64 cur_time;
-
-	PSCHED_GET_TIME(cur_time);
+	u64 cur_time = psched_get_time();
 
 	/* update the deadline curve */
 	rtsc_min(&cl->cl_deadline, &cl->cl_rsc, cur_time, cl->cl_cumul);
@@ -779,7 +752,7 @@
 			if (cl->cl_flags & HFSC_USC) {
 				/* class has upper limit curve */
 				if (cur_time == 0)
-					PSCHED_GET_TIME(cur_time);
+					cur_time = psched_get_time();
 
 				/* update the ulimit curve */
 				rtsc_min(&cl->cl_ulimit, &cl->cl_usc, cur_time,
@@ -1063,7 +1036,7 @@
 			if (cl->cl_parent == NULL && parentid != TC_H_ROOT)
 				return -EINVAL;
 		}
-		PSCHED_GET_TIME(cur_time);
+		cur_time = psched_get_time();
 
 		sch_tree_lock(sch);
 		if (rsc != NULL)
@@ -1149,22 +1122,11 @@
 }
 
 static void
-hfsc_destroy_filters(struct tcf_proto **fl)
-{
-	struct tcf_proto *tp;
-
-	while ((tp = *fl) != NULL) {
-		*fl = tp->next;
-		tcf_destroy(tp);
-	}
-}
-
-static void
 hfsc_destroy_class(struct Qdisc *sch, struct hfsc_class *cl)
 {
 	struct hfsc_sched *q = qdisc_priv(sch);
 
-	hfsc_destroy_filters(&cl->filter_list);
+	tcf_destroy_chain(cl->filter_list);
 	qdisc_destroy(cl->qdisc);
 #ifdef CONFIG_NET_ESTIMATOR
 	gen_kill_estimator(&cl->bstats, &cl->rate_est);
@@ -1389,7 +1351,7 @@
 		struct tcmsg *tcm)
 {
 	struct hfsc_class *cl = (struct hfsc_class *)arg;
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct rtattr *rta = (struct rtattr *)b;
 
 	tcm->tcm_parent = cl->cl_parent ? cl->cl_parent->classid : TC_H_ROOT;
@@ -1400,11 +1362,11 @@
 	RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
 	if (hfsc_dump_curves(skb, cl) < 0)
 		goto rtattr_failure;
-	rta->rta_len = skb->tail - b;
+	rta->rta_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
  rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -1459,21 +1421,11 @@
 }
 
 static void
-hfsc_watchdog(unsigned long arg)
-{
-	struct Qdisc *sch = (struct Qdisc *)arg;
-
-	sch->flags &= ~TCQ_F_THROTTLED;
-	netif_schedule(sch->dev);
-}
-
-static void
-hfsc_schedule_watchdog(struct Qdisc *sch, u64 cur_time)
+hfsc_schedule_watchdog(struct Qdisc *sch)
 {
 	struct hfsc_sched *q = qdisc_priv(sch);
 	struct hfsc_class *cl;
 	u64 next_time = 0;
-	long delay;
 
 	if ((cl = eltree_get_minel(q)) != NULL)
 		next_time = cl->cl_e;
@@ -1482,11 +1434,7 @@
 			next_time = q->root.cl_cfmin;
 	}
 	WARN_ON(next_time == 0);
-	delay = next_time - cur_time;
-	delay = PSCHED_US2JIFFIE(delay);
-
-	sch->flags |= TCQ_F_THROTTLED;
-	mod_timer(&q->wd_timer, jiffies + delay);
+	qdisc_watchdog_schedule(&q->watchdog, next_time);
 }
 
 static int
@@ -1523,9 +1471,7 @@
 
 	list_add(&q->root.hlist, &q->clhash[hfsc_hash(q->root.classid)]);
 
-	init_timer(&q->wd_timer);
-	q->wd_timer.function = hfsc_watchdog;
-	q->wd_timer.data = (unsigned long)sch;
+	qdisc_watchdog_init(&q->watchdog, sch);
 
 	return 0;
 }
@@ -1595,8 +1541,7 @@
 	__skb_queue_purge(&q->requeue);
 	q->eligible = RB_ROOT;
 	INIT_LIST_HEAD(&q->droplist);
-	del_timer(&q->wd_timer);
-	sch->flags &= ~TCQ_F_THROTTLED;
+	qdisc_watchdog_cancel(&q->watchdog);
 	sch->q.qlen = 0;
 }
 
@@ -1612,14 +1557,14 @@
 			hfsc_destroy_class(sch, cl);
 	}
 	__skb_queue_purge(&q->requeue);
-	del_timer(&q->wd_timer);
+	qdisc_watchdog_cancel(&q->watchdog);
 }
 
 static int
 hfsc_dump_qdisc(struct Qdisc *sch, struct sk_buff *skb)
 {
 	struct hfsc_sched *q = qdisc_priv(sch);
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct tc_hfsc_qopt qopt;
 
 	qopt.defcls = q->defcls;
@@ -1627,7 +1572,7 @@
 	return skb->len;
 
  rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -1681,7 +1626,7 @@
 	if ((skb = __skb_dequeue(&q->requeue)))
 		goto out;
 
-	PSCHED_GET_TIME(cur_time);
+	cur_time = psched_get_time();
 
 	/*
 	 * if there are eligible classes, use real-time criteria.
@@ -1698,7 +1643,7 @@
 		cl = vttree_get_minvt(&q->root, cur_time);
 		if (cl == NULL) {
 			sch->qstats.overlimits++;
-			hfsc_schedule_watchdog(sch, cur_time);
+			hfsc_schedule_watchdog(sch);
 			return NULL;
 		}
 	}
diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
index 3c3294d..99bcec8 100644
--- a/net/sched/sch_htb.c
+++ b/net/sched/sch_htb.c
@@ -50,6 +50,7 @@
 #include <linux/skbuff.h>
 #include <linux/list.h>
 #include <linux/compiler.h>
+#include <net/netlink.h>
 #include <net/sock.h>
 #include <net/pkt_sched.h>
 #include <linux/rbtree.h>
@@ -128,7 +129,7 @@
 	} un;
 	struct rb_node node[TC_HTB_NUMPRIO];	/* node for self or feed tree */
 	struct rb_node pq_node;	/* node for event queue */
-	unsigned long pq_key;	/* the same type as jiffies global */
+	psched_time_t pq_key;
 
 	int prio_activity;	/* for which prios are we active */
 	enum htb_cmode cmode;	/* current mode of the class */
@@ -179,10 +180,7 @@
 	struct rb_root wait_pq[TC_HTB_MAXDEPTH];
 
 	/* time of nearest event per level (row) */
-	unsigned long near_ev_cache[TC_HTB_MAXDEPTH];
-
-	/* cached value of jiffies in dequeue */
-	unsigned long jiffies;
+	psched_time_t near_ev_cache[TC_HTB_MAXDEPTH];
 
 	/* whether we hit non-work conserving class during this dequeue; we use */
 	int nwc_hit;		/* this to disable mindelay complaint in dequeue */
@@ -195,7 +193,7 @@
 
 	int rate2quantum;	/* quant = rate / rate2quantum */
 	psched_time_t now;	/* cached dequeue time */
-	struct timer_list timer;	/* send delay timer */
+	struct qdisc_watchdog watchdog;
 #ifdef HTB_RATECM
 	struct timer_list rttim;	/* rate computer timer */
 	int recmp_bucket;	/* which hash bucket to recompute next */
@@ -342,19 +340,19 @@
 {
 	struct rb_node **p = &q->wait_pq[cl->level].rb_node, *parent = NULL;
 
-	cl->pq_key = q->jiffies + PSCHED_US2JIFFIE(delay);
-	if (cl->pq_key == q->jiffies)
+	cl->pq_key = q->now + delay;
+	if (cl->pq_key == q->now)
 		cl->pq_key++;
 
 	/* update the nearest event cache */
-	if (time_after(q->near_ev_cache[cl->level], cl->pq_key))
+	if (q->near_ev_cache[cl->level] > cl->pq_key)
 		q->near_ev_cache[cl->level] = cl->pq_key;
 
 	while (*p) {
 		struct htb_class *c;
 		parent = *p;
 		c = rb_entry(parent, struct htb_class, pq_node);
-		if (time_after_eq(cl->pq_key, c->pq_key))
+		if (cl->pq_key >= c->pq_key)
 			p = &parent->rb_right;
 		else
 			p = &parent->rb_left;
@@ -679,14 +677,6 @@
 	return NET_XMIT_SUCCESS;
 }
 
-static void htb_timer(unsigned long arg)
-{
-	struct Qdisc *sch = (struct Qdisc *)arg;
-	sch->flags &= ~TCQ_F_THROTTLED;
-	wmb();
-	netif_schedule(sch->dev);
-}
-
 #ifdef HTB_RATECM
 #define RT_GEN(D,R) R+=D-(R/HTB_EWMAC);D=0
 static void htb_rate_timer(unsigned long arg)
@@ -739,7 +729,7 @@
 	cl->T = toks
 
 	while (cl) {
-		diff = PSCHED_TDIFF_SAFE(q->now, cl->t_c, (u32) cl->mbuffer);
+		diff = psched_tdiff_bounded(q->now, cl->t_c, cl->mbuffer);
 		if (cl->level >= level) {
 			if (cl->level == level)
 				cl->xstats.lends++;
@@ -778,11 +768,11 @@
 /**
  * htb_do_events - make mode changes to classes at the level
  *
- * Scans event queue for pending events and applies them. Returns jiffies to
+ * Scans event queue for pending events and applies them. Returns time of
  * next pending event (0 for no event in pq).
- * Note: Aplied are events whose have cl->pq_key <= jiffies.
+ * Note: Applied are events whose have cl->pq_key <= q->now.
  */
-static long htb_do_events(struct htb_sched *q, int level)
+static psched_time_t htb_do_events(struct htb_sched *q, int level)
 {
 	int i;
 
@@ -795,18 +785,18 @@
 			return 0;
 
 		cl = rb_entry(p, struct htb_class, pq_node);
-		if (time_after(cl->pq_key, q->jiffies)) {
-			return cl->pq_key - q->jiffies;
-		}
+		if (cl->pq_key > q->now)
+			return cl->pq_key;
+
 		htb_safe_rb_erase(p, q->wait_pq + level);
-		diff = PSCHED_TDIFF_SAFE(q->now, cl->t_c, (u32) cl->mbuffer);
+		diff = psched_tdiff_bounded(q->now, cl->t_c, cl->mbuffer);
 		htb_change_class_mode(q, cl, &diff);
 		if (cl->cmode != HTB_CAN_SEND)
 			htb_add_to_wait_tree(q, cl, diff);
 	}
 	if (net_ratelimit())
 		printk(KERN_WARNING "htb: too many events !\n");
-	return HZ / 10;
+	return q->now + PSCHED_TICKS_PER_SEC / 10;
 }
 
 /* Returns class->node+prio from id-tree where classe's id is >= id. NULL
@@ -958,30 +948,12 @@
 	return skb;
 }
 
-static void htb_delay_by(struct Qdisc *sch, long delay)
-{
-	struct htb_sched *q = qdisc_priv(sch);
-	if (delay <= 0)
-		delay = 1;
-	if (unlikely(delay > 5 * HZ)) {
-		if (net_ratelimit())
-			printk(KERN_INFO "HTB delay %ld > 5sec\n", delay);
-		delay = 5 * HZ;
-	}
-	/* why don't use jiffies here ? because expires can be in past */
-	mod_timer(&q->timer, q->jiffies + delay);
-	sch->flags |= TCQ_F_THROTTLED;
-	sch->qstats.overlimits++;
-}
-
 static struct sk_buff *htb_dequeue(struct Qdisc *sch)
 {
 	struct sk_buff *skb = NULL;
 	struct htb_sched *q = qdisc_priv(sch);
 	int level;
-	long min_delay;
-
-	q->jiffies = jiffies;
+	psched_time_t next_event;
 
 	/* try to dequeue direct packets as high prio (!) to minimize cpu work */
 	skb = __skb_dequeue(&q->direct_queue);
@@ -993,23 +965,25 @@
 
 	if (!sch->q.qlen)
 		goto fin;
-	PSCHED_GET_TIME(q->now);
+	q->now = psched_get_time();
 
-	min_delay = LONG_MAX;
+	next_event = q->now + 5 * PSCHED_TICKS_PER_SEC;
 	q->nwc_hit = 0;
 	for (level = 0; level < TC_HTB_MAXDEPTH; level++) {
 		/* common case optimization - skip event handler quickly */
 		int m;
-		long delay;
-		if (time_after_eq(q->jiffies, q->near_ev_cache[level])) {
-			delay = htb_do_events(q, level);
-			q->near_ev_cache[level] =
-			    q->jiffies + (delay ? delay : HZ);
-		} else
-			delay = q->near_ev_cache[level] - q->jiffies;
+		psched_time_t event;
 
-		if (delay && min_delay > delay)
-			min_delay = delay;
+		if (q->now >= q->near_ev_cache[level]) {
+			event = htb_do_events(q, level);
+			q->near_ev_cache[level] = event ? event :
+							  PSCHED_TICKS_PER_SEC;
+		} else
+			event = q->near_ev_cache[level];
+
+		if (event && next_event > event)
+			next_event = event;
+
 		m = ~q->row_mask[level];
 		while (m != (int)(-1)) {
 			int prio = ffz(m);
@@ -1022,7 +996,8 @@
 			}
 		}
 	}
-	htb_delay_by(sch, min_delay > 5 * HZ ? 5 * HZ : min_delay);
+	sch->qstats.overlimits++;
+	qdisc_watchdog_schedule(&q->watchdog, next_event);
 fin:
 	return skb;
 }
@@ -1075,8 +1050,7 @@
 
 		}
 	}
-	sch->flags &= ~TCQ_F_THROTTLED;
-	del_timer(&q->timer);
+	qdisc_watchdog_cancel(&q->watchdog);
 	__skb_queue_purge(&q->direct_queue);
 	sch->q.qlen = 0;
 	memset(q->row, 0, sizeof(q->row));
@@ -1113,14 +1087,12 @@
 	for (i = 0; i < TC_HTB_NUMPRIO; i++)
 		INIT_LIST_HEAD(q->drops + i);
 
-	init_timer(&q->timer);
+	qdisc_watchdog_init(&q->watchdog, sch);
 	skb_queue_head_init(&q->direct_queue);
 
 	q->direct_qlen = sch->dev->tx_queue_len;
 	if (q->direct_qlen < 2)	/* some devices have zero tx_queue_len */
 		q->direct_qlen = 2;
-	q->timer.function = htb_timer;
-	q->timer.data = (unsigned long)sch;
 
 #ifdef HTB_RATECM
 	init_timer(&q->rttim);
@@ -1139,7 +1111,7 @@
 static int htb_dump(struct Qdisc *sch, struct sk_buff *skb)
 {
 	struct htb_sched *q = qdisc_priv(sch);
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct rtattr *rta;
 	struct tc_htb_glob gopt;
 	spin_lock_bh(&sch->dev->queue_lock);
@@ -1152,12 +1124,12 @@
 	rta = (struct rtattr *)b;
 	RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
 	RTA_PUT(skb, TCA_HTB_INIT, sizeof(gopt), &gopt);
-	rta->rta_len = skb->tail - b;
+	rta->rta_len = skb_tail_pointer(skb) - b;
 	spin_unlock_bh(&sch->dev->queue_lock);
 	return skb->len;
 rtattr_failure:
 	spin_unlock_bh(&sch->dev->queue_lock);
-	skb_trim(skb, skb->tail - skb->data);
+	nlmsg_trim(skb, skb_tail_pointer(skb));
 	return -1;
 }
 
@@ -1165,7 +1137,7 @@
 			  struct sk_buff *skb, struct tcmsg *tcm)
 {
 	struct htb_class *cl = (struct htb_class *)arg;
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct rtattr *rta;
 	struct tc_htb_opt opt;
 
@@ -1188,12 +1160,12 @@
 	opt.prio = cl->un.leaf.prio;
 	opt.level = cl->level;
 	RTA_PUT(skb, TCA_HTB_PARMS, sizeof(opt), &opt);
-	rta->rta_len = skb->tail - b;
+	rta->rta_len = skb_tail_pointer(skb) - b;
 	spin_unlock_bh(&sch->dev->queue_lock);
 	return skb->len;
 rtattr_failure:
 	spin_unlock_bh(&sch->dev->queue_lock);
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -1264,16 +1236,6 @@
 	return (unsigned long)cl;
 }
 
-static void htb_destroy_filters(struct tcf_proto **fl)
-{
-	struct tcf_proto *tp;
-
-	while ((tp = *fl) != NULL) {
-		*fl = tp->next;
-		tcf_destroy(tp);
-	}
-}
-
 static inline int htb_parent_last_child(struct htb_class *cl)
 {
 	if (!cl->parent)
@@ -1302,7 +1264,7 @@
 	parent->un.leaf.prio = parent->prio;
 	parent->tokens = parent->buffer;
 	parent->ctokens = parent->cbuffer;
-	PSCHED_GET_TIME(parent->t_c);
+	parent->t_c = psched_get_time();
 	parent->cmode = HTB_CAN_SEND;
 }
 
@@ -1317,7 +1279,7 @@
 	qdisc_put_rtab(cl->rate);
 	qdisc_put_rtab(cl->ceil);
 
-	htb_destroy_filters(&cl->filter_list);
+	tcf_destroy_chain(cl->filter_list);
 
 	while (!list_empty(&cl->children))
 		htb_destroy_class(sch, list_entry(cl->children.next,
@@ -1341,7 +1303,7 @@
 {
 	struct htb_sched *q = qdisc_priv(sch);
 
-	del_timer_sync(&q->timer);
+	qdisc_watchdog_cancel(&q->watchdog);
 #ifdef HTB_RATECM
 	del_timer_sync(&q->rttim);
 #endif
@@ -1349,7 +1311,7 @@
 	   and surprisingly it worked in 2.4. But it must precede it
 	   because filter need its target class alive to be able to call
 	   unbind_filter on it (without Oops). */
-	htb_destroy_filters(&q->filter_list);
+	tcf_destroy_chain(q->filter_list);
 
 	while (!list_empty(&q->root))
 		htb_destroy_class(sch, list_entry(q->root.next,
@@ -1498,8 +1460,8 @@
 		/* set class to be in HTB_CAN_SEND state */
 		cl->tokens = hopt->buffer;
 		cl->ctokens = hopt->cbuffer;
-		cl->mbuffer = PSCHED_JIFFIE2US(HZ * 60);	/* 1min */
-		PSCHED_GET_TIME(cl->t_c);
+		cl->mbuffer = 60 * PSCHED_TICKS_PER_SEC;	/* 1min */
+		cl->t_c = psched_get_time();
 		cl->cmode = HTB_CAN_SEND;
 
 		/* attach to the hash list and parent's family */
diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c
index cfe070e..f8b9f1c 100644
--- a/net/sched/sch_ingress.c
+++ b/net/sched/sch_ingress.c
@@ -16,6 +16,7 @@
 #include <linux/netfilter_ipv6.h>
 #include <linux/netfilter.h>
 #include <linux/smp.h>
+#include <net/netlink.h>
 #include <net/pkt_sched.h>
 #include <asm/byteorder.h>
 #include <asm/uaccess.h>
@@ -169,7 +170,7 @@
 			skb->tc_index = TC_H_MIN(res.classid);
 			result = TC_ACT_OK;
 			break;
-	};
+	}
 /* backward compat */
 #else
 #ifdef	CONFIG_NET_CLS_POLICE
@@ -186,7 +187,7 @@
 		sch->bstats.bytes += skb->len;
 		result = NF_ACCEPT;
 		break;
-	};
+	}
 
 #else
 	D2PRINTK("Overriding result to ACCEPT\n");
@@ -247,16 +248,11 @@
 		skb->dev ? (*pskb)->dev->name : "(no dev)",
 		skb->len);
 
-/*
-revisit later: Use a private since lock dev->queue_lock is also
-used on the egress (might slow things for an iota)
-*/
-
 	if (dev->qdisc_ingress) {
-		spin_lock(&dev->queue_lock);
+		spin_lock(&dev->ingress_lock);
 		if ((q = dev->qdisc_ingress) != NULL)
 			fwres = q->enqueue(skb, q);
-		spin_unlock(&dev->queue_lock);
+		spin_unlock(&dev->ingress_lock);
 	}
 
 	return fwres;
@@ -345,14 +341,9 @@
 static void ingress_destroy(struct Qdisc *sch)
 {
 	struct ingress_qdisc_data *p = PRIV(sch);
-	struct tcf_proto *tp;
 
 	DPRINTK("ingress_destroy(sch %p,[qdisc %p])\n", sch, p);
-	while (p->filter_list) {
-		tp = p->filter_list;
-		p->filter_list = tp->next;
-		tcf_destroy(tp);
-	}
+	tcf_destroy_chain(p->filter_list);
 #if 0
 /* for future use */
 	qdisc_destroy(p->q);
@@ -362,16 +353,16 @@
 
 static int ingress_dump(struct Qdisc *sch, struct sk_buff *skb)
 {
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct rtattr *rta;
 
 	rta = (struct rtattr *) b;
 	RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
-	rta->rta_len = skb->tail - b;
+	rta->rta_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 1ccbfb5..5d9d8bc 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -22,6 +22,7 @@
 #include <linux/skbuff.h>
 #include <linux/rtnetlink.h>
 
+#include <net/netlink.h>
 #include <net/pkt_sched.h>
 
 #define VERSION "1.2"
@@ -54,21 +55,22 @@
 
 struct netem_sched_data {
 	struct Qdisc	*qdisc;
-	struct timer_list timer;
+	struct qdisc_watchdog watchdog;
 
-	u32 latency;
+	psched_tdiff_t latency;
+	psched_tdiff_t jitter;
+
 	u32 loss;
 	u32 limit;
 	u32 counter;
 	u32 gap;
-	u32 jitter;
 	u32 duplicate;
 	u32 reorder;
 	u32 corrupt;
 
 	struct crndstate {
-		unsigned long last;
-		unsigned long rho;
+		u32 last;
+		u32 rho;
 	} delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor;
 
 	struct disttable {
@@ -95,12 +97,12 @@
  * Next number depends on last value.
  * rho is scaled to avoid floating point.
  */
-static unsigned long get_crandom(struct crndstate *state)
+static u32 get_crandom(struct crndstate *state)
 {
 	u64 value, rho;
 	unsigned long answer;
 
-	if (state->rho == 0)	/* no correllation */
+	if (state->rho == 0)	/* no correlation */
 		return net_random();
 
 	value = net_random();
@@ -114,11 +116,13 @@
  * std deviation sigma.  Uses table lookup to approximate the desired
  * distribution, and a uniformly-distributed pseudo-random source.
  */
-static long tabledist(unsigned long mu, long sigma,
-		      struct crndstate *state, const struct disttable *dist)
+static psched_tdiff_t tabledist(psched_tdiff_t mu, psched_tdiff_t sigma,
+				struct crndstate *state,
+				const struct disttable *dist)
 {
-	long t, x;
-	unsigned long rnd;
+	psched_tdiff_t x;
+	long t;
+	u32 rnd;
 
 	if (sigma == 0)
 		return mu;
@@ -213,8 +217,8 @@
 		delay = tabledist(q->latency, q->jitter,
 				  &q->delay_cor, q->delay_dist);
 
-		PSCHED_GET_TIME(now);
-		PSCHED_TADD2(now, delay, cb->time_to_send);
+		now = psched_get_time();
+		cb->time_to_send = now + delay;
 		++q->counter;
 		ret = q->qdisc->enqueue(skb, q->qdisc);
 	} else {
@@ -222,7 +226,7 @@
 		 * Do re-ordering by putting one out of N packets at the front
 		 * of the queue.
 		 */
-		PSCHED_GET_TIME(cb->time_to_send);
+		cb->time_to_send = psched_get_time();
 		q->counter = 0;
 		ret = q->qdisc->ops->requeue(skb, q->qdisc);
 	}
@@ -269,55 +273,43 @@
 	struct netem_sched_data *q = qdisc_priv(sch);
 	struct sk_buff *skb;
 
+	smp_mb();
+	if (sch->flags & TCQ_F_THROTTLED)
+		return NULL;
+
 	skb = q->qdisc->dequeue(q->qdisc);
 	if (skb) {
 		const struct netem_skb_cb *cb
 			= (const struct netem_skb_cb *)skb->cb;
-		psched_time_t now;
+		psched_time_t now = psched_get_time();
 
 		/* if more time remaining? */
-		PSCHED_GET_TIME(now);
-
-		if (PSCHED_TLESS(cb->time_to_send, now)) {
+		if (cb->time_to_send <= now) {
 			pr_debug("netem_dequeue: return skb=%p\n", skb);
 			sch->q.qlen--;
-			sch->flags &= ~TCQ_F_THROTTLED;
 			return skb;
-		} else {
-			psched_tdiff_t delay = PSCHED_TDIFF(cb->time_to_send, now);
-
-			if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) {
-				qdisc_tree_decrease_qlen(q->qdisc, 1);
-				sch->qstats.drops++;
-				printk(KERN_ERR "netem: queue discpline %s could not requeue\n",
-				       q->qdisc->ops->id);
-			}
-
-			mod_timer(&q->timer, jiffies + PSCHED_US2JIFFIE(delay));
-			sch->flags |= TCQ_F_THROTTLED;
 		}
+
+		if (unlikely(q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS)) {
+			qdisc_tree_decrease_qlen(q->qdisc, 1);
+			sch->qstats.drops++;
+			printk(KERN_ERR "netem: %s could not requeue\n",
+			       q->qdisc->ops->id);
+		}
+
+		qdisc_watchdog_schedule(&q->watchdog, cb->time_to_send);
 	}
 
 	return NULL;
 }
 
-static void netem_watchdog(unsigned long arg)
-{
-	struct Qdisc *sch = (struct Qdisc *)arg;
-
-	pr_debug("netem_watchdog qlen=%d\n", sch->q.qlen);
-	sch->flags &= ~TCQ_F_THROTTLED;
-	netif_schedule(sch->dev);
-}
-
 static void netem_reset(struct Qdisc *sch)
 {
 	struct netem_sched_data *q = qdisc_priv(sch);
 
 	qdisc_reset(q->qdisc);
 	sch->q.qlen = 0;
-	sch->flags &= ~TCQ_F_THROTTLED;
-	del_timer_sync(&q->timer);
+	qdisc_watchdog_cancel(&q->watchdog);
 }
 
 /* Pass size change message down to embedded FIFO */
@@ -438,10 +430,11 @@
 	q->loss = qopt->loss;
 	q->duplicate = qopt->duplicate;
 
-	/* for compatiablity with earlier versions.
-	 * if gap is set, need to assume 100% probablity
+	/* for compatibility with earlier versions.
+	 * if gap is set, need to assume 100% probability
 	 */
-	q->reorder = ~0;
+	if (q->gap)
+		q->reorder = ~0;
 
 	/* Handle nested options after initial queue options.
 	 * Should have put all options in nested format but too late now.
@@ -487,22 +480,28 @@
  */
 struct fifo_sched_data {
 	u32 limit;
+	psched_time_t oldest;
 };
 
 static int tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
 {
 	struct fifo_sched_data *q = qdisc_priv(sch);
 	struct sk_buff_head *list = &sch->q;
-	const struct netem_skb_cb *ncb
-		= (const struct netem_skb_cb *)nskb->cb;
+	psched_time_t tnext = ((struct netem_skb_cb *)nskb->cb)->time_to_send;
 	struct sk_buff *skb;
 
 	if (likely(skb_queue_len(list) < q->limit)) {
+		/* Optimize for add at tail */
+		if (likely(skb_queue_empty(list) || tnext >= q->oldest)) {
+			q->oldest = tnext;
+			return qdisc_enqueue_tail(nskb, sch);
+		}
+
 		skb_queue_reverse_walk(list, skb) {
 			const struct netem_skb_cb *cb
 				= (const struct netem_skb_cb *)skb->cb;
 
-			if (!PSCHED_TLESS(ncb->time_to_send, cb->time_to_send))
+			if (tnext >= cb->time_to_send)
 				break;
 		}
 
@@ -515,7 +514,7 @@
 		return NET_XMIT_SUCCESS;
 	}
 
-	return qdisc_drop(nskb, sch);
+	return qdisc_reshape_fail(nskb, sch);
 }
 
 static int tfifo_init(struct Qdisc *sch, struct rtattr *opt)
@@ -531,6 +530,7 @@
 	} else
 		q->limit = max_t(u32, sch->dev->tx_queue_len, 1);
 
+	q->oldest = PSCHED_PASTPERFECT;
 	return 0;
 }
 
@@ -567,9 +567,7 @@
 	if (!opt)
 		return -EINVAL;
 
-	init_timer(&q->timer);
-	q->timer.function = netem_watchdog;
-	q->timer.data = (unsigned long) sch;
+	qdisc_watchdog_init(&q->watchdog, sch);
 
 	q->qdisc = qdisc_create_dflt(sch->dev, &tfifo_qdisc_ops,
 				     TC_H_MAKE(sch->handle, 1));
@@ -590,7 +588,7 @@
 {
 	struct netem_sched_data *q = qdisc_priv(sch);
 
-	del_timer_sync(&q->timer);
+	qdisc_watchdog_cancel(&q->watchdog);
 	qdisc_destroy(q->qdisc);
 	kfree(q->delay_dist);
 }
@@ -598,7 +596,7 @@
 static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
 {
 	const struct netem_sched_data *q = qdisc_priv(sch);
-	unsigned char	 *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct rtattr *rta = (struct rtattr *) b;
 	struct tc_netem_qopt qopt;
 	struct tc_netem_corr cor;
@@ -626,12 +624,12 @@
 	corrupt.correlation = q->corrupt_cor.rho;
 	RTA_PUT(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
 
-	rta->rta_len = skb->tail - b;
+	rta->rta_len = skb_tail_pointer(skb) - b;
 
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c
index de889f2..269a6e1 100644
--- a/net/sched/sch_prio.c
+++ b/net/sched/sch_prio.c
@@ -32,6 +32,7 @@
 #include <net/ip.h>
 #include <net/route.h>
 #include <linux/skbuff.h>
+#include <net/netlink.h>
 #include <net/sock.h>
 #include <net/pkt_sched.h>
 
@@ -61,7 +62,7 @@
 			*qerr = NET_XMIT_SUCCESS;
 		case TC_ACT_SHOT:
 			return NULL;
-		};
+		}
 
 		if (!q->filter_list ) {
 #else
@@ -188,13 +189,8 @@
 {
 	int prio;
 	struct prio_sched_data *q = qdisc_priv(sch);
-	struct tcf_proto *tp;
 
-	while ((tp = q->filter_list) != NULL) {
-		q->filter_list = tp->next;
-		tcf_destroy(tp);
-	}
-
+	tcf_destroy_chain(q->filter_list);
 	for (prio=0; prio<q->bands; prio++)
 		qdisc_destroy(q->queues[prio]);
 }
@@ -271,7 +267,7 @@
 static int prio_dump(struct Qdisc *sch, struct sk_buff *skb)
 {
 	struct prio_sched_data *q = qdisc_priv(sch);
-	unsigned char	 *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct tc_prio_qopt opt;
 
 	opt.bands = q->bands;
@@ -280,7 +276,7 @@
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c
index 66f3205..96dfdf7 100644
--- a/net/sched/sch_sfq.c
+++ b/net/sched/sch_sfq.c
@@ -30,6 +30,7 @@
 #include <linux/notifier.h>
 #include <linux/init.h>
 #include <net/ip.h>
+#include <net/netlink.h>
 #include <linux/ipv6.h>
 #include <net/route.h>
 #include <linux/skbuff.h>
@@ -137,7 +138,7 @@
 	switch (skb->protocol) {
 	case __constant_htons(ETH_P_IP):
 	{
-		struct iphdr *iph = skb->nh.iph;
+		const struct iphdr *iph = ip_hdr(skb);
 		h = iph->daddr;
 		h2 = iph->saddr^iph->protocol;
 		if (!(iph->frag_off&htons(IP_MF|IP_OFFSET)) &&
@@ -152,7 +153,7 @@
 	}
 	case __constant_htons(ETH_P_IPV6):
 	{
-		struct ipv6hdr *iph = skb->nh.ipv6h;
+		struct ipv6hdr *iph = ipv6_hdr(skb);
 		h = iph->daddr.s6_addr32[3];
 		h2 = iph->saddr.s6_addr32[3]^iph->nexthdr;
 		if (iph->nexthdr == IPPROTO_TCP ||
@@ -461,7 +462,7 @@
 static int sfq_dump(struct Qdisc *sch, struct sk_buff *skb)
 {
 	struct sfq_sched_data *q = qdisc_priv(sch);
-	unsigned char	 *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct tc_sfq_qopt opt;
 
 	opt.quantum = q->quantum;
@@ -476,7 +477,7 @@
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c
index 85da8da..5386295 100644
--- a/net/sched/sch_tbf.c
+++ b/net/sched/sch_tbf.c
@@ -32,6 +32,7 @@
 #include <linux/etherdevice.h>
 #include <linux/notifier.h>
 #include <net/ip.h>
+#include <net/netlink.h>
 #include <net/route.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
@@ -127,8 +128,8 @@
 	long	tokens;			/* Current number of B tokens */
 	long	ptokens;		/* Current number of P tokens */
 	psched_time_t	t_c;		/* Time check-point */
-	struct timer_list wd_timer;	/* Watchdog timer */
 	struct Qdisc	*qdisc;		/* Inner qdisc, default - bfifo queue */
+	struct qdisc_watchdog watchdog;	/* Watchdog timer */
 };
 
 #define L2T(q,L)   ((q)->R_tab->data[(L)>>(q)->R_tab->rate.cell_log])
@@ -185,14 +186,6 @@
 	return len;
 }
 
-static void tbf_watchdog(unsigned long arg)
-{
-	struct Qdisc *sch = (struct Qdisc*)arg;
-
-	sch->flags &= ~TCQ_F_THROTTLED;
-	netif_schedule(sch->dev);
-}
-
 static struct sk_buff *tbf_dequeue(struct Qdisc* sch)
 {
 	struct tbf_sched_data *q = qdisc_priv(sch);
@@ -202,13 +195,12 @@
 
 	if (skb) {
 		psched_time_t now;
-		long toks, delay;
+		long toks;
 		long ptoks = 0;
 		unsigned int len = skb->len;
 
-		PSCHED_GET_TIME(now);
-
-		toks = PSCHED_TDIFF_SAFE(now, q->t_c, q->buffer);
+		now = psched_get_time();
+		toks = psched_tdiff_bounded(now, q->t_c, q->buffer);
 
 		if (q->P_tab) {
 			ptoks = toks + q->ptokens;
@@ -230,12 +222,8 @@
 			return skb;
 		}
 
-		delay = PSCHED_US2JIFFIE(max_t(long, -toks, -ptoks));
-
-		if (delay == 0)
-			delay = 1;
-
-		mod_timer(&q->wd_timer, jiffies+delay);
+		qdisc_watchdog_schedule(&q->watchdog,
+					now + max_t(long, -toks, -ptoks));
 
 		/* Maybe we have a shorter packet in the queue,
 		   which can be sent now. It sounds cool,
@@ -254,7 +242,6 @@
 			sch->qstats.drops++;
 		}
 
-		sch->flags |= TCQ_F_THROTTLED;
 		sch->qstats.overlimits++;
 	}
 	return NULL;
@@ -266,11 +253,10 @@
 
 	qdisc_reset(q->qdisc);
 	sch->q.qlen = 0;
-	PSCHED_GET_TIME(q->t_c);
+	q->t_c = psched_get_time();
 	q->tokens = q->buffer;
 	q->ptokens = q->mtu;
-	sch->flags &= ~TCQ_F_THROTTLED;
-	del_timer(&q->wd_timer);
+	qdisc_watchdog_cancel(&q->watchdog);
 }
 
 static struct Qdisc *tbf_create_dflt_qdisc(struct Qdisc *sch, u32 limit)
@@ -377,11 +363,8 @@
 	if (opt == NULL)
 		return -EINVAL;
 
-	PSCHED_GET_TIME(q->t_c);
-	init_timer(&q->wd_timer);
-	q->wd_timer.function = tbf_watchdog;
-	q->wd_timer.data = (unsigned long)sch;
-
+	q->t_c = psched_get_time();
+	qdisc_watchdog_init(&q->watchdog, sch);
 	q->qdisc = &noop_qdisc;
 
 	return tbf_change(sch, opt);
@@ -391,7 +374,7 @@
 {
 	struct tbf_sched_data *q = qdisc_priv(sch);
 
-	del_timer(&q->wd_timer);
+	qdisc_watchdog_cancel(&q->watchdog);
 
 	if (q->P_tab)
 		qdisc_put_rtab(q->P_tab);
@@ -404,7 +387,7 @@
 static int tbf_dump(struct Qdisc *sch, struct sk_buff *skb)
 {
 	struct tbf_sched_data *q = qdisc_priv(sch);
-	unsigned char	 *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	struct rtattr *rta;
 	struct tc_tbf_qopt opt;
 
@@ -420,12 +403,12 @@
 	opt.mtu = q->mtu;
 	opt.buffer = q->buffer;
 	RTA_PUT(skb, TCA_TBF_PARMS, sizeof(opt), &opt);
-	rta->rta_len = skb->tail - b;
+	rta->rta_len = skb_tail_pointer(skb) - b;
 
 	return skb->len;
 
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
index 587123c..d24914d 100644
--- a/net/sched/sch_teql.c
+++ b/net/sched/sch_teql.c
@@ -323,7 +323,7 @@
 			nores = 1;
 			break;
 		}
-		__skb_pull(skb, skb->nh.raw - skb->data);
+		__skb_pull(skb, skb_network_offset(skb));
 	} while ((q = NEXT_SLAVE(q)) != start);
 
 	if (nores && skb_res == NULL) {
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 78d2ddb..db73ef9 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -143,7 +143,7 @@
 	/* Initialize the maximum mumber of new data packets that can be sent
 	 * in a burst.
 	 */
-	asoc->max_burst = sctp_max_burst;
+	asoc->max_burst = sp->max_burst;
 
 	/* initialize association timers */
 	asoc->timeouts[SCTP_EVENT_TIMEOUT_NONE] = 0;
@@ -714,8 +714,16 @@
 	/* Record the transition on the transport.  */
 	switch (command) {
 	case SCTP_TRANSPORT_UP:
+		/* If we are moving from UNCONFIRMED state due
+		 * to heartbeat success, report the SCTP_ADDR_CONFIRMED
+		 * state to the user, otherwise report SCTP_ADDR_AVAILABLE.
+		 */
+		if (SCTP_UNCONFIRMED == transport->state &&
+		    SCTP_HEARTBEAT_SUCCESS == error)
+			spc_state = SCTP_ADDR_CONFIRMED;
+		else
+			spc_state = SCTP_ADDR_AVAILABLE;
 		transport->state = SCTP_ACTIVE;
-		spc_state = SCTP_ADDR_AVAILABLE;
 		break;
 
 	case SCTP_TRANSPORT_DOWN:
@@ -725,7 +733,7 @@
 
 	default:
 		return;
-	};
+	}
 
 	/* Generate and send a SCTP_PEER_ADDR_CHANGE notification to the
 	 * user.
diff --git a/net/sctp/debug.c b/net/sctp/debug.c
index 5f5ab28..e8c0f74 100644
--- a/net/sctp/debug.c
+++ b/net/sctp/debug.c
@@ -93,8 +93,9 @@
 		return "FWD_TSN";
 
 	default:
-		return "unknown chunk";
-	};
+		break;
+	}
+
 	return "unknown chunk";
 }
 
diff --git a/net/sctp/input.c b/net/sctp/input.c
index 71db668..885109f 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -79,14 +79,10 @@
 /* Calculate the SCTP checksum of an SCTP packet.  */
 static inline int sctp_rcv_checksum(struct sk_buff *skb)
 {
-	struct sctphdr *sh;
-	__u32 cmp, val;
 	struct sk_buff *list = skb_shinfo(skb)->frag_list;
-
-	sh = (struct sctphdr *) skb->h.raw;
-	cmp = ntohl(sh->checksum);
-
-	val = sctp_start_cksum((__u8 *)sh, skb_headlen(skb));
+	struct sctphdr *sh = sctp_hdr(skb);
+	__u32 cmp = ntohl(sh->checksum);
+	__u32 val = sctp_start_cksum((__u8 *)sh, skb_headlen(skb));
 
 	for (; list; list = list->next)
 		val = sctp_update_cksum((__u8 *)list->data, skb_headlen(list),
@@ -138,14 +134,13 @@
 	if (skb_linearize(skb))
 		goto discard_it;
 
-	sh = (struct sctphdr *) skb->h.raw;
+	sh = sctp_hdr(skb);
 
 	/* Pull up the IP and SCTP headers. */
-	__skb_pull(skb, skb->h.raw - skb->data);
+	__skb_pull(skb, skb_transport_offset(skb));
 	if (skb->len < sizeof(struct sctphdr))
 		goto discard_it;
-	if ((skb->ip_summed != CHECKSUM_UNNECESSARY) &&
-	    (sctp_rcv_checksum(skb) < 0))
+	if (!skb_csum_unnecessary(skb) && sctp_rcv_checksum(skb) < 0)
 		goto discard_it;
 
 	skb_pull(skb, sizeof(struct sctphdr));
@@ -154,7 +149,7 @@
 	if (skb->len < sizeof(struct sctp_chunkhdr))
 		goto discard_it;
 
-	family = ipver2af(skb->nh.iph->version);
+	family = ipver2af(ip_hdr(skb)->version);
 	af = sctp_get_af_specific(family);
 	if (unlikely(!af))
 		goto discard_it;
@@ -510,30 +505,30 @@
 void sctp_v4_err(struct sk_buff *skb, __u32 info)
 {
 	struct iphdr *iph = (struct iphdr *)skb->data;
-	struct sctphdr *sh = (struct sctphdr *)(skb->data + (iph->ihl <<2));
-	int type = skb->h.icmph->type;
-	int code = skb->h.icmph->code;
+	const int ihlen = iph->ihl * 4;
+	const int type = icmp_hdr(skb)->type;
+	const int code = icmp_hdr(skb)->code;
 	struct sock *sk;
 	struct sctp_association *asoc = NULL;
 	struct sctp_transport *transport;
 	struct inet_sock *inet;
-	char *saveip, *savesctp;
+	sk_buff_data_t saveip, savesctp;
 	int err;
 
-	if (skb->len < ((iph->ihl << 2) + 8)) {
+	if (skb->len < ihlen + 8) {
 		ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
 		return;
 	}
 
 	/* Fix up skb to look at the embedded net header. */
-	saveip = skb->nh.raw;
-	savesctp  = skb->h.raw;
-	skb->nh.iph = iph;
-	skb->h.raw = (char *)sh;
-	sk = sctp_err_lookup(AF_INET, skb, sh, &asoc, &transport);
-	/* Put back, the original pointers. */
-	skb->nh.raw = saveip;
-	skb->h.raw = savesctp;
+	saveip = skb->network_header;
+	savesctp = skb->transport_header;
+	skb_reset_network_header(skb);
+	skb_set_transport_header(skb, ihlen);
+	sk = sctp_err_lookup(AF_INET, skb, sctp_hdr(skb), &asoc, &transport);
+	/* Put back, the original values. */
+	skb->network_header = saveip;
+	skb->transport_header = savesctp;
 	if (!sk) {
 		ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
 		return;
@@ -616,7 +611,7 @@
 			break;
 
 		ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length));
-		if (ch_end > skb->tail)
+		if (ch_end > skb_tail_pointer(skb))
 			break;
 
 		/* RFC 8.4, 2) If the OOTB packet contains an ABORT chunk, the
@@ -648,7 +643,7 @@
 		}
 
 		ch = (sctp_chunkhdr_t *) ch_end;
-	} while (ch_end < skb->tail);
+	} while (ch_end < skb_tail_pointer(skb));
 
 	return 0;
 
@@ -905,7 +900,7 @@
 	struct sctp_association *asoc;
 	union sctp_addr addr;
 	union sctp_addr *paddr = &addr;
-	struct sctphdr *sh = (struct sctphdr *) skb->h.raw;
+	struct sctphdr *sh = sctp_hdr(skb);
 	sctp_chunkhdr_t *ch;
 	union sctp_params params;
 	sctp_init_chunk_t *init;
diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c
index c30629e..88aa224 100644
--- a/net/sctp/inqueue.c
+++ b/net/sctp/inqueue.c
@@ -159,16 +159,16 @@
 	 * the skb->tail.
 	 */
 	if (unlikely(skb_is_nonlinear(chunk->skb))) {
-		if (chunk->chunk_end > chunk->skb->tail)
-			chunk->chunk_end = chunk->skb->tail;
+		if (chunk->chunk_end > skb_tail_pointer(chunk->skb))
+			chunk->chunk_end = skb_tail_pointer(chunk->skb);
 	}
 	skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t));
 	chunk->subh.v = NULL; /* Subheader is no longer valid.  */
 
-	if (chunk->chunk_end < chunk->skb->tail) {
+	if (chunk->chunk_end < skb_tail_pointer(chunk->skb)) {
 		/* This is not a singleton */
 		chunk->singleton = 0;
-	} else if (chunk->chunk_end > chunk->skb->tail) {
+	} else if (chunk->chunk_end > skb_tail_pointer(chunk->skb)) {
 		/* RFC 2960, Section 6.10  Bundling
 		 *
 		 * Partial chunks MUST NOT be placed in an SCTP packet.
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 0b9c49b..ca527a2 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -122,26 +122,24 @@
 			     int type, int code, int offset, __be32 info)
 {
 	struct inet6_dev *idev;
-	struct ipv6hdr *iph = (struct ipv6hdr *)skb->data;
-	struct sctphdr *sh = (struct sctphdr *)(skb->data + offset);
 	struct sock *sk;
 	struct sctp_association *asoc;
 	struct sctp_transport *transport;
 	struct ipv6_pinfo *np;
-	char *saveip, *savesctp;
+	sk_buff_data_t saveip, savesctp;
 	int err;
 
 	idev = in6_dev_get(skb->dev);
 
 	/* Fix up skb to look at the embedded net header. */
-	saveip = skb->nh.raw;
-	savesctp  = skb->h.raw;
-	skb->nh.ipv6h = iph;
-	skb->h.raw = (char *)sh;
-	sk = sctp_err_lookup(AF_INET6, skb, sh, &asoc, &transport);
+	saveip	 = skb->network_header;
+	savesctp = skb->transport_header;
+	skb_reset_network_header(skb);
+	skb_set_transport_header(skb, offset);
+	sk = sctp_err_lookup(AF_INET6, skb, sctp_hdr(skb), &asoc, &transport);
 	/* Put back, the original pointers. */
-	skb->nh.raw = saveip;
-	skb->h.raw = savesctp;
+	skb->network_header   = saveip;
+	skb->transport_header = savesctp;
 	if (!sk) {
 		ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INERRORS);
 		goto out;
@@ -391,13 +389,13 @@
 	addr->v6.sin6_flowinfo = 0; /* FIXME */
 	addr->v6.sin6_scope_id = ((struct inet6_skb_parm *)skb->cb)->iif;
 
-	sh = (struct sctphdr *) skb->h.raw;
+	sh = sctp_hdr(skb);
 	if (is_saddr) {
 		*port  = sh->source;
-		from = &skb->nh.ipv6h->saddr;
+		from = &ipv6_hdr(skb)->saddr;
 	} else {
 		*port = sh->dest;
-		from = &skb->nh.ipv6h->daddr;
+		from = &ipv6_hdr(skb)->daddr;
 	}
 	ipv6_addr_copy(&addr->v6.sin6_addr, from);
 }
@@ -606,7 +604,7 @@
 	default:
 		retval = SCTP_SCOPE_GLOBAL;
 		break;
-	};
+	}
 
 	return retval;
 }
@@ -699,7 +697,7 @@
 /* Was this packet marked by Explicit Congestion Notification? */
 static int sctp_v6_is_ce(const struct sk_buff *skb)
 {
-	return *((__u32 *)(skb->nh.ipv6h)) & htonl(1<<20);
+	return *((__u32 *)(ipv6_hdr(skb))) & htonl(1 << 20);
 }
 
 /* Dump the v6 addr to the seq file. */
@@ -766,19 +764,19 @@
 	if (msgname) {
 		sctp_inet6_msgname(msgname, addr_len);
 		sin6 = (struct sockaddr_in6 *)msgname;
-		sh = (struct sctphdr *)skb->h.raw;
+		sh = sctp_hdr(skb);
 		sin6->sin6_port = sh->source;
 
 		/* Map ipv4 address into v4-mapped-on-v6 address. */
 		if (sctp_sk(skb->sk)->v4mapped &&
-		    skb->nh.iph->version == 4) {
+		    ip_hdr(skb)->version == 4) {
 			sctp_v4_map_v6((union sctp_addr *)sin6);
-			sin6->sin6_addr.s6_addr32[3] = skb->nh.iph->saddr;
+			sin6->sin6_addr.s6_addr32[3] = ip_hdr(skb)->saddr;
 			return;
 		}
 
 		/* Otherwise, just copy the v6 address. */
-		ipv6_addr_copy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr);
+		ipv6_addr_copy(&sin6->sin6_addr, &ipv6_hdr(skb)->saddr);
 		if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) {
 			struct sctp_ulpevent *ev = sctp_skb2event(skb);
 			sin6->sin6_scope_id = ev->iif;
diff --git a/net/sctp/output.c b/net/sctp/output.c
index f875fc3..d85543d 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -176,7 +176,7 @@
 	case SCTP_XMIT_OK:
 	case SCTP_XMIT_NAGLE_DELAY:
 		break;
-	};
+	}
 
 	return retval;
 }
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
index 41abfd1..992f361 100644
--- a/net/sctp/outqueue.c
+++ b/net/sctp/outqueue.c
@@ -338,7 +338,7 @@
 				SCTP_INC_STATS(SCTP_MIB_OUTORDERCHUNKS);
 			q->empty = 0;
 			break;
-		};
+		}
 	} else {
 		list_add_tail(&chunk->list, &q->control_chunk_list);
 		SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
@@ -630,7 +630,7 @@
 			/* Retrieve a new chunk to bundle. */
 			lchunk = sctp_list_dequeue(lqueue);
 			break;
-		};
+		}
 
 		/* If we are here due to a retransmit timeout or a fast
 		 * retransmit and if there are any chunks left in the retransmit
@@ -779,7 +779,7 @@
 		default:
 			/* We built a chunk with an illegal type! */
 			BUG();
-		};
+		}
 	}
 
 	/* Is it OK to send data chunks?  */
@@ -1397,7 +1397,7 @@
 				SCTP_DEBUG_PRINTK("ACKed: %08x", tsn);
 				dbg_prt_state = 0;
 				dbg_ack_tsn = tsn;
-			};
+			}
 
 			dbg_last_ack_tsn = tsn;
 #endif /* SCTP_DEBUG */
@@ -1452,7 +1452,7 @@
 				SCTP_DEBUG_PRINTK("KEPT: %08x",tsn);
 				dbg_prt_state = 1;
 				dbg_kept_tsn = tsn;
-			};
+			}
 
 			dbg_last_kept_tsn = tsn;
 #endif /* SCTP_DEBUG */
@@ -1476,7 +1476,7 @@
 		} else {
 			SCTP_DEBUG_PRINTK("\n");
 		}
-	};
+	}
 #endif /* SCTP_DEBUG */
 	if (transport) {
 		if (bytes_acked) {
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index e17a823..c361deb 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -235,13 +235,13 @@
 	port = &addr->v4.sin_port;
 	addr->v4.sin_family = AF_INET;
 
-	sh = (struct sctphdr *) skb->h.raw;
+	sh = sctp_hdr(skb);
 	if (is_saddr) {
 		*port  = sh->source;
-		from = &skb->nh.iph->saddr;
+		from = &ip_hdr(skb)->saddr;
 	} else {
 		*port = sh->dest;
-		from = &skb->nh.iph->daddr;
+		from = &ip_hdr(skb)->daddr;
 	}
 	memcpy(&addr->v4.sin_addr.s_addr, from, sizeof(struct in_addr));
 }
@@ -530,7 +530,7 @@
 /* Was this packet marked by Explicit Congestion Notification? */
 static int sctp_v4_is_ce(const struct sk_buff *skb)
 {
-	return INET_ECN_is_ce(skb->nh.iph->tos);
+	return INET_ECN_is_ce(ip_hdr(skb)->tos);
 }
 
 /* Create and initialize a new sk for the socket returned by accept(). */
@@ -731,15 +731,13 @@
 /* Initialize and copy out a msgname from an inbound skb. */
 static void sctp_inet_skb_msgname(struct sk_buff *skb, char *msgname, int *len)
 {
-	struct sctphdr *sh;
-	struct sockaddr_in *sin;
-
 	if (msgname) {
+		struct sctphdr *sh = sctp_hdr(skb);
+		struct sockaddr_in *sin = (struct sockaddr_in *)msgname;
+
 		sctp_inet_msgname(msgname, len);
-		sin = (struct sockaddr_in *)msgname;
-		sh = (struct sctphdr *)skb->h.raw;
 		sin->sin_port = sh->source;
-		sin->sin_addr.s_addr = skb->nh.iph->saddr;
+		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
 	}
 }
 
@@ -1044,7 +1042,7 @@
 	sctp_cookie_preserve_enable 	= 1;
 
 	/* Max.Burst		    - 4 */
-	sctp_max_burst 			= SCTP_MAX_BURST;
+	sctp_max_burst 			= SCTP_DEFAULT_MAX_BURST;
 
 	/* Association.Max.Retrans  - 10 attempts
 	 * Path.Max.Retrans         - 5  attempts (per destination address)
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index f7fb29d..be783a3 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -86,7 +86,7 @@
 	struct sctp_af *af;
 	int iif = 0;
 
-	af = sctp_get_af_specific(ipver2af(chunk->skb->nh.iph->version));
+	af = sctp_get_af_specific(ipver2af(ip_hdr(chunk->skb)->version));
 	if (af)
 		iif = af->skb_iif(chunk->skb);
 
@@ -1143,7 +1143,7 @@
 
 	/* Adjust the chunk length field.  */
 	chunk->chunk_hdr->length = htons(chunklen + padlen + len);
-	chunk->chunk_end = chunk->skb->tail;
+	chunk->chunk_end = skb_tail_pointer(chunk->skb);
 
 	return target;
 }
@@ -1168,7 +1168,7 @@
 	/* Adjust the chunk length field.  */
 	chunk->chunk_hdr->length =
 		htons(ntohs(chunk->chunk_hdr->length) + len);
-	chunk->chunk_end = chunk->skb->tail;
+	chunk->chunk_end = skb_tail_pointer(chunk->skb);
 
 out:
 	return err;
@@ -1233,7 +1233,7 @@
 	asoc->temp = 1;
 	skb = chunk->skb;
 	/* Create an entry for the source address of the packet.  */
-	af = sctp_get_af_specific(ipver2af(skb->nh.iph->version));
+	af = sctp_get_af_specific(ipver2af(ip_hdr(skb)->version));
 	if (unlikely(!af))
 		goto fail;
 	af->from_skb(&asoc->c.peer_addr, skb, 1);
@@ -2077,7 +2077,7 @@
 
 			default: /* Just ignore anything else.  */
 				break;
-			};
+			}
 		}
 		break;
 
@@ -2118,7 +2118,7 @@
 		SCTP_DEBUG_PRINTK("Ignoring param: %d for association %p.\n",
 				  ntohs(param.p->type), asoc);
 		break;
-	};
+	}
 
 	return retval;
 }
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index 1355674..b37a7ad 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -464,7 +464,7 @@
 	struct sctp_ulpevent *event;
 
 	event = sctp_ulpevent_make_assoc_change(asoc,0, SCTP_CANT_STR_ASSOC,
-						(__u16)error, 0, 0,
+						(__u16)error, 0, 0, NULL,
 						GFP_ATOMIC);
 
 	if (event)
@@ -492,8 +492,13 @@
 	/* Cancel any partial delivery in progress. */
 	sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC);
 
-	event = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_LOST,
-						(__u16)error, 0, 0,
+	if (event_type == SCTP_EVENT_T_CHUNK && subtype.chunk == SCTP_CID_ABORT)
+		event = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_LOST,
+						(__u16)error, 0, 0, chunk,
+						GFP_ATOMIC);
+	else
+		event = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_LOST,
+						(__u16)error, 0, 0, NULL,
 						GFP_ATOMIC);
 	if (event)
 		sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP,
@@ -1004,7 +1009,7 @@
 		       status, state, event_type, subtype.chunk);
 		BUG();
 		break;
-	};
+	}
 
 bail:
 	return error;
@@ -1484,7 +1489,8 @@
 			printk(KERN_WARNING "Impossible command: %u, %p\n",
 			       cmd->verb, cmd->obj.ptr);
 			break;
-		};
+		}
+
 		if (error)
 			break;
 	}
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index e9097cf..9e28a5d 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -186,7 +186,7 @@
 	 * notification is passed to the upper layer.
 	 */
 	ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_SHUTDOWN_COMP,
-					     0, 0, 0, GFP_ATOMIC);
+					     0, 0, 0, NULL, GFP_ATOMIC);
 	if (ev)
 		sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP,
 				SCTP_ULPEVENT(ev));
@@ -629,7 +629,7 @@
 		case -SCTP_IERROR_BAD_SIG:
 		default:
 			return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
-		};
+		}
 	}
 
 
@@ -661,7 +661,7 @@
 	ev = sctp_ulpevent_make_assoc_change(new_asoc, 0, SCTP_COMM_UP, 0,
 					     new_asoc->c.sinit_num_ostreams,
 					     new_asoc->c.sinit_max_instreams,
-					     GFP_ATOMIC);
+					     NULL, GFP_ATOMIC);
 	if (!ev)
 		goto nomem_ev;
 
@@ -790,7 +790,7 @@
 	ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_UP,
 					     0, asoc->c.sinit_num_ostreams,
 					     asoc->c.sinit_max_instreams,
-					     GFP_ATOMIC);
+					     NULL, GFP_ATOMIC);
 
 	if (!ev)
 		goto nomem;
@@ -1195,7 +1195,7 @@
 		new_asoc->c.my_ttag   = asoc->c.my_vtag;
 		new_asoc->c.peer_ttag = asoc->c.peer_vtag;
 		break;
-	};
+	}
 
 	/* Other parameters for the endpoint SHOULD be copied from the
 	 * existing parameters of the association (e.g. number of
@@ -1625,7 +1625,7 @@
 	ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_RESTART, 0,
 					     new_asoc->c.sinit_num_ostreams,
 					     new_asoc->c.sinit_max_instreams,
-					     GFP_ATOMIC);
+					     NULL, GFP_ATOMIC);
 	if (!ev)
 		goto nomem_ev;
 
@@ -1691,7 +1691,7 @@
 	ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_UP, 0,
 					     new_asoc->c.sinit_num_ostreams,
 					     new_asoc->c.sinit_max_instreams,
-					     GFP_ATOMIC);
+					     NULL, GFP_ATOMIC);
 	if (!ev)
 		goto nomem_ev;
 
@@ -1786,7 +1786,7 @@
 					     SCTP_COMM_UP, 0,
 					     asoc->c.sinit_num_ostreams,
 					     asoc->c.sinit_max_instreams,
-					     GFP_ATOMIC);
+                                             NULL, GFP_ATOMIC);
 		if (!ev)
 			goto nomem;
 
@@ -1904,7 +1904,7 @@
 		case -SCTP_IERROR_BAD_SIG:
 		default:
 			return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
-		};
+		}
 	}
 
 	/* Compare the tie_tag in cookie with the verification tag of
@@ -1936,7 +1936,7 @@
 	default: /* Discard packet for all others. */
 		retval = sctp_sf_pdiscard(ep, asoc, type, arg, commands);
 		break;
-	};
+	}
 
 	/* Delete the tempory new association. */
 	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc));
@@ -3035,7 +3035,7 @@
 	 * notification is passed to the upper layer.
 	 */
 	ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_SHUTDOWN_COMP,
-					     0, 0, 0, GFP_ATOMIC);
+					     0, 0, 0, NULL, GFP_ATOMIC);
 	if (!ev)
 		goto nomem;
 
@@ -3115,7 +3115,7 @@
 			break;
 
 		ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length));
-		if (ch_end > skb->tail)
+		if (ch_end > skb_tail_pointer(skb))
 			break;
 
 		if (SCTP_CID_SHUTDOWN_ACK == ch->type)
@@ -3130,7 +3130,7 @@
 			return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
 
 		ch = (sctp_chunkhdr_t *) ch_end;
-	} while (ch_end < skb->tail);
+	} while (ch_end < skb_tail_pointer(skb));
 
 	if (ootb_shut_ack)
 		sctp_sf_shut_8_4_5(ep, asoc, type, arg, commands);
@@ -4816,7 +4816,7 @@
 	default:
 		BUG();
 		break;
-	};
+	}
 
 	if (!reply)
 		goto nomem;
@@ -5286,7 +5286,7 @@
 		chunk->ecn_ce_done = 1;
 
 		af = sctp_get_af_specific(
-			ipver2af(chunk->skb->nh.iph->version));
+			ipver2af(ip_hdr(chunk->skb)->version));
 
 		if (af && af->is_ce(chunk->skb) && asoc->peer.ecn_capable) {
 			/* Do real work as sideffect. */
diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c
index 5e54b17..523071c 100644
--- a/net/sctp/sm_statetable.c
+++ b/net/sctp/sm_statetable.c
@@ -101,7 +101,7 @@
 	default:
 		/* Yikes!  We got an illegal event type.  */
 		return &bug;
-	};
+	}
 }
 
 #define TYPE_SCTP_FUNC(func) {.fn = func, .name = #func}
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index a1d026f..2fc0a92 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -941,7 +941,7 @@
 	default:
 		err = -EINVAL;
 		break;
-	};
+	}
 
 out:
 	kfree(kaddrs);
@@ -2039,6 +2039,10 @@
  *                     SPP_HB_DEMAND - Request a user initiated heartbeat
  *                     to be made immediately.
  *
+ *                     SPP_HB_TIME_IS_ZERO - Specify's that the time for
+ *                     heartbeat delayis to be set to the value of 0
+ *                     milliseconds.
+ *
  *                     SPP_PMTUD_ENABLE - This field will enable PMTU
  *                     discovery upon the specified address. Note that
  *                     if the address feild is empty then all addresses
@@ -2081,13 +2085,30 @@
 			return error;
 	}
 
-	if (params->spp_hbinterval) {
-		if (trans) {
-			trans->hbinterval = msecs_to_jiffies(params->spp_hbinterval);
-		} else if (asoc) {
-			asoc->hbinterval = msecs_to_jiffies(params->spp_hbinterval);
-		} else {
-			sp->hbinterval = params->spp_hbinterval;
+	/* Note that unless the spp_flag is set to SPP_HB_ENABLE the value of
+	 * this field is ignored.  Note also that a value of zero indicates
+	 * the current setting should be left unchanged.
+	 */
+	if (params->spp_flags & SPP_HB_ENABLE) {
+
+		/* Re-zero the interval if the SPP_HB_TIME_IS_ZERO is
+		 * set.  This lets us use 0 value when this flag
+		 * is set.
+		 */
+		if (params->spp_flags & SPP_HB_TIME_IS_ZERO)
+			params->spp_hbinterval = 0;
+
+		if (params->spp_hbinterval ||
+		    (params->spp_flags & SPP_HB_TIME_IS_ZERO)) {
+			if (trans) {
+				trans->hbinterval =
+				    msecs_to_jiffies(params->spp_hbinterval);
+			} else if (asoc) {
+				asoc->hbinterval =
+				    msecs_to_jiffies(params->spp_hbinterval);
+			} else {
+				sp->hbinterval = params->spp_hbinterval;
+			}
 		}
 	}
 
@@ -2104,7 +2125,12 @@
 		}
 	}
 
-	if (params->spp_pathmtu) {
+	/* When Path MTU discovery is disabled the value specified here will
+	 * be the "fixed" path mtu (i.e. the value of the spp_flags field must
+	 * include the flag SPP_PMTUD_DISABLE for this field to have any
+	 * effect).
+	 */
+	if ((params->spp_flags & SPP_PMTUD_DISABLE) && params->spp_pathmtu) {
 		if (trans) {
 			trans->pathmtu = params->spp_pathmtu;
 			sctp_assoc_sync_pmtu(asoc);
@@ -2135,7 +2161,11 @@
 		}
 	}
 
-	if (params->spp_sackdelay) {
+	/* Note that unless the spp_flag is set to SPP_SACKDELAY_ENABLE the
+	 * value of this field is ignored.  Note also that a value of zero
+	 * indicates the current setting should be left unchanged.
+	 */
+	if ((params->spp_flags & SPP_SACKDELAY_ENABLE) && params->spp_sackdelay) {
 		if (trans) {
 			trans->sackdelay =
 				msecs_to_jiffies(params->spp_sackdelay);
@@ -2163,7 +2193,11 @@
 		}
 	}
 
-	if (params->spp_pathmaxrxt) {
+	/* Note that unless the spp_flag is set to SPP_PMTUD_ENABLE the value
+	 * of this field is ignored.  Note also that a value of zero
+	 * indicates the current setting should be left unchanged.
+	 */
+	if ((params->spp_flags & SPP_PMTUD_ENABLE) && params->spp_pathmaxrxt) {
 		if (trans) {
 			trans->pathmaxrxt = params->spp_pathmaxrxt;
 		} else if (asoc) {
@@ -2255,7 +2289,7 @@
 	return 0;
 }
 
-/* 7.1.24. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME)
+/* 7.1.23. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME)
  *
  *   This options will get or set the delayed ack timer.  The time is set
  *   in milliseconds.  If the assoc_id is 0, then this sets or gets the
@@ -2792,6 +2826,102 @@
 	return 0;
 }
 
+/*
+ * 7.1.24.  Get or set fragmented interleave (SCTP_FRAGMENT_INTERLEAVE)
+ *
+ * This options will at a minimum specify if the implementation is doing
+ * fragmented interleave.  Fragmented interleave, for a one to many
+ * socket, is when subsequent calls to receive a message may return
+ * parts of messages from different associations.  Some implementations
+ * may allow you to turn this value on or off.  If so, when turned off,
+ * no fragment interleave will occur (which will cause a head of line
+ * blocking amongst multiple associations sharing the same one to many
+ * socket).  When this option is turned on, then each receive call may
+ * come from a different association (thus the user must receive data
+ * with the extended calls (e.g. sctp_recvmsg) to keep track of which
+ * association each receive belongs to.
+ *
+ * This option takes a boolean value.  A non-zero value indicates that
+ * fragmented interleave is on.  A value of zero indicates that
+ * fragmented interleave is off.
+ *
+ * Note that it is important that an implementation that allows this
+ * option to be turned on, have it off by default.  Otherwise an unaware
+ * application using the one to many model may become confused and act
+ * incorrectly.
+ */
+static int sctp_setsockopt_fragment_interleave(struct sock *sk,
+					       char __user *optval,
+					       int optlen)
+{
+	int val;
+
+	if (optlen != sizeof(int))
+		return -EINVAL;
+	if (get_user(val, (int __user *)optval))
+		return -EFAULT;
+
+	sctp_sk(sk)->frag_interleave = (val == 0) ? 0 : 1;
+
+	return 0;
+}
+
+/*
+ * 7.1.25.  Set or Get the sctp partial delivery point
+ *       (SCTP_PARTIAL_DELIVERY_POINT)
+ * This option will set or get the SCTP partial delivery point.  This
+ * point is the size of a message where the partial delivery API will be
+ * invoked to help free up rwnd space for the peer.  Setting this to a
+ * lower value will cause partial delivery's to happen more often.  The
+ * calls argument is an integer that sets or gets the partial delivery
+ * point.
+ */
+static int sctp_setsockopt_partial_delivery_point(struct sock *sk,
+						  char __user *optval,
+						  int optlen)
+{
+	u32 val;
+
+	if (optlen != sizeof(u32))
+		return -EINVAL;
+	if (get_user(val, (int __user *)optval))
+		return -EFAULT;
+
+	sctp_sk(sk)->pd_point = val;
+
+	return 0; /* is this the right error code? */
+}
+
+/*
+ * 7.1.28.  Set or Get the maximum burst (SCTP_MAX_BURST)
+ *
+ * This option will allow a user to change the maximum burst of packets
+ * that can be emitted by this association.  Note that the default value
+ * is 4, and some implementations may restrict this setting so that it
+ * can only be lowered.
+ *
+ * NOTE: This text doesn't seem right.  Do this on a socket basis with
+ * future associations inheriting the socket value.
+ */
+static int sctp_setsockopt_maxburst(struct sock *sk,
+				    char __user *optval,
+				    int optlen)
+{
+	int val;
+
+	if (optlen != sizeof(int))
+		return -EINVAL;
+	if (get_user(val, (int __user *)optval))
+		return -EFAULT;
+
+	if (val < 0)
+		return -EINVAL;
+
+	sctp_sk(sk)->max_burst = val;
+
+	return 0;
+}
+
 /* API 6.2 setsockopt(), getsockopt()
  *
  * Applications use setsockopt() and getsockopt() to set or retrieve
@@ -2871,6 +3001,9 @@
 	case SCTP_DELAYED_ACK_TIME:
 		retval = sctp_setsockopt_delayed_ack_time(sk, optval, optlen);
 		break;
+	case SCTP_PARTIAL_DELIVERY_POINT:
+		retval = sctp_setsockopt_partial_delivery_point(sk, optval, optlen);
+		break;
 
 	case SCTP_INITMSG:
 		retval = sctp_setsockopt_initmsg(sk, optval, optlen);
@@ -2906,11 +3039,16 @@
 	case SCTP_CONTEXT:
 		retval = sctp_setsockopt_context(sk, optval, optlen);
 		break;
-
+	case SCTP_FRAGMENT_INTERLEAVE:
+		retval = sctp_setsockopt_fragment_interleave(sk, optval, optlen);
+		break;
+	case SCTP_MAX_BURST:
+		retval = sctp_setsockopt_maxburst(sk, optval, optlen);
+		break;
 	default:
 		retval = -ENOPROTOOPT;
 		break;
-	};
+	}
 
 	sctp_release_sock(sk);
 
@@ -3066,6 +3204,7 @@
 	sp->default_timetolive = 0;
 
 	sp->default_rcv_context = 0;
+	sp->max_burst = sctp_max_burst;
 
 	/* Initialize default setup parameters. These parameters
 	 * can be modified with the SCTP_INITMSG socket option or
@@ -3134,8 +3273,9 @@
 	sp->pf = sctp_get_pf_specific(sk->sk_family);
 
 	/* Control variables for partial data delivery. */
-	sp->pd_mode           = 0;
+	atomic_set(&sp->pd_mode, 0);
 	skb_queue_head_init(&sp->pd_lobby);
+	sp->frag_interleave = 0;
 
 	/* Create a per socket endpoint structure.  Even if we
 	 * change the data structure relationships, this may still
@@ -3642,7 +3782,7 @@
 	return 0;
 }
 
-/* 7.1.24. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME)
+/* 7.1.23. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME)
  *
  *   This options will get or set the delayed ack timer.  The time is set
  *   in milliseconds.  If the assoc_id is 0, then this sets or gets the
@@ -3847,7 +3987,7 @@
 		memcpy(&temp, &from->ipaddr, sizeof(temp));
 		sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
 		addrlen = sctp_get_af_specific(sk->sk_family)->sockaddr_len;
-		if(space_left < addrlen)
+		if (space_left < addrlen)
 			return -ENOMEM;
 		if (copy_to_user(to, &temp, addrlen))
 			return -EFAULT;
@@ -3936,8 +4076,9 @@
 /* Helper function that copies local addresses to user and returns the number
  * of addresses copied.
  */
-static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_addrs,
-					void __user *to)
+static int sctp_copy_laddrs_old(struct sock *sk, __u16 port,
+					int max_addrs, void *to,
+					int *bytes_copied)
 {
 	struct list_head *pos, *next;
 	struct sctp_sockaddr_entry *addr;
@@ -3954,10 +4095,10 @@
 		sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
 								&temp);
 		addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
-		if (copy_to_user(to, &temp, addrlen))
-			return -EFAULT;
+		memcpy(to, &temp, addrlen);
 
 		to += addrlen;
+		*bytes_copied += addrlen;
 		cnt ++;
 		if (cnt >= max_addrs) break;
 	}
@@ -3965,8 +4106,8 @@
 	return cnt;
 }
 
-static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port,
-				    void __user **to, size_t space_left)
+static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to,
+			    size_t space_left, int *bytes_copied)
 {
 	struct list_head *pos, *next;
 	struct sctp_sockaddr_entry *addr;
@@ -3983,14 +4124,14 @@
 		sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
 								&temp);
 		addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
-		if(space_left<addrlen)
+		if (space_left < addrlen)
 			return -ENOMEM;
-		if (copy_to_user(*to, &temp, addrlen))
-			return -EFAULT;
+		memcpy(to, &temp, addrlen);
 
-		*to += addrlen;
+		to += addrlen;
 		cnt ++;
 		space_left -= addrlen;
+		bytes_copied += addrlen;
 	}
 
 	return cnt;
@@ -4014,6 +4155,8 @@
 	int addrlen;
 	rwlock_t *addr_lock;
 	int err = 0;
+	void *addrs;
+	int bytes_copied = 0;
 
 	if (len != sizeof(struct sctp_getaddrs_old))
 		return -EINVAL;
@@ -4041,6 +4184,15 @@
 
 	to = getaddrs.addrs;
 
+	/* Allocate space for a local instance of packed array to hold all
+	 * the data.  We store addresses here first and then put write them
+	 * to the user in one shot.
+	 */
+	addrs = kmalloc(sizeof(union sctp_addr) * getaddrs.addr_num,
+			GFP_KERNEL);
+	if (!addrs)
+		return -ENOMEM;
+
 	sctp_read_lock(addr_lock);
 
 	/* If the endpoint is bound to 0.0.0.0 or ::0, get the valid
@@ -4050,13 +4202,9 @@
 		addr = list_entry(bp->address_list.next,
 				  struct sctp_sockaddr_entry, list);
 		if (sctp_is_any(&addr->a)) {
-			cnt = sctp_copy_laddrs_to_user_old(sk, bp->port,
-							   getaddrs.addr_num,
-							   to);
-			if (cnt < 0) {
-				err = cnt;
-				goto unlock;
-			}
+			cnt = sctp_copy_laddrs_old(sk, bp->port,
+						   getaddrs.addr_num,
+						   addrs, &bytes_copied);
 			goto copy_getaddrs;
 		}
 	}
@@ -4066,22 +4214,29 @@
 		memcpy(&temp, &addr->a, sizeof(temp));
 		sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
 		addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
-		if (copy_to_user(to, &temp, addrlen)) {
-			err = -EFAULT;
-			goto unlock;
-		}
+		memcpy(addrs, &temp, addrlen);
 		to += addrlen;
+		bytes_copied += addrlen;
 		cnt ++;
 		if (cnt >= getaddrs.addr_num) break;
 	}
 
 copy_getaddrs:
+	sctp_read_unlock(addr_lock);
+
+	/* copy the entire address list into the user provided space */
+	if (copy_to_user(to, addrs, bytes_copied)) {
+		err = -EFAULT;
+		goto error;
+	}
+
+	/* copy the leading structure back to user */
 	getaddrs.addr_num = cnt;
 	if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old)))
 		err = -EFAULT;
 
-unlock:
-	sctp_read_unlock(addr_lock);
+error:
+	kfree(addrs);
 	return err;
 }
 
@@ -4101,7 +4256,8 @@
 	rwlock_t *addr_lock;
 	int err = 0;
 	size_t space_left;
-	int bytes_copied;
+	int bytes_copied = 0;
+	void *addrs;
 
 	if (len <= sizeof(struct sctp_getaddrs))
 		return -EINVAL;
@@ -4129,6 +4285,9 @@
 	to = optval + offsetof(struct sctp_getaddrs,addrs);
 	space_left = len - sizeof(struct sctp_getaddrs) -
 			 offsetof(struct sctp_getaddrs,addrs);
+	addrs = kmalloc(space_left, GFP_KERNEL);
+	if (!addrs)
+		return -ENOMEM;
 
 	sctp_read_lock(addr_lock);
 
@@ -4139,11 +4298,11 @@
 		addr = list_entry(bp->address_list.next,
 				  struct sctp_sockaddr_entry, list);
 		if (sctp_is_any(&addr->a)) {
-			cnt = sctp_copy_laddrs_to_user(sk, bp->port,
-						       &to, space_left);
+			cnt = sctp_copy_laddrs(sk, bp->port, addrs,
+						space_left, &bytes_copied);
 			if (cnt < 0) {
 				err = cnt;
-				goto unlock;
+				goto error;
 			}
 			goto copy_getaddrs;
 		}
@@ -4154,26 +4313,31 @@
 		memcpy(&temp, &addr->a, sizeof(temp));
 		sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
 		addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
-		if(space_left < addrlen)
-			return -ENOMEM; /*fixme: right error?*/
-		if (copy_to_user(to, &temp, addrlen)) {
-			err = -EFAULT;
-			goto unlock;
+		if (space_left < addrlen) {
+			err =  -ENOMEM; /*fixme: right error?*/
+			goto error;
 		}
+		memcpy(addrs, &temp, addrlen);
 		to += addrlen;
+		bytes_copied += addrlen;
 		cnt ++;
 		space_left -= addrlen;
 	}
 
 copy_getaddrs:
+	sctp_read_unlock(addr_lock);
+
+	if (copy_to_user(to, addrs, bytes_copied)) {
+		err = -EFAULT;
+		goto error;
+	}
 	if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num))
 		return -EFAULT;
-	bytes_copied = ((char __user *)to) - optval;
 	if (put_user(bytes_copied, optlen))
 		return -EFAULT;
 
-unlock:
-	sctp_read_unlock(addr_lock);
+error:
+	kfree(addrs);
 	return err;
 }
 
@@ -4536,6 +4700,77 @@
 	return 0;
 }
 
+/*
+ * 7.1.24.  Get or set fragmented interleave (SCTP_FRAGMENT_INTERLEAVE)
+ * (chapter and verse is quoted at sctp_setsockopt_fragment_interleave())
+ */
+static int sctp_getsockopt_fragment_interleave(struct sock *sk, int len,
+					       char __user *optval, int __user *optlen)
+{
+	int val;
+
+	if (len < sizeof(int))
+		return -EINVAL;
+
+	len = sizeof(int);
+
+	val = sctp_sk(sk)->frag_interleave;
+	if (put_user(len, optlen))
+		return -EFAULT;
+	if (copy_to_user(optval, &val, len))
+		return -EFAULT;
+
+	return 0;
+}
+
+/*
+ * 7.1.25.  Set or Get the sctp partial delivery point
+ * (chapter and verse is quoted at sctp_setsockopt_partial_delivery_point())
+ */
+static int sctp_getsockopt_partial_delivery_point(struct sock *sk, int len,
+						  char __user *optval,
+						  int __user *optlen)
+{
+        u32 val;
+
+	if (len < sizeof(u32))
+		return -EINVAL;
+
+	len = sizeof(u32);
+
+	val = sctp_sk(sk)->pd_point;
+	if (put_user(len, optlen))
+		return -EFAULT;
+	if (copy_to_user(optval, &val, len))
+		return -EFAULT;
+
+	return -ENOTSUPP;
+}
+
+/*
+ * 7.1.28.  Set or Get the maximum burst (SCTP_MAX_BURST)
+ * (chapter and verse is quoted at sctp_setsockopt_maxburst())
+ */
+static int sctp_getsockopt_maxburst(struct sock *sk, int len,
+				    char __user *optval,
+				    int __user *optlen)
+{
+        int val;
+
+	if (len < sizeof(int))
+		return -EINVAL;
+
+	len = sizeof(int);
+
+	val = sctp_sk(sk)->max_burst;
+	if (put_user(len, optlen))
+		return -EFAULT;
+	if (copy_to_user(optval, &val, len))
+		return -EFAULT;
+
+	return -ENOTSUPP;
+}
+
 SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
 				char __user *optval, int __user *optlen)
 {
@@ -4648,10 +4883,21 @@
 	case SCTP_CONTEXT:
 		retval = sctp_getsockopt_context(sk, len, optval, optlen);
 		break;
+	case SCTP_FRAGMENT_INTERLEAVE:
+		retval = sctp_getsockopt_fragment_interleave(sk, len, optval,
+							     optlen);
+		break;
+	case SCTP_PARTIAL_DELIVERY_POINT:
+		retval = sctp_getsockopt_partial_delivery_point(sk, len, optval,
+								optlen);
+		break;
+	case SCTP_MAX_BURST:
+		retval = sctp_getsockopt_maxburst(sk, len, optval, optlen);
+		break;
 	default:
 		retval = -ENOPROTOOPT;
 		break;
-	};
+	}
 
 	sctp_release_sock(sk);
 	return retval;
@@ -4976,7 +5222,8 @@
 		break;
 	default:
 		break;
-	};
+	}
+
 	if (err)
 		goto cleanup;
 
@@ -5239,7 +5486,7 @@
 
 		default:
 			return -EINVAL;
-		};
+		}
 	}
 	return 0;
 }
@@ -5742,9 +5989,9 @@
 	 * 3) Peeling off non-partial delivery; move pd_lobby to receive_queue.
 	 */
 	skb_queue_head_init(&newsp->pd_lobby);
-	sctp_sk(newsk)->pd_mode = assoc->ulpq.pd_mode;
+	atomic_set(&sctp_sk(newsk)->pd_mode, assoc->ulpq.pd_mode);
 
-	if (sctp_sk(oldsk)->pd_mode) {
+	if (atomic_read(&sctp_sk(oldsk)->pd_mode)) {
 		struct sk_buff_head *queue;
 
 		/* Decide which queue to move pd_lobby skbs to. */
@@ -5770,7 +6017,7 @@
 		 * delivery to finish.
 		 */
 		if (assoc->ulpq.pd_mode)
-			sctp_clear_pd(oldsk);
+			sctp_clear_pd(oldsk, NULL);
 
 	}
 
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index 4d8c2ab..961df27 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -507,7 +507,7 @@
 			transport->cwnd = max(transport->cwnd/2,
 						 4*transport->asoc->pathmtu);
 		break;
-	};
+	}
 
 	transport->partial_bytes_acked = 0;
 	SCTP_DEBUG_PRINTK("%s: transport: %p reason: %d cwnd: "
diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c
index 2e11bc8..661ea2d 100644
--- a/net/sctp/ulpevent.c
+++ b/net/sctp/ulpevent.c
@@ -131,19 +131,54 @@
 struct sctp_ulpevent  *sctp_ulpevent_make_assoc_change(
 	const struct sctp_association *asoc,
 	__u16 flags, __u16 state, __u16 error, __u16 outbound,
-	__u16 inbound, gfp_t gfp)
+	__u16 inbound, struct sctp_chunk *chunk, gfp_t gfp)
 {
 	struct sctp_ulpevent *event;
 	struct sctp_assoc_change *sac;
 	struct sk_buff *skb;
 
-	event = sctp_ulpevent_new(sizeof(struct sctp_assoc_change),
+	/* If the lower layer passed in the chunk, it will be
+	 * an ABORT, so we need to include it in the sac_info.
+	 */
+	if (chunk) {
+		/* sctp_inqu_pop() has allready pulled off the chunk
+		 * header.  We need to put it back temporarily
+		 */
+		skb_push(chunk->skb, sizeof(sctp_chunkhdr_t));
+
+		/* Copy the chunk data to a new skb and reserve enough
+		 * head room to use as notification.
+		 */
+		skb = skb_copy_expand(chunk->skb,
+				      sizeof(struct sctp_assoc_change), 0, gfp);
+
+		if (!skb)
+			goto fail;
+
+		/* put back the chunk header now that we have a copy */
+		skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t));
+
+		/* Embed the event fields inside the cloned skb.  */
+		event = sctp_skb2event(skb);
+		sctp_ulpevent_init(event, MSG_NOTIFICATION, skb->truesize);
+
+		/* Include the notification structure */
+		sac = (struct sctp_assoc_change *)
+			skb_push(skb, sizeof(struct sctp_assoc_change));
+
+		/* Trim the buffer to the right length.  */
+		skb_trim(skb, sizeof(struct sctp_assoc_change) +
+			 ntohs(chunk->chunk_hdr->length));
+	} else {
+		event = sctp_ulpevent_new(sizeof(struct sctp_assoc_change),
 				  MSG_NOTIFICATION, gfp);
-	if (!event)
-		goto fail;
-	skb = sctp_event2skb(event);
-	sac = (struct sctp_assoc_change *)
-		skb_put(skb, sizeof(struct sctp_assoc_change));
+		if (!event)
+			goto fail;
+
+		skb = sctp_event2skb(event);
+		sac = (struct sctp_assoc_change *) skb_put(skb,
+					sizeof(struct sctp_assoc_change));
+	}
 
 	/* Socket Extensions for SCTP
 	 * 5.3.1.1 SCTP_ASSOC_CHANGE
diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c
index b29e3e4..34eb977 100644
--- a/net/sctp/ulpqueue.c
+++ b/net/sctp/ulpqueue.c
@@ -138,26 +138,59 @@
 /* Clear the partial delivery mode for this socket.   Note: This
  * assumes that no association is currently in partial delivery mode.
  */
-int sctp_clear_pd(struct sock *sk)
+int sctp_clear_pd(struct sock *sk, struct sctp_association *asoc)
 {
 	struct sctp_sock *sp = sctp_sk(sk);
 
-	sp->pd_mode = 0;
-	if (!skb_queue_empty(&sp->pd_lobby)) {
-		struct list_head *list;
-		sctp_skb_list_tail(&sp->pd_lobby, &sk->sk_receive_queue);
-		list = (struct list_head *)&sctp_sk(sk)->pd_lobby;
-		INIT_LIST_HEAD(list);
-		return 1;
+	if (atomic_dec_and_test(&sp->pd_mode)) {
+		/* This means there are no other associations in PD, so
+		 * we can go ahead and clear out the lobby in one shot
+		 */
+		if (!skb_queue_empty(&sp->pd_lobby)) {
+			struct list_head *list;
+			sctp_skb_list_tail(&sp->pd_lobby, &sk->sk_receive_queue);
+			list = (struct list_head *)&sctp_sk(sk)->pd_lobby;
+			INIT_LIST_HEAD(list);
+			return 1;
+		}
+	} else {
+		/* There are other associations in PD, so we only need to
+		 * pull stuff out of the lobby that belongs to the
+		 * associations that is exiting PD (all of its notifications
+		 * are posted here).
+		 */
+		if (!skb_queue_empty(&sp->pd_lobby) && asoc) {
+			struct sk_buff *skb, *tmp;
+			struct sctp_ulpevent *event;
+
+			sctp_skb_for_each(skb, &sp->pd_lobby, tmp) {
+				event = sctp_skb2event(skb);
+				if (event->asoc == asoc) {
+					__skb_unlink(skb, &sp->pd_lobby);
+					__skb_queue_tail(&sk->sk_receive_queue,
+							 skb);
+				}
+			}
+		}
 	}
+
 	return 0;
 }
 
+/* Set the pd_mode on the socket and ulpq */
+static void sctp_ulpq_set_pd(struct sctp_ulpq *ulpq)
+{
+	struct sctp_sock *sp = sctp_sk(ulpq->asoc->base.sk);
+
+	atomic_inc(&sp->pd_mode);
+	ulpq->pd_mode = 1;
+}
+
 /* Clear the pd_mode and restart any pending messages waiting for delivery. */
 static int sctp_ulpq_clear_pd(struct sctp_ulpq *ulpq)
 {
 	ulpq->pd_mode = 0;
-	return sctp_clear_pd(ulpq->asoc->base.sk);
+	return sctp_clear_pd(ulpq->asoc->base.sk, ulpq->asoc);
 }
 
 /* If the SKB of 'event' is on a list, it is the first such member
@@ -187,25 +220,35 @@
 	 * the association the cause of the partial delivery.
 	 */
 
-	if (!sctp_sk(sk)->pd_mode) {
+	if (atomic_read(&sctp_sk(sk)->pd_mode) == 0) {
 		queue = &sk->sk_receive_queue;
-	} else if (ulpq->pd_mode) {
-		/* If the association is in partial delivery, we
-		 * need to finish delivering the partially processed
-		 * packet before passing any other data.  This is
-		 * because we don't truly support stream interleaving.
-		 */
-		if ((event->msg_flags & MSG_NOTIFICATION) ||
-		    (SCTP_DATA_NOT_FRAG ==
-			    (event->msg_flags & SCTP_DATA_FRAG_MASK)))
-			queue = &sctp_sk(sk)->pd_lobby;
-		else {
-			clear_pd = event->msg_flags & MSG_EOR;
-			queue = &sk->sk_receive_queue;
+	} else {
+		if (ulpq->pd_mode) {
+			/* If the association is in partial delivery, we
+			 * need to finish delivering the partially processed
+			 * packet before passing any other data.  This is
+			 * because we don't truly support stream interleaving.
+			 */
+			if ((event->msg_flags & MSG_NOTIFICATION) ||
+			    (SCTP_DATA_NOT_FRAG ==
+				    (event->msg_flags & SCTP_DATA_FRAG_MASK)))
+				queue = &sctp_sk(sk)->pd_lobby;
+			else {
+				clear_pd = event->msg_flags & MSG_EOR;
+				queue = &sk->sk_receive_queue;
+			}
+		} else {
+			/*
+			 * If fragment interleave is enabled, we
+			 * can queue this to the recieve queue instead
+			 * of the lobby.
+			 */
+			if (sctp_sk(sk)->frag_interleave)
+				queue = &sk->sk_receive_queue;
+			else
+				queue = &sctp_sk(sk)->pd_lobby;
 		}
-	} else
-		queue = &sctp_sk(sk)->pd_lobby;
-
+	}
 
 	/* If we are harvesting multiple skbs they will be
 	 * collected on a list.
@@ -348,7 +391,7 @@
 			break;
 		pos->next = pnext;
 		pos = pnext;
-	};
+	}
 
 	event = sctp_skb2event(f_frag);
 	SCTP_INC_STATS(SCTP_MIB_REASMUSRMSGS);
@@ -367,6 +410,11 @@
 	struct sk_buff *first_frag = NULL;
 	__u32 ctsn, next_tsn;
 	struct sctp_ulpevent *retval = NULL;
+	struct sk_buff *pd_first = NULL;
+	struct sk_buff *pd_last = NULL;
+	size_t pd_len = 0;
+	struct sctp_association *asoc;
+	u32 pd_point;
 
 	/* Initialized to 0 just to avoid compiler warning message.  Will
 	 * never be used with this value. It is referenced only after it
@@ -382,6 +430,10 @@
 	 * we expect to find the remaining middle fragments and the last
 	 * fragment in order. If not, first_frag is reset to NULL and we
 	 * start the next pass when we find another first fragment.
+	 *
+	 * There is a potential to do partial delivery if user sets
+	 * SCTP_PARTIAL_DELIVERY_POINT option. Lets count some things here
+	 * to see if can do PD.
 	 */
 	skb_queue_walk(&ulpq->reasm, pos) {
 		cevent = sctp_skb2event(pos);
@@ -389,14 +441,32 @@
 
 		switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) {
 		case SCTP_DATA_FIRST_FRAG:
+			/* If this "FIRST_FRAG" is the first
+			 * element in the queue, then count it towards
+			 * possible PD.
+			 */
+			if (pos == ulpq->reasm.next) {
+			    pd_first = pos;
+			    pd_last = pos;
+			    pd_len = pos->len;
+			} else {
+			    pd_first = NULL;
+			    pd_last = NULL;
+			    pd_len = 0;
+			}
+
 			first_frag = pos;
 			next_tsn = ctsn + 1;
 			break;
 
 		case SCTP_DATA_MIDDLE_FRAG:
-			if ((first_frag) && (ctsn == next_tsn))
+			if ((first_frag) && (ctsn == next_tsn)) {
 				next_tsn++;
-			else
+				if (pd_first) {
+				    pd_last = pos;
+				    pd_len += pos->len;
+				}
+			} else
 				first_frag = NULL;
 			break;
 
@@ -406,8 +476,29 @@
 			else
 				first_frag = NULL;
 			break;
-		};
+		}
+	}
 
+	asoc = ulpq->asoc;
+	if (pd_first) {
+		/* Make sure we can enter partial deliver.
+		 * We can trigger partial delivery only if framgent
+		 * interleave is set, or the socket is not already
+		 * in  partial delivery.
+		 */
+		if (!sctp_sk(asoc->base.sk)->frag_interleave &&
+		    atomic_read(&sctp_sk(asoc->base.sk)->pd_mode))
+			goto done;
+
+		cevent = sctp_skb2event(pd_first);
+		pd_point = sctp_sk(asoc->base.sk)->pd_point;
+		if (pd_point && pd_point <= pd_len) {
+			retval = sctp_make_reassembled_event(&ulpq->reasm,
+							     pd_first,
+							     pd_last);
+			if (retval)
+				sctp_ulpq_set_pd(ulpq);
+		}
 	}
 done:
 	return retval;
@@ -465,7 +556,7 @@
 			goto done;
 		default:
 			return NULL;
-		};
+		}
 	}
 
 	/* We have the reassembled event. There is no need to look
@@ -557,7 +648,7 @@
 			break;
 		default:
 			return NULL;
-		};
+		}
 	}
 
 	/* We have the reassembled event. There is no need to look
@@ -826,19 +917,29 @@
 {
 	struct sctp_ulpevent *event;
 	struct sctp_association *asoc;
+	struct sctp_sock *sp;
 
 	asoc = ulpq->asoc;
+	sp = sctp_sk(asoc->base.sk);
 
-	/* Are we already in partial delivery mode?  */
-	if (!sctp_sk(asoc->base.sk)->pd_mode) {
+	/* If the association is already in Partial Delivery mode
+	 * we have noting to do.
+	 */
+	if (ulpq->pd_mode)
+		return;
 
+	/* If the user enabled fragment interleave socket option,
+	 * multiple associations can enter partial delivery.
+	 * Otherwise, we can only enter partial delivery if the
+	 * socket is not in partial deliver mode.
+	 */
+	if (sp->frag_interleave || atomic_read(&sp->pd_mode) == 0) {
 		/* Is partial delivery possible?  */
 		event = sctp_ulpq_retrieve_first(ulpq);
 		/* Send event to the ULP.   */
 		if (event) {
 			sctp_ulpq_tail_event(ulpq, event);
-			sctp_sk(asoc->base.sk)->pd_mode = 1;
-			ulpq->pd_mode = 1;
+			sctp_ulpq_set_pd(ulpq);
 			return;
 		}
 	}
diff --git a/net/socket.c b/net/socket.c
index ea8f81a..1ad62c0 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -585,6 +585,37 @@
 	return result;
 }
 
+/*
+ * called from sock_recv_timestamp() if sock_flag(sk, SOCK_RCVTSTAMP)
+ */
+void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
+	struct sk_buff *skb)
+{
+	ktime_t kt = skb->tstamp;
+
+	if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) {
+		struct timeval tv;
+		/* Race occurred between timestamp enabling and packet
+		   receiving.  Fill in the current time for now. */
+		if (kt.tv64 == 0)
+			kt = ktime_get_real();
+		skb->tstamp = kt;
+		tv = ktime_to_timeval(kt);
+		put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMP, sizeof(tv), &tv);
+	} else {
+		struct timespec ts;
+		/* Race occurred between timestamp enabling and packet
+		   receiving.  Fill in the current time for now. */
+		if (kt.tv64 == 0)
+			kt = ktime_get_real();
+		skb->tstamp = kt;
+		ts = ktime_to_timespec(kt);
+		put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPNS, sizeof(ts), &ts);
+	}
+}
+
+EXPORT_SYMBOL_GPL(__sock_recv_timestamp);
+
 static inline int __sock_recvmsg(struct kiocb *iocb, struct socket *sock,
 				 struct msghdr *msg, size_t size, int flags)
 {
@@ -1292,7 +1323,7 @@
 	int err, fput_needed;
 
 	sock = sockfd_lookup_light(fd, &err, &fput_needed);
-	if(sock) {
+	if (sock) {
 		err = move_addr_to_kernel(umyaddr, addrlen, address);
 		if (err >= 0) {
 			err = security_socket_bind(sock,
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index f02f24a..543b085 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -1237,20 +1237,12 @@
 
 	return res;
 }
-static int content_release(struct inode *inode, struct file *file)
-{
-	struct seq_file *m = (struct seq_file *)file->private_data;
-	struct handle *han = m->private;
-	kfree(han);
-	m->private = NULL;
-	return seq_release(inode, file);
-}
 
 static const struct file_operations content_file_operations = {
 	.open		= content_open,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
-	.release	= content_release,
+	.release	= seq_release_private,
 };
 
 static ssize_t read_flush(struct file *file, char __user *buf,
diff --git a/net/sunrpc/socklib.c b/net/sunrpc/socklib.c
index 634885b..1d377d1 100644
--- a/net/sunrpc/socklib.c
+++ b/net/sunrpc/socklib.c
@@ -154,7 +154,7 @@
 	desc.offset = sizeof(struct udphdr);
 	desc.count = skb->len - desc.offset;
 
-	if (skb->ip_summed == CHECKSUM_UNNECESSARY)
+	if (skb_csum_unnecessary(skb))
 		goto no_checksum;
 
 	desc.csum = csum_partial(skb->data, desc.offset, skb->csum);
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 2772fee..22f61ae 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -798,16 +798,12 @@
 		dprintk("svc: recvfrom returned error %d\n", -err);
 	}
 	rqstp->rq_addrlen = sizeof(rqstp->rq_addr);
-	if (skb->tstamp.off_sec == 0) {
-		struct timeval tv;
-
-		tv.tv_sec = xtime.tv_sec;
-		tv.tv_usec = xtime.tv_nsec / NSEC_PER_USEC;
-		skb_set_timestamp(skb, &tv);
+	if (skb->tstamp.tv64 == 0) {
+		skb->tstamp = ktime_get_real();
 		/* Don't enable netstamp, sunrpc doesn't
 		   need that much accuracy */
 	}
-	skb_get_timestamp(skb, &svsk->sk_sk->sk_stamp);
+	svsk->sk_sk->sk_stamp = skb->tstamp;
 	set_bit(SK_DATA, &svsk->sk_flags); /* there may be more data... */
 
 	/*
diff --git a/net/tipc/config.c b/net/tipc/config.c
index 14789a8..c71337a2 100644
--- a/net/tipc/config.c
+++ b/net/tipc/config.c
@@ -89,7 +89,7 @@
 int tipc_cfg_append_tlv(struct sk_buff *buf, int tlv_type,
 			void *tlv_data, int tlv_data_size)
 {
-	struct tlv_desc *tlv = (struct tlv_desc *)buf->tail;
+	struct tlv_desc *tlv = (struct tlv_desc *)skb_tail_pointer(buf);
 	int new_tlv_space = TLV_SPACE(tlv_data_size);
 
 	if (skb_tailroom(buf) < new_tlv_space) {
diff --git a/net/tipc/eth_media.c b/net/tipc/eth_media.c
index 9be4839..67bb29b 100644
--- a/net/tipc/eth_media.c
+++ b/net/tipc/eth_media.c
@@ -73,7 +73,7 @@
 
 	clone = skb_clone(buf, GFP_ATOMIC);
 	if (clone) {
-		clone->nh.raw = clone->data;
+		skb_reset_network_header(clone);
 		dev = ((struct eth_bearer *)(tb_ptr->usr_handle))->dev;
 		clone->dev = dev;
 		dev->hard_header(clone, dev, ETH_P_TIPC,
@@ -99,8 +99,8 @@
 
 	if (likely(eb_ptr->bearer)) {
 	       if (likely(!dev->promiscuity) ||
-		   !memcmp(buf->mac.raw,dev->dev_addr,ETH_ALEN) ||
-		   !memcmp(buf->mac.raw,dev->broadcast,ETH_ALEN)) {
+		   !memcmp(skb_mac_header(buf), dev->dev_addr, ETH_ALEN) ||
+		   !memcmp(skb_mac_header(buf), dev->broadcast, ETH_ALEN)) {
 			size = msg_size((struct tipc_msg *)buf->data);
 			skb_trim(buf, size);
 			if (likely(buf->len == size)) {
@@ -140,7 +140,7 @@
 		return -EDQUOT;
 	if (!eb_ptr->dev) {
 		eb_ptr->dev = dev;
-		eb_ptr->tipc_packet_type.type = __constant_htons(ETH_P_TIPC);
+		eb_ptr->tipc_packet_type.type = htons(ETH_P_TIPC);
 		eb_ptr->tipc_packet_type.dev = dev;
 		eb_ptr->tipc_packet_type.func = recv_msg;
 		eb_ptr->tipc_packet_type.af_packet_priv = eb_ptr;
diff --git a/net/tipc/link.c b/net/tipc/link.c
index 71c2f2f..2124f32 100644
--- a/net/tipc/link.c
+++ b/net/tipc/link.c
@@ -1001,7 +1001,7 @@
 		return 0;
 
 	skb_put(bundler, pad + size);
-	memcpy(bundler->data + to_pos, buf->data, size);
+	skb_copy_to_linear_data_offset(bundler, to_pos, buf->data, size);
 	msg_set_size(bundler_msg, to_pos + size);
 	msg_set_msgcnt(bundler_msg, msg_msgcnt(bundler_msg) + 1);
 	dbg("Packed msg # %u(%u octets) into pos %u in buf(#%u)\n",
@@ -1109,8 +1109,8 @@
 			if (bundler) {
 				msg_init(&bundler_hdr, MSG_BUNDLER, OPEN_MSG,
 					 TIPC_OK, INT_H_SIZE, l_ptr->addr);
-				memcpy(bundler->data, (unchar *)&bundler_hdr,
-				       INT_H_SIZE);
+				skb_copy_to_linear_data(bundler, &bundler_hdr,
+							INT_H_SIZE);
 				skb_trim(bundler, INT_H_SIZE);
 				link_bundle_buf(l_ptr, bundler, buf);
 				buf = bundler;
@@ -1383,9 +1383,9 @@
 	if (!buf)
 		return -ENOMEM;
 	buf->next = NULL;
-	memcpy(buf->data, (unchar *)&fragm_hdr, INT_H_SIZE);
+	skb_copy_to_linear_data(buf, &fragm_hdr, INT_H_SIZE);
 	hsz = msg_hdr_sz(hdr);
-	memcpy(buf->data + INT_H_SIZE, (unchar *)hdr, hsz);
+	skb_copy_to_linear_data_offset(buf, INT_H_SIZE, hdr, hsz);
 	msg_dbg(buf_msg(buf), ">BUILD>");
 
 	/* Chop up message: */
@@ -1416,8 +1416,8 @@
 				return -EFAULT;
 			}
 		} else
-			memcpy(buf->data + fragm_crs, sect_crs, sz);
-
+			skb_copy_to_linear_data_offset(buf, fragm_crs,
+						       sect_crs, sz);
 		sect_crs += sz;
 		sect_rest -= sz;
 		fragm_crs += sz;
@@ -1442,7 +1442,7 @@
 
 			buf->next = NULL;
 			prev->next = buf;
-			memcpy(buf->data, (unchar *)&fragm_hdr, INT_H_SIZE);
+			skb_copy_to_linear_data(buf, &fragm_hdr, INT_H_SIZE);
 			fragm_crs = INT_H_SIZE;
 			fragm_rest = fragm_sz;
 			msg_dbg(buf_msg(buf),"  >BUILD>");
@@ -2130,7 +2130,7 @@
 		buf = l_ptr->proto_msg_queue;
 		if (!buf)
 			return;
-		memcpy(buf->data, (unchar *)msg, sizeof(l_ptr->proto_msg));
+		skb_copy_to_linear_data(buf, msg, sizeof(l_ptr->proto_msg));
 		return;
 	}
 	msg_set_timestamp(msg, jiffies_to_msecs(jiffies));
@@ -2143,7 +2143,7 @@
 	if (!buf)
 		return;
 
-	memcpy(buf->data, (unchar *)msg, sizeof(l_ptr->proto_msg));
+	skb_copy_to_linear_data(buf, msg, sizeof(l_ptr->proto_msg));
 	msg_set_size(buf_msg(buf), msg_size);
 
 	if (tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr)) {
@@ -2319,8 +2319,8 @@
 		     "unable to send tunnel msg\n");
 		return;
 	}
-	memcpy(buf->data, (unchar *)tunnel_hdr, INT_H_SIZE);
-	memcpy(buf->data + INT_H_SIZE, (unchar *)msg, length);
+	skb_copy_to_linear_data(buf, tunnel_hdr, INT_H_SIZE);
+	skb_copy_to_linear_data_offset(buf, INT_H_SIZE, msg, length);
 	dbg("%c->%c:", l_ptr->b_ptr->net_plane, tunnel->b_ptr->net_plane);
 	msg_dbg(buf_msg(buf), ">SEND>");
 	tipc_link_send_buf(tunnel, buf);
@@ -2361,7 +2361,7 @@
 
 		buf = buf_acquire(INT_H_SIZE);
 		if (buf) {
-			memcpy(buf->data, (unchar *)&tunnel_hdr, INT_H_SIZE);
+			skb_copy_to_linear_data(buf, &tunnel_hdr, INT_H_SIZE);
 			msg_set_size(&tunnel_hdr, INT_H_SIZE);
 			dbg("%c->%c:", l_ptr->b_ptr->net_plane,
 			    tunnel->b_ptr->net_plane);
@@ -2426,8 +2426,9 @@
 			     "unable to send duplicate msg\n");
 			return;
 		}
-		memcpy(outbuf->data, (unchar *)&tunnel_hdr, INT_H_SIZE);
-		memcpy(outbuf->data + INT_H_SIZE, iter->data, length);
+		skb_copy_to_linear_data(outbuf, &tunnel_hdr, INT_H_SIZE);
+		skb_copy_to_linear_data_offset(outbuf, INT_H_SIZE, iter->data,
+					       length);
 		dbg("%c->%c:", l_ptr->b_ptr->net_plane,
 		    tunnel->b_ptr->net_plane);
 		msg_dbg(buf_msg(outbuf), ">SEND>");
@@ -2457,7 +2458,7 @@
 
 	eb = buf_acquire(size);
 	if (eb)
-		memcpy(eb->data, (unchar *)msg, size);
+		skb_copy_to_linear_data(eb, msg, size);
 	return eb;
 }
 
@@ -2569,7 +2570,7 @@
 		if (obuf == NULL) {
 			warn("Link unable to unbundle message(s)\n");
 			break;
-		};
+		}
 		pos += align(msg_size(buf_msg(obuf)));
 		msg_dbg(buf_msg(obuf), "     /");
 		tipc_net_route_msg(obuf);
@@ -2631,9 +2632,9 @@
 			goto exit;
 		}
 		msg_set_size(&fragm_hdr, fragm_sz + INT_H_SIZE);
-		memcpy(fragm->data, (unchar *)&fragm_hdr, INT_H_SIZE);
-		memcpy(fragm->data + INT_H_SIZE, crs, fragm_sz);
-
+		skb_copy_to_linear_data(fragm, &fragm_hdr, INT_H_SIZE);
+		skb_copy_to_linear_data_offset(fragm, INT_H_SIZE, crs,
+					       fragm_sz);
 		/*  Send queued messages first, if any: */
 
 		l_ptr->stats.sent_fragments++;
@@ -2733,8 +2734,8 @@
 		if (pbuf != NULL) {
 			pbuf->next = *pending;
 			*pending = pbuf;
-			memcpy(pbuf->data, (unchar *)imsg, msg_data_sz(fragm));
-
+			skb_copy_to_linear_data(pbuf, imsg,
+						msg_data_sz(fragm));
 			/*  Prepare buffer for subsequent fragments. */
 
 			set_long_msg_seqno(pbuf, long_msg_seq_no);
@@ -2750,7 +2751,8 @@
 		u32 fsz = get_fragm_size(pbuf);
 		u32 crs = ((msg_fragm_no(fragm) - 1) * fsz);
 		u32 exp_frags = get_expected_frags(pbuf) - 1;
-		memcpy(pbuf->data + crs, msg_data(fragm), dsz);
+		skb_copy_to_linear_data_offset(pbuf, crs,
+					       msg_data(fragm), dsz);
 		buf_discard(fbuf);
 
 		/* Is message complete? */
diff --git a/net/tipc/msg.h b/net/tipc/msg.h
index 62d5490..35d5ba1 100644
--- a/net/tipc/msg.h
+++ b/net/tipc/msg.h
@@ -1,8 +1,8 @@
 /*
  * net/tipc/msg.h: Include file for TIPC message header routines
  *
- * Copyright (c) 2000-2006, Ericsson AB
- * Copyright (c) 2005, Wind River Systems
+ * Copyright (c) 2000-2007, Ericsson AB
+ * Copyright (c) 2005-2007, Wind River Systems
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -71,8 +71,11 @@
 static inline void msg_set_bits(struct tipc_msg *m, u32 w,
 				u32 pos, u32 mask, u32 val)
 {
-	u32 word = msg_word(m,w) & ~(mask << pos);
-	msg_set_word(m, w, (word |= (val << pos)));
+	val = (val & mask) << pos;
+	val = htonl(val);
+	mask = htonl(mask << pos);
+	m->hdr[w] &= ~mask;
+	m->hdr[w] |= val;
 }
 
 /*
@@ -786,15 +789,16 @@
 	*buf = buf_acquire(sz);
 	if (!(*buf))
 		return -ENOMEM;
-	memcpy((*buf)->data, (unchar *)hdr, hsz);
+	skb_copy_to_linear_data(*buf, hdr, hsz);
 	for (res = 1, cnt = 0; res && (cnt < num_sect); cnt++) {
 		if (likely(usrmem))
 			res = !copy_from_user((*buf)->data + pos,
 					      msg_sect[cnt].iov_base,
 					      msg_sect[cnt].iov_len);
 		else
-			memcpy((*buf)->data + pos, msg_sect[cnt].iov_base,
-			       msg_sect[cnt].iov_len);
+			skb_copy_to_linear_data_offset(*buf, pos,
+						       msg_sect[cnt].iov_base,
+						       msg_sect[cnt].iov_len);
 		pos += msg_sect[cnt].iov_len;
 	}
 	if (likely(res))
diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c
index b8e1edc..4cdafa2 100644
--- a/net/tipc/netlink.c
+++ b/net/tipc/netlink.c
@@ -57,7 +57,7 @@
 
 	if (rep_buf) {
 		skb_push(rep_buf, hdr_space);
-		rep_nlh = (struct nlmsghdr *)rep_buf->data;
+		rep_nlh = nlmsg_hdr(rep_buf);
 		memcpy(rep_nlh, req_nlh, hdr_space);
 		rep_nlh->nlmsg_len = rep_buf->len;
 		genlmsg_unicast(rep_buf, req_nlh->nlmsg_pid);
diff --git a/net/tipc/port.c b/net/tipc/port.c
index 5f8217d..bcd5da0 100644
--- a/net/tipc/port.c
+++ b/net/tipc/port.c
@@ -464,7 +464,7 @@
 	msg_set_size(rmsg, data_sz + hdr_sz);
 	msg_set_nametype(rmsg, msg_nametype(msg));
 	msg_set_nameinst(rmsg, msg_nameinst(msg));
-	memcpy(rbuf->data + hdr_sz, msg_data(msg), data_sz);
+	skb_copy_to_linear_data_offset(rbuf, hdr_sz, msg_data(msg), data_sz);
 
 	/* send self-abort message when rejecting on a connected port */
 	if (msg_connected(msg)) {
@@ -1419,7 +1419,7 @@
 		return -ENOMEM;
 
 	skb_push(buf, hsz);
-	memcpy(buf->data, (unchar *)msg, hsz);
+	skb_copy_to_linear_data(buf, msg, hsz);
 	destnode = msg_destnode(msg);
 	p_ptr->publ.congested = 1;
 	if (!tipc_port_congested(p_ptr)) {
@@ -1555,7 +1555,7 @@
 	if (skb_cow(buf, LONG_H_SIZE))
 		return -ENOMEM;
 	skb_push(buf, LONG_H_SIZE);
-	memcpy(buf->data, (unchar *)msg, LONG_H_SIZE);
+	skb_copy_to_linear_data(buf, msg, LONG_H_SIZE);
 	msg_dbg(buf_msg(buf),"PREP:");
 	if (likely(destport || destnode)) {
 		p_ptr->sent++;
@@ -1679,7 +1679,7 @@
 		return -ENOMEM;
 
 	skb_push(buf, DIR_MSG_H_SIZE);
-	memcpy(buf->data, (unchar *)msg, DIR_MSG_H_SIZE);
+	skb_copy_to_linear_data(buf, msg, DIR_MSG_H_SIZE);
 	msg_dbg(msg, "buf2port: ");
 	p_ptr->sent++;
 	if (dest->node == tipc_own_addr)
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index b71739f..45832fb 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -1020,7 +1020,7 @@
 
 	if (!err) {
 		buf_crs = (unsigned char *)(TIPC_SKB_CB(buf)->handle);
-		sz = buf->tail - buf_crs;
+		sz = skb_tail_pointer(buf) - buf_crs;
 
 		needed = (buf_len - sz_copied);
 		sz_to_copy = (sz <= needed) ? sz : needed;
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 6069716..aec8cf1 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1319,7 +1319,7 @@
 		unix_attach_fds(siocb->scm, skb);
 	unix_get_secdata(siocb->scm, skb);
 
-	skb->h.raw = skb->data;
+	skb_reset_transport_header(skb);
 	err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
 	if (err)
 		goto out_free;
diff --git a/net/wanrouter/wanmain.c b/net/wanrouter/wanmain.c
index 5d2d93d..7a19e0e 100644
--- a/net/wanrouter/wanmain.c
+++ b/net/wanrouter/wanmain.c
@@ -277,8 +277,8 @@
 		skb_push(skb, 7);
 		skb->data[0] = 0;
 		skb->data[1] = NLPID_SNAP;
-		memcpy(&skb->data[2], wanrouter_oui_ether,
-		       sizeof(wanrouter_oui_ether));
+		skb_copy_to_linear_data_offset(skb, 2, wanrouter_oui_ether,
+					       sizeof(wanrouter_oui_ether));
 		*((unsigned short*)&skb->data[5]) = htons(type);
 		break;
 
@@ -339,7 +339,7 @@
 	skb->protocol = ethertype;
 	skb->pkt_type = PACKET_HOST;	/*	Physically point to point */
 	skb_pull(skb, cnt);
-	skb->mac.raw  = skb->data;
+	skb_reset_mac_header(skb);
 	return ethertype;
 }
 
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
new file mode 100644
index 0000000..a228d56
--- /dev/null
+++ b/net/wireless/Kconfig
@@ -0,0 +1,16 @@
+config CFG80211
+        tristate "Improved wireless configuration API"
+
+config WIRELESS_EXT
+	bool "Wireless extensions"
+	default n
+	---help---
+	  This option enables the legacy wireless extensions
+	  (wireless network interface configuration via ioctls.)
+
+	  Wireless extensions will be replaced by cfg80211 and
+	  will be required only by legacy drivers that implement
+	  wireless extension handlers.
+
+	  Say N (if you can) unless you know you need wireless
+	  extensions for external modules.
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
new file mode 100644
index 0000000..3a96ae6
--- /dev/null
+++ b/net/wireless/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_WIRELESS_EXT) += wext.o
+obj-$(CONFIG_CFG80211) += cfg80211.o
+
+cfg80211-y += core.o sysfs.o
diff --git a/net/wireless/core.c b/net/wireless/core.c
new file mode 100644
index 0000000..7eabd55
--- /dev/null
+++ b/net/wireless/core.c
@@ -0,0 +1,224 @@
+/*
+ * This is the linux wireless configuration interface.
+ *
+ * Copyright 2006, 2007		Johannes Berg <johannes@sipsolutions.net>
+ */
+
+#include <linux/if.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/nl80211.h>
+#include <linux/debugfs.h>
+#include <linux/notifier.h>
+#include <linux/device.h>
+#include <net/genetlink.h>
+#include <net/cfg80211.h>
+#include <net/wireless.h>
+#include "core.h"
+#include "sysfs.h"
+
+/* name for sysfs, %d is appended */
+#define PHY_NAME "phy"
+
+MODULE_AUTHOR("Johannes Berg");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("wireless configuration support");
+
+/* RCU might be appropriate here since we usually
+ * only read the list, and that can happen quite
+ * often because we need to do it for each command */
+LIST_HEAD(cfg80211_drv_list);
+DEFINE_MUTEX(cfg80211_drv_mutex);
+static int wiphy_counter;
+
+/* for debugfs */
+static struct dentry *ieee80211_debugfs_dir;
+
+/* exported functions */
+
+struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv)
+{
+	struct cfg80211_registered_device *drv;
+	int alloc_size;
+
+	alloc_size = sizeof(*drv) + sizeof_priv;
+
+	drv = kzalloc(alloc_size, GFP_KERNEL);
+	if (!drv)
+		return NULL;
+
+	drv->ops = ops;
+
+	mutex_lock(&cfg80211_drv_mutex);
+
+	drv->idx = wiphy_counter;
+
+	/* now increase counter for the next device unless
+	 * it has wrapped previously */
+	if (wiphy_counter >= 0)
+		wiphy_counter++;
+
+	mutex_unlock(&cfg80211_drv_mutex);
+
+	if (unlikely(drv->idx < 0)) {
+		/* ugh, wrapped! */
+		kfree(drv);
+		return NULL;
+	}
+
+	/* give it a proper name */
+	snprintf(drv->wiphy.dev.bus_id, BUS_ID_SIZE,
+		 PHY_NAME "%d", drv->idx);
+
+	mutex_init(&drv->mtx);
+	mutex_init(&drv->devlist_mtx);
+	INIT_LIST_HEAD(&drv->netdev_list);
+
+	device_initialize(&drv->wiphy.dev);
+	drv->wiphy.dev.class = &ieee80211_class;
+	drv->wiphy.dev.platform_data = drv;
+
+	return &drv->wiphy;
+}
+EXPORT_SYMBOL(wiphy_new);
+
+int wiphy_register(struct wiphy *wiphy)
+{
+	struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy);
+	int res;
+
+	mutex_lock(&cfg80211_drv_mutex);
+
+	res = device_add(&drv->wiphy.dev);
+	if (res)
+		goto out_unlock;
+
+	list_add(&drv->list, &cfg80211_drv_list);
+
+	/* add to debugfs */
+	drv->wiphy.debugfsdir =
+		debugfs_create_dir(wiphy_name(&drv->wiphy),
+				   ieee80211_debugfs_dir);
+
+	res = 0;
+out_unlock:
+	mutex_unlock(&cfg80211_drv_mutex);
+	return res;
+}
+EXPORT_SYMBOL(wiphy_register);
+
+void wiphy_unregister(struct wiphy *wiphy)
+{
+	struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy);
+
+	/* protect the device list */
+	mutex_lock(&cfg80211_drv_mutex);
+
+	BUG_ON(!list_empty(&drv->netdev_list));
+
+	/*
+	 * Try to grab drv->mtx. If a command is still in progress,
+	 * hopefully the driver will refuse it since it's tearing
+	 * down the device already. We wait for this command to complete
+	 * before unlinking the item from the list.
+	 * Note: as codified by the BUG_ON above we cannot get here if
+	 * a virtual interface is still associated. Hence, we can only
+	 * get to lock contention here if userspace issues a command
+	 * that identified the hardware by wiphy index.
+	 */
+	mutex_lock(&drv->mtx);
+	/* unlock again before freeing */
+	mutex_unlock(&drv->mtx);
+
+	list_del(&drv->list);
+	device_del(&drv->wiphy.dev);
+	debugfs_remove(drv->wiphy.debugfsdir);
+
+	mutex_unlock(&cfg80211_drv_mutex);
+}
+EXPORT_SYMBOL(wiphy_unregister);
+
+void cfg80211_dev_free(struct cfg80211_registered_device *drv)
+{
+	mutex_destroy(&drv->mtx);
+	mutex_destroy(&drv->devlist_mtx);
+	kfree(drv);
+}
+
+void wiphy_free(struct wiphy *wiphy)
+{
+	put_device(&wiphy->dev);
+}
+EXPORT_SYMBOL(wiphy_free);
+
+static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
+					 unsigned long state,
+					 void *ndev)
+{
+	struct net_device *dev = ndev;
+	struct cfg80211_registered_device *rdev;
+
+	if (!dev->ieee80211_ptr)
+		return 0;
+
+	rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy);
+
+	switch (state) {
+	case NETDEV_REGISTER:
+		mutex_lock(&rdev->devlist_mtx);
+		list_add(&dev->ieee80211_ptr->list, &rdev->netdev_list);
+		if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj,
+				      "phy80211")) {
+			printk(KERN_ERR "wireless: failed to add phy80211 "
+				"symlink to netdev!\n");
+		}
+		dev->ieee80211_ptr->netdev = dev;
+		mutex_unlock(&rdev->devlist_mtx);
+		break;
+	case NETDEV_UNREGISTER:
+		mutex_lock(&rdev->devlist_mtx);
+		if (!list_empty(&dev->ieee80211_ptr->list)) {
+			sysfs_remove_link(&dev->dev.kobj, "phy80211");
+			list_del_init(&dev->ieee80211_ptr->list);
+		}
+		mutex_unlock(&rdev->devlist_mtx);
+		break;
+	}
+
+	return 0;
+}
+
+static struct notifier_block cfg80211_netdev_notifier = {
+	.notifier_call = cfg80211_netdev_notifier_call,
+};
+
+static int cfg80211_init(void)
+{
+	int err = wiphy_sysfs_init();
+	if (err)
+		goto out_fail_sysfs;
+
+	err = register_netdevice_notifier(&cfg80211_netdev_notifier);
+	if (err)
+		goto out_fail_notifier;
+
+	ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL);
+
+	return 0;
+
+out_fail_notifier:
+	wiphy_sysfs_exit();
+out_fail_sysfs:
+	return err;
+}
+module_init(cfg80211_init);
+
+static void cfg80211_exit(void)
+{
+	debugfs_remove(ieee80211_debugfs_dir);
+	unregister_netdevice_notifier(&cfg80211_netdev_notifier);
+	wiphy_sysfs_exit();
+}
+module_exit(cfg80211_exit);
diff --git a/net/wireless/core.h b/net/wireless/core.h
new file mode 100644
index 0000000..158db1e
--- /dev/null
+++ b/net/wireless/core.h
@@ -0,0 +1,49 @@
+/*
+ * Wireless configuration interface internals.
+ *
+ * Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net>
+ */
+#ifndef __NET_WIRELESS_CORE_H
+#define __NET_WIRELESS_CORE_H
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <net/genetlink.h>
+#include <net/wireless.h>
+#include <net/cfg80211.h>
+
+struct cfg80211_registered_device {
+	struct cfg80211_ops *ops;
+	struct list_head list;
+	/* we hold this mutex during any call so that
+	 * we cannot do multiple calls at once, and also
+	 * to avoid the deregister call to proceed while
+	 * any call is in progress */
+	struct mutex mtx;
+
+	/* wiphy index, internal only */
+	int idx;
+
+	/* associate netdev list */
+	struct mutex devlist_mtx;
+	struct list_head netdev_list;
+
+	/* must be last because of the way we do wiphy_priv(),
+	 * and it should at least be aligned to NETDEV_ALIGN */
+	struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN)));
+};
+
+static inline
+struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy)
+{
+	BUG_ON(!wiphy);
+	return container_of(wiphy, struct cfg80211_registered_device, wiphy);
+}
+
+extern struct mutex cfg80211_drv_mutex;
+extern struct list_head cfg80211_drv_list;
+
+/* free object */
+extern void cfg80211_dev_free(struct cfg80211_registered_device *drv);
+
+#endif /* __NET_WIRELESS_CORE_H */
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
new file mode 100644
index 0000000..3ebae14
--- /dev/null
+++ b/net/wireless/sysfs.c
@@ -0,0 +1,80 @@
+/*
+ * This file provides /sys/class/ieee80211/<wiphy name>/
+ * and some default attributes.
+ *
+ * Copyright 2005-2006	Jiri Benc <jbenc@suse.cz>
+ * Copyright 2006	Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This file is GPLv2 as found in COPYING.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/nl80211.h>
+#include <linux/rtnetlink.h>
+#include <net/cfg80211.h>
+#include "sysfs.h"
+#include "core.h"
+
+static inline struct cfg80211_registered_device *dev_to_rdev(
+	struct device *dev)
+{
+	return container_of(dev, struct cfg80211_registered_device, wiphy.dev);
+}
+
+static ssize_t _show_index(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	return sprintf(buf, "%d\n", dev_to_rdev(dev)->idx);
+}
+
+static ssize_t _show_permaddr(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf)
+{
+	char *addr = dev_to_rdev(dev)->wiphy.perm_addr;
+
+	return sprintf(buf, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
+		       addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+}
+
+static struct device_attribute ieee80211_dev_attrs[] = {
+	__ATTR(index, S_IRUGO, _show_index, NULL),
+	__ATTR(macaddress, S_IRUGO, _show_permaddr, NULL),
+	{}
+};
+
+static void wiphy_dev_release(struct device *dev)
+{
+	struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
+
+	cfg80211_dev_free(rdev);
+}
+
+static int wiphy_uevent(struct device *dev, char **envp,
+			int num_envp, char *buf, int size)
+{
+	/* TODO, we probably need stuff here */
+	return 0;
+}
+
+struct class ieee80211_class = {
+	.name = "ieee80211",
+	.owner = THIS_MODULE,
+	.dev_release = wiphy_dev_release,
+	.dev_attrs = ieee80211_dev_attrs,
+#ifdef CONFIG_HOTPLUG
+	.dev_uevent = wiphy_uevent,
+#endif
+};
+
+int wiphy_sysfs_init(void)
+{
+	return class_register(&ieee80211_class);
+}
+
+void wiphy_sysfs_exit(void)
+{
+	class_unregister(&ieee80211_class);
+}
diff --git a/net/wireless/sysfs.h b/net/wireless/sysfs.h
new file mode 100644
index 0000000..65acbeb
--- /dev/null
+++ b/net/wireless/sysfs.h
@@ -0,0 +1,9 @@
+#ifndef __WIRELESS_SYSFS_H
+#define __WIRELESS_SYSFS_H
+
+extern int wiphy_sysfs_init(void);
+extern void wiphy_sysfs_exit(void);
+
+extern struct class ieee80211_class;
+
+#endif /* __WIRELESS_SYSFS_H */
diff --git a/net/wireless/wext.c b/net/wireless/wext.c
new file mode 100644
index 0000000..d6aaf65
--- /dev/null
+++ b/net/wireless/wext.c
@@ -0,0 +1,1509 @@
+/*
+ * This file implement the Wireless Extensions APIs.
+ *
+ * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
+ *
+ * (As all part of the Linux kernel, this file is GPL)
+ */
+
+/************************** DOCUMENTATION **************************/
+/*
+ * API definition :
+ * --------------
+ * See <linux/wireless.h> for details of the APIs and the rest.
+ *
+ * History :
+ * -------
+ *
+ * v1 - 5.12.01 - Jean II
+ *	o Created this file.
+ *
+ * v2 - 13.12.01 - Jean II
+ *	o Move /proc/net/wireless stuff from net/core/dev.c to here
+ *	o Make Wireless Extension IOCTLs go through here
+ *	o Added iw_handler handling ;-)
+ *	o Added standard ioctl description
+ *	o Initial dumb commit strategy based on orinoco.c
+ *
+ * v3 - 19.12.01 - Jean II
+ *	o Make sure we don't go out of standard_ioctl[] in ioctl_standard_call
+ *	o Add event dispatcher function
+ *	o Add event description
+ *	o Propagate events as rtnetlink IFLA_WIRELESS option
+ *	o Generate event on selected SET requests
+ *
+ * v4 - 18.04.02 - Jean II
+ *	o Fix stupid off by one in iw_ioctl_description : IW_ESSID_MAX_SIZE + 1
+ *
+ * v5 - 21.06.02 - Jean II
+ *	o Add IW_PRIV_TYPE_ADDR in priv_type_size (+cleanup)
+ *	o Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes
+ *	o Add IWEVCUSTOM for driver specific event/scanning token
+ *	o Turn on WE_STRICT_WRITE by default + kernel warning
+ *	o Fix WE_STRICT_WRITE in ioctl_export_private() (32 => iw_num)
+ *	o Fix off-by-one in test (extra_size <= IFNAMSIZ)
+ *
+ * v6 - 9.01.03 - Jean II
+ *	o Add common spy support : iw_handler_set_spy(), wireless_spy_update()
+ *	o Add enhanced spy support : iw_handler_set_thrspy() and event.
+ *	o Add WIRELESS_EXT version display in /proc/net/wireless
+ *
+ * v6 - 18.06.04 - Jean II
+ *	o Change get_spydata() method for added safety
+ *	o Remove spy #ifdef, they are always on -> cleaner code
+ *	o Allow any size GET request if user specifies length > max
+ *		and if request has IW_DESCR_FLAG_NOMAX flag or is SIOCGIWPRIV
+ *	o Start migrating get_wireless_stats to struct iw_handler_def
+ *	o Add wmb() in iw_handler_set_spy() for non-coherent archs/cpus
+ * Based on patch from Pavel Roskin <proski@gnu.org> :
+ *	o Fix kernel data leak to user space in private handler handling
+ *
+ * v7 - 18.3.05 - Jean II
+ *	o Remove (struct iw_point *)->pointer from events and streams
+ *	o Remove spy_offset from struct iw_handler_def
+ *	o Start deprecating dev->get_wireless_stats, output a warning
+ *	o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless
+ *	o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats)
+ *
+ * v8 - 17.02.06 - Jean II
+ *	o RtNetlink requests support (SET/GET)
+ *
+ * v8b - 03.08.06 - Herbert Xu
+ *	o Fix Wireless Event locking issues.
+ *
+ * v9 - 14.3.06 - Jean II
+ *	o Change length in ESSID and NICK to strlen() instead of strlen()+1
+ *	o Make standard_ioctl_num and standard_event_num unsigned
+ *	o Remove (struct net_device *)->get_wireless_stats()
+ *
+ * v10 - 16.3.07 - Jean II
+ *	o Prevent leaking of kernel space in stream on 64 bits.
+ */
+
+/***************************** INCLUDES *****************************/
+
+#include <linux/module.h>
+#include <linux/types.h>		/* off_t */
+#include <linux/netdevice.h>		/* struct ifreq, dev_get_by_name() */
+#include <linux/proc_fs.h>
+#include <linux/rtnetlink.h>		/* rtnetlink stuff */
+#include <linux/seq_file.h>
+#include <linux/init.h>			/* for __init */
+#include <linux/if_arp.h>		/* ARPHRD_ETHER */
+#include <linux/etherdevice.h>		/* compare_ether_addr */
+#include <linux/interrupt.h>
+
+#include <linux/wireless.h>		/* Pretty obvious */
+#include <net/iw_handler.h>		/* New driver API */
+#include <net/netlink.h>
+#include <net/wext.h>
+
+#include <asm/uaccess.h>		/* copy_to_user() */
+
+/************************* GLOBAL VARIABLES *************************/
+/*
+ * You should not use global variables, because of re-entrancy.
+ * On our case, it's only const, so it's OK...
+ */
+/*
+ * Meta-data about all the standard Wireless Extension request we
+ * know about.
+ */
+static const struct iw_ioctl_description standard_ioctl[] = {
+	[SIOCSIWCOMMIT	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_NULL,
+	},
+	[SIOCGIWNAME	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_CHAR,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWNWID	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+		.flags		= IW_DESCR_FLAG_EVENT,
+	},
+	[SIOCGIWNWID	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWFREQ	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_FREQ,
+		.flags		= IW_DESCR_FLAG_EVENT,
+	},
+	[SIOCGIWFREQ	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_FREQ,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWMODE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_UINT,
+		.flags		= IW_DESCR_FLAG_EVENT,
+	},
+	[SIOCGIWMODE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_UINT,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWSENS	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWSENS	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWRANGE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_NULL,
+	},
+	[SIOCGIWRANGE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= sizeof(struct iw_range),
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWPRIV	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_NULL,
+	},
+	[SIOCGIWPRIV	- SIOCIWFIRST] = { /* (handled directly by us) */
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct iw_priv_args),
+		.max_tokens	= 16,
+		.flags		= IW_DESCR_FLAG_NOMAX,
+	},
+	[SIOCSIWSTATS	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_NULL,
+	},
+	[SIOCGIWSTATS	- SIOCIWFIRST] = { /* (handled directly by us) */
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= sizeof(struct iw_statistics),
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWSPY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct sockaddr),
+		.max_tokens	= IW_MAX_SPY,
+	},
+	[SIOCGIWSPY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct sockaddr) +
+				  sizeof(struct iw_quality),
+		.max_tokens	= IW_MAX_SPY,
+	},
+	[SIOCSIWTHRSPY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct iw_thrspy),
+		.min_tokens	= 1,
+		.max_tokens	= 1,
+	},
+	[SIOCGIWTHRSPY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct iw_thrspy),
+		.min_tokens	= 1,
+		.max_tokens	= 1,
+	},
+	[SIOCSIWAP	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_ADDR,
+	},
+	[SIOCGIWAP	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_ADDR,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWMLME	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.min_tokens	= sizeof(struct iw_mlme),
+		.max_tokens	= sizeof(struct iw_mlme),
+	},
+	[SIOCGIWAPLIST	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct sockaddr) +
+				  sizeof(struct iw_quality),
+		.max_tokens	= IW_MAX_AP,
+		.flags		= IW_DESCR_FLAG_NOMAX,
+	},
+	[SIOCSIWSCAN	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.min_tokens	= 0,
+		.max_tokens	= sizeof(struct iw_scan_req),
+	},
+	[SIOCGIWSCAN	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_SCAN_MAX_DATA,
+		.flags		= IW_DESCR_FLAG_NOMAX,
+	},
+	[SIOCSIWESSID	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ESSID_MAX_SIZE,
+		.flags		= IW_DESCR_FLAG_EVENT,
+	},
+	[SIOCGIWESSID	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ESSID_MAX_SIZE,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWNICKN	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ESSID_MAX_SIZE,
+	},
+	[SIOCGIWNICKN	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ESSID_MAX_SIZE,
+	},
+	[SIOCSIWRATE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWRATE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWRTS	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWRTS	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWFRAG	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWFRAG	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWTXPOW	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWTXPOW	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWRETRY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWRETRY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWENCODE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ENCODING_TOKEN_MAX,
+		.flags		= IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
+	},
+	[SIOCGIWENCODE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ENCODING_TOKEN_MAX,
+		.flags		= IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
+	},
+	[SIOCSIWPOWER	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWPOWER	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWGENIE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_GENERIC_IE_MAX,
+	},
+	[SIOCGIWGENIE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_GENERIC_IE_MAX,
+	},
+	[SIOCSIWAUTH	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWAUTH	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWENCODEEXT - SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.min_tokens	= sizeof(struct iw_encode_ext),
+		.max_tokens	= sizeof(struct iw_encode_ext) +
+				  IW_ENCODING_TOKEN_MAX,
+	},
+	[SIOCGIWENCODEEXT - SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.min_tokens	= sizeof(struct iw_encode_ext),
+		.max_tokens	= sizeof(struct iw_encode_ext) +
+				  IW_ENCODING_TOKEN_MAX,
+	},
+	[SIOCSIWPMKSA - SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.min_tokens	= sizeof(struct iw_pmksa),
+		.max_tokens	= sizeof(struct iw_pmksa),
+	},
+};
+static const unsigned standard_ioctl_num = ARRAY_SIZE(standard_ioctl);
+
+/*
+ * Meta-data about all the additional standard Wireless Extension events
+ * we know about.
+ */
+static const struct iw_ioctl_description standard_event[] = {
+	[IWEVTXDROP	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_ADDR,
+	},
+	[IWEVQUAL	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_QUAL,
+	},
+	[IWEVCUSTOM	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_CUSTOM_MAX,
+	},
+	[IWEVREGISTERED	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_ADDR,
+	},
+	[IWEVEXPIRED	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_ADDR,
+	},
+	[IWEVGENIE	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_GENERIC_IE_MAX,
+	},
+	[IWEVMICHAELMICFAILURE	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= sizeof(struct iw_michaelmicfailure),
+	},
+	[IWEVASSOCREQIE	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_GENERIC_IE_MAX,
+	},
+	[IWEVASSOCRESPIE	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_GENERIC_IE_MAX,
+	},
+	[IWEVPMKIDCAND	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= sizeof(struct iw_pmkid_cand),
+	},
+};
+static const unsigned standard_event_num = ARRAY_SIZE(standard_event);
+
+/* Size (in bytes) of the various private data types */
+static const char iw_priv_type_size[] = {
+	0,				/* IW_PRIV_TYPE_NONE */
+	1,				/* IW_PRIV_TYPE_BYTE */
+	1,				/* IW_PRIV_TYPE_CHAR */
+	0,				/* Not defined */
+	sizeof(__u32),			/* IW_PRIV_TYPE_INT */
+	sizeof(struct iw_freq),		/* IW_PRIV_TYPE_FLOAT */
+	sizeof(struct sockaddr),	/* IW_PRIV_TYPE_ADDR */
+	0,				/* Not defined */
+};
+
+/* Size (in bytes) of various events */
+static const int event_type_size[] = {
+	IW_EV_LCP_LEN,			/* IW_HEADER_TYPE_NULL */
+	0,
+	IW_EV_CHAR_LEN,			/* IW_HEADER_TYPE_CHAR */
+	0,
+	IW_EV_UINT_LEN,			/* IW_HEADER_TYPE_UINT */
+	IW_EV_FREQ_LEN,			/* IW_HEADER_TYPE_FREQ */
+	IW_EV_ADDR_LEN,			/* IW_HEADER_TYPE_ADDR */
+	0,
+	IW_EV_POINT_LEN,		/* Without variable payload */
+	IW_EV_PARAM_LEN,		/* IW_HEADER_TYPE_PARAM */
+	IW_EV_QUAL_LEN,			/* IW_HEADER_TYPE_QUAL */
+};
+
+/* Size (in bytes) of various events, as packed */
+static const int event_type_pk_size[] = {
+	IW_EV_LCP_PK_LEN,		/* IW_HEADER_TYPE_NULL */
+	0,
+	IW_EV_CHAR_PK_LEN,		/* IW_HEADER_TYPE_CHAR */
+	0,
+	IW_EV_UINT_PK_LEN,		/* IW_HEADER_TYPE_UINT */
+	IW_EV_FREQ_PK_LEN,		/* IW_HEADER_TYPE_FREQ */
+	IW_EV_ADDR_PK_LEN,		/* IW_HEADER_TYPE_ADDR */
+	0,
+	IW_EV_POINT_PK_LEN,		/* Without variable payload */
+	IW_EV_PARAM_PK_LEN,		/* IW_HEADER_TYPE_PARAM */
+	IW_EV_QUAL_PK_LEN,		/* IW_HEADER_TYPE_QUAL */
+};
+
+/************************ COMMON SUBROUTINES ************************/
+/*
+ * Stuff that may be used in various place or doesn't fit in one
+ * of the section below.
+ */
+
+/* ---------------------------------------------------------------- */
+/*
+ * Return the driver handler associated with a specific Wireless Extension.
+ */
+static iw_handler get_handler(struct net_device *dev, unsigned int cmd)
+{
+	/* Don't "optimise" the following variable, it will crash */
+	unsigned int	index;		/* *MUST* be unsigned */
+
+	/* Check if we have some wireless handlers defined */
+	if (dev->wireless_handlers == NULL)
+		return NULL;
+
+	/* Try as a standard command */
+	index = cmd - SIOCIWFIRST;
+	if (index < dev->wireless_handlers->num_standard)
+		return dev->wireless_handlers->standard[index];
+
+	/* Try as a private command */
+	index = cmd - SIOCIWFIRSTPRIV;
+	if (index < dev->wireless_handlers->num_private)
+		return dev->wireless_handlers->private[index];
+
+	/* Not found */
+	return NULL;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Get statistics out of the driver
+ */
+static struct iw_statistics *get_wireless_stats(struct net_device *dev)
+{
+	/* New location */
+	if ((dev->wireless_handlers != NULL) &&
+	   (dev->wireless_handlers->get_wireless_stats != NULL))
+		return dev->wireless_handlers->get_wireless_stats(dev);
+
+	/* Not found */
+	return NULL;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Call the commit handler in the driver
+ * (if exist and if conditions are right)
+ *
+ * Note : our current commit strategy is currently pretty dumb,
+ * but we will be able to improve on that...
+ * The goal is to try to agreagate as many changes as possible
+ * before doing the commit. Drivers that will define a commit handler
+ * are usually those that need a reset after changing parameters, so
+ * we want to minimise the number of reset.
+ * A cool idea is to use a timer : at each "set" command, we re-set the
+ * timer, when the timer eventually fires, we call the driver.
+ * Hopefully, more on that later.
+ *
+ * Also, I'm waiting to see how many people will complain about the
+ * netif_running(dev) test. I'm open on that one...
+ * Hopefully, the driver will remember to do a commit in "open()" ;-)
+ */
+static int call_commit_handler(struct net_device *dev)
+{
+	if ((netif_running(dev)) &&
+	   (dev->wireless_handlers->standard[0] != NULL))
+		/* Call the commit handler on the driver */
+		return dev->wireless_handlers->standard[0](dev, NULL,
+							   NULL, NULL);
+	else
+		return 0;		/* Command completed successfully */
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Calculate size of private arguments
+ */
+static inline int get_priv_size(__u16	args)
+{
+	int	num = args & IW_PRIV_SIZE_MASK;
+	int	type = (args & IW_PRIV_TYPE_MASK) >> 12;
+
+	return num * iw_priv_type_size[type];
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Re-calculate the size of private arguments
+ */
+static inline int adjust_priv_size(__u16		args,
+				   union iwreq_data *	wrqu)
+{
+	int	num = wrqu->data.length;
+	int	max = args & IW_PRIV_SIZE_MASK;
+	int	type = (args & IW_PRIV_TYPE_MASK) >> 12;
+
+	/* Make sure the driver doesn't goof up */
+	if (max < num)
+		num = max;
+
+	return num * iw_priv_type_size[type];
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Standard Wireless Handler : get wireless stats
+ *	Allow programatic access to /proc/net/wireless even if /proc
+ *	doesn't exist... Also more efficient...
+ */
+static int iw_handler_get_iwstats(struct net_device *		dev,
+				  struct iw_request_info *	info,
+				  union iwreq_data *		wrqu,
+				  char *			extra)
+{
+	/* Get stats from the driver */
+	struct iw_statistics *stats;
+
+	stats = get_wireless_stats(dev);
+	if (stats) {
+		/* Copy statistics to extra */
+		memcpy(extra, stats, sizeof(struct iw_statistics));
+		wrqu->data.length = sizeof(struct iw_statistics);
+
+		/* Check if we need to clear the updated flag */
+		if (wrqu->data.flags != 0)
+			stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
+		return 0;
+	} else
+		return -EOPNOTSUPP;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Standard Wireless Handler : get iwpriv definitions
+ * Export the driver private handler definition
+ * They will be picked up by tools like iwpriv...
+ */
+static int iw_handler_get_private(struct net_device *		dev,
+				  struct iw_request_info *	info,
+				  union iwreq_data *		wrqu,
+				  char *			extra)
+{
+	/* Check if the driver has something to export */
+	if ((dev->wireless_handlers->num_private_args == 0) ||
+	   (dev->wireless_handlers->private_args == NULL))
+		return -EOPNOTSUPP;
+
+	/* Check if there is enough buffer up there */
+	if (wrqu->data.length < dev->wireless_handlers->num_private_args) {
+		/* User space can't know in advance how large the buffer
+		 * needs to be. Give it a hint, so that we can support
+		 * any size buffer we want somewhat efficiently... */
+		wrqu->data.length = dev->wireless_handlers->num_private_args;
+		return -E2BIG;
+	}
+
+	/* Set the number of available ioctls. */
+	wrqu->data.length = dev->wireless_handlers->num_private_args;
+
+	/* Copy structure to the user buffer. */
+	memcpy(extra, dev->wireless_handlers->private_args,
+	       sizeof(struct iw_priv_args) * wrqu->data.length);
+
+	return 0;
+}
+
+
+/******************** /proc/net/wireless SUPPORT ********************/
+/*
+ * The /proc/net/wireless file is a human readable user-space interface
+ * exporting various wireless specific statistics from the wireless devices.
+ * This is the most popular part of the Wireless Extensions ;-)
+ *
+ * This interface is a pure clone of /proc/net/dev (in net/core/dev.c).
+ * The content of the file is basically the content of "struct iw_statistics".
+ */
+
+#ifdef CONFIG_PROC_FS
+
+/* ---------------------------------------------------------------- */
+/*
+ * Print one entry (line) of /proc/net/wireless
+ */
+static void wireless_seq_printf_stats(struct seq_file *seq,
+				      struct net_device *dev)
+{
+	/* Get stats from the driver */
+	struct iw_statistics *stats = get_wireless_stats(dev);
+
+	if (stats) {
+		seq_printf(seq, "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d "
+				"%6d %6d   %6d\n",
+			   dev->name, stats->status, stats->qual.qual,
+			   stats->qual.updated & IW_QUAL_QUAL_UPDATED
+			   ? '.' : ' ',
+			   ((__s32) stats->qual.level) -
+			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
+			   stats->qual.updated & IW_QUAL_LEVEL_UPDATED
+			   ? '.' : ' ',
+			   ((__s32) stats->qual.noise) -
+			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
+			   stats->qual.updated & IW_QUAL_NOISE_UPDATED
+			   ? '.' : ' ',
+			   stats->discard.nwid, stats->discard.code,
+			   stats->discard.fragment, stats->discard.retries,
+			   stats->discard.misc, stats->miss.beacon);
+		stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
+	}
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Print info for /proc/net/wireless (print all entries)
+ */
+static int wireless_seq_show(struct seq_file *seq, void *v)
+{
+	if (v == SEQ_START_TOKEN)
+		seq_printf(seq, "Inter-| sta-|   Quality        |   Discarded "
+				"packets               | Missed | WE\n"
+				" face | tus | link level noise |  nwid  "
+				"crypt   frag  retry   misc | beacon | %d\n",
+			   WIRELESS_EXT);
+	else
+		wireless_seq_printf_stats(seq, v);
+	return 0;
+}
+
+static const struct seq_operations wireless_seq_ops = {
+	.start = dev_seq_start,
+	.next  = dev_seq_next,
+	.stop  = dev_seq_stop,
+	.show  = wireless_seq_show,
+};
+
+static int wireless_seq_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &wireless_seq_ops);
+}
+
+static const struct file_operations wireless_seq_fops = {
+	.owner	 = THIS_MODULE,
+	.open    = wireless_seq_open,
+	.read    = seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
+
+int __init wext_proc_init(void)
+{
+	/* Create /proc/net/wireless entry */
+	if (!proc_net_fops_create("wireless", S_IRUGO, &wireless_seq_fops))
+		return -ENOMEM;
+
+	return 0;
+}
+#endif	/* CONFIG_PROC_FS */
+
+/************************** IOCTL SUPPORT **************************/
+/*
+ * The original user space API to configure all those Wireless Extensions
+ * is through IOCTLs.
+ * In there, we check if we need to call the new driver API (iw_handler)
+ * or just call the driver ioctl handler.
+ */
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a standard Wireless Extension handler.
+ * We do various checks and also take care of moving data between
+ * user space and kernel space.
+ */
+static int ioctl_standard_call(struct net_device *	dev,
+			       struct ifreq *		ifr,
+			       unsigned int		cmd,
+			       iw_handler		handler)
+{
+	struct iwreq *				iwr = (struct iwreq *) ifr;
+	const struct iw_ioctl_description *	descr;
+	struct iw_request_info			info;
+	int					ret = -EINVAL;
+
+	/* Get the description of the IOCTL */
+	if ((cmd - SIOCIWFIRST) >= standard_ioctl_num)
+		return -EOPNOTSUPP;
+	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
+
+	/* Prepare the call */
+	info.cmd = cmd;
+	info.flags = 0;
+
+	/* Check if we have a pointer to user space data or not */
+	if (descr->header_type != IW_HEADER_TYPE_POINT) {
+
+		/* No extra arguments. Trivial to handle */
+		ret = handler(dev, &info, &(iwr->u), NULL);
+
+		/* Generate an event to notify listeners of the change */
+		if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
+		   ((ret == 0) || (ret == -EIWCOMMIT)))
+			wireless_send_event(dev, cmd, &(iwr->u), NULL);
+	} else {
+		char *	extra;
+		int	extra_size;
+		int	user_length = 0;
+		int	err;
+		int	essid_compat = 0;
+
+		/* Calculate space needed by arguments. Always allocate
+		 * for max space. Easier, and won't last long... */
+		extra_size = descr->max_tokens * descr->token_size;
+
+		/* Check need for ESSID compatibility for WE < 21 */
+		switch (cmd) {
+		case SIOCSIWESSID:
+		case SIOCGIWESSID:
+		case SIOCSIWNICKN:
+		case SIOCGIWNICKN:
+			if (iwr->u.data.length == descr->max_tokens + 1)
+				essid_compat = 1;
+			else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
+				char essid[IW_ESSID_MAX_SIZE + 1];
+
+				err = copy_from_user(essid, iwr->u.data.pointer,
+						     iwr->u.data.length *
+						     descr->token_size);
+				if (err)
+					return -EFAULT;
+
+				if (essid[iwr->u.data.length - 1] == '\0')
+					essid_compat = 1;
+			}
+			break;
+		default:
+			break;
+		}
+
+		iwr->u.data.length -= essid_compat;
+
+		/* Check what user space is giving us */
+		if (IW_IS_SET(cmd)) {
+			/* Check NULL pointer */
+			if ((iwr->u.data.pointer == NULL) &&
+			   (iwr->u.data.length != 0))
+				return -EFAULT;
+			/* Check if number of token fits within bounds */
+			if (iwr->u.data.length > descr->max_tokens)
+				return -E2BIG;
+			if (iwr->u.data.length < descr->min_tokens)
+				return -EINVAL;
+		} else {
+			/* Check NULL pointer */
+			if (iwr->u.data.pointer == NULL)
+				return -EFAULT;
+			/* Save user space buffer size for checking */
+			user_length = iwr->u.data.length;
+
+			/* Don't check if user_length > max to allow forward
+			 * compatibility. The test user_length < min is
+			 * implied by the test at the end. */
+
+			/* Support for very large requests */
+			if ((descr->flags & IW_DESCR_FLAG_NOMAX) &&
+			   (user_length > descr->max_tokens)) {
+				/* Allow userspace to GET more than max so
+				 * we can support any size GET requests.
+				 * There is still a limit : -ENOMEM. */
+				extra_size = user_length * descr->token_size;
+				/* Note : user_length is originally a __u16,
+				 * and token_size is controlled by us,
+				 * so extra_size won't get negative and
+				 * won't overflow... */
+			}
+		}
+
+		/* Create the kernel buffer */
+		/*    kzalloc ensures NULL-termination for essid_compat */
+		extra = kzalloc(extra_size, GFP_KERNEL);
+		if (extra == NULL)
+			return -ENOMEM;
+
+		/* If it is a SET, get all the extra data in here */
+		if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
+			err = copy_from_user(extra, iwr->u.data.pointer,
+					     iwr->u.data.length *
+					     descr->token_size);
+			if (err) {
+				kfree(extra);
+				return -EFAULT;
+			}
+		}
+
+		/* Call the handler */
+		ret = handler(dev, &info, &(iwr->u), extra);
+
+		iwr->u.data.length += essid_compat;
+
+		/* If we have something to return to the user */
+		if (!ret && IW_IS_GET(cmd)) {
+			/* Check if there is enough buffer up there */
+			if (user_length < iwr->u.data.length) {
+				kfree(extra);
+				return -E2BIG;
+			}
+
+			err = copy_to_user(iwr->u.data.pointer, extra,
+					   iwr->u.data.length *
+					   descr->token_size);
+			if (err)
+				ret =  -EFAULT;
+		}
+
+		/* Generate an event to notify listeners of the change */
+		if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
+		   ((ret == 0) || (ret == -EIWCOMMIT))) {
+			if (descr->flags & IW_DESCR_FLAG_RESTRICT)
+				/* If the event is restricted, don't
+				 * export the payload */
+				wireless_send_event(dev, cmd, &(iwr->u), NULL);
+			else
+				wireless_send_event(dev, cmd, &(iwr->u),
+						    extra);
+		}
+
+		/* Cleanup - I told you it wasn't that long ;-) */
+		kfree(extra);
+	}
+
+	/* Call commit handler if needed and defined */
+	if (ret == -EIWCOMMIT)
+		ret = call_commit_handler(dev);
+
+	/* Here, we will generate the appropriate event if needed */
+
+	return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a private Wireless Extension handler.
+ * We do various checks and also take care of moving data between
+ * user space and kernel space.
+ * It's not as nice and slimline as the standard wrapper. The cause
+ * is struct iw_priv_args, which was not really designed for the
+ * job we are going here.
+ *
+ * IMPORTANT : This function prevent to set and get data on the same
+ * IOCTL and enforce the SET/GET convention. Not doing it would be
+ * far too hairy...
+ * If you need to set and get data at the same time, please don't use
+ * a iw_handler but process it in your ioctl handler (i.e. use the
+ * old driver API).
+ */
+static int ioctl_private_call(struct net_device *dev, struct ifreq *ifr,
+			      unsigned int cmd, iw_handler handler)
+{
+	struct iwreq *			iwr = (struct iwreq *) ifr;
+	const struct iw_priv_args *	descr = NULL;
+	struct iw_request_info		info;
+	int				extra_size = 0;
+	int				i;
+	int				ret = -EINVAL;
+
+	/* Get the description of the IOCTL */
+	for (i = 0; i < dev->wireless_handlers->num_private_args; i++)
+		if (cmd == dev->wireless_handlers->private_args[i].cmd) {
+			descr = &(dev->wireless_handlers->private_args[i]);
+			break;
+		}
+
+	/* Compute the size of the set/get arguments */
+	if (descr != NULL) {
+		if (IW_IS_SET(cmd)) {
+			int	offset = 0;	/* For sub-ioctls */
+			/* Check for sub-ioctl handler */
+			if (descr->name[0] == '\0')
+				/* Reserve one int for sub-ioctl index */
+				offset = sizeof(__u32);
+
+			/* Size of set arguments */
+			extra_size = get_priv_size(descr->set_args);
+
+			/* Does it fits in iwr ? */
+			if ((descr->set_args & IW_PRIV_SIZE_FIXED) &&
+			   ((extra_size + offset) <= IFNAMSIZ))
+				extra_size = 0;
+		} else {
+			/* Size of get arguments */
+			extra_size = get_priv_size(descr->get_args);
+
+			/* Does it fits in iwr ? */
+			if ((descr->get_args & IW_PRIV_SIZE_FIXED) &&
+			   (extra_size <= IFNAMSIZ))
+				extra_size = 0;
+		}
+	}
+
+	/* Prepare the call */
+	info.cmd = cmd;
+	info.flags = 0;
+
+	/* Check if we have a pointer to user space data or not. */
+	if (extra_size == 0) {
+		/* No extra arguments. Trivial to handle */
+		ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u));
+	} else {
+		char *	extra;
+		int	err;
+
+		/* Check what user space is giving us */
+		if (IW_IS_SET(cmd)) {
+			/* Check NULL pointer */
+			if ((iwr->u.data.pointer == NULL) &&
+			   (iwr->u.data.length != 0))
+				return -EFAULT;
+
+			/* Does it fits within bounds ? */
+			if (iwr->u.data.length > (descr->set_args &
+						 IW_PRIV_SIZE_MASK))
+				return -E2BIG;
+		} else if (iwr->u.data.pointer == NULL)
+			return -EFAULT;
+
+		/* Always allocate for max space. Easier, and won't last
+		 * long... */
+		extra = kmalloc(extra_size, GFP_KERNEL);
+		if (extra == NULL)
+			return -ENOMEM;
+
+		/* If it is a SET, get all the extra data in here */
+		if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
+			err = copy_from_user(extra, iwr->u.data.pointer,
+					     extra_size);
+			if (err) {
+				kfree(extra);
+				return -EFAULT;
+			}
+		}
+
+		/* Call the handler */
+		ret = handler(dev, &info, &(iwr->u), extra);
+
+		/* If we have something to return to the user */
+		if (!ret && IW_IS_GET(cmd)) {
+
+			/* Adjust for the actual length if it's variable,
+			 * avoid leaking kernel bits outside. */
+			if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) {
+				extra_size = adjust_priv_size(descr->get_args,
+							      &(iwr->u));
+			}
+
+			err = copy_to_user(iwr->u.data.pointer, extra,
+					   extra_size);
+			if (err)
+				ret =  -EFAULT;
+		}
+
+		/* Cleanup - I told you it wasn't that long ;-) */
+		kfree(extra);
+	}
+
+
+	/* Call commit handler if needed and defined */
+	if (ret == -EIWCOMMIT)
+		ret = call_commit_handler(dev);
+
+	return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Main IOCTl dispatcher.
+ * Check the type of IOCTL and call the appropriate wrapper...
+ */
+static int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
+{
+	struct net_device *dev;
+	iw_handler	handler;
+
+	/* Permissions are already checked in dev_ioctl() before calling us.
+	 * The copy_to/from_user() of ifr is also dealt with in there */
+
+	/* Make sure the device exist */
+	if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL)
+		return -ENODEV;
+
+	/* A bunch of special cases, then the generic case...
+	 * Note that 'cmd' is already filtered in dev_ioctl() with
+	 * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
+	if (cmd == SIOCGIWSTATS)
+		return ioctl_standard_call(dev, ifr, cmd,
+					   &iw_handler_get_iwstats);
+
+	if (cmd == SIOCGIWPRIV && dev->wireless_handlers)
+		return ioctl_standard_call(dev, ifr, cmd,
+					   &iw_handler_get_private);
+
+	/* Basic check */
+	if (!netif_device_present(dev))
+		return -ENODEV;
+
+	/* New driver API : try to find the handler */
+	handler = get_handler(dev, cmd);
+	if (handler) {
+		/* Standard and private are not the same */
+		if (cmd < SIOCIWFIRSTPRIV)
+			return ioctl_standard_call(dev, ifr, cmd, handler);
+		else
+			return ioctl_private_call(dev, ifr, cmd, handler);
+	}
+	/* Old driver API : call driver ioctl handler */
+	if (dev->do_ioctl)
+		return dev->do_ioctl(dev, ifr, cmd);
+	return -EOPNOTSUPP;
+}
+
+/* entry point from dev ioctl */
+int wext_handle_ioctl(struct ifreq *ifr, unsigned int cmd,
+		      void __user *arg)
+{
+	int ret;
+
+	/* If command is `set a parameter', or
+	 * `get the encoding parameters', check if
+	 * the user has the right to do it */
+	if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE || cmd == SIOCGIWENCODEEXT)
+	    && !capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	dev_load(ifr->ifr_name);
+	rtnl_lock();
+	ret = wireless_process_ioctl(ifr, cmd);
+	rtnl_unlock();
+	if (IW_IS_GET(cmd) && copy_to_user(arg, ifr, sizeof(struct ifreq)))
+		return -EFAULT;
+	return ret;
+}
+
+/************************* EVENT PROCESSING *************************/
+/*
+ * Process events generated by the wireless layer or the driver.
+ * Most often, the event will be propagated through rtnetlink
+ */
+
+/* ---------------------------------------------------------------- */
+/*
+ * Locking...
+ * ----------
+ *
+ * Thanks to Herbert Xu <herbert@gondor.apana.org.au> for fixing
+ * the locking issue in here and implementing this code !
+ *
+ * The issue : wireless_send_event() is often called in interrupt context,
+ * while the Netlink layer can never be called in interrupt context.
+ * The fully formed RtNetlink events are queued, and then a tasklet is run
+ * to feed those to Netlink.
+ * The skb_queue is interrupt safe, and its lock is not held while calling
+ * Netlink, so there is no possibility of dealock.
+ * Jean II
+ */
+
+static struct sk_buff_head wireless_nlevent_queue;
+
+static int __init wireless_nlevent_init(void)
+{
+	skb_queue_head_init(&wireless_nlevent_queue);
+	return 0;
+}
+
+subsys_initcall(wireless_nlevent_init);
+
+static void wireless_nlevent_process(unsigned long data)
+{
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&wireless_nlevent_queue)))
+		rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
+}
+
+static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0);
+
+/* ---------------------------------------------------------------- */
+/*
+ * Fill a rtnetlink message with our event data.
+ * Note that we propage only the specified event and don't dump the
+ * current wireless config. Dumping the wireless config is far too
+ * expensive (for each parameter, the driver need to query the hardware).
+ */
+static int rtnetlink_fill_iwinfo(struct sk_buff *skb, struct net_device *dev,
+				 int type, char *event, int event_len)
+{
+	struct ifinfomsg *r;
+	struct nlmsghdr  *nlh;
+	unsigned char	 *b = skb_tail_pointer(skb);
+
+	nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r));
+	r = NLMSG_DATA(nlh);
+	r->ifi_family = AF_UNSPEC;
+	r->__ifi_pad = 0;
+	r->ifi_type = dev->type;
+	r->ifi_index = dev->ifindex;
+	r->ifi_flags = dev_get_flags(dev);
+	r->ifi_change = 0;	/* Wireless changes don't affect those flags */
+
+	/* Add the wireless events in the netlink packet */
+	RTA_PUT(skb, IFLA_WIRELESS, event_len, event);
+
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
+	return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+	nlmsg_trim(skb, b);
+	return -1;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Create and broadcast and send it on the standard rtnetlink socket
+ * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c
+ * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field
+ * within a RTM_NEWLINK event.
+ */
+static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len)
+{
+	struct sk_buff *skb;
+	int size = NLMSG_GOODSIZE;
+
+	skb = alloc_skb(size, GFP_ATOMIC);
+	if (!skb)
+		return;
+
+	if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK,
+				  event, event_len) < 0) {
+		kfree_skb(skb);
+		return;
+	}
+	NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
+	skb_queue_tail(&wireless_nlevent_queue, skb);
+	tasklet_schedule(&wireless_nlevent_tasklet);
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Main event dispatcher. Called from other parts and drivers.
+ * Send the event on the appropriate channels.
+ * May be called from interrupt context.
+ */
+void wireless_send_event(struct net_device *	dev,
+			 unsigned int		cmd,
+			 union iwreq_data *	wrqu,
+			 char *			extra)
+{
+	const struct iw_ioctl_description *	descr = NULL;
+	int extra_len = 0;
+	struct iw_event  *event;		/* Mallocated whole event */
+	int event_len;				/* Its size */
+	int hdr_len;				/* Size of the event header */
+	int wrqu_off = 0;			/* Offset in wrqu */
+	/* Don't "optimise" the following variable, it will crash */
+	unsigned	cmd_index;		/* *MUST* be unsigned */
+
+	/* Get the description of the Event */
+	if (cmd <= SIOCIWLAST) {
+		cmd_index = cmd - SIOCIWFIRST;
+		if (cmd_index < standard_ioctl_num)
+			descr = &(standard_ioctl[cmd_index]);
+	} else {
+		cmd_index = cmd - IWEVFIRST;
+		if (cmd_index < standard_event_num)
+			descr = &(standard_event[cmd_index]);
+	}
+	/* Don't accept unknown events */
+	if (descr == NULL) {
+		/* Note : we don't return an error to the driver, because
+		 * the driver would not know what to do about it. It can't
+		 * return an error to the user, because the event is not
+		 * initiated by a user request.
+		 * The best the driver could do is to log an error message.
+		 * We will do it ourselves instead...
+		 */
+		printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n",
+		       dev->name, cmd);
+		return;
+	}
+
+	/* Check extra parameters and set extra_len */
+	if (descr->header_type == IW_HEADER_TYPE_POINT) {
+		/* Check if number of token fits within bounds */
+		if (wrqu->data.length > descr->max_tokens) {
+			printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length);
+			return;
+		}
+		if (wrqu->data.length < descr->min_tokens) {
+			printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length);
+			return;
+		}
+		/* Calculate extra_len - extra is NULL for restricted events */
+		if (extra != NULL)
+			extra_len = wrqu->data.length * descr->token_size;
+		/* Always at an offset in wrqu */
+		wrqu_off = IW_EV_POINT_OFF;
+	}
+
+	/* Total length of the event */
+	hdr_len = event_type_size[descr->header_type];
+	event_len = hdr_len + extra_len;
+
+	/* Create temporary buffer to hold the event */
+	event = kmalloc(event_len, GFP_ATOMIC);
+	if (event == NULL)
+		return;
+
+	/* Fill event */
+	event->len = event_len;
+	event->cmd = cmd;
+	memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);
+	if (extra)
+		memcpy(((char *) event) + hdr_len, extra, extra_len);
+
+	/* Send via the RtNetlink event channel */
+	rtmsg_iwinfo(dev, (char *) event, event_len);
+
+	/* Cleanup */
+	kfree(event);
+
+	return;		/* Always success, I guess ;-) */
+}
+EXPORT_SYMBOL(wireless_send_event);
+
+/********************** ENHANCED IWSPY SUPPORT **********************/
+/*
+ * In the old days, the driver was handling spy support all by itself.
+ * Now, the driver can delegate this task to Wireless Extensions.
+ * It needs to use those standard spy iw_handler in struct iw_handler_def,
+ * push data to us via wireless_spy_update() and include struct iw_spy_data
+ * in its private part (and export it in net_device->wireless_data->spy_data).
+ * One of the main advantage of centralising spy support here is that
+ * it becomes much easier to improve and extend it without having to touch
+ * the drivers. One example is the addition of the Spy-Threshold events.
+ */
+
+/* ---------------------------------------------------------------- */
+/*
+ * Return the pointer to the spy data in the driver.
+ * Because this is called on the Rx path via wireless_spy_update(),
+ * we want it to be efficient...
+ */
+static inline struct iw_spy_data *get_spydata(struct net_device *dev)
+{
+	/* This is the new way */
+	if (dev->wireless_data)
+		return dev->wireless_data->spy_data;
+	return NULL;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : set Spy List
+ */
+int iw_handler_set_spy(struct net_device *	dev,
+		       struct iw_request_info *	info,
+		       union iwreq_data *	wrqu,
+		       char *			extra)
+{
+	struct iw_spy_data *	spydata = get_spydata(dev);
+	struct sockaddr *	address = (struct sockaddr *) extra;
+
+	/* Make sure driver is not buggy or using the old API */
+	if (!spydata)
+		return -EOPNOTSUPP;
+
+	/* Disable spy collection while we copy the addresses.
+	 * While we copy addresses, any call to wireless_spy_update()
+	 * will NOP. This is OK, as anyway the addresses are changing. */
+	spydata->spy_number = 0;
+
+	/* We want to operate without locking, because wireless_spy_update()
+	 * most likely will happen in the interrupt handler, and therefore
+	 * have its own locking constraints and needs performance.
+	 * The rtnl_lock() make sure we don't race with the other iw_handlers.
+	 * This make sure wireless_spy_update() "see" that the spy list
+	 * is temporarily disabled. */
+	smp_wmb();
+
+	/* Are there are addresses to copy? */
+	if (wrqu->data.length > 0) {
+		int i;
+
+		/* Copy addresses */
+		for (i = 0; i < wrqu->data.length; i++)
+			memcpy(spydata->spy_address[i], address[i].sa_data,
+			       ETH_ALEN);
+		/* Reset stats */
+		memset(spydata->spy_stat, 0,
+		       sizeof(struct iw_quality) * IW_MAX_SPY);
+	}
+
+	/* Make sure above is updated before re-enabling */
+	smp_wmb();
+
+	/* Enable addresses */
+	spydata->spy_number = wrqu->data.length;
+
+	return 0;
+}
+EXPORT_SYMBOL(iw_handler_set_spy);
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : get Spy List
+ */
+int iw_handler_get_spy(struct net_device *	dev,
+		       struct iw_request_info *	info,
+		       union iwreq_data *	wrqu,
+		       char *			extra)
+{
+	struct iw_spy_data *	spydata = get_spydata(dev);
+	struct sockaddr *	address = (struct sockaddr *) extra;
+	int			i;
+
+	/* Make sure driver is not buggy or using the old API */
+	if (!spydata)
+		return -EOPNOTSUPP;
+
+	wrqu->data.length = spydata->spy_number;
+
+	/* Copy addresses. */
+	for (i = 0; i < spydata->spy_number; i++) 	{
+		memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
+		address[i].sa_family = AF_UNIX;
+	}
+	/* Copy stats to the user buffer (just after). */
+	if (spydata->spy_number > 0)
+		memcpy(extra  + (sizeof(struct sockaddr) *spydata->spy_number),
+		       spydata->spy_stat,
+		       sizeof(struct iw_quality) * spydata->spy_number);
+	/* Reset updated flags. */
+	for (i = 0; i < spydata->spy_number; i++)
+		spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
+	return 0;
+}
+EXPORT_SYMBOL(iw_handler_get_spy);
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : set spy threshold
+ */
+int iw_handler_set_thrspy(struct net_device *	dev,
+			  struct iw_request_info *info,
+			  union iwreq_data *	wrqu,
+			  char *		extra)
+{
+	struct iw_spy_data *	spydata = get_spydata(dev);
+	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra;
+
+	/* Make sure driver is not buggy or using the old API */
+	if (!spydata)
+		return -EOPNOTSUPP;
+
+	/* Just do it */
+	memcpy(&(spydata->spy_thr_low), &(threshold->low),
+	       2 * sizeof(struct iw_quality));
+
+	/* Clear flag */
+	memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
+
+	return 0;
+}
+EXPORT_SYMBOL(iw_handler_set_thrspy);
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : get spy threshold
+ */
+int iw_handler_get_thrspy(struct net_device *	dev,
+			  struct iw_request_info *info,
+			  union iwreq_data *	wrqu,
+			  char *		extra)
+{
+	struct iw_spy_data *	spydata = get_spydata(dev);
+	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra;
+
+	/* Make sure driver is not buggy or using the old API */
+	if (!spydata)
+		return -EOPNOTSUPP;
+
+	/* Just do it */
+	memcpy(&(threshold->low), &(spydata->spy_thr_low),
+	       2 * sizeof(struct iw_quality));
+
+	return 0;
+}
+EXPORT_SYMBOL(iw_handler_get_thrspy);
+
+/*------------------------------------------------------------------*/
+/*
+ * Prepare and send a Spy Threshold event
+ */
+static void iw_send_thrspy_event(struct net_device *	dev,
+				 struct iw_spy_data *	spydata,
+				 unsigned char *	address,
+				 struct iw_quality *	wstats)
+{
+	union iwreq_data	wrqu;
+	struct iw_thrspy	threshold;
+
+	/* Init */
+	wrqu.data.length = 1;
+	wrqu.data.flags = 0;
+	/* Copy address */
+	memcpy(threshold.addr.sa_data, address, ETH_ALEN);
+	threshold.addr.sa_family = ARPHRD_ETHER;
+	/* Copy stats */
+	memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));
+	/* Copy also thresholds */
+	memcpy(&(threshold.low), &(spydata->spy_thr_low),
+	       2 * sizeof(struct iw_quality));
+
+	/* Send event to user space */
+	wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Call for the driver to update the spy data.
+ * For now, the spy data is a simple array. As the size of the array is
+ * small, this is good enough. If we wanted to support larger number of
+ * spy addresses, we should use something more efficient...
+ */
+void wireless_spy_update(struct net_device *	dev,
+			 unsigned char *	address,
+			 struct iw_quality *	wstats)
+{
+	struct iw_spy_data *	spydata = get_spydata(dev);
+	int			i;
+	int			match = -1;
+
+	/* Make sure driver is not buggy or using the old API */
+	if (!spydata)
+		return;
+
+	/* Update all records that match */
+	for (i = 0; i < spydata->spy_number; i++)
+		if (!compare_ether_addr(address, spydata->spy_address[i])) {
+			memcpy(&(spydata->spy_stat[i]), wstats,
+			       sizeof(struct iw_quality));
+			match = i;
+		}
+
+	/* Generate an event if we cross the spy threshold.
+	 * To avoid event storms, we have a simple hysteresis : we generate
+	 * event only when we go under the low threshold or above the
+	 * high threshold. */
+	if (match >= 0) {
+		if (spydata->spy_thr_under[match]) {
+			if (wstats->level > spydata->spy_thr_high.level) {
+				spydata->spy_thr_under[match] = 0;
+				iw_send_thrspy_event(dev, spydata,
+						     address, wstats);
+			}
+		} else {
+			if (wstats->level < spydata->spy_thr_low.level) {
+				spydata->spy_thr_under[match] = 1;
+				iw_send_thrspy_event(dev, spydata,
+						     address, wstats);
+			}
+		}
+	}
+}
+EXPORT_SYMBOL(wireless_spy_update);
diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
index e62ba41..0d6002fc 100644
--- a/net/x25/af_x25.c
+++ b/net/x25/af_x25.c
@@ -951,7 +951,7 @@
 	 *	Incoming Call User Data.
 	 */
 	if (skb->len >= 0) {
-		memcpy(makex25->calluserdata.cuddata, skb->data, skb->len);
+		skb_copy_from_linear_data(skb, makex25->calluserdata.cuddata, skb->len);
 		makex25->calluserdata.cudlength = skb->len;
 	}
 
@@ -1058,9 +1058,10 @@
 	 */
 	SOCK_DEBUG(sk, "x25_sendmsg: Copying user data\n");
 
-	asmptr = skb->h.raw = skb_put(skb, len);
+	skb_reset_transport_header(skb);
+	skb_put(skb, len);
 
-	rc = memcpy_fromiovec(asmptr, msg->msg_iov, len);
+	rc = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len);
 	if (rc)
 		goto out_kfree_skb;
 
@@ -1210,8 +1211,7 @@
 		}
 	}
 
-	skb->h.raw = skb->data;
-
+	skb_reset_transport_header(skb);
 	copied = skb->len;
 
 	if (copied > size) {
@@ -1280,6 +1280,12 @@
 				rc = sock_get_timestamp(sk,
 						(struct timeval __user *)argp);
 			break;
+		case SIOCGSTAMPNS:
+			rc = -EINVAL;
+			if (sk)
+				rc = sock_get_timestampns(sk,
+						(struct timespec __user *)argp);
+			break;
 		case SIOCGIFADDR:
 		case SIOCSIFADDR:
 		case SIOCGIFDSTADDR:
@@ -1521,6 +1527,12 @@
 			rc = compat_sock_get_timestamp(sk,
 					(struct timeval __user*)argp);
 		break;
+	case SIOCGSTAMPNS:
+		rc = -EINVAL;
+		if (sk)
+			rc = compat_sock_get_timestampns(sk,
+					(struct timespec __user*)argp);
+		break;
 	case SIOCGIFADDR:
 	case SIOCSIFADDR:
 	case SIOCGIFDSTADDR:
diff --git a/net/x25/x25_dev.c b/net/x25/x25_dev.c
index c7221de..848a6b6 100644
--- a/net/x25/x25_dev.c
+++ b/net/x25/x25_dev.c
@@ -48,7 +48,7 @@
 	if ((sk = x25_find_socket(lci, nb)) != NULL) {
 		int queued = 1;
 
-		skb->h.raw = skb->data;
+		skb_reset_transport_header(skb);
 		bh_lock_sock(sk);
 		if (!sock_owned_by_user(sk)) {
 			queued = x25_process_rx_frame(sk, skb);
@@ -191,7 +191,7 @@
 {
 	unsigned char *dptr;
 
-	skb->nh.raw = skb->data;
+	skb_reset_network_header(skb);
 
 	switch (nb->dev->type) {
 		case ARPHRD_X25:
diff --git a/net/x25/x25_in.c b/net/x25/x25_in.c
index c5239fc..1c88762 100644
--- a/net/x25/x25_in.c
+++ b/net/x25/x25_in.c
@@ -53,17 +53,20 @@
 
 		skb_queue_tail(&x25->fragment_queue, skb);
 
-		skbn->h.raw = skbn->data;
+		skb_reset_transport_header(skbn);
 
 		skbo = skb_dequeue(&x25->fragment_queue);
-		memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
+		skb_copy_from_linear_data(skbo, skb_put(skbn, skbo->len),
+					  skbo->len);
 		kfree_skb(skbo);
 
 		while ((skbo =
 			skb_dequeue(&x25->fragment_queue)) != NULL) {
 			skb_pull(skbo, (x25->neighbour->extended) ?
 					X25_EXT_MIN_LEN : X25_STD_MIN_LEN);
-			memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
+			skb_copy_from_linear_data(skbo,
+						  skb_put(skbn, skbo->len),
+						  skbo->len);
 			kfree_skb(skbo);
 		}
 
@@ -112,8 +115,9 @@
 			 *	Copy any Call User Data.
 			 */
 			if (skb->len >= 0) {
-				memcpy(x25->calluserdata.cuddata, skb->data,
-				       skb->len);
+				skb_copy_from_linear_data(skb,
+					      x25->calluserdata.cuddata,
+					      skb->len);
 				x25->calluserdata.cudlength = skb->len;
 			}
 			if (!sock_flag(sk, SOCK_DEAD))
diff --git a/net/x25/x25_out.c b/net/x25/x25_out.c
index 6f57378..2b96b52 100644
--- a/net/x25/x25_out.c
+++ b/net/x25/x25_out.c
@@ -61,7 +61,7 @@
 
 	if (skb->len - header_len > max_len) {
 		/* Save a copy of the Header */
-		memcpy(header, skb->data, header_len);
+		skb_copy_from_linear_data(skb, header, header_len);
 		skb_pull(skb, header_len);
 
 		frontlen = skb_headroom(skb);
@@ -84,12 +84,12 @@
 			len = max_len > skb->len ? skb->len : max_len;
 
 			/* Copy the user data */
-			memcpy(skb_put(skbn, len), skb->data, len);
+			skb_copy_from_linear_data(skb, skb_put(skbn, len), len);
 			skb_pull(skb, len);
 
 			/* Duplicate the Header */
 			skb_push(skbn, header_len);
-			memcpy(skbn->data, header, header_len);
+			skb_copy_to_linear_data(skbn, header, header_len);
 
 			if (skb->len > 0) {
 				if (x25->neighbour->extended)
diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c
index f373a8a..6249a94 100644
--- a/net/xfrm/xfrm_algo.c
+++ b/net/xfrm/xfrm_algo.c
@@ -612,175 +612,6 @@
 
 #if defined(CONFIG_INET_ESP) || defined(CONFIG_INET_ESP_MODULE) || defined(CONFIG_INET6_ESP) || defined(CONFIG_INET6_ESP_MODULE)
 
-/* Looking generic it is not used in another places. */
-
-int
-skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
-{
-	int start = skb_headlen(skb);
-	int i, copy = start - offset;
-	int elt = 0;
-
-	if (copy > 0) {
-		if (copy > len)
-			copy = len;
-		sg[elt].page = virt_to_page(skb->data + offset);
-		sg[elt].offset = (unsigned long)(skb->data + offset) % PAGE_SIZE;
-		sg[elt].length = copy;
-		elt++;
-		if ((len -= copy) == 0)
-			return elt;
-		offset += copy;
-	}
-
-	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
-		int end;
-
-		BUG_TRAP(start <= offset + len);
-
-		end = start + skb_shinfo(skb)->frags[i].size;
-		if ((copy = end - offset) > 0) {
-			skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
-
-			if (copy > len)
-				copy = len;
-			sg[elt].page = frag->page;
-			sg[elt].offset = frag->page_offset+offset-start;
-			sg[elt].length = copy;
-			elt++;
-			if (!(len -= copy))
-				return elt;
-			offset += copy;
-		}
-		start = end;
-	}
-
-	if (skb_shinfo(skb)->frag_list) {
-		struct sk_buff *list = skb_shinfo(skb)->frag_list;
-
-		for (; list; list = list->next) {
-			int end;
-
-			BUG_TRAP(start <= offset + len);
-
-			end = start + list->len;
-			if ((copy = end - offset) > 0) {
-				if (copy > len)
-					copy = len;
-				elt += skb_to_sgvec(list, sg+elt, offset - start, copy);
-				if ((len -= copy) == 0)
-					return elt;
-				offset += copy;
-			}
-			start = end;
-		}
-	}
-	BUG_ON(len);
-	return elt;
-}
-EXPORT_SYMBOL_GPL(skb_to_sgvec);
-
-/* Check that skb data bits are writable. If they are not, copy data
- * to newly created private area. If "tailbits" is given, make sure that
- * tailbits bytes beyond current end of skb are writable.
- *
- * Returns amount of elements of scatterlist to load for subsequent
- * transformations and pointer to writable trailer skb.
- */
-
-int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer)
-{
-	int copyflag;
-	int elt;
-	struct sk_buff *skb1, **skb_p;
-
-	/* If skb is cloned or its head is paged, reallocate
-	 * head pulling out all the pages (pages are considered not writable
-	 * at the moment even if they are anonymous).
-	 */
-	if ((skb_cloned(skb) || skb_shinfo(skb)->nr_frags) &&
-	    __pskb_pull_tail(skb, skb_pagelen(skb)-skb_headlen(skb)) == NULL)
-		return -ENOMEM;
-
-	/* Easy case. Most of packets will go this way. */
-	if (!skb_shinfo(skb)->frag_list) {
-		/* A little of trouble, not enough of space for trailer.
-		 * This should not happen, when stack is tuned to generate
-		 * good frames. OK, on miss we reallocate and reserve even more
-		 * space, 128 bytes is fair. */
-
-		if (skb_tailroom(skb) < tailbits &&
-		    pskb_expand_head(skb, 0, tailbits-skb_tailroom(skb)+128, GFP_ATOMIC))
-			return -ENOMEM;
-
-		/* Voila! */
-		*trailer = skb;
-		return 1;
-	}
-
-	/* Misery. We are in troubles, going to mincer fragments... */
-
-	elt = 1;
-	skb_p = &skb_shinfo(skb)->frag_list;
-	copyflag = 0;
-
-	while ((skb1 = *skb_p) != NULL) {
-		int ntail = 0;
-
-		/* The fragment is partially pulled by someone,
-		 * this can happen on input. Copy it and everything
-		 * after it. */
-
-		if (skb_shared(skb1))
-			copyflag = 1;
-
-		/* If the skb is the last, worry about trailer. */
-
-		if (skb1->next == NULL && tailbits) {
-			if (skb_shinfo(skb1)->nr_frags ||
-			    skb_shinfo(skb1)->frag_list ||
-			    skb_tailroom(skb1) < tailbits)
-				ntail = tailbits + 128;
-		}
-
-		if (copyflag ||
-		    skb_cloned(skb1) ||
-		    ntail ||
-		    skb_shinfo(skb1)->nr_frags ||
-		    skb_shinfo(skb1)->frag_list) {
-			struct sk_buff *skb2;
-
-			/* Fuck, we are miserable poor guys... */
-			if (ntail == 0)
-				skb2 = skb_copy(skb1, GFP_ATOMIC);
-			else
-				skb2 = skb_copy_expand(skb1,
-						       skb_headroom(skb1),
-						       ntail,
-						       GFP_ATOMIC);
-			if (unlikely(skb2 == NULL))
-				return -ENOMEM;
-
-			if (skb1->sk)
-				skb_set_owner_w(skb2, skb1->sk);
-
-			/* Looking around. Are we still alive?
-			 * OK, link new skb, drop old one */
-
-			skb2->next = skb1->next;
-			*skb_p = skb2;
-			kfree_skb(skb1);
-			skb1 = skb2;
-		}
-		elt++;
-		*trailer = skb1;
-		skb_p = &skb1->next;
-	}
-
-	return elt;
-}
-EXPORT_SYMBOL_GPL(skb_cow_data);
-
 void *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len)
 {
 	if (tail != skb) {
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index ee15bda..5c46958 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -62,7 +62,7 @@
 	case IPPROTO_COMP:
 		if (!pskb_may_pull(skb, sizeof(struct ip_comp_hdr)))
 			return -EINVAL;
-		*spi = htonl(ntohs(*(__be16*)(skb->h.raw + 2)));
+		*spi = htonl(ntohs(*(__be16*)(skb_transport_header(skb) + 2)));
 		*seq = 0;
 		return 0;
 	default:
@@ -72,8 +72,8 @@
 	if (!pskb_may_pull(skb, 16))
 		return -EINVAL;
 
-	*spi = *(__be32*)(skb->h.raw + offset);
-	*seq = *(__be32*)(skb->h.raw + offset_seq);
+	*spi = *(__be32*)(skb_transport_header(skb) + offset);
+	*seq = *(__be32*)(skb_transport_header(skb) + offset_seq);
 	return 0;
 }
 EXPORT_SYMBOL(xfrm_parse_spi);
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 785c3e3..263e34e 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -268,7 +268,7 @@
 static void xfrm_policy_timer(unsigned long data)
 {
 	struct xfrm_policy *xp = (struct xfrm_policy*)data;
-	unsigned long now = (unsigned long)xtime.tv_sec;
+	unsigned long now = get_seconds();
 	long next = LONG_MAX;
 	int warn = 0;
 	int dir;
@@ -579,8 +579,22 @@
 	return 0;
 }
 
-static DEFINE_MUTEX(hash_resize_mutex);
+void xfrm_spd_getinfo(struct xfrm_spdinfo *si)
+{
+	read_lock_bh(&xfrm_policy_lock);
+	si->incnt = xfrm_policy_count[XFRM_POLICY_IN];
+	si->outcnt = xfrm_policy_count[XFRM_POLICY_OUT];
+	si->fwdcnt = xfrm_policy_count[XFRM_POLICY_FWD];
+	si->inscnt = xfrm_policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX];
+	si->outscnt = xfrm_policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX];
+	si->fwdscnt = xfrm_policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX];
+	si->spdhcnt = xfrm_idx_hmask;
+	si->spdhmcnt = xfrm_policy_hashmax;
+	read_unlock_bh(&xfrm_policy_lock);
+}
+EXPORT_SYMBOL(xfrm_spd_getinfo);
 
+static DEFINE_MUTEX(hash_resize_mutex);
 static void xfrm_hash_resize(struct work_struct *__unused)
 {
 	int dir, total;
@@ -690,7 +704,7 @@
 	}
 	policy->index = delpol ? delpol->index : xfrm_gen_index(policy->type, dir);
 	hlist_add_head(&policy->byidx, xfrm_policy_byidx+idx_hash(policy->index));
-	policy->curlft.add_time = (unsigned long)xtime.tv_sec;
+	policy->curlft.add_time = get_seconds();
 	policy->curlft.use_time = 0;
 	if (!mod_timer(&policy->timer, jiffies + HZ))
 		xfrm_pol_hold(policy);
@@ -1049,7 +1063,7 @@
 		return FLOW_DIR_OUT;
 	case XFRM_POLICY_FWD:
 		return FLOW_DIR_FWD;
-	};
+	}
 }
 
 static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi *fl)
@@ -1133,7 +1147,7 @@
 	old_pol = sk->sk_policy[dir];
 	sk->sk_policy[dir] = pol;
 	if (pol) {
-		pol->curlft.add_time = (unsigned long)xtime.tv_sec;
+		pol->curlft.add_time = get_seconds();
 		pol->index = xfrm_gen_index(pol->type, XFRM_POLICY_MAX+dir);
 		__xfrm_policy_link(pol, XFRM_POLICY_MAX+dir);
 	}
@@ -1330,6 +1344,40 @@
 	return err;
 }
 
+static int inline
+xfrm_dst_alloc_copy(void **target, void *src, int size)
+{
+	if (!*target) {
+		*target = kmalloc(size, GFP_ATOMIC);
+		if (!*target)
+			return -ENOMEM;
+	}
+	memcpy(*target, src, size);
+	return 0;
+}
+
+static int inline
+xfrm_dst_update_parent(struct dst_entry *dst, struct xfrm_selector *sel)
+{
+#ifdef CONFIG_XFRM_SUB_POLICY
+	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
+	return xfrm_dst_alloc_copy((void **)&(xdst->partner),
+				   sel, sizeof(*sel));
+#else
+	return 0;
+#endif
+}
+
+static int inline
+xfrm_dst_update_origin(struct dst_entry *dst, struct flowi *fl)
+{
+#ifdef CONFIG_XFRM_SUB_POLICY
+	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
+	return xfrm_dst_alloc_copy((void **)&(xdst->origin), fl, sizeof(*fl));
+#else
+	return 0;
+#endif
+}
 
 static int stale_bundle(struct dst_entry *dst);
 
@@ -1386,7 +1434,7 @@
 		return 0;
 
 	family = dst_orig->ops->family;
-	policy->curlft.use_time = (unsigned long)xtime.tv_sec;
+	policy->curlft.use_time = get_seconds();
 	pols[0] = policy;
 	npols ++;
 	xfrm_nr += pols[0]->xfrm_nr;
@@ -1518,6 +1566,18 @@
 			err = -EHOSTUNREACH;
 			goto error;
 		}
+
+		if (npols > 1)
+			err = xfrm_dst_update_parent(dst, &pols[1]->selector);
+		else
+			err = xfrm_dst_update_origin(dst, fl);
+		if (unlikely(err)) {
+			write_unlock_bh(&policy->lock);
+			if (dst)
+				dst_free(dst);
+			goto error;
+		}
+
 		dst->next = policy->bundles;
 		policy->bundles = dst;
 		dst_hold(dst);
@@ -1682,7 +1742,7 @@
 		return 1;
 	}
 
-	pol->curlft.use_time = (unsigned long)xtime.tv_sec;
+	pol->curlft.use_time = get_seconds();
 
 	pols[0] = pol;
 	npols ++;
@@ -1694,7 +1754,7 @@
 		if (pols[1]) {
 			if (IS_ERR(pols[1]))
 				return 0;
-			pols[1]->curlft.use_time = (unsigned long)xtime.tv_sec;
+			pols[1]->curlft.use_time = get_seconds();
 			npols ++;
 		}
 	}
@@ -1933,6 +1993,15 @@
 	if (!dst_check(dst->path, ((struct xfrm_dst *)dst)->path_cookie) ||
 	    (dst->dev && !netif_running(dst->dev)))
 		return 0;
+#ifdef CONFIG_XFRM_SUB_POLICY
+	if (fl) {
+		if (first->origin && !flow_cache_uli_match(first->origin, fl))
+			return 0;
+		if (first->partner &&
+		    !xfrm_selector_match(first->partner, fl, family))
+			return 0;
+	}
+#endif
 
 	last = NULL;
 
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index e3a0bcf..f3a61eb 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -233,7 +233,7 @@
 static void xfrm_timer_handler(unsigned long data)
 {
 	struct xfrm_state *x = (struct xfrm_state*)data;
-	unsigned long now = (unsigned long)xtime.tv_sec;
+	unsigned long now = get_seconds();
 	long next = LONG_MAX;
 	int warn = 0;
 	int err = 0;
@@ -326,7 +326,7 @@
 		init_timer(&x->rtimer);
 		x->rtimer.function = xfrm_replay_timer_handler;
 		x->rtimer.data     = (unsigned long)x;
-		x->curlft.add_time = (unsigned long)xtime.tv_sec;
+		x->curlft.add_time = get_seconds();
 		x->lft.soft_byte_limit = XFRM_INF;
 		x->lft.soft_packet_limit = XFRM_INF;
 		x->lft.hard_byte_limit = XFRM_INF;
@@ -421,6 +421,16 @@
 }
 EXPORT_SYMBOL(xfrm_state_flush);
 
+void xfrm_sad_getinfo(struct xfrm_sadinfo *si)
+{
+	spin_lock_bh(&xfrm_state_lock);
+	si->sadcnt = xfrm_state_num;
+	si->sadhcnt = xfrm_state_hmask;
+	si->sadhmcnt = xfrm_state_hashmax;
+	spin_unlock_bh(&xfrm_state_lock);
+}
+EXPORT_SYMBOL(xfrm_sad_getinfo);
+
 static int
 xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
 		  struct xfrm_tmpl *tmpl,
@@ -458,7 +468,7 @@
 					     x->id.daddr.a6))
 				continue;
 			break;
-		};
+		}
 
 		xfrm_state_hold(x);
 		return x;
@@ -493,7 +503,7 @@
 					     x->props.saddr.a6))
 				continue;
 			break;
-		};
+		}
 
 		xfrm_state_hold(x);
 		return x;
@@ -722,7 +732,7 @@
 					     (struct in6_addr *)saddr))
 				continue;
 			break;
-		};
+		}
 
 		xfrm_state_hold(x);
 		return x;
@@ -755,7 +765,7 @@
 			ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6,
 				       (struct in6_addr *)daddr);
 			break;
-		};
+		}
 
 		x->km.state = XFRM_STATE_ACQ;
 		x->id.proto = proto;
@@ -1051,7 +1061,7 @@
 int xfrm_state_check_expire(struct xfrm_state *x)
 {
 	if (!x->curlft.use_time)
-		x->curlft.use_time = (unsigned long)xtime.tv_sec;
+		x->curlft.use_time = get_seconds();
 
 	if (x->km.state != XFRM_STATE_VALID)
 		return -EINVAL;
@@ -1667,37 +1677,17 @@
 }
 EXPORT_SYMBOL(xfrm_state_delete_tunnel);
 
-/*
- * This function is NOT optimal.  For example, with ESP it will give an
- * MTU that's usually two bytes short of being optimal.  However, it will
- * usually give an answer that's a multiple of 4 provided the input is
- * also a multiple of 4.
- */
 int xfrm_state_mtu(struct xfrm_state *x, int mtu)
 {
-	int res = mtu;
+	int res;
 
-	res -= x->props.header_len;
-
-	for (;;) {
-		int m = res;
-
-		if (m < 68)
-			return 68;
-
-		spin_lock_bh(&x->lock);
-		if (x->km.state == XFRM_STATE_VALID &&
-		    x->type && x->type->get_max_size)
-			m = x->type->get_max_size(x, m);
-		else
-			m += x->props.header_len;
-		spin_unlock_bh(&x->lock);
-
-		if (m <= mtu)
-			break;
-		res -= (m - mtu);
-	}
-
+	spin_lock_bh(&x->lock);
+	if (x->km.state == XFRM_STATE_VALID &&
+	    x->type && x->type->get_mtu)
+		res = x->type->get_mtu(x, mtu);
+	else
+		res = mtu;
+	spin_unlock_bh(&x->lock);
 	return res;
 }
 
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 816e369..4210d91 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -71,7 +71,7 @@
 
 	default:
 		return -EINVAL;
-	};
+	}
 
 	algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0';
 	return 0;
@@ -152,7 +152,7 @@
 
 	default:
 		goto out;
-	};
+	}
 
 	err = -EINVAL;
 	switch (p->id.proto) {
@@ -192,7 +192,7 @@
 
 	default:
 		goto out;
-	};
+	}
 
 	if ((err = verify_one_alg(xfrma, XFRMA_ALG_AUTH)))
 		goto out;
@@ -217,7 +217,7 @@
 
 	default:
 		goto out;
-	};
+	}
 
 	err = 0;
 
@@ -576,7 +576,7 @@
 	struct sk_buff *skb = sp->out_skb;
 	struct xfrm_usersa_info *p;
 	struct nlmsghdr *nlh;
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 
 	if (sp->this_idx < sp->start_idx)
 		goto out;
@@ -621,14 +621,14 @@
 	if (x->lastused)
 		RTA_PUT(skb, XFRMA_LASTUSED, sizeof(x->lastused), &x->lastused);
 
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 out:
 	sp->this_idx++;
 	return 0;
 
 nlmsg_failure:
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -672,6 +672,136 @@
 	return skb;
 }
 
+static int build_spdinfo(struct sk_buff *skb, u32 pid, u32 seq, u32 flags)
+{
+	struct xfrm_spdinfo si;
+	struct nlmsghdr *nlh;
+	u32 *f;
+
+	nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0);
+	if (nlh == NULL) /* shouldnt really happen ... */
+		return -EMSGSIZE;
+
+	f = nlmsg_data(nlh);
+	*f = flags;
+	xfrm_spd_getinfo(&si);
+
+	if (flags & XFRM_SPD_HMASK)
+		NLA_PUT_U32(skb, XFRMA_SPDHMASK, si.spdhcnt);
+	if (flags & XFRM_SPD_HMAX)
+		NLA_PUT_U32(skb, XFRMA_SPDHMAX, si.spdhmcnt);
+	if (flags & XFRM_SPD_ICNT)
+		NLA_PUT_U32(skb, XFRMA_SPDICNT, si.incnt);
+	if (flags & XFRM_SPD_OCNT)
+		NLA_PUT_U32(skb, XFRMA_SPDOCNT, si.outcnt);
+	if (flags & XFRM_SPD_FCNT)
+		NLA_PUT_U32(skb, XFRMA_SPDFCNT, si.fwdcnt);
+	if (flags & XFRM_SPD_ISCNT)
+		NLA_PUT_U32(skb, XFRMA_SPDISCNT, si.inscnt);
+	if (flags & XFRM_SPD_OSCNT)
+		NLA_PUT_U32(skb, XFRMA_SPDOSCNT, si.inscnt);
+	if (flags & XFRM_SPD_FSCNT)
+		NLA_PUT_U32(skb, XFRMA_SPDFSCNT, si.inscnt);
+
+	return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+	nlmsg_cancel(skb, nlh);
+	return -EMSGSIZE;
+}
+
+static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
+		struct rtattr **xfrma)
+{
+	struct sk_buff *r_skb;
+	u32 *flags = NLMSG_DATA(nlh);
+	u32 spid = NETLINK_CB(skb).pid;
+	u32 seq = nlh->nlmsg_seq;
+	int len = NLMSG_LENGTH(sizeof(u32));
+
+
+	if (*flags & XFRM_SPD_HMASK)
+		len += RTA_SPACE(sizeof(u32));
+	if (*flags & XFRM_SPD_HMAX)
+		len += RTA_SPACE(sizeof(u32));
+	if (*flags & XFRM_SPD_ICNT)
+		len += RTA_SPACE(sizeof(u32));
+	if (*flags & XFRM_SPD_OCNT)
+		len += RTA_SPACE(sizeof(u32));
+	if (*flags & XFRM_SPD_FCNT)
+		len += RTA_SPACE(sizeof(u32));
+	if (*flags & XFRM_SPD_ISCNT)
+		len += RTA_SPACE(sizeof(u32));
+	if (*flags & XFRM_SPD_OSCNT)
+		len += RTA_SPACE(sizeof(u32));
+	if (*flags & XFRM_SPD_FSCNT)
+		len += RTA_SPACE(sizeof(u32));
+
+	r_skb = alloc_skb(len, GFP_ATOMIC);
+	if (r_skb == NULL)
+		return -ENOMEM;
+
+	if (build_spdinfo(r_skb, spid, seq, *flags) < 0)
+		BUG();
+
+	return nlmsg_unicast(xfrm_nl, r_skb, spid);
+}
+
+static int build_sadinfo(struct sk_buff *skb, u32 pid, u32 seq, u32 flags)
+{
+	struct xfrm_sadinfo si;
+	struct nlmsghdr *nlh;
+	u32 *f;
+
+	nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSADINFO, sizeof(u32), 0);
+	if (nlh == NULL) /* shouldnt really happen ... */
+		return -EMSGSIZE;
+
+	f = nlmsg_data(nlh);
+	*f = flags;
+	xfrm_sad_getinfo(&si);
+
+	if (flags & XFRM_SAD_HMASK)
+		NLA_PUT_U32(skb, XFRMA_SADHMASK, si.sadhcnt);
+	if (flags & XFRM_SAD_HMAX)
+		NLA_PUT_U32(skb, XFRMA_SADHMAX, si.sadhmcnt);
+	if (flags & XFRM_SAD_CNT)
+		NLA_PUT_U32(skb, XFRMA_SADCNT, si.sadcnt);
+
+	return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+	nlmsg_cancel(skb, nlh);
+	return -EMSGSIZE;
+}
+
+static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
+		struct rtattr **xfrma)
+{
+	struct sk_buff *r_skb;
+	u32 *flags = NLMSG_DATA(nlh);
+	u32 spid = NETLINK_CB(skb).pid;
+	u32 seq = nlh->nlmsg_seq;
+	int len = NLMSG_LENGTH(sizeof(u32));
+
+	if (*flags & XFRM_SAD_HMASK)
+		len += RTA_SPACE(sizeof(u32));
+	if (*flags & XFRM_SAD_HMAX)
+		len += RTA_SPACE(sizeof(u32));
+	if (*flags & XFRM_SAD_CNT)
+		len += RTA_SPACE(sizeof(u32));
+
+	r_skb = alloc_skb(len, GFP_ATOMIC);
+
+	if (r_skb == NULL)
+		return -ENOMEM;
+
+	if (build_sadinfo(r_skb, spid, seq, *flags) < 0)
+		BUG();
+
+	return nlmsg_unicast(xfrm_nl, r_skb, spid);
+}
+
 static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
 		struct rtattr **xfrma)
 {
@@ -711,7 +841,7 @@
 
 	default:
 		return -EINVAL;
-	};
+	}
 
 	if (p->min > p->max)
 		return -EINVAL;
@@ -789,7 +919,7 @@
 
 	default:
 		return -EINVAL;
-	};
+	}
 
 	return 0;
 }
@@ -805,7 +935,7 @@
 
 	default:
 		return -EINVAL;
-	};
+	}
 
 	return 0;
 }
@@ -821,7 +951,7 @@
 
 	default:
 		return -EINVAL;
-	};
+	}
 
 	switch (p->action) {
 	case XFRM_POLICY_ALLOW:
@@ -830,7 +960,7 @@
 
 	default:
 		return -EINVAL;
-	};
+	}
 
 	switch (p->sel.family) {
 	case AF_INET:
@@ -845,7 +975,7 @@
 
 	default:
 		return -EINVAL;
-	};
+	}
 
 	return verify_policy_dir(p->dir);
 }
@@ -912,7 +1042,7 @@
 #endif
 		default:
 			return -EINVAL;
-		};
+		}
 	}
 
 	return 0;
@@ -1157,7 +1287,7 @@
 	struct sk_buff *in_skb = sp->in_skb;
 	struct sk_buff *skb = sp->out_skb;
 	struct nlmsghdr *nlh;
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 
 	if (sp->this_idx < sp->start_idx)
 		goto out;
@@ -1176,13 +1306,13 @@
 	if (copy_to_user_policy_type(xp->type, skb) < 0)
 		goto nlmsg_failure;
 
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 out:
 	sp->this_idx++;
 	return 0;
 
 nlmsg_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -1330,7 +1460,7 @@
 	struct xfrm_aevent_id *id;
 	struct nlmsghdr *nlh;
 	struct xfrm_lifetime_cur ltime;
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 
 	nlh = NLMSG_PUT(skb, c->pid, c->seq, XFRM_MSG_NEWAE, sizeof(*id));
 	id = NLMSG_DATA(nlh);
@@ -1362,12 +1492,12 @@
 		RTA_PUT(skb,XFRMA_ETIMER_THRESH,sizeof(u32),&etimer);
 	}
 
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
 rtattr_failure:
 nlmsg_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -1744,7 +1874,7 @@
 	struct xfrm_migrate *mp;
 	struct xfrm_userpolicy_id *pol_id;
 	struct nlmsghdr *nlh;
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	int i;
 
 	nlh = NLMSG_PUT(skb, 0, 0, XFRM_MSG_MIGRATE, sizeof(*pol_id));
@@ -1764,10 +1894,10 @@
 			goto nlmsg_failure;
 	}
 
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 nlmsg_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -1823,6 +1953,8 @@
 	[XFRM_MSG_GETAE       - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id),
 	[XFRM_MSG_REPORT      - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report),
 	[XFRM_MSG_MIGRATE     - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id),
+	[XFRM_MSG_GETSADINFO  - XFRM_MSG_BASE] = NLMSG_LENGTH(sizeof(u32)),
+	[XFRM_MSG_GETSPDINFO  - XFRM_MSG_BASE] = NLMSG_LENGTH(sizeof(u32)),
 };
 
 #undef XMSGSIZE
@@ -1850,55 +1982,40 @@
 	[XFRM_MSG_NEWAE       - XFRM_MSG_BASE] = { .doit = xfrm_new_ae  },
 	[XFRM_MSG_GETAE       - XFRM_MSG_BASE] = { .doit = xfrm_get_ae  },
 	[XFRM_MSG_MIGRATE     - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate    },
+	[XFRM_MSG_GETSADINFO  - XFRM_MSG_BASE] = { .doit = xfrm_get_sadinfo   },
+	[XFRM_MSG_GETSPDINFO  - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo   },
 };
 
-static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *errp)
+static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
 	struct rtattr *xfrma[XFRMA_MAX];
 	struct xfrm_link *link;
 	int type, min_len;
 
-	if (!(nlh->nlmsg_flags & NLM_F_REQUEST))
-		return 0;
-
 	type = nlh->nlmsg_type;
-
-	/* A control message: ignore them */
-	if (type < XFRM_MSG_BASE)
-		return 0;
-
-	/* Unknown message: reply with EINVAL */
 	if (type > XFRM_MSG_MAX)
-		goto err_einval;
+		return -EINVAL;
 
 	type -= XFRM_MSG_BASE;
 	link = &xfrm_dispatch[type];
 
 	/* All operations require privileges, even GET */
-	if (security_netlink_recv(skb, CAP_NET_ADMIN)) {
-		*errp = -EPERM;
-		return -1;
-	}
+	if (security_netlink_recv(skb, CAP_NET_ADMIN))
+		return -EPERM;
 
 	if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) ||
 	     type == (XFRM_MSG_GETPOLICY - XFRM_MSG_BASE)) &&
 	    (nlh->nlmsg_flags & NLM_F_DUMP)) {
 		if (link->dump == NULL)
-			goto err_einval;
+			return -EINVAL;
 
-		if ((*errp = netlink_dump_start(xfrm_nl, skb, nlh,
-						link->dump, NULL)) != 0) {
-			return -1;
-		}
-
-		netlink_queue_skip(nlh, skb);
-		return -1;
+		return netlink_dump_start(xfrm_nl, skb, nlh, link->dump, NULL);
 	}
 
 	memset(xfrma, 0, sizeof(xfrma));
 
 	if (nlh->nlmsg_len < (min_len = xfrm_msg_min[type]))
-		goto err_einval;
+		return -EINVAL;
 
 	if (nlh->nlmsg_len > min_len) {
 		int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
@@ -1908,7 +2025,7 @@
 			unsigned short flavor = attr->rta_type;
 			if (flavor) {
 				if (flavor > XFRMA_MAX)
-					goto err_einval;
+					return -EINVAL;
 				xfrma[flavor - 1] = attr;
 			}
 			attr = RTA_NEXT(attr, attrlen);
@@ -1916,14 +2033,9 @@
 	}
 
 	if (link->doit == NULL)
-		goto err_einval;
-	*errp = link->doit(skb, nlh, xfrma);
+		return -EINVAL;
 
-	return *errp;
-
-err_einval:
-	*errp = -EINVAL;
-	return -1;
+	return link->doit(skb, nlh, xfrma);
 }
 
 static void xfrm_netlink_rcv(struct sock *sk, int len)
@@ -1942,7 +2054,7 @@
 {
 	struct xfrm_user_expire *ue;
 	struct nlmsghdr *nlh;
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 
 	nlh = NLMSG_PUT(skb, c->pid, 0, XFRM_MSG_EXPIRE,
 			sizeof(*ue));
@@ -1952,11 +2064,11 @@
 	copy_to_user_state(x, &ue->state);
 	ue->hard = (c->data.hard != 0) ? 1 : 0;
 
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
 nlmsg_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -1999,7 +2111,7 @@
 	struct xfrm_usersa_flush *p;
 	struct nlmsghdr *nlh;
 	struct sk_buff *skb;
-	unsigned char *b;
+	sk_buff_data_t b;
 	int len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_flush));
 
 	skb = alloc_skb(len, GFP_ATOMIC);
@@ -2045,7 +2157,7 @@
 	struct xfrm_usersa_id *id;
 	struct nlmsghdr *nlh;
 	struct sk_buff *skb;
-	unsigned char *b;
+	sk_buff_data_t b;
 	int len = xfrm_sa_len(x);
 	int headlen;
 
@@ -2129,7 +2241,7 @@
 {
 	struct xfrm_user_acquire *ua;
 	struct nlmsghdr *nlh;
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 	__u32 seq = xfrm_get_acqseq();
 
 	nlh = NLMSG_PUT(skb, 0, 0, XFRM_MSG_ACQUIRE,
@@ -2153,11 +2265,11 @@
 	if (copy_to_user_policy_type(xp->type, skb) < 0)
 		goto nlmsg_failure;
 
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
 nlmsg_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -2249,7 +2361,7 @@
 	struct xfrm_user_polexpire *upe;
 	struct nlmsghdr *nlh;
 	int hard = c->data.hard;
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 
 	nlh = NLMSG_PUT(skb, c->pid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe));
 	upe = NLMSG_DATA(nlh);
@@ -2264,11 +2376,11 @@
 		goto nlmsg_failure;
 	upe->hard = !!hard;
 
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
 nlmsg_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -2300,7 +2412,7 @@
 	struct xfrm_userpolicy_id *id;
 	struct nlmsghdr *nlh;
 	struct sk_buff *skb;
-	unsigned char *b;
+	sk_buff_data_t b;
 	int len = RTA_SPACE(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr);
 	int headlen;
 
@@ -2357,7 +2469,7 @@
 {
 	struct nlmsghdr *nlh;
 	struct sk_buff *skb;
-	unsigned char *b;
+	sk_buff_data_t b;
 	int len = 0;
 #ifdef CONFIG_XFRM_SUB_POLICY
 	len += RTA_SPACE(sizeof(struct xfrm_userpolicy_type));
@@ -2410,7 +2522,7 @@
 {
 	struct xfrm_user_report *ur;
 	struct nlmsghdr *nlh;
-	unsigned char *b = skb->tail;
+	unsigned char *b = skb_tail_pointer(skb);
 
 	nlh = NLMSG_PUT(skb, 0, 0, XFRM_MSG_REPORT, sizeof(*ur));
 	ur = NLMSG_DATA(nlh);
@@ -2422,12 +2534,12 @@
 	if (addr)
 		RTA_PUT(skb, XFRMA_COADDR, sizeof(*addr), addr);
 
-	nlh->nlmsg_len = skb->tail - b;
+	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
 nlmsg_failure:
 rtattr_failure:
-	skb_trim(skb, b - skb->data);
+	nlmsg_trim(skb, b);
 	return -1;
 }
 
@@ -2466,7 +2578,7 @@
 	printk(KERN_INFO "Initializing XFRM netlink socket\n");
 
 	nlsk = netlink_kernel_create(NETLINK_XFRM, XFRMNLGRP_MAX,
-				     xfrm_netlink_rcv, THIS_MODULE);
+				     xfrm_netlink_rcv, NULL, THIS_MODULE);
 	if (nlsk == NULL)
 		return -ENOMEM;
 	rcu_assign_pointer(xfrm_nl, nlsk);
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index ad45ce7..88292e3 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -66,6 +66,8 @@
 	.read		= keyring_read,
 };
 
+EXPORT_SYMBOL(key_type_keyring);
+
 /*
  * semaphore to serialise link/link calls to prevent two link calls in parallel
  * introducing a cycle
diff --git a/security/selinux/Makefile b/security/selinux/Makefile
index faf2e02..dc3502e 100644
--- a/security/selinux/Makefile
+++ b/security/selinux/Makefile
@@ -8,5 +8,7 @@
 
 selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o
 
+selinux-$(CONFIG_NETLABEL) += netlabel.o
+
 EXTRA_CFLAGS += -Isecurity/selinux/include
 
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index da8caf1..e4396a8 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -217,6 +217,8 @@
 		audit_log_format(ab, " tcontext=%s", scontext);
 		kfree(scontext);
 	}
+
+	BUG_ON(tclass >= ARRAY_SIZE(class_to_string) || !class_to_string[tclass]);
 	audit_log_format(ab, " tclass=%s", class_to_string[tclass]);
 }
 
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index d41e24d..885a9a9 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -77,7 +77,7 @@
 #include "objsec.h"
 #include "netif.h"
 #include "xfrm.h"
-#include "selinux_netlabel.h"
+#include "netlabel.h"
 
 #define XATTR_SELINUX_SUFFIX "selinux"
 #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
@@ -2944,7 +2944,7 @@
 	int offset, ihlen, ret = -EINVAL;
 	struct iphdr _iph, *ih;
 
-	offset = skb->nh.raw - skb->data;
+	offset = skb_network_offset(skb);
 	ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph);
 	if (ih == NULL)
 		goto out;
@@ -3026,7 +3026,7 @@
 	int ret = -EINVAL, offset;
 	struct ipv6hdr _ipv6h, *ip6;
 
-	offset = skb->nh.raw - skb->data;
+	offset = skb_network_offset(skb);
 	ip6 = skb_header_pointer(skb, offset, sizeof(_ipv6h), &_ipv6h);
 	if (ip6 == NULL)
 		goto out;
@@ -3123,6 +3123,34 @@
 	return ret;
 }
 
+/**
+ * selinux_skb_extlbl_sid - Determine the external label of a packet
+ * @skb: the packet
+ * @base_sid: the SELinux SID to use as a context for MLS only external labels
+ * @sid: the packet's SID
+ *
+ * Description:
+ * Check the various different forms of external packet labeling and determine
+ * the external SID for the packet.
+ *
+ */
+static void selinux_skb_extlbl_sid(struct sk_buff *skb,
+				   u32 base_sid,
+				   u32 *sid)
+{
+	u32 xfrm_sid;
+	u32 nlbl_sid;
+
+	selinux_skb_xfrm_sid(skb, &xfrm_sid);
+	if (selinux_netlbl_skbuff_getsid(skb,
+					 (xfrm_sid == SECSID_NULL ?
+					  base_sid : xfrm_sid),
+					 &nlbl_sid) != 0)
+		nlbl_sid = SECSID_NULL;
+
+	*sid = (nlbl_sid == SECSID_NULL ? xfrm_sid : nlbl_sid);
+}
+
 /* socket security operations */
 static int socket_has_perm(struct task_struct *task, struct socket *sock,
 			   u32 perms)
@@ -3664,9 +3692,7 @@
 	if (sock && sock->sk->sk_family == PF_UNIX)
 		selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid);
 	else if (skb)
-		security_skb_extlbl_sid(skb,
-					SECINITSID_UNLABELED,
-					&peer_secid);
+		selinux_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &peer_secid);
 
 	if (peer_secid == SECSID_NULL)
 		err = -EINVAL;
@@ -3727,7 +3753,7 @@
 	u32 newsid;
 	u32 peersid;
 
-	security_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &peersid);
+	selinux_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &peersid);
 	if (peersid == SECSID_NULL) {
 		req->secid = sksec->sid;
 		req->peer_secid = SECSID_NULL;
@@ -3765,7 +3791,7 @@
 {
 	struct sk_security_struct *sksec = sk->sk_security;
 
-	security_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &sksec->peer_sid);
+	selinux_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &sksec->peer_sid);
 }
 
 static void selinux_req_classify_flow(const struct request_sock *req,
@@ -3786,7 +3812,7 @@
 		err = -EINVAL;
 		goto out;
 	}
-	nlh = (struct nlmsghdr *)skb->data;
+	nlh = nlmsg_hdr(skb);
 	
 	err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm);
 	if (err) {
diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h
index ad9fb2d..b83e740 100644
--- a/security/selinux/include/av_perm_to_string.h
+++ b/security/selinux/include/av_perm_to_string.h
@@ -128,96 +128,6 @@
    S_(SECCLASS_CAPABILITY, CAPABILITY__LEASE, "lease")
    S_(SECCLASS_CAPABILITY, CAPABILITY__AUDIT_WRITE, "audit_write")
    S_(SECCLASS_CAPABILITY, CAPABILITY__AUDIT_CONTROL, "audit_control")
-   S_(SECCLASS_PASSWD, PASSWD__PASSWD, "passwd")
-   S_(SECCLASS_PASSWD, PASSWD__CHFN, "chfn")
-   S_(SECCLASS_PASSWD, PASSWD__CHSH, "chsh")
-   S_(SECCLASS_PASSWD, PASSWD__ROOTOK, "rootok")
-   S_(SECCLASS_PASSWD, PASSWD__CRONTAB, "crontab")
-   S_(SECCLASS_DRAWABLE, DRAWABLE__CREATE, "create")
-   S_(SECCLASS_DRAWABLE, DRAWABLE__DESTROY, "destroy")
-   S_(SECCLASS_DRAWABLE, DRAWABLE__DRAW, "draw")
-   S_(SECCLASS_DRAWABLE, DRAWABLE__COPY, "copy")
-   S_(SECCLASS_DRAWABLE, DRAWABLE__GETATTR, "getattr")
-   S_(SECCLASS_GC, GC__CREATE, "create")
-   S_(SECCLASS_GC, GC__FREE, "free")
-   S_(SECCLASS_GC, GC__GETATTR, "getattr")
-   S_(SECCLASS_GC, GC__SETATTR, "setattr")
-   S_(SECCLASS_WINDOW, WINDOW__ADDCHILD, "addchild")
-   S_(SECCLASS_WINDOW, WINDOW__CREATE, "create")
-   S_(SECCLASS_WINDOW, WINDOW__DESTROY, "destroy")
-   S_(SECCLASS_WINDOW, WINDOW__MAP, "map")
-   S_(SECCLASS_WINDOW, WINDOW__UNMAP, "unmap")
-   S_(SECCLASS_WINDOW, WINDOW__CHSTACK, "chstack")
-   S_(SECCLASS_WINDOW, WINDOW__CHPROPLIST, "chproplist")
-   S_(SECCLASS_WINDOW, WINDOW__CHPROP, "chprop")
-   S_(SECCLASS_WINDOW, WINDOW__LISTPROP, "listprop")
-   S_(SECCLASS_WINDOW, WINDOW__GETATTR, "getattr")
-   S_(SECCLASS_WINDOW, WINDOW__SETATTR, "setattr")
-   S_(SECCLASS_WINDOW, WINDOW__SETFOCUS, "setfocus")
-   S_(SECCLASS_WINDOW, WINDOW__MOVE, "move")
-   S_(SECCLASS_WINDOW, WINDOW__CHSELECTION, "chselection")
-   S_(SECCLASS_WINDOW, WINDOW__CHPARENT, "chparent")
-   S_(SECCLASS_WINDOW, WINDOW__CTRLLIFE, "ctrllife")
-   S_(SECCLASS_WINDOW, WINDOW__ENUMERATE, "enumerate")
-   S_(SECCLASS_WINDOW, WINDOW__TRANSPARENT, "transparent")
-   S_(SECCLASS_WINDOW, WINDOW__MOUSEMOTION, "mousemotion")
-   S_(SECCLASS_WINDOW, WINDOW__CLIENTCOMEVENT, "clientcomevent")
-   S_(SECCLASS_WINDOW, WINDOW__INPUTEVENT, "inputevent")
-   S_(SECCLASS_WINDOW, WINDOW__DRAWEVENT, "drawevent")
-   S_(SECCLASS_WINDOW, WINDOW__WINDOWCHANGEEVENT, "windowchangeevent")
-   S_(SECCLASS_WINDOW, WINDOW__WINDOWCHANGEREQUEST, "windowchangerequest")
-   S_(SECCLASS_WINDOW, WINDOW__SERVERCHANGEEVENT, "serverchangeevent")
-   S_(SECCLASS_WINDOW, WINDOW__EXTENSIONEVENT, "extensionevent")
-   S_(SECCLASS_FONT, FONT__LOAD, "load")
-   S_(SECCLASS_FONT, FONT__FREE, "free")
-   S_(SECCLASS_FONT, FONT__GETATTR, "getattr")
-   S_(SECCLASS_FONT, FONT__USE, "use")
-   S_(SECCLASS_COLORMAP, COLORMAP__CREATE, "create")
-   S_(SECCLASS_COLORMAP, COLORMAP__FREE, "free")
-   S_(SECCLASS_COLORMAP, COLORMAP__INSTALL, "install")
-   S_(SECCLASS_COLORMAP, COLORMAP__UNINSTALL, "uninstall")
-   S_(SECCLASS_COLORMAP, COLORMAP__LIST, "list")
-   S_(SECCLASS_COLORMAP, COLORMAP__READ, "read")
-   S_(SECCLASS_COLORMAP, COLORMAP__STORE, "store")
-   S_(SECCLASS_COLORMAP, COLORMAP__GETATTR, "getattr")
-   S_(SECCLASS_COLORMAP, COLORMAP__SETATTR, "setattr")
-   S_(SECCLASS_PROPERTY, PROPERTY__CREATE, "create")
-   S_(SECCLASS_PROPERTY, PROPERTY__FREE, "free")
-   S_(SECCLASS_PROPERTY, PROPERTY__READ, "read")
-   S_(SECCLASS_PROPERTY, PROPERTY__WRITE, "write")
-   S_(SECCLASS_CURSOR, CURSOR__CREATE, "create")
-   S_(SECCLASS_CURSOR, CURSOR__CREATEGLYPH, "createglyph")
-   S_(SECCLASS_CURSOR, CURSOR__FREE, "free")
-   S_(SECCLASS_CURSOR, CURSOR__ASSIGN, "assign")
-   S_(SECCLASS_CURSOR, CURSOR__SETATTR, "setattr")
-   S_(SECCLASS_XCLIENT, XCLIENT__KILL, "kill")
-   S_(SECCLASS_XINPUT, XINPUT__LOOKUP, "lookup")
-   S_(SECCLASS_XINPUT, XINPUT__GETATTR, "getattr")
-   S_(SECCLASS_XINPUT, XINPUT__SETATTR, "setattr")
-   S_(SECCLASS_XINPUT, XINPUT__SETFOCUS, "setfocus")
-   S_(SECCLASS_XINPUT, XINPUT__WARPPOINTER, "warppointer")
-   S_(SECCLASS_XINPUT, XINPUT__ACTIVEGRAB, "activegrab")
-   S_(SECCLASS_XINPUT, XINPUT__PASSIVEGRAB, "passivegrab")
-   S_(SECCLASS_XINPUT, XINPUT__UNGRAB, "ungrab")
-   S_(SECCLASS_XINPUT, XINPUT__BELL, "bell")
-   S_(SECCLASS_XINPUT, XINPUT__MOUSEMOTION, "mousemotion")
-   S_(SECCLASS_XINPUT, XINPUT__RELABELINPUT, "relabelinput")
-   S_(SECCLASS_XSERVER, XSERVER__SCREENSAVER, "screensaver")
-   S_(SECCLASS_XSERVER, XSERVER__GETHOSTLIST, "gethostlist")
-   S_(SECCLASS_XSERVER, XSERVER__SETHOSTLIST, "sethostlist")
-   S_(SECCLASS_XSERVER, XSERVER__GETFONTPATH, "getfontpath")
-   S_(SECCLASS_XSERVER, XSERVER__SETFONTPATH, "setfontpath")
-   S_(SECCLASS_XSERVER, XSERVER__GETATTR, "getattr")
-   S_(SECCLASS_XSERVER, XSERVER__GRAB, "grab")
-   S_(SECCLASS_XSERVER, XSERVER__UNGRAB, "ungrab")
-   S_(SECCLASS_XEXTENSION, XEXTENSION__QUERY, "query")
-   S_(SECCLASS_XEXTENSION, XEXTENSION__USE, "use")
-   S_(SECCLASS_PAX, PAX__PAGEEXEC, "pageexec")
-   S_(SECCLASS_PAX, PAX__EMUTRAMP, "emutramp")
-   S_(SECCLASS_PAX, PAX__MPROTECT, "mprotect")
-   S_(SECCLASS_PAX, PAX__RANDMMAP, "randmmap")
-   S_(SECCLASS_PAX, PAX__RANDEXEC, "randexec")
-   S_(SECCLASS_PAX, PAX__SEGMEXEC, "segmexec")
    S_(SECCLASS_NETLINK_ROUTE_SOCKET, NETLINK_ROUTE_SOCKET__NLMSG_READ, "nlmsg_read")
    S_(SECCLASS_NETLINK_ROUTE_SOCKET, NETLINK_ROUTE_SOCKET__NLMSG_WRITE, "nlmsg_write")
    S_(SECCLASS_NETLINK_FIREWALL_SOCKET, NETLINK_FIREWALL_SOCKET__NLMSG_READ, "nlmsg_read")
@@ -232,16 +142,6 @@
    S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV, "nlmsg_readpriv")
    S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_READ, "nlmsg_read")
    S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_WRITE, "nlmsg_write")
-   S_(SECCLASS_DBUS, DBUS__ACQUIRE_SVC, "acquire_svc")
-   S_(SECCLASS_DBUS, DBUS__SEND_MSG, "send_msg")
-   S_(SECCLASS_NSCD, NSCD__GETPWD, "getpwd")
-   S_(SECCLASS_NSCD, NSCD__GETGRP, "getgrp")
-   S_(SECCLASS_NSCD, NSCD__GETHOST, "gethost")
-   S_(SECCLASS_NSCD, NSCD__GETSTAT, "getstat")
-   S_(SECCLASS_NSCD, NSCD__ADMIN, "admin")
-   S_(SECCLASS_NSCD, NSCD__SHMEMPWD, "shmempwd")
-   S_(SECCLASS_NSCD, NSCD__SHMEMGRP, "shmemgrp")
-   S_(SECCLASS_NSCD, NSCD__SHMEMHOST, "shmemhost")
    S_(SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, "sendto")
    S_(SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, "recvfrom")
    S_(SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, "setcontext")
@@ -256,7 +156,5 @@
    S_(SECCLASS_KEY, KEY__LINK, "link")
    S_(SECCLASS_KEY, KEY__SETATTR, "setattr")
    S_(SECCLASS_KEY, KEY__CREATE, "create")
-   S_(SECCLASS_CONTEXT, CONTEXT__TRANSLATE, "translate")
-   S_(SECCLASS_CONTEXT, CONTEXT__CONTAINS, "contains")
    S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NODE_BIND, "node_bind")
    S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NAME_CONNECT, "name_connect")
diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h
index 2de4b5f..5fee173 100644
--- a/security/selinux/include/av_permissions.h
+++ b/security/selinux/include/av_permissions.h
@@ -16,7 +16,6 @@
 #define COMMON_FILE__SWAPON                              0x00004000UL
 #define COMMON_FILE__QUOTAON                             0x00008000UL
 #define COMMON_FILE__MOUNTON                             0x00010000UL
-
 #define COMMON_SOCKET__IOCTL                             0x00000001UL
 #define COMMON_SOCKET__READ                              0x00000002UL
 #define COMMON_SOCKET__WRITE                             0x00000004UL
@@ -39,7 +38,6 @@
 #define COMMON_SOCKET__RECV_MSG                          0x00080000UL
 #define COMMON_SOCKET__SEND_MSG                          0x00100000UL
 #define COMMON_SOCKET__NAME_BIND                         0x00200000UL
-
 #define COMMON_IPC__CREATE                               0x00000001UL
 #define COMMON_IPC__DESTROY                              0x00000002UL
 #define COMMON_IPC__GETATTR                              0x00000004UL
@@ -49,7 +47,6 @@
 #define COMMON_IPC__ASSOCIATE                            0x00000040UL
 #define COMMON_IPC__UNIX_READ                            0x00000080UL
 #define COMMON_IPC__UNIX_WRITE                           0x00000100UL
-
 #define FILESYSTEM__MOUNT                         0x00000001UL
 #define FILESYSTEM__REMOUNT                       0x00000002UL
 #define FILESYSTEM__UNMOUNT                       0x00000004UL
@@ -60,7 +57,6 @@
 #define FILESYSTEM__ASSOCIATE                     0x00000080UL
 #define FILESYSTEM__QUOTAMOD                      0x00000100UL
 #define FILESYSTEM__QUOTAGET                      0x00000200UL
-
 #define DIR__IOCTL                                0x00000001UL
 #define DIR__READ                                 0x00000002UL
 #define DIR__WRITE                                0x00000004UL
@@ -78,13 +74,11 @@
 #define DIR__SWAPON                               0x00004000UL
 #define DIR__QUOTAON                              0x00008000UL
 #define DIR__MOUNTON                              0x00010000UL
-
 #define DIR__ADD_NAME                             0x00020000UL
 #define DIR__REMOVE_NAME                          0x00040000UL
 #define DIR__REPARENT                             0x00080000UL
 #define DIR__SEARCH                               0x00100000UL
 #define DIR__RMDIR                                0x00200000UL
-
 #define FILE__IOCTL                               0x00000001UL
 #define FILE__READ                                0x00000002UL
 #define FILE__WRITE                               0x00000004UL
@@ -102,11 +96,9 @@
 #define FILE__SWAPON                              0x00004000UL
 #define FILE__QUOTAON                             0x00008000UL
 #define FILE__MOUNTON                             0x00010000UL
-
 #define FILE__EXECUTE_NO_TRANS                    0x00020000UL
 #define FILE__ENTRYPOINT                          0x00040000UL
 #define FILE__EXECMOD                             0x00080000UL
-
 #define LNK_FILE__IOCTL                           0x00000001UL
 #define LNK_FILE__READ                            0x00000002UL
 #define LNK_FILE__WRITE                           0x00000004UL
@@ -124,7 +116,6 @@
 #define LNK_FILE__SWAPON                          0x00004000UL
 #define LNK_FILE__QUOTAON                         0x00008000UL
 #define LNK_FILE__MOUNTON                         0x00010000UL
-
 #define CHR_FILE__IOCTL                           0x00000001UL
 #define CHR_FILE__READ                            0x00000002UL
 #define CHR_FILE__WRITE                           0x00000004UL
@@ -142,11 +133,9 @@
 #define CHR_FILE__SWAPON                          0x00004000UL
 #define CHR_FILE__QUOTAON                         0x00008000UL
 #define CHR_FILE__MOUNTON                         0x00010000UL
-
 #define CHR_FILE__EXECUTE_NO_TRANS                0x00020000UL
 #define CHR_FILE__ENTRYPOINT                      0x00040000UL
 #define CHR_FILE__EXECMOD                         0x00080000UL
-
 #define BLK_FILE__IOCTL                           0x00000001UL
 #define BLK_FILE__READ                            0x00000002UL
 #define BLK_FILE__WRITE                           0x00000004UL
@@ -164,7 +153,6 @@
 #define BLK_FILE__SWAPON                          0x00004000UL
 #define BLK_FILE__QUOTAON                         0x00008000UL
 #define BLK_FILE__MOUNTON                         0x00010000UL
-
 #define SOCK_FILE__IOCTL                          0x00000001UL
 #define SOCK_FILE__READ                           0x00000002UL
 #define SOCK_FILE__WRITE                          0x00000004UL
@@ -182,7 +170,6 @@
 #define SOCK_FILE__SWAPON                         0x00004000UL
 #define SOCK_FILE__QUOTAON                        0x00008000UL
 #define SOCK_FILE__MOUNTON                        0x00010000UL
-
 #define FIFO_FILE__IOCTL                          0x00000001UL
 #define FIFO_FILE__READ                           0x00000002UL
 #define FIFO_FILE__WRITE                          0x00000004UL
@@ -200,9 +187,7 @@
 #define FIFO_FILE__SWAPON                         0x00004000UL
 #define FIFO_FILE__QUOTAON                        0x00008000UL
 #define FIFO_FILE__MOUNTON                        0x00010000UL
-
 #define FD__USE                                   0x00000001UL
-
 #define SOCKET__IOCTL                             0x00000001UL
 #define SOCKET__READ                              0x00000002UL
 #define SOCKET__WRITE                             0x00000004UL
@@ -225,7 +210,6 @@
 #define SOCKET__RECV_MSG                          0x00080000UL
 #define SOCKET__SEND_MSG                          0x00100000UL
 #define SOCKET__NAME_BIND                         0x00200000UL
-
 #define TCP_SOCKET__IOCTL                         0x00000001UL
 #define TCP_SOCKET__READ                          0x00000002UL
 #define TCP_SOCKET__WRITE                         0x00000004UL
@@ -248,13 +232,11 @@
 #define TCP_SOCKET__RECV_MSG                      0x00080000UL
 #define TCP_SOCKET__SEND_MSG                      0x00100000UL
 #define TCP_SOCKET__NAME_BIND                     0x00200000UL
-
 #define TCP_SOCKET__CONNECTTO                     0x00400000UL
 #define TCP_SOCKET__NEWCONN                       0x00800000UL
 #define TCP_SOCKET__ACCEPTFROM                    0x01000000UL
 #define TCP_SOCKET__NODE_BIND                     0x02000000UL
 #define TCP_SOCKET__NAME_CONNECT                  0x04000000UL
-
 #define UDP_SOCKET__IOCTL                         0x00000001UL
 #define UDP_SOCKET__READ                          0x00000002UL
 #define UDP_SOCKET__WRITE                         0x00000004UL
@@ -277,9 +259,7 @@
 #define UDP_SOCKET__RECV_MSG                      0x00080000UL
 #define UDP_SOCKET__SEND_MSG                      0x00100000UL
 #define UDP_SOCKET__NAME_BIND                     0x00200000UL
-
 #define UDP_SOCKET__NODE_BIND                     0x00400000UL
-
 #define RAWIP_SOCKET__IOCTL                       0x00000001UL
 #define RAWIP_SOCKET__READ                        0x00000002UL
 #define RAWIP_SOCKET__WRITE                       0x00000004UL
@@ -302,9 +282,7 @@
 #define RAWIP_SOCKET__RECV_MSG                    0x00080000UL
 #define RAWIP_SOCKET__SEND_MSG                    0x00100000UL
 #define RAWIP_SOCKET__NAME_BIND                   0x00200000UL
-
 #define RAWIP_SOCKET__NODE_BIND                   0x00400000UL
-
 #define NODE__TCP_RECV                            0x00000001UL
 #define NODE__TCP_SEND                            0x00000002UL
 #define NODE__UDP_RECV                            0x00000004UL
@@ -314,7 +292,6 @@
 #define NODE__ENFORCE_DEST                        0x00000040UL
 #define NODE__DCCP_RECV                           0x00000080UL
 #define NODE__DCCP_SEND                           0x00000100UL
-
 #define NETIF__TCP_RECV                           0x00000001UL
 #define NETIF__TCP_SEND                           0x00000002UL
 #define NETIF__UDP_RECV                           0x00000004UL
@@ -323,7 +300,6 @@
 #define NETIF__RAWIP_SEND                         0x00000020UL
 #define NETIF__DCCP_RECV                          0x00000040UL
 #define NETIF__DCCP_SEND                          0x00000080UL
-
 #define NETLINK_SOCKET__IOCTL                     0x00000001UL
 #define NETLINK_SOCKET__READ                      0x00000002UL
 #define NETLINK_SOCKET__WRITE                     0x00000004UL
@@ -346,7 +322,6 @@
 #define NETLINK_SOCKET__RECV_MSG                  0x00080000UL
 #define NETLINK_SOCKET__SEND_MSG                  0x00100000UL
 #define NETLINK_SOCKET__NAME_BIND                 0x00200000UL
-
 #define PACKET_SOCKET__IOCTL                      0x00000001UL
 #define PACKET_SOCKET__READ                       0x00000002UL
 #define PACKET_SOCKET__WRITE                      0x00000004UL
@@ -369,7 +344,6 @@
 #define PACKET_SOCKET__RECV_MSG                   0x00080000UL
 #define PACKET_SOCKET__SEND_MSG                   0x00100000UL
 #define PACKET_SOCKET__NAME_BIND                  0x00200000UL
-
 #define KEY_SOCKET__IOCTL                         0x00000001UL
 #define KEY_SOCKET__READ                          0x00000002UL
 #define KEY_SOCKET__WRITE                         0x00000004UL
@@ -392,7 +366,6 @@
 #define KEY_SOCKET__RECV_MSG                      0x00080000UL
 #define KEY_SOCKET__SEND_MSG                      0x00100000UL
 #define KEY_SOCKET__NAME_BIND                     0x00200000UL
-
 #define UNIX_STREAM_SOCKET__IOCTL                 0x00000001UL
 #define UNIX_STREAM_SOCKET__READ                  0x00000002UL
 #define UNIX_STREAM_SOCKET__WRITE                 0x00000004UL
@@ -415,11 +388,9 @@
 #define UNIX_STREAM_SOCKET__RECV_MSG              0x00080000UL
 #define UNIX_STREAM_SOCKET__SEND_MSG              0x00100000UL
 #define UNIX_STREAM_SOCKET__NAME_BIND             0x00200000UL
-
 #define UNIX_STREAM_SOCKET__CONNECTTO             0x00400000UL
 #define UNIX_STREAM_SOCKET__NEWCONN               0x00800000UL
 #define UNIX_STREAM_SOCKET__ACCEPTFROM            0x01000000UL
-
 #define UNIX_DGRAM_SOCKET__IOCTL                  0x00000001UL
 #define UNIX_DGRAM_SOCKET__READ                   0x00000002UL
 #define UNIX_DGRAM_SOCKET__WRITE                  0x00000004UL
@@ -442,7 +413,6 @@
 #define UNIX_DGRAM_SOCKET__RECV_MSG               0x00080000UL
 #define UNIX_DGRAM_SOCKET__SEND_MSG               0x00100000UL
 #define UNIX_DGRAM_SOCKET__NAME_BIND              0x00200000UL
-
 #define PROCESS__FORK                             0x00000001UL
 #define PROCESS__TRANSITION                       0x00000002UL
 #define PROCESS__SIGCHLD                          0x00000004UL
@@ -473,7 +443,6 @@
 #define PROCESS__EXECHEAP                         0x08000000UL
 #define PROCESS__SETKEYCREATE                     0x10000000UL
 #define PROCESS__SETSOCKCREATE                    0x20000000UL
-
 #define IPC__CREATE                               0x00000001UL
 #define IPC__DESTROY                              0x00000002UL
 #define IPC__GETATTR                              0x00000004UL
@@ -483,7 +452,6 @@
 #define IPC__ASSOCIATE                            0x00000040UL
 #define IPC__UNIX_READ                            0x00000080UL
 #define IPC__UNIX_WRITE                           0x00000100UL
-
 #define SEM__CREATE                               0x00000001UL
 #define SEM__DESTROY                              0x00000002UL
 #define SEM__GETATTR                              0x00000004UL
@@ -493,7 +461,6 @@
 #define SEM__ASSOCIATE                            0x00000040UL
 #define SEM__UNIX_READ                            0x00000080UL
 #define SEM__UNIX_WRITE                           0x00000100UL
-
 #define MSGQ__CREATE                              0x00000001UL
 #define MSGQ__DESTROY                             0x00000002UL
 #define MSGQ__GETATTR                             0x00000004UL
@@ -503,12 +470,9 @@
 #define MSGQ__ASSOCIATE                           0x00000040UL
 #define MSGQ__UNIX_READ                           0x00000080UL
 #define MSGQ__UNIX_WRITE                          0x00000100UL
-
 #define MSGQ__ENQUEUE                             0x00000200UL
-
 #define MSG__SEND                                 0x00000001UL
 #define MSG__RECEIVE                              0x00000002UL
-
 #define SHM__CREATE                               0x00000001UL
 #define SHM__DESTROY                              0x00000002UL
 #define SHM__GETATTR                              0x00000004UL
@@ -518,9 +482,7 @@
 #define SHM__ASSOCIATE                            0x00000040UL
 #define SHM__UNIX_READ                            0x00000080UL
 #define SHM__UNIX_WRITE                           0x00000100UL
-
 #define SHM__LOCK                                 0x00000200UL
-
 #define SECURITY__COMPUTE_AV                      0x00000001UL
 #define SECURITY__COMPUTE_CREATE                  0x00000002UL
 #define SECURITY__COMPUTE_MEMBER                  0x00000004UL
@@ -532,12 +494,10 @@
 #define SECURITY__SETBOOL                         0x00000100UL
 #define SECURITY__SETSECPARAM                     0x00000200UL
 #define SECURITY__SETCHECKREQPROT                 0x00000400UL
-
 #define SYSTEM__IPC_INFO                          0x00000001UL
 #define SYSTEM__SYSLOG_READ                       0x00000002UL
 #define SYSTEM__SYSLOG_MOD                        0x00000004UL
 #define SYSTEM__SYSLOG_CONSOLE                    0x00000008UL
-
 #define CAPABILITY__CHOWN                         0x00000001UL
 #define CAPABILITY__DAC_OVERRIDE                  0x00000002UL
 #define CAPABILITY__DAC_READ_SEARCH               0x00000004UL
@@ -569,110 +529,6 @@
 #define CAPABILITY__LEASE                         0x10000000UL
 #define CAPABILITY__AUDIT_WRITE                   0x20000000UL
 #define CAPABILITY__AUDIT_CONTROL                 0x40000000UL
-
-#define PASSWD__PASSWD                            0x00000001UL
-#define PASSWD__CHFN                              0x00000002UL
-#define PASSWD__CHSH                              0x00000004UL
-#define PASSWD__ROOTOK                            0x00000008UL
-#define PASSWD__CRONTAB                           0x00000010UL
-
-#define DRAWABLE__CREATE                          0x00000001UL
-#define DRAWABLE__DESTROY                         0x00000002UL
-#define DRAWABLE__DRAW                            0x00000004UL
-#define DRAWABLE__COPY                            0x00000008UL
-#define DRAWABLE__GETATTR                         0x00000010UL
-
-#define GC__CREATE                                0x00000001UL
-#define GC__FREE                                  0x00000002UL
-#define GC__GETATTR                               0x00000004UL
-#define GC__SETATTR                               0x00000008UL
-
-#define WINDOW__ADDCHILD                          0x00000001UL
-#define WINDOW__CREATE                            0x00000002UL
-#define WINDOW__DESTROY                           0x00000004UL
-#define WINDOW__MAP                               0x00000008UL
-#define WINDOW__UNMAP                             0x00000010UL
-#define WINDOW__CHSTACK                           0x00000020UL
-#define WINDOW__CHPROPLIST                        0x00000040UL
-#define WINDOW__CHPROP                            0x00000080UL
-#define WINDOW__LISTPROP                          0x00000100UL
-#define WINDOW__GETATTR                           0x00000200UL
-#define WINDOW__SETATTR                           0x00000400UL
-#define WINDOW__SETFOCUS                          0x00000800UL
-#define WINDOW__MOVE                              0x00001000UL
-#define WINDOW__CHSELECTION                       0x00002000UL
-#define WINDOW__CHPARENT                          0x00004000UL
-#define WINDOW__CTRLLIFE                          0x00008000UL
-#define WINDOW__ENUMERATE                         0x00010000UL
-#define WINDOW__TRANSPARENT                       0x00020000UL
-#define WINDOW__MOUSEMOTION                       0x00040000UL
-#define WINDOW__CLIENTCOMEVENT                    0x00080000UL
-#define WINDOW__INPUTEVENT                        0x00100000UL
-#define WINDOW__DRAWEVENT                         0x00200000UL
-#define WINDOW__WINDOWCHANGEEVENT                 0x00400000UL
-#define WINDOW__WINDOWCHANGEREQUEST               0x00800000UL
-#define WINDOW__SERVERCHANGEEVENT                 0x01000000UL
-#define WINDOW__EXTENSIONEVENT                    0x02000000UL
-
-#define FONT__LOAD                                0x00000001UL
-#define FONT__FREE                                0x00000002UL
-#define FONT__GETATTR                             0x00000004UL
-#define FONT__USE                                 0x00000008UL
-
-#define COLORMAP__CREATE                          0x00000001UL
-#define COLORMAP__FREE                            0x00000002UL
-#define COLORMAP__INSTALL                         0x00000004UL
-#define COLORMAP__UNINSTALL                       0x00000008UL
-#define COLORMAP__LIST                            0x00000010UL
-#define COLORMAP__READ                            0x00000020UL
-#define COLORMAP__STORE                           0x00000040UL
-#define COLORMAP__GETATTR                         0x00000080UL
-#define COLORMAP__SETATTR                         0x00000100UL
-
-#define PROPERTY__CREATE                          0x00000001UL
-#define PROPERTY__FREE                            0x00000002UL
-#define PROPERTY__READ                            0x00000004UL
-#define PROPERTY__WRITE                           0x00000008UL
-
-#define CURSOR__CREATE                            0x00000001UL
-#define CURSOR__CREATEGLYPH                       0x00000002UL
-#define CURSOR__FREE                              0x00000004UL
-#define CURSOR__ASSIGN                            0x00000008UL
-#define CURSOR__SETATTR                           0x00000010UL
-
-#define XCLIENT__KILL                             0x00000001UL
-
-#define XINPUT__LOOKUP                            0x00000001UL
-#define XINPUT__GETATTR                           0x00000002UL
-#define XINPUT__SETATTR                           0x00000004UL
-#define XINPUT__SETFOCUS                          0x00000008UL
-#define XINPUT__WARPPOINTER                       0x00000010UL
-#define XINPUT__ACTIVEGRAB                        0x00000020UL
-#define XINPUT__PASSIVEGRAB                       0x00000040UL
-#define XINPUT__UNGRAB                            0x00000080UL
-#define XINPUT__BELL                              0x00000100UL
-#define XINPUT__MOUSEMOTION                       0x00000200UL
-#define XINPUT__RELABELINPUT                      0x00000400UL
-
-#define XSERVER__SCREENSAVER                      0x00000001UL
-#define XSERVER__GETHOSTLIST                      0x00000002UL
-#define XSERVER__SETHOSTLIST                      0x00000004UL
-#define XSERVER__GETFONTPATH                      0x00000008UL
-#define XSERVER__SETFONTPATH                      0x00000010UL
-#define XSERVER__GETATTR                          0x00000020UL
-#define XSERVER__GRAB                             0x00000040UL
-#define XSERVER__UNGRAB                           0x00000080UL
-
-#define XEXTENSION__QUERY                         0x00000001UL
-#define XEXTENSION__USE                           0x00000002UL
-
-#define PAX__PAGEEXEC                             0x00000001UL
-#define PAX__EMUTRAMP                             0x00000002UL
-#define PAX__MPROTECT                             0x00000004UL
-#define PAX__RANDMMAP                             0x00000008UL
-#define PAX__RANDEXEC                             0x00000010UL
-#define PAX__SEGMEXEC                             0x00000020UL
-
 #define NETLINK_ROUTE_SOCKET__IOCTL               0x00000001UL
 #define NETLINK_ROUTE_SOCKET__READ                0x00000002UL
 #define NETLINK_ROUTE_SOCKET__WRITE               0x00000004UL
@@ -695,10 +551,8 @@
 #define NETLINK_ROUTE_SOCKET__RECV_MSG            0x00080000UL
 #define NETLINK_ROUTE_SOCKET__SEND_MSG            0x00100000UL
 #define NETLINK_ROUTE_SOCKET__NAME_BIND           0x00200000UL
-
 #define NETLINK_ROUTE_SOCKET__NLMSG_READ          0x00400000UL
 #define NETLINK_ROUTE_SOCKET__NLMSG_WRITE         0x00800000UL
-
 #define NETLINK_FIREWALL_SOCKET__IOCTL            0x00000001UL
 #define NETLINK_FIREWALL_SOCKET__READ             0x00000002UL
 #define NETLINK_FIREWALL_SOCKET__WRITE            0x00000004UL
@@ -721,10 +575,8 @@
 #define NETLINK_FIREWALL_SOCKET__RECV_MSG         0x00080000UL
 #define NETLINK_FIREWALL_SOCKET__SEND_MSG         0x00100000UL
 #define NETLINK_FIREWALL_SOCKET__NAME_BIND        0x00200000UL
-
 #define NETLINK_FIREWALL_SOCKET__NLMSG_READ       0x00400000UL
 #define NETLINK_FIREWALL_SOCKET__NLMSG_WRITE      0x00800000UL
-
 #define NETLINK_TCPDIAG_SOCKET__IOCTL             0x00000001UL
 #define NETLINK_TCPDIAG_SOCKET__READ              0x00000002UL
 #define NETLINK_TCPDIAG_SOCKET__WRITE             0x00000004UL
@@ -747,10 +599,8 @@
 #define NETLINK_TCPDIAG_SOCKET__RECV_MSG          0x00080000UL
 #define NETLINK_TCPDIAG_SOCKET__SEND_MSG          0x00100000UL
 #define NETLINK_TCPDIAG_SOCKET__NAME_BIND         0x00200000UL
-
 #define NETLINK_TCPDIAG_SOCKET__NLMSG_READ        0x00400000UL
 #define NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE       0x00800000UL
-
 #define NETLINK_NFLOG_SOCKET__IOCTL               0x00000001UL
 #define NETLINK_NFLOG_SOCKET__READ                0x00000002UL
 #define NETLINK_NFLOG_SOCKET__WRITE               0x00000004UL
@@ -773,7 +623,6 @@
 #define NETLINK_NFLOG_SOCKET__RECV_MSG            0x00080000UL
 #define NETLINK_NFLOG_SOCKET__SEND_MSG            0x00100000UL
 #define NETLINK_NFLOG_SOCKET__NAME_BIND           0x00200000UL
-
 #define NETLINK_XFRM_SOCKET__IOCTL                0x00000001UL
 #define NETLINK_XFRM_SOCKET__READ                 0x00000002UL
 #define NETLINK_XFRM_SOCKET__WRITE                0x00000004UL
@@ -796,10 +645,8 @@
 #define NETLINK_XFRM_SOCKET__RECV_MSG             0x00080000UL
 #define NETLINK_XFRM_SOCKET__SEND_MSG             0x00100000UL
 #define NETLINK_XFRM_SOCKET__NAME_BIND            0x00200000UL
-
 #define NETLINK_XFRM_SOCKET__NLMSG_READ           0x00400000UL
 #define NETLINK_XFRM_SOCKET__NLMSG_WRITE          0x00800000UL
-
 #define NETLINK_SELINUX_SOCKET__IOCTL             0x00000001UL
 #define NETLINK_SELINUX_SOCKET__READ              0x00000002UL
 #define NETLINK_SELINUX_SOCKET__WRITE             0x00000004UL
@@ -822,7 +669,6 @@
 #define NETLINK_SELINUX_SOCKET__RECV_MSG          0x00080000UL
 #define NETLINK_SELINUX_SOCKET__SEND_MSG          0x00100000UL
 #define NETLINK_SELINUX_SOCKET__NAME_BIND         0x00200000UL
-
 #define NETLINK_AUDIT_SOCKET__IOCTL               0x00000001UL
 #define NETLINK_AUDIT_SOCKET__READ                0x00000002UL
 #define NETLINK_AUDIT_SOCKET__WRITE               0x00000004UL
@@ -845,12 +691,10 @@
 #define NETLINK_AUDIT_SOCKET__RECV_MSG            0x00080000UL
 #define NETLINK_AUDIT_SOCKET__SEND_MSG            0x00100000UL
 #define NETLINK_AUDIT_SOCKET__NAME_BIND           0x00200000UL
-
 #define NETLINK_AUDIT_SOCKET__NLMSG_READ          0x00400000UL
 #define NETLINK_AUDIT_SOCKET__NLMSG_WRITE         0x00800000UL
 #define NETLINK_AUDIT_SOCKET__NLMSG_RELAY         0x01000000UL
 #define NETLINK_AUDIT_SOCKET__NLMSG_READPRIV      0x02000000UL
-
 #define NETLINK_IP6FW_SOCKET__IOCTL               0x00000001UL
 #define NETLINK_IP6FW_SOCKET__READ                0x00000002UL
 #define NETLINK_IP6FW_SOCKET__WRITE               0x00000004UL
@@ -873,10 +717,8 @@
 #define NETLINK_IP6FW_SOCKET__RECV_MSG            0x00080000UL
 #define NETLINK_IP6FW_SOCKET__SEND_MSG            0x00100000UL
 #define NETLINK_IP6FW_SOCKET__NAME_BIND           0x00200000UL
-
 #define NETLINK_IP6FW_SOCKET__NLMSG_READ          0x00400000UL
 #define NETLINK_IP6FW_SOCKET__NLMSG_WRITE         0x00800000UL
-
 #define NETLINK_DNRT_SOCKET__IOCTL                0x00000001UL
 #define NETLINK_DNRT_SOCKET__READ                 0x00000002UL
 #define NETLINK_DNRT_SOCKET__WRITE                0x00000004UL
@@ -899,24 +741,10 @@
 #define NETLINK_DNRT_SOCKET__RECV_MSG             0x00080000UL
 #define NETLINK_DNRT_SOCKET__SEND_MSG             0x00100000UL
 #define NETLINK_DNRT_SOCKET__NAME_BIND            0x00200000UL
-
-#define DBUS__ACQUIRE_SVC                         0x00000001UL
-#define DBUS__SEND_MSG                            0x00000002UL
-
-#define NSCD__GETPWD                              0x00000001UL
-#define NSCD__GETGRP                              0x00000002UL
-#define NSCD__GETHOST                             0x00000004UL
-#define NSCD__GETSTAT                             0x00000008UL
-#define NSCD__ADMIN                               0x00000010UL
-#define NSCD__SHMEMPWD                            0x00000020UL
-#define NSCD__SHMEMGRP                            0x00000040UL
-#define NSCD__SHMEMHOST                           0x00000080UL
-
 #define ASSOCIATION__SENDTO                       0x00000001UL
 #define ASSOCIATION__RECVFROM                     0x00000002UL
 #define ASSOCIATION__SETCONTEXT                   0x00000004UL
 #define ASSOCIATION__POLMATCH                     0x00000008UL
-
 #define NETLINK_KOBJECT_UEVENT_SOCKET__IOCTL      0x00000001UL
 #define NETLINK_KOBJECT_UEVENT_SOCKET__READ       0x00000002UL
 #define NETLINK_KOBJECT_UEVENT_SOCKET__WRITE      0x00000004UL
@@ -939,7 +767,6 @@
 #define NETLINK_KOBJECT_UEVENT_SOCKET__RECV_MSG   0x00080000UL
 #define NETLINK_KOBJECT_UEVENT_SOCKET__SEND_MSG   0x00100000UL
 #define NETLINK_KOBJECT_UEVENT_SOCKET__NAME_BIND  0x00200000UL
-
 #define APPLETALK_SOCKET__IOCTL                   0x00000001UL
 #define APPLETALK_SOCKET__READ                    0x00000002UL
 #define APPLETALK_SOCKET__WRITE                   0x00000004UL
@@ -962,11 +789,9 @@
 #define APPLETALK_SOCKET__RECV_MSG                0x00080000UL
 #define APPLETALK_SOCKET__SEND_MSG                0x00100000UL
 #define APPLETALK_SOCKET__NAME_BIND               0x00200000UL
-
 #define PACKET__SEND                              0x00000001UL
 #define PACKET__RECV                              0x00000002UL
 #define PACKET__RELABELTO                         0x00000004UL
-
 #define KEY__VIEW                                 0x00000001UL
 #define KEY__READ                                 0x00000002UL
 #define KEY__WRITE                                0x00000004UL
@@ -974,10 +799,6 @@
 #define KEY__LINK                                 0x00000010UL
 #define KEY__SETATTR                              0x00000020UL
 #define KEY__CREATE                               0x00000040UL
-
-#define CONTEXT__TRANSLATE                        0x00000001UL
-#define CONTEXT__CONTAINS                         0x00000002UL
-
 #define DCCP_SOCKET__IOCTL                        0x00000001UL
 #define DCCP_SOCKET__READ                         0x00000002UL
 #define DCCP_SOCKET__WRITE                        0x00000004UL
diff --git a/security/selinux/include/class_to_string.h b/security/selinux/include/class_to_string.h
index 9f3ebb1..3787990 100644
--- a/security/selinux/include/class_to_string.h
+++ b/security/selinux/include/class_to_string.h
@@ -2,7 +2,7 @@
 /*
  * Security object class definitions
  */
-    S_("null")
+    S_(NULL)
     S_("security")
     S_("process")
     S_("system")
@@ -32,19 +32,19 @@
     S_("msgq")
     S_("shm")
     S_("ipc")
-    S_("passwd")
-    S_("drawable")
-    S_("window")
-    S_("gc")
-    S_("font")
-    S_("colormap")
-    S_("property")
-    S_("cursor")
-    S_("xclient")
-    S_("xinput")
-    S_("xserver")
-    S_("xextension")
-    S_("pax")
+    S_(NULL)
+    S_(NULL)
+    S_(NULL)
+    S_(NULL)
+    S_(NULL)
+    S_(NULL)
+    S_(NULL)
+    S_(NULL)
+    S_(NULL)
+    S_(NULL)
+    S_(NULL)
+    S_(NULL)
+    S_(NULL)
     S_("netlink_route_socket")
     S_("netlink_firewall_socket")
     S_("netlink_tcpdiag_socket")
@@ -54,12 +54,12 @@
     S_("netlink_audit_socket")
     S_("netlink_ip6fw_socket")
     S_("netlink_dnrt_socket")
-    S_("dbus")
-    S_("nscd")
+    S_(NULL)
+    S_(NULL)
     S_("association")
     S_("netlink_kobject_uevent_socket")
     S_("appletalk_socket")
     S_("packet")
     S_("key")
-    S_("context")
+    S_(NULL)
     S_("dccp_socket")
diff --git a/security/selinux/include/flask.h b/security/selinux/include/flask.h
index 67cef37..35f309f 100644
--- a/security/selinux/include/flask.h
+++ b/security/selinux/include/flask.h
@@ -34,19 +34,6 @@
 #define SECCLASS_MSGQ                                    27
 #define SECCLASS_SHM                                     28
 #define SECCLASS_IPC                                     29
-#define SECCLASS_PASSWD                                  30
-#define SECCLASS_DRAWABLE                                31
-#define SECCLASS_WINDOW                                  32
-#define SECCLASS_GC                                      33
-#define SECCLASS_FONT                                    34
-#define SECCLASS_COLORMAP                                35
-#define SECCLASS_PROPERTY                                36
-#define SECCLASS_CURSOR                                  37
-#define SECCLASS_XCLIENT                                 38
-#define SECCLASS_XINPUT                                  39
-#define SECCLASS_XSERVER                                 40
-#define SECCLASS_XEXTENSION                              41
-#define SECCLASS_PAX                                     42
 #define SECCLASS_NETLINK_ROUTE_SOCKET                    43
 #define SECCLASS_NETLINK_FIREWALL_SOCKET                 44
 #define SECCLASS_NETLINK_TCPDIAG_SOCKET                  45
@@ -56,14 +43,11 @@
 #define SECCLASS_NETLINK_AUDIT_SOCKET                    49
 #define SECCLASS_NETLINK_IP6FW_SOCKET                    50
 #define SECCLASS_NETLINK_DNRT_SOCKET                     51
-#define SECCLASS_DBUS                                    52
-#define SECCLASS_NSCD                                    53
 #define SECCLASS_ASSOCIATION                             54
 #define SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET           55
 #define SECCLASS_APPLETALK_SOCKET                        56
 #define SECCLASS_PACKET                                  57
 #define SECCLASS_KEY                                     58
-#define SECCLASS_CONTEXT                                 59
 #define SECCLASS_DCCP_SOCKET                             60
 
 /*
diff --git a/security/selinux/include/selinux_netlabel.h b/security/selinux/include/netlabel.h
similarity index 94%
rename from security/selinux/include/selinux_netlabel.h
rename to security/selinux/include/netlabel.h
index 2a732c9..218e3f7 100644
--- a/security/selinux/include/selinux_netlabel.h
+++ b/security/selinux/include/netlabel.h
@@ -38,19 +38,22 @@
 
 #ifdef CONFIG_NETLABEL
 void selinux_netlbl_cache_invalidate(void);
-int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid);
-int selinux_netlbl_socket_post_create(struct socket *sock);
-void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock);
-int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
-				struct sk_buff *skb,
-				struct avc_audit_data *ad);
+
 void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec,
 				      int family);
 void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec,
 				     int family);
 void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec,
 				      struct sk_security_struct *newssec);
+
+int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid);
+
+void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock);
+int selinux_netlbl_socket_post_create(struct socket *sock);
 int selinux_netlbl_inode_permission(struct inode *inode, int mask);
+int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
+				struct sk_buff *skb,
+				struct avc_audit_data *ad);
 int selinux_netlbl_socket_setsockopt(struct socket *sock,
 				     int level,
 				     int optname);
@@ -60,6 +63,25 @@
 	return;
 }
 
+static inline void selinux_netlbl_sk_security_reset(
+	                                       struct sk_security_struct *ssec,
+					       int family)
+{
+	return;
+}
+static inline void selinux_netlbl_sk_security_init(
+	                                       struct sk_security_struct *ssec,
+					       int family)
+{
+	return;
+}
+static inline void selinux_netlbl_sk_security_clone(
+	                                    struct sk_security_struct *ssec,
+					    struct sk_security_struct *newssec)
+{
+	return;
+}
+
 static inline int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
 					       u32 base_sid,
 					       u32 *sid)
@@ -68,51 +90,26 @@
 	return 0;
 }
 
-static inline int selinux_netlbl_socket_post_create(struct socket *sock)
-{
-	return 0;
-}
-
 static inline void selinux_netlbl_sock_graft(struct sock *sk,
 					     struct socket *sock)
 {
 	return;
 }
-
+static inline int selinux_netlbl_socket_post_create(struct socket *sock)
+{
+	return 0;
+}
+static inline int selinux_netlbl_inode_permission(struct inode *inode,
+						  int mask)
+{
+	return 0;
+}
 static inline int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
 					      struct sk_buff *skb,
 					      struct avc_audit_data *ad)
 {
 	return 0;
 }
-
-static inline void selinux_netlbl_sk_security_reset(
-					       struct sk_security_struct *ssec,
-					       int family)
-{
-	return;
-}
-
-static inline void selinux_netlbl_sk_security_init(
-	                                       struct sk_security_struct *ssec,
-					       int family)
-{
-	return;
-}
-
-static inline void selinux_netlbl_sk_security_clone(
-	                                   struct sk_security_struct *ssec,
-					   struct sk_security_struct *newssec)
-{
-	return;
-}
-
-static inline int selinux_netlbl_inode_permission(struct inode *inode,
-						  int mask)
-{
-	return 0;
-}
-
 static inline int selinux_netlbl_socket_setsockopt(struct socket *sock,
 						   int level,
 						   int optname)
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 210eec7..b94378a 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -34,7 +34,7 @@
 #define POLICYDB_VERSION_MAX	POLICYDB_VERSION_RANGETRANS
 #endif
 
-struct sk_buff;
+struct netlbl_lsm_secattr;
 
 extern int selinux_enabled;
 extern int selinux_mls_enabled;
@@ -82,8 +82,6 @@
 int security_node_sid(u16 domain, void *addr, u32 addrlen,
 	u32 *out_sid);
 
-void security_skb_extlbl_sid(struct sk_buff *skb, u32 base_sid, u32 *sid);
-
 int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
                                  u16 tclass);
 
@@ -102,5 +100,30 @@
 int security_genfs_sid(const char *fstype, char *name, u16 sclass,
 	u32 *sid);
 
+#ifdef CONFIG_NETLABEL
+int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
+				   u32 base_sid,
+				   u32 *sid);
+
+int security_netlbl_sid_to_secattr(u32 sid,
+				   struct netlbl_lsm_secattr *secattr);
+#else
+static inline int security_netlbl_secattr_to_sid(
+					    struct netlbl_lsm_secattr *secattr,
+					    u32 base_sid,
+					    u32 *sid)
+{
+	return -EIDRM;
+}
+
+static inline int security_netlbl_sid_to_secattr(u32 sid,
+					   struct netlbl_lsm_secattr *secattr)
+{
+	return -ENOENT;
+}
+#endif /* CONFIG_NETLABEL */
+
+const char *security_get_initial_sid_context(u32 sid);
+
 #endif /* _SELINUX_SECURITY_H_ */
 
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
new file mode 100644
index 0000000..bf87507
--- /dev/null
+++ b/security/selinux/netlabel.c
@@ -0,0 +1,363 @@
+/*
+ * SELinux NetLabel Support
+ *
+ * This file provides the necessary glue to tie NetLabel into the SELinux
+ * subsystem.
+ *
+ * Author: Paul Moore <paul.moore@hp.com>
+ *
+ */
+
+/*
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2007
+ *
+ * This program is free software;  you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY;  without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program;  if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/spinlock.h>
+#include <linux/rcupdate.h>
+#include <net/sock.h>
+#include <net/netlabel.h>
+
+#include "objsec.h"
+#include "security.h"
+
+/**
+ * selinux_netlbl_socket_setsid - Label a socket using the NetLabel mechanism
+ * @sock: the socket to label
+ * @sid: the SID to use
+ *
+ * Description:
+ * Attempt to label a socket using the NetLabel mechanism using the given
+ * SID.  Returns zero values on success, negative values on failure.  The
+ * caller is responsibile for calling rcu_read_lock() before calling this
+ * this function and rcu_read_unlock() after this function returns.
+ *
+ */
+static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid)
+{
+	int rc;
+	struct sk_security_struct *sksec = sock->sk->sk_security;
+	struct netlbl_lsm_secattr secattr;
+
+	rc = security_netlbl_sid_to_secattr(sid, &secattr);
+	if (rc != 0)
+		return rc;
+
+	rc = netlbl_socket_setattr(sock, &secattr);
+	if (rc == 0) {
+		spin_lock_bh(&sksec->nlbl_lock);
+		sksec->nlbl_state = NLBL_LABELED;
+		spin_unlock_bh(&sksec->nlbl_lock);
+	}
+
+	return rc;
+}
+
+/**
+ * selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache
+ *
+ * Description:
+ * Invalidate the NetLabel security attribute mapping cache.
+ *
+ */
+void selinux_netlbl_cache_invalidate(void)
+{
+	netlbl_cache_invalidate();
+}
+
+/**
+ * selinux_netlbl_sk_security_reset - Reset the NetLabel fields
+ * @ssec: the sk_security_struct
+ * @family: the socket family
+ *
+ * Description:
+ * Called when the NetLabel state of a sk_security_struct needs to be reset.
+ * The caller is responsibile for all the NetLabel sk_security_struct locking.
+ *
+ */
+void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec,
+				      int family)
+{
+        if (family == PF_INET)
+		ssec->nlbl_state = NLBL_REQUIRE;
+	else
+		ssec->nlbl_state = NLBL_UNSET;
+}
+
+/**
+ * selinux_netlbl_sk_security_init - Setup the NetLabel fields
+ * @ssec: the sk_security_struct
+ * @family: the socket family
+ *
+ * Description:
+ * Called when a new sk_security_struct is allocated to initialize the NetLabel
+ * fields.
+ *
+ */
+void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec,
+				     int family)
+{
+	/* No locking needed, we are the only one who has access to ssec */
+	selinux_netlbl_sk_security_reset(ssec, family);
+	spin_lock_init(&ssec->nlbl_lock);
+}
+
+/**
+ * selinux_netlbl_sk_security_clone - Copy the NetLabel fields
+ * @ssec: the original sk_security_struct
+ * @newssec: the cloned sk_security_struct
+ *
+ * Description:
+ * Clone the NetLabel specific sk_security_struct fields from @ssec to
+ * @newssec.
+ *
+ */
+void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec,
+				      struct sk_security_struct *newssec)
+{
+	/* We don't need to take newssec->nlbl_lock because we are the only
+	 * thread with access to newssec, but we do need to take the RCU read
+	 * lock as other threads could have access to ssec */
+	rcu_read_lock();
+	selinux_netlbl_sk_security_reset(newssec, ssec->sk->sk_family);
+	newssec->sclass = ssec->sclass;
+	rcu_read_unlock();
+}
+
+/**
+ * selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel
+ * @skb: the packet
+ * @base_sid: the SELinux SID to use as a context for MLS only attributes
+ * @sid: the SID
+ *
+ * Description:
+ * Call the NetLabel mechanism to get the security attributes of the given
+ * packet and use those attributes to determine the correct context/SID to
+ * assign to the packet.  Returns zero on success, negative values on failure.
+ *
+ */
+int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid)
+{
+	int rc;
+	struct netlbl_lsm_secattr secattr;
+
+	netlbl_secattr_init(&secattr);
+	rc = netlbl_skbuff_getattr(skb, &secattr);
+	if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
+		rc = security_netlbl_secattr_to_sid(&secattr,
+						    base_sid,
+						    sid);
+	else
+		*sid = SECSID_NULL;
+	netlbl_secattr_destroy(&secattr);
+
+	return rc;
+}
+
+/**
+ * selinux_netlbl_sock_graft - Netlabel the new socket
+ * @sk: the new connection
+ * @sock: the new socket
+ *
+ * Description:
+ * The connection represented by @sk is being grafted onto @sock so set the
+ * socket's NetLabel to match the SID of @sk.
+ *
+ */
+void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)
+{
+	struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
+	struct sk_security_struct *sksec = sk->sk_security;
+	struct netlbl_lsm_secattr secattr;
+	u32 nlbl_peer_sid;
+
+	sksec->sclass = isec->sclass;
+
+	rcu_read_lock();
+
+	if (sksec->nlbl_state != NLBL_REQUIRE) {
+		rcu_read_unlock();
+		return;
+	}
+
+	netlbl_secattr_init(&secattr);
+	if (netlbl_sock_getattr(sk, &secattr) == 0 &&
+	    secattr.flags != NETLBL_SECATTR_NONE &&
+	    security_netlbl_secattr_to_sid(&secattr,
+					   SECINITSID_UNLABELED,
+					   &nlbl_peer_sid) == 0)
+		sksec->peer_sid = nlbl_peer_sid;
+	netlbl_secattr_destroy(&secattr);
+
+	/* Try to set the NetLabel on the socket to save time later, if we fail
+	 * here we will pick up the pieces in later calls to
+	 * selinux_netlbl_inode_permission(). */
+	selinux_netlbl_socket_setsid(sock, sksec->sid);
+
+	rcu_read_unlock();
+}
+
+/**
+ * selinux_netlbl_socket_post_create - Label a socket using NetLabel
+ * @sock: the socket to label
+ *
+ * Description:
+ * Attempt to label a socket using the NetLabel mechanism using the given
+ * SID.  Returns zero values on success, negative values on failure.
+ *
+ */
+int selinux_netlbl_socket_post_create(struct socket *sock)
+{
+	int rc = 0;
+	struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
+	struct sk_security_struct *sksec = sock->sk->sk_security;
+
+	sksec->sclass = isec->sclass;
+
+	rcu_read_lock();
+	if (sksec->nlbl_state == NLBL_REQUIRE)
+		rc = selinux_netlbl_socket_setsid(sock, sksec->sid);
+	rcu_read_unlock();
+
+	return rc;
+}
+
+/**
+ * selinux_netlbl_inode_permission - Verify the socket is NetLabel labeled
+ * @inode: the file descriptor's inode
+ * @mask: the permission mask
+ *
+ * Description:
+ * Looks at a file's inode and if it is marked as a socket protected by
+ * NetLabel then verify that the socket has been labeled, if not try to label
+ * the socket now with the inode's SID.  Returns zero on success, negative
+ * values on failure.
+ *
+ */
+int selinux_netlbl_inode_permission(struct inode *inode, int mask)
+{
+	int rc;
+	struct sk_security_struct *sksec;
+	struct socket *sock;
+
+	if (!S_ISSOCK(inode->i_mode) ||
+	    ((mask & (MAY_WRITE | MAY_APPEND)) == 0))
+		return 0;
+	sock = SOCKET_I(inode);
+	sksec = sock->sk->sk_security;
+
+	rcu_read_lock();
+	if (sksec->nlbl_state != NLBL_REQUIRE) {
+		rcu_read_unlock();
+		return 0;
+	}
+	local_bh_disable();
+	bh_lock_sock_nested(sock->sk);
+	rc = selinux_netlbl_socket_setsid(sock, sksec->sid);
+	bh_unlock_sock(sock->sk);
+	local_bh_enable();
+	rcu_read_unlock();
+
+	return rc;
+}
+
+/**
+ * selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel
+ * @sksec: the sock's sk_security_struct
+ * @skb: the packet
+ * @ad: the audit data
+ *
+ * Description:
+ * Fetch the NetLabel security attributes from @skb and perform an access check
+ * against the receiving socket.  Returns zero on success, negative values on
+ * error.
+ *
+ */
+int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
+				struct sk_buff *skb,
+				struct avc_audit_data *ad)
+{
+	int rc;
+	u32 netlbl_sid;
+	u32 recv_perm;
+
+	rc = selinux_netlbl_skbuff_getsid(skb,
+					  SECINITSID_UNLABELED,
+					  &netlbl_sid);
+	if (rc != 0)
+		return rc;
+
+	if (netlbl_sid == SECSID_NULL)
+		return 0;
+
+	switch (sksec->sclass) {
+	case SECCLASS_UDP_SOCKET:
+		recv_perm = UDP_SOCKET__RECVFROM;
+		break;
+	case SECCLASS_TCP_SOCKET:
+		recv_perm = TCP_SOCKET__RECVFROM;
+		break;
+	default:
+		recv_perm = RAWIP_SOCKET__RECVFROM;
+	}
+
+	rc = avc_has_perm(sksec->sid,
+			  netlbl_sid,
+			  sksec->sclass,
+			  recv_perm,
+			  ad);
+	if (rc == 0)
+		return 0;
+
+	netlbl_skbuff_err(skb, rc);
+	return rc;
+}
+
+/**
+ * selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel
+ * @sock: the socket
+ * @level: the socket level or protocol
+ * @optname: the socket option name
+ *
+ * Description:
+ * Check the setsockopt() call and if the user is trying to replace the IP
+ * options on a socket and a NetLabel is in place for the socket deny the
+ * access; otherwise allow the access.  Returns zero when the access is
+ * allowed, -EACCES when denied, and other negative values on error.
+ *
+ */
+int selinux_netlbl_socket_setsockopt(struct socket *sock,
+				     int level,
+				     int optname)
+{
+	int rc = 0;
+	struct sk_security_struct *sksec = sock->sk->sk_security;
+	struct netlbl_lsm_secattr secattr;
+
+	rcu_read_lock();
+	if (level == IPPROTO_IP && optname == IP_OPTIONS &&
+	    sksec->nlbl_state == NLBL_LABELED) {
+		netlbl_secattr_init(&secattr);
+		rc = netlbl_socket_getattr(sock, &secattr);
+		if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
+			rc = -EACCES;
+		netlbl_secattr_destroy(&secattr);
+	}
+	rcu_read_unlock();
+
+	return rc;
+}
diff --git a/security/selinux/netlink.c b/security/selinux/netlink.c
index e203883..f49046d 100644
--- a/security/selinux/netlink.c
+++ b/security/selinux/netlink.c
@@ -66,7 +66,7 @@
 static void selnl_notify(int msgtype, void *data)
 {
 	int len;
-	unsigned char *tmp;
+	sk_buff_data_t tmp;
 	struct sk_buff *skb;
 	struct nlmsghdr *nlh;
 	
@@ -104,7 +104,7 @@
 
 static int __init selnl_init(void)
 {
-	selnl = netlink_kernel_create(NETLINK_SELINUX, SELNLGRP_MAX, NULL,
+	selnl = netlink_kernel_create(NETLINK_SELINUX, SELNLGRP_MAX, NULL, NULL,
 	                              THIS_MODULE);
 	if (selnl == NULL)
 		panic("SELinux:  Cannot create netlink socket.");
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 93b3177..aca099a 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -96,12 +96,18 @@
 	SEL_COMMIT_BOOLS, /* commit new boolean values */
 	SEL_MLS,	/* return if MLS policy is enabled */
 	SEL_DISABLE,	/* disable SELinux until next reboot */
-	SEL_AVC,	/* AVC management directory */
 	SEL_MEMBER,	/* compute polyinstantiation membership decision */
 	SEL_CHECKREQPROT, /* check requested protection, not kernel-applied one */
 	SEL_COMPAT_NET,	/* whether to use old compat network packet controls */
+	SEL_INO_NEXT,	/* The next inode number to use */
 };
 
+static unsigned long sel_last_ino = SEL_INO_NEXT - 1;
+
+#define SEL_INITCON_INO_OFFSET 	0x01000000
+#define SEL_BOOL_INO_OFFSET	0x02000000
+#define SEL_INO_MASK		0x00ffffff
+
 #define TMPBUFLEN	12
 static ssize_t sel_read_enforce(struct file *filp, char __user *buf,
 				size_t count, loff_t *ppos)
@@ -777,8 +783,6 @@
 	return ret;
 }
 
-#define BOOL_INO_OFFSET 30
-
 static ssize_t sel_read_bool(struct file *filep, char __user *buf,
 			     size_t count, loff_t *ppos)
 {
@@ -806,14 +810,14 @@
 	}
 
 	inode = filep->f_path.dentry->d_inode;
-	cur_enforcing = security_get_bool_value(inode->i_ino - BOOL_INO_OFFSET);
+	cur_enforcing = security_get_bool_value(inode->i_ino&SEL_INO_MASK);
 	if (cur_enforcing < 0) {
 		ret = cur_enforcing;
 		goto out;
 	}
 
 	length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing,
-			  bool_pending_values[inode->i_ino - BOOL_INO_OFFSET]);
+			  bool_pending_values[inode->i_ino&SEL_INO_MASK]);
 	ret = simple_read_from_buffer(buf, count, ppos, page, length);
 out:
 	mutex_unlock(&sel_mutex);
@@ -865,7 +869,7 @@
 		new_value = 1;
 
 	inode = filep->f_path.dentry->d_inode;
-	bool_pending_values[inode->i_ino - BOOL_INO_OFFSET] = new_value;
+	bool_pending_values[inode->i_ino&SEL_INO_MASK] = new_value;
 	length = count;
 
 out:
@@ -1029,7 +1033,7 @@
 		isec->sid = sid;
 		isec->initialized = 1;
 		inode->i_fop = &sel_bool_ops;
-		inode->i_ino = i + BOOL_INO_OFFSET;
+		inode->i_ino = i|SEL_BOOL_INO_OFFSET;
 		d_add(dentry, inode);
 	}
 	bool_num = num;
@@ -1234,6 +1238,56 @@
 			goto out;
 		}
 		inode->i_fop = files[i].ops;
+		inode->i_ino = ++sel_last_ino;
+		d_add(dentry, inode);
+	}
+out:
+	return ret;
+}
+
+static ssize_t sel_read_initcon(struct file * file, char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	struct inode *inode;
+	char *con;
+	u32 sid, len;
+	ssize_t ret;
+
+	inode = file->f_path.dentry->d_inode;
+	sid = inode->i_ino&SEL_INO_MASK;
+	ret = security_sid_to_context(sid, &con, &len);
+	if (ret < 0)
+		return ret;
+
+	ret = simple_read_from_buffer(buf, count, ppos, con, len);
+	kfree(con);
+	return ret;
+}
+
+static const struct file_operations sel_initcon_ops = {
+	.read		= sel_read_initcon,
+};
+
+static int sel_make_initcon_files(struct dentry *dir)
+{
+	int i, ret = 0;
+
+	for (i = 1; i <= SECINITSID_NUM; i++) {
+		struct inode *inode;
+		struct dentry *dentry;
+		dentry = d_alloc_name(dir, security_get_initial_sid_context(i));
+		if (!dentry) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO);
+		if (!inode) {
+			ret = -ENOMEM;
+			goto out;
+		}
+		inode->i_fop = &sel_initcon_ops;
+		inode->i_ino = i|SEL_INITCON_INO_OFFSET;
 		d_add(dentry, inode);
 	}
 out:
@@ -1252,6 +1306,7 @@
 	}
 	inode->i_op = &simple_dir_inode_operations;
 	inode->i_fop = &simple_dir_operations;
+	inode->i_ino = ++sel_last_ino;
 	/* directory inodes start off with i_nlink == 2 (for "." entry) */
 	inc_nlink(inode);
 	d_add(dentry, inode);
@@ -1314,6 +1369,7 @@
 		ret = -ENOMEM;
 		goto err;
 	}
+	inode->i_ino = ++sel_last_ino;
 	isec = (struct inode_security_struct*)inode->i_security;
 	isec->sid = SECINITSID_DEVNULL;
 	isec->sclass = SECCLASS_CHR_FILE;
@@ -1336,6 +1392,21 @@
 	ret = sel_make_avc_files(dentry);
 	if (ret)
 		goto err;
+
+	dentry = d_alloc_name(sb->s_root, "initial_contexts");
+	if (!dentry) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	ret = sel_make_dir(root_inode, dentry);
+	if (ret)
+		goto err;
+
+	ret = sel_make_initcon_files(dentry);
+	if (ret)
+		goto err;
+
 out:
 	return ret;
 err:
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 1e52356..40660ff 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -39,7 +39,6 @@
 #include <linux/sched.h>
 #include <linux/audit.h>
 #include <linux/mutex.h>
-#include <net/sock.h>
 #include <net/netlabel.h>
 
 #include "flask.h"
@@ -53,7 +52,7 @@
 #include "conditional.h"
 #include "mls.h"
 #include "objsec.h"
-#include "selinux_netlabel.h"
+#include "netlabel.h"
 #include "xfrm.h"
 #include "ebitmap.h"
 
@@ -594,6 +593,13 @@
 
 #include "initial_sid_to_string.h"
 
+const char *security_get_initial_sid_context(u32 sid)
+{
+	if (unlikely(sid > SECINITSID_NUM))
+		return NULL;
+	return initial_sid_to_string[sid];
+}
+
 /**
  * security_sid_to_context - Obtain a context for a given SID.
  * @sid: security identifier, SID
@@ -1050,6 +1056,8 @@
 
 	for (i = 1; i < kdefs->cts_len; i++) {
 		def_class = kdefs->class_to_string[i];
+		if (!def_class)
+			continue;
 		if (i > p->p_classes.nprim) {
 			printk(KERN_INFO
 			       "security:  class %s not defined in policy\n",
@@ -1249,6 +1257,7 @@
 }
 
 extern void selinux_complete_init(void);
+static int security_preserve_bools(struct policydb *p);
 
 /**
  * security_load_policy - Load a security policy configuration.
@@ -1325,6 +1334,12 @@
 		goto err;
 	}
 
+	rc = security_preserve_bools(&newpolicydb);
+	if (rc) {
+		printk(KERN_ERR "security:  unable to preserve booleans\n");
+		goto err;
+	}
+
 	/* Clone the SID table. */
 	sidtab_shutdown(&sidtab);
 	if (sidtab_map(&sidtab, clone_sid, &newsidtab)) {
@@ -1882,6 +1897,37 @@
 	return rc;
 }
 
+static int security_preserve_bools(struct policydb *p)
+{
+	int rc, nbools = 0, *bvalues = NULL, i;
+	char **bnames = NULL;
+	struct cond_bool_datum *booldatum;
+	struct cond_node *cur;
+
+	rc = security_get_bools(&nbools, &bnames, &bvalues);
+	if (rc)
+		goto out;
+	for (i = 0; i < nbools; i++) {
+		booldatum = hashtab_search(p->p_bools.table, bnames[i]);
+		if (booldatum)
+			booldatum->state = bvalues[i];
+	}
+	for (cur = p->cond_list; cur != NULL; cur = cur->next) {
+		rc = evaluate_cond_node(p, cur);
+		if (rc)
+			goto out;
+	}
+
+out:
+	if (bnames) {
+		for (i = 0; i < nbools; i++)
+			kfree(bnames[i]);
+	}
+	kfree(bnames);
+	kfree(bvalues);
+	return rc;
+}
+
 /*
  * security_sid_mls_copy() - computes a new sid based on the given
  * sid and the mls portion of mls_sid.
@@ -2198,41 +2244,15 @@
 	aurule_callback = callback;
 }
 
-/**
- * security_skb_extlbl_sid - Determine the external label of a packet
- * @skb: the packet
- * @base_sid: the SELinux SID to use as a context for MLS only external labels
- * @sid: the packet's SID
- *
- * Description:
- * Check the various different forms of external packet labeling and determine
- * the external SID for the packet.
- *
- */
-void security_skb_extlbl_sid(struct sk_buff *skb, u32 base_sid, u32 *sid)
-{
-	u32 xfrm_sid;
-	u32 nlbl_sid;
-
-	selinux_skb_xfrm_sid(skb, &xfrm_sid);
-	if (selinux_netlbl_skbuff_getsid(skb,
-					 (xfrm_sid == SECSID_NULL ?
-					  base_sid : xfrm_sid),
-					 &nlbl_sid) != 0)
-		nlbl_sid = SECSID_NULL;
-
-	*sid = (nlbl_sid == SECSID_NULL ? xfrm_sid : nlbl_sid);
-}
-
 #ifdef CONFIG_NETLABEL
 /*
- * This is the structure we store inside the NetLabel cache block.
+ * NetLabel cache structure
  */
-#define NETLBL_CACHE(x)           ((struct netlbl_cache *)(x))
+#define NETLBL_CACHE(x)           ((struct selinux_netlbl_cache *)(x))
 #define NETLBL_CACHE_T_NONE       0
 #define NETLBL_CACHE_T_SID        1
 #define NETLBL_CACHE_T_MLS        2
-struct netlbl_cache {
+struct selinux_netlbl_cache {
 	u32 type;
 	union {
 		u32 sid;
@@ -2241,7 +2261,7 @@
 };
 
 /**
- * selinux_netlbl_cache_free - Free the NetLabel cached data
+ * security_netlbl_cache_free - Free the NetLabel cached data
  * @data: the data to free
  *
  * Description:
@@ -2249,9 +2269,9 @@
  * netlbl_lsm_cache structure.
  *
  */
-static void selinux_netlbl_cache_free(const void *data)
+static void security_netlbl_cache_free(const void *data)
 {
-	struct netlbl_cache *cache;
+	struct selinux_netlbl_cache *cache;
 
 	if (data == NULL)
 		return;
@@ -2266,33 +2286,33 @@
 }
 
 /**
- * selinux_netlbl_cache_add - Add an entry to the NetLabel cache
- * @skb: the packet
+ * security_netlbl_cache_add - Add an entry to the NetLabel cache
+ * @secattr: the NetLabel packet security attributes
  * @ctx: the SELinux context
  *
  * Description:
  * Attempt to cache the context in @ctx, which was derived from the packet in
- * @skb, in the NetLabel subsystem cache.
+ * @skb, in the NetLabel subsystem cache.  This function assumes @secattr has
+ * already been initialized.
  *
  */
-static void selinux_netlbl_cache_add(struct sk_buff *skb, struct context *ctx)
+static void security_netlbl_cache_add(struct netlbl_lsm_secattr *secattr,
+				      struct context *ctx)
 {
-	struct netlbl_cache *cache = NULL;
-	struct netlbl_lsm_secattr secattr;
+	struct selinux_netlbl_cache *cache = NULL;
 
-	netlbl_secattr_init(&secattr);
-	secattr.cache = netlbl_secattr_cache_alloc(GFP_ATOMIC);
-	if (secattr.cache == NULL)
-		goto netlbl_cache_add_return;
+	secattr->cache = netlbl_secattr_cache_alloc(GFP_ATOMIC);
+	if (secattr->cache == NULL)
+		return;
 
 	cache = kzalloc(sizeof(*cache),	GFP_ATOMIC);
 	if (cache == NULL)
-		goto netlbl_cache_add_return;
+		return;
 
 	cache->type = NETLBL_CACHE_T_MLS;
 	if (ebitmap_cpy(&cache->data.mls_label.level[0].cat,
 			&ctx->range.level[0].cat) != 0)
-		goto netlbl_cache_add_return;
+		return;
 	cache->data.mls_label.level[1].cat.highbit =
 		cache->data.mls_label.level[0].cat.highbit;
 	cache->data.mls_label.level[1].cat.node =
@@ -2300,52 +2320,40 @@
 	cache->data.mls_label.level[0].sens = ctx->range.level[0].sens;
 	cache->data.mls_label.level[1].sens = ctx->range.level[0].sens;
 
-	secattr.cache->free = selinux_netlbl_cache_free;
-	secattr.cache->data = (void *)cache;
-	secattr.flags = NETLBL_SECATTR_CACHE;
-
-	netlbl_cache_add(skb, &secattr);
-
-netlbl_cache_add_return:
-	netlbl_secattr_destroy(&secattr);
+	secattr->cache->free = security_netlbl_cache_free;
+	secattr->cache->data = (void *)cache;
+	secattr->flags |= NETLBL_SECATTR_CACHE;
 }
 
 /**
- * selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache
- *
- * Description:
- * Invalidate the NetLabel security attribute mapping cache.
- *
- */
-void selinux_netlbl_cache_invalidate(void)
-{
-	netlbl_cache_invalidate();
-}
-
-/**
- * selinux_netlbl_secattr_to_sid - Convert a NetLabel secattr to a SELinux SID
- * @skb: the network packet
+ * security_netlbl_secattr_to_sid - Convert a NetLabel secattr to a SELinux SID
  * @secattr: the NetLabel packet security attributes
  * @base_sid: the SELinux SID to use as a context for MLS only attributes
  * @sid: the SELinux SID
  *
  * Description:
- * Convert the given NetLabel packet security attributes in @secattr into a
+ * Convert the given NetLabel security attributes in @secattr into a
  * SELinux SID.  If the @secattr field does not contain a full SELinux
- * SID/context then use the context in @base_sid as the foundation.  If @skb
- * is not NULL attempt to cache as much data as possibile.  Returns zero on
- * success, negative values on failure.
+ * SID/context then use the context in @base_sid as the foundation.  If
+ * possibile the 'cache' field of @secattr is set and the CACHE flag is set;
+ * this is to allow the @secattr to be used by NetLabel to cache the secattr to
+ * SID conversion for future lookups.  Returns zero on success, negative
+ * values on failure.
  *
  */
-static int selinux_netlbl_secattr_to_sid(struct sk_buff *skb,
-					 struct netlbl_lsm_secattr *secattr,
-					 u32 base_sid,
-					 u32 *sid)
+int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
+				   u32 base_sid,
+				   u32 *sid)
 {
 	int rc = -EIDRM;
 	struct context *ctx;
 	struct context ctx_new;
-	struct netlbl_cache *cache;
+	struct selinux_netlbl_cache *cache;
+
+	if (!ss_initialized) {
+		*sid = SECSID_NULL;
+		return 0;
+	}
 
 	POLICY_RDLOCK;
 
@@ -2410,8 +2418,8 @@
 		if (rc != 0)
 			goto netlbl_secattr_to_sid_return_cleanup;
 
-		if (skb != NULL)
-			selinux_netlbl_cache_add(skb, &ctx_new);
+		security_netlbl_cache_add(secattr, &ctx_new);
+
 		ebitmap_destroy(&ctx_new.range.level[0].cat);
 	} else {
 		*sid = SECSID_NULL;
@@ -2427,338 +2435,43 @@
 }
 
 /**
- * selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel
- * @skb: the packet
- * @base_sid: the SELinux SID to use as a context for MLS only attributes
- * @sid: the SID
+ * security_netlbl_sid_to_secattr - Convert a SELinux SID to a NetLabel secattr
+ * @sid: the SELinux SID
+ * @secattr: the NetLabel packet security attributes
  *
  * Description:
- * Call the NetLabel mechanism to get the security attributes of the given
- * packet and use those attributes to determine the correct context/SID to
- * assign to the packet.  Returns zero on success, negative values on failure.
+ * Convert the given SELinux SID in @sid into a NetLabel security attribute.
+ * Returns zero on success, negative values on failure.
  *
  */
-int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid)
-{
-	int rc;
-	struct netlbl_lsm_secattr secattr;
-
-	netlbl_secattr_init(&secattr);
-	rc = netlbl_skbuff_getattr(skb, &secattr);
-	if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
-		rc = selinux_netlbl_secattr_to_sid(skb,
-						   &secattr,
-						   base_sid,
-						   sid);
-	else
-		*sid = SECSID_NULL;
-	netlbl_secattr_destroy(&secattr);
-
-	return rc;
-}
-
-/**
- * selinux_netlbl_socket_setsid - Label a socket using the NetLabel mechanism
- * @sock: the socket to label
- * @sid: the SID to use
- *
- * Description:
- * Attempt to label a socket using the NetLabel mechanism using the given
- * SID.  Returns zero values on success, negative values on failure.  The
- * caller is responsibile for calling rcu_read_lock() before calling this
- * this function and rcu_read_unlock() after this function returns.
- *
- */
-static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid)
+int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr)
 {
 	int rc = -ENOENT;
-	struct sk_security_struct *sksec = sock->sk->sk_security;
-	struct netlbl_lsm_secattr secattr;
 	struct context *ctx;
 
+	netlbl_secattr_init(secattr);
+
 	if (!ss_initialized)
 		return 0;
 
-	netlbl_secattr_init(&secattr);
-
 	POLICY_RDLOCK;
-
 	ctx = sidtab_search(&sidtab, sid);
 	if (ctx == NULL)
-		goto netlbl_socket_setsid_return;
-
-	secattr.domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1],
-				 GFP_ATOMIC);
-	secattr.flags |= NETLBL_SECATTR_DOMAIN;
-	mls_export_netlbl_lvl(ctx, &secattr);
-	rc = mls_export_netlbl_cat(ctx, &secattr);
+		goto netlbl_sid_to_secattr_failure;
+	secattr->domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1],
+				  GFP_ATOMIC);
+	secattr->flags |= NETLBL_SECATTR_DOMAIN;
+	mls_export_netlbl_lvl(ctx, secattr);
+	rc = mls_export_netlbl_cat(ctx, secattr);
 	if (rc != 0)
-		goto netlbl_socket_setsid_return;
-
-	rc = netlbl_socket_setattr(sock, &secattr);
-	if (rc == 0) {
-		spin_lock_bh(&sksec->nlbl_lock);
-		sksec->nlbl_state = NLBL_LABELED;
-		spin_unlock_bh(&sksec->nlbl_lock);
-	}
-
-netlbl_socket_setsid_return:
+		goto netlbl_sid_to_secattr_failure;
 	POLICY_RDUNLOCK;
-	netlbl_secattr_destroy(&secattr);
-	return rc;
-}
 
-/**
- * selinux_netlbl_sk_security_reset - Reset the NetLabel fields
- * @ssec: the sk_security_struct
- * @family: the socket family
- *
- * Description:
- * Called when the NetLabel state of a sk_security_struct needs to be reset.
- * The caller is responsibile for all the NetLabel sk_security_struct locking.
- *
- */
-void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec,
-				      int family)
-{
-        if (family == PF_INET)
-		ssec->nlbl_state = NLBL_REQUIRE;
-	else
-		ssec->nlbl_state = NLBL_UNSET;
-}
+	return 0;
 
-/**
- * selinux_netlbl_sk_security_init - Setup the NetLabel fields
- * @ssec: the sk_security_struct
- * @family: the socket family
- *
- * Description:
- * Called when a new sk_security_struct is allocated to initialize the NetLabel
- * fields.
- *
- */
-void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec,
-				     int family)
-{
-	/* No locking needed, we are the only one who has access to ssec */
-	selinux_netlbl_sk_security_reset(ssec, family);
-	spin_lock_init(&ssec->nlbl_lock);
-}
-
-/**
- * selinux_netlbl_sk_security_clone - Copy the NetLabel fields
- * @ssec: the original sk_security_struct
- * @newssec: the cloned sk_security_struct
- *
- * Description:
- * Clone the NetLabel specific sk_security_struct fields from @ssec to
- * @newssec.
- *
- */
-void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec,
-				      struct sk_security_struct *newssec)
-{
-	/* We don't need to take newssec->nlbl_lock because we are the only
-	 * thread with access to newssec, but we do need to take the RCU read
-	 * lock as other threads could have access to ssec */
-	rcu_read_lock();
-	selinux_netlbl_sk_security_reset(newssec, ssec->sk->sk_family);
-	newssec->sclass = ssec->sclass;
-	rcu_read_unlock();
-}
-
-/**
- * selinux_netlbl_socket_post_create - Label a socket using NetLabel
- * @sock: the socket to label
- *
- * Description:
- * Attempt to label a socket using the NetLabel mechanism using the given
- * SID.  Returns zero values on success, negative values on failure.
- *
- */
-int selinux_netlbl_socket_post_create(struct socket *sock)
-{
-	int rc = 0;
-	struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
-	struct sk_security_struct *sksec = sock->sk->sk_security;
-
-	sksec->sclass = isec->sclass;
-
-	rcu_read_lock();
-	if (sksec->nlbl_state == NLBL_REQUIRE)
-		rc = selinux_netlbl_socket_setsid(sock, sksec->sid);
-	rcu_read_unlock();
-
-	return rc;
-}
-
-/**
- * selinux_netlbl_sock_graft - Netlabel the new socket
- * @sk: the new connection
- * @sock: the new socket
- *
- * Description:
- * The connection represented by @sk is being grafted onto @sock so set the
- * socket's NetLabel to match the SID of @sk.
- *
- */
-void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)
-{
-	struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
-	struct sk_security_struct *sksec = sk->sk_security;
-	struct netlbl_lsm_secattr secattr;
-	u32 nlbl_peer_sid;
-
-	sksec->sclass = isec->sclass;
-
-	rcu_read_lock();
-
-	if (sksec->nlbl_state != NLBL_REQUIRE) {
-		rcu_read_unlock();
-		return;
-	}
-
-	netlbl_secattr_init(&secattr);
-	if (netlbl_sock_getattr(sk, &secattr) == 0 &&
-	    secattr.flags != NETLBL_SECATTR_NONE &&
-	    selinux_netlbl_secattr_to_sid(NULL,
-					  &secattr,
-					  SECINITSID_UNLABELED,
-					  &nlbl_peer_sid) == 0)
-		sksec->peer_sid = nlbl_peer_sid;
-	netlbl_secattr_destroy(&secattr);
-
-	/* Try to set the NetLabel on the socket to save time later, if we fail
-	 * here we will pick up the pieces in later calls to
-	 * selinux_netlbl_inode_permission(). */
-	selinux_netlbl_socket_setsid(sock, sksec->sid);
-
-	rcu_read_unlock();
-}
-
-/**
- * selinux_netlbl_inode_permission - Verify the socket is NetLabel labeled
- * @inode: the file descriptor's inode
- * @mask: the permission mask
- *
- * Description:
- * Looks at a file's inode and if it is marked as a socket protected by
- * NetLabel then verify that the socket has been labeled, if not try to label
- * the socket now with the inode's SID.  Returns zero on success, negative
- * values on failure.
- *
- */
-int selinux_netlbl_inode_permission(struct inode *inode, int mask)
-{
-	int rc;
-	struct sk_security_struct *sksec;
-	struct socket *sock;
-
-	if (!S_ISSOCK(inode->i_mode) ||
-	    ((mask & (MAY_WRITE | MAY_APPEND)) == 0))
-		return 0;
-	sock = SOCKET_I(inode);
-	sksec = sock->sk->sk_security;
-
-	rcu_read_lock();
-	if (sksec->nlbl_state != NLBL_REQUIRE) {
-		rcu_read_unlock();
-		return 0;
-	}
-	local_bh_disable();
-	bh_lock_sock_nested(sock->sk);
-	rc = selinux_netlbl_socket_setsid(sock, sksec->sid);
-	bh_unlock_sock(sock->sk);
-	local_bh_enable();
-	rcu_read_unlock();
-
-	return rc;
-}
-
-/**
- * selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel
- * @sksec: the sock's sk_security_struct
- * @skb: the packet
- * @ad: the audit data
- *
- * Description:
- * Fetch the NetLabel security attributes from @skb and perform an access check
- * against the receiving socket.  Returns zero on success, negative values on
- * error.
- *
- */
-int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
-				struct sk_buff *skb,
-				struct avc_audit_data *ad)
-{
-	int rc;
-	u32 netlbl_sid;
-	u32 recv_perm;
-
-	rc = selinux_netlbl_skbuff_getsid(skb,
-					  SECINITSID_UNLABELED,
-					  &netlbl_sid);
-	if (rc != 0)
-		return rc;
-
-	if (netlbl_sid == SECSID_NULL)
-		return 0;
-
-	switch (sksec->sclass) {
-	case SECCLASS_UDP_SOCKET:
-		recv_perm = UDP_SOCKET__RECVFROM;
-		break;
-	case SECCLASS_TCP_SOCKET:
-		recv_perm = TCP_SOCKET__RECVFROM;
-		break;
-	default:
-		recv_perm = RAWIP_SOCKET__RECVFROM;
-	}
-
-	rc = avc_has_perm(sksec->sid,
-			  netlbl_sid,
-			  sksec->sclass,
-			  recv_perm,
-			  ad);
-	if (rc == 0)
-		return 0;
-
-	netlbl_skbuff_err(skb, rc);
-	return rc;
-}
-
-/**
- * selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel
- * @sock: the socket
- * @level: the socket level or protocol
- * @optname: the socket option name
- *
- * Description:
- * Check the setsockopt() call and if the user is trying to replace the IP
- * options on a socket and a NetLabel is in place for the socket deny the
- * access; otherwise allow the access.  Returns zero when the access is
- * allowed, -EACCES when denied, and other negative values on error.
- *
- */
-int selinux_netlbl_socket_setsockopt(struct socket *sock,
-				     int level,
-				     int optname)
-{
-	int rc = 0;
-	struct sk_security_struct *sksec = sock->sk->sk_security;
-	struct netlbl_lsm_secattr secattr;
-
-	rcu_read_lock();
-	if (level == IPPROTO_IP && optname == IP_OPTIONS &&
-	    sksec->nlbl_state == NLBL_LABELED) {
-		netlbl_secattr_init(&secattr);
-		rc = netlbl_socket_getattr(sock, &secattr);
-		if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
-			rc = -EACCES;
-		netlbl_secattr_destroy(&secattr);
-	}
-	rcu_read_unlock();
-
+netlbl_sid_to_secattr_failure:
+	POLICY_RDUNLOCK;
+	netlbl_secattr_destroy(secattr);
 	return rc;
 }
 #endif /* CONFIG_NETLABEL */
diff --git a/sound/aoa/codecs/snd-aoa-codec-onyx.c b/sound/aoa/codecs/snd-aoa-codec-onyx.c
index b00fc48..7f980be 100644
--- a/sound/aoa/codecs/snd-aoa-codec-onyx.c
+++ b/sound/aoa/codecs/snd-aoa-codec-onyx.c
@@ -1062,9 +1062,9 @@
 
 	while ((dev = of_get_next_child(busnode, dev)) != NULL) {
 		if (device_is_compatible(dev, "pcm3052")) {
-			u32 *addr;
+			const u32 *addr;
 			printk(KERN_DEBUG PFX "found pcm3052\n");
-			addr = (u32 *) get_property(dev, "reg", NULL);
+			addr = of_get_property(dev, "reg", NULL);
 			if (!addr)
 				return -ENODEV;
 			return onyx_create(adapter, dev, (*addr)>>1);
diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.c b/sound/aoa/codecs/snd-aoa-codec-tas.c
index 2cd81fa..ceca384 100644
--- a/sound/aoa/codecs/snd-aoa-codec-tas.c
+++ b/sound/aoa/codecs/snd-aoa-codec-tas.c
@@ -939,9 +939,9 @@
 
 	while ((dev = of_get_next_child(busnode, dev)) != NULL) {
 		if (device_is_compatible(dev, "tas3004")) {
-			u32 *addr;
+			const u32 *addr;
 			printk(KERN_DEBUG PFX "found tas3004\n");
-			addr = (u32 *) get_property(dev, "reg", NULL);
+			addr = of_get_property(dev, "reg", NULL);
 			if (!addr)
 				continue;
 			return tas_create(adapter, dev, ((*addr) >> 1) & 0x7f);
@@ -950,9 +950,10 @@
 		 * property that says 'tas3004', they just have a 'deq'
 		 * node without any such property... */
 		if (strcmp(dev->name, "deq") == 0) {
-			u32 *_addr, addr;
+			const u32 *_addr;
+			u32 addr;
 			printk(KERN_DEBUG PFX "found 'deq' node\n");
-			_addr = (u32 *) get_property(dev, "i2c-address", NULL);
+			_addr = of_get_property(dev, "i2c-address", NULL);
 			if (!_addr)
 				continue;
 			addr = ((*_addr) >> 1) & 0x7f;
diff --git a/sound/aoa/core/snd-aoa-gpio-feature.c b/sound/aoa/core/snd-aoa-gpio-feature.c
index 2b03bc7..805dcbf 100644
--- a/sound/aoa/core/snd-aoa-gpio-feature.c
+++ b/sound/aoa/core/snd-aoa-gpio-feature.c
@@ -55,7 +55,7 @@
 				    int *gpioactiveptr)
 {
 	struct device_node *np, *gpio;
-	u32 *reg;
+	const u32 *reg;
 	const char *audio_gpio;
 
 	*gpioptr = -1;
@@ -71,7 +71,7 @@
 		if (!gpio)
 			return NULL;
 		while ((np = of_get_next_child(gpio, np))) {
-			audio_gpio = get_property(np, "audio-gpio", NULL);
+			audio_gpio = of_get_property(np, "audio-gpio", NULL);
 			if (!audio_gpio)
 				continue;
 			if (strcmp(audio_gpio, name) == 0)
@@ -84,7 +84,7 @@
 			return NULL;
 	}
 
-	reg = (u32 *)get_property(np, "reg", NULL);
+	reg = of_get_property(np, "reg", NULL);
 	if (!reg)
 		return NULL;
 
@@ -96,7 +96,7 @@
 	if (*gpioptr < 0x50)
 		*gpioptr += 0x50;
 
-	reg = (u32 *)get_property(np, "audio-gpio-active-state", NULL);
+	reg = of_get_property(np, "audio-gpio-active-state", NULL);
 	if (!reg)
 		/* Apple seems to default to 1, but
 		 * that doesn't seem right at least on most
diff --git a/sound/aoa/fabrics/snd-aoa-fabric-layout.c b/sound/aoa/fabrics/snd-aoa-fabric-layout.c
index 1b94ba6..9880628 100644
--- a/sound/aoa/fabrics/snd-aoa-fabric-layout.c
+++ b/sound/aoa/fabrics/snd-aoa-fabric-layout.c
@@ -724,7 +724,7 @@
 		       struct layout_dev *ldev,
 		       struct codec_connect_info *cci)
 {
-	u32 *ref;
+	const u32 *ref;
 	char propname[32];
 	struct codec_connection *cc;
 
@@ -732,7 +732,7 @@
 	if (codec->node && (strcmp(codec->node->name, "codec") == 0)) {
 		snprintf(propname, sizeof(propname),
 			 "platform-%s-codec-ref", codec->name);
-		ref = (u32*)get_property(ldev->sound, propname, NULL);
+		ref = of_get_property(ldev->sound, propname, NULL);
 		if (!ref) {
 			printk(KERN_INFO "snd-aoa-fabric-layout: "
 				"required property %s not present\n", propname);
@@ -946,7 +946,7 @@
 static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
 {
 	struct device_node *sound = NULL;
-	unsigned int *layout_id;
+	const unsigned int *layout_id;
 	struct layout *layout;
 	struct layout_dev *ldev = NULL;
 	int err;
@@ -962,7 +962,7 @@
 	}
 	if (!sound) return -ENODEV;
 
-	layout_id = (unsigned int *) get_property(sound, "layout-id", NULL);
+	layout_id = of_get_property(sound, "layout-id", NULL);
 	if (!layout_id)
 		goto outnodev;
 	printk(KERN_INFO "snd-aoa-fabric-layout: found bus with layout %d\n",
diff --git a/sound/aoa/soundbus/core.c b/sound/aoa/soundbus/core.c
index 47b3e37..8b2e9b9 100644
--- a/sound/aoa/soundbus/core.c
+++ b/sound/aoa/soundbus/core.c
@@ -61,9 +61,9 @@
 {
 	struct soundbus_dev * soundbus_dev;
 	struct of_device * of;
-	char *scratch, *compat, *compat2;
-	int i = 0;
-	int length, cplen, cplen2, seen = 0;
+	const char *compat;
+	int retval = 0, i = 0, length = 0;
+	int cplen, seen = 0;
 
 	if (!dev)
 		return -ENODEV;
@@ -75,63 +75,47 @@
 	of = &soundbus_dev->ofdev;
 
 	/* stuff we want to pass to /sbin/hotplug */
-	envp[i++] = scratch = buffer;
-	length = scnprintf (scratch, buffer_size, "OF_NAME=%s", of->node->name);
-	++length;
-	buffer_size -= length;
-	if ((buffer_size <= 0) || (i >= num_envp))
-		return -ENOMEM;
-	scratch += length;
+	retval = add_uevent_var(envp, num_envp, &i,
+				buffer, buffer_size, &length,
+				"OF_NAME=%s", of->node->name);
+	if (retval)
+		return retval;
 
-	envp[i++] = scratch;
-	length = scnprintf (scratch, buffer_size, "OF_TYPE=%s", of->node->type);
-	++length;
-	buffer_size -= length;
-	if ((buffer_size <= 0) || (i >= num_envp))
-		return -ENOMEM;
-	scratch += length;
+	retval = add_uevent_var(envp, num_envp, &i,
+				buffer, buffer_size, &length,
+				"OF_TYPE=%s", of->node->type);
+	if (retval)
+		return retval;
 
 	/* Since the compatible field can contain pretty much anything
 	 * it's not really legal to split it out with commas. We split it
 	 * up using a number of environment variables instead. */
 
-	compat = (char *) get_property(of->node, "compatible", &cplen);
-	compat2 = compat;
-	cplen2= cplen;
+	compat = of_get_property(of->node, "compatible", &cplen);
 	while (compat && cplen > 0) {
-		envp[i++] = scratch;
-		length = scnprintf (scratch, buffer_size,
-				     "OF_COMPATIBLE_%d=%s", seen, compat);
-		++length;
-		buffer_size -= length;
-		if ((buffer_size <= 0) || (i >= num_envp))
-			return -ENOMEM;
-		scratch += length;
-		length = strlen (compat) + 1;
-		compat += length;
-		cplen -= length;
-		seen++;
+		int tmp = length;
+		retval = add_uevent_var(envp, num_envp, &i,
+					buffer, buffer_size, &length,
+					"OF_COMPATIBLE_%d=%s", seen, compat);
+		if (retval)
+			return retval;
+		compat += length - tmp;
+		cplen -= length - tmp;
+		seen += 1;
 	}
 
-	envp[i++] = scratch;
-	length = scnprintf (scratch, buffer_size, "OF_COMPATIBLE_N=%d", seen);
-	++length;
-	buffer_size -= length;
-	if ((buffer_size <= 0) || (i >= num_envp))
-		return -ENOMEM;
-	scratch += length;
-
-	envp[i++] = scratch;
-	length = scnprintf (scratch, buffer_size, "MODALIAS=%s",
-			soundbus_dev->modalias);
-
-	buffer_size -= length;
-	if ((buffer_size <= 0) || (i >= num_envp))
-		return -ENOMEM;
+	retval = add_uevent_var(envp, num_envp, &i,
+				buffer, buffer_size, &length,
+				"OF_COMPATIBLE_N=%d", seen);
+	if (retval)
+		return retval;
+	retval = add_uevent_var(envp, num_envp, &i,
+				buffer, buffer_size, &length,
+				"MODALIAS=%s", soundbus_dev->modalias);
 
 	envp[i] = NULL;
 
-	return 0;
+	return retval;
 }
 
 static int soundbus_device_remove(struct device *dev)
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-core.c b/sound/aoa/soundbus/i2sbus/i2sbus-core.c
index e36f6aa..79fc4bc 100644
--- a/sound/aoa/soundbus/i2sbus/i2sbus-core.c
+++ b/sound/aoa/soundbus/i2sbus/i2sbus-core.c
@@ -122,7 +122,7 @@
 {
 	struct device_node *parent;
 	int pindex, rc = -ENXIO;
-	u32 *reg;
+	const u32 *reg;
 
 	/* Machines with layout 76 and 36 (K2 based) have a weird device
 	 * tree what we need to special case.
@@ -141,7 +141,7 @@
 	rc = of_address_to_resource(parent, pindex, res);
 	if (rc)
 		goto bail;
-	reg = (u32 *)get_property(np, "reg", NULL);
+	reg = of_get_property(np, "reg", NULL);
 	if (reg == NULL) {
 		rc = -ENXIO;
 		goto bail;
@@ -188,8 +188,8 @@
 		}
 	}
 	if (i == 1) {
-		u32 *layout_id;
-		layout_id = (u32*) get_property(sound, "layout-id", NULL);
+		const u32 *layout_id =
+			of_get_property(sound, "layout-id", NULL);
 		if (layout_id) {
 			layout = *layout_id;
 			snprintf(dev->sound.modalias, 32,
diff --git a/sound/oss/dmasound/dmasound_awacs.c b/sound/oss/dmasound/dmasound_awacs.c
index 37773b1..730fa1d 100644
--- a/sound/oss/dmasound/dmasound_awacs.c
+++ b/sound/oss/dmasound/dmasound_awacs.c
@@ -257,7 +257,7 @@
 /*
  * Stuff for restoring after a sleep.
  */
-static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when);
+static void awacs_sleep_notify(struct pmu_sleep_notifier *self, int when);
 struct pmu_sleep_notifier awacs_sleep_notifier = {
 	awacs_sleep_notify, SLEEP_LEVEL_SOUND,
 };
@@ -346,36 +346,42 @@
 int
 setup_audio_gpio(const char *name, const char* compatible, int *gpio_addr, int* gpio_pol)
 {
+	struct device_node *gpiop;
 	struct device_node *np;
 	const u32* pp;
+	int ret = -ENODEV;
 
-	np = find_devices("gpio");
-	if (!np)
-		return -ENODEV;
+	gpiop = of_find_node_by_name(NULL, "gpio");
+	if (!gpiop)
+		goto done;
 
-	np = np->child;
+	np = of_get_next_child(gpiop, NULL);
 	while(np != 0) {
 		if (name) {
 			const char *property =
-				get_property(np,"audio-gpio",NULL);
+				of_get_property(np,"audio-gpio",NULL);
 			if (property != 0 && strcmp(property,name) == 0)
 				break;
 		} else if (compatible && device_is_compatible(np, compatible))
 			break;
-		np = np->sibling;
+		np = of_get_next_child(gpiop, np);
 	}
 	if (!np)
-		return -ENODEV;
-	pp = get_property(np, "AAPL,address", NULL);
+		goto done;
+	pp = of_get_property(np, "AAPL,address", NULL);
 	if (!pp)
-		return -ENODEV;
+		goto done;
 	*gpio_addr = (*pp) & 0x0000ffff;
-	pp = get_property(np, "audio-gpio-active-state", NULL);
+	pp = of_get_property(np, "audio-gpio-active-state", NULL);
 	if (pp)
 		*gpio_pol = *pp;
 	else
 		*gpio_pol = 1;
-	return irq_of_parse_and_map(np, 0);
+	ret = irq_of_parse_and_map(np, 0);
+done:
+	of_node_put(np);
+	of_node_put(gpiop);
+	return ret;
 }
 
 static inline void
@@ -578,7 +584,7 @@
 }
 
 static void __init
-tas_init_frame_rates(unsigned int *prop, unsigned int l)
+tas_init_frame_rates(const unsigned int *prop, unsigned int l)
 {
 	int i ;
 	if (prop) {
@@ -1419,7 +1425,7 @@
  * Save state when going to sleep, restore it afterwards.
  */
 /* FIXME: sort out disabling/re-enabling of read stuff as well */
-static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when)
+static void awacs_sleep_notify(struct pmu_sleep_notifier *self, int when)
 {
 	unsigned long flags;
 
@@ -1548,7 +1554,6 @@
 		spin_unlock_irqrestore(&dmasound.lock, flags);
 		UNLOCK();
 	}
-	return PBOOK_SLEEP_OK;
 }
 #endif /* CONFIG_PM */
 
@@ -2553,32 +2558,33 @@
 static struct device_node* __init
 get_snd_io_node(void)
 {
-	struct device_node *np = NULL;
+	struct device_node *np;
 
 	/* set up awacs_node for early OF which doesn't have a full set of
 	 * properties on davbus
-	*/
-
-	awacs_node = find_devices("awacs");
+	 */
+	awacs_node = of_find_node_by_name(NULL, "awacs");
 	if (awacs_node)
 		awacs_revision = AWACS_AWACS;
 
 	/* powermac models after 9500 (other than those which use DACA or
 	 * Tumbler) have a node called "davbus".
 	 */
-	np = find_devices("davbus");
+	np = of_find_node_by_name(NULL, "davbus");
 	/*
 	 * if we didn't find a davbus device, try 'i2s-a' since
 	 * this seems to be what iBooks (& Tumbler) have.
 	 */
-	if (np == NULL)
-		np = i2s_node = find_devices("i2s-a");
+	if (np == NULL) {
+		i2s_node = of_find_node_by_name(NULL, "i2s-a");
+		np = of_node_get(i2s_node);
+	}
 
 	/* if we didn't find this - perhaps we are on an early model
 	 * which _only_ has an 'awacs' node
 	*/
 	if (np == NULL && awacs_node)
-		np = awacs_node ;
+		np = of_node_get(awacs_node);
 
 	/* if we failed all these return null - this will cause the
 	 * driver to give up...
@@ -2597,9 +2603,9 @@
 {
 	struct device_node *info;
 
-	info = find_devices("sound");
-	while (info && info->parent != io)
-		info = info->next;
+	for_each_node_by_name(info, "sound")
+		if (info->parent == io)
+			break;
 	return info;
 }
 
@@ -2635,11 +2641,17 @@
 static void __init
 get_expansion_type(void)
 {
-	if (find_devices("perch") != NULL)
-		has_perch = 1;
+	struct device_node *dn;
 
-	if (find_devices("pb-ziva-pc") != NULL)
+	dn = of_find_node_by_name(NULL, "perch");
+	if (dn != NULL)
+		has_perch = 1;
+	of_node_put(dn);
+
+	dn = of_find_node_by_name(NULL, "pb-ziva-pc");
+	if (dn != NULL)
 		has_ziva = 1;
+	of_node_put(dn);
 	/* need to work out how we deal with iMac SRS module */
 }
 
@@ -2652,7 +2664,7 @@
 */
 
 static void __init
-awacs_init_frame_rates(unsigned int *prop, unsigned int l)
+awacs_init_frame_rates(const unsigned int *prop, unsigned int l)
 {
 	int i ;
 	if (prop) {
@@ -2675,7 +2687,7 @@
 }
 
 static void __init
-burgundy_init_frame_rates(unsigned int *prop, unsigned int l)
+burgundy_init_frame_rates(const unsigned int *prop, unsigned int l)
 {
 	int temp[9] ;
 	int i = 0 ;
@@ -2701,7 +2713,7 @@
 }
 
 static void __init
-daca_init_frame_rates(unsigned int *prop, unsigned int l)
+daca_init_frame_rates(const unsigned int *prop, unsigned int l)
 {
 	int temp[9] ;
 	int i = 0 ;
@@ -2728,7 +2740,7 @@
 }
 
 static void __init
-init_frame_rates(unsigned int *prop, unsigned int l)
+init_frame_rates(const unsigned int *prop, unsigned int l)
 {
 	switch (awacs_revision) {
 		case AWACS_TUMBLER:
@@ -2828,7 +2840,7 @@
 #ifdef DEBUG_DMASOUND
 printk("dmasound_pmac: couldn't find sound io OF node\n");
 #endif
-		return -ENODEV ;
+		goto no_device;
 	}
 
 	/* find the OF node that tells us about the sound sub-system
@@ -2840,7 +2852,7 @@
 #ifdef DEBUG_DMASOUND
 printk("dmasound_pmac: couldn't find 'sound' OF node\n");
 #endif
-			return -ENODEV ;
+			goto no_device;
 		}
 	}
 
@@ -2849,7 +2861,7 @@
 #ifdef DEBUG_DMASOUND
 printk("dmasound_pmac: couldn't find a Codec we can handle\n");
 #endif
-		return -ENODEV ; /* we don't know this type of h/w */
+		goto no_device; /* we don't know this type of h/w */
 	}
 
 	/* set up perch, ziva, SRS or whatever else we have as sound
@@ -2867,11 +2879,12 @@
 		 * machines).
 		 */
 		if (awacs_node) {
-			io = awacs_node ;
+			of_node_put(io);
+			io = of_node_get(awacs_node);
 			if (of_get_address(io, 2, NULL, NULL) == NULL) {
 				printk("dmasound_pmac: can't use %s\n",
 				       io->full_name);
-				return -ENODEV;
+				goto no_device;
 			}
 		} else
 			printk("dmasound_pmac: can't use %s\n", io->full_name);
@@ -2882,7 +2895,7 @@
 			       awacs_rsrc[0].end - awacs_rsrc[0].start + 1,
 			       " (IO)") == NULL) {
 		printk(KERN_ERR "dmasound: can't request IO resource !\n");
-		return -ENODEV;
+		goto no_device;
 	}
 	if (of_address_to_resource(io, 1, &awacs_rsrc[1]) ||
 	    request_mem_region(awacs_rsrc[1].start,
@@ -2891,7 +2904,7 @@
 		release_mem_region(awacs_rsrc[0].start,
 				   awacs_rsrc[0].end - awacs_rsrc[0].start + 1);
 		printk(KERN_ERR "dmasound: can't request Tx DMA resource !\n");
-		return -ENODEV;
+		goto no_device;
 	}
 	if (of_address_to_resource(io, 2, &awacs_rsrc[2]) ||
 	    request_mem_region(awacs_rsrc[2].start,
@@ -2902,7 +2915,7 @@
 		release_mem_region(awacs_rsrc[1].start,
 				   awacs_rsrc[1].end - awacs_rsrc[1].start + 1);
 		printk(KERN_ERR "dmasound: can't request Rx DMA resource !\n");
-		return -ENODEV;
+		goto no_device;
 	}
 
 	awacs_beep_dev = input_allocate_device();
@@ -2914,7 +2927,7 @@
 		release_mem_region(awacs_rsrc[2].start,
 				   awacs_rsrc[2].end - awacs_rsrc[2].start + 1);
 		printk(KERN_ERR "dmasound: can't allocate input device !\n");
-		return -ENOMEM;
+		goto no_device;
 	}
 
 	awacs_beep_dev->name = "dmasound beeper";
@@ -2942,7 +2955,8 @@
 	awacs_rx_irq = irq_of_parse_and_map(io, 2);
 
 	/* Hack for legacy crap that will be killed someday */
-	awacs_node = io;
+	of_node_put(awacs_node);
+	awacs_node = of_node_get(io);
 
 	/* if we have an awacs or screamer - probe the chip to make
 	 * sure we have the right revision.
@@ -2973,24 +2987,26 @@
 	*/
 
 	if (info) {
-		unsigned int *prop, l;
+		const unsigned int *prop;
+		unsigned int l;
 
 		sound_device_id = 0;
 		/* device ID appears post g3 b&w */
-		prop = (unsigned int *)get_property(info, "device-id", NULL);
+		prop = of_get_property(info, "device-id", NULL);
 		if (prop != 0)
 			sound_device_id = *prop;
 
 		/* look for a property saying what sample rates
 		   are available */
 
-		prop = (unsigned int *)get_property(info, "sample-rates", &l);
+		prop = of_get_property(info, "sample-rates", &l);
 		if (prop == 0)
-			prop = (unsigned int *) get_property
-				(info, "output-frame-rates", &l);
+			prop = of_get_property(info, "output-frame-rates", &l);
 
 		/* if it's there use it to set up frame rates */
 		init_frame_rates(prop, l) ;
+		of_node_put(info);
+		info = NULL;
 	}
 
 	if (awacs)
@@ -3160,7 +3176,16 @@
 	 */
 	input_register_device(awacs_beep_dev);
 
+	of_node_put(io);
+
 	return dmasound_init();
+
+no_device:
+	of_node_put(info);
+	of_node_put(awacs_node);
+	of_node_put(i2s_node);
+	of_node_put(io);
+	return -ENODEV ;
 }
 
 static void __exit dmasound_awacs_cleanup(void)
@@ -3179,6 +3204,8 @@
 	}
 	dmasound_deinit();
 
+	of_node_put(awacs_node);
+	of_node_put(i2s_node);
 }
 
 MODULE_DESCRIPTION("PowerMac built-in audio driver.");
diff --git a/sound/oss/dmasound/tas_common.c b/sound/oss/dmasound/tas_common.c
index 665e85b..b295ef6 100644
--- a/sound/oss/dmasound/tas_common.c
+++ b/sound/oss/dmasound/tas_common.c
@@ -41,7 +41,6 @@
 
 static u8 tas_i2c_address = 0x34;
 static struct i2c_client *tas_client;
-static struct device_node* tas_node;
 
 static int tas_attach_adapter(struct i2c_adapter *);
 static int tas_detach_client(struct i2c_client *);
@@ -190,17 +189,18 @@
 int __init
 tas_init(int driver_id, const char *driver_name)
 {
-	u32* paddr;
+	const u32* paddr;
+	struct device_node *tas_node;
 
 	printk(KERN_INFO "tas driver [%s])\n", driver_name);
 
 #ifndef CONFIG_I2C_POWERMAC
 	request_module("i2c-powermac");
 #endif
-	tas_node = find_devices("deq");
+	tas_node = of_find_node_by_name("deq");
 	if (tas_node == NULL)
 		return -ENODEV;
-	paddr = (u32 *)get_property(tas_node, "i2c-address", NULL);
+	paddr = of_get_property(tas_node, "i2c-address", NULL);
 	if (paddr) {
 		tas_i2c_address = (*paddr) >> 1;
 		printk(KERN_INFO "using i2c address: 0x%x from device-tree\n",
@@ -208,6 +208,7 @@
 	} else    
 		printk(KERN_INFO "using i2c address: 0x%x (default)\n",
 				tas_i2c_address);
+	of_node_put(tas_node);
 
 	return i2c_add_driver(&tas_driver);
 }
diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c
index c64af55..2bae9c1 100644
--- a/sound/ppc/pmac.c
+++ b/sound/ppc/pmac.c
@@ -816,6 +816,7 @@
 
 	if (chip->pdev)
 		pci_dev_put(chip->pdev);
+	of_node_put(chip->node);
 	kfree(chip);
 	return 0;
 }
@@ -863,8 +864,10 @@
  */
 static int __init snd_pmac_detect(struct snd_pmac *chip)
 {
-	struct device_node *sound = NULL;
-	unsigned int *prop, l;
+	struct device_node *sound;
+	struct device_node *dn;
+	const unsigned int *prop;
+	unsigned int l;
 	struct macio_chip* macio;
 
 	if (!machine_is(powermac))
@@ -890,22 +893,21 @@
 	else if (machine_is_compatible("PowerBook1,1")
 		 || machine_is_compatible("AAPL,PowerBook1998"))
 		chip->is_pbook_G3 = 1;
-	chip->node = find_devices("awacs");
-	if (chip->node)
-		sound = chip->node;
+	chip->node = of_find_node_by_name(NULL, "awacs");
+	sound = of_node_get(chip->node);
 
 	/*
 	 * powermac G3 models have a node called "davbus"
 	 * with a child called "sound".
 	 */
 	if (!chip->node)
-		chip->node = find_devices("davbus");
+		chip->node = of_find_node_by_name(NULL, "davbus");
 	/*
 	 * if we didn't find a davbus device, try 'i2s-a' since
 	 * this seems to be what iBooks have
 	 */
 	if (! chip->node) {
-		chip->node = find_devices("i2s-a");
+		chip->node = of_find_node_by_name(NULL, "i2s-a");
 		if (chip->node && chip->node->parent &&
 		    chip->node->parent->parent) {
 			if (device_is_compatible(chip->node->parent->parent,
@@ -917,22 +919,25 @@
 		return -ENODEV;
 
 	if (!sound) {
-		sound = find_devices("sound");
+		sound = of_find_node_by_name(NULL, "sound");
 		while (sound && sound->parent != chip->node)
-			sound = sound->next;
+			sound = of_find_node_by_name(sound, "sound");
 	}
-	if (! sound)
+	if (! sound) {
+		of_node_put(chip->node);
 		return -ENODEV;
-	prop = (unsigned int *) get_property(sound, "sub-frame", NULL);
+	}
+	prop = of_get_property(sound, "sub-frame", NULL);
 	if (prop && *prop < 16)
 		chip->subframe = *prop;
-	prop = (unsigned int *) get_property(sound, "layout-id", NULL);
+	prop = of_get_property(sound, "layout-id", NULL);
 	if (prop) {
 		/* partly deprecate snd-powermac, for those machines
 		 * that have a layout-id property for now */
 		printk(KERN_INFO "snd-powermac no longer handles any "
 				 "machines with a layout-id property "
 				 "in the device-tree, use snd-aoa.\n");
+		of_node_put(chip->node);
 		return -ENODEV;
 	}
 	/* This should be verified on older screamers */
@@ -967,10 +972,12 @@
 		chip->freq_table = tumbler_freqs;
 		chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */
 	}
-	prop = (unsigned int *)get_property(sound, "device-id", NULL);
+	prop = of_get_property(sound, "device-id", NULL);
 	if (prop)
 		chip->device_id = *prop;
-	chip->has_iic = (find_devices("perch") != NULL);
+	dn = of_find_node_by_name(NULL, "perch");
+	chip->has_iic = (dn != NULL);
+	of_node_put(dn);
 
 	/* We need the PCI device for DMA allocations, let's use a crude method
 	 * for now ...
@@ -997,10 +1004,9 @@
 
 	/* look for a property saying what sample rates
 	   are available */
-	prop = (unsigned int *) get_property(sound, "sample-rates", &l);
+	prop = of_get_property(sound, "sample-rates", &l);
 	if (! prop)
-		prop = (unsigned int *) get_property(sound,
-						     "output-frame-rates", &l);
+		prop = of_get_property(sound, "output-frame-rates", &l);
 	if (prop) {
 		int i;
 		chip->freqs_ok = 0;
@@ -1021,6 +1027,7 @@
 		chip->freqs_ok = 1;
 	}
 
+	of_node_put(sound);
 	return 0;
 }
 
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index 8f074c7..54e333f 100644
--- a/sound/ppc/tumbler.c
+++ b/sound/ppc/tumbler.c
@@ -1031,32 +1031,40 @@
 /* look for audio-gpio device */
 static struct device_node *find_audio_device(const char *name)
 {
+	struct device_node *gpiop;
 	struct device_node *np;
   
-	if (! (np = find_devices("gpio")))
+	gpiop = of_find_node_by_name(NULL, "gpio");
+	if (! gpiop)
 		return NULL;
   
-	for (np = np->child; np; np = np->sibling) {
-		const char *property = get_property(np, "audio-gpio", NULL);
+	for (np = of_get_next_child(gpiop, NULL); np;
+			np = of_get_next_child(gpiop, np)) {
+		const char *property = of_get_property(np, "audio-gpio", NULL);
 		if (property && strcmp(property, name) == 0)
-			return np;
+			break;
 	}  
-	return NULL;
+	of_node_put(gpiop);
+	return np;
 }
 
 /* look for audio-gpio device */
 static struct device_node *find_compatible_audio_device(const char *name)
 {
+	struct device_node *gpiop;
 	struct device_node *np;
   
-	if (! (np = find_devices("gpio")))
+	gpiop = of_find_node_by_name(NULL, "gpio");
+	if (!gpiop)
 		return NULL;
   
-	for (np = np->child; np; np = np->sibling) {
+	for (np = of_get_next_child(gpiop, NULL); np;
+			np = of_get_next_child(gpiop, np)) {
 		if (device_is_compatible(np, name))
-			return np;
+			break;
 	}  
-	return NULL;
+	of_node_put(gpiop);
+	return np;
 }
 
 /* find an audio device and get its address */
@@ -1066,6 +1074,7 @@
 	struct device_node *node;
 	const u32 *base;
 	u32 addr;
+	long ret;
 
 	if (is_compatible)
 		node = find_compatible_audio_device(device);
@@ -1077,12 +1086,13 @@
 		return -ENODEV;
 	}
 
-	base = get_property(node, "AAPL,address", NULL);
+	base = of_get_property(node, "AAPL,address", NULL);
 	if (! base) {
-		base = get_property(node, "reg", NULL);
+		base = of_get_property(node, "reg", NULL);
 		if (!base) {
 			DBG("(E) cannot find address for device %s !\n", device);
 			snd_printd("cannot find address for device %s\n", device);
+			of_node_put(node);
 			return -ENODEV;
 		}
 		addr = *base;
@@ -1093,7 +1103,7 @@
 
 	gp->addr = addr & 0x0000ffff;
 	/* Try to find the active state, default to 0 ! */
-	base = get_property(node, "audio-gpio-active-state", NULL);
+	base = of_get_property(node, "audio-gpio-active-state", NULL);
 	if (base) {
 		gp->active_state = *base;
 		gp->active_val = (*base) ? 0x5 : 0x4;
@@ -1108,7 +1118,7 @@
 		 * as we don't yet have an interpreter for these things
 		 */
 		if (platform)
-			prop = get_property(node, platform, NULL);
+			prop = of_get_property(node, platform, NULL);
 		if (prop) {
 			if (prop[3] == 0x9 && prop[4] == 0x9) {
 				gp->active_val = 0xd;
@@ -1124,7 +1134,9 @@
 	DBG("(I) GPIO device %s found, offset: %x, active state: %d !\n",
 	    device, gp->addr, gp->active_state);
 
-	return irq_of_parse_and_map(node, 0);
+	ret = irq_of_parse_and_map(node, 0);
+	of_node_put(node);
+	return ret;
 }
 
 /* reset audio */
@@ -1310,7 +1322,7 @@
 {
 	int i, err;
 	struct pmac_tumbler *mix;
-	u32 *paddr;
+	const u32 *paddr;
 	struct device_node *tas_node, *np;
 	char *chipname;
 
@@ -1331,9 +1343,9 @@
 
 	for (np = chip->node->child; np; np = np->sibling) {
 		if (!strcmp(np->name, "sound")) {
-			if (get_property(np, "has-anded-reset", NULL))
+			if (of_get_property(np, "has-anded-reset", NULL))
 				mix->anded_reset = 1;
-			if (get_property(np, "layout-id", NULL))
+			if (of_get_property(np, "layout-id", NULL))
 				mix->reset_on_sleep = 0;
 			break;
 		}
@@ -1342,19 +1354,20 @@
 		return err;
 
 	/* set up TAS */
-	tas_node = find_devices("deq");
+	tas_node = of_find_node_by_name(NULL, "deq");
 	if (tas_node == NULL)
-		tas_node = find_devices("codec");
+		tas_node = of_find_node_by_name(NULL, "codec");
 	if (tas_node == NULL)
 		return -ENODEV;
 
-	paddr = (u32 *)get_property(tas_node, "i2c-address", NULL);
+	paddr = of_get_property(tas_node, "i2c-address", NULL);
 	if (paddr == NULL)
-		paddr = (u32 *)get_property(tas_node, "reg", NULL);
+		paddr = of_get_property(tas_node, "reg", NULL);
 	if (paddr)
 		mix->i2c.addr = (*paddr) >> 1;
 	else
 		mix->i2c.addr = TAS_I2C_ADDR;
+	of_node_put(tas_node);
 
 	DBG("(I) TAS i2c address is: %x\n", mix->i2c.addr);
 
diff --git a/sound/sparc/amd7930.c b/sound/sparc/amd7930.c
index c899786..07962a3 100644
--- a/sound/sparc/amd7930.c
+++ b/sound/sparc/amd7930.c
@@ -1067,8 +1067,8 @@
 
 static int __devinit amd7930_obio_attach(struct device_node *dp)
 {
-	struct linux_prom_registers *regs;
-	struct linux_prom_irqs *irqp;
+	const struct linux_prom_registers *regs;
+	const struct linux_prom_irqs *irqp;
 	struct resource res, *rp;
 	int len;
 
diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c
index f5956d5..900a00d 100644
--- a/sound/sparc/cs4231.c
+++ b/sound/sparc/cs4231.c
@@ -2284,7 +2284,7 @@
 			if (!strcmp(edev->prom_node->name, "SUNW,CS4231")) {
 				match = 1;
 			} else if (!strcmp(edev->prom_node->name, "audio")) {
-				char *compat;
+				const char *compat;
 
 				compat = of_get_property(edev->prom_node,
 							 "compatible", NULL);